You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
			
				
					614 lines
				
				15 KiB
			
		
		
			
		
	
	
					614 lines
				
				15 KiB
			| 
											8 years ago
										 | /**
 | ||
|  |  * @file    panda.c
 | ||
|  |  * @author  Jessy Diamond Exum
 | ||
|  |  * @date    16 June 2017
 | ||
|  |  * @version 0.1
 | ||
|  |  * @brief   Driver for the Comma.ai Panda CAN adapter to allow it to be controlled via
 | ||
|  |  * the Linux SocketCAN interface.
 | ||
|  |  * @see https://github.com/commaai/panda for the full project.
 | ||
|  |  * @see Inspired by net/can/usb/mcba_usb.c from Linux Kernel 4.12-rc4.
 | ||
|  |  */
 | ||
|  | 
 | ||
|  | #include <linux/can.h>
 | ||
|  | #include <linux/can/dev.h>
 | ||
|  | #include <linux/can/error.h>
 | ||
|  | #include <linux/init.h>             // Macros used to mark up functions e.g., __init __exit
 | ||
|  | #include <linux/kernel.h>           // Contains types, macros, functions for the kernel
 | ||
|  | #include <linux/module.h>           // Core header for loading LKMs into the kernel
 | ||
|  | #include <linux/netdevice.h>
 | ||
|  | #include <linux/usb.h>
 | ||
|  | 
 | ||
|  | /* vendor and product id */
 | ||
|  | #define PANDA_MODULE_NAME "panda"
 | ||
|  | #define PANDA_VENDOR_ID 0XBBAA
 | ||
|  | #define PANDA_PRODUCT_ID 0XDDCC
 | ||
|  | 
 | ||
|  | #define PANDA_MAX_TX_URBS 20
 | ||
|  | #define PANDA_CTX_FREE PANDA_MAX_TX_URBS
 | ||
|  | 
 | ||
|  | #define PANDA_USB_RX_BUFF_SIZE 0x40
 | ||
|  | #define PANDA_USB_TX_BUFF_SIZE (sizeof(struct panda_usb_can_msg))
 | ||
|  | 
 | ||
|  | #define PANDA_NUM_CAN_INTERFACES 3
 | ||
|  | 
 | ||
|  | #define PANDA_CAN_TRANSMIT 1
 | ||
|  | #define PANDA_CAN_EXTENDED 4
 | ||
|  | 
 | ||
|  | #define PANDA_BITRATE 500000
 | ||
|  | 
 | ||
|  | #define PANDA_DLC_MASK  0x0F
 | ||
|  | 
 | ||
|  | struct panda_usb_ctx {
 | ||
|  |   struct panda_inf_priv *priv;
 | ||
|  |   u32 ndx;
 | ||
|  |   u8 dlc;
 | ||
|  | };
 | ||
|  | 
 | ||
|  | struct panda_dev_priv;
 | ||
|  | 
 | ||
|  | struct panda_inf_priv {
 | ||
|  |   struct can_priv can;
 | ||
|  |   struct panda_usb_ctx tx_context[PANDA_MAX_TX_URBS];
 | ||
|  |   struct net_device *netdev;
 | ||
|  |   struct usb_anchor tx_submitted;
 | ||
|  |   atomic_t free_ctx_cnt;
 | ||
|  |   u8 interface_num;
 | ||
|  |   u8 mcu_can_ifnum;
 | ||
|  |   struct panda_dev_priv *priv_dev;
 | ||
|  | };
 | ||
|  | 
 | ||
|  | struct panda_dev_priv {
 | ||
|  |   struct usb_device *udev;
 | ||
|  |   struct device *dev;
 | ||
|  |   struct usb_anchor rx_submitted;
 | ||
|  |   struct panda_inf_priv *interfaces[PANDA_NUM_CAN_INTERFACES];
 | ||
|  | };
 | ||
|  | 
 | ||
|  | struct __packed panda_usb_can_msg {
 | ||
|  |   u32 rir;
 | ||
|  |   u32 bus_dat_len;
 | ||
|  |   u8 data[8];
 | ||
|  | };
 | ||
|  | 
 | ||
|  | static const struct usb_device_id panda_usb_table[] = {
 | ||
|  |   { USB_DEVICE(PANDA_VENDOR_ID, PANDA_PRODUCT_ID) },
 | ||
|  |   {} /* Terminating entry */
 | ||
|  | };
 | ||
|  | 
 | ||
|  | MODULE_DEVICE_TABLE(usb, panda_usb_table);
 | ||
|  | 
 | ||
|  | 
 | ||
|  | // panda:       CAN1 = 0   CAN2 = 1   CAN3 = 4
 | ||
|  | const int can_numbering[] = {0,1,4};
 | ||
|  | 
 | ||
|  | struct panda_inf_priv *
 | ||
|  | panda_get_inf_from_bus_id(struct panda_dev_priv *priv_dev, int bus_id){
 | ||
|  |   int inf_num;
 | ||
|  |   for(inf_num = 0; inf_num < PANDA_NUM_CAN_INTERFACES; inf_num++)
 | ||
|  |     if(can_numbering[inf_num] == bus_id)
 | ||
|  |       return priv_dev->interfaces[inf_num];
 | ||
|  |   return NULL;
 | ||
|  | }
 | ||
|  | 
 | ||
|  | // CTX handling shamlessly ripped from mcba_usb.c linux driver
 | ||
|  | static inline void panda_init_ctx(struct panda_inf_priv *priv)
 | ||
|  | {
 | ||
|  |   int i = 0;
 | ||
|  | 
 | ||
|  |   for (i = 0; i < PANDA_MAX_TX_URBS; i++) {
 | ||
|  |     priv->tx_context[i].ndx = PANDA_CTX_FREE;
 | ||
|  |     priv->tx_context[i].priv = priv;
 | ||
|  |   }
 | ||
|  | 
 | ||
|  |   atomic_set(&priv->free_ctx_cnt, ARRAY_SIZE(priv->tx_context));
 | ||
|  | }
 | ||
|  | 
 | ||
|  | static inline struct panda_usb_ctx *panda_usb_get_free_ctx(struct panda_inf_priv *priv,
 | ||
|  | 							 struct can_frame *cf)
 | ||
|  | {
 | ||
|  |   int i = 0;
 | ||
|  |   struct panda_usb_ctx *ctx = NULL;
 | ||
|  | 
 | ||
|  |   for (i = 0; i < PANDA_MAX_TX_URBS; i++) {
 | ||
|  |     if (priv->tx_context[i].ndx == PANDA_CTX_FREE) {
 | ||
|  |       ctx = &priv->tx_context[i];
 | ||
|  |       ctx->ndx = i;
 | ||
|  |       ctx->dlc = cf->can_dlc;
 | ||
|  | 
 | ||
|  |       atomic_dec(&priv->free_ctx_cnt);
 | ||
|  |       break;
 | ||
|  |     }
 | ||
|  |   }
 | ||
|  | 
 | ||
|  |   printk("CTX num %d\n", atomic_read(&priv->free_ctx_cnt));
 | ||
|  |   if (!atomic_read(&priv->free_ctx_cnt)){
 | ||
|  |     /* That was the last free ctx. Slow down tx path */
 | ||
|  |     printk("SENDING TOO FAST\n");
 | ||
|  |     netif_stop_queue(priv->netdev);
 | ||
|  |   }
 | ||
|  | 
 | ||
|  |   return ctx;
 | ||
|  | }
 | ||
|  | 
 | ||
|  | /* panda_usb_free_ctx and panda_usb_get_free_ctx are executed by different
 | ||
|  |  * threads. The order of execution in below function is important.
 | ||
|  |  */
 | ||
|  | static inline void panda_usb_free_ctx(struct panda_usb_ctx *ctx)
 | ||
|  | {
 | ||
|  |   /* Increase number of free ctxs before freeing ctx */
 | ||
|  |   atomic_inc(&ctx->priv->free_ctx_cnt);
 | ||
|  | 
 | ||
|  |   ctx->ndx = PANDA_CTX_FREE;
 | ||
|  | 
 | ||
|  |   /* Wake up the queue once ctx is marked free */
 | ||
|  |   netif_wake_queue(ctx->priv->netdev);
 | ||
|  | }
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | static void panda_urb_unlink(struct panda_inf_priv *priv)
 | ||
|  | {
 | ||
|  |   usb_kill_anchored_urbs(&priv->priv_dev->rx_submitted);
 | ||
|  |   usb_kill_anchored_urbs(&priv->tx_submitted);
 | ||
|  | }
 | ||
|  | 
 | ||
|  | static int panda_set_output_enable(struct panda_inf_priv* priv, bool enable){
 | ||
|  |   return usb_control_msg(priv->priv_dev->udev,
 | ||
|  | 			 usb_sndctrlpipe(priv->priv_dev->udev, 0),
 | ||
|  | 			 0xDC, USB_TYPE_VENDOR | USB_RECIP_DEVICE,
 | ||
|  | 			 enable ? 0x1337 : 0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
 | ||
|  | }
 | ||
|  | 
 | ||
|  | static void panda_usb_write_bulk_callback(struct urb *urb)
 | ||
|  | {
 | ||
|  |   struct panda_usb_ctx *ctx = urb->context;
 | ||
|  |   struct net_device *netdev;
 | ||
|  | 
 | ||
|  |   WARN_ON(!ctx);
 | ||
|  | 
 | ||
|  |   netdev = ctx->priv->netdev;
 | ||
|  | 
 | ||
|  |   /* free up our allocated buffer */
 | ||
|  |   usb_free_coherent(urb->dev, urb->transfer_buffer_length,
 | ||
|  | 		    urb->transfer_buffer, urb->transfer_dma);
 | ||
|  | 
 | ||
|  |   if (!netif_device_present(netdev))
 | ||
|  |     return;
 | ||
|  | 
 | ||
|  |   netdev->stats.tx_packets++;
 | ||
|  |   netdev->stats.tx_bytes += ctx->dlc;
 | ||
|  | 
 | ||
|  |   can_get_echo_skb(netdev, ctx->ndx);
 | ||
|  | 
 | ||
|  |   if (urb->status)
 | ||
|  |     netdev_info(netdev, "Tx URB aborted (%d)\n", urb->status);
 | ||
|  | 
 | ||
|  |   /* Release the context */
 | ||
|  |   panda_usb_free_ctx(ctx);
 | ||
|  | }
 | ||
|  | 
 | ||
|  | 
 | ||
|  | static netdev_tx_t panda_usb_xmit(struct panda_inf_priv *priv,
 | ||
|  | 				  struct panda_usb_can_msg *usb_msg,
 | ||
|  | 				  struct panda_usb_ctx *ctx)
 | ||
|  | {
 | ||
|  |   struct urb *urb;
 | ||
|  |   u8 *buf;
 | ||
|  |   int err;
 | ||
|  | 
 | ||
|  |   /* create a URB, and a buffer for it, and copy the data to the URB */
 | ||
|  |   urb = usb_alloc_urb(0, GFP_ATOMIC);
 | ||
|  |   if (!urb)
 | ||
|  |     return -ENOMEM;
 | ||
|  | 
 | ||
|  |   buf = usb_alloc_coherent(priv->priv_dev->udev,
 | ||
|  | 			   PANDA_USB_TX_BUFF_SIZE, GFP_ATOMIC,
 | ||
|  | 			   &urb->transfer_dma);
 | ||
|  |   if (!buf) {
 | ||
|  |     err = -ENOMEM;
 | ||
|  |     goto nomembuf;
 | ||
|  |   }
 | ||
|  | 
 | ||
|  |   memcpy(buf, usb_msg, PANDA_USB_TX_BUFF_SIZE);
 | ||
|  | 
 | ||
|  |   usb_fill_bulk_urb(urb, priv->priv_dev->udev,
 | ||
|  | 		    usb_sndbulkpipe(priv->priv_dev->udev, 3), buf,
 | ||
|  | 		    PANDA_USB_TX_BUFF_SIZE, panda_usb_write_bulk_callback,
 | ||
|  | 		    ctx);
 | ||
|  | 
 | ||
|  |   urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
 | ||
|  |   usb_anchor_urb(urb, &priv->tx_submitted);
 | ||
|  | 
 | ||
|  |   err = usb_submit_urb(urb, GFP_ATOMIC);
 | ||
|  |   if (unlikely(err))
 | ||
|  |     goto failed;
 | ||
|  | 
 | ||
|  |   /* Release our reference to this URB, the USB core will eventually free it entirely. */
 | ||
|  |   usb_free_urb(urb);
 | ||
|  | 
 | ||
|  |   return 0;
 | ||
|  | 
 | ||
|  |  failed:
 | ||
|  |   usb_unanchor_urb(urb);
 | ||
|  |   usb_free_coherent(priv->priv_dev->udev, PANDA_USB_TX_BUFF_SIZE, buf, urb->transfer_dma);
 | ||
|  | 
 | ||
|  |   if (err == -ENODEV)
 | ||
|  |     netif_device_detach(priv->netdev);
 | ||
|  |   else
 | ||
|  |     netdev_warn(priv->netdev, "failed tx_urb %d\n", err);
 | ||
|  | 
 | ||
|  |  nomembuf:
 | ||
|  |   usb_free_urb(urb);
 | ||
|  | 
 | ||
|  |   return err;
 | ||
|  | }
 | ||
|  | 
 | ||
|  | static void panda_usb_process_can_rx(struct panda_dev_priv *priv_dev,
 | ||
|  | 				     struct panda_usb_can_msg *msg)
 | ||
|  | {
 | ||
|  |   struct can_frame *cf;
 | ||
|  |   struct sk_buff *skb;
 | ||
|  |   int bus_num;
 | ||
|  |   struct panda_inf_priv *priv_inf;
 | ||
|  |   struct net_device_stats *stats;
 | ||
|  | 
 | ||
|  |   bus_num = (msg->bus_dat_len >> 4) & 0xf;
 | ||
|  |   priv_inf = panda_get_inf_from_bus_id(priv_dev, bus_num);
 | ||
|  |   if(!priv_inf){
 | ||
|  |     printk("Got something on an unused interface %d\n", bus_num);
 | ||
|  |     return;
 | ||
|  |   }
 | ||
|  |   printk("Recv bus %d\n", bus_num);
 | ||
|  | 
 | ||
|  |   stats = &priv_inf->netdev->stats;
 | ||
|  |   //u16 sid;
 | ||
|  | 
 | ||
|  |   if (!netif_device_present(priv_inf->netdev))
 | ||
|  |     return;
 | ||
|  | 
 | ||
|  |   skb = alloc_can_skb(priv_inf->netdev, &cf);
 | ||
|  |   if (!skb)
 | ||
|  |     return;
 | ||
|  | 
 | ||
|  |   if(msg->rir & PANDA_CAN_EXTENDED){
 | ||
|  |     cf->can_id = (msg->rir >> 3) | CAN_EFF_FLAG;
 | ||
|  |   }else{
 | ||
|  |     cf->can_id = (msg->rir >> 21);
 | ||
|  |   }
 | ||
|  | 
 | ||
|  |   // TODO: Handle Remote Frames
 | ||
|  |   //if (msg->dlc & MCBA_DLC_RTR_MASK)
 | ||
|  |   //  cf->can_id |= CAN_RTR_FLAG;
 | ||
|  | 
 | ||
|  |   cf->can_dlc = get_can_dlc(msg->bus_dat_len & PANDA_DLC_MASK);
 | ||
|  | 
 | ||
|  |   memcpy(cf->data, msg->data, cf->can_dlc);
 | ||
|  | 
 | ||
|  |   stats->rx_packets++;
 | ||
|  |   stats->rx_bytes += cf->can_dlc;
 | ||
|  | 
 | ||
|  |   netif_rx(skb);
 | ||
|  | }
 | ||
|  | 
 | ||
|  | static void panda_usb_read_int_callback(struct urb *urb)
 | ||
|  | {
 | ||
|  |   struct panda_dev_priv *priv_dev = urb->context;
 | ||
|  |   int retval;
 | ||
|  |   int pos = 0;
 | ||
|  |   int inf_num;
 | ||
|  | 
 | ||
|  |   switch (urb->status) {
 | ||
|  |   case 0: /* success */
 | ||
|  |     break;
 | ||
|  |   case -ENOENT:
 | ||
|  |   case -ESHUTDOWN:
 | ||
|  |     return;
 | ||
|  |   default:
 | ||
|  |     dev_info(priv_dev->dev, "Rx URB aborted (%d)\n", urb->status);
 | ||
|  |     goto resubmit_urb;
 | ||
|  |   }
 | ||
|  | 
 | ||
|  |   while (pos < urb->actual_length) {
 | ||
|  |     struct panda_usb_can_msg *msg;
 | ||
|  | 
 | ||
|  |     if (pos + sizeof(struct panda_usb_can_msg) > urb->actual_length) {
 | ||
|  |       dev_err(priv_dev->dev, "format error\n");
 | ||
|  |       break;
 | ||
|  |     }
 | ||
|  | 
 | ||
|  |     msg = (struct panda_usb_can_msg *)(urb->transfer_buffer + pos);
 | ||
|  | 
 | ||
|  |     panda_usb_process_can_rx(priv_dev, msg);
 | ||
|  | 
 | ||
|  |     pos += sizeof(struct panda_usb_can_msg);
 | ||
|  |   }
 | ||
|  | 
 | ||
|  |  resubmit_urb:
 | ||
|  |   usb_fill_int_urb(urb, priv_dev->udev,
 | ||
|  | 		    usb_rcvintpipe(priv_dev->udev, 1),
 | ||
|  | 		    urb->transfer_buffer, PANDA_USB_RX_BUFF_SIZE,
 | ||
|  | 		    panda_usb_read_int_callback, priv_dev, 5);
 | ||
|  | 
 | ||
|  |   retval = usb_submit_urb(urb, GFP_ATOMIC);
 | ||
|  | 
 | ||
|  |   if (retval == -ENODEV){
 | ||
|  |     for(inf_num = 0; inf_num < PANDA_NUM_CAN_INTERFACES; inf_num++)
 | ||
|  |       if(priv_dev->interfaces[inf_num])
 | ||
|  | 	netif_device_detach(priv_dev->interfaces[inf_num]->netdev);
 | ||
|  |   }else if (retval)
 | ||
|  |     dev_err(priv_dev->dev, "failed resubmitting read bulk urb: %d\n", retval);
 | ||
|  | }
 | ||
|  | 
 | ||
|  | 
 | ||
|  | static int panda_usb_start(struct panda_dev_priv *priv_dev)
 | ||
|  | {
 | ||
|  |   int err;
 | ||
|  |   struct urb *urb = NULL;
 | ||
|  |   u8 *buf;
 | ||
|  |   int inf_num;
 | ||
|  | 
 | ||
|  |   for(inf_num = 0; inf_num < PANDA_NUM_CAN_INTERFACES; inf_num++)
 | ||
|  |     panda_init_ctx(priv_dev->interfaces[inf_num]);
 | ||
|  | 
 | ||
|  |   err = usb_set_interface(priv_dev->udev, 0, 1);
 | ||
|  |   if (err) {
 | ||
|  |     dev_err(priv_dev->dev, "Can not set alternate setting to 1, error: %i", err);
 | ||
|  |     return err;
 | ||
|  |   }
 | ||
|  | 
 | ||
|  |   /* create a URB, and a buffer for it */
 | ||
|  |   urb = usb_alloc_urb(0, GFP_KERNEL);
 | ||
|  |   if (!urb) {
 | ||
|  |     return -ENOMEM;
 | ||
|  |   }
 | ||
|  | 
 | ||
|  |   buf = usb_alloc_coherent(priv_dev->udev, PANDA_USB_RX_BUFF_SIZE,
 | ||
|  | 			   GFP_KERNEL, &urb->transfer_dma);
 | ||
|  |   if (!buf) {
 | ||
|  |     dev_err(priv_dev->dev, "No memory left for USB buffer\n");
 | ||
|  |     usb_free_urb(urb);
 | ||
|  |     return -ENOMEM;
 | ||
|  |   }
 | ||
|  | 
 | ||
|  |   usb_fill_int_urb(urb, priv_dev->udev,
 | ||
|  |                    usb_rcvintpipe(priv_dev->udev, 1),
 | ||
|  |                    buf, PANDA_USB_RX_BUFF_SIZE,
 | ||
|  |                    panda_usb_read_int_callback, priv_dev, 5);
 | ||
|  |   urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
 | ||
|  | 
 | ||
|  |   usb_anchor_urb(urb, &priv_dev->rx_submitted);
 | ||
|  | 
 | ||
|  |   err = usb_submit_urb(urb, GFP_KERNEL);
 | ||
|  |   if (err) {
 | ||
|  |   usb_unanchor_urb(urb);
 | ||
|  |     usb_free_coherent(priv_dev->udev, PANDA_USB_RX_BUFF_SIZE,
 | ||
|  | 		      buf, urb->transfer_dma);
 | ||
|  |     usb_free_urb(urb);
 | ||
|  |     dev_err(priv_dev->dev, "Failed in start, while submitting urb.\n");
 | ||
|  |     return err;
 | ||
|  |   }
 | ||
|  | 
 | ||
|  |   /* Drop reference, USB core will take care of freeing it */
 | ||
|  |   usb_free_urb(urb);
 | ||
|  | 
 | ||
|  | 
 | ||
|  |   return 0;
 | ||
|  | }
 | ||
|  | 
 | ||
|  | /* Open USB device */
 | ||
|  | static int panda_usb_open(struct net_device *netdev)
 | ||
|  | {
 | ||
|  |   struct panda_inf_priv *priv = netdev_priv(netdev);
 | ||
|  |   int err;
 | ||
|  | 
 | ||
|  |   /* common open */
 | ||
|  |   err = open_candev(netdev);
 | ||
|  |   if (err)
 | ||
|  |     return err;
 | ||
|  | 
 | ||
|  |   //priv->can_speed_check = true;
 | ||
|  |   priv->can.state = CAN_STATE_ERROR_ACTIVE;
 | ||
|  | 
 | ||
|  |   netif_start_queue(netdev);
 | ||
|  | 
 | ||
|  |   return 0;
 | ||
|  | }
 | ||
|  | 
 | ||
|  | /* Close USB device */
 | ||
|  | static int panda_usb_close(struct net_device *netdev)
 | ||
|  | {
 | ||
|  |   struct panda_inf_priv *priv = netdev_priv(netdev);
 | ||
|  | 
 | ||
|  |   priv->can.state = CAN_STATE_STOPPED;
 | ||
|  | 
 | ||
|  |   netif_stop_queue(netdev);
 | ||
|  | 
 | ||
|  |   /* Stop polling */
 | ||
|  |   panda_urb_unlink(priv);
 | ||
|  | 
 | ||
|  |   close_candev(netdev);
 | ||
|  | 
 | ||
|  |   return 0;
 | ||
|  | }
 | ||
|  | 
 | ||
|  | static netdev_tx_t panda_usb_start_xmit(struct sk_buff *skb,
 | ||
|  | 					struct net_device *netdev)
 | ||
|  | {
 | ||
|  |   struct panda_inf_priv *priv_inf = netdev_priv(netdev);
 | ||
|  |   struct can_frame *cf = (struct can_frame *)skb->data;
 | ||
|  |   struct panda_usb_ctx *ctx = NULL;
 | ||
|  |   struct net_device_stats *stats = &priv_inf->netdev->stats;
 | ||
|  |   int err;
 | ||
|  |   struct panda_usb_can_msg usb_msg = {};
 | ||
|  |   int bus = priv_inf->mcu_can_ifnum;
 | ||
|  | 
 | ||
|  |   if (can_dropped_invalid_skb(netdev, skb)){
 | ||
|  |     printk("Invalid CAN packet");
 | ||
|  |     return NETDEV_TX_OK;
 | ||
|  |   }
 | ||
|  | 
 | ||
|  |   ctx = panda_usb_get_free_ctx(priv_inf, cf);
 | ||
|  | 
 | ||
|  |   //Warning: cargo cult. Can't tell what this is for, but it is
 | ||
|  |   //everywhere and encouraged in the documentation.
 | ||
|  |   can_put_echo_skb(skb, priv_inf->netdev, ctx->ndx);
 | ||
|  | 
 | ||
|  |   if(cf->can_id & CAN_EFF_FLAG){
 | ||
|  |     usb_msg.rir = cpu_to_le32(((cf->can_id & 0x1FFFFFFF) << 3) |
 | ||
|  | 			      PANDA_CAN_TRANSMIT | PANDA_CAN_EXTENDED);
 | ||
|  |   }else{
 | ||
|  |     usb_msg.rir = cpu_to_le32(((cf->can_id & 0x7FF) << 21) | PANDA_CAN_TRANSMIT);
 | ||
|  |   }
 | ||
|  |   usb_msg.bus_dat_len = cpu_to_le32((cf->can_dlc & 0x0F) | (bus << 4));
 | ||
|  | 
 | ||
|  |   memcpy(usb_msg.data, cf->data, cf->can_dlc);
 | ||
|  | 
 | ||
|  |   //TODO Handle Remote Frames
 | ||
|  |   //if (cf->can_id & CAN_RTR_FLAG)
 | ||
|  |   //  usb_msg.dlc |= PANDA_DLC_RTR_MASK;
 | ||
|  | 
 | ||
|  |   netdev_err(netdev, "Received data from socket. canid: %x; len: %d\n", cf->can_id, cf->can_dlc);
 | ||
|  | 
 | ||
|  |   err = panda_usb_xmit(priv_inf, &usb_msg, ctx);
 | ||
|  |   if (err)
 | ||
|  |     goto xmit_failed;
 | ||
|  | 
 | ||
|  |   return NETDEV_TX_OK;
 | ||
|  | 
 | ||
|  |  xmit_failed:
 | ||
|  |   can_free_echo_skb(priv_inf->netdev, ctx->ndx);
 | ||
|  |   panda_usb_free_ctx(ctx);
 | ||
|  |   dev_kfree_skb(skb);
 | ||
|  |   stats->tx_dropped++;
 | ||
|  | 
 | ||
|  |   return NETDEV_TX_OK;
 | ||
|  | }
 | ||
|  | 
 | ||
|  | static const struct net_device_ops panda_netdev_ops = {
 | ||
|  |   .ndo_open = panda_usb_open,
 | ||
|  |   .ndo_stop = panda_usb_close,
 | ||
|  |   .ndo_start_xmit = panda_usb_start_xmit,
 | ||
|  | };
 | ||
|  | 
 | ||
|  | static int panda_usb_probe(struct usb_interface *intf,
 | ||
|  | 			   const struct usb_device_id *id)
 | ||
|  | {
 | ||
|  |   struct net_device *netdev;
 | ||
|  |   struct panda_inf_priv *priv_inf;
 | ||
|  |   int err = -ENOMEM;
 | ||
|  |   int inf_num;
 | ||
|  |   struct panda_dev_priv *priv_dev;
 | ||
|  |   struct usb_device *usbdev = interface_to_usbdev(intf);
 | ||
|  | 
 | ||
|  |   priv_dev = kzalloc(sizeof(struct panda_dev_priv), GFP_KERNEL);
 | ||
|  |   if (!priv_dev) {
 | ||
|  |     dev_err(&intf->dev, "Couldn't alloc priv_dev\n");
 | ||
|  |     return -ENOMEM;
 | ||
|  |   }
 | ||
|  |   priv_dev->udev = usbdev;
 | ||
|  |   priv_dev->dev = &intf->dev;
 | ||
|  |   usb_set_intfdata(intf, priv_dev);
 | ||
|  | 
 | ||
|  |   ////// Interface privs
 | ||
|  |   for(inf_num = 0; inf_num < PANDA_NUM_CAN_INTERFACES; inf_num++){
 | ||
|  |     netdev = alloc_candev(sizeof(struct panda_inf_priv), PANDA_MAX_TX_URBS);
 | ||
|  |     if (!netdev) {
 | ||
|  |       dev_err(&intf->dev, "Couldn't alloc candev\n");
 | ||
|  |       goto cleanup_candev;
 | ||
|  |     }
 | ||
|  |     netdev->netdev_ops = &panda_netdev_ops;
 | ||
|  |     netdev->flags |= IFF_ECHO; /* we support local echo */
 | ||
|  | 
 | ||
|  |     priv_inf = netdev_priv(netdev);
 | ||
|  |     priv_inf->netdev = netdev;
 | ||
|  |     priv_inf->priv_dev = priv_dev;
 | ||
|  |     priv_inf->interface_num = inf_num;
 | ||
|  |     priv_inf->mcu_can_ifnum = can_numbering[inf_num];
 | ||
|  | 
 | ||
|  |     init_usb_anchor(&priv_dev->rx_submitted);
 | ||
|  |     init_usb_anchor(&priv_inf->tx_submitted);
 | ||
|  | 
 | ||
|  |     /* Init CAN device */
 | ||
|  |     priv_inf->can.state = CAN_STATE_STOPPED;
 | ||
|  |     priv_inf->can.bittiming.bitrate = PANDA_BITRATE;
 | ||
|  | 
 | ||
|  |     SET_NETDEV_DEV(netdev, &intf->dev);
 | ||
|  | 
 | ||
|  |     err = register_candev(netdev);
 | ||
|  |     if (err) {
 | ||
|  |       netdev_err(netdev, "couldn't register PANDA CAN device: %d\n", err);
 | ||
|  |       free_candev(priv_inf->netdev);
 | ||
|  |       goto cleanup_candev;
 | ||
|  |     }
 | ||
|  | 
 | ||
|  |     priv_dev->interfaces[inf_num] = priv_inf;
 | ||
|  |   }
 | ||
|  | 
 | ||
|  |   err = panda_usb_start(priv_dev);
 | ||
|  |   if (err) {
 | ||
|  |     dev_err(&intf->dev, "Failed to initialize Comma.ai Panda CAN controller\n");
 | ||
|  |     goto cleanup_candev;
 | ||
|  |   }
 | ||
|  | 
 | ||
|  |   err = panda_set_output_enable(priv_inf, true);
 | ||
|  |   if (err) {
 | ||
|  |     dev_info(&intf->dev, "Failed to initialize send enable message to Panda.\n");
 | ||
|  |     goto cleanup_candev;
 | ||
|  |   }
 | ||
|  | 
 | ||
|  |   dev_info(&intf->dev, "Comma.ai Panda CAN controller connected\n");
 | ||
|  | 
 | ||
|  |   return 0;
 | ||
|  | 
 | ||
|  |  cleanup_candev:
 | ||
|  |   for(inf_num = 0; inf_num < PANDA_NUM_CAN_INTERFACES; inf_num++){
 | ||
|  |     priv_inf = priv_dev->interfaces[inf_num];
 | ||
|  |     if(priv_inf){
 | ||
|  |       unregister_candev(priv_inf->netdev);
 | ||
|  |       free_candev(priv_inf->netdev);
 | ||
|  |     }else
 | ||
|  |       break;
 | ||
|  |   }
 | ||
|  | 
 | ||
|  |   kfree(priv_dev);
 | ||
|  | 
 | ||
|  |   return err;
 | ||
|  | }
 | ||
|  | 
 | ||
|  | /* Called by the usb core when driver is unloaded or device is removed */
 | ||
|  | static void panda_usb_disconnect(struct usb_interface *intf)
 | ||
|  | {
 | ||
|  |   struct panda_dev_priv *priv_dev = usb_get_intfdata(intf);
 | ||
|  |   struct panda_inf_priv *priv_inf;
 | ||
|  |   int inf_num;
 | ||
|  | 
 | ||
|  |   usb_set_intfdata(intf, NULL);
 | ||
|  | 
 | ||
|  |   for(inf_num = 0; inf_num < PANDA_NUM_CAN_INTERFACES; inf_num++){
 | ||
|  |     priv_inf = priv_dev->interfaces[inf_num];
 | ||
|  |     if(priv_inf){
 | ||
|  |       netdev_info(priv_inf->netdev, "device disconnected\n");
 | ||
|  |       unregister_candev(priv_inf->netdev);
 | ||
|  |       free_candev(priv_inf->netdev);
 | ||
|  |     }else
 | ||
|  |       break;
 | ||
|  |   }
 | ||
|  | 
 | ||
|  |   panda_urb_unlink(priv_inf);
 | ||
|  |   kfree(priv_dev);
 | ||
|  | }
 | ||
|  | 
 | ||
|  | static struct usb_driver panda_usb_driver = {
 | ||
|  |   .name = PANDA_MODULE_NAME,
 | ||
|  |   .probe = panda_usb_probe,
 | ||
|  |   .disconnect = panda_usb_disconnect,
 | ||
|  |   .id_table = panda_usb_table,
 | ||
|  | };
 | ||
|  | 
 | ||
|  | module_usb_driver(panda_usb_driver);
 | ||
|  | 
 | ||
|  | MODULE_LICENSE("GPL");
 | ||
|  | MODULE_AUTHOR("Jessy Diamond Exum <jessy.diamondman@gmail.com>");
 | ||
|  | MODULE_DESCRIPTION("SocketCAN driver for Comma.ai's Panda Adapter.");
 | ||
|  | MODULE_VERSION("0.1");
 |