]> rtime.felk.cvut.cz Git - socketcan-devel.git/commitdiff
Updated structure and added lists of URBs (similarly to usb driver in LinCAN).
authorJiri Vanek <vanekjir@fel.cvut.cz>
Mon, 19 Mar 2012 10:00:20 +0000 (11:00 +0100)
committerJiri Vanek <vanekjir@fel.cvut.cz>
Mon, 19 Mar 2012 10:00:20 +0000 (11:00 +0100)
kernel/2.6/drivers/net/can/usb/ctu_usbcan.c

index 7142b199c9e158be0ab34646d5509009e37e8a38..036252c2061d8112e03f0f9ccb7570c4bcfce348 100644 (file)
@@ -9,6 +9,8 @@
 #include <linux/mutex.h>
 #include <linux/netdevice.h>
 #include <linux/version.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
 
 #include <linux/can.h>
 #include <linux/can/dev.h>
 
 #define USBCAN_TRANSFER_SIZE 16
 
-
 #define CAN_MSG_LENGTH 8
-/* Definitions to use for canmsg_t and canfilt_t flags */
+
 #define MSG_RTR   (1<<0)
 #define MSG_OVR   (1<<1)
 #define MSG_EXT   (1<<2)
 #define MSG_LOCAL (1<<3)
 
+
+#define USBCAN_DATA_OK (1)
+#define USBCAN_TX_PENDING      (2)
+
 MODULE_LICENSE("GPL");
 
 /* table of devices that work with this driver */
@@ -49,9 +54,10 @@ struct usbcan_message {
        struct ctu_usbcan_usb *dev;
        u8      msg[USBCAN_TRANSFER_SIZE];
        u8      dlc;
+       struct list_head list_node;
 };
 /*
-Structure of byte array msg in struct usbcan_message that represens CAN message (little endian):
+Structure of byte array msg in struct usbcan_message that represents CAN message (little endian):
        msg[0] - reserved (1 byte)
        msg[1] - length (1 byte)
        msg[2:3] - flags (2 bytes)
@@ -71,9 +77,33 @@ struct ctu_usbcan_usb {
 
        u8 bulk_in_endpointAddr;        /* the address of the bulk in endpoint */
        u8 bulk_out_endpointAddr;       /* the address of the bulk out endpoint */
+
+       struct mutex            io_mutex;               /* synchronize I/O with disconnect */
+
+       struct list_head        rx_pend_list;           /* URBs waiting for data receive */
+       struct list_head        rx_ready_list;          /* URBs with valid received data */
+       struct list_head        tx_idle_list;           /* URBs prepared to hold Tx messages */
+       struct list_head        tx_pend_list;           /* URBs holding Tx messages in progress */
+       struct list_head        tx_ready_list;          /* URBs with yet confirmed Tx messages */
+
+       spinlock_t              list_lock;                      /* list lock */
+       struct task_struct *comthread;                  /* usb communication kernel thread  */
+       wait_queue_head_t queue;
+
+       volatile long flags;
 };
 
 
+static void usbcan_usb_message_move_list(struct ctu_usbcan_usb *dev,
+                       struct usbcan_message *m, struct list_head *head)
+{
+       unsigned long flags;    
+       spin_lock_irqsave(&dev->list_lock, flags);
+       list_del(&m->list_node);
+       list_add_tail(&m->list_node, head);
+       spin_unlock_irqrestore(&dev->list_lock, flags);
+}
+
 
 static void ctu_usbcan_tx_callback(struct urb *urb){
 
@@ -83,41 +113,34 @@ static void ctu_usbcan_tx_callback(struct urb *urb){
        if (!netif_device_present(m->dev->netdev))
                return;
 
+       if (urb->status != 0){
+               err("TX callback: error");
+               return;
+       }
+
+       /* success */
+
        printk("TX callback: URB successfully transmitted\n");
 
        stats->tx_packets++;
        stats->tx_bytes += m->dlc;
 
 
+       set_bit(USBCAN_DATA_OK,&m->dev->flags);
+       usbcan_usb_message_move_list(m->dev, m, &m->dev->tx_ready_list);
+       wake_up_process(m->dev->comthread);
+
 }
 
 static void ctu_usbcan_rx_callback(struct urb *urb)
 {
-       struct can_frame *cf;
-       struct sk_buff *skb;
+       
        struct usbcan_message *m = urb->context;
-       u8 *ptr;
-       int i, len;
-       u16 flags = 0;
-       struct net_device_stats *stats = &m->dev->netdev->stats;
-
+       
        if (!netif_device_present(m->dev->netdev))
                return;
 
 
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,32)
-       skb = alloc_can_skb(m->dev->netdev, &cf);
-#else
-       skb = netdev_alloc_skb(m->dev->netdev, sizeof(struct can_frame)); 
-       skb->protocol = htons(ETH_P_CAN);
-       cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
-#endif
-
-       if (skb == NULL){
-               err("RX callback: error alloc skb\n");
-               return;
-       }
-
        if(urb->status != 0){
                err("RX callback: error");
                return;
@@ -127,29 +150,9 @@ static void ctu_usbcan_rx_callback(struct urb *urb)
        
        printk("RX callback: URB successfully received\n");
 
-       len=*(u8 *)(m->msg+1);
-       if(len > CAN_MSG_LENGTH) len = CAN_MSG_LENGTH;
-
-       flags = le16_to_cpu(*(u16 *)(m->msg+2));
-       cf->can_id = le32_to_cpu((*(u32 *)(m->msg+4)));
-
-       if (flags & MSG_RTR)
-               cf->can_id |= CAN_RTR_FLAG;
-
-       if (flags & MSG_EXT)
-               cf->can_id |= CAN_EFF_FLAG;
-
-       cf->can_dlc = len;
-
-       for(ptr=m->msg+8,i=0; i < len; ptr++,i++) {
-               cf->data[i]=*ptr;
-       }
-
-
-       netif_rx(skb);
-
-       stats->rx_packets++;
-       stats->rx_bytes += cf->can_dlc;
+       set_bit(USBCAN_DATA_OK,&m->dev->flags);
+       usbcan_usb_message_move_list(m->dev, m, &m->dev->rx_ready_list);
+       wake_up_process(m->dev->comthread);
 
 }
 
@@ -157,28 +160,15 @@ static netdev_tx_t ctu_usbcan_start_xmit(struct sk_buff *skb, struct net_device
 {
        struct ctu_usbcan_usb *dev = netdev_priv(netdev);
        struct can_frame *cf = (struct can_frame *)skb->data;
+       struct usbcan_message *m;
        int i, retval, len;
        u8 *ptr;
        u16 flags = 0;
        
-       /* Prepare transmit urb  */
-       struct usbcan_message *m;
-       struct urb *u = usb_alloc_urb(0, GFP_ATOMIC);
-
-       if (!u){
-               err("Error allocating usb transmit urb");
+       if(list_empty(&dev->tx_idle_list)) 
                goto exit;
-       }
 
-       m = kzalloc(sizeof(struct usbcan_message), GFP_ATOMIC);
-       if(!m) {
-               usb_free_urb(u);
-               err("Error allocating transmit usbcan_message");
-               goto exit;
-       }
-       m->u = u;
-       u->dev = dev->udev;
-       m->dev = dev;
+       m = list_first_entry(&dev->tx_idle_list, typeof(*m), list_node);
 
        /* naplneni     */
 
@@ -206,75 +196,310 @@ static netdev_tx_t ctu_usbcan_start_xmit(struct sk_buff *skb, struct net_device
 
        m->dlc = (u8) len;
 
-       /* odeslani */
 
-       usb_fill_bulk_urb(u, dev->udev,
-               usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
-               m->msg, USBCAN_TRANSFER_SIZE, ctu_usbcan_tx_callback, m);
 
+       usbcan_usb_message_move_list(dev, m, &dev->tx_pend_list);
 
+       /* odeslani     */ 
        retval = usb_submit_urb(m->u, GFP_ATOMIC);
        if (retval){
                err("Error submitting URB: %d", retval);
+               usbcan_usb_message_move_list(dev, m, &dev->tx_idle_list);
                goto exit;
        }
-       
 
-exit:
 
-       usb_free_urb(u);
+exit:
 
        return NETDEV_TX_OK;
 
 }
 
-static int ctu_usbcan_open(struct net_device *netdev)
+static void usbcan_kthread_free_urbs(struct ctu_usbcan_usb *dev)
 {
+       while(!list_empty(&dev->rx_pend_list)) {
+               struct usbcan_message *m;
+               m = list_first_entry(&dev->rx_pend_list, typeof(*m), list_node);
+               usb_kill_urb(m->u);
+               usbcan_usb_message_move_list(dev, m, &dev->rx_ready_list);
+       }
 
-       struct ctu_usbcan_usb *dev = netdev_priv(netdev);
-       int retval;
+       while(!list_empty(&dev->tx_pend_list)) {
+               struct usbcan_message *m;
+               m = list_first_entry(&dev->tx_pend_list, typeof(*m), list_node);
+               usb_kill_urb(m->u);
+               usbcan_usb_message_move_list(dev, m, &dev->tx_idle_list);
+       }
+
+       while(!list_empty(&dev->rx_ready_list)) {
+               struct usbcan_message *m;
+               m = list_first_entry(&dev->rx_ready_list, typeof(*m), list_node);
+               list_del(&m->list_node);
+               usb_free_urb(m->u);
+               kfree(m);
+       }
+
+       while(!list_empty(&dev->tx_ready_list)) {
+               struct usbcan_message *m;
+               m = list_first_entry(&dev->tx_ready_list, typeof(*m), list_node);
+               list_del(&m->list_node);
+               usb_free_urb(m->u);
+               kfree(m);
+       }
+
+       while(!list_empty(&dev->tx_idle_list)) {
+               struct usbcan_message *m;
+               m = list_first_entry(&dev->tx_idle_list, typeof(*m), list_node);
+               list_del(&m->list_node);
+               usb_free_urb(m->u);
+               kfree(m);
+       }
+
+}
+
+void usbcan_kthread_write_handler(struct ctu_usbcan_usb *dev, struct usbcan_message *m)
+{
 
+       usbcan_usb_message_move_list(dev, m, &dev->tx_idle_list);
+
+}
+
+void usbcan_kthread_read_handler(struct ctu_usbcan_usb *dev, struct usbcan_message *m)
+{
+
+       struct can_frame *cf;
+       struct sk_buff *skb;
+       u8 *ptr;
+       int i, len, retval;
+       u16 flags = 0;
+       struct net_device_stats *stats = &m->dev->netdev->stats;
+
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,32)
+       skb = alloc_can_skb(m->dev->netdev, &cf);
+#else
+       skb = netdev_alloc_skb(m->dev->netdev, sizeof(struct can_frame)); 
+       skb->protocol = htons(ETH_P_CAN);
+       cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
+#endif
+
+       if (skb == NULL){
+               err("RX: error alloc skb\n");
+               return;
+       }
+
+       
+       printk("RX: URB successfully received\n");
+
+       len=*(u8 *)(m->msg+1);
+       if(len > CAN_MSG_LENGTH) len = CAN_MSG_LENGTH;
+
+       flags = le16_to_cpu(*(u16 *)(m->msg+2));
+       cf->can_id = le32_to_cpu((*(u32 *)(m->msg+4)));
+
+       if (flags & MSG_RTR)
+               cf->can_id |= CAN_RTR_FLAG;
+
+       if (flags & MSG_EXT)
+               cf->can_id |= CAN_EFF_FLAG;
+
+       cf->can_dlc = len;
+
+       for(ptr=m->msg+8,i=0; i < len; ptr++,i++) {
+               cf->data[i]=*ptr;
+       }
+
+
+       netif_rx(skb);
+
+       stats->rx_packets++;
+       stats->rx_bytes += cf->can_dlc;
+
+       /* Renewing RX urb */
+
+       usbcan_usb_message_move_list(dev, m, &dev->rx_pend_list);
+       retval = usb_submit_urb (m->u, GFP_KERNEL);
+       if (retval<0) {
+               err("URB error %d\n", retval);
+       }
+
+
+}
+
+
+static int usbcan_sleep_thread(struct ctu_usbcan_usb *dev)
+{
+       int     rc = 0;
+
+       /* Wait until a signal arrives or we are woken up */
+       for (;;) {
+               try_to_freeze();
+               set_current_state(TASK_INTERRUPTIBLE);
+               if (signal_pending(current)) {
+                       rc = -EINTR;
+                       break;
+               }
+               if (
+                       kthread_should_stop() ||
+                       test_bit(USBCAN_DATA_OK,&dev->flags)
+               )
+                       break;
+               schedule();
+       }
+       __set_current_state(TASK_RUNNING);
+       return rc;
+}
+
+int usbcan_kthread(void *data)
+{
+
+       struct ctu_usbcan_usb *dev=(struct ctu_usbcan_usb *) data;
+
+       int i, retval;
 
-       /* Prepare receive urb  */
        struct usbcan_message *m;
        struct urb *u;
 
-       printk("CTU USBCAN: opening device\n");
+       printk("CTU USBCAN: kthread running\n");
 
-       u = usb_alloc_urb(0, GFP_KERNEL);
-       if (!u){
-               err("Error allocating usb receive urb");
-               goto exit;
+       INIT_LIST_HEAD(&dev->rx_pend_list);
+       INIT_LIST_HEAD(&dev->rx_ready_list);
+       INIT_LIST_HEAD(&dev->tx_idle_list);
+       INIT_LIST_HEAD(&dev->tx_pend_list);
+       INIT_LIST_HEAD(&dev->tx_ready_list);
+
+       /* Prepare receive urbs  */
+       for (i=0;i<USBCAN_TOT_RX_URBS;i++){
+               
+               u = usb_alloc_urb(0, GFP_KERNEL);               
+               m = kzalloc(sizeof(struct usbcan_message), GFP_KERNEL);
+               
+               if (!u){
+                       err("Error allocating usb receive urb");
+                       goto exit;
+               }
+
+               if(!m) {
+                       usb_free_urb(u);
+                       err("Error allocating receive usbcan_message");
+                       goto exit;
+               }
+               m->u = u;
+               u->dev = dev->udev;
+               m->dev = dev;
+               usb_fill_bulk_urb(u, dev->udev,
+                       usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr),
+                       m->msg, USBCAN_TRANSFER_SIZE, ctu_usbcan_rx_callback, m);
+
+
+               list_add_tail(&m->list_node, &dev->rx_ready_list);
        }
 
-       m = kzalloc(sizeof(struct usbcan_message), GFP_KERNEL);
-       if(!m) {
-               usb_free_urb(u);
-               err("Error allocating receive usbcan_message");
-               goto exit;
+       /* Prepare transmit urbs  */
+       for (i=0;i<USBCAN_TOT_TX_URBS;i++){
+
+               u = usb_alloc_urb(0, GFP_ATOMIC);
+               m = kzalloc(sizeof(struct usbcan_message), GFP_ATOMIC);
+
+               if (!u){
+                       err("Error allocating usb transmit urb");
+                       goto exit;
+               }
+       
+               if(!m) {
+                       usb_free_urb(u);
+                       err("Error allocating transmit usbcan_message");
+                       goto exit;
+               }
+               m->u = u;
+               u->dev = dev->udev;
+               m->dev = dev;
+
+               usb_fill_bulk_urb(u, dev->udev,
+                       usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
+                       m->msg, USBCAN_TRANSFER_SIZE, ctu_usbcan_tx_callback, m);
+
+               list_add_tail(&m->list_node, &dev->tx_idle_list);
        }
-       m->u = u;
-       u->dev = dev->udev;
-       m->dev = dev;
-       usb_fill_bulk_urb(u, dev->udev,
-               usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr),
-               m->msg, USBCAN_TRANSFER_SIZE, ctu_usbcan_rx_callback, m);
 
 
-       retval=usb_submit_urb(m->u, GFP_KERNEL);
-       if (retval){
-               err("Error submitting URB: %d", retval);
-               goto exit;
+       for (i=0;i<USBCAN_TOT_RX_URBS;i++){
+               
+               m = list_first_entry(&dev->rx_ready_list, typeof(*m), list_node);
+               usbcan_usb_message_move_list(dev, m, &dev->rx_pend_list);
+
+               retval=usb_submit_urb(m->u, GFP_KERNEL);
+               if (retval){
+                       err("Error submitting URB: %d", retval);
+                       goto exit;
+               }
+       }
+
+
+       /* an endless loop in which we are doing our work */
+       for(;;)
+       {
+
+
+               /* We need to do a memory barrier here to be sure that
+               the flags are visible on all CPUs. */
+               mb();
+
+               /* fall asleep */
+               if (!kthread_should_stop() && (usbcan_sleep_thread(dev)<0)){
+                       break;
+               }
+               /* We need to do a memory barrier here to be sure that the flags are visible on all CPUs. */
+               mb();
+
+               if (kthread_should_stop()){
+                       break;
+               }
+
+               mb();
+
+               clear_bit(USBCAN_DATA_OK,&dev->flags);
+
+               
+               while(!list_empty(&dev->rx_ready_list)) {
+                       struct usbcan_message *m;
+                       m = list_first_entry(&dev->rx_ready_list, typeof(*m), list_node);
+                       usbcan_kthread_read_handler(dev, m);
+               }
+
+               while(!list_empty(&dev->tx_ready_list)) {
+                       struct usbcan_message *m;
+                       m = list_first_entry(&dev->tx_ready_list, typeof(*m), list_node);
+                       usbcan_kthread_write_handler(dev, m);
+               }
+
        }
 
+
 exit:
-       usb_free_urb(u);
+       usbcan_kthread_free_urbs(dev);
+       printk("CTU USBCAN: usbcan thread finished!");
+       
+       return 0;
+
+}
+
+static int ctu_usbcan_open(struct net_device *netdev)
+{
+
+       struct ctu_usbcan_usb *dev = netdev_priv(netdev);
+
+       /* start kernel thread */
+       dev->comthread = kthread_run(usbcan_kthread, (void *)dev, "usbcan_1");
 
        return 0;
 }
 
 static int ctu_usbcan_close(struct net_device *netdev)
 {
+
+       struct ctu_usbcan_usb *dev = netdev_priv(netdev);
+       kthread_stop(dev->comthread);
        
        return 0;
 }