diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 10:05:51 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 10:05:51 +0000 |
commit | 5d1646d90e1f2cceb9f0828f4b28318cd0ec7744 (patch) | |
tree | a94efe259b9009378be6d90eb30d2b019d95c194 /drivers/net/can/usb | |
parent | Initial commit. (diff) | |
download | linux-5d1646d90e1f2cceb9f0828f4b28318cd0ec7744.tar.xz linux-5d1646d90e1f2cceb9f0828f4b28318cd0ec7744.zip |
Adding upstream version 5.10.209.upstream/5.10.209upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
20 files changed, 16919 insertions, 0 deletions
diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig new file mode 100644 index 000000000..bcb331b0c --- /dev/null +++ b/drivers/net/can/usb/Kconfig @@ -0,0 +1,121 @@ +# SPDX-License-Identifier: GPL-2.0-only +menu "CAN USB interfaces" + depends on USB + +config CAN_8DEV_USB + tristate "8 devices USB2CAN interface" + help + This driver supports the USB2CAN interface + from 8 devices (http://www.8devices.com). + +config CAN_EMS_USB + tristate "EMS CPC-USB/ARM7 CAN/USB interface" + help + This driver is for the one channel CPC-USB/ARM7 CAN/USB interface + from EMS Dr. Thomas Wuensche (http://www.ems-wuensche.de). + +config CAN_ESD_USB2 + tristate "ESD USB/2 CAN/USB interface" + help + This driver supports the CAN-USB/2 interface + from esd electronic system design gmbh (http://www.esd.eu). + +config CAN_GS_USB + tristate "Geschwister Schneider UG interfaces" + help + This driver supports the Geschwister Schneider and bytewerk.org + candleLight USB CAN interfaces USB/CAN devices + If unsure choose N, + choose Y for built in support, + M to compile as module (module will be named: gs_usb). + +config CAN_KVASER_USB + tristate "Kvaser CAN/USB interface" + help + This driver adds support for Kvaser CAN/USB devices like Kvaser + Leaf Light, Kvaser USBcan II and Kvaser Memorator Pro 5xHS. + + The driver provides support for the following devices: + - Kvaser Leaf Light + - Kvaser Leaf Professional HS + - Kvaser Leaf SemiPro HS + - Kvaser Leaf Professional LS + - Kvaser Leaf Professional SWC + - Kvaser Leaf Professional LIN + - Kvaser Leaf SemiPro LS + - Kvaser Leaf SemiPro SWC + - Kvaser Memorator II HS/HS + - Kvaser USBcan Professional HS/HS + - Kvaser Leaf Light GI + - Kvaser Leaf Professional HS (OBD-II connector) + - Kvaser Memorator Professional HS/LS + - Kvaser Leaf Light "China" + - Kvaser BlackBird SemiPro + - Kvaser USBcan R + - Kvaser Leaf Light v2 + - Kvaser Mini PCI Express HS + - Kvaser Mini PCI Express 2xHS + - Kvaser USBcan Light 2xHS + - Kvaser USBcan II HS/HS + - Kvaser USBcan II HS/LS + - Kvaser USBcan Rugged ("USBcan Rev B") + - Kvaser Memorator HS/HS + - Kvaser Memorator HS/LS + - Scania VCI2 (if you have the Kvaser logo on top) + - Kvaser BlackBird v2 + - Kvaser Leaf Pro HS v2 + - Kvaser Hybrid 2xCAN/LIN + - Kvaser Hybrid Pro 2xCAN/LIN + - Kvaser Memorator 2xHS v2 + - Kvaser Memorator Pro 2xHS v2 + - Kvaser Memorator Pro 5xHS + - Kvaser USBcan Light 4xHS + - Kvaser USBcan Pro 2xHS v2 + - Kvaser USBcan Pro 5xHS + - ATI Memorator Pro 2xHS v2 + - ATI USBcan Pro 2xHS v2 + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called kvaser_usb. + +config CAN_MCBA_USB + tristate "Microchip CAN BUS Analyzer interface" + help + This driver supports the CAN BUS Analyzer interface + from Microchip (http://www.microchip.com/development-tools/). + +config CAN_PEAK_USB + tristate "PEAK PCAN-USB/USB Pro interfaces for CAN 2.0b/CAN-FD" + help + This driver supports the PEAK-System Technik USB adapters that enable + access to the CAN bus, with respect to the CAN 2.0b and/or CAN-FD + standards, that is: + + PCAN-USB single CAN 2.0b channel USB adapter + PCAN-USB Pro dual CAN 2.0b channels USB adapter + PCAN-USB FD single CAN-FD channel USB adapter + PCAN-USB Pro FD dual CAN-FD channels USB adapter + PCAN-Chip USB CAN-FD to USB stamp module + PCAN-USB X6 6 CAN-FD channels USB adapter + + (see also http://www.peak-system.com). + +config CAN_UCAN + tristate "Theobroma Systems UCAN interface" + help + This driver supports the Theobroma Systems + UCAN USB-CAN interface. + + 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) + +endmenu diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile new file mode 100644 index 000000000..aa0f17c0b --- /dev/null +++ b/drivers/net/can/usb/Makefile @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the Linux Controller Area Network USB drivers. +# + +obj-$(CONFIG_CAN_8DEV_USB) += usb_8dev.o +obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o +obj-$(CONFIG_CAN_ESD_USB2) += esd_usb2.o +obj-$(CONFIG_CAN_GS_USB) += gs_usb.o +obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb/ +obj-$(CONFIG_CAN_MCBA_USB) += mcba_usb.o +obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/ +obj-$(CONFIG_CAN_UCAN) += ucan.o diff --git a/drivers/net/can/usb/ems_usb.c b/drivers/net/can/usb/ems_usb.c new file mode 100644 index 000000000..ff05b5230 --- /dev/null +++ b/drivers/net/can/usb/ems_usb.c @@ -0,0 +1,1087 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * CAN driver for EMS Dr. Thomas Wuensche CPC-USB/ARM7 + * + * Copyright (C) 2004-2009 EMS Dr. Thomas Wuensche + */ +#include <linux/signal.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/usb.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> + +MODULE_AUTHOR("Sebastian Haas <haas@ems-wuensche.com>"); +MODULE_DESCRIPTION("CAN driver for EMS Dr. Thomas Wuensche CAN/USB interfaces"); +MODULE_LICENSE("GPL v2"); + +/* Control-Values for CPC_Control() Command Subject Selection */ +#define CONTR_CAN_MESSAGE 0x04 +#define CONTR_CAN_STATE 0x0C +#define CONTR_BUS_ERROR 0x1C + +/* Control Command Actions */ +#define CONTR_CONT_OFF 0 +#define CONTR_CONT_ON 1 +#define CONTR_ONCE 2 + +/* Messages from CPC to PC */ +#define CPC_MSG_TYPE_CAN_FRAME 1 /* CAN data frame */ +#define CPC_MSG_TYPE_RTR_FRAME 8 /* CAN remote frame */ +#define CPC_MSG_TYPE_CAN_PARAMS 12 /* Actual CAN parameters */ +#define CPC_MSG_TYPE_CAN_STATE 14 /* CAN state message */ +#define CPC_MSG_TYPE_EXT_CAN_FRAME 16 /* Extended CAN data frame */ +#define CPC_MSG_TYPE_EXT_RTR_FRAME 17 /* Extended remote frame */ +#define CPC_MSG_TYPE_CONTROL 19 /* change interface behavior */ +#define CPC_MSG_TYPE_CONFIRM 20 /* command processed confirmation */ +#define CPC_MSG_TYPE_OVERRUN 21 /* overrun events */ +#define CPC_MSG_TYPE_CAN_FRAME_ERROR 23 /* detected bus errors */ +#define CPC_MSG_TYPE_ERR_COUNTER 25 /* RX/TX error counter */ + +/* Messages from the PC to the CPC interface */ +#define CPC_CMD_TYPE_CAN_FRAME 1 /* CAN data frame */ +#define CPC_CMD_TYPE_CONTROL 3 /* control of interface behavior */ +#define CPC_CMD_TYPE_CAN_PARAMS 6 /* set CAN parameters */ +#define CPC_CMD_TYPE_RTR_FRAME 13 /* CAN remote frame */ +#define CPC_CMD_TYPE_CAN_STATE 14 /* CAN state message */ +#define CPC_CMD_TYPE_EXT_CAN_FRAME 15 /* Extended CAN data frame */ +#define CPC_CMD_TYPE_EXT_RTR_FRAME 16 /* Extended CAN remote frame */ +#define CPC_CMD_TYPE_CAN_EXIT 200 /* exit the CAN */ + +#define CPC_CMD_TYPE_INQ_ERR_COUNTER 25 /* request the CAN error counters */ +#define CPC_CMD_TYPE_CLEAR_MSG_QUEUE 8 /* clear CPC_MSG queue */ +#define CPC_CMD_TYPE_CLEAR_CMD_QUEUE 28 /* clear CPC_CMD queue */ + +#define CPC_CC_TYPE_SJA1000 2 /* Philips basic CAN controller */ + +#define CPC_CAN_ECODE_ERRFRAME 0x01 /* Ecode type */ + +/* Overrun types */ +#define CPC_OVR_EVENT_CAN 0x01 +#define CPC_OVR_EVENT_CANSTATE 0x02 +#define CPC_OVR_EVENT_BUSERROR 0x04 + +/* + * If the CAN controller lost a message we indicate it with the highest bit + * set in the count field. + */ +#define CPC_OVR_HW 0x80 + +/* Size of the "struct ems_cpc_msg" without the union */ +#define CPC_MSG_HEADER_LEN 11 +#define CPC_CAN_MSG_MIN_SIZE 5 + +/* Define these values to match your devices */ +#define USB_CPCUSB_VENDOR_ID 0x12D6 + +#define USB_CPCUSB_ARM7_PRODUCT_ID 0x0444 + +/* Mode register NXP LPC2119/SJA1000 CAN Controller */ +#define SJA1000_MOD_NORMAL 0x00 +#define SJA1000_MOD_RM 0x01 + +/* ECC register NXP LPC2119/SJA1000 CAN Controller */ +#define SJA1000_ECC_SEG 0x1F +#define SJA1000_ECC_DIR 0x20 +#define SJA1000_ECC_ERR 0x06 +#define SJA1000_ECC_BIT 0x00 +#define SJA1000_ECC_FORM 0x40 +#define SJA1000_ECC_STUFF 0x80 +#define SJA1000_ECC_MASK 0xc0 + +/* Status register content */ +#define SJA1000_SR_BS 0x80 +#define SJA1000_SR_ES 0x40 + +#define SJA1000_DEFAULT_OUTPUT_CONTROL 0xDA + +/* + * The device actually uses a 16MHz clock to generate the CAN clock + * but it expects SJA1000 bit settings based on 8MHz (is internally + * converted). + */ +#define EMS_USB_ARM7_CLOCK 8000000 + +#define CPC_TX_QUEUE_TRIGGER_LOW 25 +#define CPC_TX_QUEUE_TRIGGER_HIGH 35 + +/* + * CAN-Message representation in a CPC_MSG. Message object type is + * CPC_MSG_TYPE_CAN_FRAME or CPC_MSG_TYPE_RTR_FRAME or + * CPC_MSG_TYPE_EXT_CAN_FRAME or CPC_MSG_TYPE_EXT_RTR_FRAME. + */ +struct cpc_can_msg { + __le32 id; + u8 length; + u8 msg[8]; +}; + +/* Representation of the CAN parameters for the SJA1000 controller */ +struct cpc_sja1000_params { + u8 mode; + u8 acc_code0; + u8 acc_code1; + u8 acc_code2; + u8 acc_code3; + u8 acc_mask0; + u8 acc_mask1; + u8 acc_mask2; + u8 acc_mask3; + u8 btr0; + u8 btr1; + u8 outp_contr; +}; + +/* CAN params message representation */ +struct cpc_can_params { + u8 cc_type; + + /* Will support M16C CAN controller in the future */ + union { + struct cpc_sja1000_params sja1000; + } cc_params; +}; + +/* Structure for confirmed message handling */ +struct cpc_confirm { + u8 error; /* error code */ +}; + +/* Structure for overrun conditions */ +struct cpc_overrun { + u8 event; + u8 count; +}; + +/* SJA1000 CAN errors (compatible to NXP LPC2119) */ +struct cpc_sja1000_can_error { + u8 ecc; + u8 rxerr; + u8 txerr; +}; + +/* structure for CAN error conditions */ +struct cpc_can_error { + u8 ecode; + + struct { + u8 cc_type; + + /* Other controllers may also provide error code capture regs */ + union { + struct cpc_sja1000_can_error sja1000; + } regs; + } cc; +}; + +/* + * Structure containing RX/TX error counter. This structure is used to request + * the values of the CAN controllers TX and RX error counter. + */ +struct cpc_can_err_counter { + u8 rx; + u8 tx; +}; + +/* Main message type used between library and application */ +struct __packed ems_cpc_msg { + u8 type; /* type of message */ + u8 length; /* length of data within union 'msg' */ + u8 msgid; /* confirmation handle */ + __le32 ts_sec; /* timestamp in seconds */ + __le32 ts_nsec; /* timestamp in nano seconds */ + + union __packed { + u8 generic[64]; + struct cpc_can_msg can_msg; + struct cpc_can_params can_params; + struct cpc_confirm confirmation; + struct cpc_overrun overrun; + struct cpc_can_error error; + struct cpc_can_err_counter err_counter; + u8 can_state; + } msg; +}; + +/* + * Table of devices that work with this driver + * NOTE: This driver supports only CPC-USB/ARM7 (LPC2119) yet. + */ +static struct usb_device_id ems_usb_table[] = { + {USB_DEVICE(USB_CPCUSB_VENDOR_ID, USB_CPCUSB_ARM7_PRODUCT_ID)}, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, ems_usb_table); + +#define RX_BUFFER_SIZE 64 +#define CPC_HEADER_SIZE 4 +#define INTR_IN_BUFFER_SIZE 4 + +#define MAX_RX_URBS 10 +#define MAX_TX_URBS 10 + +struct ems_usb; + +struct ems_tx_urb_context { + struct ems_usb *dev; + + u32 echo_index; + u8 dlc; +}; + +struct ems_usb { + struct can_priv can; /* must be the first member */ + + struct sk_buff *echo_skb[MAX_TX_URBS]; + + struct usb_device *udev; + struct net_device *netdev; + + atomic_t active_tx_urbs; + struct usb_anchor tx_submitted; + struct ems_tx_urb_context tx_contexts[MAX_TX_URBS]; + + struct usb_anchor rx_submitted; + + struct urb *intr_urb; + + u8 *tx_msg_buffer; + + u8 *intr_in_buffer; + unsigned int free_slots; /* remember number of available slots */ + + struct ems_cpc_msg active_params; /* active controller parameters */ + void *rxbuf[MAX_RX_URBS]; + dma_addr_t rxbuf_dma[MAX_RX_URBS]; +}; + +static void ems_usb_read_interrupt_callback(struct urb *urb) +{ + struct ems_usb *dev = urb->context; + struct net_device *netdev = dev->netdev; + int err; + + if (!netif_device_present(netdev)) + return; + + switch (urb->status) { + case 0: + dev->free_slots = dev->intr_in_buffer[1]; + if (dev->free_slots > CPC_TX_QUEUE_TRIGGER_HIGH && + netif_queue_stopped(netdev)) + netif_wake_queue(netdev); + break; + + case -ECONNRESET: /* unlink */ + case -ENOENT: + case -EPIPE: + case -EPROTO: + case -ESHUTDOWN: + return; + + default: + netdev_info(netdev, "Rx interrupt aborted %d\n", urb->status); + break; + } + + err = usb_submit_urb(urb, GFP_ATOMIC); + + if (err == -ENODEV) + netif_device_detach(netdev); + else if (err) + netdev_err(netdev, "failed resubmitting intr urb: %d\n", err); +} + +static void ems_usb_rx_can_msg(struct ems_usb *dev, struct ems_cpc_msg *msg) +{ + struct can_frame *cf; + struct sk_buff *skb; + int i; + struct net_device_stats *stats = &dev->netdev->stats; + + skb = alloc_can_skb(dev->netdev, &cf); + if (skb == NULL) + return; + + cf->can_id = le32_to_cpu(msg->msg.can_msg.id); + cf->can_dlc = get_can_dlc(msg->msg.can_msg.length & 0xF); + + if (msg->type == CPC_MSG_TYPE_EXT_CAN_FRAME || + msg->type == CPC_MSG_TYPE_EXT_RTR_FRAME) + cf->can_id |= CAN_EFF_FLAG; + + if (msg->type == CPC_MSG_TYPE_RTR_FRAME || + msg->type == CPC_MSG_TYPE_EXT_RTR_FRAME) { + cf->can_id |= CAN_RTR_FLAG; + } else { + for (i = 0; i < cf->can_dlc; i++) + cf->data[i] = msg->msg.can_msg.msg[i]; + } + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); +} + +static void ems_usb_rx_err(struct ems_usb *dev, struct ems_cpc_msg *msg) +{ + struct can_frame *cf; + struct sk_buff *skb; + struct net_device_stats *stats = &dev->netdev->stats; + + skb = alloc_can_err_skb(dev->netdev, &cf); + if (skb == NULL) + return; + + if (msg->type == CPC_MSG_TYPE_CAN_STATE) { + u8 state = msg->msg.can_state; + + if (state & SJA1000_SR_BS) { + dev->can.state = CAN_STATE_BUS_OFF; + cf->can_id |= CAN_ERR_BUSOFF; + + dev->can.can_stats.bus_off++; + can_bus_off(dev->netdev); + } else if (state & SJA1000_SR_ES) { + dev->can.state = CAN_STATE_ERROR_WARNING; + dev->can.can_stats.error_warning++; + } else { + dev->can.state = CAN_STATE_ERROR_ACTIVE; + dev->can.can_stats.error_passive++; + } + } else if (msg->type == CPC_MSG_TYPE_CAN_FRAME_ERROR) { + u8 ecc = msg->msg.error.cc.regs.sja1000.ecc; + u8 txerr = msg->msg.error.cc.regs.sja1000.txerr; + u8 rxerr = msg->msg.error.cc.regs.sja1000.rxerr; + + /* bus error interrupt */ + dev->can.can_stats.bus_error++; + stats->rx_errors++; + + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + + switch (ecc & SJA1000_ECC_MASK) { + case SJA1000_ECC_BIT: + cf->data[2] |= CAN_ERR_PROT_BIT; + break; + case SJA1000_ECC_FORM: + cf->data[2] |= CAN_ERR_PROT_FORM; + break; + case SJA1000_ECC_STUFF: + cf->data[2] |= CAN_ERR_PROT_STUFF; + break; + default: + cf->data[3] = ecc & SJA1000_ECC_SEG; + break; + } + + /* Error occurred during transmission? */ + if ((ecc & SJA1000_ECC_DIR) == 0) + cf->data[2] |= CAN_ERR_PROT_TX; + + if (dev->can.state == CAN_STATE_ERROR_WARNING || + dev->can.state == CAN_STATE_ERROR_PASSIVE) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = (txerr > rxerr) ? + CAN_ERR_CRTL_TX_PASSIVE : CAN_ERR_CRTL_RX_PASSIVE; + } + } else if (msg->type == CPC_MSG_TYPE_OVERRUN) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + + stats->rx_over_errors++; + stats->rx_errors++; + } + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); +} + +/* + * callback for bulk IN urb + */ +static void ems_usb_read_bulk_callback(struct urb *urb) +{ + struct ems_usb *dev = urb->context; + struct net_device *netdev; + int retval; + + netdev = dev->netdev; + + if (!netif_device_present(netdev)) + return; + + switch (urb->status) { + case 0: /* success */ + break; + + case -ENOENT: + return; + + default: + netdev_info(netdev, "Rx URB aborted (%d)\n", urb->status); + goto resubmit_urb; + } + + if (urb->actual_length > CPC_HEADER_SIZE) { + struct ems_cpc_msg *msg; + u8 *ibuf = urb->transfer_buffer; + u8 msg_count, start; + + msg_count = ibuf[0] & ~0x80; + + start = CPC_HEADER_SIZE; + + while (msg_count) { + msg = (struct ems_cpc_msg *)&ibuf[start]; + + switch (msg->type) { + case CPC_MSG_TYPE_CAN_STATE: + /* Process CAN state changes */ + ems_usb_rx_err(dev, msg); + break; + + case CPC_MSG_TYPE_CAN_FRAME: + case CPC_MSG_TYPE_EXT_CAN_FRAME: + case CPC_MSG_TYPE_RTR_FRAME: + case CPC_MSG_TYPE_EXT_RTR_FRAME: + ems_usb_rx_can_msg(dev, msg); + break; + + case CPC_MSG_TYPE_CAN_FRAME_ERROR: + /* Process errorframe */ + ems_usb_rx_err(dev, msg); + break; + + case CPC_MSG_TYPE_OVERRUN: + /* Message lost while receiving */ + ems_usb_rx_err(dev, msg); + break; + } + + start += CPC_MSG_HEADER_LEN + msg->length; + msg_count--; + + if (start > urb->transfer_buffer_length) { + netdev_err(netdev, "format error\n"); + break; + } + } + } + +resubmit_urb: + usb_fill_bulk_urb(urb, dev->udev, usb_rcvbulkpipe(dev->udev, 2), + urb->transfer_buffer, RX_BUFFER_SIZE, + ems_usb_read_bulk_callback, dev); + + retval = usb_submit_urb(urb, GFP_ATOMIC); + + if (retval == -ENODEV) + netif_device_detach(netdev); + else if (retval) + netdev_err(netdev, + "failed resubmitting read bulk urb: %d\n", retval); +} + +/* + * callback for bulk IN urb + */ +static void ems_usb_write_bulk_callback(struct urb *urb) +{ + struct ems_tx_urb_context *context = urb->context; + struct ems_usb *dev; + struct net_device *netdev; + + BUG_ON(!context); + + dev = context->dev; + netdev = dev->netdev; + + /* free up our allocated buffer */ + usb_free_coherent(urb->dev, urb->transfer_buffer_length, + urb->transfer_buffer, urb->transfer_dma); + + atomic_dec(&dev->active_tx_urbs); + + if (!netif_device_present(netdev)) + return; + + if (urb->status) + netdev_info(netdev, "Tx URB aborted (%d)\n", urb->status); + + netif_trans_update(netdev); + + /* transmission complete interrupt */ + netdev->stats.tx_packets++; + netdev->stats.tx_bytes += context->dlc; + + can_get_echo_skb(netdev, context->echo_index); + + /* Release context */ + context->echo_index = MAX_TX_URBS; + +} + +/* + * Send the given CPC command synchronously + */ +static int ems_usb_command_msg(struct ems_usb *dev, struct ems_cpc_msg *msg) +{ + int actual_length; + + /* Copy payload */ + memcpy(&dev->tx_msg_buffer[CPC_HEADER_SIZE], msg, + msg->length + CPC_MSG_HEADER_LEN); + + /* Clear header */ + memset(&dev->tx_msg_buffer[0], 0, CPC_HEADER_SIZE); + + return usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 2), + &dev->tx_msg_buffer[0], + msg->length + CPC_MSG_HEADER_LEN + CPC_HEADER_SIZE, + &actual_length, 1000); +} + +/* + * Change CAN controllers' mode register + */ +static int ems_usb_write_mode(struct ems_usb *dev, u8 mode) +{ + dev->active_params.msg.can_params.cc_params.sja1000.mode = mode; + + return ems_usb_command_msg(dev, &dev->active_params); +} + +/* + * Send a CPC_Control command to change behaviour when interface receives a CAN + * message, bus error or CAN state changed notifications. + */ +static int ems_usb_control_cmd(struct ems_usb *dev, u8 val) +{ + struct ems_cpc_msg cmd; + + cmd.type = CPC_CMD_TYPE_CONTROL; + cmd.length = CPC_MSG_HEADER_LEN + 1; + + cmd.msgid = 0; + + cmd.msg.generic[0] = val; + + return ems_usb_command_msg(dev, &cmd); +} + +/* + * Start interface + */ +static int ems_usb_start(struct ems_usb *dev) +{ + struct net_device *netdev = dev->netdev; + int err, i; + + dev->intr_in_buffer[0] = 0; + dev->free_slots = 50; /* initial size */ + + for (i = 0; i < MAX_RX_URBS; i++) { + struct urb *urb = NULL; + u8 *buf = NULL; + dma_addr_t buf_dma; + + /* create a URB, and a buffer for it */ + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + err = -ENOMEM; + break; + } + + buf = usb_alloc_coherent(dev->udev, RX_BUFFER_SIZE, GFP_KERNEL, + &buf_dma); + if (!buf) { + netdev_err(netdev, "No memory left for USB buffer\n"); + usb_free_urb(urb); + err = -ENOMEM; + break; + } + + urb->transfer_dma = buf_dma; + + usb_fill_bulk_urb(urb, dev->udev, usb_rcvbulkpipe(dev->udev, 2), + buf, RX_BUFFER_SIZE, + ems_usb_read_bulk_callback, dev); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(urb, &dev->rx_submitted); + + err = usb_submit_urb(urb, GFP_KERNEL); + if (err) { + usb_unanchor_urb(urb); + usb_free_coherent(dev->udev, RX_BUFFER_SIZE, buf, + urb->transfer_dma); + usb_free_urb(urb); + break; + } + + dev->rxbuf[i] = buf; + dev->rxbuf_dma[i] = buf_dma; + + /* Drop reference, USB core will take care of freeing it */ + usb_free_urb(urb); + } + + /* Did we submit any URBs */ + if (i == 0) { + netdev_warn(netdev, "couldn't setup read URBs\n"); + return err; + } + + /* Warn if we've couldn't transmit all the URBs */ + if (i < MAX_RX_URBS) + netdev_warn(netdev, "rx performance may be slow\n"); + + /* Setup and start interrupt URB */ + usb_fill_int_urb(dev->intr_urb, dev->udev, + usb_rcvintpipe(dev->udev, 1), + dev->intr_in_buffer, + INTR_IN_BUFFER_SIZE, + ems_usb_read_interrupt_callback, dev, 1); + + err = usb_submit_urb(dev->intr_urb, GFP_KERNEL); + if (err) { + netdev_warn(netdev, "intr URB submit failed: %d\n", err); + + return err; + } + + /* CPC-USB will transfer received message to host */ + err = ems_usb_control_cmd(dev, CONTR_CAN_MESSAGE | CONTR_CONT_ON); + if (err) + goto failed; + + /* CPC-USB will transfer CAN state changes to host */ + err = ems_usb_control_cmd(dev, CONTR_CAN_STATE | CONTR_CONT_ON); + if (err) + goto failed; + + /* CPC-USB will transfer bus errors to host */ + err = ems_usb_control_cmd(dev, CONTR_BUS_ERROR | CONTR_CONT_ON); + if (err) + goto failed; + + err = ems_usb_write_mode(dev, SJA1000_MOD_NORMAL); + if (err) + goto failed; + + dev->can.state = CAN_STATE_ERROR_ACTIVE; + + return 0; + +failed: + netdev_warn(netdev, "couldn't submit control: %d\n", err); + + return err; +} + +static void unlink_all_urbs(struct ems_usb *dev) +{ + int i; + + usb_unlink_urb(dev->intr_urb); + + usb_kill_anchored_urbs(&dev->rx_submitted); + + for (i = 0; i < MAX_RX_URBS; ++i) + usb_free_coherent(dev->udev, RX_BUFFER_SIZE, + dev->rxbuf[i], dev->rxbuf_dma[i]); + + usb_kill_anchored_urbs(&dev->tx_submitted); + atomic_set(&dev->active_tx_urbs, 0); + + for (i = 0; i < MAX_TX_URBS; i++) + dev->tx_contexts[i].echo_index = MAX_TX_URBS; +} + +static int ems_usb_open(struct net_device *netdev) +{ + struct ems_usb *dev = netdev_priv(netdev); + int err; + + err = ems_usb_write_mode(dev, SJA1000_MOD_RM); + if (err) + return err; + + /* common open */ + err = open_candev(netdev); + if (err) + return err; + + /* finally start device */ + err = ems_usb_start(dev); + if (err) { + if (err == -ENODEV) + netif_device_detach(dev->netdev); + + netdev_warn(netdev, "couldn't start device: %d\n", err); + + close_candev(netdev); + + return err; + } + + + netif_start_queue(netdev); + + return 0; +} + +static netdev_tx_t ems_usb_start_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct ems_usb *dev = netdev_priv(netdev); + struct ems_tx_urb_context *context = NULL; + struct net_device_stats *stats = &netdev->stats; + struct can_frame *cf = (struct can_frame *)skb->data; + struct ems_cpc_msg *msg; + struct urb *urb; + u8 *buf; + int i, err; + size_t size = CPC_HEADER_SIZE + CPC_MSG_HEADER_LEN + + sizeof(struct cpc_can_msg); + + 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) + goto nomem; + + buf = usb_alloc_coherent(dev->udev, size, GFP_ATOMIC, &urb->transfer_dma); + if (!buf) { + netdev_err(netdev, "No memory left for USB buffer\n"); + usb_free_urb(urb); + goto nomem; + } + + msg = (struct ems_cpc_msg *)&buf[CPC_HEADER_SIZE]; + + msg->msg.can_msg.id = cpu_to_le32(cf->can_id & CAN_ERR_MASK); + msg->msg.can_msg.length = cf->can_dlc; + + if (cf->can_id & CAN_RTR_FLAG) { + msg->type = cf->can_id & CAN_EFF_FLAG ? + CPC_CMD_TYPE_EXT_RTR_FRAME : CPC_CMD_TYPE_RTR_FRAME; + + msg->length = CPC_CAN_MSG_MIN_SIZE; + } else { + msg->type = cf->can_id & CAN_EFF_FLAG ? + CPC_CMD_TYPE_EXT_CAN_FRAME : CPC_CMD_TYPE_CAN_FRAME; + + for (i = 0; i < cf->can_dlc; i++) + msg->msg.can_msg.msg[i] = cf->data[i]; + + msg->length = CPC_CAN_MSG_MIN_SIZE + cf->can_dlc; + } + + for (i = 0; i < MAX_TX_URBS; i++) { + if (dev->tx_contexts[i].echo_index == MAX_TX_URBS) { + context = &dev->tx_contexts[i]; + break; + } + } + + /* + * May never happen! When this happens we'd more URBs in flight as + * allowed (MAX_TX_URBS). + */ + if (!context) { + usb_free_coherent(dev->udev, size, buf, urb->transfer_dma); + usb_free_urb(urb); + + netdev_warn(netdev, "couldn't find free context\n"); + + return NETDEV_TX_BUSY; + } + + context->dev = dev; + context->echo_index = i; + context->dlc = cf->can_dlc; + + usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, 2), buf, + size, ems_usb_write_bulk_callback, context); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(urb, &dev->tx_submitted); + + can_put_echo_skb(skb, netdev, context->echo_index); + + atomic_inc(&dev->active_tx_urbs); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (unlikely(err)) { + can_free_echo_skb(netdev, context->echo_index); + + usb_unanchor_urb(urb); + usb_free_coherent(dev->udev, size, buf, urb->transfer_dma); + + atomic_dec(&dev->active_tx_urbs); + + if (err == -ENODEV) { + netif_device_detach(netdev); + } else { + netdev_warn(netdev, "failed tx_urb %d\n", err); + + stats->tx_dropped++; + } + } else { + netif_trans_update(netdev); + + /* Slow down tx path */ + if (atomic_read(&dev->active_tx_urbs) >= MAX_TX_URBS || + dev->free_slots < CPC_TX_QUEUE_TRIGGER_LOW) { + netif_stop_queue(netdev); + } + } + + /* + * Release our reference to this URB, the USB core will eventually free + * it entirely. + */ + usb_free_urb(urb); + + return NETDEV_TX_OK; + +nomem: + dev_kfree_skb(skb); + stats->tx_dropped++; + + return NETDEV_TX_OK; +} + +static int ems_usb_close(struct net_device *netdev) +{ + struct ems_usb *dev = netdev_priv(netdev); + + /* Stop polling */ + unlink_all_urbs(dev); + + netif_stop_queue(netdev); + + /* Set CAN controller to reset mode */ + if (ems_usb_write_mode(dev, SJA1000_MOD_RM)) + netdev_warn(netdev, "couldn't stop device"); + + close_candev(netdev); + + return 0; +} + +static const struct net_device_ops ems_usb_netdev_ops = { + .ndo_open = ems_usb_open, + .ndo_stop = ems_usb_close, + .ndo_start_xmit = ems_usb_start_xmit, + .ndo_change_mtu = can_change_mtu, +}; + +static const struct can_bittiming_const ems_usb_bittiming_const = { + .name = "ems_usb", + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 64, + .brp_inc = 1, +}; + +static int ems_usb_set_mode(struct net_device *netdev, enum can_mode mode) +{ + struct ems_usb *dev = netdev_priv(netdev); + + switch (mode) { + case CAN_MODE_START: + if (ems_usb_write_mode(dev, SJA1000_MOD_NORMAL)) + netdev_warn(netdev, "couldn't start device"); + + if (netif_queue_stopped(netdev)) + netif_wake_queue(netdev); + break; + + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int ems_usb_set_bittiming(struct net_device *netdev) +{ + struct ems_usb *dev = netdev_priv(netdev); + struct can_bittiming *bt = &dev->can.bittiming; + u8 btr0, btr1; + + btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6); + btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) | + (((bt->phase_seg2 - 1) & 0x7) << 4); + if (dev->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + btr1 |= 0x80; + + netdev_info(netdev, "setting BTR0=0x%02x BTR1=0x%02x\n", btr0, btr1); + + dev->active_params.msg.can_params.cc_params.sja1000.btr0 = btr0; + dev->active_params.msg.can_params.cc_params.sja1000.btr1 = btr1; + + return ems_usb_command_msg(dev, &dev->active_params); +} + +static void init_params_sja1000(struct ems_cpc_msg *msg) +{ + struct cpc_sja1000_params *sja1000 = + &msg->msg.can_params.cc_params.sja1000; + + msg->type = CPC_CMD_TYPE_CAN_PARAMS; + msg->length = sizeof(struct cpc_can_params); + msg->msgid = 0; + + msg->msg.can_params.cc_type = CPC_CC_TYPE_SJA1000; + + /* Acceptance filter open */ + sja1000->acc_code0 = 0x00; + sja1000->acc_code1 = 0x00; + sja1000->acc_code2 = 0x00; + sja1000->acc_code3 = 0x00; + + /* Acceptance filter open */ + sja1000->acc_mask0 = 0xFF; + sja1000->acc_mask1 = 0xFF; + sja1000->acc_mask2 = 0xFF; + sja1000->acc_mask3 = 0xFF; + + sja1000->btr0 = 0; + sja1000->btr1 = 0; + + sja1000->outp_contr = SJA1000_DEFAULT_OUTPUT_CONTROL; + sja1000->mode = SJA1000_MOD_RM; +} + +/* + * probe function for new CPC-USB devices + */ +static int ems_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct net_device *netdev; + struct ems_usb *dev; + int i, err = -ENOMEM; + + netdev = alloc_candev(sizeof(struct ems_usb), MAX_TX_URBS); + if (!netdev) { + dev_err(&intf->dev, "ems_usb: Couldn't alloc candev\n"); + return -ENOMEM; + } + + dev = netdev_priv(netdev); + + dev->udev = interface_to_usbdev(intf); + dev->netdev = netdev; + + dev->can.state = CAN_STATE_STOPPED; + dev->can.clock.freq = EMS_USB_ARM7_CLOCK; + dev->can.bittiming_const = &ems_usb_bittiming_const; + dev->can.do_set_bittiming = ems_usb_set_bittiming; + dev->can.do_set_mode = ems_usb_set_mode; + dev->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES; + + netdev->netdev_ops = &ems_usb_netdev_ops; + + netdev->flags |= IFF_ECHO; /* we support local echo */ + + init_usb_anchor(&dev->rx_submitted); + + init_usb_anchor(&dev->tx_submitted); + atomic_set(&dev->active_tx_urbs, 0); + + for (i = 0; i < MAX_TX_URBS; i++) + dev->tx_contexts[i].echo_index = MAX_TX_URBS; + + dev->intr_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->intr_urb) + goto cleanup_candev; + + dev->intr_in_buffer = kzalloc(INTR_IN_BUFFER_SIZE, GFP_KERNEL); + if (!dev->intr_in_buffer) + goto cleanup_intr_urb; + + dev->tx_msg_buffer = kzalloc(CPC_HEADER_SIZE + + sizeof(struct ems_cpc_msg), GFP_KERNEL); + if (!dev->tx_msg_buffer) + goto cleanup_intr_in_buffer; + + usb_set_intfdata(intf, dev); + + SET_NETDEV_DEV(netdev, &intf->dev); + + init_params_sja1000(&dev->active_params); + + err = ems_usb_command_msg(dev, &dev->active_params); + if (err) { + netdev_err(netdev, "couldn't initialize controller: %d\n", err); + goto cleanup_tx_msg_buffer; + } + + err = register_candev(netdev); + if (err) { + netdev_err(netdev, "couldn't register CAN device: %d\n", err); + goto cleanup_tx_msg_buffer; + } + + return 0; + +cleanup_tx_msg_buffer: + kfree(dev->tx_msg_buffer); + +cleanup_intr_in_buffer: + kfree(dev->intr_in_buffer); + +cleanup_intr_urb: + usb_free_urb(dev->intr_urb); + +cleanup_candev: + free_candev(netdev); + + return err; +} + +/* + * called by the usb core when the device is removed from the system + */ +static void ems_usb_disconnect(struct usb_interface *intf) +{ + struct ems_usb *dev = usb_get_intfdata(intf); + + usb_set_intfdata(intf, NULL); + + if (dev) { + unregister_netdev(dev->netdev); + + unlink_all_urbs(dev); + + usb_free_urb(dev->intr_urb); + + kfree(dev->intr_in_buffer); + kfree(dev->tx_msg_buffer); + + free_candev(dev->netdev); + } +} + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver ems_usb_driver = { + .name = "ems_usb", + .probe = ems_usb_probe, + .disconnect = ems_usb_disconnect, + .id_table = ems_usb_table, +}; + +module_usb_driver(ems_usb_driver); diff --git a/drivers/net/can/usb/esd_usb2.c b/drivers/net/can/usb/esd_usb2.c new file mode 100644 index 000000000..c9ccce6c6 --- /dev/null +++ b/drivers/net/can/usb/esd_usb2.c @@ -0,0 +1,1163 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * CAN driver for esd CAN-USB/2 and CAN-USB/Micro + * + * Copyright (C) 2010-2012 Matthias Fuchs <matthias.fuchs@esd.eu>, esd gmbh + */ +#include <linux/signal.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/usb.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> + +MODULE_AUTHOR("Matthias Fuchs <matthias.fuchs@esd.eu>"); +MODULE_DESCRIPTION("CAN driver for esd CAN-USB/2 and CAN-USB/Micro interfaces"); +MODULE_LICENSE("GPL v2"); + +/* Define these values to match your devices */ +#define USB_ESDGMBH_VENDOR_ID 0x0ab4 +#define USB_CANUSB2_PRODUCT_ID 0x0010 +#define USB_CANUSBM_PRODUCT_ID 0x0011 + +#define ESD_USB2_CAN_CLOCK 60000000 +#define ESD_USBM_CAN_CLOCK 36000000 +#define ESD_USB2_MAX_NETS 2 + +/* USB2 commands */ +#define CMD_VERSION 1 /* also used for VERSION_REPLY */ +#define CMD_CAN_RX 2 /* device to host only */ +#define CMD_CAN_TX 3 /* also used for TX_DONE */ +#define CMD_SETBAUD 4 /* also used for SETBAUD_REPLY */ +#define CMD_TS 5 /* also used for TS_REPLY */ +#define CMD_IDADD 6 /* also used for IDADD_REPLY */ + +/* esd CAN message flags - dlc field */ +#define ESD_RTR 0x10 + +/* esd CAN message flags - id field */ +#define ESD_EXTID 0x20000000 +#define ESD_EVENT 0x40000000 +#define ESD_IDMASK 0x1fffffff + +/* esd CAN event ids used by this driver */ +#define ESD_EV_CAN_ERROR_EXT 2 + +/* baudrate message flags */ +#define ESD_USB2_UBR 0x80000000 +#define ESD_USB2_LOM 0x40000000 +#define ESD_USB2_NO_BAUDRATE 0x7fffffff +#define ESD_USB2_TSEG1_MIN 1 +#define ESD_USB2_TSEG1_MAX 16 +#define ESD_USB2_TSEG1_SHIFT 16 +#define ESD_USB2_TSEG2_MIN 1 +#define ESD_USB2_TSEG2_MAX 8 +#define ESD_USB2_TSEG2_SHIFT 20 +#define ESD_USB2_SJW_MAX 4 +#define ESD_USB2_SJW_SHIFT 14 +#define ESD_USBM_SJW_SHIFT 24 +#define ESD_USB2_BRP_MIN 1 +#define ESD_USB2_BRP_MAX 1024 +#define ESD_USB2_BRP_INC 1 +#define ESD_USB2_3_SAMPLES 0x00800000 + +/* esd IDADD message */ +#define ESD_ID_ENABLE 0x80 +#define ESD_MAX_ID_SEGMENT 64 + +/* SJA1000 ECC register (emulated by usb2 firmware) */ +#define SJA1000_ECC_SEG 0x1F +#define SJA1000_ECC_DIR 0x20 +#define SJA1000_ECC_ERR 0x06 +#define SJA1000_ECC_BIT 0x00 +#define SJA1000_ECC_FORM 0x40 +#define SJA1000_ECC_STUFF 0x80 +#define SJA1000_ECC_MASK 0xc0 + +/* esd bus state event codes */ +#define ESD_BUSSTATE_MASK 0xc0 +#define ESD_BUSSTATE_WARN 0x40 +#define ESD_BUSSTATE_ERRPASSIVE 0x80 +#define ESD_BUSSTATE_BUSOFF 0xc0 + +#define RX_BUFFER_SIZE 1024 +#define MAX_RX_URBS 4 +#define MAX_TX_URBS 16 /* must be power of 2 */ + +struct header_msg { + u8 len; /* len is always the total message length in 32bit words */ + u8 cmd; + u8 rsvd[2]; +}; + +struct version_msg { + u8 len; + u8 cmd; + u8 rsvd; + u8 flags; + __le32 drv_version; +}; + +struct version_reply_msg { + u8 len; + u8 cmd; + u8 nets; + u8 features; + __le32 version; + u8 name[16]; + __le32 rsvd; + __le32 ts; +}; + +struct rx_msg { + u8 len; + u8 cmd; + u8 net; + u8 dlc; + __le32 ts; + __le32 id; /* upper 3 bits contain flags */ + u8 data[8]; +}; + +struct tx_msg { + u8 len; + u8 cmd; + u8 net; + u8 dlc; + u32 hnd; /* opaque handle, not used by device */ + __le32 id; /* upper 3 bits contain flags */ + u8 data[8]; +}; + +struct tx_done_msg { + u8 len; + u8 cmd; + u8 net; + u8 status; + u32 hnd; /* opaque handle, not used by device */ + __le32 ts; +}; + +struct id_filter_msg { + u8 len; + u8 cmd; + u8 net; + u8 option; + __le32 mask[ESD_MAX_ID_SEGMENT + 1]; +}; + +struct set_baudrate_msg { + u8 len; + u8 cmd; + u8 net; + u8 rsvd; + __le32 baud; +}; + +/* Main message type used between library and application */ +struct __attribute__ ((packed)) esd_usb2_msg { + union { + struct header_msg hdr; + struct version_msg version; + struct version_reply_msg version_reply; + struct rx_msg rx; + struct tx_msg tx; + struct tx_done_msg txdone; + struct set_baudrate_msg setbaud; + struct id_filter_msg filter; + } msg; +}; + +static struct usb_device_id esd_usb2_table[] = { + {USB_DEVICE(USB_ESDGMBH_VENDOR_ID, USB_CANUSB2_PRODUCT_ID)}, + {USB_DEVICE(USB_ESDGMBH_VENDOR_ID, USB_CANUSBM_PRODUCT_ID)}, + {} +}; +MODULE_DEVICE_TABLE(usb, esd_usb2_table); + +struct esd_usb2_net_priv; + +struct esd_tx_urb_context { + struct esd_usb2_net_priv *priv; + u32 echo_index; + int dlc; +}; + +struct esd_usb2 { + struct usb_device *udev; + struct esd_usb2_net_priv *nets[ESD_USB2_MAX_NETS]; + + struct usb_anchor rx_submitted; + + int net_count; + u32 version; + int rxinitdone; + void *rxbuf[MAX_RX_URBS]; + dma_addr_t rxbuf_dma[MAX_RX_URBS]; +}; + +struct esd_usb2_net_priv { + struct can_priv can; /* must be the first member */ + + atomic_t active_tx_jobs; + struct usb_anchor tx_submitted; + struct esd_tx_urb_context tx_contexts[MAX_TX_URBS]; + + struct esd_usb2 *usb2; + struct net_device *netdev; + int index; + u8 old_state; + struct can_berr_counter bec; +}; + +static void esd_usb2_rx_event(struct esd_usb2_net_priv *priv, + struct esd_usb2_msg *msg) +{ + struct net_device_stats *stats = &priv->netdev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u32 id = le32_to_cpu(msg->msg.rx.id) & ESD_IDMASK; + + if (id == ESD_EV_CAN_ERROR_EXT) { + u8 state = msg->msg.rx.data[0]; + u8 ecc = msg->msg.rx.data[1]; + u8 rxerr = msg->msg.rx.data[2]; + u8 txerr = msg->msg.rx.data[3]; + + netdev_dbg(priv->netdev, + "CAN_ERR_EV_EXT: dlc=%#02x state=%02x ecc=%02x rec=%02x tec=%02x\n", + msg->msg.rx.dlc, state, ecc, rxerr, txerr); + + skb = alloc_can_err_skb(priv->netdev, &cf); + if (skb == NULL) { + stats->rx_dropped++; + return; + } + + if (state != priv->old_state) { + priv->old_state = state; + + switch (state & ESD_BUSSTATE_MASK) { + case ESD_BUSSTATE_BUSOFF: + priv->can.state = CAN_STATE_BUS_OFF; + cf->can_id |= CAN_ERR_BUSOFF; + priv->can.can_stats.bus_off++; + can_bus_off(priv->netdev); + break; + case ESD_BUSSTATE_WARN: + priv->can.state = CAN_STATE_ERROR_WARNING; + priv->can.can_stats.error_warning++; + break; + case ESD_BUSSTATE_ERRPASSIVE: + priv->can.state = CAN_STATE_ERROR_PASSIVE; + priv->can.can_stats.error_passive++; + break; + default: + priv->can.state = CAN_STATE_ERROR_ACTIVE; + txerr = 0; + rxerr = 0; + break; + } + } else { + priv->can.can_stats.bus_error++; + stats->rx_errors++; + + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + + switch (ecc & SJA1000_ECC_MASK) { + case SJA1000_ECC_BIT: + cf->data[2] |= CAN_ERR_PROT_BIT; + break; + case SJA1000_ECC_FORM: + cf->data[2] |= CAN_ERR_PROT_FORM; + break; + case SJA1000_ECC_STUFF: + cf->data[2] |= CAN_ERR_PROT_STUFF; + break; + default: + break; + } + + /* Error occurred during transmission? */ + if (!(ecc & SJA1000_ECC_DIR)) + cf->data[2] |= CAN_ERR_PROT_TX; + + /* Bit stream position in CAN frame as the error was detected */ + cf->data[3] = ecc & SJA1000_ECC_SEG; + + if (priv->can.state == CAN_STATE_ERROR_WARNING || + priv->can.state == CAN_STATE_ERROR_PASSIVE) { + cf->data[1] = (txerr > rxerr) ? + CAN_ERR_CRTL_TX_PASSIVE : + CAN_ERR_CRTL_RX_PASSIVE; + } + cf->data[6] = txerr; + cf->data[7] = rxerr; + } + + priv->bec.txerr = txerr; + priv->bec.rxerr = rxerr; + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + } +} + +static void esd_usb2_rx_can_msg(struct esd_usb2_net_priv *priv, + struct esd_usb2_msg *msg) +{ + struct net_device_stats *stats = &priv->netdev->stats; + struct can_frame *cf; + struct sk_buff *skb; + int i; + u32 id; + + if (!netif_device_present(priv->netdev)) + return; + + id = le32_to_cpu(msg->msg.rx.id); + + if (id & ESD_EVENT) { + esd_usb2_rx_event(priv, msg); + } else { + skb = alloc_can_skb(priv->netdev, &cf); + if (skb == NULL) { + stats->rx_dropped++; + return; + } + + cf->can_id = id & ESD_IDMASK; + cf->can_dlc = get_can_dlc(msg->msg.rx.dlc & ~ESD_RTR); + + if (id & ESD_EXTID) + cf->can_id |= CAN_EFF_FLAG; + + if (msg->msg.rx.dlc & ESD_RTR) { + cf->can_id |= CAN_RTR_FLAG; + } else { + for (i = 0; i < cf->can_dlc; i++) + cf->data[i] = msg->msg.rx.data[i]; + } + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + } + + return; +} + +static void esd_usb2_tx_done_msg(struct esd_usb2_net_priv *priv, + struct esd_usb2_msg *msg) +{ + struct net_device_stats *stats = &priv->netdev->stats; + struct net_device *netdev = priv->netdev; + struct esd_tx_urb_context *context; + + if (!netif_device_present(netdev)) + return; + + context = &priv->tx_contexts[msg->msg.txdone.hnd & (MAX_TX_URBS - 1)]; + + if (!msg->msg.txdone.status) { + stats->tx_packets++; + stats->tx_bytes += context->dlc; + can_get_echo_skb(netdev, context->echo_index); + } else { + stats->tx_errors++; + can_free_echo_skb(netdev, context->echo_index); + } + + /* Release context */ + context->echo_index = MAX_TX_URBS; + atomic_dec(&priv->active_tx_jobs); + + netif_wake_queue(netdev); +} + +static void esd_usb2_read_bulk_callback(struct urb *urb) +{ + struct esd_usb2 *dev = urb->context; + int retval; + int pos = 0; + int i; + + switch (urb->status) { + case 0: /* success */ + break; + + case -ENOENT: + case -EPIPE: + case -EPROTO: + case -ESHUTDOWN: + return; + + default: + dev_info(dev->udev->dev.parent, + "Rx URB aborted (%d)\n", urb->status); + goto resubmit_urb; + } + + while (pos < urb->actual_length) { + struct esd_usb2_msg *msg; + + msg = (struct esd_usb2_msg *)(urb->transfer_buffer + pos); + + switch (msg->msg.hdr.cmd) { + case CMD_CAN_RX: + if (msg->msg.rx.net >= dev->net_count) { + dev_err(dev->udev->dev.parent, "format error\n"); + break; + } + + esd_usb2_rx_can_msg(dev->nets[msg->msg.rx.net], msg); + break; + + case CMD_CAN_TX: + if (msg->msg.txdone.net >= dev->net_count) { + dev_err(dev->udev->dev.parent, "format error\n"); + break; + } + + esd_usb2_tx_done_msg(dev->nets[msg->msg.txdone.net], + msg); + break; + } + + pos += msg->msg.hdr.len << 2; + + if (pos > urb->actual_length) { + dev_err(dev->udev->dev.parent, "format error\n"); + break; + } + } + +resubmit_urb: + usb_fill_bulk_urb(urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1), + urb->transfer_buffer, RX_BUFFER_SIZE, + esd_usb2_read_bulk_callback, dev); + + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval == -ENODEV) { + for (i = 0; i < dev->net_count; i++) { + if (dev->nets[i]) + netif_device_detach(dev->nets[i]->netdev); + } + } else if (retval) { + dev_err(dev->udev->dev.parent, + "failed resubmitting read bulk urb: %d\n", retval); + } + + return; +} + +/* + * callback for bulk IN urb + */ +static void esd_usb2_write_bulk_callback(struct urb *urb) +{ + struct esd_tx_urb_context *context = urb->context; + struct esd_usb2_net_priv *priv; + struct net_device *netdev; + size_t size = sizeof(struct esd_usb2_msg); + + WARN_ON(!context); + + priv = context->priv; + netdev = priv->netdev; + + /* free up our allocated buffer */ + usb_free_coherent(urb->dev, size, + urb->transfer_buffer, urb->transfer_dma); + + if (!netif_device_present(netdev)) + return; + + if (urb->status) + netdev_info(netdev, "Tx URB aborted (%d)\n", urb->status); + + netif_trans_update(netdev); +} + +static ssize_t show_firmware(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct usb_interface *intf = to_usb_interface(d); + struct esd_usb2 *dev = usb_get_intfdata(intf); + + return sprintf(buf, "%d.%d.%d\n", + (dev->version >> 12) & 0xf, + (dev->version >> 8) & 0xf, + dev->version & 0xff); +} +static DEVICE_ATTR(firmware, 0444, show_firmware, NULL); + +static ssize_t show_hardware(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct usb_interface *intf = to_usb_interface(d); + struct esd_usb2 *dev = usb_get_intfdata(intf); + + return sprintf(buf, "%d.%d.%d\n", + (dev->version >> 28) & 0xf, + (dev->version >> 24) & 0xf, + (dev->version >> 16) & 0xff); +} +static DEVICE_ATTR(hardware, 0444, show_hardware, NULL); + +static ssize_t show_nets(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct usb_interface *intf = to_usb_interface(d); + struct esd_usb2 *dev = usb_get_intfdata(intf); + + return sprintf(buf, "%d", dev->net_count); +} +static DEVICE_ATTR(nets, 0444, show_nets, NULL); + +static int esd_usb2_send_msg(struct esd_usb2 *dev, struct esd_usb2_msg *msg) +{ + int actual_length; + + return usb_bulk_msg(dev->udev, + usb_sndbulkpipe(dev->udev, 2), + msg, + msg->msg.hdr.len << 2, + &actual_length, + 1000); +} + +static int esd_usb2_wait_msg(struct esd_usb2 *dev, + struct esd_usb2_msg *msg) +{ + int actual_length; + + return usb_bulk_msg(dev->udev, + usb_rcvbulkpipe(dev->udev, 1), + msg, + sizeof(*msg), + &actual_length, + 1000); +} + +static int esd_usb2_setup_rx_urbs(struct esd_usb2 *dev) +{ + int i, err = 0; + + if (dev->rxinitdone) + return 0; + + for (i = 0; i < MAX_RX_URBS; i++) { + struct urb *urb = NULL; + u8 *buf = NULL; + dma_addr_t buf_dma; + + /* create a URB, and a buffer for it */ + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + err = -ENOMEM; + break; + } + + buf = usb_alloc_coherent(dev->udev, RX_BUFFER_SIZE, GFP_KERNEL, + &buf_dma); + if (!buf) { + dev_warn(dev->udev->dev.parent, + "No memory left for USB buffer\n"); + err = -ENOMEM; + goto freeurb; + } + + urb->transfer_dma = buf_dma; + + usb_fill_bulk_urb(urb, dev->udev, + usb_rcvbulkpipe(dev->udev, 1), + buf, RX_BUFFER_SIZE, + esd_usb2_read_bulk_callback, dev); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(urb, &dev->rx_submitted); + + err = usb_submit_urb(urb, GFP_KERNEL); + if (err) { + usb_unanchor_urb(urb); + usb_free_coherent(dev->udev, RX_BUFFER_SIZE, buf, + urb->transfer_dma); + goto freeurb; + } + + dev->rxbuf[i] = buf; + dev->rxbuf_dma[i] = buf_dma; + +freeurb: + /* Drop reference, USB core will take care of freeing it */ + usb_free_urb(urb); + if (err) + break; + } + + /* Did we submit any URBs */ + if (i == 0) { + dev_err(dev->udev->dev.parent, "couldn't setup read URBs\n"); + return err; + } + + /* Warn if we've couldn't transmit all the URBs */ + if (i < MAX_RX_URBS) { + dev_warn(dev->udev->dev.parent, + "rx performance may be slow\n"); + } + + dev->rxinitdone = 1; + return 0; +} + +/* + * Start interface + */ +static int esd_usb2_start(struct esd_usb2_net_priv *priv) +{ + struct esd_usb2 *dev = priv->usb2; + struct net_device *netdev = priv->netdev; + struct esd_usb2_msg *msg; + int err, i; + + msg = kmalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) { + err = -ENOMEM; + goto out; + } + + /* + * Enable all IDs + * The IDADD message takes up to 64 32 bit bitmasks (2048 bits). + * Each bit represents one 11 bit CAN identifier. A set bit + * enables reception of the corresponding CAN identifier. A cleared + * bit disabled this identifier. An additional bitmask value + * following the CAN 2.0A bits is used to enable reception of + * extended CAN frames. Only the LSB of this final mask is checked + * for the complete 29 bit ID range. The IDADD message also allows + * filter configuration for an ID subset. In this case you can add + * the number of the starting bitmask (0..64) to the filter.option + * field followed by only some bitmasks. + */ + msg->msg.hdr.cmd = CMD_IDADD; + msg->msg.hdr.len = 2 + ESD_MAX_ID_SEGMENT; + msg->msg.filter.net = priv->index; + msg->msg.filter.option = ESD_ID_ENABLE; /* start with segment 0 */ + for (i = 0; i < ESD_MAX_ID_SEGMENT; i++) + msg->msg.filter.mask[i] = cpu_to_le32(0xffffffff); + /* enable 29bit extended IDs */ + msg->msg.filter.mask[ESD_MAX_ID_SEGMENT] = cpu_to_le32(0x00000001); + + err = esd_usb2_send_msg(dev, msg); + if (err) + goto out; + + err = esd_usb2_setup_rx_urbs(dev); + if (err) + goto out; + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + +out: + if (err == -ENODEV) + netif_device_detach(netdev); + if (err) + netdev_err(netdev, "couldn't start device: %d\n", err); + + kfree(msg); + return err; +} + +static void unlink_all_urbs(struct esd_usb2 *dev) +{ + struct esd_usb2_net_priv *priv; + int i, j; + + usb_kill_anchored_urbs(&dev->rx_submitted); + + for (i = 0; i < MAX_RX_URBS; ++i) + usb_free_coherent(dev->udev, RX_BUFFER_SIZE, + dev->rxbuf[i], dev->rxbuf_dma[i]); + + for (i = 0; i < dev->net_count; i++) { + priv = dev->nets[i]; + if (priv) { + usb_kill_anchored_urbs(&priv->tx_submitted); + atomic_set(&priv->active_tx_jobs, 0); + + for (j = 0; j < MAX_TX_URBS; j++) + priv->tx_contexts[j].echo_index = MAX_TX_URBS; + } + } +} + +static int esd_usb2_open(struct net_device *netdev) +{ + struct esd_usb2_net_priv *priv = netdev_priv(netdev); + int err; + + /* common open */ + err = open_candev(netdev); + if (err) + return err; + + /* finally start device */ + err = esd_usb2_start(priv); + if (err) { + netdev_warn(netdev, "couldn't start device: %d\n", err); + close_candev(netdev); + return err; + } + + netif_start_queue(netdev); + + return 0; +} + +static netdev_tx_t esd_usb2_start_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + struct esd_usb2_net_priv *priv = netdev_priv(netdev); + struct esd_usb2 *dev = priv->usb2; + struct esd_tx_urb_context *context = NULL; + struct net_device_stats *stats = &netdev->stats; + struct can_frame *cf = (struct can_frame *)skb->data; + struct esd_usb2_msg *msg; + struct urb *urb; + u8 *buf; + int i, err; + int ret = NETDEV_TX_OK; + size_t size = sizeof(struct esd_usb2_msg); + + 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) { + stats->tx_dropped++; + dev_kfree_skb(skb); + goto nourbmem; + } + + buf = usb_alloc_coherent(dev->udev, size, GFP_ATOMIC, + &urb->transfer_dma); + if (!buf) { + netdev_err(netdev, "No memory left for USB buffer\n"); + stats->tx_dropped++; + dev_kfree_skb(skb); + goto nobufmem; + } + + msg = (struct esd_usb2_msg *)buf; + + msg->msg.hdr.len = 3; /* minimal length */ + msg->msg.hdr.cmd = CMD_CAN_TX; + msg->msg.tx.net = priv->index; + msg->msg.tx.dlc = cf->can_dlc; + msg->msg.tx.id = cpu_to_le32(cf->can_id & CAN_ERR_MASK); + + if (cf->can_id & CAN_RTR_FLAG) + msg->msg.tx.dlc |= ESD_RTR; + + if (cf->can_id & CAN_EFF_FLAG) + msg->msg.tx.id |= cpu_to_le32(ESD_EXTID); + + for (i = 0; i < cf->can_dlc; i++) + msg->msg.tx.data[i] = cf->data[i]; + + msg->msg.hdr.len += (cf->can_dlc + 3) >> 2; + + for (i = 0; i < MAX_TX_URBS; i++) { + if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) { + context = &priv->tx_contexts[i]; + break; + } + } + + /* + * This may never happen. + */ + if (!context) { + netdev_warn(netdev, "couldn't find free context\n"); + ret = NETDEV_TX_BUSY; + goto releasebuf; + } + + context->priv = priv; + context->echo_index = i; + context->dlc = cf->can_dlc; + + /* hnd must not be 0 - MSB is stripped in txdone handling */ + msg->msg.tx.hnd = 0x80000000 | i; /* returned in TX done message */ + + usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, 2), buf, + msg->msg.hdr.len << 2, + esd_usb2_write_bulk_callback, context); + + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + usb_anchor_urb(urb, &priv->tx_submitted); + + can_put_echo_skb(skb, netdev, context->echo_index); + + atomic_inc(&priv->active_tx_jobs); + + /* Slow down tx path */ + if (atomic_read(&priv->active_tx_jobs) >= MAX_TX_URBS) + netif_stop_queue(netdev); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err) { + can_free_echo_skb(netdev, context->echo_index); + + atomic_dec(&priv->active_tx_jobs); + usb_unanchor_urb(urb); + + stats->tx_dropped++; + + if (err == -ENODEV) + netif_device_detach(netdev); + else + netdev_warn(netdev, "failed tx_urb %d\n", err); + + goto releasebuf; + } + + netif_trans_update(netdev); + + /* + * Release our reference to this URB, the USB core will eventually free + * it entirely. + */ + usb_free_urb(urb); + + return NETDEV_TX_OK; + +releasebuf: + usb_free_coherent(dev->udev, size, buf, urb->transfer_dma); + +nobufmem: + usb_free_urb(urb); + +nourbmem: + return ret; +} + +static int esd_usb2_close(struct net_device *netdev) +{ + struct esd_usb2_net_priv *priv = netdev_priv(netdev); + struct esd_usb2_msg *msg; + int i; + + msg = kmalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + /* Disable all IDs (see esd_usb2_start()) */ + msg->msg.hdr.cmd = CMD_IDADD; + msg->msg.hdr.len = 2 + ESD_MAX_ID_SEGMENT; + msg->msg.filter.net = priv->index; + msg->msg.filter.option = ESD_ID_ENABLE; /* start with segment 0 */ + for (i = 0; i <= ESD_MAX_ID_SEGMENT; i++) + msg->msg.filter.mask[i] = 0; + if (esd_usb2_send_msg(priv->usb2, msg) < 0) + netdev_err(netdev, "sending idadd message failed\n"); + + /* set CAN controller to reset mode */ + msg->msg.hdr.len = 2; + msg->msg.hdr.cmd = CMD_SETBAUD; + msg->msg.setbaud.net = priv->index; + msg->msg.setbaud.rsvd = 0; + msg->msg.setbaud.baud = cpu_to_le32(ESD_USB2_NO_BAUDRATE); + if (esd_usb2_send_msg(priv->usb2, msg) < 0) + netdev_err(netdev, "sending setbaud message failed\n"); + + priv->can.state = CAN_STATE_STOPPED; + + netif_stop_queue(netdev); + + close_candev(netdev); + + kfree(msg); + + return 0; +} + +static const struct net_device_ops esd_usb2_netdev_ops = { + .ndo_open = esd_usb2_open, + .ndo_stop = esd_usb2_close, + .ndo_start_xmit = esd_usb2_start_xmit, + .ndo_change_mtu = can_change_mtu, +}; + +static const struct can_bittiming_const esd_usb2_bittiming_const = { + .name = "esd_usb2", + .tseg1_min = ESD_USB2_TSEG1_MIN, + .tseg1_max = ESD_USB2_TSEG1_MAX, + .tseg2_min = ESD_USB2_TSEG2_MIN, + .tseg2_max = ESD_USB2_TSEG2_MAX, + .sjw_max = ESD_USB2_SJW_MAX, + .brp_min = ESD_USB2_BRP_MIN, + .brp_max = ESD_USB2_BRP_MAX, + .brp_inc = ESD_USB2_BRP_INC, +}; + +static int esd_usb2_set_bittiming(struct net_device *netdev) +{ + struct esd_usb2_net_priv *priv = netdev_priv(netdev); + struct can_bittiming *bt = &priv->can.bittiming; + struct esd_usb2_msg *msg; + int err; + u32 canbtr; + int sjw_shift; + + canbtr = ESD_USB2_UBR; + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + canbtr |= ESD_USB2_LOM; + + canbtr |= (bt->brp - 1) & (ESD_USB2_BRP_MAX - 1); + + if (le16_to_cpu(priv->usb2->udev->descriptor.idProduct) == + USB_CANUSBM_PRODUCT_ID) + sjw_shift = ESD_USBM_SJW_SHIFT; + else + sjw_shift = ESD_USB2_SJW_SHIFT; + + canbtr |= ((bt->sjw - 1) & (ESD_USB2_SJW_MAX - 1)) + << sjw_shift; + canbtr |= ((bt->prop_seg + bt->phase_seg1 - 1) + & (ESD_USB2_TSEG1_MAX - 1)) + << ESD_USB2_TSEG1_SHIFT; + canbtr |= ((bt->phase_seg2 - 1) & (ESD_USB2_TSEG2_MAX - 1)) + << ESD_USB2_TSEG2_SHIFT; + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + canbtr |= ESD_USB2_3_SAMPLES; + + msg = kmalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + msg->msg.hdr.len = 2; + msg->msg.hdr.cmd = CMD_SETBAUD; + msg->msg.setbaud.net = priv->index; + msg->msg.setbaud.rsvd = 0; + msg->msg.setbaud.baud = cpu_to_le32(canbtr); + + netdev_info(netdev, "setting BTR=%#x\n", canbtr); + + err = esd_usb2_send_msg(priv->usb2, msg); + + kfree(msg); + return err; +} + +static int esd_usb2_get_berr_counter(const struct net_device *netdev, + struct can_berr_counter *bec) +{ + struct esd_usb2_net_priv *priv = netdev_priv(netdev); + + bec->txerr = priv->bec.txerr; + bec->rxerr = priv->bec.rxerr; + + return 0; +} + +static int esd_usb2_set_mode(struct net_device *netdev, enum can_mode mode) +{ + switch (mode) { + case CAN_MODE_START: + netif_wake_queue(netdev); + break; + + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int esd_usb2_probe_one_net(struct usb_interface *intf, int index) +{ + struct esd_usb2 *dev = usb_get_intfdata(intf); + struct net_device *netdev; + struct esd_usb2_net_priv *priv; + int err = 0; + int i; + + netdev = alloc_candev(sizeof(*priv), MAX_TX_URBS); + if (!netdev) { + dev_err(&intf->dev, "couldn't alloc candev\n"); + err = -ENOMEM; + goto done; + } + + priv = netdev_priv(netdev); + + init_usb_anchor(&priv->tx_submitted); + atomic_set(&priv->active_tx_jobs, 0); + + for (i = 0; i < MAX_TX_URBS; i++) + priv->tx_contexts[i].echo_index = MAX_TX_URBS; + + priv->usb2 = dev; + priv->netdev = netdev; + priv->index = index; + + priv->can.state = CAN_STATE_STOPPED; + priv->can.ctrlmode_supported = CAN_CTRLMODE_LISTENONLY; + + if (le16_to_cpu(dev->udev->descriptor.idProduct) == + USB_CANUSBM_PRODUCT_ID) + priv->can.clock.freq = ESD_USBM_CAN_CLOCK; + else { + priv->can.clock.freq = ESD_USB2_CAN_CLOCK; + priv->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES; + } + + priv->can.bittiming_const = &esd_usb2_bittiming_const; + priv->can.do_set_bittiming = esd_usb2_set_bittiming; + priv->can.do_set_mode = esd_usb2_set_mode; + priv->can.do_get_berr_counter = esd_usb2_get_berr_counter; + + netdev->flags |= IFF_ECHO; /* we support local echo */ + + netdev->netdev_ops = &esd_usb2_netdev_ops; + + SET_NETDEV_DEV(netdev, &intf->dev); + netdev->dev_id = index; + + err = register_candev(netdev); + if (err) { + dev_err(&intf->dev, "couldn't register CAN device: %d\n", err); + free_candev(netdev); + err = -ENOMEM; + goto done; + } + + dev->nets[index] = priv; + netdev_info(netdev, "device %s registered\n", netdev->name); + +done: + return err; +} + +/* + * probe function for new USB2 devices + * + * check version information and number of available + * CAN interfaces + */ +static int esd_usb2_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct esd_usb2 *dev; + struct esd_usb2_msg *msg; + int i, err; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + err = -ENOMEM; + goto done; + } + + dev->udev = interface_to_usbdev(intf); + + init_usb_anchor(&dev->rx_submitted); + + usb_set_intfdata(intf, dev); + + msg = kmalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) { + err = -ENOMEM; + goto free_msg; + } + + /* query number of CAN interfaces (nets) */ + msg->msg.hdr.cmd = CMD_VERSION; + msg->msg.hdr.len = 2; + msg->msg.version.rsvd = 0; + msg->msg.version.flags = 0; + msg->msg.version.drv_version = 0; + + err = esd_usb2_send_msg(dev, msg); + if (err < 0) { + dev_err(&intf->dev, "sending version message failed\n"); + goto free_msg; + } + + err = esd_usb2_wait_msg(dev, msg); + if (err < 0) { + dev_err(&intf->dev, "no version message answer\n"); + goto free_msg; + } + + dev->net_count = (int)msg->msg.version_reply.nets; + dev->version = le32_to_cpu(msg->msg.version_reply.version); + + if (device_create_file(&intf->dev, &dev_attr_firmware)) + dev_err(&intf->dev, + "Couldn't create device file for firmware\n"); + + if (device_create_file(&intf->dev, &dev_attr_hardware)) + dev_err(&intf->dev, + "Couldn't create device file for hardware\n"); + + if (device_create_file(&intf->dev, &dev_attr_nets)) + dev_err(&intf->dev, + "Couldn't create device file for nets\n"); + + /* do per device probing */ + for (i = 0; i < dev->net_count; i++) + esd_usb2_probe_one_net(intf, i); + +free_msg: + kfree(msg); + if (err) + kfree(dev); +done: + return err; +} + +/* + * called by the usb core when the device is removed from the system + */ +static void esd_usb2_disconnect(struct usb_interface *intf) +{ + struct esd_usb2 *dev = usb_get_intfdata(intf); + struct net_device *netdev; + int i; + + device_remove_file(&intf->dev, &dev_attr_firmware); + device_remove_file(&intf->dev, &dev_attr_hardware); + device_remove_file(&intf->dev, &dev_attr_nets); + + usb_set_intfdata(intf, NULL); + + if (dev) { + for (i = 0; i < dev->net_count; i++) { + if (dev->nets[i]) { + netdev = dev->nets[i]->netdev; + unregister_netdev(netdev); + free_candev(netdev); + } + } + unlink_all_urbs(dev); + kfree(dev); + } +} + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver esd_usb2_driver = { + .name = "esd_usb2", + .probe = esd_usb2_probe, + .disconnect = esd_usb2_disconnect, + .id_table = esd_usb2_table, +}; + +module_usb_driver(esd_usb2_driver); diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c new file mode 100644 index 000000000..864db200f --- /dev/null +++ b/drivers/net/can/usb/gs_usb.c @@ -0,0 +1,1080 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* CAN driver for Geschwister Schneider USB/CAN devices + * and bytewerk.org candleLight USB CAN interfaces. + * + * Copyright (C) 2013-2016 Geschwister Schneider Technologie-, + * Entwicklungs- und Vertriebs UG (Haftungsbeschränkt). + * Copyright (C) 2016 Hubert Denkmair + * + * Many thanks to all socketcan devs! + */ + +#include <linux/init.h> +#include <linux/signal.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/usb.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> + +/* Device specific constants */ +#define USB_GSUSB_1_VENDOR_ID 0x1d50 +#define USB_GSUSB_1_PRODUCT_ID 0x606f + +#define USB_CANDLELIGHT_VENDOR_ID 0x1209 +#define USB_CANDLELIGHT_PRODUCT_ID 0x2323 + +#define GSUSB_ENDPOINT_IN 1 +#define GSUSB_ENDPOINT_OUT 2 + +/* Device specific constants */ +enum gs_usb_breq { + GS_USB_BREQ_HOST_FORMAT = 0, + GS_USB_BREQ_BITTIMING, + GS_USB_BREQ_MODE, + GS_USB_BREQ_BERR, + GS_USB_BREQ_BT_CONST, + GS_USB_BREQ_DEVICE_CONFIG, + GS_USB_BREQ_TIMESTAMP, + GS_USB_BREQ_IDENTIFY, +}; + +enum gs_can_mode { + /* reset a channel. turns it off */ + GS_CAN_MODE_RESET = 0, + /* starts a channel */ + GS_CAN_MODE_START +}; + +enum gs_can_state { + GS_CAN_STATE_ERROR_ACTIVE = 0, + GS_CAN_STATE_ERROR_WARNING, + GS_CAN_STATE_ERROR_PASSIVE, + GS_CAN_STATE_BUS_OFF, + GS_CAN_STATE_STOPPED, + GS_CAN_STATE_SLEEPING +}; + +enum gs_can_identify_mode { + GS_CAN_IDENTIFY_OFF = 0, + GS_CAN_IDENTIFY_ON +}; + +/* data types passed between host and device */ + +/* The firmware on the original USB2CAN by Geschwister Schneider + * Technologie Entwicklungs- und Vertriebs UG exchanges all data + * between the host and the device in host byte order. This is done + * with the struct gs_host_config::byte_order member, which is sent + * first to indicate the desired byte order. + * + * The widely used open source firmware candleLight doesn't support + * this feature and exchanges the data in little endian byte order. + */ +struct gs_host_config { + __le32 byte_order; +} __packed; + +struct gs_device_config { + u8 reserved1; + u8 reserved2; + u8 reserved3; + u8 icount; + __le32 sw_version; + __le32 hw_version; +} __packed; + +#define GS_CAN_MODE_NORMAL 0 +#define GS_CAN_MODE_LISTEN_ONLY BIT(0) +#define GS_CAN_MODE_LOOP_BACK BIT(1) +#define GS_CAN_MODE_TRIPLE_SAMPLE BIT(2) +#define GS_CAN_MODE_ONE_SHOT BIT(3) + +struct gs_device_mode { + __le32 mode; + __le32 flags; +} __packed; + +struct gs_device_state { + __le32 state; + __le32 rxerr; + __le32 txerr; +} __packed; + +struct gs_device_bittiming { + __le32 prop_seg; + __le32 phase_seg1; + __le32 phase_seg2; + __le32 sjw; + __le32 brp; +} __packed; + +struct gs_identify_mode { + __le32 mode; +} __packed; + +#define GS_CAN_FEATURE_LISTEN_ONLY BIT(0) +#define GS_CAN_FEATURE_LOOP_BACK BIT(1) +#define GS_CAN_FEATURE_TRIPLE_SAMPLE BIT(2) +#define GS_CAN_FEATURE_ONE_SHOT BIT(3) +#define GS_CAN_FEATURE_HW_TIMESTAMP BIT(4) +#define GS_CAN_FEATURE_IDENTIFY BIT(5) + +struct gs_device_bt_const { + __le32 feature; + __le32 fclk_can; + __le32 tseg1_min; + __le32 tseg1_max; + __le32 tseg2_min; + __le32 tseg2_max; + __le32 sjw_max; + __le32 brp_min; + __le32 brp_max; + __le32 brp_inc; +} __packed; + +#define GS_CAN_FLAG_OVERFLOW 1 + +struct gs_host_frame { + u32 echo_id; + __le32 can_id; + + u8 can_dlc; + u8 channel; + u8 flags; + u8 reserved; + + u8 data[8]; +} __packed; +/* The GS USB devices make use of the same flags and masks as in + * linux/can.h and linux/can/error.h, and no additional mapping is necessary. + */ + +/* Only send a max of GS_MAX_TX_URBS frames per channel at a time. */ +#define GS_MAX_TX_URBS 10 +/* Only launch a max of GS_MAX_RX_URBS usb requests at a time. */ +#define GS_MAX_RX_URBS 30 +/* Maximum number of interfaces the driver supports per device. + * Current hardware only supports 2 interfaces. The future may vary. + */ +#define GS_MAX_INTF 2 + +struct gs_tx_context { + struct gs_can *dev; + unsigned int echo_id; +}; + +struct gs_can { + struct can_priv can; /* must be the first member */ + + struct gs_usb *parent; + + struct net_device *netdev; + struct usb_device *udev; + struct usb_interface *iface; + + struct can_bittiming_const bt_const; + unsigned int channel; /* channel number */ + + /* This lock prevents a race condition between xmit and receive. */ + spinlock_t tx_ctx_lock; + struct gs_tx_context tx_context[GS_MAX_TX_URBS]; + + struct usb_anchor tx_submitted; + atomic_t active_tx_urbs; + void *rxbuf[GS_MAX_RX_URBS]; + dma_addr_t rxbuf_dma[GS_MAX_RX_URBS]; +}; + +/* usb interface struct */ +struct gs_usb { + struct gs_can *canch[GS_MAX_INTF]; + struct usb_anchor rx_submitted; + struct usb_device *udev; + u8 active_channels; +}; + +/* 'allocate' a tx context. + * returns a valid tx context or NULL if there is no space. + */ +static struct gs_tx_context *gs_alloc_tx_context(struct gs_can *dev) +{ + int i = 0; + unsigned long flags; + + spin_lock_irqsave(&dev->tx_ctx_lock, flags); + + for (; i < GS_MAX_TX_URBS; i++) { + if (dev->tx_context[i].echo_id == GS_MAX_TX_URBS) { + dev->tx_context[i].echo_id = i; + spin_unlock_irqrestore(&dev->tx_ctx_lock, flags); + return &dev->tx_context[i]; + } + } + + spin_unlock_irqrestore(&dev->tx_ctx_lock, flags); + return NULL; +} + +/* releases a tx context + */ +static void gs_free_tx_context(struct gs_tx_context *txc) +{ + txc->echo_id = GS_MAX_TX_URBS; +} + +/* Get a tx context by id. + */ +static struct gs_tx_context *gs_get_tx_context(struct gs_can *dev, + unsigned int id) +{ + unsigned long flags; + + if (id < GS_MAX_TX_URBS) { + spin_lock_irqsave(&dev->tx_ctx_lock, flags); + if (dev->tx_context[id].echo_id == id) { + spin_unlock_irqrestore(&dev->tx_ctx_lock, flags); + return &dev->tx_context[id]; + } + spin_unlock_irqrestore(&dev->tx_ctx_lock, flags); + } + return NULL; +} + +static int gs_cmd_reset(struct gs_can *gsdev) +{ + struct gs_device_mode *dm; + struct usb_interface *intf = gsdev->iface; + int rc; + + dm = kzalloc(sizeof(*dm), GFP_KERNEL); + if (!dm) + return -ENOMEM; + + dm->mode = GS_CAN_MODE_RESET; + + rc = usb_control_msg(interface_to_usbdev(intf), + usb_sndctrlpipe(interface_to_usbdev(intf), 0), + GS_USB_BREQ_MODE, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + gsdev->channel, + 0, + dm, + sizeof(*dm), + 1000); + + kfree(dm); + + return rc; +} + +static void gs_update_state(struct gs_can *dev, struct can_frame *cf) +{ + struct can_device_stats *can_stats = &dev->can.can_stats; + + if (cf->can_id & CAN_ERR_RESTARTED) { + dev->can.state = CAN_STATE_ERROR_ACTIVE; + can_stats->restarts++; + } else if (cf->can_id & CAN_ERR_BUSOFF) { + dev->can.state = CAN_STATE_BUS_OFF; + can_stats->bus_off++; + } else if (cf->can_id & CAN_ERR_CRTL) { + if ((cf->data[1] & CAN_ERR_CRTL_TX_WARNING) || + (cf->data[1] & CAN_ERR_CRTL_RX_WARNING)) { + dev->can.state = CAN_STATE_ERROR_WARNING; + can_stats->error_warning++; + } else if ((cf->data[1] & CAN_ERR_CRTL_TX_PASSIVE) || + (cf->data[1] & CAN_ERR_CRTL_RX_PASSIVE)) { + dev->can.state = CAN_STATE_ERROR_PASSIVE; + can_stats->error_passive++; + } else { + dev->can.state = CAN_STATE_ERROR_ACTIVE; + } + } +} + +static void gs_usb_receive_bulk_callback(struct urb *urb) +{ + struct gs_usb *usbcan = urb->context; + struct gs_can *dev; + struct net_device *netdev; + int rc; + struct net_device_stats *stats; + struct gs_host_frame *hf = urb->transfer_buffer; + struct gs_tx_context *txc; + struct can_frame *cf; + struct sk_buff *skb; + + BUG_ON(!usbcan); + + switch (urb->status) { + case 0: /* success */ + break; + case -ENOENT: + case -ESHUTDOWN: + return; + default: + /* do not resubmit aborted urbs. eg: when device goes down */ + return; + } + + /* device reports out of range channel id */ + if (hf->channel >= GS_MAX_INTF) + goto device_detach; + + dev = usbcan->canch[hf->channel]; + + netdev = dev->netdev; + stats = &netdev->stats; + + if (!netif_device_present(netdev)) + return; + + if (hf->echo_id == -1) { /* normal rx */ + skb = alloc_can_skb(dev->netdev, &cf); + if (!skb) + return; + + cf->can_id = le32_to_cpu(hf->can_id); + + cf->can_dlc = get_can_dlc(hf->can_dlc); + memcpy(cf->data, hf->data, 8); + + /* ERROR frames tell us information about the controller */ + if (le32_to_cpu(hf->can_id) & CAN_ERR_FLAG) + gs_update_state(dev, cf); + + netdev->stats.rx_packets++; + netdev->stats.rx_bytes += hf->can_dlc; + + netif_rx(skb); + } else { /* echo_id == hf->echo_id */ + if (hf->echo_id >= GS_MAX_TX_URBS) { + netdev_err(netdev, + "Unexpected out of range echo id %d\n", + hf->echo_id); + goto resubmit_urb; + } + + netdev->stats.tx_packets++; + netdev->stats.tx_bytes += hf->can_dlc; + + txc = gs_get_tx_context(dev, hf->echo_id); + + /* bad devices send bad echo_ids. */ + if (!txc) { + netdev_err(netdev, + "Unexpected unused echo id %d\n", + hf->echo_id); + goto resubmit_urb; + } + + can_get_echo_skb(netdev, hf->echo_id); + + gs_free_tx_context(txc); + + atomic_dec(&dev->active_tx_urbs); + + netif_wake_queue(netdev); + } + + if (hf->flags & GS_CAN_FLAG_OVERFLOW) { + stats->rx_over_errors++; + stats->rx_errors++; + + skb = alloc_can_err_skb(netdev, &cf); + if (!skb) + goto resubmit_urb; + + cf->can_id |= CAN_ERR_CRTL; + cf->can_dlc = CAN_ERR_DLC; + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + netif_rx(skb); + } + + resubmit_urb: + usb_fill_bulk_urb(urb, + usbcan->udev, + usb_rcvbulkpipe(usbcan->udev, GSUSB_ENDPOINT_IN), + hf, + sizeof(struct gs_host_frame), + gs_usb_receive_bulk_callback, + usbcan + ); + + rc = usb_submit_urb(urb, GFP_ATOMIC); + + /* USB failure take down all interfaces */ + if (rc == -ENODEV) { + device_detach: + for (rc = 0; rc < GS_MAX_INTF; rc++) { + if (usbcan->canch[rc]) + netif_device_detach(usbcan->canch[rc]->netdev); + } + } +} + +static int gs_usb_set_bittiming(struct net_device *netdev) +{ + struct gs_can *dev = netdev_priv(netdev); + struct can_bittiming *bt = &dev->can.bittiming; + struct usb_interface *intf = dev->iface; + int rc; + struct gs_device_bittiming *dbt; + + dbt = kmalloc(sizeof(*dbt), GFP_KERNEL); + if (!dbt) + return -ENOMEM; + + dbt->prop_seg = cpu_to_le32(bt->prop_seg); + dbt->phase_seg1 = cpu_to_le32(bt->phase_seg1); + dbt->phase_seg2 = cpu_to_le32(bt->phase_seg2); + dbt->sjw = cpu_to_le32(bt->sjw); + dbt->brp = cpu_to_le32(bt->brp); + + /* request bit timings */ + rc = usb_control_msg(interface_to_usbdev(intf), + usb_sndctrlpipe(interface_to_usbdev(intf), 0), + GS_USB_BREQ_BITTIMING, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + dev->channel, + 0, + dbt, + sizeof(*dbt), + 1000); + + kfree(dbt); + + if (rc < 0) + dev_err(netdev->dev.parent, "Couldn't set bittimings (err=%d)", + rc); + + return (rc > 0) ? 0 : rc; +} + +static void gs_usb_xmit_callback(struct urb *urb) +{ + struct gs_tx_context *txc = urb->context; + struct gs_can *dev = txc->dev; + struct net_device *netdev = dev->netdev; + + if (urb->status) + netdev_info(netdev, "usb xmit fail %d\n", txc->echo_id); + + usb_free_coherent(urb->dev, + urb->transfer_buffer_length, + urb->transfer_buffer, + urb->transfer_dma); +} + +static netdev_tx_t gs_can_start_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + struct gs_can *dev = netdev_priv(netdev); + struct net_device_stats *stats = &dev->netdev->stats; + struct urb *urb; + struct gs_host_frame *hf; + struct can_frame *cf; + int rc; + unsigned int idx; + struct gs_tx_context *txc; + + if (can_dropped_invalid_skb(netdev, skb)) + return NETDEV_TX_OK; + + /* find an empty context to keep track of transmission */ + txc = gs_alloc_tx_context(dev); + if (!txc) + return NETDEV_TX_BUSY; + + /* create a URB, and a buffer for it */ + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) + goto nomem_urb; + + hf = usb_alloc_coherent(dev->udev, sizeof(*hf), GFP_ATOMIC, + &urb->transfer_dma); + if (!hf) { + netdev_err(netdev, "No memory left for USB buffer\n"); + goto nomem_hf; + } + + idx = txc->echo_id; + + if (idx >= GS_MAX_TX_URBS) { + netdev_err(netdev, "Invalid tx context %d\n", idx); + goto badidx; + } + + hf->echo_id = idx; + hf->channel = dev->channel; + hf->flags = 0; + hf->reserved = 0; + + cf = (struct can_frame *)skb->data; + + hf->can_id = cpu_to_le32(cf->can_id); + hf->can_dlc = cf->can_dlc; + memcpy(hf->data, cf->data, cf->can_dlc); + + usb_fill_bulk_urb(urb, dev->udev, + usb_sndbulkpipe(dev->udev, GSUSB_ENDPOINT_OUT), + hf, + sizeof(*hf), + gs_usb_xmit_callback, + txc); + + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(urb, &dev->tx_submitted); + + can_put_echo_skb(skb, netdev, idx); + + atomic_inc(&dev->active_tx_urbs); + + rc = usb_submit_urb(urb, GFP_ATOMIC); + if (unlikely(rc)) { /* usb send failed */ + atomic_dec(&dev->active_tx_urbs); + + can_free_echo_skb(netdev, idx); + gs_free_tx_context(txc); + + usb_unanchor_urb(urb); + usb_free_coherent(dev->udev, + sizeof(*hf), + hf, + urb->transfer_dma); + + if (rc == -ENODEV) { + netif_device_detach(netdev); + } else { + netdev_err(netdev, "usb_submit failed (err=%d)\n", rc); + stats->tx_dropped++; + } + } else { + /* Slow down tx path */ + if (atomic_read(&dev->active_tx_urbs) >= GS_MAX_TX_URBS) + netif_stop_queue(netdev); + } + + /* let usb core take care of this urb */ + usb_free_urb(urb); + + return NETDEV_TX_OK; + + badidx: + usb_free_coherent(dev->udev, + sizeof(*hf), + hf, + urb->transfer_dma); + nomem_hf: + usb_free_urb(urb); + + nomem_urb: + gs_free_tx_context(txc); + dev_kfree_skb(skb); + stats->tx_dropped++; + return NETDEV_TX_OK; +} + +static int gs_can_open(struct net_device *netdev) +{ + struct gs_can *dev = netdev_priv(netdev); + struct gs_usb *parent = dev->parent; + int rc, i; + struct gs_device_mode *dm; + u32 ctrlmode; + u32 flags = 0; + + rc = open_candev(netdev); + if (rc) + return rc; + + if (!parent->active_channels) { + for (i = 0; i < GS_MAX_RX_URBS; i++) { + struct urb *urb; + u8 *buf; + dma_addr_t buf_dma; + + /* alloc rx urb */ + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + return -ENOMEM; + + /* alloc rx buffer */ + buf = usb_alloc_coherent(dev->udev, + sizeof(struct gs_host_frame), + GFP_KERNEL, + &buf_dma); + if (!buf) { + netdev_err(netdev, + "No memory left for USB buffer\n"); + usb_free_urb(urb); + return -ENOMEM; + } + + urb->transfer_dma = buf_dma; + + /* fill, anchor, and submit rx urb */ + usb_fill_bulk_urb(urb, + dev->udev, + usb_rcvbulkpipe(dev->udev, + GSUSB_ENDPOINT_IN), + buf, + sizeof(struct gs_host_frame), + gs_usb_receive_bulk_callback, + parent); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + usb_anchor_urb(urb, &parent->rx_submitted); + + rc = usb_submit_urb(urb, GFP_KERNEL); + if (rc) { + if (rc == -ENODEV) + netif_device_detach(dev->netdev); + + netdev_err(netdev, + "usb_submit failed (err=%d)\n", + rc); + + usb_unanchor_urb(urb); + usb_free_coherent(dev->udev, + sizeof(struct gs_host_frame), + buf, + buf_dma); + usb_free_urb(urb); + break; + } + + dev->rxbuf[i] = buf; + dev->rxbuf_dma[i] = buf_dma; + + /* Drop reference, + * USB core will take care of freeing it + */ + usb_free_urb(urb); + } + } + + dm = kmalloc(sizeof(*dm), GFP_KERNEL); + if (!dm) + return -ENOMEM; + + /* flags */ + ctrlmode = dev->can.ctrlmode; + + if (ctrlmode & CAN_CTRLMODE_LOOPBACK) + flags |= GS_CAN_MODE_LOOP_BACK; + else if (ctrlmode & CAN_CTRLMODE_LISTENONLY) + flags |= GS_CAN_MODE_LISTEN_ONLY; + + /* Controller is not allowed to retry TX + * this mode is unavailable on atmels uc3c hardware + */ + if (ctrlmode & CAN_CTRLMODE_ONE_SHOT) + flags |= GS_CAN_MODE_ONE_SHOT; + + if (ctrlmode & CAN_CTRLMODE_3_SAMPLES) + flags |= GS_CAN_MODE_TRIPLE_SAMPLE; + + /* finally start device */ + dev->can.state = CAN_STATE_ERROR_ACTIVE; + dm->mode = cpu_to_le32(GS_CAN_MODE_START); + dm->flags = cpu_to_le32(flags); + rc = usb_control_msg(interface_to_usbdev(dev->iface), + usb_sndctrlpipe(interface_to_usbdev(dev->iface), 0), + GS_USB_BREQ_MODE, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE, + dev->channel, + 0, + dm, + sizeof(*dm), + 1000); + + if (rc < 0) { + netdev_err(netdev, "Couldn't start device (err=%d)\n", rc); + kfree(dm); + dev->can.state = CAN_STATE_STOPPED; + return rc; + } + + kfree(dm); + + parent->active_channels++; + if (!(dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)) + netif_start_queue(netdev); + + return 0; +} + +static int gs_can_close(struct net_device *netdev) +{ + int rc; + struct gs_can *dev = netdev_priv(netdev); + struct gs_usb *parent = dev->parent; + unsigned int i; + + netif_stop_queue(netdev); + + /* Stop polling */ + parent->active_channels--; + if (!parent->active_channels) { + usb_kill_anchored_urbs(&parent->rx_submitted); + for (i = 0; i < GS_MAX_RX_URBS; i++) + usb_free_coherent(dev->udev, + sizeof(struct gs_host_frame), + dev->rxbuf[i], + dev->rxbuf_dma[i]); + } + + /* Stop sending URBs */ + usb_kill_anchored_urbs(&dev->tx_submitted); + atomic_set(&dev->active_tx_urbs, 0); + + dev->can.state = CAN_STATE_STOPPED; + + /* reset the device */ + rc = gs_cmd_reset(dev); + if (rc < 0) + netdev_warn(netdev, "Couldn't shutdown device (err=%d)", rc); + + /* reset tx contexts */ + for (rc = 0; rc < GS_MAX_TX_URBS; rc++) { + dev->tx_context[rc].dev = dev; + dev->tx_context[rc].echo_id = GS_MAX_TX_URBS; + } + + /* close the netdev */ + close_candev(netdev); + + return 0; +} + +static const struct net_device_ops gs_usb_netdev_ops = { + .ndo_open = gs_can_open, + .ndo_stop = gs_can_close, + .ndo_start_xmit = gs_can_start_xmit, + .ndo_change_mtu = can_change_mtu, +}; + +static int gs_usb_set_identify(struct net_device *netdev, bool do_identify) +{ + struct gs_can *dev = netdev_priv(netdev); + struct gs_identify_mode *imode; + int rc; + + imode = kmalloc(sizeof(*imode), GFP_KERNEL); + + if (!imode) + return -ENOMEM; + + if (do_identify) + imode->mode = cpu_to_le32(GS_CAN_IDENTIFY_ON); + else + imode->mode = cpu_to_le32(GS_CAN_IDENTIFY_OFF); + + rc = usb_control_msg(interface_to_usbdev(dev->iface), + usb_sndctrlpipe(interface_to_usbdev(dev->iface), + 0), + GS_USB_BREQ_IDENTIFY, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE, + dev->channel, + 0, + imode, + sizeof(*imode), + 100); + + kfree(imode); + + return (rc > 0) ? 0 : rc; +} + +/* blink LED's for finding the this interface */ +static int gs_usb_set_phys_id(struct net_device *dev, + enum ethtool_phys_id_state state) +{ + int rc = 0; + + switch (state) { + case ETHTOOL_ID_ACTIVE: + rc = gs_usb_set_identify(dev, GS_CAN_IDENTIFY_ON); + break; + case ETHTOOL_ID_INACTIVE: + rc = gs_usb_set_identify(dev, GS_CAN_IDENTIFY_OFF); + break; + default: + break; + } + + return rc; +} + +static const struct ethtool_ops gs_usb_ethtool_ops = { + .set_phys_id = gs_usb_set_phys_id, +}; + +static struct gs_can *gs_make_candev(unsigned int channel, + struct usb_interface *intf, + struct gs_device_config *dconf) +{ + struct gs_can *dev; + struct net_device *netdev; + int rc; + struct gs_device_bt_const *bt_const; + u32 feature; + + bt_const = kmalloc(sizeof(*bt_const), GFP_KERNEL); + if (!bt_const) + return ERR_PTR(-ENOMEM); + + /* fetch bit timing constants */ + rc = usb_control_msg(interface_to_usbdev(intf), + usb_rcvctrlpipe(interface_to_usbdev(intf), 0), + GS_USB_BREQ_BT_CONST, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + channel, + 0, + bt_const, + sizeof(*bt_const), + 1000); + + if (rc < 0) { + dev_err(&intf->dev, + "Couldn't get bit timing const for channel (err=%d)\n", + rc); + kfree(bt_const); + return ERR_PTR(rc); + } + + /* create netdev */ + netdev = alloc_candev(sizeof(struct gs_can), GS_MAX_TX_URBS); + if (!netdev) { + dev_err(&intf->dev, "Couldn't allocate candev\n"); + kfree(bt_const); + return ERR_PTR(-ENOMEM); + } + + dev = netdev_priv(netdev); + + netdev->netdev_ops = &gs_usb_netdev_ops; + + netdev->flags |= IFF_ECHO; /* we support full roundtrip echo */ + + /* dev setup */ + strcpy(dev->bt_const.name, "gs_usb"); + dev->bt_const.tseg1_min = le32_to_cpu(bt_const->tseg1_min); + dev->bt_const.tseg1_max = le32_to_cpu(bt_const->tseg1_max); + dev->bt_const.tseg2_min = le32_to_cpu(bt_const->tseg2_min); + dev->bt_const.tseg2_max = le32_to_cpu(bt_const->tseg2_max); + dev->bt_const.sjw_max = le32_to_cpu(bt_const->sjw_max); + dev->bt_const.brp_min = le32_to_cpu(bt_const->brp_min); + dev->bt_const.brp_max = le32_to_cpu(bt_const->brp_max); + dev->bt_const.brp_inc = le32_to_cpu(bt_const->brp_inc); + + dev->udev = interface_to_usbdev(intf); + dev->iface = intf; + dev->netdev = netdev; + dev->channel = channel; + + init_usb_anchor(&dev->tx_submitted); + atomic_set(&dev->active_tx_urbs, 0); + spin_lock_init(&dev->tx_ctx_lock); + for (rc = 0; rc < GS_MAX_TX_URBS; rc++) { + dev->tx_context[rc].dev = dev; + dev->tx_context[rc].echo_id = GS_MAX_TX_URBS; + } + + /* can setup */ + dev->can.state = CAN_STATE_STOPPED; + dev->can.clock.freq = le32_to_cpu(bt_const->fclk_can); + dev->can.bittiming_const = &dev->bt_const; + dev->can.do_set_bittiming = gs_usb_set_bittiming; + + dev->can.ctrlmode_supported = 0; + + feature = le32_to_cpu(bt_const->feature); + if (feature & GS_CAN_FEATURE_LISTEN_ONLY) + dev->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY; + + if (feature & GS_CAN_FEATURE_LOOP_BACK) + dev->can.ctrlmode_supported |= CAN_CTRLMODE_LOOPBACK; + + if (feature & GS_CAN_FEATURE_TRIPLE_SAMPLE) + dev->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES; + + if (feature & GS_CAN_FEATURE_ONE_SHOT) + dev->can.ctrlmode_supported |= CAN_CTRLMODE_ONE_SHOT; + + SET_NETDEV_DEV(netdev, &intf->dev); + + if (le32_to_cpu(dconf->sw_version) > 1) + if (feature & GS_CAN_FEATURE_IDENTIFY) + netdev->ethtool_ops = &gs_usb_ethtool_ops; + + kfree(bt_const); + + rc = register_candev(dev->netdev); + if (rc) { + free_candev(dev->netdev); + dev_err(&intf->dev, "Couldn't register candev (err=%d)\n", rc); + return ERR_PTR(rc); + } + + return dev; +} + +static void gs_destroy_candev(struct gs_can *dev) +{ + unregister_candev(dev->netdev); + usb_kill_anchored_urbs(&dev->tx_submitted); + free_candev(dev->netdev); +} + +static int gs_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct gs_usb *dev; + int rc = -ENOMEM; + unsigned int icount, i; + struct gs_host_config *hconf; + struct gs_device_config *dconf; + + hconf = kmalloc(sizeof(*hconf), GFP_KERNEL); + if (!hconf) + return -ENOMEM; + + hconf->byte_order = cpu_to_le32(0x0000beef); + + /* send host config */ + rc = usb_control_msg(interface_to_usbdev(intf), + usb_sndctrlpipe(interface_to_usbdev(intf), 0), + GS_USB_BREQ_HOST_FORMAT, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 1, + intf->cur_altsetting->desc.bInterfaceNumber, + hconf, + sizeof(*hconf), + 1000); + + kfree(hconf); + + if (rc < 0) { + dev_err(&intf->dev, "Couldn't send data format (err=%d)\n", + rc); + return rc; + } + + dconf = kmalloc(sizeof(*dconf), GFP_KERNEL); + if (!dconf) + return -ENOMEM; + + /* read device config */ + rc = usb_control_msg(interface_to_usbdev(intf), + usb_rcvctrlpipe(interface_to_usbdev(intf), 0), + GS_USB_BREQ_DEVICE_CONFIG, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 1, + intf->cur_altsetting->desc.bInterfaceNumber, + dconf, + sizeof(*dconf), + 1000); + if (rc < 0) { + dev_err(&intf->dev, "Couldn't get device config: (err=%d)\n", + rc); + kfree(dconf); + return rc; + } + + icount = dconf->icount + 1; + dev_info(&intf->dev, "Configuring for %d interfaces\n", icount); + + if (icount > GS_MAX_INTF) { + dev_err(&intf->dev, + "Driver cannot handle more that %d CAN interfaces\n", + GS_MAX_INTF); + kfree(dconf); + return -EINVAL; + } + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + kfree(dconf); + return -ENOMEM; + } + + init_usb_anchor(&dev->rx_submitted); + + usb_set_intfdata(intf, dev); + dev->udev = interface_to_usbdev(intf); + + for (i = 0; i < icount; i++) { + dev->canch[i] = gs_make_candev(i, intf, dconf); + if (IS_ERR_OR_NULL(dev->canch[i])) { + /* save error code to return later */ + rc = PTR_ERR(dev->canch[i]); + + /* on failure destroy previously created candevs */ + icount = i; + for (i = 0; i < icount; i++) + gs_destroy_candev(dev->canch[i]); + + usb_kill_anchored_urbs(&dev->rx_submitted); + kfree(dconf); + kfree(dev); + return rc; + } + dev->canch[i]->parent = dev; + } + + kfree(dconf); + + return 0; +} + +static void gs_usb_disconnect(struct usb_interface *intf) +{ + unsigned i; + struct gs_usb *dev = usb_get_intfdata(intf); + usb_set_intfdata(intf, NULL); + + if (!dev) { + dev_err(&intf->dev, "Disconnect (nodata)\n"); + return; + } + + for (i = 0; i < GS_MAX_INTF; i++) + if (dev->canch[i]) + gs_destroy_candev(dev->canch[i]); + + usb_kill_anchored_urbs(&dev->rx_submitted); + kfree(dev); +} + +static const struct usb_device_id gs_usb_table[] = { + { USB_DEVICE_INTERFACE_NUMBER(USB_GSUSB_1_VENDOR_ID, + USB_GSUSB_1_PRODUCT_ID, 0) }, + { USB_DEVICE_INTERFACE_NUMBER(USB_CANDLELIGHT_VENDOR_ID, + USB_CANDLELIGHT_PRODUCT_ID, 0) }, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, gs_usb_table); + +static struct usb_driver gs_usb_driver = { + .name = "gs_usb", + .probe = gs_usb_probe, + .disconnect = gs_usb_disconnect, + .id_table = gs_usb_table, +}; + +module_usb_driver(gs_usb_driver); + +MODULE_AUTHOR("Maximilian Schneider <mws@schneidersoft.net>"); +MODULE_DESCRIPTION( +"Socket CAN device driver for Geschwister Schneider Technologie-, " +"Entwicklungs- und Vertriebs UG. USB2.0 to CAN interfaces\n" +"and bytewerk.org candleLight USB CAN interfaces."); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/can/usb/kvaser_usb/Makefile b/drivers/net/can/usb/kvaser_usb/Makefile new file mode 100644 index 000000000..cf260044f --- /dev/null +++ b/drivers/net/can/usb/kvaser_usb/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o +kvaser_usb-y = kvaser_usb_core.o kvaser_usb_leaf.o kvaser_usb_hydra.o diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb.h b/drivers/net/can/usb/kvaser_usb/kvaser_usb.h new file mode 100644 index 000000000..5699531f8 --- /dev/null +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb.h @@ -0,0 +1,219 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Parts of this driver are based on the following: + * - Kvaser linux leaf driver (version 4.78) + * - CAN driver for esd CAN-USB/2 + * - Kvaser linux usbcanII driver (version 5.3) + * - Kvaser linux mhydra driver (version 5.24) + * + * Copyright (C) 2002-2018 KVASER AB, Sweden. All rights reserved. + * Copyright (C) 2010 Matthias Fuchs <matthias.fuchs@esd.eu>, esd gmbh + * Copyright (C) 2012 Olivier Sobrie <olivier@sobrie.be> + * Copyright (C) 2015 Valeo S.A. + */ + +#ifndef KVASER_USB_H +#define KVASER_USB_H + +/* Kvaser USB CAN dongles are divided into three major platforms: + * - Hydra: Running firmware labeled as 'mhydra' + * - Leaf: Based on Renesas M32C or Freescale i.MX28, running firmware labeled + * as 'filo' + * - UsbcanII: Based on Renesas M16C, running firmware labeled as 'helios' + */ + +#include <linux/completion.h> +#include <linux/spinlock.h> +#include <linux/types.h> +#include <linux/usb.h> + +#include <linux/can.h> +#include <linux/can/dev.h> + +#define KVASER_USB_MAX_RX_URBS 4 +#define KVASER_USB_MAX_TX_URBS 128 +#define KVASER_USB_TIMEOUT 1000 /* msecs */ +#define KVASER_USB_RX_BUFFER_SIZE 3072 +#define KVASER_USB_MAX_NET_DEVICES 5 + +/* Kvaser USB device quirks */ +#define KVASER_USB_QUIRK_HAS_SILENT_MODE BIT(0) +#define KVASER_USB_QUIRK_HAS_TXRX_ERRORS BIT(1) +#define KVASER_USB_QUIRK_IGNORE_CLK_FREQ BIT(2) + +/* Device capabilities */ +#define KVASER_USB_CAP_BERR_CAP 0x01 +#define KVASER_USB_CAP_EXT_CAP 0x02 +#define KVASER_USB_HYDRA_CAP_EXT_CMD 0x04 + +struct kvaser_usb_dev_cfg; + +enum kvaser_usb_leaf_family { + KVASER_LEAF, + KVASER_USBCAN, +}; + +#define KVASER_USB_HYDRA_MAX_CMD_LEN 128 +struct kvaser_usb_dev_card_data_hydra { + u8 channel_to_he[KVASER_USB_MAX_NET_DEVICES]; + u8 sysdbg_he; + spinlock_t transid_lock; /* lock for transid */ + u16 transid; + /* lock for usb_rx_leftover and usb_rx_leftover_len */ + spinlock_t usb_rx_leftover_lock; + u8 usb_rx_leftover[KVASER_USB_HYDRA_MAX_CMD_LEN]; + u8 usb_rx_leftover_len; +}; +struct kvaser_usb_dev_card_data { + u32 ctrlmode_supported; + u32 capabilities; + struct kvaser_usb_dev_card_data_hydra hydra; +}; + +/* Context for an outstanding, not yet ACKed, transmission */ +struct kvaser_usb_tx_urb_context { + struct kvaser_usb_net_priv *priv; + u32 echo_index; + int dlc; +}; + +struct kvaser_usb_busparams { + __le32 bitrate; + u8 tseg1; + u8 tseg2; + u8 sjw; + u8 nsamples; +} __packed; + +struct kvaser_usb { + struct usb_device *udev; + struct usb_interface *intf; + struct kvaser_usb_net_priv *nets[KVASER_USB_MAX_NET_DEVICES]; + const struct kvaser_usb_driver_info *driver_info; + const struct kvaser_usb_dev_cfg *cfg; + + struct usb_endpoint_descriptor *bulk_in, *bulk_out; + struct usb_anchor rx_submitted; + + /* @max_tx_urbs: Firmware-reported maximum number of outstanding, + * not yet ACKed, transmissions on this device. This value is + * also used as a sentinel for marking free tx contexts. + */ + u32 fw_version; + unsigned int nchannels; + unsigned int max_tx_urbs; + struct kvaser_usb_dev_card_data card_data; + + bool rxinitdone; + void *rxbuf[KVASER_USB_MAX_RX_URBS]; + dma_addr_t rxbuf_dma[KVASER_USB_MAX_RX_URBS]; +}; + +struct kvaser_usb_net_priv { + struct can_priv can; + struct can_berr_counter bec; + + /* subdriver-specific data */ + void *sub_priv; + + struct kvaser_usb *dev; + struct net_device *netdev; + int channel; + + struct completion start_comp, stop_comp, flush_comp, + get_busparams_comp; + struct usb_anchor tx_submitted; + + struct kvaser_usb_busparams busparams_nominal, busparams_data; + + spinlock_t tx_contexts_lock; /* lock for active_tx_contexts */ + int active_tx_contexts; + struct kvaser_usb_tx_urb_context tx_contexts[]; +}; + +/** + * struct kvaser_usb_dev_ops - Device specific functions + * @dev_set_mode: used for can.do_set_mode + * @dev_set_bittiming: used for can.do_set_bittiming + * @dev_get_busparams: readback arbitration busparams + * @dev_set_data_bittiming: used for can.do_set_data_bittiming + * @dev_get_data_busparams: readback data busparams + * @dev_get_berr_counter: used for can.do_get_berr_counter + * + * @dev_setup_endpoints: setup USB in and out endpoints + * @dev_init_card: initialize card + * @dev_init_channel: initialize channel + * @dev_remove_channel: uninitialize channel + * @dev_get_software_info: get software info + * @dev_get_software_details: get software details + * @dev_get_card_info: get card info + * @dev_get_capabilities: discover device capabilities + * + * @dev_set_opt_mode: set ctrlmod + * @dev_start_chip: start the CAN controller + * @dev_stop_chip: stop the CAN controller + * @dev_reset_chip: reset the CAN controller + * @dev_flush_queue: flush outstanding CAN messages + * @dev_read_bulk_callback: handle incoming commands + * @dev_frame_to_cmd: translate struct can_frame into device command + */ +struct kvaser_usb_dev_ops { + int (*dev_set_mode)(struct net_device *netdev, enum can_mode mode); + int (*dev_set_bittiming)(const struct net_device *netdev, + const struct kvaser_usb_busparams *busparams); + int (*dev_get_busparams)(struct kvaser_usb_net_priv *priv); + int (*dev_set_data_bittiming)(const struct net_device *netdev, + const struct kvaser_usb_busparams *busparams); + int (*dev_get_data_busparams)(struct kvaser_usb_net_priv *priv); + int (*dev_get_berr_counter)(const struct net_device *netdev, + struct can_berr_counter *bec); + int (*dev_setup_endpoints)(struct kvaser_usb *dev); + int (*dev_init_card)(struct kvaser_usb *dev); + int (*dev_init_channel)(struct kvaser_usb_net_priv *priv); + void (*dev_remove_channel)(struct kvaser_usb_net_priv *priv); + int (*dev_get_software_info)(struct kvaser_usb *dev); + int (*dev_get_software_details)(struct kvaser_usb *dev); + int (*dev_get_card_info)(struct kvaser_usb *dev); + int (*dev_get_capabilities)(struct kvaser_usb *dev); + int (*dev_set_opt_mode)(const struct kvaser_usb_net_priv *priv); + int (*dev_start_chip)(struct kvaser_usb_net_priv *priv); + int (*dev_stop_chip)(struct kvaser_usb_net_priv *priv); + int (*dev_reset_chip)(struct kvaser_usb *dev, int channel); + int (*dev_flush_queue)(struct kvaser_usb_net_priv *priv); + void (*dev_read_bulk_callback)(struct kvaser_usb *dev, void *buf, + int len); + void *(*dev_frame_to_cmd)(const struct kvaser_usb_net_priv *priv, + const struct sk_buff *skb, int *frame_len, + int *cmd_len, u16 transid); +}; + +struct kvaser_usb_driver_info { + u32 quirks; + enum kvaser_usb_leaf_family family; + const struct kvaser_usb_dev_ops *ops; +}; + +struct kvaser_usb_dev_cfg { + const struct can_clock clock; + const unsigned int timestamp_freq; + const struct can_bittiming_const * const bittiming_const; + const struct can_bittiming_const * const data_bittiming_const; +}; + +extern const struct kvaser_usb_dev_ops kvaser_usb_hydra_dev_ops; +extern const struct kvaser_usb_dev_ops kvaser_usb_leaf_dev_ops; + +void kvaser_usb_unlink_tx_urbs(struct kvaser_usb_net_priv *priv); + +int kvaser_usb_recv_cmd(const struct kvaser_usb *dev, void *cmd, int len, + int *actual_len); + +int kvaser_usb_send_cmd(const struct kvaser_usb *dev, void *cmd, int len); + +int kvaser_usb_send_cmd_async(struct kvaser_usb_net_priv *priv, void *cmd, + int len); + +int kvaser_usb_can_rx_over_error(struct net_device *netdev); + +extern const struct can_bittiming_const kvaser_usb_flexc_bittiming_const; + +#endif /* KVASER_USB_H */ diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c new file mode 100644 index 000000000..1f015b496 --- /dev/null +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c @@ -0,0 +1,959 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Parts of this driver are based on the following: + * - Kvaser linux leaf driver (version 4.78) + * - CAN driver for esd CAN-USB/2 + * - Kvaser linux usbcanII driver (version 5.3) + * - Kvaser linux mhydra driver (version 5.24) + * + * Copyright (C) 2002-2018 KVASER AB, Sweden. All rights reserved. + * Copyright (C) 2010 Matthias Fuchs <matthias.fuchs@esd.eu>, esd gmbh + * Copyright (C) 2012 Olivier Sobrie <olivier@sobrie.be> + * Copyright (C) 2015 Valeo S.A. + */ + +#include <linux/completion.h> +#include <linux/device.h> +#include <linux/gfp.h> +#include <linux/if.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/spinlock.h> +#include <linux/types.h> +#include <linux/usb.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> +#include <linux/can/netlink.h> + +#include "kvaser_usb.h" + +/* Kvaser USB vendor id. */ +#define KVASER_VENDOR_ID 0x0bfd + +/* Kvaser Leaf USB devices product ids */ +#define USB_LEAF_DEVEL_PRODUCT_ID 10 +#define USB_LEAF_LITE_PRODUCT_ID 11 +#define USB_LEAF_PRO_PRODUCT_ID 12 +#define USB_LEAF_SPRO_PRODUCT_ID 14 +#define USB_LEAF_PRO_LS_PRODUCT_ID 15 +#define USB_LEAF_PRO_SWC_PRODUCT_ID 16 +#define USB_LEAF_PRO_LIN_PRODUCT_ID 17 +#define USB_LEAF_SPRO_LS_PRODUCT_ID 18 +#define USB_LEAF_SPRO_SWC_PRODUCT_ID 19 +#define USB_MEMO2_DEVEL_PRODUCT_ID 22 +#define USB_MEMO2_HSHS_PRODUCT_ID 23 +#define USB_UPRO_HSHS_PRODUCT_ID 24 +#define USB_LEAF_LITE_GI_PRODUCT_ID 25 +#define USB_LEAF_PRO_OBDII_PRODUCT_ID 26 +#define USB_MEMO2_HSLS_PRODUCT_ID 27 +#define USB_LEAF_LITE_CH_PRODUCT_ID 28 +#define USB_BLACKBIRD_SPRO_PRODUCT_ID 29 +#define USB_OEM_MERCURY_PRODUCT_ID 34 +#define USB_OEM_LEAF_PRODUCT_ID 35 +#define USB_CAN_R_PRODUCT_ID 39 +#define USB_LEAF_LITE_V2_PRODUCT_ID 288 +#define USB_MINI_PCIE_HS_PRODUCT_ID 289 +#define USB_LEAF_LIGHT_HS_V2_OEM_PRODUCT_ID 290 +#define USB_USBCAN_LIGHT_2HS_PRODUCT_ID 291 +#define USB_MINI_PCIE_2HS_PRODUCT_ID 292 + +/* Kvaser USBCan-II devices product ids */ +#define USB_USBCAN_REVB_PRODUCT_ID 2 +#define USB_VCI2_PRODUCT_ID 3 +#define USB_USBCAN2_PRODUCT_ID 4 +#define USB_MEMORATOR_PRODUCT_ID 5 + +/* Kvaser Minihydra USB devices product ids */ +#define USB_BLACKBIRD_V2_PRODUCT_ID 258 +#define USB_MEMO_PRO_5HS_PRODUCT_ID 260 +#define USB_USBCAN_PRO_5HS_PRODUCT_ID 261 +#define USB_USBCAN_LIGHT_4HS_PRODUCT_ID 262 +#define USB_LEAF_PRO_HS_V2_PRODUCT_ID 263 +#define USB_USBCAN_PRO_2HS_V2_PRODUCT_ID 264 +#define USB_MEMO_2HS_PRODUCT_ID 265 +#define USB_MEMO_PRO_2HS_V2_PRODUCT_ID 266 +#define USB_HYBRID_CANLIN_PRODUCT_ID 267 +#define USB_ATI_USBCAN_PRO_2HS_V2_PRODUCT_ID 268 +#define USB_ATI_MEMO_PRO_2HS_V2_PRODUCT_ID 269 +#define USB_HYBRID_PRO_CANLIN_PRODUCT_ID 270 + +static const struct kvaser_usb_driver_info kvaser_usb_driver_info_hydra = { + .quirks = 0, + .ops = &kvaser_usb_hydra_dev_ops, +}; + +static const struct kvaser_usb_driver_info kvaser_usb_driver_info_usbcan = { + .quirks = KVASER_USB_QUIRK_HAS_TXRX_ERRORS | + KVASER_USB_QUIRK_HAS_SILENT_MODE, + .family = KVASER_USBCAN, + .ops = &kvaser_usb_leaf_dev_ops, +}; + +static const struct kvaser_usb_driver_info kvaser_usb_driver_info_leaf = { + .quirks = KVASER_USB_QUIRK_IGNORE_CLK_FREQ, + .family = KVASER_LEAF, + .ops = &kvaser_usb_leaf_dev_ops, +}; + +static const struct kvaser_usb_driver_info kvaser_usb_driver_info_leaf_err = { + .quirks = KVASER_USB_QUIRK_HAS_TXRX_ERRORS | + KVASER_USB_QUIRK_IGNORE_CLK_FREQ, + .family = KVASER_LEAF, + .ops = &kvaser_usb_leaf_dev_ops, +}; + +static const struct kvaser_usb_driver_info kvaser_usb_driver_info_leaf_err_listen = { + .quirks = KVASER_USB_QUIRK_HAS_TXRX_ERRORS | + KVASER_USB_QUIRK_HAS_SILENT_MODE | + KVASER_USB_QUIRK_IGNORE_CLK_FREQ, + .family = KVASER_LEAF, + .ops = &kvaser_usb_leaf_dev_ops, +}; + +static const struct kvaser_usb_driver_info kvaser_usb_driver_info_leafimx = { + .quirks = 0, + .ops = &kvaser_usb_leaf_dev_ops, +}; + +static const struct usb_device_id kvaser_usb_table[] = { + /* Leaf M32C USB product IDs */ + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_DEVEL_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_leaf }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_leaf }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_leaf_err_listen }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_leaf_err_listen }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LS_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_leaf_err_listen }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_SWC_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_leaf_err_listen }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LIN_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_leaf_err_listen }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_LS_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_leaf_err_listen }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_SWC_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_leaf_err_listen }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_DEVEL_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_leaf_err_listen }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSHS_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_leaf_err_listen }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_UPRO_HSHS_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_leaf_err }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_GI_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_leaf }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_OBDII_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_leaf_err_listen }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSLS_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_leaf_err }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_CH_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_leaf_err }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_BLACKBIRD_SPRO_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_leaf_err }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_MERCURY_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_leaf_err }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_LEAF_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_leaf_err }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_CAN_R_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_leaf_err }, + + /* Leaf i.MX28 USB product IDs */ + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_V2_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_leafimx }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_MINI_PCIE_HS_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_leafimx }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LIGHT_HS_V2_OEM_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_leafimx }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_USBCAN_LIGHT_2HS_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_leafimx }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_MINI_PCIE_2HS_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_leafimx }, + + /* USBCANII USB product IDs */ + { USB_DEVICE(KVASER_VENDOR_ID, USB_USBCAN2_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_usbcan }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_USBCAN_REVB_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_usbcan }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMORATOR_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_usbcan }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_VCI2_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_usbcan }, + + /* Minihydra USB product IDs */ + { USB_DEVICE(KVASER_VENDOR_ID, USB_BLACKBIRD_V2_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_hydra }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO_PRO_5HS_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_hydra }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_USBCAN_PRO_5HS_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_hydra }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_USBCAN_LIGHT_4HS_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_hydra }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_HS_V2_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_hydra }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_USBCAN_PRO_2HS_V2_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_hydra }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO_2HS_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_hydra }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO_PRO_2HS_V2_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_hydra }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_HYBRID_CANLIN_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_hydra }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_ATI_USBCAN_PRO_2HS_V2_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_hydra }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_ATI_MEMO_PRO_2HS_V2_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_hydra }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_HYBRID_PRO_CANLIN_PRODUCT_ID), + .driver_info = (kernel_ulong_t)&kvaser_usb_driver_info_hydra }, + { } +}; +MODULE_DEVICE_TABLE(usb, kvaser_usb_table); + +int kvaser_usb_send_cmd(const struct kvaser_usb *dev, void *cmd, int len) +{ + int actual_len; /* Not used */ + + return usb_bulk_msg(dev->udev, + usb_sndbulkpipe(dev->udev, + dev->bulk_out->bEndpointAddress), + cmd, len, &actual_len, KVASER_USB_TIMEOUT); +} + +int kvaser_usb_recv_cmd(const struct kvaser_usb *dev, void *cmd, int len, + int *actual_len) +{ + return usb_bulk_msg(dev->udev, + usb_rcvbulkpipe(dev->udev, + dev->bulk_in->bEndpointAddress), + cmd, len, actual_len, KVASER_USB_TIMEOUT); +} + +static void kvaser_usb_send_cmd_callback(struct urb *urb) +{ + struct net_device *netdev = urb->context; + + kfree(urb->transfer_buffer); + + if (urb->status) + netdev_warn(netdev, "urb status received: %d\n", urb->status); +} + +int kvaser_usb_send_cmd_async(struct kvaser_usb_net_priv *priv, void *cmd, + int len) +{ + struct kvaser_usb *dev = priv->dev; + struct net_device *netdev = priv->netdev; + struct urb *urb; + int err; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) + return -ENOMEM; + + usb_fill_bulk_urb(urb, dev->udev, + usb_sndbulkpipe(dev->udev, + dev->bulk_out->bEndpointAddress), + cmd, len, kvaser_usb_send_cmd_callback, netdev); + usb_anchor_urb(urb, &priv->tx_submitted); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err) { + netdev_err(netdev, "Error transmitting URB\n"); + usb_unanchor_urb(urb); + } + usb_free_urb(urb); + + return 0; +} + +int kvaser_usb_can_rx_over_error(struct net_device *netdev) +{ + struct net_device_stats *stats = &netdev->stats; + struct can_frame *cf; + struct sk_buff *skb; + + stats->rx_over_errors++; + stats->rx_errors++; + + skb = alloc_can_err_skb(netdev, &cf); + if (!skb) { + stats->rx_dropped++; + netdev_warn(netdev, "No memory left for err_skb\n"); + return -ENOMEM; + } + + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + + return 0; +} + +static void kvaser_usb_read_bulk_callback(struct urb *urb) +{ + struct kvaser_usb *dev = urb->context; + const struct kvaser_usb_dev_ops *ops = dev->driver_info->ops; + int err; + unsigned int i; + + switch (urb->status) { + case 0: + break; + case -ENOENT: + case -EPIPE: + case -EPROTO: + case -ESHUTDOWN: + return; + default: + dev_info(&dev->intf->dev, "Rx URB aborted (%d)\n", urb->status); + goto resubmit_urb; + } + + ops->dev_read_bulk_callback(dev, urb->transfer_buffer, + urb->actual_length); + +resubmit_urb: + usb_fill_bulk_urb(urb, dev->udev, + usb_rcvbulkpipe(dev->udev, + dev->bulk_in->bEndpointAddress), + urb->transfer_buffer, KVASER_USB_RX_BUFFER_SIZE, + kvaser_usb_read_bulk_callback, dev); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err == -ENODEV) { + for (i = 0; i < dev->nchannels; i++) { + if (!dev->nets[i]) + continue; + + netif_device_detach(dev->nets[i]->netdev); + } + } else if (err) { + dev_err(&dev->intf->dev, + "Failed resubmitting read bulk urb: %d\n", err); + } +} + +static int kvaser_usb_setup_rx_urbs(struct kvaser_usb *dev) +{ + int i, err = 0; + + if (dev->rxinitdone) + return 0; + + for (i = 0; i < KVASER_USB_MAX_RX_URBS; i++) { + struct urb *urb = NULL; + u8 *buf = NULL; + dma_addr_t buf_dma; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + err = -ENOMEM; + break; + } + + buf = usb_alloc_coherent(dev->udev, KVASER_USB_RX_BUFFER_SIZE, + GFP_KERNEL, &buf_dma); + if (!buf) { + dev_warn(&dev->intf->dev, + "No memory left for USB buffer\n"); + usb_free_urb(urb); + err = -ENOMEM; + break; + } + + usb_fill_bulk_urb(urb, dev->udev, + usb_rcvbulkpipe + (dev->udev, + dev->bulk_in->bEndpointAddress), + buf, KVASER_USB_RX_BUFFER_SIZE, + kvaser_usb_read_bulk_callback, dev); + urb->transfer_dma = buf_dma; + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(urb, &dev->rx_submitted); + + err = usb_submit_urb(urb, GFP_KERNEL); + if (err) { + usb_unanchor_urb(urb); + usb_free_coherent(dev->udev, + KVASER_USB_RX_BUFFER_SIZE, buf, + buf_dma); + usb_free_urb(urb); + break; + } + + dev->rxbuf[i] = buf; + dev->rxbuf_dma[i] = buf_dma; + + usb_free_urb(urb); + } + + if (i == 0) { + dev_warn(&dev->intf->dev, "Cannot setup read URBs, error %d\n", + err); + return err; + } else if (i < KVASER_USB_MAX_RX_URBS) { + dev_warn(&dev->intf->dev, "RX performances may be slow\n"); + } + + dev->rxinitdone = true; + + return 0; +} + +static int kvaser_usb_open(struct net_device *netdev) +{ + struct kvaser_usb_net_priv *priv = netdev_priv(netdev); + struct kvaser_usb *dev = priv->dev; + const struct kvaser_usb_dev_ops *ops = dev->driver_info->ops; + int err; + + err = open_candev(netdev); + if (err) + return err; + + err = ops->dev_set_opt_mode(priv); + if (err) + goto error; + + err = ops->dev_start_chip(priv); + if (err) { + netdev_warn(netdev, "Cannot start device, error %d\n", err); + goto error; + } + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + return 0; + +error: + close_candev(netdev); + return err; +} + +static void kvaser_usb_reset_tx_urb_contexts(struct kvaser_usb_net_priv *priv) +{ + int i, max_tx_urbs; + + max_tx_urbs = priv->dev->max_tx_urbs; + + priv->active_tx_contexts = 0; + for (i = 0; i < max_tx_urbs; i++) + priv->tx_contexts[i].echo_index = max_tx_urbs; +} + +/* This method might sleep. Do not call it in the atomic context + * of URB completions. + */ +void kvaser_usb_unlink_tx_urbs(struct kvaser_usb_net_priv *priv) +{ + usb_kill_anchored_urbs(&priv->tx_submitted); + kvaser_usb_reset_tx_urb_contexts(priv); +} + +static void kvaser_usb_unlink_all_urbs(struct kvaser_usb *dev) +{ + int i; + + usb_kill_anchored_urbs(&dev->rx_submitted); + + for (i = 0; i < KVASER_USB_MAX_RX_URBS; i++) + usb_free_coherent(dev->udev, KVASER_USB_RX_BUFFER_SIZE, + dev->rxbuf[i], dev->rxbuf_dma[i]); + + for (i = 0; i < dev->nchannels; i++) { + struct kvaser_usb_net_priv *priv = dev->nets[i]; + + if (priv) + kvaser_usb_unlink_tx_urbs(priv); + } +} + +static int kvaser_usb_close(struct net_device *netdev) +{ + struct kvaser_usb_net_priv *priv = netdev_priv(netdev); + struct kvaser_usb *dev = priv->dev; + const struct kvaser_usb_dev_ops *ops = dev->driver_info->ops; + int err; + + netif_stop_queue(netdev); + + err = ops->dev_flush_queue(priv); + if (err) + netdev_warn(netdev, "Cannot flush queue, error %d\n", err); + + if (ops->dev_reset_chip) { + err = ops->dev_reset_chip(dev, priv->channel); + if (err) + netdev_warn(netdev, "Cannot reset card, error %d\n", + err); + } + + err = ops->dev_stop_chip(priv); + if (err) + netdev_warn(netdev, "Cannot stop device, error %d\n", err); + + /* reset tx contexts */ + kvaser_usb_unlink_tx_urbs(priv); + + priv->can.state = CAN_STATE_STOPPED; + close_candev(priv->netdev); + + return 0; +} + +static int kvaser_usb_set_bittiming(struct net_device *netdev) +{ + struct kvaser_usb_net_priv *priv = netdev_priv(netdev); + struct kvaser_usb *dev = priv->dev; + const struct kvaser_usb_dev_ops *ops = dev->driver_info->ops; + struct can_bittiming *bt = &priv->can.bittiming; + + struct kvaser_usb_busparams busparams; + int tseg1 = bt->prop_seg + bt->phase_seg1; + int tseg2 = bt->phase_seg2; + int sjw = bt->sjw; + int err = -EOPNOTSUPP; + + busparams.bitrate = cpu_to_le32(bt->bitrate); + busparams.sjw = (u8)sjw; + busparams.tseg1 = (u8)tseg1; + busparams.tseg2 = (u8)tseg2; + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + busparams.nsamples = 3; + else + busparams.nsamples = 1; + + err = ops->dev_set_bittiming(netdev, &busparams); + if (err) + return err; + + err = kvaser_usb_setup_rx_urbs(priv->dev); + if (err) + return err; + + err = ops->dev_get_busparams(priv); + if (err) { + /* Treat EOPNOTSUPP as success */ + if (err == -EOPNOTSUPP) + err = 0; + return err; + } + + if (memcmp(&busparams, &priv->busparams_nominal, + sizeof(priv->busparams_nominal)) != 0) + err = -EINVAL; + + return err; +} + +static int kvaser_usb_set_data_bittiming(struct net_device *netdev) +{ + struct kvaser_usb_net_priv *priv = netdev_priv(netdev); + struct kvaser_usb *dev = priv->dev; + const struct kvaser_usb_dev_ops *ops = dev->driver_info->ops; + struct can_bittiming *dbt = &priv->can.data_bittiming; + + struct kvaser_usb_busparams busparams; + int tseg1 = dbt->prop_seg + dbt->phase_seg1; + int tseg2 = dbt->phase_seg2; + int sjw = dbt->sjw; + int err; + + if (!ops->dev_set_data_bittiming || + !ops->dev_get_data_busparams) + return -EOPNOTSUPP; + + busparams.bitrate = cpu_to_le32(dbt->bitrate); + busparams.sjw = (u8)sjw; + busparams.tseg1 = (u8)tseg1; + busparams.tseg2 = (u8)tseg2; + busparams.nsamples = 1; + + err = ops->dev_set_data_bittiming(netdev, &busparams); + if (err) + return err; + + err = kvaser_usb_setup_rx_urbs(priv->dev); + if (err) + return err; + + err = ops->dev_get_data_busparams(priv); + if (err) + return err; + + if (memcmp(&busparams, &priv->busparams_data, + sizeof(priv->busparams_data)) != 0) + err = -EINVAL; + + return err; +} + +static void kvaser_usb_write_bulk_callback(struct urb *urb) +{ + struct kvaser_usb_tx_urb_context *context = urb->context; + struct kvaser_usb_net_priv *priv; + struct net_device *netdev; + + if (WARN_ON(!context)) + return; + + priv = context->priv; + netdev = priv->netdev; + + kfree(urb->transfer_buffer); + + if (!netif_device_present(netdev)) + return; + + if (urb->status) + netdev_info(netdev, "Tx URB aborted (%d)\n", urb->status); +} + +static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + struct kvaser_usb_net_priv *priv = netdev_priv(netdev); + struct kvaser_usb *dev = priv->dev; + const struct kvaser_usb_dev_ops *ops = dev->driver_info->ops; + struct net_device_stats *stats = &netdev->stats; + struct kvaser_usb_tx_urb_context *context = NULL; + struct urb *urb; + void *buf; + int cmd_len = 0; + int err, ret = NETDEV_TX_OK; + unsigned int i; + unsigned long flags; + + if (can_dropped_invalid_skb(netdev, skb)) + return NETDEV_TX_OK; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + stats->tx_dropped++; + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + + spin_lock_irqsave(&priv->tx_contexts_lock, flags); + for (i = 0; i < dev->max_tx_urbs; i++) { + if (priv->tx_contexts[i].echo_index == dev->max_tx_urbs) { + context = &priv->tx_contexts[i]; + + context->echo_index = i; + ++priv->active_tx_contexts; + if (priv->active_tx_contexts >= (int)dev->max_tx_urbs) + netif_stop_queue(netdev); + + break; + } + } + spin_unlock_irqrestore(&priv->tx_contexts_lock, flags); + + /* This should never happen; it implies a flow control bug */ + if (!context) { + netdev_warn(netdev, "cannot find free context\n"); + + ret = NETDEV_TX_BUSY; + goto freeurb; + } + + buf = ops->dev_frame_to_cmd(priv, skb, &context->dlc, &cmd_len, + context->echo_index); + if (!buf) { + stats->tx_dropped++; + dev_kfree_skb(skb); + spin_lock_irqsave(&priv->tx_contexts_lock, flags); + + context->echo_index = dev->max_tx_urbs; + --priv->active_tx_contexts; + netif_wake_queue(netdev); + + spin_unlock_irqrestore(&priv->tx_contexts_lock, flags); + goto freeurb; + } + + context->priv = priv; + + can_put_echo_skb(skb, netdev, context->echo_index); + + usb_fill_bulk_urb(urb, dev->udev, + usb_sndbulkpipe(dev->udev, + dev->bulk_out->bEndpointAddress), + buf, cmd_len, kvaser_usb_write_bulk_callback, + context); + usb_anchor_urb(urb, &priv->tx_submitted); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (unlikely(err)) { + spin_lock_irqsave(&priv->tx_contexts_lock, flags); + + can_free_echo_skb(netdev, context->echo_index); + context->echo_index = dev->max_tx_urbs; + --priv->active_tx_contexts; + netif_wake_queue(netdev); + + spin_unlock_irqrestore(&priv->tx_contexts_lock, flags); + + usb_unanchor_urb(urb); + kfree(buf); + + stats->tx_dropped++; + + if (err == -ENODEV) + netif_device_detach(netdev); + else + netdev_warn(netdev, "Failed tx_urb %d\n", err); + + goto freeurb; + } + + ret = NETDEV_TX_OK; + +freeurb: + usb_free_urb(urb); + return ret; +} + +static const struct net_device_ops kvaser_usb_netdev_ops = { + .ndo_open = kvaser_usb_open, + .ndo_stop = kvaser_usb_close, + .ndo_start_xmit = kvaser_usb_start_xmit, + .ndo_change_mtu = can_change_mtu, +}; + +static void kvaser_usb_remove_interfaces(struct kvaser_usb *dev) +{ + const struct kvaser_usb_dev_ops *ops = dev->driver_info->ops; + int i; + + for (i = 0; i < dev->nchannels; i++) { + if (!dev->nets[i]) + continue; + + unregister_candev(dev->nets[i]->netdev); + } + + kvaser_usb_unlink_all_urbs(dev); + + for (i = 0; i < dev->nchannels; i++) { + if (!dev->nets[i]) + continue; + + if (ops->dev_remove_channel) + ops->dev_remove_channel(dev->nets[i]); + + free_candev(dev->nets[i]->netdev); + } +} + +static int kvaser_usb_init_one(struct kvaser_usb *dev, int channel) +{ + struct net_device *netdev; + struct kvaser_usb_net_priv *priv; + const struct kvaser_usb_driver_info *driver_info = dev->driver_info; + const struct kvaser_usb_dev_ops *ops = driver_info->ops; + int err; + + if (ops->dev_reset_chip) { + err = ops->dev_reset_chip(dev, channel); + if (err) + return err; + } + + netdev = alloc_candev(struct_size(priv, tx_contexts, dev->max_tx_urbs), + dev->max_tx_urbs); + if (!netdev) { + dev_err(&dev->intf->dev, "Cannot alloc candev\n"); + return -ENOMEM; + } + + priv = netdev_priv(netdev); + + init_usb_anchor(&priv->tx_submitted); + init_completion(&priv->start_comp); + init_completion(&priv->stop_comp); + init_completion(&priv->flush_comp); + init_completion(&priv->get_busparams_comp); + priv->can.ctrlmode_supported = 0; + + priv->dev = dev; + priv->netdev = netdev; + priv->channel = channel; + + spin_lock_init(&priv->tx_contexts_lock); + kvaser_usb_reset_tx_urb_contexts(priv); + + priv->can.state = CAN_STATE_STOPPED; + priv->can.clock.freq = dev->cfg->clock.freq; + priv->can.bittiming_const = dev->cfg->bittiming_const; + priv->can.do_set_bittiming = kvaser_usb_set_bittiming; + priv->can.do_set_mode = ops->dev_set_mode; + if ((driver_info->quirks & KVASER_USB_QUIRK_HAS_TXRX_ERRORS) || + (priv->dev->card_data.capabilities & KVASER_USB_CAP_BERR_CAP)) + priv->can.do_get_berr_counter = ops->dev_get_berr_counter; + if (driver_info->quirks & KVASER_USB_QUIRK_HAS_SILENT_MODE) + priv->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY; + + priv->can.ctrlmode_supported |= dev->card_data.ctrlmode_supported; + + if (priv->can.ctrlmode_supported & CAN_CTRLMODE_FD) { + priv->can.data_bittiming_const = dev->cfg->data_bittiming_const; + priv->can.do_set_data_bittiming = kvaser_usb_set_data_bittiming; + } + + netdev->flags |= IFF_ECHO; + + netdev->netdev_ops = &kvaser_usb_netdev_ops; + + SET_NETDEV_DEV(netdev, &dev->intf->dev); + netdev->dev_id = channel; + + dev->nets[channel] = priv; + + if (ops->dev_init_channel) { + err = ops->dev_init_channel(priv); + if (err) + goto err; + } + + err = register_candev(netdev); + if (err) { + dev_err(&dev->intf->dev, "Failed to register CAN device\n"); + goto err; + } + + netdev_dbg(netdev, "device registered\n"); + + return 0; + +err: + free_candev(netdev); + dev->nets[channel] = NULL; + return err; +} + +static int kvaser_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct kvaser_usb *dev; + int err; + int i; + const struct kvaser_usb_driver_info *driver_info; + const struct kvaser_usb_dev_ops *ops; + + driver_info = (const struct kvaser_usb_driver_info *)id->driver_info; + if (!driver_info) + return -ENODEV; + + dev = devm_kzalloc(&intf->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->intf = intf; + dev->driver_info = driver_info; + ops = driver_info->ops; + + err = ops->dev_setup_endpoints(dev); + if (err) { + dev_err(&intf->dev, "Cannot get usb endpoint(s)"); + return err; + } + + dev->udev = interface_to_usbdev(intf); + + init_usb_anchor(&dev->rx_submitted); + + usb_set_intfdata(intf, dev); + + dev->card_data.ctrlmode_supported = 0; + dev->card_data.capabilities = 0; + err = ops->dev_init_card(dev); + if (err) { + dev_err(&intf->dev, + "Failed to initialize card, error %d\n", err); + return err; + } + + err = ops->dev_get_software_info(dev); + if (err) { + dev_err(&intf->dev, + "Cannot get software info, error %d\n", err); + return err; + } + + if (ops->dev_get_software_details) { + err = ops->dev_get_software_details(dev); + if (err) { + dev_err(&intf->dev, + "Cannot get software details, error %d\n", err); + return err; + } + } + + if (WARN_ON(!dev->cfg)) + return -ENODEV; + + dev_dbg(&intf->dev, "Firmware version: %d.%d.%d\n", + ((dev->fw_version >> 24) & 0xff), + ((dev->fw_version >> 16) & 0xff), + (dev->fw_version & 0xffff)); + + dev_dbg(&intf->dev, "Max outstanding tx = %d URBs\n", dev->max_tx_urbs); + + err = ops->dev_get_card_info(dev); + if (err) { + dev_err(&intf->dev, "Cannot get card info, error %d\n", err); + return err; + } + + if (ops->dev_get_capabilities) { + err = ops->dev_get_capabilities(dev); + if (err) { + dev_err(&intf->dev, + "Cannot get capabilities, error %d\n", err); + kvaser_usb_remove_interfaces(dev); + return err; + } + } + + for (i = 0; i < dev->nchannels; i++) { + err = kvaser_usb_init_one(dev, i); + if (err) { + kvaser_usb_remove_interfaces(dev); + return err; + } + } + + return 0; +} + +static void kvaser_usb_disconnect(struct usb_interface *intf) +{ + struct kvaser_usb *dev = usb_get_intfdata(intf); + + usb_set_intfdata(intf, NULL); + + if (!dev) + return; + + kvaser_usb_remove_interfaces(dev); +} + +static struct usb_driver kvaser_usb_driver = { + .name = "kvaser_usb", + .probe = kvaser_usb_probe, + .disconnect = kvaser_usb_disconnect, + .id_table = kvaser_usb_table, +}; + +module_usb_driver(kvaser_usb_driver); + +MODULE_AUTHOR("Olivier Sobrie <olivier@sobrie.be>"); +MODULE_AUTHOR("Kvaser AB <support@kvaser.com>"); +MODULE_DESCRIPTION("CAN driver for Kvaser CAN/USB devices"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c new file mode 100644 index 000000000..233bbfeaa --- /dev/null +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c @@ -0,0 +1,2152 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Parts of this driver are based on the following: + * - Kvaser linux mhydra driver (version 5.24) + * - CAN driver for esd CAN-USB/2 + * + * Copyright (C) 2018 KVASER AB, Sweden. All rights reserved. + * Copyright (C) 2010 Matthias Fuchs <matthias.fuchs@esd.eu>, esd gmbh + * + * Known issues: + * - Transition from CAN_STATE_ERROR_WARNING to CAN_STATE_ERROR_ACTIVE is only + * reported after a call to do_get_berr_counter(), since firmware does not + * distinguish between ERROR_WARNING and ERROR_ACTIVE. + * - Hardware timestamps are not set for CAN Tx frames. + */ + +#include <linux/completion.h> +#include <linux/device.h> +#include <linux/gfp.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/netdevice.h> +#include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/usb.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> +#include <linux/can/netlink.h> + +#include "kvaser_usb.h" + +/* Forward declarations */ +static const struct kvaser_usb_dev_cfg kvaser_usb_hydra_dev_cfg_kcan; +static const struct kvaser_usb_dev_cfg kvaser_usb_hydra_dev_cfg_flexc; + +#define KVASER_USB_HYDRA_BULK_EP_IN_ADDR 0x82 +#define KVASER_USB_HYDRA_BULK_EP_OUT_ADDR 0x02 + +#define KVASER_USB_HYDRA_MAX_TRANSID 0xff +#define KVASER_USB_HYDRA_MIN_TRANSID 0x01 + +/* Minihydra command IDs */ +#define CMD_SET_BUSPARAMS_REQ 16 +#define CMD_GET_BUSPARAMS_REQ 17 +#define CMD_GET_BUSPARAMS_RESP 18 +#define CMD_GET_CHIP_STATE_REQ 19 +#define CMD_CHIP_STATE_EVENT 20 +#define CMD_SET_DRIVERMODE_REQ 21 +#define CMD_START_CHIP_REQ 26 +#define CMD_START_CHIP_RESP 27 +#define CMD_STOP_CHIP_REQ 28 +#define CMD_STOP_CHIP_RESP 29 +#define CMD_TX_CAN_MESSAGE 33 +#define CMD_GET_CARD_INFO_REQ 34 +#define CMD_GET_CARD_INFO_RESP 35 +#define CMD_GET_SOFTWARE_INFO_REQ 38 +#define CMD_GET_SOFTWARE_INFO_RESP 39 +#define CMD_ERROR_EVENT 45 +#define CMD_FLUSH_QUEUE 48 +#define CMD_TX_ACKNOWLEDGE 50 +#define CMD_FLUSH_QUEUE_RESP 66 +#define CMD_SET_BUSPARAMS_FD_REQ 69 +#define CMD_SET_BUSPARAMS_FD_RESP 70 +#define CMD_SET_BUSPARAMS_RESP 85 +#define CMD_GET_CAPABILITIES_REQ 95 +#define CMD_GET_CAPABILITIES_RESP 96 +#define CMD_RX_MESSAGE 106 +#define CMD_MAP_CHANNEL_REQ 200 +#define CMD_MAP_CHANNEL_RESP 201 +#define CMD_GET_SOFTWARE_DETAILS_REQ 202 +#define CMD_GET_SOFTWARE_DETAILS_RESP 203 +#define CMD_EXTENDED 255 + +/* Minihydra extended command IDs */ +#define CMD_TX_CAN_MESSAGE_FD 224 +#define CMD_TX_ACKNOWLEDGE_FD 225 +#define CMD_RX_MESSAGE_FD 226 + +/* Hydra commands are handled by different threads in firmware. + * The threads are denoted hydra entity (HE). Each HE got a unique 6-bit + * address. The address is used in hydra commands to get/set source and + * destination HE. There are two predefined HE addresses, the remaining + * addresses are different between devices and firmware versions. Hence, we need + * to enumerate the addresses (see kvaser_usb_hydra_map_channel()). + */ + +/* Well-known HE addresses */ +#define KVASER_USB_HYDRA_HE_ADDRESS_ROUTER 0x00 +#define KVASER_USB_HYDRA_HE_ADDRESS_ILLEGAL 0x3e + +#define KVASER_USB_HYDRA_TRANSID_CANHE 0x40 +#define KVASER_USB_HYDRA_TRANSID_SYSDBG 0x61 + +struct kvaser_cmd_map_ch_req { + char name[16]; + u8 channel; + u8 reserved[11]; +} __packed; + +struct kvaser_cmd_map_ch_res { + u8 he_addr; + u8 channel; + u8 reserved[26]; +} __packed; + +struct kvaser_cmd_card_info { + __le32 serial_number; + __le32 clock_res; + __le32 mfg_date; + __le32 ean[2]; + u8 hw_version; + u8 usb_mode; + u8 hw_type; + u8 reserved0; + u8 nchannels; + u8 reserved1[3]; +} __packed; + +struct kvaser_cmd_sw_info { + u8 reserved0[8]; + __le16 max_outstanding_tx; + u8 reserved1[18]; +} __packed; + +struct kvaser_cmd_sw_detail_req { + u8 use_ext_cmd; + u8 reserved[27]; +} __packed; + +/* Software detail flags */ +#define KVASER_USB_HYDRA_SW_FLAG_FW_BETA BIT(2) +#define KVASER_USB_HYDRA_SW_FLAG_FW_BAD BIT(4) +#define KVASER_USB_HYDRA_SW_FLAG_FREQ_80M BIT(5) +#define KVASER_USB_HYDRA_SW_FLAG_EXT_CMD BIT(9) +#define KVASER_USB_HYDRA_SW_FLAG_CANFD BIT(10) +#define KVASER_USB_HYDRA_SW_FLAG_NONISO BIT(11) +#define KVASER_USB_HYDRA_SW_FLAG_EXT_CAP BIT(12) +struct kvaser_cmd_sw_detail_res { + __le32 sw_flags; + __le32 sw_version; + __le32 sw_name; + __le32 ean[2]; + __le32 max_bitrate; + u8 reserved[4]; +} __packed; + +/* Sub commands for cap_req and cap_res */ +#define KVASER_USB_HYDRA_CAP_CMD_LISTEN_MODE 0x02 +#define KVASER_USB_HYDRA_CAP_CMD_ERR_REPORT 0x05 +#define KVASER_USB_HYDRA_CAP_CMD_ONE_SHOT 0x06 +struct kvaser_cmd_cap_req { + __le16 cap_cmd; + u8 reserved[26]; +} __packed; + +/* Status codes for cap_res */ +#define KVASER_USB_HYDRA_CAP_STAT_OK 0x00 +#define KVASER_USB_HYDRA_CAP_STAT_NOT_IMPL 0x01 +#define KVASER_USB_HYDRA_CAP_STAT_UNAVAIL 0x02 +struct kvaser_cmd_cap_res { + __le16 cap_cmd; + __le16 status; + __le32 mask; + __le32 value; + u8 reserved[16]; +} __packed; + +/* CMD_ERROR_EVENT error codes */ +#define KVASER_USB_HYDRA_ERROR_EVENT_CAN 0x01 +#define KVASER_USB_HYDRA_ERROR_EVENT_PARAM 0x09 +struct kvaser_cmd_error_event { + __le16 timestamp[3]; + u8 reserved; + u8 error_code; + __le16 info1; + __le16 info2; +} __packed; + +/* Chip state status flags. Used for chip_state_event and err_frame_data. */ +#define KVASER_USB_HYDRA_BUS_ERR_ACT 0x00 +#define KVASER_USB_HYDRA_BUS_ERR_PASS BIT(5) +#define KVASER_USB_HYDRA_BUS_BUS_OFF BIT(6) +struct kvaser_cmd_chip_state_event { + __le16 timestamp[3]; + u8 tx_err_counter; + u8 rx_err_counter; + u8 bus_status; + u8 reserved[19]; +} __packed; + +/* Busparam modes */ +#define KVASER_USB_HYDRA_BUS_MODE_CAN 0x00 +#define KVASER_USB_HYDRA_BUS_MODE_CANFD_ISO 0x01 +#define KVASER_USB_HYDRA_BUS_MODE_NONISO 0x02 +struct kvaser_cmd_set_busparams { + struct kvaser_usb_busparams busparams_nominal; + u8 reserved0[4]; + struct kvaser_usb_busparams busparams_data; + u8 canfd_mode; + u8 reserved1[7]; +} __packed; + +/* Busparam type */ +#define KVASER_USB_HYDRA_BUSPARAM_TYPE_CAN 0x00 +#define KVASER_USB_HYDRA_BUSPARAM_TYPE_CANFD 0x01 +struct kvaser_cmd_get_busparams_req { + u8 type; + u8 reserved[27]; +} __packed; + +struct kvaser_cmd_get_busparams_res { + struct kvaser_usb_busparams busparams; + u8 reserved[20]; +} __packed; + +/* Ctrl modes */ +#define KVASER_USB_HYDRA_CTRLMODE_NORMAL 0x01 +#define KVASER_USB_HYDRA_CTRLMODE_LISTEN 0x02 +struct kvaser_cmd_set_ctrlmode { + u8 mode; + u8 reserved[27]; +} __packed; + +struct kvaser_err_frame_data { + u8 bus_status; + u8 reserved0; + u8 tx_err_counter; + u8 rx_err_counter; + u8 reserved1[4]; +} __packed; + +struct kvaser_cmd_rx_can { + u8 cmd_len; + u8 cmd_no; + u8 channel; + u8 flags; + __le16 timestamp[3]; + u8 dlc; + u8 padding; + __le32 id; + union { + u8 data[8]; + struct kvaser_err_frame_data err_frame_data; + }; +} __packed; + +/* Extended CAN ID flag. Used in rx_can and tx_can */ +#define KVASER_USB_HYDRA_EXTENDED_FRAME_ID BIT(31) +struct kvaser_cmd_tx_can { + __le32 id; + u8 data[8]; + u8 dlc; + u8 flags; + __le16 transid; + u8 channel; + u8 reserved[11]; +} __packed; + +struct kvaser_cmd_header { + u8 cmd_no; + /* The destination HE address is stored in 0..5 of he_addr. + * The upper part of source HE address is stored in 6..7 of he_addr, and + * the lower part is stored in 12..15 of transid. + */ + u8 he_addr; + __le16 transid; +} __packed; + +struct kvaser_cmd { + struct kvaser_cmd_header header; + union { + struct kvaser_cmd_map_ch_req map_ch_req; + struct kvaser_cmd_map_ch_res map_ch_res; + + struct kvaser_cmd_card_info card_info; + struct kvaser_cmd_sw_info sw_info; + struct kvaser_cmd_sw_detail_req sw_detail_req; + struct kvaser_cmd_sw_detail_res sw_detail_res; + + struct kvaser_cmd_cap_req cap_req; + struct kvaser_cmd_cap_res cap_res; + + struct kvaser_cmd_error_event error_event; + + struct kvaser_cmd_set_busparams set_busparams_req; + struct kvaser_cmd_get_busparams_req get_busparams_req; + struct kvaser_cmd_get_busparams_res get_busparams_res; + + struct kvaser_cmd_chip_state_event chip_state_event; + + struct kvaser_cmd_set_ctrlmode set_ctrlmode; + + struct kvaser_cmd_rx_can rx_can; + struct kvaser_cmd_tx_can tx_can; + } __packed; +} __packed; + +/* CAN frame flags. Used in rx_can, ext_rx_can, tx_can and ext_tx_can */ +#define KVASER_USB_HYDRA_CF_FLAG_ERROR_FRAME BIT(0) +#define KVASER_USB_HYDRA_CF_FLAG_OVERRUN BIT(1) +#define KVASER_USB_HYDRA_CF_FLAG_REMOTE_FRAME BIT(4) +#define KVASER_USB_HYDRA_CF_FLAG_EXTENDED_ID BIT(5) +#define KVASER_USB_HYDRA_CF_FLAG_TX_ACK BIT(6) +/* CAN frame flags. Used in ext_rx_can and ext_tx_can */ +#define KVASER_USB_HYDRA_CF_FLAG_OSM_NACK BIT(12) +#define KVASER_USB_HYDRA_CF_FLAG_ABL BIT(13) +#define KVASER_USB_HYDRA_CF_FLAG_FDF BIT(16) +#define KVASER_USB_HYDRA_CF_FLAG_BRS BIT(17) +#define KVASER_USB_HYDRA_CF_FLAG_ESI BIT(18) + +/* KCAN packet header macros. Used in ext_rx_can and ext_tx_can */ +#define KVASER_USB_KCAN_DATA_DLC_BITS 4 +#define KVASER_USB_KCAN_DATA_DLC_SHIFT 8 +#define KVASER_USB_KCAN_DATA_DLC_MASK \ + GENMASK(KVASER_USB_KCAN_DATA_DLC_BITS - 1 + \ + KVASER_USB_KCAN_DATA_DLC_SHIFT, \ + KVASER_USB_KCAN_DATA_DLC_SHIFT) + +#define KVASER_USB_KCAN_DATA_BRS BIT(14) +#define KVASER_USB_KCAN_DATA_FDF BIT(15) +#define KVASER_USB_KCAN_DATA_OSM BIT(16) +#define KVASER_USB_KCAN_DATA_AREQ BIT(31) +#define KVASER_USB_KCAN_DATA_SRR BIT(31) +#define KVASER_USB_KCAN_DATA_RTR BIT(29) +#define KVASER_USB_KCAN_DATA_IDE BIT(30) +struct kvaser_cmd_ext_rx_can { + __le32 flags; + __le32 id; + __le32 kcan_id; + __le32 kcan_header; + __le64 timestamp; + union { + u8 kcan_payload[64]; + struct kvaser_err_frame_data err_frame_data; + }; +} __packed; + +struct kvaser_cmd_ext_tx_can { + __le32 flags; + __le32 id; + __le32 kcan_id; + __le32 kcan_header; + u8 databytes; + u8 dlc; + u8 reserved[6]; + u8 kcan_payload[64]; +} __packed; + +struct kvaser_cmd_ext_tx_ack { + __le32 flags; + u8 reserved0[4]; + __le64 timestamp; + u8 reserved1[8]; +} __packed; + +/* struct for extended commands (CMD_EXTENDED) */ +struct kvaser_cmd_ext { + struct kvaser_cmd_header header; + __le16 len; + u8 cmd_no_ext; + u8 reserved; + + union { + struct kvaser_cmd_ext_rx_can rx_can; + struct kvaser_cmd_ext_tx_can tx_can; + struct kvaser_cmd_ext_tx_ack tx_ack; + } __packed; +} __packed; + +struct kvaser_usb_net_hydra_priv { + int pending_get_busparams_type; +}; + +static const struct can_bittiming_const kvaser_usb_hydra_kcan_bittiming_c = { + .name = "kvaser_usb_kcan", + .tseg1_min = 1, + .tseg1_max = 255, + .tseg2_min = 1, + .tseg2_max = 32, + .sjw_max = 16, + .brp_min = 1, + .brp_max = 8192, + .brp_inc = 1, +}; + +const struct can_bittiming_const kvaser_usb_flexc_bittiming_const = { + .name = "kvaser_usb_flex", + .tseg1_min = 4, + .tseg1_max = 16, + .tseg2_min = 2, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 256, + .brp_inc = 1, +}; + +#define KVASER_USB_HYDRA_TRANSID_BITS 12 +#define KVASER_USB_HYDRA_TRANSID_MASK \ + GENMASK(KVASER_USB_HYDRA_TRANSID_BITS - 1, 0) +#define KVASER_USB_HYDRA_HE_ADDR_SRC_MASK GENMASK(7, 6) +#define KVASER_USB_HYDRA_HE_ADDR_DEST_MASK GENMASK(5, 0) +#define KVASER_USB_HYDRA_HE_ADDR_SRC_BITS 2 +static inline u16 kvaser_usb_hydra_get_cmd_transid(const struct kvaser_cmd *cmd) +{ + return le16_to_cpu(cmd->header.transid) & KVASER_USB_HYDRA_TRANSID_MASK; +} + +static inline void kvaser_usb_hydra_set_cmd_transid(struct kvaser_cmd *cmd, + u16 transid) +{ + cmd->header.transid = + cpu_to_le16(transid & KVASER_USB_HYDRA_TRANSID_MASK); +} + +static inline u8 kvaser_usb_hydra_get_cmd_src_he(const struct kvaser_cmd *cmd) +{ + return (cmd->header.he_addr & KVASER_USB_HYDRA_HE_ADDR_SRC_MASK) >> + KVASER_USB_HYDRA_HE_ADDR_SRC_BITS | + le16_to_cpu(cmd->header.transid) >> + KVASER_USB_HYDRA_TRANSID_BITS; +} + +static inline void kvaser_usb_hydra_set_cmd_dest_he(struct kvaser_cmd *cmd, + u8 dest_he) +{ + cmd->header.he_addr = + (cmd->header.he_addr & KVASER_USB_HYDRA_HE_ADDR_SRC_MASK) | + (dest_he & KVASER_USB_HYDRA_HE_ADDR_DEST_MASK); +} + +static u8 kvaser_usb_hydra_channel_from_cmd(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + int i; + u8 channel = 0xff; + u8 src_he = kvaser_usb_hydra_get_cmd_src_he(cmd); + + for (i = 0; i < KVASER_USB_MAX_NET_DEVICES; i++) { + if (dev->card_data.hydra.channel_to_he[i] == src_he) { + channel = i; + break; + } + } + + return channel; +} + +static u16 kvaser_usb_hydra_get_next_transid(struct kvaser_usb *dev) +{ + unsigned long flags; + u16 transid; + struct kvaser_usb_dev_card_data_hydra *card_data = + &dev->card_data.hydra; + + spin_lock_irqsave(&card_data->transid_lock, flags); + transid = card_data->transid; + if (transid >= KVASER_USB_HYDRA_MAX_TRANSID) + transid = KVASER_USB_HYDRA_MIN_TRANSID; + else + transid++; + card_data->transid = transid; + spin_unlock_irqrestore(&card_data->transid_lock, flags); + + return transid; +} + +static size_t kvaser_usb_hydra_cmd_size(struct kvaser_cmd *cmd) +{ + size_t ret; + + if (cmd->header.cmd_no == CMD_EXTENDED) + ret = le16_to_cpu(((struct kvaser_cmd_ext *)cmd)->len); + else + ret = sizeof(struct kvaser_cmd); + + return ret; +} + +static struct kvaser_usb_net_priv * +kvaser_usb_hydra_net_priv_from_cmd(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_net_priv *priv = NULL; + u8 channel = kvaser_usb_hydra_channel_from_cmd(dev, cmd); + + if (channel >= dev->nchannels) + dev_err(&dev->intf->dev, + "Invalid channel number (%d)\n", channel); + else + priv = dev->nets[channel]; + + return priv; +} + +static ktime_t +kvaser_usb_hydra_ktime_from_rx_cmd(const struct kvaser_usb_dev_cfg *cfg, + const struct kvaser_cmd *cmd) +{ + u64 ticks; + + if (cmd->header.cmd_no == CMD_EXTENDED) { + struct kvaser_cmd_ext *cmd_ext = (struct kvaser_cmd_ext *)cmd; + + ticks = le64_to_cpu(cmd_ext->rx_can.timestamp); + } else { + ticks = le16_to_cpu(cmd->rx_can.timestamp[0]); + ticks += (u64)(le16_to_cpu(cmd->rx_can.timestamp[1])) << 16; + ticks += (u64)(le16_to_cpu(cmd->rx_can.timestamp[2])) << 32; + } + + return ns_to_ktime(div_u64(ticks * 1000, cfg->timestamp_freq)); +} + +static int kvaser_usb_hydra_send_simple_cmd(struct kvaser_usb *dev, + u8 cmd_no, int channel) +{ + struct kvaser_cmd *cmd; + size_t cmd_len; + int err; + + cmd = kcalloc(1, sizeof(struct kvaser_cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->header.cmd_no = cmd_no; + cmd_len = kvaser_usb_hydra_cmd_size(cmd); + if (channel < 0) { + kvaser_usb_hydra_set_cmd_dest_he + (cmd, KVASER_USB_HYDRA_HE_ADDRESS_ILLEGAL); + } else { + if (channel >= KVASER_USB_MAX_NET_DEVICES) { + dev_err(&dev->intf->dev, "channel (%d) out of range.\n", + channel); + err = -EINVAL; + goto end; + } + kvaser_usb_hydra_set_cmd_dest_he + (cmd, dev->card_data.hydra.channel_to_he[channel]); + } + kvaser_usb_hydra_set_cmd_transid + (cmd, kvaser_usb_hydra_get_next_transid(dev)); + + err = kvaser_usb_send_cmd(dev, cmd, cmd_len); + if (err) + goto end; + +end: + kfree(cmd); + + return err; +} + +static int +kvaser_usb_hydra_send_simple_cmd_async(struct kvaser_usb_net_priv *priv, + u8 cmd_no) +{ + struct kvaser_cmd *cmd; + struct kvaser_usb *dev = priv->dev; + size_t cmd_len; + int err; + + cmd = kcalloc(1, sizeof(struct kvaser_cmd), GFP_ATOMIC); + if (!cmd) + return -ENOMEM; + + cmd->header.cmd_no = cmd_no; + cmd_len = kvaser_usb_hydra_cmd_size(cmd); + + kvaser_usb_hydra_set_cmd_dest_he + (cmd, dev->card_data.hydra.channel_to_he[priv->channel]); + kvaser_usb_hydra_set_cmd_transid + (cmd, kvaser_usb_hydra_get_next_transid(dev)); + + err = kvaser_usb_send_cmd_async(priv, cmd, cmd_len); + if (err) + kfree(cmd); + + return err; +} + +/* This function is used for synchronously waiting on hydra control commands. + * Note: Compared to kvaser_usb_hydra_read_bulk_callback(), we never need to + * handle partial hydra commands. Since hydra control commands are always + * non-extended commands. + */ +static int kvaser_usb_hydra_wait_cmd(const struct kvaser_usb *dev, u8 cmd_no, + struct kvaser_cmd *cmd) +{ + void *buf; + int err; + unsigned long timeout = jiffies + msecs_to_jiffies(KVASER_USB_TIMEOUT); + + if (cmd->header.cmd_no == CMD_EXTENDED) { + dev_err(&dev->intf->dev, "Wait for CMD_EXTENDED not allowed\n"); + return -EINVAL; + } + + buf = kzalloc(KVASER_USB_RX_BUFFER_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + do { + int actual_len = 0; + int pos = 0; + + err = kvaser_usb_recv_cmd(dev, buf, KVASER_USB_RX_BUFFER_SIZE, + &actual_len); + if (err < 0) + goto end; + + while (pos < actual_len) { + struct kvaser_cmd *tmp_cmd; + size_t cmd_len; + + tmp_cmd = buf + pos; + cmd_len = kvaser_usb_hydra_cmd_size(tmp_cmd); + if (pos + cmd_len > actual_len) { + dev_err_ratelimited(&dev->intf->dev, + "Format error\n"); + break; + } + + if (tmp_cmd->header.cmd_no == cmd_no) { + memcpy(cmd, tmp_cmd, cmd_len); + goto end; + } + pos += cmd_len; + } + } while (time_before(jiffies, timeout)); + + err = -EINVAL; + +end: + kfree(buf); + + return err; +} + +static int kvaser_usb_hydra_map_channel_resp(struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + u8 he, channel; + u16 transid = kvaser_usb_hydra_get_cmd_transid(cmd); + struct kvaser_usb_dev_card_data_hydra *card_data = + &dev->card_data.hydra; + + if (transid > 0x007f || transid < 0x0040) { + dev_err(&dev->intf->dev, + "CMD_MAP_CHANNEL_RESP, invalid transid: 0x%x\n", + transid); + return -EINVAL; + } + + switch (transid) { + case KVASER_USB_HYDRA_TRANSID_CANHE: + case KVASER_USB_HYDRA_TRANSID_CANHE + 1: + case KVASER_USB_HYDRA_TRANSID_CANHE + 2: + case KVASER_USB_HYDRA_TRANSID_CANHE + 3: + case KVASER_USB_HYDRA_TRANSID_CANHE + 4: + channel = transid & 0x000f; + he = cmd->map_ch_res.he_addr; + card_data->channel_to_he[channel] = he; + break; + case KVASER_USB_HYDRA_TRANSID_SYSDBG: + card_data->sysdbg_he = cmd->map_ch_res.he_addr; + break; + default: + dev_warn(&dev->intf->dev, + "Unknown CMD_MAP_CHANNEL_RESP transid=0x%x\n", + transid); + break; + } + + return 0; +} + +static int kvaser_usb_hydra_map_channel(struct kvaser_usb *dev, u16 transid, + u8 channel, const char *name) +{ + struct kvaser_cmd *cmd; + int err; + + cmd = kcalloc(1, sizeof(struct kvaser_cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + strcpy(cmd->map_ch_req.name, name); + cmd->header.cmd_no = CMD_MAP_CHANNEL_REQ; + kvaser_usb_hydra_set_cmd_dest_he + (cmd, KVASER_USB_HYDRA_HE_ADDRESS_ROUTER); + cmd->map_ch_req.channel = channel; + + kvaser_usb_hydra_set_cmd_transid(cmd, transid); + + err = kvaser_usb_send_cmd(dev, cmd, kvaser_usb_hydra_cmd_size(cmd)); + if (err) + goto end; + + err = kvaser_usb_hydra_wait_cmd(dev, CMD_MAP_CHANNEL_RESP, cmd); + if (err) + goto end; + + err = kvaser_usb_hydra_map_channel_resp(dev, cmd); + if (err) + goto end; + +end: + kfree(cmd); + + return err; +} + +static int kvaser_usb_hydra_get_single_capability(struct kvaser_usb *dev, + u16 cap_cmd_req, u16 *status) +{ + struct kvaser_usb_dev_card_data *card_data = &dev->card_data; + struct kvaser_cmd *cmd; + size_t cmd_len; + u32 value = 0; + u32 mask = 0; + u16 cap_cmd_res; + int err; + int i; + + cmd = kcalloc(1, sizeof(struct kvaser_cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->header.cmd_no = CMD_GET_CAPABILITIES_REQ; + cmd_len = kvaser_usb_hydra_cmd_size(cmd); + cmd->cap_req.cap_cmd = cpu_to_le16(cap_cmd_req); + + kvaser_usb_hydra_set_cmd_dest_he(cmd, card_data->hydra.sysdbg_he); + kvaser_usb_hydra_set_cmd_transid + (cmd, kvaser_usb_hydra_get_next_transid(dev)); + + err = kvaser_usb_send_cmd(dev, cmd, cmd_len); + if (err) + goto end; + + err = kvaser_usb_hydra_wait_cmd(dev, CMD_GET_CAPABILITIES_RESP, cmd); + if (err) + goto end; + + *status = le16_to_cpu(cmd->cap_res.status); + + if (*status != KVASER_USB_HYDRA_CAP_STAT_OK) + goto end; + + cap_cmd_res = le16_to_cpu(cmd->cap_res.cap_cmd); + switch (cap_cmd_res) { + case KVASER_USB_HYDRA_CAP_CMD_LISTEN_MODE: + case KVASER_USB_HYDRA_CAP_CMD_ERR_REPORT: + case KVASER_USB_HYDRA_CAP_CMD_ONE_SHOT: + value = le32_to_cpu(cmd->cap_res.value); + mask = le32_to_cpu(cmd->cap_res.mask); + break; + default: + dev_warn(&dev->intf->dev, "Unknown capability command %u\n", + cap_cmd_res); + break; + } + + for (i = 0; i < dev->nchannels; i++) { + if (BIT(i) & (value & mask)) { + switch (cap_cmd_res) { + case KVASER_USB_HYDRA_CAP_CMD_LISTEN_MODE: + card_data->ctrlmode_supported |= + CAN_CTRLMODE_LISTENONLY; + break; + case KVASER_USB_HYDRA_CAP_CMD_ERR_REPORT: + card_data->capabilities |= + KVASER_USB_CAP_BERR_CAP; + break; + case KVASER_USB_HYDRA_CAP_CMD_ONE_SHOT: + card_data->ctrlmode_supported |= + CAN_CTRLMODE_ONE_SHOT; + break; + } + } + } + +end: + kfree(cmd); + + return err; +} + +static void kvaser_usb_hydra_start_chip_reply(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_net_priv *priv; + + priv = kvaser_usb_hydra_net_priv_from_cmd(dev, cmd); + if (!priv) + return; + + if (completion_done(&priv->start_comp) && + netif_queue_stopped(priv->netdev)) { + netif_wake_queue(priv->netdev); + } else { + netif_start_queue(priv->netdev); + complete(&priv->start_comp); + } +} + +static void kvaser_usb_hydra_stop_chip_reply(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_net_priv *priv; + + priv = kvaser_usb_hydra_net_priv_from_cmd(dev, cmd); + if (!priv) + return; + + complete(&priv->stop_comp); +} + +static void kvaser_usb_hydra_flush_queue_reply(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_net_priv *priv; + + priv = kvaser_usb_hydra_net_priv_from_cmd(dev, cmd); + if (!priv) + return; + + complete(&priv->flush_comp); +} + +static void kvaser_usb_hydra_get_busparams_reply(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_net_priv *priv; + struct kvaser_usb_net_hydra_priv *hydra; + + priv = kvaser_usb_hydra_net_priv_from_cmd(dev, cmd); + if (!priv) + return; + + hydra = priv->sub_priv; + if (!hydra) + return; + + switch (hydra->pending_get_busparams_type) { + case KVASER_USB_HYDRA_BUSPARAM_TYPE_CAN: + memcpy(&priv->busparams_nominal, &cmd->get_busparams_res.busparams, + sizeof(priv->busparams_nominal)); + break; + case KVASER_USB_HYDRA_BUSPARAM_TYPE_CANFD: + memcpy(&priv->busparams_data, &cmd->get_busparams_res.busparams, + sizeof(priv->busparams_nominal)); + break; + default: + dev_warn(&dev->intf->dev, "Unknown get_busparams_type %d\n", + hydra->pending_get_busparams_type); + break; + } + hydra->pending_get_busparams_type = -1; + + complete(&priv->get_busparams_comp); +} + +static void +kvaser_usb_hydra_bus_status_to_can_state(const struct kvaser_usb_net_priv *priv, + u8 bus_status, + const struct can_berr_counter *bec, + enum can_state *new_state) +{ + if (bus_status & KVASER_USB_HYDRA_BUS_BUS_OFF) { + *new_state = CAN_STATE_BUS_OFF; + } else if (bus_status & KVASER_USB_HYDRA_BUS_ERR_PASS) { + *new_state = CAN_STATE_ERROR_PASSIVE; + } else if (bus_status == KVASER_USB_HYDRA_BUS_ERR_ACT) { + if (bec->txerr >= 128 || bec->rxerr >= 128) { + netdev_warn(priv->netdev, + "ERR_ACTIVE but err tx=%u or rx=%u >=128\n", + bec->txerr, bec->rxerr); + *new_state = CAN_STATE_ERROR_PASSIVE; + } else if (bec->txerr >= 96 || bec->rxerr >= 96) { + *new_state = CAN_STATE_ERROR_WARNING; + } else { + *new_state = CAN_STATE_ERROR_ACTIVE; + } + } +} + +static void kvaser_usb_hydra_update_state(struct kvaser_usb_net_priv *priv, + u8 bus_status, + const struct can_berr_counter *bec) +{ + struct net_device *netdev = priv->netdev; + struct can_frame *cf; + struct sk_buff *skb; + struct net_device_stats *stats; + enum can_state new_state, old_state; + + old_state = priv->can.state; + + kvaser_usb_hydra_bus_status_to_can_state(priv, bus_status, bec, + &new_state); + + if (new_state == old_state) + return; + + /* Ignore state change if previous state was STOPPED and the new state + * is BUS_OFF. Firmware always report this as BUS_OFF, since firmware + * does not distinguish between BUS_OFF and STOPPED. + */ + if (old_state == CAN_STATE_STOPPED && new_state == CAN_STATE_BUS_OFF) + return; + + skb = alloc_can_err_skb(netdev, &cf); + if (skb) { + enum can_state tx_state, rx_state; + + tx_state = (bec->txerr >= bec->rxerr) ? + new_state : CAN_STATE_ERROR_ACTIVE; + rx_state = (bec->txerr <= bec->rxerr) ? + new_state : CAN_STATE_ERROR_ACTIVE; + can_change_state(netdev, cf, tx_state, rx_state); + } + + if (new_state == CAN_STATE_BUS_OFF && old_state < CAN_STATE_BUS_OFF) { + if (!priv->can.restart_ms) + kvaser_usb_hydra_send_simple_cmd_async + (priv, CMD_STOP_CHIP_REQ); + + can_bus_off(netdev); + } + + if (!skb) { + netdev_warn(netdev, "No memory left for err_skb\n"); + return; + } + + if (priv->can.restart_ms && + old_state >= CAN_STATE_BUS_OFF && + new_state < CAN_STATE_BUS_OFF) + priv->can.can_stats.restarts++; + + if (new_state != CAN_STATE_BUS_OFF) { + cf->data[6] = bec->txerr; + cf->data[7] = bec->rxerr; + } + + stats = &netdev->stats; + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); +} + +static void kvaser_usb_hydra_state_event(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_net_priv *priv; + struct can_berr_counter bec; + u8 bus_status; + + priv = kvaser_usb_hydra_net_priv_from_cmd(dev, cmd); + if (!priv) + return; + + bus_status = cmd->chip_state_event.bus_status; + bec.txerr = cmd->chip_state_event.tx_err_counter; + bec.rxerr = cmd->chip_state_event.rx_err_counter; + + kvaser_usb_hydra_update_state(priv, bus_status, &bec); + priv->bec.txerr = bec.txerr; + priv->bec.rxerr = bec.rxerr; +} + +static void kvaser_usb_hydra_error_event_parameter(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + /* info1 will contain the offending cmd_no */ + switch (le16_to_cpu(cmd->error_event.info1)) { + case CMD_START_CHIP_REQ: + dev_warn(&dev->intf->dev, + "CMD_START_CHIP_REQ error in parameter\n"); + break; + + case CMD_STOP_CHIP_REQ: + dev_warn(&dev->intf->dev, + "CMD_STOP_CHIP_REQ error in parameter\n"); + break; + + case CMD_FLUSH_QUEUE: + dev_warn(&dev->intf->dev, + "CMD_FLUSH_QUEUE error in parameter\n"); + break; + + case CMD_SET_BUSPARAMS_REQ: + dev_warn(&dev->intf->dev, + "Set bittiming failed. Error in parameter\n"); + break; + + case CMD_SET_BUSPARAMS_FD_REQ: + dev_warn(&dev->intf->dev, + "Set data bittiming failed. Error in parameter\n"); + break; + + default: + dev_warn(&dev->intf->dev, + "Unhandled parameter error event cmd_no (%u)\n", + le16_to_cpu(cmd->error_event.info1)); + break; + } +} + +static void kvaser_usb_hydra_error_event(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + switch (cmd->error_event.error_code) { + case KVASER_USB_HYDRA_ERROR_EVENT_PARAM: + kvaser_usb_hydra_error_event_parameter(dev, cmd); + break; + + case KVASER_USB_HYDRA_ERROR_EVENT_CAN: + /* Wrong channel mapping?! This should never happen! + * info1 will contain the offending cmd_no + */ + dev_err(&dev->intf->dev, + "Received CAN error event for cmd_no (%u)\n", + le16_to_cpu(cmd->error_event.info1)); + break; + + default: + dev_warn(&dev->intf->dev, + "Unhandled error event (%d)\n", + cmd->error_event.error_code); + break; + } +} + +static void +kvaser_usb_hydra_error_frame(struct kvaser_usb_net_priv *priv, + const struct kvaser_err_frame_data *err_frame_data, + ktime_t hwtstamp) +{ + struct net_device *netdev = priv->netdev; + struct net_device_stats *stats = &netdev->stats; + struct can_frame *cf; + struct sk_buff *skb; + struct skb_shared_hwtstamps *shhwtstamps; + struct can_berr_counter bec; + enum can_state new_state, old_state; + u8 bus_status; + + priv->can.can_stats.bus_error++; + stats->rx_errors++; + + bus_status = err_frame_data->bus_status; + bec.txerr = err_frame_data->tx_err_counter; + bec.rxerr = err_frame_data->rx_err_counter; + + old_state = priv->can.state; + kvaser_usb_hydra_bus_status_to_can_state(priv, bus_status, &bec, + &new_state); + + skb = alloc_can_err_skb(netdev, &cf); + + if (new_state != old_state) { + if (skb) { + enum can_state tx_state, rx_state; + + tx_state = (bec.txerr >= bec.rxerr) ? + new_state : CAN_STATE_ERROR_ACTIVE; + rx_state = (bec.txerr <= bec.rxerr) ? + new_state : CAN_STATE_ERROR_ACTIVE; + + can_change_state(netdev, cf, tx_state, rx_state); + + if (priv->can.restart_ms && + old_state >= CAN_STATE_BUS_OFF && + new_state < CAN_STATE_BUS_OFF) + cf->can_id |= CAN_ERR_RESTARTED; + } + + if (new_state == CAN_STATE_BUS_OFF) { + if (!priv->can.restart_ms) + kvaser_usb_hydra_send_simple_cmd_async + (priv, CMD_STOP_CHIP_REQ); + + can_bus_off(netdev); + } + } + + if (!skb) { + stats->rx_dropped++; + netdev_warn(netdev, "No memory left for err_skb\n"); + return; + } + + shhwtstamps = skb_hwtstamps(skb); + shhwtstamps->hwtstamp = hwtstamp; + + cf->can_id |= CAN_ERR_BUSERROR; + if (new_state != CAN_STATE_BUS_OFF) { + cf->data[6] = bec.txerr; + cf->data[7] = bec.rxerr; + } + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + + priv->bec.txerr = bec.txerr; + priv->bec.rxerr = bec.rxerr; +} + +static void kvaser_usb_hydra_one_shot_fail(struct kvaser_usb_net_priv *priv, + const struct kvaser_cmd_ext *cmd) +{ + struct net_device *netdev = priv->netdev; + struct net_device_stats *stats = &netdev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u32 flags; + + skb = alloc_can_err_skb(netdev, &cf); + if (!skb) { + stats->rx_dropped++; + netdev_warn(netdev, "No memory left for err_skb\n"); + return; + } + + cf->can_id |= CAN_ERR_BUSERROR; + flags = le32_to_cpu(cmd->tx_ack.flags); + + if (flags & KVASER_USB_HYDRA_CF_FLAG_OSM_NACK) + cf->can_id |= CAN_ERR_ACK; + if (flags & KVASER_USB_HYDRA_CF_FLAG_ABL) { + cf->can_id |= CAN_ERR_LOSTARB; + priv->can.can_stats.arbitration_lost++; + } + + stats->tx_errors++; + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); +} + +static void kvaser_usb_hydra_tx_acknowledge(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_tx_urb_context *context; + struct kvaser_usb_net_priv *priv; + unsigned long irq_flags; + bool one_shot_fail = false; + bool is_err_frame = false; + u16 transid = kvaser_usb_hydra_get_cmd_transid(cmd); + + priv = kvaser_usb_hydra_net_priv_from_cmd(dev, cmd); + if (!priv) + return; + + if (!netif_device_present(priv->netdev)) + return; + + if (cmd->header.cmd_no == CMD_EXTENDED) { + struct kvaser_cmd_ext *cmd_ext = (struct kvaser_cmd_ext *)cmd; + u32 flags = le32_to_cpu(cmd_ext->tx_ack.flags); + + if (flags & (KVASER_USB_HYDRA_CF_FLAG_OSM_NACK | + KVASER_USB_HYDRA_CF_FLAG_ABL)) { + kvaser_usb_hydra_one_shot_fail(priv, cmd_ext); + one_shot_fail = true; + } + + is_err_frame = flags & KVASER_USB_HYDRA_CF_FLAG_TX_ACK && + flags & KVASER_USB_HYDRA_CF_FLAG_ERROR_FRAME; + } + + context = &priv->tx_contexts[transid % dev->max_tx_urbs]; + if (!one_shot_fail && !is_err_frame) { + struct net_device_stats *stats = &priv->netdev->stats; + + stats->tx_packets++; + stats->tx_bytes += can_dlc2len(context->dlc); + } + + spin_lock_irqsave(&priv->tx_contexts_lock, irq_flags); + + can_get_echo_skb(priv->netdev, context->echo_index); + context->echo_index = dev->max_tx_urbs; + --priv->active_tx_contexts; + netif_wake_queue(priv->netdev); + + spin_unlock_irqrestore(&priv->tx_contexts_lock, irq_flags); +} + +static void kvaser_usb_hydra_rx_msg_std(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_net_priv *priv = NULL; + struct can_frame *cf; + struct sk_buff *skb; + struct skb_shared_hwtstamps *shhwtstamps; + struct net_device_stats *stats; + u8 flags; + ktime_t hwtstamp; + + priv = kvaser_usb_hydra_net_priv_from_cmd(dev, cmd); + if (!priv) + return; + + stats = &priv->netdev->stats; + + flags = cmd->rx_can.flags; + hwtstamp = kvaser_usb_hydra_ktime_from_rx_cmd(dev->cfg, cmd); + + if (flags & KVASER_USB_HYDRA_CF_FLAG_ERROR_FRAME) { + kvaser_usb_hydra_error_frame(priv, &cmd->rx_can.err_frame_data, + hwtstamp); + return; + } + + skb = alloc_can_skb(priv->netdev, &cf); + if (!skb) { + stats->rx_dropped++; + return; + } + + shhwtstamps = skb_hwtstamps(skb); + shhwtstamps->hwtstamp = hwtstamp; + + cf->can_id = le32_to_cpu(cmd->rx_can.id); + + if (cf->can_id & KVASER_USB_HYDRA_EXTENDED_FRAME_ID) { + cf->can_id &= CAN_EFF_MASK; + cf->can_id |= CAN_EFF_FLAG; + } else { + cf->can_id &= CAN_SFF_MASK; + } + + if (flags & KVASER_USB_HYDRA_CF_FLAG_OVERRUN) + kvaser_usb_can_rx_over_error(priv->netdev); + + cf->can_dlc = get_can_dlc(cmd->rx_can.dlc); + + if (flags & KVASER_USB_HYDRA_CF_FLAG_REMOTE_FRAME) + cf->can_id |= CAN_RTR_FLAG; + else + memcpy(cf->data, cmd->rx_can.data, cf->can_dlc); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); +} + +static void kvaser_usb_hydra_rx_msg_ext(const struct kvaser_usb *dev, + const struct kvaser_cmd_ext *cmd) +{ + struct kvaser_cmd *std_cmd = (struct kvaser_cmd *)cmd; + struct kvaser_usb_net_priv *priv; + struct canfd_frame *cf; + struct sk_buff *skb; + struct skb_shared_hwtstamps *shhwtstamps; + struct net_device_stats *stats; + u32 flags; + u8 dlc; + u32 kcan_header; + ktime_t hwtstamp; + + priv = kvaser_usb_hydra_net_priv_from_cmd(dev, std_cmd); + if (!priv) + return; + + stats = &priv->netdev->stats; + + kcan_header = le32_to_cpu(cmd->rx_can.kcan_header); + dlc = (kcan_header & KVASER_USB_KCAN_DATA_DLC_MASK) >> + KVASER_USB_KCAN_DATA_DLC_SHIFT; + + flags = le32_to_cpu(cmd->rx_can.flags); + hwtstamp = kvaser_usb_hydra_ktime_from_rx_cmd(dev->cfg, std_cmd); + + if (flags & KVASER_USB_HYDRA_CF_FLAG_ERROR_FRAME) { + kvaser_usb_hydra_error_frame(priv, &cmd->rx_can.err_frame_data, + hwtstamp); + return; + } + + if (flags & KVASER_USB_HYDRA_CF_FLAG_FDF) + skb = alloc_canfd_skb(priv->netdev, &cf); + else + skb = alloc_can_skb(priv->netdev, (struct can_frame **)&cf); + + if (!skb) { + stats->rx_dropped++; + return; + } + + shhwtstamps = skb_hwtstamps(skb); + shhwtstamps->hwtstamp = hwtstamp; + + cf->can_id = le32_to_cpu(cmd->rx_can.id); + + if (flags & KVASER_USB_HYDRA_CF_FLAG_EXTENDED_ID) { + cf->can_id &= CAN_EFF_MASK; + cf->can_id |= CAN_EFF_FLAG; + } else { + cf->can_id &= CAN_SFF_MASK; + } + + if (flags & KVASER_USB_HYDRA_CF_FLAG_OVERRUN) + kvaser_usb_can_rx_over_error(priv->netdev); + + if (flags & KVASER_USB_HYDRA_CF_FLAG_FDF) { + cf->len = can_dlc2len(get_canfd_dlc(dlc)); + if (flags & KVASER_USB_HYDRA_CF_FLAG_BRS) + cf->flags |= CANFD_BRS; + if (flags & KVASER_USB_HYDRA_CF_FLAG_ESI) + cf->flags |= CANFD_ESI; + } else { + cf->len = get_can_dlc(dlc); + } + + if (flags & KVASER_USB_HYDRA_CF_FLAG_REMOTE_FRAME) + cf->can_id |= CAN_RTR_FLAG; + else + memcpy(cf->data, cmd->rx_can.kcan_payload, cf->len); + + stats->rx_packets++; + stats->rx_bytes += cf->len; + netif_rx(skb); +} + +static void kvaser_usb_hydra_handle_cmd_std(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + switch (cmd->header.cmd_no) { + case CMD_START_CHIP_RESP: + kvaser_usb_hydra_start_chip_reply(dev, cmd); + break; + + case CMD_STOP_CHIP_RESP: + kvaser_usb_hydra_stop_chip_reply(dev, cmd); + break; + + case CMD_FLUSH_QUEUE_RESP: + kvaser_usb_hydra_flush_queue_reply(dev, cmd); + break; + + case CMD_CHIP_STATE_EVENT: + kvaser_usb_hydra_state_event(dev, cmd); + break; + + case CMD_GET_BUSPARAMS_RESP: + kvaser_usb_hydra_get_busparams_reply(dev, cmd); + break; + + case CMD_ERROR_EVENT: + kvaser_usb_hydra_error_event(dev, cmd); + break; + + case CMD_TX_ACKNOWLEDGE: + kvaser_usb_hydra_tx_acknowledge(dev, cmd); + break; + + case CMD_RX_MESSAGE: + kvaser_usb_hydra_rx_msg_std(dev, cmd); + break; + + /* Ignored commands */ + case CMD_SET_BUSPARAMS_RESP: + case CMD_SET_BUSPARAMS_FD_RESP: + break; + + default: + dev_warn(&dev->intf->dev, "Unhandled command (%d)\n", + cmd->header.cmd_no); + break; + } +} + +static void kvaser_usb_hydra_handle_cmd_ext(const struct kvaser_usb *dev, + const struct kvaser_cmd_ext *cmd) +{ + switch (cmd->cmd_no_ext) { + case CMD_TX_ACKNOWLEDGE_FD: + kvaser_usb_hydra_tx_acknowledge(dev, (struct kvaser_cmd *)cmd); + break; + + case CMD_RX_MESSAGE_FD: + kvaser_usb_hydra_rx_msg_ext(dev, cmd); + break; + + default: + dev_warn(&dev->intf->dev, "Unhandled extended command (%d)\n", + cmd->header.cmd_no); + break; + } +} + +static void kvaser_usb_hydra_handle_cmd(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + if (cmd->header.cmd_no == CMD_EXTENDED) + kvaser_usb_hydra_handle_cmd_ext + (dev, (struct kvaser_cmd_ext *)cmd); + else + kvaser_usb_hydra_handle_cmd_std(dev, cmd); +} + +static void * +kvaser_usb_hydra_frame_to_cmd_ext(const struct kvaser_usb_net_priv *priv, + const struct sk_buff *skb, int *frame_len, + int *cmd_len, u16 transid) +{ + struct kvaser_usb *dev = priv->dev; + struct kvaser_cmd_ext *cmd; + struct canfd_frame *cf = (struct canfd_frame *)skb->data; + u8 dlc = can_len2dlc(cf->len); + u8 nbr_of_bytes = cf->len; + u32 flags; + u32 id; + u32 kcan_id; + u32 kcan_header; + + *frame_len = nbr_of_bytes; + + cmd = kcalloc(1, sizeof(struct kvaser_cmd_ext), GFP_ATOMIC); + if (!cmd) + return NULL; + + kvaser_usb_hydra_set_cmd_dest_he + ((struct kvaser_cmd *)cmd, + dev->card_data.hydra.channel_to_he[priv->channel]); + kvaser_usb_hydra_set_cmd_transid((struct kvaser_cmd *)cmd, transid); + + cmd->header.cmd_no = CMD_EXTENDED; + cmd->cmd_no_ext = CMD_TX_CAN_MESSAGE_FD; + + *cmd_len = ALIGN(sizeof(struct kvaser_cmd_ext) - + sizeof(cmd->tx_can.kcan_payload) + nbr_of_bytes, + 8); + + cmd->len = cpu_to_le16(*cmd_len); + + cmd->tx_can.databytes = nbr_of_bytes; + cmd->tx_can.dlc = dlc; + + if (cf->can_id & CAN_EFF_FLAG) { + id = cf->can_id & CAN_EFF_MASK; + flags = KVASER_USB_HYDRA_CF_FLAG_EXTENDED_ID; + kcan_id = (cf->can_id & CAN_EFF_MASK) | + KVASER_USB_KCAN_DATA_IDE | KVASER_USB_KCAN_DATA_SRR; + } else { + id = cf->can_id & CAN_SFF_MASK; + flags = 0; + kcan_id = cf->can_id & CAN_SFF_MASK; + } + + if (cf->can_id & CAN_ERR_FLAG) + flags |= KVASER_USB_HYDRA_CF_FLAG_ERROR_FRAME; + + kcan_header = ((dlc << KVASER_USB_KCAN_DATA_DLC_SHIFT) & + KVASER_USB_KCAN_DATA_DLC_MASK) | + KVASER_USB_KCAN_DATA_AREQ | + (priv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT ? + KVASER_USB_KCAN_DATA_OSM : 0); + + if (can_is_canfd_skb(skb)) { + kcan_header |= KVASER_USB_KCAN_DATA_FDF | + (cf->flags & CANFD_BRS ? + KVASER_USB_KCAN_DATA_BRS : 0); + } else { + if (cf->can_id & CAN_RTR_FLAG) { + kcan_id |= KVASER_USB_KCAN_DATA_RTR; + cmd->tx_can.databytes = 0; + flags |= KVASER_USB_HYDRA_CF_FLAG_REMOTE_FRAME; + } + } + + cmd->tx_can.kcan_id = cpu_to_le32(kcan_id); + cmd->tx_can.id = cpu_to_le32(id); + cmd->tx_can.flags = cpu_to_le32(flags); + cmd->tx_can.kcan_header = cpu_to_le32(kcan_header); + + memcpy(cmd->tx_can.kcan_payload, cf->data, nbr_of_bytes); + + return cmd; +} + +static void * +kvaser_usb_hydra_frame_to_cmd_std(const struct kvaser_usb_net_priv *priv, + const struct sk_buff *skb, int *frame_len, + int *cmd_len, u16 transid) +{ + struct kvaser_usb *dev = priv->dev; + struct kvaser_cmd *cmd; + struct can_frame *cf = (struct can_frame *)skb->data; + u32 flags; + u32 id; + + *frame_len = cf->can_dlc; + + cmd = kcalloc(1, sizeof(struct kvaser_cmd), GFP_ATOMIC); + if (!cmd) + return NULL; + + kvaser_usb_hydra_set_cmd_dest_he + (cmd, dev->card_data.hydra.channel_to_he[priv->channel]); + kvaser_usb_hydra_set_cmd_transid(cmd, transid); + + cmd->header.cmd_no = CMD_TX_CAN_MESSAGE; + + *cmd_len = ALIGN(sizeof(struct kvaser_cmd), 8); + + if (cf->can_id & CAN_EFF_FLAG) { + id = (cf->can_id & CAN_EFF_MASK); + id |= KVASER_USB_HYDRA_EXTENDED_FRAME_ID; + } else { + id = cf->can_id & CAN_SFF_MASK; + } + + cmd->tx_can.dlc = cf->can_dlc; + + flags = (cf->can_id & CAN_EFF_FLAG ? + KVASER_USB_HYDRA_CF_FLAG_EXTENDED_ID : 0); + + if (cf->can_id & CAN_RTR_FLAG) + flags |= KVASER_USB_HYDRA_CF_FLAG_REMOTE_FRAME; + + flags |= (cf->can_id & CAN_ERR_FLAG ? + KVASER_USB_HYDRA_CF_FLAG_ERROR_FRAME : 0); + + cmd->tx_can.id = cpu_to_le32(id); + cmd->tx_can.flags = flags; + + memcpy(cmd->tx_can.data, cf->data, *frame_len); + + return cmd; +} + +static int kvaser_usb_hydra_set_mode(struct net_device *netdev, + enum can_mode mode) +{ + int err = 0; + + switch (mode) { + case CAN_MODE_START: + /* CAN controller automatically recovers from BUS_OFF */ + break; + default: + err = -EOPNOTSUPP; + } + + return err; +} + +static int kvaser_usb_hydra_get_busparams(struct kvaser_usb_net_priv *priv, + int busparams_type) +{ + struct kvaser_usb *dev = priv->dev; + struct kvaser_usb_net_hydra_priv *hydra = priv->sub_priv; + struct kvaser_cmd *cmd; + size_t cmd_len; + int err; + + if (!hydra) + return -EINVAL; + + cmd = kcalloc(1, sizeof(struct kvaser_cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->header.cmd_no = CMD_GET_BUSPARAMS_REQ; + cmd_len = kvaser_usb_hydra_cmd_size(cmd); + kvaser_usb_hydra_set_cmd_dest_he + (cmd, dev->card_data.hydra.channel_to_he[priv->channel]); + kvaser_usb_hydra_set_cmd_transid + (cmd, kvaser_usb_hydra_get_next_transid(dev)); + cmd->get_busparams_req.type = busparams_type; + hydra->pending_get_busparams_type = busparams_type; + + reinit_completion(&priv->get_busparams_comp); + + err = kvaser_usb_send_cmd(dev, cmd, cmd_len); + if (err) + return err; + + if (!wait_for_completion_timeout(&priv->get_busparams_comp, + msecs_to_jiffies(KVASER_USB_TIMEOUT))) + return -ETIMEDOUT; + + return err; +} + +static int kvaser_usb_hydra_get_nominal_busparams(struct kvaser_usb_net_priv *priv) +{ + return kvaser_usb_hydra_get_busparams(priv, KVASER_USB_HYDRA_BUSPARAM_TYPE_CAN); +} + +static int kvaser_usb_hydra_get_data_busparams(struct kvaser_usb_net_priv *priv) +{ + return kvaser_usb_hydra_get_busparams(priv, KVASER_USB_HYDRA_BUSPARAM_TYPE_CANFD); +} + +static int kvaser_usb_hydra_set_bittiming(const struct net_device *netdev, + const struct kvaser_usb_busparams *busparams) +{ + struct kvaser_cmd *cmd; + struct kvaser_usb_net_priv *priv = netdev_priv(netdev); + struct kvaser_usb *dev = priv->dev; + size_t cmd_len; + int err; + + cmd = kcalloc(1, sizeof(struct kvaser_cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->header.cmd_no = CMD_SET_BUSPARAMS_REQ; + cmd_len = kvaser_usb_hydra_cmd_size(cmd); + memcpy(&cmd->set_busparams_req.busparams_nominal, busparams, + sizeof(cmd->set_busparams_req.busparams_nominal)); + + kvaser_usb_hydra_set_cmd_dest_he + (cmd, dev->card_data.hydra.channel_to_he[priv->channel]); + kvaser_usb_hydra_set_cmd_transid + (cmd, kvaser_usb_hydra_get_next_transid(dev)); + + err = kvaser_usb_send_cmd(dev, cmd, cmd_len); + + kfree(cmd); + + return err; +} + +static int kvaser_usb_hydra_set_data_bittiming(const struct net_device *netdev, + const struct kvaser_usb_busparams *busparams) +{ + struct kvaser_cmd *cmd; + struct kvaser_usb_net_priv *priv = netdev_priv(netdev); + struct kvaser_usb *dev = priv->dev; + size_t cmd_len; + int err; + + cmd = kcalloc(1, sizeof(struct kvaser_cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->header.cmd_no = CMD_SET_BUSPARAMS_FD_REQ; + cmd_len = kvaser_usb_hydra_cmd_size(cmd); + memcpy(&cmd->set_busparams_req.busparams_data, busparams, + sizeof(cmd->set_busparams_req.busparams_data)); + + if (priv->can.ctrlmode & CAN_CTRLMODE_FD) { + if (priv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO) + cmd->set_busparams_req.canfd_mode = + KVASER_USB_HYDRA_BUS_MODE_NONISO; + else + cmd->set_busparams_req.canfd_mode = + KVASER_USB_HYDRA_BUS_MODE_CANFD_ISO; + } + + kvaser_usb_hydra_set_cmd_dest_he + (cmd, dev->card_data.hydra.channel_to_he[priv->channel]); + kvaser_usb_hydra_set_cmd_transid + (cmd, kvaser_usb_hydra_get_next_transid(dev)); + + err = kvaser_usb_send_cmd(dev, cmd, cmd_len); + + kfree(cmd); + + return err; +} + +static int kvaser_usb_hydra_get_berr_counter(const struct net_device *netdev, + struct can_berr_counter *bec) +{ + struct kvaser_usb_net_priv *priv = netdev_priv(netdev); + int err; + + err = kvaser_usb_hydra_send_simple_cmd(priv->dev, + CMD_GET_CHIP_STATE_REQ, + priv->channel); + if (err) + return err; + + *bec = priv->bec; + + return 0; +} + +static int kvaser_usb_hydra_setup_endpoints(struct kvaser_usb *dev) +{ + const struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *ep; + int i; + + iface_desc = dev->intf->cur_altsetting; + + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + ep = &iface_desc->endpoint[i].desc; + + if (!dev->bulk_in && usb_endpoint_is_bulk_in(ep) && + ep->bEndpointAddress == KVASER_USB_HYDRA_BULK_EP_IN_ADDR) + dev->bulk_in = ep; + + if (!dev->bulk_out && usb_endpoint_is_bulk_out(ep) && + ep->bEndpointAddress == KVASER_USB_HYDRA_BULK_EP_OUT_ADDR) + dev->bulk_out = ep; + + if (dev->bulk_in && dev->bulk_out) + return 0; + } + + return -ENODEV; +} + +static int kvaser_usb_hydra_init_card(struct kvaser_usb *dev) +{ + int err; + unsigned int i; + struct kvaser_usb_dev_card_data_hydra *card_data = + &dev->card_data.hydra; + + card_data->transid = KVASER_USB_HYDRA_MIN_TRANSID; + spin_lock_init(&card_data->transid_lock); + + memset(card_data->usb_rx_leftover, 0, KVASER_USB_HYDRA_MAX_CMD_LEN); + card_data->usb_rx_leftover_len = 0; + spin_lock_init(&card_data->usb_rx_leftover_lock); + + memset(card_data->channel_to_he, KVASER_USB_HYDRA_HE_ADDRESS_ILLEGAL, + sizeof(card_data->channel_to_he)); + card_data->sysdbg_he = 0; + + for (i = 0; i < KVASER_USB_MAX_NET_DEVICES; i++) { + err = kvaser_usb_hydra_map_channel + (dev, + (KVASER_USB_HYDRA_TRANSID_CANHE | i), + i, "CAN"); + if (err) { + dev_err(&dev->intf->dev, + "CMD_MAP_CHANNEL_REQ failed for CAN%u\n", i); + return err; + } + } + + err = kvaser_usb_hydra_map_channel(dev, KVASER_USB_HYDRA_TRANSID_SYSDBG, + 0, "SYSDBG"); + if (err) { + dev_err(&dev->intf->dev, + "CMD_MAP_CHANNEL_REQ failed for SYSDBG\n"); + return err; + } + + return 0; +} + +static int kvaser_usb_hydra_init_channel(struct kvaser_usb_net_priv *priv) +{ + struct kvaser_usb_net_hydra_priv *hydra; + + hydra = devm_kzalloc(&priv->dev->intf->dev, sizeof(*hydra), GFP_KERNEL); + if (!hydra) + return -ENOMEM; + + priv->sub_priv = hydra; + + return 0; +} + +static int kvaser_usb_hydra_get_software_info(struct kvaser_usb *dev) +{ + struct kvaser_cmd cmd; + int err; + + err = kvaser_usb_hydra_send_simple_cmd(dev, CMD_GET_SOFTWARE_INFO_REQ, + -1); + if (err) + return err; + + memset(&cmd, 0, sizeof(struct kvaser_cmd)); + err = kvaser_usb_hydra_wait_cmd(dev, CMD_GET_SOFTWARE_INFO_RESP, &cmd); + if (err) + return err; + + dev->max_tx_urbs = min_t(unsigned int, KVASER_USB_MAX_TX_URBS, + le16_to_cpu(cmd.sw_info.max_outstanding_tx)); + + return 0; +} + +static int kvaser_usb_hydra_get_software_details(struct kvaser_usb *dev) +{ + struct kvaser_cmd *cmd; + size_t cmd_len; + int err; + u32 flags; + struct kvaser_usb_dev_card_data *card_data = &dev->card_data; + + cmd = kcalloc(1, sizeof(struct kvaser_cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->header.cmd_no = CMD_GET_SOFTWARE_DETAILS_REQ; + cmd_len = kvaser_usb_hydra_cmd_size(cmd); + cmd->sw_detail_req.use_ext_cmd = 1; + kvaser_usb_hydra_set_cmd_dest_he + (cmd, KVASER_USB_HYDRA_HE_ADDRESS_ILLEGAL); + + kvaser_usb_hydra_set_cmd_transid + (cmd, kvaser_usb_hydra_get_next_transid(dev)); + + err = kvaser_usb_send_cmd(dev, cmd, cmd_len); + if (err) + goto end; + + err = kvaser_usb_hydra_wait_cmd(dev, CMD_GET_SOFTWARE_DETAILS_RESP, + cmd); + if (err) + goto end; + + dev->fw_version = le32_to_cpu(cmd->sw_detail_res.sw_version); + flags = le32_to_cpu(cmd->sw_detail_res.sw_flags); + + if (flags & KVASER_USB_HYDRA_SW_FLAG_FW_BAD) { + dev_err(&dev->intf->dev, + "Bad firmware, device refuse to run!\n"); + err = -EINVAL; + goto end; + } + + if (flags & KVASER_USB_HYDRA_SW_FLAG_FW_BETA) + dev_info(&dev->intf->dev, "Beta firmware in use\n"); + + if (flags & KVASER_USB_HYDRA_SW_FLAG_EXT_CAP) + card_data->capabilities |= KVASER_USB_CAP_EXT_CAP; + + if (flags & KVASER_USB_HYDRA_SW_FLAG_EXT_CMD) + card_data->capabilities |= KVASER_USB_HYDRA_CAP_EXT_CMD; + + if (flags & KVASER_USB_HYDRA_SW_FLAG_CANFD) + card_data->ctrlmode_supported |= CAN_CTRLMODE_FD; + + if (flags & KVASER_USB_HYDRA_SW_FLAG_NONISO) + card_data->ctrlmode_supported |= CAN_CTRLMODE_FD_NON_ISO; + + if (flags & KVASER_USB_HYDRA_SW_FLAG_FREQ_80M) + dev->cfg = &kvaser_usb_hydra_dev_cfg_kcan; + else + dev->cfg = &kvaser_usb_hydra_dev_cfg_flexc; + +end: + kfree(cmd); + + return err; +} + +static int kvaser_usb_hydra_get_card_info(struct kvaser_usb *dev) +{ + struct kvaser_cmd cmd; + int err; + + err = kvaser_usb_hydra_send_simple_cmd(dev, CMD_GET_CARD_INFO_REQ, -1); + if (err) + return err; + + memset(&cmd, 0, sizeof(struct kvaser_cmd)); + err = kvaser_usb_hydra_wait_cmd(dev, CMD_GET_CARD_INFO_RESP, &cmd); + if (err) + return err; + + dev->nchannels = cmd.card_info.nchannels; + if (dev->nchannels > KVASER_USB_MAX_NET_DEVICES) + return -EINVAL; + + return 0; +} + +static int kvaser_usb_hydra_get_capabilities(struct kvaser_usb *dev) +{ + int err; + u16 status; + + if (!(dev->card_data.capabilities & KVASER_USB_CAP_EXT_CAP)) { + dev_info(&dev->intf->dev, + "No extended capability support. Upgrade your device.\n"); + return 0; + } + + err = kvaser_usb_hydra_get_single_capability + (dev, + KVASER_USB_HYDRA_CAP_CMD_LISTEN_MODE, + &status); + if (err) + return err; + if (status) + dev_info(&dev->intf->dev, + "KVASER_USB_HYDRA_CAP_CMD_LISTEN_MODE failed %u\n", + status); + + err = kvaser_usb_hydra_get_single_capability + (dev, + KVASER_USB_HYDRA_CAP_CMD_ERR_REPORT, + &status); + if (err) + return err; + if (status) + dev_info(&dev->intf->dev, + "KVASER_USB_HYDRA_CAP_CMD_ERR_REPORT failed %u\n", + status); + + err = kvaser_usb_hydra_get_single_capability + (dev, KVASER_USB_HYDRA_CAP_CMD_ONE_SHOT, + &status); + if (err) + return err; + if (status) + dev_info(&dev->intf->dev, + "KVASER_USB_HYDRA_CAP_CMD_ONE_SHOT failed %u\n", + status); + + return 0; +} + +static int kvaser_usb_hydra_set_opt_mode(const struct kvaser_usb_net_priv *priv) +{ + struct kvaser_usb *dev = priv->dev; + struct kvaser_cmd *cmd; + size_t cmd_len; + int err; + + if ((priv->can.ctrlmode & + (CAN_CTRLMODE_FD | CAN_CTRLMODE_FD_NON_ISO)) == + CAN_CTRLMODE_FD_NON_ISO) { + netdev_warn(priv->netdev, + "CTRLMODE_FD shall be on if CTRLMODE_FD_NON_ISO is on\n"); + return -EINVAL; + } + + cmd = kcalloc(1, sizeof(struct kvaser_cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->header.cmd_no = CMD_SET_DRIVERMODE_REQ; + cmd_len = kvaser_usb_hydra_cmd_size(cmd); + kvaser_usb_hydra_set_cmd_dest_he + (cmd, dev->card_data.hydra.channel_to_he[priv->channel]); + kvaser_usb_hydra_set_cmd_transid + (cmd, kvaser_usb_hydra_get_next_transid(dev)); + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + cmd->set_ctrlmode.mode = KVASER_USB_HYDRA_CTRLMODE_LISTEN; + else + cmd->set_ctrlmode.mode = KVASER_USB_HYDRA_CTRLMODE_NORMAL; + + err = kvaser_usb_send_cmd(dev, cmd, cmd_len); + kfree(cmd); + + return err; +} + +static int kvaser_usb_hydra_start_chip(struct kvaser_usb_net_priv *priv) +{ + int err; + + reinit_completion(&priv->start_comp); + + err = kvaser_usb_hydra_send_simple_cmd(priv->dev, CMD_START_CHIP_REQ, + priv->channel); + if (err) + return err; + + if (!wait_for_completion_timeout(&priv->start_comp, + msecs_to_jiffies(KVASER_USB_TIMEOUT))) + return -ETIMEDOUT; + + return 0; +} + +static int kvaser_usb_hydra_stop_chip(struct kvaser_usb_net_priv *priv) +{ + int err; + + reinit_completion(&priv->stop_comp); + + /* Make sure we do not report invalid BUS_OFF from CMD_CHIP_STATE_EVENT + * see comment in kvaser_usb_hydra_update_state() + */ + priv->can.state = CAN_STATE_STOPPED; + + err = kvaser_usb_hydra_send_simple_cmd(priv->dev, CMD_STOP_CHIP_REQ, + priv->channel); + if (err) + return err; + + if (!wait_for_completion_timeout(&priv->stop_comp, + msecs_to_jiffies(KVASER_USB_TIMEOUT))) + return -ETIMEDOUT; + + return 0; +} + +static int kvaser_usb_hydra_flush_queue(struct kvaser_usb_net_priv *priv) +{ + int err; + + reinit_completion(&priv->flush_comp); + + err = kvaser_usb_hydra_send_simple_cmd(priv->dev, CMD_FLUSH_QUEUE, + priv->channel); + if (err) + return err; + + if (!wait_for_completion_timeout(&priv->flush_comp, + msecs_to_jiffies(KVASER_USB_TIMEOUT))) + return -ETIMEDOUT; + + return 0; +} + +/* A single extended hydra command can be transmitted in multiple transfers + * We have to buffer partial hydra commands, and handle them on next callback. + */ +static void kvaser_usb_hydra_read_bulk_callback(struct kvaser_usb *dev, + void *buf, int len) +{ + unsigned long irq_flags; + struct kvaser_cmd *cmd; + int pos = 0; + size_t cmd_len; + struct kvaser_usb_dev_card_data_hydra *card_data = + &dev->card_data.hydra; + int usb_rx_leftover_len; + spinlock_t *usb_rx_leftover_lock = &card_data->usb_rx_leftover_lock; + + spin_lock_irqsave(usb_rx_leftover_lock, irq_flags); + usb_rx_leftover_len = card_data->usb_rx_leftover_len; + if (usb_rx_leftover_len) { + int remaining_bytes; + + cmd = (struct kvaser_cmd *)card_data->usb_rx_leftover; + + cmd_len = kvaser_usb_hydra_cmd_size(cmd); + + remaining_bytes = min_t(unsigned int, len, + cmd_len - usb_rx_leftover_len); + /* Make sure we do not overflow usb_rx_leftover */ + if (remaining_bytes + usb_rx_leftover_len > + KVASER_USB_HYDRA_MAX_CMD_LEN) { + dev_err(&dev->intf->dev, "Format error\n"); + spin_unlock_irqrestore(usb_rx_leftover_lock, irq_flags); + return; + } + + memcpy(card_data->usb_rx_leftover + usb_rx_leftover_len, buf, + remaining_bytes); + pos += remaining_bytes; + + if (remaining_bytes + usb_rx_leftover_len == cmd_len) { + kvaser_usb_hydra_handle_cmd(dev, cmd); + usb_rx_leftover_len = 0; + } else { + /* Command still not complete */ + usb_rx_leftover_len += remaining_bytes; + } + card_data->usb_rx_leftover_len = usb_rx_leftover_len; + } + spin_unlock_irqrestore(usb_rx_leftover_lock, irq_flags); + + while (pos < len) { + cmd = buf + pos; + + cmd_len = kvaser_usb_hydra_cmd_size(cmd); + + if (pos + cmd_len > len) { + /* We got first part of a command */ + int leftover_bytes; + + leftover_bytes = len - pos; + /* Make sure we do not overflow usb_rx_leftover */ + if (leftover_bytes > KVASER_USB_HYDRA_MAX_CMD_LEN) { + dev_err(&dev->intf->dev, "Format error\n"); + return; + } + spin_lock_irqsave(usb_rx_leftover_lock, irq_flags); + memcpy(card_data->usb_rx_leftover, buf + pos, + leftover_bytes); + card_data->usb_rx_leftover_len = leftover_bytes; + spin_unlock_irqrestore(usb_rx_leftover_lock, irq_flags); + break; + } + + kvaser_usb_hydra_handle_cmd(dev, cmd); + pos += cmd_len; + } +} + +static void * +kvaser_usb_hydra_frame_to_cmd(const struct kvaser_usb_net_priv *priv, + const struct sk_buff *skb, int *frame_len, + int *cmd_len, u16 transid) +{ + void *buf; + + if (priv->dev->card_data.capabilities & KVASER_USB_HYDRA_CAP_EXT_CMD) + buf = kvaser_usb_hydra_frame_to_cmd_ext(priv, skb, frame_len, + cmd_len, transid); + else + buf = kvaser_usb_hydra_frame_to_cmd_std(priv, skb, frame_len, + cmd_len, transid); + + return buf; +} + +const struct kvaser_usb_dev_ops kvaser_usb_hydra_dev_ops = { + .dev_set_mode = kvaser_usb_hydra_set_mode, + .dev_set_bittiming = kvaser_usb_hydra_set_bittiming, + .dev_get_busparams = kvaser_usb_hydra_get_nominal_busparams, + .dev_set_data_bittiming = kvaser_usb_hydra_set_data_bittiming, + .dev_get_data_busparams = kvaser_usb_hydra_get_data_busparams, + .dev_get_berr_counter = kvaser_usb_hydra_get_berr_counter, + .dev_setup_endpoints = kvaser_usb_hydra_setup_endpoints, + .dev_init_card = kvaser_usb_hydra_init_card, + .dev_init_channel = kvaser_usb_hydra_init_channel, + .dev_get_software_info = kvaser_usb_hydra_get_software_info, + .dev_get_software_details = kvaser_usb_hydra_get_software_details, + .dev_get_card_info = kvaser_usb_hydra_get_card_info, + .dev_get_capabilities = kvaser_usb_hydra_get_capabilities, + .dev_set_opt_mode = kvaser_usb_hydra_set_opt_mode, + .dev_start_chip = kvaser_usb_hydra_start_chip, + .dev_stop_chip = kvaser_usb_hydra_stop_chip, + .dev_reset_chip = NULL, + .dev_flush_queue = kvaser_usb_hydra_flush_queue, + .dev_read_bulk_callback = kvaser_usb_hydra_read_bulk_callback, + .dev_frame_to_cmd = kvaser_usb_hydra_frame_to_cmd, +}; + +static const struct kvaser_usb_dev_cfg kvaser_usb_hydra_dev_cfg_kcan = { + .clock = { + .freq = 80000000, + }, + .timestamp_freq = 80, + .bittiming_const = &kvaser_usb_hydra_kcan_bittiming_c, + .data_bittiming_const = &kvaser_usb_hydra_kcan_bittiming_c, +}; + +static const struct kvaser_usb_dev_cfg kvaser_usb_hydra_dev_cfg_flexc = { + .clock = { + .freq = 24000000, + }, + .timestamp_freq = 1, + .bittiming_const = &kvaser_usb_flexc_bittiming_const, +}; diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c new file mode 100644 index 000000000..f06d63db9 --- /dev/null +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c @@ -0,0 +1,1838 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Parts of this driver are based on the following: + * - Kvaser linux leaf driver (version 4.78) + * - CAN driver for esd CAN-USB/2 + * - Kvaser linux usbcanII driver (version 5.3) + * + * Copyright (C) 2002-2018 KVASER AB, Sweden. All rights reserved. + * Copyright (C) 2010 Matthias Fuchs <matthias.fuchs@esd.eu>, esd gmbh + * Copyright (C) 2012 Olivier Sobrie <olivier@sobrie.be> + * Copyright (C) 2015 Valeo S.A. + */ + +#include <linux/completion.h> +#include <linux/device.h> +#include <linux/gfp.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/netdevice.h> +#include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/usb.h> +#include <linux/workqueue.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> +#include <linux/can/netlink.h> + +#include "kvaser_usb.h" + +#define MAX_USBCAN_NET_DEVICES 2 + +/* Command header size */ +#define CMD_HEADER_LEN 2 + +/* Kvaser CAN message flags */ +#define MSG_FLAG_ERROR_FRAME BIT(0) +#define MSG_FLAG_OVERRUN BIT(1) +#define MSG_FLAG_NERR BIT(2) +#define MSG_FLAG_WAKEUP BIT(3) +#define MSG_FLAG_REMOTE_FRAME BIT(4) +#define MSG_FLAG_RESERVED BIT(5) +#define MSG_FLAG_TX_ACK BIT(6) +#define MSG_FLAG_TX_REQUEST BIT(7) + +/* CAN states (M16C CxSTRH register) */ +#define M16C_STATE_BUS_RESET BIT(0) +#define M16C_STATE_BUS_ERROR BIT(4) +#define M16C_STATE_BUS_PASSIVE BIT(5) +#define M16C_STATE_BUS_OFF BIT(6) + +/* Leaf/usbcan command ids */ +#define CMD_RX_STD_MESSAGE 12 +#define CMD_TX_STD_MESSAGE 13 +#define CMD_RX_EXT_MESSAGE 14 +#define CMD_TX_EXT_MESSAGE 15 +#define CMD_SET_BUS_PARAMS 16 +#define CMD_GET_BUS_PARAMS 17 +#define CMD_GET_BUS_PARAMS_REPLY 18 +#define CMD_GET_CHIP_STATE 19 +#define CMD_CHIP_STATE_EVENT 20 +#define CMD_SET_CTRL_MODE 21 +#define CMD_RESET_CHIP 24 +#define CMD_START_CHIP 26 +#define CMD_START_CHIP_REPLY 27 +#define CMD_STOP_CHIP 28 +#define CMD_STOP_CHIP_REPLY 29 + +#define CMD_USBCAN_CLOCK_OVERFLOW_EVENT 33 + +#define CMD_GET_CARD_INFO 34 +#define CMD_GET_CARD_INFO_REPLY 35 +#define CMD_GET_SOFTWARE_INFO 38 +#define CMD_GET_SOFTWARE_INFO_REPLY 39 +#define CMD_ERROR_EVENT 45 +#define CMD_FLUSH_QUEUE 48 +#define CMD_TX_ACKNOWLEDGE 50 +#define CMD_CAN_ERROR_EVENT 51 +#define CMD_FLUSH_QUEUE_REPLY 68 +#define CMD_GET_CAPABILITIES_REQ 95 +#define CMD_GET_CAPABILITIES_RESP 96 + +#define CMD_LEAF_LOG_MESSAGE 106 + +/* Leaf frequency options */ +#define KVASER_USB_LEAF_SWOPTION_FREQ_MASK 0x60 +#define KVASER_USB_LEAF_SWOPTION_FREQ_16_MHZ_CLK 0 +#define KVASER_USB_LEAF_SWOPTION_FREQ_32_MHZ_CLK BIT(5) +#define KVASER_USB_LEAF_SWOPTION_FREQ_24_MHZ_CLK BIT(6) + +#define KVASER_USB_LEAF_SWOPTION_EXT_CAP BIT(12) + +/* error factors */ +#define M16C_EF_ACKE BIT(0) +#define M16C_EF_CRCE BIT(1) +#define M16C_EF_FORME BIT(2) +#define M16C_EF_STFE BIT(3) +#define M16C_EF_BITE0 BIT(4) +#define M16C_EF_BITE1 BIT(5) +#define M16C_EF_RCVE BIT(6) +#define M16C_EF_TRE BIT(7) + +/* Only Leaf-based devices can report M16C error factors, + * thus define our own error status flags for USBCANII + */ +#define USBCAN_ERROR_STATE_NONE 0 +#define USBCAN_ERROR_STATE_TX_ERROR BIT(0) +#define USBCAN_ERROR_STATE_RX_ERROR BIT(1) +#define USBCAN_ERROR_STATE_BUSERROR BIT(2) + +/* ctrl modes */ +#define KVASER_CTRL_MODE_NORMAL 1 +#define KVASER_CTRL_MODE_SILENT 2 +#define KVASER_CTRL_MODE_SELFRECEPTION 3 +#define KVASER_CTRL_MODE_OFF 4 + +/* Extended CAN identifier flag */ +#define KVASER_EXTENDED_FRAME BIT(31) + +struct kvaser_cmd_simple { + u8 tid; + u8 channel; +} __packed; + +struct kvaser_cmd_cardinfo { + u8 tid; + u8 nchannels; + __le32 serial_number; + __le32 padding0; + __le32 clock_resolution; + __le32 mfgdate; + u8 ean[8]; + u8 hw_revision; + union { + struct { + u8 usb_hs_mode; + } __packed leaf1; + struct { + u8 padding; + } __packed usbcan1; + } __packed; + __le16 padding1; +} __packed; + +struct leaf_cmd_softinfo { + u8 tid; + u8 padding0; + __le32 sw_options; + __le32 fw_version; + __le16 max_outstanding_tx; + __le16 padding1[9]; +} __packed; + +struct usbcan_cmd_softinfo { + u8 tid; + u8 fw_name[5]; + __le16 max_outstanding_tx; + u8 padding[6]; + __le32 fw_version; + __le16 checksum; + __le16 sw_options; +} __packed; + +struct kvaser_cmd_busparams { + u8 tid; + u8 channel; + struct kvaser_usb_busparams busparams; +} __packed; + +struct kvaser_cmd_tx_can { + u8 channel; + u8 tid; + u8 data[14]; + union { + struct { + u8 padding; + u8 flags; + } __packed leaf; + struct { + u8 flags; + u8 padding; + } __packed usbcan; + } __packed; +} __packed; + +struct kvaser_cmd_rx_can_header { + u8 channel; + u8 flag; +} __packed; + +struct leaf_cmd_rx_can { + u8 channel; + u8 flag; + + __le16 time[3]; + u8 data[14]; +} __packed; + +struct usbcan_cmd_rx_can { + u8 channel; + u8 flag; + + u8 data[14]; + __le16 time; +} __packed; + +struct leaf_cmd_chip_state_event { + u8 tid; + u8 channel; + + __le16 time[3]; + u8 tx_errors_count; + u8 rx_errors_count; + + u8 status; + u8 padding[3]; +} __packed; + +struct usbcan_cmd_chip_state_event { + u8 tid; + u8 channel; + + u8 tx_errors_count; + u8 rx_errors_count; + __le16 time; + + u8 status; + u8 padding[3]; +} __packed; + +struct kvaser_cmd_tx_acknowledge_header { + u8 channel; + u8 tid; +} __packed; + +struct leaf_cmd_can_error_event { + u8 tid; + u8 flags; + __le16 time[3]; + u8 channel; + u8 padding; + u8 tx_errors_count; + u8 rx_errors_count; + u8 status; + u8 error_factor; +} __packed; + +struct usbcan_cmd_can_error_event { + u8 tid; + u8 padding; + u8 tx_errors_count_ch0; + u8 rx_errors_count_ch0; + u8 tx_errors_count_ch1; + u8 rx_errors_count_ch1; + u8 status_ch0; + u8 status_ch1; + __le16 time; +} __packed; + +/* CMD_ERROR_EVENT error codes */ +#define KVASER_USB_LEAF_ERROR_EVENT_TX_QUEUE_FULL 0x8 +#define KVASER_USB_LEAF_ERROR_EVENT_PARAM 0x9 + +struct leaf_cmd_error_event { + u8 tid; + u8 error_code; + __le16 timestamp[3]; + __le16 padding; + __le16 info1; + __le16 info2; +} __packed; + +struct usbcan_cmd_error_event { + u8 tid; + u8 error_code; + __le16 info1; + __le16 info2; + __le16 timestamp; + __le16 padding; +} __packed; + +struct kvaser_cmd_ctrl_mode { + u8 tid; + u8 channel; + u8 ctrl_mode; + u8 padding[3]; +} __packed; + +struct kvaser_cmd_flush_queue { + u8 tid; + u8 channel; + u8 flags; + u8 padding[3]; +} __packed; + +struct leaf_cmd_log_message { + u8 channel; + u8 flags; + __le16 time[3]; + u8 dlc; + u8 time_offset; + __le32 id; + u8 data[8]; +} __packed; + +/* Sub commands for cap_req and cap_res */ +#define KVASER_USB_LEAF_CAP_CMD_LISTEN_MODE 0x02 +#define KVASER_USB_LEAF_CAP_CMD_ERR_REPORT 0x05 +struct kvaser_cmd_cap_req { + __le16 padding0; + __le16 cap_cmd; + __le16 padding1; + __le16 channel; +} __packed; + +/* Status codes for cap_res */ +#define KVASER_USB_LEAF_CAP_STAT_OK 0x00 +#define KVASER_USB_LEAF_CAP_STAT_NOT_IMPL 0x01 +#define KVASER_USB_LEAF_CAP_STAT_UNAVAIL 0x02 +struct kvaser_cmd_cap_res { + __le16 padding; + __le16 cap_cmd; + __le16 status; + __le32 mask; + __le32 value; +} __packed; + +struct kvaser_cmd { + u8 len; + u8 id; + union { + struct kvaser_cmd_simple simple; + struct kvaser_cmd_cardinfo cardinfo; + struct kvaser_cmd_busparams busparams; + + struct kvaser_cmd_rx_can_header rx_can_header; + struct kvaser_cmd_tx_acknowledge_header tx_acknowledge_header; + + union { + struct leaf_cmd_softinfo softinfo; + struct leaf_cmd_rx_can rx_can; + struct leaf_cmd_chip_state_event chip_state_event; + struct leaf_cmd_can_error_event can_error_event; + struct leaf_cmd_log_message log_message; + struct leaf_cmd_error_event error_event; + struct kvaser_cmd_cap_req cap_req; + struct kvaser_cmd_cap_res cap_res; + } __packed leaf; + + union { + struct usbcan_cmd_softinfo softinfo; + struct usbcan_cmd_rx_can rx_can; + struct usbcan_cmd_chip_state_event chip_state_event; + struct usbcan_cmd_can_error_event can_error_event; + struct usbcan_cmd_error_event error_event; + } __packed usbcan; + + struct kvaser_cmd_tx_can tx_can; + struct kvaser_cmd_ctrl_mode ctrl_mode; + struct kvaser_cmd_flush_queue flush_queue; + } u; +} __packed; + +#define CMD_SIZE_ANY 0xff +#define kvaser_fsize(field) sizeof_field(struct kvaser_cmd, field) + +static const u8 kvaser_usb_leaf_cmd_sizes_leaf[] = { + [CMD_START_CHIP_REPLY] = kvaser_fsize(u.simple), + [CMD_STOP_CHIP_REPLY] = kvaser_fsize(u.simple), + [CMD_GET_CARD_INFO_REPLY] = kvaser_fsize(u.cardinfo), + [CMD_TX_ACKNOWLEDGE] = kvaser_fsize(u.tx_acknowledge_header), + [CMD_GET_SOFTWARE_INFO_REPLY] = kvaser_fsize(u.leaf.softinfo), + [CMD_RX_STD_MESSAGE] = kvaser_fsize(u.leaf.rx_can), + [CMD_RX_EXT_MESSAGE] = kvaser_fsize(u.leaf.rx_can), + [CMD_LEAF_LOG_MESSAGE] = kvaser_fsize(u.leaf.log_message), + [CMD_CHIP_STATE_EVENT] = kvaser_fsize(u.leaf.chip_state_event), + [CMD_CAN_ERROR_EVENT] = kvaser_fsize(u.leaf.can_error_event), + [CMD_GET_CAPABILITIES_RESP] = kvaser_fsize(u.leaf.cap_res), + [CMD_GET_BUS_PARAMS_REPLY] = kvaser_fsize(u.busparams), + [CMD_ERROR_EVENT] = kvaser_fsize(u.leaf.error_event), + /* ignored events: */ + [CMD_FLUSH_QUEUE_REPLY] = CMD_SIZE_ANY, +}; + +static const u8 kvaser_usb_leaf_cmd_sizes_usbcan[] = { + [CMD_START_CHIP_REPLY] = kvaser_fsize(u.simple), + [CMD_STOP_CHIP_REPLY] = kvaser_fsize(u.simple), + [CMD_GET_CARD_INFO_REPLY] = kvaser_fsize(u.cardinfo), + [CMD_TX_ACKNOWLEDGE] = kvaser_fsize(u.tx_acknowledge_header), + [CMD_GET_SOFTWARE_INFO_REPLY] = kvaser_fsize(u.usbcan.softinfo), + [CMD_RX_STD_MESSAGE] = kvaser_fsize(u.usbcan.rx_can), + [CMD_RX_EXT_MESSAGE] = kvaser_fsize(u.usbcan.rx_can), + [CMD_CHIP_STATE_EVENT] = kvaser_fsize(u.usbcan.chip_state_event), + [CMD_CAN_ERROR_EVENT] = kvaser_fsize(u.usbcan.can_error_event), + [CMD_ERROR_EVENT] = kvaser_fsize(u.usbcan.error_event), + /* ignored events: */ + [CMD_USBCAN_CLOCK_OVERFLOW_EVENT] = CMD_SIZE_ANY, +}; + +/* Summary of a kvaser error event, for a unified Leaf/Usbcan error + * handling. Some discrepancies between the two families exist: + * + * - USBCAN firmware does not report M16C "error factors" + * - USBCAN controllers has difficulties reporting if the raised error + * event is for ch0 or ch1. They leave such arbitration to the OS + * driver by letting it compare error counters with previous values + * and decide the error event's channel. Thus for USBCAN, the channel + * field is only advisory. + */ +struct kvaser_usb_err_summary { + u8 channel, status, txerr, rxerr; + union { + struct { + u8 error_factor; + } leaf; + struct { + u8 other_ch_status; + u8 error_state; + } usbcan; + }; +}; + +struct kvaser_usb_net_leaf_priv { + struct kvaser_usb_net_priv *net; + + struct delayed_work chip_state_req_work; +}; + +static const struct can_bittiming_const kvaser_usb_leaf_m16c_bittiming_const = { + .name = "kvaser_usb_ucii", + .tseg1_min = 4, + .tseg1_max = 16, + .tseg2_min = 2, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 16, + .brp_inc = 1, +}; + +static const struct can_bittiming_const kvaser_usb_leaf_m32c_bittiming_const = { + .name = "kvaser_usb_leaf", + .tseg1_min = 3, + .tseg1_max = 16, + .tseg2_min = 2, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 2, + .brp_max = 128, + .brp_inc = 2, +}; + +static const struct kvaser_usb_dev_cfg kvaser_usb_leaf_usbcan_dev_cfg = { + .clock = { + .freq = 8000000, + }, + .timestamp_freq = 1, + .bittiming_const = &kvaser_usb_leaf_m16c_bittiming_const, +}; + +static const struct kvaser_usb_dev_cfg kvaser_usb_leaf_m32c_dev_cfg = { + .clock = { + .freq = 16000000, + }, + .timestamp_freq = 1, + .bittiming_const = &kvaser_usb_leaf_m32c_bittiming_const, +}; + +static const struct kvaser_usb_dev_cfg kvaser_usb_leaf_imx_dev_cfg_16mhz = { + .clock = { + .freq = 16000000, + }, + .timestamp_freq = 1, + .bittiming_const = &kvaser_usb_flexc_bittiming_const, +}; + +static const struct kvaser_usb_dev_cfg kvaser_usb_leaf_imx_dev_cfg_24mhz = { + .clock = { + .freq = 24000000, + }, + .timestamp_freq = 1, + .bittiming_const = &kvaser_usb_flexc_bittiming_const, +}; + +static const struct kvaser_usb_dev_cfg kvaser_usb_leaf_imx_dev_cfg_32mhz = { + .clock = { + .freq = 32000000, + }, + .timestamp_freq = 1, + .bittiming_const = &kvaser_usb_flexc_bittiming_const, +}; + +static int kvaser_usb_leaf_verify_size(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + /* buffer size >= cmd->len ensured by caller */ + u8 min_size = 0; + + switch (dev->driver_info->family) { + case KVASER_LEAF: + if (cmd->id < ARRAY_SIZE(kvaser_usb_leaf_cmd_sizes_leaf)) + min_size = kvaser_usb_leaf_cmd_sizes_leaf[cmd->id]; + break; + case KVASER_USBCAN: + if (cmd->id < ARRAY_SIZE(kvaser_usb_leaf_cmd_sizes_usbcan)) + min_size = kvaser_usb_leaf_cmd_sizes_usbcan[cmd->id]; + break; + } + + if (min_size == CMD_SIZE_ANY) + return 0; + + if (min_size) { + min_size += CMD_HEADER_LEN; + if (cmd->len >= min_size) + return 0; + + dev_err_ratelimited(&dev->intf->dev, + "Received command %u too short (size %u, needed %u)", + cmd->id, cmd->len, min_size); + return -EIO; + } + + dev_warn_ratelimited(&dev->intf->dev, + "Unhandled command (%d, size %d)\n", + cmd->id, cmd->len); + return -EINVAL; +} + +static void * +kvaser_usb_leaf_frame_to_cmd(const struct kvaser_usb_net_priv *priv, + const struct sk_buff *skb, int *frame_len, + int *cmd_len, u16 transid) +{ + struct kvaser_usb *dev = priv->dev; + struct kvaser_cmd *cmd; + u8 *cmd_tx_can_flags = NULL; /* GCC */ + struct can_frame *cf = (struct can_frame *)skb->data; + + *frame_len = cf->can_dlc; + + cmd = kmalloc(sizeof(*cmd), GFP_ATOMIC); + if (cmd) { + cmd->u.tx_can.tid = transid & 0xff; + cmd->len = *cmd_len = CMD_HEADER_LEN + + sizeof(struct kvaser_cmd_tx_can); + cmd->u.tx_can.channel = priv->channel; + + switch (dev->driver_info->family) { + case KVASER_LEAF: + cmd_tx_can_flags = &cmd->u.tx_can.leaf.flags; + break; + case KVASER_USBCAN: + cmd_tx_can_flags = &cmd->u.tx_can.usbcan.flags; + break; + } + + *cmd_tx_can_flags = 0; + + if (cf->can_id & CAN_EFF_FLAG) { + cmd->id = CMD_TX_EXT_MESSAGE; + cmd->u.tx_can.data[0] = (cf->can_id >> 24) & 0x1f; + cmd->u.tx_can.data[1] = (cf->can_id >> 18) & 0x3f; + cmd->u.tx_can.data[2] = (cf->can_id >> 14) & 0x0f; + cmd->u.tx_can.data[3] = (cf->can_id >> 6) & 0xff; + cmd->u.tx_can.data[4] = cf->can_id & 0x3f; + } else { + cmd->id = CMD_TX_STD_MESSAGE; + cmd->u.tx_can.data[0] = (cf->can_id >> 6) & 0x1f; + cmd->u.tx_can.data[1] = cf->can_id & 0x3f; + } + + cmd->u.tx_can.data[5] = cf->can_dlc; + memcpy(&cmd->u.tx_can.data[6], cf->data, cf->can_dlc); + + if (cf->can_id & CAN_RTR_FLAG) + *cmd_tx_can_flags |= MSG_FLAG_REMOTE_FRAME; + } + return cmd; +} + +static int kvaser_usb_leaf_wait_cmd(const struct kvaser_usb *dev, u8 id, + struct kvaser_cmd *cmd) +{ + struct kvaser_cmd *tmp; + void *buf; + int actual_len; + int err; + int pos; + unsigned long to = jiffies + msecs_to_jiffies(KVASER_USB_TIMEOUT); + + buf = kzalloc(KVASER_USB_RX_BUFFER_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + do { + err = kvaser_usb_recv_cmd(dev, buf, KVASER_USB_RX_BUFFER_SIZE, + &actual_len); + if (err < 0) + goto end; + + pos = 0; + while (pos <= actual_len - CMD_HEADER_LEN) { + tmp = buf + pos; + + /* Handle commands crossing the USB endpoint max packet + * size boundary. Check kvaser_usb_read_bulk_callback() + * for further details. + */ + if (tmp->len == 0) { + pos = round_up(pos, + le16_to_cpu + (dev->bulk_in->wMaxPacketSize)); + continue; + } + + if (pos + tmp->len > actual_len) { + dev_err_ratelimited(&dev->intf->dev, + "Format error\n"); + break; + } + + if (tmp->id == id) { + memcpy(cmd, tmp, tmp->len); + goto end; + } + + pos += tmp->len; + } + } while (time_before(jiffies, to)); + + err = -EINVAL; + +end: + kfree(buf); + + if (err == 0) + err = kvaser_usb_leaf_verify_size(dev, cmd); + + return err; +} + +static int kvaser_usb_leaf_send_simple_cmd(const struct kvaser_usb *dev, + u8 cmd_id, int channel) +{ + struct kvaser_cmd *cmd; + int rc; + + cmd = kmalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->id = cmd_id; + cmd->len = CMD_HEADER_LEN + sizeof(struct kvaser_cmd_simple); + cmd->u.simple.channel = channel; + cmd->u.simple.tid = 0xff; + + rc = kvaser_usb_send_cmd(dev, cmd, cmd->len); + + kfree(cmd); + return rc; +} + +static void kvaser_usb_leaf_get_software_info_leaf(struct kvaser_usb *dev, + const struct leaf_cmd_softinfo *softinfo) +{ + u32 sw_options = le32_to_cpu(softinfo->sw_options); + + dev->fw_version = le32_to_cpu(softinfo->fw_version); + dev->max_tx_urbs = le16_to_cpu(softinfo->max_outstanding_tx); + + if (sw_options & KVASER_USB_LEAF_SWOPTION_EXT_CAP) + dev->card_data.capabilities |= KVASER_USB_CAP_EXT_CAP; + + if (dev->driver_info->quirks & KVASER_USB_QUIRK_IGNORE_CLK_FREQ) { + /* Firmware expects bittiming parameters calculated for 16MHz + * clock, regardless of the actual clock + */ + dev->cfg = &kvaser_usb_leaf_m32c_dev_cfg; + } else { + switch (sw_options & KVASER_USB_LEAF_SWOPTION_FREQ_MASK) { + case KVASER_USB_LEAF_SWOPTION_FREQ_16_MHZ_CLK: + dev->cfg = &kvaser_usb_leaf_imx_dev_cfg_16mhz; + break; + case KVASER_USB_LEAF_SWOPTION_FREQ_24_MHZ_CLK: + dev->cfg = &kvaser_usb_leaf_imx_dev_cfg_24mhz; + break; + case KVASER_USB_LEAF_SWOPTION_FREQ_32_MHZ_CLK: + dev->cfg = &kvaser_usb_leaf_imx_dev_cfg_32mhz; + break; + } + } +} + +static int kvaser_usb_leaf_get_software_info_inner(struct kvaser_usb *dev) +{ + struct kvaser_cmd cmd; + int err; + + err = kvaser_usb_leaf_send_simple_cmd(dev, CMD_GET_SOFTWARE_INFO, 0); + if (err) + return err; + + err = kvaser_usb_leaf_wait_cmd(dev, CMD_GET_SOFTWARE_INFO_REPLY, &cmd); + if (err) + return err; + + switch (dev->driver_info->family) { + case KVASER_LEAF: + kvaser_usb_leaf_get_software_info_leaf(dev, &cmd.u.leaf.softinfo); + break; + case KVASER_USBCAN: + dev->fw_version = le32_to_cpu(cmd.u.usbcan.softinfo.fw_version); + dev->max_tx_urbs = + le16_to_cpu(cmd.u.usbcan.softinfo.max_outstanding_tx); + dev->cfg = &kvaser_usb_leaf_usbcan_dev_cfg; + break; + } + + return 0; +} + +static int kvaser_usb_leaf_get_software_info(struct kvaser_usb *dev) +{ + int err; + int retry = 3; + + /* On some x86 laptops, plugging a Kvaser device again after + * an unplug makes the firmware always ignore the very first + * command. For such a case, provide some room for retries + * instead of completely exiting the driver. + */ + do { + err = kvaser_usb_leaf_get_software_info_inner(dev); + } while (--retry && err == -ETIMEDOUT); + + return err; +} + +static int kvaser_usb_leaf_get_card_info(struct kvaser_usb *dev) +{ + struct kvaser_cmd cmd; + int err; + + err = kvaser_usb_leaf_send_simple_cmd(dev, CMD_GET_CARD_INFO, 0); + if (err) + return err; + + err = kvaser_usb_leaf_wait_cmd(dev, CMD_GET_CARD_INFO_REPLY, &cmd); + if (err) + return err; + + dev->nchannels = cmd.u.cardinfo.nchannels; + if (dev->nchannels > KVASER_USB_MAX_NET_DEVICES || + (dev->driver_info->family == KVASER_USBCAN && + dev->nchannels > MAX_USBCAN_NET_DEVICES)) + return -EINVAL; + + return 0; +} + +static int kvaser_usb_leaf_get_single_capability(struct kvaser_usb *dev, + u16 cap_cmd_req, u16 *status) +{ + struct kvaser_usb_dev_card_data *card_data = &dev->card_data; + struct kvaser_cmd *cmd; + u32 value = 0; + u32 mask = 0; + u16 cap_cmd_res; + int err; + int i; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->id = CMD_GET_CAPABILITIES_REQ; + cmd->u.leaf.cap_req.cap_cmd = cpu_to_le16(cap_cmd_req); + cmd->len = CMD_HEADER_LEN + sizeof(struct kvaser_cmd_cap_req); + + err = kvaser_usb_send_cmd(dev, cmd, cmd->len); + if (err) + goto end; + + err = kvaser_usb_leaf_wait_cmd(dev, CMD_GET_CAPABILITIES_RESP, cmd); + if (err) + goto end; + + *status = le16_to_cpu(cmd->u.leaf.cap_res.status); + + if (*status != KVASER_USB_LEAF_CAP_STAT_OK) + goto end; + + cap_cmd_res = le16_to_cpu(cmd->u.leaf.cap_res.cap_cmd); + switch (cap_cmd_res) { + case KVASER_USB_LEAF_CAP_CMD_LISTEN_MODE: + case KVASER_USB_LEAF_CAP_CMD_ERR_REPORT: + value = le32_to_cpu(cmd->u.leaf.cap_res.value); + mask = le32_to_cpu(cmd->u.leaf.cap_res.mask); + break; + default: + dev_warn(&dev->intf->dev, "Unknown capability command %u\n", + cap_cmd_res); + break; + } + + for (i = 0; i < dev->nchannels; i++) { + if (BIT(i) & (value & mask)) { + switch (cap_cmd_res) { + case KVASER_USB_LEAF_CAP_CMD_LISTEN_MODE: + card_data->ctrlmode_supported |= + CAN_CTRLMODE_LISTENONLY; + break; + case KVASER_USB_LEAF_CAP_CMD_ERR_REPORT: + card_data->capabilities |= + KVASER_USB_CAP_BERR_CAP; + break; + } + } + } + +end: + kfree(cmd); + + return err; +} + +static int kvaser_usb_leaf_get_capabilities_leaf(struct kvaser_usb *dev) +{ + int err; + u16 status; + + if (!(dev->card_data.capabilities & KVASER_USB_CAP_EXT_CAP)) { + dev_info(&dev->intf->dev, + "No extended capability support. Upgrade device firmware.\n"); + return 0; + } + + err = kvaser_usb_leaf_get_single_capability(dev, + KVASER_USB_LEAF_CAP_CMD_LISTEN_MODE, + &status); + if (err) + return err; + if (status) + dev_info(&dev->intf->dev, + "KVASER_USB_LEAF_CAP_CMD_LISTEN_MODE failed %u\n", + status); + + err = kvaser_usb_leaf_get_single_capability(dev, + KVASER_USB_LEAF_CAP_CMD_ERR_REPORT, + &status); + if (err) + return err; + if (status) + dev_info(&dev->intf->dev, + "KVASER_USB_LEAF_CAP_CMD_ERR_REPORT failed %u\n", + status); + + return 0; +} + +static int kvaser_usb_leaf_get_capabilities(struct kvaser_usb *dev) +{ + int err = 0; + + if (dev->driver_info->family == KVASER_LEAF) + err = kvaser_usb_leaf_get_capabilities_leaf(dev); + + return err; +} + +static void kvaser_usb_leaf_tx_acknowledge(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct net_device_stats *stats; + struct kvaser_usb_tx_urb_context *context; + struct kvaser_usb_net_priv *priv; + unsigned long flags; + u8 channel, tid; + + channel = cmd->u.tx_acknowledge_header.channel; + tid = cmd->u.tx_acknowledge_header.tid; + + if (channel >= dev->nchannels) { + dev_err(&dev->intf->dev, + "Invalid channel number (%d)\n", channel); + return; + } + + priv = dev->nets[channel]; + + if (!netif_device_present(priv->netdev)) + return; + + stats = &priv->netdev->stats; + + context = &priv->tx_contexts[tid % dev->max_tx_urbs]; + + /* Sometimes the state change doesn't come after a bus-off event */ + if (priv->can.restart_ms && priv->can.state == CAN_STATE_BUS_OFF) { + struct sk_buff *skb; + struct can_frame *cf; + + skb = alloc_can_err_skb(priv->netdev, &cf); + if (skb) { + cf->can_id |= CAN_ERR_RESTARTED; + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + } else { + netdev_err(priv->netdev, + "No memory left for err_skb\n"); + } + + priv->can.can_stats.restarts++; + netif_carrier_on(priv->netdev); + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + } + + stats->tx_packets++; + stats->tx_bytes += context->dlc; + + spin_lock_irqsave(&priv->tx_contexts_lock, flags); + + can_get_echo_skb(priv->netdev, context->echo_index); + context->echo_index = dev->max_tx_urbs; + --priv->active_tx_contexts; + netif_wake_queue(priv->netdev); + + spin_unlock_irqrestore(&priv->tx_contexts_lock, flags); +} + +static int kvaser_usb_leaf_simple_cmd_async(struct kvaser_usb_net_priv *priv, + u8 cmd_id) +{ + struct kvaser_cmd *cmd; + int err; + + cmd = kzalloc(sizeof(*cmd), GFP_ATOMIC); + if (!cmd) + return -ENOMEM; + + cmd->len = CMD_HEADER_LEN + sizeof(struct kvaser_cmd_simple); + cmd->id = cmd_id; + cmd->u.simple.channel = priv->channel; + + err = kvaser_usb_send_cmd_async(priv, cmd, cmd->len); + if (err) + kfree(cmd); + + return err; +} + +static void kvaser_usb_leaf_chip_state_req_work(struct work_struct *work) +{ + struct kvaser_usb_net_leaf_priv *leaf = + container_of(work, struct kvaser_usb_net_leaf_priv, + chip_state_req_work.work); + struct kvaser_usb_net_priv *priv = leaf->net; + + kvaser_usb_leaf_simple_cmd_async(priv, CMD_GET_CHIP_STATE); +} + +static void +kvaser_usb_leaf_rx_error_update_can_state(struct kvaser_usb_net_priv *priv, + const struct kvaser_usb_err_summary *es, + struct can_frame *cf) +{ + struct kvaser_usb *dev = priv->dev; + struct net_device_stats *stats = &priv->netdev->stats; + enum can_state cur_state, new_state, tx_state, rx_state; + + netdev_dbg(priv->netdev, "Error status: 0x%02x\n", es->status); + + new_state = priv->can.state; + cur_state = priv->can.state; + + if (es->status & (M16C_STATE_BUS_OFF | M16C_STATE_BUS_RESET)) { + new_state = CAN_STATE_BUS_OFF; + } else if (es->status & M16C_STATE_BUS_PASSIVE) { + new_state = CAN_STATE_ERROR_PASSIVE; + } else if ((es->status & M16C_STATE_BUS_ERROR) && + cur_state >= CAN_STATE_BUS_OFF) { + /* Guard against spurious error events after a busoff */ + } else if (es->txerr >= 128 || es->rxerr >= 128) { + new_state = CAN_STATE_ERROR_PASSIVE; + } else if (es->txerr >= 96 || es->rxerr >= 96) { + new_state = CAN_STATE_ERROR_WARNING; + } else { + new_state = CAN_STATE_ERROR_ACTIVE; + } + + if (new_state != cur_state) { + tx_state = (es->txerr >= es->rxerr) ? new_state : 0; + rx_state = (es->txerr <= es->rxerr) ? new_state : 0; + + can_change_state(priv->netdev, cf, tx_state, rx_state); + } + + if (priv->can.restart_ms && + cur_state == CAN_STATE_BUS_OFF && + new_state < CAN_STATE_BUS_OFF) + priv->can.can_stats.restarts++; + + switch (dev->driver_info->family) { + case KVASER_LEAF: + if (es->leaf.error_factor) { + priv->can.can_stats.bus_error++; + stats->rx_errors++; + } + break; + case KVASER_USBCAN: + if (es->usbcan.error_state & USBCAN_ERROR_STATE_TX_ERROR) + stats->tx_errors++; + if (es->usbcan.error_state & USBCAN_ERROR_STATE_RX_ERROR) + stats->rx_errors++; + if (es->usbcan.error_state & USBCAN_ERROR_STATE_BUSERROR) + priv->can.can_stats.bus_error++; + break; + } + + priv->bec.txerr = es->txerr; + priv->bec.rxerr = es->rxerr; +} + +static void kvaser_usb_leaf_rx_error(const struct kvaser_usb *dev, + const struct kvaser_usb_err_summary *es) +{ + struct can_frame *cf; + struct can_frame tmp_cf = { .can_id = CAN_ERR_FLAG, + .can_dlc = CAN_ERR_DLC }; + struct sk_buff *skb; + struct net_device_stats *stats; + struct kvaser_usb_net_priv *priv; + struct kvaser_usb_net_leaf_priv *leaf; + enum can_state old_state, new_state; + + if (es->channel >= dev->nchannels) { + dev_err(&dev->intf->dev, + "Invalid channel number (%d)\n", es->channel); + return; + } + + priv = dev->nets[es->channel]; + leaf = priv->sub_priv; + stats = &priv->netdev->stats; + + /* Ignore e.g. state change to bus-off reported just after stopping */ + if (!netif_running(priv->netdev)) + return; + + /* Update all of the CAN interface's state and error counters before + * trying any memory allocation that can actually fail with -ENOMEM. + * + * We send a temporary stack-allocated error CAN frame to + * can_change_state() for the very same reason. + * + * TODO: Split can_change_state() responsibility between updating the + * CAN interface's state and counters, and the setting up of CAN error + * frame ID and data to userspace. Remove stack allocation afterwards. + */ + old_state = priv->can.state; + kvaser_usb_leaf_rx_error_update_can_state(priv, es, &tmp_cf); + new_state = priv->can.state; + + /* If there are errors, request status updates periodically as we do + * not get automatic notifications of improved state. + */ + if (new_state < CAN_STATE_BUS_OFF && + (es->rxerr || es->txerr || new_state == CAN_STATE_ERROR_PASSIVE)) + schedule_delayed_work(&leaf->chip_state_req_work, + msecs_to_jiffies(500)); + + skb = alloc_can_err_skb(priv->netdev, &cf); + if (!skb) { + stats->rx_dropped++; + return; + } + memcpy(cf, &tmp_cf, sizeof(*cf)); + + if (new_state != old_state) { + if (es->status & + (M16C_STATE_BUS_OFF | M16C_STATE_BUS_RESET)) { + if (!priv->can.restart_ms) + kvaser_usb_leaf_simple_cmd_async(priv, + CMD_STOP_CHIP); + netif_carrier_off(priv->netdev); + } + + if (priv->can.restart_ms && + old_state == CAN_STATE_BUS_OFF && + new_state < CAN_STATE_BUS_OFF) { + cf->can_id |= CAN_ERR_RESTARTED; + netif_carrier_on(priv->netdev); + } + } + + switch (dev->driver_info->family) { + case KVASER_LEAF: + if (es->leaf.error_factor) { + cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT; + + if (es->leaf.error_factor & M16C_EF_ACKE) + cf->data[3] = CAN_ERR_PROT_LOC_ACK; + if (es->leaf.error_factor & M16C_EF_CRCE) + cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ; + if (es->leaf.error_factor & M16C_EF_FORME) + cf->data[2] |= CAN_ERR_PROT_FORM; + if (es->leaf.error_factor & M16C_EF_STFE) + cf->data[2] |= CAN_ERR_PROT_STUFF; + if (es->leaf.error_factor & M16C_EF_BITE0) + cf->data[2] |= CAN_ERR_PROT_BIT0; + if (es->leaf.error_factor & M16C_EF_BITE1) + cf->data[2] |= CAN_ERR_PROT_BIT1; + if (es->leaf.error_factor & M16C_EF_TRE) + cf->data[2] |= CAN_ERR_PROT_TX; + } + break; + case KVASER_USBCAN: + if (es->usbcan.error_state & USBCAN_ERROR_STATE_BUSERROR) + cf->can_id |= CAN_ERR_BUSERROR; + break; + } + + if (new_state != CAN_STATE_BUS_OFF) { + cf->data[6] = es->txerr; + cf->data[7] = es->rxerr; + } + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); +} + +/* For USBCAN, report error to userspace if the channels's errors counter + * has changed, or we're the only channel seeing a bus error state. + */ +static void +kvaser_usb_leaf_usbcan_conditionally_rx_error(const struct kvaser_usb *dev, + struct kvaser_usb_err_summary *es) +{ + struct kvaser_usb_net_priv *priv; + unsigned int channel; + bool report_error; + + channel = es->channel; + if (channel >= dev->nchannels) { + dev_err(&dev->intf->dev, + "Invalid channel number (%d)\n", channel); + return; + } + + priv = dev->nets[channel]; + report_error = false; + + if (es->txerr != priv->bec.txerr) { + es->usbcan.error_state |= USBCAN_ERROR_STATE_TX_ERROR; + report_error = true; + } + if (es->rxerr != priv->bec.rxerr) { + es->usbcan.error_state |= USBCAN_ERROR_STATE_RX_ERROR; + report_error = true; + } + if ((es->status & M16C_STATE_BUS_ERROR) && + !(es->usbcan.other_ch_status & M16C_STATE_BUS_ERROR)) { + es->usbcan.error_state |= USBCAN_ERROR_STATE_BUSERROR; + report_error = true; + } + + if (report_error) + kvaser_usb_leaf_rx_error(dev, es); +} + +static void kvaser_usb_leaf_usbcan_rx_error(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_err_summary es = { }; + + switch (cmd->id) { + /* Sometimes errors are sent as unsolicited chip state events */ + case CMD_CHIP_STATE_EVENT: + es.channel = cmd->u.usbcan.chip_state_event.channel; + es.status = cmd->u.usbcan.chip_state_event.status; + es.txerr = cmd->u.usbcan.chip_state_event.tx_errors_count; + es.rxerr = cmd->u.usbcan.chip_state_event.rx_errors_count; + kvaser_usb_leaf_usbcan_conditionally_rx_error(dev, &es); + break; + + case CMD_CAN_ERROR_EVENT: + es.channel = 0; + es.status = cmd->u.usbcan.can_error_event.status_ch0; + es.txerr = cmd->u.usbcan.can_error_event.tx_errors_count_ch0; + es.rxerr = cmd->u.usbcan.can_error_event.rx_errors_count_ch0; + es.usbcan.other_ch_status = + cmd->u.usbcan.can_error_event.status_ch1; + kvaser_usb_leaf_usbcan_conditionally_rx_error(dev, &es); + + /* The USBCAN firmware supports up to 2 channels. + * Now that ch0 was checked, check if ch1 has any errors. + */ + if (dev->nchannels == MAX_USBCAN_NET_DEVICES) { + es.channel = 1; + es.status = cmd->u.usbcan.can_error_event.status_ch1; + es.txerr = + cmd->u.usbcan.can_error_event.tx_errors_count_ch1; + es.rxerr = + cmd->u.usbcan.can_error_event.rx_errors_count_ch1; + es.usbcan.other_ch_status = + cmd->u.usbcan.can_error_event.status_ch0; + kvaser_usb_leaf_usbcan_conditionally_rx_error(dev, &es); + } + break; + + default: + dev_err(&dev->intf->dev, "Invalid cmd id (%d)\n", cmd->id); + } +} + +static void kvaser_usb_leaf_leaf_rx_error(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_err_summary es = { }; + + switch (cmd->id) { + case CMD_CAN_ERROR_EVENT: + es.channel = cmd->u.leaf.can_error_event.channel; + es.status = cmd->u.leaf.can_error_event.status; + es.txerr = cmd->u.leaf.can_error_event.tx_errors_count; + es.rxerr = cmd->u.leaf.can_error_event.rx_errors_count; + es.leaf.error_factor = cmd->u.leaf.can_error_event.error_factor; + break; + case CMD_LEAF_LOG_MESSAGE: + es.channel = cmd->u.leaf.log_message.channel; + es.status = cmd->u.leaf.log_message.data[0]; + es.txerr = cmd->u.leaf.log_message.data[2]; + es.rxerr = cmd->u.leaf.log_message.data[3]; + es.leaf.error_factor = cmd->u.leaf.log_message.data[1]; + break; + case CMD_CHIP_STATE_EVENT: + es.channel = cmd->u.leaf.chip_state_event.channel; + es.status = cmd->u.leaf.chip_state_event.status; + es.txerr = cmd->u.leaf.chip_state_event.tx_errors_count; + es.rxerr = cmd->u.leaf.chip_state_event.rx_errors_count; + es.leaf.error_factor = 0; + break; + default: + dev_err(&dev->intf->dev, "Invalid cmd id (%d)\n", cmd->id); + return; + } + + kvaser_usb_leaf_rx_error(dev, &es); +} + +static void kvaser_usb_leaf_rx_can_err(const struct kvaser_usb_net_priv *priv, + const struct kvaser_cmd *cmd) +{ + if (cmd->u.rx_can_header.flag & (MSG_FLAG_ERROR_FRAME | + MSG_FLAG_NERR)) { + struct net_device_stats *stats = &priv->netdev->stats; + + netdev_err(priv->netdev, "Unknown error (flags: 0x%02x)\n", + cmd->u.rx_can_header.flag); + + stats->rx_errors++; + return; + } + + if (cmd->u.rx_can_header.flag & MSG_FLAG_OVERRUN) + kvaser_usb_can_rx_over_error(priv->netdev); +} + +static void kvaser_usb_leaf_rx_can_msg(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_net_priv *priv; + struct can_frame *cf; + struct sk_buff *skb; + struct net_device_stats *stats; + u8 channel = cmd->u.rx_can_header.channel; + const u8 *rx_data = NULL; /* GCC */ + + if (channel >= dev->nchannels) { + dev_err(&dev->intf->dev, + "Invalid channel number (%d)\n", channel); + return; + } + + priv = dev->nets[channel]; + stats = &priv->netdev->stats; + + if ((cmd->u.rx_can_header.flag & MSG_FLAG_ERROR_FRAME) && + (dev->driver_info->family == KVASER_LEAF && + cmd->id == CMD_LEAF_LOG_MESSAGE)) { + kvaser_usb_leaf_leaf_rx_error(dev, cmd); + return; + } else if (cmd->u.rx_can_header.flag & (MSG_FLAG_ERROR_FRAME | + MSG_FLAG_NERR | + MSG_FLAG_OVERRUN)) { + kvaser_usb_leaf_rx_can_err(priv, cmd); + return; + } else if (cmd->u.rx_can_header.flag & ~MSG_FLAG_REMOTE_FRAME) { + netdev_warn(priv->netdev, + "Unhandled frame (flags: 0x%02x)\n", + cmd->u.rx_can_header.flag); + return; + } + + switch (dev->driver_info->family) { + case KVASER_LEAF: + rx_data = cmd->u.leaf.rx_can.data; + break; + case KVASER_USBCAN: + rx_data = cmd->u.usbcan.rx_can.data; + break; + } + + skb = alloc_can_skb(priv->netdev, &cf); + if (!skb) { + stats->rx_dropped++; + return; + } + + if (dev->driver_info->family == KVASER_LEAF && cmd->id == + CMD_LEAF_LOG_MESSAGE) { + cf->can_id = le32_to_cpu(cmd->u.leaf.log_message.id); + if (cf->can_id & KVASER_EXTENDED_FRAME) + cf->can_id &= CAN_EFF_MASK | CAN_EFF_FLAG; + else + cf->can_id &= CAN_SFF_MASK; + + cf->can_dlc = get_can_dlc(cmd->u.leaf.log_message.dlc); + + if (cmd->u.leaf.log_message.flags & MSG_FLAG_REMOTE_FRAME) + cf->can_id |= CAN_RTR_FLAG; + else + memcpy(cf->data, &cmd->u.leaf.log_message.data, + cf->can_dlc); + } else { + cf->can_id = ((rx_data[0] & 0x1f) << 6) | (rx_data[1] & 0x3f); + + if (cmd->id == CMD_RX_EXT_MESSAGE) { + cf->can_id <<= 18; + cf->can_id |= ((rx_data[2] & 0x0f) << 14) | + ((rx_data[3] & 0xff) << 6) | + (rx_data[4] & 0x3f); + cf->can_id |= CAN_EFF_FLAG; + } + + cf->can_dlc = get_can_dlc(rx_data[5]); + + if (cmd->u.rx_can_header.flag & MSG_FLAG_REMOTE_FRAME) + cf->can_id |= CAN_RTR_FLAG; + else + memcpy(cf->data, &rx_data[6], cf->can_dlc); + } + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); +} + +static void kvaser_usb_leaf_error_event_parameter(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + u16 info1 = 0; + + switch (dev->driver_info->family) { + case KVASER_LEAF: + info1 = le16_to_cpu(cmd->u.leaf.error_event.info1); + break; + case KVASER_USBCAN: + info1 = le16_to_cpu(cmd->u.usbcan.error_event.info1); + break; + } + + /* info1 will contain the offending cmd_no */ + switch (info1) { + case CMD_SET_CTRL_MODE: + dev_warn(&dev->intf->dev, + "CMD_SET_CTRL_MODE error in parameter\n"); + break; + + case CMD_SET_BUS_PARAMS: + dev_warn(&dev->intf->dev, + "CMD_SET_BUS_PARAMS error in parameter\n"); + break; + + default: + dev_warn(&dev->intf->dev, + "Unhandled parameter error event cmd_no (%u)\n", + info1); + break; + } +} + +static void kvaser_usb_leaf_error_event(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + u8 error_code = 0; + + switch (dev->driver_info->family) { + case KVASER_LEAF: + error_code = cmd->u.leaf.error_event.error_code; + break; + case KVASER_USBCAN: + error_code = cmd->u.usbcan.error_event.error_code; + break; + } + + switch (error_code) { + case KVASER_USB_LEAF_ERROR_EVENT_TX_QUEUE_FULL: + /* Received additional CAN message, when firmware TX queue is + * already full. Something is wrong with the driver. + * This should never happen! + */ + dev_err(&dev->intf->dev, + "Received error event TX_QUEUE_FULL\n"); + break; + case KVASER_USB_LEAF_ERROR_EVENT_PARAM: + kvaser_usb_leaf_error_event_parameter(dev, cmd); + break; + + default: + dev_warn(&dev->intf->dev, + "Unhandled error event (%d)\n", error_code); + break; + } +} + +static void kvaser_usb_leaf_start_chip_reply(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_net_priv *priv; + u8 channel = cmd->u.simple.channel; + + if (channel >= dev->nchannels) { + dev_err(&dev->intf->dev, + "Invalid channel number (%d)\n", channel); + return; + } + + priv = dev->nets[channel]; + + if (completion_done(&priv->start_comp) && + netif_queue_stopped(priv->netdev)) { + netif_wake_queue(priv->netdev); + } else { + netif_start_queue(priv->netdev); + complete(&priv->start_comp); + } +} + +static void kvaser_usb_leaf_stop_chip_reply(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_net_priv *priv; + u8 channel = cmd->u.simple.channel; + + if (channel >= dev->nchannels) { + dev_err(&dev->intf->dev, + "Invalid channel number (%d)\n", channel); + return; + } + + priv = dev->nets[channel]; + + complete(&priv->stop_comp); +} + +static void kvaser_usb_leaf_get_busparams_reply(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_net_priv *priv; + u8 channel = cmd->u.busparams.channel; + + if (channel >= dev->nchannels) { + dev_err(&dev->intf->dev, + "Invalid channel number (%d)\n", channel); + return; + } + + priv = dev->nets[channel]; + memcpy(&priv->busparams_nominal, &cmd->u.busparams.busparams, + sizeof(priv->busparams_nominal)); + + complete(&priv->get_busparams_comp); +} + +static void kvaser_usb_leaf_handle_command(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + if (kvaser_usb_leaf_verify_size(dev, cmd) < 0) + return; + + switch (cmd->id) { + case CMD_START_CHIP_REPLY: + kvaser_usb_leaf_start_chip_reply(dev, cmd); + break; + + case CMD_STOP_CHIP_REPLY: + kvaser_usb_leaf_stop_chip_reply(dev, cmd); + break; + + case CMD_RX_STD_MESSAGE: + case CMD_RX_EXT_MESSAGE: + kvaser_usb_leaf_rx_can_msg(dev, cmd); + break; + + case CMD_LEAF_LOG_MESSAGE: + if (dev->driver_info->family != KVASER_LEAF) + goto warn; + kvaser_usb_leaf_rx_can_msg(dev, cmd); + break; + + case CMD_CHIP_STATE_EVENT: + case CMD_CAN_ERROR_EVENT: + if (dev->driver_info->family == KVASER_LEAF) + kvaser_usb_leaf_leaf_rx_error(dev, cmd); + else + kvaser_usb_leaf_usbcan_rx_error(dev, cmd); + break; + + case CMD_TX_ACKNOWLEDGE: + kvaser_usb_leaf_tx_acknowledge(dev, cmd); + break; + + case CMD_ERROR_EVENT: + kvaser_usb_leaf_error_event(dev, cmd); + break; + + case CMD_GET_BUS_PARAMS_REPLY: + kvaser_usb_leaf_get_busparams_reply(dev, cmd); + break; + + /* Ignored commands */ + case CMD_USBCAN_CLOCK_OVERFLOW_EVENT: + if (dev->driver_info->family != KVASER_USBCAN) + goto warn; + break; + + case CMD_FLUSH_QUEUE_REPLY: + if (dev->driver_info->family != KVASER_LEAF) + goto warn; + break; + + default: +warn: dev_warn(&dev->intf->dev, "Unhandled command (%d)\n", cmd->id); + break; + } +} + +static void kvaser_usb_leaf_read_bulk_callback(struct kvaser_usb *dev, + void *buf, int len) +{ + struct kvaser_cmd *cmd; + int pos = 0; + + while (pos <= len - CMD_HEADER_LEN) { + cmd = buf + pos; + + /* The Kvaser firmware can only read and write commands that + * does not cross the USB's endpoint wMaxPacketSize boundary. + * If a follow-up command crosses such boundary, firmware puts + * a placeholder zero-length command in its place then aligns + * the real command to the next max packet size. + * + * Handle such cases or we're going to miss a significant + * number of events in case of a heavy rx load on the bus. + */ + if (cmd->len == 0) { + pos = round_up(pos, le16_to_cpu + (dev->bulk_in->wMaxPacketSize)); + continue; + } + + if (pos + cmd->len > len) { + dev_err_ratelimited(&dev->intf->dev, "Format error\n"); + break; + } + + kvaser_usb_leaf_handle_command(dev, cmd); + pos += cmd->len; + } +} + +static int kvaser_usb_leaf_set_opt_mode(const struct kvaser_usb_net_priv *priv) +{ + struct kvaser_cmd *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->id = CMD_SET_CTRL_MODE; + cmd->len = CMD_HEADER_LEN + sizeof(struct kvaser_cmd_ctrl_mode); + cmd->u.ctrl_mode.tid = 0xff; + cmd->u.ctrl_mode.channel = priv->channel; + + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + cmd->u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_SILENT; + else + cmd->u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_NORMAL; + + rc = kvaser_usb_send_cmd(priv->dev, cmd, cmd->len); + + kfree(cmd); + return rc; +} + +static int kvaser_usb_leaf_start_chip(struct kvaser_usb_net_priv *priv) +{ + int err; + + reinit_completion(&priv->start_comp); + + err = kvaser_usb_leaf_send_simple_cmd(priv->dev, CMD_START_CHIP, + priv->channel); + if (err) + return err; + + if (!wait_for_completion_timeout(&priv->start_comp, + msecs_to_jiffies(KVASER_USB_TIMEOUT))) + return -ETIMEDOUT; + + return 0; +} + +static int kvaser_usb_leaf_stop_chip(struct kvaser_usb_net_priv *priv) +{ + struct kvaser_usb_net_leaf_priv *leaf = priv->sub_priv; + int err; + + reinit_completion(&priv->stop_comp); + + cancel_delayed_work(&leaf->chip_state_req_work); + + err = kvaser_usb_leaf_send_simple_cmd(priv->dev, CMD_STOP_CHIP, + priv->channel); + if (err) + return err; + + if (!wait_for_completion_timeout(&priv->stop_comp, + msecs_to_jiffies(KVASER_USB_TIMEOUT))) + return -ETIMEDOUT; + + return 0; +} + +static int kvaser_usb_leaf_reset_chip(struct kvaser_usb *dev, int channel) +{ + return kvaser_usb_leaf_send_simple_cmd(dev, CMD_RESET_CHIP, channel); +} + +static int kvaser_usb_leaf_flush_queue(struct kvaser_usb_net_priv *priv) +{ + struct kvaser_cmd *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->id = CMD_FLUSH_QUEUE; + cmd->len = CMD_HEADER_LEN + sizeof(struct kvaser_cmd_flush_queue); + cmd->u.flush_queue.channel = priv->channel; + cmd->u.flush_queue.flags = 0x00; + + rc = kvaser_usb_send_cmd(priv->dev, cmd, cmd->len); + + kfree(cmd); + return rc; +} + +static int kvaser_usb_leaf_init_card(struct kvaser_usb *dev) +{ + struct kvaser_usb_dev_card_data *card_data = &dev->card_data; + + card_data->ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES; + + return 0; +} + +static int kvaser_usb_leaf_init_channel(struct kvaser_usb_net_priv *priv) +{ + struct kvaser_usb_net_leaf_priv *leaf; + + leaf = devm_kzalloc(&priv->dev->intf->dev, sizeof(*leaf), GFP_KERNEL); + if (!leaf) + return -ENOMEM; + + leaf->net = priv; + INIT_DELAYED_WORK(&leaf->chip_state_req_work, + kvaser_usb_leaf_chip_state_req_work); + + priv->sub_priv = leaf; + + return 0; +} + +static void kvaser_usb_leaf_remove_channel(struct kvaser_usb_net_priv *priv) +{ + struct kvaser_usb_net_leaf_priv *leaf = priv->sub_priv; + + if (leaf) + cancel_delayed_work_sync(&leaf->chip_state_req_work); +} + +static int kvaser_usb_leaf_set_bittiming(const struct net_device *netdev, + const struct kvaser_usb_busparams *busparams) +{ + struct kvaser_usb_net_priv *priv = netdev_priv(netdev); + struct kvaser_usb *dev = priv->dev; + struct kvaser_cmd *cmd; + int rc; + + cmd = kmalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->id = CMD_SET_BUS_PARAMS; + cmd->len = CMD_HEADER_LEN + sizeof(struct kvaser_cmd_busparams); + cmd->u.busparams.channel = priv->channel; + cmd->u.busparams.tid = 0xff; + memcpy(&cmd->u.busparams.busparams, busparams, + sizeof(cmd->u.busparams.busparams)); + + rc = kvaser_usb_send_cmd(dev, cmd, cmd->len); + + kfree(cmd); + return rc; +} + +static int kvaser_usb_leaf_get_busparams(struct kvaser_usb_net_priv *priv) +{ + int err; + + if (priv->dev->driver_info->family == KVASER_USBCAN) + return -EOPNOTSUPP; + + reinit_completion(&priv->get_busparams_comp); + + err = kvaser_usb_leaf_send_simple_cmd(priv->dev, CMD_GET_BUS_PARAMS, + priv->channel); + if (err) + return err; + + if (!wait_for_completion_timeout(&priv->get_busparams_comp, + msecs_to_jiffies(KVASER_USB_TIMEOUT))) + return -ETIMEDOUT; + + return 0; +} + +static int kvaser_usb_leaf_set_mode(struct net_device *netdev, + enum can_mode mode) +{ + struct kvaser_usb_net_priv *priv = netdev_priv(netdev); + int err; + + switch (mode) { + case CAN_MODE_START: + kvaser_usb_unlink_tx_urbs(priv); + + err = kvaser_usb_leaf_simple_cmd_async(priv, CMD_START_CHIP); + if (err) + return err; + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int kvaser_usb_leaf_get_berr_counter(const struct net_device *netdev, + struct can_berr_counter *bec) +{ + struct kvaser_usb_net_priv *priv = netdev_priv(netdev); + + *bec = priv->bec; + + return 0; +} + +static int kvaser_usb_leaf_setup_endpoints(struct kvaser_usb *dev) +{ + const struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + int i; + + iface_desc = dev->intf->cur_altsetting; + + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + + if (!dev->bulk_in && usb_endpoint_is_bulk_in(endpoint)) + dev->bulk_in = endpoint; + + if (!dev->bulk_out && usb_endpoint_is_bulk_out(endpoint)) + dev->bulk_out = endpoint; + + /* use first bulk endpoint for in and out */ + if (dev->bulk_in && dev->bulk_out) + return 0; + } + + return -ENODEV; +} + +const struct kvaser_usb_dev_ops kvaser_usb_leaf_dev_ops = { + .dev_set_mode = kvaser_usb_leaf_set_mode, + .dev_set_bittiming = kvaser_usb_leaf_set_bittiming, + .dev_get_busparams = kvaser_usb_leaf_get_busparams, + .dev_set_data_bittiming = NULL, + .dev_get_data_busparams = NULL, + .dev_get_berr_counter = kvaser_usb_leaf_get_berr_counter, + .dev_setup_endpoints = kvaser_usb_leaf_setup_endpoints, + .dev_init_card = kvaser_usb_leaf_init_card, + .dev_init_channel = kvaser_usb_leaf_init_channel, + .dev_remove_channel = kvaser_usb_leaf_remove_channel, + .dev_get_software_info = kvaser_usb_leaf_get_software_info, + .dev_get_software_details = NULL, + .dev_get_card_info = kvaser_usb_leaf_get_card_info, + .dev_get_capabilities = kvaser_usb_leaf_get_capabilities, + .dev_set_opt_mode = kvaser_usb_leaf_set_opt_mode, + .dev_start_chip = kvaser_usb_leaf_start_chip, + .dev_stop_chip = kvaser_usb_leaf_stop_chip, + .dev_reset_chip = kvaser_usb_leaf_reset_chip, + .dev_flush_queue = kvaser_usb_leaf_flush_queue, + .dev_read_bulk_callback = kvaser_usb_leaf_read_bulk_callback, + .dev_frame_to_cmd = kvaser_usb_leaf_frame_to_cmd, +}; diff --git a/drivers/net/can/usb/mcba_usb.c b/drivers/net/can/usb/mcba_usb.c new file mode 100644 index 000000000..c07e32792 --- /dev/null +++ b/drivers/net/can/usb/mcba_usb.c @@ -0,0 +1,918 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* SocketCAN driver for Microchip CAN BUS Analyzer Tool + * + * Copyright (C) 2017 Mobica Limited + * + * This driver is inspired by the 4.6.2 version of net/can/usb/usb_8dev.c + */ + +#include <asm/unaligned.h> +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> +#include <linux/can/led.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/signal.h> +#include <linux/slab.h> +#include <linux/usb.h> + +/* vendor and product id */ +#define MCBA_MODULE_NAME "mcba_usb" +#define MCBA_VENDOR_ID 0x04d8 +#define MCBA_PRODUCT_ID 0x0a30 + +/* driver constants */ +#define MCBA_MAX_RX_URBS 20 +#define MCBA_MAX_TX_URBS 20 +#define MCBA_CTX_FREE MCBA_MAX_TX_URBS + +/* RX buffer must be bigger than msg size since at the + * beginning USB messages are stacked. + */ +#define MCBA_USB_RX_BUFF_SIZE 64 +#define MCBA_USB_TX_BUFF_SIZE (sizeof(struct mcba_usb_msg)) + +/* Microchip command id */ +#define MBCA_CMD_RECEIVE_MESSAGE 0xE3 +#define MBCA_CMD_I_AM_ALIVE_FROM_CAN 0xF5 +#define MBCA_CMD_I_AM_ALIVE_FROM_USB 0xF7 +#define MBCA_CMD_CHANGE_BIT_RATE 0xA1 +#define MBCA_CMD_TRANSMIT_MESSAGE_EV 0xA3 +#define MBCA_CMD_SETUP_TERMINATION_RESISTANCE 0xA8 +#define MBCA_CMD_READ_FW_VERSION 0xA9 +#define MBCA_CMD_NOTHING_TO_SEND 0xFF +#define MBCA_CMD_TRANSMIT_MESSAGE_RSP 0xE2 + +#define MCBA_VER_REQ_USB 1 +#define MCBA_VER_REQ_CAN 2 + +/* Drive the CAN_RES signal LOW "0" to activate R24 and R25 */ +#define MCBA_VER_TERMINATION_ON 0 +#define MCBA_VER_TERMINATION_OFF 1 + +#define MCBA_SIDL_EXID_MASK 0x8 +#define MCBA_DLC_MASK 0xf +#define MCBA_DLC_RTR_MASK 0x40 + +#define MCBA_CAN_STATE_WRN_TH 95 +#define MCBA_CAN_STATE_ERR_PSV_TH 127 + +#define MCBA_TERMINATION_DISABLED CAN_TERMINATION_DISABLED +#define MCBA_TERMINATION_ENABLED 120 + +struct mcba_usb_ctx { + struct mcba_priv *priv; + u32 ndx; + u8 dlc; + bool can; +}; + +/* Structure to hold all of our device specific stuff */ +struct mcba_priv { + struct can_priv can; /* must be the first member */ + struct sk_buff *echo_skb[MCBA_MAX_TX_URBS]; + struct mcba_usb_ctx tx_context[MCBA_MAX_TX_URBS]; + struct usb_device *udev; + struct net_device *netdev; + struct usb_anchor tx_submitted; + struct usb_anchor rx_submitted; + struct can_berr_counter bec; + bool usb_ka_first_pass; + bool can_ka_first_pass; + bool can_speed_check; + atomic_t free_ctx_cnt; + void *rxbuf[MCBA_MAX_RX_URBS]; + dma_addr_t rxbuf_dma[MCBA_MAX_RX_URBS]; + int rx_pipe; + int tx_pipe; +}; + +/* CAN frame */ +struct __packed mcba_usb_msg_can { + u8 cmd_id; + __be16 eid; + __be16 sid; + u8 dlc; + u8 data[8]; + u8 timestamp[4]; + u8 checksum; +}; + +/* command frame */ +struct __packed mcba_usb_msg { + u8 cmd_id; + u8 unused[18]; +}; + +struct __packed mcba_usb_msg_ka_usb { + u8 cmd_id; + u8 termination_state; + u8 soft_ver_major; + u8 soft_ver_minor; + u8 unused[15]; +}; + +struct __packed mcba_usb_msg_ka_can { + u8 cmd_id; + u8 tx_err_cnt; + u8 rx_err_cnt; + u8 rx_buff_ovfl; + u8 tx_bus_off; + __be16 can_bitrate; + __le16 rx_lost; + u8 can_stat; + u8 soft_ver_major; + u8 soft_ver_minor; + u8 debug_mode; + u8 test_complete; + u8 test_result; + u8 unused[4]; +}; + +struct __packed mcba_usb_msg_change_bitrate { + u8 cmd_id; + __be16 bitrate; + u8 unused[16]; +}; + +struct __packed mcba_usb_msg_termination { + u8 cmd_id; + u8 termination; + u8 unused[17]; +}; + +struct __packed mcba_usb_msg_fw_ver { + u8 cmd_id; + u8 pic; + u8 unused[17]; +}; + +static const struct usb_device_id mcba_usb_table[] = { + { USB_DEVICE(MCBA_VENDOR_ID, MCBA_PRODUCT_ID) }, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, mcba_usb_table); + +static const u16 mcba_termination[] = { MCBA_TERMINATION_DISABLED, + MCBA_TERMINATION_ENABLED }; + +static const u32 mcba_bitrate[] = { 20000, 33333, 50000, 80000, 83333, + 100000, 125000, 150000, 175000, 200000, + 225000, 250000, 275000, 300000, 500000, + 625000, 800000, 1000000 }; + +static inline void mcba_init_ctx(struct mcba_priv *priv) +{ + int i = 0; + + for (i = 0; i < MCBA_MAX_TX_URBS; i++) { + priv->tx_context[i].ndx = MCBA_CTX_FREE; + priv->tx_context[i].priv = priv; + } + + atomic_set(&priv->free_ctx_cnt, ARRAY_SIZE(priv->tx_context)); +} + +static inline struct mcba_usb_ctx *mcba_usb_get_free_ctx(struct mcba_priv *priv, + struct can_frame *cf) +{ + int i = 0; + struct mcba_usb_ctx *ctx = NULL; + + for (i = 0; i < MCBA_MAX_TX_URBS; i++) { + if (priv->tx_context[i].ndx == MCBA_CTX_FREE) { + ctx = &priv->tx_context[i]; + ctx->ndx = i; + + if (cf) { + ctx->can = true; + ctx->dlc = cf->can_dlc; + } else { + ctx->can = false; + ctx->dlc = 0; + } + + atomic_dec(&priv->free_ctx_cnt); + break; + } + } + + if (!atomic_read(&priv->free_ctx_cnt)) + /* That was the last free ctx. Slow down tx path */ + netif_stop_queue(priv->netdev); + + return ctx; +} + +/* mcba_usb_free_ctx and mcba_usb_get_free_ctx are executed by different + * threads. The order of execution in below function is important. + */ +static inline void mcba_usb_free_ctx(struct mcba_usb_ctx *ctx) +{ + /* Increase number of free ctxs before freeing ctx */ + atomic_inc(&ctx->priv->free_ctx_cnt); + + ctx->ndx = MCBA_CTX_FREE; + + /* Wake up the queue once ctx is marked free */ + netif_wake_queue(ctx->priv->netdev); +} + +static void mcba_usb_write_bulk_callback(struct urb *urb) +{ + struct mcba_usb_ctx *ctx = urb->context; + struct net_device *netdev; + + WARN_ON(!ctx); + + netdev = ctx->priv->netdev; + + /* free up our allocated buffer */ + usb_free_coherent(urb->dev, urb->transfer_buffer_length, + urb->transfer_buffer, urb->transfer_dma); + + if (ctx->can) { + if (!netif_device_present(netdev)) + return; + + netdev->stats.tx_packets++; + netdev->stats.tx_bytes += ctx->dlc; + + can_led_event(netdev, CAN_LED_EVENT_TX); + can_get_echo_skb(netdev, ctx->ndx); + } + + if (urb->status) + netdev_info(netdev, "Tx URB aborted (%d)\n", urb->status); + + /* Release the context */ + mcba_usb_free_ctx(ctx); +} + +/* Send data to device */ +static netdev_tx_t mcba_usb_xmit(struct mcba_priv *priv, + struct mcba_usb_msg *usb_msg, + struct mcba_usb_ctx *ctx) +{ + struct urb *urb; + u8 *buf; + int err; + + /* create a URB, and a buffer for it, and copy the data to the URB */ + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) + return -ENOMEM; + + buf = usb_alloc_coherent(priv->udev, MCBA_USB_TX_BUFF_SIZE, GFP_ATOMIC, + &urb->transfer_dma); + if (!buf) { + err = -ENOMEM; + goto nomembuf; + } + + memcpy(buf, usb_msg, MCBA_USB_TX_BUFF_SIZE); + + usb_fill_bulk_urb(urb, priv->udev, priv->tx_pipe, buf, MCBA_USB_TX_BUFF_SIZE, + mcba_usb_write_bulk_callback, ctx); + + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(urb, &priv->tx_submitted); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (unlikely(err)) + goto failed; + + /* Release our reference to this URB, the USB core will eventually free + * it entirely. + */ + usb_free_urb(urb); + + return 0; + +failed: + usb_unanchor_urb(urb); + usb_free_coherent(priv->udev, MCBA_USB_TX_BUFF_SIZE, buf, + urb->transfer_dma); + + if (err == -ENODEV) + netif_device_detach(priv->netdev); + else + netdev_warn(priv->netdev, "failed tx_urb %d\n", err); + +nomembuf: + usb_free_urb(urb); + + return err; +} + +/* Send data to device */ +static netdev_tx_t mcba_usb_start_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + struct mcba_priv *priv = netdev_priv(netdev); + struct can_frame *cf = (struct can_frame *)skb->data; + struct mcba_usb_ctx *ctx = NULL; + struct net_device_stats *stats = &priv->netdev->stats; + u16 sid; + int err; + struct mcba_usb_msg_can usb_msg = { + .cmd_id = MBCA_CMD_TRANSMIT_MESSAGE_EV + }; + + if (can_dropped_invalid_skb(netdev, skb)) + return NETDEV_TX_OK; + + ctx = mcba_usb_get_free_ctx(priv, cf); + if (!ctx) + return NETDEV_TX_BUSY; + + if (cf->can_id & CAN_EFF_FLAG) { + /* SIDH | SIDL | EIDH | EIDL + * 28 - 21 | 20 19 18 x x x 17 16 | 15 - 8 | 7 - 0 + */ + sid = MCBA_SIDL_EXID_MASK; + /* store 28-18 bits */ + sid |= (cf->can_id & 0x1ffc0000) >> 13; + /* store 17-16 bits */ + sid |= (cf->can_id & 0x30000) >> 16; + put_unaligned_be16(sid, &usb_msg.sid); + + /* store 15-0 bits */ + put_unaligned_be16(cf->can_id & 0xffff, &usb_msg.eid); + } else { + /* SIDH | SIDL + * 10 - 3 | 2 1 0 x x x x x + */ + put_unaligned_be16((cf->can_id & CAN_SFF_MASK) << 5, + &usb_msg.sid); + usb_msg.eid = 0; + } + + usb_msg.dlc = cf->can_dlc; + + memcpy(usb_msg.data, cf->data, usb_msg.dlc); + + if (cf->can_id & CAN_RTR_FLAG) + usb_msg.dlc |= MCBA_DLC_RTR_MASK; + + can_put_echo_skb(skb, priv->netdev, ctx->ndx); + + err = mcba_usb_xmit(priv, (struct mcba_usb_msg *)&usb_msg, ctx); + if (err) + goto xmit_failed; + + return NETDEV_TX_OK; + +xmit_failed: + can_free_echo_skb(priv->netdev, ctx->ndx); + mcba_usb_free_ctx(ctx); + stats->tx_dropped++; + + return NETDEV_TX_OK; +} + +/* Send cmd to device */ +static void mcba_usb_xmit_cmd(struct mcba_priv *priv, + struct mcba_usb_msg *usb_msg) +{ + struct mcba_usb_ctx *ctx = NULL; + int err; + + ctx = mcba_usb_get_free_ctx(priv, NULL); + if (!ctx) { + netdev_err(priv->netdev, + "Lack of free ctx. Sending (%d) cmd aborted", + usb_msg->cmd_id); + + return; + } + + err = mcba_usb_xmit(priv, usb_msg, ctx); + if (err) + netdev_err(priv->netdev, "Failed to send cmd (%d)", + usb_msg->cmd_id); +} + +static void mcba_usb_xmit_change_bitrate(struct mcba_priv *priv, u16 bitrate) +{ + struct mcba_usb_msg_change_bitrate usb_msg = { + .cmd_id = MBCA_CMD_CHANGE_BIT_RATE + }; + + put_unaligned_be16(bitrate, &usb_msg.bitrate); + + mcba_usb_xmit_cmd(priv, (struct mcba_usb_msg *)&usb_msg); +} + +static void mcba_usb_xmit_read_fw_ver(struct mcba_priv *priv, u8 pic) +{ + struct mcba_usb_msg_fw_ver usb_msg = { + .cmd_id = MBCA_CMD_READ_FW_VERSION, + .pic = pic + }; + + mcba_usb_xmit_cmd(priv, (struct mcba_usb_msg *)&usb_msg); +} + +static void mcba_usb_process_can(struct mcba_priv *priv, + struct mcba_usb_msg_can *msg) +{ + struct can_frame *cf; + struct sk_buff *skb; + struct net_device_stats *stats = &priv->netdev->stats; + u16 sid; + + skb = alloc_can_skb(priv->netdev, &cf); + if (!skb) + return; + + sid = get_unaligned_be16(&msg->sid); + + if (sid & MCBA_SIDL_EXID_MASK) { + /* SIDH | SIDL | EIDH | EIDL + * 28 - 21 | 20 19 18 x x x 17 16 | 15 - 8 | 7 - 0 + */ + cf->can_id = CAN_EFF_FLAG; + + /* store 28-18 bits */ + cf->can_id |= (sid & 0xffe0) << 13; + /* store 17-16 bits */ + cf->can_id |= (sid & 3) << 16; + /* store 15-0 bits */ + cf->can_id |= get_unaligned_be16(&msg->eid); + } else { + /* SIDH | SIDL + * 10 - 3 | 2 1 0 x x x x x + */ + cf->can_id = (sid & 0xffe0) >> 5; + } + + if (msg->dlc & MCBA_DLC_RTR_MASK) + cf->can_id |= CAN_RTR_FLAG; + + cf->can_dlc = get_can_dlc(msg->dlc & MCBA_DLC_MASK); + + memcpy(cf->data, msg->data, cf->can_dlc); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + + can_led_event(priv->netdev, CAN_LED_EVENT_RX); + netif_rx(skb); +} + +static void mcba_usb_process_ka_usb(struct mcba_priv *priv, + struct mcba_usb_msg_ka_usb *msg) +{ + if (unlikely(priv->usb_ka_first_pass)) { + netdev_info(priv->netdev, "PIC USB version %hhu.%hhu\n", + msg->soft_ver_major, msg->soft_ver_minor); + + priv->usb_ka_first_pass = false; + } + + if (msg->termination_state == MCBA_VER_TERMINATION_ON) + priv->can.termination = MCBA_TERMINATION_ENABLED; + else + priv->can.termination = MCBA_TERMINATION_DISABLED; +} + +static u32 convert_can2host_bitrate(struct mcba_usb_msg_ka_can *msg) +{ + const u32 bitrate = get_unaligned_be16(&msg->can_bitrate); + + if ((bitrate == 33) || (bitrate == 83)) + return bitrate * 1000 + 333; + else + return bitrate * 1000; +} + +static void mcba_usb_process_ka_can(struct mcba_priv *priv, + struct mcba_usb_msg_ka_can *msg) +{ + if (unlikely(priv->can_ka_first_pass)) { + netdev_info(priv->netdev, "PIC CAN version %hhu.%hhu\n", + msg->soft_ver_major, msg->soft_ver_minor); + + priv->can_ka_first_pass = false; + } + + if (unlikely(priv->can_speed_check)) { + const u32 bitrate = convert_can2host_bitrate(msg); + + priv->can_speed_check = false; + + if (bitrate != priv->can.bittiming.bitrate) + netdev_err( + priv->netdev, + "Wrong bitrate reported by the device (%u). Expected %u", + bitrate, priv->can.bittiming.bitrate); + } + + priv->bec.txerr = msg->tx_err_cnt; + priv->bec.rxerr = msg->rx_err_cnt; + + if (msg->tx_bus_off) + priv->can.state = CAN_STATE_BUS_OFF; + + else if ((priv->bec.txerr > MCBA_CAN_STATE_ERR_PSV_TH) || + (priv->bec.rxerr > MCBA_CAN_STATE_ERR_PSV_TH)) + priv->can.state = CAN_STATE_ERROR_PASSIVE; + + else if ((priv->bec.txerr > MCBA_CAN_STATE_WRN_TH) || + (priv->bec.rxerr > MCBA_CAN_STATE_WRN_TH)) + priv->can.state = CAN_STATE_ERROR_WARNING; +} + +static void mcba_usb_process_rx(struct mcba_priv *priv, + struct mcba_usb_msg *msg) +{ + switch (msg->cmd_id) { + case MBCA_CMD_I_AM_ALIVE_FROM_CAN: + mcba_usb_process_ka_can(priv, + (struct mcba_usb_msg_ka_can *)msg); + break; + + case MBCA_CMD_I_AM_ALIVE_FROM_USB: + mcba_usb_process_ka_usb(priv, + (struct mcba_usb_msg_ka_usb *)msg); + break; + + case MBCA_CMD_RECEIVE_MESSAGE: + mcba_usb_process_can(priv, (struct mcba_usb_msg_can *)msg); + break; + + case MBCA_CMD_NOTHING_TO_SEND: + /* Side effect of communication between PIC_USB and PIC_CAN. + * PIC_CAN is telling us that it has nothing to send + */ + break; + + case MBCA_CMD_TRANSMIT_MESSAGE_RSP: + /* Transmission response from the device containing timestamp */ + break; + + default: + netdev_warn(priv->netdev, "Unsupported msg (0x%hhX)", + msg->cmd_id); + break; + } +} + +/* Callback for reading data from device + * + * Check urb status, call read function and resubmit urb read operation. + */ +static void mcba_usb_read_bulk_callback(struct urb *urb) +{ + struct mcba_priv *priv = urb->context; + struct net_device *netdev; + int retval; + int pos = 0; + + netdev = priv->netdev; + + if (!netif_device_present(netdev)) + return; + + switch (urb->status) { + case 0: /* success */ + break; + + case -ENOENT: + case -EPIPE: + case -EPROTO: + case -ESHUTDOWN: + return; + + default: + netdev_info(netdev, "Rx URB aborted (%d)\n", urb->status); + + goto resubmit_urb; + } + + while (pos < urb->actual_length) { + struct mcba_usb_msg *msg; + + if (pos + sizeof(struct mcba_usb_msg) > urb->actual_length) { + netdev_err(priv->netdev, "format error\n"); + break; + } + + msg = (struct mcba_usb_msg *)(urb->transfer_buffer + pos); + mcba_usb_process_rx(priv, msg); + + pos += sizeof(struct mcba_usb_msg); + } + +resubmit_urb: + + usb_fill_bulk_urb(urb, priv->udev, + priv->rx_pipe, + urb->transfer_buffer, MCBA_USB_RX_BUFF_SIZE, + mcba_usb_read_bulk_callback, priv); + + retval = usb_submit_urb(urb, GFP_ATOMIC); + + if (retval == -ENODEV) + netif_device_detach(netdev); + else if (retval) + netdev_err(netdev, "failed resubmitting read bulk urb: %d\n", + retval); +} + +/* Start USB device */ +static int mcba_usb_start(struct mcba_priv *priv) +{ + struct net_device *netdev = priv->netdev; + int err, i; + + mcba_init_ctx(priv); + + for (i = 0; i < MCBA_MAX_RX_URBS; i++) { + struct urb *urb = NULL; + u8 *buf; + dma_addr_t buf_dma; + + /* create a URB, and a buffer for it */ + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + err = -ENOMEM; + break; + } + + buf = usb_alloc_coherent(priv->udev, MCBA_USB_RX_BUFF_SIZE, + GFP_KERNEL, &buf_dma); + if (!buf) { + netdev_err(netdev, "No memory left for USB buffer\n"); + usb_free_urb(urb); + err = -ENOMEM; + break; + } + + urb->transfer_dma = buf_dma; + + usb_fill_bulk_urb(urb, priv->udev, + priv->rx_pipe, + buf, MCBA_USB_RX_BUFF_SIZE, + mcba_usb_read_bulk_callback, priv); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(urb, &priv->rx_submitted); + + err = usb_submit_urb(urb, GFP_KERNEL); + if (err) { + usb_unanchor_urb(urb); + usb_free_coherent(priv->udev, MCBA_USB_RX_BUFF_SIZE, + buf, buf_dma); + usb_free_urb(urb); + break; + } + + priv->rxbuf[i] = buf; + priv->rxbuf_dma[i] = buf_dma; + + /* Drop reference, USB core will take care of freeing it */ + usb_free_urb(urb); + } + + /* Did we submit any URBs */ + if (i == 0) { + netdev_warn(netdev, "couldn't setup read URBs\n"); + return err; + } + + /* Warn if we've couldn't transmit all the URBs */ + if (i < MCBA_MAX_RX_URBS) + netdev_warn(netdev, "rx performance may be slow\n"); + + mcba_usb_xmit_read_fw_ver(priv, MCBA_VER_REQ_USB); + mcba_usb_xmit_read_fw_ver(priv, MCBA_VER_REQ_CAN); + + return err; +} + +/* Open USB device */ +static int mcba_usb_open(struct net_device *netdev) +{ + struct mcba_priv *priv = netdev_priv(netdev); + int err; + + /* common open */ + err = open_candev(netdev); + if (err) + return err; + + priv->can_speed_check = true; + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + can_led_event(netdev, CAN_LED_EVENT_OPEN); + netif_start_queue(netdev); + + return 0; +} + +static void mcba_urb_unlink(struct mcba_priv *priv) +{ + int i; + + usb_kill_anchored_urbs(&priv->rx_submitted); + + for (i = 0; i < MCBA_MAX_RX_URBS; ++i) + usb_free_coherent(priv->udev, MCBA_USB_RX_BUFF_SIZE, + priv->rxbuf[i], priv->rxbuf_dma[i]); + + usb_kill_anchored_urbs(&priv->tx_submitted); +} + +/* Close USB device */ +static int mcba_usb_close(struct net_device *netdev) +{ + struct mcba_priv *priv = netdev_priv(netdev); + + priv->can.state = CAN_STATE_STOPPED; + + netif_stop_queue(netdev); + + /* Stop polling */ + mcba_urb_unlink(priv); + + close_candev(netdev); + can_led_event(netdev, CAN_LED_EVENT_STOP); + + return 0; +} + +/* Set network device mode + * + * Maybe we should leave this function empty, because the device + * set mode variable with open command. + */ +static int mcba_net_set_mode(struct net_device *netdev, enum can_mode mode) +{ + return 0; +} + +static int mcba_net_get_berr_counter(const struct net_device *netdev, + struct can_berr_counter *bec) +{ + struct mcba_priv *priv = netdev_priv(netdev); + + bec->txerr = priv->bec.txerr; + bec->rxerr = priv->bec.rxerr; + + return 0; +} + +static const struct net_device_ops mcba_netdev_ops = { + .ndo_open = mcba_usb_open, + .ndo_stop = mcba_usb_close, + .ndo_start_xmit = mcba_usb_start_xmit, +}; + +/* Microchip CANBUS has hardcoded bittiming values by default. + * This function sends request via USB to change the speed and align bittiming + * values for presentation purposes only + */ +static int mcba_net_set_bittiming(struct net_device *netdev) +{ + struct mcba_priv *priv = netdev_priv(netdev); + const u16 bitrate_kbps = priv->can.bittiming.bitrate / 1000; + + mcba_usb_xmit_change_bitrate(priv, bitrate_kbps); + + return 0; +} + +static int mcba_set_termination(struct net_device *netdev, u16 term) +{ + struct mcba_priv *priv = netdev_priv(netdev); + struct mcba_usb_msg_termination usb_msg = { + .cmd_id = MBCA_CMD_SETUP_TERMINATION_RESISTANCE + }; + + if (term == MCBA_TERMINATION_ENABLED) + usb_msg.termination = MCBA_VER_TERMINATION_ON; + else + usb_msg.termination = MCBA_VER_TERMINATION_OFF; + + mcba_usb_xmit_cmd(priv, (struct mcba_usb_msg *)&usb_msg); + + return 0; +} + +static int mcba_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct net_device *netdev; + struct mcba_priv *priv; + int err; + struct usb_device *usbdev = interface_to_usbdev(intf); + struct usb_endpoint_descriptor *in, *out; + + err = usb_find_common_endpoints(intf->cur_altsetting, &in, &out, NULL, NULL); + if (err) { + dev_err(&intf->dev, "Can't find endpoints\n"); + return err; + } + + netdev = alloc_candev(sizeof(struct mcba_priv), MCBA_MAX_TX_URBS); + if (!netdev) { + dev_err(&intf->dev, "Couldn't alloc candev\n"); + return -ENOMEM; + } + + priv = netdev_priv(netdev); + + priv->udev = usbdev; + priv->netdev = netdev; + priv->usb_ka_first_pass = true; + priv->can_ka_first_pass = true; + priv->can_speed_check = false; + + init_usb_anchor(&priv->rx_submitted); + init_usb_anchor(&priv->tx_submitted); + + usb_set_intfdata(intf, priv); + + /* Init CAN device */ + priv->can.state = CAN_STATE_STOPPED; + priv->can.termination_const = mcba_termination; + priv->can.termination_const_cnt = ARRAY_SIZE(mcba_termination); + priv->can.bitrate_const = mcba_bitrate; + priv->can.bitrate_const_cnt = ARRAY_SIZE(mcba_bitrate); + + priv->can.do_set_termination = mcba_set_termination; + priv->can.do_set_mode = mcba_net_set_mode; + priv->can.do_get_berr_counter = mcba_net_get_berr_counter; + priv->can.do_set_bittiming = mcba_net_set_bittiming; + + netdev->netdev_ops = &mcba_netdev_ops; + + netdev->flags |= IFF_ECHO; /* we support local echo */ + + SET_NETDEV_DEV(netdev, &intf->dev); + + err = register_candev(netdev); + if (err) { + netdev_err(netdev, "couldn't register CAN device: %d\n", err); + + goto cleanup_free_candev; + } + + priv->rx_pipe = usb_rcvbulkpipe(priv->udev, in->bEndpointAddress); + priv->tx_pipe = usb_sndbulkpipe(priv->udev, out->bEndpointAddress); + + devm_can_led_init(netdev); + + /* Start USB dev only if we have successfully registered CAN device */ + err = mcba_usb_start(priv); + if (err) { + if (err == -ENODEV) + netif_device_detach(priv->netdev); + + netdev_warn(netdev, "couldn't start device: %d\n", err); + + goto cleanup_unregister_candev; + } + + dev_info(&intf->dev, "Microchip CAN BUS Analyzer connected\n"); + + return 0; + +cleanup_unregister_candev: + unregister_candev(priv->netdev); + +cleanup_free_candev: + free_candev(netdev); + + return err; +} + +/* Called by the usb core when driver is unloaded or device is removed */ +static void mcba_usb_disconnect(struct usb_interface *intf) +{ + struct mcba_priv *priv = usb_get_intfdata(intf); + + usb_set_intfdata(intf, NULL); + + netdev_info(priv->netdev, "device disconnected\n"); + + unregister_candev(priv->netdev); + mcba_urb_unlink(priv); + free_candev(priv->netdev); +} + +static struct usb_driver mcba_usb_driver = { + .name = MCBA_MODULE_NAME, + .probe = mcba_usb_probe, + .disconnect = mcba_usb_disconnect, + .id_table = mcba_usb_table, +}; + +module_usb_driver(mcba_usb_driver); + +MODULE_AUTHOR("Remigiusz Kołłątaj <remigiusz.kollataj@mobica.com>"); +MODULE_DESCRIPTION("SocketCAN driver for Microchip CAN BUS Analyzer Tool"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/can/usb/peak_usb/Makefile b/drivers/net/can/usb/peak_usb/Makefile new file mode 100644 index 000000000..80789f91e --- /dev/null +++ b/drivers/net/can/usb/peak_usb/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_CAN_PEAK_USB) += peak_usb.o +peak_usb-y = pcan_usb_core.o pcan_usb.o pcan_usb_pro.o pcan_usb_fd.o diff --git a/drivers/net/can/usb/peak_usb/pcan_usb.c b/drivers/net/can/usb/peak_usb/pcan_usb.c new file mode 100644 index 000000000..ce2d0761c --- /dev/null +++ b/drivers/net/can/usb/peak_usb/pcan_usb.c @@ -0,0 +1,1031 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * CAN driver for PEAK System PCAN-USB adapter + * Derived from the PCAN project file driver/src/pcan_usb.c + * + * Copyright (C) 2003-2010 PEAK System-Technik GmbH + * Copyright (C) 2011-2012 Stephane Grosjean <s.grosjean@peak-system.com> + * + * Many thanks to Klaus Hitschler <klaus.hitschler@gmx.de> + */ +#include <linux/netdevice.h> +#include <linux/usb.h> +#include <linux/module.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> + +#include "pcan_usb_core.h" + +MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB adapter"); + +/* PCAN-USB Endpoints */ +#define PCAN_USB_EP_CMDOUT 1 +#define PCAN_USB_EP_CMDIN (PCAN_USB_EP_CMDOUT | USB_DIR_IN) +#define PCAN_USB_EP_MSGOUT 2 +#define PCAN_USB_EP_MSGIN (PCAN_USB_EP_MSGOUT | USB_DIR_IN) + +/* PCAN-USB command struct */ +#define PCAN_USB_CMD_FUNC 0 +#define PCAN_USB_CMD_NUM 1 +#define PCAN_USB_CMD_ARGS 2 +#define PCAN_USB_CMD_ARGS_LEN 14 +#define PCAN_USB_CMD_LEN (PCAN_USB_CMD_ARGS + \ + PCAN_USB_CMD_ARGS_LEN) + +/* PCAN-USB commands */ +#define PCAN_USB_CMD_BITRATE 1 +#define PCAN_USB_CMD_SET_BUS 3 +#define PCAN_USB_CMD_DEVID 4 +#define PCAN_USB_CMD_SN 6 +#define PCAN_USB_CMD_REGISTER 9 +#define PCAN_USB_CMD_EXT_VCC 10 +#define PCAN_USB_CMD_ERR_FR 11 + +/* PCAN_USB_CMD_SET_BUS number arg */ +#define PCAN_USB_BUS_XCVER 2 +#define PCAN_USB_BUS_SILENT_MODE 3 + +/* PCAN_USB_CMD_xxx functions */ +#define PCAN_USB_GET 1 +#define PCAN_USB_SET 2 + +/* PCAN-USB command timeout (ms.) */ +#define PCAN_USB_COMMAND_TIMEOUT 1000 + +/* PCAN-USB startup timeout (ms.) */ +#define PCAN_USB_STARTUP_TIMEOUT 10 + +/* PCAN-USB rx/tx buffers size */ +#define PCAN_USB_RX_BUFFER_SIZE 64 +#define PCAN_USB_TX_BUFFER_SIZE 64 + +#define PCAN_USB_MSG_HEADER_LEN 2 + +/* PCAN-USB adapter internal clock (MHz) */ +#define PCAN_USB_CRYSTAL_HZ 16000000 + +/* PCAN-USB USB message record status/len field */ +#define PCAN_USB_STATUSLEN_TIMESTAMP (1 << 7) +#define PCAN_USB_STATUSLEN_INTERNAL (1 << 6) +#define PCAN_USB_STATUSLEN_EXT_ID (1 << 5) +#define PCAN_USB_STATUSLEN_RTR (1 << 4) +#define PCAN_USB_STATUSLEN_DLC (0xf) + +/* PCAN-USB error flags */ +#define PCAN_USB_ERROR_TXFULL 0x01 +#define PCAN_USB_ERROR_RXQOVR 0x02 +#define PCAN_USB_ERROR_BUS_LIGHT 0x04 +#define PCAN_USB_ERROR_BUS_HEAVY 0x08 +#define PCAN_USB_ERROR_BUS_OFF 0x10 +#define PCAN_USB_ERROR_RXQEMPTY 0x20 +#define PCAN_USB_ERROR_QOVR 0x40 +#define PCAN_USB_ERROR_TXQFULL 0x80 + +#define PCAN_USB_ERROR_BUS (PCAN_USB_ERROR_BUS_LIGHT | \ + PCAN_USB_ERROR_BUS_HEAVY | \ + PCAN_USB_ERROR_BUS_OFF) + +/* SJA1000 modes */ +#define SJA1000_MODE_NORMAL 0x00 +#define SJA1000_MODE_INIT 0x01 + +/* + * tick duration = 42.666 us => + * (tick_number * 44739243) >> 20 ~ (tick_number * 42666) / 1000 + * accuracy = 10^-7 + */ +#define PCAN_USB_TS_DIV_SHIFTER 20 +#define PCAN_USB_TS_US_PER_TICK 44739243 + +/* PCAN-USB messages record types */ +#define PCAN_USB_REC_ERROR 1 +#define PCAN_USB_REC_ANALOG 2 +#define PCAN_USB_REC_BUSLOAD 3 +#define PCAN_USB_REC_TS 4 +#define PCAN_USB_REC_BUSEVT 5 + +/* CAN bus events notifications selection mask */ +#define PCAN_USB_ERR_RXERR 0x02 /* ask for rxerr counter */ +#define PCAN_USB_ERR_TXERR 0x04 /* ask for txerr counter */ + +/* This mask generates an usb packet each time the state of the bus changes. + * In other words, its interest is to know which side among rx and tx is + * responsible of the change of the bus state. + */ +#define PCAN_USB_BERR_MASK (PCAN_USB_ERR_RXERR | PCAN_USB_ERR_TXERR) + +/* identify bus event packets with rx/tx error counters */ +#define PCAN_USB_ERR_CNT_DEC 0x00 /* counters are decreasing */ +#define PCAN_USB_ERR_CNT_INC 0x80 /* counters are increasing */ + +/* private to PCAN-USB adapter */ +struct pcan_usb { + struct peak_usb_device dev; + struct peak_time_ref time_ref; + struct timer_list restart_timer; + struct can_berr_counter bec; +}; + +/* incoming message context for decoding */ +struct pcan_usb_msg_context { + u16 ts16; + u8 prev_ts8; + u8 *ptr; + u8 *end; + u8 rec_cnt; + u8 rec_idx; + u8 rec_ts_idx; + struct net_device *netdev; + struct pcan_usb *pdev; +}; + +/* + * send a command + */ +static int pcan_usb_send_cmd(struct peak_usb_device *dev, u8 f, u8 n, u8 *p) +{ + int err; + int actual_length; + + /* usb device unregistered? */ + if (!(dev->state & PCAN_USB_STATE_CONNECTED)) + return 0; + + dev->cmd_buf[PCAN_USB_CMD_FUNC] = f; + dev->cmd_buf[PCAN_USB_CMD_NUM] = n; + + if (p) + memcpy(dev->cmd_buf + PCAN_USB_CMD_ARGS, + p, PCAN_USB_CMD_ARGS_LEN); + + err = usb_bulk_msg(dev->udev, + usb_sndbulkpipe(dev->udev, PCAN_USB_EP_CMDOUT), + dev->cmd_buf, PCAN_USB_CMD_LEN, &actual_length, + PCAN_USB_COMMAND_TIMEOUT); + if (err) + netdev_err(dev->netdev, + "sending cmd f=0x%x n=0x%x failure: %d\n", + f, n, err); + return err; +} + +/* + * send a command then wait for its response + */ +static int pcan_usb_wait_rsp(struct peak_usb_device *dev, u8 f, u8 n, u8 *p) +{ + int err; + int actual_length; + + /* usb device unregistered? */ + if (!(dev->state & PCAN_USB_STATE_CONNECTED)) + return 0; + + /* first, send command */ + err = pcan_usb_send_cmd(dev, f, n, NULL); + if (err) + return err; + + err = usb_bulk_msg(dev->udev, + usb_rcvbulkpipe(dev->udev, PCAN_USB_EP_CMDIN), + dev->cmd_buf, PCAN_USB_CMD_LEN, &actual_length, + PCAN_USB_COMMAND_TIMEOUT); + if (err) + netdev_err(dev->netdev, + "waiting rsp f=0x%x n=0x%x failure: %d\n", f, n, err); + else if (p) + memcpy(p, dev->cmd_buf + PCAN_USB_CMD_ARGS, + PCAN_USB_CMD_ARGS_LEN); + + return err; +} + +static int pcan_usb_set_sja1000(struct peak_usb_device *dev, u8 mode) +{ + u8 args[PCAN_USB_CMD_ARGS_LEN] = { + [1] = mode, + }; + + return pcan_usb_send_cmd(dev, PCAN_USB_CMD_REGISTER, PCAN_USB_SET, + args); +} + +static int pcan_usb_set_bus(struct peak_usb_device *dev, u8 onoff) +{ + u8 args[PCAN_USB_CMD_ARGS_LEN] = { + [0] = !!onoff, + }; + + return pcan_usb_send_cmd(dev, PCAN_USB_CMD_SET_BUS, PCAN_USB_BUS_XCVER, + args); +} + +static int pcan_usb_set_silent(struct peak_usb_device *dev, u8 onoff) +{ + u8 args[PCAN_USB_CMD_ARGS_LEN] = { + [0] = !!onoff, + }; + + return pcan_usb_send_cmd(dev, PCAN_USB_CMD_SET_BUS, + PCAN_USB_BUS_SILENT_MODE, args); +} + +/* send the cmd to be notified from bus errors */ +static int pcan_usb_set_err_frame(struct peak_usb_device *dev, u8 err_mask) +{ + u8 args[PCAN_USB_CMD_ARGS_LEN] = { + [0] = err_mask, + }; + + return pcan_usb_send_cmd(dev, PCAN_USB_CMD_ERR_FR, PCAN_USB_SET, args); +} + +static int pcan_usb_set_ext_vcc(struct peak_usb_device *dev, u8 onoff) +{ + u8 args[PCAN_USB_CMD_ARGS_LEN] = { + [0] = !!onoff, + }; + + return pcan_usb_send_cmd(dev, PCAN_USB_CMD_EXT_VCC, PCAN_USB_SET, args); +} + +/* + * set bittiming value to can + */ +static int pcan_usb_set_bittiming(struct peak_usb_device *dev, + struct can_bittiming *bt) +{ + u8 args[PCAN_USB_CMD_ARGS_LEN]; + u8 btr0, btr1; + + btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6); + btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) | + (((bt->phase_seg2 - 1) & 0x7) << 4); + if (dev->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + btr1 |= 0x80; + + netdev_info(dev->netdev, "setting BTR0=0x%02x BTR1=0x%02x\n", + btr0, btr1); + + args[0] = btr1; + args[1] = btr0; + + return pcan_usb_send_cmd(dev, PCAN_USB_CMD_BITRATE, PCAN_USB_SET, args); +} + +/* + * init/reset can + */ +static int pcan_usb_write_mode(struct peak_usb_device *dev, u8 onoff) +{ + int err; + + err = pcan_usb_set_bus(dev, onoff); + if (err) + return err; + + if (!onoff) { + err = pcan_usb_set_sja1000(dev, SJA1000_MODE_INIT); + } else { + /* the PCAN-USB needs time to init */ + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(msecs_to_jiffies(PCAN_USB_STARTUP_TIMEOUT)); + } + + return err; +} + +/* + * handle end of waiting for the device to reset + */ +static void pcan_usb_restart(struct timer_list *t) +{ + struct pcan_usb *pdev = from_timer(pdev, t, restart_timer); + struct peak_usb_device *dev = &pdev->dev; + + /* notify candev and netdev */ + peak_usb_restart_complete(dev); +} + +/* + * handle the submission of the restart urb + */ +static void pcan_usb_restart_pending(struct urb *urb) +{ + struct pcan_usb *pdev = urb->context; + + /* the PCAN-USB needs time to restart */ + mod_timer(&pdev->restart_timer, + jiffies + msecs_to_jiffies(PCAN_USB_STARTUP_TIMEOUT)); + + /* can delete usb resources */ + peak_usb_async_complete(urb); +} + +/* + * handle asynchronous restart + */ +static int pcan_usb_restart_async(struct peak_usb_device *dev, struct urb *urb, + u8 *buf) +{ + struct pcan_usb *pdev = container_of(dev, struct pcan_usb, dev); + + if (timer_pending(&pdev->restart_timer)) + return -EBUSY; + + /* set bus on */ + buf[PCAN_USB_CMD_FUNC] = 3; + buf[PCAN_USB_CMD_NUM] = 2; + buf[PCAN_USB_CMD_ARGS] = 1; + + usb_fill_bulk_urb(urb, dev->udev, + usb_sndbulkpipe(dev->udev, PCAN_USB_EP_CMDOUT), + buf, PCAN_USB_CMD_LEN, + pcan_usb_restart_pending, pdev); + + return usb_submit_urb(urb, GFP_ATOMIC); +} + +/* + * read serial number from device + */ +static int pcan_usb_get_serial(struct peak_usb_device *dev, u32 *serial_number) +{ + u8 args[PCAN_USB_CMD_ARGS_LEN]; + int err; + + err = pcan_usb_wait_rsp(dev, PCAN_USB_CMD_SN, PCAN_USB_GET, args); + if (err) { + netdev_err(dev->netdev, "getting serial failure: %d\n", err); + } else if (serial_number) { + __le32 tmp32; + + memcpy(&tmp32, args, 4); + *serial_number = le32_to_cpu(tmp32); + } + + return err; +} + +/* + * read device id from device + */ +static int pcan_usb_get_device_id(struct peak_usb_device *dev, u32 *device_id) +{ + u8 args[PCAN_USB_CMD_ARGS_LEN]; + int err; + + err = pcan_usb_wait_rsp(dev, PCAN_USB_CMD_DEVID, PCAN_USB_GET, args); + if (err) + netdev_err(dev->netdev, "getting device id failure: %d\n", err); + else if (device_id) + *device_id = args[0]; + + return err; +} + +/* + * update current time ref with received timestamp + */ +static int pcan_usb_update_ts(struct pcan_usb_msg_context *mc) +{ + __le16 tmp16; + + if ((mc->ptr+2) > mc->end) + return -EINVAL; + + memcpy(&tmp16, mc->ptr, 2); + + mc->ts16 = le16_to_cpu(tmp16); + + if (mc->rec_idx > 0) + peak_usb_update_ts_now(&mc->pdev->time_ref, mc->ts16); + else + peak_usb_set_ts_now(&mc->pdev->time_ref, mc->ts16); + + return 0; +} + +/* + * decode received timestamp + */ +static int pcan_usb_decode_ts(struct pcan_usb_msg_context *mc, u8 first_packet) +{ + /* only 1st packet supplies a word timestamp */ + if (first_packet) { + __le16 tmp16; + + if ((mc->ptr + 2) > mc->end) + return -EINVAL; + + memcpy(&tmp16, mc->ptr, 2); + mc->ptr += 2; + + mc->ts16 = le16_to_cpu(tmp16); + mc->prev_ts8 = mc->ts16 & 0x00ff; + } else { + u8 ts8; + + if ((mc->ptr + 1) > mc->end) + return -EINVAL; + + ts8 = *mc->ptr++; + + if (ts8 < mc->prev_ts8) + mc->ts16 += 0x100; + + mc->ts16 &= 0xff00; + mc->ts16 |= ts8; + mc->prev_ts8 = ts8; + } + + return 0; +} + +static int pcan_usb_decode_error(struct pcan_usb_msg_context *mc, u8 n, + u8 status_len) +{ + struct sk_buff *skb; + struct can_frame *cf; + enum can_state new_state; + + /* ignore this error until 1st ts received */ + if (n == PCAN_USB_ERROR_QOVR) + if (!mc->pdev->time_ref.tick_count) + return 0; + + new_state = mc->pdev->dev.can.state; + + switch (mc->pdev->dev.can.state) { + case CAN_STATE_ERROR_ACTIVE: + if (n & PCAN_USB_ERROR_BUS_LIGHT) { + new_state = CAN_STATE_ERROR_WARNING; + break; + } + fallthrough; + + case CAN_STATE_ERROR_WARNING: + if (n & PCAN_USB_ERROR_BUS_HEAVY) { + new_state = CAN_STATE_ERROR_PASSIVE; + break; + } + if (n & PCAN_USB_ERROR_BUS_OFF) { + new_state = CAN_STATE_BUS_OFF; + break; + } + if (n & ~PCAN_USB_ERROR_BUS) { + /* + * trick to bypass next comparison and process other + * errors + */ + new_state = CAN_STATE_MAX; + break; + } + if ((n & PCAN_USB_ERROR_BUS_LIGHT) == 0) { + /* no error (back to active state) */ + new_state = CAN_STATE_ERROR_ACTIVE; + break; + } + break; + + case CAN_STATE_ERROR_PASSIVE: + if (n & PCAN_USB_ERROR_BUS_OFF) { + new_state = CAN_STATE_BUS_OFF; + break; + } + if (n & PCAN_USB_ERROR_BUS_LIGHT) { + new_state = CAN_STATE_ERROR_WARNING; + break; + } + if (n & ~PCAN_USB_ERROR_BUS) { + /* + * trick to bypass next comparison and process other + * errors + */ + new_state = CAN_STATE_MAX; + break; + } + + if ((n & PCAN_USB_ERROR_BUS_HEAVY) == 0) { + /* no error (back to warning state) */ + new_state = CAN_STATE_ERROR_WARNING; + break; + } + break; + + default: + /* do nothing waiting for restart */ + return 0; + } + + /* donot post any error if current state didn't change */ + if (mc->pdev->dev.can.state == new_state) + return 0; + + /* allocate an skb to store the error frame */ + skb = alloc_can_err_skb(mc->netdev, &cf); + if (!skb) + return -ENOMEM; + + switch (new_state) { + case CAN_STATE_BUS_OFF: + cf->can_id |= CAN_ERR_BUSOFF; + mc->pdev->dev.can.can_stats.bus_off++; + can_bus_off(mc->netdev); + break; + + case CAN_STATE_ERROR_PASSIVE: + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = (mc->pdev->bec.txerr > mc->pdev->bec.rxerr) ? + CAN_ERR_CRTL_TX_PASSIVE : + CAN_ERR_CRTL_RX_PASSIVE; + cf->data[6] = mc->pdev->bec.txerr; + cf->data[7] = mc->pdev->bec.rxerr; + + mc->pdev->dev.can.can_stats.error_passive++; + break; + + case CAN_STATE_ERROR_WARNING: + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = (mc->pdev->bec.txerr > mc->pdev->bec.rxerr) ? + CAN_ERR_CRTL_TX_WARNING : + CAN_ERR_CRTL_RX_WARNING; + cf->data[6] = mc->pdev->bec.txerr; + cf->data[7] = mc->pdev->bec.rxerr; + + mc->pdev->dev.can.can_stats.error_warning++; + break; + + case CAN_STATE_ERROR_ACTIVE: + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = CAN_ERR_CRTL_ACTIVE; + + /* sync local copies of rxerr/txerr counters */ + mc->pdev->bec.txerr = 0; + mc->pdev->bec.rxerr = 0; + break; + + default: + /* CAN_STATE_MAX (trick to handle other errors) */ + if (n & PCAN_USB_ERROR_TXQFULL) + netdev_dbg(mc->netdev, "device Tx queue full)\n"); + + if (n & PCAN_USB_ERROR_RXQOVR) { + netdev_dbg(mc->netdev, "data overrun interrupt\n"); + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW; + mc->netdev->stats.rx_over_errors++; + mc->netdev->stats.rx_errors++; + } + + cf->data[6] = mc->pdev->bec.txerr; + cf->data[7] = mc->pdev->bec.rxerr; + + new_state = mc->pdev->dev.can.state; + break; + } + + mc->pdev->dev.can.state = new_state; + + if (status_len & PCAN_USB_STATUSLEN_TIMESTAMP) { + struct skb_shared_hwtstamps *hwts = skb_hwtstamps(skb); + + peak_usb_get_ts_time(&mc->pdev->time_ref, mc->ts16, + &hwts->hwtstamp); + } + + mc->netdev->stats.rx_packets++; + mc->netdev->stats.rx_bytes += cf->can_dlc; + netif_rx(skb); + + return 0; +} + +/* decode bus event usb packet: first byte contains rxerr while 2nd one contains + * txerr. + */ +static int pcan_usb_handle_bus_evt(struct pcan_usb_msg_context *mc, u8 ir) +{ + struct pcan_usb *pdev = mc->pdev; + + /* acccording to the content of the packet */ + switch (ir) { + case PCAN_USB_ERR_CNT_DEC: + case PCAN_USB_ERR_CNT_INC: + + /* save rx/tx error counters from in the device context */ + pdev->bec.rxerr = mc->ptr[1]; + pdev->bec.txerr = mc->ptr[2]; + break; + + default: + /* reserved */ + break; + } + + return 0; +} + +/* + * decode non-data usb message + */ +static int pcan_usb_decode_status(struct pcan_usb_msg_context *mc, + u8 status_len) +{ + u8 rec_len = status_len & PCAN_USB_STATUSLEN_DLC; + u8 f, n; + int err; + + /* check whether function and number can be read */ + if ((mc->ptr + 2) > mc->end) + return -EINVAL; + + f = mc->ptr[PCAN_USB_CMD_FUNC]; + n = mc->ptr[PCAN_USB_CMD_NUM]; + mc->ptr += PCAN_USB_CMD_ARGS; + + if (status_len & PCAN_USB_STATUSLEN_TIMESTAMP) { + int err = pcan_usb_decode_ts(mc, !mc->rec_ts_idx); + + if (err) + return err; + + /* Next packet in the buffer will have a timestamp on a single + * byte + */ + mc->rec_ts_idx++; + } + + switch (f) { + case PCAN_USB_REC_ERROR: + err = pcan_usb_decode_error(mc, n, status_len); + if (err) + return err; + break; + + case PCAN_USB_REC_ANALOG: + /* analog values (ignored) */ + rec_len = 2; + break; + + case PCAN_USB_REC_BUSLOAD: + /* bus load (ignored) */ + rec_len = 1; + break; + + case PCAN_USB_REC_TS: + /* only timestamp */ + if (pcan_usb_update_ts(mc)) + return -EINVAL; + break; + + case PCAN_USB_REC_BUSEVT: + /* bus event notifications (get rxerr/txerr) */ + err = pcan_usb_handle_bus_evt(mc, n); + if (err) + return err; + break; + default: + netdev_err(mc->netdev, "unexpected function %u\n", f); + break; + } + + if ((mc->ptr + rec_len) > mc->end) + return -EINVAL; + + mc->ptr += rec_len; + + return 0; +} + +/* + * decode data usb message + */ +static int pcan_usb_decode_data(struct pcan_usb_msg_context *mc, u8 status_len) +{ + u8 rec_len = status_len & PCAN_USB_STATUSLEN_DLC; + struct sk_buff *skb; + struct can_frame *cf; + struct skb_shared_hwtstamps *hwts; + + skb = alloc_can_skb(mc->netdev, &cf); + if (!skb) + return -ENOMEM; + + if (status_len & PCAN_USB_STATUSLEN_EXT_ID) { + __le32 tmp32; + + if ((mc->ptr + 4) > mc->end) + goto decode_failed; + + memcpy(&tmp32, mc->ptr, 4); + mc->ptr += 4; + + cf->can_id = (le32_to_cpu(tmp32) >> 3) | CAN_EFF_FLAG; + } else { + __le16 tmp16; + + if ((mc->ptr + 2) > mc->end) + goto decode_failed; + + memcpy(&tmp16, mc->ptr, 2); + mc->ptr += 2; + + cf->can_id = le16_to_cpu(tmp16) >> 5; + } + + cf->can_dlc = get_can_dlc(rec_len); + + /* Only first packet timestamp is a word */ + if (pcan_usb_decode_ts(mc, !mc->rec_ts_idx)) + goto decode_failed; + + /* Next packet in the buffer will have a timestamp on a single byte */ + mc->rec_ts_idx++; + + /* read data */ + memset(cf->data, 0x0, sizeof(cf->data)); + if (status_len & PCAN_USB_STATUSLEN_RTR) { + cf->can_id |= CAN_RTR_FLAG; + } else { + if ((mc->ptr + rec_len) > mc->end) + goto decode_failed; + + memcpy(cf->data, mc->ptr, cf->can_dlc); + mc->ptr += rec_len; + } + + /* convert timestamp into kernel time */ + hwts = skb_hwtstamps(skb); + peak_usb_get_ts_time(&mc->pdev->time_ref, mc->ts16, &hwts->hwtstamp); + + /* update statistics */ + mc->netdev->stats.rx_packets++; + mc->netdev->stats.rx_bytes += cf->can_dlc; + /* push the skb */ + netif_rx(skb); + + return 0; + +decode_failed: + dev_kfree_skb(skb); + return -EINVAL; +} + +/* + * process incoming message + */ +static int pcan_usb_decode_msg(struct peak_usb_device *dev, u8 *ibuf, u32 lbuf) +{ + struct pcan_usb_msg_context mc = { + .rec_cnt = ibuf[1], + .ptr = ibuf + PCAN_USB_MSG_HEADER_LEN, + .end = ibuf + lbuf, + .netdev = dev->netdev, + .pdev = container_of(dev, struct pcan_usb, dev), + }; + int err; + + for (err = 0; mc.rec_idx < mc.rec_cnt && !err; mc.rec_idx++) { + u8 sl = *mc.ptr++; + + /* handle status and error frames here */ + if (sl & PCAN_USB_STATUSLEN_INTERNAL) { + err = pcan_usb_decode_status(&mc, sl); + /* handle normal can frames here */ + } else { + err = pcan_usb_decode_data(&mc, sl); + } + } + + return err; +} + +/* + * process any incoming buffer + */ +static int pcan_usb_decode_buf(struct peak_usb_device *dev, struct urb *urb) +{ + int err = 0; + + if (urb->actual_length > PCAN_USB_MSG_HEADER_LEN) { + err = pcan_usb_decode_msg(dev, urb->transfer_buffer, + urb->actual_length); + + } else if (urb->actual_length > 0) { + netdev_err(dev->netdev, "usb message length error (%u)\n", + urb->actual_length); + err = -EINVAL; + } + + return err; +} + +/* + * process outgoing packet + */ +static int pcan_usb_encode_msg(struct peak_usb_device *dev, struct sk_buff *skb, + u8 *obuf, size_t *size) +{ + struct net_device *netdev = dev->netdev; + struct net_device_stats *stats = &netdev->stats; + struct can_frame *cf = (struct can_frame *)skb->data; + u8 *pc; + + obuf[0] = 2; + obuf[1] = 1; + + pc = obuf + PCAN_USB_MSG_HEADER_LEN; + + /* status/len byte */ + *pc = cf->can_dlc; + if (cf->can_id & CAN_RTR_FLAG) + *pc |= PCAN_USB_STATUSLEN_RTR; + + /* can id */ + if (cf->can_id & CAN_EFF_FLAG) { + __le32 tmp32 = cpu_to_le32((cf->can_id & CAN_ERR_MASK) << 3); + + *pc |= PCAN_USB_STATUSLEN_EXT_ID; + memcpy(++pc, &tmp32, 4); + pc += 4; + } else { + __le16 tmp16 = cpu_to_le16((cf->can_id & CAN_ERR_MASK) << 5); + + memcpy(++pc, &tmp16, 2); + pc += 2; + } + + /* can data */ + if (!(cf->can_id & CAN_RTR_FLAG)) { + memcpy(pc, cf->data, cf->can_dlc); + pc += cf->can_dlc; + } + + obuf[(*size)-1] = (u8)(stats->tx_packets & 0xff); + + return 0; +} + +/* socket callback used to copy berr counters values received through USB */ +static int pcan_usb_get_berr_counter(const struct net_device *netdev, + struct can_berr_counter *bec) +{ + struct peak_usb_device *dev = netdev_priv(netdev); + struct pcan_usb *pdev = container_of(dev, struct pcan_usb, dev); + + *bec = pdev->bec; + + /* must return 0 */ + return 0; +} + +/* + * start interface + */ +static int pcan_usb_start(struct peak_usb_device *dev) +{ + struct pcan_usb *pdev = container_of(dev, struct pcan_usb, dev); + int err; + + /* number of bits used in timestamps read from adapter struct */ + peak_usb_init_time_ref(&pdev->time_ref, &pcan_usb); + + pdev->bec.rxerr = 0; + pdev->bec.txerr = 0; + + /* be notified on error counter changes (if requested by user) */ + if (dev->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) { + err = pcan_usb_set_err_frame(dev, PCAN_USB_BERR_MASK); + if (err) + netdev_warn(dev->netdev, + "Asking for BERR reporting error %u\n", + err); + } + + /* if revision greater than 3, can put silent mode on/off */ + if (dev->device_rev > 3) { + err = pcan_usb_set_silent(dev, + dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY); + if (err) + return err; + } + + return pcan_usb_set_ext_vcc(dev, 0); +} + +static int pcan_usb_init(struct peak_usb_device *dev) +{ + struct pcan_usb *pdev = container_of(dev, struct pcan_usb, dev); + u32 serial_number; + int err; + + /* initialize a timer needed to wait for hardware restart */ + timer_setup(&pdev->restart_timer, pcan_usb_restart, 0); + + /* + * explicit use of dev_xxx() instead of netdev_xxx() here: + * information displayed are related to the device itself, not + * to the canx netdevice. + */ + err = pcan_usb_get_serial(dev, &serial_number); + if (err) { + dev_err(dev->netdev->dev.parent, + "unable to read %s serial number (err %d)\n", + pcan_usb.name, err); + return err; + } + + dev_info(dev->netdev->dev.parent, + "PEAK-System %s adapter hwrev %u serial %08X (%u channel)\n", + pcan_usb.name, dev->device_rev, serial_number, + pcan_usb.ctrl_count); + + return 0; +} + +/* + * probe function for new PCAN-USB usb interface + */ +static int pcan_usb_probe(struct usb_interface *intf) +{ + struct usb_host_interface *if_desc; + int i; + + if_desc = intf->altsetting; + + /* check interface endpoint addresses */ + for (i = 0; i < if_desc->desc.bNumEndpoints; i++) { + struct usb_endpoint_descriptor *ep = &if_desc->endpoint[i].desc; + + switch (ep->bEndpointAddress) { + case PCAN_USB_EP_CMDOUT: + case PCAN_USB_EP_CMDIN: + case PCAN_USB_EP_MSGOUT: + case PCAN_USB_EP_MSGIN: + break; + default: + return -ENODEV; + } + } + + return 0; +} + +/* + * describe the PCAN-USB adapter + */ +static const struct can_bittiming_const pcan_usb_const = { + .name = "pcan_usb", + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 64, + .brp_inc = 1, +}; + +const struct peak_usb_adapter pcan_usb = { + .name = "PCAN-USB", + .device_id = PCAN_USB_PRODUCT_ID, + .ctrl_count = 1, + .ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_BERR_REPORTING, + .clock = { + .freq = PCAN_USB_CRYSTAL_HZ / 2 , + }, + .bittiming_const = &pcan_usb_const, + + /* size of device private data */ + .sizeof_dev_private = sizeof(struct pcan_usb), + + /* timestamps usage */ + .ts_used_bits = 16, + .ts_period = 24575, /* calibration period in ts. */ + .us_per_ts_scale = PCAN_USB_TS_US_PER_TICK, /* us=(ts*scale) */ + .us_per_ts_shift = PCAN_USB_TS_DIV_SHIFTER, /* >> shift */ + + /* give here messages in/out endpoints */ + .ep_msg_in = PCAN_USB_EP_MSGIN, + .ep_msg_out = {PCAN_USB_EP_MSGOUT}, + + /* size of rx/tx usb buffers */ + .rx_buffer_size = PCAN_USB_RX_BUFFER_SIZE, + .tx_buffer_size = PCAN_USB_TX_BUFFER_SIZE, + + /* device callbacks */ + .intf_probe = pcan_usb_probe, + .dev_init = pcan_usb_init, + .dev_set_bus = pcan_usb_write_mode, + .dev_set_bittiming = pcan_usb_set_bittiming, + .dev_get_device_id = pcan_usb_get_device_id, + .dev_decode_buf = pcan_usb_decode_buf, + .dev_encode_msg = pcan_usb_encode_msg, + .dev_start = pcan_usb_start, + .dev_restart_async = pcan_usb_restart_async, + .do_get_berr_counter = pcan_usb_get_berr_counter, +}; diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c new file mode 100644 index 000000000..73c1bc3cb --- /dev/null +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c @@ -0,0 +1,1020 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * CAN driver for PEAK System USB adapters + * Derived from the PCAN project file driver/src/pcan_usb_core.c + * + * Copyright (C) 2003-2010 PEAK System-Technik GmbH + * Copyright (C) 2010-2012 Stephane Grosjean <s.grosjean@peak-system.com> + * + * Many thanks to Klaus Hitschler <klaus.hitschler@gmx.de> + */ +#include <linux/init.h> +#include <linux/signal.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/usb.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> + +#include "pcan_usb_core.h" + +MODULE_AUTHOR("Stephane Grosjean <s.grosjean@peak-system.com>"); +MODULE_DESCRIPTION("CAN driver for PEAK-System USB adapters"); +MODULE_LICENSE("GPL v2"); + +/* Table of devices that work with this driver */ +static struct usb_device_id peak_usb_table[] = { + {USB_DEVICE(PCAN_USB_VENDOR_ID, PCAN_USB_PRODUCT_ID)}, + {USB_DEVICE(PCAN_USB_VENDOR_ID, PCAN_USBPRO_PRODUCT_ID)}, + {USB_DEVICE(PCAN_USB_VENDOR_ID, PCAN_USBFD_PRODUCT_ID)}, + {USB_DEVICE(PCAN_USB_VENDOR_ID, PCAN_USBPROFD_PRODUCT_ID)}, + {USB_DEVICE(PCAN_USB_VENDOR_ID, PCAN_USBCHIP_PRODUCT_ID)}, + {USB_DEVICE(PCAN_USB_VENDOR_ID, PCAN_USBX6_PRODUCT_ID)}, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, peak_usb_table); + +/* List of supported PCAN-USB adapters (NULL terminated list) */ +static const struct peak_usb_adapter *const peak_usb_adapters_list[] = { + &pcan_usb, + &pcan_usb_pro, + &pcan_usb_fd, + &pcan_usb_pro_fd, + &pcan_usb_chip, + &pcan_usb_x6, +}; + +/* + * dump memory + */ +#define DUMP_WIDTH 16 +void pcan_dump_mem(char *prompt, void *p, int l) +{ + pr_info("%s dumping %s (%d bytes):\n", + PCAN_USB_DRIVER_NAME, prompt ? prompt : "memory", l); + print_hex_dump(KERN_INFO, PCAN_USB_DRIVER_NAME " ", DUMP_PREFIX_NONE, + DUMP_WIDTH, 1, p, l, false); +} + +/* + * initialize a time_ref object with usb adapter own settings + */ +void peak_usb_init_time_ref(struct peak_time_ref *time_ref, + const struct peak_usb_adapter *adapter) +{ + if (time_ref) { + memset(time_ref, 0, sizeof(struct peak_time_ref)); + time_ref->adapter = adapter; + } +} + +/* + * sometimes, another now may be more recent than current one... + */ +void peak_usb_update_ts_now(struct peak_time_ref *time_ref, u32 ts_now) +{ + time_ref->ts_dev_2 = ts_now; + + /* should wait at least two passes before computing */ + if (ktime_to_ns(time_ref->tv_host) > 0) { + u32 delta_ts = time_ref->ts_dev_2 - time_ref->ts_dev_1; + + if (time_ref->ts_dev_2 < time_ref->ts_dev_1) + delta_ts &= (1 << time_ref->adapter->ts_used_bits) - 1; + + time_ref->ts_total += delta_ts; + } +} + +/* + * register device timestamp as now + */ +void peak_usb_set_ts_now(struct peak_time_ref *time_ref, u32 ts_now) +{ + if (ktime_to_ns(time_ref->tv_host_0) == 0) { + /* use monotonic clock to correctly compute further deltas */ + time_ref->tv_host_0 = ktime_get(); + time_ref->tv_host = ktime_set(0, 0); + } else { + /* + * delta_us should not be >= 2^32 => delta should be < 4294s + * handle 32-bits wrapping here: if count of s. reaches 4200, + * reset counters and change time base + */ + if (ktime_to_ns(time_ref->tv_host)) { + ktime_t delta = ktime_sub(time_ref->tv_host, + time_ref->tv_host_0); + if (ktime_to_ns(delta) > (4200ull * NSEC_PER_SEC)) { + time_ref->tv_host_0 = time_ref->tv_host; + time_ref->ts_total = 0; + } + } + + time_ref->tv_host = ktime_get(); + time_ref->tick_count++; + } + + time_ref->ts_dev_1 = time_ref->ts_dev_2; + peak_usb_update_ts_now(time_ref, ts_now); +} + +/* + * compute time according to current ts and time_ref data + */ +void peak_usb_get_ts_time(struct peak_time_ref *time_ref, u32 ts, ktime_t *time) +{ + /* protect from getting time before setting now */ + if (ktime_to_ns(time_ref->tv_host)) { + u64 delta_us; + s64 delta_ts = 0; + + /* General case: dev_ts_1 < dev_ts_2 < ts, with: + * + * - dev_ts_1 = previous sync timestamp + * - dev_ts_2 = last sync timestamp + * - ts = event timestamp + * - ts_period = known sync period (theoretical) + * ~ dev_ts2 - dev_ts1 + * *but*: + * + * - time counters wrap (see adapter->ts_used_bits) + * - sometimes, dev_ts_1 < ts < dev_ts2 + * + * "normal" case (sync time counters increase): + * must take into account case when ts wraps (tsw) + * + * < ts_period > < > + * | | | + * ---+--------+----+-------0-+--+--> + * ts_dev_1 | ts_dev_2 | + * ts tsw + */ + if (time_ref->ts_dev_1 < time_ref->ts_dev_2) { + /* case when event time (tsw) wraps */ + if (ts < time_ref->ts_dev_1) + delta_ts = BIT_ULL(time_ref->adapter->ts_used_bits); + + /* Otherwise, sync time counter (ts_dev_2) has wrapped: + * handle case when event time (tsn) hasn't. + * + * < ts_period > < > + * | | | + * ---+--------+--0-+---------+--+--> + * ts_dev_1 | ts_dev_2 | + * tsn ts + */ + } else if (time_ref->ts_dev_1 < ts) { + delta_ts = -BIT_ULL(time_ref->adapter->ts_used_bits); + } + + /* add delay between last sync and event timestamps */ + delta_ts += (signed int)(ts - time_ref->ts_dev_2); + + /* add time from beginning to last sync */ + delta_ts += time_ref->ts_total; + + /* convert ticks number into microseconds */ + delta_us = delta_ts * time_ref->adapter->us_per_ts_scale; + delta_us >>= time_ref->adapter->us_per_ts_shift; + + *time = ktime_add_us(time_ref->tv_host_0, delta_us); + } else { + *time = ktime_get(); + } +} + +/* + * post received skb after having set any hw timestamp + */ +int peak_usb_netif_rx(struct sk_buff *skb, + struct peak_time_ref *time_ref, u32 ts_low) +{ + struct skb_shared_hwtstamps *hwts = skb_hwtstamps(skb); + + peak_usb_get_ts_time(time_ref, ts_low, &hwts->hwtstamp); + + return netif_rx(skb); +} + +/* + * callback for bulk Rx urb + */ +static void peak_usb_read_bulk_callback(struct urb *urb) +{ + struct peak_usb_device *dev = urb->context; + struct net_device *netdev; + int err; + + netdev = dev->netdev; + + if (!netif_device_present(netdev)) + return; + + /* check reception status */ + switch (urb->status) { + case 0: + /* success */ + break; + + case -EILSEQ: + case -ENOENT: + case -ECONNRESET: + case -ESHUTDOWN: + return; + + default: + if (net_ratelimit()) + netdev_err(netdev, + "Rx urb aborted (%d)\n", urb->status); + goto resubmit_urb; + } + + /* protect from any incoming empty msgs */ + if ((urb->actual_length > 0) && (dev->adapter->dev_decode_buf)) { + /* handle these kinds of msgs only if _start callback called */ + if (dev->state & PCAN_USB_STATE_STARTED) { + err = dev->adapter->dev_decode_buf(dev, urb); + if (err) + pcan_dump_mem("received usb message", + urb->transfer_buffer, + urb->transfer_buffer_length); + } + } + +resubmit_urb: + usb_fill_bulk_urb(urb, dev->udev, + usb_rcvbulkpipe(dev->udev, dev->ep_msg_in), + urb->transfer_buffer, dev->adapter->rx_buffer_size, + peak_usb_read_bulk_callback, dev); + + usb_anchor_urb(urb, &dev->rx_submitted); + err = usb_submit_urb(urb, GFP_ATOMIC); + if (!err) + return; + + usb_unanchor_urb(urb); + + if (err == -ENODEV) + netif_device_detach(netdev); + else + netdev_err(netdev, "failed resubmitting read bulk urb: %d\n", + err); +} + +/* + * callback for bulk Tx urb + */ +static void peak_usb_write_bulk_callback(struct urb *urb) +{ + struct peak_tx_urb_context *context = urb->context; + struct peak_usb_device *dev; + struct net_device *netdev; + + BUG_ON(!context); + + dev = context->dev; + netdev = dev->netdev; + + atomic_dec(&dev->active_tx_urbs); + + if (!netif_device_present(netdev)) + return; + + /* check tx status */ + switch (urb->status) { + case 0: + /* transmission complete */ + netdev->stats.tx_packets++; + netdev->stats.tx_bytes += context->data_len; + + /* prevent tx timeout */ + netif_trans_update(netdev); + break; + + default: + if (net_ratelimit()) + netdev_err(netdev, "Tx urb aborted (%d)\n", + urb->status); + case -EPROTO: + case -ENOENT: + case -ECONNRESET: + case -ESHUTDOWN: + + break; + } + + /* should always release echo skb and corresponding context */ + can_get_echo_skb(netdev, context->echo_index); + context->echo_index = PCAN_USB_MAX_TX_URBS; + + /* do wakeup tx queue in case of success only */ + if (!urb->status) + netif_wake_queue(netdev); +} + +/* + * called by netdev to send one skb on the CAN interface. + */ +static netdev_tx_t peak_usb_ndo_start_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + struct peak_usb_device *dev = netdev_priv(netdev); + struct peak_tx_urb_context *context = NULL; + struct net_device_stats *stats = &netdev->stats; + struct canfd_frame *cfd = (struct canfd_frame *)skb->data; + struct urb *urb; + u8 *obuf; + int i, err; + size_t size = dev->adapter->tx_buffer_size; + + if (can_dropped_invalid_skb(netdev, skb)) + return NETDEV_TX_OK; + + for (i = 0; i < PCAN_USB_MAX_TX_URBS; i++) + if (dev->tx_contexts[i].echo_index == PCAN_USB_MAX_TX_URBS) { + context = dev->tx_contexts + i; + break; + } + + if (!context) { + /* should not occur except during restart */ + return NETDEV_TX_BUSY; + } + + urb = context->urb; + obuf = urb->transfer_buffer; + + err = dev->adapter->dev_encode_msg(dev, skb, obuf, &size); + if (err) { + if (net_ratelimit()) + netdev_err(netdev, "packet dropped\n"); + dev_kfree_skb(skb); + stats->tx_dropped++; + return NETDEV_TX_OK; + } + + context->echo_index = i; + + /* Note: this works with CANFD frames too */ + context->data_len = cfd->len; + + usb_anchor_urb(urb, &dev->tx_submitted); + + can_put_echo_skb(skb, netdev, context->echo_index); + + atomic_inc(&dev->active_tx_urbs); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err) { + can_free_echo_skb(netdev, context->echo_index); + + usb_unanchor_urb(urb); + + /* this context is not used in fact */ + context->echo_index = PCAN_USB_MAX_TX_URBS; + + atomic_dec(&dev->active_tx_urbs); + + switch (err) { + case -ENODEV: + netif_device_detach(netdev); + break; + default: + netdev_warn(netdev, "tx urb submitting failed err=%d\n", + err); + fallthrough; + case -ENOENT: + /* cable unplugged */ + stats->tx_dropped++; + } + } else { + netif_trans_update(netdev); + + /* slow down tx path */ + if (atomic_read(&dev->active_tx_urbs) >= PCAN_USB_MAX_TX_URBS) + netif_stop_queue(netdev); + } + + return NETDEV_TX_OK; +} + +/* + * start the CAN interface. + * Rx and Tx urbs are allocated here. Rx urbs are submitted here. + */ +static int peak_usb_start(struct peak_usb_device *dev) +{ + struct net_device *netdev = dev->netdev; + int err, i; + + for (i = 0; i < PCAN_USB_MAX_RX_URBS; i++) { + struct urb *urb; + u8 *buf; + + /* create a URB, and a buffer for it, to receive usb messages */ + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + err = -ENOMEM; + break; + } + + buf = kmalloc(dev->adapter->rx_buffer_size, GFP_KERNEL); + if (!buf) { + usb_free_urb(urb); + err = -ENOMEM; + break; + } + + usb_fill_bulk_urb(urb, dev->udev, + usb_rcvbulkpipe(dev->udev, dev->ep_msg_in), + buf, dev->adapter->rx_buffer_size, + peak_usb_read_bulk_callback, dev); + + /* ask last usb_free_urb() to also kfree() transfer_buffer */ + urb->transfer_flags |= URB_FREE_BUFFER; + usb_anchor_urb(urb, &dev->rx_submitted); + + err = usb_submit_urb(urb, GFP_KERNEL); + if (err) { + if (err == -ENODEV) + netif_device_detach(dev->netdev); + + usb_unanchor_urb(urb); + kfree(buf); + usb_free_urb(urb); + break; + } + + /* drop reference, USB core will take care of freeing it */ + usb_free_urb(urb); + } + + /* did we submit any URBs? Warn if we was not able to submit all urbs */ + if (i < PCAN_USB_MAX_RX_URBS) { + if (i == 0) { + netdev_err(netdev, "couldn't setup any rx URB\n"); + return err; + } + + netdev_warn(netdev, "rx performance may be slow\n"); + } + + /* pre-alloc tx buffers and corresponding urbs */ + for (i = 0; i < PCAN_USB_MAX_TX_URBS; i++) { + struct peak_tx_urb_context *context; + struct urb *urb; + u8 *buf; + + /* create a URB and a buffer for it, to transmit usb messages */ + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + err = -ENOMEM; + break; + } + + buf = kmalloc(dev->adapter->tx_buffer_size, GFP_KERNEL); + if (!buf) { + usb_free_urb(urb); + err = -ENOMEM; + break; + } + + context = dev->tx_contexts + i; + context->dev = dev; + context->urb = urb; + + usb_fill_bulk_urb(urb, dev->udev, + usb_sndbulkpipe(dev->udev, dev->ep_msg_out), + buf, dev->adapter->tx_buffer_size, + peak_usb_write_bulk_callback, context); + + /* ask last usb_free_urb() to also kfree() transfer_buffer */ + urb->transfer_flags |= URB_FREE_BUFFER; + } + + /* warn if we were not able to allocate enough tx contexts */ + if (i < PCAN_USB_MAX_TX_URBS) { + if (i == 0) { + netdev_err(netdev, "couldn't setup any tx URB\n"); + goto err_tx; + } + + netdev_warn(netdev, "tx performance may be slow\n"); + } + + if (dev->adapter->dev_start) { + err = dev->adapter->dev_start(dev); + if (err) + goto err_adapter; + } + + dev->state |= PCAN_USB_STATE_STARTED; + + /* can set bus on now */ + if (dev->adapter->dev_set_bus) { + err = dev->adapter->dev_set_bus(dev, 1); + if (err) + goto err_adapter; + } + + dev->can.state = CAN_STATE_ERROR_ACTIVE; + + return 0; + +err_adapter: + if (err == -ENODEV) + netif_device_detach(dev->netdev); + + netdev_warn(netdev, "couldn't submit control: %d\n", err); + + for (i = 0; i < PCAN_USB_MAX_TX_URBS; i++) { + usb_free_urb(dev->tx_contexts[i].urb); + dev->tx_contexts[i].urb = NULL; + } +err_tx: + usb_kill_anchored_urbs(&dev->rx_submitted); + + return err; +} + +/* + * called by netdev to open the corresponding CAN interface. + */ +static int peak_usb_ndo_open(struct net_device *netdev) +{ + struct peak_usb_device *dev = netdev_priv(netdev); + int err; + + /* common open */ + err = open_candev(netdev); + if (err) + return err; + + /* finally start device */ + err = peak_usb_start(dev); + if (err) { + netdev_err(netdev, "couldn't start device: %d\n", err); + close_candev(netdev); + return err; + } + + netif_start_queue(netdev); + + return 0; +} + +/* + * unlink in-flight Rx and Tx urbs and free their memory. + */ +static void peak_usb_unlink_all_urbs(struct peak_usb_device *dev) +{ + int i; + + /* free all Rx (submitted) urbs */ + usb_kill_anchored_urbs(&dev->rx_submitted); + + /* free unsubmitted Tx urbs first */ + for (i = 0; i < PCAN_USB_MAX_TX_URBS; i++) { + struct urb *urb = dev->tx_contexts[i].urb; + + if (!urb || + dev->tx_contexts[i].echo_index != PCAN_USB_MAX_TX_URBS) { + /* + * this urb is already released or always submitted, + * let usb core free by itself + */ + continue; + } + + usb_free_urb(urb); + dev->tx_contexts[i].urb = NULL; + } + + /* then free all submitted Tx urbs */ + usb_kill_anchored_urbs(&dev->tx_submitted); + atomic_set(&dev->active_tx_urbs, 0); +} + +/* + * called by netdev to close the corresponding CAN interface. + */ +static int peak_usb_ndo_stop(struct net_device *netdev) +{ + struct peak_usb_device *dev = netdev_priv(netdev); + + dev->state &= ~PCAN_USB_STATE_STARTED; + netif_stop_queue(netdev); + + close_candev(netdev); + + dev->can.state = CAN_STATE_STOPPED; + + /* unlink all pending urbs and free used memory */ + peak_usb_unlink_all_urbs(dev); + + if (dev->adapter->dev_stop) + dev->adapter->dev_stop(dev); + + /* can set bus off now */ + if (dev->adapter->dev_set_bus) { + int err = dev->adapter->dev_set_bus(dev, 0); + if (err) + return err; + } + + return 0; +} + +/* + * handle end of waiting for the device to reset + */ +void peak_usb_restart_complete(struct peak_usb_device *dev) +{ + /* finally MUST update can state */ + dev->can.state = CAN_STATE_ERROR_ACTIVE; + + /* netdev queue can be awaken now */ + netif_wake_queue(dev->netdev); +} + +void peak_usb_async_complete(struct urb *urb) +{ + kfree(urb->transfer_buffer); + usb_free_urb(urb); +} + +/* + * device (auto-)restart mechanism runs in a timer context => + * MUST handle restart with asynchronous usb transfers + */ +static int peak_usb_restart(struct peak_usb_device *dev) +{ + struct urb *urb; + int err; + u8 *buf; + + /* + * if device doesn't define any asynchronous restart handler, simply + * wake the netdev queue up + */ + if (!dev->adapter->dev_restart_async) { + peak_usb_restart_complete(dev); + return 0; + } + + /* first allocate a urb to handle the asynchronous steps */ + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) + return -ENOMEM; + + /* also allocate enough space for the commands to send */ + buf = kmalloc(PCAN_USB_MAX_CMD_LEN, GFP_ATOMIC); + if (!buf) { + usb_free_urb(urb); + return -ENOMEM; + } + + /* call the device specific handler for the restart */ + err = dev->adapter->dev_restart_async(dev, urb, buf); + if (!err) + return 0; + + kfree(buf); + usb_free_urb(urb); + + return err; +} + +/* + * candev callback used to change CAN mode. + * Warning: this is called from a timer context! + */ +static int peak_usb_set_mode(struct net_device *netdev, enum can_mode mode) +{ + struct peak_usb_device *dev = netdev_priv(netdev); + int err = 0; + + switch (mode) { + case CAN_MODE_START: + err = peak_usb_restart(dev); + if (err) + netdev_err(netdev, "couldn't start device (err %d)\n", + err); + break; + + default: + return -EOPNOTSUPP; + } + + return err; +} + +/* + * candev callback used to set device nominal/arbitration bitrate. + */ +static int peak_usb_set_bittiming(struct net_device *netdev) +{ + struct peak_usb_device *dev = netdev_priv(netdev); + const struct peak_usb_adapter *pa = dev->adapter; + + if (pa->dev_set_bittiming) { + struct can_bittiming *bt = &dev->can.bittiming; + int err = pa->dev_set_bittiming(dev, bt); + + if (err) + netdev_info(netdev, "couldn't set bitrate (err %d)\n", + err); + return err; + } + + return 0; +} + +/* + * candev callback used to set device data bitrate. + */ +static int peak_usb_set_data_bittiming(struct net_device *netdev) +{ + struct peak_usb_device *dev = netdev_priv(netdev); + const struct peak_usb_adapter *pa = dev->adapter; + + if (pa->dev_set_data_bittiming) { + struct can_bittiming *bt = &dev->can.data_bittiming; + int err = pa->dev_set_data_bittiming(dev, bt); + + if (err) + netdev_info(netdev, + "couldn't set data bitrate (err %d)\n", + err); + + return err; + } + + return 0; +} + +static const struct net_device_ops peak_usb_netdev_ops = { + .ndo_open = peak_usb_ndo_open, + .ndo_stop = peak_usb_ndo_stop, + .ndo_start_xmit = peak_usb_ndo_start_xmit, + .ndo_change_mtu = can_change_mtu, +}; + +/* + * create one device which is attached to CAN controller #ctrl_idx of the + * usb adapter. + */ +static int peak_usb_create_dev(const struct peak_usb_adapter *peak_usb_adapter, + struct usb_interface *intf, int ctrl_idx) +{ + struct usb_device *usb_dev = interface_to_usbdev(intf); + int sizeof_candev = peak_usb_adapter->sizeof_dev_private; + struct peak_usb_device *dev; + struct net_device *netdev; + int i, err; + u16 tmp16; + + if (sizeof_candev < sizeof(struct peak_usb_device)) + sizeof_candev = sizeof(struct peak_usb_device); + + netdev = alloc_candev(sizeof_candev, PCAN_USB_MAX_TX_URBS); + if (!netdev) { + dev_err(&intf->dev, "%s: couldn't alloc candev\n", + PCAN_USB_DRIVER_NAME); + return -ENOMEM; + } + + dev = netdev_priv(netdev); + + /* allocate a buffer large enough to send commands */ + dev->cmd_buf = kzalloc(PCAN_USB_MAX_CMD_LEN, GFP_KERNEL); + if (!dev->cmd_buf) { + err = -ENOMEM; + goto lbl_free_candev; + } + + dev->udev = usb_dev; + dev->netdev = netdev; + dev->adapter = peak_usb_adapter; + dev->ctrl_idx = ctrl_idx; + dev->state = PCAN_USB_STATE_CONNECTED; + + dev->ep_msg_in = peak_usb_adapter->ep_msg_in; + dev->ep_msg_out = peak_usb_adapter->ep_msg_out[ctrl_idx]; + + dev->can.clock = peak_usb_adapter->clock; + dev->can.bittiming_const = peak_usb_adapter->bittiming_const; + dev->can.do_set_bittiming = peak_usb_set_bittiming; + dev->can.data_bittiming_const = peak_usb_adapter->data_bittiming_const; + dev->can.do_set_data_bittiming = peak_usb_set_data_bittiming; + dev->can.do_set_mode = peak_usb_set_mode; + dev->can.do_get_berr_counter = peak_usb_adapter->do_get_berr_counter; + dev->can.ctrlmode_supported = peak_usb_adapter->ctrlmode_supported; + + netdev->netdev_ops = &peak_usb_netdev_ops; + + netdev->flags |= IFF_ECHO; /* we support local echo */ + + init_usb_anchor(&dev->rx_submitted); + + init_usb_anchor(&dev->tx_submitted); + atomic_set(&dev->active_tx_urbs, 0); + + for (i = 0; i < PCAN_USB_MAX_TX_URBS; i++) + dev->tx_contexts[i].echo_index = PCAN_USB_MAX_TX_URBS; + + dev->prev_siblings = usb_get_intfdata(intf); + usb_set_intfdata(intf, dev); + + SET_NETDEV_DEV(netdev, &intf->dev); + netdev->dev_id = ctrl_idx; + + err = register_candev(netdev); + if (err) { + dev_err(&intf->dev, "couldn't register CAN device: %d\n", err); + goto lbl_restore_intf_data; + } + + if (dev->prev_siblings) + (dev->prev_siblings)->next_siblings = dev; + + /* keep hw revision into the netdevice */ + tmp16 = le16_to_cpu(usb_dev->descriptor.bcdDevice); + dev->device_rev = tmp16 >> 8; + + if (dev->adapter->dev_init) { + err = dev->adapter->dev_init(dev); + if (err) + goto lbl_unregister_candev; + } + + /* set bus off */ + if (dev->adapter->dev_set_bus) { + err = dev->adapter->dev_set_bus(dev, 0); + if (err) + goto adap_dev_free; + } + + /* get device number early */ + if (dev->adapter->dev_get_device_id) + dev->adapter->dev_get_device_id(dev, &dev->device_number); + + netdev_info(netdev, "attached to %s channel %u (device %u)\n", + peak_usb_adapter->name, ctrl_idx, dev->device_number); + + return 0; + +adap_dev_free: + if (dev->adapter->dev_free) + dev->adapter->dev_free(dev); + +lbl_unregister_candev: + unregister_candev(netdev); + +lbl_restore_intf_data: + usb_set_intfdata(intf, dev->prev_siblings); + kfree(dev->cmd_buf); + +lbl_free_candev: + free_candev(netdev); + + return err; +} + +/* + * called by the usb core when the device is unplugged from the system + */ +static void peak_usb_disconnect(struct usb_interface *intf) +{ + struct peak_usb_device *dev; + struct peak_usb_device *dev_prev_siblings; + + /* unregister as many netdev devices as siblings */ + for (dev = usb_get_intfdata(intf); dev; dev = dev_prev_siblings) { + struct net_device *netdev = dev->netdev; + char name[IFNAMSIZ]; + + dev_prev_siblings = dev->prev_siblings; + dev->state &= ~PCAN_USB_STATE_CONNECTED; + strlcpy(name, netdev->name, IFNAMSIZ); + + unregister_netdev(netdev); + + kfree(dev->cmd_buf); + dev->next_siblings = NULL; + if (dev->adapter->dev_free) + dev->adapter->dev_free(dev); + + free_candev(netdev); + dev_info(&intf->dev, "%s removed\n", name); + } + + usb_set_intfdata(intf, NULL); +} + +/* + * probe function for new PEAK-System devices + */ +static int peak_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *usb_dev = interface_to_usbdev(intf); + const u16 usb_id_product = le16_to_cpu(usb_dev->descriptor.idProduct); + const struct peak_usb_adapter *peak_usb_adapter = NULL; + int i, err = -ENOMEM; + + /* get corresponding PCAN-USB adapter */ + for (i = 0; i < ARRAY_SIZE(peak_usb_adapters_list); i++) + if (peak_usb_adapters_list[i]->device_id == usb_id_product) { + peak_usb_adapter = peak_usb_adapters_list[i]; + break; + } + + if (!peak_usb_adapter) { + /* should never come except device_id bad usage in this file */ + pr_err("%s: didn't find device id. 0x%x in devices list\n", + PCAN_USB_DRIVER_NAME, usb_id_product); + return -ENODEV; + } + + /* got corresponding adapter: check if it handles current interface */ + if (peak_usb_adapter->intf_probe) { + err = peak_usb_adapter->intf_probe(intf); + if (err) + return err; + } + + for (i = 0; i < peak_usb_adapter->ctrl_count; i++) { + err = peak_usb_create_dev(peak_usb_adapter, intf, i); + if (err) { + /* deregister already created devices */ + peak_usb_disconnect(intf); + break; + } + } + + return err; +} + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver peak_usb_driver = { + .name = PCAN_USB_DRIVER_NAME, + .disconnect = peak_usb_disconnect, + .probe = peak_usb_probe, + .id_table = peak_usb_table, +}; + +static int __init peak_usb_init(void) +{ + int err; + + /* register this driver with the USB subsystem */ + err = usb_register(&peak_usb_driver); + if (err) + pr_err("%s: usb_register failed (err %d)\n", + PCAN_USB_DRIVER_NAME, err); + + return err; +} + +static int peak_usb_do_device_exit(struct device *d, void *arg) +{ + struct usb_interface *intf = to_usb_interface(d); + struct peak_usb_device *dev; + + /* stop as many netdev devices as siblings */ + for (dev = usb_get_intfdata(intf); dev; dev = dev->prev_siblings) { + struct net_device *netdev = dev->netdev; + + if (netif_device_present(netdev)) + if (dev->adapter->dev_exit) + dev->adapter->dev_exit(dev); + } + + return 0; +} + +static void __exit peak_usb_exit(void) +{ + int err; + + /* last chance do send any synchronous commands here */ + err = driver_for_each_device(&peak_usb_driver.drvwrap.driver, NULL, + NULL, peak_usb_do_device_exit); + if (err) + pr_err("%s: failed to stop all can devices (err %d)\n", + PCAN_USB_DRIVER_NAME, err); + + /* deregister this driver with the USB subsystem */ + usb_deregister(&peak_usb_driver); + + pr_info("%s: PCAN-USB interfaces driver unloaded\n", + PCAN_USB_DRIVER_NAME); +} + +module_init(peak_usb_init); +module_exit(peak_usb_exit); diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.h b/drivers/net/can/usb/peak_usb/pcan_usb_core.h new file mode 100644 index 000000000..4b1528a42 --- /dev/null +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.h @@ -0,0 +1,152 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * CAN driver for PEAK System USB adapters + * Derived from the PCAN project file driver/src/pcan_usb_core.c + * + * Copyright (C) 2003-2010 PEAK System-Technik GmbH + * Copyright (C) 2010-2012 Stephane Grosjean <s.grosjean@peak-system.com> + * + * Many thanks to Klaus Hitschler <klaus.hitschler@gmx.de> + */ +#ifndef PCAN_USB_CORE_H +#define PCAN_USB_CORE_H + +/* PEAK-System vendor id. */ +#define PCAN_USB_VENDOR_ID 0x0c72 + +/* supported device ids. */ +#define PCAN_USB_PRODUCT_ID 0x000c +#define PCAN_USBPRO_PRODUCT_ID 0x000d +#define PCAN_USBPROFD_PRODUCT_ID 0x0011 +#define PCAN_USBFD_PRODUCT_ID 0x0012 +#define PCAN_USBCHIP_PRODUCT_ID 0x0013 +#define PCAN_USBX6_PRODUCT_ID 0x0014 + +#define PCAN_USB_DRIVER_NAME "peak_usb" + +/* number of urbs that are submitted for rx/tx per channel */ +#define PCAN_USB_MAX_RX_URBS 4 +#define PCAN_USB_MAX_TX_URBS 10 + +/* usb adapters maximum channels per usb interface */ +#define PCAN_USB_MAX_CHANNEL 2 + +/* maximum length of the usb commands sent to/received from the devices */ +#define PCAN_USB_MAX_CMD_LEN 32 + +struct peak_usb_device; + +/* PEAK-System USB adapter descriptor */ +struct peak_usb_adapter { + char *name; + u32 device_id; + u32 ctrlmode_supported; + struct can_clock clock; + const struct can_bittiming_const * const bittiming_const; + const struct can_bittiming_const * const data_bittiming_const; + unsigned int ctrl_count; + + int (*intf_probe)(struct usb_interface *intf); + + int (*dev_init)(struct peak_usb_device *dev); + void (*dev_exit)(struct peak_usb_device *dev); + void (*dev_free)(struct peak_usb_device *dev); + int (*dev_open)(struct peak_usb_device *dev); + int (*dev_close)(struct peak_usb_device *dev); + int (*dev_set_bittiming)(struct peak_usb_device *dev, + struct can_bittiming *bt); + int (*dev_set_data_bittiming)(struct peak_usb_device *dev, + struct can_bittiming *bt); + int (*dev_set_bus)(struct peak_usb_device *dev, u8 onoff); + int (*dev_get_device_id)(struct peak_usb_device *dev, u32 *device_id); + int (*dev_decode_buf)(struct peak_usb_device *dev, struct urb *urb); + int (*dev_encode_msg)(struct peak_usb_device *dev, struct sk_buff *skb, + u8 *obuf, size_t *size); + int (*dev_start)(struct peak_usb_device *dev); + int (*dev_stop)(struct peak_usb_device *dev); + int (*dev_restart_async)(struct peak_usb_device *dev, struct urb *urb, + u8 *buf); + int (*do_get_berr_counter)(const struct net_device *netdev, + struct can_berr_counter *bec); + u8 ep_msg_in; + u8 ep_msg_out[PCAN_USB_MAX_CHANNEL]; + u8 ts_used_bits; + u32 ts_period; + u8 us_per_ts_shift; + u32 us_per_ts_scale; + + int rx_buffer_size; + int tx_buffer_size; + int sizeof_dev_private; +}; + +extern const struct peak_usb_adapter pcan_usb; +extern const struct peak_usb_adapter pcan_usb_pro; +extern const struct peak_usb_adapter pcan_usb_fd; +extern const struct peak_usb_adapter pcan_usb_chip; +extern const struct peak_usb_adapter pcan_usb_pro_fd; +extern const struct peak_usb_adapter pcan_usb_x6; + +struct peak_time_ref { + ktime_t tv_host_0, tv_host; + u32 ts_dev_1, ts_dev_2; + u64 ts_total; + u32 tick_count; + const struct peak_usb_adapter *adapter; +}; + +struct peak_tx_urb_context { + struct peak_usb_device *dev; + u32 echo_index; + u8 data_len; + struct urb *urb; +}; + +#define PCAN_USB_STATE_CONNECTED 0x00000001 +#define PCAN_USB_STATE_STARTED 0x00000002 + +/* PEAK-System USB device */ +struct peak_usb_device { + struct can_priv can; + const struct peak_usb_adapter *adapter; + unsigned int ctrl_idx; + u32 state; + + struct sk_buff *echo_skb[PCAN_USB_MAX_TX_URBS]; + + struct usb_device *udev; + struct net_device *netdev; + + atomic_t active_tx_urbs; + struct usb_anchor tx_submitted; + struct peak_tx_urb_context tx_contexts[PCAN_USB_MAX_TX_URBS]; + + u8 *cmd_buf; + struct usb_anchor rx_submitted; + + u32 device_number; + u8 device_rev; + + u8 ep_msg_in; + u8 ep_msg_out; + + u16 bus_load; + + struct peak_usb_device *prev_siblings; + struct peak_usb_device *next_siblings; +}; + +void pcan_dump_mem(char *prompt, void *p, int l); + +/* common timestamp management */ +void peak_usb_init_time_ref(struct peak_time_ref *time_ref, + const struct peak_usb_adapter *adapter); +void peak_usb_update_ts_now(struct peak_time_ref *time_ref, u32 ts_now); +void peak_usb_set_ts_now(struct peak_time_ref *time_ref, u32 ts_now); +void peak_usb_get_ts_time(struct peak_time_ref *time_ref, u32 ts, ktime_t *tv); +int peak_usb_netif_rx(struct sk_buff *skb, + struct peak_time_ref *time_ref, u32 ts_low); +void peak_usb_async_complete(struct urb *urb); +void peak_usb_restart_complete(struct peak_usb_device *dev); + +#endif diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c new file mode 100644 index 000000000..301a0f54f --- /dev/null +++ b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c @@ -0,0 +1,1294 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * CAN driver for PEAK System PCAN-USB FD / PCAN-USB Pro FD adapter + * + * Copyright (C) 2013-2014 Stephane Grosjean <s.grosjean@peak-system.com> + */ +#include <linux/netdevice.h> +#include <linux/usb.h> +#include <linux/module.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> +#include <linux/can/dev/peak_canfd.h> + +#include "pcan_usb_core.h" +#include "pcan_usb_pro.h" + +MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB FD adapter"); +MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB Pro FD adapter"); + +#define PCAN_USBPROFD_CHANNEL_COUNT 2 +#define PCAN_USBFD_CHANNEL_COUNT 1 + +/* PCAN-USB Pro FD adapter internal clock (Hz) */ +#define PCAN_UFD_CRYSTAL_HZ 80000000 + +#define PCAN_UFD_CMD_BUFFER_SIZE 512 +#define PCAN_UFD_LOSPD_PKT_SIZE 64 + +/* PCAN-USB Pro FD command timeout (ms.) */ +#define PCAN_UFD_CMD_TIMEOUT_MS 1000 + +/* PCAN-USB Pro FD rx/tx buffers size */ +#define PCAN_UFD_RX_BUFFER_SIZE 2048 +#define PCAN_UFD_TX_BUFFER_SIZE 512 + +/* read some versions info from the hw device */ +struct __packed pcan_ufd_fw_info { + __le16 size_of; /* sizeof this */ + __le16 type; /* type of this structure */ + u8 hw_type; /* Type of hardware (HW_TYPE_xxx) */ + u8 bl_version[3]; /* Bootloader version */ + u8 hw_version; /* Hardware version (PCB) */ + u8 fw_version[3]; /* Firmware version */ + __le32 dev_id[2]; /* "device id" per CAN */ + __le32 ser_no; /* S/N */ + __le32 flags; /* special functions */ +}; + +/* handle device specific info used by the netdevices */ +struct pcan_usb_fd_if { + struct peak_usb_device *dev[PCAN_USB_MAX_CHANNEL]; + struct pcan_ufd_fw_info fw_info; + struct peak_time_ref time_ref; + int cm_ignore_count; + int dev_opened_count; +}; + +/* device information */ +struct pcan_usb_fd_device { + struct peak_usb_device dev; + struct can_berr_counter bec; + struct pcan_usb_fd_if *usb_if; + u8 *cmd_buffer_addr; +}; + +/* Extended USB commands (non uCAN commands) */ + +/* Clock Modes command */ +#define PCAN_UFD_CMD_CLK_SET 0x80 + +#define PCAN_UFD_CLK_80MHZ 0x0 +#define PCAN_UFD_CLK_60MHZ 0x1 +#define PCAN_UFD_CLK_40MHZ 0x2 +#define PCAN_UFD_CLK_30MHZ 0x3 +#define PCAN_UFD_CLK_24MHZ 0x4 +#define PCAN_UFD_CLK_20MHZ 0x5 +#define PCAN_UFD_CLK_DEF PCAN_UFD_CLK_80MHZ + +struct __packed pcan_ufd_clock { + __le16 opcode_channel; + + u8 mode; + u8 unused[5]; +}; + +/* LED control command */ +#define PCAN_UFD_CMD_LED_SET 0x86 + +#define PCAN_UFD_LED_DEV 0x00 +#define PCAN_UFD_LED_FAST 0x01 +#define PCAN_UFD_LED_SLOW 0x02 +#define PCAN_UFD_LED_ON 0x03 +#define PCAN_UFD_LED_OFF 0x04 +#define PCAN_UFD_LED_DEF PCAN_UFD_LED_DEV + +struct __packed pcan_ufd_led { + __le16 opcode_channel; + + u8 mode; + u8 unused[5]; +}; + +/* Extended usage of uCAN commands CMD_xxx_xx_OPTION for PCAN-USB Pro FD */ +#define PCAN_UFD_FLTEXT_CALIBRATION 0x8000 + +struct __packed pcan_ufd_options { + __le16 opcode_channel; + + __le16 ucan_mask; + u16 unused; + __le16 usb_mask; +}; + +/* Extended usage of uCAN messages for PCAN-USB Pro FD */ +#define PCAN_UFD_MSG_CALIBRATION 0x100 + +struct __packed pcan_ufd_ts_msg { + __le16 size; + __le16 type; + __le32 ts_low; + __le32 ts_high; + __le16 usb_frame_index; + u16 unused; +}; + +#define PCAN_UFD_MSG_OVERRUN 0x101 + +#define PCAN_UFD_OVMSG_CHANNEL(o) ((o)->channel & 0xf) + +struct __packed pcan_ufd_ovr_msg { + __le16 size; + __le16 type; + __le32 ts_low; + __le32 ts_high; + u8 channel; + u8 unused[3]; +}; + +static inline int pufd_omsg_get_channel(struct pcan_ufd_ovr_msg *om) +{ + return om->channel & 0xf; +} + +/* Clock mode frequency values */ +static const u32 pcan_usb_fd_clk_freq[6] = { + [PCAN_UFD_CLK_80MHZ] = 80000000, + [PCAN_UFD_CLK_60MHZ] = 60000000, + [PCAN_UFD_CLK_40MHZ] = 40000000, + [PCAN_UFD_CLK_30MHZ] = 30000000, + [PCAN_UFD_CLK_24MHZ] = 24000000, + [PCAN_UFD_CLK_20MHZ] = 20000000 +}; + +/* return a device USB interface */ +static inline +struct pcan_usb_fd_if *pcan_usb_fd_dev_if(struct peak_usb_device *dev) +{ + struct pcan_usb_fd_device *pdev = + container_of(dev, struct pcan_usb_fd_device, dev); + return pdev->usb_if; +} + +/* return a device USB commands buffer */ +static inline void *pcan_usb_fd_cmd_buffer(struct peak_usb_device *dev) +{ + struct pcan_usb_fd_device *pdev = + container_of(dev, struct pcan_usb_fd_device, dev); + return pdev->cmd_buffer_addr; +} + +/* send PCAN-USB Pro FD commands synchronously */ +static int pcan_usb_fd_send_cmd(struct peak_usb_device *dev, void *cmd_tail) +{ + void *cmd_head = pcan_usb_fd_cmd_buffer(dev); + int err = 0; + u8 *packet_ptr; + int packet_len; + ptrdiff_t cmd_len; + + /* usb device unregistered? */ + if (!(dev->state & PCAN_USB_STATE_CONNECTED)) + return 0; + + /* if a packet is not filled completely by commands, the command list + * is terminated with an "end of collection" record. + */ + cmd_len = cmd_tail - cmd_head; + if (cmd_len <= (PCAN_UFD_CMD_BUFFER_SIZE - sizeof(u64))) { + memset(cmd_tail, 0xff, sizeof(u64)); + cmd_len += sizeof(u64); + } + + packet_ptr = cmd_head; + packet_len = cmd_len; + + /* firmware is not able to re-assemble 512 bytes buffer in full-speed */ + if (unlikely(dev->udev->speed != USB_SPEED_HIGH)) + packet_len = min(packet_len, PCAN_UFD_LOSPD_PKT_SIZE); + + do { + err = usb_bulk_msg(dev->udev, + usb_sndbulkpipe(dev->udev, + PCAN_USBPRO_EP_CMDOUT), + packet_ptr, packet_len, + NULL, PCAN_UFD_CMD_TIMEOUT_MS); + if (err) { + netdev_err(dev->netdev, + "sending command failure: %d\n", err); + break; + } + + packet_ptr += packet_len; + cmd_len -= packet_len; + + if (cmd_len < PCAN_UFD_LOSPD_PKT_SIZE) + packet_len = cmd_len; + + } while (packet_len > 0); + + return err; +} + +/* build the commands list in the given buffer, to enter operational mode */ +static int pcan_usb_fd_build_restart_cmd(struct peak_usb_device *dev, u8 *buf) +{ + struct pucan_wr_err_cnt *prc; + struct pucan_command *cmd; + u8 *pc = buf; + + /* 1st, reset error counters: */ + prc = (struct pucan_wr_err_cnt *)pc; + prc->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, + PUCAN_CMD_WR_ERR_CNT); + + /* select both counters */ + prc->sel_mask = cpu_to_le16(PUCAN_WRERRCNT_TE|PUCAN_WRERRCNT_RE); + + /* and reset their values */ + prc->tx_counter = 0; + prc->rx_counter = 0; + + /* moves the pointer forward */ + pc += sizeof(struct pucan_wr_err_cnt); + + /* add command to switch from ISO to non-ISO mode, if fw allows it */ + if (dev->can.ctrlmode_supported & CAN_CTRLMODE_FD_NON_ISO) { + struct pucan_options *puo = (struct pucan_options *)pc; + + puo->opcode_channel = + (dev->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO) ? + pucan_cmd_opcode_channel(dev->ctrl_idx, + PUCAN_CMD_CLR_DIS_OPTION) : + pucan_cmd_opcode_channel(dev->ctrl_idx, + PUCAN_CMD_SET_EN_OPTION); + + puo->options = cpu_to_le16(PUCAN_OPTION_CANDFDISO); + + /* to be sure that no other extended bits will be taken into + * account + */ + puo->unused = 0; + + /* moves the pointer forward */ + pc += sizeof(struct pucan_options); + } + + /* next, go back to operational mode */ + cmd = (struct pucan_command *)pc; + cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, + (dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) ? + PUCAN_CMD_LISTEN_ONLY_MODE : + PUCAN_CMD_NORMAL_MODE); + pc += sizeof(struct pucan_command); + + return pc - buf; +} + +/* set CAN bus on/off */ +static int pcan_usb_fd_set_bus(struct peak_usb_device *dev, u8 onoff) +{ + u8 *pc = pcan_usb_fd_cmd_buffer(dev); + int l; + + if (onoff) { + /* build the cmds list to enter operational mode */ + l = pcan_usb_fd_build_restart_cmd(dev, pc); + } else { + struct pucan_command *cmd = (struct pucan_command *)pc; + + /* build cmd to go back to reset mode */ + cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, + PUCAN_CMD_RESET_MODE); + l = sizeof(struct pucan_command); + } + + /* send the command */ + return pcan_usb_fd_send_cmd(dev, pc + l); +} + +/* set filtering masks: + * + * idx in range [0..63] selects a row #idx, all rows otherwise + * mask in range [0..0xffffffff] defines up to 32 CANIDs in the row(s) + * + * Each bit of this 64 x 32 bits array defines a CANID value: + * + * bit[i,j] = 1 implies that CANID=(i x 32)+j will be received, while + * bit[i,j] = 0 implies that CANID=(i x 32)+j will be discarded. + */ +static int pcan_usb_fd_set_filter_std(struct peak_usb_device *dev, int idx, + u32 mask) +{ + struct pucan_filter_std *cmd = pcan_usb_fd_cmd_buffer(dev); + int i, n; + + /* select all rows when idx is out of range [0..63] */ + if ((idx < 0) || (idx >= (1 << PUCAN_FLTSTD_ROW_IDX_BITS))) { + n = 1 << PUCAN_FLTSTD_ROW_IDX_BITS; + idx = 0; + + /* select the row (and only the row) otherwise */ + } else { + n = idx + 1; + } + + for (i = idx; i < n; i++, cmd++) { + cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, + PUCAN_CMD_FILTER_STD); + cmd->idx = cpu_to_le16(i); + cmd->mask = cpu_to_le32(mask); + } + + /* send the command */ + return pcan_usb_fd_send_cmd(dev, cmd); +} + +/* set/unset options + * + * onoff set(1)/unset(0) options + * mask each bit defines a kind of options to set/unset + */ +static int pcan_usb_fd_set_options(struct peak_usb_device *dev, + bool onoff, u16 ucan_mask, u16 usb_mask) +{ + struct pcan_ufd_options *cmd = pcan_usb_fd_cmd_buffer(dev); + + cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, + (onoff) ? PUCAN_CMD_SET_EN_OPTION : + PUCAN_CMD_CLR_DIS_OPTION); + + cmd->ucan_mask = cpu_to_le16(ucan_mask); + cmd->usb_mask = cpu_to_le16(usb_mask); + + /* send the command */ + return pcan_usb_fd_send_cmd(dev, ++cmd); +} + +/* setup LED control */ +static int pcan_usb_fd_set_can_led(struct peak_usb_device *dev, u8 led_mode) +{ + struct pcan_ufd_led *cmd = pcan_usb_fd_cmd_buffer(dev); + + cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, + PCAN_UFD_CMD_LED_SET); + cmd->mode = led_mode; + + /* send the command */ + return pcan_usb_fd_send_cmd(dev, ++cmd); +} + +/* set CAN clock domain */ +static int pcan_usb_fd_set_clock_domain(struct peak_usb_device *dev, + u8 clk_mode) +{ + struct pcan_ufd_clock *cmd = pcan_usb_fd_cmd_buffer(dev); + + cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, + PCAN_UFD_CMD_CLK_SET); + cmd->mode = clk_mode; + + /* send the command */ + return pcan_usb_fd_send_cmd(dev, ++cmd); +} + +/* set bittiming for CAN and CAN-FD header */ +static int pcan_usb_fd_set_bittiming_slow(struct peak_usb_device *dev, + struct can_bittiming *bt) +{ + struct pucan_timing_slow *cmd = pcan_usb_fd_cmd_buffer(dev); + + cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, + PUCAN_CMD_TIMING_SLOW); + cmd->sjw_t = PUCAN_TSLOW_SJW_T(bt->sjw - 1, + dev->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES); + + cmd->tseg2 = PUCAN_TSLOW_TSEG2(bt->phase_seg2 - 1); + cmd->tseg1 = PUCAN_TSLOW_TSEG1(bt->prop_seg + bt->phase_seg1 - 1); + cmd->brp = cpu_to_le16(PUCAN_TSLOW_BRP(bt->brp - 1)); + + cmd->ewl = 96; /* default */ + + /* send the command */ + return pcan_usb_fd_send_cmd(dev, ++cmd); +} + +/* set CAN-FD bittiming for data */ +static int pcan_usb_fd_set_bittiming_fast(struct peak_usb_device *dev, + struct can_bittiming *bt) +{ + struct pucan_timing_fast *cmd = pcan_usb_fd_cmd_buffer(dev); + + cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, + PUCAN_CMD_TIMING_FAST); + cmd->sjw = PUCAN_TFAST_SJW(bt->sjw - 1); + cmd->tseg2 = PUCAN_TFAST_TSEG2(bt->phase_seg2 - 1); + cmd->tseg1 = PUCAN_TFAST_TSEG1(bt->prop_seg + bt->phase_seg1 - 1); + cmd->brp = cpu_to_le16(PUCAN_TFAST_BRP(bt->brp - 1)); + + /* send the command */ + return pcan_usb_fd_send_cmd(dev, ++cmd); +} + +/* handle restart but in asynchronously way + * (uses PCAN-USB Pro code to complete asynchronous request) + */ +static int pcan_usb_fd_restart_async(struct peak_usb_device *dev, + struct urb *urb, u8 *buf) +{ + u8 *pc = buf; + + /* build the entire cmds list in the provided buffer, to go back into + * operational mode. + */ + pc += pcan_usb_fd_build_restart_cmd(dev, pc); + + /* add EOC */ + memset(pc, 0xff, sizeof(struct pucan_command)); + pc += sizeof(struct pucan_command); + + /* complete the URB */ + usb_fill_bulk_urb(urb, dev->udev, + usb_sndbulkpipe(dev->udev, PCAN_USBPRO_EP_CMDOUT), + buf, pc - buf, + pcan_usb_pro_restart_complete, dev); + + /* and submit it. */ + return usb_submit_urb(urb, GFP_ATOMIC); +} + +static int pcan_usb_fd_drv_loaded(struct peak_usb_device *dev, bool loaded) +{ + struct pcan_usb_fd_device *pdev = + container_of(dev, struct pcan_usb_fd_device, dev); + + pdev->cmd_buffer_addr[0] = 0; + pdev->cmd_buffer_addr[1] = !!loaded; + + return pcan_usb_pro_send_req(dev, + PCAN_USBPRO_REQ_FCT, + PCAN_USBPRO_FCT_DRVLD, + pdev->cmd_buffer_addr, + PCAN_USBPRO_FCT_DRVLD_REQ_LEN); +} + +static int pcan_usb_fd_decode_canmsg(struct pcan_usb_fd_if *usb_if, + struct pucan_msg *rx_msg) +{ + struct pucan_rx_msg *rm = (struct pucan_rx_msg *)rx_msg; + struct peak_usb_device *dev; + struct net_device *netdev; + struct canfd_frame *cfd; + struct sk_buff *skb; + const u16 rx_msg_flags = le16_to_cpu(rm->flags); + + if (pucan_msg_get_channel(rm) >= ARRAY_SIZE(usb_if->dev)) + return -ENOMEM; + + dev = usb_if->dev[pucan_msg_get_channel(rm)]; + netdev = dev->netdev; + + if (rx_msg_flags & PUCAN_MSG_EXT_DATA_LEN) { + /* CANFD frame case */ + skb = alloc_canfd_skb(netdev, &cfd); + if (!skb) + return -ENOMEM; + + if (rx_msg_flags & PUCAN_MSG_BITRATE_SWITCH) + cfd->flags |= CANFD_BRS; + + if (rx_msg_flags & PUCAN_MSG_ERROR_STATE_IND) + cfd->flags |= CANFD_ESI; + + cfd->len = can_dlc2len(get_canfd_dlc(pucan_msg_get_dlc(rm))); + } else { + /* CAN 2.0 frame case */ + skb = alloc_can_skb(netdev, (struct can_frame **)&cfd); + if (!skb) + return -ENOMEM; + + cfd->len = get_can_dlc(pucan_msg_get_dlc(rm)); + } + + cfd->can_id = le32_to_cpu(rm->can_id); + + if (rx_msg_flags & PUCAN_MSG_EXT_ID) + cfd->can_id |= CAN_EFF_FLAG; + + if (rx_msg_flags & PUCAN_MSG_RTR) + cfd->can_id |= CAN_RTR_FLAG; + else + memcpy(cfd->data, rm->d, cfd->len); + + netdev->stats.rx_packets++; + netdev->stats.rx_bytes += cfd->len; + + peak_usb_netif_rx(skb, &usb_if->time_ref, le32_to_cpu(rm->ts_low)); + + return 0; +} + +/* handle uCAN status message */ +static int pcan_usb_fd_decode_status(struct pcan_usb_fd_if *usb_if, + struct pucan_msg *rx_msg) +{ + struct pucan_status_msg *sm = (struct pucan_status_msg *)rx_msg; + struct pcan_usb_fd_device *pdev; + enum can_state new_state = CAN_STATE_ERROR_ACTIVE; + enum can_state rx_state, tx_state; + struct peak_usb_device *dev; + struct net_device *netdev; + struct can_frame *cf; + struct sk_buff *skb; + + if (pucan_stmsg_get_channel(sm) >= ARRAY_SIZE(usb_if->dev)) + return -ENOMEM; + + dev = usb_if->dev[pucan_stmsg_get_channel(sm)]; + pdev = container_of(dev, struct pcan_usb_fd_device, dev); + netdev = dev->netdev; + + /* nothing should be sent while in BUS_OFF state */ + if (dev->can.state == CAN_STATE_BUS_OFF) + return 0; + + if (sm->channel_p_w_b & PUCAN_BUS_BUSOFF) { + new_state = CAN_STATE_BUS_OFF; + } else if (sm->channel_p_w_b & PUCAN_BUS_PASSIVE) { + new_state = CAN_STATE_ERROR_PASSIVE; + } else if (sm->channel_p_w_b & PUCAN_BUS_WARNING) { + new_state = CAN_STATE_ERROR_WARNING; + } else { + /* back to (or still in) ERROR_ACTIVE state */ + new_state = CAN_STATE_ERROR_ACTIVE; + pdev->bec.txerr = 0; + pdev->bec.rxerr = 0; + } + + /* state hasn't changed */ + if (new_state == dev->can.state) + return 0; + + /* handle bus state change */ + tx_state = (pdev->bec.txerr >= pdev->bec.rxerr) ? new_state : 0; + rx_state = (pdev->bec.txerr <= pdev->bec.rxerr) ? new_state : 0; + + /* allocate an skb to store the error frame */ + skb = alloc_can_err_skb(netdev, &cf); + if (skb) + can_change_state(netdev, cf, tx_state, rx_state); + + /* things must be done even in case of OOM */ + if (new_state == CAN_STATE_BUS_OFF) + can_bus_off(netdev); + + if (!skb) + return -ENOMEM; + + netdev->stats.rx_packets++; + netdev->stats.rx_bytes += cf->can_dlc; + + peak_usb_netif_rx(skb, &usb_if->time_ref, le32_to_cpu(sm->ts_low)); + + return 0; +} + +/* handle uCAN error message */ +static int pcan_usb_fd_decode_error(struct pcan_usb_fd_if *usb_if, + struct pucan_msg *rx_msg) +{ + struct pucan_error_msg *er = (struct pucan_error_msg *)rx_msg; + struct pcan_usb_fd_device *pdev; + struct peak_usb_device *dev; + + if (pucan_ermsg_get_channel(er) >= ARRAY_SIZE(usb_if->dev)) + return -EINVAL; + + dev = usb_if->dev[pucan_ermsg_get_channel(er)]; + pdev = container_of(dev, struct pcan_usb_fd_device, dev); + + /* keep a trace of tx and rx error counters for later use */ + pdev->bec.txerr = er->tx_err_cnt; + pdev->bec.rxerr = er->rx_err_cnt; + + return 0; +} + +/* handle uCAN overrun message */ +static int pcan_usb_fd_decode_overrun(struct pcan_usb_fd_if *usb_if, + struct pucan_msg *rx_msg) +{ + struct pcan_ufd_ovr_msg *ov = (struct pcan_ufd_ovr_msg *)rx_msg; + struct peak_usb_device *dev; + struct net_device *netdev; + struct can_frame *cf; + struct sk_buff *skb; + + if (pufd_omsg_get_channel(ov) >= ARRAY_SIZE(usb_if->dev)) + return -EINVAL; + + dev = usb_if->dev[pufd_omsg_get_channel(ov)]; + netdev = dev->netdev; + + /* allocate an skb to store the error frame */ + skb = alloc_can_err_skb(netdev, &cf); + if (!skb) + return -ENOMEM; + + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW; + + peak_usb_netif_rx(skb, &usb_if->time_ref, le32_to_cpu(ov->ts_low)); + + netdev->stats.rx_over_errors++; + netdev->stats.rx_errors++; + + return 0; +} + +/* handle USB calibration message */ +static void pcan_usb_fd_decode_ts(struct pcan_usb_fd_if *usb_if, + struct pucan_msg *rx_msg) +{ + struct pcan_ufd_ts_msg *ts = (struct pcan_ufd_ts_msg *)rx_msg; + + /* should wait until clock is stabilized */ + if (usb_if->cm_ignore_count > 0) + usb_if->cm_ignore_count--; + else + peak_usb_set_ts_now(&usb_if->time_ref, le32_to_cpu(ts->ts_low)); +} + +/* callback for bulk IN urb */ +static int pcan_usb_fd_decode_buf(struct peak_usb_device *dev, struct urb *urb) +{ + struct pcan_usb_fd_if *usb_if = pcan_usb_fd_dev_if(dev); + struct net_device *netdev = dev->netdev; + struct pucan_msg *rx_msg; + u8 *msg_ptr, *msg_end; + int err = 0; + + /* loop reading all the records from the incoming message */ + msg_ptr = urb->transfer_buffer; + msg_end = urb->transfer_buffer + urb->actual_length; + for (; msg_ptr < msg_end;) { + u16 rx_msg_type, rx_msg_size; + + rx_msg = (struct pucan_msg *)msg_ptr; + if (!rx_msg->size) { + /* null packet found: end of list */ + break; + } + + rx_msg_size = le16_to_cpu(rx_msg->size); + rx_msg_type = le16_to_cpu(rx_msg->type); + + /* check if the record goes out of current packet */ + if (msg_ptr + rx_msg_size > msg_end) { + netdev_err(netdev, + "got frag rec: should inc usb rx buf sze\n"); + err = -EBADMSG; + break; + } + + switch (rx_msg_type) { + case PUCAN_MSG_CAN_RX: + err = pcan_usb_fd_decode_canmsg(usb_if, rx_msg); + if (err < 0) + goto fail; + break; + + case PCAN_UFD_MSG_CALIBRATION: + pcan_usb_fd_decode_ts(usb_if, rx_msg); + break; + + case PUCAN_MSG_ERROR: + err = pcan_usb_fd_decode_error(usb_if, rx_msg); + if (err < 0) + goto fail; + break; + + case PUCAN_MSG_STATUS: + err = pcan_usb_fd_decode_status(usb_if, rx_msg); + if (err < 0) + goto fail; + break; + + case PCAN_UFD_MSG_OVERRUN: + err = pcan_usb_fd_decode_overrun(usb_if, rx_msg); + if (err < 0) + goto fail; + break; + + default: + netdev_err(netdev, + "unhandled msg type 0x%02x (%d): ignored\n", + rx_msg_type, rx_msg_type); + break; + } + + msg_ptr += rx_msg_size; + } + +fail: + if (err) + pcan_dump_mem("received msg", + urb->transfer_buffer, urb->actual_length); + return err; +} + +/* CAN/CANFD frames encoding callback */ +static int pcan_usb_fd_encode_msg(struct peak_usb_device *dev, + struct sk_buff *skb, u8 *obuf, size_t *size) +{ + struct pucan_tx_msg *tx_msg = (struct pucan_tx_msg *)obuf; + struct canfd_frame *cfd = (struct canfd_frame *)skb->data; + u16 tx_msg_size, tx_msg_flags; + u8 can_dlc; + + if (cfd->len > CANFD_MAX_DLEN) + return -EINVAL; + + tx_msg_size = ALIGN(sizeof(struct pucan_tx_msg) + cfd->len, 4); + tx_msg->size = cpu_to_le16(tx_msg_size); + tx_msg->type = cpu_to_le16(PUCAN_MSG_CAN_TX); + + tx_msg_flags = 0; + if (cfd->can_id & CAN_EFF_FLAG) { + tx_msg_flags |= PUCAN_MSG_EXT_ID; + tx_msg->can_id = cpu_to_le32(cfd->can_id & CAN_EFF_MASK); + } else { + tx_msg->can_id = cpu_to_le32(cfd->can_id & CAN_SFF_MASK); + } + + if (can_is_canfd_skb(skb)) { + /* considering a CANFD frame */ + can_dlc = can_len2dlc(cfd->len); + + tx_msg_flags |= PUCAN_MSG_EXT_DATA_LEN; + + if (cfd->flags & CANFD_BRS) + tx_msg_flags |= PUCAN_MSG_BITRATE_SWITCH; + + if (cfd->flags & CANFD_ESI) + tx_msg_flags |= PUCAN_MSG_ERROR_STATE_IND; + } else { + /* CAND 2.0 frames */ + can_dlc = cfd->len; + + if (cfd->can_id & CAN_RTR_FLAG) + tx_msg_flags |= PUCAN_MSG_RTR; + } + + tx_msg->flags = cpu_to_le16(tx_msg_flags); + tx_msg->channel_dlc = PUCAN_MSG_CHANNEL_DLC(dev->ctrl_idx, can_dlc); + memcpy(tx_msg->d, cfd->data, cfd->len); + + /* add null size message to tag the end (messages are 32-bits aligned) + */ + tx_msg = (struct pucan_tx_msg *)(obuf + tx_msg_size); + + tx_msg->size = 0; + + /* set the whole size of the USB packet to send */ + *size = tx_msg_size + sizeof(u32); + + return 0; +} + +/* start the interface (last chance before set bus on) */ +static int pcan_usb_fd_start(struct peak_usb_device *dev) +{ + struct pcan_usb_fd_device *pdev = + container_of(dev, struct pcan_usb_fd_device, dev); + int err; + + /* set filter mode: all acceptance */ + err = pcan_usb_fd_set_filter_std(dev, -1, 0xffffffff); + if (err) + return err; + + /* opening first device: */ + if (pdev->usb_if->dev_opened_count == 0) { + /* reset time_ref */ + peak_usb_init_time_ref(&pdev->usb_if->time_ref, + &pcan_usb_pro_fd); + + /* enable USB calibration messages */ + err = pcan_usb_fd_set_options(dev, 1, + PUCAN_OPTION_ERROR, + PCAN_UFD_FLTEXT_CALIBRATION); + } + + pdev->usb_if->dev_opened_count++; + + /* reset cached error counters */ + pdev->bec.txerr = 0; + pdev->bec.rxerr = 0; + + return err; +} + +/* socket callback used to copy berr counters values received through USB */ +static int pcan_usb_fd_get_berr_counter(const struct net_device *netdev, + struct can_berr_counter *bec) +{ + struct peak_usb_device *dev = netdev_priv(netdev); + struct pcan_usb_fd_device *pdev = + container_of(dev, struct pcan_usb_fd_device, dev); + + *bec = pdev->bec; + + /* must return 0 */ + return 0; +} + +/* stop interface (last chance before set bus off) */ +static int pcan_usb_fd_stop(struct peak_usb_device *dev) +{ + struct pcan_usb_fd_device *pdev = + container_of(dev, struct pcan_usb_fd_device, dev); + + /* turn off special msgs for that interface if no other dev opened */ + if (pdev->usb_if->dev_opened_count == 1) + pcan_usb_fd_set_options(dev, 0, + PUCAN_OPTION_ERROR, + PCAN_UFD_FLTEXT_CALIBRATION); + pdev->usb_if->dev_opened_count--; + + return 0; +} + +/* called when probing, to initialize a device object */ +static int pcan_usb_fd_init(struct peak_usb_device *dev) +{ + struct pcan_usb_fd_device *pdev = + container_of(dev, struct pcan_usb_fd_device, dev); + int i, err = -ENOMEM; + + /* do this for 1st channel only */ + if (!dev->prev_siblings) { + /* allocate netdevices common structure attached to first one */ + pdev->usb_if = kzalloc(sizeof(*pdev->usb_if), GFP_KERNEL); + if (!pdev->usb_if) + goto err_out; + + /* allocate command buffer once for all for the interface */ + pdev->cmd_buffer_addr = kzalloc(PCAN_UFD_CMD_BUFFER_SIZE, + GFP_KERNEL); + if (!pdev->cmd_buffer_addr) + goto err_out_1; + + /* number of ts msgs to ignore before taking one into account */ + pdev->usb_if->cm_ignore_count = 5; + + err = pcan_usb_pro_send_req(dev, PCAN_USBPRO_REQ_INFO, + PCAN_USBPRO_INFO_FW, + &pdev->usb_if->fw_info, + sizeof(pdev->usb_if->fw_info)); + if (err) { + dev_err(dev->netdev->dev.parent, + "unable to read %s firmware info (err %d)\n", + dev->adapter->name, err); + goto err_out_2; + } + + /* explicit use of dev_xxx() instead of netdev_xxx() here: + * information displayed are related to the device itself, not + * to the canx (channel) device. + */ + dev_info(dev->netdev->dev.parent, + "PEAK-System %s v%u fw v%u.%u.%u (%u channels)\n", + dev->adapter->name, pdev->usb_if->fw_info.hw_version, + pdev->usb_if->fw_info.fw_version[0], + pdev->usb_if->fw_info.fw_version[1], + pdev->usb_if->fw_info.fw_version[2], + dev->adapter->ctrl_count); + + /* check for ability to switch between ISO/non-ISO modes */ + if (pdev->usb_if->fw_info.fw_version[0] >= 2) { + /* firmware >= 2.x supports ISO/non-ISO switching */ + dev->can.ctrlmode_supported |= CAN_CTRLMODE_FD_NON_ISO; + } else { + /* firmware < 2.x only supports fixed(!) non-ISO */ + dev->can.ctrlmode |= CAN_CTRLMODE_FD_NON_ISO; + } + + /* tell the hardware the can driver is running */ + err = pcan_usb_fd_drv_loaded(dev, 1); + if (err) { + dev_err(dev->netdev->dev.parent, + "unable to tell %s driver is loaded (err %d)\n", + dev->adapter->name, err); + goto err_out_2; + } + } else { + /* otherwise, simply copy previous sibling's values */ + struct pcan_usb_fd_device *ppdev = + container_of(dev->prev_siblings, + struct pcan_usb_fd_device, dev); + + pdev->usb_if = ppdev->usb_if; + pdev->cmd_buffer_addr = ppdev->cmd_buffer_addr; + + /* do a copy of the ctrlmode[_supported] too */ + dev->can.ctrlmode = ppdev->dev.can.ctrlmode; + dev->can.ctrlmode_supported = ppdev->dev.can.ctrlmode_supported; + } + + pdev->usb_if->dev[dev->ctrl_idx] = dev; + dev->device_number = + le32_to_cpu(pdev->usb_if->fw_info.dev_id[dev->ctrl_idx]); + + /* set clock domain */ + for (i = 0; i < ARRAY_SIZE(pcan_usb_fd_clk_freq); i++) + if (dev->adapter->clock.freq == pcan_usb_fd_clk_freq[i]) + break; + + if (i >= ARRAY_SIZE(pcan_usb_fd_clk_freq)) { + dev_warn(dev->netdev->dev.parent, + "incompatible clock frequencies\n"); + err = -EINVAL; + goto err_out_2; + } + + pcan_usb_fd_set_clock_domain(dev, i); + + /* set LED in default state (end of init phase) */ + pcan_usb_fd_set_can_led(dev, PCAN_UFD_LED_DEF); + + return 0; + +err_out_2: + kfree(pdev->cmd_buffer_addr); +err_out_1: + kfree(pdev->usb_if); +err_out: + return err; +} + +/* called when driver module is being unloaded */ +static void pcan_usb_fd_exit(struct peak_usb_device *dev) +{ + struct pcan_usb_fd_device *pdev = + container_of(dev, struct pcan_usb_fd_device, dev); + + /* when rmmod called before unplug and if down, should reset things + * before leaving + */ + if (dev->can.state != CAN_STATE_STOPPED) { + /* set bus off on the corresponding channel */ + pcan_usb_fd_set_bus(dev, 0); + } + + /* switch off corresponding CAN LEDs */ + pcan_usb_fd_set_can_led(dev, PCAN_UFD_LED_OFF); + + /* if channel #0 (only) */ + if (dev->ctrl_idx == 0) { + /* turn off calibration message if any device were opened */ + if (pdev->usb_if->dev_opened_count > 0) + pcan_usb_fd_set_options(dev, 0, + PUCAN_OPTION_ERROR, + PCAN_UFD_FLTEXT_CALIBRATION); + + /* tell USB adapter that the driver is being unloaded */ + pcan_usb_fd_drv_loaded(dev, 0); + } +} + +/* called when the USB adapter is unplugged */ +static void pcan_usb_fd_free(struct peak_usb_device *dev) +{ + /* last device: can free shared objects now */ + if (!dev->prev_siblings && !dev->next_siblings) { + struct pcan_usb_fd_device *pdev = + container_of(dev, struct pcan_usb_fd_device, dev); + + /* free commands buffer */ + kfree(pdev->cmd_buffer_addr); + + /* free usb interface object */ + kfree(pdev->usb_if); + } +} + +/* describes the PCAN-USB FD adapter */ +static const struct can_bittiming_const pcan_usb_fd_const = { + .name = "pcan_usb_fd", + .tseg1_min = 1, + .tseg1_max = (1 << PUCAN_TSLOW_TSGEG1_BITS), + .tseg2_min = 1, + .tseg2_max = (1 << PUCAN_TSLOW_TSGEG2_BITS), + .sjw_max = (1 << PUCAN_TSLOW_SJW_BITS), + .brp_min = 1, + .brp_max = (1 << PUCAN_TSLOW_BRP_BITS), + .brp_inc = 1, +}; + +static const struct can_bittiming_const pcan_usb_fd_data_const = { + .name = "pcan_usb_fd", + .tseg1_min = 1, + .tseg1_max = (1 << PUCAN_TFAST_TSGEG1_BITS), + .tseg2_min = 1, + .tseg2_max = (1 << PUCAN_TFAST_TSGEG2_BITS), + .sjw_max = (1 << PUCAN_TFAST_SJW_BITS), + .brp_min = 1, + .brp_max = (1 << PUCAN_TFAST_BRP_BITS), + .brp_inc = 1, +}; + +const struct peak_usb_adapter pcan_usb_fd = { + .name = "PCAN-USB FD", + .device_id = PCAN_USBFD_PRODUCT_ID, + .ctrl_count = PCAN_USBFD_CHANNEL_COUNT, + .ctrlmode_supported = CAN_CTRLMODE_FD | + CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY, + .clock = { + .freq = PCAN_UFD_CRYSTAL_HZ, + }, + .bittiming_const = &pcan_usb_fd_const, + .data_bittiming_const = &pcan_usb_fd_data_const, + + /* size of device private data */ + .sizeof_dev_private = sizeof(struct pcan_usb_fd_device), + + /* timestamps usage */ + .ts_used_bits = 32, + .ts_period = 1000000, /* calibration period in ts. */ + .us_per_ts_scale = 1, /* us = (ts * scale) >> shift */ + .us_per_ts_shift = 0, + + /* give here messages in/out endpoints */ + .ep_msg_in = PCAN_USBPRO_EP_MSGIN, + .ep_msg_out = {PCAN_USBPRO_EP_MSGOUT_0}, + + /* size of rx/tx usb buffers */ + .rx_buffer_size = PCAN_UFD_RX_BUFFER_SIZE, + .tx_buffer_size = PCAN_UFD_TX_BUFFER_SIZE, + + /* device callbacks */ + .intf_probe = pcan_usb_pro_probe, /* same as PCAN-USB Pro */ + .dev_init = pcan_usb_fd_init, + + .dev_exit = pcan_usb_fd_exit, + .dev_free = pcan_usb_fd_free, + .dev_set_bus = pcan_usb_fd_set_bus, + .dev_set_bittiming = pcan_usb_fd_set_bittiming_slow, + .dev_set_data_bittiming = pcan_usb_fd_set_bittiming_fast, + .dev_decode_buf = pcan_usb_fd_decode_buf, + .dev_start = pcan_usb_fd_start, + .dev_stop = pcan_usb_fd_stop, + .dev_restart_async = pcan_usb_fd_restart_async, + .dev_encode_msg = pcan_usb_fd_encode_msg, + + .do_get_berr_counter = pcan_usb_fd_get_berr_counter, +}; + +/* describes the PCAN-CHIP USB */ +static const struct can_bittiming_const pcan_usb_chip_const = { + .name = "pcan_chip_usb", + .tseg1_min = 1, + .tseg1_max = (1 << PUCAN_TSLOW_TSGEG1_BITS), + .tseg2_min = 1, + .tseg2_max = (1 << PUCAN_TSLOW_TSGEG2_BITS), + .sjw_max = (1 << PUCAN_TSLOW_SJW_BITS), + .brp_min = 1, + .brp_max = (1 << PUCAN_TSLOW_BRP_BITS), + .brp_inc = 1, +}; + +static const struct can_bittiming_const pcan_usb_chip_data_const = { + .name = "pcan_chip_usb", + .tseg1_min = 1, + .tseg1_max = (1 << PUCAN_TFAST_TSGEG1_BITS), + .tseg2_min = 1, + .tseg2_max = (1 << PUCAN_TFAST_TSGEG2_BITS), + .sjw_max = (1 << PUCAN_TFAST_SJW_BITS), + .brp_min = 1, + .brp_max = (1 << PUCAN_TFAST_BRP_BITS), + .brp_inc = 1, +}; + +const struct peak_usb_adapter pcan_usb_chip = { + .name = "PCAN-Chip USB", + .device_id = PCAN_USBCHIP_PRODUCT_ID, + .ctrl_count = PCAN_USBFD_CHANNEL_COUNT, + .ctrlmode_supported = CAN_CTRLMODE_FD | + CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY, + .clock = { + .freq = PCAN_UFD_CRYSTAL_HZ, + }, + .bittiming_const = &pcan_usb_chip_const, + .data_bittiming_const = &pcan_usb_chip_data_const, + + /* size of device private data */ + .sizeof_dev_private = sizeof(struct pcan_usb_fd_device), + + /* timestamps usage */ + .ts_used_bits = 32, + .ts_period = 1000000, /* calibration period in ts. */ + .us_per_ts_scale = 1, /* us = (ts * scale) >> shift */ + .us_per_ts_shift = 0, + + /* give here messages in/out endpoints */ + .ep_msg_in = PCAN_USBPRO_EP_MSGIN, + .ep_msg_out = {PCAN_USBPRO_EP_MSGOUT_0}, + + /* size of rx/tx usb buffers */ + .rx_buffer_size = PCAN_UFD_RX_BUFFER_SIZE, + .tx_buffer_size = PCAN_UFD_TX_BUFFER_SIZE, + + /* device callbacks */ + .intf_probe = pcan_usb_pro_probe, /* same as PCAN-USB Pro */ + .dev_init = pcan_usb_fd_init, + + .dev_exit = pcan_usb_fd_exit, + .dev_free = pcan_usb_fd_free, + .dev_set_bus = pcan_usb_fd_set_bus, + .dev_set_bittiming = pcan_usb_fd_set_bittiming_slow, + .dev_set_data_bittiming = pcan_usb_fd_set_bittiming_fast, + .dev_decode_buf = pcan_usb_fd_decode_buf, + .dev_start = pcan_usb_fd_start, + .dev_stop = pcan_usb_fd_stop, + .dev_restart_async = pcan_usb_fd_restart_async, + .dev_encode_msg = pcan_usb_fd_encode_msg, + + .do_get_berr_counter = pcan_usb_fd_get_berr_counter, +}; + +/* describes the PCAN-USB Pro FD adapter */ +static const struct can_bittiming_const pcan_usb_pro_fd_const = { + .name = "pcan_usb_pro_fd", + .tseg1_min = 1, + .tseg1_max = (1 << PUCAN_TSLOW_TSGEG1_BITS), + .tseg2_min = 1, + .tseg2_max = (1 << PUCAN_TSLOW_TSGEG2_BITS), + .sjw_max = (1 << PUCAN_TSLOW_SJW_BITS), + .brp_min = 1, + .brp_max = (1 << PUCAN_TSLOW_BRP_BITS), + .brp_inc = 1, +}; + +static const struct can_bittiming_const pcan_usb_pro_fd_data_const = { + .name = "pcan_usb_pro_fd", + .tseg1_min = 1, + .tseg1_max = (1 << PUCAN_TFAST_TSGEG1_BITS), + .tseg2_min = 1, + .tseg2_max = (1 << PUCAN_TFAST_TSGEG2_BITS), + .sjw_max = (1 << PUCAN_TFAST_SJW_BITS), + .brp_min = 1, + .brp_max = (1 << PUCAN_TFAST_BRP_BITS), + .brp_inc = 1, +}; + +const struct peak_usb_adapter pcan_usb_pro_fd = { + .name = "PCAN-USB Pro FD", + .device_id = PCAN_USBPROFD_PRODUCT_ID, + .ctrl_count = PCAN_USBPROFD_CHANNEL_COUNT, + .ctrlmode_supported = CAN_CTRLMODE_FD | + CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY, + .clock = { + .freq = PCAN_UFD_CRYSTAL_HZ, + }, + .bittiming_const = &pcan_usb_pro_fd_const, + .data_bittiming_const = &pcan_usb_pro_fd_data_const, + + /* size of device private data */ + .sizeof_dev_private = sizeof(struct pcan_usb_fd_device), + + /* timestamps usage */ + .ts_used_bits = 32, + .ts_period = 1000000, /* calibration period in ts. */ + .us_per_ts_scale = 1, /* us = (ts * scale) >> shift */ + .us_per_ts_shift = 0, + + /* give here messages in/out endpoints */ + .ep_msg_in = PCAN_USBPRO_EP_MSGIN, + .ep_msg_out = {PCAN_USBPRO_EP_MSGOUT_0, PCAN_USBPRO_EP_MSGOUT_1}, + + /* size of rx/tx usb buffers */ + .rx_buffer_size = PCAN_UFD_RX_BUFFER_SIZE, + .tx_buffer_size = PCAN_UFD_TX_BUFFER_SIZE, + + /* device callbacks */ + .intf_probe = pcan_usb_pro_probe, /* same as PCAN-USB Pro */ + .dev_init = pcan_usb_fd_init, + + .dev_exit = pcan_usb_fd_exit, + .dev_free = pcan_usb_fd_free, + .dev_set_bus = pcan_usb_fd_set_bus, + .dev_set_bittiming = pcan_usb_fd_set_bittiming_slow, + .dev_set_data_bittiming = pcan_usb_fd_set_bittiming_fast, + .dev_decode_buf = pcan_usb_fd_decode_buf, + .dev_start = pcan_usb_fd_start, + .dev_stop = pcan_usb_fd_stop, + .dev_restart_async = pcan_usb_fd_restart_async, + .dev_encode_msg = pcan_usb_fd_encode_msg, + + .do_get_berr_counter = pcan_usb_fd_get_berr_counter, +}; + +/* describes the PCAN-USB X6 adapter */ +static const struct can_bittiming_const pcan_usb_x6_const = { + .name = "pcan_usb_x6", + .tseg1_min = 1, + .tseg1_max = (1 << PUCAN_TSLOW_TSGEG1_BITS), + .tseg2_min = 1, + .tseg2_max = (1 << PUCAN_TSLOW_TSGEG2_BITS), + .sjw_max = (1 << PUCAN_TSLOW_SJW_BITS), + .brp_min = 1, + .brp_max = (1 << PUCAN_TSLOW_BRP_BITS), + .brp_inc = 1, +}; + +static const struct can_bittiming_const pcan_usb_x6_data_const = { + .name = "pcan_usb_x6", + .tseg1_min = 1, + .tseg1_max = (1 << PUCAN_TFAST_TSGEG1_BITS), + .tseg2_min = 1, + .tseg2_max = (1 << PUCAN_TFAST_TSGEG2_BITS), + .sjw_max = (1 << PUCAN_TFAST_SJW_BITS), + .brp_min = 1, + .brp_max = (1 << PUCAN_TFAST_BRP_BITS), + .brp_inc = 1, +}; + +const struct peak_usb_adapter pcan_usb_x6 = { + .name = "PCAN-USB X6", + .device_id = PCAN_USBX6_PRODUCT_ID, + .ctrl_count = PCAN_USBPROFD_CHANNEL_COUNT, + .ctrlmode_supported = CAN_CTRLMODE_FD | + CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY, + .clock = { + .freq = PCAN_UFD_CRYSTAL_HZ, + }, + .bittiming_const = &pcan_usb_x6_const, + .data_bittiming_const = &pcan_usb_x6_data_const, + + /* size of device private data */ + .sizeof_dev_private = sizeof(struct pcan_usb_fd_device), + + /* timestamps usage */ + .ts_used_bits = 32, + .ts_period = 1000000, /* calibration period in ts. */ + .us_per_ts_scale = 1, /* us = (ts * scale) >> shift */ + .us_per_ts_shift = 0, + + /* give here messages in/out endpoints */ + .ep_msg_in = PCAN_USBPRO_EP_MSGIN, + .ep_msg_out = {PCAN_USBPRO_EP_MSGOUT_0, PCAN_USBPRO_EP_MSGOUT_1}, + + /* size of rx/tx usb buffers */ + .rx_buffer_size = PCAN_UFD_RX_BUFFER_SIZE, + .tx_buffer_size = PCAN_UFD_TX_BUFFER_SIZE, + + /* device callbacks */ + .intf_probe = pcan_usb_pro_probe, /* same as PCAN-USB Pro */ + .dev_init = pcan_usb_fd_init, + + .dev_exit = pcan_usb_fd_exit, + .dev_free = pcan_usb_fd_free, + .dev_set_bus = pcan_usb_fd_set_bus, + .dev_set_bittiming = pcan_usb_fd_set_bittiming_slow, + .dev_set_data_bittiming = pcan_usb_fd_set_bittiming_fast, + .dev_decode_buf = pcan_usb_fd_decode_buf, + .dev_start = pcan_usb_fd_start, + .dev_stop = pcan_usb_fd_stop, + .dev_restart_async = pcan_usb_fd_restart_async, + .dev_encode_msg = pcan_usb_fd_encode_msg, + + .do_get_berr_counter = pcan_usb_fd_get_berr_counter, +}; diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c new file mode 100644 index 000000000..c7564773f --- /dev/null +++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c @@ -0,0 +1,1050 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * CAN driver for PEAK System PCAN-USB Pro adapter + * Derived from the PCAN project file driver/src/pcan_usbpro.c + * + * Copyright (C) 2003-2011 PEAK System-Technik GmbH + * Copyright (C) 2011-2012 Stephane Grosjean <s.grosjean@peak-system.com> + */ +#include <linux/netdevice.h> +#include <linux/usb.h> +#include <linux/module.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> + +#include "pcan_usb_core.h" +#include "pcan_usb_pro.h" + +MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB Pro adapter"); + +#define PCAN_USBPRO_CHANNEL_COUNT 2 + +/* PCAN-USB Pro adapter internal clock (MHz) */ +#define PCAN_USBPRO_CRYSTAL_HZ 56000000 + +/* PCAN-USB Pro command timeout (ms.) */ +#define PCAN_USBPRO_COMMAND_TIMEOUT 1000 + +/* PCAN-USB Pro rx/tx buffers size */ +#define PCAN_USBPRO_RX_BUFFER_SIZE 1024 +#define PCAN_USBPRO_TX_BUFFER_SIZE 64 + +#define PCAN_USBPRO_MSG_HEADER_LEN 4 + +/* some commands responses need to be re-submitted */ +#define PCAN_USBPRO_RSP_SUBMIT_MAX 2 + +#define PCAN_USBPRO_RTR 0x01 +#define PCAN_USBPRO_EXT 0x02 + +#define PCAN_USBPRO_CMD_BUFFER_SIZE 512 + +/* handle device specific info used by the netdevices */ +struct pcan_usb_pro_interface { + struct peak_usb_device *dev[PCAN_USBPRO_CHANNEL_COUNT]; + struct peak_time_ref time_ref; + int cm_ignore_count; + int dev_opened_count; +}; + +/* device information */ +struct pcan_usb_pro_device { + struct peak_usb_device dev; + struct pcan_usb_pro_interface *usb_if; + u32 cached_ccbt; +}; + +/* internal structure used to handle messages sent to bulk urb */ +struct pcan_usb_pro_msg { + u8 *rec_ptr; + int rec_buffer_size; + int rec_buffer_len; + union { + __le16 *rec_cnt_rd; + __le32 *rec_cnt; + u8 *rec_buffer; + } u; +}; + +/* records sizes table indexed on message id. (8-bits value) */ +static u16 pcan_usb_pro_sizeof_rec[256] = { + [PCAN_USBPRO_SETBTR] = sizeof(struct pcan_usb_pro_btr), + [PCAN_USBPRO_SETBUSACT] = sizeof(struct pcan_usb_pro_busact), + [PCAN_USBPRO_SETSILENT] = sizeof(struct pcan_usb_pro_silent), + [PCAN_USBPRO_SETFILTR] = sizeof(struct pcan_usb_pro_filter), + [PCAN_USBPRO_SETTS] = sizeof(struct pcan_usb_pro_setts), + [PCAN_USBPRO_GETDEVID] = sizeof(struct pcan_usb_pro_devid), + [PCAN_USBPRO_SETLED] = sizeof(struct pcan_usb_pro_setled), + [PCAN_USBPRO_RXMSG8] = sizeof(struct pcan_usb_pro_rxmsg), + [PCAN_USBPRO_RXMSG4] = sizeof(struct pcan_usb_pro_rxmsg) - 4, + [PCAN_USBPRO_RXMSG0] = sizeof(struct pcan_usb_pro_rxmsg) - 8, + [PCAN_USBPRO_RXRTR] = sizeof(struct pcan_usb_pro_rxmsg) - 8, + [PCAN_USBPRO_RXSTATUS] = sizeof(struct pcan_usb_pro_rxstatus), + [PCAN_USBPRO_RXTS] = sizeof(struct pcan_usb_pro_rxts), + [PCAN_USBPRO_TXMSG8] = sizeof(struct pcan_usb_pro_txmsg), + [PCAN_USBPRO_TXMSG4] = sizeof(struct pcan_usb_pro_txmsg) - 4, + [PCAN_USBPRO_TXMSG0] = sizeof(struct pcan_usb_pro_txmsg) - 8, +}; + +/* + * initialize PCAN-USB Pro message data structure + */ +static u8 *pcan_msg_init(struct pcan_usb_pro_msg *pm, void *buffer_addr, + int buffer_size) +{ + if (buffer_size < PCAN_USBPRO_MSG_HEADER_LEN) + return NULL; + + pm->u.rec_buffer = (u8 *)buffer_addr; + pm->rec_buffer_size = pm->rec_buffer_len = buffer_size; + pm->rec_ptr = pm->u.rec_buffer + PCAN_USBPRO_MSG_HEADER_LEN; + + return pm->rec_ptr; +} + +static u8 *pcan_msg_init_empty(struct pcan_usb_pro_msg *pm, + void *buffer_addr, int buffer_size) +{ + u8 *pr = pcan_msg_init(pm, buffer_addr, buffer_size); + + if (pr) { + pm->rec_buffer_len = PCAN_USBPRO_MSG_HEADER_LEN; + *pm->u.rec_cnt = 0; + } + return pr; +} + +/* + * add one record to a message being built + */ +static int pcan_msg_add_rec(struct pcan_usb_pro_msg *pm, int id, ...) +{ + int len, i; + u8 *pc; + va_list ap; + + va_start(ap, id); + + pc = pm->rec_ptr + 1; + + i = 0; + switch (id) { + case PCAN_USBPRO_TXMSG8: + i += 4; + fallthrough; + case PCAN_USBPRO_TXMSG4: + i += 4; + fallthrough; + case PCAN_USBPRO_TXMSG0: + *pc++ = va_arg(ap, int); + *pc++ = va_arg(ap, int); + *pc++ = va_arg(ap, int); + *(__le32 *)pc = cpu_to_le32(va_arg(ap, u32)); + pc += 4; + memcpy(pc, va_arg(ap, int *), i); + pc += i; + break; + + case PCAN_USBPRO_SETBTR: + case PCAN_USBPRO_GETDEVID: + *pc++ = va_arg(ap, int); + pc += 2; + *(__le32 *)pc = cpu_to_le32(va_arg(ap, u32)); + pc += 4; + break; + + case PCAN_USBPRO_SETFILTR: + case PCAN_USBPRO_SETBUSACT: + case PCAN_USBPRO_SETSILENT: + *pc++ = va_arg(ap, int); + *(__le16 *)pc = cpu_to_le16(va_arg(ap, int)); + pc += 2; + break; + + case PCAN_USBPRO_SETLED: + *pc++ = va_arg(ap, int); + *(__le16 *)pc = cpu_to_le16(va_arg(ap, int)); + pc += 2; + *(__le32 *)pc = cpu_to_le32(va_arg(ap, u32)); + pc += 4; + break; + + case PCAN_USBPRO_SETTS: + pc++; + *(__le16 *)pc = cpu_to_le16(va_arg(ap, int)); + pc += 2; + break; + + default: + pr_err("%s: %s(): unknown data type %02Xh (%d)\n", + PCAN_USB_DRIVER_NAME, __func__, id, id); + pc--; + break; + } + + len = pc - pm->rec_ptr; + if (len > 0) { + le32_add_cpu(pm->u.rec_cnt, 1); + *pm->rec_ptr = id; + + pm->rec_ptr = pc; + pm->rec_buffer_len += len; + } + + va_end(ap); + + return len; +} + +/* + * send PCAN-USB Pro command synchronously + */ +static int pcan_usb_pro_send_cmd(struct peak_usb_device *dev, + struct pcan_usb_pro_msg *pum) +{ + int actual_length; + int err; + + /* usb device unregistered? */ + if (!(dev->state & PCAN_USB_STATE_CONNECTED)) + return 0; + + err = usb_bulk_msg(dev->udev, + usb_sndbulkpipe(dev->udev, PCAN_USBPRO_EP_CMDOUT), + pum->u.rec_buffer, pum->rec_buffer_len, + &actual_length, PCAN_USBPRO_COMMAND_TIMEOUT); + if (err) + netdev_err(dev->netdev, "sending command failure: %d\n", err); + + return err; +} + +/* + * wait for PCAN-USB Pro command response + */ +static int pcan_usb_pro_wait_rsp(struct peak_usb_device *dev, + struct pcan_usb_pro_msg *pum) +{ + u8 req_data_type, req_channel; + int actual_length; + int i, err = 0; + + /* usb device unregistered? */ + if (!(dev->state & PCAN_USB_STATE_CONNECTED)) + return 0; + + req_data_type = pum->u.rec_buffer[4]; + req_channel = pum->u.rec_buffer[5]; + + *pum->u.rec_cnt = 0; + for (i = 0; !err && i < PCAN_USBPRO_RSP_SUBMIT_MAX; i++) { + struct pcan_usb_pro_msg rsp; + union pcan_usb_pro_rec *pr; + u32 r, rec_cnt; + u16 rec_len; + u8 *pc; + + err = usb_bulk_msg(dev->udev, + usb_rcvbulkpipe(dev->udev, PCAN_USBPRO_EP_CMDIN), + pum->u.rec_buffer, pum->rec_buffer_len, + &actual_length, PCAN_USBPRO_COMMAND_TIMEOUT); + if (err) { + netdev_err(dev->netdev, "waiting rsp error %d\n", err); + break; + } + + if (actual_length == 0) + continue; + + err = -EBADMSG; + if (actual_length < PCAN_USBPRO_MSG_HEADER_LEN) { + netdev_err(dev->netdev, + "got abnormal too small rsp (len=%d)\n", + actual_length); + break; + } + + pc = pcan_msg_init(&rsp, pum->u.rec_buffer, + actual_length); + + rec_cnt = le32_to_cpu(*rsp.u.rec_cnt); + + /* loop on records stored into message */ + for (r = 0; r < rec_cnt; r++) { + pr = (union pcan_usb_pro_rec *)pc; + rec_len = pcan_usb_pro_sizeof_rec[pr->data_type]; + if (!rec_len) { + netdev_err(dev->netdev, + "got unprocessed record in msg\n"); + pcan_dump_mem("rcvd rsp msg", pum->u.rec_buffer, + actual_length); + break; + } + + /* check if response corresponds to request */ + if (pr->data_type != req_data_type) + netdev_err(dev->netdev, + "got unwanted rsp %xh: ignored\n", + pr->data_type); + + /* check if channel in response corresponds too */ + else if ((req_channel != 0xff) && \ + (pr->bus_act.channel != req_channel)) + netdev_err(dev->netdev, + "got rsp %xh but on chan%u: ignored\n", + req_data_type, pr->bus_act.channel); + + /* got the response */ + else + return 0; + + /* otherwise, go on with next record in message */ + pc += rec_len; + } + } + + return (i >= PCAN_USBPRO_RSP_SUBMIT_MAX) ? -ERANGE : err; +} + +int pcan_usb_pro_send_req(struct peak_usb_device *dev, int req_id, + int req_value, void *req_addr, int req_size) +{ + int err; + u8 req_type; + unsigned int p; + + /* usb device unregistered? */ + if (!(dev->state & PCAN_USB_STATE_CONNECTED)) + return 0; + + req_type = USB_TYPE_VENDOR | USB_RECIP_OTHER; + + switch (req_id) { + case PCAN_USBPRO_REQ_FCT: + p = usb_sndctrlpipe(dev->udev, 0); + break; + + default: + p = usb_rcvctrlpipe(dev->udev, 0); + req_type |= USB_DIR_IN; + memset(req_addr, '\0', req_size); + break; + } + + err = usb_control_msg(dev->udev, p, req_id, req_type, req_value, 0, + req_addr, req_size, 2 * USB_CTRL_GET_TIMEOUT); + if (err < 0) { + netdev_info(dev->netdev, + "unable to request usb[type=%d value=%d] err=%d\n", + req_id, req_value, err); + return err; + } + + return 0; +} + +static int pcan_usb_pro_set_ts(struct peak_usb_device *dev, u16 onoff) +{ + struct pcan_usb_pro_msg um; + + pcan_msg_init_empty(&um, dev->cmd_buf, PCAN_USB_MAX_CMD_LEN); + pcan_msg_add_rec(&um, PCAN_USBPRO_SETTS, onoff); + + return pcan_usb_pro_send_cmd(dev, &um); +} + +static int pcan_usb_pro_set_bitrate(struct peak_usb_device *dev, u32 ccbt) +{ + struct pcan_usb_pro_device *pdev = + container_of(dev, struct pcan_usb_pro_device, dev); + struct pcan_usb_pro_msg um; + + pcan_msg_init_empty(&um, dev->cmd_buf, PCAN_USB_MAX_CMD_LEN); + pcan_msg_add_rec(&um, PCAN_USBPRO_SETBTR, dev->ctrl_idx, ccbt); + + /* cache the CCBT value to reuse it before next buson */ + pdev->cached_ccbt = ccbt; + + return pcan_usb_pro_send_cmd(dev, &um); +} + +static int pcan_usb_pro_set_bus(struct peak_usb_device *dev, u8 onoff) +{ + struct pcan_usb_pro_msg um; + + /* if bus=on, be sure the bitrate being set before! */ + if (onoff) { + struct pcan_usb_pro_device *pdev = + container_of(dev, struct pcan_usb_pro_device, dev); + + pcan_usb_pro_set_bitrate(dev, pdev->cached_ccbt); + } + + pcan_msg_init_empty(&um, dev->cmd_buf, PCAN_USB_MAX_CMD_LEN); + pcan_msg_add_rec(&um, PCAN_USBPRO_SETBUSACT, dev->ctrl_idx, onoff); + + return pcan_usb_pro_send_cmd(dev, &um); +} + +static int pcan_usb_pro_set_silent(struct peak_usb_device *dev, u8 onoff) +{ + struct pcan_usb_pro_msg um; + + pcan_msg_init_empty(&um, dev->cmd_buf, PCAN_USB_MAX_CMD_LEN); + pcan_msg_add_rec(&um, PCAN_USBPRO_SETSILENT, dev->ctrl_idx, onoff); + + return pcan_usb_pro_send_cmd(dev, &um); +} + +static int pcan_usb_pro_set_filter(struct peak_usb_device *dev, u16 filter_mode) +{ + struct pcan_usb_pro_msg um; + + pcan_msg_init_empty(&um, dev->cmd_buf, PCAN_USB_MAX_CMD_LEN); + pcan_msg_add_rec(&um, PCAN_USBPRO_SETFILTR, dev->ctrl_idx, filter_mode); + + return pcan_usb_pro_send_cmd(dev, &um); +} + +static int pcan_usb_pro_set_led(struct peak_usb_device *dev, u8 mode, + u32 timeout) +{ + struct pcan_usb_pro_msg um; + + pcan_msg_init_empty(&um, dev->cmd_buf, PCAN_USB_MAX_CMD_LEN); + pcan_msg_add_rec(&um, PCAN_USBPRO_SETLED, dev->ctrl_idx, mode, timeout); + + return pcan_usb_pro_send_cmd(dev, &um); +} + +static int pcan_usb_pro_get_device_id(struct peak_usb_device *dev, + u32 *device_id) +{ + struct pcan_usb_pro_devid *pdn; + struct pcan_usb_pro_msg um; + int err; + u8 *pc; + + pc = pcan_msg_init_empty(&um, dev->cmd_buf, PCAN_USB_MAX_CMD_LEN); + pcan_msg_add_rec(&um, PCAN_USBPRO_GETDEVID, dev->ctrl_idx); + + err = pcan_usb_pro_send_cmd(dev, &um); + if (err) + return err; + + err = pcan_usb_pro_wait_rsp(dev, &um); + if (err) + return err; + + pdn = (struct pcan_usb_pro_devid *)pc; + if (device_id) + *device_id = le32_to_cpu(pdn->serial_num); + + return err; +} + +static int pcan_usb_pro_set_bittiming(struct peak_usb_device *dev, + struct can_bittiming *bt) +{ + u32 ccbt; + + ccbt = (dev->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) ? 0x00800000 : 0; + ccbt |= (bt->sjw - 1) << 24; + ccbt |= (bt->phase_seg2 - 1) << 20; + ccbt |= (bt->prop_seg + bt->phase_seg1 - 1) << 16; /* = tseg1 */ + ccbt |= bt->brp - 1; + + netdev_info(dev->netdev, "setting ccbt=0x%08x\n", ccbt); + + return pcan_usb_pro_set_bitrate(dev, ccbt); +} + +void pcan_usb_pro_restart_complete(struct urb *urb) +{ + /* can delete usb resources */ + peak_usb_async_complete(urb); + + /* notify candev and netdev */ + peak_usb_restart_complete(urb->context); +} + +/* + * handle restart but in asynchronously way + */ +static int pcan_usb_pro_restart_async(struct peak_usb_device *dev, + struct urb *urb, u8 *buf) +{ + struct pcan_usb_pro_msg um; + + pcan_msg_init_empty(&um, buf, PCAN_USB_MAX_CMD_LEN); + pcan_msg_add_rec(&um, PCAN_USBPRO_SETBUSACT, dev->ctrl_idx, 1); + + usb_fill_bulk_urb(urb, dev->udev, + usb_sndbulkpipe(dev->udev, PCAN_USBPRO_EP_CMDOUT), + buf, PCAN_USB_MAX_CMD_LEN, + pcan_usb_pro_restart_complete, dev); + + return usb_submit_urb(urb, GFP_ATOMIC); +} + +static int pcan_usb_pro_drv_loaded(struct peak_usb_device *dev, int loaded) +{ + u8 *buffer; + int err; + + buffer = kzalloc(PCAN_USBPRO_FCT_DRVLD_REQ_LEN, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + buffer[0] = 0; + buffer[1] = !!loaded; + + err = pcan_usb_pro_send_req(dev, PCAN_USBPRO_REQ_FCT, + PCAN_USBPRO_FCT_DRVLD, buffer, + PCAN_USBPRO_FCT_DRVLD_REQ_LEN); + kfree(buffer); + + return err; +} + +static inline +struct pcan_usb_pro_interface *pcan_usb_pro_dev_if(struct peak_usb_device *dev) +{ + struct pcan_usb_pro_device *pdev = + container_of(dev, struct pcan_usb_pro_device, dev); + return pdev->usb_if; +} + +static int pcan_usb_pro_handle_canmsg(struct pcan_usb_pro_interface *usb_if, + struct pcan_usb_pro_rxmsg *rx) +{ + const unsigned int ctrl_idx = (rx->len >> 4) & 0x0f; + struct peak_usb_device *dev = usb_if->dev[ctrl_idx]; + struct net_device *netdev = dev->netdev; + struct can_frame *can_frame; + struct sk_buff *skb; + struct skb_shared_hwtstamps *hwts; + + skb = alloc_can_skb(netdev, &can_frame); + if (!skb) + return -ENOMEM; + + can_frame->can_id = le32_to_cpu(rx->id); + can_frame->can_dlc = rx->len & 0x0f; + + if (rx->flags & PCAN_USBPRO_EXT) + can_frame->can_id |= CAN_EFF_FLAG; + + if (rx->flags & PCAN_USBPRO_RTR) + can_frame->can_id |= CAN_RTR_FLAG; + else + memcpy(can_frame->data, rx->data, can_frame->can_dlc); + + hwts = skb_hwtstamps(skb); + peak_usb_get_ts_time(&usb_if->time_ref, le32_to_cpu(rx->ts32), + &hwts->hwtstamp); + + netdev->stats.rx_packets++; + netdev->stats.rx_bytes += can_frame->can_dlc; + netif_rx(skb); + + return 0; +} + +static int pcan_usb_pro_handle_error(struct pcan_usb_pro_interface *usb_if, + struct pcan_usb_pro_rxstatus *er) +{ + const u16 raw_status = le16_to_cpu(er->status); + const unsigned int ctrl_idx = (er->channel >> 4) & 0x0f; + struct peak_usb_device *dev = usb_if->dev[ctrl_idx]; + struct net_device *netdev = dev->netdev; + struct can_frame *can_frame; + enum can_state new_state = CAN_STATE_ERROR_ACTIVE; + u8 err_mask = 0; + struct sk_buff *skb; + struct skb_shared_hwtstamps *hwts; + + /* nothing should be sent while in BUS_OFF state */ + if (dev->can.state == CAN_STATE_BUS_OFF) + return 0; + + if (!raw_status) { + /* no error bit (back to active state) */ + dev->can.state = CAN_STATE_ERROR_ACTIVE; + return 0; + } + + if (raw_status & (PCAN_USBPRO_STATUS_OVERRUN | + PCAN_USBPRO_STATUS_QOVERRUN)) { + /* trick to bypass next comparison and process other errors */ + new_state = CAN_STATE_MAX; + } + + if (raw_status & PCAN_USBPRO_STATUS_BUS) { + new_state = CAN_STATE_BUS_OFF; + } else if (raw_status & PCAN_USBPRO_STATUS_ERROR) { + u32 rx_err_cnt = (le32_to_cpu(er->err_frm) & 0x00ff0000) >> 16; + u32 tx_err_cnt = (le32_to_cpu(er->err_frm) & 0xff000000) >> 24; + + if (rx_err_cnt > 127) + err_mask |= CAN_ERR_CRTL_RX_PASSIVE; + else if (rx_err_cnt > 96) + err_mask |= CAN_ERR_CRTL_RX_WARNING; + + if (tx_err_cnt > 127) + err_mask |= CAN_ERR_CRTL_TX_PASSIVE; + else if (tx_err_cnt > 96) + err_mask |= CAN_ERR_CRTL_TX_WARNING; + + if (err_mask & (CAN_ERR_CRTL_RX_WARNING | + CAN_ERR_CRTL_TX_WARNING)) + new_state = CAN_STATE_ERROR_WARNING; + else if (err_mask & (CAN_ERR_CRTL_RX_PASSIVE | + CAN_ERR_CRTL_TX_PASSIVE)) + new_state = CAN_STATE_ERROR_PASSIVE; + } + + /* donot post any error if current state didn't change */ + if (dev->can.state == new_state) + return 0; + + /* allocate an skb to store the error frame */ + skb = alloc_can_err_skb(netdev, &can_frame); + if (!skb) + return -ENOMEM; + + switch (new_state) { + case CAN_STATE_BUS_OFF: + can_frame->can_id |= CAN_ERR_BUSOFF; + dev->can.can_stats.bus_off++; + can_bus_off(netdev); + break; + + case CAN_STATE_ERROR_PASSIVE: + can_frame->can_id |= CAN_ERR_CRTL; + can_frame->data[1] |= err_mask; + dev->can.can_stats.error_passive++; + break; + + case CAN_STATE_ERROR_WARNING: + can_frame->can_id |= CAN_ERR_CRTL; + can_frame->data[1] |= err_mask; + dev->can.can_stats.error_warning++; + break; + + case CAN_STATE_ERROR_ACTIVE: + break; + + default: + /* CAN_STATE_MAX (trick to handle other errors) */ + if (raw_status & PCAN_USBPRO_STATUS_OVERRUN) { + can_frame->can_id |= CAN_ERR_PROT; + can_frame->data[2] |= CAN_ERR_PROT_OVERLOAD; + netdev->stats.rx_over_errors++; + netdev->stats.rx_errors++; + } + + if (raw_status & PCAN_USBPRO_STATUS_QOVERRUN) { + can_frame->can_id |= CAN_ERR_CRTL; + can_frame->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW; + netdev->stats.rx_over_errors++; + netdev->stats.rx_errors++; + } + + new_state = CAN_STATE_ERROR_ACTIVE; + break; + } + + dev->can.state = new_state; + + hwts = skb_hwtstamps(skb); + peak_usb_get_ts_time(&usb_if->time_ref, le32_to_cpu(er->ts32), &hwts->hwtstamp); + netdev->stats.rx_packets++; + netdev->stats.rx_bytes += can_frame->can_dlc; + netif_rx(skb); + + return 0; +} + +static void pcan_usb_pro_handle_ts(struct pcan_usb_pro_interface *usb_if, + struct pcan_usb_pro_rxts *ts) +{ + /* should wait until clock is stabilized */ + if (usb_if->cm_ignore_count > 0) + usb_if->cm_ignore_count--; + else + peak_usb_set_ts_now(&usb_if->time_ref, + le32_to_cpu(ts->ts64[1])); +} + +/* + * callback for bulk IN urb + */ +static int pcan_usb_pro_decode_buf(struct peak_usb_device *dev, struct urb *urb) +{ + struct pcan_usb_pro_interface *usb_if = pcan_usb_pro_dev_if(dev); + struct net_device *netdev = dev->netdev; + struct pcan_usb_pro_msg usb_msg; + u8 *rec_ptr, *msg_end; + u16 rec_cnt; + int err = 0; + + rec_ptr = pcan_msg_init(&usb_msg, urb->transfer_buffer, + urb->actual_length); + if (!rec_ptr) { + netdev_err(netdev, "bad msg hdr len %d\n", urb->actual_length); + return -EINVAL; + } + + /* loop reading all the records from the incoming message */ + msg_end = urb->transfer_buffer + urb->actual_length; + rec_cnt = le16_to_cpu(*usb_msg.u.rec_cnt_rd); + for (; rec_cnt > 0; rec_cnt--) { + union pcan_usb_pro_rec *pr = (union pcan_usb_pro_rec *)rec_ptr; + u16 sizeof_rec = pcan_usb_pro_sizeof_rec[pr->data_type]; + + if (!sizeof_rec) { + netdev_err(netdev, + "got unsupported rec in usb msg:\n"); + err = -ENOTSUPP; + break; + } + + /* check if the record goes out of current packet */ + if (rec_ptr + sizeof_rec > msg_end) { + netdev_err(netdev, + "got frag rec: should inc usb rx buf size\n"); + err = -EBADMSG; + break; + } + + switch (pr->data_type) { + case PCAN_USBPRO_RXMSG8: + case PCAN_USBPRO_RXMSG4: + case PCAN_USBPRO_RXMSG0: + case PCAN_USBPRO_RXRTR: + err = pcan_usb_pro_handle_canmsg(usb_if, &pr->rx_msg); + if (err < 0) + goto fail; + break; + + case PCAN_USBPRO_RXSTATUS: + err = pcan_usb_pro_handle_error(usb_if, &pr->rx_status); + if (err < 0) + goto fail; + break; + + case PCAN_USBPRO_RXTS: + pcan_usb_pro_handle_ts(usb_if, &pr->rx_ts); + break; + + default: + netdev_err(netdev, + "unhandled rec type 0x%02x (%d): ignored\n", + pr->data_type, pr->data_type); + break; + } + + rec_ptr += sizeof_rec; + } + +fail: + if (err) + pcan_dump_mem("received msg", + urb->transfer_buffer, urb->actual_length); + + return err; +} + +static int pcan_usb_pro_encode_msg(struct peak_usb_device *dev, + struct sk_buff *skb, u8 *obuf, size_t *size) +{ + struct can_frame *cf = (struct can_frame *)skb->data; + u8 data_type, len, flags; + struct pcan_usb_pro_msg usb_msg; + + pcan_msg_init_empty(&usb_msg, obuf, *size); + + if ((cf->can_id & CAN_RTR_FLAG) || (cf->can_dlc == 0)) + data_type = PCAN_USBPRO_TXMSG0; + else if (cf->can_dlc <= 4) + data_type = PCAN_USBPRO_TXMSG4; + else + data_type = PCAN_USBPRO_TXMSG8; + + len = (dev->ctrl_idx << 4) | (cf->can_dlc & 0x0f); + + flags = 0; + if (cf->can_id & CAN_EFF_FLAG) + flags |= 0x02; + if (cf->can_id & CAN_RTR_FLAG) + flags |= 0x01; + + pcan_msg_add_rec(&usb_msg, data_type, 0, flags, len, cf->can_id, + cf->data); + + *size = usb_msg.rec_buffer_len; + + return 0; +} + +static int pcan_usb_pro_start(struct peak_usb_device *dev) +{ + struct pcan_usb_pro_device *pdev = + container_of(dev, struct pcan_usb_pro_device, dev); + int err; + + err = pcan_usb_pro_set_silent(dev, + dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY); + if (err) + return err; + + /* filter mode: 0-> All OFF; 1->bypass */ + err = pcan_usb_pro_set_filter(dev, 1); + if (err) + return err; + + /* opening first device: */ + if (pdev->usb_if->dev_opened_count == 0) { + /* reset time_ref */ + peak_usb_init_time_ref(&pdev->usb_if->time_ref, &pcan_usb_pro); + + /* ask device to send ts messages */ + err = pcan_usb_pro_set_ts(dev, 1); + } + + pdev->usb_if->dev_opened_count++; + + return err; +} + +/* + * stop interface + * (last chance before set bus off) + */ +static int pcan_usb_pro_stop(struct peak_usb_device *dev) +{ + struct pcan_usb_pro_device *pdev = + container_of(dev, struct pcan_usb_pro_device, dev); + + /* turn off ts msgs for that interface if no other dev opened */ + if (pdev->usb_if->dev_opened_count == 1) + pcan_usb_pro_set_ts(dev, 0); + + pdev->usb_if->dev_opened_count--; + + return 0; +} + +/* + * called when probing to initialize a device object. + */ +static int pcan_usb_pro_init(struct peak_usb_device *dev) +{ + struct pcan_usb_pro_device *pdev = + container_of(dev, struct pcan_usb_pro_device, dev); + struct pcan_usb_pro_interface *usb_if = NULL; + struct pcan_usb_pro_fwinfo *fi = NULL; + struct pcan_usb_pro_blinfo *bi = NULL; + int err; + + /* do this for 1st channel only */ + if (!dev->prev_siblings) { + /* allocate netdevices common structure attached to first one */ + usb_if = kzalloc(sizeof(struct pcan_usb_pro_interface), + GFP_KERNEL); + fi = kmalloc(sizeof(struct pcan_usb_pro_fwinfo), GFP_KERNEL); + bi = kmalloc(sizeof(struct pcan_usb_pro_blinfo), GFP_KERNEL); + if (!usb_if || !fi || !bi) { + err = -ENOMEM; + goto err_out; + } + + /* number of ts msgs to ignore before taking one into account */ + usb_if->cm_ignore_count = 5; + + /* + * explicit use of dev_xxx() instead of netdev_xxx() here: + * information displayed are related to the device itself, not + * to the canx netdevices. + */ + err = pcan_usb_pro_send_req(dev, PCAN_USBPRO_REQ_INFO, + PCAN_USBPRO_INFO_FW, + fi, sizeof(*fi)); + if (err) { + dev_err(dev->netdev->dev.parent, + "unable to read %s firmware info (err %d)\n", + pcan_usb_pro.name, err); + goto err_out; + } + + err = pcan_usb_pro_send_req(dev, PCAN_USBPRO_REQ_INFO, + PCAN_USBPRO_INFO_BL, + bi, sizeof(*bi)); + if (err) { + dev_err(dev->netdev->dev.parent, + "unable to read %s bootloader info (err %d)\n", + pcan_usb_pro.name, err); + goto err_out; + } + + /* tell the device the can driver is running */ + err = pcan_usb_pro_drv_loaded(dev, 1); + if (err) + goto err_out; + + dev_info(dev->netdev->dev.parent, + "PEAK-System %s hwrev %u serial %08X.%08X (%u channels)\n", + pcan_usb_pro.name, + bi->hw_rev, bi->serial_num_hi, bi->serial_num_lo, + pcan_usb_pro.ctrl_count); + } else { + usb_if = pcan_usb_pro_dev_if(dev->prev_siblings); + } + + pdev->usb_if = usb_if; + usb_if->dev[dev->ctrl_idx] = dev; + + /* set LED in default state (end of init phase) */ + pcan_usb_pro_set_led(dev, 0, 1); + + kfree(bi); + kfree(fi); + + return 0; + + err_out: + kfree(bi); + kfree(fi); + kfree(usb_if); + + return err; +} + +static void pcan_usb_pro_exit(struct peak_usb_device *dev) +{ + struct pcan_usb_pro_device *pdev = + container_of(dev, struct pcan_usb_pro_device, dev); + + /* + * when rmmod called before unplug and if down, should reset things + * before leaving + */ + if (dev->can.state != CAN_STATE_STOPPED) { + /* set bus off on the corresponding channel */ + pcan_usb_pro_set_bus(dev, 0); + } + + /* if channel #0 (only) */ + if (dev->ctrl_idx == 0) { + /* turn off calibration message if any device were opened */ + if (pdev->usb_if->dev_opened_count > 0) + pcan_usb_pro_set_ts(dev, 0); + + /* tell the PCAN-USB Pro device the driver is being unloaded */ + pcan_usb_pro_drv_loaded(dev, 0); + } +} + +/* + * called when PCAN-USB Pro adapter is unplugged + */ +static void pcan_usb_pro_free(struct peak_usb_device *dev) +{ + /* last device: can free pcan_usb_pro_interface object now */ + if (!dev->prev_siblings && !dev->next_siblings) + kfree(pcan_usb_pro_dev_if(dev)); +} + +/* + * probe function for new PCAN-USB Pro usb interface + */ +int pcan_usb_pro_probe(struct usb_interface *intf) +{ + struct usb_host_interface *if_desc; + int i; + + if_desc = intf->altsetting; + + /* check interface endpoint addresses */ + for (i = 0; i < if_desc->desc.bNumEndpoints; i++) { + struct usb_endpoint_descriptor *ep = &if_desc->endpoint[i].desc; + + /* + * below is the list of valid ep addresses. Any other ep address + * is considered as not-CAN interface address => no dev created + */ + switch (ep->bEndpointAddress) { + case PCAN_USBPRO_EP_CMDOUT: + case PCAN_USBPRO_EP_CMDIN: + case PCAN_USBPRO_EP_MSGOUT_0: + case PCAN_USBPRO_EP_MSGOUT_1: + case PCAN_USBPRO_EP_MSGIN: + case PCAN_USBPRO_EP_UNUSED: + break; + default: + return -ENODEV; + } + } + + return 0; +} + +/* + * describe the PCAN-USB Pro adapter + */ +static const struct can_bittiming_const pcan_usb_pro_const = { + .name = "pcan_usb_pro", + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 1024, + .brp_inc = 1, +}; + +const struct peak_usb_adapter pcan_usb_pro = { + .name = "PCAN-USB Pro", + .device_id = PCAN_USBPRO_PRODUCT_ID, + .ctrl_count = PCAN_USBPRO_CHANNEL_COUNT, + .ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY, + .clock = { + .freq = PCAN_USBPRO_CRYSTAL_HZ, + }, + .bittiming_const = &pcan_usb_pro_const, + + /* size of device private data */ + .sizeof_dev_private = sizeof(struct pcan_usb_pro_device), + + /* timestamps usage */ + .ts_used_bits = 32, + .ts_period = 1000000, /* calibration period in ts. */ + .us_per_ts_scale = 1, /* us = (ts * scale) >> shift */ + .us_per_ts_shift = 0, + + /* give here messages in/out endpoints */ + .ep_msg_in = PCAN_USBPRO_EP_MSGIN, + .ep_msg_out = {PCAN_USBPRO_EP_MSGOUT_0, PCAN_USBPRO_EP_MSGOUT_1}, + + /* size of rx/tx usb buffers */ + .rx_buffer_size = PCAN_USBPRO_RX_BUFFER_SIZE, + .tx_buffer_size = PCAN_USBPRO_TX_BUFFER_SIZE, + + /* device callbacks */ + .intf_probe = pcan_usb_pro_probe, + .dev_init = pcan_usb_pro_init, + .dev_exit = pcan_usb_pro_exit, + .dev_free = pcan_usb_pro_free, + .dev_set_bus = pcan_usb_pro_set_bus, + .dev_set_bittiming = pcan_usb_pro_set_bittiming, + .dev_get_device_id = pcan_usb_pro_get_device_id, + .dev_decode_buf = pcan_usb_pro_decode_buf, + .dev_encode_msg = pcan_usb_pro_encode_msg, + .dev_start = pcan_usb_pro_start, + .dev_stop = pcan_usb_pro_stop, + .dev_restart_async = pcan_usb_pro_restart_async, +}; diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.h b/drivers/net/can/usb/peak_usb/pcan_usb_pro.h new file mode 100644 index 000000000..6bb12357d --- /dev/null +++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.h @@ -0,0 +1,184 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * CAN driver for PEAK System PCAN-USB Pro adapter + * Derived from the PCAN project file driver/src/pcan_usbpro_fw.h + * + * Copyright (C) 2003-2011 PEAK System-Technik GmbH + * Copyright (C) 2011-2012 Stephane Grosjean <s.grosjean@peak-system.com> + */ +#ifndef PCAN_USB_PRO_H +#define PCAN_USB_PRO_H + +/* + * USB Vendor request data types + */ +#define PCAN_USBPRO_REQ_INFO 0 +#define PCAN_USBPRO_REQ_FCT 2 + +/* Vendor Request value for XXX_INFO */ +#define PCAN_USBPRO_INFO_BL 0 +#define PCAN_USBPRO_INFO_FW 1 + +/* PCAN-USB Pro (FD) Endpoints */ +#define PCAN_USBPRO_EP_CMDOUT 1 +#define PCAN_USBPRO_EP_CMDIN (PCAN_USBPRO_EP_CMDOUT | USB_DIR_IN) +#define PCAN_USBPRO_EP_MSGOUT_0 2 +#define PCAN_USBPRO_EP_MSGIN (PCAN_USBPRO_EP_MSGOUT_0 | USB_DIR_IN) +#define PCAN_USBPRO_EP_MSGOUT_1 3 +#define PCAN_USBPRO_EP_UNUSED (PCAN_USBPRO_EP_MSGOUT_1 | USB_DIR_IN) + +/* Vendor Request value for XXX_FCT */ +#define PCAN_USBPRO_FCT_DRVLD 5 /* tell device driver is loaded */ +#define PCAN_USBPRO_FCT_DRVLD_REQ_LEN 16 + +/* PCAN_USBPRO_INFO_BL vendor request record type */ +struct __packed pcan_usb_pro_blinfo { + __le32 ctrl_type; + u8 version[4]; + u8 day; + u8 month; + u8 year; + u8 dummy; + __le32 serial_num_hi; + __le32 serial_num_lo; + __le32 hw_type; + __le32 hw_rev; +}; + +/* PCAN_USBPRO_INFO_FW vendor request record type */ +struct __packed pcan_usb_pro_fwinfo { + __le32 ctrl_type; + u8 version[4]; + u8 day; + u8 month; + u8 year; + u8 dummy; + __le32 fw_type; +}; + +/* + * USB Command record types + */ +#define PCAN_USBPRO_SETBTR 0x02 +#define PCAN_USBPRO_SETBUSACT 0x04 +#define PCAN_USBPRO_SETSILENT 0x05 +#define PCAN_USBPRO_SETFILTR 0x0a +#define PCAN_USBPRO_SETTS 0x10 +#define PCAN_USBPRO_GETDEVID 0x12 +#define PCAN_USBPRO_SETLED 0x1C +#define PCAN_USBPRO_RXMSG8 0x80 +#define PCAN_USBPRO_RXMSG4 0x81 +#define PCAN_USBPRO_RXMSG0 0x82 +#define PCAN_USBPRO_RXRTR 0x83 +#define PCAN_USBPRO_RXSTATUS 0x84 +#define PCAN_USBPRO_RXTS 0x85 +#define PCAN_USBPRO_TXMSG8 0x41 +#define PCAN_USBPRO_TXMSG4 0x42 +#define PCAN_USBPRO_TXMSG0 0x43 + +/* record structures */ +struct __packed pcan_usb_pro_btr { + u8 data_type; + u8 channel; + __le16 dummy; + __le32 CCBT; +}; + +struct __packed pcan_usb_pro_busact { + u8 data_type; + u8 channel; + __le16 onoff; +}; + +struct __packed pcan_usb_pro_silent { + u8 data_type; + u8 channel; + __le16 onoff; +}; + +struct __packed pcan_usb_pro_filter { + u8 data_type; + u8 dummy; + __le16 filter_mode; +}; + +struct __packed pcan_usb_pro_setts { + u8 data_type; + u8 dummy; + __le16 mode; +}; + +struct __packed pcan_usb_pro_devid { + u8 data_type; + u8 channel; + __le16 dummy; + __le32 serial_num; +}; + +struct __packed pcan_usb_pro_setled { + u8 data_type; + u8 channel; + __le16 mode; + __le32 timeout; +}; + +struct __packed pcan_usb_pro_rxmsg { + u8 data_type; + u8 client; + u8 flags; + u8 len; + __le32 ts32; + __le32 id; + + u8 data[8]; +}; + +#define PCAN_USBPRO_STATUS_ERROR 0x0001 +#define PCAN_USBPRO_STATUS_BUS 0x0002 +#define PCAN_USBPRO_STATUS_OVERRUN 0x0004 +#define PCAN_USBPRO_STATUS_QOVERRUN 0x0008 + +struct __packed pcan_usb_pro_rxstatus { + u8 data_type; + u8 channel; + __le16 status; + __le32 ts32; + __le32 err_frm; +}; + +struct __packed pcan_usb_pro_rxts { + u8 data_type; + u8 dummy[3]; + __le32 ts64[2]; +}; + +struct __packed pcan_usb_pro_txmsg { + u8 data_type; + u8 client; + u8 flags; + u8 len; + __le32 id; + u8 data[8]; +}; + +union pcan_usb_pro_rec { + u8 data_type; + struct pcan_usb_pro_btr btr; + struct pcan_usb_pro_busact bus_act; + struct pcan_usb_pro_silent silent_mode; + struct pcan_usb_pro_filter filter_mode; + struct pcan_usb_pro_setts ts; + struct pcan_usb_pro_devid dev_id; + struct pcan_usb_pro_setled set_led; + struct pcan_usb_pro_rxmsg rx_msg; + struct pcan_usb_pro_rxstatus rx_status; + struct pcan_usb_pro_rxts rx_ts; + struct pcan_usb_pro_txmsg tx_msg; +}; + +int pcan_usb_pro_probe(struct usb_interface *intf); +int pcan_usb_pro_send_req(struct peak_usb_device *dev, int req_id, + int req_value, void *req_addr, int req_size); +void pcan_usb_pro_restart_complete(struct urb *urb); + +#endif diff --git a/drivers/net/can/usb/ucan.c b/drivers/net/can/usb/ucan.c new file mode 100644 index 000000000..dc5290b36 --- /dev/null +++ b/drivers/net/can/usb/ucan.c @@ -0,0 +1,1606 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* Driver for Theobroma Systems UCAN devices, Protocol Version 3 + * + * Copyright (C) 2018 Theobroma Systems Design und Consulting GmbH + * + * + * General Description: + * + * The USB Device uses three Endpoints: + * + * CONTROL Endpoint: Is used the setup the device (start, stop, + * info, configure). + * + * 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. + */ + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/signal.h> +#include <linux/skbuff.h> +#include <linux/slab.h> +#include <linux/usb.h> + +#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 + +/* 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 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 + * + * 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, + /* 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 = 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 */ +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_TX_COMPLETE = 1, /* CAN frame transmission completed */ + UCAN_IN_RX = 2, /* CAN frame received */ +}; + +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 ucan_ctl_cmd_start cmd_start; + /* Setup Bittiming + * bmRequest == UCAN_COMMAND_SET_BITTIMING + */ + struct ucan_ctl_cmd_set_bittiming cmd_set_bittiming; + /* Get Device Information + * bmRequest == UCAN_COMMAND_GET; wValue = UCAN_COMMAND_GET_INFO + */ + 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 { + __le16 len; /* Length of the content include header */ + u8 type; /* UCAN_OUT_TX and friends */ + u8 subtype; /* command sub type */ + + union { + /* Transmit CAN frame + * (type == UCAN_TX) && ((msg.can_msg.id & CAN_RTR_FLAG) == 0) + * subtype stores the echo id + */ + struct ucan_can_msg can_msg; + } msg; +} __packed __aligned(0x4); + +/* IN Endpoint, inbound messages */ +struct ucan_message_in { + __le16 len; /* Length of the content include header */ + u8 type; /* UCAN_IN_RX and friends */ + u8 subtype; /* command sub type */ + + union { + /* CAN Frame received + * (type == UCAN_IN_RX) + * && ((msg.can_msg.id & CAN_RTR_FLAG) == 0) + */ + 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_IN_HDR_SIZE offsetof(struct ucan_message_in, msg) +#define UCAN_IN_LEN(member) (UCAN_OUT_HDR_SIZE + sizeof(member)) + +struct ucan_priv; + +/* Context Information for transmission URBs */ +struct ucan_urb_context { + struct ucan_priv *up; + u8 dlc; + bool allocated; +}; + +/* Information reported by the USB device */ +struct ucan_device_info { + struct can_bittiming_const bittiming_const; + u8 tx_fifo; +}; + +/* Driver private data */ +struct ucan_priv { + /* must be the first member */ + struct can_priv can; + + /* linux USB device structures */ + struct usb_device *udev; + struct usb_interface *intf; + struct net_device *netdev; + + /* 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; + + union ucan_ctl_payload *ctl_msg_buffer; + struct ucan_device_info device_info; + + /* transmission control information and locks */ + spinlock_t context_lock; + unsigned int available_tx_urbs; + struct ucan_urb_context *context_array; +}; + +static u8 ucan_get_can_dlc(struct ucan_can_msg *msg, u16 len) +{ + 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 void ucan_release_context_array(struct ucan_priv *up) +{ + if (!up->context_array) + return; + + /* 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_alloc_context_array(struct ucan_priv *up) +{ + 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; + } + + 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 struct ucan_urb_context *ucan_alloc_context(struct ucan_priv *up) +{ + 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_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); +} + +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) +{ + struct can_bittiming_const *bittiming = + &up->device_info.bittiming_const; + u16 ctrlmodes; + + /* store the data */ + 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 = 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 (ctrlmodes & UCAN_MODE_LOOPBACK) + up->can.ctrlmode_supported |= CAN_CTRLMODE_LOOPBACK; + if (ctrlmodes & UCAN_MODE_SILENT) + up->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY; + if (ctrlmodes & UCAN_MODE_3_SAMPLES) + up->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES; + if (ctrlmodes & UCAN_MODE_ONE_SHOT) + up->can.ctrlmode_supported |= CAN_CTRLMODE_ONE_SHOT; + if (ctrlmodes & UCAN_MODE_BERR_REPORT) + up->can.ctrlmode_supported |= CAN_CTRLMODE_BERR_REPORTING; +} + +/* Handle a CAN error frame that we have received from the device. + * Returns true if the can state has changed. + */ +static bool ucan_handle_error_frame(struct ucan_priv *up, + struct ucan_message_in *m, + canid_t canid) +{ + 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; + + 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_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 = CAN_STATE_ERROR_WARNING; + + if (d1 & (CAN_ERR_CRTL_RX_PASSIVE | CAN_ERR_CRTL_TX_PASSIVE)) + new_state = CAN_STATE_ERROR_PASSIVE; + } + + /* 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++; + } + + /* 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) { + up->can.state = new_state; + return true; + } + + /* we switched into a worse state */ + up->can.state = new_state; + 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 + * + * This function allocates an skb and transferres it to the Linux + * network stack + */ +static void ucan_rx_can_msg(struct ucan_priv *up, struct ucan_message_in *m) +{ + int len; + canid_t 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)) { + 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) { + 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 */ + skb = alloc_can_skb(up->netdev, &cf); + if (!skb) + return; + + /* fill the can frame */ + cf->can_id = canid; + + /* compute DLC taking RTR_FLAG into account */ + cf->can_dlc = ucan_get_can_dlc(&m->msg.can_msg, len); + + /* 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_rx(skb); +} + +/* 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; + } + + 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 pos; + struct ucan_priv *up = urb->context; + struct net_device *netdev = up->netdev; + 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) { + case 0: + break; + case -ENOENT: + case -EPIPE: + case -EPROTO: + case -ESHUTDOWN: + case -ETIME: + /* 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) { + netdev_warn(up->netdev, + "invalid message (short; no hdr; l:%d)\n", + urb->actual_length); + goto resubmit; + } + + /* 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) { + 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; + } + + switch (m->type) { + case UCAN_IN_RX: + ucan_rx_can_msg(up, m); + break; + case UCAN_IN_TX_COMPLETE: + ucan_tx_complete_msg(up, m); + break; + default: + netdev_warn(up->netdev, + "invalid message (type; t:%d)\n", + m->type); + 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_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_ATOMIC); + + 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; + + /* get the urb context */ + if (WARN_ON_ONCE(!context)) + return; + + /* free up our allocated buffer */ + usb_free_coherent(urb->dev, + sizeof(struct ucan_message_out), + urb->transfer_buffer, + urb->transfer_dma); + + up = context->up; + if (WARN_ON_ONCE(!up)) + return; + + /* sanity check */ + if (!netif_device_present(up->netdev)) + return; + + /* 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); + + /* 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); + + up->netdev->stats.tx_dropped++; + + /* 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"); + } +} + +static void ucan_cleanup_rx_urbs(struct ucan_priv *up, struct urb **urbs) +{ + int i; + + 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]); + } + } + + memset(urbs, 0, sizeof(*urbs) * UCAN_MAX_RX_URBS); +} + +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); + + for (i = 0; i < UCAN_MAX_RX_URBS; i++) { + void *buf; + + urbs[i] = usb_alloc_urb(0, GFP_KERNEL); + if (!urbs[i]) + goto err; + + buf = usb_alloc_coherent(up->udev, + up->in_ep_size, + GFP_KERNEL, &urbs[i]->transfer_dma); + if (!buf) { + /* cleanup this urb */ + usb_free_urb(urbs[i]); + urbs[i] = NULL; + goto err; + } + + 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); + + 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) { + netdev_err(up->netdev, + "could not submit urb; code: %d\n", + ret); + goto err; + } + + /* 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; +} + +/* 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; + 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); + + /* Driver is ready to receive data - start the USB device */ + ret = ucan_ctrl_command_out(up, UCAN_COMMAND_START, 0, 2); + 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 */ + netif_start_queue(netdev); + + return 0; + +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; +} + +static struct urb *ucan_prepare_tx_urb(struct ucan_priv *up, + struct ucan_urb_context *context, + struct can_frame *cf, + u8 echo_index) +{ + int mlen; + struct urb *urb; + struct ucan_message_out *m; + + /* 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(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); + if (!m) { + netdev_err(up->netdev, "no memory left for USB buffer\n"); + usb_free_urb(urb); + return NULL; + } + + /* build the USB message */ + 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_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; + memcpy(m->msg.can_msg.data, cf->data, cf->can_dlc); + } + m->len = cpu_to_le16(mlen); + + context->dlc = cf->can_dlc; + + m->subtype = echo_index; + + /* build the urb */ + usb_fill_bulk_urb(urb, up->udev, + usb_sndbulkpipe(up->udev, + up->out_ep_addr), + m, mlen, ucan_write_bulk_callback, context); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + 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 */ + if (ret) { + /* on error, clean up */ + 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, + "xmit err: failed to submit urb %d\n", + ret); + up->netdev->stats.tx_dropped++; + } + return NETDEV_TX_OK; + } + + netif_trans_update(netdev); + + /* 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++; + + return NETDEV_TX_OK; +} + +/* Device goes down + * + * Clean up used resources + */ +static int ucan_close(struct net_device *netdev) +{ + int ret; + struct ucan_priv *up = netdev_priv(netdev); + + up->can.state = CAN_STATE_STOPPED; + + /* stop sending data */ + usb_kill_anchored_urbs(&up->tx_urbs); + + /* 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) + netdev_err(up->netdev, + "could not stop device, code: %d\n", + ret); + + ret = ucan_ctrl_command_out(up, UCAN_COMMAND_RESET, 0, 0); + if (ret < 0) + netdev_err(up->netdev, + "could not reset device, code: %d\n", + ret); + + netif_stop_queue(netdev); + + ucan_release_context_array(up); + + 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_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; +} + +/* 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) +{ + 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 */ +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_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 */ + iface_desc = intf->cur_altsetting; + if (!iface_desc) + return -ENODEV; + + dev_info(&udev->dev, + "%s: probing device on interface #%d\n", + UCAN_DRIVER_NAME, + iface_desc->desc.bInterfaceNumber); + + /* 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; + } + + /* 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; + + if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) != 0) && + ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_BULK)) { + /* In Endpoint */ + 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 */ + out_ep_addr = ep->bEndpointAddress; + out_ep_addr &= USB_ENDPOINT_NUMBER_MASK; + out_ep_size = le16_to_cpu(ep->wMaxPacketSize); + } + } + + /* 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 (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 (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; + } + + /* 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; + } + + /* 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 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_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); + + /* initialize 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); + + /* 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(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 { + strcpy(firmware_str, "unknown"); + } + + /* device is compatible, reset it */ + ret = ucan_ctrl_command_out(up, UCAN_COMMAND_RESET, 0, 0); + if (ret < 0) + goto err_free_candev; + + 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; + + /* 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); + 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 ucan_priv *up = usb_get_intfdata(intf); + + usb_set_intfdata(intf, NULL); + + if (up) { + unregister_netdev(up->netdev); + free_candev(up->netdev); + } +} + +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_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"); diff --git a/drivers/net/can/usb/usb_8dev.c b/drivers/net/can/usb/usb_8dev.c new file mode 100644 index 000000000..885c54c6f --- /dev/null +++ b/drivers/net/can/usb/usb_8dev.c @@ -0,0 +1,1026 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * CAN driver for "8 devices" USB2CAN converter + * + * Copyright (C) 2012 Bernd Krumboeck (krumboeck@universalnet.at) + * + * This driver is inspired by the 3.2.0 version of drivers/net/can/usb/ems_usb.c + * and drivers/net/can/usb/esd_usb2.c + * + * Many thanks to Gerhard Bertelsmann (info@gerhard-bertelsmann.de) + * for testing and fixing this driver. Also many thanks to "8 devices", + * who were very cooperative and answered my questions. + */ + +#include <linux/signal.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/usb.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> +#include <linux/can/led.h> + +/* driver constants */ +#define MAX_RX_URBS 20 +#define MAX_TX_URBS 20 +#define RX_BUFFER_SIZE 64 + +/* vendor and product id */ +#define USB_8DEV_VENDOR_ID 0x0483 +#define USB_8DEV_PRODUCT_ID 0x1234 + +/* endpoints */ +enum usb_8dev_endpoint { + USB_8DEV_ENDP_DATA_RX = 1, + USB_8DEV_ENDP_DATA_TX, + USB_8DEV_ENDP_CMD_RX, + USB_8DEV_ENDP_CMD_TX +}; + +/* device CAN clock */ +#define USB_8DEV_ABP_CLOCK 32000000 + +/* setup flags */ +#define USB_8DEV_SILENT 0x01 +#define USB_8DEV_LOOPBACK 0x02 +#define USB_8DEV_DISABLE_AUTO_RESTRANS 0x04 +#define USB_8DEV_STATUS_FRAME 0x08 + +/* commands */ +enum usb_8dev_cmd { + USB_8DEV_RESET = 1, + USB_8DEV_OPEN, + USB_8DEV_CLOSE, + USB_8DEV_SET_SPEED, + USB_8DEV_SET_MASK_FILTER, + USB_8DEV_GET_STATUS, + USB_8DEV_GET_STATISTICS, + USB_8DEV_GET_SERIAL, + USB_8DEV_GET_SOFTW_VER, + USB_8DEV_GET_HARDW_VER, + USB_8DEV_RESET_TIMESTAMP, + USB_8DEV_GET_SOFTW_HARDW_VER +}; + +/* command options */ +#define USB_8DEV_BAUD_MANUAL 0x09 +#define USB_8DEV_CMD_START 0x11 +#define USB_8DEV_CMD_END 0x22 + +#define USB_8DEV_CMD_SUCCESS 0 +#define USB_8DEV_CMD_ERROR 255 + +#define USB_8DEV_CMD_TIMEOUT 1000 + +/* frames */ +#define USB_8DEV_DATA_START 0x55 +#define USB_8DEV_DATA_END 0xAA + +#define USB_8DEV_TYPE_CAN_FRAME 0 +#define USB_8DEV_TYPE_ERROR_FRAME 3 + +#define USB_8DEV_EXTID 0x01 +#define USB_8DEV_RTR 0x02 +#define USB_8DEV_ERR_FLAG 0x04 + +/* status */ +#define USB_8DEV_STATUSMSG_OK 0x00 /* Normal condition. */ +#define USB_8DEV_STATUSMSG_OVERRUN 0x01 /* Overrun occurred when sending */ +#define USB_8DEV_STATUSMSG_BUSLIGHT 0x02 /* Error counter has reached 96 */ +#define USB_8DEV_STATUSMSG_BUSHEAVY 0x03 /* Error count. has reached 128 */ +#define USB_8DEV_STATUSMSG_BUSOFF 0x04 /* Device is in BUSOFF */ +#define USB_8DEV_STATUSMSG_STUFF 0x20 /* Stuff Error */ +#define USB_8DEV_STATUSMSG_FORM 0x21 /* Form Error */ +#define USB_8DEV_STATUSMSG_ACK 0x23 /* Ack Error */ +#define USB_8DEV_STATUSMSG_BIT0 0x24 /* Bit1 Error */ +#define USB_8DEV_STATUSMSG_BIT1 0x25 /* Bit0 Error */ +#define USB_8DEV_STATUSMSG_CRC 0x27 /* CRC Error */ + +#define USB_8DEV_RP_MASK 0x7F /* Mask for Receive Error Bit */ + + +/* table of devices that work with this driver */ +static const struct usb_device_id usb_8dev_table[] = { + { USB_DEVICE(USB_8DEV_VENDOR_ID, USB_8DEV_PRODUCT_ID) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, usb_8dev_table); + +struct usb_8dev_tx_urb_context { + struct usb_8dev_priv *priv; + + u32 echo_index; + u8 dlc; +}; + +/* Structure to hold all of our device specific stuff */ +struct usb_8dev_priv { + struct can_priv can; /* must be the first member */ + + struct sk_buff *echo_skb[MAX_TX_URBS]; + + struct usb_device *udev; + struct net_device *netdev; + + atomic_t active_tx_urbs; + struct usb_anchor tx_submitted; + struct usb_8dev_tx_urb_context tx_contexts[MAX_TX_URBS]; + + struct usb_anchor rx_submitted; + + struct can_berr_counter bec; + + u8 *cmd_msg_buffer; + + struct mutex usb_8dev_cmd_lock; + void *rxbuf[MAX_RX_URBS]; + dma_addr_t rxbuf_dma[MAX_RX_URBS]; +}; + +/* tx frame */ +struct __packed usb_8dev_tx_msg { + u8 begin; + u8 flags; /* RTR and EXT_ID flag */ + __be32 id; /* upper 3 bits not used */ + u8 dlc; /* data length code 0-8 bytes */ + u8 data[8]; /* 64-bit data */ + u8 end; +}; + +/* rx frame */ +struct __packed usb_8dev_rx_msg { + u8 begin; + u8 type; /* frame type */ + u8 flags; /* RTR and EXT_ID flag */ + __be32 id; /* upper 3 bits not used */ + u8 dlc; /* data length code 0-8 bytes */ + u8 data[8]; /* 64-bit data */ + __be32 timestamp; /* 32-bit timestamp */ + u8 end; +}; + +/* command frame */ +struct __packed usb_8dev_cmd_msg { + u8 begin; + u8 channel; /* unknown - always 0 */ + u8 command; /* command to execute */ + u8 opt1; /* optional parameter / return value */ + u8 opt2; /* optional parameter 2 */ + u8 data[10]; /* optional parameter and data */ + u8 end; +}; + +static int usb_8dev_send_cmd_msg(struct usb_8dev_priv *priv, u8 *msg, int size) +{ + int actual_length; + + return usb_bulk_msg(priv->udev, + usb_sndbulkpipe(priv->udev, USB_8DEV_ENDP_CMD_TX), + msg, size, &actual_length, USB_8DEV_CMD_TIMEOUT); +} + +static int usb_8dev_wait_cmd_msg(struct usb_8dev_priv *priv, u8 *msg, int size, + int *actual_length) +{ + return usb_bulk_msg(priv->udev, + usb_rcvbulkpipe(priv->udev, USB_8DEV_ENDP_CMD_RX), + msg, size, actual_length, USB_8DEV_CMD_TIMEOUT); +} + +/* Send command to device and receive result. + * Command was successful when opt1 = 0. + */ +static int usb_8dev_send_cmd(struct usb_8dev_priv *priv, + struct usb_8dev_cmd_msg *out, + struct usb_8dev_cmd_msg *in) +{ + int err; + int num_bytes_read; + struct net_device *netdev; + + netdev = priv->netdev; + + out->begin = USB_8DEV_CMD_START; + out->end = USB_8DEV_CMD_END; + + mutex_lock(&priv->usb_8dev_cmd_lock); + + memcpy(priv->cmd_msg_buffer, out, + sizeof(struct usb_8dev_cmd_msg)); + + err = usb_8dev_send_cmd_msg(priv, priv->cmd_msg_buffer, + sizeof(struct usb_8dev_cmd_msg)); + if (err < 0) { + netdev_err(netdev, "sending command message failed\n"); + goto failed; + } + + err = usb_8dev_wait_cmd_msg(priv, priv->cmd_msg_buffer, + sizeof(struct usb_8dev_cmd_msg), + &num_bytes_read); + if (err < 0) { + netdev_err(netdev, "no command message answer\n"); + goto failed; + } + + memcpy(in, priv->cmd_msg_buffer, sizeof(struct usb_8dev_cmd_msg)); + + if (in->begin != USB_8DEV_CMD_START || in->end != USB_8DEV_CMD_END || + num_bytes_read != 16 || in->opt1 != 0) + err = -EPROTO; + +failed: + mutex_unlock(&priv->usb_8dev_cmd_lock); + return err; +} + +/* Send open command to device */ +static int usb_8dev_cmd_open(struct usb_8dev_priv *priv) +{ + struct can_bittiming *bt = &priv->can.bittiming; + struct usb_8dev_cmd_msg outmsg; + struct usb_8dev_cmd_msg inmsg; + u32 ctrlmode = priv->can.ctrlmode; + u32 flags = USB_8DEV_STATUS_FRAME; + __be32 beflags; + __be16 bebrp; + + memset(&outmsg, 0, sizeof(outmsg)); + outmsg.command = USB_8DEV_OPEN; + outmsg.opt1 = USB_8DEV_BAUD_MANUAL; + outmsg.data[0] = bt->prop_seg + bt->phase_seg1; + outmsg.data[1] = bt->phase_seg2; + outmsg.data[2] = bt->sjw; + + /* BRP */ + bebrp = cpu_to_be16((u16)bt->brp); + memcpy(&outmsg.data[3], &bebrp, sizeof(bebrp)); + + /* flags */ + if (ctrlmode & CAN_CTRLMODE_LOOPBACK) + flags |= USB_8DEV_LOOPBACK; + if (ctrlmode & CAN_CTRLMODE_LISTENONLY) + flags |= USB_8DEV_SILENT; + if (ctrlmode & CAN_CTRLMODE_ONE_SHOT) + flags |= USB_8DEV_DISABLE_AUTO_RESTRANS; + + beflags = cpu_to_be32(flags); + memcpy(&outmsg.data[5], &beflags, sizeof(beflags)); + + return usb_8dev_send_cmd(priv, &outmsg, &inmsg); +} + +/* Send close command to device */ +static int usb_8dev_cmd_close(struct usb_8dev_priv *priv) +{ + struct usb_8dev_cmd_msg inmsg; + struct usb_8dev_cmd_msg outmsg = { + .channel = 0, + .command = USB_8DEV_CLOSE, + .opt1 = 0, + .opt2 = 0 + }; + + return usb_8dev_send_cmd(priv, &outmsg, &inmsg); +} + +/* Get firmware and hardware version */ +static int usb_8dev_cmd_version(struct usb_8dev_priv *priv, u32 *res) +{ + struct usb_8dev_cmd_msg inmsg; + struct usb_8dev_cmd_msg outmsg = { + .channel = 0, + .command = USB_8DEV_GET_SOFTW_HARDW_VER, + .opt1 = 0, + .opt2 = 0 + }; + + int err = usb_8dev_send_cmd(priv, &outmsg, &inmsg); + if (err) + return err; + + *res = be32_to_cpup((__be32 *)inmsg.data); + + return err; +} + +/* Set network device mode + * + * Maybe we should leave this function empty, because the device + * set mode variable with open command. + */ +static int usb_8dev_set_mode(struct net_device *netdev, enum can_mode mode) +{ + struct usb_8dev_priv *priv = netdev_priv(netdev); + int err = 0; + + switch (mode) { + case CAN_MODE_START: + err = usb_8dev_cmd_open(priv); + if (err) + netdev_warn(netdev, "couldn't start device"); + break; + + default: + return -EOPNOTSUPP; + } + + return err; +} + +/* Read error/status frames */ +static void usb_8dev_rx_err_msg(struct usb_8dev_priv *priv, + struct usb_8dev_rx_msg *msg) +{ + struct can_frame *cf; + struct sk_buff *skb; + struct net_device_stats *stats = &priv->netdev->stats; + + /* Error message: + * byte 0: Status + * byte 1: bit 7: Receive Passive + * byte 1: bit 0-6: Receive Error Counter + * byte 2: Transmit Error Counter + * byte 3: Always 0 (maybe reserved for future use) + */ + + u8 state = msg->data[0]; + u8 rxerr = msg->data[1] & USB_8DEV_RP_MASK; + u8 txerr = msg->data[2]; + int rx_errors = 0; + int tx_errors = 0; + + skb = alloc_can_err_skb(priv->netdev, &cf); + if (!skb) + return; + + switch (state) { + case USB_8DEV_STATUSMSG_OK: + priv->can.state = CAN_STATE_ERROR_ACTIVE; + cf->can_id |= CAN_ERR_PROT; + cf->data[2] = CAN_ERR_PROT_ACTIVE; + break; + case USB_8DEV_STATUSMSG_BUSOFF: + priv->can.state = CAN_STATE_BUS_OFF; + cf->can_id |= CAN_ERR_BUSOFF; + priv->can.can_stats.bus_off++; + can_bus_off(priv->netdev); + break; + case USB_8DEV_STATUSMSG_OVERRUN: + case USB_8DEV_STATUSMSG_BUSLIGHT: + case USB_8DEV_STATUSMSG_BUSHEAVY: + cf->can_id |= CAN_ERR_CRTL; + break; + default: + priv->can.state = CAN_STATE_ERROR_WARNING; + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + priv->can.can_stats.bus_error++; + break; + } + + switch (state) { + case USB_8DEV_STATUSMSG_OK: + case USB_8DEV_STATUSMSG_BUSOFF: + break; + case USB_8DEV_STATUSMSG_ACK: + cf->can_id |= CAN_ERR_ACK; + tx_errors = 1; + break; + case USB_8DEV_STATUSMSG_CRC: + cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ; + rx_errors = 1; + break; + case USB_8DEV_STATUSMSG_BIT0: + cf->data[2] |= CAN_ERR_PROT_BIT0; + tx_errors = 1; + break; + case USB_8DEV_STATUSMSG_BIT1: + cf->data[2] |= CAN_ERR_PROT_BIT1; + tx_errors = 1; + break; + case USB_8DEV_STATUSMSG_FORM: + cf->data[2] |= CAN_ERR_PROT_FORM; + rx_errors = 1; + break; + case USB_8DEV_STATUSMSG_STUFF: + cf->data[2] |= CAN_ERR_PROT_STUFF; + rx_errors = 1; + break; + case USB_8DEV_STATUSMSG_OVERRUN: + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + stats->rx_over_errors++; + rx_errors = 1; + break; + case USB_8DEV_STATUSMSG_BUSLIGHT: + priv->can.state = CAN_STATE_ERROR_WARNING; + cf->data[1] = (txerr > rxerr) ? + CAN_ERR_CRTL_TX_WARNING : + CAN_ERR_CRTL_RX_WARNING; + priv->can.can_stats.error_warning++; + break; + case USB_8DEV_STATUSMSG_BUSHEAVY: + priv->can.state = CAN_STATE_ERROR_PASSIVE; + cf->data[1] = (txerr > rxerr) ? + CAN_ERR_CRTL_TX_PASSIVE : + CAN_ERR_CRTL_RX_PASSIVE; + priv->can.can_stats.error_passive++; + break; + default: + netdev_warn(priv->netdev, + "Unknown status/error message (%d)\n", state); + break; + } + + if (tx_errors) { + cf->data[2] |= CAN_ERR_PROT_TX; + stats->tx_errors++; + } + + if (rx_errors) + stats->rx_errors++; + if (priv->can.state != CAN_STATE_BUS_OFF) { + cf->data[6] = txerr; + cf->data[7] = rxerr; + } + + priv->bec.txerr = txerr; + priv->bec.rxerr = rxerr; + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); +} + +/* Read data and status frames */ +static void usb_8dev_rx_can_msg(struct usb_8dev_priv *priv, + struct usb_8dev_rx_msg *msg) +{ + struct can_frame *cf; + struct sk_buff *skb; + struct net_device_stats *stats = &priv->netdev->stats; + + if (msg->type == USB_8DEV_TYPE_ERROR_FRAME && + msg->flags == USB_8DEV_ERR_FLAG) { + usb_8dev_rx_err_msg(priv, msg); + } else if (msg->type == USB_8DEV_TYPE_CAN_FRAME) { + skb = alloc_can_skb(priv->netdev, &cf); + if (!skb) + return; + + cf->can_id = be32_to_cpu(msg->id); + cf->can_dlc = get_can_dlc(msg->dlc & 0xF); + + if (msg->flags & USB_8DEV_EXTID) + cf->can_id |= CAN_EFF_FLAG; + + if (msg->flags & USB_8DEV_RTR) + cf->can_id |= CAN_RTR_FLAG; + else + memcpy(cf->data, msg->data, cf->can_dlc); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + + can_led_event(priv->netdev, CAN_LED_EVENT_RX); + } else { + netdev_warn(priv->netdev, "frame type %d unknown", + msg->type); + } + +} + +/* Callback for reading data from device + * + * Check urb status, call read function and resubmit urb read operation. + */ +static void usb_8dev_read_bulk_callback(struct urb *urb) +{ + struct usb_8dev_priv *priv = urb->context; + struct net_device *netdev; + int retval; + int pos = 0; + + netdev = priv->netdev; + + if (!netif_device_present(netdev)) + return; + + switch (urb->status) { + case 0: /* success */ + break; + + case -ENOENT: + case -EPIPE: + case -EPROTO: + case -ESHUTDOWN: + return; + + default: + netdev_info(netdev, "Rx URB aborted (%d)\n", + urb->status); + goto resubmit_urb; + } + + while (pos < urb->actual_length) { + struct usb_8dev_rx_msg *msg; + + if (pos + sizeof(struct usb_8dev_rx_msg) > urb->actual_length) { + netdev_err(priv->netdev, "format error\n"); + break; + } + + msg = (struct usb_8dev_rx_msg *)(urb->transfer_buffer + pos); + usb_8dev_rx_can_msg(priv, msg); + + pos += sizeof(struct usb_8dev_rx_msg); + } + +resubmit_urb: + usb_fill_bulk_urb(urb, priv->udev, + usb_rcvbulkpipe(priv->udev, USB_8DEV_ENDP_DATA_RX), + urb->transfer_buffer, RX_BUFFER_SIZE, + usb_8dev_read_bulk_callback, priv); + + retval = usb_submit_urb(urb, GFP_ATOMIC); + + if (retval == -ENODEV) + netif_device_detach(netdev); + else if (retval) + netdev_err(netdev, + "failed resubmitting read bulk urb: %d\n", retval); +} + +/* Callback handler for write operations + * + * Free allocated buffers, check transmit status and + * calculate statistic. + */ +static void usb_8dev_write_bulk_callback(struct urb *urb) +{ + struct usb_8dev_tx_urb_context *context = urb->context; + struct usb_8dev_priv *priv; + struct net_device *netdev; + + BUG_ON(!context); + + priv = context->priv; + netdev = priv->netdev; + + /* free up our allocated buffer */ + usb_free_coherent(urb->dev, urb->transfer_buffer_length, + urb->transfer_buffer, urb->transfer_dma); + + atomic_dec(&priv->active_tx_urbs); + + if (!netif_device_present(netdev)) + return; + + if (urb->status) + netdev_info(netdev, "Tx URB aborted (%d)\n", + urb->status); + + netdev->stats.tx_packets++; + netdev->stats.tx_bytes += context->dlc; + + can_get_echo_skb(netdev, context->echo_index); + + can_led_event(netdev, CAN_LED_EVENT_TX); + + /* Release context */ + context->echo_index = MAX_TX_URBS; + + netif_wake_queue(netdev); +} + +/* Send data to device */ +static netdev_tx_t usb_8dev_start_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + struct usb_8dev_priv *priv = netdev_priv(netdev); + struct net_device_stats *stats = &netdev->stats; + struct can_frame *cf = (struct can_frame *) skb->data; + struct usb_8dev_tx_msg *msg; + struct urb *urb; + struct usb_8dev_tx_urb_context *context = NULL; + u8 *buf; + int i, err; + size_t size = sizeof(struct usb_8dev_tx_msg); + + 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) + goto nomem; + + buf = usb_alloc_coherent(priv->udev, size, GFP_ATOMIC, + &urb->transfer_dma); + if (!buf) { + netdev_err(netdev, "No memory left for USB buffer\n"); + goto nomembuf; + } + + memset(buf, 0, size); + + msg = (struct usb_8dev_tx_msg *)buf; + msg->begin = USB_8DEV_DATA_START; + msg->flags = 0x00; + + if (cf->can_id & CAN_RTR_FLAG) + msg->flags |= USB_8DEV_RTR; + + if (cf->can_id & CAN_EFF_FLAG) + msg->flags |= USB_8DEV_EXTID; + + msg->id = cpu_to_be32(cf->can_id & CAN_ERR_MASK); + msg->dlc = cf->can_dlc; + memcpy(msg->data, cf->data, cf->can_dlc); + msg->end = USB_8DEV_DATA_END; + + for (i = 0; i < MAX_TX_URBS; i++) { + if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) { + context = &priv->tx_contexts[i]; + break; + } + } + + /* May never happen! When this happens we'd more URBs in flight as + * allowed (MAX_TX_URBS). + */ + if (!context) + goto nofreecontext; + + context->priv = priv; + context->echo_index = i; + context->dlc = cf->can_dlc; + + usb_fill_bulk_urb(urb, priv->udev, + usb_sndbulkpipe(priv->udev, USB_8DEV_ENDP_DATA_TX), + buf, size, usb_8dev_write_bulk_callback, context); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(urb, &priv->tx_submitted); + + can_put_echo_skb(skb, netdev, context->echo_index); + + atomic_inc(&priv->active_tx_urbs); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (unlikely(err)) { + can_free_echo_skb(netdev, context->echo_index); + + usb_unanchor_urb(urb); + usb_free_coherent(priv->udev, size, buf, urb->transfer_dma); + + atomic_dec(&priv->active_tx_urbs); + + if (err == -ENODEV) + netif_device_detach(netdev); + else + netdev_warn(netdev, "failed tx_urb %d\n", err); + stats->tx_dropped++; + } else if (atomic_read(&priv->active_tx_urbs) >= MAX_TX_URBS) + /* Slow down tx path */ + netif_stop_queue(netdev); + + /* Release our reference to this URB, the USB core will eventually free + * it entirely. + */ + usb_free_urb(urb); + + return NETDEV_TX_OK; + +nofreecontext: + usb_free_coherent(priv->udev, size, buf, urb->transfer_dma); + usb_free_urb(urb); + + netdev_warn(netdev, "couldn't find free context"); + + return NETDEV_TX_BUSY; + +nomembuf: + usb_free_urb(urb); + +nomem: + dev_kfree_skb(skb); + stats->tx_dropped++; + + return NETDEV_TX_OK; +} + +static int usb_8dev_get_berr_counter(const struct net_device *netdev, + struct can_berr_counter *bec) +{ + struct usb_8dev_priv *priv = netdev_priv(netdev); + + bec->txerr = priv->bec.txerr; + bec->rxerr = priv->bec.rxerr; + + return 0; +} + +/* Start USB device */ +static int usb_8dev_start(struct usb_8dev_priv *priv) +{ + struct net_device *netdev = priv->netdev; + int err, i; + + for (i = 0; i < MAX_RX_URBS; i++) { + struct urb *urb = NULL; + u8 *buf; + dma_addr_t buf_dma; + + /* create a URB, and a buffer for it */ + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + err = -ENOMEM; + break; + } + + buf = usb_alloc_coherent(priv->udev, RX_BUFFER_SIZE, GFP_KERNEL, + &buf_dma); + if (!buf) { + netdev_err(netdev, "No memory left for USB buffer\n"); + usb_free_urb(urb); + err = -ENOMEM; + break; + } + + urb->transfer_dma = buf_dma; + + usb_fill_bulk_urb(urb, priv->udev, + usb_rcvbulkpipe(priv->udev, + USB_8DEV_ENDP_DATA_RX), + buf, RX_BUFFER_SIZE, + usb_8dev_read_bulk_callback, priv); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(urb, &priv->rx_submitted); + + err = usb_submit_urb(urb, GFP_KERNEL); + if (err) { + usb_unanchor_urb(urb); + usb_free_coherent(priv->udev, RX_BUFFER_SIZE, buf, + urb->transfer_dma); + usb_free_urb(urb); + break; + } + + priv->rxbuf[i] = buf; + priv->rxbuf_dma[i] = buf_dma; + + /* Drop reference, USB core will take care of freeing it */ + usb_free_urb(urb); + } + + /* Did we submit any URBs */ + if (i == 0) { + netdev_warn(netdev, "couldn't setup read URBs\n"); + return err; + } + + /* Warn if we've couldn't transmit all the URBs */ + if (i < MAX_RX_URBS) + netdev_warn(netdev, "rx performance may be slow\n"); + + err = usb_8dev_cmd_open(priv); + if (err) + goto failed; + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + return 0; + +failed: + if (err == -ENODEV) + netif_device_detach(priv->netdev); + + netdev_warn(netdev, "couldn't submit control: %d\n", err); + + return err; +} + +/* Open USB device */ +static int usb_8dev_open(struct net_device *netdev) +{ + struct usb_8dev_priv *priv = netdev_priv(netdev); + int err; + + /* common open */ + err = open_candev(netdev); + if (err) + return err; + + can_led_event(netdev, CAN_LED_EVENT_OPEN); + + /* finally start device */ + err = usb_8dev_start(priv); + if (err) { + if (err == -ENODEV) + netif_device_detach(priv->netdev); + + netdev_warn(netdev, "couldn't start device: %d\n", + err); + + close_candev(netdev); + + return err; + } + + netif_start_queue(netdev); + + return 0; +} + +static void unlink_all_urbs(struct usb_8dev_priv *priv) +{ + int i; + + usb_kill_anchored_urbs(&priv->rx_submitted); + + for (i = 0; i < MAX_RX_URBS; ++i) + usb_free_coherent(priv->udev, RX_BUFFER_SIZE, + priv->rxbuf[i], priv->rxbuf_dma[i]); + + usb_kill_anchored_urbs(&priv->tx_submitted); + atomic_set(&priv->active_tx_urbs, 0); + + for (i = 0; i < MAX_TX_URBS; i++) + priv->tx_contexts[i].echo_index = MAX_TX_URBS; +} + +/* Close USB device */ +static int usb_8dev_close(struct net_device *netdev) +{ + struct usb_8dev_priv *priv = netdev_priv(netdev); + int err = 0; + + /* Send CLOSE command to CAN controller */ + err = usb_8dev_cmd_close(priv); + if (err) + netdev_warn(netdev, "couldn't stop device"); + + priv->can.state = CAN_STATE_STOPPED; + + netif_stop_queue(netdev); + + /* Stop polling */ + unlink_all_urbs(priv); + + close_candev(netdev); + + can_led_event(netdev, CAN_LED_EVENT_STOP); + + return err; +} + +static const struct net_device_ops usb_8dev_netdev_ops = { + .ndo_open = usb_8dev_open, + .ndo_stop = usb_8dev_close, + .ndo_start_xmit = usb_8dev_start_xmit, + .ndo_change_mtu = can_change_mtu, +}; + +static const struct can_bittiming_const usb_8dev_bittiming_const = { + .name = "usb_8dev", + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 1024, + .brp_inc = 1, +}; + +/* Probe USB device + * + * Check device and firmware. + * Set supported modes and bittiming constants. + * Allocate some memory. + */ +static int usb_8dev_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct net_device *netdev; + struct usb_8dev_priv *priv; + int i, err = -ENOMEM; + u32 version; + char buf[18]; + struct usb_device *usbdev = interface_to_usbdev(intf); + + /* product id looks strange, better we also check iProduct string */ + if (usb_string(usbdev, usbdev->descriptor.iProduct, buf, + sizeof(buf)) > 0 && strcmp(buf, "USB2CAN converter")) { + dev_info(&usbdev->dev, "ignoring: not an USB2CAN converter\n"); + return -ENODEV; + } + + netdev = alloc_candev(sizeof(struct usb_8dev_priv), MAX_TX_URBS); + if (!netdev) { + dev_err(&intf->dev, "Couldn't alloc candev\n"); + return -ENOMEM; + } + + priv = netdev_priv(netdev); + + priv->udev = usbdev; + priv->netdev = netdev; + + priv->can.state = CAN_STATE_STOPPED; + priv->can.clock.freq = USB_8DEV_ABP_CLOCK; + priv->can.bittiming_const = &usb_8dev_bittiming_const; + priv->can.do_set_mode = usb_8dev_set_mode; + priv->can.do_get_berr_counter = usb_8dev_get_berr_counter; + priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | + CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_ONE_SHOT; + + netdev->netdev_ops = &usb_8dev_netdev_ops; + + netdev->flags |= IFF_ECHO; /* we support local echo */ + + init_usb_anchor(&priv->rx_submitted); + + init_usb_anchor(&priv->tx_submitted); + atomic_set(&priv->active_tx_urbs, 0); + + for (i = 0; i < MAX_TX_URBS; i++) + priv->tx_contexts[i].echo_index = MAX_TX_URBS; + + priv->cmd_msg_buffer = devm_kzalloc(&intf->dev, sizeof(struct usb_8dev_cmd_msg), + GFP_KERNEL); + if (!priv->cmd_msg_buffer) + goto cleanup_candev; + + usb_set_intfdata(intf, priv); + + SET_NETDEV_DEV(netdev, &intf->dev); + + mutex_init(&priv->usb_8dev_cmd_lock); + + err = register_candev(netdev); + if (err) { + netdev_err(netdev, + "couldn't register CAN device: %d\n", err); + goto cleanup_candev; + } + + err = usb_8dev_cmd_version(priv, &version); + if (err) { + netdev_err(netdev, "can't get firmware version\n"); + goto cleanup_unregister_candev; + } else { + netdev_info(netdev, + "firmware: %d.%d, hardware: %d.%d\n", + (version>>24) & 0xff, (version>>16) & 0xff, + (version>>8) & 0xff, version & 0xff); + } + + devm_can_led_init(netdev); + + return 0; + +cleanup_unregister_candev: + unregister_netdev(priv->netdev); + +cleanup_candev: + free_candev(netdev); + + return err; + +} + +/* Called by the usb core when driver is unloaded or device is removed */ +static void usb_8dev_disconnect(struct usb_interface *intf) +{ + struct usb_8dev_priv *priv = usb_get_intfdata(intf); + + usb_set_intfdata(intf, NULL); + + if (priv) { + netdev_info(priv->netdev, "device disconnected\n"); + + unregister_netdev(priv->netdev); + unlink_all_urbs(priv); + free_candev(priv->netdev); + } + +} + +static struct usb_driver usb_8dev_driver = { + .name = "usb_8dev", + .probe = usb_8dev_probe, + .disconnect = usb_8dev_disconnect, + .id_table = usb_8dev_table, +}; + +module_usb_driver(usb_8dev_driver); + +MODULE_AUTHOR("Bernd Krumboeck <krumboeck@universalnet.at>"); +MODULE_DESCRIPTION("CAN driver for 8 devices USB2CAN interfaces"); +MODULE_LICENSE("GPL v2"); |