Re: [PATCH v6 5/5] drivers: Implement module modaliases for USB

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




Le 02/12/2022 à 23:47, Allen Webb a écrit :
> Add the per-subsystem logic needed to print match-based modaliases to
> the USB subsystem, so the modalias sysfs attribute for modules will
> function for modules that register USB drivers.
> 
> Signed-off-by: Allen Webb <allenwebb@xxxxxxxxxx>
> ---
>   drivers/base/Makefile          |   2 +-
>   drivers/base/base.h            |   8 +
>   drivers/base/mod_devicetable.c | 257 +++++++++++++++++++++++++++++++++
>   drivers/usb/core/driver.c      |   2 +
>   4 files changed, 268 insertions(+), 1 deletion(-)
>   create mode 100644 drivers/base/mod_devicetable.c
> 
> diff --git a/drivers/base/Makefile b/drivers/base/Makefile
> index 83217d243c25b..924d46ae987f4 100644
> --- a/drivers/base/Makefile
> +++ b/drivers/base/Makefile
> @@ -15,7 +15,7 @@ obj-y				+= firmware_loader/
>   obj-$(CONFIG_NUMA)	+= node.o
>   obj-$(CONFIG_MEMORY_HOTPLUG) += memory.o
>   ifeq ($(CONFIG_SYSFS),y)
> -obj-$(CONFIG_MODULES)	+= module.o
> +obj-$(CONFIG_MODULES)	+= mod_devicetable.o module.o
>   endif
>   obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o
>   obj-$(CONFIG_REGMAP)	+= regmap/
> diff --git a/drivers/base/base.h b/drivers/base/base.h
> index b902d1ecc247f..fec56271104fa 100644
> --- a/drivers/base/base.h
> +++ b/drivers/base/base.h
> @@ -173,6 +173,14 @@ static inline void module_add_driver(struct module *mod,
>   static inline void module_remove_driver(struct device_driver *drv) { }
>   #endif
>   
> +#if defined(CONFIG_SYSFS) && defined(CONFIG_MODULES)
> +ssize_t usb_drv_to_modalias(struct device_driver *drv, char *buf,
> +			    size_t count);
> +#else
> +static inline ssize_t usb_drv_to_modalias(struct device_driver *drv, char *buf,
> +					  size_t count) { return -EINVAL; }
> +#endif
> +
>   #ifdef CONFIG_DEVTMPFS
>   extern int devtmpfs_init(void);
>   #else
> diff --git a/drivers/base/mod_devicetable.c b/drivers/base/mod_devicetable.c
> new file mode 100644
> index 0000000000000..d7f198aad430f
> --- /dev/null
> +++ b/drivers/base/mod_devicetable.c
> @@ -0,0 +1,257 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * mod_devicetable.c - helpers for displaying modaliases through sysfs.
> + *
> + * This borrows a lot from file2alias.c
> + */
> +
> +#include <linux/device/bus.h>
> +#include <linux/device.h>
> +#include <linux/usb.h>
> +
> +#include "base.h"
> +#include "../usb/core/usb.h"
> +
> +/* Helper macro to add a modalias field to the string buffer associated with
> + * a match id.
> + *
> + * Note that:
> + *   + len should be a ssize_t and is modified in the macro
> + *   + sep should be a string literal and is concatenated as part of a format
> + *     string
> + *   + field is the struct field of the match id
> + */
> +#define ADD(buf, count, len, sep, cond, field)				\
> +do {				                                        \
> +	char *buf_ = buf;						\
> +	size_t count_ = count;                                          \
> +	if (cond)							\
> +		(len) += scnprintf(&buf_[len],				\
> +			count_ - (len),					\
> +			sizeof(field) == 1 ? (sep "%02X") :		\
> +			sizeof(field) == 2 ? (sep "%04X") :		\
> +			sizeof(field) == 4 ? (sep "%08X") : "",		\
> +			(field));					\
> +	else								\
> +		(len) += scnprintf(&buf_[len], count_ - (len), (sep "*")); \
> +} while (0)

I think it would be better in your macro returns the updated len instead 
of updating it in place.

> +
> +#ifdef CONFIG_USB
> +/* USB related modaliases can be split because of device number matching, so
> + * this function handles individual modaliases for one segment of the range.
> + */
> +static ssize_t usb_id_to_modalias(const struct usb_device_id *id,
> +				  unsigned int bcdDevice_initial,

No camelCase please.

See https://docs.kernel.org/process/coding-style.html#naming

> +				  int bcdDevice_initial_digits,
> +				  unsigned char range_lo,
> +				  unsigned char range_hi,
> +				  unsigned char max, const char *mod_name,
> +				  char *buf, size_t count)
> +{
> +	ssize_t len = 0;
> +
> +	ADD(buf, count, len, "alias usb:v",
> +	    id->match_flags & USB_DEVICE_ID_MATCH_VENDOR, id->idVendor);
> +	ADD(buf, count, len, "p", id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT,
> +	    id->idProduct);
> +
> +	len += scnprintf(&buf[len], count - len, "d");
> +	if (bcdDevice_initial_digits)
> +		len += scnprintf(&buf[len], count - len, "%0*X",
> +			bcdDevice_initial_digits, bcdDevice_initial);
> +	if (range_lo == range_hi) {
> +		len += scnprintf(&buf[len], count - len, "%X", range_lo);
> +	} else if (range_lo > 0 || range_hi < max) {
> +		if (range_lo > 0x9 || range_hi < 0xA) {
> +			len += scnprintf(&buf[len], count - len, "[%X-%X]",
> +					 range_lo, range_hi);
> +		} else {
> +			len += scnprintf(&buf[len], count - len,
> +				range_lo < 0x9 ? "[%X-9" : "[%X",
> +				range_lo);

Can it fit on 2 lines ?

> +			len += scnprintf(&buf[len], count - len,
> +				range_hi > 0xA ? "A-%X]" : "%X]",
> +				range_hi);

Same ?

> +		}
> +	}
> +	if (bcdDevice_initial_digits < (sizeof(id->bcdDevice_lo) * 2 - 1))
> +		len += scnprintf(&buf[len], count - len, "*");
> +
> +	ADD(buf, count, len, "dc",
> +	    id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS, id->bDeviceClass);
> +	ADD(buf, count, len, "dsc",
> +	    id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS,
> +	    id->bDeviceSubClass);
> +	ADD(buf, count, len, "dp",
> +	    id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL,
> +	    id->bDeviceProtocol);
> +	ADD(buf, count, len, "ic",
> +	    id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS,
> +	    id->bInterfaceClass);
> +	ADD(buf, count, len, "isc",
> +	    id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS,
> +	    id->bInterfaceSubClass);
> +	ADD(buf, count, len, "ip",
> +	    id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL,
> +	    id->bInterfaceProtocol);
> +	ADD(buf, count, len, "in",
> +	    id->match_flags & USB_DEVICE_ID_MATCH_INT_NUMBER,
> +	    id->bInterfaceNumber);
> +
> +	len += scnprintf(&buf[len], count - len, " %s\n", mod_name);
> +	return len;
> +}
> +
> +/* Handles increment/decrement of BCD formatted integers */
> +/* Returns the previous value, so it works like i++ or i-- */
> +static unsigned int incbcd(unsigned int *bcd,
> +			   int inc,
> +			   unsigned char max,
> +			   size_t chars)
> +{
> +	unsigned int init = *bcd, i, j;
> +	unsigned long long c, dec = 0, div;

Can you simplify this function with helpers from include/linux/bcd.h ?

> +
> +	/* If bcd is not in BCD format, just increment */
> +	if (max > 0x9) {
> +		*bcd += inc;
> +		return init;
> +	}
> +
> +	/* Convert BCD to Decimal */
> +	for (i = 0 ; i < chars ; i++) {
> +		c = (*bcd >> (i << 2)) & 0xf;
> +		c = c > 9 ? 9 : c; /* force to bcd just in case */
> +		for (j = 0 ; j < i ; j++)
> +			c = c * 10;
> +		dec += c;
> +	}
> +
> +	/* Do our increment/decrement */
> +	dec += inc;
> +	*bcd  = 0;
> +
> +	/* Convert back to BCD */
> +	for (i = 0 ; i < chars ; i++) {
> +		for (c = 1, j = 0 ; j < i ; j++)
> +			c = c * 10;
> +		div = dec;
> +		(void)do_div(div, c); /* div = div / c */
> +		c = do_div(div, 10); /* c = div % 10; div = div / 10 */
> +		*bcd += c << (i << 2);
> +	}
> +	return init;
> +}
> +
> +/* Print the modaliases for the specified struct usb_device_id. */
> +static ssize_t usb_id_to_modalias_multi(const struct usb_device_id *id,
> +					const char *mod_name, char *buf,
> +					size_t count)
> +{
> +	ssize_t len = 0;
> +	unsigned int devlo, devhi;
> +	unsigned char chi, clo, max;
> +	int ndigits;
> +
> +	devlo = id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO ?
> +		id->bcdDevice_lo : 0x0U;
> +	devhi = id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI ?
> +		id->bcdDevice_hi : ~0x0U;
> +
> +	/* Figure out if this entry is in bcd or hex format */
> +	max = 0x9; /* Default to decimal format */
> +	for (ndigits = 0 ; ndigits < sizeof(id->bcdDevice_lo) * 2 ; ndigits++) {
> +		clo = (devlo >> (ndigits << 2)) & 0xf;
> +		chi = ((devhi > 0x9999 ? 0x9999 : devhi) >>
> +		       (ndigits << 2)) & 0xf;
> +		if (clo > max || chi > max) {
> +			max = 0xf;
> +			break;
> +		}
> +	}
> +
> +	/*
> +	 * Some modules (visor) have empty slots as placeholder for
> +	 * run-time specification that results in catch-all alias
> +	 */
> +	if (!(id->idVendor || id->idProduct || id->bDeviceClass ||
> +	      id->bInterfaceClass))
> +		return len;
> +
> +	/* Convert numeric bcdDevice range into fnmatch-able pattern(s) */
> +	for (ndigits = sizeof(id->bcdDevice_lo) * 2 - 1; devlo <= devhi;
> +	     ndigits--) {
> +		clo = devlo & 0xf;
> +		chi = devhi & 0xf;
> +		/* If we are in bcd mode, truncate if necessary */
> +		if (chi > max)
> +			chi = max;
> +		devlo >>= 4;
> +		devhi >>= 4;
> +
> +		if (devlo == devhi || !ndigits) {
> +			len += usb_id_to_modalias(id, devlo, ndigits, clo, chi,
> +						  max, mod_name, buf + len,
> +						  count - len);
> +			break;
> +		}
> +
> +		if (clo > 0x0)
> +			len += usb_id_to_modalias(id,
> +				incbcd(&devlo, 1, max,
> +				       sizeof(id->bcdDevice_lo) * 2),
> +				ndigits, clo, max, max, mod_name, buf + len,
> +				count - len);
> +
> +		if (chi < max)
> +			len += usb_id_to_modalias(id,
> +				incbcd(&devhi, -1, max,
> +				       sizeof(id->bcdDevice_lo) * 2),
> +				ndigits, 0x0, chi, max, mod_name, buf + len,
> +				count - len);
> +	}
> +	return len;
> +}
> +
> +/* Print the modaliases for the given driver assumed to be an usb_driver or
> + * usb_device_driver.
> + *
> + * "alias" is prepended and the module name is appended to each modalias to
> + * match the format in modules.aliases.
> + *
> + * The modaliases will be written out to @buf with @count being the maximum
> + * bytes to write. The return value is a negative errno on error or the number
> + * of bytes written to @buf on success.
> + */
> +ssize_t usb_drv_to_modalias(struct device_driver *drv, char *buf,
> +			    size_t count)
> +{
> +	ssize_t len = 0;
> +	const struct usb_device_id *id;
> +	const char *mod_name;
> +
> +	if (drv->bus != &usb_bus_type)
> +		return -EINVAL;
> +
> +	if (drv->owner)
> +		mod_name = drv->owner->name;
> +	else
> +		mod_name = drv->mod_name;
> +
> +	if (is_usb_device_driver(drv))
> +		id = to_usb_device_driver(drv)->id_table;
> +	else
> +		id = to_usb_driver(drv)->id_table;
> +	if (!id)
> +		return len;

Would be more explicit to return 0;

> +
> +	for (; id->match_flags; id++) {
> +		len += usb_id_to_modalias_multi(id, mod_name, buf + len,
> +						count - len);
> +	}
> +	return len;
> +}
> +#else
> +inline ssize_t usb_drv_to_modalias(struct device_driver *drv, char *buf,
> +				   size_t count){ return 0; }

Why inline ? How can that work ?

> +#endif
> diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
> index 7e7e119c253fb..fdbc197b64c9c 100644
> --- a/drivers/usb/core/driver.c
> +++ b/drivers/usb/core/driver.c
> @@ -32,6 +32,7 @@
>   #include <linux/usb/quirks.h>
>   #include <linux/usb/hcd.h>
>   
> +#include "../../base/base.h"
>   #include "usb.h"
>   
>   
> @@ -2030,4 +2031,5 @@ struct bus_type usb_bus_type = {
>   	.match =	usb_device_match,
>   	.uevent =	usb_uevent,
>   	.need_parent_lock =	true,
> +	.drv_to_modalias = usb_drv_to_modalias,
>   };

As far as I can see, usb_drv_to_modalias() is never called outside this 
file, so it shouldn't be defined in a .h and it should be static.




[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux