Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 16 Apr 2008 18:54:02 +0200
From:      Hans Petter Selasky <hselasky@c2i.net>
To:        freebsd-usb@freebsd.org, Maurice Castro <maurice@castro.aus.net>
Cc:        FreeBSD-gnats-submit@freebsd.org
Subject:   Re: usb/122819: Patch to provide dynamic additions to the usb quirks table
Message-ID:  <200804161854.03438.hselasky@c2i.net>
In-Reply-To: <200804161454.m3GEs2Ha005211@atum.castro.aus.net>
References:  <200804161454.m3GEs2Ha005211@atum.castro.aus.net>

next in thread | previous in thread | raw e-mail | index | archive | help
Hi,

Maybe you can prefix the string version of the quirks by the USB module name ?

For example:

MS_REVZ -> UMS_REVZ
AU_NO_FRAC -> UAUDIO_NO_FRAC

Then it is easier to know where the quirk belongs.

Add a short description of what the quirk does in the "usb" manpage.

There might be a race condition reading and writing the quirks. Probably a 
mutex is appropriate.

You can improve the quirk string to numerical conversion code by using binary 
search.

Else your patch looks fine to me.

--HPS

On Wednesday 16 April 2008, Maurice Castro wrote:
> >Number:         122819
> >Category:       usb
> >Synopsis:       Patch to provide dynamic additions to the usb quirks table
> >Confidential:   no
> >Severity:       non-critical
> >Priority:       medium
> >Responsible:    freebsd-usb
> >State:          open
> >Quarter:
> >Keywords:
> >Date-Required:
> >Class:          change-request
> >Submitter-Id:   current-users
> >Arrival-Date:   Wed Apr 16 15:40:01 UTC 2008
> >Closed-Date:
> >Last-Modified:
> >Originator:     Maurice Castro
> >Release:        FreeBSD 7.0-RELEASE i386
> >Organization:
> >Environment:
>
> System: FreeBSD atum.castro.aus.net 7.0-RELEASE FreeBSD 7.0-RELEASE #9: Wed
> Apr 16 17:42:53 EST 2008
> root@atum.castro.aus.net:/scratch/src/sys/i386/compile/USBTEST i386
>
>
>
> 	FreeBSD all versions
>
> >Description:
>
> 	The current USB implementation uses a quirks table that is compiled
> 	into the kernel. Under most circumstances this approach works.
> 	However, some manufacturers of USB devices have reused keys used
> 	in the table and hence changing the compiled in table will result
> 	in an inappropriate entries being present. A localised method of
> 	changing the quirks values without recompiling their kernel would
> 	assist developers and users facing this problem. USB developers in
> 	particular can benefit from the ability to prevent a device
> 	inappropriately identifying itself as a hid device without having
> 	to recompile their kernel.
>
> 	The supplied patch uses entries in the kernel environment to create
> 	a dynamic quirks table. The data is available at boot time so
> 	devices that are connected across a reboot are correctly handled.
> 	This table can also be updated after boot time by calling an IOCTL.
>
> >How-To-Repeat:
> >Fix:
>
> diff -ur /usr/src/share/man/man4/usb.4 /scratch/src/share/man/man4/usb.4
> --- /usr/src/share/man/man4/usb.4	2008-04-11 22:43:31.000000000 +1000
> +++ /scratch/src/share/man/man4/usb.4	2008-04-17 00:23:48.000000000 +1000
> @@ -288,6 +288,66 @@
>  .Em DANGEROUS
>  and should be used with great care since it
>  can destroy the bus integrity.
> +.It Dv USB_SETDYNQUIRKS
> +This command will cause the dynamic quirks table to be rebuilt from the
> +contents of the kernel environment. Environment strings of the form
> +.Pp
> +.Ic usb.quirk.N="VENDOR PRODUCT REVISION FLAGS"
> +.Pp
> +where
> +.Ic N
> +is a number between 0 and 9 and quirks must be numbered contiguously;
> +.Ic VENDOR PRODUCT
> +and
> +.Ic REVISION
> +are constants that identify the device (the value 0xffff for
> +.Ic REVISION
> +denotes all revisions); and
> +.Ic FLAGS
> +is any combination of
> +.Bl -bullet -offset indent -compact
> +.It
> +SWAP_UNICODE
> +.It
> +MS_REVZ
> +.It
> +NO_STRINGS
> +.It
> +BAD_ADC
> +.It
> +BUS_POWERED
> +.It
> +BAD_AUDIO
> +.It
> +SPUR_BUT_UP
> +.It
> +AU_NO_XU
> +.It
> +POWER_CLAIM
> +.It
> +AU_NO_FRAC
> +.It
> +AU_INP_ASYNC
> +.It
> +BROKEN_BIDIR
> +.It
> +OPEN_CLEARSTALL
> +.It
> +HID_IGNORE
> +.It
> +KBD_IGNORE
> +.It
> +MS_BAD_CLASS
> +.It
> +MS_LEADING_BYTE
> +.El
> +separated by "|" characters. These lines set the quirks for each device
> +identified.
> +.Pp
> +The dynamic quirks table is designed to supplement the quirks table built
> +in to the kernel. It is of particular use to developers working with
> devices +that inappropriately share vendor, product and revision
> information and hence +cannot be correctly added in to the kernel's quirks
> table.
>  .El
>  .Pp
>  The include file
> diff -ur /usr/src/sys/dev/usb/usb.c /scratch/src/sys/dev/usb/usb.c
> --- /usr/src/sys/dev/usb/usb.c	2008-04-11 22:43:56.000000000 +1000
> +++ /scratch/src/sys/dev/usb/usb.c	2008-04-16 23:23:55.000000000 +1000
> @@ -668,6 +668,10 @@
>  		*(struct usb_device_stats *)data = sc->sc_bus->stats;
>  		break;
>
> +	case USB_SETDYNQUIRKS:
> +    		usbd_populate_dynamic_quirks();
> +		break;
> +
>  	default:
>  		return (EINVAL);
>  	}
> diff -ur /usr/src/sys/dev/usb/usb.h /scratch/src/sys/dev/usb/usb.h
> --- /usr/src/sys/dev/usb/usb.h	2008-04-11 22:43:56.000000000 +1000
> +++ /scratch/src/sys/dev/usb/usb.h	2008-04-16 23:22:34.000000000 +1000
> @@ -673,6 +673,7 @@
>  #define USB_DISCOVER		_IO  ('U', 3)
>  #define USB_DEVICEINFO		_IOWR('U', 4, struct usb_device_info)
>  #define USB_DEVICESTATS		_IOR ('U', 5, struct usb_device_stats)
> +#define USB_SETDYNQUIRKS	_IO  ('U', 6)
>
>  /* Generic HID device */
>  #define USB_GET_REPORT_DESC	_IOR ('U', 21, struct usb_ctl_report_desc)
> diff -ur /usr/src/sys/dev/usb/usb_quirks.c
> /scratch/src/sys/dev/usb/usb_quirks.c ---
> /usr/src/sys/dev/usb/usb_quirks.c	2008-04-11 22:43:56.000000000 +1000 +++
> /scratch/src/sys/dev/usb/usb_quirks.c	2008-04-16 17:42:12.000000000 +1000
> @@ -42,8 +42,11 @@
>
>  #include <sys/param.h>
>  #include <sys/systm.h>
> +#include <sys/kernel.h>
> +#include <sys/ctype.h>
>
>  #include <dev/usb/usb.h>
> +#include <sys/kenv.h>
>
>  #include "usbdevs.h"
>  #include <dev/usb/usb_quirks.h>
> @@ -54,12 +57,14 @@
>
>  #define ANY 0xffff
>
> -static const struct usbd_quirk_entry {
> +struct usbd_quirk_entry {
>  	u_int16_t idVendor;
>  	u_int16_t idProduct;
>  	u_int16_t bcdDevice;
>  	struct usbd_quirks quirks;
> -} usb_quirks[] = {
> +};
> +
> +static struct usbd_quirk_entry usb_quirks[] = {
>   { USB_VENDOR_INSIDEOUT, USB_PRODUCT_INSIDEOUT_EDGEPORT4,
>     						    0x094, { UQ_SWAP_UNICODE}},
>   { USB_VENDOR_DALLAS, USB_PRODUCT_DALLAS_J6502,	    0x0a2, { UQ_BAD_ADC
> }}, @@ -117,15 +122,24 @@
>
>  const struct usbd_quirks usbd_no_quirk = { 0 };
>
> -const struct usbd_quirks *
> -usbd_find_quirk(usb_device_descriptor_t *d)
> +#define MAX_DYNAMIC_USB_QUIRKS 10
> +#define ENVNAMEROOT "usb.quirk."
> +#define SEPCHAR "|"
> +
> +static struct usbd_quirk_entry dynamic_usb_quirks[MAX_DYNAMIC_USB_QUIRKS];
> +
> +static struct usbd_quirks *
> +usbd_search_quirk(struct usbd_quirk_entry *quirks, usb_device_descriptor_t
> *d); +
> +static struct usbd_quirks *
> +usbd_search_quirk(struct usbd_quirk_entry *quirks, usb_device_descriptor_t
> *d) {
> -	const struct usbd_quirk_entry *t;
> +	struct usbd_quirk_entry *t;
>  	u_int16_t vendor = UGETW(d->idVendor);
>  	u_int16_t product = UGETW(d->idProduct);
>  	u_int16_t revision = UGETW(d->bcdDevice);
>
> -	for (t = usb_quirks; t->idVendor != 0; t++) {
> +	for (t = quirks; t->idVendor != 0; t++) {
>  		if (t->idVendor  == vendor &&
>  		    t->idProduct == product &&
>  		    (t->bcdDevice == ANY || t->bcdDevice == revision))
> @@ -139,3 +153,134 @@
>  #endif
>  	return (&t->quirks);
>  }
> +
> +const struct usbd_quirks *
> +usbd_find_quirk(usb_device_descriptor_t *d)
> +{
> +	struct usbd_quirks *quirks;
> +	/* check the dynamic quirks list first for local entries */
> +	quirks = usbd_search_quirk((struct usbd_quirk_entry *)
> dynamic_usb_quirks, d); +	if (quirks->uq_flags != 0)
> +		return quirks;
> +	/* check the compiled in quirks list if dynamic entry not set */
> +	return(usbd_search_quirk((struct usbd_quirk_entry *) usb_quirks, d));
> +}
> +
> +void usbd_populate_dynamic_quirks()
> +{
> +	/* the size of envkey must exceed the length of ENVNAMEROOT */
> +	/* and the maximum number of digits in MAX_DYNAMIC_USB_QUIRKS plus 1 */
> +	/* as the environment size is limitted to 512 entries and a maximum */
> +	/* of 128 usb devices are supported 3 digits is appropriate for
> +	all valid values of MAX_DYNAMIC_USB_QUIRKS */
> +	const int envkeysz = strlen(ENVNAMEROOT)+3+1;
> +	char envkey[envkeysz];
> +	int i;
> +	char *env;
> +	char *pt;
> +	char *e;
> +	int n;
> +	for (i=0; i<MAX_DYNAMIC_USB_QUIRKS; i++)
> +	{
> +		dynamic_usb_quirks[i].quirks.uq_flags = 0;
> +		dynamic_usb_quirks[i].idVendor = 0;
> +		dynamic_usb_quirks[i].idProduct = 0;
> +		dynamic_usb_quirks[i].bcdDevice = 0;
> +		snprintf(envkey,envkeysz,"%s%d",ENVNAMEROOT,i);
> +		if (testenv(envkey))
> +		{
> +#ifdef USB_DEBUG
> +			printf("usbd config %s\n", envkey);
> +#endif
> +			env = getenv(envkey);
> +			dynamic_usb_quirks[i].idVendor = strtoul(env, &pt, 0);
> +			dynamic_usb_quirks[i].idProduct = strtoul(pt, &pt, 0);
> +			dynamic_usb_quirks[i].bcdDevice = strtoul(pt, &pt, 0);
> +			/* skip anything which isn't a flag */
> +			while (*pt && !isalpha(*pt)) pt++;
> +			/* read in flags */
> +			while (*pt)
> +			{
> +				e = strstr(pt,SEPCHAR);
> +				if (!e)
> +				{
> +					n = strlen(pt);
> +				}
> +				else
> +				{
> +					n = e - pt;
> +				}
> +				if (!strncmp("SWAP_UNICODE", pt, n))
> +					dynamic_usb_quirks[i].quirks.uq_flags |= UQ_SWAP_UNICODE;
> +				if (!strncmp("MS_REVZ", pt, n))
> +					dynamic_usb_quirks[i].quirks.uq_flags |= UQ_MS_REVZ;
> +				if (!strncmp("NO_STRINGS", pt, n))
> +					dynamic_usb_quirks[i].quirks.uq_flags |= UQ_NO_STRINGS;
> +				if (!strncmp("BAD_ADC", pt, n))
> +					dynamic_usb_quirks[i].quirks.uq_flags |= UQ_BAD_ADC;
> +				if (!strncmp("BUS_POWERED", pt, n))
> +					dynamic_usb_quirks[i].quirks.uq_flags |= UQ_BUS_POWERED;
> +				if (!strncmp("BAD_AUDIO", pt, n))
> +					dynamic_usb_quirks[i].quirks.uq_flags |= UQ_BAD_AUDIO;
> +				if (!strncmp("SPUR_BUT_UP", pt, n))
> +					dynamic_usb_quirks[i].quirks.uq_flags |= UQ_SPUR_BUT_UP;
> +				if (!strncmp("AU_NO_XU", pt, n))
> +					dynamic_usb_quirks[i].quirks.uq_flags |= UQ_AU_NO_XU;
> +				if (!strncmp("POWER_CLAIM", pt, n))
> +					dynamic_usb_quirks[i].quirks.uq_flags |= UQ_POWER_CLAIM;
> +				if (!strncmp("AU_NO_FRAC", pt, n))
> +					dynamic_usb_quirks[i].quirks.uq_flags |= UQ_AU_NO_FRAC;
> +				if (!strncmp("AU_INP_ASYNC", pt, n))
> +					dynamic_usb_quirks[i].quirks.uq_flags |= UQ_AU_INP_ASYNC;
> +				if (!strncmp("BROKEN_BIDIR", pt, n))
> +					dynamic_usb_quirks[i].quirks.uq_flags |= UQ_BROKEN_BIDIR;
> +				if (!strncmp("OPEN_CLEARSTALL", pt, n))
> +					dynamic_usb_quirks[i].quirks.uq_flags |= UQ_OPEN_CLEARSTALL;
> +				if (!strncmp("HID_IGNORE", pt, n))
> +					dynamic_usb_quirks[i].quirks.uq_flags |= UQ_HID_IGNORE;
> +				if (!strncmp("KBD_IGNORE", pt, n))
> +					dynamic_usb_quirks[i].quirks.uq_flags |= UQ_KBD_IGNORE;
> +				if (!strncmp("MS_BAD_CLASS", pt, n))
> +					dynamic_usb_quirks[i].quirks.uq_flags |= UQ_MS_BAD_CLASS;
> +				if (!strncmp("MS_LEADING_BYTE", pt, n))
> +					dynamic_usb_quirks[i].quirks.uq_flags |= UQ_MS_LEADING_BYTE;
> +				pt += n;
> +				pt += strspn(pt, SEPCHAR);
> +			}
> +#ifdef USB_DEBUG
> +			printf("usbd quirk %d %x %x %x %x\n",
> +				dynamic_usb_quirks[i].quirks.uq_flags,
> +				dynamic_usb_quirks[i].idVendor,
> +				dynamic_usb_quirks[i].idProduct,
> +				dynamic_usb_quirks[i].bcdDevice,
> +				dynamic_usb_quirks[i].quirks.uq_flags
> +				);
> +#endif
> +			freeenv(env);
> +		}
> +		else
> +		{
> +			break;
> +		}
> +	}
> +	for (; i<MAX_DYNAMIC_USB_QUIRKS; i++)
> +	{
> +#ifdef USB_DEBUG
> +		printf("usbd clear dynamic quirk %d\n", i);
> +#endif
> +		dynamic_usb_quirks[i].quirks.uq_flags = 0;
> +		dynamic_usb_quirks[i].idVendor = 0;
> +		dynamic_usb_quirks[i].idProduct = 0;
> +		dynamic_usb_quirks[i].bcdDevice = 0;
> +		snprintf(envkey,envkeysz,"%s%d",ENVNAMEROOT,i);
> +		if (testenv(envkey))
> +		{
> +#ifdef USB_DEBUG
> +			printf("usbd key %s invalid as earlier entry missing\n", envkey);
> +#endif
> +		}
> +	}
> +}
> +
> +SYSINIT(usbd_populate_dynamic_quirks, SI_SUB_KLD, SI_ORDER_MIDDLE,
> +    usbd_populate_dynamic_quirks, NULL);
> diff -ur /usr/src/sys/dev/usb/usb_quirks.h
> /scratch/src/sys/dev/usb/usb_quirks.h ---
> /usr/src/sys/dev/usb/usb_quirks.h	2008-04-11 22:43:56.000000000 +1000 +++
> /scratch/src/sys/dev/usb/usb_quirks.h	2008-04-16 11:09:35.000000000 +1000
> @@ -62,3 +62,5 @@
>  extern const struct usbd_quirks usbd_no_quirk;
>
>  const struct usbd_quirks *usbd_find_quirk(usb_device_descriptor_t *);
> +
> +void usbd_populate_dynamic_quirks(void);
>
> >Release-Note:
> >Audit-Trail:
> >Unformatted:
>
> _______________________________________________
> freebsd-usb@freebsd.org mailing list
> http://lists.freebsd.org/mailman/listinfo/freebsd-usb
> To unsubscribe, send any mail to "freebsd-usb-unsubscribe@freebsd.org"





Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200804161854.03438.hselasky>