From 86a5f1e8ecb8de5df5ba9683a9c3a4d6460c20e9 Mon Sep 17 00:00:00 2001 From: Pavel Pisa Date: Fri, 25 Nov 2011 01:39:25 +0100 Subject: [PATCH] Userspace LIN master: Provide state encapsulation to test kernel code. Signed-off-by: Pavel Pisa --- misc/tty_lin_master/Makefile | 25 ++- misc/tty_lin_master/lin_common.c | 51 ++++++ misc/tty_lin_master/lin_common.h | 59 +++++++ misc/tty_lin_master/main.c | 267 ++++++++++++++++++++----------- 4 files changed, 310 insertions(+), 92 deletions(-) create mode 100644 misc/tty_lin_master/lin_common.c create mode 100644 misc/tty_lin_master/lin_common.h diff --git a/misc/tty_lin_master/Makefile b/misc/tty_lin_master/Makefile index 06443ed..fa01dcf 100644 --- a/misc/tty_lin_master/Makefile +++ b/misc/tty_lin_master/Makefile @@ -1,2 +1,23 @@ -all: main.c - gcc main.c -lrt -std=gnu99 -Wall -pedantic -o main +all: default + +CC = gcc +CFLAGS = -std=gnu99 -Wall -pedantic -ggdb +LDFLAGS = -lrt -ggdb + +.PHONY: default dep + +default: main + +main : main.o lin_common.o + +dep: + $(CC) $(CFLAGS) $(CPPFLAGS) -w -E -M *.c $(MORE_C_FILES) > depend + +depend: + @touch depend + +clean : + rm -f *.o *~ depend + +-include depend + diff --git a/misc/tty_lin_master/lin_common.c b/misc/tty_lin_master/lin_common.c new file mode 100644 index 0000000..6fffafc --- /dev/null +++ b/misc/tty_lin_master/lin_common.c @@ -0,0 +1,51 @@ +#include +#include "lin_common.h" + +const unsigned char sllin_id_parity_table[64] = { + 0x80,0xc0,0x40,0x00,0xc0,0x80,0x00,0x40, + 0x00,0x40,0xc0,0x80,0x40,0x00,0x80,0xc0, + 0x40,0x00,0x80,0xc0,0x00,0x40,0xc0,0x80, + 0xc0,0x80,0x00,0x40,0x80,0xc0,0x40,0x00, + 0x00,0x40,0xc0,0x80,0x40,0x00,0x80,0xc0, + 0x80,0xc0,0x40,0x00,0xc0,0x80,0x00,0x40, + 0xc0,0x80,0x00,0x40,0x80,0xc0,0x40,0x00, + 0x40,0x00,0x80,0xc0,0x00,0x40,0xc0,0x80 +}; + + +int sllin_setup_msg(struct sllin *sl, int mode, int id, + unsigned char *data, int len) +{ + if (id > 0x3f) + return -1; + + sl->rx_cnt = 0; + sl->tx_cnt = 0; + sl->rx_expect = 0; + + sl->tx_buff[SLLIN_BUFF_BREAK] = 0; + sl->tx_buff[SLLIN_BUFF_SYNC] = 0x55; + sl->tx_buff[SLLIN_BUFF_ID] = id | sllin_id_parity_table[id]; + sl->tx_lim = SLLIN_BUFF_DATA; + + if ((data != NULL) && len) { + int i; + unsigned csum = 0; + + sl->tx_lim += len; + memcpy(sl->tx_buff + SLLIN_BUFF_DATA, data, len); + /* compute data parity there */ + for (i = SLLIN_BUFF_DATA; i < sl->tx_lim; i++) { + csum += sl->tx_buff[i]; + if (csum > 255) + csum -= 255; + } + + sl->tx_buff[sl->tx_lim++] = csum; + } + if (len != 0) + sl->rx_lim += len + 1; + + return 0; +} + diff --git a/misc/tty_lin_master/lin_common.h b/misc/tty_lin_master/lin_common.h new file mode 100644 index 0000000..74cbe71 --- /dev/null +++ b/misc/tty_lin_master/lin_common.h @@ -0,0 +1,59 @@ +#ifndef _LIN_COMMON_H +#define _LIN_COMMON_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* maximum buffer len to store whole LIN message*/ +#define SLLIN_DATA_MAX 8 +#define SLLIN_BUFF_LEN (1 /*break*/ + 1 /*sync*/ + 1 /*ID*/ + \ + SLLIN_DATA_MAX + 1 /*checksum*/) +#define SLLIN_BUFF_BREAK 0 +#define SLLIN_BUFF_SYNC 1 +#define SLLIN_BUFF_ID 2 +#define SLLIN_BUFF_DATA 3 + +extern const unsigned char sllin_id_parity_table[64]; + +struct sllin_tty; + +struct sllin { + /* Various fields. */ + struct sllin_tty *tty; /* ptr to TTY structure */ + + /* LIN message buffer and actual processed data counts */ + unsigned char rx_buff[SLLIN_BUFF_LEN]; /* LIN Rx buffer */ + unsigned char tx_buff[SLLIN_BUFF_LEN]; /* LIN Tx buffer */ + int rx_expect; /* expected number of Rx chars */ + int rx_lim; /* maximum Rx chars for ID */ + int rx_cnt; /* message buffer Rx fill level */ + int tx_lim; /* actual limit of bytes to Tx */ + int tx_cnt; /* number of already Tx bytes */ + char lin_master; /* node is a master node */ + int lin_baud; /* LIN baudrate */ + int lin_break_baud; /* Baudrate used for break send */ + int lin_state; /* state */ + int id_to_send; /* there is ID to be sent */ + + unsigned long flags; /* Flag values/ mode etc */ +#define SLF_INUSE 0 /* Channel in use */ +#define SLF_ERROR 1 /* Parity, etc. error */ +#define SLF_RXEVENT 2 /* Rx wake event */ +#define SLF_TXEVENT 3 /* Tx wake event */ +#define SLF_MSGEVENT 4 /* CAN message to sent */ +}; + +int sllin_setup_msg(struct sllin *sl, int mode, int id, + unsigned char *data, int len); + + +#ifdef __cplusplus +} /* extern "C"*/ +#endif + + +#endif /*_LIN_COMMON_H*/ diff --git a/misc/tty_lin_master/main.c b/misc/tty_lin_master/main.c index 7da775f..8d490c9 100644 --- a/misc/tty_lin_master/main.c +++ b/misc/tty_lin_master/main.c @@ -15,153 +15,199 @@ #include #include /* clock_nanosleep */ #include +#include "lin_common.h" #define LIN_HDR_SIZE 2 -#define LIN_PKT_MAX_SIZE 16 /* FIXME */ -int lin_baudrate = 19200; -int lin_break_baud = 0; +struct sllin_tty { + int tty_fd; + struct termios tattr_orig; + struct termios tattr; + struct serial_struct sattr; +}; + +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) +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 void set_input_mode(int tty) +static int tty_set_mode(struct sllin_tty *tty, int baudrate) { - /* Flush input and output queues. */ - if (tcflush(tty, TCIOFLUSH) != 0) { - perror("tcflush"); - exit(EXIT_FAILURE); + if(!isatty(tty->tty_fd)) { + fprintf(stderr, "Not a terminal.\n"); + return -1; } - if(!isatty(tty)) { - fprintf(stderr, "Not a terminal.\n"); - exit(EXIT_FAILURE); + /* Flush input and output queues. */ + if (tcflush(tty->tty_fd, TCIOFLUSH) != 0) { + perror("tcflush"); + return -1;; } /* Save settings for later restoring */ - tcgetattr(tty, &tattr_orig); + tcgetattr(tty->tty_fd, &tty->tattr_orig); /* Save settings into global variables for later use */ - tcgetattr(tty, &tattr); - if (ioctl(tty, TIOCGSERIAL, &sattr) < 0) - perror("ioctl()"); + if (tcgetattr(tty->tty_fd, &tty->tattr) < 0) + perror("tcgetattr"); + + if (ioctl(tty->tty_fd, TIOCGSERIAL, &tty->sattr) < 0) + perror("ioctl TIOCGSERIAL"); /* 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 /* 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; + } /* 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); + tcflush(sl->tty->tty_fd, 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 */ @@ -171,14 +217,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 @@ -203,6 +249,31 @@ 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 \n\n"); @@ -218,6 +289,8 @@ static void usage(void) int main(int argc, char* argv[]) { + struct sllin *sl = &sllin_data; + static struct option long_opts[] = { {"device" , 1, 0, 'd'}, {"baud" , 1, 0, 'B'}, @@ -227,23 +300,41 @@ int main(int argc, char* argv[]) {0, 0, 0, 0} }; int opt; - char dev[32] = {'\0'}; - int tty; + 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': - strncpy((char*)&dev, optarg, 32); + dev_fname = optarg; break; case 'B': - lin_baudrate = atoi(optarg); + 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': @@ -261,36 +352,32 @@ int main(int argc, char* argv[]) } /* Device name was not set by user */ - if (strlen(dev) == 0) { + if (strlen(dev_fname) == 0) { usage(); exit(EXIT_FAILURE); } /* ----------------------------------- */ - tty = open(dev, O_RDWR); - if (tty < 0) { - perror("open()"); - return -4; + 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; } -- 2.39.2