DORMANTno replies

[PATCH 4/7] usbhid: add hid_quirks module parameter

From: Paul Walmsley <hidden>
Date: 2007-03-19 04:22:00

From: Paul Walmsley <redacted>

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

Signed-off-by: Paul Walmsley <redacted>

---
  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;
  }
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help