[Xenial SRU][PATCH 0/2] Support SocketCAN over USB on Dell IoT 300x Gateways

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

[Xenial SRU][PATCH 0/2] Support SocketCAN over USB on Dell IoT 300x Gateways

Shrirang Bagul
BugLink: http://bugs.launchpad.net/bugs/1774563

This driver is from IXXAT and supports SocketCAN over USB.
(https://www.ixxat.com)

Dell IoT 300x gateways support USB-CAN peripherals. Dell expects these
devices also support the SocketCAN interface over USB and is working with
HMS to develop and upstream the driver. Meanwhile, we'll have to carry this
driver as a SAUCE patch to support our Ubuntu Core 16 release.

The driver will be built only for amd64 kernels and attach itself to
IXXAT USB-CAN adapters with VID 0x08d8.

The functionality in this driver has already been verified by Canonical
CE-QA and Dell/HMS in a test kernel snap released based on
Ubuntu-4.4.0-116.140

Shrirang Bagul (2):
  UBUNTU: SAUCE: (no-up) Support IXXAT USB SocketCAN device
  UBUNTU: [Config] CONFIG_CAN_HMS_USB=m

 debian.master/config/config.common.ubuntu |    1 +
 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 ++++++++++++++++++
 9 files changed, 4354 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

--
2.17.0


--
kernel-team mailing list
[hidden email]
https://lists.ubuntu.com/mailman/listinfo/kernel-team
Reply | Threaded
Open this post in threaded view
|

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

Shrirang Bagul
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 <[hidden email]>
---
 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"
 ##
 ##
 ##
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/
+##
+##
+##
 ##
 ##
 ##
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 <[hidden email]>");
+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 <[hidden email]>
+ *
+ * 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 <[hidden email]>
+ *
+ * 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,
+};
--
2.17.0


--
kernel-team mailing list
[hidden email]
https://lists.ubuntu.com/mailman/listinfo/kernel-team
Reply | Threaded
Open this post in threaded view
|

[Xenial SRU][PATCH 2/2] UBUNTU: [Config] CONFIG_CAN_HMS_USB=m

Shrirang Bagul
In reply to this post by Shrirang Bagul
BugLink: http://bugs.launchpad.net/bugs/1774563

Signed-off-by: Shrirang Bagul <[hidden email]>
---
 debian.master/config/config.common.ubuntu | 1 +
 1 file changed, 1 insertion(+)

diff --git a/debian.master/config/config.common.ubuntu b/debian.master/config/config.common.ubuntu
index 101c6d7ecd97..466027ff3524 100644
--- a/debian.master/config/config.common.ubuntu
+++ b/debian.master/config/config.common.ubuntu
@@ -1070,6 +1070,7 @@ CONFIG_CAN_FLEXCAN=m
 CONFIG_CAN_GRCAN=m
 CONFIG_CAN_GS_USB=m
 CONFIG_CAN_GW=m
+CONFIG_CAN_HMS_USB=m
 CONFIG_CAN_JANZ_ICAN3=m
 CONFIG_CAN_KVASER_PCI=m
 CONFIG_CAN_KVASER_USB=m
--
2.17.0


--
kernel-team mailing list
[hidden email]
https://lists.ubuntu.com/mailman/listinfo/kernel-team
Reply | Threaded
Open this post in threaded view
|

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

Kleber Sacilotto de Souza
In reply to this post by Shrirang Bagul
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 <[hidden email]>
> ---
>  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
+##
+##
+##
 ##
 ##
 ##


> 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 <[hidden email]>");
> +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 <[hidden email]>
> + *
> + * 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 <[hidden email]>
> + *
> + * 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,
> +};
>

--
kernel-team mailing list
[hidden email]
https://lists.ubuntu.com/mailman/listinfo/kernel-team
Reply | Threaded
Open this post in threaded view
|

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

Shrirang Bagul
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 <[hidden email]>
> > ---
> >  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 <[hidden email]>");
> > +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 <[hidden email]>
> > + *
> > + * 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 <[hidden email]>
> > + *
> > + * 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,
> > +};
> >
--
kernel-team mailing list
[hidden email]
https://lists.ubuntu.com/mailman/listinfo/kernel-team

signature.asc (849 bytes) Download Attachment