All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4] can/peak_usb: add support for PEAK new CANFD USB adapters
@ 2015-01-06 10:00 Stephane Grosjean
  2015-01-07 17:03 ` Marc Kleine-Budde
  2015-01-14 12:05 ` Fwd: " Stephane Grosjean
  0 siblings, 2 replies; 18+ messages in thread
From: Stephane Grosjean @ 2015-01-06 10:00 UTC (permalink / raw)
  To: linux-can; +Cc: Oliver Hartkopp, Stephane Grosjean

Add support for the following new PEAK-System technik CANFD USB adapters:

PCAN-USB FD             single CANFD channel USB adapter
PCAN-USB Pro FD         dual CANFD channels USB adapter

Signed-off-by: Stephane Grosjean <s.grosjean@peak-system.com>
---
v4: pcan_usb_fd.c: fix missing code for setting standard filtering criteria on
    received CANID, in function pcan_usb_fd_set_filter_std().
    Note: driver behavior is to accept all CANID.

 drivers/net/can/usb/Kconfig                  |   14 +-
 drivers/net/can/usb/peak_usb/Makefile        |    2 +-
 drivers/net/can/usb/peak_usb/pcan_ucan.h     |  192 +++++
 drivers/net/can/usb/peak_usb/pcan_usb.c      |    1 +
 drivers/net/can/usb/peak_usb/pcan_usb_core.c |   66 +-
 drivers/net/can/usb/peak_usb/pcan_usb_core.h |   14 +-
 drivers/net/can/usb/peak_usb/pcan_usb_fd.c   | 1070 ++++++++++++++++++++++++++
 drivers/net/can/usb/peak_usb/pcan_usb_pro.c  |   17 +-
 drivers/net/can/usb/peak_usb/pcan_usb_pro.h  |   14 +
 9 files changed, 1362 insertions(+), 28 deletions(-)
 create mode 100644 drivers/net/can/usb/peak_usb/pcan_ucan.h
 create mode 100644 drivers/net/can/usb/peak_usb/pcan_usb_fd.c

diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig
index a77db919..6dbbc85 100644
--- a/drivers/net/can/usb/Kconfig
+++ b/drivers/net/can/usb/Kconfig
@@ -53,10 +53,18 @@ config CAN_KVASER_USB
 	  module will be called kvaser_usb.
 
 config CAN_PEAK_USB
-	tristate "PEAK PCAN-USB/USB Pro interfaces"
+	tristate "PEAK PCAN-USB/USB Pro interfaces for CAN 2.0b/CAN-FD"
 	---help---
-	  This driver supports the PCAN-USB and PCAN-USB Pro adapters
-	  from PEAK-System Technik (http://www.peak-system.com).
+	  This driver supports the PEAK-System Technik USB adapters that enable
+	  access to the CAN bus, with repect to the CAN 2.0b and/or CAN-FD
+	  standards, that is:
+
+	  PCAN-USB             single CAN 2.0b channel USB adapter
+	  PCAN-USB Pro         dual CAN 2.0b channels USB adapter
+	  PCAN-USB FD          single CAN-FD channel USB adapter
+	  PCAN-USB Pro FD      dual CAN-FD channels USB adapter
+
+	  (see also http://www.peak-system.com).
 
 config CAN_8DEV_USB
 	tristate "8 devices USB2CAN interface"
diff --git a/drivers/net/can/usb/peak_usb/Makefile b/drivers/net/can/usb/peak_usb/Makefile
index 1aefbc8..1839e9c 100644
--- a/drivers/net/can/usb/peak_usb/Makefile
+++ b/drivers/net/can/usb/peak_usb/Makefile
@@ -1,2 +1,2 @@
 obj-$(CONFIG_CAN_PEAK_USB) += peak_usb.o
-peak_usb-y = pcan_usb_core.o pcan_usb.o pcan_usb_pro.o
+peak_usb-y = pcan_usb_core.o pcan_usb.o pcan_usb_pro.o pcan_usb_fd.o
diff --git a/drivers/net/can/usb/peak_usb/pcan_ucan.h b/drivers/net/can/usb/peak_usb/pcan_ucan.h
new file mode 100644
index 0000000..02c0424
--- /dev/null
+++ b/drivers/net/can/usb/peak_usb/pcan_ucan.h
@@ -0,0 +1,192 @@
+/*
+ * CAN driver for PEAK System micro-CAN based adapters
+ *
+ * Copyright (C) 2003-2011 PEAK System-Technik GmbH
+ * Copyright (C) 2011-2013 Stephane Grosjean <s.grosjean@peak-system.com>
+ *
+ * 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 PUCAN_H
+#define PUCAN_H
+
+/* uCAN commands opcodes list (low-order 10 bits) */
+#define PUCAN_CMD_NOP			0x000
+#define PUCAN_CMD_RESET_MODE		0x001
+#define PUCAN_CMD_NORMAL_MODE		0x002
+#define PUCAN_CMD_LISTEN_ONLY_MODE	0x003
+#define PUCAN_CMD_TIMING_SLOW		0x004
+#define PUCAN_CMD_TIMING_FAST		0x005
+#define PUCAN_CMD_FILTER_STD		0x008
+#define PUCAN_CMD_TX_ABORT		0x009
+#define PUCAN_CMD_WR_ERR_CNT		0x00a
+#define PUCAN_CMD_RX_FRAME_ENABLE	0x00b
+#define PUCAN_CMD_RX_FRAME_DISABLE	0x00c
+#define PUCAN_CMD_END_OF_COLLECTION	0x3ff
+
+/* uCAN received messages list */
+#define PUCAN_MSG_CAN_RX		0x0001
+#define PUCAN_MSG_ERROR			0x0002
+#define PUCAN_MSG_STATUS		0x0003
+#define PUCAN_MSG_BUSLOAD		0x0004
+#define PUCAN_MSG_CAN_TX		0x1000
+
+/* uCAN command common header */
+#define PUCAN_CMD_OPCODE_CHANNEL(c, o)	(((c) << 12) | ((o) & 0x3ff))
+
+struct __packed pucan_command {
+	__le16	opcode_channel;
+	u16	args[3];
+};
+
+/* uCAN TIMING_SLOW command fields */
+#define PUCAN_TSLOW_SJW_T(s, t)		(((s) & 0xf) | ((!!(t)) << 7))
+#define PUCAN_TSLOW_TSEG2(t)		((t) & 0xf)
+#define PUCAN_TSLOW_TSEG1(t)		((t) & 0x3f)
+#define PUCAN_TSLOW_BRP(b)		((b) & 0x3ff)
+
+struct __packed pucan_timing_slow {
+	__le16	opcode_channel;
+
+	u8	ewl;		/* Error Warning limit */
+	u8	sjw_t;		/* Sync Jump Width + Triple sampling */
+	u8	tseg2;		/* Timing SEGment 2 */
+	u8	tseg1;		/* Timing SEGment 1 */
+
+	__le16	brp;		/* BaudRate Prescaler */
+};
+
+/* uCAN TIMING_FAST command fields */
+#define PUCAN_TFAST_SJW(s)		((s) & 0x3)
+#define PUCAN_TFAST_TSEG2(t)		((t) & 0x7)
+#define PUCAN_TFAST_TSEG1(t)		((t) & 0xf)
+#define PUCAN_TFAST_BRP(b)		((b) & 0x3ff)
+
+struct __packed pucan_timing_fast {
+	__le16	opcode_channel;
+
+	u8	unused;
+	u8	sjw;		/* Sync Jump Width */
+	u8	tseg2;		/* Timing SEGment 2 */
+	u8	tseg1;		/* Timing SEGment 1 */
+
+	__le16	brp;		/* BaudRate Prescaler */
+};
+
+/* uCAN FILTER_STD command fields */
+#define PUCAN_FLTSTD_ROW_IDX_BITS	6
+
+struct __packed pucan_filter_std {
+	__le16	opcode_channel;
+
+	__le16	idx;
+	__le32	mask;		/* CAN-ID bitmask in idx range */
+};
+
+/* uCAN RX_FRAME_ENABLE command fields */
+#define PUCAN_FLTEXT_ERROR		0x0001
+#define PUCAN_FLTEXT_BUSLOAD		0x0002
+
+struct __packed pucan_filter_ext {
+	__le16	opcode_channel;
+
+	__le16	ext_mask;
+	u32	unused;
+};
+
+/* uCAN received messages global format */
+struct __packed pucan_msg {
+	__le16	size;
+	__le16	type;
+	__le32	ts_low;
+	__le32	ts_high;
+};
+
+/* uCAN flags for CAN/CANFD messages */
+#define PUCAN_MSG_SELF_RECEIVE		0x80
+#define PUCAN_MSG_ERROR_STATE_IND	0x40	/* error state indicator */
+#define PUCAN_MSG_BITRATE_SWITCH	0x20	/* bitrate switch */
+#define PUCAN_MSG_EXT_DATA_LEN		0x10	/* extended data length */
+#define PUCAN_MSG_SINGLE_SHOT		0x08
+#define PUCAN_MSG_LOOPED_BACK		0x04
+#define PUCAN_MSG_EXT_ID		0x02
+#define PUCAN_MSG_RTR			0x01
+
+#define PUCAN_MSG_CHANNEL(m)		((m)->channel_dlc & 0xf)
+#define PUCAN_MSG_DLC(m)		((m)->channel_dlc >> 4)
+
+struct __packed pucan_rx_msg {
+	__le16	size;
+	__le16	type;
+	__le32	ts_low;
+	__le32	ts_high;
+	__le32	tag_low;
+	__le32	tag_high;
+	u8	channel_dlc;
+	u8	client;
+	__le16	flags;
+	__le32	can_id;
+	u8	d[0];
+};
+
+/* uCAN error types */
+#define PUCAN_ERMSG_BIT_ERROR		0
+#define PUCAN_ERMSG_FORM_ERROR		1
+#define PUCAN_ERMSG_STUFF_ERROR		2
+#define PUCAN_ERMSG_OTHER_ERROR		3
+#define PUCAN_ERMSG_ERR_CNT_DEC		4
+
+#define PUCAN_ERMSG_CHANNEL(e)		((e)->channel_type_d & 0x0f)
+#define PUCAN_ERMSG_ERRTYPE(e)		(((e)->channel_type_d >> 4) & 0x07)
+#define PUCAN_ERMSG_D(e)		((e)->channel_type_d & 0x80)
+
+#define PUCAN_ERMSG_ERRCODE(e)		((e)->code_g & 0x7f)
+#define PUCAN_ERMSG_G(e)		((e)->code_g & 0x80)
+
+struct __packed pucan_error_msg {
+	__le16	size;
+	__le16	type;
+	__le32	ts_low;
+	__le32	ts_high;
+	u8	channel_type_d;
+	u8	code_g;
+	u8	tx_err_cnt;
+	u8	rx_err_cnt;
+};
+
+#define PUCAN_STMSG_CHANNEL(e)		((e)->channel_p_w_b & 0x0f)
+#define PUCAN_STMSG_PASSIVE(e)		((e)->channel_p_w_b & 0x20)
+#define PUCAN_STMSG_WARNING(e)		((e)->channel_p_w_b & 0x40)
+#define PUCAN_STMSG_BUSOFF(e)		((e)->channel_p_w_b & 0x80)
+
+struct __packed pucan_status_msg {
+	__le16	size;
+	__le16	type;
+	__le32	ts_low;
+	__le32	ts_high;
+	u8	channel_p_w_b;
+	u8	unused[3];
+};
+
+/* uCAN transmitted message format */
+#define PUCAN_MSG_CHANNEL_DLC(c, d)	(((c) & 0xf) | ((d) << 4))
+
+struct __packed pucan_tx_msg {
+	__le16	size;
+	__le16	type;
+	__le32	tag_low;
+	__le32	tag_high;
+	u8	channel_dlc;
+	u8	client;
+	__le16	flags;
+	__le32	can_id;
+	u8	d[0];
+};
+
+#endif
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb.c b/drivers/net/can/usb/peak_usb/pcan_usb.c
index 4e1659d..5972c7a 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb.c
@@ -858,6 +858,7 @@ struct peak_usb_adapter pcan_usb = {
 	.name = "PCAN-USB",
 	.device_id = PCAN_USB_PRODUCT_ID,
 	.ctrl_count = 1,
+	.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY,
 	.clock = {
 		.freq = PCAN_USB_CRYSTAL_HZ / 2 ,
 	},
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
index c62f48a..7e8ebc4 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_core.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
@@ -37,6 +37,8 @@ MODULE_LICENSE("GPL v2");
 static struct usb_device_id peak_usb_table[] = {
 	{USB_DEVICE(PCAN_USB_VENDOR_ID, PCAN_USB_PRODUCT_ID)},
 	{USB_DEVICE(PCAN_USB_VENDOR_ID, PCAN_USBPRO_PRODUCT_ID)},
+	{USB_DEVICE(PCAN_USB_VENDOR_ID, PCAN_USBFD_PRODUCT_ID)},
+	{USB_DEVICE(PCAN_USB_VENDOR_ID, PCAN_USBPROFD_PRODUCT_ID)},
 	{} /* Terminating entry */
 };
 
@@ -46,6 +48,8 @@ MODULE_DEVICE_TABLE(usb, peak_usb_table);
 static struct peak_usb_adapter *peak_usb_adapters_list[] = {
 	&pcan_usb,
 	&pcan_usb_pro,
+	&pcan_usb_fd,
+	&pcan_usb_pro_fd,
 	NULL,
 };
 
@@ -165,6 +169,21 @@ void peak_usb_get_ts_tv(struct peak_time_ref *time_ref, u32 ts,
 }
 
 /*
+ * post received skb after having set any hw timestamp
+ */
+int peak_usb_netif_rx(struct sk_buff *skb,
+		      struct peak_time_ref *time_ref, u32 ts_low, u32 ts_high)
+{
+	struct skb_shared_hwtstamps *hwts = skb_hwtstamps(skb);
+	struct timeval tv;
+
+	peak_usb_get_ts_tv(time_ref, ts_low, &tv);
+	hwts->hwtstamp = timeval_to_ktime(tv);
+
+	return netif_rx(skb);
+}
+
+/*
  * callback for bulk Rx urb
  */
 static void peak_usb_read_bulk_callback(struct urb *urb)
@@ -253,7 +272,7 @@ static void peak_usb_write_bulk_callback(struct urb *urb)
 	case 0:
 		/* transmission complete */
 		netdev->stats.tx_packets++;
-		netdev->stats.tx_bytes += context->dlc;
+		netdev->stats.tx_bytes += context->data_len;
 
 		/* prevent tx timeout */
 		netdev->trans_start = jiffies;
@@ -289,7 +308,7 @@ static netdev_tx_t peak_usb_ndo_start_xmit(struct sk_buff *skb,
 	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 canfd_frame *cfd = (struct canfd_frame *)skb->data;
 	struct urb *urb;
 	u8 *obuf;
 	int i, err;
@@ -322,7 +341,9 @@ static netdev_tx_t peak_usb_ndo_start_xmit(struct sk_buff *skb,
 	}
 
 	context->echo_index = i;
-	context->dlc = cf->can_dlc;
+
+	/* Note: this works with CANFD frames too */
+	context->data_len = cfd->len;
 
 	usb_anchor_urb(urb, &dev->tx_submitted);
 
@@ -679,19 +700,43 @@ static int peak_usb_set_mode(struct net_device *netdev, enum can_mode mode)
 }
 
 /*
- * candev callback used to set device bitrate.
+ * candev callback used to set device nominal/arbitration bitrate.
  */
 static int peak_usb_set_bittiming(struct net_device *netdev)
 {
 	struct peak_usb_device *dev = netdev_priv(netdev);
-	struct can_bittiming *bt = &dev->can.bittiming;
+	struct peak_usb_adapter *pa = dev->adapter;
 
-	if (dev->adapter->dev_set_bittiming) {
-		int err = dev->adapter->dev_set_bittiming(dev, bt);
+	if (pa->dev_set_bittiming) {
+		struct can_bittiming *bt = &dev->can.bittiming;
+		int err = pa->dev_set_bittiming(dev, bt);
 
 		if (err)
 			netdev_info(netdev, "couldn't set bitrate (err %d)\n",
-				err);
+				    err);
+		return err;
+	}
+
+	return 0;
+}
+
+/*
+ * candev callback used to set device data bitrate.
+ */
+static int peak_usb_set_data_bittiming(struct net_device *netdev)
+{
+	struct peak_usb_device *dev = netdev_priv(netdev);
+	struct peak_usb_adapter *pa = dev->adapter;
+
+	if (pa->dev_set_data_bittiming) {
+		struct can_bittiming *bt = &dev->can.data_bittiming;
+		int err = pa->dev_set_data_bittiming(dev, bt);
+
+		if (err)
+			netdev_info(netdev,
+				    "couldn't set data bitrate (err %d)\n",
+				    err);
+
 		return err;
 	}
 
@@ -750,9 +795,10 @@ static int peak_usb_create_dev(struct peak_usb_adapter *peak_usb_adapter,
 	dev->can.clock = peak_usb_adapter->clock;
 	dev->can.bittiming_const = &peak_usb_adapter->bittiming_const;
 	dev->can.do_set_bittiming = peak_usb_set_bittiming;
+	dev->can.data_bittiming_const = &peak_usb_adapter->data_bittiming_const;
+	dev->can.do_set_data_bittiming = peak_usb_set_data_bittiming;
 	dev->can.do_set_mode = peak_usb_set_mode;
-	dev->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
-				      CAN_CTRLMODE_LISTENONLY;
+	dev->can.ctrlmode_supported = peak_usb_adapter->ctrlmode_supported;
 
 	netdev->netdev_ops = &peak_usb_netdev_ops;
 
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.h b/drivers/net/can/usb/peak_usb/pcan_usb_core.h
index 073b47f..13d44a5 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_core.h
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.h
@@ -25,6 +25,8 @@
 /* supported device ids. */
 #define PCAN_USB_PRODUCT_ID		0x000c
 #define PCAN_USBPRO_PRODUCT_ID		0x000d
+#define PCAN_USBPROFD_PRODUCT_ID	0x0011
+#define PCAN_USBFD_PRODUCT_ID		0x0012
 
 #define PCAN_USB_DRIVER_NAME		"peak_usb"
 
@@ -44,8 +46,10 @@ struct peak_usb_device;
 struct peak_usb_adapter {
 	char *name;
 	u32 device_id;
+	u32 ctrlmode_supported;
 	struct can_clock clock;
 	const struct can_bittiming_const bittiming_const;
+	const struct can_bittiming_const data_bittiming_const;
 	unsigned int ctrl_count;
 
 	int (*intf_probe)(struct usb_interface *intf);
@@ -57,6 +61,8 @@ struct peak_usb_adapter {
 	int (*dev_close)(struct peak_usb_device *dev);
 	int (*dev_set_bittiming)(struct peak_usb_device *dev,
 					struct can_bittiming *bt);
+	int (*dev_set_data_bittiming)(struct peak_usb_device *dev,
+				      struct can_bittiming *bt);
 	int (*dev_set_bus)(struct peak_usb_device *dev, u8 onoff);
 	int (*dev_get_device_id)(struct peak_usb_device *dev, u32 *device_id);
 	int (*dev_decode_buf)(struct peak_usb_device *dev, struct urb *urb);
@@ -80,6 +86,8 @@ struct peak_usb_adapter {
 
 extern struct peak_usb_adapter pcan_usb;
 extern struct peak_usb_adapter pcan_usb_pro;
+extern struct peak_usb_adapter pcan_usb_pro_fd;
+extern struct peak_usb_adapter pcan_usb_fd;
 
 struct peak_time_ref {
 	struct timeval tv_host_0, tv_host;
@@ -92,7 +100,7 @@ struct peak_time_ref {
 struct peak_tx_urb_context {
 	struct peak_usb_device *dev;
 	u32 echo_index;
-	u8 dlc;
+	u8 data_len;
 	struct urb *urb;
 };
 
@@ -139,7 +147,9 @@ 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);
-
+int peak_usb_netif_rx(struct sk_buff *skb,
+		      struct peak_time_ref *time_ref, u32 ts_low, u32 ts_high);
 void peak_usb_async_complete(struct urb *urb);
 void peak_usb_restart_complete(struct peak_usb_device *dev);
+
 #endif
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c
new file mode 100644
index 0000000..57d73b5
--- /dev/null
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c
@@ -0,0 +1,1070 @@
+/*
+ * CAN driver for PEAK System PCAN-USB FD / PCAN-USB Pro FD adapter
+ *
+ * Copyright (C) 2013-2014 Stephane Grosjean <s.grosjean@peak-system.com>
+ *
+ * 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"
+#include "pcan_ucan.h"
+
+MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB FD adapter");
+MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB Pro FD adapter");
+
+#define PCAN_USBPROFD_CHANNEL_COUNT	2
+#define PCAN_USBFD_CHANNEL_COUNT	1
+
+/* PCAN-USB Pro FD adapter internal clock (MHz) */
+#define PCAN_UFD_CRYSTAL_HZ		80000000
+
+#define PCAN_UFD_CMD_BUFFER_SIZE	512
+#define PCAN_UFD_LOSPD_PKT_SIZE		64
+
+/* PCAN-USB Pro FD command timeout (ms.) */
+#define PCAN_UFD_CMD_TIMEOUT_MS		1000
+
+/* PCAN-USB Pro FD rx/tx buffers size */
+#define PCAN_UFD_RX_BUFFER_SIZE		2048
+#define PCAN_UFD_TX_BUFFER_SIZE		512
+
+/* read some versions info from the hw devcie */
+struct __packed pcan_ufd_fw_info {
+	__le16	size_of;	/* sizeof this */
+	__le16	type;		/* type of this structure */
+	u8	hw_type;	/* Type of hardware (HW_TYPE_xxx) */
+	u8	bl_version[3];	/* Bootloader version */
+	u8	hw_version;	/* Hardware version (PCB) */
+	u8	fw_version[3];	/* Firmware version */
+	__le32	dev_id[2];	/* "device id" per CAN */
+	__le32	ser_no;		/* S/N */
+	__le32	flags;		/* special functions */
+};
+
+/* handle device specific info used by the netdevices */
+struct pcan_usb_fd_if {
+	struct peak_usb_device *dev[PCAN_USB_MAX_CHANNEL];
+	struct pcan_ufd_fw_info	fw_info;
+	struct peak_time_ref	time_ref;
+	int			cm_ignore_count;
+	int			dev_opened_count;
+};
+
+/* device information */
+struct pcan_usb_fd_device {
+	struct peak_usb_device	dev;
+	struct pcan_usb_fd_if *	usb_if;
+
+	u8 *	cmd_buffer_addr;
+
+	uint	tx_error_counter;
+	uint	rx_error_counter;
+};
+
+/* Extended USB commands (non uCAN commands) */
+
+/* Clock Modes command */
+#define PCAN_UFD_CMD_CLK_SET		0x80
+
+#define PCAN_UFD_CLK_80MHZ		0x0
+#define PCAN_UFD_CLK_60MHZ		0x1
+#define PCAN_UFD_CLK_40MHZ		0x2
+#define PCAN_UFD_CLK_30MHZ		0x3
+#define PCAN_UFD_CLK_24MHZ		0x4
+#define PCAN_UFD_CLK_20MHZ		0x5
+#define PCAN_UFD_CLK_DEF		PCAN_UFD_CLK_80MHZ
+
+struct __packed pcan_ufd_clock {
+	__le16	opcode_channel;
+
+	u8	mode;
+	u8	unused[5];
+};
+
+/* LED control command */
+#define PCAN_UFD_CMD_LED_SET		0x86
+
+#define PCAN_UFD_LED_DEV		0x00
+#define PCAN_UFD_LED_FAST		0x01
+#define PCAN_UFD_LED_SLOW		0x02
+#define PCAN_UFD_LED_ON			0x03
+#define PCAN_UFD_LED_OFF		0x04
+#define PCAN_UFD_LED_DEF		PCAN_UFD_LED_DEV
+
+struct __packed pcan_ufd_led {
+	__le16	opcode_channel;
+
+	u8	mode;
+	u8	unused[5];
+};
+
+/* Extended usage of uCAN commands CMD_RX_FRAME_xxxABLE for PCAN-USB Pro FD */
+#define PCAN_UFD_FLTEXT_CALIBRATION	0x8000
+
+struct __packed pcan_ufd_filter_ext {
+	__le16	opcode_channel;
+
+	__le16	ext_mask;
+	u16	unused;
+	__le16	usb_mask;
+};
+
+/* Extended usage of uCAN messages for PCAN-USB Pro FD */
+#define PCAN_UFD_MSG_CALIBRATION	0x100
+
+struct __packed pcan_ufd_ts_msg {
+	__le16	size;
+	__le16	type;
+	__le32	ts_low;
+	__le32	ts_high;
+	__le16	usb_frame_index;
+	u16	unused;
+};
+
+#define PCAN_UFD_MSG_OVERRUN		0x101
+
+#define PCAN_UFD_OVMSG_CHANNEL(o)	((o)->channel & 0xf)
+
+struct __packed pcan_ufd_ovr_msg {
+	__le16	size;
+	__le16	type;
+	__le32	ts_low;
+	__le32	ts_high;
+	u8	channel;
+	u8	unused[3];
+};
+
+/* Clock mode frequency values */
+static const u32 pcan_usb_fd_clk_freq[6] = {
+	[PCAN_UFD_CLK_80MHZ] = 80000000,
+	[PCAN_UFD_CLK_60MHZ] = 60000000,
+	[PCAN_UFD_CLK_40MHZ] = 40000000,
+	[PCAN_UFD_CLK_30MHZ] = 30000000,
+	[PCAN_UFD_CLK_24MHZ] = 24000000,
+	[PCAN_UFD_CLK_20MHZ] = 20000000
+};
+
+/* build the opcode_channel field with respect to the correct endianess */
+static inline __le16 pucan_cmd_opcode_channel(int opcode, int channel)
+{
+	return cpu_to_le16(PUCAN_CMD_OPCODE_CHANNEL(opcode, channel));
+}
+
+/* return a device USB interface */
+static inline
+struct pcan_usb_fd_if *pcan_usb_fd_dev_if(struct peak_usb_device *dev)
+{
+	struct pcan_usb_fd_device *pdev =
+			container_of(dev, struct pcan_usb_fd_device, dev);
+	return pdev->usb_if;
+}
+
+/* return a device USB commands buffer */
+static inline void *pcan_usb_fd_cmd_buffer(struct peak_usb_device *dev)
+{
+	struct pcan_usb_fd_device *pdev =
+			container_of(dev, struct pcan_usb_fd_device, dev);
+	return pdev->cmd_buffer_addr;
+}
+
+/* send PCAN-USB Pro FD commands synchronously */
+static int pcan_usb_fd_send_cmd(struct peak_usb_device *dev, void *cmd_tail)
+{
+	void *cmd_head = pcan_usb_fd_cmd_buffer(dev);
+	int err;
+	u8 *packet_ptr;
+	int i, n = 1, packet_len;
+	ptrdiff_t cmd_len;
+
+	/* usb device unregistered? */
+	if (!(dev->state & PCAN_USB_STATE_CONNECTED))
+		return 0;
+
+	/*
+	 * if a packet is not filled completely by commands, the command list
+	 * is terminated with an "end of collection" record.
+	 */
+	cmd_len = cmd_tail - cmd_head;
+	if (cmd_len <= (PCAN_UFD_CMD_BUFFER_SIZE - sizeof(u64))) {
+		memset(cmd_tail, 0xff, sizeof(u64));
+		cmd_len += sizeof(u64);
+	}
+
+	packet_ptr = cmd_head;
+
+	/* firmware is not able to re-assemble 512 bytes buffer in full-speed */
+	if ((dev->udev->speed != USB_SPEED_HIGH) &&
+	    (cmd_len > PCAN_UFD_LOSPD_PKT_SIZE)) {
+		packet_len = PCAN_UFD_LOSPD_PKT_SIZE;
+		n += cmd_len / packet_len;
+	} else {
+		packet_len = cmd_len;
+	}
+
+	for (i = 0; i < n; i++) {
+		err = usb_bulk_msg(dev->udev,
+				   usb_sndbulkpipe(dev->udev,
+						   PCAN_USBPRO_EP_CMDOUT),
+				   packet_ptr, packet_len,
+				   NULL, PCAN_UFD_CMD_TIMEOUT_MS);
+		if (err) {
+			netdev_err(dev->netdev,
+				   "sending command failure: %d\n", err);
+			break;
+		}
+
+		packet_ptr += packet_len;
+	}
+
+	return err;
+}
+
+static int pcan_usb_fd_set_bus(struct peak_usb_device *dev, u8 onoff)
+{
+	struct pucan_command *cmd = pcan_usb_fd_cmd_buffer(dev);
+	u16 opcode;
+
+	if (onoff) {
+		opcode = (dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) ?
+				PUCAN_CMD_LISTEN_ONLY_MODE :
+				PUCAN_CMD_NORMAL_MODE;
+	} else {
+		opcode = PUCAN_CMD_RESET_MODE;
+	}
+
+	cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, opcode);
+
+	/* send the command */
+	return pcan_usb_fd_send_cmd(dev, ++cmd);
+}
+
+/*
+ * set filtering masks:
+ *
+ *	idx  in range [0..63] selects a row #idx, all rows otherwise
+ *	mask in range [0..0xffffffff] defines up to 32 CANIDs in the row(s)
+ *
+ *	Each bit of this 64 x 32 bits array defines a CANID value:
+ *
+ *	bit[i,j] = 1 implies that CANID=(i x 32)+j will be received, while
+ *	bit[i,j] = 0 implies that CANID=(i x 32)+j will be discarded.
+ */
+static int pcan_usb_fd_set_filter_std(struct peak_usb_device *dev, int idx,
+				      u32 mask)
+{
+	struct pucan_filter_std *cmd = pcan_usb_fd_cmd_buffer(dev);
+	int i, n;
+
+	/* select all rows when idx is out of range [0..63] */
+	if ((idx < 0) || (idx >= (1 << PUCAN_FLTSTD_ROW_IDX_BITS))) {
+		n = 1 << PUCAN_FLTSTD_ROW_IDX_BITS;
+		idx = 0;
+
+	/* select the row (and only the row) otherwise */
+	} else {
+		n = idx + 1;
+	}
+
+	for (i = idx; i < n; i++, cmd++) {
+		cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx,
+							PUCAN_CMD_FILTER_STD);
+		cmd->idx = cpu_to_le16(i);
+		cmd->mask = cpu_to_le32(mask);
+	}
+
+	/* send the command */
+	return pcan_usb_fd_send_cmd(dev, cmd);
+}
+
+/*
+ * set/unset notifications filter:
+ *
+ *	onoff	sets(1)/unset(0) notifications
+ *	mask	each bit defines a kind of notification to set/unset
+ */
+static int pcan_usb_fd_set_filter_ext(struct peak_usb_device *dev,
+				      int onoff, u16 ext_mask, u16 usb_mask)
+{
+	struct pcan_ufd_filter_ext *cmd = pcan_usb_fd_cmd_buffer(dev);
+
+	cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx,
+					(onoff) ? PUCAN_CMD_RX_FRAME_ENABLE :
+						  PUCAN_CMD_RX_FRAME_DISABLE);
+
+	cmd->ext_mask = cpu_to_le16(ext_mask);
+	cmd->usb_mask = cpu_to_le16(usb_mask);
+
+	/* send the command */
+	return pcan_usb_fd_send_cmd(dev, ++cmd);
+}
+
+/* setup LED control */
+static int pcan_usb_fd_set_can_led(struct peak_usb_device *dev, u8 led_mode)
+{
+	struct pcan_ufd_led *cmd = pcan_usb_fd_cmd_buffer(dev);
+
+	cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx,
+						       PCAN_UFD_CMD_LED_SET);
+	cmd->mode = led_mode;
+
+	/* send the command */
+	return pcan_usb_fd_send_cmd(dev, ++cmd);
+}
+
+/* set CAN clock domain */
+static int pcan_usb_fd_set_clock_domain(struct peak_usb_device *dev,
+					u8 clk_mode)
+{
+	struct pcan_ufd_clock *cmd = pcan_usb_fd_cmd_buffer(dev);
+
+	cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx,
+						       PCAN_UFD_CMD_CLK_SET);
+	cmd->mode = clk_mode;
+
+	/* send the command */
+	return pcan_usb_fd_send_cmd(dev, ++cmd);
+}
+
+/* set bittiming for CAN and CAN-FD header */
+static int pcan_usb_fd_set_bittiming_slow(struct peak_usb_device *dev,
+					  struct can_bittiming *bt)
+{
+	struct pucan_timing_slow *cmd = pcan_usb_fd_cmd_buffer(dev);
+
+	cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx,
+						       PUCAN_CMD_TIMING_SLOW);
+	cmd->sjw_t = PUCAN_TSLOW_SJW_T(bt->sjw - 1,
+				dev->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES);
+
+	cmd->tseg2 = PUCAN_TSLOW_TSEG2(bt->phase_seg2 - 1);
+	cmd->tseg1 = PUCAN_TSLOW_TSEG1(bt->prop_seg + bt->phase_seg1 - 1);
+	cmd->brp = cpu_to_le16(PUCAN_TSLOW_BRP(bt->brp - 1));
+
+	cmd->ewl = 96;	/* default */
+
+	/* send the command */
+	return pcan_usb_fd_send_cmd(dev, ++cmd);
+}
+
+/* set CAN-FD bittiming for data */
+static int pcan_usb_fd_set_bittiming_fast(struct peak_usb_device *dev,
+					  struct can_bittiming *bt)
+{
+	struct pucan_timing_fast *cmd = pcan_usb_fd_cmd_buffer(dev);
+
+	cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx,
+						       PUCAN_CMD_TIMING_FAST);
+	cmd->sjw = PUCAN_TFAST_SJW(bt->sjw - 1);
+	cmd->tseg2 = PUCAN_TFAST_TSEG2(bt->phase_seg2 - 1);
+	cmd->tseg1 = PUCAN_TFAST_TSEG1(bt->prop_seg + bt->phase_seg1 - 1);
+	cmd->brp = cpu_to_le16(PUCAN_TFAST_BRP(bt->brp - 1));
+
+	/* send the command */
+	return pcan_usb_fd_send_cmd(dev, ++cmd);
+}
+
+/*
+ * handle restart but in asynchronously way
+ * (uses PCAN-USB Pro code to complete)
+ */
+static int pcan_usb_fd_restart_async(struct peak_usb_device *dev,
+				     struct urb *urb, u8 *buf)
+{
+	struct pucan_command *cmd = (struct pucan_command *)buf;
+
+	cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx,
+				(dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) ?
+						PUCAN_CMD_LISTEN_ONLY_MODE :
+						PUCAN_CMD_NORMAL_MODE);
+	/* EOC */
+	memset(cmd+1, 0xff, sizeof(struct pucan_command));
+
+	usb_fill_bulk_urb(urb, dev->udev,
+			  usb_sndbulkpipe(dev->udev, PCAN_USBPRO_EP_CMDOUT),
+			  buf, 2 * sizeof(struct pucan_command),
+			  pcan_usb_pro_restart_complete, dev);
+
+	return usb_submit_urb(urb, GFP_ATOMIC);
+}
+
+static int pcan_usb_fd_drv_loaded(struct peak_usb_device *dev, int loaded)
+{
+	struct pcan_usb_fd_device *pdev =
+			container_of(dev, struct pcan_usb_fd_device, dev);
+
+	pdev->cmd_buffer_addr[0] = 0;
+	pdev->cmd_buffer_addr[1] = !!loaded;
+
+	return pcan_usb_pro_send_req(dev,
+				PCAN_USBPRO_REQ_FCT,
+				PCAN_USBPRO_FCT_DRVLD,
+				pdev->cmd_buffer_addr,
+				PCAN_USBPRO_FCT_DRVLD_REQ_LEN);
+}
+
+static int pcan_usb_fd_decode_canmsg(struct pcan_usb_fd_if *usb_if,
+				     struct pucan_msg *rx_msg)
+{
+	struct pucan_rx_msg *rm = (struct pucan_rx_msg *)rx_msg;
+	struct peak_usb_device *dev = usb_if->dev[PUCAN_MSG_CHANNEL(rm)];
+	struct net_device *netdev = dev->netdev;
+	struct canfd_frame *cfd;
+	struct sk_buff *skb;
+	const u16 rx_msg_flags = le16_to_cpu(rm->flags);
+
+	if (rx_msg_flags & PUCAN_MSG_EXT_DATA_LEN) {
+		/* CANFD frame case */
+		skb = alloc_canfd_skb(netdev, &cfd);
+		if (!skb)
+			return -ENOMEM;
+
+		if (rx_msg_flags & PUCAN_MSG_BITRATE_SWITCH)
+			cfd->flags |= CANFD_BRS;
+
+		if (rx_msg_flags & PUCAN_MSG_ERROR_STATE_IND)
+			cfd->flags |= CANFD_ESI;
+
+		cfd->len = can_dlc2len(PUCAN_MSG_DLC(rm));
+	} else {
+		/* CANFD frame case */
+		skb = alloc_can_skb(netdev, (struct can_frame **)&cfd);
+		if (!skb)
+			return -ENOMEM;
+
+		cfd->len = get_can_dlc(PUCAN_MSG_DLC(rm));
+	}
+
+	cfd->can_id = le32_to_cpu(rm->can_id);
+
+	if (rx_msg_flags & PUCAN_MSG_EXT_ID)
+		cfd->can_id |= CAN_EFF_FLAG;
+
+	if (rx_msg_flags & PUCAN_MSG_RTR)
+		cfd->can_id |= CAN_RTR_FLAG;
+	else
+		memcpy(cfd->data, rm->d, cfd->len);
+
+	peak_usb_netif_rx(skb, &usb_if->time_ref,
+			  le32_to_cpu(rm->ts_low), le32_to_cpu(rm->ts_high));
+
+	netdev->stats.rx_packets++;
+	netdev->stats.rx_bytes += cfd->len;
+
+	return 0;
+}
+
+/* handle uCAN status message */
+static int pcan_usb_fd_decode_status(struct pcan_usb_fd_if *usb_if,
+				     struct pucan_msg *rx_msg)
+{
+	struct pucan_status_msg *st = (struct pucan_status_msg *)rx_msg;
+	struct peak_usb_device *dev = usb_if->dev[PUCAN_STMSG_CHANNEL(st)];
+	struct pcan_usb_fd_device *pdev =
+			container_of(dev, struct pcan_usb_fd_device, dev);
+	enum can_state new_state = CAN_STATE_ERROR_ACTIVE;
+	struct net_device *netdev = dev->netdev;
+	struct can_frame *cf;
+	struct sk_buff *skb;
+
+	/* nothing should be sent while in BUS_OFF state */
+	if (dev->can.state == CAN_STATE_BUS_OFF)
+		return 0;
+
+	if (PUCAN_STMSG_BUSOFF(st)) {
+		new_state = CAN_STATE_BUS_OFF;
+	} else if (PUCAN_STMSG_PASSIVE(st)) {
+		new_state = CAN_STATE_ERROR_PASSIVE;
+	} else if (PUCAN_STMSG_WARNING(st)) {
+		new_state = CAN_STATE_ERROR_WARNING;
+	} else {
+		/* no error bit (so, no error skb, back to active state) */
+		dev->can.state = CAN_STATE_ERROR_ACTIVE;
+		pdev->tx_error_counter = 0;
+		pdev->rx_error_counter = 0;
+		return 0;
+	}
+
+	/* donot post any error if current state didn't change */
+	if (dev->can.state == new_state)
+		return 0;
+
+	/* allocate an skb to store the error frame */
+	skb = alloc_can_err_skb(netdev, &cf);
+	if (!skb)
+		return -ENOMEM;
+
+	switch (new_state) {
+	case CAN_STATE_BUS_OFF:
+		cf->can_id |= CAN_ERR_BUSOFF;
+		can_bus_off(netdev);
+		break;
+
+	case CAN_STATE_ERROR_PASSIVE:
+		cf->can_id |= CAN_ERR_CRTL;
+		if (pdev->rx_error_counter > 127)
+			cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE;
+		if (pdev->tx_error_counter > 127)
+			cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE;
+
+		dev->can.can_stats.error_passive++;
+		break;
+
+	case CAN_STATE_ERROR_WARNING:
+		cf->can_id |= CAN_ERR_CRTL;
+		if (pdev->rx_error_counter > 96)
+			cf->data[1] |= CAN_ERR_CRTL_RX_WARNING;
+		if (pdev->tx_error_counter > 96)
+			cf->data[1] |= CAN_ERR_CRTL_TX_WARNING;
+
+		dev->can.can_stats.error_warning++;
+		break;
+
+	default:
+		/* default case never happens, only for warnings */
+		new_state = CAN_STATE_ERROR_ACTIVE;
+
+	case CAN_STATE_ERROR_ACTIVE:	/* fallthrough */
+		pdev->tx_error_counter = 0;
+		pdev->rx_error_counter = 0;
+		break;
+	}
+
+	dev->can.state = new_state;
+
+	peak_usb_netif_rx(skb, &usb_if->time_ref,
+			  le32_to_cpu(st->ts_low), le32_to_cpu(st->ts_high));
+
+	netdev->stats.rx_packets++;
+	netdev->stats.rx_bytes += cf->can_dlc;
+
+	return 0;
+}
+
+/* handle uCAN error message */
+static int pcan_usb_fd_decode_error(struct pcan_usb_fd_if *usb_if,
+				    struct pucan_msg *rx_msg)
+{
+	struct pucan_error_msg *er = (struct pucan_error_msg *)rx_msg;
+	struct peak_usb_device *dev = usb_if->dev[PUCAN_ERMSG_CHANNEL(er)];
+	struct pcan_usb_fd_device *pdev =
+			container_of(dev, struct pcan_usb_fd_device, dev);
+
+	/* keep a trace of tx and rx error counters for later use */
+	pdev->tx_error_counter = er->tx_err_cnt;
+	pdev->rx_error_counter = er->rx_err_cnt;
+
+	return 0;
+}
+
+/* handle uCAN overrun message */
+static int pcan_usb_fd_decode_overrun(struct pcan_usb_fd_if *usb_if,
+				      struct pucan_msg *rx_msg)
+{
+	struct pcan_ufd_ovr_msg *ov = (struct pcan_ufd_ovr_msg *)rx_msg;
+	struct peak_usb_device *dev = usb_if->dev[PCAN_UFD_OVMSG_CHANNEL(ov)];
+	struct net_device *netdev = dev->netdev;
+	struct can_frame *cf;
+	struct sk_buff *skb;
+
+	/* allocate an skb to store the error frame */
+	skb = alloc_can_err_skb(netdev, &cf);
+	if (!skb)
+		return -ENOMEM;
+
+	cf->can_id |= CAN_ERR_CRTL;
+	cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
+
+	peak_usb_netif_rx(skb, &usb_if->time_ref,
+			  le32_to_cpu(ov->ts_low), le32_to_cpu(ov->ts_high));
+
+	netdev->stats.rx_over_errors++;
+	netdev->stats.rx_errors++;
+
+	return 0;
+}
+
+/* handle USB calibration message */
+static void pcan_usb_fd_decode_ts(struct pcan_usb_fd_if *usb_if,
+				  struct pucan_msg *rx_msg)
+{
+	struct pcan_ufd_ts_msg *ts = (struct pcan_ufd_ts_msg *)rx_msg;
+
+	/* should wait until clock is stabilized */
+	if (usb_if->cm_ignore_count > 0)
+		usb_if->cm_ignore_count--;
+	else
+		peak_usb_set_ts_now(&usb_if->time_ref, le32_to_cpu(ts->ts_low));
+}
+
+/* callback for bulk IN urb */
+static int pcan_usb_fd_decode_buf(struct peak_usb_device *dev, struct urb *urb)
+{
+	struct pcan_usb_fd_if *usb_if = pcan_usb_fd_dev_if(dev);
+	struct net_device *netdev = dev->netdev;
+	struct pucan_msg *rx_msg;
+	u8 *msg_ptr, *msg_end;
+	int err = 0;
+
+	/* loop reading all the records from the incoming message */
+	msg_ptr = urb->transfer_buffer;
+	msg_end = urb->transfer_buffer + urb->actual_length;
+	for (; msg_ptr < msg_end;) {
+		u16 rx_msg_type, rx_msg_size;
+
+		rx_msg = (struct pucan_msg *)msg_ptr;
+		if (!rx_msg->size) {
+			/* null packet found: end of list */
+			break;
+		}
+
+		rx_msg_size = le16_to_cpu(rx_msg->size);
+		rx_msg_type = le16_to_cpu(rx_msg->type);
+
+		/* check if the record goes out of current packet */
+		if (msg_ptr + rx_msg_size > msg_end) {
+			netdev_err(netdev,
+				   "got frag rec: should inc usb rx buf sze\n");
+			err = -EBADMSG;
+			break;
+		}
+
+		switch (rx_msg_type) {
+		case PUCAN_MSG_CAN_RX:
+			err = pcan_usb_fd_decode_canmsg(usb_if, rx_msg);
+			if (err < 0)
+				goto fail;
+			break;
+
+		case PCAN_UFD_MSG_CALIBRATION:
+			pcan_usb_fd_decode_ts(usb_if, rx_msg);
+			break;
+
+		case PUCAN_MSG_ERROR:
+			err = pcan_usb_fd_decode_error(usb_if, rx_msg);
+			if (err < 0)
+				goto fail;
+			break;
+
+		case PUCAN_MSG_STATUS:
+			err = pcan_usb_fd_decode_status(usb_if, rx_msg);
+			if (err < 0)
+				goto fail;
+			break;
+
+		case PCAN_UFD_MSG_OVERRUN:
+			err = pcan_usb_fd_decode_overrun(usb_if, rx_msg);
+			if (err < 0)
+				goto fail;
+			break;
+
+		default:
+			netdev_err(netdev,
+				   "unhandled msg type 0x%02x (%d): ignored\n",
+				   rx_msg_type, rx_msg_type);
+			break;
+		}
+
+		msg_ptr += rx_msg_size;
+	}
+
+fail:
+	if (err)
+		pcan_dump_mem("received msg",
+			      urb->transfer_buffer, urb->actual_length);
+	return err;
+}
+
+/* CAN/CANFD frames encoding callback */
+static int pcan_usb_fd_encode_msg(struct peak_usb_device *dev,
+				  struct sk_buff *skb, u8 *obuf, size_t *size)
+{
+	struct pucan_tx_msg *tx_msg = (struct pucan_tx_msg *)obuf;
+	struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
+	u16 tx_msg_size, tx_msg_flags;
+	u8 can_dlc;
+
+	tx_msg_size = ALIGN(sizeof(struct pucan_tx_msg) + cfd->len, 4);
+	tx_msg->size = cpu_to_le16(tx_msg_size);
+	tx_msg->type = cpu_to_le16(PUCAN_MSG_CAN_TX);
+
+	tx_msg_flags = 0;
+	if (cfd->can_id & CAN_EFF_FLAG) {
+		tx_msg_flags |= PUCAN_MSG_EXT_ID;
+		tx_msg->can_id = cpu_to_le32(cfd->can_id & CAN_EFF_MASK);
+	} else {
+		tx_msg->can_id = cpu_to_le32(cfd->can_id & CAN_SFF_MASK);
+	}
+
+	if (skb->len == CANFD_MTU) {
+		/* considering a CANFD frame */
+		can_dlc = can_len2dlc(cfd->len);
+
+		tx_msg_flags |= PUCAN_MSG_EXT_DATA_LEN;
+
+		if (cfd->flags & CANFD_BRS)
+			tx_msg_flags |= PUCAN_MSG_BITRATE_SWITCH;
+
+		if (cfd->flags & CANFD_ESI)
+			tx_msg_flags |= PUCAN_MSG_ERROR_STATE_IND;
+	} else {
+		/* CAND 2.0 frames */
+		can_dlc = cfd->len;
+
+		if (cfd->can_id & CAN_RTR_FLAG)
+			tx_msg_flags |= PUCAN_MSG_RTR;
+	}
+
+	tx_msg->flags = cpu_to_le16(tx_msg_flags);
+	tx_msg->channel_dlc = PUCAN_MSG_CHANNEL_DLC(dev->ctrl_idx, can_dlc);
+	memcpy(tx_msg->d, cfd->data, cfd->len);
+
+	/* add null size message to tag the end (messages are 32-bits aligned)*/
+	tx_msg = (struct pucan_tx_msg *)(obuf + tx_msg_size);
+
+	tx_msg->size = 0;
+
+	/* set the whole size of the USB packet to send */
+	*size = tx_msg_size + sizeof(u32);
+
+	return 0;
+}
+
+/* start the interface (last chance before set bus on) */
+static int pcan_usb_fd_start(struct peak_usb_device *dev)
+{
+	struct pcan_usb_fd_device *pdev =
+			container_of(dev, struct pcan_usb_fd_device, dev);
+	int err;
+
+	/* set filter mode: all acceptance */
+	err = pcan_usb_fd_set_filter_std(dev, -1, 0xffffffff);
+	if (err)
+		return err;
+
+	/* opening first device: */
+	if (pdev->usb_if->dev_opened_count == 0) {
+		/* reset time_ref */
+		peak_usb_init_time_ref(&pdev->usb_if->time_ref,
+				       &pcan_usb_pro_fd);
+
+		/* enable USB calibration messages */
+		err = pcan_usb_fd_set_filter_ext(dev, 1,
+						 PUCAN_FLTEXT_ERROR,
+						 PCAN_UFD_FLTEXT_CALIBRATION);
+	}
+
+	pdev->usb_if->dev_opened_count++;
+
+	/* reset cached error counters */
+	pdev->tx_error_counter = 0;
+	pdev->rx_error_counter = 0;
+
+	return err;
+}
+
+/* stop interface (last chance before set bus off) */
+static int pcan_usb_fd_stop(struct peak_usb_device *dev)
+{
+	struct pcan_usb_fd_device *pdev =
+			container_of(dev, struct pcan_usb_fd_device, dev);
+
+	/* turn off special msgs for that interface if no other dev opened */
+	if (pdev->usb_if->dev_opened_count == 1)
+		pcan_usb_fd_set_filter_ext(dev, 0,
+					   PUCAN_FLTEXT_ERROR,
+					   PCAN_UFD_FLTEXT_CALIBRATION);
+	pdev->usb_if->dev_opened_count--;
+
+	return 0;
+}
+
+/* called when probing, to initialize a device object */
+static int pcan_usb_fd_init(struct peak_usb_device *dev)
+{
+	struct pcan_usb_fd_device *pdev =
+			container_of(dev, struct pcan_usb_fd_device, dev);
+	int i, err = -ENOMEM;
+
+	/* do this for 1st channel only */
+	if (!dev->prev_siblings) {
+		/* allocate netdevices common structure attached to first one */
+		pdev->usb_if = kzalloc(sizeof(*pdev->usb_if), GFP_KERNEL);
+		if (!pdev->usb_if)
+			goto err_out;
+
+		/* allocate command buffer once for all for the interface */
+		pdev->cmd_buffer_addr = kmalloc(PCAN_UFD_CMD_BUFFER_SIZE,
+						GFP_KERNEL);
+		if (!pdev->cmd_buffer_addr)
+			goto err_out_1;
+
+		/* number of ts msgs to ignore before taking one into account */
+		pdev->usb_if->cm_ignore_count = 5;
+
+		err = pcan_usb_pro_send_req(dev, PCAN_USBPRO_REQ_INFO,
+					    PCAN_USBPRO_INFO_FW,
+					    &pdev->usb_if->fw_info,
+					    sizeof(pdev->usb_if->fw_info));
+		if (err) {
+			dev_err(dev->netdev->dev.parent,
+				"unable to read %s firmware info (err %d)\n",
+				dev->adapter->name, err);
+			goto err_out_2;
+		}
+
+		/*
+		 * explicit use of dev_xxx() instead of netdev_xxx() here:
+		 * information displayed are related to the device itself, not
+		 * to the canx (channel) device.
+		 */
+		dev_info(dev->netdev->dev.parent,
+			 "PEAK-System %s v%u fw v%u.%u.%u (%u channels)\n",
+			 dev->adapter->name, pdev->usb_if->fw_info.hw_version,
+			 pdev->usb_if->fw_info.fw_version[0],
+			 pdev->usb_if->fw_info.fw_version[1],
+			 pdev->usb_if->fw_info.fw_version[2],
+			 dev->adapter->ctrl_count);
+
+		/* tell the hardware the can driver is running */
+		err = pcan_usb_fd_drv_loaded(dev, 1);
+		if (err) {
+			dev_err(dev->netdev->dev.parent,
+				"unable to tell %s driver is loaded (err %d)\n",
+				dev->adapter->name, err);
+			goto err_out_2;
+		}
+
+	} else {
+		/* otherwise, simply copy previous sibling's values */
+		struct pcan_usb_fd_device *ppdev =
+			container_of(dev->prev_siblings,
+				     struct pcan_usb_fd_device, dev);
+
+		pdev->usb_if = ppdev->usb_if;
+		pdev->cmd_buffer_addr = ppdev->cmd_buffer_addr;
+	}
+
+	pdev->usb_if->dev[dev->ctrl_idx] = dev;
+	dev->device_number =
+		le32_to_cpu(pdev->usb_if->fw_info.dev_id[dev->ctrl_idx]);
+
+	/* set clock domain */
+	for (i = 0; i < ARRAY_SIZE(pcan_usb_fd_clk_freq); i++)
+		if (dev->adapter->clock.freq == pcan_usb_fd_clk_freq[i])
+			break;
+
+	if (i >= ARRAY_SIZE(pcan_usb_fd_clk_freq)) {
+		dev_warn(dev->netdev->dev.parent,
+			 "incompatible clock frequencies\n");
+		err = -EINVAL;
+		goto err_out_2;
+	}
+
+	pcan_usb_fd_set_clock_domain(dev, i);
+
+	/* set LED in default state (end of init phase) */
+	pcan_usb_fd_set_can_led(dev, PCAN_UFD_LED_DEF);
+
+	return 0;
+
+err_out_2:
+	kfree(pdev->cmd_buffer_addr);
+err_out_1:
+	kfree(pdev->usb_if);
+err_out:
+	return err;
+}
+
+/* called when driver module is being unloaded */
+static void pcan_usb_fd_exit(struct peak_usb_device *dev)
+{
+	struct pcan_usb_fd_device *pdev =
+			container_of(dev, struct pcan_usb_fd_device, dev);
+
+	/*
+	 * when rmmod called before unplug and if down, should reset things
+	 * before leaving
+	 */
+	if (dev->can.state != CAN_STATE_STOPPED) {
+		/* set bus off on the corresponding channel */
+		pcan_usb_fd_set_bus(dev, 0);
+	}
+
+	/* switch off corresponding CAN LEDs */
+	pcan_usb_fd_set_can_led(dev, PCAN_UFD_LED_OFF);
+
+	/* if channel #0 (only) */
+	if (dev->ctrl_idx == 0) {
+		/* turn off calibration message if any device were opened */
+		if (pdev->usb_if->dev_opened_count > 0)
+			pcan_usb_fd_set_filter_ext(dev, 0,
+						   PUCAN_FLTEXT_ERROR,
+						   PCAN_UFD_FLTEXT_CALIBRATION);
+
+		/* tell USB adapter that the driver is being unloaded */
+		pcan_usb_fd_drv_loaded(dev, 0);
+	}
+}
+
+/* called when the USB adapter is unplugged */
+static void pcan_usb_fd_free(struct peak_usb_device *dev)
+{
+	/* last device: can free shared objects now */
+	if (!dev->prev_siblings && !dev->next_siblings) {
+		struct pcan_usb_fd_device *pdev =
+			container_of(dev, struct pcan_usb_fd_device, dev);
+
+		/* free commands buffer */
+		kfree(pdev->cmd_buffer_addr);
+
+		/* free usb interface object */
+		kfree(pdev->usb_if);
+	}
+}
+
+/* describes the PCAN-USB FD adapter */
+struct peak_usb_adapter pcan_usb_fd = {
+	.name = "PCAN-USB FD",
+	.device_id = PCAN_USBFD_PRODUCT_ID,
+	.ctrl_count = PCAN_USBFD_CHANNEL_COUNT,
+	.ctrlmode_supported = CAN_CTRLMODE_FD |
+			CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY,
+	.clock = {
+		.freq = PCAN_UFD_CRYSTAL_HZ,
+	},
+	.bittiming_const = {
+		.name = "pcan_usb_fd",
+		.tseg1_min = 1,
+		.tseg1_max = 64,
+		.tseg2_min = 1,
+		.tseg2_max = 16,
+		.sjw_max = 16,
+		.brp_min = 1,
+		.brp_max = 1024,
+		.brp_inc = 1,
+	},
+	.data_bittiming_const = {
+		.name = "pcan_usb_fd",
+		.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_fd_device),
+
+	/* timestamps usage */
+	.ts_used_bits = 32,
+	.ts_period = 1000000, /* calibration period in ts. */
+	.us_per_ts_scale = 1, /* us = (ts * scale) >> shift */
+	.us_per_ts_shift = 0,
+
+	/* give here messages in/out endpoints */
+	.ep_msg_in = PCAN_USBPRO_EP_MSGIN,
+	.ep_msg_out = {PCAN_USBPRO_EP_MSGOUT_0},
+
+	/* size of rx/tx usb buffers */
+	.rx_buffer_size = PCAN_UFD_RX_BUFFER_SIZE,
+	.tx_buffer_size = PCAN_UFD_TX_BUFFER_SIZE,
+
+	/* device callbacks */
+	.intf_probe = pcan_usb_pro_probe,	/* same as PCAN-USB Pro */
+	.dev_init = pcan_usb_fd_init,
+
+	.dev_exit = pcan_usb_fd_exit,
+	.dev_free = pcan_usb_fd_free,
+	.dev_set_bus = pcan_usb_fd_set_bus,
+	.dev_set_bittiming = pcan_usb_fd_set_bittiming_slow,
+	.dev_set_data_bittiming = pcan_usb_fd_set_bittiming_fast,
+	.dev_decode_buf = pcan_usb_fd_decode_buf,
+	.dev_start = pcan_usb_fd_start,
+	.dev_stop = pcan_usb_fd_stop,
+	.dev_restart_async = pcan_usb_fd_restart_async,
+	.dev_encode_msg = pcan_usb_fd_encode_msg,
+};
+
+/* describes the PCAN-USB Pro FD adapter */
+struct peak_usb_adapter pcan_usb_pro_fd = {
+	.name = "PCAN-USB Pro FD",
+	.device_id = PCAN_USBPROFD_PRODUCT_ID,
+	.ctrl_count = PCAN_USBPROFD_CHANNEL_COUNT,
+	.ctrlmode_supported = CAN_CTRLMODE_FD |
+			CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY,
+	.clock = {
+		.freq = PCAN_UFD_CRYSTAL_HZ,
+	},
+	.bittiming_const = {
+		.name = "pcan_usb_pro_fd",
+		.tseg1_min = 1,
+		.tseg1_max = 64,
+		.tseg2_min = 1,
+		.tseg2_max = 16,
+		.sjw_max = 16,
+		.brp_min = 1,
+		.brp_max = 1024,
+		.brp_inc = 1,
+	},
+	.data_bittiming_const = {
+		.name = "pcan_usb_pro_fd",
+		.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_fd_device),
+
+	/* timestamps usage */
+	.ts_used_bits = 32,
+	.ts_period = 1000000, /* calibration period in ts. */
+	.us_per_ts_scale = 1, /* us = (ts * scale) >> shift */
+	.us_per_ts_shift = 0,
+
+	/* give here messages in/out endpoints */
+	.ep_msg_in = PCAN_USBPRO_EP_MSGIN,
+	.ep_msg_out = {PCAN_USBPRO_EP_MSGOUT_0, PCAN_USBPRO_EP_MSGOUT_1},
+
+	/* size of rx/tx usb buffers */
+	.rx_buffer_size = PCAN_UFD_RX_BUFFER_SIZE,
+	.tx_buffer_size = PCAN_UFD_TX_BUFFER_SIZE,
+
+	/* device callbacks */
+	.intf_probe = pcan_usb_pro_probe,	/* same as PCAN-USB Pro */
+	.dev_init = pcan_usb_fd_init,
+
+	.dev_exit = pcan_usb_fd_exit,
+	.dev_free = pcan_usb_fd_free,
+	.dev_set_bus = pcan_usb_fd_set_bus,
+	.dev_set_bittiming = pcan_usb_fd_set_bittiming_slow,
+	.dev_set_data_bittiming = pcan_usb_fd_set_bittiming_fast,
+	.dev_decode_buf = pcan_usb_fd_decode_buf,
+	.dev_start = pcan_usb_fd_start,
+	.dev_stop = pcan_usb_fd_stop,
+	.dev_restart_async = pcan_usb_fd_restart_async,
+	.dev_encode_msg = pcan_usb_fd_encode_msg,
+};
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
index 4cfa3b8..a764045 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
@@ -27,14 +27,6 @@
 
 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) */
@@ -322,8 +314,8 @@ static int pcan_usb_pro_wait_rsp(struct peak_usb_device *dev,
 	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 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;
@@ -475,7 +467,7 @@ static int pcan_usb_pro_set_bittiming(struct peak_usb_device *dev,
 	return pcan_usb_pro_set_bitrate(dev, ccbt);
 }
 
-static void pcan_usb_pro_restart_complete(struct urb *urb)
+void pcan_usb_pro_restart_complete(struct urb *urb)
 {
 	/* can delete usb resources */
 	peak_usb_async_complete(urb);
@@ -977,7 +969,7 @@ static void pcan_usb_pro_free(struct peak_usb_device *dev)
 /*
  * probe function for new PCAN-USB Pro usb interface
  */
-static int pcan_usb_pro_probe(struct usb_interface *intf)
+int pcan_usb_pro_probe(struct usb_interface *intf)
 {
 	struct usb_host_interface *if_desc;
 	int i;
@@ -1015,6 +1007,7 @@ struct peak_usb_adapter pcan_usb_pro = {
 	.name = "PCAN-USB Pro",
 	.device_id = PCAN_USBPRO_PRODUCT_ID,
 	.ctrl_count = PCAN_USBPRO_CHANNEL_COUNT,
+	.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY,
 	.clock = {
 		.freq = PCAN_USBPRO_CRYSTAL_HZ,
 	},
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.h b/drivers/net/can/usb/peak_usb/pcan_usb_pro.h
index 837cee2..31cef84 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_pro.h
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.h
@@ -27,6 +27,14 @@
 #define PCAN_USBPRO_INFO_BL		0
 #define PCAN_USBPRO_INFO_FW		1
 
+/* PCAN-USB Pro (FD) Endpoints */
+#define PCAN_USBPRO_EP_CMDOUT		1
+#define PCAN_USBPRO_EP_CMDIN		(PCAN_USBPRO_EP_CMDOUT | USB_DIR_IN)
+#define PCAN_USBPRO_EP_MSGOUT_0		2
+#define PCAN_USBPRO_EP_MSGIN		(PCAN_USBPRO_EP_MSGOUT_0 | USB_DIR_IN)
+#define PCAN_USBPRO_EP_MSGOUT_1		3
+#define PCAN_USBPRO_EP_UNUSED		(PCAN_USBPRO_EP_MSGOUT_1 | USB_DIR_IN)
+
 /* Vendor Request value for XXX_FCT */
 #define PCAN_USBPRO_FCT_DRVLD		5 /* tell device driver is loaded */
 #define PCAN_USBPRO_FCT_DRVLD_REQ_LEN	16
@@ -176,4 +184,10 @@ union pcan_usb_pro_rec {
 	struct pcan_usb_pro_txmsg	tx_msg;
 };
 
+extern int pcan_usb_pro_probe(struct usb_interface *intf);
+extern int pcan_usb_pro_send_req(struct peak_usb_device *dev, int req_id,
+		                                 int req_value, void *req_addr,
+						 int req_size);
+extern void pcan_usb_pro_restart_complete(struct urb *urb);
+
 #endif
-- 
1.9.1


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

* Re: [PATCH v4] can/peak_usb: add support for PEAK new CANFD USB adapters
  2015-01-06 10:00 [PATCH v4] can/peak_usb: add support for PEAK new CANFD USB adapters Stephane Grosjean
@ 2015-01-07 17:03 ` Marc Kleine-Budde
  2015-01-07 17:37   ` Oliver Hartkopp
                     ` (3 more replies)
  2015-01-14 12:05 ` Fwd: " Stephane Grosjean
  1 sibling, 4 replies; 18+ messages in thread
From: Marc Kleine-Budde @ 2015-01-07 17:03 UTC (permalink / raw)
  To: Stephane Grosjean, linux-can; +Cc: Oliver Hartkopp

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

On 01/06/2015 11:00 AM, Stephane Grosjean wrote:
> Add support for the following new PEAK-System technik CANFD USB adapters:
> 
> PCAN-USB FD             single CANFD channel USB adapter
> PCAN-USB Pro FD         dual CANFD channels USB adapter

Can you please work out the correct ISO/non-ISO for CAN-fd with Oliver.
If you need CAN_CTRLMODE_FD_NON_ISO make your patch based on

    https://gitorious.org/linux-can/linux-can.git testing

> 
> Signed-off-by: Stephane Grosjean <s.grosjean@peak-system.com>
> ---
> v4: pcan_usb_fd.c: fix missing code for setting standard filtering criteria on
>     received CANID, in function pcan_usb_fd_set_filter_std().
>     Note: driver behavior is to accept all CANID.
> 
>  drivers/net/can/usb/Kconfig                  |   14 +-
>  drivers/net/can/usb/peak_usb/Makefile        |    2 +-
>  drivers/net/can/usb/peak_usb/pcan_ucan.h     |  192 +++++
>  drivers/net/can/usb/peak_usb/pcan_usb.c      |    1 +
>  drivers/net/can/usb/peak_usb/pcan_usb_core.c |   66 +-
>  drivers/net/can/usb/peak_usb/pcan_usb_core.h |   14 +-
>  drivers/net/can/usb/peak_usb/pcan_usb_fd.c   | 1070 ++++++++++++++++++++++++++
>  drivers/net/can/usb/peak_usb/pcan_usb_pro.c  |   17 +-
>  drivers/net/can/usb/peak_usb/pcan_usb_pro.h  |   14 +
>  9 files changed, 1362 insertions(+), 28 deletions(-)
>  create mode 100644 drivers/net/can/usb/peak_usb/pcan_ucan.h
>  create mode 100644 drivers/net/can/usb/peak_usb/pcan_usb_fd.c
> 
> diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig
> index a77db919..6dbbc85 100644
> --- a/drivers/net/can/usb/Kconfig
> +++ b/drivers/net/can/usb/Kconfig
> @@ -53,10 +53,18 @@ config CAN_KVASER_USB
>  	  module will be called kvaser_usb.
>  
>  config CAN_PEAK_USB
> -	tristate "PEAK PCAN-USB/USB Pro interfaces"
> +	tristate "PEAK PCAN-USB/USB Pro interfaces for CAN 2.0b/CAN-FD"
>  	---help---
> -	  This driver supports the PCAN-USB and PCAN-USB Pro adapters
> -	  from PEAK-System Technik (http://www.peak-system.com).
> +	  This driver supports the PEAK-System Technik USB adapters that enable
> +	  access to the CAN bus, with repect to the CAN 2.0b and/or CAN-FD
> +	  standards, that is:
> +
> +	  PCAN-USB             single CAN 2.0b channel USB adapter
> +	  PCAN-USB Pro         dual CAN 2.0b channels USB adapter
> +	  PCAN-USB FD          single CAN-FD channel USB adapter
> +	  PCAN-USB Pro FD      dual CAN-FD channels USB adapter
> +
> +	  (see also http://www.peak-system.com).
>  
>  config CAN_8DEV_USB
>  	tristate "8 devices USB2CAN interface"
> diff --git a/drivers/net/can/usb/peak_usb/Makefile b/drivers/net/can/usb/peak_usb/Makefile
> index 1aefbc8..1839e9c 100644
> --- a/drivers/net/can/usb/peak_usb/Makefile
> +++ b/drivers/net/can/usb/peak_usb/Makefile
> @@ -1,2 +1,2 @@
>  obj-$(CONFIG_CAN_PEAK_USB) += peak_usb.o
> -peak_usb-y = pcan_usb_core.o pcan_usb.o pcan_usb_pro.o
> +peak_usb-y = pcan_usb_core.o pcan_usb.o pcan_usb_pro.o pcan_usb_fd.o
> diff --git a/drivers/net/can/usb/peak_usb/pcan_ucan.h b/drivers/net/can/usb/peak_usb/pcan_ucan.h
> new file mode 100644
> index 0000000..02c0424
> --- /dev/null
> +++ b/drivers/net/can/usb/peak_usb/pcan_ucan.h
> @@ -0,0 +1,192 @@
> +/*
> + * CAN driver for PEAK System micro-CAN based adapters
> + *
> + * Copyright (C) 2003-2011 PEAK System-Technik GmbH
> + * Copyright (C) 2011-2013 Stephane Grosjean <s.grosjean@peak-system.com>
> + *
> + * 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 PUCAN_H
> +#define PUCAN_H
> +
> +/* uCAN commands opcodes list (low-order 10 bits) */
> +#define PUCAN_CMD_NOP			0x000
> +#define PUCAN_CMD_RESET_MODE		0x001
> +#define PUCAN_CMD_NORMAL_MODE		0x002
> +#define PUCAN_CMD_LISTEN_ONLY_MODE	0x003
> +#define PUCAN_CMD_TIMING_SLOW		0x004
> +#define PUCAN_CMD_TIMING_FAST		0x005
> +#define PUCAN_CMD_FILTER_STD		0x008
> +#define PUCAN_CMD_TX_ABORT		0x009
> +#define PUCAN_CMD_WR_ERR_CNT		0x00a
> +#define PUCAN_CMD_RX_FRAME_ENABLE	0x00b
> +#define PUCAN_CMD_RX_FRAME_DISABLE	0x00c
> +#define PUCAN_CMD_END_OF_COLLECTION	0x3ff
> +
> +/* uCAN received messages list */
> +#define PUCAN_MSG_CAN_RX		0x0001
> +#define PUCAN_MSG_ERROR			0x0002
> +#define PUCAN_MSG_STATUS		0x0003
> +#define PUCAN_MSG_BUSLOAD		0x0004
> +#define PUCAN_MSG_CAN_TX		0x1000
> +
> +/* uCAN command common header */
> +#define PUCAN_CMD_OPCODE_CHANNEL(c, o)	(((c) << 12) | ((o) & 0x3ff))
> +
> +struct __packed pucan_command {
> +	__le16	opcode_channel;
> +	u16	args[3];
> +};
> +
> +/* uCAN TIMING_SLOW command fields */
> +#define PUCAN_TSLOW_SJW_T(s, t)		(((s) & 0xf) | ((!!(t)) << 7))
> +#define PUCAN_TSLOW_TSEG2(t)		((t) & 0xf)
> +#define PUCAN_TSLOW_TSEG1(t)		((t) & 0x3f)
> +#define PUCAN_TSLOW_BRP(b)		((b) & 0x3ff)
> +
> +struct __packed pucan_timing_slow {
> +	__le16	opcode_channel;
> +
> +	u8	ewl;		/* Error Warning limit */
> +	u8	sjw_t;		/* Sync Jump Width + Triple sampling */
> +	u8	tseg2;		/* Timing SEGment 2 */
> +	u8	tseg1;		/* Timing SEGment 1 */
> +
> +	__le16	brp;		/* BaudRate Prescaler */
> +};
> +
> +/* uCAN TIMING_FAST command fields */
> +#define PUCAN_TFAST_SJW(s)		((s) & 0x3)
> +#define PUCAN_TFAST_TSEG2(t)		((t) & 0x7)
> +#define PUCAN_TFAST_TSEG1(t)		((t) & 0xf)
> +#define PUCAN_TFAST_BRP(b)		((b) & 0x3ff)
> +
> +struct __packed pucan_timing_fast {
> +	__le16	opcode_channel;
> +
> +	u8	unused;
> +	u8	sjw;		/* Sync Jump Width */
> +	u8	tseg2;		/* Timing SEGment 2 */
> +	u8	tseg1;		/* Timing SEGment 1 */
> +
> +	__le16	brp;		/* BaudRate Prescaler */
> +};
> +
> +/* uCAN FILTER_STD command fields */
> +#define PUCAN_FLTSTD_ROW_IDX_BITS	6
> +
> +struct __packed pucan_filter_std {
> +	__le16	opcode_channel;
> +
> +	__le16	idx;
> +	__le32	mask;		/* CAN-ID bitmask in idx range */
> +};
> +
> +/* uCAN RX_FRAME_ENABLE command fields */
> +#define PUCAN_FLTEXT_ERROR		0x0001
> +#define PUCAN_FLTEXT_BUSLOAD		0x0002
> +
> +struct __packed pucan_filter_ext {
> +	__le16	opcode_channel;
> +
> +	__le16	ext_mask;
> +	u32	unused;
> +};
> +
> +/* uCAN received messages global format */
> +struct __packed pucan_msg {
> +	__le16	size;
> +	__le16	type;
> +	__le32	ts_low;
> +	__le32	ts_high;
> +};
> +
> +/* uCAN flags for CAN/CANFD messages */
> +#define PUCAN_MSG_SELF_RECEIVE		0x80
> +#define PUCAN_MSG_ERROR_STATE_IND	0x40	/* error state indicator */
> +#define PUCAN_MSG_BITRATE_SWITCH	0x20	/* bitrate switch */
> +#define PUCAN_MSG_EXT_DATA_LEN		0x10	/* extended data length */
> +#define PUCAN_MSG_SINGLE_SHOT		0x08
> +#define PUCAN_MSG_LOOPED_BACK		0x04
> +#define PUCAN_MSG_EXT_ID		0x02
> +#define PUCAN_MSG_RTR			0x01
> +
> +#define PUCAN_MSG_CHANNEL(m)		((m)->channel_dlc & 0xf)
> +#define PUCAN_MSG_DLC(m)		((m)->channel_dlc >> 4)

Do not dereference a pointer in a define. Either create a static inline
function for this, or create proper defines for the constants and open
code it.

> +
> +struct __packed pucan_rx_msg {
> +	__le16	size;
> +	__le16	type;
> +	__le32	ts_low;
> +	__le32	ts_high;
> +	__le32	tag_low;
> +	__le32	tag_high;
> +	u8	channel_dlc;
> +	u8	client;
> +	__le16	flags;
> +	__le32	can_id;
> +	u8	d[0];
> +};
> +
> +/* uCAN error types */
> +#define PUCAN_ERMSG_BIT_ERROR		0
> +#define PUCAN_ERMSG_FORM_ERROR		1
> +#define PUCAN_ERMSG_STUFF_ERROR		2
> +#define PUCAN_ERMSG_OTHER_ERROR		3
> +#define PUCAN_ERMSG_ERR_CNT_DEC		4
> +
> +#define PUCAN_ERMSG_CHANNEL(e)		((e)->channel_type_d & 0x0f)
> +#define PUCAN_ERMSG_ERRTYPE(e)		(((e)->channel_type_d >> 4) & 0x07)
> +#define PUCAN_ERMSG_D(e)		((e)->channel_type_d & 0x80)
> +
> +#define PUCAN_ERMSG_ERRCODE(e)		((e)->code_g & 0x7f)
> +#define PUCAN_ERMSG_G(e)		((e)->code_g & 0x80)

same here

> +
> +struct __packed pucan_error_msg {
> +	__le16	size;
> +	__le16	type;
> +	__le32	ts_low;
> +	__le32	ts_high;
> +	u8	channel_type_d;
> +	u8	code_g;
> +	u8	tx_err_cnt;
> +	u8	rx_err_cnt;
> +};
> +
> +#define PUCAN_STMSG_CHANNEL(e)		((e)->channel_p_w_b & 0x0f)
> +#define PUCAN_STMSG_PASSIVE(e)		((e)->channel_p_w_b & 0x20)
> +#define PUCAN_STMSG_WARNING(e)		((e)->channel_p_w_b & 0x40)
> +#define PUCAN_STMSG_BUSOFF(e)		((e)->channel_p_w_b & 0x80)

same here

> +
> +struct __packed pucan_status_msg {
> +	__le16	size;
> +	__le16	type;
> +	__le32	ts_low;
> +	__le32	ts_high;
> +	u8	channel_p_w_b;
> +	u8	unused[3];
> +};
> +
> +/* uCAN transmitted message format */
> +#define PUCAN_MSG_CHANNEL_DLC(c, d)	(((c) & 0xf) | ((d) << 4))
> +
> +struct __packed pucan_tx_msg {
> +	__le16	size;
> +	__le16	type;
> +	__le32	tag_low;
> +	__le32	tag_high;
> +	u8	channel_dlc;
> +	u8	client;
> +	__le16	flags;
> +	__le32	can_id;
> +	u8	d[0];
> +};
> +
> +#endif
> diff --git a/drivers/net/can/usb/peak_usb/pcan_usb.c b/drivers/net/can/usb/peak_usb/pcan_usb.c
> index 4e1659d..5972c7a 100644
> --- a/drivers/net/can/usb/peak_usb/pcan_usb.c
> +++ b/drivers/net/can/usb/peak_usb/pcan_usb.c
> @@ -858,6 +858,7 @@ struct peak_usb_adapter pcan_usb = {
>  	.name = "PCAN-USB",
>  	.device_id = PCAN_USB_PRODUCT_ID,
>  	.ctrl_count = 1,
> +	.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY,

Can you move all ctrlmode_supported related code into a seperate patch.

>  	.clock = {
>  		.freq = PCAN_USB_CRYSTAL_HZ / 2 ,
>  	},
> diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
> index c62f48a..7e8ebc4 100644
> --- a/drivers/net/can/usb/peak_usb/pcan_usb_core.c
> +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
> @@ -37,6 +37,8 @@ MODULE_LICENSE("GPL v2");
>  static struct usb_device_id peak_usb_table[] = {
>  	{USB_DEVICE(PCAN_USB_VENDOR_ID, PCAN_USB_PRODUCT_ID)},
>  	{USB_DEVICE(PCAN_USB_VENDOR_ID, PCAN_USBPRO_PRODUCT_ID)},
> +	{USB_DEVICE(PCAN_USB_VENDOR_ID, PCAN_USBFD_PRODUCT_ID)},
> +	{USB_DEVICE(PCAN_USB_VENDOR_ID, PCAN_USBPROFD_PRODUCT_ID)},
>  	{} /* Terminating entry */
>  };
>  
> @@ -46,6 +48,8 @@ MODULE_DEVICE_TABLE(usb, peak_usb_table);
>  static struct peak_usb_adapter *peak_usb_adapters_list[] = {
>  	&pcan_usb,
>  	&pcan_usb_pro,
> +	&pcan_usb_fd,
> +	&pcan_usb_pro_fd,
>  	NULL,
>  };
>  
> @@ -165,6 +169,21 @@ void peak_usb_get_ts_tv(struct peak_time_ref *time_ref, u32 ts,
>  }
>  
>  /*
> + * post received skb after having set any hw timestamp
> + */
> +int peak_usb_netif_rx(struct sk_buff *skb,
> +		      struct peak_time_ref *time_ref, u32 ts_low, u32 ts_high)
> +{
> +	struct skb_shared_hwtstamps *hwts = skb_hwtstamps(skb);
> +	struct timeval tv;
> +
> +	peak_usb_get_ts_tv(time_ref, ts_low, &tv);
> +	hwts->hwtstamp = timeval_to_ktime(tv);
> +
> +	return netif_rx(skb);
> +}
> +
> +/*
>   * callback for bulk Rx urb
>   */
>  static void peak_usb_read_bulk_callback(struct urb *urb)
> @@ -253,7 +272,7 @@ static void peak_usb_write_bulk_callback(struct urb *urb)
>  	case 0:
>  		/* transmission complete */
>  		netdev->stats.tx_packets++;
> -		netdev->stats.tx_bytes += context->dlc;
> +		netdev->stats.tx_bytes += context->data_len;
>  
>  		/* prevent tx timeout */
>  		netdev->trans_start = jiffies;
> @@ -289,7 +308,7 @@ static netdev_tx_t peak_usb_ndo_start_xmit(struct sk_buff *skb,
>  	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 canfd_frame *cfd = (struct canfd_frame *)skb->data;
>  	struct urb *urb;
>  	u8 *obuf;
>  	int i, err;
> @@ -322,7 +341,9 @@ static netdev_tx_t peak_usb_ndo_start_xmit(struct sk_buff *skb,
>  	}
>  
>  	context->echo_index = i;
> -	context->dlc = cf->can_dlc;
> +
> +	/* Note: this works with CANFD frames too */
> +	context->data_len = cfd->len;
>  
>  	usb_anchor_urb(urb, &dev->tx_submitted);
>  
> @@ -679,19 +700,43 @@ static int peak_usb_set_mode(struct net_device *netdev, enum can_mode mode)
>  }
>  
>  /*
> - * candev callback used to set device bitrate.
> + * candev callback used to set device nominal/arbitration bitrate.
>   */
>  static int peak_usb_set_bittiming(struct net_device *netdev)
>  {
>  	struct peak_usb_device *dev = netdev_priv(netdev);
> -	struct can_bittiming *bt = &dev->can.bittiming;
> +	struct peak_usb_adapter *pa = dev->adapter;
>  
> -	if (dev->adapter->dev_set_bittiming) {
> -		int err = dev->adapter->dev_set_bittiming(dev, bt);
> +	if (pa->dev_set_bittiming) {
> +		struct can_bittiming *bt = &dev->can.bittiming;
> +		int err = pa->dev_set_bittiming(dev, bt);
>  
>  		if (err)
>  			netdev_info(netdev, "couldn't set bitrate (err %d)\n",
> -				err);
> +				    err);
> +		return err;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * candev callback used to set device data bitrate.
> + */
> +static int peak_usb_set_data_bittiming(struct net_device *netdev)
> +{
> +	struct peak_usb_device *dev = netdev_priv(netdev);
> +	struct peak_usb_adapter *pa = dev->adapter;
> +
> +	if (pa->dev_set_data_bittiming) {
> +		struct can_bittiming *bt = &dev->can.data_bittiming;
> +		int err = pa->dev_set_data_bittiming(dev, bt);
> +
> +		if (err)
> +			netdev_info(netdev,
> +				    "couldn't set data bitrate (err %d)\n",
> +				    err);
> +
>  		return err;
>  	}
>  
> @@ -750,9 +795,10 @@ static int peak_usb_create_dev(struct peak_usb_adapter *peak_usb_adapter,
>  	dev->can.clock = peak_usb_adapter->clock;
>  	dev->can.bittiming_const = &peak_usb_adapter->bittiming_const;
>  	dev->can.do_set_bittiming = peak_usb_set_bittiming;
> +	dev->can.data_bittiming_const = &peak_usb_adapter->data_bittiming_const;
> +	dev->can.do_set_data_bittiming = peak_usb_set_data_bittiming;
>  	dev->can.do_set_mode = peak_usb_set_mode;
> -	dev->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
> -				      CAN_CTRLMODE_LISTENONLY;
> +	dev->can.ctrlmode_supported = peak_usb_adapter->ctrlmode_supported;

All ctrlmode_supported can go into a seperate patch.

>  
>  	netdev->netdev_ops = &peak_usb_netdev_ops;
>  
> diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.h b/drivers/net/can/usb/peak_usb/pcan_usb_core.h
> index 073b47f..13d44a5 100644
> --- a/drivers/net/can/usb/peak_usb/pcan_usb_core.h
> +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.h
> @@ -25,6 +25,8 @@
>  /* supported device ids. */
>  #define PCAN_USB_PRODUCT_ID		0x000c
>  #define PCAN_USBPRO_PRODUCT_ID		0x000d
> +#define PCAN_USBPROFD_PRODUCT_ID	0x0011
> +#define PCAN_USBFD_PRODUCT_ID		0x0012
>  
>  #define PCAN_USB_DRIVER_NAME		"peak_usb"
>  
> @@ -44,8 +46,10 @@ struct peak_usb_device;
>  struct peak_usb_adapter {
>  	char *name;
>  	u32 device_id;
> +	u32 ctrlmode_supported;

seperate patch

>  	struct can_clock clock;
>  	const struct can_bittiming_const bittiming_const;
> +	const struct can_bittiming_const data_bittiming_const;
>  	unsigned int ctrl_count;
>  
>  	int (*intf_probe)(struct usb_interface *intf);
> @@ -57,6 +61,8 @@ struct peak_usb_adapter {
>  	int (*dev_close)(struct peak_usb_device *dev);
>  	int (*dev_set_bittiming)(struct peak_usb_device *dev,
>  					struct can_bittiming *bt);
> +	int (*dev_set_data_bittiming)(struct peak_usb_device *dev,
> +				      struct can_bittiming *bt);
>  	int (*dev_set_bus)(struct peak_usb_device *dev, u8 onoff);
>  	int (*dev_get_device_id)(struct peak_usb_device *dev, u32 *device_id);
>  	int (*dev_decode_buf)(struct peak_usb_device *dev, struct urb *urb);
> @@ -80,6 +86,8 @@ struct peak_usb_adapter {
>  
>  extern struct peak_usb_adapter pcan_usb;
>  extern struct peak_usb_adapter pcan_usb_pro;
> +extern struct peak_usb_adapter pcan_usb_pro_fd;
> +extern struct peak_usb_adapter pcan_usb_fd;
>  
>  struct peak_time_ref {
>  	struct timeval tv_host_0, tv_host;
> @@ -92,7 +100,7 @@ struct peak_time_ref {
>  struct peak_tx_urb_context {
>  	struct peak_usb_device *dev;
>  	u32 echo_index;
> -	u8 dlc;
> +	u8 data_len;
>  	struct urb *urb;
>  };
>  
> @@ -139,7 +147,9 @@ 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);
> -
> +int peak_usb_netif_rx(struct sk_buff *skb,
> +		      struct peak_time_ref *time_ref, u32 ts_low, u32 ts_high);
>  void peak_usb_async_complete(struct urb *urb);
>  void peak_usb_restart_complete(struct peak_usb_device *dev);
> +
>  #endif
> diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c
> new file mode 100644
> index 0000000..57d73b5
> --- /dev/null
> +++ b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c
> @@ -0,0 +1,1070 @@
> +/*
> + * CAN driver for PEAK System PCAN-USB FD / PCAN-USB Pro FD adapter
> + *
> + * Copyright (C) 2013-2014 Stephane Grosjean <s.grosjean@peak-system.com>
> + *
> + * 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"
> +#include "pcan_ucan.h"
> +
> +MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB FD adapter");
> +MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB Pro FD adapter");
> +
> +#define PCAN_USBPROFD_CHANNEL_COUNT	2
> +#define PCAN_USBFD_CHANNEL_COUNT	1
> +
> +/* PCAN-USB Pro FD adapter internal clock (MHz) */
> +#define PCAN_UFD_CRYSTAL_HZ		80000000

HZ or MHz?

> +
> +#define PCAN_UFD_CMD_BUFFER_SIZE	512
> +#define PCAN_UFD_LOSPD_PKT_SIZE		64
> +
> +/* PCAN-USB Pro FD command timeout (ms.) */
> +#define PCAN_UFD_CMD_TIMEOUT_MS		1000
> +
> +/* PCAN-USB Pro FD rx/tx buffers size */
> +#define PCAN_UFD_RX_BUFFER_SIZE		2048
> +#define PCAN_UFD_TX_BUFFER_SIZE		512
> +
> +/* read some versions info from the hw devcie */
> +struct __packed pcan_ufd_fw_info {
> +	__le16	size_of;	/* sizeof this */
> +	__le16	type;		/* type of this structure */
> +	u8	hw_type;	/* Type of hardware (HW_TYPE_xxx) */
> +	u8	bl_version[3];	/* Bootloader version */
> +	u8	hw_version;	/* Hardware version (PCB) */
> +	u8	fw_version[3];	/* Firmware version */
> +	__le32	dev_id[2];	/* "device id" per CAN */
> +	__le32	ser_no;		/* S/N */
> +	__le32	flags;		/* special functions */
> +};
> +
> +/* handle device specific info used by the netdevices */
> +struct pcan_usb_fd_if {
> +	struct peak_usb_device *dev[PCAN_USB_MAX_CHANNEL];
> +	struct pcan_ufd_fw_info	fw_info;
> +	struct peak_time_ref	time_ref;
> +	int			cm_ignore_count;
> +	int			dev_opened_count;
> +};
> +
> +/* device information */
> +struct pcan_usb_fd_device {
> +	struct peak_usb_device	dev;
> +	struct pcan_usb_fd_if *	usb_if;
	struct pcan_usb_fd_if *usb_if
> +
> +	u8 *	cmd_buffer_addr;
	u8 *cmd_buffer_addr
> +
> +	uint	tx_error_counter;
> +	uint	rx_error_counter;
	unsigned int

Please don't use a tab after the struct foo, u8, unsinged int, etc...,
as it doesn't align.

> +};
> +
> +/* Extended USB commands (non uCAN commands) */
> +
> +/* Clock Modes command */
> +#define PCAN_UFD_CMD_CLK_SET		0x80
> +
> +#define PCAN_UFD_CLK_80MHZ		0x0
> +#define PCAN_UFD_CLK_60MHZ		0x1
> +#define PCAN_UFD_CLK_40MHZ		0x2
> +#define PCAN_UFD_CLK_30MHZ		0x3
> +#define PCAN_UFD_CLK_24MHZ		0x4
> +#define PCAN_UFD_CLK_20MHZ		0x5
> +#define PCAN_UFD_CLK_DEF		PCAN_UFD_CLK_80MHZ
> +
> +struct __packed pcan_ufd_clock {
> +	__le16	opcode_channel;
> +
> +	u8	mode;
> +	u8	unused[5];
> +};
> +
> +/* LED control command */
> +#define PCAN_UFD_CMD_LED_SET		0x86
> +
> +#define PCAN_UFD_LED_DEV		0x00
> +#define PCAN_UFD_LED_FAST		0x01
> +#define PCAN_UFD_LED_SLOW		0x02
> +#define PCAN_UFD_LED_ON			0x03
> +#define PCAN_UFD_LED_OFF		0x04
> +#define PCAN_UFD_LED_DEF		PCAN_UFD_LED_DEV
> +
> +struct __packed pcan_ufd_led {
> +	__le16	opcode_channel;
> +
> +	u8	mode;
> +	u8	unused[5];
> +};
> +
> +/* Extended usage of uCAN commands CMD_RX_FRAME_xxxABLE for PCAN-USB Pro FD */
> +#define PCAN_UFD_FLTEXT_CALIBRATION	0x8000
> +
> +struct __packed pcan_ufd_filter_ext {
> +	__le16	opcode_channel;
> +
> +	__le16	ext_mask;
> +	u16	unused;
> +	__le16	usb_mask;
> +};
> +
> +/* Extended usage of uCAN messages for PCAN-USB Pro FD */
> +#define PCAN_UFD_MSG_CALIBRATION	0x100
> +
> +struct __packed pcan_ufd_ts_msg {
> +	__le16	size;
> +	__le16	type;
> +	__le32	ts_low;
> +	__le32	ts_high;
> +	__le16	usb_frame_index;
> +	u16	unused;
> +};
> +
> +#define PCAN_UFD_MSG_OVERRUN		0x101
> +
> +#define PCAN_UFD_OVMSG_CHANNEL(o)	((o)->channel & 0xf)

no deref please

> +
> +struct __packed pcan_ufd_ovr_msg {
> +	__le16	size;
> +	__le16	type;
> +	__le32	ts_low;
> +	__le32	ts_high;
> +	u8	channel;
> +	u8	unused[3];
> +};
> +
> +/* Clock mode frequency values */
> +static const u32 pcan_usb_fd_clk_freq[6] = {
> +	[PCAN_UFD_CLK_80MHZ] = 80000000,
> +	[PCAN_UFD_CLK_60MHZ] = 60000000,
> +	[PCAN_UFD_CLK_40MHZ] = 40000000,
> +	[PCAN_UFD_CLK_30MHZ] = 30000000,
> +	[PCAN_UFD_CLK_24MHZ] = 24000000,
> +	[PCAN_UFD_CLK_20MHZ] = 20000000
> +};
> +
> +/* build the opcode_channel field with respect to the correct endianess */
> +static inline __le16 pucan_cmd_opcode_channel(int opcode, int channel)
> +{
> +	return cpu_to_le16(PUCAN_CMD_OPCODE_CHANNEL(opcode, channel));

Just get rid of the PUCAN_CMD_OPCODE_CHANNEL here.

> +}
> +
> +/* return a device USB interface */
> +static inline
> +struct pcan_usb_fd_if *pcan_usb_fd_dev_if(struct peak_usb_device *dev)
> +{
> +	struct pcan_usb_fd_device *pdev =
> +			container_of(dev, struct pcan_usb_fd_device, dev);
> +	return pdev->usb_if;
> +}
> +
> +/* return a device USB commands buffer */
> +static inline void *pcan_usb_fd_cmd_buffer(struct peak_usb_device *dev)
> +{
> +	struct pcan_usb_fd_device *pdev =
> +			container_of(dev, struct pcan_usb_fd_device, dev);
> +	return pdev->cmd_buffer_addr;
> +}
> +
> +/* send PCAN-USB Pro FD commands synchronously */
> +static int pcan_usb_fd_send_cmd(struct peak_usb_device *dev, void *cmd_tail)
> +{
> +	void *cmd_head = pcan_usb_fd_cmd_buffer(dev);
> +	int err;
> +	u8 *packet_ptr;
> +	int i, n = 1, packet_len;
> +	ptrdiff_t cmd_len;
> +
> +	/* usb device unregistered? */
> +	if (!(dev->state & PCAN_USB_STATE_CONNECTED))
> +		return 0;
> +
> +	/*
> +	 * if a packet is not filled completely by commands, the command list
> +	 * is terminated with an "end of collection" record.
> +	 */
> +	cmd_len = cmd_tail - cmd_head;
> +	if (cmd_len <= (PCAN_UFD_CMD_BUFFER_SIZE - sizeof(u64))) {
> +		memset(cmd_tail, 0xff, sizeof(u64));
> +		cmd_len += sizeof(u64);
> +	}
> +
> +	packet_ptr = cmd_head;
> +
> +	/* firmware is not able to re-assemble 512 bytes buffer in full-speed */
> +	if ((dev->udev->speed != USB_SPEED_HIGH) &&
> +	    (cmd_len > PCAN_UFD_LOSPD_PKT_SIZE)) {
> +		packet_len = PCAN_UFD_LOSPD_PKT_SIZE;
> +		n += cmd_len / packet_len;
> +	} else {
> +		packet_len = cmd_len;
> +	}
> +
> +	for (i = 0; i < n; i++) {
> +		err = usb_bulk_msg(dev->udev,
> +				   usb_sndbulkpipe(dev->udev,
> +						   PCAN_USBPRO_EP_CMDOUT),
> +				   packet_ptr, packet_len,
> +				   NULL, PCAN_UFD_CMD_TIMEOUT_MS);
> +		if (err) {
> +			netdev_err(dev->netdev,
> +				   "sending command failure: %d\n", err);
> +			break;
> +		}
> +
> +		packet_ptr += packet_len;
> +	}
> +
> +	return err;
> +}
> +
> +static int pcan_usb_fd_set_bus(struct peak_usb_device *dev, u8 onoff)
> +{
> +	struct pucan_command *cmd = pcan_usb_fd_cmd_buffer(dev);
> +	u16 opcode;
> +
> +	if (onoff) {
> +		opcode = (dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) ?
> +				PUCAN_CMD_LISTEN_ONLY_MODE :
> +				PUCAN_CMD_NORMAL_MODE;
> +	} else {
> +		opcode = PUCAN_CMD_RESET_MODE;
> +	}
> +
> +	cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, opcode);
> +
> +	/* send the command */
> +	return pcan_usb_fd_send_cmd(dev, ++cmd);
> +}
> +
> +/*
> + * set filtering masks:
> + *
> + *	idx  in range [0..63] selects a row #idx, all rows otherwise
> + *	mask in range [0..0xffffffff] defines up to 32 CANIDs in the row(s)
> + *
> + *	Each bit of this 64 x 32 bits array defines a CANID value:
> + *
> + *	bit[i,j] = 1 implies that CANID=(i x 32)+j will be received, while
> + *	bit[i,j] = 0 implies that CANID=(i x 32)+j will be discarded.
> + */
> +static int pcan_usb_fd_set_filter_std(struct peak_usb_device *dev, int idx,
> +				      u32 mask)
> +{
> +	struct pucan_filter_std *cmd = pcan_usb_fd_cmd_buffer(dev);
> +	int i, n;
> +
> +	/* select all rows when idx is out of range [0..63] */
> +	if ((idx < 0) || (idx >= (1 << PUCAN_FLTSTD_ROW_IDX_BITS))) {
> +		n = 1 << PUCAN_FLTSTD_ROW_IDX_BITS;
> +		idx = 0;
> +
> +	/* select the row (and only the row) otherwise */
> +	} else {
> +		n = idx + 1;
> +	}
> +
> +	for (i = idx; i < n; i++, cmd++) {
> +		cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx,
> +							PUCAN_CMD_FILTER_STD);
> +		cmd->idx = cpu_to_le16(i);
> +		cmd->mask = cpu_to_le32(mask);
> +	}
> +
> +	/* send the command */
> +	return pcan_usb_fd_send_cmd(dev, cmd);
> +}
> +
> +/*
> + * set/unset notifications filter:
> + *
> + *	onoff	sets(1)/unset(0) notifications
> + *	mask	each bit defines a kind of notification to set/unset
> + */
> +static int pcan_usb_fd_set_filter_ext(struct peak_usb_device *dev,
> +				      int onoff, u16 ext_mask, u16 usb_mask)
bool onoff
> +{
> +	struct pcan_ufd_filter_ext *cmd = pcan_usb_fd_cmd_buffer(dev);
> +
> +	cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx,
> +					(onoff) ? PUCAN_CMD_RX_FRAME_ENABLE :
> +						  PUCAN_CMD_RX_FRAME_DISABLE);
> +
> +	cmd->ext_mask = cpu_to_le16(ext_mask);
> +	cmd->usb_mask = cpu_to_le16(usb_mask);
> +
> +	/* send the command */
> +	return pcan_usb_fd_send_cmd(dev, ++cmd);
> +}
> +
> +/* setup LED control */
> +static int pcan_usb_fd_set_can_led(struct peak_usb_device *dev, u8 led_mode)
> +{
> +	struct pcan_ufd_led *cmd = pcan_usb_fd_cmd_buffer(dev);
> +
> +	cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx,
> +						       PCAN_UFD_CMD_LED_SET);
> +	cmd->mode = led_mode;
> +
> +	/* send the command */
> +	return pcan_usb_fd_send_cmd(dev, ++cmd);
> +}
> +
> +/* set CAN clock domain */
> +static int pcan_usb_fd_set_clock_domain(struct peak_usb_device *dev,
> +					u8 clk_mode)
> +{
> +	struct pcan_ufd_clock *cmd = pcan_usb_fd_cmd_buffer(dev);
> +
> +	cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx,
> +						       PCAN_UFD_CMD_CLK_SET);
> +	cmd->mode = clk_mode;
> +
> +	/* send the command */
> +	return pcan_usb_fd_send_cmd(dev, ++cmd);
> +}
> +
> +/* set bittiming for CAN and CAN-FD header */
> +static int pcan_usb_fd_set_bittiming_slow(struct peak_usb_device *dev,
> +					  struct can_bittiming *bt)
> +{
> +	struct pucan_timing_slow *cmd = pcan_usb_fd_cmd_buffer(dev);
> +
> +	cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx,
> +						       PUCAN_CMD_TIMING_SLOW);
> +	cmd->sjw_t = PUCAN_TSLOW_SJW_T(bt->sjw - 1,
> +				dev->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES);
> +
> +	cmd->tseg2 = PUCAN_TSLOW_TSEG2(bt->phase_seg2 - 1);
> +	cmd->tseg1 = PUCAN_TSLOW_TSEG1(bt->prop_seg + bt->phase_seg1 - 1);
> +	cmd->brp = cpu_to_le16(PUCAN_TSLOW_BRP(bt->brp - 1));
> +
> +	cmd->ewl = 96;	/* default */
> +
> +	/* send the command */
> +	return pcan_usb_fd_send_cmd(dev, ++cmd);
> +}
> +
> +/* set CAN-FD bittiming for data */
> +static int pcan_usb_fd_set_bittiming_fast(struct peak_usb_device *dev,
> +					  struct can_bittiming *bt)
> +{
> +	struct pucan_timing_fast *cmd = pcan_usb_fd_cmd_buffer(dev);
> +
> +	cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx,
> +						       PUCAN_CMD_TIMING_FAST);
> +	cmd->sjw = PUCAN_TFAST_SJW(bt->sjw - 1);
> +	cmd->tseg2 = PUCAN_TFAST_TSEG2(bt->phase_seg2 - 1);
> +	cmd->tseg1 = PUCAN_TFAST_TSEG1(bt->prop_seg + bt->phase_seg1 - 1);
> +	cmd->brp = cpu_to_le16(PUCAN_TFAST_BRP(bt->brp - 1));
> +
> +	/* send the command */
> +	return pcan_usb_fd_send_cmd(dev, ++cmd);
> +}
> +
> +/*
> + * handle restart but in asynchronously way
> + * (uses PCAN-USB Pro code to complete)
> + */
> +static int pcan_usb_fd_restart_async(struct peak_usb_device *dev,
> +				     struct urb *urb, u8 *buf)
> +{
> +	struct pucan_command *cmd = (struct pucan_command *)buf;
> +
> +	cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx,
> +				(dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) ?
> +						PUCAN_CMD_LISTEN_ONLY_MODE :
> +						PUCAN_CMD_NORMAL_MODE);
> +	/* EOC */
> +	memset(cmd+1, 0xff, sizeof(struct pucan_command));
	cmd + 1
> +
> +	usb_fill_bulk_urb(urb, dev->udev,
> +			  usb_sndbulkpipe(dev->udev, PCAN_USBPRO_EP_CMDOUT),
> +			  buf, 2 * sizeof(struct pucan_command),
> +			  pcan_usb_pro_restart_complete, dev);
> +
> +	return usb_submit_urb(urb, GFP_ATOMIC);
> +}
> +
> +static int pcan_usb_fd_drv_loaded(struct peak_usb_device *dev, int loaded)
bool loaded
> +{
> +	struct pcan_usb_fd_device *pdev =
> +			container_of(dev, struct pcan_usb_fd_device, dev);
> +
> +	pdev->cmd_buffer_addr[0] = 0;
> +	pdev->cmd_buffer_addr[1] = !!loaded;
> +
> +	return pcan_usb_pro_send_req(dev,
> +				PCAN_USBPRO_REQ_FCT,
> +				PCAN_USBPRO_FCT_DRVLD,
> +				pdev->cmd_buffer_addr,
> +				PCAN_USBPRO_FCT_DRVLD_REQ_LEN);
> +}
> +
> +static int pcan_usb_fd_decode_canmsg(struct pcan_usb_fd_if *usb_if,
> +				     struct pucan_msg *rx_msg)
> +{
> +	struct pucan_rx_msg *rm = (struct pucan_rx_msg *)rx_msg;
> +	struct peak_usb_device *dev = usb_if->dev[PUCAN_MSG_CHANNEL(rm)];
> +	struct net_device *netdev = dev->netdev;
> +	struct canfd_frame *cfd;
> +	struct sk_buff *skb;
> +	const u16 rx_msg_flags = le16_to_cpu(rm->flags);
> +
> +	if (rx_msg_flags & PUCAN_MSG_EXT_DATA_LEN) {
> +		/* CANFD frame case */
> +		skb = alloc_canfd_skb(netdev, &cfd);
> +		if (!skb)
> +			return -ENOMEM;
> +
> +		if (rx_msg_flags & PUCAN_MSG_BITRATE_SWITCH)
> +			cfd->flags |= CANFD_BRS;
> +
> +		if (rx_msg_flags & PUCAN_MSG_ERROR_STATE_IND)
> +			cfd->flags |= CANFD_ESI;
> +
> +		cfd->len = can_dlc2len(PUCAN_MSG_DLC(rm));

please use get_canfd_dlc() first:

	cfd->len = can_dlc2len(get_canfd_dlc(PUCAN_MSG_DLC(rm)));

Although AFAICS it makes no difference here.

> +	} else {
> +		/* CANFD frame case */
> +		skb = alloc_can_skb(netdev, (struct can_frame **)&cfd);
> +		if (!skb)
> +			return -ENOMEM;
> +
> +		cfd->len = get_can_dlc(PUCAN_MSG_DLC(rm));
> +	}
> +
> +	cfd->can_id = le32_to_cpu(rm->can_id);
> +
> +	if (rx_msg_flags & PUCAN_MSG_EXT_ID)
> +		cfd->can_id |= CAN_EFF_FLAG;
> +
> +	if (rx_msg_flags & PUCAN_MSG_RTR)
> +		cfd->can_id |= CAN_RTR_FLAG;
> +	else
> +		memcpy(cfd->data, rm->d, cfd->len);
> +
> +	peak_usb_netif_rx(skb, &usb_if->time_ref,
> +			  le32_to_cpu(rm->ts_low), le32_to_cpu(rm->ts_high));
> +
> +	netdev->stats.rx_packets++;
> +	netdev->stats.rx_bytes += cfd->len;
> +
> +	return 0;
> +}
> +
> +/* handle uCAN status message */
> +static int pcan_usb_fd_decode_status(struct pcan_usb_fd_if *usb_if,
> +				     struct pucan_msg *rx_msg)
> +{
> +	struct pucan_status_msg *st = (struct pucan_status_msg *)rx_msg;
> +	struct peak_usb_device *dev = usb_if->dev[PUCAN_STMSG_CHANNEL(st)];
> +	struct pcan_usb_fd_device *pdev =
> +			container_of(dev, struct pcan_usb_fd_device, dev);
> +	enum can_state new_state = CAN_STATE_ERROR_ACTIVE;
> +	struct net_device *netdev = dev->netdev;
> +	struct can_frame *cf;
> +	struct sk_buff *skb;

Please talk to Wolfgang and Andri Yngvason about the state handling.

> +
> +	/* nothing should be sent while in BUS_OFF state */
> +	if (dev->can.state == CAN_STATE_BUS_OFF)
> +		return 0;
> +
> +	if (PUCAN_STMSG_BUSOFF(st)) {
> +		new_state = CAN_STATE_BUS_OFF;
> +	} else if (PUCAN_STMSG_PASSIVE(st)) {
> +		new_state = CAN_STATE_ERROR_PASSIVE;
> +	} else if (PUCAN_STMSG_WARNING(st)) {
> +		new_state = CAN_STATE_ERROR_WARNING;
> +	} else {
> +		/* no error bit (so, no error skb, back to active state) */
> +		dev->can.state = CAN_STATE_ERROR_ACTIVE;
> +		pdev->tx_error_counter = 0;
> +		pdev->rx_error_counter = 0;
> +		return 0;
> +	}
> +
> +	/* donot post any error if current state didn't change */
do not
> +	if (dev->can.state == new_state)
> +		return 0;
> +
> +	/* allocate an skb to store the error frame */
> +	skb = alloc_can_err_skb(netdev, &cf);
> +	if (!skb)
> +		return -ENOMEM;

In case of an OOM, the some vital state change code is missed, please
rearange your code to deal with OOM.

> +
> +	switch (new_state) {
> +	case CAN_STATE_BUS_OFF:
> +		cf->can_id |= CAN_ERR_BUSOFF;
> +		can_bus_off(netdev);
> +		break;
> +
> +	case CAN_STATE_ERROR_PASSIVE:
> +		cf->can_id |= CAN_ERR_CRTL;
> +		if (pdev->rx_error_counter > 127)
> +			cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE;
> +		if (pdev->tx_error_counter > 127)
> +			cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE;
> +
> +		dev->can.can_stats.error_passive++;
> +		break;
> +
> +	case CAN_STATE_ERROR_WARNING:
> +		cf->can_id |= CAN_ERR_CRTL;
> +		if (pdev->rx_error_counter > 96)
> +			cf->data[1] |= CAN_ERR_CRTL_RX_WARNING;
> +		if (pdev->tx_error_counter > 96)
> +			cf->data[1] |= CAN_ERR_CRTL_TX_WARNING;
> +
> +		dev->can.can_stats.error_warning++;
> +		break;
> +
> +	default:
> +		/* default case never happens, only for warnings */
> +		new_state = CAN_STATE_ERROR_ACTIVE;
> +
> +	case CAN_STATE_ERROR_ACTIVE:	/* fallthrough */
> +		pdev->tx_error_counter = 0;
> +		pdev->rx_error_counter = 0;
> +		break;
> +	}
> +
> +	dev->can.state = new_state;
> +
> +	peak_usb_netif_rx(skb, &usb_if->time_ref,
> +			  le32_to_cpu(st->ts_low), le32_to_cpu(st->ts_high));
> +
> +	netdev->stats.rx_packets++;
> +	netdev->stats.rx_bytes += cf->can_dlc;
> +
> +	return 0;
> +}
> +
> +/* handle uCAN error message */
> +static int pcan_usb_fd_decode_error(struct pcan_usb_fd_if *usb_if,
> +				    struct pucan_msg *rx_msg)
> +{
> +	struct pucan_error_msg *er = (struct pucan_error_msg *)rx_msg;
> +	struct peak_usb_device *dev = usb_if->dev[PUCAN_ERMSG_CHANNEL(er)];
> +	struct pcan_usb_fd_device *pdev =
> +			container_of(dev, struct pcan_usb_fd_device, dev);
> +
> +	/* keep a trace of tx and rx error counters for later use */
> +	pdev->tx_error_counter = er->tx_err_cnt;
> +	pdev->rx_error_counter = er->rx_err_cnt;

Does is make sense to implement priv->can.do_get_berr_counter, too? You
might fill a "struct can_berr_counter" here instead of using
{r,t}x_error_counter.

> +
> +	return 0;
> +}
> +
> +/* handle uCAN overrun message */
> +static int pcan_usb_fd_decode_overrun(struct pcan_usb_fd_if *usb_if,
> +				      struct pucan_msg *rx_msg)
> +{
> +	struct pcan_ufd_ovr_msg *ov = (struct pcan_ufd_ovr_msg *)rx_msg;
> +	struct peak_usb_device *dev = usb_if->dev[PCAN_UFD_OVMSG_CHANNEL(ov)];
> +	struct net_device *netdev = dev->netdev;
> +	struct can_frame *cf;
> +	struct sk_buff *skb;
> +
> +	/* allocate an skb to store the error frame */
> +	skb = alloc_can_err_skb(netdev, &cf);
> +	if (!skb)
> +		return -ENOMEM;

Please to the stats, even in case of OOM.

> +
> +	cf->can_id |= CAN_ERR_CRTL;
> +	cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
> +
> +	peak_usb_netif_rx(skb, &usb_if->time_ref,
> +			  le32_to_cpu(ov->ts_low), le32_to_cpu(ov->ts_high));
> +
> +	netdev->stats.rx_over_errors++;
> +	netdev->stats.rx_errors++;
> +
> +	return 0;
> +}
> +
> +/* handle USB calibration message */
> +static void pcan_usb_fd_decode_ts(struct pcan_usb_fd_if *usb_if,
> +				  struct pucan_msg *rx_msg)
> +{
> +	struct pcan_ufd_ts_msg *ts = (struct pcan_ufd_ts_msg *)rx_msg;
> +
> +	/* should wait until clock is stabilized */
> +	if (usb_if->cm_ignore_count > 0)
> +		usb_if->cm_ignore_count--;
> +	else
> +		peak_usb_set_ts_now(&usb_if->time_ref, le32_to_cpu(ts->ts_low));
> +}
> +
> +/* callback for bulk IN urb */
> +static int pcan_usb_fd_decode_buf(struct peak_usb_device *dev, struct urb *urb)
> +{
> +	struct pcan_usb_fd_if *usb_if = pcan_usb_fd_dev_if(dev);
> +	struct net_device *netdev = dev->netdev;
> +	struct pucan_msg *rx_msg;
> +	u8 *msg_ptr, *msg_end;
> +	int err = 0;
> +
> +	/* loop reading all the records from the incoming message */
> +	msg_ptr = urb->transfer_buffer;
> +	msg_end = urb->transfer_buffer + urb->actual_length;
> +	for (; msg_ptr < msg_end;) {
> +		u16 rx_msg_type, rx_msg_size;
> +
> +		rx_msg = (struct pucan_msg *)msg_ptr;
> +		if (!rx_msg->size) {

You might access transfer_buffer out of bounds here. Please adjust the
check in the for() loop.

> +			/* null packet found: end of list */
> +			break;
> +		}
> +
> +		rx_msg_size = le16_to_cpu(rx_msg->size);
> +		rx_msg_type = le16_to_cpu(rx_msg->type);
> +
> +		/* check if the record goes out of current packet */

rx_msg_size is not to be trusted, as it comes from the outside. You have
to check if size is big enough for the advertised rx_msg_type.

> +		if (msg_ptr + rx_msg_size > msg_end) {
> +			netdev_err(netdev,
> +				   "got frag rec: should inc usb rx buf sze\n");
> +			err = -EBADMSG;
> +			break;
> +		}
> +

Why don't you case the rx_msg into the correct type here?

> +		switch (rx_msg_type) {
> +		case PUCAN_MSG_CAN_RX:
> +			err = pcan_usb_fd_decode_canmsg(usb_if, rx_msg);
> +			if (err < 0)
> +				goto fail;
> +			break;
> +
> +		case PCAN_UFD_MSG_CALIBRATION:
> +			pcan_usb_fd_decode_ts(usb_if, rx_msg);
> +			break;
> +
> +		case PUCAN_MSG_ERROR:
> +			err = pcan_usb_fd_decode_error(usb_if, rx_msg);
> +			if (err < 0)
> +				goto fail;
> +			break;
> +
> +		case PUCAN_MSG_STATUS:
> +			err = pcan_usb_fd_decode_status(usb_if, rx_msg);
> +			if (err < 0)
> +				goto fail;
> +			break;
> +
> +		case PCAN_UFD_MSG_OVERRUN:
> +			err = pcan_usb_fd_decode_overrun(usb_if, rx_msg);
> +			if (err < 0)
> +				goto fail;
> +			break;
> +
> +		default:
> +			netdev_err(netdev,
> +				   "unhandled msg type 0x%02x (%d): ignored\n",
> +				   rx_msg_type, rx_msg_type);
> +			break;
> +		}
> +
> +		msg_ptr += rx_msg_size;
> +	}
> +
> +fail:
> +	if (err)
> +		pcan_dump_mem("received msg",
> +			      urb->transfer_buffer, urb->actual_length);
> +	return err;
> +}
> +
> +/* CAN/CANFD frames encoding callback */
> +static int pcan_usb_fd_encode_msg(struct peak_usb_device *dev,
> +				  struct sk_buff *skb, u8 *obuf, size_t *size)
> +{
> +	struct pucan_tx_msg *tx_msg = (struct pucan_tx_msg *)obuf;
> +	struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
> +	u16 tx_msg_size, tx_msg_flags;
> +	u8 can_dlc;
> +
> +	tx_msg_size = ALIGN(sizeof(struct pucan_tx_msg) + cfd->len, 4);
> +	tx_msg->size = cpu_to_le16(tx_msg_size);
> +	tx_msg->type = cpu_to_le16(PUCAN_MSG_CAN_TX);
> +
> +	tx_msg_flags = 0;
> +	if (cfd->can_id & CAN_EFF_FLAG) {
> +		tx_msg_flags |= PUCAN_MSG_EXT_ID;
> +		tx_msg->can_id = cpu_to_le32(cfd->can_id & CAN_EFF_MASK);
> +	} else {
> +		tx_msg->can_id = cpu_to_le32(cfd->can_id & CAN_SFF_MASK);
> +	}
> +
> +	if (skb->len == CANFD_MTU) {
can_is_canfd_skb()
> +		/* considering a CANFD frame */
> +		can_dlc = can_len2dlc(cfd->len);
> +
> +		tx_msg_flags |= PUCAN_MSG_EXT_DATA_LEN;
> +
> +		if (cfd->flags & CANFD_BRS)
> +			tx_msg_flags |= PUCAN_MSG_BITRATE_SWITCH;
> +
> +		if (cfd->flags & CANFD_ESI)
> +			tx_msg_flags |= PUCAN_MSG_ERROR_STATE_IND;
> +	} else {
> +		/* CAND 2.0 frames */
> +		can_dlc = cfd->len;
> +
> +		if (cfd->can_id & CAN_RTR_FLAG)
> +			tx_msg_flags |= PUCAN_MSG_RTR;
> +	}
> +
> +	tx_msg->flags = cpu_to_le16(tx_msg_flags);
> +	tx_msg->channel_dlc = PUCAN_MSG_CHANNEL_DLC(dev->ctrl_idx, can_dlc);
> +	memcpy(tx_msg->d, cfd->data, cfd->len);
> +
> +	/* add null size message to tag the end (messages are 32-bits aligned)*/

add space before the */, make a multiline comment if needed.

> +	tx_msg = (struct pucan_tx_msg *)(obuf + tx_msg_size);
> +
> +	tx_msg->size = 0;
> +
> +	/* set the whole size of the USB packet to send */
> +	*size = tx_msg_size + sizeof(u32);
> +
> +	return 0;
> +}
> +
> +/* start the interface (last chance before set bus on) */
> +static int pcan_usb_fd_start(struct peak_usb_device *dev)
> +{
> +	struct pcan_usb_fd_device *pdev =
> +			container_of(dev, struct pcan_usb_fd_device, dev);
> +	int err;
> +
> +	/* set filter mode: all acceptance */
> +	err = pcan_usb_fd_set_filter_std(dev, -1, 0xffffffff);
> +	if (err)
> +		return err;
> +
> +	/* opening first device: */
> +	if (pdev->usb_if->dev_opened_count == 0) {
> +		/* reset time_ref */
> +		peak_usb_init_time_ref(&pdev->usb_if->time_ref,
> +				       &pcan_usb_pro_fd);
> +
> +		/* enable USB calibration messages */
> +		err = pcan_usb_fd_set_filter_ext(dev, 1,
> +						 PUCAN_FLTEXT_ERROR,
> +						 PCAN_UFD_FLTEXT_CALIBRATION);
> +	}
> +
> +	pdev->usb_if->dev_opened_count++;
> +
> +	/* reset cached error counters */
> +	pdev->tx_error_counter = 0;
> +	pdev->rx_error_counter = 0;
> +
> +	return err;
> +}
> +
> +/* stop interface (last chance before set bus off) */
> +static int pcan_usb_fd_stop(struct peak_usb_device *dev)
> +{
> +	struct pcan_usb_fd_device *pdev =
> +			container_of(dev, struct pcan_usb_fd_device, dev);
> +
> +	/* turn off special msgs for that interface if no other dev opened */
> +	if (pdev->usb_if->dev_opened_count == 1)
> +		pcan_usb_fd_set_filter_ext(dev, 0,
> +					   PUCAN_FLTEXT_ERROR,
> +					   PCAN_UFD_FLTEXT_CALIBRATION);
> +	pdev->usb_if->dev_opened_count--;
> +
> +	return 0;
> +}
> +
> +/* called when probing, to initialize a device object */
> +static int pcan_usb_fd_init(struct peak_usb_device *dev)
> +{
> +	struct pcan_usb_fd_device *pdev =
> +			container_of(dev, struct pcan_usb_fd_device, dev);
> +	int i, err = -ENOMEM;
> +
> +	/* do this for 1st channel only */
> +	if (!dev->prev_siblings) {
> +		/* allocate netdevices common structure attached to first one */
> +		pdev->usb_if = kzalloc(sizeof(*pdev->usb_if), GFP_KERNEL);
> +		if (!pdev->usb_if)
> +			goto err_out;
> +
> +		/* allocate command buffer once for all for the interface */
> +		pdev->cmd_buffer_addr = kmalloc(PCAN_UFD_CMD_BUFFER_SIZE,
> +						GFP_KERNEL);
> +		if (!pdev->cmd_buffer_addr)
> +			goto err_out_1;
> +
> +		/* number of ts msgs to ignore before taking one into account */
> +		pdev->usb_if->cm_ignore_count = 5;
> +
> +		err = pcan_usb_pro_send_req(dev, PCAN_USBPRO_REQ_INFO,
> +					    PCAN_USBPRO_INFO_FW,
> +					    &pdev->usb_if->fw_info,
> +					    sizeof(pdev->usb_if->fw_info));
> +		if (err) {
> +			dev_err(dev->netdev->dev.parent,
> +				"unable to read %s firmware info (err %d)\n",
> +				dev->adapter->name, err);
> +			goto err_out_2;
> +		}
> +
> +		/*
> +		 * explicit use of dev_xxx() instead of netdev_xxx() here:
> +		 * information displayed are related to the device itself, not
> +		 * to the canx (channel) device.
> +		 */
> +		dev_info(dev->netdev->dev.parent,
> +			 "PEAK-System %s v%u fw v%u.%u.%u (%u channels)\n",
> +			 dev->adapter->name, pdev->usb_if->fw_info.hw_version,
> +			 pdev->usb_if->fw_info.fw_version[0],
> +			 pdev->usb_if->fw_info.fw_version[1],
> +			 pdev->usb_if->fw_info.fw_version[2],
> +			 dev->adapter->ctrl_count);
> +
> +		/* tell the hardware the can driver is running */
> +		err = pcan_usb_fd_drv_loaded(dev, 1);
> +		if (err) {
> +			dev_err(dev->netdev->dev.parent,
> +				"unable to tell %s driver is loaded (err %d)\n",
> +				dev->adapter->name, err);
> +			goto err_out_2;
> +		}
> +
> +	} else {
> +		/* otherwise, simply copy previous sibling's values */
> +		struct pcan_usb_fd_device *ppdev =
> +			container_of(dev->prev_siblings,
> +				     struct pcan_usb_fd_device, dev);
> +
> +		pdev->usb_if = ppdev->usb_if;
> +		pdev->cmd_buffer_addr = ppdev->cmd_buffer_addr;
> +	}
> +
> +	pdev->usb_if->dev[dev->ctrl_idx] = dev;
> +	dev->device_number =
> +		le32_to_cpu(pdev->usb_if->fw_info.dev_id[dev->ctrl_idx]);
> +
> +	/* set clock domain */
> +	for (i = 0; i < ARRAY_SIZE(pcan_usb_fd_clk_freq); i++)
> +		if (dev->adapter->clock.freq == pcan_usb_fd_clk_freq[i])
> +			break;
> +
> +	if (i >= ARRAY_SIZE(pcan_usb_fd_clk_freq)) {
> +		dev_warn(dev->netdev->dev.parent,
> +			 "incompatible clock frequencies\n");
> +		err = -EINVAL;
> +		goto err_out_2;
> +	}
> +
> +	pcan_usb_fd_set_clock_domain(dev, i);
> +
> +	/* set LED in default state (end of init phase) */
> +	pcan_usb_fd_set_can_led(dev, PCAN_UFD_LED_DEF);
> +
> +	return 0;
> +
> +err_out_2:
> +	kfree(pdev->cmd_buffer_addr);
> +err_out_1:
> +	kfree(pdev->usb_if);
> +err_out:
> +	return err;
> +}
> +
> +/* called when driver module is being unloaded */
> +static void pcan_usb_fd_exit(struct peak_usb_device *dev)
> +{
> +	struct pcan_usb_fd_device *pdev =
> +			container_of(dev, struct pcan_usb_fd_device, dev);
> +
> +	/*
> +	 * when rmmod called before unplug and if down, should reset things
> +	 * before leaving
> +	 */

multiline comments in net are like this:

/* foo
 * bar
 */
> +	if (dev->can.state != CAN_STATE_STOPPED) {
> +		/* set bus off on the corresponding channel */
> +		pcan_usb_fd_set_bus(dev, 0);
> +	}
> +
> +	/* switch off corresponding CAN LEDs */
> +	pcan_usb_fd_set_can_led(dev, PCAN_UFD_LED_OFF);
> +
> +	/* if channel #0 (only) */
> +	if (dev->ctrl_idx == 0) {
> +		/* turn off calibration message if any device were opened */
> +		if (pdev->usb_if->dev_opened_count > 0)
> +			pcan_usb_fd_set_filter_ext(dev, 0,
> +						   PUCAN_FLTEXT_ERROR,
> +						   PCAN_UFD_FLTEXT_CALIBRATION);
> +
> +		/* tell USB adapter that the driver is being unloaded */
> +		pcan_usb_fd_drv_loaded(dev, 0);
> +	}
> +}
> +
> +/* called when the USB adapter is unplugged */
> +static void pcan_usb_fd_free(struct peak_usb_device *dev)
> +{
> +	/* last device: can free shared objects now */
> +	if (!dev->prev_siblings && !dev->next_siblings) {
> +		struct pcan_usb_fd_device *pdev =
> +			container_of(dev, struct pcan_usb_fd_device, dev);
> +
> +		/* free commands buffer */
> +		kfree(pdev->cmd_buffer_addr);
> +
> +		/* free usb interface object */
> +		kfree(pdev->usb_if);
> +	}
> +}
> +
> +/* describes the PCAN-USB FD adapter */
> +struct peak_usb_adapter pcan_usb_fd = {
> +	.name = "PCAN-USB FD",
> +	.device_id = PCAN_USBFD_PRODUCT_ID,
> +	.ctrl_count = PCAN_USBFD_CHANNEL_COUNT,
> +	.ctrlmode_supported = CAN_CTRLMODE_FD |
> +			CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY,
> +	.clock = {
> +		.freq = PCAN_UFD_CRYSTAL_HZ,
> +	},
> +	.bittiming_const = {
> +		.name = "pcan_usb_fd",
> +		.tseg1_min = 1,
> +		.tseg1_max = 64,
> +		.tseg2_min = 1,
> +		.tseg2_max = 16,
> +		.sjw_max = 16,
> +		.brp_min = 1,
> +		.brp_max = 1024,
> +		.brp_inc = 1,
> +	},
> +	.data_bittiming_const = {
> +		.name = "pcan_usb_fd",
> +		.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_fd_device),
> +
> +	/* timestamps usage */
> +	.ts_used_bits = 32,
> +	.ts_period = 1000000, /* calibration period in ts. */
> +	.us_per_ts_scale = 1, /* us = (ts * scale) >> shift */
> +	.us_per_ts_shift = 0,
> +
> +	/* give here messages in/out endpoints */
> +	.ep_msg_in = PCAN_USBPRO_EP_MSGIN,
> +	.ep_msg_out = {PCAN_USBPRO_EP_MSGOUT_0},
> +
> +	/* size of rx/tx usb buffers */
> +	.rx_buffer_size = PCAN_UFD_RX_BUFFER_SIZE,
> +	.tx_buffer_size = PCAN_UFD_TX_BUFFER_SIZE,
> +
> +	/* device callbacks */
> +	.intf_probe = pcan_usb_pro_probe,	/* same as PCAN-USB Pro */
> +	.dev_init = pcan_usb_fd_init,
> +
> +	.dev_exit = pcan_usb_fd_exit,
> +	.dev_free = pcan_usb_fd_free,
> +	.dev_set_bus = pcan_usb_fd_set_bus,
> +	.dev_set_bittiming = pcan_usb_fd_set_bittiming_slow,
> +	.dev_set_data_bittiming = pcan_usb_fd_set_bittiming_fast,
> +	.dev_decode_buf = pcan_usb_fd_decode_buf,
> +	.dev_start = pcan_usb_fd_start,
> +	.dev_stop = pcan_usb_fd_stop,
> +	.dev_restart_async = pcan_usb_fd_restart_async,
> +	.dev_encode_msg = pcan_usb_fd_encode_msg,
> +};
> +
> +/* describes the PCAN-USB Pro FD adapter */
> +struct peak_usb_adapter pcan_usb_pro_fd = {
> +	.name = "PCAN-USB Pro FD",
> +	.device_id = PCAN_USBPROFD_PRODUCT_ID,
> +	.ctrl_count = PCAN_USBPROFD_CHANNEL_COUNT,
> +	.ctrlmode_supported = CAN_CTRLMODE_FD |
> +			CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY,
> +	.clock = {
> +		.freq = PCAN_UFD_CRYSTAL_HZ,
> +	},
> +	.bittiming_const = {
> +		.name = "pcan_usb_pro_fd",
> +		.tseg1_min = 1,
> +		.tseg1_max = 64,
> +		.tseg2_min = 1,
> +		.tseg2_max = 16,
> +		.sjw_max = 16,
> +		.brp_min = 1,
> +		.brp_max = 1024,
> +		.brp_inc = 1,
> +	},
> +	.data_bittiming_const = {
> +		.name = "pcan_usb_pro_fd",
> +		.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_fd_device),
> +
> +	/* timestamps usage */
> +	.ts_used_bits = 32,
> +	.ts_period = 1000000, /* calibration period in ts. */
> +	.us_per_ts_scale = 1, /* us = (ts * scale) >> shift */
> +	.us_per_ts_shift = 0,
> +
> +	/* give here messages in/out endpoints */
> +	.ep_msg_in = PCAN_USBPRO_EP_MSGIN,
> +	.ep_msg_out = {PCAN_USBPRO_EP_MSGOUT_0, PCAN_USBPRO_EP_MSGOUT_1},
> +
> +	/* size of rx/tx usb buffers */
> +	.rx_buffer_size = PCAN_UFD_RX_BUFFER_SIZE,
> +	.tx_buffer_size = PCAN_UFD_TX_BUFFER_SIZE,
> +
> +	/* device callbacks */
> +	.intf_probe = pcan_usb_pro_probe,	/* same as PCAN-USB Pro */
> +	.dev_init = pcan_usb_fd_init,
> +
> +	.dev_exit = pcan_usb_fd_exit,
> +	.dev_free = pcan_usb_fd_free,
> +	.dev_set_bus = pcan_usb_fd_set_bus,
> +	.dev_set_bittiming = pcan_usb_fd_set_bittiming_slow,
> +	.dev_set_data_bittiming = pcan_usb_fd_set_bittiming_fast,
> +	.dev_decode_buf = pcan_usb_fd_decode_buf,
> +	.dev_start = pcan_usb_fd_start,
> +	.dev_stop = pcan_usb_fd_stop,
> +	.dev_restart_async = pcan_usb_fd_restart_async,
> +	.dev_encode_msg = pcan_usb_fd_encode_msg,
> +};

What about the following hunks (but not the .ctrlmode_supported) ? They
can go into a seperate patch which comes first?

> diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
> index 4cfa3b8..a764045 100644
> --- a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
> +++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
> @@ -27,14 +27,6 @@
>  
>  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) */
> @@ -322,8 +314,8 @@ static int pcan_usb_pro_wait_rsp(struct peak_usb_device *dev,
>  	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 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;
> @@ -475,7 +467,7 @@ static int pcan_usb_pro_set_bittiming(struct peak_usb_device *dev,
>  	return pcan_usb_pro_set_bitrate(dev, ccbt);
>  }
>  
> -static void pcan_usb_pro_restart_complete(struct urb *urb)
> +void pcan_usb_pro_restart_complete(struct urb *urb)
>  {
>  	/* can delete usb resources */
>  	peak_usb_async_complete(urb);
> @@ -977,7 +969,7 @@ static void pcan_usb_pro_free(struct peak_usb_device *dev)
>  /*
>   * probe function for new PCAN-USB Pro usb interface
>   */
> -static int pcan_usb_pro_probe(struct usb_interface *intf)
> +int pcan_usb_pro_probe(struct usb_interface *intf)
>  {
>  	struct usb_host_interface *if_desc;
>  	int i;
> @@ -1015,6 +1007,7 @@ struct peak_usb_adapter pcan_usb_pro = {
>  	.name = "PCAN-USB Pro",
>  	.device_id = PCAN_USBPRO_PRODUCT_ID,
>  	.ctrl_count = PCAN_USBPRO_CHANNEL_COUNT,
> +	.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY,
>  	.clock = {
>  		.freq = PCAN_USBPRO_CRYSTAL_HZ,
>  	},
> diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.h b/drivers/net/can/usb/peak_usb/pcan_usb_pro.h
> index 837cee2..31cef84 100644
> --- a/drivers/net/can/usb/peak_usb/pcan_usb_pro.h
> +++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.h
> @@ -27,6 +27,14 @@
>  #define PCAN_USBPRO_INFO_BL		0
>  #define PCAN_USBPRO_INFO_FW		1
>  
> +/* PCAN-USB Pro (FD) Endpoints */
> +#define PCAN_USBPRO_EP_CMDOUT		1
> +#define PCAN_USBPRO_EP_CMDIN		(PCAN_USBPRO_EP_CMDOUT | USB_DIR_IN)
> +#define PCAN_USBPRO_EP_MSGOUT_0		2
> +#define PCAN_USBPRO_EP_MSGIN		(PCAN_USBPRO_EP_MSGOUT_0 | USB_DIR_IN)
> +#define PCAN_USBPRO_EP_MSGOUT_1		3
> +#define PCAN_USBPRO_EP_UNUSED		(PCAN_USBPRO_EP_MSGOUT_1 | USB_DIR_IN)
> +
>  /* Vendor Request value for XXX_FCT */
>  #define PCAN_USBPRO_FCT_DRVLD		5 /* tell device driver is loaded */
>  #define PCAN_USBPRO_FCT_DRVLD_REQ_LEN	16
> @@ -176,4 +184,10 @@ union pcan_usb_pro_rec {
>  	struct pcan_usb_pro_txmsg	tx_msg;
>  };
>  
> +extern int pcan_usb_pro_probe(struct usb_interface *intf);
> +extern int pcan_usb_pro_send_req(struct peak_usb_device *dev, int req_id,
> +		                                 int req_value, void *req_addr,
> +						 int req_size);
> +extern void pcan_usb_pro_restart_complete(struct urb *urb);

IIRC we're killing the "extern" in function definitions in the kernel.

> +
>  #endif
> 

Marc

-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [PATCH v4] can/peak_usb: add support for PEAK new CANFD USB adapters
  2015-01-07 17:03 ` Marc Kleine-Budde
@ 2015-01-07 17:37   ` Oliver Hartkopp
  2015-01-07 18:37     ` iproute2 fd-non-iso PoC - was " Oliver Hartkopp
  2015-01-08  9:09     ` Marc Kleine-Budde
  2015-01-13 13:18   ` Stephane Grosjean
                     ` (2 subsequent siblings)
  3 siblings, 2 replies; 18+ messages in thread
From: Oliver Hartkopp @ 2015-01-07 17:37 UTC (permalink / raw)
  To: Marc Kleine-Budde, Stephane Grosjean, linux-can

On 07.01.2015 18:03, Marc Kleine-Budde wrote:
> On 01/06/2015 11:00 AM, Stephane Grosjean wrote:
>> Add support for the following new PEAK-System technik CANFD USB adapters:
>>
>> PCAN-USB FD             single CANFD channel USB adapter
>> PCAN-USB Pro FD         dual CANFD channels USB adapter
>
> Can you please work out the correct ISO/non-ISO for CAN-fd with Oliver.
> If you need CAN_CTRLMODE_FD_NON_ISO make your patch based on
>
>      https://gitorious.org/linux-can/linux-can.git testing
>

Good idea.

@Stephane: Do you already have the latest UCAN IP core from Philipp which 
supports the ISO/non-ISO switching? (I don't have it)

The question is if it makes sense to provide a driver for the current UCAN IP 
core and later provide a separate patch when the new (switchable) UCAN IP core 
is available.

But in this case we need to check for the UCAN revision whether it is fixed to 
non-ISO or if it is switchable.

Maybe it makes sense to support ONLY the latest (== switchable) UCAN revision 
for the mainline driver. The update to the latest UCAN revision is mandatory 
anyway.

What is your suggestion?

Regards,
Oliver


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

* iproute2 fd-non-iso PoC - was Re: [PATCH v4] can/peak_usb: add support for PEAK new CANFD USB adapters
  2015-01-07 17:37   ` Oliver Hartkopp
@ 2015-01-07 18:37     ` Oliver Hartkopp
  2015-01-08  9:04       ` Marc Kleine-Budde
  2015-01-08  9:09     ` Marc Kleine-Budde
  1 sibling, 1 reply; 18+ messages in thread
From: Oliver Hartkopp @ 2015-01-07 18:37 UTC (permalink / raw)
  To: Marc Kleine-Budde, Stephane Grosjean, linux-can

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

Just a short test:

I added u32 ctrlmode in Stephanes peak_usb_adapter struct for a test:

@@ -44,8 +46,11 @@ struct peak_usb_device;
 struct peak_usb_adapter {
        char *name;
        u32 device_id;
+       u32 ctrlmode_supported;
+       u32 ctrlmode;
        struct can_clock clock;

Which looks like this in pcan_usb_pro.c

@@ -1015,6 +1007,8 @@ struct peak_usb_adapter pcan_usb_pro = {
        .name = "PCAN-USB Pro",
        .device_id = PCAN_USBPRO_PRODUCT_ID,
        .ctrl_count = PCAN_USBPRO_CHANNEL_COUNT,
+       .ctrlmode = 0,
+       .ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY,
        .clock = {

and in pcan_usb_fd.c :

/* describes the PCAN-USB Pro FD adapter */
struct peak_usb_adapter pcan_usb_pro_fd = {
        .name = "PCAN-USB Pro FD",
        .device_id = PCAN_USBPROFD_PRODUCT_ID,
        .ctrl_count = PCAN_USBPROFD_CHANNEL_COUNT,
        .ctrlmode = CAN_CTRLMODE_FD_NON_ISO,
        .ctrlmode_supported = CAN_CTRLMODE_FD |
                        CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY,
        .clock = {

Finally the ip tools gives this output (when FD is enabled):

# ip -det link show can0
17: can0: <NOARP,ECHO> mtu 72 qdisc noop state DOWN mode DEFAULT group default qlen 10
    link/can  promiscuity 0 
    can <FD,FD-NON-ISO> state STOPPED restart-ms 0 
	  pcan_usb_fd: tseg1 1..64 tseg2 1..16 sjw 1..16 brp 1..1024 brp-inc 1
	  pcan_usb_fd: dtseg1 1..16 dtseg2 1..8 dsjw 1..4 dbrp 1..1024 dbrp-inc 1
	  clock 80000000

AND - as expected - removing this flag is denied:

# ip link set dev can0 type can fd-non-iso off
RTNETLINK answers: Operation not supported

Regards,
Oliver

ps. Current patch for iproute2 is attached. Will send a proper patch when
CAN_CTRLMODE_FD_NON_ISO found its way into mainline.



[-- Attachment #2: iproute2-fd-non-iso.patch --]
[-- Type: text/x-patch, Size: 1624 bytes --]

diff --git a/include/linux/can/netlink.h b/include/linux/can/netlink.h
index 25fd52c..6d4ec2a 100644
--- a/include/linux/can/netlink.h
+++ b/include/linux/can/netlink.h
@@ -98,6 +98,7 @@ struct can_ctrlmode {
 #define CAN_CTRLMODE_BERR_REPORTING	0x10	/* Bus-error reporting */
 #define CAN_CTRLMODE_FD			0x20	/* CAN FD mode */
 #define CAN_CTRLMODE_PRESUME_ACK	0x40	/* Ignore missing CAN ACKs */
+#define CAN_CTRLMODE_FD_NON_ISO		0x80	/* CAN FD in non-ISO mode */
 
 /*
  * CAN device statistics
diff --git a/ip/iplink_can.c b/ip/iplink_can.c
index fb50332..f1b089d 100644
--- a/ip/iplink_can.c
+++ b/ip/iplink_can.c
@@ -37,6 +37,7 @@ static void print_usage(FILE *f)
 		"\t[ one-shot { on | off } ]\n"
 		"\t[ berr-reporting { on | off } ]\n"
 		"\t[ fd { on | off } ]\n"
+		"\t[ fd-non-iso { on | off } ]\n"
 		"\t[ presume-ack { on | off } ]\n"
 		"\n"
 		"\t[ restart-ms TIME-MS ]\n"
@@ -100,6 +101,7 @@ static void print_ctrlmode(FILE *f, __u32 cm)
 	_PF(CAN_CTRLMODE_ONE_SHOT, "ONE-SHOT");
 	_PF(CAN_CTRLMODE_BERR_REPORTING, "BERR-REPORTING");
 	_PF(CAN_CTRLMODE_FD, "FD");
+	_PF(CAN_CTRLMODE_FD_NON_ISO, "FD-NON-ISO");
 	_PF(CAN_CTRLMODE_PRESUME_ACK, "PRESUME-ACK");
 #undef _PF
 	if (cm)
@@ -203,6 +205,10 @@ static int can_parse_opt(struct link_util *lu, int argc, char **argv,
 			NEXT_ARG();
 			set_ctrlmode("fd", *argv, &cm,
 				     CAN_CTRLMODE_FD);
+		} else if (matches(*argv, "fd-non-iso") == 0) {
+			NEXT_ARG();
+			set_ctrlmode("fd-non-iso", *argv, &cm,
+				     CAN_CTRLMODE_FD_NON_ISO);
 		} else if (matches(*argv, "presume-ack") == 0) {
 			NEXT_ARG();
 			set_ctrlmode("presume-ack", *argv, &cm,

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

* Re: iproute2 fd-non-iso PoC - was Re: [PATCH v4] can/peak_usb: add support for PEAK new CANFD USB adapters
  2015-01-07 18:37     ` iproute2 fd-non-iso PoC - was " Oliver Hartkopp
@ 2015-01-08  9:04       ` Marc Kleine-Budde
  0 siblings, 0 replies; 18+ messages in thread
From: Marc Kleine-Budde @ 2015-01-08  9:04 UTC (permalink / raw)
  To: Oliver Hartkopp, Stephane Grosjean, linux-can

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

On 01/07/2015 07:37 PM, Oliver Hartkopp wrote:
> Just a short test:
> 
> I added u32 ctrlmode in Stephanes peak_usb_adapter struct for a test:
> 
> @@ -44,8 +46,11 @@ struct peak_usb_device;
>  struct peak_usb_adapter {
>         char *name;
>         u32 device_id;
> +       u32 ctrlmode_supported;
> +       u32 ctrlmode;
>         struct can_clock clock;
> 
> Which looks like this in pcan_usb_pro.c
> 
> @@ -1015,6 +1007,8 @@ struct peak_usb_adapter pcan_usb_pro = {
>         .name = "PCAN-USB Pro",
>         .device_id = PCAN_USBPRO_PRODUCT_ID,
>         .ctrl_count = PCAN_USBPRO_CHANNEL_COUNT,
> +       .ctrlmode = 0,

Not needed, as 0 is default.

> +       .ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY,
>         .clock = {
> 
> and in pcan_usb_fd.c :
> 
> /* describes the PCAN-USB Pro FD adapter */
> struct peak_usb_adapter pcan_usb_pro_fd = {
>         .name = "PCAN-USB Pro FD",
>         .device_id = PCAN_USBPROFD_PRODUCT_ID,
>         .ctrl_count = PCAN_USBPROFD_CHANNEL_COUNT,
>         .ctrlmode = CAN_CTRLMODE_FD_NON_ISO,
>         .ctrlmode_supported = CAN_CTRLMODE_FD |
>                         CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY,
>         .clock = {
> 
> Finally the ip tools gives this output (when FD is enabled):
> 
> # ip -det link show can0
> 17: can0: <NOARP,ECHO> mtu 72 qdisc noop state DOWN mode DEFAULT group default qlen 10
>     link/can  promiscuity 0 
>     can <FD,FD-NON-ISO> state STOPPED restart-ms 0 
> 	  pcan_usb_fd: tseg1 1..64 tseg2 1..16 sjw 1..16 brp 1..1024 brp-inc 1
> 	  pcan_usb_fd: dtseg1 1..16 dtseg2 1..8 dsjw 1..4 dbrp 1..1024 dbrp-inc 1
> 	  clock 80000000
> 
> AND - as expected - removing this flag is denied:
> 
> # ip link set dev can0 type can fd-non-iso off
> RTNETLINK answers: Operation not supported
> 
> Regards,
> Oliver
> 
> ps. Current patch for iproute2 is attached. Will send a proper patch when
> CAN_CTRLMODE_FD_NON_ISO found its way into mainline.
> 
> 
> 
> iproute2-fd-non-iso.patch
> 
> 
> diff --git a/include/linux/can/netlink.h b/include/linux/can/netlink.h
> index 25fd52c..6d4ec2a 100644
> --- a/include/linux/can/netlink.h
> +++ b/include/linux/can/netlink.h
> @@ -98,6 +98,7 @@ struct can_ctrlmode {
>  #define CAN_CTRLMODE_BERR_REPORTING	0x10	/* Bus-error reporting */
>  #define CAN_CTRLMODE_FD			0x20	/* CAN FD mode */
>  #define CAN_CTRLMODE_PRESUME_ACK	0x40	/* Ignore missing CAN ACKs */
> +#define CAN_CTRLMODE_FD_NON_ISO		0x80	/* CAN FD in non-ISO mode */

AFAIC the headers are imported from the Linux tree, so no need to modify
this file in the final patch. You might have to ask for the import when
sending these patches, though.

Marc
-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [PATCH v4] can/peak_usb: add support for PEAK new CANFD USB adapters
  2015-01-07 17:37   ` Oliver Hartkopp
  2015-01-07 18:37     ` iproute2 fd-non-iso PoC - was " Oliver Hartkopp
@ 2015-01-08  9:09     ` Marc Kleine-Budde
  1 sibling, 0 replies; 18+ messages in thread
From: Marc Kleine-Budde @ 2015-01-08  9:09 UTC (permalink / raw)
  To: Oliver Hartkopp, Stephane Grosjean, linux-can

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

On 01/07/2015 06:37 PM, Oliver Hartkopp wrote:
> On 07.01.2015 18:03, Marc Kleine-Budde wrote:
>> On 01/06/2015 11:00 AM, Stephane Grosjean wrote:
>>> Add support for the following new PEAK-System technik CANFD USB
>>> adapters:
>>>
>>> PCAN-USB FD             single CANFD channel USB adapter
>>> PCAN-USB Pro FD         dual CANFD channels USB adapter
>>
>> Can you please work out the correct ISO/non-ISO for CAN-fd with Oliver.
>> If you need CAN_CTRLMODE_FD_NON_ISO make your patch based on
>>
>>      https://gitorious.org/linux-can/linux-can.git testing
>>
> 
> Good idea.
> 
> @Stephane: Do you already have the latest UCAN IP core from Philipp
> which supports the ISO/non-ISO switching? (I don't have it)
> 
> The question is if it makes sense to provide a driver for the current
> UCAN IP core and later provide a separate patch when the new
> (switchable) UCAN IP core is available.
> 
> But in this case we need to check for the UCAN revision whether it is
> fixed to non-ISO or if it is switchable.
> 
> Maybe it makes sense to support ONLY the latest (== switchable) UCAN
> revision for the mainline driver. The update to the latest UCAN revision
> is mandatory anyway.

I think supporting both variants is the way to go. As there are some
devices with the old firmware in the wild, you have to check the
firmware revision anyways.

Marc

-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [PATCH v4] can/peak_usb: add support for PEAK new CANFD USB adapters
  2015-01-07 17:03 ` Marc Kleine-Budde
  2015-01-07 17:37   ` Oliver Hartkopp
@ 2015-01-13 13:18   ` Stephane Grosjean
  2015-01-13 13:29     ` Marc Kleine-Budde
  2015-01-14 10:50   ` Stephane Grosjean
  2015-01-14 10:56   ` Stephane Grosjean
  3 siblings, 1 reply; 18+ messages in thread
From: Stephane Grosjean @ 2015-01-13 13:18 UTC (permalink / raw)
  To: Marc Kleine-Budde, linux-can; +Cc: Oliver Hartkopp

Hi Marc,

Le 07/01/2015 18:03, Marc Kleine-Budde a écrit :
> On 01/06/2015 11:00 AM, Stephane Grosjean wrote:
>   
> @@ -750,9 +795,10 @@ static int peak_usb_create_dev(struct peak_usb_adapter *peak_usb_adapter,
>   	dev->can.clock = peak_usb_adapter->clock;
>   	dev->can.bittiming_const = &peak_usb_adapter->bittiming_const;
>   	dev->can.do_set_bittiming = peak_usb_set_bittiming;
> +	dev->can.data_bittiming_const = &peak_usb_adapter->data_bittiming_const;
> +	dev->can.do_set_data_bittiming = peak_usb_set_data_bittiming;
>   	dev->can.do_set_mode = peak_usb_set_mode;
> -	dev->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
> -				      CAN_CTRLMODE_LISTENONLY;
> +	dev->can.ctrlmode_supported = peak_usb_adapter->ctrlmode_supported;
> All ctrlmode_supported can go into a seperate patch.

I'll try but can you please explain why?

>
>> diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.h b/drivers/net/can/usb/peak_usb/pcan_usb_core.h
>> index 073b47f..13d44a5 100644
>> --- a/drivers/net/can/usb/peak_usb/pcan_usb_core.h
>> +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.h
>> @@ -25,6 +25,8 @@
>>   /* supported device ids. */
>>   #define PCAN_USB_PRODUCT_ID		0x000c
>>   #define PCAN_USBPRO_PRODUCT_ID		0x000d
>> +#define PCAN_USBPROFD_PRODUCT_ID	0x0011
>> +#define PCAN_USBFD_PRODUCT_ID		0x0012
>>   
>>   #define PCAN_USB_DRIVER_NAME		"peak_usb"
>>   
>> @@ -44,8 +46,10 @@ struct peak_usb_device;
>>   struct peak_usb_adapter {
>>   	char *name;
>>   	u32 device_id;
>> +	u32 ctrlmode_supported;
> seperate patch

same
>
> +
> +/* handle uCAN status message */
> +static int pcan_usb_fd_decode_status(struct pcan_usb_fd_if *usb_if,
> +				     struct pucan_msg *rx_msg)
> +{
> +	struct pucan_status_msg *st = (struct pucan_status_msg *)rx_msg;
> +	struct peak_usb_device *dev = usb_if->dev[PUCAN_STMSG_CHANNEL(st)];
> +	struct pcan_usb_fd_device *pdev =
> +			container_of(dev, struct pcan_usb_fd_device, dev);
> +	enum can_state new_state = CAN_STATE_ERROR_ACTIVE;
> +	struct net_device *netdev = dev->netdev;
> +	struct can_frame *cf;
> +	struct sk_buff *skb;
> Please talk to Wolfgang and Andri Yngvason about the state handling.

Okay.
>
> What about the following hunks (but not the .ctrlmode_supported) ? They
> can go into a seperate patch which comes first?

So. You want me to do a serie of patches, right?
- a first patch that would change the existing files
- another one that would add the ctrl_mode_supported chanegs
- another one that would add the new files
>> diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
>> index 4cfa3b8..a764045 100644
>> --- a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
>> +++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
>> @@ -27,14 +27,6 @@
>>   
>>   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) */
>> @@ -322,8 +314,8 @@ static int pcan_usb_pro_wait_rsp(struct peak_usb_device *dev,
>>   	return (i >= PCAN_USBPRO_RSP_SUBMIT_MAX) ? -ERANGE : err;
>>   }

>>   
>> +extern int pcan_usb_pro_probe(struct usb_interface *intf);
>> +extern int pcan_usb_pro_send_req(struct peak_usb_device *dev, int req_id,
>> +		                                 int req_value, void *req_addr,
>> +						 int req_size);
>> +extern void pcan_usb_pro_restart_complete(struct urb *urb);
> IIRC we're killing the "extern" in function definitions in the kernel.

Ok. So I remove all "extern" keywords even from existing files, right?

> Marc 

Stéphane.
--
PEAK-System Technik GmbH
Sitz der Gesellschaft Darmstadt
Handelsregister Darmstadt HRB 9183 
Geschaeftsfuehrung: Alexander Gach, Uwe Wilhelm
--

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

* Re: [PATCH v4] can/peak_usb: add support for PEAK new CANFD USB adapters
  2015-01-13 13:18   ` Stephane Grosjean
@ 2015-01-13 13:29     ` Marc Kleine-Budde
  2015-01-13 13:32       ` Marc Kleine-Budde
  0 siblings, 1 reply; 18+ messages in thread
From: Marc Kleine-Budde @ 2015-01-13 13:29 UTC (permalink / raw)
  To: Stephane Grosjean, linux-can; +Cc: Oliver Hartkopp

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

On 01/13/2015 02:18 PM, Stephane Grosjean wrote:
> Hi Marc,
> 
> Le 07/01/2015 18:03, Marc Kleine-Budde a écrit :
>> On 01/06/2015 11:00 AM, Stephane Grosjean wrote:
>>   @@ -750,9 +795,10 @@ static int peak_usb_create_dev(struct
>> peak_usb_adapter *peak_usb_adapter,
>>       dev->can.clock = peak_usb_adapter->clock;
>>       dev->can.bittiming_const = &peak_usb_adapter->bittiming_const;
>>       dev->can.do_set_bittiming = peak_usb_set_bittiming;
>> +    dev->can.data_bittiming_const =
>> &peak_usb_adapter->data_bittiming_const;
>> +    dev->can.do_set_data_bittiming = peak_usb_set_data_bittiming;
>>       dev->can.do_set_mode = peak_usb_set_mode;
>> -    dev->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
>> -                      CAN_CTRLMODE_LISTENONLY;
>> +    dev->can.ctrlmode_supported = peak_usb_adapter->ctrlmode_supported;
>> All ctrlmode_supported can go into a seperate patch.
> 
> I'll try but can you please explain why?

Because it's a separate feature. Making the review easier.

[...]

>> What about the following hunks (but not the .ctrlmode_supported) ? They
>> can go into a seperate patch which comes first?
> 
> So. You want me to do a serie of patches, right?
> - a first patch that would change the existing files
> - another one that would add the ctrl_mode_supported chanegs
> - another one that would add the new files

It's not about existing and new files. A patch should handle a single
topic and be easy to review. If something can be separated it should.

For example, adding the ctrl_mode_supported for the existing adapters
would be such a feature. Making the functions in pcan_usb_pro.c non
static, moving the #defines and adding the function declarations to
pcan_usb_pro.h is another preparation patch, so that the final patch,
that adds the new adapter is smaller and thus easier to review.

>>> diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
>>> b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
>>> index 4cfa3b8..a764045 100644
>>> --- a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
>>> +++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
>>> @@ -27,14 +27,6 @@
>>>     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) */
>>> @@ -322,8 +314,8 @@ static int pcan_usb_pro_wait_rsp(struct
>>> peak_usb_device *dev,
>>>       return (i >= PCAN_USBPRO_RSP_SUBMIT_MAX) ? -ERANGE : err;
>>>   }
> 
>>>   +extern int pcan_usb_pro_probe(struct usb_interface *intf);
>>> +extern int pcan_usb_pro_send_req(struct peak_usb_device *dev, int
>>> req_id,
>>> +                                         int req_value, void *req_addr,
>>> +                         int req_size);
>>> +extern void pcan_usb_pro_restart_complete(struct urb *urb);
>> IIRC we're killing the "extern" in function definitions in the kernel.
> 
> Ok. So I remove all "extern" keywords even from existing files, right?

Up to you, this probably would be another patch. However, please don't
add new "extern"s :)

Marc

-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [PATCH v4] can/peak_usb: add support for PEAK new CANFD USB adapters
  2015-01-13 13:29     ` Marc Kleine-Budde
@ 2015-01-13 13:32       ` Marc Kleine-Budde
  0 siblings, 0 replies; 18+ messages in thread
From: Marc Kleine-Budde @ 2015-01-13 13:32 UTC (permalink / raw)
  To: Stephane Grosjean, linux-can; +Cc: Oliver Hartkopp

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

On 01/13/2015 02:29 PM, Marc Kleine-Budde wrote:
> On 01/13/2015 02:18 PM, Stephane Grosjean wrote:
>> Hi Marc,
>>
>> Le 07/01/2015 18:03, Marc Kleine-Budde a écrit :
>>> On 01/06/2015 11:00 AM, Stephane Grosjean wrote:
>>>   @@ -750,9 +795,10 @@ static int peak_usb_create_dev(struct
>>> peak_usb_adapter *peak_usb_adapter,
>>>       dev->can.clock = peak_usb_adapter->clock;
>>>       dev->can.bittiming_const = &peak_usb_adapter->bittiming_const;
>>>       dev->can.do_set_bittiming = peak_usb_set_bittiming;
>>> +    dev->can.data_bittiming_const =
>>> &peak_usb_adapter->data_bittiming_const;
>>> +    dev->can.do_set_data_bittiming = peak_usb_set_data_bittiming;
>>>       dev->can.do_set_mode = peak_usb_set_mode;
>>> -    dev->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
>>> -                      CAN_CTRLMODE_LISTENONLY;
>>> +    dev->can.ctrlmode_supported = peak_usb_adapter->ctrlmode_supported;
>>> All ctrlmode_supported can go into a seperate patch.
>>
>> I'll try but can you please explain why?

BTW: you can split a patch into multiple patches with git quite easy:

# move branch head one patch backwards, working copy stays unmodified:
git reset HEAD^

# for each hunk git will ask you if you want to stage it.
# you can even split a hunk.
git add -p

or use git gui instead of git add -p.

Marc

-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [PATCH v4] can/peak_usb: add support for PEAK new CANFD USB adapters
  2015-01-07 17:03 ` Marc Kleine-Budde
  2015-01-07 17:37   ` Oliver Hartkopp
  2015-01-13 13:18   ` Stephane Grosjean
@ 2015-01-14 10:50   ` Stephane Grosjean
  2015-01-14 10:51     ` Marc Kleine-Budde
  2015-01-14 10:56   ` Stephane Grosjean
  3 siblings, 1 reply; 18+ messages in thread
From: Stephane Grosjean @ 2015-01-14 10:50 UTC (permalink / raw)
  To: Marc Kleine-Budde, linux-can; +Cc: Oliver Hartkopp

Hi Marc,

Sorry, I don't have got Andri's e-mail address... Could you give it to 
me, please?

Thx in advance,

Stéphane

Le 07/01/2015 18:03, Marc Kleine-Budde a écrit :
>> +/* handle uCAN status message */
>> >+static int pcan_usb_fd_decode_status(struct pcan_usb_fd_if *usb_if,
>> >+				     struct pucan_msg *rx_msg)
>> >+{
>> >+	struct pucan_status_msg *st = (struct pucan_status_msg *)rx_msg;
>> >+	struct peak_usb_device *dev = usb_if->dev[PUCAN_STMSG_CHANNEL(st)];
>> >+	struct pcan_usb_fd_device *pdev =
>> >+			container_of(dev, struct pcan_usb_fd_device, dev);
>> >+	enum can_state new_state = CAN_STATE_ERROR_ACTIVE;
>> >+	struct net_device *netdev = dev->netdev;
>> >+	struct can_frame *cf;
>> >+	struct sk_buff *skb;
> Please talk to Wolfgang and Andri Yngvason about the state handling.
>

--
PEAK-System Technik GmbH
Sitz der Gesellschaft Darmstadt
Handelsregister Darmstadt HRB 9183 
Geschaeftsfuehrung: Alexander Gach, Uwe Wilhelm
--

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

* Re: [PATCH v4] can/peak_usb: add support for PEAK new CANFD USB adapters
  2015-01-14 10:50   ` Stephane Grosjean
@ 2015-01-14 10:51     ` Marc Kleine-Budde
  0 siblings, 0 replies; 18+ messages in thread
From: Marc Kleine-Budde @ 2015-01-14 10:51 UTC (permalink / raw)
  To: Stephane Grosjean, linux-can; +Cc: Oliver Hartkopp, Andri Yngvason

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

On 01/14/2015 11:50 AM, Stephane Grosjean wrote:
> Hi Marc,
> 
> Sorry, I don't have got Andri's e-mail address... Could you give it to
> me, please?

Cc'ed

Marc

-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [PATCH v4] can/peak_usb: add support for PEAK new CANFD USB adapters
  2015-01-07 17:03 ` Marc Kleine-Budde
                     ` (2 preceding siblings ...)
  2015-01-14 10:50   ` Stephane Grosjean
@ 2015-01-14 10:56   ` Stephane Grosjean
  2015-01-14 11:07     ` Marc Kleine-Budde
  3 siblings, 1 reply; 18+ messages in thread
From: Stephane Grosjean @ 2015-01-14 10:56 UTC (permalink / raw)
  To: Marc Kleine-Budde; +Cc: linux-can, Oliver Hartkopp

Hi again Marc,

Well... What's wrong?

    $ git clone https://gitorious.org/linux-can/linux-can.git
    Cloning into 'linux-can'...
    error: RPC failed; result=22, HTTP code = 504
    fatal: The remote end hung up unexpectedly

(same with linux-can-next...)

Regards,

Stéphane

Le 07/01/2015 18:03, Marc Kleine-Budde a écrit :
> On 01/06/2015 11:00 AM, Stephane Grosjean wrote:
>> >Add support for the following new PEAK-System technik CANFD USB adapters:
>> >
>> >PCAN-USB FD             single CANFD channel USB adapter
>> >PCAN-USB Pro FD         dual CANFD channels USB adapter
> Can you please work out the correct ISO/non-ISO for CAN-fd with Oliver.
> If you need CAN_CTRLMODE_FD_NON_ISO make your patch based on
>
>      https://gitorious.org/linux-can/linux-can.git  testing
>

--
PEAK-System Technik GmbH
Sitz der Gesellschaft Darmstadt
Handelsregister Darmstadt HRB 9183 
Geschaeftsfuehrung: Alexander Gach, Uwe Wilhelm
--

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

* Re: [PATCH v4] can/peak_usb: add support for PEAK new CANFD USB adapters
  2015-01-14 10:56   ` Stephane Grosjean
@ 2015-01-14 11:07     ` Marc Kleine-Budde
  2015-01-14 18:55       ` Oliver Hartkopp
  0 siblings, 1 reply; 18+ messages in thread
From: Marc Kleine-Budde @ 2015-01-14 11:07 UTC (permalink / raw)
  To: Stephane Grosjean; +Cc: linux-can, Oliver Hartkopp

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

On 01/14/2015 11:56 AM, Stephane Grosjean wrote:
> Hi again Marc,
> 
> Well... What's wrong?
> 
>    $ git clone https://gitorious.org/linux-can/linux-can.git
>    Cloning into 'linux-can'...
>    error: RPC failed; result=22, HTTP code = 504
>    fatal: The remote end hung up unexpectedly
> 
> (same with linux-can-next...)

Same here:
> ➜ (pts/32) frogger@hardanger:/tmp git clone https://gitorious.org/linux-can/linux-can.git 
> Cloning into 'linux-can'...
> error: RPC failed; result=22, HTTP code = 504
> fatal: The remote end hung up unexpectedly

504 is Gateway timeout - maybe the kernel is too big to clone :D

I'd suggest to add can and can-next as a remote to an existing linux git tree.

Skip the first step if you already have a linux git tree:

    git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
    cd linux
    git remote add -f can https://gitorious.org/linux-can/linux-can.git
    git remote add -f can-next https://gitorious.org/linux-can/linux-can-next.git

Marc

-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Fwd: [PATCH v4] can/peak_usb: add support for PEAK new CANFD USB adapters
  2015-01-06 10:00 [PATCH v4] can/peak_usb: add support for PEAK new CANFD USB adapters Stephane Grosjean
  2015-01-07 17:03 ` Marc Kleine-Budde
@ 2015-01-14 12:05 ` Stephane Grosjean
  2015-01-14 13:00   ` Andri Yngvason
  1 sibling, 1 reply; 18+ messages in thread
From: Stephane Grosjean @ 2015-01-14 12:05 UTC (permalink / raw)
  To: Wolfgang Grandegger, andri.yngvason; +Cc: Oliver Hartkopp, linux-can

Hi Wolfgang and Andri,

So, Marc told me to start a discussion around the handling of the status 
information this CANFD driver is able to receive from the hardware.
This, how to proceed next, please? FYI, everything is located in 
"pcan_usb_fd_decode_status()" below...

Thanks and regards,

Stéphane

-------- Message transféré --------
Sujet : 	[PATCH v4] can/peak_usb: add support for PEAK new CANFD USB 
adapters
Date : 	Tue, 6 Jan 2015 11:00:46 +0100
De : 	Stephane Grosjean <s.grosjean@peak-system.com>
Pour : 	linux-can@vger.kernel.org
Copie à : 	Oliver Hartkopp <socketcan@hartkopp.net>, Stephane Grosjean 
<s.grosjean@peak-system.com>



Add support for the following new PEAK-System technik CANFD USB adapters:

PCAN-USB FD             single CANFD channel USB adapter
PCAN-USB Pro FD         dual CANFD channels USB adapter

Signed-off-by: Stephane Grosjean <s.grosjean@peak-system.com>
---
v4: pcan_usb_fd.c: fix missing code for setting standard filtering criteria on
     received CANID, in function pcan_usb_fd_set_filter_std().
     Note: driver behavior is to accept all CANID.

  drivers/net/can/usb/Kconfig                  |   14 +-
  drivers/net/can/usb/peak_usb/Makefile        |    2 +-
  drivers/net/can/usb/peak_usb/pcan_ucan.h     |  192 +++++
  drivers/net/can/usb/peak_usb/pcan_usb.c      |    1 +
  drivers/net/can/usb/peak_usb/pcan_usb_core.c |   66 +-
  drivers/net/can/usb/peak_usb/pcan_usb_core.h |   14 +-
  drivers/net/can/usb/peak_usb/pcan_usb_fd.c   | 1070 ++++++++++++++++++++++++++
  drivers/net/can/usb/peak_usb/pcan_usb_pro.c  |   17 +-
  drivers/net/can/usb/peak_usb/pcan_usb_pro.h  |   14 +
  9 files changed, 1362 insertions(+), 28 deletions(-)
  create mode 100644 drivers/net/can/usb/peak_usb/pcan_ucan.h
  create mode 100644 drivers/net/can/usb/peak_usb/pcan_usb_fd.c

diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig
index a77db919..6dbbc85 100644
--- a/drivers/net/can/usb/Kconfig
+++ b/drivers/net/can/usb/Kconfig
@@ -53,10 +53,18 @@ config CAN_KVASER_USB
  	  module will be called kvaser_usb.
  
  config CAN_PEAK_USB
-	tristate "PEAK PCAN-USB/USB Pro interfaces"
+	tristate "PEAK PCAN-USB/USB Pro interfaces for CAN 2.0b/CAN-FD"
  	---help---
-	  This driver supports the PCAN-USB and PCAN-USB Pro adapters
-	  from PEAK-System Technik (http://www.peak-system.com).
+	  This driver supports the PEAK-System Technik USB adapters that enable
+	  access to the CAN bus, with repect to the CAN 2.0b and/or CAN-FD
+	  standards, that is:
+
+	  PCAN-USB             single CAN 2.0b channel USB adapter
+	  PCAN-USB Pro         dual CAN 2.0b channels USB adapter
+	  PCAN-USB FD          single CAN-FD channel USB adapter
+	  PCAN-USB Pro FD      dual CAN-FD channels USB adapter
+
+	  (see also http://www.peak-system.com).
  
  config CAN_8DEV_USB
  	tristate "8 devices USB2CAN interface"
diff --git a/drivers/net/can/usb/peak_usb/Makefile b/drivers/net/can/usb/peak_usb/Makefile
index 1aefbc8..1839e9c 100644
--- a/drivers/net/can/usb/peak_usb/Makefile
+++ b/drivers/net/can/usb/peak_usb/Makefile
@@ -1,2 +1,2 @@
  obj-$(CONFIG_CAN_PEAK_USB) += peak_usb.o
-peak_usb-y = pcan_usb_core.o pcan_usb.o pcan_usb_pro.o
+peak_usb-y = pcan_usb_core.o pcan_usb.o pcan_usb_pro.o pcan_usb_fd.o
diff --git a/drivers/net/can/usb/peak_usb/pcan_ucan.h b/drivers/net/can/usb/peak_usb/pcan_ucan.h
new file mode 100644
index 0000000..02c0424
--- /dev/null
+++ b/drivers/net/can/usb/peak_usb/pcan_ucan.h
@@ -0,0 +1,192 @@
+/*
+ * CAN driver for PEAK System micro-CAN based adapters
+ *
+ * Copyright (C) 2003-2011 PEAK System-Technik GmbH
+ * Copyright (C) 2011-2013 Stephane Grosjean <s.grosjean@peak-system.com>
+ *
+ * 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 PUCAN_H
+#define PUCAN_H
+
+/* uCAN commands opcodes list (low-order 10 bits) */
+#define PUCAN_CMD_NOP			0x000
+#define PUCAN_CMD_RESET_MODE		0x001
+#define PUCAN_CMD_NORMAL_MODE		0x002
+#define PUCAN_CMD_LISTEN_ONLY_MODE	0x003
+#define PUCAN_CMD_TIMING_SLOW		0x004
+#define PUCAN_CMD_TIMING_FAST		0x005
+#define PUCAN_CMD_FILTER_STD		0x008
+#define PUCAN_CMD_TX_ABORT		0x009
+#define PUCAN_CMD_WR_ERR_CNT		0x00a
+#define PUCAN_CMD_RX_FRAME_ENABLE	0x00b
+#define PUCAN_CMD_RX_FRAME_DISABLE	0x00c
+#define PUCAN_CMD_END_OF_COLLECTION	0x3ff
+
+/* uCAN received messages list */
+#define PUCAN_MSG_CAN_RX		0x0001
+#define PUCAN_MSG_ERROR			0x0002
+#define PUCAN_MSG_STATUS		0x0003
+#define PUCAN_MSG_BUSLOAD		0x0004
+#define PUCAN_MSG_CAN_TX		0x1000
+
+/* uCAN command common header */
+#define PUCAN_CMD_OPCODE_CHANNEL(c, o)	(((c) << 12) | ((o) & 0x3ff))
+
+struct __packed pucan_command {
+	__le16	opcode_channel;
+	u16	args[3];
+};
+
+/* uCAN TIMING_SLOW command fields */
+#define PUCAN_TSLOW_SJW_T(s, t)		(((s) & 0xf) | ((!!(t)) << 7))
+#define PUCAN_TSLOW_TSEG2(t)		((t) & 0xf)
+#define PUCAN_TSLOW_TSEG1(t)		((t) & 0x3f)
+#define PUCAN_TSLOW_BRP(b)		((b) & 0x3ff)
+
+struct __packed pucan_timing_slow {
+	__le16	opcode_channel;
+
+	u8	ewl;		/* Error Warning limit */
+	u8	sjw_t;		/* Sync Jump Width + Triple sampling */
+	u8	tseg2;		/* Timing SEGment 2 */
+	u8	tseg1;		/* Timing SEGment 1 */
+
+	__le16	brp;		/* BaudRate Prescaler */
+};
+
+/* uCAN TIMING_FAST command fields */
+#define PUCAN_TFAST_SJW(s)		((s) & 0x3)
+#define PUCAN_TFAST_TSEG2(t)		((t) & 0x7)
+#define PUCAN_TFAST_TSEG1(t)		((t) & 0xf)
+#define PUCAN_TFAST_BRP(b)		((b) & 0x3ff)
+
+struct __packed pucan_timing_fast {
+	__le16	opcode_channel;
+
+	u8	unused;
+	u8	sjw;		/* Sync Jump Width */
+	u8	tseg2;		/* Timing SEGment 2 */
+	u8	tseg1;		/* Timing SEGment 1 */
+
+	__le16	brp;		/* BaudRate Prescaler */
+};
+
+/* uCAN FILTER_STD command fields */
+#define PUCAN_FLTSTD_ROW_IDX_BITS	6
+
+struct __packed pucan_filter_std {
+	__le16	opcode_channel;
+
+	__le16	idx;
+	__le32	mask;		/* CAN-ID bitmask in idx range */
+};
+
+/* uCAN RX_FRAME_ENABLE command fields */
+#define PUCAN_FLTEXT_ERROR		0x0001
+#define PUCAN_FLTEXT_BUSLOAD		0x0002
+
+struct __packed pucan_filter_ext {
+	__le16	opcode_channel;
+
+	__le16	ext_mask;
+	u32	unused;
+};
+
+/* uCAN received messages global format */
+struct __packed pucan_msg {
+	__le16	size;
+	__le16	type;
+	__le32	ts_low;
+	__le32	ts_high;
+};
+
+/* uCAN flags for CAN/CANFD messages */
+#define PUCAN_MSG_SELF_RECEIVE		0x80
+#define PUCAN_MSG_ERROR_STATE_IND	0x40	/* error state indicator */
+#define PUCAN_MSG_BITRATE_SWITCH	0x20	/* bitrate switch */
+#define PUCAN_MSG_EXT_DATA_LEN		0x10	/* extended data length */
+#define PUCAN_MSG_SINGLE_SHOT		0x08
+#define PUCAN_MSG_LOOPED_BACK		0x04
+#define PUCAN_MSG_EXT_ID		0x02
+#define PUCAN_MSG_RTR			0x01
+
+#define PUCAN_MSG_CHANNEL(m)		((m)->channel_dlc & 0xf)
+#define PUCAN_MSG_DLC(m)		((m)->channel_dlc >> 4)
+
+struct __packed pucan_rx_msg {
+	__le16	size;
+	__le16	type;
+	__le32	ts_low;
+	__le32	ts_high;
+	__le32	tag_low;
+	__le32	tag_high;
+	u8	channel_dlc;
+	u8	client;
+	__le16	flags;
+	__le32	can_id;
+	u8	d[0];
+};
+
+/* uCAN error types */
+#define PUCAN_ERMSG_BIT_ERROR		0
+#define PUCAN_ERMSG_FORM_ERROR		1
+#define PUCAN_ERMSG_STUFF_ERROR		2
+#define PUCAN_ERMSG_OTHER_ERROR		3
+#define PUCAN_ERMSG_ERR_CNT_DEC		4
+
+#define PUCAN_ERMSG_CHANNEL(e)		((e)->channel_type_d & 0x0f)
+#define PUCAN_ERMSG_ERRTYPE(e)		(((e)->channel_type_d >> 4) & 0x07)
+#define PUCAN_ERMSG_D(e)		((e)->channel_type_d & 0x80)
+
+#define PUCAN_ERMSG_ERRCODE(e)		((e)->code_g & 0x7f)
+#define PUCAN_ERMSG_G(e)		((e)->code_g & 0x80)
+
+struct __packed pucan_error_msg {
+	__le16	size;
+	__le16	type;
+	__le32	ts_low;
+	__le32	ts_high;
+	u8	channel_type_d;
+	u8	code_g;
+	u8	tx_err_cnt;
+	u8	rx_err_cnt;
+};
+
+#define PUCAN_STMSG_CHANNEL(e)		((e)->channel_p_w_b & 0x0f)
+#define PUCAN_STMSG_PASSIVE(e)		((e)->channel_p_w_b & 0x20)
+#define PUCAN_STMSG_WARNING(e)		((e)->channel_p_w_b & 0x40)
+#define PUCAN_STMSG_BUSOFF(e)		((e)->channel_p_w_b & 0x80)
+
+struct __packed pucan_status_msg {
+	__le16	size;
+	__le16	type;
+	__le32	ts_low;
+	__le32	ts_high;
+	u8	channel_p_w_b;
+	u8	unused[3];
+};
+
+/* uCAN transmitted message format */
+#define PUCAN_MSG_CHANNEL_DLC(c, d)	(((c) & 0xf) | ((d) << 4))
+
+struct __packed pucan_tx_msg {
+	__le16	size;
+	__le16	type;
+	__le32	tag_low;
+	__le32	tag_high;
+	u8	channel_dlc;
+	u8	client;
+	__le16	flags;
+	__le32	can_id;
+	u8	d[0];
+};
+
+#endif
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb.c b/drivers/net/can/usb/peak_usb/pcan_usb.c
index 4e1659d..5972c7a 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb.c
@@ -858,6 +858,7 @@ struct peak_usb_adapter pcan_usb = {
  	.name = "PCAN-USB",
  	.device_id = PCAN_USB_PRODUCT_ID,
  	.ctrl_count = 1,
+	.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY,
  	.clock = {
  		.freq = PCAN_USB_CRYSTAL_HZ / 2 ,
  	},
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
index c62f48a..7e8ebc4 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_core.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
@@ -37,6 +37,8 @@ MODULE_LICENSE("GPL v2");
  static struct usb_device_id peak_usb_table[] = {
  	{USB_DEVICE(PCAN_USB_VENDOR_ID, PCAN_USB_PRODUCT_ID)},
  	{USB_DEVICE(PCAN_USB_VENDOR_ID, PCAN_USBPRO_PRODUCT_ID)},
+	{USB_DEVICE(PCAN_USB_VENDOR_ID, PCAN_USBFD_PRODUCT_ID)},
+	{USB_DEVICE(PCAN_USB_VENDOR_ID, PCAN_USBPROFD_PRODUCT_ID)},
  	{} /* Terminating entry */
  };
  
@@ -46,6 +48,8 @@ MODULE_DEVICE_TABLE(usb, peak_usb_table);
  static struct peak_usb_adapter *peak_usb_adapters_list[] = {
  	&pcan_usb,
  	&pcan_usb_pro,
+	&pcan_usb_fd,
+	&pcan_usb_pro_fd,
  	NULL,
  };
  
@@ -165,6 +169,21 @@ void peak_usb_get_ts_tv(struct peak_time_ref *time_ref, u32 ts,
  }
  
  /*
+ * post received skb after having set any hw timestamp
+ */
+int peak_usb_netif_rx(struct sk_buff *skb,
+		      struct peak_time_ref *time_ref, u32 ts_low, u32 ts_high)
+{
+	struct skb_shared_hwtstamps *hwts = skb_hwtstamps(skb);
+	struct timeval tv;
+
+	peak_usb_get_ts_tv(time_ref, ts_low, &tv);
+	hwts->hwtstamp = timeval_to_ktime(tv);
+
+	return netif_rx(skb);
+}
+
+/*
   * callback for bulk Rx urb
   */
  static void peak_usb_read_bulk_callback(struct urb *urb)
@@ -253,7 +272,7 @@ static void peak_usb_write_bulk_callback(struct urb *urb)
  	case 0:
  		/* transmission complete */
  		netdev->stats.tx_packets++;
-		netdev->stats.tx_bytes += context->dlc;
+		netdev->stats.tx_bytes += context->data_len;
  
  		/* prevent tx timeout */
  		netdev->trans_start = jiffies;
@@ -289,7 +308,7 @@ static netdev_tx_t peak_usb_ndo_start_xmit(struct sk_buff *skb,
  	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 canfd_frame *cfd = (struct canfd_frame *)skb->data;
  	struct urb *urb;
  	u8 *obuf;
  	int i, err;
@@ -322,7 +341,9 @@ static netdev_tx_t peak_usb_ndo_start_xmit(struct sk_buff *skb,
  	}
  
  	context->echo_index = i;
-	context->dlc = cf->can_dlc;
+
+	/* Note: this works with CANFD frames too */
+	context->data_len = cfd->len;
  
  	usb_anchor_urb(urb, &dev->tx_submitted);
  
@@ -679,19 +700,43 @@ static int peak_usb_set_mode(struct net_device *netdev, enum can_mode mode)
  }
  
  /*
- * candev callback used to set device bitrate.
+ * candev callback used to set device nominal/arbitration bitrate.
   */
  static int peak_usb_set_bittiming(struct net_device *netdev)
  {
  	struct peak_usb_device *dev = netdev_priv(netdev);
-	struct can_bittiming *bt = &dev->can.bittiming;
+	struct peak_usb_adapter *pa = dev->adapter;
  
-	if (dev->adapter->dev_set_bittiming) {
-		int err = dev->adapter->dev_set_bittiming(dev, bt);
+	if (pa->dev_set_bittiming) {
+		struct can_bittiming *bt = &dev->can.bittiming;
+		int err = pa->dev_set_bittiming(dev, bt);
  
  		if (err)
  			netdev_info(netdev, "couldn't set bitrate (err %d)\n",
-				err);
+				    err);
+		return err;
+	}
+
+	return 0;
+}
+
+/*
+ * candev callback used to set device data bitrate.
+ */
+static int peak_usb_set_data_bittiming(struct net_device *netdev)
+{
+	struct peak_usb_device *dev = netdev_priv(netdev);
+	struct peak_usb_adapter *pa = dev->adapter;
+
+	if (pa->dev_set_data_bittiming) {
+		struct can_bittiming *bt = &dev->can.data_bittiming;
+		int err = pa->dev_set_data_bittiming(dev, bt);
+
+		if (err)
+			netdev_info(netdev,
+				    "couldn't set data bitrate (err %d)\n",
+				    err);
+
  		return err;
  	}
  
@@ -750,9 +795,10 @@ static int peak_usb_create_dev(struct peak_usb_adapter *peak_usb_adapter,
  	dev->can.clock = peak_usb_adapter->clock;
  	dev->can.bittiming_const = &peak_usb_adapter->bittiming_const;
  	dev->can.do_set_bittiming = peak_usb_set_bittiming;
+	dev->can.data_bittiming_const = &peak_usb_adapter->data_bittiming_const;
+	dev->can.do_set_data_bittiming = peak_usb_set_data_bittiming;
  	dev->can.do_set_mode = peak_usb_set_mode;
-	dev->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
-				      CAN_CTRLMODE_LISTENONLY;
+	dev->can.ctrlmode_supported = peak_usb_adapter->ctrlmode_supported;
  
  	netdev->netdev_ops = &peak_usb_netdev_ops;
  
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.h b/drivers/net/can/usb/peak_usb/pcan_usb_core.h
index 073b47f..13d44a5 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_core.h
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.h
@@ -25,6 +25,8 @@
  /* supported device ids. */
  #define PCAN_USB_PRODUCT_ID		0x000c
  #define PCAN_USBPRO_PRODUCT_ID		0x000d
+#define PCAN_USBPROFD_PRODUCT_ID	0x0011
+#define PCAN_USBFD_PRODUCT_ID		0x0012
  
  #define PCAN_USB_DRIVER_NAME		"peak_usb"
  
@@ -44,8 +46,10 @@ struct peak_usb_device;
  struct peak_usb_adapter {
  	char *name;
  	u32 device_id;
+	u32 ctrlmode_supported;
  	struct can_clock clock;
  	const struct can_bittiming_const bittiming_const;
+	const struct can_bittiming_const data_bittiming_const;
  	unsigned int ctrl_count;
  
  	int (*intf_probe)(struct usb_interface *intf);
@@ -57,6 +61,8 @@ struct peak_usb_adapter {
  	int (*dev_close)(struct peak_usb_device *dev);
  	int (*dev_set_bittiming)(struct peak_usb_device *dev,
  					struct can_bittiming *bt);
+	int (*dev_set_data_bittiming)(struct peak_usb_device *dev,
+				      struct can_bittiming *bt);
  	int (*dev_set_bus)(struct peak_usb_device *dev, u8 onoff);
  	int (*dev_get_device_id)(struct peak_usb_device *dev, u32 *device_id);
  	int (*dev_decode_buf)(struct peak_usb_device *dev, struct urb *urb);
@@ -80,6 +86,8 @@ struct peak_usb_adapter {
  
  extern struct peak_usb_adapter pcan_usb;
  extern struct peak_usb_adapter pcan_usb_pro;
+extern struct peak_usb_adapter pcan_usb_pro_fd;
+extern struct peak_usb_adapter pcan_usb_fd;
  
  struct peak_time_ref {
  	struct timeval tv_host_0, tv_host;
@@ -92,7 +100,7 @@ struct peak_time_ref {
  struct peak_tx_urb_context {
  	struct peak_usb_device *dev;
  	u32 echo_index;
-	u8 dlc;
+	u8 data_len;
  	struct urb *urb;
  };
  
@@ -139,7 +147,9 @@ 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);
-
+int peak_usb_netif_rx(struct sk_buff *skb,
+		      struct peak_time_ref *time_ref, u32 ts_low, u32 ts_high);
  void peak_usb_async_complete(struct urb *urb);
  void peak_usb_restart_complete(struct peak_usb_device *dev);
+
  #endif
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c
new file mode 100644
index 0000000..57d73b5
--- /dev/null
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c
@@ -0,0 +1,1070 @@
+/*
+ * CAN driver for PEAK System PCAN-USB FD / PCAN-USB Pro FD adapter
+ *
+ * Copyright (C) 2013-2014 Stephane Grosjean <s.grosjean@peak-system.com>
+ *
+ * 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"
+#include "pcan_ucan.h"
+
+MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB FD adapter");
+MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB Pro FD adapter");
+
+#define PCAN_USBPROFD_CHANNEL_COUNT	2
+#define PCAN_USBFD_CHANNEL_COUNT	1
+
+/* PCAN-USB Pro FD adapter internal clock (MHz) */
+#define PCAN_UFD_CRYSTAL_HZ		80000000
+
+#define PCAN_UFD_CMD_BUFFER_SIZE	512
+#define PCAN_UFD_LOSPD_PKT_SIZE		64
+
+/* PCAN-USB Pro FD command timeout (ms.) */
+#define PCAN_UFD_CMD_TIMEOUT_MS		1000
+
+/* PCAN-USB Pro FD rx/tx buffers size */
+#define PCAN_UFD_RX_BUFFER_SIZE		2048
+#define PCAN_UFD_TX_BUFFER_SIZE		512
+
+/* read some versions info from the hw devcie */
+struct __packed pcan_ufd_fw_info {
+	__le16	size_of;	/* sizeof this */
+	__le16	type;		/* type of this structure */
+	u8	hw_type;	/* Type of hardware (HW_TYPE_xxx) */
+	u8	bl_version[3];	/* Bootloader version */
+	u8	hw_version;	/* Hardware version (PCB) */
+	u8	fw_version[3];	/* Firmware version */
+	__le32	dev_id[2];	/* "device id" per CAN */
+	__le32	ser_no;		/* S/N */
+	__le32	flags;		/* special functions */
+};
+
+/* handle device specific info used by the netdevices */
+struct pcan_usb_fd_if {
+	struct peak_usb_device *dev[PCAN_USB_MAX_CHANNEL];
+	struct pcan_ufd_fw_info	fw_info;
+	struct peak_time_ref	time_ref;
+	int			cm_ignore_count;
+	int			dev_opened_count;
+};
+
+/* device information */
+struct pcan_usb_fd_device {
+	struct peak_usb_device	dev;
+	struct pcan_usb_fd_if *	usb_if;
+
+	u8 *	cmd_buffer_addr;
+
+	uint	tx_error_counter;
+	uint	rx_error_counter;
+};
+
+/* Extended USB commands (non uCAN commands) */
+
+/* Clock Modes command */
+#define PCAN_UFD_CMD_CLK_SET		0x80
+
+#define PCAN_UFD_CLK_80MHZ		0x0
+#define PCAN_UFD_CLK_60MHZ		0x1
+#define PCAN_UFD_CLK_40MHZ		0x2
+#define PCAN_UFD_CLK_30MHZ		0x3
+#define PCAN_UFD_CLK_24MHZ		0x4
+#define PCAN_UFD_CLK_20MHZ		0x5
+#define PCAN_UFD_CLK_DEF		PCAN_UFD_CLK_80MHZ
+
+struct __packed pcan_ufd_clock {
+	__le16	opcode_channel;
+
+	u8	mode;
+	u8	unused[5];
+};
+
+/* LED control command */
+#define PCAN_UFD_CMD_LED_SET		0x86
+
+#define PCAN_UFD_LED_DEV		0x00
+#define PCAN_UFD_LED_FAST		0x01
+#define PCAN_UFD_LED_SLOW		0x02
+#define PCAN_UFD_LED_ON			0x03
+#define PCAN_UFD_LED_OFF		0x04
+#define PCAN_UFD_LED_DEF		PCAN_UFD_LED_DEV
+
+struct __packed pcan_ufd_led {
+	__le16	opcode_channel;
+
+	u8	mode;
+	u8	unused[5];
+};
+
+/* Extended usage of uCAN commands CMD_RX_FRAME_xxxABLE for PCAN-USB Pro FD */
+#define PCAN_UFD_FLTEXT_CALIBRATION	0x8000
+
+struct __packed pcan_ufd_filter_ext {
+	__le16	opcode_channel;
+
+	__le16	ext_mask;
+	u16	unused;
+	__le16	usb_mask;
+};
+
+/* Extended usage of uCAN messages for PCAN-USB Pro FD */
+#define PCAN_UFD_MSG_CALIBRATION	0x100
+
+struct __packed pcan_ufd_ts_msg {
+	__le16	size;
+	__le16	type;
+	__le32	ts_low;
+	__le32	ts_high;
+	__le16	usb_frame_index;
+	u16	unused;
+};
+
+#define PCAN_UFD_MSG_OVERRUN		0x101
+
+#define PCAN_UFD_OVMSG_CHANNEL(o)	((o)->channel & 0xf)
+
+struct __packed pcan_ufd_ovr_msg {
+	__le16	size;
+	__le16	type;
+	__le32	ts_low;
+	__le32	ts_high;
+	u8	channel;
+	u8	unused[3];
+};
+
+/* Clock mode frequency values */
+static const u32 pcan_usb_fd_clk_freq[6] = {
+	[PCAN_UFD_CLK_80MHZ] = 80000000,
+	[PCAN_UFD_CLK_60MHZ] = 60000000,
+	[PCAN_UFD_CLK_40MHZ] = 40000000,
+	[PCAN_UFD_CLK_30MHZ] = 30000000,
+	[PCAN_UFD_CLK_24MHZ] = 24000000,
+	[PCAN_UFD_CLK_20MHZ] = 20000000
+};
+
+/* build the opcode_channel field with respect to the correct endianess */
+static inline __le16 pucan_cmd_opcode_channel(int opcode, int channel)
+{
+	return cpu_to_le16(PUCAN_CMD_OPCODE_CHANNEL(opcode, channel));
+}
+
+/* return a device USB interface */
+static inline
+struct pcan_usb_fd_if *pcan_usb_fd_dev_if(struct peak_usb_device *dev)
+{
+	struct pcan_usb_fd_device *pdev =
+			container_of(dev, struct pcan_usb_fd_device, dev);
+	return pdev->usb_if;
+}
+
+/* return a device USB commands buffer */
+static inline void *pcan_usb_fd_cmd_buffer(struct peak_usb_device *dev)
+{
+	struct pcan_usb_fd_device *pdev =
+			container_of(dev, struct pcan_usb_fd_device, dev);
+	return pdev->cmd_buffer_addr;
+}
+
+/* send PCAN-USB Pro FD commands synchronously */
+static int pcan_usb_fd_send_cmd(struct peak_usb_device *dev, void *cmd_tail)
+{
+	void *cmd_head = pcan_usb_fd_cmd_buffer(dev);
+	int err;
+	u8 *packet_ptr;
+	int i, n = 1, packet_len;
+	ptrdiff_t cmd_len;
+
+	/* usb device unregistered? */
+	if (!(dev->state & PCAN_USB_STATE_CONNECTED))
+		return 0;
+
+	/*
+	 * if a packet is not filled completely by commands, the command list
+	 * is terminated with an "end of collection" record.
+	 */
+	cmd_len = cmd_tail - cmd_head;
+	if (cmd_len <= (PCAN_UFD_CMD_BUFFER_SIZE - sizeof(u64))) {
+		memset(cmd_tail, 0xff, sizeof(u64));
+		cmd_len += sizeof(u64);
+	}
+
+	packet_ptr = cmd_head;
+
+	/* firmware is not able to re-assemble 512 bytes buffer in full-speed */
+	if ((dev->udev->speed != USB_SPEED_HIGH) &&
+	    (cmd_len > PCAN_UFD_LOSPD_PKT_SIZE)) {
+		packet_len = PCAN_UFD_LOSPD_PKT_SIZE;
+		n += cmd_len / packet_len;
+	} else {
+		packet_len = cmd_len;
+	}
+
+	for (i = 0; i < n; i++) {
+		err = usb_bulk_msg(dev->udev,
+				   usb_sndbulkpipe(dev->udev,
+						   PCAN_USBPRO_EP_CMDOUT),
+				   packet_ptr, packet_len,
+				   NULL, PCAN_UFD_CMD_TIMEOUT_MS);
+		if (err) {
+			netdev_err(dev->netdev,
+				   "sending command failure: %d\n", err);
+			break;
+		}
+
+		packet_ptr += packet_len;
+	}
+
+	return err;
+}
+
+static int pcan_usb_fd_set_bus(struct peak_usb_device *dev, u8 onoff)
+{
+	struct pucan_command *cmd = pcan_usb_fd_cmd_buffer(dev);
+	u16 opcode;
+
+	if (onoff) {
+		opcode = (dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) ?
+				PUCAN_CMD_LISTEN_ONLY_MODE :
+				PUCAN_CMD_NORMAL_MODE;
+	} else {
+		opcode = PUCAN_CMD_RESET_MODE;
+	}
+
+	cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, opcode);
+
+	/* send the command */
+	return pcan_usb_fd_send_cmd(dev, ++cmd);
+}
+
+/*
+ * set filtering masks:
+ *
+ *	idx  in range [0..63] selects a row #idx, all rows otherwise
+ *	mask in range [0..0xffffffff] defines up to 32 CANIDs in the row(s)
+ *
+ *	Each bit of this 64 x 32 bits array defines a CANID value:
+ *
+ *	bit[i,j] = 1 implies that CANID=(i x 32)+j will be received, while
+ *	bit[i,j] = 0 implies that CANID=(i x 32)+j will be discarded.
+ */
+static int pcan_usb_fd_set_filter_std(struct peak_usb_device *dev, int idx,
+				      u32 mask)
+{
+	struct pucan_filter_std *cmd = pcan_usb_fd_cmd_buffer(dev);
+	int i, n;
+
+	/* select all rows when idx is out of range [0..63] */
+	if ((idx < 0) || (idx >= (1 << PUCAN_FLTSTD_ROW_IDX_BITS))) {
+		n = 1 << PUCAN_FLTSTD_ROW_IDX_BITS;
+		idx = 0;
+
+	/* select the row (and only the row) otherwise */
+	} else {
+		n = idx + 1;
+	}
+
+	for (i = idx; i < n; i++, cmd++) {
+		cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx,
+							PUCAN_CMD_FILTER_STD);
+		cmd->idx = cpu_to_le16(i);
+		cmd->mask = cpu_to_le32(mask);
+	}
+
+	/* send the command */
+	return pcan_usb_fd_send_cmd(dev, cmd);
+}
+
+/*
+ * set/unset notifications filter:
+ *
+ *	onoff	sets(1)/unset(0) notifications
+ *	mask	each bit defines a kind of notification to set/unset
+ */
+static int pcan_usb_fd_set_filter_ext(struct peak_usb_device *dev,
+				      int onoff, u16 ext_mask, u16 usb_mask)
+{
+	struct pcan_ufd_filter_ext *cmd = pcan_usb_fd_cmd_buffer(dev);
+
+	cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx,
+					(onoff) ? PUCAN_CMD_RX_FRAME_ENABLE :
+						  PUCAN_CMD_RX_FRAME_DISABLE);
+
+	cmd->ext_mask = cpu_to_le16(ext_mask);
+	cmd->usb_mask = cpu_to_le16(usb_mask);
+
+	/* send the command */
+	return pcan_usb_fd_send_cmd(dev, ++cmd);
+}
+
+/* setup LED control */
+static int pcan_usb_fd_set_can_led(struct peak_usb_device *dev, u8 led_mode)
+{
+	struct pcan_ufd_led *cmd = pcan_usb_fd_cmd_buffer(dev);
+
+	cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx,
+						       PCAN_UFD_CMD_LED_SET);
+	cmd->mode = led_mode;
+
+	/* send the command */
+	return pcan_usb_fd_send_cmd(dev, ++cmd);
+}
+
+/* set CAN clock domain */
+static int pcan_usb_fd_set_clock_domain(struct peak_usb_device *dev,
+					u8 clk_mode)
+{
+	struct pcan_ufd_clock *cmd = pcan_usb_fd_cmd_buffer(dev);
+
+	cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx,
+						       PCAN_UFD_CMD_CLK_SET);
+	cmd->mode = clk_mode;
+
+	/* send the command */
+	return pcan_usb_fd_send_cmd(dev, ++cmd);
+}
+
+/* set bittiming for CAN and CAN-FD header */
+static int pcan_usb_fd_set_bittiming_slow(struct peak_usb_device *dev,
+					  struct can_bittiming *bt)
+{
+	struct pucan_timing_slow *cmd = pcan_usb_fd_cmd_buffer(dev);
+
+	cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx,
+						       PUCAN_CMD_TIMING_SLOW);
+	cmd->sjw_t = PUCAN_TSLOW_SJW_T(bt->sjw - 1,
+				dev->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES);
+
+	cmd->tseg2 = PUCAN_TSLOW_TSEG2(bt->phase_seg2 - 1);
+	cmd->tseg1 = PUCAN_TSLOW_TSEG1(bt->prop_seg + bt->phase_seg1 - 1);
+	cmd->brp = cpu_to_le16(PUCAN_TSLOW_BRP(bt->brp - 1));
+
+	cmd->ewl = 96;	/* default */
+
+	/* send the command */
+	return pcan_usb_fd_send_cmd(dev, ++cmd);
+}
+
+/* set CAN-FD bittiming for data */
+static int pcan_usb_fd_set_bittiming_fast(struct peak_usb_device *dev,
+					  struct can_bittiming *bt)
+{
+	struct pucan_timing_fast *cmd = pcan_usb_fd_cmd_buffer(dev);
+
+	cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx,
+						       PUCAN_CMD_TIMING_FAST);
+	cmd->sjw = PUCAN_TFAST_SJW(bt->sjw - 1);
+	cmd->tseg2 = PUCAN_TFAST_TSEG2(bt->phase_seg2 - 1);
+	cmd->tseg1 = PUCAN_TFAST_TSEG1(bt->prop_seg + bt->phase_seg1 - 1);
+	cmd->brp = cpu_to_le16(PUCAN_TFAST_BRP(bt->brp - 1));
+
+	/* send the command */
+	return pcan_usb_fd_send_cmd(dev, ++cmd);
+}
+
+/*
+ * handle restart but in asynchronously way
+ * (uses PCAN-USB Pro code to complete)
+ */
+static int pcan_usb_fd_restart_async(struct peak_usb_device *dev,
+				     struct urb *urb, u8 *buf)
+{
+	struct pucan_command *cmd = (struct pucan_command *)buf;
+
+	cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx,
+				(dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) ?
+						PUCAN_CMD_LISTEN_ONLY_MODE :
+						PUCAN_CMD_NORMAL_MODE);
+	/* EOC */
+	memset(cmd+1, 0xff, sizeof(struct pucan_command));
+
+	usb_fill_bulk_urb(urb, dev->udev,
+			  usb_sndbulkpipe(dev->udev, PCAN_USBPRO_EP_CMDOUT),
+			  buf, 2 * sizeof(struct pucan_command),
+			  pcan_usb_pro_restart_complete, dev);
+
+	return usb_submit_urb(urb, GFP_ATOMIC);
+}
+
+static int pcan_usb_fd_drv_loaded(struct peak_usb_device *dev, int loaded)
+{
+	struct pcan_usb_fd_device *pdev =
+			container_of(dev, struct pcan_usb_fd_device, dev);
+
+	pdev->cmd_buffer_addr[0] = 0;
+	pdev->cmd_buffer_addr[1] = !!loaded;
+
+	return pcan_usb_pro_send_req(dev,
+				PCAN_USBPRO_REQ_FCT,
+				PCAN_USBPRO_FCT_DRVLD,
+				pdev->cmd_buffer_addr,
+				PCAN_USBPRO_FCT_DRVLD_REQ_LEN);
+}
+
+static int pcan_usb_fd_decode_canmsg(struct pcan_usb_fd_if *usb_if,
+				     struct pucan_msg *rx_msg)
+{
+	struct pucan_rx_msg *rm = (struct pucan_rx_msg *)rx_msg;
+	struct peak_usb_device *dev = usb_if->dev[PUCAN_MSG_CHANNEL(rm)];
+	struct net_device *netdev = dev->netdev;
+	struct canfd_frame *cfd;
+	struct sk_buff *skb;
+	const u16 rx_msg_flags = le16_to_cpu(rm->flags);
+
+	if (rx_msg_flags & PUCAN_MSG_EXT_DATA_LEN) {
+		/* CANFD frame case */
+		skb = alloc_canfd_skb(netdev, &cfd);
+		if (!skb)
+			return -ENOMEM;
+
+		if (rx_msg_flags & PUCAN_MSG_BITRATE_SWITCH)
+			cfd->flags |= CANFD_BRS;
+
+		if (rx_msg_flags & PUCAN_MSG_ERROR_STATE_IND)
+			cfd->flags |= CANFD_ESI;
+
+		cfd->len = can_dlc2len(PUCAN_MSG_DLC(rm));
+	} else {
+		/* CANFD frame case */
+		skb = alloc_can_skb(netdev, (struct can_frame **)&cfd);
+		if (!skb)
+			return -ENOMEM;
+
+		cfd->len = get_can_dlc(PUCAN_MSG_DLC(rm));
+	}
+
+	cfd->can_id = le32_to_cpu(rm->can_id);
+
+	if (rx_msg_flags & PUCAN_MSG_EXT_ID)
+		cfd->can_id |= CAN_EFF_FLAG;
+
+	if (rx_msg_flags & PUCAN_MSG_RTR)
+		cfd->can_id |= CAN_RTR_FLAG;
+	else
+		memcpy(cfd->data, rm->d, cfd->len);
+
+	peak_usb_netif_rx(skb, &usb_if->time_ref,
+			  le32_to_cpu(rm->ts_low), le32_to_cpu(rm->ts_high));
+
+	netdev->stats.rx_packets++;
+	netdev->stats.rx_bytes += cfd->len;
+
+	return 0;
+}
+
+/* handle uCAN status message */
+static int pcan_usb_fd_decode_status(struct pcan_usb_fd_if *usb_if,
+				     struct pucan_msg *rx_msg)
+{
+	struct pucan_status_msg *st = (struct pucan_status_msg *)rx_msg;
+	struct peak_usb_device *dev = usb_if->dev[PUCAN_STMSG_CHANNEL(st)];
+	struct pcan_usb_fd_device *pdev =
+			container_of(dev, struct pcan_usb_fd_device, dev);
+	enum can_state new_state = CAN_STATE_ERROR_ACTIVE;
+	struct net_device *netdev = dev->netdev;
+	struct can_frame *cf;
+	struct sk_buff *skb;
+
+	/* nothing should be sent while in BUS_OFF state */
+	if (dev->can.state == CAN_STATE_BUS_OFF)
+		return 0;
+
+	if (PUCAN_STMSG_BUSOFF(st)) {
+		new_state = CAN_STATE_BUS_OFF;
+	} else if (PUCAN_STMSG_PASSIVE(st)) {
+		new_state = CAN_STATE_ERROR_PASSIVE;
+	} else if (PUCAN_STMSG_WARNING(st)) {
+		new_state = CAN_STATE_ERROR_WARNING;
+	} else {
+		/* no error bit (so, no error skb, back to active state) */
+		dev->can.state = CAN_STATE_ERROR_ACTIVE;
+		pdev->tx_error_counter = 0;
+		pdev->rx_error_counter = 0;
+		return 0;
+	}
+
+	/* donot post any error if current state didn't change */
+	if (dev->can.state == new_state)
+		return 0;
+
+	/* allocate an skb to store the error frame */
+	skb = alloc_can_err_skb(netdev, &cf);
+	if (!skb)
+		return -ENOMEM;
+
+	switch (new_state) {
+	case CAN_STATE_BUS_OFF:
+		cf->can_id |= CAN_ERR_BUSOFF;
+		can_bus_off(netdev);
+		break;
+
+	case CAN_STATE_ERROR_PASSIVE:
+		cf->can_id |= CAN_ERR_CRTL;
+		if (pdev->rx_error_counter > 127)
+			cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE;
+		if (pdev->tx_error_counter > 127)
+			cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE;
+
+		dev->can.can_stats.error_passive++;
+		break;
+
+	case CAN_STATE_ERROR_WARNING:
+		cf->can_id |= CAN_ERR_CRTL;
+		if (pdev->rx_error_counter > 96)
+			cf->data[1] |= CAN_ERR_CRTL_RX_WARNING;
+		if (pdev->tx_error_counter > 96)
+			cf->data[1] |= CAN_ERR_CRTL_TX_WARNING;
+
+		dev->can.can_stats.error_warning++;
+		break;
+
+	default:
+		/* default case never happens, only for warnings */
+		new_state = CAN_STATE_ERROR_ACTIVE;
+
+	case CAN_STATE_ERROR_ACTIVE:	/* fallthrough */
+		pdev->tx_error_counter = 0;
+		pdev->rx_error_counter = 0;
+		break;
+	}
+
+	dev->can.state = new_state;
+
+	peak_usb_netif_rx(skb, &usb_if->time_ref,
+			  le32_to_cpu(st->ts_low), le32_to_cpu(st->ts_high));
+
+	netdev->stats.rx_packets++;
+	netdev->stats.rx_bytes += cf->can_dlc;
+
+	return 0;
+}
+
+/* handle uCAN error message */
+static int pcan_usb_fd_decode_error(struct pcan_usb_fd_if *usb_if,
+				    struct pucan_msg *rx_msg)
+{
+	struct pucan_error_msg *er = (struct pucan_error_msg *)rx_msg;
+	struct peak_usb_device *dev = usb_if->dev[PUCAN_ERMSG_CHANNEL(er)];
+	struct pcan_usb_fd_device *pdev =
+			container_of(dev, struct pcan_usb_fd_device, dev);
+
+	/* keep a trace of tx and rx error counters for later use */
+	pdev->tx_error_counter = er->tx_err_cnt;
+	pdev->rx_error_counter = er->rx_err_cnt;
+
+	return 0;
+}
+
+/* handle uCAN overrun message */
+static int pcan_usb_fd_decode_overrun(struct pcan_usb_fd_if *usb_if,
+				      struct pucan_msg *rx_msg)
+{
+	struct pcan_ufd_ovr_msg *ov = (struct pcan_ufd_ovr_msg *)rx_msg;
+	struct peak_usb_device *dev = usb_if->dev[PCAN_UFD_OVMSG_CHANNEL(ov)];
+	struct net_device *netdev = dev->netdev;
+	struct can_frame *cf;
+	struct sk_buff *skb;
+
+	/* allocate an skb to store the error frame */
+	skb = alloc_can_err_skb(netdev, &cf);
+	if (!skb)
+		return -ENOMEM;
+
+	cf->can_id |= CAN_ERR_CRTL;
+	cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
+
+	peak_usb_netif_rx(skb, &usb_if->time_ref,
+			  le32_to_cpu(ov->ts_low), le32_to_cpu(ov->ts_high));
+
+	netdev->stats.rx_over_errors++;
+	netdev->stats.rx_errors++;
+
+	return 0;
+}
+
+/* handle USB calibration message */
+static void pcan_usb_fd_decode_ts(struct pcan_usb_fd_if *usb_if,
+				  struct pucan_msg *rx_msg)
+{
+	struct pcan_ufd_ts_msg *ts = (struct pcan_ufd_ts_msg *)rx_msg;
+
+	/* should wait until clock is stabilized */
+	if (usb_if->cm_ignore_count > 0)
+		usb_if->cm_ignore_count--;
+	else
+		peak_usb_set_ts_now(&usb_if->time_ref, le32_to_cpu(ts->ts_low));
+}
+
+/* callback for bulk IN urb */
+static int pcan_usb_fd_decode_buf(struct peak_usb_device *dev, struct urb *urb)
+{
+	struct pcan_usb_fd_if *usb_if = pcan_usb_fd_dev_if(dev);
+	struct net_device *netdev = dev->netdev;
+	struct pucan_msg *rx_msg;
+	u8 *msg_ptr, *msg_end;
+	int err = 0;
+
+	/* loop reading all the records from the incoming message */
+	msg_ptr = urb->transfer_buffer;
+	msg_end = urb->transfer_buffer + urb->actual_length;
+	for (; msg_ptr < msg_end;) {
+		u16 rx_msg_type, rx_msg_size;
+
+		rx_msg = (struct pucan_msg *)msg_ptr;
+		if (!rx_msg->size) {
+			/* null packet found: end of list */
+			break;
+		}
+
+		rx_msg_size = le16_to_cpu(rx_msg->size);
+		rx_msg_type = le16_to_cpu(rx_msg->type);
+
+		/* check if the record goes out of current packet */
+		if (msg_ptr + rx_msg_size > msg_end) {
+			netdev_err(netdev,
+				   "got frag rec: should inc usb rx buf sze\n");
+			err = -EBADMSG;
+			break;
+		}
+
+		switch (rx_msg_type) {
+		case PUCAN_MSG_CAN_RX:
+			err = pcan_usb_fd_decode_canmsg(usb_if, rx_msg);
+			if (err < 0)
+				goto fail;
+			break;
+
+		case PCAN_UFD_MSG_CALIBRATION:
+			pcan_usb_fd_decode_ts(usb_if, rx_msg);
+			break;
+
+		case PUCAN_MSG_ERROR:
+			err = pcan_usb_fd_decode_error(usb_if, rx_msg);
+			if (err < 0)
+				goto fail;
+			break;
+
+		case PUCAN_MSG_STATUS:
+			err = pcan_usb_fd_decode_status(usb_if, rx_msg);
+			if (err < 0)
+				goto fail;
+			break;
+
+		case PCAN_UFD_MSG_OVERRUN:
+			err = pcan_usb_fd_decode_overrun(usb_if, rx_msg);
+			if (err < 0)
+				goto fail;
+			break;
+
+		default:
+			netdev_err(netdev,
+				   "unhandled msg type 0x%02x (%d): ignored\n",
+				   rx_msg_type, rx_msg_type);
+			break;
+		}
+
+		msg_ptr += rx_msg_size;
+	}
+
+fail:
+	if (err)
+		pcan_dump_mem("received msg",
+			      urb->transfer_buffer, urb->actual_length);
+	return err;
+}
+
+/* CAN/CANFD frames encoding callback */
+static int pcan_usb_fd_encode_msg(struct peak_usb_device *dev,
+				  struct sk_buff *skb, u8 *obuf, size_t *size)
+{
+	struct pucan_tx_msg *tx_msg = (struct pucan_tx_msg *)obuf;
+	struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
+	u16 tx_msg_size, tx_msg_flags;
+	u8 can_dlc;
+
+	tx_msg_size = ALIGN(sizeof(struct pucan_tx_msg) + cfd->len, 4);
+	tx_msg->size = cpu_to_le16(tx_msg_size);
+	tx_msg->type = cpu_to_le16(PUCAN_MSG_CAN_TX);
+
+	tx_msg_flags = 0;
+	if (cfd->can_id & CAN_EFF_FLAG) {
+		tx_msg_flags |= PUCAN_MSG_EXT_ID;
+		tx_msg->can_id = cpu_to_le32(cfd->can_id & CAN_EFF_MASK);
+	} else {
+		tx_msg->can_id = cpu_to_le32(cfd->can_id & CAN_SFF_MASK);
+	}
+
+	if (skb->len == CANFD_MTU) {
+		/* considering a CANFD frame */
+		can_dlc = can_len2dlc(cfd->len);
+
+		tx_msg_flags |= PUCAN_MSG_EXT_DATA_LEN;
+
+		if (cfd->flags & CANFD_BRS)
+			tx_msg_flags |= PUCAN_MSG_BITRATE_SWITCH;
+
+		if (cfd->flags & CANFD_ESI)
+			tx_msg_flags |= PUCAN_MSG_ERROR_STATE_IND;
+	} else {
+		/* CAND 2.0 frames */
+		can_dlc = cfd->len;
+
+		if (cfd->can_id & CAN_RTR_FLAG)
+			tx_msg_flags |= PUCAN_MSG_RTR;
+	}
+
+	tx_msg->flags = cpu_to_le16(tx_msg_flags);
+	tx_msg->channel_dlc = PUCAN_MSG_CHANNEL_DLC(dev->ctrl_idx, can_dlc);
+	memcpy(tx_msg->d, cfd->data, cfd->len);
+
+	/* add null size message to tag the end (messages are 32-bits aligned)*/
+	tx_msg = (struct pucan_tx_msg *)(obuf + tx_msg_size);
+
+	tx_msg->size = 0;
+
+	/* set the whole size of the USB packet to send */
+	*size = tx_msg_size + sizeof(u32);
+
+	return 0;
+}
+
+/* start the interface (last chance before set bus on) */
+static int pcan_usb_fd_start(struct peak_usb_device *dev)
+{
+	struct pcan_usb_fd_device *pdev =
+			container_of(dev, struct pcan_usb_fd_device, dev);
+	int err;
+
+	/* set filter mode: all acceptance */
+	err = pcan_usb_fd_set_filter_std(dev, -1, 0xffffffff);
+	if (err)
+		return err;
+
+	/* opening first device: */
+	if (pdev->usb_if->dev_opened_count == 0) {
+		/* reset time_ref */
+		peak_usb_init_time_ref(&pdev->usb_if->time_ref,
+				       &pcan_usb_pro_fd);
+
+		/* enable USB calibration messages */
+		err = pcan_usb_fd_set_filter_ext(dev, 1,
+						 PUCAN_FLTEXT_ERROR,
+						 PCAN_UFD_FLTEXT_CALIBRATION);
+	}
+
+	pdev->usb_if->dev_opened_count++;
+
+	/* reset cached error counters */
+	pdev->tx_error_counter = 0;
+	pdev->rx_error_counter = 0;
+
+	return err;
+}
+
+/* stop interface (last chance before set bus off) */
+static int pcan_usb_fd_stop(struct peak_usb_device *dev)
+{
+	struct pcan_usb_fd_device *pdev =
+			container_of(dev, struct pcan_usb_fd_device, dev);
+
+	/* turn off special msgs for that interface if no other dev opened */
+	if (pdev->usb_if->dev_opened_count == 1)
+		pcan_usb_fd_set_filter_ext(dev, 0,
+					   PUCAN_FLTEXT_ERROR,
+					   PCAN_UFD_FLTEXT_CALIBRATION);
+	pdev->usb_if->dev_opened_count--;
+
+	return 0;
+}
+
+/* called when probing, to initialize a device object */
+static int pcan_usb_fd_init(struct peak_usb_device *dev)
+{
+	struct pcan_usb_fd_device *pdev =
+			container_of(dev, struct pcan_usb_fd_device, dev);
+	int i, err = -ENOMEM;
+
+	/* do this for 1st channel only */
+	if (!dev->prev_siblings) {
+		/* allocate netdevices common structure attached to first one */
+		pdev->usb_if = kzalloc(sizeof(*pdev->usb_if), GFP_KERNEL);
+		if (!pdev->usb_if)
+			goto err_out;
+
+		/* allocate command buffer once for all for the interface */
+		pdev->cmd_buffer_addr = kmalloc(PCAN_UFD_CMD_BUFFER_SIZE,
+						GFP_KERNEL);
+		if (!pdev->cmd_buffer_addr)
+			goto err_out_1;
+
+		/* number of ts msgs to ignore before taking one into account */
+		pdev->usb_if->cm_ignore_count = 5;
+
+		err = pcan_usb_pro_send_req(dev, PCAN_USBPRO_REQ_INFO,
+					    PCAN_USBPRO_INFO_FW,
+					    &pdev->usb_if->fw_info,
+					    sizeof(pdev->usb_if->fw_info));
+		if (err) {
+			dev_err(dev->netdev->dev.parent,
+				"unable to read %s firmware info (err %d)\n",
+				dev->adapter->name, err);
+			goto err_out_2;
+		}
+
+		/*
+		 * explicit use of dev_xxx() instead of netdev_xxx() here:
+		 * information displayed are related to the device itself, not
+		 * to the canx (channel) device.
+		 */
+		dev_info(dev->netdev->dev.parent,
+			 "PEAK-System %s v%u fw v%u.%u.%u (%u channels)\n",
+			 dev->adapter->name, pdev->usb_if->fw_info.hw_version,
+			 pdev->usb_if->fw_info.fw_version[0],
+			 pdev->usb_if->fw_info.fw_version[1],
+			 pdev->usb_if->fw_info.fw_version[2],
+			 dev->adapter->ctrl_count);
+
+		/* tell the hardware the can driver is running */
+		err = pcan_usb_fd_drv_loaded(dev, 1);
+		if (err) {
+			dev_err(dev->netdev->dev.parent,
+				"unable to tell %s driver is loaded (err %d)\n",
+				dev->adapter->name, err);
+			goto err_out_2;
+		}
+
+	} else {
+		/* otherwise, simply copy previous sibling's values */
+		struct pcan_usb_fd_device *ppdev =
+			container_of(dev->prev_siblings,
+				     struct pcan_usb_fd_device, dev);
+
+		pdev->usb_if = ppdev->usb_if;
+		pdev->cmd_buffer_addr = ppdev->cmd_buffer_addr;
+	}
+
+	pdev->usb_if->dev[dev->ctrl_idx] = dev;
+	dev->device_number =
+		le32_to_cpu(pdev->usb_if->fw_info.dev_id[dev->ctrl_idx]);
+
+	/* set clock domain */
+	for (i = 0; i < ARRAY_SIZE(pcan_usb_fd_clk_freq); i++)
+		if (dev->adapter->clock.freq == pcan_usb_fd_clk_freq[i])
+			break;
+
+	if (i >= ARRAY_SIZE(pcan_usb_fd_clk_freq)) {
+		dev_warn(dev->netdev->dev.parent,
+			 "incompatible clock frequencies\n");
+		err = -EINVAL;
+		goto err_out_2;
+	}
+
+	pcan_usb_fd_set_clock_domain(dev, i);
+
+	/* set LED in default state (end of init phase) */
+	pcan_usb_fd_set_can_led(dev, PCAN_UFD_LED_DEF);
+
+	return 0;
+
+err_out_2:
+	kfree(pdev->cmd_buffer_addr);
+err_out_1:
+	kfree(pdev->usb_if);
+err_out:
+	return err;
+}
+
+/* called when driver module is being unloaded */
+static void pcan_usb_fd_exit(struct peak_usb_device *dev)
+{
+	struct pcan_usb_fd_device *pdev =
+			container_of(dev, struct pcan_usb_fd_device, dev);
+
+	/*
+	 * when rmmod called before unplug and if down, should reset things
+	 * before leaving
+	 */
+	if (dev->can.state != CAN_STATE_STOPPED) {
+		/* set bus off on the corresponding channel */
+		pcan_usb_fd_set_bus(dev, 0);
+	}
+
+	/* switch off corresponding CAN LEDs */
+	pcan_usb_fd_set_can_led(dev, PCAN_UFD_LED_OFF);
+
+	/* if channel #0 (only) */
+	if (dev->ctrl_idx == 0) {
+		/* turn off calibration message if any device were opened */
+		if (pdev->usb_if->dev_opened_count > 0)
+			pcan_usb_fd_set_filter_ext(dev, 0,
+						   PUCAN_FLTEXT_ERROR,
+						   PCAN_UFD_FLTEXT_CALIBRATION);
+
+		/* tell USB adapter that the driver is being unloaded */
+		pcan_usb_fd_drv_loaded(dev, 0);
+	}
+}
+
+/* called when the USB adapter is unplugged */
+static void pcan_usb_fd_free(struct peak_usb_device *dev)
+{
+	/* last device: can free shared objects now */
+	if (!dev->prev_siblings && !dev->next_siblings) {
+		struct pcan_usb_fd_device *pdev =
+			container_of(dev, struct pcan_usb_fd_device, dev);
+
+		/* free commands buffer */
+		kfree(pdev->cmd_buffer_addr);
+
+		/* free usb interface object */
+		kfree(pdev->usb_if);
+	}
+}
+
+/* describes the PCAN-USB FD adapter */
+struct peak_usb_adapter pcan_usb_fd = {
+	.name = "PCAN-USB FD",
+	.device_id = PCAN_USBFD_PRODUCT_ID,
+	.ctrl_count = PCAN_USBFD_CHANNEL_COUNT,
+	.ctrlmode_supported = CAN_CTRLMODE_FD |
+			CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY,
+	.clock = {
+		.freq = PCAN_UFD_CRYSTAL_HZ,
+	},
+	.bittiming_const = {
+		.name = "pcan_usb_fd",
+		.tseg1_min = 1,
+		.tseg1_max = 64,
+		.tseg2_min = 1,
+		.tseg2_max = 16,
+		.sjw_max = 16,
+		.brp_min = 1,
+		.brp_max = 1024,
+		.brp_inc = 1,
+	},
+	.data_bittiming_const = {
+		.name = "pcan_usb_fd",
+		.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_fd_device),
+
+	/* timestamps usage */
+	.ts_used_bits = 32,
+	.ts_period = 1000000, /* calibration period in ts. */
+	.us_per_ts_scale = 1, /* us = (ts * scale) >> shift */
+	.us_per_ts_shift = 0,
+
+	/* give here messages in/out endpoints */
+	.ep_msg_in = PCAN_USBPRO_EP_MSGIN,
+	.ep_msg_out = {PCAN_USBPRO_EP_MSGOUT_0},
+
+	/* size of rx/tx usb buffers */
+	.rx_buffer_size = PCAN_UFD_RX_BUFFER_SIZE,
+	.tx_buffer_size = PCAN_UFD_TX_BUFFER_SIZE,
+
+	/* device callbacks */
+	.intf_probe = pcan_usb_pro_probe,	/* same as PCAN-USB Pro */
+	.dev_init = pcan_usb_fd_init,
+
+	.dev_exit = pcan_usb_fd_exit,
+	.dev_free = pcan_usb_fd_free,
+	.dev_set_bus = pcan_usb_fd_set_bus,
+	.dev_set_bittiming = pcan_usb_fd_set_bittiming_slow,
+	.dev_set_data_bittiming = pcan_usb_fd_set_bittiming_fast,
+	.dev_decode_buf = pcan_usb_fd_decode_buf,
+	.dev_start = pcan_usb_fd_start,
+	.dev_stop = pcan_usb_fd_stop,
+	.dev_restart_async = pcan_usb_fd_restart_async,
+	.dev_encode_msg = pcan_usb_fd_encode_msg,
+};
+
+/* describes the PCAN-USB Pro FD adapter */
+struct peak_usb_adapter pcan_usb_pro_fd = {
+	.name = "PCAN-USB Pro FD",
+	.device_id = PCAN_USBPROFD_PRODUCT_ID,
+	.ctrl_count = PCAN_USBPROFD_CHANNEL_COUNT,
+	.ctrlmode_supported = CAN_CTRLMODE_FD |
+			CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY,
+	.clock = {
+		.freq = PCAN_UFD_CRYSTAL_HZ,
+	},
+	.bittiming_const = {
+		.name = "pcan_usb_pro_fd",
+		.tseg1_min = 1,
+		.tseg1_max = 64,
+		.tseg2_min = 1,
+		.tseg2_max = 16,
+		.sjw_max = 16,
+		.brp_min = 1,
+		.brp_max = 1024,
+		.brp_inc = 1,
+	},
+	.data_bittiming_const = {
+		.name = "pcan_usb_pro_fd",
+		.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_fd_device),
+
+	/* timestamps usage */
+	.ts_used_bits = 32,
+	.ts_period = 1000000, /* calibration period in ts. */
+	.us_per_ts_scale = 1, /* us = (ts * scale) >> shift */
+	.us_per_ts_shift = 0,
+
+	/* give here messages in/out endpoints */
+	.ep_msg_in = PCAN_USBPRO_EP_MSGIN,
+	.ep_msg_out = {PCAN_USBPRO_EP_MSGOUT_0, PCAN_USBPRO_EP_MSGOUT_1},
+
+	/* size of rx/tx usb buffers */
+	.rx_buffer_size = PCAN_UFD_RX_BUFFER_SIZE,
+	.tx_buffer_size = PCAN_UFD_TX_BUFFER_SIZE,
+
+	/* device callbacks */
+	.intf_probe = pcan_usb_pro_probe,	/* same as PCAN-USB Pro */
+	.dev_init = pcan_usb_fd_init,
+
+	.dev_exit = pcan_usb_fd_exit,
+	.dev_free = pcan_usb_fd_free,
+	.dev_set_bus = pcan_usb_fd_set_bus,
+	.dev_set_bittiming = pcan_usb_fd_set_bittiming_slow,
+	.dev_set_data_bittiming = pcan_usb_fd_set_bittiming_fast,
+	.dev_decode_buf = pcan_usb_fd_decode_buf,
+	.dev_start = pcan_usb_fd_start,
+	.dev_stop = pcan_usb_fd_stop,
+	.dev_restart_async = pcan_usb_fd_restart_async,
+	.dev_encode_msg = pcan_usb_fd_encode_msg,
+};
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
index 4cfa3b8..a764045 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
@@ -27,14 +27,6 @@
  
  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) */
@@ -322,8 +314,8 @@ static int pcan_usb_pro_wait_rsp(struct peak_usb_device *dev,
  	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 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;
@@ -475,7 +467,7 @@ static int pcan_usb_pro_set_bittiming(struct peak_usb_device *dev,
  	return pcan_usb_pro_set_bitrate(dev, ccbt);
  }
  
-static void pcan_usb_pro_restart_complete(struct urb *urb)
+void pcan_usb_pro_restart_complete(struct urb *urb)
  {
  	/* can delete usb resources */
  	peak_usb_async_complete(urb);
@@ -977,7 +969,7 @@ static void pcan_usb_pro_free(struct peak_usb_device *dev)
  /*
   * probe function for new PCAN-USB Pro usb interface
   */
-static int pcan_usb_pro_probe(struct usb_interface *intf)
+int pcan_usb_pro_probe(struct usb_interface *intf)
  {
  	struct usb_host_interface *if_desc;
  	int i;
@@ -1015,6 +1007,7 @@ struct peak_usb_adapter pcan_usb_pro = {
  	.name = "PCAN-USB Pro",
  	.device_id = PCAN_USBPRO_PRODUCT_ID,
  	.ctrl_count = PCAN_USBPRO_CHANNEL_COUNT,
+	.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY,
  	.clock = {
  		.freq = PCAN_USBPRO_CRYSTAL_HZ,
  	},
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.h b/drivers/net/can/usb/peak_usb/pcan_usb_pro.h
index 837cee2..31cef84 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_pro.h
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.h
@@ -27,6 +27,14 @@
  #define PCAN_USBPRO_INFO_BL		0
  #define PCAN_USBPRO_INFO_FW		1
  
+/* PCAN-USB Pro (FD) Endpoints */
+#define PCAN_USBPRO_EP_CMDOUT		1
+#define PCAN_USBPRO_EP_CMDIN		(PCAN_USBPRO_EP_CMDOUT | USB_DIR_IN)
+#define PCAN_USBPRO_EP_MSGOUT_0		2
+#define PCAN_USBPRO_EP_MSGIN		(PCAN_USBPRO_EP_MSGOUT_0 | USB_DIR_IN)
+#define PCAN_USBPRO_EP_MSGOUT_1		3
+#define PCAN_USBPRO_EP_UNUSED		(PCAN_USBPRO_EP_MSGOUT_1 | USB_DIR_IN)
+
  /* Vendor Request value for XXX_FCT */
  #define PCAN_USBPRO_FCT_DRVLD		5 /* tell device driver is loaded */
  #define PCAN_USBPRO_FCT_DRVLD_REQ_LEN	16
@@ -176,4 +184,10 @@ union pcan_usb_pro_rec {
  	struct pcan_usb_pro_txmsg	tx_msg;
  };
  
+extern int pcan_usb_pro_probe(struct usb_interface *intf);
+extern int pcan_usb_pro_send_req(struct peak_usb_device *dev, int req_id,
+		                                 int req_value, void *req_addr,
+						 int req_size);
+extern void pcan_usb_pro_restart_complete(struct urb *urb);
+
  #endif
-- 
1.9.1



--
PEAK-System Technik GmbH
Sitz der Gesellschaft Darmstadt
Handelsregister Darmstadt HRB 9183 
Geschaeftsfuehrung: Alexander Gach, Uwe Wilhelm
--

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

* Re: Fwd: [PATCH v4] can/peak_usb: add support for PEAK new CANFD USB adapters
  2015-01-14 12:05 ` Fwd: " Stephane Grosjean
@ 2015-01-14 13:00   ` Andri Yngvason
  0 siblings, 0 replies; 18+ messages in thread
From: Andri Yngvason @ 2015-01-14 13:00 UTC (permalink / raw)
  To: Stephane Grosjean, Wolfgang Grandegger; +Cc: Oliver Hartkopp, linux-can

Quoting Stephane Grosjean (2015-01-14 12:05:50)
> Hi Wolfgang and Andri,
> 
> So, Marc told me to start a discussion around the handling of the status 
> information this CANFD driver is able to receive from the hardware.
> This, how to proceed next, please? FYI, everything is located in 
> "pcan_usb_fd_decode_status()" below...
> 
Hi Stephane,

A lot of the state-transition logic is the same between platforms, but because
everyone implements their own logic, things don't behave exactly the same way
between platforms (as they should).

See:
http://article.gmane.org/gmane.linux.can/7162
http://article.gmane.org/gmane.linux.can/7163
http://article.gmane.org/gmane.linux.can/7164

...
> +
> +/* handle uCAN status message */
> +static int pcan_usb_fd_decode_status(struct pcan_usb_fd_if *usb_if,
> +                                    struct pucan_msg *rx_msg)
> +{
> +       struct pucan_status_msg *st = (struct pucan_status_msg *)rx_msg;
> +       struct peak_usb_device *dev = usb_if->dev[PUCAN_STMSG_CHANNEL(st)];
> +       struct pcan_usb_fd_device *pdev =
> +                       container_of(dev, struct pcan_usb_fd_device, dev);
> +       enum can_state new_state = CAN_STATE_ERROR_ACTIVE;
> +       struct net_device *netdev = dev->netdev;
> +       struct can_frame *cf;
> +       struct sk_buff *skb;
> +
> +       /* nothing should be sent while in BUS_OFF state */
> +       if (dev->can.state == CAN_STATE_BUS_OFF)
> +               return 0;
> +
> +       if (PUCAN_STMSG_BUSOFF(st)) {
> +               new_state = CAN_STATE_BUS_OFF;
> +       } else if (PUCAN_STMSG_PASSIVE(st)) {
> +               new_state = CAN_STATE_ERROR_PASSIVE;
> +       } else if (PUCAN_STMSG_WARNING(st)) {
> +               new_state = CAN_STATE_ERROR_WARNING;
> +       } else {
> +               /* no error bit (so, no error skb, back to active state) */
> +               dev->can.state = CAN_STATE_ERROR_ACTIVE;
> +               pdev->tx_error_counter = 0;
> +               pdev->rx_error_counter = 0;
> +               return 0;
> +       }
> +
> +       /* donot post any error if current state didn't change */
> +       if (dev->can.state == new_state)
> +               return 0;
> +
> +       /* allocate an skb to store the error frame */
> +       skb = alloc_can_err_skb(netdev, &cf);
> +       if (!skb)
> +               return -ENOMEM;
> +
You can replace this:
> +       switch (new_state) {
> +       case CAN_STATE_BUS_OFF:
> +               cf->can_id |= CAN_ERR_BUSOFF;
> +               can_bus_off(netdev);
> +               break;
> +
> +       case CAN_STATE_ERROR_PASSIVE:
> +               cf->can_id |= CAN_ERR_CRTL;
> +               if (pdev->rx_error_counter > 127)
> +                       cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE;
> +               if (pdev->tx_error_counter > 127)
> +                       cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE;
> +
> +               dev->can.can_stats.error_passive++;
> +               break;
> +
> +       case CAN_STATE_ERROR_WARNING:
> +               cf->can_id |= CAN_ERR_CRTL;
> +               if (pdev->rx_error_counter > 96)
> +                       cf->data[1] |= CAN_ERR_CRTL_RX_WARNING;
> +               if (pdev->tx_error_counter > 96)
> +                       cf->data[1] |= CAN_ERR_CRTL_TX_WARNING;
> +
> +               dev->can.can_stats.error_warning++;
> +               break;
> +
> +       default:
> +               /* default case never happens, only for warnings */
> +               new_state = CAN_STATE_ERROR_ACTIVE;
> +
> +       case CAN_STATE_ERROR_ACTIVE:    /* fallthrough */
> +               pdev->tx_error_counter = 0;
> +               pdev->rx_error_counter = 0;
> +               break;
> +       }
> +
> +       dev->can.state = new_state;
With something like this:
    if(state != dev->can.state) {
        tx_state = get_state_from_counter(pdev->rx_error_counter);
        rx_state = get_state_from_counter(pdev->tx_error_counter);

        can_change_state(dev, cf, tx_state, rx_state);

        if(state == CAN_STATE_BUS_OFF)
            can_bus_off(dev);
    }

> +
> +       peak_usb_netif_rx(skb, &usb_if->time_ref,
> +                         le32_to_cpu(st->ts_low), le32_to_cpu(st->ts_high));
> +
> +       netdev->stats.rx_packets++;
> +       netdev->stats.rx_bytes += cf->can_dlc;
> +
> +       return 0;
> +}
> +

You can test the state transitions by disconnecting and connecting the can-bus
and verify that the state goes active -> warning -> passive when you disconnect
then passive -> warning -> active when you connect the bus again.

This shows how I tested my code: http://article.gmane.org/gmane.linux.can/7164

Best regards,
Andri

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

* Re: [PATCH v4] can/peak_usb: add support for PEAK new CANFD USB adapters
  2015-01-14 11:07     ` Marc Kleine-Budde
@ 2015-01-14 18:55       ` Oliver Hartkopp
  2015-01-14 20:57         ` Marc Kleine-Budde
  0 siblings, 1 reply; 18+ messages in thread
From: Oliver Hartkopp @ 2015-01-14 18:55 UTC (permalink / raw)
  To: Marc Kleine-Budde, Stephane Grosjean; +Cc: linux-can

On 14.01.2015 12:07, Marc Kleine-Budde wrote:

> Same here:
>> ➜ (pts/32) frogger@hardanger:/tmp git clone https://gitorious.org/linux-can/linux-can.git
>> Cloning into 'linux-can'...
>> error: RPC failed; result=22, HTTP code = 504
>> fatal: The remote end hung up unexpectedly
>
> 504 is Gateway timeout - maybe the kernel is too big to clone :D
>
> I'd suggest to add can and can-next as a remote to an existing linux git tree.
>

It already gets stuck, when you just try to open the Linux-CAN project start 
page at https://gitorious.org/linux-can ...

What about trying to host linux-can and linux-can-next on kernel.org ?

The userspace stuff can remain on gitorious - as these are small repos.

Regards,
Oliver

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

* Re: [PATCH v4] can/peak_usb: add support for PEAK new CANFD USB adapters
  2015-01-14 18:55       ` Oliver Hartkopp
@ 2015-01-14 20:57         ` Marc Kleine-Budde
  2015-01-15 15:49           ` Marc Kleine-Budde
  0 siblings, 1 reply; 18+ messages in thread
From: Marc Kleine-Budde @ 2015-01-14 20:57 UTC (permalink / raw)
  To: Oliver Hartkopp, Stephane Grosjean; +Cc: linux-can

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

On 01/14/2015 07:55 PM, Oliver Hartkopp wrote:
> It already gets stuck, when you just try to open the Linux-CAN project
> start page at https://gitorious.org/linux-can ...

grrr :(

> What about trying to host linux-can and linux-can-next on kernel.org ?

I hope my GPG has enough signatures. I'll ask for a kernel.org account
tomorrow.

> The userspace stuff can remain on gitorious - as these are small repos.

Let's see.

Marc

-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [PATCH v4] can/peak_usb: add support for PEAK new CANFD USB adapters
  2015-01-14 20:57         ` Marc Kleine-Budde
@ 2015-01-15 15:49           ` Marc Kleine-Budde
  0 siblings, 0 replies; 18+ messages in thread
From: Marc Kleine-Budde @ 2015-01-15 15:49 UTC (permalink / raw)
  To: Oliver Hartkopp, Stephane Grosjean; +Cc: linux-can

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

On 01/14/2015 09:57 PM, Marc Kleine-Budde wrote:
> On 01/14/2015 07:55 PM, Oliver Hartkopp wrote:
>> It already gets stuck, when you just try to open the Linux-CAN project
>> start page at https://gitorious.org/linux-can ...
> 
> grrr :(
> 
>> What about trying to host linux-can and linux-can-next on kernel.org ?
> 
> I hope my GPG has enough signatures. I'll ask for a kernel.org account
> tomorrow.

Done \o/

Marc
-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

end of thread, other threads:[~2015-01-15 15:49 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-01-06 10:00 [PATCH v4] can/peak_usb: add support for PEAK new CANFD USB adapters Stephane Grosjean
2015-01-07 17:03 ` Marc Kleine-Budde
2015-01-07 17:37   ` Oliver Hartkopp
2015-01-07 18:37     ` iproute2 fd-non-iso PoC - was " Oliver Hartkopp
2015-01-08  9:04       ` Marc Kleine-Budde
2015-01-08  9:09     ` Marc Kleine-Budde
2015-01-13 13:18   ` Stephane Grosjean
2015-01-13 13:29     ` Marc Kleine-Budde
2015-01-13 13:32       ` Marc Kleine-Budde
2015-01-14 10:50   ` Stephane Grosjean
2015-01-14 10:51     ` Marc Kleine-Budde
2015-01-14 10:56   ` Stephane Grosjean
2015-01-14 11:07     ` Marc Kleine-Budde
2015-01-14 18:55       ` Oliver Hartkopp
2015-01-14 20:57         ` Marc Kleine-Budde
2015-01-15 15:49           ` Marc Kleine-Budde
2015-01-14 12:05 ` Fwd: " Stephane Grosjean
2015-01-14 13:00   ` Andri Yngvason

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.