From nobody Sun Feb 8 05:52:54 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id D8DFEC77B73 for ; Mon, 22 May 2023 20:02:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233793AbjEVUCy (ORCPT ); Mon, 22 May 2023 16:02:54 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52322 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231439AbjEVUCu (ORCPT ); Mon, 22 May 2023 16:02:50 -0400 Received: from mout.gmx.net (mout.gmx.net [212.227.15.19]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5A39B1A5; Mon, 22 May 2023 13:02:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=gmx.net; s=s31663417; t=1684785727; i=ps.report@gmx.net; bh=nao6uyuQHto+LCIE2B02jIMUT45rOiW4em4xC7+uKEs=; h=X-UI-Sender-Class:From:To:Cc:Subject:Date; b=ZSaBQb2o12JD3v35LWI4uyxet8GjrrIxAcKbwxEVEKz8+lVSKewMx5XfP208cEFFz mzaRThn6FtfPAHNbK1vOSvF2D3c1AG5DyoaK33WUIfrlaJP7wltSH4ymP2UZ+t42nb 8K66/mx1iZNCklQKGiU1PKKYWaj31vweMzEYkckV2dvUPwhtaTSgoO9Ab4xaaCTXRG YtPpgV7dTKV/N9tq6Cp1bPV0d5rIevhSs2IAa7oBRrhj+lK/8GIITBCzqCgjatkmKZ plg1VRIf+tB5KXKt5oCsTXDhmgGsoLphgVXVAVQdeiBJMdJis/TInWEAr7+tVlKEkl kHjYHPBLckTjw== X-UI-Sender-Class: 724b4f7f-cbec-4199-ad4e-598c01a50d3a Received: from localhost.fritz.box ([62.216.209.74]) by mail.gmx.net (mrgmx004 [212.227.17.190]) with ESMTPSA (Nemesis) id 1MjS5A-1qOito2p3q-00kzfp; Mon, 22 May 2023 22:02:06 +0200 From: Peter Seiderer To: linux-can@vger.kernel.org Cc: Florian Ferg , Marc Kleine-Budde , Vincent Mailhol , Wolfgang Grandegger , socketcan@hms-networks.de, linux-kernel@vger.kernel.org, Peter Seiderer Subject: [PATCH v9] can: usb: IXXAT USB-to-CAN adapters drivers Date: Mon, 22 May 2023 22:01:44 +0200 Message-Id: <20230522200144.15949-1-ps.report@gmx.net> X-Mailer: git-send-email 2.40.1 MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Provags-ID: V03:K1:uS37nkfLUX+QlQdlPd7/9Sg4Bzqhr1B/EbrstN6HnfTREyuyS9P qOOJ9wISlT3P3YH4jTOJOCWbMUvpOovl2RDaHHXtEKzGsPHNSHCmbmO9o/QxMpuJVcTEg/Z G9gBv3udAM/QvEW5gSqm7ELwGNWtVL8W8R/99AkEeSmYtMT6GM+xKql4N2rPjLM1J/oBNce N8aPIwNmSplAi6IYS8wKQ== UI-OutboundReport: notjunk:1;M01:P0:3d6P4lPNEBg=;4zUB54WTAcjSAm4U8R413HGmmme wMj6xQwketQOI4XfRCclSyEBSsh5DXbc6o+PiXJj2hyVzDErg9KIb1f01AHgY3ZqNjVQYugCz VXn570C+7d01nhS6Jl1xRo/GFwkZhtyJ1KGL/R+9x69omuxZPdBaBf8PAPW1rhCgGNSG229XW f4NzOvs8pJnkq51T1kbnAflUEZQ2zMN8j0k4sXpsdOaXQ+CDsYaN5lge2HgsLF8ApWtfRgdan CQ4PGDCqOUJIqglwphFkCtrVr7QKZCVUw4OcvtSxrwIV6R1qH1AMp25KkjEh6oSKYyEpIdXdf pfEnEl6rllJr/hfgvBkry2YyRovP2uDl0iJgcWwHml0CMyRFQMwCSSXtmbPg2cyHfqktvg5fB OEGg6IXhZyaKg7IdufYgj67nJDWKL1PHxot7MqaQih7WJQMM3UeI0gug+MaqGhnld1l25W+Eo Mb8QytXQT9pgt5o5K8EB746aNGkFW/xzy9aDhQd5+COf5+r3BiMTFFHosYBAJ1N1a4FyhJ+iG HQ1ipXkTAIiHvzVJYQqVKscQXAtt4lY7Z4pqyk3Eqd109bfl8bUPpwEx1seBh7u/FJrUA20q+ wsx3PJ72AgZWaAkPc7DjqqC+5UO6x5Fj8PErS0gbRk541KTgVMksjSk2tGOnFljUJBafwoak0 GFgGzUJtimXtH6CD6dhaZ2FHl6HErT6NcKQHFqBT+tqcL1Njy6XypV8dVmO7sAVKa5DJhiGlG DKxPPR8m0+9lvGrihAKcAwu3hqIqA+i1BJdkj9OFDlC3lYovQ6hCrmnJqsl4mxGz5XLFoMhal kpZD93h51wQJyeT0hGbNjhl0DHkhUA9Ic+XYJSOLuQ+XznyyEL9zBl+0UmWUBd9WIBHol8bA8 XrE4b/VjMC2UIIpPms4PC2lAPB62+4OO8+bCxg6FkG+79+zS1JQDo/LE+m7RIhsmpD/VHHqA7 RRnfhnWKwBb6zGYGLPphNBiZ4pE= Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" From: Florian Ferg This patch adds the driver for the IXXAT USB-to-CAN interfaces. There is an adapter for the older communication layer cl1 and another adapter for the newer communication layer cl2. Signed-off-by: Florian Ferg Signed-off-by: Marc Kleine-Budde Signed-off-by: Peter Seiderer --- Changes v1 -> v7 (Florian Ferg ): - for detailed history see https://codeberg.org/psreport/socketcan-linux-ix-usb-can Changes v7 -> v8 (Marc Kleine-Budde ) - port to recent net-next Changes v8 -> v9 (Peter Seiderer ) Addressed review comments by Vincent Mailhol: - update copyright headers (use SPDX identifier 'GPL-2.0-only' instead of 'GPL-2.0', remove full GPL boilerplate) - reorder includes (lexicographic order) - remove IXXAT_USB_MODES defines (move modes directly in the declaration of structs) - remove bittiming defines (move directly in declaration of structs) - remove _EP1_IN/_EP1_OUT defines (move directly in declaration of struct= s) - remove rcv_size/snd_size vars (use sizeof(*cmd) and sizeof(cmd->res) di= rectly) - use GENMASK/FIELD_PREP (IXXAT_USB_CAN_BTR1_TIME_SEG1_MASK/IXXAT_USB_CAN= _BTR1_TIME_SEG2_MASK) - update dev_err/netdev_err (use %pe, remove 'Error:' prefix) - use U32_MAX instead of 0x00000000FFFFFFFF - do not increase the rx_bytes count when receiving a remove frame - ixxat_can_msg_cl1/ixxat_can_msg_cl2 use union __le32 status - do not increase the statistics for error frames - do not use parenthesis in log messages - remove double brackets (sparse workaround for struct init) - fill netdev->ethtool_ops with default - fill netdev->dev_port - use FIELD_GET/FIELD_PREP instead of IXXAT_USB_DECODE_DLC/IXXAT_USB_ENCO= DE_DLC - use driver_info intead of open coded ixxat_usb_get_adapter Addressed checkpatch.pl warnings: - change MODULE_LICENSE to 'GPL' - checkpatch.pl WARNING: Prefer "GPL" ov= er "GPL v2" --- drivers/net/can/usb/Kconfig | 17 + drivers/net/can/usb/Makefile | 1 + drivers/net/can/usb/ixxat_usb/Makefile | 2 + drivers/net/can/usb/ixxat_usb/ixxat_usb_cl1.c | 100 ++ drivers/net/can/usb/ixxat_usb/ixxat_usb_cl2.c | 176 +++ .../net/can/usb/ixxat_usb/ixxat_usb_core.c | 1277 +++++++++++++++++ .../net/can/usb/ixxat_usb/ixxat_usb_core.h | 511 +++++++ 7 files changed, 2084 insertions(+) create mode 100644 drivers/net/can/usb/ixxat_usb/Makefile create mode 100644 drivers/net/can/usb/ixxat_usb/ixxat_usb_cl1.c create mode 100644 drivers/net/can/usb/ixxat_usb/ixxat_usb_cl2.c create mode 100644 drivers/net/can/usb/ixxat_usb/ixxat_usb_core.c create mode 100644 drivers/net/can/usb/ixxat_usb/ixxat_usb_core.h diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig index 58fcd2b34820..b50706f2a5e6 100644 --- a/drivers/net/can/usb/Kconfig +++ b/drivers/net/can/usb/Kconfig @@ -62,6 +62,23 @@ config CAN_GS_USB choose Y for built in support, M to compile as module (module will be named: gs_usb). =20 +config CAN_IXXAT_USB + tristate "IXXAT USB-to-CAN interfaces" + help + This driver adds support for IXXAT USB-to-CAN devices. + + The driver provides support for the following devices: + - IXXAT USB-to-CAN compact + - IXXAT USB-to-CAN embedded + - IXXAT USB-to-CAN professional + - IXXAT USB-to-CAN automotive + - IXXAT USB-to-CAN FD compact + - IXXAT USB-to-CAN FD professional + - IXXAT USB-to-CAN FD automotive + - IXXAT USB-to-CAN FD MiniPCIe + - IXXAT USB-to-CAR + - IXXAT CAN-IDM101 + config CAN_KVASER_USB tristate "Kvaser CAN/USB interface" help diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile index 8b11088e9a59..52b4cc66ff30 100644 --- a/drivers/net/can/usb/Makefile +++ b/drivers/net/can/usb/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_CAN_ESD_USB) +=3D esd_usb.o obj-$(CONFIG_CAN_ETAS_ES58X) +=3D etas_es58x/ obj-$(CONFIG_CAN_F81604) +=3D f81604.o obj-$(CONFIG_CAN_GS_USB) +=3D gs_usb.o +obj-$(CONFIG_CAN_IXXAT_USB) +=3D ixxat_usb/ obj-$(CONFIG_CAN_KVASER_USB) +=3D kvaser_usb/ obj-$(CONFIG_CAN_MCBA_USB) +=3D mcba_usb.o obj-$(CONFIG_CAN_PEAK_USB) +=3D peak_usb/ diff --git a/drivers/net/can/usb/ixxat_usb/Makefile b/drivers/net/can/usb/i= xxat_usb/Makefile new file mode 100644 index 000000000000..125d2705979f --- /dev/null +++ b/drivers/net/can/usb/ixxat_usb/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_CAN_IXXAT_USB) +=3D ixxat_usb2can.o +ixxat_usb2can-y =3D ixxat_usb_cl1.o ixxat_usb_cl2.o ixxat_usb_core.o diff --git a/drivers/net/can/usb/ixxat_usb/ixxat_usb_cl1.c b/drivers/net/ca= n/usb/ixxat_usb/ixxat_usb_cl1.c new file mode 100644 index 000000000000..cfd95ff722cd --- /dev/null +++ b/drivers/net/can/usb/ixxat_usb/ixxat_usb_cl1.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2018 HMS Industrial Networks + */ + +#include +#include +#include + +#include "ixxat_usb_core.h" + +#define IXXAT_USB_CLOCK 8000000 + +#define IXXAT_USB_BUFFER_SIZE_RX 512 +#define IXXAT_USB_BUFFER_SIZE_TX 256 + +#define IXXAT_USB_BTMODE_TSM_CL1 0x80 + +#define IXXAT_USB_CAN_CMD_INIT 0x325 + +#define IXXAT_USB_CAN_BTR0_BRP_MASK GENMASK(5, 0) +#define IXXAT_USB_CAN_BTR0_SJW_MASK GENMASK(7, 6) + +#define IXXAT_USB_CAN_BTR1_TIME_SEG1_MASK GENMASK(3, 0) +#define IXXAT_USB_CAN_BTR1_TIME_SEG2_MASK GENMASK(6, 4) + +static const struct can_bittiming_const usb2can_bt =3D { + .name =3D IXXAT_USB_CTRL_NAME, + .tseg1_min =3D 1, + .tseg1_max =3D 16, + .tseg2_min =3D 1, + .tseg2_max =3D 8, + .sjw_max =3D 4, + .brp_min =3D 1, + .brp_max =3D 64, + .brp_inc =3D 1, +}; + +static int ixxat_usb_init_ctrl(struct ixxat_usb_device *dev) +{ + const struct can_bittiming *bt =3D &dev->can.bittiming; + const u16 port =3D dev->ctrl_index; + int err; + struct ixxat_usb_init_cl1_cmd *cmd; + u8 opmode =3D IXXAT_USB_OPMODE_EXTENDED | IXXAT_USB_OPMODE_STANDARD; + u8 btr0 =3D FIELD_PREP(IXXAT_USB_CAN_BTR0_BRP_MASK, bt->brp - 1) | + FIELD_PREP(IXXAT_USB_CAN_BTR0_SJW_MASK, bt->sjw - 1); + u8 btr1 =3D FIELD_PREP(IXXAT_USB_CAN_BTR1_TIME_SEG1_MASK, bt->prop_seg + = bt->phase_seg1 - 1) | + FIELD_PREP(IXXAT_USB_CAN_BTR1_TIME_SEG2_MASK, bt->phase_seg2 - 1); + + cmd =3D kmalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + if (dev->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + btr1 |=3D IXXAT_USB_BTMODE_TSM_CL1; + if (dev->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) + opmode |=3D IXXAT_USB_OPMODE_ERRFRAME; + if (dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + opmode |=3D IXXAT_USB_OPMODE_LISTONLY; + + ixxat_usb_setup_cmd(&cmd->req, &cmd->res); + cmd->req.size =3D cpu_to_le32(sizeof(*cmd) - sizeof(cmd->res)); + cmd->req.code =3D cpu_to_le32(IXXAT_USB_CAN_CMD_INIT); + cmd->req.port =3D cpu_to_le16(port); + cmd->mode =3D opmode; + cmd->btr0 =3D btr0; + cmd->btr1 =3D btr1; + + err =3D ixxat_usb_send_cmd(dev->udev, port, cmd, sizeof(*cmd), &cmd->res, + sizeof(cmd->res)); + kfree(cmd); + return err; +} + +const struct ixxat_usb_adapter usb2can_cl1 =3D { + .clock =3D IXXAT_USB_CLOCK, + .bt =3D &usb2can_bt, + .btd =3D NULL, + .modes =3D CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_BERR_REPORTING | + CAN_CTRLMODE_LISTENONLY, + .buffer_size_rx =3D IXXAT_USB_BUFFER_SIZE_RX, + .buffer_size_tx =3D IXXAT_USB_BUFFER_SIZE_TX, + .ep_msg_in =3D { + 1 | USB_DIR_IN, + 2 | USB_DIR_IN, + 3 | USB_DIR_IN, + 4 | USB_DIR_IN, + 5 | USB_DIR_IN, + }, + .ep_msg_out =3D { + 1 | USB_DIR_OUT, + 2 | USB_DIR_OUT, + 3 | USB_DIR_OUT, + 4 | USB_DIR_OUT, + 5 | USB_DIR_OUT, + }, + .ep_offs =3D 0, + .init_ctrl =3D ixxat_usb_init_ctrl +}; diff --git a/drivers/net/can/usb/ixxat_usb/ixxat_usb_cl2.c b/drivers/net/ca= n/usb/ixxat_usb/ixxat_usb_cl2.c new file mode 100644 index 000000000000..268544f52f1e --- /dev/null +++ b/drivers/net/can/usb/ixxat_usb/ixxat_usb_cl2.c @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2018 HMS Industrial Networks + */ + +#include +#include +#include + +#include "ixxat_usb_core.h" + +#define IXXAT_USB_CLOCK 80000000 + +#define IXXAT_USB_BUFFER_SIZE_RX 512 +#define IXXAT_USB_BUFFER_SIZE_TX 512 + +#define IXXAT_USB_CAN_CMD_INIT 0x337 + +static const struct can_bittiming_const usb2can_bt =3D { + .name =3D IXXAT_USB_CTRL_NAME, + .tseg1_min =3D 1, + .tseg1_max =3D 256, + .tseg2_min =3D 1, + .tseg2_max =3D 256, + .sjw_max =3D 128, + .brp_min =3D 2, + .brp_max =3D 513, + .brp_inc =3D 1, +}; + +static const struct can_bittiming_const usb2can_btd =3D { + .name =3D IXXAT_USB_CTRL_NAME, + .tseg1_min =3D 1, + .tseg1_max =3D 256, + .tseg2_min =3D 1, + .tseg2_max =3D 256, + .sjw_max =3D 128, + .brp_min =3D 2, + .brp_max =3D 513, + .brp_inc =3D 1, +}; + +static const struct can_bittiming_const canidm_bt =3D { + .name =3D IXXAT_USB_CTRL_NAME, + .tseg1_min =3D 1, + .tseg1_max =3D 256, + .tseg2_min =3D 1, + .tseg2_max =3D 128, + .sjw_max =3D 128, + .brp_min =3D 1, + .brp_max =3D 512, + .brp_inc =3D 1 +}; + +static const struct can_bittiming_const canidm_btd =3D { + .name =3D IXXAT_USB_CTRL_NAME, + .tseg1_min =3D 1, + .tseg1_max =3D 32, + .tseg2_min =3D 1, + .tseg2_max =3D 16, + .sjw_max =3D 8, + .brp_min =3D 1, + .brp_max =3D 32, + .brp_inc =3D 1 +}; + +static int ixxat_usb_init_ctrl(struct ixxat_usb_device *dev) +{ + const struct can_bittiming *bt =3D &dev->can.bittiming; + const struct can_bittiming *btd =3D &dev->can.data_bittiming; + const u16 port =3D dev->ctrl_index; + int err; + struct ixxat_usb_init_cl2_cmd *cmd; + u32 btmode =3D IXXAT_USB_BTMODE_NAT; + u8 exmode =3D 0; + u8 opmode =3D IXXAT_USB_OPMODE_EXTENDED | IXXAT_USB_OPMODE_STANDARD; + + cmd =3D kmalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + if (dev->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + btmode =3D IXXAT_USB_BTMODE_TSM; + if (dev->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) + opmode |=3D IXXAT_USB_OPMODE_ERRFRAME; + if (dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + opmode |=3D IXXAT_USB_OPMODE_LISTONLY; + if (dev->can.ctrlmode & (CAN_CTRLMODE_FD | CAN_CTRLMODE_FD_NON_ISO)) { + exmode |=3D IXXAT_USB_EXMODE_EXTDATA | IXXAT_USB_EXMODE_FASTDATA; + + if (!(dev->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO)) + exmode |=3D IXXAT_USB_EXMODE_ISOFD; + } + + ixxat_usb_setup_cmd(&cmd->req, &cmd->res); + cmd->req.size =3D cpu_to_le32(sizeof(*cmd) - sizeof(cmd->res)); + cmd->req.code =3D cpu_to_le32(IXXAT_USB_CAN_CMD_INIT); + cmd->req.port =3D cpu_to_le16(port); + cmd->opmode =3D opmode; + cmd->exmode =3D exmode; + cmd->sdr.mode =3D cpu_to_le32(btmode); + cmd->sdr.bps =3D cpu_to_le32(bt->brp); + cmd->sdr.ts1 =3D cpu_to_le16(bt->prop_seg + bt->phase_seg1); + cmd->sdr.ts2 =3D cpu_to_le16(bt->phase_seg2); + cmd->sdr.sjw =3D cpu_to_le16(bt->sjw); + cmd->sdr.tdo =3D 0; + + if (exmode) { + cmd->fdr.mode =3D cpu_to_le32(btmode); + cmd->fdr.bps =3D cpu_to_le32(btd->brp); + cmd->fdr.ts1 =3D cpu_to_le16(btd->prop_seg + btd->phase_seg1); + cmd->fdr.ts2 =3D cpu_to_le16(btd->phase_seg2); + cmd->fdr.sjw =3D cpu_to_le16(btd->sjw); + cmd->fdr.tdo =3D cpu_to_le16(btd->brp * (btd->phase_seg1 + 1 + + btd->prop_seg)); + } + + err =3D ixxat_usb_send_cmd(dev->udev, port, cmd, sizeof(*cmd), &cmd->res, + sizeof(cmd->res)); + kfree(cmd); + return err; +} + +const struct ixxat_usb_adapter usb2can_cl2 =3D { + .clock =3D IXXAT_USB_CLOCK, + .bt =3D &usb2can_bt, + .btd =3D &usb2can_btd, + .modes =3D CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_BERR_REPORTING | CAN_CTRLMODE_FD | + CAN_CTRLMODE_FD_NON_ISO, + .buffer_size_rx =3D IXXAT_USB_BUFFER_SIZE_RX, + .buffer_size_tx =3D IXXAT_USB_BUFFER_SIZE_TX, + .ep_msg_in =3D { + 1 | USB_DIR_IN, + 2 | USB_DIR_IN, + 3 | USB_DIR_IN, + 4 | USB_DIR_IN, + 5 | USB_DIR_IN + }, + .ep_msg_out =3D { + 1 | USB_DIR_OUT, + 2 | USB_DIR_OUT, + 3 | USB_DIR_OUT, + 4 | USB_DIR_OUT, + 5 | USB_DIR_OUT, + }, + .ep_offs =3D 1, + .init_ctrl =3D ixxat_usb_init_ctrl +}; + +const struct ixxat_usb_adapter can_idm =3D { + .clock =3D IXXAT_USB_CLOCK, + .bt =3D &canidm_bt, + .btd =3D &canidm_btd, + .modes =3D CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_BERR_REPORTING | CAN_CTRLMODE_FD | + CAN_CTRLMODE_FD_NON_ISO, + .buffer_size_rx =3D IXXAT_USB_BUFFER_SIZE_RX, + .buffer_size_tx =3D IXXAT_USB_BUFFER_SIZE_TX, + .ep_msg_in =3D { + 2 | USB_DIR_IN, + 4 | USB_DIR_IN, + 6 | USB_DIR_IN, + 8 | USB_DIR_IN, + 10 | USB_DIR_IN, + }, + .ep_msg_out =3D { + 1 | USB_DIR_OUT, + 3 | USB_DIR_OUT, + 5 | USB_DIR_OUT, + 7 | USB_DIR_OUT, + 9 | USB_DIR_OUT, + }, + .ep_offs =3D 0, + .init_ctrl =3D ixxat_usb_init_ctrl +}; diff --git a/drivers/net/can/usb/ixxat_usb/ixxat_usb_core.c b/drivers/net/c= an/usb/ixxat_usb/ixxat_usb_core.c new file mode 100644 index 000000000000..8787d27a3886 --- /dev/null +++ b/drivers/net/can/usb/ixxat_usb/ixxat_usb_core.c @@ -0,0 +1,1277 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2018 HMS Industrial Networks + */ + +#include +#include +#include +#include +#include +#include + +#include "ixxat_usb_core.h" + +MODULE_AUTHOR("Marcel Schmidt "); +MODULE_DESCRIPTION("CAN driver for IXXAT USB-to-CAN / CAN FD adapters"); +MODULE_LICENSE("GPL"); + +/* Table of devices that work with this driver */ +static const struct usb_device_id ixxat_usb_table[] =3D { + { USB_DEVICE(IXXAT_USB_VENDOR_ID, USB2CAN_COMPACT_PRODUCT_ID), + .driver_info =3D (kernel_ulong_t)&usb2can_cl1, }, + { USB_DEVICE(IXXAT_USB_VENDOR_ID, USB2CAN_EMBEDDED_PRODUCT_ID), + .driver_info =3D (kernel_ulong_t)&usb2can_cl1, }, + { USB_DEVICE(IXXAT_USB_VENDOR_ID, USB2CAN_PROFESSIONAL_PRODUCT_ID), + .driver_info =3D (kernel_ulong_t)&usb2can_cl1, }, + { USB_DEVICE(IXXAT_USB_VENDOR_ID, USB2CAN_AUTOMOTIVE_PRODUCT_ID), + .driver_info =3D (kernel_ulong_t)&usb2can_cl1, }, + { USB_DEVICE(IXXAT_USB_VENDOR_ID, USB2CAN_FD_COMPACT_PRODUCT_ID), + .driver_info =3D (kernel_ulong_t)&usb2can_cl2, }, + { USB_DEVICE(IXXAT_USB_VENDOR_ID, USB2CAN_FD_PROFESSIONAL_PRODUCT_ID), + .driver_info =3D (kernel_ulong_t)&usb2can_cl2, }, + { USB_DEVICE(IXXAT_USB_VENDOR_ID, USB2CAN_FD_AUTOMOTIVE_PRODUCT_ID), + .driver_info =3D (kernel_ulong_t)&usb2can_cl2, }, + { USB_DEVICE(IXXAT_USB_VENDOR_ID, USB2CAN_FD_PCIE_MINI_PRODUCT_ID), + .driver_info =3D (kernel_ulong_t)&usb2can_cl2, }, + { USB_DEVICE(IXXAT_USB_VENDOR_ID, USB2CAR_PRODUCT_ID), + .driver_info =3D (kernel_ulong_t)&usb2can_cl2, }, + { USB_DEVICE(IXXAT_USB_VENDOR_ID, CAN_IDM101_PRODUCT_ID), + .driver_info =3D (kernel_ulong_t)&can_idm, }, + { USB_DEVICE(IXXAT_USB_VENDOR_ID, CAN_IDM200_PRODUCT_ID), + .driver_info =3D (kernel_ulong_t)&can_idm, }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, ixxat_usb_table); + +void ixxat_usb_setup_cmd(struct ixxat_usb_dal_req *req, + struct ixxat_usb_dal_res *res) +{ + req->size =3D cpu_to_le32(sizeof(*req)); + req->port =3D cpu_to_le16(0xffff); + req->socket =3D cpu_to_le16(0xffff); + req->code =3D cpu_to_le32(0); + + res->res_size =3D cpu_to_le32(sizeof(*res)); + res->ret_size =3D cpu_to_le32(0); + res->code =3D cpu_to_le32(0xffffffff); +} + +int ixxat_usb_send_cmd(struct usb_device *dev, const u16 port, void *req, + const u16 req_size, void *res, const u16 res_size) +{ + const int to =3D msecs_to_jiffies(IXXAT_USB_MSG_TIMEOUT); + const u8 rq =3D 0xff; + const u8 rti =3D USB_TYPE_VENDOR | USB_DIR_IN; + const u8 rto =3D USB_TYPE_VENDOR | USB_DIR_OUT; + int i; + int pos =3D 0; + int rcp =3D usb_rcvctrlpipe(dev, 0); + int scp =3D usb_sndctrlpipe(dev, 0); + int ret =3D 0; + struct ixxat_usb_dal_res *dal_res =3D res; + + for (i =3D 0; i < IXXAT_USB_MAX_COM_REQ; ++i) { + ret =3D usb_control_msg(dev, scp, rq, rto, port, 0, req, req_size, + to); + if (ret < 0) + msleep(IXXAT_USB_MSG_CYCLE); + else + break; + } + + if (ret < 0) { + dev_err(&dev->dev, "TX command failure: %pe\n", ERR_PTR(ret)); + goto fail; + } + + for (i =3D 0; i < IXXAT_USB_MAX_COM_REQ; ++i) { + const int rs =3D res_size - pos; + void *rb =3D res + pos; + + ret =3D usb_control_msg(dev, rcp, rq, rti, port, 0, rb, rs, to); + if (ret < 0) { + msleep(IXXAT_USB_MSG_CYCLE); + continue; + } + + pos +=3D ret; + if (pos < res_size) + msleep(IXXAT_USB_MSG_CYCLE); + else + break; + } + + if (pos !=3D res_size) + ret =3D -EBADMSG; + + if (ret < 0) { + dev_err(&dev->dev, "RX command failure: %pe\n", ERR_PTR(ret)); + goto fail; + } + + ret =3D le32_to_cpu(dal_res->code); + +fail: + return ret; +} + +static void ixxat_usb_update_ts_now(struct ixxat_usb_device *dev, u32 ts_n= ow) +{ + u32 *ts_dev =3D &dev->time_ref.ts_dev_0; + ktime_t *kt_host =3D &dev->time_ref.kt_host_0; + u64 timebase =3D (u64)U32_MAX - (u64)(*ts_dev) + (u64)ts_now; + + *kt_host =3D ktime_add_us(*kt_host, timebase); + *ts_dev =3D ts_now; +} + +static void ixxat_usb_get_ts_tv(struct ixxat_usb_device *dev, u32 ts, + ktime_t *k_time) +{ + ktime_t tmp_time =3D dev->time_ref.kt_host_0; + + if (ts < dev->time_ref.ts_dev_last) + ixxat_usb_update_ts_now(dev, ts); + + dev->time_ref.ts_dev_last =3D ts; + tmp_time =3D ktime_add_us(tmp_time, ts - dev->time_ref.ts_dev_0); + + if (k_time) + *k_time =3D tmp_time; +} + +static void ixxat_usb_set_ts_now(struct ixxat_usb_device *dev, u32 ts_now) +{ + dev->time_ref.ts_dev_0 =3D ts_now; + dev->time_ref.kt_host_0 =3D ktime_get_real(); + dev->time_ref.ts_dev_last =3D ts_now; +} + +static int ixxat_usb_get_dev_caps(struct usb_device *dev, + struct ixxat_dev_caps *dev_caps) +{ + int i; + int err; + struct ixxat_usb_caps_cmd *cmd; + const u32 cmd_size =3D sizeof(*cmd); + const u32 req_size =3D sizeof(cmd->req); + const u32 rcv_size =3D cmd_size - req_size; + const u32 snd_size =3D req_size + sizeof(cmd->res); + u16 num_ctrl; + + cmd =3D kmalloc(cmd_size, GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + ixxat_usb_setup_cmd(&cmd->req, &cmd->res); + cmd->req.code =3D cpu_to_le32(IXXAT_USB_BRD_CMD_GET_DEVCAPS); + cmd->res.res_size =3D cpu_to_le32(rcv_size); + + err =3D ixxat_usb_send_cmd(dev, le16_to_cpu(cmd->req.port), cmd, snd_size, + &cmd->res, rcv_size); + if (err) + goto fail; + + dev_caps->bus_ctrl_count =3D cmd->caps.bus_ctrl_count; + num_ctrl =3D le16_to_cpu(dev_caps->bus_ctrl_count); + if (num_ctrl > ARRAY_SIZE(dev_caps->bus_ctrl_types)) { + err =3D -EINVAL; + goto fail; + } + + for (i =3D 0; i < num_ctrl; i++) + dev_caps->bus_ctrl_types[i] =3D cmd->caps.bus_ctrl_types[i]; + +fail: + kfree(cmd); + return err; +} + +static int ixxat_usb_get_dev_info(struct usb_device *dev, + struct ixxat_dev_info *dev_info) +{ + int err; + struct ixxat_usb_info_cmd *cmd; + const u32 cmd_size =3D sizeof(*cmd); + const u32 req_size =3D sizeof(cmd->req); + const u32 rcv_size =3D cmd_size - req_size; + const u32 snd_size =3D req_size + sizeof(cmd->res); + + cmd =3D kmalloc(cmd_size, GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + ixxat_usb_setup_cmd(&cmd->req, &cmd->res); + cmd->req.code =3D cpu_to_le32(IXXAT_USB_BRD_CMD_GET_DEVINFO); + cmd->res.res_size =3D cpu_to_le32(rcv_size); + + err =3D ixxat_usb_send_cmd(dev, le16_to_cpu(cmd->req.port), cmd, snd_size, + &cmd->res, rcv_size); + if (err) + goto fail; + + if (dev_info) { + memcpy(dev_info->device_id, &cmd->info.device_id, + sizeof(cmd->info.device_id)); + memcpy(dev_info->device_name, &cmd->info.device_name, + sizeof(cmd->info.device_name)); + dev_info->device_fpga_version =3D cmd->info.device_fpga_version; + dev_info->device_version =3D cmd->info.device_version; + } + +fail: + kfree(cmd); + return err; +} + +static int ixxat_usb_start_ctrl(struct ixxat_usb_device *dev, u32 *time_re= f) +{ + const u16 port =3D dev->ctrl_index; + int err; + struct ixxat_usb_start_cmd *cmd; + const u32 cmd_size =3D sizeof(*cmd); + const u32 req_size =3D sizeof(cmd->req); + const u32 rcv_size =3D cmd_size - req_size; + const u32 snd_size =3D req_size + sizeof(cmd->res); + + cmd =3D kmalloc(cmd_size, GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + ixxat_usb_setup_cmd(&cmd->req, &cmd->res); + cmd->req.code =3D cpu_to_le32(IXXAT_USB_CAN_CMD_START); + cmd->req.port =3D cpu_to_le16(port); + cmd->res.res_size =3D cpu_to_le32(rcv_size); + cmd->time =3D 0; + + err =3D ixxat_usb_send_cmd(dev->udev, port, cmd, snd_size, &cmd->res, + rcv_size); + if (err) + goto fail; + + if (time_ref) + *time_ref =3D le32_to_cpu(cmd->time); + +fail: + kfree(cmd); + return err; +} + +static int ixxat_usb_stop_ctrl(struct ixxat_usb_device *dev) +{ + const u16 port =3D dev->ctrl_index; + int err; + struct ixxat_usb_stop_cmd *cmd; + const u32 rcv_size =3D sizeof(cmd->res); + const u32 snd_size =3D sizeof(*cmd); + + cmd =3D kmalloc(snd_size, GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + ixxat_usb_setup_cmd(&cmd->req, &cmd->res); + cmd->req.size =3D cpu_to_le32(snd_size - rcv_size); + cmd->req.code =3D cpu_to_le32(IXXAT_USB_CAN_CMD_STOP); + cmd->req.port =3D cpu_to_le16(port); + cmd->action =3D cpu_to_le32(IXXAT_USB_STOP_ACTION_CLEARALL); + + err =3D ixxat_usb_send_cmd(dev->udev, port, cmd, snd_size, &cmd->res, + rcv_size); + kfree(cmd); + return err; +} + +static int ixxat_usb_power_ctrl(struct usb_device *dev, u8 mode) +{ + int err; + struct ixxat_usb_power_cmd *cmd; + const u32 rcv_size =3D sizeof(cmd->res); + const u32 snd_size =3D sizeof(*cmd); + + cmd =3D kmalloc(snd_size, GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + ixxat_usb_setup_cmd(&cmd->req, &cmd->res); + cmd->req.size =3D cpu_to_le32(snd_size - rcv_size); + cmd->req.code =3D cpu_to_le32(IXXAT_USB_BRD_CMD_POWER); + cmd->mode =3D mode; + + err =3D ixxat_usb_send_cmd(dev, le16_to_cpu(cmd->req.port), cmd, snd_size, + &cmd->res, rcv_size); + kfree(cmd); + return err; +} + +static int ixxat_usb_reset_ctrl(struct ixxat_usb_device *dev) +{ + const u16 port =3D dev->ctrl_index; + int err; + struct ixxat_usb_dal_cmd *cmd; + const u32 snd_size =3D sizeof(*cmd); + const u32 rcv_size =3D sizeof(cmd->res); + + cmd =3D kmalloc(snd_size, GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + ixxat_usb_setup_cmd(&cmd->req, &cmd->res); + cmd->req.code =3D cpu_to_le32(IXXAT_USB_CAN_CMD_RESET); + cmd->req.port =3D cpu_to_le16(port); + + err =3D ixxat_usb_send_cmd(dev->udev, port, cmd, snd_size, &cmd->res, + rcv_size); + kfree(cmd); + return err; +} + +static void ixxat_usb_stop_queue(struct ixxat_usb_device *dev) +{ + struct net_device *netdev =3D dev->netdev; + u32 i; + + netif_stop_queue(netdev); + usb_kill_anchored_urbs(&dev->rx_submitted); + usb_kill_anchored_urbs(&dev->tx_submitted); + atomic_set(&dev->active_tx_urbs, 0); + for (i =3D 0; i < IXXAT_USB_MAX_TX_URBS; i++) { + if (dev->tx_contexts[i].echo_index !=3D IXXAT_USB_MAX_TX_URBS) { + can_free_echo_skb(netdev, i, NULL); + dev->tx_contexts[i].echo_index =3D IXXAT_USB_MAX_TX_URBS; + } + } +} + +static int ixxat_usb_restart(struct ixxat_usb_device *dev) +{ + int err; + struct net_device *netdev =3D dev->netdev; + u32 t; + + ixxat_usb_stop_queue(dev); + err =3D ixxat_usb_stop_ctrl(dev); + if (err) + goto fail; + + err =3D ixxat_usb_start_ctrl(dev, &t); + if (err) + goto fail; + + dev->can.state =3D CAN_STATE_ERROR_ACTIVE; + netif_wake_queue(netdev); + +fail: + return err; +} + +static int ixxat_usb_set_mode(struct net_device *netdev, enum can_mode mod= e) +{ + struct ixxat_usb_device *dev =3D netdev_priv(netdev); + + if (mode !=3D CAN_MODE_START) + return -EOPNOTSUPP; + + return ixxat_usb_restart(dev); +} + +static int ixxat_usb_get_berr_counter(const struct net_device *netdev, + struct can_berr_counter *bec) +{ + struct ixxat_usb_device *dev =3D netdev_priv(netdev); + + *bec =3D dev->bec; + return 0; +} + +static int ixxat_usb_handle_canmsg(struct ixxat_usb_device *dev, + struct ixxat_can_msg *rx) +{ + const u32 ixx_flags =3D le32_to_cpu(rx->base.flags); + const u8 dlc =3D FIELD_GET(IXXAT_USB_MSG_FLAGS_DLC, ixx_flags); + struct canfd_frame *cf; + struct net_device *netdev =3D dev->netdev; + struct sk_buff *skb; + u8 flags =3D 0; + u8 len; + u8 min_size; + + if (ixx_flags & IXXAT_USB_FDMSG_FLAGS_EDL) { + if (ixx_flags & IXXAT_USB_FDMSG_FLAGS_FDR) + flags |=3D CANFD_BRS; + + if (ixx_flags & IXXAT_USB_FDMSG_FLAGS_ESI) + flags |=3D CANFD_ESI; + + len =3D can_fd_dlc2len(dlc); + } else { + len =3D can_cc_dlc2len(dlc); + } + + min_size =3D sizeof(rx->base) + len; + + if (dev->adapter =3D=3D &usb2can_cl1) + min_size +=3D sizeof(rx->cl1) - sizeof(rx->cl1.data); + else + min_size +=3D sizeof(rx->cl2) - sizeof(rx->cl2.data); + + if (rx->base.size < (min_size - 1)) { + netdev_err(netdev, "Invalid can data message size\n"); + return -EBADMSG; + } + + if (ixx_flags & IXXAT_USB_MSG_FLAGS_OVR) { + netdev->stats.rx_over_errors++; + netdev->stats.rx_errors++; + netdev_err(netdev, "Message overflow\n"); + } + + if (ixx_flags & IXXAT_USB_FDMSG_FLAGS_EDL) + skb =3D alloc_canfd_skb(netdev, &cf); + else + skb =3D alloc_can_skb(netdev, (struct can_frame **)&cf); + + if (!skb) + return -ENOMEM; + + cf->can_id =3D le32_to_cpu(rx->base.msg_id); + cf->len =3D len; + cf->flags |=3D flags; + + if (ixx_flags & IXXAT_USB_MSG_FLAGS_EXT) + cf->can_id |=3D CAN_EFF_FLAG; + + if (ixx_flags & IXXAT_USB_MSG_FLAGS_RTR) { + cf->can_id |=3D CAN_RTR_FLAG; + } else { + if (dev->adapter =3D=3D &usb2can_cl1) + memcpy(cf->data, rx->cl1.data, len); + else + memcpy(cf->data, rx->cl2.data, len); + + netdev->stats.rx_bytes +=3D cf->len; + } + + ixxat_usb_get_ts_tv(dev, le32_to_cpu(rx->base.time), &skb->tstamp); + + netdev->stats.rx_packets++; + + netif_rx(skb); + + return 0; +} + +static int ixxat_usb_handle_status(struct ixxat_usb_device *dev, + struct ixxat_can_msg *rx) +{ + struct net_device *netdev =3D dev->netdev; + struct can_frame *can_frame; + struct sk_buff *skb; + enum can_state new_state =3D CAN_STATE_ERROR_ACTIVE; + u32 raw_status; + u8 min_size =3D sizeof(rx->base) + sizeof(raw_status); + + if (dev->adapter =3D=3D &usb2can_cl1) + min_size +=3D sizeof(rx->cl1) - sizeof(rx->cl1.data); + else + min_size +=3D sizeof(rx->cl2) - sizeof(rx->cl2.data); + + if (rx->base.size < (min_size - 1)) { + netdev_err(netdev, "Invalid can status message size\n"); + return -EBADMSG; + } + + if (dev->adapter =3D=3D &usb2can_cl1) + raw_status =3D le32_to_cpu(rx->cl1.status); + else + raw_status =3D le32_to_cpu(rx->cl2.status); + + if (raw_status !=3D IXXAT_USB_CAN_STATUS_OK) { + if (raw_status & IXXAT_USB_CAN_STATUS_BUSOFF) { + dev->can.can_stats.bus_off++; + new_state =3D CAN_STATE_BUS_OFF; + can_bus_off(netdev); + } else { + if (raw_status & IXXAT_USB_CAN_STATUS_ERRLIM) { + dev->can.can_stats.error_warning++; + new_state =3D CAN_STATE_ERROR_WARNING; + } + + if (raw_status & IXXAT_USB_CAN_STATUS_ERR_PAS) { + dev->can.can_stats.error_passive++; + new_state =3D CAN_STATE_ERROR_PASSIVE; + } + + if (raw_status & IXXAT_USB_CAN_STATUS_OVERRUN) + new_state =3D CAN_STATE_MAX; + } + } + + if (new_state =3D=3D CAN_STATE_ERROR_ACTIVE) { + dev->bec.txerr =3D 0; + dev->bec.rxerr =3D 0; + } + + if (new_state !=3D CAN_STATE_MAX) + dev->can.state =3D new_state; + + skb =3D alloc_can_err_skb(netdev, &can_frame); + if (!skb) + return -ENOMEM; + + switch (new_state) { + case CAN_STATE_ERROR_ACTIVE: + can_frame->can_id |=3D CAN_ERR_CRTL; + can_frame->data[1] |=3D CAN_ERR_CRTL_ACTIVE; + break; + case CAN_STATE_ERROR_WARNING: + can_frame->can_id |=3D CAN_ERR_CRTL; + can_frame->data[1] |=3D CAN_ERR_CRTL_TX_WARNING; + can_frame->data[1] |=3D CAN_ERR_CRTL_RX_WARNING; + break; + case CAN_STATE_ERROR_PASSIVE: + can_frame->can_id |=3D CAN_ERR_CRTL; + can_frame->data[1] |=3D CAN_ERR_CRTL_TX_PASSIVE; + can_frame->data[1] |=3D CAN_ERR_CRTL_RX_PASSIVE; + break; + case CAN_STATE_BUS_OFF: + can_frame->can_id |=3D CAN_ERR_BUSOFF; + break; + case CAN_STATE_MAX: + can_frame->can_id |=3D CAN_ERR_CRTL; + can_frame->data[1] |=3D CAN_ERR_CRTL_RX_OVERFLOW; + break; + default: + netdev_err(netdev, "Unhandled can status %d\n", + new_state); + break; + } + + netif_rx(skb); + + return 0; +} + +static int ixxat_usb_handle_error(struct ixxat_usb_device *dev, + struct ixxat_can_msg *rx) +{ + struct net_device *netdev =3D dev->netdev; + struct can_frame *can_frame; + struct sk_buff *skb; + u8 raw_error; + u8 min_size =3D sizeof(rx->base) + IXXAT_USB_CAN_ERROR_LEN; + + if (dev->adapter =3D=3D &usb2can_cl1) + min_size +=3D sizeof(rx->cl1) - sizeof(rx->cl1.data); + else + min_size +=3D sizeof(rx->cl2) - sizeof(rx->cl2.data); + + if (rx->base.size < (min_size - 1)) { + netdev_err(netdev, "Invalid can error message size\n"); + return -EBADMSG; + } + + if (dev->can.state =3D=3D CAN_STATE_BUS_OFF) + return 0; + + if (dev->adapter =3D=3D &usb2can_cl1) { + raw_error =3D rx->cl1.data[IXXAT_USB_CAN_ERROR_CODE]; + dev->bec.rxerr =3D rx->cl1.data[IXXAT_USB_CAN_ERROR_COUNTER_RX]; + dev->bec.txerr =3D rx->cl1.data[IXXAT_USB_CAN_ERROR_COUNTER_TX]; + } else { + raw_error =3D rx->cl2.data[IXXAT_USB_CAN_ERROR_CODE]; + dev->bec.rxerr =3D rx->cl2.data[IXXAT_USB_CAN_ERROR_COUNTER_RX]; + dev->bec.txerr =3D rx->cl2.data[IXXAT_USB_CAN_ERROR_COUNTER_TX]; + } + + if (raw_error =3D=3D IXXAT_USB_CAN_ERROR_ACK) + netdev->stats.tx_errors++; + else + netdev->stats.rx_errors++; + + skb =3D alloc_can_err_skb(netdev, &can_frame); + if (!skb) + return -ENOMEM; + + switch (raw_error) { + case IXXAT_USB_CAN_ERROR_ACK: + can_frame->can_id |=3D CAN_ERR_ACK; + break; + case IXXAT_USB_CAN_ERROR_BIT: + can_frame->can_id |=3D CAN_ERR_PROT; + can_frame->data[2] |=3D CAN_ERR_PROT_BIT; + break; + case IXXAT_USB_CAN_ERROR_CRC: + can_frame->can_id |=3D CAN_ERR_PROT; + can_frame->data[3] |=3D CAN_ERR_PROT_LOC_CRC_SEQ; + break; + case IXXAT_USB_CAN_ERROR_FORM: + can_frame->can_id |=3D CAN_ERR_PROT; + can_frame->data[2] |=3D CAN_ERR_PROT_FORM; + break; + case IXXAT_USB_CAN_ERROR_STUFF: + can_frame->can_id |=3D CAN_ERR_PROT; + can_frame->data[2] |=3D CAN_ERR_PROT_STUFF; + break; + default: + can_frame->can_id |=3D CAN_ERR_PROT; + can_frame->data[2] |=3D CAN_ERR_PROT_UNSPEC; + break; + } + + netif_rx(skb); + + return 0; +} + +static void ixxat_usb_decode_buf(struct urb *urb) +{ + struct ixxat_usb_device *dev =3D urb->context; + struct net_device *netdev =3D dev->netdev; + struct ixxat_can_msg *can_msg; + int err =3D 0; + u32 pos =3D 0; + u8 *data =3D urb->transfer_buffer; + + while (pos < urb->actual_length) { + u32 time; + u8 size; + u8 type; + + can_msg =3D (struct ixxat_can_msg *)&data[pos]; + if (!can_msg || !can_msg->base.size) { + err =3D -ENOTSUPP; + netdev_err(netdev, "Unsupported usb msg: %pe\n", + ERR_PTR(err)); + break; + } + + size =3D can_msg->base.size + 1; + if (size < sizeof(can_msg->base) || + (pos + size) > urb->actual_length) { + err =3D -EBADMSG; + netdev_err(netdev, + "Invalid usb message size: %pe\n", + ERR_PTR(err)); + break; + } + + type =3D le32_to_cpu(can_msg->base.flags); + type &=3D IXXAT_USB_MSG_FLAGS_TYPE; + time =3D le32_to_cpu(can_msg->base.time); + + switch (type) { + case IXXAT_USB_CAN_DATA: + err =3D ixxat_usb_handle_canmsg(dev, can_msg); + if (err) + goto fail; + break; + case IXXAT_USB_CAN_STATUS: + err =3D ixxat_usb_handle_status(dev, can_msg); + if (err) + goto fail; + break; + case IXXAT_USB_CAN_ERROR: + err =3D ixxat_usb_handle_error(dev, can_msg); + if (err) + goto fail; + break; + case IXXAT_USB_CAN_TIMEOVR: + ixxat_usb_get_ts_tv(dev, time, NULL); + break; + case IXXAT_USB_CAN_INFO: + case IXXAT_USB_CAN_WAKEUP: + case IXXAT_USB_CAN_TIMERST: + break; + default: + netdev_err(netdev, + "Unhandled rec type 0x%02x : ignored\n", + type); + break; + } + + pos +=3D size; + } + +fail: + if (err) + netdev_err(netdev, "Buffer decoding failed: %pe\n", ERR_PTR(err)); +} + +static int ixxat_usb_encode_msg(struct ixxat_usb_device *dev, + struct sk_buff *skb, u8 *obuf) +{ + int size; + struct canfd_frame *cf =3D (struct canfd_frame *)skb->data; + struct ixxat_can_msg can_msg =3D { 0 }; + struct ixxat_can_msg_base *msg_base =3D &can_msg.base; + u32 flags =3D 0; + u32 msg_id =3D 0; + + if (cf->can_id & CAN_RTR_FLAG) + flags |=3D IXXAT_USB_MSG_FLAGS_RTR; + + if (cf->can_id & CAN_EFF_FLAG) { + flags |=3D IXXAT_USB_MSG_FLAGS_EXT; + msg_id =3D cf->can_id & CAN_EFF_MASK; + } else { + msg_id =3D cf->can_id & CAN_SFF_MASK; + } + + if (can_is_canfd_skb(skb)) { + flags |=3D IXXAT_USB_FDMSG_FLAGS_EDL; + + if (!(cf->can_id & CAN_RTR_FLAG) && (cf->flags & CANFD_BRS)) + flags |=3D IXXAT_USB_FDMSG_FLAGS_FDR; + + flags |=3D FIELD_PREP(IXXAT_USB_MSG_FLAGS_DLC, can_fd_len2dlc(cf->len)); + } else { + flags |=3D FIELD_PREP(IXXAT_USB_MSG_FLAGS_DLC, cf->len); + } + + msg_base->flags =3D cpu_to_le32(flags); + msg_base->msg_id =3D cpu_to_le32(msg_id); + msg_base->size =3D sizeof(*msg_base) + cf->len - 1; + if (dev->adapter =3D=3D &usb2can_cl1) { + msg_base->size +=3D sizeof(can_msg.cl1); + msg_base->size -=3D sizeof(can_msg.cl1.data); + memcpy(can_msg.cl1.data, cf->data, cf->len); + } else { + msg_base->size +=3D sizeof(can_msg.cl2); + msg_base->size -=3D sizeof(can_msg.cl2.data); + memcpy(can_msg.cl2.data, cf->data, cf->len); + } + + size =3D msg_base->size + 1; + memcpy(obuf, &can_msg, size); + return size; +} + +static void ixxat_usb_read_bulk_callback(struct urb *urb) +{ + struct ixxat_usb_device *dev =3D urb->context; + const struct ixxat_usb_adapter *adapter =3D dev->adapter; + struct net_device *netdev =3D dev->netdev; + struct usb_device *udev =3D dev->udev; + int err; + + if (!netif_device_present(netdev)) + return; + + switch (urb->status) { + case 0: /* success */ + break; + case -EPROTO: + case -EILSEQ: + case -ENOENT: + case -ECONNRESET: + case -ESHUTDOWN: + return; + default: + netdev_err(netdev, "Rx urb aborted %d\n", urb->status); + goto resubmit_urb; + } + + if (urb->actual_length > 0) + if (dev->state & IXXAT_USB_STATE_STARTED) + ixxat_usb_decode_buf(urb); + +resubmit_urb: + usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, dev->ep_msg_in), + urb->transfer_buffer, adapter->buffer_size_rx, + ixxat_usb_read_bulk_callback, dev); + + usb_anchor_urb(urb, &dev->rx_submitted); + err =3D usb_submit_urb(urb, GFP_ATOMIC); + if (!err) + return; + + usb_unanchor_urb(urb); + + if (err =3D=3D -ENODEV) + netif_device_detach(netdev); + else + netdev_err(netdev, + "Failed to resubmit read bulk urb: %pe\n", ERR_PTR(err)); +} + +static void ixxat_usb_write_bulk_callback(struct urb *urb) +{ + struct ixxat_tx_urb_context *context =3D urb->context; + struct ixxat_usb_device *dev; + struct net_device *netdev; + + if (WARN_ON(!context)) + return; + + dev =3D context->dev; + netdev =3D dev->netdev; + + if (!netif_device_present(netdev)) + return; + + if (!urb->status) { + netdev->stats.tx_packets++; + netdev->stats.tx_bytes +=3D can_get_echo_skb(netdev, context->echo_index= , NULL); + } else { + netdev_err(netdev, "Tx urb aborted: %pe\n", ERR_PTR(urb->status)); + can_free_echo_skb(netdev, context->echo_index, NULL); + } + + context->echo_index =3D IXXAT_USB_MAX_TX_URBS; + atomic_dec(&dev->active_tx_urbs); + + if (!urb->status) + netif_wake_queue(netdev); +} + +static netdev_tx_t ixxat_usb_start_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + int err; + int size; + struct ixxat_usb_device *dev =3D netdev_priv(netdev); + struct ixxat_tx_urb_context *context =3D NULL; + struct net_device_stats *stats =3D &netdev->stats; + struct urb *urb; + u8 *obuf; + u32 i; + + if (can_dropped_invalid_skb(netdev, skb)) + return NETDEV_TX_OK; + + for (i =3D 0; i < IXXAT_USB_MAX_TX_URBS; i++) { + if (dev->tx_contexts[i].echo_index =3D=3D IXXAT_USB_MAX_TX_URBS) { + context =3D dev->tx_contexts + i; + break; + } + } + + if (WARN_ON_ONCE(!context)) + return NETDEV_TX_BUSY; + + urb =3D context->urb; + obuf =3D urb->transfer_buffer; + + size =3D ixxat_usb_encode_msg(dev, skb, obuf); + + context->echo_index =3D i; + + urb->transfer_buffer_length =3D size; + usb_anchor_urb(urb, &dev->tx_submitted); + can_put_echo_skb(skb, netdev, i, 0); + atomic_inc(&dev->active_tx_urbs); + + err =3D usb_submit_urb(urb, GFP_ATOMIC); + if (err) { + can_free_echo_skb(netdev, i, NULL); + usb_unanchor_urb(urb); + atomic_dec(&dev->active_tx_urbs); + + context->echo_index =3D IXXAT_USB_MAX_TX_URBS; + + if (err =3D=3D -ENODEV) { + netif_device_detach(netdev); + } else { + stats->tx_dropped++; + netdev_err(netdev, + "Submitting tx-urb failed: %pe\n", ERR_PTR(err)); + } + } else { + if (atomic_read(&dev->active_tx_urbs) >=3D IXXAT_USB_MAX_TX_URBS) + netif_stop_queue(netdev); + } + + return NETDEV_TX_OK; +} + +static int ixxat_usb_setup_rx_urbs(struct ixxat_usb_device *dev) +{ + int i; + int err =3D 0; + const struct ixxat_usb_adapter *adapter =3D dev->adapter; + struct net_device *netdev =3D dev->netdev; + struct usb_device *udev =3D dev->udev; + + for (i =3D 0; i < IXXAT_USB_MAX_RX_URBS; i++) { + struct urb *urb; + u8 *buf; + + urb =3D usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + err =3D -ENOMEM; + netdev_err(netdev, "No memory for URBs: %pe\n", + ERR_PTR(err)); + break; + } + + buf =3D kmalloc(adapter->buffer_size_rx, GFP_KERNEL); + if (!buf) { + usb_free_urb(urb); + err =3D -ENOMEM; + netdev_err(netdev, + "No memory for USB-buffer: %pe\n", ERR_PTR(err)); + break; + } + + usb_fill_bulk_urb(urb, udev, + usb_rcvbulkpipe(udev, dev->ep_msg_in), buf, + adapter->buffer_size_rx, + ixxat_usb_read_bulk_callback, dev); + + urb->transfer_flags |=3D URB_FREE_BUFFER; + usb_anchor_urb(urb, &dev->rx_submitted); + + err =3D usb_submit_urb(urb, GFP_KERNEL); + if (err) { + usb_unanchor_urb(urb); + kfree(buf); + usb_free_urb(urb); + + if (err =3D=3D -ENODEV) + netif_device_detach(netdev); + + break; + } + + usb_free_urb(urb); + } + + if (i =3D=3D 0) + netdev_err(netdev, "Couldn't setup any rx-URBs\n"); + + return err; +} + +static int ixxat_usb_setup_tx_urbs(struct ixxat_usb_device *dev) +{ + int i; + int ret =3D 0; + const struct ixxat_usb_adapter *adapter =3D dev->adapter; + struct net_device *netdev =3D dev->netdev; + struct usb_device *udev =3D dev->udev; + + for (i =3D 0; i < IXXAT_USB_MAX_TX_URBS; i++) { + struct ixxat_tx_urb_context *context; + struct urb *urb; + u8 *buf; + + urb =3D usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + ret =3D -ENOMEM; + netdev_err(netdev, "No memory for URBs: %pe\n", + ERR_PTR(ret)); + break; + } + + buf =3D kmalloc(adapter->buffer_size_tx, GFP_KERNEL); + if (!buf) { + usb_free_urb(urb); + ret =3D -ENOMEM; + netdev_err(netdev, + "No memory for USB-buffer: %pe\n", ERR_PTR(ret)); + break; + } + + context =3D dev->tx_contexts + i; + context->dev =3D dev; + context->urb =3D urb; + + usb_fill_bulk_urb(urb, udev, + usb_sndbulkpipe(udev, dev->ep_msg_out), buf, + adapter->buffer_size_tx, + ixxat_usb_write_bulk_callback, context); + + urb->transfer_flags |=3D URB_FREE_BUFFER; + } + + if (i =3D=3D 0) { + netdev_err(netdev, "Couldn't setup any tx-URBs\n"); + usb_kill_anchored_urbs(&dev->rx_submitted); + } + + return ret; +} + +static void ixxat_usb_disconnect(struct usb_interface *intf) +{ + struct ixxat_usb_device *dev; + struct ixxat_usb_device *prev_dev; + + /* unregister the given device and all previous devices */ + for (dev =3D usb_get_intfdata(intf); dev; dev =3D prev_dev) { + prev_dev =3D dev->prev_dev; + unregister_netdev(dev->netdev); + free_candev(dev->netdev); + } + + usb_set_intfdata(intf, NULL); +} + +static int ixxat_usb_start(struct ixxat_usb_device *dev) +{ + int err; + int i; + u32 time_ref =3D 0; + const struct ixxat_usb_adapter *adapter =3D dev->adapter; + + err =3D ixxat_usb_setup_rx_urbs(dev); + if (err) + return err; + + err =3D ixxat_usb_setup_tx_urbs(dev); + if (err) + return err; + + /* Try to reset the controller, in case it is already initialized + * from a previous unclean shutdown + */ + ixxat_usb_reset_ctrl(dev); + + if (adapter->init_ctrl) { + err =3D adapter->init_ctrl(dev); + if (err) + goto fail; + } + + if (!(dev->state & IXXAT_USB_STATE_STARTED)) { + err =3D ixxat_usb_start_ctrl(dev, &time_ref); + if (err) + goto fail; + + ixxat_usb_set_ts_now(dev, time_ref); + } + + dev->bec.txerr =3D 0; + dev->bec.rxerr =3D 0; + + dev->state |=3D IXXAT_USB_STATE_STARTED; + dev->can.state =3D CAN_STATE_ERROR_ACTIVE; + return 0; + +fail: + if (err =3D=3D -ENODEV) + netif_device_detach(dev->netdev); + + netdev_err(dev->netdev, "Couldn't submit control: %pe\n", ERR_PTR(err)); + + for (i =3D 0; i < IXXAT_USB_MAX_TX_URBS; i++) { + usb_free_urb(dev->tx_contexts[i].urb); + dev->tx_contexts[i].urb =3D NULL; + } + + return err; +} + +static int ixxat_usb_open(struct net_device *netdev) +{ + struct ixxat_usb_device *dev =3D netdev_priv(netdev); + int err; + + /* common open */ + err =3D open_candev(netdev); + if (err) + goto fail; + + /* finally start device */ + err =3D ixxat_usb_start(dev); + if (err) { + netdev_err(netdev, "Couldn't start device: %pe\n", ERR_PTR(err)); + close_candev(netdev); + goto fail; + } + + netif_start_queue(netdev); + +fail: + return err; +} + +static int ixxat_usb_stop(struct net_device *netdev) +{ + int err =3D 0; + struct ixxat_usb_device *dev =3D netdev_priv(netdev); + + ixxat_usb_stop_queue(dev); + if (dev->state & IXXAT_USB_STATE_STARTED) { + err =3D ixxat_usb_stop_ctrl(dev); + if (err) + netdev_warn(netdev, "Error %d: Cannot stop device\n", + err); + } + + dev->state &=3D ~IXXAT_USB_STATE_STARTED; + close_candev(netdev); + dev->can.state =3D CAN_STATE_STOPPED; + return err; +} + +static const struct net_device_ops ixxat_usb_netdev_ops =3D { + .ndo_open =3D ixxat_usb_open, + .ndo_stop =3D ixxat_usb_stop, + .ndo_start_xmit =3D ixxat_usb_start_xmit +}; + +static const struct ethtool_ops ixxat_usb_ethtool_ops =3D { + .get_ts_info =3D ethtool_op_get_ts_info, +}; + +static int ixxat_usb_create_dev(struct usb_interface *intf, + const struct ixxat_usb_adapter *adapter, + u16 ctrl_index) +{ + struct usb_device *udev =3D interface_to_usbdev(intf); + struct ixxat_usb_device *dev; + struct net_device *netdev; + int err; + int i; + + netdev =3D alloc_candev(sizeof(*dev), IXXAT_USB_MAX_TX_URBS); + if (!netdev) { + dev_err(&intf->dev, "Cannot allocate candev\n"); + return -ENOMEM; + } + + dev =3D netdev_priv(netdev); + dev->udev =3D udev; + dev->netdev =3D netdev; + dev->adapter =3D adapter; + dev->ctrl_index =3D ctrl_index; + dev->state =3D IXXAT_USB_STATE_CONNECTED; + + i =3D ctrl_index + adapter->ep_offs; + dev->ep_msg_in =3D adapter->ep_msg_in[i]; + dev->ep_msg_out =3D adapter->ep_msg_out[i]; + + dev->can.clock.freq =3D adapter->clock; + dev->can.bittiming_const =3D adapter->bt; + dev->can.data_bittiming_const =3D adapter->btd; + + dev->can.do_set_mode =3D ixxat_usb_set_mode; + dev->can.do_get_berr_counter =3D ixxat_usb_get_berr_counter; + + dev->can.ctrlmode_supported =3D adapter->modes; + + netdev->netdev_ops =3D &ixxat_usb_netdev_ops; + netdev->ethtool_ops =3D &ixxat_usb_ethtool_ops; + + netdev->flags |=3D IFF_ECHO; + netdev->dev_port =3D ctrl_index; + + init_usb_anchor(&dev->rx_submitted); + init_usb_anchor(&dev->tx_submitted); + + atomic_set(&dev->active_tx_urbs, 0); + + for (i =3D 0; i < IXXAT_USB_MAX_TX_URBS; i++) + dev->tx_contexts[i].echo_index =3D IXXAT_USB_MAX_TX_URBS; + + dev->prev_dev =3D usb_get_intfdata(intf); + usb_set_intfdata(intf, dev); + + SET_NETDEV_DEV(netdev, &intf->dev); + err =3D register_candev(netdev); + if (err) { + dev_err(&intf->dev, "Failed to register can device: %pe\n", + ERR_PTR(err)); + goto free_candev; + } + + if (dev->prev_dev) + (dev->prev_dev)->next_dev =3D dev; + + err =3D ixxat_usb_get_dev_info(udev, &dev->dev_info); + if (err) { + dev_err(&intf->dev, + "Failed to get device information: %pe\n", ERR_PTR(err)); + goto unreg_candev; + } + + netdev_info(netdev, "%s: Connected Channel %u (device %s)\n", + dev->dev_info.device_name, ctrl_index, + dev->dev_info.device_id); + + return 0; + +unreg_candev: + unregister_candev(netdev); +free_candev: + usb_set_intfdata(intf, dev->prev_dev); + free_candev(netdev); + return err; +} + +static int ixxat_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev =3D interface_to_usbdev(intf); + struct usb_host_interface *host_intf =3D intf->altsetting; + const struct ixxat_usb_adapter *adapter; + struct ixxat_dev_caps dev_caps; + u16 i; + int err; + + usb_reset_configuration(udev); + + adapter =3D (const struct ixxat_usb_adapter *)id->driver_info; + + for (i =3D 0; i < host_intf->desc.bNumEndpoints; i++) { + const u8 epaddr =3D host_intf->endpoint[i].desc.bEndpointAddress; + int match; + u8 j; + + /* Check if usb-endpoint address matches known usb-endpoints */ + for (j =3D 0; j < IXXAT_USB_MAX_CHANNEL; j++) { + u8 ep_msg_in =3D adapter->ep_msg_in[j]; + u8 ep_msg_out =3D adapter->ep_msg_in[j]; + + if (epaddr =3D=3D ep_msg_in || epaddr =3D=3D ep_msg_out) { + match =3D 1; + break; + } + } + + if (!match) + return -ENODEV; + } + + err =3D ixxat_usb_power_ctrl(udev, IXXAT_USB_POWER_WAKEUP); + if (err) + return err; + + msleep(IXXAT_USB_POWER_WAKEUP_TIME); + + err =3D ixxat_usb_get_dev_caps(udev, &dev_caps); + if (err) { + dev_err(&intf->dev, + "Failed to get device capabilities: %pe\n", ERR_PTR(err)); + return err; + } + + err =3D -ENODEV; + for (i =3D 0; i < le16_to_cpu(dev_caps.bus_ctrl_count); i++) { + u16 dev_bustype =3D le16_to_cpu(dev_caps.bus_ctrl_types[i]); + u8 bustype =3D IXXAT_USB_BUS_TYPE(dev_bustype); + + if (bustype =3D=3D IXXAT_USB_BUS_CAN) + err =3D ixxat_usb_create_dev(intf, adapter, i); + + if (err) { + /* deregister already created devices */ + ixxat_usb_disconnect(intf); + return err; + } + } + + return err; +} + +static struct usb_driver ixxat_usb_driver =3D { + .name =3D IXXAT_USB_DRIVER_NAME, + .probe =3D ixxat_usb_probe, + .disconnect =3D ixxat_usb_disconnect, + .id_table =3D ixxat_usb_table, +}; + +module_usb_driver(ixxat_usb_driver); diff --git a/drivers/net/can/usb/ixxat_usb/ixxat_usb_core.h b/drivers/net/c= an/usb/ixxat_usb/ixxat_usb_core.h new file mode 100644 index 000000000000..56c6f3b938d8 --- /dev/null +++ b/drivers/net/can/usb/ixxat_usb/ixxat_usb_core.h @@ -0,0 +1,511 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2018 HMS Industrial Networks + */ + +#ifndef IXXAT_USB_CORE_H +#define IXXAT_USB_CORE_H + +#define IXXAT_USB_DRIVER_NAME "ixxat_usb2can" +#define IXXAT_USB_CTRL_NAME "ixxat_usb" + +#define IXXAT_USB_VENDOR_ID 0x08d8 + +/* supported device ids: CL1 */ +#define USB2CAN_COMPACT_PRODUCT_ID 0x0008 +#define USB2CAN_EMBEDDED_PRODUCT_ID 0x0009 +#define USB2CAN_PROFESSIONAL_PRODUCT_ID 0x000A +#define USB2CAN_AUTOMOTIVE_PRODUCT_ID 0x000B + +/* supported device ids: CL2 */ +#define USB2CAN_FD_COMPACT_PRODUCT_ID 0x0014 +#define USB2CAN_FD_PROFESSIONAL_PRODUCT_ID 0x0016 +#define USB2CAN_FD_AUTOMOTIVE_PRODUCT_ID 0x0017 +#define USB2CAN_FD_PCIE_MINI_PRODUCT_ID 0x001B +#define USB2CAR_PRODUCT_ID 0x001C +#define CAN_IDM101_PRODUCT_ID 0xFF12 +#define CAN_IDM200_PRODUCT_ID 0xFF13 + +#define IXXAT_USB_BUS_CAN 1 + +#define IXXAT_USB_BUS_TYPE(type) ((u8)(((type) >> 8) & 0x00FF)) + +#define IXXAT_USB_STATE_CONNECTED BIT(0) +#define IXXAT_USB_STATE_STARTED BIT(1) + +#define IXXAT_USB_MAX_CHANNEL 5 +#define IXXAT_USB_MAX_TYPES 32 +#define IXXAT_USB_MAX_RX_URBS 4 +#define IXXAT_USB_MAX_TX_URBS 10 +#define IXXAT_USB_MAX_COM_REQ 10 + +#define IXXAT_USB_MSG_TIMEOUT 50 +#define IXXAT_USB_MSG_CYCLE 20 + +#define IXXAT_USB_POWER_WAKEUP 0 +#define IXXAT_USB_POWER_WAKEUP_TIME 500 + +#define IXXAT_USB_OPMODE_STANDARD BIT(0) +#define IXXAT_USB_OPMODE_EXTENDED BIT(1) +#define IXXAT_USB_OPMODE_ERRFRAME BIT(2) +#define IXXAT_USB_OPMODE_LISTONLY BIT(3) + +#define IXXAT_USB_EXMODE_EXTDATA BIT(0) +#define IXXAT_USB_EXMODE_FASTDATA BIT(1) +#define IXXAT_USB_EXMODE_ISOFD BIT(2) + +#define IXXAT_USB_BTMODE_NAT BIT(0) +#define IXXAT_USB_BTMODE_TSM BIT(1) + +#define IXXAT_USB_STOP_ACTION_CLEARALL 3 + +#define IXXAT_RESTART_TASK_CYCLE_TIME 20 + +#define IXXAT_USB_CAN_DATA 0x00 +#define IXXAT_USB_CAN_INFO 0x01 +#define IXXAT_USB_CAN_ERROR 0x02 +#define IXXAT_USB_CAN_STATUS 0x03 +#define IXXAT_USB_CAN_WAKEUP 0x04 +#define IXXAT_USB_CAN_TIMEOVR 0x05 +#define IXXAT_USB_CAN_TIMERST 0x06 + +#define IXXAT_USB_CAN_STATUS_OK 0x00000000 +#define IXXAT_USB_CAN_STATUS_OVERRUN 0x00000002 +#define IXXAT_USB_CAN_STATUS_ERRLIM 0x00000004 +#define IXXAT_USB_CAN_STATUS_BUSOFF 0x00000008 +#define IXXAT_USB_CAN_STATUS_ERR_PAS 0x00002000 + +#define IXXAT_USB_CAN_ERROR_LEN 5 + +#define IXXAT_USB_CAN_ERROR_CODE 0 +#define IXXAT_USB_CAN_ERROR_COUNTER_RX 3 +#define IXXAT_USB_CAN_ERROR_COUNTER_TX 4 + +#define IXXAT_USB_CAN_ERROR_STUFF 1 +#define IXXAT_USB_CAN_ERROR_FORM 2 +#define IXXAT_USB_CAN_ERROR_ACK 3 +#define IXXAT_USB_CAN_ERROR_BIT 4 +#define IXXAT_USB_CAN_ERROR_CRC 6 + +#define IXXAT_USB_MSG_FLAGS_TYPE 0x000000FF +#define IXXAT_USB_MSG_FLAGS_DLC 0x000F0000 +#define IXXAT_USB_MSG_FLAGS_OVR 0x00100000 +#define IXXAT_USB_MSG_FLAGS_RTR 0x00400000 +#define IXXAT_USB_MSG_FLAGS_EXT 0x00800000 + +#define IXXAT_USB_FDMSG_FLAGS_EDL 0x00000400 +#define IXXAT_USB_FDMSG_FLAGS_FDR 0x00000800 +#define IXXAT_USB_FDMSG_FLAGS_ESI 0x00001000 + +#define IXXAT_USB_CAN_CMD_START 0x326 +#define IXXAT_USB_CAN_CMD_STOP 0x327 +#define IXXAT_USB_CAN_CMD_RESET 0x328 + +#define IXXAT_USB_BRD_CMD_GET_DEVCAPS 0x401 +#define IXXAT_USB_BRD_CMD_GET_DEVINFO 0x402 +#define IXXAT_USB_BRD_CMD_POWER 0x421 + +/** + * struct ixxat_can_msg_base - IXXAT CAN message base (CL1/CL2) + * @size: Message size (this field excluded) + * @time: Message timestamp + * @msg_id: Message ID + * @flags: Message flags + * + * Contains the common fields of an IXXAT CAN message on both CL1 and CL2 + * devices + */ +struct ixxat_can_msg_base { + u8 size; + __le32 time; + __le32 msg_id; + __le32 flags; +} __packed; + +/** + * struct ixxat_can_msg_cl1 - IXXAT CAN message (CL1) + * @data: Message data (standard CAN frame) + * + * Contains the fields of an IXXAT CAN message on CL1 devices + */ +struct ixxat_can_msg_cl1 { + union { + u8 data[CAN_MAX_DLEN]; + __le32 status; + } __packed; +} __packed; + +/** + * struct ixxat_can_msg_cl2 - IXXAT CAN message (CL2) + * @client_id: Client ID + * @data: Message data (CAN FD frame) + * + * Contains the fields of an IXXAT CAN message on CL2 devices + */ +struct ixxat_can_msg_cl2 { + __le32 client_id; + union { + u8 data[CANFD_MAX_DLEN]; + __le32 status; + } __packed; +} __packed; + +/** + * struct ixxat_can_msg - IXXAT CAN message + * @base: Base message + * @cl1: Cl1 message + * @cl2: Cl2 message + * + * Contains an IXXAT CAN message + */ +struct ixxat_can_msg { + struct ixxat_can_msg_base base; + union { + struct ixxat_can_msg_cl1 cl1; + struct ixxat_can_msg_cl2 cl2; + }; +} __packed; + +/** + * struct ixxat_dev_caps - Device capabilities + * @bus_ctrl_count: Stores the bus controller counter + * @bus_ctrl_types: Stores the bus controller types + * + * Contains the device capabilities + */ +struct ixxat_dev_caps { + __le16 bus_ctrl_count; + __le16 bus_ctrl_types[IXXAT_USB_MAX_TYPES]; +} __packed; + +/** + * struct ixxat_canbtp Bittiming parameters (CL2) + * @mode: Operation mode + * @bps: Bits per second + * @ts1: First time segment + * @ts2: Second time segment + * @sjw: Synchronization jump width + * @tdo: Transmitter delay offset + * + * Bittiming parameters of a CL2 initialization request + */ +struct ixxat_canbtp { + __le32 mode; + __le32 bps; + __le16 ts1; + __le16 ts2; + __le16 sjw; + __le16 tdo; +} __packed; + +/** + * struct ixxat_dev_info IXXAT usb device information + * @device_name: Name of the device + * @device_id: Device identification ( unique device id) + * @device_version: Device version ( 0, 1, ...) + * @device_fpga_version: Version of FPGA design + * + * Contains device information of IXXAT USB devices + */ +struct ixxat_dev_info { + char device_name[16]; + char device_id[16]; + __le16 device_version; + __le32 device_fpga_version; +} __packed; + +/** + * struct ixxat_time_ref Time reference + * @kt_host_0: Latest time on the host + * @ts_dev_0: Latest time stamp on the device + * @ts_dev_last: Last device time stamp + * + * Contains time references of the device and the host + */ +struct ixxat_time_ref { + ktime_t kt_host_0; + u32 ts_dev_0; + u32 ts_dev_last; +}; + +/** + * struct ixxat_tx_urb_context URB content for transmission + * @dev: IXXAT USB device + * @urb: USB request block + * @echo_index: Echo index + * @dlc: Data length code + * @count: Counter + * + * Contains content for USB request block transmissions + */ +struct ixxat_tx_urb_context { + struct ixxat_usb_device *dev; + struct urb *urb; + u32 echo_index; +}; + +/** + * struct ixxat_usb_device IXXAT USB device + * @can: CAN common private data + * @adapter: USB network descriptor + * @udev: USB device + * @netdev: Net_device + * @active_tx_urbs: Active tx urbs + * @tx_submitted: Submitted tx usb anchor + * @tx_contexts: Buffer for tx contexts + * @rx_submitted: Submitted rx usb anchor + * @state: Device state + * @ctrl_index: Controller index + * @ep_msg_in: USB endpoint for incoming messages + * @ep_msg_out: USB endpoint for outgoing messages + * @prev_dev: Previous opened device + * @next_dev: Next opened device in list + * @time_ref: Time reference + * @dev_info: Device information + * @bec: CAN error counter + * + * IXXAT USB-to-CAN device + */ +struct ixxat_usb_device { + struct can_priv can; + const struct ixxat_usb_adapter *adapter; + struct usb_device *udev; + struct net_device *netdev; + + atomic_t active_tx_urbs; + struct usb_anchor tx_submitted; + struct ixxat_tx_urb_context tx_contexts[IXXAT_USB_MAX_TX_URBS]; + struct usb_anchor rx_submitted; + + u32 state; + u16 ctrl_index; + + u8 ep_msg_in; + u8 ep_msg_out; + + struct ixxat_usb_device *prev_dev; + struct ixxat_usb_device *next_dev; + + struct ixxat_time_ref time_ref; + struct ixxat_dev_info dev_info; + + struct can_berr_counter bec; +}; + +/** + * struct ixxat_usb_dal_req IXXAT device request block + * @size: Request size + * @port: Request port + * @socket: Request socket + * @code: Request code + * + * IXXAT device request block + */ +struct ixxat_usb_dal_req { + __le32 size; + __le16 port; + __le16 socket; + __le32 code; +} __packed; + +/** + * struct ixxat_usb_dal_res IXXAT device response block + * @res_size: Expected response size + * @ret_size: Actual response size + * @code: Return code + * + * IXXAT device response block + */ +struct ixxat_usb_dal_res { + __le32 res_size; + __le32 ret_size; + __le32 code; +} __packed; + +/** + * struct ixxat_usb_dal_cmd IXXAT device command + * @req: Request block + * @req: Response block + * + * IXXAT device command + */ +struct ixxat_usb_dal_cmd { + struct ixxat_usb_dal_req req; + struct ixxat_usb_dal_res res; +} __packed; + +/** + * struct ixxat_usb_caps_cmd Device capabilities command + * @req: Request block + * @res: Response block + * @caps: Device capabilities + * + * Can be sent to a device to request its capabilities + */ +struct ixxat_usb_caps_cmd { + struct ixxat_usb_dal_req req; + struct ixxat_usb_dal_res res; + struct ixxat_dev_caps caps; +} __packed; + +/** + * struct ixxat_usb_init_cl1_cmd Initialization command (CL1) + * @req: Request block + * @mode: Operation mode + * @btr0: Bittiming register 0 + * @btr1: Bittiming register 1 + * @padding: 1 byte padding + * @res: Response block + * + * Can be sent to a CL1 device to initialize it + */ +struct ixxat_usb_init_cl1_cmd { + struct ixxat_usb_dal_req req; + u8 mode; + u8 btr0; + u8 btr1; + u8 padding; + struct ixxat_usb_dal_res res; +} __packed; + +/** + * struct ixxat_usb_init_cl2_cmd Initialization command (CL2) + * @req: Request block + * @opmode: Operation mode + * @exmode: Extended mode + * @sdr: Stadard bittiming parameters + * @fdr: Fast data bittiming parameters + * @_padding: 2 bytes padding + * @res: Response block + * + * Can be sent to a CL2 device to initialize it + */ +struct ixxat_usb_init_cl2_cmd { + struct ixxat_usb_dal_req req; + u8 opmode; + u8 exmode; + struct ixxat_canbtp sdr; + struct ixxat_canbtp fdr; + __le16 _padding; + struct ixxat_usb_dal_res res; +} __packed; + +/** + * struct ixxat_usb_start_cmd Controller start command + * @req: Request block + * @res: Response block + * @time: Timestamp + * + * Can be sent to a device to start its controller + */ +struct ixxat_usb_start_cmd { + struct ixxat_usb_dal_req req; + struct ixxat_usb_dal_res res; + __le32 time; +} __packed; + +/** + * struct ixxat_usb_stop_cmd Controller stop command + * @req: Request block + * @action: Stop action + * @res: Response block + * + * Can be sent to a device to start its controller + */ +struct ixxat_usb_stop_cmd { + struct ixxat_usb_dal_req req; + __le32 action; + struct ixxat_usb_dal_res res; +} __packed; + +/** + * struct ixxat_usb_power_cmd Power command + * @req: Request block + * @mode: Power mode + * @_padding1: 1 byte padding + * @_padding2: 2 byte padding + * @res: Response block + * + * Can be sent to a device to set its power mode + */ +struct ixxat_usb_power_cmd { + struct ixxat_usb_dal_req req; + u8 mode; + u8 _padding1; + __le16 _padding2; + struct ixxat_usb_dal_res res; +} __packed; + +/** + * struct ixxat_usb_info_cmd Device information command + * @req: Request block + * @res: Response block + * @info: Device information + * + * Can be sent to a device to request its device information + */ +struct ixxat_usb_info_cmd { + struct ixxat_usb_dal_req req; + struct ixxat_usb_dal_res res; + struct ixxat_dev_info info; +} __packed; + +/** + * struct ixxat_usb_adapter IXXAT USB device adapter + * @clock: Clock frequency + * @bt: Bittiming constants + * @btd: Data bittiming constants + * @modes: Supported modes + * @buffer_size_rx: Buffer size for receiving + * @buffer_size_tx: Buffer size for transfer + * @ep_msg_in: USB endpoint buffer for incoming messages + * @ep_msg_out: USB endpoint buffer for outgoing messages + * @ep_offs: Endpoint offset (device depended) + * + * Device Adapter for IXXAT USB devices + */ +struct ixxat_usb_adapter { + const u32 clock; + const struct can_bittiming_const *bt; + const struct can_bittiming_const *btd; + const u32 modes; + const u16 buffer_size_rx; + const u16 buffer_size_tx; + const u8 ep_msg_in[IXXAT_USB_MAX_CHANNEL]; + const u8 ep_msg_out[IXXAT_USB_MAX_CHANNEL]; + const u8 ep_offs; + int (*init_ctrl)(struct ixxat_usb_device *dev); +}; + +extern const struct ixxat_usb_adapter usb2can_cl1; +extern const struct ixxat_usb_adapter usb2can_cl2; +extern const struct ixxat_usb_adapter can_idm; + +/** + * ixxat_usb_setup_cmd() - Setup a device command + * @req: Request block + * @res: Response block + * + * This function sets the default values in the request and the response b= lock + * of a device command + */ +void ixxat_usb_setup_cmd(struct ixxat_usb_dal_req *req, + struct ixxat_usb_dal_res *res); + +/** + * ixxat_usb_send_cmd() - Send a command to the device + * @dev: USB device + * @port: Command port + * @req: Command request buffer + * @req_size: Command request size + * @res: Command response buffer + * @res_size: Command response size + * + * This function sends a specific command to the device + * + * Return: Negative error code or zero on success + */ +int ixxat_usb_send_cmd(struct usb_device *dev, const u16 port, void *req, + const u16 req_size, void *res, const u16 res_size); + +#endif /* IXXAT_USB_CORE_H */ --=20 2.40.1