#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 */
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)
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){
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;
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);
}
{
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 */
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;
}