#include <linux/can.h>
#include <linux/kthread.h>
#include <linux/hrtimer.h>
+#include <linux/version.h>
#include "linux/lin_bus.h"
/* Should be in include/linux/tty.h */
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,
};
*/
static int sltty_change_speed(struct tty_struct *tty, unsigned speed)
{
- struct ktermios old_termios;
+ struct ktermios old_termios, termios;
int cflag;
mutex_lock(&tty->termios_mutex);
- 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);
return 0;
}
+static void sllin_slave_finish_rx_msg(struct sllin *sl)
+{
+ if (sllin_rx_validate(sl) == -1) {
+ netdev_dbg(sl->dev, "sllin: RX validation failed.\n");
+ sllin_report_error(sl, LIN_ERR_CHECKSUM);
+ } else {
+ /* Send CAN non-RTR frame with data */
+ netdev_dbg(sl->dev, "sllin: sending NON-RTR CAN frame with LIN payload.");
+ sll_bump(sl); /* send packet to the network layer */
+ }
+ /* Prepare for reception of new header */
+ sl->rx_cnt = 0;
+ sl->rx_expect = SLLIN_BUFF_ID + 1;
+ sl->rx_len_unknown = false; /* We do know exact length of the header */
+ sl->header_received = false;
+}
+
static void sllin_slave_receive_buf(struct tty_struct *tty,
const unsigned char *cp, char *fp, int count)
{
/* Read the characters out of the buffer */
while (count--) {
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
+ */
+ if (sl->rx_len_unknown == true)
+ {
+ hrtimer_cancel(&sl->rx_timer);
+ sllin_slave_finish_rx_msg(sl);
+
+ set_bit(SLF_RXEVENT, &sl->flags);
+ wake_up(&sl->kwt_wq);
+ }
+
netdev_dbg(sl->dev, "sllin_slave_receive_buf char 0x%02x ignored "
"due marker 0x%02x, flags 0x%lx\n",
*cp, *(fp-1), sl->flags);
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;
sl->header_received = true;
+ hrtimer_start(&sl->rx_timer,
+ ktime_add(ktime_get(), sl->rx_timer_timeout),
+ HRTIMER_MODE_ABS);
sll_send_rtr(sl);
continue;
}
/* Response received */
if ((sl->header_received == true) &&
- ((sl->rx_cnt >= sl->rx_expect) ||
- ((sl->rx_len_unknown == true) && (count == 0)))) {
+ ((sl->rx_cnt >= sl->rx_expect))) {
- sll_bump(sl);
+ hrtimer_cancel(&sl->rx_timer);
netdev_dbg(sl->dev, "Received LIN header & LIN response. "
"rx_cnt = %u, rx_expect = %u\n", sl->rx_cnt,
sl->rx_expect);
+ sllin_slave_finish_rx_msg(sl);
- /* Prepare for reception of new header */
- sl->rx_cnt = 0;
- sl->rx_expect = SLLIN_BUFF_ID + 1;
- sl->rx_len_unknown = false; /* We do know exact length of the header */
- sl->header_received = false;
+ set_bit(SLF_RXEVENT, &sl->flags);
+ wake_up(&sl->kwt_wq);
}
}
}
{
struct sllin *sl = container_of(hrtimer, struct sllin, rx_timer);
- sllin_report_error(sl, LIN_ERR_RX_TIMEOUT);
- set_bit(SLF_TMOUTEVENT, &sl->flags);
+ if (sl->lin_master) {
+ 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;
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 lin_id;
+ struct sllin_conf_entry *sce;
netdev_dbg(sl->dev, "sllin_kwthread started.\n");
sched_setscheduler(current, SCHED_FIFO, &schparam);
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)));
/* 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",
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)) {
+ int mode;
+
+ 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);
+ }
+
+ 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);
+ }
+ 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)