]> rtime.felk.cvut.cz Git - linux-lin.git/blob - misc/tty_lin_master/main.c
8d490c9cc8e0530b286f7be0de22845f56b9cd1b
[linux-lin.git] / misc / tty_lin_master / main.c
1 /*
2  * UART-LIN master implementation
3  */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <unistd.h>
8 #include <fcntl.h>
9 #include <string.h>
10 #include <termios.h>
11 #include <stdint.h>
12 #include <sys/ioctl.h>
13 #include <linux/serial.h> /* struct struct_serial */
14 #include <unistd.h>
15 #include <fcntl.h>
16 #include <time.h> /* clock_nanosleep */
17 #include <getopt.h>
18 #include "lin_common.h"
19
20 #define LIN_HDR_SIZE            2
21
22 struct sllin_tty {
23         int tty_fd;
24         struct termios tattr_orig;
25         struct termios tattr;
26         struct serial_struct sattr;
27 };
28
29 struct sllin_tty sllin_tty_data;
30
31 struct sllin sllin_data = {
32         .tty = &sllin_tty_data,
33 };
34
35 /* ------------------------------------------------------------------------ */
36 static void tty_reset_mode(struct sllin_tty *tty)
37 {
38         tcsetattr(tty->tty_fd, TCSANOW, &tty->tattr_orig);
39 }
40
41 static int tty_set_baudrate(struct sllin_tty *tty, int baudrate)
42 {
43         /* Set "non-standard" baudrate in serial_struct struct */
44         tty->sattr.flags &= (~ASYNC_SPD_MASK);
45         tty->sattr.flags |= (ASYNC_SPD_CUST);
46         tty->sattr.custom_divisor = (tty->sattr.baud_base + baudrate / 2) / baudrate;
47         if (ioctl(tty->tty_fd, TIOCSSERIAL, &tty->sattr) < 0)
48         {
49                 perror("ioctl TIOCSSERIAL");
50                 return -1;
51         }
52
53 //      cfsetispeed(&tty->tattr, B38400);
54 //      cfsetospeed(&tty->tattr, B38400);
55 //
56 //      if (tcsetattr(tty->tty_fd, TCSANOW, &tty->tattr) == -1) {
57 //              perror("tcsetattr()");
58 //              return -1;
59 //      }
60
61         return 0;
62 }
63
64 static int tty_set_mode(struct sllin_tty *tty, int baudrate)
65 {
66         if(!isatty(tty->tty_fd)) {
67                 fprintf(stderr, "Not a terminal.\n");
68                 return -1;
69         }
70
71         /* Flush input and output queues. */
72         if (tcflush(tty->tty_fd, TCIOFLUSH) != 0) {
73                 perror("tcflush");
74                 return -1;;
75         }
76
77         /* Save settings for later restoring */
78         tcgetattr(tty->tty_fd, &tty->tattr_orig);
79
80         /* Save settings into global variables for later use */
81         if (tcgetattr(tty->tty_fd, &tty->tattr) < 0)
82                 perror("tcgetattr");
83
84         if (ioctl(tty->tty_fd, TIOCGSERIAL, &tty->sattr) < 0)
85                 perror("ioctl TIOCGSERIAL");
86
87         /* Set RAW mode */
88 #if 0
89         tty->tattr.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
90                                 | INLCR | IGNCR | ICRNL | IXON);
91         tty->tattr.c_oflag &= ~OPOST;
92         tty->tattr.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
93         tty->tattr.c_cflag &= ~(CSIZE | PARENB);
94         tty->tattr.c_cflag |= CS8;
95
96         tty->tattr.c_cc[VMIN] = 1;
97         tty->tattr.c_cc[VTIME] = 0;
98 #else
99         /* 8 data bits                  */
100         /* Enable receiver              */
101         /* Ignore CD (local connection) */
102         tty->tattr.c_cflag = CS8 | CREAD | CLOCAL;
103         tty->tattr.c_iflag = 0;
104         tty->tattr.c_oflag = NL0 | CR0 | TAB0 | BS0 | VT0 | FF0;
105         tty->tattr.c_lflag = 0;
106
107         tty->tattr.c_cc[VINTR]    = '\0';
108         tty->tattr.c_cc[VQUIT]    = '\0';
109         tty->tattr.c_cc[VERASE]   = '\0';
110         tty->tattr.c_cc[VKILL]    = '\0';
111         tty->tattr.c_cc[VEOF]     = '\0';
112         tty->tattr.c_cc[VTIME]    = '\0';
113         tty->tattr.c_cc[VMIN]     = 1;
114         tty->tattr.c_cc[VSWTC]    = '\0';
115         tty->tattr.c_cc[VSTART]   = '\0';
116         tty->tattr.c_cc[VSTOP]    = '\0';
117         tty->tattr.c_cc[VSUSP]    = '\0';
118         tty->tattr.c_cc[VEOL]     = '\0';
119         tty->tattr.c_cc[VREPRINT] = '\0';
120         tty->tattr.c_cc[VDISCARD] = '\0';
121         tty->tattr.c_cc[VWERASE]  = '\0';
122         tty->tattr.c_cc[VLNEXT]   = '\0';
123         tty->tattr.c_cc[VEOL2]    = '\0';
124 #endif
125
126         /* Set TX, RX speed to 38400 -- this value allows
127            to use custom speed in struct struct_serial */
128         cfsetispeed(&tty->tattr, B38400);
129         cfsetospeed(&tty->tattr, B38400);
130
131         if (tcsetattr(tty->tty_fd, TCSANOW, &tty->tattr) == -1) {
132                 perror("tcsetattr()");
133                 return -1;
134         }
135
136         /* Set real speed */
137         tty_set_baudrate(tty, baudrate);
138
139         return 0;
140 }
141
142 /* ------------------------------------------------------------------------ */
143
144 int sllin_open(struct sllin *sl, const char *dev_fname, int baudrate)
145 {
146         int fd;
147
148         sl->lin_baud = baudrate;
149
150         /* Calculate baudrate for sending LIN break */
151         sl->lin_break_baud = (sl->lin_baud * 2) / 3;
152
153         fd = open(dev_fname, O_RDWR);
154         if (fd < 0) {
155                 perror("open()");
156                 return -1;
157         }
158         sl->tty->tty_fd = fd;
159
160         return tty_set_mode(sl->tty, sl->lin_baud);
161 }
162
163 int sllin_close(struct sllin *sl)
164 {
165         tty_reset_mode(sl->tty);
166         close(sl->tty->tty_fd);
167         return 0;
168 }
169
170 int send_header(struct sllin *sl, int lin_id)
171 {
172         int buff[3];
173
174         buff[0] = 0x00; /* Fake break */
175         buff[1] = 0x55; /* Sync byte */
176
177         lin_id &= 0x3f;
178         lin_id |= sllin_id_parity_table[lin_id];
179         buff[2] = lin_id; /* LIN ID: 1 */
180
181         printf("send_header() invoked\n");
182         tcflush(sl->tty->tty_fd, TCIOFLUSH);
183
184         /* Decrease speed to send BREAK
185            (simulated with 0x00 data frame) */
186         tty_set_baudrate(sl->tty, sl->lin_break_baud);
187
188         printf("Write break\n");
189         write(sl->tty->tty_fd, &buff[0], 1); /* Write "break" */
190 #if 0
191         read(sl->tty->tty_fd, &buff[0], 1);
192         printf("Break read\n");
193 #else
194         {
195                 struct timespec sleep_time;
196                 sleep_time.tv_sec = 0;
197                 sleep_time.tv_nsec = ((1000000000ll * 11) / sl->lin_break_baud);
198                 clock_nanosleep(CLOCK_MONOTONIC, 0, &sleep_time, NULL);
199         }
200 #endif
201
202         /* Restore "normal" speed */
203         tty_set_baudrate(sl->tty, sl->lin_baud);
204
205         write(sl->tty->tty_fd, &buff[1], 1); /* Sync Byte Field */
206         write(sl->tty->tty_fd, &buff[2], 1); /* PID -- Protected Identifier Field */
207         return 0;
208 }
209
210 int read_header(struct sllin *sl)
211 {
212         int p0, p1; /* Parity bits */
213         int par_rec; /* Parity received as a part of a packet */
214         int par_calc; /* Calculated parity */
215         int received = 0;
216         uint8_t buff[LIN_HDR_SIZE];
217         memset(buff, '\0', sizeof(buff));
218
219         while (1) {
220                 received = read(sl->tty->tty_fd, &buff[0], 1);
221                 if (received == -1)
222                         perror("read()");
223                 
224                 if (buff[0] != 0x55) /* Sync byte field */
225                         continue;
226
227                 received = read(sl->tty->tty_fd, &buff[1], 1);
228                 if (received == -1)
229                         perror("read()");
230                 else
231                         break;
232         }
233
234         p0 = (buff[1] ^ (buff[1] >> 1) ^ (buff[1] >> 2) ^ (buff[1] >> 4)) & 0x1;
235         p1 = ~(((buff[1] >> 1) ^ (buff[1] >> 3) ^ (buff[1] >> 4) ^ (buff[1] >> 5))) & 0x1;
236
237         printf("%02X ", buff[0]);
238         printf("%02X ", buff[1]);
239
240         par_rec = (buff[1] & 0xc0) >> 6;
241         par_calc = p0 | (p1 << 1);
242         printf("| LIN id: %02X ", buff[1] & 0x3f);
243         //printf("| par_rec: %X; par_calc: %X ", par_rec, par_calc);
244         if (par_rec == par_calc)
245                 printf("| parity OK");
246         
247         printf("\n");
248
249         return 0;
250 }
251
252 int parse_arr(unsigned char *buff, const char *str, int len_max)
253 {
254         char *p;
255         int len = 0;
256
257         do {
258                 if (len >= len_max)
259                         return -1;
260                 
261                 *buff = strtol(str, &p, 0);
262                 if(str == p)
263                         return -1;
264
265                 str = p;
266
267                 len++;
268                 buff++;
269         } while (*(str++) == ',');
270
271         if (*(--str) != '\0')
272                 return -1;
273
274         return len;
275 }
276
277 static void usage(void)
278 {
279         printf("Usage: lin_master <parameters>\n\n");
280         printf("Mandatory parameters:\n");
281         printf("  -d, --device <device>   Device name, e.g. /dev/ttyS0\n\n");
282         printf("Optional parameters:\n");
283         printf("  -B, --baud <baud>       LIN bus baudrate. Default value is 19200.\n");
284         printf("                          Recommendet values are 2400, 9600, 19200\n");
285         printf("  -i, --id <num>          LIN frame ID\n");
286         printf("  -r, --response <num>    Values of data fields sent from slave task\n");
287         printf("  -h, --help              Help, i.e. this screen\n");
288 }
289
290 int main(int argc, char* argv[])
291 {
292         struct sllin *sl = &sllin_data;
293
294         static struct option long_opts[] = {
295                 {"device"  , 1, 0, 'd'},
296                 {"baud"    , 1, 0, 'B'},
297                 {"id"      , 1, 0, 'i'},
298                 {"response", 1, 0, 'r'},
299                 {"help"    , 0, 0, 'h'},
300                 {0, 0, 0, 0}
301         };
302         int opt;
303         char *dev_fname = "";
304         int lin_baudrate = 19200;
305         int lin_id = 1;
306         int resp_len = 0;
307         unsigned char resp_data[SLLIN_DATA_MAX + 1];
308         char *p;
309
310         while ((opt = getopt_long(argc, argv, "d:B:i:r:h", &long_opts[0], NULL)) != EOF) {
311                 switch (opt) {
312                         case 'd':
313                                 dev_fname = optarg;
314                                 break;
315
316                         case 'B':
317                                 lin_baudrate = strtol(optarg, &p, 10);
318                                 if ((p == optarg) || ((*p != '\0') && (*p != ' '))) {
319                                         fprintf(stderr, "Baudrate format error\n");
320                                         exit(EXIT_FAILURE);
321                                 }
322                                 break;
323
324                         case 'i':
325                                 lin_id = strtol(optarg, &p, 0);
326                                 if ((p == optarg) || ((*p != '\0') && (*p != ' '))) {
327                                         fprintf(stderr, "LIN ID format error\n");
328                                         exit(EXIT_FAILURE);
329                                 }
330                                 break;
331
332                         case 'r':
333                                 resp_len = parse_arr(resp_data, optarg, SLLIN_DATA_MAX);
334                                 if (resp_len < 0) {
335                                         fprintf(stderr, "Response data format error\n");
336                                         exit(EXIT_FAILURE);
337                                 }
338                                 break;
339
340                         case 'h':
341                         default:
342                                 usage();
343                                 exit(opt == 'h' ? 0 : 1);
344                                 break;
345                 }
346         }
347
348         if (optind < argc) {
349                 usage();
350                 //fprintf(stderr, "Expected argument after options\n");
351                 exit(EXIT_FAILURE);
352         }
353
354         /* Device name was not set by user */
355         if (strlen(dev_fname) == 0) {
356                 usage();
357                 exit(EXIT_FAILURE);
358         }
359
360         /* ----------------------------------- */
361         if (sllin_open(sl, dev_fname, lin_baudrate) < 0) {
362                 fprintf (stderr, "sllin_open open failed\n");
363                 exit(EXIT_FAILURE);
364         }
365
366         fcntl(fileno(stdin), F_SETFL, O_NONBLOCK);
367         printf("Press enter to terminate.\n\n");
368
369
370         while(1) {
371                 char c;
372
373                 send_header(sl, lin_id);
374                 sleep(1);
375
376                 if (read(fileno(stdin), &c, 1) > 0)
377                         break;
378         }
379
380         sllin_close(sl);
381
382         return EXIT_SUCCESS;
383 }