Skip to contentSkip to conversation

can: kvaser_usb: Simplify identification of physical CAN interfaces

PATCH Superseded Jimmy Assarsson 3 participants 12 messages Jul 23, 2025 – Jul 24, 2025 CAN network drivers

v1 of 4 Superseded Latest is v4 →

lore
Share & tools
lore
#0 lore raw

cover can: kvaser_usb: Simplify identification of physical CAN interfaces

This patch series simplifies the process of identifying which network interface (can0..canX) corresponds to which physical CAN channel on Kvaser USB based CAN interfaces.

Jimmy Assarsson (9):
can: kvaser_usb: Add support to control CAN LEDs on device
can: kvaser_usb: Add support for ethtool set_phys_id()
can: kvaser_usb: Add intermediate variables
can: kvaser_usb: Move comment regarding max_tx_urbs
can: kvaser_usb: Store the different firmware version components in a
struct
can: kvaser_usb: Store additional device information
can: kvaser_usb: Add devlink support
can: kvaser_usb: Expose device information via devlink info_get()
can: kvaser_usb: Add devlink port support

 drivers/net/can/usb/Kconfig                   |   1 +
 drivers/net/can/usb/kvaser_usb/Makefile       |   2 +-
 drivers/net/can/usb/kvaser_usb/kvaser_usb.h   |  33 +++-
 .../net/can/usb/kvaser_usb/kvaser_usb_core.c  | 144 +++++++++++++-----
 .../can/usb/kvaser_usb/kvaser_usb_devlink.c   |  88 +++++++++++
 .../net/can/usb/kvaser_usb/kvaser_usb_hydra.c |  63 +++++++-
 .../net/can/usb/kvaser_usb/kvaser_usb_leaf.c  |  75 ++++++++-
 7 files changed, 359 insertions(+), 47 deletions(-)
 create mode 100644 drivers/net/can/usb/kvaser_usb/kvaser_usb_devlink.c
Signature · 1 line
2.49.0
#1 lore raw

PATCH 1/9 can: kvaser_usb: Add support to control CAN LEDs on device

Add support to turn on/off CAN LEDs on device.

---
 drivers/net/can/usb/kvaser_usb/kvaser_usb.h   |  9 ++++
 .../net/can/usb/kvaser_usb/kvaser_usb_hydra.c | 51 ++++++++++++++++++
 .../net/can/usb/kvaser_usb/kvaser_usb_leaf.c  | 54 +++++++++++++++++++
 3 files changed, 114 insertions(+)
drivers/net/can/usb/kvaser_usb/kvaser_usb.h +9 -0
index f6c77eca9f43..032dc1821f04 100644
@@ -54,6 +54,11 @@ enum kvaser_usb_leaf_family {
 	KVASER_USBCAN,
 };
 
+enum kvaser_usb_led_state {
+	KVASER_USB_LED_ON = 0,
+	KVASER_USB_LED_OFF = 1,
+};
+
 #define KVASER_USB_HYDRA_MAX_CMD_LEN		128
 struct kvaser_usb_dev_card_data_hydra {
 	u8 channel_to_he[KVASER_USB_MAX_NET_DEVICES];
@@ -149,6 +154,7 @@ struct kvaser_usb_net_priv {
  * @dev_get_software_details:	get software details
  * @dev_get_card_info:		get card info
  * @dev_get_capabilities:	discover device capabilities
+ * @dev_set_led:		turn on/off device LED
  *
  * @dev_set_opt_mode:		set ctrlmod
  * @dev_start_chip:		start the CAN controller
@@ -176,6 +182,9 @@ struct kvaser_usb_dev_ops {
 	int (*dev_get_software_details)(struct kvaser_usb *dev);
 	int (*dev_get_card_info)(struct kvaser_usb *dev);
 	int (*dev_get_capabilities)(struct kvaser_usb *dev);
+	int (*dev_set_led)(struct kvaser_usb_net_priv *priv,
+			   enum kvaser_usb_led_state state,
+			   u16 duration_ms);
 	int (*dev_set_opt_mode)(const struct kvaser_usb_net_priv *priv);
 	int (*dev_start_chip)(struct kvaser_usb_net_priv *priv);
 	int (*dev_stop_chip)(struct kvaser_usb_net_priv *priv);
drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c +51 -0

93 lines not on this page · raw message

drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c +54 -0

108 lines not on this page · raw message

Signature · 1 line
2.49.0
#2 lore raw

PATCH 2/9 can: kvaser_usb: Add support for ethtool set_phys_id()

In reply to Jimmy Assarsson

Add support for ethtool set_phys_id(), to physically locate devices by flashing a LED on the device.

---
 .../net/can/usb/kvaser_usb/kvaser_usb_core.c  | 29 +++++++++++++++++++
 1 file changed, 29 insertions(+)
drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c +29 -0
index daf42080f942..8003c3662247 100644
@@ -753,6 +753,34 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb,
 	return ret;
 }
 
+static int kvaser_usb_set_phys_id(struct net_device *netdev,
+				  enum ethtool_phys_id_state state)
+{
+	struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
+	const struct kvaser_usb_dev_ops *ops = priv->dev->driver_info->ops;
+	int ret = 0;
+
+	switch (state) {
+	case ETHTOOL_ID_ACTIVE:
+		return 3; /* 3 On/Off cycles per second */
+	case ETHTOOL_ID_ON:
+		ret = ops->dev_set_led(priv, KVASER_USB_LED_ON, 1000);
+		break;
+	case ETHTOOL_ID_OFF:
+		ret = ops->dev_set_led(priv, KVASER_USB_LED_OFF, 1000);
+		break;
+	case ETHTOOL_ID_INACTIVE:
+		/* Turn LED off and restore standard function after 1ms */
+		ret = ops->dev_set_led(priv, KVASER_USB_LED_OFF, 1);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
 static const struct net_device_ops kvaser_usb_netdev_ops = {
 	.ndo_open = kvaser_usb_open,
 	.ndo_stop = kvaser_usb_close,
@@ -763,6 +791,7 @@ static const struct net_device_ops kvaser_usb_netdev_ops = {
 
 static const struct ethtool_ops kvaser_usb_ethtool_ops = {
 	.get_ts_info = can_ethtool_op_get_ts_info_hwts,
+	.set_phys_id = kvaser_usb_set_phys_id,
 };
 
 static void kvaser_usb_remove_interfaces(struct kvaser_usb *dev)
Signature · 1 line
2.49.0
#3 lore raw

PATCH 3/9 can: kvaser_usb: Add intermediate variables

In reply to Jimmy Assarsson

Add intermediate variables, for readability and to simplify future patches.

---
 .../net/can/usb/kvaser_usb/kvaser_usb_core.c  | 20 ++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)
drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c +13 -7
index 8003c3662247..264840d84987 100644
@@ -364,10 +364,13 @@ static void kvaser_usb_read_bulk_callback(struct urb *urb)
 	err = usb_submit_urb(urb, GFP_ATOMIC);
 	if (err == -ENODEV) {
 		for (i = 0; i < dev->nchannels; i++) {
-			if (!dev->nets[i])
+			struct kvaser_usb_net_priv *priv;
+
+			priv = dev->nets[i];
+			if (!priv)
 				continue;
 
-			netif_device_detach(dev->nets[i]->netdev);
+			netif_device_detach(priv->netdev);
 		}
 	} else if (err) {
 		dev_err(&dev->intf->dev,
@@ -798,24 +801,27 @@ static void kvaser_usb_remove_interfaces(struct kvaser_usb *dev)
 {
 	const struct kvaser_usb_dev_ops *ops = dev->driver_info->ops;
 	int i;
+	struct kvaser_usb_net_priv *priv;
 
 	for (i = 0; i < dev->nchannels; i++) {
-		if (!dev->nets[i])
+		priv = dev->nets[i];
+		if (!priv)
 			continue;
 
-		unregister_candev(dev->nets[i]->netdev);
+		unregister_candev(priv->netdev);
 	}
 
 	kvaser_usb_unlink_all_urbs(dev);
 
 	for (i = 0; i < dev->nchannels; i++) {
-		if (!dev->nets[i])
+		priv = dev->nets[i];
+		if (!priv)
 			continue;
 
 		if (ops->dev_remove_channel)
-			ops->dev_remove_channel(dev->nets[i]);
+			ops->dev_remove_channel(priv);
 
-		free_candev(dev->nets[i]->netdev);
+		free_candev(priv->netdev);
 	}
 }
 
Signature · 1 line
2.49.0
#4 lore raw

PATCH 4/9 can: kvaser_usb: Move comment regarding max_tx_urbs

In reply to Jimmy Assarsson

Move comment regarding max_tx_urbs, to where the struct member is declared.

---
 drivers/net/can/usb/kvaser_usb/kvaser_usb.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/can/usb/kvaser_usb/kvaser_usb.h +2 -2
index 032dc1821f04..fba972e7220d 100644
@@ -101,12 +101,12 @@ struct kvaser_usb {
 	struct usb_endpoint_descriptor *bulk_in, *bulk_out;
 	struct usb_anchor rx_submitted;
 
+	u32 fw_version;
+	unsigned int nchannels;
 	/* @max_tx_urbs: Firmware-reported maximum number of outstanding,
 	 * not yet ACKed, transmissions on this device. This value is
 	 * also used as a sentinel for marking free tx contexts.
 	 */
-	u32 fw_version;
-	unsigned int nchannels;
 	unsigned int max_tx_urbs;
 	struct kvaser_usb_dev_card_data card_data;
 
Signature · 1 line
2.49.0
#5 lore raw

PATCH 5/9 can: kvaser_usb: Store the different firmware version components in a struct

In reply to Jimmy Assarsson

Store firmware version in kvaser_usb_fw_version struct, specifying the different components of the version number.

---
 drivers/net/can/usb/kvaser_usb/kvaser_usb.h       | 12 +++++++++++-
 drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c  |  8 ++++----
 drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c |  6 +++++-
 drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c  | 15 +++++++++++++--
 4 files changed, 33 insertions(+), 8 deletions(-)
drivers/net/can/usb/kvaser_usb/kvaser_usb.h +11 -1
index fba972e7220d..a36d86494113 100644
@@ -47,6 +47,10 @@
 #define KVASER_USB_CAP_EXT_CAP			0x02
 #define KVASER_USB_HYDRA_CAP_EXT_CMD		0x04
 
+#define KVASER_USB_SW_VERSION_MAJOR_MASK GENMASK(31, 24)
+#define KVASER_USB_SW_VERSION_MINOR_MASK GENMASK(23, 16)
+#define KVASER_USB_SW_VERSION_BUILD_MASK GENMASK(15, 0)
+
 struct kvaser_usb_dev_cfg;
 
 enum kvaser_usb_leaf_family {
@@ -83,6 +87,12 @@ struct kvaser_usb_tx_urb_context {
 	u32 echo_index;
 };
 
+struct kvaser_usb_fw_version {
+	u8 major;
+	u8 minor;
+	u16 build;
+};
+
 struct kvaser_usb_busparams {
 	__le32 bitrate;
 	u8 tseg1;
@@ -101,7 +111,7 @@ struct kvaser_usb {
 	struct usb_endpoint_descriptor *bulk_in, *bulk_out;
 	struct usb_anchor rx_submitted;
 
-	u32 fw_version;
+	struct kvaser_usb_fw_version fw_version;
 	unsigned int nchannels;
 	/* @max_tx_urbs: Firmware-reported maximum number of outstanding,
 	 * not yet ACKed, transmissions on this device. This value is
drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c +4 -4
index 264840d84987..68f23cd61ec6 100644
@@ -965,10 +965,10 @@ static int kvaser_usb_probe(struct usb_interface *intf,
 	if (WARN_ON(!dev->cfg))
 		return -ENODEV;
 
-	dev_dbg(&intf->dev, "Firmware version: %d.%d.%d\n",
-		((dev->fw_version >> 24) & 0xff),
-		((dev->fw_version >> 16) & 0xff),
-		(dev->fw_version & 0xffff));
+	dev_dbg(&intf->dev, "Firmware version: %u.%u.%u\n",
+		dev->fw_version.major,
+		dev->fw_version.minor,
+		dev->fw_version.build);
 
 	dev_dbg(&intf->dev, "Max outstanding tx = %d URBs\n", dev->max_tx_urbs);
 
drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c +5 -1
index 758fd13f1bf4..4107b39e396b 100644
@@ -1839,6 +1839,7 @@ static int kvaser_usb_hydra_get_software_details(struct kvaser_usb *dev)
 	size_t cmd_len;
 	int err;
 	u32 flags;
+	u32 fw_version;
 	struct kvaser_usb_dev_card_data *card_data = &dev->card_data;
 
 	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
@@ -1863,7 +1864,10 @@ static int kvaser_usb_hydra_get_software_details(struct kvaser_usb *dev)
 	if (err)
 		goto end;
 
-	dev->fw_version = le32_to_cpu(cmd->sw_detail_res.sw_version);
+	fw_version = le32_to_cpu(cmd->sw_detail_res.sw_version);
+	dev->fw_version.major = FIELD_GET(KVASER_USB_SW_VERSION_MAJOR_MASK, fw_version);
+	dev->fw_version.minor = FIELD_GET(KVASER_USB_SW_VERSION_MINOR_MASK, fw_version);
+	dev->fw_version.build = FIELD_GET(KVASER_USB_SW_VERSION_BUILD_MASK, fw_version);
 	flags = le32_to_cpu(cmd->sw_detail_res.sw_flags);
 
 	if (flags & KVASER_USB_HYDRA_SW_FLAG_FW_BAD) {
drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c +13 -2
index a67855521ccc..b4f5d4fba630 100644
@@ -741,9 +741,13 @@ static int kvaser_usb_leaf_send_simple_cmd(const struct kvaser_usb *dev,
 static void kvaser_usb_leaf_get_software_info_leaf(struct kvaser_usb *dev,
 						   const struct leaf_cmd_softinfo *softinfo)
 {
+	u32 fw_version;
 	u32 sw_options = le32_to_cpu(softinfo->sw_options);
 
-	dev->fw_version = le32_to_cpu(softinfo->fw_version);
+	fw_version = le32_to_cpu(softinfo->fw_version);
+	dev->fw_version.major = FIELD_GET(KVASER_USB_SW_VERSION_MAJOR_MASK, fw_version);
+	dev->fw_version.minor = FIELD_GET(KVASER_USB_SW_VERSION_MINOR_MASK, fw_version);
+	dev->fw_version.build = FIELD_GET(KVASER_USB_SW_VERSION_BUILD_MASK, fw_version);
 	dev->max_tx_urbs = le16_to_cpu(softinfo->max_outstanding_tx);
 
 	if (sw_options & KVASER_USB_LEAF_SWOPTION_EXT_CAP)
@@ -784,6 +788,7 @@ static int kvaser_usb_leaf_get_software_info_inner(struct kvaser_usb *dev)
 {
 	struct kvaser_cmd cmd;
 	int err;
+	u32 fw_version;
 
 	err = kvaser_usb_leaf_send_simple_cmd(dev, CMD_GET_SOFTWARE_INFO, 0);
 	if (err)
@@ -798,7 +803,13 @@ static int kvaser_usb_leaf_get_software_info_inner(struct kvaser_usb *dev)
 		kvaser_usb_leaf_get_software_info_leaf(dev, &cmd.u.leaf.softinfo);
 		break;
 	case KVASER_USBCAN:
-		dev->fw_version = le32_to_cpu(cmd.u.usbcan.softinfo.fw_version);
+		fw_version = le32_to_cpu(cmd.u.usbcan.softinfo.fw_version);
+		dev->fw_version.major = FIELD_GET(KVASER_USB_SW_VERSION_MAJOR_MASK,
+						  fw_version);
+		dev->fw_version.minor = FIELD_GET(KVASER_USB_SW_VERSION_MINOR_MASK,
+						  fw_version);
+		dev->fw_version.build = FIELD_GET(KVASER_USB_SW_VERSION_BUILD_MASK,
+						  fw_version);
 		dev->max_tx_urbs =
 			le16_to_cpu(cmd.u.usbcan.softinfo.max_outstanding_tx);
 		dev->cfg = &kvaser_usb_leaf_usbcan_dev_cfg;
Signature · 1 line
2.49.0
#6 lore raw

PATCH 6/9 can: kvaser_usb: Store additional device information

In reply to Jimmy Assarsson

Store additional device information; EAN (product number), serial_number and hardware revision.

---
 drivers/net/can/usb/kvaser_usb/kvaser_usb.h       | 3 +++
 drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c | 6 +++++-
 drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c  | 6 +++++-
 3 files changed, 13 insertions(+), 2 deletions(-)
drivers/net/can/usb/kvaser_usb/kvaser_usb.h +3 -0
index a36d86494113..35c2cf3d4486 100644
@@ -111,7 +111,10 @@ struct kvaser_usb {
 	struct usb_endpoint_descriptor *bulk_in, *bulk_out;
 	struct usb_anchor rx_submitted;
 
+	u32 ean[2];
+	u32 serial_number;
 	struct kvaser_usb_fw_version fw_version;
+	u8 hw_revision;
 	unsigned int nchannels;
 	/* @max_tx_urbs: Firmware-reported maximum number of outstanding,
 	 * not yet ACKed, transmissions on this device. This value is
drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c +5 -1
index 4107b39e396b..a8930bf933a7 100644
@@ -114,7 +114,7 @@ struct kvaser_cmd_card_info {
 	__le32 clock_res;
 	__le32 mfg_date;
 	__le32 ean[2];
-	u8 hw_version;
+	u8 hw_revision;
 	u8 usb_mode;
 	u8 hw_type;
 	u8 reserved0;
@@ -1918,6 +1918,10 @@ static int kvaser_usb_hydra_get_card_info(struct kvaser_usb *dev)
 	err = kvaser_usb_hydra_wait_cmd(dev, CMD_GET_CARD_INFO_RESP, &cmd);
 	if (err)
 		return err;
+	dev->ean[1] = le32_to_cpu(cmd.card_info.ean[1]);
+	dev->ean[0] = le32_to_cpu(cmd.card_info.ean[0]);
+	dev->serial_number = le32_to_cpu(cmd.card_info.serial_number);
+	dev->hw_revision = cmd.card_info.hw_revision;
 
 	dev->nchannels = cmd.card_info.nchannels;
 	if (dev->nchannels > KVASER_USB_MAX_NET_DEVICES)
drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c +5 -1
index b4f5d4fba630..c29828a94ad0 100644
@@ -138,7 +138,7 @@ struct kvaser_cmd_cardinfo {
 	__le32 padding0;
 	__le32 clock_resolution;
 	__le32 mfgdate;
-	u8 ean[8];
+	__le32 ean[2];
 	u8 hw_revision;
 	union {
 		struct {
@@ -854,6 +854,10 @@ static int kvaser_usb_leaf_get_card_info(struct kvaser_usb *dev)
 	    (dev->driver_info->family == KVASER_USBCAN &&
 	     dev->nchannels > MAX_USBCAN_NET_DEVICES))
 		return -EINVAL;
+	dev->ean[1] = le32_to_cpu(cmd.u.cardinfo.ean[1]);
+	dev->ean[0] = le32_to_cpu(cmd.u.cardinfo.ean[0]);
+	dev->serial_number = le32_to_cpu(cmd.u.cardinfo.serial_number);
+	dev->hw_revision = cmd.u.cardinfo.hw_revision;
 
 	return 0;
 }
Signature · 1 line
2.49.0
#7 lore raw

PATCH 7/9 can: kvaser_usb: Add devlink support

In reply to Jimmy Assarsson

Add devlink support at device level.

Example output:

$ devlink dev
usb/1-1.3:1.0
$ devlink dev info
usb/1-1.3:1.0:
  driver kvaser_usb
---
 drivers/net/can/usb/Kconfig                   |  1 +
 drivers/net/can/usb/kvaser_usb/Makefile       |  2 +-
 drivers/net/can/usb/kvaser_usb/kvaser_usb.h   |  3 +
 .../net/can/usb/kvaser_usb/kvaser_usb_core.c  | 72 ++++++++++++-------
 .../can/usb/kvaser_usb/kvaser_usb_devlink.c   | 10 +++
 5 files changed, 62 insertions(+), 26 deletions(-)
 create mode 100644 drivers/net/can/usb/kvaser_usb/kvaser_usb_devlink.c
drivers/net/can/usb/Kconfig +1 -0
index 9dae0c71a2e1..a7547a83120e 100644
@@ -66,6 +66,7 @@ config CAN_GS_USB
 
 config CAN_KVASER_USB
 	tristate "Kvaser CAN/USB interface"
+	select NET_DEVLINK
 	help
 	  This driver adds support for Kvaser CAN/USB devices like Kvaser
 	  Leaf Light, Kvaser USBcan II and Kvaser Memorator Pro 5xHS.
drivers/net/can/usb/kvaser_usb/Makefile +1 -1
index cf260044f0b9..41b4a11555aa 100644
@@ -1,3 +1,3 @@
 # SPDX-License-Identifier: GPL-2.0-only
 obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o
-kvaser_usb-y = kvaser_usb_core.o kvaser_usb_leaf.o kvaser_usb_hydra.o
+kvaser_usb-y = kvaser_usb_core.o kvaser_usb_devlink.o kvaser_usb_leaf.o kvaser_usb_hydra.o
drivers/net/can/usb/kvaser_usb/kvaser_usb.h +3 -0
index 35c2cf3d4486..d5f913ac9b44 100644
@@ -27,6 +27,7 @@
 #include <linux/spinlock.h>
 #include <linux/types.h>
 #include <linux/usb.h>
+#include <net/devlink.h>
 
 #include <linux/can.h>
 #include <linux/can/dev.h>
@@ -226,6 +227,8 @@ struct kvaser_usb_dev_cfg {
 extern const struct kvaser_usb_dev_ops kvaser_usb_hydra_dev_ops;
 extern const struct kvaser_usb_dev_ops kvaser_usb_leaf_dev_ops;
 
+extern const struct devlink_ops kvaser_usb_devlink_ops;
+
 void kvaser_usb_unlink_tx_urbs(struct kvaser_usb_net_priv *priv);
 
 int kvaser_usb_recv_cmd(const struct kvaser_usb *dev, void *cmd, int len,
drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c +47 -25

128 lines not on this page · raw message

drivers/net/can/usb/kvaser_usb/kvaser_usb_devlink.c new file +10 -0
new file mode 100644
index 000000000000..9a3a8966a0a1
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
+/* kvaser_usb devlink functions
+ *
+ * Copyright (C) 2025 KVASER AB, Sweden. All rights reserved.
+ */
+
+#include <net/devlink.h>
+
+const struct devlink_ops kvaser_usb_devlink_ops = {
+};
Signature · 1 line
2.49.0
#8 lore raw

PATCH 8/9 can: kvaser_usb: Expose device information via devlink info_get()

In reply to Jimmy Assarsson

Expose device information via devlink info_get():
* Serial number
* Firmware version
* Hardware revision
* EAN (product number)

Example output:

$ devlink dev
usb/1-1.2:1.0
$ devlink dev info
usb/1-1.2:1.0:
  driver kvaser_usb
  serial_number 1020
  versions:
      fixed:
        board.rev 1
        board.id 7330130009653
      running:
        fw 3.22.527
---
 .../can/usb/kvaser_usb/kvaser_usb_devlink.c   | 53 +++++++++++++++++++
 1 file changed, 53 insertions(+)
drivers/net/can/usb/kvaser_usb/kvaser_usb_devlink.c +53 -0
index 9a3a8966a0a1..aca36794198e 100644
@@ -6,5 +6,58 @@
 
 #include <net/devlink.h>
 
+#include "kvaser_usb.h"
+
+#define KVASER_USB_EAN_MSB 0x00073301
+
+static int kvaser_usb_devlink_info_get(struct devlink *devlink,
+				       struct devlink_info_req *req,
+				       struct netlink_ext_ack *extack)
+{
+	struct kvaser_usb *dev = devlink_priv(devlink);
+	char buf[16]; /* 73301XXXXXXXXXX */
+	int ret;
+
+	if (dev->serial_number) {
+		snprintf(buf, sizeof(buf), "%u", dev->serial_number);
+		ret = devlink_info_serial_number_put(req, buf);
+		if (ret)
+			return ret;
+	}
+
+	if (dev->fw_version.major) {
+		snprintf(buf, sizeof(buf), "%u.%u.%u",
+			 dev->fw_version.major,
+			 dev->fw_version.minor,
+			 dev->fw_version.build);
+		ret = devlink_info_version_running_put(req,
+						       DEVLINK_INFO_VERSION_GENERIC_FW,
+						       buf);
+		if (ret)
+			return ret;
+	}
+
+	if (dev->hw_revision) {
+		snprintf(buf, sizeof(buf), "%u", dev->hw_revision);
+		ret = devlink_info_version_fixed_put(req,
+						     DEVLINK_INFO_VERSION_GENERIC_BOARD_REV,
+						     buf);
+		if (ret)
+			return ret;
+	}
+
+	if (dev->ean[1] == KVASER_USB_EAN_MSB) {
+		snprintf(buf, sizeof(buf), "%x%08x", dev->ean[1], dev->ean[0]);
+		ret = devlink_info_version_fixed_put(req,
+						     DEVLINK_INFO_VERSION_GENERIC_BOARD_ID,
+						     buf);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
 const struct devlink_ops kvaser_usb_devlink_ops = {
+	.info_get = kvaser_usb_devlink_info_get,
 };
Signature · 1 line
2.49.0
#9 lore raw

PATCH 9/9 can: kvaser_usb: Add devlink port support

In reply to Jimmy Assarsson

Register each CAN channel of the device as an devlink physical port. This makes it easier to get device information for a given network interface (i.e. can2).

Example output:

$ devlink dev
usb/1-1.3:1.0
$ devlink port
usb/1-1.3:1.0/0: type eth netdev can0 flavour physical port 0 splittable false
usb/1-1.3:1.0/1: type eth netdev can1 flavour physical port 1 splittable false
$ devlink port show can1
usb/1-1.3:1.0/1: type eth netdev can1 flavour physical port 0 splittable false
$ devlink dev info
usb/1-1.3:1.0:
  driver kvaser_usb
  serial_number 1020
  versions:
      fixed:
        board.rev 1
        board.id 7330130009653
      running:
        fw 3.22.527
$ ethtool -i can1
driver: kvaser_usb
version: 6.12.10-arch1-1
firmware-version: 3.22.527
expansion-rom-version:
bus-info: 1-1.3:1.0
supports-statistics: no
supports-test: no
supports-eeprom-access: no
supports-register-dump: no
supports-priv-flags: no
---
 drivers/net/can/usb/kvaser_usb/kvaser_usb.h   |  4 +++
 .../net/can/usb/kvaser_usb/kvaser_usb_core.c  | 15 ++++++++---
 .../can/usb/kvaser_usb/kvaser_usb_devlink.c   | 25 +++++++++++++++++++
 3 files changed, 41 insertions(+), 3 deletions(-)
drivers/net/can/usb/kvaser_usb/kvaser_usb.h +4 -0
index d5f913ac9b44..46a1b6907a50 100644
@@ -131,6 +131,7 @@ struct kvaser_usb {
 
 struct kvaser_usb_net_priv {
 	struct can_priv can;
+	struct devlink_port devlink_port;
 	struct can_berr_counter bec;
 
 	/* subdriver-specific data */
@@ -229,6 +230,9 @@ extern const struct kvaser_usb_dev_ops kvaser_usb_leaf_dev_ops;
 
 extern const struct devlink_ops kvaser_usb_devlink_ops;
 
+int kvaser_usb_devlink_port_register(struct kvaser_usb_net_priv *priv);
+void kvaser_usb_devlink_port_unregister(struct kvaser_usb_net_priv *priv);
+
 void kvaser_usb_unlink_tx_urbs(struct kvaser_usb_net_priv *priv);
 
 int kvaser_usb_recv_cmd(const struct kvaser_usb *dev, void *cmd, int len,
drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c +12 -3
index d93b9460c683..4b29cc3604f4 100644
@@ -821,6 +821,7 @@ static void kvaser_usb_remove_interfaces(struct kvaser_usb *dev)
 		if (ops->dev_remove_channel)
 			ops->dev_remove_channel(priv);
 
+		kvaser_usb_devlink_port_unregister(priv);
 		free_candev(priv->netdev);
 	}
 }
@@ -893,20 +894,28 @@ static int kvaser_usb_init_one(struct kvaser_usb *dev, int channel)
 	if (ops->dev_init_channel) {
 		err = ops->dev_init_channel(priv);
 		if (err)
-			goto err;
+			goto candev_free;
+	}
+
+	err = kvaser_usb_devlink_port_register(priv);
+	if (err) {
+		dev_err(&dev->intf->dev, "Failed to register devlink port\n");
+		goto candev_free;
 	}
 
 	err = register_candev(netdev);
 	if (err) {
 		dev_err(&dev->intf->dev, "Failed to register CAN device\n");
-		goto err;
+		goto unregister_devlink_port;
 	}
 
 	netdev_dbg(netdev, "device registered\n");
 
 	return 0;
 
-err:
+unregister_devlink_port:
+	kvaser_usb_devlink_port_unregister(priv);
+candev_free:
 	free_candev(netdev);
 	dev->nets[channel] = NULL;
 	return err;
drivers/net/can/usb/kvaser_usb/kvaser_usb_devlink.c +25 -0
index aca36794198e..81e389e49a3e 100644
@@ -4,6 +4,7 @@
  * Copyright (C) 2025 KVASER AB, Sweden. All rights reserved.
  */
 
+#include <linux/netdevice.h>
 #include <net/devlink.h>
 
 #include "kvaser_usb.h"
@@ -61,3 +62,27 @@ static int kvaser_usb_devlink_info_get(struct devlink *devlink,
 const struct devlink_ops kvaser_usb_devlink_ops = {
 	.info_get = kvaser_usb_devlink_info_get,
 };
+
+int kvaser_usb_devlink_port_register(struct kvaser_usb_net_priv *priv)
+{
+	int ret;
+	struct devlink_port_attrs attrs = {
+		.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL,
+		.phys.port_number = priv->channel,
+	};
+	devlink_port_attrs_set(&priv->devlink_port, &attrs);
+
+	ret = devlink_port_register(priv_to_devlink(priv->dev),
+				    &priv->devlink_port, priv->channel);
+	if (ret)
+		return ret;
+
+	SET_NETDEV_DEVLINK_PORT(priv->netdev, &priv->devlink_port);
+
+	return 0;
+}
+
+void kvaser_usb_devlink_port_unregister(struct kvaser_usb_net_priv *priv)
+{
+	devlink_port_unregister(&priv->devlink_port);
+}
Signature · 1 line
2.49.0
#10 lore raw
In reply to Jimmy Assarsson
Quoted 3 lines · Jimmy Assarsson

This patch series simplifies the process of identifying which network interface (can0..canX) corresponds to which physical CAN channel on Kvaser USB based CAN interfaces.

This series mirrors the kvaser_pciefd one, so I will not repeat the same comment twice. I let you manage it directly :)

One last comment for both the kvaser_pciefd and the kvaser_usb: you may want to add an entry to the devlink documentation. Something like this:

- https://git.kernel.org/torvalds/c/9f63f96aac92
- https://git.kernel.org/torvalds/c/115dd5469019

Yours sincerely,
Vincent Mailhol

#11 lore raw
Quoted 8 lines · Vincent Mailhol
Quoted 3 lines · Jimmy Assarsson

This patch series simplifies the process of identifying which network interface (can0..canX) corresponds to which physical CAN channel on Kvaser USB based CAN interfaces.

This series mirrors the kvaser_pciefd one, so I will not repeat the same comment twice. I let you manage it directly :)

Quoted 8 lines

One last comment for both the kvaser_pciefd and the kvaser_usb: you may want to add an entry to the devlink documentation. Something like this:

- https://git.kernel.org/torvalds/c/9f63f96aac92
- https://git.kernel.org/torvalds/c/115dd5469019

Yours sincerely,
Vincent Mailhol

Hi Vincent,
Thanks for great review comments!

I think I've fixed all of them for kvaser_pciefd in v2 [1].
I'll send v2 of the kvaser_usb series once we've straighten out the usage of netdev.dev_{id,port}.

[1] https://lore.kernel.org/linux-can/[email protected]/T/#m8f7fa76d70b4c7c32c69f74035663cdb330fdf02

Best regards,
jimmy