can: kvaser_usb: kvaser_usb_leaf: Get capabilities from device
[ Upstream commit 35364f5b41a4917fe94a3f393d149b63ec583297 ]
Use the CMD_GET_CAPABILITIES_REQ command to query the device for certain
capabilities. We are only interested in LISTENONLY mode and wither the
device reports CAN error counters.
Fixes: 080f40a6fa
("can: kvaser_usb: Add support for Kvaser CAN/USB devices")
Reported-by: Anssi Hannula <anssi.hannula@bitwise.fi>
Tested-by: Anssi Hannula <anssi.hannula@bitwise.fi>
Signed-off-by: Jimmy Assarsson <extja@kvaser.com>
Link: https://lore.kernel.org/all/20221010185237.319219-3-extja@kvaser.com
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
parent
3a9d74f339
commit
0e56748852
@ -73,6 +73,8 @@
|
||||
#define CMD_TX_ACKNOWLEDGE 50
|
||||
#define CMD_CAN_ERROR_EVENT 51
|
||||
#define CMD_FLUSH_QUEUE_REPLY 68
|
||||
#define CMD_GET_CAPABILITIES_REQ 95
|
||||
#define CMD_GET_CAPABILITIES_RESP 96
|
||||
|
||||
#define CMD_LEAF_LOG_MESSAGE 106
|
||||
|
||||
@ -82,6 +84,8 @@
|
||||
#define KVASER_USB_LEAF_SWOPTION_FREQ_32_MHZ_CLK BIT(5)
|
||||
#define KVASER_USB_LEAF_SWOPTION_FREQ_24_MHZ_CLK BIT(6)
|
||||
|
||||
#define KVASER_USB_LEAF_SWOPTION_EXT_CAP BIT(12)
|
||||
|
||||
/* error factors */
|
||||
#define M16C_EF_ACKE BIT(0)
|
||||
#define M16C_EF_CRCE BIT(1)
|
||||
@ -277,6 +281,28 @@ struct leaf_cmd_log_message {
|
||||
u8 data[8];
|
||||
} __packed;
|
||||
|
||||
/* Sub commands for cap_req and cap_res */
|
||||
#define KVASER_USB_LEAF_CAP_CMD_LISTEN_MODE 0x02
|
||||
#define KVASER_USB_LEAF_CAP_CMD_ERR_REPORT 0x05
|
||||
struct kvaser_cmd_cap_req {
|
||||
__le16 padding0;
|
||||
__le16 cap_cmd;
|
||||
__le16 padding1;
|
||||
__le16 channel;
|
||||
} __packed;
|
||||
|
||||
/* Status codes for cap_res */
|
||||
#define KVASER_USB_LEAF_CAP_STAT_OK 0x00
|
||||
#define KVASER_USB_LEAF_CAP_STAT_NOT_IMPL 0x01
|
||||
#define KVASER_USB_LEAF_CAP_STAT_UNAVAIL 0x02
|
||||
struct kvaser_cmd_cap_res {
|
||||
__le16 padding;
|
||||
__le16 cap_cmd;
|
||||
__le16 status;
|
||||
__le32 mask;
|
||||
__le32 value;
|
||||
} __packed;
|
||||
|
||||
struct kvaser_cmd {
|
||||
u8 len;
|
||||
u8 id;
|
||||
@ -294,6 +320,8 @@ struct kvaser_cmd {
|
||||
struct leaf_cmd_chip_state_event chip_state_event;
|
||||
struct leaf_cmd_error_event error_event;
|
||||
struct leaf_cmd_log_message log_message;
|
||||
struct kvaser_cmd_cap_req cap_req;
|
||||
struct kvaser_cmd_cap_res cap_res;
|
||||
} __packed leaf;
|
||||
|
||||
union {
|
||||
@ -323,6 +351,7 @@ static const u8 kvaser_usb_leaf_cmd_sizes_leaf[] = {
|
||||
[CMD_LEAF_LOG_MESSAGE] = kvaser_fsize(u.leaf.log_message),
|
||||
[CMD_CHIP_STATE_EVENT] = kvaser_fsize(u.leaf.chip_state_event),
|
||||
[CMD_CAN_ERROR_EVENT] = kvaser_fsize(u.leaf.error_event),
|
||||
[CMD_GET_CAPABILITIES_RESP] = kvaser_fsize(u.leaf.cap_res),
|
||||
/* ignored events: */
|
||||
[CMD_FLUSH_QUEUE_REPLY] = CMD_SIZE_ANY,
|
||||
};
|
||||
@ -607,6 +636,9 @@ static void kvaser_usb_leaf_get_software_info_leaf(struct kvaser_usb *dev,
|
||||
dev->fw_version = le32_to_cpu(softinfo->fw_version);
|
||||
dev->max_tx_urbs = le16_to_cpu(softinfo->max_outstanding_tx);
|
||||
|
||||
if (sw_options & KVASER_USB_LEAF_SWOPTION_EXT_CAP)
|
||||
dev->card_data.capabilities |= KVASER_USB_CAP_EXT_CAP;
|
||||
|
||||
if (dev->driver_info->quirks & KVASER_USB_QUIRK_IGNORE_CLK_FREQ) {
|
||||
/* Firmware expects bittiming parameters calculated for 16MHz
|
||||
* clock, regardless of the actual clock
|
||||
@ -694,6 +726,116 @@ static int kvaser_usb_leaf_get_card_info(struct kvaser_usb *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kvaser_usb_leaf_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 = kzalloc(sizeof(*cmd), GFP_KERNEL);
|
||||
if (!cmd)
|
||||
return -ENOMEM;
|
||||
|
||||
cmd->id = CMD_GET_CAPABILITIES_REQ;
|
||||
cmd->u.leaf.cap_req.cap_cmd = cpu_to_le16(cap_cmd_req);
|
||||
cmd->len = CMD_HEADER_LEN + sizeof(struct kvaser_cmd_cap_req);
|
||||
|
||||
err = kvaser_usb_send_cmd(dev, cmd, cmd->len);
|
||||
if (err)
|
||||
goto end;
|
||||
|
||||
err = kvaser_usb_leaf_wait_cmd(dev, CMD_GET_CAPABILITIES_RESP, cmd);
|
||||
if (err)
|
||||
goto end;
|
||||
|
||||
*status = le16_to_cpu(cmd->u.leaf.cap_res.status);
|
||||
|
||||
if (*status != KVASER_USB_LEAF_CAP_STAT_OK)
|
||||
goto end;
|
||||
|
||||
cap_cmd_res = le16_to_cpu(cmd->u.leaf.cap_res.cap_cmd);
|
||||
switch (cap_cmd_res) {
|
||||
case KVASER_USB_LEAF_CAP_CMD_LISTEN_MODE:
|
||||
case KVASER_USB_LEAF_CAP_CMD_ERR_REPORT:
|
||||
value = le32_to_cpu(cmd->u.leaf.cap_res.value);
|
||||
mask = le32_to_cpu(cmd->u.leaf.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_LEAF_CAP_CMD_LISTEN_MODE:
|
||||
card_data->ctrlmode_supported |=
|
||||
CAN_CTRLMODE_LISTENONLY;
|
||||
break;
|
||||
case KVASER_USB_LEAF_CAP_CMD_ERR_REPORT:
|
||||
card_data->capabilities |=
|
||||
KVASER_USB_CAP_BERR_CAP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
kfree(cmd);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int kvaser_usb_leaf_get_capabilities_leaf(struct kvaser_usb *dev)
|
||||
{
|
||||
int err;
|
||||
u16 status;
|
||||
|
||||
if (!(dev->card_data.capabilities & KVASER_USB_CAP_EXT_CAP)) {
|
||||
dev_info(&dev->intf->dev,
|
||||
"No extended capability support. Upgrade device firmware.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = kvaser_usb_leaf_get_single_capability(dev,
|
||||
KVASER_USB_LEAF_CAP_CMD_LISTEN_MODE,
|
||||
&status);
|
||||
if (err)
|
||||
return err;
|
||||
if (status)
|
||||
dev_info(&dev->intf->dev,
|
||||
"KVASER_USB_LEAF_CAP_CMD_LISTEN_MODE failed %u\n",
|
||||
status);
|
||||
|
||||
err = kvaser_usb_leaf_get_single_capability(dev,
|
||||
KVASER_USB_LEAF_CAP_CMD_ERR_REPORT,
|
||||
&status);
|
||||
if (err)
|
||||
return err;
|
||||
if (status)
|
||||
dev_info(&dev->intf->dev,
|
||||
"KVASER_USB_LEAF_CAP_CMD_ERR_REPORT failed %u\n",
|
||||
status);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kvaser_usb_leaf_get_capabilities(struct kvaser_usb *dev)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (dev->driver_info->family == KVASER_LEAF)
|
||||
err = kvaser_usb_leaf_get_capabilities_leaf(dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void kvaser_usb_leaf_tx_acknowledge(const struct kvaser_usb *dev,
|
||||
const struct kvaser_cmd *cmd)
|
||||
{
|
||||
@ -1490,7 +1632,7 @@ const struct kvaser_usb_dev_ops kvaser_usb_leaf_dev_ops = {
|
||||
.dev_get_software_info = kvaser_usb_leaf_get_software_info,
|
||||
.dev_get_software_details = NULL,
|
||||
.dev_get_card_info = kvaser_usb_leaf_get_card_info,
|
||||
.dev_get_capabilities = NULL,
|
||||
.dev_get_capabilities = kvaser_usb_leaf_get_capabilities,
|
||||
.dev_set_opt_mode = kvaser_usb_leaf_set_opt_mode,
|
||||
.dev_start_chip = kvaser_usb_leaf_start_chip,
|
||||
.dev_stop_chip = kvaser_usb_leaf_stop_chip,
|
||||
|
Loading…
Reference in New Issue
Block a user