+static void sllin_receive_buf(struct tty_struct *tty,
+ const unsigned char *cp, char *fp, int count)
+{
+ struct sllin *sl = (struct sllin *) tty->disc_data;
+ netdev_dbg(sl->dev, "sllin_receive_buf invoked, count = %u\n", count);
+
+ if (!sl || sl->magic != SLLIN_MAGIC || !netif_running(sl->dev))
+ return;
+
+ if (sl->lin_master)
+ sllin_master_receive_buf(tty, cp, fp, count);
+ else
+ sllin_slave_receive_buf(tty, cp, fp, count);
+
+}
+
+static int sllin_send_tx_buff(struct sllin *sl)
+{
+ struct tty_struct *tty = sl->tty;
+ int remains;
+ int res;
+
+ set_bit(SLF_TXBUFF_RQ, &sl->flags);
+ do {
+ if (unlikely(test_and_set_bit(SLF_TXBUFF_INPR, &sl->flags)))
+ 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)
+ remains = sl->tx_lim - sl->tx_cnt;
+ else
+ remains = 1;
+#else
+ remains = sl->tx_lim - sl->tx_cnt;
+#endif
+
+ res = tty->ops->write(tty, sl->tx_buff + sl->tx_cnt, remains);
+ if (res < 0)
+ goto error_in_write;
+
+ remains -= res;
+ sl->tx_cnt += res;
+
+ if (remains > 0) {
+ set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+ res = tty->ops->write(tty, sl->tx_buff + sl->tx_cnt, remains);
+ if (res < 0) {
+ clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+ goto error_in_write;
+ }
+
+ remains -= res;
+ sl->tx_cnt += res;
+ }
+
+ netdev_dbg(sl->dev, "sllin_send_tx_buff sent %d, remains %d\n",
+ 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)));
+
+#ifdef SLLIN_LED_TRIGGER
+ sllin_led_event(sl->dev, SLLIN_LED_EVENT_TX);
+#endif
+
+ return 0;
+
+error_in_write:
+ clear_bit(SLF_TXBUFF_INPR, &sl->flags);
+ return -1;
+
+}
+
+#ifdef BREAK_BY_BAUD
+static int sllin_send_break(struct sllin *sl)
+{
+ struct tty_struct *tty = sl->tty;
+ unsigned long break_baud;
+ int res;
+
+ break_baud = ((sl->lin_baud * 2) / 3);
+ sltty_change_speed(tty, break_baud);
+
+ tty->ops->flush_buffer(tty);
+ sl->rx_cnt = SLLIN_BUFF_BREAK;
+
+ sl->rx_expect = SLLIN_BUFF_BREAK + 1;
+ sl->lin_state = SLSTATE_BREAK_SENT;
+
+ res = sllin_send_tx_buff(sl);
+ if (res < 0) {
+ sl->lin_state = SLSTATE_IDLE;
+ return res;
+ }
+
+ return 0;
+}
+#else /* BREAK_BY_BAUD */
+
+static int sllin_send_break(struct sllin *sl)
+{
+ struct tty_struct *tty = sl->tty;
+ int retval;
+ unsigned long break_baud;
+ unsigned long usleep_range_min;
+ unsigned long usleep_range_max;
+
+ break_baud = ((sl->lin_baud * 2) / 3);
+ sl->rx_cnt = SLLIN_BUFF_BREAK;
+ sl->rx_expect = SLLIN_BUFF_BREAK + 1;
+ sl->lin_state = SLSTATE_BREAK_SENT;
+
+ /* Do the break ourselves; Inspired by
+ http://lxr.linux.no/#linux+v3.1.2/drivers/tty/tty_io.c#L2452 */
+ retval = tty->ops->break_ctl(tty, -1);
+ if (retval)
+ return retval;
+
+ /* udelay(712); */
+ usleep_range_min = (1000000l * SLLIN_SAMPLES_PER_CHAR) / break_baud;
+ usleep_range_max = usleep_range_min + 50;
+ usleep_range(usleep_range_min, usleep_range_max);
+
+ retval = tty->ops->break_ctl(tty, 0);
+ usleep_range_min = (1000000l * 1 /* 1 bit */) / break_baud;
+ usleep_range_max = usleep_range_min + 30;
+ usleep_range(usleep_range_min, usleep_range_max);
+
+ tty->ops->flush_buffer(tty);
+
+ sl->tx_cnt = SLLIN_BUFF_SYNC;
+
+ netdev_dbg(sl->dev, "Break sent.\n");
+ set_bit(SLF_RXEVENT, &sl->flags);
+ wake_up(&sl->kwt_wq);
+
+ return 0;
+}
+#endif /* BREAK_BY_BAUD */
+
+
+static enum hrtimer_restart sllin_rx_timeout_handler(struct hrtimer *hrtimer)
+{
+ struct sllin *sl = container_of(hrtimer, struct sllin, rx_timer);
+
+ /*
+ * 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 {
+ sllin_slave_finish_rx_msg(sl);
+ set_bit(SLF_RXEVENT, &sl->flags);
+ }
+ wake_up(&sl->kwt_wq);
+
+ return HRTIMER_NORESTART;
+}
+
+/*****************************************
+ * sllin_kwthread - kernel worker thread
+ *****************************************/
+
+static int sllin_kwthread(void *ptr)
+{
+ struct sllin *sl = (struct sllin *)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);
+
+ clear_bit(SLF_ERROR, &sl->flags);
+ sltty_change_speed(tty, sl->lin_baud);
+
+ while (!kthread_should_stop()) {
+ struct can_frame *cf;
+ u8 *lin_data;
+ int lin_dlc;
+ u8 lin_data_buff[SLLIN_DATA_MAX];
+
+
+ if ((sl->lin_state == SLSTATE_IDLE) && sl->lin_master &&
+ sl->id_to_send) {
+ if (sllin_send_break(sl) < 0) {
+ /* error processing */
+ }
+ }
+
+ wait_event_killable(sl->kwt_wq, kthread_should_stop() ||
+ 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_ID_RECEIVED) ||
+ (((sl->lin_state == SLSTATE_IDLE) ||
+ (sl->lin_state == SLSTATE_RESPONSE_WAIT))
+ && test_bit(SLF_MSGEVENT, &sl->flags)));
+
+ if (test_and_clear_bit(SLF_RXEVENT, &sl->flags)) {
+ netdev_dbg(sl->dev, "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);
+ netdev_dbg(sl->dev, "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)) {
+ netdev_dbg(sl->dev, "sllin_kthread TXEVENT\n");
+ }
+
+ if (test_and_clear_bit(SLF_TMOUTEVENT, &sl->flags)) {
+ netdev_dbg(sl->dev, "sllin_kthread TMOUTEVENT\n");
+ sllin_reset_buffs(sl);
+
+ sl->lin_state = SLSTATE_IDLE;
+ }
+
+ switch (sl->lin_state) {
+ case SLSTATE_IDLE:
+ 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) {
+ struct sllin_conf_entry *sce;
+
+ netdev_dbg(sl->dev, "%s: RTR SFF CAN frame, ID = %x\n",
+ __func__, cf->can_id & LIN_ID_MASK);
+
+ 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)
+ && (sce->dlc > 0)) {
+
+ if (sce->frame_fl & LIN_SINGLE_RESPONSE)
+ sce->frame_fl &= ~LIN_CACHE_RESPONSE;
+
+ 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;
+ } else {
+ lin_data = NULL;
+ lin_dlc = sce->dlc;
+ }
+ 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)
+ lin_dlc = SLLIN_DATA_MAX;
+ tx_bytes = lin_dlc;
+ }
+
+ if (sllin_setup_msg(sl, mode, cf->can_id & LIN_ID_MASK,
+ lin_data, lin_dlc) != -1) {
+
+ sl->id_to_send = true;
+ sl->data_to_send = (lin_data != NULL) ? true : false;
+ sl->resp_len_known = (lin_dlc > 0) ? true : false;
+ sl->dev->stats.tx_packets++;
+ sl->dev->stats.tx_bytes += tx_bytes;
+ }
+
+ clear_bit(SLF_MSGEVENT, &sl->flags);
+ kfree_skb(sl->tx_req_skb);
+ netif_wake_queue(sl->dev);
+ hrtimer_start(&sl->rx_timer,
+ ktime_add(ktime_get(), sl->rx_timer_timeout),
+ HRTIMER_MODE_ABS);
+ break;
+
+ case SLSTATE_BREAK_SENT:
+#ifdef BREAK_BY_BAUD
+ if (sl->rx_cnt <= SLLIN_BUFF_BREAK)
+ continue;
+
+ res = sltty_change_speed(tty, sl->lin_baud);
+#endif
+
+ sl->lin_state = SLSTATE_ID_SENT;
+ sllin_send_tx_buff(sl);
+ break;
+
+ case SLSTATE_ID_SENT:
+ hrtimer_cancel(&sl->rx_timer);
+ sl->id_to_send = false;
+ if (sl->data_to_send) {
+ sllin_send_tx_buff(sl);
+ sl->lin_state = SLSTATE_RESPONSE_SENT;
+ sl->rx_expect = sl->tx_lim;
+ goto slstate_response_sent;
+ } else {
+ if (sl->resp_len_known) {
+ sl->rx_expect = sl->rx_lim;
+ } else {
+ sl->rx_expect = SLLIN_BUFF_DATA + 2;
+ }
+ sl->lin_state = SLSTATE_RESPONSE_WAIT;
+ /* If we don't receive anything, timer will "unblock" us */
+ hrtimer_start(&sl->rx_timer,
+ ktime_add(ktime_get(), sl->rx_timer_timeout),
+ HRTIMER_MODE_ABS);
+ goto slstate_response_wait;
+ }
+ break;
+
+ case SLSTATE_RESPONSE_WAIT:
+slstate_response_wait:
+ if (test_bit(SLF_MSGEVENT, &sl->flags)) {
+ unsigned char *lin_buff;
+ cf = (struct can_frame *)sl->tx_req_skb->data;
+
+ 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);
+ netdev_dbg(sl->dev, "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) {
+
+ sl->rx_expect = sl->tx_lim;
+ sl->data_to_send = true;
+ sl->dev->stats.tx_packets++;
+ sl->dev->stats.tx_bytes += tx_bytes;
+
+ if (!sl->lin_master) {
+ sl->tx_cnt = SLLIN_BUFF_DATA;
+ }
+
+ sllin_send_tx_buff(sl);
+ clear_bit(SLF_MSGEVENT, &sl->flags);
+ kfree_skb(sl->tx_req_skb);
+ netif_wake_queue(sl->dev);
+
+ sl->lin_state = SLSTATE_RESPONSE_SENT;
+ goto slstate_response_sent;
+ }
+ } else {
+ sl->lin_state = SLSTATE_RESPONSE_WAIT_BUS;
+ }
+ }
+
+ /* Be aware, no BREAK here */
+ case SLSTATE_RESPONSE_WAIT_BUS:
+ if (sl->rx_cnt < sl->rx_expect)
+ continue;
+
+ hrtimer_cancel(&sl->rx_timer);
+ netdev_dbg(sl->dev, "response received ID %d len %d\n",
+ sl->rx_buff[SLLIN_BUFF_ID], sl->rx_cnt - SLLIN_BUFF_DATA - 1);
+
+ if (sllin_rx_validate(sl) == -1) {
+ netdev_dbg(sl->dev, "RX validation failed.\n");
+ sllin_report_error(sl, LIN_ERR_CHECKSUM);
+ } else {
+ /* Send CAN non-RTR frame with data */
+ netdev_dbg(sl->dev, "sending NON-RTR CAN frame with LIN payload.");
+ sll_bump(sl); /* send packet to the network layer */
+ }
+
+ sl->id_to_send = false;
+ 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)) {
+
+ if (sce->frame_fl & LIN_SINGLE_RESPONSE)
+ sce->frame_fl &= ~LIN_CACHE_RESPONSE;
+
+ 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)
+ continue;
+
+ hrtimer_cancel(&sl->rx_timer);
+ sll_bump(sl); /* send packet to the network layer */
+ netdev_dbg(sl->dev, "response sent ID %d len %d\n",
+ sl->rx_buff[SLLIN_BUFF_ID], sl->rx_cnt - SLLIN_BUFF_DATA - 1);
+
+ sl->id_to_send = false;
+ sl->lin_state = SLSTATE_IDLE;
+ break;
+ }
+ }
+
+ hrtimer_cancel(&sl->rx_timer);
+ netdev_dbg(sl->dev, "sllin_kwthread stopped.\n");
+
+ return 0;
+}
+
+