panda/drivers/linux/panda.c

617 lines
16 KiB
C
Raw Normal View History

/**
* @file panda.c
2017-06-25 23:59:04 -06:00
* @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.
2017-06-27 20:46:26 -06:00
* @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
#define SAFETY_ALLOUTPUT 17
#define SAFETY_SILENT 0
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;
2017-06-21 21:17:55 -06:00
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 = 2
const int can_numbering[] = {0,1,2};
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);
}
Revert commits that broke USB for openpilot. Revert "fix openpilot board flashing" This reverts commit 8ff93ad5da39f8dc4bf6fe632f26418b696fd230. Revert "Fixed output_enabled led not turning off when mode changed to no output." This reverts commit 27a8af11075d92d03c389713694a879905877cf0. Revert "Fixed loopback test for new GMLAN 'can4' behavior." This reverts commit 59592f599af01a667b4fd966e613b8f504d62dc2. Revert "GMLAN is now always mapped through CAN4 (index 3)" This reverts commit 329c09102435bfd9b1fbb60694139a5ff7bf4148. Revert "Removed compile time config for CAN loopback, implemented as usb message." This reverts commit e1a4c3298557fccf854ed5cbda448f8c0015b7ea. Revert "Change all output safety mode identifier to prevent user mistakes." This reverts commit 6b363e2e92fcd5e7f25b5458fe9008ff8f9fd664. Revert "untabify" This reverts commit 191f67b083e182323ba956c3ab75df10bec2f863. Revert "Refactor of safety to support more modular additions of safety policies." This reverts commit e5b524eddc82e53587cc47dcf15b22fd35890a92. Revert "Split up some more header files into compilation units." This reverts commit e2a78912f5b649822974fc0e974ec50d9d9c7d10. Revert "Enabled emulated control writes over USB." This reverts commit 133cfe970379d6881de26289616d1d9085bb5986. Revert "Moved CAN and USART code out of main.c and into more appropriate files." This reverts commit daad2dc0620d629e7db0dd68dee5595ed2b57160. Revert "Large Panda CAN cleanup. Restrict GMLAN to valid baud rates." This reverts commit a0616a2bc2ac2bfd99223aaa84912e6f649c9d54. Revert "Panda library now correctly sends USB direction bit." This reverts commit 1712c901d4b46b2726b3165a7cb2e91c281c662b. Revert "Board makefile now automatically calculates header file dependencies." This reverts commit 4a8d4e597b397ca6d68dd5dd2a376c8354dc3422. Revert "Loopback test works over wifi. (Disable trying to send over wifi)" This reverts commit dae636968af482e170aade1d785a1e197e9f3c04. Revert "Fix legacy board build" This reverts commit 62bf4e575686c84c672eb0d341ad41f174141c2d. Revert "Style cop" This reverts commit c439f43726feb30cf2ec486ffcad6ac94ab5e128. Revert "Untabify" This reverts commit 41e5eec6211c23836535af49380f74350a0ceb12. Revert "Fixed disabling gmlan." This reverts commit 5e1e45a4afade384b628e44587dd8e37d3dcd8cd. Revert "Removed dead code, standardized canid in more commands, better erroring behavior." This reverts commit b59aeb6d87ddd85406ec42e4ed8a74a232d506a4. Revert "loopback test works with new CAN bus ids." This reverts commit 75970861cf2b025173afb906e4e243861bed506a. Revert "Large reorganization of code and early integration of can bitrate setting." This reverts commit a1ed7b62ee66ec8f56bba488c38c67b69eead8cf.
2017-07-12 12:25:10 -06:00
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 ? SAFETY_ALLOUTPUT : SAFETY_SILENT, 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;
2017-06-21 21:17:55 -06:00
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)
{
2017-06-21 21:17:55 -06:00
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:
2017-06-21 21:17:55 -06:00
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) {
2017-06-21 21:17:55 -06:00
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:
2017-06-21 21:17:55 -06:00
usb_fill_int_urb(urb, priv_dev->udev,
usb_rcvintpipe(priv_dev->udev, 1),
urb->transfer_buffer, PANDA_USB_RX_BUFF_SIZE,
2017-06-21 21:17:55 -06:00
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)
2017-06-21 21:17:55 -06:00
dev_err(priv_dev->dev, "failed resubmitting read bulk urb: %d\n", retval);
}
2017-06-21 21:17:55 -06:00
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]);
2017-06-21 21:17:55 -06:00
err = usb_set_interface(priv_dev->udev, 0, 1);
if (err) {
2017-06-21 21:17:55 -06:00
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;
}
2017-06-21 21:17:55 -06:00
buf = usb_alloc_coherent(priv_dev->udev, PANDA_USB_RX_BUFF_SIZE,
GFP_KERNEL, &urb->transfer_dma);
if (!buf) {
2017-06-21 21:17:55 -06:00
dev_err(priv_dev->dev, "No memory left for USB buffer\n");
usb_free_urb(urb);
return -ENOMEM;
}
2017-06-21 21:17:55 -06:00
usb_fill_int_urb(urb, priv_dev->udev,
usb_rcvintpipe(priv_dev->udev, 1),
buf, PANDA_USB_RX_BUFF_SIZE,
2017-06-21 21:17:55 -06:00
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);
2017-06-21 21:17:55 -06:00
usb_free_coherent(priv_dev->udev, PANDA_USB_RX_BUFF_SIZE,
buf, urb->transfer_dma);
usb_free_urb(urb);
2017-06-21 21:17:55 -06:00
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,
2017-06-21 21:17:55 -06:00
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;
2017-06-21 21:17:55 -06:00
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;
}
2017-06-21 21:17:55 -06:00
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");