Hello Florian, Am 28.06.2018 um 15:55 schrieb Florian Ferg: > From: Florian Ferg <flfe@xxxxxxxxxxxxxxx> > > 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 <flfe@xxxxxxxxxxxxxxx> > --- > [v3] > Moved can message handling to ixxat_usb_core.c > > [v2] > Fixed some style issues. > Using ktime API for timestamps. > Reworked the driver to be only one module with adapters for cl1 / cl2. > CAN-IDM100 device will not be released. The driver handles CANIDM-101 now. > Added error passive recognition. (Even though current devices don't > signal it. Future devices probably will.) > > 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 | 154 +++ > drivers/net/can/usb/ixxat_usb/ixxat_usb_cl2.c | 284 +++++ > drivers/net/can/usb/ixxat_usb/ixxat_usb_core.c | 1362 ++++++++++++++++++++++++ > drivers/net/can/usb/ixxat_usb/ixxat_usb_core.h | 487 +++++++++ > 7 files changed, 2307 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 c36f4bd..ca21974 100644 > --- a/drivers/net/can/usb/Kconfig > +++ b/drivers/net/can/usb/Kconfig > @@ -89,4 +89,21 @@ config CAN_MCBA_USB > This driver supports the CAN BUS Analyzer interface > from Microchip (http://www.microchip.com/development-tools/). > > +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 > + > endmenu > diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile > index 49ac7b9..9d63b8d 100644 > --- a/drivers/net/can/usb/Makefile > +++ b/drivers/net/can/usb/Makefile > @@ -10,3 +10,4 @@ obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o > obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/ > obj-$(CONFIG_CAN_8DEV_USB) += usb_8dev.o > obj-$(CONFIG_CAN_MCBA_USB) += mcba_usb.o > +obj-$(CONFIG_CAN_IXXAT_USB) += ixxat_usb/ > diff --git a/drivers/net/can/usb/ixxat_usb/Makefile b/drivers/net/can/usb/ixxat_usb/Makefile > new file mode 100644 > index 0000000..125d270 > --- /dev/null > +++ b/drivers/net/can/usb/ixxat_usb/Makefile > @@ -0,0 +1,2 @@ > +obj-$(CONFIG_CAN_IXXAT_USB) += ixxat_usb2can.o > +ixxat_usb2can-y = 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/can/usb/ixxat_usb/ixxat_usb_cl1.c > new file mode 100644 > index 0000000..523d2d9 > --- /dev/null > +++ b/drivers/net/can/usb/ixxat_usb/ixxat_usb_cl1.c > @@ -0,0 +1,154 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +/* CAN driver adapter for IXXAT USB-to-CAN CL1 > + * > + * Copyright (C) 2018 HMS Industrial Networks <socketcan@xxxxxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU General Public License as published > + * by the Free Software Foundation; version 2 of the License. > + * > + * This program is distributed in the hope that it will be useful, but > + * WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * General Public License for more details. > + */ > + > +#include <linux/can/dev.h> > +#include <linux/usb.h> > + > +#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_MODES (CAN_CTRLMODE_3_SAMPLES | \ > + CAN_CTRLMODE_BERR_REPORTING | \ > + CAN_CTRLMODE_LISTENONLY) \ > + > +#define IXXAT_USB_BTMODE_TSM_CL1 0x80 > + > +/* bittiming parameters */ > +#define IXXAT_USB2CAN_NAME "ixxat_usb" > + > +#define IXXAT_USB2CAN_TSEG1_MIN 1 > +#define IXXAT_USB2CAN_TSEG1_MAX 16 > +#define IXXAT_USB2CAN_TSEG2_MIN 1 > +#define IXXAT_USB2CAN_TSEG2_MAX 8 > +#define IXXAT_USB2CAN_SJW_MAX 4 > +#define IXXAT_USB2CAN_BRP_MIN 1 > +#define IXXAT_USB2CAN_BRP_MAX 64 > +#define IXXAT_USB2CAN_BRP_INC 1 > + > +/* USB endpoint mapping for CL1 */ > +#define IXXAT_USB2CAN_EP1_IN (1 | USB_DIR_IN) > +#define IXXAT_USB2CAN_EP2_IN (2 | USB_DIR_IN) > +#define IXXAT_USB2CAN_EP3_IN (3 | USB_DIR_IN) > +#define IXXAT_USB2CAN_EP4_IN (4 | USB_DIR_IN) > +#define IXXAT_USB2CAN_EP5_IN (5 | USB_DIR_IN) > + > +#define IXXAT_USB2CAN_EP1_OUT (1 | USB_DIR_OUT) > +#define IXXAT_USB2CAN_EP2_OUT (2 | USB_DIR_OUT) > +#define IXXAT_USB2CAN_EP3_OUT (3 | USB_DIR_OUT) > +#define IXXAT_USB2CAN_EP4_OUT (4 | USB_DIR_OUT) > +#define IXXAT_USB2CAN_EP5_OUT (5 | USB_DIR_OUT) > + > +#define IXXAT_USB_CAN_CMD_INIT 0x325 > + > +struct ixxat_usb_ctrl_init_cl1_req { > + struct ixxat_usb_dal_req dal_req; > + u8 mode; > + u8 btr0; > + u8 btr1; > + u8 padding; > +} __packed; > + > +static const struct can_bittiming_const usb2can_bt = { > + .name = IXXAT_USB2CAN_NAME, > + .tseg1_min = IXXAT_USB2CAN_TSEG1_MIN, > + .tseg1_max = IXXAT_USB2CAN_TSEG1_MAX, > + .tseg2_min = IXXAT_USB2CAN_TSEG2_MIN, > + .tseg2_max = IXXAT_USB2CAN_TSEG2_MAX, > + .sjw_max = IXXAT_USB2CAN_SJW_MAX, > + .brp_min = IXXAT_USB2CAN_BRP_MIN, > + .brp_max = IXXAT_USB2CAN_BRP_MAX, > + .brp_inc = IXXAT_USB2CAN_BRP_INC, > +}; > + > +static int ixxat_usb_init_ctrl(struct ixxat_usb_device *dev) > +{ > + int ret = -ENODEV; > + u8 data[IXXAT_USB_CMD_BUFFER_SIZE] = { 0 }; > + struct ixxat_usb_ctrl_init_cl1_req *req; > + struct ixxat_usb_ctrl_init_res *res; > + u32 req_size = sizeof(*req); > + u32 res_size = sizeof(*res); > + u8 opmode = IXXAT_USB_OPMODE_EXTENDED | IXXAT_USB_OPMODE_STANDARD; > + const struct can_bittiming *bt = &dev->can.bittiming; > + > + /* Bittiming calculation from kernel > + * (see can-utils/can-calc-bit-timing.c) > + */ > + u8 btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6); > + u8 btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) | > + (((bt->phase_seg2 - 1) & 0x7) << 4); > + > + req = (struct ixxat_usb_ctrl_init_cl1_req *)data; > + res = (struct ixxat_usb_ctrl_init_res *)(data + req_size); > + > + if (dev->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) > + btr1 |= IXXAT_USB_BTMODE_TSM_CL1; > + if (dev->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) > + opmode |= IXXAT_USB_OPMODE_ERRFRAME; > + if (dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) > + opmode |= IXXAT_USB_OPMODE_LISTONLY; > + > + req->dal_req.req_size = cpu_to_le32(req_size); > + req->dal_req.req_code = cpu_to_le32(IXXAT_USB_CAN_CMD_INIT); > + req->dal_req.req_port = cpu_to_le16(dev->ctrl_index); > + req->dal_req.req_socket = 0xffff; > + req->mode = opmode; > + req->btr0 = btr0; > + req->btr1 = btr1; > + > + res->dal_res.res_size = cpu_to_le32(res_size); > + res->dal_res.ret_size = 0; > + res->dal_res.ret_code = 0xffffffff; > + > + ret = ixxat_usb_send_cmd(dev->udev, &req->dal_req); > + if (ret < 0) > + return ret; You changed error handling to "if (ret < 0)" for *all* functions. Please use "ret < 0" only, if a positive return code means success, otherwise "if (err)". > + ret = ixxat_usb_rcv_cmd(dev->udev, &res->dal_res, dev->ctrl_index); > + if (ret < 0) > + return ret; > + > + return le32_to_cpu(res->dal_res.ret_code); > +} > + > +const struct ixxat_usb_adapter usb2can_cl1 = { > + .clock = IXXAT_USB_CLOCK, > + .bt = &usb2can_bt, > + .btd = NULL, > + .modes = IXXAT_USB_MODES, > + .buffer_size_rx = IXXAT_USB_BUFFER_SIZE_RX, > + .buffer_size_tx = IXXAT_USB_BUFFER_SIZE_TX, > + .ep_msg_in = { > + IXXAT_USB2CAN_EP1_IN, > + IXXAT_USB2CAN_EP2_IN, > + IXXAT_USB2CAN_EP3_IN, > + IXXAT_USB2CAN_EP4_IN, > + IXXAT_USB2CAN_EP5_IN > + }, > + .ep_msg_out = { > + IXXAT_USB2CAN_EP1_OUT, > + IXXAT_USB2CAN_EP2_OUT, > + IXXAT_USB2CAN_EP3_OUT, > + IXXAT_USB2CAN_EP4_OUT, > + IXXAT_USB2CAN_EP5_OUT > + }, > + .ep_offs = 0, > + .init_ctrl = ixxat_usb_init_ctrl > +}; > diff --git a/drivers/net/can/usb/ixxat_usb/ixxat_usb_cl2.c b/drivers/net/can/usb/ixxat_usb/ixxat_usb_cl2.c > new file mode 100644 > index 0000000..67fc37a > --- /dev/null > +++ b/drivers/net/can/usb/ixxat_usb/ixxat_usb_cl2.c > @@ -0,0 +1,284 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +/* CAN driver adapter for IXXAT USB-to-CAN CL2 > + * > + * Copyright (C) 2018 HMS Industrial Networks <socketcan@xxxxxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU General Public License as published > + * by the Free Software Foundation; version 2 of the License. > + * > + * This program is distributed in the hope that it will be useful, but > + * WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * General Public License for more details. > + */ > + > +#include <linux/can/dev.h> > +#include <linux/usb.h> > + > +#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_MODES (CAN_CTRLMODE_3_SAMPLES | \ > + CAN_CTRLMODE_LISTENONLY | \ > + CAN_CTRLMODE_BERR_REPORTING | \ > + CAN_CTRLMODE_FD | \ > + CAN_CTRLMODE_FD_NON_ISO) > + > +/* bittiming parameters CL2 */ > +#define IXXAT_USB2CAN_NAME "ifi_can" > + > +#define IXXAT_USB2CAN_TSEG1_MIN 1 > +#define IXXAT_USB2CAN_TSEG1_MAX 256 > +#define IXXAT_USB2CAN_TSEG2_MIN 1 > +#define IXXAT_USB2CAN_TSEG2_MAX 256 > +#define IXXAT_USB2CAN_SJW_MAX 128 > +#define IXXAT_USB2CAN_BRP_MIN 2 > +#define IXXAT_USB2CAN_BRP_MAX 513 > +#define IXXAT_USB2CAN_BRP_INC 1 > + > +#define IXXAT_USB2CAN_TSEG1_MIN_DATA 1 > +#define IXXAT_USB2CAN_TSEG1_MAX_DATA 256 > +#define IXXAT_USB2CAN_TSEG2_MIN_DATA 1 > +#define IXXAT_USB2CAN_TSEG2_MAX_DATA 256 > +#define IXXAT_USB2CAN_SJW_MAX_DATA 128 > +#define IXXAT_USB2CAN_BRP_MIN_DATA 2 > +#define IXXAT_USB2CAN_BRP_MAX_DATA 513 > +#define IXXAT_USB2CAN_BRP_INC_DATA 1 > + > +/* bittiming parameters CAN IDM */ > +#define IXXAT_CANIDM_NAME "mcan" > + > +#define IXXAT_CANIDM_TSEG1_MIN 1 > +#define IXXAT_CANIDM_TSEG1_MAX 256 > +#define IXXAT_CANIDM_TSEG2_MIN 1 > +#define IXXAT_CANIDM_TSEG2_MAX 128 > +#define IXXAT_CANIDM_SJW_MAX 128 > +#define IXXAT_CANIDM_BRP_MIN 1 > +#define IXXAT_CANIDM_BRP_MAX 512 > +#define IXXAT_CANIDM_BRP_INC 1 > + > +#define IXXAT_CANIDM_TSEG1_MIN_DATA 1 > +#define IXXAT_CANIDM_TSEG1_MAX_DATA 32 > +#define IXXAT_CANIDM_TSEG2_MIN_DATA 1 > +#define IXXAT_CANIDM_TSEG2_MAX_DATA 16 > +#define IXXAT_CANIDM_SJW_MAX_DATA 8 > +#define IXXAT_CANIDM_BRP_MIN_DATA 1 > +#define IXXAT_CANIDM_BRP_MAX_DATA 32 > +#define IXXAT_CANIDM_BRP_INC_DATA 1 > + > +/* USB endpoint mapping for CL2 */ > +#define IXXAT_USB2CAN_EP1_IN (1 | USB_DIR_IN) > +#define IXXAT_USB2CAN_EP2_IN (2 | USB_DIR_IN) > +#define IXXAT_USB2CAN_EP3_IN (3 | USB_DIR_IN) > +#define IXXAT_USB2CAN_EP4_IN (4 | USB_DIR_IN) > +#define IXXAT_USB2CAN_EP5_IN (5 | USB_DIR_IN) > + > +#define IXXAT_USB2CAN_EP1_OUT (1 | USB_DIR_OUT) > +#define IXXAT_USB2CAN_EP2_OUT (2 | USB_DIR_OUT) > +#define IXXAT_USB2CAN_EP3_OUT (3 | USB_DIR_OUT) > +#define IXXAT_USB2CAN_EP4_OUT (4 | USB_DIR_OUT) > +#define IXXAT_USB2CAN_EP5_OUT (5 | USB_DIR_OUT) > + > +/* USB endpoint mapping for CAN IDM */ > +#define IXXAT_CANIDM_EP1_IN (2 | USB_DIR_IN) > +#define IXXAT_CANIDM_EP2_IN (4 | USB_DIR_IN) > +#define IXXAT_CANIDM_EP3_IN (6 | USB_DIR_IN) > +#define IXXAT_CANIDM_EP4_IN (8 | USB_DIR_IN) > +#define IXXAT_CANIDM_EP5_IN (10 | USB_DIR_IN) > + > +#define IXXAT_CANIDM_EP1_OUT (1 | USB_DIR_OUT) > +#define IXXAT_CANIDM_EP2_OUT (3 | USB_DIR_OUT) > +#define IXXAT_CANIDM_EP3_OUT (5 | USB_DIR_OUT) > +#define IXXAT_CANIDM_EP4_OUT (7 | USB_DIR_OUT) > +#define IXXAT_CANIDM_EP5_OUT (9 | USB_DIR_OUT) > + > +#define IXXAT_USB_CAN_CMD_INIT 0x337 > + > +struct ixxat_canbtp { > + u32 mode; > + u32 bps; > + u16 ts1; > + u16 ts2; > + u16 sjw; > + u16 tdo; > +} __packed; Could you here and below please use __le32 and friends. > + > +struct ixxat_usb_ctrl_init_cl2_req { > + struct ixxat_usb_dal_req dal_req; > + u8 opmode; > + u8 exmode; > + struct ixxat_canbtp sdr; > + struct ixxat_canbtp fdr; > + u16 _padding; > +} __packed; > + > +static const struct can_bittiming_const usb2can_bt = { > + .name = IXXAT_USB2CAN_NAME, > + .tseg1_min = IXXAT_USB2CAN_TSEG1_MIN, > + .tseg1_max = IXXAT_USB2CAN_TSEG1_MAX, > + .tseg2_min = IXXAT_USB2CAN_TSEG2_MIN, > + .tseg2_max = IXXAT_USB2CAN_TSEG2_MAX, > + .sjw_max = IXXAT_USB2CAN_SJW_MAX, > + .brp_min = IXXAT_USB2CAN_BRP_MIN, > + .brp_max = IXXAT_USB2CAN_BRP_MAX, > + .brp_inc = IXXAT_USB2CAN_BRP_INC, > +}; > + > +static const struct can_bittiming_const usb2can_btd = { > + .name = IXXAT_USB2CAN_NAME, > + .tseg1_min = IXXAT_USB2CAN_TSEG1_MIN_DATA, > + .tseg1_max = IXXAT_USB2CAN_TSEG1_MAX_DATA, > + .tseg2_min = IXXAT_USB2CAN_TSEG2_MIN_DATA, > + .tseg2_max = IXXAT_USB2CAN_TSEG2_MAX_DATA, > + .sjw_max = IXXAT_USB2CAN_SJW_MAX_DATA, > + .brp_min = IXXAT_USB2CAN_BRP_MIN_DATA, > + .brp_max = IXXAT_USB2CAN_BRP_MAX_DATA, > + .brp_inc = IXXAT_USB2CAN_BRP_INC_DATA, > +}; > + > +static const struct can_bittiming_const canidm_bt = { > + .name = IXXAT_CANIDM_NAME, > + .tseg1_min = IXXAT_CANIDM_TSEG1_MIN, > + .tseg1_max = IXXAT_CANIDM_TSEG1_MAX, > + .tseg2_min = IXXAT_CANIDM_TSEG2_MIN, > + .tseg2_max = IXXAT_CANIDM_TSEG2_MAX, > + .sjw_max = IXXAT_CANIDM_SJW_MAX, > + .brp_min = IXXAT_CANIDM_BRP_MIN, > + .brp_max = IXXAT_CANIDM_BRP_MAX, > + .brp_inc = IXXAT_CANIDM_BRP_INC > +}; > + > +static const struct can_bittiming_const canidm_btd = { > + .name = IXXAT_CANIDM_NAME, > + .tseg1_min = IXXAT_CANIDM_TSEG1_MIN_DATA, > + .tseg1_max = IXXAT_CANIDM_TSEG1_MAX_DATA, > + .tseg2_min = IXXAT_CANIDM_TSEG2_MIN_DATA, > + .tseg2_max = IXXAT_CANIDM_TSEG2_MAX_DATA, > + .sjw_max = IXXAT_CANIDM_SJW_MAX_DATA, > + .brp_min = IXXAT_CANIDM_BRP_MIN_DATA, > + .brp_max = IXXAT_CANIDM_BRP_MAX_DATA, > + .brp_inc = IXXAT_CANIDM_BRP_INC_DATA > +}; > + > +static int ixxat_usb_init_ctrl(struct ixxat_usb_device *dev) > +{ > + int ret = -ENODEV; Preinitialisation not used! > + u8 data[IXXAT_USB_CMD_BUFFER_SIZE] = { 0 }; > + struct ixxat_usb_ctrl_init_cl2_req *req; > + struct ixxat_usb_ctrl_init_res *res; > + u32 req_size = sizeof(*req); > + u32 res_size = sizeof(*res); > + u8 opmode = IXXAT_USB_OPMODE_EXTENDED | IXXAT_USB_OPMODE_STANDARD; > + u8 exmode = 0; > + u32 btmode = IXXAT_USB_BTMODE_NAT; > + const struct can_bittiming *bt = &dev->can.bittiming; > + const struct can_bittiming *btd = &dev->can.data_bittiming; > + > + req = (struct ixxat_usb_ctrl_init_cl2_req *)data; > + res = (struct ixxat_usb_ctrl_init_res *)(data + req_size); Why not using a struct containing both, the req and res struct. More below... > + > + if (dev->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) > + btmode = IXXAT_USB_BTMODE_TSM; > + if (dev->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) > + opmode |= IXXAT_USB_OPMODE_ERRFRAME; > + if (dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) > + opmode |= IXXAT_USB_OPMODE_LISTONLY; > + if ((CAN_CTRLMODE_FD | CAN_CTRLMODE_FD_NON_ISO) & dev->can.ctrlmode) > + exmode |= IXXAT_USB_EXMODE_EXTDATA | IXXAT_USB_EXMODE_FASTDATA; > + if (!(CAN_CTRLMODE_FD_NON_ISO & dev->can.ctrlmode) && exmode) > + exmode |= IXXAT_USB_EXMODE_ISOFD; > + > + req->dal_req.req_size = cpu_to_le32(req_size); > + req->dal_req.req_code = cpu_to_le32(IXXAT_USB_CAN_CMD_INIT); > + req->dal_req.req_port = cpu_to_le16(dev->ctrl_index); > + req->dal_req.req_socket = 0xffff; > + req->opmode = opmode; > + req->exmode = exmode; > + > + req->sdr.mode = cpu_to_le32(btmode); > + req->sdr.bps = cpu_to_le32(bt->brp); > + req->sdr.ts1 = cpu_to_le16(bt->prop_seg + bt->phase_seg1); > + req->sdr.ts2 = cpu_to_le16(bt->phase_seg2); > + req->sdr.sjw = cpu_to_le16(bt->sjw); > + req->sdr.tdo = 0; > + > + if (exmode) { > + req->fdr.mode = cpu_to_le32(btmode); > + req->fdr.bps = cpu_to_le32(btd->brp); > + req->fdr.ts1 = cpu_to_le16(btd->prop_seg + btd->phase_seg1); > + req->fdr.ts2 = cpu_to_le16(btd->phase_seg2); > + req->fdr.sjw = cpu_to_le16(btd->sjw); > + req->fdr.tdo = cpu_to_le16(btd->brp * (btd->phase_seg1 + 1 + > + btd->prop_seg)); > + } > + > + res->dal_res.res_size = cpu_to_le32(res_size); > + res->dal_res.ret_size = 0; > + res->dal_res.ret_code = 0xffffffff; > + > + ret = ixxat_usb_send_cmd(dev->udev, &req->dal_req); > + if (ret < 0) > + return ret; > + > + ret = ixxat_usb_rcv_cmd(dev->udev, &res->dal_res, dev->ctrl_index); > + if (ret < 0) > + return ret; > + > + return le32_to_cpu(res->dal_res.ret_code); > +} > + > +const struct ixxat_usb_adapter usb2can_cl2 = { > + .clock = IXXAT_USB_CLOCK, > + .bt = &usb2can_bt, > + .btd = &usb2can_btd, > + .modes = IXXAT_USB_MODES, > + .buffer_size_rx = IXXAT_USB_BUFFER_SIZE_RX, > + .buffer_size_tx = IXXAT_USB_BUFFER_SIZE_TX, > + .ep_msg_in = { > + IXXAT_USB2CAN_EP1_IN, > + IXXAT_USB2CAN_EP2_IN, > + IXXAT_USB2CAN_EP3_IN, > + IXXAT_USB2CAN_EP4_IN, > + IXXAT_USB2CAN_EP5_IN > + }, > + .ep_msg_out = { > + IXXAT_USB2CAN_EP1_OUT, > + IXXAT_USB2CAN_EP2_OUT, > + IXXAT_USB2CAN_EP3_OUT, > + IXXAT_USB2CAN_EP4_OUT, > + IXXAT_USB2CAN_EP5_OUT > + }, > + .ep_offs = 1, > + .init_ctrl = ixxat_usb_init_ctrl > +}; > + > +const struct ixxat_usb_adapter can_idm = { > + .clock = IXXAT_USB_CLOCK, > + .bt = &canidm_bt, > + .btd = &canidm_btd, > + .modes = IXXAT_USB_MODES, > + .buffer_size_rx = IXXAT_USB_BUFFER_SIZE_RX, > + .buffer_size_tx = IXXAT_USB_BUFFER_SIZE_TX, > + .ep_msg_in = { > + IXXAT_CANIDM_EP1_IN, > + IXXAT_CANIDM_EP2_IN, > + IXXAT_CANIDM_EP3_IN, > + IXXAT_CANIDM_EP4_IN, > + IXXAT_CANIDM_EP5_IN > + }, > + .ep_msg_out = { > + IXXAT_CANIDM_EP1_OUT, > + IXXAT_CANIDM_EP2_OUT, > + IXXAT_CANIDM_EP3_OUT, > + IXXAT_CANIDM_EP4_OUT, > + IXXAT_CANIDM_EP5_OUT > + }, > + .ep_offs = 0, > + .init_ctrl = ixxat_usb_init_ctrl > +}; > diff --git a/drivers/net/can/usb/ixxat_usb/ixxat_usb_core.c b/drivers/net/can/usb/ixxat_usb/ixxat_usb_core.c > new file mode 100644 > index 0000000..bbfc15c > --- /dev/null > +++ b/drivers/net/can/usb/ixxat_usb/ixxat_usb_core.c > @@ -0,0 +1,1362 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +/* CAN driver for IXXAT USB-to-CAN > + * > + * Copyright (C) 2018 HMS Industrial Networks <socketcan@xxxxxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU General Public License as published > + * by the Free Software Foundation; version 2 of the License. > + * > + * This program is distributed in the hope that it will be useful, but > + * WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * General Public License for more details. > + */ > + > +#include <linux/can.h> > +#include <linux/can/dev.h> > +#include <linux/can/error.h> > +#include <linux/can/netlink.h> > +#include <linux/kthread.h> > +#include <linux/module.h> > +#include <linux/netdevice.h> > +#include <linux/usb.h> > + > +#include "ixxat_usb_core.h" > + > +MODULE_AUTHOR("Marcel Schmidt <socketcan@xxxxxxxxxxxxxxx>"); > +MODULE_DESCRIPTION("CAN driver for IXXAT USB-to-CAN FD adapters"); > +MODULE_LICENSE("GPL v2"); > + > +/* Table of devices that work with this driver */ > +static const struct usb_device_id ixxat_usb_table[] = { > + { USB_DEVICE(IXXAT_USB_VENDOR_ID, USB2CAN_COMPACT_PRODUCT_ID) }, > + { USB_DEVICE(IXXAT_USB_VENDOR_ID, USB2CAN_EMBEDDED_PRODUCT_ID) }, > + { USB_DEVICE(IXXAT_USB_VENDOR_ID, USB2CAN_PROFESSIONAL_PRODUCT_ID) }, > + { USB_DEVICE(IXXAT_USB_VENDOR_ID, USB2CAN_AUTOMOTIVE_PRODUCT_ID) }, > + { USB_DEVICE(IXXAT_USB_VENDOR_ID, USB2CAN_FD_COMPACT_PRODUCT_ID) }, > + { USB_DEVICE(IXXAT_USB_VENDOR_ID, USB2CAN_FD_PROFESSIONAL_PRODUCT_ID) }, > + { USB_DEVICE(IXXAT_USB_VENDOR_ID, USB2CAN_FD_AUTOMOTIVE_PRODUCT_ID) }, > + { USB_DEVICE(IXXAT_USB_VENDOR_ID, USB2CAN_FD_PCIE_MINI_PRODUCT_ID) }, > + { USB_DEVICE(IXXAT_USB_VENDOR_ID, USB2CAR_PRODUCT_ID) }, > + { USB_DEVICE(IXXAT_USB_VENDOR_ID, CAN_IDM101_PRODUCT_ID) }, > + { } /* Terminating entry */ > +}; > + > +MODULE_DEVICE_TABLE(usb, ixxat_usb_table); > + > +void ixxat_usb_update_ts_now(struct ixxat_usb_device *dev, u32 ts_now) > +{ > + u64 timebase; > + u32 *ts_dev = &dev->time_ref.ts_dev_0; > + ktime_t *kt_host = &dev->time_ref.kt_host_0; > + > + timebase = (u64)0x00000000FFFFFFFF - (u64)(*ts_dev) + (u64)ts_now; > + > + *kt_host = ktime_add_us(*kt_host, timebase); > + *ts_dev = ts_now; > +} > + > +void ixxat_usb_get_ts_tv(struct ixxat_usb_device *dev, u32 ts, ktime_t *k_time) > +{ > + ktime_t tmp_time = 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 = ts; > + tmp_time = ktime_add_us(tmp_time, ts - dev->time_ref.ts_dev_0); > + > + if (k_time) > + *k_time = tmp_time; > +} > + > +void ixxat_usb_set_ts_now(struct ixxat_usb_device *dev, u32 ts_now) > +{ > + struct timeval tmp_timeval; > + > + dev->time_ref.ts_dev_0 = ts_now; > + do_gettimeofday(&tmp_timeval); ktime_get_real() ? > + dev->time_ref.kt_host_0 = timeval_to_ktime(tmp_timeval); > + dev->time_ref.ts_dev_last = ts_now; > +} > + > +int ixxat_usb_send_cmd(struct usb_device *dev, > + struct ixxat_usb_dal_req *dal_req) > +{ > + int ret = 0; > + int i; > + u16 size; > + u16 value; > + u8 request; > + u8 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); Please initialize the values in the declararion above. > + size += sizeof(struct ixxat_usb_dal_res); le32_to_cpu? > + > + buf = kmalloc(size, GFP_KERNEL); > + if (!buf) > + return -ENOMEM; > + > + memcpy(buf, (u8 *)dal_req, size); Cast needed? I'm trying to understand what you are doing here. You assume that behind "dal_req" is "struct ixxat_usb_dal_res". That's not really transparent! > + > + for (i = 0; i < IXXAT_USB_MAX_COM_REQ; ++i) { > + const int to = msecs_to_jiffies(IXXAT_USB_MSG_TIMEOUT); > + > + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request, > + requesttype, value, 0, buf, size, to); > + > + if (ret < 0) > + msleep(IXXAT_USB_MSG_CYCLE); > + else > + break; > + } > + > + kfree(buf); > + > + if (ret < 0) > + dev_err(&dev->dev, "Error %d: Sending command failure\n", ret); > + > + return ret; > +} > + > +int ixxat_usb_rcv_cmd(struct usb_device *dev, > + struct ixxat_usb_dal_res *dal_res, int value) > +{ > + int ret; > + int res_size; > + int i; > + int size_to_read; > + u8 req; > + u8 req_type; > + u8 *buf; > + > + req = 0xff; > + req_type = USB_TYPE_VENDOR | USB_DIR_IN; > + res_size = 0; > + size_to_read = le32_to_cpu(dal_res->res_size); See above, here and in other functions. > + buf = kmalloc(size_to_read, GFP_KERNEL); > + if (!buf) > + return -ENOMEM; > + > + for (i = 0; i < IXXAT_USB_MAX_COM_REQ; ++i) { > + const int to = msecs_to_jiffies(IXXAT_USB_MSG_TIMEOUT); > + void *data = buf + (u8)res_size; > + const int size = size_to_read - res_size; Like you do here! > + > + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), > + req, req_type, value, 0, > + data, size, to); > + > + if (ret < 0) { > + msleep(IXXAT_USB_MSG_CYCLE); > + continue; > + } > + > + res_size += ret; > + if (res_size < size_to_read) > + msleep(IXXAT_USB_MSG_CYCLE); > + else > + break; > + } > + > + if (res_size != size_to_read) > + ret = -EBADMSG; > + > + if (ret < 0) { > + dev_err(&dev->dev, "Error %d: Receiving command failure\n", > + ret); > + kfree(buf); > + return ret; > + } > + > + memcpy((u8 *)dal_res, buf, size_to_read); Is the cast needed? > + kfree(buf); > + > + return ret; > +} > + > +static int ixxat_usb_get_dev_caps(struct usb_device *dev, > + struct ixxat_dev_caps *dev_caps) > +{ > + int i; > + int ret; > + u8 data[IXXAT_USB_CMD_BUFFER_SIZE] = { 0 }; > + struct ixxat_usb_dev_caps_req *req; > + struct ixxat_usb_dev_caps_res *res; > + u32 req_size = sizeof(*req); > + u32 res_size = sizeof(*res); > + > + req = (struct ixxat_usb_dev_caps_req *)data; > + res = (struct ixxat_usb_dev_caps_res *)(data + req_size); > + > + req->dal_req.req_size = cpu_to_le32(req_size); > + req->dal_req.req_code = cpu_to_le32(IXXAT_USB_BRD_CMD_GET_DEVCAPS); > + req->dal_req.req_port = 0xffff; > + req->dal_req.req_socket = 0xffff; What about using a helper function to setup "dal_req"... > + res->dal_res.res_size = cpu_to_le32(res_size); > + res->dal_res.ret_size = 0; > + res->dal_res.ret_code = 0xffffffff; and or "dal_res". The same pattern, also with data, repeats 5..6 times! > + ret = ixxat_usb_send_cmd(dev, &req->dal_req); > + if (ret < 0) > + return ret; > + > + ret = ixxat_usb_rcv_cmd(dev, &res->dal_res, 0xffff); > + if (ret < 0) > + return ret; > + > + dev_caps->bus_ctrl_count = le16_to_cpu(res->dev_caps.bus_ctrl_count); > + for (i = 0; i < dev_caps->bus_ctrl_count; ++i) { > + u16 type = le16_to_cpu(res->dev_caps.bus_ctrl_types[i]); > + > + dev_caps->bus_ctrl_types[i] = type; > + } > + > + return 0; > +} > + > +static int ixxat_usb_get_dev_info(struct ixxat_usb_device *dev, > + struct ixxat_usb_dev_info *dev_info) > +{ > + int ret; > + u8 data[IXXAT_USB_CMD_BUFFER_SIZE] = { 0 }; > + struct ixxat_usb_dev_info_req *req; > + struct ixxat_usb_dev_info_res *res; > + u32 req_size = sizeof(*req); > + u32 res_size = sizeof(*res); > + > + req = (struct ixxat_usb_dev_info_req *)data; > + res = (struct ixxat_usb_dev_info_res *)(data + req_size); > + > + req->dal_req.req_size = cpu_to_le32(req_size); > + req->dal_req.req_code = cpu_to_le32(IXXAT_USB_BRD_CMD_GET_DEVINFO); > + req->dal_req.req_port = 0xffff; > + req->dal_req.req_socket = 0xffff; > + > + res->dal_res.res_size = cpu_to_le32(res_size); > + res->dal_res.ret_size = 0; > + res->dal_res.ret_code = 0xffffffff; > + > + ret = ixxat_usb_send_cmd(dev->udev, &req->dal_req); > + if (ret < 0) > + return ret; > + > + ret = ixxat_usb_rcv_cmd(dev->udev, &res->dal_res, 0xffff); > + if (ret < 0) > + return ret; > + > + if (dev_info) { > + const size_t id_size = sizeof(res->info.device_id); > + const size_t nm_size = sizeof(res->info.device_name); > + const u32 fpgav = le32_to_cpu(res->info.device_fpga_version); > + const u16 devv = le16_to_cpu(res->info.device_version); > + > + memcpy(dev_info->device_id, &res->info.device_id, id_size); > + memcpy(dev_info->device_name, &res->info.device_name, nm_size); > + dev_info->device_fpga_version = fpgav; > + dev_info->device_version = devv; > + } > + > + return le32_to_cpu(res->dal_res.ret_code); > +} > + > +static int ixxat_usb_start_ctrl(struct ixxat_usb_device *dev, u32 *time_ref) > +{ > + int ret = -ENODEV; Not needed! > + u8 data[IXXAT_USB_CMD_BUFFER_SIZE] = { 0 }; > + struct ixxat_usb_ctrl_start_req *req; > + struct ixxat_usb_ctrl_start_res *res; > + u32 req_size = sizeof(*req); > + u32 res_size = sizeof(*res); > + > + req = (struct ixxat_usb_ctrl_start_req *)data; > + res = (struct ixxat_usb_ctrl_start_res *)(data + req_size); > + > + req->dal_req.req_size = cpu_to_le32(req_size); > + req->dal_req.req_code = cpu_to_le32(IXXAT_USB_CAN_CMD_START); > + req->dal_req.req_port = cpu_to_le16(dev->ctrl_index); > + req->dal_req.req_socket = 0xffff; > + > + res->dal_res.res_size = cpu_to_le32(res_size); > + res->dal_res.ret_size = 0; > + res->dal_res.ret_code = 0xffffffff; > + res->start_time = 0; > + > + ret = ixxat_usb_send_cmd(dev->udev, &req->dal_req); > + if (ret < 0) > + return ret; > + > + ret = ixxat_usb_rcv_cmd(dev->udev, &res->dal_res, dev->ctrl_index); > + if (ret < 0) > + return ret; > + > + if (time_ref) > + *time_ref = le32_to_cpu(res->start_time); > + > + return le32_to_cpu(res->dal_res.ret_code); > +} > + > +static int ixxat_usb_stop_ctrl(struct ixxat_usb_device *dev) > +{ > + int ret = -ENODEV; Ditto! > + u8 data[IXXAT_USB_CMD_BUFFER_SIZE] = { 0 }; > + struct ixxat_usb_ctrl_stop_req *req; > + struct ixxat_usb_ctrl_stop_res *res; > + u32 req_size = sizeof(*req); > + u32 res_size = sizeof(*res); > + > + req = (struct ixxat_usb_ctrl_stop_req *)data; > + res = (struct ixxat_usb_ctrl_stop_res *)(data + req_size); > + > + req->dal_req.req_size = cpu_to_le32(req_size); > + req->dal_req.req_code = cpu_to_le32(IXXAT_USB_CAN_CMD_STOP); > + req->dal_req.req_port = cpu_to_le16(dev->ctrl_index); > + req->dal_req.req_socket = 0xffff; > + req->action = cpu_to_le32(IXXAT_USB_STOP_ACTION_CLEARALL); > + > + res->dal_res.res_size = cpu_to_le32(res_size); > + res->dal_res.ret_size = 0; > + res->dal_res.ret_code = 0xffffffff; > + > + ret = ixxat_usb_send_cmd(dev->udev, &req->dal_req); > + if (ret < 0) > + return ret; > + > + ret = ixxat_usb_rcv_cmd(dev->udev, &res->dal_res, dev->ctrl_index); > + if (ret < 0) > + return ret; > + > + if (!le32_to_cpu(res->dal_res.ret_code)) > + dev->can.state = CAN_STATE_STOPPED; > + > + return le32_to_cpu(res->dal_res.ret_code); > +} > + > +static int ixxat_usb_power_ctrl(struct usb_device *dev, u8 mode) > +{ > + int ret = -ENODEV; Not needed! > + u8 data[IXXAT_USB_CMD_BUFFER_SIZE] = { 0 }; > + struct ixxat_usb_power_req *req; > + struct ixxat_usb_power_res *res; > + u32 req_size = sizeof(*req); > + u32 res_size = sizeof(*res); > + > + req = (struct ixxat_usb_power_req *)data; > + res = (struct ixxat_usb_power_res *)(data + req_size); > + > + req->dal_req.req_size = cpu_to_le32(req_size); > + req->dal_req.req_code = cpu_to_le32(IXXAT_USB_BRD_CMD_POWER); > + req->dal_req.req_port = cpu_to_le16(0xffff); > + req->dal_req.req_socket = 0xffff; > + req->mode = mode; > + > + res->dal_res.res_size = cpu_to_le32(res_size); > + res->dal_res.ret_size = 0; > + res->dal_res.ret_code = 0xffffffff; > + > + ret = ixxat_usb_send_cmd(dev, &req->dal_req); > + if (ret < 0) > + return ret; > + > + ret = ixxat_usb_rcv_cmd(dev, &res->dal_res, 0xffff); > + if (ret < 0) > + return ret; > + > + return le32_to_cpu(res->dal_res.ret_code); > +} > + > +static int ixxat_usb_reset_ctrl(struct ixxat_usb_device *dev) > +{ > + int ret = -ENODEV; Ditto! > + u8 data[IXXAT_USB_CMD_BUFFER_SIZE] = { 0 }; > + struct ixxat_usb_dal_req *req; > + struct ixxat_usb_dal_res *res; > + u32 req_size = sizeof(*req); > + u32 res_size = sizeof(*res); > + > + req = (struct ixxat_usb_dal_req *)data; > + res = (struct ixxat_usb_dal_res *)(data + req_size); See above! > + req->req_size = cpu_to_le32(req_size); > + req->req_code = cpu_to_le32(IXXAT_USB_CAN_CMD_RESET); > + req->req_port = cpu_to_le16(dev->ctrl_index); > + req->req_socket = 0xffff; > + > + res->res_size = cpu_to_le32(res_size); > + res->ret_size = 0; > + res->ret_code = 0xffffffff; > + > + ret = ixxat_usb_send_cmd(dev->udev, req); > + if (ret < 0) > + return ret; > + > + ret = ixxat_usb_rcv_cmd(dev->udev, res, dev->ctrl_index); > + if (ret < 0) > + return ret; > + > + return le32_to_cpu(res->ret_code); > +} > + > +static void ixxat_usb_unlink_all_urbs(struct ixxat_usb_device *dev) > +{ > + usb_kill_anchored_urbs(&dev->rx_submitted); > + usb_kill_anchored_urbs(&dev->tx_submitted); > + atomic_set(&dev->active_tx_urbs, 0); > +} > + > +static int ixxat_usb_set_mode(struct net_device *netdev, enum can_mode mode) > +{ > + struct ixxat_usb_device *dev = netdev_priv(netdev); > + > + switch (mode) { > + case CAN_MODE_START: > + dev->restart_flag = 1; > + wake_up_interruptible(&dev->wait_queue); > + break; > + case CAN_MODE_STOP: > + case CAN_MODE_SLEEP: > + default: > + return -EOPNOTSUPP; > + } > + > + return 0; > +} > + > +static int ixxat_usb_get_berr_counter(const struct net_device *netdev, > + struct can_berr_counter *bec) > +{ > + struct ixxat_usb_device *dev = netdev_priv(netdev); > + > + *bec = dev->bec; > + return 0; > +} > + > +static int ixxat_usb_handle_canmsg(struct ixxat_usb_device *dev, > + struct ixxat_can_msg *rx) > +{ > + struct net_device *netdev = dev->netdev; > + struct canfd_frame *can_frame; > + struct sk_buff *skb; > + const u32 flags = le32_to_cpu(rx->flags); > + const u8 dlc = IXXAT_USB_DECODE_DLC(flags); > + > + if (flags & IXXAT_USB_FDMSG_FLAGS_EDL) > + skb = alloc_canfd_skb(netdev, &can_frame); > + else > + skb = alloc_can_skb(netdev, (struct can_frame **)&can_frame); Argh, we should avoid such ugly casts! But I find it in other drivers as well :(. > + > + if (!skb) > + return -ENOMEM; > + > + if (flags & IXXAT_USB_FDMSG_FLAGS_EDL) { > + if (flags & IXXAT_USB_FDMSG_FLAGS_FDR) > + can_frame->flags |= CANFD_BRS; > + > + if (flags & IXXAT_USB_FDMSG_FLAGS_ESI) > + can_frame->flags |= CANFD_ESI; > + > + can_frame->len = can_dlc2len(get_canfd_dlc(dlc)); > + } else { > + can_frame->len = get_can_dlc(dlc); > + } > + > + if (flags & IXXAT_USB_MSG_FLAGS_OVR) { > + netdev->stats.rx_over_errors++; > + netdev->stats.rx_errors++; > + netdev_err(netdev, "Error: Message overflow\n"); > + } > + > + can_frame->can_id = le32_to_cpu(rx->msg_id); > + > + if (flags & IXXAT_USB_MSG_FLAGS_EXT) > + can_frame->can_id |= CAN_EFF_FLAG; > + > + if (flags & IXXAT_USB_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); > + > + netdev->stats.rx_packets++; > + netdev->stats.rx_bytes += can_frame->len; > + 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 = dev->netdev; > + struct can_frame *can_frame; > + struct sk_buff *skb; > + > + u32 raw_status = le32_to_cpu(*(u32 *)(rx->data)); > + enum can_state new_state = CAN_STATE_ERROR_ACTIVE; > + > + skb = alloc_can_err_skb(netdev, &can_frame); > + if (!skb) > + return -ENOMEM; > + > + if (raw_status == IXXAT_USB_CAN_STATUS_OK) { > + dev->can.state = CAN_STATE_ERROR_ACTIVE; > + can_frame->can_id |= CAN_ERR_CRTL; > + can_frame->data[1] |= CAN_ERR_CRTL_ACTIVE; > + } else if (raw_status & IXXAT_USB_CAN_STATUS_BUSOFF) { > + can_frame->can_id |= CAN_ERR_BUSOFF; > + dev->can.can_stats.bus_off++; > + new_state = CAN_STATE_BUS_OFF; > + can_bus_off(netdev); Does the device recover automatically from bus-off? > + } else { > + if (raw_status & IXXAT_USB_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_USB_CAN_STATUS_ERR_PAS) { > + can_frame->can_id |= CAN_ERR_CRTL; > + can_frame->data[1] |= CAN_ERR_CRTL_TX_PASSIVE; > + can_frame->data[1] |= CAN_ERR_CRTL_RX_PASSIVE; > + dev->can.can_stats.error_passive++; > + new_state = CAN_STATE_ERROR_PASSIVE; > + } > + > + if (raw_status & IXXAT_USB_CAN_STATUS_OVERRUN) { > + can_frame->can_id |= CAN_ERR_CRTL; > + can_frame->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW; > + new_state = CAN_STATE_MAX; > + } > + } > + > + if (new_state == CAN_STATE_ERROR_ACTIVE) { > + dev->bec.txerr = 0; > + dev->bec.rxerr = 0; > + } > + > + if (new_state != CAN_STATE_MAX) > + dev->can.state = new_state; > + > + netdev->stats.rx_packets++; > + netdev->stats.rx_bytes += can_frame->can_dlc; > + 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 = dev->netdev; > + struct can_frame *can_frame; > + struct sk_buff *skb; > + u8 raw_error = rx->data[0]; > + > + if (dev->can.state == CAN_STATE_BUS_OFF) > + return 0; > + > + skb = alloc_can_err_skb(netdev, &can_frame); > + if (!skb) > + return -ENOMEM; > + > + switch (raw_error) { > + case IXXAT_USB_CAN_ERROR_ACK: > + can_frame->can_id |= CAN_ERR_ACK; > + netdev->stats.tx_errors++; > + break; > + case IXXAT_USB_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_USB_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_USB_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_USB_CAN_ERROR_STUFF: > + can_frame->can_id |= CAN_ERR_PROT; > + can_frame->data[2] |= CAN_ERR_PROT_STUFF; > + netdev->stats.rx_errors++; > + break; > + default: > + can_frame->can_id |= CAN_ERR_PROT; > + can_frame->data[2] |= CAN_ERR_PROT_UNSPEC; > + netdev->stats.rx_errors++; > + break; > + } > + > + netdev->stats.rx_packets++; > + netdev->stats.rx_bytes += can_frame->can_dlc; > + netif_rx(skb); > + > + dev->bec.rxerr = rx->data[3]; > + dev->bec.txerr = rx->data[4]; > + > + return 0; > +} > + > +static int ixxat_usb_decode_buf(struct urb *urb) > +{ > + struct ixxat_usb_device *dev = urb->context; > + struct net_device *netdev = dev->netdev; > + struct ixxat_can_msg can_msg = {0}; > + int ret = 0; > + u32 msg_end = urb->actual_length; > + u32 read_size = 0; > + u8 *data = urb->transfer_buffer; > + > + while (msg_end > 0) { > + u8 msg_type; > + > + if (dev->adapter == &usb2can_cl1) { > + struct ixxat_can_msg_cl1 *msg_cl1; > + > + msg_cl1 = (struct ixxat_can_msg_cl1 *)&data[read_size]; > + can_msg.size = msg_cl1->size; > + can_msg.time = msg_cl1->time; > + can_msg.msg_id = msg_cl1->msg_id; > + can_msg.flags = msg_cl1->flags; > + can_msg.data = msg_cl1->data;decode_buf > + } else { > + struct ixxat_can_msg_cl2 *msg_cl2; > + > + msg_cl2 = (struct ixxat_can_msg_cl2 *)&data[read_size]; > + can_msg.size = msg_cl2->size; > + can_msg.time = msg_cl2->time; > + can_msg.msg_id = msg_cl2->msg_id; > + can_msg.flags = msg_cl2->flags; > + can_msg.data = msg_cl2->data; > + } See my comments in a previous email about using unions. > + > + if (!can_msg.size) { > + ret = -ENOTSUPP; > + netdev_err(netdev, "Error %d: Unsupported usb msg\n", > + ret); > + break; > + } > + > + if ((read_size + can_msg.size + 1) > urb->actual_length) { > + ret = -EBADMSG; > + netdev_err(netdev, > + "Error %d: Usb rx-buffer size unknown\n", > + ret); > + break; > + } > + > + msg_type = le32_to_cpu(can_msg.flags); > + msg_type &= IXXAT_USB_MSG_FLAGS_TYPE; > + > + switch (msg_type) { > + case IXXAT_USB_CAN_DATA: > + ret = ixxat_usb_handle_canmsg(dev, &can_msg); > + if (ret < 0) > + goto fail; > + break; > + case IXXAT_USB_CAN_STATUS: > + ret = ixxat_usb_handle_status(dev, &can_msg); > + if (ret < 0) > + goto fail; > + break; > + case IXXAT_USB_CAN_ERROR: > + ret = ixxat_usb_handle_error(dev, &can_msg); > + if (ret < 0) > + goto fail; > + break; > + case IXXAT_USB_CAN_TIMEOVR: > + ixxat_usb_get_ts_tv(dev, can_msg.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 (%d): ignored\n", > + msg_type, msg_type); > + break; > + } > + > + read_size += (can_msg.size + 1); > + msg_end -= (can_msg.size + 1); > + } > + > +fail: > + if (ret < 0) > + netdev_err(netdev, "Error %d: Buffer decoding failed\n", ret); > + > + return ret; > +} > + > +static void ixxat_usb_read_bulk(struct urb *urb) > +{ > + struct ixxat_usb_device *dev = urb->context; > + const struct ixxat_usb_adapter *adapter = dev->adapter; > + struct net_device *netdev = dev->netdev; > + struct usb_device *udev = dev->udev; > + int ret; > + > + 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) > + ret = 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, dev); > + > + usb_anchor_urb(urb, &dev->rx_submitted); > + ret = usb_submit_urb(urb, GFP_ATOMIC); > + if (ret == 0) > + return; > + > + usb_unanchor_urb(urb); > + > + if (ret == -ENODEV) > + netif_device_detach(netdev); > + else > + netdev_err(netdev, > + "Error %d: Failed to resubmit read bulk urb\n", ret); > +} > + > +static void ixxat_usb_write_bulk(struct urb *urb) > +{ > + struct ixxat_tx_urb_context *context = urb->context; > + struct ixxat_usb_device *dev; > + struct net_device *netdev; > + > + if (WARN_ON(!context)) > + return; > + > + dev = context->dev; > + netdev = dev->netdev; > + atomic_dec(&dev->active_tx_urbs); > + > + if (!netif_device_present(netdev)) > + return; > + > + if (!urb->status) { > + netdev->stats.tx_packets += context->count; > + netdev->stats.tx_bytes += context->dlc; > + } else { > + netdev_err(netdev, "Error %d: Tx urb aborted\n", urb->status); > + } > + > + can_get_echo_skb(netdev, context->echo_index); > + context->echo_index = IXXAT_USB_MAX_TX_URBS; > + > + if (!urb->status) > + netif_wake_queue(netdev); > +} > + > +static void ixxat_usb_encode_msg(struct ixxat_usb_device *dev, > + struct sk_buff *skb, u8 *obuf, > + size_t *size) > +{ > + struct canfd_frame *cf = (struct canfd_frame *)skb->data; > + struct ixxat_can_msg can_msg = {0}; > + > + if (cf->can_id & CAN_RTR_FLAG) > + can_msg.flags |= IXXAT_USB_MSG_FLAGS_RTR; > + > + if (cf->can_id & CAN_EFF_FLAG) { > + can_msg.flags |= IXXAT_USB_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 (can_is_canfd_skb(skb)) { > + can_msg.flags |= IXXAT_USB_FDMSG_FLAGS_EDL; > + > + if (!(cf->can_id & CAN_RTR_FLAG) && (cf->flags & CANFD_BRS)) > + can_msg.flags |= IXXAT_USB_FDMSG_FLAGS_FDR; > + > + can_msg.flags |= IXXAT_USB_ENCODE_DLC(can_len2dlc(cf->len)); > + } else { > + can_msg.flags |= IXXAT_USB_ENCODE_DLC(cf->len); > + } > + > + 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.data is just a pointer! > + > + if (dev->adapter == &usb2can_cl1) { > + struct ixxat_can_msg_cl1 msg_cl1 = {0}; > + > + msg_cl1.size = (u8)(sizeof(msg_cl1) - 1 - CAN_MAX_DLEN); > + msg_cl1.size += cf->len; > + > + msg_cl1.msg_id = can_msg.msg_id; > + msg_cl1.flags = can_msg.flags; > + memcpy(msg_cl1.data, cf->data, cf->len); > + > + *size = msg_cl1.size + 1; > + memcpy(obuf, &msg_cl1, *size); > + } else { > + struct ixxat_can_msg_cl2 msg_cl2 = {0}; > + > + msg_cl2.size = (u8)(sizeof(msg_cl2) - 1 - CANFD_MAX_DLEN); > + msg_cl2.size += cf->len; > + msg_cl2.msg_id = can_msg.msg_id; > + msg_cl2.flags = can_msg.flags; > + memcpy(msg_cl2.data, cf->data, cf->len); > + > + *size = msg_cl2.size + 1; > + memcpy(obuf, &msg_cl2, *size); > + } > + > + skb->data_len = *size; > +} > + > +static netdev_tx_t ixxat_usb_start_xmit(struct sk_buff *skb, > + struct net_device *netdev) > +{ > + struct ixxat_usb_device *dev = netdev_priv(netdev); > + struct ixxat_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, ret; > + size_t size = dev->adapter->buffer_size_tx; > + > + 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 (WARN_ON_ONCE(!context)) > + return NETDEV_TX_BUSY; > + > + urb = context->urb; > + obuf = urb->transfer_buffer; > + > + ixxat_usb_encode_msg(dev, skb, obuf, &size); > + > + context->echo_index = i; > + context->dlc = cf->len; > + context->count = 1; > + > + urb->transfer_buffer_length = size; > + usb_anchor_urb(urb, &dev->tx_submitted); > + can_put_echo_skb(skb, netdev, context->echo_index); > + atomic_inc(&dev->active_tx_urbs); > + > + ret = usb_submit_urb(urb, GFP_ATOMIC); > + if (unlikely(ret)) { > + can_free_echo_skb(netdev, context->echo_index); > + usb_unanchor_urb(urb); > + atomic_dec(&dev->active_tx_urbs); > + > + context->echo_index = IXXAT_USB_MAX_TX_URBS; > + > + if (ret == -ENODEV) { > + netif_device_detach(netdev); > + } else { > + stats->tx_dropped++; > + netdev_err(netdev, > + "Error %d: Submitting tx-urb failed\n", ret); > + } > + } else { > + if (atomic_read(&dev->active_tx_urbs) >= 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 ret = 0; > + const struct ixxat_usb_adapter *adapter = dev->adapter; > + struct net_device *netdev = dev->netdev; > + struct usb_device *udev = dev->udev; > + > + for (i = 0; i < IXXAT_USB_MAX_RX_URBS; i++) { > + struct urb *urb; > + u8 *buf; > + > + urb = usb_alloc_urb(0, GFP_KERNEL); > + if (!urb) { > + ret = -ENOMEM; > + netdev_err(netdev, "Error %d: No memory for URBs\n", > + ret); > + break; > + } > + > + buf = kmalloc(adapter->buffer_size_rx, GFP_KERNEL); > + if (!buf) { > + usb_free_urb(urb); > + ret = -ENOMEM; > + netdev_err(netdev, > + "Error %d: No memory for USB-buffer\n", ret); > + break; > + } > + > + usb_fill_bulk_urb(urb, udev, > + usb_rcvbulkpipe(udev, dev->ep_msg_in), buf, > + adapter->buffer_size_rx, ixxat_usb_read_bulk, > + dev); > + > + urb->transfer_flags |= URB_FREE_BUFFER; > + usb_anchor_urb(urb, &dev->rx_submitted); > + > + ret = usb_submit_urb(urb, GFP_KERNEL); > + if (ret < 0) { > + usb_unanchor_urb(urb); > + kfree(buf); > + usb_free_urb(urb); > + > + if (ret == -ENODEV) > + netif_device_detach(netdev); > + > + break; > + } > + > + usb_free_urb(urb); > + } > + > + if (i == 0) > + netdev_err(netdev, "Error: Couldn't setup any rx-URBs\n"); > + > + return ret; > +} > + > +static int ixxat_usb_setup_tx_urbs(struct ixxat_usb_device *dev) > +{ > + int i; > + int ret = 0; > + const struct ixxat_usb_adapter *adapter = dev->adapter; > + struct net_device *netdev = dev->netdev; > + struct usb_device *udev = dev->udev; > + > + for (i = 0; i < IXXAT_USB_MAX_TX_URBS; i++) { > + struct ixxat_tx_urb_context *context; > + struct urb *urb = NULL; > + u8 *buf = NULL; > + > + urb = usb_alloc_urb(0, GFP_KERNEL); > + if (!urb) { > + ret = -ENOMEM; > + netdev_err(netdev, "Error %d: No memory for URBs\n", > + ret); > + break; > + } > + > + buf = kmalloc(adapter->buffer_size_tx, GFP_KERNEL); > + if (!buf) { > + usb_free_urb(urb); > + ret = -ENOMEM; > + netdev_err(netdev, > + "Error %d: No memory for USB-buffer\n", ret); > + break; > + } > + > + context = dev->tx_contexts + i; > + context->dev = dev; > + context->urb = urb; > + > + usb_fill_bulk_urb(urb, udev, > + usb_sndbulkpipe(udev, dev->ep_msg_out), buf, > + adapter->buffer_size_tx, ixxat_usb_write_bulk, > + context); > + > + urb->transfer_flags |= URB_FREE_BUFFER; > + } > + > + if (i == 0) { > + netdev_err(netdev, "Error: 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 = usb_get_intfdata(intf); dev; dev = prev_dev) { > + prev_dev = dev->prev_dev; > + unregister_netdev(dev->netdev); > + free_candev(dev->netdev); > + } > + > + usb_set_intfdata(intf, NULL); > +} > + > +static int ixxat_usb_restart_task(void *user_data) > +{ > + u32 time_ref; > + struct ixxat_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) { > + ixxat_usb_stop_ctrl(dev); > + ixxat_usb_start_ctrl(dev, &time_ref); > + dev->restart_flag = 0; > + dev->can.state = CAN_STATE_ERROR_ACTIVE; > + } > + } else { > + msleep(IXXAT_RESTART_TASK_CYCLE_TIME); > + } > + } > + return 0; > +} > + > +static int ixxat_usb_init(struct ixxat_usb_device *dev) > +{ > + dev->restart_task = kthread_run(&ixxat_usb_restart_task, dev, > + "restart_thread"); Do you really need a task for that purpose? > + if (!dev->restart_task) > + return -ENOBUFS; > + > + return 0; > +} > + > +static int ixxat_usb_start(struct ixxat_usb_device *dev) > +{ > + int ret; > + int i; > + u32 time_ref = 0; > + const struct ixxat_usb_adapter *adapter = dev->adapter; > + > + ret = ixxat_usb_setup_rx_urbs(dev); > + if (ret < 0) > + return ret; > + > + ret = ixxat_usb_setup_tx_urbs(dev); > + if (ret < 0) > + return ret; > + > + /* 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) { > + ret = adapter->init_ctrl(dev); > + if (ret < 0) > + goto fail; > + } > + > + if (dev->ctrl_opened_count == 0) { > + ret = ixxat_usb_start_ctrl(dev, &time_ref); > + if (ret < 0) > + goto fail; > + > + ixxat_usb_set_ts_now(dev, time_ref); > + } > + > + dev->ctrl_opened_count++; > + > + dev->bec.txerr = 0; > + dev->bec.rxerr = 0; > + > + dev->state |= IXXAT_USB_STATE_STARTED; > + dev->can.state = CAN_STATE_ERROR_ACTIVE; > + > + return 0; > + > +fail: > + if (ret == -ENODEV) > + netif_device_detach(dev->netdev); > + > + netdev_err(dev->netdev, "Error %d: Couldn't submit control\n", ret); > + > + for (i = 0; i < IXXAT_USB_MAX_TX_URBS; i++) { > + usb_free_urb(dev->tx_contexts[i].urb); > + dev->tx_contexts[i].urb = NULL; > + } > + > + return ret; > +} > + > +static int ixxat_usb_open(struct net_device *netdev) > +{ > + struct ixxat_usb_device *dev = netdev_priv(netdev); > + int ret; > + > + /* common open */ > + ret = open_candev(netdev); > + if (ret < 0) > + return ret; > + > + /* finally start device */ > + ret = ixxat_usb_start(dev); > + if (ret < 0) { > + netdev_err(netdev, "Error %d: Couldn't start device.\n", ret); > + close_candev(netdev); > + return ret; > + } > + > + netif_start_queue(netdev); > + > + return 0; > +} > + > +static int ixxat_usb_stop(struct net_device *netdev) > +{ > + int ret = 0; > + struct ixxat_usb_device *dev = netdev_priv(netdev); > + > + netif_stop_queue(netdev); > + ixxat_usb_unlink_all_urbs(dev); > + > + if (dev->ctrl_opened_count == 1) { > + ret = ixxat_usb_stop_ctrl(dev); > + if (ret < 0) > + return ret; > + } > + dev->ctrl_opened_count--; > + > + close_candev(netdev); > + > + dev->can.state = CAN_STATE_STOPPED; > + > + return 0; > +} > + > +static const struct net_device_ops ixxat_usb_netdev_ops = { > + .ndo_open = ixxat_usb_open, > + .ndo_stop = ixxat_usb_stop, > + .ndo_start_xmit = ixxat_usb_start_xmit > +}; > + > +static const struct ixxat_usb_adapter *ixxat_usb_get_adapter(const u16 id) > +{ > + switch (id) { > + case USB2CAN_COMPACT_PRODUCT_ID: > + case USB2CAN_EMBEDDED_PRODUCT_ID: > + case USB2CAN_PROFESSIONAL_PRODUCT_ID: > + case USB2CAN_AUTOMOTIVE_PRODUCT_ID: > + return &usb2can_cl1; > + case USB2CAN_FD_COMPACT_PRODUCT_ID: > + case USB2CAN_FD_PROFESSIONAL_PRODUCT_ID: > + case USB2CAN_FD_AUTOMOTIVE_PRODUCT_ID: > + case USB2CAN_FD_PCIE_MINI_PRODUCT_ID: > + case USB2CAR_PRODUCT_ID: > + return &usb2can_cl2; > + case CAN_IDM101_PRODUCT_ID: > + return &can_idm; > + default: > + return NULL; > + } > +} > + > +static int ixxat_usb_create_dev(struct usb_interface *intf, > + const struct ixxat_usb_adapter *adapter, > + u16 ctrl_index) > +{ > + struct usb_device *usb_dev = interface_to_usbdev(intf); > + struct ixxat_usb_device *dev; > + struct net_device *netdev; > + int ret; > + int i; > + > + netdev = alloc_candev(sizeof(*dev), IXXAT_USB_MAX_TX_URBS); > + if (!netdev) { > + dev_err(&intf->dev, "Cannot allocate candev\n"); > + return -ENOMEM; > + } > + > + dev = netdev_priv(netdev); > + > + dev->restart_task = NULL; > + dev->restart_flag = 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 = adapter; > + dev->ctrl_index = ctrl_index; > + dev->state = IXXAT_USB_STATE_CONNECTED; > + > + i = ctrl_index + adapter->ep_offs; > + dev->ep_msg_in = adapter->ep_msg_in[i]; > + dev->ep_msg_out = adapter->ep_msg_out[i]; > + > + dev->can.clock.freq = adapter->clock; > + dev->can.bittiming_const = adapter->bt; > + dev->can.data_bittiming_const = adapter->btd; > + > + dev->can.do_set_mode = ixxat_usb_set_mode; > + dev->can.do_get_berr_counter = ixxat_usb_get_berr_counter; > + > + dev->can.ctrlmode_supported = adapter->modes; > + > + netdev->netdev_ops = &ixxat_usb_netdev_ops; > + > + netdev->flags |= IFF_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_dev = usb_get_intfdata(intf); > + usb_set_intfdata(intf, dev); > + > + SET_NETDEV_DEV(netdev, &intf->dev); > + ret = register_candev(netdev); > + if (ret < 0) { > + dev_err(&intf->dev, "Error %d: Failed to register can device\n", > + ret); > + goto fail; > + } > + > + if (dev->prev_dev) > + (dev->prev_dev)->next_dev = dev; > + > + ret = ixxat_usb_init(dev); > + if (ret < 0) { > + dev_err(&intf->dev, > + "Error %d: Failed to initialize can device\n", ret); unregister_candev() missing! > + goto fail; > + } > + > + ret = ixxat_usb_get_dev_info(dev, &dev->dev_info); > + if (ret < 0) { > + dev_err(&intf->dev, > + "Error %d: Failed to get device information\n", ret); > + goto fail; What about the running thread? Please use a serialized cleanup with labels. > + } > + > + netdev_info(netdev, "%s: Connected Channel %u (device %s)\n", > + dev->dev_info.device_name, ctrl_index, > + dev->dev_info.device_id); > + > + return 0; > + > +fail: > + usb_set_intfdata(intf, dev->prev_dev); > + free_candev(netdev); > + return ret; > +} > + > +static int ixxat_usb_probe(struct usb_interface *intf, > + const struct usb_device_id *id) > +{ > + struct usb_device *udev = interface_to_usbdev(intf); > + struct usb_host_interface *host_intf = intf->altsetting; > + const struct ixxat_usb_adapter *adapter; > + struct ixxat_dev_caps dev_caps; > + u16 i; > + int ret; > + > + usb_reset_configuration(udev); > + > + adapter = ixxat_usb_get_adapter(id->idProduct); > + if (!adapter) { > + dev_err(&intf->dev, "%s: Unknown device id %d\n", > + IXXAT_USB_DRIVER_NAME, id->idProduct); > + return -ENODEV; > + } > + > + for (i = 0; i < host_intf->desc.bNumEndpoints; i++) { > + const u8 epaddr = host_intf->endpoint[i].desc.bEndpointAddress; > + int match; > + u8 j; > + > + /* Check if usb-endpoint address matches known usb-endpoints */ > + for (j = 0; j < IXXAT_USB_MAX_CHANNEL; j++) { > + u8 ep_msg_in = adapter->ep_msg_in[j]; > + u8 ep_msg_out = adapter->ep_msg_in[j]; > + > + if (epaddr == ep_msg_in || epaddr == ep_msg_out) { > + match = 1; > + break; > + } > + } > + > + if (!match) > + return -ENODEV; > + } > + > + ret = ixxat_usb_power_ctrl(udev, IXXAT_USB_POWER_WAKEUP); > + if (ret < 0) > + return ret; > + > + msleep(IXXAT_USB_POWER_WAKEUP_TIME); > + > + ret = ixxat_usb_get_dev_caps(udev, &dev_caps); > + if (ret < 0) { > + dev_err(&intf->dev, "Failed to get device capabilities\n"); > + return ret; > + } > + > + ret = -ENODEV; > + for (i = 0; i < dev_caps.bus_ctrl_count; i++) { > + u8 bustype = IXXAT_USB_BUS_TYPE(dev_caps.bus_ctrl_types[i]); > + > + if (bustype == IXXAT_USB_BUS_CAN) > + ret = ixxat_usb_create_dev(intf, adapter, i); > + > + if (ret < 0) { > + /* deregister already created devices */ > + ixxat_usb_disconnect(intf); > + return ret; > + } > + } > + > + return ret; > +} > + > +static struct usb_driver ixxat_usb_driver = { > + .name = IXXAT_USB_DRIVER_NAME, > + .probe = ixxat_usb_probe, > + .disconnect = ixxat_usb_disconnect, > + .id_table = 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/can/usb/ixxat_usb/ixxat_usb_core.h > new file mode 100644 > index 0000000..84ef596 > --- /dev/null > +++ b/drivers/net/can/usb/ixxat_usb/ixxat_usb_core.h > @@ -0,0 +1,487 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > + > +/* CAN driver base for IXXAT USB-to-CAN > + * > + * Copyright (C) 2018 HMS Industrial Networks <socketcan@xxxxxxxxxxxxxxx> > + * > + * 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 IXXAT_USB_CORE_H > +#define IXXAT_USB_CORE_H > + > +#define IXXAT_USB_DRIVER_NAME "ixxat_usb2can" > + > +#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 IXXAT_USB_BUS_CAN 1 > + > +#define IXXAT_USB_BUS_TYPE(type) ((u8)(((type) >> 8) & 0x00FF)) > + > +#define IXXAT_USB_STATE_CONNECTED 0x00000001 > +#define IXXAT_USB_STATE_STARTED 0x00000002 > + > +#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_CMD_BUFFER_SIZE 256 > + > +#define IXXAT_USB_OPMODE_STANDARD 0x01 > +#define IXXAT_USB_OPMODE_EXTENDED 0x02 > +#define IXXAT_USB_OPMODE_ERRFRAME 0x04 > +#define IXXAT_USB_OPMODE_LISTONLY 0x08 > + > +#define IXXAT_USB_EXMODE_EXTDATA 0x01 > +#define IXXAT_USB_EXMODE_FASTDATA 0x02 > +#define IXXAT_USB_EXMODE_ISOFD 0x04 > + > +#define IXXAT_USB_BTMODE_NAT 0x00000001 > +#define IXXAT_USB_BTMODE_TSM 0x00000002 > + > +#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_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_DECODE_DLC(flags) (((flags) & IXXAT_USB_MSG_FLAGS_DLC) >> 16) > +#define IXXAT_USB_ENCODE_DLC(len) (((len) << 16) & IXXAT_USB_MSG_FLAGS_DLC) > + > +#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 - CAN message > + * @size: CAN message size > + * @time: CAN message timestamp > + * @msg_id: CAN message ID > + * @flags: CAN message flags > + * @data: CAN message data > + * > + * IXXAT CAN message (interpreted CL1 or CL2 message) > + */ > +struct ixxat_can_msg { > + u8 size; > + u32 time; > + u32 msg_id; > + u32 flags; > + u8 *data; > +}; > + > +/** > + * struct ixxat_can_msg_cl1 - CAN message (CL1) > + * @size: CAN message size > + * @time: CAN message timestamp > + * @msg_id: CAN message ID > + * @flags: CAN message flags > + * @data: CAN message data > + * > + * IXXAT CAN message as delivered by CL1 devices > + */ > +struct ixxat_can_msg_cl1 { > + u8 size; > + u32 time; > + u32 msg_id; > + u32 flags; Please use __le32 and friends > + u8 data[CAN_MAX_DLEN]; > +} __packed; > + > +/** > + * struct ixxat_can_msg_cl2 - CAN message (CL2) > + * @size: CAN message size > + * @time: CAN message timestamp > + * @msg_id: CAN message ID > + * @flags: CAN message flags > + * @client_id: CAN message client ID > + * @data: CAN message data > + * > + * IXXAT CAN message as delivered by CL2 devices > + */ > +struct ixxat_can_msg_cl2 { > + u8 size; > + u32 time; > + u32 msg_id; > + u32 flags; > + u32 client_id; > + u8 data[CANFD_MAX_DLEN]; > +} __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 needed to identifies the device > + */ > +struct ixxat_dev_caps { > + u16 bus_ctrl_count; > + u16 bus_ctrl_types[IXXAT_USB_MAX_TYPES]; > +} __packed; > + > +/** > + * struct ixxat_usb_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: device version of FPGA design > + * > + * Contains device information for IXXAT usb devices > + */ > +struct ixxat_usb_dev_info { > + char device_name[16]; > + char device_id[16]; > + u16 device_version; > + u32 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 from the device and 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 > + * echo_index: echo index > + * @dlc: data length code > + * @count: counter > + * @urb: USB request block > + * > + * Contains content for USB request block transmissions > + */ > +struct ixxat_tx_urb_context { > + struct ixxat_usb_device *dev; > + u32 echo_index; > + u8 dlc; > + u8 count; > + struct urb *urb; > +}; > + > +/** > + * struct ixxat_usb_device IXXAT usb device > + * @can: can_priv > + * @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_opened_count: Counter for opened controllers > + * @ctrl_index: Controller index > + * @ep_msg_in: USB endpoint for incoming messages > + * @ep_msg_out: USB endpoint for outgoing messages > + * @restart_task: Task to restart everything if needed > + * @restart_flag: Flag to determine if it should be restarted > + * @must_quit: Flag to determine if it should be quit > + * @wait_queue: Head of the wait queue > + * @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; > + > + u32 ctrl_opened_count; > + u16 ctrl_index; > + > + u8 ep_msg_in; > + u8 ep_msg_out; > + > + struct task_struct *restart_task; > + u8 restart_flag; > + u8 must_quit; > + wait_queue_head_t wait_queue; > + > + struct ixxat_usb_device *prev_dev; > + struct ixxat_usb_device *next_dev; > + > + struct ixxat_time_ref time_ref; > + struct ixxat_usb_dev_info dev_info; > + > + struct can_berr_counter bec; > +}; > + > +/** > + * struct ixxat_usb_dal_req IXXAT device request block > + * @req_size: size of the request > + * @req_port: port of the request > + * @req_socket: socket of the request > + * @req_code: code of the request > + * > + * Can be send to the IXXAT device to request information > + */ > +struct ixxat_usb_dal_req { > + u32 req_size; > + u16 req_port; > + u16 req_socket; > + u32 req_code; > +} __packed; > + > +/** > + * struct ixxat_usb_dal_res IXXAT device respond block > + * @res_size: size of the respond > + * @ret_size: size of the return > + * @ret_code: return code > + * > + * After a request, this respond block comes back > + */ > +struct ixxat_usb_dal_res { > + u32 res_size; > + u32 ret_size; > + u32 ret_code; > +} __packed; > + > +/** > + * struct ixxat_usb_dev_caps_req IXXAT device capabilities request block > + * @dal_req: Request block > + * > + * Can be send to the IXXAT device to request device capabilities > + */ > +struct ixxat_usb_dev_caps_req { > + struct ixxat_usb_dal_req dal_req; > +} __packed; > + > +/** > + * struct ixxat_usb_dev_caps_res IXXAT device respond block > + * @dal_res: respond block > + * @dev_caps: device capabilities > + * > + * Device response contains the device capabilities > + */ > +struct ixxat_usb_dev_caps_res { > + struct ixxat_usb_dal_res dal_res; > + struct ixxat_dev_caps dev_caps; > +} __packed; > + > +/** > + * struct ixxat_usb_ctrl_init_res IXXAT initialization respond block > + * @dal_res: respond block > + * > + * Device response to the initialization request > + */ > +struct ixxat_usb_ctrl_init_res { > + struct ixxat_usb_dal_res dal_res; > +} __packed; > + > +/** > + * struct ixxat_usb_ctrl_start_req IXXAT request block to start the controller > + * @dal_req: Request block > + * > + * Can be send to the IXXAT device to request controller start > + */ > +struct ixxat_usb_ctrl_start_req { > + struct ixxat_usb_dal_req dal_req; > +} __packed; > + > +/** > + * struct ixxat_usb_ctrl_start_res IXXAT start respond block > + * @dal_res: respond block > + * @start_time: Device starting point > + * > + * Device response to the start request > + */ > +struct ixxat_usb_ctrl_start_res { > + struct ixxat_usb_dal_res dal_res; > + u32 start_time; > +} __packed; > + > +struct ixxat_usb_power_req { > + struct ixxat_usb_dal_req dal_req; > + u8 mode; > + u8 _padding1; > + u16 _padding2; > +} __packed; > + > +struct ixxat_usb_power_res { > + struct ixxat_usb_dal_res dal_res; > +} __packed; > + > +/** > + * struct ixxat_usb_ctrl_stop_req IXXAT request block to stop the controller > + * @dal_req: Request block > + * @action: Requested action > + * > + * Can be send to the IXXAT device to request controller stop > + */ > +struct ixxat_usb_ctrl_stop_req { > + struct ixxat_usb_dal_req dal_req; > + u32 action; > +} __packed; > + > +/** > + * struct ixxat_usb_ctrl_stop_res IXXAT stop respond block > + * @dal_res: respond block > + * > + * Device response to the stop request > + */ > +struct ixxat_usb_ctrl_stop_res { > + struct ixxat_usb_dal_res dal_res; > +} __packed; > + > +/** > + * struct ixxat_usb_dev_info_req IXXAT request block for device info > + * @dal_req: Request block > + * > + * Can be send to the IXXAT device to request device information > + */ > +struct ixxat_usb_dev_info_req { > + struct ixxat_usb_dal_req dal_req; > +} __packed; > + > +/** > + * struct ixxat_usb_dev_info_res IXXAT device info respond block > + * @dal_res: respond block > + * @info: Device information > + * > + * Device response to the device information request > + */ > +struct ixxat_usb_dev_info_res { > + struct ixxat_usb_dal_res dal_res; > + struct ixxat_usb_dev_info info; > +} __packed; > + > +/** > + * struct ixxat_usb_adapter USB network descriptor > + * @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) > + * > + * Network descriptor for IXXAT usb to can 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_send_cmd() - Send a command to the can bus > + * @dev: USB device > + * @dal_req: Requested command > + * > + * This function sends a specific command to the device. > + * > + * Return: Error, if one occurred else 0 > + */ > +int ixxat_usb_send_cmd(struct usb_device *dev, > + struct ixxat_usb_dal_req *dal_req); > + > +/** > + * ixxat_usb_rcv_cmd() - Receive a command from the can bus > + * @dev: USB device > + * @dal_req: Responded command > + * @value: Requested values > + * > + * This function receives a specific command from the device. > + * > + * Return: Error, if one occurred else 0 > + */ > +int ixxat_usb_rcv_cmd(struct usb_device *dev, struct ixxat_usb_dal_res *dal_res, > + int value); > + > +#endif /* IXXAT_USB_CORE_H */ > Thanks, Wolfgang. -- To unsubscribe from this list: send the line "unsubscribe linux-can" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html