diff options
author | hselasky <hselasky@FreeBSD.org> | 2015-10-08 07:17:35 +0000 |
---|---|---|
committer | hselasky <hselasky@FreeBSD.org> | 2015-10-08 07:17:35 +0000 |
commit | 60e5f4dda1ebe3af7e222b97c3eb6de34c5e38ea (patch) | |
tree | 065cd3bfdccd0ec909618e3fe9be3e651393bf8a | |
parent | 221f5f4a8e3034bb64122f7ddcfa04d18457de30 (diff) | |
download | FreeBSD-src-60e5f4dda1ebe3af7e222b97c3eb6de34c5e38ea.zip FreeBSD-src-60e5f4dda1ebe3af7e222b97c3eb6de34c5e38ea.tar.gz |
MFC r288180:
Implement support for reading USB quirks from the kernel environment.
Refer to the usb_quirk(4) manual page for more details on how to use
this new feature.
Submitted by: Maxime Soule <btik-fbsd@scoubidou.com>
PR: 203249
-rw-r--r-- | share/man/man4/usb_quirk.4 | 57 | ||||
-rw-r--r-- | sys/dev/usb/quirk/usb_quirk.c | 139 |
2 files changed, 192 insertions, 4 deletions
diff --git a/share/man/man4/usb_quirk.4 b/share/man/man4/usb_quirk.4 index 5b3f01c..e22bbc8 100644 --- a/share/man/man4/usb_quirk.4 +++ b/share/man/man4/usb_quirk.4 @@ -16,7 +16,7 @@ .\" .\" $FreeBSD$ .\" -.Dd May 7, 2015 +.Dd September 24, 2015 .Dt USB_QUIRK 4 .Os .Sh NAME @@ -177,7 +177,53 @@ ejects after HID command .Pp See .Pa /sys/dev/usb/quirk/usb_quirk.h -for the complete list of supported quirks. +or run "usbconfig dump_quirk_names" for the complete list of supported quirks. +.Sh LOADER TUNABLE +The following tunable can be set at the +.Xr loader 8 +prompt before booting the kernel, or stored in +.Xr loader.conf 5 . +.Bl -tag -width indent +.It Va hw.usb.quirk.%d +The value is a string whose format is: +.Bd -literal -offset indent +.Qo VendorId ProductId LowRevision HighRevision UQ_QUIRK,... Qc +.Ed +.Pp +Installs the quirks +.Ic UQ_QUIRK,... +for all USB devices matching +.Ic VendorId , +.Ic ProductId +and has a hardware revision between and including +.Ic LowRevision +and +.Ic HighRevision . +.Pp +.Ic VendorId , +.Ic ProductId , +.Ic LowRevision +and +.Ic HighRevision +are all 16 bits numbers which can be decimal or hexadecimal based. +.Pp +A maximum of 100 variables +.Ic hw.usb.quirk.0, .1, ..., .99 +can be defined. +.Pp +If a matching entry is found in the kernel's internal quirks table, it +is replaced by the new definition. +.Pp +Else a new entry is created given that the quirk table is not full. +.Pp +The kernel iterates over the +.Ic hw.usb.quirk.N +variables starting at +.Ic N = 0 +and stops at +.Ic N = 99 +or the first non-existing one. +.El .Sh EXAMPLES After attaching a .Nm u3g @@ -186,6 +232,13 @@ device which appears as a USB device on .Bd -literal -offset indent usbconfig -d ugen0.3 add_quirk UQ_MSC_EJECT_WAIT .Ed +.Pp +To install a quirk at boot time, place one or several lines like the +following in +.Xr loader.conf 5 : +.Bd -literal -offset indent +hw.usb.quirk.0="0x04d9 0xfa50 0 0xffff UQ_KBD_IGNORE" +.Ed .Sh SEE ALSO .Xr usbconfig 8 .Sh HISTORY diff --git a/sys/dev/usb/quirk/usb_quirk.c b/sys/dev/usb/quirk/usb_quirk.c index c756b24..c4395a2 100644 --- a/sys/dev/usb/quirk/usb_quirk.c +++ b/sys/dev/usb/quirk/usb_quirk.c @@ -61,6 +61,7 @@ MODULE_VERSION(usb_quirk, 1); #define USB_DEV_QUIRKS_MAX 384 #define USB_SUB_QUIRKS_MAX 8 +#define USB_QUIRK_ENVROOT "hw.usb.quirk." struct usb_quirk_entry { uint16_t vid; @@ -607,8 +608,32 @@ static const char *usb_quirk_str[USB_QUIRK_MAX] = { static const char * usb_quirkstr(uint16_t quirk) { - return ((quirk < USB_QUIRK_MAX) ? - usb_quirk_str[quirk] : "USB_QUIRK_UNKNOWN"); + return ((quirk < USB_QUIRK_MAX && usb_quirk_str[quirk] != NULL) ? + usb_quirk_str[quirk] : "UQ_UNKNOWN"); +} + +/*------------------------------------------------------------------------* + * usb_strquirk + * + * This function converts a string into a USB quirk code. + * + * Returns: + * Less than USB_QUIRK_MAX: Quirk code + * Else: Quirk code not found + *------------------------------------------------------------------------*/ +static uint16_t +usb_strquirk(const char *str, size_t len) +{ + const char *quirk; + uint16_t x; + + for (x = 0; x != USB_QUIRK_MAX; x++) { + quirk = usb_quirkstr(x); + if (strncmp(str, quirk, len) == 0 && + quirk[len] == 0) + break; + } + return (x); } /*------------------------------------------------------------------------* @@ -853,12 +878,122 @@ usb_quirk_ioctl(unsigned long cmd, caddr_t data, return (ENOIOCTL); } +/*------------------------------------------------------------------------* + * usb_quirk_strtou16 + * + * Helper function to scan a 16-bit integer. + *------------------------------------------------------------------------*/ +static uint16_t +usb_quirk_strtou16(const char **pptr, const char *name, const char *what) +{ + unsigned long value; + char *end; + + value = strtoul(*pptr, &end, 0); + if (value > 65535 || *pptr == end || (*end != ' ' && *end != '\t')) { + printf("%s: %s 16-bit %s value set to zero\n", + name, what, *end == 0 ? "incomplete" : "invalid"); + return (0); + } + *pptr = end + 1; + return ((uint16_t)value); +} + +/*------------------------------------------------------------------------* + * usb_quirk_add_entry_from_str + * + * Add a USB quirk entry from string. + * "VENDOR PRODUCT LO_REV HI_REV QUIRK[,QUIRK[,...]]" + *------------------------------------------------------------------------*/ +static void +usb_quirk_add_entry_from_str(const char *name, const char *env) +{ + struct usb_quirk_entry entry = { }; + struct usb_quirk_entry *new; + uint16_t quirk_idx; + uint16_t quirk; + const char *end; + + /* check for invalid environment variable */ + if (name == NULL || env == NULL) + return; + + if (bootverbose) + printf("Adding USB QUIRK '%s' = '%s'\n", name, env); + + /* parse device information */ + entry.vid = usb_quirk_strtou16(&env, name, "Vendor ID"); + entry.pid = usb_quirk_strtou16(&env, name, "Product ID"); + entry.lo_rev = usb_quirk_strtou16(&env, name, "Low revision"); + entry.hi_rev = usb_quirk_strtou16(&env, name, "High revision"); + + /* parse quirk information */ + quirk_idx = 0; + while (*env != 0 && quirk_idx != USB_SUB_QUIRKS_MAX) { + /* skip whitespace before quirks */ + while (*env == ' ' || *env == '\t') + env++; + + /* look for quirk separation character */ + end = strchr(env, ','); + if (end == NULL) + end = env + strlen(env); + + /* lookup quirk in string table */ + quirk = usb_strquirk(env, end - env); + if (quirk < USB_QUIRK_MAX) { + entry.quirks[quirk_idx++] = quirk; + } else { + printf("%s: unknown USB quirk '%.*s' (skipped)\n", + name, (int)(end - env), env); + } + env = end; + + /* skip quirk delimiter, if any */ + if (*env != 0) + env++; + } + + /* register quirk */ + if (quirk_idx != 0) { + if (*env != 0) { + printf("%s: Too many USB quirks, only %d allowed!\n", + name, USB_SUB_QUIRKS_MAX); + } + mtx_lock(&usb_quirk_mtx); + new = usb_quirk_get_entry(entry.vid, entry.pid, + entry.lo_rev, entry.hi_rev, 1); + if (new == NULL) + printf("%s: USB quirks table is full!\n", name); + else + memcpy(new->quirks, entry.quirks, sizeof(entry.quirks)); + mtx_unlock(&usb_quirk_mtx); + } else { + printf("%s: No USB quirks found!\n", name); + } +} + static void usb_quirk_init(void *arg) { + char envkey[sizeof(USB_QUIRK_ENVROOT) + 2]; /* 2 digits max, 0 to 99 */ + int i; + /* initialize mutex */ mtx_init(&usb_quirk_mtx, "USB quirk", NULL, MTX_DEF); + /* look for quirks defined by the environment variable */ + for (i = 0; i != 100; i++) { + snprintf(envkey, sizeof(envkey), USB_QUIRK_ENVROOT "%d", i); + + /* Stop at first undefined var */ + if (!testenv(envkey)) + break; + + /* parse environment variable */ + usb_quirk_add_entry_from_str(envkey, getenv(envkey)); + } + /* register our function */ usb_test_quirk_p = &usb_test_quirk_by_info; usb_quirk_ioctl_p = &usb_quirk_ioctl; |