]> rtime.felk.cvut.cz Git - linux-lin.git/blobdiff - 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
index faa01cebd707a5642a0203fe526f8fce9d6493bd..a111fde1fce54229be40ddddf6717d2eef0651f0 100644 (file)
  * UART-LIN master implementation
  */
 
+
+#define USE_TERMIOS2
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <string.h>
-#include <termios.h>
 #include <stdint.h>
 #include <sys/ioctl.h>
-#include <linux/serial.h> /* struct struct_serial */
 #include <unistd.h>
 #include <fcntl.h>
 #include <time.h> /* clock_nanosleep */
+#include <getopt.h>
+
+#ifndef USE_TERMIOS2
+  #include <linux/serial.h> /* struct struct_serial */
+  #include <termios.h>
+#else /*USE_TERMIOS2*/
+  #include <asm/ioctls.h>
+  #include <asm/termbits.h>
+#endif /*USE_TERMIOS2*/
+
+#include "lin_common.h"
 
 #define LIN_HDR_SIZE           2
-#define LIN_PKT_MAX_SIZE       16 /* FIXME */
 
-const int lin_baudrate = 19200;
-int lin_break_baud = 0;
+struct sllin_tty {
+       int tty_fd;
+
+#ifndef USE_TERMIOS2
+       struct termios tattr_orig;
+       struct termios tattr;
+       struct serial_struct sattr;
+#else /*USE_TERMIOS2*/
+       struct termios2 tattr_orig;
+       struct termios2 tattr;
+#endif /*USE_TERMIOS2*/
+};
+
+struct sllin_tty sllin_tty_data;
+
+struct sllin sllin_data = {
+       .tty = &sllin_tty_data,
+};
 
-struct termios tattr_orig;
-struct termios tattr;
-struct serial_struct sattr;
 /* ------------------------------------------------------------------------ */
-static void reset_input_mode(int tty)
+
+#ifndef USE_TERMIOS2
+
+static void tty_reset_mode(struct sllin_tty *tty)
 {
-       tcsetattr(tty, TCSANOW, &tattr_orig);
+       tcsetattr(tty->tty_fd, TCSANOW, &tty->tattr_orig);
 }
 
-static void set_uart_baudrate(int tty, int speed)
+static int tty_set_baudrate(struct sllin_tty *tty, int baudrate)
 {
        /* Set "non-standard" baudrate in serial_struct struct */
-       sattr.flags &= (~ASYNC_SPD_MASK);
-       sattr.flags |= (ASYNC_SPD_CUST);
-       sattr.custom_divisor = ((sattr.baud_base) / speed);
-       if (ioctl(tty, TIOCSSERIAL, &sattr) < 0)
+       tty->sattr.flags &= (~ASYNC_SPD_MASK);
+       tty->sattr.flags |= (ASYNC_SPD_CUST);
+       tty->sattr.custom_divisor = (tty->sattr.baud_base + baudrate / 2) / baudrate;
+       if (ioctl(tty->tty_fd, TIOCSSERIAL, &tty->sattr) < 0)
        {
-               perror("ioctl()");
+               perror("ioctl TIOCSSERIAL");
+               return -1;
        }
 
-//     cfsetispeed(&tattr, B38400);
-//     cfsetospeed(&tattr, B38400);
+//     cfsetispeed(&tty->tattr, B38400);
+//     cfsetospeed(&tty->tattr, B38400);
 //
-//     if (tcsetattr(tty, TCSANOW, &tattr) == -1)      
+//     if (tcsetattr(tty->tty_fd, TCSANOW, &tty->tattr) == -1) {
 //             perror("tcsetattr()");
+//             return -1;
+//     }
+
+       return 0;
+}
+
+static int tty_flush(struct sllin_tty *tty, int queue_selector)
+{
+       return tcflush(tty->tty_fd, queue_selector);
+}
+
+#else /*USE_TERMIOS2*/
+
+static void tty_reset_mode(struct sllin_tty *tty)
+{
+       ioctl(tty->tty_fd, TCSETS2, &tty->tattr_orig);
+}
+
+static int tty_set_baudrate(struct sllin_tty *tty, int baudrate)
+{
+       tty->tattr.c_ospeed = baudrate;
+       tty->tattr.c_ispeed = baudrate;
+       tty->tattr.c_cflag &= ~CBAUD;
+       tty->tattr.c_cflag |= BOTHER;
+
+       if(ioctl(tty->tty_fd, TCSETS2, &tty->tattr)) {
+               perror("ioctl TIOCSSERIAL");
+               return -1;
+       }
+
+       return 0;
 }
 
-static void set_input_mode(int tty)
+static int tty_flush(struct sllin_tty *tty, int queue_selector)
 {
+       return ioctl(tty->tty_fd, TCFLSH, queue_selector);
+}
+
+#endif /*USE_TERMIOS2*/
+
+
+static int tty_set_mode(struct sllin_tty *tty, int baudrate)
+{
+       if(!isatty(tty->tty_fd)) {
+               fprintf(stderr, "Not a terminal.\n");
+               return -1;
+       }
+
        /* Flush input and output queues. */
-       if (tcflush(tty, TCIOFLUSH) != 0) {
+       if (tty_flush(tty, TCIOFLUSH) != 0) {
                perror("tcflush");
-               exit(EXIT_FAILURE);
+               return -1;;
        }
 
-       if(!isatty(tty)) {
-               fprintf(stderr, "Not a terminal.\n");
-               exit(EXIT_FAILURE);
+#ifndef USE_TERMIOS2
+
+       /* Save settings for later restoring */
+       if (tcgetattr(tty->tty_fd, &tty->tattr_orig) < 0) {
+               perror("tcgetattr");
+               return -1;
        }
 
+       /* Save settings into global variables for later use */
+       if (tcgetattr(tty->tty_fd, &tty->tattr) < 0) {
+               perror("tcgetattr");
+               return -1;
+       }
+
+       /* Save settings into global variables for later use */
+       if (ioctl(tty->tty_fd, TIOCGSERIAL, &tty->sattr) < 0) {
+               perror("ioctl TIOCGSERIAL");
+       }
+
+#else /*USE_TERMIOS2*/
+
        /* Save settings for later restoring */
-       tcgetattr(tty, &tattr_orig);
+       if (ioctl(tty->tty_fd, TCGETS2, &tty->tattr_orig) < 0) {
+               perror("ioctl TCGETS2");
+               return -1;
+       }
 
        /* Save settings into global variables for later use */
-       tcgetattr(tty, &tattr);
-       if (ioctl(tty, TIOCGSERIAL, &sattr) < 0)
-               perror("ioctl()");
+       if (ioctl(tty->tty_fd, TCGETS2, &tty->tattr) < 0) {
+               perror("ioctl TCGETS2");
+               return -1;
+       }
+
+#endif /*USE_TERMIOS2*/
+
 
        /* Set RAW mode */
 #if 0
-       tattr.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
+       tty->tattr.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
                                | INLCR | IGNCR | ICRNL | IXON);
-       tattr.c_oflag &= ~OPOST;
-       tattr.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
-       tattr.c_cflag &= ~(CSIZE | PARENB);
-       tattr.c_cflag |= CS8;
+       tty->tattr.c_oflag &= ~OPOST;
+       tty->tattr.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
+       tty->tattr.c_cflag &= ~(CSIZE | PARENB);
+       tty->tattr.c_cflag |= CS8;
 
-       tattr.c_cc[VMIN] = 1;
-       tattr.c_cc[VTIME] = 0;
+       tty->tattr.c_cc[VMIN] = 1;
+       tty->tattr.c_cc[VTIME] = 0;
 #else
        /* 8 data bits                  */
        /* Enable receiver              */
        /* Ignore CD (local connection) */
-       tattr.c_cflag = CS8 | CREAD | CLOCAL;
-       tattr.c_iflag = 0;
-       tattr.c_oflag = NL0 | CR0 | TAB0 | BS0 | VT0 | FF0;
-       tattr.c_lflag = 0;
-
-       tattr.c_cc[VINTR]    = '\0';
-       tattr.c_cc[VQUIT]    = '\0';
-       tattr.c_cc[VERASE]   = '\0';
-       tattr.c_cc[VKILL]    = '\0';
-       tattr.c_cc[VEOF]     = '\0';
-       tattr.c_cc[VTIME]    = '\0';
-       tattr.c_cc[VMIN]     = 1;
-       tattr.c_cc[VSWTC]    = '\0';
-       tattr.c_cc[VSTART]   = '\0';
-       tattr.c_cc[VSTOP]    = '\0';
-       tattr.c_cc[VSUSP]    = '\0';
-       tattr.c_cc[VEOL]     = '\0';
-       tattr.c_cc[VREPRINT] = '\0';
-       tattr.c_cc[VDISCARD] = '\0';
-       tattr.c_cc[VWERASE]  = '\0';
-       tattr.c_cc[VLNEXT]   = '\0';
-       tattr.c_cc[VEOL2]    = '\0';
+       tty->tattr.c_cflag = CS8 | CREAD | CLOCAL;
+       tty->tattr.c_iflag = 0;
+       tty->tattr.c_oflag = NL0 | CR0 | TAB0 | BS0 | VT0 | FF0;
+       tty->tattr.c_lflag = 0;
+
+       tty->tattr.c_cc[VINTR]    = '\0';
+       tty->tattr.c_cc[VQUIT]    = '\0';
+       tty->tattr.c_cc[VERASE]   = '\0';
+       tty->tattr.c_cc[VKILL]    = '\0';
+       tty->tattr.c_cc[VEOF]     = '\0';
+       tty->tattr.c_cc[VTIME]    = '\0';
+       tty->tattr.c_cc[VMIN]     = 1;
+       tty->tattr.c_cc[VSWTC]    = '\0';
+       tty->tattr.c_cc[VSTART]   = '\0';
+       tty->tattr.c_cc[VSTOP]    = '\0';
+       tty->tattr.c_cc[VSUSP]    = '\0';
+       tty->tattr.c_cc[VEOL]     = '\0';
+       tty->tattr.c_cc[VREPRINT] = '\0';
+       tty->tattr.c_cc[VDISCARD] = '\0';
+       tty->tattr.c_cc[VWERASE]  = '\0';
+       tty->tattr.c_cc[VLNEXT]   = '\0';
+       tty->tattr.c_cc[VEOL2]    = '\0';
 #endif
 
+#ifndef USE_TERMIOS2
        /* Set TX, RX speed to 38400 -- this value allows
           to use custom speed in struct struct_serial */
-       cfsetispeed(&tattr, B38400);
-       cfsetospeed(&tattr, B38400);
+       cfsetispeed(&tty->tattr, B38400);
+       cfsetospeed(&tty->tattr, B38400);
 
-       if (tcsetattr(tty, TCSANOW, &tattr) == -1)      
+       if (tcsetattr(tty->tty_fd, TCSANOW, &tty->tattr) == -1) {
                perror("tcsetattr()");
+               return -1;
+       }
+
+#else /*USE_TERMIOS2*/
+
+       /* Set new parameters with previous speed and left */
+       /* tty_set_baudrate() to do the rest  */
+       if(ioctl(tty->tty_fd, TCSETS2, &tty->tattr)) {
+               perror("ioctl TIOCSSERIAL");
+               return -1;
+       }
+
+#endif /*USE_TERMIOS2*/
 
        /* Set real speed */
-       set_uart_baudrate(tty, lin_baudrate);
+       tty_set_baudrate(tty, baudrate);
+
+       return 0;
+}
+
+/* ------------------------------------------------------------------------ */
+
+int sllin_open(struct sllin *sl, const char *dev_fname, int baudrate)
+{
+       int fd;
+
+       sl->lin_baud = baudrate;
 
        /* Calculate baudrate for sending LIN break */
-       lin_break_baud = ((lin_baudrate * 2) / 3);
+       sl->lin_break_baud = (sl->lin_baud * 2) / 3;
+
+       fd = open(dev_fname, O_RDWR);
+       if (fd < 0) {
+               perror("open()");
+               return -1;
+       }
+       sl->tty->tty_fd = fd;
+
+       return tty_set_mode(sl->tty, sl->lin_baud);
 }
 
+int sllin_close(struct sllin *sl)
+{
+       tty_reset_mode(sl->tty);
+       close(sl->tty->tty_fd);
+       return 0;
+}
 
-int send_header(int tty)
+int send_header(struct sllin *sl, int lin_id)
 {
        int buff[3];
+
        buff[0] = 0x00; /* Fake break */
        buff[1] = 0x55; /* Sync byte */
-       buff[2] = 0xC1; /* LIN ID: 1 */
+
+       lin_id &= 0x3f;
+       lin_id |= sllin_id_parity_table[lin_id];
+       buff[2] = lin_id; /* LIN ID: 1 */
 
        printf("send_header() invoked\n");
-       tcflush(tty, TCIOFLUSH);
+       tty_flush(sl->tty, TCIOFLUSH);
 
        /* Decrease speed to send BREAK
           (simulated with 0x00 data frame) */
-       set_uart_baudrate(tty, lin_break_baud);
+       tty_set_baudrate(sl->tty, sl->lin_break_baud);
 
        printf("Write break\n");
-       write(tty, &buff[0], 1); /* Write "break" */
+       write(sl->tty->tty_fd, &buff[0], 1); /* Write "break" */
 #if 0
-       read(tty, &buff[0], 1);
+       read(sl->tty->tty_fd, &buff[0], 1);
        printf("Break read\n");
 #else
        {
                struct timespec sleep_time;
                sleep_time.tv_sec = 0;
-               sleep_time.tv_nsec = ((1000000000ll * 11) / lin_break_baud);
+               sleep_time.tv_nsec = ((1000000000ll * 11) / sl->lin_break_baud);
                clock_nanosleep(CLOCK_MONOTONIC, 0, &sleep_time, NULL);
        }
 #endif
 
        /* Restore "normal" speed */
-       set_uart_baudrate(tty, lin_baudrate);
+       tty_set_baudrate(sl->tty, sl->lin_baud);
 
-       write(tty, &buff[1], 1); /* Sync Byte Field */
-       write(tty, &buff[2], 1); /* PID -- Protected Identifier Field */
+       write(sl->tty->tty_fd, &buff[1], 1); /* Sync Byte Field */
+       write(sl->tty->tty_fd, &buff[2], 1); /* PID -- Protected Identifier Field */
        return 0;
 }
 
-int read_header(int tty)
+int read_header(struct sllin *sl)
 {
        int p0, p1; /* Parity bits */
        int par_rec; /* Parity received as a part of a packet */
@@ -170,14 +309,14 @@ int read_header(int tty)
        memset(buff, '\0', sizeof(buff));
 
        while (1) {
-               received = read(tty, &buff[0], 1);
+               received = read(sl->tty->tty_fd, &buff[0], 1);
                if (received == -1)
                        perror("read()");
                
                if (buff[0] != 0x55) /* Sync byte field */
                        continue;
 
-               received = read(tty, &buff[1], 1);
+               received = read(sl->tty->tty_fd, &buff[1], 1);
                if (received == -1)
                        perror("read()");
                else
@@ -202,43 +341,135 @@ int read_header(int tty)
        return 0;
 }
 
+int parse_arr(unsigned char *buff, const char *str, int len_max)
+{
+       char *p;
+       int len = 0;
+
+       do {
+               if (len >= len_max)
+                       return -1;
+               
+               *buff = strtol(str, &p, 0);
+               if(str == p)
+                       return -1;
+
+               str = p;
+
+               len++;
+               buff++;
+       } while (*(str++) == ',');
+
+       if (*(--str) != '\0')
+               return -1;
+
+       return len;
+}
+
+static void usage(void)
+{
+       printf("Usage: lin_master <parameters>\n\n");
+       printf("Mandatory parameters:\n");
+       printf("  -d, --device <device>   Device name, e.g. /dev/ttyS0\n\n");
+       printf("Optional parameters:\n");
+       printf("  -B, --baud <baud>       LIN bus baudrate. Default value is 19200.\n");
+       printf("                          Recommendet values are 2400, 9600, 19200\n");
+       printf("  -i, --id <num>          LIN frame ID\n");
+       printf("  -r, --response <num>    Values of data fields sent from slave task\n");
+       printf("  -h, --help              Help, i.e. this screen\n");
+}
 
 int main(int argc, char* argv[])
 {
-       char dev[32];
-       int tty;
+       struct sllin *sl = &sllin_data;
+
+       static struct option long_opts[] = {
+               {"device"  , 1, 0, 'd'},
+               {"baud"    , 1, 0, 'B'},
+               {"id"      , 1, 0, 'i'},
+               {"response", 1, 0, 'r'},
+               {"help"    , 0, 0, 'h'},
+               {0, 0, 0, 0}
+       };
+       int opt;
+       char *dev_fname = "";
+       int lin_baudrate = 19200;
+       int lin_id = 1;
+       int resp_len = 0;
+       unsigned char resp_data[SLLIN_DATA_MAX + 1];
+       char *p;
+
+       while ((opt = getopt_long(argc, argv, "d:B:i:r:h", &long_opts[0], NULL)) != EOF) {
+               switch (opt) {
+                       case 'd':
+                               dev_fname = optarg;
+                               break;
+
+                       case 'B':
+                               lin_baudrate = strtol(optarg, &p, 10);
+                               if ((p == optarg) || ((*p != '\0') && (*p != ' '))) {
+                                       fprintf(stderr, "Baudrate format error\n");
+                                       exit(EXIT_FAILURE);
+                               }
+                               break;
+
+                       case 'i':
+                               lin_id = strtol(optarg, &p, 0);
+                               if ((p == optarg) || ((*p != '\0') && (*p != ' '))) {
+                                       fprintf(stderr, "LIN ID format error\n");
+                                       exit(EXIT_FAILURE);
+                               }
+                               break;
+
+                       case 'r':
+                               resp_len = parse_arr(resp_data, optarg, SLLIN_DATA_MAX);
+                               if (resp_len < 0) {
+                                       fprintf(stderr, "Response data format error\n");
+                                       exit(EXIT_FAILURE);
+                               }
+                               break;
+
+                       case 'h':
+                       default:
+                               usage();
+                               exit(opt == 'h' ? 0 : 1);
+                               break;
+               }
+       }
 
-       if (argc < 2) {
-               fprintf(stderr, "Device is missing\n");
-               fprintf(stderr, "Usage: %s DEVICE\n", argv[0]);
-               return -3;
+       if (optind < argc) {
+               usage();
+               //fprintf(stderr, "Expected argument after options\n");
+               exit(EXIT_FAILURE);
        }
 
-       strncpy((char*)&dev, argv[1], 32);
-       tty = open(dev, O_RDWR);
-       if (tty < 0) {
-               perror("open()");
-               return -4;
+       /* Device name was not set by user */
+       if (strlen(dev_fname) == 0) {
+               usage();
+               exit(EXIT_FAILURE);
+       }
+
+       /* ----------------------------------- */
+       if (sllin_open(sl, dev_fname, lin_baudrate) < 0) {
+               fprintf (stderr, "sllin_open open failed\n");
+               exit(EXIT_FAILURE);
        }
 
        fcntl(fileno(stdin), F_SETFL, O_NONBLOCK);
        printf("Press enter to terminate.\n\n");
 
-       /* Configure UART */
-       set_input_mode(tty);
 
        while(1) {
                char c;
 
-               send_header(tty);
+               send_header(sl, lin_id);
                sleep(1);
 
                if (read(fileno(stdin), &c, 1) > 0)
                        break;
        }
 
-       reset_input_mode(tty);
-       close(tty);
+       sllin_close(sl);
 
        return EXIT_SUCCESS;
 }