mirror of
https://github.com/dragonpilot/dragonpilot.git
synced 2026-06-24 15:32:07 +08:00
bc7b9b38ae
a648ccae Add os import 042562dd Extracted wifi connect from test helpers ac0fd5dd query fw versions example - use extended diagnostic session 4e9d788a Remove not-needed cadillac-init f0a5d154 typo c093286b Add bootkick after re-enabling phone power (#401) eadb0dbb security upgrades (#397) 7c13bec0 Command to get signature (#399) dad439a4 static assert on size of health packet (#398) da9da466 Fix VERSION df4159c8 Revert "Revert "Register readback on most modules. Still need to convert the other ones (#396)"" 56ec2150 Revert "Register readback on most modules. Still need to convert the other ones (#396)" 893e4861 Register readback on most modules. Still need to convert the other ones (#396) 6bbae7be VW safety: allow cancel spam on both buses to be compatible with camera and gateway integration d5f7a287 bump panda 1bcc351f ignition_can: set it to False after 2s of not seeing CAN msgs 96137f1a VW can based ignition not needed. it has ignition line at camera as well. 1b004a18 Same flake8 version as the one in openpilot e82ba5f2 Same pylint version as the one in openpilot 656f99b0 Interrupt refactor (NVIC_SM_1: #334) and Fault handling (#377) (PR #373) 000282e5 Fix can_logger.py to run correctly on python3 (#392) 7f9b4a59 Fix USB device enumeration on Windows 8.1 and Windows 10 (#393) dec565c7 Update Misra test coverage, which now includes rule 2.7 fb6bc3ba Fix Misra 878dd00a solve race condition is relay_malfunction right after changing the relay status by adding a counter 2d4cb05c add a safety mode counter a6797a21 Implement USB power mode on uno 670f90cc Merge branch 'master' of github.com:commaai/panda ca39a5d8 Added faults integer to health packet e1c34a1a Panda Jungle testing (#394) 2a093a39 Added heartbeat to echo test 22464356 Fixed health struct size. We should really get an automated test for this f458d67a Add uptime counter to the health packet (#391) 16624811 enable CAN transcievers outside the set_safety_mode function, which is not related a7c98744 bump panda ver 1192d934 Power saving refactor (#389) d58d08fb Fix Misra 17.8: can't mod function params bc685ac9 Minor indent a54b86c4 Failure of set_safety_mode falls back to SILENT. Failure to set silent results in hanging 597436d3 NOOUTPUT safety mode is now SILENT. NOOUTPUT still exists but keeps C… (#388) d229f8dc ESP forced off in EON build. this prevents ESP to be turned on when e… (#387) 8a044b34 forgot Hyundai: now also using make_msg 4f9c8796 remove abunch of lines from safety regression tests by using common make_msg function fb814143 mispelled word 57f5ef8c Fix misra: addr can't be more than 29 bits anyway 68ff5012 typo d5c772b0 Fixe Toyota message white-list 48197a92 Better masking for ELM mode b8fe78c3 VW is also tested for safety replay 212d336b Safety Chrysler: Added cancel spam button check d44b5621 fix print in example 02d579a5 functional addr handling 6249a183 tx_hook shall have a white-list of messages (#381) 8138fc14 uds: handle function addrs and fw version query example 6626a542 Fixed python health api b9b79e8b uds zero second timeout e0de1a4f define ALLOW_DEBUG in safety tests 86dec4b8 Safety modes that violate ISO26262 requirements are not compiled in RELEASE build e74ed936 safety tests a bit more simplified 2027765b relay malfunction test centralized 8af1a01a clean up safety tests e8f7a3b2 upd panda cfcce8f0 WIP: Relay malfunction (#384) 69d9d610 No tabs in mazda safety a86418c1 insignificant changes f239b996 single addr was better d063a188 Hyundai safety: re-enable button spam safety check 4d1edc06 skip tx_hook if a message is forwarded (#379) df2ff045 bump version 168461d5 added fault state to health packet b3e1a133 uds: better debug prints 68c39fb3 uds: no need for threads if you always drain rx 91b7c5bb bump Panda Ver 26cb4dc4 Fixed pylint error 32725cc3 Fixed misra compliance e33b4bea Added echo script 312ba62d minor comment cleanupo e90897a8 Fix board detection on white 0e72c183 always stop executing if safety mode fails to be set (suggested by jyoung8607) e8d7ed1d Rename function name to not confuse safety_set_mode and set_safety_mode ff86db65 improve uds message processing 512ab3f2 except Exception 37ce507a py3 all bac4d854 dos and python3 501db8d1 uds drain before send and use has_obd() f2cbec16 Added has_obd() to python library 48e5b182 Add SDK downloading to the build step (#314) e0762c2e Add Python & USB API for controlling phone power (#313) ba9fb69f New health packet struct also in the python libs git-subtree-dir: panda git-subtree-split: a648ccae4b3661ca6de7a4ac199cc44a41442b74
617 lines
16 KiB
C
617 lines
16 KiB
C
/**
|
|
* @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
|
|
|
|
#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;
|
|
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 ? 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;
|
|
|
|
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");
|