// 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);