X-Git-Url: http://rtime.felk.cvut.cz/gitweb/linux-lin.git/blobdiff_plain/4d395daeab8a91e2eb519fba8b213a84f33d1ea0..615d01fa409911a54dbde55f19c15182e7e849bc:/sllin/sllin.c diff --git a/sllin/sllin.c b/sllin/sllin.c index 36f23a5..3437631 100644 --- a/sllin/sllin.c +++ b/sllin/sllin.c @@ -62,6 +62,7 @@ #include #include #include +#include #include "linux/lin_bus.h" /* Should be in include/linux/tty.h */ @@ -113,6 +114,7 @@ enum slstate { SLSTATE_RESPONSE_WAIT_BUS, /* Wait for response from LIN bus only (CAN frames from network stack are not processed in this moment) */ + SLSTATE_ID_RECEIVED, SLSTATE_RESPONSE_SENT, }; @@ -205,29 +207,46 @@ const unsigned char sllin_id_parity_table[] = { */ static int sltty_change_speed(struct tty_struct *tty, unsigned speed) { - struct ktermios old_termios; + struct ktermios old_termios, termios; int cflag; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 0) mutex_lock(&tty->termios_mutex); +#else + down_write(&tty->termios_rwsem); +#endif - old_termios = *(tty->termios); +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0) + old_termios = termios = *(tty->termios); +#else + old_termios = termios = tty->termios; +#endif 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; + termios.c_cflag = cflag; + termios.c_oflag = 0; + termios.c_lflag = 0; /* Enable interrupt when UART-Break or Framing error received */ - tty->termios->c_iflag = BRKINT | INPCK; + termios.c_iflag = BRKINT | INPCK; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0) + *(tty->termios) = termios; +#else + tty->termios = termios; +#endif tty_encode_baud_rate(tty, speed, speed); if (tty->ops->set_termios) tty->ops->set_termios(tty, &old_termios); +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 0) mutex_unlock(&tty->termios_mutex); +#else + up_write(&tty->termios_rwsem); +#endif return 0; } @@ -300,7 +319,12 @@ static void sllin_write_wakeup(struct tty_struct *tty) return; /* ongoing concurrent processing */ clear_bit(SLF_TXBUFF_RQ, &sl->flags); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0) smp_mb__after_clear_bit(); +#else + smp_mb__after_atomic(); +#endif if (sl->lin_state != SLSTATE_BREAK_SENT) remains = sl->tx_lim - sl->tx_cnt; @@ -314,7 +338,11 @@ static void sllin_write_wakeup(struct tty_struct *tty) remains -= actual; } clear_bit(SLF_TXBUFF_INPR, &sl->flags); +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0) smp_mb__after_clear_bit(); +#else + smp_mb__after_atomic(); +#endif } while (unlikely(test_bit(SLF_TXBUFF_RQ, &sl->flags))); @@ -506,6 +534,9 @@ static void sllin_master_receive_buf(struct tty_struct *tty, */ static void sllin_report_error(struct sllin *sl, int err) { + unsigned char *lin_buff; + int lin_id; + switch (err) { case LIN_ERR_CHECKSUM: sl->dev->stats.rx_crc_errors++; @@ -520,7 +551,9 @@ static void sllin_report_error(struct sllin *sl, int err) break; } - sllin_send_canfr(sl, 0 | CAN_EFF_FLAG | + lin_buff = (sl->lin_master) ? sl->tx_buff : sl->rx_buff; + lin_id = lin_buff[SLLIN_BUFF_ID] & LIN_ID_MASK; + sllin_send_canfr(sl, lin_id | CAN_EFF_FLAG | (err & ~LIN_ID_MASK), NULL, 0); } @@ -700,10 +733,12 @@ static void sllin_slave_receive_buf(struct tty_struct *tty, if (fp && *fp++) { /* * If we don't know the length of the current message - * we received the break of the next message. - * Evaluate the previous one before continuing + * and received at least the LIN ID, we received here + * the break of the next message. + * Evaluate the previous one before continuing. */ - if (sl->rx_len_unknown == true) + if ((sl->rx_len_unknown == true) && + (sl->rx_cnt >= SLLIN_BUFF_ID)) { hrtimer_cancel(&sl->rx_timer); sllin_slave_finish_rx_msg(sl); @@ -755,10 +790,12 @@ static void sllin_slave_receive_buf(struct tty_struct *tty, spin_lock_irqsave(&sl->linfr_lock, flags); + sl->lin_state = SLSTATE_ID_RECEIVED; /* Is the length of data set in frame cache? */ - if (sce->frame_fl & LIN_CACHE_RESPONSE) { + if (sce->dlc > 0) { sl->rx_expect += sce->dlc + 1; /* + checksum */ sl->rx_len_unknown = false; + wake_up(&sl->kwt_wq); } else { sl->rx_expect += SLLIN_DATA_MAX + 1; /* + checksum */ sl->rx_len_unknown = true; @@ -784,6 +821,8 @@ static void sllin_slave_receive_buf(struct tty_struct *tty, sl->rx_expect); sllin_slave_finish_rx_msg(sl); + set_bit(SLF_RXEVENT, &sl->flags); + wake_up(&sl->kwt_wq); } } } @@ -816,7 +855,11 @@ static int sllin_send_tx_buff(struct sllin *sl) return 0; /* ongoing concurrent processing */ clear_bit(SLF_TXBUFF_RQ, &sl->flags); +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0) smp_mb__after_clear_bit(); +#else + smp_mb__after_atomic(); +#endif #ifdef BREAK_BY_BAUD if (sl->lin_state != SLSTATE_BREAK_SENT) @@ -850,7 +893,11 @@ static int sllin_send_tx_buff(struct sllin *sl) sl->tx_cnt, remains); clear_bit(SLF_TXBUFF_INPR, &sl->flags); +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0) smp_mb__after_clear_bit(); +#else + smp_mb__after_atomic(); +#endif } while (unlikely(test_bit(SLF_TXBUFF_RQ, &sl->flags))); @@ -934,7 +981,16 @@ static enum hrtimer_restart sllin_rx_timeout_handler(struct hrtimer *hrtimer) { struct sllin *sl = container_of(hrtimer, struct sllin, rx_timer); - if (sl->lin_master) { + /* + * Signal timeout when: + * master: We did not receive as much characters as expected + * slave: * we did not receive any data bytes at all + * * we know the length and didn't receive enough + */ + if ((sl->lin_master) || + (sl->rx_cnt <= SLLIN_BUFF_DATA) || + ((!sl->rx_len_unknown) && + (sl->rx_cnt < sl->rx_expect))) { sllin_report_error(sl, LIN_ERR_RX_TIMEOUT); set_bit(SLF_TMOUTEVENT, &sl->flags); } else { @@ -956,7 +1012,10 @@ static int sllin_kwthread(void *ptr) struct tty_struct *tty = sl->tty; struct sched_param schparam = { .sched_priority = 40 }; int tx_bytes = 0; /* Used for Network statistics */ - + unsigned long flags; + int mode; + int lin_id; + struct sllin_conf_entry *sce; netdev_dbg(sl->dev, "sllin_kwthread started.\n"); sched_setscheduler(current, SCHED_FIFO, &schparam); @@ -983,6 +1042,7 @@ static int sllin_kwthread(void *ptr) test_bit(SLF_TXEVENT, &sl->flags) || test_bit(SLF_TMOUTEVENT, &sl->flags) || test_bit(SLF_ERROR, &sl->flags) || + (sl->lin_state == SLSTATE_ID_RECEIVED) || (((sl->lin_state == SLSTATE_IDLE) || (sl->lin_state == SLSTATE_RESPONSE_WAIT)) && test_bit(SLF_MSGEVENT, &sl->flags))); @@ -1024,11 +1084,14 @@ static int sllin_kwthread(void *ptr) if (!test_bit(SLF_MSGEVENT, &sl->flags)) break; + mode = 0; cf = (struct can_frame *)sl->tx_req_skb->data; + if (cf->can_id & LIN_CHECKSUM_EXTENDED) + mode |= SLLIN_STPMSG_CHCKSUM_ENH; + /* SFF RTR CAN frame -> LIN header */ if (cf->can_id & CAN_RTR_FLAG) { - unsigned long flags; struct sllin_conf_entry *sce; netdev_dbg(sl->dev, "%s: RTR SFF CAN frame, ID = %x\n", @@ -1036,6 +1099,8 @@ static int sllin_kwthread(void *ptr) sce = &sl->linfr_cache[cf->can_id & LIN_ID_MASK]; spin_lock_irqsave(&sl->linfr_lock, flags); + if (sce->frame_fl & LIN_CHECKSUM_EXTENDED) + mode |= SLLIN_STPMSG_CHCKSUM_ENH; /* Is there Slave response in linfr_cache to be sent? */ if ((sce->frame_fl & LIN_CACHE_RESPONSE) @@ -1056,9 +1121,15 @@ static int sllin_kwthread(void *ptr) spin_unlock_irqrestore(&sl->linfr_lock, flags); } else { /* SFF NON-RTR CAN frame -> LIN header + LIN response */ + struct sllin_conf_entry *sce; + netdev_dbg(sl->dev, "%s: NON-RTR SFF CAN frame, ID = %x\n", __func__, (int)cf->can_id & LIN_ID_MASK); + sce = &sl->linfr_cache[cf->can_id & LIN_ID_MASK]; + if (sce->frame_fl & LIN_CHECKSUM_EXTENDED) + mode |= SLLIN_STPMSG_CHCKSUM_ENH; + lin_data = cf->data; lin_dlc = cf->can_dlc; if (lin_dlc > SLLIN_DATA_MAX) @@ -1066,7 +1137,7 @@ static int sllin_kwthread(void *ptr) tx_bytes = lin_dlc; } - if (sllin_setup_msg(sl, 0, cf->can_id & LIN_ID_MASK, + if (sllin_setup_msg(sl, mode, cf->can_id & LIN_ID_MASK, lin_data, lin_dlc) != -1) { sl->id_to_send = true; @@ -1177,6 +1248,51 @@ slstate_response_wait: sl->lin_state = SLSTATE_IDLE; break; + case SLSTATE_ID_RECEIVED: + lin_id = sl->rx_buff[SLLIN_BUFF_ID] & LIN_ID_MASK; + sce = &sl->linfr_cache[lin_id]; + spin_lock_irqsave(&sl->linfr_lock, flags); + + if ((sce->frame_fl & LIN_CACHE_RESPONSE) + && (sce->dlc > 0)) { + + netdev_dbg(sl->dev, "Sending LIN response from linfr_cache\n"); + + lin_data = sce->data; + lin_dlc = sce->dlc; + if (lin_dlc > SLLIN_DATA_MAX) + lin_dlc = SLLIN_DATA_MAX; + memcpy(lin_data_buff, lin_data, lin_dlc); + lin_data = lin_data_buff; + tx_bytes = lin_dlc; + + mode = SLLIN_STPMSG_RESPONLY; + if (sce->frame_fl & LIN_CHECKSUM_EXTENDED) + mode |= SLLIN_STPMSG_CHCKSUM_ENH; + + if (sllin_setup_msg(sl, mode, lin_id & LIN_ID_MASK, + lin_data, lin_dlc) != -1) { + + sl->rx_expect = sl->tx_lim; + sl->data_to_send = true; + sl->dev->stats.tx_packets++; + sl->dev->stats.tx_bytes += tx_bytes; + sl->resp_len_known = true; + + if (!sl->lin_master) { + sl->tx_cnt = SLLIN_BUFF_DATA; + } + sllin_send_tx_buff(sl); + } + + hrtimer_start(&sl->rx_timer, + ktime_add(ktime_get(), sl->rx_timer_timeout), + HRTIMER_MODE_ABS); + } + spin_unlock_irqrestore(&sl->linfr_lock, flags); + sl->lin_state = SLSTATE_IDLE; + break; + case SLSTATE_RESPONSE_SENT: slstate_response_sent: if (sl->rx_cnt < sl->tx_lim) @@ -1258,7 +1374,12 @@ static struct sllin *sll_alloc(dev_t line) char name[IFNAMSIZ]; sprintf(name, "sllin%d", i); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)) dev = alloc_netdev(sizeof(*sl), name, sll_setup); +#else + dev = alloc_netdev(sizeof(*sl), name, NET_NAME_UNKNOWN, sll_setup); +#endif + if (!dev) return NULL; dev->base_addr = i;