diff mbox series

NACK: [Xenial SRU][PATCH 1/2] UBUNTU: SAUCE: (no-up) Support IXXAT USB SocketCAN device

Message ID 68f36406-a977-8960-95f1-dc7128b36d29@canonical.com
State New
Headers show
Series NACK: [Xenial SRU][PATCH 1/2] UBUNTU: SAUCE: (no-up) Support IXXAT USB SocketCAN device | expand

Commit Message

Kleber Sacilotto de Souza June 5, 2018, 5:29 p.m. UTC
Hi Shrirang,

On 05/31/18 22:47, Shrirang Bagul wrote:
> BugLink: http://bugs.launchpad.net/bugs/1774563
> 
> This driver from IXXAT adds support for SocketCAN over USB.
> (https://www.ixxat.com)
> 
> Signed-off-by: Shrirang Bagul <shrirang.bagul@canonical.com>
> ---
>  ubuntu/Kconfig              |    3 +-
>  ubuntu/Makefile             |    5 +-
>  ubuntu/ixxat/Kconfig        |    8 +
>  ubuntu/ixxat/Makefile       |    4 +
>  ubuntu/ixxat/ixx_usb_core.c |  923 +++++++++++++++++++
>  ubuntu/ixxat/ixx_usb_core.h |  289 ++++++
>  ubuntu/ixxat/ixx_usb_fd.c   | 1673 +++++++++++++++++++++++++++++++++++
>  ubuntu/ixxat/ixx_usb_v2.c   | 1450 ++++++++++++++++++++++++++++++
>  8 files changed, 4353 insertions(+), 2 deletions(-)
>  create mode 100644 ubuntu/ixxat/Kconfig
>  create mode 100644 ubuntu/ixxat/Makefile
>  create mode 100644 ubuntu/ixxat/ixx_usb_core.c
>  create mode 100644 ubuntu/ixxat/ixx_usb_core.h
>  create mode 100644 ubuntu/ixxat/ixx_usb_fd.c
>  create mode 100644 ubuntu/ixxat/ixx_usb_v2.c
> 
> diff --git a/ubuntu/Kconfig b/ubuntu/Kconfig
> index bc2fb5530593..a3ad3d87ce53 100644
> --- a/ubuntu/Kconfig
> +++ b/ubuntu/Kconfig
> @@ -30,10 +30,11 @@ source "ubuntu/opennsl/Kconfig"
>  ##
>  ##
>  ##
> +source "ubuntu/bnxt/Kconfig"
>  ##
>  ##
>  ##
> -source "ubuntu/bnxt/Kconfig"
> +source "ubuntu/ixxat/Kconfig"
>  ##
>  ##
>  ##

Please add the 3 lines with "##" above the new entry so the patch
doesn't need to touch the exiting entries and avoid merge conflicts by
that. E.g.:



> diff --git a/ubuntu/Makefile b/ubuntu/Makefile
> index 85e1c900735c..62dd1e1b7b46 100644
> --- a/ubuntu/Makefile
> +++ b/ubuntu/Makefile
> @@ -44,11 +44,14 @@ obj-$(CONFIG_OPENNSL) += opennsl/
>  ##
>  ##
>  ##
> -##
>  obj-$(CONFIG_BNXT_BPO) += bnxt/
>  ##
>  ##
>  ##
> +obj-$(CONFIG_CAN_HMS_USB) += ixxat/
> +##
> +##
> +##
>  ##
>  ##
>  ##

Same here.


Thanks,
Kleber

> diff --git a/ubuntu/ixxat/Kconfig b/ubuntu/ixxat/Kconfig
> new file mode 100644
> index 000000000000..63ff0d054d9e
> --- /dev/null
> +++ b/ubuntu/ixxat/Kconfig
> @@ -0,0 +1,8 @@
> +config CAN_HMS_USB
> +	tristate "HMS USB SocketCAN"
> +	depends on X86 || X86_64
> +	depends on USB && CAN_DEV
> +	---help---
> +	This driver is from IXXAT and supports SocketCAN over USB.
> +	(https://www.ixxat.com)
> +
> diff --git a/ubuntu/ixxat/Makefile b/ubuntu/ixxat/Makefile
> new file mode 100644
> index 000000000000..d4ee67ebdd24
> --- /dev/null
> +++ b/ubuntu/ixxat/Makefile
> @@ -0,0 +1,4 @@
> +mod-name += ixx_usb
> +obj-m += ixx_usb.o
> +ixx_usb-objs := ixx_usb_v2.o ixx_usb_fd.o ixx_usb_core.o
> +
> diff --git a/ubuntu/ixxat/ixx_usb_core.c b/ubuntu/ixxat/ixx_usb_core.c
> new file mode 100644
> index 000000000000..d258b6e46453
> --- /dev/null
> +++ b/ubuntu/ixxat/ixx_usb_core.c
> @@ -0,0 +1,923 @@
> +/*
> + * CAN driver for IXXAT USB-to-CAN V2 adapters
> + *
> + * Copyright (C) 2003-2014 Michael Hengler IXXAT Automation GmbH
> + *
> + * Based on code originally by pcan_usb_core
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published
> + * by the Free Software Foundation; version 2 of the License.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + */
> +#include <linux/init.h>
> +#include <linux/signal.h>
> +#include <linux/slab.h>
> +#include <linux/module.h>
> +#include <linux/netdevice.h>
> +#include <linux/usb.h>
> +#include <linux/errno.h>
> +#include <linux/skbuff.h>
> +#include <linux/types.h>
> +#include <linux/can.h>
> +#include <linux/can/dev.h>
> +#include <linux/can/error.h>
> +#include <asm-generic/errno.h>
> +
> +#include "ixx_usb_core.h"
> +
> +MODULE_AUTHOR("Michael Hengler <mhengler@ixxat.de>");
> +MODULE_DESCRIPTION("CAN driver for IXXAT USB-to-CAN V2 adapters");
> +MODULE_LICENSE("GPL v2");
> +
> +#define IXXAT_USB_DRIVER_NAME	"ixx_usb"
> +
> +#define IXXAT_USB_BUS_CAN	1 // CAN
> +#define IXXAT_USB_BUS_TYPE(BusCtrl)  (u8)  ( ((BusCtrl) >> 8) & 0x00FF )
> +#define IXXAT_USB_VENDOR_ID	0x08d8
> +
> +#define IXXAT_USB_STATE_CONNECTED 0x00000001
> +#define IXXAT_USB_STATE_STARTED   0x00000002
> +
> +
> +/* Table of devices that work with this driver */
> +static struct usb_device_id ixxat_usb_table[] = {
> +	{USB_DEVICE(IXXAT_USB_VENDOR_ID, USB_TO_CAN_V2_COMPACT_PRODUCT_ID)},
> +	{USB_DEVICE(IXXAT_USB_VENDOR_ID, USB_TO_CAN_V2_EMBEDDED_PRODUCT_ID)},
> +	{USB_DEVICE(IXXAT_USB_VENDOR_ID, USB_TO_CAN_V2_PROFESSIONAL_PRODUCT_ID)},
> +	{USB_DEVICE(IXXAT_USB_VENDOR_ID, USB_TO_CAN_V2_AUTOMOTIVE_PRODUCT_ID)},
> +	{USB_DEVICE(IXXAT_USB_VENDOR_ID, USB_TO_LIN_V2_PRODUCT_ID)},
> +	{USB_DEVICE(IXXAT_USB_VENDOR_ID, USB_TO_KLINE_V2_PRODUCT_ID)},
> +#ifdef CANFD_CAPABLE
> +	{USB_DEVICE(IXXAT_USB_VENDOR_ID, USB_TO_CAN_FD_AUTOMOTIVE_PRODUCT_ID)},
> +	{USB_DEVICE(IXXAT_USB_VENDOR_ID, USB_TO_CAN_FD_COMPACT_PRODUCT_ID)},
> +	{USB_DEVICE(IXXAT_USB_VENDOR_ID, USB_TO_CAN_FD_PROFESSIONAL_PRODUCT_ID)},
> +	{USB_DEVICE(IXXAT_USB_VENDOR_ID, USB_TO_CAN_FD_PCIE_MINI_PRODUCT_ID)},
> +	{USB_DEVICE(IXXAT_USB_VENDOR_ID, USB_TO_CAR_ID)},
> +	{USB_DEVICE(IXXAT_USB_VENDOR_ID, DELL_EDGE_GW3002_PRODUCT_ID)},
> +#endif
> +	{} /* Terminating entry */
> +};
> +
> +MODULE_DEVICE_TABLE(usb, ixxat_usb_table);
> +
> +/* List of supported IXX-USB adapters (NULL terminated list) */
> +static struct ixx_usb_adapter *ixx_usb_adapters_list[] = {
> +	&usb_to_can_v2_compact,
> +	&usb_to_can_v2_automotive,
> +	&usb_to_can_v2_embedded,
> +	&usb_to_can_v2_professional,
> +	&usb_to_can_v2_low_speed,
> +	&usb_to_can_v2_extended,
> +#ifdef CANFD_CAPABLE
> +	&usb_to_can_fd_automotive,
> +	&usb_to_can_fd_compact,
> +	&usb_to_can_fd_professional,
> +	&usb_to_can_fd_pcie_mini,
> +	&usb_to_car,
> +	&dell_edge_gw3002,
> +#endif
> +	NULL,
> +};
> +
> +/*
> + * dump memory
> + */
> +#define DUMP_WIDTH    16
> +void ixxat_dump_mem(char *prompt, void *p, int l)
> +{
> +	pr_info("%s dumping %s (%d bytes):\n",
> +			IXXAT_USB_DRIVER_NAME, prompt ? prompt : "memory", l);
> +	print_hex_dump(KERN_INFO, IXXAT_USB_DRIVER_NAME " ", DUMP_PREFIX_NONE,
> +			DUMP_WIDTH, 1, p, l, false);
> +}
> +
> +static void ixxat_usb_add_us(struct timeval *tv, u64 delta_us)
> +{
> +	/* number of s. to add to final time */
> +	u32 delta_s = div_u64(delta_us, 1000000);
> +
> +	delta_us -= delta_s * 1000000;
> +
> +	tv->tv_usec += delta_us;
> +	if (tv->tv_usec >= 1000000) {
> +		tv->tv_usec -= 1000000;
> +		delta_s++;
> +	}
> +	tv->tv_sec += delta_s;
> +}
> +
> +void ixxat_usb_get_ts_tv(struct ixx_usb_device *dev, u32 ts, ktime_t *k_time)
> +{
> +	struct timeval tv = dev->time_ref.tv_host_0;
> +
> +	if (ts < dev->time_ref.ts_dev_last) {
> +		ixxat_usb_update_ts_now(dev, ts);
> +	}
> +
> +	dev->time_ref.ts_dev_last = ts;
> +	ixxat_usb_add_us(&tv, ts - dev->time_ref.ts_dev_0);
> +
> +	if(k_time)
> +		*k_time = timeval_to_ktime(tv);
> +}
> +
> +void ixxat_usb_update_ts_now(struct ixx_usb_device *dev, u32 hw_time_base)
> +{
> +	u64 timebase;
> +
> +	timebase = (u64)0x00000000FFFFFFFF - (u64)dev->time_ref.ts_dev_0 + (u64)hw_time_base;
> +
> +	ixxat_usb_add_us(&dev->time_ref.tv_host_0, timebase);
> +
> +	dev->time_ref.ts_dev_0 = hw_time_base;
> +}
> +
> +void ixxat_usb_set_ts_now(struct ixx_usb_device *dev, u32 hw_time_base)
> +{
> +	dev->time_ref.ts_dev_0 = hw_time_base;
> +	do_gettimeofday(&dev->time_ref.tv_host_0);
> +	dev->time_ref.ts_dev_last = hw_time_base;
> +}
> +
> +/*
> + * callback for bulk Rx urb
> + */
> +static void ixxat_usb_read_bulk_callback(struct urb *urb)
> +{
> +	struct ixx_usb_device *dev = urb->context;
> +	struct net_device *netdev;
> +	int err;
> +
> +	netdev = dev->netdev;
> +
> +	if (!netif_device_present(netdev))
> +		return;
> +
> +	/* check reception status */
> +	switch (urb->status) {
> +		case 0:
> +			/* success */
> +			break;
> +
> +		case -EILSEQ:
> +		case -ENOENT:
> +		case -ECONNRESET:
> +		case -ESHUTDOWN:
> +			return;
> +
> +		default:
> +			if (net_ratelimit())
> +				netdev_err(netdev, "Rx urb aborted (%d)\n",
> +						urb->status);
> +			goto resubmit_urb;
> +	}
> +
> +	/* protect from any incoming empty msgs */
> +	if ((urb->actual_length > 0) && (dev->adapter->dev_decode_buf)) {
> +		/* handle these kinds of msgs only if _start callback called */
> +		if (dev->state & IXXAT_USB_STATE_STARTED)
> +			err = dev->adapter->dev_decode_buf(dev, urb);
> +	}
> +
> +resubmit_urb: usb_fill_bulk_urb(urb, dev->udev,
> +			      usb_rcvbulkpipe(dev->udev, dev->ep_msg_in),
> +			      urb->transfer_buffer, dev->adapter->rx_buffer_size,
> +			      ixxat_usb_read_bulk_callback, dev);
> +
> +	      usb_anchor_urb(urb, &dev->rx_submitted);
> +	      err = usb_submit_urb(urb, GFP_ATOMIC);
> +	      if (!err)
> +		      return;
> +
> +	      usb_unanchor_urb(urb);
> +
> +	      if (err == -ENODEV)
> +		      netif_device_detach(netdev);
> +	      else
> +		      netdev_err(netdev, "failed resubmitting read bulk urb: %d\n",
> +				      err);
> +}
> +
> +/*
> + * callback for bulk Tx urb
> + */
> +static void ixxat_usb_write_bulk_callback(struct urb *urb)
> +{
> +	struct ixx_tx_urb_context *context = urb->context;
> +	struct ixx_usb_device *dev;
> +	struct net_device *netdev;
> +
> +	BUG_ON(!context);
> +
> +	dev = context->dev;
> +	netdev = dev->netdev;
> +
> +	atomic_dec(&dev->active_tx_urbs);
> +
> +	if (!netif_device_present(netdev))
> +		return;
> +
> +	/* check tx status */
> +	switch (urb->status) {
> +		case 0:
> +			/* transmission complete */
> +			netdev->stats.tx_packets += context->count;
> +			netdev->stats.tx_bytes += context->dlc;
> +
> +			/* prevent tx timeout */
> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
> +			netif_trans_update(netdev);
> +#else
> +			netdev->trans_start = jiffies;
> +#endif
> +			break;
> +
> +
> +		case -EPROTO:
> +		case -ENOENT:
> +		case -ECONNRESET:
> +		case -ESHUTDOWN:
> +			break;
> +		default:
> +			if (net_ratelimit())
> +				netdev_err(netdev, "Tx urb aborted (%d)\n",
> +						urb->status);
> +			break;
> +	}
> +
> +	/* should always release echo skb and corresponding context */
> +	can_get_echo_skb(netdev, context->echo_index);
> +	context->echo_index = IXXAT_USB_MAX_TX_URBS;
> +
> +	/* do wakeup tx queue in case of success only */
> +	if (!urb->status)
> +		netif_wake_queue(netdev);
> +}
> +
> +/*
> + * called by netdev to send one skb on the CAN interface.
> + */
> +static netdev_tx_t ixxat_usb_ndo_start_xmit(struct sk_buff *skb,
> +		struct net_device *netdev)
> +{
> +	struct ixx_usb_device *dev = netdev_priv(netdev);
> +	struct ixx_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, err;
> +	size_t size = dev->adapter->tx_buffer_size;
> +
> +	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 (!context) {
> +		/* should not occur except during restart */
> +		return NETDEV_TX_BUSY;
> +	}
> +
> +	urb = context->urb;
> +	obuf = urb->transfer_buffer;
> +
> +	err = dev->adapter->dev_encode_msg(dev, skb, obuf, &size);
> +
> +	context->echo_index = i;
> +	context->dlc = cf->len;
> +	context->count = 1;
> +
> +	urb->transfer_buffer_length = size;
> +
> +	if (err) {
> +		if (net_ratelimit())
> +			netdev_err(netdev, "packet dropped\n");
> +		dev_kfree_skb(skb);
> +		stats->tx_dropped++;
> +		return NETDEV_TX_OK;
> +	}
> +
> +	usb_anchor_urb(urb, &dev->tx_submitted);
> +
> +	can_put_echo_skb(skb, netdev, context->echo_index);
> +
> +	atomic_inc(&dev->active_tx_urbs);
> +
> +	err = usb_submit_urb(urb, GFP_ATOMIC);
> +	if (err) {
> +		can_free_echo_skb(netdev, context->echo_index);
> +
> +		usb_unanchor_urb(urb);
> +
> +		/* this context is not used in fact */
> +		context->echo_index = IXXAT_USB_MAX_TX_URBS;
> +
> +		atomic_dec(&dev->active_tx_urbs);
> +
> +		switch (err) {
> +			case -ENODEV:
> +				netif_device_detach(netdev);
> +				break;
> +			case -ENOENT:
> +				/* cable unplugged */
> +				stats->tx_dropped++;
> +				break;
> +			default:
> +				stats->tx_dropped++;
> +				netdev_warn(netdev, "tx urb submitting failed err=%d\n",
> +						err);
> +		}
> +	} else {
> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
> +		netif_trans_update(netdev);
> +#else
> +		netdev->trans_start = jiffies;
> +#endif
> +
> +		/* slow down tx path */
> +		if (atomic_read(&dev->active_tx_urbs) >= IXXAT_USB_MAX_TX_URBS)
> +			netif_stop_queue(netdev);
> +	}
> +
> +	return NETDEV_TX_OK;
> +}
> +
> +/*
> + * start the CAN interface.
> + * Rx and Tx urbs are allocated here. Rx urbs are submitted here.
> + */
> +static int ixxat_usb_start(struct ixx_usb_device *dev)
> +{
> +	struct net_device *netdev = dev->netdev;
> +	int err, i;
> +
> +	for (i = 0; i < IXXAT_USB_MAX_RX_URBS; i++) {
> +		struct urb *urb;
> +		u8 *buf;
> +
> +		/* create a URB, and a buffer for it, to receive usb messages */
> +		urb = usb_alloc_urb(0, GFP_KERNEL);
> +		if (!urb) {
> +			netdev_err(netdev, "No memory left for URBs\n");
> +			err = -ENOMEM;
> +			break;
> +		}
> +
> +		buf = kmalloc(dev->adapter->rx_buffer_size, GFP_KERNEL);
> +		if (!buf) {
> +			usb_free_urb(urb);
> +			err = -ENOMEM;
> +			break;
> +		}
> +
> +		usb_fill_bulk_urb(urb, dev->udev,
> +				usb_rcvbulkpipe(dev->udev, dev->ep_msg_in), buf,
> +				dev->adapter->rx_buffer_size,
> +				ixxat_usb_read_bulk_callback, dev);
> +
> +		/* ask last usb_free_urb() to also kfree() transfer_buffer */
> +		urb->transfer_flags |= URB_FREE_BUFFER;
> +		usb_anchor_urb(urb, &dev->rx_submitted);
> +
> +		err = usb_submit_urb(urb, GFP_KERNEL);
> +		if (err) {
> +			if (err == -ENODEV)
> +				netif_device_detach(dev->netdev);
> +
> +			usb_unanchor_urb(urb);
> +			kfree(buf);
> +			usb_free_urb(urb);
> +			break;
> +		}
> +
> +		/* drop reference, USB core will take care of freeing it */
> +		usb_free_urb(urb);
> +	}
> +
> +	/* did we submit any URBs? Warn if we was not able to submit all urbs */
> +	if (i < IXXAT_USB_MAX_RX_URBS) {
> +		if (i == 0) {
> +			netdev_err(netdev, "couldn't setup any rx URB\n");
> +			return err;
> +		}
> +
> +		netdev_warn(netdev, "rx performance may be slow\n");
> +	}
> +
> +	/* pre-alloc tx buffers and corresponding urbs */
> +	for (i = 0; i < IXXAT_USB_MAX_TX_URBS; i++) {
> +		struct ixx_tx_urb_context *context;
> +		struct urb *urb;
> +		u8 *buf;
> +
> +		/* create a URB and a buffer for it, to transmit usb messages */
> +		urb = usb_alloc_urb(0, GFP_KERNEL);
> +		if (!urb) {
> +			netdev_err(netdev, "No memory left for URBs\n");
> +			err = -ENOMEM;
> +			break;
> +		}
> +
> +		buf = kmalloc(dev->adapter->tx_buffer_size, GFP_KERNEL);
> +		if (!buf) {
> +			usb_free_urb(urb);
> +			err = -ENOMEM;
> +			break;
> +		}
> +
> +		context = dev->tx_contexts + i;
> +		context->dev = dev;
> +		context->urb = urb;
> +
> +		usb_fill_bulk_urb(urb, dev->udev,
> +				usb_sndbulkpipe(dev->udev, dev->ep_msg_out),
> +				buf, dev->adapter->tx_buffer_size,
> +				ixxat_usb_write_bulk_callback, context);
> +
> +		/* ask last usb_free_urb() to also kfree() transfer_buffer */
> +		urb->transfer_flags |= URB_FREE_BUFFER;
> +	}
> +
> +	/* warn if we were not able to allocate enough tx contexts */
> +	if (i < IXXAT_USB_MAX_TX_URBS) {
> +		if (i == 0) {
> +			netdev_err(netdev, "couldn't setup any tx URB\n");
> +			goto err_tx;
> +		}
> +
> +		netdev_warn(netdev, "tx performance may be slow\n");
> +	}
> +
> +	if (dev->adapter->dev_start) {
> +		err = dev->adapter->dev_start(dev);
> +		if (err)
> +			goto err_adapter;
> +	}
> +
> +	dev->state |= IXXAT_USB_STATE_STARTED;
> +
> +	dev->can.state = CAN_STATE_ERROR_ACTIVE;
> +
> +	return 0;
> +
> +err_adapter: if (err == -ENODEV)
> +		     netif_device_detach(dev->netdev);
> +
> +	     netdev_warn(netdev, "couldn't submit control: %d\n", err);
> +
> +	     for (i = 0; i < IXXAT_USB_MAX_TX_URBS; i++) {
> +		     usb_free_urb(dev->tx_contexts[i].urb);
> +		     dev->tx_contexts[i].urb = NULL;
> +	     }
> +err_tx: usb_kill_anchored_urbs(&dev->rx_submitted);
> +
> +	return err;
> +}
> +
> +/*
> + * called by netdev to open the corresponding CAN interface.
> + */
> +static int ixxat_usb_ndo_open(struct net_device *netdev)
> +{
> +	struct ixx_usb_device *dev = netdev_priv(netdev);
> +	int err;
> +
> +	/* common open */
> +	err = open_candev(netdev);
> +	if (err)
> +		return err;
> +
> +	/* finally start device */
> +	err = ixxat_usb_start(dev);
> +	if (err) {
> +		netdev_err(netdev, "couldn't start device: %d\n", err);
> +		close_candev(netdev);
> +		return err;
> +	}
> +
> +	netif_start_queue(netdev);
> +
> +	return 0;
> +}
> +
> +/*
> + * unlink in-flight Rx and Tx urbs and free their memory.
> + */
> +static void ixxat_usb_unlink_all_urbs(struct ixx_usb_device *dev)
> +{
> +	int i;
> +
> +	/* free all Rx (submitted) urbs */
> +	usb_kill_anchored_urbs(&dev->rx_submitted);
> +
> +	/* free unsubmitted Tx urbs first */
> +	for (i = 0; i < IXXAT_USB_MAX_TX_URBS; i++) {
> +		struct urb *urb = dev->tx_contexts[i].urb;
> +
> +		if (!urb
> +				|| dev->tx_contexts[i].echo_index
> +				!= IXXAT_USB_MAX_TX_URBS) {
> +			/*
> +			 * this urb is already released or always submitted,
> +			 * let usb core free by itself
> +			 */
> +			continue;
> +		}
> +
> +		usb_free_urb(urb);
> +		dev->tx_contexts[i].urb = NULL;
> +	}
> +
> +	/* then free all submitted Tx urbs */
> +	usb_kill_anchored_urbs(&dev->tx_submitted);
> +	atomic_set(&dev->active_tx_urbs, 0);
> +}
> +
> +/*
> + * called by netdev to close the corresponding CAN interface.
> + */
> +static int ixxat_usb_ndo_stop(struct net_device *netdev)
> +{
> +	struct ixx_usb_device *dev = netdev_priv(netdev);
> +
> +	dev->state &= ~IXXAT_USB_STATE_STARTED;
> +	netif_stop_queue(netdev);
> +
> +	/* unlink all pending urbs and free used memory */
> +	ixxat_usb_unlink_all_urbs(dev);
> +
> +	if (dev->adapter->dev_stop)
> +		dev->adapter->dev_stop(dev);
> +
> +	close_candev(netdev);
> +
> +	dev->can.state = CAN_STATE_STOPPED;
> +
> +	return 0;
> +}
> +
> +/*
> + * handle end of waiting for the device to reset
> + */
> +void ixxat_usb_restart_complete(struct ixx_usb_device *dev)
> +{
> +	/* finally MUST update can state */
> +	dev->can.state = CAN_STATE_ERROR_ACTIVE;
> +
> +	/* netdev queue can be awaken now */
> +	netif_wake_queue(dev->netdev);
> +}
> +
> +void ixxat_usb_async_complete(struct urb *urb)
> +{
> +	kfree(urb->transfer_buffer);
> +	usb_free_urb(urb);
> +}
> +
> +/*
> + * candev callback used to change CAN mode.
> + * Warning: this is called from a timer context!
> + */
> +static int ixxat_usb_set_mode(struct net_device *netdev, enum can_mode mode)
> +{
> +	struct ixx_usb_device *dev = netdev_priv(netdev);
> +	int err = 0;
> +
> +	switch (mode) {
> +		case CAN_MODE_START:
> +			dev->restart_flag = 1;
> +			wake_up_interruptible(&dev->wait_queue);
> +			break;
> +		default:
> +			return -EOPNOTSUPP;
> +	}
> +
> +	return err;
> +}
> +
> +/*
> + * candev callback used to set device bitrate.
> + */
> +static int ixxat_usb_set_bittiming(struct net_device *netdev)
> +{
> +	struct ixx_usb_device* dev = (struct ixx_usb_device*) netdev_priv(
> +			netdev);
> +	struct can_bittiming *bt = &dev->can.bittiming;
> +
> +	if (dev->adapter->dev_set_bittiming) {
> +		int err = dev->adapter->dev_set_bittiming(dev, bt);
> +
> +		if (err)
> +			netdev_info(netdev, "couldn't set bitrate (err %d)\n",
> +					err);
> +		return err;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * candev callback used to set error counters.
> + */
> +static int ixxat_usb_get_berr_counter(const struct net_device *netdev,
> +		struct can_berr_counter *bec)
> +{
> +	struct ixx_usb_device* dev = (struct ixx_usb_device*) netdev_priv(
> +			netdev);
> +
> +	*bec = dev->bec;
> +
> +	return 0;
> +}
> +
> +static const struct net_device_ops ixx_usb_netdev_ops = { .ndo_open =
> +	ixxat_usb_ndo_open, .ndo_stop = ixxat_usb_ndo_stop,
> +	.ndo_start_xmit = ixxat_usb_ndo_start_xmit,
> +#ifdef CANFD_CAPABLE
> +	.ndo_change_mtu = can_change_mtu,
> +#endif
> +};
> +
> +/*
> + * create one device which is attached to CAN controller #ctrl_idx of the
> + * usb adapter.
> + */
> +static int ixxat_usb_create_dev(struct ixx_usb_adapter *ixx_usb_adapter,
> +		struct usb_interface *intf, int ctrl_idx)
> +{
> +	struct usb_device *usb_dev = interface_to_usbdev(intf);
> +	int sizeof_candev = ixx_usb_adapter->sizeof_dev_private;
> +	struct ixx_usb_device *dev;
> +	struct net_device *netdev;
> +	int i, err = 0, ep_off = 0;
> +	u16 tmp16;
> +
> +	if (sizeof_candev < sizeof(struct ixx_usb_device))
> +		sizeof_candev = sizeof(struct ixx_usb_device);
> +
> +	netdev = alloc_candev(sizeof_candev, IXXAT_USB_MAX_TX_URBS);
> +	if (!netdev) {
> +		dev_err(&intf->dev, "%s: couldn't alloc candev\n",
> +				IXXAT_USB_DRIVER_NAME);
> +		return -ENOMEM;
> +	}
> +
> +	dev = netdev_priv(netdev);
> +
> +	dev->transmit_ptr = 0;
> +	dev->transmit_dlc = 0;
> +	dev->transmit_count = 0;
> +
> +	dev->restart_flag = 0;
> +	dev->restart_task = 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 = ixx_usb_adapter;
> +	dev->ctrl_idx = ctrl_idx;
> +	dev->state = IXXAT_USB_STATE_CONNECTED;
> +
> +	ep_off = ixx_usb_adapter->has_bgi_ep ? 1 : 0;
> +
> +	/* Add +1 because of the bgi endpoint */
> +	dev->ep_msg_in = ixx_usb_adapter->ep_msg_in[ctrl_idx+ep_off];
> +	dev->ep_msg_out = ixx_usb_adapter->ep_msg_out[ctrl_idx+ep_off];
> +
> +	dev->can.clock = ixx_usb_adapter->clock;
> +	dev->can.bittiming_const = &ixx_usb_adapter->bittiming_const;
> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 3)
> +	dev->can.data_bittiming_const = &ixx_usb_adapter->data_bittiming_const;
> +#endif
> +
> +	dev->can.do_set_bittiming = ixxat_usb_set_bittiming;
> +	dev->can.do_set_mode = ixxat_usb_set_mode;
> +	dev->can.do_get_berr_counter = ixxat_usb_get_berr_counter;
> +
> +	dev->can.ctrlmode_supported = ixx_usb_adapter->ctrlmode_supported;
> +
> +	netdev->netdev_ops = &ixx_usb_netdev_ops;
> +
> +	netdev->flags |= IFF_ECHO; /* we support local echo */
> +
> +	init_usb_anchor(&dev->rx_submitted);
> +
> +	init_usb_anchor(&dev->tx_submitted);
> +	atomic_set(&dev->active_tx_urbs, 0);
> +
> +	for (i = 0; i < IXXAT_USB_MAX_TX_URBS; i++)
> +		dev->tx_contexts[i].echo_index = IXXAT_USB_MAX_TX_URBS;
> +
> +	dev->prev_siblings = usb_get_intfdata(intf);
> +	usb_set_intfdata(intf, dev);
> +
> +	SET_NETDEV_DEV(netdev, &intf->dev);
> +
> +	err = register_candev(netdev);
> +	if (err) {
> +		dev_err(&intf->dev, "couldn't register CAN device: %d\n", err);
> +		goto lbl_set_intf_data;
> +	}
> +
> +	if (dev->prev_siblings)
> +		(dev->prev_siblings)->next_siblings = dev;
> +
> +	/* keep hw revision into the netdevice */
> +	tmp16 = le16_to_cpu(usb_dev->descriptor.bcdDevice);
> +	dev->device_rev = tmp16 >> 8;
> +
> +	if (dev->adapter->dev_init) {
> +		err = dev->adapter->dev_init(dev);
> +		if (err)
> +			goto lbl_set_intf_data;
> +	}
> +
> +	if (dev->adapter->intf_get_info)
> +		dev->adapter->intf_get_info(dev,
> +				&dev->dev_info);
> +
> +	netdev_info(netdev, "attached to %s channel %u (device %s)\n",
> +			dev->dev_info.device_name, ctrl_idx,
> +			dev->dev_info.device_id);
> +
> +	return 0;
> +
> +lbl_set_intf_data: usb_set_intfdata(intf, dev->prev_siblings);
> +		   free_candev(netdev);
> +
> +		   return err;
> +}
> +
> +/*
> + * called by the usb core when the device is unplugged from the system
> + */
> +static void ixxat_usb_disconnect(struct usb_interface *intf)
> +{
> +	struct ixx_usb_device *dev;
> +	struct ixx_usb_device *dev_prev_siblings;
> +
> +	/* unregister as many netdev devices as siblings */
> +	for (dev = usb_get_intfdata(intf); dev; dev = dev_prev_siblings) {
> +		struct net_device *netdev = dev->netdev;
> +		char name[IFNAMSIZ];
> +
> +		dev_prev_siblings = dev->prev_siblings;
> +		dev->state &= ~IXXAT_USB_STATE_CONNECTED;
> +		strncpy(name, netdev->name, IFNAMSIZ);
> +
> +		unregister_netdev(netdev);
> +
> +		dev->next_siblings = NULL;
> +		if (dev->adapter->dev_free)
> +			dev->adapter->dev_free(dev);
> +
> +		free_candev(netdev);
> +		dev_dbg(&intf->dev, "%s removed\n", name);
> +	}
> +
> +	usb_set_intfdata(intf, NULL);
> +}
> +
> +/*
> + * probe function for new ixxat-usb devices
> + */
> +static int ixxat_usb_probe(struct usb_interface *intf,
> +		const struct usb_device_id *id)
> +{
> +	struct usb_device *usb_dev = interface_to_usbdev(intf);
> +	struct ixx_usb_adapter *ixx_usb_adapter, **pp;
> +	int i, err = -ENOMEM;
> +	struct ixx_dev_caps dev_caps;
> +
> +	usb_dev = interface_to_usbdev(intf);
> +
> +	usb_reset_configuration(usb_dev);
> +
> +	/* get corresponding IXX-USB adapter */
> +	for (pp = ixx_usb_adapters_list; *pp; pp++)
> +		if ((*pp)->device_id == le16_to_cpu(usb_dev->descriptor.idProduct))
> +			break;
> +
> +	ixx_usb_adapter = *pp;
> +	if (!ixx_usb_adapter) {
> +		/* should never come except device_id bad usage in this file */
> +		pr_err("%s: didn't find device id. 0x%x in devices list\n",
> +				IXXAT_USB_DRIVER_NAME, le16_to_cpu(usb_dev->descriptor.idProduct));
> +		return -ENODEV;
> +	}
> +
> +	/* got corresponding adapter: check if it handles current interface */
> +	if (ixx_usb_adapter->intf_probe) {
> +		err = ixx_usb_adapter->intf_probe(intf);
> +		if (err)
> +			return err;
> +	}
> +
> +	if (ixx_usb_adapter->dev_power) {
> +		err = ixx_usb_adapter->dev_power(usb_dev, IXXAT_USB_POWER_WAKEUP);
> +		if (err)
> +			return err;
> +
> +		/* Give usb device some time to start its can controllers */
> +		msleep(500);
> +	}
> +
> +	/* got corresponding adapter: check the available controllers */
> +	if (ixx_usb_adapter->dev_get_dev_caps) {
> +		err = ixx_usb_adapter->dev_get_dev_caps(usb_dev, &dev_caps);
> +		if (err)
> +			return err;
> +
> +		for (i = 0; i < dev_caps.bus_ctrl_count; i++) {
> +			if ( IXXAT_USB_BUS_CAN
> +					== IXXAT_USB_BUS_TYPE(dev_caps.bus_ctrl_types[i]))
> +				ixx_usb_adapter->ctrl_count++;
> +		}
> +
> +		for (i = 0; i < dev_caps.bus_ctrl_count; i++) {
> +			if ( IXXAT_USB_BUS_CAN == IXXAT_USB_BUS_TYPE(dev_caps.bus_ctrl_types[i]))
> +				err = ixxat_usb_create_dev(ixx_usb_adapter, intf, i);
> +			if (err) {
> +				/* deregister already created devices */
> +				ixxat_usb_disconnect(intf);
> +				break;
> +			}
> +		}
> +	}
> +
> +	return err;
> +}
> +
> +/* usb specific object needed to register this driver with the usb subsystem */
> +static struct usb_driver ixx_usb_driver = {
> +	.name = IXXAT_USB_DRIVER_NAME,
> +	.disconnect = ixxat_usb_disconnect,
> +	.probe = ixxat_usb_probe,
> +	.id_table = ixxat_usb_table,
> +};
> +
> +static int __init ixx_usb_init(void)
> +{
> +	int err;
> +
> +	/* register this driver with the USB subsystem */
> +	err = usb_register(&ixx_usb_driver);
> +	if (err)
> +		pr_err("%s: usb_register failed (err %d)\n",
> +				IXXAT_USB_DRIVER_NAME, err);
> +
> +	return err;
> +}
> +
> +static int ixxat_usb_do_device_exit(struct device *d, void *arg)
> +{
> +	struct usb_interface
> +		*intf = (struct usb_interface*)to_usb_interface(d);
> +	struct ixx_usb_device *dev;
> +
> +	/* stop as many netdev devices as siblings */
> +	for (dev = usb_get_intfdata(intf); dev; dev = dev->prev_siblings) {
> +		struct net_device *netdev = dev->netdev;
> +
> +		if (netif_device_present(netdev))
> +			if (dev->adapter->dev_exit)
> +				dev->adapter->dev_exit(dev);
> +	}
> +
> +	return 0;
> +}
> +
> +static void __exit ixx_usb_exit(void)
> +{
> +	int err;
> +
> +	/* last chance do send any synchronous commands here */
> +	err = driver_for_each_device(&ixx_usb_driver.drvwrap.driver, NULL,
> +			NULL, ixxat_usb_do_device_exit);
> +	if (err)
> +		pr_err("%s: failed to stop all can devices (err %d)\n",
> +				IXXAT_USB_DRIVER_NAME, err);
> +
> +	/* deregister this driver with the USB subsystem */
> +	usb_deregister(&ixx_usb_driver);
> +
> +	pr_info("%s: IXX-USB interfaces driver unloaded\n",
> +			IXXAT_USB_DRIVER_NAME);
> +}
> +
> +module_init(ixx_usb_init);
> +module_exit(ixx_usb_exit);
> diff --git a/ubuntu/ixxat/ixx_usb_core.h b/ubuntu/ixxat/ixx_usb_core.h
> new file mode 100644
> index 000000000000..79c11d8f1ea8
> --- /dev/null
> +++ b/ubuntu/ixxat/ixx_usb_core.h
> @@ -0,0 +1,289 @@
> +/*
> + * CAN driver for IXXAT USB-to-CAN V2 adapters
> + *
> + * Copyright (C) 2003-2014 Michael Hengler IXXAT Automation GmbH
> + *
> + * Based on code originally by pcan_usb_core
> + *
> + * 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 IXX_USB_CORE_H
> +#define IXX_USB_CORE_H
> +
> +#include <linux/version.h>
> +
> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 3)
> +#define CANFD_CAPABLE 1
> +#endif
> +
> +extern struct ixx_usb_adapter usb_to_can_v2_compact;
> +extern struct ixx_usb_adapter usb_to_can_v2_automotive;
> +extern struct ixx_usb_adapter usb_to_can_v2_embedded;
> +extern struct ixx_usb_adapter usb_to_can_v2_professional;
> +extern struct ixx_usb_adapter usb_to_can_v2_low_speed;
> +extern struct ixx_usb_adapter usb_to_can_v2_extended;
> +
> +#ifdef CANFD_CAPABLE
> +extern struct ixx_usb_adapter usb_to_can_fd_automotive;
> +extern struct ixx_usb_adapter usb_to_can_fd_compact;
> +extern struct ixx_usb_adapter usb_to_can_fd_professional;
> +extern struct ixx_usb_adapter usb_to_can_fd_pcie_mini;
> +extern struct ixx_usb_adapter usb_to_car;
> +extern struct ixx_usb_adapter dell_edge_gw3002;
> +#endif
> +
> +#ifndef CAN_MAX_DLEN
> +#define CAN_MAX_DLEN 8
> +#endif
> +
> +#ifndef CANFD_MAX_DLEN
> +#define CANFD_MAX_DLEN 64
> +#endif
> +
> +
> +/* supported device ids. */
> +#define USB_TO_CAN_V2_COMPACT_PRODUCT_ID       0x0008
> +#define USB_TO_CAN_V2_EMBEDDED_PRODUCT_ID      0x0009
> +#define USB_TO_CAN_V2_PROFESSIONAL_PRODUCT_ID  0x000A
> +#define USB_TO_CAN_V2_AUTOMOTIVE_PRODUCT_ID    0x000B
> +#define USB_TO_LIN_V2_PRODUCT_ID               0x000C
> +#define USB_TO_KLINE_V2_PRODUCT_ID             0x000D
> +#define USB_TO_CAN_V2_LOW_SPEED_PRODUCT_ID     0xFFFF
> +#define USB_TO_CAN_V2_EXTENDED_PRODUCT_ID      0x000E
> +
> +#define USB_TO_CAN_FD_COMPACT_PRODUCT_ID       0x0014
> +#define USB_TO_CAN_FD_PROFESSIONAL_PRODUCT_ID  0x0016
> +#define USB_TO_CAN_FD_AUTOMOTIVE_PRODUCT_ID    0x0017
> +#define USB_TO_CAN_FD_PCIE_MINI_PRODUCT_ID     0x001B
> +#define USB_TO_CAR_ID                          0x001C
> +#define DELL_EDGE_GW3002_PRODUCT_ID            0xFF11
> +
> +#define IXXAT_USB_MAX_CHANNEL  5
> +
> +/* number of urbs that are submitted for rx/tx per channel */
> +#define IXXAT_USB_MAX_RX_URBS             4
> +#define IXXAT_USB_MAX_TX_URBS             10
> +
> +#define IXX_BTMODE_NAT 0x01
> +
> +#define IXXAT_USB_POWER_WAKEUP    0
> +#define IXXAT_USB_POWER_SLEEP     1
> +
> +struct ixx_usb_device;
> +
> +struct ixx_dev_caps
> +{
> +	u16 bus_ctrl_count;
> +	u16 bus_ctrl_types[32];
> +} __packed;
> +
> +struct ixx_ctrl_caps
> +{
> +	u16 ctrl_type;
> +	u16 bus_coupling;
> +	u32 features;
> +	u32 clock_freq;
> +	u32 tsc_divisor;
> +	u32 cms_divisor;
> +	u32 cms_max_ticks;
> +	u32 dtx_divisor;
> +	u32 dtx_max_ticks;
> +} __packed;
> +
> +struct canbtp
> +{
> +	u32 mode;   // timing mode (see CAN_BTMODE_ const)
> +	u32 bps;    // bits per second or prescaler (see CAN_BTMODE_)
> +	u16 ts1;    // length of time segment 1 in quantas
> +	u16 ts2;    // length of time segment 2 in quantas
> +	u16 sjw;    // re-synchronisation jump width in quantas
> +	u16 tdo;    // transceiver delay compensation offset in quantas
> +	// (0 = disabled)
> +} __packed;
> +
> +struct ixx_ctrl_caps_v2
> +{
> +	u16 ctrl_type;
> +	u16 bus_coupling;
> +	u32 features;
> +
> +	u32 clock_freq;
> +	struct canbtp sdr_range_min;
> +	struct canbtp sdr_range_max;
> +	struct canbtp fdr_range_min;
> +	struct canbtp fdr_range_max;
> +
> +	u32 tsc_freq;
> +	u32 tsc_divisor;
> +
> +	u32 cms_freq;
> +	u32 cms_divisor;
> +	u32 cms_max_ticks;
> +
> +	u32 dtx_freq;
> +	u32 dtx_divisor;
> +	u32 dtx_max_ticks;
> +} __packed;
> +
> +struct ixx_intf_info
> +{
> +	char   device_name[16];       // device name
> +	char   device_id[16];         // device identification ( unique device id)
> +	u16    device_version;        // device version ( 0, 1, ...)
> +	u32    device_fpga_version;   // device version of FPGA design
> +} __packed;
> +
> +struct ixx_intf_fw_info
> +{
> +	u32 firmware_type;  // type of currently running firmware
> +	u16 reserved;       // reserved
> +	u16 major_version;  // major firmware version number
> +	u16 minor_version;  // minor firmware version number
> +	u16 build_version;  // build firmware version number
> +} __packed;
> +
> +struct ixx_usb_adapter {
> +	char *name;
> +	u32 device_id;
> +	struct can_clock clock;
> +	const struct can_bittiming_const bittiming_const;
> +	const struct can_bittiming_const data_bittiming_const;
> +
> +	unsigned int ctrl_count;
> +
> +	u32 ctrlmode_supported;
> +
> +	int (*intf_probe)(struct usb_interface *intf);
> +
> +	int (*dev_get_dev_caps)(struct usb_device *usb_dev, struct ixx_dev_caps* dev_caps);
> +	int (*dev_get_ctrl_caps)(struct usb_device *usb_dev, struct ixx_ctrl_caps* ctrl_caps, int index);
> +
> +	int (*intf_get_info)(struct ixx_usb_device *dev, struct ixx_intf_info* intf_info);
> +	int (*intf_get_fw_info)(struct ixx_usb_device *dev, struct ixx_intf_fw_info* fw_info);
> +
> +	int (*dev_init)(struct ixx_usb_device *dev);
> +	void (*dev_exit)(struct ixx_usb_device *dev);
> +	void (*dev_free)(struct ixx_usb_device *dev);
> +	int (*dev_open)(struct ixx_usb_device *dev);
> +	int (*dev_close)(struct ixx_usb_device *dev);
> +	int (*dev_set_bittiming)(struct ixx_usb_device *dev, struct can_bittiming *bt);
> +	int (*dev_set_bus)(struct ixx_usb_device *dev, u8 onoff);
> +	int (*dev_decode_buf)(struct ixx_usb_device *dev, struct urb *urb);
> +	int (*dev_encode_msg)(struct ixx_usb_device *dev, struct sk_buff *skb,
> +			u8 *obuf, size_t *size);
> +	int (*dev_start)(struct ixx_usb_device *dev);
> +	int (*dev_stop)(struct ixx_usb_device *dev);
> +	int (*dev_restart_async)(struct ixx_usb_device *dev, struct urb *urb,
> +			u8 *buf);
> +	int (*dev_power)(struct usb_device *usb_dev, u8 mode);
> +	u8 ep_msg_in[IXXAT_USB_MAX_CHANNEL];
> +	u8 ep_msg_out[IXXAT_USB_MAX_CHANNEL];
> +
> +	int rx_buffer_size;
> +	int tx_buffer_size;
> +	int sizeof_dev_private;
> +
> +	int has_bgi_ep;
> +
> +};
> +
> +struct ixx_time_ref {
> +	struct timeval tv_host_0;
> +	u32 ts_dev_0;
> +	u32 ts_dev_last;
> +};
> +
> +struct ixx_tx_urb_context {
> +	struct ixx_usb_device *dev;
> +	u32 echo_index;
> +	u8 dlc;
> +	u8 count;
> +	struct urb *urb;
> +};
> +
> +/*IXXAT USB device */
> +struct ixx_usb_device {
> +	struct can_priv can;
> +	struct ixx_usb_adapter *adapter;
> +	unsigned int ctrl_idx;
> +	u32 state;
> +
> +	struct sk_buff *echo_skb[IXXAT_USB_MAX_TX_URBS];
> +
> +	struct usb_device *udev;
> +	struct net_device *netdev;
> +
> +	atomic_t active_tx_urbs;
> +	struct usb_anchor tx_submitted;
> +	struct ixx_tx_urb_context tx_contexts[IXXAT_USB_MAX_TX_URBS];
> +
> +	struct usb_anchor rx_submitted;
> +
> +	u32 device_number;
> +	u8 device_rev;
> +
> +	u8 ep_msg_in;
> +	u8 ep_msg_out;
> +
> +	u8 transmit_buffer[256];
> +	u8 transmit_ptr;
> +	u8 transmit_count;
> +	u8 transmit_dlc;
> +
> +	struct task_struct *restart_task;
> +	u8 restart_flag;
> +	u8 must_quit;
> +	wait_queue_head_t wait_queue;
> +
> +	struct ixx_usb_device *prev_siblings;
> +	struct ixx_usb_device *next_siblings;
> +
> +	u8 btr0;
> +	u8 btr1;
> +
> +	int ctrl_opened_count;
> +
> +	struct ixx_time_ref time_ref;
> +
> +	struct ixx_intf_info dev_info;
> +	struct ixx_intf_fw_info fw_info;
> +
> +	struct can_berr_counter bec;
> +};
> +
> +struct ixx_can_msg
> +{
> +	u8  size;
> +	u32 time;
> +	u32 msg_id;
> +	u32 flags;
> +	u8  data[CAN_MAX_DLEN];
> +} __packed;
> +
> +struct ixx_can_msg_v2
> +{
> +	u8  size;
> +	u32 time;
> +	u32 msg_id;
> +	u32 flags;
> +	u32 client_id;
> +	u8  data[CANFD_MAX_DLEN];
> +} __packed;
> +
> +void ixxat_dump_mem(char *prompt, void *p, int l);
> +
> +void ixxat_usb_update_ts_now(struct ixx_usb_device *dev, u32 ts_now);
> +void ixxat_usb_set_ts_now(struct ixx_usb_device *dev, u32 ts_now);
> +void ixxat_usb_get_ts_tv(struct ixx_usb_device *dev, u32 ts,
> +		ktime_t* k_time);
> +
> +void ixxat_usb_async_complete(struct urb *urb);
> +void ixxat_usb_restart_complete(struct ixx_usb_device *dev);
> +#endif
> diff --git a/ubuntu/ixxat/ixx_usb_fd.c b/ubuntu/ixxat/ixx_usb_fd.c
> new file mode 100644
> index 000000000000..63d5b9944a85
> --- /dev/null
> +++ b/ubuntu/ixxat/ixx_usb_fd.c
> @@ -0,0 +1,1673 @@
> +/*
> + * CAN driver for IXXAT USB-to-CAN FD
> + *
> + * Copyright (C) 2017 Michael Hengler <mhengler@ixxat.de>
> + *
> + * Based on code originally by pcan_usb_core
> + *
> + * 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/delay.h>
> +#include <linux/can.h>
> +#include <linux/can/dev.h>
> +#include <linux/can/error.h>
> +#include <linux/kthread.h>
> +#include <linux/sched.h>
> +#include <linux/wait.h>
> +#include <linux/types.h>
> +#include <linux/gfp.h>
> +#include <asm-generic/errno.h>
> +#include <stdarg.h>
> +
> +#include "ixx_usb_core.h"
> +
> +#ifdef CANFD_CAPABLE
> +
> +MODULE_SUPPORTED_DEVICE("IXXAT Automation GmbH USB-to-CAN FD");
> +
> +/* use ifi can fd clock due to internal bittiming calculations */
> +#define IFIFD_CRYSTAL_HZ	      80000000
> +
> +/* usb-to-can fd Endpoints */
> +#define IXXAT_USBFD_EP_CMDOUT   0
> +#define IXXAT_USBFD_EP_CMDIN    (IXXAT_USBFD_EP_CMDOUT | USB_DIR_IN)
> +#define IXXAT_USBFD_EP_MSGOUT_0 1
> +#define IXXAT_USBFD_EP_MSGIN_0  (IXXAT_USBFD_EP_MSGOUT_0 | USB_DIR_IN)
> +#define IXXAT_USBFD_EP_MSGOUT_1 2
> +#define IXXAT_USBFD_EP_MSGIN_1  (IXXAT_USBFD_EP_MSGOUT_1 | USB_DIR_IN)
> +#define IXXAT_USBFD_EP_MSGOUT_2 3
> +#define IXXAT_USBFD_EP_MSGIN_2  (IXXAT_USBFD_EP_MSGOUT_2 | USB_DIR_IN)
> +#define IXXAT_USBFD_EP_MSGOUT_3 4
> +#define IXXAT_USBFD_EP_MSGIN_3  (IXXAT_USBFD_EP_MSGOUT_3 | USB_DIR_IN)
> +#define IXXAT_USBFD_EP_MSGOUT_4 5
> +#define IXXAT_USBFD_EP_MSGIN_4  (IXXAT_USBFD_EP_MSGOUT_4 | USB_DIR_IN)
> +
> +/* DELL Edge GW3002 Endpoints */
> +#define DELL_EDGE_GW3002_EP_MSGOUT_0 1
> +#define DELL_EDGE_GW3002_EP_MSGIN_0  (2 | USB_DIR_IN)
> +#define DELL_EDGE_GW3002_EP_MSGOUT_1 3
> +#define DELL_EDGE_GW3002_EP_MSGIN_1  (4 | USB_DIR_IN)
> +#define DELL_EDGE_GW3002_EP_MSGOUT_2 5
> +#define DELL_EDGE_GW3002_EP_MSGIN_2  (6 | USB_DIR_IN)
> +#define DELL_EDGE_GW3002_EP_MSGOUT_3 7
> +#define DELL_EDGE_GW3002_EP_MSGIN_3  (8 | USB_DIR_IN)
> +#define DELL_EDGE_GW3002_EP_MSGOUT_4 9
> +#define DELL_EDGE_GW3002_EP_MSGIN_4  (10 | USB_DIR_IN)
> +
> +/* usb-to-can fd rx/tx buffers size */
> +#define IXXAT_USBFD_RX_BUFFER_SIZE          512
> +#define IXXAT_USBFD_TX_BUFFER_SIZE          512
> +
> +#define IXXAT_USBFD_CMD_BUFFER_SIZE         256
> +
> +/* reception of 11-bit id messages */
> +#define IXXAT_USBFD_OPMODE_STANDARD         0x01
> +/* reception of 29-bit id messages */
> +#define IXXAT_USBFD_OPMODE_EXTENDED         0x02
> +/* enable reception of error frames */
> +#define IXXAT_USBFD_OPMODE_ERRFRAME         0x04
> +/* listen only mode (TX passive) */
> +#define IXXAT_USBFD_OPMODE_LISTONLY         0x08
> +
> +/* no extended operation */
> +#define IXXAT_USBFD_EXMODE_DISABLED         0x00
> +/* extended data length */
> +#define IXXAT_USBFD_EXMODE_EXTDATA          0x01
> +/* fast data bit rate */
> +#define IXXAT_USBFD_EXMODE_FASTDATA         0x02
> +/* ISO conform CAN-FD frame */
> +#define IXXAT_USBFD_EXMODE_ISOFD            0x04
> +
> +/* Stuff error */
> +#define IXXAT_USBFD_CAN_ERROR_STUFF         1
> +/* Form error */
> +#define IXXAT_USBFD_CAN_ERROR_FORM          2
> +/* Acknowledgment error */
> +#define IXXAT_USBFD_CAN_ERROR_ACK           3
> +/* Bit error */
> +#define IXXAT_USBFD_CAN_ERROR_BIT           4
> +/* Fast data bit rate error */
> +#define IXXAT_USBFD_CAN_ERROR_FAST_DATA     5
> +/* CRC error */
> +#define IXXAT_USBFD_CAN_ERROR_CRC           6
> +/* Other (unspecified) error */
> +#define IXXAT_USBFD_CAN_ERROR_OTHER         7
> +
> +/* Data overrun occurred */
> +#define IXXAT_USBFD_CAN_STATUS_OVRRUN    0x02
> +/* Error warning limit exceeded */
> +#define IXXAT_USBFD_CAN_STATUS_ERRLIM    0x04
> +/* Bus off status */
> +#define IXXAT_USBFD_CAN_STATUS_BUSOFF    0x08
> +
> +#define IXXAT_USBFD_CAN_DATA             0x00
> +#define IXXAT_USBFD_CAN_INFO             0x01
> +#define IXXAT_USBFD_CAN_ERROR            0x02
> +#define IXXAT_USBFD_CAN_STATUS           0x03
> +#define IXXAT_USBFD_CAN_WAKEUP           0x04
> +#define IXXAT_USBFD_CAN_TIMEOVR          0x05
> +#define IXXAT_USBFD_CAN_TIMERST          0x06
> +
> +
> +#define IXXAT_USBFD_MSG_FLAGS_TYPE   0x000000FF
> +#define IXXAT_USBFD_MSG_FLAGS_SSM    0x00000100
> +#define IXXAT_USBFD_MSG_FLAGS_HPM    0x00000200
> +#define IXXAT_USBFD_MSG_FLAGS_EDL    0x00000400
> +#define IXXAT_USBFD_MSG_FLAGS_FDR    0x00000800
> +#define IXXAT_USBFD_MSG_FLAGS_ESI    0x00001000
> +#define IXXAT_USBFD_MSG_FLAGS_RES    0x0000E000
> +#define IXXAT_USBFD_MSG_FLAGS_DLC    0x000F0000
> +#define IXXAT_USBFD_MSG_FLAGS_OVR    0x00100000
> +#define IXXAT_USBFD_MSG_FLAGS_SRR    0x00200000
> +#define IXXAT_USBFD_MSG_FLAGS_RTR    0x00400000
> +#define IXXAT_USBFD_MSG_FLAGS_EXT    0x00800000
> +#define IXXAT_USBFD_MSG_FLAGS_AFC    0xFF000000
> +
> +#define IXXAT_USBFD_BAL_CMD_CLASS    3
> +#define IXXAT_USBFD_BRD_CMD_CLASS    4
> +
> +#define IXXAT_USBFD_BRD_CMD_CAT      0
> +#define IXXAT_USBFD_CAN_CMD_CAT      1
> +
> +#define IXXAT_USBFD_VCI_CMD_CODE(Class, Function) \
> +	((u32) (((Class) << 8) | (Function)))
> +
> +#define IXXAT_USBFD_BRD_CMD_CODE(Category, Function) \
> +	IXXAT_USBFD_VCI_CMD_CODE(IXXAT_USBFD_BRD_CMD_CLASS, \
> +		((Category) << 5) | (Function))
> +
> +#define IXXAT_USBFD_BAL_CMD_CODE(Category, Function) \
> +	IXXAT_USBFD_VCI_CMD_CODE(IXXAT_USBFD_BAL_CMD_CLASS, \
> +		((Category) << 5) | (Function))
> +
> +#define IXXAT_USBFD_CAN_GET_CAPS_CMD \
> +	IXXAT_USBFD_BAL_CMD_CODE(IXXAT_USBFD_CAN_CMD_CAT, 0)
> +#define IXXAT_USBFD_POWER_CMD \
> +	IXXAT_USBFD_BRD_CMD_CODE(IXXAT_USBFD_CAN_CMD_CAT, 1)
> +#define IXXAT_USBFD_CAN_INIT_CMD \
> +	IXXAT_USBFD_BAL_CMD_CODE(IXXAT_USBFD_CAN_CMD_CAT, 5)
> +#define IXXAT_USBFD_CAN_START_CMD \
> +	IXXAT_USBFD_BAL_CMD_CODE(IXXAT_USBFD_CAN_CMD_CAT, 6)
> +#define IXXAT_USBFD_CAN_STOP_CMD \
> +	IXXAT_USBFD_BAL_CMD_CODE(IXXAT_USBFD_CAN_CMD_CAT, 7)
> +#define IXXAT_USBFD_CAN_RESET_CMD \
> +	IXXAT_USBFD_BAL_CMD_CODE(IXXAT_USBFD_CAN_CMD_CAT, 8)
> +/* Additional commands for USB-to-CAN FD */
> +#define IXXAT_USBFD_INIT_V2_CMD \
> +	IXXAT_USBFD_BAL_CMD_CODE(IXXAT_USBFD_CAN_CMD_CAT, 23)
> +
> +#define IXXAT_USBFD_BRD_GET_FWINFO_CMD \
> +	IXXAT_USBFD_BRD_CMD_CODE(IXXAT_USBFD_BRD_CMD_CAT, 0)
> +#define IXXAT_USBFD_BRD_GET_DEVCAPS_CMD \
> +	IXXAT_USBFD_BRD_CMD_CODE(IXXAT_USBFD_BRD_CMD_CAT, 1)
> +#define IXXAT_USBFD_BRD_GET_DEVINFO_CMD \
> +	IXXAT_USBFD_BRD_CMD_CODE(IXXAT_USBFD_BRD_CMD_CAT, 2)
> +
> +struct ixx_usbfd_dal_req {
> +	u32 req_size;
> +	u16 req_port;
> +	u16 req_socket;
> +	u32 req_code;
> +} __packed;
> +
> +struct ixx_usbfd_dal_res {
> +	u32 res_size;
> +	u32 ret_size;
> +	u32 ret_code;
> +} __packed;
> +
> +// Additional structures for the for USB-to-CAN FD
> +
> +struct ixx_usbfd_dev_power_req {
> +	struct ixx_usbfd_dal_req dal_req;
> +	u8  mode;
> +	u8  _padding1;
> +	u16 _padding2;
> +} __packed;
> +
> +struct ixx_usbfd_dev_power_res {
> +	struct ixx_usbfd_dal_res dal_res;
> +} __packed;
> +
> +struct ixx_usbfd_ctrl_init_v2_req {
> +	struct ixx_usbfd_dal_req dal_req;
> +	u8                opmode;
> +	u8                exmode;
> +	struct canbtp	    sdr;
> +	struct canbtp	    fdr;
> +	u16		      _padding;
> +} __packed;
> +
> +struct ixx_usbfd_ctrl_init_v2_res {
> +	struct ixx_usbfd_dal_res dal_res;
> +} __packed;
> +
> +struct ixx_usbfd_dev_caps_req {
> +	struct ixx_usbfd_dal_req dal_req;
> +} __packed;
> +
> +struct ixx_usbfd_dev_caps_res {
> +	struct ixx_usbfd_dal_res dal_res;
> +	struct ixx_dev_caps dev_caps;
> +} __packed;
> +
> +struct ixx_usbfd_ctrl_caps_req {
> +	struct ixx_usbfd_dal_req dal_req;
> +} __packed;
> +
> +struct ixx_usbfd_ctrl_caps_res {
> +	struct ixx_usbfd_dal_res dal_res;
> +	struct ixx_ctrl_caps ctrl_caps;
> +} __packed;
> +
> +struct ixx_usbfd_ctrl_init_req {
> +	struct ixx_usbfd_dal_req dal_req;
> +	u8 mode;
> +	u8 btr0;
> +	u8 btr1;
> +	u8 padding;
> +} __packed;
> +
> +struct ixx_usbfd_ctrl_init_res {
> +	struct ixx_usbfd_dal_res dal_res;
> +} __packed;
> +
> +struct ixx_usbfd_ctrl_start_req {
> +	struct ixx_usbfd_dal_req dal_req;
> +} __packed;
> +
> +struct ixx_usbfd_ctrl_start_res {
> +	struct ixx_usbfd_dal_res dal_res;
> +	u32 start_time;
> +} __packed;
> +
> +struct ixx_usbfd_ctrl_stop_req {
> +	struct ixx_usbfd_dal_req dal_req;
> +	u32 action;
> +} __packed;
> +
> +struct ixx_usbfd_ctrl_stop_res {
> +	struct ixx_usbfd_dal_res dal_res;
> +} __packed;
> +
> +struct ixx_usbfd_brd_get_fwinfo_req {
> +	struct ixx_usbfd_dal_req dal_req;
> +} __packed;
> +
> +struct ixx_usbfd_brd_get_fwinfo_res {
> +	struct ixx_usbfd_dal_res dal_res;
> +	struct ixx_intf_fw_info fwinfo;
> +} __packed;
> +
> +struct ixx_usbfd_brd_get_intf_info_req {
> +	struct ixx_usbfd_dal_req dal_req;
> +} __packed;
> +
> +struct ixx_usbfd_brd_get_intf_info_res {
> +	struct ixx_usbfd_dal_res dal_res;
> +	struct ixx_intf_info info;
> +} __packed;
> +
> +/*
> + * send usb-to-can fd command synchronously
> + */
> +static int ixx_usbfd_send_cmd(struct usb_device *dev,
> +		struct ixx_usbfd_dal_req *dal_req)
> +{
> +	int err, i;
> +	u16 size, value;
> +	u8  request, 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) +
> +		sizeof(const struct ixx_usbfd_dal_res);
> +
> +	buf = kmalloc(size, GFP_KERNEL);
> +	if(!buf)
> +		return -ENOMEM;
> +	memcpy(buf, (u8 *)dal_req, size);
> +
> +	for (i = 0; i < 10; ++i) {
> +		err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
> +			requesttype,
> +			value,
> +			0,
> +			buf,
> +			size,
> +			msecs_to_jiffies(50));
> +
> +		if (err < 0)
> +			msleep(20);
> +		else
> +			break;
> +	}
> +
> +	kfree(buf);
> +
> +	if (err < 0) {
> +		dev_err(&dev->dev, "sending command failure: %d\n", err);
> +		return err;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * receive usb-to-can fd command synchronously
> + */
> +static int ixx_usbfd_rcv_cmd(struct usb_device *dev,
> +		struct ixx_usbfd_dal_res *dal_res, int value)
> +{
> +	int err, res_size, i, size_to_read;
> +	u8  request, requesttype;
> +	u8 *buf;
> +
> +	request      = 0xff;
> +	requesttype  = 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 < 10; ++i) {
> +		err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request,
> +				requesttype, value,
> +				0, buf + (u8) res_size,
> +				size_to_read - res_size, msecs_to_jiffies(50));
> +
> +		if (err < 0) {
> +			msleep(20);
> +			continue;
> +		}
> +
> +		res_size += err;
> +		if (res_size < size_to_read)
> +			msleep(20);
> +		else
> +			break;
> +	}
> +
> +	if (res_size != size_to_read)
> +		err = -EBADMSG;
> +
> +	if (err < 0) {
> +		dev_err(&dev->dev, "receiving command failure: %d\n", err);
> +		kfree(buf);
> +		return err;
> +	}
> +
> +	memcpy((u8 *)dal_res, buf, size_to_read);
> +	kfree(buf);
> +
> +	return err;
> +}
> +
> +static int ixx_usbfd_init_ctrl(struct ixx_usb_device *dev, u8 mode,
> +			u8 exmode,
> +			struct can_bittiming *arbitration_phase,
> +			struct can_bittiming *data_phase)
> +{
> +	int err = -ENODEV;
> +	u8 data[IXXAT_USBFD_CMD_BUFFER_SIZE] = { 0 };
> +	struct ixx_usbfd_ctrl_init_v2_req *ctrl_init_req;
> +	struct ixx_usbfd_ctrl_init_v2_res *ctrl_init_res;
> +	u32 req_size = sizeof(*ctrl_init_req);
> +
> +	ctrl_init_req = (struct ixx_usbfd_ctrl_init_v2_req *) data;
> +	ctrl_init_res = (struct ixx_usbfd_ctrl_init_v2_res *)(data + req_size);
> +
> +	ctrl_init_req->dal_req.req_size   = cpu_to_le32(req_size);
> +	ctrl_init_req->dal_req.req_code   =
> +		cpu_to_le32(IXXAT_USBFD_INIT_V2_CMD);
> +	ctrl_init_req->dal_req.req_port   = cpu_to_le16(dev->ctrl_idx);
> +	ctrl_init_req->dal_req.req_socket = 0xffff;
> +	ctrl_init_req->opmode             = mode;
> +	ctrl_init_req->exmode             = exmode;
> +
> +	ctrl_init_req->sdr.mode = cpu_to_le32(IXX_BTMODE_NAT);
> +	ctrl_init_req->sdr.bps  = cpu_to_le32(arbitration_phase->brp);
> +	ctrl_init_req->sdr.ts1  =
> +		cpu_to_le16(arbitration_phase->prop_seg +
> +		arbitration_phase->phase_seg1);
> +	ctrl_init_req->sdr.ts2  = cpu_to_le16(arbitration_phase->phase_seg2);
> +	ctrl_init_req->sdr.sjw  = cpu_to_le16(arbitration_phase->sjw);
> +	ctrl_init_req->sdr.tdo  = 0;
> +
> +	if (exmode) {
> +		ctrl_init_req->fdr.mode = cpu_to_le32(IXX_BTMODE_NAT);
> +		ctrl_init_req->fdr.bps  = cpu_to_le32(data_phase->brp);
> +		ctrl_init_req->fdr.ts1  =
> +			cpu_to_le16(data_phase->prop_seg +
> +			data_phase->phase_seg1);
> +		ctrl_init_req->fdr.ts2  = cpu_to_le16(data_phase->phase_seg2);
> +		ctrl_init_req->fdr.sjw  = cpu_to_le16(data_phase->sjw);
> +		ctrl_init_req->fdr.tdo  =
> +			cpu_to_le16((1 + data_phase->phase_seg1 +
> +				data_phase->prop_seg) *
> +			data_phase->brp);
> +	}
> +
> +	ctrl_init_res->dal_res.res_size =
> +		cpu_to_le32(sizeof(*ctrl_init_res));
> +	ctrl_init_res->dal_res.ret_size = 0;
> +	ctrl_init_res->dal_res.ret_code = 0xffffffff;
> +
> +	err = ixx_usbfd_send_cmd(dev->udev, &ctrl_init_req->dal_req);
> +	if (err < 0)
> +		return err;
> +
> +	err = ixx_usbfd_rcv_cmd(dev->udev,
> +		&ctrl_init_res->dal_res,
> +		dev->ctrl_idx);
> +	if (err < 0)
> +		return err;
> +
> +	return le32_to_cpu(ctrl_init_res->dal_res.ret_code);
> +}
> +
> +static int ixx_usbfd_start_ctrl(struct ixx_usb_device *dev, u32 *time_ref)
> +{
> +	int err = -ENODEV;
> +	u8 data[IXXAT_USBFD_CMD_BUFFER_SIZE] = { 0 };
> +	struct ixx_usbfd_ctrl_start_req *ctrl_start_req;
> +	struct ixx_usbfd_ctrl_start_res *ctrl_start_res;
> +	u32 req_size = sizeof(*ctrl_start_req);
> +
> +	ctrl_start_req = (struct ixx_usbfd_ctrl_start_req *) data;
> +	ctrl_start_res = (struct ixx_usbfd_ctrl_start_res *)(data + req_size);
> +
> +	ctrl_start_req->dal_req.req_size   = cpu_to_le32(req_size);
> +	ctrl_start_req->dal_req.req_code   =
> +		cpu_to_le32(IXXAT_USBFD_CAN_START_CMD);
> +	ctrl_start_req->dal_req.req_port   = cpu_to_le16(dev->ctrl_idx);
> +	ctrl_start_req->dal_req.req_socket = 0xffff;
> +
> +	ctrl_start_res->dal_res.res_size =
> +		cpu_to_le32(sizeof(*ctrl_start_res));
> +	ctrl_start_res->dal_res.ret_size = 0;
> +	ctrl_start_res->dal_res.ret_code = 0xffffffff;
> +	ctrl_start_res->start_time       = 0;
> +
> +	err = ixx_usbfd_send_cmd(dev->udev, &ctrl_start_req->dal_req);
> +	if (err < 0)
> +		return err;
> +
> +	err = ixx_usbfd_rcv_cmd(dev->udev,
> +		&ctrl_start_res->dal_res,
> +		dev->ctrl_idx);
> +	if (err < 0)
> +		return err;
> +
> +	if (time_ref)
> +		*time_ref = le32_to_cpu(ctrl_start_res->start_time);
> +
> +	return le32_to_cpu(ctrl_start_res->dal_res.ret_code);
> +}
> +
> +static int ixx_usbfd_stop_ctrl(struct ixx_usb_device *dev)
> +{
> +	int err = -ENODEV;
> +	u8 data[IXXAT_USBFD_CMD_BUFFER_SIZE] = { 0 };
> +	struct ixx_usbfd_ctrl_stop_req *ctrl_stop_req;
> +	struct ixx_usbfd_ctrl_stop_res *ctrl_stop_res;
> +	u32 req_size = sizeof(*ctrl_stop_req);
> +
> +	ctrl_stop_req = (struct ixx_usbfd_ctrl_stop_req *) data;
> +	ctrl_stop_res = (struct ixx_usbfd_ctrl_stop_res *)(data + req_size);
> +
> +	ctrl_stop_req->dal_req.req_size   = cpu_to_le32(req_size);
> +	ctrl_stop_req->dal_req.req_code   =
> +		cpu_to_le32(IXXAT_USBFD_CAN_STOP_CMD);
> +	ctrl_stop_req->dal_req.req_port   = cpu_to_le16(dev->ctrl_idx);
> +	ctrl_stop_req->dal_req.req_socket = 0xffff;
> +	ctrl_stop_req->action             = cpu_to_le32(0x3);
> +
> +	ctrl_stop_res->dal_res.res_size =
> +		cpu_to_le32(sizeof(*ctrl_stop_res));
> +	ctrl_stop_res->dal_res.ret_size = 0;
> +	ctrl_stop_res->dal_res.ret_code = 0xffffffff;
> +
> +	err = ixx_usbfd_send_cmd(dev->udev, &ctrl_stop_req->dal_req);
> +	if (err < 0)
> +		return err;
> +
> +	err = ixx_usbfd_rcv_cmd(dev->udev,
> +		&ctrl_stop_res->dal_res,
> +		dev->ctrl_idx);
> +	if (err < 0)
> +		return err;
> +
> +	if (!le32_to_cpu(ctrl_stop_res->dal_res.ret_code))
> +		dev->can.state = CAN_STATE_STOPPED;
> +
> +	return le32_to_cpu(ctrl_stop_res->dal_res.ret_code);
> +}
> +
> +static int ixx_usbfd_reset_ctrl(struct ixx_usb_device *dev)
> +{
> +	int err = -ENODEV;
> +	u8 data[IXXAT_USBFD_CMD_BUFFER_SIZE] = { 0 };
> +	struct ixx_usbfd_dal_req *dal_req;
> +	struct ixx_usbfd_dal_res *dal_res;
> +	u32 req_size = sizeof(*dal_req);
> +
> +	dal_req = (struct ixx_usbfd_dal_req *) data;
> +	dal_res = (struct ixx_usbfd_dal_res *)(data + req_size);
> +
> +	dal_req->req_size   = cpu_to_le32(req_size);
> +	dal_req->req_code   = cpu_to_le32(IXXAT_USBFD_CAN_RESET_CMD);
> +	dal_req->req_port   = cpu_to_le16(dev->ctrl_idx);
> +	dal_req->req_socket = 0xffff;
> +
> +	dal_res->res_size = cpu_to_le32(sizeof(*dal_res));
> +	dal_res->ret_size = 0;
> +	dal_res->ret_code = 0xffffffff;
> +
> +	err = ixx_usbfd_send_cmd(dev->udev, dal_req);
> +	if (err < 0)
> +		return err;
> +
> +	err = ixx_usbfd_rcv_cmd(dev->udev, dal_res,
> +			dev->ctrl_idx);
> +	if (err < 0)
> +		return err;
> +
> +	return le32_to_cpu(dal_res->ret_code);
> +}
> +
> +static int ixx_usbfd_power_ctrl(struct usb_device *dev, u8 mode)
> +{
> +	int err = -ENODEV;
> +	u8 data[IXXAT_USBFD_CMD_BUFFER_SIZE] = { 0 };
> +	struct ixx_usbfd_dev_power_req *ctrl_power_req;
> +	struct ixx_usbfd_dev_power_res *ctrl_power_res;
> +	u32 req_size = sizeof(*ctrl_power_req);
> +
> +	ctrl_power_req = (struct ixx_usbfd_dev_power_req *) data;
> +	ctrl_power_res = (struct ixx_usbfd_dev_power_res *)(data + req_size);
> +
> +	ctrl_power_req->dal_req.req_size   = cpu_to_le32(req_size);
> +	ctrl_power_req->dal_req.req_code   =
> +		cpu_to_le32(IXXAT_USBFD_POWER_CMD);
> +	ctrl_power_req->dal_req.req_port   = cpu_to_le16(0xffff);
> +	ctrl_power_req->dal_req.req_socket = 0xffff;
> +	ctrl_power_req->mode               = mode;
> +
> +	ctrl_power_res->dal_res.res_size =
> +		cpu_to_le32(sizeof(*ctrl_power_res));
> +	ctrl_power_res->dal_res.ret_size = 0;
> +	ctrl_power_res->dal_res.ret_code = 0xffffffff;
> +
> +	err = ixx_usbfd_send_cmd(dev, &ctrl_power_req->dal_req);
> +	if (err < 0)
> +		return err;
> +
> +	err = ixx_usbfd_rcv_cmd(dev,
> +		&ctrl_power_res->dal_res,
> +		0xffff);
> +	if (err < 0)
> +		return err;
> +
> +	return le32_to_cpu(ctrl_power_res->dal_res.ret_code);
> +}
> +
> +/*
> + * handle restart but in asynchronously way
> + */
> +static int ixx_usbfd_restart_task(void *user_data)
> +{
> +	u32 time_ref;
> +	struct ixx_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) {
> +				ixx_usbfd_stop_ctrl(dev);
> +				ixx_usbfd_start_ctrl(dev, &time_ref);
> +				dev->restart_flag = 0;
> +				dev->can.state = CAN_STATE_ERROR_ACTIVE;
> +			}
> +		} else
> +			msleep(20);
> +	}
> +	return 0;
> +}
> +
> +static int ixx_usbfd_handle_canmsg(struct ixx_usb_device *dev,
> +		struct ixx_can_msg_v2 *rx)
> +{
> +	struct net_device *netdev = dev->netdev;
> +	struct canfd_frame *can_frame;
> +	struct sk_buff *skb;
> +	const u32 flags = le32_to_cpu(rx->flags);
> +
> +	if (flags & IXXAT_USBFD_MSG_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_USBFD_MSG_FLAGS_EDL) {
> +		if (flags & IXXAT_USBFD_MSG_FLAGS_FDR)
> +			can_frame->flags |= CANFD_BRS;
> +
> +		if (flags & IXXAT_USBFD_MSG_FLAGS_ESI)
> +			can_frame->flags |= CANFD_ESI;
> +
> +		can_frame->len =
> +			can_dlc2len(
> +			get_canfd_dlc((flags & IXXAT_USBFD_MSG_FLAGS_DLC)
> +			>> 16));
> +	}	else {
> +		can_frame->len =
> +			get_canfd_dlc((flags & IXXAT_USBFD_MSG_FLAGS_DLC)
> +			>> 16);
> +	}
> +
> +	if (flags & IXXAT_USBFD_MSG_FLAGS_OVR) {
> +		netdev->stats.rx_over_errors++;
> +		netdev->stats.rx_errors++;
> +	}
> +
> +	can_frame->can_id = le32_to_cpu(rx->msg_id);
> +
> +	if (flags & IXXAT_USBFD_MSG_FLAGS_EXT)
> +		can_frame->can_id |= CAN_EFF_FLAG;
> +
> +	if (flags & IXXAT_USBFD_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);
> +
> +	netif_rx(skb);
> +	netdev->stats.rx_packets++;
> +	netdev->stats.rx_bytes += can_frame->len;
> +
> +	return 0;
> +}
> +
> +static int ixx_usbfd_handle_error(struct ixx_usb_device *dev,
> +		struct ixx_can_msg_v2 *rx)
> +{
> +	struct net_device *netdev = dev->netdev;
> +	struct can_frame *can_frame;
> +	struct sk_buff *skb;
> +	u8 raw_status = 0;
> +
> +	/* nothing should be sent while in BUS_OFF state */
> +	if (dev->can.state == CAN_STATE_BUS_OFF)
> +		return 0;
> +
> +	raw_status = rx->data[0];
> +
> +	/* allocate an skb to store the error frame */
> +	skb = alloc_can_err_skb(netdev, &can_frame);
> +	if (!skb)
> +		return -ENOMEM;
> +
> +	switch (raw_status) {
> +	case IXXAT_USBFD_CAN_ERROR_ACK:
> +		can_frame->can_id |= CAN_ERR_ACK;
> +		netdev->stats.tx_errors++;
> +		break;
> +	case IXXAT_USBFD_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_USBFD_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_USBFD_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_USBFD_CAN_ERROR_STUFF:
> +		can_frame->can_id |= CAN_ERR_PROT;
> +		can_frame->data[2] |= CAN_ERR_PROT_STUFF;
> +		netdev->stats.rx_errors++;
> +		break;
> +	case IXXAT_USBFD_CAN_ERROR_OTHER:
> +		can_frame->can_id |= CAN_ERR_PROT;
> +		can_frame->data[2] |= CAN_ERR_PROT_UNSPEC;
> +		netdev->stats.rx_errors++;
> +		break;
> +	default:
> +		can_frame->can_id |= CAN_ERR_PROT;
> +		netdev->stats.rx_errors++;
> +	}
> +
> +	netif_rx(skb);
> +	netdev->stats.rx_packets++;
> +	netdev->stats.rx_bytes += can_frame->can_dlc;
> +
> +	dev->bec.txerr = le16_to_cpu(rx->data[1]);
> +	dev->bec.rxerr = le16_to_cpu(rx->data[3]);
> +
> +	return 0;
> +}
> +
> +static int ixx_usbfd_handle_status(struct ixx_usb_device *dev,
> +		struct ixx_can_msg_v2 *rx)
> +{
> +	struct net_device *netdev = dev->netdev;
> +	struct can_frame *can_frame;
> +	struct sk_buff *skb;
> +	u8 raw_status = 0;
> +	u32 new_state = 0;
> +
> +	raw_status = rx->data[0];
> +
> +	/* nothing should be sent while in BUS_OFF state */
> +	if (dev->can.state == CAN_STATE_BUS_OFF)
> +		return 0;
> +
> +	if (!raw_status) {
> +		/* no error bit (back to active state) */
> +		dev->can.state = CAN_STATE_ERROR_ACTIVE;
> +
> +		dev->bec.txerr = 0;
> +		dev->bec.rxerr = 0;
> +		return 0;
> +	}
> +
> +	/* allocate an skb to store the error frame */
> +	skb = alloc_can_err_skb(netdev, &can_frame);
> +	if (!skb)
> +		return -ENOMEM;
> +
> +	if (raw_status & IXXAT_USBFD_CAN_STATUS_BUSOFF) {
> +		can_frame->can_id |= CAN_ERR_BUSOFF;
> +		new_state = CAN_STATE_BUS_OFF;
> +		dev->can.can_stats.bus_off++;
> +		can_bus_off(netdev);
> +	} else {
> +		if (raw_status & IXXAT_USBFD_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_USBFD_CAN_STATUS_OVRRUN) {
> +			can_frame->can_id |= CAN_ERR_PROT;
> +			can_frame->data[2] |= CAN_ERR_PROT_OVERLOAD;
> +			netdev->stats.rx_over_errors++;
> +			netdev->stats.rx_errors++;
> +		}
> +
> +		if (!new_state) {
> +			new_state = CAN_STATE_ERROR_ACTIVE;
> +
> +			dev->bec.txerr = 0;
> +			dev->bec.rxerr = 0;
> +		}
> +	}
> +
> +	dev->can.state = new_state;
> +
> +	netif_rx(skb);
> +	netdev->stats.rx_packets++;
> +	netdev->stats.rx_bytes += can_frame->can_dlc;
> +
> +	return 0;
> +}
> +
> +/*
> + * callback for bulk IN urb
> + */
> +static int ixx_usbfd_decode_buf(struct ixx_usb_device *dev, struct urb *urb)
> +{
> +	struct net_device *netdev = dev->netdev;
> +	struct ixx_can_msg_v2 *can_msg;
> +	u32 msg_end;
> +	int err = 0;
> +	u32 read_size = 0;
> +	u8 msg_type;
> +
> +	u8 *data = urb->transfer_buffer;
> +
> +	/* loop reading all the records from the incoming message */
> +	msg_end = urb->actual_length;
> +	for (; msg_end > 0;) {
> +		can_msg = (struct ixx_can_msg_v2 *) &data[read_size];
> +
> +		if (!can_msg || !can_msg->size) {
> +			netdev_err(netdev, "got unsupported rec in usb msg:\n");
> +			err = -ENOTSUPP;
> +			break;
> +		}
> +
> +		/* check if the record goes out of current packet */
> +		if ((read_size + can_msg->size + 1) > urb->actual_length) {
> +			netdev_err(netdev,
> +			"got frag rec: should inc usb rx buf size\n");
> +			err = -EBADMSG;
> +			break;
> +		}
> +
> +		msg_type = (le32_to_cpu(can_msg->flags) &
> +			IXXAT_USBFD_MSG_FLAGS_TYPE);
> +
> +		switch (msg_type) {
> +
> +		case IXXAT_USBFD_CAN_DATA:
> +			err = ixx_usbfd_handle_canmsg(dev, can_msg);
> +			if (err < 0)
> +				goto fail;
> +			break;
> +
> +		case IXXAT_USBFD_CAN_STATUS:
> +			err = ixx_usbfd_handle_status(dev, can_msg);
> +			if (err < 0)
> +				goto fail;
> +			break;
> +
> +		case IXXAT_USBFD_CAN_ERROR:
> +			err = ixx_usbfd_handle_error(dev, can_msg);
> +			if (err < 0)
> +				goto fail;
> +			break;
> +
> +		case IXXAT_USBFD_CAN_TIMEOVR:
> +			ixxat_usb_get_ts_tv(dev, can_msg->time, NULL);
> +			break;
> +
> +		case IXXAT_USBFD_CAN_INFO:
> +		case IXXAT_USBFD_CAN_WAKEUP:
> +		case IXXAT_USBFD_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 (err)
> +		ixxat_dump_mem("received msg", urb->transfer_buffer,
> +				urb->actual_length);
> +
> +	return err;
> +}
> +
> +static int ixx_usbfd_encode_msg(struct ixx_usb_device *dev, struct sk_buff *skb,
> +		u8 *obuf, size_t *size)
> +{
> +	struct canfd_frame *cf = (struct canfd_frame *) skb->data;
> +	struct ixx_can_msg_v2 can_msg = { 0 };
> +
> +	if (cf->can_id & CAN_RTR_FLAG)
> +		can_msg.flags |= IXXAT_USBFD_MSG_FLAGS_RTR;
> +
> +	if (cf->can_id & CAN_EFF_FLAG) {
> +		can_msg.flags |= IXXAT_USBFD_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 (dev->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT)
> +		can_msg.flags |= IXXAT_USBFD_MSG_FLAGS_SSM;
> +
> +	if (skb->len == CANFD_MTU) {
> +		can_msg.flags |= IXXAT_USBFD_MSG_FLAGS_EDL;
> +
> +		if (!(cf->can_id & CAN_RTR_FLAG) && (cf->flags & CANFD_BRS))
> +			can_msg.flags |= IXXAT_USBFD_MSG_FLAGS_FDR;
> +	}
> +
> +	can_msg.flags |= (can_len2dlc(cf->len) << 16) &
> +		IXXAT_USBFD_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;
> +
> +	return 0;
> +}
> +
> +static int ixx_usbfd_start(struct ixx_usb_device *dev)
> +{
> +	int err;
> +	u32 time_ref = 0;
> +	u8 can_opmode = IXXAT_USBFD_OPMODE_EXTENDED
> +			| IXXAT_USBFD_OPMODE_STANDARD;
> +	u8 can_exmode = 0;
> +
> +	if (dev->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
> +		can_opmode |= IXXAT_USBFD_OPMODE_ERRFRAME;
> +	if (dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
> +		can_opmode |= IXXAT_USBFD_OPMODE_LISTONLY;
> +
> +	if ((CAN_CTRLMODE_FD | CAN_CTRLMODE_FD_NON_ISO) & dev->can.ctrlmode)
> +		can_exmode |= IXXAT_USBFD_EXMODE_EXTDATA |
> +			IXXAT_USBFD_EXMODE_FASTDATA;
> +
> +	if (!(CAN_CTRLMODE_FD_NON_ISO & dev->can.ctrlmode) && can_exmode)
> +		can_exmode |= IXXAT_USBFD_EXMODE_ISOFD;
> +
> +	/* Try to reset the controller, in case it is already initalized
> +	   from a previous unclean shutdown */
> +	ixx_usbfd_reset_ctrl(dev);
> +
> +	err = ixx_usbfd_init_ctrl(dev, can_opmode,
> +			can_exmode,
> +			&dev->can.bittiming,
> +			&dev->can.data_bittiming);
> +	if (err)
> +		return err;
> +
> +	/* opening first device: */
> +	if (dev->ctrl_opened_count == 0) {
> +		err = ixx_usbfd_start_ctrl(dev, &time_ref);
> +		if (err)
> +			return err;
> +
> +		ixxat_usb_set_ts_now(dev, time_ref);
> +	}
> +
> +	dev->ctrl_opened_count++;
> +
> +	dev->bec.txerr = 0;
> +	dev->bec.rxerr = 0;
> +
> +	return err;
> +}
> +
> +/*
> + * stop interface
> + * (last chance before set bus off)
> + */
> +static int ixx_usbfd_stop(struct ixx_usb_device *dev)
> +{
> +	int err;
> +
> +	if (dev->ctrl_opened_count == 1) {
> +		err = ixx_usbfd_stop_ctrl(dev);
> +		if (err)
> +			return err;
> +	}
> +
> +	dev->ctrl_opened_count--;
> +
> +	return 0;
> +}
> +
> +/*
> + * called when probing to initialize a device object.
> + */
> +static int ixx_usbfd_init(struct ixx_usb_device *dev)
> +{
> +	dev->restart_task = kthread_run(&ixx_usbfd_restart_task, dev,
> +			"restart_thread");
> +	if (!dev->restart_task)
> +		return -ENOBUFS;
> +
> +	return 0;
> +}
> +
> +static void ixx_usbfd_exit(struct ixx_usb_device *dev)
> +{
> +	ixx_usbfd_reset_ctrl(dev);
> +
> +	dev->must_quit = 1;
> +	dev->restart_flag = 1;
> +	wake_up_interruptible(&dev->wait_queue);
> +	if (dev->restart_task)
> +		kthread_stop(dev->restart_task);
> +}
> +
> +/*
> + * probe function for new IXXAT USB-to-CAN FD interface
> + */
> +static int ixx_usbfd_probe(struct usb_interface *intf)
> +{
> +	struct usb_host_interface *if_desc;
> +	int i;
> +
> +	if_desc = intf->altsetting;
> +
> +	/* check interface endpoint addresses */
> +	for (i = 0; i < if_desc->desc.bNumEndpoints; i++) {
> +		struct usb_endpoint_descriptor *ep = &if_desc->endpoint[i].desc;
> +
> +		/*
> +		 * below is the list of valid ep addreses. Any other ep address
> +		 * is considered as not-CAN interface address => no dev created
> +		 */
> +		switch (ep->bEndpointAddress) {
> +		case IXXAT_USBFD_EP_MSGOUT_0:
> +		case IXXAT_USBFD_EP_MSGOUT_1:
> +		case IXXAT_USBFD_EP_MSGOUT_2:
> +		case IXXAT_USBFD_EP_MSGOUT_3:
> +		case IXXAT_USBFD_EP_MSGOUT_4:
> +		case IXXAT_USBFD_EP_MSGIN_0:
> +		case IXXAT_USBFD_EP_MSGIN_1:
> +		case IXXAT_USBFD_EP_MSGIN_2:
> +		case IXXAT_USBFD_EP_MSGIN_3:
> +		case IXXAT_USBFD_EP_MSGIN_4:
> +
> +			break;
> +		default:
> +			return -ENODEV;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int ixx_usbfd_get_dev_caps(struct usb_device *dev,
> +		struct ixx_dev_caps *dev_caps)
> +{
> +	int err = -ENODEV, i;
> +	u8 data[IXXAT_USBFD_CMD_BUFFER_SIZE] = { 0 };
> +	struct ixx_usbfd_dev_caps_req *dev_caps_req;
> +	struct ixx_usbfd_dev_caps_res *dev_caps_res;
> +	u32 req_size = sizeof(*dev_caps_req);
> +
> +	dev_caps_req = (struct ixx_usbfd_dev_caps_req *) data;
> +	dev_caps_res = (struct ixx_usbfd_dev_caps_res *)(data + req_size);
> +
> +	dev_caps_req->dal_req.req_size   = cpu_to_le32(req_size);
> +	dev_caps_req->dal_req.req_code   =
> +		cpu_to_le32(IXXAT_USBFD_BRD_GET_DEVCAPS_CMD);
> +	dev_caps_req->dal_req.req_port   = 0xffff;
> +	dev_caps_req->dal_req.req_socket = 0xffff;
> +
> +	dev_caps_res->dal_res.res_size = cpu_to_le32(
> +		sizeof(*dev_caps_res));
> +	dev_caps_res->dal_res.ret_size = 0;
> +	dev_caps_res->dal_res.ret_code = 0xffffffff;
> +
> +	err = ixx_usbfd_send_cmd(dev, &dev_caps_req->dal_req);
> +	if (err < 0)
> +		return err;
> +
> +	err = ixx_usbfd_rcv_cmd(dev, &dev_caps_res->dal_res,
> +			0xffff);
> +	if (err < 0)
> +		return err;
> +
> +	dev_caps->bus_ctrl_count =
> +		le16_to_cpu(dev_caps_res->dev_caps.bus_ctrl_count);
> +	for (i = 0; i < dev_caps->bus_ctrl_count; ++i)
> +		dev_caps->bus_ctrl_types[i] =
> +			le16_to_cpu(dev_caps_res->dev_caps.bus_ctrl_types[i]);
> +
> +	return 0;
> +}
> +
> +static int ixx_usbfd_get_ctrl_caps(struct usb_device *dev,
> +		struct ixx_ctrl_caps *ctrl_caps, int index)
> +{
> +	int err = -ENODEV;
> +	u8 data[IXXAT_USBFD_CMD_BUFFER_SIZE] = { 0 };
> +	struct ixx_usbfd_ctrl_caps_req *ctrl_caps_req;
> +	struct ixx_usbfd_ctrl_caps_res *ctrl_caps_res;
> +	u32 req_size = sizeof(*ctrl_caps_req);
> +
> +	ctrl_caps_req = (struct ixx_usbfd_ctrl_caps_req *) data;
> +	ctrl_caps_res = (struct ixx_usbfd_ctrl_caps_res *)(data + req_size);
> +
> +	ctrl_caps_req->dal_req.req_size   = cpu_to_le32(req_size);
> +	ctrl_caps_req->dal_req.req_code   =
> +		cpu_to_le32(IXXAT_USBFD_CAN_GET_CAPS_CMD);
> +	ctrl_caps_req->dal_req.req_port   = cpu_to_le16(index);
> +	ctrl_caps_req->dal_req.req_socket = 0xffff;
> +
> +	ctrl_caps_res->dal_res.res_size =
> +		cpu_to_le32(sizeof(*ctrl_caps_res));
> +	ctrl_caps_res->dal_res.ret_size = 0;
> +	ctrl_caps_res->dal_res.ret_code = 0xffffffff;
> +
> +	err = ixx_usbfd_send_cmd(dev, &ctrl_caps_req->dal_req);
> +	if (err < 0)
> +		return err;
> +
> +	err = ixx_usbfd_rcv_cmd(dev,
> +			&ctrl_caps_res->dal_res,
> +			index);
> +	if (err < 0)
> +		return err;
> +
> +	ctrl_caps->bus_coupling =
> +		le16_to_cpu(ctrl_caps_res->ctrl_caps.bus_coupling);
> +	ctrl_caps->clock_freq =
> +		le32_to_cpu(ctrl_caps_res->ctrl_caps.clock_freq);
> +	ctrl_caps->cms_divisor =
> +		le32_to_cpu(ctrl_caps_res->ctrl_caps.cms_divisor);
> +	ctrl_caps->cms_max_ticks =
> +		le32_to_cpu(ctrl_caps_res->ctrl_caps.cms_max_ticks);
> +	ctrl_caps->ctrl_type = le16_to_cpu(ctrl_caps_res->ctrl_caps.ctrl_type);
> +	ctrl_caps->dtx_divisor =
> +		le32_to_cpu(ctrl_caps_res->ctrl_caps.dtx_divisor);
> +	ctrl_caps->dtx_max_ticks =
> +		le32_to_cpu(ctrl_caps_res->ctrl_caps.dtx_max_ticks);
> +	ctrl_caps->features = le32_to_cpu(ctrl_caps_res->ctrl_caps.features);
> +	ctrl_caps->tsc_divisor =
> +		le32_to_cpu(ctrl_caps_res->ctrl_caps.tsc_divisor);
> +
> +	return 0;
> +}
> +
> +static int ixx_usbfd_get_fwinfo(struct ixx_usb_device *dev,
> +		struct ixx_intf_fw_info *fwinfo)
> +{
> +	int err = -ENODEV;
> +	u8 data[IXXAT_USBFD_CMD_BUFFER_SIZE] = { 0 };
> +	struct ixx_usbfd_brd_get_fwinfo_req *fw_info_req;
> +	struct ixx_usbfd_brd_get_fwinfo_res *fw_info_res;
> +	u32 req_size = sizeof(*fw_info_req);
> +
> +	fw_info_req = (struct ixx_usbfd_brd_get_fwinfo_req *) data;
> +	fw_info_res = (struct ixx_usbfd_brd_get_fwinfo_res *)(data + req_size);
> +
> +	fw_info_req->dal_req.req_size   = cpu_to_le32(req_size);
> +	fw_info_req->dal_req.req_code   =
> +		cpu_to_le32(IXXAT_USBFD_BRD_GET_FWINFO_CMD);
> +	fw_info_req->dal_req.req_port   = 0xffff;
> +	fw_info_req->dal_req.req_socket = 0xffff;
> +
> +	fw_info_res->dal_res.res_size =
> +		cpu_to_le32(sizeof(*fw_info_res));
> +	fw_info_res->dal_res.ret_size = 0;
> +	fw_info_res->dal_res.ret_code = 0xffffffff;
> +
> +	err = ixx_usbfd_send_cmd(dev->udev, &fw_info_req->dal_req);
> +	if (err < 0)
> +		return err;
> +
> +	err = ixx_usbfd_rcv_cmd(dev->udev,
> +		&fw_info_res->dal_res, 0xffff);
> +	if (err < 0)
> +		return err;
> +
> +	if (fwinfo) {
> +		fwinfo->build_version = le16_to_cpu(
> +			fw_info_res->fwinfo.build_version);
> +		fwinfo->firmware_type = le32_to_cpu(
> +			fw_info_res->fwinfo.firmware_type);
> +		fwinfo->major_version = le16_to_cpu(
> +			fw_info_res->fwinfo.major_version);
> +		fwinfo->minor_version = le16_to_cpu(
> +			fw_info_res->fwinfo.minor_version);
> +		fwinfo->reserved = le16_to_cpu(fw_info_res->fwinfo.reserved);
> +	}
> +
> +	return le32_to_cpu(fw_info_res->dal_res.ret_code);
> +}
> +
> +static int ixx_usbfd_get_dev_info(struct ixx_usb_device *dev,
> +		struct ixx_intf_info *dev_info)
> +{
> +	int err = -ENODEV;
> +	u8 data[IXXAT_USBFD_CMD_BUFFER_SIZE] = { 0 };
> +	struct ixx_usbfd_brd_get_intf_info_req *dev_info_req;
> +	struct ixx_usbfd_brd_get_intf_info_res *dev_info_res;
> +	u32 req_size = sizeof(*dev_info_req);
> +
> +	dev_info_req = (struct ixx_usbfd_brd_get_intf_info_req *) data;
> +	dev_info_res =
> +		(struct ixx_usbfd_brd_get_intf_info_res *)(data + req_size);
> +
> +	dev_info_req->dal_req.req_size = cpu_to_le32(req_size);
> +	dev_info_req->dal_req.req_code =
> +		cpu_to_le32(IXXAT_USBFD_BRD_GET_DEVINFO_CMD);
> +	dev_info_req->dal_req.req_port = 0xffff;
> +	dev_info_req->dal_req.req_socket = 0xffff;
> +
> +	dev_info_res->dal_res.res_size =
> +		cpu_to_le32(sizeof(*dev_info_res));
> +	dev_info_res->dal_res.ret_size = 0;
> +	dev_info_res->dal_res.ret_code = 0xffffffff;
> +
> +	err = ixx_usbfd_send_cmd(dev->udev,
> +			&dev_info_req->dal_req);
> +	if (err < 0)
> +		return err;
> +
> +	err = ixx_usbfd_rcv_cmd(dev->udev,
> +			&dev_info_res->dal_res,
> +			0xffff);
> +	if (err < 0)
> +		return err;
> +
> +	if (dev_info) {
> +		memcpy(dev_info->device_id, &dev_info_res->info.device_id,
> +			sizeof(dev_info_res->info.device_id));
> +		memcpy(dev_info->device_name, &dev_info_res->info.device_name,
> +			sizeof(dev_info_res->info.device_name));
> +		dev_info->device_fpga_version = le16_to_cpu(
> +			dev_info_res->info.device_fpga_version);
> +		dev_info->device_version = le32_to_cpu(
> +			dev_info_res->info.device_version);
> +	}
> +
> +	return le32_to_cpu(dev_info_res->dal_res.ret_code);
> +}
> +
> +/*
> + * describes the USB-to-CAN FD automotive adapter
> + */
> +struct ixx_usb_adapter usb_to_can_fd_automotive = {
> +	.name = "USB-to-CAN FD automotive",
> +	.device_id = USB_TO_CAN_FD_AUTOMOTIVE_PRODUCT_ID,
> +	.clock = {
> +		.freq = IFIFD_CRYSTAL_HZ,
> +	},
> +
> +	.bittiming_const = {
> +		.name = "ifi_can",
> +		.tseg1_min = 1,
> +		.tseg1_max = 256,
> +		.tseg2_min = 1,
> +		.tseg2_max = 256,
> +		.sjw_max = 128,
> +		.brp_min = 2,
> +		.brp_max = 513,
> +		.brp_inc = 1, },
> +
> +	.data_bittiming_const = {
> +		.name = "ifi_can",
> +		.tseg1_min = 1,
> +		.tseg1_max = 256,
> +		.tseg2_min = 1,
> +		.tseg2_max = 256,
> +		.sjw_max = 128,
> +		.brp_min = 2,
> +		.brp_max = 513,
> +		.brp_inc = 1, },
> +
> +	.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
> +		CAN_CTRLMODE_LISTENONLY |
> +		CAN_CTRLMODE_LOOPBACK |
> +		CAN_CTRLMODE_BERR_REPORTING |
> +		CAN_CTRLMODE_FD |
> +		CAN_CTRLMODE_FD_NON_ISO,
> +
> +	/* size of device private data */
> +	.sizeof_dev_private = sizeof(const struct ixx_usb_device),
> +
> +	/* give here messages in/out endpoints */
> +	.ep_msg_in = {  IXXAT_USBFD_EP_MSGIN_0, IXXAT_USBFD_EP_MSGIN_1,
> +			IXXAT_USBFD_EP_MSGIN_2, IXXAT_USBFD_EP_MSGIN_3,
> +			IXXAT_USBFD_EP_MSGIN_4 },
> +	.ep_msg_out = { IXXAT_USBFD_EP_MSGOUT_0, IXXAT_USBFD_EP_MSGOUT_1,
> +			IXXAT_USBFD_EP_MSGOUT_2, IXXAT_USBFD_EP_MSGOUT_3,
> +			IXXAT_USBFD_EP_MSGOUT_4 },
> +
> +	/* size of rx/tx usb buffers */
> +	.rx_buffer_size = IXXAT_USBFD_RX_BUFFER_SIZE,
> +	.tx_buffer_size = IXXAT_USBFD_TX_BUFFER_SIZE,
> +
> +	/* device callbacks */
> +	.intf_probe = ixx_usbfd_probe,
> +	.dev_get_dev_caps = ixx_usbfd_get_dev_caps,
> +	.dev_get_ctrl_caps = ixx_usbfd_get_ctrl_caps,
> +	.dev_init = ixx_usbfd_init,
> +	.dev_exit = ixx_usbfd_exit,
> +	.intf_get_info = ixx_usbfd_get_dev_info,
> +	.intf_get_fw_info = ixx_usbfd_get_fwinfo,
> +	.dev_decode_buf = ixx_usbfd_decode_buf,
> +	.dev_encode_msg = ixx_usbfd_encode_msg,
> +	.dev_start = ixx_usbfd_start,
> +	.dev_stop = ixx_usbfd_stop,
> +	.dev_power = ixx_usbfd_power_ctrl,
> +	.has_bgi_ep = 1,
> +};
> +
> +/*
> + * describes the USB-to-CAN FD compact adapter
> + */
> +struct ixx_usb_adapter usb_to_can_fd_compact = {
> +	.name = "USB-to-CAN FD compact",
> +	.device_id = USB_TO_CAN_FD_COMPACT_PRODUCT_ID,
> +	.clock = {
> +		.freq = IFIFD_CRYSTAL_HZ,
> +	},
> +
> +	.bittiming_const = {
> +		.name = "ifi_can",
> +		.tseg1_min = 1,
> +		.tseg1_max = 256,
> +		.tseg2_min = 1,
> +		.tseg2_max = 256,
> +		.sjw_max = 128,
> +		.brp_min = 2,
> +		.brp_max = 513,
> +		.brp_inc = 1, },
> +
> +	.data_bittiming_const = {
> +		.name = "ifi_can",
> +		.tseg1_min = 1,
> +		.tseg1_max = 256,
> +		.tseg2_min = 1,
> +		.tseg2_max = 256,
> +		.sjw_max = 128,
> +		.brp_min = 2,
> +		.brp_max = 513,
> +		.brp_inc = 1, },
> +
> +	.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
> +		CAN_CTRLMODE_LISTENONLY |
> +		CAN_CTRLMODE_LOOPBACK |
> +		CAN_CTRLMODE_BERR_REPORTING |
> +		CAN_CTRLMODE_FD |
> +		CAN_CTRLMODE_FD_NON_ISO,
> +
> +	/* size of device private data */
> +	.sizeof_dev_private = sizeof(const struct ixx_usb_device),
> +
> +	/* give here messages in/out endpoints */
> +	.ep_msg_in = {  IXXAT_USBFD_EP_MSGIN_0, IXXAT_USBFD_EP_MSGIN_1,
> +			IXXAT_USBFD_EP_MSGIN_2, IXXAT_USBFD_EP_MSGIN_3,
> +			IXXAT_USBFD_EP_MSGIN_4 },
> +	.ep_msg_out = { IXXAT_USBFD_EP_MSGOUT_0, IXXAT_USBFD_EP_MSGOUT_1,
> +			IXXAT_USBFD_EP_MSGOUT_2, IXXAT_USBFD_EP_MSGOUT_3,
> +			IXXAT_USBFD_EP_MSGOUT_4 },
> +
> +	/* size of rx/tx usb buffers */
> +	.rx_buffer_size = IXXAT_USBFD_RX_BUFFER_SIZE,
> +	.tx_buffer_size = IXXAT_USBFD_TX_BUFFER_SIZE,
> +
> +	/* device callbacks */
> +	.intf_probe = ixx_usbfd_probe,
> +	.dev_get_dev_caps = ixx_usbfd_get_dev_caps,
> +	.dev_get_ctrl_caps = ixx_usbfd_get_ctrl_caps,
> +	.dev_init = ixx_usbfd_init,
> +	.dev_exit = ixx_usbfd_exit,
> +	.intf_get_info = ixx_usbfd_get_dev_info,
> +	.intf_get_fw_info = ixx_usbfd_get_fwinfo,
> +	.dev_decode_buf = ixx_usbfd_decode_buf,
> +	.dev_encode_msg = ixx_usbfd_encode_msg,
> +	.dev_start = ixx_usbfd_start,
> +	.dev_stop = ixx_usbfd_stop,
> +	.dev_power = ixx_usbfd_power_ctrl,
> +	.has_bgi_ep = 1,
> +};
> +
> +/*
> + * describes the DELL Edge GW3002
> + */
> +struct ixx_usb_adapter dell_edge_gw3002 = {
> +	.name = "USB DELL Edge GW3002",
> +	.device_id = DELL_EDGE_GW3002_PRODUCT_ID,
> +	.clock = {
> +		.freq = IFIFD_CRYSTAL_HZ,
> +	},
> +
> +	.bittiming_const = {
> +		.name = "mcan",
> +		.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 = "mcan",
> +		.tseg1_min = 1,
> +		.tseg1_max = 16,
> +		.tseg2_min = 1,
> +		.tseg2_max = 8,
> +		.sjw_max = 4,
> +		.brp_min = 1,
> +		.brp_max = 32,
> +		.brp_inc = 1, },
> +
> +	.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
> +		CAN_CTRLMODE_LISTENONLY |
> +		CAN_CTRLMODE_LOOPBACK |
> +		CAN_CTRLMODE_BERR_REPORTING
> +
> +		/* currently not supported
> +		CAN_CTRLMODE_FD |
> +		CAN_CTRLMODE_FD_NON_ISO
> +		*/,
> +
> +	/* size of device private data */
> +	.sizeof_dev_private = sizeof(const struct ixx_usb_device),
> +
> +	/* give here messages in/out endpoints */
> +	.ep_msg_in = {  DELL_EDGE_GW3002_EP_MSGIN_0, DELL_EDGE_GW3002_EP_MSGIN_1,
> +			DELL_EDGE_GW3002_EP_MSGIN_2, DELL_EDGE_GW3002_EP_MSGIN_3,
> +			DELL_EDGE_GW3002_EP_MSGIN_4 },
> +	.ep_msg_out = { DELL_EDGE_GW3002_EP_MSGOUT_0, DELL_EDGE_GW3002_EP_MSGOUT_1,
> +			DELL_EDGE_GW3002_EP_MSGOUT_2, DELL_EDGE_GW3002_EP_MSGOUT_3,
> +			DELL_EDGE_GW3002_EP_MSGOUT_4 },
> +
> +	/* size of rx/tx usb buffers */
> +	.rx_buffer_size = IXXAT_USBFD_RX_BUFFER_SIZE,
> +	.tx_buffer_size = IXXAT_USBFD_TX_BUFFER_SIZE,
> +
> +	/* device callbacks */
> +	.intf_probe = ixx_usbfd_probe,
> +	.dev_get_dev_caps = ixx_usbfd_get_dev_caps,
> +	.dev_get_ctrl_caps = ixx_usbfd_get_ctrl_caps,
> +	.dev_init = ixx_usbfd_init,
> +	.dev_exit = ixx_usbfd_exit,
> +	.intf_get_info = ixx_usbfd_get_dev_info,
> +	.intf_get_fw_info = ixx_usbfd_get_fwinfo,
> +	.dev_decode_buf = ixx_usbfd_decode_buf,
> +	.dev_encode_msg = ixx_usbfd_encode_msg,
> +	.dev_start = ixx_usbfd_start,
> +	.dev_stop = ixx_usbfd_stop,
> +	.dev_power = ixx_usbfd_power_ctrl,
> +	.has_bgi_ep = 0,
> +};
> +
> +/*
> + * describes the USB-to-CAN FD professional adapter
> + */
> +struct ixx_usb_adapter usb_to_can_fd_professional = {
> +	.name = "USB-to-CAN FD professional",
> +	.device_id = USB_TO_CAN_FD_PROFESSIONAL_PRODUCT_ID,
> +	.clock = {
> +		.freq = IFIFD_CRYSTAL_HZ,
> +	},
> +
> +	.bittiming_const = {
> +		.name = "ifi_can",
> +		.tseg1_min = 1,
> +		.tseg1_max = 256,
> +		.tseg2_min = 1,
> +		.tseg2_max = 256,
> +		.sjw_max = 128,
> +		.brp_min = 2,
> +		.brp_max = 513,
> +		.brp_inc = 1, },
> +
> +	.data_bittiming_const = {
> +		.name = "ifi_can",
> +		.tseg1_min = 1,
> +		.tseg1_max = 256,
> +		.tseg2_min = 1,
> +		.tseg2_max = 256,
> +		.sjw_max = 128,
> +		.brp_min = 2,
> +		.brp_max = 513,
> +		.brp_inc = 1, },
> +
> +	.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
> +		CAN_CTRLMODE_LISTENONLY |
> +		CAN_CTRLMODE_LOOPBACK |
> +		CAN_CTRLMODE_BERR_REPORTING |
> +		CAN_CTRLMODE_FD |
> +		CAN_CTRLMODE_FD_NON_ISO,
> +
> +	/* size of device private data */
> +	.sizeof_dev_private = sizeof(const struct ixx_usb_device),
> +
> +	/* give here messages in/out endpoints */
> +	.ep_msg_in = {  IXXAT_USBFD_EP_MSGIN_0, IXXAT_USBFD_EP_MSGIN_1,
> +			IXXAT_USBFD_EP_MSGIN_2, IXXAT_USBFD_EP_MSGIN_3,
> +			IXXAT_USBFD_EP_MSGIN_4 },
> +	.ep_msg_out = { IXXAT_USBFD_EP_MSGOUT_0, IXXAT_USBFD_EP_MSGOUT_1,
> +			IXXAT_USBFD_EP_MSGOUT_2, IXXAT_USBFD_EP_MSGOUT_3,
> +			IXXAT_USBFD_EP_MSGOUT_4 },
> +
> +	/* size of rx/tx usb buffers */
> +	.rx_buffer_size = IXXAT_USBFD_RX_BUFFER_SIZE,
> +	.tx_buffer_size = IXXAT_USBFD_TX_BUFFER_SIZE,
> +
> +	/* device callbacks */
> +	.intf_probe = ixx_usbfd_probe,
> +	.dev_get_dev_caps = ixx_usbfd_get_dev_caps,
> +	.dev_get_ctrl_caps = ixx_usbfd_get_ctrl_caps,
> +	.dev_init = ixx_usbfd_init,
> +	.dev_exit = ixx_usbfd_exit,
> +	.intf_get_info = ixx_usbfd_get_dev_info,
> +	.intf_get_fw_info = ixx_usbfd_get_fwinfo,
> +	.dev_decode_buf = ixx_usbfd_decode_buf,
> +	.dev_encode_msg = ixx_usbfd_encode_msg,
> +	.dev_start = ixx_usbfd_start,
> +	.dev_stop = ixx_usbfd_stop,
> +	.dev_power = ixx_usbfd_power_ctrl,
> +	.has_bgi_ep = 1,
> +};
> +
> +/*
> + * describes the USB-to-CAN FD PCIe mini adapter
> + */
> +struct ixx_usb_adapter usb_to_can_fd_pcie_mini = {
> +	.name = "USB-to-CAN FD PCIe mini",
> +	.device_id = USB_TO_CAN_FD_PCIE_MINI_PRODUCT_ID,
> +	.clock = {
> +		.freq = IFIFD_CRYSTAL_HZ,
> +	},
> +
> +	.bittiming_const = {
> +		.name = "ifi_can",
> +		.tseg1_min = 1,
> +		.tseg1_max = 256,
> +		.tseg2_min = 1,
> +		.tseg2_max = 256,
> +		.sjw_max = 128,
> +		.brp_min = 2,
> +		.brp_max = 513,
> +		.brp_inc = 1, },
> +
> +	.data_bittiming_const = {
> +		.name = "ifi_can",
> +		.tseg1_min = 1,
> +		.tseg1_max = 256,
> +		.tseg2_min = 1,
> +		.tseg2_max = 256,
> +		.sjw_max = 128,
> +		.brp_min = 2,
> +		.brp_max = 513,
> +		.brp_inc = 1, },
> +
> +	.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
> +		CAN_CTRLMODE_LISTENONLY |
> +		CAN_CTRLMODE_LOOPBACK |
> +		CAN_CTRLMODE_BERR_REPORTING |
> +		CAN_CTRLMODE_FD |
> +		CAN_CTRLMODE_FD_NON_ISO,
> +
> +	/* size of device private data */
> +	.sizeof_dev_private = sizeof(const struct ixx_usb_device),
> +
> +	/* give here messages in/out endpoints */
> +	.ep_msg_in = {  IXXAT_USBFD_EP_MSGIN_0, IXXAT_USBFD_EP_MSGIN_1,
> +			IXXAT_USBFD_EP_MSGIN_2, IXXAT_USBFD_EP_MSGIN_3,
> +			IXXAT_USBFD_EP_MSGIN_4 },
> +	.ep_msg_out = { IXXAT_USBFD_EP_MSGOUT_0, IXXAT_USBFD_EP_MSGOUT_1,
> +			IXXAT_USBFD_EP_MSGOUT_2, IXXAT_USBFD_EP_MSGOUT_3,
> +			IXXAT_USBFD_EP_MSGOUT_4 },
> +
> +	/* size of rx/tx usb buffers */
> +	.rx_buffer_size = IXXAT_USBFD_RX_BUFFER_SIZE,
> +	.tx_buffer_size = IXXAT_USBFD_TX_BUFFER_SIZE,
> +
> +	/* device callbacks */
> +	.intf_probe = ixx_usbfd_probe,
> +	.dev_get_dev_caps = ixx_usbfd_get_dev_caps,
> +	.dev_get_ctrl_caps = ixx_usbfd_get_ctrl_caps,
> +	.dev_init = ixx_usbfd_init,
> +	.dev_exit = ixx_usbfd_exit,
> +	.intf_get_info = ixx_usbfd_get_dev_info,
> +	.intf_get_fw_info = ixx_usbfd_get_fwinfo,
> +	.dev_decode_buf = ixx_usbfd_decode_buf,
> +	.dev_encode_msg = ixx_usbfd_encode_msg,
> +	.dev_start = ixx_usbfd_start,
> +	.dev_stop = ixx_usbfd_stop,
> +	.dev_power = ixx_usbfd_power_ctrl,
> +	.has_bgi_ep = 1,
> +};
> +
> +/*
> + * describes the USB-to-CAR adapter
> + */
> +struct ixx_usb_adapter usb_to_car = {
> +	.name = "USB-to-CAR",
> +	.device_id = USB_TO_CAR_ID,
> +	.clock = {
> +		.freq = IFIFD_CRYSTAL_HZ,
> +	},
> +
> +	.bittiming_const = {
> +		.name = "ifi_can",
> +		.tseg1_min = 1,
> +		.tseg1_max = 256,
> +		.tseg2_min = 1,
> +		.tseg2_max = 256,
> +		.sjw_max = 128,
> +		.brp_min = 2,
> +		.brp_max = 513,
> +		.brp_inc = 1, },
> +
> +	.data_bittiming_const = {
> +		.name = "ifi_can",
> +		.tseg1_min = 1,
> +		.tseg1_max = 256,
> +		.tseg2_min = 1,
> +		.tseg2_max = 256,
> +		.sjw_max = 128,
> +		.brp_min = 2,
> +		.brp_max = 513,
> +		.brp_inc = 1, },
> +
> +	.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
> +		CAN_CTRLMODE_LISTENONLY |
> +		CAN_CTRLMODE_LOOPBACK |
> +		CAN_CTRLMODE_BERR_REPORTING |
> +		CAN_CTRLMODE_FD |
> +		CAN_CTRLMODE_FD_NON_ISO,
> +
> +	/* size of device private data */
> +	.sizeof_dev_private = sizeof(const struct ixx_usb_device),
> +
> +	/* give here messages in/out endpoints */
> +	.ep_msg_in = {  IXXAT_USBFD_EP_MSGIN_0, IXXAT_USBFD_EP_MSGIN_1,
> +			IXXAT_USBFD_EP_MSGIN_2, IXXAT_USBFD_EP_MSGIN_3,
> +			IXXAT_USBFD_EP_MSGIN_4 },
> +	.ep_msg_out = { IXXAT_USBFD_EP_MSGOUT_0, IXXAT_USBFD_EP_MSGOUT_1,
> +			IXXAT_USBFD_EP_MSGOUT_2, IXXAT_USBFD_EP_MSGOUT_3,
> +			IXXAT_USBFD_EP_MSGOUT_4 },
> +
> +	/* size of rx/tx usb buffers */
> +	.rx_buffer_size = IXXAT_USBFD_RX_BUFFER_SIZE,
> +	.tx_buffer_size = IXXAT_USBFD_TX_BUFFER_SIZE,
> +
> +	/* device callbacks */
> +	.intf_probe = ixx_usbfd_probe,
> +	.dev_get_dev_caps = ixx_usbfd_get_dev_caps,
> +	.dev_get_ctrl_caps = ixx_usbfd_get_ctrl_caps,
> +	.dev_init = ixx_usbfd_init,
> +	.dev_exit = ixx_usbfd_exit,
> +	.intf_get_info = ixx_usbfd_get_dev_info,
> +	.intf_get_fw_info = ixx_usbfd_get_fwinfo,
> +	.dev_decode_buf = ixx_usbfd_decode_buf,
> +	.dev_encode_msg = ixx_usbfd_encode_msg,
> +	.dev_start = ixx_usbfd_start,
> +	.dev_stop = ixx_usbfd_stop,
> +	.dev_power = ixx_usbfd_power_ctrl,
> +	.has_bgi_ep = 1,
> +};
> +
> +
> +#endif // CANFD_CAPABLE
> diff --git a/ubuntu/ixxat/ixx_usb_v2.c b/ubuntu/ixxat/ixx_usb_v2.c
> new file mode 100644
> index 000000000000..3fe639c5b817
> --- /dev/null
> +++ b/ubuntu/ixxat/ixx_usb_v2.c
> @@ -0,0 +1,1450 @@
> +/*
> + * CAN driver for IXXAT USB-to-CAN V2
> + *
> + * Copyright (C) 2014 Michael Hengler <mhengler@ixxat.de>
> + *
> + * Based on code originally by pcan_usb_core
> + *
> + * 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/delay.h>
> +#include <linux/can.h>
> +#include <linux/can/dev.h>
> +#include <linux/can/error.h>
> +#include <linux/kthread.h>
> +#include <linux/sched.h>
> +#include <linux/wait.h>
> +#include <linux/types.h>
> +#include <linux/gfp.h>
> +#include <asm-generic/errno.h>
> +#include <stdarg.h>
> +
> +#include "ixx_usb_core.h"
> +
> +MODULE_SUPPORTED_DEVICE("IXXAT Automation GmbH USB-to-CAN V2");
> +
> +/* use sja 1000 clock due to internal bittiming calculations */
> +#define SJA1000_CRYSTAL_HZ	      8000000
> +
> +/* usb-to-can v2 Endpoints */
> +#define IXXAT_USBV2_EP_CMDOUT   0
> +#define IXXAT_USBV2_EP_CMDIN    (IXXAT_USBV2_EP_CMDOUT | USB_DIR_IN)
> +#define IXXAT_USBV2_EP_MSGOUT_0 1
> +#define IXXAT_USBV2_EP_MSGIN_0  (IXXAT_USBV2_EP_MSGOUT_0 | USB_DIR_IN)
> +#define IXXAT_USBV2_EP_MSGOUT_1 2
> +#define IXXAT_USBV2_EP_MSGIN_1  (IXXAT_USBV2_EP_MSGOUT_1 | USB_DIR_IN)
> +#define IXXAT_USBV2_EP_MSGOUT_2 3
> +#define IXXAT_USBV2_EP_MSGIN_2  (IXXAT_USBV2_EP_MSGOUT_2 | USB_DIR_IN)
> +#define IXXAT_USBV2_EP_MSGOUT_3 4
> +#define IXXAT_USBV2_EP_MSGIN_3  (IXXAT_USBV2_EP_MSGOUT_3 | USB_DIR_IN)
> +#define IXXAT_USBV2_EP_MSGOUT_4 5
> +#define IXXAT_USBV2_EP_MSGIN_4  (IXXAT_USBV2_EP_MSGOUT_4 | USB_DIR_IN)
> +
> +/* usb-to-can v2 rx/tx buffers size */
> +#define IXXAT_USBV2_RX_BUFFER_SIZE      512
> +#define IXXAT_USBV2_TX_BUFFER_SIZE      256
> +
> +#define IXXAT_USBV2_CMD_BUFFER_SIZE     256
> +
> +#define IXXAT_USBV2_OPMODE_STANDARD  0x01 /* reception of 11-bit id messages */
> +#define IXXAT_USBV2_OPMODE_EXTENDED  0x02 /* reception of 29-bit id messages */
> +#define IXXAT_USBV2_OPMODE_ERRFRAME  0x04 /* enable reception of error frames */
> +#define IXXAT_USBV2_OPMODE_LISTONLY  0x08 /* listen only mode (TX passive) */
> +
> +/* Stuff error */
> +#define IXXAT_USBV2_CAN_ERROR_STUFF      1
> +/* Form error */
> +#define IXXAT_USBV2_CAN_ERROR_FORM       2
> +/* Acknowledgment error */
> +#define IXXAT_USBV2_CAN_ERROR_ACK        3
> +/* Bit error */
> +#define IXXAT_USBV2_CAN_ERROR_BIT        4
> +/* CRC error */
> +#define IXXAT_USBV2_CAN_ERROR_CRC        6
> +/* Other (unspecified) error */
> +#define IXXAT_USBV2_CAN_ERROR_OTHER      7
> +
> +/* Data overrun occurred */
> +#define IXXAT_USBV2_CAN_STATUS_OVRRUN    0x02
> +/* Error warning limit exceeded */
> +#define IXXAT_USBV2_CAN_STATUS_ERRLIM    0x04
> +/* Bus off status */
> +#define IXXAT_USBV2_CAN_STATUS_BUSOFF    0x08
> +
> +#define IXXAT_USBV2_CAN_DATA             0x00
> +#define IXXAT_USBV2_CAN_INFO             0x01
> +#define IXXAT_USBV2_CAN_ERROR            0x02
> +#define IXXAT_USBV2_CAN_STATUS           0x03
> +#define IXXAT_USBV2_CAN_WAKEUP           0x04
> +#define IXXAT_USBV2_CAN_TIMEOVR          0x05
> +#define IXXAT_USBV2_CAN_TIMERST          0x06
> +
> +#define IXXAT_USBV2_MSG_FLAGS_TYPE       0x000000FF
> +#define IXXAT_USBV2_MSG_FLAGS_SSM        0x00000100
> +#define IXXAT_USBV2_MSG_FLAGS_HPM        0x00000600
> +#define IXXAT_USBV2_MSG_FLAGS_RES        0x0000F800
> +#define IXXAT_USBV2_MSG_FLAGS_DLC        0x000F0000
> +#define IXXAT_USBV2_MSG_FLAGS_OVR        0x00100000
> +#define IXXAT_USBV2_MSG_FLAGS_SRR        0x00200000
> +#define IXXAT_USBV2_MSG_FLAGS_RTR        0x00400000
> +#define IXXAT_USBV2_MSG_FLAGS_EXT        0x00800000
> +#define IXXAT_USBV2_MSG_FLAGS_AFC        0xFF000000
> +
> +#define IXXAT_USBV2_BAL_CMD_CLASS        3
> +#define IXXAT_USBV2_BRD_CMD_CLASS        4
> +#define IXXAT_USBV2_BMG_CMD_CLASS        5
> +
> +#define IXXAT_USBV2_BRD_CMD_CAT          0
> +#define IXXAT_USBV2_CAN_CMD_CAT          1
> +
> +#define IXXAT_USBV2_VCI_CMD_CODE(Class, Function) \
> +	((u32) (((Class) << 8) | (Function)))
> +
> +#define IXXAT_USBV2_BRD_CMD_CODE(Category, Function) \
> +	IXXAT_USBV2_VCI_CMD_CODE(IXXAT_USBV2_BRD_CMD_CLASS, \
> +		((Category) << 5) | (Function))
> +
> +#define IXXAT_USBV2_BAL_CMD_CODE(Category, Function) \
> +	IXXAT_USBV2_VCI_CMD_CODE(IXXAT_USBV2_BAL_CMD_CLASS, \
> +		((Category) << 5) | (Function))
> +
> +#define IXXAT_USBV2_CAN_GET_CAPS_CMD \
> +	IXXAT_USBV2_BAL_CMD_CODE(IXXAT_USBV2_CAN_CMD_CAT, 0)
> +#define IXXAT_USBV2_CAN_INIT_CMD \
> +	IXXAT_USBV2_BAL_CMD_CODE(IXXAT_USBV2_CAN_CMD_CAT, 5)
> +#define IXXAT_USBV2_CAN_START_CMD \
> +	IXXAT_USBV2_BAL_CMD_CODE(IXXAT_USBV2_CAN_CMD_CAT, 6)
> +#define IXXAT_USBV2_CAN_STOP_CMD \
> +	IXXAT_USBV2_BAL_CMD_CODE(IXXAT_USBV2_CAN_CMD_CAT, 7)
> +#define IXXAT_USBV2_CAN_RESET_CMD \
> +	IXXAT_USBV2_BAL_CMD_CODE(IXXAT_USBV2_CAN_CMD_CAT, 8)
> +
> +#define IXXAT_USBV2_BRD_GET_FWINFO_CMD \
> +	IXXAT_USBV2_BRD_CMD_CODE(IXXAT_USBV2_BRD_CMD_CAT, 0)
> +#define IXXAT_USBV2_BRD_GET_DEVCAPS_CMD \
> +	IXXAT_USBV2_BRD_CMD_CODE(IXXAT_USBV2_BRD_CMD_CAT, 1)
> +#define IXXAT_USBV2_BRD_GET_DEVINFO_CMD \
> +	IXXAT_USBV2_BRD_CMD_CODE(IXXAT_USBV2_BRD_CMD_CAT, 2)
> +
> +struct ixx_usbv2_dal_req {
> +	u32 req_size;
> +	u16 req_port;
> +	u16 req_socket;
> +	u32 req_code;
> +} __packed;
> +
> +struct ixx_usbv2_dal_res {
> +	u32 res_size;
> +	u32 ret_size;
> +	u32 ret_code;
> +} __packed;
> +
> +struct ixx_usbv2_dev_caps_req {
> +	struct ixx_usbv2_dal_req dal_req;
> +} __packed;
> +
> +struct ixx_usbv2_dev_caps_res {
> +	struct ixx_usbv2_dal_res dal_res;
> +	struct ixx_dev_caps dev_caps;
> +} __packed;
> +
> +struct ixx_usbv2_ctrl_caps_req {
> +	struct ixx_usbv2_dal_req dal_req;
> +} __packed;
> +
> +struct ixx_usbv2_ctrl_caps_res {
> +	struct ixx_usbv2_dal_res dal_res;
> +	struct ixx_ctrl_caps ctrl_caps;
> +} __packed;
> +
> +struct ixx_usbv2_ctrl_init_req {
> +	struct ixx_usbv2_dal_req dal_req;
> +	u8 mode;
> +	u8 btr0;
> +	u8 btr1;
> +	u8 padding;
> +} __packed;
> +
> +struct ixx_usbv2_ctrl_init_res {
> +	struct ixx_usbv2_dal_res dal_res;
> +} __packed;
> +
> +struct ixx_usbv2_ctrl_start_req {
> +	struct ixx_usbv2_dal_req dal_req;
> +} __packed;
> +
> +struct ixx_usbv2_ctrl_start_res {
> +	struct ixx_usbv2_dal_res dal_res;
> +	u32 start_time;
> +} __packed;
> +
> +struct ixx_usbv2_ctrl_stop_req {
> +	struct ixx_usbv2_dal_req dal_req;
> +	u32 action;
> +} __packed;
> +
> +struct ixx_usbv2_ctrl_stop_res {
> +	struct ixx_usbv2_dal_res dal_res;
> +} __packed;
> +
> +struct ixx_usbv2_brd_get_fwinfo_req {
> +	struct ixx_usbv2_dal_req dal_req;
> +} __packed;
> +
> +struct ixx_usbv2_brd_get_fwinfo_res {
> +	struct ixx_usbv2_dal_res dal_res;
> +	struct ixx_intf_fw_info fwinfo;
> +} __packed;
> +
> +struct ixx_usbv2_brd_get_intf_info_req {
> +	struct ixx_usbv2_dal_req dal_req;
> +} __packed;
> +
> +struct ixx_usbv2_brd_get_intf_info_res {
> +	struct ixx_usbv2_dal_res dal_res;
> +	struct ixx_intf_info info;
> +} __packed;
> +
> +/*
> + * send usb-to-can v2 command synchronously
> + */
> +static int ixx_usbv2_send_cmd(struct usb_device *dev,
> +		struct ixx_usbv2_dal_req *dal_req)
> +{
> +	int err, i;
> +	u16 size, value;
> +	u8  request, 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) +
> +		sizeof(const struct ixx_usbv2_dal_res);
> +
> +	buf = kmalloc(size, GFP_KERNEL);
> +	if(!buf)
> +		return -ENOMEM;
> +	memcpy(buf, (u8 *)dal_req, size);
> +
> +
> +	for (i = 0; i < 10; ++i) {
> +		err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
> +			requesttype,
> +			value,
> +			0,
> +			buf,
> +			size,
> +			msecs_to_jiffies(50));
> +
> +		if (err < 0)
> +			msleep(20);
> +		else
> +			break;
> +	}
> +
> +	kfree(buf);
> +
> +	if (err < 0) {
> +		dev_err(&dev->dev, "sending command failure: %d\n", err);
> +		return err;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * receive usb-to-can v2 command synchronously
> + */
> +static int ixx_usbv2_rcv_cmd(struct usb_device *dev,
> +		struct ixx_usbv2_dal_res *dal_res, int value)
> +{
> +	int err, res_size, i, size_to_read;
> +	u8  request, requesttype;
> +	u8 *buf;
> +
> +	request      = 0xff;
> +	requesttype  = 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 < 10; ++i) {
> +		err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request,
> +				requesttype, value,
> +				0, buf + (u8) res_size,
> +				size_to_read - res_size, msecs_to_jiffies(50));
> +
> +		if (err < 0) {
> +			msleep(20);
> +			continue;
> +		}
> +
> +		res_size += err;
> +		if (res_size < size_to_read)
> +			msleep(20);
> +		else
> +			break;
> +	}
> +
> +	if (res_size != size_to_read)
> +		err = -EBADMSG;
> +
> +	if (err < 0) {
> +		dev_err(&dev->dev, "receiving command failure: %d\n", err);
> +		kfree(buf);
> +		return err;
> +	}
> +
> +	memcpy((u8 *)dal_res, buf, size_to_read);
> +	kfree(buf);
> +
> +	return err;
> +}
> +
> +static int ixx_usbv2_init_ctrl(struct ixx_usb_device *dev, u8 mode, u8 btr0,
> +		u8 btr1)
> +{
> +	int err = -ENODEV;
> +	u8 data[IXXAT_USBV2_CMD_BUFFER_SIZE] = { 0 };
> +	struct ixx_usbv2_ctrl_init_req *ctrl_init_req;
> +	struct ixx_usbv2_ctrl_init_res *ctrl_init_res;
> +	u32 req_size = sizeof(*ctrl_init_req);
> +
> +	ctrl_init_req = (struct ixx_usbv2_ctrl_init_req *) data;
> +	ctrl_init_res = (struct ixx_usbv2_ctrl_init_res *)(data + req_size);
> +
> +	ctrl_init_req->dal_req.req_size   = cpu_to_le32(req_size);
> +	ctrl_init_req->dal_req.req_code   =
> +		cpu_to_le32(IXXAT_USBV2_CAN_INIT_CMD);
> +	ctrl_init_req->dal_req.req_port   = cpu_to_le16(dev->ctrl_idx);
> +	ctrl_init_req->dal_req.req_socket = 0xffff;
> +	ctrl_init_req->mode               = mode;
> +	ctrl_init_req->btr0               = btr0;
> +	ctrl_init_req->btr1               = btr1;
> +
> +	ctrl_init_res->dal_res.res_size = cpu_to_le32(
> +		sizeof(*ctrl_init_res));
> +	ctrl_init_res->dal_res.ret_size = 0;
> +	ctrl_init_res->dal_res.ret_code = 0xffffffff;
> +
> +	err = ixx_usbv2_send_cmd(dev->udev, &ctrl_init_req->dal_req);
> +	if (err < 0)
> +		return err;
> +
> +	err = ixx_usbv2_rcv_cmd(dev->udev,
> +		&ctrl_init_res->dal_res,
> +		dev->ctrl_idx);
> +	if (err < 0)
> +		return err;
> +
> +	return le32_to_cpu(ctrl_init_res->dal_res.ret_code);
> +}
> +
> +static int ixx_usbv2_start_ctrl(struct ixx_usb_device *dev, u32 *time_ref)
> +{
> +	int err = -ENODEV;
> +	u8 data[IXXAT_USBV2_CMD_BUFFER_SIZE] = { 0 };
> +	struct ixx_usbv2_ctrl_start_req *ctrl_start_req;
> +	struct ixx_usbv2_ctrl_start_res *ctrl_start_res;
> +	u32 req_size = sizeof(*ctrl_start_req);
> +
> +	ctrl_start_req = (struct ixx_usbv2_ctrl_start_req *) data;
> +	ctrl_start_res = (struct ixx_usbv2_ctrl_start_res *)(data + req_size);
> +
> +	ctrl_start_req->dal_req.req_size   = cpu_to_le32(req_size);
> +	ctrl_start_req->dal_req.req_code   =
> +		cpu_to_le32(IXXAT_USBV2_CAN_START_CMD);
> +	ctrl_start_req->dal_req.req_port   = cpu_to_le16(dev->ctrl_idx);
> +	ctrl_start_req->dal_req.req_socket = 0xffff;
> +
> +	ctrl_start_res->dal_res.res_size = cpu_to_le32(
> +		sizeof(*ctrl_start_res));
> +	ctrl_start_res->dal_res.ret_size = 0;
> +	ctrl_start_res->dal_res.ret_code = 0xffffffff;
> +	ctrl_start_res->start_time       = 0;
> +
> +	err = ixx_usbv2_send_cmd(dev->udev, &ctrl_start_req->dal_req);
> +	if (err < 0)
> +		return err;
> +
> +	err = ixx_usbv2_rcv_cmd(dev->udev,
> +		&ctrl_start_res->dal_res,
> +		dev->ctrl_idx);
> +	if (err < 0)
> +		return err;
> +
> +	if (time_ref)
> +		*time_ref = le32_to_cpu(ctrl_start_res->start_time);
> +
> +	return le32_to_cpu(ctrl_start_res->dal_res.ret_code);
> +}
> +
> +static int ixx_usbv2_stop_ctrl(struct ixx_usb_device *dev)
> +{
> +	int err = -ENODEV;
> +	u8 data[IXXAT_USBV2_CMD_BUFFER_SIZE] = { 0 };
> +	struct ixx_usbv2_ctrl_stop_req *ctrl_stop_req;
> +	struct ixx_usbv2_ctrl_stop_res *ctrl_stop_res;
> +	u32 req_size = sizeof(struct ixx_usbv2_ctrl_stop_req);
> +
> +	ctrl_stop_req = (struct ixx_usbv2_ctrl_stop_req *) data;
> +	ctrl_stop_res = (struct ixx_usbv2_ctrl_stop_res *)(data + req_size);
> +
> +	ctrl_stop_req->dal_req.req_size   = cpu_to_le32(req_size);
> +	ctrl_stop_req->dal_req.req_code   =
> +		cpu_to_le32(IXXAT_USBV2_CAN_STOP_CMD);
> +	ctrl_stop_req->dal_req.req_port   = cpu_to_le16(dev->ctrl_idx);
> +	ctrl_stop_req->dal_req.req_socket = 0xffff;
> +	ctrl_stop_req->action             = cpu_to_le32(0x3);
> +
> +	ctrl_stop_res->dal_res.res_size =
> +		cpu_to_le32(sizeof(*ctrl_stop_res));
> +	ctrl_stop_res->dal_res.ret_size = 0;
> +	ctrl_stop_res->dal_res.ret_code = 0xffffffff;
> +
> +	err = ixx_usbv2_send_cmd(dev->udev, &ctrl_stop_req->dal_req);
> +	if (err < 0)
> +		return err;
> +
> +	err = ixx_usbv2_rcv_cmd(dev->udev,
> +		&ctrl_stop_res->dal_res,
> +		dev->ctrl_idx);
> +	if (err < 0)
> +		return err;
> +
> +	if (!le32_to_cpu(ctrl_stop_res->dal_res.ret_code))
> +		dev->can.state = CAN_STATE_STOPPED;
> +
> +	return le32_to_cpu(ctrl_stop_res->dal_res.ret_code);
> +}
> +
> +static int ixx_usbv2_reset_ctrl(struct ixx_usb_device *dev)
> +{
> +	int err = -ENODEV;
> +	u8 data[IXXAT_USBV2_CMD_BUFFER_SIZE] = { 0 };
> +	struct ixx_usbv2_dal_req *dal_req;
> +	struct ixx_usbv2_dal_res *dal_res;
> +	u32 req_size = sizeof(*dal_req);
> +
> +	dal_req = (struct ixx_usbv2_dal_req *) data;
> +	dal_res = (struct ixx_usbv2_dal_res *)(data + req_size);
> +
> +	dal_req->req_size   = cpu_to_le32(req_size);
> +	dal_req->req_code   = cpu_to_le32(IXXAT_USBV2_CAN_RESET_CMD);
> +	dal_req->req_port   = cpu_to_le16(dev->ctrl_idx);
> +	dal_req->req_socket = 0xffff;
> +
> +	dal_res->res_size = cpu_to_le32(sizeof(*dal_res));
> +	dal_res->ret_size = 0;
> +	dal_res->ret_code = 0xffffffff;
> +
> +	err = ixx_usbv2_send_cmd(dev->udev, dal_req);
> +	if (err < 0)
> +		return err;
> +
> +	err = ixx_usbv2_rcv_cmd(dev->udev, dal_res,
> +			dev->ctrl_idx);
> +	if (err < 0)
> +		return err;
> +
> +	return le32_to_cpu(dal_res->ret_code);
> +}
> +
> +static int ixx_usbv2_set_bittiming(struct ixx_usb_device *dev,
> +		struct can_bittiming *bt)
> +{
> +	u8 btr0 = 0, btr1 = 0, can_opmode = 0;
> +
> +	btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6);
> +	btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf)
> +			| (((bt->phase_seg2 - 1) & 0x7) << 4);
> +	if (dev->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
> +		btr1 |= 0x80;
> +
> +	can_opmode = IXXAT_USBV2_OPMODE_EXTENDED | IXXAT_USBV2_OPMODE_STANDARD;
> +
> +	if (dev->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
> +		can_opmode |= IXXAT_USBV2_OPMODE_ERRFRAME;
> +	if (dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
> +		can_opmode |= IXXAT_USBV2_OPMODE_LISTONLY;
> +
> +	dev->btr0 = btr0;
> +	dev->btr1 = btr1;
> +
> +	netdev_dbg(dev->netdev, "setting btr0=0x%08x btr1=0x%08x mode=0x%08x\n",
> +			btr0, btr1, can_opmode);
> +
> +	return ixx_usbv2_init_ctrl(dev, can_opmode, btr0, btr1);
> +}
> +
> +/*
> + * handle restart but in asynchronously way
> + */
> +static int ixx_usbv2_restart_task(void *user_data)
> +{
> +	u32 time_ref;
> +	struct ixx_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) {
> +				ixx_usbv2_stop_ctrl(dev);
> +				ixx_usbv2_start_ctrl(dev, &time_ref);
> +				dev->restart_flag = 0;
> +				dev->can.state = CAN_STATE_ERROR_ACTIVE;
> +			}
> +		} else
> +			msleep(20);
> +	}
> +	return 0;
> +}
> +
> +static int ixx_usbv2_handle_canmsg(struct ixx_usb_device *dev,
> +		struct ixx_can_msg *rx)
> +{
> +	struct net_device *netdev = dev->netdev;
> +	struct can_frame *can_frame;
> +	struct sk_buff *skb;
> +
> +	skb = alloc_can_skb(netdev, &can_frame);
> +	if (!skb)
> +		return -ENOMEM;
> +
> +	if (le32_to_cpu(rx->flags) & IXXAT_USBV2_MSG_FLAGS_OVR) {
> +		netdev->stats.rx_over_errors++;
> +		netdev->stats.rx_errors++;
> +	}
> +
> +	can_frame->can_id = le32_to_cpu(rx->msg_id);
> +	can_frame->can_dlc =
> +		(le32_to_cpu(rx->flags) &
> +		IXXAT_USBV2_MSG_FLAGS_DLC) >> 16;
> +
> +	if (le32_to_cpu(rx->flags) & IXXAT_USBV2_MSG_FLAGS_EXT)
> +		can_frame->can_id |= CAN_EFF_FLAG;
> +
> +	if (le32_to_cpu(rx->flags) & IXXAT_USBV2_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);
> +
> +	netif_rx(skb);
> +	netdev->stats.rx_packets++;
> +	netdev->stats.rx_bytes += can_frame->can_dlc;
> +
> +	return 0;
> +}
> +
> +static int ixx_usbv2_handle_error(struct ixx_usb_device *dev,
> +		struct ixx_can_msg *rx)
> +{
> +	struct net_device *netdev = dev->netdev;
> +	struct can_frame *can_frame;
> +	struct sk_buff *skb;
> +	u8 raw_status = 0;
> +
> +	/* nothing should be sent while in BUS_OFF state */
> +	if (dev->can.state == CAN_STATE_BUS_OFF)
> +		return 0;
> +
> +	raw_status = rx->data[0];
> +
> +	/* allocate an skb to store the error frame */
> +	skb = alloc_can_err_skb(netdev, &can_frame);
> +	if (!skb)
> +		return -ENOMEM;
> +
> +	switch (raw_status) {
> +	case IXXAT_USBV2_CAN_ERROR_ACK:
> +		can_frame->can_id |= CAN_ERR_ACK;
> +		netdev->stats.tx_errors++;
> +		break;
> +	case IXXAT_USBV2_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_USBV2_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_USBV2_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_USBV2_CAN_ERROR_STUFF:
> +		can_frame->can_id |= CAN_ERR_PROT;
> +		can_frame->data[2] |= CAN_ERR_PROT_STUFF;
> +		netdev->stats.rx_errors++;
> +		break;
> +	case IXXAT_USBV2_CAN_ERROR_OTHER:
> +		can_frame->can_id |= CAN_ERR_PROT;
> +		can_frame->data[2] |= CAN_ERR_PROT_UNSPEC;
> +		netdev->stats.rx_errors++;
> +		break;
> +	default:
> +		can_frame->can_id |= CAN_ERR_PROT;
> +		netdev->stats.rx_errors++;
> +	}
> +
> +	netif_rx(skb);
> +	netdev->stats.rx_packets++;
> +	netdev->stats.rx_bytes += can_frame->can_dlc;
> +
> +	dev->bec.txerr = le16_to_cpu(rx->data[1]);
> +	dev->bec.rxerr = le16_to_cpu(rx->data[3]);
> +
> +	return 0;
> +}
> +
> +static int ixx_usbv2_handle_status(struct ixx_usb_device *dev,
> +		struct ixx_can_msg *rx)
> +{
> +	struct net_device *netdev = dev->netdev;
> +	struct can_frame *can_frame;
> +	struct sk_buff *skb;
> +	u8 raw_status = 0;
> +	u32 new_state = 0;
> +
> +	raw_status = rx->data[0];
> +
> +	/* nothing should be sent while in BUS_OFF state */
> +	if (dev->can.state == CAN_STATE_BUS_OFF)
> +		return 0;
> +
> +	if (!raw_status) {
> +		/* no error bit (back to active state) */
> +		dev->can.state = CAN_STATE_ERROR_ACTIVE;
> +
> +		dev->bec.txerr = 0;
> +		dev->bec.rxerr = 0;
> +		return 0;
> +	}
> +
> +	/* allocate an skb to store the error frame */
> +	skb = alloc_can_err_skb(netdev, &can_frame);
> +	if (!skb)
> +		return -ENOMEM;
> +
> +	if (raw_status & IXXAT_USBV2_CAN_STATUS_BUSOFF) {
> +		can_frame->can_id |= CAN_ERR_BUSOFF;
> +		new_state = CAN_STATE_BUS_OFF;
> +		dev->can.can_stats.bus_off++;
> +		can_bus_off(netdev);
> +	} else {
> +		if (raw_status & IXXAT_USBV2_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_USBV2_CAN_STATUS_OVRRUN) {
> +			can_frame->can_id |= CAN_ERR_PROT;
> +			can_frame->data[2] |= CAN_ERR_PROT_OVERLOAD;
> +			netdev->stats.rx_over_errors++;
> +			netdev->stats.rx_errors++;
> +		}
> +
> +		if (!new_state) {
> +			new_state = CAN_STATE_ERROR_ACTIVE;
> +
> +			dev->bec.txerr = 0;
> +			dev->bec.rxerr = 0;
> +		}
> +	}
> +
> +	dev->can.state = new_state;
> +
> +	netif_rx(skb);
> +	netdev->stats.rx_packets++;
> +	netdev->stats.rx_bytes += can_frame->can_dlc;
> +
> +	return 0;
> +}
> +
> +/*
> + * callback for bulk IN urb
> + */
> +static int ixx_usbv2_decode_buf(struct ixx_usb_device *dev, struct urb *urb)
> +{
> +	struct net_device *netdev = dev->netdev;
> +	struct ixx_can_msg *can_msg;
> +	u32 msg_end;
> +	int err = 0;
> +	u32 read_size = 0;
> +	u8 msg_type;
> +	u8 *data;
> +
> +	data = urb->transfer_buffer;
> +
> +	/* loop reading all the records from the incoming message */
> +	msg_end = urb->actual_length;
> +	for (; msg_end > 0;) {
> +		can_msg = (struct ixx_can_msg *) &data[read_size];
> +
> +		if (!can_msg || !can_msg->size) {
> +			netdev_err(netdev, "got unsupported rec in usb msg:\n");
> +			err = -ENOTSUPP;
> +			break;
> +		}
> +
> +		/* check if the record goes out of current packet */
> +		if ((read_size + can_msg->size + 1) > urb->actual_length) {
> +			netdev_err(netdev,
> +			"got frag rec: should inc usb rx buf size\n");
> +			err = -EBADMSG;
> +			break;
> +		}
> +
> +		msg_type =
> +			(le32_to_cpu(can_msg->flags) &
> +			IXXAT_USBV2_MSG_FLAGS_TYPE);
> +
> +		switch (msg_type) {
> +
> +		case IXXAT_USBV2_CAN_DATA:
> +			err = ixx_usbv2_handle_canmsg(dev, can_msg);
> +			if (err < 0)
> +				goto fail;
> +			break;
> +
> +		case IXXAT_USBV2_CAN_STATUS:
> +			err = ixx_usbv2_handle_status(dev, can_msg);
> +			if (err < 0)
> +				goto fail;
> +			break;
> +
> +		case IXXAT_USBV2_CAN_ERROR:
> +			err = ixx_usbv2_handle_error(dev, can_msg);
> +			if (err < 0)
> +				goto fail;
> +			break;
> +
> +		case IXXAT_USBV2_CAN_TIMEOVR:
> +			ixxat_usb_get_ts_tv(dev, can_msg->time, NULL);
> +			break;
> +
> +		case IXXAT_USBV2_CAN_INFO:
> +		case IXXAT_USBV2_CAN_WAKEUP:
> +		case IXXAT_USBV2_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 (err)
> +		ixxat_dump_mem("received msg", urb->transfer_buffer,
> +				urb->actual_length);
> +
> +	return err;
> +}
> +
> +static int ixx_usbv2_encode_msg(struct ixx_usb_device *dev, struct sk_buff *skb,
> +		u8 *obuf, size_t *size)
> +{
> +	struct can_frame *cf = (struct can_frame *) skb->data;
> +	struct ixx_can_msg can_msg = { 0 };
> +
> +	if (cf->can_id & CAN_RTR_FLAG)
> +		can_msg.flags |= IXXAT_USBV2_MSG_FLAGS_RTR;
> +
> +	if (cf->can_id & CAN_EFF_FLAG) {
> +		can_msg.flags |= IXXAT_USBV2_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 (dev->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT)
> +		can_msg.flags |= IXXAT_USBV2_MSG_FLAGS_SSM;
> +
> +	can_msg.flags |= (cf->can_dlc << 16) & IXXAT_USBV2_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;
> +
> +	return 0;
> +}
> +
> +static int ixx_usbv2_start(struct ixx_usb_device *dev)
> +{
> +	int err;
> +	u32 time_ref = 0;
> +	u8 can_opmode = IXXAT_USBV2_OPMODE_EXTENDED
> +			| IXXAT_USBV2_OPMODE_STANDARD;
> +
> +	if (dev->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
> +		can_opmode |= IXXAT_USBV2_OPMODE_ERRFRAME;
> +	if (dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
> +		can_opmode |= IXXAT_USBV2_OPMODE_LISTONLY;
> +
> +	err = ixx_usbv2_init_ctrl(dev, can_opmode, dev->btr0, dev->btr1);
> +	if (err)
> +		return err;
> +
> +	/* opening first device: */
> +	if (dev->ctrl_opened_count == 0) {
> +		err = ixx_usbv2_start_ctrl(dev, &time_ref);
> +		if (err)
> +			return err;
> +
> +		ixxat_usb_set_ts_now(dev, time_ref);
> +	}
> +
> +	dev->ctrl_opened_count++;
> +
> +	dev->bec.txerr = 0;
> +	dev->bec.rxerr = 0;
> +
> +	return err;
> +}
> +
> +/*
> + * stop interface
> + * (last chance before set bus off)
> + */
> +static int ixx_usbv2_stop(struct ixx_usb_device *dev)
> +{
> +	int err;
> +
> +	if (dev->ctrl_opened_count == 1) {
> +		err = ixx_usbv2_stop_ctrl(dev);
> +		if (err)
> +			return err;
> +	}
> +
> +	/* turn off ts msgs for that interface if no other dev opened */
> +//	if (pdev->usb_if->dev_opened_count == 1)
> +//		ixx_usbv2_set_ts(dev, 0);
> +	dev->ctrl_opened_count--;
> +
> +	return 0;
> +}
> +
> +/*
> + * called when probing to initialize a device object.
> + */
> +static int ixx_usbv2_init(struct ixx_usb_device *dev)
> +{
> +	dev->restart_task = kthread_run(&ixx_usbv2_restart_task, dev,
> +		"restart_thread");
> +	if (!dev->restart_task)
> +		return -ENOBUFS;
> +
> +	return 0;
> +}
> +
> +static void ixx_usbv2_exit(struct ixx_usb_device *dev)
> +{
> +	ixx_usbv2_reset_ctrl(dev);
> +
> +	dev->must_quit = 1;
> +	dev->restart_flag = 1;
> +	wake_up_interruptible(&dev->wait_queue);
> +	if (dev->restart_task)
> +		kthread_stop(dev->restart_task);
> +}
> +
> +/*
> + * probe function for new IXXAT USB-to-CAN V2 interface
> + */
> +static int ixx_usbv2_probe(struct usb_interface *intf)
> +{
> +	struct usb_host_interface *if_desc;
> +	int i;
> +
> +	if_desc = intf->altsetting;
> +
> +	/* check interface endpoint addresses */
> +	for (i = 0; i < if_desc->desc.bNumEndpoints; i++) {
> +		struct usb_endpoint_descriptor *ep = &if_desc->endpoint[i].desc;
> +
> +		/*
> +		 * below is the list of valid ep addreses. Any other ep address
> +		 * is considered as not-CAN interface address => no dev created
> +		 */
> +		switch (ep->bEndpointAddress) {
> +		case IXXAT_USBV2_EP_MSGOUT_0:
> +		case IXXAT_USBV2_EP_MSGOUT_1:
> +		case IXXAT_USBV2_EP_MSGOUT_2:
> +		case IXXAT_USBV2_EP_MSGOUT_3:
> +		case IXXAT_USBV2_EP_MSGOUT_4:
> +		case IXXAT_USBV2_EP_MSGIN_0:
> +		case IXXAT_USBV2_EP_MSGIN_1:
> +		case IXXAT_USBV2_EP_MSGIN_2:
> +		case IXXAT_USBV2_EP_MSGIN_3:
> +		case IXXAT_USBV2_EP_MSGIN_4:
> +
> +			break;
> +		default:
> +			return -ENODEV;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int ixx_usbv2_get_dev_caps(struct usb_device *dev,
> +		struct ixx_dev_caps *dev_caps)
> +{
> +	int err = -ENODEV, i;
> +	u8 data[IXXAT_USBV2_CMD_BUFFER_SIZE] = { 0 };
> +	struct ixx_usbv2_dev_caps_req *dev_caps_req;
> +	struct ixx_usbv2_dev_caps_res *dev_caps_res;
> +	u32 req_size = sizeof(*dev_caps_req);
> +
> +	dev_caps_req = (struct ixx_usbv2_dev_caps_req *) data;
> +	dev_caps_res = (struct ixx_usbv2_dev_caps_res *)(data + req_size);
> +
> +	dev_caps_req->dal_req.req_size   = cpu_to_le32(req_size);
> +	dev_caps_req->dal_req.req_code   =
> +		cpu_to_le32(IXXAT_USBV2_BRD_GET_DEVCAPS_CMD);
> +	dev_caps_req->dal_req.req_port   = 0xffff;
> +	dev_caps_req->dal_req.req_socket = 0xffff;
> +
> +	dev_caps_res->dal_res.res_size =
> +		cpu_to_le32(sizeof(*dev_caps_res));
> +	dev_caps_res->dal_res.ret_size = 0;
> +	dev_caps_res->dal_res.ret_code = 0xffffffff;
> +
> +	err = ixx_usbv2_send_cmd(dev, &dev_caps_req->dal_req);
> +	if (err < 0)
> +		return err;
> +
> +	err = ixx_usbv2_rcv_cmd(dev, &dev_caps_res->dal_res,
> +			0xffff);
> +	if (err < 0)
> +		return err;
> +
> +	dev_caps->bus_ctrl_count =
> +		le16_to_cpu(dev_caps_res->dev_caps.bus_ctrl_count);
> +	for (i = 0; i < dev_caps->bus_ctrl_count; ++i)
> +		dev_caps->bus_ctrl_types[i] =
> +			le16_to_cpu(dev_caps_res->dev_caps.bus_ctrl_types[i]);
> +
> +	return 0;
> +}
> +
> +static int ixx_usbv2_get_ctrl_caps(struct usb_device *dev,
> +		struct ixx_ctrl_caps *ctrl_caps, int index)
> +{
> +	int err = -ENODEV;
> +	u8 data[IXXAT_USBV2_CMD_BUFFER_SIZE] = { 0 };
> +	struct ixx_usbv2_ctrl_caps_req *ctrl_caps_req;
> +	struct ixx_usbv2_ctrl_caps_res *ctrl_caps_res;
> +	u32 req_size = sizeof(*ctrl_caps_req);
> +
> +	ctrl_caps_req = (struct ixx_usbv2_ctrl_caps_req *) data;
> +	ctrl_caps_res = (struct ixx_usbv2_ctrl_caps_res *)(data + req_size);
> +
> +	ctrl_caps_req->dal_req.req_size   = cpu_to_le32(req_size);
> +	ctrl_caps_req->dal_req.req_code   =
> +		cpu_to_le32(IXXAT_USBV2_CAN_GET_CAPS_CMD);
> +	ctrl_caps_req->dal_req.req_port   = cpu_to_le16(index);
> +	ctrl_caps_req->dal_req.req_socket = 0xffff;
> +
> +	ctrl_caps_res->dal_res.res_size =
> +		cpu_to_le32(sizeof(*ctrl_caps_res));
> +	ctrl_caps_res->dal_res.ret_size = 0;
> +	ctrl_caps_res->dal_res.ret_code = 0xffffffff;
> +
> +	err = ixx_usbv2_send_cmd(dev, &ctrl_caps_req->dal_req);
> +	if (err < 0)
> +		return err;
> +
> +	err = ixx_usbv2_rcv_cmd(dev, &ctrl_caps_res->dal_res,
> +			index);
> +	if (err < 0)
> +		return err;
> +
> +	ctrl_caps->bus_coupling = le16_to_cpu(
> +			ctrl_caps_res->ctrl_caps.bus_coupling);
> +	ctrl_caps->clock_freq =
> +		le32_to_cpu(ctrl_caps_res->ctrl_caps.clock_freq);
> +	ctrl_caps->cms_divisor = le32_to_cpu(
> +		ctrl_caps_res->ctrl_caps.cms_divisor);
> +	ctrl_caps->cms_max_ticks = le32_to_cpu(
> +		ctrl_caps_res->ctrl_caps.cms_max_ticks);
> +	ctrl_caps->ctrl_type = le16_to_cpu(ctrl_caps_res->ctrl_caps.ctrl_type);
> +	ctrl_caps->dtx_divisor = le32_to_cpu(
> +		ctrl_caps_res->ctrl_caps.dtx_divisor);
> +	ctrl_caps->dtx_max_ticks = le32_to_cpu(
> +		ctrl_caps_res->ctrl_caps.dtx_max_ticks);
> +	ctrl_caps->features = le32_to_cpu(ctrl_caps_res->ctrl_caps.features);
> +	ctrl_caps->tsc_divisor = le32_to_cpu(
> +		ctrl_caps_res->ctrl_caps.tsc_divisor);
> +
> +	return 0;
> +}
> +
> +static int ixx_usbv2_get_fwinfo(struct ixx_usb_device *dev,
> +		struct ixx_intf_fw_info *fwinfo)
> +{
> +	int err = -ENODEV;
> +	u8 data[IXXAT_USBV2_CMD_BUFFER_SIZE] = { 0 };
> +	struct ixx_usbv2_brd_get_fwinfo_req *fw_info_req;
> +	struct ixx_usbv2_brd_get_fwinfo_res *fw_info_res;
> +	u32 req_size = sizeof(*fw_info_req);
> +
> +	fw_info_req = (struct ixx_usbv2_brd_get_fwinfo_req *) data;
> +	fw_info_res = (struct ixx_usbv2_brd_get_fwinfo_res *)(data + req_size);
> +
> +	fw_info_req->dal_req.req_size   = cpu_to_le32(req_size);
> +	fw_info_req->dal_req.req_code   =
> +		cpu_to_le32(IXXAT_USBV2_BRD_GET_FWINFO_CMD);
> +	fw_info_req->dal_req.req_port   = 0xffff;
> +	fw_info_req->dal_req.req_socket = 0xffff;
> +
> +	fw_info_res->dal_res.res_size =
> +		cpu_to_le32(sizeof(*fw_info_res));
> +	fw_info_res->dal_res.ret_size = 0;
> +	fw_info_res->dal_res.ret_code = 0xffffffff;
> +
> +	err = ixx_usbv2_send_cmd(dev->udev, &fw_info_req->dal_req);
> +	if (err < 0)
> +		return err;
> +
> +	err = ixx_usbv2_rcv_cmd(dev->udev,
> +		&fw_info_res->dal_res, 0xffff);
> +	if (err < 0)
> +		return err;
> +
> +	if (fwinfo) {
> +		fwinfo->build_version =
> +			le16_to_cpu(fw_info_res->fwinfo.build_version);
> +		fwinfo->firmware_type =
> +			le32_to_cpu(fw_info_res->fwinfo.firmware_type);
> +		fwinfo->major_version =
> +			le16_to_cpu(fw_info_res->fwinfo.major_version);
> +		fwinfo->minor_version =
> +			le16_to_cpu(fw_info_res->fwinfo.minor_version);
> +		fwinfo->reserved =
> +			le16_to_cpu(fw_info_res->fwinfo.reserved);
> +	}
> +
> +	return le32_to_cpu(fw_info_res->dal_res.ret_code);
> +}
> +
> +static int ixx_usbv2_get_dev_info(struct ixx_usb_device *dev,
> +		struct ixx_intf_info *dev_info)
> +{
> +	int err = -ENODEV;
> +	u8 data[IXXAT_USBV2_CMD_BUFFER_SIZE] = { 0 };
> +	struct ixx_usbv2_brd_get_intf_info_req *dev_info_req;
> +	struct ixx_usbv2_brd_get_intf_info_res *dev_info_res;
> +	u32 req_size = sizeof(*dev_info_req);
> +
> +	dev_info_req = (struct ixx_usbv2_brd_get_intf_info_req *) data;
> +	dev_info_res =
> +		(struct ixx_usbv2_brd_get_intf_info_res *)(data + req_size);
> +
> +	dev_info_req->dal_req.req_size   = cpu_to_le32(req_size);
> +	dev_info_req->dal_req.req_code   =
> +		cpu_to_le32(IXXAT_USBV2_BRD_GET_DEVINFO_CMD);
> +	dev_info_req->dal_req.req_port   = 0xffff;
> +	dev_info_req->dal_req.req_socket = 0xffff;
> +
> +	dev_info_res->dal_res.res_size =
> +		cpu_to_le32(sizeof(*dev_info_res));
> +	dev_info_res->dal_res.ret_size = 0;
> +	dev_info_res->dal_res.ret_code = 0xffffffff;
> +
> +	err = ixx_usbv2_send_cmd(dev->udev, &dev_info_req->dal_req);
> +	if (err < 0)
> +		return err;
> +
> +	err = ixx_usbv2_rcv_cmd(dev->udev,
> +		&dev_info_res->dal_res, 0xffff);
> +	if (err < 0)
> +		return err;
> +
> +	if (dev_info) {
> +		memcpy(dev_info->device_id, &dev_info_res->info.device_id,
> +			sizeof(dev_info_res->info.device_id));
> +		memcpy(dev_info->device_name, &dev_info_res->info.device_name,
> +			sizeof(dev_info_res->info.device_name));
> +		dev_info->device_fpga_version =
> +			le16_to_cpu(dev_info_res->info.device_fpga_version);
> +		dev_info->device_version =
> +			le32_to_cpu(dev_info_res->info.device_version);
> +	}
> +
> +	return le32_to_cpu(dev_info_res->dal_res.ret_code);
> +}
> +
> +/*
> + * describe the describes the USB-to-CAN V2 compact adapter
> + */
> +struct ixx_usb_adapter usb_to_can_v2_compact = {
> +	.name = "USB-to-CAN V2 compact",
> +	.device_id = USB_TO_CAN_V2_COMPACT_PRODUCT_ID,
> +	.clock = {
> +		.freq = SJA1000_CRYSTAL_HZ,
> +	},
> +	.bittiming_const = {
> +		.name = "ixxat_usb",
> +		.tseg1_min = 1,
> +		.tseg1_max = 16,
> +		.tseg2_min = 1,
> +		.tseg2_max = 8,
> +		.sjw_max = 4,
> +		.brp_min = 1,
> +		.brp_max = 64,
> +		.brp_inc = 1,
> +	},
> +
> +	.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
> +		CAN_CTRLMODE_BERR_REPORTING |
> +		CAN_CTRLMODE_LOOPBACK |
> +		CAN_CTRLMODE_LISTENONLY,
> +
> +	/* size of device private data */
> +	.sizeof_dev_private = sizeof(struct ixx_usb_device),
> +
> +	/* give here messages in/out endpoints */
> +	.ep_msg_in = {  IXXAT_USBV2_EP_MSGIN_0, IXXAT_USBV2_EP_MSGIN_1,
> +		IXXAT_USBV2_EP_MSGIN_2, IXXAT_USBV2_EP_MSGIN_3,
> +		IXXAT_USBV2_EP_MSGIN_4 },
> +	.ep_msg_out = { IXXAT_USBV2_EP_MSGOUT_0, IXXAT_USBV2_EP_MSGOUT_1,
> +		IXXAT_USBV2_EP_MSGOUT_2, IXXAT_USBV2_EP_MSGOUT_3,
> +		IXXAT_USBV2_EP_MSGOUT_4 },
> +
> +	/* size of rx/tx usb buffers */
> +	.rx_buffer_size = IXXAT_USBV2_RX_BUFFER_SIZE,
> +	.tx_buffer_size = IXXAT_USBV2_TX_BUFFER_SIZE,
> +
> +	/* device callbacks */
> +	.intf_probe = ixx_usbv2_probe,
> +	.dev_get_dev_caps = ixx_usbv2_get_dev_caps,
> +	.dev_get_ctrl_caps = ixx_usbv2_get_ctrl_caps,
> +	.dev_init = ixx_usbv2_init,
> +	.dev_exit = ixx_usbv2_exit,
> +	.dev_set_bittiming = ixx_usbv2_set_bittiming,
> +	.intf_get_info = ixx_usbv2_get_dev_info,
> +	.intf_get_fw_info = ixx_usbv2_get_fwinfo,
> +	.dev_decode_buf = ixx_usbv2_decode_buf,
> +	.dev_encode_msg = ixx_usbv2_encode_msg,
> +	.dev_start = ixx_usbv2_start,
> +	.dev_stop = ixx_usbv2_stop,
> +};
> +
> +/*
> + * describes the USB-to-CAN V2 automotive adapter
> + */
> +struct ixx_usb_adapter usb_to_can_v2_automotive = {
> +	.name = "USB-to-CAN V2 automotive",
> +	.device_id = USB_TO_CAN_V2_AUTOMOTIVE_PRODUCT_ID,
> +	.clock = {
> +		.freq = SJA1000_CRYSTAL_HZ,
> +	},
> +	.bittiming_const = {
> +		.name = "ixxat_usb",
> +		.tseg1_min = 1,
> +		.tseg1_max = 16,
> +		.tseg2_min = 1,
> +		.tseg2_max = 8,
> +		.sjw_max = 4,
> +		.brp_min = 1,
> +		.brp_max = 64,
> +		.brp_inc = 1,
> +	},
> +
> +	.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
> +		CAN_CTRLMODE_BERR_REPORTING |
> +		CAN_CTRLMODE_LOOPBACK |
> +		CAN_CTRLMODE_LISTENONLY,
> +
> +	/* size of device private data */
> +	.sizeof_dev_private = sizeof(struct ixx_usb_device),
> +
> +	/* give here messages in/out endpoints */
> +	.ep_msg_in = {  IXXAT_USBV2_EP_MSGIN_0, IXXAT_USBV2_EP_MSGIN_1,
> +		IXXAT_USBV2_EP_MSGIN_2, IXXAT_USBV2_EP_MSGIN_3,
> +		IXXAT_USBV2_EP_MSGIN_4 },
> +	.ep_msg_out = { IXXAT_USBV2_EP_MSGOUT_0, IXXAT_USBV2_EP_MSGOUT_1,
> +		IXXAT_USBV2_EP_MSGOUT_2, IXXAT_USBV2_EP_MSGOUT_3,
> +		IXXAT_USBV2_EP_MSGOUT_4 },
> +
> +	/* size of rx/tx usb buffers */
> +	.rx_buffer_size = IXXAT_USBV2_RX_BUFFER_SIZE,
> +	.tx_buffer_size = IXXAT_USBV2_TX_BUFFER_SIZE,
> +
> +	/* device callbacks */
> +	.intf_probe = ixx_usbv2_probe,
> +	.dev_get_dev_caps = ixx_usbv2_get_dev_caps,
> +	.dev_get_ctrl_caps = ixx_usbv2_get_ctrl_caps,
> +	.dev_init = ixx_usbv2_init,
> +	.dev_exit = ixx_usbv2_exit,
> +	.dev_set_bittiming = ixx_usbv2_set_bittiming,
> +	.intf_get_info = ixx_usbv2_get_dev_info,
> +	.intf_get_fw_info = ixx_usbv2_get_fwinfo,
> +	.dev_decode_buf = ixx_usbv2_decode_buf,
> +	.dev_encode_msg = ixx_usbv2_encode_msg,
> +	.dev_start = ixx_usbv2_start,
> +	.dev_stop = ixx_usbv2_stop,
> +};
> +
> +/*
> + * describes the USB-to-CAN V2 embedded adapter
> + */
> +struct ixx_usb_adapter usb_to_can_v2_embedded = {
> +	.name = "USB-to-CAN V2 embedded",
> +	.device_id = USB_TO_CAN_V2_EMBEDDED_PRODUCT_ID,
> +	.clock = {
> +		.freq = SJA1000_CRYSTAL_HZ,
> +	},
> +	.bittiming_const = {
> +		.name = "ixxat_usb",
> +		.tseg1_min = 1,
> +		.tseg1_max = 16,
> +		.tseg2_min = 1,
> +		.tseg2_max = 8,
> +		.sjw_max = 4,
> +		.brp_min = 1,
> +		.brp_max = 64,
> +		.brp_inc = 1,
> +	},
> +
> +	.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
> +		CAN_CTRLMODE_BERR_REPORTING |
> +		CAN_CTRLMODE_LOOPBACK |
> +		CAN_CTRLMODE_LISTENONLY,
> +
> +	/* size of device private data */
> +	.sizeof_dev_private = sizeof(struct ixx_usb_device),
> +
> +	/* give here messages in/out endpoints */
> +	.ep_msg_in = {  IXXAT_USBV2_EP_MSGIN_0, IXXAT_USBV2_EP_MSGIN_1,
> +		IXXAT_USBV2_EP_MSGIN_2, IXXAT_USBV2_EP_MSGIN_3,
> +		IXXAT_USBV2_EP_MSGIN_4 },
> +	.ep_msg_out = { IXXAT_USBV2_EP_MSGOUT_0, IXXAT_USBV2_EP_MSGOUT_1,
> +		IXXAT_USBV2_EP_MSGOUT_2, IXXAT_USBV2_EP_MSGOUT_3,
> +		IXXAT_USBV2_EP_MSGOUT_4 },
> +
> +	/* size of rx/tx usb buffers */
> +	.rx_buffer_size = IXXAT_USBV2_RX_BUFFER_SIZE,
> +	.tx_buffer_size = IXXAT_USBV2_TX_BUFFER_SIZE,
> +
> +	/* device callbacks */
> +	.intf_probe = ixx_usbv2_probe,
> +	.dev_get_dev_caps = ixx_usbv2_get_dev_caps,
> +	.dev_get_ctrl_caps = ixx_usbv2_get_ctrl_caps,
> +	.dev_init = ixx_usbv2_init,
> +	.dev_exit = ixx_usbv2_exit,
> +	.dev_set_bittiming = ixx_usbv2_set_bittiming,
> +	.intf_get_info = ixx_usbv2_get_dev_info,
> +	.intf_get_fw_info = ixx_usbv2_get_fwinfo,
> +	.dev_decode_buf = ixx_usbv2_decode_buf,
> +	.dev_encode_msg = ixx_usbv2_encode_msg,
> +	.dev_start = ixx_usbv2_start,
> +	.dev_stop = ixx_usbv2_stop,
> +};
> +
> +/*
> + * describes the USB-to-CAN V2 professional adapter
> + */
> +struct ixx_usb_adapter usb_to_can_v2_professional = {
> +	.name = "USB-to-CAN V2 professional",
> +	.device_id = USB_TO_CAN_V2_PROFESSIONAL_PRODUCT_ID,
> +	.clock = {
> +		.freq = SJA1000_CRYSTAL_HZ,
> +	},
> +	.bittiming_const = {
> +		.name = "ixxat_usb",
> +		.tseg1_min = 1,
> +		.tseg1_max = 16,
> +		.tseg2_min = 1,
> +		.tseg2_max = 8,
> +		.sjw_max = 4,
> +		.brp_min = 1,
> +		.brp_max = 64,
> +		.brp_inc = 1,
> +	},
> +
> +	.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
> +		CAN_CTRLMODE_BERR_REPORTING |
> +		CAN_CTRLMODE_LOOPBACK |
> +		CAN_CTRLMODE_LISTENONLY,
> +
> +	/* size of device private data */
> +	.sizeof_dev_private = sizeof(struct ixx_usb_device),
> +
> +	/* give here messages in/out endpoints */
> +	.ep_msg_in = {  IXXAT_USBV2_EP_MSGIN_0, IXXAT_USBV2_EP_MSGIN_1,
> +		IXXAT_USBV2_EP_MSGIN_2, IXXAT_USBV2_EP_MSGIN_3,
> +		IXXAT_USBV2_EP_MSGIN_4 },
> +	.ep_msg_out = { IXXAT_USBV2_EP_MSGOUT_0, IXXAT_USBV2_EP_MSGOUT_1,
> +		IXXAT_USBV2_EP_MSGOUT_2, IXXAT_USBV2_EP_MSGOUT_3,
> +		IXXAT_USBV2_EP_MSGOUT_4 },
> +
> +	/* size of rx/tx usb buffers */
> +	.rx_buffer_size = IXXAT_USBV2_RX_BUFFER_SIZE,
> +	.tx_buffer_size = IXXAT_USBV2_TX_BUFFER_SIZE,
> +
> +	/* device callbacks */
> +	.intf_probe = ixx_usbv2_probe,
> +	.dev_get_dev_caps = ixx_usbv2_get_dev_caps,
> +	.dev_get_ctrl_caps = ixx_usbv2_get_ctrl_caps,
> +	.dev_init = ixx_usbv2_init,
> +	.dev_exit = ixx_usbv2_exit,
> +	.dev_set_bittiming = ixx_usbv2_set_bittiming,
> +	.intf_get_info = ixx_usbv2_get_dev_info,
> +	.intf_get_fw_info = ixx_usbv2_get_fwinfo,
> +	.dev_decode_buf = ixx_usbv2_decode_buf,
> +	.dev_encode_msg = ixx_usbv2_encode_msg,
> +	.dev_start = ixx_usbv2_start,
> +	.dev_stop = ixx_usbv2_stop,
> +};
> +
> +/*
> + * describes the USB-to-CAN V2 low speed adapter
> + */
> +struct ixx_usb_adapter usb_to_can_v2_low_speed = {
> +	.name = "USB-to-CAN V2 low speed",
> +	.device_id = USB_TO_CAN_V2_LOW_SPEED_PRODUCT_ID,
> +	.clock = {
> +		.freq = SJA1000_CRYSTAL_HZ,
> +	},
> +	.bittiming_const = {
> +		.name = "ixxat_usb",
> +		.tseg1_min = 1,
> +		.tseg1_max = 16,
> +		.tseg2_min = 1,
> +		.tseg2_max = 8,
> +		.sjw_max = 4,
> +		.brp_min = 1,
> +		.brp_max = 64,
> +		.brp_inc = 1,
> +	},
> +
> +	.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
> +		CAN_CTRLMODE_BERR_REPORTING |
> +		CAN_CTRLMODE_LOOPBACK |
> +		CAN_CTRLMODE_LISTENONLY,
> +
> +	/* size of device private data */
> +	.sizeof_dev_private = sizeof(struct ixx_usb_device),
> +
> +	/* give here messages in/out endpoints */
> +	.ep_msg_in = {  IXXAT_USBV2_EP_MSGIN_0, IXXAT_USBV2_EP_MSGIN_1,
> +		IXXAT_USBV2_EP_MSGIN_2, IXXAT_USBV2_EP_MSGIN_3,
> +		IXXAT_USBV2_EP_MSGIN_4 },
> +	.ep_msg_out = { IXXAT_USBV2_EP_MSGOUT_0, IXXAT_USBV2_EP_MSGOUT_1,
> +		IXXAT_USBV2_EP_MSGOUT_2, IXXAT_USBV2_EP_MSGOUT_3,
> +		IXXAT_USBV2_EP_MSGOUT_4 },
> +
> +	/* size of rx/tx usb buffers */
> +	.rx_buffer_size = IXXAT_USBV2_RX_BUFFER_SIZE,
> +	.tx_buffer_size = IXXAT_USBV2_TX_BUFFER_SIZE,
> +
> +	/* device callbacks */
> +	.intf_probe = ixx_usbv2_probe,
> +	.dev_get_dev_caps = ixx_usbv2_get_dev_caps,
> +	.dev_get_ctrl_caps = ixx_usbv2_get_ctrl_caps,
> +	.dev_init = ixx_usbv2_init,
> +	.dev_exit = ixx_usbv2_exit,
> +	.dev_set_bittiming = ixx_usbv2_set_bittiming,
> +	.intf_get_info = ixx_usbv2_get_dev_info,
> +	.intf_get_fw_info = ixx_usbv2_get_fwinfo,
> +	.dev_decode_buf = ixx_usbv2_decode_buf,
> +	.dev_encode_msg = ixx_usbv2_encode_msg,
> +	.dev_start = ixx_usbv2_start,
> +	.dev_stop = ixx_usbv2_stop,
> +};
> +
> +/*
> + * describes the USB-to-CAN V2 extended adapter
> + */
> +struct ixx_usb_adapter usb_to_can_v2_extended = {
> +	.name = "USB-to-CAN V2 extended",
> +	.device_id = USB_TO_CAN_V2_EXTENDED_PRODUCT_ID,
> +	.clock = {
> +		.freq = SJA1000_CRYSTAL_HZ,
> +	},
> +	.bittiming_const = {
> +		.name = "ixxat_usb",
> +		.tseg1_min = 1,
> +		.tseg1_max = 16,
> +		.tseg2_min = 1,
> +		.tseg2_max = 8,
> +		.sjw_max = 4,
> +		.brp_min = 1,
> +		.brp_max = 64,
> +		.brp_inc = 1,
> +	},
> +
> +	.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
> +		CAN_CTRLMODE_BERR_REPORTING |
> +		CAN_CTRLMODE_LOOPBACK |
> +		CAN_CTRLMODE_LISTENONLY,
> +
> +	/* size of device private data */
> +	.sizeof_dev_private = sizeof(struct ixx_usb_device),
> +
> +	/* give here messages in/out endpoints */
> +	.ep_msg_in = {  IXXAT_USBV2_EP_MSGIN_0, IXXAT_USBV2_EP_MSGIN_1,
> +		IXXAT_USBV2_EP_MSGIN_2, IXXAT_USBV2_EP_MSGIN_3,
> +		IXXAT_USBV2_EP_MSGIN_4 },
> +	.ep_msg_out = { IXXAT_USBV2_EP_MSGOUT_0, IXXAT_USBV2_EP_MSGOUT_1,
> +		IXXAT_USBV2_EP_MSGOUT_2, IXXAT_USBV2_EP_MSGOUT_3,
> +		IXXAT_USBV2_EP_MSGOUT_4 },
> +
> +	/* size of rx/tx usb buffers */
> +	.rx_buffer_size = IXXAT_USBV2_RX_BUFFER_SIZE,
> +	.tx_buffer_size = IXXAT_USBV2_TX_BUFFER_SIZE,
> +
> +	/* device callbacks */
> +	.intf_probe = ixx_usbv2_probe,
> +	.dev_get_dev_caps = ixx_usbv2_get_dev_caps,
> +	.dev_get_ctrl_caps = ixx_usbv2_get_ctrl_caps,
> +	.dev_init = ixx_usbv2_init,
> +	.dev_exit = ixx_usbv2_exit,
> +	.dev_set_bittiming = ixx_usbv2_set_bittiming,
> +	.intf_get_info = ixx_usbv2_get_dev_info,
> +	.intf_get_fw_info = ixx_usbv2_get_fwinfo,
> +	.dev_decode_buf = ixx_usbv2_decode_buf,
> +	.dev_encode_msg = ixx_usbv2_encode_msg,
> +	.dev_start = ixx_usbv2_start,
> +	.dev_stop = ixx_usbv2_stop,
> +};
>

Comments

Shrirang Bagul June 6, 2018, 1:33 a.m. UTC | #1
Hi Kleber,

On Tue, 2018-06-05 at 10:29 -0700, Kleber Souza wrote:
> Hi Shrirang,
> 
> On 05/31/18 22:47, Shrirang Bagul wrote:
> > BugLink: http://bugs.launchpad.net/bugs/1774563
> > 
> > This driver from IXXAT adds support for SocketCAN over USB.
> > (https://www.ixxat.com)
> > 
> > Signed-off-by: Shrirang Bagul <shrirang.bagul@canonical.com>
> > ---
> >  ubuntu/Kconfig              |    3 +-
> >  ubuntu/Makefile             |    5 +-
> >  ubuntu/ixxat/Kconfig        |    8 +
> >  ubuntu/ixxat/Makefile       |    4 +
> >  ubuntu/ixxat/ixx_usb_core.c |  923 +++++++++++++++++++
> >  ubuntu/ixxat/ixx_usb_core.h |  289 ++++++
> >  ubuntu/ixxat/ixx_usb_fd.c   | 1673 +++++++++++++++++++++++++++++++++++
> >  ubuntu/ixxat/ixx_usb_v2.c   | 1450 ++++++++++++++++++++++++++++++
> >  8 files changed, 4353 insertions(+), 2 deletions(-)
> >  create mode 100644 ubuntu/ixxat/Kconfig
> >  create mode 100644 ubuntu/ixxat/Makefile
> >  create mode 100644 ubuntu/ixxat/ixx_usb_core.c
> >  create mode 100644 ubuntu/ixxat/ixx_usb_core.h
> >  create mode 100644 ubuntu/ixxat/ixx_usb_fd.c
> >  create mode 100644 ubuntu/ixxat/ixx_usb_v2.c
> > 
> > diff --git a/ubuntu/Kconfig b/ubuntu/Kconfig
> > index bc2fb5530593..a3ad3d87ce53 100644
> > --- a/ubuntu/Kconfig
> > +++ b/ubuntu/Kconfig
> > @@ -30,10 +30,11 @@ source "ubuntu/opennsl/Kconfig"
> >  ##
> >  ##
> >  ##
> > +source "ubuntu/bnxt/Kconfig"
> >  ##
> >  ##
> >  ##
> > -source "ubuntu/bnxt/Kconfig"
> > +source "ubuntu/ixxat/Kconfig"
> >  ##
> >  ##
> >  ##
> 
> Please add the 3 lines with "##" above the new entry so the patch
> doesn't need to touch the exiting entries and avoid merge conflicts by
> that. E.g.:
> 
> --- a/ubuntu/Kconfig
> +++ b/ubuntu/Kconfig
> @@ -37,6 +37,10 @@ source "ubuntu/bnxt/Kconfig"
>  ##
>  ##
>  ##
> +source "ubuntu/ixxat/Kconfig
> +##
> +##
> +##
>  ##
>  ##
>  ##
understood.
> 
> 
> > diff --git a/ubuntu/Makefile b/ubuntu/Makefile
> > index 85e1c900735c..62dd1e1b7b46 100644
> > --- a/ubuntu/Makefile
> > +++ b/ubuntu/Makefile
> > @@ -44,11 +44,14 @@ obj-$(CONFIG_OPENNSL) += opennsl/
> >  ##
> >  ##
> >  ##
> > -##
> >  obj-$(CONFIG_BNXT_BPO) += bnxt/
> >  ##
> >  ##
> >  ##
> > +obj-$(CONFIG_CAN_HMS_USB) += ixxat/
> > +##
> > +##
> > +##
> >  ##
> >  ##
> >  ##
> 
> Same here.
Sent v2 to with suggested changes.

Thanks,
Shrirang
> 
> 
> Thanks,
> Kleber
> 
> > diff --git a/ubuntu/ixxat/Kconfig b/ubuntu/ixxat/Kconfig
> > new file mode 100644
> > index 000000000000..63ff0d054d9e
> > --- /dev/null
> > +++ b/ubuntu/ixxat/Kconfig
> > @@ -0,0 +1,8 @@
> > +config CAN_HMS_USB
> > +	tristate "HMS USB SocketCAN"
> > +	depends on X86 || X86_64
> > +	depends on USB && CAN_DEV
> > +	---help---
> > +	This driver is from IXXAT and supports SocketCAN over USB.
> > +	(https://www.ixxat.com)
> > +
> > diff --git a/ubuntu/ixxat/Makefile b/ubuntu/ixxat/Makefile
> > new file mode 100644
> > index 000000000000..d4ee67ebdd24
> > --- /dev/null
> > +++ b/ubuntu/ixxat/Makefile
> > @@ -0,0 +1,4 @@
> > +mod-name += ixx_usb
> > +obj-m += ixx_usb.o
> > +ixx_usb-objs := ixx_usb_v2.o ixx_usb_fd.o ixx_usb_core.o
> > +
> > diff --git a/ubuntu/ixxat/ixx_usb_core.c b/ubuntu/ixxat/ixx_usb_core.c
> > new file mode 100644
> > index 000000000000..d258b6e46453
> > --- /dev/null
> > +++ b/ubuntu/ixxat/ixx_usb_core.c
> > @@ -0,0 +1,923 @@
> > +/*
> > + * CAN driver for IXXAT USB-to-CAN V2 adapters
> > + *
> > + * Copyright (C) 2003-2014 Michael Hengler IXXAT Automation GmbH
> > + *
> > + * Based on code originally by pcan_usb_core
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms of the GNU General Public License as published
> > + * by the Free Software Foundation; version 2 of the License.
> > + *
> > + * This program is distributed in the hope that it will be useful, but
> > + * WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> > + * General Public License for more details.
> > + */
> > +#include <linux/init.h>
> > +#include <linux/signal.h>
> > +#include <linux/slab.h>
> > +#include <linux/module.h>
> > +#include <linux/netdevice.h>
> > +#include <linux/usb.h>
> > +#include <linux/errno.h>
> > +#include <linux/skbuff.h>
> > +#include <linux/types.h>
> > +#include <linux/can.h>
> > +#include <linux/can/dev.h>
> > +#include <linux/can/error.h>
> > +#include <asm-generic/errno.h>
> > +
> > +#include "ixx_usb_core.h"
> > +
> > +MODULE_AUTHOR("Michael Hengler <mhengler@ixxat.de>");
> > +MODULE_DESCRIPTION("CAN driver for IXXAT USB-to-CAN V2 adapters");
> > +MODULE_LICENSE("GPL v2");
> > +
> > +#define IXXAT_USB_DRIVER_NAME	"ixx_usb"
> > +
> > +#define IXXAT_USB_BUS_CAN	1 // CAN
> > +#define IXXAT_USB_BUS_TYPE(BusCtrl)  (u8)  ( ((BusCtrl) >> 8) & 0x00FF )
> > +#define IXXAT_USB_VENDOR_ID	0x08d8
> > +
> > +#define IXXAT_USB_STATE_CONNECTED 0x00000001
> > +#define IXXAT_USB_STATE_STARTED   0x00000002
> > +
> > +
> > +/* Table of devices that work with this driver */
> > +static struct usb_device_id ixxat_usb_table[] = {
> > +	{USB_DEVICE(IXXAT_USB_VENDOR_ID, USB_TO_CAN_V2_COMPACT_PRODUCT_ID)},
> > +	{USB_DEVICE(IXXAT_USB_VENDOR_ID, USB_TO_CAN_V2_EMBEDDED_PRODUCT_ID)},
> > +	{USB_DEVICE(IXXAT_USB_VENDOR_ID, USB_TO_CAN_V2_PROFESSIONAL_PRODUCT_ID)},
> > +	{USB_DEVICE(IXXAT_USB_VENDOR_ID, USB_TO_CAN_V2_AUTOMOTIVE_PRODUCT_ID)},
> > +	{USB_DEVICE(IXXAT_USB_VENDOR_ID, USB_TO_LIN_V2_PRODUCT_ID)},
> > +	{USB_DEVICE(IXXAT_USB_VENDOR_ID, USB_TO_KLINE_V2_PRODUCT_ID)},
> > +#ifdef CANFD_CAPABLE
> > +	{USB_DEVICE(IXXAT_USB_VENDOR_ID, USB_TO_CAN_FD_AUTOMOTIVE_PRODUCT_ID)},
> > +	{USB_DEVICE(IXXAT_USB_VENDOR_ID, USB_TO_CAN_FD_COMPACT_PRODUCT_ID)},
> > +	{USB_DEVICE(IXXAT_USB_VENDOR_ID, USB_TO_CAN_FD_PROFESSIONAL_PRODUCT_ID)},
> > +	{USB_DEVICE(IXXAT_USB_VENDOR_ID, USB_TO_CAN_FD_PCIE_MINI_PRODUCT_ID)},
> > +	{USB_DEVICE(IXXAT_USB_VENDOR_ID, USB_TO_CAR_ID)},
> > +	{USB_DEVICE(IXXAT_USB_VENDOR_ID, DELL_EDGE_GW3002_PRODUCT_ID)},
> > +#endif
> > +	{} /* Terminating entry */
> > +};
> > +
> > +MODULE_DEVICE_TABLE(usb, ixxat_usb_table);
> > +
> > +/* List of supported IXX-USB adapters (NULL terminated list) */
> > +static struct ixx_usb_adapter *ixx_usb_adapters_list[] = {
> > +	&usb_to_can_v2_compact,
> > +	&usb_to_can_v2_automotive,
> > +	&usb_to_can_v2_embedded,
> > +	&usb_to_can_v2_professional,
> > +	&usb_to_can_v2_low_speed,
> > +	&usb_to_can_v2_extended,
> > +#ifdef CANFD_CAPABLE
> > +	&usb_to_can_fd_automotive,
> > +	&usb_to_can_fd_compact,
> > +	&usb_to_can_fd_professional,
> > +	&usb_to_can_fd_pcie_mini,
> > +	&usb_to_car,
> > +	&dell_edge_gw3002,
> > +#endif
> > +	NULL,
> > +};
> > +
> > +/*
> > + * dump memory
> > + */
> > +#define DUMP_WIDTH    16
> > +void ixxat_dump_mem(char *prompt, void *p, int l)
> > +{
> > +	pr_info("%s dumping %s (%d bytes):\n",
> > +			IXXAT_USB_DRIVER_NAME, prompt ? prompt : "memory", l);
> > +	print_hex_dump(KERN_INFO, IXXAT_USB_DRIVER_NAME " ", DUMP_PREFIX_NONE,
> > +			DUMP_WIDTH, 1, p, l, false);
> > +}
> > +
> > +static void ixxat_usb_add_us(struct timeval *tv, u64 delta_us)
> > +{
> > +	/* number of s. to add to final time */
> > +	u32 delta_s = div_u64(delta_us, 1000000);
> > +
> > +	delta_us -= delta_s * 1000000;
> > +
> > +	tv->tv_usec += delta_us;
> > +	if (tv->tv_usec >= 1000000) {
> > +		tv->tv_usec -= 1000000;
> > +		delta_s++;
> > +	}
> > +	tv->tv_sec += delta_s;
> > +}
> > +
> > +void ixxat_usb_get_ts_tv(struct ixx_usb_device *dev, u32 ts, ktime_t *k_time)
> > +{
> > +	struct timeval tv = dev->time_ref.tv_host_0;
> > +
> > +	if (ts < dev->time_ref.ts_dev_last) {
> > +		ixxat_usb_update_ts_now(dev, ts);
> > +	}
> > +
> > +	dev->time_ref.ts_dev_last = ts;
> > +	ixxat_usb_add_us(&tv, ts - dev->time_ref.ts_dev_0);
> > +
> > +	if(k_time)
> > +		*k_time = timeval_to_ktime(tv);
> > +}
> > +
> > +void ixxat_usb_update_ts_now(struct ixx_usb_device *dev, u32 hw_time_base)
> > +{
> > +	u64 timebase;
> > +
> > +	timebase = (u64)0x00000000FFFFFFFF - (u64)dev->time_ref.ts_dev_0 + (u64)hw_time_base;
> > +
> > +	ixxat_usb_add_us(&dev->time_ref.tv_host_0, timebase);
> > +
> > +	dev->time_ref.ts_dev_0 = hw_time_base;
> > +}
> > +
> > +void ixxat_usb_set_ts_now(struct ixx_usb_device *dev, u32 hw_time_base)
> > +{
> > +	dev->time_ref.ts_dev_0 = hw_time_base;
> > +	do_gettimeofday(&dev->time_ref.tv_host_0);
> > +	dev->time_ref.ts_dev_last = hw_time_base;
> > +}
> > +
> > +/*
> > + * callback for bulk Rx urb
> > + */
> > +static void ixxat_usb_read_bulk_callback(struct urb *urb)
> > +{
> > +	struct ixx_usb_device *dev = urb->context;
> > +	struct net_device *netdev;
> > +	int err;
> > +
> > +	netdev = dev->netdev;
> > +
> > +	if (!netif_device_present(netdev))
> > +		return;
> > +
> > +	/* check reception status */
> > +	switch (urb->status) {
> > +		case 0:
> > +			/* success */
> > +			break;
> > +
> > +		case -EILSEQ:
> > +		case -ENOENT:
> > +		case -ECONNRESET:
> > +		case -ESHUTDOWN:
> > +			return;
> > +
> > +		default:
> > +			if (net_ratelimit())
> > +				netdev_err(netdev, "Rx urb aborted (%d)\n",
> > +						urb->status);
> > +			goto resubmit_urb;
> > +	}
> > +
> > +	/* protect from any incoming empty msgs */
> > +	if ((urb->actual_length > 0) && (dev->adapter->dev_decode_buf)) {
> > +		/* handle these kinds of msgs only if _start callback called */
> > +		if (dev->state & IXXAT_USB_STATE_STARTED)
> > +			err = dev->adapter->dev_decode_buf(dev, urb);
> > +	}
> > +
> > +resubmit_urb: usb_fill_bulk_urb(urb, dev->udev,
> > +			      usb_rcvbulkpipe(dev->udev, dev->ep_msg_in),
> > +			      urb->transfer_buffer, dev->adapter->rx_buffer_size,
> > +			      ixxat_usb_read_bulk_callback, dev);
> > +
> > +	      usb_anchor_urb(urb, &dev->rx_submitted);
> > +	      err = usb_submit_urb(urb, GFP_ATOMIC);
> > +	      if (!err)
> > +		      return;
> > +
> > +	      usb_unanchor_urb(urb);
> > +
> > +	      if (err == -ENODEV)
> > +		      netif_device_detach(netdev);
> > +	      else
> > +		      netdev_err(netdev, "failed resubmitting read bulk urb: %d\n",
> > +				      err);
> > +}
> > +
> > +/*
> > + * callback for bulk Tx urb
> > + */
> > +static void ixxat_usb_write_bulk_callback(struct urb *urb)
> > +{
> > +	struct ixx_tx_urb_context *context = urb->context;
> > +	struct ixx_usb_device *dev;
> > +	struct net_device *netdev;
> > +
> > +	BUG_ON(!context);
> > +
> > +	dev = context->dev;
> > +	netdev = dev->netdev;
> > +
> > +	atomic_dec(&dev->active_tx_urbs);
> > +
> > +	if (!netif_device_present(netdev))
> > +		return;
> > +
> > +	/* check tx status */
> > +	switch (urb->status) {
> > +		case 0:
> > +			/* transmission complete */
> > +			netdev->stats.tx_packets += context->count;
> > +			netdev->stats.tx_bytes += context->dlc;
> > +
> > +			/* prevent tx timeout */
> > +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
> > +			netif_trans_update(netdev);
> > +#else
> > +			netdev->trans_start = jiffies;
> > +#endif
> > +			break;
> > +
> > +
> > +		case -EPROTO:
> > +		case -ENOENT:
> > +		case -ECONNRESET:
> > +		case -ESHUTDOWN:
> > +			break;
> > +		default:
> > +			if (net_ratelimit())
> > +				netdev_err(netdev, "Tx urb aborted (%d)\n",
> > +						urb->status);
> > +			break;
> > +	}
> > +
> > +	/* should always release echo skb and corresponding context */
> > +	can_get_echo_skb(netdev, context->echo_index);
> > +	context->echo_index = IXXAT_USB_MAX_TX_URBS;
> > +
> > +	/* do wakeup tx queue in case of success only */
> > +	if (!urb->status)
> > +		netif_wake_queue(netdev);
> > +}
> > +
> > +/*
> > + * called by netdev to send one skb on the CAN interface.
> > + */
> > +static netdev_tx_t ixxat_usb_ndo_start_xmit(struct sk_buff *skb,
> > +		struct net_device *netdev)
> > +{
> > +	struct ixx_usb_device *dev = netdev_priv(netdev);
> > +	struct ixx_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, err;
> > +	size_t size = dev->adapter->tx_buffer_size;
> > +
> > +	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 (!context) {
> > +		/* should not occur except during restart */
> > +		return NETDEV_TX_BUSY;
> > +	}
> > +
> > +	urb = context->urb;
> > +	obuf = urb->transfer_buffer;
> > +
> > +	err = dev->adapter->dev_encode_msg(dev, skb, obuf, &size);
> > +
> > +	context->echo_index = i;
> > +	context->dlc = cf->len;
> > +	context->count = 1;
> > +
> > +	urb->transfer_buffer_length = size;
> > +
> > +	if (err) {
> > +		if (net_ratelimit())
> > +			netdev_err(netdev, "packet dropped\n");
> > +		dev_kfree_skb(skb);
> > +		stats->tx_dropped++;
> > +		return NETDEV_TX_OK;
> > +	}
> > +
> > +	usb_anchor_urb(urb, &dev->tx_submitted);
> > +
> > +	can_put_echo_skb(skb, netdev, context->echo_index);
> > +
> > +	atomic_inc(&dev->active_tx_urbs);
> > +
> > +	err = usb_submit_urb(urb, GFP_ATOMIC);
> > +	if (err) {
> > +		can_free_echo_skb(netdev, context->echo_index);
> > +
> > +		usb_unanchor_urb(urb);
> > +
> > +		/* this context is not used in fact */
> > +		context->echo_index = IXXAT_USB_MAX_TX_URBS;
> > +
> > +		atomic_dec(&dev->active_tx_urbs);
> > +
> > +		switch (err) {
> > +			case -ENODEV:
> > +				netif_device_detach(netdev);
> > +				break;
> > +			case -ENOENT:
> > +				/* cable unplugged */
> > +				stats->tx_dropped++;
> > +				break;
> > +			default:
> > +				stats->tx_dropped++;
> > +				netdev_warn(netdev, "tx urb submitting failed err=%d\n",
> > +						err);
> > +		}
> > +	} else {
> > +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
> > +		netif_trans_update(netdev);
> > +#else
> > +		netdev->trans_start = jiffies;
> > +#endif
> > +
> > +		/* slow down tx path */
> > +		if (atomic_read(&dev->active_tx_urbs) >= IXXAT_USB_MAX_TX_URBS)
> > +			netif_stop_queue(netdev);
> > +	}
> > +
> > +	return NETDEV_TX_OK;
> > +}
> > +
> > +/*
> > + * start the CAN interface.
> > + * Rx and Tx urbs are allocated here. Rx urbs are submitted here.
> > + */
> > +static int ixxat_usb_start(struct ixx_usb_device *dev)
> > +{
> > +	struct net_device *netdev = dev->netdev;
> > +	int err, i;
> > +
> > +	for (i = 0; i < IXXAT_USB_MAX_RX_URBS; i++) {
> > +		struct urb *urb;
> > +		u8 *buf;
> > +
> > +		/* create a URB, and a buffer for it, to receive usb messages */
> > +		urb = usb_alloc_urb(0, GFP_KERNEL);
> > +		if (!urb) {
> > +			netdev_err(netdev, "No memory left for URBs\n");
> > +			err = -ENOMEM;
> > +			break;
> > +		}
> > +
> > +		buf = kmalloc(dev->adapter->rx_buffer_size, GFP_KERNEL);
> > +		if (!buf) {
> > +			usb_free_urb(urb);
> > +			err = -ENOMEM;
> > +			break;
> > +		}
> > +
> > +		usb_fill_bulk_urb(urb, dev->udev,
> > +				usb_rcvbulkpipe(dev->udev, dev->ep_msg_in), buf,
> > +				dev->adapter->rx_buffer_size,
> > +				ixxat_usb_read_bulk_callback, dev);
> > +
> > +		/* ask last usb_free_urb() to also kfree() transfer_buffer */
> > +		urb->transfer_flags |= URB_FREE_BUFFER;
> > +		usb_anchor_urb(urb, &dev->rx_submitted);
> > +
> > +		err = usb_submit_urb(urb, GFP_KERNEL);
> > +		if (err) {
> > +			if (err == -ENODEV)
> > +				netif_device_detach(dev->netdev);
> > +
> > +			usb_unanchor_urb(urb);
> > +			kfree(buf);
> > +			usb_free_urb(urb);
> > +			break;
> > +		}
> > +
> > +		/* drop reference, USB core will take care of freeing it */
> > +		usb_free_urb(urb);
> > +	}
> > +
> > +	/* did we submit any URBs? Warn if we was not able to submit all urbs */
> > +	if (i < IXXAT_USB_MAX_RX_URBS) {
> > +		if (i == 0) {
> > +			netdev_err(netdev, "couldn't setup any rx URB\n");
> > +			return err;
> > +		}
> > +
> > +		netdev_warn(netdev, "rx performance may be slow\n");
> > +	}
> > +
> > +	/* pre-alloc tx buffers and corresponding urbs */
> > +	for (i = 0; i < IXXAT_USB_MAX_TX_URBS; i++) {
> > +		struct ixx_tx_urb_context *context;
> > +		struct urb *urb;
> > +		u8 *buf;
> > +
> > +		/* create a URB and a buffer for it, to transmit usb messages */
> > +		urb = usb_alloc_urb(0, GFP_KERNEL);
> > +		if (!urb) {
> > +			netdev_err(netdev, "No memory left for URBs\n");
> > +			err = -ENOMEM;
> > +			break;
> > +		}
> > +
> > +		buf = kmalloc(dev->adapter->tx_buffer_size, GFP_KERNEL);
> > +		if (!buf) {
> > +			usb_free_urb(urb);
> > +			err = -ENOMEM;
> > +			break;
> > +		}
> > +
> > +		context = dev->tx_contexts + i;
> > +		context->dev = dev;
> > +		context->urb = urb;
> > +
> > +		usb_fill_bulk_urb(urb, dev->udev,
> > +				usb_sndbulkpipe(dev->udev, dev->ep_msg_out),
> > +				buf, dev->adapter->tx_buffer_size,
> > +				ixxat_usb_write_bulk_callback, context);
> > +
> > +		/* ask last usb_free_urb() to also kfree() transfer_buffer */
> > +		urb->transfer_flags |= URB_FREE_BUFFER;
> > +	}
> > +
> > +	/* warn if we were not able to allocate enough tx contexts */
> > +	if (i < IXXAT_USB_MAX_TX_URBS) {
> > +		if (i == 0) {
> > +			netdev_err(netdev, "couldn't setup any tx URB\n");
> > +			goto err_tx;
> > +		}
> > +
> > +		netdev_warn(netdev, "tx performance may be slow\n");
> > +	}
> > +
> > +	if (dev->adapter->dev_start) {
> > +		err = dev->adapter->dev_start(dev);
> > +		if (err)
> > +			goto err_adapter;
> > +	}
> > +
> > +	dev->state |= IXXAT_USB_STATE_STARTED;
> > +
> > +	dev->can.state = CAN_STATE_ERROR_ACTIVE;
> > +
> > +	return 0;
> > +
> > +err_adapter: if (err == -ENODEV)
> > +		     netif_device_detach(dev->netdev);
> > +
> > +	     netdev_warn(netdev, "couldn't submit control: %d\n", err);
> > +
> > +	     for (i = 0; i < IXXAT_USB_MAX_TX_URBS; i++) {
> > +		     usb_free_urb(dev->tx_contexts[i].urb);
> > +		     dev->tx_contexts[i].urb = NULL;
> > +	     }
> > +err_tx: usb_kill_anchored_urbs(&dev->rx_submitted);
> > +
> > +	return err;
> > +}
> > +
> > +/*
> > + * called by netdev to open the corresponding CAN interface.
> > + */
> > +static int ixxat_usb_ndo_open(struct net_device *netdev)
> > +{
> > +	struct ixx_usb_device *dev = netdev_priv(netdev);
> > +	int err;
> > +
> > +	/* common open */
> > +	err = open_candev(netdev);
> > +	if (err)
> > +		return err;
> > +
> > +	/* finally start device */
> > +	err = ixxat_usb_start(dev);
> > +	if (err) {
> > +		netdev_err(netdev, "couldn't start device: %d\n", err);
> > +		close_candev(netdev);
> > +		return err;
> > +	}
> > +
> > +	netif_start_queue(netdev);
> > +
> > +	return 0;
> > +}
> > +
> > +/*
> > + * unlink in-flight Rx and Tx urbs and free their memory.
> > + */
> > +static void ixxat_usb_unlink_all_urbs(struct ixx_usb_device *dev)
> > +{
> > +	int i;
> > +
> > +	/* free all Rx (submitted) urbs */
> > +	usb_kill_anchored_urbs(&dev->rx_submitted);
> > +
> > +	/* free unsubmitted Tx urbs first */
> > +	for (i = 0; i < IXXAT_USB_MAX_TX_URBS; i++) {
> > +		struct urb *urb = dev->tx_contexts[i].urb;
> > +
> > +		if (!urb
> > +				|| dev->tx_contexts[i].echo_index
> > +				!= IXXAT_USB_MAX_TX_URBS) {
> > +			/*
> > +			 * this urb is already released or always submitted,
> > +			 * let usb core free by itself
> > +			 */
> > +			continue;
> > +		}
> > +
> > +		usb_free_urb(urb);
> > +		dev->tx_contexts[i].urb = NULL;
> > +	}
> > +
> > +	/* then free all submitted Tx urbs */
> > +	usb_kill_anchored_urbs(&dev->tx_submitted);
> > +	atomic_set(&dev->active_tx_urbs, 0);
> > +}
> > +
> > +/*
> > + * called by netdev to close the corresponding CAN interface.
> > + */
> > +static int ixxat_usb_ndo_stop(struct net_device *netdev)
> > +{
> > +	struct ixx_usb_device *dev = netdev_priv(netdev);
> > +
> > +	dev->state &= ~IXXAT_USB_STATE_STARTED;
> > +	netif_stop_queue(netdev);
> > +
> > +	/* unlink all pending urbs and free used memory */
> > +	ixxat_usb_unlink_all_urbs(dev);
> > +
> > +	if (dev->adapter->dev_stop)
> > +		dev->adapter->dev_stop(dev);
> > +
> > +	close_candev(netdev);
> > +
> > +	dev->can.state = CAN_STATE_STOPPED;
> > +
> > +	return 0;
> > +}
> > +
> > +/*
> > + * handle end of waiting for the device to reset
> > + */
> > +void ixxat_usb_restart_complete(struct ixx_usb_device *dev)
> > +{
> > +	/* finally MUST update can state */
> > +	dev->can.state = CAN_STATE_ERROR_ACTIVE;
> > +
> > +	/* netdev queue can be awaken now */
> > +	netif_wake_queue(dev->netdev);
> > +}
> > +
> > +void ixxat_usb_async_complete(struct urb *urb)
> > +{
> > +	kfree(urb->transfer_buffer);
> > +	usb_free_urb(urb);
> > +}
> > +
> > +/*
> > + * candev callback used to change CAN mode.
> > + * Warning: this is called from a timer context!
> > + */
> > +static int ixxat_usb_set_mode(struct net_device *netdev, enum can_mode mode)
> > +{
> > +	struct ixx_usb_device *dev = netdev_priv(netdev);
> > +	int err = 0;
> > +
> > +	switch (mode) {
> > +		case CAN_MODE_START:
> > +			dev->restart_flag = 1;
> > +			wake_up_interruptible(&dev->wait_queue);
> > +			break;
> > +		default:
> > +			return -EOPNOTSUPP;
> > +	}
> > +
> > +	return err;
> > +}
> > +
> > +/*
> > + * candev callback used to set device bitrate.
> > + */
> > +static int ixxat_usb_set_bittiming(struct net_device *netdev)
> > +{
> > +	struct ixx_usb_device* dev = (struct ixx_usb_device*) netdev_priv(
> > +			netdev);
> > +	struct can_bittiming *bt = &dev->can.bittiming;
> > +
> > +	if (dev->adapter->dev_set_bittiming) {
> > +		int err = dev->adapter->dev_set_bittiming(dev, bt);
> > +
> > +		if (err)
> > +			netdev_info(netdev, "couldn't set bitrate (err %d)\n",
> > +					err);
> > +		return err;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +/*
> > + * candev callback used to set error counters.
> > + */
> > +static int ixxat_usb_get_berr_counter(const struct net_device *netdev,
> > +		struct can_berr_counter *bec)
> > +{
> > +	struct ixx_usb_device* dev = (struct ixx_usb_device*) netdev_priv(
> > +			netdev);
> > +
> > +	*bec = dev->bec;
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct net_device_ops ixx_usb_netdev_ops = { .ndo_open =
> > +	ixxat_usb_ndo_open, .ndo_stop = ixxat_usb_ndo_stop,
> > +	.ndo_start_xmit = ixxat_usb_ndo_start_xmit,
> > +#ifdef CANFD_CAPABLE
> > +	.ndo_change_mtu = can_change_mtu,
> > +#endif
> > +};
> > +
> > +/*
> > + * create one device which is attached to CAN controller #ctrl_idx of the
> > + * usb adapter.
> > + */
> > +static int ixxat_usb_create_dev(struct ixx_usb_adapter *ixx_usb_adapter,
> > +		struct usb_interface *intf, int ctrl_idx)
> > +{
> > +	struct usb_device *usb_dev = interface_to_usbdev(intf);
> > +	int sizeof_candev = ixx_usb_adapter->sizeof_dev_private;
> > +	struct ixx_usb_device *dev;
> > +	struct net_device *netdev;
> > +	int i, err = 0, ep_off = 0;
> > +	u16 tmp16;
> > +
> > +	if (sizeof_candev < sizeof(struct ixx_usb_device))
> > +		sizeof_candev = sizeof(struct ixx_usb_device);
> > +
> > +	netdev = alloc_candev(sizeof_candev, IXXAT_USB_MAX_TX_URBS);
> > +	if (!netdev) {
> > +		dev_err(&intf->dev, "%s: couldn't alloc candev\n",
> > +				IXXAT_USB_DRIVER_NAME);
> > +		return -ENOMEM;
> > +	}
> > +
> > +	dev = netdev_priv(netdev);
> > +
> > +	dev->transmit_ptr = 0;
> > +	dev->transmit_dlc = 0;
> > +	dev->transmit_count = 0;
> > +
> > +	dev->restart_flag = 0;
> > +	dev->restart_task = 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 = ixx_usb_adapter;
> > +	dev->ctrl_idx = ctrl_idx;
> > +	dev->state = IXXAT_USB_STATE_CONNECTED;
> > +
> > +	ep_off = ixx_usb_adapter->has_bgi_ep ? 1 : 0;
> > +
> > +	/* Add +1 because of the bgi endpoint */
> > +	dev->ep_msg_in = ixx_usb_adapter->ep_msg_in[ctrl_idx+ep_off];
> > +	dev->ep_msg_out = ixx_usb_adapter->ep_msg_out[ctrl_idx+ep_off];
> > +
> > +	dev->can.clock = ixx_usb_adapter->clock;
> > +	dev->can.bittiming_const = &ixx_usb_adapter->bittiming_const;
> > +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 3)
> > +	dev->can.data_bittiming_const = &ixx_usb_adapter->data_bittiming_const;
> > +#endif
> > +
> > +	dev->can.do_set_bittiming = ixxat_usb_set_bittiming;
> > +	dev->can.do_set_mode = ixxat_usb_set_mode;
> > +	dev->can.do_get_berr_counter = ixxat_usb_get_berr_counter;
> > +
> > +	dev->can.ctrlmode_supported = ixx_usb_adapter->ctrlmode_supported;
> > +
> > +	netdev->netdev_ops = &ixx_usb_netdev_ops;
> > +
> > +	netdev->flags |= IFF_ECHO; /* we support local echo */
> > +
> > +	init_usb_anchor(&dev->rx_submitted);
> > +
> > +	init_usb_anchor(&dev->tx_submitted);
> > +	atomic_set(&dev->active_tx_urbs, 0);
> > +
> > +	for (i = 0; i < IXXAT_USB_MAX_TX_URBS; i++)
> > +		dev->tx_contexts[i].echo_index = IXXAT_USB_MAX_TX_URBS;
> > +
> > +	dev->prev_siblings = usb_get_intfdata(intf);
> > +	usb_set_intfdata(intf, dev);
> > +
> > +	SET_NETDEV_DEV(netdev, &intf->dev);
> > +
> > +	err = register_candev(netdev);
> > +	if (err) {
> > +		dev_err(&intf->dev, "couldn't register CAN device: %d\n", err);
> > +		goto lbl_set_intf_data;
> > +	}
> > +
> > +	if (dev->prev_siblings)
> > +		(dev->prev_siblings)->next_siblings = dev;
> > +
> > +	/* keep hw revision into the netdevice */
> > +	tmp16 = le16_to_cpu(usb_dev->descriptor.bcdDevice);
> > +	dev->device_rev = tmp16 >> 8;
> > +
> > +	if (dev->adapter->dev_init) {
> > +		err = dev->adapter->dev_init(dev);
> > +		if (err)
> > +			goto lbl_set_intf_data;
> > +	}
> > +
> > +	if (dev->adapter->intf_get_info)
> > +		dev->adapter->intf_get_info(dev,
> > +				&dev->dev_info);
> > +
> > +	netdev_info(netdev, "attached to %s channel %u (device %s)\n",
> > +			dev->dev_info.device_name, ctrl_idx,
> > +			dev->dev_info.device_id);
> > +
> > +	return 0;
> > +
> > +lbl_set_intf_data: usb_set_intfdata(intf, dev->prev_siblings);
> > +		   free_candev(netdev);
> > +
> > +		   return err;
> > +}
> > +
> > +/*
> > + * called by the usb core when the device is unplugged from the system
> > + */
> > +static void ixxat_usb_disconnect(struct usb_interface *intf)
> > +{
> > +	struct ixx_usb_device *dev;
> > +	struct ixx_usb_device *dev_prev_siblings;
> > +
> > +	/* unregister as many netdev devices as siblings */
> > +	for (dev = usb_get_intfdata(intf); dev; dev = dev_prev_siblings) {
> > +		struct net_device *netdev = dev->netdev;
> > +		char name[IFNAMSIZ];
> > +
> > +		dev_prev_siblings = dev->prev_siblings;
> > +		dev->state &= ~IXXAT_USB_STATE_CONNECTED;
> > +		strncpy(name, netdev->name, IFNAMSIZ);
> > +
> > +		unregister_netdev(netdev);
> > +
> > +		dev->next_siblings = NULL;
> > +		if (dev->adapter->dev_free)
> > +			dev->adapter->dev_free(dev);
> > +
> > +		free_candev(netdev);
> > +		dev_dbg(&intf->dev, "%s removed\n", name);
> > +	}
> > +
> > +	usb_set_intfdata(intf, NULL);
> > +}
> > +
> > +/*
> > + * probe function for new ixxat-usb devices
> > + */
> > +static int ixxat_usb_probe(struct usb_interface *intf,
> > +		const struct usb_device_id *id)
> > +{
> > +	struct usb_device *usb_dev = interface_to_usbdev(intf);
> > +	struct ixx_usb_adapter *ixx_usb_adapter, **pp;
> > +	int i, err = -ENOMEM;
> > +	struct ixx_dev_caps dev_caps;
> > +
> > +	usb_dev = interface_to_usbdev(intf);
> > +
> > +	usb_reset_configuration(usb_dev);
> > +
> > +	/* get corresponding IXX-USB adapter */
> > +	for (pp = ixx_usb_adapters_list; *pp; pp++)
> > +		if ((*pp)->device_id == le16_to_cpu(usb_dev->descriptor.idProduct))
> > +			break;
> > +
> > +	ixx_usb_adapter = *pp;
> > +	if (!ixx_usb_adapter) {
> > +		/* should never come except device_id bad usage in this file */
> > +		pr_err("%s: didn't find device id. 0x%x in devices list\n",
> > +				IXXAT_USB_DRIVER_NAME, le16_to_cpu(usb_dev->descriptor.idProduct));
> > +		return -ENODEV;
> > +	}
> > +
> > +	/* got corresponding adapter: check if it handles current interface */
> > +	if (ixx_usb_adapter->intf_probe) {
> > +		err = ixx_usb_adapter->intf_probe(intf);
> > +		if (err)
> > +			return err;
> > +	}
> > +
> > +	if (ixx_usb_adapter->dev_power) {
> > +		err = ixx_usb_adapter->dev_power(usb_dev, IXXAT_USB_POWER_WAKEUP);
> > +		if (err)
> > +			return err;
> > +
> > +		/* Give usb device some time to start its can controllers */
> > +		msleep(500);
> > +	}
> > +
> > +	/* got corresponding adapter: check the available controllers */
> > +	if (ixx_usb_adapter->dev_get_dev_caps) {
> > +		err = ixx_usb_adapter->dev_get_dev_caps(usb_dev, &dev_caps);
> > +		if (err)
> > +			return err;
> > +
> > +		for (i = 0; i < dev_caps.bus_ctrl_count; i++) {
> > +			if ( IXXAT_USB_BUS_CAN
> > +					== IXXAT_USB_BUS_TYPE(dev_caps.bus_ctrl_types[i]))
> > +				ixx_usb_adapter->ctrl_count++;
> > +		}
> > +
> > +		for (i = 0; i < dev_caps.bus_ctrl_count; i++) {
> > +			if ( IXXAT_USB_BUS_CAN == IXXAT_USB_BUS_TYPE(dev_caps.bus_ctrl_types[i]))
> > +				err = ixxat_usb_create_dev(ixx_usb_adapter, intf, i);
> > +			if (err) {
> > +				/* deregister already created devices */
> > +				ixxat_usb_disconnect(intf);
> > +				break;
> > +			}
> > +		}
> > +	}
> > +
> > +	return err;
> > +}
> > +
> > +/* usb specific object needed to register this driver with the usb subsystem */
> > +static struct usb_driver ixx_usb_driver = {
> > +	.name = IXXAT_USB_DRIVER_NAME,
> > +	.disconnect = ixxat_usb_disconnect,
> > +	.probe = ixxat_usb_probe,
> > +	.id_table = ixxat_usb_table,
> > +};
> > +
> > +static int __init ixx_usb_init(void)
> > +{
> > +	int err;
> > +
> > +	/* register this driver with the USB subsystem */
> > +	err = usb_register(&ixx_usb_driver);
> > +	if (err)
> > +		pr_err("%s: usb_register failed (err %d)\n",
> > +				IXXAT_USB_DRIVER_NAME, err);
> > +
> > +	return err;
> > +}
> > +
> > +static int ixxat_usb_do_device_exit(struct device *d, void *arg)
> > +{
> > +	struct usb_interface
> > +		*intf = (struct usb_interface*)to_usb_interface(d);
> > +	struct ixx_usb_device *dev;
> > +
> > +	/* stop as many netdev devices as siblings */
> > +	for (dev = usb_get_intfdata(intf); dev; dev = dev->prev_siblings) {
> > +		struct net_device *netdev = dev->netdev;
> > +
> > +		if (netif_device_present(netdev))
> > +			if (dev->adapter->dev_exit)
> > +				dev->adapter->dev_exit(dev);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static void __exit ixx_usb_exit(void)
> > +{
> > +	int err;
> > +
> > +	/* last chance do send any synchronous commands here */
> > +	err = driver_for_each_device(&ixx_usb_driver.drvwrap.driver, NULL,
> > +			NULL, ixxat_usb_do_device_exit);
> > +	if (err)
> > +		pr_err("%s: failed to stop all can devices (err %d)\n",
> > +				IXXAT_USB_DRIVER_NAME, err);
> > +
> > +	/* deregister this driver with the USB subsystem */
> > +	usb_deregister(&ixx_usb_driver);
> > +
> > +	pr_info("%s: IXX-USB interfaces driver unloaded\n",
> > +			IXXAT_USB_DRIVER_NAME);
> > +}
> > +
> > +module_init(ixx_usb_init);
> > +module_exit(ixx_usb_exit);
> > diff --git a/ubuntu/ixxat/ixx_usb_core.h b/ubuntu/ixxat/ixx_usb_core.h
> > new file mode 100644
> > index 000000000000..79c11d8f1ea8
> > --- /dev/null
> > +++ b/ubuntu/ixxat/ixx_usb_core.h
> > @@ -0,0 +1,289 @@
> > +/*
> > + * CAN driver for IXXAT USB-to-CAN V2 adapters
> > + *
> > + * Copyright (C) 2003-2014 Michael Hengler IXXAT Automation GmbH
> > + *
> > + * Based on code originally by pcan_usb_core
> > + *
> > + * 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 IXX_USB_CORE_H
> > +#define IXX_USB_CORE_H
> > +
> > +#include <linux/version.h>
> > +
> > +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 3)
> > +#define CANFD_CAPABLE 1
> > +#endif
> > +
> > +extern struct ixx_usb_adapter usb_to_can_v2_compact;
> > +extern struct ixx_usb_adapter usb_to_can_v2_automotive;
> > +extern struct ixx_usb_adapter usb_to_can_v2_embedded;
> > +extern struct ixx_usb_adapter usb_to_can_v2_professional;
> > +extern struct ixx_usb_adapter usb_to_can_v2_low_speed;
> > +extern struct ixx_usb_adapter usb_to_can_v2_extended;
> > +
> > +#ifdef CANFD_CAPABLE
> > +extern struct ixx_usb_adapter usb_to_can_fd_automotive;
> > +extern struct ixx_usb_adapter usb_to_can_fd_compact;
> > +extern struct ixx_usb_adapter usb_to_can_fd_professional;
> > +extern struct ixx_usb_adapter usb_to_can_fd_pcie_mini;
> > +extern struct ixx_usb_adapter usb_to_car;
> > +extern struct ixx_usb_adapter dell_edge_gw3002;
> > +#endif
> > +
> > +#ifndef CAN_MAX_DLEN
> > +#define CAN_MAX_DLEN 8
> > +#endif
> > +
> > +#ifndef CANFD_MAX_DLEN
> > +#define CANFD_MAX_DLEN 64
> > +#endif
> > +
> > +
> > +/* supported device ids. */
> > +#define USB_TO_CAN_V2_COMPACT_PRODUCT_ID       0x0008
> > +#define USB_TO_CAN_V2_EMBEDDED_PRODUCT_ID      0x0009
> > +#define USB_TO_CAN_V2_PROFESSIONAL_PRODUCT_ID  0x000A
> > +#define USB_TO_CAN_V2_AUTOMOTIVE_PRODUCT_ID    0x000B
> > +#define USB_TO_LIN_V2_PRODUCT_ID               0x000C
> > +#define USB_TO_KLINE_V2_PRODUCT_ID             0x000D
> > +#define USB_TO_CAN_V2_LOW_SPEED_PRODUCT_ID     0xFFFF
> > +#define USB_TO_CAN_V2_EXTENDED_PRODUCT_ID      0x000E
> > +
> > +#define USB_TO_CAN_FD_COMPACT_PRODUCT_ID       0x0014
> > +#define USB_TO_CAN_FD_PROFESSIONAL_PRODUCT_ID  0x0016
> > +#define USB_TO_CAN_FD_AUTOMOTIVE_PRODUCT_ID    0x0017
> > +#define USB_TO_CAN_FD_PCIE_MINI_PRODUCT_ID     0x001B
> > +#define USB_TO_CAR_ID                          0x001C
> > +#define DELL_EDGE_GW3002_PRODUCT_ID            0xFF11
> > +
> > +#define IXXAT_USB_MAX_CHANNEL  5
> > +
> > +/* number of urbs that are submitted for rx/tx per channel */
> > +#define IXXAT_USB_MAX_RX_URBS             4
> > +#define IXXAT_USB_MAX_TX_URBS             10
> > +
> > +#define IXX_BTMODE_NAT 0x01
> > +
> > +#define IXXAT_USB_POWER_WAKEUP    0
> > +#define IXXAT_USB_POWER_SLEEP     1
> > +
> > +struct ixx_usb_device;
> > +
> > +struct ixx_dev_caps
> > +{
> > +	u16 bus_ctrl_count;
> > +	u16 bus_ctrl_types[32];
> > +} __packed;
> > +
> > +struct ixx_ctrl_caps
> > +{
> > +	u16 ctrl_type;
> > +	u16 bus_coupling;
> > +	u32 features;
> > +	u32 clock_freq;
> > +	u32 tsc_divisor;
> > +	u32 cms_divisor;
> > +	u32 cms_max_ticks;
> > +	u32 dtx_divisor;
> > +	u32 dtx_max_ticks;
> > +} __packed;
> > +
> > +struct canbtp
> > +{
> > +	u32 mode;   // timing mode (see CAN_BTMODE_ const)
> > +	u32 bps;    // bits per second or prescaler (see CAN_BTMODE_)
> > +	u16 ts1;    // length of time segment 1 in quantas
> > +	u16 ts2;    // length of time segment 2 in quantas
> > +	u16 sjw;    // re-synchronisation jump width in quantas
> > +	u16 tdo;    // transceiver delay compensation offset in quantas
> > +	// (0 = disabled)
> > +} __packed;
> > +
> > +struct ixx_ctrl_caps_v2
> > +{
> > +	u16 ctrl_type;
> > +	u16 bus_coupling;
> > +	u32 features;
> > +
> > +	u32 clock_freq;
> > +	struct canbtp sdr_range_min;
> > +	struct canbtp sdr_range_max;
> > +	struct canbtp fdr_range_min;
> > +	struct canbtp fdr_range_max;
> > +
> > +	u32 tsc_freq;
> > +	u32 tsc_divisor;
> > +
> > +	u32 cms_freq;
> > +	u32 cms_divisor;
> > +	u32 cms_max_ticks;
> > +
> > +	u32 dtx_freq;
> > +	u32 dtx_divisor;
> > +	u32 dtx_max_ticks;
> > +} __packed;
> > +
> > +struct ixx_intf_info
> > +{
> > +	char   device_name[16];       // device name
> > +	char   device_id[16];         // device identification ( unique device id)
> > +	u16    device_version;        // device version ( 0, 1, ...)
> > +	u32    device_fpga_version;   // device version of FPGA design
> > +} __packed;
> > +
> > +struct ixx_intf_fw_info
> > +{
> > +	u32 firmware_type;  // type of currently running firmware
> > +	u16 reserved;       // reserved
> > +	u16 major_version;  // major firmware version number
> > +	u16 minor_version;  // minor firmware version number
> > +	u16 build_version;  // build firmware version number
> > +} __packed;
> > +
> > +struct ixx_usb_adapter {
> > +	char *name;
> > +	u32 device_id;
> > +	struct can_clock clock;
> > +	const struct can_bittiming_const bittiming_const;
> > +	const struct can_bittiming_const data_bittiming_const;
> > +
> > +	unsigned int ctrl_count;
> > +
> > +	u32 ctrlmode_supported;
> > +
> > +	int (*intf_probe)(struct usb_interface *intf);
> > +
> > +	int (*dev_get_dev_caps)(struct usb_device *usb_dev, struct ixx_dev_caps* dev_caps);
> > +	int (*dev_get_ctrl_caps)(struct usb_device *usb_dev, struct ixx_ctrl_caps* ctrl_caps, int index);
> > +
> > +	int (*intf_get_info)(struct ixx_usb_device *dev, struct ixx_intf_info* intf_info);
> > +	int (*intf_get_fw_info)(struct ixx_usb_device *dev, struct ixx_intf_fw_info* fw_info);
> > +
> > +	int (*dev_init)(struct ixx_usb_device *dev);
> > +	void (*dev_exit)(struct ixx_usb_device *dev);
> > +	void (*dev_free)(struct ixx_usb_device *dev);
> > +	int (*dev_open)(struct ixx_usb_device *dev);
> > +	int (*dev_close)(struct ixx_usb_device *dev);
> > +	int (*dev_set_bittiming)(struct ixx_usb_device *dev, struct can_bittiming *bt);
> > +	int (*dev_set_bus)(struct ixx_usb_device *dev, u8 onoff);
> > +	int (*dev_decode_buf)(struct ixx_usb_device *dev, struct urb *urb);
> > +	int (*dev_encode_msg)(struct ixx_usb_device *dev, struct sk_buff *skb,
> > +			u8 *obuf, size_t *size);
> > +	int (*dev_start)(struct ixx_usb_device *dev);
> > +	int (*dev_stop)(struct ixx_usb_device *dev);
> > +	int (*dev_restart_async)(struct ixx_usb_device *dev, struct urb *urb,
> > +			u8 *buf);
> > +	int (*dev_power)(struct usb_device *usb_dev, u8 mode);
> > +	u8 ep_msg_in[IXXAT_USB_MAX_CHANNEL];
> > +	u8 ep_msg_out[IXXAT_USB_MAX_CHANNEL];
> > +
> > +	int rx_buffer_size;
> > +	int tx_buffer_size;
> > +	int sizeof_dev_private;
> > +
> > +	int has_bgi_ep;
> > +
> > +};
> > +
> > +struct ixx_time_ref {
> > +	struct timeval tv_host_0;
> > +	u32 ts_dev_0;
> > +	u32 ts_dev_last;
> > +};
> > +
> > +struct ixx_tx_urb_context {
> > +	struct ixx_usb_device *dev;
> > +	u32 echo_index;
> > +	u8 dlc;
> > +	u8 count;
> > +	struct urb *urb;
> > +};
> > +
> > +/*IXXAT USB device */
> > +struct ixx_usb_device {
> > +	struct can_priv can;
> > +	struct ixx_usb_adapter *adapter;
> > +	unsigned int ctrl_idx;
> > +	u32 state;
> > +
> > +	struct sk_buff *echo_skb[IXXAT_USB_MAX_TX_URBS];
> > +
> > +	struct usb_device *udev;
> > +	struct net_device *netdev;
> > +
> > +	atomic_t active_tx_urbs;
> > +	struct usb_anchor tx_submitted;
> > +	struct ixx_tx_urb_context tx_contexts[IXXAT_USB_MAX_TX_URBS];
> > +
> > +	struct usb_anchor rx_submitted;
> > +
> > +	u32 device_number;
> > +	u8 device_rev;
> > +
> > +	u8 ep_msg_in;
> > +	u8 ep_msg_out;
> > +
> > +	u8 transmit_buffer[256];
> > +	u8 transmit_ptr;
> > +	u8 transmit_count;
> > +	u8 transmit_dlc;
> > +
> > +	struct task_struct *restart_task;
> > +	u8 restart_flag;
> > +	u8 must_quit;
> > +	wait_queue_head_t wait_queue;
> > +
> > +	struct ixx_usb_device *prev_siblings;
> > +	struct ixx_usb_device *next_siblings;
> > +
> > +	u8 btr0;
> > +	u8 btr1;
> > +
> > +	int ctrl_opened_count;
> > +
> > +	struct ixx_time_ref time_ref;
> > +
> > +	struct ixx_intf_info dev_info;
> > +	struct ixx_intf_fw_info fw_info;
> > +
> > +	struct can_berr_counter bec;
> > +};
> > +
> > +struct ixx_can_msg
> > +{
> > +	u8  size;
> > +	u32 time;
> > +	u32 msg_id;
> > +	u32 flags;
> > +	u8  data[CAN_MAX_DLEN];
> > +} __packed;
> > +
> > +struct ixx_can_msg_v2
> > +{
> > +	u8  size;
> > +	u32 time;
> > +	u32 msg_id;
> > +	u32 flags;
> > +	u32 client_id;
> > +	u8  data[CANFD_MAX_DLEN];
> > +} __packed;
> > +
> > +void ixxat_dump_mem(char *prompt, void *p, int l);
> > +
> > +void ixxat_usb_update_ts_now(struct ixx_usb_device *dev, u32 ts_now);
> > +void ixxat_usb_set_ts_now(struct ixx_usb_device *dev, u32 ts_now);
> > +void ixxat_usb_get_ts_tv(struct ixx_usb_device *dev, u32 ts,
> > +		ktime_t* k_time);
> > +
> > +void ixxat_usb_async_complete(struct urb *urb);
> > +void ixxat_usb_restart_complete(struct ixx_usb_device *dev);
> > +#endif
> > diff --git a/ubuntu/ixxat/ixx_usb_fd.c b/ubuntu/ixxat/ixx_usb_fd.c
> > new file mode 100644
> > index 000000000000..63d5b9944a85
> > --- /dev/null
> > +++ b/ubuntu/ixxat/ixx_usb_fd.c
> > @@ -0,0 +1,1673 @@
> > +/*
> > + * CAN driver for IXXAT USB-to-CAN FD
> > + *
> > + * Copyright (C) 2017 Michael Hengler <mhengler@ixxat.de>
> > + *
> > + * Based on code originally by pcan_usb_core
> > + *
> > + * 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/delay.h>
> > +#include <linux/can.h>
> > +#include <linux/can/dev.h>
> > +#include <linux/can/error.h>
> > +#include <linux/kthread.h>
> > +#include <linux/sched.h>
> > +#include <linux/wait.h>
> > +#include <linux/types.h>
> > +#include <linux/gfp.h>
> > +#include <asm-generic/errno.h>
> > +#include <stdarg.h>
> > +
> > +#include "ixx_usb_core.h"
> > +
> > +#ifdef CANFD_CAPABLE
> > +
> > +MODULE_SUPPORTED_DEVICE("IXXAT Automation GmbH USB-to-CAN FD");
> > +
> > +/* use ifi can fd clock due to internal bittiming calculations */
> > +#define IFIFD_CRYSTAL_HZ	      80000000
> > +
> > +/* usb-to-can fd Endpoints */
> > +#define IXXAT_USBFD_EP_CMDOUT   0
> > +#define IXXAT_USBFD_EP_CMDIN    (IXXAT_USBFD_EP_CMDOUT | USB_DIR_IN)
> > +#define IXXAT_USBFD_EP_MSGOUT_0 1
> > +#define IXXAT_USBFD_EP_MSGIN_0  (IXXAT_USBFD_EP_MSGOUT_0 | USB_DIR_IN)
> > +#define IXXAT_USBFD_EP_MSGOUT_1 2
> > +#define IXXAT_USBFD_EP_MSGIN_1  (IXXAT_USBFD_EP_MSGOUT_1 | USB_DIR_IN)
> > +#define IXXAT_USBFD_EP_MSGOUT_2 3
> > +#define IXXAT_USBFD_EP_MSGIN_2  (IXXAT_USBFD_EP_MSGOUT_2 | USB_DIR_IN)
> > +#define IXXAT_USBFD_EP_MSGOUT_3 4
> > +#define IXXAT_USBFD_EP_MSGIN_3  (IXXAT_USBFD_EP_MSGOUT_3 | USB_DIR_IN)
> > +#define IXXAT_USBFD_EP_MSGOUT_4 5
> > +#define IXXAT_USBFD_EP_MSGIN_4  (IXXAT_USBFD_EP_MSGOUT_4 | USB_DIR_IN)
> > +
> > +/* DELL Edge GW3002 Endpoints */
> > +#define DELL_EDGE_GW3002_EP_MSGOUT_0 1
> > +#define DELL_EDGE_GW3002_EP_MSGIN_0  (2 | USB_DIR_IN)
> > +#define DELL_EDGE_GW3002_EP_MSGOUT_1 3
> > +#define DELL_EDGE_GW3002_EP_MSGIN_1  (4 | USB_DIR_IN)
> > +#define DELL_EDGE_GW3002_EP_MSGOUT_2 5
> > +#define DELL_EDGE_GW3002_EP_MSGIN_2  (6 | USB_DIR_IN)
> > +#define DELL_EDGE_GW3002_EP_MSGOUT_3 7
> > +#define DELL_EDGE_GW3002_EP_MSGIN_3  (8 | USB_DIR_IN)
> > +#define DELL_EDGE_GW3002_EP_MSGOUT_4 9
> > +#define DELL_EDGE_GW3002_EP_MSGIN_4  (10 | USB_DIR_IN)
> > +
> > +/* usb-to-can fd rx/tx buffers size */
> > +#define IXXAT_USBFD_RX_BUFFER_SIZE          512
> > +#define IXXAT_USBFD_TX_BUFFER_SIZE          512
> > +
> > +#define IXXAT_USBFD_CMD_BUFFER_SIZE         256
> > +
> > +/* reception of 11-bit id messages */
> > +#define IXXAT_USBFD_OPMODE_STANDARD         0x01
> > +/* reception of 29-bit id messages */
> > +#define IXXAT_USBFD_OPMODE_EXTENDED         0x02
> > +/* enable reception of error frames */
> > +#define IXXAT_USBFD_OPMODE_ERRFRAME         0x04
> > +/* listen only mode (TX passive) */
> > +#define IXXAT_USBFD_OPMODE_LISTONLY         0x08
> > +
> > +/* no extended operation */
> > +#define IXXAT_USBFD_EXMODE_DISABLED         0x00
> > +/* extended data length */
> > +#define IXXAT_USBFD_EXMODE_EXTDATA          0x01
> > +/* fast data bit rate */
> > +#define IXXAT_USBFD_EXMODE_FASTDATA         0x02
> > +/* ISO conform CAN-FD frame */
> > +#define IXXAT_USBFD_EXMODE_ISOFD            0x04
> > +
> > +/* Stuff error */
> > +#define IXXAT_USBFD_CAN_ERROR_STUFF         1
> > +/* Form error */
> > +#define IXXAT_USBFD_CAN_ERROR_FORM          2
> > +/* Acknowledgment error */
> > +#define IXXAT_USBFD_CAN_ERROR_ACK           3
> > +/* Bit error */
> > +#define IXXAT_USBFD_CAN_ERROR_BIT           4
> > +/* Fast data bit rate error */
> > +#define IXXAT_USBFD_CAN_ERROR_FAST_DATA     5
> > +/* CRC error */
> > +#define IXXAT_USBFD_CAN_ERROR_CRC           6
> > +/* Other (unspecified) error */
> > +#define IXXAT_USBFD_CAN_ERROR_OTHER         7
> > +
> > +/* Data overrun occurred */
> > +#define IXXAT_USBFD_CAN_STATUS_OVRRUN    0x02
> > +/* Error warning limit exceeded */
> > +#define IXXAT_USBFD_CAN_STATUS_ERRLIM    0x04
> > +/* Bus off status */
> > +#define IXXAT_USBFD_CAN_STATUS_BUSOFF    0x08
> > +
> > +#define IXXAT_USBFD_CAN_DATA             0x00
> > +#define IXXAT_USBFD_CAN_INFO             0x01
> > +#define IXXAT_USBFD_CAN_ERROR            0x02
> > +#define IXXAT_USBFD_CAN_STATUS           0x03
> > +#define IXXAT_USBFD_CAN_WAKEUP           0x04
> > +#define IXXAT_USBFD_CAN_TIMEOVR          0x05
> > +#define IXXAT_USBFD_CAN_TIMERST          0x06
> > +
> > +
> > +#define IXXAT_USBFD_MSG_FLAGS_TYPE   0x000000FF
> > +#define IXXAT_USBFD_MSG_FLAGS_SSM    0x00000100
> > +#define IXXAT_USBFD_MSG_FLAGS_HPM    0x00000200
> > +#define IXXAT_USBFD_MSG_FLAGS_EDL    0x00000400
> > +#define IXXAT_USBFD_MSG_FLAGS_FDR    0x00000800
> > +#define IXXAT_USBFD_MSG_FLAGS_ESI    0x00001000
> > +#define IXXAT_USBFD_MSG_FLAGS_RES    0x0000E000
> > +#define IXXAT_USBFD_MSG_FLAGS_DLC    0x000F0000
> > +#define IXXAT_USBFD_MSG_FLAGS_OVR    0x00100000
> > +#define IXXAT_USBFD_MSG_FLAGS_SRR    0x00200000
> > +#define IXXAT_USBFD_MSG_FLAGS_RTR    0x00400000
> > +#define IXXAT_USBFD_MSG_FLAGS_EXT    0x00800000
> > +#define IXXAT_USBFD_MSG_FLAGS_AFC    0xFF000000
> > +
> > +#define IXXAT_USBFD_BAL_CMD_CLASS    3
> > +#define IXXAT_USBFD_BRD_CMD_CLASS    4
> > +
> > +#define IXXAT_USBFD_BRD_CMD_CAT      0
> > +#define IXXAT_USBFD_CAN_CMD_CAT      1
> > +
> > +#define IXXAT_USBFD_VCI_CMD_CODE(Class, Function) \
> > +	((u32) (((Class) << 8) | (Function)))
> > +
> > +#define IXXAT_USBFD_BRD_CMD_CODE(Category, Function) \
> > +	IXXAT_USBFD_VCI_CMD_CODE(IXXAT_USBFD_BRD_CMD_CLASS, \
> > +		((Category) << 5) | (Function))
> > +
> > +#define IXXAT_USBFD_BAL_CMD_CODE(Category, Function) \
> > +	IXXAT_USBFD_VCI_CMD_CODE(IXXAT_USBFD_BAL_CMD_CLASS, \
> > +		((Category) << 5) | (Function))
> > +
> > +#define IXXAT_USBFD_CAN_GET_CAPS_CMD \
> > +	IXXAT_USBFD_BAL_CMD_CODE(IXXAT_USBFD_CAN_CMD_CAT, 0)
> > +#define IXXAT_USBFD_POWER_CMD \
> > +	IXXAT_USBFD_BRD_CMD_CODE(IXXAT_USBFD_CAN_CMD_CAT, 1)
> > +#define IXXAT_USBFD_CAN_INIT_CMD \
> > +	IXXAT_USBFD_BAL_CMD_CODE(IXXAT_USBFD_CAN_CMD_CAT, 5)
> > +#define IXXAT_USBFD_CAN_START_CMD \
> > +	IXXAT_USBFD_BAL_CMD_CODE(IXXAT_USBFD_CAN_CMD_CAT, 6)
> > +#define IXXAT_USBFD_CAN_STOP_CMD \
> > +	IXXAT_USBFD_BAL_CMD_CODE(IXXAT_USBFD_CAN_CMD_CAT, 7)
> > +#define IXXAT_USBFD_CAN_RESET_CMD \
> > +	IXXAT_USBFD_BAL_CMD_CODE(IXXAT_USBFD_CAN_CMD_CAT, 8)
> > +/* Additional commands for USB-to-CAN FD */
> > +#define IXXAT_USBFD_INIT_V2_CMD \
> > +	IXXAT_USBFD_BAL_CMD_CODE(IXXAT_USBFD_CAN_CMD_CAT, 23)
> > +
> > +#define IXXAT_USBFD_BRD_GET_FWINFO_CMD \
> > +	IXXAT_USBFD_BRD_CMD_CODE(IXXAT_USBFD_BRD_CMD_CAT, 0)
> > +#define IXXAT_USBFD_BRD_GET_DEVCAPS_CMD \
> > +	IXXAT_USBFD_BRD_CMD_CODE(IXXAT_USBFD_BRD_CMD_CAT, 1)
> > +#define IXXAT_USBFD_BRD_GET_DEVINFO_CMD \
> > +	IXXAT_USBFD_BRD_CMD_CODE(IXXAT_USBFD_BRD_CMD_CAT, 2)
> > +
> > +struct ixx_usbfd_dal_req {
> > +	u32 req_size;
> > +	u16 req_port;
> > +	u16 req_socket;
> > +	u32 req_code;
> > +} __packed;
> > +
> > +struct ixx_usbfd_dal_res {
> > +	u32 res_size;
> > +	u32 ret_size;
> > +	u32 ret_code;
> > +} __packed;
> > +
> > +// Additional structures for the for USB-to-CAN FD
> > +
> > +struct ixx_usbfd_dev_power_req {
> > +	struct ixx_usbfd_dal_req dal_req;
> > +	u8  mode;
> > +	u8  _padding1;
> > +	u16 _padding2;
> > +} __packed;
> > +
> > +struct ixx_usbfd_dev_power_res {
> > +	struct ixx_usbfd_dal_res dal_res;
> > +} __packed;
> > +
> > +struct ixx_usbfd_ctrl_init_v2_req {
> > +	struct ixx_usbfd_dal_req dal_req;
> > +	u8                opmode;
> > +	u8                exmode;
> > +	struct canbtp	    sdr;
> > +	struct canbtp	    fdr;
> > +	u16		      _padding;
> > +} __packed;
> > +
> > +struct ixx_usbfd_ctrl_init_v2_res {
> > +	struct ixx_usbfd_dal_res dal_res;
> > +} __packed;
> > +
> > +struct ixx_usbfd_dev_caps_req {
> > +	struct ixx_usbfd_dal_req dal_req;
> > +} __packed;
> > +
> > +struct ixx_usbfd_dev_caps_res {
> > +	struct ixx_usbfd_dal_res dal_res;
> > +	struct ixx_dev_caps dev_caps;
> > +} __packed;
> > +
> > +struct ixx_usbfd_ctrl_caps_req {
> > +	struct ixx_usbfd_dal_req dal_req;
> > +} __packed;
> > +
> > +struct ixx_usbfd_ctrl_caps_res {
> > +	struct ixx_usbfd_dal_res dal_res;
> > +	struct ixx_ctrl_caps ctrl_caps;
> > +} __packed;
> > +
> > +struct ixx_usbfd_ctrl_init_req {
> > +	struct ixx_usbfd_dal_req dal_req;
> > +	u8 mode;
> > +	u8 btr0;
> > +	u8 btr1;
> > +	u8 padding;
> > +} __packed;
> > +
> > +struct ixx_usbfd_ctrl_init_res {
> > +	struct ixx_usbfd_dal_res dal_res;
> > +} __packed;
> > +
> > +struct ixx_usbfd_ctrl_start_req {
> > +	struct ixx_usbfd_dal_req dal_req;
> > +} __packed;
> > +
> > +struct ixx_usbfd_ctrl_start_res {
> > +	struct ixx_usbfd_dal_res dal_res;
> > +	u32 start_time;
> > +} __packed;
> > +
> > +struct ixx_usbfd_ctrl_stop_req {
> > +	struct ixx_usbfd_dal_req dal_req;
> > +	u32 action;
> > +} __packed;
> > +
> > +struct ixx_usbfd_ctrl_stop_res {
> > +	struct ixx_usbfd_dal_res dal_res;
> > +} __packed;
> > +
> > +struct ixx_usbfd_brd_get_fwinfo_req {
> > +	struct ixx_usbfd_dal_req dal_req;
> > +} __packed;
> > +
> > +struct ixx_usbfd_brd_get_fwinfo_res {
> > +	struct ixx_usbfd_dal_res dal_res;
> > +	struct ixx_intf_fw_info fwinfo;
> > +} __packed;
> > +
> > +struct ixx_usbfd_brd_get_intf_info_req {
> > +	struct ixx_usbfd_dal_req dal_req;
> > +} __packed;
> > +
> > +struct ixx_usbfd_brd_get_intf_info_res {
> > +	struct ixx_usbfd_dal_res dal_res;
> > +	struct ixx_intf_info info;
> > +} __packed;
> > +
> > +/*
> > + * send usb-to-can fd command synchronously
> > + */
> > +static int ixx_usbfd_send_cmd(struct usb_device *dev,
> > +		struct ixx_usbfd_dal_req *dal_req)
> > +{
> > +	int err, i;
> > +	u16 size, value;
> > +	u8  request, 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) +
> > +		sizeof(const struct ixx_usbfd_dal_res);
> > +
> > +	buf = kmalloc(size, GFP_KERNEL);
> > +	if(!buf)
> > +		return -ENOMEM;
> > +	memcpy(buf, (u8 *)dal_req, size);
> > +
> > +	for (i = 0; i < 10; ++i) {
> > +		err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
> > +			requesttype,
> > +			value,
> > +			0,
> > +			buf,
> > +			size,
> > +			msecs_to_jiffies(50));
> > +
> > +		if (err < 0)
> > +			msleep(20);
> > +		else
> > +			break;
> > +	}
> > +
> > +	kfree(buf);
> > +
> > +	if (err < 0) {
> > +		dev_err(&dev->dev, "sending command failure: %d\n", err);
> > +		return err;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +/*
> > + * receive usb-to-can fd command synchronously
> > + */
> > +static int ixx_usbfd_rcv_cmd(struct usb_device *dev,
> > +		struct ixx_usbfd_dal_res *dal_res, int value)
> > +{
> > +	int err, res_size, i, size_to_read;
> > +	u8  request, requesttype;
> > +	u8 *buf;
> > +
> > +	request      = 0xff;
> > +	requesttype  = 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 < 10; ++i) {
> > +		err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request,
> > +				requesttype, value,
> > +				0, buf + (u8) res_size,
> > +				size_to_read - res_size, msecs_to_jiffies(50));
> > +
> > +		if (err < 0) {
> > +			msleep(20);
> > +			continue;
> > +		}
> > +
> > +		res_size += err;
> > +		if (res_size < size_to_read)
> > +			msleep(20);
> > +		else
> > +			break;
> > +	}
> > +
> > +	if (res_size != size_to_read)
> > +		err = -EBADMSG;
> > +
> > +	if (err < 0) {
> > +		dev_err(&dev->dev, "receiving command failure: %d\n", err);
> > +		kfree(buf);
> > +		return err;
> > +	}
> > +
> > +	memcpy((u8 *)dal_res, buf, size_to_read);
> > +	kfree(buf);
> > +
> > +	return err;
> > +}
> > +
> > +static int ixx_usbfd_init_ctrl(struct ixx_usb_device *dev, u8 mode,
> > +			u8 exmode,
> > +			struct can_bittiming *arbitration_phase,
> > +			struct can_bittiming *data_phase)
> > +{
> > +	int err = -ENODEV;
> > +	u8 data[IXXAT_USBFD_CMD_BUFFER_SIZE] = { 0 };
> > +	struct ixx_usbfd_ctrl_init_v2_req *ctrl_init_req;
> > +	struct ixx_usbfd_ctrl_init_v2_res *ctrl_init_res;
> > +	u32 req_size = sizeof(*ctrl_init_req);
> > +
> > +	ctrl_init_req = (struct ixx_usbfd_ctrl_init_v2_req *) data;
> > +	ctrl_init_res = (struct ixx_usbfd_ctrl_init_v2_res *)(data + req_size);
> > +
> > +	ctrl_init_req->dal_req.req_size   = cpu_to_le32(req_size);
> > +	ctrl_init_req->dal_req.req_code   =
> > +		cpu_to_le32(IXXAT_USBFD_INIT_V2_CMD);
> > +	ctrl_init_req->dal_req.req_port   = cpu_to_le16(dev->ctrl_idx);
> > +	ctrl_init_req->dal_req.req_socket = 0xffff;
> > +	ctrl_init_req->opmode             = mode;
> > +	ctrl_init_req->exmode             = exmode;
> > +
> > +	ctrl_init_req->sdr.mode = cpu_to_le32(IXX_BTMODE_NAT);
> > +	ctrl_init_req->sdr.bps  = cpu_to_le32(arbitration_phase->brp);
> > +	ctrl_init_req->sdr.ts1  =
> > +		cpu_to_le16(arbitration_phase->prop_seg +
> > +		arbitration_phase->phase_seg1);
> > +	ctrl_init_req->sdr.ts2  = cpu_to_le16(arbitration_phase->phase_seg2);
> > +	ctrl_init_req->sdr.sjw  = cpu_to_le16(arbitration_phase->sjw);
> > +	ctrl_init_req->sdr.tdo  = 0;
> > +
> > +	if (exmode) {
> > +		ctrl_init_req->fdr.mode = cpu_to_le32(IXX_BTMODE_NAT);
> > +		ctrl_init_req->fdr.bps  = cpu_to_le32(data_phase->brp);
> > +		ctrl_init_req->fdr.ts1  =
> > +			cpu_to_le16(data_phase->prop_seg +
> > +			data_phase->phase_seg1);
> > +		ctrl_init_req->fdr.ts2  = cpu_to_le16(data_phase->phase_seg2);
> > +		ctrl_init_req->fdr.sjw  = cpu_to_le16(data_phase->sjw);
> > +		ctrl_init_req->fdr.tdo  =
> > +			cpu_to_le16((1 + data_phase->phase_seg1 +
> > +				data_phase->prop_seg) *
> > +			data_phase->brp);
> > +	}
> > +
> > +	ctrl_init_res->dal_res.res_size =
> > +		cpu_to_le32(sizeof(*ctrl_init_res));
> > +	ctrl_init_res->dal_res.ret_size = 0;
> > +	ctrl_init_res->dal_res.ret_code = 0xffffffff;
> > +
> > +	err = ixx_usbfd_send_cmd(dev->udev, &ctrl_init_req->dal_req);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	err = ixx_usbfd_rcv_cmd(dev->udev,
> > +		&ctrl_init_res->dal_res,
> > +		dev->ctrl_idx);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	return le32_to_cpu(ctrl_init_res->dal_res.ret_code);
> > +}
> > +
> > +static int ixx_usbfd_start_ctrl(struct ixx_usb_device *dev, u32 *time_ref)
> > +{
> > +	int err = -ENODEV;
> > +	u8 data[IXXAT_USBFD_CMD_BUFFER_SIZE] = { 0 };
> > +	struct ixx_usbfd_ctrl_start_req *ctrl_start_req;
> > +	struct ixx_usbfd_ctrl_start_res *ctrl_start_res;
> > +	u32 req_size = sizeof(*ctrl_start_req);
> > +
> > +	ctrl_start_req = (struct ixx_usbfd_ctrl_start_req *) data;
> > +	ctrl_start_res = (struct ixx_usbfd_ctrl_start_res *)(data + req_size);
> > +
> > +	ctrl_start_req->dal_req.req_size   = cpu_to_le32(req_size);
> > +	ctrl_start_req->dal_req.req_code   =
> > +		cpu_to_le32(IXXAT_USBFD_CAN_START_CMD);
> > +	ctrl_start_req->dal_req.req_port   = cpu_to_le16(dev->ctrl_idx);
> > +	ctrl_start_req->dal_req.req_socket = 0xffff;
> > +
> > +	ctrl_start_res->dal_res.res_size =
> > +		cpu_to_le32(sizeof(*ctrl_start_res));
> > +	ctrl_start_res->dal_res.ret_size = 0;
> > +	ctrl_start_res->dal_res.ret_code = 0xffffffff;
> > +	ctrl_start_res->start_time       = 0;
> > +
> > +	err = ixx_usbfd_send_cmd(dev->udev, &ctrl_start_req->dal_req);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	err = ixx_usbfd_rcv_cmd(dev->udev,
> > +		&ctrl_start_res->dal_res,
> > +		dev->ctrl_idx);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	if (time_ref)
> > +		*time_ref = le32_to_cpu(ctrl_start_res->start_time);
> > +
> > +	return le32_to_cpu(ctrl_start_res->dal_res.ret_code);
> > +}
> > +
> > +static int ixx_usbfd_stop_ctrl(struct ixx_usb_device *dev)
> > +{
> > +	int err = -ENODEV;
> > +	u8 data[IXXAT_USBFD_CMD_BUFFER_SIZE] = { 0 };
> > +	struct ixx_usbfd_ctrl_stop_req *ctrl_stop_req;
> > +	struct ixx_usbfd_ctrl_stop_res *ctrl_stop_res;
> > +	u32 req_size = sizeof(*ctrl_stop_req);
> > +
> > +	ctrl_stop_req = (struct ixx_usbfd_ctrl_stop_req *) data;
> > +	ctrl_stop_res = (struct ixx_usbfd_ctrl_stop_res *)(data + req_size);
> > +
> > +	ctrl_stop_req->dal_req.req_size   = cpu_to_le32(req_size);
> > +	ctrl_stop_req->dal_req.req_code   =
> > +		cpu_to_le32(IXXAT_USBFD_CAN_STOP_CMD);
> > +	ctrl_stop_req->dal_req.req_port   = cpu_to_le16(dev->ctrl_idx);
> > +	ctrl_stop_req->dal_req.req_socket = 0xffff;
> > +	ctrl_stop_req->action             = cpu_to_le32(0x3);
> > +
> > +	ctrl_stop_res->dal_res.res_size =
> > +		cpu_to_le32(sizeof(*ctrl_stop_res));
> > +	ctrl_stop_res->dal_res.ret_size = 0;
> > +	ctrl_stop_res->dal_res.ret_code = 0xffffffff;
> > +
> > +	err = ixx_usbfd_send_cmd(dev->udev, &ctrl_stop_req->dal_req);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	err = ixx_usbfd_rcv_cmd(dev->udev,
> > +		&ctrl_stop_res->dal_res,
> > +		dev->ctrl_idx);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	if (!le32_to_cpu(ctrl_stop_res->dal_res.ret_code))
> > +		dev->can.state = CAN_STATE_STOPPED;
> > +
> > +	return le32_to_cpu(ctrl_stop_res->dal_res.ret_code);
> > +}
> > +
> > +static int ixx_usbfd_reset_ctrl(struct ixx_usb_device *dev)
> > +{
> > +	int err = -ENODEV;
> > +	u8 data[IXXAT_USBFD_CMD_BUFFER_SIZE] = { 0 };
> > +	struct ixx_usbfd_dal_req *dal_req;
> > +	struct ixx_usbfd_dal_res *dal_res;
> > +	u32 req_size = sizeof(*dal_req);
> > +
> > +	dal_req = (struct ixx_usbfd_dal_req *) data;
> > +	dal_res = (struct ixx_usbfd_dal_res *)(data + req_size);
> > +
> > +	dal_req->req_size   = cpu_to_le32(req_size);
> > +	dal_req->req_code   = cpu_to_le32(IXXAT_USBFD_CAN_RESET_CMD);
> > +	dal_req->req_port   = cpu_to_le16(dev->ctrl_idx);
> > +	dal_req->req_socket = 0xffff;
> > +
> > +	dal_res->res_size = cpu_to_le32(sizeof(*dal_res));
> > +	dal_res->ret_size = 0;
> > +	dal_res->ret_code = 0xffffffff;
> > +
> > +	err = ixx_usbfd_send_cmd(dev->udev, dal_req);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	err = ixx_usbfd_rcv_cmd(dev->udev, dal_res,
> > +			dev->ctrl_idx);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	return le32_to_cpu(dal_res->ret_code);
> > +}
> > +
> > +static int ixx_usbfd_power_ctrl(struct usb_device *dev, u8 mode)
> > +{
> > +	int err = -ENODEV;
> > +	u8 data[IXXAT_USBFD_CMD_BUFFER_SIZE] = { 0 };
> > +	struct ixx_usbfd_dev_power_req *ctrl_power_req;
> > +	struct ixx_usbfd_dev_power_res *ctrl_power_res;
> > +	u32 req_size = sizeof(*ctrl_power_req);
> > +
> > +	ctrl_power_req = (struct ixx_usbfd_dev_power_req *) data;
> > +	ctrl_power_res = (struct ixx_usbfd_dev_power_res *)(data + req_size);
> > +
> > +	ctrl_power_req->dal_req.req_size   = cpu_to_le32(req_size);
> > +	ctrl_power_req->dal_req.req_code   =
> > +		cpu_to_le32(IXXAT_USBFD_POWER_CMD);
> > +	ctrl_power_req->dal_req.req_port   = cpu_to_le16(0xffff);
> > +	ctrl_power_req->dal_req.req_socket = 0xffff;
> > +	ctrl_power_req->mode               = mode;
> > +
> > +	ctrl_power_res->dal_res.res_size =
> > +		cpu_to_le32(sizeof(*ctrl_power_res));
> > +	ctrl_power_res->dal_res.ret_size = 0;
> > +	ctrl_power_res->dal_res.ret_code = 0xffffffff;
> > +
> > +	err = ixx_usbfd_send_cmd(dev, &ctrl_power_req->dal_req);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	err = ixx_usbfd_rcv_cmd(dev,
> > +		&ctrl_power_res->dal_res,
> > +		0xffff);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	return le32_to_cpu(ctrl_power_res->dal_res.ret_code);
> > +}
> > +
> > +/*
> > + * handle restart but in asynchronously way
> > + */
> > +static int ixx_usbfd_restart_task(void *user_data)
> > +{
> > +	u32 time_ref;
> > +	struct ixx_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) {
> > +				ixx_usbfd_stop_ctrl(dev);
> > +				ixx_usbfd_start_ctrl(dev, &time_ref);
> > +				dev->restart_flag = 0;
> > +				dev->can.state = CAN_STATE_ERROR_ACTIVE;
> > +			}
> > +		} else
> > +			msleep(20);
> > +	}
> > +	return 0;
> > +}
> > +
> > +static int ixx_usbfd_handle_canmsg(struct ixx_usb_device *dev,
> > +		struct ixx_can_msg_v2 *rx)
> > +{
> > +	struct net_device *netdev = dev->netdev;
> > +	struct canfd_frame *can_frame;
> > +	struct sk_buff *skb;
> > +	const u32 flags = le32_to_cpu(rx->flags);
> > +
> > +	if (flags & IXXAT_USBFD_MSG_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_USBFD_MSG_FLAGS_EDL) {
> > +		if (flags & IXXAT_USBFD_MSG_FLAGS_FDR)
> > +			can_frame->flags |= CANFD_BRS;
> > +
> > +		if (flags & IXXAT_USBFD_MSG_FLAGS_ESI)
> > +			can_frame->flags |= CANFD_ESI;
> > +
> > +		can_frame->len =
> > +			can_dlc2len(
> > +			get_canfd_dlc((flags & IXXAT_USBFD_MSG_FLAGS_DLC)
> > +			>> 16));
> > +	}	else {
> > +		can_frame->len =
> > +			get_canfd_dlc((flags & IXXAT_USBFD_MSG_FLAGS_DLC)
> > +			>> 16);
> > +	}
> > +
> > +	if (flags & IXXAT_USBFD_MSG_FLAGS_OVR) {
> > +		netdev->stats.rx_over_errors++;
> > +		netdev->stats.rx_errors++;
> > +	}
> > +
> > +	can_frame->can_id = le32_to_cpu(rx->msg_id);
> > +
> > +	if (flags & IXXAT_USBFD_MSG_FLAGS_EXT)
> > +		can_frame->can_id |= CAN_EFF_FLAG;
> > +
> > +	if (flags & IXXAT_USBFD_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);
> > +
> > +	netif_rx(skb);
> > +	netdev->stats.rx_packets++;
> > +	netdev->stats.rx_bytes += can_frame->len;
> > +
> > +	return 0;
> > +}
> > +
> > +static int ixx_usbfd_handle_error(struct ixx_usb_device *dev,
> > +		struct ixx_can_msg_v2 *rx)
> > +{
> > +	struct net_device *netdev = dev->netdev;
> > +	struct can_frame *can_frame;
> > +	struct sk_buff *skb;
> > +	u8 raw_status = 0;
> > +
> > +	/* nothing should be sent while in BUS_OFF state */
> > +	if (dev->can.state == CAN_STATE_BUS_OFF)
> > +		return 0;
> > +
> > +	raw_status = rx->data[0];
> > +
> > +	/* allocate an skb to store the error frame */
> > +	skb = alloc_can_err_skb(netdev, &can_frame);
> > +	if (!skb)
> > +		return -ENOMEM;
> > +
> > +	switch (raw_status) {
> > +	case IXXAT_USBFD_CAN_ERROR_ACK:
> > +		can_frame->can_id |= CAN_ERR_ACK;
> > +		netdev->stats.tx_errors++;
> > +		break;
> > +	case IXXAT_USBFD_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_USBFD_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_USBFD_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_USBFD_CAN_ERROR_STUFF:
> > +		can_frame->can_id |= CAN_ERR_PROT;
> > +		can_frame->data[2] |= CAN_ERR_PROT_STUFF;
> > +		netdev->stats.rx_errors++;
> > +		break;
> > +	case IXXAT_USBFD_CAN_ERROR_OTHER:
> > +		can_frame->can_id |= CAN_ERR_PROT;
> > +		can_frame->data[2] |= CAN_ERR_PROT_UNSPEC;
> > +		netdev->stats.rx_errors++;
> > +		break;
> > +	default:
> > +		can_frame->can_id |= CAN_ERR_PROT;
> > +		netdev->stats.rx_errors++;
> > +	}
> > +
> > +	netif_rx(skb);
> > +	netdev->stats.rx_packets++;
> > +	netdev->stats.rx_bytes += can_frame->can_dlc;
> > +
> > +	dev->bec.txerr = le16_to_cpu(rx->data[1]);
> > +	dev->bec.rxerr = le16_to_cpu(rx->data[3]);
> > +
> > +	return 0;
> > +}
> > +
> > +static int ixx_usbfd_handle_status(struct ixx_usb_device *dev,
> > +		struct ixx_can_msg_v2 *rx)
> > +{
> > +	struct net_device *netdev = dev->netdev;
> > +	struct can_frame *can_frame;
> > +	struct sk_buff *skb;
> > +	u8 raw_status = 0;
> > +	u32 new_state = 0;
> > +
> > +	raw_status = rx->data[0];
> > +
> > +	/* nothing should be sent while in BUS_OFF state */
> > +	if (dev->can.state == CAN_STATE_BUS_OFF)
> > +		return 0;
> > +
> > +	if (!raw_status) {
> > +		/* no error bit (back to active state) */
> > +		dev->can.state = CAN_STATE_ERROR_ACTIVE;
> > +
> > +		dev->bec.txerr = 0;
> > +		dev->bec.rxerr = 0;
> > +		return 0;
> > +	}
> > +
> > +	/* allocate an skb to store the error frame */
> > +	skb = alloc_can_err_skb(netdev, &can_frame);
> > +	if (!skb)
> > +		return -ENOMEM;
> > +
> > +	if (raw_status & IXXAT_USBFD_CAN_STATUS_BUSOFF) {
> > +		can_frame->can_id |= CAN_ERR_BUSOFF;
> > +		new_state = CAN_STATE_BUS_OFF;
> > +		dev->can.can_stats.bus_off++;
> > +		can_bus_off(netdev);
> > +	} else {
> > +		if (raw_status & IXXAT_USBFD_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_USBFD_CAN_STATUS_OVRRUN) {
> > +			can_frame->can_id |= CAN_ERR_PROT;
> > +			can_frame->data[2] |= CAN_ERR_PROT_OVERLOAD;
> > +			netdev->stats.rx_over_errors++;
> > +			netdev->stats.rx_errors++;
> > +		}
> > +
> > +		if (!new_state) {
> > +			new_state = CAN_STATE_ERROR_ACTIVE;
> > +
> > +			dev->bec.txerr = 0;
> > +			dev->bec.rxerr = 0;
> > +		}
> > +	}
> > +
> > +	dev->can.state = new_state;
> > +
> > +	netif_rx(skb);
> > +	netdev->stats.rx_packets++;
> > +	netdev->stats.rx_bytes += can_frame->can_dlc;
> > +
> > +	return 0;
> > +}
> > +
> > +/*
> > + * callback for bulk IN urb
> > + */
> > +static int ixx_usbfd_decode_buf(struct ixx_usb_device *dev, struct urb *urb)
> > +{
> > +	struct net_device *netdev = dev->netdev;
> > +	struct ixx_can_msg_v2 *can_msg;
> > +	u32 msg_end;
> > +	int err = 0;
> > +	u32 read_size = 0;
> > +	u8 msg_type;
> > +
> > +	u8 *data = urb->transfer_buffer;
> > +
> > +	/* loop reading all the records from the incoming message */
> > +	msg_end = urb->actual_length;
> > +	for (; msg_end > 0;) {
> > +		can_msg = (struct ixx_can_msg_v2 *) &data[read_size];
> > +
> > +		if (!can_msg || !can_msg->size) {
> > +			netdev_err(netdev, "got unsupported rec in usb msg:\n");
> > +			err = -ENOTSUPP;
> > +			break;
> > +		}
> > +
> > +		/* check if the record goes out of current packet */
> > +		if ((read_size + can_msg->size + 1) > urb->actual_length) {
> > +			netdev_err(netdev,
> > +			"got frag rec: should inc usb rx buf size\n");
> > +			err = -EBADMSG;
> > +			break;
> > +		}
> > +
> > +		msg_type = (le32_to_cpu(can_msg->flags) &
> > +			IXXAT_USBFD_MSG_FLAGS_TYPE);
> > +
> > +		switch (msg_type) {
> > +
> > +		case IXXAT_USBFD_CAN_DATA:
> > +			err = ixx_usbfd_handle_canmsg(dev, can_msg);
> > +			if (err < 0)
> > +				goto fail;
> > +			break;
> > +
> > +		case IXXAT_USBFD_CAN_STATUS:
> > +			err = ixx_usbfd_handle_status(dev, can_msg);
> > +			if (err < 0)
> > +				goto fail;
> > +			break;
> > +
> > +		case IXXAT_USBFD_CAN_ERROR:
> > +			err = ixx_usbfd_handle_error(dev, can_msg);
> > +			if (err < 0)
> > +				goto fail;
> > +			break;
> > +
> > +		case IXXAT_USBFD_CAN_TIMEOVR:
> > +			ixxat_usb_get_ts_tv(dev, can_msg->time, NULL);
> > +			break;
> > +
> > +		case IXXAT_USBFD_CAN_INFO:
> > +		case IXXAT_USBFD_CAN_WAKEUP:
> > +		case IXXAT_USBFD_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 (err)
> > +		ixxat_dump_mem("received msg", urb->transfer_buffer,
> > +				urb->actual_length);
> > +
> > +	return err;
> > +}
> > +
> > +static int ixx_usbfd_encode_msg(struct ixx_usb_device *dev, struct sk_buff *skb,
> > +		u8 *obuf, size_t *size)
> > +{
> > +	struct canfd_frame *cf = (struct canfd_frame *) skb->data;
> > +	struct ixx_can_msg_v2 can_msg = { 0 };
> > +
> > +	if (cf->can_id & CAN_RTR_FLAG)
> > +		can_msg.flags |= IXXAT_USBFD_MSG_FLAGS_RTR;
> > +
> > +	if (cf->can_id & CAN_EFF_FLAG) {
> > +		can_msg.flags |= IXXAT_USBFD_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 (dev->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT)
> > +		can_msg.flags |= IXXAT_USBFD_MSG_FLAGS_SSM;
> > +
> > +	if (skb->len == CANFD_MTU) {
> > +		can_msg.flags |= IXXAT_USBFD_MSG_FLAGS_EDL;
> > +
> > +		if (!(cf->can_id & CAN_RTR_FLAG) && (cf->flags & CANFD_BRS))
> > +			can_msg.flags |= IXXAT_USBFD_MSG_FLAGS_FDR;
> > +	}
> > +
> > +	can_msg.flags |= (can_len2dlc(cf->len) << 16) &
> > +		IXXAT_USBFD_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;
> > +
> > +	return 0;
> > +}
> > +
> > +static int ixx_usbfd_start(struct ixx_usb_device *dev)
> > +{
> > +	int err;
> > +	u32 time_ref = 0;
> > +	u8 can_opmode = IXXAT_USBFD_OPMODE_EXTENDED
> > +			| IXXAT_USBFD_OPMODE_STANDARD;
> > +	u8 can_exmode = 0;
> > +
> > +	if (dev->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
> > +		can_opmode |= IXXAT_USBFD_OPMODE_ERRFRAME;
> > +	if (dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
> > +		can_opmode |= IXXAT_USBFD_OPMODE_LISTONLY;
> > +
> > +	if ((CAN_CTRLMODE_FD | CAN_CTRLMODE_FD_NON_ISO) & dev->can.ctrlmode)
> > +		can_exmode |= IXXAT_USBFD_EXMODE_EXTDATA |
> > +			IXXAT_USBFD_EXMODE_FASTDATA;
> > +
> > +	if (!(CAN_CTRLMODE_FD_NON_ISO & dev->can.ctrlmode) && can_exmode)
> > +		can_exmode |= IXXAT_USBFD_EXMODE_ISOFD;
> > +
> > +	/* Try to reset the controller, in case it is already initalized
> > +	   from a previous unclean shutdown */
> > +	ixx_usbfd_reset_ctrl(dev);
> > +
> > +	err = ixx_usbfd_init_ctrl(dev, can_opmode,
> > +			can_exmode,
> > +			&dev->can.bittiming,
> > +			&dev->can.data_bittiming);
> > +	if (err)
> > +		return err;
> > +
> > +	/* opening first device: */
> > +	if (dev->ctrl_opened_count == 0) {
> > +		err = ixx_usbfd_start_ctrl(dev, &time_ref);
> > +		if (err)
> > +			return err;
> > +
> > +		ixxat_usb_set_ts_now(dev, time_ref);
> > +	}
> > +
> > +	dev->ctrl_opened_count++;
> > +
> > +	dev->bec.txerr = 0;
> > +	dev->bec.rxerr = 0;
> > +
> > +	return err;
> > +}
> > +
> > +/*
> > + * stop interface
> > + * (last chance before set bus off)
> > + */
> > +static int ixx_usbfd_stop(struct ixx_usb_device *dev)
> > +{
> > +	int err;
> > +
> > +	if (dev->ctrl_opened_count == 1) {
> > +		err = ixx_usbfd_stop_ctrl(dev);
> > +		if (err)
> > +			return err;
> > +	}
> > +
> > +	dev->ctrl_opened_count--;
> > +
> > +	return 0;
> > +}
> > +
> > +/*
> > + * called when probing to initialize a device object.
> > + */
> > +static int ixx_usbfd_init(struct ixx_usb_device *dev)
> > +{
> > +	dev->restart_task = kthread_run(&ixx_usbfd_restart_task, dev,
> > +			"restart_thread");
> > +	if (!dev->restart_task)
> > +		return -ENOBUFS;
> > +
> > +	return 0;
> > +}
> > +
> > +static void ixx_usbfd_exit(struct ixx_usb_device *dev)
> > +{
> > +	ixx_usbfd_reset_ctrl(dev);
> > +
> > +	dev->must_quit = 1;
> > +	dev->restart_flag = 1;
> > +	wake_up_interruptible(&dev->wait_queue);
> > +	if (dev->restart_task)
> > +		kthread_stop(dev->restart_task);
> > +}
> > +
> > +/*
> > + * probe function for new IXXAT USB-to-CAN FD interface
> > + */
> > +static int ixx_usbfd_probe(struct usb_interface *intf)
> > +{
> > +	struct usb_host_interface *if_desc;
> > +	int i;
> > +
> > +	if_desc = intf->altsetting;
> > +
> > +	/* check interface endpoint addresses */
> > +	for (i = 0; i < if_desc->desc.bNumEndpoints; i++) {
> > +		struct usb_endpoint_descriptor *ep = &if_desc->endpoint[i].desc;
> > +
> > +		/*
> > +		 * below is the list of valid ep addreses. Any other ep address
> > +		 * is considered as not-CAN interface address => no dev created
> > +		 */
> > +		switch (ep->bEndpointAddress) {
> > +		case IXXAT_USBFD_EP_MSGOUT_0:
> > +		case IXXAT_USBFD_EP_MSGOUT_1:
> > +		case IXXAT_USBFD_EP_MSGOUT_2:
> > +		case IXXAT_USBFD_EP_MSGOUT_3:
> > +		case IXXAT_USBFD_EP_MSGOUT_4:
> > +		case IXXAT_USBFD_EP_MSGIN_0:
> > +		case IXXAT_USBFD_EP_MSGIN_1:
> > +		case IXXAT_USBFD_EP_MSGIN_2:
> > +		case IXXAT_USBFD_EP_MSGIN_3:
> > +		case IXXAT_USBFD_EP_MSGIN_4:
> > +
> > +			break;
> > +		default:
> > +			return -ENODEV;
> > +		}
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int ixx_usbfd_get_dev_caps(struct usb_device *dev,
> > +		struct ixx_dev_caps *dev_caps)
> > +{
> > +	int err = -ENODEV, i;
> > +	u8 data[IXXAT_USBFD_CMD_BUFFER_SIZE] = { 0 };
> > +	struct ixx_usbfd_dev_caps_req *dev_caps_req;
> > +	struct ixx_usbfd_dev_caps_res *dev_caps_res;
> > +	u32 req_size = sizeof(*dev_caps_req);
> > +
> > +	dev_caps_req = (struct ixx_usbfd_dev_caps_req *) data;
> > +	dev_caps_res = (struct ixx_usbfd_dev_caps_res *)(data + req_size);
> > +
> > +	dev_caps_req->dal_req.req_size   = cpu_to_le32(req_size);
> > +	dev_caps_req->dal_req.req_code   =
> > +		cpu_to_le32(IXXAT_USBFD_BRD_GET_DEVCAPS_CMD);
> > +	dev_caps_req->dal_req.req_port   = 0xffff;
> > +	dev_caps_req->dal_req.req_socket = 0xffff;
> > +
> > +	dev_caps_res->dal_res.res_size = cpu_to_le32(
> > +		sizeof(*dev_caps_res));
> > +	dev_caps_res->dal_res.ret_size = 0;
> > +	dev_caps_res->dal_res.ret_code = 0xffffffff;
> > +
> > +	err = ixx_usbfd_send_cmd(dev, &dev_caps_req->dal_req);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	err = ixx_usbfd_rcv_cmd(dev, &dev_caps_res->dal_res,
> > +			0xffff);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	dev_caps->bus_ctrl_count =
> > +		le16_to_cpu(dev_caps_res->dev_caps.bus_ctrl_count);
> > +	for (i = 0; i < dev_caps->bus_ctrl_count; ++i)
> > +		dev_caps->bus_ctrl_types[i] =
> > +			le16_to_cpu(dev_caps_res->dev_caps.bus_ctrl_types[i]);
> > +
> > +	return 0;
> > +}
> > +
> > +static int ixx_usbfd_get_ctrl_caps(struct usb_device *dev,
> > +		struct ixx_ctrl_caps *ctrl_caps, int index)
> > +{
> > +	int err = -ENODEV;
> > +	u8 data[IXXAT_USBFD_CMD_BUFFER_SIZE] = { 0 };
> > +	struct ixx_usbfd_ctrl_caps_req *ctrl_caps_req;
> > +	struct ixx_usbfd_ctrl_caps_res *ctrl_caps_res;
> > +	u32 req_size = sizeof(*ctrl_caps_req);
> > +
> > +	ctrl_caps_req = (struct ixx_usbfd_ctrl_caps_req *) data;
> > +	ctrl_caps_res = (struct ixx_usbfd_ctrl_caps_res *)(data + req_size);
> > +
> > +	ctrl_caps_req->dal_req.req_size   = cpu_to_le32(req_size);
> > +	ctrl_caps_req->dal_req.req_code   =
> > +		cpu_to_le32(IXXAT_USBFD_CAN_GET_CAPS_CMD);
> > +	ctrl_caps_req->dal_req.req_port   = cpu_to_le16(index);
> > +	ctrl_caps_req->dal_req.req_socket = 0xffff;
> > +
> > +	ctrl_caps_res->dal_res.res_size =
> > +		cpu_to_le32(sizeof(*ctrl_caps_res));
> > +	ctrl_caps_res->dal_res.ret_size = 0;
> > +	ctrl_caps_res->dal_res.ret_code = 0xffffffff;
> > +
> > +	err = ixx_usbfd_send_cmd(dev, &ctrl_caps_req->dal_req);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	err = ixx_usbfd_rcv_cmd(dev,
> > +			&ctrl_caps_res->dal_res,
> > +			index);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	ctrl_caps->bus_coupling =
> > +		le16_to_cpu(ctrl_caps_res->ctrl_caps.bus_coupling);
> > +	ctrl_caps->clock_freq =
> > +		le32_to_cpu(ctrl_caps_res->ctrl_caps.clock_freq);
> > +	ctrl_caps->cms_divisor =
> > +		le32_to_cpu(ctrl_caps_res->ctrl_caps.cms_divisor);
> > +	ctrl_caps->cms_max_ticks =
> > +		le32_to_cpu(ctrl_caps_res->ctrl_caps.cms_max_ticks);
> > +	ctrl_caps->ctrl_type = le16_to_cpu(ctrl_caps_res->ctrl_caps.ctrl_type);
> > +	ctrl_caps->dtx_divisor =
> > +		le32_to_cpu(ctrl_caps_res->ctrl_caps.dtx_divisor);
> > +	ctrl_caps->dtx_max_ticks =
> > +		le32_to_cpu(ctrl_caps_res->ctrl_caps.dtx_max_ticks);
> > +	ctrl_caps->features = le32_to_cpu(ctrl_caps_res->ctrl_caps.features);
> > +	ctrl_caps->tsc_divisor =
> > +		le32_to_cpu(ctrl_caps_res->ctrl_caps.tsc_divisor);
> > +
> > +	return 0;
> > +}
> > +
> > +static int ixx_usbfd_get_fwinfo(struct ixx_usb_device *dev,
> > +		struct ixx_intf_fw_info *fwinfo)
> > +{
> > +	int err = -ENODEV;
> > +	u8 data[IXXAT_USBFD_CMD_BUFFER_SIZE] = { 0 };
> > +	struct ixx_usbfd_brd_get_fwinfo_req *fw_info_req;
> > +	struct ixx_usbfd_brd_get_fwinfo_res *fw_info_res;
> > +	u32 req_size = sizeof(*fw_info_req);
> > +
> > +	fw_info_req = (struct ixx_usbfd_brd_get_fwinfo_req *) data;
> > +	fw_info_res = (struct ixx_usbfd_brd_get_fwinfo_res *)(data + req_size);
> > +
> > +	fw_info_req->dal_req.req_size   = cpu_to_le32(req_size);
> > +	fw_info_req->dal_req.req_code   =
> > +		cpu_to_le32(IXXAT_USBFD_BRD_GET_FWINFO_CMD);
> > +	fw_info_req->dal_req.req_port   = 0xffff;
> > +	fw_info_req->dal_req.req_socket = 0xffff;
> > +
> > +	fw_info_res->dal_res.res_size =
> > +		cpu_to_le32(sizeof(*fw_info_res));
> > +	fw_info_res->dal_res.ret_size = 0;
> > +	fw_info_res->dal_res.ret_code = 0xffffffff;
> > +
> > +	err = ixx_usbfd_send_cmd(dev->udev, &fw_info_req->dal_req);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	err = ixx_usbfd_rcv_cmd(dev->udev,
> > +		&fw_info_res->dal_res, 0xffff);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	if (fwinfo) {
> > +		fwinfo->build_version = le16_to_cpu(
> > +			fw_info_res->fwinfo.build_version);
> > +		fwinfo->firmware_type = le32_to_cpu(
> > +			fw_info_res->fwinfo.firmware_type);
> > +		fwinfo->major_version = le16_to_cpu(
> > +			fw_info_res->fwinfo.major_version);
> > +		fwinfo->minor_version = le16_to_cpu(
> > +			fw_info_res->fwinfo.minor_version);
> > +		fwinfo->reserved = le16_to_cpu(fw_info_res->fwinfo.reserved);
> > +	}
> > +
> > +	return le32_to_cpu(fw_info_res->dal_res.ret_code);
> > +}
> > +
> > +static int ixx_usbfd_get_dev_info(struct ixx_usb_device *dev,
> > +		struct ixx_intf_info *dev_info)
> > +{
> > +	int err = -ENODEV;
> > +	u8 data[IXXAT_USBFD_CMD_BUFFER_SIZE] = { 0 };
> > +	struct ixx_usbfd_brd_get_intf_info_req *dev_info_req;
> > +	struct ixx_usbfd_brd_get_intf_info_res *dev_info_res;
> > +	u32 req_size = sizeof(*dev_info_req);
> > +
> > +	dev_info_req = (struct ixx_usbfd_brd_get_intf_info_req *) data;
> > +	dev_info_res =
> > +		(struct ixx_usbfd_brd_get_intf_info_res *)(data + req_size);
> > +
> > +	dev_info_req->dal_req.req_size = cpu_to_le32(req_size);
> > +	dev_info_req->dal_req.req_code =
> > +		cpu_to_le32(IXXAT_USBFD_BRD_GET_DEVINFO_CMD);
> > +	dev_info_req->dal_req.req_port = 0xffff;
> > +	dev_info_req->dal_req.req_socket = 0xffff;
> > +
> > +	dev_info_res->dal_res.res_size =
> > +		cpu_to_le32(sizeof(*dev_info_res));
> > +	dev_info_res->dal_res.ret_size = 0;
> > +	dev_info_res->dal_res.ret_code = 0xffffffff;
> > +
> > +	err = ixx_usbfd_send_cmd(dev->udev,
> > +			&dev_info_req->dal_req);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	err = ixx_usbfd_rcv_cmd(dev->udev,
> > +			&dev_info_res->dal_res,
> > +			0xffff);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	if (dev_info) {
> > +		memcpy(dev_info->device_id, &dev_info_res->info.device_id,
> > +			sizeof(dev_info_res->info.device_id));
> > +		memcpy(dev_info->device_name, &dev_info_res->info.device_name,
> > +			sizeof(dev_info_res->info.device_name));
> > +		dev_info->device_fpga_version = le16_to_cpu(
> > +			dev_info_res->info.device_fpga_version);
> > +		dev_info->device_version = le32_to_cpu(
> > +			dev_info_res->info.device_version);
> > +	}
> > +
> > +	return le32_to_cpu(dev_info_res->dal_res.ret_code);
> > +}
> > +
> > +/*
> > + * describes the USB-to-CAN FD automotive adapter
> > + */
> > +struct ixx_usb_adapter usb_to_can_fd_automotive = {
> > +	.name = "USB-to-CAN FD automotive",
> > +	.device_id = USB_TO_CAN_FD_AUTOMOTIVE_PRODUCT_ID,
> > +	.clock = {
> > +		.freq = IFIFD_CRYSTAL_HZ,
> > +	},
> > +
> > +	.bittiming_const = {
> > +		.name = "ifi_can",
> > +		.tseg1_min = 1,
> > +		.tseg1_max = 256,
> > +		.tseg2_min = 1,
> > +		.tseg2_max = 256,
> > +		.sjw_max = 128,
> > +		.brp_min = 2,
> > +		.brp_max = 513,
> > +		.brp_inc = 1, },
> > +
> > +	.data_bittiming_const = {
> > +		.name = "ifi_can",
> > +		.tseg1_min = 1,
> > +		.tseg1_max = 256,
> > +		.tseg2_min = 1,
> > +		.tseg2_max = 256,
> > +		.sjw_max = 128,
> > +		.brp_min = 2,
> > +		.brp_max = 513,
> > +		.brp_inc = 1, },
> > +
> > +	.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
> > +		CAN_CTRLMODE_LISTENONLY |
> > +		CAN_CTRLMODE_LOOPBACK |
> > +		CAN_CTRLMODE_BERR_REPORTING |
> > +		CAN_CTRLMODE_FD |
> > +		CAN_CTRLMODE_FD_NON_ISO,
> > +
> > +	/* size of device private data */
> > +	.sizeof_dev_private = sizeof(const struct ixx_usb_device),
> > +
> > +	/* give here messages in/out endpoints */
> > +	.ep_msg_in = {  IXXAT_USBFD_EP_MSGIN_0, IXXAT_USBFD_EP_MSGIN_1,
> > +			IXXAT_USBFD_EP_MSGIN_2, IXXAT_USBFD_EP_MSGIN_3,
> > +			IXXAT_USBFD_EP_MSGIN_4 },
> > +	.ep_msg_out = { IXXAT_USBFD_EP_MSGOUT_0, IXXAT_USBFD_EP_MSGOUT_1,
> > +			IXXAT_USBFD_EP_MSGOUT_2, IXXAT_USBFD_EP_MSGOUT_3,
> > +			IXXAT_USBFD_EP_MSGOUT_4 },
> > +
> > +	/* size of rx/tx usb buffers */
> > +	.rx_buffer_size = IXXAT_USBFD_RX_BUFFER_SIZE,
> > +	.tx_buffer_size = IXXAT_USBFD_TX_BUFFER_SIZE,
> > +
> > +	/* device callbacks */
> > +	.intf_probe = ixx_usbfd_probe,
> > +	.dev_get_dev_caps = ixx_usbfd_get_dev_caps,
> > +	.dev_get_ctrl_caps = ixx_usbfd_get_ctrl_caps,
> > +	.dev_init = ixx_usbfd_init,
> > +	.dev_exit = ixx_usbfd_exit,
> > +	.intf_get_info = ixx_usbfd_get_dev_info,
> > +	.intf_get_fw_info = ixx_usbfd_get_fwinfo,
> > +	.dev_decode_buf = ixx_usbfd_decode_buf,
> > +	.dev_encode_msg = ixx_usbfd_encode_msg,
> > +	.dev_start = ixx_usbfd_start,
> > +	.dev_stop = ixx_usbfd_stop,
> > +	.dev_power = ixx_usbfd_power_ctrl,
> > +	.has_bgi_ep = 1,
> > +};
> > +
> > +/*
> > + * describes the USB-to-CAN FD compact adapter
> > + */
> > +struct ixx_usb_adapter usb_to_can_fd_compact = {
> > +	.name = "USB-to-CAN FD compact",
> > +	.device_id = USB_TO_CAN_FD_COMPACT_PRODUCT_ID,
> > +	.clock = {
> > +		.freq = IFIFD_CRYSTAL_HZ,
> > +	},
> > +
> > +	.bittiming_const = {
> > +		.name = "ifi_can",
> > +		.tseg1_min = 1,
> > +		.tseg1_max = 256,
> > +		.tseg2_min = 1,
> > +		.tseg2_max = 256,
> > +		.sjw_max = 128,
> > +		.brp_min = 2,
> > +		.brp_max = 513,
> > +		.brp_inc = 1, },
> > +
> > +	.data_bittiming_const = {
> > +		.name = "ifi_can",
> > +		.tseg1_min = 1,
> > +		.tseg1_max = 256,
> > +		.tseg2_min = 1,
> > +		.tseg2_max = 256,
> > +		.sjw_max = 128,
> > +		.brp_min = 2,
> > +		.brp_max = 513,
> > +		.brp_inc = 1, },
> > +
> > +	.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
> > +		CAN_CTRLMODE_LISTENONLY |
> > +		CAN_CTRLMODE_LOOPBACK |
> > +		CAN_CTRLMODE_BERR_REPORTING |
> > +		CAN_CTRLMODE_FD |
> > +		CAN_CTRLMODE_FD_NON_ISO,
> > +
> > +	/* size of device private data */
> > +	.sizeof_dev_private = sizeof(const struct ixx_usb_device),
> > +
> > +	/* give here messages in/out endpoints */
> > +	.ep_msg_in = {  IXXAT_USBFD_EP_MSGIN_0, IXXAT_USBFD_EP_MSGIN_1,
> > +			IXXAT_USBFD_EP_MSGIN_2, IXXAT_USBFD_EP_MSGIN_3,
> > +			IXXAT_USBFD_EP_MSGIN_4 },
> > +	.ep_msg_out = { IXXAT_USBFD_EP_MSGOUT_0, IXXAT_USBFD_EP_MSGOUT_1,
> > +			IXXAT_USBFD_EP_MSGOUT_2, IXXAT_USBFD_EP_MSGOUT_3,
> > +			IXXAT_USBFD_EP_MSGOUT_4 },
> > +
> > +	/* size of rx/tx usb buffers */
> > +	.rx_buffer_size = IXXAT_USBFD_RX_BUFFER_SIZE,
> > +	.tx_buffer_size = IXXAT_USBFD_TX_BUFFER_SIZE,
> > +
> > +	/* device callbacks */
> > +	.intf_probe = ixx_usbfd_probe,
> > +	.dev_get_dev_caps = ixx_usbfd_get_dev_caps,
> > +	.dev_get_ctrl_caps = ixx_usbfd_get_ctrl_caps,
> > +	.dev_init = ixx_usbfd_init,
> > +	.dev_exit = ixx_usbfd_exit,
> > +	.intf_get_info = ixx_usbfd_get_dev_info,
> > +	.intf_get_fw_info = ixx_usbfd_get_fwinfo,
> > +	.dev_decode_buf = ixx_usbfd_decode_buf,
> > +	.dev_encode_msg = ixx_usbfd_encode_msg,
> > +	.dev_start = ixx_usbfd_start,
> > +	.dev_stop = ixx_usbfd_stop,
> > +	.dev_power = ixx_usbfd_power_ctrl,
> > +	.has_bgi_ep = 1,
> > +};
> > +
> > +/*
> > + * describes the DELL Edge GW3002
> > + */
> > +struct ixx_usb_adapter dell_edge_gw3002 = {
> > +	.name = "USB DELL Edge GW3002",
> > +	.device_id = DELL_EDGE_GW3002_PRODUCT_ID,
> > +	.clock = {
> > +		.freq = IFIFD_CRYSTAL_HZ,
> > +	},
> > +
> > +	.bittiming_const = {
> > +		.name = "mcan",
> > +		.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 = "mcan",
> > +		.tseg1_min = 1,
> > +		.tseg1_max = 16,
> > +		.tseg2_min = 1,
> > +		.tseg2_max = 8,
> > +		.sjw_max = 4,
> > +		.brp_min = 1,
> > +		.brp_max = 32,
> > +		.brp_inc = 1, },
> > +
> > +	.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
> > +		CAN_CTRLMODE_LISTENONLY |
> > +		CAN_CTRLMODE_LOOPBACK |
> > +		CAN_CTRLMODE_BERR_REPORTING
> > +
> > +		/* currently not supported
> > +		CAN_CTRLMODE_FD |
> > +		CAN_CTRLMODE_FD_NON_ISO
> > +		*/,
> > +
> > +	/* size of device private data */
> > +	.sizeof_dev_private = sizeof(const struct ixx_usb_device),
> > +
> > +	/* give here messages in/out endpoints */
> > +	.ep_msg_in = {  DELL_EDGE_GW3002_EP_MSGIN_0, DELL_EDGE_GW3002_EP_MSGIN_1,
> > +			DELL_EDGE_GW3002_EP_MSGIN_2, DELL_EDGE_GW3002_EP_MSGIN_3,
> > +			DELL_EDGE_GW3002_EP_MSGIN_4 },
> > +	.ep_msg_out = { DELL_EDGE_GW3002_EP_MSGOUT_0, DELL_EDGE_GW3002_EP_MSGOUT_1,
> > +			DELL_EDGE_GW3002_EP_MSGOUT_2, DELL_EDGE_GW3002_EP_MSGOUT_3,
> > +			DELL_EDGE_GW3002_EP_MSGOUT_4 },
> > +
> > +	/* size of rx/tx usb buffers */
> > +	.rx_buffer_size = IXXAT_USBFD_RX_BUFFER_SIZE,
> > +	.tx_buffer_size = IXXAT_USBFD_TX_BUFFER_SIZE,
> > +
> > +	/* device callbacks */
> > +	.intf_probe = ixx_usbfd_probe,
> > +	.dev_get_dev_caps = ixx_usbfd_get_dev_caps,
> > +	.dev_get_ctrl_caps = ixx_usbfd_get_ctrl_caps,
> > +	.dev_init = ixx_usbfd_init,
> > +	.dev_exit = ixx_usbfd_exit,
> > +	.intf_get_info = ixx_usbfd_get_dev_info,
> > +	.intf_get_fw_info = ixx_usbfd_get_fwinfo,
> > +	.dev_decode_buf = ixx_usbfd_decode_buf,
> > +	.dev_encode_msg = ixx_usbfd_encode_msg,
> > +	.dev_start = ixx_usbfd_start,
> > +	.dev_stop = ixx_usbfd_stop,
> > +	.dev_power = ixx_usbfd_power_ctrl,
> > +	.has_bgi_ep = 0,
> > +};
> > +
> > +/*
> > + * describes the USB-to-CAN FD professional adapter
> > + */
> > +struct ixx_usb_adapter usb_to_can_fd_professional = {
> > +	.name = "USB-to-CAN FD professional",
> > +	.device_id = USB_TO_CAN_FD_PROFESSIONAL_PRODUCT_ID,
> > +	.clock = {
> > +		.freq = IFIFD_CRYSTAL_HZ,
> > +	},
> > +
> > +	.bittiming_const = {
> > +		.name = "ifi_can",
> > +		.tseg1_min = 1,
> > +		.tseg1_max = 256,
> > +		.tseg2_min = 1,
> > +		.tseg2_max = 256,
> > +		.sjw_max = 128,
> > +		.brp_min = 2,
> > +		.brp_max = 513,
> > +		.brp_inc = 1, },
> > +
> > +	.data_bittiming_const = {
> > +		.name = "ifi_can",
> > +		.tseg1_min = 1,
> > +		.tseg1_max = 256,
> > +		.tseg2_min = 1,
> > +		.tseg2_max = 256,
> > +		.sjw_max = 128,
> > +		.brp_min = 2,
> > +		.brp_max = 513,
> > +		.brp_inc = 1, },
> > +
> > +	.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
> > +		CAN_CTRLMODE_LISTENONLY |
> > +		CAN_CTRLMODE_LOOPBACK |
> > +		CAN_CTRLMODE_BERR_REPORTING |
> > +		CAN_CTRLMODE_FD |
> > +		CAN_CTRLMODE_FD_NON_ISO,
> > +
> > +	/* size of device private data */
> > +	.sizeof_dev_private = sizeof(const struct ixx_usb_device),
> > +
> > +	/* give here messages in/out endpoints */
> > +	.ep_msg_in = {  IXXAT_USBFD_EP_MSGIN_0, IXXAT_USBFD_EP_MSGIN_1,
> > +			IXXAT_USBFD_EP_MSGIN_2, IXXAT_USBFD_EP_MSGIN_3,
> > +			IXXAT_USBFD_EP_MSGIN_4 },
> > +	.ep_msg_out = { IXXAT_USBFD_EP_MSGOUT_0, IXXAT_USBFD_EP_MSGOUT_1,
> > +			IXXAT_USBFD_EP_MSGOUT_2, IXXAT_USBFD_EP_MSGOUT_3,
> > +			IXXAT_USBFD_EP_MSGOUT_4 },
> > +
> > +	/* size of rx/tx usb buffers */
> > +	.rx_buffer_size = IXXAT_USBFD_RX_BUFFER_SIZE,
> > +	.tx_buffer_size = IXXAT_USBFD_TX_BUFFER_SIZE,
> > +
> > +	/* device callbacks */
> > +	.intf_probe = ixx_usbfd_probe,
> > +	.dev_get_dev_caps = ixx_usbfd_get_dev_caps,
> > +	.dev_get_ctrl_caps = ixx_usbfd_get_ctrl_caps,
> > +	.dev_init = ixx_usbfd_init,
> > +	.dev_exit = ixx_usbfd_exit,
> > +	.intf_get_info = ixx_usbfd_get_dev_info,
> > +	.intf_get_fw_info = ixx_usbfd_get_fwinfo,
> > +	.dev_decode_buf = ixx_usbfd_decode_buf,
> > +	.dev_encode_msg = ixx_usbfd_encode_msg,
> > +	.dev_start = ixx_usbfd_start,
> > +	.dev_stop = ixx_usbfd_stop,
> > +	.dev_power = ixx_usbfd_power_ctrl,
> > +	.has_bgi_ep = 1,
> > +};
> > +
> > +/*
> > + * describes the USB-to-CAN FD PCIe mini adapter
> > + */
> > +struct ixx_usb_adapter usb_to_can_fd_pcie_mini = {
> > +	.name = "USB-to-CAN FD PCIe mini",
> > +	.device_id = USB_TO_CAN_FD_PCIE_MINI_PRODUCT_ID,
> > +	.clock = {
> > +		.freq = IFIFD_CRYSTAL_HZ,
> > +	},
> > +
> > +	.bittiming_const = {
> > +		.name = "ifi_can",
> > +		.tseg1_min = 1,
> > +		.tseg1_max = 256,
> > +		.tseg2_min = 1,
> > +		.tseg2_max = 256,
> > +		.sjw_max = 128,
> > +		.brp_min = 2,
> > +		.brp_max = 513,
> > +		.brp_inc = 1, },
> > +
> > +	.data_bittiming_const = {
> > +		.name = "ifi_can",
> > +		.tseg1_min = 1,
> > +		.tseg1_max = 256,
> > +		.tseg2_min = 1,
> > +		.tseg2_max = 256,
> > +		.sjw_max = 128,
> > +		.brp_min = 2,
> > +		.brp_max = 513,
> > +		.brp_inc = 1, },
> > +
> > +	.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
> > +		CAN_CTRLMODE_LISTENONLY |
> > +		CAN_CTRLMODE_LOOPBACK |
> > +		CAN_CTRLMODE_BERR_REPORTING |
> > +		CAN_CTRLMODE_FD |
> > +		CAN_CTRLMODE_FD_NON_ISO,
> > +
> > +	/* size of device private data */
> > +	.sizeof_dev_private = sizeof(const struct ixx_usb_device),
> > +
> > +	/* give here messages in/out endpoints */
> > +	.ep_msg_in = {  IXXAT_USBFD_EP_MSGIN_0, IXXAT_USBFD_EP_MSGIN_1,
> > +			IXXAT_USBFD_EP_MSGIN_2, IXXAT_USBFD_EP_MSGIN_3,
> > +			IXXAT_USBFD_EP_MSGIN_4 },
> > +	.ep_msg_out = { IXXAT_USBFD_EP_MSGOUT_0, IXXAT_USBFD_EP_MSGOUT_1,
> > +			IXXAT_USBFD_EP_MSGOUT_2, IXXAT_USBFD_EP_MSGOUT_3,
> > +			IXXAT_USBFD_EP_MSGOUT_4 },
> > +
> > +	/* size of rx/tx usb buffers */
> > +	.rx_buffer_size = IXXAT_USBFD_RX_BUFFER_SIZE,
> > +	.tx_buffer_size = IXXAT_USBFD_TX_BUFFER_SIZE,
> > +
> > +	/* device callbacks */
> > +	.intf_probe = ixx_usbfd_probe,
> > +	.dev_get_dev_caps = ixx_usbfd_get_dev_caps,
> > +	.dev_get_ctrl_caps = ixx_usbfd_get_ctrl_caps,
> > +	.dev_init = ixx_usbfd_init,
> > +	.dev_exit = ixx_usbfd_exit,
> > +	.intf_get_info = ixx_usbfd_get_dev_info,
> > +	.intf_get_fw_info = ixx_usbfd_get_fwinfo,
> > +	.dev_decode_buf = ixx_usbfd_decode_buf,
> > +	.dev_encode_msg = ixx_usbfd_encode_msg,
> > +	.dev_start = ixx_usbfd_start,
> > +	.dev_stop = ixx_usbfd_stop,
> > +	.dev_power = ixx_usbfd_power_ctrl,
> > +	.has_bgi_ep = 1,
> > +};
> > +
> > +/*
> > + * describes the USB-to-CAR adapter
> > + */
> > +struct ixx_usb_adapter usb_to_car = {
> > +	.name = "USB-to-CAR",
> > +	.device_id = USB_TO_CAR_ID,
> > +	.clock = {
> > +		.freq = IFIFD_CRYSTAL_HZ,
> > +	},
> > +
> > +	.bittiming_const = {
> > +		.name = "ifi_can",
> > +		.tseg1_min = 1,
> > +		.tseg1_max = 256,
> > +		.tseg2_min = 1,
> > +		.tseg2_max = 256,
> > +		.sjw_max = 128,
> > +		.brp_min = 2,
> > +		.brp_max = 513,
> > +		.brp_inc = 1, },
> > +
> > +	.data_bittiming_const = {
> > +		.name = "ifi_can",
> > +		.tseg1_min = 1,
> > +		.tseg1_max = 256,
> > +		.tseg2_min = 1,
> > +		.tseg2_max = 256,
> > +		.sjw_max = 128,
> > +		.brp_min = 2,
> > +		.brp_max = 513,
> > +		.brp_inc = 1, },
> > +
> > +	.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
> > +		CAN_CTRLMODE_LISTENONLY |
> > +		CAN_CTRLMODE_LOOPBACK |
> > +		CAN_CTRLMODE_BERR_REPORTING |
> > +		CAN_CTRLMODE_FD |
> > +		CAN_CTRLMODE_FD_NON_ISO,
> > +
> > +	/* size of device private data */
> > +	.sizeof_dev_private = sizeof(const struct ixx_usb_device),
> > +
> > +	/* give here messages in/out endpoints */
> > +	.ep_msg_in = {  IXXAT_USBFD_EP_MSGIN_0, IXXAT_USBFD_EP_MSGIN_1,
> > +			IXXAT_USBFD_EP_MSGIN_2, IXXAT_USBFD_EP_MSGIN_3,
> > +			IXXAT_USBFD_EP_MSGIN_4 },
> > +	.ep_msg_out = { IXXAT_USBFD_EP_MSGOUT_0, IXXAT_USBFD_EP_MSGOUT_1,
> > +			IXXAT_USBFD_EP_MSGOUT_2, IXXAT_USBFD_EP_MSGOUT_3,
> > +			IXXAT_USBFD_EP_MSGOUT_4 },
> > +
> > +	/* size of rx/tx usb buffers */
> > +	.rx_buffer_size = IXXAT_USBFD_RX_BUFFER_SIZE,
> > +	.tx_buffer_size = IXXAT_USBFD_TX_BUFFER_SIZE,
> > +
> > +	/* device callbacks */
> > +	.intf_probe = ixx_usbfd_probe,
> > +	.dev_get_dev_caps = ixx_usbfd_get_dev_caps,
> > +	.dev_get_ctrl_caps = ixx_usbfd_get_ctrl_caps,
> > +	.dev_init = ixx_usbfd_init,
> > +	.dev_exit = ixx_usbfd_exit,
> > +	.intf_get_info = ixx_usbfd_get_dev_info,
> > +	.intf_get_fw_info = ixx_usbfd_get_fwinfo,
> > +	.dev_decode_buf = ixx_usbfd_decode_buf,
> > +	.dev_encode_msg = ixx_usbfd_encode_msg,
> > +	.dev_start = ixx_usbfd_start,
> > +	.dev_stop = ixx_usbfd_stop,
> > +	.dev_power = ixx_usbfd_power_ctrl,
> > +	.has_bgi_ep = 1,
> > +};
> > +
> > +
> > +#endif // CANFD_CAPABLE
> > diff --git a/ubuntu/ixxat/ixx_usb_v2.c b/ubuntu/ixxat/ixx_usb_v2.c
> > new file mode 100644
> > index 000000000000..3fe639c5b817
> > --- /dev/null
> > +++ b/ubuntu/ixxat/ixx_usb_v2.c
> > @@ -0,0 +1,1450 @@
> > +/*
> > + * CAN driver for IXXAT USB-to-CAN V2
> > + *
> > + * Copyright (C) 2014 Michael Hengler <mhengler@ixxat.de>
> > + *
> > + * Based on code originally by pcan_usb_core
> > + *
> > + * 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/delay.h>
> > +#include <linux/can.h>
> > +#include <linux/can/dev.h>
> > +#include <linux/can/error.h>
> > +#include <linux/kthread.h>
> > +#include <linux/sched.h>
> > +#include <linux/wait.h>
> > +#include <linux/types.h>
> > +#include <linux/gfp.h>
> > +#include <asm-generic/errno.h>
> > +#include <stdarg.h>
> > +
> > +#include "ixx_usb_core.h"
> > +
> > +MODULE_SUPPORTED_DEVICE("IXXAT Automation GmbH USB-to-CAN V2");
> > +
> > +/* use sja 1000 clock due to internal bittiming calculations */
> > +#define SJA1000_CRYSTAL_HZ	      8000000
> > +
> > +/* usb-to-can v2 Endpoints */
> > +#define IXXAT_USBV2_EP_CMDOUT   0
> > +#define IXXAT_USBV2_EP_CMDIN    (IXXAT_USBV2_EP_CMDOUT | USB_DIR_IN)
> > +#define IXXAT_USBV2_EP_MSGOUT_0 1
> > +#define IXXAT_USBV2_EP_MSGIN_0  (IXXAT_USBV2_EP_MSGOUT_0 | USB_DIR_IN)
> > +#define IXXAT_USBV2_EP_MSGOUT_1 2
> > +#define IXXAT_USBV2_EP_MSGIN_1  (IXXAT_USBV2_EP_MSGOUT_1 | USB_DIR_IN)
> > +#define IXXAT_USBV2_EP_MSGOUT_2 3
> > +#define IXXAT_USBV2_EP_MSGIN_2  (IXXAT_USBV2_EP_MSGOUT_2 | USB_DIR_IN)
> > +#define IXXAT_USBV2_EP_MSGOUT_3 4
> > +#define IXXAT_USBV2_EP_MSGIN_3  (IXXAT_USBV2_EP_MSGOUT_3 | USB_DIR_IN)
> > +#define IXXAT_USBV2_EP_MSGOUT_4 5
> > +#define IXXAT_USBV2_EP_MSGIN_4  (IXXAT_USBV2_EP_MSGOUT_4 | USB_DIR_IN)
> > +
> > +/* usb-to-can v2 rx/tx buffers size */
> > +#define IXXAT_USBV2_RX_BUFFER_SIZE      512
> > +#define IXXAT_USBV2_TX_BUFFER_SIZE      256
> > +
> > +#define IXXAT_USBV2_CMD_BUFFER_SIZE     256
> > +
> > +#define IXXAT_USBV2_OPMODE_STANDARD  0x01 /* reception of 11-bit id messages */
> > +#define IXXAT_USBV2_OPMODE_EXTENDED  0x02 /* reception of 29-bit id messages */
> > +#define IXXAT_USBV2_OPMODE_ERRFRAME  0x04 /* enable reception of error frames */
> > +#define IXXAT_USBV2_OPMODE_LISTONLY  0x08 /* listen only mode (TX passive) */
> > +
> > +/* Stuff error */
> > +#define IXXAT_USBV2_CAN_ERROR_STUFF      1
> > +/* Form error */
> > +#define IXXAT_USBV2_CAN_ERROR_FORM       2
> > +/* Acknowledgment error */
> > +#define IXXAT_USBV2_CAN_ERROR_ACK        3
> > +/* Bit error */
> > +#define IXXAT_USBV2_CAN_ERROR_BIT        4
> > +/* CRC error */
> > +#define IXXAT_USBV2_CAN_ERROR_CRC        6
> > +/* Other (unspecified) error */
> > +#define IXXAT_USBV2_CAN_ERROR_OTHER      7
> > +
> > +/* Data overrun occurred */
> > +#define IXXAT_USBV2_CAN_STATUS_OVRRUN    0x02
> > +/* Error warning limit exceeded */
> > +#define IXXAT_USBV2_CAN_STATUS_ERRLIM    0x04
> > +/* Bus off status */
> > +#define IXXAT_USBV2_CAN_STATUS_BUSOFF    0x08
> > +
> > +#define IXXAT_USBV2_CAN_DATA             0x00
> > +#define IXXAT_USBV2_CAN_INFO             0x01
> > +#define IXXAT_USBV2_CAN_ERROR            0x02
> > +#define IXXAT_USBV2_CAN_STATUS           0x03
> > +#define IXXAT_USBV2_CAN_WAKEUP           0x04
> > +#define IXXAT_USBV2_CAN_TIMEOVR          0x05
> > +#define IXXAT_USBV2_CAN_TIMERST          0x06
> > +
> > +#define IXXAT_USBV2_MSG_FLAGS_TYPE       0x000000FF
> > +#define IXXAT_USBV2_MSG_FLAGS_SSM        0x00000100
> > +#define IXXAT_USBV2_MSG_FLAGS_HPM        0x00000600
> > +#define IXXAT_USBV2_MSG_FLAGS_RES        0x0000F800
> > +#define IXXAT_USBV2_MSG_FLAGS_DLC        0x000F0000
> > +#define IXXAT_USBV2_MSG_FLAGS_OVR        0x00100000
> > +#define IXXAT_USBV2_MSG_FLAGS_SRR        0x00200000
> > +#define IXXAT_USBV2_MSG_FLAGS_RTR        0x00400000
> > +#define IXXAT_USBV2_MSG_FLAGS_EXT        0x00800000
> > +#define IXXAT_USBV2_MSG_FLAGS_AFC        0xFF000000
> > +
> > +#define IXXAT_USBV2_BAL_CMD_CLASS        3
> > +#define IXXAT_USBV2_BRD_CMD_CLASS        4
> > +#define IXXAT_USBV2_BMG_CMD_CLASS        5
> > +
> > +#define IXXAT_USBV2_BRD_CMD_CAT          0
> > +#define IXXAT_USBV2_CAN_CMD_CAT          1
> > +
> > +#define IXXAT_USBV2_VCI_CMD_CODE(Class, Function) \
> > +	((u32) (((Class) << 8) | (Function)))
> > +
> > +#define IXXAT_USBV2_BRD_CMD_CODE(Category, Function) \
> > +	IXXAT_USBV2_VCI_CMD_CODE(IXXAT_USBV2_BRD_CMD_CLASS, \
> > +		((Category) << 5) | (Function))
> > +
> > +#define IXXAT_USBV2_BAL_CMD_CODE(Category, Function) \
> > +	IXXAT_USBV2_VCI_CMD_CODE(IXXAT_USBV2_BAL_CMD_CLASS, \
> > +		((Category) << 5) | (Function))
> > +
> > +#define IXXAT_USBV2_CAN_GET_CAPS_CMD \
> > +	IXXAT_USBV2_BAL_CMD_CODE(IXXAT_USBV2_CAN_CMD_CAT, 0)
> > +#define IXXAT_USBV2_CAN_INIT_CMD \
> > +	IXXAT_USBV2_BAL_CMD_CODE(IXXAT_USBV2_CAN_CMD_CAT, 5)
> > +#define IXXAT_USBV2_CAN_START_CMD \
> > +	IXXAT_USBV2_BAL_CMD_CODE(IXXAT_USBV2_CAN_CMD_CAT, 6)
> > +#define IXXAT_USBV2_CAN_STOP_CMD \
> > +	IXXAT_USBV2_BAL_CMD_CODE(IXXAT_USBV2_CAN_CMD_CAT, 7)
> > +#define IXXAT_USBV2_CAN_RESET_CMD \
> > +	IXXAT_USBV2_BAL_CMD_CODE(IXXAT_USBV2_CAN_CMD_CAT, 8)
> > +
> > +#define IXXAT_USBV2_BRD_GET_FWINFO_CMD \
> > +	IXXAT_USBV2_BRD_CMD_CODE(IXXAT_USBV2_BRD_CMD_CAT, 0)
> > +#define IXXAT_USBV2_BRD_GET_DEVCAPS_CMD \
> > +	IXXAT_USBV2_BRD_CMD_CODE(IXXAT_USBV2_BRD_CMD_CAT, 1)
> > +#define IXXAT_USBV2_BRD_GET_DEVINFO_CMD \
> > +	IXXAT_USBV2_BRD_CMD_CODE(IXXAT_USBV2_BRD_CMD_CAT, 2)
> > +
> > +struct ixx_usbv2_dal_req {
> > +	u32 req_size;
> > +	u16 req_port;
> > +	u16 req_socket;
> > +	u32 req_code;
> > +} __packed;
> > +
> > +struct ixx_usbv2_dal_res {
> > +	u32 res_size;
> > +	u32 ret_size;
> > +	u32 ret_code;
> > +} __packed;
> > +
> > +struct ixx_usbv2_dev_caps_req {
> > +	struct ixx_usbv2_dal_req dal_req;
> > +} __packed;
> > +
> > +struct ixx_usbv2_dev_caps_res {
> > +	struct ixx_usbv2_dal_res dal_res;
> > +	struct ixx_dev_caps dev_caps;
> > +} __packed;
> > +
> > +struct ixx_usbv2_ctrl_caps_req {
> > +	struct ixx_usbv2_dal_req dal_req;
> > +} __packed;
> > +
> > +struct ixx_usbv2_ctrl_caps_res {
> > +	struct ixx_usbv2_dal_res dal_res;
> > +	struct ixx_ctrl_caps ctrl_caps;
> > +} __packed;
> > +
> > +struct ixx_usbv2_ctrl_init_req {
> > +	struct ixx_usbv2_dal_req dal_req;
> > +	u8 mode;
> > +	u8 btr0;
> > +	u8 btr1;
> > +	u8 padding;
> > +} __packed;
> > +
> > +struct ixx_usbv2_ctrl_init_res {
> > +	struct ixx_usbv2_dal_res dal_res;
> > +} __packed;
> > +
> > +struct ixx_usbv2_ctrl_start_req {
> > +	struct ixx_usbv2_dal_req dal_req;
> > +} __packed;
> > +
> > +struct ixx_usbv2_ctrl_start_res {
> > +	struct ixx_usbv2_dal_res dal_res;
> > +	u32 start_time;
> > +} __packed;
> > +
> > +struct ixx_usbv2_ctrl_stop_req {
> > +	struct ixx_usbv2_dal_req dal_req;
> > +	u32 action;
> > +} __packed;
> > +
> > +struct ixx_usbv2_ctrl_stop_res {
> > +	struct ixx_usbv2_dal_res dal_res;
> > +} __packed;
> > +
> > +struct ixx_usbv2_brd_get_fwinfo_req {
> > +	struct ixx_usbv2_dal_req dal_req;
> > +} __packed;
> > +
> > +struct ixx_usbv2_brd_get_fwinfo_res {
> > +	struct ixx_usbv2_dal_res dal_res;
> > +	struct ixx_intf_fw_info fwinfo;
> > +} __packed;
> > +
> > +struct ixx_usbv2_brd_get_intf_info_req {
> > +	struct ixx_usbv2_dal_req dal_req;
> > +} __packed;
> > +
> > +struct ixx_usbv2_brd_get_intf_info_res {
> > +	struct ixx_usbv2_dal_res dal_res;
> > +	struct ixx_intf_info info;
> > +} __packed;
> > +
> > +/*
> > + * send usb-to-can v2 command synchronously
> > + */
> > +static int ixx_usbv2_send_cmd(struct usb_device *dev,
> > +		struct ixx_usbv2_dal_req *dal_req)
> > +{
> > +	int err, i;
> > +	u16 size, value;
> > +	u8  request, 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) +
> > +		sizeof(const struct ixx_usbv2_dal_res);
> > +
> > +	buf = kmalloc(size, GFP_KERNEL);
> > +	if(!buf)
> > +		return -ENOMEM;
> > +	memcpy(buf, (u8 *)dal_req, size);
> > +
> > +
> > +	for (i = 0; i < 10; ++i) {
> > +		err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
> > +			requesttype,
> > +			value,
> > +			0,
> > +			buf,
> > +			size,
> > +			msecs_to_jiffies(50));
> > +
> > +		if (err < 0)
> > +			msleep(20);
> > +		else
> > +			break;
> > +	}
> > +
> > +	kfree(buf);
> > +
> > +	if (err < 0) {
> > +		dev_err(&dev->dev, "sending command failure: %d\n", err);
> > +		return err;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +/*
> > + * receive usb-to-can v2 command synchronously
> > + */
> > +static int ixx_usbv2_rcv_cmd(struct usb_device *dev,
> > +		struct ixx_usbv2_dal_res *dal_res, int value)
> > +{
> > +	int err, res_size, i, size_to_read;
> > +	u8  request, requesttype;
> > +	u8 *buf;
> > +
> > +	request      = 0xff;
> > +	requesttype  = 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 < 10; ++i) {
> > +		err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request,
> > +				requesttype, value,
> > +				0, buf + (u8) res_size,
> > +				size_to_read - res_size, msecs_to_jiffies(50));
> > +
> > +		if (err < 0) {
> > +			msleep(20);
> > +			continue;
> > +		}
> > +
> > +		res_size += err;
> > +		if (res_size < size_to_read)
> > +			msleep(20);
> > +		else
> > +			break;
> > +	}
> > +
> > +	if (res_size != size_to_read)
> > +		err = -EBADMSG;
> > +
> > +	if (err < 0) {
> > +		dev_err(&dev->dev, "receiving command failure: %d\n", err);
> > +		kfree(buf);
> > +		return err;
> > +	}
> > +
> > +	memcpy((u8 *)dal_res, buf, size_to_read);
> > +	kfree(buf);
> > +
> > +	return err;
> > +}
> > +
> > +static int ixx_usbv2_init_ctrl(struct ixx_usb_device *dev, u8 mode, u8 btr0,
> > +		u8 btr1)
> > +{
> > +	int err = -ENODEV;
> > +	u8 data[IXXAT_USBV2_CMD_BUFFER_SIZE] = { 0 };
> > +	struct ixx_usbv2_ctrl_init_req *ctrl_init_req;
> > +	struct ixx_usbv2_ctrl_init_res *ctrl_init_res;
> > +	u32 req_size = sizeof(*ctrl_init_req);
> > +
> > +	ctrl_init_req = (struct ixx_usbv2_ctrl_init_req *) data;
> > +	ctrl_init_res = (struct ixx_usbv2_ctrl_init_res *)(data + req_size);
> > +
> > +	ctrl_init_req->dal_req.req_size   = cpu_to_le32(req_size);
> > +	ctrl_init_req->dal_req.req_code   =
> > +		cpu_to_le32(IXXAT_USBV2_CAN_INIT_CMD);
> > +	ctrl_init_req->dal_req.req_port   = cpu_to_le16(dev->ctrl_idx);
> > +	ctrl_init_req->dal_req.req_socket = 0xffff;
> > +	ctrl_init_req->mode               = mode;
> > +	ctrl_init_req->btr0               = btr0;
> > +	ctrl_init_req->btr1               = btr1;
> > +
> > +	ctrl_init_res->dal_res.res_size = cpu_to_le32(
> > +		sizeof(*ctrl_init_res));
> > +	ctrl_init_res->dal_res.ret_size = 0;
> > +	ctrl_init_res->dal_res.ret_code = 0xffffffff;
> > +
> > +	err = ixx_usbv2_send_cmd(dev->udev, &ctrl_init_req->dal_req);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	err = ixx_usbv2_rcv_cmd(dev->udev,
> > +		&ctrl_init_res->dal_res,
> > +		dev->ctrl_idx);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	return le32_to_cpu(ctrl_init_res->dal_res.ret_code);
> > +}
> > +
> > +static int ixx_usbv2_start_ctrl(struct ixx_usb_device *dev, u32 *time_ref)
> > +{
> > +	int err = -ENODEV;
> > +	u8 data[IXXAT_USBV2_CMD_BUFFER_SIZE] = { 0 };
> > +	struct ixx_usbv2_ctrl_start_req *ctrl_start_req;
> > +	struct ixx_usbv2_ctrl_start_res *ctrl_start_res;
> > +	u32 req_size = sizeof(*ctrl_start_req);
> > +
> > +	ctrl_start_req = (struct ixx_usbv2_ctrl_start_req *) data;
> > +	ctrl_start_res = (struct ixx_usbv2_ctrl_start_res *)(data + req_size);
> > +
> > +	ctrl_start_req->dal_req.req_size   = cpu_to_le32(req_size);
> > +	ctrl_start_req->dal_req.req_code   =
> > +		cpu_to_le32(IXXAT_USBV2_CAN_START_CMD);
> > +	ctrl_start_req->dal_req.req_port   = cpu_to_le16(dev->ctrl_idx);
> > +	ctrl_start_req->dal_req.req_socket = 0xffff;
> > +
> > +	ctrl_start_res->dal_res.res_size = cpu_to_le32(
> > +		sizeof(*ctrl_start_res));
> > +	ctrl_start_res->dal_res.ret_size = 0;
> > +	ctrl_start_res->dal_res.ret_code = 0xffffffff;
> > +	ctrl_start_res->start_time       = 0;
> > +
> > +	err = ixx_usbv2_send_cmd(dev->udev, &ctrl_start_req->dal_req);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	err = ixx_usbv2_rcv_cmd(dev->udev,
> > +		&ctrl_start_res->dal_res,
> > +		dev->ctrl_idx);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	if (time_ref)
> > +		*time_ref = le32_to_cpu(ctrl_start_res->start_time);
> > +
> > +	return le32_to_cpu(ctrl_start_res->dal_res.ret_code);
> > +}
> > +
> > +static int ixx_usbv2_stop_ctrl(struct ixx_usb_device *dev)
> > +{
> > +	int err = -ENODEV;
> > +	u8 data[IXXAT_USBV2_CMD_BUFFER_SIZE] = { 0 };
> > +	struct ixx_usbv2_ctrl_stop_req *ctrl_stop_req;
> > +	struct ixx_usbv2_ctrl_stop_res *ctrl_stop_res;
> > +	u32 req_size = sizeof(struct ixx_usbv2_ctrl_stop_req);
> > +
> > +	ctrl_stop_req = (struct ixx_usbv2_ctrl_stop_req *) data;
> > +	ctrl_stop_res = (struct ixx_usbv2_ctrl_stop_res *)(data + req_size);
> > +
> > +	ctrl_stop_req->dal_req.req_size   = cpu_to_le32(req_size);
> > +	ctrl_stop_req->dal_req.req_code   =
> > +		cpu_to_le32(IXXAT_USBV2_CAN_STOP_CMD);
> > +	ctrl_stop_req->dal_req.req_port   = cpu_to_le16(dev->ctrl_idx);
> > +	ctrl_stop_req->dal_req.req_socket = 0xffff;
> > +	ctrl_stop_req->action             = cpu_to_le32(0x3);
> > +
> > +	ctrl_stop_res->dal_res.res_size =
> > +		cpu_to_le32(sizeof(*ctrl_stop_res));
> > +	ctrl_stop_res->dal_res.ret_size = 0;
> > +	ctrl_stop_res->dal_res.ret_code = 0xffffffff;
> > +
> > +	err = ixx_usbv2_send_cmd(dev->udev, &ctrl_stop_req->dal_req);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	err = ixx_usbv2_rcv_cmd(dev->udev,
> > +		&ctrl_stop_res->dal_res,
> > +		dev->ctrl_idx);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	if (!le32_to_cpu(ctrl_stop_res->dal_res.ret_code))
> > +		dev->can.state = CAN_STATE_STOPPED;
> > +
> > +	return le32_to_cpu(ctrl_stop_res->dal_res.ret_code);
> > +}
> > +
> > +static int ixx_usbv2_reset_ctrl(struct ixx_usb_device *dev)
> > +{
> > +	int err = -ENODEV;
> > +	u8 data[IXXAT_USBV2_CMD_BUFFER_SIZE] = { 0 };
> > +	struct ixx_usbv2_dal_req *dal_req;
> > +	struct ixx_usbv2_dal_res *dal_res;
> > +	u32 req_size = sizeof(*dal_req);
> > +
> > +	dal_req = (struct ixx_usbv2_dal_req *) data;
> > +	dal_res = (struct ixx_usbv2_dal_res *)(data + req_size);
> > +
> > +	dal_req->req_size   = cpu_to_le32(req_size);
> > +	dal_req->req_code   = cpu_to_le32(IXXAT_USBV2_CAN_RESET_CMD);
> > +	dal_req->req_port   = cpu_to_le16(dev->ctrl_idx);
> > +	dal_req->req_socket = 0xffff;
> > +
> > +	dal_res->res_size = cpu_to_le32(sizeof(*dal_res));
> > +	dal_res->ret_size = 0;
> > +	dal_res->ret_code = 0xffffffff;
> > +
> > +	err = ixx_usbv2_send_cmd(dev->udev, dal_req);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	err = ixx_usbv2_rcv_cmd(dev->udev, dal_res,
> > +			dev->ctrl_idx);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	return le32_to_cpu(dal_res->ret_code);
> > +}
> > +
> > +static int ixx_usbv2_set_bittiming(struct ixx_usb_device *dev,
> > +		struct can_bittiming *bt)
> > +{
> > +	u8 btr0 = 0, btr1 = 0, can_opmode = 0;
> > +
> > +	btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6);
> > +	btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf)
> > +			| (((bt->phase_seg2 - 1) & 0x7) << 4);
> > +	if (dev->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
> > +		btr1 |= 0x80;
> > +
> > +	can_opmode = IXXAT_USBV2_OPMODE_EXTENDED | IXXAT_USBV2_OPMODE_STANDARD;
> > +
> > +	if (dev->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
> > +		can_opmode |= IXXAT_USBV2_OPMODE_ERRFRAME;
> > +	if (dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
> > +		can_opmode |= IXXAT_USBV2_OPMODE_LISTONLY;
> > +
> > +	dev->btr0 = btr0;
> > +	dev->btr1 = btr1;
> > +
> > +	netdev_dbg(dev->netdev, "setting btr0=0x%08x btr1=0x%08x mode=0x%08x\n",
> > +			btr0, btr1, can_opmode);
> > +
> > +	return ixx_usbv2_init_ctrl(dev, can_opmode, btr0, btr1);
> > +}
> > +
> > +/*
> > + * handle restart but in asynchronously way
> > + */
> > +static int ixx_usbv2_restart_task(void *user_data)
> > +{
> > +	u32 time_ref;
> > +	struct ixx_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) {
> > +				ixx_usbv2_stop_ctrl(dev);
> > +				ixx_usbv2_start_ctrl(dev, &time_ref);
> > +				dev->restart_flag = 0;
> > +				dev->can.state = CAN_STATE_ERROR_ACTIVE;
> > +			}
> > +		} else
> > +			msleep(20);
> > +	}
> > +	return 0;
> > +}
> > +
> > +static int ixx_usbv2_handle_canmsg(struct ixx_usb_device *dev,
> > +		struct ixx_can_msg *rx)
> > +{
> > +	struct net_device *netdev = dev->netdev;
> > +	struct can_frame *can_frame;
> > +	struct sk_buff *skb;
> > +
> > +	skb = alloc_can_skb(netdev, &can_frame);
> > +	if (!skb)
> > +		return -ENOMEM;
> > +
> > +	if (le32_to_cpu(rx->flags) & IXXAT_USBV2_MSG_FLAGS_OVR) {
> > +		netdev->stats.rx_over_errors++;
> > +		netdev->stats.rx_errors++;
> > +	}
> > +
> > +	can_frame->can_id = le32_to_cpu(rx->msg_id);
> > +	can_frame->can_dlc =
> > +		(le32_to_cpu(rx->flags) &
> > +		IXXAT_USBV2_MSG_FLAGS_DLC) >> 16;
> > +
> > +	if (le32_to_cpu(rx->flags) & IXXAT_USBV2_MSG_FLAGS_EXT)
> > +		can_frame->can_id |= CAN_EFF_FLAG;
> > +
> > +	if (le32_to_cpu(rx->flags) & IXXAT_USBV2_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);
> > +
> > +	netif_rx(skb);
> > +	netdev->stats.rx_packets++;
> > +	netdev->stats.rx_bytes += can_frame->can_dlc;
> > +
> > +	return 0;
> > +}
> > +
> > +static int ixx_usbv2_handle_error(struct ixx_usb_device *dev,
> > +		struct ixx_can_msg *rx)
> > +{
> > +	struct net_device *netdev = dev->netdev;
> > +	struct can_frame *can_frame;
> > +	struct sk_buff *skb;
> > +	u8 raw_status = 0;
> > +
> > +	/* nothing should be sent while in BUS_OFF state */
> > +	if (dev->can.state == CAN_STATE_BUS_OFF)
> > +		return 0;
> > +
> > +	raw_status = rx->data[0];
> > +
> > +	/* allocate an skb to store the error frame */
> > +	skb = alloc_can_err_skb(netdev, &can_frame);
> > +	if (!skb)
> > +		return -ENOMEM;
> > +
> > +	switch (raw_status) {
> > +	case IXXAT_USBV2_CAN_ERROR_ACK:
> > +		can_frame->can_id |= CAN_ERR_ACK;
> > +		netdev->stats.tx_errors++;
> > +		break;
> > +	case IXXAT_USBV2_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_USBV2_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_USBV2_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_USBV2_CAN_ERROR_STUFF:
> > +		can_frame->can_id |= CAN_ERR_PROT;
> > +		can_frame->data[2] |= CAN_ERR_PROT_STUFF;
> > +		netdev->stats.rx_errors++;
> > +		break;
> > +	case IXXAT_USBV2_CAN_ERROR_OTHER:
> > +		can_frame->can_id |= CAN_ERR_PROT;
> > +		can_frame->data[2] |= CAN_ERR_PROT_UNSPEC;
> > +		netdev->stats.rx_errors++;
> > +		break;
> > +	default:
> > +		can_frame->can_id |= CAN_ERR_PROT;
> > +		netdev->stats.rx_errors++;
> > +	}
> > +
> > +	netif_rx(skb);
> > +	netdev->stats.rx_packets++;
> > +	netdev->stats.rx_bytes += can_frame->can_dlc;
> > +
> > +	dev->bec.txerr = le16_to_cpu(rx->data[1]);
> > +	dev->bec.rxerr = le16_to_cpu(rx->data[3]);
> > +
> > +	return 0;
> > +}
> > +
> > +static int ixx_usbv2_handle_status(struct ixx_usb_device *dev,
> > +		struct ixx_can_msg *rx)
> > +{
> > +	struct net_device *netdev = dev->netdev;
> > +	struct can_frame *can_frame;
> > +	struct sk_buff *skb;
> > +	u8 raw_status = 0;
> > +	u32 new_state = 0;
> > +
> > +	raw_status = rx->data[0];
> > +
> > +	/* nothing should be sent while in BUS_OFF state */
> > +	if (dev->can.state == CAN_STATE_BUS_OFF)
> > +		return 0;
> > +
> > +	if (!raw_status) {
> > +		/* no error bit (back to active state) */
> > +		dev->can.state = CAN_STATE_ERROR_ACTIVE;
> > +
> > +		dev->bec.txerr = 0;
> > +		dev->bec.rxerr = 0;
> > +		return 0;
> > +	}
> > +
> > +	/* allocate an skb to store the error frame */
> > +	skb = alloc_can_err_skb(netdev, &can_frame);
> > +	if (!skb)
> > +		return -ENOMEM;
> > +
> > +	if (raw_status & IXXAT_USBV2_CAN_STATUS_BUSOFF) {
> > +		can_frame->can_id |= CAN_ERR_BUSOFF;
> > +		new_state = CAN_STATE_BUS_OFF;
> > +		dev->can.can_stats.bus_off++;
> > +		can_bus_off(netdev);
> > +	} else {
> > +		if (raw_status & IXXAT_USBV2_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_USBV2_CAN_STATUS_OVRRUN) {
> > +			can_frame->can_id |= CAN_ERR_PROT;
> > +			can_frame->data[2] |= CAN_ERR_PROT_OVERLOAD;
> > +			netdev->stats.rx_over_errors++;
> > +			netdev->stats.rx_errors++;
> > +		}
> > +
> > +		if (!new_state) {
> > +			new_state = CAN_STATE_ERROR_ACTIVE;
> > +
> > +			dev->bec.txerr = 0;
> > +			dev->bec.rxerr = 0;
> > +		}
> > +	}
> > +
> > +	dev->can.state = new_state;
> > +
> > +	netif_rx(skb);
> > +	netdev->stats.rx_packets++;
> > +	netdev->stats.rx_bytes += can_frame->can_dlc;
> > +
> > +	return 0;
> > +}
> > +
> > +/*
> > + * callback for bulk IN urb
> > + */
> > +static int ixx_usbv2_decode_buf(struct ixx_usb_device *dev, struct urb *urb)
> > +{
> > +	struct net_device *netdev = dev->netdev;
> > +	struct ixx_can_msg *can_msg;
> > +	u32 msg_end;
> > +	int err = 0;
> > +	u32 read_size = 0;
> > +	u8 msg_type;
> > +	u8 *data;
> > +
> > +	data = urb->transfer_buffer;
> > +
> > +	/* loop reading all the records from the incoming message */
> > +	msg_end = urb->actual_length;
> > +	for (; msg_end > 0;) {
> > +		can_msg = (struct ixx_can_msg *) &data[read_size];
> > +
> > +		if (!can_msg || !can_msg->size) {
> > +			netdev_err(netdev, "got unsupported rec in usb msg:\n");
> > +			err = -ENOTSUPP;
> > +			break;
> > +		}
> > +
> > +		/* check if the record goes out of current packet */
> > +		if ((read_size + can_msg->size + 1) > urb->actual_length) {
> > +			netdev_err(netdev,
> > +			"got frag rec: should inc usb rx buf size\n");
> > +			err = -EBADMSG;
> > +			break;
> > +		}
> > +
> > +		msg_type =
> > +			(le32_to_cpu(can_msg->flags) &
> > +			IXXAT_USBV2_MSG_FLAGS_TYPE);
> > +
> > +		switch (msg_type) {
> > +
> > +		case IXXAT_USBV2_CAN_DATA:
> > +			err = ixx_usbv2_handle_canmsg(dev, can_msg);
> > +			if (err < 0)
> > +				goto fail;
> > +			break;
> > +
> > +		case IXXAT_USBV2_CAN_STATUS:
> > +			err = ixx_usbv2_handle_status(dev, can_msg);
> > +			if (err < 0)
> > +				goto fail;
> > +			break;
> > +
> > +		case IXXAT_USBV2_CAN_ERROR:
> > +			err = ixx_usbv2_handle_error(dev, can_msg);
> > +			if (err < 0)
> > +				goto fail;
> > +			break;
> > +
> > +		case IXXAT_USBV2_CAN_TIMEOVR:
> > +			ixxat_usb_get_ts_tv(dev, can_msg->time, NULL);
> > +			break;
> > +
> > +		case IXXAT_USBV2_CAN_INFO:
> > +		case IXXAT_USBV2_CAN_WAKEUP:
> > +		case IXXAT_USBV2_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 (err)
> > +		ixxat_dump_mem("received msg", urb->transfer_buffer,
> > +				urb->actual_length);
> > +
> > +	return err;
> > +}
> > +
> > +static int ixx_usbv2_encode_msg(struct ixx_usb_device *dev, struct sk_buff *skb,
> > +		u8 *obuf, size_t *size)
> > +{
> > +	struct can_frame *cf = (struct can_frame *) skb->data;
> > +	struct ixx_can_msg can_msg = { 0 };
> > +
> > +	if (cf->can_id & CAN_RTR_FLAG)
> > +		can_msg.flags |= IXXAT_USBV2_MSG_FLAGS_RTR;
> > +
> > +	if (cf->can_id & CAN_EFF_FLAG) {
> > +		can_msg.flags |= IXXAT_USBV2_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 (dev->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT)
> > +		can_msg.flags |= IXXAT_USBV2_MSG_FLAGS_SSM;
> > +
> > +	can_msg.flags |= (cf->can_dlc << 16) & IXXAT_USBV2_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;
> > +
> > +	return 0;
> > +}
> > +
> > +static int ixx_usbv2_start(struct ixx_usb_device *dev)
> > +{
> > +	int err;
> > +	u32 time_ref = 0;
> > +	u8 can_opmode = IXXAT_USBV2_OPMODE_EXTENDED
> > +			| IXXAT_USBV2_OPMODE_STANDARD;
> > +
> > +	if (dev->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
> > +		can_opmode |= IXXAT_USBV2_OPMODE_ERRFRAME;
> > +	if (dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
> > +		can_opmode |= IXXAT_USBV2_OPMODE_LISTONLY;
> > +
> > +	err = ixx_usbv2_init_ctrl(dev, can_opmode, dev->btr0, dev->btr1);
> > +	if (err)
> > +		return err;
> > +
> > +	/* opening first device: */
> > +	if (dev->ctrl_opened_count == 0) {
> > +		err = ixx_usbv2_start_ctrl(dev, &time_ref);
> > +		if (err)
> > +			return err;
> > +
> > +		ixxat_usb_set_ts_now(dev, time_ref);
> > +	}
> > +
> > +	dev->ctrl_opened_count++;
> > +
> > +	dev->bec.txerr = 0;
> > +	dev->bec.rxerr = 0;
> > +
> > +	return err;
> > +}
> > +
> > +/*
> > + * stop interface
> > + * (last chance before set bus off)
> > + */
> > +static int ixx_usbv2_stop(struct ixx_usb_device *dev)
> > +{
> > +	int err;
> > +
> > +	if (dev->ctrl_opened_count == 1) {
> > +		err = ixx_usbv2_stop_ctrl(dev);
> > +		if (err)
> > +			return err;
> > +	}
> > +
> > +	/* turn off ts msgs for that interface if no other dev opened */
> > +//	if (pdev->usb_if->dev_opened_count == 1)
> > +//		ixx_usbv2_set_ts(dev, 0);
> > +	dev->ctrl_opened_count--;
> > +
> > +	return 0;
> > +}
> > +
> > +/*
> > + * called when probing to initialize a device object.
> > + */
> > +static int ixx_usbv2_init(struct ixx_usb_device *dev)
> > +{
> > +	dev->restart_task = kthread_run(&ixx_usbv2_restart_task, dev,
> > +		"restart_thread");
> > +	if (!dev->restart_task)
> > +		return -ENOBUFS;
> > +
> > +	return 0;
> > +}
> > +
> > +static void ixx_usbv2_exit(struct ixx_usb_device *dev)
> > +{
> > +	ixx_usbv2_reset_ctrl(dev);
> > +
> > +	dev->must_quit = 1;
> > +	dev->restart_flag = 1;
> > +	wake_up_interruptible(&dev->wait_queue);
> > +	if (dev->restart_task)
> > +		kthread_stop(dev->restart_task);
> > +}
> > +
> > +/*
> > + * probe function for new IXXAT USB-to-CAN V2 interface
> > + */
> > +static int ixx_usbv2_probe(struct usb_interface *intf)
> > +{
> > +	struct usb_host_interface *if_desc;
> > +	int i;
> > +
> > +	if_desc = intf->altsetting;
> > +
> > +	/* check interface endpoint addresses */
> > +	for (i = 0; i < if_desc->desc.bNumEndpoints; i++) {
> > +		struct usb_endpoint_descriptor *ep = &if_desc->endpoint[i].desc;
> > +
> > +		/*
> > +		 * below is the list of valid ep addreses. Any other ep address
> > +		 * is considered as not-CAN interface address => no dev created
> > +		 */
> > +		switch (ep->bEndpointAddress) {
> > +		case IXXAT_USBV2_EP_MSGOUT_0:
> > +		case IXXAT_USBV2_EP_MSGOUT_1:
> > +		case IXXAT_USBV2_EP_MSGOUT_2:
> > +		case IXXAT_USBV2_EP_MSGOUT_3:
> > +		case IXXAT_USBV2_EP_MSGOUT_4:
> > +		case IXXAT_USBV2_EP_MSGIN_0:
> > +		case IXXAT_USBV2_EP_MSGIN_1:
> > +		case IXXAT_USBV2_EP_MSGIN_2:
> > +		case IXXAT_USBV2_EP_MSGIN_3:
> > +		case IXXAT_USBV2_EP_MSGIN_4:
> > +
> > +			break;
> > +		default:
> > +			return -ENODEV;
> > +		}
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int ixx_usbv2_get_dev_caps(struct usb_device *dev,
> > +		struct ixx_dev_caps *dev_caps)
> > +{
> > +	int err = -ENODEV, i;
> > +	u8 data[IXXAT_USBV2_CMD_BUFFER_SIZE] = { 0 };
> > +	struct ixx_usbv2_dev_caps_req *dev_caps_req;
> > +	struct ixx_usbv2_dev_caps_res *dev_caps_res;
> > +	u32 req_size = sizeof(*dev_caps_req);
> > +
> > +	dev_caps_req = (struct ixx_usbv2_dev_caps_req *) data;
> > +	dev_caps_res = (struct ixx_usbv2_dev_caps_res *)(data + req_size);
> > +
> > +	dev_caps_req->dal_req.req_size   = cpu_to_le32(req_size);
> > +	dev_caps_req->dal_req.req_code   =
> > +		cpu_to_le32(IXXAT_USBV2_BRD_GET_DEVCAPS_CMD);
> > +	dev_caps_req->dal_req.req_port   = 0xffff;
> > +	dev_caps_req->dal_req.req_socket = 0xffff;
> > +
> > +	dev_caps_res->dal_res.res_size =
> > +		cpu_to_le32(sizeof(*dev_caps_res));
> > +	dev_caps_res->dal_res.ret_size = 0;
> > +	dev_caps_res->dal_res.ret_code = 0xffffffff;
> > +
> > +	err = ixx_usbv2_send_cmd(dev, &dev_caps_req->dal_req);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	err = ixx_usbv2_rcv_cmd(dev, &dev_caps_res->dal_res,
> > +			0xffff);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	dev_caps->bus_ctrl_count =
> > +		le16_to_cpu(dev_caps_res->dev_caps.bus_ctrl_count);
> > +	for (i = 0; i < dev_caps->bus_ctrl_count; ++i)
> > +		dev_caps->bus_ctrl_types[i] =
> > +			le16_to_cpu(dev_caps_res->dev_caps.bus_ctrl_types[i]);
> > +
> > +	return 0;
> > +}
> > +
> > +static int ixx_usbv2_get_ctrl_caps(struct usb_device *dev,
> > +		struct ixx_ctrl_caps *ctrl_caps, int index)
> > +{
> > +	int err = -ENODEV;
> > +	u8 data[IXXAT_USBV2_CMD_BUFFER_SIZE] = { 0 };
> > +	struct ixx_usbv2_ctrl_caps_req *ctrl_caps_req;
> > +	struct ixx_usbv2_ctrl_caps_res *ctrl_caps_res;
> > +	u32 req_size = sizeof(*ctrl_caps_req);
> > +
> > +	ctrl_caps_req = (struct ixx_usbv2_ctrl_caps_req *) data;
> > +	ctrl_caps_res = (struct ixx_usbv2_ctrl_caps_res *)(data + req_size);
> > +
> > +	ctrl_caps_req->dal_req.req_size   = cpu_to_le32(req_size);
> > +	ctrl_caps_req->dal_req.req_code   =
> > +		cpu_to_le32(IXXAT_USBV2_CAN_GET_CAPS_CMD);
> > +	ctrl_caps_req->dal_req.req_port   = cpu_to_le16(index);
> > +	ctrl_caps_req->dal_req.req_socket = 0xffff;
> > +
> > +	ctrl_caps_res->dal_res.res_size =
> > +		cpu_to_le32(sizeof(*ctrl_caps_res));
> > +	ctrl_caps_res->dal_res.ret_size = 0;
> > +	ctrl_caps_res->dal_res.ret_code = 0xffffffff;
> > +
> > +	err = ixx_usbv2_send_cmd(dev, &ctrl_caps_req->dal_req);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	err = ixx_usbv2_rcv_cmd(dev, &ctrl_caps_res->dal_res,
> > +			index);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	ctrl_caps->bus_coupling = le16_to_cpu(
> > +			ctrl_caps_res->ctrl_caps.bus_coupling);
> > +	ctrl_caps->clock_freq =
> > +		le32_to_cpu(ctrl_caps_res->ctrl_caps.clock_freq);
> > +	ctrl_caps->cms_divisor = le32_to_cpu(
> > +		ctrl_caps_res->ctrl_caps.cms_divisor);
> > +	ctrl_caps->cms_max_ticks = le32_to_cpu(
> > +		ctrl_caps_res->ctrl_caps.cms_max_ticks);
> > +	ctrl_caps->ctrl_type = le16_to_cpu(ctrl_caps_res->ctrl_caps.ctrl_type);
> > +	ctrl_caps->dtx_divisor = le32_to_cpu(
> > +		ctrl_caps_res->ctrl_caps.dtx_divisor);
> > +	ctrl_caps->dtx_max_ticks = le32_to_cpu(
> > +		ctrl_caps_res->ctrl_caps.dtx_max_ticks);
> > +	ctrl_caps->features = le32_to_cpu(ctrl_caps_res->ctrl_caps.features);
> > +	ctrl_caps->tsc_divisor = le32_to_cpu(
> > +		ctrl_caps_res->ctrl_caps.tsc_divisor);
> > +
> > +	return 0;
> > +}
> > +
> > +static int ixx_usbv2_get_fwinfo(struct ixx_usb_device *dev,
> > +		struct ixx_intf_fw_info *fwinfo)
> > +{
> > +	int err = -ENODEV;
> > +	u8 data[IXXAT_USBV2_CMD_BUFFER_SIZE] = { 0 };
> > +	struct ixx_usbv2_brd_get_fwinfo_req *fw_info_req;
> > +	struct ixx_usbv2_brd_get_fwinfo_res *fw_info_res;
> > +	u32 req_size = sizeof(*fw_info_req);
> > +
> > +	fw_info_req = (struct ixx_usbv2_brd_get_fwinfo_req *) data;
> > +	fw_info_res = (struct ixx_usbv2_brd_get_fwinfo_res *)(data + req_size);
> > +
> > +	fw_info_req->dal_req.req_size   = cpu_to_le32(req_size);
> > +	fw_info_req->dal_req.req_code   =
> > +		cpu_to_le32(IXXAT_USBV2_BRD_GET_FWINFO_CMD);
> > +	fw_info_req->dal_req.req_port   = 0xffff;
> > +	fw_info_req->dal_req.req_socket = 0xffff;
> > +
> > +	fw_info_res->dal_res.res_size =
> > +		cpu_to_le32(sizeof(*fw_info_res));
> > +	fw_info_res->dal_res.ret_size = 0;
> > +	fw_info_res->dal_res.ret_code = 0xffffffff;
> > +
> > +	err = ixx_usbv2_send_cmd(dev->udev, &fw_info_req->dal_req);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	err = ixx_usbv2_rcv_cmd(dev->udev,
> > +		&fw_info_res->dal_res, 0xffff);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	if (fwinfo) {
> > +		fwinfo->build_version =
> > +			le16_to_cpu(fw_info_res->fwinfo.build_version);
> > +		fwinfo->firmware_type =
> > +			le32_to_cpu(fw_info_res->fwinfo.firmware_type);
> > +		fwinfo->major_version =
> > +			le16_to_cpu(fw_info_res->fwinfo.major_version);
> > +		fwinfo->minor_version =
> > +			le16_to_cpu(fw_info_res->fwinfo.minor_version);
> > +		fwinfo->reserved =
> > +			le16_to_cpu(fw_info_res->fwinfo.reserved);
> > +	}
> > +
> > +	return le32_to_cpu(fw_info_res->dal_res.ret_code);
> > +}
> > +
> > +static int ixx_usbv2_get_dev_info(struct ixx_usb_device *dev,
> > +		struct ixx_intf_info *dev_info)
> > +{
> > +	int err = -ENODEV;
> > +	u8 data[IXXAT_USBV2_CMD_BUFFER_SIZE] = { 0 };
> > +	struct ixx_usbv2_brd_get_intf_info_req *dev_info_req;
> > +	struct ixx_usbv2_brd_get_intf_info_res *dev_info_res;
> > +	u32 req_size = sizeof(*dev_info_req);
> > +
> > +	dev_info_req = (struct ixx_usbv2_brd_get_intf_info_req *) data;
> > +	dev_info_res =
> > +		(struct ixx_usbv2_brd_get_intf_info_res *)(data + req_size);
> > +
> > +	dev_info_req->dal_req.req_size   = cpu_to_le32(req_size);
> > +	dev_info_req->dal_req.req_code   =
> > +		cpu_to_le32(IXXAT_USBV2_BRD_GET_DEVINFO_CMD);
> > +	dev_info_req->dal_req.req_port   = 0xffff;
> > +	dev_info_req->dal_req.req_socket = 0xffff;
> > +
> > +	dev_info_res->dal_res.res_size =
> > +		cpu_to_le32(sizeof(*dev_info_res));
> > +	dev_info_res->dal_res.ret_size = 0;
> > +	dev_info_res->dal_res.ret_code = 0xffffffff;
> > +
> > +	err = ixx_usbv2_send_cmd(dev->udev, &dev_info_req->dal_req);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	err = ixx_usbv2_rcv_cmd(dev->udev,
> > +		&dev_info_res->dal_res, 0xffff);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	if (dev_info) {
> > +		memcpy(dev_info->device_id, &dev_info_res->info.device_id,
> > +			sizeof(dev_info_res->info.device_id));
> > +		memcpy(dev_info->device_name, &dev_info_res->info.device_name,
> > +			sizeof(dev_info_res->info.device_name));
> > +		dev_info->device_fpga_version =
> > +			le16_to_cpu(dev_info_res->info.device_fpga_version);
> > +		dev_info->device_version =
> > +			le32_to_cpu(dev_info_res->info.device_version);
> > +	}
> > +
> > +	return le32_to_cpu(dev_info_res->dal_res.ret_code);
> > +}
> > +
> > +/*
> > + * describe the describes the USB-to-CAN V2 compact adapter
> > + */
> > +struct ixx_usb_adapter usb_to_can_v2_compact = {
> > +	.name = "USB-to-CAN V2 compact",
> > +	.device_id = USB_TO_CAN_V2_COMPACT_PRODUCT_ID,
> > +	.clock = {
> > +		.freq = SJA1000_CRYSTAL_HZ,
> > +	},
> > +	.bittiming_const = {
> > +		.name = "ixxat_usb",
> > +		.tseg1_min = 1,
> > +		.tseg1_max = 16,
> > +		.tseg2_min = 1,
> > +		.tseg2_max = 8,
> > +		.sjw_max = 4,
> > +		.brp_min = 1,
> > +		.brp_max = 64,
> > +		.brp_inc = 1,
> > +	},
> > +
> > +	.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
> > +		CAN_CTRLMODE_BERR_REPORTING |
> > +		CAN_CTRLMODE_LOOPBACK |
> > +		CAN_CTRLMODE_LISTENONLY,
> > +
> > +	/* size of device private data */
> > +	.sizeof_dev_private = sizeof(struct ixx_usb_device),
> > +
> > +	/* give here messages in/out endpoints */
> > +	.ep_msg_in = {  IXXAT_USBV2_EP_MSGIN_0, IXXAT_USBV2_EP_MSGIN_1,
> > +		IXXAT_USBV2_EP_MSGIN_2, IXXAT_USBV2_EP_MSGIN_3,
> > +		IXXAT_USBV2_EP_MSGIN_4 },
> > +	.ep_msg_out = { IXXAT_USBV2_EP_MSGOUT_0, IXXAT_USBV2_EP_MSGOUT_1,
> > +		IXXAT_USBV2_EP_MSGOUT_2, IXXAT_USBV2_EP_MSGOUT_3,
> > +		IXXAT_USBV2_EP_MSGOUT_4 },
> > +
> > +	/* size of rx/tx usb buffers */
> > +	.rx_buffer_size = IXXAT_USBV2_RX_BUFFER_SIZE,
> > +	.tx_buffer_size = IXXAT_USBV2_TX_BUFFER_SIZE,
> > +
> > +	/* device callbacks */
> > +	.intf_probe = ixx_usbv2_probe,
> > +	.dev_get_dev_caps = ixx_usbv2_get_dev_caps,
> > +	.dev_get_ctrl_caps = ixx_usbv2_get_ctrl_caps,
> > +	.dev_init = ixx_usbv2_init,
> > +	.dev_exit = ixx_usbv2_exit,
> > +	.dev_set_bittiming = ixx_usbv2_set_bittiming,
> > +	.intf_get_info = ixx_usbv2_get_dev_info,
> > +	.intf_get_fw_info = ixx_usbv2_get_fwinfo,
> > +	.dev_decode_buf = ixx_usbv2_decode_buf,
> > +	.dev_encode_msg = ixx_usbv2_encode_msg,
> > +	.dev_start = ixx_usbv2_start,
> > +	.dev_stop = ixx_usbv2_stop,
> > +};
> > +
> > +/*
> > + * describes the USB-to-CAN V2 automotive adapter
> > + */
> > +struct ixx_usb_adapter usb_to_can_v2_automotive = {
> > +	.name = "USB-to-CAN V2 automotive",
> > +	.device_id = USB_TO_CAN_V2_AUTOMOTIVE_PRODUCT_ID,
> > +	.clock = {
> > +		.freq = SJA1000_CRYSTAL_HZ,
> > +	},
> > +	.bittiming_const = {
> > +		.name = "ixxat_usb",
> > +		.tseg1_min = 1,
> > +		.tseg1_max = 16,
> > +		.tseg2_min = 1,
> > +		.tseg2_max = 8,
> > +		.sjw_max = 4,
> > +		.brp_min = 1,
> > +		.brp_max = 64,
> > +		.brp_inc = 1,
> > +	},
> > +
> > +	.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
> > +		CAN_CTRLMODE_BERR_REPORTING |
> > +		CAN_CTRLMODE_LOOPBACK |
> > +		CAN_CTRLMODE_LISTENONLY,
> > +
> > +	/* size of device private data */
> > +	.sizeof_dev_private = sizeof(struct ixx_usb_device),
> > +
> > +	/* give here messages in/out endpoints */
> > +	.ep_msg_in = {  IXXAT_USBV2_EP_MSGIN_0, IXXAT_USBV2_EP_MSGIN_1,
> > +		IXXAT_USBV2_EP_MSGIN_2, IXXAT_USBV2_EP_MSGIN_3,
> > +		IXXAT_USBV2_EP_MSGIN_4 },
> > +	.ep_msg_out = { IXXAT_USBV2_EP_MSGOUT_0, IXXAT_USBV2_EP_MSGOUT_1,
> > +		IXXAT_USBV2_EP_MSGOUT_2, IXXAT_USBV2_EP_MSGOUT_3,
> > +		IXXAT_USBV2_EP_MSGOUT_4 },
> > +
> > +	/* size of rx/tx usb buffers */
> > +	.rx_buffer_size = IXXAT_USBV2_RX_BUFFER_SIZE,
> > +	.tx_buffer_size = IXXAT_USBV2_TX_BUFFER_SIZE,
> > +
> > +	/* device callbacks */
> > +	.intf_probe = ixx_usbv2_probe,
> > +	.dev_get_dev_caps = ixx_usbv2_get_dev_caps,
> > +	.dev_get_ctrl_caps = ixx_usbv2_get_ctrl_caps,
> > +	.dev_init = ixx_usbv2_init,
> > +	.dev_exit = ixx_usbv2_exit,
> > +	.dev_set_bittiming = ixx_usbv2_set_bittiming,
> > +	.intf_get_info = ixx_usbv2_get_dev_info,
> > +	.intf_get_fw_info = ixx_usbv2_get_fwinfo,
> > +	.dev_decode_buf = ixx_usbv2_decode_buf,
> > +	.dev_encode_msg = ixx_usbv2_encode_msg,
> > +	.dev_start = ixx_usbv2_start,
> > +	.dev_stop = ixx_usbv2_stop,
> > +};
> > +
> > +/*
> > + * describes the USB-to-CAN V2 embedded adapter
> > + */
> > +struct ixx_usb_adapter usb_to_can_v2_embedded = {
> > +	.name = "USB-to-CAN V2 embedded",
> > +	.device_id = USB_TO_CAN_V2_EMBEDDED_PRODUCT_ID,
> > +	.clock = {
> > +		.freq = SJA1000_CRYSTAL_HZ,
> > +	},
> > +	.bittiming_const = {
> > +		.name = "ixxat_usb",
> > +		.tseg1_min = 1,
> > +		.tseg1_max = 16,
> > +		.tseg2_min = 1,
> > +		.tseg2_max = 8,
> > +		.sjw_max = 4,
> > +		.brp_min = 1,
> > +		.brp_max = 64,
> > +		.brp_inc = 1,
> > +	},
> > +
> > +	.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
> > +		CAN_CTRLMODE_BERR_REPORTING |
> > +		CAN_CTRLMODE_LOOPBACK |
> > +		CAN_CTRLMODE_LISTENONLY,
> > +
> > +	/* size of device private data */
> > +	.sizeof_dev_private = sizeof(struct ixx_usb_device),
> > +
> > +	/* give here messages in/out endpoints */
> > +	.ep_msg_in = {  IXXAT_USBV2_EP_MSGIN_0, IXXAT_USBV2_EP_MSGIN_1,
> > +		IXXAT_USBV2_EP_MSGIN_2, IXXAT_USBV2_EP_MSGIN_3,
> > +		IXXAT_USBV2_EP_MSGIN_4 },
> > +	.ep_msg_out = { IXXAT_USBV2_EP_MSGOUT_0, IXXAT_USBV2_EP_MSGOUT_1,
> > +		IXXAT_USBV2_EP_MSGOUT_2, IXXAT_USBV2_EP_MSGOUT_3,
> > +		IXXAT_USBV2_EP_MSGOUT_4 },
> > +
> > +	/* size of rx/tx usb buffers */
> > +	.rx_buffer_size = IXXAT_USBV2_RX_BUFFER_SIZE,
> > +	.tx_buffer_size = IXXAT_USBV2_TX_BUFFER_SIZE,
> > +
> > +	/* device callbacks */
> > +	.intf_probe = ixx_usbv2_probe,
> > +	.dev_get_dev_caps = ixx_usbv2_get_dev_caps,
> > +	.dev_get_ctrl_caps = ixx_usbv2_get_ctrl_caps,
> > +	.dev_init = ixx_usbv2_init,
> > +	.dev_exit = ixx_usbv2_exit,
> > +	.dev_set_bittiming = ixx_usbv2_set_bittiming,
> > +	.intf_get_info = ixx_usbv2_get_dev_info,
> > +	.intf_get_fw_info = ixx_usbv2_get_fwinfo,
> > +	.dev_decode_buf = ixx_usbv2_decode_buf,
> > +	.dev_encode_msg = ixx_usbv2_encode_msg,
> > +	.dev_start = ixx_usbv2_start,
> > +	.dev_stop = ixx_usbv2_stop,
> > +};
> > +
> > +/*
> > + * describes the USB-to-CAN V2 professional adapter
> > + */
> > +struct ixx_usb_adapter usb_to_can_v2_professional = {
> > +	.name = "USB-to-CAN V2 professional",
> > +	.device_id = USB_TO_CAN_V2_PROFESSIONAL_PRODUCT_ID,
> > +	.clock = {
> > +		.freq = SJA1000_CRYSTAL_HZ,
> > +	},
> > +	.bittiming_const = {
> > +		.name = "ixxat_usb",
> > +		.tseg1_min = 1,
> > +		.tseg1_max = 16,
> > +		.tseg2_min = 1,
> > +		.tseg2_max = 8,
> > +		.sjw_max = 4,
> > +		.brp_min = 1,
> > +		.brp_max = 64,
> > +		.brp_inc = 1,
> > +	},
> > +
> > +	.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
> > +		CAN_CTRLMODE_BERR_REPORTING |
> > +		CAN_CTRLMODE_LOOPBACK |
> > +		CAN_CTRLMODE_LISTENONLY,
> > +
> > +	/* size of device private data */
> > +	.sizeof_dev_private = sizeof(struct ixx_usb_device),
> > +
> > +	/* give here messages in/out endpoints */
> > +	.ep_msg_in = {  IXXAT_USBV2_EP_MSGIN_0, IXXAT_USBV2_EP_MSGIN_1,
> > +		IXXAT_USBV2_EP_MSGIN_2, IXXAT_USBV2_EP_MSGIN_3,
> > +		IXXAT_USBV2_EP_MSGIN_4 },
> > +	.ep_msg_out = { IXXAT_USBV2_EP_MSGOUT_0, IXXAT_USBV2_EP_MSGOUT_1,
> > +		IXXAT_USBV2_EP_MSGOUT_2, IXXAT_USBV2_EP_MSGOUT_3,
> > +		IXXAT_USBV2_EP_MSGOUT_4 },
> > +
> > +	/* size of rx/tx usb buffers */
> > +	.rx_buffer_size = IXXAT_USBV2_RX_BUFFER_SIZE,
> > +	.tx_buffer_size = IXXAT_USBV2_TX_BUFFER_SIZE,
> > +
> > +	/* device callbacks */
> > +	.intf_probe = ixx_usbv2_probe,
> > +	.dev_get_dev_caps = ixx_usbv2_get_dev_caps,
> > +	.dev_get_ctrl_caps = ixx_usbv2_get_ctrl_caps,
> > +	.dev_init = ixx_usbv2_init,
> > +	.dev_exit = ixx_usbv2_exit,
> > +	.dev_set_bittiming = ixx_usbv2_set_bittiming,
> > +	.intf_get_info = ixx_usbv2_get_dev_info,
> > +	.intf_get_fw_info = ixx_usbv2_get_fwinfo,
> > +	.dev_decode_buf = ixx_usbv2_decode_buf,
> > +	.dev_encode_msg = ixx_usbv2_encode_msg,
> > +	.dev_start = ixx_usbv2_start,
> > +	.dev_stop = ixx_usbv2_stop,
> > +};
> > +
> > +/*
> > + * describes the USB-to-CAN V2 low speed adapter
> > + */
> > +struct ixx_usb_adapter usb_to_can_v2_low_speed = {
> > +	.name = "USB-to-CAN V2 low speed",
> > +	.device_id = USB_TO_CAN_V2_LOW_SPEED_PRODUCT_ID,
> > +	.clock = {
> > +		.freq = SJA1000_CRYSTAL_HZ,
> > +	},
> > +	.bittiming_const = {
> > +		.name = "ixxat_usb",
> > +		.tseg1_min = 1,
> > +		.tseg1_max = 16,
> > +		.tseg2_min = 1,
> > +		.tseg2_max = 8,
> > +		.sjw_max = 4,
> > +		.brp_min = 1,
> > +		.brp_max = 64,
> > +		.brp_inc = 1,
> > +	},
> > +
> > +	.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
> > +		CAN_CTRLMODE_BERR_REPORTING |
> > +		CAN_CTRLMODE_LOOPBACK |
> > +		CAN_CTRLMODE_LISTENONLY,
> > +
> > +	/* size of device private data */
> > +	.sizeof_dev_private = sizeof(struct ixx_usb_device),
> > +
> > +	/* give here messages in/out endpoints */
> > +	.ep_msg_in = {  IXXAT_USBV2_EP_MSGIN_0, IXXAT_USBV2_EP_MSGIN_1,
> > +		IXXAT_USBV2_EP_MSGIN_2, IXXAT_USBV2_EP_MSGIN_3,
> > +		IXXAT_USBV2_EP_MSGIN_4 },
> > +	.ep_msg_out = { IXXAT_USBV2_EP_MSGOUT_0, IXXAT_USBV2_EP_MSGOUT_1,
> > +		IXXAT_USBV2_EP_MSGOUT_2, IXXAT_USBV2_EP_MSGOUT_3,
> > +		IXXAT_USBV2_EP_MSGOUT_4 },
> > +
> > +	/* size of rx/tx usb buffers */
> > +	.rx_buffer_size = IXXAT_USBV2_RX_BUFFER_SIZE,
> > +	.tx_buffer_size = IXXAT_USBV2_TX_BUFFER_SIZE,
> > +
> > +	/* device callbacks */
> > +	.intf_probe = ixx_usbv2_probe,
> > +	.dev_get_dev_caps = ixx_usbv2_get_dev_caps,
> > +	.dev_get_ctrl_caps = ixx_usbv2_get_ctrl_caps,
> > +	.dev_init = ixx_usbv2_init,
> > +	.dev_exit = ixx_usbv2_exit,
> > +	.dev_set_bittiming = ixx_usbv2_set_bittiming,
> > +	.intf_get_info = ixx_usbv2_get_dev_info,
> > +	.intf_get_fw_info = ixx_usbv2_get_fwinfo,
> > +	.dev_decode_buf = ixx_usbv2_decode_buf,
> > +	.dev_encode_msg = ixx_usbv2_encode_msg,
> > +	.dev_start = ixx_usbv2_start,
> > +	.dev_stop = ixx_usbv2_stop,
> > +};
> > +
> > +/*
> > + * describes the USB-to-CAN V2 extended adapter
> > + */
> > +struct ixx_usb_adapter usb_to_can_v2_extended = {
> > +	.name = "USB-to-CAN V2 extended",
> > +	.device_id = USB_TO_CAN_V2_EXTENDED_PRODUCT_ID,
> > +	.clock = {
> > +		.freq = SJA1000_CRYSTAL_HZ,
> > +	},
> > +	.bittiming_const = {
> > +		.name = "ixxat_usb",
> > +		.tseg1_min = 1,
> > +		.tseg1_max = 16,
> > +		.tseg2_min = 1,
> > +		.tseg2_max = 8,
> > +		.sjw_max = 4,
> > +		.brp_min = 1,
> > +		.brp_max = 64,
> > +		.brp_inc = 1,
> > +	},
> > +
> > +	.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
> > +		CAN_CTRLMODE_BERR_REPORTING |
> > +		CAN_CTRLMODE_LOOPBACK |
> > +		CAN_CTRLMODE_LISTENONLY,
> > +
> > +	/* size of device private data */
> > +	.sizeof_dev_private = sizeof(struct ixx_usb_device),
> > +
> > +	/* give here messages in/out endpoints */
> > +	.ep_msg_in = {  IXXAT_USBV2_EP_MSGIN_0, IXXAT_USBV2_EP_MSGIN_1,
> > +		IXXAT_USBV2_EP_MSGIN_2, IXXAT_USBV2_EP_MSGIN_3,
> > +		IXXAT_USBV2_EP_MSGIN_4 },
> > +	.ep_msg_out = { IXXAT_USBV2_EP_MSGOUT_0, IXXAT_USBV2_EP_MSGOUT_1,
> > +		IXXAT_USBV2_EP_MSGOUT_2, IXXAT_USBV2_EP_MSGOUT_3,
> > +		IXXAT_USBV2_EP_MSGOUT_4 },
> > +
> > +	/* size of rx/tx usb buffers */
> > +	.rx_buffer_size = IXXAT_USBV2_RX_BUFFER_SIZE,
> > +	.tx_buffer_size = IXXAT_USBV2_TX_BUFFER_SIZE,
> > +
> > +	/* device callbacks */
> > +	.intf_probe = ixx_usbv2_probe,
> > +	.dev_get_dev_caps = ixx_usbv2_get_dev_caps,
> > +	.dev_get_ctrl_caps = ixx_usbv2_get_ctrl_caps,
> > +	.dev_init = ixx_usbv2_init,
> > +	.dev_exit = ixx_usbv2_exit,
> > +	.dev_set_bittiming = ixx_usbv2_set_bittiming,
> > +	.intf_get_info = ixx_usbv2_get_dev_info,
> > +	.intf_get_fw_info = ixx_usbv2_get_fwinfo,
> > +	.dev_decode_buf = ixx_usbv2_decode_buf,
> > +	.dev_encode_msg = ixx_usbv2_encode_msg,
> > +	.dev_start = ixx_usbv2_start,
> > +	.dev_stop = ixx_usbv2_stop,
> > +};
> >
diff mbox series

Patch

--- a/ubuntu/Kconfig
+++ b/ubuntu/Kconfig
@@ -37,6 +37,10 @@  source "ubuntu/bnxt/Kconfig"
 ##
 ##
 ##
+source "ubuntu/ixxat/Kconfig
+##
+##
+##
 ##
 ##
 ##