]> rtime.felk.cvut.cz Git - linux-lin.git/blobdiff - sllin/sllin.c
sllin: Added timer handling timeout when receiving LIN response.
[linux-lin.git] / sllin / sllin.c
index bd6f3cd6c2ae7fe185257dae122d2f30707e4941..fee14d193cf94d2d776aed91a6c2c02494363c4b 100644 (file)
@@ -58,6 +58,7 @@
 #include <linux/init.h>
 #include <linux/can.h>
 #include <linux/kthread.h>
+#include <linux/hrtimer.h>
 
 /* Should be in include/linux/tty.h */
 #define N_SLLIN         25
@@ -108,14 +109,15 @@ struct sllin {
        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_lim;         /* maximum Rx chars for current frame */
        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_state;      /* state */
-       int                     id_to_send;     /* there is ID to be sent */
+       char                    id_to_send;     /* there is ID to be sent */
+       char                    data_to_send;   /* there are data to be sent */
 
        unsigned long           flags;          /* Flag values/ mode etc     */
 #define SLF_INUSE              0               /* Channel in use            */
@@ -123,10 +125,14 @@ struct sllin {
 #define SLF_RXEVENT            2               /* Rx wake event             */
 #define SLF_TXEVENT            3               /* Tx wake event             */
 #define SLF_MSGEVENT           4               /* CAN message to sent       */
+#define SLF_TMOUTEVENT          5               /* Timeout on received data  */
 
        dev_t                   line;
        struct task_struct      *kwthread;
        wait_queue_head_t       kwt_wq;
+       struct hrtimer          rx_timer;       /* RX timeout timer */
+       ktime_t                 rx_timer_timeout; /* RX timeout timer value */
+       struct sk_buff          *rec_skb;       /* Socket buffer with received CAN frame */
 };
 
 static struct net_device **sllin_devs;
@@ -296,8 +302,8 @@ static void sllin_write_wakeup(struct tty_struct *tty)
        struct sllin *sl = (struct sllin *) tty->disc_data;
 
        /* First make sure we're connected. */
-       //if (!sl || sl->magic != SLLIN_MAGIC || !netif_running(sl->dev))
-       //      return;
+       if (!sl || sl->magic != SLLIN_MAGIC || !netif_running(sl->dev))
+               return;
 
        if (sl->lin_state != SLSTATE_BREAK_SENT)
                remains = sl->tx_lim - sl->tx_cnt;
@@ -328,24 +334,28 @@ static netdev_tx_t sll_xmit(struct sk_buff *skb, struct net_device *dev)
        struct sllin *sl = netdev_priv(dev);
 
        if (skb->len != sizeof(struct can_frame))
-               goto out;
+               goto err_out;
 
        spin_lock(&sl->lock);
        if (!netif_running(dev))  {
                spin_unlock(&sl->lock);
                printk(KERN_WARNING "%s: xmit: iface is down\n", dev->name);
-               goto out;
+               goto err_out;
        }
        if (sl->tty == NULL) {
                spin_unlock(&sl->lock);
-               goto out;
+               goto err_out;
        }
 
        netif_stop_queue(sl->dev);
-       sll_encaps(sl, (struct can_frame *) skb->data); /* encaps & send */
+
+       sl->rec_skb = skb;
+       set_bit(SLF_MSGEVENT, &sl->flags);
+       wake_up(&sl->kwt_wq);
        spin_unlock(&sl->lock);
+       return NETDEV_TX_OK;
 
-out:
+err_out:
        kfree_skb(skb);
        return NETDEV_TX_OK;
 }
@@ -439,8 +449,8 @@ static void sllin_receive_buf(struct tty_struct *tty,
 
        printk(KERN_INFO "sllin_receive_buf invoked\n");
 
-       //if (!sl || sl->magic != SLLIN_MAGIC || !netif_running(sl->dev))
-       //      return;
+       if (!sl || sl->magic != SLLIN_MAGIC || !netif_running(sl->dev))
+               return;
 
        /* Read the characters out of the buffer */
        while (count--) {
@@ -488,6 +498,7 @@ int sllin_setup_msg(struct sllin *sl, int mode, int id,
        sl->rx_cnt = 0;
        sl->tx_cnt = 0;
        sl->rx_expect = 0;
+       sl->rx_lim = SLLIN_BUFF_LEN;
 
        sl->tx_buff[SLLIN_BUFF_BREAK] = 0;
        sl->tx_buff[SLLIN_BUFF_SYNC]  = 0x55;
@@ -510,7 +521,7 @@ int sllin_setup_msg(struct sllin *sl, int mode, int id,
                sl->tx_buff[sl->tx_lim++] = csum;
        }
        if (len != 0)
-               sl->rx_lim += len + 1;
+               sl->rx_lim = SLLIN_BUFF_DATA + len + 1;
 
        return 0;
 }
@@ -608,6 +619,8 @@ int sllin_send_break(struct sllin *sl)
        tty->ops->flush_buffer(tty);
 
        sl->tx_cnt = SLLIN_BUFF_SYNC;
+
+       printk(KERN_INFO "sllin: Break sent.\n");
        set_bit(SLF_RXEVENT, &sl->flags);
        wake_up(&sl->kwt_wq);
 
@@ -615,6 +628,18 @@ int sllin_send_break(struct sllin *sl)
 }
 #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);
+
+       set_bit(SLF_TMOUTEVENT, &sl->flags);
+       wake_up(&sl->kwt_wq);
+
+       return HRTIMER_NORESTART;
+}
+
+
 /*****************************************
  *  sllin_kwthread - kernel worker thread
  *****************************************/
@@ -625,7 +650,7 @@ int sllin_kwthread(void *ptr)
        struct tty_struct *tty = sl->tty;
        struct sched_param schparam = { .sched_priority = 40 };
        int res;
-       unsigned char buff[8] = {0x2, 0x3, 0x4, 0x5};
+       struct can_frame *cf;
 
        printk(KERN_INFO "sllin: sllin_kwthread started.\n");
        sched_setscheduler(current, SCHED_FIFO, &schparam);
@@ -633,10 +658,6 @@ int sllin_kwthread(void *ptr)
        clear_bit(SLF_ERROR, &sl->flags);
        sltty_change_speed(tty, sl->lin_baud);
 
-       //sllin_setup_msg(sl, 0, 0x01, NULL, 0);
-       sllin_setup_msg(sl, 0, 0x02, buff, 4);
-       sl->id_to_send = true;
-
        while (!kthread_should_stop()) {
                if ((sl->lin_state == SLSTATE_IDLE) && sl->lin_master &&
                        sl->id_to_send) {
@@ -647,7 +668,9 @@ int sllin_kwthread(void *ptr)
 
                wait_event_killable(sl->kwt_wq, kthread_should_stop() ||
                        test_bit(SLF_RXEVENT, &sl->flags) ||
-                       test_bit(SLF_TXEVENT, &sl->flags));
+                       test_bit(SLF_TXEVENT, &sl->flags) ||
+                       test_bit(SLF_TMOUTEVENT, &sl->flags) ||
+                       ((sl->lin_state == SLSTATE_IDLE) && test_bit(SLF_MSGEVENT, &sl->flags)));
 
                if (test_and_clear_bit(SLF_RXEVENT, &sl->flags)) {
                        printk(KERN_INFO "sllin_kthread RXEVENT \n");
@@ -657,11 +680,57 @@ int sllin_kwthread(void *ptr)
                        printk(KERN_INFO "sllin_kthread TXEVENT \n");
                }
 
+               if (test_and_clear_bit(SLF_TMOUTEVENT, &sl->flags)) {
+                       printk(KERN_INFO "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;
+                       
+                       sl->lin_state = SLSTATE_IDLE;
+               }
+
+               if ((sl->lin_state == SLSTATE_IDLE) && test_bit(SLF_MSGEVENT, &sl->flags)) {
+                       cf = (struct can_frame *)sl->rec_skb->data;
+
+                       /* We do care only about SFF frames */
+                       if (cf->can_id & CAN_EFF_FLAG)
+                               goto release_skb;
+
+                       if (cf->can_id & CAN_RTR_FLAG) {
+                               printk(KERN_INFO "%s: RTR CAN frame, ID = %x\n",
+                                       __FUNCTION__, cf->can_id & CAN_SFF_MASK);
+                               if (sllin_setup_msg(sl, 0, 
+                                       cf->can_id & CAN_SFF_MASK, NULL, 0) != -1) {
+                                       sl->id_to_send = true;
+                                       sl->data_to_send = false;
+                               }
+                       } else {
+                               printk(KERN_INFO "%s: NON-RTR CAN frame, ID = %x\n",
+                                       __FUNCTION__, (int)cf->can_id & CAN_SFF_MASK);
+
+                               if (sllin_setup_msg(sl, 0, cf->can_id & CAN_SFF_MASK, 
+                                       cf->data, cf->can_dlc) != -1) {
+                                       sl->id_to_send = true;
+                                       sl->data_to_send = true;
+                               }
+                       }
+
+               release_skb:
+                       clear_bit(SLF_MSGEVENT, &sl->flags);
+                       kfree_skb(sl->rec_skb);
+                       netif_wake_queue(sl->dev);
+               }
+
                switch (sl->lin_state) {
                        case SLSTATE_BREAK_SENT:
+#ifdef BREAK_BY_BAUD
                                if (sl->rx_cnt <= SLLIN_BUFF_BREAK)
                                        continue;
-#ifdef BREAK_BY_BAUD
+
                                res = sltty_change_speed(tty, sl->lin_baud);
 #endif
 
@@ -670,6 +739,45 @@ int sllin_kwthread(void *ptr)
                                break;
 
                        case SLSTATE_ID_SENT:
+                               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 {
+                                       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 (sl->rx_cnt < sl->rx_expect)
+                                       continue;
+                       
+                               hrtimer_cancel(&sl->rx_timer);
+                               printk(KERN_INFO "sllin: response received ID %d len %d\n",
+                                       sl->rx_buff[SLLIN_BUFF_ID], sl->rx_cnt - SLLIN_BUFF_DATA - 1);
+                               // check checksum in sl->rx_buff
+                               // send CAN non-RTR frame with data
+                               sl->id_to_send = false;
+                               sl->lin_state = SLSTATE_IDLE;
+                               break;
+
+                       case SLSTATE_RESPONSE_SENT:
+                       slstate_response_sent:
+                               if (sl->rx_cnt < sl->tx_lim)
+                                       continue;
+                               
+                               printk(KERN_INFO "sllin: 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;
@@ -683,7 +791,7 @@ int sllin_kwthread(void *ptr)
                /* netif_wake_queue(sl->dev); allow next Tx packet arrival */
        }
 
-
+       hrtimer_cancel(&sl->rx_timer);
        printk(KERN_INFO "sllin: sllin_kwthread stopped.\n");
 
        return 0;
@@ -815,17 +923,29 @@ static int sllin_open(struct tty_struct *tty)
 
        if (!test_bit(SLF_INUSE, &sl->flags)) {
                /* Perform the low-level SLLIN initialization. */
-               sl->rx_cnt    = 0;
+               sl->lin_master = true;
+
+               sl->rx_cnt = 0;
                sl->rx_expect = 0;
-               sl->rx_lim    = 0;
-               sl->tx_cnt    = 0;
-               sl->tx_lim    = 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;
 
                sl->lin_baud  = 19200;
 
-               sl->lin_master = 1;
                sl->lin_state = SLSTATE_IDLE;
 
+#define SAMPLES_PER_CHAR       10
+#define CHARS_TO_TIMEOUT       6
+               hrtimer_init(&sl->rx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+               sl->rx_timer.function = sllin_rx_timeout_handler;
+               /* timeval_to_ktime(msg_head->ival1); */
+               sl->rx_timer_timeout = ns_to_ktime(
+                       (1000000000 / sl->lin_baud) * 
+                       SAMPLES_PER_CHAR * CHARS_TO_TIMEOUT); 
                set_bit(SLF_INUSE, &sl->flags);
 
                init_waitqueue_head(&sl->kwt_wq);