usb/122819: Patch to provide dynamic additions to the usb
quirks table
Hans Petter Selasky
hselasky at c2i.net
Wed Apr 16 17:52:55 UTC 2008
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 at 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 at freebsd.org mailing list
> http://lists.freebsd.org/mailman/listinfo/freebsd-usb
> To unsubscribe, send any mail to "freebsd-usb-unsubscribe at freebsd.org"
More information about the freebsd-usb
mailing list