* Re: [PATCH] can: kvaser_usb: Add support for Kvaser CAN/USB devices
2012-07-30 5:32 [PATCH] can: kvaser_usb: Add support for Kvaser CAN/USB devices Olivier Sobrie
@ 2012-07-30 11:11 ` Marc Kleine-Budde
2012-07-30 13:33 ` Olivier Sobrie
2012-08-06 5:21 ` [PATCH v2] " Olivier Sobrie
` (4 subsequent siblings)
5 siblings, 1 reply; 43+ messages in thread
From: Marc Kleine-Budde @ 2012-07-30 11:11 UTC (permalink / raw)
To: Olivier Sobrie; +Cc: Wolfgang Grandegger, linux-can, netdev
[-- Attachment #1: Type: text/plain, Size: 43005 bytes --]
On 07/30/2012 07:32 AM, Olivier Sobrie wrote:
> This driver provides support for several Kvaser CAN/USB devices.
> Such kind of devices supports up to three can network interfaces.
>
> It has been tested with a Kvaser USB Leaf Light (one network interface)
> connected to a pch_can interface.
> The firmware version of the Kvaser device was 2.5.205.
Please add linux-usb@vger.kernel.org to Cc for review of the USB part.
Please combine .h and .c file. Please make use of netdev_LEVEL() for
error printing, not dev_LEVEL().
Please review if all members of the struct kvaser_msg are properly
aligned. You never access the struct kvaser_msg_* members directly, as
they are unaligned. Please check for le16 and le32 access. You missed to
convert the bitrate.
Please check if your driver survives hot-unplugging while sending and
receiving CAN frames at maximum laod.
More comments inline,
regards, Marc
> List of Kvaser devices supported by the driver:
> - Kvaser Leaf prototype (P010v2 and v3)
> - Kvaser Leaf Light (P010v3)
> - 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, Prototype
> - 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 OEM Mercury
> - Kvaser OEM Leaf
> - Kvaser USBcan R
>
> Signed-off-by: Olivier Sobrie <olivier@sobrie.be>
> ---
> drivers/net/can/usb/Kconfig | 33 ++
> drivers/net/can/usb/Makefile | 1 +
> drivers/net/can/usb/kvaser_usb.c | 1062 ++++++++++++++++++++++++++++++++++++++
> drivers/net/can/usb/kvaser_usb.h | 237 +++++++++
> 4 files changed, 1333 insertions(+)
> create mode 100644 drivers/net/can/usb/kvaser_usb.c
> create mode 100644 drivers/net/can/usb/kvaser_usb.h
>
> diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig
> index 0a68768..578955f 100644
> --- a/drivers/net/can/usb/Kconfig
> +++ b/drivers/net/can/usb/Kconfig
> @@ -13,6 +13,39 @@ config CAN_ESD_USB2
> This driver supports the CAN-USB/2 interface
> from esd electronic system design gmbh (http://www.esd.eu).
>
> +config CAN_KVASER_USB
> + tristate "Kvaser CAN/USB interface"
> + ---help---
> + This driver adds support for Kvaser CAN/USB devices like Kvaser
> + Leaf Light.
> +
> + The driver gives support for the following devices:
> + - Kvaser Leaf prototype (P010v2 and v3)
> + - Kvaser Leaf Light (P010v3)
> + - 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, Prototype
> + - 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 OEM Mercury
> + - Kvaser OEM Leaf
> + - Kvaser USBcan R
> +
> + If unsure, say N.
> +
> + To compile this driver as a module, choose M here: the
> + module will be called kvaser_usb.
> +
> config CAN_PEAK_USB
> tristate "PEAK PCAN-USB/USB Pro interfaces"
> ---help---
> diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile
> index da6d1d3..80a2ee4 100644
> --- a/drivers/net/can/usb/Makefile
> +++ b/drivers/net/can/usb/Makefile
> @@ -4,6 +4,7 @@
>
> obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o
> obj-$(CONFIG_CAN_ESD_USB2) += esd_usb2.o
> +obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o
> obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/
>
> ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
> diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c
> new file mode 100644
> index 0000000..4965480
> --- /dev/null
> +++ b/drivers/net/can/usb/kvaser_usb.c
> @@ -0,0 +1,1062 @@
> +/*
Please add a license statement and probably your copyright:
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
You also should copy the copyright from the drivers you used:
> + * Parts of this driver are based on the following:
> + * - Kvaser linux leaf driver (version 4.78)
I just downloaded their driver and noticed that it's quite sparse on
stating the license the code is released under.
"doc/HTMLhelp/copyright.htx" is quite restrictive, the word GPL occurs 3
times, all in MODULE_LICENSE("GPL"). Running modinfo on the usbcan.ko
shows "license: GPL"
> + * - CAN driver for esd CAN-USB/2
> + */
> +
> +#include <linux/init.h>
> +#include <linux/completion.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 "kvaser_usb.h"
> +
> +struct kvaser_usb_tx_urb_context {
> + struct kvaser_usb_net_priv *priv;
Huh - how does this work without forward declaration?
> + u32 echo_index;
> + int dlc;
> +};
> +
> +struct kvaser_usb {
> + struct usb_device *udev;
> + struct kvaser_usb_net_priv *nets[MAX_NET_DEVICES];
> +
> + struct usb_anchor rx_submitted;
> +
> + u32 fw_version;
> + unsigned int nchannels;
> +
> + bool rxinitdone;
> +};
> +
> +struct kvaser_usb_net_priv {
> + struct can_priv can;
> +
> + atomic_t active_tx_urbs;
> + struct usb_anchor tx_submitted;
> + struct kvaser_usb_tx_urb_context tx_contexts[MAX_TX_URBS];
> +
> + int open_time;
please remove open_time
> + struct completion start_stop_comp;
> +
> + struct kvaser_usb *dev;
> + struct net_device *netdev;
> + int channel;
> + struct can_berr_counter bec;
> +};
> +
> +static struct usb_device_id kvaser_usb_table[] = {
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_DEVEL_PRODUCT_ID) },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_PRODUCT_ID) },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_PRODUCT_ID),
> + .driver_info = KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_PRODUCT_ID),
> + .driver_info = KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LS_PRODUCT_ID),
> + .driver_info = KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_SWC_PRODUCT_ID),
> + .driver_info = KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LIN_PRODUCT_ID),
> + .driver_info = KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_LS_PRODUCT_ID),
> + .driver_info = KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_SWC_PRODUCT_ID),
> + .driver_info = KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_DEVEL_PRODUCT_ID),
> + .driver_info = KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSHS_PRODUCT_ID),
> + .driver_info = KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_UPRO_HSHS_PRODUCT_ID) },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_GI_PRODUCT_ID) },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_OBDII_PRODUCT_ID),
> + .driver_info = KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSLS_PRODUCT_ID) },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_CH_PRODUCT_ID) },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_BLACKBIRD_SPRO_PRODUCT_ID) },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_MERCURY_PRODUCT_ID) },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_LEAF_PRODUCT_ID) },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_CAN_R_PRODUCT_ID) },
> + { }
> +};
> +MODULE_DEVICE_TABLE(usb, kvaser_usb_table);
> +
> +static int kvaser_usb_send_msg(const struct kvaser_usb *dev,
> + struct kvaser_msg *msg)
inline?
> +{
> + int actual_len;
> +
> + return usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 1),
^
Can you please introduce a #define for this.
> + msg, msg->len, &actual_len, USB_SEND_TIMEOUT);
> +}
> +
> +static int kvaser_usb_wait_msg(const struct kvaser_usb *dev, u8 id,
> + struct kvaser_msg *msg)
> +{
> + struct kvaser_msg *tmp;
> + void *buf;
> + int actual_len;
> + int err;
> + int pos = 0;
> +
> + buf = kzalloc(RX_BUFFER_SIZE, GFP_KERNEL);
> + if (!buf)
> + return -ENOMEM;
> +
> + err = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, 129),
^^^
dito
> + buf, RX_BUFFER_SIZE, &actual_len,
> + USB_RECEIVE_TIMEOUT);
> + if (err < 0) {
> + kfree(buf);
> + return err;
> + }
> +
> + while (pos < actual_len) {
Please check that pos + sizeof(*msg) is < actual_len, as you fill access
it later.
> + tmp = buf + pos;
> +
> + if (!tmp->len)
> + break;
> +
> + if (tmp->id == id) {
> + memcpy(msg, tmp, sizeof(*msg));
> + kfree(buf);
> + return 0;
> + }
> +
> + pos += tmp->len;
> + }
> +
> + kfree(buf);
> +
> + return -EINVAL;
> +}
> +
> +static int kvaser_usb_send_simple_msg(const struct kvaser_usb *dev,
> + u8 msg_id, int channel)
> +{
> + struct kvaser_msg msg;
> + int err;
> +
> + msg.len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple);
> + msg.id = msg_id;
> + msg.u.simple.channel = channel;
> + msg.u.simple.tid = 0xff;
Please use C99 struct initializer.
struct kvaser_msg msg = {
.len = ,
.id =,
};
> +
> + err = kvaser_usb_send_msg(dev, &msg);
just:
return err;
> + if (err)
> + return err;
> +
> + return 0;
> +}
> +
> +static int kvaser_usb_get_software_info(struct kvaser_usb *dev)
> +{
> + struct kvaser_msg msg;
> + int err;
> +
> + err = kvaser_usb_send_simple_msg(dev, CMD_GET_SOFTWARE_INFO, 0);
> + if (err)
> + return err;
> +
> + err = kvaser_usb_wait_msg(dev, CMD_GET_SOFTWARE_INFO_REPLY, &msg);
> + if (err)
> + return err;
> +
> + dev->fw_version = le32_to_cpu(msg.u.softinfo.fw_version);
> +
> + return 0;
> +}
> +
> +static int kvaser_usb_get_card_info(struct kvaser_usb *dev)
> +{
> + struct kvaser_msg msg;
> + int err;
> +
> + err = kvaser_usb_send_simple_msg(dev, CMD_GET_CARD_INFO, 0);
> + if (err)
> + return err;
> +
> + err = kvaser_usb_wait_msg(dev, CMD_GET_CARD_INFO_REPLY, &msg);
> + if (err)
> + return err;
> +
> + dev->nchannels = msg.u.cardinfo.nchannels;
> +
> + return 0;
> +}
> +
> +static void kvaser_usb_tx_acknowledge(const struct kvaser_usb *dev,
> + const struct kvaser_msg *msg)
> +{
> + struct net_device_stats *stats;
> + struct kvaser_usb_tx_urb_context *context;
> + struct kvaser_usb_net_priv *priv;
> + u8 channel = msg->u.tx_acknowledge.channel;
> + u8 tid = msg->u.tx_acknowledge.tid;
> +
> + if (channel >= dev->nchannels) {
> + dev_err(dev->udev->dev.parent,
> + "Invalid channel number (%d)\n", channel);
> + return;
> + }
can you do a check for (channel >= dev->nchannels), in a central place?
e.g. kvaser_usb_handle_message()?
> +
> + priv = dev->nets[channel];
> +
> + if (!netif_device_present(priv->netdev))
> + return;
> +
> + stats = &priv->netdev->stats;
> +
> + context = &priv->tx_contexts[tid % MAX_TX_URBS];
> +
> + /*
> + * It looks like the firmware never sets the flags field of the
> + * tx_acknowledge frame and never reports a transmit failure.
> + * If the can message can't be transmited (e.g. incompatible
> + * bitrates), a frame CMD_CAN_ERROR_EVENT is sent (with a null
> + * tid) and the firmware tries to transmit again the packet until
> + * it succeeds. Once the packet is successfully transmitted, then
> + * the tx_acknowledge frame is sent.
> + */
> +
> + stats->tx_packets++;
> + stats->tx_bytes += context->dlc;
> + can_get_echo_skb(priv->netdev, context->echo_index);
> +
> + context->echo_index = MAX_TX_URBS;
> + atomic_dec(&priv->active_tx_urbs);
> +
> + netif_wake_queue(priv->netdev);
> +}
> +
> +static void kvaser_usb_rx_error(const struct kvaser_usb *dev,
> + const struct kvaser_msg *msg)
> +{
> + struct can_frame *cf;
> + struct sk_buff *skb;
> + struct net_device_stats *stats;
> + struct kvaser_usb_net_priv *priv;
> + u8 channel, status, txerr, rxerr;
> +
> + if (msg->id == CMD_CAN_ERROR_EVENT) {
> + channel = msg->u.error_event.channel;
> + status = msg->u.error_event.status;
> + txerr = msg->u.error_event.tx_errors_count;
> + rxerr = msg->u.error_event.rx_errors_count;
> + } else {
> + channel = msg->u.chip_state_event.channel;
> + status = msg->u.chip_state_event.status;
> + txerr = msg->u.chip_state_event.tx_errors_count;
> + rxerr = msg->u.chip_state_event.rx_errors_count;
> + }
> +
> + if (channel >= dev->nchannels) {
> + dev_err(dev->udev->dev.parent,
> + "Invalid channel number (%d)\n", channel);
> + return;
> + }
> +
> + priv = dev->nets[channel];
> + stats = &priv->netdev->stats;
> +
> + skb = alloc_can_err_skb(priv->netdev, &cf);
> + if (!skb) {
> + stats->rx_dropped++;
> + return;
> + }
> +
> + if ((status & M16C_STATE_BUS_OFF) ||
> + (status & M16C_STATE_BUS_RESET)) {
> + priv->can.state = CAN_STATE_BUS_OFF;
> + cf->can_id |= CAN_ERR_BUSOFF;
> + can_bus_off(priv->netdev);
> + } else if (status & M16C_STATE_BUS_ERROR) {
> + priv->can.state = CAN_STATE_ERROR_WARNING;
> + priv->can.can_stats.error_warning++;
> + } else if (status & M16C_STATE_BUS_PASSIVE) {
> + priv->can.state = CAN_STATE_ERROR_PASSIVE;
> + priv->can.can_stats.error_passive++;
> + } else {
> + priv->can.state = CAN_STATE_ERROR_ACTIVE;
> + cf->can_id |= CAN_ERR_PROT;
> + cf->data[2] = CAN_ERR_PROT_ACTIVE;
> + }
> +
> + if (msg->id == CMD_CAN_ERROR_EVENT) {
> + u8 error_factor = msg->u.error_event.error_factor;
> +
> + priv->can.can_stats.bus_error++;
> + stats->rx_errors++;
> +
> + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
> +
> + 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;
> + }
> +
> + if (error_factor & M16C_EF_ACKE)
> + cf->data[3] |= (CAN_ERR_PROT_LOC_ACK |
> + CAN_ERR_PROT_LOC_ACK_DEL);
> + if (error_factor & M16C_EF_CRCE)
> + cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ |
> + CAN_ERR_PROT_LOC_CRC_DEL);
> + if (error_factor & M16C_EF_FORME)
> + cf->data[2] |= CAN_ERR_PROT_FORM;
> + if (error_factor & M16C_EF_STFE)
> + cf->data[2] |= CAN_ERR_PROT_STUFF;
> + if (error_factor & M16C_EF_BITE0)
> + cf->data[2] |= CAN_ERR_PROT_BIT0;
> + if (error_factor & M16C_EF_BITE1)
> + cf->data[2] |= CAN_ERR_PROT_BIT1;
> + if (error_factor & M16C_EF_TRE)
> + cf->data[2] |= CAN_ERR_PROT_TX;
> + }
> +
> + cf->data[6] = txerr;
> + cf->data[7] = rxerr;
> +
> + netif_rx(skb);
> +
> + priv->bec.txerr = txerr;
> + priv->bec.rxerr = rxerr;
> +
> + stats->rx_packets++;
> + stats->rx_bytes += cf->can_dlc;
> +}
> +
> +static void kvaser_usb_rx_can_msg(const struct kvaser_usb *dev,
> + const struct kvaser_msg *msg)
> +{
> + struct kvaser_usb_net_priv *priv;
> + struct can_frame *cf;
> + struct sk_buff *skb;
> + struct net_device_stats *stats;
> + u8 channel = msg->u.rx_can.channel;
> +
> + if (channel >= dev->nchannels) {
> + dev_err(dev->udev->dev.parent,
> + "Invalid channel number (%d)\n", channel);
> + return;
> + }
> +
> + priv = dev->nets[channel];
> + stats = &priv->netdev->stats;
> +
> + skb = alloc_can_skb(priv->netdev, &cf);
> + if (skb == NULL) {
> + stats->tx_dropped++;
> + return;
> + }
> +
> + cf->can_id = ((msg->u.rx_can.msg[0] & 0x1f) << 6) |
> + (msg->u.rx_can.msg[1] & 0x3f);
> + cf->can_dlc = get_can_dlc(msg->u.rx_can.msg[5]);
> +
> + if (msg->id == CMD_RX_EXT_MESSAGE) {
> + cf->can_id <<= 18;
> + cf->can_id += ((msg->u.rx_can.msg[2] & 0x0f) << 14) |
|=
is more appropriate here
> + ((msg->u.rx_can.msg[3] & 0xff) << 6) |
> + (msg->u.rx_can.msg[4] & 0x3f);
> + cf->can_id |= CAN_EFF_FLAG;
> + }
> +
> + if (msg->u.rx_can.flag & MSG_FLAG_REMOTE_FRAME) {
> + cf->can_id |= CAN_RTR_FLAG;
> + } else if (msg->u.rx_can.flag & (MSG_FLAG_ERROR_FRAME |
> + MSG_FLAG_NERR)) {
> + cf->can_id |= CAN_ERR_FLAG;
> + cf->can_dlc = CAN_ERR_DLC;
What kind of error is this? Can you set cf->data? What about the
original cd->can_id? What about the stats->rx_*error* stats?
> + } else if (msg->u.rx_can.flag & MSG_FLAG_OVERRUN) {
> + cf->can_id = CAN_ERR_FLAG | CAN_ERR_CRTL;
> + cf->can_dlc = CAN_ERR_DLC;
> + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
> +
> + stats->rx_over_errors++;
> + stats->rx_errors++;
> + } else if (!msg->u.rx_can.flag) {
> + memcpy(cf->data, &msg->u.rx_can.msg[6], cf->can_dlc);
> + } else {
> + kfree_skb(skb);
> + return;
> + }
> +
> + netif_rx(skb);
> +
> + stats->rx_packets++;
> + stats->rx_bytes += cf->can_dlc;
> +}
> +
> +static void kvaser_usb_start_stop_chip_reply(const struct kvaser_usb *dev,
> + const struct kvaser_msg *msg)
> +{
> + struct kvaser_usb_net_priv *priv;
> + u8 channel = msg->u.simple.channel;
> +
> + if (channel >= dev->nchannels) {
> + dev_err(dev->udev->dev.parent,
> + "Invalid channel number (%d)\n", channel);
> + return;
> + }
> +
> + priv = dev->nets[channel];
> +
> + complete(&priv->start_stop_comp);
> +}
> +
> +static void kvaser_usb_handle_message(const struct kvaser_usb *dev,
> + const struct kvaser_msg *msg)
> +{
> + switch (msg->id) {
> + case CMD_START_CHIP_REPLY:
> + case CMD_STOP_CHIP_REPLY:
> + kvaser_usb_start_stop_chip_reply(dev, msg);
> + break;
> +
> + case CMD_RX_STD_MESSAGE:
> + case CMD_RX_EXT_MESSAGE:
> + kvaser_usb_rx_can_msg(dev, msg);
> + break;
> +
> + case CMD_CHIP_STATE_EVENT:
> + case CMD_CAN_ERROR_EVENT:
> + kvaser_usb_rx_error(dev, msg);
> + break;
> +
> + case CMD_TX_ACKNOWLEDGE:
> + kvaser_usb_tx_acknowledge(dev, msg);
> + break;
> +
> + default:
> + dev_warn(dev->udev->dev.parent,
> + "Unhandled message (%d)\n", msg->id);
> + break;
> + }
> +}
> +
> +static void kvaser_usb_read_bulk_callback(struct urb *urb)
> +{
> + struct kvaser_usb *dev = urb->context;
> + struct kvaser_msg *msg;
> + int pos = 0;
> + int err, i;
> +
> + switch (urb->status) {
> + case 0:
> + break;
> + case -ENOENT:
> + 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) {
please check here for pos + sizeof(*msg), too
> + msg = urb->transfer_buffer + pos;
> +
> + if (!msg->len)
> + break;
> +
> + kvaser_usb_handle_message(dev, msg);
> +
> + if (pos > urb->actual_length) {
> + dev_err(dev->udev->dev.parent, "Format error\n");
> + break;
> + }
> +
> + pos += msg->len;
> + }
> +
> +resubmit_urb:
> + usb_fill_bulk_urb(urb, dev->udev, usb_rcvbulkpipe(dev->udev, 129),
^^^
use #define
> + urb->transfer_buffer, 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->udev->dev.parent,
> + "Failed resubmitting read bulk urb: %d\n", err);
> + }
> +
> + return;
> +}
> +
> +static int kvaser_usb_setup_rx_urbs(struct kvaser_usb *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;
> +
> + urb = usb_alloc_urb(0, GFP_KERNEL);
> + if (!urb) {
> + dev_warn(dev->udev->dev.parent,
> + "No memory left for URBs\n");
> + err = -ENOMEM;
> + break;
> + }
> +
> + buf = usb_alloc_coherent(dev->udev, RX_BUFFER_SIZE,
> + GFP_KERNEL, &urb->transfer_dma);
> + if (!buf) {
> + dev_warn(dev->udev->dev.parent,
> + "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, 129),
use #define
> + buf, RX_BUFFER_SIZE,
> + kvaser_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);
> + break;
> + }
> +
> + usb_free_urb(urb);
> + }
> +
> + if (i == 0) {
> + dev_warn(dev->udev->dev.parent,
> + "Cannot setup read URBs, error %d\n", err);
> + return err;
> + } else if (i < MAX_RX_URBS) {
> + dev_warn(dev->udev->dev.parent,
> + "RX performances may be slow\n");
> + }
> +
> + dev->rxinitdone = true;
> +
> + return 0;
> +}
> +
> +static int kvaser_usb_set_opt_mode(const struct kvaser_usb_net_priv *priv)
> +{
> + struct kvaser_msg msg;
> +
> + memset(&msg, 0x00, sizeof(msg));
> + msg.id = CMD_SET_CTRL_MODE;
> + msg.len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_ctrl_mode);
> + msg.u.ctrl_mode.tid = 0xff;
> + msg.u.ctrl_mode.channel = priv->channel;
please use C99 struct initializers
> +
> + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
> + msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_SILENT;
> + else
> + msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_NORMAL;
> +
> + return kvaser_usb_send_msg(priv->dev, &msg);
> +}
> +
> +static int kvaser_usb_start_chip(struct kvaser_usb_net_priv *priv)
> +{
> + int err;
> +
> + init_completion(&priv->start_stop_comp);
> +
> + err = kvaser_usb_send_simple_msg(priv->dev, CMD_START_CHIP,
> + priv->channel);
> + if (err)
> + return err;
> +
> + if (!wait_for_completion_timeout(&priv->start_stop_comp,
> + msecs_to_jiffies(START_TIMEOUT)))
> + return -ETIMEDOUT;
> +
> + 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;
> + int err;
> +
> + err = open_candev(netdev);
> + if (err)
> + return err;
> +
> + err = kvaser_usb_setup_rx_urbs(dev);
> + if (err)
> + return err;
> +
> + err = kvaser_usb_set_opt_mode(priv);
> + if (err)
> + return err;
> +
> + err = kvaser_usb_start_chip(priv);
> + if (err) {
> + netdev_warn(netdev, "Cannot start device, error %d\n", err);
> + close_candev(netdev);
> + return err;
> + }
> +
> + priv->can.state = CAN_STATE_ERROR_ACTIVE;
> + priv->open_time = jiffies;
> + netif_start_queue(netdev);
> +
> + return 0;
> +}
> +
> +static void kvaser_usb_unlink_tx_urbs(struct kvaser_usb_net_priv *priv)
> +{
> + int i;
> +
> + usb_kill_anchored_urbs(&priv->tx_submitted);
> + atomic_set(&priv->active_tx_urbs, 0);
> +
> + for (i = 0; i < MAX_TX_URBS; i++)
ARRAY_SIZE(priv->tx_contexts) instead of MAX_TX_URBS
> + priv->tx_contexts[i].echo_index = MAX_TX_URBS;
> +}
> +
> +static void kvaser_usb_unlink_all_urbs(struct kvaser_usb *dev)
> +{
> + int i;
> +
> + usb_kill_anchored_urbs(&dev->rx_submitted);
> +
> + for (i = 0; i < MAX_NET_DEVICES; i++) {
ARRAY_SIZE()
> + struct kvaser_usb_net_priv *priv = dev->nets[i];
> +
> + if (priv)
> + kvaser_usb_unlink_tx_urbs(priv);
> + }
> +}
> +
> +static int kvaser_usb_stop_chip(struct kvaser_usb_net_priv *priv)
> +{
> + int err;
> +
> + init_completion(&priv->start_stop_comp);
> +
> + err = kvaser_usb_send_simple_msg(priv->dev, CMD_STOP_CHIP,
> + priv->channel);
> + if (err)
> + return err;
> +
> + if (!wait_for_completion_timeout(&priv->start_stop_comp,
> + msecs_to_jiffies(STOP_TIMEOUT)))
> + return -ETIMEDOUT;
> +
> + return 0;
> +}
> +
> +static int kvaser_usb_flush_queue(struct kvaser_usb_net_priv *priv)
> +{
> + struct kvaser_msg msg;
> +
> + memset(&msg, 0x00, sizeof(msg));
> + msg.id = CMD_FLUSH_QUEUE;
> + msg.len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_flush_queue);
> + msg.u.flush_queue.channel = priv->channel;
C99 initialziers, please
> +
> + return kvaser_usb_send_msg(priv->dev, &msg);
> +}
> +
> +static int kvaser_usb_close(struct net_device *netdev)
> +{
> + struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
> + int err;
> +
> + netif_stop_queue(netdev);
> +
> + err = kvaser_usb_flush_queue(priv);
> + if (err)
> + netdev_warn(netdev, "Cannot flush queue, error %d\n", err);
> +
> + err = kvaser_usb_stop_chip(priv);
> + if (err) {
> + netdev_warn(netdev, "Cannot stop device, error %d\n", err);
> + return err;
> + }
> +
> + kvaser_usb_unlink_tx_urbs(priv);
> +
> + priv->can.state = CAN_STATE_STOPPED;
> + close_candev(priv->netdev);
> + priv->open_time = 0;
> +
> + return 0;
> +}
> +
> +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;
> +
> + usb_free_coherent(urb->dev, urb->transfer_buffer_length,
> + 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);
> +
> + netdev->trans_start = jiffies;
Is trans_start needed? at least for non-usb devices it works without.
> +}
> +
> +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;
> + struct net_device_stats *stats = &netdev->stats;
> + struct can_frame *cf = (struct can_frame *)skb->data;
> + struct kvaser_usb_tx_urb_context *context = NULL;
> + struct urb *urb;
> + void *buf;
> + struct kvaser_msg *msg;
> + int i, err;
> + int ret = NETDEV_TX_OK;
> +
> + if (can_dropped_invalid_skb(netdev, skb))
> + return NETDEV_TX_OK;
> +
> + urb = usb_alloc_urb(0, GFP_ATOMIC);
> + if (!urb) {
> + netdev_err(netdev, "No memory left for URBs\n");
> + stats->tx_dropped++;
> + dev_kfree_skb(skb);
> + goto nourbmem;
> + }
> +
> + buf = usb_alloc_coherent(dev->udev, sizeof(struct kvaser_msg),
> + 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 kvaser_msg *)buf;
> + msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_tx_can);
> + msg->u.tx_can.flags = 0;
> + msg->u.tx_can.channel = priv->channel;
> +
> + if (cf->can_id & CAN_EFF_FLAG) {
> + msg->id = CMD_TX_EXT_MESSAGE;
> + msg->u.tx_can.msg[0] = (cf->can_id >> 24) & 0x1f;
> + msg->u.tx_can.msg[1] = (cf->can_id >> 18) & 0x3f;
> + msg->u.tx_can.msg[2] = (cf->can_id >> 14) & 0x0f;
> + msg->u.tx_can.msg[3] = (cf->can_id >> 6) & 0xff;
> + msg->u.tx_can.msg[4] = cf->can_id & 0x3f;
> + } else {
> + msg->id = CMD_TX_STD_MESSAGE;
> + msg->u.tx_can.msg[0] = (cf->can_id >> 6) & 0x1f;
> + msg->u.tx_can.msg[1] = cf->can_id & 0x3f;
> + }
> +
> + msg->u.tx_can.msg[5] = cf->can_dlc;
> + memcpy(&msg->u.tx_can.msg[6], cf->data, cf->can_dlc);
> +
> + if (cf->can_id & CAN_RTR_FLAG)
> + msg->u.tx_can.flags |= MSG_FLAG_REMOTE_FRAME;
> +
> + for (i = 0; i < MAX_TX_URBS; i++) {
ARRAY_SIZE
> + if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) {
> + context = &priv->tx_contexts[i];
> + break;
> + }
> + }
> +
> + if (!context) {
> + netdev_warn(netdev, "cannot find free context\n");
> + ret = NETDEV_TX_BUSY;
> + goto releasebuf;
> + }
> +
> + context->priv = priv;
> + context->echo_index = i;
> + context->dlc = cf->can_dlc;
> +
> + msg->u.tx_can.tid = context->echo_index;
> +
> + usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, 1),
> + buf, msg->len,
> + kvaser_usb_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);
> +
> + if (atomic_read(&priv->active_tx_urbs) >= MAX_TX_URBS)
> + netif_stop_queue(netdev);
> +
> + err = usb_submit_urb(urb, GFP_ATOMIC);
> + if (unlikely(err)) {
> + can_free_echo_skb(netdev, context->echo_index);
> +
> + atomic_dec(&priv->active_tx_urbs);
> + 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;
> + }
> +
> + netdev->trans_start = jiffies;
> +
> + usb_free_urb(urb);
> +
> + return NETDEV_TX_OK;
> +
> +releasebuf:
> + usb_free_coherent(dev->udev, sizeof(struct kvaser_msg),
> + buf, urb->transfer_dma);
> +nobufmem:
> + usb_free_urb(urb);
> +nourbmem:
> + 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,
> +};
> +
> +static struct can_bittiming_const kvaser_usb_bittiming_const = {
> + .name = "kvaser_usb",
> + .tseg1_min = KVASER_USB_TSEG1_MIN,
> + .tseg1_max = KVASER_USB_TSEG1_MAX,
> + .tseg2_min = KVASER_USB_TSEG2_MIN,
> + .tseg2_max = KVASER_USB_TSEG2_MAX,
> + .sjw_max = KVASER_USB_SJW_MAX,
> + .brp_min = KVASER_USB_BRP_MIN,
> + .brp_max = KVASER_USB_BRP_MAX,
> + .brp_inc = KVASER_USB_BRP_INC,
> +};
> +
> +static int kvaser_usb_set_bittiming(struct net_device *netdev)
> +{
> + struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
> + struct can_bittiming *bt = &priv->can.bittiming;
> + struct kvaser_usb *dev = priv->dev;
> + struct kvaser_msg msg;
> +
> + msg.id = CMD_SET_BUS_PARAMS;
> + msg.len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_busparams);
> + msg.u.busparams.channel = priv->channel;
> + msg.u.busparams.tid = 0xff;
> + msg.u.busparams.bitrate = bt->bitrate;
bitrate is le32
> + msg.u.busparams.sjw = bt->sjw;
> + msg.u.busparams.tseg1 = bt->prop_seg + bt->phase_seg1;
> + msg.u.busparams.tseg2 = bt->phase_seg2;
C99 initializers, please
> +
> + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
> + msg.u.busparams.no_samp = 3;
> + else
> + msg.u.busparams.no_samp = 1;
> +
> + return kvaser_usb_send_msg(dev, &msg);
> +}
> +
> +static int kvaser_usb_set_mode(struct net_device *netdev,
> + enum can_mode mode)
> +{
> + struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
> +
> + if (!priv->open_time)
> + return -EINVAL;
> +
> + switch (mode) {
> + case CAN_MODE_START:
> + if (netif_queue_stopped(netdev))
> + netif_wake_queue(netdev);
No need to restart your USB device?
> + break;
> + default:
> + return -EOPNOTSUPP;
> + }
> +
> + return 0;
> +}
> +
> +static int kvaser_usb_get_berr_counter(const struct net_device *netdev,
> + struct can_berr_counter *bec)
> +{
> + struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
> +
> + bec->txerr = priv->bec.txerr;
> + bec->rxerr = priv->bec.rxerr;
> +
> + return 0;
> +}
> +
> +static int kvaser_usb_init_one(struct usb_interface *intf,
> + const struct usb_device_id *id, int channel)
> +{
> + struct kvaser_usb *dev = usb_get_intfdata(intf);
> + struct net_device *netdev;
> + struct kvaser_usb_net_priv *priv;
> + int i, err;
> +
> + netdev = alloc_candev(sizeof(*priv), MAX_TX_URBS);
> + if (!netdev) {
> + dev_err(&intf->dev, "Cannot alloc candev\n");
> + return -ENOMEM;
> + }
> +
> + priv = netdev_priv(netdev);
> +
> + 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->dev = dev;
> + priv->netdev = netdev;
> + priv->channel = channel;
> +
> + priv->can.state = CAN_STATE_STOPPED;
> + priv->can.clock.freq = CAN_USB_CLOCK;
> + priv->can.bittiming_const = &kvaser_usb_bittiming_const;
> + priv->can.do_set_bittiming = kvaser_usb_set_bittiming;
> + priv->can.do_set_mode = kvaser_usb_set_mode;
> + priv->can.do_get_berr_counter = kvaser_usb_get_berr_counter;
> + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES;
> + if (id->driver_info & KVASER_HAS_SILENT_MODE)
> + priv->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY;
> +
> + netdev->flags |= IFF_ECHO;
> +
> + netdev->netdev_ops = &kvaser_usb_netdev_ops;
> +
> + SET_NETDEV_DEV(netdev, &intf->dev);
> +
> + err = register_candev(netdev);
> + if (err) {
> + dev_err(&intf->dev, "Failed to register can device\n");
> + free_candev(netdev);
> + return err;
> + }
> +
> + dev->nets[channel] = priv;
> + netdev_info(netdev, "device %s registered\n", netdev->name);
netdev_info should take care of printing the device's name.
> +
> + return 0;
> +}
> +
> +static int kvaser_usb_probe(struct usb_interface *intf,
> + const struct usb_device_id *id)
> +{
> + struct kvaser_usb *dev;
> + int err = -ENOMEM;
> + int i;
> +
> + dev = kzalloc(sizeof(*dev), GFP_KERNEL);
Who will free dev on driver unload? Please make use of devm_kzalloc().
> + if (!dev)
> + return -ENOMEM;
> +
> + dev->udev = interface_to_usbdev(intf);
> +
> + init_usb_anchor(&dev->rx_submitted);
> +
> + usb_set_intfdata(intf, dev);
> +
> + if (kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, 0)) {
> + dev_err(&intf->dev, "Cannot reset kvaser\n");
> + goto error;
> + }
> +
> + if (kvaser_usb_get_software_info(dev)) {
> + dev_err(&intf->dev, "Cannot get software infos\n");
> + goto error;
> + }
> +
> + if (kvaser_usb_get_card_info(dev)) {
> + dev_err(&intf->dev, "Cannot get card infos\n");
> + goto error;
> + }
> +
> + dev_dbg(&intf->dev, "Firmware version: %d.%d.%d\n",
> + ((dev->fw_version >> 24) & 0xff),
> + ((dev->fw_version >> 16) & 0xff),
> + (dev->fw_version & 0xffff));
> +
> + for (i = 0; i < dev->nchannels; i++)
> + kvaser_usb_init_one(intf, id, i);
> +
> + return 0;
> +
> +error:
> + kfree(dev);
> + return err;
> +}
> +
> +static void kvaser_usb_disconnect(struct usb_interface *intf)
> +{
> + struct kvaser_usb *dev = usb_get_intfdata(intf);
> + int i;
> +
> + usb_set_intfdata(intf, NULL);
> +
> + if (!dev)
> + return;
> +
> + for (i = 0; i < dev->nchannels; i++) {
> + if (!dev->nets[i])
> + continue;
> +
> + unregister_netdev(dev->nets[i]->netdev);
> + free_candev(dev->nets[i]->netdev);
> + }
> +
> + kvaser_usb_unlink_all_urbs(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_DESCRIPTION("Can driver for Kvaser CAN/USB devices");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/net/can/usb/kvaser_usb.h b/drivers/net/can/usb/kvaser_usb.h
> new file mode 100644
> index 0000000..8e0b6ab
> --- /dev/null
> +++ b/drivers/net/can/usb/kvaser_usb.h
> @@ -0,0 +1,237 @@
> +#ifndef _KVASER_USB_H_
> +#define _KVASER_USB_H_
> +
> +#define MAX_TX_URBS 16
Please no tab between define and macro name
> +#define MAX_RX_URBS 4
> +#define START_TIMEOUT 1000
> +#define STOP_TIMEOUT 1000
> +#define USB_SEND_TIMEOUT 1000
> +#define USB_RECEIVE_TIMEOUT 1000
> +#define RX_BUFFER_SIZE 3072
> +#define CAN_USB_CLOCK 8000000
> +#define MAX_NET_DEVICES 3
> +
> +/* Kvaser USB devices */
> +#define KVASER_VENDOR_ID 0x0bfd
> +#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
> +
> +/* USB devices features */
> +#define KVASER_HAS_SILENT_MODE (1 << 0)
pleae use BIT(0)
> +
> +/* Message header size */
> +#define MSG_HEADER_LEN 2
> +
> +/* Can message flags */
> +#define MSG_FLAG_ERROR_FRAME (1 << 0)
> +#define MSG_FLAG_OVERRUN (1 << 1)
> +#define MSG_FLAG_NERR (1 << 2)
> +#define MSG_FLAG_WAKEUP (1 << 3)
> +#define MSG_FLAG_REMOTE_FRAME (1 << 4)
> +#define MSG_FLAG_RESERVED (1 << 5)
> +#define MSG_FLAG_TX_ACK (1 << 6)
> +#define MSG_FLAG_TX_REQUEST (1 << 7)
> +
> +/* Can states */
> +#define M16C_STATE_BUS_RESET (1 << 0)
> +#define M16C_STATE_BUS_ERROR (1 << 4)
> +#define M16C_STATE_BUS_PASSIVE (1 << 5)
> +#define M16C_STATE_BUS_OFF (1 << 6)
> +
> +/* Can msg 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_GET_CTRL_MODE 22
> +#define CMD_GET_CTRL_MODE_REPLY 23
> +#define CMD_RESET_CHIP 24
> +#define CMD_RESET_CHIP_REPLY 25
> +#define CMD_START_CHIP 26
> +#define CMD_START_CHIP_REPLY 27
> +#define CMD_STOP_CHIP 28
> +#define CMD_STOP_CHIP_REPLY 29
> +#define CMD_GET_CARD_INFO2 32
> +#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_USB_THROTTLE 77
> +
> +/* error factors */
> +#define M16C_EF_ACKE (1 << 0)
> +#define M16C_EF_CRCE (1 << 1)
> +#define M16C_EF_FORME (1 << 2)
> +#define M16C_EF_STFE (1 << 3)
> +#define M16C_EF_BITE0 (1 << 4)
> +#define M16C_EF_BITE1 (1 << 5)
> +#define M16C_EF_RCVE (1 << 6)
> +#define M16C_EF_TRE (1 << 7)
> +
> +/* bittiming parameters */
> +#define KVASER_USB_TSEG1_MIN 1
> +#define KVASER_USB_TSEG1_MAX 16
> +#define KVASER_USB_TSEG2_MIN 1
> +#define KVASER_USB_TSEG2_MAX 8
> +#define KVASER_USB_SJW_MAX 4
> +#define KVASER_USB_BRP_MIN 1
> +#define KVASER_USB_BRP_MAX 64
> +#define KVASER_USB_BRP_INC 1
> +
> +/* 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
> +
> +struct kvaser_msg_simple {
> + u8 tid;
> + u8 channel;
> +} __packed;
> +
> +struct kvaser_msg_cardinfo {
> + u8 tid;
> + u8 nchannels;
> + __le32 serial_number;
> + __le32 padding;
> + __le32 clock_resolution;
> + __le32 mfgdate;
> + u8 ean[8];
> + u8 hw_revision;
> + u8 usb_hs_mode;
> + __le16 padding2;
> +} __packed;
> +
> +struct kvaser_msg_cardinfo2 {
> + u8 tid;
> + u8 channel;
> + u8 pcb_id[24];
> + __le32 oem_unlock_code;
> +} __packed;
> +
> +struct kvaser_msg_softinfo {
> + u8 tid;
> + u8 channel;
> + __le32 sw_options;
> + __le32 fw_version;
> + __le16 max_outstanding_tx;
> + __le16 padding[9];
> +} __packed;
> +
> +struct kvaser_msg_busparams {
> + u8 tid;
> + u8 channel;
> + __le32 bitrate;
> + u8 tseg1;
> + u8 tseg2;
> + u8 sjw;
> + u8 no_samp;
> +} __packed;
> +
> +struct kvaser_msg_tx_can {
> + u8 channel;
> + u8 tid;
> + u8 msg[14];
> + u8 padding;
> + u8 flags;
> +} __packed;
> +
> +struct kvaser_msg_rx_can {
> + u8 channel;
> + u8 flag;
> + __le16 time[3];
> + u8 msg[14];
> +} __packed;
> +
> +struct kvaser_msg_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 kvaser_msg_tx_acknowledge {
> + u8 channel;
> + u8 tid;
> + __le16 time[3];
> + u8 flags;
> + u8 time_offset;
> +} __packed;
> +
> +struct kvaser_msg_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 kvaser_msg_ctrl_mode {
> + u8 tid;
> + u8 channel;
> + u8 ctrl_mode;
> + u8 padding[3];
> +} __packed;
> +
> +struct kvaser_msg_flush_queue {
> + u8 tid;
> + u8 channel;
> + u8 flags;
> + u8 padding[3];
> +} __packed;
> +
> +struct kvaser_msg {
> + u8 len;
> + u8 id;
> + union {
> + struct kvaser_msg_simple simple;
> + struct kvaser_msg_cardinfo cardinfo;
> + struct kvaser_msg_cardinfo2 cardinfo2;
> + struct kvaser_msg_softinfo softinfo;
> + struct kvaser_msg_busparams busparams;
> + struct kvaser_msg_tx_can tx_can;
> + struct kvaser_msg_rx_can rx_can;
> + struct kvaser_msg_chip_state_event chip_state_event;
> + struct kvaser_msg_tx_acknowledge tx_acknowledge;
> + struct kvaser_msg_error_event error_event;
> + struct kvaser_msg_ctrl_mode ctrl_mode;
> + struct kvaser_msg_ctrl_mode flush_queue;
> + } u;
> +} __packed;
> +
> +#endif
>
--
Pengutronix e.K. | Marc Kleine-Budde |
Industrial Linux Solutions | Phone: +49-231-2826-924 |
Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 262 bytes --]
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH] can: kvaser_usb: Add support for Kvaser CAN/USB devices
2012-07-30 11:11 ` Marc Kleine-Budde
@ 2012-07-30 13:33 ` Olivier Sobrie
2012-07-31 9:56 ` Marc Kleine-Budde
0 siblings, 1 reply; 43+ messages in thread
From: Olivier Sobrie @ 2012-07-30 13:33 UTC (permalink / raw)
To: Marc Kleine-Budde; +Cc: Wolfgang Grandegger, linux-can, netdev
Hi Marc,
On Mon, Jul 30, 2012 at 01:11:46PM +0200, Marc Kleine-Budde wrote:
> On 07/30/2012 07:32 AM, Olivier Sobrie wrote:
> > This driver provides support for several Kvaser CAN/USB devices.
> > Such kind of devices supports up to three can network interfaces.
> >
> > It has been tested with a Kvaser USB Leaf Light (one network interface)
> > connected to a pch_can interface.
> > The firmware version of the Kvaser device was 2.5.205.
>
> Please add linux-usb@vger.kernel.org to Cc for review of the USB part.
Ok I'll do it when I send the new version of the patch.
But it might be a good idea to add an entry in the MAINTAINERS file so
that when someone sends a patch they are aware of this when the
get_maintainer script is invoked.
> Please combine .h and .c file. Please make use of netdev_LEVEL() for
> error printing, not dev_LEVEL().
I'll combine the .c and .h.
I used the netdev_LEVEL() everywhere it was possible. It requires to
have access to a pointer to netdev which is not always possible;
that's the reason why I used dev_LEVEL().
>
> Please review if all members of the struct kvaser_msg are properly
> aligned. You never access the struct kvaser_msg_* members directly, as
> they are unaligned. Please check for le16 and le32 access. You missed to
> convert the bitrate.
Indeed. Thanks. I'll check if I didn't missed another one.
>
> Please check if your driver survives hot-unplugging while sending and
> receiving CAN frames at maximum laod.
I tested this with two Kvaser sending frames with "cangen can0 -g 0 -i"
never saw a crash.
>
> More comments inline,
> regards, Marc
>
> > List of Kvaser devices supported by the driver:
> > - Kvaser Leaf prototype (P010v2 and v3)
> > - Kvaser Leaf Light (P010v3)
> > - 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, Prototype
> > - 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 OEM Mercury
> > - Kvaser OEM Leaf
> > - Kvaser USBcan R
> >
> > Signed-off-by: Olivier Sobrie <olivier@sobrie.be>
> > ---
> > drivers/net/can/usb/Kconfig | 33 ++
> > drivers/net/can/usb/Makefile | 1 +
> > drivers/net/can/usb/kvaser_usb.c | 1062 ++++++++++++++++++++++++++++++++++++++
> > drivers/net/can/usb/kvaser_usb.h | 237 +++++++++
> > 4 files changed, 1333 insertions(+)
> > create mode 100644 drivers/net/can/usb/kvaser_usb.c
> > create mode 100644 drivers/net/can/usb/kvaser_usb.h
> >
> > diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig
> > index 0a68768..578955f 100644
> > --- a/drivers/net/can/usb/Kconfig
> > +++ b/drivers/net/can/usb/Kconfig
> > @@ -13,6 +13,39 @@ config CAN_ESD_USB2
> > This driver supports the CAN-USB/2 interface
> > from esd electronic system design gmbh (http://www.esd.eu).
> >
> > +config CAN_KVASER_USB
> > + tristate "Kvaser CAN/USB interface"
> > + ---help---
> > + This driver adds support for Kvaser CAN/USB devices like Kvaser
> > + Leaf Light.
> > +
> > + The driver gives support for the following devices:
> > + - Kvaser Leaf prototype (P010v2 and v3)
> > + - Kvaser Leaf Light (P010v3)
> > + - 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, Prototype
> > + - 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 OEM Mercury
> > + - Kvaser OEM Leaf
> > + - Kvaser USBcan R
> > +
> > + If unsure, say N.
> > +
> > + To compile this driver as a module, choose M here: the
> > + module will be called kvaser_usb.
> > +
> > config CAN_PEAK_USB
> > tristate "PEAK PCAN-USB/USB Pro interfaces"
> > ---help---
> > diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile
> > index da6d1d3..80a2ee4 100644
> > --- a/drivers/net/can/usb/Makefile
> > +++ b/drivers/net/can/usb/Makefile
> > @@ -4,6 +4,7 @@
> >
> > obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o
> > obj-$(CONFIG_CAN_ESD_USB2) += esd_usb2.o
> > +obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o
> > obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/
> >
> > ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
> > diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c
> > new file mode 100644
> > index 0000000..4965480
> > --- /dev/null
> > +++ b/drivers/net/can/usb/kvaser_usb.c
> > @@ -0,0 +1,1062 @@
> > +/*
>
> Please add a license statement and probably your copyright:
>
> * This program is free software; you can redistribute it and/or
> * modify it under the terms of the GNU General Public License as
> * published by the Free Software Foundation version 2.
>
> You also should copy the copyright from the drivers you used:
>
> > + * Parts of this driver are based on the following:
> > + * - Kvaser linux leaf driver (version 4.78)
>
> I just downloaded their driver and noticed that it's quite sparse on
> stating the license the code is released under.
> "doc/HTMLhelp/copyright.htx" is quite restrictive, the word GPL occurs 3
> times, all in MODULE_LICENSE("GPL"). Running modinfo on the usbcan.ko
> shows "license: GPL"
I'll add the license statement.
In fact it's the leaf.ko which is used for this device and it is under
GPL as modinfo said.
>
> > + * - CAN driver for esd CAN-USB/2
> > + */
> > +
> > +#include <linux/init.h>
> > +#include <linux/completion.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 "kvaser_usb.h"
> > +
> > +struct kvaser_usb_tx_urb_context {
> > + struct kvaser_usb_net_priv *priv;
>
> Huh - how does this work without forward declaration?
It works.
"In C and C++ it is possible to declare pointers to structs before
declaring their struct layout, provided the pointers are not
dereferenced--this is known as forward declaration."
See http://www.linuxtopia.org/online_books/an_introduction_to_gcc/gccintro_94.html
>
> > + u32 echo_index;
> > + int dlc;
> > +};
> > +
> > +struct kvaser_usb {
> > + struct usb_device *udev;
> > + struct kvaser_usb_net_priv *nets[MAX_NET_DEVICES];
> > +
> > + struct usb_anchor rx_submitted;
> > +
> > + u32 fw_version;
> > + unsigned int nchannels;
> > +
> > + bool rxinitdone;
> > +};
> > +
> > +struct kvaser_usb_net_priv {
> > + struct can_priv can;
> > +
> > + atomic_t active_tx_urbs;
> > + struct usb_anchor tx_submitted;
> > + struct kvaser_usb_tx_urb_context tx_contexts[MAX_TX_URBS];
> > +
> > + int open_time;
>
> please remove open_time
Ok.
>
> > + struct completion start_stop_comp;
> > +
> > + struct kvaser_usb *dev;
> > + struct net_device *netdev;
> > + int channel;
> > + struct can_berr_counter bec;
> > +};
> > +
> > +static struct usb_device_id kvaser_usb_table[] = {
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_DEVEL_PRODUCT_ID) },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_PRODUCT_ID) },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_PRODUCT_ID),
> > + .driver_info = KVASER_HAS_SILENT_MODE },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_PRODUCT_ID),
> > + .driver_info = KVASER_HAS_SILENT_MODE },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LS_PRODUCT_ID),
> > + .driver_info = KVASER_HAS_SILENT_MODE },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_SWC_PRODUCT_ID),
> > + .driver_info = KVASER_HAS_SILENT_MODE },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LIN_PRODUCT_ID),
> > + .driver_info = KVASER_HAS_SILENT_MODE },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_LS_PRODUCT_ID),
> > + .driver_info = KVASER_HAS_SILENT_MODE },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_SWC_PRODUCT_ID),
> > + .driver_info = KVASER_HAS_SILENT_MODE },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_DEVEL_PRODUCT_ID),
> > + .driver_info = KVASER_HAS_SILENT_MODE },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSHS_PRODUCT_ID),
> > + .driver_info = KVASER_HAS_SILENT_MODE },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_UPRO_HSHS_PRODUCT_ID) },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_GI_PRODUCT_ID) },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_OBDII_PRODUCT_ID),
> > + .driver_info = KVASER_HAS_SILENT_MODE },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSLS_PRODUCT_ID) },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_CH_PRODUCT_ID) },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_BLACKBIRD_SPRO_PRODUCT_ID) },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_MERCURY_PRODUCT_ID) },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_LEAF_PRODUCT_ID) },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_CAN_R_PRODUCT_ID) },
> > + { }
> > +};
> > +MODULE_DEVICE_TABLE(usb, kvaser_usb_table);
> > +
> > +static int kvaser_usb_send_msg(const struct kvaser_usb *dev,
> > + struct kvaser_msg *msg)
> inline?
Ok.
> > +{
> > + int actual_len;
> > +
> > + return usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 1),
> ^
> Can you please introduce a #define for this.
Ok. No problem.
>
> > + msg, msg->len, &actual_len, USB_SEND_TIMEOUT);
> > +}
> > +
> > +static int kvaser_usb_wait_msg(const struct kvaser_usb *dev, u8 id,
> > + struct kvaser_msg *msg)
> > +{
> > + struct kvaser_msg *tmp;
> > + void *buf;
> > + int actual_len;
> > + int err;
> > + int pos = 0;
> > +
> > + buf = kzalloc(RX_BUFFER_SIZE, GFP_KERNEL);
> > + if (!buf)
> > + return -ENOMEM;
> > +
> > + err = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, 129),
> ^^^
> dito
Ok too.
>
> > + buf, RX_BUFFER_SIZE, &actual_len,
> > + USB_RECEIVE_TIMEOUT);
> > + if (err < 0) {
> > + kfree(buf);
> > + return err;
> > + }
> > +
> > + while (pos < actual_len) {
>
> Please check that pos + sizeof(*msg) is < actual_len, as you fill access
> it later.
I'll instead perform a check on 'pos + tmp->len < actual_len' and copy
only tmp->len instead of sizeof(*msg).
Thanks.
>
> > + tmp = buf + pos;
> > +
> > + if (!tmp->len)
> > + break;
> > +
> > + if (tmp->id == id) {
> > + memcpy(msg, tmp, sizeof(*msg));
> > + kfree(buf);
> > + return 0;
> > + }
> > +
> > + pos += tmp->len;
> > + }
> > +
> > + kfree(buf);
> > +
> > + return -EINVAL;
> > +}
> > +
> > +static int kvaser_usb_send_simple_msg(const struct kvaser_usb *dev,
> > + u8 msg_id, int channel)
> > +{
> > + struct kvaser_msg msg;
> > + int err;
> > +
> > + msg.len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple);
> > + msg.id = msg_id;
> > + msg.u.simple.channel = channel;
> > + msg.u.simple.tid = 0xff;
>
> Please use C99 struct initializer.
>
> struct kvaser_msg msg = {
> .len = ,
> .id =,
> };
Ok.
>
>
> > +
> > + err = kvaser_usb_send_msg(dev, &msg);
>
> just:
> return err;
Ok.
>
> > + if (err)
> > + return err;
> > +
> > + return 0;
> > +}
> > +
> > +static int kvaser_usb_get_software_info(struct kvaser_usb *dev)
> > +{
> > + struct kvaser_msg msg;
> > + int err;
> > +
> > + err = kvaser_usb_send_simple_msg(dev, CMD_GET_SOFTWARE_INFO, 0);
> > + if (err)
> > + return err;
> > +
> > + err = kvaser_usb_wait_msg(dev, CMD_GET_SOFTWARE_INFO_REPLY, &msg);
> > + if (err)
> > + return err;
> > +
> > + dev->fw_version = le32_to_cpu(msg.u.softinfo.fw_version);
> > +
> > + return 0;
> > +}
> > +
> > +static int kvaser_usb_get_card_info(struct kvaser_usb *dev)
> > +{
> > + struct kvaser_msg msg;
> > + int err;
> > +
> > + err = kvaser_usb_send_simple_msg(dev, CMD_GET_CARD_INFO, 0);
> > + if (err)
> > + return err;
> > +
> > + err = kvaser_usb_wait_msg(dev, CMD_GET_CARD_INFO_REPLY, &msg);
> > + if (err)
> > + return err;
> > +
> > + dev->nchannels = msg.u.cardinfo.nchannels;
> > +
> > + return 0;
> > +}
> > +
> > +static void kvaser_usb_tx_acknowledge(const struct kvaser_usb *dev,
> > + const struct kvaser_msg *msg)
> > +{
> > + struct net_device_stats *stats;
> > + struct kvaser_usb_tx_urb_context *context;
> > + struct kvaser_usb_net_priv *priv;
> > + u8 channel = msg->u.tx_acknowledge.channel;
> > + u8 tid = msg->u.tx_acknowledge.tid;
> > +
> > + if (channel >= dev->nchannels) {
> > + dev_err(dev->udev->dev.parent,
> > + "Invalid channel number (%d)\n", channel);
> > + return;
> > + }
>
> can you do a check for (channel >= dev->nchannels), in a central place?
> e.g. kvaser_usb_handle_message()?
The problem is that channel is not always at the same place in the
messages I get from the hardware. 'tid' and 'channel' are inverted for
tx and rx frames.
e.g.
struct kvaser_msg_tx_can {
u8 channel;
u8 tid;
u8 msg[14];
u8 padding;
u8 flags;
} __packed;
struct kvaser_msg_busparams {
u8 tid;
u8 channel;
__le32 bitrate;
u8 tseg1;
u8 tseg2;
u8 sjw;
u8 no_samp;
} __packed;
>
> > +
> > + priv = dev->nets[channel];
> > +
> > + if (!netif_device_present(priv->netdev))
> > + return;
> > +
> > + stats = &priv->netdev->stats;
> > +
> > + context = &priv->tx_contexts[tid % MAX_TX_URBS];
> > +
> > + /*
> > + * It looks like the firmware never sets the flags field of the
> > + * tx_acknowledge frame and never reports a transmit failure.
> > + * If the can message can't be transmited (e.g. incompatible
> > + * bitrates), a frame CMD_CAN_ERROR_EVENT is sent (with a null
> > + * tid) and the firmware tries to transmit again the packet until
> > + * it succeeds. Once the packet is successfully transmitted, then
> > + * the tx_acknowledge frame is sent.
> > + */
> > +
> > + stats->tx_packets++;
> > + stats->tx_bytes += context->dlc;
> > + can_get_echo_skb(priv->netdev, context->echo_index);
> > +
> > + context->echo_index = MAX_TX_URBS;
> > + atomic_dec(&priv->active_tx_urbs);
> > +
> > + netif_wake_queue(priv->netdev);
> > +}
> > +
> > +static void kvaser_usb_rx_error(const struct kvaser_usb *dev,
> > + const struct kvaser_msg *msg)
> > +{
> > + struct can_frame *cf;
> > + struct sk_buff *skb;
> > + struct net_device_stats *stats;
> > + struct kvaser_usb_net_priv *priv;
> > + u8 channel, status, txerr, rxerr;
> > +
> > + if (msg->id == CMD_CAN_ERROR_EVENT) {
> > + channel = msg->u.error_event.channel;
> > + status = msg->u.error_event.status;
> > + txerr = msg->u.error_event.tx_errors_count;
> > + rxerr = msg->u.error_event.rx_errors_count;
> > + } else {
> > + channel = msg->u.chip_state_event.channel;
> > + status = msg->u.chip_state_event.status;
> > + txerr = msg->u.chip_state_event.tx_errors_count;
> > + rxerr = msg->u.chip_state_event.rx_errors_count;
> > + }
> > +
> > + if (channel >= dev->nchannels) {
> > + dev_err(dev->udev->dev.parent,
> > + "Invalid channel number (%d)\n", channel);
> > + return;
> > + }
> > +
> > + priv = dev->nets[channel];
> > + stats = &priv->netdev->stats;
> > +
> > + skb = alloc_can_err_skb(priv->netdev, &cf);
> > + if (!skb) {
> > + stats->rx_dropped++;
> > + return;
> > + }
> > +
> > + if ((status & M16C_STATE_BUS_OFF) ||
> > + (status & M16C_STATE_BUS_RESET)) {
> > + priv->can.state = CAN_STATE_BUS_OFF;
> > + cf->can_id |= CAN_ERR_BUSOFF;
> > + can_bus_off(priv->netdev);
> > + } else if (status & M16C_STATE_BUS_ERROR) {
> > + priv->can.state = CAN_STATE_ERROR_WARNING;
> > + priv->can.can_stats.error_warning++;
> > + } else if (status & M16C_STATE_BUS_PASSIVE) {
> > + priv->can.state = CAN_STATE_ERROR_PASSIVE;
> > + priv->can.can_stats.error_passive++;
> > + } else {
> > + priv->can.state = CAN_STATE_ERROR_ACTIVE;
> > + cf->can_id |= CAN_ERR_PROT;
> > + cf->data[2] = CAN_ERR_PROT_ACTIVE;
> > + }
> > +
> > + if (msg->id == CMD_CAN_ERROR_EVENT) {
> > + u8 error_factor = msg->u.error_event.error_factor;
> > +
> > + priv->can.can_stats.bus_error++;
> > + stats->rx_errors++;
> > +
> > + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
> > +
> > + 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;
> > + }
> > +
> > + if (error_factor & M16C_EF_ACKE)
> > + cf->data[3] |= (CAN_ERR_PROT_LOC_ACK |
> > + CAN_ERR_PROT_LOC_ACK_DEL);
> > + if (error_factor & M16C_EF_CRCE)
> > + cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ |
> > + CAN_ERR_PROT_LOC_CRC_DEL);
> > + if (error_factor & M16C_EF_FORME)
> > + cf->data[2] |= CAN_ERR_PROT_FORM;
> > + if (error_factor & M16C_EF_STFE)
> > + cf->data[2] |= CAN_ERR_PROT_STUFF;
> > + if (error_factor & M16C_EF_BITE0)
> > + cf->data[2] |= CAN_ERR_PROT_BIT0;
> > + if (error_factor & M16C_EF_BITE1)
> > + cf->data[2] |= CAN_ERR_PROT_BIT1;
> > + if (error_factor & M16C_EF_TRE)
> > + cf->data[2] |= CAN_ERR_PROT_TX;
> > + }
> > +
> > + cf->data[6] = txerr;
> > + cf->data[7] = rxerr;
> > +
> > + netif_rx(skb);
> > +
> > + priv->bec.txerr = txerr;
> > + priv->bec.rxerr = rxerr;
> > +
> > + stats->rx_packets++;
> > + stats->rx_bytes += cf->can_dlc;
> > +}
> > +
> > +static void kvaser_usb_rx_can_msg(const struct kvaser_usb *dev,
> > + const struct kvaser_msg *msg)
> > +{
> > + struct kvaser_usb_net_priv *priv;
> > + struct can_frame *cf;
> > + struct sk_buff *skb;
> > + struct net_device_stats *stats;
> > + u8 channel = msg->u.rx_can.channel;
> > +
> > + if (channel >= dev->nchannels) {
> > + dev_err(dev->udev->dev.parent,
> > + "Invalid channel number (%d)\n", channel);
> > + return;
> > + }
> > +
> > + priv = dev->nets[channel];
> > + stats = &priv->netdev->stats;
> > +
> > + skb = alloc_can_skb(priv->netdev, &cf);
> > + if (skb == NULL) {
> > + stats->tx_dropped++;
> > + return;
> > + }
> > +
> > + cf->can_id = ((msg->u.rx_can.msg[0] & 0x1f) << 6) |
> > + (msg->u.rx_can.msg[1] & 0x3f);
> > + cf->can_dlc = get_can_dlc(msg->u.rx_can.msg[5]);
> > +
> > + if (msg->id == CMD_RX_EXT_MESSAGE) {
> > + cf->can_id <<= 18;
> > + cf->can_id += ((msg->u.rx_can.msg[2] & 0x0f) << 14) |
> |=
>
> is more appropriate here
Ok.
>
> > + ((msg->u.rx_can.msg[3] & 0xff) << 6) |
> > + (msg->u.rx_can.msg[4] & 0x3f);
> > + cf->can_id |= CAN_EFF_FLAG;
> > + }
> > +
> > + if (msg->u.rx_can.flag & MSG_FLAG_REMOTE_FRAME) {
> > + cf->can_id |= CAN_RTR_FLAG;
> > + } else if (msg->u.rx_can.flag & (MSG_FLAG_ERROR_FRAME |
> > + MSG_FLAG_NERR)) {
> > + cf->can_id |= CAN_ERR_FLAG;
> > + cf->can_dlc = CAN_ERR_DLC;
>
> What kind of error is this? Can you set cf->data? What about the
> original cd->can_id? What about the stats->rx_*error* stats?
Good question I've to take a look to this.
>
> > + } else if (msg->u.rx_can.flag & MSG_FLAG_OVERRUN) {
> > + cf->can_id = CAN_ERR_FLAG | CAN_ERR_CRTL;
> > + cf->can_dlc = CAN_ERR_DLC;
> > + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
> > +
> > + stats->rx_over_errors++;
> > + stats->rx_errors++;
> > + } else if (!msg->u.rx_can.flag) {
> > + memcpy(cf->data, &msg->u.rx_can.msg[6], cf->can_dlc);
> > + } else {
> > + kfree_skb(skb);
> > + return;
> > + }
> > +
> > + netif_rx(skb);
> > +
> > + stats->rx_packets++;
> > + stats->rx_bytes += cf->can_dlc;
> > +}
> > +
> > +static void kvaser_usb_start_stop_chip_reply(const struct kvaser_usb *dev,
> > + const struct kvaser_msg *msg)
> > +{
> > + struct kvaser_usb_net_priv *priv;
> > + u8 channel = msg->u.simple.channel;
> > +
> > + if (channel >= dev->nchannels) {
> > + dev_err(dev->udev->dev.parent,
> > + "Invalid channel number (%d)\n", channel);
> > + return;
> > + }
> > +
> > + priv = dev->nets[channel];
> > +
> > + complete(&priv->start_stop_comp);
> > +}
> > +
> > +static void kvaser_usb_handle_message(const struct kvaser_usb *dev,
> > + const struct kvaser_msg *msg)
> > +{
> > + switch (msg->id) {
> > + case CMD_START_CHIP_REPLY:
> > + case CMD_STOP_CHIP_REPLY:
> > + kvaser_usb_start_stop_chip_reply(dev, msg);
> > + break;
> > +
> > + case CMD_RX_STD_MESSAGE:
> > + case CMD_RX_EXT_MESSAGE:
> > + kvaser_usb_rx_can_msg(dev, msg);
> > + break;
> > +
> > + case CMD_CHIP_STATE_EVENT:
> > + case CMD_CAN_ERROR_EVENT:
> > + kvaser_usb_rx_error(dev, msg);
> > + break;
> > +
> > + case CMD_TX_ACKNOWLEDGE:
> > + kvaser_usb_tx_acknowledge(dev, msg);
> > + break;
> > +
> > + default:
> > + dev_warn(dev->udev->dev.parent,
> > + "Unhandled message (%d)\n", msg->id);
> > + break;
> > + }
> > +}
> > +
> > +static void kvaser_usb_read_bulk_callback(struct urb *urb)
> > +{
> > + struct kvaser_usb *dev = urb->context;
> > + struct kvaser_msg *msg;
> > + int pos = 0;
> > + int err, i;
> > +
> > + switch (urb->status) {
> > + case 0:
> > + break;
> > + case -ENOENT:
> > + 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) {
>
> please check here for pos + sizeof(*msg), too
Same as above.
>
> > + msg = urb->transfer_buffer + pos;
> > +
> > + if (!msg->len)
> > + break;
> > +
> > + kvaser_usb_handle_message(dev, msg);
> > +
> > + if (pos > urb->actual_length) {
> > + dev_err(dev->udev->dev.parent, "Format error\n");
> > + break;
> > + }
> > +
> > + pos += msg->len;
> > + }
> > +
> > +resubmit_urb:
> > + usb_fill_bulk_urb(urb, dev->udev, usb_rcvbulkpipe(dev->udev, 129),
> ^^^
>
> use #define
Ok.
>
> > + urb->transfer_buffer, 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->udev->dev.parent,
> > + "Failed resubmitting read bulk urb: %d\n", err);
> > + }
> > +
> > + return;
> > +}
> > +
> > +static int kvaser_usb_setup_rx_urbs(struct kvaser_usb *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;
> > +
> > + urb = usb_alloc_urb(0, GFP_KERNEL);
> > + if (!urb) {
> > + dev_warn(dev->udev->dev.parent,
> > + "No memory left for URBs\n");
> > + err = -ENOMEM;
> > + break;
> > + }
> > +
> > + buf = usb_alloc_coherent(dev->udev, RX_BUFFER_SIZE,
> > + GFP_KERNEL, &urb->transfer_dma);
> > + if (!buf) {
> > + dev_warn(dev->udev->dev.parent,
> > + "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, 129),
>
> use #define
Ok.
>
> > + buf, RX_BUFFER_SIZE,
> > + kvaser_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);
> > + break;
> > + }
> > +
> > + usb_free_urb(urb);
> > + }
> > +
> > + if (i == 0) {
> > + dev_warn(dev->udev->dev.parent,
> > + "Cannot setup read URBs, error %d\n", err);
> > + return err;
> > + } else if (i < MAX_RX_URBS) {
> > + dev_warn(dev->udev->dev.parent,
> > + "RX performances may be slow\n");
> > + }
> > +
> > + dev->rxinitdone = true;
> > +
> > + return 0;
> > +}
> > +
> > +static int kvaser_usb_set_opt_mode(const struct kvaser_usb_net_priv *priv)
> > +{
> > + struct kvaser_msg msg;
> > +
> > + memset(&msg, 0x00, sizeof(msg));
> > + msg.id = CMD_SET_CTRL_MODE;
> > + msg.len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_ctrl_mode);
> > + msg.u.ctrl_mode.tid = 0xff;
> > + msg.u.ctrl_mode.channel = priv->channel;
>
> please use C99 struct initializers
Ok.
>
> > +
> > + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
> > + msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_SILENT;
> > + else
> > + msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_NORMAL;
> > +
> > + return kvaser_usb_send_msg(priv->dev, &msg);
> > +}
> > +
> > +static int kvaser_usb_start_chip(struct kvaser_usb_net_priv *priv)
> > +{
> > + int err;
> > +
> > + init_completion(&priv->start_stop_comp);
> > +
> > + err = kvaser_usb_send_simple_msg(priv->dev, CMD_START_CHIP,
> > + priv->channel);
> > + if (err)
> > + return err;
> > +
> > + if (!wait_for_completion_timeout(&priv->start_stop_comp,
> > + msecs_to_jiffies(START_TIMEOUT)))
> > + return -ETIMEDOUT;
> > +
> > + 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;
> > + int err;
> > +
> > + err = open_candev(netdev);
> > + if (err)
> > + return err;
> > +
> > + err = kvaser_usb_setup_rx_urbs(dev);
> > + if (err)
> > + return err;
> > +
> > + err = kvaser_usb_set_opt_mode(priv);
> > + if (err)
> > + return err;
> > +
> > + err = kvaser_usb_start_chip(priv);
> > + if (err) {
> > + netdev_warn(netdev, "Cannot start device, error %d\n", err);
> > + close_candev(netdev);
> > + return err;
> > + }
> > +
> > + priv->can.state = CAN_STATE_ERROR_ACTIVE;
> > + priv->open_time = jiffies;
> > + netif_start_queue(netdev);
> > +
> > + return 0;
> > +}
> > +
> > +static void kvaser_usb_unlink_tx_urbs(struct kvaser_usb_net_priv *priv)
> > +{
> > + int i;
> > +
> > + usb_kill_anchored_urbs(&priv->tx_submitted);
> > + atomic_set(&priv->active_tx_urbs, 0);
> > +
> > + for (i = 0; i < MAX_TX_URBS; i++)
> ARRAY_SIZE(priv->tx_contexts) instead of MAX_TX_URBS
Ok.
> > + priv->tx_contexts[i].echo_index = MAX_TX_URBS;
> > +}
> > +
> > +static void kvaser_usb_unlink_all_urbs(struct kvaser_usb *dev)
> > +{
> > + int i;
> > +
> > + usb_kill_anchored_urbs(&dev->rx_submitted);
> > +
> > + for (i = 0; i < MAX_NET_DEVICES; i++) {
> ARRAY_SIZE()
> > + struct kvaser_usb_net_priv *priv = dev->nets[i];
> > +
> > + if (priv)
> > + kvaser_usb_unlink_tx_urbs(priv);
> > + }
> > +}
> > +
> > +static int kvaser_usb_stop_chip(struct kvaser_usb_net_priv *priv)
> > +{
> > + int err;
> > +
> > + init_completion(&priv->start_stop_comp);
> > +
> > + err = kvaser_usb_send_simple_msg(priv->dev, CMD_STOP_CHIP,
> > + priv->channel);
> > + if (err)
> > + return err;
> > +
> > + if (!wait_for_completion_timeout(&priv->start_stop_comp,
> > + msecs_to_jiffies(STOP_TIMEOUT)))
> > + return -ETIMEDOUT;
> > +
> > + return 0;
> > +}
> > +
> > +static int kvaser_usb_flush_queue(struct kvaser_usb_net_priv *priv)
> > +{
> > + struct kvaser_msg msg;
> > +
> > + memset(&msg, 0x00, sizeof(msg));
> > + msg.id = CMD_FLUSH_QUEUE;
> > + msg.len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_flush_queue);
> > + msg.u.flush_queue.channel = priv->channel;
> C99 initialziers, please
Ok.
> > +
> > + return kvaser_usb_send_msg(priv->dev, &msg);
> > +}
> > +
> > +static int kvaser_usb_close(struct net_device *netdev)
> > +{
> > + struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
> > + int err;
> > +
> > + netif_stop_queue(netdev);
> > +
> > + err = kvaser_usb_flush_queue(priv);
> > + if (err)
> > + netdev_warn(netdev, "Cannot flush queue, error %d\n", err);
> > +
> > + err = kvaser_usb_stop_chip(priv);
> > + if (err) {
> > + netdev_warn(netdev, "Cannot stop device, error %d\n", err);
> > + return err;
> > + }
> > +
> > + kvaser_usb_unlink_tx_urbs(priv);
> > +
> > + priv->can.state = CAN_STATE_STOPPED;
> > + close_candev(priv->netdev);
> > + priv->open_time = 0;
> > +
> > + return 0;
> > +}
> > +
> > +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;
> > +
> > + usb_free_coherent(urb->dev, urb->transfer_buffer_length,
> > + 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);
> > +
> > + netdev->trans_start = jiffies;
>
> Is trans_start needed? at least for non-usb devices it works without.
I don't know, I'll try to figure this out.
I see it's used in the two others CAN/USB drivers, 'ems_usb.c' and
'esd_usb2.c'
>
> > +}
> > +
> > +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;
> > + struct net_device_stats *stats = &netdev->stats;
> > + struct can_frame *cf = (struct can_frame *)skb->data;
> > + struct kvaser_usb_tx_urb_context *context = NULL;
> > + struct urb *urb;
> > + void *buf;
> > + struct kvaser_msg *msg;
> > + int i, err;
> > + int ret = NETDEV_TX_OK;
> > +
> > + if (can_dropped_invalid_skb(netdev, skb))
> > + return NETDEV_TX_OK;
> > +
> > + urb = usb_alloc_urb(0, GFP_ATOMIC);
> > + if (!urb) {
> > + netdev_err(netdev, "No memory left for URBs\n");
> > + stats->tx_dropped++;
> > + dev_kfree_skb(skb);
> > + goto nourbmem;
> > + }
> > +
> > + buf = usb_alloc_coherent(dev->udev, sizeof(struct kvaser_msg),
> > + 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 kvaser_msg *)buf;
> > + msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_tx_can);
> > + msg->u.tx_can.flags = 0;
> > + msg->u.tx_can.channel = priv->channel;
> > +
> > + if (cf->can_id & CAN_EFF_FLAG) {
> > + msg->id = CMD_TX_EXT_MESSAGE;
> > + msg->u.tx_can.msg[0] = (cf->can_id >> 24) & 0x1f;
> > + msg->u.tx_can.msg[1] = (cf->can_id >> 18) & 0x3f;
> > + msg->u.tx_can.msg[2] = (cf->can_id >> 14) & 0x0f;
> > + msg->u.tx_can.msg[3] = (cf->can_id >> 6) & 0xff;
> > + msg->u.tx_can.msg[4] = cf->can_id & 0x3f;
> > + } else {
> > + msg->id = CMD_TX_STD_MESSAGE;
> > + msg->u.tx_can.msg[0] = (cf->can_id >> 6) & 0x1f;
> > + msg->u.tx_can.msg[1] = cf->can_id & 0x3f;
> > + }
> > +
> > + msg->u.tx_can.msg[5] = cf->can_dlc;
> > + memcpy(&msg->u.tx_can.msg[6], cf->data, cf->can_dlc);
> > +
> > + if (cf->can_id & CAN_RTR_FLAG)
> > + msg->u.tx_can.flags |= MSG_FLAG_REMOTE_FRAME;
> > +
> > + for (i = 0; i < MAX_TX_URBS; i++) {
> ARRAY_SIZE
Ok.
> > + if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) {
> > + context = &priv->tx_contexts[i];
> > + break;
> > + }
> > + }
> > +
> > + if (!context) {
> > + netdev_warn(netdev, "cannot find free context\n");
> > + ret = NETDEV_TX_BUSY;
> > + goto releasebuf;
> > + }
> > +
> > + context->priv = priv;
> > + context->echo_index = i;
> > + context->dlc = cf->can_dlc;
> > +
> > + msg->u.tx_can.tid = context->echo_index;
> > +
> > + usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, 1),
> > + buf, msg->len,
> > + kvaser_usb_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);
> > +
> > + if (atomic_read(&priv->active_tx_urbs) >= MAX_TX_URBS)
> > + netif_stop_queue(netdev);
> > +
> > + err = usb_submit_urb(urb, GFP_ATOMIC);
> > + if (unlikely(err)) {
> > + can_free_echo_skb(netdev, context->echo_index);
> > +
> > + atomic_dec(&priv->active_tx_urbs);
> > + 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;
> > + }
> > +
> > + netdev->trans_start = jiffies;
> > +
> > + usb_free_urb(urb);
> > +
> > + return NETDEV_TX_OK;
> > +
> > +releasebuf:
> > + usb_free_coherent(dev->udev, sizeof(struct kvaser_msg),
> > + buf, urb->transfer_dma);
> > +nobufmem:
> > + usb_free_urb(urb);
> > +nourbmem:
> > + 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,
> > +};
> > +
> > +static struct can_bittiming_const kvaser_usb_bittiming_const = {
> > + .name = "kvaser_usb",
> > + .tseg1_min = KVASER_USB_TSEG1_MIN,
> > + .tseg1_max = KVASER_USB_TSEG1_MAX,
> > + .tseg2_min = KVASER_USB_TSEG2_MIN,
> > + .tseg2_max = KVASER_USB_TSEG2_MAX,
> > + .sjw_max = KVASER_USB_SJW_MAX,
> > + .brp_min = KVASER_USB_BRP_MIN,
> > + .brp_max = KVASER_USB_BRP_MAX,
> > + .brp_inc = KVASER_USB_BRP_INC,
> > +};
> > +
> > +static int kvaser_usb_set_bittiming(struct net_device *netdev)
> > +{
> > + struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
> > + struct can_bittiming *bt = &priv->can.bittiming;
> > + struct kvaser_usb *dev = priv->dev;
> > + struct kvaser_msg msg;
> > +
> > + msg.id = CMD_SET_BUS_PARAMS;
> > + msg.len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_busparams);
> > + msg.u.busparams.channel = priv->channel;
> > + msg.u.busparams.tid = 0xff;
> > + msg.u.busparams.bitrate = bt->bitrate;
>
> bitrate is le32
Indeed ! I'll fix this.
>
> > + msg.u.busparams.sjw = bt->sjw;
> > + msg.u.busparams.tseg1 = bt->prop_seg + bt->phase_seg1;
> > + msg.u.busparams.tseg2 = bt->phase_seg2;
>
> C99 initializers, please
>
> > +
> > + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
> > + msg.u.busparams.no_samp = 3;
> > + else
> > + msg.u.busparams.no_samp = 1;
> > +
> > + return kvaser_usb_send_msg(dev, &msg);
> > +}
> > +
> > +static int kvaser_usb_set_mode(struct net_device *netdev,
> > + enum can_mode mode)
> > +{
> > + struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
> > +
> > + if (!priv->open_time)
> > + return -EINVAL;
> > +
> > + switch (mode) {
> > + case CAN_MODE_START:
> > + if (netif_queue_stopped(netdev))
> > + netif_wake_queue(netdev);
>
> No need to restart your USB device?
No. I don't think so.
The module continuously tries to transmit the frame and isn't stopped.
So there is no need to restart it if it has been explicitely stopped.
When it cannot transmit, the module try again and sends continuously
CMD_CAN_ERROR_EVENT frames until it succeeds to transmit the frame.
If the device is stopped with the command CMD_STOP_CHIP then it stops
sending these CMD_CAN_ERROR_EVENT.
Should I handle this in another manner?
>
> > + break;
> > + default:
> > + return -EOPNOTSUPP;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int kvaser_usb_get_berr_counter(const struct net_device *netdev,
> > + struct can_berr_counter *bec)
> > +{
> > + struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
> > +
> > + bec->txerr = priv->bec.txerr;
> > + bec->rxerr = priv->bec.rxerr;
> > +
> > + return 0;
> > +}
> > +
> > +static int kvaser_usb_init_one(struct usb_interface *intf,
> > + const struct usb_device_id *id, int channel)
> > +{
> > + struct kvaser_usb *dev = usb_get_intfdata(intf);
> > + struct net_device *netdev;
> > + struct kvaser_usb_net_priv *priv;
> > + int i, err;
> > +
> > + netdev = alloc_candev(sizeof(*priv), MAX_TX_URBS);
> > + if (!netdev) {
> > + dev_err(&intf->dev, "Cannot alloc candev\n");
> > + return -ENOMEM;
> > + }
> > +
> > + priv = netdev_priv(netdev);
> > +
> > + 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->dev = dev;
> > + priv->netdev = netdev;
> > + priv->channel = channel;
> > +
> > + priv->can.state = CAN_STATE_STOPPED;
> > + priv->can.clock.freq = CAN_USB_CLOCK;
> > + priv->can.bittiming_const = &kvaser_usb_bittiming_const;
> > + priv->can.do_set_bittiming = kvaser_usb_set_bittiming;
> > + priv->can.do_set_mode = kvaser_usb_set_mode;
> > + priv->can.do_get_berr_counter = kvaser_usb_get_berr_counter;
> > + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES;
> > + if (id->driver_info & KVASER_HAS_SILENT_MODE)
> > + priv->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY;
> > +
> > + netdev->flags |= IFF_ECHO;
> > +
> > + netdev->netdev_ops = &kvaser_usb_netdev_ops;
> > +
> > + SET_NETDEV_DEV(netdev, &intf->dev);
> > +
> > + err = register_candev(netdev);
> > + if (err) {
> > + dev_err(&intf->dev, "Failed to register can device\n");
> > + free_candev(netdev);
> > + return err;
> > + }
> > +
> > + dev->nets[channel] = priv;
> > + netdev_info(netdev, "device %s registered\n", netdev->name);
>
> netdev_info should take care of printing the device's name.
Ok.
> > +
> > + return 0;
> > +}
> > +
> > +static int kvaser_usb_probe(struct usb_interface *intf,
> > + const struct usb_device_id *id)
> > +{
> > + struct kvaser_usb *dev;
> > + int err = -ENOMEM;
> > + int i;
> > +
> > + dev = kzalloc(sizeof(*dev), GFP_KERNEL);
>
> Who will free dev on driver unload? Please make use of devm_kzalloc().
Ok. kfree is missing is disconnect().
I'll replace it by devm_kzalloc() and devm_free().
>
> > + if (!dev)
> > + return -ENOMEM;
> > +
> > + dev->udev = interface_to_usbdev(intf);
> > +
> > + init_usb_anchor(&dev->rx_submitted);
> > +
> > + usb_set_intfdata(intf, dev);
> > +
> > + if (kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, 0)) {
> > + dev_err(&intf->dev, "Cannot reset kvaser\n");
> > + goto error;
> > + }
> > +
> > + if (kvaser_usb_get_software_info(dev)) {
> > + dev_err(&intf->dev, "Cannot get software infos\n");
> > + goto error;
> > + }
> > +
> > + if (kvaser_usb_get_card_info(dev)) {
> > + dev_err(&intf->dev, "Cannot get card infos\n");
> > + goto error;
> > + }
> > +
> > + dev_dbg(&intf->dev, "Firmware version: %d.%d.%d\n",
> > + ((dev->fw_version >> 24) & 0xff),
> > + ((dev->fw_version >> 16) & 0xff),
> > + (dev->fw_version & 0xffff));
> > +
> > + for (i = 0; i < dev->nchannels; i++)
> > + kvaser_usb_init_one(intf, id, i);
> > +
> > + return 0;
> > +
> > +error:
> > + kfree(dev);
> > + return err;
> > +}
> > +
> > +static void kvaser_usb_disconnect(struct usb_interface *intf)
> > +{
> > + struct kvaser_usb *dev = usb_get_intfdata(intf);
> > + int i;
> > +
> > + usb_set_intfdata(intf, NULL);
> > +
> > + if (!dev)
> > + return;
> > +
> > + for (i = 0; i < dev->nchannels; i++) {
> > + if (!dev->nets[i])
> > + continue;
> > +
> > + unregister_netdev(dev->nets[i]->netdev);
> > + free_candev(dev->nets[i]->netdev);
> > + }
> > +
> > + kvaser_usb_unlink_all_urbs(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_DESCRIPTION("Can driver for Kvaser CAN/USB devices");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/drivers/net/can/usb/kvaser_usb.h b/drivers/net/can/usb/kvaser_usb.h
> > new file mode 100644
> > index 0000000..8e0b6ab
> > --- /dev/null
> > +++ b/drivers/net/can/usb/kvaser_usb.h
> > @@ -0,0 +1,237 @@
> > +#ifndef _KVASER_USB_H_
> > +#define _KVASER_USB_H_
> > +
> > +#define MAX_TX_URBS 16
> Please no tab between define and macro name
Ok I didn't know it was not allowed... checkpatch didn't complain.
> > +#define MAX_RX_URBS 4
> > +#define START_TIMEOUT 1000
> > +#define STOP_TIMEOUT 1000
> > +#define USB_SEND_TIMEOUT 1000
> > +#define USB_RECEIVE_TIMEOUT 1000
> > +#define RX_BUFFER_SIZE 3072
> > +#define CAN_USB_CLOCK 8000000
> > +#define MAX_NET_DEVICES 3
> > +
> > +/* Kvaser USB devices */
> > +#define KVASER_VENDOR_ID 0x0bfd
> > +#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
> > +
> > +/* USB devices features */
> > +#define KVASER_HAS_SILENT_MODE (1 << 0)
> pleae use BIT(0)
> > +
> > +/* Message header size */
> > +#define MSG_HEADER_LEN 2
> > +
> > +/* Can message flags */
> > +#define MSG_FLAG_ERROR_FRAME (1 << 0)
> > +#define MSG_FLAG_OVERRUN (1 << 1)
> > +#define MSG_FLAG_NERR (1 << 2)
> > +#define MSG_FLAG_WAKEUP (1 << 3)
> > +#define MSG_FLAG_REMOTE_FRAME (1 << 4)
> > +#define MSG_FLAG_RESERVED (1 << 5)
> > +#define MSG_FLAG_TX_ACK (1 << 6)
> > +#define MSG_FLAG_TX_REQUEST (1 << 7)
> > +
> > +/* Can states */
> > +#define M16C_STATE_BUS_RESET (1 << 0)
> > +#define M16C_STATE_BUS_ERROR (1 << 4)
> > +#define M16C_STATE_BUS_PASSIVE (1 << 5)
> > +#define M16C_STATE_BUS_OFF (1 << 6)
> > +
> > +/* Can msg 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_GET_CTRL_MODE 22
> > +#define CMD_GET_CTRL_MODE_REPLY 23
> > +#define CMD_RESET_CHIP 24
> > +#define CMD_RESET_CHIP_REPLY 25
> > +#define CMD_START_CHIP 26
> > +#define CMD_START_CHIP_REPLY 27
> > +#define CMD_STOP_CHIP 28
> > +#define CMD_STOP_CHIP_REPLY 29
> > +#define CMD_GET_CARD_INFO2 32
> > +#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_USB_THROTTLE 77
> > +
> > +/* error factors */
> > +#define M16C_EF_ACKE (1 << 0)
> > +#define M16C_EF_CRCE (1 << 1)
> > +#define M16C_EF_FORME (1 << 2)
> > +#define M16C_EF_STFE (1 << 3)
> > +#define M16C_EF_BITE0 (1 << 4)
> > +#define M16C_EF_BITE1 (1 << 5)
> > +#define M16C_EF_RCVE (1 << 6)
> > +#define M16C_EF_TRE (1 << 7)
> > +
> > +/* bittiming parameters */
> > +#define KVASER_USB_TSEG1_MIN 1
> > +#define KVASER_USB_TSEG1_MAX 16
> > +#define KVASER_USB_TSEG2_MIN 1
> > +#define KVASER_USB_TSEG2_MAX 8
> > +#define KVASER_USB_SJW_MAX 4
> > +#define KVASER_USB_BRP_MIN 1
> > +#define KVASER_USB_BRP_MAX 64
> > +#define KVASER_USB_BRP_INC 1
> > +
> > +/* 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
> > +
> > +struct kvaser_msg_simple {
> > + u8 tid;
> > + u8 channel;
> > +} __packed;
> > +
> > +struct kvaser_msg_cardinfo {
> > + u8 tid;
> > + u8 nchannels;
> > + __le32 serial_number;
> > + __le32 padding;
> > + __le32 clock_resolution;
> > + __le32 mfgdate;
> > + u8 ean[8];
> > + u8 hw_revision;
> > + u8 usb_hs_mode;
> > + __le16 padding2;
> > +} __packed;
> > +
> > +struct kvaser_msg_cardinfo2 {
> > + u8 tid;
> > + u8 channel;
> > + u8 pcb_id[24];
> > + __le32 oem_unlock_code;
> > +} __packed;
> > +
> > +struct kvaser_msg_softinfo {
> > + u8 tid;
> > + u8 channel;
> > + __le32 sw_options;
> > + __le32 fw_version;
> > + __le16 max_outstanding_tx;
> > + __le16 padding[9];
> > +} __packed;
> > +
> > +struct kvaser_msg_busparams {
> > + u8 tid;
> > + u8 channel;
> > + __le32 bitrate;
> > + u8 tseg1;
> > + u8 tseg2;
> > + u8 sjw;
> > + u8 no_samp;
> > +} __packed;
> > +
> > +struct kvaser_msg_tx_can {
> > + u8 channel;
> > + u8 tid;
> > + u8 msg[14];
> > + u8 padding;
> > + u8 flags;
> > +} __packed;
> > +
> > +struct kvaser_msg_rx_can {
> > + u8 channel;
> > + u8 flag;
> > + __le16 time[3];
> > + u8 msg[14];
> > +} __packed;
> > +
> > +struct kvaser_msg_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 kvaser_msg_tx_acknowledge {
> > + u8 channel;
> > + u8 tid;
> > + __le16 time[3];
> > + u8 flags;
> > + u8 time_offset;
> > +} __packed;
> > +
> > +struct kvaser_msg_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 kvaser_msg_ctrl_mode {
> > + u8 tid;
> > + u8 channel;
> > + u8 ctrl_mode;
> > + u8 padding[3];
> > +} __packed;
> > +
> > +struct kvaser_msg_flush_queue {
> > + u8 tid;
> > + u8 channel;
> > + u8 flags;
> > + u8 padding[3];
> > +} __packed;
> > +
> > +struct kvaser_msg {
> > + u8 len;
> > + u8 id;
> > + union {
> > + struct kvaser_msg_simple simple;
> > + struct kvaser_msg_cardinfo cardinfo;
> > + struct kvaser_msg_cardinfo2 cardinfo2;
> > + struct kvaser_msg_softinfo softinfo;
> > + struct kvaser_msg_busparams busparams;
> > + struct kvaser_msg_tx_can tx_can;
> > + struct kvaser_msg_rx_can rx_can;
> > + struct kvaser_msg_chip_state_event chip_state_event;
> > + struct kvaser_msg_tx_acknowledge tx_acknowledge;
> > + struct kvaser_msg_error_event error_event;
> > + struct kvaser_msg_ctrl_mode ctrl_mode;
> > + struct kvaser_msg_ctrl_mode flush_queue;
> > + } u;
> > +} __packed;
> > +
> > +#endif
> >
>
>
> --
> Pengutronix e.K. | Marc Kleine-Budde |
> Industrial Linux Solutions | Phone: +49-231-2826-924 |
> Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
> Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
>
Thanks for the review.
Regards,
--
Olivier
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH] can: kvaser_usb: Add support for Kvaser CAN/USB devices
2012-07-30 13:33 ` Olivier Sobrie
@ 2012-07-31 9:56 ` Marc Kleine-Budde
2012-07-31 13:06 ` Olivier Sobrie
2012-08-05 20:41 ` Wolfgang Grandegger
0 siblings, 2 replies; 43+ messages in thread
From: Marc Kleine-Budde @ 2012-07-31 9:56 UTC (permalink / raw)
To: Olivier Sobrie; +Cc: Wolfgang Grandegger, linux-can, netdev
[-- Attachment #1: Type: text/plain, Size: 51547 bytes --]
On 07/30/2012 03:33 PM, Olivier Sobrie wrote:
> Hi Marc,
>
> On Mon, Jul 30, 2012 at 01:11:46PM +0200, Marc Kleine-Budde wrote:
>> On 07/30/2012 07:32 AM, Olivier Sobrie wrote:
>>> This driver provides support for several Kvaser CAN/USB devices.
>>> Such kind of devices supports up to three can network interfaces.
>>>
>>> It has been tested with a Kvaser USB Leaf Light (one network interface)
>>> connected to a pch_can interface.
>>> The firmware version of the Kvaser device was 2.5.205.
>>
>> Please add linux-usb@vger.kernel.org to Cc for review of the USB part.
>
> Ok I'll do it when I send the new version of the patch.
> But it might be a good idea to add an entry in the MAINTAINERS file so
> that when someone sends a patch they are aware of this when the
> get_maintainer script is invoked.
Interesting Idea. We should discuss this here, however we should not
bother the USB List when sending USB unrelated patches.
>> Please combine .h and .c file. Please make use of netdev_LEVEL() for
>> error printing, not dev_LEVEL().
>
> I'll combine the .c and .h.
> I used the netdev_LEVEL() everywhere it was possible. It requires to
> have access to a pointer to netdev which is not always possible;
> that's the reason why I used dev_LEVEL().
I see, you used it when channel is invalid. So you have obviously no netdev.
>> Please review if all members of the struct kvaser_msg are properly
>> aligned. You never access the struct kvaser_msg_* members directly, as
>> they are unaligned. Please check for le16 and le32 access. You missed to
>> convert the bitrate.
>
> Indeed. Thanks. I'll check if I didn't missed another one.
Tnx
>> Please check if your driver survives hot-unplugging while sending and
>> receiving CAN frames at maximum laod.
>
> I tested this with two Kvaser sending frames with "cangen can0 -g 0 -i"
> never saw a crash.
Please test send sending and receiving at the same time.
>> More comments inline,
>> regards, Marc
>>
>>> List of Kvaser devices supported by the driver:
>>> - Kvaser Leaf prototype (P010v2 and v3)
>>> - Kvaser Leaf Light (P010v3)
>>> - 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, Prototype
>>> - 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 OEM Mercury
>>> - Kvaser OEM Leaf
>>> - Kvaser USBcan R
>>>
>>> Signed-off-by: Olivier Sobrie <olivier@sobrie.be>
>>> ---
>>> drivers/net/can/usb/Kconfig | 33 ++
>>> drivers/net/can/usb/Makefile | 1 +
>>> drivers/net/can/usb/kvaser_usb.c | 1062 ++++++++++++++++++++++++++++++++++++++
>>> drivers/net/can/usb/kvaser_usb.h | 237 +++++++++
>>> 4 files changed, 1333 insertions(+)
>>> create mode 100644 drivers/net/can/usb/kvaser_usb.c
>>> create mode 100644 drivers/net/can/usb/kvaser_usb.h
>>>
>>> diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig
>>> index 0a68768..578955f 100644
>>> --- a/drivers/net/can/usb/Kconfig
>>> +++ b/drivers/net/can/usb/Kconfig
>>> @@ -13,6 +13,39 @@ config CAN_ESD_USB2
>>> This driver supports the CAN-USB/2 interface
>>> from esd electronic system design gmbh (http://www.esd.eu).
>>>
>>> +config CAN_KVASER_USB
>>> + tristate "Kvaser CAN/USB interface"
>>> + ---help---
>>> + This driver adds support for Kvaser CAN/USB devices like Kvaser
>>> + Leaf Light.
>>> +
>>> + The driver gives support for the following devices:
>>> + - Kvaser Leaf prototype (P010v2 and v3)
>>> + - Kvaser Leaf Light (P010v3)
>>> + - 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, Prototype
>>> + - 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 OEM Mercury
>>> + - Kvaser OEM Leaf
>>> + - Kvaser USBcan R
>>> +
>>> + If unsure, say N.
>>> +
>>> + To compile this driver as a module, choose M here: the
>>> + module will be called kvaser_usb.
>>> +
>>> config CAN_PEAK_USB
>>> tristate "PEAK PCAN-USB/USB Pro interfaces"
>>> ---help---
>>> diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile
>>> index da6d1d3..80a2ee4 100644
>>> --- a/drivers/net/can/usb/Makefile
>>> +++ b/drivers/net/can/usb/Makefile
>>> @@ -4,6 +4,7 @@
>>>
>>> obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o
>>> obj-$(CONFIG_CAN_ESD_USB2) += esd_usb2.o
>>> +obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o
>>> obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/
>>>
>>> ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
>>> diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c
>>> new file mode 100644
>>> index 0000000..4965480
>>> --- /dev/null
>>> +++ b/drivers/net/can/usb/kvaser_usb.c
>>> @@ -0,0 +1,1062 @@
>>> +/*
>>
>> Please add a license statement and probably your copyright:
>>
>> * This program is free software; you can redistribute it and/or
>> * modify it under the terms of the GNU General Public License as
>> * published by the Free Software Foundation version 2.
>>
>> You also should copy the copyright from the drivers you used:
>>
>>> + * Parts of this driver are based on the following:
>>> + * - Kvaser linux leaf driver (version 4.78)
>>
>> I just downloaded their driver and noticed that it's quite sparse on
>> stating the license the code is released under.
>> "doc/HTMLhelp/copyright.htx" is quite restrictive, the word GPL occurs 3
>> times, all in MODULE_LICENSE("GPL"). Running modinfo on the usbcan.ko
>> shows "license: GPL"
>
> I'll add the license statement.
> In fact it's the leaf.ko which is used for this device and it is under
> GPL as modinfo said.
I just talked to my boss and we're the same opinion, that
MODULE_LICENSE("GPL") is a technical term and not relevant if the
included license doesn't say a word about GPL. If the kvaser tarball
violates the GPL, however is written on different sheet of paper (as we
say in Germany).
So I cannot put my S-o-b under this driver as long as we haven't talked
to kvaser.
>>> + * - CAN driver for esd CAN-USB/2
>>> + */
>>> +
>>> +#include <linux/init.h>
>>> +#include <linux/completion.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 "kvaser_usb.h"
>>> +
>>> +struct kvaser_usb_tx_urb_context {
>>> + struct kvaser_usb_net_priv *priv;
>>
>> Huh - how does this work without forward declaration?
>
> It works.
Yes, obviously :)
> "In C and C++ it is possible to declare pointers to structs before
> declaring their struct layout, provided the pointers are not
> dereferenced--this is known as forward declaration."
>
> See http://www.linuxtopia.org/online_books/an_introduction_to_gcc/gccintro_94.html
Thanks for the link.
>>
>>> + u32 echo_index;
>>> + int dlc;
>>> +};
>>> +
>>> +struct kvaser_usb {
>>> + struct usb_device *udev;
>>> + struct kvaser_usb_net_priv *nets[MAX_NET_DEVICES];
>>> +
>>> + struct usb_anchor rx_submitted;
>>> +
>>> + u32 fw_version;
>>> + unsigned int nchannels;
>>> +
>>> + bool rxinitdone;
>>> +};
>>> +
>>> +struct kvaser_usb_net_priv {
>>> + struct can_priv can;
>>> +
>>> + atomic_t active_tx_urbs;
>>> + struct usb_anchor tx_submitted;
>>> + struct kvaser_usb_tx_urb_context tx_contexts[MAX_TX_URBS];
>>> +
>>> + int open_time;
>>
>> please remove open_time
>
> Ok.
>
>>
>>> + struct completion start_stop_comp;
>>> +
>>> + struct kvaser_usb *dev;
>>> + struct net_device *netdev;
>>> + int channel;
>>> + struct can_berr_counter bec;
>>> +};
>>> +
>>> +static struct usb_device_id kvaser_usb_table[] = {
>>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_DEVEL_PRODUCT_ID) },
>>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_PRODUCT_ID) },
>>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_PRODUCT_ID),
>>> + .driver_info = KVASER_HAS_SILENT_MODE },
>>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_PRODUCT_ID),
>>> + .driver_info = KVASER_HAS_SILENT_MODE },
>>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LS_PRODUCT_ID),
>>> + .driver_info = KVASER_HAS_SILENT_MODE },
>>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_SWC_PRODUCT_ID),
>>> + .driver_info = KVASER_HAS_SILENT_MODE },
>>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LIN_PRODUCT_ID),
>>> + .driver_info = KVASER_HAS_SILENT_MODE },
>>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_LS_PRODUCT_ID),
>>> + .driver_info = KVASER_HAS_SILENT_MODE },
>>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_SWC_PRODUCT_ID),
>>> + .driver_info = KVASER_HAS_SILENT_MODE },
>>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_DEVEL_PRODUCT_ID),
>>> + .driver_info = KVASER_HAS_SILENT_MODE },
>>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSHS_PRODUCT_ID),
>>> + .driver_info = KVASER_HAS_SILENT_MODE },
>>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_UPRO_HSHS_PRODUCT_ID) },
>>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_GI_PRODUCT_ID) },
>>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_OBDII_PRODUCT_ID),
>>> + .driver_info = KVASER_HAS_SILENT_MODE },
>>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSLS_PRODUCT_ID) },
>>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_CH_PRODUCT_ID) },
>>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_BLACKBIRD_SPRO_PRODUCT_ID) },
>>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_MERCURY_PRODUCT_ID) },
>>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_LEAF_PRODUCT_ID) },
>>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_CAN_R_PRODUCT_ID) },
>>> + { }
>>> +};
>>> +MODULE_DEVICE_TABLE(usb, kvaser_usb_table);
>>> +
>>> +static int kvaser_usb_send_msg(const struct kvaser_usb *dev,
>>> + struct kvaser_msg *msg)
>> inline?
>
> Ok.
>
>>> +{
>>> + int actual_len;
>>> +
>>> + return usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 1),
>> ^
>> Can you please introduce a #define for this.
>
> Ok. No problem.
>
>>
>>> + msg, msg->len, &actual_len, USB_SEND_TIMEOUT);
>>> +}
>>> +
>>> +static int kvaser_usb_wait_msg(const struct kvaser_usb *dev, u8 id,
>>> + struct kvaser_msg *msg)
>>> +{
>>> + struct kvaser_msg *tmp;
>>> + void *buf;
>>> + int actual_len;
>>> + int err;
>>> + int pos = 0;
>>> +
>>> + buf = kzalloc(RX_BUFFER_SIZE, GFP_KERNEL);
>>> + if (!buf)
>>> + return -ENOMEM;
>>> +
>>> + err = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, 129),
>> ^^^
>> dito
>
> Ok too.
>
>>
>>> + buf, RX_BUFFER_SIZE, &actual_len,
>>> + USB_RECEIVE_TIMEOUT);
>>> + if (err < 0) {
>>> + kfree(buf);
>>> + return err;
>>> + }
>>> +
>>> + while (pos < actual_len) {
>>
>> Please check that pos + sizeof(*msg) is < actual_len, as you fill access
>> it later.
>
> I'll instead perform a check on 'pos + tmp->len < actual_len' and copy
> only tmp->len instead of sizeof(*msg).
> Thanks.
Even better, saves some bytes to be copied. Take care not to deref tmp,
unless you checked that tmp is in valid memory.
>>
>>> + tmp = buf + pos;
>>> +
>>> + if (!tmp->len)
>>> + break;
>>> +
>>> + if (tmp->id == id) {
>>> + memcpy(msg, tmp, sizeof(*msg));
>>> + kfree(buf);
>>> + return 0;
>>> + }
>>> +
>>> + pos += tmp->len;
>>> + }
>>> +
>>> + kfree(buf);
>>> +
>>> + return -EINVAL;
>>> +}
>>> +
>>> +static int kvaser_usb_send_simple_msg(const struct kvaser_usb *dev,
>>> + u8 msg_id, int channel)
>>> +{
>>> + struct kvaser_msg msg;
>>> + int err;
>>> +
>>> + msg.len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple);
>>> + msg.id = msg_id;
>>> + msg.u.simple.channel = channel;
>>> + msg.u.simple.tid = 0xff;
>>
>> Please use C99 struct initializer.
>>
>> struct kvaser_msg msg = {
>> .len = ,
>> .id =,
>> };
>
> Ok.
>
>>
>>
>>> +
>>> + err = kvaser_usb_send_msg(dev, &msg);
>>
>> just:
>> return err;
>
> Ok.
>
>>
>>> + if (err)
>>> + return err;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int kvaser_usb_get_software_info(struct kvaser_usb *dev)
>>> +{
>>> + struct kvaser_msg msg;
>>> + int err;
>>> +
>>> + err = kvaser_usb_send_simple_msg(dev, CMD_GET_SOFTWARE_INFO, 0);
>>> + if (err)
>>> + return err;
>>> +
>>> + err = kvaser_usb_wait_msg(dev, CMD_GET_SOFTWARE_INFO_REPLY, &msg);
>>> + if (err)
>>> + return err;
>>> +
>>> + dev->fw_version = le32_to_cpu(msg.u.softinfo.fw_version);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int kvaser_usb_get_card_info(struct kvaser_usb *dev)
>>> +{
>>> + struct kvaser_msg msg;
>>> + int err;
>>> +
>>> + err = kvaser_usb_send_simple_msg(dev, CMD_GET_CARD_INFO, 0);
>>> + if (err)
>>> + return err;
>>> +
>>> + err = kvaser_usb_wait_msg(dev, CMD_GET_CARD_INFO_REPLY, &msg);
>>> + if (err)
>>> + return err;
>>> +
>>> + dev->nchannels = msg.u.cardinfo.nchannels;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static void kvaser_usb_tx_acknowledge(const struct kvaser_usb *dev,
>>> + const struct kvaser_msg *msg)
>>> +{
>>> + struct net_device_stats *stats;
>>> + struct kvaser_usb_tx_urb_context *context;
>>> + struct kvaser_usb_net_priv *priv;
>>> + u8 channel = msg->u.tx_acknowledge.channel;
>>> + u8 tid = msg->u.tx_acknowledge.tid;
>>> +
>>> + if (channel >= dev->nchannels) {
>>> + dev_err(dev->udev->dev.parent,
>>> + "Invalid channel number (%d)\n", channel);
>>> + return;
>>> + }
>>
>> can you do a check for (channel >= dev->nchannels), in a central place?
>> e.g. kvaser_usb_handle_message()?
>
> The problem is that channel is not always at the same place in the
> messages I get from the hardware. 'tid' and 'channel' are inverted for
> tx and rx frames.
> e.g.
Grr...who's written that firmware :D
>
> struct kvaser_msg_tx_can {
> u8 channel;
> u8 tid;
> u8 msg[14];
> u8 padding;
> u8 flags;
> } __packed;
>
> struct kvaser_msg_busparams {
> u8 tid;
> u8 channel;
> __le32 bitrate;
> u8 tseg1;
> u8 tseg2;
> u8 sjw;
> u8 no_samp;
> } __packed;
>
>>
>>> +
>>> + priv = dev->nets[channel];
>>> +
>>> + if (!netif_device_present(priv->netdev))
>>> + return;
>>> +
>>> + stats = &priv->netdev->stats;
>>> +
>>> + context = &priv->tx_contexts[tid % MAX_TX_URBS];
>>> +
>>> + /*
>>> + * It looks like the firmware never sets the flags field of the
>>> + * tx_acknowledge frame and never reports a transmit failure.
>>> + * If the can message can't be transmited (e.g. incompatible
>>> + * bitrates), a frame CMD_CAN_ERROR_EVENT is sent (with a null
>>> + * tid) and the firmware tries to transmit again the packet until
>>> + * it succeeds. Once the packet is successfully transmitted, then
>>> + * the tx_acknowledge frame is sent.
>>> + */
>>> +
>>> + stats->tx_packets++;
>>> + stats->tx_bytes += context->dlc;
>>> + can_get_echo_skb(priv->netdev, context->echo_index);
>>> +
>>> + context->echo_index = MAX_TX_URBS;
>>> + atomic_dec(&priv->active_tx_urbs);
>>> +
>>> + netif_wake_queue(priv->netdev);
>>> +}
>>> +
>>> +static void kvaser_usb_rx_error(const struct kvaser_usb *dev,
>>> + const struct kvaser_msg *msg)
>>> +{
>>> + struct can_frame *cf;
>>> + struct sk_buff *skb;
>>> + struct net_device_stats *stats;
>>> + struct kvaser_usb_net_priv *priv;
>>> + u8 channel, status, txerr, rxerr;
>>> +
>>> + if (msg->id == CMD_CAN_ERROR_EVENT) {
>>> + channel = msg->u.error_event.channel;
>>> + status = msg->u.error_event.status;
>>> + txerr = msg->u.error_event.tx_errors_count;
>>> + rxerr = msg->u.error_event.rx_errors_count;
>>> + } else {
>>> + channel = msg->u.chip_state_event.channel;
>>> + status = msg->u.chip_state_event.status;
>>> + txerr = msg->u.chip_state_event.tx_errors_count;
>>> + rxerr = msg->u.chip_state_event.rx_errors_count;
>>> + }
>>> +
>>> + if (channel >= dev->nchannels) {
>>> + dev_err(dev->udev->dev.parent,
>>> + "Invalid channel number (%d)\n", channel);
>>> + return;
>>> + }
>>> +
>>> + priv = dev->nets[channel];
>>> + stats = &priv->netdev->stats;
>>> +
>>> + skb = alloc_can_err_skb(priv->netdev, &cf);
>>> + if (!skb) {
>>> + stats->rx_dropped++;
>>> + return;
>>> + }
>>> +
>>> + if ((status & M16C_STATE_BUS_OFF) ||
>>> + (status & M16C_STATE_BUS_RESET)) {
>>> + priv->can.state = CAN_STATE_BUS_OFF;
>>> + cf->can_id |= CAN_ERR_BUSOFF;
>>> + can_bus_off(priv->netdev);
you should increment priv->can.can_stats.bus_off
What does the firmware do in this state? Does it automatically try to
recover and try to send the outstanding frames?
If so, you should turn of the CAN interface, it possible. See:
http://lxr.free-electrons.com/source/drivers/net/can/at91_can.c#L986
Please test Bus-Off behaviour:
- setup working CAN network
- short circuit CAN-H and CAN-L wires
- start "candump any,0:0,#FFFFFFFF" on one shell
- send one can frame on the other
then
- remove the short circuit
- see if the can frame is transmitted to the other side
- it should show up as an echo'ed CAN frame on the sender side
Repeat the same test with disconnecting CAN-H and CAN-L from the other
CAN station instead of short circuit.
Please send the output from candump.
>>> + } else if (status & M16C_STATE_BUS_ERROR) {
>>> + priv->can.state = CAN_STATE_ERROR_WARNING;
>>> + priv->can.can_stats.error_warning++;
>>> + } else if (status & M16C_STATE_BUS_PASSIVE) {
>>> + priv->can.state = CAN_STATE_ERROR_PASSIVE;
>>> + priv->can.can_stats.error_passive++;
>>> + } else {
>>> + priv->can.state = CAN_STATE_ERROR_ACTIVE;
>>> + cf->can_id |= CAN_ERR_PROT;
>>> + cf->data[2] = CAN_ERR_PROT_ACTIVE;
>>> + }
>>> +
>>> + if (msg->id == CMD_CAN_ERROR_EVENT) {
>>> + u8 error_factor = msg->u.error_event.error_factor;
>>> +
>>> + priv->can.can_stats.bus_error++;
>>> + stats->rx_errors++;
>>> +
>>> + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
>>> +
>>> + 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;
>>> + }
>>> +
>>> + if (error_factor & M16C_EF_ACKE)
>>> + cf->data[3] |= (CAN_ERR_PROT_LOC_ACK |
>>> + CAN_ERR_PROT_LOC_ACK_DEL);
>>> + if (error_factor & M16C_EF_CRCE)
>>> + cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ |
>>> + CAN_ERR_PROT_LOC_CRC_DEL);
>>> + if (error_factor & M16C_EF_FORME)
>>> + cf->data[2] |= CAN_ERR_PROT_FORM;
>>> + if (error_factor & M16C_EF_STFE)
>>> + cf->data[2] |= CAN_ERR_PROT_STUFF;
>>> + if (error_factor & M16C_EF_BITE0)
>>> + cf->data[2] |= CAN_ERR_PROT_BIT0;
>>> + if (error_factor & M16C_EF_BITE1)
>>> + cf->data[2] |= CAN_ERR_PROT_BIT1;
>>> + if (error_factor & M16C_EF_TRE)
>>> + cf->data[2] |= CAN_ERR_PROT_TX;
>>> + }
>>> +
>>> + cf->data[6] = txerr;
>>> + cf->data[7] = rxerr;
>>> +
>>> + netif_rx(skb);
>>> +
>>> + priv->bec.txerr = txerr;
>>> + priv->bec.rxerr = rxerr;
>>> +
>>> + stats->rx_packets++;
>>> + stats->rx_bytes += cf->can_dlc;
>>> +}
>>> +
>>> +static void kvaser_usb_rx_can_msg(const struct kvaser_usb *dev,
>>> + const struct kvaser_msg *msg)
>>> +{
>>> + struct kvaser_usb_net_priv *priv;
>>> + struct can_frame *cf;
>>> + struct sk_buff *skb;
>>> + struct net_device_stats *stats;
>>> + u8 channel = msg->u.rx_can.channel;
>>> +
>>> + if (channel >= dev->nchannels) {
>>> + dev_err(dev->udev->dev.parent,
>>> + "Invalid channel number (%d)\n", channel);
>>> + return;
>>> + }
>>> +
>>> + priv = dev->nets[channel];
>>> + stats = &priv->netdev->stats;
>>> +
>>> + skb = alloc_can_skb(priv->netdev, &cf);
>>> + if (skb == NULL) {
>>> + stats->tx_dropped++;
>>> + return;
>>> + }
>>> +
>>> + cf->can_id = ((msg->u.rx_can.msg[0] & 0x1f) << 6) |
>>> + (msg->u.rx_can.msg[1] & 0x3f);
>>> + cf->can_dlc = get_can_dlc(msg->u.rx_can.msg[5]);
>>> +
>>> + if (msg->id == CMD_RX_EXT_MESSAGE) {
>>> + cf->can_id <<= 18;
>>> + cf->can_id += ((msg->u.rx_can.msg[2] & 0x0f) << 14) |
>> |=
>>
>> is more appropriate here
>
> Ok.
>
>>
>>> + ((msg->u.rx_can.msg[3] & 0xff) << 6) |
>>> + (msg->u.rx_can.msg[4] & 0x3f);
>>> + cf->can_id |= CAN_EFF_FLAG;
>>> + }
>>> +
>>> + if (msg->u.rx_can.flag & MSG_FLAG_REMOTE_FRAME) {
>>> + cf->can_id |= CAN_RTR_FLAG;
>>> + } else if (msg->u.rx_can.flag & (MSG_FLAG_ERROR_FRAME |
>>> + MSG_FLAG_NERR)) {
>>> + cf->can_id |= CAN_ERR_FLAG;
>>> + cf->can_dlc = CAN_ERR_DLC;
>>
>> What kind of error is this? Can you set cf->data? What about the
>> original cd->can_id? What about the stats->rx_*error* stats?
>
> Good question I've to take a look to this.
>
>>
>>> + } else if (msg->u.rx_can.flag & MSG_FLAG_OVERRUN) {
>>> + cf->can_id = CAN_ERR_FLAG | CAN_ERR_CRTL;
>>> + cf->can_dlc = CAN_ERR_DLC;
>>> + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
>>> +
>>> + stats->rx_over_errors++;
>>> + stats->rx_errors++;
>>> + } else if (!msg->u.rx_can.flag) {
>>> + memcpy(cf->data, &msg->u.rx_can.msg[6], cf->can_dlc);
>>> + } else {
>>> + kfree_skb(skb);
>>> + return;
>>> + }
>>> +
>>> + netif_rx(skb);
>>> +
>>> + stats->rx_packets++;
>>> + stats->rx_bytes += cf->can_dlc;
>>> +}
>>> +
>>> +static void kvaser_usb_start_stop_chip_reply(const struct kvaser_usb *dev,
>>> + const struct kvaser_msg *msg)
>>> +{
>>> + struct kvaser_usb_net_priv *priv;
>>> + u8 channel = msg->u.simple.channel;
>>> +
>>> + if (channel >= dev->nchannels) {
>>> + dev_err(dev->udev->dev.parent,
>>> + "Invalid channel number (%d)\n", channel);
>>> + return;
>>> + }
>>> +
>>> + priv = dev->nets[channel];
>>> +
>>> + complete(&priv->start_stop_comp);
>>> +}
>>> +
>>> +static void kvaser_usb_handle_message(const struct kvaser_usb *dev,
>>> + const struct kvaser_msg *msg)
>>> +{
>>> + switch (msg->id) {
>>> + case CMD_START_CHIP_REPLY:
>>> + case CMD_STOP_CHIP_REPLY:
>>> + kvaser_usb_start_stop_chip_reply(dev, msg);
>>> + break;
>>> +
>>> + case CMD_RX_STD_MESSAGE:
>>> + case CMD_RX_EXT_MESSAGE:
>>> + kvaser_usb_rx_can_msg(dev, msg);
>>> + break;
>>> +
>>> + case CMD_CHIP_STATE_EVENT:
>>> + case CMD_CAN_ERROR_EVENT:
>>> + kvaser_usb_rx_error(dev, msg);
>>> + break;
>>> +
>>> + case CMD_TX_ACKNOWLEDGE:
>>> + kvaser_usb_tx_acknowledge(dev, msg);
>>> + break;
>>> +
>>> + default:
>>> + dev_warn(dev->udev->dev.parent,
>>> + "Unhandled message (%d)\n", msg->id);
>>> + break;
>>> + }
>>> +}
>>> +
>>> +static void kvaser_usb_read_bulk_callback(struct urb *urb)
>>> +{
>>> + struct kvaser_usb *dev = urb->context;
>>> + struct kvaser_msg *msg;
>>> + int pos = 0;
>>> + int err, i;
>>> +
>>> + switch (urb->status) {
>>> + case 0:
>>> + break;
>>> + case -ENOENT:
>>> + 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) {
>>
>> please check here for pos + sizeof(*msg), too
>
> Same as above.
>
>>
>>> + msg = urb->transfer_buffer + pos;
>>> +
>>> + if (!msg->len)
>>> + break;
>>> +
>>> + kvaser_usb_handle_message(dev, msg);
>>> +
>>> + if (pos > urb->actual_length) {
>>> + dev_err(dev->udev->dev.parent, "Format error\n");
>>> + break;
>>> + }
>>> +
>>> + pos += msg->len;
>>> + }
>>> +
>>> +resubmit_urb:
>>> + usb_fill_bulk_urb(urb, dev->udev, usb_rcvbulkpipe(dev->udev, 129),
>> ^^^
>>
>> use #define
>
> Ok.
>
>>
>>> + urb->transfer_buffer, 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->udev->dev.parent,
>>> + "Failed resubmitting read bulk urb: %d\n", err);
>>> + }
>>> +
>>> + return;
>>> +}
>>> +
>>> +static int kvaser_usb_setup_rx_urbs(struct kvaser_usb *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;
>>> +
>>> + urb = usb_alloc_urb(0, GFP_KERNEL);
>>> + if (!urb) {
>>> + dev_warn(dev->udev->dev.parent,
>>> + "No memory left for URBs\n");
>>> + err = -ENOMEM;
>>> + break;
>>> + }
>>> +
>>> + buf = usb_alloc_coherent(dev->udev, RX_BUFFER_SIZE,
>>> + GFP_KERNEL, &urb->transfer_dma);
>>> + if (!buf) {
>>> + dev_warn(dev->udev->dev.parent,
>>> + "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, 129),
>>
>> use #define
>
> Ok.
>
>>
>>> + buf, RX_BUFFER_SIZE,
>>> + kvaser_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);
>>> + break;
>>> + }
>>> +
>>> + usb_free_urb(urb);
>>> + }
>>> +
>>> + if (i == 0) {
>>> + dev_warn(dev->udev->dev.parent,
>>> + "Cannot setup read URBs, error %d\n", err);
>>> + return err;
>>> + } else if (i < MAX_RX_URBS) {
>>> + dev_warn(dev->udev->dev.parent,
>>> + "RX performances may be slow\n");
>>> + }
>>> +
>>> + dev->rxinitdone = true;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int kvaser_usb_set_opt_mode(const struct kvaser_usb_net_priv *priv)
>>> +{
>>> + struct kvaser_msg msg;
>>> +
>>> + memset(&msg, 0x00, sizeof(msg));
>>> + msg.id = CMD_SET_CTRL_MODE;
>>> + msg.len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_ctrl_mode);
>>> + msg.u.ctrl_mode.tid = 0xff;
>>> + msg.u.ctrl_mode.channel = priv->channel;
>>
>> please use C99 struct initializers
>
> Ok.
>
>>
>>> +
>>> + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
>>> + msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_SILENT;
>>> + else
>>> + msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_NORMAL;
>>> +
>>> + return kvaser_usb_send_msg(priv->dev, &msg);
>>> +}
>>> +
>>> +static int kvaser_usb_start_chip(struct kvaser_usb_net_priv *priv)
>>> +{
>>> + int err;
>>> +
>>> + init_completion(&priv->start_stop_comp);
>>> +
>>> + err = kvaser_usb_send_simple_msg(priv->dev, CMD_START_CHIP,
>>> + priv->channel);
>>> + if (err)
>>> + return err;
>>> +
>>> + if (!wait_for_completion_timeout(&priv->start_stop_comp,
>>> + msecs_to_jiffies(START_TIMEOUT)))
>>> + return -ETIMEDOUT;
>>> +
>>> + 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;
>>> + int err;
>>> +
>>> + err = open_candev(netdev);
>>> + if (err)
>>> + return err;
>>> +
>>> + err = kvaser_usb_setup_rx_urbs(dev);
>>> + if (err)
>>> + return err;
>>> +
>>> + err = kvaser_usb_set_opt_mode(priv);
>>> + if (err)
>>> + return err;
>>> +
>>> + err = kvaser_usb_start_chip(priv);
>>> + if (err) {
>>> + netdev_warn(netdev, "Cannot start device, error %d\n", err);
>>> + close_candev(netdev);
>>> + return err;
>>> + }
>>> +
>>> + priv->can.state = CAN_STATE_ERROR_ACTIVE;
>>> + priv->open_time = jiffies;
>>> + netif_start_queue(netdev);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static void kvaser_usb_unlink_tx_urbs(struct kvaser_usb_net_priv *priv)
>>> +{
>>> + int i;
>>> +
>>> + usb_kill_anchored_urbs(&priv->tx_submitted);
>>> + atomic_set(&priv->active_tx_urbs, 0);
>>> +
>>> + for (i = 0; i < MAX_TX_URBS; i++)
>> ARRAY_SIZE(priv->tx_contexts) instead of MAX_TX_URBS
>
> Ok.
>
>>> + priv->tx_contexts[i].echo_index = MAX_TX_URBS;
>>> +}
>>> +
>>> +static void kvaser_usb_unlink_all_urbs(struct kvaser_usb *dev)
>>> +{
>>> + int i;
>>> +
>>> + usb_kill_anchored_urbs(&dev->rx_submitted);
>>> +
>>> + for (i = 0; i < MAX_NET_DEVICES; i++) {
>> ARRAY_SIZE()
>>> + struct kvaser_usb_net_priv *priv = dev->nets[i];
>>> +
>>> + if (priv)
>>> + kvaser_usb_unlink_tx_urbs(priv);
>>> + }
>>> +}
>>> +
>>> +static int kvaser_usb_stop_chip(struct kvaser_usb_net_priv *priv)
>>> +{
>>> + int err;
>>> +
>>> + init_completion(&priv->start_stop_comp);
>>> +
>>> + err = kvaser_usb_send_simple_msg(priv->dev, CMD_STOP_CHIP,
>>> + priv->channel);
>>> + if (err)
>>> + return err;
>>> +
>>> + if (!wait_for_completion_timeout(&priv->start_stop_comp,
>>> + msecs_to_jiffies(STOP_TIMEOUT)))
>>> + return -ETIMEDOUT;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int kvaser_usb_flush_queue(struct kvaser_usb_net_priv *priv)
>>> +{
>>> + struct kvaser_msg msg;
>>> +
>>> + memset(&msg, 0x00, sizeof(msg));
>>> + msg.id = CMD_FLUSH_QUEUE;
>>> + msg.len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_flush_queue);
>>> + msg.u.flush_queue.channel = priv->channel;
>> C99 initialziers, please
>
> Ok.
>
>>> +
>>> + return kvaser_usb_send_msg(priv->dev, &msg);
>>> +}
>>> +
>>> +static int kvaser_usb_close(struct net_device *netdev)
>>> +{
>>> + struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
>>> + int err;
>>> +
>>> + netif_stop_queue(netdev);
>>> +
>>> + err = kvaser_usb_flush_queue(priv);
>>> + if (err)
>>> + netdev_warn(netdev, "Cannot flush queue, error %d\n", err);
>>> +
>>> + err = kvaser_usb_stop_chip(priv);
>>> + if (err) {
>>> + netdev_warn(netdev, "Cannot stop device, error %d\n", err);
>>> + return err;
>>> + }
>>> +
>>> + kvaser_usb_unlink_tx_urbs(priv);
>>> +
>>> + priv->can.state = CAN_STATE_STOPPED;
>>> + close_candev(priv->netdev);
>>> + priv->open_time = 0;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +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;
>>> +
>>> + usb_free_coherent(urb->dev, urb->transfer_buffer_length,
>>> + 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);
>>> +
>>> + netdev->trans_start = jiffies;
>>
>> Is trans_start needed? at least for non-usb devices it works without.
>
> I don't know, I'll try to figure this out.
> I see it's used in the two others CAN/USB drivers, 'ems_usb.c' and
> 'esd_usb2.c'
>
>>
>>> +}
>>> +
>>> +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;
>>> + struct net_device_stats *stats = &netdev->stats;
>>> + struct can_frame *cf = (struct can_frame *)skb->data;
>>> + struct kvaser_usb_tx_urb_context *context = NULL;
>>> + struct urb *urb;
>>> + void *buf;
>>> + struct kvaser_msg *msg;
>>> + int i, err;
>>> + int ret = NETDEV_TX_OK;
>>> +
>>> + if (can_dropped_invalid_skb(netdev, skb))
>>> + return NETDEV_TX_OK;
>>> +
>>> + urb = usb_alloc_urb(0, GFP_ATOMIC);
>>> + if (!urb) {
>>> + netdev_err(netdev, "No memory left for URBs\n");
>>> + stats->tx_dropped++;
>>> + dev_kfree_skb(skb);
>>> + goto nourbmem;
>>> + }
>>> +
>>> + buf = usb_alloc_coherent(dev->udev, sizeof(struct kvaser_msg),
>>> + 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 kvaser_msg *)buf;
>>> + msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_tx_can);
>>> + msg->u.tx_can.flags = 0;
>>> + msg->u.tx_can.channel = priv->channel;
>>> +
>>> + if (cf->can_id & CAN_EFF_FLAG) {
>>> + msg->id = CMD_TX_EXT_MESSAGE;
>>> + msg->u.tx_can.msg[0] = (cf->can_id >> 24) & 0x1f;
>>> + msg->u.tx_can.msg[1] = (cf->can_id >> 18) & 0x3f;
>>> + msg->u.tx_can.msg[2] = (cf->can_id >> 14) & 0x0f;
>>> + msg->u.tx_can.msg[3] = (cf->can_id >> 6) & 0xff;
>>> + msg->u.tx_can.msg[4] = cf->can_id & 0x3f;
>>> + } else {
>>> + msg->id = CMD_TX_STD_MESSAGE;
>>> + msg->u.tx_can.msg[0] = (cf->can_id >> 6) & 0x1f;
>>> + msg->u.tx_can.msg[1] = cf->can_id & 0x3f;
>>> + }
>>> +
>>> + msg->u.tx_can.msg[5] = cf->can_dlc;
>>> + memcpy(&msg->u.tx_can.msg[6], cf->data, cf->can_dlc);
>>> +
>>> + if (cf->can_id & CAN_RTR_FLAG)
>>> + msg->u.tx_can.flags |= MSG_FLAG_REMOTE_FRAME;
>>> +
>>> + for (i = 0; i < MAX_TX_URBS; i++) {
>> ARRAY_SIZE
>
> Ok.
>
>>> + if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) {
>>> + context = &priv->tx_contexts[i];
>>> + break;
>>> + }
>>> + }
>>> +
>>> + if (!context) {
>>> + netdev_warn(netdev, "cannot find free context\n");
>>> + ret = NETDEV_TX_BUSY;
>>> + goto releasebuf;
>>> + }
>>> +
>>> + context->priv = priv;
>>> + context->echo_index = i;
>>> + context->dlc = cf->can_dlc;
>>> +
>>> + msg->u.tx_can.tid = context->echo_index;
>>> +
>>> + usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, 1),
>>> + buf, msg->len,
>>> + kvaser_usb_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);
>>> +
>>> + if (atomic_read(&priv->active_tx_urbs) >= MAX_TX_URBS)
>>> + netif_stop_queue(netdev);
>>> +
>>> + err = usb_submit_urb(urb, GFP_ATOMIC);
>>> + if (unlikely(err)) {
>>> + can_free_echo_skb(netdev, context->echo_index);
>>> +
>>> + atomic_dec(&priv->active_tx_urbs);
>>> + 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;
>>> + }
>>> +
>>> + netdev->trans_start = jiffies;
>>> +
>>> + usb_free_urb(urb);
>>> +
>>> + return NETDEV_TX_OK;
>>> +
>>> +releasebuf:
>>> + usb_free_coherent(dev->udev, sizeof(struct kvaser_msg),
>>> + buf, urb->transfer_dma);
>>> +nobufmem:
>>> + usb_free_urb(urb);
>>> +nourbmem:
>>> + 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,
>>> +};
>>> +
>>> +static struct can_bittiming_const kvaser_usb_bittiming_const = {
>>> + .name = "kvaser_usb",
>>> + .tseg1_min = KVASER_USB_TSEG1_MIN,
>>> + .tseg1_max = KVASER_USB_TSEG1_MAX,
>>> + .tseg2_min = KVASER_USB_TSEG2_MIN,
>>> + .tseg2_max = KVASER_USB_TSEG2_MAX,
>>> + .sjw_max = KVASER_USB_SJW_MAX,
>>> + .brp_min = KVASER_USB_BRP_MIN,
>>> + .brp_max = KVASER_USB_BRP_MAX,
>>> + .brp_inc = KVASER_USB_BRP_INC,
>>> +};
>>> +
>>> +static int kvaser_usb_set_bittiming(struct net_device *netdev)
>>> +{
>>> + struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
>>> + struct can_bittiming *bt = &priv->can.bittiming;
>>> + struct kvaser_usb *dev = priv->dev;
>>> + struct kvaser_msg msg;
>>> +
>>> + msg.id = CMD_SET_BUS_PARAMS;
>>> + msg.len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_busparams);
>>> + msg.u.busparams.channel = priv->channel;
>>> + msg.u.busparams.tid = 0xff;
>>> + msg.u.busparams.bitrate = bt->bitrate;
>>
>> bitrate is le32
>
> Indeed ! I'll fix this.
>
>>
>>> + msg.u.busparams.sjw = bt->sjw;
>>> + msg.u.busparams.tseg1 = bt->prop_seg + bt->phase_seg1;
>>> + msg.u.busparams.tseg2 = bt->phase_seg2;
>>
>> C99 initializers, please
>>
>>> +
>>> + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
>>> + msg.u.busparams.no_samp = 3;
>>> + else
>>> + msg.u.busparams.no_samp = 1;
>>> +
>>> + return kvaser_usb_send_msg(dev, &msg);
>>> +}
>>> +
>>> +static int kvaser_usb_set_mode(struct net_device *netdev,
>>> + enum can_mode mode)
>>> +{
>>> + struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
>>> +
>>> + if (!priv->open_time)
>>> + return -EINVAL;
>>> +
>>> + switch (mode) {
>>> + case CAN_MODE_START:
>>> + if (netif_queue_stopped(netdev))
>>> + netif_wake_queue(netdev);
>>
>> No need to restart your USB device?
>
> No. I don't think so.
> The module continuously tries to transmit the frame and isn't stopped.
> So there is no need to restart it if it has been explicitely stopped.
>
> When it cannot transmit, the module try again and sends continuously
> CMD_CAN_ERROR_EVENT frames until it succeeds to transmit the frame.
> If the device is stopped with the command CMD_STOP_CHIP then it stops
> sending these CMD_CAN_ERROR_EVENT.
> Should I handle this in another manner?
If the firmware automatically recovers from busoff (like the at91 does),
you should stop the chip it priv->can.restart_ms == 0 and let the chip
continue working otherwise.
>
>>
>>> + break;
>>> + default:
>>> + return -EOPNOTSUPP;
>>> + }
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int kvaser_usb_get_berr_counter(const struct net_device *netdev,
>>> + struct can_berr_counter *bec)
>>> +{
>>> + struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
>>> +
>>> + bec->txerr = priv->bec.txerr;
>>> + bec->rxerr = priv->bec.rxerr;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int kvaser_usb_init_one(struct usb_interface *intf,
>>> + const struct usb_device_id *id, int channel)
>>> +{
>>> + struct kvaser_usb *dev = usb_get_intfdata(intf);
>>> + struct net_device *netdev;
>>> + struct kvaser_usb_net_priv *priv;
>>> + int i, err;
>>> +
>>> + netdev = alloc_candev(sizeof(*priv), MAX_TX_URBS);
>>> + if (!netdev) {
>>> + dev_err(&intf->dev, "Cannot alloc candev\n");
>>> + return -ENOMEM;
>>> + }
>>> +
>>> + priv = netdev_priv(netdev);
>>> +
>>> + 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->dev = dev;
>>> + priv->netdev = netdev;
>>> + priv->channel = channel;
>>> +
>>> + priv->can.state = CAN_STATE_STOPPED;
>>> + priv->can.clock.freq = CAN_USB_CLOCK;
>>> + priv->can.bittiming_const = &kvaser_usb_bittiming_const;
>>> + priv->can.do_set_bittiming = kvaser_usb_set_bittiming;
>>> + priv->can.do_set_mode = kvaser_usb_set_mode;
>>> + priv->can.do_get_berr_counter = kvaser_usb_get_berr_counter;
>>> + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES;
>>> + if (id->driver_info & KVASER_HAS_SILENT_MODE)
>>> + priv->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY;
>>> +
>>> + netdev->flags |= IFF_ECHO;
>>> +
>>> + netdev->netdev_ops = &kvaser_usb_netdev_ops;
>>> +
>>> + SET_NETDEV_DEV(netdev, &intf->dev);
>>> +
>>> + err = register_candev(netdev);
>>> + if (err) {
>>> + dev_err(&intf->dev, "Failed to register can device\n");
>>> + free_candev(netdev);
>>> + return err;
>>> + }
>>> +
>>> + dev->nets[channel] = priv;
>>> + netdev_info(netdev, "device %s registered\n", netdev->name);
>>
>> netdev_info should take care of printing the device's name.
>
> Ok.
>
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int kvaser_usb_probe(struct usb_interface *intf,
>>> + const struct usb_device_id *id)
>>> +{
>>> + struct kvaser_usb *dev;
>>> + int err = -ENOMEM;
>>> + int i;
>>> +
>>> + dev = kzalloc(sizeof(*dev), GFP_KERNEL);
>>
>> Who will free dev on driver unload? Please make use of devm_kzalloc().
>
> Ok. kfree is missing is disconnect().
> I'll replace it by devm_kzalloc() and devm_free().
The beauty of devm_kzalloc is you don't have to call *_free, its
automatically called if probe fails or when remove function has been called.
>
>>
>>> + if (!dev)
>>> + return -ENOMEM;
>>> +
>>> + dev->udev = interface_to_usbdev(intf);
>>> +
>>> + init_usb_anchor(&dev->rx_submitted);
>>> +
>>> + usb_set_intfdata(intf, dev);
>>> +
>>> + if (kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, 0)) {
>>> + dev_err(&intf->dev, "Cannot reset kvaser\n");
>>> + goto error;
>>> + }
>>> +
>>> + if (kvaser_usb_get_software_info(dev)) {
>>> + dev_err(&intf->dev, "Cannot get software infos\n");
>>> + goto error;
>>> + }
>>> +
>>> + if (kvaser_usb_get_card_info(dev)) {
>>> + dev_err(&intf->dev, "Cannot get card infos\n");
>>> + goto error;
>>> + }
>>> +
>>> + dev_dbg(&intf->dev, "Firmware version: %d.%d.%d\n",
>>> + ((dev->fw_version >> 24) & 0xff),
>>> + ((dev->fw_version >> 16) & 0xff),
>>> + (dev->fw_version & 0xffff));
>>> +
>>> + for (i = 0; i < dev->nchannels; i++)
>>> + kvaser_usb_init_one(intf, id, i);
>>> +
>>> + return 0;
>>> +
>>> +error:
>>> + kfree(dev);
>>> + return err;
>>> +}
>>> +
>>> +static void kvaser_usb_disconnect(struct usb_interface *intf)
>>> +{
>>> + struct kvaser_usb *dev = usb_get_intfdata(intf);
>>> + int i;
>>> +
>>> + usb_set_intfdata(intf, NULL);
>>> +
>>> + if (!dev)
>>> + return;
>>> +
>>> + for (i = 0; i < dev->nchannels; i++) {
>>> + if (!dev->nets[i])
>>> + continue;
>>> +
>>> + unregister_netdev(dev->nets[i]->netdev);
>>> + free_candev(dev->nets[i]->netdev);
>>> + }
>>> +
>>> + kvaser_usb_unlink_all_urbs(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_DESCRIPTION("Can driver for Kvaser CAN/USB devices");
>>> +MODULE_LICENSE("GPL v2");
>>> diff --git a/drivers/net/can/usb/kvaser_usb.h b/drivers/net/can/usb/kvaser_usb.h
>>> new file mode 100644
>>> index 0000000..8e0b6ab
>>> --- /dev/null
>>> +++ b/drivers/net/can/usb/kvaser_usb.h
>>> @@ -0,0 +1,237 @@
>>> +#ifndef _KVASER_USB_H_
>>> +#define _KVASER_USB_H_
>>> +
>>> +#define MAX_TX_URBS 16
>> Please no tab between define and macro name
>
> Ok I didn't know it was not allowed... checkpatch didn't complain.
It's allowed, but not used without tab it's more common, at least among
CAN drivers.
>
>>> +#define MAX_RX_URBS 4
>>> +#define START_TIMEOUT 1000
>>> +#define STOP_TIMEOUT 1000
>>> +#define USB_SEND_TIMEOUT 1000
>>> +#define USB_RECEIVE_TIMEOUT 1000
>>> +#define RX_BUFFER_SIZE 3072
>>> +#define CAN_USB_CLOCK 8000000
>>> +#define MAX_NET_DEVICES 3
>>> +
>>> +/* Kvaser USB devices */
>>> +#define KVASER_VENDOR_ID 0x0bfd
>>> +#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
>>> +
>>> +/* USB devices features */
>>> +#define KVASER_HAS_SILENT_MODE (1 << 0)
>> pleae use BIT(0)
>>> +
>>> +/* Message header size */
>>> +#define MSG_HEADER_LEN 2
>>> +
>>> +/* Can message flags */
>>> +#define MSG_FLAG_ERROR_FRAME (1 << 0)
>>> +#define MSG_FLAG_OVERRUN (1 << 1)
>>> +#define MSG_FLAG_NERR (1 << 2)
>>> +#define MSG_FLAG_WAKEUP (1 << 3)
>>> +#define MSG_FLAG_REMOTE_FRAME (1 << 4)
>>> +#define MSG_FLAG_RESERVED (1 << 5)
>>> +#define MSG_FLAG_TX_ACK (1 << 6)
>>> +#define MSG_FLAG_TX_REQUEST (1 << 7)
>>> +
>>> +/* Can states */
>>> +#define M16C_STATE_BUS_RESET (1 << 0)
>>> +#define M16C_STATE_BUS_ERROR (1 << 4)
>>> +#define M16C_STATE_BUS_PASSIVE (1 << 5)
>>> +#define M16C_STATE_BUS_OFF (1 << 6)
>>> +
>>> +/* Can msg 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_GET_CTRL_MODE 22
>>> +#define CMD_GET_CTRL_MODE_REPLY 23
>>> +#define CMD_RESET_CHIP 24
>>> +#define CMD_RESET_CHIP_REPLY 25
>>> +#define CMD_START_CHIP 26
>>> +#define CMD_START_CHIP_REPLY 27
>>> +#define CMD_STOP_CHIP 28
>>> +#define CMD_STOP_CHIP_REPLY 29
>>> +#define CMD_GET_CARD_INFO2 32
>>> +#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_USB_THROTTLE 77
>>> +
>>> +/* error factors */
>>> +#define M16C_EF_ACKE (1 << 0)
>>> +#define M16C_EF_CRCE (1 << 1)
>>> +#define M16C_EF_FORME (1 << 2)
>>> +#define M16C_EF_STFE (1 << 3)
>>> +#define M16C_EF_BITE0 (1 << 4)
>>> +#define M16C_EF_BITE1 (1 << 5)
>>> +#define M16C_EF_RCVE (1 << 6)
>>> +#define M16C_EF_TRE (1 << 7)
>>> +
>>> +/* bittiming parameters */
>>> +#define KVASER_USB_TSEG1_MIN 1
>>> +#define KVASER_USB_TSEG1_MAX 16
>>> +#define KVASER_USB_TSEG2_MIN 1
>>> +#define KVASER_USB_TSEG2_MAX 8
>>> +#define KVASER_USB_SJW_MAX 4
>>> +#define KVASER_USB_BRP_MIN 1
>>> +#define KVASER_USB_BRP_MAX 64
>>> +#define KVASER_USB_BRP_INC 1
>>> +
>>> +/* 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
>>> +
>>> +struct kvaser_msg_simple {
>>> + u8 tid;
>>> + u8 channel;
>>> +} __packed;
>>> +
>>> +struct kvaser_msg_cardinfo {
>>> + u8 tid;
>>> + u8 nchannels;
>>> + __le32 serial_number;
>>> + __le32 padding;
>>> + __le32 clock_resolution;
>>> + __le32 mfgdate;
>>> + u8 ean[8];
>>> + u8 hw_revision;
>>> + u8 usb_hs_mode;
>>> + __le16 padding2;
>>> +} __packed;
>>> +
>>> +struct kvaser_msg_cardinfo2 {
>>> + u8 tid;
>>> + u8 channel;
>>> + u8 pcb_id[24];
>>> + __le32 oem_unlock_code;
>>> +} __packed;
>>> +
>>> +struct kvaser_msg_softinfo {
>>> + u8 tid;
>>> + u8 channel;
>>> + __le32 sw_options;
>>> + __le32 fw_version;
>>> + __le16 max_outstanding_tx;
>>> + __le16 padding[9];
>>> +} __packed;
>>> +
>>> +struct kvaser_msg_busparams {
>>> + u8 tid;
>>> + u8 channel;
>>> + __le32 bitrate;
>>> + u8 tseg1;
>>> + u8 tseg2;
>>> + u8 sjw;
>>> + u8 no_samp;
>>> +} __packed;
>>> +
>>> +struct kvaser_msg_tx_can {
>>> + u8 channel;
>>> + u8 tid;
>>> + u8 msg[14];
>>> + u8 padding;
>>> + u8 flags;
>>> +} __packed;
>>> +
>>> +struct kvaser_msg_rx_can {
>>> + u8 channel;
>>> + u8 flag;
>>> + __le16 time[3];
>>> + u8 msg[14];
>>> +} __packed;
>>> +
>>> +struct kvaser_msg_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 kvaser_msg_tx_acknowledge {
>>> + u8 channel;
>>> + u8 tid;
>>> + __le16 time[3];
>>> + u8 flags;
>>> + u8 time_offset;
>>> +} __packed;
>>> +
>>> +struct kvaser_msg_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 kvaser_msg_ctrl_mode {
>>> + u8 tid;
>>> + u8 channel;
>>> + u8 ctrl_mode;
>>> + u8 padding[3];
>>> +} __packed;
>>> +
>>> +struct kvaser_msg_flush_queue {
>>> + u8 tid;
>>> + u8 channel;
>>> + u8 flags;
>>> + u8 padding[3];
>>> +} __packed;
>>> +
>>> +struct kvaser_msg {
>>> + u8 len;
>>> + u8 id;
>>> + union {
>>> + struct kvaser_msg_simple simple;
>>> + struct kvaser_msg_cardinfo cardinfo;
>>> + struct kvaser_msg_cardinfo2 cardinfo2;
>>> + struct kvaser_msg_softinfo softinfo;
>>> + struct kvaser_msg_busparams busparams;
>>> + struct kvaser_msg_tx_can tx_can;
>>> + struct kvaser_msg_rx_can rx_can;
>>> + struct kvaser_msg_chip_state_event chip_state_event;
>>> + struct kvaser_msg_tx_acknowledge tx_acknowledge;
>>> + struct kvaser_msg_error_event error_event;
>>> + struct kvaser_msg_ctrl_mode ctrl_mode;
>>> + struct kvaser_msg_ctrl_mode flush_queue;
>>> + } u;
>>> +} __packed;
>>> +
>>> +#endif
>>>
>>
>>
>> --
>> Pengutronix e.K. | Marc Kleine-Budde |
>> Industrial Linux Solutions | Phone: +49-231-2826-924 |
>> Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
>> Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
>>
>
> Thanks for the review.
np,
regards, Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Industrial Linux Solutions | Phone: +49-231-2826-924 |
Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 262 bytes --]
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH] can: kvaser_usb: Add support for Kvaser CAN/USB devices
2012-07-31 9:56 ` Marc Kleine-Budde
@ 2012-07-31 13:06 ` Olivier Sobrie
2012-07-31 13:27 ` Marc Kleine-Budde
2012-07-31 13:43 ` Wolfgang Grandegger
2012-08-05 20:41 ` Wolfgang Grandegger
1 sibling, 2 replies; 43+ messages in thread
From: Olivier Sobrie @ 2012-07-31 13:06 UTC (permalink / raw)
To: Marc Kleine-Budde; +Cc: Wolfgang Grandegger, linux-can, netdev
On Tue, Jul 31, 2012 at 11:56:22AM +0200, Marc Kleine-Budde wrote:
> On 07/30/2012 03:33 PM, Olivier Sobrie wrote:
> > Hi Marc,
> >
> > On Mon, Jul 30, 2012 at 01:11:46PM +0200, Marc Kleine-Budde wrote:
> >> On 07/30/2012 07:32 AM, Olivier Sobrie wrote:
> >>> This driver provides support for several Kvaser CAN/USB devices.
> >>> Such kind of devices supports up to three can network interfaces.
> >>>
> >>> It has been tested with a Kvaser USB Leaf Light (one network interface)
> >>> connected to a pch_can interface.
> >>> The firmware version of the Kvaser device was 2.5.205.
> >>
> >> Please add linux-usb@vger.kernel.org to Cc for review of the USB part.
> >
> > Ok I'll do it when I send the new version of the patch.
> > But it might be a good idea to add an entry in the MAINTAINERS file so
> > that when someone sends a patch they are aware of this when the
> > get_maintainer script is invoked.
>
> Interesting Idea. We should discuss this here, however we should not
> bother the USB List when sending USB unrelated patches.
>
> >> Please combine .h and .c file. Please make use of netdev_LEVEL() for
> >> error printing, not dev_LEVEL().
> >
> > I'll combine the .c and .h.
> > I used the netdev_LEVEL() everywhere it was possible. It requires to
> > have access to a pointer to netdev which is not always possible;
> > that's the reason why I used dev_LEVEL().
>
> I see, you used it when channel is invalid. So you have obviously no netdev.
Indeed.
>
> >> Please review if all members of the struct kvaser_msg are properly
> >> aligned. You never access the struct kvaser_msg_* members directly, as
> >> they are unaligned. Please check for le16 and le32 access. You missed to
> >> convert the bitrate.
> >
> > Indeed. Thanks. I'll check if I didn't missed another one.
>
> Tnx
>
> >> Please check if your driver survives hot-unplugging while sending and
> >> receiving CAN frames at maximum laod.
> >
> > I tested this with two Kvaser sending frames with "cangen can0 -g 0 -i"
> > never saw a crash.
>
> Please test send sending and receiving at the same time.
Yes that's what I did; "cangen can0 -g 0 -i" on both sides.
>
> >> More comments inline,
> >> regards, Marc
> >>
> >>> List of Kvaser devices supported by the driver:
> >>> - Kvaser Leaf prototype (P010v2 and v3)
> >>> - Kvaser Leaf Light (P010v3)
> >>> - 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, Prototype
> >>> - 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 OEM Mercury
> >>> - Kvaser OEM Leaf
> >>> - Kvaser USBcan R
> >>>
> >>> Signed-off-by: Olivier Sobrie <olivier@sobrie.be>
> >>> ---
> >>> drivers/net/can/usb/Kconfig | 33 ++
> >>> drivers/net/can/usb/Makefile | 1 +
> >>> drivers/net/can/usb/kvaser_usb.c | 1062 ++++++++++++++++++++++++++++++++++++++
> >>> drivers/net/can/usb/kvaser_usb.h | 237 +++++++++
> >>> 4 files changed, 1333 insertions(+)
> >>> create mode 100644 drivers/net/can/usb/kvaser_usb.c
> >>> create mode 100644 drivers/net/can/usb/kvaser_usb.h
> >>>
> >>> diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig
> >>> index 0a68768..578955f 100644
> >>> --- a/drivers/net/can/usb/Kconfig
> >>> +++ b/drivers/net/can/usb/Kconfig
> >>> @@ -13,6 +13,39 @@ config CAN_ESD_USB2
> >>> This driver supports the CAN-USB/2 interface
> >>> from esd electronic system design gmbh (http://www.esd.eu).
> >>>
> >>> +config CAN_KVASER_USB
> >>> + tristate "Kvaser CAN/USB interface"
> >>> + ---help---
> >>> + This driver adds support for Kvaser CAN/USB devices like Kvaser
> >>> + Leaf Light.
> >>> +
> >>> + The driver gives support for the following devices:
> >>> + - Kvaser Leaf prototype (P010v2 and v3)
> >>> + - Kvaser Leaf Light (P010v3)
> >>> + - 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, Prototype
> >>> + - 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 OEM Mercury
> >>> + - Kvaser OEM Leaf
> >>> + - Kvaser USBcan R
> >>> +
> >>> + If unsure, say N.
> >>> +
> >>> + To compile this driver as a module, choose M here: the
> >>> + module will be called kvaser_usb.
> >>> +
> >>> config CAN_PEAK_USB
> >>> tristate "PEAK PCAN-USB/USB Pro interfaces"
> >>> ---help---
> >>> diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile
> >>> index da6d1d3..80a2ee4 100644
> >>> --- a/drivers/net/can/usb/Makefile
> >>> +++ b/drivers/net/can/usb/Makefile
> >>> @@ -4,6 +4,7 @@
> >>>
> >>> obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o
> >>> obj-$(CONFIG_CAN_ESD_USB2) += esd_usb2.o
> >>> +obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o
> >>> obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/
> >>>
> >>> ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
> >>> diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c
> >>> new file mode 100644
> >>> index 0000000..4965480
> >>> --- /dev/null
> >>> +++ b/drivers/net/can/usb/kvaser_usb.c
> >>> @@ -0,0 +1,1062 @@
> >>> +/*
> >>
> >> Please add a license statement and probably your copyright:
> >>
> >> * This program is free software; you can redistribute it and/or
> >> * modify it under the terms of the GNU General Public License as
> >> * published by the Free Software Foundation version 2.
> >>
> >> You also should copy the copyright from the drivers you used:
> >>
> >>> + * Parts of this driver are based on the following:
> >>> + * - Kvaser linux leaf driver (version 4.78)
> >>
> >> I just downloaded their driver and noticed that it's quite sparse on
> >> stating the license the code is released under.
> >> "doc/HTMLhelp/copyright.htx" is quite restrictive, the word GPL occurs 3
> >> times, all in MODULE_LICENSE("GPL"). Running modinfo on the usbcan.ko
> >> shows "license: GPL"
> >
> > I'll add the license statement.
> > In fact it's the leaf.ko which is used for this device and it is under
> > GPL as modinfo said.
>
> I just talked to my boss and we're the same opinion, that
> MODULE_LICENSE("GPL") is a technical term and not relevant if the
> included license doesn't say a word about GPL. If the kvaser tarball
> violates the GPL, however is written on different sheet of paper (as we
> say in Germany).
>
> So I cannot put my S-o-b under this driver as long as we haven't talked
> to kvaser.
Ok I thought it was sufficient enough to have MODULE_LICENSE("GPL") in
the code to indicate it is a GPL driver. I'll ask Kvaser before sending
any new version of the patch.
>
> >>> + * - CAN driver for esd CAN-USB/2
> >>> + */
> >>> +
> >>> +#include <linux/init.h>
> >>> +#include <linux/completion.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 "kvaser_usb.h"
> >>> +
> >>> +struct kvaser_usb_tx_urb_context {
> >>> + struct kvaser_usb_net_priv *priv;
> >>
> >> Huh - how does this work without forward declaration?
> >
> > It works.
>
> Yes, obviously :)
>
> > "In C and C++ it is possible to declare pointers to structs before
> > declaring their struct layout, provided the pointers are not
> > dereferenced--this is known as forward declaration."
> >
> > See http://www.linuxtopia.org/online_books/an_introduction_to_gcc/gccintro_94.html
>
> Thanks for the link.
> >>
> >>> + u32 echo_index;
> >>> + int dlc;
> >>> +};
> >>> +
> >>> +struct kvaser_usb {
> >>> + struct usb_device *udev;
> >>> + struct kvaser_usb_net_priv *nets[MAX_NET_DEVICES];
> >>> +
> >>> + struct usb_anchor rx_submitted;
> >>> +
> >>> + u32 fw_version;
> >>> + unsigned int nchannels;
> >>> +
> >>> + bool rxinitdone;
> >>> +};
> >>> +
> >>> +struct kvaser_usb_net_priv {
> >>> + struct can_priv can;
> >>> +
> >>> + atomic_t active_tx_urbs;
> >>> + struct usb_anchor tx_submitted;
> >>> + struct kvaser_usb_tx_urb_context tx_contexts[MAX_TX_URBS];
> >>> +
> >>> + int open_time;
> >>
> >> please remove open_time
> >
> > Ok.
> >
> >>
> >>> + struct completion start_stop_comp;
> >>> +
> >>> + struct kvaser_usb *dev;
> >>> + struct net_device *netdev;
> >>> + int channel;
> >>> + struct can_berr_counter bec;
> >>> +};
> >>> +
> >>> +static struct usb_device_id kvaser_usb_table[] = {
> >>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_DEVEL_PRODUCT_ID) },
> >>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_PRODUCT_ID) },
> >>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_PRODUCT_ID),
> >>> + .driver_info = KVASER_HAS_SILENT_MODE },
> >>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_PRODUCT_ID),
> >>> + .driver_info = KVASER_HAS_SILENT_MODE },
> >>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LS_PRODUCT_ID),
> >>> + .driver_info = KVASER_HAS_SILENT_MODE },
> >>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_SWC_PRODUCT_ID),
> >>> + .driver_info = KVASER_HAS_SILENT_MODE },
> >>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LIN_PRODUCT_ID),
> >>> + .driver_info = KVASER_HAS_SILENT_MODE },
> >>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_LS_PRODUCT_ID),
> >>> + .driver_info = KVASER_HAS_SILENT_MODE },
> >>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_SWC_PRODUCT_ID),
> >>> + .driver_info = KVASER_HAS_SILENT_MODE },
> >>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_DEVEL_PRODUCT_ID),
> >>> + .driver_info = KVASER_HAS_SILENT_MODE },
> >>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSHS_PRODUCT_ID),
> >>> + .driver_info = KVASER_HAS_SILENT_MODE },
> >>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_UPRO_HSHS_PRODUCT_ID) },
> >>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_GI_PRODUCT_ID) },
> >>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_OBDII_PRODUCT_ID),
> >>> + .driver_info = KVASER_HAS_SILENT_MODE },
> >>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSLS_PRODUCT_ID) },
> >>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_CH_PRODUCT_ID) },
> >>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_BLACKBIRD_SPRO_PRODUCT_ID) },
> >>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_MERCURY_PRODUCT_ID) },
> >>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_LEAF_PRODUCT_ID) },
> >>> + { USB_DEVICE(KVASER_VENDOR_ID, USB_CAN_R_PRODUCT_ID) },
> >>> + { }
> >>> +};
> >>> +MODULE_DEVICE_TABLE(usb, kvaser_usb_table);
> >>> +
> >>> +static int kvaser_usb_send_msg(const struct kvaser_usb *dev,
> >>> + struct kvaser_msg *msg)
> >> inline?
> >
> > Ok.
> >
> >>> +{
> >>> + int actual_len;
> >>> +
> >>> + return usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 1),
> >> ^
> >> Can you please introduce a #define for this.
> >
> > Ok. No problem.
> >
> >>
> >>> + msg, msg->len, &actual_len, USB_SEND_TIMEOUT);
> >>> +}
> >>> +
> >>> +static int kvaser_usb_wait_msg(const struct kvaser_usb *dev, u8 id,
> >>> + struct kvaser_msg *msg)
> >>> +{
> >>> + struct kvaser_msg *tmp;
> >>> + void *buf;
> >>> + int actual_len;
> >>> + int err;
> >>> + int pos = 0;
> >>> +
> >>> + buf = kzalloc(RX_BUFFER_SIZE, GFP_KERNEL);
> >>> + if (!buf)
> >>> + return -ENOMEM;
> >>> +
> >>> + err = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, 129),
> >> ^^^
> >> dito
> >
> > Ok too.
> >
> >>
> >>> + buf, RX_BUFFER_SIZE, &actual_len,
> >>> + USB_RECEIVE_TIMEOUT);
> >>> + if (err < 0) {
> >>> + kfree(buf);
> >>> + return err;
> >>> + }
> >>> +
> >>> + while (pos < actual_len) {
> >>
> >> Please check that pos + sizeof(*msg) is < actual_len, as you fill access
> >> it later.
> >
> > I'll instead perform a check on 'pos + tmp->len < actual_len' and copy
> > only tmp->len instead of sizeof(*msg).
> > Thanks.
>
> Even better, saves some bytes to be copied. Take care not to deref tmp,
> unless you checked that tmp is in valid memory.
Ok. I changed the loop by 'while (pos <= actual_len - MSG_HEADER_LEN)'
and then perform the check on 'pos + tmp->len < actual_len'.
>
> >>
> >>> + tmp = buf + pos;
> >>> +
> >>> + if (!tmp->len)
> >>> + break;
> >>> +
> >>> + if (tmp->id == id) {
> >>> + memcpy(msg, tmp, sizeof(*msg));
> >>> + kfree(buf);
> >>> + return 0;
> >>> + }
> >>> +
> >>> + pos += tmp->len;
> >>> + }
> >>> +
> >>> + kfree(buf);
> >>> +
> >>> + return -EINVAL;
> >>> +}
> >>> +
> >>> +static int kvaser_usb_send_simple_msg(const struct kvaser_usb *dev,
> >>> + u8 msg_id, int channel)
> >>> +{
> >>> + struct kvaser_msg msg;
> >>> + int err;
> >>> +
> >>> + msg.len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple);
> >>> + msg.id = msg_id;
> >>> + msg.u.simple.channel = channel;
> >>> + msg.u.simple.tid = 0xff;
> >>
> >> Please use C99 struct initializer.
> >>
> >> struct kvaser_msg msg = {
> >> .len = ,
> >> .id =,
> >> };
> >
> > Ok.
> >
> >>
> >>
> >>> +
> >>> + err = kvaser_usb_send_msg(dev, &msg);
> >>
> >> just:
> >> return err;
> >
> > Ok.
> >
> >>
> >>> + if (err)
> >>> + return err;
> >>> +
> >>> + return 0;
> >>> +}
> >>> +
> >>> +static int kvaser_usb_get_software_info(struct kvaser_usb *dev)
> >>> +{
> >>> + struct kvaser_msg msg;
> >>> + int err;
> >>> +
> >>> + err = kvaser_usb_send_simple_msg(dev, CMD_GET_SOFTWARE_INFO, 0);
> >>> + if (err)
> >>> + return err;
> >>> +
> >>> + err = kvaser_usb_wait_msg(dev, CMD_GET_SOFTWARE_INFO_REPLY, &msg);
> >>> + if (err)
> >>> + return err;
> >>> +
> >>> + dev->fw_version = le32_to_cpu(msg.u.softinfo.fw_version);
> >>> +
> >>> + return 0;
> >>> +}
> >>> +
> >>> +static int kvaser_usb_get_card_info(struct kvaser_usb *dev)
> >>> +{
> >>> + struct kvaser_msg msg;
> >>> + int err;
> >>> +
> >>> + err = kvaser_usb_send_simple_msg(dev, CMD_GET_CARD_INFO, 0);
> >>> + if (err)
> >>> + return err;
> >>> +
> >>> + err = kvaser_usb_wait_msg(dev, CMD_GET_CARD_INFO_REPLY, &msg);
> >>> + if (err)
> >>> + return err;
> >>> +
> >>> + dev->nchannels = msg.u.cardinfo.nchannels;
> >>> +
> >>> + return 0;
> >>> +}
> >>> +
> >>> +static void kvaser_usb_tx_acknowledge(const struct kvaser_usb *dev,
> >>> + const struct kvaser_msg *msg)
> >>> +{
> >>> + struct net_device_stats *stats;
> >>> + struct kvaser_usb_tx_urb_context *context;
> >>> + struct kvaser_usb_net_priv *priv;
> >>> + u8 channel = msg->u.tx_acknowledge.channel;
> >>> + u8 tid = msg->u.tx_acknowledge.tid;
> >>> +
> >>> + if (channel >= dev->nchannels) {
> >>> + dev_err(dev->udev->dev.parent,
> >>> + "Invalid channel number (%d)\n", channel);
> >>> + return;
> >>> + }
> >>
> >> can you do a check for (channel >= dev->nchannels), in a central place?
> >> e.g. kvaser_usb_handle_message()?
> >
> > The problem is that channel is not always at the same place in the
> > messages I get from the hardware. 'tid' and 'channel' are inverted for
> > tx and rx frames.
> > e.g.
>
> Grr...who's written that firmware :D
>
> >
> > struct kvaser_msg_tx_can {
> > u8 channel;
> > u8 tid;
> > u8 msg[14];
> > u8 padding;
> > u8 flags;
> > } __packed;
> >
> > struct kvaser_msg_busparams {
> > u8 tid;
> > u8 channel;
> > __le32 bitrate;
> > u8 tseg1;
> > u8 tseg2;
> > u8 sjw;
> > u8 no_samp;
> > } __packed;
> >
> >>
> >>> +
> >>> + priv = dev->nets[channel];
> >>> +
> >>> + if (!netif_device_present(priv->netdev))
> >>> + return;
> >>> +
> >>> + stats = &priv->netdev->stats;
> >>> +
> >>> + context = &priv->tx_contexts[tid % MAX_TX_URBS];
> >>> +
> >>> + /*
> >>> + * It looks like the firmware never sets the flags field of the
> >>> + * tx_acknowledge frame and never reports a transmit failure.
> >>> + * If the can message can't be transmited (e.g. incompatible
> >>> + * bitrates), a frame CMD_CAN_ERROR_EVENT is sent (with a null
> >>> + * tid) and the firmware tries to transmit again the packet until
> >>> + * it succeeds. Once the packet is successfully transmitted, then
> >>> + * the tx_acknowledge frame is sent.
> >>> + */
> >>> +
> >>> + stats->tx_packets++;
> >>> + stats->tx_bytes += context->dlc;
> >>> + can_get_echo_skb(priv->netdev, context->echo_index);
> >>> +
> >>> + context->echo_index = MAX_TX_URBS;
> >>> + atomic_dec(&priv->active_tx_urbs);
> >>> +
> >>> + netif_wake_queue(priv->netdev);
> >>> +}
> >>> +
> >>> +static void kvaser_usb_rx_error(const struct kvaser_usb *dev,
> >>> + const struct kvaser_msg *msg)
> >>> +{
> >>> + struct can_frame *cf;
> >>> + struct sk_buff *skb;
> >>> + struct net_device_stats *stats;
> >>> + struct kvaser_usb_net_priv *priv;
> >>> + u8 channel, status, txerr, rxerr;
> >>> +
> >>> + if (msg->id == CMD_CAN_ERROR_EVENT) {
> >>> + channel = msg->u.error_event.channel;
> >>> + status = msg->u.error_event.status;
> >>> + txerr = msg->u.error_event.tx_errors_count;
> >>> + rxerr = msg->u.error_event.rx_errors_count;
> >>> + } else {
> >>> + channel = msg->u.chip_state_event.channel;
> >>> + status = msg->u.chip_state_event.status;
> >>> + txerr = msg->u.chip_state_event.tx_errors_count;
> >>> + rxerr = msg->u.chip_state_event.rx_errors_count;
> >>> + }
> >>> +
> >>> + if (channel >= dev->nchannels) {
> >>> + dev_err(dev->udev->dev.parent,
> >>> + "Invalid channel number (%d)\n", channel);
> >>> + return;
> >>> + }
> >>> +
> >>> + priv = dev->nets[channel];
> >>> + stats = &priv->netdev->stats;
> >>> +
> >>> + skb = alloc_can_err_skb(priv->netdev, &cf);
> >>> + if (!skb) {
> >>> + stats->rx_dropped++;
> >>> + return;
> >>> + }
> >>> +
> >>> + if ((status & M16C_STATE_BUS_OFF) ||
> >>> + (status & M16C_STATE_BUS_RESET)) {
> >>> + priv->can.state = CAN_STATE_BUS_OFF;
> >>> + cf->can_id |= CAN_ERR_BUSOFF;
> >>> + can_bus_off(priv->netdev);
>
> you should increment priv->can.can_stats.bus_off
> What does the firmware do in this state? Does it automatically try to
> recover and try to send the outstanding frames?
Yes that's what I observed.
>
> If so, you should turn of the CAN interface, it possible. See:
> http://lxr.free-electrons.com/source/drivers/net/can/at91_can.c#L986
Ok I'll have a look at this.
>
> Please test Bus-Off behaviour:
> - setup working CAN network
> - short circuit CAN-H and CAN-L wires
> - start "candump any,0:0,#FFFFFFFF" on one shell
> - send one can frame on the other
>
> then
>
> - remove the short circuit
> - see if the can frame is transmitted to the other side
> - it should show up as an echo'ed CAN frame on the sender side
>
> Repeat the same test with disconnecting CAN-H and CAN-L from the other
> CAN station instead of short circuit.
>
> Please send the output from candump.
1) With the short circuit:
I perform the test you described. It showed that the Kvaser passes from
ERROR-WARNING to ERROR-PASSIVE and then BUS-OFF. But after going to the
state BUS-OFF it comes back to ERROR-WARNING.
can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
...
can1 200000C8 [8] 00 00 90 00 00 00 00 00 ERRORFRAME <-- bus off
can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
...
can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
can1 123 [2] 11 22 <-- short circuit removed
I see the echo and on the other end I see the frame coming in.
By the way I see that the txerr and rxerr fields of the structure
kvaser_msg_error_event stay at 0.
2) With CAN-H and CAN-L disconnected:
I never see the bus going in OFF state. It stays in PASSIVE mode.
can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
...
can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
can1 123 [2] 11 22 <-- other end connected
>
> >>> + } else if (status & M16C_STATE_BUS_ERROR) {
> >>> + priv->can.state = CAN_STATE_ERROR_WARNING;
> >>> + priv->can.can_stats.error_warning++;
> >>> + } else if (status & M16C_STATE_BUS_PASSIVE) {
> >>> + priv->can.state = CAN_STATE_ERROR_PASSIVE;
> >>> + priv->can.can_stats.error_passive++;
> >>> + } else {
> >>> + priv->can.state = CAN_STATE_ERROR_ACTIVE;
> >>> + cf->can_id |= CAN_ERR_PROT;
> >>> + cf->data[2] = CAN_ERR_PROT_ACTIVE;
> >>> + }
> >>> +
> >>> + if (msg->id == CMD_CAN_ERROR_EVENT) {
> >>> + u8 error_factor = msg->u.error_event.error_factor;
> >>> +
> >>> + priv->can.can_stats.bus_error++;
> >>> + stats->rx_errors++;
> >>> +
> >>> + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
> >>> +
> >>> + 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;
> >>> + }
> >>> +
> >>> + if (error_factor & M16C_EF_ACKE)
> >>> + cf->data[3] |= (CAN_ERR_PROT_LOC_ACK |
> >>> + CAN_ERR_PROT_LOC_ACK_DEL);
> >>> + if (error_factor & M16C_EF_CRCE)
> >>> + cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ |
> >>> + CAN_ERR_PROT_LOC_CRC_DEL);
> >>> + if (error_factor & M16C_EF_FORME)
> >>> + cf->data[2] |= CAN_ERR_PROT_FORM;
> >>> + if (error_factor & M16C_EF_STFE)
> >>> + cf->data[2] |= CAN_ERR_PROT_STUFF;
> >>> + if (error_factor & M16C_EF_BITE0)
> >>> + cf->data[2] |= CAN_ERR_PROT_BIT0;
> >>> + if (error_factor & M16C_EF_BITE1)
> >>> + cf->data[2] |= CAN_ERR_PROT_BIT1;
> >>> + if (error_factor & M16C_EF_TRE)
> >>> + cf->data[2] |= CAN_ERR_PROT_TX;
> >>> + }
> >>> +
> >>> + cf->data[6] = txerr;
> >>> + cf->data[7] = rxerr;
> >>> +
> >>> + netif_rx(skb);
> >>> +
> >>> + priv->bec.txerr = txerr;
> >>> + priv->bec.rxerr = rxerr;
> >>> +
> >>> + stats->rx_packets++;
> >>> + stats->rx_bytes += cf->can_dlc;
> >>> +}
> >>> +
> >>> +static void kvaser_usb_rx_can_msg(const struct kvaser_usb *dev,
> >>> + const struct kvaser_msg *msg)
> >>> +{
> >>> + struct kvaser_usb_net_priv *priv;
> >>> + struct can_frame *cf;
> >>> + struct sk_buff *skb;
> >>> + struct net_device_stats *stats;
> >>> + u8 channel = msg->u.rx_can.channel;
> >>> +
> >>> + if (channel >= dev->nchannels) {
> >>> + dev_err(dev->udev->dev.parent,
> >>> + "Invalid channel number (%d)\n", channel);
> >>> + return;
> >>> + }
> >>> +
> >>> + priv = dev->nets[channel];
> >>> + stats = &priv->netdev->stats;
> >>> +
> >>> + skb = alloc_can_skb(priv->netdev, &cf);
> >>> + if (skb == NULL) {
> >>> + stats->tx_dropped++;
> >>> + return;
> >>> + }
> >>> +
> >>> + cf->can_id = ((msg->u.rx_can.msg[0] & 0x1f) << 6) |
> >>> + (msg->u.rx_can.msg[1] & 0x3f);
> >>> + cf->can_dlc = get_can_dlc(msg->u.rx_can.msg[5]);
> >>> +
> >>> + if (msg->id == CMD_RX_EXT_MESSAGE) {
> >>> + cf->can_id <<= 18;
> >>> + cf->can_id += ((msg->u.rx_can.msg[2] & 0x0f) << 14) |
> >> |=
> >>
> >> is more appropriate here
> >
> > Ok.
> >
> >>
> >>> + ((msg->u.rx_can.msg[3] & 0xff) << 6) |
> >>> + (msg->u.rx_can.msg[4] & 0x3f);
> >>> + cf->can_id |= CAN_EFF_FLAG;
> >>> + }
> >>> +
> >>> + if (msg->u.rx_can.flag & MSG_FLAG_REMOTE_FRAME) {
> >>> + cf->can_id |= CAN_RTR_FLAG;
> >>> + } else if (msg->u.rx_can.flag & (MSG_FLAG_ERROR_FRAME |
> >>> + MSG_FLAG_NERR)) {
> >>> + cf->can_id |= CAN_ERR_FLAG;
> >>> + cf->can_dlc = CAN_ERR_DLC;
> >>
> >> What kind of error is this? Can you set cf->data? What about the
> >> original cd->can_id? What about the stats->rx_*error* stats?
> >
> > Good question I've to take a look to this.
> >
> >>
> >>> + } else if (msg->u.rx_can.flag & MSG_FLAG_OVERRUN) {
> >>> + cf->can_id = CAN_ERR_FLAG | CAN_ERR_CRTL;
> >>> + cf->can_dlc = CAN_ERR_DLC;
> >>> + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
> >>> +
> >>> + stats->rx_over_errors++;
> >>> + stats->rx_errors++;
> >>> + } else if (!msg->u.rx_can.flag) {
> >>> + memcpy(cf->data, &msg->u.rx_can.msg[6], cf->can_dlc);
> >>> + } else {
> >>> + kfree_skb(skb);
> >>> + return;
> >>> + }
> >>> +
> >>> + netif_rx(skb);
> >>> +
> >>> + stats->rx_packets++;
> >>> + stats->rx_bytes += cf->can_dlc;
> >>> +}
> >>> +
> >>> +static void kvaser_usb_start_stop_chip_reply(const struct kvaser_usb *dev,
> >>> + const struct kvaser_msg *msg)
> >>> +{
> >>> + struct kvaser_usb_net_priv *priv;
> >>> + u8 channel = msg->u.simple.channel;
> >>> +
> >>> + if (channel >= dev->nchannels) {
> >>> + dev_err(dev->udev->dev.parent,
> >>> + "Invalid channel number (%d)\n", channel);
> >>> + return;
> >>> + }
> >>> +
> >>> + priv = dev->nets[channel];
> >>> +
> >>> + complete(&priv->start_stop_comp);
> >>> +}
> >>> +
> >>> +static void kvaser_usb_handle_message(const struct kvaser_usb *dev,
> >>> + const struct kvaser_msg *msg)
> >>> +{
> >>> + switch (msg->id) {
> >>> + case CMD_START_CHIP_REPLY:
> >>> + case CMD_STOP_CHIP_REPLY:
> >>> + kvaser_usb_start_stop_chip_reply(dev, msg);
> >>> + break;
> >>> +
> >>> + case CMD_RX_STD_MESSAGE:
> >>> + case CMD_RX_EXT_MESSAGE:
> >>> + kvaser_usb_rx_can_msg(dev, msg);
> >>> + break;
> >>> +
> >>> + case CMD_CHIP_STATE_EVENT:
> >>> + case CMD_CAN_ERROR_EVENT:
> >>> + kvaser_usb_rx_error(dev, msg);
> >>> + break;
> >>> +
> >>> + case CMD_TX_ACKNOWLEDGE:
> >>> + kvaser_usb_tx_acknowledge(dev, msg);
> >>> + break;
> >>> +
> >>> + default:
> >>> + dev_warn(dev->udev->dev.parent,
> >>> + "Unhandled message (%d)\n", msg->id);
> >>> + break;
> >>> + }
> >>> +}
> >>> +
> >>> +static void kvaser_usb_read_bulk_callback(struct urb *urb)
> >>> +{
> >>> + struct kvaser_usb *dev = urb->context;
> >>> + struct kvaser_msg *msg;
> >>> + int pos = 0;
> >>> + int err, i;
> >>> +
> >>> + switch (urb->status) {
> >>> + case 0:
> >>> + break;
> >>> + case -ENOENT:
> >>> + 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) {
> >>
> >> please check here for pos + sizeof(*msg), too
> >
> > Same as above.
> >
> >>
> >>> + msg = urb->transfer_buffer + pos;
> >>> +
> >>> + if (!msg->len)
> >>> + break;
> >>> +
> >>> + kvaser_usb_handle_message(dev, msg);
> >>> +
> >>> + if (pos > urb->actual_length) {
> >>> + dev_err(dev->udev->dev.parent, "Format error\n");
> >>> + break;
> >>> + }
> >>> +
> >>> + pos += msg->len;
> >>> + }
> >>> +
> >>> +resubmit_urb:
> >>> + usb_fill_bulk_urb(urb, dev->udev, usb_rcvbulkpipe(dev->udev, 129),
> >> ^^^
> >>
> >> use #define
> >
> > Ok.
> >
> >>
> >>> + urb->transfer_buffer, 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->udev->dev.parent,
> >>> + "Failed resubmitting read bulk urb: %d\n", err);
> >>> + }
> >>> +
> >>> + return;
> >>> +}
> >>> +
> >>> +static int kvaser_usb_setup_rx_urbs(struct kvaser_usb *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;
> >>> +
> >>> + urb = usb_alloc_urb(0, GFP_KERNEL);
> >>> + if (!urb) {
> >>> + dev_warn(dev->udev->dev.parent,
> >>> + "No memory left for URBs\n");
> >>> + err = -ENOMEM;
> >>> + break;
> >>> + }
> >>> +
> >>> + buf = usb_alloc_coherent(dev->udev, RX_BUFFER_SIZE,
> >>> + GFP_KERNEL, &urb->transfer_dma);
> >>> + if (!buf) {
> >>> + dev_warn(dev->udev->dev.parent,
> >>> + "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, 129),
> >>
> >> use #define
> >
> > Ok.
> >
> >>
> >>> + buf, RX_BUFFER_SIZE,
> >>> + kvaser_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);
> >>> + break;
> >>> + }
> >>> +
> >>> + usb_free_urb(urb);
> >>> + }
> >>> +
> >>> + if (i == 0) {
> >>> + dev_warn(dev->udev->dev.parent,
> >>> + "Cannot setup read URBs, error %d\n", err);
> >>> + return err;
> >>> + } else if (i < MAX_RX_URBS) {
> >>> + dev_warn(dev->udev->dev.parent,
> >>> + "RX performances may be slow\n");
> >>> + }
> >>> +
> >>> + dev->rxinitdone = true;
> >>> +
> >>> + return 0;
> >>> +}
> >>> +
> >>> +static int kvaser_usb_set_opt_mode(const struct kvaser_usb_net_priv *priv)
> >>> +{
> >>> + struct kvaser_msg msg;
> >>> +
> >>> + memset(&msg, 0x00, sizeof(msg));
> >>> + msg.id = CMD_SET_CTRL_MODE;
> >>> + msg.len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_ctrl_mode);
> >>> + msg.u.ctrl_mode.tid = 0xff;
> >>> + msg.u.ctrl_mode.channel = priv->channel;
> >>
> >> please use C99 struct initializers
> >
> > Ok.
> >
> >>
> >>> +
> >>> + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
> >>> + msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_SILENT;
> >>> + else
> >>> + msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_NORMAL;
> >>> +
> >>> + return kvaser_usb_send_msg(priv->dev, &msg);
> >>> +}
> >>> +
> >>> +static int kvaser_usb_start_chip(struct kvaser_usb_net_priv *priv)
> >>> +{
> >>> + int err;
> >>> +
> >>> + init_completion(&priv->start_stop_comp);
> >>> +
> >>> + err = kvaser_usb_send_simple_msg(priv->dev, CMD_START_CHIP,
> >>> + priv->channel);
> >>> + if (err)
> >>> + return err;
> >>> +
> >>> + if (!wait_for_completion_timeout(&priv->start_stop_comp,
> >>> + msecs_to_jiffies(START_TIMEOUT)))
> >>> + return -ETIMEDOUT;
> >>> +
> >>> + 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;
> >>> + int err;
> >>> +
> >>> + err = open_candev(netdev);
> >>> + if (err)
> >>> + return err;
> >>> +
> >>> + err = kvaser_usb_setup_rx_urbs(dev);
> >>> + if (err)
> >>> + return err;
> >>> +
> >>> + err = kvaser_usb_set_opt_mode(priv);
> >>> + if (err)
> >>> + return err;
> >>> +
> >>> + err = kvaser_usb_start_chip(priv);
> >>> + if (err) {
> >>> + netdev_warn(netdev, "Cannot start device, error %d\n", err);
> >>> + close_candev(netdev);
> >>> + return err;
> >>> + }
> >>> +
> >>> + priv->can.state = CAN_STATE_ERROR_ACTIVE;
> >>> + priv->open_time = jiffies;
> >>> + netif_start_queue(netdev);
> >>> +
> >>> + return 0;
> >>> +}
> >>> +
> >>> +static void kvaser_usb_unlink_tx_urbs(struct kvaser_usb_net_priv *priv)
> >>> +{
> >>> + int i;
> >>> +
> >>> + usb_kill_anchored_urbs(&priv->tx_submitted);
> >>> + atomic_set(&priv->active_tx_urbs, 0);
> >>> +
> >>> + for (i = 0; i < MAX_TX_URBS; i++)
> >> ARRAY_SIZE(priv->tx_contexts) instead of MAX_TX_URBS
> >
> > Ok.
> >
> >>> + priv->tx_contexts[i].echo_index = MAX_TX_URBS;
> >>> +}
> >>> +
> >>> +static void kvaser_usb_unlink_all_urbs(struct kvaser_usb *dev)
> >>> +{
> >>> + int i;
> >>> +
> >>> + usb_kill_anchored_urbs(&dev->rx_submitted);
> >>> +
> >>> + for (i = 0; i < MAX_NET_DEVICES; i++) {
> >> ARRAY_SIZE()
> >>> + struct kvaser_usb_net_priv *priv = dev->nets[i];
> >>> +
> >>> + if (priv)
> >>> + kvaser_usb_unlink_tx_urbs(priv);
> >>> + }
> >>> +}
> >>> +
> >>> +static int kvaser_usb_stop_chip(struct kvaser_usb_net_priv *priv)
> >>> +{
> >>> + int err;
> >>> +
> >>> + init_completion(&priv->start_stop_comp);
> >>> +
> >>> + err = kvaser_usb_send_simple_msg(priv->dev, CMD_STOP_CHIP,
> >>> + priv->channel);
> >>> + if (err)
> >>> + return err;
> >>> +
> >>> + if (!wait_for_completion_timeout(&priv->start_stop_comp,
> >>> + msecs_to_jiffies(STOP_TIMEOUT)))
> >>> + return -ETIMEDOUT;
> >>> +
> >>> + return 0;
> >>> +}
> >>> +
> >>> +static int kvaser_usb_flush_queue(struct kvaser_usb_net_priv *priv)
> >>> +{
> >>> + struct kvaser_msg msg;
> >>> +
> >>> + memset(&msg, 0x00, sizeof(msg));
> >>> + msg.id = CMD_FLUSH_QUEUE;
> >>> + msg.len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_flush_queue);
> >>> + msg.u.flush_queue.channel = priv->channel;
> >> C99 initialziers, please
> >
> > Ok.
> >
> >>> +
> >>> + return kvaser_usb_send_msg(priv->dev, &msg);
> >>> +}
> >>> +
> >>> +static int kvaser_usb_close(struct net_device *netdev)
> >>> +{
> >>> + struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
> >>> + int err;
> >>> +
> >>> + netif_stop_queue(netdev);
> >>> +
> >>> + err = kvaser_usb_flush_queue(priv);
> >>> + if (err)
> >>> + netdev_warn(netdev, "Cannot flush queue, error %d\n", err);
> >>> +
> >>> + err = kvaser_usb_stop_chip(priv);
> >>> + if (err) {
> >>> + netdev_warn(netdev, "Cannot stop device, error %d\n", err);
> >>> + return err;
> >>> + }
> >>> +
> >>> + kvaser_usb_unlink_tx_urbs(priv);
> >>> +
> >>> + priv->can.state = CAN_STATE_STOPPED;
> >>> + close_candev(priv->netdev);
> >>> + priv->open_time = 0;
> >>> +
> >>> + return 0;
> >>> +}
> >>> +
> >>> +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;
> >>> +
> >>> + usb_free_coherent(urb->dev, urb->transfer_buffer_length,
> >>> + 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);
> >>> +
> >>> + netdev->trans_start = jiffies;
> >>
> >> Is trans_start needed? at least for non-usb devices it works without.
> >
> > I don't know, I'll try to figure this out.
> > I see it's used in the two others CAN/USB drivers, 'ems_usb.c' and
> > 'esd_usb2.c'
> >
> >>
> >>> +}
> >>> +
> >>> +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;
> >>> + struct net_device_stats *stats = &netdev->stats;
> >>> + struct can_frame *cf = (struct can_frame *)skb->data;
> >>> + struct kvaser_usb_tx_urb_context *context = NULL;
> >>> + struct urb *urb;
> >>> + void *buf;
> >>> + struct kvaser_msg *msg;
> >>> + int i, err;
> >>> + int ret = NETDEV_TX_OK;
> >>> +
> >>> + if (can_dropped_invalid_skb(netdev, skb))
> >>> + return NETDEV_TX_OK;
> >>> +
> >>> + urb = usb_alloc_urb(0, GFP_ATOMIC);
> >>> + if (!urb) {
> >>> + netdev_err(netdev, "No memory left for URBs\n");
> >>> + stats->tx_dropped++;
> >>> + dev_kfree_skb(skb);
> >>> + goto nourbmem;
> >>> + }
> >>> +
> >>> + buf = usb_alloc_coherent(dev->udev, sizeof(struct kvaser_msg),
> >>> + 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 kvaser_msg *)buf;
> >>> + msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_tx_can);
> >>> + msg->u.tx_can.flags = 0;
> >>> + msg->u.tx_can.channel = priv->channel;
> >>> +
> >>> + if (cf->can_id & CAN_EFF_FLAG) {
> >>> + msg->id = CMD_TX_EXT_MESSAGE;
> >>> + msg->u.tx_can.msg[0] = (cf->can_id >> 24) & 0x1f;
> >>> + msg->u.tx_can.msg[1] = (cf->can_id >> 18) & 0x3f;
> >>> + msg->u.tx_can.msg[2] = (cf->can_id >> 14) & 0x0f;
> >>> + msg->u.tx_can.msg[3] = (cf->can_id >> 6) & 0xff;
> >>> + msg->u.tx_can.msg[4] = cf->can_id & 0x3f;
> >>> + } else {
> >>> + msg->id = CMD_TX_STD_MESSAGE;
> >>> + msg->u.tx_can.msg[0] = (cf->can_id >> 6) & 0x1f;
> >>> + msg->u.tx_can.msg[1] = cf->can_id & 0x3f;
> >>> + }
> >>> +
> >>> + msg->u.tx_can.msg[5] = cf->can_dlc;
> >>> + memcpy(&msg->u.tx_can.msg[6], cf->data, cf->can_dlc);
> >>> +
> >>> + if (cf->can_id & CAN_RTR_FLAG)
> >>> + msg->u.tx_can.flags |= MSG_FLAG_REMOTE_FRAME;
> >>> +
> >>> + for (i = 0; i < MAX_TX_URBS; i++) {
> >> ARRAY_SIZE
> >
> > Ok.
> >
> >>> + if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) {
> >>> + context = &priv->tx_contexts[i];
> >>> + break;
> >>> + }
> >>> + }
> >>> +
> >>> + if (!context) {
> >>> + netdev_warn(netdev, "cannot find free context\n");
> >>> + ret = NETDEV_TX_BUSY;
> >>> + goto releasebuf;
> >>> + }
> >>> +
> >>> + context->priv = priv;
> >>> + context->echo_index = i;
> >>> + context->dlc = cf->can_dlc;
> >>> +
> >>> + msg->u.tx_can.tid = context->echo_index;
> >>> +
> >>> + usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, 1),
> >>> + buf, msg->len,
> >>> + kvaser_usb_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);
> >>> +
> >>> + if (atomic_read(&priv->active_tx_urbs) >= MAX_TX_URBS)
> >>> + netif_stop_queue(netdev);
> >>> +
> >>> + err = usb_submit_urb(urb, GFP_ATOMIC);
> >>> + if (unlikely(err)) {
> >>> + can_free_echo_skb(netdev, context->echo_index);
> >>> +
> >>> + atomic_dec(&priv->active_tx_urbs);
> >>> + 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;
> >>> + }
> >>> +
> >>> + netdev->trans_start = jiffies;
> >>> +
> >>> + usb_free_urb(urb);
> >>> +
> >>> + return NETDEV_TX_OK;
> >>> +
> >>> +releasebuf:
> >>> + usb_free_coherent(dev->udev, sizeof(struct kvaser_msg),
> >>> + buf, urb->transfer_dma);
> >>> +nobufmem:
> >>> + usb_free_urb(urb);
> >>> +nourbmem:
> >>> + 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,
> >>> +};
> >>> +
> >>> +static struct can_bittiming_const kvaser_usb_bittiming_const = {
> >>> + .name = "kvaser_usb",
> >>> + .tseg1_min = KVASER_USB_TSEG1_MIN,
> >>> + .tseg1_max = KVASER_USB_TSEG1_MAX,
> >>> + .tseg2_min = KVASER_USB_TSEG2_MIN,
> >>> + .tseg2_max = KVASER_USB_TSEG2_MAX,
> >>> + .sjw_max = KVASER_USB_SJW_MAX,
> >>> + .brp_min = KVASER_USB_BRP_MIN,
> >>> + .brp_max = KVASER_USB_BRP_MAX,
> >>> + .brp_inc = KVASER_USB_BRP_INC,
> >>> +};
> >>> +
> >>> +static int kvaser_usb_set_bittiming(struct net_device *netdev)
> >>> +{
> >>> + struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
> >>> + struct can_bittiming *bt = &priv->can.bittiming;
> >>> + struct kvaser_usb *dev = priv->dev;
> >>> + struct kvaser_msg msg;
> >>> +
> >>> + msg.id = CMD_SET_BUS_PARAMS;
> >>> + msg.len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_busparams);
> >>> + msg.u.busparams.channel = priv->channel;
> >>> + msg.u.busparams.tid = 0xff;
> >>> + msg.u.busparams.bitrate = bt->bitrate;
> >>
> >> bitrate is le32
> >
> > Indeed ! I'll fix this.
> >
> >>
> >>> + msg.u.busparams.sjw = bt->sjw;
> >>> + msg.u.busparams.tseg1 = bt->prop_seg + bt->phase_seg1;
> >>> + msg.u.busparams.tseg2 = bt->phase_seg2;
> >>
> >> C99 initializers, please
> >>
> >>> +
> >>> + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
> >>> + msg.u.busparams.no_samp = 3;
> >>> + else
> >>> + msg.u.busparams.no_samp = 1;
> >>> +
> >>> + return kvaser_usb_send_msg(dev, &msg);
> >>> +}
> >>> +
> >>> +static int kvaser_usb_set_mode(struct net_device *netdev,
> >>> + enum can_mode mode)
> >>> +{
> >>> + struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
> >>> +
> >>> + if (!priv->open_time)
> >>> + return -EINVAL;
> >>> +
> >>> + switch (mode) {
> >>> + case CAN_MODE_START:
> >>> + if (netif_queue_stopped(netdev))
> >>> + netif_wake_queue(netdev);
> >>
> >> No need to restart your USB device?
> >
> > No. I don't think so.
> > The module continuously tries to transmit the frame and isn't stopped.
> > So there is no need to restart it if it has been explicitely stopped.
> >
> > When it cannot transmit, the module try again and sends continuously
> > CMD_CAN_ERROR_EVENT frames until it succeeds to transmit the frame.
> > If the device is stopped with the command CMD_STOP_CHIP then it stops
> > sending these CMD_CAN_ERROR_EVENT.
> > Should I handle this in another manner?
>
> If the firmware automatically recovers from busoff (like the at91 does),
> you should stop the chip it priv->can.restart_ms == 0 and let the chip
> continue working otherwise.
>
> >
> >>
> >>> + break;
> >>> + default:
> >>> + return -EOPNOTSUPP;
> >>> + }
> >>> +
> >>> + return 0;
> >>> +}
> >>> +
> >>> +static int kvaser_usb_get_berr_counter(const struct net_device *netdev,
> >>> + struct can_berr_counter *bec)
> >>> +{
> >>> + struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
> >>> +
> >>> + bec->txerr = priv->bec.txerr;
> >>> + bec->rxerr = priv->bec.rxerr;
> >>> +
> >>> + return 0;
> >>> +}
> >>> +
> >>> +static int kvaser_usb_init_one(struct usb_interface *intf,
> >>> + const struct usb_device_id *id, int channel)
> >>> +{
> >>> + struct kvaser_usb *dev = usb_get_intfdata(intf);
> >>> + struct net_device *netdev;
> >>> + struct kvaser_usb_net_priv *priv;
> >>> + int i, err;
> >>> +
> >>> + netdev = alloc_candev(sizeof(*priv), MAX_TX_URBS);
> >>> + if (!netdev) {
> >>> + dev_err(&intf->dev, "Cannot alloc candev\n");
> >>> + return -ENOMEM;
> >>> + }
> >>> +
> >>> + priv = netdev_priv(netdev);
> >>> +
> >>> + 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->dev = dev;
> >>> + priv->netdev = netdev;
> >>> + priv->channel = channel;
> >>> +
> >>> + priv->can.state = CAN_STATE_STOPPED;
> >>> + priv->can.clock.freq = CAN_USB_CLOCK;
> >>> + priv->can.bittiming_const = &kvaser_usb_bittiming_const;
> >>> + priv->can.do_set_bittiming = kvaser_usb_set_bittiming;
> >>> + priv->can.do_set_mode = kvaser_usb_set_mode;
> >>> + priv->can.do_get_berr_counter = kvaser_usb_get_berr_counter;
> >>> + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES;
> >>> + if (id->driver_info & KVASER_HAS_SILENT_MODE)
> >>> + priv->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY;
> >>> +
> >>> + netdev->flags |= IFF_ECHO;
> >>> +
> >>> + netdev->netdev_ops = &kvaser_usb_netdev_ops;
> >>> +
> >>> + SET_NETDEV_DEV(netdev, &intf->dev);
> >>> +
> >>> + err = register_candev(netdev);
> >>> + if (err) {
> >>> + dev_err(&intf->dev, "Failed to register can device\n");
> >>> + free_candev(netdev);
> >>> + return err;
> >>> + }
> >>> +
> >>> + dev->nets[channel] = priv;
> >>> + netdev_info(netdev, "device %s registered\n", netdev->name);
> >>
> >> netdev_info should take care of printing the device's name.
> >
> > Ok.
> >
> >>> +
> >>> + return 0;
> >>> +}
> >>> +
> >>> +static int kvaser_usb_probe(struct usb_interface *intf,
> >>> + const struct usb_device_id *id)
> >>> +{
> >>> + struct kvaser_usb *dev;
> >>> + int err = -ENOMEM;
> >>> + int i;
> >>> +
> >>> + dev = kzalloc(sizeof(*dev), GFP_KERNEL);
> >>
> >> Who will free dev on driver unload? Please make use of devm_kzalloc().
> >
> > Ok. kfree is missing is disconnect().
> > I'll replace it by devm_kzalloc() and devm_free().
>
> The beauty of devm_kzalloc is you don't have to call *_free, its
> automatically called if probe fails or when remove function has been called.
Cool :-)
>
> >
> >>
> >>> + if (!dev)
> >>> + return -ENOMEM;
> >>> +
> >>> + dev->udev = interface_to_usbdev(intf);
> >>> +
> >>> + init_usb_anchor(&dev->rx_submitted);
> >>> +
> >>> + usb_set_intfdata(intf, dev);
> >>> +
> >>> + if (kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, 0)) {
> >>> + dev_err(&intf->dev, "Cannot reset kvaser\n");
> >>> + goto error;
> >>> + }
> >>> +
> >>> + if (kvaser_usb_get_software_info(dev)) {
> >>> + dev_err(&intf->dev, "Cannot get software infos\n");
> >>> + goto error;
> >>> + }
> >>> +
> >>> + if (kvaser_usb_get_card_info(dev)) {
> >>> + dev_err(&intf->dev, "Cannot get card infos\n");
> >>> + goto error;
> >>> + }
> >>> +
> >>> + dev_dbg(&intf->dev, "Firmware version: %d.%d.%d\n",
> >>> + ((dev->fw_version >> 24) & 0xff),
> >>> + ((dev->fw_version >> 16) & 0xff),
> >>> + (dev->fw_version & 0xffff));
> >>> +
> >>> + for (i = 0; i < dev->nchannels; i++)
> >>> + kvaser_usb_init_one(intf, id, i);
> >>> +
> >>> + return 0;
> >>> +
> >>> +error:
> >>> + kfree(dev);
> >>> + return err;
> >>> +}
> >>> +
> >>> +static void kvaser_usb_disconnect(struct usb_interface *intf)
> >>> +{
> >>> + struct kvaser_usb *dev = usb_get_intfdata(intf);
> >>> + int i;
> >>> +
> >>> + usb_set_intfdata(intf, NULL);
> >>> +
> >>> + if (!dev)
> >>> + return;
> >>> +
> >>> + for (i = 0; i < dev->nchannels; i++) {
> >>> + if (!dev->nets[i])
> >>> + continue;
> >>> +
> >>> + unregister_netdev(dev->nets[i]->netdev);
> >>> + free_candev(dev->nets[i]->netdev);
> >>> + }
> >>> +
> >>> + kvaser_usb_unlink_all_urbs(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_DESCRIPTION("Can driver for Kvaser CAN/USB devices");
> >>> +MODULE_LICENSE("GPL v2");
> >>> diff --git a/drivers/net/can/usb/kvaser_usb.h b/drivers/net/can/usb/kvaser_usb.h
> >>> new file mode 100644
> >>> index 0000000..8e0b6ab
> >>> --- /dev/null
> >>> +++ b/drivers/net/can/usb/kvaser_usb.h
> >>> @@ -0,0 +1,237 @@
> >>> +#ifndef _KVASER_USB_H_
> >>> +#define _KVASER_USB_H_
> >>> +
> >>> +#define MAX_TX_URBS 16
> >> Please no tab between define and macro name
> >
> > Ok I didn't know it was not allowed... checkpatch didn't complain.
>
> It's allowed, but not used without tab it's more common, at least among
> CAN drivers.
>
> >
> >>> +#define MAX_RX_URBS 4
> >>> +#define START_TIMEOUT 1000
> >>> +#define STOP_TIMEOUT 1000
> >>> +#define USB_SEND_TIMEOUT 1000
> >>> +#define USB_RECEIVE_TIMEOUT 1000
> >>> +#define RX_BUFFER_SIZE 3072
> >>> +#define CAN_USB_CLOCK 8000000
> >>> +#define MAX_NET_DEVICES 3
> >>> +
> >>> +/* Kvaser USB devices */
> >>> +#define KVASER_VENDOR_ID 0x0bfd
> >>> +#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
> >>> +
> >>> +/* USB devices features */
> >>> +#define KVASER_HAS_SILENT_MODE (1 << 0)
> >> pleae use BIT(0)
> >>> +
> >>> +/* Message header size */
> >>> +#define MSG_HEADER_LEN 2
> >>> +
> >>> +/* Can message flags */
> >>> +#define MSG_FLAG_ERROR_FRAME (1 << 0)
> >>> +#define MSG_FLAG_OVERRUN (1 << 1)
> >>> +#define MSG_FLAG_NERR (1 << 2)
> >>> +#define MSG_FLAG_WAKEUP (1 << 3)
> >>> +#define MSG_FLAG_REMOTE_FRAME (1 << 4)
> >>> +#define MSG_FLAG_RESERVED (1 << 5)
> >>> +#define MSG_FLAG_TX_ACK (1 << 6)
> >>> +#define MSG_FLAG_TX_REQUEST (1 << 7)
> >>> +
> >>> +/* Can states */
> >>> +#define M16C_STATE_BUS_RESET (1 << 0)
> >>> +#define M16C_STATE_BUS_ERROR (1 << 4)
> >>> +#define M16C_STATE_BUS_PASSIVE (1 << 5)
> >>> +#define M16C_STATE_BUS_OFF (1 << 6)
> >>> +
> >>> +/* Can msg 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_GET_CTRL_MODE 22
> >>> +#define CMD_GET_CTRL_MODE_REPLY 23
> >>> +#define CMD_RESET_CHIP 24
> >>> +#define CMD_RESET_CHIP_REPLY 25
> >>> +#define CMD_START_CHIP 26
> >>> +#define CMD_START_CHIP_REPLY 27
> >>> +#define CMD_STOP_CHIP 28
> >>> +#define CMD_STOP_CHIP_REPLY 29
> >>> +#define CMD_GET_CARD_INFO2 32
> >>> +#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_USB_THROTTLE 77
> >>> +
> >>> +/* error factors */
> >>> +#define M16C_EF_ACKE (1 << 0)
> >>> +#define M16C_EF_CRCE (1 << 1)
> >>> +#define M16C_EF_FORME (1 << 2)
> >>> +#define M16C_EF_STFE (1 << 3)
> >>> +#define M16C_EF_BITE0 (1 << 4)
> >>> +#define M16C_EF_BITE1 (1 << 5)
> >>> +#define M16C_EF_RCVE (1 << 6)
> >>> +#define M16C_EF_TRE (1 << 7)
> >>> +
> >>> +/* bittiming parameters */
> >>> +#define KVASER_USB_TSEG1_MIN 1
> >>> +#define KVASER_USB_TSEG1_MAX 16
> >>> +#define KVASER_USB_TSEG2_MIN 1
> >>> +#define KVASER_USB_TSEG2_MAX 8
> >>> +#define KVASER_USB_SJW_MAX 4
> >>> +#define KVASER_USB_BRP_MIN 1
> >>> +#define KVASER_USB_BRP_MAX 64
> >>> +#define KVASER_USB_BRP_INC 1
> >>> +
> >>> +/* 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
> >>> +
> >>> +struct kvaser_msg_simple {
> >>> + u8 tid;
> >>> + u8 channel;
> >>> +} __packed;
> >>> +
> >>> +struct kvaser_msg_cardinfo {
> >>> + u8 tid;
> >>> + u8 nchannels;
> >>> + __le32 serial_number;
> >>> + __le32 padding;
> >>> + __le32 clock_resolution;
> >>> + __le32 mfgdate;
> >>> + u8 ean[8];
> >>> + u8 hw_revision;
> >>> + u8 usb_hs_mode;
> >>> + __le16 padding2;
> >>> +} __packed;
> >>> +
> >>> +struct kvaser_msg_cardinfo2 {
> >>> + u8 tid;
> >>> + u8 channel;
> >>> + u8 pcb_id[24];
> >>> + __le32 oem_unlock_code;
> >>> +} __packed;
> >>> +
> >>> +struct kvaser_msg_softinfo {
> >>> + u8 tid;
> >>> + u8 channel;
> >>> + __le32 sw_options;
> >>> + __le32 fw_version;
> >>> + __le16 max_outstanding_tx;
> >>> + __le16 padding[9];
> >>> +} __packed;
> >>> +
> >>> +struct kvaser_msg_busparams {
> >>> + u8 tid;
> >>> + u8 channel;
> >>> + __le32 bitrate;
> >>> + u8 tseg1;
> >>> + u8 tseg2;
> >>> + u8 sjw;
> >>> + u8 no_samp;
> >>> +} __packed;
> >>> +
> >>> +struct kvaser_msg_tx_can {
> >>> + u8 channel;
> >>> + u8 tid;
> >>> + u8 msg[14];
> >>> + u8 padding;
> >>> + u8 flags;
> >>> +} __packed;
> >>> +
> >>> +struct kvaser_msg_rx_can {
> >>> + u8 channel;
> >>> + u8 flag;
> >>> + __le16 time[3];
> >>> + u8 msg[14];
> >>> +} __packed;
> >>> +
> >>> +struct kvaser_msg_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 kvaser_msg_tx_acknowledge {
> >>> + u8 channel;
> >>> + u8 tid;
> >>> + __le16 time[3];
> >>> + u8 flags;
> >>> + u8 time_offset;
> >>> +} __packed;
> >>> +
> >>> +struct kvaser_msg_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 kvaser_msg_ctrl_mode {
> >>> + u8 tid;
> >>> + u8 channel;
> >>> + u8 ctrl_mode;
> >>> + u8 padding[3];
> >>> +} __packed;
> >>> +
> >>> +struct kvaser_msg_flush_queue {
> >>> + u8 tid;
> >>> + u8 channel;
> >>> + u8 flags;
> >>> + u8 padding[3];
> >>> +} __packed;
> >>> +
> >>> +struct kvaser_msg {
> >>> + u8 len;
> >>> + u8 id;
> >>> + union {
> >>> + struct kvaser_msg_simple simple;
> >>> + struct kvaser_msg_cardinfo cardinfo;
> >>> + struct kvaser_msg_cardinfo2 cardinfo2;
> >>> + struct kvaser_msg_softinfo softinfo;
> >>> + struct kvaser_msg_busparams busparams;
> >>> + struct kvaser_msg_tx_can tx_can;
> >>> + struct kvaser_msg_rx_can rx_can;
> >>> + struct kvaser_msg_chip_state_event chip_state_event;
> >>> + struct kvaser_msg_tx_acknowledge tx_acknowledge;
> >>> + struct kvaser_msg_error_event error_event;
> >>> + struct kvaser_msg_ctrl_mode ctrl_mode;
> >>> + struct kvaser_msg_ctrl_mode flush_queue;
> >>> + } u;
> >>> +} __packed;
> >>> +
> >>> +#endif
> >>>
> >>
> >>
> >> --
> >> Pengutronix e.K. | Marc Kleine-Budde |
> >> Industrial Linux Solutions | Phone: +49-231-2826-924 |
> >> Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
> >> Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
> >>
> >
> > Thanks for the review.
> np,
>
> regards, Marc
>
> --
> Pengutronix e.K. | Marc Kleine-Budde |
> Industrial Linux Solutions | Phone: +49-231-2826-924 |
> Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
> Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
>
Thanks,
--
Olivier
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH] can: kvaser_usb: Add support for Kvaser CAN/USB devices
2012-07-31 13:06 ` Olivier Sobrie
@ 2012-07-31 13:27 ` Marc Kleine-Budde
2012-08-02 10:53 ` Olivier Sobrie
2012-07-31 13:43 ` Wolfgang Grandegger
1 sibling, 1 reply; 43+ messages in thread
From: Marc Kleine-Budde @ 2012-07-31 13:27 UTC (permalink / raw)
To: Olivier Sobrie; +Cc: Wolfgang Grandegger, linux-can, netdev
[-- Attachment #1: Type: text/plain, Size: 10413 bytes --]
On 07/31/2012 03:06 PM, Olivier Sobrie wrote:
[...]
>>>> Please check if your driver survives hot-unplugging while sending and
>>>> receiving CAN frames at maximum laod.
>>>
>>> I tested this with two Kvaser sending frames with "cangen can0 -g 0 -i"
>>> never saw a crash.
>>
>> Please test send sending and receiving at the same time.
>
> Yes that's what I did; "cangen can0 -g 0 -i" on both sides.
Fine.
[...]
>>>>> --- /dev/null
>>>>> +++ b/drivers/net/can/usb/kvaser_usb.c
>>>>> @@ -0,0 +1,1062 @@
>>>>> +/*
>>>>
>>>> Please add a license statement and probably your copyright:
>>>>
>>>> * This program is free software; you can redistribute it and/or
>>>> * modify it under the terms of the GNU General Public License as
>>>> * published by the Free Software Foundation version 2.
>>>>
>>>> You also should copy the copyright from the drivers you used:
>>>>
>>>>> + * Parts of this driver are based on the following:
>>>>> + * - Kvaser linux leaf driver (version 4.78)
>>>>
>>>> I just downloaded their driver and noticed that it's quite sparse on
>>>> stating the license the code is released under.
>>>> "doc/HTMLhelp/copyright.htx" is quite restrictive, the word GPL occurs 3
>>>> times, all in MODULE_LICENSE("GPL"). Running modinfo on the usbcan.ko
>>>> shows "license: GPL"
>>>
>>> I'll add the license statement.
>>> In fact it's the leaf.ko which is used for this device and it is under
>>> GPL as modinfo said.
>>
>> I just talked to my boss and we're the same opinion, that
>> MODULE_LICENSE("GPL") is a technical term and not relevant if the
>> included license doesn't say a word about GPL. If the kvaser tarball
>> violates the GPL, however is written on different sheet of paper (as we
>> say in Germany).
>>
>> So I cannot put my S-o-b under this driver as long as we haven't talked
>> to kvaser.
>
> Ok I thought it was sufficient enough to have MODULE_LICENSE("GPL") in
> the code to indicate it is a GPL driver. I'll ask Kvaser before sending
> any new version of the patch.
We can continue the review process, this problem has to be sorted out
before I can apply this patch to linux-can-next tree.
[...]
>>>>> +static void kvaser_usb_rx_error(const struct kvaser_usb *dev,
>>>>> + const struct kvaser_msg *msg)
>>>>> +{
>>>>> + struct can_frame *cf;
>>>>> + struct sk_buff *skb;
>>>>> + struct net_device_stats *stats;
>>>>> + struct kvaser_usb_net_priv *priv;
>>>>> + u8 channel, status, txerr, rxerr;
>>>>> +
>>>>> + if (msg->id == CMD_CAN_ERROR_EVENT) {
>>>>> + channel = msg->u.error_event.channel;
>>>>> + status = msg->u.error_event.status;
>>>>> + txerr = msg->u.error_event.tx_errors_count;
>>>>> + rxerr = msg->u.error_event.rx_errors_count;
>>>>> + } else {
>>>>> + channel = msg->u.chip_state_event.channel;
>>>>> + status = msg->u.chip_state_event.status;
>>>>> + txerr = msg->u.chip_state_event.tx_errors_count;
>>>>> + rxerr = msg->u.chip_state_event.rx_errors_count;
>>>>> + }
>>>>> +
>>>>> + if (channel >= dev->nchannels) {
>>>>> + dev_err(dev->udev->dev.parent,
>>>>> + "Invalid channel number (%d)\n", channel);
>>>>> + return;
>>>>> + }
>>>>> +
>>>>> + priv = dev->nets[channel];
>>>>> + stats = &priv->netdev->stats;
>>>>> +
>>>>> + skb = alloc_can_err_skb(priv->netdev, &cf);
>>>>> + if (!skb) {
>>>>> + stats->rx_dropped++;
>>>>> + return;
>>>>> + }
>>>>> +
>>>>> + if ((status & M16C_STATE_BUS_OFF) ||
>>>>> + (status & M16C_STATE_BUS_RESET)) {
>>>>> + priv->can.state = CAN_STATE_BUS_OFF;
>>>>> + cf->can_id |= CAN_ERR_BUSOFF;
>>>>> + can_bus_off(priv->netdev);
>>
>> you should increment priv->can.can_stats.bus_off
>> What does the firmware do in this state? Does it automatically try to
>> recover and try to send the outstanding frames?
>
> Yes that's what I observed.
It's behaves like the at91_can.
>> If so, you should turn of the CAN interface, it possible. See:
>> http://lxr.free-electrons.com/source/drivers/net/can/at91_can.c#L986
>
> Ok I'll have a look at this.
>
>>
>> Please test Bus-Off behaviour:
>> - setup working CAN network
>> - short circuit CAN-H and CAN-L wires
>> - start "candump any,0:0,#FFFFFFFF" on one shell
>> - send one can frame on the other
>>
>> then
>>
>> - remove the short circuit
>> - see if the can frame is transmitted to the other side
>> - it should show up as an echo'ed CAN frame on the sender side
>>
>> Repeat the same test with disconnecting CAN-H and CAN-L from the other
>> CAN station instead of short circuit.
>>
>> Please send the output from candump.
>
> 1) With the short circuit:
>
> I perform the test you described. It showed that the Kvaser passes from
> ERROR-WARNING to ERROR-PASSIVE and then BUS-OFF. But after going to the
> state BUS-OFF it comes back to ERROR-WARNING.
>
> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
Why don't we have any rx/tx numbers in the error frame?
> ...
> can1 200000C8 [8] 00 00 90 00 00 00 00 00 ERRORFRAME <-- bus off
> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> ...
> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> can1 123 [2] 11 22 <-- short circuit removed
>
> I see the echo and on the other end I see the frame coming in.
> By the way I see that the txerr and rxerr fields of the structure
> kvaser_msg_error_event stay at 0.
>
> 2) With CAN-H and CAN-L disconnected:
>
> I never see the bus going in OFF state. It stays in PASSIVE mode.
>
> can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
> ...
> can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 80 1B 00 00 00 00 ERRORFRAME
> can1 123 [2] 11 22 <-- other end connected
From the hardware point of view the short circuit and open end tests
look good. Please adjust the driver to turn off the CAN interface in
case of a bus off if restart_ms is 0.
>>>>> + } else if (status & M16C_STATE_BUS_ERROR) {
>>>>> + priv->can.state = CAN_STATE_ERROR_WARNING;
>>>>> + priv->can.can_stats.error_warning++;
>>>>> + } else if (status & M16C_STATE_BUS_PASSIVE) {
>>>>> + priv->can.state = CAN_STATE_ERROR_PASSIVE;
>>>>> + priv->can.can_stats.error_passive++;
>>>>> + } else {
>>>>> + priv->can.state = CAN_STATE_ERROR_ACTIVE;
>>>>> + cf->can_id |= CAN_ERR_PROT;
>>>>> + cf->data[2] = CAN_ERR_PROT_ACTIVE;
>>>>> + }
>>>>> +
>>>>> + if (msg->id == CMD_CAN_ERROR_EVENT) {
>>>>> + u8 error_factor = msg->u.error_event.error_factor;
>>>>> +
>>>>> + priv->can.can_stats.bus_error++;
>>>>> + stats->rx_errors++;
>>>>> +
>>>>> + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
>>>>> +
>>>>> + 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;
Please use CAN_ERR_CRTL_RX_WARNING, CAN_ERR_CRTL_TX_WARNING where
appropriate.
>>>>> + }
>>>>> +
>>>>> + if (error_factor & M16C_EF_ACKE)
>>>>> + cf->data[3] |= (CAN_ERR_PROT_LOC_ACK |
>>>>> + CAN_ERR_PROT_LOC_ACK_DEL);
>>>>> + if (error_factor & M16C_EF_CRCE)
>>>>> + cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ |
>>>>> + CAN_ERR_PROT_LOC_CRC_DEL);
>>>>> + if (error_factor & M16C_EF_FORME)
>>>>> + cf->data[2] |= CAN_ERR_PROT_FORM;
>>>>> + if (error_factor & M16C_EF_STFE)
>>>>> + cf->data[2] |= CAN_ERR_PROT_STUFF;
>>>>> + if (error_factor & M16C_EF_BITE0)
>>>>> + cf->data[2] |= CAN_ERR_PROT_BIT0;
>>>>> + if (error_factor & M16C_EF_BITE1)
>>>>> + cf->data[2] |= CAN_ERR_PROT_BIT1;
>>>>> + if (error_factor & M16C_EF_TRE)
>>>>> + cf->data[2] |= CAN_ERR_PROT_TX;
>>>>> + }
>>>>> +
>>>>> + cf->data[6] = txerr;
>>>>> + cf->data[7] = rxerr;
>>>>> +
>>>>> + netif_rx(skb);
>>>>> +
>>>>> + priv->bec.txerr = txerr;
>>>>> + priv->bec.rxerr = rxerr;
>>>>> +
>>>>> + stats->rx_packets++;
>>>>> + stats->rx_bytes += cf->can_dlc;
>>>>> +}
>>>>> +
[...]
>>>>> +static int kvaser_usb_get_berr_counter(const struct net_device *netdev,
>>>>> + struct can_berr_counter *bec)
>>>>> +{
>>>>> + struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
>>>>> +
>>>>> + bec->txerr = priv->bec.txerr;
>>>>> + bec->rxerr = priv->bec.rxerr;
I think you can copy the struct like this:
*bec = priv->bec;
>>>>> +
>>>>> + return 0;
>>>>> +}
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Industrial Linux Solutions | Phone: +49-231-2826-924 |
Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 262 bytes --]
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH] can: kvaser_usb: Add support for Kvaser CAN/USB devices
2012-07-31 13:27 ` Marc Kleine-Budde
@ 2012-08-02 10:53 ` Olivier Sobrie
2012-08-02 11:56 ` Marc Kleine-Budde
0 siblings, 1 reply; 43+ messages in thread
From: Olivier Sobrie @ 2012-08-02 10:53 UTC (permalink / raw)
To: Marc Kleine-Budde; +Cc: Wolfgang Grandegger, linux-can, netdev
Hello,
On Tue, Jul 31, 2012 at 03:27:55PM +0200, Marc Kleine-Budde wrote:
> We can continue the review process, this problem has to be sorted out
> before I can apply this patch to linux-can-next tree.
Ok.
> > 1) With the short circuit:
> >
> > I perform the test you described. It showed that the Kvaser passes from
> > ERROR-WARNING to ERROR-PASSIVE and then BUS-OFF. But after going to the
> > state BUS-OFF it comes back to ERROR-WARNING.
> >
> > can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> > can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> > can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> > can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> > can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> > can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> > can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> > can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> > can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
>
> Why don't we have any rx/tx numbers in the error frame?
Because the hardware seems to not update the tx/rx_errors_count
fields :-(
> From the hardware point of view the short circuit and open end tests
> look good. Please adjust the driver to turn off the CAN interface in
> case of a bus off if restart_ms is 0.
And in the case where restart_ms is not 0? Don't I've to put it off so
and drop the frame?
I actually implemeted it as you said and here is what I observed in
candump output with restart_ms set to 100 ms:
t0: Short circuit between CAN-H and CAN-L + cansend can1 123#1122
can1 2000008C [8] 00 04 90 00 00 00 00 00 ERRORFRAME
controller-problem{rx-error-warning}
protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
bus-error
can1 2000008C [8] 00 10 90 00 00 00 00 00 ERRORFRAME
controller-problem{rx-error-passive}
protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
bus-error
can1 200000C8 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
bus-off
bus-error
...
can1 2000008C [8] 00 04 90 00 00 00 00 00 ERRORFRAME
controller-problem{rx-error-warning}
protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
bus-error
can1 2000008C [8] 00 10 90 00 00 00 00 00 ERRORFRAME
controller-problem{rx-error-passive}
protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
bus-error
can1 200000C8 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
bus-off
bus-error
t1: short circuit removed
can1 123 [2] 11 22
can1 20000100 [8] 00 00 00 00 00 00 00 00 ERRORFRAME
restarted-after-bus-of
The echo coming before the restart looks weird? No?
Shouldn't we drop the frame once BUF-OFF is reached?
> >>>>> + 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;
>
> Please use CAN_ERR_CRTL_RX_WARNING, CAN_ERR_CRTL_TX_WARNING where
> appropriate.
Ok. As the hardware doesn't report good values for txerr and rxerr, I'll
also remove the tests on txerr and rxerr.
I observed the same behavior with the original driver.
I asked Kvaser for this problem. I've to wait before their developer is
back (same for the GPL issue).
> >>>>> +static int kvaser_usb_get_berr_counter(const struct net_device *netdev,
> >>>>> + struct can_berr_counter *bec)
> >>>>> +{
> >>>>> + struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
> >>>>> +
> >>>>> + bec->txerr = priv->bec.txerr;
> >>>>> + bec->rxerr = priv->bec.rxerr;
>
> I think you can copy the struct like this:
>
> *bec = priv->bec;
Thanks. I'll remove the function kvaser_usb_get_berr_counter as the
hardware seems to never report txerr and rxerr.
I'll look deeper at this driver during the week-end if possible...
Thanks a lot for your help,
--
Olivier
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH] can: kvaser_usb: Add support for Kvaser CAN/USB devices
2012-08-02 10:53 ` Olivier Sobrie
@ 2012-08-02 11:56 ` Marc Kleine-Budde
2012-08-02 12:16 ` Olivier Sobrie
2012-08-05 20:43 ` Wolfgang Grandegger
0 siblings, 2 replies; 43+ messages in thread
From: Marc Kleine-Budde @ 2012-08-02 11:56 UTC (permalink / raw)
To: Olivier Sobrie; +Cc: Wolfgang Grandegger, linux-can, netdev
[-- Attachment #1: Type: text/plain, Size: 4693 bytes --]
On 08/02/2012 12:53 PM, Olivier Sobrie wrote:
>>> 1) With the short circuit:
>>>
>>> I perform the test you described. It showed that the Kvaser passes from
>>> ERROR-WARNING to ERROR-PASSIVE and then BUS-OFF. But after going to the
>>> state BUS-OFF it comes back to ERROR-WARNING.
>>>
>>> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
>>> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
>>> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
>>> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
>>> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
>>> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
>>> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
>>> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
>>> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
>>
>> Why don't we have any rx/tx numbers in the error frame?
>
> Because the hardware seems to not update the tx/rx_errors_count
> fields :-(
Okay.
>> From the hardware point of view the short circuit and open end tests
>> look good. Please adjust the driver to turn off the CAN interface in
>> case of a bus off if restart_ms is 0.
>
> And in the case where restart_ms is not 0? Don't I've to put it off so
> and drop the frame?
No, don't drop the frame. restart-ms != 0 means the controller is
automatically restarted after the specified time (if the controller
supports). Or in your and the at91 case, automatically.
> I actually implemeted it as you said and here is what I observed in
> candump output with restart_ms set to 100 ms:
>
> t0: Short circuit between CAN-H and CAN-L + cansend can1 123#1122
> can1 2000008C [8] 00 04 90 00 00 00 00 00 ERRORFRAME
> controller-problem{rx-error-warning}
> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> bus-error
> can1 2000008C [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> controller-problem{rx-error-passive}
> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> bus-error
> can1 200000C8 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> bus-off
> bus-error
> ...
> can1 2000008C [8] 00 04 90 00 00 00 00 00 ERRORFRAME
> controller-problem{rx-error-warning}
> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> bus-error
> can1 2000008C [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> controller-problem{rx-error-passive}
> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> bus-error
> can1 200000C8 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> bus-off
> bus-error
>
> t1: short circuit removed
> can1 123 [2] 11 22
> can1 20000100 [8] 00 00 00 00 00 00 00 00 ERRORFRAME
> restarted-after-bus-of
>
> The echo coming before the restart looks weird? No?
> Shouldn't we drop the frame once BUF-OFF is reached?
No, I don't think so. But wait for Wolfgang, here's more into error
handling then me.
>
>>>>>>> + 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;
>>
>> Please use CAN_ERR_CRTL_RX_WARNING, CAN_ERR_CRTL_TX_WARNING where
>> appropriate.
> Ok. As the hardware doesn't report good values for txerr and rxerr, I'll
> also remove the tests on txerr and rxerr.
> I observed the same behavior with the original driver.
> I asked Kvaser for this problem. I've to wait before their developer is
> back (same for the GPL issue).
Okay.
>>>>>>> +static int kvaser_usb_get_berr_counter(const struct net_device *netdev,
>>>>>>> + struct can_berr_counter *bec)
>>>>>>> +{
>>>>>>> + struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
>>>>>>> +
>>>>>>> + bec->txerr = priv->bec.txerr;
>>>>>>> + bec->rxerr = priv->bec.rxerr;
>>
>> I think you can copy the struct like this:
>>
>> *bec = priv->bec;
>
> Thanks. I'll remove the function kvaser_usb_get_berr_counter as the
> hardware seems to never report txerr and rxerr.
Sounds reasonable.
BTW: is it possible to update the firmware on these devices?
> I'll look deeper at this driver during the week-end if possible...
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Industrial Linux Solutions | Phone: +49-231-2826-924 |
Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 262 bytes --]
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH] can: kvaser_usb: Add support for Kvaser CAN/USB devices
2012-08-02 11:56 ` Marc Kleine-Budde
@ 2012-08-02 12:16 ` Olivier Sobrie
2012-08-02 12:33 ` Marc Kleine-Budde
2012-08-05 20:43 ` Wolfgang Grandegger
1 sibling, 1 reply; 43+ messages in thread
From: Olivier Sobrie @ 2012-08-02 12:16 UTC (permalink / raw)
To: Marc Kleine-Budde; +Cc: Wolfgang Grandegger, linux-can, netdev
On Thu, Aug 02, 2012 at 01:56:23PM +0200, Marc Kleine-Budde wrote:
> BTW: is it possible to update the firmware on these devices?
Yes it possible. But I don't know how to do it... I never did it.
I saw on their website that they propose a zipfile with new firmwares.
Inside the zipfile there is a tool 'Update.exe' which, I suppose, does
the upgrade.
Kr,
--
Olivier
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH] can: kvaser_usb: Add support for Kvaser CAN/USB devices
2012-08-02 12:16 ` Olivier Sobrie
@ 2012-08-02 12:33 ` Marc Kleine-Budde
2012-08-02 13:20 ` Olivier Sobrie
0 siblings, 1 reply; 43+ messages in thread
From: Marc Kleine-Budde @ 2012-08-02 12:33 UTC (permalink / raw)
To: Olivier Sobrie; +Cc: Wolfgang Grandegger, linux-can, netdev
[-- Attachment #1: Type: text/plain, Size: 771 bytes --]
On 08/02/2012 02:16 PM, Olivier Sobrie wrote:
> On Thu, Aug 02, 2012 at 01:56:23PM +0200, Marc Kleine-Budde wrote:
>> BTW: is it possible to update the firmware on these devices?
>
> Yes it possible. But I don't know how to do it... I never did it.
> I saw on their website that they propose a zipfile with new firmwares.
> Inside the zipfile there is a tool 'Update.exe' which, I suppose, does
> the upgrade.
...on windows only :). If they would be cool, they support dfu.
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Industrial Linux Solutions | Phone: +49-231-2826-924 |
Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 262 bytes --]
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH] can: kvaser_usb: Add support for Kvaser CAN/USB devices
2012-08-02 12:33 ` Marc Kleine-Budde
@ 2012-08-02 13:20 ` Olivier Sobrie
0 siblings, 0 replies; 43+ messages in thread
From: Olivier Sobrie @ 2012-08-02 13:20 UTC (permalink / raw)
To: Marc Kleine-Budde; +Cc: Wolfgang Grandegger, linux-can, netdev
On Thu, Aug 02, 2012 at 02:33:56PM +0200, Marc Kleine-Budde wrote:
> On 08/02/2012 02:16 PM, Olivier Sobrie wrote:
> > On Thu, Aug 02, 2012 at 01:56:23PM +0200, Marc Kleine-Budde wrote:
> >> BTW: is it possible to update the firmware on these devices?
> >
> > Yes it possible. But I don't know how to do it... I never did it.
> > I saw on their website that they propose a zipfile with new firmwares.
> > Inside the zipfile there is a tool 'Update.exe' which, I suppose, does
> > the upgrade.
>
> ...on windows only :). If they would be cool, they support dfu.
Indeed. I'll check if they are cool ;-)
--
Olivier
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH] can: kvaser_usb: Add support for Kvaser CAN/USB devices
2012-08-02 11:56 ` Marc Kleine-Budde
2012-08-02 12:16 ` Olivier Sobrie
@ 2012-08-05 20:43 ` Wolfgang Grandegger
2012-08-06 5:27 ` Olivier Sobrie
1 sibling, 1 reply; 43+ messages in thread
From: Wolfgang Grandegger @ 2012-08-05 20:43 UTC (permalink / raw)
To: Marc Kleine-Budde; +Cc: Olivier Sobrie, linux-can, netdev
On 08/02/2012 01:56 PM, Marc Kleine-Budde wrote:
> On 08/02/2012 12:53 PM, Olivier Sobrie wrote:
...
>> I actually implemeted it as you said and here is what I observed in
>> candump output with restart_ms set to 100 ms:
>>
>> t0: Short circuit between CAN-H and CAN-L + cansend can1 123#1122
>> can1 2000008C [8] 00 04 90 00 00 00 00 00 ERRORFRAME
>> controller-problem{rx-error-warning}
>> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
>> bus-error
>> can1 2000008C [8] 00 10 90 00 00 00 00 00 ERRORFRAME
>> controller-problem{rx-error-passive}
>> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
>> bus-error
>> can1 200000C8 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
>> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
>> bus-off
>> bus-error
>> ...
FYI, the option "-td" nicely shows the timing. I assume that the
"warning->passive->bus-off" sequence repeats because automatic bus-off
is active via ip option "restart-ms xx ms". What I'm missing is the
message "restarted-after-bus-off". Maybe it's not listed here...
>> can1 2000008C [8] 00 04 90 00 00 00 00 00 ERRORFRAME
>> controller-problem{rx-error-warning}
>> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
>> bus-error
>> can1 2000008C [8] 00 10 90 00 00 00 00 00 ERRORFRAME
>> controller-problem{rx-error-passive}
>> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
>> bus-error
>> can1 200000C8 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
>> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
>> bus-off
>> bus-error
>>
>> t1: short circuit removed
>> can1 123 [2] 11 22
>> can1 20000100 [8] 00 00 00 00 00 00 00 00 ERRORFRAME
>> restarted-after-bus-of
>>
>> The echo coming before the restart looks weird? No?
Yes. It should come later.
>> Shouldn't we drop the frame once BUF-OFF is reached?
No.
> No, I don't think so. But wait for Wolfgang, here's more into error
> handling then me.
Looking to your code it's not clear to me how you do recovery from
bus-off (called "restart"). This is usually done via
do_set_mode(CAN_MODE_START), either manually (restart-ms==0) or
automatically (restart-ms>0). Could you please explain how the device
does bus-off recovery? Does the hardware do it automatically? This
should be suppressed if if restart-ms==0 (as Marc already pointed out).
Wolfgang.
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH] can: kvaser_usb: Add support for Kvaser CAN/USB devices
2012-08-05 20:43 ` Wolfgang Grandegger
@ 2012-08-06 5:27 ` Olivier Sobrie
0 siblings, 0 replies; 43+ messages in thread
From: Olivier Sobrie @ 2012-08-06 5:27 UTC (permalink / raw)
To: Wolfgang Grandegger; +Cc: Marc Kleine-Budde, linux-can, netdev
On Sun, Aug 05, 2012 at 10:43:55PM +0200, Wolfgang Grandegger wrote:
> On 08/02/2012 01:56 PM, Marc Kleine-Budde wrote:
> > On 08/02/2012 12:53 PM, Olivier Sobrie wrote:
> ...
> >> I actually implemeted it as you said and here is what I observed in
> >> candump output with restart_ms set to 100 ms:
> >>
> >> t0: Short circuit between CAN-H and CAN-L + cansend can1 123#1122
> >> can1 2000008C [8] 00 04 90 00 00 00 00 00 ERRORFRAME
> >> controller-problem{rx-error-warning}
> >> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> >> bus-error
> >> can1 2000008C [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> >> controller-problem{rx-error-passive}
> >> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> >> bus-error
> >> can1 200000C8 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
> >> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> >> bus-off
> >> bus-error
> >> ...
>
> FYI, the option "-td" nicely shows the timing. I assume that the
> "warning->passive->bus-off" sequence repeats because automatic bus-off
> is active via ip option "restart-ms xx ms". What I'm missing is the
> message "restarted-after-bus-off". Maybe it's not listed here...
Indeed it's missing. Please look at the patch I just sent if it is now
correct or not.
>
> >> can1 2000008C [8] 00 04 90 00 00 00 00 00 ERRORFRAME
> >> controller-problem{rx-error-warning}
> >> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> >> bus-error
> >> can1 2000008C [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> >> controller-problem{rx-error-passive}
> >> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> >> bus-error
> >> can1 200000C8 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
> >> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> >> bus-off
> >> bus-error
> >>
> >> t1: short circuit removed
> >> can1 123 [2] 11 22
> >> can1 20000100 [8] 00 00 00 00 00 00 00 00 ERRORFRAME
> >> restarted-after-bus-of
> >>
> >> The echo coming before the restart looks weird? No?
>
> Yes. It should come later.
Ok I've to change this.
>
> >> Shouldn't we drop the frame once BUF-OFF is reached?
>
> No.
Ok.
>
> > No, I don't think so. But wait for Wolfgang, here's more into error
> > handling then me.
>
> Looking to your code it's not clear to me how you do recovery from
> bus-off (called "restart"). This is usually done via
> do_set_mode(CAN_MODE_START), either manually (restart-ms==0) or
> automatically (restart-ms>0). Could you please explain how the device
> does bus-off recovery? Does the hardware do it automatically? This
> should be suppressed if if restart-ms==0 (as Marc already pointed out).
The hardware automatically recovers from BUS-OFF if it isn't stopped.
In the second version of the patch I just sent, I put the device off if
restart-ms == 0.
Thanks,
--
Olivier
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH] can: kvaser_usb: Add support for Kvaser CAN/USB devices
2012-07-31 13:06 ` Olivier Sobrie
2012-07-31 13:27 ` Marc Kleine-Budde
@ 2012-07-31 13:43 ` Wolfgang Grandegger
1 sibling, 0 replies; 43+ messages in thread
From: Wolfgang Grandegger @ 2012-07-31 13:43 UTC (permalink / raw)
To: Olivier Sobrie; +Cc: Marc Kleine-Budde, linux-can, netdev
On 07/31/2012 03:06 PM, Olivier Sobrie wrote:
> On Tue, Jul 31, 2012 at 11:56:22AM +0200, Marc Kleine-Budde wrote:
...
>> Please test Bus-Off behaviour:
>> - setup working CAN network
>> - short circuit CAN-H and CAN-L wires
>> - start "candump any,0:0,#FFFFFFFF" on one shell
>> - send one can frame on the other
>>
>> then
>>
>> - remove the short circuit
>> - see if the can frame is transmitted to the other side
>> - it should show up as an echo'ed CAN frame on the sender side
>>
>> Repeat the same test with disconnecting CAN-H and CAN-L from the other
>> CAN station instead of short circuit.
>>
>> Please send the output from candump.
>
> 1) With the short circuit:
>
> I perform the test you described. It showed that the Kvaser passes from
> ERROR-WARNING to ERROR-PASSIVE and then BUS-OFF. But after going to the
> state BUS-OFF it comes back to ERROR-WARNING.
You can use the option "-e" to get a human readable output.
> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
A state change (0x10 here) should only be reported once.
> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> ...
> can1 200000C8 [8] 00 00 90 00 00 00 00 00 ERRORFRAME <-- bus off
> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> ...
> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> can1 123 [2] 11 22 <-- short circuit removed
>
> I see the echo and on the other end I see the frame coming in.
> By the way I see that the txerr and rxerr fields of the structure
> kvaser_msg_error_event stay at 0.
If possible, they should be set for any CAN error message.
>
> 2) With CAN-H and CAN-L disconnected:
>
> I never see the bus going in OFF state. It stays in PASSIVE mode.
That is the correct behavior. You should get ACK slot (and not ACK
delimiter), IIRC.
I'm offline for the rest of the week. I will have a closer look next
week (to your next version(s)).
Wolfgang.
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH] can: kvaser_usb: Add support for Kvaser CAN/USB devices
2012-07-31 9:56 ` Marc Kleine-Budde
2012-07-31 13:06 ` Olivier Sobrie
@ 2012-08-05 20:41 ` Wolfgang Grandegger
1 sibling, 0 replies; 43+ messages in thread
From: Wolfgang Grandegger @ 2012-08-05 20:41 UTC (permalink / raw)
To: Marc Kleine-Budde; +Cc: Olivier Sobrie, linux-can, netdev
On 07/31/2012 11:56 AM, Marc Kleine-Budde wrote:
> On 07/30/2012 03:33 PM, Olivier Sobrie wrote:
...
>>>> +static void kvaser_usb_rx_error(const struct kvaser_usb *dev,
>>>> + const struct kvaser_msg *msg)
>>>> +{
>>>> + struct can_frame *cf;
>>>> + struct sk_buff *skb;
>>>> + struct net_device_stats *stats;
>>>> + struct kvaser_usb_net_priv *priv;
>>>> + u8 channel, status, txerr, rxerr;
>>>> +
>>>> + if (msg->id == CMD_CAN_ERROR_EVENT) {
>>>> + channel = msg->u.error_event.channel;
>>>> + status = msg->u.error_event.status;
>>>> + txerr = msg->u.error_event.tx_errors_count;
>>>> + rxerr = msg->u.error_event.rx_errors_count;
>>>> + } else {
>>>> + channel = msg->u.chip_state_event.channel;
>>>> + status = msg->u.chip_state_event.status;
>>>> + txerr = msg->u.chip_state_event.tx_errors_count;
>>>> + rxerr = msg->u.chip_state_event.rx_errors_count;
>>>> + }
>>>> +
>>>> + if (channel >= dev->nchannels) {
>>>> + dev_err(dev->udev->dev.parent,
>>>> + "Invalid channel number (%d)\n", channel);
>>>> + return;
>>>> + }
>>>> +
>>>> + priv = dev->nets[channel];
>>>> + stats = &priv->netdev->stats;
>>>> +
>>>> + skb = alloc_can_err_skb(priv->netdev, &cf);
>>>> + if (!skb) {
>>>> + stats->rx_dropped++;
>>>> + return;
>>>> + }
>>>> +
>>>> + if ((status & M16C_STATE_BUS_OFF) ||
>>>> + (status & M16C_STATE_BUS_RESET)) {
>>>> + priv->can.state = CAN_STATE_BUS_OFF;
>>>> + cf->can_id |= CAN_ERR_BUSOFF;
>>>> + can_bus_off(priv->netdev);
>
> you should increment priv->can.can_stats.bus_off
No, already done in can_bus_off():
http://lxr.free-electrons.com/source/drivers/net/can/dev.c#L439
Wolfgang.
^ permalink raw reply [flat|nested] 43+ messages in thread
* [PATCH v2] can: kvaser_usb: Add support for Kvaser CAN/USB devices
2012-07-30 5:32 [PATCH] can: kvaser_usb: Add support for Kvaser CAN/USB devices Olivier Sobrie
2012-07-30 11:11 ` Marc Kleine-Budde
@ 2012-08-06 5:21 ` Olivier Sobrie
2012-08-06 8:10 ` Oliver Neukum
2012-08-07 6:26 ` Wolfgang Grandegger
2012-08-13 13:51 ` [PATCH v3] " Olivier Sobrie
` (3 subsequent siblings)
5 siblings, 2 replies; 43+ messages in thread
From: Olivier Sobrie @ 2012-08-06 5:21 UTC (permalink / raw)
To: Wolfgang Grandegger, Marc Kleine-Budde, linux-can, linux-usb
Cc: netdev, Olivier Sobrie
This driver provides support for several Kvaser CAN/USB devices.
Such kind of devices supports up to three can network interfaces.
It has been tested with a Kvaser USB Leaf Light (one network interface)
connected to a pch_can interface.
The firmware version of the Kvaser device was 2.5.205.
List of Kvaser devices supported by the driver:
- Kvaser Leaf prototype (P010v2 and v3)
- Kvaser Leaf Light (P010v3)
- 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, Prototype
- 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 OEM Mercury
- Kvaser OEM Leaf
- Kvaser USBcan R
Signed-off-by: Olivier Sobrie <olivier@sobrie.be>
---
Changes since v1:
- added copyrights
- kvaser_usb.h merged into kvaser.c
- added kvaser_usb_get_endpoints to find eindpoints instead of
hardcoding their address
- some cleanup and comestic changes
- fixed issues with errors handling
- fixed restart-ms == 0 case
- removed do_get_berr_counter method since the hardware doens't return
good values for txerr and rxerr.
If someone in the linux-usb mailing can review it, it would be nice.
Concerning the errors, it behaves like that now:
1) Short-circuit CAN-H and CAN-L and restart-ms = 0
t0: short-circuit + 'cansend can1 123#112233'
can1 2000008C [8] 00 0C 90 00 00 00 00 00 ERRORFRAME
controller-problem{rx-error-warning,tx-error-warning}
protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
bus-error
can1 2000008C [8] 00 30 90 00 00 00 00 00 ERRORFRAME
controller-problem{rx-error-passive,tx-error-passive}
protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
bus-error
can1 200000C8 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
bus-off
bus-error
t1: remove short-circuit + 'ip link set can1 type can restart'
can1 20000100 [8] 00 00 00 00 00 00 00 00 ERRORFRAME
restarted-after-bus-off
can1 20000004 [8] 00 0C 00 00 00 00 00 00 ERRORFRAME
controller-problem{rx-error-warning,tx-error-warning}
2) Short-circuit CAN-H and CAN-L and restart-ms = 100
t0: short-circuit + cansend can1 123#112233
can1 2000008C [8] 00 0C 90 00 00 00 00 00 ERRORFRAME
controller-problem{rx-error-warning,tx-error-warning}
protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
bus-error
can1 2000008C [8] 00 30 90 00 00 00 00 00 ERRORFRAME
controller-problem{rx-error-passive,tx-error-passive}
protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
bus-error
can1 200000C8 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
bus-off
bus-error
can1 2000018C [8] 00 0C 90 00 00 00 00 00 ERRORFRAME
controller-problem{rx-error-warning,tx-error-warning}
protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
bus-error
restarted-after-bus-off
can1 2000008C [8] 00 0C 90 00 00 00 00 00 ERRORFRAME
controller-problem{rx-error-warning,tx-error-warning}
protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
bus-error
can1 2000008C [8] 00 30 90 00 00 00 00 00 ERRORFRAME
controller-problem{rx-error-passive,tx-error-passive}
protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
bus-error
can1 200000C8 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
bus-off
bus-error
...
can1 2000018C [8] 00 0C 90 00 00 00 00 00 ERRORFRAME
controller-problem{rx-error-warning,tx-error-warning}
protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
bus-error
restarted-after-bus-off
can1 2000008C [8] 00 0C 90 00 00 00 00 00 ERRORFRAME
controller-problem{rx-error-warning,tx-error-warning}
protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
bus-error
can1 2000008C [8] 00 30 90 00 00 00 00 00 ERRORFRAME
controller-problem{rx-error-passive,tx-error-passive}
protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
bus-error
t1: remove short-circuit
can1 123 [3] 11 22 33
can1 20000008 [8] 00 00 40 00 00 00 00 00 ERRORFRAME
protocol-violation{{back-to-error-active}{}}
3) CAN-H and CAN-L disconnected
t0: CAN-H and CAN-L disconnected + cansend can1 123#112233
can1 20000004 [8] 00 30 00 00 00 00 00 00 ERRORFRAME
controller-problem{rx-error-passive,tx-error-passive}
can1 2000008C [8] 00 30 80 1B 00 00 00 00 ERRORFRAME
controller-problem{rx-error-passive,tx-error-passive}
protocol-violation{{error-on-tx}{acknowledge-delimiter}}
bus-error
t1: CAN-H and CAN-L reconnected
can1 123 [3] 11 22 33
can1 20000004 [8] 00 30 00 00 00 00 00 00 ERRORFRAME
controller-problem{rx-error-passive,tx-error-passive}
Let me know if it is correct.
Thanks,
Olivier
drivers/net/can/usb/Kconfig | 33 +
drivers/net/can/usb/Makefile | 1 +
drivers/net/can/usb/kvaser_usb.c | 1437 ++++++++++++++++++++++++++++++++++++++
3 files changed, 1471 insertions(+)
create mode 100644 drivers/net/can/usb/kvaser_usb.c
diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig
index 0a68768..578955f 100644
--- a/drivers/net/can/usb/Kconfig
+++ b/drivers/net/can/usb/Kconfig
@@ -13,6 +13,39 @@ config CAN_ESD_USB2
This driver supports the CAN-USB/2 interface
from esd electronic system design gmbh (http://www.esd.eu).
+config CAN_KVASER_USB
+ tristate "Kvaser CAN/USB interface"
+ ---help---
+ This driver adds support for Kvaser CAN/USB devices like Kvaser
+ Leaf Light.
+
+ The driver gives support for the following devices:
+ - Kvaser Leaf prototype (P010v2 and v3)
+ - Kvaser Leaf Light (P010v3)
+ - 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, Prototype
+ - 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 OEM Mercury
+ - Kvaser OEM Leaf
+ - Kvaser USBcan R
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called kvaser_usb.
+
config CAN_PEAK_USB
tristate "PEAK PCAN-USB/USB Pro interfaces"
---help---
diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile
index da6d1d3..80a2ee4 100644
--- a/drivers/net/can/usb/Makefile
+++ b/drivers/net/can/usb/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o
obj-$(CONFIG_CAN_ESD_USB2) += esd_usb2.o
+obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o
obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/
ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c
new file mode 100644
index 0000000..eea0655
--- /dev/null
+++ b/drivers/net/can/usb/kvaser_usb.c
@@ -0,0 +1,1437 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * Parts of this driver are based on the following:
+ * - Kvaser linux leaf driver (version 4.78)
+ * - CAN driver for esd CAN-USB/2
+ *
+ * Copyright (C) 2002-2006 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>
+ */
+
+#include <linux/init.h>
+#include <linux/completion.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>
+
+#define MAX_TX_URBS 16
+#define MAX_RX_URBS 4
+#define START_TIMEOUT 1000
+#define STOP_TIMEOUT 1000
+#define USB_SEND_TIMEOUT 1000
+#define USB_RECV_TIMEOUT 1000
+#define RX_BUFFER_SIZE 3072
+#define CAN_USB_CLOCK 8000000
+#define MAX_NET_DEVICES 3
+
+/* Kvaser USB devices */
+#define KVASER_VENDOR_ID 0x0bfd
+#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
+
+/* USB devices features */
+#define KVASER_HAS_SILENT_MODE BIT(0)
+
+/* Message header size */
+#define MSG_HEADER_LEN 2
+
+/* 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 */
+#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)
+
+/* Can msg 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_GET_CTRL_MODE 22
+#define CMD_GET_CTRL_MODE_REPLY 23
+#define CMD_RESET_CHIP 24
+#define CMD_RESET_CARD 25
+#define CMD_START_CHIP 26
+#define CMD_START_CHIP_REPLY 27
+#define CMD_STOP_CHIP 28
+#define CMD_STOP_CHIP_REPLY 29
+#define CMD_GET_CARD_INFO2 32
+#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_RESET_ERROR_COUNTER 49
+#define CMD_TX_ACKNOWLEDGE 50
+#define CMD_CAN_ERROR_EVENT 51
+#define CMD_USB_THROTTLE 77
+
+/* 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)
+
+/* bittiming parameters */
+#define KVASER_USB_TSEG1_MIN 1
+#define KVASER_USB_TSEG1_MAX 16
+#define KVASER_USB_TSEG2_MIN 1
+#define KVASER_USB_TSEG2_MAX 8
+#define KVASER_USB_SJW_MAX 4
+#define KVASER_USB_BRP_MIN 1
+#define KVASER_USB_BRP_MAX 64
+#define KVASER_USB_BRP_INC 1
+
+/* 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
+
+struct kvaser_msg_simple {
+ u8 tid;
+ u8 channel;
+} __packed;
+
+struct kvaser_msg_cardinfo {
+ u8 tid;
+ u8 nchannels;
+ __le32 serial_number;
+ __le32 padding;
+ __le32 clock_resolution;
+ __le32 mfgdate;
+ u8 ean[8];
+ u8 hw_revision;
+ u8 usb_hs_mode;
+ __le16 padding2;
+} __packed;
+
+struct kvaser_msg_cardinfo2 {
+ u8 tid;
+ u8 channel;
+ u8 pcb_id[24];
+ __le32 oem_unlock_code;
+} __packed;
+
+struct kvaser_msg_softinfo {
+ u8 tid;
+ u8 channel;
+ __le32 sw_options;
+ __le32 fw_version;
+ __le16 max_outstanding_tx;
+ __le16 padding[9];
+} __packed;
+
+struct kvaser_msg_busparams {
+ u8 tid;
+ u8 channel;
+ __le32 bitrate;
+ u8 tseg1;
+ u8 tseg2;
+ u8 sjw;
+ u8 no_samp;
+} __packed;
+
+struct kvaser_msg_tx_can {
+ u8 channel;
+ u8 tid;
+ u8 msg[14];
+ u8 padding;
+ u8 flags;
+} __packed;
+
+struct kvaser_msg_rx_can {
+ u8 channel;
+ u8 flag;
+ __le16 time[3];
+ u8 msg[14];
+} __packed;
+
+struct kvaser_msg_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 kvaser_msg_tx_acknowledge {
+ u8 channel;
+ u8 tid;
+ __le16 time[3];
+ u8 flags;
+ u8 time_offset;
+} __packed;
+
+struct kvaser_msg_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 kvaser_msg_ctrl_mode {
+ u8 tid;
+ u8 channel;
+ u8 ctrl_mode;
+ u8 padding[3];
+} __packed;
+
+struct kvaser_msg_flush_queue {
+ u8 tid;
+ u8 channel;
+ u8 flags;
+ u8 padding[3];
+} __packed;
+
+struct kvaser_msg {
+ u8 len;
+ u8 id;
+ union {
+ struct kvaser_msg_simple simple;
+ struct kvaser_msg_cardinfo cardinfo;
+ struct kvaser_msg_cardinfo2 cardinfo2;
+ struct kvaser_msg_softinfo softinfo;
+ struct kvaser_msg_busparams busparams;
+ struct kvaser_msg_tx_can tx_can;
+ struct kvaser_msg_rx_can rx_can;
+ struct kvaser_msg_chip_state_event chip_state_event;
+ struct kvaser_msg_tx_acknowledge tx_acknowledge;
+ struct kvaser_msg_error_event error_event;
+ struct kvaser_msg_ctrl_mode ctrl_mode;
+ struct kvaser_msg_flush_queue flush_queue;
+ } u;
+} __packed;
+
+struct kvaser_usb_tx_urb_context {
+ struct kvaser_usb_net_priv *priv;
+ u32 echo_index;
+ int dlc;
+};
+
+struct kvaser_usb {
+ struct usb_device *udev;
+ struct kvaser_usb_net_priv *nets[MAX_NET_DEVICES];
+
+ struct usb_endpoint_descriptor *bulk_in, *bulk_out;
+ struct usb_anchor rx_submitted;
+
+ u32 fw_version;
+ unsigned int nchannels;
+
+ bool rxinitdone;
+};
+
+struct kvaser_usb_net_priv {
+ struct can_priv can;
+
+ atomic_t active_tx_urbs;
+ struct usb_anchor tx_submitted;
+ struct kvaser_usb_tx_urb_context tx_contexts[MAX_TX_URBS];
+
+ struct completion start_comp, stop_comp;
+
+ struct kvaser_usb *dev;
+ struct net_device *netdev;
+ int channel;
+
+ struct can_frame cf_err_old;
+};
+
+static struct usb_device_id kvaser_usb_table[] = {
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_DEVEL_PRODUCT_ID) },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_PRODUCT_ID) },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_PRODUCT_ID),
+ .driver_info = KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_PRODUCT_ID),
+ .driver_info = KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_SWC_PRODUCT_ID),
+ .driver_info = KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LIN_PRODUCT_ID),
+ .driver_info = KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_LS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_SWC_PRODUCT_ID),
+ .driver_info = KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_DEVEL_PRODUCT_ID),
+ .driver_info = KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSHS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_UPRO_HSHS_PRODUCT_ID) },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_GI_PRODUCT_ID) },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_OBDII_PRODUCT_ID),
+ .driver_info = KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSLS_PRODUCT_ID) },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_CH_PRODUCT_ID) },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_BLACKBIRD_SPRO_PRODUCT_ID) },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_MERCURY_PRODUCT_ID) },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_LEAF_PRODUCT_ID) },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_CAN_R_PRODUCT_ID) },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, kvaser_usb_table);
+
+static inline int kvaser_usb_send_msg(const struct kvaser_usb *dev,
+ struct kvaser_msg *msg)
+{
+ int actual_len;
+
+ return usb_bulk_msg(dev->udev,
+ usb_sndbulkpipe(dev->udev,
+ dev->bulk_out->bEndpointAddress),
+ msg, msg->len, &actual_len,
+ USB_SEND_TIMEOUT);
+}
+
+static int kvaser_usb_wait_msg(const struct kvaser_usb *dev, u8 id,
+ struct kvaser_msg *msg)
+{
+ struct kvaser_msg *tmp;
+ void *buf;
+ int actual_len;
+ int err;
+ int pos = 0;
+
+ buf = kzalloc(RX_BUFFER_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ err = usb_bulk_msg(dev->udev,
+ usb_rcvbulkpipe(dev->udev,
+ dev->bulk_in->bEndpointAddress),
+ buf, RX_BUFFER_SIZE, &actual_len,
+ USB_RECV_TIMEOUT);
+ if (err < 0) {
+ kfree(buf);
+ return err;
+ }
+
+ while (pos <= actual_len - MSG_HEADER_LEN) {
+ tmp = buf + pos;
+
+ if (!tmp->len)
+ break;
+
+ if (pos + tmp->len > actual_len) {
+ dev_err(dev->udev->dev.parent, "Format error\n");
+ break;
+ }
+
+ if (tmp->id == id) {
+ memcpy(msg, tmp, tmp->len);
+ kfree(buf);
+ return 0;
+ }
+
+ pos += tmp->len;
+ }
+
+ kfree(buf);
+
+ return -EINVAL;
+}
+
+static int kvaser_usb_send_simple_msg(const struct kvaser_usb *dev,
+ u8 msg_id, int channel)
+{
+ struct kvaser_msg msg = {
+ .len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple),
+ .id = msg_id,
+ .u.simple.channel = channel,
+ .u.simple.tid = 0xff,
+ };
+
+ return kvaser_usb_send_msg(dev, &msg);
+}
+
+static int kvaser_usb_get_software_info(struct kvaser_usb *dev)
+{
+ struct kvaser_msg msg;
+ int err;
+
+ err = kvaser_usb_send_simple_msg(dev, CMD_GET_SOFTWARE_INFO, 0);
+ if (err)
+ return err;
+
+ err = kvaser_usb_wait_msg(dev, CMD_GET_SOFTWARE_INFO_REPLY, &msg);
+ if (err)
+ return err;
+
+ dev->fw_version = le32_to_cpu(msg.u.softinfo.fw_version);
+
+ return 0;
+}
+
+static int kvaser_usb_get_card_info(struct kvaser_usb *dev)
+{
+ struct kvaser_msg msg;
+ int err;
+
+ err = kvaser_usb_send_simple_msg(dev, CMD_GET_CARD_INFO, 0);
+ if (err)
+ return err;
+
+ err = kvaser_usb_wait_msg(dev, CMD_GET_CARD_INFO_REPLY, &msg);
+ if (err)
+ return err;
+
+ dev->nchannels = msg.u.cardinfo.nchannels;
+
+ return 0;
+}
+
+static void kvaser_usb_tx_acknowledge(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ struct net_device_stats *stats;
+ struct kvaser_usb_tx_urb_context *context;
+ struct kvaser_usb_net_priv *priv;
+ u8 channel = msg->u.tx_acknowledge.channel;
+ u8 tid = msg->u.tx_acknowledge.tid;
+
+ if (channel >= dev->nchannels) {
+ dev_err(dev->udev->dev.parent,
+ "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 % MAX_TX_URBS];
+
+ /*
+ * It looks like the firmware never sets the flags field of the
+ * tx_acknowledge frame and never reports a transmit failure.
+ * If the can message can't be transmited (e.g. incompatible
+ * bitrates), a frame CMD_CAN_ERROR_EVENT is sent (with a null
+ * tid) and the firmware tries to transmit again the packet until
+ * it succeeds. Once the packet is successfully transmitted, then
+ * the tx_acknowledge frame is sent.
+ */
+
+ stats->tx_packets++;
+ stats->tx_bytes += context->dlc;
+ can_get_echo_skb(priv->netdev, context->echo_index);
+
+ context->echo_index = MAX_TX_URBS;
+ atomic_dec(&priv->active_tx_urbs);
+
+ netif_wake_queue(priv->netdev);
+}
+
+static void kvaser_usb_simple_msg_callback(struct urb *urb)
+{
+ struct net_device *netdev = urb->context;
+
+ if (urb->status)
+ netdev_warn(netdev, "urb status received: %d\n",
+ urb->status);
+}
+
+static int kvaser_usb_simple_msg_async(struct kvaser_usb_net_priv *priv,
+ u8 msg_id)
+{
+ struct kvaser_usb *dev = priv->dev;
+ struct net_device *netdev = priv->netdev;
+ struct kvaser_msg *msg;
+ struct urb *urb;
+ void *buf;
+ int err;
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb) {
+ netdev_err(netdev, "No memory left for URBs\n");
+ return -ENOMEM;
+ }
+
+ buf = usb_alloc_coherent(dev->udev, sizeof(struct kvaser_msg),
+ GFP_ATOMIC, &urb->transfer_dma);
+ if (!buf) {
+ netdev_err(netdev, "No memory left for USB buffer\n");
+ return -ENOMEM;
+ }
+
+ msg = (struct kvaser_msg *)buf;
+ msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple);
+ msg->id = msg_id;
+ msg->u.simple.channel = priv->channel;
+
+ usb_fill_bulk_urb(urb, dev->udev,
+ usb_sndbulkpipe(dev->udev,
+ dev->bulk_out->bEndpointAddress),
+ buf, msg->len,
+ kvaser_usb_simple_msg_callback, priv);
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ 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);
+ return err;
+ }
+
+ return 0;
+}
+
+static void kvaser_usb_unlink_tx_urbs(struct kvaser_usb_net_priv *priv)
+{
+ int 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;
+}
+
+static void kvaser_usb_rx_error(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ struct net_device_stats *stats;
+ struct kvaser_usb_net_priv *priv;
+ unsigned int new_state;
+ u8 channel, status;
+
+ if (msg->id == CMD_CAN_ERROR_EVENT) {
+ channel = msg->u.error_event.channel;
+ status = msg->u.error_event.status;
+ } else {
+ channel = msg->u.chip_state_event.channel;
+ status = msg->u.chip_state_event.status;
+ }
+
+ if (channel >= dev->nchannels) {
+ dev_err(dev->udev->dev.parent,
+ "Invalid channel number (%d)\n", channel);
+ return;
+ }
+
+ priv = dev->nets[channel];
+ stats = &priv->netdev->stats;
+
+ if (status & M16C_STATE_BUS_RESET) {
+ kvaser_usb_unlink_tx_urbs(priv);
+ return;
+ }
+
+ skb = alloc_can_err_skb(priv->netdev, &cf);
+ if (!skb) {
+ stats->rx_dropped++;
+ return;
+ }
+
+ if (status & M16C_STATE_BUS_OFF) {
+ cf->can_id |= CAN_ERR_BUSOFF;
+
+ if (!priv->can.restart_ms)
+ kvaser_usb_simple_msg_async(priv, CMD_STOP_CHIP);
+
+ if (!priv->can.state != CAN_ERR_BUSOFF) {
+ priv->can.can_stats.bus_off++;
+ netif_carrier_off(priv->netdev);
+ }
+
+ new_state = CAN_STATE_BUS_OFF;
+ } else if (status & M16C_STATE_BUS_PASSIVE) {
+ cf->can_id |= CAN_ERR_CRTL;
+ cf->data[1] = CAN_ERR_CRTL_TX_PASSIVE |
+ CAN_ERR_CRTL_RX_PASSIVE;
+
+ if (priv->can.state != CAN_STATE_ERROR_PASSIVE)
+ priv->can.can_stats.error_passive++;
+
+ new_state = CAN_STATE_ERROR_PASSIVE;
+ } else if (status & M16C_STATE_BUS_ERROR) {
+ cf->can_id |= CAN_ERR_CRTL;
+ cf->data[1] = CAN_ERR_CRTL_TX_WARNING |
+ CAN_ERR_CRTL_RX_WARNING;
+
+ if (priv->can.state != CAN_STATE_ERROR_WARNING)
+ priv->can.can_stats.error_warning++;
+
+ new_state = CAN_STATE_ERROR_WARNING;
+ } else {
+ cf->can_id |= CAN_ERR_PROT;
+ cf->data[2] = CAN_ERR_PROT_ACTIVE;
+
+ new_state = CAN_STATE_ERROR_ACTIVE;
+ }
+
+ if (priv->can.restart_ms &&
+ (priv->can.state >= CAN_STATE_BUS_OFF) &&
+ (new_state < CAN_STATE_BUS_OFF)) {
+ cf->can_id |= CAN_ERR_RESTARTED;
+ priv->can.can_stats.restarts++;
+ netif_carrier_on(priv->netdev);
+ }
+
+ if (msg->id == CMD_CAN_ERROR_EVENT) {
+ u8 error_factor = msg->u.error_event.error_factor;
+
+ priv->can.can_stats.bus_error++;
+ stats->rx_errors++;
+
+ cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
+
+ if (error_factor & M16C_EF_ACKE)
+ cf->data[3] |= (CAN_ERR_PROT_LOC_ACK |
+ CAN_ERR_PROT_LOC_ACK_DEL);
+ if (error_factor & M16C_EF_CRCE)
+ cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ |
+ CAN_ERR_PROT_LOC_CRC_DEL);
+ if (error_factor & M16C_EF_FORME)
+ cf->data[2] |= CAN_ERR_PROT_FORM;
+ if (error_factor & M16C_EF_STFE)
+ cf->data[2] |= CAN_ERR_PROT_STUFF;
+ if (error_factor & M16C_EF_BITE0)
+ cf->data[2] |= CAN_ERR_PROT_BIT0;
+ if (error_factor & M16C_EF_BITE1)
+ cf->data[2] |= CAN_ERR_PROT_BIT1;
+ if (error_factor & M16C_EF_TRE)
+ cf->data[2] |= CAN_ERR_PROT_TX;
+ }
+
+ priv->can.state = new_state;
+
+ if (!memcmp(cf, &priv->cf_err_old, sizeof(*cf))) {
+ kfree_skb(skb);
+ return;
+ }
+
+ netif_rx(skb);
+
+ priv->cf_err_old = *cf;
+
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+}
+
+static void kvaser_usb_rx_can_msg(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ struct kvaser_usb_net_priv *priv;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ struct net_device_stats *stats;
+ u8 channel = msg->u.rx_can.channel;
+
+ if (channel >= dev->nchannels) {
+ dev_err(dev->udev->dev.parent,
+ "Invalid channel number (%d)\n", channel);
+ return;
+ }
+
+ priv = dev->nets[channel];
+ stats = &priv->netdev->stats;
+
+ skb = alloc_can_skb(priv->netdev, &cf);
+ if (skb == NULL) {
+ stats->tx_dropped++;
+ return;
+ }
+
+ cf->can_id = ((msg->u.rx_can.msg[0] & 0x1f) << 6) |
+ (msg->u.rx_can.msg[1] & 0x3f);
+ cf->can_dlc = get_can_dlc(msg->u.rx_can.msg[5]);
+
+ if (msg->id == CMD_RX_EXT_MESSAGE) {
+ cf->can_id <<= 18;
+ cf->can_id |= ((msg->u.rx_can.msg[2] & 0x0f) << 14) |
+ ((msg->u.rx_can.msg[3] & 0xff) << 6) |
+ (msg->u.rx_can.msg[4] & 0x3f);
+ cf->can_id |= CAN_EFF_FLAG;
+ }
+
+ if (msg->u.rx_can.flag & MSG_FLAG_REMOTE_FRAME) {
+ cf->can_id |= CAN_RTR_FLAG;
+ } else if (msg->u.rx_can.flag & (MSG_FLAG_ERROR_FRAME |
+ MSG_FLAG_NERR)) {
+ cf->can_id |= CAN_ERR_FLAG;
+ cf->can_dlc = CAN_ERR_DLC;
+ cf->data[1] = CAN_ERR_CRTL_UNSPEC;
+
+ stats->rx_errors++;
+ } else if (msg->u.rx_can.flag & MSG_FLAG_OVERRUN) {
+ cf->can_id = CAN_ERR_FLAG | CAN_ERR_CRTL;
+ cf->can_dlc = CAN_ERR_DLC;
+ cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+
+ stats->rx_over_errors++;
+ stats->rx_errors++;
+ } else if (!msg->u.rx_can.flag) {
+ memcpy(cf->data, &msg->u.rx_can.msg[6], cf->can_dlc);
+ } else {
+ kfree_skb(skb);
+ return;
+ }
+
+ netif_rx(skb);
+
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+}
+
+static void kvaser_usb_start_chip_reply(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ struct kvaser_usb_net_priv *priv;
+ u8 channel = msg->u.simple.channel;
+
+ if (channel >= dev->nchannels) {
+ dev_err(dev->udev->dev.parent,
+ "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_stop_chip_reply(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ struct kvaser_usb_net_priv *priv;
+ u8 channel = msg->u.simple.channel;
+
+ if (channel >= dev->nchannels) {
+ dev_err(dev->udev->dev.parent,
+ "Invalid channel number (%d)\n", channel);
+ return;
+ }
+
+ priv = dev->nets[channel];
+
+ complete(&priv->stop_comp);
+}
+
+static void kvaser_usb_handle_message(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ switch (msg->id) {
+ case CMD_START_CHIP_REPLY:
+ kvaser_usb_start_chip_reply(dev, msg);
+ break;
+
+ case CMD_STOP_CHIP_REPLY:
+ kvaser_usb_stop_chip_reply(dev, msg);
+ break;
+
+ case CMD_RX_STD_MESSAGE:
+ case CMD_RX_EXT_MESSAGE:
+ kvaser_usb_rx_can_msg(dev, msg);
+ break;
+
+ case CMD_CHIP_STATE_EVENT:
+ case CMD_CAN_ERROR_EVENT:
+ kvaser_usb_rx_error(dev, msg);
+ break;
+
+ case CMD_TX_ACKNOWLEDGE:
+ kvaser_usb_tx_acknowledge(dev, msg);
+ break;
+
+ default:
+ dev_warn(dev->udev->dev.parent,
+ "Unhandled message (%d)\n", msg->id);
+ break;
+ }
+}
+
+static void kvaser_usb_read_bulk_callback(struct urb *urb)
+{
+ struct kvaser_usb *dev = urb->context;
+ struct kvaser_msg *msg;
+ int pos = 0;
+ int err, i;
+
+ switch (urb->status) {
+ case 0:
+ break;
+ case -ENOENT:
+ 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 - MSG_HEADER_LEN) {
+ msg = urb->transfer_buffer + pos;
+
+ if (!msg->len)
+ break;
+
+ if (pos + msg->len > urb->actual_length) {
+ dev_err(dev->udev->dev.parent, "Format error\n");
+ break;
+ }
+
+ kvaser_usb_handle_message(dev, msg);
+
+ pos += msg->len;
+ }
+
+resubmit_urb:
+ usb_fill_bulk_urb(urb, dev->udev,
+ usb_rcvbulkpipe(dev->udev,
+ dev->bulk_in->bEndpointAddress),
+ urb->transfer_buffer, 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->udev->dev.parent,
+ "Failed resubmitting read bulk urb: %d\n", err);
+ }
+
+ return;
+}
+
+static int kvaser_usb_setup_rx_urbs(struct kvaser_usb *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;
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ dev_warn(dev->udev->dev.parent,
+ "No memory left for URBs\n");
+ err = -ENOMEM;
+ break;
+ }
+
+ buf = usb_alloc_coherent(dev->udev, RX_BUFFER_SIZE,
+ GFP_KERNEL, &urb->transfer_dma);
+ if (!buf) {
+ dev_warn(dev->udev->dev.parent,
+ "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, RX_BUFFER_SIZE,
+ kvaser_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);
+ break;
+ }
+
+ usb_free_urb(urb);
+ }
+
+ if (i == 0) {
+ dev_warn(dev->udev->dev.parent,
+ "Cannot setup read URBs, error %d\n", err);
+ return err;
+ } else if (i < MAX_RX_URBS) {
+ dev_warn(dev->udev->dev.parent,
+ "RX performances may be slow\n");
+ }
+
+ dev->rxinitdone = true;
+
+ return 0;
+}
+
+static int kvaser_usb_set_opt_mode(const struct kvaser_usb_net_priv *priv)
+{
+ struct kvaser_msg msg = {
+ .id = CMD_SET_CTRL_MODE,
+ .len = MSG_HEADER_LEN +
+ sizeof(struct kvaser_msg_ctrl_mode),
+ .u.ctrl_mode.tid = 0xff,
+ .u.ctrl_mode.channel = priv->channel,
+ };
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
+ msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_SILENT;
+ else
+ msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_NORMAL;
+
+ return kvaser_usb_send_msg(priv->dev, &msg);
+}
+
+static int kvaser_usb_start_chip(struct kvaser_usb_net_priv *priv)
+{
+ int err;
+
+ init_completion(&priv->start_comp);
+
+ err = kvaser_usb_send_simple_msg(priv->dev, CMD_START_CHIP,
+ priv->channel);
+ if (err)
+ return err;
+
+ if (!wait_for_completion_timeout(&priv->start_comp,
+ msecs_to_jiffies(START_TIMEOUT)))
+ return -ETIMEDOUT;
+
+ 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;
+ int err;
+
+ err = open_candev(netdev);
+ if (err)
+ return err;
+
+ err = kvaser_usb_setup_rx_urbs(dev);
+ if (err)
+ return err;
+
+ err = kvaser_usb_set_opt_mode(priv);
+ if (err)
+ return err;
+
+ err = kvaser_usb_start_chip(priv);
+ if (err) {
+ netdev_warn(netdev, "Cannot start device, error %d\n", err);
+ close_candev(netdev);
+ return err;
+ }
+
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+
+ return 0;
+}
+
+static void kvaser_usb_unlink_all_urbs(struct kvaser_usb *dev)
+{
+ int i;
+
+ usb_kill_anchored_urbs(&dev->rx_submitted);
+
+ for (i = 0; i < MAX_NET_DEVICES; i++) {
+ struct kvaser_usb_net_priv *priv = dev->nets[i];
+
+ if (priv)
+ kvaser_usb_unlink_tx_urbs(priv);
+ }
+}
+
+static int kvaser_usb_stop_chip(struct kvaser_usb_net_priv *priv)
+{
+ int err;
+
+ init_completion(&priv->stop_comp);
+
+ err = kvaser_usb_send_simple_msg(priv->dev, CMD_STOP_CHIP,
+ priv->channel);
+ if (err)
+ return err;
+
+ if (!wait_for_completion_timeout(&priv->stop_comp,
+ msecs_to_jiffies(STOP_TIMEOUT)))
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int kvaser_usb_flush_queue(struct kvaser_usb_net_priv *priv)
+{
+ struct kvaser_msg msg = {
+ .id = CMD_FLUSH_QUEUE,
+ .len = MSG_HEADER_LEN +
+ sizeof(struct kvaser_msg_flush_queue),
+ .u.flush_queue.channel = priv->channel,
+ .u.flush_queue.flags = 0x00,
+ };
+
+ return kvaser_usb_send_msg(priv->dev, &msg);
+}
+
+static int kvaser_usb_close(struct net_device *netdev)
+{
+ struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
+ struct kvaser_usb *dev = priv->dev;
+ int err;
+
+ netif_stop_queue(netdev);
+
+ err = kvaser_usb_flush_queue(priv);
+ if (err)
+ netdev_warn(netdev, "Cannot flush queue, error %d\n", err);
+
+ if (kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, priv->channel))
+ netdev_warn(netdev, "Cannot reset card, error %d\n", err);
+
+ err = kvaser_usb_stop_chip(priv);
+ if (err) {
+ netdev_warn(netdev, "Cannot stop device, error %d\n", err);
+ return err;
+ }
+
+ priv->can.state = CAN_STATE_STOPPED;
+ close_candev(priv->netdev);
+
+ return 0;
+}
+
+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;
+
+ usb_free_coherent(urb->dev, urb->transfer_buffer_length,
+ 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);
+}
+
+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;
+ struct net_device_stats *stats = &netdev->stats;
+ struct can_frame *cf = (struct can_frame *)skb->data;
+ struct kvaser_usb_tx_urb_context *context = NULL;
+ struct urb *urb;
+ void *buf;
+ struct kvaser_msg *msg;
+ int i, err;
+ int ret = NETDEV_TX_OK;
+
+ if (can_dropped_invalid_skb(netdev, skb))
+ return NETDEV_TX_OK;
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb) {
+ netdev_err(netdev, "No memory left for URBs\n");
+ stats->tx_dropped++;
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+
+ buf = usb_alloc_coherent(dev->udev, sizeof(struct kvaser_msg),
+ 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 kvaser_msg *)buf;
+ msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_tx_can);
+ msg->u.tx_can.flags = 0;
+ msg->u.tx_can.channel = priv->channel;
+
+ if (cf->can_id & CAN_EFF_FLAG) {
+ msg->id = CMD_TX_EXT_MESSAGE;
+ msg->u.tx_can.msg[0] = (cf->can_id >> 24) & 0x1f;
+ msg->u.tx_can.msg[1] = (cf->can_id >> 18) & 0x3f;
+ msg->u.tx_can.msg[2] = (cf->can_id >> 14) & 0x0f;
+ msg->u.tx_can.msg[3] = (cf->can_id >> 6) & 0xff;
+ msg->u.tx_can.msg[4] = cf->can_id & 0x3f;
+ } else {
+ msg->id = CMD_TX_STD_MESSAGE;
+ msg->u.tx_can.msg[0] = (cf->can_id >> 6) & 0x1f;
+ msg->u.tx_can.msg[1] = cf->can_id & 0x3f;
+ }
+
+ msg->u.tx_can.msg[5] = cf->can_dlc;
+ memcpy(&msg->u.tx_can.msg[6], cf->data, cf->can_dlc);
+
+ if (cf->can_id & CAN_RTR_FLAG)
+ msg->u.tx_can.flags |= MSG_FLAG_REMOTE_FRAME;
+
+ for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++) {
+ if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) {
+ context = &priv->tx_contexts[i];
+ break;
+ }
+ }
+
+ if (!context) {
+ netdev_warn(netdev, "cannot find free context\n");
+ ret = NETDEV_TX_BUSY;
+ goto releasebuf;
+ }
+
+ context->priv = priv;
+ context->echo_index = i;
+ context->dlc = cf->can_dlc;
+
+ msg->u.tx_can.tid = context->echo_index;
+
+ usb_fill_bulk_urb(urb, dev->udev,
+ usb_sndbulkpipe(dev->udev,
+ dev->bulk_out->bEndpointAddress),
+ buf, msg->len,
+ kvaser_usb_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);
+
+ if (atomic_read(&priv->active_tx_urbs) >= MAX_TX_URBS)
+ netif_stop_queue(netdev);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (unlikely(err)) {
+ can_free_echo_skb(netdev, context->echo_index);
+
+ atomic_dec(&priv->active_tx_urbs);
+ 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;
+ }
+
+ netdev->trans_start = jiffies;
+
+ usb_free_urb(urb);
+
+ return NETDEV_TX_OK;
+
+releasebuf:
+ usb_free_coherent(dev->udev, sizeof(struct kvaser_msg),
+ buf, urb->transfer_dma);
+nobufmem:
+ 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,
+};
+
+static struct can_bittiming_const kvaser_usb_bittiming_const = {
+ .name = "kvaser_usb",
+ .tseg1_min = KVASER_USB_TSEG1_MIN,
+ .tseg1_max = KVASER_USB_TSEG1_MAX,
+ .tseg2_min = KVASER_USB_TSEG2_MIN,
+ .tseg2_max = KVASER_USB_TSEG2_MAX,
+ .sjw_max = KVASER_USB_SJW_MAX,
+ .brp_min = KVASER_USB_BRP_MIN,
+ .brp_max = KVASER_USB_BRP_MAX,
+ .brp_inc = KVASER_USB_BRP_INC,
+};
+
+static int kvaser_usb_set_bittiming(struct net_device *netdev)
+{
+ struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
+ struct can_bittiming *bt = &priv->can.bittiming;
+ struct kvaser_usb *dev = priv->dev;
+ struct kvaser_msg msg = {
+ .id = CMD_SET_BUS_PARAMS,
+ .len = MSG_HEADER_LEN +
+ sizeof(struct kvaser_msg_busparams),
+ .u.busparams.channel = priv->channel,
+ .u.busparams.tid = 0xff,
+ .u.busparams.bitrate = cpu_to_le32(bt->bitrate),
+ .u.busparams.sjw = bt->sjw,
+ .u.busparams.tseg1 = bt->prop_seg + bt->phase_seg1,
+ .u.busparams.tseg2 = bt->phase_seg2,
+ };
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
+ msg.u.busparams.no_samp = 3;
+ else
+ msg.u.busparams.no_samp = 1;
+
+ return kvaser_usb_send_msg(dev, &msg);
+}
+
+static int kvaser_usb_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:
+ err = kvaser_usb_simple_msg_async(priv, CMD_START_CHIP);
+ if (err)
+ return err;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int kvaser_usb_init_one(struct usb_interface *intf,
+ const struct usb_device_id *id, int channel)
+{
+ struct kvaser_usb *dev = usb_get_intfdata(intf);
+ struct net_device *netdev;
+ struct kvaser_usb_net_priv *priv;
+ int i, err;
+
+ netdev = alloc_candev(sizeof(*priv), MAX_TX_URBS);
+ if (!netdev) {
+ dev_err(&intf->dev, "Cannot alloc candev\n");
+ return -ENOMEM;
+ }
+
+ priv = netdev_priv(netdev);
+
+ init_completion(&priv->start_comp);
+ init_completion(&priv->stop_comp);
+
+ init_usb_anchor(&priv->tx_submitted);
+ atomic_set(&priv->active_tx_urbs, 0);
+
+ for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++)
+ priv->tx_contexts[i].echo_index = MAX_TX_URBS;
+
+ priv->dev = dev;
+ priv->netdev = netdev;
+ priv->channel = channel;
+
+ priv->can.state = CAN_STATE_STOPPED;
+ priv->can.clock.freq = CAN_USB_CLOCK;
+ priv->can.bittiming_const = &kvaser_usb_bittiming_const;
+ priv->can.do_set_bittiming = kvaser_usb_set_bittiming;
+ priv->can.do_set_mode = kvaser_usb_set_mode;
+ priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES;
+ if (id->driver_info & KVASER_HAS_SILENT_MODE)
+ priv->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY;
+
+ netdev->flags |= IFF_ECHO;
+
+ netdev->netdev_ops = &kvaser_usb_netdev_ops;
+
+ SET_NETDEV_DEV(netdev, &intf->dev);
+
+ err = register_candev(netdev);
+ if (err) {
+ dev_err(&intf->dev, "Failed to register can device\n");
+ free_candev(netdev);
+ return err;
+ }
+
+ dev->nets[channel] = priv;
+ netdev_dbg(netdev, "device registered\n");
+
+ return 0;
+}
+
+static void kvaser_usb_get_endpoints(const struct usb_interface *intf,
+ struct usb_endpoint_descriptor **in,
+ struct usb_endpoint_descriptor **out)
+{
+ const struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ int i;
+
+ iface_desc = &intf->altsetting[0];
+
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+
+ if (usb_endpoint_is_bulk_in(endpoint))
+ *in = endpoint;
+
+ if (usb_endpoint_is_bulk_out(endpoint))
+ *out = endpoint;
+ }
+}
+
+static int kvaser_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct kvaser_usb *dev;
+ int err = -ENOMEM;
+ int i;
+
+ dev = devm_kzalloc(&intf->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ kvaser_usb_get_endpoints(intf, &dev->bulk_in, &dev->bulk_out);
+ if (!dev->bulk_in || !dev->bulk_out) {
+ 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);
+
+ for (i = 0; i < MAX_NET_DEVICES; i++)
+ kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, i);
+
+ err = kvaser_usb_get_software_info(dev);
+ if (err) {
+ dev_err(&intf->dev,
+ "Cannot get software infos, error %d\n", err);
+ return err;
+ }
+
+ err = kvaser_usb_get_card_info(dev);
+ if (err) {
+ dev_err(&intf->dev,
+ "Cannot get card infos, error %d\n", err);
+ return err;
+ }
+
+ dev_dbg(&intf->dev, "Firmware version: %d.%d.%d\n",
+ ((dev->fw_version >> 24) & 0xff),
+ ((dev->fw_version >> 16) & 0xff),
+ (dev->fw_version & 0xffff));
+
+ for (i = 0; i < dev->nchannels; i++)
+ kvaser_usb_init_one(intf, id, i);
+
+ return 0;
+}
+
+static void kvaser_usb_disconnect(struct usb_interface *intf)
+{
+ struct kvaser_usb *dev = usb_get_intfdata(intf);
+ int i;
+
+ usb_set_intfdata(intf, NULL);
+
+ if (!dev)
+ return;
+
+ for (i = 0; i < dev->nchannels; i++) {
+ if (!dev->nets[i])
+ continue;
+
+ unregister_netdev(dev->nets[i]->netdev);
+ free_candev(dev->nets[i]->netdev);
+ }
+
+ kvaser_usb_unlink_all_urbs(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_DESCRIPTION("Can driver for Kvaser CAN/USB devices");
+MODULE_LICENSE("GPL v2");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* Re: [PATCH v2] can: kvaser_usb: Add support for Kvaser CAN/USB devices
2012-08-06 5:21 ` [PATCH v2] " Olivier Sobrie
@ 2012-08-06 8:10 ` Oliver Neukum
2012-08-08 5:28 ` Olivier Sobrie
2012-08-07 6:26 ` Wolfgang Grandegger
1 sibling, 1 reply; 43+ messages in thread
From: Oliver Neukum @ 2012-08-06 8:10 UTC (permalink / raw)
To: Olivier Sobrie
Cc: Wolfgang Grandegger, Marc Kleine-Budde, linux-can, linux-usb, netdev
On Monday 06 August 2012 07:21:29 Olivier Sobrie wrote:
> This driver provides support for several Kvaser CAN/USB devices.
> Such kind of devices supports up to three can network interfaces.
>
> It has been tested with a Kvaser USB Leaf Light (one network interface)
> connected to a pch_can interface.
> The firmware version of the Kvaser device was 2.5.205.
> +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;
> + struct net_device_stats *stats = &netdev->stats;
> + struct can_frame *cf = (struct can_frame *)skb->data;
> + struct kvaser_usb_tx_urb_context *context = NULL;
> + struct urb *urb;
> + void *buf;
> + struct kvaser_msg *msg;
> + int i, err;
> + int ret = NETDEV_TX_OK;
> +
> + if (can_dropped_invalid_skb(netdev, skb))
> + return NETDEV_TX_OK;
> +
> + urb = usb_alloc_urb(0, GFP_ATOMIC);
> + if (!urb) {
> + netdev_err(netdev, "No memory left for URBs\n");
> + stats->tx_dropped++;
> + dev_kfree_skb(skb);
> + return NETDEV_TX_OK;
> + }
> +
> + buf = usb_alloc_coherent(dev->udev, sizeof(struct kvaser_msg),
> + GFP_ATOMIC, &urb->transfer_dma);
usb_alloc_coherent() as a rule only makes sense if you reuse the buffer
and in some cases not even then. Please use a simple kmalloc()
> + if (!buf) {
> + netdev_err(netdev, "No memory left for USB buffer\n");
> + stats->tx_dropped++;
> + dev_kfree_skb(skb);
> + goto nobufmem;
> + }
> +
> + msg = (struct kvaser_msg *)buf;
> + msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_tx_can);
> + msg->u.tx_can.flags = 0;
> + msg->u.tx_can.channel = priv->channel;
> +
> + if (cf->can_id & CAN_EFF_FLAG) {
> + msg->id = CMD_TX_EXT_MESSAGE;
> + msg->u.tx_can.msg[0] = (cf->can_id >> 24) & 0x1f;
> + msg->u.tx_can.msg[1] = (cf->can_id >> 18) & 0x3f;
> + msg->u.tx_can.msg[2] = (cf->can_id >> 14) & 0x0f;
> + msg->u.tx_can.msg[3] = (cf->can_id >> 6) & 0xff;
> + msg->u.tx_can.msg[4] = cf->can_id & 0x3f;
> + } else {
> + msg->id = CMD_TX_STD_MESSAGE;
> + msg->u.tx_can.msg[0] = (cf->can_id >> 6) & 0x1f;
> + msg->u.tx_can.msg[1] = cf->can_id & 0x3f;
> + }
> +
> + msg->u.tx_can.msg[5] = cf->can_dlc;
> + memcpy(&msg->u.tx_can.msg[6], cf->data, cf->can_dlc);
> +
> + if (cf->can_id & CAN_RTR_FLAG)
> + msg->u.tx_can.flags |= MSG_FLAG_REMOTE_FRAME;
> +
> + for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++) {
> + if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) {
> + context = &priv->tx_contexts[i];
> + break;
> + }
> + }
> +
> + if (!context) {
> + netdev_warn(netdev, "cannot find free context\n");
> + ret = NETDEV_TX_BUSY;
> + goto releasebuf;
> + }
> +
> + context->priv = priv;
> + context->echo_index = i;
> + context->dlc = cf->can_dlc;
> +
> + msg->u.tx_can.tid = context->echo_index;
> +
> + usb_fill_bulk_urb(urb, dev->udev,
> + usb_sndbulkpipe(dev->udev,
> + dev->bulk_out->bEndpointAddress),
> + buf, msg->len,
> + kvaser_usb_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);
> +
> + if (atomic_read(&priv->active_tx_urbs) >= MAX_TX_URBS)
> + netif_stop_queue(netdev);
> +
> + err = usb_submit_urb(urb, GFP_ATOMIC);
> + if (unlikely(err)) {
> + can_free_echo_skb(netdev, context->echo_index);
> +
> + atomic_dec(&priv->active_tx_urbs);
> + 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;
> + }
> +
> + netdev->trans_start = jiffies;
> +
> + usb_free_urb(urb);
> +
> + return NETDEV_TX_OK;
> +
> +releasebuf:
> + usb_free_coherent(dev->udev, sizeof(struct kvaser_msg),
> + buf, urb->transfer_dma);
> +nobufmem:
> + usb_free_urb(urb);
> + return ret;
> +}
> +static int kvaser_usb_init_one(struct usb_interface *intf,
> + const struct usb_device_id *id, int channel)
> +{
> + struct kvaser_usb *dev = usb_get_intfdata(intf);
> + struct net_device *netdev;
> + struct kvaser_usb_net_priv *priv;
> + int i, err;
> +
> + netdev = alloc_candev(sizeof(*priv), MAX_TX_URBS);
> + if (!netdev) {
> + dev_err(&intf->dev, "Cannot alloc candev\n");
> + return -ENOMEM;
> + }
> +
> + priv = netdev_priv(netdev);
> +
> + init_completion(&priv->start_comp);
> + init_completion(&priv->stop_comp);
> +
> + init_usb_anchor(&priv->tx_submitted);
> + atomic_set(&priv->active_tx_urbs, 0);
> +
> + for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++)
> + priv->tx_contexts[i].echo_index = MAX_TX_URBS;
> +
> + priv->dev = dev;
> + priv->netdev = netdev;
> + priv->channel = channel;
> +
> + priv->can.state = CAN_STATE_STOPPED;
> + priv->can.clock.freq = CAN_USB_CLOCK;
> + priv->can.bittiming_const = &kvaser_usb_bittiming_const;
> + priv->can.do_set_bittiming = kvaser_usb_set_bittiming;
> + priv->can.do_set_mode = kvaser_usb_set_mode;
> + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES;
> + if (id->driver_info & KVASER_HAS_SILENT_MODE)
> + priv->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY;
> +
> + netdev->flags |= IFF_ECHO;
> +
> + netdev->netdev_ops = &kvaser_usb_netdev_ops;
> +
> + SET_NETDEV_DEV(netdev, &intf->dev);
> +
> + err = register_candev(netdev);
> + if (err) {
> + dev_err(&intf->dev, "Failed to register can device\n");
> + free_candev(netdev);
> + return err;
> + }
> +
Here the device is usable.
> + dev->nets[channel] = priv;
And only know you init this field. Looks like a race condition.
> + netdev_dbg(netdev, "device registered\n");
> +
> + return 0;
> +}
> +
> +static void kvaser_usb_disconnect(struct usb_interface *intf)
> +{
> + struct kvaser_usb *dev = usb_get_intfdata(intf);
> + int i;
> +
> + usb_set_intfdata(intf, NULL);
> +
> + if (!dev)
> + return;
> +
> + for (i = 0; i < dev->nchannels; i++) {
> + if (!dev->nets[i])
> + continue;
> +
> + unregister_netdev(dev->nets[i]->netdev);
> + free_candev(dev->nets[i]->netdev);
> + }
> +
> + kvaser_usb_unlink_all_urbs(dev);
So what happens if an URB completes between freeing the candev
and unlinking and proceeds to push data to upper layers?
Regards
Oliver
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH v2] can: kvaser_usb: Add support for Kvaser CAN/USB devices
2012-08-06 8:10 ` Oliver Neukum
@ 2012-08-08 5:28 ` Olivier Sobrie
0 siblings, 0 replies; 43+ messages in thread
From: Olivier Sobrie @ 2012-08-08 5:28 UTC (permalink / raw)
To: Oliver Neukum
Cc: Wolfgang Grandegger, Marc Kleine-Budde, linux-can, linux-usb, netdev
Hi Oliver,
On Mon, Aug 06, 2012 at 10:10:43AM +0200, Oliver Neukum wrote:
> On Monday 06 August 2012 07:21:29 Olivier Sobrie wrote:
> > This driver provides support for several Kvaser CAN/USB devices.
> > Such kind of devices supports up to three can network interfaces.
> >
> > It has been tested with a Kvaser USB Leaf Light (one network interface)
> > connected to a pch_can interface.
> > The firmware version of the Kvaser device was 2.5.205.
>
> > +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;
> > + struct net_device_stats *stats = &netdev->stats;
> > + struct can_frame *cf = (struct can_frame *)skb->data;
> > + struct kvaser_usb_tx_urb_context *context = NULL;
> > + struct urb *urb;
> > + void *buf;
> > + struct kvaser_msg *msg;
> > + int i, err;
> > + int ret = NETDEV_TX_OK;
> > +
> > + if (can_dropped_invalid_skb(netdev, skb))
> > + return NETDEV_TX_OK;
> > +
> > + urb = usb_alloc_urb(0, GFP_ATOMIC);
> > + if (!urb) {
> > + netdev_err(netdev, "No memory left for URBs\n");
> > + stats->tx_dropped++;
> > + dev_kfree_skb(skb);
> > + return NETDEV_TX_OK;
> > + }
> > +
> > + buf = usb_alloc_coherent(dev->udev, sizeof(struct kvaser_msg),
> > + GFP_ATOMIC, &urb->transfer_dma);
>
> usb_alloc_coherent() as a rule only makes sense if you reuse the buffer
> and in some cases not even then. Please use a simple kmalloc()
Ok thanks. I'll change it.
>
> > + if (!buf) {
> > + netdev_err(netdev, "No memory left for USB buffer\n");
> > + stats->tx_dropped++;
> > + dev_kfree_skb(skb);
> > + goto nobufmem;
> > + }
> > +
> > + msg = (struct kvaser_msg *)buf;
> > + msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_tx_can);
> > + msg->u.tx_can.flags = 0;
> > + msg->u.tx_can.channel = priv->channel;
> > +
> > + if (cf->can_id & CAN_EFF_FLAG) {
> > + msg->id = CMD_TX_EXT_MESSAGE;
> > + msg->u.tx_can.msg[0] = (cf->can_id >> 24) & 0x1f;
> > + msg->u.tx_can.msg[1] = (cf->can_id >> 18) & 0x3f;
> > + msg->u.tx_can.msg[2] = (cf->can_id >> 14) & 0x0f;
> > + msg->u.tx_can.msg[3] = (cf->can_id >> 6) & 0xff;
> > + msg->u.tx_can.msg[4] = cf->can_id & 0x3f;
> > + } else {
> > + msg->id = CMD_TX_STD_MESSAGE;
> > + msg->u.tx_can.msg[0] = (cf->can_id >> 6) & 0x1f;
> > + msg->u.tx_can.msg[1] = cf->can_id & 0x3f;
> > + }
> > +
> > + msg->u.tx_can.msg[5] = cf->can_dlc;
> > + memcpy(&msg->u.tx_can.msg[6], cf->data, cf->can_dlc);
> > +
> > + if (cf->can_id & CAN_RTR_FLAG)
> > + msg->u.tx_can.flags |= MSG_FLAG_REMOTE_FRAME;
> > +
> > + for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++) {
> > + if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) {
> > + context = &priv->tx_contexts[i];
> > + break;
> > + }
> > + }
> > +
> > + if (!context) {
> > + netdev_warn(netdev, "cannot find free context\n");
> > + ret = NETDEV_TX_BUSY;
> > + goto releasebuf;
> > + }
> > +
> > + context->priv = priv;
> > + context->echo_index = i;
> > + context->dlc = cf->can_dlc;
> > +
> > + msg->u.tx_can.tid = context->echo_index;
> > +
> > + usb_fill_bulk_urb(urb, dev->udev,
> > + usb_sndbulkpipe(dev->udev,
> > + dev->bulk_out->bEndpointAddress),
> > + buf, msg->len,
> > + kvaser_usb_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);
> > +
> > + if (atomic_read(&priv->active_tx_urbs) >= MAX_TX_URBS)
> > + netif_stop_queue(netdev);
> > +
> > + err = usb_submit_urb(urb, GFP_ATOMIC);
> > + if (unlikely(err)) {
> > + can_free_echo_skb(netdev, context->echo_index);
> > +
> > + atomic_dec(&priv->active_tx_urbs);
> > + 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;
> > + }
> > +
> > + netdev->trans_start = jiffies;
> > +
> > + usb_free_urb(urb);
> > +
> > + return NETDEV_TX_OK;
> > +
> > +releasebuf:
> > + usb_free_coherent(dev->udev, sizeof(struct kvaser_msg),
> > + buf, urb->transfer_dma);
> > +nobufmem:
> > + usb_free_urb(urb);
> > + return ret;
> > +}
>
> > +static int kvaser_usb_init_one(struct usb_interface *intf,
> > + const struct usb_device_id *id, int channel)
> > +{
> > + struct kvaser_usb *dev = usb_get_intfdata(intf);
> > + struct net_device *netdev;
> > + struct kvaser_usb_net_priv *priv;
> > + int i, err;
> > +
> > + netdev = alloc_candev(sizeof(*priv), MAX_TX_URBS);
> > + if (!netdev) {
> > + dev_err(&intf->dev, "Cannot alloc candev\n");
> > + return -ENOMEM;
> > + }
> > +
> > + priv = netdev_priv(netdev);
> > +
> > + init_completion(&priv->start_comp);
> > + init_completion(&priv->stop_comp);
> > +
> > + init_usb_anchor(&priv->tx_submitted);
> > + atomic_set(&priv->active_tx_urbs, 0);
> > +
> > + for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++)
> > + priv->tx_contexts[i].echo_index = MAX_TX_URBS;
> > +
> > + priv->dev = dev;
> > + priv->netdev = netdev;
> > + priv->channel = channel;
> > +
> > + priv->can.state = CAN_STATE_STOPPED;
> > + priv->can.clock.freq = CAN_USB_CLOCK;
> > + priv->can.bittiming_const = &kvaser_usb_bittiming_const;
> > + priv->can.do_set_bittiming = kvaser_usb_set_bittiming;
> > + priv->can.do_set_mode = kvaser_usb_set_mode;
> > + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES;
> > + if (id->driver_info & KVASER_HAS_SILENT_MODE)
> > + priv->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY;
> > +
> > + netdev->flags |= IFF_ECHO;
> > +
> > + netdev->netdev_ops = &kvaser_usb_netdev_ops;
> > +
> > + SET_NETDEV_DEV(netdev, &intf->dev);
> > +
> > + err = register_candev(netdev);
> > + if (err) {
> > + dev_err(&intf->dev, "Failed to register can device\n");
> > + free_candev(netdev);
> > + return err;
> > + }
> > +
>
> Here the device is usable.
>
> > + dev->nets[channel] = priv;
>
> And only know you init this field. Looks like a race condition.
Argh... Indeed.
Thanks
>
> > + netdev_dbg(netdev, "device registered\n");
> > +
> > + return 0;
> > +}
> > +
>
> > +static void kvaser_usb_disconnect(struct usb_interface *intf)
> > +{
> > + struct kvaser_usb *dev = usb_get_intfdata(intf);
> > + int i;
> > +
> > + usb_set_intfdata(intf, NULL);
> > +
> > + if (!dev)
> > + return;
> > +
> > + for (i = 0; i < dev->nchannels; i++) {
> > + if (!dev->nets[i])
> > + continue;
> > +
> > + unregister_netdev(dev->nets[i]->netdev);
> > + free_candev(dev->nets[i]->netdev);
> > + }
> > +
> > + kvaser_usb_unlink_all_urbs(dev);
>
> So what happens if an URB completes between freeing the candev
> and unlinking and proceeds to push data to upper layers?
In this case I should avoid using netdev context, i.e. pointer to "struct
kvaser_usb_net_priv" in urb callbacks.
Is that what you mean?
I see it is well done in kvaser_usb_read_bulk_callback()
but not in kvaser_usb_write_bulk_callback().
I'll better protect the callbacks and move the free_candev after the
unlink.
Thank you,
--
Olivier
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH v2] can: kvaser_usb: Add support for Kvaser CAN/USB devices
2012-08-06 5:21 ` [PATCH v2] " Olivier Sobrie
2012-08-06 8:10 ` Oliver Neukum
@ 2012-08-07 6:26 ` Wolfgang Grandegger
2012-08-08 6:14 ` Olivier Sobrie
1 sibling, 1 reply; 43+ messages in thread
From: Wolfgang Grandegger @ 2012-08-07 6:26 UTC (permalink / raw)
To: Olivier Sobrie; +Cc: Marc Kleine-Budde, linux-can, linux-usb, netdev
On 08/06/2012 07:21 AM, Olivier Sobrie wrote:
> This driver provides support for several Kvaser CAN/USB devices.
> Such kind of devices supports up to three can network interfaces.
s/can/CAN/
> It has been tested with a Kvaser USB Leaf Light (one network interface)
> connected to a pch_can interface.
> The firmware version of the Kvaser device was 2.5.205.
>
> List of Kvaser devices supported by the driver:
> - Kvaser Leaf prototype (P010v2 and v3)
> - Kvaser Leaf Light (P010v3)
> - 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, Prototype
> - 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 OEM Mercury
> - Kvaser OEM Leaf
> - Kvaser USBcan R
Impressive list! What CAN controllers are used inside the devices? SJA1000?
> Signed-off-by: Olivier Sobrie <olivier@sobrie.be>
> ---
> Changes since v1:
> - added copyrights
> - kvaser_usb.h merged into kvaser.c
> - added kvaser_usb_get_endpoints to find eindpoints instead of
> hardcoding their address
> - some cleanup and comestic changes
> - fixed issues with errors handling
> - fixed restart-ms == 0 case
> - removed do_get_berr_counter method since the hardware doens't return
> good values for txerr and rxerr.
>
> If someone in the linux-usb mailing can review it, it would be nice.
>
> Concerning the errors, it behaves like that now:
>
> 1) Short-circuit CAN-H and CAN-L and restart-ms = 0
>
> t0: short-circuit + 'cansend can1 123#112233'
>
> can1 2000008C [8] 00 0C 90 00 00 00 00 00 ERRORFRAME
> controller-problem{rx-error-warning,tx-error-warning}
> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> bus-error
> can1 2000008C [8] 00 30 90 00 00 00 00 00 ERRORFRAME
> controller-problem{rx-error-passive,tx-error-passive}
> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> bus-error
> can1 200000C8 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> bus-off
> bus-error
>
> t1: remove short-circuit + 'ip link set can1 type can restart'
>
> can1 20000100 [8] 00 00 00 00 00 00 00 00 ERRORFRAME
> restarted-after-bus-off
> can1 20000004 [8] 00 0C 00 00 00 00 00 00 ERRORFRAME
> controller-problem{rx-error-warning,tx-error-warning}
Why do we get the last error message? Maybe the firmware does it that
way (going down passive->warning->active).
> 2) Short-circuit CAN-H and CAN-L and restart-ms = 100
>
> t0: short-circuit + cansend can1 123#112233
>
> can1 2000008C [8] 00 0C 90 00 00 00 00 00 ERRORFRAME
> controller-problem{rx-error-warning,tx-error-warning}
> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> bus-error
> can1 2000008C [8] 00 30 90 00 00 00 00 00 ERRORFRAME
> controller-problem{rx-error-passive,tx-error-passive}
> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> bus-error
> can1 200000C8 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> bus-off
> bus-error
> can1 2000018C [8] 00 0C 90 00 00 00 00 00 ERRORFRAME
> controller-problem{rx-error-warning,tx-error-warning}
> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> bus-error
> restarted-after-bus-off
> can1 2000008C [8] 00 0C 90 00 00 00 00 00 ERRORFRAME
> controller-problem{rx-error-warning,tx-error-warning}
> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> bus-error
> can1 2000008C [8] 00 30 90 00 00 00 00 00 ERRORFRAME
> controller-problem{rx-error-passive,tx-error-passive}
> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> bus-error
> can1 200000C8 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> bus-off
> bus-error
> ...
>
> can1 2000018C [8] 00 0C 90 00 00 00 00 00 ERRORFRAME
> controller-problem{rx-error-warning,tx-error-warning}
> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> bus-error
> restarted-after-bus-off
> can1 2000008C [8] 00 0C 90 00 00 00 00 00 ERRORFRAME
> controller-problem{rx-error-warning,tx-error-warning}
> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> bus-error
> can1 2000008C [8] 00 30 90 00 00 00 00 00 ERRORFRAME
> controller-problem{rx-error-passive,tx-error-passive}
> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> bus-error
>
> t1: remove short-circuit
>
> can1 123 [3] 11 22 33
> can1 20000008 [8] 00 00 40 00 00 00 00 00 ERRORFRAME
> protocol-violation{{back-to-error-active}{}}
The order is still inverted but likely the firmware is doing it that way.
> 3) CAN-H and CAN-L disconnected
>
> t0: CAN-H and CAN-L disconnected + cansend can1 123#112233
>
> can1 20000004 [8] 00 30 00 00 00 00 00 00 ERRORFRAME
> controller-problem{rx-error-passive,tx-error-passive}
> can1 2000008C [8] 00 30 80 1B 00 00 00 00 ERRORFRAME
> controller-problem{rx-error-passive,tx-error-passive}
> protocol-violation{{error-on-tx}{acknowledge-delimiter}}
> bus-error
>
> t1: CAN-H and CAN-L reconnected
>
> can1 123 [3] 11 22 33
> can1 20000004 [8] 00 30 00 00 00 00 00 00 ERRORFRAME
> controller-problem{rx-error-passive,tx-error-passive}
Why do we get an error-passive message? Now I will have a closer look to
the code...
> Let me know if it is correct.
>
> Thanks,
>
> Olivier
>
> drivers/net/can/usb/Kconfig | 33 +
> drivers/net/can/usb/Makefile | 1 +
> drivers/net/can/usb/kvaser_usb.c | 1437 ++++++++++++++++++++++++++++++++++++++
> 3 files changed, 1471 insertions(+)
> create mode 100644 drivers/net/can/usb/kvaser_usb.c
>
> diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig
> index 0a68768..578955f 100644
> --- a/drivers/net/can/usb/Kconfig
> +++ b/drivers/net/can/usb/Kconfig
> @@ -13,6 +13,39 @@ config CAN_ESD_USB2
> This driver supports the CAN-USB/2 interface
> from esd electronic system design gmbh (http://www.esd.eu).
>
> +config CAN_KVASER_USB
> + tristate "Kvaser CAN/USB interface"
> + ---help---
> + This driver adds support for Kvaser CAN/USB devices like Kvaser
> + Leaf Light.
> +
> + The driver gives support for the following devices:
> + - Kvaser Leaf prototype (P010v2 and v3)
> + - Kvaser Leaf Light (P010v3)
> + - 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, Prototype
> + - 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 OEM Mercury
> + - Kvaser OEM Leaf
> + - Kvaser USBcan R
> +
> + If unsure, say N.
> +
> + To compile this driver as a module, choose M here: the
> + module will be called kvaser_usb.
> +
> config CAN_PEAK_USB
> tristate "PEAK PCAN-USB/USB Pro interfaces"
> ---help---
> diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile
> index da6d1d3..80a2ee4 100644
> --- a/drivers/net/can/usb/Makefile
> +++ b/drivers/net/can/usb/Makefile
> @@ -4,6 +4,7 @@
>
> obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o
> obj-$(CONFIG_CAN_ESD_USB2) += esd_usb2.o
> +obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o
> obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/
>
> ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
> diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c
> new file mode 100644
> index 0000000..eea0655
> --- /dev/null
> +++ b/drivers/net/can/usb/kvaser_usb.c
> @@ -0,0 +1,1437 @@
> +/*
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation version 2.
> + *
> + * Parts of this driver are based on the following:
> + * - Kvaser linux leaf driver (version 4.78)
> + * - CAN driver for esd CAN-USB/2
> + *
> + * Copyright (C) 2002-2006 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>
> + */
> +
> +#include <linux/init.h>
> +#include <linux/completion.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>
> +
> +#define MAX_TX_URBS 16
> +#define MAX_RX_URBS 4
> +#define START_TIMEOUT 1000
> +#define STOP_TIMEOUT 1000
> +#define USB_SEND_TIMEOUT 1000
> +#define USB_RECV_TIMEOUT 1000
A comment on the units would be nice!
> +#define RX_BUFFER_SIZE 3072
> +#define CAN_USB_CLOCK 8000000
> +#define MAX_NET_DEVICES 3
> +
> +/* Kvaser USB devices */
> +#define KVASER_VENDOR_ID 0x0bfd
> +#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
> +
> +/* USB devices features */
> +#define KVASER_HAS_SILENT_MODE BIT(0)
> +
> +/* Message header size */
> +#define MSG_HEADER_LEN 2
> +
> +/* 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 */
> +#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)
> +
> +/* Can msg 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_GET_CTRL_MODE 22
> +#define CMD_GET_CTRL_MODE_REPLY 23
> +#define CMD_RESET_CHIP 24
> +#define CMD_RESET_CARD 25
> +#define CMD_START_CHIP 26
> +#define CMD_START_CHIP_REPLY 27
> +#define CMD_STOP_CHIP 28
> +#define CMD_STOP_CHIP_REPLY 29
> +#define CMD_GET_CARD_INFO2 32
> +#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_RESET_ERROR_COUNTER 49
> +#define CMD_TX_ACKNOWLEDGE 50
> +#define CMD_CAN_ERROR_EVENT 51
> +#define CMD_USB_THROTTLE 77
> +
> +/* 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)
> +
> +/* bittiming parameters */
> +#define KVASER_USB_TSEG1_MIN 1
> +#define KVASER_USB_TSEG1_MAX 16
> +#define KVASER_USB_TSEG2_MIN 1
> +#define KVASER_USB_TSEG2_MAX 8
> +#define KVASER_USB_SJW_MAX 4
> +#define KVASER_USB_BRP_MIN 1
> +#define KVASER_USB_BRP_MAX 64
> +#define KVASER_USB_BRP_INC 1
> +
> +/* 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
> +
> +struct kvaser_msg_simple {
> + u8 tid;
> + u8 channel;
> +} __packed;
> +
> +struct kvaser_msg_cardinfo {
> + u8 tid;
> + u8 nchannels;
> + __le32 serial_number;
> + __le32 padding;
> + __le32 clock_resolution;
> + __le32 mfgdate;
> + u8 ean[8];
> + u8 hw_revision;
> + u8 usb_hs_mode;
> + __le16 padding2;
> +} __packed;
> +
> +struct kvaser_msg_cardinfo2 {
> + u8 tid;
> + u8 channel;
> + u8 pcb_id[24];
> + __le32 oem_unlock_code;
> +} __packed;
> +
> +struct kvaser_msg_softinfo {
> + u8 tid;
> + u8 channel;
> + __le32 sw_options;
> + __le32 fw_version;
> + __le16 max_outstanding_tx;
> + __le16 padding[9];
> +} __packed;
> +
> +struct kvaser_msg_busparams {
> + u8 tid;
> + u8 channel;
> + __le32 bitrate;
> + u8 tseg1;
> + u8 tseg2;
> + u8 sjw;
> + u8 no_samp;
> +} __packed;
> +
> +struct kvaser_msg_tx_can {
> + u8 channel;
> + u8 tid;
> + u8 msg[14];
> + u8 padding;
> + u8 flags;
> +} __packed;
> +
> +struct kvaser_msg_rx_can {
> + u8 channel;
> + u8 flag;
> + __le16 time[3];
> + u8 msg[14];
> +} __packed;
> +
> +struct kvaser_msg_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 kvaser_msg_tx_acknowledge {
> + u8 channel;
> + u8 tid;
> + __le16 time[3];
> + u8 flags;
> + u8 time_offset;
> +} __packed;
> +
> +struct kvaser_msg_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 kvaser_msg_ctrl_mode {
> + u8 tid;
> + u8 channel;
> + u8 ctrl_mode;
> + u8 padding[3];
> +} __packed;
> +
> +struct kvaser_msg_flush_queue {
> + u8 tid;
> + u8 channel;
> + u8 flags;
> + u8 padding[3];
> +} __packed;
> +
> +struct kvaser_msg {
> + u8 len;
> + u8 id;
> + union {
> + struct kvaser_msg_simple simple;
> + struct kvaser_msg_cardinfo cardinfo;
> + struct kvaser_msg_cardinfo2 cardinfo2;
> + struct kvaser_msg_softinfo softinfo;
> + struct kvaser_msg_busparams busparams;
> + struct kvaser_msg_tx_can tx_can;
> + struct kvaser_msg_rx_can rx_can;
> + struct kvaser_msg_chip_state_event chip_state_event;
> + struct kvaser_msg_tx_acknowledge tx_acknowledge;
> + struct kvaser_msg_error_event error_event;
> + struct kvaser_msg_ctrl_mode ctrl_mode;
> + struct kvaser_msg_flush_queue flush_queue;
> + } u;
> +} __packed;
> +
> +struct kvaser_usb_tx_urb_context {
> + struct kvaser_usb_net_priv *priv;
> + u32 echo_index;
> + int dlc;
> +};
> +
> +struct kvaser_usb {
> + struct usb_device *udev;
> + struct kvaser_usb_net_priv *nets[MAX_NET_DEVICES];
> +
> + struct usb_endpoint_descriptor *bulk_in, *bulk_out;
> + struct usb_anchor rx_submitted;
> +
> + u32 fw_version;
> + unsigned int nchannels;
> +
> + bool rxinitdone;
> +};
> +
> +struct kvaser_usb_net_priv {
> + struct can_priv can;
> +
> + atomic_t active_tx_urbs;
> + struct usb_anchor tx_submitted;
> + struct kvaser_usb_tx_urb_context tx_contexts[MAX_TX_URBS];
> +
> + struct completion start_comp, stop_comp;
> +
> + struct kvaser_usb *dev;
> + struct net_device *netdev;
> + int channel;
> +
> + struct can_frame cf_err_old;
> +};
> +
> +static struct usb_device_id kvaser_usb_table[] = {
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_DEVEL_PRODUCT_ID) },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_PRODUCT_ID) },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_PRODUCT_ID),
> + .driver_info = KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_PRODUCT_ID),
> + .driver_info = KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LS_PRODUCT_ID),
> + .driver_info = KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_SWC_PRODUCT_ID),
> + .driver_info = KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LIN_PRODUCT_ID),
> + .driver_info = KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_LS_PRODUCT_ID),
> + .driver_info = KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_SWC_PRODUCT_ID),
> + .driver_info = KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_DEVEL_PRODUCT_ID),
> + .driver_info = KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSHS_PRODUCT_ID),
> + .driver_info = KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_UPRO_HSHS_PRODUCT_ID) },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_GI_PRODUCT_ID) },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_OBDII_PRODUCT_ID),
> + .driver_info = KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSLS_PRODUCT_ID) },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_CH_PRODUCT_ID) },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_BLACKBIRD_SPRO_PRODUCT_ID) },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_MERCURY_PRODUCT_ID) },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_LEAF_PRODUCT_ID) },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_CAN_R_PRODUCT_ID) },
> + { }
> +};
> +MODULE_DEVICE_TABLE(usb, kvaser_usb_table);
> +
> +static inline int kvaser_usb_send_msg(const struct kvaser_usb *dev,
> + struct kvaser_msg *msg)
> +{
> + int actual_len;
> +
> + return usb_bulk_msg(dev->udev,
> + usb_sndbulkpipe(dev->udev,
> + dev->bulk_out->bEndpointAddress),
> + msg, msg->len, &actual_len,
> + USB_SEND_TIMEOUT);
> +}
> +
> +static int kvaser_usb_wait_msg(const struct kvaser_usb *dev, u8 id,
> + struct kvaser_msg *msg)
> +{
> + struct kvaser_msg *tmp;
> + void *buf;
> + int actual_len;
> + int err;
> + int pos = 0;
> +
> + buf = kzalloc(RX_BUFFER_SIZE, GFP_KERNEL);
> + if (!buf)
> + return -ENOMEM;
> +
> + err = usb_bulk_msg(dev->udev,
> + usb_rcvbulkpipe(dev->udev,
> + dev->bulk_in->bEndpointAddress),
> + buf, RX_BUFFER_SIZE, &actual_len,
> + USB_RECV_TIMEOUT);
> + if (err < 0) {
> + kfree(buf);
> + return err;
> + }
goto might be more elegant/efficient here.
> +
> + while (pos <= actual_len - MSG_HEADER_LEN) {
> + tmp = buf + pos;
> +
> + if (!tmp->len)
> + break;
> +
> + if (pos + tmp->len > actual_len) {
> + dev_err(dev->udev->dev.parent, "Format error\n");
> + break;
> + }
> +
> + if (tmp->id == id) {
> + memcpy(msg, tmp, tmp->len);
> + kfree(buf);
> + return 0;
> + }
> +
> + pos += tmp->len;
> + }
> +
> + kfree(buf);
> +
> + return -EINVAL;
> +}
> +
> +static int kvaser_usb_send_simple_msg(const struct kvaser_usb *dev,
> + u8 msg_id, int channel)
> +{
> + struct kvaser_msg msg = {
> + .len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple),
> + .id = msg_id,
> + .u.simple.channel = channel,
> + .u.simple.tid = 0xff,
> + };
> +
> + return kvaser_usb_send_msg(dev, &msg);
> +}
> +
> +static int kvaser_usb_get_software_info(struct kvaser_usb *dev)
> +{
> + struct kvaser_msg msg;
> + int err;
> +
> + err = kvaser_usb_send_simple_msg(dev, CMD_GET_SOFTWARE_INFO, 0);
> + if (err)
> + return err;
> +
> + err = kvaser_usb_wait_msg(dev, CMD_GET_SOFTWARE_INFO_REPLY, &msg);
> + if (err)
> + return err;
> +
> + dev->fw_version = le32_to_cpu(msg.u.softinfo.fw_version);
> +
> + return 0;
> +}
> +
> +static int kvaser_usb_get_card_info(struct kvaser_usb *dev)
> +{
> + struct kvaser_msg msg;
> + int err;
> +
> + err = kvaser_usb_send_simple_msg(dev, CMD_GET_CARD_INFO, 0);
> + if (err)
> + return err;
> +
> + err = kvaser_usb_wait_msg(dev, CMD_GET_CARD_INFO_REPLY, &msg);
> + if (err)
> + return err;
> +
> + dev->nchannels = msg.u.cardinfo.nchannels;
> +
> + return 0;
> +}
> +
> +static void kvaser_usb_tx_acknowledge(const struct kvaser_usb *dev,
> + const struct kvaser_msg *msg)
> +{
> + struct net_device_stats *stats;
> + struct kvaser_usb_tx_urb_context *context;
> + struct kvaser_usb_net_priv *priv;
> + u8 channel = msg->u.tx_acknowledge.channel;
> + u8 tid = msg->u.tx_acknowledge.tid;
> +
> + if (channel >= dev->nchannels) {
> + dev_err(dev->udev->dev.parent,
> + "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 % MAX_TX_URBS];
> +
> + /*
> + * It looks like the firmware never sets the flags field of the
> + * tx_acknowledge frame and never reports a transmit failure.
> + * If the can message can't be transmited (e.g. incompatible
> + * bitrates), a frame CMD_CAN_ERROR_EVENT is sent (with a null
> + * tid) and the firmware tries to transmit again the packet until
> + * it succeeds. Once the packet is successfully transmitted, then
> + * the tx_acknowledge frame is sent.
> + */
> +
> + stats->tx_packets++;
> + stats->tx_bytes += context->dlc;
> + can_get_echo_skb(priv->netdev, context->echo_index);
> +
> + context->echo_index = MAX_TX_URBS;
> + atomic_dec(&priv->active_tx_urbs);
> +
> + netif_wake_queue(priv->netdev);
> +}
> +
> +static void kvaser_usb_simple_msg_callback(struct urb *urb)
> +{
> + struct net_device *netdev = urb->context;
> +
> + if (urb->status)
> + netdev_warn(netdev, "urb status received: %d\n",
> + urb->status);
> +}
> +
> +static int kvaser_usb_simple_msg_async(struct kvaser_usb_net_priv *priv,
> + u8 msg_id)
> +{
> + struct kvaser_usb *dev = priv->dev;
> + struct net_device *netdev = priv->netdev;
> + struct kvaser_msg *msg;
> + struct urb *urb;
> + void *buf;
> + int err;
> +
> + urb = usb_alloc_urb(0, GFP_ATOMIC);
> + if (!urb) {
> + netdev_err(netdev, "No memory left for URBs\n");
> + return -ENOMEM;
> + }
> +
> + buf = usb_alloc_coherent(dev->udev, sizeof(struct kvaser_msg),
> + GFP_ATOMIC, &urb->transfer_dma);
> + if (!buf) {
> + netdev_err(netdev, "No memory left for USB buffer\n");
> + return -ENOMEM;
> + }
> +
> + msg = (struct kvaser_msg *)buf;
> + msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple);
> + msg->id = msg_id;
> + msg->u.simple.channel = priv->channel;
> +
> + usb_fill_bulk_urb(urb, dev->udev,
> + usb_sndbulkpipe(dev->udev,
> + dev->bulk_out->bEndpointAddress),
> + buf, msg->len,
> + kvaser_usb_simple_msg_callback, priv);
> + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
> + 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);
> + return err;
> + }
> +
> + return 0;
> +}
> +
> +static void kvaser_usb_unlink_tx_urbs(struct kvaser_usb_net_priv *priv)
> +{
> + int 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;
> +}
> +
> +static void kvaser_usb_rx_error(const struct kvaser_usb *dev,
> + const struct kvaser_msg *msg)
> +{
> + struct can_frame *cf;
> + struct sk_buff *skb;
> + struct net_device_stats *stats;
> + struct kvaser_usb_net_priv *priv;
> + unsigned int new_state;
> + u8 channel, status;
> +
> + if (msg->id == CMD_CAN_ERROR_EVENT) {
> + channel = msg->u.error_event.channel;
> + status = msg->u.error_event.status;
> + } else {
> + channel = msg->u.chip_state_event.channel;
> + status = msg->u.chip_state_event.status;
> + }
> +
> + if (channel >= dev->nchannels) {
> + dev_err(dev->udev->dev.parent,
> + "Invalid channel number (%d)\n", channel);
> + return;
> + }
> +
> + priv = dev->nets[channel];
> + stats = &priv->netdev->stats;
> +
> + if (status & M16C_STATE_BUS_RESET) {
> + kvaser_usb_unlink_tx_urbs(priv);
> + return;
> + }
> + skb = alloc_can_err_skb(priv->netdev, &cf);
> + if (!skb) {
> + stats->rx_dropped++;
> + return;
Cleanup? kvaser_usb_unlink_tx_urbs()?
> + }
> +
> + if (status & M16C_STATE_BUS_OFF) {
> + cf->can_id |= CAN_ERR_BUSOFF;
> +
> + if (!priv->can.restart_ms)
> + kvaser_usb_simple_msg_async(priv, CMD_STOP_CHIP);
> +
> + if (!priv->can.state != CAN_ERR_BUSOFF) {
> + priv->can.can_stats.bus_off++;
> + netif_carrier_off(priv->netdev);
> + }
> +
> + new_state = CAN_STATE_BUS_OFF;
> + } else if (status & M16C_STATE_BUS_PASSIVE) {
> + cf->can_id |= CAN_ERR_CRTL;
> + cf->data[1] = CAN_ERR_CRTL_TX_PASSIVE |
> + CAN_ERR_CRTL_RX_PASSIVE;
State changes should only be report when the state really changes.
Therefore it should go under the if block below.
> + if (priv->can.state != CAN_STATE_ERROR_PASSIVE)
> + priv->can.can_stats.error_passive++;
> +
> + new_state = CAN_STATE_ERROR_PASSIVE;
> + } else if (status & M16C_STATE_BUS_ERROR) {
Hm, strange, a bus error is not a state change. You use here if...else
if... Isn't it possible that more than one bit is set.
> + cf->can_id |= CAN_ERR_CRTL;
> + cf->data[1] = CAN_ERR_CRTL_TX_WARNING |
> + CAN_ERR_CRTL_RX_WARNING;
See above.
> + if (priv->can.state != CAN_STATE_ERROR_WARNING)
> + priv->can.can_stats.error_warning++;
> +
> + new_state = CAN_STATE_ERROR_WARNING;
> + } else {
> + cf->can_id |= CAN_ERR_PROT;
> + cf->data[2] = CAN_ERR_PROT_ACTIVE;
> +
> + new_state = CAN_STATE_ERROR_ACTIVE;
> + }
> +
> + if (priv->can.restart_ms &&
> + (priv->can.state >= CAN_STATE_BUS_OFF) &&
> + (new_state < CAN_STATE_BUS_OFF)) {
> + cf->can_id |= CAN_ERR_RESTARTED;
> + priv->can.can_stats.restarts++;
> + netif_carrier_on(priv->netdev);
> + }
> +
> + if (msg->id == CMD_CAN_ERROR_EVENT) {
> + u8 error_factor = msg->u.error_event.error_factor;
> +
> + priv->can.can_stats.bus_error++;
> + stats->rx_errors++;
> +
> + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
> +
> + if (error_factor & M16C_EF_ACKE)
> + cf->data[3] |= (CAN_ERR_PROT_LOC_ACK |
> + CAN_ERR_PROT_LOC_ACK_DEL);
> + if (error_factor & M16C_EF_CRCE)
> + cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ |
> + CAN_ERR_PROT_LOC_CRC_DEL);
> + if (error_factor & M16C_EF_FORME)
> + cf->data[2] |= CAN_ERR_PROT_FORM;
> + if (error_factor & M16C_EF_STFE)
> + cf->data[2] |= CAN_ERR_PROT_STUFF;
> + if (error_factor & M16C_EF_BITE0)
> + cf->data[2] |= CAN_ERR_PROT_BIT0;
> + if (error_factor & M16C_EF_BITE1)
> + cf->data[2] |= CAN_ERR_PROT_BIT1;
> + if (error_factor & M16C_EF_TRE)
> + cf->data[2] |= CAN_ERR_PROT_TX;
> + }
> +
> + priv->can.state = new_state;
> +
> + if (!memcmp(cf, &priv->cf_err_old, sizeof(*cf))) {
> + kfree_skb(skb);
> + return;
> + }
Hm, the firmware seems not to clear error conditions? Anyway, state
change and error reporting is magic on many CAN controllers. Just the
SJA1000 is doing it nicely.
> + netif_rx(skb);
> +
> + priv->cf_err_old = *cf;
> +
> + stats->rx_packets++;
> + stats->rx_bytes += cf->can_dlc;
> +}
> +
> +static void kvaser_usb_rx_can_msg(const struct kvaser_usb *dev,
> + const struct kvaser_msg *msg)
> +{
> + struct kvaser_usb_net_priv *priv;
> + struct can_frame *cf;
> + struct sk_buff *skb;
> + struct net_device_stats *stats;
> + u8 channel = msg->u.rx_can.channel;
> +
> + if (channel >= dev->nchannels) {
> + dev_err(dev->udev->dev.parent,
> + "Invalid channel number (%d)\n", channel);
> + return;
> + }
> +
> + priv = dev->nets[channel];
> + stats = &priv->netdev->stats;
> +
> + skb = alloc_can_skb(priv->netdev, &cf);
> + if (skb == NULL) {
s/skb == NULL)/!skb/ ?
> + stats->tx_dropped++;
> + return;
> + }
> +
> + cf->can_id = ((msg->u.rx_can.msg[0] & 0x1f) << 6) |
> + (msg->u.rx_can.msg[1] & 0x3f);
> + cf->can_dlc = get_can_dlc(msg->u.rx_can.msg[5]);
> +
> + if (msg->id == CMD_RX_EXT_MESSAGE) {
> + cf->can_id <<= 18;
> + cf->can_id |= ((msg->u.rx_can.msg[2] & 0x0f) << 14) |
> + ((msg->u.rx_can.msg[3] & 0xff) << 6) |
> + (msg->u.rx_can.msg[4] & 0x3f);
> + cf->can_id |= CAN_EFF_FLAG;
> + }
> +
> + if (msg->u.rx_can.flag & MSG_FLAG_REMOTE_FRAME) {
> + cf->can_id |= CAN_RTR_FLAG;
> + } else if (msg->u.rx_can.flag & (MSG_FLAG_ERROR_FRAME |
> + MSG_FLAG_NERR)) {
> + cf->can_id |= CAN_ERR_FLAG;
> + cf->can_dlc = CAN_ERR_DLC;
> + cf->data[1] = CAN_ERR_CRTL_UNSPEC;
What is the meaning of such errors? A comment, netdev_err() or
netdev_dbg() would be nice.
> +
> + stats->rx_errors++;
> + } else if (msg->u.rx_can.flag & MSG_FLAG_OVERRUN) {
> + cf->can_id = CAN_ERR_FLAG | CAN_ERR_CRTL;
> + cf->can_dlc = CAN_ERR_DLC;
> + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
> +
> + stats->rx_over_errors++;
> + stats->rx_errors++;
> + } else if (!msg->u.rx_can.flag) {
> + memcpy(cf->data, &msg->u.rx_can.msg[6], cf->can_dlc);
> + } else {
> + kfree_skb(skb);
> + return;
> + }
> +
> + netif_rx(skb);
> +
> + stats->rx_packets++;
> + stats->rx_bytes += cf->can_dlc;
> +}
> +
> +static void kvaser_usb_start_chip_reply(const struct kvaser_usb *dev,
> + const struct kvaser_msg *msg)
> +{
> + struct kvaser_usb_net_priv *priv;
> + u8 channel = msg->u.simple.channel;
> +
> + if (channel >= dev->nchannels) {
> + dev_err(dev->udev->dev.parent,
> + "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_stop_chip_reply(const struct kvaser_usb *dev,
> + const struct kvaser_msg *msg)
> +{
> + struct kvaser_usb_net_priv *priv;
> + u8 channel = msg->u.simple.channel;
> +
> + if (channel >= dev->nchannels) {
> + dev_err(dev->udev->dev.parent,
> + "Invalid channel number (%d)\n", channel);
> + return;
> + }
> +
> + priv = dev->nets[channel];
> +
> + complete(&priv->stop_comp);
> +}
> +
> +static void kvaser_usb_handle_message(const struct kvaser_usb *dev,
> + const struct kvaser_msg *msg)
> +{
> + switch (msg->id) {
> + case CMD_START_CHIP_REPLY:
> + kvaser_usb_start_chip_reply(dev, msg);
> + break;
> +
> + case CMD_STOP_CHIP_REPLY:
> + kvaser_usb_stop_chip_reply(dev, msg);
> + break;
> +
> + case CMD_RX_STD_MESSAGE:
> + case CMD_RX_EXT_MESSAGE:
> + kvaser_usb_rx_can_msg(dev, msg);
> + break;
> +
> + case CMD_CHIP_STATE_EVENT:
> + case CMD_CAN_ERROR_EVENT:
> + kvaser_usb_rx_error(dev, msg);
> + break;
> +
> + case CMD_TX_ACKNOWLEDGE:
> + kvaser_usb_tx_acknowledge(dev, msg);
> + break;
> +
> + default:
> + dev_warn(dev->udev->dev.parent,
> + "Unhandled message (%d)\n", msg->id);
> + break;
> + }
> +}
> +
> +static void kvaser_usb_read_bulk_callback(struct urb *urb)
> +{
> + struct kvaser_usb *dev = urb->context;
> + struct kvaser_msg *msg;
> + int pos = 0;
> + int err, i;
> +
> + switch (urb->status) {
> + case 0:
> + break;
> + case -ENOENT:
> + 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 - MSG_HEADER_LEN) {
> + msg = urb->transfer_buffer + pos;
> +
> + if (!msg->len)
> + break;
> +
> + if (pos + msg->len > urb->actual_length) {
> + dev_err(dev->udev->dev.parent, "Format error\n");
> + break;
> + }
> +
> + kvaser_usb_handle_message(dev, msg);
> +
> + pos += msg->len;
> + }
> +
> +resubmit_urb:
> + usb_fill_bulk_urb(urb, dev->udev,
> + usb_rcvbulkpipe(dev->udev,
> + dev->bulk_in->bEndpointAddress),
> + urb->transfer_buffer, 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->udev->dev.parent,
> + "Failed resubmitting read bulk urb: %d\n", err);
> + }
> +
> + return;
> +}
> +
> +static int kvaser_usb_setup_rx_urbs(struct kvaser_usb *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;
> +
> + urb = usb_alloc_urb(0, GFP_KERNEL);
> + if (!urb) {
> + dev_warn(dev->udev->dev.parent,
> + "No memory left for URBs\n");
> + err = -ENOMEM;
> + break;
> + }
> +
> + buf = usb_alloc_coherent(dev->udev, RX_BUFFER_SIZE,
> + GFP_KERNEL, &urb->transfer_dma);
> + if (!buf) {
> + dev_warn(dev->udev->dev.parent,
> + "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, RX_BUFFER_SIZE,
> + kvaser_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);
> + break;
> + }
> +
> + usb_free_urb(urb);
> + }
> +
> + if (i == 0) {
> + dev_warn(dev->udev->dev.parent,
> + "Cannot setup read URBs, error %d\n", err);
> + return err;
> + } else if (i < MAX_RX_URBS) {
> + dev_warn(dev->udev->dev.parent,
> + "RX performances may be slow\n");
> + }
> +
> + dev->rxinitdone = true;
> +
> + return 0;
> +}
> +
> +static int kvaser_usb_set_opt_mode(const struct kvaser_usb_net_priv *priv)
> +{
> + struct kvaser_msg msg = {
> + .id = CMD_SET_CTRL_MODE,
> + .len = MSG_HEADER_LEN +
> + sizeof(struct kvaser_msg_ctrl_mode),
> + .u.ctrl_mode.tid = 0xff,
> + .u.ctrl_mode.channel = priv->channel,
> + };
> +
> + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
> + msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_SILENT;
> + else
> + msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_NORMAL;
> +
> + return kvaser_usb_send_msg(priv->dev, &msg);
> +}
> +
> +static int kvaser_usb_start_chip(struct kvaser_usb_net_priv *priv)
> +{
> + int err;
> +
> + init_completion(&priv->start_comp);
> +
> + err = kvaser_usb_send_simple_msg(priv->dev, CMD_START_CHIP,
> + priv->channel);
> + if (err)
> + return err;
> +
> + if (!wait_for_completion_timeout(&priv->start_comp,
> + msecs_to_jiffies(START_TIMEOUT)))
> + return -ETIMEDOUT;
> +
> + 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;
> + int err;
> +
> + err = open_candev(netdev);
> + if (err)
> + return err;
> +
> + err = kvaser_usb_setup_rx_urbs(dev);
> + if (err)
> + return err;
close_candev()? Using "goto" is preferred for error handling.
> +
> + err = kvaser_usb_set_opt_mode(priv);
> + if (err)
> + return err;
> +
> + err = kvaser_usb_start_chip(priv);
> + if (err) {
> + netdev_warn(netdev, "Cannot start device, error %d\n", err);
> + close_candev(netdev);
> + return err;
> + }
> +
> + priv->can.state = CAN_STATE_ERROR_ACTIVE;
> +
> + return 0;
> +}
> +
> +static void kvaser_usb_unlink_all_urbs(struct kvaser_usb *dev)
> +{
> + int i;
> +
> + usb_kill_anchored_urbs(&dev->rx_submitted);
> +
> + for (i = 0; i < MAX_NET_DEVICES; i++) {
> + struct kvaser_usb_net_priv *priv = dev->nets[i];
> +
> + if (priv)
> + kvaser_usb_unlink_tx_urbs(priv);
> + }
> +}
> +
> +static int kvaser_usb_stop_chip(struct kvaser_usb_net_priv *priv)
> +{
> + int err;
> +
> + init_completion(&priv->stop_comp);
> +
> + err = kvaser_usb_send_simple_msg(priv->dev, CMD_STOP_CHIP,
> + priv->channel);
> + if (err)
> + return err;
> +
> + if (!wait_for_completion_timeout(&priv->stop_comp,
> + msecs_to_jiffies(STOP_TIMEOUT)))
> + return -ETIMEDOUT;
> +
> + return 0;
> +}
> +
> +static int kvaser_usb_flush_queue(struct kvaser_usb_net_priv *priv)
> +{
> + struct kvaser_msg msg = {
> + .id = CMD_FLUSH_QUEUE,
> + .len = MSG_HEADER_LEN +
> + sizeof(struct kvaser_msg_flush_queue),
> + .u.flush_queue.channel = priv->channel,
> + .u.flush_queue.flags = 0x00,
> + };
> +
> + return kvaser_usb_send_msg(priv->dev, &msg);
> +}
> +
> +static int kvaser_usb_close(struct net_device *netdev)
> +{
> + struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
> + struct kvaser_usb *dev = priv->dev;
> + int err;
> +
> + netif_stop_queue(netdev);
> +
> + err = kvaser_usb_flush_queue(priv);
> + if (err)
> + netdev_warn(netdev, "Cannot flush queue, error %d\n", err);
> +
> + if (kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, priv->channel))
> + netdev_warn(netdev, "Cannot reset card, error %d\n", err);
> +
> + err = kvaser_usb_stop_chip(priv);
> + if (err) {
> + netdev_warn(netdev, "Cannot stop device, error %d\n", err);
> + return err;
close_candev()? Returning here is maybe not a good idea?
> + }
> +
> + priv->can.state = CAN_STATE_STOPPED;
> + close_candev(priv->netdev);
> +
> + return 0;
> +}
> +
> +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;
> +
> + usb_free_coherent(urb->dev, urb->transfer_buffer_length,
> + 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);
> +}
> +
> +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;
> + struct net_device_stats *stats = &netdev->stats;
> + struct can_frame *cf = (struct can_frame *)skb->data;
> + struct kvaser_usb_tx_urb_context *context = NULL;
> + struct urb *urb;
> + void *buf;
> + struct kvaser_msg *msg;
> + int i, err;
> + int ret = NETDEV_TX_OK;
> +
> + if (can_dropped_invalid_skb(netdev, skb))
> + return NETDEV_TX_OK;
> +
> + urb = usb_alloc_urb(0, GFP_ATOMIC);
> + if (!urb) {
> + netdev_err(netdev, "No memory left for URBs\n");
> + stats->tx_dropped++;
> + dev_kfree_skb(skb);
> + return NETDEV_TX_OK;
> + }
> +
> + buf = usb_alloc_coherent(dev->udev, sizeof(struct kvaser_msg),
> + 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 kvaser_msg *)buf;
> + msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_tx_can);
> + msg->u.tx_can.flags = 0;
> + msg->u.tx_can.channel = priv->channel;
> +
> + if (cf->can_id & CAN_EFF_FLAG) {
> + msg->id = CMD_TX_EXT_MESSAGE;
> + msg->u.tx_can.msg[0] = (cf->can_id >> 24) & 0x1f;
> + msg->u.tx_can.msg[1] = (cf->can_id >> 18) & 0x3f;
> + msg->u.tx_can.msg[2] = (cf->can_id >> 14) & 0x0f;
> + msg->u.tx_can.msg[3] = (cf->can_id >> 6) & 0xff;
> + msg->u.tx_can.msg[4] = cf->can_id & 0x3f;
> + } else {
> + msg->id = CMD_TX_STD_MESSAGE;
> + msg->u.tx_can.msg[0] = (cf->can_id >> 6) & 0x1f;
> + msg->u.tx_can.msg[1] = cf->can_id & 0x3f;
> + }
> +
> + msg->u.tx_can.msg[5] = cf->can_dlc;
> + memcpy(&msg->u.tx_can.msg[6], cf->data, cf->can_dlc);
> +
> + if (cf->can_id & CAN_RTR_FLAG)
> + msg->u.tx_can.flags |= MSG_FLAG_REMOTE_FRAME;
> +
> + for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++) {
> + if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) {
> + context = &priv->tx_contexts[i];
> + break;
> + }
> + }
> +
> + if (!context) {
> + netdev_warn(netdev, "cannot find free context\n");
> + ret = NETDEV_TX_BUSY;
> + goto releasebuf;
> + }
> +
> + context->priv = priv;
> + context->echo_index = i;
> + context->dlc = cf->can_dlc;
> +
> + msg->u.tx_can.tid = context->echo_index;
> +
> + usb_fill_bulk_urb(urb, dev->udev,
> + usb_sndbulkpipe(dev->udev,
> + dev->bulk_out->bEndpointAddress),
> + buf, msg->len,
> + kvaser_usb_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);
> +
> + if (atomic_read(&priv->active_tx_urbs) >= MAX_TX_URBS)
> + netif_stop_queue(netdev);
> +
> + err = usb_submit_urb(urb, GFP_ATOMIC);
> + if (unlikely(err)) {
> + can_free_echo_skb(netdev, context->echo_index);
> +
> + atomic_dec(&priv->active_tx_urbs);
> + 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;
> + }
> +
> + netdev->trans_start = jiffies;
> +
> + usb_free_urb(urb);
> +
> + return NETDEV_TX_OK;
> +
> +releasebuf:
> + usb_free_coherent(dev->udev, sizeof(struct kvaser_msg),
> + buf, urb->transfer_dma);
> +nobufmem:
> + 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,
> +};
> +
> +static struct can_bittiming_const kvaser_usb_bittiming_const = {
> + .name = "kvaser_usb",
> + .tseg1_min = KVASER_USB_TSEG1_MIN,
> + .tseg1_max = KVASER_USB_TSEG1_MAX,
> + .tseg2_min = KVASER_USB_TSEG2_MIN,
> + .tseg2_max = KVASER_USB_TSEG2_MAX,
> + .sjw_max = KVASER_USB_SJW_MAX,
> + .brp_min = KVASER_USB_BRP_MIN,
> + .brp_max = KVASER_USB_BRP_MAX,
> + .brp_inc = KVASER_USB_BRP_INC,
> +};
> +
> +static int kvaser_usb_set_bittiming(struct net_device *netdev)
> +{
> + struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
> + struct can_bittiming *bt = &priv->can.bittiming;
> + struct kvaser_usb *dev = priv->dev;
> + struct kvaser_msg msg = {
> + .id = CMD_SET_BUS_PARAMS,
> + .len = MSG_HEADER_LEN +
> + sizeof(struct kvaser_msg_busparams),
> + .u.busparams.channel = priv->channel,
> + .u.busparams.tid = 0xff,
> + .u.busparams.bitrate = cpu_to_le32(bt->bitrate),
> + .u.busparams.sjw = bt->sjw,
> + .u.busparams.tseg1 = bt->prop_seg + bt->phase_seg1,
> + .u.busparams.tseg2 = bt->phase_seg2,
> + };
> +
> + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
> + msg.u.busparams.no_samp = 3;
> + else
> + msg.u.busparams.no_samp = 1;
> +
> + return kvaser_usb_send_msg(dev, &msg);
> +}
> +
> +static int kvaser_usb_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:
> + err = kvaser_usb_simple_msg_async(priv, CMD_START_CHIP);
> + if (err)
> + return err;
> + break;
> + default:
> + return -EOPNOTSUPP;
> + }
> +
> + return 0;
> +}
> +
> +static int kvaser_usb_init_one(struct usb_interface *intf,
> + const struct usb_device_id *id, int channel)
> +{
> + struct kvaser_usb *dev = usb_get_intfdata(intf);
> + struct net_device *netdev;
> + struct kvaser_usb_net_priv *priv;
> + int i, err;
> +
> + netdev = alloc_candev(sizeof(*priv), MAX_TX_URBS);
> + if (!netdev) {
> + dev_err(&intf->dev, "Cannot alloc candev\n");
> + return -ENOMEM;
> + }
> +
> + priv = netdev_priv(netdev);
> +
> + init_completion(&priv->start_comp);
> + init_completion(&priv->stop_comp);
> +
> + init_usb_anchor(&priv->tx_submitted);
> + atomic_set(&priv->active_tx_urbs, 0);
> +
> + for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++)
> + priv->tx_contexts[i].echo_index = MAX_TX_URBS;
> +
> + priv->dev = dev;
> + priv->netdev = netdev;
> + priv->channel = channel;
> +
> + priv->can.state = CAN_STATE_STOPPED;
> + priv->can.clock.freq = CAN_USB_CLOCK;
> + priv->can.bittiming_const = &kvaser_usb_bittiming_const;
> + priv->can.do_set_bittiming = kvaser_usb_set_bittiming;
> + priv->can.do_set_mode = kvaser_usb_set_mode;
> + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES;
> + if (id->driver_info & KVASER_HAS_SILENT_MODE)
> + priv->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY;
> +
> + netdev->flags |= IFF_ECHO;
> +
> + netdev->netdev_ops = &kvaser_usb_netdev_ops;
> +
> + SET_NETDEV_DEV(netdev, &intf->dev);
> +
> + err = register_candev(netdev);
> + if (err) {
> + dev_err(&intf->dev, "Failed to register can device\n");
> + free_candev(netdev);
> + return err;
> + }
> +
> + dev->nets[channel] = priv;
> + netdev_dbg(netdev, "device registered\n");
> +
> + return 0;
> +}
> +
> +static void kvaser_usb_get_endpoints(const struct usb_interface *intf,
> + struct usb_endpoint_descriptor **in,
> + struct usb_endpoint_descriptor **out)
> +{
> + const struct usb_host_interface *iface_desc;
> + struct usb_endpoint_descriptor *endpoint;
> + int i;
> +
> + iface_desc = &intf->altsetting[0];
> +
> + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
> + endpoint = &iface_desc->endpoint[i].desc;
> +
> + if (usb_endpoint_is_bulk_in(endpoint))
> + *in = endpoint;
> +
> + if (usb_endpoint_is_bulk_out(endpoint))
> + *out = endpoint;
> + }
> +}
> +
> +static int kvaser_usb_probe(struct usb_interface *intf,
> + const struct usb_device_id *id)
> +{
> + struct kvaser_usb *dev;
> + int err = -ENOMEM;
> + int i;
> +
> + dev = devm_kzalloc(&intf->dev, sizeof(*dev), GFP_KERNEL);
> + if (!dev)
> + return -ENOMEM;
> +
> + kvaser_usb_get_endpoints(intf, &dev->bulk_in, &dev->bulk_out);
> + if (!dev->bulk_in || !dev->bulk_out) {
> + 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);
> +
> + for (i = 0; i < MAX_NET_DEVICES; i++)
> + kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, i);
> +
> + err = kvaser_usb_get_software_info(dev);
> + if (err) {
> + dev_err(&intf->dev,
> + "Cannot get software infos, error %d\n", err);
> + return err;
> + }
> +
> + err = kvaser_usb_get_card_info(dev);
> + if (err) {
> + dev_err(&intf->dev,
> + "Cannot get card infos, error %d\n", err);
> + return err;
> + }
> +
> + dev_dbg(&intf->dev, "Firmware version: %d.%d.%d\n",
> + ((dev->fw_version >> 24) & 0xff),
> + ((dev->fw_version >> 16) & 0xff),
> + (dev->fw_version & 0xffff));
> +
> + for (i = 0; i < dev->nchannels; i++)
> + kvaser_usb_init_one(intf, id, i);
> +
> + return 0;
> +}
> +
> +static void kvaser_usb_disconnect(struct usb_interface *intf)
> +{
> + struct kvaser_usb *dev = usb_get_intfdata(intf);
> + int i;
> +
> + usb_set_intfdata(intf, NULL);
> +
> + if (!dev)
> + return;
> +
> + for (i = 0; i < dev->nchannels; i++) {
> + if (!dev->nets[i])
> + continue;
> +
> + unregister_netdev(dev->nets[i]->netdev);
> + free_candev(dev->nets[i]->netdev);
> + }
> +
> + kvaser_usb_unlink_all_urbs(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_DESCRIPTION("Can driver for Kvaser CAN/USB devices");
s/Can/CAN/
> +MODULE_LICENSE("GPL v2");
Wolfgang.
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH v2] can: kvaser_usb: Add support for Kvaser CAN/USB devices
2012-08-07 6:26 ` Wolfgang Grandegger
@ 2012-08-08 6:14 ` Olivier Sobrie
2012-08-08 8:25 ` Wolfgang Grandegger
0 siblings, 1 reply; 43+ messages in thread
From: Olivier Sobrie @ 2012-08-08 6:14 UTC (permalink / raw)
To: Wolfgang Grandegger; +Cc: Marc Kleine-Budde, linux-can, linux-usb, netdev
Hi Wolfgang,
On Tue, Aug 07, 2012 at 08:26:38AM +0200, Wolfgang Grandegger wrote:
> On 08/06/2012 07:21 AM, Olivier Sobrie wrote:
> > This driver provides support for several Kvaser CAN/USB devices.
> > Such kind of devices supports up to three can network interfaces.
>
> s/can/CAN/
>
> > It has been tested with a Kvaser USB Leaf Light (one network interface)
> > connected to a pch_can interface.
> > The firmware version of the Kvaser device was 2.5.205.
> >
> > List of Kvaser devices supported by the driver:
> > - Kvaser Leaf prototype (P010v2 and v3)
> > - Kvaser Leaf Light (P010v3)
> > - 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, Prototype
> > - 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 OEM Mercury
> > - Kvaser OEM Leaf
> > - Kvaser USBcan R
>
> Impressive list! What CAN controllers are used inside the devices? SJA1000?
I took this list from the Kvaser driver. However I only have a Kvaser
Leaf Light device thus I'm not sure it will work with other ones.
If you prefer I can only let Kvaser Leaf Light instead of the full list.
In my device it looks to be a Renesas M16C controller.
>
> > Signed-off-by: Olivier Sobrie <olivier@sobrie.be>
> > ---
> > Changes since v1:
> > - added copyrights
> > - kvaser_usb.h merged into kvaser.c
> > - added kvaser_usb_get_endpoints to find eindpoints instead of
> > hardcoding their address
> > - some cleanup and comestic changes
> > - fixed issues with errors handling
> > - fixed restart-ms == 0 case
> > - removed do_get_berr_counter method since the hardware doens't return
> > good values for txerr and rxerr.
> >
> > If someone in the linux-usb mailing can review it, it would be nice.
> >
> > Concerning the errors, it behaves like that now:
> >
> > 1) Short-circuit CAN-H and CAN-L and restart-ms = 0
> >
> > t0: short-circuit + 'cansend can1 123#112233'
> >
> > can1 2000008C [8] 00 0C 90 00 00 00 00 00 ERRORFRAME
> > controller-problem{rx-error-warning,tx-error-warning}
> > protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> > bus-error
> > can1 2000008C [8] 00 30 90 00 00 00 00 00 ERRORFRAME
> > controller-problem{rx-error-passive,tx-error-passive}
> > protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> > bus-error
> > can1 200000C8 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
> > protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> > bus-off
> > bus-error
> >
> > t1: remove short-circuit + 'ip link set can1 type can restart'
> >
> > can1 20000100 [8] 00 00 00 00 00 00 00 00 ERRORFRAME
> > restarted-after-bus-off
> > can1 20000004 [8] 00 0C 00 00 00 00 00 00 ERRORFRAME
> > controller-problem{rx-error-warning,tx-error-warning}
>
> Why do we get the last error message? Maybe the firmware does it that
> way (going down passive->warning->active).
It goes in that order: warning -> passive -> bus off -> warning
-> passive -> ...
>
> > 2) Short-circuit CAN-H and CAN-L and restart-ms = 100
> >
> > t0: short-circuit + cansend can1 123#112233
> >
> > can1 2000008C [8] 00 0C 90 00 00 00 00 00 ERRORFRAME
> > controller-problem{rx-error-warning,tx-error-warning}
> > protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> > bus-error
> > can1 2000008C [8] 00 30 90 00 00 00 00 00 ERRORFRAME
> > controller-problem{rx-error-passive,tx-error-passive}
> > protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> > bus-error
> > can1 200000C8 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
> > protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> > bus-off
> > bus-error
> > can1 2000018C [8] 00 0C 90 00 00 00 00 00 ERRORFRAME
> > controller-problem{rx-error-warning,tx-error-warning}
> > protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> > bus-error
> > restarted-after-bus-off
> > can1 2000008C [8] 00 0C 90 00 00 00 00 00 ERRORFRAME
> > controller-problem{rx-error-warning,tx-error-warning}
> > protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> > bus-error
> > can1 2000008C [8] 00 30 90 00 00 00 00 00 ERRORFRAME
> > controller-problem{rx-error-passive,tx-error-passive}
> > protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> > bus-error
> > can1 200000C8 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
> > protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> > bus-off
> > bus-error
> > ...
> >
> > can1 2000018C [8] 00 0C 90 00 00 00 00 00 ERRORFRAME
> > controller-problem{rx-error-warning,tx-error-warning}
> > protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> > bus-error
> > restarted-after-bus-off
> > can1 2000008C [8] 00 0C 90 00 00 00 00 00 ERRORFRAME
> > controller-problem{rx-error-warning,tx-error-warning}
> > protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> > bus-error
> > can1 2000008C [8] 00 30 90 00 00 00 00 00 ERRORFRAME
> > controller-problem{rx-error-passive,tx-error-passive}
> > protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
> > bus-error
> >
> > t1: remove short-circuit
> >
> > can1 123 [3] 11 22 33
> > can1 20000008 [8] 00 00 40 00 00 00 00 00 ERRORFRAME
> > protocol-violation{{back-to-error-active}{}}
>
> The order is still inverted but likely the firmware is doing it that way.
Indeed the firmware does it that way: it sends the acknwledge of the
frame beofre the state change. I can avoid that behavior by checking the
state in the acknowledge frame and send the restart frame if the bus was
off.
>
> > 3) CAN-H and CAN-L disconnected
> >
> > t0: CAN-H and CAN-L disconnected + cansend can1 123#112233
> >
> > can1 20000004 [8] 00 30 00 00 00 00 00 00 ERRORFRAME
> > controller-problem{rx-error-passive,tx-error-passive}
> > can1 2000008C [8] 00 30 80 1B 00 00 00 00 ERRORFRAME
> > controller-problem{rx-error-passive,tx-error-passive}
> > protocol-violation{{error-on-tx}{acknowledge-delimiter}}
> > bus-error
> >
> > t1: CAN-H and CAN-L reconnected
> >
> > can1 123 [3] 11 22 33
> > can1 20000004 [8] 00 30 00 00 00 00 00 00 ERRORFRAME
> > controller-problem{rx-error-passive,tx-error-passive}
>
> Why do we get an error-passive message? Now I will have a closer look to
> the code...
The firmware sends a CMD_CAN_ERROR_EVENT with the passive bit set...
> > +static void kvaser_usb_rx_error(const struct kvaser_usb *dev,
> > + const struct kvaser_msg *msg)
> > +{
> > + struct can_frame *cf;
> > + struct sk_buff *skb;
> > + struct net_device_stats *stats;
> > + struct kvaser_usb_net_priv *priv;
> > + unsigned int new_state;
> > + u8 channel, status;
> > +
> > + if (msg->id == CMD_CAN_ERROR_EVENT) {
> > + channel = msg->u.error_event.channel;
> > + status = msg->u.error_event.status;
> > + } else {
> > + channel = msg->u.chip_state_event.channel;
> > + status = msg->u.chip_state_event.status;
> > + }
> > +
> > + if (channel >= dev->nchannels) {
> > + dev_err(dev->udev->dev.parent,
> > + "Invalid channel number (%d)\n", channel);
> > + return;
> > + }
> > +
> > + priv = dev->nets[channel];
> > + stats = &priv->netdev->stats;
> > +
> > + if (status & M16C_STATE_BUS_RESET) {
> > + kvaser_usb_unlink_tx_urbs(priv);
> > + return;
> > + }
> > + skb = alloc_can_err_skb(priv->netdev, &cf);
> > + if (!skb) {
> > + stats->rx_dropped++;
> > + return;
>
> Cleanup? kvaser_usb_unlink_tx_urbs()?
If I get the M16C_STATE_BUS_RESET I'll not receive the ack frames anymore.
I need to set the context->echo_index back to MAX_TX_URBS to not loose tx
urbs.
By the way I think a can_free_echo_skb() is missing...
>
> > + }
> > +
> > + if (status & M16C_STATE_BUS_OFF) {
> > + cf->can_id |= CAN_ERR_BUSOFF;
> > +
> > + if (!priv->can.restart_ms)
> > + kvaser_usb_simple_msg_async(priv, CMD_STOP_CHIP);
> > +
> > + if (!priv->can.state != CAN_ERR_BUSOFF) {
> > + priv->can.can_stats.bus_off++;
> > + netif_carrier_off(priv->netdev);
> > + }
> > +
> > + new_state = CAN_STATE_BUS_OFF;
> > + } else if (status & M16C_STATE_BUS_PASSIVE) {
> > + cf->can_id |= CAN_ERR_CRTL;
> > + cf->data[1] = CAN_ERR_CRTL_TX_PASSIVE |
> > + CAN_ERR_CRTL_RX_PASSIVE;
>
> State changes should only be report when the state really changes.
> Therefore it should go under the if block below.
Ok. Is it possible to get such sequence:
can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
2 questions:
1) If the bus is still in CAN_ERR_CRTL_RX_PASSIVE after the second frame,
shouldn't we let the corresponding bit set, 0x10?
2) Can we send multiple times same frame wih same error bits set?
>
> > + if (priv->can.state != CAN_STATE_ERROR_PASSIVE)
> > + priv->can.can_stats.error_passive++;
> > +
> > + new_state = CAN_STATE_ERROR_PASSIVE;
> > + } else if (status & M16C_STATE_BUS_ERROR) {
>
> Hm, strange, a bus error is not a state change. You use here if...else
> if... Isn't it possible that more than one bit is set.
Indeed it is possible to have multiple bits set.
e.g. M16C_STATE_BUS_PASSIVE + M16C_STATE_BUS_ERROR or M16C_STATE_BUS_OFF + M16C_STATE_BUS_ERROR.
What error should I report in case of M16C_STATE_BUS_ERROR?
>
> > + cf->can_id |= CAN_ERR_CRTL;
> > + cf->data[1] = CAN_ERR_CRTL_TX_WARNING |
> > + CAN_ERR_CRTL_RX_WARNING;
>
> See above.
>
> > + if (priv->can.state != CAN_STATE_ERROR_WARNING)
> > + priv->can.can_stats.error_warning++;
> > +
> > + new_state = CAN_STATE_ERROR_WARNING;
> > + } else {
> > + cf->can_id |= CAN_ERR_PROT;
> > + cf->data[2] = CAN_ERR_PROT_ACTIVE;
> > +
> > + new_state = CAN_STATE_ERROR_ACTIVE;
> > + }
> > +
> > + if (priv->can.restart_ms &&
> > + (priv->can.state >= CAN_STATE_BUS_OFF) &&
> > + (new_state < CAN_STATE_BUS_OFF)) {
> > + cf->can_id |= CAN_ERR_RESTARTED;
> > + priv->can.can_stats.restarts++;
> > + netif_carrier_on(priv->netdev);
> > + }
> > +
> > + if (msg->id == CMD_CAN_ERROR_EVENT) {
> > + u8 error_factor = msg->u.error_event.error_factor;
> > +
> > + priv->can.can_stats.bus_error++;
> > + stats->rx_errors++;
> > +
> > + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
> > +
> > + if (error_factor & M16C_EF_ACKE)
> > + cf->data[3] |= (CAN_ERR_PROT_LOC_ACK |
> > + CAN_ERR_PROT_LOC_ACK_DEL);
> > + if (error_factor & M16C_EF_CRCE)
> > + cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ |
> > + CAN_ERR_PROT_LOC_CRC_DEL);
> > + if (error_factor & M16C_EF_FORME)
> > + cf->data[2] |= CAN_ERR_PROT_FORM;
> > + if (error_factor & M16C_EF_STFE)
> > + cf->data[2] |= CAN_ERR_PROT_STUFF;
> > + if (error_factor & M16C_EF_BITE0)
> > + cf->data[2] |= CAN_ERR_PROT_BIT0;
> > + if (error_factor & M16C_EF_BITE1)
> > + cf->data[2] |= CAN_ERR_PROT_BIT1;
> > + if (error_factor & M16C_EF_TRE)
> > + cf->data[2] |= CAN_ERR_PROT_TX;
> > + }
> > +
> > + priv->can.state = new_state;
> > +
> > + if (!memcmp(cf, &priv->cf_err_old, sizeof(*cf))) {
> > + kfree_skb(skb);
> > + return;
> > + }
>
> Hm, the firmware seems not to clear error conditions? Anyway, state
> change and error reporting is magic on many CAN controllers. Just the
> SJA1000 is doing it nicely.
I added it to prevent sending two times the same error. It happens that
the firmware sends multiple times the same error message.
>
> > + netif_rx(skb);
> > +
> > + priv->cf_err_old = *cf;
> > +
> > + stats->rx_packets++;
> > + stats->rx_bytes += cf->can_dlc;
> > +}
> > +
> > +static void kvaser_usb_rx_can_msg(const struct kvaser_usb *dev,
> > + const struct kvaser_msg *msg)
> > +{
> > + struct kvaser_usb_net_priv *priv;
> > + struct can_frame *cf;
> > + struct sk_buff *skb;
> > + struct net_device_stats *stats;
> > + u8 channel = msg->u.rx_can.channel;
> > +
> > + if (channel >= dev->nchannels) {
> > + dev_err(dev->udev->dev.parent,
> > + "Invalid channel number (%d)\n", channel);
> > + return;
> > + }
> > +
> > + priv = dev->nets[channel];
> > + stats = &priv->netdev->stats;
> > +
> > + skb = alloc_can_skb(priv->netdev, &cf);
> > + if (skb == NULL) {
>
> s/skb == NULL)/!skb/ ?
>
> > + stats->tx_dropped++;
> > + return;
> > + }
> > +
> > + cf->can_id = ((msg->u.rx_can.msg[0] & 0x1f) << 6) |
> > + (msg->u.rx_can.msg[1] & 0x3f);
> > + cf->can_dlc = get_can_dlc(msg->u.rx_can.msg[5]);
> > +
> > + if (msg->id == CMD_RX_EXT_MESSAGE) {
> > + cf->can_id <<= 18;
> > + cf->can_id |= ((msg->u.rx_can.msg[2] & 0x0f) << 14) |
> > + ((msg->u.rx_can.msg[3] & 0xff) << 6) |
> > + (msg->u.rx_can.msg[4] & 0x3f);
> > + cf->can_id |= CAN_EFF_FLAG;
> > + }
> > +
> > + if (msg->u.rx_can.flag & MSG_FLAG_REMOTE_FRAME) {
> > + cf->can_id |= CAN_RTR_FLAG;
> > + } else if (msg->u.rx_can.flag & (MSG_FLAG_ERROR_FRAME |
> > + MSG_FLAG_NERR)) {
> > + cf->can_id |= CAN_ERR_FLAG;
> > + cf->can_dlc = CAN_ERR_DLC;
> > + cf->data[1] = CAN_ERR_CRTL_UNSPEC;
>
> What is the meaning of such errors? A comment, netdev_err() or
> netdev_dbg() would be nice.
I never reached this error with the hardware I've... I don't know what's
the meaning of this flag... I will add a trace.
>
> > +
> > + stats->rx_errors++;
> > + } else if (msg->u.rx_can.flag & MSG_FLAG_OVERRUN) {
> > + cf->can_id = CAN_ERR_FLAG | CAN_ERR_CRTL;
> > + cf->can_dlc = CAN_ERR_DLC;
> > + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
> > +
> > + stats->rx_over_errors++;
> > + stats->rx_errors++;
> > + } else if (!msg->u.rx_can.flag) {
> > + memcpy(cf->data, &msg->u.rx_can.msg[6], cf->can_dlc);
> > + } else {
> > + kfree_skb(skb);
> > + return;
> > + }
> > +
> > + netif_rx(skb);
> > +
> > + stats->rx_packets++;
> > + stats->rx_bytes += cf->can_dlc;
> > +}
> > +
I'll fix all others small things you mentionned.
Thank you!
--
Olivier
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH v2] can: kvaser_usb: Add support for Kvaser CAN/USB devices
2012-08-08 6:14 ` Olivier Sobrie
@ 2012-08-08 8:25 ` Wolfgang Grandegger
[not found] ` <5022227F.60800-5Yr1BZd7O62+XT7JhA+gdA@public.gmane.org>
0 siblings, 1 reply; 43+ messages in thread
From: Wolfgang Grandegger @ 2012-08-08 8:25 UTC (permalink / raw)
To: Olivier Sobrie; +Cc: Marc Kleine-Budde, linux-can, linux-usb, netdev
Hi Oliver,
On 08/08/2012 08:14 AM, Olivier Sobrie wrote:
> Hi Wolfgang,
>
> On Tue, Aug 07, 2012 at 08:26:38AM +0200, Wolfgang Grandegger wrote:
>> On 08/06/2012 07:21 AM, Olivier Sobrie wrote:
>>> This driver provides support for several Kvaser CAN/USB devices.
>>> Such kind of devices supports up to three can network interfaces.
>>
>> s/can/CAN/
>>
>>> It has been tested with a Kvaser USB Leaf Light (one network interface)
>>> connected to a pch_can interface.
>>> The firmware version of the Kvaser device was 2.5.205.
>>>
>>> List of Kvaser devices supported by the driver:
>>> - Kvaser Leaf prototype (P010v2 and v3)
>>> - Kvaser Leaf Light (P010v3)
>>> - 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, Prototype
>>> - 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 OEM Mercury
>>> - Kvaser OEM Leaf
>>> - Kvaser USBcan R
>>
>> Impressive list! What CAN controllers are used inside the devices? SJA1000?
>
> I took this list from the Kvaser driver. However I only have a Kvaser
> Leaf Light device thus I'm not sure it will work with other ones.
> If you prefer I can only let Kvaser Leaf Light instead of the full list.
> In my device it looks to be a Renesas M16C controller.
OK. Checking the manual, if available, could help to understand how the
firmware handles bus errors and state changes.
>>> Signed-off-by: Olivier Sobrie <olivier@sobrie.be>
>>> ---
>>> Changes since v1:
>>> - added copyrights
>>> - kvaser_usb.h merged into kvaser.c
>>> - added kvaser_usb_get_endpoints to find eindpoints instead of
>>> hardcoding their address
>>> - some cleanup and comestic changes
>>> - fixed issues with errors handling
>>> - fixed restart-ms == 0 case
>>> - removed do_get_berr_counter method since the hardware doens't return
>>> good values for txerr and rxerr.
>>>
>>> If someone in the linux-usb mailing can review it, it would be nice.
>>>
>>> Concerning the errors, it behaves like that now:
>>>
>>> 1) Short-circuit CAN-H and CAN-L and restart-ms = 0
>>>
>>> t0: short-circuit + 'cansend can1 123#112233'
>>>
>>> can1 2000008C [8] 00 0C 90 00 00 00 00 00 ERRORFRAME
>>> controller-problem{rx-error-warning,tx-error-warning}
>>> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
>>> bus-error
>>> can1 2000008C [8] 00 30 90 00 00 00 00 00 ERRORFRAME
>>> controller-problem{rx-error-passive,tx-error-passive}
>>> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
>>> bus-error
>>> can1 200000C8 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
>>> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
>>> bus-off
>>> bus-error
>>>
>>> t1: remove short-circuit + 'ip link set can1 type can restart'
>>>
>>> can1 20000100 [8] 00 00 00 00 00 00 00 00 ERRORFRAME
>>> restarted-after-bus-off
>>> can1 20000004 [8] 00 0C 00 00 00 00 00 00 ERRORFRAME
>>> controller-problem{rx-error-warning,tx-error-warning}
>>
>> Why do we get the last error message? Maybe the firmware does it that
>> way (going down passive->warning->active).
>
> It goes in that order: warning -> passive -> bus off -> warning
> -> passive -> ...
Just for curiosity? You don't see back to "error active"?
>>> 2) Short-circuit CAN-H and CAN-L and restart-ms = 100
>>>
>>> t0: short-circuit + cansend can1 123#112233
>>>
>>> can1 2000008C [8] 00 0C 90 00 00 00 00 00 ERRORFRAME
>>> controller-problem{rx-error-warning,tx-error-warning}
>>> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
>>> bus-error
>>> can1 2000008C [8] 00 30 90 00 00 00 00 00 ERRORFRAME
>>> controller-problem{rx-error-passive,tx-error-passive}
>>> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
>>> bus-error
>>> can1 200000C8 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
>>> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
>>> bus-off
>>> bus-error
>>> can1 2000018C [8] 00 0C 90 00 00 00 00 00 ERRORFRAME
>>> controller-problem{rx-error-warning,tx-error-warning}
>>> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
>>> bus-error
>>> restarted-after-bus-off
>>> can1 2000008C [8] 00 0C 90 00 00 00 00 00 ERRORFRAME
>>> controller-problem{rx-error-warning,tx-error-warning}
>>> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
>>> bus-error
>>> can1 2000008C [8] 00 30 90 00 00 00 00 00 ERRORFRAME
>>> controller-problem{rx-error-passive,tx-error-passive}
>>> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
>>> bus-error
>>> can1 200000C8 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
>>> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
>>> bus-off
>>> bus-error
>>> ...
>>>
>>> can1 2000018C [8] 00 0C 90 00 00 00 00 00 ERRORFRAME
>>> controller-problem{rx-error-warning,tx-error-warning}
>>> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
>>> bus-error
>>> restarted-after-bus-off
>>> can1 2000008C [8] 00 0C 90 00 00 00 00 00 ERRORFRAME
>>> controller-problem{rx-error-warning,tx-error-warning}
>>> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
>>> bus-error
>>> can1 2000008C [8] 00 30 90 00 00 00 00 00 ERRORFRAME
>>> controller-problem{rx-error-passive,tx-error-passive}
>>> protocol-violation{{tx-recessive-bit-error,error-on-tx}{}}
>>> bus-error
>>>
>>> t1: remove short-circuit
>>>
>>> can1 123 [3] 11 22 33
>>> can1 20000008 [8] 00 00 40 00 00 00 00 00 ERRORFRAME
>>> protocol-violation{{back-to-error-active}{}}
>>
>> The order is still inverted but likely the firmware is doing it that way.
>
> Indeed the firmware does it that way: it sends the acknwledge of the
> frame beofre the state change. I can avoid that behavior by checking the
> state in the acknowledge frame and send the restart frame if the bus was
> off.
Well, if the firmware does it wrong, I would not really care. Also,
could you use timestamping to see if they come close together.
>>> 3) CAN-H and CAN-L disconnected
>>>
>>> t0: CAN-H and CAN-L disconnected + cansend can1 123#112233
>>>
>>> can1 20000004 [8] 00 30 00 00 00 00 00 00 ERRORFRAME
>>> controller-problem{rx-error-passive,tx-error-passive}
>>> can1 2000008C [8] 00 30 80 1B 00 00 00 00 ERRORFRAME
>>> controller-problem{rx-error-passive,tx-error-passive}
>>> protocol-violation{{error-on-tx}{acknowledge-delimiter}}
>>> bus-error
>>>
>>> t1: CAN-H and CAN-L reconnected
>>>
>>> can1 123 [3] 11 22 33
>>> can1 20000004 [8] 00 30 00 00 00 00 00 00 ERRORFRAME
>>> controller-problem{rx-error-passive,tx-error-passive}
>>
>> Why do we get an error-passive message? Now I will have a closer look to
>> the code...
>
> The firmware sends a CMD_CAN_ERROR_EVENT with the passive bit set...
Maybe the order is again inverted. Do they come at the same time
(visiable with candump -td).
>>> +static void kvaser_usb_rx_error(const struct kvaser_usb *dev,
>>> + const struct kvaser_msg *msg)
>>> +{
>>> + struct can_frame *cf;
>>> + struct sk_buff *skb;
>>> + struct net_device_stats *stats;
>>> + struct kvaser_usb_net_priv *priv;
>>> + unsigned int new_state;
>>> + u8 channel, status;
>>> +
>>> + if (msg->id == CMD_CAN_ERROR_EVENT) {
>>> + channel = msg->u.error_event.channel;
>>> + status = msg->u.error_event.status;
>>> + } else {
>>> + channel = msg->u.chip_state_event.channel;
>>> + status = msg->u.chip_state_event.status;
>>> + }
>>> +
>>> + if (channel >= dev->nchannels) {
>>> + dev_err(dev->udev->dev.parent,
>>> + "Invalid channel number (%d)\n", channel);
>>> + return;
>>> + }
>>> +
>>> + priv = dev->nets[channel];
>>> + stats = &priv->netdev->stats;
>>> +
>>> + if (status & M16C_STATE_BUS_RESET) {
>>> + kvaser_usb_unlink_tx_urbs(priv);
>>> + return;
>>> + }
>>> + skb = alloc_can_err_skb(priv->netdev, &cf);
>>> + if (!skb) {
>>> + stats->rx_dropped++;
>>> + return;
>>
>> Cleanup? kvaser_usb_unlink_tx_urbs()?
>
> If I get the M16C_STATE_BUS_RESET I'll not receive the ack frames anymore.
> I need to set the context->echo_index back to MAX_TX_URBS to not loose tx
> urbs.
> By the way I think a can_free_echo_skb() is missing...
>
>>
>>> + }
>>> +
>>> + if (status & M16C_STATE_BUS_OFF) {
>>> + cf->can_id |= CAN_ERR_BUSOFF;
>>> +
>>> + if (!priv->can.restart_ms)
>>> + kvaser_usb_simple_msg_async(priv, CMD_STOP_CHIP);
>>> +
>>> + if (!priv->can.state != CAN_ERR_BUSOFF) {
>>> + priv->can.can_stats.bus_off++;
>>> + netif_carrier_off(priv->netdev);
>>> + }
>>> +
>>> + new_state = CAN_STATE_BUS_OFF;
>>> + } else if (status & M16C_STATE_BUS_PASSIVE) {
>>> + cf->can_id |= CAN_ERR_CRTL;
>>> + cf->data[1] = CAN_ERR_CRTL_TX_PASSIVE |
>>> + CAN_ERR_CRTL_RX_PASSIVE;
>>
>> State changes should only be report when the state really changes.
>> Therefore it should go under the if block below.
>
> Ok. Is it possible to get such sequence:
> can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 10 90 00 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
> can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
>
> 2 questions:
> 1) If the bus is still in CAN_ERR_CRTL_RX_PASSIVE after the second frame,
> shouldn't we let the corresponding bit set, 0x10?
No, see below.
> 2) Can we send multiple times same frame wih same error bits set?
Yes, because that's what the hardware reports.
>>
>>> + if (priv->can.state != CAN_STATE_ERROR_PASSIVE)
>>> + priv->can.can_stats.error_passive++;
>>> +
>>> + new_state = CAN_STATE_ERROR_PASSIVE;
>>> + } else if (status & M16C_STATE_BUS_ERROR) {
>>
>> Hm, strange, a bus error is not a state change. You use here if...else
>> if... Isn't it possible that more than one bit is set.
>
> Indeed it is possible to have multiple bits set.
> e.g. M16C_STATE_BUS_PASSIVE + M16C_STATE_BUS_ERROR or M16C_STATE_BUS_OFF + M16C_STATE_BUS_ERROR.
OK, that's the normal behaviour. Obviously they send bus errors together
with the *actual* state. The hardware does usuallly report bus errors
frequently while the error condition persists.
> What error should I report in case of M16C_STATE_BUS_ERROR?
To make that clear, I have added an (old) output from the SJA1000, which
is the defacto reference. Bus error reporting is enabled and no cable is
connected. Watch the TX error count increasing and how the state changes:
$ ./candump -e 0xffff any
can0 20000088 [8] 00 00 80 19 00 08 00 00 ERRORFRAME
\ \ \-- ACK slot.
\ \-- error occured on transmission
\-- Bus-error | Protocol violations (data[2], data[3]).
can0 20000088 [8] 00 00 80 19 00 10 00 00 ERRORFRAME
can0 20000088 [8] 00 00 80 19 00 18 00 00 ERRORFRAME
can0 20000088 [8] 00 00 80 19 00 20 00 00 ERRORFRAME
can0 20000088 [8] 00 00 80 19 00 28 00 00 ERRORFRAME
can0 20000088 [8] 00 00 80 19 00 30 00 00 ERRORFRAME
can0 20000088 [8] 00 00 80 19 00 38 00 00 ERRORFRAME
can0 20000088 [8] 00 00 80 19 00 40 00 00 ERRORFRAME
can0 20000088 [8] 00 00 80 19 00 48 00 00 ERRORFRAME
can0 20000088 [8] 00 00 80 19 00 50 00 00 ERRORFRAME
can0 20000088 [8] 00 00 80 19 00 58 00 00 ERRORFRAME
can0 2000008C [8] 00 08 80 19 00 60 00 00 ERRORFRAME
\ \-- reached warning level for TX errors
\-- | Controller problems (see data[1]).
can0 20000088 [8] 00 00 80 19 00 68 00 00 ERRORFRAME
can0 20000088 [8] 00 00 80 19 00 70 00 00 ERRORFRAME
can0 20000088 [8] 00 00 80 19 00 78 00 00 ERRORFRAME
can0 2000008C [8] 00 20 80 19 00 80 00 00 ERRORFRAME
\ \-- reached passive level for TX errors
\-- | Controller problems (see data[1]).
can0 20000088 [8] 00 00 80 19 00 80 00 00 ERRORFRAME
\ \-- RXerror count
\-- TXerror count
can0 20000088 [8] 00 00 80 19 00 80 00 00 ERRORFRAME
...
can0 20000088 [8] 00 00 80 19 00 80 00 00 ERRORFRAME
>
>>
>>> + cf->can_id |= CAN_ERR_CRTL;
>>> + cf->data[1] = CAN_ERR_CRTL_TX_WARNING |
>>> + CAN_ERR_CRTL_RX_WARNING;
>>
>> See above.
>>
>>> + if (priv->can.state != CAN_STATE_ERROR_WARNING)
>>> + priv->can.can_stats.error_warning++;
>>> +
>>> + new_state = CAN_STATE_ERROR_WARNING;
>>> + } else {
>>> + cf->can_id |= CAN_ERR_PROT;
>>> + cf->data[2] = CAN_ERR_PROT_ACTIVE;
>>> +
>>> + new_state = CAN_STATE_ERROR_ACTIVE;
>>> + }
>>> +
>>> + if (priv->can.restart_ms &&
>>> + (priv->can.state >= CAN_STATE_BUS_OFF) &&
>>> + (new_state < CAN_STATE_BUS_OFF)) {
>>> + cf->can_id |= CAN_ERR_RESTARTED;
>>> + priv->can.can_stats.restarts++;
>>> + netif_carrier_on(priv->netdev);
>>> + }
>>> +
>>> + if (msg->id == CMD_CAN_ERROR_EVENT) {
>>> + u8 error_factor = msg->u.error_event.error_factor;
>>> +
>>> + priv->can.can_stats.bus_error++;
>>> + stats->rx_errors++;
>>> +
>>> + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
>>> +
>>> + if (error_factor & M16C_EF_ACKE)
>>> + cf->data[3] |= (CAN_ERR_PROT_LOC_ACK |
>>> + CAN_ERR_PROT_LOC_ACK_DEL);
>>> + if (error_factor & M16C_EF_CRCE)
>>> + cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ |
>>> + CAN_ERR_PROT_LOC_CRC_DEL);
>>> + if (error_factor & M16C_EF_FORME)
>>> + cf->data[2] |= CAN_ERR_PROT_FORM;
>>> + if (error_factor & M16C_EF_STFE)
>>> + cf->data[2] |= CAN_ERR_PROT_STUFF;
>>> + if (error_factor & M16C_EF_BITE0)
>>> + cf->data[2] |= CAN_ERR_PROT_BIT0;
>>> + if (error_factor & M16C_EF_BITE1)
>>> + cf->data[2] |= CAN_ERR_PROT_BIT1;
>>> + if (error_factor & M16C_EF_TRE)
>>> + cf->data[2] |= CAN_ERR_PROT_TX;
>>> + }
>>> +
>>> + priv->can.state = new_state;
>>> +
>>> + if (!memcmp(cf, &priv->cf_err_old, sizeof(*cf))) {
>>> + kfree_skb(skb);
>>> + return;
>>> + }
>>
>> Hm, the firmware seems not to clear error conditions? Anyway, state
>> change and error reporting is magic on many CAN controllers. Just the
>> SJA1000 is doing it nicely.
>
> I added it to prevent sending two times the same error. It happens that
> the firmware sends multiple times the same error message.
Can then be removed, I think, see above.
>>
>>> + netif_rx(skb);
>>> +
>>> + priv->cf_err_old = *cf;
>>> +
>>> + stats->rx_packets++;
>>> + stats->rx_bytes += cf->can_dlc;
>>> +}
>>> +
>>> +static void kvaser_usb_rx_can_msg(const struct kvaser_usb *dev,
>>> + const struct kvaser_msg *msg)
>>> +{
>>> + struct kvaser_usb_net_priv *priv;
>>> + struct can_frame *cf;
>>> + struct sk_buff *skb;
>>> + struct net_device_stats *stats;
>>> + u8 channel = msg->u.rx_can.channel;
>>> +
>>> + if (channel >= dev->nchannels) {
>>> + dev_err(dev->udev->dev.parent,
>>> + "Invalid channel number (%d)\n", channel);
>>> + return;
>>> + }
>>> +
>>> + priv = dev->nets[channel];
>>> + stats = &priv->netdev->stats;
>>> +
>>> + skb = alloc_can_skb(priv->netdev, &cf);
>>> + if (skb == NULL) {
>>
>> s/skb == NULL)/!skb/ ?
>>
>>> + stats->tx_dropped++;
>>> + return;
>>> + }
>>> +
>>> + cf->can_id = ((msg->u.rx_can.msg[0] & 0x1f) << 6) |
>>> + (msg->u.rx_can.msg[1] & 0x3f);
>>> + cf->can_dlc = get_can_dlc(msg->u.rx_can.msg[5]);
>>> +
>>> + if (msg->id == CMD_RX_EXT_MESSAGE) {
>>> + cf->can_id <<= 18;
>>> + cf->can_id |= ((msg->u.rx_can.msg[2] & 0x0f) << 14) |
>>> + ((msg->u.rx_can.msg[3] & 0xff) << 6) |
>>> + (msg->u.rx_can.msg[4] & 0x3f);
>>> + cf->can_id |= CAN_EFF_FLAG;
>>> + }
>>> +
>>> + if (msg->u.rx_can.flag & MSG_FLAG_REMOTE_FRAME) {
>>> + cf->can_id |= CAN_RTR_FLAG;
>>> + } else if (msg->u.rx_can.flag & (MSG_FLAG_ERROR_FRAME |
>>> + MSG_FLAG_NERR)) {
>>> + cf->can_id |= CAN_ERR_FLAG;
>>> + cf->can_dlc = CAN_ERR_DLC;
>>> + cf->data[1] = CAN_ERR_CRTL_UNSPEC;
>>
>> What is the meaning of such errors? A comment, netdev_err() or
>> netdev_dbg() would be nice.
>
> I never reached this error with the hardware I've... I don't know what's
> the meaning of this flag... I will add a trace.
Then add a netdev_err().
Wolfgang.
^ permalink raw reply [flat|nested] 43+ messages in thread
* [PATCH v3] can: kvaser_usb: Add support for Kvaser CAN/USB devices
2012-07-30 5:32 [PATCH] can: kvaser_usb: Add support for Kvaser CAN/USB devices Olivier Sobrie
2012-07-30 11:11 ` Marc Kleine-Budde
2012-08-06 5:21 ` [PATCH v2] " Olivier Sobrie
@ 2012-08-13 13:51 ` Olivier Sobrie
2012-09-20 5:06 ` [PATCH v4] " Olivier Sobrie
` (2 subsequent siblings)
5 siblings, 0 replies; 43+ messages in thread
From: Olivier Sobrie @ 2012-08-13 13:51 UTC (permalink / raw)
To: Wolfgang Grandegger, Marc Kleine-Budde, linux-can, linux-usb
Cc: netdev, Olivier Sobrie
This driver provides support for several Kvaser CAN/USB devices.
Such kind of devices supports up to three CAN network interfaces.
It has been tested with a Kvaser USB Leaf Light (one network interface)
connected to a pch_can interface.
The firmware version of the Kvaser device was 2.5.205.
List of Kvaser devices supported by the driver:
- Kvaser Leaf prototype (P010v2 and v3)
- Kvaser Leaf Light (P010v3)
- 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, Prototype
- 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 OEM Mercury
- Kvaser OEM Leaf
- Kvaser USBcan R
Signed-off-by: Olivier Sobrie <olivier@sobrie.be>
---
Changes since v2:
- update of error handling
- add method do_get_berr_counter if hardware support it
- fix race condition in probe function
- ...
Here is the behavior in case of errors:
Case 1: restart-ms 0 + short-circuit
t0: short-circuit + cansend can1 123@1122
(016.731173) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000009) can1 20000088 [8] 00 00 D0 00 00 00 00 00 ERRORFRAME
(000.000010) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000005) can1 20000088 [8] 00 00 D0 00 00 00 00 00 ERRORFRAME
(000.000006) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000004) can1 20000088 [8] 00 00 D0 00 00 00 00 00 ERRORFRAME
(000.000007) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000007) can1 20000088 [8] 00 00 D0 00 00 00 00 00 ERRORFRAME
(000.000007) can1 2000008C [8] 00 30 90 00 00 00 00 00 ERRORFRAME
(000.001062) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000011) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000008) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000007) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000978) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000007) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000009) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000008) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.001108) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000007) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000007) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000006) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000007) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.001082) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000046) can1 200000C8 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
t1: ip link set can1 type can restart:
(125.211773) can1 20000100 [8] 00 00 00 00 00 00 00 00 ERRORFRAME
(000.020207) can1 20000088 [8] 00 00 40 00 00 00 00 00 ERRORFRAME
Case 2: short-circuit + restart-ms > 0
t0: short-circuit + cansend can1 123@1122
(006.079601) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000091) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000083) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000083) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000082) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000086) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000083) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000082) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000083) can1 2000008C [8] 00 30 90 00 00 00 00 00 ERRORFRAME
(000.000352) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000103) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000106) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000105) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000808) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000125) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000110) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000108) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000106) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000110) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000560) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000104) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000104) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000106) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000123) can1 200000C8 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.010835) can1 20000188 [8] 00 00 D0 00 00 00 00 00 ERRORFRAME
(000.000090) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000082) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
...
(000.000084) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000082) can1 2000008C [8] 00 30 90 00 00 00 00 00 ERRORFRAME
(000.000431) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
...
(000.000105) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000112) can1 200000C8 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.010842) can1 20000188 [8] 00 00 D0 00 00 00 00 00 ERRORFRAME
...
(000.001118) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000086) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000082) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000084) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000084) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000083) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000082) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000085) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000082) can1 2000008C [8] 00 30 90 00 00 00 00 00 ERRORFRAME
(000.000082) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000349) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000087) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000081) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000082) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000903) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000110) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000093) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000083) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000082) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000755) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000112) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000093) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000084) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000082) can1 200000C8 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
t1: short-circuit removed
(000.011273) can1 20000100 [8] 00 00 00 00 00 00 00 00 ERRORFRAME
(000.000006) can1 123 [2] 11 22
Case 3: CAN-H and CAN-L disconnected
t0: CAN-H and CAN-L disconnect + cansend can1 123#1122
(524.620978) can1 20000088 [8] 00 00 80 19 00 00 00 00 ERRORFRAME
(000.000978) can1 20000088 [8] 00 00 80 19 00 00 00 00 ERRORFRAME
(000.001126) can1 20000088 [8] 00 00 80 19 00 00 00 00 ERRORFRAME
(000.000007) can1 20000088 [8] 00 00 80 19 00 00 00 00 ERRORFRAME
(000.000996) can1 20000088 [8] 00 00 80 19 00 00 00 00 ERRORFRAME
(000.000006) can1 20000088 [8] 00 00 80 19 00 00 00 00 ERRORFRAME
(000.001134) can1 20000088 [8] 00 00 80 19 00 00 00 00 ERRORFRAME
(000.000006) can1 20000088 [8] 00 00 80 19 00 00 00 00 ERRORFRAME
(000.001775) can1 20000088 [8] 00 00 80 19 00 00 00 00 ERRORFRAME
(000.000006) can1 20000088 [8] 00 00 80 19 00 00 00 00 ERRORFRAME
(000.000310) can1 20000088 [8] 00 00 80 19 00 00 00 00 ERRORFRAME
(000.000005) can1 20000088 [8] 00 00 80 19 00 00 00 00 ERRORFRAME
(000.001126) can1 20000088 [8] 00 00 80 19 00 00 00 00 ERRORFRAME
(000.000005) can1 20000088 [8] 00 00 80 19 00 00 00 00 ERRORFRAME
(000.001176) can1 20000088 [8] 00 00 80 19 00 00 00 00 ERRORFRAME
(000.000006) can1 2000008C [8] 00 30 80 19 00 00 00 00 ERRORFRAME
(000.000949) can1 20000088 [8] 00 00 80 19 00 00 00 00 ERRORFRAME
...
t1: CAN-H and CAN-L connected:
(000.000005) can1 20000088 [8] 00 00 80 19 00 00 00 00 ERRORFRAME
(004.270502) can1 123 [2] 11 22
(002.242773) can1 20000088 [8] 00 00 40 00 00 00 00 00 ERRORFRAME
Remarks:
- With my device the txerr and rxerr always stay equals to 0
because the hardware doesn't report it... It passes from active state
to passive and then off.
With other hardware, i.e. other than Leaf Light, the txerr and rxerr
are incremented (following Kvaser support)
- With case 2, sometimes after a bus off event, the hardware doesn't
report that it's in active state again... This is why when I get an
acknowledge frame I deduce it's active again...
Kind regards,
Olivier
drivers/net/can/usb/Kconfig | 33 +
drivers/net/can/usb/Makefile | 1 +
drivers/net/can/usb/kvaser_usb.c | 1525 ++++++++++++++++++++++++++++++++++++++
3 files changed, 1559 insertions(+)
create mode 100644 drivers/net/can/usb/kvaser_usb.c
diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig
index 0a68768..578955f 100644
--- a/drivers/net/can/usb/Kconfig
+++ b/drivers/net/can/usb/Kconfig
@@ -13,6 +13,39 @@ config CAN_ESD_USB2
This driver supports the CAN-USB/2 interface
from esd electronic system design gmbh (http://www.esd.eu).
+config CAN_KVASER_USB
+ tristate "Kvaser CAN/USB interface"
+ ---help---
+ This driver adds support for Kvaser CAN/USB devices like Kvaser
+ Leaf Light.
+
+ The driver gives support for the following devices:
+ - Kvaser Leaf prototype (P010v2 and v3)
+ - Kvaser Leaf Light (P010v3)
+ - 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, Prototype
+ - 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 OEM Mercury
+ - Kvaser OEM Leaf
+ - Kvaser USBcan R
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called kvaser_usb.
+
config CAN_PEAK_USB
tristate "PEAK PCAN-USB/USB Pro interfaces"
---help---
diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile
index da6d1d3..80a2ee4 100644
--- a/drivers/net/can/usb/Makefile
+++ b/drivers/net/can/usb/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o
obj-$(CONFIG_CAN_ESD_USB2) += esd_usb2.o
+obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o
obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/
ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c
new file mode 100644
index 0000000..938f188
--- /dev/null
+++ b/drivers/net/can/usb/kvaser_usb.c
@@ -0,0 +1,1525 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * Parts of this driver are based on the following:
+ * - Kvaser linux leaf driver (version 4.78)
+ * - CAN driver for esd CAN-USB/2
+ *
+ * Copyright (C) 2002-2006 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>
+ */
+
+#include <linux/init.h>
+#include <linux/completion.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>
+
+#define MAX_TX_URBS 16
+#define MAX_RX_URBS 4
+#define START_TIMEOUT 1000 /* msecs */
+#define STOP_TIMEOUT 1000 /* msecs */
+#define USB_SEND_TIMEOUT 1000 /* msecs */
+#define USB_RECV_TIMEOUT 1000 /* msecs */
+#define RX_BUFFER_SIZE 3072
+#define CAN_USB_CLOCK 8000000
+#define MAX_NET_DEVICES 3
+
+/* Kvaser USB devices */
+#define KVASER_VENDOR_ID 0x0bfd
+#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
+
+/* USB devices features */
+#define KVASER_HAS_SILENT_MODE BIT(0)
+#define KVASER_HAS_TXRX_ERRORS BIT(1)
+
+/* Message header size */
+#define MSG_HEADER_LEN 2
+
+/* 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 */
+#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)
+
+/* Can msg 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_GET_CTRL_MODE 22
+#define CMD_GET_CTRL_MODE_REPLY 23
+#define CMD_RESET_CHIP 24
+#define CMD_RESET_CARD 25
+#define CMD_START_CHIP 26
+#define CMD_START_CHIP_REPLY 27
+#define CMD_STOP_CHIP 28
+#define CMD_STOP_CHIP_REPLY 29
+#define CMD_GET_CARD_INFO2 32
+#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_RESET_ERROR_COUNTER 49
+#define CMD_TX_ACKNOWLEDGE 50
+#define CMD_CAN_ERROR_EVENT 51
+#define CMD_USB_THROTTLE 77
+
+/* 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)
+
+/* bittiming parameters */
+#define KVASER_USB_TSEG1_MIN 1
+#define KVASER_USB_TSEG1_MAX 16
+#define KVASER_USB_TSEG2_MIN 1
+#define KVASER_USB_TSEG2_MAX 8
+#define KVASER_USB_SJW_MAX 4
+#define KVASER_USB_BRP_MIN 1
+#define KVASER_USB_BRP_MAX 64
+#define KVASER_USB_BRP_INC 1
+
+/* 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
+
+struct kvaser_msg_simple {
+ u8 tid;
+ u8 channel;
+} __packed;
+
+struct kvaser_msg_cardinfo {
+ u8 tid;
+ u8 nchannels;
+ __le32 serial_number;
+ __le32 padding;
+ __le32 clock_resolution;
+ __le32 mfgdate;
+ u8 ean[8];
+ u8 hw_revision;
+ u8 usb_hs_mode;
+ __le16 padding2;
+} __packed;
+
+struct kvaser_msg_cardinfo2 {
+ u8 tid;
+ u8 channel;
+ u8 pcb_id[24];
+ __le32 oem_unlock_code;
+} __packed;
+
+struct kvaser_msg_softinfo {
+ u8 tid;
+ u8 channel;
+ __le32 sw_options;
+ __le32 fw_version;
+ __le16 max_outstanding_tx;
+ __le16 padding[9];
+} __packed;
+
+struct kvaser_msg_busparams {
+ u8 tid;
+ u8 channel;
+ __le32 bitrate;
+ u8 tseg1;
+ u8 tseg2;
+ u8 sjw;
+ u8 no_samp;
+} __packed;
+
+struct kvaser_msg_tx_can {
+ u8 channel;
+ u8 tid;
+ u8 msg[14];
+ u8 padding;
+ u8 flags;
+} __packed;
+
+struct kvaser_msg_rx_can {
+ u8 channel;
+ u8 flag;
+ __le16 time[3];
+ u8 msg[14];
+} __packed;
+
+struct kvaser_msg_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 kvaser_msg_tx_acknowledge {
+ u8 channel;
+ u8 tid;
+ __le16 time[3];
+ u8 flags;
+ u8 time_offset;
+} __packed;
+
+struct kvaser_msg_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 kvaser_msg_ctrl_mode {
+ u8 tid;
+ u8 channel;
+ u8 ctrl_mode;
+ u8 padding[3];
+} __packed;
+
+struct kvaser_msg_flush_queue {
+ u8 tid;
+ u8 channel;
+ u8 flags;
+ u8 padding[3];
+} __packed;
+
+struct kvaser_msg {
+ u8 len;
+ u8 id;
+ union {
+ struct kvaser_msg_simple simple;
+ struct kvaser_msg_cardinfo cardinfo;
+ struct kvaser_msg_cardinfo2 cardinfo2;
+ struct kvaser_msg_softinfo softinfo;
+ struct kvaser_msg_busparams busparams;
+ struct kvaser_msg_tx_can tx_can;
+ struct kvaser_msg_rx_can rx_can;
+ struct kvaser_msg_chip_state_event chip_state_event;
+ struct kvaser_msg_tx_acknowledge tx_acknowledge;
+ struct kvaser_msg_error_event error_event;
+ struct kvaser_msg_ctrl_mode ctrl_mode;
+ struct kvaser_msg_flush_queue flush_queue;
+ } u;
+} __packed;
+
+struct kvaser_usb_tx_urb_context {
+ struct kvaser_usb_net_priv *priv;
+ u32 echo_index;
+ int dlc;
+};
+
+struct kvaser_usb {
+ struct usb_device *udev;
+ struct kvaser_usb_net_priv *nets[MAX_NET_DEVICES];
+
+ struct usb_endpoint_descriptor *bulk_in, *bulk_out;
+ struct usb_anchor rx_submitted;
+
+ u32 fw_version;
+ unsigned int nchannels;
+
+ bool rxinitdone;
+ void *rxbuf[MAX_RX_URBS];
+ dma_addr_t rxbuf_dma[MAX_RX_URBS];
+};
+
+struct kvaser_usb_net_priv {
+ struct can_priv can;
+
+ atomic_t active_tx_urbs;
+ struct usb_anchor tx_submitted;
+ struct kvaser_usb_tx_urb_context tx_contexts[MAX_TX_URBS];
+
+ struct completion start_comp, stop_comp;
+
+ struct kvaser_usb *dev;
+ struct net_device *netdev;
+ int channel;
+
+ struct can_berr_counter bec;
+};
+
+static struct usb_device_id kvaser_usb_table[] = {
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_DEVEL_PRODUCT_ID) },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_PRODUCT_ID) },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_SWC_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LIN_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_LS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_SWC_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_DEVEL_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSHS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_UPRO_HSHS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_GI_PRODUCT_ID) },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_OBDII_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSLS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_CH_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_BLACKBIRD_SPRO_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_MERCURY_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_LEAF_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_CAN_R_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, kvaser_usb_table);
+
+static inline int kvaser_usb_send_msg(const struct kvaser_usb *dev,
+ struct kvaser_msg *msg)
+{
+ int actual_len;
+
+ return usb_bulk_msg(dev->udev,
+ usb_sndbulkpipe(dev->udev,
+ dev->bulk_out->bEndpointAddress),
+ msg, msg->len, &actual_len,
+ USB_SEND_TIMEOUT);
+}
+
+static int kvaser_usb_wait_msg(const struct kvaser_usb *dev, u8 id,
+ struct kvaser_msg *msg)
+{
+ struct kvaser_msg *tmp;
+ void *buf;
+ int actual_len;
+ int err;
+ int pos = 0;
+
+ buf = kzalloc(RX_BUFFER_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ err = usb_bulk_msg(dev->udev,
+ usb_rcvbulkpipe(dev->udev,
+ dev->bulk_in->bEndpointAddress),
+ buf, RX_BUFFER_SIZE, &actual_len,
+ USB_RECV_TIMEOUT);
+ if (err < 0)
+ goto end;
+
+ while (pos <= actual_len - MSG_HEADER_LEN) {
+ tmp = buf + pos;
+
+ if (!tmp->len)
+ break;
+
+ if (pos + tmp->len > actual_len) {
+ dev_err(dev->udev->dev.parent, "Format error\n");
+ break;
+ }
+
+ if (tmp->id == id) {
+ memcpy(msg, tmp, tmp->len);
+ goto end;
+ }
+
+ pos += tmp->len;
+ }
+
+ err = -EINVAL;
+
+end:
+ kfree(buf);
+
+ return err;
+}
+
+static int kvaser_usb_send_simple_msg(const struct kvaser_usb *dev,
+ u8 msg_id, int channel)
+{
+ struct kvaser_msg msg = {
+ .len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple),
+ .id = msg_id,
+ .u.simple.channel = channel,
+ .u.simple.tid = 0xff,
+ };
+
+ return kvaser_usb_send_msg(dev, &msg);
+}
+
+static int kvaser_usb_get_software_info(struct kvaser_usb *dev)
+{
+ struct kvaser_msg msg;
+ int err;
+
+ err = kvaser_usb_send_simple_msg(dev, CMD_GET_SOFTWARE_INFO, 0);
+ if (err)
+ return err;
+
+ err = kvaser_usb_wait_msg(dev, CMD_GET_SOFTWARE_INFO_REPLY, &msg);
+ if (err)
+ return err;
+
+ dev->fw_version = le32_to_cpu(msg.u.softinfo.fw_version);
+
+ return 0;
+}
+
+static int kvaser_usb_get_card_info(struct kvaser_usb *dev)
+{
+ struct kvaser_msg msg;
+ int err;
+
+ err = kvaser_usb_send_simple_msg(dev, CMD_GET_CARD_INFO, 0);
+ if (err)
+ return err;
+
+ err = kvaser_usb_wait_msg(dev, CMD_GET_CARD_INFO_REPLY, &msg);
+ if (err)
+ return err;
+
+ dev->nchannels = msg.u.cardinfo.nchannels;
+
+ return 0;
+}
+
+static void kvaser_usb_tx_acknowledge(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ struct net_device_stats *stats;
+ struct kvaser_usb_tx_urb_context *context;
+ struct kvaser_usb_net_priv *priv;
+ struct sk_buff *skb;
+ struct can_frame *cf;
+ u8 channel = msg->u.tx_acknowledge.channel;
+ u8 tid = msg->u.tx_acknowledge.tid;
+
+ if (channel >= dev->nchannels) {
+ dev_err(dev->udev->dev.parent,
+ "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 % 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)) {
+ skb = alloc_can_err_skb(priv->netdev, &cf);
+ if (skb) {
+ cf->can_id |= CAN_ERR_RESTARTED;
+ netif_rx(skb);
+
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+ } 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;
+ can_get_echo_skb(priv->netdev, context->echo_index);
+
+ context->echo_index = MAX_TX_URBS;
+ atomic_dec(&priv->active_tx_urbs);
+
+ netif_wake_queue(priv->netdev);
+}
+
+static void kvaser_usb_simple_msg_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);
+}
+
+static int kvaser_usb_simple_msg_async(struct kvaser_usb_net_priv *priv,
+ u8 msg_id)
+{
+ struct kvaser_usb *dev = priv->dev;
+ struct net_device *netdev = priv->netdev;
+ struct kvaser_msg *msg;
+ struct urb *urb;
+ void *buf;
+ int err;
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb) {
+ netdev_err(netdev, "No memory left for URBs\n");
+ return -ENOMEM;
+ }
+
+ buf = kmalloc(sizeof(struct kvaser_msg), GFP_ATOMIC);
+ if (!buf) {
+ netdev_err(netdev, "No memory left for USB buffer\n");
+ return -ENOMEM;
+ }
+
+ msg = (struct kvaser_msg *)buf;
+ msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple);
+ msg->id = msg_id;
+ msg->u.simple.channel = priv->channel;
+
+ usb_fill_bulk_urb(urb, dev->udev,
+ usb_sndbulkpipe(dev->udev,
+ dev->bulk_out->bEndpointAddress),
+ buf, msg->len,
+ kvaser_usb_simple_msg_callback, priv);
+ 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);
+ kfree(buf);
+ return err;
+ }
+
+ usb_free_urb(urb);
+
+ return 0;
+}
+
+static void kvaser_usb_unlink_tx_urbs(struct kvaser_usb_net_priv *priv)
+{
+ int 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;
+}
+
+static void kvaser_usb_rx_error(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ struct net_device_stats *stats;
+ struct kvaser_usb_net_priv *priv;
+ unsigned int new_state;
+ u8 channel, status, txerr, rxerr;
+
+ if (msg->id == CMD_CAN_ERROR_EVENT) {
+ channel = msg->u.error_event.channel;
+ status = msg->u.error_event.status;
+ txerr = msg->u.error_event.tx_errors_count;
+ rxerr = msg->u.error_event.rx_errors_count;
+ } else {
+ channel = msg->u.chip_state_event.channel;
+ status = msg->u.chip_state_event.status;
+ txerr = msg->u.chip_state_event.tx_errors_count;
+ rxerr = msg->u.chip_state_event.rx_errors_count;
+ }
+
+ if (channel >= dev->nchannels) {
+ dev_err(dev->udev->dev.parent,
+ "Invalid channel number (%d)\n", channel);
+ return;
+ }
+
+ priv = dev->nets[channel];
+ stats = &priv->netdev->stats;
+
+ if (status & M16C_STATE_BUS_RESET) {
+ kvaser_usb_unlink_tx_urbs(priv);
+ return;
+ }
+
+ skb = alloc_can_err_skb(priv->netdev, &cf);
+ if (!skb) {
+ stats->rx_dropped++;
+ return;
+ }
+
+ cf->can_id |= CAN_ERR_BUSERROR;
+
+ new_state = priv->can.state;
+
+ netdev_dbg(priv->netdev, "Error status: 0x%02x\n", status);
+
+ if (status & M16C_STATE_BUS_OFF) {
+ cf->can_id |= CAN_ERR_BUSOFF;
+
+ priv->can.can_stats.bus_off++;
+ if (!priv->can.restart_ms)
+ kvaser_usb_simple_msg_async(priv, CMD_STOP_CHIP);
+
+ netif_carrier_off(priv->netdev);
+
+ new_state = CAN_STATE_BUS_OFF;
+ }
+
+ if (status & M16C_STATE_BUS_PASSIVE) {
+ if (priv->can.state != CAN_STATE_ERROR_PASSIVE) {
+ cf->can_id |= CAN_ERR_CRTL;
+
+ if ((txerr > 0) || (rxerr > 0))
+ cf->data[1] = (txerr > rxerr)
+ ? CAN_ERR_CRTL_TX_PASSIVE
+ : CAN_ERR_CRTL_RX_PASSIVE;
+ else
+ cf->data[1] = CAN_ERR_CRTL_TX_PASSIVE |
+ CAN_ERR_CRTL_RX_PASSIVE;
+
+ priv->can.can_stats.error_passive++;
+ }
+
+ new_state = CAN_STATE_ERROR_PASSIVE;
+ }
+
+ if (status == M16C_STATE_BUS_ERROR) {
+ if ((priv->can.state < CAN_STATE_ERROR_WARNING) &&
+ ((txerr > 96) || (rxerr > 96))) {
+ cf->can_id |= CAN_ERR_CRTL;
+ cf->data[1] = (txerr > rxerr)
+ ? CAN_ERR_CRTL_TX_WARNING
+ : CAN_ERR_CRTL_RX_WARNING;
+
+ priv->can.can_stats.error_warning++;
+ new_state = CAN_STATE_ERROR_WARNING;
+ } else if (priv->can.state > CAN_STATE_ERROR_ACTIVE) {
+ cf->can_id |= CAN_ERR_PROT;
+ cf->data[2] = CAN_ERR_PROT_ACTIVE;
+
+ new_state = CAN_STATE_ERROR_ACTIVE;
+ }
+ }
+
+ if (!status) {
+ cf->can_id |= CAN_ERR_PROT;
+ cf->data[2] = CAN_ERR_PROT_ACTIVE;
+
+ new_state = CAN_STATE_ERROR_ACTIVE;
+ }
+
+ if (priv->can.restart_ms &&
+ (priv->can.state >= CAN_STATE_BUS_OFF) &&
+ (new_state < CAN_STATE_BUS_OFF)) {
+ cf->can_id |= CAN_ERR_RESTARTED;
+ netif_carrier_on(priv->netdev);
+
+ priv->can.can_stats.restarts++;
+ }
+
+ if (msg->id == CMD_CAN_ERROR_EVENT) {
+ u8 error_factor = msg->u.error_event.error_factor;
+
+ priv->can.can_stats.bus_error++;
+ stats->rx_errors++;
+
+ if (error_factor)
+ cf->can_id |= CAN_ERR_PROT;
+
+ if (error_factor & M16C_EF_ACKE)
+ cf->data[3] |= (CAN_ERR_PROT_LOC_ACK);
+ if (error_factor & M16C_EF_CRCE)
+ cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ |
+ CAN_ERR_PROT_LOC_CRC_DEL);
+ if (error_factor & M16C_EF_FORME)
+ cf->data[2] |= CAN_ERR_PROT_FORM;
+ if (error_factor & M16C_EF_STFE)
+ cf->data[2] |= CAN_ERR_PROT_STUFF;
+ if (error_factor & M16C_EF_BITE0)
+ cf->data[2] |= CAN_ERR_PROT_BIT0;
+ if (error_factor & M16C_EF_BITE1)
+ cf->data[2] |= CAN_ERR_PROT_BIT1;
+ if (error_factor & M16C_EF_TRE)
+ cf->data[2] |= CAN_ERR_PROT_TX;
+ }
+
+ cf->data[6] = txerr;
+ cf->data[7] = rxerr;
+
+ priv->bec.txerr = txerr;
+ priv->bec.rxerr = rxerr;
+
+ priv->can.state = new_state;
+
+ netif_rx(skb);
+
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+}
+
+static void kvaser_usb_rx_can_msg(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ struct kvaser_usb_net_priv *priv;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ struct net_device_stats *stats;
+ u8 channel = msg->u.rx_can.channel;
+
+ if (channel >= dev->nchannels) {
+ dev_err(dev->udev->dev.parent,
+ "Invalid channel number (%d)\n", channel);
+ return;
+ }
+
+ priv = dev->nets[channel];
+ stats = &priv->netdev->stats;
+
+ skb = alloc_can_skb(priv->netdev, &cf);
+ if (!skb) {
+ stats->tx_dropped++;
+ return;
+ }
+
+ cf->can_id = ((msg->u.rx_can.msg[0] & 0x1f) << 6) |
+ (msg->u.rx_can.msg[1] & 0x3f);
+ cf->can_dlc = get_can_dlc(msg->u.rx_can.msg[5]);
+
+ if (msg->id == CMD_RX_EXT_MESSAGE) {
+ cf->can_id <<= 18;
+ cf->can_id |= ((msg->u.rx_can.msg[2] & 0x0f) << 14) |
+ ((msg->u.rx_can.msg[3] & 0xff) << 6) |
+ (msg->u.rx_can.msg[4] & 0x3f);
+ cf->can_id |= CAN_EFF_FLAG;
+ }
+
+ if (msg->u.rx_can.flag & MSG_FLAG_REMOTE_FRAME) {
+ cf->can_id |= CAN_RTR_FLAG;
+ } else if (msg->u.rx_can.flag & (MSG_FLAG_ERROR_FRAME |
+ MSG_FLAG_NERR)) {
+ cf->can_id |= CAN_ERR_FLAG;
+ cf->can_dlc = CAN_ERR_DLC;
+
+ netdev_err(priv->netdev, "Unknow error (flags: 0x%02x)\n",
+ msg->u.rx_can.flag);
+
+ stats->rx_errors++;
+ } else if (msg->u.rx_can.flag & MSG_FLAG_OVERRUN) {
+ cf->can_id = CAN_ERR_FLAG | CAN_ERR_CRTL;
+ cf->can_dlc = CAN_ERR_DLC;
+ cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+
+ stats->rx_over_errors++;
+ stats->rx_errors++;
+ } else if (!msg->u.rx_can.flag) {
+ memcpy(cf->data, &msg->u.rx_can.msg[6], cf->can_dlc);
+ } else {
+ kfree_skb(skb);
+ return;
+ }
+
+ netif_rx(skb);
+
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+}
+
+static void kvaser_usb_start_chip_reply(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ struct kvaser_usb_net_priv *priv;
+ u8 channel = msg->u.simple.channel;
+
+ if (channel >= dev->nchannels) {
+ dev_err(dev->udev->dev.parent,
+ "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_stop_chip_reply(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ struct kvaser_usb_net_priv *priv;
+ u8 channel = msg->u.simple.channel;
+
+ if (channel >= dev->nchannels) {
+ dev_err(dev->udev->dev.parent,
+ "Invalid channel number (%d)\n", channel);
+ return;
+ }
+
+ priv = dev->nets[channel];
+
+ complete(&priv->stop_comp);
+}
+
+static void kvaser_usb_handle_message(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ switch (msg->id) {
+ case CMD_START_CHIP_REPLY:
+ kvaser_usb_start_chip_reply(dev, msg);
+ break;
+
+ case CMD_STOP_CHIP_REPLY:
+ kvaser_usb_stop_chip_reply(dev, msg);
+ break;
+
+ case CMD_RX_STD_MESSAGE:
+ case CMD_RX_EXT_MESSAGE:
+ kvaser_usb_rx_can_msg(dev, msg);
+ break;
+
+ case CMD_CHIP_STATE_EVENT:
+ case CMD_CAN_ERROR_EVENT:
+ kvaser_usb_rx_error(dev, msg);
+ break;
+
+ case CMD_TX_ACKNOWLEDGE:
+ kvaser_usb_tx_acknowledge(dev, msg);
+ break;
+
+ default:
+ dev_warn(dev->udev->dev.parent,
+ "Unhandled message (%d)\n", msg->id);
+ break;
+ }
+}
+
+static void kvaser_usb_read_bulk_callback(struct urb *urb)
+{
+ struct kvaser_usb *dev = urb->context;
+ struct kvaser_msg *msg;
+ int pos = 0;
+ int err, i;
+
+ switch (urb->status) {
+ case 0:
+ break;
+ case -ENOENT:
+ 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 - MSG_HEADER_LEN) {
+ msg = urb->transfer_buffer + pos;
+
+ if (!msg->len)
+ break;
+
+ if (pos + msg->len > urb->actual_length) {
+ dev_err(dev->udev->dev.parent, "Format error\n");
+ break;
+ }
+
+ kvaser_usb_handle_message(dev, msg);
+
+ pos += msg->len;
+ }
+
+resubmit_urb:
+ usb_fill_bulk_urb(urb, dev->udev,
+ usb_rcvbulkpipe(dev->udev,
+ dev->bulk_in->bEndpointAddress),
+ urb->transfer_buffer, 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->udev->dev.parent,
+ "Failed resubmitting read bulk urb: %d\n", err);
+ }
+
+ return;
+}
+
+static int kvaser_usb_setup_rx_urbs(struct kvaser_usb *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;
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ dev_warn(dev->udev->dev.parent,
+ "No memory left for URBs\n");
+ 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");
+ usb_free_urb(urb);
+ err = -ENOMEM;
+ break;
+ }
+
+ usb_fill_bulk_urb(urb, dev->udev,
+ usb_rcvbulkpipe(dev->udev,
+ dev->bulk_in->bEndpointAddress),
+ buf, 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, RX_BUFFER_SIZE, buf,
+ buf_dma);
+ break;
+ }
+
+ dev->rxbuf[i] = buf;
+ dev->rxbuf_dma[i] = buf_dma;
+
+ usb_free_urb(urb);
+ }
+
+ if (i == 0) {
+ dev_warn(dev->udev->dev.parent,
+ "Cannot setup read URBs, error %d\n", err);
+ return err;
+ } else if (i < MAX_RX_URBS) {
+ dev_warn(dev->udev->dev.parent,
+ "RX performances may be slow\n");
+ }
+
+ dev->rxinitdone = true;
+
+ return 0;
+}
+
+static int kvaser_usb_set_opt_mode(const struct kvaser_usb_net_priv *priv)
+{
+ struct kvaser_msg msg = {
+ .id = CMD_SET_CTRL_MODE,
+ .len = MSG_HEADER_LEN +
+ sizeof(struct kvaser_msg_ctrl_mode),
+ .u.ctrl_mode.tid = 0xff,
+ .u.ctrl_mode.channel = priv->channel,
+ };
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
+ msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_SILENT;
+ else
+ msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_NORMAL;
+
+ return kvaser_usb_send_msg(priv->dev, &msg);
+}
+
+static int kvaser_usb_start_chip(struct kvaser_usb_net_priv *priv)
+{
+ int err;
+
+ init_completion(&priv->start_comp);
+
+ err = kvaser_usb_send_simple_msg(priv->dev, CMD_START_CHIP,
+ priv->channel);
+ if (err)
+ return err;
+
+ if (!wait_for_completion_timeout(&priv->start_comp,
+ msecs_to_jiffies(START_TIMEOUT)))
+ return -ETIMEDOUT;
+
+ 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;
+ int err;
+
+ err = open_candev(netdev);
+ if (err)
+ return err;
+
+ err = kvaser_usb_setup_rx_urbs(dev);
+ if (err)
+ goto error;
+
+ err = kvaser_usb_set_opt_mode(priv);
+ if (err)
+ goto error;
+
+ err = kvaser_usb_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_unlink_all_urbs(struct kvaser_usb *dev)
+{
+ int i;
+
+ 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 < MAX_NET_DEVICES; i++) {
+ struct kvaser_usb_net_priv *priv = dev->nets[i];
+
+ if (priv)
+ kvaser_usb_unlink_tx_urbs(priv);
+ }
+}
+
+static int kvaser_usb_stop_chip(struct kvaser_usb_net_priv *priv)
+{
+ int err;
+
+ init_completion(&priv->stop_comp);
+
+ err = kvaser_usb_send_simple_msg(priv->dev, CMD_STOP_CHIP,
+ priv->channel);
+ if (err)
+ return err;
+
+ if (!wait_for_completion_timeout(&priv->stop_comp,
+ msecs_to_jiffies(STOP_TIMEOUT)))
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int kvaser_usb_flush_queue(struct kvaser_usb_net_priv *priv)
+{
+ struct kvaser_msg msg = {
+ .id = CMD_FLUSH_QUEUE,
+ .len = MSG_HEADER_LEN +
+ sizeof(struct kvaser_msg_flush_queue),
+ .u.flush_queue.channel = priv->channel,
+ .u.flush_queue.flags = 0x00,
+ };
+
+ return kvaser_usb_send_msg(priv->dev, &msg);
+}
+
+static int kvaser_usb_close(struct net_device *netdev)
+{
+ struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
+ struct kvaser_usb *dev = priv->dev;
+ int err;
+
+ netif_stop_queue(netdev);
+
+ err = kvaser_usb_flush_queue(priv);
+ if (err)
+ netdev_warn(netdev, "Cannot flush queue, error %d\n", err);
+
+ if (kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, priv->channel))
+ netdev_warn(netdev, "Cannot reset card, error %d\n", err);
+
+ err = kvaser_usb_stop_chip(priv);
+ if (err)
+ netdev_warn(netdev, "Cannot stop device, error %d\n", err);
+
+ priv->can.state = CAN_STATE_STOPPED;
+ close_candev(priv->netdev);
+
+ return 0;
+}
+
+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;
+ struct net_device_stats *stats = &netdev->stats;
+ struct can_frame *cf = (struct can_frame *)skb->data;
+ struct kvaser_usb_tx_urb_context *context = NULL;
+ struct urb *urb;
+ void *buf;
+ struct kvaser_msg *msg;
+ int i, err;
+ int ret = NETDEV_TX_OK;
+
+ if (can_dropped_invalid_skb(netdev, skb))
+ return NETDEV_TX_OK;
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb) {
+ netdev_err(netdev, "No memory left for URBs\n");
+ stats->tx_dropped++;
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+
+ buf = kmalloc(sizeof(struct kvaser_msg), GFP_ATOMIC);
+ if (!buf) {
+ netdev_err(netdev, "No memory left for USB buffer\n");
+ stats->tx_dropped++;
+ dev_kfree_skb(skb);
+ goto nobufmem;
+ }
+
+ msg = (struct kvaser_msg *)buf;
+ msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_tx_can);
+ msg->u.tx_can.flags = 0;
+ msg->u.tx_can.channel = priv->channel;
+
+ if (cf->can_id & CAN_EFF_FLAG) {
+ msg->id = CMD_TX_EXT_MESSAGE;
+ msg->u.tx_can.msg[0] = (cf->can_id >> 24) & 0x1f;
+ msg->u.tx_can.msg[1] = (cf->can_id >> 18) & 0x3f;
+ msg->u.tx_can.msg[2] = (cf->can_id >> 14) & 0x0f;
+ msg->u.tx_can.msg[3] = (cf->can_id >> 6) & 0xff;
+ msg->u.tx_can.msg[4] = cf->can_id & 0x3f;
+ } else {
+ msg->id = CMD_TX_STD_MESSAGE;
+ msg->u.tx_can.msg[0] = (cf->can_id >> 6) & 0x1f;
+ msg->u.tx_can.msg[1] = cf->can_id & 0x3f;
+ }
+
+ msg->u.tx_can.msg[5] = cf->can_dlc;
+ memcpy(&msg->u.tx_can.msg[6], cf->data, cf->can_dlc);
+
+ if (cf->can_id & CAN_RTR_FLAG)
+ msg->u.tx_can.flags |= MSG_FLAG_REMOTE_FRAME;
+
+ for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++) {
+ if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) {
+ context = &priv->tx_contexts[i];
+ break;
+ }
+ }
+
+ if (!context) {
+ netdev_warn(netdev, "cannot find free context\n");
+ ret = NETDEV_TX_BUSY;
+ goto releasebuf;
+ }
+
+ context->priv = priv;
+ context->echo_index = i;
+ context->dlc = cf->can_dlc;
+
+ msg->u.tx_can.tid = context->echo_index;
+
+ usb_fill_bulk_urb(urb, dev->udev,
+ usb_sndbulkpipe(dev->udev,
+ dev->bulk_out->bEndpointAddress),
+ buf, msg->len,
+ kvaser_usb_write_bulk_callback, context);
+ usb_anchor_urb(urb, &priv->tx_submitted);
+
+ can_put_echo_skb(skb, netdev, context->echo_index);
+
+ atomic_inc(&priv->active_tx_urbs);
+
+ if (atomic_read(&priv->active_tx_urbs) >= MAX_TX_URBS)
+ netif_stop_queue(netdev);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (unlikely(err)) {
+ can_free_echo_skb(netdev, context->echo_index);
+
+ atomic_dec(&priv->active_tx_urbs);
+ 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;
+ }
+
+ netdev->trans_start = jiffies;
+
+ usb_free_urb(urb);
+
+ return NETDEV_TX_OK;
+
+releasebuf:
+ kfree(buf);
+nobufmem:
+ 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,
+};
+
+static struct can_bittiming_const kvaser_usb_bittiming_const = {
+ .name = "kvaser_usb",
+ .tseg1_min = KVASER_USB_TSEG1_MIN,
+ .tseg1_max = KVASER_USB_TSEG1_MAX,
+ .tseg2_min = KVASER_USB_TSEG2_MIN,
+ .tseg2_max = KVASER_USB_TSEG2_MAX,
+ .sjw_max = KVASER_USB_SJW_MAX,
+ .brp_min = KVASER_USB_BRP_MIN,
+ .brp_max = KVASER_USB_BRP_MAX,
+ .brp_inc = KVASER_USB_BRP_INC,
+};
+
+static int kvaser_usb_set_bittiming(struct net_device *netdev)
+{
+ struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
+ struct can_bittiming *bt = &priv->can.bittiming;
+ struct kvaser_usb *dev = priv->dev;
+ struct kvaser_msg msg = {
+ .id = CMD_SET_BUS_PARAMS,
+ .len = MSG_HEADER_LEN +
+ sizeof(struct kvaser_msg_busparams),
+ .u.busparams.channel = priv->channel,
+ .u.busparams.tid = 0xff,
+ .u.busparams.bitrate = cpu_to_le32(bt->bitrate),
+ .u.busparams.sjw = bt->sjw,
+ .u.busparams.tseg1 = bt->prop_seg + bt->phase_seg1,
+ .u.busparams.tseg2 = bt->phase_seg2,
+ };
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
+ msg.u.busparams.no_samp = 3;
+ else
+ msg.u.busparams.no_samp = 1;
+
+ return kvaser_usb_send_msg(dev, &msg);
+}
+
+static int kvaser_usb_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:
+ err = kvaser_usb_simple_msg_async(priv, CMD_START_CHIP);
+ if (err)
+ return err;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int kvaser_usb_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_init_one(struct usb_interface *intf,
+ const struct usb_device_id *id, int channel)
+{
+ struct kvaser_usb *dev = usb_get_intfdata(intf);
+ struct net_device *netdev;
+ struct kvaser_usb_net_priv *priv;
+ int i, err;
+
+ netdev = alloc_candev(sizeof(*priv), MAX_TX_URBS);
+ if (!netdev) {
+ dev_err(&intf->dev, "Cannot alloc candev\n");
+ return -ENOMEM;
+ }
+
+ priv = netdev_priv(netdev);
+
+ init_completion(&priv->start_comp);
+ init_completion(&priv->stop_comp);
+
+ init_usb_anchor(&priv->tx_submitted);
+ atomic_set(&priv->active_tx_urbs, 0);
+
+ for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++)
+ priv->tx_contexts[i].echo_index = MAX_TX_URBS;
+
+ priv->dev = dev;
+ priv->netdev = netdev;
+ priv->channel = channel;
+
+ priv->can.state = CAN_STATE_STOPPED;
+ priv->can.clock.freq = CAN_USB_CLOCK;
+ priv->can.bittiming_const = &kvaser_usb_bittiming_const;
+ priv->can.do_set_bittiming = kvaser_usb_set_bittiming;
+ priv->can.do_set_mode = kvaser_usb_set_mode;
+ if (id->driver_info & KVASER_HAS_TXRX_ERRORS)
+ priv->can.do_get_berr_counter = kvaser_usb_get_berr_counter;
+ priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES;
+ if (id->driver_info & KVASER_HAS_SILENT_MODE)
+ priv->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY;
+
+ netdev->flags |= IFF_ECHO;
+
+ netdev->netdev_ops = &kvaser_usb_netdev_ops;
+
+ SET_NETDEV_DEV(netdev, &intf->dev);
+
+ dev->nets[channel] = priv;
+
+ err = register_candev(netdev);
+ if (err) {
+ dev_err(&intf->dev, "Failed to register can device\n");
+ free_candev(netdev);
+ return err;
+ }
+
+ netdev_dbg(netdev, "device registered\n");
+
+ return 0;
+}
+
+static void kvaser_usb_get_endpoints(const struct usb_interface *intf,
+ struct usb_endpoint_descriptor **in,
+ struct usb_endpoint_descriptor **out)
+{
+ const struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ int i;
+
+ iface_desc = &intf->altsetting[0];
+
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+
+ if (usb_endpoint_is_bulk_in(endpoint))
+ *in = endpoint;
+
+ if (usb_endpoint_is_bulk_out(endpoint))
+ *out = endpoint;
+ }
+}
+
+static int kvaser_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct kvaser_usb *dev;
+ int err = -ENOMEM;
+ int i;
+
+ dev = devm_kzalloc(&intf->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ kvaser_usb_get_endpoints(intf, &dev->bulk_in, &dev->bulk_out);
+ if (!dev->bulk_in || !dev->bulk_out) {
+ 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);
+
+ for (i = 0; i < MAX_NET_DEVICES; i++)
+ kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, i);
+
+ err = kvaser_usb_get_software_info(dev);
+ if (err) {
+ dev_err(&intf->dev,
+ "Cannot get software infos, error %d\n", err);
+ return err;
+ }
+
+ err = kvaser_usb_get_card_info(dev);
+ if (err) {
+ dev_err(&intf->dev,
+ "Cannot get card infos, error %d\n", err);
+ return err;
+ }
+
+ dev_dbg(&intf->dev, "Firmware version: %d.%d.%d\n",
+ ((dev->fw_version >> 24) & 0xff),
+ ((dev->fw_version >> 16) & 0xff),
+ (dev->fw_version & 0xffff));
+
+ for (i = 0; i < dev->nchannels; i++)
+ kvaser_usb_init_one(intf, id, i);
+
+ return 0;
+}
+
+static void kvaser_usb_disconnect(struct usb_interface *intf)
+{
+ struct kvaser_usb *dev = usb_get_intfdata(intf);
+ int i;
+
+ usb_set_intfdata(intf, NULL);
+
+ if (!dev)
+ return;
+
+ for (i = 0; i < dev->nchannels; i++) {
+ if (!dev->nets[i])
+ continue;
+
+ unregister_netdev(dev->nets[i]->netdev);
+ }
+
+ kvaser_usb_unlink_all_urbs(dev);
+
+ for (i = 0; i < dev->nchannels; i++)
+ free_candev(dev->nets[i]->netdev);
+}
+
+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_DESCRIPTION("CAN driver for Kvaser CAN/USB devices");
+MODULE_LICENSE("GPL v2");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* [PATCH v4] can: kvaser_usb: Add support for Kvaser CAN/USB devices
2012-07-30 5:32 [PATCH] can: kvaser_usb: Add support for Kvaser CAN/USB devices Olivier Sobrie
` (2 preceding siblings ...)
2012-08-13 13:51 ` [PATCH v3] " Olivier Sobrie
@ 2012-09-20 5:06 ` Olivier Sobrie
[not found] ` <1348117587-2905-1-git-send-email-olivier-Ui3EtX6WB9GzQB+pC5nmwQ@public.gmane.org>
2012-10-02 7:16 ` [PATCH v5] " Olivier Sobrie
[not found] ` <1343626352-24760-1-git-send-email-olivier-Ui3EtX6WB9GzQB+pC5nmwQ@public.gmane.org>
5 siblings, 1 reply; 43+ messages in thread
From: Olivier Sobrie @ 2012-09-20 5:06 UTC (permalink / raw)
To: Wolfgang Grandegger, Marc Kleine-Budde, linux-can, Kvaser Support
Cc: netdev, linux-usb, Olivier Sobrie
This driver provides support for several Kvaser CAN/USB devices.
Such kind of devices supports up to three CAN network interfaces.
It has been tested with a Kvaser USB Leaf Light (one network interface)
connected to a pch_can interface.
The firmware version of the Kvaser device was 2.5.205.
List of Kvaser devices supported by the driver:
- Kvaser Leaf prototype (P010v2 and v3)
- Kvaser Leaf Light (P010v3)
- 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, Prototype
- 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 OEM Mercury
- Kvaser OEM Leaf
- Kvaser USBcan R
Signed-off-by: Olivier Sobrie <olivier@sobrie.be>
---
Changes since v3:
- add support for CMD_LOG_MESSAGE
drivers/net/can/usb/Kconfig | 33 +
drivers/net/can/usb/Makefile | 1 +
drivers/net/can/usb/kvaser_usb.c | 1555 ++++++++++++++++++++++++++++++++++++++
3 files changed, 1589 insertions(+)
create mode 100644 drivers/net/can/usb/kvaser_usb.c
diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig
index 0a68768..578955f 100644
--- a/drivers/net/can/usb/Kconfig
+++ b/drivers/net/can/usb/Kconfig
@@ -13,6 +13,39 @@ config CAN_ESD_USB2
This driver supports the CAN-USB/2 interface
from esd electronic system design gmbh (http://www.esd.eu).
+config CAN_KVASER_USB
+ tristate "Kvaser CAN/USB interface"
+ ---help---
+ This driver adds support for Kvaser CAN/USB devices like Kvaser
+ Leaf Light.
+
+ The driver gives support for the following devices:
+ - Kvaser Leaf prototype (P010v2 and v3)
+ - Kvaser Leaf Light (P010v3)
+ - 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, Prototype
+ - 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 OEM Mercury
+ - Kvaser OEM Leaf
+ - Kvaser USBcan R
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called kvaser_usb.
+
config CAN_PEAK_USB
tristate "PEAK PCAN-USB/USB Pro interfaces"
---help---
diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile
index da6d1d3..80a2ee4 100644
--- a/drivers/net/can/usb/Makefile
+++ b/drivers/net/can/usb/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o
obj-$(CONFIG_CAN_ESD_USB2) += esd_usb2.o
+obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o
obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/
ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c
new file mode 100644
index 0000000..3509ca5
--- /dev/null
+++ b/drivers/net/can/usb/kvaser_usb.c
@@ -0,0 +1,1555 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * Parts of this driver are based on the following:
+ * - Kvaser linux leaf driver (version 4.78)
+ * - CAN driver for esd CAN-USB/2
+ *
+ * Copyright (C) 2002-2006 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>
+ */
+
+#include <linux/init.h>
+#include <linux/completion.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>
+
+#define MAX_TX_URBS 16
+#define MAX_RX_URBS 4
+#define START_TIMEOUT 1000 /* msecs */
+#define STOP_TIMEOUT 1000 /* msecs */
+#define USB_SEND_TIMEOUT 1000 /* msecs */
+#define USB_RECV_TIMEOUT 1000 /* msecs */
+#define RX_BUFFER_SIZE 3072
+#define CAN_USB_CLOCK 8000000
+#define MAX_NET_DEVICES 3
+
+/* Kvaser USB devices */
+#define KVASER_VENDOR_ID 0x0bfd
+#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
+
+/* USB devices features */
+#define KVASER_HAS_SILENT_MODE BIT(0)
+#define KVASER_HAS_TXRX_ERRORS BIT(1)
+
+/* Message header size */
+#define MSG_HEADER_LEN 2
+
+/* 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 */
+#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)
+
+/* Can msg 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_GET_CTRL_MODE 22
+#define CMD_GET_CTRL_MODE_REPLY 23
+#define CMD_RESET_CHIP 24
+#define CMD_RESET_CARD 25
+#define CMD_START_CHIP 26
+#define CMD_START_CHIP_REPLY 27
+#define CMD_STOP_CHIP 28
+#define CMD_STOP_CHIP_REPLY 29
+#define CMD_GET_CARD_INFO2 32
+#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_RESET_ERROR_COUNTER 49
+#define CMD_TX_ACKNOWLEDGE 50
+#define CMD_CAN_ERROR_EVENT 51
+#define CMD_USB_THROTTLE 77
+#define CMD_LOG_MESSAGE 106
+
+/* 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)
+
+/* bittiming parameters */
+#define KVASER_USB_TSEG1_MIN 1
+#define KVASER_USB_TSEG1_MAX 16
+#define KVASER_USB_TSEG2_MIN 1
+#define KVASER_USB_TSEG2_MAX 8
+#define KVASER_USB_SJW_MAX 4
+#define KVASER_USB_BRP_MIN 1
+#define KVASER_USB_BRP_MAX 64
+#define KVASER_USB_BRP_INC 1
+
+/* 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
+
+struct kvaser_msg_simple {
+ u8 tid;
+ u8 channel;
+} __packed;
+
+struct kvaser_msg_cardinfo {
+ u8 tid;
+ u8 nchannels;
+ __le32 serial_number;
+ __le32 padding;
+ __le32 clock_resolution;
+ __le32 mfgdate;
+ u8 ean[8];
+ u8 hw_revision;
+ u8 usb_hs_mode;
+ __le16 padding2;
+} __packed;
+
+struct kvaser_msg_cardinfo2 {
+ u8 tid;
+ u8 channel;
+ u8 pcb_id[24];
+ __le32 oem_unlock_code;
+} __packed;
+
+struct kvaser_msg_softinfo {
+ u8 tid;
+ u8 channel;
+ __le32 sw_options;
+ __le32 fw_version;
+ __le16 max_outstanding_tx;
+ __le16 padding[9];
+} __packed;
+
+struct kvaser_msg_busparams {
+ u8 tid;
+ u8 channel;
+ __le32 bitrate;
+ u8 tseg1;
+ u8 tseg2;
+ u8 sjw;
+ u8 no_samp;
+} __packed;
+
+struct kvaser_msg_tx_can {
+ u8 channel;
+ u8 tid;
+ u8 msg[14];
+ u8 padding;
+ u8 flags;
+} __packed;
+
+struct kvaser_msg_rx_can {
+ u8 channel;
+ u8 flag;
+ __le16 time[3];
+ u8 msg[14];
+} __packed;
+
+struct kvaser_msg_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 kvaser_msg_tx_acknowledge {
+ u8 channel;
+ u8 tid;
+ __le16 time[3];
+ u8 flags;
+ u8 time_offset;
+} __packed;
+
+struct kvaser_msg_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 kvaser_msg_ctrl_mode {
+ u8 tid;
+ u8 channel;
+ u8 ctrl_mode;
+ u8 padding[3];
+} __packed;
+
+struct kvaser_msg_flush_queue {
+ u8 tid;
+ u8 channel;
+ u8 flags;
+ u8 padding[3];
+} __packed;
+
+struct kvaser_msg_log_message {
+ u8 channel;
+ u8 flags;
+ __le16 time[3];
+ u8 dlc;
+ u8 time_offset;
+ __le32 id;
+ u8 data[8];
+} __packed;
+
+struct kvaser_msg {
+ u8 len;
+ u8 id;
+ union {
+ struct kvaser_msg_simple simple;
+ struct kvaser_msg_cardinfo cardinfo;
+ struct kvaser_msg_cardinfo2 cardinfo2;
+ struct kvaser_msg_softinfo softinfo;
+ struct kvaser_msg_busparams busparams;
+ struct kvaser_msg_tx_can tx_can;
+ struct kvaser_msg_rx_can rx_can;
+ struct kvaser_msg_chip_state_event chip_state_event;
+ struct kvaser_msg_tx_acknowledge tx_acknowledge;
+ struct kvaser_msg_error_event error_event;
+ struct kvaser_msg_ctrl_mode ctrl_mode;
+ struct kvaser_msg_flush_queue flush_queue;
+ struct kvaser_msg_log_message log_message;
+ } u;
+} __packed;
+
+struct kvaser_usb_tx_urb_context {
+ struct kvaser_usb_net_priv *priv;
+ u32 echo_index;
+ int dlc;
+};
+
+struct kvaser_usb {
+ struct usb_device *udev;
+ struct kvaser_usb_net_priv *nets[MAX_NET_DEVICES];
+
+ struct usb_endpoint_descriptor *bulk_in, *bulk_out;
+ struct usb_anchor rx_submitted;
+
+ u32 fw_version;
+ unsigned int nchannels;
+
+ bool rxinitdone;
+ void *rxbuf[MAX_RX_URBS];
+ dma_addr_t rxbuf_dma[MAX_RX_URBS];
+};
+
+struct kvaser_usb_net_priv {
+ struct can_priv can;
+
+ atomic_t active_tx_urbs;
+ struct usb_anchor tx_submitted;
+ struct kvaser_usb_tx_urb_context tx_contexts[MAX_TX_URBS];
+
+ struct completion start_comp, stop_comp;
+
+ struct kvaser_usb *dev;
+ struct net_device *netdev;
+ int channel;
+
+ struct can_berr_counter bec;
+};
+
+static struct usb_device_id kvaser_usb_table[] = {
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_DEVEL_PRODUCT_ID) },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_PRODUCT_ID) },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_SWC_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LIN_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_LS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_SWC_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_DEVEL_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSHS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_UPRO_HSHS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_GI_PRODUCT_ID) },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_OBDII_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSLS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_CH_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_BLACKBIRD_SPRO_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_MERCURY_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_LEAF_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_CAN_R_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, kvaser_usb_table);
+
+static inline int kvaser_usb_send_msg(const struct kvaser_usb *dev,
+ struct kvaser_msg *msg)
+{
+ int actual_len;
+
+ return usb_bulk_msg(dev->udev,
+ usb_sndbulkpipe(dev->udev,
+ dev->bulk_out->bEndpointAddress),
+ msg, msg->len, &actual_len,
+ USB_SEND_TIMEOUT);
+}
+
+static int kvaser_usb_wait_msg(const struct kvaser_usb *dev, u8 id,
+ struct kvaser_msg *msg)
+{
+ struct kvaser_msg *tmp;
+ void *buf;
+ int actual_len;
+ int err;
+ int pos = 0;
+
+ buf = kzalloc(RX_BUFFER_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ err = usb_bulk_msg(dev->udev,
+ usb_rcvbulkpipe(dev->udev,
+ dev->bulk_in->bEndpointAddress),
+ buf, RX_BUFFER_SIZE, &actual_len,
+ USB_RECV_TIMEOUT);
+ if (err < 0)
+ goto end;
+
+ while (pos <= actual_len - MSG_HEADER_LEN) {
+ tmp = buf + pos;
+
+ if (!tmp->len)
+ break;
+
+ if (pos + tmp->len > actual_len) {
+ dev_err(dev->udev->dev.parent, "Format error\n");
+ break;
+ }
+
+ if (tmp->id == id) {
+ memcpy(msg, tmp, tmp->len);
+ goto end;
+ }
+
+ pos += tmp->len;
+ }
+
+ err = -EINVAL;
+
+end:
+ kfree(buf);
+
+ return err;
+}
+
+static int kvaser_usb_send_simple_msg(const struct kvaser_usb *dev,
+ u8 msg_id, int channel)
+{
+ struct kvaser_msg msg = {
+ .len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple),
+ .id = msg_id,
+ .u.simple.channel = channel,
+ .u.simple.tid = 0xff,
+ };
+
+ return kvaser_usb_send_msg(dev, &msg);
+}
+
+static int kvaser_usb_get_software_info(struct kvaser_usb *dev)
+{
+ struct kvaser_msg msg;
+ int err;
+
+ err = kvaser_usb_send_simple_msg(dev, CMD_GET_SOFTWARE_INFO, 0);
+ if (err)
+ return err;
+
+ err = kvaser_usb_wait_msg(dev, CMD_GET_SOFTWARE_INFO_REPLY, &msg);
+ if (err)
+ return err;
+
+ dev->fw_version = le32_to_cpu(msg.u.softinfo.fw_version);
+
+ return 0;
+}
+
+static int kvaser_usb_get_card_info(struct kvaser_usb *dev)
+{
+ struct kvaser_msg msg;
+ int err;
+
+ err = kvaser_usb_send_simple_msg(dev, CMD_GET_CARD_INFO, 0);
+ if (err)
+ return err;
+
+ err = kvaser_usb_wait_msg(dev, CMD_GET_CARD_INFO_REPLY, &msg);
+ if (err)
+ return err;
+
+ dev->nchannels = msg.u.cardinfo.nchannels;
+
+ return 0;
+}
+
+static void kvaser_usb_tx_acknowledge(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ struct net_device_stats *stats;
+ struct kvaser_usb_tx_urb_context *context;
+ struct kvaser_usb_net_priv *priv;
+ struct sk_buff *skb;
+ struct can_frame *cf;
+ u8 channel = msg->u.tx_acknowledge.channel;
+ u8 tid = msg->u.tx_acknowledge.tid;
+
+ if (channel >= dev->nchannels) {
+ dev_err(dev->udev->dev.parent,
+ "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 % 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)) {
+ skb = alloc_can_err_skb(priv->netdev, &cf);
+ if (skb) {
+ cf->can_id |= CAN_ERR_RESTARTED;
+ netif_rx(skb);
+
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+ } 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;
+ can_get_echo_skb(priv->netdev, context->echo_index);
+
+ context->echo_index = MAX_TX_URBS;
+ atomic_dec(&priv->active_tx_urbs);
+
+ netif_wake_queue(priv->netdev);
+}
+
+static void kvaser_usb_simple_msg_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);
+}
+
+static int kvaser_usb_simple_msg_async(struct kvaser_usb_net_priv *priv,
+ u8 msg_id)
+{
+ struct kvaser_usb *dev = priv->dev;
+ struct net_device *netdev = priv->netdev;
+ struct kvaser_msg *msg;
+ struct urb *urb;
+ void *buf;
+ int err;
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb) {
+ netdev_err(netdev, "No memory left for URBs\n");
+ return -ENOMEM;
+ }
+
+ buf = kmalloc(sizeof(struct kvaser_msg), GFP_ATOMIC);
+ if (!buf) {
+ netdev_err(netdev, "No memory left for USB buffer\n");
+ return -ENOMEM;
+ }
+
+ msg = (struct kvaser_msg *)buf;
+ msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple);
+ msg->id = msg_id;
+ msg->u.simple.channel = priv->channel;
+
+ usb_fill_bulk_urb(urb, dev->udev,
+ usb_sndbulkpipe(dev->udev,
+ dev->bulk_out->bEndpointAddress),
+ buf, msg->len,
+ kvaser_usb_simple_msg_callback, priv);
+ 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);
+ kfree(buf);
+ return err;
+ }
+
+ usb_free_urb(urb);
+
+ return 0;
+}
+
+static void kvaser_usb_unlink_tx_urbs(struct kvaser_usb_net_priv *priv)
+{
+ int 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;
+}
+
+static void kvaser_usb_rx_error(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ struct net_device_stats *stats;
+ struct kvaser_usb_net_priv *priv;
+ unsigned int new_state;
+ u8 channel, status, txerr, rxerr, error_factor;
+
+ switch (msg->id) {
+ case CMD_CAN_ERROR_EVENT:
+ channel = msg->u.error_event.channel;
+ status = msg->u.error_event.status;
+ txerr = msg->u.error_event.tx_errors_count;
+ rxerr = msg->u.error_event.rx_errors_count;
+ error_factor = msg->u.error_event.error_factor;
+ break;
+ case CMD_LOG_MESSAGE:
+ channel = msg->u.log_message.channel;
+ status = msg->u.log_message.data[0];
+ txerr = msg->u.log_message.data[2];
+ rxerr = msg->u.log_message.data[3];
+ error_factor = msg->u.log_message.data[1];
+ break;
+ case CMD_CHIP_STATE_EVENT:
+ channel = msg->u.chip_state_event.channel;
+ status = msg->u.chip_state_event.status;
+ txerr = msg->u.chip_state_event.tx_errors_count;
+ rxerr = msg->u.chip_state_event.rx_errors_count;
+ error_factor = 0;
+ break;
+ default:
+ dev_err(dev->udev->dev.parent, "Invalid msg id (%d)\n",
+ msg->id);
+ return;
+ }
+
+ if (channel >= dev->nchannels) {
+ dev_err(dev->udev->dev.parent,
+ "Invalid channel number (%d)\n", channel);
+ return;
+ }
+
+ priv = dev->nets[channel];
+ stats = &priv->netdev->stats;
+
+ if (status & M16C_STATE_BUS_RESET) {
+ kvaser_usb_unlink_tx_urbs(priv);
+ return;
+ }
+
+ skb = alloc_can_err_skb(priv->netdev, &cf);
+ if (!skb) {
+ stats->rx_dropped++;
+ return;
+ }
+
+ cf->can_id |= CAN_ERR_BUSERROR;
+
+ new_state = priv->can.state;
+
+ netdev_dbg(priv->netdev, "Error status: 0x%02x\n", status);
+
+ if (status & M16C_STATE_BUS_OFF) {
+ cf->can_id |= CAN_ERR_BUSOFF;
+
+ priv->can.can_stats.bus_off++;
+ if (!priv->can.restart_ms)
+ kvaser_usb_simple_msg_async(priv, CMD_STOP_CHIP);
+
+ netif_carrier_off(priv->netdev);
+
+ new_state = CAN_STATE_BUS_OFF;
+ }
+
+ if (status & M16C_STATE_BUS_PASSIVE) {
+ if (priv->can.state != CAN_STATE_ERROR_PASSIVE) {
+ cf->can_id |= CAN_ERR_CRTL;
+
+ if ((txerr > 0) || (rxerr > 0))
+ cf->data[1] = (txerr > rxerr)
+ ? CAN_ERR_CRTL_TX_PASSIVE
+ : CAN_ERR_CRTL_RX_PASSIVE;
+ else
+ cf->data[1] = CAN_ERR_CRTL_TX_PASSIVE |
+ CAN_ERR_CRTL_RX_PASSIVE;
+
+ priv->can.can_stats.error_passive++;
+ }
+
+ new_state = CAN_STATE_ERROR_PASSIVE;
+ }
+
+ if (status == M16C_STATE_BUS_ERROR) {
+ if ((priv->can.state < CAN_STATE_ERROR_WARNING) &&
+ ((txerr > 96) || (rxerr > 96))) {
+ cf->can_id |= CAN_ERR_CRTL;
+ cf->data[1] = (txerr > rxerr)
+ ? CAN_ERR_CRTL_TX_WARNING
+ : CAN_ERR_CRTL_RX_WARNING;
+
+ priv->can.can_stats.error_warning++;
+ new_state = CAN_STATE_ERROR_WARNING;
+ } else if (priv->can.state > CAN_STATE_ERROR_ACTIVE) {
+ cf->can_id |= CAN_ERR_PROT;
+ cf->data[2] = CAN_ERR_PROT_ACTIVE;
+
+ new_state = CAN_STATE_ERROR_ACTIVE;
+ }
+ }
+
+ if (!status) {
+ cf->can_id |= CAN_ERR_PROT;
+ cf->data[2] = CAN_ERR_PROT_ACTIVE;
+
+ new_state = CAN_STATE_ERROR_ACTIVE;
+ }
+
+ if (priv->can.restart_ms &&
+ (priv->can.state >= CAN_STATE_BUS_OFF) &&
+ (new_state < CAN_STATE_BUS_OFF)) {
+ cf->can_id |= CAN_ERR_RESTARTED;
+ netif_carrier_on(priv->netdev);
+
+ priv->can.can_stats.restarts++;
+ }
+
+ if (error_factor) {
+ priv->can.can_stats.bus_error++;
+ stats->rx_errors++;
+
+ cf->can_id |= CAN_ERR_PROT;
+
+ if (error_factor & M16C_EF_ACKE)
+ cf->data[3] |= (CAN_ERR_PROT_LOC_ACK);
+ if (error_factor & M16C_EF_CRCE)
+ cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ |
+ CAN_ERR_PROT_LOC_CRC_DEL);
+ if (error_factor & M16C_EF_FORME)
+ cf->data[2] |= CAN_ERR_PROT_FORM;
+ if (error_factor & M16C_EF_STFE)
+ cf->data[2] |= CAN_ERR_PROT_STUFF;
+ if (error_factor & M16C_EF_BITE0)
+ cf->data[2] |= CAN_ERR_PROT_BIT0;
+ if (error_factor & M16C_EF_BITE1)
+ cf->data[2] |= CAN_ERR_PROT_BIT1;
+ if (error_factor & M16C_EF_TRE)
+ cf->data[2] |= CAN_ERR_PROT_TX;
+ }
+
+ cf->data[6] = txerr;
+ cf->data[7] = rxerr;
+
+ priv->bec.txerr = txerr;
+ priv->bec.rxerr = rxerr;
+
+ priv->can.state = new_state;
+
+ netif_rx(skb);
+
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+}
+
+static void kvaser_usb_rx_can_msg(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ struct kvaser_usb_net_priv *priv;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ struct net_device_stats *stats;
+ u8 channel = msg->u.rx_can.channel;
+
+ if (channel >= dev->nchannels) {
+ dev_err(dev->udev->dev.parent,
+ "Invalid channel number (%d)\n", channel);
+ return;
+ }
+
+ priv = dev->nets[channel];
+ stats = &priv->netdev->stats;
+
+ skb = alloc_can_skb(priv->netdev, &cf);
+ if (!skb) {
+ stats->tx_dropped++;
+ return;
+ }
+
+ cf->can_id = ((msg->u.rx_can.msg[0] & 0x1f) << 6) |
+ (msg->u.rx_can.msg[1] & 0x3f);
+ cf->can_dlc = get_can_dlc(msg->u.rx_can.msg[5]);
+
+ if (msg->id == CMD_RX_EXT_MESSAGE) {
+ cf->can_id <<= 18;
+ cf->can_id |= ((msg->u.rx_can.msg[2] & 0x0f) << 14) |
+ ((msg->u.rx_can.msg[3] & 0xff) << 6) |
+ (msg->u.rx_can.msg[4] & 0x3f);
+ cf->can_id |= CAN_EFF_FLAG;
+ }
+
+ if (msg->u.rx_can.flag & MSG_FLAG_REMOTE_FRAME) {
+ cf->can_id |= CAN_RTR_FLAG;
+ } else if (msg->u.rx_can.flag & (MSG_FLAG_ERROR_FRAME |
+ MSG_FLAG_NERR)) {
+ cf->can_id |= CAN_ERR_FLAG;
+ cf->can_dlc = CAN_ERR_DLC;
+
+ netdev_err(priv->netdev, "Unknow error (flags: 0x%02x)\n",
+ msg->u.rx_can.flag);
+
+ stats->rx_errors++;
+ } else if (msg->u.rx_can.flag & MSG_FLAG_OVERRUN) {
+ cf->can_id = CAN_ERR_FLAG | CAN_ERR_CRTL;
+ cf->can_dlc = CAN_ERR_DLC;
+ cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+
+ stats->rx_over_errors++;
+ stats->rx_errors++;
+ } else if (!msg->u.rx_can.flag) {
+ memcpy(cf->data, &msg->u.rx_can.msg[6], cf->can_dlc);
+ } else {
+ kfree_skb(skb);
+ return;
+ }
+
+ netif_rx(skb);
+
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+}
+
+static void kvaser_usb_start_chip_reply(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ struct kvaser_usb_net_priv *priv;
+ u8 channel = msg->u.simple.channel;
+
+ if (channel >= dev->nchannels) {
+ dev_err(dev->udev->dev.parent,
+ "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_stop_chip_reply(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ struct kvaser_usb_net_priv *priv;
+ u8 channel = msg->u.simple.channel;
+
+ if (channel >= dev->nchannels) {
+ dev_err(dev->udev->dev.parent,
+ "Invalid channel number (%d)\n", channel);
+ return;
+ }
+
+ priv = dev->nets[channel];
+
+ complete(&priv->stop_comp);
+}
+
+static void kvaser_usb_handle_message(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ switch (msg->id) {
+ case CMD_START_CHIP_REPLY:
+ kvaser_usb_start_chip_reply(dev, msg);
+ break;
+
+ case CMD_STOP_CHIP_REPLY:
+ kvaser_usb_stop_chip_reply(dev, msg);
+ break;
+
+ case CMD_RX_STD_MESSAGE:
+ case CMD_RX_EXT_MESSAGE:
+ kvaser_usb_rx_can_msg(dev, msg);
+ break;
+
+ case CMD_CHIP_STATE_EVENT:
+ case CMD_CAN_ERROR_EVENT:
+ kvaser_usb_rx_error(dev, msg);
+ break;
+
+ case CMD_LOG_MESSAGE:
+ if (msg->u.log_message.flags & MSG_FLAG_ERROR_FRAME)
+ kvaser_usb_rx_error(dev, msg);
+ break;
+
+ case CMD_TX_ACKNOWLEDGE:
+ kvaser_usb_tx_acknowledge(dev, msg);
+ break;
+
+ default:
+ dev_warn(dev->udev->dev.parent,
+ "Unhandled message (%d)\n", msg->id);
+ break;
+ }
+}
+
+static void kvaser_usb_read_bulk_callback(struct urb *urb)
+{
+ struct kvaser_usb *dev = urb->context;
+ struct kvaser_msg *msg;
+ int pos = 0;
+ int err, i;
+
+ switch (urb->status) {
+ case 0:
+ break;
+ case -ENOENT:
+ 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 - MSG_HEADER_LEN) {
+ msg = urb->transfer_buffer + pos;
+
+ if (!msg->len)
+ break;
+
+ if (pos + msg->len > urb->actual_length) {
+ dev_err(dev->udev->dev.parent, "Format error\n");
+ break;
+ }
+
+ kvaser_usb_handle_message(dev, msg);
+
+ pos += msg->len;
+ }
+
+resubmit_urb:
+ usb_fill_bulk_urb(urb, dev->udev,
+ usb_rcvbulkpipe(dev->udev,
+ dev->bulk_in->bEndpointAddress),
+ urb->transfer_buffer, 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->udev->dev.parent,
+ "Failed resubmitting read bulk urb: %d\n", err);
+ }
+
+ return;
+}
+
+static int kvaser_usb_setup_rx_urbs(struct kvaser_usb *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;
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ dev_warn(dev->udev->dev.parent,
+ "No memory left for URBs\n");
+ 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");
+ usb_free_urb(urb);
+ err = -ENOMEM;
+ break;
+ }
+
+ usb_fill_bulk_urb(urb, dev->udev,
+ usb_rcvbulkpipe(dev->udev,
+ dev->bulk_in->bEndpointAddress),
+ buf, 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, RX_BUFFER_SIZE, buf,
+ buf_dma);
+ break;
+ }
+
+ dev->rxbuf[i] = buf;
+ dev->rxbuf_dma[i] = buf_dma;
+
+ usb_free_urb(urb);
+ }
+
+ if (i == 0) {
+ dev_warn(dev->udev->dev.parent,
+ "Cannot setup read URBs, error %d\n", err);
+ return err;
+ } else if (i < MAX_RX_URBS) {
+ dev_warn(dev->udev->dev.parent,
+ "RX performances may be slow\n");
+ }
+
+ dev->rxinitdone = true;
+
+ return 0;
+}
+
+static int kvaser_usb_set_opt_mode(const struct kvaser_usb_net_priv *priv)
+{
+ struct kvaser_msg msg = {
+ .id = CMD_SET_CTRL_MODE,
+ .len = MSG_HEADER_LEN +
+ sizeof(struct kvaser_msg_ctrl_mode),
+ .u.ctrl_mode.tid = 0xff,
+ .u.ctrl_mode.channel = priv->channel,
+ };
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
+ msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_SILENT;
+ else
+ msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_NORMAL;
+
+ return kvaser_usb_send_msg(priv->dev, &msg);
+}
+
+static int kvaser_usb_start_chip(struct kvaser_usb_net_priv *priv)
+{
+ int err;
+
+ init_completion(&priv->start_comp);
+
+ err = kvaser_usb_send_simple_msg(priv->dev, CMD_START_CHIP,
+ priv->channel);
+ if (err)
+ return err;
+
+ if (!wait_for_completion_timeout(&priv->start_comp,
+ msecs_to_jiffies(START_TIMEOUT)))
+ return -ETIMEDOUT;
+
+ 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;
+ int err;
+
+ err = open_candev(netdev);
+ if (err)
+ return err;
+
+ err = kvaser_usb_setup_rx_urbs(dev);
+ if (err)
+ goto error;
+
+ err = kvaser_usb_set_opt_mode(priv);
+ if (err)
+ goto error;
+
+ err = kvaser_usb_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_unlink_all_urbs(struct kvaser_usb *dev)
+{
+ int i;
+
+ 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 < MAX_NET_DEVICES; i++) {
+ struct kvaser_usb_net_priv *priv = dev->nets[i];
+
+ if (priv)
+ kvaser_usb_unlink_tx_urbs(priv);
+ }
+}
+
+static int kvaser_usb_stop_chip(struct kvaser_usb_net_priv *priv)
+{
+ int err;
+
+ init_completion(&priv->stop_comp);
+
+ err = kvaser_usb_send_simple_msg(priv->dev, CMD_STOP_CHIP,
+ priv->channel);
+ if (err)
+ return err;
+
+ if (!wait_for_completion_timeout(&priv->stop_comp,
+ msecs_to_jiffies(STOP_TIMEOUT)))
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int kvaser_usb_flush_queue(struct kvaser_usb_net_priv *priv)
+{
+ struct kvaser_msg msg = {
+ .id = CMD_FLUSH_QUEUE,
+ .len = MSG_HEADER_LEN +
+ sizeof(struct kvaser_msg_flush_queue),
+ .u.flush_queue.channel = priv->channel,
+ .u.flush_queue.flags = 0x00,
+ };
+
+ return kvaser_usb_send_msg(priv->dev, &msg);
+}
+
+static int kvaser_usb_close(struct net_device *netdev)
+{
+ struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
+ struct kvaser_usb *dev = priv->dev;
+ int err;
+
+ netif_stop_queue(netdev);
+
+ err = kvaser_usb_flush_queue(priv);
+ if (err)
+ netdev_warn(netdev, "Cannot flush queue, error %d\n", err);
+
+ if (kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, priv->channel))
+ netdev_warn(netdev, "Cannot reset card, error %d\n", err);
+
+ err = kvaser_usb_stop_chip(priv);
+ if (err)
+ netdev_warn(netdev, "Cannot stop device, error %d\n", err);
+
+ priv->can.state = CAN_STATE_STOPPED;
+ close_candev(priv->netdev);
+
+ return 0;
+}
+
+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;
+ struct net_device_stats *stats = &netdev->stats;
+ struct can_frame *cf = (struct can_frame *)skb->data;
+ struct kvaser_usb_tx_urb_context *context = NULL;
+ struct urb *urb;
+ void *buf;
+ struct kvaser_msg *msg;
+ int i, err;
+ int ret = NETDEV_TX_OK;
+
+ if (can_dropped_invalid_skb(netdev, skb))
+ return NETDEV_TX_OK;
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb) {
+ netdev_err(netdev, "No memory left for URBs\n");
+ stats->tx_dropped++;
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+
+ buf = kmalloc(sizeof(struct kvaser_msg), GFP_ATOMIC);
+ if (!buf) {
+ netdev_err(netdev, "No memory left for USB buffer\n");
+ stats->tx_dropped++;
+ dev_kfree_skb(skb);
+ goto nobufmem;
+ }
+
+ msg = (struct kvaser_msg *)buf;
+ msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_tx_can);
+ msg->u.tx_can.flags = 0;
+ msg->u.tx_can.channel = priv->channel;
+
+ if (cf->can_id & CAN_EFF_FLAG) {
+ msg->id = CMD_TX_EXT_MESSAGE;
+ msg->u.tx_can.msg[0] = (cf->can_id >> 24) & 0x1f;
+ msg->u.tx_can.msg[1] = (cf->can_id >> 18) & 0x3f;
+ msg->u.tx_can.msg[2] = (cf->can_id >> 14) & 0x0f;
+ msg->u.tx_can.msg[3] = (cf->can_id >> 6) & 0xff;
+ msg->u.tx_can.msg[4] = cf->can_id & 0x3f;
+ } else {
+ msg->id = CMD_TX_STD_MESSAGE;
+ msg->u.tx_can.msg[0] = (cf->can_id >> 6) & 0x1f;
+ msg->u.tx_can.msg[1] = cf->can_id & 0x3f;
+ }
+
+ msg->u.tx_can.msg[5] = cf->can_dlc;
+ memcpy(&msg->u.tx_can.msg[6], cf->data, cf->can_dlc);
+
+ if (cf->can_id & CAN_RTR_FLAG)
+ msg->u.tx_can.flags |= MSG_FLAG_REMOTE_FRAME;
+
+ for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++) {
+ if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) {
+ context = &priv->tx_contexts[i];
+ break;
+ }
+ }
+
+ if (!context) {
+ netdev_warn(netdev, "cannot find free context\n");
+ ret = NETDEV_TX_BUSY;
+ goto releasebuf;
+ }
+
+ context->priv = priv;
+ context->echo_index = i;
+ context->dlc = cf->can_dlc;
+
+ msg->u.tx_can.tid = context->echo_index;
+
+ usb_fill_bulk_urb(urb, dev->udev,
+ usb_sndbulkpipe(dev->udev,
+ dev->bulk_out->bEndpointAddress),
+ buf, msg->len,
+ kvaser_usb_write_bulk_callback, context);
+ usb_anchor_urb(urb, &priv->tx_submitted);
+
+ can_put_echo_skb(skb, netdev, context->echo_index);
+
+ atomic_inc(&priv->active_tx_urbs);
+
+ if (atomic_read(&priv->active_tx_urbs) >= MAX_TX_URBS)
+ netif_stop_queue(netdev);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (unlikely(err)) {
+ can_free_echo_skb(netdev, context->echo_index);
+
+ atomic_dec(&priv->active_tx_urbs);
+ 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;
+ }
+
+ netdev->trans_start = jiffies;
+
+ usb_free_urb(urb);
+
+ return NETDEV_TX_OK;
+
+releasebuf:
+ kfree(buf);
+nobufmem:
+ 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,
+};
+
+static struct can_bittiming_const kvaser_usb_bittiming_const = {
+ .name = "kvaser_usb",
+ .tseg1_min = KVASER_USB_TSEG1_MIN,
+ .tseg1_max = KVASER_USB_TSEG1_MAX,
+ .tseg2_min = KVASER_USB_TSEG2_MIN,
+ .tseg2_max = KVASER_USB_TSEG2_MAX,
+ .sjw_max = KVASER_USB_SJW_MAX,
+ .brp_min = KVASER_USB_BRP_MIN,
+ .brp_max = KVASER_USB_BRP_MAX,
+ .brp_inc = KVASER_USB_BRP_INC,
+};
+
+static int kvaser_usb_set_bittiming(struct net_device *netdev)
+{
+ struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
+ struct can_bittiming *bt = &priv->can.bittiming;
+ struct kvaser_usb *dev = priv->dev;
+ struct kvaser_msg msg = {
+ .id = CMD_SET_BUS_PARAMS,
+ .len = MSG_HEADER_LEN +
+ sizeof(struct kvaser_msg_busparams),
+ .u.busparams.channel = priv->channel,
+ .u.busparams.tid = 0xff,
+ .u.busparams.bitrate = cpu_to_le32(bt->bitrate),
+ .u.busparams.sjw = bt->sjw,
+ .u.busparams.tseg1 = bt->prop_seg + bt->phase_seg1,
+ .u.busparams.tseg2 = bt->phase_seg2,
+ };
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
+ msg.u.busparams.no_samp = 3;
+ else
+ msg.u.busparams.no_samp = 1;
+
+ return kvaser_usb_send_msg(dev, &msg);
+}
+
+static int kvaser_usb_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:
+ err = kvaser_usb_simple_msg_async(priv, CMD_START_CHIP);
+ if (err)
+ return err;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int kvaser_usb_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_init_one(struct usb_interface *intf,
+ const struct usb_device_id *id, int channel)
+{
+ struct kvaser_usb *dev = usb_get_intfdata(intf);
+ struct net_device *netdev;
+ struct kvaser_usb_net_priv *priv;
+ int i, err;
+
+ netdev = alloc_candev(sizeof(*priv), MAX_TX_URBS);
+ if (!netdev) {
+ dev_err(&intf->dev, "Cannot alloc candev\n");
+ return -ENOMEM;
+ }
+
+ priv = netdev_priv(netdev);
+
+ init_completion(&priv->start_comp);
+ init_completion(&priv->stop_comp);
+
+ init_usb_anchor(&priv->tx_submitted);
+ atomic_set(&priv->active_tx_urbs, 0);
+
+ for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++)
+ priv->tx_contexts[i].echo_index = MAX_TX_URBS;
+
+ priv->dev = dev;
+ priv->netdev = netdev;
+ priv->channel = channel;
+
+ priv->can.state = CAN_STATE_STOPPED;
+ priv->can.clock.freq = CAN_USB_CLOCK;
+ priv->can.bittiming_const = &kvaser_usb_bittiming_const;
+ priv->can.do_set_bittiming = kvaser_usb_set_bittiming;
+ priv->can.do_set_mode = kvaser_usb_set_mode;
+ if (id->driver_info & KVASER_HAS_TXRX_ERRORS)
+ priv->can.do_get_berr_counter = kvaser_usb_get_berr_counter;
+ priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES;
+ if (id->driver_info & KVASER_HAS_SILENT_MODE)
+ priv->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY;
+
+ netdev->flags |= IFF_ECHO;
+
+ netdev->netdev_ops = &kvaser_usb_netdev_ops;
+
+ SET_NETDEV_DEV(netdev, &intf->dev);
+
+ dev->nets[channel] = priv;
+
+ err = register_candev(netdev);
+ if (err) {
+ dev_err(&intf->dev, "Failed to register can device\n");
+ free_candev(netdev);
+ return err;
+ }
+
+ netdev_dbg(netdev, "device registered\n");
+
+ return 0;
+}
+
+static void kvaser_usb_get_endpoints(const struct usb_interface *intf,
+ struct usb_endpoint_descriptor **in,
+ struct usb_endpoint_descriptor **out)
+{
+ const struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ int i;
+
+ iface_desc = &intf->altsetting[0];
+
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+
+ if (usb_endpoint_is_bulk_in(endpoint))
+ *in = endpoint;
+
+ if (usb_endpoint_is_bulk_out(endpoint))
+ *out = endpoint;
+ }
+}
+
+static int kvaser_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct kvaser_usb *dev;
+ int err = -ENOMEM;
+ int i;
+
+ dev = devm_kzalloc(&intf->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ kvaser_usb_get_endpoints(intf, &dev->bulk_in, &dev->bulk_out);
+ if (!dev->bulk_in || !dev->bulk_out) {
+ 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);
+
+ for (i = 0; i < MAX_NET_DEVICES; i++)
+ kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, i);
+
+ err = kvaser_usb_get_software_info(dev);
+ if (err) {
+ dev_err(&intf->dev,
+ "Cannot get software infos, error %d\n", err);
+ return err;
+ }
+
+ err = kvaser_usb_get_card_info(dev);
+ if (err) {
+ dev_err(&intf->dev,
+ "Cannot get card infos, error %d\n", err);
+ return err;
+ }
+
+ dev_dbg(&intf->dev, "Firmware version: %d.%d.%d\n",
+ ((dev->fw_version >> 24) & 0xff),
+ ((dev->fw_version >> 16) & 0xff),
+ (dev->fw_version & 0xffff));
+
+ for (i = 0; i < dev->nchannels; i++)
+ kvaser_usb_init_one(intf, id, i);
+
+ return 0;
+}
+
+static void kvaser_usb_disconnect(struct usb_interface *intf)
+{
+ struct kvaser_usb *dev = usb_get_intfdata(intf);
+ int i;
+
+ usb_set_intfdata(intf, NULL);
+
+ if (!dev)
+ return;
+
+ for (i = 0; i < dev->nchannels; i++) {
+ if (!dev->nets[i])
+ continue;
+
+ unregister_netdev(dev->nets[i]->netdev);
+ }
+
+ kvaser_usb_unlink_all_urbs(dev);
+
+ for (i = 0; i < dev->nchannels; i++)
+ free_candev(dev->nets[i]->netdev);
+}
+
+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_DESCRIPTION("CAN driver for Kvaser CAN/USB devices");
+MODULE_LICENSE("GPL v2");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* [PATCH v5] can: kvaser_usb: Add support for Kvaser CAN/USB devices
2012-07-30 5:32 [PATCH] can: kvaser_usb: Add support for Kvaser CAN/USB devices Olivier Sobrie
` (3 preceding siblings ...)
2012-09-20 5:06 ` [PATCH v4] " Olivier Sobrie
@ 2012-10-02 7:16 ` Olivier Sobrie
2012-11-07 20:29 ` Marc Kleine-Budde
[not found] ` <1343626352-24760-1-git-send-email-olivier-Ui3EtX6WB9GzQB+pC5nmwQ@public.gmane.org>
5 siblings, 1 reply; 43+ messages in thread
From: Olivier Sobrie @ 2012-10-02 7:16 UTC (permalink / raw)
To: Wolfgang Grandegger, Marc Kleine-Budde, linux-can, Kvaser Support
Cc: netdev, linux-usb, Olivier Sobrie, Daniel Berglund
This driver provides support for several Kvaser CAN/USB devices.
Such kind of devices supports up to three CAN network interfaces.
It has been tested with a Kvaser USB Leaf Light (one network interface)
connected to a pch_can interface.
The firmware version of the Kvaser device was 2.5.205.
List of Kvaser devices supported by the driver:
- 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
Signed-off-by: Daniel Berglund <db@kvaser.com>
Signed-off-by: Olivier Sobrie <olivier@sobrie.be>
---
Hi,
This new patch fixes the errors pointed out by Marc and Wolfgang.
Changes since v4:
- add missing usb_free_urb()
- put error message in a separate function
- handle return code of kvaser_usb_init_one() in probe function
Olivier
drivers/net/can/usb/Kconfig | 29 +
drivers/net/can/usb/Makefile | 1 +
drivers/net/can/usb/kvaser_usb.c | 1596 ++++++++++++++++++++++++++++++++++++++
3 files changed, 1626 insertions(+)
create mode 100644 drivers/net/can/usb/kvaser_usb.c
diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig
index 0a68768..a4e4bee 100644
--- a/drivers/net/can/usb/Kconfig
+++ b/drivers/net/can/usb/Kconfig
@@ -13,6 +13,35 @@ config CAN_ESD_USB2
This driver supports the CAN-USB/2 interface
from esd electronic system design gmbh (http://www.esd.eu).
+config CAN_KVASER_USB
+ tristate "Kvaser CAN/USB interface"
+ ---help---
+ This driver adds support for Kvaser CAN/USB devices like Kvaser
+ Leaf Light.
+
+ The driver gives 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
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called kvaser_usb.
+
config CAN_PEAK_USB
tristate "PEAK PCAN-USB/USB Pro interfaces"
---help---
diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile
index da6d1d3..80a2ee4 100644
--- a/drivers/net/can/usb/Makefile
+++ b/drivers/net/can/usb/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o
obj-$(CONFIG_CAN_ESD_USB2) += esd_usb2.o
+obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o
obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/
ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c
new file mode 100644
index 0000000..bd48807
--- /dev/null
+++ b/drivers/net/can/usb/kvaser_usb.c
@@ -0,0 +1,1596 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * Parts of this driver are based on the following:
+ * - Kvaser linux leaf driver (version 4.78)
+ * - CAN driver for esd CAN-USB/2
+ *
+ * Copyright (C) 2002-2006 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>
+ */
+
+#include <linux/init.h>
+#include <linux/completion.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>
+
+#define MAX_TX_URBS 16
+#define MAX_RX_URBS 4
+#define START_TIMEOUT 1000 /* msecs */
+#define STOP_TIMEOUT 1000 /* msecs */
+#define USB_SEND_TIMEOUT 1000 /* msecs */
+#define USB_RECV_TIMEOUT 1000 /* msecs */
+#define RX_BUFFER_SIZE 3072
+#define CAN_USB_CLOCK 8000000
+#define MAX_NET_DEVICES 3
+
+/* Kvaser USB devices */
+#define KVASER_VENDOR_ID 0x0bfd
+#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
+
+/* USB devices features */
+#define KVASER_HAS_SILENT_MODE BIT(0)
+#define KVASER_HAS_TXRX_ERRORS BIT(1)
+
+/* Message header size */
+#define MSG_HEADER_LEN 2
+
+/* 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 */
+#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)
+
+/* Can msg 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_GET_CTRL_MODE 22
+#define CMD_GET_CTRL_MODE_REPLY 23
+#define CMD_RESET_CHIP 24
+#define CMD_RESET_CARD 25
+#define CMD_START_CHIP 26
+#define CMD_START_CHIP_REPLY 27
+#define CMD_STOP_CHIP 28
+#define CMD_STOP_CHIP_REPLY 29
+#define CMD_GET_CARD_INFO2 32
+#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_RESET_ERROR_COUNTER 49
+#define CMD_TX_ACKNOWLEDGE 50
+#define CMD_CAN_ERROR_EVENT 51
+#define CMD_USB_THROTTLE 77
+#define CMD_LOG_MESSAGE 106
+
+/* 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)
+
+/* bittiming parameters */
+#define KVASER_USB_TSEG1_MIN 1
+#define KVASER_USB_TSEG1_MAX 16
+#define KVASER_USB_TSEG2_MIN 1
+#define KVASER_USB_TSEG2_MAX 8
+#define KVASER_USB_SJW_MAX 4
+#define KVASER_USB_BRP_MIN 1
+#define KVASER_USB_BRP_MAX 64
+#define KVASER_USB_BRP_INC 1
+
+/* 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
+
+struct kvaser_msg_simple {
+ u8 tid;
+ u8 channel;
+} __packed;
+
+struct kvaser_msg_cardinfo {
+ u8 tid;
+ u8 nchannels;
+ __le32 serial_number;
+ __le32 padding;
+ __le32 clock_resolution;
+ __le32 mfgdate;
+ u8 ean[8];
+ u8 hw_revision;
+ u8 usb_hs_mode;
+ __le16 padding2;
+} __packed;
+
+struct kvaser_msg_cardinfo2 {
+ u8 tid;
+ u8 channel;
+ u8 pcb_id[24];
+ __le32 oem_unlock_code;
+} __packed;
+
+struct kvaser_msg_softinfo {
+ u8 tid;
+ u8 channel;
+ __le32 sw_options;
+ __le32 fw_version;
+ __le16 max_outstanding_tx;
+ __le16 padding[9];
+} __packed;
+
+struct kvaser_msg_busparams {
+ u8 tid;
+ u8 channel;
+ __le32 bitrate;
+ u8 tseg1;
+ u8 tseg2;
+ u8 sjw;
+ u8 no_samp;
+} __packed;
+
+struct kvaser_msg_tx_can {
+ u8 channel;
+ u8 tid;
+ u8 msg[14];
+ u8 padding;
+ u8 flags;
+} __packed;
+
+struct kvaser_msg_rx_can {
+ u8 channel;
+ u8 flag;
+ __le16 time[3];
+ u8 msg[14];
+} __packed;
+
+struct kvaser_msg_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 kvaser_msg_tx_acknowledge {
+ u8 channel;
+ u8 tid;
+ __le16 time[3];
+ u8 flags;
+ u8 time_offset;
+} __packed;
+
+struct kvaser_msg_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 kvaser_msg_ctrl_mode {
+ u8 tid;
+ u8 channel;
+ u8 ctrl_mode;
+ u8 padding[3];
+} __packed;
+
+struct kvaser_msg_flush_queue {
+ u8 tid;
+ u8 channel;
+ u8 flags;
+ u8 padding[3];
+} __packed;
+
+struct kvaser_msg_log_message {
+ u8 channel;
+ u8 flags;
+ __le16 time[3];
+ u8 dlc;
+ u8 time_offset;
+ __le32 id;
+ u8 data[8];
+} __packed;
+
+struct kvaser_msg {
+ u8 len;
+ u8 id;
+ union {
+ struct kvaser_msg_simple simple;
+ struct kvaser_msg_cardinfo cardinfo;
+ struct kvaser_msg_cardinfo2 cardinfo2;
+ struct kvaser_msg_softinfo softinfo;
+ struct kvaser_msg_busparams busparams;
+ struct kvaser_msg_tx_can tx_can;
+ struct kvaser_msg_rx_can rx_can;
+ struct kvaser_msg_chip_state_event chip_state_event;
+ struct kvaser_msg_tx_acknowledge tx_acknowledge;
+ struct kvaser_msg_error_event error_event;
+ struct kvaser_msg_ctrl_mode ctrl_mode;
+ struct kvaser_msg_flush_queue flush_queue;
+ struct kvaser_msg_log_message log_message;
+ } u;
+} __packed;
+
+struct kvaser_usb_tx_urb_context {
+ struct kvaser_usb_net_priv *priv;
+ u32 echo_index;
+ int dlc;
+};
+
+struct kvaser_usb {
+ struct usb_device *udev;
+ struct kvaser_usb_net_priv *nets[MAX_NET_DEVICES];
+
+ struct usb_endpoint_descriptor *bulk_in, *bulk_out;
+ struct usb_anchor rx_submitted;
+
+ u32 fw_version;
+ unsigned int nchannels;
+
+ bool rxinitdone;
+ void *rxbuf[MAX_RX_URBS];
+ dma_addr_t rxbuf_dma[MAX_RX_URBS];
+};
+
+struct kvaser_usb_net_priv {
+ struct can_priv can;
+
+ atomic_t active_tx_urbs;
+ struct usb_anchor tx_submitted;
+ struct kvaser_usb_tx_urb_context tx_contexts[MAX_TX_URBS];
+
+ struct completion start_comp, stop_comp;
+
+ struct kvaser_usb *dev;
+ struct net_device *netdev;
+ int channel;
+
+ struct can_berr_counter bec;
+};
+
+static struct usb_device_id kvaser_usb_table[] = {
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_DEVEL_PRODUCT_ID) },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_PRODUCT_ID) },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_SWC_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LIN_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_LS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_SWC_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_DEVEL_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSHS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_UPRO_HSHS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_GI_PRODUCT_ID) },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_OBDII_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSLS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_CH_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_BLACKBIRD_SPRO_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_MERCURY_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_LEAF_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_CAN_R_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, kvaser_usb_table);
+
+static inline int kvaser_usb_send_msg(const struct kvaser_usb *dev,
+ struct kvaser_msg *msg)
+{
+ int actual_len;
+
+ return usb_bulk_msg(dev->udev,
+ usb_sndbulkpipe(dev->udev,
+ dev->bulk_out->bEndpointAddress),
+ msg, msg->len, &actual_len,
+ USB_SEND_TIMEOUT);
+}
+
+static int kvaser_usb_wait_msg(const struct kvaser_usb *dev, u8 id,
+ struct kvaser_msg *msg)
+{
+ struct kvaser_msg *tmp;
+ void *buf;
+ int actual_len;
+ int err;
+ int pos = 0;
+
+ buf = kzalloc(RX_BUFFER_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ err = usb_bulk_msg(dev->udev,
+ usb_rcvbulkpipe(dev->udev,
+ dev->bulk_in->bEndpointAddress),
+ buf, RX_BUFFER_SIZE, &actual_len,
+ USB_RECV_TIMEOUT);
+ if (err < 0)
+ goto end;
+
+ while (pos <= actual_len - MSG_HEADER_LEN) {
+ tmp = buf + pos;
+
+ if (!tmp->len)
+ break;
+
+ if (pos + tmp->len > actual_len) {
+ dev_err(dev->udev->dev.parent, "Format error\n");
+ break;
+ }
+
+ if (tmp->id == id) {
+ memcpy(msg, tmp, tmp->len);
+ goto end;
+ }
+
+ pos += tmp->len;
+ }
+
+ err = -EINVAL;
+
+end:
+ kfree(buf);
+
+ return err;
+}
+
+static int kvaser_usb_send_simple_msg(const struct kvaser_usb *dev,
+ u8 msg_id, int channel)
+{
+ struct kvaser_msg msg = {
+ .len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple),
+ .id = msg_id,
+ .u.simple.channel = channel,
+ .u.simple.tid = 0xff,
+ };
+
+ return kvaser_usb_send_msg(dev, &msg);
+}
+
+static int kvaser_usb_get_software_info(struct kvaser_usb *dev)
+{
+ struct kvaser_msg msg;
+ int err;
+
+ err = kvaser_usb_send_simple_msg(dev, CMD_GET_SOFTWARE_INFO, 0);
+ if (err)
+ return err;
+
+ err = kvaser_usb_wait_msg(dev, CMD_GET_SOFTWARE_INFO_REPLY, &msg);
+ if (err)
+ return err;
+
+ dev->fw_version = le32_to_cpu(msg.u.softinfo.fw_version);
+
+ return 0;
+}
+
+static int kvaser_usb_get_card_info(struct kvaser_usb *dev)
+{
+ struct kvaser_msg msg;
+ int err;
+
+ err = kvaser_usb_send_simple_msg(dev, CMD_GET_CARD_INFO, 0);
+ if (err)
+ return err;
+
+ err = kvaser_usb_wait_msg(dev, CMD_GET_CARD_INFO_REPLY, &msg);
+ if (err)
+ return err;
+
+ dev->nchannels = msg.u.cardinfo.nchannels;
+
+ return 0;
+}
+
+static void kvaser_usb_tx_acknowledge(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ struct net_device_stats *stats;
+ struct kvaser_usb_tx_urb_context *context;
+ struct kvaser_usb_net_priv *priv;
+ struct sk_buff *skb;
+ struct can_frame *cf;
+ u8 channel = msg->u.tx_acknowledge.channel;
+ u8 tid = msg->u.tx_acknowledge.tid;
+
+ if (channel >= dev->nchannels) {
+ dev_err(dev->udev->dev.parent,
+ "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 % 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)) {
+ skb = alloc_can_err_skb(priv->netdev, &cf);
+ if (skb) {
+ cf->can_id |= CAN_ERR_RESTARTED;
+ netif_rx(skb);
+
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+ } 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;
+ can_get_echo_skb(priv->netdev, context->echo_index);
+
+ context->echo_index = MAX_TX_URBS;
+ atomic_dec(&priv->active_tx_urbs);
+
+ netif_wake_queue(priv->netdev);
+}
+
+static void kvaser_usb_simple_msg_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);
+}
+
+static int kvaser_usb_simple_msg_async(struct kvaser_usb_net_priv *priv,
+ u8 msg_id)
+{
+ struct kvaser_usb *dev = priv->dev;
+ struct net_device *netdev = priv->netdev;
+ struct kvaser_msg *msg;
+ struct urb *urb;
+ void *buf;
+ int err;
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb) {
+ netdev_err(netdev, "No memory left for URBs\n");
+ return -ENOMEM;
+ }
+
+ buf = kmalloc(sizeof(struct kvaser_msg), GFP_ATOMIC);
+ if (!buf) {
+ netdev_err(netdev, "No memory left for USB buffer\n");
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ msg = (struct kvaser_msg *)buf;
+ msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple);
+ msg->id = msg_id;
+ msg->u.simple.channel = priv->channel;
+
+ usb_fill_bulk_urb(urb, dev->udev,
+ usb_sndbulkpipe(dev->udev,
+ dev->bulk_out->bEndpointAddress),
+ buf, msg->len,
+ kvaser_usb_simple_msg_callback, priv);
+ 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);
+ kfree(buf);
+ return err;
+ }
+
+ usb_free_urb(urb);
+
+ return 0;
+}
+
+static void kvaser_usb_unlink_tx_urbs(struct kvaser_usb_net_priv *priv)
+{
+ int 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;
+}
+
+static void kvaser_usb_rx_error(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ struct net_device_stats *stats;
+ struct kvaser_usb_net_priv *priv;
+ unsigned int new_state;
+ u8 channel, status, txerr, rxerr, error_factor;
+
+ switch (msg->id) {
+ case CMD_CAN_ERROR_EVENT:
+ channel = msg->u.error_event.channel;
+ status = msg->u.error_event.status;
+ txerr = msg->u.error_event.tx_errors_count;
+ rxerr = msg->u.error_event.rx_errors_count;
+ error_factor = msg->u.error_event.error_factor;
+ break;
+ case CMD_LOG_MESSAGE:
+ channel = msg->u.log_message.channel;
+ status = msg->u.log_message.data[0];
+ txerr = msg->u.log_message.data[2];
+ rxerr = msg->u.log_message.data[3];
+ error_factor = msg->u.log_message.data[1];
+ break;
+ case CMD_CHIP_STATE_EVENT:
+ channel = msg->u.chip_state_event.channel;
+ status = msg->u.chip_state_event.status;
+ txerr = msg->u.chip_state_event.tx_errors_count;
+ rxerr = msg->u.chip_state_event.rx_errors_count;
+ error_factor = 0;
+ break;
+ default:
+ dev_err(dev->udev->dev.parent, "Invalid msg id (%d)\n",
+ msg->id);
+ return;
+ }
+
+ if (channel >= dev->nchannels) {
+ dev_err(dev->udev->dev.parent,
+ "Invalid channel number (%d)\n", channel);
+ return;
+ }
+
+ priv = dev->nets[channel];
+ stats = &priv->netdev->stats;
+
+ if (status & M16C_STATE_BUS_RESET) {
+ kvaser_usb_unlink_tx_urbs(priv);
+ return;
+ }
+
+ skb = alloc_can_err_skb(priv->netdev, &cf);
+ if (!skb) {
+ stats->rx_dropped++;
+ return;
+ }
+
+ new_state = priv->can.state;
+
+ netdev_dbg(priv->netdev, "Error status: 0x%02x\n", status);
+
+ if (status & M16C_STATE_BUS_OFF) {
+ cf->can_id |= CAN_ERR_BUSOFF;
+
+ priv->can.can_stats.bus_off++;
+ if (!priv->can.restart_ms)
+ kvaser_usb_simple_msg_async(priv, CMD_STOP_CHIP);
+
+ netif_carrier_off(priv->netdev);
+
+ new_state = CAN_STATE_BUS_OFF;
+ } else if (status & M16C_STATE_BUS_PASSIVE) {
+ if (priv->can.state != CAN_STATE_ERROR_PASSIVE) {
+ cf->can_id |= CAN_ERR_CRTL;
+
+ if (txerr || rxerr)
+ cf->data[1] = (txerr > rxerr)
+ ? CAN_ERR_CRTL_TX_PASSIVE
+ : CAN_ERR_CRTL_RX_PASSIVE;
+ else
+ cf->data[1] = CAN_ERR_CRTL_TX_PASSIVE |
+ CAN_ERR_CRTL_RX_PASSIVE;
+
+ priv->can.can_stats.error_passive++;
+ }
+
+ new_state = CAN_STATE_ERROR_PASSIVE;
+ }
+
+ if (status == M16C_STATE_BUS_ERROR) {
+ if ((priv->can.state < CAN_STATE_ERROR_WARNING) &&
+ ((txerr >= 96) || (rxerr >= 96))) {
+ cf->can_id |= CAN_ERR_CRTL;
+ cf->data[1] = (txerr > rxerr)
+ ? CAN_ERR_CRTL_TX_WARNING
+ : CAN_ERR_CRTL_RX_WARNING;
+
+ priv->can.can_stats.error_warning++;
+ new_state = CAN_STATE_ERROR_WARNING;
+ } else if (priv->can.state > CAN_STATE_ERROR_ACTIVE) {
+ cf->can_id |= CAN_ERR_PROT;
+ cf->data[2] = CAN_ERR_PROT_ACTIVE;
+
+ new_state = CAN_STATE_ERROR_ACTIVE;
+ }
+ }
+
+ if (!status) {
+ cf->can_id |= CAN_ERR_PROT;
+ cf->data[2] = CAN_ERR_PROT_ACTIVE;
+
+ new_state = CAN_STATE_ERROR_ACTIVE;
+ }
+
+ if (priv->can.restart_ms &&
+ (priv->can.state >= CAN_STATE_BUS_OFF) &&
+ (new_state < CAN_STATE_BUS_OFF)) {
+ cf->can_id |= CAN_ERR_RESTARTED;
+ netif_carrier_on(priv->netdev);
+
+ priv->can.can_stats.restarts++;
+ }
+
+ if (error_factor) {
+ priv->can.can_stats.bus_error++;
+ stats->rx_errors++;
+
+ cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT;
+
+ if (error_factor & M16C_EF_ACKE)
+ cf->data[3] |= (CAN_ERR_PROT_LOC_ACK);
+ if (error_factor & M16C_EF_CRCE)
+ cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ |
+ CAN_ERR_PROT_LOC_CRC_DEL);
+ if (error_factor & M16C_EF_FORME)
+ cf->data[2] |= CAN_ERR_PROT_FORM;
+ if (error_factor & M16C_EF_STFE)
+ cf->data[2] |= CAN_ERR_PROT_STUFF;
+ if (error_factor & M16C_EF_BITE0)
+ cf->data[2] |= CAN_ERR_PROT_BIT0;
+ if (error_factor & M16C_EF_BITE1)
+ cf->data[2] |= CAN_ERR_PROT_BIT1;
+ if (error_factor & M16C_EF_TRE)
+ cf->data[2] |= CAN_ERR_PROT_TX;
+ }
+
+ cf->data[6] = txerr;
+ cf->data[7] = rxerr;
+
+ priv->bec.txerr = txerr;
+ priv->bec.rxerr = rxerr;
+
+ priv->can.state = new_state;
+
+ netif_rx(skb);
+
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+}
+
+static void kvaser_usb_rx_can_err(const struct kvaser_usb_net_priv *priv,
+ const struct kvaser_msg *msg)
+{
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ struct net_device_stats *stats = &priv->netdev->stats;
+
+ if (msg->u.rx_can.flag & (MSG_FLAG_ERROR_FRAME |
+ MSG_FLAG_NERR)) {
+ netdev_err(priv->netdev, "Unknow error (flags: 0x%02x)\n",
+ msg->u.rx_can.flag);
+
+ stats->rx_errors++;
+ return;
+ }
+
+ if (msg->u.rx_can.flag & MSG_FLAG_OVERRUN) {
+ skb = alloc_can_err_skb(priv->netdev, &cf);
+ if (!skb) {
+ stats->tx_dropped++;
+ return;
+ }
+
+ cf->can_id |= CAN_ERR_CRTL;
+ cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+
+ stats->rx_over_errors++;
+ stats->rx_errors++;
+
+ netif_rx(skb);
+
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+ }
+}
+
+static void kvaser_usb_rx_can_msg(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ struct kvaser_usb_net_priv *priv;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ struct net_device_stats *stats;
+ u8 channel = msg->u.rx_can.channel;
+
+ if (channel >= dev->nchannels) {
+ dev_err(dev->udev->dev.parent,
+ "Invalid channel number (%d)\n", channel);
+ return;
+ }
+
+ priv = dev->nets[channel];
+ stats = &priv->netdev->stats;
+
+ if (msg->u.rx_can.flag & (MSG_FLAG_ERROR_FRAME | MSG_FLAG_NERR |
+ MSG_FLAG_OVERRUN)) {
+ kvaser_usb_rx_can_err(priv, msg);
+ return;
+ } else if (msg->u.rx_can.flag & ~MSG_FLAG_REMOTE_FRAME) {
+ netdev_warn(priv->netdev,
+ "Unhandled frame (flags: 0x%02x)",
+ msg->u.rx_can.flag);
+ return;
+ }
+
+ skb = alloc_can_skb(priv->netdev, &cf);
+ if (!skb) {
+ stats->tx_dropped++;
+ return;
+ }
+
+ cf->can_id = ((msg->u.rx_can.msg[0] & 0x1f) << 6) |
+ (msg->u.rx_can.msg[1] & 0x3f);
+ cf->can_dlc = get_can_dlc(msg->u.rx_can.msg[5]);
+
+ if (msg->id == CMD_RX_EXT_MESSAGE) {
+ cf->can_id <<= 18;
+ cf->can_id |= ((msg->u.rx_can.msg[2] & 0x0f) << 14) |
+ ((msg->u.rx_can.msg[3] & 0xff) << 6) |
+ (msg->u.rx_can.msg[4] & 0x3f);
+ cf->can_id |= CAN_EFF_FLAG;
+ }
+
+ if (msg->u.rx_can.flag & MSG_FLAG_REMOTE_FRAME)
+ cf->can_id |= CAN_RTR_FLAG;
+ else
+ memcpy(cf->data, &msg->u.rx_can.msg[6], cf->can_dlc);
+
+ netif_rx(skb);
+
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+}
+
+static void kvaser_usb_start_chip_reply(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ struct kvaser_usb_net_priv *priv;
+ u8 channel = msg->u.simple.channel;
+
+ if (channel >= dev->nchannels) {
+ dev_err(dev->udev->dev.parent,
+ "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_stop_chip_reply(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ struct kvaser_usb_net_priv *priv;
+ u8 channel = msg->u.simple.channel;
+
+ if (channel >= dev->nchannels) {
+ dev_err(dev->udev->dev.parent,
+ "Invalid channel number (%d)\n", channel);
+ return;
+ }
+
+ priv = dev->nets[channel];
+
+ complete(&priv->stop_comp);
+}
+
+static void kvaser_usb_handle_message(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ switch (msg->id) {
+ case CMD_START_CHIP_REPLY:
+ kvaser_usb_start_chip_reply(dev, msg);
+ break;
+
+ case CMD_STOP_CHIP_REPLY:
+ kvaser_usb_stop_chip_reply(dev, msg);
+ break;
+
+ case CMD_RX_STD_MESSAGE:
+ case CMD_RX_EXT_MESSAGE:
+ kvaser_usb_rx_can_msg(dev, msg);
+ break;
+
+ case CMD_CHIP_STATE_EVENT:
+ case CMD_CAN_ERROR_EVENT:
+ kvaser_usb_rx_error(dev, msg);
+ break;
+
+ case CMD_LOG_MESSAGE:
+ if (msg->u.log_message.flags & MSG_FLAG_ERROR_FRAME)
+ kvaser_usb_rx_error(dev, msg);
+ break;
+
+ case CMD_TX_ACKNOWLEDGE:
+ kvaser_usb_tx_acknowledge(dev, msg);
+ break;
+
+ default:
+ dev_warn(dev->udev->dev.parent,
+ "Unhandled message (%d)\n", msg->id);
+ break;
+ }
+}
+
+static void kvaser_usb_read_bulk_callback(struct urb *urb)
+{
+ struct kvaser_usb *dev = urb->context;
+ struct kvaser_msg *msg;
+ int pos = 0;
+ int err, i;
+
+ switch (urb->status) {
+ case 0:
+ break;
+ case -ENOENT:
+ 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 - MSG_HEADER_LEN) {
+ msg = urb->transfer_buffer + pos;
+
+ if (!msg->len)
+ break;
+
+ if (pos + msg->len > urb->actual_length) {
+ dev_err(dev->udev->dev.parent, "Format error\n");
+ break;
+ }
+
+ kvaser_usb_handle_message(dev, msg);
+
+ pos += msg->len;
+ }
+
+resubmit_urb:
+ usb_fill_bulk_urb(urb, dev->udev,
+ usb_rcvbulkpipe(dev->udev,
+ dev->bulk_in->bEndpointAddress),
+ urb->transfer_buffer, 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->udev->dev.parent,
+ "Failed resubmitting read bulk urb: %d\n", err);
+ }
+
+ return;
+}
+
+static int kvaser_usb_setup_rx_urbs(struct kvaser_usb *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;
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ dev_warn(dev->udev->dev.parent,
+ "No memory left for URBs\n");
+ 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");
+ usb_free_urb(urb);
+ err = -ENOMEM;
+ break;
+ }
+
+ usb_fill_bulk_urb(urb, dev->udev,
+ usb_rcvbulkpipe(dev->udev,
+ dev->bulk_in->bEndpointAddress),
+ buf, 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, 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->udev->dev.parent,
+ "Cannot setup read URBs, error %d\n", err);
+ return err;
+ } else if (i < MAX_RX_URBS) {
+ dev_warn(dev->udev->dev.parent,
+ "RX performances may be slow\n");
+ }
+
+ dev->rxinitdone = true;
+
+ return 0;
+}
+
+static int kvaser_usb_set_opt_mode(const struct kvaser_usb_net_priv *priv)
+{
+ struct kvaser_msg msg = {
+ .id = CMD_SET_CTRL_MODE,
+ .len = MSG_HEADER_LEN +
+ sizeof(struct kvaser_msg_ctrl_mode),
+ .u.ctrl_mode.tid = 0xff,
+ .u.ctrl_mode.channel = priv->channel,
+ };
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
+ msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_SILENT;
+ else
+ msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_NORMAL;
+
+ return kvaser_usb_send_msg(priv->dev, &msg);
+}
+
+static int kvaser_usb_start_chip(struct kvaser_usb_net_priv *priv)
+{
+ int err;
+
+ init_completion(&priv->start_comp);
+
+ err = kvaser_usb_send_simple_msg(priv->dev, CMD_START_CHIP,
+ priv->channel);
+ if (err)
+ return err;
+
+ if (!wait_for_completion_timeout(&priv->start_comp,
+ msecs_to_jiffies(START_TIMEOUT)))
+ return -ETIMEDOUT;
+
+ 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;
+ int err;
+
+ err = open_candev(netdev);
+ if (err)
+ return err;
+
+ err = kvaser_usb_setup_rx_urbs(dev);
+ if (err)
+ goto error;
+
+ err = kvaser_usb_set_opt_mode(priv);
+ if (err)
+ goto error;
+
+ err = kvaser_usb_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_unlink_all_urbs(struct kvaser_usb *dev)
+{
+ int i;
+
+ 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 < MAX_NET_DEVICES; i++) {
+ struct kvaser_usb_net_priv *priv = dev->nets[i];
+
+ if (priv)
+ kvaser_usb_unlink_tx_urbs(priv);
+ }
+}
+
+static int kvaser_usb_stop_chip(struct kvaser_usb_net_priv *priv)
+{
+ int err;
+
+ init_completion(&priv->stop_comp);
+
+ err = kvaser_usb_send_simple_msg(priv->dev, CMD_STOP_CHIP,
+ priv->channel);
+ if (err)
+ return err;
+
+ if (!wait_for_completion_timeout(&priv->stop_comp,
+ msecs_to_jiffies(STOP_TIMEOUT)))
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int kvaser_usb_flush_queue(struct kvaser_usb_net_priv *priv)
+{
+ struct kvaser_msg msg = {
+ .id = CMD_FLUSH_QUEUE,
+ .len = MSG_HEADER_LEN +
+ sizeof(struct kvaser_msg_flush_queue),
+ .u.flush_queue.channel = priv->channel,
+ .u.flush_queue.flags = 0x00,
+ };
+
+ return kvaser_usb_send_msg(priv->dev, &msg);
+}
+
+static int kvaser_usb_close(struct net_device *netdev)
+{
+ struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
+ struct kvaser_usb *dev = priv->dev;
+ int err;
+
+ netif_stop_queue(netdev);
+
+ err = kvaser_usb_flush_queue(priv);
+ if (err)
+ netdev_warn(netdev, "Cannot flush queue, error %d\n", err);
+
+ if (kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, priv->channel))
+ netdev_warn(netdev, "Cannot reset card, error %d\n", err);
+
+ err = kvaser_usb_stop_chip(priv);
+ if (err)
+ netdev_warn(netdev, "Cannot stop device, error %d\n", err);
+
+ priv->can.state = CAN_STATE_STOPPED;
+ close_candev(priv->netdev);
+
+ return 0;
+}
+
+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;
+ struct net_device_stats *stats = &netdev->stats;
+ struct can_frame *cf = (struct can_frame *)skb->data;
+ struct kvaser_usb_tx_urb_context *context = NULL;
+ struct urb *urb;
+ void *buf;
+ struct kvaser_msg *msg;
+ int i, err;
+ int ret = NETDEV_TX_OK;
+
+ if (can_dropped_invalid_skb(netdev, skb))
+ return NETDEV_TX_OK;
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb) {
+ netdev_err(netdev, "No memory left for URBs\n");
+ stats->tx_dropped++;
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+
+ buf = kmalloc(sizeof(struct kvaser_msg), GFP_ATOMIC);
+ if (!buf) {
+ netdev_err(netdev, "No memory left for USB buffer\n");
+ stats->tx_dropped++;
+ dev_kfree_skb(skb);
+ usb_free_urb(urb);
+ goto nobufmem;
+ }
+
+ msg = buf;
+ msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_tx_can);
+ msg->u.tx_can.flags = 0;
+ msg->u.tx_can.channel = priv->channel;
+
+ if (cf->can_id & CAN_EFF_FLAG) {
+ msg->id = CMD_TX_EXT_MESSAGE;
+ msg->u.tx_can.msg[0] = (cf->can_id >> 24) & 0x1f;
+ msg->u.tx_can.msg[1] = (cf->can_id >> 18) & 0x3f;
+ msg->u.tx_can.msg[2] = (cf->can_id >> 14) & 0x0f;
+ msg->u.tx_can.msg[3] = (cf->can_id >> 6) & 0xff;
+ msg->u.tx_can.msg[4] = cf->can_id & 0x3f;
+ } else {
+ msg->id = CMD_TX_STD_MESSAGE;
+ msg->u.tx_can.msg[0] = (cf->can_id >> 6) & 0x1f;
+ msg->u.tx_can.msg[1] = cf->can_id & 0x3f;
+ }
+
+ msg->u.tx_can.msg[5] = cf->can_dlc;
+ memcpy(&msg->u.tx_can.msg[6], cf->data, cf->can_dlc);
+
+ if (cf->can_id & CAN_RTR_FLAG)
+ msg->u.tx_can.flags |= MSG_FLAG_REMOTE_FRAME;
+
+ for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++) {
+ if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) {
+ context = &priv->tx_contexts[i];
+ break;
+ }
+ }
+
+ if (!context) {
+ netdev_warn(netdev, "cannot find free context\n");
+ ret = NETDEV_TX_BUSY;
+ goto releasebuf;
+ }
+
+ context->priv = priv;
+ context->echo_index = i;
+ context->dlc = cf->can_dlc;
+
+ msg->u.tx_can.tid = context->echo_index;
+
+ usb_fill_bulk_urb(urb, dev->udev,
+ usb_sndbulkpipe(dev->udev,
+ dev->bulk_out->bEndpointAddress),
+ buf, msg->len,
+ kvaser_usb_write_bulk_callback, context);
+ usb_anchor_urb(urb, &priv->tx_submitted);
+
+ can_put_echo_skb(skb, netdev, context->echo_index);
+
+ atomic_inc(&priv->active_tx_urbs);
+
+ if (atomic_read(&priv->active_tx_urbs) >= MAX_TX_URBS)
+ netif_stop_queue(netdev);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (unlikely(err)) {
+ can_free_echo_skb(netdev, context->echo_index);
+
+ atomic_dec(&priv->active_tx_urbs);
+ 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;
+ }
+
+ usb_free_urb(urb);
+
+ return NETDEV_TX_OK;
+
+releasebuf:
+ kfree(buf);
+nobufmem:
+ 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,
+};
+
+static struct can_bittiming_const kvaser_usb_bittiming_const = {
+ .name = "kvaser_usb",
+ .tseg1_min = KVASER_USB_TSEG1_MIN,
+ .tseg1_max = KVASER_USB_TSEG1_MAX,
+ .tseg2_min = KVASER_USB_TSEG2_MIN,
+ .tseg2_max = KVASER_USB_TSEG2_MAX,
+ .sjw_max = KVASER_USB_SJW_MAX,
+ .brp_min = KVASER_USB_BRP_MIN,
+ .brp_max = KVASER_USB_BRP_MAX,
+ .brp_inc = KVASER_USB_BRP_INC,
+};
+
+static int kvaser_usb_set_bittiming(struct net_device *netdev)
+{
+ struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
+ struct can_bittiming *bt = &priv->can.bittiming;
+ struct kvaser_usb *dev = priv->dev;
+ struct kvaser_msg msg = {
+ .id = CMD_SET_BUS_PARAMS,
+ .len = MSG_HEADER_LEN +
+ sizeof(struct kvaser_msg_busparams),
+ .u.busparams.channel = priv->channel,
+ .u.busparams.tid = 0xff,
+ .u.busparams.bitrate = cpu_to_le32(bt->bitrate),
+ .u.busparams.sjw = bt->sjw,
+ .u.busparams.tseg1 = bt->prop_seg + bt->phase_seg1,
+ .u.busparams.tseg2 = bt->phase_seg2,
+ };
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
+ msg.u.busparams.no_samp = 3;
+ else
+ msg.u.busparams.no_samp = 1;
+
+ return kvaser_usb_send_msg(dev, &msg);
+}
+
+static int kvaser_usb_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:
+ err = kvaser_usb_simple_msg_async(priv, CMD_START_CHIP);
+ if (err)
+ return err;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int kvaser_usb_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 void kvaser_usb_remove_interfaces(struct kvaser_usb *dev)
+{
+ int i;
+
+ for (i = 0; i < dev->nchannels; i++) {
+ if (!dev->nets[i])
+ continue;
+
+ unregister_netdev(dev->nets[i]->netdev);
+ }
+
+ kvaser_usb_unlink_all_urbs(dev);
+
+ for (i = 0; i < dev->nchannels; i++) {
+ if (!dev->nets[i])
+ continue;
+
+ free_candev(dev->nets[i]->netdev);
+ }
+}
+
+static int kvaser_usb_init_one(struct usb_interface *intf,
+ const struct usb_device_id *id, int channel)
+{
+ struct kvaser_usb *dev = usb_get_intfdata(intf);
+ struct net_device *netdev;
+ struct kvaser_usb_net_priv *priv;
+ int i, err;
+
+ netdev = alloc_candev(sizeof(*priv), MAX_TX_URBS);
+ if (!netdev) {
+ dev_err(&intf->dev, "Cannot alloc candev\n");
+ return -ENOMEM;
+ }
+
+ priv = netdev_priv(netdev);
+
+ init_completion(&priv->start_comp);
+ init_completion(&priv->stop_comp);
+
+ init_usb_anchor(&priv->tx_submitted);
+ atomic_set(&priv->active_tx_urbs, 0);
+
+ for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++)
+ priv->tx_contexts[i].echo_index = MAX_TX_URBS;
+
+ priv->dev = dev;
+ priv->netdev = netdev;
+ priv->channel = channel;
+
+ priv->can.state = CAN_STATE_STOPPED;
+ priv->can.clock.freq = CAN_USB_CLOCK;
+ priv->can.bittiming_const = &kvaser_usb_bittiming_const;
+ priv->can.do_set_bittiming = kvaser_usb_set_bittiming;
+ priv->can.do_set_mode = kvaser_usb_set_mode;
+ if (id->driver_info & KVASER_HAS_TXRX_ERRORS)
+ priv->can.do_get_berr_counter = kvaser_usb_get_berr_counter;
+ priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES;
+ if (id->driver_info & KVASER_HAS_SILENT_MODE)
+ priv->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY;
+
+ netdev->flags |= IFF_ECHO;
+
+ netdev->netdev_ops = &kvaser_usb_netdev_ops;
+
+ SET_NETDEV_DEV(netdev, &intf->dev);
+
+ dev->nets[channel] = priv;
+
+ err = register_candev(netdev);
+ if (err) {
+ dev_err(&intf->dev, "Failed to register can device\n");
+ free_candev(netdev);
+ dev->nets[channel] = NULL;
+ return err;
+ }
+
+ netdev_dbg(netdev, "device registered\n");
+
+ return 0;
+}
+
+static void kvaser_usb_get_endpoints(const struct usb_interface *intf,
+ struct usb_endpoint_descriptor **in,
+ struct usb_endpoint_descriptor **out)
+{
+ const struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ int i;
+
+ iface_desc = &intf->altsetting[0];
+
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+
+ if (usb_endpoint_is_bulk_in(endpoint))
+ *in = endpoint;
+
+ if (usb_endpoint_is_bulk_out(endpoint))
+ *out = endpoint;
+ }
+}
+
+static int kvaser_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct kvaser_usb *dev;
+ int err = -ENOMEM;
+ int i;
+
+ dev = devm_kzalloc(&intf->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ kvaser_usb_get_endpoints(intf, &dev->bulk_in, &dev->bulk_out);
+ if (!dev->bulk_in || !dev->bulk_out) {
+ 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);
+
+ for (i = 0; i < MAX_NET_DEVICES; i++)
+ kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, i);
+
+ err = kvaser_usb_get_software_info(dev);
+ if (err) {
+ dev_err(&intf->dev,
+ "Cannot get software infos, error %d\n", err);
+ return err;
+ }
+
+ err = kvaser_usb_get_card_info(dev);
+ if (err) {
+ dev_err(&intf->dev,
+ "Cannot get card infos, error %d\n", err);
+ return err;
+ }
+
+ dev_dbg(&intf->dev, "Firmware version: %d.%d.%d\n",
+ ((dev->fw_version >> 24) & 0xff),
+ ((dev->fw_version >> 16) & 0xff),
+ (dev->fw_version & 0xffff));
+
+ for (i = 0; i < dev->nchannels; i++) {
+ err = kvaser_usb_init_one(intf, id, 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_DESCRIPTION("CAN driver for Kvaser CAN/USB devices");
+MODULE_LICENSE("GPL v2");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* Re: [PATCH v5] can: kvaser_usb: Add support for Kvaser CAN/USB devices
2012-10-02 7:16 ` [PATCH v5] " Olivier Sobrie
@ 2012-11-07 20:29 ` Marc Kleine-Budde
2012-11-20 8:46 ` Olivier Sobrie
0 siblings, 1 reply; 43+ messages in thread
From: Marc Kleine-Budde @ 2012-11-07 20:29 UTC (permalink / raw)
To: Olivier Sobrie; +Cc: Wolfgang Grandegger, linux-can, netdev, linux-usb
[-- Attachment #1: Type: text/plain, Size: 48491 bytes --]
On 10/02/2012 09:16 AM, Olivier Sobrie wrote:
> This driver provides support for several Kvaser CAN/USB devices.
> Such kind of devices supports up to three CAN network interfaces.
>
> It has been tested with a Kvaser USB Leaf Light (one network interface)
> connected to a pch_can interface.
> The firmware version of the Kvaser device was 2.5.205.
>
> List of Kvaser devices supported by the driver:
> - 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
>
> Signed-off-by: Daniel Berglund <db@kvaser.com>
> Signed-off-by: Olivier Sobrie <olivier@sobrie.be>
Sorry I forgot about the review.
Looks quite good, some comments inline.
> ---
> Hi,
>
> This new patch fixes the errors pointed out by Marc and Wolfgang.
>
> Changes since v4:
> - add missing usb_free_urb()
> - put error message in a separate function
> - handle return code of kvaser_usb_init_one() in probe function
>
> Olivier
>
> drivers/net/can/usb/Kconfig | 29 +
> drivers/net/can/usb/Makefile | 1 +
> drivers/net/can/usb/kvaser_usb.c | 1596 ++++++++++++++++++++++++++++++++++++++
> 3 files changed, 1626 insertions(+)
> create mode 100644 drivers/net/can/usb/kvaser_usb.c
>
> diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig
> index 0a68768..a4e4bee 100644
> --- a/drivers/net/can/usb/Kconfig
> +++ b/drivers/net/can/usb/Kconfig
> @@ -13,6 +13,35 @@ config CAN_ESD_USB2
> This driver supports the CAN-USB/2 interface
> from esd electronic system design gmbh (http://www.esd.eu).
>
> +config CAN_KVASER_USB
> + tristate "Kvaser CAN/USB interface"
> + ---help---
> + This driver adds support for Kvaser CAN/USB devices like Kvaser
> + Leaf Light.
> +
> + The driver gives 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
> +
> + If unsure, say N.
> +
> + To compile this driver as a module, choose M here: the
> + module will be called kvaser_usb.
> +
> config CAN_PEAK_USB
> tristate "PEAK PCAN-USB/USB Pro interfaces"
> ---help---
> diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile
> index da6d1d3..80a2ee4 100644
> --- a/drivers/net/can/usb/Makefile
> +++ b/drivers/net/can/usb/Makefile
> @@ -4,6 +4,7 @@
>
> obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o
> obj-$(CONFIG_CAN_ESD_USB2) += esd_usb2.o
> +obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o
> obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/
>
> ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
> diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c
> new file mode 100644
> index 0000000..bd48807
> --- /dev/null
> +++ b/drivers/net/can/usb/kvaser_usb.c
> @@ -0,0 +1,1596 @@
> +/*
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation version 2.
> + *
> + * Parts of this driver are based on the following:
> + * - Kvaser linux leaf driver (version 4.78)
> + * - CAN driver for esd CAN-USB/2
> + *
> + * Copyright (C) 2002-2006 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>
> + */
> +
> +#include <linux/init.h>
> +#include <linux/completion.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>
> +
> +#define MAX_TX_URBS 16
> +#define MAX_RX_URBS 4
> +#define START_TIMEOUT 1000 /* msecs */
> +#define STOP_TIMEOUT 1000 /* msecs */
> +#define USB_SEND_TIMEOUT 1000 /* msecs */
> +#define USB_RECV_TIMEOUT 1000 /* msecs */
> +#define RX_BUFFER_SIZE 3072
> +#define CAN_USB_CLOCK 8000000
> +#define MAX_NET_DEVICES 3
> +
> +/* Kvaser USB devices */
> +#define KVASER_VENDOR_ID 0x0bfd
> +#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
> +
> +/* USB devices features */
> +#define KVASER_HAS_SILENT_MODE BIT(0)
> +#define KVASER_HAS_TXRX_ERRORS BIT(1)
> +
> +/* Message header size */
> +#define MSG_HEADER_LEN 2
> +
> +/* 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 */
> +#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)
> +
> +/* Can msg 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_GET_CTRL_MODE 22
> +#define CMD_GET_CTRL_MODE_REPLY 23
> +#define CMD_RESET_CHIP 24
> +#define CMD_RESET_CARD 25
> +#define CMD_START_CHIP 26
> +#define CMD_START_CHIP_REPLY 27
> +#define CMD_STOP_CHIP 28
> +#define CMD_STOP_CHIP_REPLY 29
> +#define CMD_GET_CARD_INFO2 32
> +#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_RESET_ERROR_COUNTER 49
> +#define CMD_TX_ACKNOWLEDGE 50
> +#define CMD_CAN_ERROR_EVENT 51
> +#define CMD_USB_THROTTLE 77
> +#define CMD_LOG_MESSAGE 106
> +
> +/* 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)
> +
> +/* bittiming parameters */
> +#define KVASER_USB_TSEG1_MIN 1
> +#define KVASER_USB_TSEG1_MAX 16
> +#define KVASER_USB_TSEG2_MIN 1
> +#define KVASER_USB_TSEG2_MAX 8
> +#define KVASER_USB_SJW_MAX 4
> +#define KVASER_USB_BRP_MIN 1
> +#define KVASER_USB_BRP_MAX 64
> +#define KVASER_USB_BRP_INC 1
> +
> +/* 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
> +
> +struct kvaser_msg_simple {
> + u8 tid;
> + u8 channel;
> +} __packed;
> +
> +struct kvaser_msg_cardinfo {
> + u8 tid;
> + u8 nchannels;
> + __le32 serial_number;
> + __le32 padding;
> + __le32 clock_resolution;
> + __le32 mfgdate;
> + u8 ean[8];
> + u8 hw_revision;
> + u8 usb_hs_mode;
> + __le16 padding2;
> +} __packed;
> +
> +struct kvaser_msg_cardinfo2 {
> + u8 tid;
> + u8 channel;
> + u8 pcb_id[24];
> + __le32 oem_unlock_code;
> +} __packed;
> +
> +struct kvaser_msg_softinfo {
> + u8 tid;
> + u8 channel;
> + __le32 sw_options;
> + __le32 fw_version;
> + __le16 max_outstanding_tx;
> + __le16 padding[9];
> +} __packed;
> +
> +struct kvaser_msg_busparams {
> + u8 tid;
> + u8 channel;
> + __le32 bitrate;
> + u8 tseg1;
> + u8 tseg2;
> + u8 sjw;
> + u8 no_samp;
> +} __packed;
> +
> +struct kvaser_msg_tx_can {
> + u8 channel;
> + u8 tid;
> + u8 msg[14];
> + u8 padding;
> + u8 flags;
> +} __packed;
> +
> +struct kvaser_msg_rx_can {
> + u8 channel;
> + u8 flag;
> + __le16 time[3];
> + u8 msg[14];
> +} __packed;
> +
> +struct kvaser_msg_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 kvaser_msg_tx_acknowledge {
> + u8 channel;
> + u8 tid;
> + __le16 time[3];
> + u8 flags;
> + u8 time_offset;
> +} __packed;
> +
> +struct kvaser_msg_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 kvaser_msg_ctrl_mode {
> + u8 tid;
> + u8 channel;
> + u8 ctrl_mode;
> + u8 padding[3];
> +} __packed;
> +
> +struct kvaser_msg_flush_queue {
> + u8 tid;
> + u8 channel;
> + u8 flags;
> + u8 padding[3];
> +} __packed;
> +
> +struct kvaser_msg_log_message {
> + u8 channel;
> + u8 flags;
> + __le16 time[3];
> + u8 dlc;
> + u8 time_offset;
> + __le32 id;
> + u8 data[8];
> +} __packed;
> +
> +struct kvaser_msg {
> + u8 len;
> + u8 id;
> + union {
> + struct kvaser_msg_simple simple;
> + struct kvaser_msg_cardinfo cardinfo;
> + struct kvaser_msg_cardinfo2 cardinfo2;
> + struct kvaser_msg_softinfo softinfo;
> + struct kvaser_msg_busparams busparams;
> + struct kvaser_msg_tx_can tx_can;
> + struct kvaser_msg_rx_can rx_can;
> + struct kvaser_msg_chip_state_event chip_state_event;
> + struct kvaser_msg_tx_acknowledge tx_acknowledge;
> + struct kvaser_msg_error_event error_event;
> + struct kvaser_msg_ctrl_mode ctrl_mode;
> + struct kvaser_msg_flush_queue flush_queue;
> + struct kvaser_msg_log_message log_message;
> + } u;
> +} __packed;
> +
> +struct kvaser_usb_tx_urb_context {
> + struct kvaser_usb_net_priv *priv;
> + u32 echo_index;
> + int dlc;
> +};
> +
> +struct kvaser_usb {
> + struct usb_device *udev;
> + struct kvaser_usb_net_priv *nets[MAX_NET_DEVICES];
> +
> + struct usb_endpoint_descriptor *bulk_in, *bulk_out;
> + struct usb_anchor rx_submitted;
> +
> + u32 fw_version;
> + unsigned int nchannels;
> +
> + bool rxinitdone;
> + void *rxbuf[MAX_RX_URBS];
> + dma_addr_t rxbuf_dma[MAX_RX_URBS];
> +};
> +
> +struct kvaser_usb_net_priv {
> + struct can_priv can;
> +
> + atomic_t active_tx_urbs;
> + struct usb_anchor tx_submitted;
> + struct kvaser_usb_tx_urb_context tx_contexts[MAX_TX_URBS];
> +
> + struct completion start_comp, stop_comp;
> +
> + struct kvaser_usb *dev;
> + struct net_device *netdev;
> + int channel;
> +
> + struct can_berr_counter bec;
> +};
> +
> +static struct usb_device_id kvaser_usb_table[] = {
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_DEVEL_PRODUCT_ID) },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_PRODUCT_ID) },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> + KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> + KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LS_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> + KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_SWC_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> + KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LIN_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> + KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_LS_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> + KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_SWC_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> + KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_DEVEL_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> + KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSHS_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> + KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_UPRO_HSHS_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_GI_PRODUCT_ID) },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_OBDII_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> + KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSLS_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_CH_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_BLACKBIRD_SPRO_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_MERCURY_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_LEAF_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_CAN_R_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS },
> + { }
> +};
> +MODULE_DEVICE_TABLE(usb, kvaser_usb_table);
> +
> +static inline int kvaser_usb_send_msg(const struct kvaser_usb *dev,
> + struct kvaser_msg *msg)
> +{
> + int actual_len;
> +
> + return usb_bulk_msg(dev->udev,
> + usb_sndbulkpipe(dev->udev,
> + dev->bulk_out->bEndpointAddress),
> + msg, msg->len, &actual_len,
> + USB_SEND_TIMEOUT);
> +}
> +
> +static int kvaser_usb_wait_msg(const struct kvaser_usb *dev, u8 id,
> + struct kvaser_msg *msg)
> +{
> + struct kvaser_msg *tmp;
> + void *buf;
> + int actual_len;
> + int err;
> + int pos = 0;
> +
> + buf = kzalloc(RX_BUFFER_SIZE, GFP_KERNEL);
> + if (!buf)
> + return -ENOMEM;
> +
> + err = usb_bulk_msg(dev->udev,
> + usb_rcvbulkpipe(dev->udev,
> + dev->bulk_in->bEndpointAddress),
> + buf, RX_BUFFER_SIZE, &actual_len,
> + USB_RECV_TIMEOUT);
> + if (err < 0)
> + goto end;
> +
> + while (pos <= actual_len - MSG_HEADER_LEN) {
> + tmp = buf + pos;
> +
> + if (!tmp->len)
> + break;
> +
> + if (pos + tmp->len > actual_len) {
> + dev_err(dev->udev->dev.parent, "Format error\n");
> + break;
> + }
> +
> + if (tmp->id == id) {
> + memcpy(msg, tmp, tmp->len);
> + goto end;
> + }
> +
> + pos += tmp->len;
> + }
> +
> + err = -EINVAL;
> +
> +end:
> + kfree(buf);
> +
> + return err;
> +}
> +
> +static int kvaser_usb_send_simple_msg(const struct kvaser_usb *dev,
> + u8 msg_id, int channel)
> +{
> + struct kvaser_msg msg = {
> + .len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple),
> + .id = msg_id,
> + .u.simple.channel = channel,
> + .u.simple.tid = 0xff,
> + };
> +
> + return kvaser_usb_send_msg(dev, &msg);
> +}
> +
> +static int kvaser_usb_get_software_info(struct kvaser_usb *dev)
> +{
> + struct kvaser_msg msg;
> + int err;
> +
> + err = kvaser_usb_send_simple_msg(dev, CMD_GET_SOFTWARE_INFO, 0);
> + if (err)
> + return err;
> +
> + err = kvaser_usb_wait_msg(dev, CMD_GET_SOFTWARE_INFO_REPLY, &msg);
> + if (err)
> + return err;
> +
> + dev->fw_version = le32_to_cpu(msg.u.softinfo.fw_version);
> +
> + return 0;
> +}
> +
> +static int kvaser_usb_get_card_info(struct kvaser_usb *dev)
> +{
> + struct kvaser_msg msg;
> + int err;
> +
> + err = kvaser_usb_send_simple_msg(dev, CMD_GET_CARD_INFO, 0);
> + if (err)
> + return err;
> +
> + err = kvaser_usb_wait_msg(dev, CMD_GET_CARD_INFO_REPLY, &msg);
> + if (err)
> + return err;
> +
> + dev->nchannels = msg.u.cardinfo.nchannels;
> +
> + return 0;
> +}
> +
> +static void kvaser_usb_tx_acknowledge(const struct kvaser_usb *dev,
> + const struct kvaser_msg *msg)
> +{
> + struct net_device_stats *stats;
> + struct kvaser_usb_tx_urb_context *context;
> + struct kvaser_usb_net_priv *priv;
> + struct sk_buff *skb;
> + struct can_frame *cf;
> + u8 channel = msg->u.tx_acknowledge.channel;
> + u8 tid = msg->u.tx_acknowledge.tid;
> +
> + if (channel >= dev->nchannels) {
> + dev_err(dev->udev->dev.parent,
> + "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 % 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)) {
> + skb = alloc_can_err_skb(priv->netdev, &cf);
> + if (skb) {
> + cf->can_id |= CAN_ERR_RESTARTED;
> + netif_rx(skb);
> +
> + stats->rx_packets++;
> + stats->rx_bytes += cf->can_dlc;
> + } 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;
> + can_get_echo_skb(priv->netdev, context->echo_index);
> +
> + context->echo_index = MAX_TX_URBS;
> + atomic_dec(&priv->active_tx_urbs);
> +
> + netif_wake_queue(priv->netdev);
> +}
> +
> +static void kvaser_usb_simple_msg_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);
> +}
> +
> +static int kvaser_usb_simple_msg_async(struct kvaser_usb_net_priv *priv,
> + u8 msg_id)
> +{
> + struct kvaser_usb *dev = priv->dev;
> + struct net_device *netdev = priv->netdev;
> + struct kvaser_msg *msg;
> + struct urb *urb;
> + void *buf;
> + int err;
> +
> + urb = usb_alloc_urb(0, GFP_ATOMIC);
> + if (!urb) {
> + netdev_err(netdev, "No memory left for URBs\n");
> + return -ENOMEM;
> + }
> +
> + buf = kmalloc(sizeof(struct kvaser_msg), GFP_ATOMIC);
> + if (!buf) {
> + netdev_err(netdev, "No memory left for USB buffer\n");
> + usb_free_urb(urb);
> + return -ENOMEM;
> + }
> +
> + msg = (struct kvaser_msg *)buf;
> + msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple);
> + msg->id = msg_id;
> + msg->u.simple.channel = priv->channel;
> +
> + usb_fill_bulk_urb(urb, dev->udev,
> + usb_sndbulkpipe(dev->udev,
> + dev->bulk_out->bEndpointAddress),
> + buf, msg->len,
> + kvaser_usb_simple_msg_callback, priv);
> + 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);
> + kfree(buf);
> + return err;
> + }
> +
> + usb_free_urb(urb);
> +
> + return 0;
> +}
> +
> +static void kvaser_usb_unlink_tx_urbs(struct kvaser_usb_net_priv *priv)
> +{
> + int 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;
> +}
> +
> +static void kvaser_usb_rx_error(const struct kvaser_usb *dev,
> + const struct kvaser_msg *msg)
> +{
> + struct can_frame *cf;
> + struct sk_buff *skb;
> + struct net_device_stats *stats;
> + struct kvaser_usb_net_priv *priv;
> + unsigned int new_state;
> + u8 channel, status, txerr, rxerr, error_factor;
> +
> + switch (msg->id) {
> + case CMD_CAN_ERROR_EVENT:
> + channel = msg->u.error_event.channel;
> + status = msg->u.error_event.status;
> + txerr = msg->u.error_event.tx_errors_count;
> + rxerr = msg->u.error_event.rx_errors_count;
> + error_factor = msg->u.error_event.error_factor;
> + break;
> + case CMD_LOG_MESSAGE:
> + channel = msg->u.log_message.channel;
> + status = msg->u.log_message.data[0];
> + txerr = msg->u.log_message.data[2];
> + rxerr = msg->u.log_message.data[3];
> + error_factor = msg->u.log_message.data[1];
> + break;
> + case CMD_CHIP_STATE_EVENT:
> + channel = msg->u.chip_state_event.channel;
> + status = msg->u.chip_state_event.status;
> + txerr = msg->u.chip_state_event.tx_errors_count;
> + rxerr = msg->u.chip_state_event.rx_errors_count;
> + error_factor = 0;
> + break;
> + default:
> + dev_err(dev->udev->dev.parent, "Invalid msg id (%d)\n",
> + msg->id);
> + return;
> + }
> +
> + if (channel >= dev->nchannels) {
> + dev_err(dev->udev->dev.parent,
> + "Invalid channel number (%d)\n", channel);
> + return;
> + }
> +
> + priv = dev->nets[channel];
> + stats = &priv->netdev->stats;
> +
> + if (status & M16C_STATE_BUS_RESET) {
> + kvaser_usb_unlink_tx_urbs(priv);
> + return;
> + }
> +
> + skb = alloc_can_err_skb(priv->netdev, &cf);
> + if (!skb) {
> + stats->rx_dropped++;
> + return;
> + }
> +
> + new_state = priv->can.state;
> +
> + netdev_dbg(priv->netdev, "Error status: 0x%02x\n", status);
> +
> + if (status & M16C_STATE_BUS_OFF) {
> + cf->can_id |= CAN_ERR_BUSOFF;
> +
> + priv->can.can_stats.bus_off++;
> + if (!priv->can.restart_ms)
> + kvaser_usb_simple_msg_async(priv, CMD_STOP_CHIP);
> +
> + netif_carrier_off(priv->netdev);
> +
> + new_state = CAN_STATE_BUS_OFF;
> + } else if (status & M16C_STATE_BUS_PASSIVE) {
> + if (priv->can.state != CAN_STATE_ERROR_PASSIVE) {
> + cf->can_id |= CAN_ERR_CRTL;
> +
> + if (txerr || rxerr)
> + cf->data[1] = (txerr > rxerr)
> + ? CAN_ERR_CRTL_TX_PASSIVE
> + : CAN_ERR_CRTL_RX_PASSIVE;
> + else
> + cf->data[1] = CAN_ERR_CRTL_TX_PASSIVE |
> + CAN_ERR_CRTL_RX_PASSIVE;
> +
> + priv->can.can_stats.error_passive++;
> + }
> +
> + new_state = CAN_STATE_ERROR_PASSIVE;
> + }
> +
> + if (status == M16C_STATE_BUS_ERROR) {
> + if ((priv->can.state < CAN_STATE_ERROR_WARNING) &&
> + ((txerr >= 96) || (rxerr >= 96))) {
> + cf->can_id |= CAN_ERR_CRTL;
> + cf->data[1] = (txerr > rxerr)
> + ? CAN_ERR_CRTL_TX_WARNING
> + : CAN_ERR_CRTL_RX_WARNING;
> +
> + priv->can.can_stats.error_warning++;
> + new_state = CAN_STATE_ERROR_WARNING;
> + } else if (priv->can.state > CAN_STATE_ERROR_ACTIVE) {
> + cf->can_id |= CAN_ERR_PROT;
> + cf->data[2] = CAN_ERR_PROT_ACTIVE;
> +
> + new_state = CAN_STATE_ERROR_ACTIVE;
> + }
> + }
> +
> + if (!status) {
> + cf->can_id |= CAN_ERR_PROT;
> + cf->data[2] = CAN_ERR_PROT_ACTIVE;
> +
> + new_state = CAN_STATE_ERROR_ACTIVE;
> + }
> +
> + if (priv->can.restart_ms &&
> + (priv->can.state >= CAN_STATE_BUS_OFF) &&
> + (new_state < CAN_STATE_BUS_OFF)) {
> + cf->can_id |= CAN_ERR_RESTARTED;
> + netif_carrier_on(priv->netdev);
> +
> + priv->can.can_stats.restarts++;
> + }
> +
> + if (error_factor) {
> + priv->can.can_stats.bus_error++;
> + stats->rx_errors++;
> +
> + cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT;
> +
> + if (error_factor & M16C_EF_ACKE)
> + cf->data[3] |= (CAN_ERR_PROT_LOC_ACK);
> + if (error_factor & M16C_EF_CRCE)
> + cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ |
> + CAN_ERR_PROT_LOC_CRC_DEL);
> + if (error_factor & M16C_EF_FORME)
> + cf->data[2] |= CAN_ERR_PROT_FORM;
> + if (error_factor & M16C_EF_STFE)
> + cf->data[2] |= CAN_ERR_PROT_STUFF;
> + if (error_factor & M16C_EF_BITE0)
> + cf->data[2] |= CAN_ERR_PROT_BIT0;
> + if (error_factor & M16C_EF_BITE1)
> + cf->data[2] |= CAN_ERR_PROT_BIT1;
> + if (error_factor & M16C_EF_TRE)
> + cf->data[2] |= CAN_ERR_PROT_TX;
> + }
> +
> + cf->data[6] = txerr;
> + cf->data[7] = rxerr;
> +
> + priv->bec.txerr = txerr;
> + priv->bec.rxerr = rxerr;
> +
> + priv->can.state = new_state;
> +
> + netif_rx(skb);
> +
> + stats->rx_packets++;
> + stats->rx_bytes += cf->can_dlc;
> +}
> +
> +static void kvaser_usb_rx_can_err(const struct kvaser_usb_net_priv *priv,
> + const struct kvaser_msg *msg)
> +{
> + struct can_frame *cf;
> + struct sk_buff *skb;
> + struct net_device_stats *stats = &priv->netdev->stats;
> +
> + if (msg->u.rx_can.flag & (MSG_FLAG_ERROR_FRAME |
> + MSG_FLAG_NERR)) {
> + netdev_err(priv->netdev, "Unknow error (flags: 0x%02x)\n",
> + msg->u.rx_can.flag);
> +
> + stats->rx_errors++;
> + return;
> + }
> +
> + if (msg->u.rx_can.flag & MSG_FLAG_OVERRUN) {
> + skb = alloc_can_err_skb(priv->netdev, &cf);
> + if (!skb) {
> + stats->tx_dropped++;
Should be rx, as we are in the rx function.
> + return;
> + }
> +
> + cf->can_id |= CAN_ERR_CRTL;
> + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
> +
> + stats->rx_over_errors++;
> + stats->rx_errors++;
> +
> + netif_rx(skb);
> +
> + stats->rx_packets++;
> + stats->rx_bytes += cf->can_dlc;
> + }
> +}
> +
> +static void kvaser_usb_rx_can_msg(const struct kvaser_usb *dev,
> + const struct kvaser_msg *msg)
> +{
> + struct kvaser_usb_net_priv *priv;
> + struct can_frame *cf;
> + struct sk_buff *skb;
> + struct net_device_stats *stats;
> + u8 channel = msg->u.rx_can.channel;
> +
> + if (channel >= dev->nchannels) {
> + dev_err(dev->udev->dev.parent,
> + "Invalid channel number (%d)\n", channel);
> + return;
> + }
> +
> + priv = dev->nets[channel];
> + stats = &priv->netdev->stats;
> +
> + if (msg->u.rx_can.flag & (MSG_FLAG_ERROR_FRAME | MSG_FLAG_NERR |
> + MSG_FLAG_OVERRUN)) {
> + kvaser_usb_rx_can_err(priv, msg);
> + return;
> + } else if (msg->u.rx_can.flag & ~MSG_FLAG_REMOTE_FRAME) {
> + netdev_warn(priv->netdev,
> + "Unhandled frame (flags: 0x%02x)",
> + msg->u.rx_can.flag);
> + return;
> + }
> +
> + skb = alloc_can_skb(priv->netdev, &cf);
> + if (!skb) {
> + stats->tx_dropped++;
> + return;
> + }
> +
> + cf->can_id = ((msg->u.rx_can.msg[0] & 0x1f) << 6) |
> + (msg->u.rx_can.msg[1] & 0x3f);
> + cf->can_dlc = get_can_dlc(msg->u.rx_can.msg[5]);
> +
> + if (msg->id == CMD_RX_EXT_MESSAGE) {
> + cf->can_id <<= 18;
> + cf->can_id |= ((msg->u.rx_can.msg[2] & 0x0f) << 14) |
> + ((msg->u.rx_can.msg[3] & 0xff) << 6) |
> + (msg->u.rx_can.msg[4] & 0x3f);
> + cf->can_id |= CAN_EFF_FLAG;
> + }
> +
> + if (msg->u.rx_can.flag & MSG_FLAG_REMOTE_FRAME)
> + cf->can_id |= CAN_RTR_FLAG;
> + else
> + memcpy(cf->data, &msg->u.rx_can.msg[6], cf->can_dlc);
> +
> + netif_rx(skb);
> +
> + stats->rx_packets++;
> + stats->rx_bytes += cf->can_dlc;
> +}
> +
> +static void kvaser_usb_start_chip_reply(const struct kvaser_usb *dev,
> + const struct kvaser_msg *msg)
> +{
> + struct kvaser_usb_net_priv *priv;
> + u8 channel = msg->u.simple.channel;
> +
> + if (channel >= dev->nchannels) {
> + dev_err(dev->udev->dev.parent,
> + "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_stop_chip_reply(const struct kvaser_usb *dev,
> + const struct kvaser_msg *msg)
> +{
> + struct kvaser_usb_net_priv *priv;
> + u8 channel = msg->u.simple.channel;
> +
> + if (channel >= dev->nchannels) {
> + dev_err(dev->udev->dev.parent,
> + "Invalid channel number (%d)\n", channel);
> + return;
> + }
> +
> + priv = dev->nets[channel];
> +
> + complete(&priv->stop_comp);
> +}
> +
> +static void kvaser_usb_handle_message(const struct kvaser_usb *dev,
> + const struct kvaser_msg *msg)
> +{
> + switch (msg->id) {
> + case CMD_START_CHIP_REPLY:
> + kvaser_usb_start_chip_reply(dev, msg);
> + break;
> +
> + case CMD_STOP_CHIP_REPLY:
> + kvaser_usb_stop_chip_reply(dev, msg);
> + break;
> +
> + case CMD_RX_STD_MESSAGE:
> + case CMD_RX_EXT_MESSAGE:
> + kvaser_usb_rx_can_msg(dev, msg);
> + break;
> +
> + case CMD_CHIP_STATE_EVENT:
> + case CMD_CAN_ERROR_EVENT:
> + kvaser_usb_rx_error(dev, msg);
> + break;
> +
> + case CMD_LOG_MESSAGE:
> + if (msg->u.log_message.flags & MSG_FLAG_ERROR_FRAME)
> + kvaser_usb_rx_error(dev, msg);
> + break;
> +
> + case CMD_TX_ACKNOWLEDGE:
> + kvaser_usb_tx_acknowledge(dev, msg);
> + break;
> +
> + default:
> + dev_warn(dev->udev->dev.parent,
> + "Unhandled message (%d)\n", msg->id);
> + break;
> + }
> +}
> +
> +static void kvaser_usb_read_bulk_callback(struct urb *urb)
> +{
> + struct kvaser_usb *dev = urb->context;
> + struct kvaser_msg *msg;
> + int pos = 0;
> + int err, i;
> +
> + switch (urb->status) {
> + case 0:
> + break;
> + case -ENOENT:
> + 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 - MSG_HEADER_LEN) {
> + msg = urb->transfer_buffer + pos;
> +
> + if (!msg->len)
> + break;
> +
> + if (pos + msg->len > urb->actual_length) {
> + dev_err(dev->udev->dev.parent, "Format error\n");
> + break;
> + }
> +
> + kvaser_usb_handle_message(dev, msg);
> +
> + pos += msg->len;
> + }
> +
> +resubmit_urb:
> + usb_fill_bulk_urb(urb, dev->udev,
> + usb_rcvbulkpipe(dev->udev,
> + dev->bulk_in->bEndpointAddress),
> + urb->transfer_buffer, 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->udev->dev.parent,
> + "Failed resubmitting read bulk urb: %d\n", err);
> + }
> +
> + return;
> +}
> +
> +static int kvaser_usb_setup_rx_urbs(struct kvaser_usb *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;
> +
> + urb = usb_alloc_urb(0, GFP_KERNEL);
> + if (!urb) {
> + dev_warn(dev->udev->dev.parent,
> + "No memory left for URBs\n");
> + 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");
> + usb_free_urb(urb);
> + err = -ENOMEM;
> + break;
> + }
> +
> + usb_fill_bulk_urb(urb, dev->udev,
> + usb_rcvbulkpipe(dev->udev,
> + dev->bulk_in->bEndpointAddress),
> + buf, 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, 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->udev->dev.parent,
> + "Cannot setup read URBs, error %d\n", err);
> + return err;
> + } else if (i < MAX_RX_URBS) {
> + dev_warn(dev->udev->dev.parent,
> + "RX performances may be slow\n");
> + }
> +
> + dev->rxinitdone = true;
> +
> + return 0;
> +}
> +
> +static int kvaser_usb_set_opt_mode(const struct kvaser_usb_net_priv *priv)
> +{
> + struct kvaser_msg msg = {
> + .id = CMD_SET_CTRL_MODE,
> + .len = MSG_HEADER_LEN +
> + sizeof(struct kvaser_msg_ctrl_mode),
> + .u.ctrl_mode.tid = 0xff,
> + .u.ctrl_mode.channel = priv->channel,
> + };
> +
> + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
> + msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_SILENT;
> + else
> + msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_NORMAL;
> +
> + return kvaser_usb_send_msg(priv->dev, &msg);
> +}
> +
> +static int kvaser_usb_start_chip(struct kvaser_usb_net_priv *priv)
> +{
> + int err;
> +
> + init_completion(&priv->start_comp);
> +
> + err = kvaser_usb_send_simple_msg(priv->dev, CMD_START_CHIP,
> + priv->channel);
> + if (err)
> + return err;
> +
> + if (!wait_for_completion_timeout(&priv->start_comp,
> + msecs_to_jiffies(START_TIMEOUT)))
> + return -ETIMEDOUT;
> +
> + 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;
> + int err;
> +
> + err = open_candev(netdev);
> + if (err)
> + return err;
> +
> + err = kvaser_usb_setup_rx_urbs(dev);
> + if (err)
> + goto error;
> +
> + err = kvaser_usb_set_opt_mode(priv);
> + if (err)
> + goto error;
> +
> + err = kvaser_usb_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_unlink_all_urbs(struct kvaser_usb *dev)
> +{
> + int i;
> +
> + 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 < MAX_NET_DEVICES; i++) {
> + struct kvaser_usb_net_priv *priv = dev->nets[i];
> +
> + if (priv)
> + kvaser_usb_unlink_tx_urbs(priv);
> + }
> +}
> +
> +static int kvaser_usb_stop_chip(struct kvaser_usb_net_priv *priv)
> +{
> + int err;
> +
> + init_completion(&priv->stop_comp);
> +
> + err = kvaser_usb_send_simple_msg(priv->dev, CMD_STOP_CHIP,
> + priv->channel);
> + if (err)
> + return err;
> +
> + if (!wait_for_completion_timeout(&priv->stop_comp,
> + msecs_to_jiffies(STOP_TIMEOUT)))
> + return -ETIMEDOUT;
> +
> + return 0;
> +}
> +
> +static int kvaser_usb_flush_queue(struct kvaser_usb_net_priv *priv)
> +{
> + struct kvaser_msg msg = {
> + .id = CMD_FLUSH_QUEUE,
> + .len = MSG_HEADER_LEN +
> + sizeof(struct kvaser_msg_flush_queue),
> + .u.flush_queue.channel = priv->channel,
> + .u.flush_queue.flags = 0x00,
> + };
> +
> + return kvaser_usb_send_msg(priv->dev, &msg);
> +}
> +
> +static int kvaser_usb_close(struct net_device *netdev)
> +{
> + struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
> + struct kvaser_usb *dev = priv->dev;
> + int err;
> +
> + netif_stop_queue(netdev);
> +
> + err = kvaser_usb_flush_queue(priv);
> + if (err)
> + netdev_warn(netdev, "Cannot flush queue, error %d\n", err);
> +
> + if (kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, priv->channel))
> + netdev_warn(netdev, "Cannot reset card, error %d\n", err);
> +
> + err = kvaser_usb_stop_chip(priv);
> + if (err)
> + netdev_warn(netdev, "Cannot stop device, error %d\n", err);
> +
> + priv->can.state = CAN_STATE_STOPPED;
> + close_candev(priv->netdev);
> +
> + return 0;
> +}
> +
> +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;
> + struct net_device_stats *stats = &netdev->stats;
> + struct can_frame *cf = (struct can_frame *)skb->data;
> + struct kvaser_usb_tx_urb_context *context = NULL;
> + struct urb *urb;
> + void *buf;
> + struct kvaser_msg *msg;
> + int i, err;
> + int ret = NETDEV_TX_OK;
> +
> + if (can_dropped_invalid_skb(netdev, skb))
> + return NETDEV_TX_OK;
> +
> + urb = usb_alloc_urb(0, GFP_ATOMIC);
> + if (!urb) {
> + netdev_err(netdev, "No memory left for URBs\n");
> + stats->tx_dropped++;
Move the dev_kfree_skb to the end and goto there.
> + dev_kfree_skb(skb);
> + return NETDEV_TX_OK;
> + }
> +
> + buf = kmalloc(sizeof(struct kvaser_msg), GFP_ATOMIC);
> + if (!buf) {
> + netdev_err(netdev, "No memory left for USB buffer\n");
> + stats->tx_dropped++;
You cann usb_free_urb twice...here and in the error handling at the end.
> + dev_kfree_skb(skb);
> + usb_free_urb(urb);
> + goto nobufmem;
> + }
> +
> + msg = buf;
> + msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_tx_can);
> + msg->u.tx_can.flags = 0;
> + msg->u.tx_can.channel = priv->channel;
> +
> + if (cf->can_id & CAN_EFF_FLAG) {
> + msg->id = CMD_TX_EXT_MESSAGE;
> + msg->u.tx_can.msg[0] = (cf->can_id >> 24) & 0x1f;
> + msg->u.tx_can.msg[1] = (cf->can_id >> 18) & 0x3f;
> + msg->u.tx_can.msg[2] = (cf->can_id >> 14) & 0x0f;
> + msg->u.tx_can.msg[3] = (cf->can_id >> 6) & 0xff;
> + msg->u.tx_can.msg[4] = cf->can_id & 0x3f;
> + } else {
> + msg->id = CMD_TX_STD_MESSAGE;
> + msg->u.tx_can.msg[0] = (cf->can_id >> 6) & 0x1f;
> + msg->u.tx_can.msg[1] = cf->can_id & 0x3f;
> + }
> +
> + msg->u.tx_can.msg[5] = cf->can_dlc;
> + memcpy(&msg->u.tx_can.msg[6], cf->data, cf->can_dlc);
> +
> + if (cf->can_id & CAN_RTR_FLAG)
> + msg->u.tx_can.flags |= MSG_FLAG_REMOTE_FRAME;
> +
> + for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++) {
> + if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) {
> + context = &priv->tx_contexts[i];
> + break;
> + }
> + }
> +
> + if (!context) {
> + netdev_warn(netdev, "cannot find free context\n");
> + ret = NETDEV_TX_BUSY;
> + goto releasebuf;
> + }
> +
> + context->priv = priv;
> + context->echo_index = i;
> + context->dlc = cf->can_dlc;
> +
> + msg->u.tx_can.tid = context->echo_index;
> +
> + usb_fill_bulk_urb(urb, dev->udev,
> + usb_sndbulkpipe(dev->udev,
> + dev->bulk_out->bEndpointAddress),
> + buf, msg->len,
> + kvaser_usb_write_bulk_callback, context);
> + usb_anchor_urb(urb, &priv->tx_submitted);
> +
> + can_put_echo_skb(skb, netdev, context->echo_index);
> +
> + atomic_inc(&priv->active_tx_urbs);
> +
> + if (atomic_read(&priv->active_tx_urbs) >= MAX_TX_URBS)
> + netif_stop_queue(netdev);
> +
> + err = usb_submit_urb(urb, GFP_ATOMIC);
> + if (unlikely(err)) {
> + can_free_echo_skb(netdev, context->echo_index);
> +
> + atomic_dec(&priv->active_tx_urbs);
> + 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;
> + }
> +
> + usb_free_urb(urb);
> +
> + return NETDEV_TX_OK;
> +
> +releasebuf:
> + kfree(buf);
> +nobufmem:
> + 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,
> +};
> +
> +static struct can_bittiming_const kvaser_usb_bittiming_const = {
> + .name = "kvaser_usb",
> + .tseg1_min = KVASER_USB_TSEG1_MIN,
> + .tseg1_max = KVASER_USB_TSEG1_MAX,
> + .tseg2_min = KVASER_USB_TSEG2_MIN,
> + .tseg2_max = KVASER_USB_TSEG2_MAX,
> + .sjw_max = KVASER_USB_SJW_MAX,
> + .brp_min = KVASER_USB_BRP_MIN,
> + .brp_max = KVASER_USB_BRP_MAX,
> + .brp_inc = KVASER_USB_BRP_INC,
> +};
> +
> +static int kvaser_usb_set_bittiming(struct net_device *netdev)
> +{
> + struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
> + struct can_bittiming *bt = &priv->can.bittiming;
> + struct kvaser_usb *dev = priv->dev;
> + struct kvaser_msg msg = {
> + .id = CMD_SET_BUS_PARAMS,
> + .len = MSG_HEADER_LEN +
> + sizeof(struct kvaser_msg_busparams),
> + .u.busparams.channel = priv->channel,
> + .u.busparams.tid = 0xff,
> + .u.busparams.bitrate = cpu_to_le32(bt->bitrate),
> + .u.busparams.sjw = bt->sjw,
> + .u.busparams.tseg1 = bt->prop_seg + bt->phase_seg1,
> + .u.busparams.tseg2 = bt->phase_seg2,
> + };
> +
> + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
> + msg.u.busparams.no_samp = 3;
> + else
> + msg.u.busparams.no_samp = 1;
> +
> + return kvaser_usb_send_msg(dev, &msg);
> +}
> +
> +static int kvaser_usb_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:
> + err = kvaser_usb_simple_msg_async(priv, CMD_START_CHIP);
> + if (err)
> + return err;
> + break;
> + default:
> + return -EOPNOTSUPP;
> + }
> +
> + return 0;
> +}
> +
> +static int kvaser_usb_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 void kvaser_usb_remove_interfaces(struct kvaser_usb *dev)
> +{
> + int i;
> +
> + for (i = 0; i < dev->nchannels; i++) {
> + if (!dev->nets[i])
> + continue;
> +
> + unregister_netdev(dev->nets[i]->netdev);
> + }
> +
> + kvaser_usb_unlink_all_urbs(dev);
> +
> + for (i = 0; i < dev->nchannels; i++) {
> + if (!dev->nets[i])
> + continue;
> +
> + free_candev(dev->nets[i]->netdev);
> + }
> +}
> +
> +static int kvaser_usb_init_one(struct usb_interface *intf,
> + const struct usb_device_id *id, int channel)
> +{
> + struct kvaser_usb *dev = usb_get_intfdata(intf);
> + struct net_device *netdev;
> + struct kvaser_usb_net_priv *priv;
> + int i, err;
> +
> + netdev = alloc_candev(sizeof(*priv), MAX_TX_URBS);
> + if (!netdev) {
> + dev_err(&intf->dev, "Cannot alloc candev\n");
> + return -ENOMEM;
> + }
> +
> + priv = netdev_priv(netdev);
> +
> + init_completion(&priv->start_comp);
> + init_completion(&priv->stop_comp);
> +
> + init_usb_anchor(&priv->tx_submitted);
> + atomic_set(&priv->active_tx_urbs, 0);
> +
> + for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++)
> + priv->tx_contexts[i].echo_index = MAX_TX_URBS;
> +
> + priv->dev = dev;
> + priv->netdev = netdev;
> + priv->channel = channel;
> +
> + priv->can.state = CAN_STATE_STOPPED;
> + priv->can.clock.freq = CAN_USB_CLOCK;
> + priv->can.bittiming_const = &kvaser_usb_bittiming_const;
> + priv->can.do_set_bittiming = kvaser_usb_set_bittiming;
> + priv->can.do_set_mode = kvaser_usb_set_mode;
> + if (id->driver_info & KVASER_HAS_TXRX_ERRORS)
> + priv->can.do_get_berr_counter = kvaser_usb_get_berr_counter;
> + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES;
> + if (id->driver_info & KVASER_HAS_SILENT_MODE)
> + priv->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY;
> +
> + netdev->flags |= IFF_ECHO;
> +
> + netdev->netdev_ops = &kvaser_usb_netdev_ops;
> +
> + SET_NETDEV_DEV(netdev, &intf->dev);
> +
> + dev->nets[channel] = priv;
> +
> + err = register_candev(netdev);
> + if (err) {
> + dev_err(&intf->dev, "Failed to register can device\n");
> + free_candev(netdev);
> + dev->nets[channel] = NULL;
> + return err;
> + }
> +
> + netdev_dbg(netdev, "device registered\n");
> +
> + return 0;
> +}
> +
> +static void kvaser_usb_get_endpoints(const struct usb_interface *intf,
> + struct usb_endpoint_descriptor **in,
> + struct usb_endpoint_descriptor **out)
> +{
> + const struct usb_host_interface *iface_desc;
> + struct usb_endpoint_descriptor *endpoint;
> + int i;
> +
> + iface_desc = &intf->altsetting[0];
> +
> + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
> + endpoint = &iface_desc->endpoint[i].desc;
> +
> + if (usb_endpoint_is_bulk_in(endpoint))
> + *in = endpoint;
> +
> + if (usb_endpoint_is_bulk_out(endpoint))
> + *out = endpoint;
> + }
> +}
> +
> +static int kvaser_usb_probe(struct usb_interface *intf,
> + const struct usb_device_id *id)
> +{
> + struct kvaser_usb *dev;
> + int err = -ENOMEM;
> + int i;
> +
> + dev = devm_kzalloc(&intf->dev, sizeof(*dev), GFP_KERNEL);
> + if (!dev)
> + return -ENOMEM;
> +
> + kvaser_usb_get_endpoints(intf, &dev->bulk_in, &dev->bulk_out);
> + if (!dev->bulk_in || !dev->bulk_out) {
> + 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);
> +
> + for (i = 0; i < MAX_NET_DEVICES; i++)
> + kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, i);
> +
> + err = kvaser_usb_get_software_info(dev);
> + if (err) {
> + dev_err(&intf->dev,
> + "Cannot get software infos, error %d\n", err);
> + return err;
> + }
> +
> + err = kvaser_usb_get_card_info(dev);
> + if (err) {
> + dev_err(&intf->dev,
> + "Cannot get card infos, error %d\n", err);
> + return err;
> + }
> +
> + dev_dbg(&intf->dev, "Firmware version: %d.%d.%d\n",
> + ((dev->fw_version >> 24) & 0xff),
> + ((dev->fw_version >> 16) & 0xff),
> + (dev->fw_version & 0xffff));
> +
> + for (i = 0; i < dev->nchannels; i++) {
> + err = kvaser_usb_init_one(intf, id, 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
^^^
nitpick, please add a "," there.
> +};
>
can you please add MODULE_DEVICE_TABLE(usb, kvaser_usb_table);
> +module_usb_driver(kvaser_usb_driver);
> +
> +MODULE_AUTHOR("Olivier Sobrie <olivier@sobrie.be>");
> +MODULE_DESCRIPTION("CAN driver for Kvaser CAN/USB devices");
> +MODULE_LICENSE("GPL v2");
>
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Industrial Linux Solutions | Phone: +49-231-2826-924 |
Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 259 bytes --]
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH v5] can: kvaser_usb: Add support for Kvaser CAN/USB devices
2012-11-07 20:29 ` Marc Kleine-Budde
@ 2012-11-20 8:46 ` Olivier Sobrie
2012-11-20 10:59 ` Marc Kleine-Budde
0 siblings, 1 reply; 43+ messages in thread
From: Olivier Sobrie @ 2012-11-20 8:46 UTC (permalink / raw)
To: Marc Kleine-Budde; +Cc: Wolfgang Grandegger, linux-can, netdev, linux-usb
Hi Marc,
On Wed, Nov 07, 2012 at 09:29:05PM +0100, Marc Kleine-Budde wrote:
> On 10/02/2012 09:16 AM, Olivier Sobrie wrote:
> > This driver provides support for several Kvaser CAN/USB devices.
> > Such kind of devices supports up to three CAN network interfaces.
> >
> > It has been tested with a Kvaser USB Leaf Light (one network interface)
> > connected to a pch_can interface.
> > The firmware version of the Kvaser device was 2.5.205.
> >
> > List of Kvaser devices supported by the driver:
> > - 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
> >
> > Signed-off-by: Daniel Berglund <db@kvaser.com>
> > Signed-off-by: Olivier Sobrie <olivier@sobrie.be>
>
> Sorry I forgot about the review.
> Looks quite good, some comments inline.
Sorry for the late reply but I was in holiday during the last
3 weeks :-)
See my comments below.
>
> > ---
> > Hi,
> >
> > This new patch fixes the errors pointed out by Marc and Wolfgang.
> >
> > Changes since v4:
> > - add missing usb_free_urb()
> > - put error message in a separate function
> > - handle return code of kvaser_usb_init_one() in probe function
> >
> > Olivier
> >
> > drivers/net/can/usb/Kconfig | 29 +
> > drivers/net/can/usb/Makefile | 1 +
> > drivers/net/can/usb/kvaser_usb.c | 1596 ++++++++++++++++++++++++++++++++++++++
> > 3 files changed, 1626 insertions(+)
> > create mode 100644 drivers/net/can/usb/kvaser_usb.c
> >
> > diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig
> > index 0a68768..a4e4bee 100644
> > --- a/drivers/net/can/usb/Kconfig
> > +++ b/drivers/net/can/usb/Kconfig
> > @@ -13,6 +13,35 @@ config CAN_ESD_USB2
> > This driver supports the CAN-USB/2 interface
> > from esd electronic system design gmbh (http://www.esd.eu).
> >
> > +config CAN_KVASER_USB
> > + tristate "Kvaser CAN/USB interface"
> > + ---help---
> > + This driver adds support for Kvaser CAN/USB devices like Kvaser
> > + Leaf Light.
> > +
> > + The driver gives 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
> > +
> > + If unsure, say N.
> > +
> > + To compile this driver as a module, choose M here: the
> > + module will be called kvaser_usb.
> > +
> > config CAN_PEAK_USB
> > tristate "PEAK PCAN-USB/USB Pro interfaces"
> > ---help---
> > diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile
> > index da6d1d3..80a2ee4 100644
> > --- a/drivers/net/can/usb/Makefile
> > +++ b/drivers/net/can/usb/Makefile
> > @@ -4,6 +4,7 @@
> >
> > obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o
> > obj-$(CONFIG_CAN_ESD_USB2) += esd_usb2.o
> > +obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o
> > obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/
> >
> > ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
> > diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c
> > new file mode 100644
> > index 0000000..bd48807
> > --- /dev/null
> > +++ b/drivers/net/can/usb/kvaser_usb.c
> > @@ -0,0 +1,1596 @@
> > +/*
> > + * This program is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU General Public License as
> > + * published by the Free Software Foundation version 2.
> > + *
> > + * Parts of this driver are based on the following:
> > + * - Kvaser linux leaf driver (version 4.78)
> > + * - CAN driver for esd CAN-USB/2
> > + *
> > + * Copyright (C) 2002-2006 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>
> > + */
> > +
> > +#include <linux/init.h>
> > +#include <linux/completion.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>
> > +
> > +#define MAX_TX_URBS 16
> > +#define MAX_RX_URBS 4
> > +#define START_TIMEOUT 1000 /* msecs */
> > +#define STOP_TIMEOUT 1000 /* msecs */
> > +#define USB_SEND_TIMEOUT 1000 /* msecs */
> > +#define USB_RECV_TIMEOUT 1000 /* msecs */
> > +#define RX_BUFFER_SIZE 3072
> > +#define CAN_USB_CLOCK 8000000
> > +#define MAX_NET_DEVICES 3
> > +
> > +/* Kvaser USB devices */
> > +#define KVASER_VENDOR_ID 0x0bfd
> > +#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
> > +
> > +/* USB devices features */
> > +#define KVASER_HAS_SILENT_MODE BIT(0)
> > +#define KVASER_HAS_TXRX_ERRORS BIT(1)
> > +
> > +/* Message header size */
> > +#define MSG_HEADER_LEN 2
> > +
> > +/* 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 */
> > +#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)
> > +
> > +/* Can msg 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_GET_CTRL_MODE 22
> > +#define CMD_GET_CTRL_MODE_REPLY 23
> > +#define CMD_RESET_CHIP 24
> > +#define CMD_RESET_CARD 25
> > +#define CMD_START_CHIP 26
> > +#define CMD_START_CHIP_REPLY 27
> > +#define CMD_STOP_CHIP 28
> > +#define CMD_STOP_CHIP_REPLY 29
> > +#define CMD_GET_CARD_INFO2 32
> > +#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_RESET_ERROR_COUNTER 49
> > +#define CMD_TX_ACKNOWLEDGE 50
> > +#define CMD_CAN_ERROR_EVENT 51
> > +#define CMD_USB_THROTTLE 77
> > +#define CMD_LOG_MESSAGE 106
> > +
> > +/* 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)
> > +
> > +/* bittiming parameters */
> > +#define KVASER_USB_TSEG1_MIN 1
> > +#define KVASER_USB_TSEG1_MAX 16
> > +#define KVASER_USB_TSEG2_MIN 1
> > +#define KVASER_USB_TSEG2_MAX 8
> > +#define KVASER_USB_SJW_MAX 4
> > +#define KVASER_USB_BRP_MIN 1
> > +#define KVASER_USB_BRP_MAX 64
> > +#define KVASER_USB_BRP_INC 1
> > +
> > +/* 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
> > +
> > +struct kvaser_msg_simple {
> > + u8 tid;
> > + u8 channel;
> > +} __packed;
> > +
> > +struct kvaser_msg_cardinfo {
> > + u8 tid;
> > + u8 nchannels;
> > + __le32 serial_number;
> > + __le32 padding;
> > + __le32 clock_resolution;
> > + __le32 mfgdate;
> > + u8 ean[8];
> > + u8 hw_revision;
> > + u8 usb_hs_mode;
> > + __le16 padding2;
> > +} __packed;
> > +
> > +struct kvaser_msg_cardinfo2 {
> > + u8 tid;
> > + u8 channel;
> > + u8 pcb_id[24];
> > + __le32 oem_unlock_code;
> > +} __packed;
> > +
> > +struct kvaser_msg_softinfo {
> > + u8 tid;
> > + u8 channel;
> > + __le32 sw_options;
> > + __le32 fw_version;
> > + __le16 max_outstanding_tx;
> > + __le16 padding[9];
> > +} __packed;
> > +
> > +struct kvaser_msg_busparams {
> > + u8 tid;
> > + u8 channel;
> > + __le32 bitrate;
> > + u8 tseg1;
> > + u8 tseg2;
> > + u8 sjw;
> > + u8 no_samp;
> > +} __packed;
> > +
> > +struct kvaser_msg_tx_can {
> > + u8 channel;
> > + u8 tid;
> > + u8 msg[14];
> > + u8 padding;
> > + u8 flags;
> > +} __packed;
> > +
> > +struct kvaser_msg_rx_can {
> > + u8 channel;
> > + u8 flag;
> > + __le16 time[3];
> > + u8 msg[14];
> > +} __packed;
> > +
> > +struct kvaser_msg_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 kvaser_msg_tx_acknowledge {
> > + u8 channel;
> > + u8 tid;
> > + __le16 time[3];
> > + u8 flags;
> > + u8 time_offset;
> > +} __packed;
> > +
> > +struct kvaser_msg_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 kvaser_msg_ctrl_mode {
> > + u8 tid;
> > + u8 channel;
> > + u8 ctrl_mode;
> > + u8 padding[3];
> > +} __packed;
> > +
> > +struct kvaser_msg_flush_queue {
> > + u8 tid;
> > + u8 channel;
> > + u8 flags;
> > + u8 padding[3];
> > +} __packed;
> > +
> > +struct kvaser_msg_log_message {
> > + u8 channel;
> > + u8 flags;
> > + __le16 time[3];
> > + u8 dlc;
> > + u8 time_offset;
> > + __le32 id;
> > + u8 data[8];
> > +} __packed;
> > +
> > +struct kvaser_msg {
> > + u8 len;
> > + u8 id;
> > + union {
> > + struct kvaser_msg_simple simple;
> > + struct kvaser_msg_cardinfo cardinfo;
> > + struct kvaser_msg_cardinfo2 cardinfo2;
> > + struct kvaser_msg_softinfo softinfo;
> > + struct kvaser_msg_busparams busparams;
> > + struct kvaser_msg_tx_can tx_can;
> > + struct kvaser_msg_rx_can rx_can;
> > + struct kvaser_msg_chip_state_event chip_state_event;
> > + struct kvaser_msg_tx_acknowledge tx_acknowledge;
> > + struct kvaser_msg_error_event error_event;
> > + struct kvaser_msg_ctrl_mode ctrl_mode;
> > + struct kvaser_msg_flush_queue flush_queue;
> > + struct kvaser_msg_log_message log_message;
> > + } u;
> > +} __packed;
> > +
> > +struct kvaser_usb_tx_urb_context {
> > + struct kvaser_usb_net_priv *priv;
> > + u32 echo_index;
> > + int dlc;
> > +};
> > +
> > +struct kvaser_usb {
> > + struct usb_device *udev;
> > + struct kvaser_usb_net_priv *nets[MAX_NET_DEVICES];
> > +
> > + struct usb_endpoint_descriptor *bulk_in, *bulk_out;
> > + struct usb_anchor rx_submitted;
> > +
> > + u32 fw_version;
> > + unsigned int nchannels;
> > +
> > + bool rxinitdone;
> > + void *rxbuf[MAX_RX_URBS];
> > + dma_addr_t rxbuf_dma[MAX_RX_URBS];
> > +};
> > +
> > +struct kvaser_usb_net_priv {
> > + struct can_priv can;
> > +
> > + atomic_t active_tx_urbs;
> > + struct usb_anchor tx_submitted;
> > + struct kvaser_usb_tx_urb_context tx_contexts[MAX_TX_URBS];
> > +
> > + struct completion start_comp, stop_comp;
> > +
> > + struct kvaser_usb *dev;
> > + struct net_device *netdev;
> > + int channel;
> > +
> > + struct can_berr_counter bec;
> > +};
> > +
> > +static struct usb_device_id kvaser_usb_table[] = {
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_DEVEL_PRODUCT_ID) },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_PRODUCT_ID) },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_PRODUCT_ID),
> > + .driver_info = KVASER_HAS_TXRX_ERRORS |
> > + KVASER_HAS_SILENT_MODE },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_PRODUCT_ID),
> > + .driver_info = KVASER_HAS_TXRX_ERRORS |
> > + KVASER_HAS_SILENT_MODE },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LS_PRODUCT_ID),
> > + .driver_info = KVASER_HAS_TXRX_ERRORS |
> > + KVASER_HAS_SILENT_MODE },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_SWC_PRODUCT_ID),
> > + .driver_info = KVASER_HAS_TXRX_ERRORS |
> > + KVASER_HAS_SILENT_MODE },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LIN_PRODUCT_ID),
> > + .driver_info = KVASER_HAS_TXRX_ERRORS |
> > + KVASER_HAS_SILENT_MODE },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_LS_PRODUCT_ID),
> > + .driver_info = KVASER_HAS_TXRX_ERRORS |
> > + KVASER_HAS_SILENT_MODE },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_SWC_PRODUCT_ID),
> > + .driver_info = KVASER_HAS_TXRX_ERRORS |
> > + KVASER_HAS_SILENT_MODE },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_DEVEL_PRODUCT_ID),
> > + .driver_info = KVASER_HAS_TXRX_ERRORS |
> > + KVASER_HAS_SILENT_MODE },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSHS_PRODUCT_ID),
> > + .driver_info = KVASER_HAS_TXRX_ERRORS |
> > + KVASER_HAS_SILENT_MODE },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_UPRO_HSHS_PRODUCT_ID),
> > + .driver_info = KVASER_HAS_TXRX_ERRORS },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_GI_PRODUCT_ID) },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_OBDII_PRODUCT_ID),
> > + .driver_info = KVASER_HAS_TXRX_ERRORS |
> > + KVASER_HAS_SILENT_MODE },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSLS_PRODUCT_ID),
> > + .driver_info = KVASER_HAS_TXRX_ERRORS },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_CH_PRODUCT_ID),
> > + .driver_info = KVASER_HAS_TXRX_ERRORS },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_BLACKBIRD_SPRO_PRODUCT_ID),
> > + .driver_info = KVASER_HAS_TXRX_ERRORS },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_MERCURY_PRODUCT_ID),
> > + .driver_info = KVASER_HAS_TXRX_ERRORS },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_LEAF_PRODUCT_ID),
> > + .driver_info = KVASER_HAS_TXRX_ERRORS },
> > + { USB_DEVICE(KVASER_VENDOR_ID, USB_CAN_R_PRODUCT_ID),
> > + .driver_info = KVASER_HAS_TXRX_ERRORS },
> > + { }
> > +};
> > +MODULE_DEVICE_TABLE(usb, kvaser_usb_table);
> > +
> > +static inline int kvaser_usb_send_msg(const struct kvaser_usb *dev,
> > + struct kvaser_msg *msg)
> > +{
> > + int actual_len;
> > +
> > + return usb_bulk_msg(dev->udev,
> > + usb_sndbulkpipe(dev->udev,
> > + dev->bulk_out->bEndpointAddress),
> > + msg, msg->len, &actual_len,
> > + USB_SEND_TIMEOUT);
> > +}
> > +
> > +static int kvaser_usb_wait_msg(const struct kvaser_usb *dev, u8 id,
> > + struct kvaser_msg *msg)
> > +{
> > + struct kvaser_msg *tmp;
> > + void *buf;
> > + int actual_len;
> > + int err;
> > + int pos = 0;
> > +
> > + buf = kzalloc(RX_BUFFER_SIZE, GFP_KERNEL);
> > + if (!buf)
> > + return -ENOMEM;
> > +
> > + err = usb_bulk_msg(dev->udev,
> > + usb_rcvbulkpipe(dev->udev,
> > + dev->bulk_in->bEndpointAddress),
> > + buf, RX_BUFFER_SIZE, &actual_len,
> > + USB_RECV_TIMEOUT);
> > + if (err < 0)
> > + goto end;
> > +
> > + while (pos <= actual_len - MSG_HEADER_LEN) {
> > + tmp = buf + pos;
> > +
> > + if (!tmp->len)
> > + break;
> > +
> > + if (pos + tmp->len > actual_len) {
> > + dev_err(dev->udev->dev.parent, "Format error\n");
> > + break;
> > + }
> > +
> > + if (tmp->id == id) {
> > + memcpy(msg, tmp, tmp->len);
> > + goto end;
> > + }
> > +
> > + pos += tmp->len;
> > + }
> > +
> > + err = -EINVAL;
> > +
> > +end:
> > + kfree(buf);
> > +
> > + return err;
> > +}
> > +
> > +static int kvaser_usb_send_simple_msg(const struct kvaser_usb *dev,
> > + u8 msg_id, int channel)
> > +{
> > + struct kvaser_msg msg = {
> > + .len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple),
> > + .id = msg_id,
> > + .u.simple.channel = channel,
> > + .u.simple.tid = 0xff,
> > + };
> > +
> > + return kvaser_usb_send_msg(dev, &msg);
> > +}
> > +
> > +static int kvaser_usb_get_software_info(struct kvaser_usb *dev)
> > +{
> > + struct kvaser_msg msg;
> > + int err;
> > +
> > + err = kvaser_usb_send_simple_msg(dev, CMD_GET_SOFTWARE_INFO, 0);
> > + if (err)
> > + return err;
> > +
> > + err = kvaser_usb_wait_msg(dev, CMD_GET_SOFTWARE_INFO_REPLY, &msg);
> > + if (err)
> > + return err;
> > +
> > + dev->fw_version = le32_to_cpu(msg.u.softinfo.fw_version);
> > +
> > + return 0;
> > +}
> > +
> > +static int kvaser_usb_get_card_info(struct kvaser_usb *dev)
> > +{
> > + struct kvaser_msg msg;
> > + int err;
> > +
> > + err = kvaser_usb_send_simple_msg(dev, CMD_GET_CARD_INFO, 0);
> > + if (err)
> > + return err;
> > +
> > + err = kvaser_usb_wait_msg(dev, CMD_GET_CARD_INFO_REPLY, &msg);
> > + if (err)
> > + return err;
> > +
> > + dev->nchannels = msg.u.cardinfo.nchannels;
> > +
> > + return 0;
> > +}
> > +
> > +static void kvaser_usb_tx_acknowledge(const struct kvaser_usb *dev,
> > + const struct kvaser_msg *msg)
> > +{
> > + struct net_device_stats *stats;
> > + struct kvaser_usb_tx_urb_context *context;
> > + struct kvaser_usb_net_priv *priv;
> > + struct sk_buff *skb;
> > + struct can_frame *cf;
> > + u8 channel = msg->u.tx_acknowledge.channel;
> > + u8 tid = msg->u.tx_acknowledge.tid;
> > +
> > + if (channel >= dev->nchannels) {
> > + dev_err(dev->udev->dev.parent,
> > + "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 % 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)) {
> > + skb = alloc_can_err_skb(priv->netdev, &cf);
> > + if (skb) {
> > + cf->can_id |= CAN_ERR_RESTARTED;
> > + netif_rx(skb);
> > +
> > + stats->rx_packets++;
> > + stats->rx_bytes += cf->can_dlc;
> > + } 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;
> > + can_get_echo_skb(priv->netdev, context->echo_index);
> > +
> > + context->echo_index = MAX_TX_URBS;
> > + atomic_dec(&priv->active_tx_urbs);
> > +
> > + netif_wake_queue(priv->netdev);
> > +}
> > +
> > +static void kvaser_usb_simple_msg_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);
> > +}
> > +
> > +static int kvaser_usb_simple_msg_async(struct kvaser_usb_net_priv *priv,
> > + u8 msg_id)
> > +{
> > + struct kvaser_usb *dev = priv->dev;
> > + struct net_device *netdev = priv->netdev;
> > + struct kvaser_msg *msg;
> > + struct urb *urb;
> > + void *buf;
> > + int err;
> > +
> > + urb = usb_alloc_urb(0, GFP_ATOMIC);
> > + if (!urb) {
> > + netdev_err(netdev, "No memory left for URBs\n");
> > + return -ENOMEM;
> > + }
> > +
> > + buf = kmalloc(sizeof(struct kvaser_msg), GFP_ATOMIC);
> > + if (!buf) {
> > + netdev_err(netdev, "No memory left for USB buffer\n");
> > + usb_free_urb(urb);
> > + return -ENOMEM;
> > + }
> > +
> > + msg = (struct kvaser_msg *)buf;
> > + msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple);
> > + msg->id = msg_id;
> > + msg->u.simple.channel = priv->channel;
> > +
> > + usb_fill_bulk_urb(urb, dev->udev,
> > + usb_sndbulkpipe(dev->udev,
> > + dev->bulk_out->bEndpointAddress),
> > + buf, msg->len,
> > + kvaser_usb_simple_msg_callback, priv);
> > + 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);
> > + kfree(buf);
> > + return err;
> > + }
> > +
> > + usb_free_urb(urb);
> > +
> > + return 0;
> > +}
> > +
> > +static void kvaser_usb_unlink_tx_urbs(struct kvaser_usb_net_priv *priv)
> > +{
> > + int 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;
> > +}
> > +
> > +static void kvaser_usb_rx_error(const struct kvaser_usb *dev,
> > + const struct kvaser_msg *msg)
> > +{
> > + struct can_frame *cf;
> > + struct sk_buff *skb;
> > + struct net_device_stats *stats;
> > + struct kvaser_usb_net_priv *priv;
> > + unsigned int new_state;
> > + u8 channel, status, txerr, rxerr, error_factor;
> > +
> > + switch (msg->id) {
> > + case CMD_CAN_ERROR_EVENT:
> > + channel = msg->u.error_event.channel;
> > + status = msg->u.error_event.status;
> > + txerr = msg->u.error_event.tx_errors_count;
> > + rxerr = msg->u.error_event.rx_errors_count;
> > + error_factor = msg->u.error_event.error_factor;
> > + break;
> > + case CMD_LOG_MESSAGE:
> > + channel = msg->u.log_message.channel;
> > + status = msg->u.log_message.data[0];
> > + txerr = msg->u.log_message.data[2];
> > + rxerr = msg->u.log_message.data[3];
> > + error_factor = msg->u.log_message.data[1];
> > + break;
> > + case CMD_CHIP_STATE_EVENT:
> > + channel = msg->u.chip_state_event.channel;
> > + status = msg->u.chip_state_event.status;
> > + txerr = msg->u.chip_state_event.tx_errors_count;
> > + rxerr = msg->u.chip_state_event.rx_errors_count;
> > + error_factor = 0;
> > + break;
> > + default:
> > + dev_err(dev->udev->dev.parent, "Invalid msg id (%d)\n",
> > + msg->id);
> > + return;
> > + }
> > +
> > + if (channel >= dev->nchannels) {
> > + dev_err(dev->udev->dev.parent,
> > + "Invalid channel number (%d)\n", channel);
> > + return;
> > + }
> > +
> > + priv = dev->nets[channel];
> > + stats = &priv->netdev->stats;
> > +
> > + if (status & M16C_STATE_BUS_RESET) {
> > + kvaser_usb_unlink_tx_urbs(priv);
> > + return;
> > + }
> > +
> > + skb = alloc_can_err_skb(priv->netdev, &cf);
> > + if (!skb) {
> > + stats->rx_dropped++;
> > + return;
> > + }
> > +
> > + new_state = priv->can.state;
> > +
> > + netdev_dbg(priv->netdev, "Error status: 0x%02x\n", status);
> > +
> > + if (status & M16C_STATE_BUS_OFF) {
> > + cf->can_id |= CAN_ERR_BUSOFF;
> > +
> > + priv->can.can_stats.bus_off++;
> > + if (!priv->can.restart_ms)
> > + kvaser_usb_simple_msg_async(priv, CMD_STOP_CHIP);
> > +
> > + netif_carrier_off(priv->netdev);
> > +
> > + new_state = CAN_STATE_BUS_OFF;
> > + } else if (status & M16C_STATE_BUS_PASSIVE) {
> > + if (priv->can.state != CAN_STATE_ERROR_PASSIVE) {
> > + cf->can_id |= CAN_ERR_CRTL;
> > +
> > + if (txerr || rxerr)
> > + cf->data[1] = (txerr > rxerr)
> > + ? CAN_ERR_CRTL_TX_PASSIVE
> > + : CAN_ERR_CRTL_RX_PASSIVE;
> > + else
> > + cf->data[1] = CAN_ERR_CRTL_TX_PASSIVE |
> > + CAN_ERR_CRTL_RX_PASSIVE;
> > +
> > + priv->can.can_stats.error_passive++;
> > + }
> > +
> > + new_state = CAN_STATE_ERROR_PASSIVE;
> > + }
> > +
> > + if (status == M16C_STATE_BUS_ERROR) {
> > + if ((priv->can.state < CAN_STATE_ERROR_WARNING) &&
> > + ((txerr >= 96) || (rxerr >= 96))) {
> > + cf->can_id |= CAN_ERR_CRTL;
> > + cf->data[1] = (txerr > rxerr)
> > + ? CAN_ERR_CRTL_TX_WARNING
> > + : CAN_ERR_CRTL_RX_WARNING;
> > +
> > + priv->can.can_stats.error_warning++;
> > + new_state = CAN_STATE_ERROR_WARNING;
> > + } else if (priv->can.state > CAN_STATE_ERROR_ACTIVE) {
> > + cf->can_id |= CAN_ERR_PROT;
> > + cf->data[2] = CAN_ERR_PROT_ACTIVE;
> > +
> > + new_state = CAN_STATE_ERROR_ACTIVE;
> > + }
> > + }
> > +
> > + if (!status) {
> > + cf->can_id |= CAN_ERR_PROT;
> > + cf->data[2] = CAN_ERR_PROT_ACTIVE;
> > +
> > + new_state = CAN_STATE_ERROR_ACTIVE;
> > + }
> > +
> > + if (priv->can.restart_ms &&
> > + (priv->can.state >= CAN_STATE_BUS_OFF) &&
> > + (new_state < CAN_STATE_BUS_OFF)) {
> > + cf->can_id |= CAN_ERR_RESTARTED;
> > + netif_carrier_on(priv->netdev);
> > +
> > + priv->can.can_stats.restarts++;
> > + }
> > +
> > + if (error_factor) {
> > + priv->can.can_stats.bus_error++;
> > + stats->rx_errors++;
> > +
> > + cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT;
> > +
> > + if (error_factor & M16C_EF_ACKE)
> > + cf->data[3] |= (CAN_ERR_PROT_LOC_ACK);
> > + if (error_factor & M16C_EF_CRCE)
> > + cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ |
> > + CAN_ERR_PROT_LOC_CRC_DEL);
> > + if (error_factor & M16C_EF_FORME)
> > + cf->data[2] |= CAN_ERR_PROT_FORM;
> > + if (error_factor & M16C_EF_STFE)
> > + cf->data[2] |= CAN_ERR_PROT_STUFF;
> > + if (error_factor & M16C_EF_BITE0)
> > + cf->data[2] |= CAN_ERR_PROT_BIT0;
> > + if (error_factor & M16C_EF_BITE1)
> > + cf->data[2] |= CAN_ERR_PROT_BIT1;
> > + if (error_factor & M16C_EF_TRE)
> > + cf->data[2] |= CAN_ERR_PROT_TX;
> > + }
> > +
> > + cf->data[6] = txerr;
> > + cf->data[7] = rxerr;
> > +
> > + priv->bec.txerr = txerr;
> > + priv->bec.rxerr = rxerr;
> > +
> > + priv->can.state = new_state;
> > +
> > + netif_rx(skb);
> > +
> > + stats->rx_packets++;
> > + stats->rx_bytes += cf->can_dlc;
> > +}
> > +
> > +static void kvaser_usb_rx_can_err(const struct kvaser_usb_net_priv *priv,
> > + const struct kvaser_msg *msg)
> > +{
> > + struct can_frame *cf;
> > + struct sk_buff *skb;
> > + struct net_device_stats *stats = &priv->netdev->stats;
> > +
> > + if (msg->u.rx_can.flag & (MSG_FLAG_ERROR_FRAME |
> > + MSG_FLAG_NERR)) {
> > + netdev_err(priv->netdev, "Unknow error (flags: 0x%02x)\n",
> > + msg->u.rx_can.flag);
> > +
> > + stats->rx_errors++;
> > + return;
> > + }
> > +
> > + if (msg->u.rx_can.flag & MSG_FLAG_OVERRUN) {
> > + skb = alloc_can_err_skb(priv->netdev, &cf);
> > + if (!skb) {
> > + stats->tx_dropped++;
>
> Should be rx, as we are in the rx function.
Ok I'll fix that.
>
> > + return;
> > + }
> > +
> > + cf->can_id |= CAN_ERR_CRTL;
> > + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
> > +
> > + stats->rx_over_errors++;
> > + stats->rx_errors++;
> > +
> > + netif_rx(skb);
> > +
> > + stats->rx_packets++;
> > + stats->rx_bytes += cf->can_dlc;
> > + }
> > +}
> > +
> > +static void kvaser_usb_rx_can_msg(const struct kvaser_usb *dev,
> > + const struct kvaser_msg *msg)
> > +{
> > + struct kvaser_usb_net_priv *priv;
> > + struct can_frame *cf;
> > + struct sk_buff *skb;
> > + struct net_device_stats *stats;
> > + u8 channel = msg->u.rx_can.channel;
> > +
> > + if (channel >= dev->nchannels) {
> > + dev_err(dev->udev->dev.parent,
> > + "Invalid channel number (%d)\n", channel);
> > + return;
> > + }
> > +
> > + priv = dev->nets[channel];
> > + stats = &priv->netdev->stats;
> > +
> > + if (msg->u.rx_can.flag & (MSG_FLAG_ERROR_FRAME | MSG_FLAG_NERR |
> > + MSG_FLAG_OVERRUN)) {
> > + kvaser_usb_rx_can_err(priv, msg);
> > + return;
> > + } else if (msg->u.rx_can.flag & ~MSG_FLAG_REMOTE_FRAME) {
> > + netdev_warn(priv->netdev,
> > + "Unhandled frame (flags: 0x%02x)",
> > + msg->u.rx_can.flag);
> > + return;
> > + }
> > +
> > + skb = alloc_can_skb(priv->netdev, &cf);
> > + if (!skb) {
> > + stats->tx_dropped++;
> > + return;
> > + }
> > +
> > + cf->can_id = ((msg->u.rx_can.msg[0] & 0x1f) << 6) |
> > + (msg->u.rx_can.msg[1] & 0x3f);
> > + cf->can_dlc = get_can_dlc(msg->u.rx_can.msg[5]);
> > +
> > + if (msg->id == CMD_RX_EXT_MESSAGE) {
> > + cf->can_id <<= 18;
> > + cf->can_id |= ((msg->u.rx_can.msg[2] & 0x0f) << 14) |
> > + ((msg->u.rx_can.msg[3] & 0xff) << 6) |
> > + (msg->u.rx_can.msg[4] & 0x3f);
> > + cf->can_id |= CAN_EFF_FLAG;
> > + }
> > +
> > + if (msg->u.rx_can.flag & MSG_FLAG_REMOTE_FRAME)
> > + cf->can_id |= CAN_RTR_FLAG;
> > + else
> > + memcpy(cf->data, &msg->u.rx_can.msg[6], cf->can_dlc);
> > +
> > + netif_rx(skb);
> > +
> > + stats->rx_packets++;
> > + stats->rx_bytes += cf->can_dlc;
> > +}
> > +
> > +static void kvaser_usb_start_chip_reply(const struct kvaser_usb *dev,
> > + const struct kvaser_msg *msg)
> > +{
> > + struct kvaser_usb_net_priv *priv;
> > + u8 channel = msg->u.simple.channel;
> > +
> > + if (channel >= dev->nchannels) {
> > + dev_err(dev->udev->dev.parent,
> > + "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_stop_chip_reply(const struct kvaser_usb *dev,
> > + const struct kvaser_msg *msg)
> > +{
> > + struct kvaser_usb_net_priv *priv;
> > + u8 channel = msg->u.simple.channel;
> > +
> > + if (channel >= dev->nchannels) {
> > + dev_err(dev->udev->dev.parent,
> > + "Invalid channel number (%d)\n", channel);
> > + return;
> > + }
> > +
> > + priv = dev->nets[channel];
> > +
> > + complete(&priv->stop_comp);
> > +}
> > +
> > +static void kvaser_usb_handle_message(const struct kvaser_usb *dev,
> > + const struct kvaser_msg *msg)
> > +{
> > + switch (msg->id) {
> > + case CMD_START_CHIP_REPLY:
> > + kvaser_usb_start_chip_reply(dev, msg);
> > + break;
> > +
> > + case CMD_STOP_CHIP_REPLY:
> > + kvaser_usb_stop_chip_reply(dev, msg);
> > + break;
> > +
> > + case CMD_RX_STD_MESSAGE:
> > + case CMD_RX_EXT_MESSAGE:
> > + kvaser_usb_rx_can_msg(dev, msg);
> > + break;
> > +
> > + case CMD_CHIP_STATE_EVENT:
> > + case CMD_CAN_ERROR_EVENT:
> > + kvaser_usb_rx_error(dev, msg);
> > + break;
> > +
> > + case CMD_LOG_MESSAGE:
> > + if (msg->u.log_message.flags & MSG_FLAG_ERROR_FRAME)
> > + kvaser_usb_rx_error(dev, msg);
> > + break;
> > +
> > + case CMD_TX_ACKNOWLEDGE:
> > + kvaser_usb_tx_acknowledge(dev, msg);
> > + break;
> > +
> > + default:
> > + dev_warn(dev->udev->dev.parent,
> > + "Unhandled message (%d)\n", msg->id);
> > + break;
> > + }
> > +}
> > +
> > +static void kvaser_usb_read_bulk_callback(struct urb *urb)
> > +{
> > + struct kvaser_usb *dev = urb->context;
> > + struct kvaser_msg *msg;
> > + int pos = 0;
> > + int err, i;
> > +
> > + switch (urb->status) {
> > + case 0:
> > + break;
> > + case -ENOENT:
> > + 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 - MSG_HEADER_LEN) {
> > + msg = urb->transfer_buffer + pos;
> > +
> > + if (!msg->len)
> > + break;
> > +
> > + if (pos + msg->len > urb->actual_length) {
> > + dev_err(dev->udev->dev.parent, "Format error\n");
> > + break;
> > + }
> > +
> > + kvaser_usb_handle_message(dev, msg);
> > +
> > + pos += msg->len;
> > + }
> > +
> > +resubmit_urb:
> > + usb_fill_bulk_urb(urb, dev->udev,
> > + usb_rcvbulkpipe(dev->udev,
> > + dev->bulk_in->bEndpointAddress),
> > + urb->transfer_buffer, 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->udev->dev.parent,
> > + "Failed resubmitting read bulk urb: %d\n", err);
> > + }
> > +
> > + return;
> > +}
> > +
> > +static int kvaser_usb_setup_rx_urbs(struct kvaser_usb *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;
> > +
> > + urb = usb_alloc_urb(0, GFP_KERNEL);
> > + if (!urb) {
> > + dev_warn(dev->udev->dev.parent,
> > + "No memory left for URBs\n");
> > + 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");
> > + usb_free_urb(urb);
> > + err = -ENOMEM;
> > + break;
> > + }
> > +
> > + usb_fill_bulk_urb(urb, dev->udev,
> > + usb_rcvbulkpipe(dev->udev,
> > + dev->bulk_in->bEndpointAddress),
> > + buf, 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, 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->udev->dev.parent,
> > + "Cannot setup read URBs, error %d\n", err);
> > + return err;
> > + } else if (i < MAX_RX_URBS) {
> > + dev_warn(dev->udev->dev.parent,
> > + "RX performances may be slow\n");
> > + }
> > +
> > + dev->rxinitdone = true;
> > +
> > + return 0;
> > +}
> > +
> > +static int kvaser_usb_set_opt_mode(const struct kvaser_usb_net_priv *priv)
> > +{
> > + struct kvaser_msg msg = {
> > + .id = CMD_SET_CTRL_MODE,
> > + .len = MSG_HEADER_LEN +
> > + sizeof(struct kvaser_msg_ctrl_mode),
> > + .u.ctrl_mode.tid = 0xff,
> > + .u.ctrl_mode.channel = priv->channel,
> > + };
> > +
> > + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
> > + msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_SILENT;
> > + else
> > + msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_NORMAL;
> > +
> > + return kvaser_usb_send_msg(priv->dev, &msg);
> > +}
> > +
> > +static int kvaser_usb_start_chip(struct kvaser_usb_net_priv *priv)
> > +{
> > + int err;
> > +
> > + init_completion(&priv->start_comp);
> > +
> > + err = kvaser_usb_send_simple_msg(priv->dev, CMD_START_CHIP,
> > + priv->channel);
> > + if (err)
> > + return err;
> > +
> > + if (!wait_for_completion_timeout(&priv->start_comp,
> > + msecs_to_jiffies(START_TIMEOUT)))
> > + return -ETIMEDOUT;
> > +
> > + 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;
> > + int err;
> > +
> > + err = open_candev(netdev);
> > + if (err)
> > + return err;
> > +
> > + err = kvaser_usb_setup_rx_urbs(dev);
> > + if (err)
> > + goto error;
> > +
> > + err = kvaser_usb_set_opt_mode(priv);
> > + if (err)
> > + goto error;
> > +
> > + err = kvaser_usb_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_unlink_all_urbs(struct kvaser_usb *dev)
> > +{
> > + int i;
> > +
> > + 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 < MAX_NET_DEVICES; i++) {
> > + struct kvaser_usb_net_priv *priv = dev->nets[i];
> > +
> > + if (priv)
> > + kvaser_usb_unlink_tx_urbs(priv);
> > + }
> > +}
> > +
> > +static int kvaser_usb_stop_chip(struct kvaser_usb_net_priv *priv)
> > +{
> > + int err;
> > +
> > + init_completion(&priv->stop_comp);
> > +
> > + err = kvaser_usb_send_simple_msg(priv->dev, CMD_STOP_CHIP,
> > + priv->channel);
> > + if (err)
> > + return err;
> > +
> > + if (!wait_for_completion_timeout(&priv->stop_comp,
> > + msecs_to_jiffies(STOP_TIMEOUT)))
> > + return -ETIMEDOUT;
> > +
> > + return 0;
> > +}
> > +
> > +static int kvaser_usb_flush_queue(struct kvaser_usb_net_priv *priv)
> > +{
> > + struct kvaser_msg msg = {
> > + .id = CMD_FLUSH_QUEUE,
> > + .len = MSG_HEADER_LEN +
> > + sizeof(struct kvaser_msg_flush_queue),
> > + .u.flush_queue.channel = priv->channel,
> > + .u.flush_queue.flags = 0x00,
> > + };
> > +
> > + return kvaser_usb_send_msg(priv->dev, &msg);
> > +}
> > +
> > +static int kvaser_usb_close(struct net_device *netdev)
> > +{
> > + struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
> > + struct kvaser_usb *dev = priv->dev;
> > + int err;
> > +
> > + netif_stop_queue(netdev);
> > +
> > + err = kvaser_usb_flush_queue(priv);
> > + if (err)
> > + netdev_warn(netdev, "Cannot flush queue, error %d\n", err);
> > +
> > + if (kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, priv->channel))
> > + netdev_warn(netdev, "Cannot reset card, error %d\n", err);
> > +
> > + err = kvaser_usb_stop_chip(priv);
> > + if (err)
> > + netdev_warn(netdev, "Cannot stop device, error %d\n", err);
> > +
> > + priv->can.state = CAN_STATE_STOPPED;
> > + close_candev(priv->netdev);
> > +
> > + return 0;
> > +}
> > +
> > +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;
> > + struct net_device_stats *stats = &netdev->stats;
> > + struct can_frame *cf = (struct can_frame *)skb->data;
> > + struct kvaser_usb_tx_urb_context *context = NULL;
> > + struct urb *urb;
> > + void *buf;
> > + struct kvaser_msg *msg;
> > + int i, err;
> > + int ret = NETDEV_TX_OK;
> > +
> > + if (can_dropped_invalid_skb(netdev, skb))
> > + return NETDEV_TX_OK;
> > +
> > + urb = usb_alloc_urb(0, GFP_ATOMIC);
> > + if (!urb) {
> > + netdev_err(netdev, "No memory left for URBs\n");
> > + stats->tx_dropped++;
>
> Move the dev_kfree_skb to the end and goto there.
I assume you mean doing something like that at the end of the function:
releasebuf:
kfree(buf);
nobufmem:
usb_free_urb(urb);
nourbmem:
dev_kfree_skb(skb);
return ret;
If I do that it will give problems when the 'releasebuf' condition is
reached. The skb buffer will be freed twice. The skb is already freed
by the function can_free_echo_skb().
>
> > + dev_kfree_skb(skb);
> > + return NETDEV_TX_OK;
> > + }
> > +
> > + buf = kmalloc(sizeof(struct kvaser_msg), GFP_ATOMIC);
> > + if (!buf) {
> > + netdev_err(netdev, "No memory left for USB buffer\n");
> > + stats->tx_dropped++;
> You cann usb_free_urb twice...here and in the error handling at the end.
Indeed thanks.
>
> > + dev_kfree_skb(skb);
> > + usb_free_urb(urb);
> > + goto nobufmem;
> > + }
> > +
> > + msg = buf;
> > + msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_tx_can);
> > + msg->u.tx_can.flags = 0;
> > + msg->u.tx_can.channel = priv->channel;
> > +
> > + if (cf->can_id & CAN_EFF_FLAG) {
> > + msg->id = CMD_TX_EXT_MESSAGE;
> > + msg->u.tx_can.msg[0] = (cf->can_id >> 24) & 0x1f;
> > + msg->u.tx_can.msg[1] = (cf->can_id >> 18) & 0x3f;
> > + msg->u.tx_can.msg[2] = (cf->can_id >> 14) & 0x0f;
> > + msg->u.tx_can.msg[3] = (cf->can_id >> 6) & 0xff;
> > + msg->u.tx_can.msg[4] = cf->can_id & 0x3f;
> > + } else {
> > + msg->id = CMD_TX_STD_MESSAGE;
> > + msg->u.tx_can.msg[0] = (cf->can_id >> 6) & 0x1f;
> > + msg->u.tx_can.msg[1] = cf->can_id & 0x3f;
> > + }
> > +
> > + msg->u.tx_can.msg[5] = cf->can_dlc;
> > + memcpy(&msg->u.tx_can.msg[6], cf->data, cf->can_dlc);
> > +
> > + if (cf->can_id & CAN_RTR_FLAG)
> > + msg->u.tx_can.flags |= MSG_FLAG_REMOTE_FRAME;
> > +
> > + for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++) {
> > + if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) {
> > + context = &priv->tx_contexts[i];
> > + break;
> > + }
> > + }
> > +
> > + if (!context) {
> > + netdev_warn(netdev, "cannot find free context\n");
> > + ret = NETDEV_TX_BUSY;
> > + goto releasebuf;
> > + }
> > +
> > + context->priv = priv;
> > + context->echo_index = i;
> > + context->dlc = cf->can_dlc;
> > +
> > + msg->u.tx_can.tid = context->echo_index;
> > +
> > + usb_fill_bulk_urb(urb, dev->udev,
> > + usb_sndbulkpipe(dev->udev,
> > + dev->bulk_out->bEndpointAddress),
> > + buf, msg->len,
> > + kvaser_usb_write_bulk_callback, context);
> > + usb_anchor_urb(urb, &priv->tx_submitted);
> > +
> > + can_put_echo_skb(skb, netdev, context->echo_index);
> > +
> > + atomic_inc(&priv->active_tx_urbs);
> > +
> > + if (atomic_read(&priv->active_tx_urbs) >= MAX_TX_URBS)
> > + netif_stop_queue(netdev);
> > +
> > + err = usb_submit_urb(urb, GFP_ATOMIC);
> > + if (unlikely(err)) {
> > + can_free_echo_skb(netdev, context->echo_index);
> > +
> > + atomic_dec(&priv->active_tx_urbs);
> > + 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;
> > + }
> > +
> > + usb_free_urb(urb);
> > +
> > + return NETDEV_TX_OK;
> > +
> > +releasebuf:
> > + kfree(buf);
> > +nobufmem:
> > + 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,
> > +};
> > +
> > +static struct can_bittiming_const kvaser_usb_bittiming_const = {
> > + .name = "kvaser_usb",
> > + .tseg1_min = KVASER_USB_TSEG1_MIN,
> > + .tseg1_max = KVASER_USB_TSEG1_MAX,
> > + .tseg2_min = KVASER_USB_TSEG2_MIN,
> > + .tseg2_max = KVASER_USB_TSEG2_MAX,
> > + .sjw_max = KVASER_USB_SJW_MAX,
> > + .brp_min = KVASER_USB_BRP_MIN,
> > + .brp_max = KVASER_USB_BRP_MAX,
> > + .brp_inc = KVASER_USB_BRP_INC,
> > +};
> > +
> > +static int kvaser_usb_set_bittiming(struct net_device *netdev)
> > +{
> > + struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
> > + struct can_bittiming *bt = &priv->can.bittiming;
> > + struct kvaser_usb *dev = priv->dev;
> > + struct kvaser_msg msg = {
> > + .id = CMD_SET_BUS_PARAMS,
> > + .len = MSG_HEADER_LEN +
> > + sizeof(struct kvaser_msg_busparams),
> > + .u.busparams.channel = priv->channel,
> > + .u.busparams.tid = 0xff,
> > + .u.busparams.bitrate = cpu_to_le32(bt->bitrate),
> > + .u.busparams.sjw = bt->sjw,
> > + .u.busparams.tseg1 = bt->prop_seg + bt->phase_seg1,
> > + .u.busparams.tseg2 = bt->phase_seg2,
> > + };
> > +
> > + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
> > + msg.u.busparams.no_samp = 3;
> > + else
> > + msg.u.busparams.no_samp = 1;
> > +
> > + return kvaser_usb_send_msg(dev, &msg);
> > +}
> > +
> > +static int kvaser_usb_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:
> > + err = kvaser_usb_simple_msg_async(priv, CMD_START_CHIP);
> > + if (err)
> > + return err;
> > + break;
> > + default:
> > + return -EOPNOTSUPP;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int kvaser_usb_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 void kvaser_usb_remove_interfaces(struct kvaser_usb *dev)
> > +{
> > + int i;
> > +
> > + for (i = 0; i < dev->nchannels; i++) {
> > + if (!dev->nets[i])
> > + continue;
> > +
> > + unregister_netdev(dev->nets[i]->netdev);
> > + }
> > +
> > + kvaser_usb_unlink_all_urbs(dev);
> > +
> > + for (i = 0; i < dev->nchannels; i++) {
> > + if (!dev->nets[i])
> > + continue;
> > +
> > + free_candev(dev->nets[i]->netdev);
> > + }
> > +}
> > +
> > +static int kvaser_usb_init_one(struct usb_interface *intf,
> > + const struct usb_device_id *id, int channel)
> > +{
> > + struct kvaser_usb *dev = usb_get_intfdata(intf);
> > + struct net_device *netdev;
> > + struct kvaser_usb_net_priv *priv;
> > + int i, err;
> > +
> > + netdev = alloc_candev(sizeof(*priv), MAX_TX_URBS);
> > + if (!netdev) {
> > + dev_err(&intf->dev, "Cannot alloc candev\n");
> > + return -ENOMEM;
> > + }
> > +
> > + priv = netdev_priv(netdev);
> > +
> > + init_completion(&priv->start_comp);
> > + init_completion(&priv->stop_comp);
> > +
> > + init_usb_anchor(&priv->tx_submitted);
> > + atomic_set(&priv->active_tx_urbs, 0);
> > +
> > + for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++)
> > + priv->tx_contexts[i].echo_index = MAX_TX_URBS;
> > +
> > + priv->dev = dev;
> > + priv->netdev = netdev;
> > + priv->channel = channel;
> > +
> > + priv->can.state = CAN_STATE_STOPPED;
> > + priv->can.clock.freq = CAN_USB_CLOCK;
> > + priv->can.bittiming_const = &kvaser_usb_bittiming_const;
> > + priv->can.do_set_bittiming = kvaser_usb_set_bittiming;
> > + priv->can.do_set_mode = kvaser_usb_set_mode;
> > + if (id->driver_info & KVASER_HAS_TXRX_ERRORS)
> > + priv->can.do_get_berr_counter = kvaser_usb_get_berr_counter;
> > + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES;
> > + if (id->driver_info & KVASER_HAS_SILENT_MODE)
> > + priv->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY;
> > +
> > + netdev->flags |= IFF_ECHO;
> > +
> > + netdev->netdev_ops = &kvaser_usb_netdev_ops;
> > +
> > + SET_NETDEV_DEV(netdev, &intf->dev);
> > +
> > + dev->nets[channel] = priv;
> > +
> > + err = register_candev(netdev);
> > + if (err) {
> > + dev_err(&intf->dev, "Failed to register can device\n");
> > + free_candev(netdev);
> > + dev->nets[channel] = NULL;
> > + return err;
> > + }
> > +
> > + netdev_dbg(netdev, "device registered\n");
> > +
> > + return 0;
> > +}
> > +
> > +static void kvaser_usb_get_endpoints(const struct usb_interface *intf,
> > + struct usb_endpoint_descriptor **in,
> > + struct usb_endpoint_descriptor **out)
> > +{
> > + const struct usb_host_interface *iface_desc;
> > + struct usb_endpoint_descriptor *endpoint;
> > + int i;
> > +
> > + iface_desc = &intf->altsetting[0];
> > +
> > + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
> > + endpoint = &iface_desc->endpoint[i].desc;
> > +
> > + if (usb_endpoint_is_bulk_in(endpoint))
> > + *in = endpoint;
> > +
> > + if (usb_endpoint_is_bulk_out(endpoint))
> > + *out = endpoint;
> > + }
> > +}
> > +
> > +static int kvaser_usb_probe(struct usb_interface *intf,
> > + const struct usb_device_id *id)
> > +{
> > + struct kvaser_usb *dev;
> > + int err = -ENOMEM;
> > + int i;
> > +
> > + dev = devm_kzalloc(&intf->dev, sizeof(*dev), GFP_KERNEL);
> > + if (!dev)
> > + return -ENOMEM;
> > +
> > + kvaser_usb_get_endpoints(intf, &dev->bulk_in, &dev->bulk_out);
> > + if (!dev->bulk_in || !dev->bulk_out) {
> > + 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);
> > +
> > + for (i = 0; i < MAX_NET_DEVICES; i++)
> > + kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, i);
> > +
> > + err = kvaser_usb_get_software_info(dev);
> > + if (err) {
> > + dev_err(&intf->dev,
> > + "Cannot get software infos, error %d\n", err);
> > + return err;
> > + }
> > +
> > + err = kvaser_usb_get_card_info(dev);
> > + if (err) {
> > + dev_err(&intf->dev,
> > + "Cannot get card infos, error %d\n", err);
> > + return err;
> > + }
> > +
> > + dev_dbg(&intf->dev, "Firmware version: %d.%d.%d\n",
> > + ((dev->fw_version >> 24) & 0xff),
> > + ((dev->fw_version >> 16) & 0xff),
> > + (dev->fw_version & 0xffff));
> > +
> > + for (i = 0; i < dev->nchannels; i++) {
> > + err = kvaser_usb_init_one(intf, id, 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
> ^^^
> nitpick, please add a "," there.
Ok.
> > +};
> >
> can you please add MODULE_DEVICE_TABLE(usb, kvaser_usb_table);
It is already present just after the kvaser_usb_table structure.
>
> > +module_usb_driver(kvaser_usb_driver);
> > +
> > +MODULE_AUTHOR("Olivier Sobrie <olivier@sobrie.be>");
> > +MODULE_DESCRIPTION("CAN driver for Kvaser CAN/USB devices");
> > +MODULE_LICENSE("GPL v2");
> >
>
> Marc
> --
> Pengutronix e.K. | Marc Kleine-Budde |
> Industrial Linux Solutions | Phone: +49-231-2826-924 |
> Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
> Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
>
Thank you,
--
Olivier
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH v5] can: kvaser_usb: Add support for Kvaser CAN/USB devices
2012-11-20 8:46 ` Olivier Sobrie
@ 2012-11-20 10:59 ` Marc Kleine-Budde
0 siblings, 0 replies; 43+ messages in thread
From: Marc Kleine-Budde @ 2012-11-20 10:59 UTC (permalink / raw)
To: Olivier Sobrie; +Cc: Wolfgang Grandegger, linux-can, netdev, linux-usb
[-- Attachment #1: Type: text/plain, Size: 5252 bytes --]
On 11/20/2012 09:46 AM, Olivier Sobrie wrote:
[...]
>>> +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;
>>> + struct net_device_stats *stats = &netdev->stats;
>>> + struct can_frame *cf = (struct can_frame *)skb->data;
>>> + struct kvaser_usb_tx_urb_context *context = NULL;
>>> + struct urb *urb;
>>> + void *buf;
>>> + struct kvaser_msg *msg;
>>> + int i, err;
>>> + int ret = NETDEV_TX_OK;
>>> +
>>> + if (can_dropped_invalid_skb(netdev, skb))
>>> + return NETDEV_TX_OK;
>>> +
>>> + urb = usb_alloc_urb(0, GFP_ATOMIC);
>>> + if (!urb) {
>>> + netdev_err(netdev, "No memory left for URBs\n");
>>> + stats->tx_dropped++;
>>
>> Move the dev_kfree_skb to the end and goto there.
>
> I assume you mean doing something like that at the end of the function:
Yes.
> releasebuf:
> kfree(buf);
> nobufmem:
> usb_free_urb(urb);
> nourbmem:
> dev_kfree_skb(skb);
> return ret;
>
> If I do that it will give problems when the 'releasebuf' condition is
> reached. The skb buffer will be freed twice. The skb is already freed
> by the function can_free_echo_skb().
Okay. dev_kfree_skb(skb) will work with skb == NULL. So just set skb to
NULL after can_free_echo_skb(). Maybe along with a short comment: "set
to NULL to avoid double free in dev_kfree_skb(skb)".
>
>>
>>> + dev_kfree_skb(skb);
>>> + return NETDEV_TX_OK;
>>> + }
>>> +
>>> + buf = kmalloc(sizeof(struct kvaser_msg), GFP_ATOMIC);
>>> + if (!buf) {
>>> + netdev_err(netdev, "No memory left for USB buffer\n");
>>> + stats->tx_dropped++;
>> You cann usb_free_urb twice...here and in the error handling at the end.
>
> Indeed thanks.
>
>>
>>> + dev_kfree_skb(skb);
>>> + usb_free_urb(urb);
>>> + goto nobufmem;
>>> + }
>>> +
>>> + msg = buf;
>>> + msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_tx_can);
>>> + msg->u.tx_can.flags = 0;
>>> + msg->u.tx_can.channel = priv->channel;
>>> +
>>> + if (cf->can_id & CAN_EFF_FLAG) {
>>> + msg->id = CMD_TX_EXT_MESSAGE;
>>> + msg->u.tx_can.msg[0] = (cf->can_id >> 24) & 0x1f;
>>> + msg->u.tx_can.msg[1] = (cf->can_id >> 18) & 0x3f;
>>> + msg->u.tx_can.msg[2] = (cf->can_id >> 14) & 0x0f;
>>> + msg->u.tx_can.msg[3] = (cf->can_id >> 6) & 0xff;
>>> + msg->u.tx_can.msg[4] = cf->can_id & 0x3f;
>>> + } else {
>>> + msg->id = CMD_TX_STD_MESSAGE;
>>> + msg->u.tx_can.msg[0] = (cf->can_id >> 6) & 0x1f;
>>> + msg->u.tx_can.msg[1] = cf->can_id & 0x3f;
>>> + }
>>> +
>>> + msg->u.tx_can.msg[5] = cf->can_dlc;
>>> + memcpy(&msg->u.tx_can.msg[6], cf->data, cf->can_dlc);
>>> +
>>> + if (cf->can_id & CAN_RTR_FLAG)
>>> + msg->u.tx_can.flags |= MSG_FLAG_REMOTE_FRAME;
>>> +
>>> + for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++) {
>>> + if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) {
>>> + context = &priv->tx_contexts[i];
>>> + break;
>>> + }
>>> + }
>>> +
>>> + if (!context) {
>>> + netdev_warn(netdev, "cannot find free context\n");
>>> + ret = NETDEV_TX_BUSY;
>>> + goto releasebuf;
>>> + }
>>> +
>>> + context->priv = priv;
>>> + context->echo_index = i;
>>> + context->dlc = cf->can_dlc;
>>> +
>>> + msg->u.tx_can.tid = context->echo_index;
>>> +
>>> + usb_fill_bulk_urb(urb, dev->udev,
>>> + usb_sndbulkpipe(dev->udev,
>>> + dev->bulk_out->bEndpointAddress),
>>> + buf, msg->len,
>>> + kvaser_usb_write_bulk_callback, context);
>>> + usb_anchor_urb(urb, &priv->tx_submitted);
>>> +
>>> + can_put_echo_skb(skb, netdev, context->echo_index);
>>> +
>>> + atomic_inc(&priv->active_tx_urbs);
>>> +
>>> + if (atomic_read(&priv->active_tx_urbs) >= MAX_TX_URBS)
>>> + netif_stop_queue(netdev);
>>> +
>>> + err = usb_submit_urb(urb, GFP_ATOMIC);
>>> + if (unlikely(err)) {
>>> + can_free_echo_skb(netdev, context->echo_index);
>>> +
skb = NULL; /* +comment */
>>> + atomic_dec(&priv->active_tx_urbs);
>>> + 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;
>>> + }
>>> +
>>> + usb_free_urb(urb);
>>> +
>>> + return NETDEV_TX_OK;
>>> +
>>> +releasebuf:
>>> + kfree(buf);
>>> +nobufmem:
>>> + usb_free_urb(urb);
>>> + return ret;
>>> +}
[...]
>>> +static struct usb_driver kvaser_usb_driver = {
>>> + .name = "kvaser_usb",
>>> + .probe = kvaser_usb_probe,
>>> + .disconnect = kvaser_usb_disconnect,
>>> + .id_table = kvaser_usb_table
>> ^^^
>> nitpick, please add a "," there.
>
> Ok.
>
>>> +};
>>>
>> can you please add MODULE_DEVICE_TABLE(usb, kvaser_usb_table);
>
> It is already present just after the kvaser_usb_table structure.
:) You're right.
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Industrial Linux Solutions | Phone: +49-231-2826-924 |
Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 261 bytes --]
^ permalink raw reply [flat|nested] 43+ messages in thread
[parent not found: <1343626352-24760-1-git-send-email-olivier-Ui3EtX6WB9GzQB+pC5nmwQ@public.gmane.org>]
* [PATCH v6] can: kvaser_usb: Add support for Kvaser CAN/USB devices
[not found] ` <1343626352-24760-1-git-send-email-olivier-Ui3EtX6WB9GzQB+pC5nmwQ@public.gmane.org>
@ 2012-11-21 7:11 ` Olivier Sobrie
2012-11-22 11:01 ` Marc Kleine-Budde
2012-11-22 15:01 ` Olivier Sobrie
0 siblings, 2 replies; 43+ messages in thread
From: Olivier Sobrie @ 2012-11-21 7:11 UTC (permalink / raw)
To: Wolfgang Grandegger, Marc Kleine-Budde, linux-can-u79uwXL29TY76Z2rM5mHXA
Cc: netdev-u79uwXL29TY76Z2rM5mHXA, linux-usb-u79uwXL29TY76Z2rM5mHXA,
Olivier Sobrie, Daniel Berglund
This driver provides support for several Kvaser CAN/USB devices.
Such kind of devices supports up to three CAN network interfaces.
It has been tested with a Kvaser USB Leaf Light (one network interface)
connected to a pch_can interface.
The firmware version of the Kvaser device was 2.5.205.
List of Kvaser devices supported by the driver:
- 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
Signed-off-by: Daniel Berglund <db-4bktM1XPm2LQT0dZR+AlfA@public.gmane.org>
Signed-off-by: Olivier Sobrie <olivier-Ui3EtX6WB9GzQB+pC5nmwQ@public.gmane.org>
---
Hi,
This version includes the last changes requested by Marc on version 5 of
the patch.
Olivier
drivers/net/can/usb/Kconfig | 29 +
drivers/net/can/usb/Makefile | 1 +
drivers/net/can/usb/kvaser_usb.c | 1598 ++++++++++++++++++++++++++++++++++++++
3 files changed, 1628 insertions(+)
create mode 100644 drivers/net/can/usb/kvaser_usb.c
diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig
index 0a68768..a4e4bee 100644
--- a/drivers/net/can/usb/Kconfig
+++ b/drivers/net/can/usb/Kconfig
@@ -13,6 +13,35 @@ config CAN_ESD_USB2
This driver supports the CAN-USB/2 interface
from esd electronic system design gmbh (http://www.esd.eu).
+config CAN_KVASER_USB
+ tristate "Kvaser CAN/USB interface"
+ ---help---
+ This driver adds support for Kvaser CAN/USB devices like Kvaser
+ Leaf Light.
+
+ The driver gives 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
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called kvaser_usb.
+
config CAN_PEAK_USB
tristate "PEAK PCAN-USB/USB Pro interfaces"
---help---
diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile
index da6d1d3..80a2ee4 100644
--- a/drivers/net/can/usb/Makefile
+++ b/drivers/net/can/usb/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o
obj-$(CONFIG_CAN_ESD_USB2) += esd_usb2.o
+obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o
obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/
ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c
new file mode 100644
index 0000000..8807bf8
--- /dev/null
+++ b/drivers/net/can/usb/kvaser_usb.c
@@ -0,0 +1,1598 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * Parts of this driver are based on the following:
+ * - Kvaser linux leaf driver (version 4.78)
+ * - CAN driver for esd CAN-USB/2
+ *
+ * Copyright (C) 2002-2006 KVASER AB, Sweden. All rights reserved.
+ * Copyright (C) 2010 Matthias Fuchs <matthias.fuchs-iOnpLzIbIdM@public.gmane.org>, esd gmbh
+ * Copyright (C) 2012 Olivier Sobrie <olivier-Ui3EtX6WB9GzQB+pC5nmwQ@public.gmane.org>
+ */
+
+#include <linux/init.h>
+#include <linux/completion.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>
+
+#define MAX_TX_URBS 16
+#define MAX_RX_URBS 4
+#define START_TIMEOUT 1000 /* msecs */
+#define STOP_TIMEOUT 1000 /* msecs */
+#define USB_SEND_TIMEOUT 1000 /* msecs */
+#define USB_RECV_TIMEOUT 1000 /* msecs */
+#define RX_BUFFER_SIZE 3072
+#define CAN_USB_CLOCK 8000000
+#define MAX_NET_DEVICES 3
+
+/* Kvaser USB devices */
+#define KVASER_VENDOR_ID 0x0bfd
+#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
+
+/* USB devices features */
+#define KVASER_HAS_SILENT_MODE BIT(0)
+#define KVASER_HAS_TXRX_ERRORS BIT(1)
+
+/* Message header size */
+#define MSG_HEADER_LEN 2
+
+/* 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 */
+#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)
+
+/* Can msg 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_GET_CTRL_MODE 22
+#define CMD_GET_CTRL_MODE_REPLY 23
+#define CMD_RESET_CHIP 24
+#define CMD_RESET_CARD 25
+#define CMD_START_CHIP 26
+#define CMD_START_CHIP_REPLY 27
+#define CMD_STOP_CHIP 28
+#define CMD_STOP_CHIP_REPLY 29
+#define CMD_GET_CARD_INFO2 32
+#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_RESET_ERROR_COUNTER 49
+#define CMD_TX_ACKNOWLEDGE 50
+#define CMD_CAN_ERROR_EVENT 51
+#define CMD_USB_THROTTLE 77
+#define CMD_LOG_MESSAGE 106
+
+/* 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)
+
+/* bittiming parameters */
+#define KVASER_USB_TSEG1_MIN 1
+#define KVASER_USB_TSEG1_MAX 16
+#define KVASER_USB_TSEG2_MIN 1
+#define KVASER_USB_TSEG2_MAX 8
+#define KVASER_USB_SJW_MAX 4
+#define KVASER_USB_BRP_MIN 1
+#define KVASER_USB_BRP_MAX 64
+#define KVASER_USB_BRP_INC 1
+
+/* 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
+
+struct kvaser_msg_simple {
+ u8 tid;
+ u8 channel;
+} __packed;
+
+struct kvaser_msg_cardinfo {
+ u8 tid;
+ u8 nchannels;
+ __le32 serial_number;
+ __le32 padding;
+ __le32 clock_resolution;
+ __le32 mfgdate;
+ u8 ean[8];
+ u8 hw_revision;
+ u8 usb_hs_mode;
+ __le16 padding2;
+} __packed;
+
+struct kvaser_msg_cardinfo2 {
+ u8 tid;
+ u8 channel;
+ u8 pcb_id[24];
+ __le32 oem_unlock_code;
+} __packed;
+
+struct kvaser_msg_softinfo {
+ u8 tid;
+ u8 channel;
+ __le32 sw_options;
+ __le32 fw_version;
+ __le16 max_outstanding_tx;
+ __le16 padding[9];
+} __packed;
+
+struct kvaser_msg_busparams {
+ u8 tid;
+ u8 channel;
+ __le32 bitrate;
+ u8 tseg1;
+ u8 tseg2;
+ u8 sjw;
+ u8 no_samp;
+} __packed;
+
+struct kvaser_msg_tx_can {
+ u8 channel;
+ u8 tid;
+ u8 msg[14];
+ u8 padding;
+ u8 flags;
+} __packed;
+
+struct kvaser_msg_rx_can {
+ u8 channel;
+ u8 flag;
+ __le16 time[3];
+ u8 msg[14];
+} __packed;
+
+struct kvaser_msg_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 kvaser_msg_tx_acknowledge {
+ u8 channel;
+ u8 tid;
+ __le16 time[3];
+ u8 flags;
+ u8 time_offset;
+} __packed;
+
+struct kvaser_msg_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 kvaser_msg_ctrl_mode {
+ u8 tid;
+ u8 channel;
+ u8 ctrl_mode;
+ u8 padding[3];
+} __packed;
+
+struct kvaser_msg_flush_queue {
+ u8 tid;
+ u8 channel;
+ u8 flags;
+ u8 padding[3];
+} __packed;
+
+struct kvaser_msg_log_message {
+ u8 channel;
+ u8 flags;
+ __le16 time[3];
+ u8 dlc;
+ u8 time_offset;
+ __le32 id;
+ u8 data[8];
+} __packed;
+
+struct kvaser_msg {
+ u8 len;
+ u8 id;
+ union {
+ struct kvaser_msg_simple simple;
+ struct kvaser_msg_cardinfo cardinfo;
+ struct kvaser_msg_cardinfo2 cardinfo2;
+ struct kvaser_msg_softinfo softinfo;
+ struct kvaser_msg_busparams busparams;
+ struct kvaser_msg_tx_can tx_can;
+ struct kvaser_msg_rx_can rx_can;
+ struct kvaser_msg_chip_state_event chip_state_event;
+ struct kvaser_msg_tx_acknowledge tx_acknowledge;
+ struct kvaser_msg_error_event error_event;
+ struct kvaser_msg_ctrl_mode ctrl_mode;
+ struct kvaser_msg_flush_queue flush_queue;
+ struct kvaser_msg_log_message log_message;
+ } u;
+} __packed;
+
+struct kvaser_usb_tx_urb_context {
+ struct kvaser_usb_net_priv *priv;
+ u32 echo_index;
+ int dlc;
+};
+
+struct kvaser_usb {
+ struct usb_device *udev;
+ struct kvaser_usb_net_priv *nets[MAX_NET_DEVICES];
+
+ struct usb_endpoint_descriptor *bulk_in, *bulk_out;
+ struct usb_anchor rx_submitted;
+
+ u32 fw_version;
+ unsigned int nchannels;
+
+ bool rxinitdone;
+ void *rxbuf[MAX_RX_URBS];
+ dma_addr_t rxbuf_dma[MAX_RX_URBS];
+};
+
+struct kvaser_usb_net_priv {
+ struct can_priv can;
+
+ atomic_t active_tx_urbs;
+ struct usb_anchor tx_submitted;
+ struct kvaser_usb_tx_urb_context tx_contexts[MAX_TX_URBS];
+
+ struct completion start_comp, stop_comp;
+
+ struct kvaser_usb *dev;
+ struct net_device *netdev;
+ int channel;
+
+ struct can_berr_counter bec;
+};
+
+static struct usb_device_id kvaser_usb_table[] = {
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_DEVEL_PRODUCT_ID) },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_PRODUCT_ID) },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_SWC_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LIN_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_LS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_SWC_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_DEVEL_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSHS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_UPRO_HSHS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_GI_PRODUCT_ID) },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_OBDII_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSLS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_CH_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_BLACKBIRD_SPRO_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_MERCURY_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_LEAF_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_CAN_R_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, kvaser_usb_table);
+
+static inline int kvaser_usb_send_msg(const struct kvaser_usb *dev,
+ struct kvaser_msg *msg)
+{
+ int actual_len;
+
+ return usb_bulk_msg(dev->udev,
+ usb_sndbulkpipe(dev->udev,
+ dev->bulk_out->bEndpointAddress),
+ msg, msg->len, &actual_len,
+ USB_SEND_TIMEOUT);
+}
+
+static int kvaser_usb_wait_msg(const struct kvaser_usb *dev, u8 id,
+ struct kvaser_msg *msg)
+{
+ struct kvaser_msg *tmp;
+ void *buf;
+ int actual_len;
+ int err;
+ int pos = 0;
+
+ buf = kzalloc(RX_BUFFER_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ err = usb_bulk_msg(dev->udev,
+ usb_rcvbulkpipe(dev->udev,
+ dev->bulk_in->bEndpointAddress),
+ buf, RX_BUFFER_SIZE, &actual_len,
+ USB_RECV_TIMEOUT);
+ if (err < 0)
+ goto end;
+
+ while (pos <= actual_len - MSG_HEADER_LEN) {
+ tmp = buf + pos;
+
+ if (!tmp->len)
+ break;
+
+ if (pos + tmp->len > actual_len) {
+ dev_err(dev->udev->dev.parent, "Format error\n");
+ break;
+ }
+
+ if (tmp->id == id) {
+ memcpy(msg, tmp, tmp->len);
+ goto end;
+ }
+
+ pos += tmp->len;
+ }
+
+ err = -EINVAL;
+
+end:
+ kfree(buf);
+
+ return err;
+}
+
+static int kvaser_usb_send_simple_msg(const struct kvaser_usb *dev,
+ u8 msg_id, int channel)
+{
+ struct kvaser_msg msg = {
+ .len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple),
+ .id = msg_id,
+ .u.simple.channel = channel,
+ .u.simple.tid = 0xff,
+ };
+
+ return kvaser_usb_send_msg(dev, &msg);
+}
+
+static int kvaser_usb_get_software_info(struct kvaser_usb *dev)
+{
+ struct kvaser_msg msg;
+ int err;
+
+ err = kvaser_usb_send_simple_msg(dev, CMD_GET_SOFTWARE_INFO, 0);
+ if (err)
+ return err;
+
+ err = kvaser_usb_wait_msg(dev, CMD_GET_SOFTWARE_INFO_REPLY, &msg);
+ if (err)
+ return err;
+
+ dev->fw_version = le32_to_cpu(msg.u.softinfo.fw_version);
+
+ return 0;
+}
+
+static int kvaser_usb_get_card_info(struct kvaser_usb *dev)
+{
+ struct kvaser_msg msg;
+ int err;
+
+ err = kvaser_usb_send_simple_msg(dev, CMD_GET_CARD_INFO, 0);
+ if (err)
+ return err;
+
+ err = kvaser_usb_wait_msg(dev, CMD_GET_CARD_INFO_REPLY, &msg);
+ if (err)
+ return err;
+
+ dev->nchannels = msg.u.cardinfo.nchannels;
+
+ return 0;
+}
+
+static void kvaser_usb_tx_acknowledge(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ struct net_device_stats *stats;
+ struct kvaser_usb_tx_urb_context *context;
+ struct kvaser_usb_net_priv *priv;
+ struct sk_buff *skb;
+ struct can_frame *cf;
+ u8 channel = msg->u.tx_acknowledge.channel;
+ u8 tid = msg->u.tx_acknowledge.tid;
+
+ if (channel >= dev->nchannels) {
+ dev_err(dev->udev->dev.parent,
+ "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 % 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)) {
+ skb = alloc_can_err_skb(priv->netdev, &cf);
+ if (skb) {
+ cf->can_id |= CAN_ERR_RESTARTED;
+ netif_rx(skb);
+
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+ } 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;
+ can_get_echo_skb(priv->netdev, context->echo_index);
+
+ context->echo_index = MAX_TX_URBS;
+ atomic_dec(&priv->active_tx_urbs);
+
+ netif_wake_queue(priv->netdev);
+}
+
+static void kvaser_usb_simple_msg_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);
+}
+
+static int kvaser_usb_simple_msg_async(struct kvaser_usb_net_priv *priv,
+ u8 msg_id)
+{
+ struct kvaser_usb *dev = priv->dev;
+ struct net_device *netdev = priv->netdev;
+ struct kvaser_msg *msg;
+ struct urb *urb;
+ void *buf;
+ int err;
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb) {
+ netdev_err(netdev, "No memory left for URBs\n");
+ return -ENOMEM;
+ }
+
+ buf = kmalloc(sizeof(struct kvaser_msg), GFP_ATOMIC);
+ if (!buf) {
+ netdev_err(netdev, "No memory left for USB buffer\n");
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ msg = (struct kvaser_msg *)buf;
+ msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple);
+ msg->id = msg_id;
+ msg->u.simple.channel = priv->channel;
+
+ usb_fill_bulk_urb(urb, dev->udev,
+ usb_sndbulkpipe(dev->udev,
+ dev->bulk_out->bEndpointAddress),
+ buf, msg->len,
+ kvaser_usb_simple_msg_callback, priv);
+ 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);
+ kfree(buf);
+ return err;
+ }
+
+ usb_free_urb(urb);
+
+ return 0;
+}
+
+static void kvaser_usb_unlink_tx_urbs(struct kvaser_usb_net_priv *priv)
+{
+ int 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;
+}
+
+static void kvaser_usb_rx_error(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ struct net_device_stats *stats;
+ struct kvaser_usb_net_priv *priv;
+ unsigned int new_state;
+ u8 channel, status, txerr, rxerr, error_factor;
+
+ switch (msg->id) {
+ case CMD_CAN_ERROR_EVENT:
+ channel = msg->u.error_event.channel;
+ status = msg->u.error_event.status;
+ txerr = msg->u.error_event.tx_errors_count;
+ rxerr = msg->u.error_event.rx_errors_count;
+ error_factor = msg->u.error_event.error_factor;
+ break;
+ case CMD_LOG_MESSAGE:
+ channel = msg->u.log_message.channel;
+ status = msg->u.log_message.data[0];
+ txerr = msg->u.log_message.data[2];
+ rxerr = msg->u.log_message.data[3];
+ error_factor = msg->u.log_message.data[1];
+ break;
+ case CMD_CHIP_STATE_EVENT:
+ channel = msg->u.chip_state_event.channel;
+ status = msg->u.chip_state_event.status;
+ txerr = msg->u.chip_state_event.tx_errors_count;
+ rxerr = msg->u.chip_state_event.rx_errors_count;
+ error_factor = 0;
+ break;
+ default:
+ dev_err(dev->udev->dev.parent, "Invalid msg id (%d)\n",
+ msg->id);
+ return;
+ }
+
+ if (channel >= dev->nchannels) {
+ dev_err(dev->udev->dev.parent,
+ "Invalid channel number (%d)\n", channel);
+ return;
+ }
+
+ priv = dev->nets[channel];
+ stats = &priv->netdev->stats;
+
+ if (status & M16C_STATE_BUS_RESET) {
+ kvaser_usb_unlink_tx_urbs(priv);
+ return;
+ }
+
+ skb = alloc_can_err_skb(priv->netdev, &cf);
+ if (!skb) {
+ stats->rx_dropped++;
+ return;
+ }
+
+ new_state = priv->can.state;
+
+ netdev_dbg(priv->netdev, "Error status: 0x%02x\n", status);
+
+ if (status & M16C_STATE_BUS_OFF) {
+ cf->can_id |= CAN_ERR_BUSOFF;
+
+ priv->can.can_stats.bus_off++;
+ if (!priv->can.restart_ms)
+ kvaser_usb_simple_msg_async(priv, CMD_STOP_CHIP);
+
+ netif_carrier_off(priv->netdev);
+
+ new_state = CAN_STATE_BUS_OFF;
+ } else if (status & M16C_STATE_BUS_PASSIVE) {
+ if (priv->can.state != CAN_STATE_ERROR_PASSIVE) {
+ cf->can_id |= CAN_ERR_CRTL;
+
+ if (txerr || rxerr)
+ cf->data[1] = (txerr > rxerr)
+ ? CAN_ERR_CRTL_TX_PASSIVE
+ : CAN_ERR_CRTL_RX_PASSIVE;
+ else
+ cf->data[1] = CAN_ERR_CRTL_TX_PASSIVE |
+ CAN_ERR_CRTL_RX_PASSIVE;
+
+ priv->can.can_stats.error_passive++;
+ }
+
+ new_state = CAN_STATE_ERROR_PASSIVE;
+ }
+
+ if (status == M16C_STATE_BUS_ERROR) {
+ if ((priv->can.state < CAN_STATE_ERROR_WARNING) &&
+ ((txerr >= 96) || (rxerr >= 96))) {
+ cf->can_id |= CAN_ERR_CRTL;
+ cf->data[1] = (txerr > rxerr)
+ ? CAN_ERR_CRTL_TX_WARNING
+ : CAN_ERR_CRTL_RX_WARNING;
+
+ priv->can.can_stats.error_warning++;
+ new_state = CAN_STATE_ERROR_WARNING;
+ } else if (priv->can.state > CAN_STATE_ERROR_ACTIVE) {
+ cf->can_id |= CAN_ERR_PROT;
+ cf->data[2] = CAN_ERR_PROT_ACTIVE;
+
+ new_state = CAN_STATE_ERROR_ACTIVE;
+ }
+ }
+
+ if (!status) {
+ cf->can_id |= CAN_ERR_PROT;
+ cf->data[2] = CAN_ERR_PROT_ACTIVE;
+
+ new_state = CAN_STATE_ERROR_ACTIVE;
+ }
+
+ if (priv->can.restart_ms &&
+ (priv->can.state >= CAN_STATE_BUS_OFF) &&
+ (new_state < CAN_STATE_BUS_OFF)) {
+ cf->can_id |= CAN_ERR_RESTARTED;
+ netif_carrier_on(priv->netdev);
+
+ priv->can.can_stats.restarts++;
+ }
+
+ if (error_factor) {
+ priv->can.can_stats.bus_error++;
+ stats->rx_errors++;
+
+ cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT;
+
+ if (error_factor & M16C_EF_ACKE)
+ cf->data[3] |= (CAN_ERR_PROT_LOC_ACK);
+ if (error_factor & M16C_EF_CRCE)
+ cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ |
+ CAN_ERR_PROT_LOC_CRC_DEL);
+ if (error_factor & M16C_EF_FORME)
+ cf->data[2] |= CAN_ERR_PROT_FORM;
+ if (error_factor & M16C_EF_STFE)
+ cf->data[2] |= CAN_ERR_PROT_STUFF;
+ if (error_factor & M16C_EF_BITE0)
+ cf->data[2] |= CAN_ERR_PROT_BIT0;
+ if (error_factor & M16C_EF_BITE1)
+ cf->data[2] |= CAN_ERR_PROT_BIT1;
+ if (error_factor & M16C_EF_TRE)
+ cf->data[2] |= CAN_ERR_PROT_TX;
+ }
+
+ cf->data[6] = txerr;
+ cf->data[7] = rxerr;
+
+ priv->bec.txerr = txerr;
+ priv->bec.rxerr = rxerr;
+
+ priv->can.state = new_state;
+
+ netif_rx(skb);
+
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+}
+
+static void kvaser_usb_rx_can_err(const struct kvaser_usb_net_priv *priv,
+ const struct kvaser_msg *msg)
+{
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ struct net_device_stats *stats = &priv->netdev->stats;
+
+ if (msg->u.rx_can.flag & (MSG_FLAG_ERROR_FRAME |
+ MSG_FLAG_NERR)) {
+ netdev_err(priv->netdev, "Unknow error (flags: 0x%02x)\n",
+ msg->u.rx_can.flag);
+
+ stats->rx_errors++;
+ return;
+ }
+
+ if (msg->u.rx_can.flag & MSG_FLAG_OVERRUN) {
+ skb = alloc_can_err_skb(priv->netdev, &cf);
+ if (!skb) {
+ stats->rx_dropped++;
+ return;
+ }
+
+ cf->can_id |= CAN_ERR_CRTL;
+ cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+
+ stats->rx_over_errors++;
+ stats->rx_errors++;
+
+ netif_rx(skb);
+
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+ }
+}
+
+static void kvaser_usb_rx_can_msg(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ struct kvaser_usb_net_priv *priv;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ struct net_device_stats *stats;
+ u8 channel = msg->u.rx_can.channel;
+
+ if (channel >= dev->nchannels) {
+ dev_err(dev->udev->dev.parent,
+ "Invalid channel number (%d)\n", channel);
+ return;
+ }
+
+ priv = dev->nets[channel];
+ stats = &priv->netdev->stats;
+
+ if (msg->u.rx_can.flag & (MSG_FLAG_ERROR_FRAME | MSG_FLAG_NERR |
+ MSG_FLAG_OVERRUN)) {
+ kvaser_usb_rx_can_err(priv, msg);
+ return;
+ } else if (msg->u.rx_can.flag & ~MSG_FLAG_REMOTE_FRAME) {
+ netdev_warn(priv->netdev,
+ "Unhandled frame (flags: 0x%02x)",
+ msg->u.rx_can.flag);
+ return;
+ }
+
+ skb = alloc_can_skb(priv->netdev, &cf);
+ if (!skb) {
+ stats->tx_dropped++;
+ return;
+ }
+
+ cf->can_id = ((msg->u.rx_can.msg[0] & 0x1f) << 6) |
+ (msg->u.rx_can.msg[1] & 0x3f);
+ cf->can_dlc = get_can_dlc(msg->u.rx_can.msg[5]);
+
+ if (msg->id == CMD_RX_EXT_MESSAGE) {
+ cf->can_id <<= 18;
+ cf->can_id |= ((msg->u.rx_can.msg[2] & 0x0f) << 14) |
+ ((msg->u.rx_can.msg[3] & 0xff) << 6) |
+ (msg->u.rx_can.msg[4] & 0x3f);
+ cf->can_id |= CAN_EFF_FLAG;
+ }
+
+ if (msg->u.rx_can.flag & MSG_FLAG_REMOTE_FRAME)
+ cf->can_id |= CAN_RTR_FLAG;
+ else
+ memcpy(cf->data, &msg->u.rx_can.msg[6], cf->can_dlc);
+
+ netif_rx(skb);
+
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+}
+
+static void kvaser_usb_start_chip_reply(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ struct kvaser_usb_net_priv *priv;
+ u8 channel = msg->u.simple.channel;
+
+ if (channel >= dev->nchannels) {
+ dev_err(dev->udev->dev.parent,
+ "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_stop_chip_reply(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ struct kvaser_usb_net_priv *priv;
+ u8 channel = msg->u.simple.channel;
+
+ if (channel >= dev->nchannels) {
+ dev_err(dev->udev->dev.parent,
+ "Invalid channel number (%d)\n", channel);
+ return;
+ }
+
+ priv = dev->nets[channel];
+
+ complete(&priv->stop_comp);
+}
+
+static void kvaser_usb_handle_message(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ switch (msg->id) {
+ case CMD_START_CHIP_REPLY:
+ kvaser_usb_start_chip_reply(dev, msg);
+ break;
+
+ case CMD_STOP_CHIP_REPLY:
+ kvaser_usb_stop_chip_reply(dev, msg);
+ break;
+
+ case CMD_RX_STD_MESSAGE:
+ case CMD_RX_EXT_MESSAGE:
+ kvaser_usb_rx_can_msg(dev, msg);
+ break;
+
+ case CMD_CHIP_STATE_EVENT:
+ case CMD_CAN_ERROR_EVENT:
+ kvaser_usb_rx_error(dev, msg);
+ break;
+
+ case CMD_LOG_MESSAGE:
+ if (msg->u.log_message.flags & MSG_FLAG_ERROR_FRAME)
+ kvaser_usb_rx_error(dev, msg);
+ break;
+
+ case CMD_TX_ACKNOWLEDGE:
+ kvaser_usb_tx_acknowledge(dev, msg);
+ break;
+
+ default:
+ dev_warn(dev->udev->dev.parent,
+ "Unhandled message (%d)\n", msg->id);
+ break;
+ }
+}
+
+static void kvaser_usb_read_bulk_callback(struct urb *urb)
+{
+ struct kvaser_usb *dev = urb->context;
+ struct kvaser_msg *msg;
+ int pos = 0;
+ int err, i;
+
+ switch (urb->status) {
+ case 0:
+ break;
+ case -ENOENT:
+ 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 - MSG_HEADER_LEN) {
+ msg = urb->transfer_buffer + pos;
+
+ if (!msg->len)
+ break;
+
+ if (pos + msg->len > urb->actual_length) {
+ dev_err(dev->udev->dev.parent, "Format error\n");
+ break;
+ }
+
+ kvaser_usb_handle_message(dev, msg);
+
+ pos += msg->len;
+ }
+
+resubmit_urb:
+ usb_fill_bulk_urb(urb, dev->udev,
+ usb_rcvbulkpipe(dev->udev,
+ dev->bulk_in->bEndpointAddress),
+ urb->transfer_buffer, 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->udev->dev.parent,
+ "Failed resubmitting read bulk urb: %d\n", err);
+ }
+
+ return;
+}
+
+static int kvaser_usb_setup_rx_urbs(struct kvaser_usb *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;
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ dev_warn(dev->udev->dev.parent,
+ "No memory left for URBs\n");
+ 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");
+ usb_free_urb(urb);
+ err = -ENOMEM;
+ break;
+ }
+
+ usb_fill_bulk_urb(urb, dev->udev,
+ usb_rcvbulkpipe(dev->udev,
+ dev->bulk_in->bEndpointAddress),
+ buf, 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, 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->udev->dev.parent,
+ "Cannot setup read URBs, error %d\n", err);
+ return err;
+ } else if (i < MAX_RX_URBS) {
+ dev_warn(dev->udev->dev.parent,
+ "RX performances may be slow\n");
+ }
+
+ dev->rxinitdone = true;
+
+ return 0;
+}
+
+static int kvaser_usb_set_opt_mode(const struct kvaser_usb_net_priv *priv)
+{
+ struct kvaser_msg msg = {
+ .id = CMD_SET_CTRL_MODE,
+ .len = MSG_HEADER_LEN +
+ sizeof(struct kvaser_msg_ctrl_mode),
+ .u.ctrl_mode.tid = 0xff,
+ .u.ctrl_mode.channel = priv->channel,
+ };
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
+ msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_SILENT;
+ else
+ msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_NORMAL;
+
+ return kvaser_usb_send_msg(priv->dev, &msg);
+}
+
+static int kvaser_usb_start_chip(struct kvaser_usb_net_priv *priv)
+{
+ int err;
+
+ init_completion(&priv->start_comp);
+
+ err = kvaser_usb_send_simple_msg(priv->dev, CMD_START_CHIP,
+ priv->channel);
+ if (err)
+ return err;
+
+ if (!wait_for_completion_timeout(&priv->start_comp,
+ msecs_to_jiffies(START_TIMEOUT)))
+ return -ETIMEDOUT;
+
+ 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;
+ int err;
+
+ err = open_candev(netdev);
+ if (err)
+ return err;
+
+ err = kvaser_usb_setup_rx_urbs(dev);
+ if (err)
+ goto error;
+
+ err = kvaser_usb_set_opt_mode(priv);
+ if (err)
+ goto error;
+
+ err = kvaser_usb_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_unlink_all_urbs(struct kvaser_usb *dev)
+{
+ int i;
+
+ 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 < MAX_NET_DEVICES; i++) {
+ struct kvaser_usb_net_priv *priv = dev->nets[i];
+
+ if (priv)
+ kvaser_usb_unlink_tx_urbs(priv);
+ }
+}
+
+static int kvaser_usb_stop_chip(struct kvaser_usb_net_priv *priv)
+{
+ int err;
+
+ init_completion(&priv->stop_comp);
+
+ err = kvaser_usb_send_simple_msg(priv->dev, CMD_STOP_CHIP,
+ priv->channel);
+ if (err)
+ return err;
+
+ if (!wait_for_completion_timeout(&priv->stop_comp,
+ msecs_to_jiffies(STOP_TIMEOUT)))
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int kvaser_usb_flush_queue(struct kvaser_usb_net_priv *priv)
+{
+ struct kvaser_msg msg = {
+ .id = CMD_FLUSH_QUEUE,
+ .len = MSG_HEADER_LEN +
+ sizeof(struct kvaser_msg_flush_queue),
+ .u.flush_queue.channel = priv->channel,
+ .u.flush_queue.flags = 0x00,
+ };
+
+ return kvaser_usb_send_msg(priv->dev, &msg);
+}
+
+static int kvaser_usb_close(struct net_device *netdev)
+{
+ struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
+ struct kvaser_usb *dev = priv->dev;
+ int err;
+
+ netif_stop_queue(netdev);
+
+ err = kvaser_usb_flush_queue(priv);
+ if (err)
+ netdev_warn(netdev, "Cannot flush queue, error %d\n", err);
+
+ if (kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, priv->channel))
+ netdev_warn(netdev, "Cannot reset card, error %d\n", err);
+
+ err = kvaser_usb_stop_chip(priv);
+ if (err)
+ netdev_warn(netdev, "Cannot stop device, error %d\n", err);
+
+ priv->can.state = CAN_STATE_STOPPED;
+ close_candev(priv->netdev);
+
+ return 0;
+}
+
+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;
+ struct net_device_stats *stats = &netdev->stats;
+ struct can_frame *cf = (struct can_frame *)skb->data;
+ struct kvaser_usb_tx_urb_context *context = NULL;
+ struct urb *urb;
+ void *buf;
+ struct kvaser_msg *msg;
+ int i, err;
+ int ret = NETDEV_TX_OK;
+
+ if (can_dropped_invalid_skb(netdev, skb))
+ return NETDEV_TX_OK;
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb) {
+ netdev_err(netdev, "No memory left for URBs\n");
+ stats->tx_dropped++;
+ goto nourbmem;
+ }
+
+ buf = kmalloc(sizeof(struct kvaser_msg), GFP_ATOMIC);
+ if (!buf) {
+ netdev_err(netdev, "No memory left for USB buffer\n");
+ stats->tx_dropped++;
+ goto nobufmem;
+ }
+
+ msg = buf;
+ msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_tx_can);
+ msg->u.tx_can.flags = 0;
+ msg->u.tx_can.channel = priv->channel;
+
+ if (cf->can_id & CAN_EFF_FLAG) {
+ msg->id = CMD_TX_EXT_MESSAGE;
+ msg->u.tx_can.msg[0] = (cf->can_id >> 24) & 0x1f;
+ msg->u.tx_can.msg[1] = (cf->can_id >> 18) & 0x3f;
+ msg->u.tx_can.msg[2] = (cf->can_id >> 14) & 0x0f;
+ msg->u.tx_can.msg[3] = (cf->can_id >> 6) & 0xff;
+ msg->u.tx_can.msg[4] = cf->can_id & 0x3f;
+ } else {
+ msg->id = CMD_TX_STD_MESSAGE;
+ msg->u.tx_can.msg[0] = (cf->can_id >> 6) & 0x1f;
+ msg->u.tx_can.msg[1] = cf->can_id & 0x3f;
+ }
+
+ msg->u.tx_can.msg[5] = cf->can_dlc;
+ memcpy(&msg->u.tx_can.msg[6], cf->data, cf->can_dlc);
+
+ if (cf->can_id & CAN_RTR_FLAG)
+ msg->u.tx_can.flags |= MSG_FLAG_REMOTE_FRAME;
+
+ for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++) {
+ if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) {
+ context = &priv->tx_contexts[i];
+ break;
+ }
+ }
+
+ if (!context) {
+ netdev_warn(netdev, "cannot find free context\n");
+ ret = NETDEV_TX_BUSY;
+ goto releasebuf;
+ }
+
+ context->priv = priv;
+ context->echo_index = i;
+ context->dlc = cf->can_dlc;
+
+ msg->u.tx_can.tid = context->echo_index;
+
+ usb_fill_bulk_urb(urb, dev->udev,
+ usb_sndbulkpipe(dev->udev,
+ dev->bulk_out->bEndpointAddress),
+ buf, msg->len,
+ kvaser_usb_write_bulk_callback, context);
+ usb_anchor_urb(urb, &priv->tx_submitted);
+
+ can_put_echo_skb(skb, netdev, context->echo_index);
+
+ atomic_inc(&priv->active_tx_urbs);
+
+ if (atomic_read(&priv->active_tx_urbs) >= MAX_TX_URBS)
+ netif_stop_queue(netdev);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (unlikely(err)) {
+ can_free_echo_skb(netdev, context->echo_index);
+
+ skb = NULL; /* set to NULL to avoid double free in
+ * dev_kfree_skb(skb) */
+
+ atomic_dec(&priv->active_tx_urbs);
+ 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;
+ }
+
+ usb_free_urb(urb);
+
+ return NETDEV_TX_OK;
+
+releasebuf:
+ kfree(buf);
+nobufmem:
+ usb_free_urb(urb);
+nourbmem:
+ dev_kfree_skb(skb);
+ 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,
+};
+
+static struct can_bittiming_const kvaser_usb_bittiming_const = {
+ .name = "kvaser_usb",
+ .tseg1_min = KVASER_USB_TSEG1_MIN,
+ .tseg1_max = KVASER_USB_TSEG1_MAX,
+ .tseg2_min = KVASER_USB_TSEG2_MIN,
+ .tseg2_max = KVASER_USB_TSEG2_MAX,
+ .sjw_max = KVASER_USB_SJW_MAX,
+ .brp_min = KVASER_USB_BRP_MIN,
+ .brp_max = KVASER_USB_BRP_MAX,
+ .brp_inc = KVASER_USB_BRP_INC,
+};
+
+static int kvaser_usb_set_bittiming(struct net_device *netdev)
+{
+ struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
+ struct can_bittiming *bt = &priv->can.bittiming;
+ struct kvaser_usb *dev = priv->dev;
+ struct kvaser_msg msg = {
+ .id = CMD_SET_BUS_PARAMS,
+ .len = MSG_HEADER_LEN +
+ sizeof(struct kvaser_msg_busparams),
+ .u.busparams.channel = priv->channel,
+ .u.busparams.tid = 0xff,
+ .u.busparams.bitrate = cpu_to_le32(bt->bitrate),
+ .u.busparams.sjw = bt->sjw,
+ .u.busparams.tseg1 = bt->prop_seg + bt->phase_seg1,
+ .u.busparams.tseg2 = bt->phase_seg2,
+ };
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
+ msg.u.busparams.no_samp = 3;
+ else
+ msg.u.busparams.no_samp = 1;
+
+ return kvaser_usb_send_msg(dev, &msg);
+}
+
+static int kvaser_usb_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:
+ err = kvaser_usb_simple_msg_async(priv, CMD_START_CHIP);
+ if (err)
+ return err;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int kvaser_usb_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 void kvaser_usb_remove_interfaces(struct kvaser_usb *dev)
+{
+ int i;
+
+ for (i = 0; i < dev->nchannels; i++) {
+ if (!dev->nets[i])
+ continue;
+
+ unregister_netdev(dev->nets[i]->netdev);
+ }
+
+ kvaser_usb_unlink_all_urbs(dev);
+
+ for (i = 0; i < dev->nchannels; i++) {
+ if (!dev->nets[i])
+ continue;
+
+ free_candev(dev->nets[i]->netdev);
+ }
+}
+
+static int kvaser_usb_init_one(struct usb_interface *intf,
+ const struct usb_device_id *id, int channel)
+{
+ struct kvaser_usb *dev = usb_get_intfdata(intf);
+ struct net_device *netdev;
+ struct kvaser_usb_net_priv *priv;
+ int i, err;
+
+ netdev = alloc_candev(sizeof(*priv), MAX_TX_URBS);
+ if (!netdev) {
+ dev_err(&intf->dev, "Cannot alloc candev\n");
+ return -ENOMEM;
+ }
+
+ priv = netdev_priv(netdev);
+
+ init_completion(&priv->start_comp);
+ init_completion(&priv->stop_comp);
+
+ init_usb_anchor(&priv->tx_submitted);
+ atomic_set(&priv->active_tx_urbs, 0);
+
+ for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++)
+ priv->tx_contexts[i].echo_index = MAX_TX_URBS;
+
+ priv->dev = dev;
+ priv->netdev = netdev;
+ priv->channel = channel;
+
+ priv->can.state = CAN_STATE_STOPPED;
+ priv->can.clock.freq = CAN_USB_CLOCK;
+ priv->can.bittiming_const = &kvaser_usb_bittiming_const;
+ priv->can.do_set_bittiming = kvaser_usb_set_bittiming;
+ priv->can.do_set_mode = kvaser_usb_set_mode;
+ if (id->driver_info & KVASER_HAS_TXRX_ERRORS)
+ priv->can.do_get_berr_counter = kvaser_usb_get_berr_counter;
+ priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES;
+ if (id->driver_info & KVASER_HAS_SILENT_MODE)
+ priv->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY;
+
+ netdev->flags |= IFF_ECHO;
+
+ netdev->netdev_ops = &kvaser_usb_netdev_ops;
+
+ SET_NETDEV_DEV(netdev, &intf->dev);
+
+ dev->nets[channel] = priv;
+
+ err = register_candev(netdev);
+ if (err) {
+ dev_err(&intf->dev, "Failed to register can device\n");
+ free_candev(netdev);
+ dev->nets[channel] = NULL;
+ return err;
+ }
+
+ netdev_dbg(netdev, "device registered\n");
+
+ return 0;
+}
+
+static void kvaser_usb_get_endpoints(const struct usb_interface *intf,
+ struct usb_endpoint_descriptor **in,
+ struct usb_endpoint_descriptor **out)
+{
+ const struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ int i;
+
+ iface_desc = &intf->altsetting[0];
+
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+
+ if (usb_endpoint_is_bulk_in(endpoint))
+ *in = endpoint;
+
+ if (usb_endpoint_is_bulk_out(endpoint))
+ *out = endpoint;
+ }
+}
+
+static int kvaser_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct kvaser_usb *dev;
+ int err = -ENOMEM;
+ int i;
+
+ dev = devm_kzalloc(&intf->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ kvaser_usb_get_endpoints(intf, &dev->bulk_in, &dev->bulk_out);
+ if (!dev->bulk_in || !dev->bulk_out) {
+ 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);
+
+ for (i = 0; i < MAX_NET_DEVICES; i++)
+ kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, i);
+
+ err = kvaser_usb_get_software_info(dev);
+ if (err) {
+ dev_err(&intf->dev,
+ "Cannot get software infos, error %d\n", err);
+ return err;
+ }
+
+ err = kvaser_usb_get_card_info(dev);
+ if (err) {
+ dev_err(&intf->dev,
+ "Cannot get card infos, error %d\n", err);
+ return err;
+ }
+
+ dev_dbg(&intf->dev, "Firmware version: %d.%d.%d\n",
+ ((dev->fw_version >> 24) & 0xff),
+ ((dev->fw_version >> 16) & 0xff),
+ (dev->fw_version & 0xffff));
+
+ for (i = 0; i < dev->nchannels; i++) {
+ err = kvaser_usb_init_one(intf, id, 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-Ui3EtX6WB9GzQB+pC5nmwQ@public.gmane.org>");
+MODULE_DESCRIPTION("CAN driver for Kvaser CAN/USB devices");
+MODULE_LICENSE("GPL v2");
--
1.7.9.5
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related [flat|nested] 43+ messages in thread
* Re: [PATCH v6] can: kvaser_usb: Add support for Kvaser CAN/USB devices
2012-11-21 7:11 ` [PATCH v6] " Olivier Sobrie
@ 2012-11-22 11:01 ` Marc Kleine-Budde
2012-11-22 15:01 ` Olivier Sobrie
1 sibling, 0 replies; 43+ messages in thread
From: Marc Kleine-Budde @ 2012-11-22 11:01 UTC (permalink / raw)
To: Olivier Sobrie
Cc: Wolfgang Grandegger, linux-can, netdev, linux-usb, Daniel Berglund
[-- Attachment #1: Type: text/plain, Size: 4436 bytes --]
On 11/21/2012 08:11 AM, Olivier Sobrie wrote:
> This driver provides support for several Kvaser CAN/USB devices.
> Such kind of devices supports up to three CAN network interfaces.
>
> It has been tested with a Kvaser USB Leaf Light (one network interface)
> connected to a pch_can interface.
> The firmware version of the Kvaser device was 2.5.205.
>
> List of Kvaser devices supported by the driver:
> - 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
>
> Signed-off-by: Daniel Berglund <db@kvaser.com>
> Signed-off-by: Olivier Sobrie <olivier@sobrie.be>
> ---
[...]
> +static struct usb_device_id kvaser_usb_table[] = {
this can be const
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_DEVEL_PRODUCT_ID) },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_PRODUCT_ID) },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> + KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> + KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LS_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> + KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_SWC_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> + KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LIN_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> + KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_LS_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> + KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_SWC_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> + KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_DEVEL_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> + KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSHS_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> + KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_UPRO_HSHS_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_GI_PRODUCT_ID) },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_OBDII_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> + KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSLS_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_CH_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_BLACKBIRD_SPRO_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_MERCURY_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_LEAF_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_CAN_R_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS },
> + { }
> +};
[...]
> +static struct can_bittiming_const kvaser_usb_bittiming_const = {
this, too.
> + .name = "kvaser_usb",
> + .tseg1_min = KVASER_USB_TSEG1_MIN,
> + .tseg1_max = KVASER_USB_TSEG1_MAX,
> + .tseg2_min = KVASER_USB_TSEG2_MIN,
> + .tseg2_max = KVASER_USB_TSEG2_MAX,
> + .sjw_max = KVASER_USB_SJW_MAX,
> + .brp_min = KVASER_USB_BRP_MIN,
> + .brp_max = KVASER_USB_BRP_MAX,
> + .brp_inc = KVASER_USB_BRP_INC,
> +};
I'm adding the consts while applying the patch.
Thanks,
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Industrial Linux Solutions | Phone: +49-231-2826-924 |
Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 261 bytes --]
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH v6] can: kvaser_usb: Add support for Kvaser CAN/USB devices
2012-11-21 7:11 ` [PATCH v6] " Olivier Sobrie
2012-11-22 11:01 ` Marc Kleine-Budde
@ 2012-11-22 15:01 ` Olivier Sobrie
2012-11-22 21:30 ` Greg KH
1 sibling, 1 reply; 43+ messages in thread
From: Olivier Sobrie @ 2012-11-22 15:01 UTC (permalink / raw)
To: Wolfgang Grandegger, Marc Kleine-Budde, linux-can
Cc: netdev, linux-usb, Daniel Berglund
Hi linux-usb folks,
Is there someone who can help me to fix the following errors?
smatch warnings:
+ drivers/net/can/usb/kvaser_usb.c:431 kvaser_usb_send_simple_msg() error: doing
+dma on the stack ((null))
+ drivers/net/can/usb/kvaser_usb.c:1073 kvaser_usb_set_opt_mode() error: doing
+dma on the stack ((null))
+ drivers/net/can/usb/kvaser_usb.c:1174 kvaser_usb_flush_queue() error: doing
+dma on the stack ((null))
+ drivers/net/can/usb/kvaser_usb.c:1384 kvaser_usb_set_bittiming() error: doing
+dma on the stack ((null))
I assume it's due to the buffer I pass to the function usb_bulk_msg()
which is on the stack and can't be.
Do I just have to kmalloc a buffer and give it to the usb_bulk_msg()
function? That's what I understood by reading
"Documentation/DMA-API-HOWTO.txt" section "What memory is DMA'able?"...
and from commit
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=commitdiff;h=32ec4576c3fb37316b1d11a04b220527822f3f0d
Thanks,
Olivier
On Wed, Nov 21, 2012 at 08:11:13AM +0100, Olivier Sobrie wrote:
> This driver provides support for several Kvaser CAN/USB devices.
> Such kind of devices supports up to three CAN network interfaces.
>
> It has been tested with a Kvaser USB Leaf Light (one network interface)
> connected to a pch_can interface.
> The firmware version of the Kvaser device was 2.5.205.
>
> List of Kvaser devices supported by the driver:
> - 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
>
> Signed-off-by: Daniel Berglund <db@kvaser.com>
> Signed-off-by: Olivier Sobrie <olivier@sobrie.be>
> ---
> Hi,
>
> This version includes the last changes requested by Marc on version 5 of
> the patch.
>
> Olivier
>
> drivers/net/can/usb/Kconfig | 29 +
> drivers/net/can/usb/Makefile | 1 +
> drivers/net/can/usb/kvaser_usb.c | 1598 ++++++++++++++++++++++++++++++++++++++
> 3 files changed, 1628 insertions(+)
> create mode 100644 drivers/net/can/usb/kvaser_usb.c
>
> diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig
> index 0a68768..a4e4bee 100644
> --- a/drivers/net/can/usb/Kconfig
> +++ b/drivers/net/can/usb/Kconfig
> @@ -13,6 +13,35 @@ config CAN_ESD_USB2
> This driver supports the CAN-USB/2 interface
> from esd electronic system design gmbh (http://www.esd.eu).
>
> +config CAN_KVASER_USB
> + tristate "Kvaser CAN/USB interface"
> + ---help---
> + This driver adds support for Kvaser CAN/USB devices like Kvaser
> + Leaf Light.
> +
> + The driver gives 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
> +
> + If unsure, say N.
> +
> + To compile this driver as a module, choose M here: the
> + module will be called kvaser_usb.
> +
> config CAN_PEAK_USB
> tristate "PEAK PCAN-USB/USB Pro interfaces"
> ---help---
> diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile
> index da6d1d3..80a2ee4 100644
> --- a/drivers/net/can/usb/Makefile
> +++ b/drivers/net/can/usb/Makefile
> @@ -4,6 +4,7 @@
>
> obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o
> obj-$(CONFIG_CAN_ESD_USB2) += esd_usb2.o
> +obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o
> obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/
>
> ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
> diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c
> new file mode 100644
> index 0000000..8807bf8
> --- /dev/null
> +++ b/drivers/net/can/usb/kvaser_usb.c
> @@ -0,0 +1,1598 @@
> +/*
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation version 2.
> + *
> + * Parts of this driver are based on the following:
> + * - Kvaser linux leaf driver (version 4.78)
> + * - CAN driver for esd CAN-USB/2
> + *
> + * Copyright (C) 2002-2006 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>
> + */
> +
> +#include <linux/init.h>
> +#include <linux/completion.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>
> +
> +#define MAX_TX_URBS 16
> +#define MAX_RX_URBS 4
> +#define START_TIMEOUT 1000 /* msecs */
> +#define STOP_TIMEOUT 1000 /* msecs */
> +#define USB_SEND_TIMEOUT 1000 /* msecs */
> +#define USB_RECV_TIMEOUT 1000 /* msecs */
> +#define RX_BUFFER_SIZE 3072
> +#define CAN_USB_CLOCK 8000000
> +#define MAX_NET_DEVICES 3
> +
> +/* Kvaser USB devices */
> +#define KVASER_VENDOR_ID 0x0bfd
> +#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
> +
> +/* USB devices features */
> +#define KVASER_HAS_SILENT_MODE BIT(0)
> +#define KVASER_HAS_TXRX_ERRORS BIT(1)
> +
> +/* Message header size */
> +#define MSG_HEADER_LEN 2
> +
> +/* 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 */
> +#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)
> +
> +/* Can msg 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_GET_CTRL_MODE 22
> +#define CMD_GET_CTRL_MODE_REPLY 23
> +#define CMD_RESET_CHIP 24
> +#define CMD_RESET_CARD 25
> +#define CMD_START_CHIP 26
> +#define CMD_START_CHIP_REPLY 27
> +#define CMD_STOP_CHIP 28
> +#define CMD_STOP_CHIP_REPLY 29
> +#define CMD_GET_CARD_INFO2 32
> +#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_RESET_ERROR_COUNTER 49
> +#define CMD_TX_ACKNOWLEDGE 50
> +#define CMD_CAN_ERROR_EVENT 51
> +#define CMD_USB_THROTTLE 77
> +#define CMD_LOG_MESSAGE 106
> +
> +/* 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)
> +
> +/* bittiming parameters */
> +#define KVASER_USB_TSEG1_MIN 1
> +#define KVASER_USB_TSEG1_MAX 16
> +#define KVASER_USB_TSEG2_MIN 1
> +#define KVASER_USB_TSEG2_MAX 8
> +#define KVASER_USB_SJW_MAX 4
> +#define KVASER_USB_BRP_MIN 1
> +#define KVASER_USB_BRP_MAX 64
> +#define KVASER_USB_BRP_INC 1
> +
> +/* 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
> +
> +struct kvaser_msg_simple {
> + u8 tid;
> + u8 channel;
> +} __packed;
> +
> +struct kvaser_msg_cardinfo {
> + u8 tid;
> + u8 nchannels;
> + __le32 serial_number;
> + __le32 padding;
> + __le32 clock_resolution;
> + __le32 mfgdate;
> + u8 ean[8];
> + u8 hw_revision;
> + u8 usb_hs_mode;
> + __le16 padding2;
> +} __packed;
> +
> +struct kvaser_msg_cardinfo2 {
> + u8 tid;
> + u8 channel;
> + u8 pcb_id[24];
> + __le32 oem_unlock_code;
> +} __packed;
> +
> +struct kvaser_msg_softinfo {
> + u8 tid;
> + u8 channel;
> + __le32 sw_options;
> + __le32 fw_version;
> + __le16 max_outstanding_tx;
> + __le16 padding[9];
> +} __packed;
> +
> +struct kvaser_msg_busparams {
> + u8 tid;
> + u8 channel;
> + __le32 bitrate;
> + u8 tseg1;
> + u8 tseg2;
> + u8 sjw;
> + u8 no_samp;
> +} __packed;
> +
> +struct kvaser_msg_tx_can {
> + u8 channel;
> + u8 tid;
> + u8 msg[14];
> + u8 padding;
> + u8 flags;
> +} __packed;
> +
> +struct kvaser_msg_rx_can {
> + u8 channel;
> + u8 flag;
> + __le16 time[3];
> + u8 msg[14];
> +} __packed;
> +
> +struct kvaser_msg_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 kvaser_msg_tx_acknowledge {
> + u8 channel;
> + u8 tid;
> + __le16 time[3];
> + u8 flags;
> + u8 time_offset;
> +} __packed;
> +
> +struct kvaser_msg_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 kvaser_msg_ctrl_mode {
> + u8 tid;
> + u8 channel;
> + u8 ctrl_mode;
> + u8 padding[3];
> +} __packed;
> +
> +struct kvaser_msg_flush_queue {
> + u8 tid;
> + u8 channel;
> + u8 flags;
> + u8 padding[3];
> +} __packed;
> +
> +struct kvaser_msg_log_message {
> + u8 channel;
> + u8 flags;
> + __le16 time[3];
> + u8 dlc;
> + u8 time_offset;
> + __le32 id;
> + u8 data[8];
> +} __packed;
> +
> +struct kvaser_msg {
> + u8 len;
> + u8 id;
> + union {
> + struct kvaser_msg_simple simple;
> + struct kvaser_msg_cardinfo cardinfo;
> + struct kvaser_msg_cardinfo2 cardinfo2;
> + struct kvaser_msg_softinfo softinfo;
> + struct kvaser_msg_busparams busparams;
> + struct kvaser_msg_tx_can tx_can;
> + struct kvaser_msg_rx_can rx_can;
> + struct kvaser_msg_chip_state_event chip_state_event;
> + struct kvaser_msg_tx_acknowledge tx_acknowledge;
> + struct kvaser_msg_error_event error_event;
> + struct kvaser_msg_ctrl_mode ctrl_mode;
> + struct kvaser_msg_flush_queue flush_queue;
> + struct kvaser_msg_log_message log_message;
> + } u;
> +} __packed;
> +
> +struct kvaser_usb_tx_urb_context {
> + struct kvaser_usb_net_priv *priv;
> + u32 echo_index;
> + int dlc;
> +};
> +
> +struct kvaser_usb {
> + struct usb_device *udev;
> + struct kvaser_usb_net_priv *nets[MAX_NET_DEVICES];
> +
> + struct usb_endpoint_descriptor *bulk_in, *bulk_out;
> + struct usb_anchor rx_submitted;
> +
> + u32 fw_version;
> + unsigned int nchannels;
> +
> + bool rxinitdone;
> + void *rxbuf[MAX_RX_URBS];
> + dma_addr_t rxbuf_dma[MAX_RX_URBS];
> +};
> +
> +struct kvaser_usb_net_priv {
> + struct can_priv can;
> +
> + atomic_t active_tx_urbs;
> + struct usb_anchor tx_submitted;
> + struct kvaser_usb_tx_urb_context tx_contexts[MAX_TX_URBS];
> +
> + struct completion start_comp, stop_comp;
> +
> + struct kvaser_usb *dev;
> + struct net_device *netdev;
> + int channel;
> +
> + struct can_berr_counter bec;
> +};
> +
> +static struct usb_device_id kvaser_usb_table[] = {
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_DEVEL_PRODUCT_ID) },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_PRODUCT_ID) },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> + KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> + KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LS_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> + KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_SWC_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> + KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LIN_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> + KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_LS_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> + KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_SWC_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> + KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_DEVEL_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> + KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSHS_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> + KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_UPRO_HSHS_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_GI_PRODUCT_ID) },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_OBDII_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> + KVASER_HAS_SILENT_MODE },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSLS_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_CH_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_BLACKBIRD_SPRO_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_MERCURY_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_LEAF_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS },
> + { USB_DEVICE(KVASER_VENDOR_ID, USB_CAN_R_PRODUCT_ID),
> + .driver_info = KVASER_HAS_TXRX_ERRORS },
> + { }
> +};
> +MODULE_DEVICE_TABLE(usb, kvaser_usb_table);
> +
> +static inline int kvaser_usb_send_msg(const struct kvaser_usb *dev,
> + struct kvaser_msg *msg)
> +{
> + int actual_len;
> +
> + return usb_bulk_msg(dev->udev,
> + usb_sndbulkpipe(dev->udev,
> + dev->bulk_out->bEndpointAddress),
> + msg, msg->len, &actual_len,
> + USB_SEND_TIMEOUT);
> +}
> +
> +static int kvaser_usb_wait_msg(const struct kvaser_usb *dev, u8 id,
> + struct kvaser_msg *msg)
> +{
> + struct kvaser_msg *tmp;
> + void *buf;
> + int actual_len;
> + int err;
> + int pos = 0;
> +
> + buf = kzalloc(RX_BUFFER_SIZE, GFP_KERNEL);
> + if (!buf)
> + return -ENOMEM;
> +
> + err = usb_bulk_msg(dev->udev,
> + usb_rcvbulkpipe(dev->udev,
> + dev->bulk_in->bEndpointAddress),
> + buf, RX_BUFFER_SIZE, &actual_len,
> + USB_RECV_TIMEOUT);
> + if (err < 0)
> + goto end;
> +
> + while (pos <= actual_len - MSG_HEADER_LEN) {
> + tmp = buf + pos;
> +
> + if (!tmp->len)
> + break;
> +
> + if (pos + tmp->len > actual_len) {
> + dev_err(dev->udev->dev.parent, "Format error\n");
> + break;
> + }
> +
> + if (tmp->id == id) {
> + memcpy(msg, tmp, tmp->len);
> + goto end;
> + }
> +
> + pos += tmp->len;
> + }
> +
> + err = -EINVAL;
> +
> +end:
> + kfree(buf);
> +
> + return err;
> +}
> +
> +static int kvaser_usb_send_simple_msg(const struct kvaser_usb *dev,
> + u8 msg_id, int channel)
> +{
> + struct kvaser_msg msg = {
> + .len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple),
> + .id = msg_id,
> + .u.simple.channel = channel,
> + .u.simple.tid = 0xff,
> + };
> +
> + return kvaser_usb_send_msg(dev, &msg);
> +}
> +
> +static int kvaser_usb_get_software_info(struct kvaser_usb *dev)
> +{
> + struct kvaser_msg msg;
> + int err;
> +
> + err = kvaser_usb_send_simple_msg(dev, CMD_GET_SOFTWARE_INFO, 0);
> + if (err)
> + return err;
> +
> + err = kvaser_usb_wait_msg(dev, CMD_GET_SOFTWARE_INFO_REPLY, &msg);
> + if (err)
> + return err;
> +
> + dev->fw_version = le32_to_cpu(msg.u.softinfo.fw_version);
> +
> + return 0;
> +}
> +
> +static int kvaser_usb_get_card_info(struct kvaser_usb *dev)
> +{
> + struct kvaser_msg msg;
> + int err;
> +
> + err = kvaser_usb_send_simple_msg(dev, CMD_GET_CARD_INFO, 0);
> + if (err)
> + return err;
> +
> + err = kvaser_usb_wait_msg(dev, CMD_GET_CARD_INFO_REPLY, &msg);
> + if (err)
> + return err;
> +
> + dev->nchannels = msg.u.cardinfo.nchannels;
> +
> + return 0;
> +}
> +
> +static void kvaser_usb_tx_acknowledge(const struct kvaser_usb *dev,
> + const struct kvaser_msg *msg)
> +{
> + struct net_device_stats *stats;
> + struct kvaser_usb_tx_urb_context *context;
> + struct kvaser_usb_net_priv *priv;
> + struct sk_buff *skb;
> + struct can_frame *cf;
> + u8 channel = msg->u.tx_acknowledge.channel;
> + u8 tid = msg->u.tx_acknowledge.tid;
> +
> + if (channel >= dev->nchannels) {
> + dev_err(dev->udev->dev.parent,
> + "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 % 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)) {
> + skb = alloc_can_err_skb(priv->netdev, &cf);
> + if (skb) {
> + cf->can_id |= CAN_ERR_RESTARTED;
> + netif_rx(skb);
> +
> + stats->rx_packets++;
> + stats->rx_bytes += cf->can_dlc;
> + } 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;
> + can_get_echo_skb(priv->netdev, context->echo_index);
> +
> + context->echo_index = MAX_TX_URBS;
> + atomic_dec(&priv->active_tx_urbs);
> +
> + netif_wake_queue(priv->netdev);
> +}
> +
> +static void kvaser_usb_simple_msg_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);
> +}
> +
> +static int kvaser_usb_simple_msg_async(struct kvaser_usb_net_priv *priv,
> + u8 msg_id)
> +{
> + struct kvaser_usb *dev = priv->dev;
> + struct net_device *netdev = priv->netdev;
> + struct kvaser_msg *msg;
> + struct urb *urb;
> + void *buf;
> + int err;
> +
> + urb = usb_alloc_urb(0, GFP_ATOMIC);
> + if (!urb) {
> + netdev_err(netdev, "No memory left for URBs\n");
> + return -ENOMEM;
> + }
> +
> + buf = kmalloc(sizeof(struct kvaser_msg), GFP_ATOMIC);
> + if (!buf) {
> + netdev_err(netdev, "No memory left for USB buffer\n");
> + usb_free_urb(urb);
> + return -ENOMEM;
> + }
> +
> + msg = (struct kvaser_msg *)buf;
> + msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple);
> + msg->id = msg_id;
> + msg->u.simple.channel = priv->channel;
> +
> + usb_fill_bulk_urb(urb, dev->udev,
> + usb_sndbulkpipe(dev->udev,
> + dev->bulk_out->bEndpointAddress),
> + buf, msg->len,
> + kvaser_usb_simple_msg_callback, priv);
> + 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);
> + kfree(buf);
> + return err;
> + }
> +
> + usb_free_urb(urb);
> +
> + return 0;
> +}
> +
> +static void kvaser_usb_unlink_tx_urbs(struct kvaser_usb_net_priv *priv)
> +{
> + int 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;
> +}
> +
> +static void kvaser_usb_rx_error(const struct kvaser_usb *dev,
> + const struct kvaser_msg *msg)
> +{
> + struct can_frame *cf;
> + struct sk_buff *skb;
> + struct net_device_stats *stats;
> + struct kvaser_usb_net_priv *priv;
> + unsigned int new_state;
> + u8 channel, status, txerr, rxerr, error_factor;
> +
> + switch (msg->id) {
> + case CMD_CAN_ERROR_EVENT:
> + channel = msg->u.error_event.channel;
> + status = msg->u.error_event.status;
> + txerr = msg->u.error_event.tx_errors_count;
> + rxerr = msg->u.error_event.rx_errors_count;
> + error_factor = msg->u.error_event.error_factor;
> + break;
> + case CMD_LOG_MESSAGE:
> + channel = msg->u.log_message.channel;
> + status = msg->u.log_message.data[0];
> + txerr = msg->u.log_message.data[2];
> + rxerr = msg->u.log_message.data[3];
> + error_factor = msg->u.log_message.data[1];
> + break;
> + case CMD_CHIP_STATE_EVENT:
> + channel = msg->u.chip_state_event.channel;
> + status = msg->u.chip_state_event.status;
> + txerr = msg->u.chip_state_event.tx_errors_count;
> + rxerr = msg->u.chip_state_event.rx_errors_count;
> + error_factor = 0;
> + break;
> + default:
> + dev_err(dev->udev->dev.parent, "Invalid msg id (%d)\n",
> + msg->id);
> + return;
> + }
> +
> + if (channel >= dev->nchannels) {
> + dev_err(dev->udev->dev.parent,
> + "Invalid channel number (%d)\n", channel);
> + return;
> + }
> +
> + priv = dev->nets[channel];
> + stats = &priv->netdev->stats;
> +
> + if (status & M16C_STATE_BUS_RESET) {
> + kvaser_usb_unlink_tx_urbs(priv);
> + return;
> + }
> +
> + skb = alloc_can_err_skb(priv->netdev, &cf);
> + if (!skb) {
> + stats->rx_dropped++;
> + return;
> + }
> +
> + new_state = priv->can.state;
> +
> + netdev_dbg(priv->netdev, "Error status: 0x%02x\n", status);
> +
> + if (status & M16C_STATE_BUS_OFF) {
> + cf->can_id |= CAN_ERR_BUSOFF;
> +
> + priv->can.can_stats.bus_off++;
> + if (!priv->can.restart_ms)
> + kvaser_usb_simple_msg_async(priv, CMD_STOP_CHIP);
> +
> + netif_carrier_off(priv->netdev);
> +
> + new_state = CAN_STATE_BUS_OFF;
> + } else if (status & M16C_STATE_BUS_PASSIVE) {
> + if (priv->can.state != CAN_STATE_ERROR_PASSIVE) {
> + cf->can_id |= CAN_ERR_CRTL;
> +
> + if (txerr || rxerr)
> + cf->data[1] = (txerr > rxerr)
> + ? CAN_ERR_CRTL_TX_PASSIVE
> + : CAN_ERR_CRTL_RX_PASSIVE;
> + else
> + cf->data[1] = CAN_ERR_CRTL_TX_PASSIVE |
> + CAN_ERR_CRTL_RX_PASSIVE;
> +
> + priv->can.can_stats.error_passive++;
> + }
> +
> + new_state = CAN_STATE_ERROR_PASSIVE;
> + }
> +
> + if (status == M16C_STATE_BUS_ERROR) {
> + if ((priv->can.state < CAN_STATE_ERROR_WARNING) &&
> + ((txerr >= 96) || (rxerr >= 96))) {
> + cf->can_id |= CAN_ERR_CRTL;
> + cf->data[1] = (txerr > rxerr)
> + ? CAN_ERR_CRTL_TX_WARNING
> + : CAN_ERR_CRTL_RX_WARNING;
> +
> + priv->can.can_stats.error_warning++;
> + new_state = CAN_STATE_ERROR_WARNING;
> + } else if (priv->can.state > CAN_STATE_ERROR_ACTIVE) {
> + cf->can_id |= CAN_ERR_PROT;
> + cf->data[2] = CAN_ERR_PROT_ACTIVE;
> +
> + new_state = CAN_STATE_ERROR_ACTIVE;
> + }
> + }
> +
> + if (!status) {
> + cf->can_id |= CAN_ERR_PROT;
> + cf->data[2] = CAN_ERR_PROT_ACTIVE;
> +
> + new_state = CAN_STATE_ERROR_ACTIVE;
> + }
> +
> + if (priv->can.restart_ms &&
> + (priv->can.state >= CAN_STATE_BUS_OFF) &&
> + (new_state < CAN_STATE_BUS_OFF)) {
> + cf->can_id |= CAN_ERR_RESTARTED;
> + netif_carrier_on(priv->netdev);
> +
> + priv->can.can_stats.restarts++;
> + }
> +
> + if (error_factor) {
> + priv->can.can_stats.bus_error++;
> + stats->rx_errors++;
> +
> + cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT;
> +
> + if (error_factor & M16C_EF_ACKE)
> + cf->data[3] |= (CAN_ERR_PROT_LOC_ACK);
> + if (error_factor & M16C_EF_CRCE)
> + cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ |
> + CAN_ERR_PROT_LOC_CRC_DEL);
> + if (error_factor & M16C_EF_FORME)
> + cf->data[2] |= CAN_ERR_PROT_FORM;
> + if (error_factor & M16C_EF_STFE)
> + cf->data[2] |= CAN_ERR_PROT_STUFF;
> + if (error_factor & M16C_EF_BITE0)
> + cf->data[2] |= CAN_ERR_PROT_BIT0;
> + if (error_factor & M16C_EF_BITE1)
> + cf->data[2] |= CAN_ERR_PROT_BIT1;
> + if (error_factor & M16C_EF_TRE)
> + cf->data[2] |= CAN_ERR_PROT_TX;
> + }
> +
> + cf->data[6] = txerr;
> + cf->data[7] = rxerr;
> +
> + priv->bec.txerr = txerr;
> + priv->bec.rxerr = rxerr;
> +
> + priv->can.state = new_state;
> +
> + netif_rx(skb);
> +
> + stats->rx_packets++;
> + stats->rx_bytes += cf->can_dlc;
> +}
> +
> +static void kvaser_usb_rx_can_err(const struct kvaser_usb_net_priv *priv,
> + const struct kvaser_msg *msg)
> +{
> + struct can_frame *cf;
> + struct sk_buff *skb;
> + struct net_device_stats *stats = &priv->netdev->stats;
> +
> + if (msg->u.rx_can.flag & (MSG_FLAG_ERROR_FRAME |
> + MSG_FLAG_NERR)) {
> + netdev_err(priv->netdev, "Unknow error (flags: 0x%02x)\n",
> + msg->u.rx_can.flag);
> +
> + stats->rx_errors++;
> + return;
> + }
> +
> + if (msg->u.rx_can.flag & MSG_FLAG_OVERRUN) {
> + skb = alloc_can_err_skb(priv->netdev, &cf);
> + if (!skb) {
> + stats->rx_dropped++;
> + return;
> + }
> +
> + cf->can_id |= CAN_ERR_CRTL;
> + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
> +
> + stats->rx_over_errors++;
> + stats->rx_errors++;
> +
> + netif_rx(skb);
> +
> + stats->rx_packets++;
> + stats->rx_bytes += cf->can_dlc;
> + }
> +}
> +
> +static void kvaser_usb_rx_can_msg(const struct kvaser_usb *dev,
> + const struct kvaser_msg *msg)
> +{
> + struct kvaser_usb_net_priv *priv;
> + struct can_frame *cf;
> + struct sk_buff *skb;
> + struct net_device_stats *stats;
> + u8 channel = msg->u.rx_can.channel;
> +
> + if (channel >= dev->nchannels) {
> + dev_err(dev->udev->dev.parent,
> + "Invalid channel number (%d)\n", channel);
> + return;
> + }
> +
> + priv = dev->nets[channel];
> + stats = &priv->netdev->stats;
> +
> + if (msg->u.rx_can.flag & (MSG_FLAG_ERROR_FRAME | MSG_FLAG_NERR |
> + MSG_FLAG_OVERRUN)) {
> + kvaser_usb_rx_can_err(priv, msg);
> + return;
> + } else if (msg->u.rx_can.flag & ~MSG_FLAG_REMOTE_FRAME) {
> + netdev_warn(priv->netdev,
> + "Unhandled frame (flags: 0x%02x)",
> + msg->u.rx_can.flag);
> + return;
> + }
> +
> + skb = alloc_can_skb(priv->netdev, &cf);
> + if (!skb) {
> + stats->tx_dropped++;
> + return;
> + }
> +
> + cf->can_id = ((msg->u.rx_can.msg[0] & 0x1f) << 6) |
> + (msg->u.rx_can.msg[1] & 0x3f);
> + cf->can_dlc = get_can_dlc(msg->u.rx_can.msg[5]);
> +
> + if (msg->id == CMD_RX_EXT_MESSAGE) {
> + cf->can_id <<= 18;
> + cf->can_id |= ((msg->u.rx_can.msg[2] & 0x0f) << 14) |
> + ((msg->u.rx_can.msg[3] & 0xff) << 6) |
> + (msg->u.rx_can.msg[4] & 0x3f);
> + cf->can_id |= CAN_EFF_FLAG;
> + }
> +
> + if (msg->u.rx_can.flag & MSG_FLAG_REMOTE_FRAME)
> + cf->can_id |= CAN_RTR_FLAG;
> + else
> + memcpy(cf->data, &msg->u.rx_can.msg[6], cf->can_dlc);
> +
> + netif_rx(skb);
> +
> + stats->rx_packets++;
> + stats->rx_bytes += cf->can_dlc;
> +}
> +
> +static void kvaser_usb_start_chip_reply(const struct kvaser_usb *dev,
> + const struct kvaser_msg *msg)
> +{
> + struct kvaser_usb_net_priv *priv;
> + u8 channel = msg->u.simple.channel;
> +
> + if (channel >= dev->nchannels) {
> + dev_err(dev->udev->dev.parent,
> + "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_stop_chip_reply(const struct kvaser_usb *dev,
> + const struct kvaser_msg *msg)
> +{
> + struct kvaser_usb_net_priv *priv;
> + u8 channel = msg->u.simple.channel;
> +
> + if (channel >= dev->nchannels) {
> + dev_err(dev->udev->dev.parent,
> + "Invalid channel number (%d)\n", channel);
> + return;
> + }
> +
> + priv = dev->nets[channel];
> +
> + complete(&priv->stop_comp);
> +}
> +
> +static void kvaser_usb_handle_message(const struct kvaser_usb *dev,
> + const struct kvaser_msg *msg)
> +{
> + switch (msg->id) {
> + case CMD_START_CHIP_REPLY:
> + kvaser_usb_start_chip_reply(dev, msg);
> + break;
> +
> + case CMD_STOP_CHIP_REPLY:
> + kvaser_usb_stop_chip_reply(dev, msg);
> + break;
> +
> + case CMD_RX_STD_MESSAGE:
> + case CMD_RX_EXT_MESSAGE:
> + kvaser_usb_rx_can_msg(dev, msg);
> + break;
> +
> + case CMD_CHIP_STATE_EVENT:
> + case CMD_CAN_ERROR_EVENT:
> + kvaser_usb_rx_error(dev, msg);
> + break;
> +
> + case CMD_LOG_MESSAGE:
> + if (msg->u.log_message.flags & MSG_FLAG_ERROR_FRAME)
> + kvaser_usb_rx_error(dev, msg);
> + break;
> +
> + case CMD_TX_ACKNOWLEDGE:
> + kvaser_usb_tx_acknowledge(dev, msg);
> + break;
> +
> + default:
> + dev_warn(dev->udev->dev.parent,
> + "Unhandled message (%d)\n", msg->id);
> + break;
> + }
> +}
> +
> +static void kvaser_usb_read_bulk_callback(struct urb *urb)
> +{
> + struct kvaser_usb *dev = urb->context;
> + struct kvaser_msg *msg;
> + int pos = 0;
> + int err, i;
> +
> + switch (urb->status) {
> + case 0:
> + break;
> + case -ENOENT:
> + 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 - MSG_HEADER_LEN) {
> + msg = urb->transfer_buffer + pos;
> +
> + if (!msg->len)
> + break;
> +
> + if (pos + msg->len > urb->actual_length) {
> + dev_err(dev->udev->dev.parent, "Format error\n");
> + break;
> + }
> +
> + kvaser_usb_handle_message(dev, msg);
> +
> + pos += msg->len;
> + }
> +
> +resubmit_urb:
> + usb_fill_bulk_urb(urb, dev->udev,
> + usb_rcvbulkpipe(dev->udev,
> + dev->bulk_in->bEndpointAddress),
> + urb->transfer_buffer, 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->udev->dev.parent,
> + "Failed resubmitting read bulk urb: %d\n", err);
> + }
> +
> + return;
> +}
> +
> +static int kvaser_usb_setup_rx_urbs(struct kvaser_usb *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;
> +
> + urb = usb_alloc_urb(0, GFP_KERNEL);
> + if (!urb) {
> + dev_warn(dev->udev->dev.parent,
> + "No memory left for URBs\n");
> + 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");
> + usb_free_urb(urb);
> + err = -ENOMEM;
> + break;
> + }
> +
> + usb_fill_bulk_urb(urb, dev->udev,
> + usb_rcvbulkpipe(dev->udev,
> + dev->bulk_in->bEndpointAddress),
> + buf, 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, 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->udev->dev.parent,
> + "Cannot setup read URBs, error %d\n", err);
> + return err;
> + } else if (i < MAX_RX_URBS) {
> + dev_warn(dev->udev->dev.parent,
> + "RX performances may be slow\n");
> + }
> +
> + dev->rxinitdone = true;
> +
> + return 0;
> +}
> +
> +static int kvaser_usb_set_opt_mode(const struct kvaser_usb_net_priv *priv)
> +{
> + struct kvaser_msg msg = {
> + .id = CMD_SET_CTRL_MODE,
> + .len = MSG_HEADER_LEN +
> + sizeof(struct kvaser_msg_ctrl_mode),
> + .u.ctrl_mode.tid = 0xff,
> + .u.ctrl_mode.channel = priv->channel,
> + };
> +
> + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
> + msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_SILENT;
> + else
> + msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_NORMAL;
> +
> + return kvaser_usb_send_msg(priv->dev, &msg);
> +}
> +
> +static int kvaser_usb_start_chip(struct kvaser_usb_net_priv *priv)
> +{
> + int err;
> +
> + init_completion(&priv->start_comp);
> +
> + err = kvaser_usb_send_simple_msg(priv->dev, CMD_START_CHIP,
> + priv->channel);
> + if (err)
> + return err;
> +
> + if (!wait_for_completion_timeout(&priv->start_comp,
> + msecs_to_jiffies(START_TIMEOUT)))
> + return -ETIMEDOUT;
> +
> + 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;
> + int err;
> +
> + err = open_candev(netdev);
> + if (err)
> + return err;
> +
> + err = kvaser_usb_setup_rx_urbs(dev);
> + if (err)
> + goto error;
> +
> + err = kvaser_usb_set_opt_mode(priv);
> + if (err)
> + goto error;
> +
> + err = kvaser_usb_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_unlink_all_urbs(struct kvaser_usb *dev)
> +{
> + int i;
> +
> + 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 < MAX_NET_DEVICES; i++) {
> + struct kvaser_usb_net_priv *priv = dev->nets[i];
> +
> + if (priv)
> + kvaser_usb_unlink_tx_urbs(priv);
> + }
> +}
> +
> +static int kvaser_usb_stop_chip(struct kvaser_usb_net_priv *priv)
> +{
> + int err;
> +
> + init_completion(&priv->stop_comp);
> +
> + err = kvaser_usb_send_simple_msg(priv->dev, CMD_STOP_CHIP,
> + priv->channel);
> + if (err)
> + return err;
> +
> + if (!wait_for_completion_timeout(&priv->stop_comp,
> + msecs_to_jiffies(STOP_TIMEOUT)))
> + return -ETIMEDOUT;
> +
> + return 0;
> +}
> +
> +static int kvaser_usb_flush_queue(struct kvaser_usb_net_priv *priv)
> +{
> + struct kvaser_msg msg = {
> + .id = CMD_FLUSH_QUEUE,
> + .len = MSG_HEADER_LEN +
> + sizeof(struct kvaser_msg_flush_queue),
> + .u.flush_queue.channel = priv->channel,
> + .u.flush_queue.flags = 0x00,
> + };
> +
> + return kvaser_usb_send_msg(priv->dev, &msg);
> +}
> +
> +static int kvaser_usb_close(struct net_device *netdev)
> +{
> + struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
> + struct kvaser_usb *dev = priv->dev;
> + int err;
> +
> + netif_stop_queue(netdev);
> +
> + err = kvaser_usb_flush_queue(priv);
> + if (err)
> + netdev_warn(netdev, "Cannot flush queue, error %d\n", err);
> +
> + if (kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, priv->channel))
> + netdev_warn(netdev, "Cannot reset card, error %d\n", err);
> +
> + err = kvaser_usb_stop_chip(priv);
> + if (err)
> + netdev_warn(netdev, "Cannot stop device, error %d\n", err);
> +
> + priv->can.state = CAN_STATE_STOPPED;
> + close_candev(priv->netdev);
> +
> + return 0;
> +}
> +
> +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;
> + struct net_device_stats *stats = &netdev->stats;
> + struct can_frame *cf = (struct can_frame *)skb->data;
> + struct kvaser_usb_tx_urb_context *context = NULL;
> + struct urb *urb;
> + void *buf;
> + struct kvaser_msg *msg;
> + int i, err;
> + int ret = NETDEV_TX_OK;
> +
> + if (can_dropped_invalid_skb(netdev, skb))
> + return NETDEV_TX_OK;
> +
> + urb = usb_alloc_urb(0, GFP_ATOMIC);
> + if (!urb) {
> + netdev_err(netdev, "No memory left for URBs\n");
> + stats->tx_dropped++;
> + goto nourbmem;
> + }
> +
> + buf = kmalloc(sizeof(struct kvaser_msg), GFP_ATOMIC);
> + if (!buf) {
> + netdev_err(netdev, "No memory left for USB buffer\n");
> + stats->tx_dropped++;
> + goto nobufmem;
> + }
> +
> + msg = buf;
> + msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_tx_can);
> + msg->u.tx_can.flags = 0;
> + msg->u.tx_can.channel = priv->channel;
> +
> + if (cf->can_id & CAN_EFF_FLAG) {
> + msg->id = CMD_TX_EXT_MESSAGE;
> + msg->u.tx_can.msg[0] = (cf->can_id >> 24) & 0x1f;
> + msg->u.tx_can.msg[1] = (cf->can_id >> 18) & 0x3f;
> + msg->u.tx_can.msg[2] = (cf->can_id >> 14) & 0x0f;
> + msg->u.tx_can.msg[3] = (cf->can_id >> 6) & 0xff;
> + msg->u.tx_can.msg[4] = cf->can_id & 0x3f;
> + } else {
> + msg->id = CMD_TX_STD_MESSAGE;
> + msg->u.tx_can.msg[0] = (cf->can_id >> 6) & 0x1f;
> + msg->u.tx_can.msg[1] = cf->can_id & 0x3f;
> + }
> +
> + msg->u.tx_can.msg[5] = cf->can_dlc;
> + memcpy(&msg->u.tx_can.msg[6], cf->data, cf->can_dlc);
> +
> + if (cf->can_id & CAN_RTR_FLAG)
> + msg->u.tx_can.flags |= MSG_FLAG_REMOTE_FRAME;
> +
> + for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++) {
> + if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) {
> + context = &priv->tx_contexts[i];
> + break;
> + }
> + }
> +
> + if (!context) {
> + netdev_warn(netdev, "cannot find free context\n");
> + ret = NETDEV_TX_BUSY;
> + goto releasebuf;
> + }
> +
> + context->priv = priv;
> + context->echo_index = i;
> + context->dlc = cf->can_dlc;
> +
> + msg->u.tx_can.tid = context->echo_index;
> +
> + usb_fill_bulk_urb(urb, dev->udev,
> + usb_sndbulkpipe(dev->udev,
> + dev->bulk_out->bEndpointAddress),
> + buf, msg->len,
> + kvaser_usb_write_bulk_callback, context);
> + usb_anchor_urb(urb, &priv->tx_submitted);
> +
> + can_put_echo_skb(skb, netdev, context->echo_index);
> +
> + atomic_inc(&priv->active_tx_urbs);
> +
> + if (atomic_read(&priv->active_tx_urbs) >= MAX_TX_URBS)
> + netif_stop_queue(netdev);
> +
> + err = usb_submit_urb(urb, GFP_ATOMIC);
> + if (unlikely(err)) {
> + can_free_echo_skb(netdev, context->echo_index);
> +
> + skb = NULL; /* set to NULL to avoid double free in
> + * dev_kfree_skb(skb) */
> +
> + atomic_dec(&priv->active_tx_urbs);
> + 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;
> + }
> +
> + usb_free_urb(urb);
> +
> + return NETDEV_TX_OK;
> +
> +releasebuf:
> + kfree(buf);
> +nobufmem:
> + usb_free_urb(urb);
> +nourbmem:
> + dev_kfree_skb(skb);
> + 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,
> +};
> +
> +static struct can_bittiming_const kvaser_usb_bittiming_const = {
> + .name = "kvaser_usb",
> + .tseg1_min = KVASER_USB_TSEG1_MIN,
> + .tseg1_max = KVASER_USB_TSEG1_MAX,
> + .tseg2_min = KVASER_USB_TSEG2_MIN,
> + .tseg2_max = KVASER_USB_TSEG2_MAX,
> + .sjw_max = KVASER_USB_SJW_MAX,
> + .brp_min = KVASER_USB_BRP_MIN,
> + .brp_max = KVASER_USB_BRP_MAX,
> + .brp_inc = KVASER_USB_BRP_INC,
> +};
> +
> +static int kvaser_usb_set_bittiming(struct net_device *netdev)
> +{
> + struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
> + struct can_bittiming *bt = &priv->can.bittiming;
> + struct kvaser_usb *dev = priv->dev;
> + struct kvaser_msg msg = {
> + .id = CMD_SET_BUS_PARAMS,
> + .len = MSG_HEADER_LEN +
> + sizeof(struct kvaser_msg_busparams),
> + .u.busparams.channel = priv->channel,
> + .u.busparams.tid = 0xff,
> + .u.busparams.bitrate = cpu_to_le32(bt->bitrate),
> + .u.busparams.sjw = bt->sjw,
> + .u.busparams.tseg1 = bt->prop_seg + bt->phase_seg1,
> + .u.busparams.tseg2 = bt->phase_seg2,
> + };
> +
> + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
> + msg.u.busparams.no_samp = 3;
> + else
> + msg.u.busparams.no_samp = 1;
> +
> + return kvaser_usb_send_msg(dev, &msg);
> +}
> +
> +static int kvaser_usb_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:
> + err = kvaser_usb_simple_msg_async(priv, CMD_START_CHIP);
> + if (err)
> + return err;
> + break;
> + default:
> + return -EOPNOTSUPP;
> + }
> +
> + return 0;
> +}
> +
> +static int kvaser_usb_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 void kvaser_usb_remove_interfaces(struct kvaser_usb *dev)
> +{
> + int i;
> +
> + for (i = 0; i < dev->nchannels; i++) {
> + if (!dev->nets[i])
> + continue;
> +
> + unregister_netdev(dev->nets[i]->netdev);
> + }
> +
> + kvaser_usb_unlink_all_urbs(dev);
> +
> + for (i = 0; i < dev->nchannels; i++) {
> + if (!dev->nets[i])
> + continue;
> +
> + free_candev(dev->nets[i]->netdev);
> + }
> +}
> +
> +static int kvaser_usb_init_one(struct usb_interface *intf,
> + const struct usb_device_id *id, int channel)
> +{
> + struct kvaser_usb *dev = usb_get_intfdata(intf);
> + struct net_device *netdev;
> + struct kvaser_usb_net_priv *priv;
> + int i, err;
> +
> + netdev = alloc_candev(sizeof(*priv), MAX_TX_URBS);
> + if (!netdev) {
> + dev_err(&intf->dev, "Cannot alloc candev\n");
> + return -ENOMEM;
> + }
> +
> + priv = netdev_priv(netdev);
> +
> + init_completion(&priv->start_comp);
> + init_completion(&priv->stop_comp);
> +
> + init_usb_anchor(&priv->tx_submitted);
> + atomic_set(&priv->active_tx_urbs, 0);
> +
> + for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++)
> + priv->tx_contexts[i].echo_index = MAX_TX_URBS;
> +
> + priv->dev = dev;
> + priv->netdev = netdev;
> + priv->channel = channel;
> +
> + priv->can.state = CAN_STATE_STOPPED;
> + priv->can.clock.freq = CAN_USB_CLOCK;
> + priv->can.bittiming_const = &kvaser_usb_bittiming_const;
> + priv->can.do_set_bittiming = kvaser_usb_set_bittiming;
> + priv->can.do_set_mode = kvaser_usb_set_mode;
> + if (id->driver_info & KVASER_HAS_TXRX_ERRORS)
> + priv->can.do_get_berr_counter = kvaser_usb_get_berr_counter;
> + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES;
> + if (id->driver_info & KVASER_HAS_SILENT_MODE)
> + priv->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY;
> +
> + netdev->flags |= IFF_ECHO;
> +
> + netdev->netdev_ops = &kvaser_usb_netdev_ops;
> +
> + SET_NETDEV_DEV(netdev, &intf->dev);
> +
> + dev->nets[channel] = priv;
> +
> + err = register_candev(netdev);
> + if (err) {
> + dev_err(&intf->dev, "Failed to register can device\n");
> + free_candev(netdev);
> + dev->nets[channel] = NULL;
> + return err;
> + }
> +
> + netdev_dbg(netdev, "device registered\n");
> +
> + return 0;
> +}
> +
> +static void kvaser_usb_get_endpoints(const struct usb_interface *intf,
> + struct usb_endpoint_descriptor **in,
> + struct usb_endpoint_descriptor **out)
> +{
> + const struct usb_host_interface *iface_desc;
> + struct usb_endpoint_descriptor *endpoint;
> + int i;
> +
> + iface_desc = &intf->altsetting[0];
> +
> + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
> + endpoint = &iface_desc->endpoint[i].desc;
> +
> + if (usb_endpoint_is_bulk_in(endpoint))
> + *in = endpoint;
> +
> + if (usb_endpoint_is_bulk_out(endpoint))
> + *out = endpoint;
> + }
> +}
> +
> +static int kvaser_usb_probe(struct usb_interface *intf,
> + const struct usb_device_id *id)
> +{
> + struct kvaser_usb *dev;
> + int err = -ENOMEM;
> + int i;
> +
> + dev = devm_kzalloc(&intf->dev, sizeof(*dev), GFP_KERNEL);
> + if (!dev)
> + return -ENOMEM;
> +
> + kvaser_usb_get_endpoints(intf, &dev->bulk_in, &dev->bulk_out);
> + if (!dev->bulk_in || !dev->bulk_out) {
> + 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);
> +
> + for (i = 0; i < MAX_NET_DEVICES; i++)
> + kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, i);
> +
> + err = kvaser_usb_get_software_info(dev);
> + if (err) {
> + dev_err(&intf->dev,
> + "Cannot get software infos, error %d\n", err);
> + return err;
> + }
> +
> + err = kvaser_usb_get_card_info(dev);
> + if (err) {
> + dev_err(&intf->dev,
> + "Cannot get card infos, error %d\n", err);
> + return err;
> + }
> +
> + dev_dbg(&intf->dev, "Firmware version: %d.%d.%d\n",
> + ((dev->fw_version >> 24) & 0xff),
> + ((dev->fw_version >> 16) & 0xff),
> + (dev->fw_version & 0xffff));
> +
> + for (i = 0; i < dev->nchannels; i++) {
> + err = kvaser_usb_init_one(intf, id, 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_DESCRIPTION("CAN driver for Kvaser CAN/USB devices");
> +MODULE_LICENSE("GPL v2");
> --
> 1.7.9.5
>
--
Olivier
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH v6] can: kvaser_usb: Add support for Kvaser CAN/USB devices
2012-11-22 15:01 ` Olivier Sobrie
@ 2012-11-22 21:30 ` Greg KH
2012-11-23 8:48 ` Marc Kleine-Budde
0 siblings, 1 reply; 43+ messages in thread
From: Greg KH @ 2012-11-22 21:30 UTC (permalink / raw)
To: Olivier Sobrie
Cc: Wolfgang Grandegger, Marc Kleine-Budde, linux-can, netdev,
linux-usb, Daniel Berglund
On Thu, Nov 22, 2012 at 04:01:49PM +0100, Olivier Sobrie wrote:
> Hi linux-usb folks,
>
> Is there someone who can help me to fix the following errors?
>
> smatch warnings:
>
> + drivers/net/can/usb/kvaser_usb.c:431 kvaser_usb_send_simple_msg() error: doing
> +dma on the stack ((null))
> + drivers/net/can/usb/kvaser_usb.c:1073 kvaser_usb_set_opt_mode() error: doing
> +dma on the stack ((null))
> + drivers/net/can/usb/kvaser_usb.c:1174 kvaser_usb_flush_queue() error: doing
> +dma on the stack ((null))
> + drivers/net/can/usb/kvaser_usb.c:1384 kvaser_usb_set_bittiming() error: doing
> +dma on the stack ((null))
>
> I assume it's due to the buffer I pass to the function usb_bulk_msg()
> which is on the stack and can't be.
> Do I just have to kmalloc a buffer and give it to the usb_bulk_msg()
> function? That's what I understood by reading
> "Documentation/DMA-API-HOWTO.txt" section "What memory is DMA'able?"...
> and from commit
> http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=commitdiff;h=32ec4576c3fb37316b1d11a04b220527822f3f0d
Yes, that is all that is needed.
thanks,
greg k-h
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH v6] can: kvaser_usb: Add support for Kvaser CAN/USB devices
2012-11-22 21:30 ` Greg KH
@ 2012-11-23 8:48 ` Marc Kleine-Budde
2012-11-23 13:30 ` [PATCH] kvaser_usb: fix "dma on the stack" errors Olivier Sobrie
0 siblings, 1 reply; 43+ messages in thread
From: Marc Kleine-Budde @ 2012-11-23 8:48 UTC (permalink / raw)
To: Greg KH
Cc: Olivier Sobrie, Wolfgang Grandegger, linux-can, netdev,
linux-usb, Daniel Berglund
[-- Attachment #1: Type: text/plain, Size: 1594 bytes --]
On 11/22/2012 10:30 PM, Greg KH wrote:
> On Thu, Nov 22, 2012 at 04:01:49PM +0100, Olivier Sobrie wrote:
>> Hi linux-usb folks,
>>
>> Is there someone who can help me to fix the following errors?
>>
>> smatch warnings:
>>
>> + drivers/net/can/usb/kvaser_usb.c:431 kvaser_usb_send_simple_msg() error: doing
>> +dma on the stack ((null))
>> + drivers/net/can/usb/kvaser_usb.c:1073 kvaser_usb_set_opt_mode() error: doing
>> +dma on the stack ((null))
>> + drivers/net/can/usb/kvaser_usb.c:1174 kvaser_usb_flush_queue() error: doing
>> +dma on the stack ((null))
>> + drivers/net/can/usb/kvaser_usb.c:1384 kvaser_usb_set_bittiming() error: doing
>> +dma on the stack ((null))
>>
>> I assume it's due to the buffer I pass to the function usb_bulk_msg()
>> which is on the stack and can't be.
>> Do I just have to kmalloc a buffer and give it to the usb_bulk_msg()
>> function? That's what I understood by reading
>> "Documentation/DMA-API-HOWTO.txt" section "What memory is DMA'able?"...
>> and from commit
>> http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=commitdiff;h=32ec4576c3fb37316b1d11a04b220527822f3f0d
>
> Yes, that is all that is needed.
Thanks Greg. Olivier, you can post an incremental patch, I'll squash it
before sending the patches upstream.
regards,
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Industrial Linux Solutions | Phone: +49-231-2826-924 |
Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 261 bytes --]
^ permalink raw reply [flat|nested] 43+ messages in thread
* [PATCH] kvaser_usb: fix "dma on the stack" errors
2012-11-23 8:48 ` Marc Kleine-Budde
@ 2012-11-23 13:30 ` Olivier Sobrie
2012-11-23 13:40 ` Olivier Sobrie
0 siblings, 1 reply; 43+ messages in thread
From: Olivier Sobrie @ 2012-11-23 13:30 UTC (permalink / raw)
To: Marc Kleine-Budde, linux-can
Cc: Greg KH, Wolfgang Grandegge, netdev, linux-usb, Daniel Berglund,
Olivier Sobrie
The dma buffer given to usb_bulk_msg() must be allocated and not on
the stack.
See Documentation/DMA-API-HOWTO.txt section "What memory is DMA'able?"
Signed-off-by: Olivier Sobrie <olivier@sobrie.be>
---
Here is the incremental patch.
Thank you Greg !
Olivier
drivers/net/can/usb/kvaser_usb.c | 110 ++++++++++++++++++++++++--------------
1 file changed, 69 insertions(+), 41 deletions(-)
diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c
index 8807bf8..7ac6e82 100644
--- a/drivers/net/can/usb/kvaser_usb.c
+++ b/drivers/net/can/usb/kvaser_usb.c
@@ -421,14 +421,21 @@ end:
static int kvaser_usb_send_simple_msg(const struct kvaser_usb *dev,
u8 msg_id, int channel)
{
- struct kvaser_msg msg = {
- .len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple),
- .id = msg_id,
- .u.simple.channel = channel,
- .u.simple.tid = 0xff,
- };
-
- return kvaser_usb_send_msg(dev, &msg);
+ struct kvaser_msg *msg;
+ int rc;
+
+ msg = kmalloc(sizeof(*msg), GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple);
+ msg->u.simple.channel = channel;
+ msg->u.simple.tid = 0xff;
+
+ rc = kvaser_usb_send_msg(dev, msg);
+
+ kfree(msg);
+ return rc;
}
static int kvaser_usb_get_software_info(struct kvaser_usb *dev)
@@ -1057,20 +1064,27 @@ static int kvaser_usb_setup_rx_urbs(struct kvaser_usb *dev)
static int kvaser_usb_set_opt_mode(const struct kvaser_usb_net_priv *priv)
{
- struct kvaser_msg msg = {
- .id = CMD_SET_CTRL_MODE,
- .len = MSG_HEADER_LEN +
- sizeof(struct kvaser_msg_ctrl_mode),
- .u.ctrl_mode.tid = 0xff,
- .u.ctrl_mode.channel = priv->channel,
- };
+ struct kvaser_msg *msg;
+ int rc;
+
+ msg = kmalloc(sizeof(*msg), GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->id = CMD_SET_CTRL_MODE;
+ msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_ctrl_mode);
+ msg->u.ctrl_mode.tid = 0xff;
+ msg->u.ctrl_mode.channel = priv->channel;
if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
- msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_SILENT;
+ msg->u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_SILENT;
else
- msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_NORMAL;
+ msg->u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_NORMAL;
+
+ rc = kvaser_usb_send_msg(priv->dev, msg);
- return kvaser_usb_send_msg(priv->dev, &msg);
+ kfree(msg);
+ return rc;
}
static int kvaser_usb_start_chip(struct kvaser_usb_net_priv *priv)
@@ -1163,15 +1177,22 @@ static int kvaser_usb_stop_chip(struct kvaser_usb_net_priv *priv)
static int kvaser_usb_flush_queue(struct kvaser_usb_net_priv *priv)
{
- struct kvaser_msg msg = {
- .id = CMD_FLUSH_QUEUE,
- .len = MSG_HEADER_LEN +
- sizeof(struct kvaser_msg_flush_queue),
- .u.flush_queue.channel = priv->channel,
- .u.flush_queue.flags = 0x00,
- };
-
- return kvaser_usb_send_msg(priv->dev, &msg);
+ struct kvaser_msg *msg;
+ int rc;
+
+ msg = kmalloc(sizeof(*msg), GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->id = CMD_FLUSH_QUEUE;
+ msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_flush_queue);
+ msg->u.flush_queue.channel = priv->channel;
+ msg->u.flush_queue.flags = 0x00;
+
+ rc = kvaser_usb_send_msg(priv->dev, msg);
+
+ kfree(msg);
+ return rc;
}
static int kvaser_usb_close(struct net_device *netdev)
@@ -1364,24 +1385,31 @@ static int kvaser_usb_set_bittiming(struct net_device *netdev)
struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
struct can_bittiming *bt = &priv->can.bittiming;
struct kvaser_usb *dev = priv->dev;
- struct kvaser_msg msg = {
- .id = CMD_SET_BUS_PARAMS,
- .len = MSG_HEADER_LEN +
- sizeof(struct kvaser_msg_busparams),
- .u.busparams.channel = priv->channel,
- .u.busparams.tid = 0xff,
- .u.busparams.bitrate = cpu_to_le32(bt->bitrate),
- .u.busparams.sjw = bt->sjw,
- .u.busparams.tseg1 = bt->prop_seg + bt->phase_seg1,
- .u.busparams.tseg2 = bt->phase_seg2,
- };
+ struct kvaser_msg *msg;
+ int rc;
+
+ msg = kmalloc(sizeof(*msg), GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->id = CMD_SET_BUS_PARAMS;
+ msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_busparams);
+ msg->u.busparams.channel = priv->channel;
+ msg->u.busparams.tid = 0xff;
+ msg->u.busparams.bitrate = cpu_to_le32(bt->bitrate);
+ msg->u.busparams.sjw = bt->sjw;
+ msg->u.busparams.tseg1 = bt->prop_seg + bt->phase_seg1;
+ msg->u.busparams.tseg2 = bt->phase_seg2;
if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
- msg.u.busparams.no_samp = 3;
+ msg->u.busparams.no_samp = 3;
else
- msg.u.busparams.no_samp = 1;
+ msg->u.busparams.no_samp = 1;
+
+ rc = kvaser_usb_send_msg(dev, msg);
- return kvaser_usb_send_msg(dev, &msg);
+ kfree(msg);
+ return rc;
}
static int kvaser_usb_set_mode(struct net_device *netdev,
--
1.7.9.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* Re: [PATCH] kvaser_usb: fix "dma on the stack" errors
2012-11-23 13:30 ` [PATCH] kvaser_usb: fix "dma on the stack" errors Olivier Sobrie
@ 2012-11-23 13:40 ` Olivier Sobrie
2012-11-23 13:48 ` Marc Kleine-Budde
2012-11-23 13:54 ` [PATCH v2] " Olivier Sobrie
0 siblings, 2 replies; 43+ messages in thread
From: Olivier Sobrie @ 2012-11-23 13:40 UTC (permalink / raw)
To: Marc Kleine-Budde, linux-can
Cc: Greg KH, Wolfgang Grandegge, netdev, linux-usb, Daniel Berglund
On Fri, Nov 23, 2012 at 02:30:28PM +0100, Olivier Sobrie wrote:
> The dma buffer given to usb_bulk_msg() must be allocated and not on
> the stack.
> See Documentation/DMA-API-HOWTO.txt section "What memory is DMA'able?"
>
> Signed-off-by: Olivier Sobrie <olivier@sobrie.be>
> ---
> Here is the incremental patch.
> Thank you Greg !
>
> Olivier
>
> drivers/net/can/usb/kvaser_usb.c | 110 ++++++++++++++++++++++++--------------
> 1 file changed, 69 insertions(+), 41 deletions(-)
>
> diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c
> index 8807bf8..7ac6e82 100644
> --- a/drivers/net/can/usb/kvaser_usb.c
> +++ b/drivers/net/can/usb/kvaser_usb.c
> @@ -421,14 +421,21 @@ end:
> static int kvaser_usb_send_simple_msg(const struct kvaser_usb *dev,
> u8 msg_id, int channel)
> {
> - struct kvaser_msg msg = {
> - .len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple),
> - .id = msg_id,
> - .u.simple.channel = channel,
> - .u.simple.tid = 0xff,
> - };
> -
> - return kvaser_usb_send_msg(dev, &msg);
> + struct kvaser_msg *msg;
> + int rc;
> +
> + msg = kmalloc(sizeof(*msg), GFP_KERNEL);
> + if (!msg)
> + return -ENOMEM;
> +
Doh! I removed by mistake the line "msg->id = msg_id"... grr
> + msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple);
> + msg->u.simple.channel = channel;
> + msg->u.simple.tid = 0xff;
> +
> + rc = kvaser_usb_send_msg(dev, msg);
> +
> + kfree(msg);
> + return rc;
> }
>
> static int kvaser_usb_get_software_info(struct kvaser_usb *dev)
> @@ -1057,20 +1064,27 @@ static int kvaser_usb_setup_rx_urbs(struct kvaser_usb *dev)
>
> static int kvaser_usb_set_opt_mode(const struct kvaser_usb_net_priv *priv)
> {
> - struct kvaser_msg msg = {
> - .id = CMD_SET_CTRL_MODE,
> - .len = MSG_HEADER_LEN +
> - sizeof(struct kvaser_msg_ctrl_mode),
> - .u.ctrl_mode.tid = 0xff,
> - .u.ctrl_mode.channel = priv->channel,
> - };
> + struct kvaser_msg *msg;
> + int rc;
> +
> + msg = kmalloc(sizeof(*msg), GFP_KERNEL);
> + if (!msg)
> + return -ENOMEM;
> +
> + msg->id = CMD_SET_CTRL_MODE;
> + msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_ctrl_mode);
> + msg->u.ctrl_mode.tid = 0xff;
> + msg->u.ctrl_mode.channel = priv->channel;
>
> if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
> - msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_SILENT;
> + msg->u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_SILENT;
> else
> - msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_NORMAL;
> + msg->u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_NORMAL;
> +
> + rc = kvaser_usb_send_msg(priv->dev, msg);
>
> - return kvaser_usb_send_msg(priv->dev, &msg);
> + kfree(msg);
> + return rc;
> }
>
> static int kvaser_usb_start_chip(struct kvaser_usb_net_priv *priv)
> @@ -1163,15 +1177,22 @@ static int kvaser_usb_stop_chip(struct kvaser_usb_net_priv *priv)
>
> static int kvaser_usb_flush_queue(struct kvaser_usb_net_priv *priv)
> {
> - struct kvaser_msg msg = {
> - .id = CMD_FLUSH_QUEUE,
> - .len = MSG_HEADER_LEN +
> - sizeof(struct kvaser_msg_flush_queue),
> - .u.flush_queue.channel = priv->channel,
> - .u.flush_queue.flags = 0x00,
> - };
> -
> - return kvaser_usb_send_msg(priv->dev, &msg);
> + struct kvaser_msg *msg;
> + int rc;
> +
> + msg = kmalloc(sizeof(*msg), GFP_KERNEL);
> + if (!msg)
> + return -ENOMEM;
> +
> + msg->id = CMD_FLUSH_QUEUE;
> + msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_flush_queue);
> + msg->u.flush_queue.channel = priv->channel;
> + msg->u.flush_queue.flags = 0x00;
> +
> + rc = kvaser_usb_send_msg(priv->dev, msg);
> +
> + kfree(msg);
> + return rc;
> }
>
> static int kvaser_usb_close(struct net_device *netdev)
> @@ -1364,24 +1385,31 @@ static int kvaser_usb_set_bittiming(struct net_device *netdev)
> struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
> struct can_bittiming *bt = &priv->can.bittiming;
> struct kvaser_usb *dev = priv->dev;
> - struct kvaser_msg msg = {
> - .id = CMD_SET_BUS_PARAMS,
> - .len = MSG_HEADER_LEN +
> - sizeof(struct kvaser_msg_busparams),
> - .u.busparams.channel = priv->channel,
> - .u.busparams.tid = 0xff,
> - .u.busparams.bitrate = cpu_to_le32(bt->bitrate),
> - .u.busparams.sjw = bt->sjw,
> - .u.busparams.tseg1 = bt->prop_seg + bt->phase_seg1,
> - .u.busparams.tseg2 = bt->phase_seg2,
> - };
> + struct kvaser_msg *msg;
> + int rc;
> +
> + msg = kmalloc(sizeof(*msg), GFP_KERNEL);
> + if (!msg)
> + return -ENOMEM;
> +
> + msg->id = CMD_SET_BUS_PARAMS;
> + msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_busparams);
> + msg->u.busparams.channel = priv->channel;
> + msg->u.busparams.tid = 0xff;
> + msg->u.busparams.bitrate = cpu_to_le32(bt->bitrate);
> + msg->u.busparams.sjw = bt->sjw;
> + msg->u.busparams.tseg1 = bt->prop_seg + bt->phase_seg1;
> + msg->u.busparams.tseg2 = bt->phase_seg2;
>
> if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
> - msg.u.busparams.no_samp = 3;
> + msg->u.busparams.no_samp = 3;
> else
> - msg.u.busparams.no_samp = 1;
> + msg->u.busparams.no_samp = 1;
> +
> + rc = kvaser_usb_send_msg(dev, msg);
>
> - return kvaser_usb_send_msg(dev, &msg);
> + kfree(msg);
> + return rc;
> }
>
> static int kvaser_usb_set_mode(struct net_device *netdev,
> --
> 1.7.9.5
>
--
Olivier
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH] kvaser_usb: fix "dma on the stack" errors
2012-11-23 13:40 ` Olivier Sobrie
@ 2012-11-23 13:48 ` Marc Kleine-Budde
2012-11-23 13:54 ` [PATCH v2] " Olivier Sobrie
1 sibling, 0 replies; 43+ messages in thread
From: Marc Kleine-Budde @ 2012-11-23 13:48 UTC (permalink / raw)
To: Olivier Sobrie; +Cc: linux-can, Wolfgang Grandegge, netdev, linux-usb
[-- Attachment #1: Type: text/plain, Size: 1725 bytes --]
On 11/23/2012 02:40 PM, Olivier Sobrie wrote:
> On Fri, Nov 23, 2012 at 02:30:28PM +0100, Olivier Sobrie wrote:
>> The dma buffer given to usb_bulk_msg() must be allocated and not on
>> the stack.
>> See Documentation/DMA-API-HOWTO.txt section "What memory is DMA'able?"
>>
>> Signed-off-by: Olivier Sobrie <olivier@sobrie.be>
>> ---
>> Here is the incremental patch.
>> Thank you Greg !
>>
>> Olivier
>>
>> drivers/net/can/usb/kvaser_usb.c | 110 ++++++++++++++++++++++++--------------
>> 1 file changed, 69 insertions(+), 41 deletions(-)
>>
>> diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c
>> index 8807bf8..7ac6e82 100644
>> --- a/drivers/net/can/usb/kvaser_usb.c
>> +++ b/drivers/net/can/usb/kvaser_usb.c
>> @@ -421,14 +421,21 @@ end:
>> static int kvaser_usb_send_simple_msg(const struct kvaser_usb *dev,
>> u8 msg_id, int channel)
>> {
>> - struct kvaser_msg msg = {
>> - .len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple),
>> - .id = msg_id,
>> - .u.simple.channel = channel,
>> - .u.simple.tid = 0xff,
>> - };
>> -
>> - return kvaser_usb_send_msg(dev, &msg);
>> + struct kvaser_msg *msg;
>> + int rc;
>> +
>> + msg = kmalloc(sizeof(*msg), GFP_KERNEL);
>> + if (!msg)
>> + return -ENOMEM;
>> +
>
> Doh! I removed by mistake the line "msg->id = msg_id"... grr
Please send a v2 version of this patch with this problem fixed.
MarcMarc
--
Pengutronix e.K. | Marc Kleine-Budde |
Industrial Linux Solutions | Phone: +49-231-2826-924 |
Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 261 bytes --]
^ permalink raw reply [flat|nested] 43+ messages in thread
* [PATCH v2] kvaser_usb: fix "dma on the stack" errors
2012-11-23 13:40 ` Olivier Sobrie
2012-11-23 13:48 ` Marc Kleine-Budde
@ 2012-11-23 13:54 ` Olivier Sobrie
2012-11-23 14:08 ` Marc Kleine-Budde
1 sibling, 1 reply; 43+ messages in thread
From: Olivier Sobrie @ 2012-11-23 13:54 UTC (permalink / raw)
To: Marc Kleine-Budde, linux-can
Cc: Greg KH, Wolfgang Grandegge, netdev, linux-usb, Daniel Berglund,
Olivier Sobrie
The dma buffer given to usb_bulk_msg() must be allocated and not on
the stack.
See Documentation/DMA-API-HOWTO.txt section "What memory is DMA'able?"
Signed-off-by: Olivier Sobrie <olivier@sobrie.be>
---
drivers/net/can/usb/kvaser_usb.c | 111 ++++++++++++++++++++++++--------------
1 file changed, 70 insertions(+), 41 deletions(-)
diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c
index 8807bf8..1b159e7 100644
--- a/drivers/net/can/usb/kvaser_usb.c
+++ b/drivers/net/can/usb/kvaser_usb.c
@@ -421,14 +421,22 @@ end:
static int kvaser_usb_send_simple_msg(const struct kvaser_usb *dev,
u8 msg_id, int channel)
{
- struct kvaser_msg msg = {
- .len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple),
- .id = msg_id,
- .u.simple.channel = channel,
- .u.simple.tid = 0xff,
- };
-
- return kvaser_usb_send_msg(dev, &msg);
+ struct kvaser_msg *msg;
+ int rc;
+
+ msg = kmalloc(sizeof(*msg), GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->id = msg_id;
+ msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple);
+ msg->u.simple.channel = channel;
+ msg->u.simple.tid = 0xff;
+
+ rc = kvaser_usb_send_msg(dev, msg);
+
+ kfree(msg);
+ return rc;
}
static int kvaser_usb_get_software_info(struct kvaser_usb *dev)
@@ -1057,20 +1065,27 @@ static int kvaser_usb_setup_rx_urbs(struct kvaser_usb *dev)
static int kvaser_usb_set_opt_mode(const struct kvaser_usb_net_priv *priv)
{
- struct kvaser_msg msg = {
- .id = CMD_SET_CTRL_MODE,
- .len = MSG_HEADER_LEN +
- sizeof(struct kvaser_msg_ctrl_mode),
- .u.ctrl_mode.tid = 0xff,
- .u.ctrl_mode.channel = priv->channel,
- };
+ struct kvaser_msg *msg;
+ int rc;
+
+ msg = kmalloc(sizeof(*msg), GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->id = CMD_SET_CTRL_MODE;
+ msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_ctrl_mode);
+ msg->u.ctrl_mode.tid = 0xff;
+ msg->u.ctrl_mode.channel = priv->channel;
if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
- msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_SILENT;
+ msg->u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_SILENT;
else
- msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_NORMAL;
+ msg->u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_NORMAL;
- return kvaser_usb_send_msg(priv->dev, &msg);
+ rc = kvaser_usb_send_msg(priv->dev, msg);
+
+ kfree(msg);
+ return rc;
}
static int kvaser_usb_start_chip(struct kvaser_usb_net_priv *priv)
@@ -1163,15 +1178,22 @@ static int kvaser_usb_stop_chip(struct kvaser_usb_net_priv *priv)
static int kvaser_usb_flush_queue(struct kvaser_usb_net_priv *priv)
{
- struct kvaser_msg msg = {
- .id = CMD_FLUSH_QUEUE,
- .len = MSG_HEADER_LEN +
- sizeof(struct kvaser_msg_flush_queue),
- .u.flush_queue.channel = priv->channel,
- .u.flush_queue.flags = 0x00,
- };
-
- return kvaser_usb_send_msg(priv->dev, &msg);
+ struct kvaser_msg *msg;
+ int rc;
+
+ msg = kmalloc(sizeof(*msg), GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->id = CMD_FLUSH_QUEUE;
+ msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_flush_queue);
+ msg->u.flush_queue.channel = priv->channel;
+ msg->u.flush_queue.flags = 0x00;
+
+ rc = kvaser_usb_send_msg(priv->dev, msg);
+
+ kfree(msg);
+ return rc;
}
static int kvaser_usb_close(struct net_device *netdev)
@@ -1364,24 +1386,31 @@ static int kvaser_usb_set_bittiming(struct net_device *netdev)
struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
struct can_bittiming *bt = &priv->can.bittiming;
struct kvaser_usb *dev = priv->dev;
- struct kvaser_msg msg = {
- .id = CMD_SET_BUS_PARAMS,
- .len = MSG_HEADER_LEN +
- sizeof(struct kvaser_msg_busparams),
- .u.busparams.channel = priv->channel,
- .u.busparams.tid = 0xff,
- .u.busparams.bitrate = cpu_to_le32(bt->bitrate),
- .u.busparams.sjw = bt->sjw,
- .u.busparams.tseg1 = bt->prop_seg + bt->phase_seg1,
- .u.busparams.tseg2 = bt->phase_seg2,
- };
+ struct kvaser_msg *msg;
+ int rc;
+
+ msg = kmalloc(sizeof(*msg), GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->id = CMD_SET_BUS_PARAMS;
+ msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_busparams);
+ msg->u.busparams.channel = priv->channel;
+ msg->u.busparams.tid = 0xff;
+ msg->u.busparams.bitrate = cpu_to_le32(bt->bitrate);
+ msg->u.busparams.sjw = bt->sjw;
+ msg->u.busparams.tseg1 = bt->prop_seg + bt->phase_seg1;
+ msg->u.busparams.tseg2 = bt->phase_seg2;
if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
- msg.u.busparams.no_samp = 3;
+ msg->u.busparams.no_samp = 3;
else
- msg.u.busparams.no_samp = 1;
+ msg->u.busparams.no_samp = 1;
+
+ rc = kvaser_usb_send_msg(dev, msg);
- return kvaser_usb_send_msg(dev, &msg);
+ kfree(msg);
+ return rc;
}
static int kvaser_usb_set_mode(struct net_device *netdev,
--
1.7.9.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* Re: [PATCH v2] kvaser_usb: fix "dma on the stack" errors
2012-11-23 13:54 ` [PATCH v2] " Olivier Sobrie
@ 2012-11-23 14:08 ` Marc Kleine-Budde
2012-11-23 14:16 ` Olivier Sobrie
0 siblings, 1 reply; 43+ messages in thread
From: Marc Kleine-Budde @ 2012-11-23 14:08 UTC (permalink / raw)
To: Olivier Sobrie; +Cc: linux-can, Wolfgang Grandegge, netdev, linux-usb
[-- Attachment #1: Type: text/plain, Size: 640 bytes --]
On 11/23/2012 02:54 PM, Olivier Sobrie wrote:
> The dma buffer given to usb_bulk_msg() must be allocated and not on
> the stack.
> See Documentation/DMA-API-HOWTO.txt section "What memory is DMA'able?"
>
> Signed-off-by: Olivier Sobrie <olivier@sobrie.be>
Thanks, I've squashed it into the original patch and pushed to
linux-can-next/for-davem
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Industrial Linux Solutions | Phone: +49-231-2826-924 |
Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 261 bytes --]
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH v2] kvaser_usb: fix "dma on the stack" errors
2012-11-23 14:08 ` Marc Kleine-Budde
@ 2012-11-23 14:16 ` Olivier Sobrie
0 siblings, 0 replies; 43+ messages in thread
From: Olivier Sobrie @ 2012-11-23 14:16 UTC (permalink / raw)
To: Marc Kleine-Budde; +Cc: linux-can, Wolfgang Grandegge, netdev, linux-usb
On Fri, Nov 23, 2012 at 03:08:08PM +0100, Marc Kleine-Budde wrote:
> On 11/23/2012 02:54 PM, Olivier Sobrie wrote:
> > The dma buffer given to usb_bulk_msg() must be allocated and not on
> > the stack.
> > See Documentation/DMA-API-HOWTO.txt section "What memory is DMA'able?"
> >
> > Signed-off-by: Olivier Sobrie <olivier@sobrie.be>
>
> Thanks, I've squashed it into the original patch and pushed to
> linux-can-next/for-davem
Ok thanks.
Have a good week-end,
Olivier
>
> Marc
>
> --
> Pengutronix e.K. | Marc Kleine-Budde |
> Industrial Linux Solutions | Phone: +49-231-2826-924 |
> Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
> Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
>
--
Olivier
^ permalink raw reply [flat|nested] 43+ messages in thread