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