From patchwork Tue Jun 5 17:29:01 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kleber Sacilotto de Souza X-Patchwork-Id: 925565 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.ubuntu.com (client-ip=91.189.94.19; helo=huckleberry.canonical.com; envelope-from=kernel-team-bounces@lists.ubuntu.com; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=canonical.com Received: from huckleberry.canonical.com (huckleberry.canonical.com [91.189.94.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 410f1d11wXz9s1w; Wed, 6 Jun 2018 03:29:25 +1000 (AEST) Received: from localhost ([127.0.0.1] helo=huckleberry.canonical.com) by huckleberry.canonical.com with esmtp (Exim 4.86_2) (envelope-from ) id 1fQFlj-000061-Lx; Tue, 05 Jun 2018 17:29:15 +0000 Received: from youngberry.canonical.com ([91.189.89.112]) by huckleberry.canonical.com with esmtps (TLS1.0:DHE_RSA_AES_128_CBC_SHA1:128) (Exim 4.86_2) (envelope-from ) id 1fQFlc-0008Vr-12 for kernel-team@lists.ubuntu.com; Tue, 05 Jun 2018 17:29:08 +0000 Received: from mail-io0-f198.google.com ([209.85.223.198]) by youngberry.canonical.com with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.76) (envelope-from ) id 1fQFlb-0000Pm-BT for kernel-team@lists.ubuntu.com; Tue, 05 Jun 2018 17:29:07 +0000 Received: by mail-io0-f198.google.com with SMTP id x23-v6so2655898iob.1 for ; Tue, 05 Jun 2018 10:29:07 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:subject:to:references:from:openpgp:autocrypt :message-id:date:user-agent:mime-version:in-reply-to :content-language:content-transfer-encoding; bh=86EagH7YvPc901mBidMUZI9t44UPKmAFBHyOulSkBsA=; b=BG740j36wvpTtGsSonuDnZMBO6gWMzU3pg6tTEyCCm81oK3n63ssFLLclTF4m4gC3K 0p5KiiVYV6Kzw+TCG89+kJXxhqOEYy4RyksLTmHmFlMv9PZFfQ9CXAFo0zXVCBwop2+E 3wCHWyGehkO6leLkA+gVUCbTT952uLVXx3dNFJjn+g2IIzrVvRNfPNo6TPI+eE8oCmRH HJulZi25Jn+R337UlKMJyWpVe9Kv4R2P9grX6Zc/gUArWRZLMZYxWbfJ9fb9ECqHD3uG cZwSD1XeK2pcTc4eROtLqYTzakGrxch4eG9RUaHi0fIyz2lAOHk+aHG/JWSM9T2QVpdU 8SGQ== X-Gm-Message-State: APt69E1cLdMMcvLJOz4HGog+PV5iewLBxwPG+T28xiFZ7bnTkwxJ3Qi8 zWUAG76XYE/nr3YzjaxpEQ9/1VwIb8VG8w2FTrAYg0O82gbn2Ji8wzPt+KfwFBtR10RdaQfOPug mllYfvcWUARWoAJw9NKyv2jWZvFe5hxCs/zI1hzSErQ== X-Received: by 2002:a24:2f12:: with SMTP id j18-v6mr155473itj.28.1528219745424; Tue, 05 Jun 2018 10:29:05 -0700 (PDT) X-Google-Smtp-Source: ADUXVKLLBOa0fil4vfa2v3MC36rLvyDwfXGJjCRiTt3KbpNREGAeYHXeW9m+Yj/o2aFKouyH28En8g== X-Received: by 2002:a24:2f12:: with SMTP id j18-v6mr155400itj.28.1528219744515; Tue, 05 Jun 2018 10:29:04 -0700 (PDT) Received: from [10.155.60.244] (50-201-118-110-static.hfc.comcastbusiness.net. [50.201.118.110]) by smtp.gmail.com with ESMTPSA id k130-v6sm939034itb.0.2018.06.05.10.29.02 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 05 Jun 2018 10:29:03 -0700 (PDT) Subject: NACK: [Xenial SRU][PATCH 1/2] UBUNTU: SAUCE: (no-up) Support IXXAT USB SocketCAN device To: Shrirang Bagul , kernel-team@lists.ubuntu.com References: <20180601054759.14851-1-shrirang.bagul@canonical.com> <20180601054759.14851-2-shrirang.bagul@canonical.com> From: Kleber Souza Openpgp: preference=signencrypt Autocrypt: addr=kleber.souza@canonical.com; prefer-encrypt=mutual; keydata= xsBNBFjjmLgBCADW/wnobGtt4lIvs0nkVbvecpvmvH6j7oFy92KxnAVPr4akWmLwLHH8id1k tKJlR1KlINf089anZfIK9uC6lFWjlmrg94U+9zZHUlG+MdLeJrqRWJAxqjz2DT3EYq9vDpxt uLaZws5EAWvxswa9oTtbwIWA1sqeps5DWUw95zFGeaxS/hisdlywU5G+I/pKLNkwTMyjwICC gHuUvCNuuOt5ZDu3i6Z76XKedu6YyWSVquesMzWAt6XO3QTXLB2b67eqalxxbTSHdkzrt5sR Ai4BQhr5d3jziYWRK5tPi+nj72/kWv0C12WQqzSFOZ5rYEZu3Ypyu+t4AoTzJ1GpzZEhABEB AAHNNktsZWJlciBTYWNpbG90dG8gZGUgU291emEgPGtsZWJlci5zb3V6YUBjYW5vbmljYWwu Y29tPsLAgAQTAQgAKgIbAwULCQgHAgYVCAkKCwIEFgIDAQIeAQIXgAIZAQUCWi/glwUJAy17 XwAKCRBGqvM1cOEWK/27CACn4Y+Rf9IovjXKJVLcd8e4tRPJA162r0sa5maqSNQfK9lO4Ooz v44VG2wW8yA7eTjoWQLV24vRWeGnmHhqX4/aFWTEevn/jJZba2sanurlEDMgDjMbnHVSfkir 5JQ5zBQ2l85d1DN/2wE90t8AnNKou8n+HSd26rteRbufEqjesYYG4lgYdz/9Idz8JyniAEgb 0HBeVlw5LczZ1Mib4hsPCGiCsklK0ozn1UBmeBcfZo/Y45u+PlQBB2Cb2NaALl5bwermciC7 qRjfOw/GGzRbUA8xCyuMa5dgEcc12I22Uc/1/GgZVofBf8uwdeli+x1VU05TiC82+tmGOxPa 6a3UzsBNBFjjmLgBCADCaiu81f/8dT2lFVZyYkqBGRQQjqGXpRGo6uL3g1VBFKDr1wlb/d2k BJwGbW+MKgJRrWShp++KGsYFY0S71CFBVkX/hb3FoZsztoHmk5kUOFuV3RXgspjZrsA1SB/d GY8BRWSOTHOpRV/JOHgT4cTcV7oTPvKx1qYh1cFGqf2KmMcrxD/PAVa0KmUIMEIxlQxPTzSD sD1LGBnYUnfvJbxT9soFUdwYZS0/WgCarBkckFhLowgOf8ck0YtZ3xf3AFEJHxQlx49B4nXc NM03JQblnWc6nUGcBeuHuDsAMvl7SepWHrhzP2bcYHm1hZ1iC6mSy6MAsKdop8mXlwtSLk0H ABEBAAHCwF8EGAEIAAkFAljjmLgCGwwACgkQRqrzNXDhFitmUAgAgcgRsKF7Kn0mQfpZ5E6e XVdWkPqXgu0/iPBkClb3JBVb95b+94m5tbgTa50YzgOvNNiZ3j9TPEKPIdaJXNIl8l5yKZzM 9XWQbI+VSaGETHrOlAfej7mn3TSp1aOl2HPcRNEDEQiFKGd+5wu4DAoAITOXmOEozMY8vGl4 jaVre1hgmaAr0Uq1p9Aq1hrzK4l26h3+fzx4aDhgkaytT5cRWK4rrgbRlD/5AMtq80+AeTcs F6wB4OCK4DMRHWB8ui4rraUkSphR8DuCAJgfKVK23sCcVukZINnNdL837zwobPG3nb8m/Hoq FNfUEtKa48csK1onun8E+TahphOjb+PpAA== Message-ID: <68f36406-a977-8960-95f1-dc7128b36d29@canonical.com> Date: Tue, 5 Jun 2018 10:29:01 -0700 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.8.0 MIME-Version: 1.0 In-Reply-To: <20180601054759.14851-2-shrirang.bagul@canonical.com> Content-Language: en-US X-BeenThere: kernel-team@lists.ubuntu.com X-Mailman-Version: 2.1.20 Precedence: list List-Id: Kernel team discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: kernel-team-bounces@lists.ubuntu.com Sender: "kernel-team" Hi Shrirang, On 05/31/18 22:47, Shrirang Bagul wrote: > BugLink: http://bugs.launchpad.net/bugs/1774563 > > This driver from IXXAT adds support for SocketCAN over USB. > (https://www.ixxat.com) > > Signed-off-by: Shrirang Bagul > --- > ubuntu/Kconfig | 3 +- > ubuntu/Makefile | 5 +- > ubuntu/ixxat/Kconfig | 8 + > ubuntu/ixxat/Makefile | 4 + > ubuntu/ixxat/ixx_usb_core.c | 923 +++++++++++++++++++ > ubuntu/ixxat/ixx_usb_core.h | 289 ++++++ > ubuntu/ixxat/ixx_usb_fd.c | 1673 +++++++++++++++++++++++++++++++++++ > ubuntu/ixxat/ixx_usb_v2.c | 1450 ++++++++++++++++++++++++++++++ > 8 files changed, 4353 insertions(+), 2 deletions(-) > create mode 100644 ubuntu/ixxat/Kconfig > create mode 100644 ubuntu/ixxat/Makefile > create mode 100644 ubuntu/ixxat/ixx_usb_core.c > create mode 100644 ubuntu/ixxat/ixx_usb_core.h > create mode 100644 ubuntu/ixxat/ixx_usb_fd.c > create mode 100644 ubuntu/ixxat/ixx_usb_v2.c > > diff --git a/ubuntu/Kconfig b/ubuntu/Kconfig > index bc2fb5530593..a3ad3d87ce53 100644 > --- a/ubuntu/Kconfig > +++ b/ubuntu/Kconfig > @@ -30,10 +30,11 @@ source "ubuntu/opennsl/Kconfig" > ## > ## > ## > +source "ubuntu/bnxt/Kconfig" > ## > ## > ## > -source "ubuntu/bnxt/Kconfig" > +source "ubuntu/ixxat/Kconfig" > ## > ## > ## Please add the 3 lines with "##" above the new entry so the patch doesn't need to touch the exiting entries and avoid merge conflicts by that. E.g.: > diff --git a/ubuntu/Makefile b/ubuntu/Makefile > index 85e1c900735c..62dd1e1b7b46 100644 > --- a/ubuntu/Makefile > +++ b/ubuntu/Makefile > @@ -44,11 +44,14 @@ obj-$(CONFIG_OPENNSL) += opennsl/ > ## > ## > ## > -## > obj-$(CONFIG_BNXT_BPO) += bnxt/ > ## > ## > ## > +obj-$(CONFIG_CAN_HMS_USB) += ixxat/ > +## > +## > +## > ## > ## > ## Same here. Thanks, Kleber > diff --git a/ubuntu/ixxat/Kconfig b/ubuntu/ixxat/Kconfig > new file mode 100644 > index 000000000000..63ff0d054d9e > --- /dev/null > +++ b/ubuntu/ixxat/Kconfig > @@ -0,0 +1,8 @@ > +config CAN_HMS_USB > + tristate "HMS USB SocketCAN" > + depends on X86 || X86_64 > + depends on USB && CAN_DEV > + ---help--- > + This driver is from IXXAT and supports SocketCAN over USB. > + (https://www.ixxat.com) > + > diff --git a/ubuntu/ixxat/Makefile b/ubuntu/ixxat/Makefile > new file mode 100644 > index 000000000000..d4ee67ebdd24 > --- /dev/null > +++ b/ubuntu/ixxat/Makefile > @@ -0,0 +1,4 @@ > +mod-name += ixx_usb > +obj-m += ixx_usb.o > +ixx_usb-objs := ixx_usb_v2.o ixx_usb_fd.o ixx_usb_core.o > + > diff --git a/ubuntu/ixxat/ixx_usb_core.c b/ubuntu/ixxat/ixx_usb_core.c > new file mode 100644 > index 000000000000..d258b6e46453 > --- /dev/null > +++ b/ubuntu/ixxat/ixx_usb_core.c > @@ -0,0 +1,923 @@ > +/* > + * CAN driver for IXXAT USB-to-CAN V2 adapters > + * > + * Copyright (C) 2003-2014 Michael Hengler IXXAT Automation GmbH > + * > + * Based on code originally by pcan_usb_core > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU General Public License as published > + * by the Free Software Foundation; version 2 of the License. > + * > + * This program is distributed in the hope that it will be useful, but > + * WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * General Public License for more details. > + */ > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include "ixx_usb_core.h" > + > +MODULE_AUTHOR("Michael Hengler "); > +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 > + > +#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 > + * > + * 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 > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#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 > + * > + * 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 > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#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, > +}; > --- a/ubuntu/Kconfig +++ b/ubuntu/Kconfig @@ -37,6 +37,10 @@ source "ubuntu/bnxt/Kconfig" ## ## ## +source "ubuntu/ixxat/Kconfig +## +## +## ## ## ##