Commit 36207c34 authored by Marc Kleine-Budde's avatar Marc Kleine-Budde

Merge patch series "can: peak_usb: Introduce configurable CAN channel ID"

Lukas Magel <lukas.magel@posteo.net> says:

This series of patches introduces the CAN channel ID for the PEAK USB
CAN interfaces. The id can be read/written via ethtool and is exposed as
a read-only attribute via sysfs. This allows users to set the id via
ethtool and write udev rules to match against the sysfs attribute.

Part of the patches were originally introduced by Stéphane Grosjean and
were modified slightly. See the following threads for the original patches:

* https://lore.kernel.org/linux-can/20220128150157.1222850-1-s.grosjean@peak-system.com
* https://lore.kernel.org/linux-can/20211117150132.37056-1-s.grosjean@peak-system.com

changes since v2: https://lore.kernel.org/all/20221030105939.87658-1-lukas.magel@posteo.net
* Fix the issues raised on netdev
* Rename user device ID to CAN channel ID to make the 1-to-N mapping
  between USB device and exposed CAN channels more obvious

changes since v1: https://lore.kernel.org/all/20221022213535.8495-1-lukas.magel@posteo.net
* Fix type of devid_le in ethtool peak_usb_(get|set)_eeprom
* Fix signed-off tags

Link: https://lore.kernel.org/all/20221213080309.9013-1-lukas.magel@posteo.net
[mkl: added links]
Signed-off-by: default avatarMarc Kleine-Budde <mkl@pengutronix.de>
parents e9ac502c 73019de1
What: /sys/class/net/<iface>/peak_usb/can_channel_id
Date: November 2022
KernelVersion: 6.2
Contact: Stephane Grosjean <s.grosjean@peak-system.com>
Description:
PEAK PCAN-USB devices support user-configurable CAN channel
identifiers. Contrary to a USB serial number, these identifiers
are writable and can be set per CAN interface. This means that
if a USB device exports multiple CAN interfaces, each of them
can be assigned a unique channel ID.
This attribute provides read-only access to the currently
configured value of the channel identifier. Depending on the
device type, the identifier has a length of 8 or 32 bit. The
value read from this attribute is always an 8 digit 32 bit
hexadecimal value in big endian format. If the device only
supports an 8 bit identifier, the upper 24 bit of the value are
set to zero.
......@@ -9,10 +9,11 @@
* Many thanks to Klaus Hitschler <klaus.hitschler@gmx.de>
*/
#include <asm/unaligned.h>
#include <linux/ethtool.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/usb.h>
#include <linux/module.h>
#include <linux/ethtool.h>
#include <linux/can.h>
#include <linux/can/dev.h>
......@@ -381,23 +382,42 @@ static int pcan_usb_get_serial(struct peak_usb_device *dev, u32 *serial_number)
}
/*
* read device id from device
* read can channel id from device
*/
static int pcan_usb_get_device_id(struct peak_usb_device *dev, u32 *device_id)
static int pcan_usb_get_can_channel_id(struct peak_usb_device *dev, u32 *can_ch_id)
{
u8 args[PCAN_USB_CMD_ARGS_LEN];
int err;
err = pcan_usb_wait_rsp(dev, PCAN_USB_CMD_DEVID, PCAN_USB_GET, args);
if (err)
netdev_err(dev->netdev, "getting device id failure: %d\n", err);
netdev_err(dev->netdev, "getting can channel id failure: %d\n", err);
else
*device_id = args[0];
*can_ch_id = args[0];
return err;
}
/* set a new CAN channel id in the flash memory of the device */
static int pcan_usb_set_can_channel_id(struct peak_usb_device *dev, u32 can_ch_id)
{
u8 args[PCAN_USB_CMD_ARGS_LEN];
/* this kind of device supports 8-bit values only */
if (can_ch_id > U8_MAX)
return -EINVAL;
/* during the flash process the device disconnects during ~1.25 s.:
* prohibit access when interface is UP
*/
if (dev->netdev->flags & IFF_UP)
return -EBUSY;
args[0] = can_ch_id;
return pcan_usb_send_cmd(dev, PCAN_USB_CMD_DEVID, PCAN_USB_SET, args);
}
/*
* update current time ref with received timestamp
*/
......@@ -963,9 +983,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,
};
/*
......@@ -1017,7 +1046,8 @@ const struct peak_usb_adapter pcan_usb = {
.dev_init = pcan_usb_init,
.dev_set_bus = pcan_usb_write_mode,
.dev_set_bittiming = pcan_usb_set_bittiming,
.dev_get_device_id = pcan_usb_get_device_id,
.dev_get_can_channel_id = pcan_usb_get_can_channel_id,
.dev_set_can_channel_id = pcan_usb_set_can_channel_id,
.dev_decode_buf = pcan_usb_decode_buf,
.dev_encode_msg = pcan_usb_encode_msg,
.dev_start = pcan_usb_start,
......
......@@ -8,13 +8,15 @@
*
* Many thanks to Klaus Hitschler <klaus.hitschler@gmx.de>
*/
#include <linux/device.h>
#include <linux/ethtool.h>
#include <linux/init.h>
#include <linux/signal.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/signal.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/usb.h>
#include <linux/ethtool.h>
#include <linux/can.h>
#include <linux/can/dev.h>
......@@ -53,6 +55,26 @@ static const struct usb_device_id peak_usb_table[] = {
MODULE_DEVICE_TABLE(usb, peak_usb_table);
static ssize_t can_channel_id_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct net_device *netdev = to_net_dev(dev);
struct peak_usb_device *peak_dev = netdev_priv(netdev);
return sysfs_emit(buf, "%08X\n", peak_dev->can_channel_id);
}
static DEVICE_ATTR_RO(can_channel_id);
/* mutable to avoid cast in attribute_group */
static struct attribute *peak_usb_sysfs_attrs[] = {
&dev_attr_can_channel_id.attr,
NULL,
};
static const struct attribute_group peak_usb_sysfs_group = {
.name = "peak_usb",
.attrs = peak_usb_sysfs_attrs,
};
/*
* dump memory
*/
......@@ -808,6 +830,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 =
......@@ -881,6 +983,9 @@ static int peak_usb_create_dev(const struct peak_usb_adapter *peak_usb_adapter,
/* add ethtool support */
netdev->ethtool_ops = peak_usb_adapter->ethtool_ops;
/* register peak_usb sysfs files */
netdev->sysfs_groups[0] = &peak_usb_sysfs_group;
init_usb_anchor(&dev->rx_submitted);
init_usb_anchor(&dev->tx_submitted);
......@@ -921,12 +1026,11 @@ static int peak_usb_create_dev(const struct peak_usb_adapter *peak_usb_adapter,
goto adap_dev_free;
}
/* get device number early */
if (dev->adapter->dev_get_device_id)
dev->adapter->dev_get_device_id(dev, &dev->device_number);
/* get CAN channel id early */
dev->adapter->dev_get_can_channel_id(dev, &dev->can_channel_id);
netdev_info(netdev, "attached to %s channel %u (device %u)\n",
peak_usb_adapter->name, ctrl_idx, dev->device_number);
netdev_info(netdev, "attached to %s channel %u (device 0x%08X)\n",
peak_usb_adapter->name, ctrl_idx, dev->can_channel_id);
return 0;
......@@ -964,7 +1068,7 @@ static void peak_usb_disconnect(struct usb_interface *intf)
dev->state &= ~PCAN_USB_STATE_CONNECTED;
strscpy(name, netdev->name, IFNAMSIZ);
unregister_netdev(netdev);
unregister_candev(netdev);
kfree(dev->cmd_buf);
dev->next_siblings = NULL;
......
......@@ -60,7 +60,8 @@ struct peak_usb_adapter {
int (*dev_set_data_bittiming)(struct peak_usb_device *dev,
struct can_bittiming *bt);
int (*dev_set_bus)(struct peak_usb_device *dev, u8 onoff);
int (*dev_get_device_id)(struct peak_usb_device *dev, u32 *device_id);
int (*dev_get_can_channel_id)(struct peak_usb_device *dev, u32 *can_ch_id);
int (*dev_set_can_channel_id)(struct peak_usb_device *dev, u32 can_ch_id);
int (*dev_decode_buf)(struct peak_usb_device *dev, struct urb *urb);
int (*dev_encode_msg)(struct peak_usb_device *dev, struct sk_buff *skb,
u8 *obuf, size_t *size);
......@@ -122,7 +123,8 @@ struct peak_usb_device {
u8 *cmd_buf;
struct usb_anchor rx_submitted;
u32 device_number;
/* equivalent to the device ID in the Windows API */
u32 can_channel_id;
u8 device_rev;
u8 ep_msg_in;
......@@ -147,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
......@@ -4,10 +4,10 @@
*
* Copyright (C) 2013-2014 Stephane Grosjean <s.grosjean@peak-system.com>
*/
#include <linux/ethtool.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/usb.h>
#include <linux/module.h>
#include <linux/ethtool.h>
#include <linux/can.h>
#include <linux/can/dev.h>
......@@ -147,6 +147,15 @@ struct __packed pcan_ufd_ovr_msg {
u8 unused[3];
};
#define PCAN_UFD_CMD_DEVID_SET 0x81
struct __packed pcan_ufd_device_id {
__le16 opcode_channel;
u16 unused;
__le32 device_id;
};
static inline int pufd_omsg_get_channel(struct pcan_ufd_ovr_msg *om)
{
return om->channel & 0xf;
......@@ -234,6 +243,15 @@ static int pcan_usb_fd_send_cmd(struct peak_usb_device *dev, void *cmd_tail)
return err;
}
static int pcan_usb_fd_read_fwinfo(struct peak_usb_device *dev,
struct pcan_ufd_fw_info *fw_info)
{
return pcan_usb_pro_send_req(dev, PCAN_USBPRO_REQ_INFO,
PCAN_USBPRO_INFO_FW,
fw_info,
sizeof(*fw_info));
}
/* build the commands list in the given buffer, to enter operational mode */
static int pcan_usb_fd_build_restart_cmd(struct peak_usb_device *dev, u8 *buf)
{
......@@ -434,6 +452,34 @@ static int pcan_usb_fd_set_bittiming_fast(struct peak_usb_device *dev,
return pcan_usb_fd_send_cmd(dev, ++cmd);
}
/* read user CAN channel id from device */
static int pcan_usb_fd_get_can_channel_id(struct peak_usb_device *dev,
u32 *can_ch_id)
{
int err;
struct pcan_usb_fd_if *usb_if = pcan_usb_fd_dev_if(dev);
err = pcan_usb_fd_read_fwinfo(dev, &usb_if->fw_info);
if (err)
return err;
*can_ch_id = le32_to_cpu(usb_if->fw_info.dev_id[dev->ctrl_idx]);
return err;
}
/* set a new CAN channel id in the flash memory of the device */
static int pcan_usb_fd_set_can_channel_id(struct peak_usb_device *dev, u32 can_ch_id)
{
struct pcan_ufd_device_id *cmd = pcan_usb_fd_cmd_buffer(dev);
cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx,
PCAN_UFD_CMD_DEVID_SET);
cmd->device_id = cpu_to_le32(can_ch_id);
/* send the command */
return pcan_usb_fd_send_cmd(dev, ++cmd);
}
/* handle restart but in asynchronously way
* (uses PCAN-USB Pro code to complete asynchronous request)
*/
......@@ -907,10 +953,7 @@ static int pcan_usb_fd_init(struct peak_usb_device *dev)
fw_info = &pdev->usb_if->fw_info;
err = pcan_usb_pro_send_req(dev, PCAN_USBPRO_REQ_INFO,
PCAN_USBPRO_INFO_FW,
fw_info,
sizeof(*fw_info));
err = pcan_usb_fd_read_fwinfo(dev, fw_info);
if (err) {
dev_err(dev->netdev->dev.parent,
"unable to read %s firmware info (err %d)\n",
......@@ -972,7 +1015,7 @@ static int pcan_usb_fd_init(struct peak_usb_device *dev)
}
pdev->usb_if->dev[dev->ctrl_idx] = dev;
dev->device_number =
dev->can_channel_id =
le32_to_cpu(pdev->usb_if->fw_info.dev_id[dev->ctrl_idx]);
/* if vendor rsp is of type 2, then it contains EP numbers to
......@@ -1081,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 */
......@@ -1148,6 +1194,8 @@ const struct peak_usb_adapter pcan_usb_fd = {
.dev_set_bus = pcan_usb_fd_set_bus,
.dev_set_bittiming = pcan_usb_fd_set_bittiming_slow,
.dev_set_data_bittiming = pcan_usb_fd_set_bittiming_fast,
.dev_get_can_channel_id = pcan_usb_fd_get_can_channel_id,
.dev_set_can_channel_id = pcan_usb_fd_set_can_channel_id,
.dev_decode_buf = pcan_usb_fd_decode_buf,
.dev_start = pcan_usb_fd_start,
.dev_stop = pcan_usb_fd_stop,
......@@ -1222,6 +1270,8 @@ const struct peak_usb_adapter pcan_usb_chip = {
.dev_set_bus = pcan_usb_fd_set_bus,
.dev_set_bittiming = pcan_usb_fd_set_bittiming_slow,
.dev_set_data_bittiming = pcan_usb_fd_set_bittiming_fast,
.dev_get_can_channel_id = pcan_usb_fd_get_can_channel_id,
.dev_set_can_channel_id = pcan_usb_fd_set_can_channel_id,
.dev_decode_buf = pcan_usb_fd_decode_buf,
.dev_start = pcan_usb_fd_start,
.dev_stop = pcan_usb_fd_stop,
......@@ -1296,6 +1346,8 @@ const struct peak_usb_adapter pcan_usb_pro_fd = {
.dev_set_bus = pcan_usb_fd_set_bus,
.dev_set_bittiming = pcan_usb_fd_set_bittiming_slow,
.dev_set_data_bittiming = pcan_usb_fd_set_bittiming_fast,
.dev_get_can_channel_id = pcan_usb_fd_get_can_channel_id,
.dev_set_can_channel_id = pcan_usb_fd_set_can_channel_id,
.dev_decode_buf = pcan_usb_fd_decode_buf,
.dev_start = pcan_usb_fd_start,
.dev_stop = pcan_usb_fd_stop,
......@@ -1370,6 +1422,8 @@ const struct peak_usb_adapter pcan_usb_x6 = {
.dev_set_bus = pcan_usb_fd_set_bus,
.dev_set_bittiming = pcan_usb_fd_set_bittiming_slow,
.dev_set_data_bittiming = pcan_usb_fd_set_bittiming_fast,
.dev_get_can_channel_id = pcan_usb_fd_get_can_channel_id,
.dev_set_can_channel_id = pcan_usb_fd_set_can_channel_id,
.dev_decode_buf = pcan_usb_fd_decode_buf,
.dev_start = pcan_usb_fd_start,
.dev_stop = pcan_usb_fd_stop,
......
......@@ -6,10 +6,10 @@
* Copyright (C) 2003-2011 PEAK System-Technik GmbH
* Copyright (C) 2011-2012 Stephane Grosjean <s.grosjean@peak-system.com>
*/
#include <linux/ethtool.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/usb.h>
#include <linux/module.h>
#include <linux/ethtool.h>
#include <linux/can.h>
#include <linux/can/dev.h>
......@@ -76,6 +76,7 @@ static u16 pcan_usb_pro_sizeof_rec[256] = {
[PCAN_USBPRO_SETFILTR] = sizeof(struct pcan_usb_pro_filter),
[PCAN_USBPRO_SETTS] = sizeof(struct pcan_usb_pro_setts),
[PCAN_USBPRO_GETDEVID] = sizeof(struct pcan_usb_pro_devid),
[PCAN_USBPRO_SETDEVID] = sizeof(struct pcan_usb_pro_devid),
[PCAN_USBPRO_SETLED] = sizeof(struct pcan_usb_pro_setled),
[PCAN_USBPRO_RXMSG8] = sizeof(struct pcan_usb_pro_rxmsg),
[PCAN_USBPRO_RXMSG4] = sizeof(struct pcan_usb_pro_rxmsg) - 4,
......@@ -149,6 +150,7 @@ static int pcan_msg_add_rec(struct pcan_usb_pro_msg *pm, int id, ...)
case PCAN_USBPRO_SETBTR:
case PCAN_USBPRO_GETDEVID:
case PCAN_USBPRO_SETDEVID:
*pc++ = va_arg(ap, int);
pc += 2;
*(__le32 *)pc = cpu_to_le32(va_arg(ap, u32));
......@@ -419,8 +421,8 @@ static int pcan_usb_pro_set_led(struct peak_usb_device *dev, u8 mode,
return pcan_usb_pro_send_cmd(dev, &um);
}
static int pcan_usb_pro_get_device_id(struct peak_usb_device *dev,
u32 *device_id)
static int pcan_usb_pro_get_can_channel_id(struct peak_usb_device *dev,
u32 *can_ch_id)
{
struct pcan_usb_pro_devid *pdn;
struct pcan_usb_pro_msg um;
......@@ -439,11 +441,23 @@ static int pcan_usb_pro_get_device_id(struct peak_usb_device *dev,
return err;
pdn = (struct pcan_usb_pro_devid *)pc;
*device_id = le32_to_cpu(pdn->dev_num);
*can_ch_id = le32_to_cpu(pdn->dev_num);
return err;
}
static int pcan_usb_pro_set_can_channel_id(struct peak_usb_device *dev,
u32 can_ch_id)
{
struct pcan_usb_pro_msg um;
pcan_msg_init_empty(&um, dev->cmd_buf, PCAN_USB_MAX_CMD_LEN);
pcan_msg_add_rec(&um, PCAN_USBPRO_SETDEVID, dev->ctrl_idx,
can_ch_id);
return pcan_usb_pro_send_cmd(dev, &um);
}
static int pcan_usb_pro_set_bittiming(struct peak_usb_device *dev,
struct can_bittiming *bt)
{
......@@ -1023,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,
};
/*
......@@ -1076,7 +1093,8 @@ const struct peak_usb_adapter pcan_usb_pro = {
.dev_free = pcan_usb_pro_free,
.dev_set_bus = pcan_usb_pro_set_bus,
.dev_set_bittiming = pcan_usb_pro_set_bittiming,
.dev_get_device_id = pcan_usb_pro_get_device_id,
.dev_get_can_channel_id = pcan_usb_pro_get_can_channel_id,
.dev_set_can_channel_id = pcan_usb_pro_set_can_channel_id,
.dev_decode_buf = pcan_usb_pro_decode_buf,
.dev_encode_msg = pcan_usb_pro_encode_msg,
.dev_start = pcan_usb_pro_start,
......
......@@ -62,6 +62,7 @@ struct __packed pcan_usb_pro_fwinfo {
#define PCAN_USBPRO_SETBTR 0x02
#define PCAN_USBPRO_SETBUSACT 0x04
#define PCAN_USBPRO_SETSILENT 0x05
#define PCAN_USBPRO_SETDEVID 0x06
#define PCAN_USBPRO_SETFILTR 0x0a
#define PCAN_USBPRO_SETTS 0x10
#define PCAN_USBPRO_GETDEVID 0x12
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment