From bbd432d5cae7107a623672d1df7529442c9a3e38 Mon Sep 17 00:00:00 2001 From: Jakob Unterwurzacher Date: Thu, 29 Mar 2018 15:23:40 +0200 Subject: can: ucan: add driver for Theobroma Systems UCAN devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The UCAN driver supports the microcontroller-based USB/CAN adapters from Theobroma Systems. There are two form-factors that run essentially the same firmware: * 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 ) The USB wire protocol has been designed to be as generic and hardware-indendent as possible in the hope of being useful for implementation on other microcontrollers. Signed-off-by: Martin Elshuber Signed-off-by: Jakob Unterwurzacher Signed-off-by: Philipp Tomsich --- drivers/net/can/usb/Kconfig | 12 + drivers/net/can/usb/Makefile | 1 + drivers/net/can/usb/ucan.c | 1285 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1298 insertions(+) create mode 100644 drivers/net/can/usb/ucan.c 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Martin Elshuber, Theobroma Systems Design und Consulting GmbH "); +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); -- cgit v1.2.3