2 * UART-LIN master implementation
14 #include <sys/ioctl.h>
17 #include <time.h> /* clock_nanosleep */
21 #include <linux/serial.h> /* struct struct_serial */
23 #else /*USE_TERMIOS2*/
24 #include <asm/ioctls.h>
25 #include <asm/termbits.h>
26 #endif /*USE_TERMIOS2*/
28 #include "lin_common.h"
30 #define LIN_HDR_SIZE 2
36 struct termios tattr_orig;
38 struct serial_struct sattr;
39 #else /*USE_TERMIOS2*/
40 struct termios2 tattr_orig;
41 struct termios2 tattr;
42 #endif /*USE_TERMIOS2*/
45 struct sllin_tty sllin_tty_data;
47 struct sllin sllin_data = {
48 .tty = &sllin_tty_data,
51 /* ------------------------------------------------------------------------ */
55 static void tty_reset_mode(struct sllin_tty *tty)
57 tcsetattr(tty->tty_fd, TCSANOW, &tty->tattr_orig);
60 static int tty_set_baudrate(struct sllin_tty *tty, int baudrate)
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)
68 perror("ioctl TIOCSSERIAL");
72 // cfsetispeed(&tty->tattr, B38400);
73 // cfsetospeed(&tty->tattr, B38400);
75 // if (tcsetattr(tty->tty_fd, TCSANOW, &tty->tattr) == -1) {
76 // perror("tcsetattr()");
83 static int tty_flush(struct sllin_tty *tty, int queue_selector)
85 return tcflush(tty->tty_fd, queue_selector);
88 #else /*USE_TERMIOS2*/
90 static void tty_reset_mode(struct sllin_tty *tty)
92 ioctl(tty->tty_fd, TCSETS2, &tty->tattr_orig);
95 static int tty_set_baudrate(struct sllin_tty *tty, int baudrate)
97 tty->tattr.c_ospeed = baudrate;
98 tty->tattr.c_ispeed = baudrate;
99 tty->tattr.c_cflag &= ~CBAUD;
100 tty->tattr.c_cflag |= BOTHER;
102 if(ioctl(tty->tty_fd, TCSETS2, &tty->tattr)) {
103 perror("ioctl TIOCSSERIAL");
110 static int tty_flush(struct sllin_tty *tty, int queue_selector)
112 return ioctl(tty->tty_fd, TCFLSH, queue_selector);
115 #endif /*USE_TERMIOS2*/
118 static int tty_set_mode(struct sllin_tty *tty, int baudrate)
120 if(!isatty(tty->tty_fd)) {
121 fprintf(stderr, "Not a terminal.\n");
125 /* Flush input and output queues. */
126 if (tty_flush(tty, TCIOFLUSH) != 0) {
133 /* Save settings for later restoring */
134 if (tcgetattr(tty->tty_fd, &tty->tattr_orig) < 0) {
139 /* Save settings into global variables for later use */
140 if (tcgetattr(tty->tty_fd, &tty->tattr) < 0) {
145 /* Save settings into global variables for later use */
146 if (ioctl(tty->tty_fd, TIOCGSERIAL, &tty->sattr) < 0) {
147 perror("ioctl TIOCGSERIAL");
150 #else /*USE_TERMIOS2*/
152 /* Save settings for later restoring */
153 if (ioctl(tty->tty_fd, TCGETS2, &tty->tattr_orig) < 0) {
154 perror("ioctl TCGETS2");
158 /* Save settings into global variables for later use */
159 if (ioctl(tty->tty_fd, TCGETS2, &tty->tattr) < 0) {
160 perror("ioctl TCGETS2");
164 #endif /*USE_TERMIOS2*/
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;
176 tty->tattr.c_cc[VMIN] = 1;
177 tty->tattr.c_cc[VTIME] = 0;
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;
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';
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);
212 if (tcsetattr(tty->tty_fd, TCSANOW, &tty->tattr) == -1) {
213 perror("tcsetattr()");
217 #else /*USE_TERMIOS2*/
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");
226 #endif /*USE_TERMIOS2*/
229 tty_set_baudrate(tty, baudrate);
234 /* ------------------------------------------------------------------------ */
236 int sllin_open(struct sllin *sl, const char *dev_fname, int baudrate)
240 sl->lin_baud = baudrate;
242 /* Calculate baudrate for sending LIN break */
243 sl->lin_break_baud = (sl->lin_baud * 2) / 3;
245 fd = open(dev_fname, O_RDWR);
250 sl->tty->tty_fd = fd;
252 return tty_set_mode(sl->tty, sl->lin_baud);
255 int sllin_close(struct sllin *sl)
257 tty_reset_mode(sl->tty);
258 close(sl->tty->tty_fd);
262 int send_header(struct sllin *sl, int lin_id)
266 buff[0] = 0x00; /* Fake break */
267 buff[1] = 0x55; /* Sync byte */
270 lin_id |= sllin_id_parity_table[lin_id];
271 buff[2] = lin_id; /* LIN ID: 1 */
273 printf("send_header() invoked\n");
274 tty_flush(sl->tty, TCIOFLUSH);
276 /* Decrease speed to send BREAK
277 (simulated with 0x00 data frame) */
278 tty_set_baudrate(sl->tty, sl->lin_break_baud);
280 printf("Write break\n");
281 write(sl->tty->tty_fd, &buff[0], 1); /* Write "break" */
283 read(sl->tty->tty_fd, &buff[0], 1);
284 printf("Break read\n");
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);
294 /* Restore "normal" speed */
295 tty_set_baudrate(sl->tty, sl->lin_baud);
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 */
302 int read_header(struct sllin *sl)
304 int p0, p1; /* Parity bits */
305 int par_rec; /* Parity received as a part of a packet */
306 int par_calc; /* Calculated parity */
308 uint8_t buff[LIN_HDR_SIZE];
309 memset(buff, '\0', sizeof(buff));
312 received = read(sl->tty->tty_fd, &buff[0], 1);
316 if (buff[0] != 0x55) /* Sync byte field */
319 received = read(sl->tty->tty_fd, &buff[1], 1);
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;
329 printf("%02X ", buff[0]);
330 printf("%02X ", buff[1]);
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");
344 int parse_arr(unsigned char *buff, const char *str, int len_max)
353 *buff = strtol(str, &p, 0);
361 } while (*(str++) == ',');
363 if (*(--str) != '\0')
369 static void usage(void)
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");
382 int main(int argc, char* argv[])
384 struct sllin *sl = &sllin_data;
386 static struct option long_opts[] = {
387 {"device" , 1, 0, 'd'},
388 {"baud" , 1, 0, 'B'},
390 {"response", 1, 0, 'r'},
391 {"help" , 0, 0, 'h'},
395 char *dev_fname = "";
396 int lin_baudrate = 19200;
399 unsigned char resp_data[SLLIN_DATA_MAX + 1];
402 while ((opt = getopt_long(argc, argv, "d:B:i:r:h", &long_opts[0], NULL)) != EOF) {
409 lin_baudrate = strtol(optarg, &p, 10);
410 if ((p == optarg) || ((*p != '\0') && (*p != ' '))) {
411 fprintf(stderr, "Baudrate format error\n");
417 lin_id = strtol(optarg, &p, 0);
418 if ((p == optarg) || ((*p != '\0') && (*p != ' '))) {
419 fprintf(stderr, "LIN ID format error\n");
425 resp_len = parse_arr(resp_data, optarg, SLLIN_DATA_MAX);
427 fprintf(stderr, "Response data format error\n");
435 exit(opt == 'h' ? 0 : 1);
442 //fprintf(stderr, "Expected argument after options\n");
446 /* Device name was not set by user */
447 if (strlen(dev_fname) == 0) {
452 /* ----------------------------------- */
453 if (sllin_open(sl, dev_fname, lin_baudrate) < 0) {
454 fprintf (stderr, "sllin_open open failed\n");
458 fcntl(fileno(stdin), F_SETFL, O_NONBLOCK);
459 printf("Press enter to terminate.\n\n");
465 send_header(sl, lin_id);
468 if (read(fileno(stdin), &c, 1) > 0)