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> --- 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 | 425 ++++++++++ drivers/net/can/usb/ixxat_usb/ixxat_usb_cl2.c | 581 +++++++++++++ drivers/net/can/usb/ixxat_usb/ixxat_usb_core.c | 1039 ++++++++++++++++++++++++ drivers/net/can/usb/ixxat_usb/ixxat_usb_core.h | 448 ++++++++++ 7 files changed, 2513 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..14dbf9e --- /dev/null +++ b/drivers/net/can/usb/ixxat_usb/ixxat_usb_cl1.c @@ -0,0 +1,425 @@ +// 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_can_msg_cl1 { + u8 size; + u32 time; + u32 msg_id; + u32 flags; + u8 data[CAN_MAX_DLEN]; +} __packed; + +struct ixxat_usb_ctrl_init_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_handle_canmsg(struct ixxat_usb_device *dev, + struct ixxat_can_msg_cl1 *rx) +{ + struct net_device *netdev = dev->netdev; + struct can_frame *can_frame; + struct sk_buff *skb; + const u32 flags = le32_to_cpu(rx->flags); + const u8 dlcflag = (flags & IXXAT_USB_MSG_FLAGS_DLC) >> 16; + + skb = alloc_can_skb(netdev, &can_frame); + if (!skb) + return -ENOMEM; + + 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); + can_frame->can_dlc = dlcflag; + + 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->can_dlc); + + ixxat_usb_get_ts_tv(dev, le32_to_cpu(rx->time), &skb->tstamp); + + netdev->stats.rx_packets++; + netdev->stats.rx_bytes += can_frame->can_dlc; + netif_rx(skb); + + return 0; +} + +static int ixxat_usb_handle_status(struct ixxat_usb_device *dev, + struct ixxat_can_msg_cl1 *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); + } 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_cl1 *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_cl1 *can_msg; + 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; + + can_msg = (struct ixxat_can_msg_cl1 *)&data[read_size]; + if (!can_msg || !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_encode_msg(struct ixxat_usb_device *dev, + struct sk_buff *skb, u8 *obuf, + size_t *size) +{ + struct can_frame *cf = (struct can_frame *)skb->data; + struct ixxat_can_msg_cl1 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; + } + + can_msg.flags |= (cf->can_dlc << 16) & IXXAT_USB_MSG_FLAGS_DLC; + can_msg.flags = cpu_to_le32(can_msg.flags); + + can_msg.msg_id = cpu_to_le32(can_msg.msg_id); + + memcpy(can_msg.data, cf->data, cf->can_dlc); + + can_msg.size = (u8)(sizeof(can_msg) - 1 - CAN_MAX_DLEN + cf->can_dlc); + memcpy(obuf, &can_msg, can_msg.size + 1); + *size = can_msg.size + 1; + skb->data_len = *size; +} + +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_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_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; + + 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, + .decode_buf = ixxat_usb_decode_buf, + .init_ctrl = ixxat_usb_init_ctrl, + .encode_msg = ixxat_usb_encode_msg +}; 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..22eb2e4 --- /dev/null +++ b/drivers/net/can/usb/ixxat_usb/ixxat_usb_cl2.c @@ -0,0 +1,581 @@ +// 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 enpoint 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; + +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_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_handle_canmsg(struct ixxat_usb_device *dev, + struct ixxat_can_msg_cl2 *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 dlcflag = (flags & IXXAT_USB_MSG_FLAGS_DLC) >> 16; + const u8 dlc = get_canfd_dlc(dlcflag); + + 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); + + 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(dlc); + } else { + can_frame->len = 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_cl2 *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); + } 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_cl2 *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_cl2 *can_msg; + 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; + + can_msg = (struct ixxat_can_msg_cl2 *)&data[read_size]; + if (!can_msg || !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_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_cl2 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 (skb->len == CANFD_MTU) { + 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 |= (can_len2dlc(cf->len) << 16) & IXXAT_USB_MSG_FLAGS_DLC; + can_msg.flags = cpu_to_le32(can_msg.flags); + + can_msg.msg_id = cpu_to_le32(can_msg.msg_id); + + memcpy(can_msg.data, cf->data, cf->len); + + can_msg.size = (u8)(sizeof(can_msg) - 1 - CANFD_MAX_DLEN + cf->len); + memcpy(obuf, &can_msg, can_msg.size + 1); + *size = can_msg.size + 1; + skb->data_len = *size; +} + +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_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); + + 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, + .decode_buf = ixxat_usb_decode_buf, + .init_ctrl = ixxat_usb_init_ctrl, + .encode_msg = ixxat_usb_encode_msg +}; + +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, + .decode_buf = ixxat_usb_decode_buf, + .init_ctrl = ixxat_usb_init_ctrl, + .encode_msg = ixxat_usb_encode_msg +}; 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..9e94a19 --- /dev/null +++ b/drivers/net/can/usb/ixxat_usb/ixxat_usb_core.c @@ -0,0 +1,1039 @@ +// 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); + 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); + size += sizeof(struct ixxat_usb_dal_res); + + buf = kmalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + memcpy(buf, (u8 *)dal_req, size); + + 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); + + 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; + + 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); + 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; + + 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; + + 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; + 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; + 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; + 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; + 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); + + 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 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) + if (adapter->decode_buf) + ret = adapter->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 netdev_tx_t ixxat_usb_start_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + struct ixxat_usb_device *dev = netdev_priv(netdev); + const struct ixxat_usb_adapter *adapter = dev->adapter; + 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 = 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; + + adapter->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"); + 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); + 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; + } + + 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..c3bce32 --- /dev/null +++ b/drivers/net/can/usb/ixxat_usb/ixxat_usb_core.h @@ -0,0 +1,448 @@ +/* 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_MSG_FLAGS_DLC 0x000F0000 +#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_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 (*decode_buf)(struct urb *urb); + int (*init_ctrl)(struct ixxat_usb_device *dev); + void (*encode_msg)(struct ixxat_usb_device *dev, + struct sk_buff *skb, u8 *obuf, + size_t *size); +}; + +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); + +/** + * ixxat_usb_get_ts_tv() - Get ktime timestamp from device timestamp + * @dev: USB device + * @ts: device timestamp + * @k_time: ktime timestamp + * + * This function gets a ktime timestamp from a device timestamp. + * + * Return: Error, if one occurred else 0 + */ +void ixxat_usb_get_ts_tv(struct ixxat_usb_device *dev, u32 ts, ktime_t *k_time); + +#endif /* IXXAT_USB_CORE_H */ -- 2.7.4 -- 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