All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/3 V3.1] can/usb: Add PEAK-System PCAN USB adapters driver
@ 2012-01-19 10:27 Stephane Grosjean
       [not found] ` <1326968863-20229-1-git-send-email-s.grosjean-g4cQ8AsIbFbL9ATBNaCtXw@public.gmane.org>
                   ` (2 more replies)
  0 siblings, 3 replies; 15+ messages in thread
From: Stephane Grosjean @ 2012-01-19 10:27 UTC (permalink / raw)
  To: Oliver Hartkopp
  Cc: linux-usb Mailing List, netdev Mailing List,
	linux-can Mailing List, Stephane Grosjean

That serie of patches adds the peak_usb driver which handles the PCAN-USB
and PCAN-USB Pro adapters from PEAK-System (http://www.peak-system.com).
These patches are planned for kernel v3.02.00 and above.

V3.1: same as V3 + add linux-can in cc:

Stephane Grosjean (3):
  can/usb: PEAK-System Technik USB adapters driver core
  can/usb: PEAK-System Technik PCAN-USB specific part
  can/usb: PEAK-System Technik PCAN-USB Pro specific part

 drivers/net/can/usb/Kconfig                  |    6 +
 drivers/net/can/usb/Makefile                 |    1 +
 drivers/net/can/usb/peak_usb/Makefile        |    2 +
 drivers/net/can/usb/peak_usb/pcan_usb.c      |  746 ++++++++++++++++++++
 drivers/net/can/usb/peak_usb/pcan_usb_core.c |  875 ++++++++++++++++++++++++
 drivers/net/can/usb/peak_usb/pcan_usb_core.h |  138 ++++
 drivers/net/can/usb/peak_usb/pcan_usb_pro.c  |  943 ++++++++++++++++++++++++++
 drivers/net/can/usb/peak_usb/pcan_usb_pro.h  |  178 +++++
 8 files changed, 2889 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/can/usb/peak_usb/Makefile
 create mode 100644 drivers/net/can/usb/peak_usb/pcan_usb.c
 create mode 100644 drivers/net/can/usb/peak_usb/pcan_usb_core.c
 create mode 100644 drivers/net/can/usb/peak_usb/pcan_usb_core.h
 create mode 100644 drivers/net/can/usb/peak_usb/pcan_usb_pro.c
 create mode 100644 drivers/net/can/usb/peak_usb/pcan_usb_pro.h


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

* [PATCH 1/3 V3.1] can/usb: PEAK-System Technik USB adapters driver core
       [not found] ` <1326968863-20229-1-git-send-email-s.grosjean-g4cQ8AsIbFbL9ATBNaCtXw@public.gmane.org>
@ 2012-01-19 10:27   ` Stephane Grosjean
  2012-01-19 10:27   ` [PATCH 3/3 V3.1] can/usb: PEAK-System Technik PCAN-USB Pro specific part Stephane Grosjean
  1 sibling, 0 replies; 15+ messages in thread
From: Stephane Grosjean @ 2012-01-19 10:27 UTC (permalink / raw)
  To: Oliver Hartkopp
  Cc: linux-usb Mailing List, netdev Mailing List,
	linux-can Mailing List, Stephane Grosjean

This patch adds the core of the peak_usb driver which handles PEAK-System
Technik PCAN USB adapters. It defines the parts which are common to the
PCAN-USB adapters: can network interfaces management, network-to/from-usb
data path interface, timestamps management...

V3.1: same as V3 + add linux-can in cc:

Signed-off-by: Stephane Grosjean <s.grosjean-g4cQ8AsIbFbL9ATBNaCtXw@public.gmane.org>
Acked-by: Wolfgang Grandegger <wg-5Yr1BZd7O62+XT7JhA+gdA@public.gmane.org>
---
 drivers/net/can/usb/Kconfig                  |    6 +
 drivers/net/can/usb/Makefile                 |    1 +
 drivers/net/can/usb/peak_usb/Makefile        |    2 +
 drivers/net/can/usb/peak_usb/pcan_usb_core.c |  875 ++++++++++++++++++++++++++
 drivers/net/can/usb/peak_usb/pcan_usb_core.h |  138 ++++
 5 files changed, 1022 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/can/usb/peak_usb/Makefile
 create mode 100644 drivers/net/can/usb/peak_usb/pcan_usb_core.c
 create mode 100644 drivers/net/can/usb/peak_usb/pcan_usb_core.h

diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig
index 0452549..0a68768 100644
--- a/drivers/net/can/usb/Kconfig
+++ b/drivers/net/can/usb/Kconfig
@@ -13,4 +13,10 @@ config CAN_ESD_USB2
           This driver supports the CAN-USB/2 interface
           from esd electronic system design gmbh (http://www.esd.eu).
 
+config CAN_PEAK_USB
+	tristate "PEAK PCAN-USB/USB Pro interfaces"
+	---help---
+	  This driver supports the PCAN-USB and PCAN-USB Pro adapters
+	  from PEAK-System Technik (http://www.peak-system.com).
+
 endmenu
diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile
index fce3cf1..da6d1d3 100644
--- a/drivers/net/can/usb/Makefile
+++ b/drivers/net/can/usb/Makefile
@@ -4,5 +4,6 @@
 
 obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o
 obj-$(CONFIG_CAN_ESD_USB2) += esd_usb2.o
+obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/
 
 ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
diff --git a/drivers/net/can/usb/peak_usb/Makefile b/drivers/net/can/usb/peak_usb/Makefile
new file mode 100644
index 0000000..1aefbc8
--- /dev/null
+++ b/drivers/net/can/usb/peak_usb/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_CAN_PEAK_USB) += peak_usb.o
+peak_usb-y = pcan_usb_core.o pcan_usb.o pcan_usb_pro.o
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
new file mode 100644
index 0000000..22e3b5d
--- /dev/null
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
@@ -0,0 +1,875 @@
+/*
+ * CAN driver for PEAK System USB adapters
+ * Derived from the PCAN project file driver/src/pcan_usb_core.c
+ *
+ * Copyright (C) 2003-2010 PEAK System-Technik GmbH
+ * Copyright (C) 2010-2012 Stephane Grosjean <s.grosjean-g4cQ8AsIbFbL9ATBNaCtXw@public.gmane.org>
+ *
+ * Many thanks to Klaus Hitschler <klaus.hitschler-Mmb7MZpHnFY@public.gmane.org>
+ *
+ * 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 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+#include <linux/init.h>
+#include <linux/signal.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/usb.h>
+
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+
+#include "pcan_usb_core.h"
+
+MODULE_AUTHOR("Stephane Grosjean <s.grosjean-g4cQ8AsIbFbL9ATBNaCtXw@public.gmane.org>");
+MODULE_DESCRIPTION("CAN driver for PEAK-System USB adapters");
+MODULE_LICENSE("GPL v2");
+
+/* Table of devices that work with this driver */
+static struct usb_device_id peak_usb_table[] = {
+	{USB_DEVICE(PCAN_USB_VENDOR_ID, PCAN_USB_PRODUCT_ID)},
+	{USB_DEVICE(PCAN_USB_VENDOR_ID, PCAN_USBPRO_PRODUCT_ID)},
+	{} /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, peak_usb_table);
+
+/* List of supported PCAN-USB adapters (NULL terminated list) */
+static struct peak_usb_adapter *peak_usb_adapters_list[] = {
+	&pcan_usb,
+	&pcan_usb_pro,
+	NULL,
+};
+
+/*
+ * dump memory
+ */
+#define DUMP_WIDTH	16
+void dump_mem(char *prompt, void *p, int l)
+{
+	char tmp[(3 * DUMP_WIDTH) + 1];
+	u8 *pc = p;
+	int i, ltmp;
+
+	pr_info("%s dumping %s (%d bytes):\n",
+		PCAN_USB_DRIVER_NAME, prompt ? prompt : "memory", l);
+	for (i = ltmp = 0; i < l; ) {
+		ltmp += sprintf(tmp + ltmp, "%02X ", *pc++);
+		if ((++i % DUMP_WIDTH) == 0) {
+			pr_info("%s %s\n", PCAN_USB_DRIVER_NAME, tmp);
+			ltmp = 0;
+		}
+	}
+	if (i % DUMP_WIDTH)
+		pr_info("%s %s\n", PCAN_USB_DRIVER_NAME, tmp);
+}
+
+/*
+ * initialize a time_ref object with usb adapter own settings
+ */
+void peak_usb_init_time_ref(struct peak_time_ref *time_ref,
+			    struct peak_usb_adapter *adapter)
+{
+	if (time_ref) {
+		memset(time_ref, 0, sizeof(struct peak_time_ref));
+		time_ref->adapter = adapter;
+	}
+}
+
+static void peak_usb_add_us(struct timeval *tv, u32 delta_us)
+{
+	/* number of s. to add to final time */
+	u32 delta_s = delta_us / 1000000;
+
+	delta_us -= delta_s * 1000000;
+
+	tv->tv_usec += delta_us;
+	if (tv->tv_usec >= 1000000) {
+		tv->tv_usec -= 1000000;
+		delta_s++;
+	}
+	tv->tv_sec += delta_s;
+}
+
+/*
+ * sometimes, another now may be  more recent than current one...
+ */
+void peak_usb_update_ts_now(struct peak_time_ref *time_ref, u32 ts_now)
+{
+	time_ref->ts_dev_2 = ts_now;
+
+	/* should wait at least two passes before computing */
+	if (time_ref->tv_host.tv_sec > 0) {
+		u32 delta_ts = time_ref->ts_dev_2 - time_ref->ts_dev_1;
+
+		if (time_ref->ts_dev_2 < time_ref->ts_dev_1)
+			delta_ts &= (1 << time_ref->adapter->ts_used_bits) - 1;
+
+		time_ref->ts_total += delta_ts;
+	}
+}
+
+/*
+ * register device timestamp as now
+ */
+void peak_usb_set_ts_now(struct peak_time_ref *time_ref, u32 ts_now)
+{
+	if (time_ref->tv_host_0.tv_sec == 0) {
+		do_gettimeofday(&time_ref->tv_host_0);
+		time_ref->tv_host.tv_sec = 0;
+	} else {
+		/*
+		 * delta_us should not be >= 2^32 => delta_s should be < 4294
+		 * handle 32-bits wrapping here: if count of s. reaches 4200,
+		 * reset counters and change time base
+		 */
+		if (time_ref->tv_host.tv_sec != 0) {
+			u32 delta_s = time_ref->tv_host.tv_sec
+						- time_ref->tv_host_0.tv_sec;
+			if (delta_s > 4200) {
+				time_ref->tv_host_0 = time_ref->tv_host;
+				time_ref->ts_total = 0;
+			}
+		}
+
+		do_gettimeofday(&time_ref->tv_host);
+		time_ref->tick_count++;
+	}
+
+	time_ref->ts_dev_1 = time_ref->ts_dev_2;
+	peak_usb_update_ts_now(time_ref, ts_now);
+}
+
+/*
+ * compute timeval according to current ts and time_ref data
+ */
+void peak_usb_get_ts_tv(struct peak_time_ref *time_ref, u32 ts,
+			struct timeval *tv)
+{
+	/* protect from getting timeval before setting now */
+	if (time_ref->tv_host.tv_sec > 0) {
+		u64 delta_us;
+
+		delta_us = ts - time_ref->ts_dev_2;
+		if (ts < time_ref->ts_dev_2)
+			delta_us &= (1 << time_ref->adapter->ts_used_bits) - 1;
+
+		delta_us += time_ref->ts_total;
+
+		delta_us *= time_ref->adapter->us_per_ts_scale;
+		delta_us >>= time_ref->adapter->us_per_ts_shift;
+
+		*tv = time_ref->tv_host_0;
+		peak_usb_add_us(tv, (u32)delta_us);
+	} else {
+		do_gettimeofday(tv);
+	}
+}
+
+/*
+ * callback for bulk IN urb
+ */
+static void peak_usb_read_bulk_callback(struct urb *urb)
+{
+	struct peak_usb_device *dev = urb->context;
+	struct net_device *netdev;
+	int err;
+
+	netdev = dev->netdev;
+
+	if (!netif_device_present(netdev))
+		return;
+
+	switch (urb->status) {
+	case 0: /* success */
+		break;
+	case -ENOENT:
+	case -ESHUTDOWN:
+		return;
+	default:
+		netdev_info(netdev, "Rx URB aborted (%d)\n", urb->status);
+		goto resubmit_urb;
+	}
+
+	/* protect from any incoming empty msgs */
+	if ((urb->actual_length > 0) && (dev->adapter->dev_decode_buf))
+		/* handle these kinds of msgs only is _start callback called */
+		if (dev->state & PCAN_USB_STATE_STARTED) {
+			err = dev->adapter->dev_decode_buf(dev, urb);
+			if (err)
+				dump_mem("received usb message",
+					urb->transfer_buffer,
+					urb->transfer_buffer_length);
+		}
+
+resubmit_urb:
+	usb_fill_bulk_urb(urb, dev->udev,
+		usb_rcvbulkpipe(dev->udev, dev->ep_msg_in),
+		urb->transfer_buffer, dev->adapter->rx_buffer_size,
+		peak_usb_read_bulk_callback, dev);
+
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+
+	if (err == -ENODEV)
+		netif_device_detach(netdev);
+	else if (err)
+		netdev_err(netdev, "failed resubmitting read bulk urb: %d\n",
+			err);
+}
+
+/*
+ * callback for bulk OUT urb
+ */
+static void peak_usb_write_bulk_callback(struct urb *urb)
+{
+	struct peak_tx_urb_context *context = urb->context;
+	struct peak_usb_device *dev;
+	struct net_device *netdev;
+
+	BUG_ON(!context);
+
+	dev = context->dev;
+	netdev = dev->netdev;
+
+	atomic_dec(&dev->active_tx_urbs);
+
+	if (!netif_device_present(netdev))
+		return;
+
+	if (urb->status)
+		netdev_dbg(netdev, "Tx URB aborted (%d)\n", urb->status);
+
+	netdev->trans_start = jiffies;
+
+	/* transmission complete interrupt */
+	netdev->stats.tx_packets++;
+	netdev->stats.tx_bytes += context->dlc;
+
+	can_get_echo_skb(netdev, context->echo_index);
+
+	/* Release context */
+	context->echo_index = PCAN_USB_MAX_TX_URBS;
+
+	netif_wake_queue(netdev);
+}
+
+static void peak_usb_unlink_all_urbs(struct peak_usb_device *dev)
+{
+	int i;
+
+	usb_kill_anchored_urbs(&dev->rx_submitted);
+
+	usb_kill_anchored_urbs(&dev->tx_submitted);
+	atomic_set(&dev->active_tx_urbs, 0);
+
+	for (i = 0; i < PCAN_USB_MAX_TX_URBS; i++) {
+		struct urb *urb = dev->tx_contexts[i].urb;
+
+		if (urb) {
+			if (urb->transfer_buffer) {
+				usb_free_coherent(urb->dev,
+					urb->transfer_buffer_length,
+					urb->transfer_buffer,
+					urb->transfer_dma);
+			}
+			usb_free_urb(urb);
+			dev->tx_contexts[i].urb = NULL;
+		}
+		dev->tx_contexts[i].echo_index = PCAN_USB_MAX_TX_URBS;
+	}
+}
+
+/*
+ * Start interface
+ */
+static int peak_usb_start(struct peak_usb_device *dev)
+{
+	struct net_device *netdev = dev->netdev;
+	int err, i;
+
+	for (i = 0; i < PCAN_USB_MAX_RX_URBS; i++) {
+		struct urb *urb;
+		u8 *buf;
+
+		/* create a URB, and a buffer for it, to receive usb messages */
+		urb = usb_alloc_urb(0, GFP_KERNEL);
+		if (!urb) {
+			netdev_err(netdev, "No memory left for URBs\n");
+			err = -ENOMEM;
+			break;
+		}
+
+		buf = usb_alloc_coherent(dev->udev,
+			dev->adapter->rx_buffer_size, GFP_KERNEL,
+			&urb->transfer_dma);
+		if (!buf) {
+			netdev_err(netdev, "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->ep_msg_in),
+			buf, dev->adapter->rx_buffer_size,
+			peak_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) {
+			if (err == -ENODEV)
+				netif_device_detach(dev->netdev);
+
+			usb_unanchor_urb(urb);
+			usb_free_coherent(dev->udev,
+				dev->adapter->rx_buffer_size, buf,
+				urb->transfer_dma);
+			usb_free_urb(urb);
+			break;
+		}
+
+		/* drop reference, USB core will take care of freeing it */
+		usb_free_urb(urb);
+	}
+
+	/* did we submit any URBs? Warn if we was not able to submit all urbs */
+	if (i < PCAN_USB_MAX_RX_URBS) {
+		if (i == 0) {
+			netdev_err(netdev, "couldn't setup any rx URB\n");
+			return err;
+		}
+
+		netdev_warn(netdev, "rx performance may be slow\n");
+	}
+
+	/* pre-alloc tx buffers and corresponding urbs */
+	for (i = 0; i < PCAN_USB_MAX_TX_URBS; i++) {
+		struct peak_tx_urb_context *context;
+		struct urb *urb;
+		u8 *buf;
+
+		/* create a URB and a buffer for it, to transmit usb messages */
+		urb = usb_alloc_urb(0, GFP_KERNEL);
+		if (!urb) {
+			netdev_err(netdev, "No memory left for URBs\n");
+			err = -ENOMEM;
+			break;
+		}
+
+		buf = usb_alloc_coherent(dev->udev,
+			dev->adapter->tx_buffer_size, GFP_KERNEL,
+			&urb->transfer_dma);
+		if (!buf) {
+			netdev_err(netdev, "No memory left for USB buffer\n");
+			usb_free_urb(urb);
+			err = -ENOMEM;
+			break;
+		}
+
+		context = dev->tx_contexts + i;
+		context->dev = dev;
+		context->urb = urb;
+
+		usb_fill_bulk_urb(urb, dev->udev,
+			usb_sndbulkpipe(dev->udev, dev->ep_msg_out),
+			buf, dev->adapter->tx_buffer_size,
+			peak_usb_write_bulk_callback, context);
+		urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+	}
+
+	/* warn if we were able to allocate enough tx contexts */
+	if (i < PCAN_USB_MAX_TX_URBS) {
+		if (i == 0) {
+			netdev_err(netdev, "couldn't setup any tx URB\n");
+			return err;
+		}
+
+		netdev_warn(netdev, "tx performance may be slow\n");
+	}
+
+	if (dev->adapter->dev_start) {
+		err = dev->adapter->dev_start(dev);
+		if (err)
+			goto failed;
+	}
+
+	dev->state |= PCAN_USB_STATE_STARTED;
+
+	/* can set bus on now */
+	if (dev->adapter->dev_set_bus) {
+		err = dev->adapter->dev_set_bus(dev, 1);
+		if (err)
+			goto failed;
+	}
+
+	dev->can.state = CAN_STATE_ERROR_ACTIVE;
+
+	return 0;
+
+failed:
+	if (err == -ENODEV)
+		netif_device_detach(dev->netdev);
+
+	netdev_warn(netdev, "couldn't submit control: %d\n", err);
+
+	return err;
+}
+
+static netdev_tx_t peak_usb_ndo_start_xmit(struct sk_buff *skb,
+					   struct net_device *netdev)
+{
+	struct peak_usb_device *dev = netdev_priv(netdev);
+	struct peak_tx_urb_context *context = NULL;
+	struct net_device_stats *stats = &netdev->stats;
+	struct can_frame *cf = (struct can_frame *)skb->data;
+	struct urb *urb;
+	u8 *obuf;
+	int i, err;
+	size_t size = dev->adapter->tx_buffer_size;
+
+	if (can_dropped_invalid_skb(netdev, skb))
+		return NETDEV_TX_OK;
+
+	if (!dev->adapter->dev_encode_msg) {
+		dev_kfree_skb(skb);
+		stats->tx_dropped++;
+		return NETDEV_TX_OK;
+	}
+
+	for (i = 0; i < PCAN_USB_MAX_TX_URBS; i++)
+		if (dev->tx_contexts[i].echo_index == PCAN_USB_MAX_TX_URBS) {
+			context = dev->tx_contexts + i;
+			break;
+		}
+
+	if (!context) {
+		netdev_warn(netdev, "couldn't find any free context\n");
+		return NETDEV_TX_BUSY;
+	}
+
+	urb = context->urb;
+	obuf = urb->transfer_buffer;
+	context->echo_index = i;
+	context->dlc = cf->can_dlc;
+
+	err = dev->adapter->dev_encode_msg(dev, skb, obuf, &size);
+	if (err) {
+		if (net_ratelimit())
+			netdev_err(netdev, "packet dropped\n");
+		dev_kfree_skb(skb);
+		stats->tx_dropped++;
+		return NETDEV_TX_OK;
+	}
+
+	usb_anchor_urb(urb, &dev->tx_submitted);
+
+	can_put_echo_skb(skb, netdev, context->echo_index);
+
+	atomic_inc(&dev->active_tx_urbs);
+
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err) {
+		can_free_echo_skb(netdev, context->echo_index);
+
+		usb_unanchor_urb(urb);
+		dev_kfree_skb(skb);
+
+		atomic_dec(&dev->active_tx_urbs);
+
+		if (err == -ENODEV) {
+			netif_device_detach(netdev);
+		} else {
+			netdev_warn(netdev, "tx urb submitting failed err=%d\n",
+					err);
+			stats->tx_dropped++;
+		}
+	} else {
+		netdev->trans_start = jiffies;
+
+		/* Slow down tx path */
+		if (atomic_read(&dev->active_tx_urbs) >= PCAN_USB_MAX_TX_URBS)
+			netif_stop_queue(netdev);
+	}
+
+	return NETDEV_TX_OK;
+}
+
+static int peak_usb_ndo_open(struct net_device *netdev)
+{
+	struct peak_usb_device *dev = netdev_priv(netdev);
+	int err;
+
+	/* common open */
+	err = open_candev(netdev);
+	if (err)
+		return err;
+
+	/* finally start device */
+	err = peak_usb_start(dev);
+	if (err) {
+		netdev_err(netdev, "couldn't start device: %d\n", err);
+		close_candev(netdev);
+		return err;
+	}
+
+	dev->open_time = jiffies;
+	netif_start_queue(netdev);
+
+	return 0;
+}
+
+static int peak_usb_ndo_stop(struct net_device *netdev)
+{
+	struct peak_usb_device *dev = netdev_priv(netdev);
+
+	/* Stop polling */
+	peak_usb_unlink_all_urbs(dev);
+
+	netif_stop_queue(netdev);
+
+	dev->state &= ~PCAN_USB_STATE_STARTED;
+
+	if (dev->adapter->dev_stop)
+		dev->adapter->dev_stop(dev);
+
+	close_candev(netdev);
+
+	dev->open_time = 0;
+
+	dev->can.state = CAN_STATE_STOPPED;
+
+	/* can set bus off now */
+	if (dev->adapter->dev_set_bus) {
+		int err = dev->adapter->dev_set_bus(dev, 0);
+
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+static const struct net_device_ops peak_usb_netdev_ops = {
+	.ndo_open = peak_usb_ndo_open,
+	.ndo_stop = peak_usb_ndo_stop,
+	.ndo_start_xmit = peak_usb_ndo_start_xmit,
+};
+
+static int peak_usb_set_mode(struct net_device *netdev, enum can_mode mode)
+{
+	struct peak_usb_device *dev = netdev_priv(netdev);
+
+	if (!dev->open_time)
+		return -EINVAL;
+
+	switch (mode) {
+	case CAN_MODE_START:
+		if (dev->adapter->dev_set_bus) {
+			int err = dev->adapter->dev_set_bus(dev, 1);
+
+			if (err)
+				netdev_err(netdev,
+					"couldn't start device (err %d)\n",
+					err);
+			return err;
+		}
+		netif_wake_queue(netdev);
+		break;
+
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static int peak_usb_set_bittiming(struct net_device *netdev)
+{
+	struct peak_usb_device *dev = netdev_priv(netdev);
+	struct can_bittiming *bt = &dev->can.bittiming;
+
+	if (dev->adapter->dev_set_bittiming) {
+		int err = dev->adapter->dev_set_bittiming(dev, bt);
+
+		if (err)
+			netdev_info(netdev, "couldn't set bitrate (err %d)\n",
+				err);
+		return err;
+	}
+
+	return 0;
+}
+
+/*
+ * called to create one device, atached to USB adapter's CAN controller
+ * number 'ctrl_idx'
+ */
+static int peak_usb_create_dev(struct peak_usb_adapter *peak_usb_adapter,
+			       struct usb_interface *intf, int ctrl_idx)
+{
+	struct usb_device *usb_dev = interface_to_usbdev(intf);
+	int sizeof_candev = peak_usb_adapter->sizeof_dev_private;
+	struct peak_usb_device *dev;
+	struct net_device *netdev;
+	int i, err;
+	u16 tmp16;
+
+	if (sizeof_candev < sizeof(struct peak_usb_device))
+		sizeof_candev = sizeof(struct peak_usb_device);
+
+	netdev = alloc_candev(sizeof_candev, PCAN_USB_MAX_TX_URBS);
+	if (!netdev) {
+		dev_err(&intf->dev, "%s: Couldn't alloc candev\n",
+			PCAN_USB_DRIVER_NAME);
+		return -ENOMEM;
+	}
+
+	dev = netdev_priv(netdev);
+
+	dev->udev = usb_dev;
+	dev->netdev = netdev;
+	dev->adapter = peak_usb_adapter;
+	dev->ctrl_idx = ctrl_idx;
+	dev->state = PCAN_USB_STATE_CONNECTED;
+
+	dev->ep_msg_in = peak_usb_adapter->ep_msg_in;
+	dev->ep_msg_out = peak_usb_adapter->ep_msg_out[ctrl_idx];
+
+	dev->can.clock = peak_usb_adapter->clock;
+	dev->can.bittiming_const = &peak_usb_adapter->bittiming_const;
+	dev->can.do_set_bittiming = peak_usb_set_bittiming;
+	dev->can.do_set_mode = peak_usb_set_mode;
+	dev->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
+				      CAN_CTRLMODE_LISTENONLY;
+
+	netdev->netdev_ops = &peak_usb_netdev_ops;
+
+	netdev->flags |= IFF_ECHO; /* we support local echo */
+
+	init_usb_anchor(&dev->rx_submitted);
+
+	init_usb_anchor(&dev->tx_submitted);
+	atomic_set(&dev->active_tx_urbs, 0);
+
+	for (i = 0; i < PCAN_USB_MAX_TX_URBS; i++)
+		dev->tx_contexts[i].echo_index = PCAN_USB_MAX_TX_URBS;
+
+	dev->prev_siblings = usb_get_intfdata(intf);
+	usb_set_intfdata(intf, dev);
+
+	SET_NETDEV_DEV(netdev, &intf->dev);
+
+	err = register_candev(netdev);
+	if (err) {
+		dev_err(&intf->dev,
+			"couldn't register CAN device: %d\n", err);
+		goto lbl_set_intf_data;
+	}
+
+	if (dev->prev_siblings)
+		(dev->prev_siblings)->next_siblings = dev;
+
+	/* keep hw revision into the netdevice */
+	tmp16 = le16_to_cpu(usb_dev->descriptor.bcdDevice);
+	dev->device_rev = tmp16 >> 8;
+
+	if (dev->adapter->dev_init) {
+		err = dev->adapter->dev_init(dev);
+		if (err)
+			goto lbl_set_intf_data;
+	}
+
+	/* set bus off */
+	if (dev->adapter->dev_set_bus) {
+		err = dev->adapter->dev_set_bus(dev, 0);
+		if (err)
+			goto lbl_set_intf_data;
+	}
+
+	/* get device number early */
+	if (dev->adapter->dev_get_device_id)
+		dev->adapter->dev_get_device_id(dev, &dev->device_number);
+
+	dev_info(&intf->dev, "%s attached to %s can %u (device %u)\n",
+		netdev->name, peak_usb_adapter->name, ctrl_idx,
+		dev->device_number);
+
+	return 0;
+
+lbl_set_intf_data:
+	usb_set_intfdata(intf, dev->prev_siblings);
+	free_candev(netdev);
+
+	return err;
+}
+
+/*
+ * called by the usb core when the device is removed from the system
+ */
+static void peak_usb_disconnect(struct usb_interface *intf)
+{
+	struct peak_usb_device *dev;
+
+	/* unregister as netdev devices as siblings */
+	for (dev = usb_get_intfdata(intf); dev; dev = dev->prev_siblings) {
+		struct net_device *netdev = dev->netdev;
+		char name[IFNAMSIZ];
+
+		dev->state &= ~PCAN_USB_STATE_CONNECTED;
+		strncpy(name, netdev->name, IFNAMSIZ);
+
+		unregister_netdev(netdev);
+		free_candev(netdev);
+
+		peak_usb_unlink_all_urbs(dev);
+
+		dev->next_siblings = NULL;
+		if (dev->adapter->dev_free)
+			dev->adapter->dev_free(dev);
+
+		dev_info(&intf->dev, "%s removed\n", name);
+	}
+
+	usb_set_intfdata(intf, NULL);
+}
+
+/*
+ * probe function for new PEAK-System devices
+ */
+static int peak_usb_probe(struct usb_interface *intf,
+			  const struct usb_device_id *id)
+{
+	struct usb_device *usb_dev = interface_to_usbdev(intf);
+	struct peak_usb_adapter *peak_usb_adapter, **pp;
+	int i, err = -ENOMEM;
+
+	usb_dev = interface_to_usbdev(intf);
+
+	/* get corresponding PCAN-USB adapter */
+	for (pp = peak_usb_adapters_list; *pp; pp++)
+		if ((*pp)->device_id == usb_dev->descriptor.idProduct)
+			break;
+
+	peak_usb_adapter = *pp;
+	if (!peak_usb_adapter) {
+		/* should never come except device_id bad usage in this file */
+		pr_err("%s: didn't find device id. 0x%x in devices list\n",
+			PCAN_USB_DRIVER_NAME, usb_dev->descriptor.idProduct);
+		return -ENODEV;
+	}
+
+	/* got corresponding adapter: check if it handles current interface */
+	if (peak_usb_adapter->intf_probe) {
+		err = peak_usb_adapter->intf_probe(intf);
+		if (err)
+			return err;
+	}
+
+	dev_info(&intf->dev,
+		"new PEAK-System usb adapter with %u can detected:\n",
+		peak_usb_adapter->ctrl_count);
+
+	dev_info(&intf->dev, "%s %s\n",
+		(usb_dev->manufacturer) ? usb_dev->manufacturer : "PEAK-System",
+		peak_usb_adapter->name);
+
+	if (usb_dev->product) {
+		char *pc;
+
+		/* remove some non-printable chars from the product string */
+		for (pc = usb_dev->product; *pc != 0; pc++)
+			if (*pc < 32 || *pc > 127)
+				*pc = '.';
+		dev_info(&intf->dev, "%s\n", usb_dev->product);
+	}
+
+	if (usb_dev->serial)
+		dev_info(&intf->dev, "Serial: %s\n", usb_dev->serial);
+
+	for (i = 0; i < peak_usb_adapter->ctrl_count; i++) {
+		err = peak_usb_create_dev(peak_usb_adapter, intf, i);
+		if (err) {
+			/* deregister already created devices */
+			peak_usb_disconnect(intf);
+			break;
+		}
+	}
+
+	return err;
+}
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver peak_usb_driver = {
+	.name = PCAN_USB_DRIVER_NAME,
+	.disconnect = peak_usb_disconnect,
+	.probe = peak_usb_probe,
+	.id_table = peak_usb_table,
+};
+
+static int __init peak_usb_init(void)
+{
+	int err;
+
+	pr_info("%s: PCAN-USB interfaces driver loaded\n",
+		PCAN_USB_DRIVER_NAME);
+
+	/* check whether at least ONE device is supported! */
+	if (peak_usb_table[0].idVendor != PCAN_USB_VENDOR_ID)
+		pr_warn("%s: adapters list empty (check config options)!\n",
+			PCAN_USB_DRIVER_NAME);
+
+	/* register this driver with the USB subsystem */
+	err = usb_register(&peak_usb_driver);
+	if (err)
+		pr_err("%s: usb_register failed (err %d)\n",
+			PCAN_USB_DRIVER_NAME, err);
+
+	return err;
+}
+
+static int peak_usb_do_device_exit(struct device *d, void *arg)
+{
+	struct usb_interface *intf = to_usb_interface(d);
+	struct peak_usb_device *dev;
+
+	/* stop as netdev devices as siblings */
+	for (dev = usb_get_intfdata(intf); dev; dev = dev->prev_siblings) {
+		struct net_device *netdev = dev->netdev;
+
+		if (netif_device_present(netdev))
+			if (dev->adapter->dev_exit)
+				dev->adapter->dev_exit(dev);
+	}
+
+	return 0;
+}
+
+static void __exit peak_usb_exit(void)
+{
+	int err;
+
+	/* last chance do send some synchronous commands here */
+	err = driver_for_each_device(&peak_usb_driver.drvwrap.driver, NULL,
+				     NULL, peak_usb_do_device_exit);
+	if (err)
+		pr_err("%s: failed to stop all can devices (err %d)\n",
+			PCAN_USB_DRIVER_NAME, err);
+
+	/* deregister this driver with the USB subsystem */
+	usb_deregister(&peak_usb_driver);
+
+	pr_info("%s: PCAN-USB interfaces driver unloaded\n",
+		PCAN_USB_DRIVER_NAME);
+}
+
+module_init(peak_usb_init);
+module_exit(peak_usb_exit);
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.h b/drivers/net/can/usb/peak_usb/pcan_usb_core.h
new file mode 100644
index 0000000..8adda74
--- /dev/null
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.h
@@ -0,0 +1,138 @@
+/*
+ * CAN driver for PEAK System USB adapters
+ * Derived from the PCAN project file driver/src/pcan_usb_core.c
+ *
+ * Copyright (C) 2003-2010 PEAK System-Technik GmbH
+ * Copyright (C) 2010-2012 Stephane Grosjean <s.grosjean-g4cQ8AsIbFbL9ATBNaCtXw@public.gmane.org>
+ *
+ * Many thanks to Klaus Hitschler <klaus.hitschler-Mmb7MZpHnFY@public.gmane.org>
+ *
+ * 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 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+#ifndef PCAN_USB_CORE_H
+#define PCAN_USB_CORE_H
+
+/* PEAK-System vendor id. */
+#define PCAN_USB_VENDOR_ID	0x0c72
+
+/* supported device ids. */
+#define PCAN_USB_PRODUCT_ID	0x000c
+#define PCAN_USBPRO_PRODUCT_ID	0x000d
+
+#define PCAN_USB_DRIVER_NAME	"peak_usb"
+
+/* number of urbs that are submitted for rx/tx per channel */
+#define PCAN_USB_MAX_RX_URBS	4
+#define PCAN_USB_MAX_TX_URBS	10
+
+/* usb adapters maximum channels per usb interface */
+#define PCAN_USB_MAX_CHANNEL	2
+
+struct peak_usb_device;
+
+/* PEAK-System USB adapter descriptor */
+struct peak_usb_adapter {
+	char *name;
+	u32 device_id;
+	struct can_clock clock;
+	struct can_bittiming_const bittiming_const;
+	unsigned int ctrl_count;
+
+	int (*intf_probe)(struct usb_interface *intf);
+
+	int (*dev_init)(struct peak_usb_device *dev);
+	void (*dev_exit)(struct peak_usb_device *dev);
+	void (*dev_free)(struct peak_usb_device *dev);
+	int (*dev_open)(struct peak_usb_device *dev);
+	int (*dev_close)(struct peak_usb_device *dev);
+	int (*dev_set_bittiming)(struct peak_usb_device *dev,
+					struct can_bittiming *bt);
+	int (*dev_set_bus)(struct peak_usb_device *dev, u8 onoff);
+	int (*dev_get_device_id)(struct peak_usb_device *dev, u32 *device_id);
+	int (*dev_decode_buf)(struct peak_usb_device *dev, struct urb *urb);
+	int (*dev_encode_msg)(struct peak_usb_device *dev, struct sk_buff *skb,
+					u8 *obuf, size_t *size);
+	int (*dev_start)(struct peak_usb_device *dev);
+	int (*dev_stop)(struct peak_usb_device *dev);
+
+	u8 ep_msg_in;
+	u8 ep_msg_out[PCAN_USB_MAX_CHANNEL];
+	u8 ts_used_bits;
+	u32 ts_period;
+	u8 us_per_ts_shift;
+	u32 us_per_ts_scale;
+
+	int rx_buffer_size;
+	int tx_buffer_size;
+	int sizeof_dev_private;
+};
+
+extern struct peak_usb_adapter pcan_usb;
+extern struct peak_usb_adapter pcan_usb_pro;
+
+struct peak_time_ref {
+	struct timeval tv_host_0, tv_host;
+	u32 ts_dev_1, ts_dev_2;
+	u64 ts_total;
+	u32 tick_count;
+	struct peak_usb_adapter *adapter;
+};
+
+struct peak_tx_urb_context {
+	struct peak_usb_device *dev;
+	u32 echo_index;
+	u8 dlc;
+	struct urb *urb;
+};
+
+#define PCAN_USB_STATE_CONNECTED	0x00000001
+#define PCAN_USB_STATE_STARTED		0x00000002
+
+/* PEAK-System USB device */
+struct peak_usb_device {
+	struct can_priv can;
+	struct peak_usb_adapter *adapter;
+	unsigned int ctrl_idx;
+	int open_time;
+	u32 state;
+
+	struct sk_buff *echo_skb[PCAN_USB_MAX_TX_URBS];
+
+	struct usb_device *udev;
+	struct net_device *netdev;
+
+	atomic_t active_tx_urbs;
+	struct usb_anchor tx_submitted;
+	struct peak_tx_urb_context tx_contexts[PCAN_USB_MAX_TX_URBS];
+
+	struct usb_anchor rx_submitted;
+
+	u32 device_number;
+	u8 device_rev;
+
+	u8 ep_msg_in;
+	u8 ep_msg_out;
+
+	u16 bus_load;
+
+	struct peak_usb_device *prev_siblings;
+	struct peak_usb_device *next_siblings;
+};
+
+void dump_mem(char *prompt, void *p, int l);
+
+/* common timestamp management */
+void peak_usb_init_time_ref(struct peak_time_ref *time_ref,
+			    struct peak_usb_adapter *adapter);
+void peak_usb_update_ts_now(struct peak_time_ref *time_ref, u32 ts_now);
+void peak_usb_set_ts_now(struct peak_time_ref *time_ref, u32 ts_now);
+void peak_usb_get_ts_tv(struct peak_time_ref *time_ref, u32 ts,
+			struct timeval *tv);
+#endif
-- 
1.7.1

--
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] 15+ messages in thread

* [PATCH 2/3 V3.1] can/usb: PEAK-System Technik PCAN-USB specific part
  2012-01-19 10:27 [PATCH 0/3 V3.1] can/usb: Add PEAK-System PCAN USB adapters driver Stephane Grosjean
       [not found] ` <1326968863-20229-1-git-send-email-s.grosjean-g4cQ8AsIbFbL9ATBNaCtXw@public.gmane.org>
@ 2012-01-19 10:27 ` Stephane Grosjean
  2012-01-19 15:07   ` Alan Stern
  2012-01-19 19:08 ` [PATCH 0/3 V3.1] can/usb: Add PEAK-System PCAN USB adapters driver Oliver Hartkopp
  2 siblings, 1 reply; 15+ messages in thread
From: Stephane Grosjean @ 2012-01-19 10:27 UTC (permalink / raw)
  To: Oliver Hartkopp
  Cc: linux-usb Mailing List, netdev Mailing List,
	linux-can Mailing List, Stephane Grosjean

This patch adds the specific part which handles the PCAN-USB adapter from
PEAK-System Technik (http://www.peak-system.com). The PCAN-USB adapter is
a sja1000 based, mono-channel USB 1.1 adapter compliant with CAN
specifications 2.0A (11-bit ID) and 2.0B (29-bit ID).

V3.1: same as V3 + add linux-can in cc:

Signed-off-by: Stephane Grosjean <s.grosjean@peak-system.com>
Acked-by: Wolfgang Grandegger <wg@grandegger.com>
---
 drivers/net/can/usb/peak_usb/pcan_usb.c |  746 +++++++++++++++++++++++++++++++
 1 files changed, 746 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/can/usb/peak_usb/pcan_usb.c

diff --git a/drivers/net/can/usb/peak_usb/pcan_usb.c b/drivers/net/can/usb/peak_usb/pcan_usb.c
new file mode 100644
index 0000000..12737aa
--- /dev/null
+++ b/drivers/net/can/usb/peak_usb/pcan_usb.c
@@ -0,0 +1,746 @@
+/*
+ * CAN driver for PEAK System PCAN-USB adapter
+ * Derived from the PCAN project file driver/src/pcan_usb.c
+ *
+ * Copyright (C) 2003-2010 PEAK System-Technik GmbH
+ * Copyright (C) 2011-2012 Stephane Grosjean <s.grosjean@peak-system.com>
+ *
+ * Many thanks to Klaus Hitschler <klaus.hitschler@gmx.de>
+ *
+ * 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 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+#include <linux/netdevice.h>
+#include <linux/usb.h>
+#include <linux/module.h>
+
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+
+#include "pcan_usb_core.h"
+
+MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB adapter");
+
+/* PCAN-USB Endpoints */
+#define PCAN_USB_EP_CMDOUT		1
+#define PCAN_USB_EP_CMDIN		(PCAN_USB_EP_CMDOUT | USB_DIR_IN)
+#define PCAN_USB_EP_MSGOUT		2
+#define PCAN_USB_EP_MSGIN		(PCAN_USB_EP_MSGOUT | USB_DIR_IN)
+
+/* PCAN-USB parameter command */
+#define PCAN_USB_PARAMETER_LEN		14
+struct __packed pcan_usb_parameter {
+	u8 function;
+	u8 number;
+	u8 parameters[PCAN_USB_PARAMETER_LEN];
+};
+
+/* PCAN-USB command timeout (ms.) */
+#define PCAN_USB_COMMAND_TIMEOUT	1000
+
+/* PCAN-USB rx/tx buffers size */
+#define PCAN_USB_RX_BUFFER_SIZE		64
+#define PCAN_USB_TX_BUFFER_SIZE		64
+
+#define PCAN_USB_MSG_HEADER_LEN		2
+
+/* PCAN-USB adapter internal clock (MHz) */
+#define PCAN_USB_CRYSTAL_HZ		16000000
+
+/* PCAN-USB USB message record status/len field */
+#define PCAN_USB_STATUSLEN_TIMESTAMP	(1 << 7)
+#define PCAN_USB_STATUSLEN_INTERNAL	(1 << 6)
+#define PCAN_USB_STATUSLEN_EXT_ID	(1 << 5)
+#define PCAN_USB_STATUSLEN_RTR		(1 << 4)
+#define PCAN_USB_STATUSLEN_DLC		(0xf)
+
+/* PCAN-USB error flags */
+#define PCAN_USB_ERROR_TXFULL		0x01
+#define PCAN_USB_ERROR_RXQOVR		0x02
+#define PCAN_USB_ERROR_BUS_LIGHT	0x04
+#define PCAN_USB_ERROR_BUS_HEAVY	0x08
+#define PCAN_USB_ERROR_BUS_OFF		0x10
+#define PCAN_USB_ERROR_RXQEMPTY		0x20
+#define PCAN_USB_ERROR_QOVR		0x40
+#define PCAN_USB_ERROR_TXQFULL		0x80
+
+/* SJA1000 modes */
+#define SJA1000_MODE_NORMAL		0x00
+#define SJA1000_MODE_INIT		0x01
+
+/*
+ * tick duration = 42.666 us =>
+ * (tick_number * 44739243) >> 20 ~ (tick_number * 42666) / 1000
+ * accuracy = 10^-7
+ */
+#define PCAN_USB_TS_DIV_SHIFTER		20
+#define PCAN_USB_TS_US_PER_TICK		44739243
+
+/* PCAN-USB messages record types */
+#define PCAN_USB_REC_ERROR		1
+#define PCAN_USB_REC_ANALOG		2
+#define PCAN_USB_REC_BUSLOAD		3
+#define PCAN_USB_REC_TS			4
+#define PCAN_USB_REC_BUSEVT		5
+
+/* private to PCAN-USB adapter */
+struct pcan_usb {
+	struct peak_usb_device dev;
+	struct peak_time_ref time_ref;
+};
+
+/* incoming message context for decoding */
+struct pcan_usb_msg_context {
+	u16 ts16;
+	u8 prev_ts8;
+	u8 *ptr;
+	u8 *end;
+	u8 rec_cnt;
+	u8 rec_idx;
+	u8 rec_data_idx;
+	struct net_device *netdev;
+	struct pcan_usb *pdev;
+};
+
+/*
+ * send a command
+ */
+static int pcan_usb_send_cmd(struct peak_usb_device *dev, u8 f, u8 n, u8 *p)
+{
+	int err;
+	int actual_length;
+	struct pcan_usb_parameter cmd = {
+		.function = f,
+		.number = n,
+	};
+
+	/* usb device unregistered? */
+	if (!(dev->state & PCAN_USB_STATE_CONNECTED))
+		return 0;
+
+	if (p)
+		memcpy(cmd.parameters, p, ARRAY_SIZE(cmd.parameters));
+
+	err = usb_bulk_msg(dev->udev,
+		usb_sndbulkpipe(dev->udev, PCAN_USB_EP_CMDOUT), &cmd,
+		sizeof(struct pcan_usb_parameter), &actual_length,
+		PCAN_USB_COMMAND_TIMEOUT);
+	if (err)
+		netdev_err(dev->netdev,
+			"sending cmd f=0x%x n=0x%x failure: %d\n",
+			f, n, err);
+
+	return err;
+}
+
+/*
+ * send a command then wait for its response
+ */
+static int pcan_usb_wait_rsp(struct peak_usb_device *dev, u8 f, u8 n, u8 *p)
+{
+	int err;
+	int actual_length;
+	struct pcan_usb_parameter cmd = {
+		.function = f,
+		.number = n,
+	};
+
+	/* usb device unregistered? */
+	if (!(dev->state & PCAN_USB_STATE_CONNECTED))
+		return 0;
+
+	/* first, send command */
+	err = pcan_usb_send_cmd(dev, f, n, NULL);
+	if (err)
+		return err;
+
+	err = usb_bulk_msg(dev->udev,
+		usb_rcvbulkpipe(dev->udev, PCAN_USB_EP_CMDIN), &cmd,
+		sizeof(struct pcan_usb_parameter), &actual_length,
+		PCAN_USB_COMMAND_TIMEOUT);
+	if (err)
+		netdev_err(dev->netdev,
+			"waiting rsp f=0x%x n=0x%x failure: %d\n", f, n, err);
+	else if (p)
+		memcpy(p, cmd.parameters, ARRAY_SIZE(cmd.parameters));
+
+	return err;
+}
+
+static int pcan_usb_set_sja1000(struct peak_usb_device *dev, u8 mode)
+{
+	u8 args[PCAN_USB_PARAMETER_LEN] = {
+		[1] = mode,
+	};
+
+	return pcan_usb_send_cmd(dev, 9, 2, args);
+}
+
+static int pcan_usb_set_bus(struct peak_usb_device *dev, u8 onoff)
+{
+	u8 args[PCAN_USB_PARAMETER_LEN] = {
+		[0] = onoff ? 1 : 0,
+	};
+
+	return pcan_usb_send_cmd(dev, 3, 2, args);
+}
+
+static int pcan_usb_set_silent(struct peak_usb_device *dev, u8 onoff)
+{
+	u8 args[PCAN_USB_PARAMETER_LEN] = {
+		[0] = onoff ? 1 : 0,
+	};
+
+	return pcan_usb_send_cmd(dev, 3, 3, args);
+}
+
+static int pcan_usb_set_ext_vcc(struct peak_usb_device *dev, u8 onoff)
+{
+	u8 args[PCAN_USB_PARAMETER_LEN] = {
+		[0] = onoff ? 1 : 0,
+	};
+
+	return pcan_usb_send_cmd(dev, 10, 2, args);
+}
+
+/*
+ * set bittiming value to can
+ */
+static int pcan_usb_set_bittiming(struct peak_usb_device *dev,
+				  struct can_bittiming *bt)
+{
+	u8 args[PCAN_USB_PARAMETER_LEN];
+	u8 btr0, btr1;
+
+	btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6);
+	btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) |
+		(((bt->phase_seg2 - 1) & 0x7) << 4);
+	if (dev->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
+		btr1 |= 0x80;
+
+	netdev_info(dev->netdev, "setting BTR0=0x%02x BTR1=0x%02x\n",
+		btr0, btr1);
+
+	args[0] = btr1;
+	args[1] = btr0;
+
+	return pcan_usb_send_cmd(dev, 1, 2, args);
+}
+
+/*
+ * init/reset can
+ */
+static int pcan_usb_write_mode(struct peak_usb_device *dev, u8 onoff)
+{
+	int err;
+
+	err = pcan_usb_set_bus(dev, onoff);
+	if (err)
+		return err;
+
+	if (!onoff)
+		err = pcan_usb_set_sja1000(dev, SJA1000_MODE_INIT);
+
+	return err;
+}
+
+/*
+ * read serial number from device
+ */
+static int pcan_usb_get_serial(struct peak_usb_device *dev, u32 *serial_number)
+{
+	u8 args[PCAN_USB_PARAMETER_LEN];
+	int err;
+
+	err = pcan_usb_wait_rsp(dev, 6, 1, args);
+	if (err) {
+		netdev_err(dev->netdev, "getting serial failure: %d\n", err);
+	} else if (serial_number) {
+		u32 tmp32;
+
+		memcpy(&tmp32, args, 4);
+		*serial_number = le32_to_cpu(tmp32);
+	}
+
+	return err;
+}
+
+/*
+ * read device id from device
+ */
+static int pcan_usb_get_device_id(struct peak_usb_device *dev, u32 *device_id)
+{
+	u8 args[PCAN_USB_PARAMETER_LEN];
+	int err;
+
+	err = pcan_usb_wait_rsp(dev, 4, 1, args);
+	if (err)
+		netdev_err(dev->netdev, "getting device id failure: %d\n", err);
+	else if (device_id)
+		*device_id = args[0];
+
+	return err;
+}
+
+/*
+ * update current time ref with received timestamp
+ */
+static int pcan_usb_update_ts(struct pcan_usb_msg_context *mc)
+{
+	u16 tmp16;
+
+	if ((mc->ptr+2) > mc->end)
+		return -EINVAL;
+
+	memcpy(&tmp16, mc->ptr, 2);
+
+	mc->ts16 = le16_to_cpu(tmp16);
+
+	if (mc->rec_idx > 0)
+		peak_usb_update_ts_now(&mc->pdev->time_ref, mc->ts16);
+	else
+		peak_usb_set_ts_now(&mc->pdev->time_ref, mc->ts16);
+
+	return 0;
+}
+
+/*
+ * decode received timestamp
+ */
+static int pcan_usb_decode_ts(struct pcan_usb_msg_context *mc, u8 first_packet)
+{
+	/* only 1st packet supplies a word timestamp */
+	if (first_packet) {
+		u16 tmp16;
+
+		if ((mc->ptr + 2) > mc->end)
+			return -EINVAL;
+
+		memcpy(&tmp16, mc->ptr, 2);
+		mc->ptr += 2;
+
+		mc->ts16 = le16_to_cpu(tmp16);
+		mc->prev_ts8 = mc->ts16 & 0x00ff;
+	} else {
+		u8 ts8;
+
+		if ((mc->ptr + 1) > mc->end)
+			return -EINVAL;
+
+		ts8 = *mc->ptr++;
+
+		if (ts8 < mc->prev_ts8)
+			mc->ts16 += 0x100;
+
+		mc->ts16 &= 0xff00;
+		mc->ts16 |= ts8;
+		mc->prev_ts8 = ts8;
+	}
+
+	return 0;
+}
+
+/*
+ * decode non-data usb message
+ */
+static int pcan_usb_decode_status(struct pcan_usb_msg_context *mc,
+				  u8 status_len)
+{
+	u8 rec_len = status_len & PCAN_USB_STATUSLEN_DLC;
+	struct sk_buff *skb;
+	struct can_frame *cf;
+	struct timeval tv;
+	u8 f, n;
+
+	/* check whether function and number can be read */
+	if ((mc->ptr + 2) > mc->end)
+		return -EINVAL;
+
+	f = mc->ptr[0];
+	n = mc->ptr[1];
+	mc->ptr += 2;
+
+	if (status_len & PCAN_USB_STATUSLEN_TIMESTAMP) {
+		int err = pcan_usb_decode_ts(mc, !mc->rec_idx);
+
+		if (err)
+			return err;
+	}
+
+	switch (f) {
+	case PCAN_USB_REC_ERROR:
+		/* no status flag => ignore record */
+		if (!n)
+			break;
+
+		/* ignore this error until 1st ts received */
+		if (n == PCAN_USB_ERROR_QOVR)
+			if (!mc->pdev->time_ref.tick_count)
+				break;
+
+		/* allocate an skb to store the error frame */
+		skb = alloc_can_err_skb(mc->netdev, &cf);
+		if (!skb)
+			return -ENOMEM;
+
+		if (n & (PCAN_USB_ERROR_RXQOVR | PCAN_USB_ERROR_QOVR)) {
+			cf->can_id |= CAN_ERR_CRTL;
+			cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
+			mc->netdev->stats.rx_over_errors++;
+			mc->netdev->stats.rx_errors++;
+		}
+		if (n & PCAN_USB_ERROR_BUS_OFF) {
+			cf->can_id |= CAN_ERR_BUSOFF;
+			can_bus_off(mc->netdev);
+		}
+		if (n & PCAN_USB_ERROR_BUS_HEAVY) {
+			cf->can_id |= CAN_ERR_CRTL;
+			cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE |
+					CAN_ERR_CRTL_RX_PASSIVE;
+			mc->pdev->dev.can.can_stats.error_passive++;
+		}
+		if (n & PCAN_USB_ERROR_BUS_LIGHT) {
+			cf->can_id |= CAN_ERR_CRTL;
+			cf->data[1] |= CAN_ERR_CRTL_TX_WARNING |
+					CAN_ERR_CRTL_RX_WARNING;
+			mc->pdev->dev.can.can_stats.error_warning++;
+		}
+
+		if (status_len & PCAN_USB_STATUSLEN_TIMESTAMP) {
+			peak_usb_get_ts_tv(&mc->pdev->time_ref, mc->ts16, &tv);
+			skb->tstamp = timeval_to_ktime(tv);
+		}
+		netif_rx(skb);
+		mc->netdev->stats.rx_packets++;
+		mc->netdev->stats.rx_bytes += cf->can_dlc;
+		break;
+
+	case PCAN_USB_REC_ANALOG:
+		/* analog values (ignored) */
+		rec_len = 2;
+		break;
+
+	case PCAN_USB_REC_BUSLOAD:
+		/* bus load (ignored) */
+		rec_len = 1;
+		break;
+
+	case PCAN_USB_REC_TS:
+		/* only timestamp */
+		if (pcan_usb_update_ts(mc))
+			return -EINVAL;
+		break;
+
+	case PCAN_USB_REC_BUSEVT:
+		/* error frame/bus event */
+		if (n & PCAN_USB_ERROR_TXQFULL)
+			netdev_dbg(mc->netdev, "device Tx queue full)\n");
+		break;
+	default:
+		netdev_err(mc->netdev, "unexpected function %u\n", f);
+		break;
+	}
+
+	if ((mc->ptr + rec_len) > mc->end)
+		return -EINVAL;
+
+	mc->ptr += rec_len;
+
+	return 0;
+}
+
+/*
+ * decode data usb message
+ */
+static int pcan_usb_decode_data(struct pcan_usb_msg_context *mc, u8 status_len)
+{
+	u8 rec_len = status_len & PCAN_USB_STATUSLEN_DLC;
+	struct sk_buff *skb;
+	struct can_frame *cf;
+	struct timeval tv;
+
+	skb = alloc_can_skb(mc->netdev, &cf);
+	if (!skb)
+		return -ENOMEM;
+
+	if (status_len & PCAN_USB_STATUSLEN_EXT_ID) {
+		u32 tmp32;
+
+		if ((mc->ptr + 4) > mc->end)
+			goto decode_failed;
+
+		memcpy(&tmp32, mc->ptr, 4);
+		mc->ptr += 4;
+
+		cf->can_id = le32_to_cpu(tmp32 >> 3) | CAN_EFF_FLAG;
+	} else {
+		u16 tmp16;
+
+		if ((mc->ptr + 2) > mc->end)
+			goto decode_failed;
+
+		memcpy(&tmp16, mc->ptr, 2);
+		mc->ptr += 2;
+
+		cf->can_id = le16_to_cpu(tmp16 >> 5);
+	}
+
+	cf->can_dlc = get_can_dlc(rec_len);
+
+	/* first data packet timestamp is a word */
+	if (pcan_usb_decode_ts(mc, !mc->rec_data_idx))
+		goto decode_failed;
+
+	/* read data */
+	memset(cf->data, 0x0, sizeof(cf->data));
+	if (status_len & PCAN_USB_STATUSLEN_RTR) {
+		cf->can_id |= CAN_RTR_FLAG;
+	} else {
+		if ((mc->ptr + rec_len) > mc->end)
+			goto decode_failed;
+
+		memcpy(cf->data, mc->ptr, rec_len);
+		mc->ptr += rec_len;
+	}
+
+	/* convert timestamp into kernel time */
+	peak_usb_get_ts_tv(&mc->pdev->time_ref, mc->ts16, &tv);
+	skb->tstamp = timeval_to_ktime(tv);
+
+	/* push the skb */
+	netif_rx(skb);
+
+	/* update statistics */
+	mc->netdev->stats.rx_packets++;
+	mc->netdev->stats.rx_bytes += cf->can_dlc;
+
+	return 0;
+
+decode_failed:
+	dev_kfree_skb(skb);
+	return -EINVAL;
+}
+
+/*
+ * process incoming message
+ */
+static int pcan_usb_decode_msg(struct peak_usb_device *dev, u8 *ibuf, u32 lbuf)
+{
+	struct pcan_usb_msg_context mc = {
+		.rec_cnt = ibuf[1],
+		.ptr = ibuf + PCAN_USB_MSG_HEADER_LEN,
+		.end = ibuf + lbuf,
+		.netdev = dev->netdev,
+		.pdev = container_of(dev, struct pcan_usb, dev),
+	};
+	int err;
+
+	for (err = 0; mc.rec_idx < mc.rec_cnt && !err; mc.rec_idx++) {
+		u8 sl = *mc.ptr++;
+
+		/* handle status and error frames here */
+		if (sl & PCAN_USB_STATUSLEN_INTERNAL) {
+			err = pcan_usb_decode_status(&mc, sl);
+		/* handle normal can frames here */
+		} else {
+			err = pcan_usb_decode_data(&mc, sl);
+			mc.rec_data_idx++;
+		}
+	}
+
+	return err;
+}
+
+/*
+ * process any incoming buffer
+ */
+static int pcan_usb_decode_buf(struct peak_usb_device *dev, struct urb *urb)
+{
+	int err = 0;
+
+	if (urb->actual_length > PCAN_USB_MSG_HEADER_LEN) {
+		err = pcan_usb_decode_msg(dev, urb->transfer_buffer,
+			urb->actual_length);
+
+	} else if (urb->actual_length > 0) {
+		netdev_err(dev->netdev, "usb message length error (%u)\n",
+			urb->actual_length);
+		err = -EINVAL;
+	}
+
+	return err;
+}
+
+/*
+ * process outgoing packet
+ */
+static int pcan_usb_encode_msg(struct peak_usb_device *dev, struct sk_buff *skb,
+			       u8 *obuf, size_t *size)
+{
+	struct net_device *netdev = dev->netdev;
+	struct net_device_stats *stats = &netdev->stats;
+	struct can_frame *cf = (struct can_frame *)skb->data;
+	u8 *pc;
+
+	obuf[0] = 2;
+	obuf[1] = 1;
+
+	pc = obuf + PCAN_USB_MSG_HEADER_LEN;
+
+	/* status/len byte */
+	*pc = cf->can_dlc;
+	if (cf->can_id & CAN_RTR_FLAG)
+		*pc |= PCAN_USB_STATUSLEN_RTR;
+
+	/* can id */
+	if (cf->can_id & CAN_EFF_FLAG) {
+		__le32 tmp32 = cpu_to_le32(cf->can_id & CAN_ERR_MASK);
+
+		tmp32 <<= 3;
+		*pc |= PCAN_USB_STATUSLEN_EXT_ID;
+		memcpy(++pc, &tmp32, 4);
+		pc += 4;
+	} else {
+		__le16 tmp16 = cpu_to_le32(cf->can_id & CAN_ERR_MASK);
+
+		tmp16 <<= 5;
+		memcpy(++pc, &tmp16, 2);
+		pc += 2;
+	}
+
+	/* can data */
+	if (!(cf->can_id & CAN_RTR_FLAG)) {
+		memcpy(pc, cf->data, cf->can_dlc);
+		pc += cf->can_dlc;
+	}
+
+	obuf[(*size)-1] = (u8)(stats->tx_packets & 0xff);
+
+	return 0;
+}
+
+/*
+ * start interface
+ */
+static int pcan_usb_start(struct peak_usb_device *dev)
+{
+	struct pcan_usb *pdev = container_of(dev, struct pcan_usb, dev);
+
+	/* number of bits used in timestamps read from adapter struct */
+	peak_usb_init_time_ref(&pdev->time_ref, &pcan_usb);
+
+	/* if revision greater than 3, can put silent mode on/off */
+	if (dev->device_rev > 3) {
+		int err;
+
+		err = pcan_usb_set_silent(dev,
+				dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY);
+		if (err)
+			return err;
+	}
+
+	return pcan_usb_set_ext_vcc(dev, 0);
+}
+
+static int pcan_usb_init(struct peak_usb_device *dev)
+{
+	u32 serial_number;
+	int err;
+
+	/*
+	 * explicit use of dev_info() instead of netdev_info() here:
+	 * information displayed are related to the device itself, not
+	 * to the canx netdevice.
+	 */
+	err = pcan_usb_get_serial(dev, &serial_number);
+	if (!err)
+		dev_info(dev->netdev->dev.parent, "serial %08X hwrev %u\n",
+			serial_number, dev->device_rev);
+
+	return err;
+}
+
+/*
+ * probe function for new PCAN-USB usb interface
+ */
+static int pcan_usb_probe(struct usb_interface *intf)
+{
+	struct usb_host_interface *if_desc;
+	int i;
+
+	if_desc = intf->altsetting;
+
+	/* check interface endpoint addresses */
+	for (i = 0; i < if_desc->desc.bNumEndpoints; i++) {
+		struct usb_endpoint_descriptor *ep = &if_desc->endpoint[i].desc;
+
+		switch (ep->bEndpointAddress) {
+		case PCAN_USB_EP_CMDOUT:
+		case PCAN_USB_EP_CMDIN:
+		case PCAN_USB_EP_MSGOUT:
+		case PCAN_USB_EP_MSGIN:
+			break;
+		default:
+			return -ENODEV;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * describe the PCAN-USB adapter
+ */
+struct peak_usb_adapter pcan_usb = {
+	.name = "PCAN-USB",
+	.device_id = PCAN_USB_PRODUCT_ID,
+	.ctrl_count = 1,
+	.clock = {
+		.freq = PCAN_USB_CRYSTAL_HZ / 2 ,
+	},
+	.bittiming_const = {
+		.name = "pcan_usb",
+		.tseg1_min = 1,
+		.tseg1_max = 16,
+		.tseg2_min = 1,
+		.tseg2_max = 8,
+		.sjw_max = 4,
+		.brp_min = 1,
+		.brp_max = 64,
+		.brp_inc = 1,
+	},
+
+	/* size of device private data */
+	.sizeof_dev_private = sizeof(struct pcan_usb),
+
+	/* timestamps usage */
+	.ts_used_bits = 16,
+	.ts_period = 24575, /* calibration period in ts. */
+	.us_per_ts_scale = PCAN_USB_TS_US_PER_TICK, /* us=(ts*scale) */
+	.us_per_ts_shift = PCAN_USB_TS_DIV_SHIFTER, /*  >> shift     */
+
+	/* give here commands/messages in/out endpoints */
+	.ep_msg_in = PCAN_USB_EP_MSGIN,
+	.ep_msg_out = {PCAN_USB_EP_MSGOUT},
+
+	/* size of rx/tx usb buffers */
+	.rx_buffer_size = PCAN_USB_RX_BUFFER_SIZE,
+	.tx_buffer_size = PCAN_USB_TX_BUFFER_SIZE,
+
+	/* device callbacks */
+	.intf_probe = pcan_usb_probe,
+	.dev_init = pcan_usb_init,
+	.dev_set_bus = pcan_usb_write_mode,
+	.dev_set_bittiming = pcan_usb_set_bittiming,
+	.dev_get_device_id = pcan_usb_get_device_id,
+	.dev_decode_buf = pcan_usb_decode_buf,
+	.dev_encode_msg = pcan_usb_encode_msg,
+	.dev_start = pcan_usb_start,
+};
-- 
1.7.1


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

* [PATCH 3/3 V3.1] can/usb: PEAK-System Technik PCAN-USB Pro specific part
       [not found] ` <1326968863-20229-1-git-send-email-s.grosjean-g4cQ8AsIbFbL9ATBNaCtXw@public.gmane.org>
  2012-01-19 10:27   ` [PATCH 1/3 V3.1] can/usb: PEAK-System Technik USB adapters driver core Stephane Grosjean
@ 2012-01-19 10:27   ` Stephane Grosjean
  1 sibling, 0 replies; 15+ messages in thread
From: Stephane Grosjean @ 2012-01-19 10:27 UTC (permalink / raw)
  To: Oliver Hartkopp
  Cc: linux-usb Mailing List, netdev Mailing List,
	linux-can Mailing List, Stephane Grosjean

This patch adds the specific part which handles the PCAN-USB Pro adapter
from PEAK-System Technik (http://www.peak-system.com). The PCAN-USB Pro
adapter is a dual-channel USB 2.0 adapter compliant with CAN specifications
 2.0A (11-bit ID) and 2.0B (29-bit ID).

V3.1: same as V3 + add linux-can in cc:

Signed-off-by: Stephane Grosjean <s.grosjean-g4cQ8AsIbFbL9ATBNaCtXw@public.gmane.org>
Acked-by: Wolfgang Grandegger <wg-5Yr1BZd7O62+XT7JhA+gdA@public.gmane.org>
---
 drivers/net/can/usb/peak_usb/pcan_usb_pro.c |  943 +++++++++++++++++++++++++++
 drivers/net/can/usb/peak_usb/pcan_usb_pro.h |  178 +++++
 2 files changed, 1121 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/can/usb/peak_usb/pcan_usb_pro.c
 create mode 100644 drivers/net/can/usb/peak_usb/pcan_usb_pro.h

diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
new file mode 100644
index 0000000..c93963e
--- /dev/null
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
@@ -0,0 +1,943 @@
+/*
+ * CAN driver for PEAK System PCAN-USB Pro adapter
+ * Derived from the PCAN project file driver/src/pcan_usbpro.c
+ *
+ * Copyright (C) 2003-2011 PEAK System-Technik GmbH
+ * Copyright (C) 2011-2012 Stephane Grosjean <s.grosjean-g4cQ8AsIbFbL9ATBNaCtXw@public.gmane.org>
+ *
+ * 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 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+#include <linux/netdevice.h>
+#include <linux/usb.h>
+#include <linux/module.h>
+
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+
+#include "pcan_usb_core.h"
+#include "pcan_usb_pro.h"
+
+MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB Pro adapter");
+
+/* PCAN-USB Pro Endpoints */
+#define PCAN_USBPRO_EP_CMDOUT		1
+#define PCAN_USBPRO_EP_CMDIN		(PCAN_USBPRO_EP_CMDOUT | USB_DIR_IN)
+#define PCAN_USBPRO_EP_MSGOUT_0		2
+#define PCAN_USBPRO_EP_MSGIN		(PCAN_USBPRO_EP_MSGOUT_0 | USB_DIR_IN)
+#define PCAN_USBPRO_EP_MSGOUT_1		3
+#define PCAN_USBPRO_EP_UNUSED		(PCAN_USBPRO_EP_MSGOUT_1 | USB_DIR_IN)
+
+#define PCAN_USBPRO_CHANNEL_COUNT	2
+
+/* PCAN-USB Pro adapter internal clock (MHz) */
+#define PCAN_USBPRO_CRYSTAL_HZ		56000000
+
+/* PCAN-USB Pro command timeout (ms.) */
+#define PCAN_USBPRO_COMMAND_TIMEOUT	1000
+
+/* PCAN-USB Pro rx/tx buffers size */
+#define PCAN_USBPRO_RX_BUFFER_SIZE	1024
+#define PCAN_USBPRO_TX_BUFFER_SIZE	64
+
+#define PCAN_USBPRO_MSG_HEADER_LEN	4
+
+/* some commands responses need to be re-submitted */
+#define PCAN_USBPRO_RSP_SUBMIT_MAX	2
+
+#define PCAN_USBPRO_RTR			0x01
+#define PCAN_USBPRO_EXT			0x02
+
+#define PCAN_USBPRO_CMD_BUFFER_SIZE	512
+
+/* handle device specific info used by the netdevices */
+struct pcan_usb_pro_interface {
+	struct peak_usb_device *dev[PCAN_USBPRO_CHANNEL_COUNT];
+	struct peak_time_ref time_ref;
+	int cm_ignore_count;
+	int dev_opened_count;
+};
+
+/* device information */
+struct pcan_usb_pro_device {
+	struct peak_usb_device dev;
+	struct pcan_usb_pro_interface *usb_if;
+};
+
+/* internal structure used to handle messages sent to bulk urb */
+struct pcan_usb_pro_msg {
+	u8 *rec_ptr;
+	int rec_buffer_size;
+	int rec_buffer_len;
+	union {
+		u16 *rec_cnt_rd;
+		u32 *rec_cnt;
+		u8 *rec_buffer;
+	} u;
+};
+
+/* records sizes table indexed on message id. (8-bits value) */
+static u16 pcan_usb_pro_sizeof_rec[256] = {
+	[PCAN_USBPRO_SETBTR] = sizeof(struct pcan_usb_pro_btr),
+	[PCAN_USBPRO_SETBUSACT] = sizeof(struct pcan_usb_pro_busact),
+	[PCAN_USBPRO_SETSILENT] = sizeof(struct pcan_usb_pro_silent),
+	[PCAN_USBPRO_SETFILTR] = sizeof(struct pcan_usb_pro_filter),
+	[PCAN_USBPRO_SETTS] = sizeof(struct pcan_usb_pro_setts),
+	[PCAN_USBPRO_GETDEVID] = sizeof(struct pcan_usb_pro_devid),
+	[PCAN_USBPRO_SETLED] = sizeof(struct pcan_usb_pro_setled),
+	[PCAN_USBPRO_RXMSG8] = sizeof(struct pcan_usb_pro_rxmsg),
+	[PCAN_USBPRO_RXMSG4] = sizeof(struct pcan_usb_pro_rxmsg) - 4,
+	[PCAN_USBPRO_RXMSG0] = sizeof(struct pcan_usb_pro_rxmsg) - 8,
+	[PCAN_USBPRO_RXRTR] = sizeof(struct pcan_usb_pro_rxmsg) - 8,
+	[PCAN_USBPRO_RXSTATUS] = sizeof(struct pcan_usb_pro_rxstatus),
+	[PCAN_USBPRO_RXTS] = sizeof(struct pcan_usb_pro_rxts),
+	[PCAN_USBPRO_TXMSG8] = sizeof(struct pcan_usb_pro_txmsg),
+	[PCAN_USBPRO_TXMSG4] = sizeof(struct pcan_usb_pro_txmsg) - 4,
+	[PCAN_USBPRO_TXMSG0] = sizeof(struct pcan_usb_pro_txmsg) - 8,
+};
+
+/*
+ * initialize PCAN-USB Pro message data structure
+ */
+static u8 *pcan_usb_pro_msg_init(struct pcan_usb_pro_msg *pm, void *buffer_addr,
+				 int buffer_size)
+{
+	if (buffer_size < PCAN_USBPRO_MSG_HEADER_LEN)
+		return NULL;
+
+	pm->u.rec_buffer = (u8 *)buffer_addr;
+	pm->rec_buffer_size = pm->rec_buffer_len = buffer_size;
+	pm->rec_ptr = pm->u.rec_buffer + PCAN_USBPRO_MSG_HEADER_LEN;
+
+	return pm->rec_ptr;
+}
+
+static u8 *pcan_usb_pro_msg_init_empty(struct pcan_usb_pro_msg *pm,
+				       void *buffer_addr, int buffer_size)
+{
+	u8 *pr = pcan_usb_pro_msg_init(pm, buffer_addr, buffer_size);
+
+	if (pr) {
+		pm->rec_buffer_len = PCAN_USBPRO_MSG_HEADER_LEN;
+		*pm->u.rec_cnt = 0;
+	}
+	return pr;
+}
+
+/*
+ * add one record to a message being built
+ */
+static int pcan_usb_pro_add_rec(struct pcan_usb_pro_msg *pm, u8 id, ...)
+{
+	int len, i;
+	u8 *pc;
+	va_list ap;
+
+	va_start(ap, id);
+
+	pc = pm->rec_ptr + 1;
+
+	i = 0;
+	switch (id) {
+	case PCAN_USBPRO_TXMSG8:
+		i += 4;
+	case PCAN_USBPRO_TXMSG4:
+		i += 4;
+	case PCAN_USBPRO_TXMSG0:
+		*pc++ = va_arg(ap, int);
+		*pc++ = va_arg(ap, int);
+		*pc++ = va_arg(ap, int);
+		*(u32 *)pc = cpu_to_le32(va_arg(ap, u32));
+		pc += 4;
+		memcpy(pc, va_arg(ap, int *), i);
+		pc += i;
+		break;
+
+	case PCAN_USBPRO_SETBTR:
+	case PCAN_USBPRO_GETDEVID:
+		*pc++ = va_arg(ap, int);
+		pc += 2;
+		*(u32 *)pc = cpu_to_le32(va_arg(ap, u32));
+		pc += 4;
+		break;
+
+	case PCAN_USBPRO_SETFILTR:
+	case PCAN_USBPRO_SETBUSACT:
+	case PCAN_USBPRO_SETSILENT:
+		*pc++ = va_arg(ap, int);
+		*(u16 *)pc = cpu_to_le16(va_arg(ap, int));
+		pc += 2;
+		break;
+
+	case PCAN_USBPRO_SETLED:
+		*pc++ = va_arg(ap, int);
+		*(u16 *)pc = cpu_to_le16(va_arg(ap, int));
+		pc += 2;
+		*(u32 *)pc = cpu_to_le32(va_arg(ap, u32));
+		pc += 4;
+		break;
+
+	case PCAN_USBPRO_SETTS:
+		pc++;
+		*(u16 *)pc = cpu_to_le16(va_arg(ap, int));
+		pc += 2;
+		break;
+
+	default:
+		pr_err("%s: %s(): unknown data type %02Xh (%d)\n",
+			PCAN_USB_DRIVER_NAME, __func__, id, id);
+		pc--;
+		break;
+	}
+
+	len = pc - pm->rec_ptr;
+	if (len > 0) {
+		*pm->u.rec_cnt = cpu_to_le32(*pm->u.rec_cnt+1);
+		*pm->rec_ptr = id;
+
+		pm->rec_ptr = pc;
+		pm->rec_buffer_len += len;
+	}
+
+	va_end(ap);
+
+	return len;
+}
+
+/*
+ * send PCAN-USB Pro command synchronously
+ */
+static int pcan_usb_pro_send_cmd(struct peak_usb_device *dev,
+				 struct pcan_usb_pro_msg *pum)
+{
+	int actual_length;
+	int err;
+
+	/* usb device unregistered? */
+	if (!(dev->state & PCAN_USB_STATE_CONNECTED))
+		return 0;
+
+	err = usb_bulk_msg(dev->udev,
+		usb_sndbulkpipe(dev->udev, PCAN_USBPRO_EP_CMDOUT),
+		pum->u.rec_buffer, pum->rec_buffer_len,
+		&actual_length, PCAN_USBPRO_COMMAND_TIMEOUT);
+	if (err)
+		netdev_err(dev->netdev, "sending command failure: %d\n", err);
+
+	return err;
+}
+
+/*
+ * wait for PCAN-USB Pro command response
+ */
+static int pcan_usb_pro_wait_rsp(struct peak_usb_device *dev,
+				 struct pcan_usb_pro_msg *pum)
+{
+	u8 req_data_type, req_channel;
+	int actual_length;
+	int i, err = 0;
+
+	/* usb device unregistered? */
+	if (!(dev->state & PCAN_USB_STATE_CONNECTED))
+		return 0;
+
+	req_data_type = pum->u.rec_buffer[4];
+	req_channel = pum->u.rec_buffer[5];
+
+	*pum->u.rec_cnt = 0;
+	for (i = 0; !err && i < PCAN_USBPRO_RSP_SUBMIT_MAX; i++) {
+		struct pcan_usb_pro_msg rsp;
+		union pcan_usb_pro_rec *pr;
+		u32 r, rec_cnt;
+		u16 rec_len;
+		u8 *pc;
+
+		err = usb_bulk_msg(dev->udev,
+			usb_rcvbulkpipe(dev->udev, PCAN_USBPRO_EP_CMDIN),
+			pum->u.rec_buffer, pum->rec_buffer_len,
+			&actual_length, PCAN_USBPRO_COMMAND_TIMEOUT);
+		if (err) {
+			netdev_err(dev->netdev, "waiting rsp error %d\n", err);
+			break;
+		}
+
+		if (actual_length == 0)
+			continue;
+
+		err = -EBADMSG;
+		if (actual_length < PCAN_USBPRO_MSG_HEADER_LEN) {
+			netdev_err(dev->netdev,
+				   "got abnormal too small rsp (len=%d)\n",
+				   actual_length);
+			break;
+		}
+
+		pc = pcan_usb_pro_msg_init(&rsp, pum->u.rec_buffer,
+			actual_length);
+
+		rec_cnt = le32_to_cpu(*rsp.u.rec_cnt);
+
+		/* loop on records stored into message */
+		for (r = 0; r < rec_cnt; r++) {
+			pr = (union pcan_usb_pro_rec *)pc;
+			rec_len = pcan_usb_pro_sizeof_rec[pr->data_type];
+			if (!rec_len) {
+				netdev_err(dev->netdev,
+					   "got unprocessed record in msg\n");
+				dump_mem("rcvd rsp msg", pum->u.rec_buffer,
+					 actual_length);
+				break;
+			}
+
+			/* check if response corresponds to request */
+			if (pr->data_type != req_data_type)
+				netdev_err(dev->netdev,
+					   "got unwanted rsp %xh: ignored\n",
+					   pr->data_type);
+
+			/* check if channel in response corresponds too */
+			else if ((req_channel != 0xff) && \
+				(pr->bus_act.channel != req_channel))
+				netdev_err(dev->netdev,
+					"got rsp %xh but on chan%u: ignored\n",
+					req_data_type, pr->bus_act.channel);
+
+			/* got the response */
+			else
+				return 0;
+
+			/* otherwise, go on with next record in message */
+			pc += rec_len;
+		}
+
+	}
+
+	return (i >= PCAN_USBPRO_RSP_SUBMIT_MAX) ? -ERANGE : err;
+}
+
+static int pcan_usb_pro_send_req(struct peak_usb_device *dev, int req_id,
+				 int req_value, void *req_addr, int req_size)
+{
+	int err;
+	u8 req_type;
+	unsigned int p;
+
+	/* usb device unregistered? */
+	if (!(dev->state & PCAN_USB_STATE_CONNECTED))
+		return 0;
+
+	memset(req_addr, '\0', req_size);
+
+	req_type = USB_TYPE_VENDOR | USB_RECIP_OTHER;
+
+	switch (req_id) {
+	case PCAN_USBPRO_REQ_FCT:
+		p = usb_sndctrlpipe(dev->udev, 0);
+		break;
+
+	default:
+		p = usb_rcvctrlpipe(dev->udev, 0);
+		req_type |= USB_DIR_IN;
+		break;
+	}
+
+	err = usb_control_msg(dev->udev, p, req_id, req_type, req_value, 0,
+			      req_addr, req_size, 2 * USB_CTRL_GET_TIMEOUT);
+	if (err < 0) {
+		netdev_info(dev->netdev,
+			    "unable to request usb[type=%d value=%d] err=%d\n",
+			    req_id, req_value, err);
+		return err;
+	}
+
+	return 0;
+}
+
+static int pcan_usb_pro_set_ts(struct peak_usb_device *dev, u16 onoff)
+{
+	struct pcan_usb_pro_msg um;
+	u8 tmp[32];
+
+	pcan_usb_pro_msg_init_empty(&um, tmp, sizeof(tmp));
+	pcan_usb_pro_add_rec(&um, PCAN_USBPRO_SETTS, onoff);
+
+	return pcan_usb_pro_send_cmd(dev, &um);
+}
+
+static int pcan_usb_pro_set_bus(struct peak_usb_device *dev, u8 onoff)
+{
+	struct pcan_usb_pro_msg um;
+	u8 tmp[32];
+
+	pcan_usb_pro_msg_init_empty(&um, tmp, sizeof(tmp));
+	pcan_usb_pro_add_rec(&um, PCAN_USBPRO_SETBUSACT, dev->ctrl_idx, onoff);
+
+	return pcan_usb_pro_send_cmd(dev, &um);
+}
+
+static int pcan_usb_pro_set_silent(struct peak_usb_device *dev, u8 onoff)
+{
+	struct pcan_usb_pro_msg um;
+	u8 tmp[32];
+
+	pcan_usb_pro_msg_init_empty(&um, tmp, sizeof(tmp));
+	pcan_usb_pro_add_rec(&um, PCAN_USBPRO_SETSILENT, dev->ctrl_idx, onoff);
+
+	return pcan_usb_pro_send_cmd(dev, &um);
+}
+
+static int pcan_usb_pro_set_filter(struct peak_usb_device *dev, u16 filter_mode)
+{
+	struct pcan_usb_pro_msg um;
+	u8 tmp[32];
+
+	pcan_usb_pro_msg_init_empty(&um, tmp, sizeof(tmp));
+	pcan_usb_pro_add_rec(&um, PCAN_USBPRO_SETFILTR, dev->ctrl_idx,
+			     filter_mode);
+
+	return pcan_usb_pro_send_cmd(dev, &um);
+}
+
+static int pcan_usb_pro_set_led(struct peak_usb_device *dev, u8 mode,
+				u32 timeout)
+{
+	struct pcan_usb_pro_msg um;
+	u8 tmp[32];
+
+	pcan_usb_pro_msg_init_empty(&um, tmp, sizeof(tmp));
+	pcan_usb_pro_add_rec(&um, PCAN_USBPRO_SETLED, dev->ctrl_idx, mode,
+			     timeout);
+
+	return pcan_usb_pro_send_cmd(dev, &um);
+}
+
+static int pcan_usb_pro_get_device_id(struct peak_usb_device *dev,
+				      u32 *device_id)
+{
+	struct pcan_usb_pro_msg um;
+	struct pcan_usb_pro_devid *pdn;
+	u8 tmp[32], *pc;
+	int err;
+
+	pc = pcan_usb_pro_msg_init_empty(&um, tmp, sizeof(tmp));
+	pcan_usb_pro_add_rec(&um, PCAN_USBPRO_GETDEVID, dev->ctrl_idx);
+
+	err =  pcan_usb_pro_send_cmd(dev, &um);
+	if (err)
+		return err;
+
+	err = pcan_usb_pro_wait_rsp(dev, &um);
+	if (err)
+		return err;
+
+	pdn = (struct pcan_usb_pro_devid *)pc;
+	if (device_id)
+		*device_id = le32_to_cpu(pdn->serial_num);
+
+	return err;
+}
+
+static int pcan_usb_pro_set_bittiming(struct peak_usb_device *dev,
+				      struct can_bittiming *bt)
+{
+	struct pcan_usb_pro_msg um;
+	u8 tmp[32];
+	u32 ccbt;
+
+	ccbt = (dev->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) ? 0x00800000 : 0;
+	ccbt |= (bt->sjw - 1) << 24;
+	ccbt |= (bt->phase_seg2 - 1) << 20;
+	ccbt |= (bt->prop_seg + bt->phase_seg1 - 1) << 16; /* = tseg1 */
+	ccbt |= bt->brp - 1;
+
+	netdev_info(dev->netdev, "setting ccbt=0x%08x\n", ccbt);
+
+	pcan_usb_pro_msg_init_empty(&um, tmp, sizeof(tmp));
+	pcan_usb_pro_add_rec(&um, PCAN_USBPRO_SETBTR, dev->ctrl_idx, ccbt);
+
+	return pcan_usb_pro_send_cmd(dev, &um);
+}
+
+static void pcan_usb_pro_drv_loaded(struct peak_usb_device *dev, int loaded)
+{
+	u8 buffer[16];
+
+	buffer[0] = 0;
+	buffer[1] = loaded ? 1 : 0;
+
+	pcan_usb_pro_send_req(dev, PCAN_USBPRO_REQ_FCT,
+			      PCAN_USBPRO_FCT_DRVLD, buffer, sizeof(buffer));
+}
+
+static inline
+struct pcan_usb_pro_interface *pcan_usb_pro_dev_if(struct peak_usb_device *dev)
+{
+	struct pcan_usb_pro_device *pdev =
+			container_of(dev, struct pcan_usb_pro_device, dev);
+	return pdev->usb_if;
+}
+
+static int pcan_usb_pro_handle_canmsg(struct pcan_usb_pro_interface *usb_if,
+				      struct pcan_usb_pro_rxmsg *rx)
+{
+	const unsigned int ctrl_idx = (rx->len >> 4) & 0x0f;
+	struct peak_usb_device *dev = usb_if->dev[ctrl_idx];
+	struct net_device *netdev = dev->netdev;
+	struct can_frame *can_frame;
+	struct sk_buff *skb;
+	struct timeval tv;
+
+	skb = alloc_can_skb(netdev, &can_frame);
+	if (!skb)
+		return -ENOMEM;
+
+	can_frame->can_id = le32_to_cpu(rx->id);
+	can_frame->can_dlc = rx->len & 0x0f;
+
+	if (rx->flags & PCAN_USBPRO_EXT)
+		can_frame->can_id |= CAN_EFF_FLAG;
+
+	if (rx->flags & PCAN_USBPRO_RTR)
+		can_frame->can_id |= CAN_RTR_FLAG;
+	else
+		memcpy(can_frame->data, rx->data, can_frame->can_dlc);
+
+	peak_usb_get_ts_tv(&usb_if->time_ref, le32_to_cpu(rx->ts32), &tv);
+	skb->tstamp = timeval_to_ktime(tv);
+
+	netif_rx(skb);
+	netdev->stats.rx_packets++;
+	netdev->stats.rx_bytes += can_frame->can_dlc;
+
+	return 0;
+}
+
+static int pcan_usb_pro_handle_error(struct pcan_usb_pro_interface *usb_if,
+				     struct pcan_usb_pro_rxstatus *er)
+{
+	const u32 raw_status = le32_to_cpu(er->status);
+	const unsigned int ctrl_idx = (er->channel >> 4) & 0x0f;
+	struct peak_usb_device *dev = usb_if->dev[ctrl_idx];
+	struct net_device *netdev = dev->netdev;
+	struct can_frame *can_frame;
+	struct sk_buff *skb;
+	struct timeval tv;
+
+	skb = alloc_can_err_skb(netdev, &can_frame);
+	if (!skb)
+		return -ENOMEM;
+
+	if (raw_status & PCAN_USBPRO_STATUS_BUS) {
+		can_frame->can_id |= CAN_ERR_BUSOFF;
+		can_bus_off(netdev);
+
+	} else if (raw_status & PCAN_USBPRO_STATUS_ERROR) {
+		u32 rx_err_cnt = (le32_to_cpu(er->err_frm) & 0x00ff0000) >> 16;
+		u32 tx_err_cnt = (le32_to_cpu(er->err_frm) & 0xff000000) >> 24;
+
+		if (rx_err_cnt > 127) {
+			can_frame->can_id |= CAN_ERR_CRTL;
+			can_frame->data[1] |= CAN_ERR_CRTL_RX_PASSIVE;
+			dev->can.can_stats.error_passive++;
+
+		} else if (rx_err_cnt > 96) {
+			can_frame->can_id |= CAN_ERR_CRTL;
+			can_frame->data[1] |= CAN_ERR_CRTL_RX_WARNING;
+			dev->can.can_stats.error_warning++;
+		}
+
+		if (tx_err_cnt > 127) {
+			can_frame->can_id |= CAN_ERR_CRTL;
+			can_frame->data[1] |= CAN_ERR_CRTL_TX_PASSIVE;
+			dev->can.can_stats.error_passive++;
+
+		} else if (tx_err_cnt > 96) {
+			can_frame->can_id |= CAN_ERR_CRTL;
+			can_frame->data[1] |= CAN_ERR_CRTL_TX_WARNING;
+			dev->can.can_stats.error_warning++;
+		}
+	}
+
+	if (raw_status & PCAN_USBPRO_STATUS_OVERRUN) {
+		can_frame->can_id |= CAN_ERR_PROT;
+		can_frame->data[2] |= CAN_ERR_PROT_OVERLOAD;
+	}
+
+	if (raw_status & PCAN_USBPRO_STATUS_QOVERRUN) {
+		can_frame->can_id |= CAN_ERR_CRTL;
+		can_frame->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
+	}
+
+	peak_usb_get_ts_tv(&usb_if->time_ref, le32_to_cpu(er->ts32), &tv);
+	skb->tstamp = timeval_to_ktime(tv);
+	netif_rx(skb);
+	netdev->stats.rx_packets++;
+	netdev->stats.rx_bytes += can_frame->can_dlc;
+
+	return 0;
+}
+
+static void pcan_usb_pro_handle_ts(struct pcan_usb_pro_interface *usb_if,
+				   struct pcan_usb_pro_rxts *ts)
+{
+	/* should wait until clock is stabilized */
+	if (usb_if->cm_ignore_count > 0)
+		usb_if->cm_ignore_count--;
+	else
+		peak_usb_set_ts_now(&usb_if->time_ref,
+				    le32_to_cpu(ts->ts64[1]));
+}
+
+/*
+ * callback for bulk IN urb
+ */
+static int pcan_usb_pro_decode_buf(struct peak_usb_device *dev, struct urb *urb)
+{
+	struct pcan_usb_pro_interface *usb_if = pcan_usb_pro_dev_if(dev);
+	struct net_device *netdev = dev->netdev;
+	struct pcan_usb_pro_msg usb_msg;
+	u8 *rec_ptr, *msg_end;
+	u16 rec_cnt;
+	int err = 0;
+
+	rec_ptr = pcan_usb_pro_msg_init(&usb_msg, urb->transfer_buffer,
+					urb->actual_length);
+	if (!rec_ptr) {
+		netdev_err(netdev, "bad msg hdr len %d\n", urb->actual_length);
+		return -EINVAL;
+	}
+
+	/* loop reading all the records from the incoming message */
+	msg_end = urb->transfer_buffer + urb->actual_length;
+	rec_cnt = le16_to_cpu(*usb_msg.u.rec_cnt_rd);
+	for (; rec_cnt > 0; rec_cnt--) {
+		union pcan_usb_pro_rec *pr = (union pcan_usb_pro_rec *)rec_ptr;
+		u16 sizeof_rec = pcan_usb_pro_sizeof_rec[pr->data_type];
+
+		if (!sizeof_rec) {
+			netdev_err(netdev,
+				   "got unsupported rec in usb msg:\n");
+			err = -ENOTSUPP;
+			break;
+		}
+
+		/* check if the record goes out of current packet */
+		if (rec_ptr + sizeof_rec > msg_end) {
+			netdev_err(netdev,
+				"got frag rec: should inc usb rx buf size\n");
+			err = -EBADMSG;
+			break;
+		}
+
+		switch (pr->data_type) {
+		case PCAN_USBPRO_RXMSG8:
+		case PCAN_USBPRO_RXMSG4:
+		case PCAN_USBPRO_RXMSG0:
+		case PCAN_USBPRO_RXRTR:
+			err = pcan_usb_pro_handle_canmsg(usb_if, &pr->rx_msg);
+			if (err < 0)
+				goto fail;
+			break;
+
+		case PCAN_USBPRO_RXSTATUS:
+			err = pcan_usb_pro_handle_error(usb_if, &pr->rx_status);
+			if (err < 0)
+				goto fail;
+			break;
+
+		case PCAN_USBPRO_RXTS:
+			pcan_usb_pro_handle_ts(usb_if, &pr->rx_ts);
+			break;
+
+		default:
+			netdev_err(netdev,
+				   "unhandled rec type 0x%02x (%d): ignored\n",
+				   pr->data_type, pr->data_type);
+			break;
+		}
+
+		rec_ptr += sizeof_rec;
+	}
+
+fail:
+	if (err)
+		dump_mem("received msg",
+			 urb->transfer_buffer, urb->actual_length);
+
+	return err;
+}
+
+static int pcan_usb_pro_encode_msg(struct peak_usb_device *dev,
+				   struct sk_buff *skb, u8 *obuf, size_t *size)
+{
+	struct can_frame *cf = (struct can_frame *)skb->data;
+	u8 data_type, len, flags;
+	struct pcan_usb_pro_msg usb_msg;
+
+	pcan_usb_pro_msg_init_empty(&usb_msg, obuf, *size);
+
+	if ((cf->can_id & CAN_RTR_FLAG) || (cf->can_dlc == 0))
+		data_type = PCAN_USBPRO_TXMSG0;
+	else if (cf->can_dlc <= 4)
+		data_type = PCAN_USBPRO_TXMSG4;
+	else
+		data_type = PCAN_USBPRO_TXMSG8;
+
+	len = (dev->ctrl_idx << 4) | (cf->can_dlc & 0x0f);
+
+	flags = 0;
+	if (cf->can_id & CAN_EFF_FLAG)
+		flags |= 0x02;
+	if (cf->can_id & CAN_RTR_FLAG)
+		flags |= 0x01;
+
+	pcan_usb_pro_add_rec(&usb_msg, data_type, 0, flags, len, cf->can_id,
+			     cf->data);
+
+	*size = usb_msg.rec_buffer_len;
+
+	return 0;
+}
+
+static int pcan_usb_pro_start(struct peak_usb_device *dev)
+{
+	struct pcan_usb_pro_device *pdev =
+			container_of(dev, struct pcan_usb_pro_device, dev);
+	int err;
+
+	err = pcan_usb_pro_set_silent(dev,
+				dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY);
+	if (err)
+		return err;
+
+	/* filter mode: 0-> All OFF; 1->bypass */
+	err = pcan_usb_pro_set_filter(dev, 1);
+	if (err)
+		return err;
+
+	/* opening first device: */
+	if (pdev->usb_if->dev_opened_count == 0) {
+		/* reset time_ref */
+		peak_usb_init_time_ref(&pdev->usb_if->time_ref, &pcan_usb_pro);
+
+		/* ask device to send ts messages */
+		err = pcan_usb_pro_set_ts(dev, 1);
+	}
+
+	pdev->usb_if->dev_opened_count++;
+
+	return err;
+}
+
+/*
+ * stop interface
+ * (last chance before set bus off)
+ */
+static int pcan_usb_pro_stop(struct peak_usb_device *dev)
+{
+	struct pcan_usb_pro_device *pdev =
+			container_of(dev, struct pcan_usb_pro_device, dev);
+
+	/* turn off ts msgs for that interface if no other dev opened */
+	if (pdev->usb_if->dev_opened_count == 1)
+		pcan_usb_pro_set_ts(dev, 0);
+
+	pdev->usb_if->dev_opened_count--;
+
+	return 0;
+}
+
+/*
+ * called when probing to initialize a device object.
+ */
+static int pcan_usb_pro_init(struct peak_usb_device *dev)
+{
+	struct pcan_usb_pro_interface *usb_if;
+	struct pcan_usb_pro_device *pdev =
+			container_of(dev, struct pcan_usb_pro_device, dev);
+
+	/* do this for 1st channel only */
+	if (!dev->prev_siblings) {
+		struct pcan_usb_pro_fwinfo fi;
+		struct pcan_usb_pro_blinfo bi;
+
+		/* allocate netdevices common structure attached to first one */
+		usb_if = kzalloc(sizeof(struct pcan_usb_pro_interface),
+				 GFP_KERNEL);
+		if (!usb_if)
+			return -ENOMEM;
+
+		/* number of ts msgs to ignore before taking one into account */
+		usb_if->cm_ignore_count = 5;
+
+		/*
+		 * explicit use of dev_info() instead of netdev_info() here:
+		 * information displayed are related to the device itself, not
+		 * to the canx netdevices.
+		 */
+		if (pcan_usb_pro_send_req(dev, PCAN_USBPRO_REQ_INFO,
+					  PCAN_USBPRO_INFO_FW,
+					  &fi, sizeof(fi)) >= 0)
+			dev_info(dev->netdev->dev.parent,
+				 "%s fw v%d.%d.%d (%02d/%02d/%02d) fw 0x%08x\n",
+				 pcan_usb_pro.name,
+				 fi.version[0], fi.version[1], fi.version[2],
+				 fi.day, fi.month, fi.year, fi.fw_type);
+
+		if (pcan_usb_pro_send_req(dev, PCAN_USBPRO_REQ_INFO,
+					  PCAN_USBPRO_INFO_BL,
+					  &bi, sizeof(bi)) >= 0) {
+			dev_info(dev->netdev->dev.parent,
+				 "bootloader v%d.%d.%d (%02d/%02d/%02d)\n",
+				 bi.version[0], bi.version[1], bi.version[2],
+				 bi.day, bi.month, bi.year);
+
+			dev_info(dev->netdev->dev.parent,
+				 "serial %08X.%08X hw %u rev %u\n",
+				 bi.serial_num_hi, bi.serial_num_lo,
+				 bi.hw_type, bi.hw_rev);
+		}
+
+		/* tell the device the can driver is running */
+		pcan_usb_pro_drv_loaded(dev, 1);
+	} else {
+		usb_if = pcan_usb_pro_dev_if(dev->prev_siblings);
+	}
+
+	pdev->usb_if = usb_if;
+	usb_if->dev[dev->ctrl_idx] = dev;
+
+	/* set LED in default state (end of init phase) */
+	pcan_usb_pro_set_led(dev, 0, 1);
+
+	return 0;
+}
+
+static void pcan_usb_pro_exit(struct peak_usb_device *dev)
+{
+	struct pcan_usb_pro_device *pdev =
+			container_of(dev, struct pcan_usb_pro_device, dev);
+
+	/*
+	 * when rmmod called before unplug and if down, should reset things
+	 * before leaving
+	 */
+	if (dev->can.state != CAN_STATE_STOPPED) {
+		/* set bus off on the corresponding channel */
+		pcan_usb_pro_set_bus(dev, 0);
+	}
+
+	/* if channel #0 (only) */
+	if (dev->ctrl_idx == 0) {
+		/* turn off calibration message if any device were opened */
+		if (pdev->usb_if->dev_opened_count > 0)
+			pcan_usb_pro_set_ts(dev, 0);
+
+		/* tell the PCAN-USB Pro device driver is being unloaded */
+		pcan_usb_pro_drv_loaded(dev, 0);
+	}
+}
+
+/*
+ * called when PCAN-USB Pro adapter is unplugged
+ */
+static void pcan_usb_pro_free(struct peak_usb_device *dev)
+{
+	/* last device: can free pcan_usb_pro_interface object now */
+	if (!dev->prev_siblings && !dev->next_siblings)
+		kfree(pcan_usb_pro_dev_if(dev));
+}
+
+/*
+ * probe function for new PCAN-USB Pro usb interface
+ */
+static int pcan_usb_pro_probe(struct usb_interface *intf)
+{
+	struct usb_host_interface *if_desc;
+	int i;
+
+	if_desc = intf->altsetting;
+
+	/* check interface endpoint addresses */
+	for (i = 0; i < if_desc->desc.bNumEndpoints; i++) {
+		struct usb_endpoint_descriptor *ep = &if_desc->endpoint[i].desc;
+
+		/*
+		 * below is the list of valid ep addreses. Any other ep address
+		 * is considered as not-CAN interface address => no dev created
+		 */
+		switch (ep->bEndpointAddress) {
+		case PCAN_USBPRO_EP_CMDOUT:
+		case PCAN_USBPRO_EP_CMDIN:
+		case PCAN_USBPRO_EP_MSGOUT_0:
+		case PCAN_USBPRO_EP_MSGOUT_1:
+		case PCAN_USBPRO_EP_MSGIN:
+		case PCAN_USBPRO_EP_UNUSED:
+			break;
+		default:
+			return -ENODEV;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * describe the PCAN-USB Pro adapter
+ */
+struct peak_usb_adapter pcan_usb_pro = {
+	.name = "PCAN-USB Pro",
+	.device_id = PCAN_USBPRO_PRODUCT_ID,
+	.ctrl_count = PCAN_USBPRO_CHANNEL_COUNT,
+	.clock = {
+		.freq = PCAN_USBPRO_CRYSTAL_HZ,
+	},
+	.bittiming_const = {
+		.name = "pcan_usb_pro",
+		.tseg1_min = 1,
+		.tseg1_max = 16,
+		.tseg2_min = 1,
+		.tseg2_max = 8,
+		.sjw_max = 4,
+		.brp_min = 1,
+		.brp_max = 1024,
+		.brp_inc = 1,
+	},
+
+	/* size of device private data */
+	.sizeof_dev_private = sizeof(struct pcan_usb_pro_device),
+
+	/* timestamps usage */
+	.ts_used_bits = 32,
+	.ts_period = 1000000, /* calibration period in ts. */
+	.us_per_ts_scale = 1, /* us = (ts * scale) >> shift */
+	.us_per_ts_shift = 0,
+
+	/* give here commands/messages in/out endpoints */
+	.ep_msg_in = PCAN_USBPRO_EP_MSGIN,
+	.ep_msg_out = {PCAN_USBPRO_EP_MSGOUT_0, PCAN_USBPRO_EP_MSGOUT_1},
+
+	/* size of rx/tx usb buffers */
+	.rx_buffer_size = PCAN_USBPRO_RX_BUFFER_SIZE,
+	.tx_buffer_size = PCAN_USBPRO_TX_BUFFER_SIZE,
+
+	/* device callbacks */
+	.intf_probe = pcan_usb_pro_probe,
+	.dev_init = pcan_usb_pro_init,
+	.dev_exit = pcan_usb_pro_exit,
+	.dev_free = pcan_usb_pro_free,
+	.dev_set_bus = pcan_usb_pro_set_bus,
+	.dev_set_bittiming = pcan_usb_pro_set_bittiming,
+	.dev_get_device_id = pcan_usb_pro_get_device_id,
+	.dev_decode_buf = pcan_usb_pro_decode_buf,
+	.dev_encode_msg = pcan_usb_pro_encode_msg,
+	.dev_start = pcan_usb_pro_start,
+	.dev_stop = pcan_usb_pro_stop,
+};
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.h b/drivers/net/can/usb/peak_usb/pcan_usb_pro.h
new file mode 100644
index 0000000..a869918
--- /dev/null
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.h
@@ -0,0 +1,178 @@
+/*
+ * CAN driver for PEAK System PCAN-USB Pro adapter
+ * Derived from the PCAN project file driver/src/pcan_usbpro_fw.h
+ *
+ * Copyright (C) 2003-2011 PEAK System-Technik GmbH
+ * Copyright (C) 2011-2012 Stephane Grosjean <s.grosjean-g4cQ8AsIbFbL9ATBNaCtXw@public.gmane.org>
+ *
+ * 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 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+#ifndef PCAN_USB_PRO_H
+#define PCAN_USB_PRO_H
+
+/*
+ * USB Vendor request data types
+ */
+#define PCAN_USBPRO_REQ_INFO		0
+#define PCAN_USBPRO_REQ_FCT		2
+
+/* Vendor Request value for XXX_INFO */
+#define PCAN_USBPRO_INFO_BL		0
+#define PCAN_USBPRO_INFO_FW		1
+
+/* Vendor Request value for XXX_FCT */
+#define PCAN_USBPRO_FCT_DRVLD		5 /* tell device driver is loaded */
+
+/* PCAN_USBPRO_INFO_BL vendor request record type */
+struct __packed pcan_usb_pro_blinfo {
+	u32 ctrl_type;
+	u8  version[4];
+	u8  day;
+	u8  month;
+	u8  year;
+	u8  dummy;
+	u32 serial_num_hi;
+	u32 serial_num_lo;
+	u32 hw_type;
+	u32 hw_rev;
+};
+
+/* PCAN_USBPRO_INFO_FW vendor request record type */
+struct __packed pcan_usb_pro_fwinfo {
+	u32 ctrl_type;
+	u8  version[4];
+	u8  day;
+	u8  month;
+	u8  year;
+	u8  dummy;
+	u32 fw_type;
+};
+
+/*
+ * USB Command record types
+ */
+#define PCAN_USBPRO_SETBTR	0x02
+#define PCAN_USBPRO_SETBUSACT	0x04
+#define PCAN_USBPRO_SETSILENT	0x05
+#define PCAN_USBPRO_SETFILTR	0x0a
+#define PCAN_USBPRO_SETTS	0x10
+#define PCAN_USBPRO_GETDEVID	0x12
+#define PCAN_USBPRO_SETLED	0x1C
+#define PCAN_USBPRO_RXMSG8	0x80
+#define PCAN_USBPRO_RXMSG4	0x81
+#define PCAN_USBPRO_RXMSG0	0x82
+#define PCAN_USBPRO_RXRTR	0x83
+#define PCAN_USBPRO_RXSTATUS	0x84
+#define PCAN_USBPRO_RXTS	0x85
+#define PCAN_USBPRO_TXMSG8	0x41
+#define PCAN_USBPRO_TXMSG4	0x42
+#define PCAN_USBPRO_TXMSG0	0x43
+
+/* record structures */
+struct __packed pcan_usb_pro_btr {
+	u8  data_type;
+	u8  channel;
+	u16 dummy;
+	u32 CCBT;
+};
+
+struct __packed pcan_usb_pro_busact {
+	u8  data_type;
+	u8  channel;
+	u16 onoff;
+};
+
+struct __packed pcan_usb_pro_silent {
+	u8  data_type;
+	u8  channel;
+	u16 onoff;
+};
+
+struct __packed pcan_usb_pro_filter {
+	u8  data_type;
+	u8  dummy;
+	u16 filter_mode;
+};
+
+struct __packed pcan_usb_pro_setts {
+	u8  data_type;
+	u8  dummy;
+	u16 mode;
+};
+
+struct __packed pcan_usb_pro_devid {
+	u8  data_type;
+	u8  channel;
+	u16 dummy;
+	u32 serial_num;
+};
+
+struct __packed pcan_usb_pro_setled {
+	u8  data_type;
+	u8  channel;
+	u16 mode;
+	u32 timeout;
+};
+
+struct __packed pcan_usb_pro_rxmsg {
+	u8  data_type;
+	u8  client;
+	u8  flags;
+	u8  len;
+	u32 ts32;
+	u32 id;
+
+	u8  data[8];
+};
+
+#define PCAN_USBPRO_STATUS_ERROR	0x0001
+#define PCAN_USBPRO_STATUS_BUS		0x0002
+#define PCAN_USBPRO_STATUS_OVERRUN	0x0004
+#define PCAN_USBPRO_STATUS_QOVERRUN	0x0008
+
+struct __packed pcan_usb_pro_rxstatus {
+	u8  data_type;
+	u8  channel;
+	u16 status;
+	u32 ts32;
+	u32 err_frm;
+};
+
+struct __packed pcan_usb_pro_rxts {
+	u8  data_type;
+	u8  dummy[3];
+	u32 ts64[2];
+};
+
+struct __packed pcan_usb_pro_txmsg {
+	u8  data_type;
+	u8  client;
+	u8  flags;
+	u8  len;
+	u32 id;
+	u8  data[8];
+};
+
+union pcan_usb_pro_rec {
+	u8				data_type;
+	struct pcan_usb_pro_btr		btr;
+	struct pcan_usb_pro_busact	bus_act;
+	struct pcan_usb_pro_silent	silent_mode;
+	struct pcan_usb_pro_filter	filter_mode;
+	struct pcan_usb_pro_setts	ts;
+	struct pcan_usb_pro_devid	dev_id;
+	struct pcan_usb_pro_setled	set_led;
+	struct pcan_usb_pro_rxmsg	rx_msg;
+	struct pcan_usb_pro_rxstatus	rx_status;
+	struct pcan_usb_pro_rxts	rx_ts;
+	struct pcan_usb_pro_txmsg	tx_msg;
+};
+
+#endif
-- 
1.7.1

--
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] 15+ messages in thread

* Re: [PATCH 2/3 V3.1] can/usb: PEAK-System Technik PCAN-USB specific part
  2012-01-19 10:27 ` [PATCH 2/3 V3.1] can/usb: PEAK-System Technik PCAN-USB " Stephane Grosjean
@ 2012-01-19 15:07   ` Alan Stern
  2012-01-20  7:52     ` Oliver Hartkopp
  0 siblings, 1 reply; 15+ messages in thread
From: Alan Stern @ 2012-01-19 15:07 UTC (permalink / raw)
  To: Stephane Grosjean
  Cc: Oliver Hartkopp, linux-usb Mailing List, netdev Mailing List,
	linux-can Mailing List

On Thu, 19 Jan 2012, Stephane Grosjean wrote:

> This patch adds the specific part which handles the PCAN-USB adapter from
> PEAK-System Technik (http://www.peak-system.com). The PCAN-USB adapter is
> a sja1000 based, mono-channel USB 1.1 adapter compliant with CAN
> specifications 2.0A (11-bit ID) and 2.0B (29-bit ID).
> 
> V3.1: same as V3 + add linux-can in cc:
> +/*
> + * send a command
> + */
> +static int pcan_usb_send_cmd(struct peak_usb_device *dev, u8 f, u8 n, u8 *p)
> +{
> +	int err;
> +	int actual_length;
> +	struct pcan_usb_parameter cmd = {
> +		.function = f,
> +		.number = n,
> +	};
> +
> +	/* usb device unregistered? */
> +	if (!(dev->state & PCAN_USB_STATE_CONNECTED))
> +		return 0;
> +
> +	if (p)
> +		memcpy(cmd.parameters, p, ARRAY_SIZE(cmd.parameters));
> +
> +	err = usb_bulk_msg(dev->udev,
> +		usb_sndbulkpipe(dev->udev, PCAN_USB_EP_CMDOUT), &cmd,
> +		sizeof(struct pcan_usb_parameter), &actual_length,
> +		PCAN_USB_COMMAND_TIMEOUT);

Here, and in several other places in this patch series, you submit
requests using a transfer buffer that is allocated on the stack.  This
is forbidden because not all platforms can do DMA to stack or static
addresses.  The transfer buffers should be allocated dynamically.

Alan Stern


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

* Re: [PATCH 0/3 V3.1] can/usb: Add PEAK-System PCAN USB adapters driver
  2012-01-19 10:27 [PATCH 0/3 V3.1] can/usb: Add PEAK-System PCAN USB adapters driver Stephane Grosjean
       [not found] ` <1326968863-20229-1-git-send-email-s.grosjean-g4cQ8AsIbFbL9ATBNaCtXw@public.gmane.org>
  2012-01-19 10:27 ` [PATCH 2/3 V3.1] can/usb: PEAK-System Technik PCAN-USB " Stephane Grosjean
@ 2012-01-19 19:08 ` Oliver Hartkopp
  2012-01-20  9:28   ` Wolfgang Grandegger
  2 siblings, 1 reply; 15+ messages in thread
From: Oliver Hartkopp @ 2012-01-19 19:08 UTC (permalink / raw)
  To: Stephane Grosjean
  Cc: linux-usb Mailing List, netdev Mailing List, linux-can Mailing List

On 19.01.2012 11:27, Stephane Grosjean wrote:

> That serie of patches adds the peak_usb driver which handles the PCAN-USB
> and PCAN-USB Pro adapters from PEAK-System (http://www.peak-system.com).
> These patches are planned for kernel v3.02.00 and above.


Hello Stephane,

i tried your patch with Linus' current tree (3.3 merge).

Plugging-in the PEAK USB adapter it turns out to be *very* informative in
the kernel log and also presents information twice:

[  153.566193] usb 2-1.2: new full-speed USB device number 6 using ehci_hcd
[  153.663659] usb 2-1.2: New USB device found, idVendor=0c72, idProduct=000c
[  153.663665] usb 2-1.2: New USB device strings: Mfr=0, Product=3, SerialNumber=0
[  153.663669] usb 2-1.2: Product: VER1:PEAK    
[  153.663671] VER2:02.8.01        
[  153.663672] DAT :06.05.2004
[  153.663672] TIME:09:35:37
[  153.663673]                              ...
[  153.682683] peak_usb: PCAN-USB interfaces driver loaded
[  153.682736] peak_usb 2-1.2:1.0: new PEAK-System usb adapter with 1 can detected:
[  153.682741] peak_usb 2-1.2:1.0: PEAK-System PCAN-USB
[  153.682746] peak_usb 2-1.2:1.0: VER1:PEAK    .VER2:02.8.01        .DAT :06.05.2004.TIME:09:35:37.                             ...
[  153.685422] peak_usb 2-1.2:1.0: serial FFFFFFFF hwrev 28
[  153.686413] peak_usb 2-1.2:1.0: can2 attached to PCAN-USB can 0 (device 46)
[  153.686521] usbcore: registered new interface driver peak_usb

As i assume that this VER2/DAT/TIME stuff and also the "..." line comes
somewhere from the device itself and is printed by the USB stack, i wonder if
we could reduce the information provided by the peak_usb driver then.

E.g.

[  153.682683] peak_usb: PCAN-USB interfaces driver loaded
# is obsolete as peak_usb speaks to us in the following lines

[  153.682736] peak_usb 2-1.2:1.0: new PEAK-System usb adapter with 1 can detected:
[  153.682741] peak_usb 2-1.2:1.0: PEAK-System PCAN-USB
# Can be merged, e.g. to
[  153.682741] peak_usb 2-1.2:1.0: PEAK-System PCAN-USB adapter hwrev 28 (one channel)


[  153.682746] peak_usb 2-1.2:1.0: VER1:PEAK    .VER2:02.8.01        .DAT :06.05.2004.TIME:09:35:37.                             ...
# If you think, this is still needed(???) -> at least kill the spaces like this:
[  153.682746] peak_usb 2-1.2:1.0: VER1:PEAK.VER2:02.8.01.DAT:06.05.2004.TIME:09:35:37....


[  153.685422] peak_usb 2-1.2:1.0: serial FFFFFFFF hwrev 28
[  153.686413] peak_usb 2-1.2:1.0: can2 attached to PCAN-USB can 0 (device 46)
# Can be merged, e.g. to
[  153.686413] peak_usb 2-1.2:1.0: PCAN-USB channel 0 (serial FFFFFFFF, device 46) attached to can2

So it would finally look like this (6 -> 2 lines):

[  153.682741] peak_usb 2-1.2:1.0: PEAK-System PCAN-USB adapter hwrev 28 (one channel)
[  153.686413] peak_usb 2-1.2:1.0: PCAN-USB channel 0 (serial FFFFFFFF, device 46) attached to can2

I'm not sure, if the serial number was identical when having two channels like in the USB-Pro.
If so the serial number in general could be moved up like this:

[  153.682741] peak_usb 2-1.2:1.0: PEAK-System PCAN-USB adapter hwrev 28 serial FFFFFFFF (one channel)
[  153.686413] peak_usb 2-1.2:1.0: PCAN-USB channel 0 (device 46) attached to can2

Btw. thanks for your contribution. You did really big steps in improving your code.
I will do some functional tests now ...

Best regards,
Oliver

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

* Re: [PATCH 2/3 V3.1] can/usb: PEAK-System Technik PCAN-USB specific part
  2012-01-19 15:07   ` Alan Stern
@ 2012-01-20  7:52     ` Oliver Hartkopp
  2012-01-20 12:28       ` Oliver Hartkopp
  0 siblings, 1 reply; 15+ messages in thread
From: Oliver Hartkopp @ 2012-01-20  7:52 UTC (permalink / raw)
  To: Stephane Grosjean; +Cc: Alan Stern, linux-can Mailing List

On 19.01.2012 16:07, Alan Stern wrote:

> On Thu, 19 Jan 2012, Stephane Grosjean wrote:

>> +static int pcan_usb_send_cmd(struct peak_usb_device *dev, u8 f, u8 n, u8 *p)


>> +	struct pcan_usb_parameter cmd = {
>> +		.function = f,
>> +		.number = n,
>> +	};
>> +
> 
> Here, and in several other places in this patch series, you submit
> requests using a transfer buffer that is allocated on the stack.  This
> is forbidden because not all platforms can do DMA to stack or static
> addresses.  The transfer buffers should be allocated dynamically.
> 
> Alan Stern


Hello Stephane,

don't know if it could be related to that remark but i get sporadic tx stalls
when sending at full speed. The RX is working then but to send again, i need
to perform a ifconfig down/up sequence.

When using

	ip link set can0 type can restart-ms 100

with the adapter then the system entirely crashes after a while.

As this may be related to the 3.3rc1 kernel and therefore i'll do some tests
with a 3.2.1 today to.

My testbed (all sja1000 based controllers) connected to one bus:

can0: EMS PCMCIA channel 0
can1: EMS PCMCIA channel 1
can2: PEAK USB

cangen -i -g0 can0
cangen -i -g0 can2

As the traffic has random IDs there could emerge the same tx ID (which is
against CAN spec). At a certain point the PCAN USB creates some error-passive
errors and finally increases a bus-off state. But it seam to not heal the tx
path from this state due to the valid traffic from can0 -> can1.

And when configured "restart-ms 100" it kills the machine then.

Btw.

cangen -i -g0 can0 -I 400
cangen -i -g0 can2 -I 600

and

cangen -i -g0 can0 -I 500
cangen -i -g0 can2 -I 500

(!!!)

both run without problems for a long time.

Best regards,
Oliver

ps. I reduced the number of lists to linux-can for the report.

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

* Re: [PATCH 0/3 V3.1] can/usb: Add PEAK-System PCAN USB adapters driver
  2012-01-19 19:08 ` [PATCH 0/3 V3.1] can/usb: Add PEAK-System PCAN USB adapters driver Oliver Hartkopp
@ 2012-01-20  9:28   ` Wolfgang Grandegger
  0 siblings, 0 replies; 15+ messages in thread
From: Wolfgang Grandegger @ 2012-01-20  9:28 UTC (permalink / raw)
  To: Oliver Hartkopp
  Cc: Stephane Grosjean, linux-usb Mailing List, netdev Mailing List,
	linux-can Mailing List

On 01/19/2012 08:08 PM, Oliver Hartkopp wrote:
> On 19.01.2012 11:27, Stephane Grosjean wrote:
> 
>> That serie of patches adds the peak_usb driver which handles the PCAN-USB
>> and PCAN-USB Pro adapters from PEAK-System (http://www.peak-system.com).
>> These patches are planned for kernel v3.02.00 and above.
> 
> 
> Hello Stephane,
> 
> i tried your patch with Linus' current tree (3.3 merge).
> 
> Plugging-in the PEAK USB adapter it turns out to be *very* informative in
> the kernel log and also presents information twice:
> 
> [  153.566193] usb 2-1.2: new full-speed USB device number 6 using ehci_hcd
> [  153.663659] usb 2-1.2: New USB device found, idVendor=0c72, idProduct=000c
> [  153.663665] usb 2-1.2: New USB device strings: Mfr=0, Product=3, SerialNumber=0
> [  153.663669] usb 2-1.2: Product: VER1:PEAK    
> [  153.663671] VER2:02.8.01        
> [  153.663672] DAT :06.05.2004
> [  153.663672] TIME:09:35:37
> [  153.663673]                              ...
> [  153.682683] peak_usb: PCAN-USB interfaces driver loaded
> [  153.682736] peak_usb 2-1.2:1.0: new PEAK-System usb adapter with 1 can detected:
> [  153.682741] peak_usb 2-1.2:1.0: PEAK-System PCAN-USB
> [  153.682746] peak_usb 2-1.2:1.0: VER1:PEAK    .VER2:02.8.01        .DAT :06.05.2004.TIME:09:35:37.                             ...
> [  153.685422] peak_usb 2-1.2:1.0: serial FFFFFFFF hwrev 28
> [  153.686413] peak_usb 2-1.2:1.0: can2 attached to PCAN-USB can 0 (device 46)
> [  153.686521] usbcore: registered new interface driver peak_usb

I already realized that the driver produces a lot of output... that's
definitely too much.


> As i assume that this VER2/DAT/TIME stuff and also the "..." line comes
> somewhere from the device itself and is printed by the USB stack, i wonder if
> we could reduce the information provided by the peak_usb driver then.
> 
> E.g.
> 
> [  153.682683] peak_usb: PCAN-USB interfaces driver loaded
> # is obsolete as peak_usb speaks to us in the following lines
> 
> [  153.682736] peak_usb 2-1.2:1.0: new PEAK-System usb adapter with 1 can detected:
> [  153.682741] peak_usb 2-1.2:1.0: PEAK-System PCAN-USB
> # Can be merged, e.g. to
> [  153.682741] peak_usb 2-1.2:1.0: PEAK-System PCAN-USB adapter hwrev 28 (one channel)
> 
> 
> [  153.682746] peak_usb 2-1.2:1.0: VER1:PEAK    .VER2:02.8.01        .DAT :06.05.2004.TIME:09:35:37.                             ...
> # If you think, this is still needed(???) -> at least kill the spaces like this:
> [  153.682746] peak_usb 2-1.2:1.0: VER1:PEAK.VER2:02.8.01.DAT:06.05.2004.TIME:09:35:37....

If it's valuable information, we could also use dev_dbg().

> [  153.685422] peak_usb 2-1.2:1.0: serial FFFFFFFF hwrev 28
> [  153.686413] peak_usb 2-1.2:1.0: can2 attached to PCAN-USB can 0 (device 46)
> # Can be merged, e.g. to
> [  153.686413] peak_usb 2-1.2:1.0: PCAN-USB channel 0 (serial FFFFFFFF, device 46) attached to can2
> 
> So it would finally look like this (6 -> 2 lines):
> 
> [  153.682741] peak_usb 2-1.2:1.0: PEAK-System PCAN-USB adapter hwrev 28 (one channel)
> [  153.686413] peak_usb 2-1.2:1.0: PCAN-USB channel 0 (serial FFFFFFFF, device 46) attached to can2
> 
> I'm not sure, if the serial number was identical when having two channels like in the USB-Pro.
> If so the serial number in general could be moved up like this:
> 
> [  153.682741] peak_usb 2-1.2:1.0: PEAK-System PCAN-USB adapter hwrev 28 serial FFFFFFFF (one channel)
> [  153.686413] peak_usb 2-1.2:1.0: PCAN-USB channel 0 (device 46) attached to can2

Just 2 lines, that would be prefect. Please also check the output of the
PCAN-USB-Pro device and try to reduce it to 2+- lines as well.

Wolfgang.


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

* Re: [PATCH 2/3 V3.1] can/usb: PEAK-System Technik PCAN-USB specific part
  2012-01-20  7:52     ` Oliver Hartkopp
@ 2012-01-20 12:28       ` Oliver Hartkopp
  2012-01-20 12:34         ` Wolfgang Grandegger
  0 siblings, 1 reply; 15+ messages in thread
From: Oliver Hartkopp @ 2012-01-20 12:28 UTC (permalink / raw)
  To: Wolfgang Grandegger, Stephane Grosjean; +Cc: Alan Stern, linux-can Mailing List

Hi Stephane,

it also fails on 3.2.1 :-(

When the TX path get stuck the ip tool says:

root@box:/home/user# ip -details -statistics link show can2
15: can2: <NO-CARRIER,NOARP,UP,ECHO> mtu 16 qdisc pfifo_fast state DOWN qlen 10
     link/can
     can state ERROR-ACTIVE restart-ms 0
     bitrate 500000 sample-point 0.875
     tq 125 prop-seg 6 phase-seg1 7 phase-seg2 2 sjw 1
     pcan_usb: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
     clock 8000000
     re-started bus-errors arbit-lost error-warn error-pass bus-off
     0          0          0          4          351        2
     RX: bytes  packets  errors  dropped overrun mcast
     11642427   2023785  3       0       3       0
     TX: bytes  packets  errors  dropped carrier collsns
     5765840    1002590  0       0       0       0


When trying to restart the PCAN-USB adapter i got this:

root@box:/home/user# ip link set can2 type can restart
RTNETLINK answers: Device or resource busy

Checking for error msgs then:

user@box:~$ candump can2,0~0,#FFFFFFFF -e
   can2  20000004  [8] 00 30 00 00 00 00 00 00   ERRORFRAME
	controller-problem{rx-error-passive,tx-error-passive}
   can2  20000004  [8] 00 30 00 00 00 00 00 00   ERRORFRAME
	controller-problem{rx-error-passive,tx-error-passive}
   can2  20000004  [8] 00 30 00 00 00 00 00 00   ERRORFRAME
	controller-problem{rx-error-passive,tx-error-passive}
   can2  20000004  [8] 00 30 00 00 00 00 00 00   ERRORFRAME
	controller-problem{rx-error-passive,tx-error-passive}
(..)
(every second a new msg)

While RX of real CAN traffic is still possible ...

ifconfig can2 down/up makes it work again.

@Wolfgang: I always get "RTNETLINK answers: Device or resource busy" when the 
device is up - even on the EMS PCMCIA card. Should the manual restart 
generally be also possible even when the device is up??
I think the automatic restart (with restart-ms) has no preconditions to the 
device up/down state.

Regards,
Oliver


On 20.01.2012 08:52, Oliver Hartkopp wrote:
> On 19.01.2012 16:07, Alan Stern wrote:
>
>> On Thu, 19 Jan 2012, Stephane Grosjean wrote:
>
>>> +static int pcan_usb_send_cmd(struct peak_usb_device *dev, u8 f, u8 n, u8 *p)
>
>
>>> +	struct pcan_usb_parameter cmd = {
>>> +		.function = f,
>>> +		.number = n,
>>> +	};
>>> +
>>
>> Here, and in several other places in this patch series, you submit
>> requests using a transfer buffer that is allocated on the stack.  This
>> is forbidden because not all platforms can do DMA to stack or static
>> addresses.  The transfer buffers should be allocated dynamically.
>>
>> Alan Stern
>
>
> Hello Stephane,
>
> don't know if it could be related to that remark but i get sporadic tx stalls
> when sending at full speed. The RX is working then but to send again, i need
> to perform a ifconfig down/up sequence.
>
> When using
>
> 	ip link set can0 type can restart-ms 100
>
> with the adapter then the system entirely crashes after a while.
>
> As this may be related to the 3.3rc1 kernel and therefore i'll do some tests
> with a 3.2.1 today to.
>
> My testbed (all sja1000 based controllers) connected to one bus:
>
> can0: EMS PCMCIA channel 0
> can1: EMS PCMCIA channel 1
> can2: PEAK USB
>
> cangen -i -g0 can0
> cangen -i -g0 can2
>
> As the traffic has random IDs there could emerge the same tx ID (which is
> against CAN spec). At a certain point the PCAN USB creates some error-passive
> errors and finally increases a bus-off state. But it seam to not heal the tx
> path from this state due to the valid traffic from can0 ->  can1.
>
> And when configured "restart-ms 100" it kills the machine then.
>
> Btw.
>
> cangen -i -g0 can0 -I 400
> cangen -i -g0 can2 -I 600
>
> and
>
> cangen -i -g0 can0 -I 500
> cangen -i -g0 can2 -I 500
>
> (!!!)
>
> both run without problems for a long time.
>
> Best regards,
> Oliver
>
> ps. I reduced the number of lists to linux-can for the report.
> --
> To unsubscribe from this list: send the line "unsubscribe linux-can" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


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

* Re: [PATCH 2/3 V3.1] can/usb: PEAK-System Technik PCAN-USB specific part
  2012-01-20 12:28       ` Oliver Hartkopp
@ 2012-01-20 12:34         ` Wolfgang Grandegger
  2012-01-20 12:36           ` Wolfgang Grandegger
  0 siblings, 1 reply; 15+ messages in thread
From: Wolfgang Grandegger @ 2012-01-20 12:34 UTC (permalink / raw)
  To: Oliver Hartkopp; +Cc: Stephane Grosjean, Alan Stern, linux-can Mailing List

Hi Oliver,

On 01/20/2012 01:28 PM, Oliver Hartkopp wrote:

> @Wolfgang: I always get "RTNETLINK answers: Device or resource busy"
> when the device is up - even on the EMS PCMCIA card. Should the manual
> restart generally be also possible even when the device is up??

The restart is only for bus-off recovery. It will *not* restart if you
are not in bus-off.

> I think the automatic restart (with restart-ms) has no preconditions to
> the device up/down state.

But it only triggers in case of bus-off. It has nothing to do with up/down.

Wolfgang.

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

* Re: [PATCH 2/3 V3.1] can/usb: PEAK-System Technik PCAN-USB specific part
  2012-01-20 12:34         ` Wolfgang Grandegger
@ 2012-01-20 12:36           ` Wolfgang Grandegger
  2012-01-20 13:37             ` Oliver Hartkopp
  0 siblings, 1 reply; 15+ messages in thread
From: Wolfgang Grandegger @ 2012-01-20 12:36 UTC (permalink / raw)
  To: Oliver Hartkopp; +Cc: Stephane Grosjean, Alan Stern, linux-can Mailing List

On 01/20/2012 01:34 PM, Wolfgang Grandegger wrote:
> Hi Oliver,
> 
> On 01/20/2012 01:28 PM, Oliver Hartkopp wrote:
> 
>> @Wolfgang: I always get "RTNETLINK answers: Device or resource busy"
>> when the device is up - even on the EMS PCMCIA card. Should the manual
>> restart generally be also possible even when the device is up??
> 
> The restart is only for bus-off recovery. It will *not* restart if you
> are not in bus-off.
> 
>> I think the automatic restart (with restart-ms) has no preconditions to
>> the device up/down state.
> 
> But it only triggers in case of bus-off. It has nothing to do with up/down.

If you are at it, please also test the bus-off and bus-off recovery...
Thanks...

Wolfgang.


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

* Re: [PATCH 2/3 V3.1] can/usb: PEAK-System Technik PCAN-USB specific part
  2012-01-20 12:36           ` Wolfgang Grandegger
@ 2012-01-20 13:37             ` Oliver Hartkopp
  2012-01-20 13:52               ` Wolfgang Grandegger
  0 siblings, 1 reply; 15+ messages in thread
From: Oliver Hartkopp @ 2012-01-20 13:37 UTC (permalink / raw)
  To: Wolfgang Grandegger, Stephane Grosjean; +Cc: linux-can Mailing List

On 20.01.2012 13:36, Wolfgang Grandegger wrote:
> On 01/20/2012 01:34 PM, Wolfgang Grandegger wrote:
>> Hi Oliver,
>>
>> On 01/20/2012 01:28 PM, Oliver Hartkopp wrote:
>>
>>> @Wolfgang: I always get "RTNETLINK answers: Device or resource busy"
>>> when the device is up - even on the EMS PCMCIA card. Should the manual
>>> restart generally be also possible even when the device is up??
>>
>> The restart is only for bus-off recovery. It will *not* restart if you
>> are not in bus-off.

Ah, ok. Reading the *open* source helps :-))

>>> I think the automatic restart (with restart-ms) has no preconditions to
>>> the device up/down state.
>>
>> But it only triggers in case of bus-off. It has nothing to do with up/down.
> 
> If you are at it, please also test the bus-off and bus-off recovery...
> Thanks...

Yes. Good point.

I set the PCAN-USB adapter (can2) to 100000 bit/s and let the two other
nodes at 500kbit/s.

With low traffic at 500kbit and sending one frame from the PCAN-USB 
(cansend can2 123#00) the bus-off is easy to reach on can2.

But the effect is:

I get every second(!) an error msg with

  can2  20000044  [8] 00 30 00 00 00 00 00 00   ERRORFRAME
    controller-problem{rx-error-passive,tx-error-passive}
    bus-off

But the state displayed in

ip -details -statistics link show can2 

always says 

15: can2: <NO-CARRIER,NOARP,UP,ECHO> mtu 16 qdisc pfifo_fast state DOWN qlen 10
    link/can
    can state ERROR-ACTIVE restart-ms 0 

... with increasing bus-off counter every second.

After setting the two other nodes at 100kbit/s too and do some traffic there, the
state of the PCAN-USB should get back to work - but is does not.

The error msgs every second remain from can2.

Regards,
Oliver

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

* Re: [PATCH 2/3 V3.1] can/usb: PEAK-System Technik PCAN-USB specific part
  2012-01-20 13:37             ` Oliver Hartkopp
@ 2012-01-20 13:52               ` Wolfgang Grandegger
  2012-01-20 14:53                 ` Stephane Grosjean
  0 siblings, 1 reply; 15+ messages in thread
From: Wolfgang Grandegger @ 2012-01-20 13:52 UTC (permalink / raw)
  To: Oliver Hartkopp; +Cc: Stephane Grosjean, linux-can Mailing List

On 01/20/2012 02:37 PM, Oliver Hartkopp wrote:
> On 20.01.2012 13:36, Wolfgang Grandegger wrote:
>> On 01/20/2012 01:34 PM, Wolfgang Grandegger wrote:
>>> Hi Oliver,
>>>
>>> On 01/20/2012 01:28 PM, Oliver Hartkopp wrote:
>>>
>>>> @Wolfgang: I always get "RTNETLINK answers: Device or resource busy"
>>>> when the device is up - even on the EMS PCMCIA card. Should the manual
>>>> restart generally be also possible even when the device is up??
>>>
>>> The restart is only for bus-off recovery. It will *not* restart if you
>>> are not in bus-off.
> 
> Ah, ok. Reading the *open* source helps :-))
> 
>>>> I think the automatic restart (with restart-ms) has no preconditions to
>>>> the device up/down state.
>>>
>>> But it only triggers in case of bus-off. It has nothing to do with up/down.
>>
>> If you are at it, please also test the bus-off and bus-off recovery...
>> Thanks...
> 
> Yes. Good point.
> 
> I set the PCAN-USB adapter (can2) to 100000 bit/s and let the two other
> nodes at 500kbit/s.
> 
> With low traffic at 500kbit and sending one frame from the PCAN-USB 
> (cansend can2 123#00) the bus-off is easy to reach on can2.
> 
> But the effect is:
> 
> I get every second(!) an error msg with
> 
>   can2  20000044  [8] 00 30 00 00 00 00 00 00   ERRORFRAME
>     controller-problem{rx-error-passive,tx-error-passive}
>     bus-off
> 
> But the state displayed in
> 
> ip -details -statistics link show can2 
> 
> always says 
> 
> 15: can2: <NO-CARRIER,NOARP,UP,ECHO> mtu 16 qdisc pfifo_fast state DOWN qlen 10
>     link/can
>     can state ERROR-ACTIVE restart-ms 0 
> 
> ... with increasing bus-off counter every second.
> 
> After setting the two other nodes at 100kbit/s too and do some traffic there, the
> state of the PCAN-USB should get back to work - but is does not.
> 
> The error msgs every second remain from can2.

The CAN error state is *not* handled at all in the driver :(. Obviously
I missed that. Stephane, could you please check error state changes to
error passive (no calble) and bus-off (short-circuit) and bus-off
recovery. "ip -d -s link show can0" and "candump any,0:0,#FFFFFFFF -e"
will give you useful information. Or even better, show use the output.

Thanks,

Wolfgang.

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

* Re: [PATCH 2/3 V3.1] can/usb: PEAK-System Technik PCAN-USB specific part
  2012-01-20 13:52               ` Wolfgang Grandegger
@ 2012-01-20 14:53                 ` Stephane Grosjean
  2012-01-20 17:32                   ` Wolfgang Grandegger
  0 siblings, 1 reply; 15+ messages in thread
From: Stephane Grosjean @ 2012-01-20 14:53 UTC (permalink / raw)
  To: Wolfgang Grandegger; +Cc: Oliver Hartkopp, linux-can Mailing List, stern

Hi,

Found time to answer to all of you:

- first, thanks for your review and tests
- next, I did a new version of the driver which fixes Alan's issue about 
the variables in the stack, and which is *very* much less verbose too.
- the driver itself sets now the can state according to some usb adapter 
events

When unplugging the can cable, candump displays now:

   can4  20000004  [8] 00 30 00 00 00 00 00 00   ERRORFRAME
         controller-problem{rx-error-passive,tx-error-passive}
   can4  20000004  [8] 00 30 00 00 00 00 00 00   ERRORFRAME
         controller-problem{rx-error-passive,tx-error-passive}
   can4  20000004  [8] 00 30 00 00 00 00 00 00   ERRORFRAME
         controller-problem{rx-error-passive,tx-error-passive}
   can4  20000004  [8] 00 30 00 00 00 00 00 00   ERRORFRAME
         controller-problem{rx-error-passive,tx-error-passive}
...

I'm looking now to do the short circuit to check for bus-off manual 
restart. I also confirm that auto restart hangs the kernel.

Thanks and regards,

Stéphane


Le 20/01/2012 14:52, Wolfgang Grandegger a écrit :
> On 01/20/2012 02:37 PM, Oliver Hartkopp wrote:
>> On 20.01.2012 13:36, Wolfgang Grandegger wrote:
>>> On 01/20/2012 01:34 PM, Wolfgang Grandegger wrote:
>>>> Hi Oliver,
>>>>
>>>> On 01/20/2012 01:28 PM, Oliver Hartkopp wrote:
>>>>
>>>>> @Wolfgang: I always get "RTNETLINK answers: Device or resource busy"
>>>>> when the device is up - even on the EMS PCMCIA card. Should the manual
>>>>> restart generally be also possible even when the device is up??
>>>> The restart is only for bus-off recovery. It will *not* restart if you
>>>> are not in bus-off.
>> Ah, ok. Reading the *open* source helps :-))
>>
>>>>> I think the automatic restart (with restart-ms) has no preconditions to
>>>>> the device up/down state.
>>>> But it only triggers in case of bus-off. It has nothing to do with up/down.
>>> If you are at it, please also test the bus-off and bus-off recovery...
>>> Thanks...
>> Yes. Good point.
>>
>> I set the PCAN-USB adapter (can2) to 100000 bit/s and let the two other
>> nodes at 500kbit/s.
>>
>> With low traffic at 500kbit and sending one frame from the PCAN-USB
>> (cansend can2 123#00) the bus-off is easy to reach on can2.
>>
>> But the effect is:
>>
>> I get every second(!) an error msg with
>>
>>    can2  20000044  [8] 00 30 00 00 00 00 00 00   ERRORFRAME
>>      controller-problem{rx-error-passive,tx-error-passive}
>>      bus-off
>>
>> But the state displayed in
>>
>> ip -details -statistics link show can2
>>
>> always says
>>
>> 15: can2:<NO-CARRIER,NOARP,UP,ECHO>  mtu 16 qdisc pfifo_fast state DOWN qlen 10
>>      link/can
>>      can state ERROR-ACTIVE restart-ms 0
>>
>> ... with increasing bus-off counter every second.
>>
>> After setting the two other nodes at 100kbit/s too and do some traffic there, the
>> state of the PCAN-USB should get back to work - but is does not.
>>
>> The error msgs every second remain from can2.
> The CAN error state is *not* handled at all in the driver :(. Obviously
> I missed that. Stephane, could you please check error state changes to
> error passive (no calble) and bus-off (short-circuit) and bus-off
> recovery. "ip -d -s link show can0" and "candump any,0:0,#FFFFFFFF -e"
> will give you useful information. Or even better, show use the output.
>
> Thanks,
>
> Wolfgang.
--
PEAK-System Technik GmbH, Otto-Roehm-Strasse 69, D-64293 Darmstadt 
Geschaeftsleitung: A.Gach/U.Wilhelm,St.Nr.:007/241/13586 FA Darmstadt 
HRB-9183 Darmstadt, Ust.IdNr.:DE 202220078, WEE-Reg.-Nr.: DE39305391 
Tel.+49 (0)6151-817320 / Fax:+49 (0)6151-817329, info@peak-system.com

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

* Re: [PATCH 2/3 V3.1] can/usb: PEAK-System Technik PCAN-USB specific part
  2012-01-20 14:53                 ` Stephane Grosjean
@ 2012-01-20 17:32                   ` Wolfgang Grandegger
  0 siblings, 0 replies; 15+ messages in thread
From: Wolfgang Grandegger @ 2012-01-20 17:32 UTC (permalink / raw)
  To: s.grosjean; +Cc: Oliver Hartkopp, linux-can Mailing List, stern

Hi Stephane,

On 01/20/2012 03:53 PM, Stephane Grosjean wrote:
> Hi,
> 
> Found time to answer to all of you:
> 
> - first, thanks for your review and tests
> - next, I did a new version of the driver which fixes Alan's issue about
> the variables in the stack, and which is *very* much less verbose too.
> - the driver itself sets now the can state according to some usb adapter
> events
> 
> When unplugging the can cable, candump displays now:
> 
>   can4  20000004  [8] 00 30 00 00 00 00 00 00   ERRORFRAME
>         controller-problem{rx-error-passive,tx-error-passive}
>   can4  20000004  [8] 00 30 00 00 00 00 00 00   ERRORFRAME
>         controller-problem{rx-error-passive,tx-error-passive}
>   can4  20000004  [8] 00 30 00 00 00 00 00 00   ERRORFRAME
>         controller-problem{rx-error-passive,tx-error-passive}
>   can4  20000004  [8] 00 30 00 00 00 00 00 00   ERRORFRAME
>         controller-problem{rx-error-passive,tx-error-passive}
> ...

Does the device report state change to error *warning* as well? Also,
the state change to error passive should only be reported *once*. Well,
it's state change. I don't known what the hardware is actually
reporting. If it does report the current state periodically, you need to
handle it accordingly.
BTW: are the error counters also availabe/readable?

Wolfgang.

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

end of thread, other threads:[~2012-01-20 17:32 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-01-19 10:27 [PATCH 0/3 V3.1] can/usb: Add PEAK-System PCAN USB adapters driver Stephane Grosjean
     [not found] ` <1326968863-20229-1-git-send-email-s.grosjean-g4cQ8AsIbFbL9ATBNaCtXw@public.gmane.org>
2012-01-19 10:27   ` [PATCH 1/3 V3.1] can/usb: PEAK-System Technik USB adapters driver core Stephane Grosjean
2012-01-19 10:27   ` [PATCH 3/3 V3.1] can/usb: PEAK-System Technik PCAN-USB Pro specific part Stephane Grosjean
2012-01-19 10:27 ` [PATCH 2/3 V3.1] can/usb: PEAK-System Technik PCAN-USB " Stephane Grosjean
2012-01-19 15:07   ` Alan Stern
2012-01-20  7:52     ` Oliver Hartkopp
2012-01-20 12:28       ` Oliver Hartkopp
2012-01-20 12:34         ` Wolfgang Grandegger
2012-01-20 12:36           ` Wolfgang Grandegger
2012-01-20 13:37             ` Oliver Hartkopp
2012-01-20 13:52               ` Wolfgang Grandegger
2012-01-20 14:53                 ` Stephane Grosjean
2012-01-20 17:32                   ` Wolfgang Grandegger
2012-01-19 19:08 ` [PATCH 0/3 V3.1] can/usb: Add PEAK-System PCAN USB adapters driver Oliver Hartkopp
2012-01-20  9:28   ` Wolfgang Grandegger

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.