X-Git-Url: http://rtime.felk.cvut.cz/gitweb/linux-lin.git/blobdiff_plain/399c19f1b3e62f76c7f5baf29a2b5c366d341335..6c1650d8219652b18fc0989a194aa6e196f27fc0:/sllin/sllin.c diff --git a/sllin/sllin.c b/sllin/sllin.c index 6a4e8bf..671ba1a 100644 --- a/sllin/sllin.c +++ b/sllin/sllin.c @@ -1,4 +1,4 @@ -/* +/* * sllin.c - serial line LIN interface driver (using tty line discipline) * * This file is derived from drivers/net/can/slcan.c @@ -32,8 +32,13 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * - * Send feedback to - * + * Idea: Oliver Hartkopp + * Copyright: (c) 2011 Czech Technical University in Prague + * (c) 2011 Volkswagen Group Research + * Authors: Pavel Pisa + * Rostislav Lisovy + * Michal Sojka + * Funded by: Volkswagen Group Research */ #define DEBUG 1 /* Enables pr_debug() printouts */ @@ -70,11 +75,19 @@ static __initdata const char banner[] = MODULE_ALIAS_LDISC(N_SLLIN); MODULE_DESCRIPTION("serial line LIN interface"); MODULE_LICENSE("GPL"); -MODULE_AUTHOR(""); +MODULE_AUTHOR("Pavel Pisa "); #define SLLIN_MAGIC 0x53CA /* #define BREAK_BY_BAUD */ +static int master = true; +static int baudrate = 0; /* Use LIN_DEFAULT_BAUDRATE when not set */ + +module_param(master, bool, 0); +MODULE_PARM_DESC(master, "LIN interface is Master device"); +module_param(baudrate, int, 0); +MODULE_PARM_DESC(baudrate, "Baudrate of LIN interface"); + static int maxdev = 10; /* MAX number of SLLIN channels; This can be overridden with insmod sllin.ko maxdev=nnn */ @@ -91,7 +104,7 @@ MODULE_PARM_DESC(maxdev, "Maximum number of sllin interfaces"); #define SLLIN_BUFF_DATA 3 #define SLLIN_SAMPLES_PER_CHAR 10 -#define SLLIN_CHARS_TO_TIMEOUT 12 +#define SLLIN_CHARS_TO_TIMEOUT 24 enum slstate { SLSTATE_IDLE = 0, @@ -133,6 +146,8 @@ struct sllin { char id_to_send; /* there is ID to be sent */ char data_to_send; /* there are data to be sent */ char resp_len_known; /* Length of the response is known */ + char header_received;/* In Slave mode, set when header was already + received */ unsigned long flags; /* Flag values/ mode etc */ #define SLF_INUSE 0 /* Channel in use */ @@ -188,10 +203,16 @@ static int sltty_change_speed(struct tty_struct *tty, unsigned speed) mutex_lock(&tty->termios_mutex); old_termios = *(tty->termios); - cflag = tty->termios->c_cflag; + + cflag = CS8 | CREAD | CLOCAL | HUPCL; cflag &= ~(CBAUD | CIBAUD); cflag |= BOTHER; tty->termios->c_cflag = cflag; + tty->termios->c_oflag = 0; + tty->termios->c_lflag = 0; + + /* Enable interrupt when UART-Break or Framing error received */ + tty->termios->c_iflag = BRKINT | INPCK; tty_encode_baud_rate(tty, speed, speed); @@ -203,7 +224,6 @@ static int sltty_change_speed(struct tty_struct *tty, unsigned speed) return 0; } - /* Send one can_frame to the network layer */ static void sllin_send_canfr(struct sllin *sl, canid_t id, char *data, int len) { @@ -245,6 +265,12 @@ static void sll_bump(struct sllin *sl) sl->rx_cnt - SLLIN_BUFF_DATA - 1); /* without checksum */ } +static void sll_send_rtr(struct sllin *sl) +{ + sllin_send_canfr(sl, (sl->rx_buff[SLLIN_BUFF_ID] & LIN_ID_MASK) | + CAN_RTR_FLAG, NULL, 0); +} + /* * Called by the driver when there's room for more data. If we have * more packets to send, we send them here. @@ -407,7 +433,7 @@ static void sllin_receive_buf(struct tty_struct *tty, { struct sllin *sl = (struct sllin *) tty->disc_data; - pr_debug("sllin: sllin_receive_buf invoked\n"); + pr_debug("sllin: sllin_receive_buf invoked, count = %u\n", count); if (!sl || sl->magic != SLLIN_MAGIC || !netif_running(sl->dev)) return; @@ -415,19 +441,33 @@ static void sllin_receive_buf(struct tty_struct *tty, /* Read the characters out of the buffer */ while (count--) { if (fp && *fp++) { - if (!test_and_set_bit(SLF_ERROR, &sl->flags)) - sl->dev->stats.rx_errors++; - pr_debug("sllin: sllin_receive_buf char 0x%02x ignored " - "due marker 0x%02x, flags 0x%lx\n", - *cp, *(fp-1), sl->flags); - cp++; - continue; + if (sl->rx_cnt > SLLIN_BUFF_BREAK) { + set_bit(SLF_ERROR, &sl->flags); + + pr_debug("sllin: sllin_receive_buf char 0x%02x ignored " + "due marker 0x%02x, flags 0x%lx\n", + *cp, *(fp-1), sl->flags); + + if (sl->lin_master == true) { + wake_up(&sl->kwt_wq); + return; + } else { + sl->rx_cnt = 0; + sl->rx_expect = SLLIN_BUFF_ID + 1; + sl->header_received = false; + return; + } + + cp++; + continue; + } } if (sl->rx_cnt < SLLIN_BUFF_LEN) { #ifndef BREAK_BY_BAUD - /* We didn't receive Break character */ + /* We didn't receive Break character -- fake it! */ if ((sl->rx_cnt == SLLIN_BUFF_BREAK) && (*cp == 0x55)) { + pr_debug("sllin: LIN_RX[%d]: 0x00\n", sl->rx_cnt); sl->rx_buff[sl->rx_cnt++] = 0x00; } #endif @@ -436,12 +476,45 @@ static void sllin_receive_buf(struct tty_struct *tty, } } - if (sl->rx_cnt >= sl->rx_expect) { - set_bit(SLF_RXEVENT, &sl->flags); - wake_up(&sl->kwt_wq); - pr_debug("sllin: sllin_receive_buf count %d, wakeup\n", sl->rx_cnt); - } else { - pr_debug("sllin: sllin_receive_buf count %d, waiting\n", sl->rx_cnt); + if (sl->lin_master == true) { + if (sl->rx_cnt >= sl->rx_expect) { /* Probably whole frame was received */ + set_bit(SLF_RXEVENT, &sl->flags); + wake_up(&sl->kwt_wq); + pr_debug("sllin: sllin_receive_buf count %d, wakeup\n", sl->rx_cnt); + } else { + pr_debug("sllin: sllin_receive_buf count %d, waiting\n", sl->rx_cnt); + } + } else { /* LIN slave */ + int lin_id; + struct sllin_conf_entry *sce; + + /* Received whole header */ + if ((sl->rx_cnt >= (SLLIN_BUFF_ID + 1)) && + (sl->header_received == false)) + { + lin_id = sl->rx_buff[SLLIN_BUFF_ID] & LIN_ID_MASK; + sce = &sl->linfr_cache[lin_id]; + + /* Is the length of data set in frame cache? */ + if (sce->frame_fl & LIN_LOC_SLAVE_CACHE) + sl->rx_expect += sce->dlc; + else + sl->rx_expect += 2; /* 1 data byte + checksum */ + + sl->header_received = true; + sll_send_rtr(sl); + } + + /* Probably whole frame was received */ + if ((sl->rx_cnt >= sl->rx_expect) && (sl->rx_cnt > SLLIN_BUFF_DATA)) { + sll_bump(sl); + pr_debug("sllin: Received LIN header & LIN response. " + "rx_cnt = %u, rx_expect = %u\n", sl->rx_cnt, + sl->rx_expect); + sl->rx_cnt = 0; + sl->rx_expect = SLLIN_BUFF_ID + 1; + sl->header_received = false; + } } } @@ -462,9 +535,13 @@ void sllin_report_error(struct sllin *sl, int err) sl->dev->stats.rx_crc_errors++; break; - case LIN_ERR_RX_TIMEOUT: + case LIN_ERR_RX_TIMEOUT: sl->dev->stats.rx_errors++; break; + + case LIN_ERR_FRAMING: + sl->dev->stats.rx_frame_errors++; + break; } sllin_send_canfr(sl, 0 | CAN_EFF_FLAG | @@ -561,6 +638,16 @@ int sllin_setup_msg(struct sllin *sl, int mode, int id, return 0; } +static void sllin_reset_buffs(struct sllin *sl) +{ + sl->rx_cnt = 0; + sl->rx_expect = 0; + sl->rx_lim = sl->lin_master ? 0 : SLLIN_BUFF_LEN; + sl->tx_cnt = 0; + sl->tx_lim = 0; + sl->id_to_send = false; + sl->data_to_send = false; +} int sllin_send_tx_buff(struct sllin *sl) { @@ -752,6 +839,7 @@ int sllin_kwthread(void *ptr) test_bit(SLF_RXEVENT, &sl->flags) || test_bit(SLF_TXEVENT, &sl->flags) || test_bit(SLF_TMOUTEVENT, &sl->flags) || + test_bit(SLF_ERROR, &sl->flags) || (((sl->lin_state == SLSTATE_IDLE) || (sl->lin_state == SLSTATE_RESPONSE_WAIT)) && test_bit(SLF_MSGEVENT, &sl->flags))); @@ -760,19 +848,30 @@ int sllin_kwthread(void *ptr) pr_debug("sllin: sllin_kthread RXEVENT\n"); } + if (test_and_clear_bit(SLF_ERROR, &sl->flags)) { + unsigned long usleep_range_min; + unsigned long usleep_range_max; + hrtimer_cancel(&sl->rx_timer); + pr_debug("sllin: sllin_kthread ERROR\n"); + + if (sl->lin_state != SLSTATE_IDLE) + sllin_report_error(sl, LIN_ERR_FRAMING); + + usleep_range_min = (1000000l * SLLIN_SAMPLES_PER_CHAR * 10) / + sl->lin_baud; + usleep_range_max = usleep_range_min + 50; + usleep_range(usleep_range_min, usleep_range_max); + sllin_reset_buffs(sl); + sl->lin_state = SLSTATE_IDLE; + } + if (test_and_clear_bit(SLF_TXEVENT, &sl->flags)) { pr_debug("sllin: sllin_kthread TXEVENT\n"); } if (test_and_clear_bit(SLF_TMOUTEVENT, &sl->flags)) { pr_debug("sllin: sllin_kthread TMOUTEVENT\n"); - sl->rx_cnt = 0; - sl->rx_expect = 0; - sl->rx_lim = sl->lin_master ? 0 : SLLIN_BUFF_LEN; - sl->tx_cnt = 0; - sl->tx_lim = 0; - sl->id_to_send = false; - sl->data_to_send = false; + sllin_reset_buffs(sl); sl->lin_state = SLSTATE_IDLE; } @@ -879,6 +978,8 @@ int sllin_kwthread(void *ptr) lin_buff = (sl->lin_master) ? sl->tx_buff : sl->rx_buff; if (cf->can_id == (lin_buff[SLLIN_BUFF_ID] & LIN_ID_MASK)) { + hrtimer_cancel(&sl->rx_timer); + pr_debug("sllin: received LIN response in a CAN frame.\n"); if (sllin_setup_msg(sl, SLLIN_STPMSG_RESPONLY, cf->can_id & LIN_ID_MASK, cf->data, cf->can_dlc) != -1) { @@ -933,6 +1034,7 @@ int sllin_kwthread(void *ptr) if (sl->rx_cnt < sl->tx_lim) continue; + hrtimer_cancel(&sl->rx_timer); sll_bump(sl); /* send packet to the network layer */ pr_debug("sllin: response sent ID %d len %d\n", sl->rx_buff[SLLIN_BUFF_ID], sl->rx_cnt - SLLIN_BUFF_DATA - 1); @@ -1074,17 +1176,18 @@ static int sllin_open(struct tty_struct *tty) if (!test_bit(SLF_INUSE, &sl->flags)) { /* Perform the low-level SLLIN initialization. */ - sl->lin_master = true; + sl->lin_master = master; +#ifdef DEBUG + if (master) + pr_debug("sllin: Configured as MASTER\n"); + else + pr_debug("sllin: Configured as SLAVE\n"); +#endif - sl->rx_cnt = 0; - sl->rx_expect = 0; - sl->rx_lim = sl->lin_master ? 0 : SLLIN_BUFF_LEN; - sl->tx_cnt = 0; - sl->tx_lim = 0; - sl->id_to_send = false; - sl->data_to_send = false; + sllin_reset_buffs(sl); - sl->lin_baud = 19200; + sl->lin_baud = (baudrate == 0) ? LIN_DEFAULT_BAUDRATE : baudrate; + pr_debug("sllin: Baudrate set to %u\n", sl->lin_baud); sl->lin_state = SLSTATE_IDLE;