summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/can/usb/Kconfig12
-rw-r--r--drivers/net/can/usb/Makefile1
-rw-r--r--drivers/net/can/usb/ucan.c1285
3 files changed, 1298 insertions, 0 deletions
diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig
index bcb272f6c68a..9f6207b69e08 100644
--- a/drivers/net/can/usb/Kconfig
+++ b/drivers/net/can/usb/Kconfig
@@ -78,4 +78,16 @@ config CAN_8DEV_USB
This driver supports the USB2CAN interface
from 8 devices (http://www.8devices.com).
+config CAN_UCAN
+ tristate "Theobroma Systems UCAN interface"
+ ---help---
+ This driver supports the Theobroma Systems
+ UCAN USB-CAN interface.
+
+ UCAN is an STM32-based USB-CAN interface that is
+ integrated on compute modules from Theobroma Systems
+ like the A31-uQ7 and the RK3399-Q7.
+
+ It is also available as a standalone USB stick.
+
endmenu
diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile
index a64cf983fb87..3afde989139b 100644
--- a/drivers/net/can/usb/Makefile
+++ b/drivers/net/can/usb/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_CAN_GS_USB) += gs_usb.o
obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o
obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/
obj-$(CONFIG_CAN_8DEV_USB) += usb_8dev.o
+obj-$(CONFIG_CAN_UCAN) += ucan.o
diff --git a/drivers/net/can/usb/ucan.c b/drivers/net/can/usb/ucan.c
new file mode 100644
index 000000000000..f447d836179e
--- /dev/null
+++ b/drivers/net/can/usb/ucan.c
@@ -0,0 +1,1285 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* Driver for Theobroma Systems UCAN devices
+ *
+ * Copyright (C) 2015 Theobroma Systems Design und Consulting GmbH
+ *
+ * This driver is inspired by the 4.0.0 version of drivers/net/can/usb/ems_usb.c
+ *
+ *
+ * General Description:
+ *
+ * The USB Device uses three Endpoints:
+ *
+ * Interrupt Endpoint: Once the device is started the device sends
+ * its TX FIFO status (space left) on this endpoint. The driver uses
+ * this information for flow control.
+ *
+ * IN Enpoint: The device sends CAN Frame Messages and Device
+ * Information using the IN endpoint.
+ *
+ * OUT Endpoint: The driver sends configuration requests, and CAN
+ * Frames on the out endpoint.
+ *
+ * Error Handling: If error reporting is turned on the device
+ * encodes error into CAN error frames (see uapi/linux/can/error.h)
+ * and sends it using the IN Endpoint. The driver updates statistics
+ * and forward it.
+ */
+
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/signal.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Martin Elshuber, Theobroma Systems Design und Consulting GmbH <martin.elshuber@theobroma-systems.com>");
+MODULE_DESCRIPTION("Driver for Theobroma Systems UCAN devices");
+
+#define MAX_TX_URBS 8
+#define MAX_RX_URBS 8
+#define TX_QUEUE_STOP_THRESHOLD 1
+
+static struct usb_device_id ucan_table[] = {
+ {USB_DEVICE_INTERFACE_NUMBER(0x2294, 0x425a, 0)}, /* Mule (soldered onto compute modules) */
+ {USB_DEVICE_INTERFACE_NUMBER(0x2294, 0x425b, 0)}, /* Seal (standalone USB stick) */
+ {} /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, ucan_table);
+
+/* UCAN Message Definitions --------------------------------------------
+ *
+ * ucan_message_out_t and ucan_message_in_t define the messages
+ * transmitted on the OUT and IN endpoint.
+ *
+ * Multibyte fields are transmitted with little endianness
+ *
+ * INTR Endpoint: a single uint32_t storing the current space in the fifo
+ *
+ * OUT Enpoint: single message of type ucan_message_out_t is
+ * transmitted on the out endpoint
+ *
+ * IN Endpoint: multiple messages ucan_message_in_t concateted in
+ * the following way:
+ *
+ * m[n].len <=> the length if message n(including the header in bytes)
+ * m[n] is is aligned to a 4 byte boundary, hence
+ * offset(m[0]) := 0;
+ * offset(m[n+1]) := offset(m[n]) + (m[n].len + 3) & 3
+ *
+ * this implies that
+ * offset(m[n]) % 4 <=> 0
+ */
+
+/* Device Global Commands */
+enum {
+ UCAN_DEVICE_GET_FW_STRING = 0,
+};
+
+/* UCAN Commands */
+enum {
+ /* start the can transceiver - val defines the operation mode */
+ UCAN_COMMAND_START = 0,
+ /* cancel pending transmissions and stop the can transceiver */
+ UCAN_COMMAND_STOP = 1,
+ /* send can transceiver into low-power sleep mode */
+ UCAN_COMMAND_SLEEP = 2,
+ /* wake up can transceiver from low-power sleep mode */
+ UCAN_COMMAND_WAKEUP = 3,
+ /* reset the can transceiver */
+ UCAN_COMMAND_RESET = 4,
+ /* get piece of info from the can transceiver - subcmd defines what
+ * piece
+ */
+ UCAN_COMMAND_GET = 5,
+ /* clear or disable hardware filter - subcmd defines which of the two */
+ UCAN_COMMAND_FILTER = 6,
+ /* Setup bittiming */
+ UCAN_COMMAND_SET_BITTIMING = 7,
+};
+
+/* UCAN_COMMAND_START and UCAN_COMMAND_GET_INFO operation modes (bitmap).
+ * Undefined bits must be set to 0.
+ */
+enum {
+ UCAN_MODE_LOOPBACK = (1 << 0),
+ UCAN_MODE_SILENT = (1 << 1),
+ UCAN_MODE_3_SAMPLES = (1 << 2),
+ UCAN_MODE_ONE_SHOT = (1 << 3),
+ UCAN_MODE_BERR_REPORT = (1 << 4),
+};
+
+/* UCAN_COMMAND_GET subcommands */
+enum {
+ UCAN_COMMAND_GET_INFO = 0,
+ UCAN_COMMAND_GET_PROTOCOL_VERSION = 1,
+};
+
+/* UCAN_COMMAND_FILTER subcommands */
+enum {
+ UCAN_FILTER_CLEAR = 0,
+ UCAN_FILTER_DISABLE = 1,
+ UCAN_FILTER_ENABLE = 2,
+};
+
+/* OUT endpoint message types */
+enum {
+ UCAN_OUT_TX = 2, /* transmit a CAN frame */
+};
+
+/* IN endpoint message types */
+enum {
+ UCAN_IN_RX = 2,
+};
+
+typedef union {
+ /***************************************************
+ * Setup Bittiming
+ * bmRequest == UCAN_COMMAND_START
+ ***************************************************/
+ struct {
+ u16 mode;
+ } __packed cmd_start;
+ /***************************************************
+ * Setup Bittiming
+ * bmRequest == UCAN_COMMAND_SET_BITTIMING
+ ***************************************************/
+ struct {
+ u32 tq; /* Time quanta (TQ) in nanoseconds */
+ u16 brp; /* TQ Prescaler */
+ u16 sample_point; /* Samplepoint on tenth percent */
+ u8 prop_seg; /* Propagation segment in TQs */
+ u8 phase_seg1; /* Phase buffer segment 1 in TQs */
+ u8 phase_seg2; /* Phase buffer segment 2 in TQs */
+ u8 sjw; /* Synchronisation jump width in TQs */
+ } __packed cmd_set_bittiming;
+ /***************************************************
+ * Setup HW Filter Modes
+ * bmRequest == UCAN_ENA_FILTER
+ ***************************************************/
+ struct {
+ u32 id;
+ u32 mask;
+ u16 mbox;
+ } __packed cmd_ena_filter;
+ /***************************************************
+ * Get Device Information
+ * bmRequest == UCAN_COMMAND_GET; wValue = UCAN_COMMAND_GET_INFO
+ ***************************************************/
+ struct {
+ u32 freq; /* Clock Frequency for tq generation */
+ u8 tx_fifo; /* Size of the transmission fifo */
+ u8 sjw_max; /* can_bittiming fields... */
+ u8 tseg1_min;
+ u8 tseg1_max;
+ u8 tseg2_min;
+ u8 tseg2_max;
+ u16 brp_inc;
+ u32 brp_min;
+ u32 brp_max; /* ...can_bittiming fields */
+ u16 ctrlmodes; /* supported control modes */
+ u16 hwfilter; /* Number of HW filter banks */
+ u16 rxmboxes; /* Number of receive Mailboxes */
+ } __packed device_info;
+ struct {
+ u32 version;
+ } __packed protocol_version;
+ struct {
+ u8 d[128];
+ } raw;
+} __packed ucan_ctl_payload_t;
+
+
+/* OUT Enpoint, outbound messages */
+struct ucan_message_out {
+ u16 len; /* Length of the content include header */
+ u16 type; /* UCAN_OUT_COMMAND and friends */
+ union {
+ /***************************************************
+ * Transmit CAN frame
+ * (type == UCAN_TX) && ((msg.can_msg.id & CAN_RTR_FLAG) == 0)
+ ***************************************************/
+ struct {
+ /* note DLC is computed by
+ * msg.len - sizeof (msg.len)
+ * - sizeof (msg.type)
+ * - sizeof (msg.can_msg.id)
+ */
+ u32 id;
+ u8 data[8];
+ /* ensure data alignment to 4
+ * by moving dlc after data
+ */
+ } __packed can_msg;
+
+ /***************************************************
+ * Transmit RTR CAN frame
+ * (type == UCAN_TX) && ((msg.can_msg.id & CAN_RTR_FLAG) != 0)
+ ***************************************************/
+ struct {
+ u32 id;
+ u8 dlc;
+ } __packed can_rtr_msg;
+ } msg;
+} __packed __aligned(0x4);
+
+/* IN Enpoint, inbound messages */
+struct ucan_message_in {
+ u16 len; /* Length of the content include header */
+ u16 type; /* UCAN_IN_DEVICE_INFO and friends */
+
+ union {
+ /***************************************************
+ * CAN Frame received
+ * (type == UCAN_IN_RX)
+ * && ((msg.can_msg.id & CAN_RTR_FLAG) == 0)
+ ***************************************************/
+ struct {
+ /* note DLC is computed by
+ * msg.len - sizeof (msg.len)
+ * - sizeof (msg.type)
+ * - sizeof (msg.can_msg.id)
+ */
+ u32 id;
+ u8 data[8];
+ /* ensure data alignment to 4
+ * by moving dlc after data
+ */
+ } __packed can_msg;
+
+ /***************************************************
+ * CAN RTR Frame received
+ * (type == UCAN_IN_RX)
+ * && ((msg.can_msg.id & CAN_RTR_FLAG) != 0)
+ ***************************************************/
+ struct {
+ u32 id;
+ u8 dlc;
+ } __packed can_rtr_msg;
+
+ } __aligned(0x4) msg;
+} __packed;
+
+/* Macros to calculate message lengths */
+#define UCAN_OUT_HDR_SIZE offsetof(struct ucan_message_out, msg)
+#define UCAN_OUT_LEN(member) (UCAN_OUT_HDR_SIZE + sizeof(member))
+
+#define UCAN_IN_HDR_SIZE offsetof(struct ucan_message_in, msg)
+#define UCAN_IN_LEN(member) (UCAN_OUT_HDR_SIZE + sizeof(member))
+
+struct ucan;
+
+/* Context Information for transmission URBs */
+struct ucan_urb_context {
+ struct ucan *up;
+ u32 echo_index;
+ u8 dlc;
+};
+
+/* Information reported by the USB device */
+struct ucan_device_info {
+ struct can_bittiming_const bittiming_const;
+ int tx_fifo;
+};
+
+/* Driver private data */
+struct ucan {
+ struct can_priv can; /* must be the first member */
+
+ u8 intf_index;
+ struct usb_device *udev;
+ struct usb_interface *intf;
+ struct net_device *netdev;
+
+ struct usb_endpoint_descriptor *out_ep;
+ struct usb_endpoint_descriptor *in_ep;
+ struct usb_endpoint_descriptor *irq_ep;
+
+ struct usb_anchor rx_urbs;
+ struct usb_anchor tx_urbs;
+ struct urb *irq_urb;
+ u32 *irq_data;
+
+ ucan_ctl_payload_t *ctl_msg_buffer;
+ struct ucan_device_info device_info;
+
+ atomic_t active_tx_urbs;
+ atomic_t free_slots;
+ struct ucan_urb_context tx_urb_contexts[MAX_TX_URBS];
+};
+
+static void ucan_tx_wake_queue_if_possible(struct ucan *up)
+{
+ if ( (netif_queue_stopped(up->netdev)) &&
+ (atomic_read(&up->active_tx_urbs) < MAX_TX_URBS) &&
+ (atomic_read(&up->free_slots) >= TX_QUEUE_STOP_THRESHOLD) ) {
+ netif_wake_queue(up->netdev);
+ }
+}
+
+static int ucan_ctrl_command_out(struct ucan *up, u8 cmd, u16 subcmd, size_t datalen)
+{
+ if (datalen > sizeof(ucan_ctl_payload_t))
+ return -ENOMEM;
+
+ return usb_control_msg(up->udev,
+ usb_sndctrlpipe(up->udev, 0),
+ cmd,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ cpu_to_le16(subcmd),
+ up->intf_index,
+ up->ctl_msg_buffer,
+ datalen,
+ 500);
+}
+
+static int ucan_ctrl_command_in(struct ucan *up, u8 cmd, u16 subcmd, size_t datalen)
+{
+ if (datalen > sizeof(ucan_ctl_payload_t))
+ return -ENOMEM;
+
+ return usb_control_msg(up->udev,
+ usb_rcvctrlpipe(up->udev, 0),
+ cmd,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ cpu_to_le16(subcmd),
+ up->intf_index,
+ up->ctl_msg_buffer,
+ datalen,
+ 500);
+}
+
+static int ucan_device_request_in(struct ucan *up, u8 cmd, u16 subcmd, size_t datalen)
+{
+ if (datalen > sizeof(ucan_ctl_payload_t))
+ return -ENOMEM;
+
+ return usb_control_msg(up->udev,
+ usb_rcvctrlpipe(up->udev, 0),
+ cmd,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ cpu_to_le16(subcmd),
+ 0,
+ up->ctl_msg_buffer,
+ datalen,
+ 500);
+}
+
+/* Request the device Information */
+static int ucan_get_device_info(struct ucan *up)
+{
+ int ret;
+ struct can_bittiming_const *bittiming =
+ &up->device_info.bittiming_const;
+
+ ret = ucan_ctrl_command_in(up, UCAN_COMMAND_GET, UCAN_COMMAND_GET_INFO,
+ sizeof(up->ctl_msg_buffer->device_info));
+ if (ret < 0)
+ return ret;
+ if (ret < sizeof(up->ctl_msg_buffer->device_info))
+ return -EINVAL;
+
+ /* store the data */
+ up->can.clock.freq = le32_to_cpu(up->ctl_msg_buffer->device_info.freq);
+ up->device_info.tx_fifo = up->ctl_msg_buffer->device_info.tx_fifo;
+ strcpy(bittiming->name, "ucan");
+ bittiming->tseg1_min = up->ctl_msg_buffer->device_info.tseg1_min;
+ bittiming->tseg1_max = up->ctl_msg_buffer->device_info.tseg1_max;
+ bittiming->tseg2_min = up->ctl_msg_buffer->device_info.tseg2_min;
+ bittiming->tseg2_max = up->ctl_msg_buffer->device_info.tseg2_max;
+ bittiming->sjw_max = up->ctl_msg_buffer->device_info.sjw_max;
+ bittiming->brp_min = le32_to_cpu(up->ctl_msg_buffer->device_info.brp_min);
+ bittiming->brp_max = le32_to_cpu(up->ctl_msg_buffer->device_info.brp_max);
+ bittiming->brp_inc = le16_to_cpu(up->ctl_msg_buffer->device_info.brp_inc);
+
+ up->can.ctrlmode_supported = 0;
+
+ if (le16_to_cpu(up->ctl_msg_buffer->device_info.ctrlmodes) & UCAN_MODE_LOOPBACK)
+ up->can.ctrlmode_supported |= CAN_CTRLMODE_LOOPBACK;
+ if (le16_to_cpu(up->ctl_msg_buffer->device_info.ctrlmodes) & UCAN_MODE_SILENT)
+ up->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY;
+ if (le16_to_cpu(up->ctl_msg_buffer->device_info.ctrlmodes) & UCAN_MODE_3_SAMPLES)
+ up->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES;
+ if (le16_to_cpu(up->ctl_msg_buffer->device_info.ctrlmodes) & UCAN_MODE_ONE_SHOT)
+ up->can.ctrlmode_supported |= CAN_CTRLMODE_ONE_SHOT;
+ if (le16_to_cpu(up->ctl_msg_buffer->device_info.ctrlmodes) & UCAN_MODE_BERR_REPORT)
+ up->can.ctrlmode_supported |= CAN_CTRLMODE_BERR_REPORTING;
+
+ return 0;
+}
+
+/* Callback when the device sends the IRQ sate *
+ *
+ * This function simply stores the current TX fifo state for flow
+ * control
+ */
+static void ucan_read_irq_callback(struct urb *urb)
+{
+ int ret;
+ struct ucan *up = urb->context;
+ struct net_device *netdev = up->netdev;
+
+ switch (urb->status) {
+ case 0:
+ atomic_set(&up->free_slots, le32_to_cpu(*up->irq_data));
+ ucan_tx_wake_queue_if_possible(up);
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ case -ETIME:
+ case -EPROTO:
+ dev_dbg(&up->udev->dev, "%s ENOENT|ESHUTDOWN|ETIME\n",
+ __func__);
+ return;
+ default:
+ dev_warn(&up->udev->dev, "%s error (%d)\n", __func__,
+ urb->status);
+ return;
+ }
+
+ usb_fill_int_urb(urb, up->udev,
+ usb_rcvintpipe(up->udev, up->irq_ep->bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK),
+ urb->transfer_buffer, up->irq_ep->wMaxPacketSize,
+ ucan_read_irq_callback, up, up->irq_ep->bInterval);
+
+ ret = usb_submit_urb(urb, GFP_KERNEL);
+
+ if (ret == -ENODEV)
+ netif_device_detach(netdev);
+ else if (ret)
+ dev_err(&up->udev->dev, "failed resubmitting urb: %d\n", ret);
+}
+
+/* Handle a CAN error frame that we have received from the device */
+static void ucan_handle_error_frame(struct ucan *up, struct ucan_message_in *m, u32 canid) {
+ enum can_state new_state = CAN_STATE_ERROR_ACTIVE;
+ struct net_device_stats *net_stats = &up->netdev->stats;
+ struct can_device_stats *can_stats = &up->can.can_stats;
+
+ if (canid & CAN_ERR_LOSTARB)
+ can_stats->arbitration_lost++;
+
+ if (canid & CAN_ERR_BUSERROR)
+ can_stats->bus_error++;
+
+ if (canid & CAN_ERR_ACK)
+ net_stats->tx_errors++;
+
+ if (canid & CAN_ERR_BUSOFF)
+ new_state = CAN_STATE_BUS_OFF;
+
+ /* controller problems, details in data[1] */
+ if (canid & CAN_ERR_CRTL) {
+ u8 d1 = m->msg.can_msg.data[1];
+
+ if (d1 & (CAN_ERR_CRTL_RX_PASSIVE | CAN_ERR_CRTL_TX_PASSIVE))
+ new_state = max(new_state, (enum can_state)CAN_STATE_ERROR_PASSIVE);
+
+ if (d1 & (CAN_ERR_CRTL_RX_WARNING | CAN_ERR_CRTL_TX_WARNING))
+ new_state = max(new_state, (enum can_state)CAN_STATE_ERROR_WARNING);
+
+ if (d1 & CAN_ERR_CRTL_RX_OVERFLOW)
+ net_stats->rx_over_errors++;
+ }
+
+ /* protocol error, details in data[2] */
+ if (canid & CAN_ERR_PROT) {
+ u8 d2 = m->msg.can_msg.data[2];
+
+ if (d2 & CAN_ERR_PROT_TX)
+ net_stats->tx_errors++;
+ else
+ net_stats->rx_errors++;
+ }
+
+ /* we switched into a better state */
+ if (up->can.state >= new_state) {
+ up->can.state = new_state;
+ return;
+ }
+
+ /* we switched into a worse state */
+ dev_dbg(&up->udev->dev, "%s: switching from state %d to %d\n", __func__, up->can.state, new_state);
+ up->can.state = new_state;
+ switch(new_state) {
+ case CAN_STATE_BUS_OFF:
+ can_stats->bus_off++;
+ can_bus_off(up->netdev);
+ netdev_info(up->netdev, "link has gone into BUS-OFF state\n");
+ break;
+ case CAN_STATE_ERROR_PASSIVE:
+ can_stats->error_passive++;
+ break;
+ case CAN_STATE_ERROR_WARNING:
+ can_stats->error_warning++;
+ break;
+ default:
+ break;
+ }
+}
+
+/* Callback on reception of a can frame via the IN endpoint
+ *
+ * This function allocates an skb and transferres it to the Linux
+ * network stack
+ */
+static void ucan_rx_can_msg(struct ucan *up, struct ucan_message_in *m)
+{
+ int len;
+ u32 canid;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ struct net_device_stats *stats = &up->netdev->stats;
+
+ /* get the contents of the length field */
+ len = le16_to_cpu(m->len);
+
+ /* check sanity */
+ if (len < UCAN_IN_HDR_SIZE + sizeof(m->msg.can_msg.id)) {
+ dev_warn(&up->udev->dev, "invalid input message len\n");
+ return;
+ }
+
+ /* handle error frames */
+ canid = le32_to_cpu(m->msg.can_msg.id);
+ if (canid & CAN_ERR_FLAG) {
+ ucan_handle_error_frame(up, m, canid);
+ /* drop frame if berr-reporting is off */
+ if (!(up->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING))
+ return;
+ }
+
+ /* allocate skb */
+ skb = alloc_can_skb(up->netdev, &cf);
+ if (!skb)
+ return;
+
+ /* fill the can frame */
+ cf->can_id = le32_to_cpu(m->msg.can_msg.id);
+ cf->can_dlc = len - (UCAN_IN_HDR_SIZE + sizeof(m->msg.can_msg.id));
+
+ if (cf->can_dlc > sizeof(m->msg.can_msg.data))
+ goto err_freeskb;
+
+ if (cf->can_dlc < 0)
+ goto err_freeskb;
+
+ if (cf->can_id & CAN_EFF_FLAG)
+ cf->can_id &=
+ (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG | CAN_ERR_FLAG);
+ else
+ cf->can_id &= (CAN_SFF_MASK | CAN_RTR_FLAG | CAN_ERR_FLAG);
+
+ if (cf->can_id & CAN_RTR_FLAG) {
+ cf->can_id |= CAN_RTR_FLAG;
+ cf->can_dlc = m->msg.can_rtr_msg.dlc;
+ } else {
+ memcpy(cf->data, m->msg.can_msg.data, cf->can_dlc);
+ }
+
+ /* pass it to Linux */
+ netif_receive_skb(skb);
+
+ /* don't count error frames as real packets */
+ if (!(canid & CAN_ERR_FLAG)) {
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+ }
+
+ return;
+err_freeskb:
+ kfree_skb(skb);
+}
+
+/* callback on reception of a USB message */
+static void ucan_read_bulk_callback(struct urb *urb)
+{
+ int ret;
+ int len;
+ int pos;
+ struct ucan *up = urb->context;
+ struct net_device *netdev = up->netdev;
+ struct ucan_message_in m;
+
+ /* check URB status */
+ switch (urb->status) {
+ case 0:
+ break;
+ case -ENOENT:
+ case -EPIPE:
+ case -EPROTO:
+ case -ESHUTDOWN:
+ case -ETIME:
+ dev_dbg(&up->udev->dev, "%s ENOENT|ESHUTDOWN|ETIME\n",
+ __func__);
+ return;
+ default:
+ goto resubmit;
+ }
+
+ /* iterate over input */
+ pos = 0;
+ while (pos < urb->actual_length) {
+ /* check sanity (length of header) */
+ if ((urb->actual_length - pos) < UCAN_IN_HDR_SIZE) {
+ dev_warn(&up->udev->dev, "invalid input message %d; too short (no header)\n",
+ urb->actual_length);
+ goto resubmit;
+ }
+
+ /* copy the message header */
+ memcpy(&m, urb->transfer_buffer + pos, UCAN_IN_HDR_SIZE);
+ len = le16_to_cpu(m.len);
+
+ /* check sanity (length of content) */
+ if (urb->actual_length - pos < len) {
+ dev_warn(&up->udev->dev, "invalid input message al:%d pos:%d len:%d; too short (no data)\n",
+ urb->actual_length, pos, len);
+ print_hex_dump(KERN_WARNING, "raw data: ", DUMP_PREFIX_ADDRESS, 16, 1, urb->transfer_buffer, urb->actual_length, true);
+
+ goto resubmit;
+ }
+
+ // copy remainder of packet
+ memcpy(&m.msg, urb->transfer_buffer + UCAN_IN_HDR_SIZE + pos,
+ len - UCAN_IN_HDR_SIZE);
+
+ switch (__le16_to_cpu(m.type)) {
+ case UCAN_IN_RX:
+ ucan_rx_can_msg(up, &m);
+ break;
+ default:
+ dev_warn(&up->udev->dev,
+ "invalid input message type\n");
+ break;
+ }
+
+ /* proceed to next message */
+ pos += len;
+ /* align to 4 byte boundary */
+ pos = round_up(pos, 4);
+ }
+
+resubmit:
+ /* resubmit urb when done */
+ usb_fill_bulk_urb(urb, up->udev,
+ usb_rcvbulkpipe(up->udev, up->in_ep->bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK),
+ urb->transfer_buffer, up->in_ep->wMaxPacketSize,
+ ucan_read_bulk_callback, up);
+
+ ret = usb_submit_urb(urb, GFP_KERNEL);
+
+ if (ret == -ENODEV)
+ netif_device_detach(netdev);
+ else if (ret)
+ dev_err(&up->udev->dev,
+ "failed resubmitting read bulk urb: %d\n", ret);
+}
+
+/* callback after transmission of a USB message */
+static void ucan_write_bulk_callback(struct urb *urb)
+{
+ struct ucan_urb_context *context = urb->context;
+ struct ucan *up;
+
+ /* get the urb context */
+ if (WARN_ON(!context))
+ return;
+
+ up = context->up;
+
+ /* free up our allocated buffer */
+ usb_free_coherent(urb->dev, sizeof(struct ucan_message_out), urb->transfer_buffer,
+ urb->transfer_dma);
+
+ atomic_dec(&up->active_tx_urbs);
+
+ /* sanity check */
+ if (!netif_device_present(up->netdev))
+ return;
+
+ /* urb state check */
+ if (urb->status)
+ netdev_info(up->netdev, "Tx URB aborted (%d)\n", urb->status);
+
+ up->netdev->trans_start = jiffies;
+
+ /* update statistics */
+ up->netdev->stats.tx_packets++;
+ up->netdev->stats.tx_bytes += context->dlc;
+
+ /* echo can frame */
+ can_get_echo_skb(up->netdev, context->echo_index);
+
+ /* Release context */
+ context->echo_index = -1;
+
+ /* restart the queue if necessary */
+ ucan_tx_wake_queue_if_possible(up);
+}
+
+/* Open the network device */
+static int ucan_open(struct net_device *netdev)
+{
+ int i;
+ int ret;
+ void *buf;
+ u16 ctrlmode;
+ struct urb *urb;
+ struct ucan *up = netdev_priv(netdev);
+
+ /* call CAN layer open */
+ ret = open_candev(netdev);
+ if (ret)
+ goto err;
+
+ ret = -ENOMEM;
+
+ /* set the queue state as empty */
+ atomic_set(&up->free_slots, up->device_info.tx_fifo);
+
+ /* initialize IRQ endpoint */
+ up->irq_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!up->irq_urb)
+ goto err;
+
+ up->irq_data = kzalloc(up->irq_ep->wMaxPacketSize, GFP_KERNEL);
+ if (!up->irq_data) {
+ usb_free_urb(up->irq_urb);
+ goto err;
+ }
+
+ usb_fill_int_urb(up->irq_urb, up->udev,
+ usb_rcvintpipe(up->udev, up->irq_ep->bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK),
+ up->irq_data, up->irq_ep->wMaxPacketSize,
+ ucan_read_irq_callback, up, up->irq_ep->bInterval);
+
+ ret = usb_submit_urb(up->irq_urb, GFP_KERNEL);
+ if (ret) {
+ kfree(up->irq_data);
+ usb_free_urb(up->irq_urb);
+ goto err;
+ }
+
+ /* initialize IN enpoint */
+ for (i = 0; i < MAX_RX_URBS; i++) {
+ ret = -ENOMEM;
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb)
+ goto err;
+
+ buf = usb_alloc_coherent(up->udev, up->in_ep->wMaxPacketSize,
+ GFP_KERNEL, &urb->transfer_dma);
+ if (!buf) {
+ usb_free_urb(urb);
+ goto err;
+ }
+
+ usb_fill_bulk_urb(urb, up->udev,
+ usb_rcvbulkpipe(up->udev, up->in_ep->bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK),
+ buf, up->in_ep->wMaxPacketSize, ucan_read_bulk_callback,
+ up);
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ usb_anchor_urb(urb, &up->rx_urbs);
+
+ ret = usb_submit_urb(urb, GFP_KERNEL);
+
+ if (ret) {
+ usb_unanchor_urb(urb);
+ usb_free_coherent(up->udev, up->in_ep->wMaxPacketSize,
+ buf, urb->transfer_dma);
+ usb_free_urb(urb);
+ goto err;
+ }
+
+ /* Drop reference, USB core will take care of freeing it */
+ usb_free_urb(urb);
+ }
+
+ /* check the control mode */
+ ctrlmode = 0;
+ if (up->can.ctrlmode & CAN_CTRLMODE_LOOPBACK)
+ ctrlmode |= UCAN_MODE_LOOPBACK;
+ if (up->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
+ ctrlmode |= UCAN_MODE_SILENT;
+ if (up->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
+ ctrlmode |= UCAN_MODE_3_SAMPLES;
+ if (up->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT)
+ ctrlmode |= UCAN_MODE_ONE_SHOT;
+
+ // Enable this in any case - filtering is down within the receive path
+ ctrlmode |= UCAN_MODE_BERR_REPORT;
+ up->ctl_msg_buffer->cmd_start.mode = cpu_to_le16(ctrlmode);
+
+ /* start the USB device */
+ ret = ucan_ctrl_command_out(up, UCAN_COMMAND_START, 0, 2);
+ if (ret < 0)
+ goto err;
+
+ up->can.state = CAN_STATE_ERROR_ACTIVE;
+
+ /* start the network queue */
+ netif_start_queue(netdev);
+
+ return 0;
+
+err:
+ usb_kill_anchored_urbs(&up->rx_urbs);
+ return ret;
+}
+
+/* callback when Linux needs to send a can frame */
+static netdev_tx_t ucan_start_xmit(struct sk_buff *skb,
+ struct net_device *netdev)
+{
+ int i, ret, mlen;
+ struct urb *urb;
+ struct ucan_urb_context *context;
+ struct ucan *up = netdev_priv(netdev);
+ struct can_frame *cf = (struct can_frame *)skb->data;
+ struct ucan_message_out *m;
+
+ /* check skb */
+ if (can_dropped_invalid_skb(netdev, skb))
+ return NETDEV_TX_OK;
+
+ /* create a URB, and a buffer for it, and copy the data to the URB */
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb) {
+ netdev_err(netdev, "No memory left for URBs\n");
+ goto drop;
+ }
+
+ m = usb_alloc_coherent(up->udev, sizeof(struct ucan_message_out), GFP_ATOMIC, &urb->transfer_dma);
+ if (!m) {
+ netdev_err(netdev, "No memory left for USB buffer\n");
+ usb_free_urb(urb);
+ goto drop;
+ }
+
+ /* build the USB message */
+ if (cf->can_dlc > sizeof(m->msg.can_msg.data))
+ cf->can_dlc = sizeof(m->msg.can_msg.data);
+
+ m->type = cpu_to_le16(UCAN_OUT_TX);
+ m->msg.can_msg.id = cpu_to_le32(cf->can_id);
+
+ if (cf->can_id & CAN_RTR_FLAG) {
+ mlen = UCAN_OUT_LEN(m->msg.can_rtr_msg);
+ m->msg.can_rtr_msg.dlc = cf->can_dlc;
+ } else {
+ mlen = UCAN_OUT_HDR_SIZE +
+ sizeof(m->msg.can_msg.id) + cf->can_dlc;
+ memcpy(m->msg.can_msg.data, cf->data, cf->can_dlc);
+ }
+ m->len = cpu_to_le16(mlen);
+
+ /* allocate a context */
+ context = NULL;
+ for (i = 0; i < MAX_TX_URBS; i++) {
+ if (up->tx_urb_contexts[i].echo_index == -1) {
+ context = &up->tx_urb_contexts[i];
+ context->up = up;
+ context->echo_index = i;
+ context->dlc = cf->can_dlc;
+ atomic_inc(&up->active_tx_urbs);
+ break;
+ }
+ }
+
+ WARN_ON_ONCE(!context);
+ if (!context) {
+ usb_free_coherent(up->udev, sizeof(struct ucan_message_out), m, urb->transfer_dma);
+ usb_free_urb(urb);
+ goto drop;
+ }
+
+ /* build the urb */
+ usb_fill_bulk_urb(urb, up->udev,
+ usb_sndbulkpipe(up->udev, up->out_ep->bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK),
+ m, mlen, ucan_write_bulk_callback, context);
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ usb_anchor_urb(urb, &up->tx_urbs);
+ can_put_echo_skb(skb, up->netdev, context->echo_index);
+ /* transmit it */
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+
+ // cleanup urb
+ if (ret) {
+ usb_unanchor_urb(urb);
+ usb_free_coherent(up->udev, sizeof(struct ucan_message_out), m, urb->transfer_dma);
+
+ /* on error, clean up */
+ can_free_echo_skb(up->netdev, context->echo_index);
+ dev_kfree_skb(skb);
+
+ context->echo_index = -1;
+ atomic_dec(&up->active_tx_urbs);
+
+ if (ret == -ENODEV) {
+ netif_device_detach(up->netdev);
+ } else {
+ netdev_warn(up->netdev, "failed tx_urb %d\n", ret);
+ up->netdev->stats.tx_dropped++;
+ }
+ }
+ else {
+ atomic_dec(&up->free_slots);
+
+ up->netdev->trans_start = jiffies;
+
+ /* Slow down tx path, if fifo state is low */
+ if ((atomic_read(&up->active_tx_urbs) >= MAX_TX_URBS) ||
+ (atomic_read(&up->free_slots) < TX_QUEUE_STOP_THRESHOLD)) {
+ netif_stop_queue(netdev);
+ }
+ }
+
+ /* release ref, as we do not need the urb anymore */
+ usb_free_urb(urb);
+
+ return NETDEV_TX_OK;
+drop:
+ dev_kfree_skb(skb);
+ up->netdev->stats.tx_dropped++;
+
+ return NETDEV_TX_OK;
+}
+
+/* Device goes down
+ *
+ * Clean up used resources
+ */
+static int ucan_close(struct net_device *netdev)
+{
+ int ret;
+ struct ucan *up = netdev_priv(netdev);
+
+ up->can.state = CAN_STATE_STOPPED;
+
+ netif_stop_queue(netdev);
+
+ ret = ucan_ctrl_command_out( up, UCAN_COMMAND_STOP, 0, 0);
+ if (ret < 0)
+ dev_err(&up->udev->dev,
+ "Could not stop UCAN device, code: %d\n", ret);
+
+ ret = ucan_ctrl_command_out( up, UCAN_COMMAND_RESET, 0, 0);
+ if (ret < 0)
+ dev_err(&up->udev->dev,
+ "Could not reset UCAN device, code: %d\n", ret);
+
+ usb_kill_urb(up->irq_urb);
+ usb_kill_anchored_urbs(&up->rx_urbs);
+ usb_kill_anchored_urbs(&up->tx_urbs);
+ atomic_set(&up->active_tx_urbs, 0);
+ atomic_set(&up->free_slots, 0);
+
+ kfree(up->irq_data);
+
+ close_candev(up->netdev);
+ return 0;
+}
+
+/* CAN driver callbacks */
+static const struct net_device_ops ucan_netdev_ops = {
+ .ndo_open = ucan_open,
+ .ndo_stop = ucan_close,
+ .ndo_start_xmit = ucan_start_xmit,
+ .ndo_change_mtu = can_change_mtu,
+};
+
+/* Request to set bittiming
+ *
+ * This function generates an USB set bittiming message and transmits
+ * it to the device
+ */
+static int ucan_set_bittiming(struct net_device *netdev)
+{
+ int ret;
+ struct ucan *up = netdev_priv(netdev);
+
+ up->ctl_msg_buffer->cmd_set_bittiming.tq = cpu_to_le32(up->can.bittiming.tq);
+ up->ctl_msg_buffer->cmd_set_bittiming.brp = cpu_to_le16(up->can.bittiming.brp);
+ up->ctl_msg_buffer->cmd_set_bittiming.sample_point =
+ cpu_to_le32(up->can.bittiming.sample_point);
+ up->ctl_msg_buffer->cmd_set_bittiming.prop_seg = up->can.bittiming.prop_seg;
+ up->ctl_msg_buffer->cmd_set_bittiming.phase_seg1 = up->can.bittiming.phase_seg1;
+ up->ctl_msg_buffer->cmd_set_bittiming.phase_seg2 = up->can.bittiming.phase_seg2;
+ up->ctl_msg_buffer->cmd_set_bittiming.sjw = up->can.bittiming.sjw;
+
+ dev_dbg(&up->udev->dev,
+ "Setup bittiming\n"
+ " bitrate: %d\n"
+ " sample-point: %d\n"
+ " tq: %d\n"
+ " prop_seg: %d\n"
+ " phase_seg1 %d\n"
+ " phase_seg2 %d\n"
+ " sjw %d\n"
+ " brp %d\n",
+ up->can.bittiming.bitrate, up->can.bittiming.sample_point,
+ up->can.bittiming.tq, up->can.bittiming.prop_seg,
+ up->can.bittiming.phase_seg1, up->can.bittiming.phase_seg2,
+ up->can.bittiming.sjw, up->can.bittiming.brp);
+
+ ret = ucan_ctrl_command_out(up, UCAN_COMMAND_SET_BITTIMING,
+ 0, sizeof(up->ctl_msg_buffer->cmd_set_bittiming));
+ return (ret < 0) ? ret : 0;
+}
+
+/* Restart the device to get it out of BUS-OFF state.
+ * Called when the user runs "ip link set can1 type can restart".
+ */
+static int ucan_set_mode(struct net_device *netdev, enum can_mode mode)
+{
+ // XXX TODO
+ return -EOPNOTSUPP;
+}
+
+/* Probe the device, reset it and gather general device information */
+static int ucan_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ int ret;
+ int i;
+ u32 protocol_version;
+ struct usb_device *udev;
+ struct net_device *netdev;
+ struct usb_host_interface *iface_desc;
+ struct ucan *up;
+ struct usb_endpoint_descriptor *ep;
+
+ udev = interface_to_usbdev(intf);
+
+ /* check if the interface is sane */
+ ret = -ENODEV;
+ iface_desc = intf->cur_altsetting;
+ if (!iface_desc)
+ goto err;
+
+ /* Infvalid interface Settings */
+ if (iface_desc->desc.bNumEndpoints != 3)
+ goto err;
+
+ dev_info(&udev->dev, "Found UCAN device on interface #%d\n",
+ iface_desc->desc.iInterface);
+
+ /* allocate driver resources */
+ ret = -ENOMEM;
+ netdev = alloc_candev(sizeof(struct ucan), MAX_TX_URBS);
+ if (!netdev)
+ goto err;
+
+ up = netdev_priv(netdev);
+
+ /* get interface descriptors */
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
+ ep = &iface_desc->endpoint[i].desc;
+
+ if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) != 0) &&
+ ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_BULK)) {
+ /* In Endpoint */
+ up->in_ep = ep;
+ } else if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ==
+ 0) &&
+ ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_BULK)) {
+ /* Out Endpoint */
+ up->out_ep = ep;
+ } else if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) !=
+ 0) &&
+ ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_INT)) {
+ /* Out Endpoint */
+ up->irq_ep = ep;
+ }
+ }
+
+ /* check if all interfaces are sane */
+ if (!up->in_ep || !up->out_ep || !up->irq_ep) {
+ dev_err(&udev->dev, "invalid endpoint configuration\n");
+ goto err_free_candev;
+ }
+ if (up->in_ep->wMaxPacketSize < sizeof(struct ucan_message_in)) {
+ dev_err(&udev->dev, "invalid in_ep MaxPacketSize\n");
+ goto err_free_candev;
+ }
+ if (up->out_ep->wMaxPacketSize < sizeof(struct ucan_message_out)) {
+ dev_err(&udev->dev, "invalid out_ep MaxPacketSize\n");
+ goto err_free_candev;
+ }
+ if (up->irq_ep->wMaxPacketSize < sizeof(u32)) {
+ dev_err(&udev->dev, "invalid irq_ep MaxPacketSize\n");
+ goto err_free_candev;
+ }
+
+ dev_dbg(&udev->dev,
+ "using EP %02x for input with max packet size 0x%x\n",
+ up->in_ep->bEndpointAddress, up->in_ep->wMaxPacketSize);
+ dev_dbg(&udev->dev,
+ "using EP %02x for output with max packet size 0x%x\n",
+ up->out_ep->bEndpointAddress, up->out_ep->wMaxPacketSize);
+ dev_dbg(&udev->dev,
+ "using EP %02x for irq input with max packet size 0x%x\n",
+ up->irq_ep->bEndpointAddress, up->irq_ep->wMaxPacketSize);
+
+ /* initialze data */
+ up->udev = udev;
+ up->intf = intf;
+ up->netdev = netdev;
+ up->intf_index = iface_desc->desc.bInterfaceNumber;
+
+ up->can.state = CAN_STATE_STOPPED;
+ up->can.bittiming_const = &up->device_info.bittiming_const;
+ up->can.do_set_bittiming = ucan_set_bittiming;
+ up->can.do_set_mode = &ucan_set_mode;
+ netdev->netdev_ops = &ucan_netdev_ops;
+
+ usb_set_intfdata(intf, up);
+ SET_NETDEV_DEV(netdev, &intf->dev);
+
+ /* allocate memory for command messages */
+ up->ctl_msg_buffer =
+ devm_kzalloc(&udev->dev, sizeof(ucan_ctl_payload_t), GFP_KERNEL);
+ if (!up->ctl_msg_buffer)
+ goto err_free_candev;
+
+ ret = ucan_ctrl_command_in(up, UCAN_COMMAND_GET, UCAN_COMMAND_GET_PROTOCOL_VERSION, sizeof(ucan_ctl_payload_t));
+
+ /* older firmware version do not support this command - those are not supported by this driver */
+ if (ret != 4) {
+ dev_err(&udev->dev,
+ "Could not read protocol version, ret=%d. The firmware on this device is too old, please update!\n",
+ ret);
+ if (ret >= 0)
+ ret = -EINVAL;
+ goto err_free_candev;
+ }
+
+ /* this driver currently supports protocol version 2 only */
+ protocol_version = le32_to_cpu(up->ctl_msg_buffer->protocol_version.version);
+ if (protocol_version != 2) {
+ dev_err(&udev->dev, "Device protocol version %d is not supported", protocol_version);
+ ret = -EINVAL;
+ goto err_free_candev;
+ }
+ dev_info(&udev->dev, "Device protocol version %d ok", protocol_version);
+
+ /* just print some device information - if available */
+ ret = ucan_device_request_in(up, UCAN_DEVICE_GET_FW_STRING, 0, sizeof(ucan_ctl_payload_t));
+ if (ret <= 0) {
+ dev_info(&udev->dev, "Device did not report firmware string\n");
+ } else {
+ /* ensure zero terminiation */
+ char str[sizeof(ucan_ctl_payload_t)+1];
+ strncpy(str, up->ctl_msg_buffer->raw.d, sizeof(ucan_ctl_payload_t));
+ dev_info(&udev->dev, "Device firmware string: %s\n", str);
+ }
+
+ /* device is compatible, reset it */
+ ret = ucan_ctrl_command_out( up, UCAN_COMMAND_RESET, 0, 0);
+ if (ret < 0)
+ goto err_free_candev;
+
+ /* gather device information */
+ ret = ucan_get_device_info(up);
+ if (ret)
+ goto err_free_candev;
+
+ dev_info(&up->udev->dev,
+ "Device Reports:\n"
+ " Frequency [Hz] : %d\n"
+ " TX Fifo [length] : %d\n"
+ " Time Segment 1 [min,max] : %d - %d\n"
+ " Time Segment 2 [min,max] : %d - %d\n"
+ " SWJ [max] : %d\n"
+ " Prescale [min-max,step] : %d - %d, %d\n"
+ " Supported modes :%s%s%s%s%s [%x]\n",
+ up->can.clock.freq, up->device_info.tx_fifo,
+ up->can.bittiming_const->tseg1_min,
+ up->can.bittiming_const->tseg1_max,
+ up->can.bittiming_const->tseg2_min,
+ up->can.bittiming_const->tseg2_max,
+ up->can.bittiming_const->sjw_max, up->can.bittiming_const->brp_min,
+ up->can.bittiming_const->brp_max, up->can.bittiming_const->brp_inc,
+ (up->can.ctrlmode_supported & CAN_CTRLMODE_LOOPBACK) ? " Loopback"
+ : "",
+ (up->can.ctrlmode_supported & CAN_CTRLMODE_LISTENONLY) ? " Silent"
+ : "",
+ (up->can.ctrlmode_supported & CAN_CTRLMODE_3_SAMPLES)
+ ? " 3-Sampling"
+ : "",
+ (up->can.ctrlmode_supported & CAN_CTRLMODE_ONE_SHOT) ? " OneShot"
+ : "",
+ (up->can.ctrlmode_supported & CAN_CTRLMODE_BERR_REPORTING)
+ ? " BusErrReport"
+ : "",
+ up->can.ctrlmode_supported);
+
+ atomic_set(&up->active_tx_urbs, 0);
+ for (i = 0; i < MAX_TX_URBS; i++)
+ up->tx_urb_contexts[i].echo_index = -1;
+
+ init_usb_anchor(&up->rx_urbs);
+ init_usb_anchor(&up->tx_urbs);
+
+ up->can.state = CAN_STATE_STOPPED;
+
+ /* register the device */
+ ret = register_candev(netdev);
+ if (ret)
+ goto err_free_candev;
+
+ /* success */
+ return 0;
+
+err_free_candev:
+ free_candev(netdev);
+err:
+ return ret;
+}
+
+/* disconnect the device */
+static void ucan_disconnect(struct usb_interface *intf)
+{
+ struct usb_device *udev;
+ struct ucan *up = usb_get_intfdata(intf);
+
+ udev = interface_to_usbdev(intf);
+
+ usb_set_intfdata(intf, NULL);
+
+ if (up) {
+ unregister_netdev(up->netdev);
+ free_candev(up->netdev);
+ }
+}
+
+/* driver callbacks */
+static struct usb_driver ucan_driver = {
+ .name = "ucan",
+ .probe = ucan_probe,
+ .disconnect = ucan_disconnect,
+ .id_table = ucan_table,
+};
+
+module_usb_driver(ucan_driver);