diff options
Diffstat (limited to 'drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c')
-rw-r--r-- | drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c | 2028 |
1 files changed, 2028 insertions, 0 deletions
diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c new file mode 100644 index 000000000000..c084bae5ec0a --- /dev/null +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c @@ -0,0 +1,2028 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Parts of this driver are based on the following: + * - Kvaser linux mhydra driver (version 5.24) + * - CAN driver for esd CAN-USB/2 + * + * Copyright (C) 2018 KVASER AB, Sweden. All rights reserved. + * Copyright (C) 2010 Matthias Fuchs <matthias.fuchs@esd.eu>, esd gmbh + * + * Known issues: + * - Transition from CAN_STATE_ERROR_WARNING to CAN_STATE_ERROR_ACTIVE is only + * reported after a call to do_get_berr_counter(), since firmware does not + * distinguish between ERROR_WARNING and ERROR_ACTIVE. + * - Hardware timestamps are not set for CAN Tx frames. + */ + +#include <linux/completion.h> +#include <linux/device.h> +#include <linux/gfp.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/netdevice.h> +#include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/usb.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> +#include <linux/can/netlink.h> + +#include "kvaser_usb.h" + +/* Forward declarations */ +static const struct kvaser_usb_dev_cfg kvaser_usb_hydra_dev_cfg_kcan; +static const struct kvaser_usb_dev_cfg kvaser_usb_hydra_dev_cfg_flexc; + +#define KVASER_USB_HYDRA_BULK_EP_IN_ADDR 0x82 +#define KVASER_USB_HYDRA_BULK_EP_OUT_ADDR 0x02 + +#define KVASER_USB_HYDRA_MAX_TRANSID 0xff +#define KVASER_USB_HYDRA_MIN_TRANSID 0x01 + +/* Minihydra command IDs */ +#define CMD_SET_BUSPARAMS_REQ 16 +#define CMD_GET_CHIP_STATE_REQ 19 +#define CMD_CHIP_STATE_EVENT 20 +#define CMD_SET_DRIVERMODE_REQ 21 +#define CMD_START_CHIP_REQ 26 +#define CMD_START_CHIP_RESP 27 +#define CMD_STOP_CHIP_REQ 28 +#define CMD_STOP_CHIP_RESP 29 +#define CMD_TX_CAN_MESSAGE 33 +#define CMD_GET_CARD_INFO_REQ 34 +#define CMD_GET_CARD_INFO_RESP 35 +#define CMD_GET_SOFTWARE_INFO_REQ 38 +#define CMD_GET_SOFTWARE_INFO_RESP 39 +#define CMD_ERROR_EVENT 45 +#define CMD_FLUSH_QUEUE 48 +#define CMD_TX_ACKNOWLEDGE 50 +#define CMD_FLUSH_QUEUE_RESP 66 +#define CMD_SET_BUSPARAMS_FD_REQ 69 +#define CMD_SET_BUSPARAMS_FD_RESP 70 +#define CMD_SET_BUSPARAMS_RESP 85 +#define CMD_GET_CAPABILITIES_REQ 95 +#define CMD_GET_CAPABILITIES_RESP 96 +#define CMD_RX_MESSAGE 106 +#define CMD_MAP_CHANNEL_REQ 200 +#define CMD_MAP_CHANNEL_RESP 201 +#define CMD_GET_SOFTWARE_DETAILS_REQ 202 +#define CMD_GET_SOFTWARE_DETAILS_RESP 203 +#define CMD_EXTENDED 255 + +/* Minihydra extended command IDs */ +#define CMD_TX_CAN_MESSAGE_FD 224 +#define CMD_TX_ACKNOWLEDGE_FD 225 +#define CMD_RX_MESSAGE_FD 226 + +/* Hydra commands are handled by different threads in firmware. + * The threads are denoted hydra entity (HE). Each HE got a unique 6-bit + * address. The address is used in hydra commands to get/set source and + * destination HE. There are two predefined HE addresses, the remaining + * addresses are different between devices and firmware versions. Hence, we need + * to enumerate the addresses (see kvaser_usb_hydra_map_channel()). + */ + +/* Well-known HE addresses */ +#define KVASER_USB_HYDRA_HE_ADDRESS_ROUTER 0x00 +#define KVASER_USB_HYDRA_HE_ADDRESS_ILLEGAL 0x3e + +#define KVASER_USB_HYDRA_TRANSID_CANHE 0x40 +#define KVASER_USB_HYDRA_TRANSID_SYSDBG 0x61 + +struct kvaser_cmd_map_ch_req { + char name[16]; + u8 channel; + u8 reserved[11]; +} __packed; + +struct kvaser_cmd_map_ch_res { + u8 he_addr; + u8 channel; + u8 reserved[26]; +} __packed; + +struct kvaser_cmd_card_info { + __le32 serial_number; + __le32 clock_res; + __le32 mfg_date; + __le32 ean[2]; + u8 hw_version; + u8 usb_mode; + u8 hw_type; + u8 reserved0; + u8 nchannels; + u8 reserved1[3]; +} __packed; + +struct kvaser_cmd_sw_info { + u8 reserved0[8]; + __le16 max_outstanding_tx; + u8 reserved1[18]; +} __packed; + +struct kvaser_cmd_sw_detail_req { + u8 use_ext_cmd; + u8 reserved[27]; +} __packed; + +/* Software detail flags */ +#define KVASER_USB_HYDRA_SW_FLAG_FW_BETA BIT(2) +#define KVASER_USB_HYDRA_SW_FLAG_FW_BAD BIT(4) +#define KVASER_USB_HYDRA_SW_FLAG_FREQ_80M BIT(5) +#define KVASER_USB_HYDRA_SW_FLAG_EXT_CMD BIT(9) +#define KVASER_USB_HYDRA_SW_FLAG_CANFD BIT(10) +#define KVASER_USB_HYDRA_SW_FLAG_NONISO BIT(11) +#define KVASER_USB_HYDRA_SW_FLAG_EXT_CAP BIT(12) +struct kvaser_cmd_sw_detail_res { + __le32 sw_flags; + __le32 sw_version; + __le32 sw_name; + __le32 ean[2]; + __le32 max_bitrate; + u8 reserved[4]; +} __packed; + +/* Sub commands for cap_req and cap_res */ +#define KVASER_USB_HYDRA_CAP_CMD_LISTEN_MODE 0x02 +#define KVASER_USB_HYDRA_CAP_CMD_ERR_REPORT 0x05 +#define KVASER_USB_HYDRA_CAP_CMD_ONE_SHOT 0x06 +struct kvaser_cmd_cap_req { + __le16 cap_cmd; + u8 reserved[26]; +} __packed; + +/* Status codes for cap_res */ +#define KVASER_USB_HYDRA_CAP_STAT_OK 0x00 +#define KVASER_USB_HYDRA_CAP_STAT_NOT_IMPL 0x01 +#define KVASER_USB_HYDRA_CAP_STAT_UNAVAIL 0x02 +struct kvaser_cmd_cap_res { + __le16 cap_cmd; + __le16 status; + __le32 mask; + __le32 value; + u8 reserved[16]; +} __packed; + +/* CMD_ERROR_EVENT error codes */ +#define KVASER_USB_HYDRA_ERROR_EVENT_CAN 0x01 +#define KVASER_USB_HYDRA_ERROR_EVENT_PARAM 0x09 +struct kvaser_cmd_error_event { + __le16 timestamp[3]; + u8 reserved; + u8 error_code; + __le16 info1; + __le16 info2; +} __packed; + +/* Chip state status flags. Used for chip_state_event and err_frame_data. */ +#define KVASER_USB_HYDRA_BUS_ERR_ACT 0x00 +#define KVASER_USB_HYDRA_BUS_ERR_PASS BIT(5) +#define KVASER_USB_HYDRA_BUS_BUS_OFF BIT(6) +struct kvaser_cmd_chip_state_event { + __le16 timestamp[3]; + u8 tx_err_counter; + u8 rx_err_counter; + u8 bus_status; + u8 reserved[19]; +} __packed; + +/* Busparam modes */ +#define KVASER_USB_HYDRA_BUS_MODE_CAN 0x00 +#define KVASER_USB_HYDRA_BUS_MODE_CANFD_ISO 0x01 +#define KVASER_USB_HYDRA_BUS_MODE_NONISO 0x02 +struct kvaser_cmd_set_busparams { + __le32 bitrate; + u8 tseg1; + u8 tseg2; + u8 sjw; + u8 nsamples; + u8 reserved0[4]; + __le32 bitrate_d; + u8 tseg1_d; + u8 tseg2_d; + u8 sjw_d; + u8 nsamples_d; + u8 canfd_mode; + u8 reserved1[7]; +} __packed; + +/* Ctrl modes */ +#define KVASER_USB_HYDRA_CTRLMODE_NORMAL 0x01 +#define KVASER_USB_HYDRA_CTRLMODE_LISTEN 0x02 +struct kvaser_cmd_set_ctrlmode { + u8 mode; + u8 reserved[27]; +} __packed; + +struct kvaser_err_frame_data { + u8 bus_status; + u8 reserved0; + u8 tx_err_counter; + u8 rx_err_counter; + u8 reserved1[4]; +} __packed; + +struct kvaser_cmd_rx_can { + u8 cmd_len; + u8 cmd_no; + u8 channel; + u8 flags; + __le16 timestamp[3]; + u8 dlc; + u8 padding; + __le32 id; + union { + u8 data[8]; + struct kvaser_err_frame_data err_frame_data; + }; +} __packed; + +/* Extended CAN ID flag. Used in rx_can and tx_can */ +#define KVASER_USB_HYDRA_EXTENDED_FRAME_ID BIT(31) +struct kvaser_cmd_tx_can { + __le32 id; + u8 data[8]; + u8 dlc; + u8 flags; + __le16 transid; + u8 channel; + u8 reserved[11]; +} __packed; + +struct kvaser_cmd_header { + u8 cmd_no; + /* The destination HE address is stored in 0..5 of he_addr. + * The upper part of source HE address is stored in 6..7 of he_addr, and + * the lower part is stored in 12..15 of transid. + */ + u8 he_addr; + __le16 transid; +} __packed; + +struct kvaser_cmd { + struct kvaser_cmd_header header; + union { + struct kvaser_cmd_map_ch_req map_ch_req; + struct kvaser_cmd_map_ch_res map_ch_res; + + struct kvaser_cmd_card_info card_info; + struct kvaser_cmd_sw_info sw_info; + struct kvaser_cmd_sw_detail_req sw_detail_req; + struct kvaser_cmd_sw_detail_res sw_detail_res; + + struct kvaser_cmd_cap_req cap_req; + struct kvaser_cmd_cap_res cap_res; + + struct kvaser_cmd_error_event error_event; + + struct kvaser_cmd_set_busparams set_busparams_req; + + struct kvaser_cmd_chip_state_event chip_state_event; + + struct kvaser_cmd_set_ctrlmode set_ctrlmode; + + struct kvaser_cmd_rx_can rx_can; + struct kvaser_cmd_tx_can tx_can; + } __packed; +} __packed; + +/* CAN frame flags. Used in rx_can, ext_rx_can, tx_can and ext_tx_can */ +#define KVASER_USB_HYDRA_CF_FLAG_ERROR_FRAME BIT(0) +#define KVASER_USB_HYDRA_CF_FLAG_OVERRUN BIT(1) +#define KVASER_USB_HYDRA_CF_FLAG_REMOTE_FRAME BIT(4) +#define KVASER_USB_HYDRA_CF_FLAG_EXTENDED_ID BIT(5) +/* CAN frame flags. Used in ext_rx_can and ext_tx_can */ +#define KVASER_USB_HYDRA_CF_FLAG_OSM_NACK BIT(12) +#define KVASER_USB_HYDRA_CF_FLAG_ABL BIT(13) +#define KVASER_USB_HYDRA_CF_FLAG_FDF BIT(16) +#define KVASER_USB_HYDRA_CF_FLAG_BRS BIT(17) +#define KVASER_USB_HYDRA_CF_FLAG_ESI BIT(18) + +/* KCAN packet header macros. Used in ext_rx_can and ext_tx_can */ +#define KVASER_USB_KCAN_DATA_DLC_BITS 4 +#define KVASER_USB_KCAN_DATA_DLC_SHIFT 8 +#define KVASER_USB_KCAN_DATA_DLC_MASK \ + GENMASK(KVASER_USB_KCAN_DATA_DLC_BITS - 1 + \ + KVASER_USB_KCAN_DATA_DLC_SHIFT, \ + KVASER_USB_KCAN_DATA_DLC_SHIFT) + +#define KVASER_USB_KCAN_DATA_BRS BIT(14) +#define KVASER_USB_KCAN_DATA_FDF BIT(15) +#define KVASER_USB_KCAN_DATA_OSM BIT(16) +#define KVASER_USB_KCAN_DATA_AREQ BIT(31) +#define KVASER_USB_KCAN_DATA_SRR BIT(31) +#define KVASER_USB_KCAN_DATA_RTR BIT(29) +#define KVASER_USB_KCAN_DATA_IDE BIT(30) +struct kvaser_cmd_ext_rx_can { + __le32 flags; + __le32 id; + __le32 kcan_id; + __le32 kcan_header; + __le64 timestamp; + union { + u8 kcan_payload[64]; + struct kvaser_err_frame_data err_frame_data; + }; +} __packed; + +struct kvaser_cmd_ext_tx_can { + __le32 flags; + __le32 id; + __le32 kcan_id; + __le32 kcan_header; + u8 databytes; + u8 dlc; + u8 reserved[6]; + u8 kcan_payload[64]; +} __packed; + +struct kvaser_cmd_ext_tx_ack { + __le32 flags; + u8 reserved0[4]; + __le64 timestamp; + u8 reserved1[8]; +} __packed; + +/* struct for extended commands (CMD_EXTENDED) */ +struct kvaser_cmd_ext { + struct kvaser_cmd_header header; + __le16 len; + u8 cmd_no_ext; + u8 reserved; + + union { + struct kvaser_cmd_ext_rx_can rx_can; + struct kvaser_cmd_ext_tx_can tx_can; + struct kvaser_cmd_ext_tx_ack tx_ack; + } __packed; +} __packed; + +static const struct can_bittiming_const kvaser_usb_hydra_kcan_bittiming_c = { + .name = "kvaser_usb_kcan", + .tseg1_min = 1, + .tseg1_max = 255, + .tseg2_min = 1, + .tseg2_max = 32, + .sjw_max = 16, + .brp_min = 1, + .brp_max = 4096, + .brp_inc = 1, +}; + +static const struct can_bittiming_const kvaser_usb_hydra_flexc_bittiming_c = { + .name = "kvaser_usb_flex", + .tseg1_min = 4, + .tseg1_max = 16, + .tseg2_min = 2, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 256, + .brp_inc = 1, +}; + +#define KVASER_USB_HYDRA_TRANSID_BITS 12 +#define KVASER_USB_HYDRA_TRANSID_MASK \ + GENMASK(KVASER_USB_HYDRA_TRANSID_BITS - 1, 0) +#define KVASER_USB_HYDRA_HE_ADDR_SRC_MASK GENMASK(7, 6) +#define KVASER_USB_HYDRA_HE_ADDR_DEST_MASK GENMASK(5, 0) +#define KVASER_USB_HYDRA_HE_ADDR_SRC_BITS 2 +static inline u16 kvaser_usb_hydra_get_cmd_transid(const struct kvaser_cmd *cmd) +{ + return le16_to_cpu(cmd->header.transid) & KVASER_USB_HYDRA_TRANSID_MASK; +} + +static inline void kvaser_usb_hydra_set_cmd_transid(struct kvaser_cmd *cmd, + u16 transid) +{ + cmd->header.transid = + cpu_to_le16(transid & KVASER_USB_HYDRA_TRANSID_MASK); +} + +static inline u8 kvaser_usb_hydra_get_cmd_src_he(const struct kvaser_cmd *cmd) +{ + return (cmd->header.he_addr & KVASER_USB_HYDRA_HE_ADDR_SRC_MASK) >> + KVASER_USB_HYDRA_HE_ADDR_SRC_BITS | + le16_to_cpu(cmd->header.transid) >> + KVASER_USB_HYDRA_TRANSID_BITS; +} + +static inline void kvaser_usb_hydra_set_cmd_dest_he(struct kvaser_cmd *cmd, + u8 dest_he) +{ + cmd->header.he_addr = + (cmd->header.he_addr & KVASER_USB_HYDRA_HE_ADDR_SRC_MASK) | + (dest_he & KVASER_USB_HYDRA_HE_ADDR_DEST_MASK); +} + +static u8 kvaser_usb_hydra_channel_from_cmd(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + int i; + u8 channel = 0xff; + u8 src_he = kvaser_usb_hydra_get_cmd_src_he(cmd); + + for (i = 0; i < KVASER_USB_MAX_NET_DEVICES; i++) { + if (dev->card_data.hydra.channel_to_he[i] == src_he) { + channel = i; + break; + } + } + + return channel; +} + +static u16 kvaser_usb_hydra_get_next_transid(struct kvaser_usb *dev) +{ + unsigned long flags; + u16 transid; + struct kvaser_usb_dev_card_data_hydra *card_data = + &dev->card_data.hydra; + + spin_lock_irqsave(&card_data->transid_lock, flags); + transid = card_data->transid; + if (transid >= KVASER_USB_HYDRA_MAX_TRANSID) + transid = KVASER_USB_HYDRA_MIN_TRANSID; + else + transid++; + card_data->transid = transid; + spin_unlock_irqrestore(&card_data->transid_lock, flags); + + return transid; +} + +static size_t kvaser_usb_hydra_cmd_size(struct kvaser_cmd *cmd) +{ + size_t ret; + + if (cmd->header.cmd_no == CMD_EXTENDED) + ret = le16_to_cpu(((struct kvaser_cmd_ext *)cmd)->len); + else + ret = sizeof(struct kvaser_cmd); + + return ret; +} + +static struct kvaser_usb_net_priv * +kvaser_usb_hydra_net_priv_from_cmd(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_net_priv *priv = NULL; + u8 channel = kvaser_usb_hydra_channel_from_cmd(dev, cmd); + + if (channel >= dev->nchannels) + dev_err(&dev->intf->dev, + "Invalid channel number (%d)\n", channel); + else + priv = dev->nets[channel]; + + return priv; +} + +static ktime_t +kvaser_usb_hydra_ktime_from_rx_cmd(const struct kvaser_usb_dev_cfg *cfg, + const struct kvaser_cmd *cmd) +{ + u64 ticks; + + if (cmd->header.cmd_no == CMD_EXTENDED) { + struct kvaser_cmd_ext *cmd_ext = (struct kvaser_cmd_ext *)cmd; + + ticks = le64_to_cpu(cmd_ext->rx_can.timestamp); + } else { + ticks = le16_to_cpu(cmd->rx_can.timestamp[0]); + ticks += (u64)(le16_to_cpu(cmd->rx_can.timestamp[1])) << 16; + ticks += (u64)(le16_to_cpu(cmd->rx_can.timestamp[2])) << 32; + } + + return ns_to_ktime(div_u64(ticks * 1000, cfg->timestamp_freq)); +} + +static int kvaser_usb_hydra_send_simple_cmd(struct kvaser_usb *dev, + u8 cmd_no, int channel) +{ + struct kvaser_cmd *cmd; + int err; + + cmd = kcalloc(1, sizeof(struct kvaser_cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->header.cmd_no = cmd_no; + if (channel < 0) { + kvaser_usb_hydra_set_cmd_dest_he + (cmd, KVASER_USB_HYDRA_HE_ADDRESS_ILLEGAL); + } else { + if (channel >= KVASER_USB_MAX_NET_DEVICES) { + dev_err(&dev->intf->dev, "channel (%d) out of range.\n", + channel); + err = -EINVAL; + goto end; + } + kvaser_usb_hydra_set_cmd_dest_he + (cmd, dev->card_data.hydra.channel_to_he[channel]); + } + kvaser_usb_hydra_set_cmd_transid + (cmd, kvaser_usb_hydra_get_next_transid(dev)); + + err = kvaser_usb_send_cmd(dev, cmd, kvaser_usb_hydra_cmd_size(cmd)); + if (err) + goto end; + +end: + kfree(cmd); + + return err; +} + +static int +kvaser_usb_hydra_send_simple_cmd_async(struct kvaser_usb_net_priv *priv, + u8 cmd_no) +{ + struct kvaser_cmd *cmd; + struct kvaser_usb *dev = priv->dev; + int err; + + cmd = kcalloc(1, sizeof(struct kvaser_cmd), GFP_ATOMIC); + if (!cmd) + return -ENOMEM; + + cmd->header.cmd_no = cmd_no; + + kvaser_usb_hydra_set_cmd_dest_he + (cmd, dev->card_data.hydra.channel_to_he[priv->channel]); + kvaser_usb_hydra_set_cmd_transid + (cmd, kvaser_usb_hydra_get_next_transid(dev)); + + err = kvaser_usb_send_cmd_async(priv, cmd, + kvaser_usb_hydra_cmd_size(cmd)); + if (err) + kfree(cmd); + + return err; +} + +/* This function is used for synchronously waiting on hydra control commands. + * Note: Compared to kvaser_usb_hydra_read_bulk_callback(), we never need to + * handle partial hydra commands. Since hydra control commands are always + * non-extended commands. + */ +static int kvaser_usb_hydra_wait_cmd(const struct kvaser_usb *dev, u8 cmd_no, + struct kvaser_cmd *cmd) +{ + void *buf; + int err; + unsigned long timeout = jiffies + msecs_to_jiffies(KVASER_USB_TIMEOUT); + + if (cmd->header.cmd_no == CMD_EXTENDED) { + dev_err(&dev->intf->dev, "Wait for CMD_EXTENDED not allowed\n"); + return -EINVAL; + } + + buf = kzalloc(KVASER_USB_RX_BUFFER_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + do { + int actual_len = 0; + int pos = 0; + + err = kvaser_usb_recv_cmd(dev, buf, KVASER_USB_RX_BUFFER_SIZE, + &actual_len); + if (err < 0) + goto end; + + while (pos < actual_len) { + struct kvaser_cmd *tmp_cmd; + size_t cmd_len; + + tmp_cmd = buf + pos; + cmd_len = kvaser_usb_hydra_cmd_size(tmp_cmd); + if (pos + cmd_len > actual_len) { + dev_err_ratelimited(&dev->intf->dev, + "Format error\n"); + break; + } + + if (tmp_cmd->header.cmd_no == cmd_no) { + memcpy(cmd, tmp_cmd, cmd_len); + goto end; + } + pos += cmd_len; + } + } while (time_before(jiffies, timeout)); + + err = -EINVAL; + +end: + kfree(buf); + + return err; +} + +static int kvaser_usb_hydra_map_channel_resp(struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + u8 he, channel; + u16 transid = kvaser_usb_hydra_get_cmd_transid(cmd); + struct kvaser_usb_dev_card_data_hydra *card_data = + &dev->card_data.hydra; + + if (transid > 0x007f || transid < 0x0040) { + dev_err(&dev->intf->dev, + "CMD_MAP_CHANNEL_RESP, invalid transid: 0x%x\n", + transid); + return -EINVAL; + } + + switch (transid) { + case KVASER_USB_HYDRA_TRANSID_CANHE: + case KVASER_USB_HYDRA_TRANSID_CANHE + 1: + case KVASER_USB_HYDRA_TRANSID_CANHE + 2: + case KVASER_USB_HYDRA_TRANSID_CANHE + 3: + case KVASER_USB_HYDRA_TRANSID_CANHE + 4: + channel = transid & 0x000f; + he = cmd->map_ch_res.he_addr; + card_data->channel_to_he[channel] = he; + break; + case KVASER_USB_HYDRA_TRANSID_SYSDBG: + card_data->sysdbg_he = cmd->map_ch_res.he_addr; + break; + default: + dev_warn(&dev->intf->dev, + "Unknown CMD_MAP_CHANNEL_RESP transid=0x%x\n", + transid); + break; + } + + return 0; +} + +static int kvaser_usb_hydra_map_channel(struct kvaser_usb *dev, u16 transid, + u8 channel, const char *name) +{ + struct kvaser_cmd *cmd; + int err; + + cmd = kcalloc(1, sizeof(struct kvaser_cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + strcpy(cmd->map_ch_req.name, name); + cmd->header.cmd_no = CMD_MAP_CHANNEL_REQ; + kvaser_usb_hydra_set_cmd_dest_he + (cmd, KVASER_USB_HYDRA_HE_ADDRESS_ROUTER); + cmd->map_ch_req.channel = channel; + + kvaser_usb_hydra_set_cmd_transid(cmd, transid); + + err = kvaser_usb_send_cmd(dev, cmd, kvaser_usb_hydra_cmd_size(cmd)); + if (err) + goto end; + + err = kvaser_usb_hydra_wait_cmd(dev, CMD_MAP_CHANNEL_RESP, cmd); + if (err) + goto end; + + err = kvaser_usb_hydra_map_channel_resp(dev, cmd); + if (err) + goto end; + +end: + kfree(cmd); + + return err; +} + +static int kvaser_usb_hydra_get_single_capability(struct kvaser_usb *dev, + u16 cap_cmd_req, u16 *status) +{ + struct kvaser_usb_dev_card_data *card_data = &dev->card_data; + struct kvaser_cmd *cmd; + u32 value = 0; + u32 mask = 0; + u16 cap_cmd_res; + int err; + int i; + + cmd = kcalloc(1, sizeof(struct kvaser_cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->header.cmd_no = CMD_GET_CAPABILITIES_REQ; + cmd->cap_req.cap_cmd = cpu_to_le16(cap_cmd_req); + + kvaser_usb_hydra_set_cmd_dest_he(cmd, card_data->hydra.sysdbg_he); + kvaser_usb_hydra_set_cmd_transid + (cmd, kvaser_usb_hydra_get_next_transid(dev)); + + err = kvaser_usb_send_cmd(dev, cmd, kvaser_usb_hydra_cmd_size(cmd)); + if (err) + goto end; + + err = kvaser_usb_hydra_wait_cmd(dev, CMD_GET_CAPABILITIES_RESP, cmd); + if (err) + goto end; + + *status = le16_to_cpu(cmd->cap_res.status); + + if (*status != KVASER_USB_HYDRA_CAP_STAT_OK) + goto end; + + cap_cmd_res = le16_to_cpu(cmd->cap_res.cap_cmd); + switch (cap_cmd_res) { + case KVASER_USB_HYDRA_CAP_CMD_LISTEN_MODE: + case KVASER_USB_HYDRA_CAP_CMD_ERR_REPORT: + case KVASER_USB_HYDRA_CAP_CMD_ONE_SHOT: + value = le32_to_cpu(cmd->cap_res.value); + mask = le32_to_cpu(cmd->cap_res.mask); + break; + default: + dev_warn(&dev->intf->dev, "Unknown capability command %u\n", + cap_cmd_res); + break; + } + + for (i = 0; i < dev->nchannels; i++) { + if (BIT(i) & (value & mask)) { + switch (cap_cmd_res) { + case KVASER_USB_HYDRA_CAP_CMD_LISTEN_MODE: + card_data->ctrlmode_supported |= + CAN_CTRLMODE_LISTENONLY; + break; + case KVASER_USB_HYDRA_CAP_CMD_ERR_REPORT: + card_data->capabilities |= + KVASER_USB_CAP_BERR_CAP; + break; + case KVASER_USB_HYDRA_CAP_CMD_ONE_SHOT: + card_data->ctrlmode_supported |= + CAN_CTRLMODE_ONE_SHOT; + break; + } + } + } + +end: + kfree(cmd); + + return err; +} + +static void kvaser_usb_hydra_start_chip_reply(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_net_priv *priv; + + priv = kvaser_usb_hydra_net_priv_from_cmd(dev, cmd); + if (!priv) + return; + + if (completion_done(&priv->start_comp) && + netif_queue_stopped(priv->netdev)) { + netif_wake_queue(priv->netdev); + } else { + netif_start_queue(priv->netdev); + complete(&priv->start_comp); + } +} + +static void kvaser_usb_hydra_stop_chip_reply(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_net_priv *priv; + + priv = kvaser_usb_hydra_net_priv_from_cmd(dev, cmd); + if (!priv) + return; + + complete(&priv->stop_comp); +} + +static void kvaser_usb_hydra_flush_queue_reply(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_net_priv *priv; + + priv = kvaser_usb_hydra_net_priv_from_cmd(dev, cmd); + if (!priv) + return; + + complete(&priv->flush_comp); +} + +static void +kvaser_usb_hydra_bus_status_to_can_state(const struct kvaser_usb_net_priv *priv, + u8 bus_status, + const struct can_berr_counter *bec, + enum can_state *new_state) +{ + if (bus_status & KVASER_USB_HYDRA_BUS_BUS_OFF) { + *new_state = CAN_STATE_BUS_OFF; + } else if (bus_status & KVASER_USB_HYDRA_BUS_ERR_PASS) { + *new_state = CAN_STATE_ERROR_PASSIVE; + } else if (bus_status == KVASER_USB_HYDRA_BUS_ERR_ACT) { + if (bec->txerr >= 128 || bec->rxerr >= 128) { + netdev_warn(priv->netdev, + "ERR_ACTIVE but err tx=%u or rx=%u >=128\n", + bec->txerr, bec->rxerr); + *new_state = CAN_STATE_ERROR_PASSIVE; + } else if (bec->txerr >= 96 || bec->rxerr >= 96) { + *new_state = CAN_STATE_ERROR_WARNING; + } else { + *new_state = CAN_STATE_ERROR_ACTIVE; + } + } +} + +static void kvaser_usb_hydra_update_state(struct kvaser_usb_net_priv *priv, + u8 bus_status, + const struct can_berr_counter *bec) +{ + struct net_device *netdev = priv->netdev; + struct can_frame *cf; + struct sk_buff *skb; + struct net_device_stats *stats; + enum can_state new_state, old_state; + + old_state = priv->can.state; + + kvaser_usb_hydra_bus_status_to_can_state(priv, bus_status, bec, + &new_state); + + if (new_state == old_state) + return; + + /* Ignore state change if previous state was STOPPED and the new state + * is BUS_OFF. Firmware always report this as BUS_OFF, since firmware + * does not distinguish between BUS_OFF and STOPPED. + */ + if (old_state == CAN_STATE_STOPPED && new_state == CAN_STATE_BUS_OFF) + return; + + skb = alloc_can_err_skb(netdev, &cf); + if (skb) { + enum can_state tx_state, rx_state; + + tx_state = (bec->txerr >= bec->rxerr) ? + new_state : CAN_STATE_ERROR_ACTIVE; + rx_state = (bec->txerr <= bec->rxerr) ? + new_state : CAN_STATE_ERROR_ACTIVE; + can_change_state(netdev, cf, tx_state, rx_state); + } + + if (new_state == CAN_STATE_BUS_OFF && old_state < CAN_STATE_BUS_OFF) { + if (!priv->can.restart_ms) + kvaser_usb_hydra_send_simple_cmd_async + (priv, CMD_STOP_CHIP_REQ); + + can_bus_off(netdev); + } + + if (!skb) { + netdev_warn(netdev, "No memory left for err_skb\n"); + return; + } + + if (priv->can.restart_ms && + old_state >= CAN_STATE_BUS_OFF && + new_state < CAN_STATE_BUS_OFF) + priv->can.can_stats.restarts++; + + cf->data[6] = bec->txerr; + cf->data[7] = bec->rxerr; + + stats = &netdev->stats; + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); +} + +static void kvaser_usb_hydra_state_event(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_net_priv *priv; + struct can_berr_counter bec; + u8 bus_status; + + priv = kvaser_usb_hydra_net_priv_from_cmd(dev, cmd); + if (!priv) + return; + + bus_status = cmd->chip_state_event.bus_status; + bec.txerr = cmd->chip_state_event.tx_err_counter; + bec.rxerr = cmd->chip_state_event.rx_err_counter; + + kvaser_usb_hydra_update_state(priv, bus_status, &bec); + priv->bec.txerr = bec.txerr; + priv->bec.rxerr = bec.rxerr; +} + +static void kvaser_usb_hydra_error_event_parameter(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + /* info1 will contain the offending cmd_no */ + switch (le16_to_cpu(cmd->error_event.info1)) { + case CMD_START_CHIP_REQ: + dev_warn(&dev->intf->dev, + "CMD_START_CHIP_REQ error in parameter\n"); + break; + + case CMD_STOP_CHIP_REQ: + dev_warn(&dev->intf->dev, + "CMD_STOP_CHIP_REQ error in parameter\n"); + break; + + case CMD_FLUSH_QUEUE: + dev_warn(&dev->intf->dev, + "CMD_FLUSH_QUEUE error in parameter\n"); + break; + + case CMD_SET_BUSPARAMS_REQ: + dev_warn(&dev->intf->dev, + "Set bittiming failed. Error in parameter\n"); + break; + + case CMD_SET_BUSPARAMS_FD_REQ: + dev_warn(&dev->intf->dev, + "Set data bittiming failed. Error in parameter\n"); + break; + + default: + dev_warn(&dev->intf->dev, + "Unhandled parameter error event cmd_no (%u)\n", + le16_to_cpu(cmd->error_event.info1)); + break; + } +} + +static void kvaser_usb_hydra_error_event(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + switch (cmd->error_event.error_code) { + case KVASER_USB_HYDRA_ERROR_EVENT_PARAM: + kvaser_usb_hydra_error_event_parameter(dev, cmd); + break; + + case KVASER_USB_HYDRA_ERROR_EVENT_CAN: + /* Wrong channel mapping?! This should never happen! + * info1 will contain the offending cmd_no + */ + dev_err(&dev->intf->dev, + "Received CAN error event for cmd_no (%u)\n", + le16_to_cpu(cmd->error_event.info1)); + break; + + default: + dev_warn(&dev->intf->dev, + "Unhandled error event (%d)\n", + cmd->error_event.error_code); + break; + } +} + +static void +kvaser_usb_hydra_error_frame(struct kvaser_usb_net_priv *priv, + const struct kvaser_err_frame_data *err_frame_data, + ktime_t hwtstamp) +{ + struct net_device *netdev = priv->netdev; + struct net_device_stats *stats = &netdev->stats; + struct can_frame *cf; + struct sk_buff *skb; + struct skb_shared_hwtstamps *shhwtstamps; + struct can_berr_counter bec; + enum can_state new_state, old_state; + u8 bus_status; + + priv->can.can_stats.bus_error++; + stats->rx_errors++; + + bus_status = err_frame_data->bus_status; + bec.txerr = err_frame_data->tx_err_counter; + bec.rxerr = err_frame_data->rx_err_counter; + + old_state = priv->can.state; + kvaser_usb_hydra_bus_status_to_can_state(priv, bus_status, &bec, + &new_state); + + skb = alloc_can_err_skb(netdev, &cf); + + if (new_state != old_state) { + if (skb) { + enum can_state tx_state, rx_state; + + tx_state = (bec.txerr >= bec.rxerr) ? + new_state : CAN_STATE_ERROR_ACTIVE; + rx_state = (bec.txerr <= bec.rxerr) ? + new_state : CAN_STATE_ERROR_ACTIVE; + + can_change_state(netdev, cf, tx_state, rx_state); + } + + if (new_state == CAN_STATE_BUS_OFF) { + if (!priv->can.restart_ms) + kvaser_usb_hydra_send_simple_cmd_async + (priv, CMD_STOP_CHIP_REQ); + + can_bus_off(netdev); + } + + if (priv->can.restart_ms && + old_state >= CAN_STATE_BUS_OFF && + new_state < CAN_STATE_BUS_OFF) + cf->can_id |= CAN_ERR_RESTARTED; + } + + if (!skb) { + stats->rx_dropped++; + netdev_warn(netdev, "No memory left for err_skb\n"); + return; + } + + shhwtstamps = skb_hwtstamps(skb); + shhwtstamps->hwtstamp = hwtstamp; + + cf->can_id |= CAN_ERR_BUSERROR; + cf->data[6] = bec.txerr; + cf->data[7] = bec.rxerr; + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + + priv->bec.txerr = bec.txerr; + priv->bec.rxerr = bec.rxerr; +} + +static void kvaser_usb_hydra_one_shot_fail(struct kvaser_usb_net_priv *priv, + const struct kvaser_cmd_ext *cmd) +{ + struct net_device *netdev = priv->netdev; + struct net_device_stats *stats = &netdev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u32 flags; + + skb = alloc_can_err_skb(netdev, &cf); + if (!skb) { + stats->rx_dropped++; + netdev_warn(netdev, "No memory left for err_skb\n"); + return; + } + + cf->can_id |= CAN_ERR_BUSERROR; + flags = le32_to_cpu(cmd->tx_ack.flags); + + if (flags & KVASER_USB_HYDRA_CF_FLAG_OSM_NACK) + cf->can_id |= CAN_ERR_ACK; + if (flags & KVASER_USB_HYDRA_CF_FLAG_ABL) { + cf->can_id |= CAN_ERR_LOSTARB; + priv->can.can_stats.arbitration_lost++; + } + + stats->tx_errors++; + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); +} + +static void kvaser_usb_hydra_tx_acknowledge(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_tx_urb_context *context; + struct kvaser_usb_net_priv *priv; + unsigned long irq_flags; + bool one_shot_fail = false; + u16 transid = kvaser_usb_hydra_get_cmd_transid(cmd); + + priv = kvaser_usb_hydra_net_priv_from_cmd(dev, cmd); + if (!priv) + return; + + if (!netif_device_present(priv->netdev)) + return; + + if (cmd->header.cmd_no == CMD_EXTENDED) { + struct kvaser_cmd_ext *cmd_ext = (struct kvaser_cmd_ext *)cmd; + u32 flags = le32_to_cpu(cmd_ext->tx_ack.flags); + + if (flags & (KVASER_USB_HYDRA_CF_FLAG_OSM_NACK | + KVASER_USB_HYDRA_CF_FLAG_ABL)) { + kvaser_usb_hydra_one_shot_fail(priv, cmd_ext); + one_shot_fail = true; + } + } + + context = &priv->tx_contexts[transid % dev->max_tx_urbs]; + if (!one_shot_fail) { + struct net_device_stats *stats = &priv->netdev->stats; + + stats->tx_packets++; + stats->tx_bytes += can_dlc2len(context->dlc); + } + + spin_lock_irqsave(&priv->tx_contexts_lock, irq_flags); + + can_get_echo_skb(priv->netdev, context->echo_index); + context->echo_index = dev->max_tx_urbs; + --priv->active_tx_contexts; + netif_wake_queue(priv->netdev); + + spin_unlock_irqrestore(&priv->tx_contexts_lock, irq_flags); +} + +static void kvaser_usb_hydra_rx_msg_std(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_net_priv *priv = NULL; + struct can_frame *cf; + struct sk_buff *skb; + struct skb_shared_hwtstamps *shhwtstamps; + struct net_device_stats *stats; + u8 flags; + ktime_t hwtstamp; + + priv = kvaser_usb_hydra_net_priv_from_cmd(dev, cmd); + if (!priv) + return; + + stats = &priv->netdev->stats; + + flags = cmd->rx_can.flags; + hwtstamp = kvaser_usb_hydra_ktime_from_rx_cmd(dev->cfg, cmd); + + if (flags & KVASER_USB_HYDRA_CF_FLAG_ERROR_FRAME) { + kvaser_usb_hydra_error_frame(priv, &cmd->rx_can.err_frame_data, + hwtstamp); + return; + } + + skb = alloc_can_skb(priv->netdev, &cf); + if (!skb) { + stats->rx_dropped++; + return; + } + + shhwtstamps = skb_hwtstamps(skb); + shhwtstamps->hwtstamp = hwtstamp; + + cf->can_id = le32_to_cpu(cmd->rx_can.id); + + if (cf->can_id & KVASER_USB_HYDRA_EXTENDED_FRAME_ID) { + cf->can_id &= CAN_EFF_MASK; + cf->can_id |= CAN_EFF_FLAG; + } else { + cf->can_id &= CAN_SFF_MASK; + } + + if (flags & KVASER_USB_HYDRA_CF_FLAG_OVERRUN) + kvaser_usb_can_rx_over_error(priv->netdev); + + cf->can_dlc = get_can_dlc(cmd->rx_can.dlc); + + if (flags & KVASER_USB_HYDRA_CF_FLAG_REMOTE_FRAME) + cf->can_id |= CAN_RTR_FLAG; + else + memcpy(cf->data, cmd->rx_can.data, cf->can_dlc); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); +} + +static void kvaser_usb_hydra_rx_msg_ext(const struct kvaser_usb *dev, + const struct kvaser_cmd_ext *cmd) +{ + struct kvaser_cmd *std_cmd = (struct kvaser_cmd *)cmd; + struct kvaser_usb_net_priv *priv; + struct canfd_frame *cf; + struct sk_buff *skb; + struct skb_shared_hwtstamps *shhwtstamps; + struct net_device_stats *stats; + u32 flags; + u8 dlc; + u32 kcan_header; + ktime_t hwtstamp; + + priv = kvaser_usb_hydra_net_priv_from_cmd(dev, std_cmd); + if (!priv) + return; + + stats |