All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] can: kvaser_usb: Add support for Kvaser CAN/USB devices
@ 2012-07-30  5:32 Olivier Sobrie
  2012-07-30 11:11 ` Marc Kleine-Budde
                   ` (5 more replies)
  0 siblings, 6 replies; 43+ messages in thread
From: Olivier Sobrie @ 2012-07-30  5:32 UTC (permalink / raw)
  To: Wolfgang Grandegger, Marc Kleine-Budde, linux-can; +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>
---
 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 @@
+/*
+ * Parts of this driver are based on the following:
+ *  - Kvaser linux leaf driver (version 4.78)
+ *  - 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;
+	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;
+	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)
+{
+	int actual_len;
+
+	return usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 1),
+			    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),
+			   buf, RX_BUFFER_SIZE, &actual_len,
+			   USB_RECEIVE_TIMEOUT);
+	if (err < 0) {
+		kfree(buf);
+		return err;
+	}
+
+	while (pos < 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;
+
+	err = kvaser_usb_send_msg(dev, &msg);
+	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;
+	}
+
+	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) |
+			      ((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;
+	} 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) {
+		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),
+			  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),
+				  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;
+
+	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++)
+		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++) {
+		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;
+
+	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;
+}
+
+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++) {
+		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;
+	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;
+	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);
+		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);
+
+	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);
+	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
+#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)
+
+/* 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
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 43+ messages in thread

* 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: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 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-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

* 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

* [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] 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 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  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-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-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

* Re: [PATCH v2] can: kvaser_usb: Add support for Kvaser CAN/USB devices
       [not found]         ` <5022227F.60800-5Yr1BZd7O62+XT7JhA+gdA@public.gmane.org>
@ 2012-08-08 13:30           ` Olivier Sobrie
  2012-08-08 15:02             ` Wolfgang Grandegger
  0 siblings, 1 reply; 43+ messages in thread
From: Olivier Sobrie @ 2012-08-08 13:30 UTC (permalink / raw)
  To: Wolfgang Grandegger
  Cc: Marc Kleine-Budde, linux-can-u79uwXL29TY76Z2rM5mHXA,
	linux-usb-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA

On Wed, Aug 08, 2012 at 10:25:35AM +0200, Wolfgang Grandegger wrote:
> 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.

Ok I'll try to find it.

> 
> >>> Signed-off-by: Olivier Sobrie <olivier-Ui3EtX6WB9GzQB+pC5nmwQ@public.gmane.org>
> >>> ---
> >>> 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"?

No but that's maybe because of my misunderstanding of the
M16C_STATE_BUS_ERROR flag.
What I see is:
t1: M16C_STATE_BUS_ERROR
t2: M16C_STATE_BUS_ERROR | M16C_STATE_BUS_PASSIVE
t3: M16C_STATE_BUS_ERROR | M16C_STATE_BUS_OFF
and then again
t4: M16C_STATE_BUS_ERROR
t2: M16C_STATE_BUS_ERROR | M16C_STATE_BUS_PASSIVE
t3: M16C_STATE_BUS_ERROR | M16C_STATE_BUS_OFF

Thus as you suggested below, the flag M16C_STATE_BUS_ERROR might not mean
CAN_STATE_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}{}}
> >>
> >> 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.

candump can1,0:0,#FFFFFFFF -td -e:
 (001.369850)  can1  123  [3] 11 22 33
 (004.716034)  can1  20000008  [8] 00 00 40 00 00 00 00 00   ERRORFRAME
	protocol-violation{{back-to-error-active}{}}

Not so close... I'll add something in the tx acknowledge.

> 
> >>> 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).

 (002.465349)  can1  123  [3] 11 22 33
 (004.562670)  can1  20000004  [8] 00 30 00 00 00 00 00 00   ERRORFRAME
	controller-problem{rx-error-passive,tx-error-passive}

> 
> >>> +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:

With my hardware I do not get txerr and rxerr... I only get
M16C_STATE_BUS_ERROR. Thus in fact I can only pass from ACTIVE to PASSIVE
without reaching the WARNING state.

People of Kvaser just told me txerr and rxerr was set with other hardwares
than Kvaser Leaf Lighti... That's the one I've... I'm unlucky :-(.
I'll adapt the code so that txerr and rxerr are reported for working
hardwares..

> 
>   $ ./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

Ok. I'll only set CAN_ERR_PROT_LOC_ACK and not CAN_ERR_PROT_LOC_DEL.

> 
> 
> > 
> >>
> >>> +		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.

Ok.

> 
> >>
> >>> +	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().

Ok.

Thank you,

-- 
Olivier
--
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	[flat|nested] 43+ messages in thread

* Re: [PATCH v2] can: kvaser_usb: Add support for Kvaser CAN/USB devices
  2012-08-08 13:30           ` Olivier Sobrie
@ 2012-08-08 15:02             ` Wolfgang Grandegger
  0 siblings, 0 replies; 43+ messages in thread
From: Wolfgang Grandegger @ 2012-08-08 15:02 UTC (permalink / raw)
  To: Olivier Sobrie
  Cc: Marc Kleine-Budde, linux-can-u79uwXL29TY76Z2rM5mHXA,
	linux-usb-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA

On 08/08/2012 03:30 PM, Olivier Sobrie wrote:
> On Wed, Aug 08, 2012 at 10:25:35AM +0200, Wolfgang Grandegger wrote:
>> 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.
> 
> Ok I'll try to find it.
> 
>>
>>>>> Signed-off-by: Olivier Sobrie <olivier-Ui3EtX6WB9GzQB+pC5nmwQ@public.gmane.org>
>>>>> ---
>>>>> 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"?
> 
> No but that's maybe because of my misunderstanding of the
> M16C_STATE_BUS_ERROR flag.
> What I see is:
> t1: M16C_STATE_BUS_ERROR
> t2: M16C_STATE_BUS_ERROR | M16C_STATE_BUS_PASSIVE
> t3: M16C_STATE_BUS_ERROR | M16C_STATE_BUS_OFF
> and then again
> t4: M16C_STATE_BUS_ERROR
> t2: M16C_STATE_BUS_ERROR | M16C_STATE_BUS_PASSIVE
> t3: M16C_STATE_BUS_ERROR | M16C_STATE_BUS_OFF
> 
> Thus as you suggested below, the flag M16C_STATE_BUS_ERROR might not mean
> CAN_STATE_ERROR_WARNING...

Do you see bus error bits set? If not, I could mean "error active",
otherwise "error warning". Meaning the device sends such messages
containing bus error information plus the current state.

>>>>> 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.
> 
> candump can1,0:0,#FFFFFFFF -td -e:
>  (001.369850)  can1  123  [3] 11 22 33
>  (004.716034)  can1  20000008  [8] 00 00 40 00 00 00 00 00   ERRORFRAME
> 	protocol-violation{{back-to-error-active}{}}
> 
> Not so close... I'll add something in the tx acknowledge.

More than three seconds, wired. I would add netdev_dbg() to the state
change and error handling code to better understand what's going on.
....

>>> 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:
> 
> With my hardware I do not get txerr and rxerr... I only get
> M16C_STATE_BUS_ERROR. Thus in fact I can only pass from ACTIVE to PASSIVE
> without reaching the WARNING state.

No problem, I just wanted to illustrate how the hardware does usually do
error and state change reporting.

Wolfgang.
--
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	[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

* Re: [PATCH v4] can: kvaser_usb: Add support for Kvaser CAN/USB devices
       [not found]   ` <1348117587-2905-1-git-send-email-olivier-Ui3EtX6WB9GzQB+pC5nmwQ@public.gmane.org>
@ 2012-09-21  9:54     ` Marc Kleine-Budde
  2012-09-22 16:02       ` Wolfgang Grandegger
  0 siblings, 1 reply; 43+ messages in thread
From: Marc Kleine-Budde @ 2012-09-21  9:54 UTC (permalink / raw)
  To: Olivier Sobrie
  Cc: Wolfgang Grandegger, linux-can-u79uwXL29TY76Z2rM5mHXA,
	Kvaser Support, netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-usb-u79uwXL29TY76Z2rM5mHXA

[-- Attachment #1: Type: text/plain, Size: 48793 bytes --]

On 09/20/2012 07:06 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.

I don't remember, have the USB people already had a look on your driver
and gave some comments?

From the CAN and network point of view looks good, some comments inline.
Would be fine, if Wolfgang can give comments or Ack about the error
frame generation.

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-Ui3EtX6WB9GzQB+pC5nmwQ@public.gmane.org>
> ---
> 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-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) {

Do you have to free the usb you just allocated?

> +		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;

and here?

> +	}
> +
> +	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) {

else if ()

as bus passive and bus off is mutually exclusive.

> +		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))) {

Is is >= 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;

Please move the error skb creation handling into a subfunction, use
can_alloc_err_skb() in that function. Please move the:

    if (msg->u.rx_can.flag & ...)

up in this function, before the alloc_can_skb().

> +
> +		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++;

This should go into the error skb generation function, too.

> +	} else if (!msg->u.rx_can.flag) {
> +		memcpy(cf->data, &msg->u.rx_can.msg[6], cf->can_dlc);

Please don't copy the contents of RTR frames.

> +	} else {
> +		kfree_skb(skb);

After you have moved the error skb generation into a seperate function,
you should get rid of the kfree(skb), too. The function should look like
this (pseude code):

static void kvaser_usb_rx_can_msg(const struct kvaser_usb *dev,
				  const struct kvaser_msg *msg)
{
	if (channel_invalid())
		return;

	if (msg->u.rx_can.flag & ERROR) {
		kvaser_usb_rx_can_err_msg();
		return;
	} else if (msg->u.rx_can.flag & INVALID_FRAME) {
		return;
	}

	skb = alloc_can_skb();

	/* existing dlc, rtr and data handling code */
	...
}


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

Do you have to call usb_free_usb() here, or does the usb frame work take
care of this?

> +			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);
What about the urb?
> +		goto nobufmem;
> +	}
> +
> +	msg = (struct kvaser_msg *)buf;

nitpick: cast is not needed, as buf is void *

> +	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;

Is this still needed?

> +
> +	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-Ui3EtX6WB9GzQB+pC5nmwQ@public.gmane.org>");
> +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 v4] can: kvaser_usb: Add support for Kvaser CAN/USB devices
  2012-09-21  9:54     ` Marc Kleine-Budde
@ 2012-09-22 16:02       ` Wolfgang Grandegger
       [not found]         ` <505DE106.6060405-5Yr1BZd7O62+XT7JhA+gdA@public.gmane.org>
  0 siblings, 1 reply; 43+ messages in thread
From: Wolfgang Grandegger @ 2012-09-22 16:02 UTC (permalink / raw)
  To: Marc Kleine-Budde
  Cc: Olivier Sobrie, linux-can, Kvaser Support, netdev, linux-usb

On 09/21/2012 11:54 AM, Marc Kleine-Budde wrote:
> On 09/20/2012 07:06 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.
> 
> I don't remember, have the USB people already had a look on your driver
> and gave some comments?

IIRC, Oliver Neukum commented on v2. Actually we ignored v3 :(, sorry!

> From the CAN and network point of view looks good, some comments inline.
> Would be fine, if Wolfgang can give comments or Ack about the error
> frame generation.

Olivier already sent candump traces for no cable connected and
short-circuiting CAN high and low. The error handling looked good, IIRC,
at least as good as the firmware can do... more comments inline...

>> 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) {
> 
> Do you have to free the usb you just allocated?
> 
>> +		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;
> 
> and here?
> 
>> +	}
>> +
>> +	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;

At state change is *not* a bus error. The line above should e moved into
the "if (error_factor)" block below.

>> +
>> +	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) {
> 
> else if ()
> 
> as bus passive and bus off is mutually exclusive.
> 
>> +		if (priv->can.state != CAN_STATE_ERROR_PASSIVE) {
>> +			cf->can_id |= CAN_ERR_CRTL;
>> +
>> +			if ((txerr > 0) || (rxerr > 0))

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))) {
> 
> Is is >= 96 ?

Yep.

>> +			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;

> Please move the error skb creation handling into a subfunction, use
> can_alloc_err_skb() in that function. Please move the:
> 
>     if (msg->u.rx_can.flag & ...)
> 
> up in this function, before the alloc_can_skb().
> 
>> +
>> +		netdev_err(priv->netdev, "Unknow error (flags: 0x%02x)\n",
>> +			   msg->u.rx_can.flag);
>> +
>> +		stats->rx_errors++;

This an *error* which does normally not happen, IIRC. Therefore I do not
see a need to pass it to the user as error message.

>> +	} 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++;
> 
> This should go into the error skb generation function, too.
> 
>> +	} else if (!msg->u.rx_can.flag) {
>> +		memcpy(cf->data, &msg->u.rx_can.msg[6], cf->can_dlc);
> 
> Please don't copy the contents of RTR frames.
> 
>> +	} else {
>> +		kfree_skb(skb);
> 
> After you have moved the error skb generation into a seperate function,
> you should get rid of the kfree(skb), too. The function should look like
> this (pseude code):
> 
> static void kvaser_usb_rx_can_msg(const struct kvaser_usb *dev,
> 				  const struct kvaser_msg *msg)
> {
> 	if (channel_invalid())
> 		return;
> 
> 	if (msg->u.rx_can.flag & ERROR) {
> 		kvaser_usb_rx_can_err_msg();
> 		return;
> 	} else if (msg->u.rx_can.flag & INVALID_FRAME) {
> 		return;
> 	}
> 
> 	skb = alloc_can_skb();
> 
> 	/* existing dlc, rtr and data handling code */
> 	...
> }
> 
> 
>> +		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);
> 
> Do you have to call usb_free_usb() here, or does the usb frame work take
> care of this?
> 
>> +			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);
> What about the urb?
>> +		goto nobufmem;
>> +	}
>> +
>> +	msg = (struct kvaser_msg *)buf;
> 
> nitpick: cast is not needed, as buf is void *
> 
>> +	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;
> 
> Is this still needed?
> 
>> +
>> +	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);

Error checking is not needed?

>> +
>> +	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");

Wolfgang.


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v4] can: kvaser_usb: Add support for Kvaser CAN/USB devices
       [not found]         ` <505DE106.6060405-5Yr1BZd7O62+XT7JhA+gdA@public.gmane.org>
@ 2012-10-02  7:14           ` Olivier Sobrie
  0 siblings, 0 replies; 43+ messages in thread
From: Olivier Sobrie @ 2012-10-02  7:14 UTC (permalink / raw)
  To: Wolfgang Grandegger
  Cc: Marc Kleine-Budde, linux-can-u79uwXL29TY76Z2rM5mHXA,
	Kvaser Support, netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-usb-u79uwXL29TY76Z2rM5mHXA

Hi Marc and Wolfgang,

On Sat, Sep 22, 2012 at 06:02:14PM +0200, Wolfgang Grandegger wrote:
> On 09/21/2012 11:54 AM, Marc Kleine-Budde wrote:
> > On 09/20/2012 07:06 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.
> > 
> > I don't remember, have the USB people already had a look on your driver
> > and gave some comments?
> 
> IIRC, Oliver Neukum commented on v2. Actually we ignored v3 :(, sorry!

No problem. Thanks for the review.

> 
> > From the CAN and network point of view looks good, some comments inline.
> > Would be fine, if Wolfgang can give comments or Ack about the error
> > frame generation.
> 
> Olivier already sent candump traces for no cable connected and
> short-circuiting CAN high and low. The error handling looked good, IIRC,
> at least as good as the firmware can do... more comments inline...
> 
> >> 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-Ui3EtX6WB9GzQB+pC5nmwQ@public.gmane.org>
> >> ---
> >> 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-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) {
> > 
> > Do you have to free the usb you just allocated?

Yes it's missing.

> > 
> >> +		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;
> > 
> > and here?

missing too.

> > 
> >> +	}
> >> +
> >> +	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;
> 
> At state change is *not* a bus error. The line above should e moved into
> the "if (error_factor)" block below.

ok

> 
> >> +
> >> +	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) {
> > 
> > else if ()
> > 
> > as bus passive and bus off is mutually exclusive.

ok

> > 
> >> +		if (priv->can.state != CAN_STATE_ERROR_PASSIVE) {
> >> +			cf->can_id |= CAN_ERR_CRTL;
> >> +
> >> +			if ((txerr > 0) || (rxerr > 0))
> 
> if (txerr | rxerr) ?

ok

> 
> >> +				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))) {
> > 
> > Is is >= 96 ?
> 
> Yep.

ok

> 
> >> +			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;
> 
> > Please move the error skb creation handling into a subfunction, use
> > can_alloc_err_skb() in that function. Please move the:
> > 
> >     if (msg->u.rx_can.flag & ...)
> > 
> > up in this function, before the alloc_can_skb().
> > 
> >> +
> >> +		netdev_err(priv->netdev, "Unknow error (flags: 0x%02x)\n",
> >> +			   msg->u.rx_can.flag);
> >> +
> >> +		stats->rx_errors++;
> 
> This an *error* which does normally not happen, IIRC. Therefore I do not
> see a need to pass it to the user as error message.

ok I do not pass an error frame for such kind of message.

> 
> >> +	} 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++;
> > 
> > This should go into the error skb generation function, too.
> > 
> >> +	} else if (!msg->u.rx_can.flag) {
> >> +		memcpy(cf->data, &msg->u.rx_can.msg[6], cf->can_dlc);
> > 
> > Please don't copy the contents of RTR frames.

It's not copied. When it's a RTR frame, "msg->u.rx_can.flag" is set to
MSG_FLAG_REMOTE_FRAME.

> > 
> >> +	} else {
> >> +		kfree_skb(skb);
> > 
> > After you have moved the error skb generation into a seperate function,
> > you should get rid of the kfree(skb), too. The function should look like
> > this (pseude code):
> > 
> > static void kvaser_usb_rx_can_msg(const struct kvaser_usb *dev,
> > 				  const struct kvaser_msg *msg)
> > {
> > 	if (channel_invalid())
> > 		return;
> > 
> > 	if (msg->u.rx_can.flag & ERROR) {
> > 		kvaser_usb_rx_can_err_msg();
> > 		return;
> > 	} else if (msg->u.rx_can.flag & INVALID_FRAME) {
> > 		return;
> > 	}
> > 
> > 	skb = alloc_can_skb();
> > 
> > 	/* existing dlc, rtr and data handling code */
> > 	...
> > }

ok

> > 
> > 
> >> +		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);
> > 
> > Do you have to call usb_free_usb() here, or does the usb frame work take
> > care of this?

It's missing. Sorry.

> > 
> >> +			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);
> > What about the urb?

Same as above.

> >> +		goto nobufmem;
> >> +	}
> >> +
> >> +	msg = (struct kvaser_msg *)buf;
> > 
> > nitpick: cast is not needed, as buf is void *

Indeed.

> > 
> >> +	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;
> > 
> > Is this still needed?

No.

> > 
> >> +
> >> +	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);
> 
> Error checking is not needed?

Yes it is better to check that, I added it in the new version.
If I cannot initialize one of the "dev->nchannels" net interfaces, I
return an error in the probing function.

> 
> >> +
> >> +	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-Ui3EtX6WB9GzQB+pC5nmwQ@public.gmane.org>");
> >> +MODULE_DESCRIPTION("CAN driver for Kvaser CAN/USB devices");
> >> +MODULE_LICENSE("GPL v2");
> 
> Wolfgang.
> 

Sorry for the delay... I was quite busy these last days.
I did the changes and fixes you proposed and will post right now a new
version of the driver. I hope I didn't forget anything.

Thank you,

-- 
Olivier
--
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	[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

* [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

end of thread, other threads:[~2012-11-23 14:16 UTC | newest]

Thread overview: 43+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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-07-31  9:56     ` Marc Kleine-Budde
2012-07-31 13:06       ` Olivier Sobrie
2012-07-31 13:27         ` Marc Kleine-Budde
2012-08-02 10:53           ` Olivier Sobrie
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-02 13:20                   ` Olivier Sobrie
2012-08-05 20:43               ` Wolfgang Grandegger
2012-08-06  5:27                 ` Olivier Sobrie
2012-07-31 13:43         ` Wolfgang Grandegger
2012-08-05 20:41       ` Wolfgang Grandegger
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
2012-08-08  6:14     ` Olivier Sobrie
2012-08-08  8:25       ` Wolfgang Grandegger
     [not found]         ` <5022227F.60800-5Yr1BZd7O62+XT7JhA+gdA@public.gmane.org>
2012-08-08 13:30           ` Olivier Sobrie
2012-08-08 15:02             ` Wolfgang Grandegger
2012-08-13 13:51 ` [PATCH v3] " Olivier Sobrie
2012-09-20  5:06 ` [PATCH v4] " Olivier Sobrie
     [not found]   ` <1348117587-2905-1-git-send-email-olivier-Ui3EtX6WB9GzQB+pC5nmwQ@public.gmane.org>
2012-09-21  9:54     ` Marc Kleine-Budde
2012-09-22 16:02       ` Wolfgang Grandegger
     [not found]         ` <505DE106.6060405-5Yr1BZd7O62+XT7JhA+gdA@public.gmane.org>
2012-10-02  7:14           ` Olivier Sobrie
2012-10-02  7:16 ` [PATCH v5] " Olivier Sobrie
2012-11-07 20:29   ` Marc Kleine-Budde
2012-11-20  8:46     ` Olivier Sobrie
2012-11-20 10:59       ` Marc Kleine-Budde
     [not found] ` <1343626352-24760-1-git-send-email-olivier-Ui3EtX6WB9GzQB+pC5nmwQ@public.gmane.org>
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
2012-11-23  8:48         ` Marc Kleine-Budde
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
2012-11-23 14:08                 ` Marc Kleine-Budde
2012-11-23 14:16                   ` Olivier Sobrie

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.