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