can: peak_usb: add ethtool interface to user-configurable CAN channel identifier
authorStephane Grosjean <s.grosjean@peak-system.com>
Mon, 16 Jan 2023 20:09:29 +0000 (20:09 +0000)
committerMarc Kleine-Budde <mkl@pengutronix.de>
Thu, 2 Feb 2023 16:39:23 +0000 (17:39 +0100)
This patch introduces 3 new functions implementing support for ethtool
access to the CAN channel ID of all USB CAN network interfaces managed by
the driver. With this patch, it is possible to read/write the CAN
channel ID from/to the EEPROM via the ethtool interface.

The CAN channel ID is a user-configurable device identifier that can be
set individually for each CAN interface of a PEAK USB device. Depending on
the device, the identifier has a length of 8 or 32 bit. The identifier
is stored in the non-volatile memory of the device.

The identifier of a CAN interface can be read/written as an 8 or 32 bit
byte string in native (little-endian) byte order, where the length depends
on the device type.

Signed-off-by: Stephane Grosjean <s.grosjean@peak-system.com>
Signed-off-by: Lukas Magel <lukas.magel@posteo.net>
Link: https://lore.kernel.org/all/20230116200932.157769-6-lukas.magel@posteo.net
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
drivers/net/can/usb/peak_usb/pcan_usb.c
drivers/net/can/usb/peak_usb/pcan_usb_core.c
drivers/net/can/usb/peak_usb/pcan_usb_core.h
drivers/net/can/usb/peak_usb/pcan_usb_fd.c
drivers/net/can/usb/peak_usb/pcan_usb_pro.c

index 44e894a1f2c23dc9e456ac7f51c42c9928d0cabe..bead4f4ba4726bfaed2dde805213ff9834a5041b 100644 (file)
@@ -982,9 +982,18 @@ static int pcan_usb_set_phys_id(struct net_device *netdev,
        return err;
 }
 
+/* This device only handles 8-bit CAN channel id. */
+static int pcan_usb_get_eeprom_len(struct net_device *netdev)
+{
+       return sizeof(u8);
+}
+
 static const struct ethtool_ops pcan_usb_ethtool_ops = {
        .set_phys_id = pcan_usb_set_phys_id,
        .get_ts_info = pcan_get_ts_info,
+       .get_eeprom_len = pcan_usb_get_eeprom_len,
+       .get_eeprom = peak_usb_get_eeprom,
+       .set_eeprom = peak_usb_set_eeprom,
 };
 
 /*
index 3a73eac314ff52ba0894c78b3947b5e8cfb0f987..3bfd27742ae46fa5af16099353da90c2893b321f 100644 (file)
@@ -808,6 +808,86 @@ static const struct net_device_ops peak_usb_netdev_ops = {
        .ndo_change_mtu = can_change_mtu,
 };
 
+/* CAN-USB devices generally handle 32-bit CAN channel IDs.
+ * In case one doesn't, then it have to overload this function.
+ */
+int peak_usb_get_eeprom_len(struct net_device *netdev)
+{
+       return sizeof(u32);
+}
+
+/* Every CAN-USB device exports the dev_get_can_channel_id() operation. It is used
+ * here to fill the data buffer with the user defined CAN channel ID.
+ */
+int peak_usb_get_eeprom(struct net_device *netdev,
+                       struct ethtool_eeprom *eeprom, u8 *data)
+{
+       struct peak_usb_device *dev = netdev_priv(netdev);
+       u32 ch_id;
+       __le32 ch_id_le;
+       int err;
+
+       err = dev->adapter->dev_get_can_channel_id(dev, &ch_id);
+       if (err)
+               return err;
+
+       /* ethtool operates on individual bytes. The byte order of the CAN
+        * channel id in memory depends on the kernel architecture. We
+        * convert the CAN channel id back to the native byte order of the PEAK
+        * device itself to ensure that the order is consistent for all
+        * host architectures.
+        */
+       ch_id_le = cpu_to_le32(ch_id);
+       memcpy(data, (u8 *)&ch_id_le + eeprom->offset, eeprom->len);
+
+       /* update cached value */
+       dev->can_channel_id = ch_id;
+       return err;
+}
+
+/* Every CAN-USB device exports the dev_get_can_channel_id()/dev_set_can_channel_id()
+ * operations. They are used here to set the new user defined CAN channel ID.
+ */
+int peak_usb_set_eeprom(struct net_device *netdev,
+                       struct ethtool_eeprom *eeprom, u8 *data)
+{
+       struct peak_usb_device *dev = netdev_priv(netdev);
+       u32 ch_id;
+       __le32 ch_id_le;
+       int err;
+
+       /* first, read the current user defined CAN channel ID */
+       err = dev->adapter->dev_get_can_channel_id(dev, &ch_id);
+       if (err) {
+               netdev_err(netdev, "Failed to init CAN channel id (err %d)\n", err);
+               return err;
+       }
+
+       /* do update the value with user given bytes.
+        * ethtool operates on individual bytes. The byte order of the CAN
+        * channel ID in memory depends on the kernel architecture. We
+        * convert the CAN channel ID back to the native byte order of the PEAK
+        * device itself to ensure that the order is consistent for all
+        * host architectures.
+        */
+       ch_id_le = cpu_to_le32(ch_id);
+       memcpy((u8 *)&ch_id_le + eeprom->offset, data, eeprom->len);
+       ch_id = le32_to_cpu(ch_id_le);
+
+       /* flash the new value now */
+       err = dev->adapter->dev_set_can_channel_id(dev, ch_id);
+       if (err) {
+               netdev_err(netdev, "Failed to write new CAN channel id (err %d)\n",
+                          err);
+               return err;
+       }
+
+       /* update cached value with the new one */
+       dev->can_channel_id = ch_id;
+
+       return 0;
+}
+
 int pcan_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info)
 {
        info->so_timestamping =
index 1e461aef0f2a76564e40c0e9b5f50f8757bf14aa..980e315186cfde9c91d2210d012db5978859bc07 100644 (file)
@@ -149,4 +149,10 @@ void peak_usb_async_complete(struct urb *urb);
 void peak_usb_restart_complete(struct peak_usb_device *dev);
 int pcan_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info);
 
+/* common 32-bit CAN channel ID ethtool management */
+int peak_usb_get_eeprom_len(struct net_device *netdev);
+int peak_usb_get_eeprom(struct net_device *netdev,
+                       struct ethtool_eeprom *eeprom, u8 *data);
+int peak_usb_set_eeprom(struct net_device *netdev,
+                       struct ethtool_eeprom *eeprom, u8 *data);
 #endif
index 1ea4cfdfd640153231c9d8577e0ba46d4f96ad39..fd925ae96331318890a7a4ca6c66a8778cfced4f 100644 (file)
@@ -1124,6 +1124,9 @@ static int pcan_usb_fd_set_phys_id(struct net_device *netdev,
 static const struct ethtool_ops pcan_usb_fd_ethtool_ops = {
        .set_phys_id = pcan_usb_fd_set_phys_id,
        .get_ts_info = pcan_get_ts_info,
+       .get_eeprom_len = peak_usb_get_eeprom_len,
+       .get_eeprom = peak_usb_get_eeprom,
+       .set_eeprom = peak_usb_set_eeprom,
 };
 
 /* describes the PCAN-USB FD adapter */
index 061f04c20f960a84ac251ba68960be9e6dbfe88d..0c805d9672bf1929db56a78a800aa264ae75de26 100644 (file)
@@ -1037,6 +1037,9 @@ static int pcan_usb_pro_set_phys_id(struct net_device *netdev,
 static const struct ethtool_ops pcan_usb_pro_ethtool_ops = {
        .set_phys_id = pcan_usb_pro_set_phys_id,
        .get_ts_info = pcan_get_ts_info,
+       .get_eeprom_len = peak_usb_get_eeprom_len,
+       .get_eeprom = peak_usb_get_eeprom,
+       .set_eeprom = peak_usb_set_eeprom,
 };
 
 /*