Author: hselasky
Date: Thu Oct  8 07:19:57 2015
New Revision: 289009
URL: https://svnweb.freebsd.org/changeset/base/289009

Log:
  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-f...@scoubidou.com>
  PR:           203249

Modified:
  stable/9/share/man/man4/usb_quirk.4
  stable/9/sys/dev/usb/quirk/usb_quirk.c
Directory Properties:
  stable/9/share/   (props changed)
  stable/9/share/man/   (props changed)
  stable/9/share/man/man4/   (props changed)
  stable/9/sys/   (props changed)
  stable/9/sys/dev/   (props changed)

Modified: stable/9/share/man/man4/usb_quirk.4
==============================================================================
--- stable/9/share/man/man4/usb_quirk.4 Thu Oct  8 07:18:28 2015        
(r289008)
+++ stable/9/share/man/man4/usb_quirk.4 Thu Oct  8 07:19:57 2015        
(r289009)
@@ -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

Modified: stable/9/sys/dev/usb/quirk/usb_quirk.c
==============================================================================
--- stable/9/sys/dev/usb/quirk/usb_quirk.c      Thu Oct  8 07:18:28 2015        
(r289008)
+++ stable/9/sys/dev/usb/quirk/usb_quirk.c      Thu Oct  8 07:19:57 2015        
(r289009)
@@ -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_QUI
 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
        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;
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to