Re: [PATCH] can: etas_es58x: report the firmware version through ethtool

From: Vincent MAILHOL
Date: Fri Nov 04 2022 - 11:36:17 EST


On Fri 4 nov. 2022 at 21:06, Marc Kleine-Budde <mkl@xxxxxxxxxxxxxx> wrote:
> On 04.11.2022 16:36:59, Vincent Mailhol wrote:
> > ES58x devices report below information in their usb product info
> > string:
> >
> > * the firmware version
> > * the bootloader version
> > * the hardware revision
> >
> > Report the firmware version through ethtool_drvinfo::fw_version.
> > Because struct ethtool_drvinfo has no fields to report the boatloader
> > version nor the hardware revision, continue to print these in the
> > kernel log (c.f. es58x_print_product_info()).
> >
> > While doing so, bump up copyright year of each modified files.
> >
> > Signed-off-by: Vincent Mailhol <mailhol.vincent@xxxxxxxxxx>
> > ---
> > drivers/net/can/usb/etas_es58x/es581_4.c | 5 +-
> > drivers/net/can/usb/etas_es58x/es58x_core.c | 140 +++++++++++++-------
> > drivers/net/can/usb/etas_es58x/es58x_core.h | 11 +-
> > drivers/net/can/usb/etas_es58x/es58x_fd.c | 5 +-
> > 4 files changed, 108 insertions(+), 53 deletions(-)
> >
> > diff --git a/drivers/net/can/usb/etas_es58x/es581_4.c b/drivers/net/can/usb/etas_es58x/es581_4.c
> > index 1bcdcece5ec7..1d6ae7b279cf 100644
> > --- a/drivers/net/can/usb/etas_es58x/es581_4.c
> > +++ b/drivers/net/can/usb/etas_es58x/es581_4.c
> > @@ -6,7 +6,7 @@
> > *
> > * Copyright (c) 2019 Robert Bosch Engineering and Business Solutions. All rights reserved.
> > * Copyright (c) 2020 ETAS K.K.. All rights reserved.
> > - * Copyright (c) 2020, 2021 Vincent Mailhol <mailhol.vincent@xxxxxxxxxx>
> > + * Copyright (c) 2020-2022 Vincent Mailhol <mailhol.vincent@xxxxxxxxxx>
> > */
> >
> > #include <linux/kernel.h>
> > @@ -492,7 +492,8 @@ const struct es58x_parameters es581_4_param = {
> > .tx_bulk_max = ES581_4_TX_BULK_MAX,
> > .urb_cmd_header_len = ES581_4_URB_CMD_HEADER_LEN,
> > .rx_urb_max = ES58X_RX_URBS_MAX,
> > - .tx_urb_max = ES58X_TX_URBS_MAX
> > + .tx_urb_max = ES58X_TX_URBS_MAX,
> > + .prod_info_delim = ','
>
> Nitpick: you can add a trailing "," here, makes the diff smaller on the
> next change :)

ACK.

> > };
> >
> > const struct es58x_operators es581_4_ops = {
> > diff --git a/drivers/net/can/usb/etas_es58x/es58x_core.c b/drivers/net/can/usb/etas_es58x/es58x_core.c
> > index 51294b717040..7c20a73169f3 100644
> > --- a/drivers/net/can/usb/etas_es58x/es58x_core.c
> > +++ b/drivers/net/can/usb/etas_es58x/es58x_core.c
> > @@ -7,7 +7,7 @@
> > *
> > * Copyright (c) 2019 Robert Bosch Engineering and Business Solutions. All rights reserved.
> > * Copyright (c) 2020 ETAS K.K.. All rights reserved.
> > - * Copyright (c) 2020, 2021 Vincent Mailhol <mailhol.vincent@xxxxxxxxxx>
> > + * Copyright (c) 2020-2022 Vincent Mailhol <mailhol.vincent@xxxxxxxxxx>
> > */
> >
> > #include <linux/ethtool.h>
> > @@ -1978,10 +1978,6 @@ static const struct net_device_ops es58x_netdev_ops = {
> > .ndo_eth_ioctl = can_eth_ioctl_hwts,
> > };
> >
> > -static const struct ethtool_ops es58x_ethtool_ops = {
> > - .get_ts_info = can_ethtool_op_get_ts_info_hwts,
> > -};
> > -
> > /**
> > * es58x_set_mode() - Change network device mode.
> > * @netdev: CAN network device.
> > @@ -2062,6 +2058,96 @@ static void es58x_init_priv(struct es58x_device *es58x_dev,
> > can->do_set_mode = es58x_set_mode;
> > }
> >
> > +/**
> > + * es58x_get_product_info() - Get the product information.
> > + * @es58x_dev: ES58X device.
> > + * @prod_info: Buffer where to store the product information.
> > + * @prod_info_len: Length of @prod_info.
> > + *
> > + * Do a synchronous call to get the product information.
> > + *
> > + * Return: zero on success, errno when any error occurs.
> > + */
> > +static int es58x_get_product_info(struct es58x_device *es58x_dev,
> > + char *prod_info, size_t prod_info_len)
> > +{
> > + struct usb_device *udev = es58x_dev->udev;
> > + const int es58x_prod_info_idx = 6;
> > + int ret;
> > +
> > + ret = usb_string(udev, es58x_prod_info_idx, prod_info, prod_info_len);
> > + if (ret < 0) {
> > + dev_err(es58x_dev->dev,
> > + "%s: Could not read the product info: %pe\n",
> > + __func__, ERR_PTR(ret));
> > + return ret;
> > + }
> > + if (ret >= prod_info_len - 1) {
> > + dev_warn(es58x_dev->dev,
> > + "%s: Buffer is too small, result might be truncated\n",
> > + __func__);
> > + }
>
> You can use usb_cache_string() that puts the requested string into a
> kmalloc()ed buffer:
>
> | https://elixir.bootlin.com/linux/v6.0/source/drivers/usb/core/message.c#L1018
>
> ...and avoids having the large stack frame.

I saw that one a long time ago, before I started sending patches on
the mailing list, but couldn't use it because it is not
EXPORT_SYMBOL_GPLed. I was also too shy to send a patch to change
it...

I guess I will add the export and use it.

> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * es58x_print_product_info() - Print the product information.
> > + * @es58x_dev: ES58X device.
> > + *
> > + * Return: zero on success, errno when any error occurs.
> > + */
> > +static int es58x_print_product_info(struct es58x_device *es58x_dev)
> > +{
> > + char prod_info[ES58X_USB_STRING_SIZE];
>
> Stack size in the kernel is limited.

'make checkstack' tels me:
0x00000000000008300 es58x_get_drvinfo []: 448
0x00000000000003ae0 es58x_print_product_info []: 448

My understanding is that anything under 512 is still acceptable. c.f.
https://www.kernel.org/doc/html/v4.12/process/submit-checklist.html

Regardless, I agree that using usb_cache_string() is better.

> > + int ret;
> > +
> > + ret = es58x_get_product_info(es58x_dev, prod_info, sizeof(prod_info));
> > + if (ret == 0)
> > + dev_info(es58x_dev->dev, "Product info: %s\n", prod_info);
> > +
> > + return ret;
> > +}
> > +
> > +/**
> > + * es58x_get_drvinfo() - Get the driver name and firmware version.
> > + * @netdev: CAN network device.
> > + * @drvinfo: Driver information.
> > + *
> > + * Populate @drvinfo with the driver name and the firwmware version.
> > + */
> > +static void es58x_get_drvinfo(struct net_device *netdev,
> > + struct ethtool_drvinfo *drvinfo)
> > +{
> > + struct es58x_device *es58x_dev = es58x_priv(netdev)->es58x_dev;
> > + char prod_info[ES58X_USB_STRING_SIZE];
> > + char *start, *end;
> > +
> > + strscpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver));
> > +
> > + if (es58x_get_product_info(es58x_dev, prod_info, sizeof(prod_info)) < 0)
> > + return;
> > +
> > + /* The firmware prefix is either "FW_V" or "FW:" */
> > + start = strstr(prod_info, "FW");
> > + if (!start)
> > + return;
> > + /* Go to first digit */
> > + while (!isdigit(*start))
> > + start++;
>
> Don't trust the input. Check for end of string before accessing it.

You are right. Now I feel ashamed of making this mistake.

> > +
> > + end = strchr(start, es58x_dev->param->prod_info_delim);
> > + if (!end || end - start >= sizeof(drvinfo->fw_version))
> > + return;
> > +
> > + strscpy(drvinfo->fw_version, start, end - start + 1);
>
> Are you misusing strscpy() here? The last parameter should be the size
> of the dest buffer, not the src buffer.

Indeed, the documentation clearly specifies that it should be the size
of the destination. I will use strncpy() instead. I already checked
that (end - start) is strictly smaller than the destination size above
so it will be fine.

Yours sincerely,
Vincent Mailhol