diff options
-rw-r--r-- | drivers/net/can/usb/Kconfig | 12 | ||||
-rw-r--r-- | drivers/net/can/usb/ucan.c | 1731 |
2 files changed, 1034 insertions, 709 deletions
diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig index 9f6207b69e08..3f81b8c91e84 100644 --- a/drivers/net/can/usb/Kconfig +++ b/drivers/net/can/usb/Kconfig @@ -84,10 +84,14 @@ config CAN_UCAN 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. + The UCAN driver supports the microcontroller-based USB/CAN + adapters from Theobroma Systems. There are two form-factors + that run essentially the same firmware: - It is also available as a standalone USB stick. + * Seal: standalone USB stick + https://www.theobroma-systems.com/seal) + * Mule: integrated on the PCB of various System-on-Modules + from Theobroma Systems like the A31-µQ7 and the RK3399-Q7 + (https://www.theobroma-systems.com/rk3399-q7) endmenu diff --git a/drivers/net/can/usb/ucan.c b/drivers/net/can/usb/ucan.c index f447d836179e..7c5b1284aa94 100644 --- a/drivers/net/can/usb/ucan.c +++ b/drivers/net/can/usb/ucan.c @@ -1,30 +1,28 @@ // SPDX-License-Identifier: GPL-2.0 -/* Driver for Theobroma Systems UCAN devices +/* Driver for Theobroma Systems UCAN devices, Protocol Version 3 * - * 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 + * Copyright (C) 2018 Theobroma Systems Design und Consulting GmbH * * * 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. + * CONTROL Endpoint: Is used the setup the device (start, stop, + * info, configure). * - * IN Enpoint: The device sends CAN Frame Messages and Device + * IN Endpoint: 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. + * 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> @@ -37,27 +35,16 @@ #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 */ -}; +#define UCAN_DRIVER_NAME "ucan" +#define UCAN_MAX_RX_URBS 8 +/* the CAN controller needs a while to enable/disable the bus */ +#define UCAN_USB_CTL_PIPE_TIMEOUT 1000 +/* this driver currently supports protocol version 3 only */ +#define UCAN_PROTOCOL_VERSION_MIN 3 +#define UCAN_PROTOCOL_VERSION_MAX 3 -MODULE_DEVICE_TABLE(usb, ucan_table); - -/* UCAN Message Definitions -------------------------------------------- +/* UCAN Message Definitions + * ------------------------ * * ucan_message_out_t and ucan_message_in_t define the messages * transmitted on the OUT and IN endpoint. @@ -66,19 +53,19 @@ MODULE_DEVICE_TABLE(usb, ucan_table); * * INTR Endpoint: a single uint32_t storing the current space in the fifo * - * OUT Enpoint: single message of type ucan_message_out_t is + * OUT Endpoint: 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 + * 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 + * this implies that + * offset(m[n]) % 4 <=> 0 */ /* Device Global Commands */ @@ -89,34 +76,36 @@ enum { /* UCAN Commands */ enum { /* start the can transceiver - val defines the operation mode */ - UCAN_COMMAND_START = 0, + UCAN_COMMAND_START = 0, /* cancel pending transmissions and stop the can transceiver */ - UCAN_COMMAND_STOP = 1, + UCAN_COMMAND_STOP = 1, /* send can transceiver into low-power sleep mode */ - UCAN_COMMAND_SLEEP = 2, + UCAN_COMMAND_SLEEP = 2, /* wake up can transceiver from low-power sleep mode */ - UCAN_COMMAND_WAKEUP = 3, + UCAN_COMMAND_WAKEUP = 3, /* reset the can transceiver */ - UCAN_COMMAND_RESET = 4, + UCAN_COMMAND_RESET = 4, /* get piece of info from the can transceiver - subcmd defines what * piece */ - UCAN_COMMAND_GET = 5, + UCAN_COMMAND_GET = 5, /* clear or disable hardware filter - subcmd defines which of the two */ - UCAN_COMMAND_FILTER = 6, + UCAN_COMMAND_FILTER = 6, /* Setup bittiming */ - UCAN_COMMAND_SET_BITTIMING = 7, + UCAN_COMMAND_SET_BITTIMING = 7, + /* recover from bus-off state */ + UCAN_COMMAND_RESTART = 8, }; /* 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_MODE_LOOPBACK = BIT(0), + UCAN_MODE_SILENT = BIT(1), + UCAN_MODE_3_SAMPLES = BIT(2), + UCAN_MODE_ONE_SHOT = BIT(3), + UCAN_MODE_BERR_REPORT = BIT(4), }; /* UCAN_COMMAND_GET subcommands */ @@ -127,344 +116,375 @@ enum { /* UCAN_COMMAND_FILTER subcommands */ enum { - UCAN_FILTER_CLEAR = 0, - UCAN_FILTER_DISABLE = 1, - UCAN_FILTER_ENABLE = 2, + 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 */ + UCAN_OUT_TX = 2, /* transmit a CAN frame */ }; /* IN endpoint message types */ enum { - UCAN_IN_RX = 2, + UCAN_IN_TX_COMPLETE = 1, /* CAN frame transmission completed */ + UCAN_IN_RX = 2, /* CAN frame received */ }; -typedef union { - /*************************************************** - * Setup Bittiming +struct ucan_ctl_cmd_start { + __le16 mode; /* OR-ing any of UCAN_MODE_* */ +} __packed; + +struct ucan_ctl_cmd_set_bittiming { + __le32 tq; /* Time quanta (TQ) in nanoseconds */ + __le16 brp; /* TQ Prescaler */ + __le16 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; + +struct ucan_ctl_cmd_device_info { + __le32 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; + __le16 brp_inc; + __le32 brp_min; + __le32 brp_max; /* ...can_bittiming fields */ + __le16 ctrlmodes; /* supported control modes */ + __le16 hwfilter; /* Number of HW filter banks */ + __le16 rxmboxes; /* Number of receive Mailboxes */ +} __packed; + +struct ucan_ctl_cmd_get_protocol_version { + __le32 version; +} __packed; + +union ucan_ctl_payload { + /* Setup Bittiming * bmRequest == UCAN_COMMAND_START - ***************************************************/ - struct { - u16 mode; - } __packed cmd_start; - /*************************************************** - * Setup Bittiming + */ + struct ucan_ctl_cmd_start 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 + */ + struct ucan_ctl_cmd_set_bittiming cmd_set_bittiming; + /* 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_ctl_cmd_device_info cmd_get_device_info; + /* Get Protocol Version + * bmRequest == UCAN_COMMAND_GET; + * wValue = UCAN_COMMAND_GET_PROTOCOL_VERSION + */ + struct ucan_ctl_cmd_get_protocol_version cmd_get_protocol_version; + + u8 raw[128]; +} __packed; + +enum { + UCAN_TX_COMPLETE_SUCCESS = BIT(0), +}; + +/* Transmission Complete within ucan_message_in */ +struct ucan_tx_complete_entry_t { + u8 echo_index; + u8 flags; +} __packed __aligned(0x2); + +/* CAN Data message format within ucan_message_in/out */ +struct ucan_can_msg { + /* note DLC is computed by + * msg.len - sizeof (msg.len) + * - sizeof (msg.type) + * - sizeof (msg.can_msg.id) + */ + __le32 id; + + union { + u8 data[CAN_MAX_DLEN]; /* Data of CAN frames */ + u8 dlc; /* RTR dlc */ + }; +} __packed; + +/* OUT Endpoint, outbound messages */ struct ucan_message_out { - u16 len; /* Length of the content include header */ - u16 type; /* UCAN_OUT_COMMAND and friends */ + __le16 len; /* Length of the content include header */ + u8 type; /* UCAN_OUT_TX and friends */ + u8 subtype; /* command sub type */ + union { - /*************************************************** - * Transmit CAN frame + /* 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; + * subtype stores the echo id + */ + struct ucan_can_msg can_msg; } msg; } __packed __aligned(0x4); -/* IN Enpoint, inbound messages */ +/* IN Endpoint, inbound messages */ struct ucan_message_in { - u16 len; /* Length of the content include header */ - u16 type; /* UCAN_IN_DEVICE_INFO and friends */ + __le16 len; /* Length of the content include header */ + u8 type; /* UCAN_IN_RX and friends */ + u8 subtype; /* command sub type */ union { - /*************************************************** - * CAN Frame received + /* 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; + */ + struct ucan_can_msg can_msg; + /* CAN transmission complete + * (type == UCAN_IN_TX_COMPLETE) + */ + struct ucan_tx_complete_entry_t can_tx_complete_msg[0]; } __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; +struct ucan_priv; /* Context Information for transmission URBs */ struct ucan_urb_context { - struct ucan *up; - u32 echo_index; + struct ucan_priv *up; u8 dlc; + bool allocated; }; /* Information reported by the USB device */ struct ucan_device_info { struct can_bittiming_const bittiming_const; - int tx_fifo; + u8 tx_fifo; }; /* Driver private data */ -struct ucan { - struct can_priv can; /* must be the first member */ +struct ucan_priv { + /* must be the first member */ + struct can_priv can; - u8 intf_index; + /* linux USB device structures */ 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; + /* lock for can->echo_skb (used around + * can_put/get/free_echo_skb + */ + spinlock_t echo_skb_lock; + + /* usb device information information */ + u8 intf_index; + u8 in_ep_addr; + u8 out_ep_addr; + u16 in_ep_size; + /* transmission and reception buffers */ struct usb_anchor rx_urbs; struct usb_anchor tx_urbs; - struct urb *irq_urb; - u32 *irq_data; - ucan_ctl_payload_t *ctl_msg_buffer; + union ucan_ctl_payload *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]; + /* transmission control information and locks */ + spinlock_t context_lock; + unsigned int available_tx_urbs; + struct ucan_urb_context *context_array; }; -static void ucan_tx_wake_queue_if_possible(struct ucan *up) +static u8 ucan_get_can_dlc(struct ucan_can_msg *msg, u16 len) { - 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); - } + if (le32_to_cpu(msg->id) & CAN_RTR_FLAG) + return get_can_dlc(msg->dlc); + else + return get_can_dlc(len - (UCAN_IN_HDR_SIZE + sizeof(msg->id))); } -static int ucan_ctrl_command_out(struct ucan *up, u8 cmd, u16 subcmd, size_t datalen) +static void ucan_release_context_array(struct ucan_priv *up) { - if (datalen > sizeof(ucan_ctl_payload_t)) - return -ENOMEM; + if (!up->context_array) + return; - 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); + /* lock is not needed because, driver is currently opening or closing */ + up->available_tx_urbs = 0; + + kfree(up->context_array); + up->context_array = NULL; } -static int ucan_ctrl_command_in(struct ucan *up, u8 cmd, u16 subcmd, size_t datalen) +static int ucan_alloc_context_array(struct ucan_priv *up) { - if (datalen > sizeof(ucan_ctl_payload_t)) + int i; + + /* release contexts if any */ + ucan_release_context_array(up); + + up->context_array = kcalloc(up->device_info.tx_fifo, + sizeof(*up->context_array), + GFP_KERNEL); + if (!up->context_array) { + netdev_err(up->netdev, + "Not enough memory to allocate tx contexts\n"); 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); + for (i = 0; i < up->device_info.tx_fifo; i++) { + up->context_array[i].allocated = false; + up->context_array[i].up = up; + } + + /* lock is not needed because, driver is currently opening */ + up->available_tx_urbs = up->device_info.tx_fifo; + + return 0; } -static int ucan_device_request_in(struct ucan *up, u8 cmd, u16 subcmd, size_t datalen) +static struct ucan_urb_context *ucan_alloc_context(struct ucan_priv *up) { - if (datalen > sizeof(ucan_ctl_payload_t)) - return -ENOMEM; + int i; + unsigned long flags; + struct ucan_urb_context *ret = NULL; + + if (WARN_ON_ONCE(!up->context_array)) + return NULL; + + /* execute context operation atomically */ + spin_lock_irqsave(&up->context_lock, flags); + + for (i = 0; i < up->device_info.tx_fifo; i++) { + if (!up->context_array[i].allocated) { + /* update context */ + ret = &up->context_array[i]; + up->context_array[i].allocated = true; + + /* stop queue if necessary */ + up->available_tx_urbs--; + if (!up->available_tx_urbs) + netif_stop_queue(up->netdev); + + break; + } + } + + spin_unlock_irqrestore(&up->context_lock, flags); + return ret; +} + +static bool ucan_release_context(struct ucan_priv *up, + struct ucan_urb_context *ctx) +{ + unsigned long flags; + bool ret = false; + + if (WARN_ON_ONCE(!up->context_array)) + return false; + /* execute context operation atomically */ + spin_lock_irqsave(&up->context_lock, flags); + + /* context was not allocated, maybe the device sent garbage */ + if (ctx->allocated) { + ctx->allocated = false; + + /* check if the queue needs to be woken */ + if (!up->available_tx_urbs) + netif_wake_queue(up->netdev); + up->available_tx_urbs++; + + ret = true; + } + + spin_unlock_irqrestore(&up->context_lock, flags); + return ret; +} + +static int ucan_ctrl_command_out(struct ucan_priv *up, + u8 cmd, u16 subcmd, u16 datalen) +{ 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); + usb_sndctrlpipe(up->udev, 0), + cmd, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE, + subcmd, + up->intf_index, + up->ctl_msg_buffer, + datalen, + UCAN_USB_CTL_PIPE_TIMEOUT); } -/* Request the device Information */ -static int ucan_get_device_info(struct ucan *up) +static int ucan_device_request_in(struct ucan_priv *up, + u8 cmd, u16 subcmd, u16 datalen) +{ + return usb_control_msg(up->udev, + usb_rcvctrlpipe(up->udev, 0), + cmd, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + subcmd, + 0, + up->ctl_msg_buffer, + datalen, + UCAN_USB_CTL_PIPE_TIMEOUT); +} + +/* Parse the device information structure reported by the device and + * setup private variables accordingly + */ +static void ucan_parse_device_info(struct ucan_priv *up, + struct ucan_ctl_cmd_device_info *device_info) { - 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; + u16 ctrlmodes; /* 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; + up->can.clock.freq = le32_to_cpu(device_info->freq); + up->device_info.tx_fifo = 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); + bittiming->tseg1_min = device_info->tseg1_min; + bittiming->tseg1_max = device_info->tseg1_max; + bittiming->tseg2_min = device_info->tseg2_min; + bittiming->tseg2_max = device_info->tseg2_max; + bittiming->sjw_max = device_info->sjw_max; + bittiming->brp_min = le32_to_cpu(device_info->brp_min); + bittiming->brp_max = le32_to_cpu(device_info->brp_max); + bittiming->brp_inc = le16_to_cpu(device_info->brp_inc); + + ctrlmodes = le16_to_cpu(device_info->ctrlmodes); up->can.ctrlmode_supported = 0; - if (le16_to_cpu(up->ctl_msg_buffer->device_info.ctrlmodes) & UCAN_MODE_LOOPBACK) + if (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) + if (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) + if (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) + if (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) + if (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 +/* Handle a CAN error frame that we have received from the device. + * Returns true if the can state has changed. */ -static void ucan_read_irq_callback(struct urb *urb) +static bool ucan_handle_error_frame(struct ucan_priv *up, + struct ucan_message_in *m, + canid_t canid) { - 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; + enum can_state new_state = up->can.state; struct net_device_stats *net_stats = &up->netdev->stats; struct can_device_stats *can_stats = &up->can.can_stats; @@ -484,14 +504,18 @@ static void ucan_handle_error_frame(struct ucan *up, struct ucan_message_in *m, 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_OVERFLOW) + net_stats->rx_over_errors++; + + /* controller state bits: if multiple are set the worst wins */ + if (d1 & CAN_ERR_CRTL_ACTIVE) + new_state = CAN_STATE_ERROR_ACTIVE; if (d1 & (CAN_ERR_CRTL_RX_WARNING | CAN_ERR_CRTL_TX_WARNING)) - new_state = max(new_state, (enum can_state)CAN_STATE_ERROR_WARNING); + new_state = CAN_STATE_ERROR_WARNING; - if (d1 & CAN_ERR_CRTL_RX_OVERFLOW) - net_stats->rx_over_errors++; + if (d1 & (CAN_ERR_CRTL_RX_PASSIVE | CAN_ERR_CRTL_TX_PASSIVE)) + new_state = CAN_STATE_ERROR_PASSIVE; } /* protocol error, details in data[2] */ @@ -504,30 +528,33 @@ static void ucan_handle_error_frame(struct ucan *up, struct ucan_message_in *m, net_stats->rx_errors++; } + /* no state change - we are done */ + if (up->can.state == new_state) + return false; + /* we switched into a better state */ - if (up->can.state >= new_state) { + if (up->can.state > new_state) { up->can.state = new_state; - return; + return true; } /* 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; + switch (new_state) { + case CAN_STATE_BUS_OFF: + can_stats->bus_off++; + can_bus_off(up->netdev); + break; + case CAN_STATE_ERROR_PASSIVE: + can_stats->error_passive++; + break; + case CAN_STATE_ERROR_WARNING: + can_stats->error_warning++; + break; + default: + break; } + return true; } /* Callback on reception of a can frame via the IN endpoint @@ -535,10 +562,10 @@ static void ucan_handle_error_frame(struct ucan *up, struct ucan_message_in *m, * 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) +static void ucan_rx_can_msg(struct ucan_priv *up, struct ucan_message_in *m) { int len; - u32 canid; + canid_t canid; struct can_frame *cf; struct sk_buff *skb; struct net_device_stats *stats = &up->netdev->stats; @@ -548,17 +575,34 @@ static void ucan_rx_can_msg(struct ucan *up, struct ucan_message_in *m) /* check sanity */ if (len < UCAN_IN_HDR_SIZE + sizeof(m->msg.can_msg.id)) { - dev_warn(&up->udev->dev, "invalid input message len\n"); + netdev_warn(up->netdev, "invalid input message len: %d\n", len); 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)) + bool busstate_changed = ucan_handle_error_frame(up, m, canid); + + /* if berr-reporting is off only state changes get through */ + if (!(up->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) && + !busstate_changed) return; + } else { + canid_t canid_mask; + /* compute the mask for canid */ + canid_mask = CAN_RTR_FLAG; + if (canid & CAN_EFF_FLAG) + canid_mask |= CAN_EFF_MASK | CAN_EFF_FLAG; + else + canid_mask |= CAN_SFF_MASK; + + if (canid & ~canid_mask) + netdev_warn(up->netdev, + "unexpected bits set (canid %x, mask %x)", + canid, canid_mask); + + canid &= canid_mask; } /* allocate skb */ @@ -567,51 +611,95 @@ static void ucan_rx_can_msg(struct ucan *up, struct ucan_message_in *m) 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; + cf->can_id = canid; - if (cf->can_dlc < 0) - goto err_freeskb; + /* compute DLC taking RTR_FLAG into account */ + cf->can_dlc = ucan_get_can_dlc(&m->msg.can_msg, len); - 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 { + /* copy the payload of non RTR frames */ + if (!(cf->can_id & CAN_RTR_FLAG) || (cf->can_id & CAN_ERR_FLAG)) memcpy(cf->data, m->msg.can_msg.data, cf->can_dlc); - } + + /* don't count error frames as real packets */ + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; /* pass it to Linux */ - netif_receive_skb(skb); + netif_rx(skb); +} - /* don't count error frames as real packets */ - if (!(canid & CAN_ERR_FLAG)) { - stats->rx_packets++; - stats->rx_bytes += cf->can_dlc; +/* callback indicating completed transmission */ +static void ucan_tx_complete_msg(struct ucan_priv *up, + struct ucan_message_in *m) +{ + unsigned long flags; + u16 count, i; + u8 echo_index, dlc; + u16 len = le16_to_cpu(m->len); + + struct ucan_urb_context *context; + + if (len < UCAN_IN_HDR_SIZE || (len % 2 != 0)) { + netdev_err(up->netdev, "invalid tx complete length\n"); + return; } - return; -err_freeskb: - kfree_skb(skb); + count = (len - UCAN_IN_HDR_SIZE) / 2; + for (i = 0; i < count; i++) { + /* we did not submit such echo ids */ + echo_index = m->msg.can_tx_complete_msg[i].echo_index; + if (echo_index >= up->device_info.tx_fifo) { + up->netdev->stats.tx_errors++; + netdev_err(up->netdev, + "invalid echo_index %d received\n", + echo_index); + continue; + } + + /* gather information from the context */ + context = &up->context_array[echo_index]; + dlc = READ_ONCE(context->dlc); + + /* Release context and restart queue if necessary. + * Also check if the context was allocated + */ + if (!ucan_release_context(up, context)) + continue; + + spin_lock_irqsave(&up->echo_skb_lock, flags); + if (m->msg.can_tx_complete_msg[i].flags & + UCAN_TX_COMPLETE_SUCCESS) { + /* update statistics */ + up->netdev->stats.tx_packets++; + up->netdev->stats.tx_bytes += dlc; + can_get_echo_skb(up->netdev, echo_index); + } else { + up->netdev->stats.tx_dropped++; + can_free_echo_skb(up->netdev, echo_index); + } + spin_unlock_irqrestore(&up->echo_skb_lock, flags); + } } /* 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 ucan_priv *up = urb->context; struct net_device *netdev = up->netdev; - struct ucan_message_in m; + struct ucan_message_in *m; + + /* the device is not up and the driver should not receive any + * data on the bulk in pipe + */ + if (WARN_ON(!up->context_array)) { + usb_free_coherent(up->udev, + up->in_ep_size, + urb->transfer_buffer, + urb->transfer_dma); + return; + } /* check URB status */ switch (urb->status) { @@ -622,47 +710,68 @@ static void ucan_read_bulk_callback(struct urb *urb) case -EPROTO: case -ESHUTDOWN: case -ETIME: - dev_dbg(&up->udev->dev, "%s ENOENT|ESHUTDOWN|ETIME\n", - __func__); + /* urb is not resubmitted -> free dma data */ + usb_free_coherent(up->udev, + up->in_ep_size, + urb->transfer_buffer, + urb->transfer_dma); + netdev_dbg(up->netdev, "not resubmitting urb; status: %d\n", + urb->status); return; default: goto resubmit; } + /* sanity check */ + if (!netif_device_present(netdev)) + return; + /* iterate over input */ pos = 0; while (pos < urb->actual_length) { + int len; + /* 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); + netdev_warn(up->netdev, + "invalid message (short; no hdr; l:%d)\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); + /* setup the message address */ + m = (struct ucan_message_in *) + ((u8 *)urb->transfer_buffer + pos); + 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); + netdev_warn(up->netdev, + "invalid message (short; no data; l:%d)\n", + urb->actual_length); + 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)) { + switch (m->type) { case UCAN_IN_RX: - ucan_rx_can_msg(up, &m); + ucan_rx_can_msg(up, m); + break; + case UCAN_IN_TX_COMPLETE: + ucan_tx_complete_msg(up, m); break; default: - dev_warn(&up->udev->dev, - "invalid input message type\n"); + netdev_warn(up->netdev, + "invalid message (type; t:%d)\n", + m->type); break; } @@ -675,144 +784,198 @@ static void ucan_read_bulk_callback(struct urb *urb) 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); - + usb_rcvbulkpipe(up->udev, + up->in_ep_addr), + urb->transfer_buffer, + up->in_ep_size, + ucan_read_bulk_callback, + up); + + usb_anchor_urb(urb, &up->rx_urbs); 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); + if (ret < 0) { + netdev_err(up->netdev, + "failed resubmitting read bulk urb: %d\n", + ret); + + usb_unanchor_urb(urb); + usb_free_coherent(up->udev, + up->in_ep_size, + urb->transfer_buffer, + urb->transfer_dma); + + if (ret == -ENODEV) + netif_device_detach(netdev); + } } /* callback after transmission of a USB message */ static void ucan_write_bulk_callback(struct urb *urb) { + unsigned long flags; + struct ucan_priv *up; struct ucan_urb_context *context = urb->context; - struct ucan *up; /* get the urb context */ - if (WARN_ON(!context)) + if (WARN_ON_ONCE(!context)) return; - up = context->up; - /* free up our allocated buffer */ - usb_free_coherent(urb->dev, sizeof(struct ucan_message_out), urb->transfer_buffer, + usb_free_coherent(urb->dev, + sizeof(struct ucan_message_out), + urb->transfer_buffer, urb->transfer_dma); - atomic_dec(&up->active_tx_urbs); + up = context->up; + if (WARN_ON_ONCE(!up)) + return; /* 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); + /* transmission failed (USB - the device will not send a TX complete) */ + if (urb->status) { + netdev_warn(up->netdev, + "failed to transmit USB message to device: %d\n", + urb->status); - up->netdev->trans_start = jiffies; + /* update counters an cleanup */ + spin_lock_irqsave(&up->echo_skb_lock, flags); + can_free_echo_skb(up->netdev, context - up->context_array); + spin_unlock_irqrestore(&up->echo_skb_lock, flags); - /* update statistics */ - up->netdev->stats.tx_packets++; - up->netdev->stats.tx_bytes += context->dlc; + up->netdev->stats.tx_dropped++; - /* 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); + /* release context and restart the queue if necessary */ + if (!ucan_release_context(up, context)) + netdev_err(up->netdev, + "urb failed, failed to release context\n"); + } } -/* Open the network device */ -static int ucan_open(struct net_device *netdev) +static void ucan_cleanup_rx_urbs(struct ucan_priv *up, struct urb **urbs) { 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; + for (i = 0; i < UCAN_MAX_RX_URBS; i++) { + if (urbs[i]) { + usb_unanchor_urb(urbs[i]); + usb_free_coherent(up->udev, + up->in_ep_size, + urbs[i]->transfer_buffer, + urbs[i]->transfer_dma); + usb_free_urb(urbs[i]); + } } - 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); + memset(urbs, 0, sizeof(*urbs) * UCAN_MAX_RX_URBS); +} - ret = usb_submit_urb(up->irq_urb, GFP_KERNEL); - if (ret) { - kfree(up->irq_data); - usb_free_urb(up->irq_urb); - goto err; - } +static int ucan_prepare_and_anchor_rx_urbs(struct ucan_priv *up, + struct urb **urbs) +{ + int i; + + memset(urbs, 0, sizeof(*urbs) * UCAN_MAX_RX_URBS); - /* initialize IN enpoint */ - for (i = 0; i < MAX_RX_URBS; i++) { - ret = -ENOMEM; + for (i = 0; i < UCAN_MAX_RX_URBS; i++) { + void *buf; - urb = usb_alloc_urb(0, GFP_KERNEL); - if (!urb) + urbs[i] = usb_alloc_urb(0, GFP_KERNEL); + if (!urbs[i]) goto err; - buf = usb_alloc_coherent(up->udev, up->in_ep->wMaxPacketSize, - GFP_KERNEL, &urb->transfer_dma); + buf = usb_alloc_coherent(up->udev, + up->in_ep_size, + GFP_KERNEL, &urbs[i]->transfer_dma); if (!buf) { - usb_free_urb(urb); + /* cleanup this urb */ + usb_free_urb(urbs[i]); + urbs[i] = NULL; 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); + usb_fill_bulk_urb(urbs[i], up->udev, + usb_rcvbulkpipe(up->udev, + up->in_ep_addr), + buf, + up->in_ep_size, + ucan_read_bulk_callback, + up); - ret = usb_submit_urb(urb, GFP_KERNEL); + urbs[i]->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(urbs[i], &up->rx_urbs); + } + return 0; + +err: + /* cleanup other unsubmitted urbs */ + ucan_cleanup_rx_urbs(up, urbs); + return -ENOMEM; +} + +/* Submits rx urbs with the semantic: Either submit all, or cleanup + * everything. I case of errors submitted urbs are killed and all urbs in + * the array are freed. I case of no errors every entry in the urb + * array is set to NULL. + */ +static int ucan_submit_rx_urbs(struct ucan_priv *up, struct urb **urbs) +{ + int i, ret; + + /* Iterate over all urbs to submit. On success remove the urb + * from the list. + */ + for (i = 0; i < UCAN_MAX_RX_URBS; i++) { + ret = usb_submit_urb(urbs[i], 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); + netdev_err(up->netdev, + "could not submit urb; code: %d\n", + ret); goto err; } - /* Drop reference, USB core will take care of freeing it */ - usb_free_urb(urb); + /* Anchor URB and drop reference, USB core will take + * care of freeing it + */ + usb_free_urb(urbs[i]); + urbs[i] = NULL; } + return 0; + +err: + /* Cleanup unsubmitted urbs */ + ucan_cleanup_rx_urbs(up, urbs); + + /* Kill urbs that are already submitted */ + usb_kill_anchored_urbs(&up->rx_urbs); + + return ret; +} - /* check the control mode */ +/* Open the network device */ +static int ucan_open(struct net_device *netdev) +{ + int ret, ret_cleanup; + u16 ctrlmode; + struct urb *urbs[UCAN_MAX_RX_URBS]; + struct ucan_priv *up = netdev_priv(netdev); + + ret = ucan_alloc_context_array(up); + if (ret) + return ret; + + /* Allocate and prepare IN URBS - allocated and anchored + * urbs are stored in urbs[] for clean + */ + ret = ucan_prepare_and_anchor_rx_urbs(up, urbs); + if (ret) + goto err_contexts; + + /* Check the control mode */ ctrlmode = 0; if (up->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) ctrlmode |= UCAN_MODE_LOOPBACK; @@ -823,66 +986,99 @@ static int ucan_open(struct net_device *netdev) 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 + /* 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); + up->ctl_msg_buffer->cmd_start.mode = cpu_to_le16(ctrlmode); - /* start the USB device */ + /* Driver is ready to receive data - start the USB device */ ret = ucan_ctrl_command_out(up, UCAN_COMMAND_START, 0, 2); - if (ret < 0) - goto err; + if (ret < 0) { + netdev_err(up->netdev, + "could not start device, code: %d\n", + ret); + goto err_reset; + } + + /* Call CAN layer open */ + ret = open_candev(netdev); + if (ret) + goto err_stop; + + /* Driver is ready to receive data. Submit RX URBS */ + ret = ucan_submit_rx_urbs(up, urbs); + if (ret) + goto err_stop; up->can.state = CAN_STATE_ERROR_ACTIVE; - /* start the network queue */ + /* Start the network queue */ netif_start_queue(netdev); return 0; -err: - usb_kill_anchored_urbs(&up->rx_urbs); +err_stop: + /* The device have started already stop it */ + ret_cleanup = ucan_ctrl_command_out(up, UCAN_COMMAND_STOP, 0, 0); + if (ret_cleanup < 0) + netdev_err(up->netdev, + "could not stop device, code: %d\n", + ret_cleanup); + +err_reset: + /* The device might have received data, reset it for + * consistent state + */ + ret_cleanup = ucan_ctrl_command_out(up, UCAN_COMMAND_RESET, 0, 0); + if (ret_cleanup < 0) + netdev_err(up->netdev, + "could not reset device, code: %d\n", + ret_cleanup); + + /* clean up unsubmitted urbs */ + ucan_cleanup_rx_urbs(up, urbs); + +err_contexts: + ucan_release_context_array(up); 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) +static struct urb *ucan_prepare_tx_urb(struct ucan_priv *up, + struct ucan_urb_context *context, + struct can_frame *cf, + u8 echo_index) { - int i, ret, mlen; + int 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; + netdev_err(up->netdev, "no memory left for URBs\n"); + return NULL; } - m = usb_alloc_coherent(up->udev, sizeof(struct ucan_message_out), GFP_ATOMIC, &urb->transfer_dma); + 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"); + netdev_err(up->netdev, "no memory left for USB buffer\n"); usb_free_urb(urb); - goto drop; + return NULL; } /* 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->type = 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; + mlen = UCAN_OUT_HDR_SIZE + + offsetof(struct ucan_can_msg, dlc) + + sizeof(m->msg.can_msg.dlc); + m->msg.can_msg.dlc = cf->can_dlc; } else { mlen = UCAN_OUT_HDR_SIZE + sizeof(m->msg.can_msg.id) + cf->can_dlc; @@ -890,73 +1086,102 @@ static netdev_tx_t ucan_start_xmit(struct sk_buff *skb, } 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; - } - } + context->dlc = cf->can_dlc; - 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; - } + m->subtype = echo_index; /* 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); + usb_sndbulkpipe(up->udev, + up->out_ep_addr), + 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); + + return urb; +} + +static void ucan_clean_up_tx_urb(struct ucan_priv *up, struct urb *urb) +{ + usb_free_coherent(up->udev, sizeof(struct ucan_message_out), + urb->transfer_buffer, urb->transfer_dma); + usb_free_urb(urb); +} + +/* callback when Linux needs to send a can frame */ +static netdev_tx_t ucan_start_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + unsigned long flags; + int ret; + u8 echo_index; + struct urb *urb; + struct ucan_urb_context *context; + struct ucan_priv *up = netdev_priv(netdev); + struct can_frame *cf = (struct can_frame *)skb->data; + + /* check skb */ + if (can_dropped_invalid_skb(netdev, skb)) + return NETDEV_TX_OK; + + /* allocate a context and slow down tx path, if fifo state is low */ + context = ucan_alloc_context(up); + echo_index = context - up->context_array; + + if (WARN_ON_ONCE(!context)) + return NETDEV_TX_BUSY; + + /* prepare urb for transmission */ + urb = ucan_prepare_tx_urb(up, context, cf, echo_index); + if (!urb) + goto drop; + + /* put the skb on can loopback stack */ + spin_lock_irqsave(&up->echo_skb_lock, flags); + can_put_echo_skb(skb, up->netdev, echo_index); + spin_unlock_irqrestore(&up->echo_skb_lock, flags); + /* transmit it */ + usb_anchor_urb(urb, &up->tx_urbs); ret = usb_submit_urb(urb, GFP_ATOMIC); - // cleanup urb + /* 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); + usb_unanchor_urb(urb); + ucan_clean_up_tx_urb(up, urb); + if (!ucan_release_context(up, context)) + netdev_err(up->netdev, + "xmit err: failed to release context\n"); + + /* remove the skb from the echo stack - this also + * frees the skb + */ + spin_lock_irqsave(&up->echo_skb_lock, flags); + can_free_echo_skb(up->netdev, echo_index); + spin_unlock_irqrestore(&up->echo_skb_lock, flags); if (ret == -ENODEV) { netif_device_detach(up->netdev); } else { - netdev_warn(up->netdev, "failed tx_urb %d\n", ret); + netdev_warn(up->netdev, + "xmit err: failed to submit urb %d\n", + ret); up->netdev->stats.tx_dropped++; } + return NETDEV_TX_OK; } - 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); - } - } + netdev->trans_start = jiffies; /* release ref, as we do not need the urb anymore */ usb_free_urb(urb); return NETDEV_TX_OK; + drop: + if (!ucan_release_context(up, context)) + netdev_err(up->netdev, + "xmit drop: failed to release context\n"); dev_kfree_skb(skb); up->netdev->stats.tx_dropped++; @@ -970,29 +1195,32 @@ drop: static int ucan_close(struct net_device *netdev) { int ret; - struct ucan *up = netdev_priv(netdev); + struct ucan_priv *up = netdev_priv(netdev); up->can.state = CAN_STATE_STOPPED; - netif_stop_queue(netdev); + /* stop sending data */ + usb_kill_anchored_urbs(&up->tx_urbs); - ret = ucan_ctrl_command_out( up, UCAN_COMMAND_STOP, 0, 0); + /* stop receiving data */ + usb_kill_anchored_urbs(&up->rx_urbs); + + /* stop and reset can device */ + 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); + netdev_err(up->netdev, + "could not stop device, code: %d\n", + ret); - ret = ucan_ctrl_command_out( up, UCAN_COMMAND_RESET, 0, 0); + 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); + netdev_err(up->netdev, + "could not reset 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); + netif_stop_queue(netdev); - kfree(up->irq_data); + ucan_release_context_array(up); close_candev(up->netdev); return 0; @@ -1014,34 +1242,21 @@ static const struct net_device_ops ucan_netdev_ops = { 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)); + struct ucan_priv *up = netdev_priv(netdev); + struct ucan_ctl_cmd_set_bittiming *cmd_set_bittiming; + + cmd_set_bittiming = &up->ctl_msg_buffer->cmd_set_bittiming; + cmd_set_bittiming->tq = cpu_to_le32(up->can.bittiming.tq); + cmd_set_bittiming->brp = cpu_to_le16(up->can.bittiming.brp); + cmd_set_bittiming->sample_point = + cpu_to_le16(up->can.bittiming.sample_point); + cmd_set_bittiming->prop_seg = up->can.bittiming.prop_seg; + cmd_set_bittiming->phase_seg1 = up->can.bittiming.phase_seg1; + cmd_set_bittiming->phase_seg2 = up->can.bittiming.phase_seg2; + cmd_set_bittiming->sjw = up->can.bittiming.sjw; + + ret = ucan_ctrl_command_out(up, UCAN_COMMAND_SET_BITTIMING, 0, + sizeof(*cmd_set_bittiming)); return (ret < 0) ? ret : 0; } @@ -1050,8 +1265,32 @@ static int ucan_set_bittiming(struct net_device *netdev) */ static int ucan_set_mode(struct net_device *netdev, enum can_mode mode) { - // XXX TODO - return -EOPNOTSUPP; + int ret; + unsigned long flags; + struct ucan_priv *up = netdev_priv(netdev); + + switch (mode) { + case CAN_MODE_START: + netdev_dbg(up->netdev, "restarting device\n"); + + ret = ucan_ctrl_command_out(up, UCAN_COMMAND_RESTART, 0, 0); + up->can.state = CAN_STATE_ERROR_ACTIVE; + + /* check if queue can be restarted, + * up->available_tx_urbs must be protected by the + * lock + */ + spin_lock_irqsave(&up->context_lock, flags); + + if (up->available_tx_urbs > 0) + netif_wake_queue(up->netdev); + + spin_unlock_irqrestore(&up->context_lock, flags); + + return ret; + default: + return -EOPNOTSUPP; + } } /* Probe the device, reset it and gather general device information */ @@ -1064,33 +1303,47 @@ static int ucan_probe(struct usb_interface *intf, struct usb_device *udev; struct net_device *netdev; struct usb_host_interface *iface_desc; - struct ucan *up; + struct ucan_priv *up; struct usb_endpoint_descriptor *ep; + u16 in_ep_size; + u16 out_ep_size; + u8 in_ep_addr; + u8 out_ep_addr; + union ucan_ctl_payload *ctl_msg_buffer; + char firmware_str[sizeof(union ucan_ctl_payload) + 1]; udev = interface_to_usbdev(intf); + /* Stage 1 - Interface Parsing + * --------------------------- + * + * Identifie the device USB interface descriptor and its + * endpoints. Probing is aborted on errors. + */ + /* 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; + return -ENODEV; - dev_info(&udev->dev, "Found UCAN device on interface #%d\n", - iface_desc->desc.iInterface); + dev_info(&udev->dev, + "%s: probing device on interface #%d\n", + UCAN_DRIVER_NAME, + iface_desc->desc.bInterfaceNumber); - /* allocate driver resources */ - ret = -ENOMEM; - netdev = alloc_candev(sizeof(struct ucan), MAX_TX_URBS); - if (!netdev) - goto err; - - up = netdev_priv(netdev); + /* interface sanity check */ + if (iface_desc->desc.bNumEndpoints != 2) { + dev_err(&udev->dev, + "%s: invalid EP count (%d)", + UCAN_DRIVER_NAME, iface_desc->desc.bNumEndpoints); + goto err_firmware_needs_update; + } - /* get interface descriptors */ + /* check interface endpoints */ + in_ep_addr = 0; + out_ep_addr = 0; + in_ep_size = 0; + out_ep_size = 0; for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { ep = &iface_desc->endpoint[i].desc; @@ -1098,147 +1351,195 @@ static int ucan_probe(struct usb_interface *intf, ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK)) { /* In Endpoint */ - up->in_ep = ep; + in_ep_addr = ep->bEndpointAddress; + in_ep_addr &= USB_ENDPOINT_NUMBER_MASK; + in_ep_size = le16_to_cpu(ep->wMaxPacketSize); } 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; + out_ep_addr = ep->bEndpointAddress; + out_ep_addr &= USB_ENDPOINT_NUMBER_MASK; + out_ep_size = le16_to_cpu(ep->wMaxPacketSize); } } - /* 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; + /* check if interface is sane */ + if (!in_ep_addr || !out_ep_addr) { + dev_err(&udev->dev, "%s: invalid endpoint configuration\n", + UCAN_DRIVER_NAME); + goto err_firmware_needs_update; } - if (up->in_ep->wMaxPacketSize < sizeof(struct ucan_message_in)) { - dev_err(&udev->dev, "invalid in_ep MaxPacketSize\n"); - goto err_free_candev; + if (in_ep_size < sizeof(struct ucan_message_in)) { + dev_err(&udev->dev, "%s: invalid in_ep MaxPacketSize\n", + UCAN_DRIVER_NAME); + goto err_firmware_needs_update; } - if (up->out_ep->wMaxPacketSize < sizeof(struct ucan_message_out)) { - dev_err(&udev->dev, "invalid out_ep MaxPacketSize\n"); - goto err_free_candev; + if (out_ep_size < sizeof(struct ucan_message_out)) { + dev_err(&udev->dev, "%s: invalid out_ep MaxPacketSize\n", + UCAN_DRIVER_NAME); + goto err_firmware_needs_update; } - if (up->irq_ep->wMaxPacketSize < sizeof(u32)) { - dev_err(&udev->dev, "invalid irq_ep MaxPacketSize\n"); - goto err_free_candev; + + /* Stage 2 - Device Identification + * ------------------------------- + * + * The device interface seems to be a ucan device. Do further + * compatibility checks. On error probing is aborted, on + * success this stage leaves the ctl_msg_buffer with the + * reported contents of a GET_INFO command (supported + * bittimings, tx_fifo depth). This information is used in + * Stage 3 for the final driver initialisation. + */ + + /* Prepare Memory for control transferes */ + ctl_msg_buffer = devm_kzalloc(&udev->dev, + sizeof(union ucan_ctl_payload), + GFP_KERNEL); + if (!ctl_msg_buffer) { + dev_err(&udev->dev, + "%s: failed to allocate control pipe memory\n", + UCAN_DRIVER_NAME); + return -ENOMEM; + } + + /* get protocol version + * + * note: ucan_ctrl_command_* wrappers cannot be used yet + * because `up` is initialised in Stage 3 + */ + ret = usb_control_msg(udev, + usb_rcvctrlpipe(udev, 0), + UCAN_COMMAND_GET, + USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE, + UCAN_COMMAND_GET_PROTOCOL_VERSION, + iface_desc->desc.bInterfaceNumber, + ctl_msg_buffer, + sizeof(union ucan_ctl_payload), + UCAN_USB_CTL_PIPE_TIMEOUT); + + /* older firmware version do not support this command - those + * are not supported by this drive + */ + if (ret != 4) { + dev_err(&udev->dev, + "%s: could not read protocol version, ret=%d\n", + UCAN_DRIVER_NAME, ret); + if (ret >= 0) + ret = -EINVAL; + goto err_firmware_needs_update; } - 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); + /* this driver currently supports protocol version 3 only */ + protocol_version = + le32_to_cpu(ctl_msg_buffer->cmd_get_protocol_version.version); + if (protocol_version < UCAN_PROTOCOL_VERSION_MIN || + protocol_version > UCAN_PROTOCOL_VERSION_MAX) { + dev_err(&udev->dev, + "%s: device protocol version %d is not supported\n", + UCAN_DRIVER_NAME, protocol_version); + goto err_firmware_needs_update; + } + + /* request the device information and store it in ctl_msg_buffer + * + * note: ucan_ctrl_command_* wrappers connot be used yet + * because `up` is initialised in Stage 3 + */ + ret = usb_control_msg(udev, + usb_rcvctrlpipe(udev, 0), + UCAN_COMMAND_GET, + USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE, + UCAN_COMMAND_GET_INFO, + iface_desc->desc.bInterfaceNumber, + ctl_msg_buffer, + sizeof(ctl_msg_buffer->cmd_get_device_info), + UCAN_USB_CTL_PIPE_TIMEOUT); + + if (ret < 0) { + dev_err(&udev->dev, "%s: failed to retrieve device info\n", + UCAN_DRIVER_NAME); + goto err_firmware_needs_update; + } + if (ret < sizeof(ctl_msg_buffer->cmd_get_device_info)) { + dev_err(&udev->dev, "%s: device reported invalid device info\n", + UCAN_DRIVER_NAME); + goto err_firmware_needs_update; + } + if (ctl_msg_buffer->cmd_get_device_info.tx_fifo == 0) { + dev_err(&udev->dev, + "%s: device reported invalid tx-fifo size\n", + UCAN_DRIVER_NAME); + goto err_firmware_needs_update; + } + + /* Stage 3 - Driver Initialisation + * ------------------------------- + * + * Register device to Linux, prepare private structures and + * reset the device. + */ + + /* allocate driver resources */ + netdev = alloc_candev(sizeof(struct ucan_priv), + ctl_msg_buffer->cmd_get_device_info.tx_fifo); + if (!netdev) { + dev_err(&udev->dev, + "%s: cannot allocate candev\n", UCAN_DRIVER_NAME); + return -ENOMEM; + } + + up = netdev_priv(netdev); /* initialze data */ up->udev = udev; up->intf = intf; up->netdev = netdev; up->intf_index = iface_desc->desc.bInterfaceNumber; + up->in_ep_addr = in_ep_addr; + up->out_ep_addr = out_ep_addr; + up->in_ep_size = in_ep_size; + up->ctl_msg_buffer = ctl_msg_buffer; + up->context_array = NULL; + up->available_tx_urbs = 0; 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; + spin_lock_init(&up->context_lock); + spin_lock_init(&up->echo_skb_lock); 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); + /* parse device information + * the data retrieved in Stage 2 is still available in + * up->ctl_msg_buffer + */ + ucan_parse_device_info(up, &ctl_msg_buffer->cmd_get_device_info); /* 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"); + ret = ucan_device_request_in(up, UCAN_DEVICE_GET_FW_STRING, 0, + sizeof(union ucan_ctl_payload)); + if (ret > 0) { + /* copy string while ensuring zero terminiation */ + strncpy(firmware_str, up->ctl_msg_buffer->raw, + sizeof(union ucan_ctl_payload)); + firmware_str[sizeof(union ucan_ctl_payload)] = '\0'; } 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); + strcpy(firmware_str, "unknown"); } /* device is compatible, reset it */ - ret = ucan_ctrl_command_out( up, UCAN_COMMAND_RESET, 0, 0); + 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); @@ -1249,22 +1550,28 @@ static int ucan_probe(struct usb_interface *intf, if (ret) goto err_free_candev; + /* initialisation complete, log device info */ + netdev_info(up->netdev, "registered device\n"); + netdev_info(up->netdev, "firmware string: %s\n", firmware_str); + /* success */ return 0; err_free_candev: free_candev(netdev); -err: return ret; + +err_firmware_needs_update: + dev_err(&udev->dev, + "%s: probe failed; try to update the device firmware\n", + UCAN_DRIVER_NAME); + return -ENODEV; } /* 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); + struct ucan_priv *up = usb_get_intfdata(intf); usb_set_intfdata(intf, NULL); @@ -1274,12 +1581,26 @@ static void ucan_disconnect(struct usb_interface *intf) } } +static struct usb_device_id ucan_table[] = { + /* Mule (soldered onto compute modules) */ + {USB_DEVICE_INTERFACE_NUMBER(0x2294, 0x425a, 0)}, + /* Seal (standalone USB stick) */ + {USB_DEVICE_INTERFACE_NUMBER(0x2294, 0x425b, 0)}, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, ucan_table); /* driver callbacks */ static struct usb_driver ucan_driver = { - .name = "ucan", + .name = UCAN_DRIVER_NAME, .probe = ucan_probe, .disconnect = ucan_disconnect, .id_table = ucan_table, }; module_usb_driver(ucan_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Martin Elshuber <martin.elshuber@theobroma-systems.com>"); +MODULE_AUTHOR("Jakob Unterwurzacher <jakob.unterwurzacher@theobroma-systems.com>"); +MODULE_DESCRIPTION("Driver for Theobroma Systems UCAN devices"); |