]> rtime.felk.cvut.cz Git - linux-lin.git/blob - misc/tty_lin_master/main.c
Userspace LIN is compiled with USE_TERMIOS2 by default now.
[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         read(sl->tty->tty_fd, &buff[0], 1);
284         printf("Break read\n");
285 #else
286         {
287                 struct timespec sleep_time;
288                 sleep_time.tv_sec = 0;
289                 sleep_time.tv_nsec = ((1000000000ll * 11) / sl->lin_break_baud);
290                 clock_nanosleep(CLOCK_MONOTONIC, 0, &sleep_time, NULL);
291         }
292 #endif
293
294         /* Restore "normal" speed */
295         tty_set_baudrate(sl->tty, sl->lin_baud);
296
297         write(sl->tty->tty_fd, &buff[1], 1); /* Sync Byte Field */
298         write(sl->tty->tty_fd, &buff[2], 1); /* PID -- Protected Identifier Field */
299         return 0;
300 }
301
302 int read_header(struct sllin *sl)
303 {
304         int p0, p1; /* Parity bits */
305         int par_rec; /* Parity received as a part of a packet */
306         int par_calc; /* Calculated parity */
307         int received = 0;
308         uint8_t buff[LIN_HDR_SIZE];
309         memset(buff, '\0', sizeof(buff));
310
311         while (1) {
312                 received = read(sl->tty->tty_fd, &buff[0], 1);
313                 if (received == -1)
314                         perror("read()");
315                 
316                 if (buff[0] != 0x55) /* Sync byte field */
317                         continue;
318
319                 received = read(sl->tty->tty_fd, &buff[1], 1);
320                 if (received == -1)
321                         perror("read()");
322                 else
323                         break;
324         }
325
326         p0 = (buff[1] ^ (buff[1] >> 1) ^ (buff[1] >> 2) ^ (buff[1] >> 4)) & 0x1;
327         p1 = ~(((buff[1] >> 1) ^ (buff[1] >> 3) ^ (buff[1] >> 4) ^ (buff[1] >> 5))) & 0x1;
328
329         printf("%02X ", buff[0]);
330         printf("%02X ", buff[1]);
331
332         par_rec = (buff[1] & 0xc0) >> 6;
333         par_calc = p0 | (p1 << 1);
334         printf("| LIN id: %02X ", buff[1] & 0x3f);
335         //printf("| par_rec: %X; par_calc: %X ", par_rec, par_calc);
336         if (par_rec == par_calc)
337                 printf("| parity OK");
338         
339         printf("\n");
340
341         return 0;
342 }
343
344 int parse_arr(unsigned char *buff, const char *str, int len_max)
345 {
346         char *p;
347         int len = 0;
348
349         do {
350                 if (len >= len_max)
351                         return -1;
352                 
353                 *buff = strtol(str, &p, 0);
354                 if(str == p)
355                         return -1;
356
357                 str = p;
358
359                 len++;
360                 buff++;
361         } while (*(str++) == ',');
362
363         if (*(--str) != '\0')
364                 return -1;
365
366         return len;
367 }
368
369 static void usage(void)
370 {
371         printf("Usage: lin_master <parameters>\n\n");
372         printf("Mandatory parameters:\n");
373         printf("  -d, --device <device>   Device name, e.g. /dev/ttyS0\n\n");
374         printf("Optional parameters:\n");
375         printf("  -B, --baud <baud>       LIN bus baudrate. Default value is 19200.\n");
376         printf("                          Recommendet values are 2400, 9600, 19200\n");
377         printf("  -i, --id <num>          LIN frame ID\n");
378         printf("  -r, --response <num>    Values of data fields sent from slave task\n");
379         printf("  -h, --help              Help, i.e. this screen\n");
380 }
381
382 int main(int argc, char* argv[])
383 {
384         struct sllin *sl = &sllin_data;
385
386         static struct option long_opts[] = {
387                 {"device"  , 1, 0, 'd'},
388                 {"baud"    , 1, 0, 'B'},
389                 {"id"      , 1, 0, 'i'},
390                 {"response", 1, 0, 'r'},
391                 {"help"    , 0, 0, 'h'},
392                 {0, 0, 0, 0}
393         };
394         int opt;
395         char *dev_fname = "";
396         int lin_baudrate = 19200;
397         int lin_id = 1;
398         int resp_len = 0;
399         unsigned char resp_data[SLLIN_DATA_MAX + 1];
400         char *p;
401
402         while ((opt = getopt_long(argc, argv, "d:B:i:r:h", &long_opts[0], NULL)) != EOF) {
403                 switch (opt) {
404                         case 'd':
405                                 dev_fname = optarg;
406                                 break;
407
408                         case 'B':
409                                 lin_baudrate = strtol(optarg, &p, 10);
410                                 if ((p == optarg) || ((*p != '\0') && (*p != ' '))) {
411                                         fprintf(stderr, "Baudrate format error\n");
412                                         exit(EXIT_FAILURE);
413                                 }
414                                 break;
415
416                         case 'i':
417                                 lin_id = strtol(optarg, &p, 0);
418                                 if ((p == optarg) || ((*p != '\0') && (*p != ' '))) {
419                                         fprintf(stderr, "LIN ID format error\n");
420                                         exit(EXIT_FAILURE);
421                                 }
422                                 break;
423
424                         case 'r':
425                                 resp_len = parse_arr(resp_data, optarg, SLLIN_DATA_MAX);
426                                 if (resp_len < 0) {
427                                         fprintf(stderr, "Response data format error\n");
428                                         exit(EXIT_FAILURE);
429                                 }
430                                 break;
431
432                         case 'h':
433                         default:
434                                 usage();
435                                 exit(opt == 'h' ? 0 : 1);
436                                 break;
437                 }
438         }
439
440         if (optind < argc) {
441                 usage();
442                 //fprintf(stderr, "Expected argument after options\n");
443                 exit(EXIT_FAILURE);
444         }
445
446         /* Device name was not set by user */
447         if (strlen(dev_fname) == 0) {
448                 usage();
449                 exit(EXIT_FAILURE);
450         }
451
452         /* ----------------------------------- */
453         if (sllin_open(sl, dev_fname, lin_baudrate) < 0) {
454                 fprintf (stderr, "sllin_open open failed\n");
455                 exit(EXIT_FAILURE);
456         }
457
458         fcntl(fileno(stdin), F_SETFL, O_NONBLOCK);
459         printf("Press enter to terminate.\n\n");
460
461
462         while(1) {
463                 char c;
464
465                 send_header(sl, lin_id);
466                 sleep(1);
467
468                 if (read(fileno(stdin), &c, 1) > 0)
469                         break;
470         }
471
472         sllin_close(sl);
473
474         return EXIT_SUCCESS;
475 }