From: Paul Walmsley <[EMAIL PROTECTED]>

Add a new module parameter, usbhid.hid_quirks, which can be used to
modify the HID quirks list.

Signed-off-by: Paul Walmsley <[EMAIL PROTECTED]>

---
 dev/drivers/usb/input/hid-core.c |  131 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 131 insertions(+)

Index: linux/dev/drivers/usb/input/hid-core.c
===================================================================
--- linux.orig/dev/drivers/usb/input/hid-core.c
+++ linux/dev/drivers/usb/input/hid-core.c
@@ -55,6 +55,31 @@ module_param_named(mousepoll, hid_mousep
 MODULE_PARM_DESC(mousepoll, "Polling interval of mice");

 /*
+ * MAX_USBHID_QUIRK_LEN: maximum length limit for a single HID quirk entry.
+ * 3 16-bit hex numbers and 1 32-bit hex number, each prefixed with 0x
+ * and space-separated, terminated by a newline or comma
+ */
+#define MAX_USBHID_QUIRK_LEN (3*(2+4+1)+1*(2+8)+1)
+/*
+ * Increase this if you need to configure lots of HID quirks at boot
+ */
+static char param_hid_quirks[MAX_USBHID_QUIRK_LEN * 4];
+/*
+ * Example of hid_quirks usage: passing "hid_quirks=0xcd5:0x1:0xffff:0x4" at
+ * boot tells the USB HID driver to ignore (0x4) vendor ID 0x0cd5, product
+ * ID 0x0001.  The 0xffff is a mask that is ANDed with the product ID
+ * before testing.  0xffff will match against only one product ID, but
+ * lesser values will cause the quirk to cover multiple products by the
+ * same vendor. HID quirk values are defined in include/linux/hid.h
+ */
+module_param_string(hid_quirks, param_hid_quirks, sizeof(param_hid_quirks), 0);
+MODULE_PARM_DESC(hid_quirks, "Modify USB HID quirks by specifying "
+                " hid_quirks=vendorID:productID:productMask:quirks"
+                "[,v:p:m:q ...] where vendorID, productID, productMask,"
+                " and quirks are all in 0x-prefixed hexadecimal");
+
+
+/*
  * Input submission and I/O error handler.
  */

@@ -1070,6 +1095,94 @@ static void hid_fixup_s510_descriptor(un
 }

 /**
+ * modify_quirk_list: add/replace/remove a HID quirk from the quirks list
+ * @quirk_buf: a text string describing the HID quirk
+ *
+ * Description:
+ *     Given a string containing a HID quirk description -
+ *     a 16-bit hexadecimal vendor ID prefixed by 0x, a colon,
+ *     a 16-bit hexadecimal product ID prefixed by 0x,  a colon, and
+ *     a 16-bit hexadecimal product ID mask prefixed by 0x,  a colon, and
+ *     a 32-bit hexadecimal quirks bitfield prefixed by 0x -
+ *     test to see if it exists in the quirks_list.  If not, the
+ *     new quirk data is added.  Otherwise, if the quirks bitfield is 0,
+ *     remove the quirk from the list; if the quirks bitfield is non-zero,
+ *     replace the existing quirk information with the user-specified
+ *     quirks.
+ *
+ * Returns: 0 OK, -error on failure.
+ */
+static int modify_quirk_list(const char *quirk_buf)
+{
+       struct quirks_list_struct *q_new, *q;
+       int list_edited = 0, m = 0;
+       char c;
+
+       /* Allocate space for the quirks_list_struct */
+       q_new = kmalloc(sizeof(struct quirks_list_struct), GFP_KERNEL);
+       if (!q_new) {
+               dbg("Could not allocate quirks_list_struct");
+               return -ENOMEM;
+       }
+
+       /* Parse the quirk_buf string into a quirks_list_struct */
+       m = sscanf(quirk_buf, "0x%hx%c0x%hx%c0x%hx%c0x%x",
+                  &q_new->hid_bl_item.idVendor, &c,
+                  &q_new->hid_bl_item.idProduct, &c,
+                  &q_new->hid_bl_item.idProductMask, &c,
+                  &q_new->hid_bl_item.quirks);
+       if (m != 7) {
+               dbg("modify_quirk_list parse error");
+               kfree(q_new);
+               return -EINVAL;
+       }
+
+       /* Iterate through the list looking for this (idVendor, idProduct,
+        * idProductMask) triple.  If we find it, delete or replace the list
+        * entry (depending on the contents of quirks).  Otherwise, add the
+        * entry at the end of the list.
+        */
+       down_write(&quirks_list_rwsem);
+       list_for_each_entry(q, &quirks_list, node) {
+               if (q->hid_bl_item.idVendor == q_new->hid_bl_item.idVendor &&
+                   q->hid_bl_item.idProduct == q_new->hid_bl_item.idProduct &&
+                   q->hid_bl_item.idProductMask == 
q_new->hid_bl_item.idProductMask) {
+                       /*
+                        * This entry already exists in the list -
+                        * delete or replace the old entry
+                        */
+                       if (q_new->hid_bl_item.quirks == 0) {
+                               list_del(&q->node);
+                               kfree(q_new);
+                       } else {
+                               list_replace(&q->node, &q_new->node);
+                       }
+
+                       kfree(q);
+                       list_edited = 1;
+                       break;
+               }
+       }
+
+       /* If we didn't edit the list, we mustn't have found an existing
+        * entry, so add the new quirk to the end of the list
+        */
+       if (!list_edited)
+               if (q_new->hid_bl_item.quirks != 0) {
+                       list_add_tail(&q_new->node, &quirks_list);
+               } else {
+                       /* Cannot add a quirk of 0x0 */
+                       kfree(q_new);
+                       return -EINVAL;
+               }
+
+       up_write(&quirks_list_rwsem);
+
+       return 0;
+}
+
+
+/**
  * usbhid_quirks_init: copy the hid_blacklist[] into the quirks_list
  *
  * Description:
@@ -1081,6 +1194,8 @@ static void hid_fixup_s510_descriptor(un
 static int usbhid_quirks_init(void)
 {
        struct quirks_list_struct *q;
+       char *quirk_buf = param_hid_quirks;
+       char *next_quirk_buf = param_hid_quirks;
        int i;

        for (i = 0; hid_blacklist[i].idVendor != 0; i++) {
@@ -1097,6 +1212,22 @@ static int usbhid_quirks_init(void)
                list_add_tail(&q->node, &quirks_list);
        }

+       /*
+        * Read the hid_quirks module parameter list and modify quirks
+        * appropriately
+        */
+       while (*quirk_buf != '\0') {
+               if (modify_quirk_list(quirk_buf) != 0)
+                       printk("Could not parse HID quirk module parameter\n");
+
+               /* Find next comma */
+               next_quirk_buf = strchr(quirk_buf, ',');
+               if (!next_quirk_buf)
+                       break;
+
+               quirk_buf = next_quirk_buf + 1;
+       }
+
        return 0;
 }

Reply via email to