[PATCH v2] can: usb: IXXAT USB-to-CAN adapters drivers

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Florian Ferg <flfe@xxxxxxxxxxxxxxx>

This patch adds the driver for the IXXAT USB-to-CAN interfaces.
There is an adapter for the older communication layer cl1 and another
adapter for the newer communication layer cl2.

Signed-off-by: Florian Ferg <flfe@xxxxxxxxxxxxxxx>
---
Fixed some style issues.
Using ktime API for timestamps.
Reworked the driver to be only one module with adapters for cl1 / cl2.
CAN-IDM100 device will not be released. The driver handles CANIDM-101 now.
Added error passive recognition. (Even though current devices don't
        signal it. Future devices probably will.)

 drivers/net/can/usb/Kconfig                    |   17 +
 drivers/net/can/usb/Makefile                   |    1 +
 drivers/net/can/usb/ixxat_usb/Makefile         |    2 +
 drivers/net/can/usb/ixxat_usb/ixxat_usb_cl1.c  |  425 ++++++++++
 drivers/net/can/usb/ixxat_usb/ixxat_usb_cl2.c  |  581 +++++++++++++
 drivers/net/can/usb/ixxat_usb/ixxat_usb_core.c | 1039 ++++++++++++++++++++++++
 drivers/net/can/usb/ixxat_usb/ixxat_usb_core.h |  448 ++++++++++
 7 files changed, 2513 insertions(+)
 create mode 100644 drivers/net/can/usb/ixxat_usb/Makefile
 create mode 100644 drivers/net/can/usb/ixxat_usb/ixxat_usb_cl1.c
 create mode 100644 drivers/net/can/usb/ixxat_usb/ixxat_usb_cl2.c
 create mode 100644 drivers/net/can/usb/ixxat_usb/ixxat_usb_core.c
 create mode 100644 drivers/net/can/usb/ixxat_usb/ixxat_usb_core.h

diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig
index c36f4bd..ca21974 100644
--- a/drivers/net/can/usb/Kconfig
+++ b/drivers/net/can/usb/Kconfig
@@ -89,4 +89,21 @@ config CAN_MCBA_USB
 	  This driver supports the CAN BUS Analyzer interface
 	  from Microchip (http://www.microchip.com/development-tools/).
 
+config CAN_IXXAT_USB
+	tristate "IXXAT USB-to-CAN interfaces"
+	---help---
+	  This driver adds support for IXXAT USB-to-CAN devices.
+
+	  The driver provides support for the following devices:
+	    - IXXAT USB-to-CAN compact
+	    - IXXAT USB-to-CAN embedded
+	    - IXXAT USB-to-CAN professional
+	    - IXXAT USB-to-CAN automotive
+	    - IXXAT USB-to-CAN FD compact
+	    - IXXAT USB-to-CAN FD professional
+	    - IXXAT USB-to-CAN FD automotive
+	    - IXXAT USB-to-CAN FD MiniPCIe
+	    - IXXAT USB-to-CAR
+	    - IXXAT CAN-IDM101
+
 endmenu
diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile
index 49ac7b9..9d63b8d 100644
--- a/drivers/net/can/usb/Makefile
+++ b/drivers/net/can/usb/Makefile
@@ -10,3 +10,4 @@ obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o
 obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/
 obj-$(CONFIG_CAN_8DEV_USB) += usb_8dev.o
 obj-$(CONFIG_CAN_MCBA_USB) += mcba_usb.o
+obj-$(CONFIG_CAN_IXXAT_USB) += ixxat_usb/
diff --git a/drivers/net/can/usb/ixxat_usb/Makefile b/drivers/net/can/usb/ixxat_usb/Makefile
new file mode 100644
index 0000000..125d270
--- /dev/null
+++ b/drivers/net/can/usb/ixxat_usb/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_CAN_IXXAT_USB) += ixxat_usb2can.o
+ixxat_usb2can-y = ixxat_usb_cl1.o ixxat_usb_cl2.o ixxat_usb_core.o
diff --git a/drivers/net/can/usb/ixxat_usb/ixxat_usb_cl1.c b/drivers/net/can/usb/ixxat_usb/ixxat_usb_cl1.c
new file mode 100644
index 0000000..14dbf9e
--- /dev/null
+++ b/drivers/net/can/usb/ixxat_usb/ixxat_usb_cl1.c
@@ -0,0 +1,425 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* CAN driver adapter for IXXAT USB-to-CAN CL1
+ *
+ * Copyright (C) 2018 HMS Industrial Networks <socketcan@xxxxxxxxxxxxxxx>
+ *
+ * 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/can/dev.h>
+#include <linux/usb.h>
+
+#include "ixxat_usb_core.h"
+
+#define IXXAT_USB_CLOCK  8000000
+
+#define IXXAT_USB_BUFFER_SIZE_RX  512
+#define IXXAT_USB_BUFFER_SIZE_TX  256
+
+#define IXXAT_USB_MODES  (CAN_CTRLMODE_3_SAMPLES | \
+			  CAN_CTRLMODE_BERR_REPORTING | \
+			  CAN_CTRLMODE_LISTENONLY) \
+
+#define IXXAT_USB_BTMODE_TSM_CL1  0x80
+
+/* bittiming parameters */
+#define IXXAT_USB2CAN_NAME  "ixxat_usb"
+
+#define IXXAT_USB2CAN_TSEG1_MIN   1
+#define IXXAT_USB2CAN_TSEG1_MAX  16
+#define IXXAT_USB2CAN_TSEG2_MIN   1
+#define IXXAT_USB2CAN_TSEG2_MAX   8
+#define IXXAT_USB2CAN_SJW_MAX     4
+#define IXXAT_USB2CAN_BRP_MIN     1
+#define IXXAT_USB2CAN_BRP_MAX    64
+#define IXXAT_USB2CAN_BRP_INC     1
+
+/* USB endpoint mapping for CL1 */
+#define IXXAT_USB2CAN_EP1_IN  (1 | USB_DIR_IN)
+#define IXXAT_USB2CAN_EP2_IN  (2 | USB_DIR_IN)
+#define IXXAT_USB2CAN_EP3_IN  (3 | USB_DIR_IN)
+#define IXXAT_USB2CAN_EP4_IN  (4 | USB_DIR_IN)
+#define IXXAT_USB2CAN_EP5_IN  (5 | USB_DIR_IN)
+
+#define IXXAT_USB2CAN_EP1_OUT  (1 | USB_DIR_OUT)
+#define IXXAT_USB2CAN_EP2_OUT  (2 | USB_DIR_OUT)
+#define IXXAT_USB2CAN_EP3_OUT  (3 | USB_DIR_OUT)
+#define IXXAT_USB2CAN_EP4_OUT  (4 | USB_DIR_OUT)
+#define IXXAT_USB2CAN_EP5_OUT  (5 | USB_DIR_OUT)
+
+#define IXXAT_USB_CAN_CMD_INIT  0x325
+
+struct ixxat_can_msg_cl1 {
+	u8  size;
+	u32 time;
+	u32 msg_id;
+	u32 flags;
+	u8  data[CAN_MAX_DLEN];
+} __packed;
+
+struct ixxat_usb_ctrl_init_req {
+	struct ixxat_usb_dal_req dal_req;
+	u8 mode;
+	u8 btr0;
+	u8 btr1;
+	u8 padding;
+} __packed;
+
+static const struct can_bittiming_const usb2can_bt = {
+	.name = IXXAT_USB2CAN_NAME,
+	.tseg1_min = IXXAT_USB2CAN_TSEG1_MIN,
+	.tseg1_max = IXXAT_USB2CAN_TSEG1_MAX,
+	.tseg2_min = IXXAT_USB2CAN_TSEG2_MIN,
+	.tseg2_max = IXXAT_USB2CAN_TSEG2_MAX,
+	.sjw_max = IXXAT_USB2CAN_SJW_MAX,
+	.brp_min = IXXAT_USB2CAN_BRP_MIN,
+	.brp_max = IXXAT_USB2CAN_BRP_MAX,
+	.brp_inc = IXXAT_USB2CAN_BRP_INC,
+};
+
+static int ixxat_usb_handle_canmsg(struct ixxat_usb_device *dev,
+				   struct ixxat_can_msg_cl1 *rx)
+{
+	struct net_device *netdev = dev->netdev;
+	struct can_frame *can_frame;
+	struct sk_buff *skb;
+	const u32 flags = le32_to_cpu(rx->flags);
+	const u8 dlcflag = (flags & IXXAT_USB_MSG_FLAGS_DLC) >> 16;
+
+	skb = alloc_can_skb(netdev, &can_frame);
+	if (!skb)
+		return -ENOMEM;
+
+	if (flags & IXXAT_USB_MSG_FLAGS_OVR) {
+		netdev->stats.rx_over_errors++;
+		netdev->stats.rx_errors++;
+		netdev_err(netdev, "Error: Message overflow\n");
+	}
+
+	can_frame->can_id = le32_to_cpu(rx->msg_id);
+	can_frame->can_dlc = dlcflag;
+
+	if (flags & IXXAT_USB_MSG_FLAGS_EXT)
+		can_frame->can_id |= CAN_EFF_FLAG;
+
+	if (flags & IXXAT_USB_MSG_FLAGS_RTR)
+		can_frame->can_id |= CAN_RTR_FLAG;
+	else
+		memcpy(can_frame->data, rx->data, can_frame->can_dlc);
+
+	ixxat_usb_get_ts_tv(dev, le32_to_cpu(rx->time), &skb->tstamp);
+
+	netdev->stats.rx_packets++;
+	netdev->stats.rx_bytes += can_frame->can_dlc;
+	netif_rx(skb);
+
+	return 0;
+}
+
+static int ixxat_usb_handle_status(struct ixxat_usb_device *dev,
+				   struct ixxat_can_msg_cl1 *rx)
+{
+	struct net_device *netdev = dev->netdev;
+	struct can_frame *can_frame;
+	struct sk_buff *skb;
+
+	u32 raw_status = le32_to_cpu(*(u32 *)(rx->data));
+	enum can_state new_state = CAN_STATE_ERROR_ACTIVE;
+
+	skb = alloc_can_err_skb(netdev, &can_frame);
+	if (!skb)
+		return -ENOMEM;
+
+	if (raw_status == IXXAT_USB_CAN_STATUS_OK) {
+		dev->can.state = CAN_STATE_ERROR_ACTIVE;
+		can_frame->can_id |= CAN_ERR_CRTL;
+		can_frame->data[1] |= CAN_ERR_CRTL_ACTIVE;
+	} else if (raw_status & IXXAT_USB_CAN_STATUS_BUSOFF) {
+		can_frame->can_id |= CAN_ERR_BUSOFF;
+		dev->can.can_stats.bus_off++;
+		new_state = CAN_STATE_BUS_OFF;
+		can_bus_off(netdev);
+	} else {
+		if (raw_status & IXXAT_USB_CAN_STATUS_ERRLIM) {
+			can_frame->can_id |= CAN_ERR_CRTL;
+			can_frame->data[1] |= CAN_ERR_CRTL_TX_WARNING;
+			can_frame->data[1] |= CAN_ERR_CRTL_RX_WARNING;
+			dev->can.can_stats.error_warning++;
+			new_state = CAN_STATE_ERROR_WARNING;
+		}
+
+		if (raw_status & IXXAT_USB_CAN_STATUS_ERR_PAS) {
+			can_frame->can_id |= CAN_ERR_CRTL;
+			can_frame->data[1] |= CAN_ERR_CRTL_TX_PASSIVE;
+			can_frame->data[1] |= CAN_ERR_CRTL_RX_PASSIVE;
+			dev->can.can_stats.error_passive++;
+			new_state = CAN_STATE_ERROR_PASSIVE;
+		}
+
+		if (raw_status & IXXAT_USB_CAN_STATUS_OVERRUN) {
+			can_frame->can_id |= CAN_ERR_CRTL;
+			can_frame->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
+			new_state = CAN_STATE_MAX;
+		}
+	}
+
+	if (new_state == CAN_STATE_ERROR_ACTIVE) {
+		dev->bec.txerr = 0;
+		dev->bec.rxerr = 0;
+	}
+
+	if (new_state != CAN_STATE_MAX)
+		dev->can.state = new_state;
+
+	netdev->stats.rx_packets++;
+	netdev->stats.rx_bytes += can_frame->can_dlc;
+	netif_rx(skb);
+
+	return 0;
+}
+
+static int ixxat_usb_handle_error(struct ixxat_usb_device *dev,
+				  struct ixxat_can_msg_cl1 *rx)
+{
+	struct net_device *netdev = dev->netdev;
+	struct can_frame *can_frame;
+	struct sk_buff *skb;
+	u8 raw_error = rx->data[0];
+
+	if (dev->can.state == CAN_STATE_BUS_OFF)
+		return 0;
+
+	skb = alloc_can_err_skb(netdev, &can_frame);
+	if (!skb)
+		return -ENOMEM;
+
+	switch (raw_error) {
+	case IXXAT_USB_CAN_ERROR_ACK:
+		can_frame->can_id |= CAN_ERR_ACK;
+		netdev->stats.tx_errors++;
+		break;
+	case IXXAT_USB_CAN_ERROR_BIT:
+		can_frame->can_id |= CAN_ERR_PROT;
+		can_frame->data[2] |= CAN_ERR_PROT_BIT;
+		netdev->stats.rx_errors++;
+		break;
+	case IXXAT_USB_CAN_ERROR_CRC:
+		can_frame->can_id |= CAN_ERR_PROT;
+		can_frame->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ;
+		netdev->stats.rx_errors++;
+		break;
+	case IXXAT_USB_CAN_ERROR_FORM:
+		can_frame->can_id |= CAN_ERR_PROT;
+		can_frame->data[2] |= CAN_ERR_PROT_FORM;
+		netdev->stats.rx_errors++;
+		break;
+	case IXXAT_USB_CAN_ERROR_STUFF:
+		can_frame->can_id |= CAN_ERR_PROT;
+		can_frame->data[2] |= CAN_ERR_PROT_STUFF;
+		netdev->stats.rx_errors++;
+		break;
+	default:
+		can_frame->can_id |= CAN_ERR_PROT;
+		can_frame->data[2] |= CAN_ERR_PROT_UNSPEC;
+		netdev->stats.rx_errors++;
+		break;
+	}
+
+	netdev->stats.rx_packets++;
+	netdev->stats.rx_bytes += can_frame->can_dlc;
+	netif_rx(skb);
+
+	dev->bec.rxerr = rx->data[3];
+	dev->bec.txerr = rx->data[4];
+
+	return 0;
+}
+
+static int ixxat_usb_decode_buf(struct urb *urb)
+{
+	struct ixxat_usb_device *dev = urb->context;
+	struct net_device *netdev = dev->netdev;
+	struct ixxat_can_msg_cl1 *can_msg;
+	int ret = 0;
+	u32 msg_end = urb->actual_length;
+	u32 read_size = 0;
+	u8 *data = urb->transfer_buffer;
+
+	while (msg_end > 0) {
+		u8 msg_type;
+
+		can_msg = (struct ixxat_can_msg_cl1 *)&data[read_size];
+		if (!can_msg || !can_msg->size) {
+			ret = -ENOTSUPP;
+			netdev_err(netdev, "Error %d: Unsupported usb msg\n",
+				   ret);
+			break;
+		}
+
+		if ((read_size + can_msg->size + 1) > urb->actual_length) {
+			ret = -EBADMSG;
+			netdev_err(netdev,
+				   "Error %d: Usb rx-buffer size unknown\n",
+				   ret);
+			break;
+		}
+
+		msg_type = le32_to_cpu(can_msg->flags);
+		msg_type &= IXXAT_USB_MSG_FLAGS_TYPE;
+
+		switch (msg_type) {
+		case IXXAT_USB_CAN_DATA:
+			ret = ixxat_usb_handle_canmsg(dev, can_msg);
+			if (ret < 0)
+				goto fail;
+			break;
+		case IXXAT_USB_CAN_STATUS:
+			ret = ixxat_usb_handle_status(dev, can_msg);
+			if (ret < 0)
+				goto fail;
+			break;
+		case IXXAT_USB_CAN_ERROR:
+			ret = ixxat_usb_handle_error(dev, can_msg);
+			if (ret < 0)
+				goto fail;
+			break;
+		case IXXAT_USB_CAN_TIMEOVR:
+			ixxat_usb_get_ts_tv(dev, can_msg->time, NULL);
+			break;
+		case IXXAT_USB_CAN_INFO:
+		case IXXAT_USB_CAN_WAKEUP:
+		case IXXAT_USB_CAN_TIMERST:
+			break;
+		default:
+			netdev_err(netdev,
+				   "Unhandled rec type 0x%02x (%d): ignored\n",
+				   msg_type, msg_type);
+			break;
+		}
+
+		read_size += (can_msg->size + 1);
+		msg_end -= (can_msg->size + 1);
+	}
+
+fail:
+	if (ret < 0)
+		netdev_err(netdev, "Error %d: Buffer decoding failed\n", ret);
+
+	return ret;
+}
+
+static void ixxat_usb_encode_msg(struct ixxat_usb_device *dev,
+				 struct sk_buff *skb, u8 *obuf,
+				 size_t *size)
+{
+	struct can_frame *cf = (struct can_frame *)skb->data;
+	struct ixxat_can_msg_cl1 can_msg = { 0 };
+
+	if (cf->can_id & CAN_RTR_FLAG)
+		can_msg.flags |= IXXAT_USB_MSG_FLAGS_RTR;
+
+	if (cf->can_id & CAN_EFF_FLAG) {
+		can_msg.flags |= IXXAT_USB_MSG_FLAGS_EXT;
+		can_msg.msg_id = cf->can_id & CAN_EFF_MASK;
+	} else {
+		can_msg.msg_id = cf->can_id & CAN_SFF_MASK;
+	}
+
+	can_msg.flags |= (cf->can_dlc << 16) & IXXAT_USB_MSG_FLAGS_DLC;
+	can_msg.flags = cpu_to_le32(can_msg.flags);
+
+	can_msg.msg_id = cpu_to_le32(can_msg.msg_id);
+
+	memcpy(can_msg.data, cf->data, cf->can_dlc);
+
+	can_msg.size = (u8)(sizeof(can_msg) - 1 - CAN_MAX_DLEN + cf->can_dlc);
+	memcpy(obuf, &can_msg, can_msg.size + 1);
+	*size = can_msg.size + 1;
+	skb->data_len = *size;
+}
+
+static int ixxat_usb_init_ctrl(struct ixxat_usb_device *dev)
+{
+	int ret = -ENODEV;
+	u8 data[IXXAT_USB_CMD_BUFFER_SIZE] = { 0 };
+	struct ixxat_usb_ctrl_init_req *req;
+	struct ixxat_usb_ctrl_init_res *res;
+	u32 req_size = sizeof(*req);
+	u32 res_size = sizeof(*res);
+	u8 opmode = IXXAT_USB_OPMODE_EXTENDED | IXXAT_USB_OPMODE_STANDARD;
+	const struct can_bittiming *bt = &dev->can.bittiming;
+
+	/* Bittiming calculation from kernel
+	 * (see can-utils/can-calc-bit-timing.c)
+	 */
+	u8 btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6);
+	u8 btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) |
+		  (((bt->phase_seg2 - 1) & 0x7) << 4);
+
+	req = (struct ixxat_usb_ctrl_init_req *)data;
+	res = (struct ixxat_usb_ctrl_init_res *)(data + req_size);
+
+	if (dev->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
+		btr1 |= IXXAT_USB_BTMODE_TSM_CL1;
+	if (dev->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
+		opmode |= IXXAT_USB_OPMODE_ERRFRAME;
+	if (dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
+		opmode |= IXXAT_USB_OPMODE_LISTONLY;
+
+	req->dal_req.req_size = cpu_to_le32(req_size);
+	req->dal_req.req_code = cpu_to_le32(IXXAT_USB_CAN_CMD_INIT);
+	req->dal_req.req_port = cpu_to_le16(dev->ctrl_index);
+	req->dal_req.req_socket = 0xffff;
+	req->mode = opmode;
+	req->btr0 = btr0;
+	req->btr1 = btr1;
+
+	res->dal_res.res_size = cpu_to_le32(res_size);
+	res->dal_res.ret_size = 0;
+	res->dal_res.ret_code = 0xffffffff;
+
+	ret = ixxat_usb_send_cmd(dev->udev, &req->dal_req);
+	if (ret < 0)
+		return ret;
+
+	ret = ixxat_usb_rcv_cmd(dev->udev, &res->dal_res, dev->ctrl_index);
+	if (ret < 0)
+		return ret;
+
+	return le32_to_cpu(res->dal_res.ret_code);
+}
+
+const struct ixxat_usb_adapter usb2can_cl1 = {
+	.clock = IXXAT_USB_CLOCK,
+	.bt = &usb2can_bt,
+	.btd = NULL,
+	.modes = IXXAT_USB_MODES,
+	.buffer_size_rx = IXXAT_USB_BUFFER_SIZE_RX,
+	.buffer_size_tx = IXXAT_USB_BUFFER_SIZE_TX,
+	.ep_msg_in = {
+		IXXAT_USB2CAN_EP1_IN,
+		IXXAT_USB2CAN_EP2_IN,
+		IXXAT_USB2CAN_EP3_IN,
+		IXXAT_USB2CAN_EP4_IN,
+		IXXAT_USB2CAN_EP5_IN
+	},
+	.ep_msg_out = {
+		IXXAT_USB2CAN_EP1_OUT,
+		IXXAT_USB2CAN_EP2_OUT,
+		IXXAT_USB2CAN_EP3_OUT,
+		IXXAT_USB2CAN_EP4_OUT,
+		IXXAT_USB2CAN_EP5_OUT
+	},
+	.ep_offs = 0,
+	.decode_buf = ixxat_usb_decode_buf,
+	.init_ctrl = ixxat_usb_init_ctrl,
+	.encode_msg = ixxat_usb_encode_msg
+};
diff --git a/drivers/net/can/usb/ixxat_usb/ixxat_usb_cl2.c b/drivers/net/can/usb/ixxat_usb/ixxat_usb_cl2.c
new file mode 100644
index 0000000..22eb2e4
--- /dev/null
+++ b/drivers/net/can/usb/ixxat_usb/ixxat_usb_cl2.c
@@ -0,0 +1,581 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* CAN driver adapter for IXXAT USB-to-CAN CL2
+ *
+ * Copyright (C) 2018 HMS Industrial Networks <socketcan@xxxxxxxxxxxxxxx>
+ *
+ * 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/can/dev.h>
+#include <linux/usb.h>
+
+#include "ixxat_usb_core.h"
+
+#define IXXAT_USB_CLOCK  80000000
+
+#define IXXAT_USB_BUFFER_SIZE_RX  512
+#define IXXAT_USB_BUFFER_SIZE_TX  512
+
+#define IXXAT_USB_MODES  (CAN_CTRLMODE_3_SAMPLES |       \
+			  CAN_CTRLMODE_LISTENONLY |      \
+			  CAN_CTRLMODE_BERR_REPORTING |  \
+			  CAN_CTRLMODE_FD |              \
+			  CAN_CTRLMODE_FD_NON_ISO)
+
+/* bittiming parameters CL2 */
+#define IXXAT_USB2CAN_NAME  "ifi_can"
+
+#define IXXAT_USB2CAN_TSEG1_MIN         1
+#define IXXAT_USB2CAN_TSEG1_MAX       256
+#define IXXAT_USB2CAN_TSEG2_MIN         1
+#define IXXAT_USB2CAN_TSEG2_MAX       256
+#define IXXAT_USB2CAN_SJW_MAX         128
+#define IXXAT_USB2CAN_BRP_MIN           2
+#define IXXAT_USB2CAN_BRP_MAX         513
+#define IXXAT_USB2CAN_BRP_INC           1
+
+#define IXXAT_USB2CAN_TSEG1_MIN_DATA    1
+#define IXXAT_USB2CAN_TSEG1_MAX_DATA  256
+#define IXXAT_USB2CAN_TSEG2_MIN_DATA    1
+#define IXXAT_USB2CAN_TSEG2_MAX_DATA  256
+#define IXXAT_USB2CAN_SJW_MAX_DATA    128
+#define IXXAT_USB2CAN_BRP_MIN_DATA      2
+#define IXXAT_USB2CAN_BRP_MAX_DATA    513
+#define IXXAT_USB2CAN_BRP_INC_DATA      1
+
+/* bittiming parameters CAN IDM */
+#define IXXAT_CANIDM_NAME  "mcan"
+
+#define IXXAT_CANIDM_TSEG1_MIN          1
+#define IXXAT_CANIDM_TSEG1_MAX        256
+#define IXXAT_CANIDM_TSEG2_MIN          1
+#define IXXAT_CANIDM_TSEG2_MAX        128
+#define IXXAT_CANIDM_SJW_MAX          128
+#define IXXAT_CANIDM_BRP_MIN            1
+#define IXXAT_CANIDM_BRP_MAX          512
+#define IXXAT_CANIDM_BRP_INC            1
+
+#define IXXAT_CANIDM_TSEG1_MIN_DATA     1
+#define IXXAT_CANIDM_TSEG1_MAX_DATA    32
+#define IXXAT_CANIDM_TSEG2_MIN_DATA     1
+#define IXXAT_CANIDM_TSEG2_MAX_DATA    16
+#define IXXAT_CANIDM_SJW_MAX_DATA       8
+#define IXXAT_CANIDM_BRP_MIN_DATA       1
+#define IXXAT_CANIDM_BRP_MAX_DATA      32
+#define IXXAT_CANIDM_BRP_INC_DATA       1
+
+/* USB enpoint mapping for CL2 */
+#define IXXAT_USB2CAN_EP1_IN   (1 | USB_DIR_IN)
+#define IXXAT_USB2CAN_EP2_IN   (2 | USB_DIR_IN)
+#define IXXAT_USB2CAN_EP3_IN   (3 | USB_DIR_IN)
+#define IXXAT_USB2CAN_EP4_IN   (4 | USB_DIR_IN)
+#define IXXAT_USB2CAN_EP5_IN   (5 | USB_DIR_IN)
+
+#define IXXAT_USB2CAN_EP1_OUT  (1 | USB_DIR_OUT)
+#define IXXAT_USB2CAN_EP2_OUT  (2 | USB_DIR_OUT)
+#define IXXAT_USB2CAN_EP3_OUT  (3 | USB_DIR_OUT)
+#define IXXAT_USB2CAN_EP4_OUT  (4 | USB_DIR_OUT)
+#define IXXAT_USB2CAN_EP5_OUT  (5 | USB_DIR_OUT)
+
+/* USB endpoint mapping for CAN IDM */
+#define IXXAT_CANIDM_EP1_IN    (2 | USB_DIR_IN)
+#define IXXAT_CANIDM_EP2_IN    (4 | USB_DIR_IN)
+#define IXXAT_CANIDM_EP3_IN    (6 | USB_DIR_IN)
+#define IXXAT_CANIDM_EP4_IN    (8 | USB_DIR_IN)
+#define IXXAT_CANIDM_EP5_IN   (10 | USB_DIR_IN)
+
+#define IXXAT_CANIDM_EP1_OUT   (1 | USB_DIR_OUT)
+#define IXXAT_CANIDM_EP2_OUT   (3 | USB_DIR_OUT)
+#define IXXAT_CANIDM_EP3_OUT   (5 | USB_DIR_OUT)
+#define IXXAT_CANIDM_EP4_OUT   (7 | USB_DIR_OUT)
+#define IXXAT_CANIDM_EP5_OUT   (9 | USB_DIR_OUT)
+
+#define IXXAT_USB_CAN_CMD_INIT  0x337
+
+struct ixxat_canbtp {
+	u32 mode;
+	u32 bps;
+	u16 ts1;
+	u16 ts2;
+	u16 sjw;
+	u16 tdo;
+} __packed;
+
+struct ixxat_can_msg_cl2 {
+	u8  size;
+	u32 time;
+	u32 msg_id;
+	u32 flags;
+	u32 client_id;
+	u8  data[CANFD_MAX_DLEN];
+} __packed;
+
+struct ixxat_usb_ctrl_init_cl2_req {
+	struct ixxat_usb_dal_req dal_req;
+	u8 opmode;
+	u8 exmode;
+	struct ixxat_canbtp sdr;
+	struct ixxat_canbtp fdr;
+	u16 _padding;
+} __packed;
+
+static const struct can_bittiming_const usb2can_bt = {
+	.name = IXXAT_USB2CAN_NAME,
+	.tseg1_min = IXXAT_USB2CAN_TSEG1_MIN,
+	.tseg1_max = IXXAT_USB2CAN_TSEG1_MAX,
+	.tseg2_min = IXXAT_USB2CAN_TSEG2_MIN,
+	.tseg2_max = IXXAT_USB2CAN_TSEG2_MAX,
+	.sjw_max = IXXAT_USB2CAN_SJW_MAX,
+	.brp_min = IXXAT_USB2CAN_BRP_MIN,
+	.brp_max = IXXAT_USB2CAN_BRP_MAX,
+	.brp_inc = IXXAT_USB2CAN_BRP_INC,
+};
+
+static const struct can_bittiming_const usb2can_btd = {
+	.name = IXXAT_USB2CAN_NAME,
+	.tseg1_min = IXXAT_USB2CAN_TSEG1_MIN_DATA,
+	.tseg1_max = IXXAT_USB2CAN_TSEG1_MAX_DATA,
+	.tseg2_min = IXXAT_USB2CAN_TSEG2_MIN_DATA,
+	.tseg2_max = IXXAT_USB2CAN_TSEG2_MAX_DATA,
+	.sjw_max = IXXAT_USB2CAN_SJW_MAX_DATA,
+	.brp_min = IXXAT_USB2CAN_BRP_MIN_DATA,
+	.brp_max = IXXAT_USB2CAN_BRP_MAX_DATA,
+	.brp_inc = IXXAT_USB2CAN_BRP_INC_DATA,
+};
+
+static const struct can_bittiming_const canidm_bt = {
+	.name = IXXAT_CANIDM_NAME,
+	.tseg1_min = IXXAT_CANIDM_TSEG1_MIN,
+	.tseg1_max = IXXAT_CANIDM_TSEG1_MAX,
+	.tseg2_min = IXXAT_CANIDM_TSEG2_MIN,
+	.tseg2_max = IXXAT_CANIDM_TSEG2_MAX,
+	.sjw_max = IXXAT_CANIDM_SJW_MAX,
+	.brp_min = IXXAT_CANIDM_BRP_MIN,
+	.brp_max = IXXAT_CANIDM_BRP_MAX,
+	.brp_inc = IXXAT_CANIDM_BRP_INC
+};
+
+static const struct can_bittiming_const canidm_btd = {
+	.name = IXXAT_CANIDM_NAME,
+	.tseg1_min = IXXAT_CANIDM_TSEG1_MIN_DATA,
+	.tseg1_max = IXXAT_CANIDM_TSEG1_MAX_DATA,
+	.tseg2_min = IXXAT_CANIDM_TSEG2_MIN_DATA,
+	.tseg2_max = IXXAT_CANIDM_TSEG2_MAX_DATA,
+	.sjw_max = IXXAT_CANIDM_SJW_MAX_DATA,
+	.brp_min = IXXAT_CANIDM_BRP_MIN_DATA,
+	.brp_max = IXXAT_CANIDM_BRP_MAX_DATA,
+	.brp_inc = IXXAT_CANIDM_BRP_INC_DATA
+};
+
+static int ixxat_usb_handle_canmsg(struct ixxat_usb_device *dev,
+				   struct ixxat_can_msg_cl2 *rx)
+{
+	struct net_device *netdev = dev->netdev;
+	struct canfd_frame *can_frame;
+	struct sk_buff *skb;
+	const u32 flags = le32_to_cpu(rx->flags);
+	const u8 dlcflag = (flags & IXXAT_USB_MSG_FLAGS_DLC) >> 16;
+	const u8 dlc = get_canfd_dlc(dlcflag);
+
+	if (flags & IXXAT_USB_FDMSG_FLAGS_EDL)
+		skb = alloc_canfd_skb(netdev, &can_frame);
+	else
+		skb = alloc_can_skb(netdev, (struct can_frame **)&can_frame);
+
+	if (!skb)
+		return -ENOMEM;
+
+	if (flags & IXXAT_USB_FDMSG_FLAGS_EDL) {
+		if (flags & IXXAT_USB_FDMSG_FLAGS_FDR)
+			can_frame->flags |= CANFD_BRS;
+
+		if (flags & IXXAT_USB_FDMSG_FLAGS_ESI)
+			can_frame->flags |= CANFD_ESI;
+
+		can_frame->len = can_dlc2len(dlc);
+	} else {
+		can_frame->len = dlc;
+	}
+
+	if (flags & IXXAT_USB_MSG_FLAGS_OVR) {
+		netdev->stats.rx_over_errors++;
+		netdev->stats.rx_errors++;
+		netdev_err(netdev, "Error: Message overflow\n");
+	}
+
+	can_frame->can_id = le32_to_cpu(rx->msg_id);
+
+	if (flags & IXXAT_USB_MSG_FLAGS_EXT)
+		can_frame->can_id |= CAN_EFF_FLAG;
+
+	if (flags & IXXAT_USB_MSG_FLAGS_RTR)
+		can_frame->can_id |= CAN_RTR_FLAG;
+	else
+		memcpy(can_frame->data, rx->data, can_frame->len);
+
+	ixxat_usb_get_ts_tv(dev, le32_to_cpu(rx->time), &skb->tstamp);
+
+	netdev->stats.rx_packets++;
+	netdev->stats.rx_bytes += can_frame->len;
+	netif_rx(skb);
+
+	return 0;
+}
+
+static int ixxat_usb_handle_status(struct ixxat_usb_device *dev,
+				   struct ixxat_can_msg_cl2 *rx)
+{
+	struct net_device *netdev = dev->netdev;
+	struct can_frame *can_frame;
+	struct sk_buff *skb;
+
+	u32 raw_status = le32_to_cpu(*(u32 *)(rx->data));
+	enum can_state new_state = CAN_STATE_ERROR_ACTIVE;
+
+	skb = alloc_can_err_skb(netdev, &can_frame);
+	if (!skb)
+		return -ENOMEM;
+
+	if (raw_status == IXXAT_USB_CAN_STATUS_OK) {
+		dev->can.state = CAN_STATE_ERROR_ACTIVE;
+		can_frame->can_id |= CAN_ERR_CRTL;
+		can_frame->data[1] |= CAN_ERR_CRTL_ACTIVE;
+	} else if (raw_status & IXXAT_USB_CAN_STATUS_BUSOFF) {
+		can_frame->can_id |= CAN_ERR_BUSOFF;
+		dev->can.can_stats.bus_off++;
+		new_state = CAN_STATE_BUS_OFF;
+		can_bus_off(netdev);
+	} else {
+		if (raw_status & IXXAT_USB_CAN_STATUS_ERRLIM) {
+			can_frame->can_id |= CAN_ERR_CRTL;
+			can_frame->data[1] |= CAN_ERR_CRTL_TX_WARNING;
+			can_frame->data[1] |= CAN_ERR_CRTL_RX_WARNING;
+			dev->can.can_stats.error_warning++;
+			new_state = CAN_STATE_ERROR_WARNING;
+		}
+
+		if (raw_status & IXXAT_USB_CAN_STATUS_ERR_PAS) {
+			can_frame->can_id |= CAN_ERR_CRTL;
+			can_frame->data[1] |= CAN_ERR_CRTL_TX_PASSIVE;
+			can_frame->data[1] |= CAN_ERR_CRTL_RX_PASSIVE;
+			dev->can.can_stats.error_passive++;
+			new_state = CAN_STATE_ERROR_PASSIVE;
+		}
+
+		if (raw_status & IXXAT_USB_CAN_STATUS_OVERRUN) {
+			can_frame->can_id |= CAN_ERR_CRTL;
+			can_frame->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
+			new_state = CAN_STATE_MAX;
+		}
+	}
+
+	if (new_state == CAN_STATE_ERROR_ACTIVE) {
+		dev->bec.txerr = 0;
+		dev->bec.rxerr = 0;
+	}
+
+	if (new_state != CAN_STATE_MAX)
+		dev->can.state = new_state;
+
+	netdev->stats.rx_packets++;
+	netdev->stats.rx_bytes += can_frame->can_dlc;
+	netif_rx(skb);
+
+	return 0;
+}
+
+static int ixxat_usb_handle_error(struct ixxat_usb_device *dev,
+				  struct ixxat_can_msg_cl2 *rx)
+{
+	struct net_device *netdev = dev->netdev;
+	struct can_frame *can_frame;
+	struct sk_buff *skb;
+	u8 raw_error = rx->data[0];
+
+	if (dev->can.state == CAN_STATE_BUS_OFF)
+		return 0;
+
+	skb = alloc_can_err_skb(netdev, &can_frame);
+	if (!skb)
+		return -ENOMEM;
+
+	switch (raw_error) {
+	case IXXAT_USB_CAN_ERROR_ACK:
+		can_frame->can_id |= CAN_ERR_ACK;
+		netdev->stats.tx_errors++;
+		break;
+	case IXXAT_USB_CAN_ERROR_BIT:
+		can_frame->can_id |= CAN_ERR_PROT;
+		can_frame->data[2] |= CAN_ERR_PROT_BIT;
+		netdev->stats.rx_errors++;
+		break;
+	case IXXAT_USB_CAN_ERROR_CRC:
+		can_frame->can_id |= CAN_ERR_PROT;
+		can_frame->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ;
+		netdev->stats.rx_errors++;
+		break;
+	case IXXAT_USB_CAN_ERROR_FORM:
+		can_frame->can_id |= CAN_ERR_PROT;
+		can_frame->data[2] |= CAN_ERR_PROT_FORM;
+		netdev->stats.rx_errors++;
+		break;
+	case IXXAT_USB_CAN_ERROR_STUFF:
+		can_frame->can_id |= CAN_ERR_PROT;
+		can_frame->data[2] |= CAN_ERR_PROT_STUFF;
+		netdev->stats.rx_errors++;
+		break;
+	default:
+		can_frame->can_id |= CAN_ERR_PROT;
+		can_frame->data[2] |= CAN_ERR_PROT_UNSPEC;
+		netdev->stats.rx_errors++;
+		break;
+	}
+
+	netdev->stats.rx_packets++;
+	netdev->stats.rx_bytes += can_frame->can_dlc;
+	netif_rx(skb);
+
+	dev->bec.rxerr = rx->data[3];
+	dev->bec.txerr = rx->data[4];
+
+	return 0;
+}
+
+static int ixxat_usb_decode_buf(struct urb *urb)
+{
+	struct ixxat_usb_device *dev = urb->context;
+	struct net_device *netdev = dev->netdev;
+	struct ixxat_can_msg_cl2 *can_msg;
+	int ret = 0;
+	u32 msg_end = urb->actual_length;
+	u32 read_size = 0;
+	u8 *data = urb->transfer_buffer;
+
+	while (msg_end > 0) {
+		u8 msg_type;
+
+		can_msg = (struct ixxat_can_msg_cl2 *)&data[read_size];
+		if (!can_msg || !can_msg->size) {
+			ret = -ENOTSUPP;
+			netdev_err(netdev, "Error %d: Unsupported usb msg\n",
+				   ret);
+			break;
+		}
+
+		if ((read_size + can_msg->size + 1) > urb->actual_length) {
+			ret = -EBADMSG;
+			netdev_err(netdev,
+				   "Error %d: Usb rx-buffer size unknown\n",
+				   ret);
+			break;
+		}
+
+		msg_type = le32_to_cpu(can_msg->flags);
+		msg_type &= IXXAT_USB_MSG_FLAGS_TYPE;
+
+		switch (msg_type) {
+		case IXXAT_USB_CAN_DATA:
+			ret = ixxat_usb_handle_canmsg(dev, can_msg);
+			if (ret < 0)
+				goto fail;
+			break;
+		case IXXAT_USB_CAN_STATUS:
+			ret = ixxat_usb_handle_status(dev, can_msg);
+			if (ret < 0)
+				goto fail;
+			break;
+		case IXXAT_USB_CAN_ERROR:
+			ret = ixxat_usb_handle_error(dev, can_msg);
+			if (ret < 0)
+				goto fail;
+			break;
+		case IXXAT_USB_CAN_TIMEOVR:
+			ixxat_usb_get_ts_tv(dev, can_msg->time, NULL);
+			break;
+		case IXXAT_USB_CAN_INFO:
+		case IXXAT_USB_CAN_WAKEUP:
+		case IXXAT_USB_CAN_TIMERST:
+			break;
+		default:
+			netdev_err(netdev,
+				   "Unhandled rec type 0x%02x (%d): ignored\n",
+				   msg_type, msg_type);
+			break;
+		}
+
+		read_size += (can_msg->size + 1);
+		msg_end -= (can_msg->size + 1);
+	}
+
+fail:
+	if (ret < 0)
+		netdev_err(netdev, "Error %d: Buffer decoding failed\n", ret);
+
+	return ret;
+}
+
+static void ixxat_usb_encode_msg(struct ixxat_usb_device *dev,
+				 struct sk_buff *skb, u8 *obuf,
+				 size_t *size)
+{
+	struct canfd_frame *cf = (struct canfd_frame *)skb->data;
+	struct ixxat_can_msg_cl2 can_msg = { 0 };
+
+	if (cf->can_id & CAN_RTR_FLAG)
+		can_msg.flags |= IXXAT_USB_MSG_FLAGS_RTR;
+
+	if (cf->can_id & CAN_EFF_FLAG) {
+		can_msg.flags |= IXXAT_USB_MSG_FLAGS_EXT;
+		can_msg.msg_id = cf->can_id & CAN_EFF_MASK;
+	} else {
+		can_msg.msg_id = cf->can_id & CAN_SFF_MASK;
+	}
+
+	if (skb->len == CANFD_MTU) {
+		can_msg.flags |= IXXAT_USB_FDMSG_FLAGS_EDL;
+
+		if (!(cf->can_id & CAN_RTR_FLAG) && (cf->flags & CANFD_BRS))
+			can_msg.flags |= IXXAT_USB_FDMSG_FLAGS_FDR;
+	}
+
+	can_msg.flags |= (can_len2dlc(cf->len) << 16) & IXXAT_USB_MSG_FLAGS_DLC;
+	can_msg.flags = cpu_to_le32(can_msg.flags);
+
+	can_msg.msg_id = cpu_to_le32(can_msg.msg_id);
+
+	memcpy(can_msg.data, cf->data, cf->len);
+
+	can_msg.size = (u8)(sizeof(can_msg) - 1 - CANFD_MAX_DLEN + cf->len);
+	memcpy(obuf, &can_msg, can_msg.size + 1);
+	*size = can_msg.size + 1;
+	skb->data_len = *size;
+}
+
+static int ixxat_usb_init_ctrl(struct ixxat_usb_device *dev)
+{
+	int ret = -ENODEV;
+	u8 data[IXXAT_USB_CMD_BUFFER_SIZE] = { 0 };
+	struct ixxat_usb_ctrl_init_cl2_req *req;
+	struct ixxat_usb_ctrl_init_res *res;
+	u32 req_size = sizeof(*req);
+	u32 res_size = sizeof(*res);
+	u8 opmode = IXXAT_USB_OPMODE_EXTENDED | IXXAT_USB_OPMODE_STANDARD;
+	u8 exmode = 0;
+	u32 btmode = IXXAT_USB_BTMODE_NAT;
+	const struct can_bittiming *bt = &dev->can.bittiming;
+	const struct can_bittiming *btd = &dev->can.data_bittiming;
+
+	req = (struct ixxat_usb_ctrl_init_cl2_req *)data;
+	res = (struct ixxat_usb_ctrl_init_res *)(data + req_size);
+
+	if (dev->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
+		btmode = IXXAT_USB_BTMODE_TSM;
+	if (dev->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
+		opmode |= IXXAT_USB_OPMODE_ERRFRAME;
+	if (dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
+		opmode |= IXXAT_USB_OPMODE_LISTONLY;
+	if ((CAN_CTRLMODE_FD | CAN_CTRLMODE_FD_NON_ISO) & dev->can.ctrlmode)
+		exmode |= IXXAT_USB_EXMODE_EXTDATA | IXXAT_USB_EXMODE_FASTDATA;
+	if (!(CAN_CTRLMODE_FD_NON_ISO & dev->can.ctrlmode) && exmode)
+		exmode |= IXXAT_USB_EXMODE_ISOFD;
+
+	req->dal_req.req_size = cpu_to_le32(req_size);
+	req->dal_req.req_code = cpu_to_le32(IXXAT_USB_CAN_CMD_INIT);
+	req->dal_req.req_port = cpu_to_le16(dev->ctrl_index);
+	req->dal_req.req_socket = 0xffff;
+	req->opmode = opmode;
+	req->exmode = exmode;
+
+	req->sdr.mode = cpu_to_le32(btmode);
+	req->sdr.bps = cpu_to_le32(bt->brp);
+	req->sdr.ts1 = cpu_to_le16(bt->prop_seg + bt->phase_seg1);
+	req->sdr.ts2 = cpu_to_le16(bt->phase_seg2);
+	req->sdr.sjw = cpu_to_le16(bt->sjw);
+	req->sdr.tdo = 0;
+
+	if (exmode) {
+		req->fdr.mode = cpu_to_le32(btmode);
+		req->fdr.bps = cpu_to_le32(btd->brp);
+		req->fdr.ts1 = cpu_to_le16(btd->prop_seg + btd->phase_seg1);
+		req->fdr.ts2 = cpu_to_le16(btd->phase_seg2);
+		req->fdr.sjw = cpu_to_le16(btd->sjw);
+		req->fdr.tdo = cpu_to_le16(btd->brp * (btd->phase_seg1 + 1 +
+						       btd->prop_seg));
+	}
+
+	res->dal_res.res_size = cpu_to_le32(res_size);
+	res->dal_res.ret_size = 0;
+	res->dal_res.ret_code = 0xffffffff;
+
+	ret = ixxat_usb_send_cmd(dev->udev, &req->dal_req);
+	if (ret < 0)
+		return ret;
+
+	ret = ixxat_usb_rcv_cmd(dev->udev, &res->dal_res, dev->ctrl_index);
+	if (ret < 0)
+		return ret;
+
+	return le32_to_cpu(res->dal_res.ret_code);
+}
+
+const struct ixxat_usb_adapter usb2can_cl2 = {
+	.clock = IXXAT_USB_CLOCK,
+	.bt = &usb2can_bt,
+	.btd = &usb2can_btd,
+	.modes = IXXAT_USB_MODES,
+	.buffer_size_rx = IXXAT_USB_BUFFER_SIZE_RX,
+	.buffer_size_tx = IXXAT_USB_BUFFER_SIZE_TX,
+	.ep_msg_in = {
+		IXXAT_USB2CAN_EP1_IN,
+		IXXAT_USB2CAN_EP2_IN,
+		IXXAT_USB2CAN_EP3_IN,
+		IXXAT_USB2CAN_EP4_IN,
+		IXXAT_USB2CAN_EP5_IN
+	},
+	.ep_msg_out = {
+		IXXAT_USB2CAN_EP1_OUT,
+		IXXAT_USB2CAN_EP2_OUT,
+		IXXAT_USB2CAN_EP3_OUT,
+		IXXAT_USB2CAN_EP4_OUT,
+		IXXAT_USB2CAN_EP5_OUT
+	},
+	.ep_offs = 1,
+	.decode_buf = ixxat_usb_decode_buf,
+	.init_ctrl = ixxat_usb_init_ctrl,
+	.encode_msg = ixxat_usb_encode_msg
+};
+
+const struct ixxat_usb_adapter can_idm = {
+	.clock = IXXAT_USB_CLOCK,
+	.bt = &canidm_bt,
+	.btd = &canidm_btd,
+	.modes = IXXAT_USB_MODES,
+	.buffer_size_rx = IXXAT_USB_BUFFER_SIZE_RX,
+	.buffer_size_tx = IXXAT_USB_BUFFER_SIZE_TX,
+	.ep_msg_in = {
+		IXXAT_CANIDM_EP1_IN,
+		IXXAT_CANIDM_EP2_IN,
+		IXXAT_CANIDM_EP3_IN,
+		IXXAT_CANIDM_EP4_IN,
+		IXXAT_CANIDM_EP5_IN
+	},
+	.ep_msg_out = {
+		IXXAT_CANIDM_EP1_OUT,
+		IXXAT_CANIDM_EP2_OUT,
+		IXXAT_CANIDM_EP3_OUT,
+		IXXAT_CANIDM_EP4_OUT,
+		IXXAT_CANIDM_EP5_OUT
+	},
+	.ep_offs = 0,
+	.decode_buf = ixxat_usb_decode_buf,
+	.init_ctrl = ixxat_usb_init_ctrl,
+	.encode_msg = ixxat_usb_encode_msg
+};
diff --git a/drivers/net/can/usb/ixxat_usb/ixxat_usb_core.c b/drivers/net/can/usb/ixxat_usb/ixxat_usb_core.c
new file mode 100644
index 0000000..9e94a19
--- /dev/null
+++ b/drivers/net/can/usb/ixxat_usb/ixxat_usb_core.c
@@ -0,0 +1,1039 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* CAN driver for IXXAT USB-to-CAN
+ *
+ * Copyright (C) 2018 HMS Industrial Networks <socketcan@xxxxxxxxxxxxxxx>
+ *
+ * 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/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+#include <linux/can/netlink.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/usb.h>
+
+#include "ixxat_usb_core.h"
+
+MODULE_AUTHOR("Marcel Schmidt <socketcan@xxxxxxxxxxxxxxx>");
+MODULE_DESCRIPTION("CAN driver for IXXAT USB-to-CAN FD adapters");
+MODULE_LICENSE("GPL v2");
+
+/* Table of devices that work with this driver */
+static const struct usb_device_id ixxat_usb_table[] = {
+	{ USB_DEVICE(IXXAT_USB_VENDOR_ID, USB2CAN_COMPACT_PRODUCT_ID) },
+	{ USB_DEVICE(IXXAT_USB_VENDOR_ID, USB2CAN_EMBEDDED_PRODUCT_ID) },
+	{ USB_DEVICE(IXXAT_USB_VENDOR_ID, USB2CAN_PROFESSIONAL_PRODUCT_ID) },
+	{ USB_DEVICE(IXXAT_USB_VENDOR_ID, USB2CAN_AUTOMOTIVE_PRODUCT_ID) },
+	{ USB_DEVICE(IXXAT_USB_VENDOR_ID, USB2CAN_FD_COMPACT_PRODUCT_ID) },
+	{ USB_DEVICE(IXXAT_USB_VENDOR_ID, USB2CAN_FD_PROFESSIONAL_PRODUCT_ID) },
+	{ USB_DEVICE(IXXAT_USB_VENDOR_ID, USB2CAN_FD_AUTOMOTIVE_PRODUCT_ID) },
+	{ USB_DEVICE(IXXAT_USB_VENDOR_ID, USB2CAN_FD_PCIE_MINI_PRODUCT_ID) },
+	{ USB_DEVICE(IXXAT_USB_VENDOR_ID, USB2CAR_PRODUCT_ID) },
+	{ USB_DEVICE(IXXAT_USB_VENDOR_ID, CAN_IDM101_PRODUCT_ID) },
+	{ } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, ixxat_usb_table);
+
+void ixxat_usb_update_ts_now(struct ixxat_usb_device *dev, u32 ts_now)
+{
+	u64 timebase;
+	u32 *ts_dev = &dev->time_ref.ts_dev_0;
+	ktime_t *kt_host = &dev->time_ref.kt_host_0;
+
+	timebase = (u64)0x00000000FFFFFFFF - (u64)(*ts_dev) + (u64)ts_now;
+
+	*kt_host = ktime_add_us(*kt_host, timebase);
+	*ts_dev = ts_now;
+}
+
+void ixxat_usb_get_ts_tv(struct ixxat_usb_device *dev, u32 ts, ktime_t *k_time)
+{
+	ktime_t tmp_time = dev->time_ref.kt_host_0;
+
+	if (ts < dev->time_ref.ts_dev_last)
+		ixxat_usb_update_ts_now(dev, ts);
+
+	dev->time_ref.ts_dev_last = ts;
+	tmp_time = ktime_add_us(tmp_time, ts - dev->time_ref.ts_dev_0);
+
+	if (k_time)
+		*k_time = tmp_time;
+}
+
+void ixxat_usb_set_ts_now(struct ixxat_usb_device *dev, u32 ts_now)
+{
+	struct timeval tmp_timeval;
+
+	dev->time_ref.ts_dev_0 = ts_now;
+	do_gettimeofday(&tmp_timeval);
+	dev->time_ref.kt_host_0 = timeval_to_ktime(tmp_timeval);
+	dev->time_ref.ts_dev_last = ts_now;
+}
+
+int ixxat_usb_send_cmd(struct usb_device *dev,
+		       struct ixxat_usb_dal_req *dal_req)
+{
+	int ret = 0;
+	int i;
+	u16 size;
+	u16 value;
+	u8 request;
+	u8 requesttype;
+	u8 *buf;
+
+	request = 0xff;
+	requesttype = USB_TYPE_VENDOR | USB_DIR_OUT;
+	value = le16_to_cpu(dal_req->req_port);
+	size = le32_to_cpu(dal_req->req_size);
+	size += sizeof(struct ixxat_usb_dal_res);
+
+	buf = kmalloc(size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	memcpy(buf, (u8 *)dal_req, size);
+
+	for (i = 0; i < IXXAT_USB_MAX_COM_REQ; ++i) {
+		const int to = msecs_to_jiffies(IXXAT_USB_MSG_TIMEOUT);
+
+		ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
+				      requesttype, value, 0, buf, size, to);
+
+		if (ret < 0)
+			msleep(IXXAT_USB_MSG_CYCLE);
+		else
+			break;
+	}
+
+	kfree(buf);
+
+	if (ret < 0)
+		dev_err(&dev->dev, "Error %d: Sending command failure\n", ret);
+
+	return ret;
+}
+
+int ixxat_usb_rcv_cmd(struct usb_device *dev,
+		      struct ixxat_usb_dal_res *dal_res, int value)
+{
+	int ret;
+	int res_size;
+	int i;
+	int size_to_read;
+	u8 req;
+	u8 req_type;
+	u8 *buf;
+
+	req = 0xff;
+	req_type = USB_TYPE_VENDOR | USB_DIR_IN;
+	res_size = 0;
+	size_to_read = le32_to_cpu(dal_res->res_size);
+
+	buf = kmalloc(size_to_read, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	for (i = 0; i < IXXAT_USB_MAX_COM_REQ; ++i) {
+		const int to = msecs_to_jiffies(IXXAT_USB_MSG_TIMEOUT);
+		void *data = buf + (u8)res_size;
+		const int size = size_to_read - res_size;
+
+		ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+				      req, req_type, value, 0,
+				      data, size, to);
+
+		if (ret < 0) {
+			msleep(IXXAT_USB_MSG_CYCLE);
+			continue;
+		}
+
+		res_size += ret;
+		if (res_size < size_to_read)
+			msleep(IXXAT_USB_MSG_CYCLE);
+		else
+			break;
+	}
+
+	if (res_size != size_to_read)
+		ret = -EBADMSG;
+
+	if (ret < 0) {
+		dev_err(&dev->dev, "Error %d: Receiving command failure\n",
+			ret);
+		kfree(buf);
+		return ret;
+	}
+
+	memcpy((u8 *)dal_res, buf, size_to_read);
+	kfree(buf);
+
+	return ret;
+}
+
+static int ixxat_usb_get_dev_caps(struct usb_device *dev,
+				  struct ixxat_dev_caps *dev_caps)
+{
+	int i;
+	int ret;
+	u8 data[IXXAT_USB_CMD_BUFFER_SIZE] = { 0 };
+	struct ixxat_usb_dev_caps_req *req;
+	struct ixxat_usb_dev_caps_res *res;
+	u32 req_size = sizeof(*req);
+	u32 res_size = sizeof(*res);
+
+	req = (struct ixxat_usb_dev_caps_req *)data;
+	res = (struct ixxat_usb_dev_caps_res *)(data + req_size);
+
+	req->dal_req.req_size = cpu_to_le32(req_size);
+	req->dal_req.req_code = cpu_to_le32(IXXAT_USB_BRD_CMD_GET_DEVCAPS);
+	req->dal_req.req_port = 0xffff;
+	req->dal_req.req_socket = 0xffff;
+
+	res->dal_res.res_size = cpu_to_le32(res_size);
+	res->dal_res.ret_size = 0;
+	res->dal_res.ret_code = 0xffffffff;
+
+	ret = ixxat_usb_send_cmd(dev, &req->dal_req);
+	if (ret < 0)
+		return ret;
+
+	ret = ixxat_usb_rcv_cmd(dev, &res->dal_res, 0xffff);
+	if (ret < 0)
+		return ret;
+
+	dev_caps->bus_ctrl_count = le16_to_cpu(res->dev_caps.bus_ctrl_count);
+	for (i = 0; i < dev_caps->bus_ctrl_count; ++i) {
+		u16 type = le16_to_cpu(res->dev_caps.bus_ctrl_types[i]);
+
+		dev_caps->bus_ctrl_types[i] = type;
+	}
+
+	return 0;
+}
+
+static int ixxat_usb_get_dev_info(struct ixxat_usb_device *dev,
+				  struct ixxat_usb_dev_info *dev_info)
+{
+	int ret;
+	u8 data[IXXAT_USB_CMD_BUFFER_SIZE] = { 0 };
+	struct ixxat_usb_dev_info_req *req;
+	struct ixxat_usb_dev_info_res *res;
+	u32 req_size = sizeof(*req);
+	u32 res_size = sizeof(*res);
+
+	req = (struct ixxat_usb_dev_info_req *)data;
+	res = (struct ixxat_usb_dev_info_res *)(data + req_size);
+
+	req->dal_req.req_size = cpu_to_le32(req_size);
+	req->dal_req.req_code = cpu_to_le32(IXXAT_USB_BRD_CMD_GET_DEVINFO);
+	req->dal_req.req_port = 0xffff;
+	req->dal_req.req_socket = 0xffff;
+
+	res->dal_res.res_size = cpu_to_le32(res_size);
+	res->dal_res.ret_size = 0;
+	res->dal_res.ret_code = 0xffffffff;
+
+	ret = ixxat_usb_send_cmd(dev->udev, &req->dal_req);
+	if (ret < 0)
+		return ret;
+
+	ret = ixxat_usb_rcv_cmd(dev->udev, &res->dal_res, 0xffff);
+	if (ret < 0)
+		return ret;
+
+	if (dev_info) {
+		const size_t id_size = sizeof(res->info.device_id);
+		const size_t nm_size = sizeof(res->info.device_name);
+		const u32 fpgav = le32_to_cpu(res->info.device_fpga_version);
+		const u16 devv = le16_to_cpu(res->info.device_version);
+
+		memcpy(dev_info->device_id, &res->info.device_id, id_size);
+		memcpy(dev_info->device_name, &res->info.device_name, nm_size);
+		dev_info->device_fpga_version = fpgav;
+		dev_info->device_version = devv;
+	}
+
+	return le32_to_cpu(res->dal_res.ret_code);
+}
+
+static int ixxat_usb_start_ctrl(struct ixxat_usb_device *dev, u32 *time_ref)
+{
+	int ret = -ENODEV;
+	u8 data[IXXAT_USB_CMD_BUFFER_SIZE] = { 0 };
+	struct ixxat_usb_ctrl_start_req *req;
+	struct ixxat_usb_ctrl_start_res *res;
+	u32 req_size = sizeof(*req);
+	u32 res_size = sizeof(*res);
+
+	req = (struct ixxat_usb_ctrl_start_req *)data;
+	res = (struct ixxat_usb_ctrl_start_res *)(data + req_size);
+
+	req->dal_req.req_size = cpu_to_le32(req_size);
+	req->dal_req.req_code = cpu_to_le32(IXXAT_USB_CAN_CMD_START);
+	req->dal_req.req_port = cpu_to_le16(dev->ctrl_index);
+	req->dal_req.req_socket = 0xffff;
+
+	res->dal_res.res_size = cpu_to_le32(res_size);
+	res->dal_res.ret_size = 0;
+	res->dal_res.ret_code = 0xffffffff;
+	res->start_time = 0;
+
+	ret = ixxat_usb_send_cmd(dev->udev, &req->dal_req);
+	if (ret < 0)
+		return ret;
+
+	ret = ixxat_usb_rcv_cmd(dev->udev, &res->dal_res, dev->ctrl_index);
+	if (ret < 0)
+		return ret;
+
+	if (time_ref)
+		*time_ref = le32_to_cpu(res->start_time);
+
+	return le32_to_cpu(res->dal_res.ret_code);
+}
+
+static int ixxat_usb_stop_ctrl(struct ixxat_usb_device *dev)
+{
+	int ret = -ENODEV;
+	u8 data[IXXAT_USB_CMD_BUFFER_SIZE] = { 0 };
+	struct ixxat_usb_ctrl_stop_req *req;
+	struct ixxat_usb_ctrl_stop_res *res;
+	u32 req_size = sizeof(*req);
+	u32 res_size = sizeof(*res);
+
+	req = (struct ixxat_usb_ctrl_stop_req *)data;
+	res = (struct ixxat_usb_ctrl_stop_res *)(data + req_size);
+
+	req->dal_req.req_size = cpu_to_le32(req_size);
+	req->dal_req.req_code = cpu_to_le32(IXXAT_USB_CAN_CMD_STOP);
+	req->dal_req.req_port = cpu_to_le16(dev->ctrl_index);
+	req->dal_req.req_socket = 0xffff;
+	req->action = cpu_to_le32(IXXAT_USB_STOP_ACTION_CLEARALL);
+
+	res->dal_res.res_size = cpu_to_le32(res_size);
+	res->dal_res.ret_size = 0;
+	res->dal_res.ret_code = 0xffffffff;
+
+	ret = ixxat_usb_send_cmd(dev->udev, &req->dal_req);
+	if (ret < 0)
+		return ret;
+
+	ret = ixxat_usb_rcv_cmd(dev->udev, &res->dal_res, dev->ctrl_index);
+	if (ret < 0)
+		return ret;
+
+	if (!le32_to_cpu(res->dal_res.ret_code))
+		dev->can.state = CAN_STATE_STOPPED;
+
+	return le32_to_cpu(res->dal_res.ret_code);
+}
+
+static int ixxat_usb_power_ctrl(struct usb_device *dev, u8 mode)
+{
+	int ret = -ENODEV;
+	u8 data[IXXAT_USB_CMD_BUFFER_SIZE] = { 0 };
+	struct ixxat_usb_power_req *req;
+	struct ixxat_usb_power_res *res;
+	u32 req_size = sizeof(*req);
+	u32 res_size = sizeof(*res);
+
+	req = (struct ixxat_usb_power_req *)data;
+	res = (struct ixxat_usb_power_res *)(data + req_size);
+
+	req->dal_req.req_size = cpu_to_le32(req_size);
+	req->dal_req.req_code = cpu_to_le32(IXXAT_USB_BRD_CMD_POWER);
+	req->dal_req.req_port = cpu_to_le16(0xffff);
+	req->dal_req.req_socket = 0xffff;
+	req->mode = mode;
+
+	res->dal_res.res_size = cpu_to_le32(res_size);
+	res->dal_res.ret_size = 0;
+	res->dal_res.ret_code = 0xffffffff;
+
+	ret = ixxat_usb_send_cmd(dev, &req->dal_req);
+	if (ret < 0)
+		return ret;
+
+	ret = ixxat_usb_rcv_cmd(dev, &res->dal_res, 0xffff);
+	if (ret < 0)
+		return ret;
+
+	return le32_to_cpu(res->dal_res.ret_code);
+}
+
+static int ixxat_usb_reset_ctrl(struct ixxat_usb_device *dev)
+{
+	int ret = -ENODEV;
+	u8 data[IXXAT_USB_CMD_BUFFER_SIZE] = { 0 };
+	struct ixxat_usb_dal_req *req;
+	struct ixxat_usb_dal_res *res;
+	u32 req_size = sizeof(*req);
+	u32 res_size = sizeof(*res);
+
+	req = (struct ixxat_usb_dal_req *)data;
+	res = (struct ixxat_usb_dal_res *)(data + req_size);
+
+	req->req_size = cpu_to_le32(req_size);
+	req->req_code = cpu_to_le32(IXXAT_USB_CAN_CMD_RESET);
+	req->req_port = cpu_to_le16(dev->ctrl_index);
+	req->req_socket = 0xffff;
+
+	res->res_size = cpu_to_le32(res_size);
+	res->ret_size = 0;
+	res->ret_code = 0xffffffff;
+
+	ret = ixxat_usb_send_cmd(dev->udev, req);
+	if (ret < 0)
+		return ret;
+
+	ret = ixxat_usb_rcv_cmd(dev->udev, res, dev->ctrl_index);
+	if (ret < 0)
+		return ret;
+
+	return le32_to_cpu(res->ret_code);
+}
+
+static void ixxat_usb_unlink_all_urbs(struct ixxat_usb_device *dev)
+{
+	usb_kill_anchored_urbs(&dev->rx_submitted);
+	usb_kill_anchored_urbs(&dev->tx_submitted);
+	atomic_set(&dev->active_tx_urbs, 0);
+}
+
+static int ixxat_usb_set_mode(struct net_device *netdev, enum can_mode mode)
+{
+	struct ixxat_usb_device *dev = netdev_priv(netdev);
+
+	switch (mode) {
+	case CAN_MODE_START:
+		dev->restart_flag = 1;
+		wake_up_interruptible(&dev->wait_queue);
+		break;
+	case CAN_MODE_STOP:
+	case CAN_MODE_SLEEP:
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static int ixxat_usb_get_berr_counter(const struct net_device *netdev,
+				      struct can_berr_counter *bec)
+{
+	struct ixxat_usb_device *dev = netdev_priv(netdev);
+
+	*bec = dev->bec;
+	return 0;
+}
+
+static void ixxat_usb_read_bulk(struct urb *urb)
+{
+	struct ixxat_usb_device *dev = urb->context;
+	const struct ixxat_usb_adapter *adapter = dev->adapter;
+	struct net_device *netdev = dev->netdev;
+	struct usb_device *udev = dev->udev;
+	int ret;
+
+	if (!netif_device_present(netdev))
+		return;
+
+	switch (urb->status) {
+	case 0: /* success */
+		break;
+	case -EPROTO:
+	case -EILSEQ:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		return;
+	default:
+		netdev_err(netdev, "Rx urb aborted /(%d)\n", urb->status);
+		goto resubmit_urb;
+	}
+
+	if (urb->actual_length > 0)
+		if (dev->state & IXXAT_USB_STATE_STARTED)
+			if (adapter->decode_buf)
+				ret = adapter->decode_buf(urb);
+
+resubmit_urb:
+	usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, dev->ep_msg_in),
+			  urb->transfer_buffer, adapter->buffer_size_rx,
+			  ixxat_usb_read_bulk, dev);
+
+	usb_anchor_urb(urb, &dev->rx_submitted);
+	ret = usb_submit_urb(urb, GFP_ATOMIC);
+	if (ret == 0)
+		return;
+
+	usb_unanchor_urb(urb);
+
+	if (ret == -ENODEV)
+		netif_device_detach(netdev);
+	else
+		netdev_err(netdev,
+			   "Error %d: Failed to resubmit read bulk urb\n", ret);
+}
+
+static void ixxat_usb_write_bulk(struct urb *urb)
+{
+	struct ixxat_tx_urb_context *context = urb->context;
+	struct ixxat_usb_device *dev;
+	struct net_device *netdev;
+
+	if (WARN_ON(!context))
+		return;
+
+	dev = context->dev;
+	netdev = dev->netdev;
+	atomic_dec(&dev->active_tx_urbs);
+
+	if (!netif_device_present(netdev))
+		return;
+
+	if (!urb->status) {
+		netdev->stats.tx_packets += context->count;
+		netdev->stats.tx_bytes += context->dlc;
+	} else {
+		netdev_err(netdev, "Error %d: Tx urb aborted\n", urb->status);
+	}
+
+	can_get_echo_skb(netdev, context->echo_index);
+	context->echo_index = IXXAT_USB_MAX_TX_URBS;
+
+	if (!urb->status)
+		netif_wake_queue(netdev);
+}
+
+static netdev_tx_t ixxat_usb_start_xmit(struct sk_buff *skb,
+					struct net_device *netdev)
+{
+	struct ixxat_usb_device *dev = netdev_priv(netdev);
+	const struct ixxat_usb_adapter *adapter = dev->adapter;
+	struct ixxat_tx_urb_context *context = NULL;
+	struct net_device_stats *stats = &netdev->stats;
+	struct canfd_frame *cf = (struct canfd_frame *)skb->data;
+	struct urb *urb;
+	u8 *obuf;
+	int i, ret;
+	size_t size = adapter->buffer_size_tx;
+
+	if (can_dropped_invalid_skb(netdev, skb))
+		return NETDEV_TX_OK;
+
+	for (i = 0; i < IXXAT_USB_MAX_TX_URBS; i++) {
+		if (dev->tx_contexts[i].echo_index == IXXAT_USB_MAX_TX_URBS) {
+			context = dev->tx_contexts + i;
+			break;
+		}
+	}
+
+	if (WARN_ON_ONCE(!context))
+		return NETDEV_TX_BUSY;
+
+	urb = context->urb;
+	obuf = urb->transfer_buffer;
+
+	adapter->encode_msg(dev, skb, obuf, &size);
+
+	context->echo_index = i;
+	context->dlc = cf->len;
+	context->count = 1;
+
+	urb->transfer_buffer_length = size;
+	usb_anchor_urb(urb, &dev->tx_submitted);
+	can_put_echo_skb(skb, netdev, context->echo_index);
+	atomic_inc(&dev->active_tx_urbs);
+
+	ret = usb_submit_urb(urb, GFP_ATOMIC);
+	if (unlikely(ret)) {
+		can_free_echo_skb(netdev, context->echo_index);
+		usb_unanchor_urb(urb);
+		atomic_dec(&dev->active_tx_urbs);
+
+		context->echo_index = IXXAT_USB_MAX_TX_URBS;
+
+		if (ret == -ENODEV) {
+			netif_device_detach(netdev);
+		} else {
+			stats->tx_dropped++;
+			netdev_err(netdev,
+				   "Error %d: Submitting tx-urb failed\n", ret);
+		}
+	} else {
+		if (atomic_read(&dev->active_tx_urbs) >= IXXAT_USB_MAX_TX_URBS)
+			netif_stop_queue(netdev);
+	}
+
+	return NETDEV_TX_OK;
+}
+
+static int ixxat_usb_setup_rx_urbs(struct ixxat_usb_device *dev)
+{
+	int i;
+	int ret = 0;
+	const struct ixxat_usb_adapter *adapter = dev->adapter;
+	struct net_device *netdev = dev->netdev;
+	struct usb_device *udev = dev->udev;
+
+	for (i = 0; i < IXXAT_USB_MAX_RX_URBS; i++) {
+		struct urb *urb;
+		u8 *buf;
+
+		urb = usb_alloc_urb(0, GFP_KERNEL);
+		if (!urb) {
+			ret = -ENOMEM;
+			netdev_err(netdev, "Error %d: No memory for URBs\n",
+				   ret);
+			break;
+		}
+
+		buf = kmalloc(adapter->buffer_size_rx, GFP_KERNEL);
+		if (!buf) {
+			usb_free_urb(urb);
+			ret = -ENOMEM;
+			netdev_err(netdev,
+				   "Error %d: No memory for USB-buffer\n", ret);
+			break;
+		}
+
+		usb_fill_bulk_urb(urb, udev,
+				  usb_rcvbulkpipe(udev, dev->ep_msg_in), buf,
+				  adapter->buffer_size_rx, ixxat_usb_read_bulk,
+				  dev);
+
+		urb->transfer_flags |= URB_FREE_BUFFER;
+		usb_anchor_urb(urb, &dev->rx_submitted);
+
+		ret = usb_submit_urb(urb, GFP_KERNEL);
+		if (ret < 0) {
+			usb_unanchor_urb(urb);
+			kfree(buf);
+			usb_free_urb(urb);
+
+			if (ret == -ENODEV)
+				netif_device_detach(netdev);
+
+			break;
+		}
+
+		usb_free_urb(urb);
+	}
+
+	if (i == 0)
+		netdev_err(netdev, "Error: Couldn't setup any rx-URBs\n");
+
+	return ret;
+}
+
+static int ixxat_usb_setup_tx_urbs(struct ixxat_usb_device *dev)
+{
+	int i;
+	int ret = 0;
+	const struct ixxat_usb_adapter *adapter = dev->adapter;
+	struct net_device *netdev = dev->netdev;
+	struct usb_device *udev = dev->udev;
+
+	for (i = 0; i < IXXAT_USB_MAX_TX_URBS; i++) {
+		struct ixxat_tx_urb_context *context;
+		struct urb *urb = NULL;
+		u8 *buf = NULL;
+
+		urb = usb_alloc_urb(0, GFP_KERNEL);
+		if (!urb) {
+			ret = -ENOMEM;
+			netdev_err(netdev, "Error %d: No memory for URBs\n",
+				   ret);
+			break;
+		}
+
+		buf = kmalloc(adapter->buffer_size_tx, GFP_KERNEL);
+		if (!buf) {
+			usb_free_urb(urb);
+			ret = -ENOMEM;
+			netdev_err(netdev,
+				   "Error %d: No memory for USB-buffer\n", ret);
+			break;
+		}
+
+		context = dev->tx_contexts + i;
+		context->dev = dev;
+		context->urb = urb;
+
+		usb_fill_bulk_urb(urb, udev,
+				  usb_sndbulkpipe(udev, dev->ep_msg_out), buf,
+				  adapter->buffer_size_tx, ixxat_usb_write_bulk,
+				  context);
+
+		urb->transfer_flags |= URB_FREE_BUFFER;
+	}
+
+	if (i == 0) {
+		netdev_err(netdev, "Error: Couldn't setup any tx-URBs\n");
+		usb_kill_anchored_urbs(&dev->rx_submitted);
+	}
+
+	return ret;
+}
+
+static void ixxat_usb_disconnect(struct usb_interface *intf)
+{
+	struct ixxat_usb_device *dev;
+	struct ixxat_usb_device *prev_dev;
+
+	/* unregister the given device and all previous devices */
+	for (dev = usb_get_intfdata(intf); dev; dev = prev_dev) {
+		prev_dev = dev->prev_dev;
+		unregister_netdev(dev->netdev);
+		free_candev(dev->netdev);
+	}
+
+	usb_set_intfdata(intf, NULL);
+}
+
+static int ixxat_usb_restart_task(void *user_data)
+{
+	u32 time_ref;
+	struct ixxat_usb_device *dev = user_data;
+
+	while (!kthread_should_stop()) {
+		if (!dev->must_quit) {
+			wait_event_interruptible(dev->wait_queue,
+						 dev->restart_flag);
+			if (!dev->must_quit) {
+				ixxat_usb_stop_ctrl(dev);
+				ixxat_usb_start_ctrl(dev, &time_ref);
+				dev->restart_flag = 0;
+				dev->can.state = CAN_STATE_ERROR_ACTIVE;
+			}
+		} else {
+			msleep(IXXAT_RESTART_TASK_CYCLE_TIME);
+		}
+	}
+	return 0;
+}
+
+static int ixxat_usb_init(struct ixxat_usb_device *dev)
+{
+	dev->restart_task = kthread_run(&ixxat_usb_restart_task, dev,
+					"restart_thread");
+	if (!dev->restart_task)
+		return -ENOBUFS;
+
+	return 0;
+}
+
+static int ixxat_usb_start(struct ixxat_usb_device *dev)
+{
+	int ret;
+	int i;
+	u32 time_ref = 0;
+	const struct ixxat_usb_adapter *adapter = dev->adapter;
+
+	ret = ixxat_usb_setup_rx_urbs(dev);
+	if (ret < 0)
+		return ret;
+
+	ret = ixxat_usb_setup_tx_urbs(dev);
+	if (ret < 0)
+		return ret;
+
+	/* Try to reset the controller, in case it is already initialized
+	 * from a previous unclean shutdown
+	 */
+	ixxat_usb_reset_ctrl(dev);
+
+	if (adapter->init_ctrl) {
+		ret = adapter->init_ctrl(dev);
+		if (ret < 0)
+			goto fail;
+	}
+
+	if (dev->ctrl_opened_count == 0) {
+		ret = ixxat_usb_start_ctrl(dev, &time_ref);
+		if (ret < 0)
+			goto fail;
+
+		ixxat_usb_set_ts_now(dev, time_ref);
+	}
+
+	dev->ctrl_opened_count++;
+
+	dev->bec.txerr = 0;
+	dev->bec.rxerr = 0;
+
+	dev->state |= IXXAT_USB_STATE_STARTED;
+	dev->can.state = CAN_STATE_ERROR_ACTIVE;
+
+	return 0;
+
+fail:
+	if (ret == -ENODEV)
+		netif_device_detach(dev->netdev);
+
+	netdev_err(dev->netdev, "Error %d: Couldn't submit control\n", ret);
+
+	for (i = 0; i < IXXAT_USB_MAX_TX_URBS; i++) {
+		usb_free_urb(dev->tx_contexts[i].urb);
+		dev->tx_contexts[i].urb = NULL;
+	}
+
+	return ret;
+}
+
+static int ixxat_usb_open(struct net_device *netdev)
+{
+	struct ixxat_usb_device *dev = netdev_priv(netdev);
+	int ret;
+
+	/* common open */
+	ret = open_candev(netdev);
+	if (ret < 0)
+		return ret;
+
+	/* finally start device */
+	ret = ixxat_usb_start(dev);
+	if (ret < 0) {
+		netdev_err(netdev, "Error %d: Couldn't start device.\n", ret);
+		close_candev(netdev);
+		return ret;
+	}
+
+	netif_start_queue(netdev);
+
+	return 0;
+}
+
+static int ixxat_usb_stop(struct net_device *netdev)
+{
+	int ret = 0;
+	struct ixxat_usb_device *dev = netdev_priv(netdev);
+
+	netif_stop_queue(netdev);
+	ixxat_usb_unlink_all_urbs(dev);
+
+	if (dev->ctrl_opened_count == 1) {
+		ret = ixxat_usb_stop_ctrl(dev);
+		if (ret < 0)
+			return ret;
+	}
+	dev->ctrl_opened_count--;
+
+	close_candev(netdev);
+
+	dev->can.state = CAN_STATE_STOPPED;
+
+	return 0;
+}
+
+static const struct net_device_ops ixxat_usb_netdev_ops = {
+	.ndo_open = ixxat_usb_open,
+	.ndo_stop = ixxat_usb_stop,
+	.ndo_start_xmit = ixxat_usb_start_xmit
+};
+
+static const struct ixxat_usb_adapter *ixxat_usb_get_adapter(const u16 id)
+{
+	switch (id) {
+	case USB2CAN_COMPACT_PRODUCT_ID:
+	case USB2CAN_EMBEDDED_PRODUCT_ID:
+	case USB2CAN_PROFESSIONAL_PRODUCT_ID:
+	case USB2CAN_AUTOMOTIVE_PRODUCT_ID:
+		return &usb2can_cl1;
+	case USB2CAN_FD_COMPACT_PRODUCT_ID:
+	case USB2CAN_FD_PROFESSIONAL_PRODUCT_ID:
+	case USB2CAN_FD_AUTOMOTIVE_PRODUCT_ID:
+	case USB2CAN_FD_PCIE_MINI_PRODUCT_ID:
+	case USB2CAR_PRODUCT_ID:
+		return &usb2can_cl2;
+	case CAN_IDM101_PRODUCT_ID:
+		return &can_idm;
+	default:
+		return NULL;
+	}
+}
+
+static int ixxat_usb_create_dev(struct usb_interface *intf,
+				const struct ixxat_usb_adapter *adapter,
+				u16 ctrl_index)
+{
+	struct usb_device *usb_dev = interface_to_usbdev(intf);
+	struct ixxat_usb_device *dev;
+	struct net_device *netdev;
+	int ret;
+	int i;
+
+	netdev = alloc_candev(sizeof(*dev), IXXAT_USB_MAX_TX_URBS);
+	if (!netdev) {
+		dev_err(&intf->dev, "Cannot allocate candev\n");
+		return -ENOMEM;
+	}
+
+	dev = netdev_priv(netdev);
+
+	dev->restart_task = NULL;
+	dev->restart_flag = 0;
+	dev->must_quit = 0;
+	init_waitqueue_head(&dev->wait_queue);
+
+	dev->ctrl_opened_count = 0;
+	dev->udev = usb_dev;
+	dev->netdev = netdev;
+	dev->adapter = adapter;
+	dev->ctrl_index = ctrl_index;
+	dev->state = IXXAT_USB_STATE_CONNECTED;
+
+	i = ctrl_index + adapter->ep_offs;
+	dev->ep_msg_in = adapter->ep_msg_in[i];
+	dev->ep_msg_out = adapter->ep_msg_out[i];
+
+	dev->can.clock.freq = adapter->clock;
+	dev->can.bittiming_const = adapter->bt;
+	dev->can.data_bittiming_const = adapter->btd;
+
+	dev->can.do_set_mode = ixxat_usb_set_mode;
+	dev->can.do_get_berr_counter = ixxat_usb_get_berr_counter;
+
+	dev->can.ctrlmode_supported = adapter->modes;
+
+	netdev->netdev_ops = &ixxat_usb_netdev_ops;
+
+	netdev->flags |= IFF_ECHO;
+
+	init_usb_anchor(&dev->rx_submitted);
+	init_usb_anchor(&dev->tx_submitted);
+
+	atomic_set(&dev->active_tx_urbs, 0);
+
+	for (i = 0; i < IXXAT_USB_MAX_TX_URBS; i++)
+		dev->tx_contexts[i].echo_index = IXXAT_USB_MAX_TX_URBS;
+
+	dev->prev_dev = usb_get_intfdata(intf);
+	usb_set_intfdata(intf, dev);
+
+	SET_NETDEV_DEV(netdev, &intf->dev);
+	ret = register_candev(netdev);
+	if (ret < 0) {
+		dev_err(&intf->dev, "Error %d: Failed to register can device\n",
+			ret);
+		goto fail;
+	}
+
+	if (dev->prev_dev)
+		(dev->prev_dev)->next_dev = dev;
+
+	ret = ixxat_usb_init(dev);
+	if (ret < 0) {
+		dev_err(&intf->dev,
+			"Error %d: Failed to initialize can device\n", ret);
+		goto fail;
+	}
+
+	ret = ixxat_usb_get_dev_info(dev, &dev->dev_info);
+	if (ret < 0) {
+		dev_err(&intf->dev,
+			"Error %d: Failed to get device information\n", ret);
+		goto fail;
+	}
+
+	netdev_info(netdev, "%s: Connected Channel %u (device %s)\n",
+		    dev->dev_info.device_name, ctrl_index,
+		    dev->dev_info.device_id);
+
+	return 0;
+
+fail:
+	usb_set_intfdata(intf, dev->prev_dev);
+	free_candev(netdev);
+	return ret;
+}
+
+static int ixxat_usb_probe(struct usb_interface *intf,
+			   const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct usb_host_interface *host_intf = intf->altsetting;
+	const struct ixxat_usb_adapter *adapter;
+	struct ixxat_dev_caps dev_caps;
+	u16 i;
+	int ret;
+
+	usb_reset_configuration(udev);
+
+	adapter = ixxat_usb_get_adapter(id->idProduct);
+	if (!adapter) {
+		dev_err(&intf->dev, "%s: Unknown device id %d\n",
+			IXXAT_USB_DRIVER_NAME, id->idProduct);
+		return -ENODEV;
+	}
+
+	for (i = 0; i < host_intf->desc.bNumEndpoints; i++) {
+		const u8 epaddr = host_intf->endpoint[i].desc.bEndpointAddress;
+		int match;
+		u8 j;
+
+		/* Check if usb-endpoint address matches known usb-endpoints */
+		for (j = 0; j < IXXAT_USB_MAX_CHANNEL; j++) {
+			u8 ep_msg_in = adapter->ep_msg_in[j];
+			u8 ep_msg_out = adapter->ep_msg_in[j];
+
+			if (epaddr == ep_msg_in || epaddr == ep_msg_out) {
+				match = 1;
+				break;
+			}
+		}
+
+		if (!match)
+			return -ENODEV;
+	}
+
+	ret = ixxat_usb_power_ctrl(udev, IXXAT_USB_POWER_WAKEUP);
+	if (ret < 0)
+		return ret;
+
+	msleep(IXXAT_USB_POWER_WAKEUP_TIME);
+
+	ret = ixxat_usb_get_dev_caps(udev, &dev_caps);
+	if (ret < 0) {
+		dev_err(&intf->dev, "Failed to get device capabilities\n");
+		return ret;
+	}
+
+	ret = -ENODEV;
+	for (i = 0; i < dev_caps.bus_ctrl_count; i++) {
+		u8 bustype = IXXAT_USB_BUS_TYPE(dev_caps.bus_ctrl_types[i]);
+
+		if (bustype == IXXAT_USB_BUS_CAN)
+			ret = ixxat_usb_create_dev(intf, adapter, i);
+
+		if (ret < 0) {
+			/* deregister already created devices */
+			ixxat_usb_disconnect(intf);
+			return ret;
+		}
+	}
+
+	return ret;
+}
+
+static struct usb_driver ixxat_usb_driver = {
+	.name = IXXAT_USB_DRIVER_NAME,
+	.probe = ixxat_usb_probe,
+	.disconnect = ixxat_usb_disconnect,
+	.id_table = ixxat_usb_table,
+};
+
+module_usb_driver(ixxat_usb_driver);
diff --git a/drivers/net/can/usb/ixxat_usb/ixxat_usb_core.h b/drivers/net/can/usb/ixxat_usb/ixxat_usb_core.h
new file mode 100644
index 0000000..c3bce32
--- /dev/null
+++ b/drivers/net/can/usb/ixxat_usb/ixxat_usb_core.h
@@ -0,0 +1,448 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/* CAN driver base for IXXAT USB-to-CAN
+ *
+ * Copyright (C) 2018 HMS Industrial Networks <socketcan@xxxxxxxxxxxxxxx>
+ *
+ * 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 IXXAT_USB_CORE_H
+#define IXXAT_USB_CORE_H
+
+#define IXXAT_USB_DRIVER_NAME  "ixxat_usb2can"
+
+#define IXXAT_USB_VENDOR_ID  0x08d8
+
+/* supported device ids: CL1 */
+#define USB2CAN_COMPACT_PRODUCT_ID          0x0008
+#define USB2CAN_EMBEDDED_PRODUCT_ID         0x0009
+#define USB2CAN_PROFESSIONAL_PRODUCT_ID     0x000A
+#define USB2CAN_AUTOMOTIVE_PRODUCT_ID       0x000B
+
+/* supported device ids: CL2 */
+#define USB2CAN_FD_COMPACT_PRODUCT_ID       0x0014
+#define USB2CAN_FD_PROFESSIONAL_PRODUCT_ID  0x0016
+#define USB2CAN_FD_AUTOMOTIVE_PRODUCT_ID    0x0017
+#define USB2CAN_FD_PCIE_MINI_PRODUCT_ID     0x001B
+#define USB2CAR_PRODUCT_ID                  0x001C
+#define CAN_IDM101_PRODUCT_ID               0xFF12
+
+#define IXXAT_USB_BUS_CAN  1
+
+#define IXXAT_USB_BUS_TYPE(type)  ((u8)(((type) >> 8) & 0x00FF))
+
+#define IXXAT_USB_STATE_CONNECTED  0x00000001
+#define IXXAT_USB_STATE_STARTED    0x00000002
+
+#define IXXAT_USB_MAX_CHANNEL   5
+#define IXXAT_USB_MAX_TYPES    32
+#define IXXAT_USB_MAX_RX_URBS   4
+#define IXXAT_USB_MAX_TX_URBS  10
+#define IXXAT_USB_MAX_COM_REQ  10
+
+#define IXXAT_USB_MSG_TIMEOUT  50
+#define IXXAT_USB_MSG_CYCLE    20
+
+#define IXXAT_USB_POWER_WAKEUP        0
+#define IXXAT_USB_POWER_WAKEUP_TIME 500
+
+#define IXXAT_USB_CMD_BUFFER_SIZE  256
+
+#define IXXAT_USB_OPMODE_STANDARD  0x01
+#define IXXAT_USB_OPMODE_EXTENDED  0x02
+#define IXXAT_USB_OPMODE_ERRFRAME  0x04
+#define IXXAT_USB_OPMODE_LISTONLY  0x08
+
+#define IXXAT_USB_EXMODE_EXTDATA   0x01
+#define IXXAT_USB_EXMODE_FASTDATA  0x02
+#define IXXAT_USB_EXMODE_ISOFD     0x04
+
+#define IXXAT_USB_BTMODE_NAT  0x00000001
+#define IXXAT_USB_BTMODE_TSM  0x00000002
+
+#define IXXAT_USB_STOP_ACTION_CLEARALL  3
+
+#define IXXAT_RESTART_TASK_CYCLE_TIME  20
+
+#define IXXAT_USB_CAN_DATA     0x00
+#define IXXAT_USB_CAN_INFO     0x01
+#define IXXAT_USB_CAN_ERROR    0x02
+#define IXXAT_USB_CAN_STATUS   0x03
+#define IXXAT_USB_CAN_WAKEUP   0x04
+#define IXXAT_USB_CAN_TIMEOVR  0x05
+#define IXXAT_USB_CAN_TIMERST  0x06
+
+#define IXXAT_USB_CAN_STATUS_OK      0x00000000
+#define IXXAT_USB_CAN_STATUS_OVERRUN 0x00000002
+#define IXXAT_USB_CAN_STATUS_ERRLIM  0x00000004
+#define IXXAT_USB_CAN_STATUS_BUSOFF  0x00000008
+#define IXXAT_USB_CAN_STATUS_ERR_PAS 0x00002000
+
+#define IXXAT_USB_CAN_ERROR_STUFF      1
+#define IXXAT_USB_CAN_ERROR_FORM       2
+#define IXXAT_USB_CAN_ERROR_ACK        3
+#define IXXAT_USB_CAN_ERROR_BIT        4
+#define IXXAT_USB_CAN_ERROR_CRC        6
+
+#define IXXAT_USB_MSG_FLAGS_TYPE  0x000000FF
+#define IXXAT_USB_MSG_FLAGS_DLC   0x000F0000
+#define IXXAT_USB_MSG_FLAGS_OVR   0x00100000
+#define IXXAT_USB_MSG_FLAGS_RTR   0x00400000
+#define IXXAT_USB_MSG_FLAGS_EXT   0x00800000
+
+#define IXXAT_USB_MSG_FLAGS_DLC   0x000F0000
+#define IXXAT_USB_MSG_FLAGS_RTR   0x00400000
+#define IXXAT_USB_MSG_FLAGS_EXT   0x00800000
+
+#define IXXAT_USB_FDMSG_FLAGS_EDL  0x00000400
+#define IXXAT_USB_FDMSG_FLAGS_FDR  0x00000800
+#define IXXAT_USB_FDMSG_FLAGS_ESI  0x00001000
+
+#define IXXAT_USB_CAN_CMD_START     0x326
+#define IXXAT_USB_CAN_CMD_STOP      0x327
+#define IXXAT_USB_CAN_CMD_RESET     0x328
+
+#define IXXAT_USB_BRD_CMD_GET_DEVCAPS  0x401
+#define IXXAT_USB_BRD_CMD_GET_DEVINFO  0x402
+#define IXXAT_USB_BRD_CMD_POWER        0x421
+
+/**
+ * struct ixxat_dev_caps - Device capabilities
+ * @bus_ctrl_count: Stores the bus controller counter
+ * @bus_ctrl_types: Stores the bus controller types
+ *
+ * Contains the device capabilities needed to identifies the device
+ */
+struct ixxat_dev_caps {
+	u16 bus_ctrl_count;
+	u16 bus_ctrl_types[IXXAT_USB_MAX_TYPES];
+} __packed;
+
+/**
+ * struct ixxat_usb_dev_info Ixxat usb device information
+ * @device_name: Name of the device
+ * @device_id: device identification ( unique device id)
+ * @device_version: device version ( 0, 1, ...)
+ * @device_fpga_version: device version of FPGA design
+ *
+ * Contains device information for ixxat usb devices
+ */
+struct ixxat_usb_dev_info {
+	char device_name[16];
+	char device_id[16];
+	u16  device_version;
+	u32  device_fpga_version;
+} __packed;
+
+/**
+ * struct ixxat_time_ref Time reference
+ * @kt_host_0: latest time on the host
+ * @ts_dev_0: latest time stamp on the device
+ * @ts_dev_last: last device time stamp
+ *
+ * Contains time references from the device and host
+ */
+struct ixxat_time_ref {
+	ktime_t kt_host_0;
+	u32 ts_dev_0;
+	u32 ts_dev_last;
+};
+
+/**
+ * struct ixxat_tx_urb_context URB content for transmission
+ * @dev: ixxat usb device
+ * echo_index: echo index
+ * @dlc: data length code
+ * @count: counter
+ * @urb: USB request block
+ *
+ * Contains content for USB request block transmissions
+ */
+struct ixxat_tx_urb_context {
+	struct ixxat_usb_device *dev;
+	u32 echo_index;
+	u8 dlc;
+	u8 count;
+	struct urb *urb;
+};
+
+/**
+ * struct ixxat_usb_device Ixxat usb device
+ * @can: can_priv
+ * @adapter: USB network descriptor
+ * @udev: usb device
+ * @netdev: net_device
+ * @active_tx_urbs: active tx urbs
+ * @tx_submitted: submitted tx usb anchor
+ * @tx_contexts: Buffer for tx contexts
+ * @rx_submitted: submitted rx usb anchor
+ * @state: device state
+ * @ctrl_opened_count: Counter for opened controllers
+ * @ctrl_index: Controller index
+ * @ep_msg_in: USB endpoint for incoming messages
+ * @ep_msg_out: USB endpoint for outgoing messages
+ * @restart_task: Task to restart everything if needed
+ * @restart_flag: Flag to determine if it should be restarted
+ * @must_quit: Flag to determine if it should be quit
+ * @wait_queue: Head of the wait queue
+ * @prev_dev: Previous opened device
+ * @next_dev: Next opened device in list
+ * @time_ref: Time reference
+ * @dev_info: Device information
+ * @bec: can error counter
+ *
+ * Ixxat usb to can device
+ */
+struct ixxat_usb_device {
+	struct can_priv can;
+	const struct ixxat_usb_adapter *adapter;
+	struct usb_device *udev;
+	struct net_device *netdev;
+
+	atomic_t active_tx_urbs;
+	struct usb_anchor tx_submitted;
+	struct ixxat_tx_urb_context tx_contexts[IXXAT_USB_MAX_TX_URBS];
+	struct usb_anchor rx_submitted;
+
+	u32 state;
+
+	u32 ctrl_opened_count;
+	u16 ctrl_index;
+
+	u8 ep_msg_in;
+	u8 ep_msg_out;
+
+	struct task_struct *restart_task;
+	u8 restart_flag;
+	u8 must_quit;
+	wait_queue_head_t wait_queue;
+
+	struct ixxat_usb_device *prev_dev;
+	struct ixxat_usb_device *next_dev;
+
+	struct ixxat_time_ref time_ref;
+	struct ixxat_usb_dev_info dev_info;
+
+	struct can_berr_counter bec;
+};
+
+/**
+ * struct ixxat_usb_dal_req Ixxat device request block
+ * @req_size: size of the request
+ * @req_port: port of the request
+ * @req_socket: socket of the request
+ * @req_code: code of the request
+ *
+ * Can be send to the Ixxat device to request information
+ */
+struct ixxat_usb_dal_req {
+	u32 req_size;
+	u16 req_port;
+	u16 req_socket;
+	u32 req_code;
+} __packed;
+
+/**
+ * struct ixxat_usb_dal_res Ixxat device respond block
+ * @res_size: size of the respond
+ * @ret_size: size of the return
+ * @ret_code: return code
+ *
+ * After a request, this respond block comes back
+ */
+struct ixxat_usb_dal_res {
+	u32 res_size;
+	u32 ret_size;
+	u32 ret_code;
+} __packed;
+
+/**
+ * struct ixxat_usb_dev_caps_req Ixxat device capabilities request block
+ * @dal_req: Request block
+ *
+ * Can be send to the Ixxat device to request device capabilities
+ */
+struct ixxat_usb_dev_caps_req {
+	struct ixxat_usb_dal_req dal_req;
+} __packed;
+
+/**
+ * struct ixxat_usb_dev_caps_res Ixxat device respond block
+ * @dal_res: respond block
+ * @dev_caps: device capabilities
+ *
+ * Device response contains the device capabilities
+ */
+struct ixxat_usb_dev_caps_res {
+	struct ixxat_usb_dal_res dal_res;
+	struct ixxat_dev_caps dev_caps;
+} __packed;
+
+/**
+ * struct ixxat_usb_ctrl_init_res Ixxat initialization respond block
+ * @dal_res: respond block
+ *
+ * Device response to the initialization request
+ */
+struct ixxat_usb_ctrl_init_res {
+	struct ixxat_usb_dal_res dal_res;
+} __packed;
+
+/**
+ * struct ixxat_usb_ctrl_start_req Ixxat request block to start the controller
+ * @dal_req: Request block
+ *
+ * Can be send to the Ixxat device to request controller start
+ */
+struct ixxat_usb_ctrl_start_req {
+	struct ixxat_usb_dal_req dal_req;
+} __packed;
+
+/**
+ * struct ixxat_usb_ctrl_start_res Ixxat start respond block
+ * @dal_res: respond block
+ * @start_time: Device starting point
+ *
+ * Device response to the start request
+ */
+struct ixxat_usb_ctrl_start_res {
+	struct ixxat_usb_dal_res dal_res;
+	u32 start_time;
+} __packed;
+
+struct ixxat_usb_power_req {
+	struct ixxat_usb_dal_req dal_req;
+	u8 mode;
+	u8 _padding1;
+	u16 _padding2;
+} __packed;
+
+struct ixxat_usb_power_res {
+	struct ixxat_usb_dal_res dal_res;
+} __packed;
+
+/**
+ * struct ixxat_usb_ctrl_stop_req Ixxat request block to stop the controller
+ * @dal_req: Request block
+ * @action: Requested action
+ *
+ * Can be send to the Ixxat device to request controller stop
+ */
+struct ixxat_usb_ctrl_stop_req {
+	struct ixxat_usb_dal_req dal_req;
+	u32 action;
+} __packed;
+
+/**
+ * struct ixxat_usb_ctrl_stop_res Ixxat stop respond block
+ * @dal_res: respond block
+ *
+ * Device response to the stop request
+ */
+struct ixxat_usb_ctrl_stop_res {
+	struct ixxat_usb_dal_res dal_res;
+} __packed;
+
+/**
+ * struct ixxat_usb_dev_info_req Ixxat request block for device info
+ * @dal_req: Request block
+ *
+ * Can be send to the Ixxat device to request device information
+ */
+struct ixxat_usb_dev_info_req {
+	struct ixxat_usb_dal_req dal_req;
+} __packed;
+
+/**
+ * struct ixxat_usb_dev_info_res Ixxat device info respond block
+ * @dal_res: respond block
+ * @info: Device information
+ *
+ * Device response to the device information request
+ */
+struct ixxat_usb_dev_info_res {
+	struct ixxat_usb_dal_res dal_res;
+	struct ixxat_usb_dev_info info;
+} __packed;
+
+/**
+ * struct ixxat_usb_adapter USB network descriptor
+ * @clock: Clock frequency
+ * @bt: bittiming constants
+ * @btd: data bittiming constants
+ * @modes: supported modes
+ * @buffer_size_rx: Buffer size for receiving
+ * @buffer_size_tx: Buffer size for transfer
+ * @ep_msg_in: USB endpoint buffer for incoming messages
+ * @ep_msg_out: USB endpoint buffer for outgoing messages
+ * @ep_offs: Endpoint offset (device depended)
+ *
+ * Network descriptor for ixxat usb to can devices
+ */
+struct ixxat_usb_adapter {
+	const u32 clock;
+	const struct can_bittiming_const *bt;
+	const struct can_bittiming_const *btd;
+	const u32 modes;
+	const u16 buffer_size_rx;
+	const u16 buffer_size_tx;
+	const u8 ep_msg_in[IXXAT_USB_MAX_CHANNEL];
+	const u8 ep_msg_out[IXXAT_USB_MAX_CHANNEL];
+	const u8 ep_offs;
+	int (*decode_buf)(struct urb *urb);
+	int (*init_ctrl)(struct ixxat_usb_device *dev);
+	void (*encode_msg)(struct ixxat_usb_device *dev,
+			   struct sk_buff *skb, u8 *obuf,
+			   size_t *size);
+};
+
+extern const struct ixxat_usb_adapter usb2can_cl1;
+extern const struct ixxat_usb_adapter usb2can_cl2;
+extern const struct ixxat_usb_adapter can_idm;
+
+/**
+ * ixxat_usb_send_cmd() - Send a command to the can bus
+ * @dev: USB device
+ * @dal_req: Requested command
+ *
+ * This function sends a specific command to the device.
+ *
+ * Return: Error, if one occurred else 0
+ */
+int ixxat_usb_send_cmd(struct usb_device *dev,
+		       struct ixxat_usb_dal_req *dal_req);
+
+/**
+ * ixxat_usb_rcv_cmd() - Receive a command from the can bus
+ * @dev: USB device
+ * @dal_req: Responded command
+ * @value: Requested values
+ *
+ * This function receives a specific command from the device.
+ *
+ * Return: Error, if one occurred else 0
+ */
+int ixxat_usb_rcv_cmd(struct usb_device *dev, struct ixxat_usb_dal_res *dal_res,
+		      int value);
+
+/**
+ * ixxat_usb_get_ts_tv() - Get ktime timestamp from device timestamp
+ * @dev: USB device
+ * @ts: device timestamp
+ * @k_time: ktime timestamp
+ *
+ * This function gets a ktime timestamp from a device timestamp.
+ *
+ * Return: Error, if one occurred else 0
+ */
+void ixxat_usb_get_ts_tv(struct ixxat_usb_device *dev, u32 ts, ktime_t *k_time);
+
+#endif /* IXXAT_USB_CORE_H */
-- 
2.7.4

--
To unsubscribe from this list: send the line "unsubscribe linux-can" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Automotive Discussions]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [eCos]     [Asterisk Internet PBX]     [Linux API]     [CAN Bus]

  Powered by Linux