2 * UART-LIN master implementation
12 #include <sys/ioctl.h>
13 #include <linux/serial.h> /* struct struct_serial */
16 #include <time.h> /* clock_nanosleep */
18 #include "lin_common.h"
20 #define LIN_HDR_SIZE 2
24 struct termios tattr_orig;
26 struct serial_struct sattr;
29 struct sllin_tty sllin_tty_data;
31 struct sllin sllin_data = {
32 .tty = &sllin_tty_data,
35 /* ------------------------------------------------------------------------ */
36 static void tty_reset_mode(struct sllin_tty *tty)
38 tcsetattr(tty->tty_fd, TCSANOW, &tty->tattr_orig);
41 static int tty_set_baudrate(struct sllin_tty *tty, int baudrate)
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)
49 perror("ioctl TIOCSSERIAL");
53 // cfsetispeed(&tty->tattr, B38400);
54 // cfsetospeed(&tty->tattr, B38400);
56 // if (tcsetattr(tty->tty_fd, TCSANOW, &tty->tattr) == -1) {
57 // perror("tcsetattr()");
64 static int tty_set_mode(struct sllin_tty *tty, int baudrate)
66 if(!isatty(tty->tty_fd)) {
67 fprintf(stderr, "Not a terminal.\n");
71 /* Flush input and output queues. */
72 if (tcflush(tty->tty_fd, TCIOFLUSH) != 0) {
77 /* Save settings for later restoring */
78 tcgetattr(tty->tty_fd, &tty->tattr_orig);
80 /* Save settings into global variables for later use */
81 if (tcgetattr(tty->tty_fd, &tty->tattr) < 0)
84 if (ioctl(tty->tty_fd, TIOCGSERIAL, &tty->sattr) < 0)
85 perror("ioctl TIOCGSERIAL");
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;
96 tty->tattr.c_cc[VMIN] = 1;
97 tty->tattr.c_cc[VTIME] = 0;
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;
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';
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);
131 if (tcsetattr(tty->tty_fd, TCSANOW, &tty->tattr) == -1) {
132 perror("tcsetattr()");
137 tty_set_baudrate(tty, baudrate);
142 /* ------------------------------------------------------------------------ */
144 int sllin_open(struct sllin *sl, const char *dev_fname, int baudrate)
148 sl->lin_baud = baudrate;
150 /* Calculate baudrate for sending LIN break */
151 sl->lin_break_baud = (sl->lin_baud * 2) / 3;
153 fd = open(dev_fname, O_RDWR);
158 sl->tty->tty_fd = fd;
160 return tty_set_mode(sl->tty, sl->lin_baud);
163 int sllin_close(struct sllin *sl)
165 tty_reset_mode(sl->tty);
166 close(sl->tty->tty_fd);
170 int send_header(struct sllin *sl, int lin_id)
174 buff[0] = 0x00; /* Fake break */
175 buff[1] = 0x55; /* Sync byte */
178 lin_id |= sllin_id_parity_table[lin_id];
179 buff[2] = lin_id; /* LIN ID: 1 */
181 printf("send_header() invoked\n");
182 tcflush(sl->tty->tty_fd, TCIOFLUSH);
184 /* Decrease speed to send BREAK
185 (simulated with 0x00 data frame) */
186 tty_set_baudrate(sl->tty, sl->lin_break_baud);
188 printf("Write break\n");
189 write(sl->tty->tty_fd, &buff[0], 1); /* Write "break" */
191 read(sl->tty->tty_fd, &buff[0], 1);
192 printf("Break read\n");
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);
202 /* Restore "normal" speed */
203 tty_set_baudrate(sl->tty, sl->lin_baud);
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 */
210 int read_header(struct sllin *sl)
212 int p0, p1; /* Parity bits */
213 int par_rec; /* Parity received as a part of a packet */
214 int par_calc; /* Calculated parity */
216 uint8_t buff[LIN_HDR_SIZE];
217 memset(buff, '\0', sizeof(buff));
220 received = read(sl->tty->tty_fd, &buff[0], 1);
224 if (buff[0] != 0x55) /* Sync byte field */
227 received = read(sl->tty->tty_fd, &buff[1], 1);
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;
237 printf("%02X ", buff[0]);
238 printf("%02X ", buff[1]);
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");
252 int parse_arr(unsigned char *buff, const char *str, int len_max)
261 *buff = strtol(str, &p, 0);
269 } while (*(str++) == ',');
271 if (*(--str) != '\0')
277 static void usage(void)
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");
290 int main(int argc, char* argv[])
292 struct sllin *sl = &sllin_data;
294 static struct option long_opts[] = {
295 {"device" , 1, 0, 'd'},
296 {"baud" , 1, 0, 'B'},
298 {"response", 1, 0, 'r'},
299 {"help" , 0, 0, 'h'},
303 char *dev_fname = "";
304 int lin_baudrate = 19200;
307 unsigned char resp_data[SLLIN_DATA_MAX + 1];
310 while ((opt = getopt_long(argc, argv, "d:B:i:r:h", &long_opts[0], NULL)) != EOF) {
317 lin_baudrate = strtol(optarg, &p, 10);
318 if ((p == optarg) || ((*p != '\0') && (*p != ' '))) {
319 fprintf(stderr, "Baudrate format error\n");
325 lin_id = strtol(optarg, &p, 0);
326 if ((p == optarg) || ((*p != '\0') && (*p != ' '))) {
327 fprintf(stderr, "LIN ID format error\n");
333 resp_len = parse_arr(resp_data, optarg, SLLIN_DATA_MAX);
335 fprintf(stderr, "Response data format error\n");
343 exit(opt == 'h' ? 0 : 1);
350 //fprintf(stderr, "Expected argument after options\n");
354 /* Device name was not set by user */
355 if (strlen(dev_fname) == 0) {
360 /* ----------------------------------- */
361 if (sllin_open(sl, dev_fname, lin_baudrate) < 0) {
362 fprintf (stderr, "sllin_open open failed\n");
366 fcntl(fileno(stdin), F_SETFL, O_NONBLOCK);
367 printf("Press enter to terminate.\n\n");
373 send_header(sl, lin_id);
376 if (read(fileno(stdin), &c, 1) > 0)