HID: Scan the device for group info before adding it
authorHenrik Rydberg <rydberg@euromail.se>
Mon, 23 Apr 2012 10:07:03 +0000 (12:07 +0200)
committerJiri Kosina <jkosina@suse.cz>
Tue, 1 May 2012 10:54:54 +0000 (12:54 +0200)
In order to allow the report descriptor to influence the hid device
properties, one needs to parse the descriptor early, without reference
to any driver. Scan the descriptor for group information during device
add, before the device has been broadcast to userland. The device
modalias will contain group information which can be used to
differentiate between modules. For starters, just handle the generic
group.

Signed-off-by: Henrik Rydberg <rydberg@euromail.se>
Acked-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
drivers/hid/hid-core.c
include/linux/hid.h

index cfcb69eb17d6d67b3fdd9f42c25f5c1dfe1d8f55..f932cd533d49de507fcae1be7cc14989428691e1 100644 (file)
@@ -658,6 +658,58 @@ static u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item)
        return NULL;
 }
 
+static void hid_scan_usage(struct hid_device *hid, u32 usage)
+{
+}
+
+/*
+ * Scan a report descriptor before the device is added to the bus.
+ * Sets device groups and other properties that determine what driver
+ * to load.
+ */
+static int hid_scan_report(struct hid_device *hid)
+{
+       unsigned int page = 0, delim = 0;
+       __u8 *start = hid->dev_rdesc;
+       __u8 *end = start + hid->dev_rsize;
+       unsigned int u, u_min = 0, u_max = 0;
+       struct hid_item item;
+
+       hid->group = HID_GROUP_GENERIC;
+       while ((start = fetch_item(start, end, &item)) != NULL) {
+               if (item.format != HID_ITEM_FORMAT_SHORT)
+                       return -EINVAL;
+               if (item.type == HID_ITEM_TYPE_GLOBAL) {
+                       if (item.tag == HID_GLOBAL_ITEM_TAG_USAGE_PAGE)
+                               page = item_udata(&item) << 16;
+               } else if (item.type == HID_ITEM_TYPE_LOCAL) {
+                       if (delim > 1)
+                               break;
+                       u = item_udata(&item);
+                       if (item.size <= 2)
+                               u += page;
+                       switch (item.tag) {
+                       case HID_LOCAL_ITEM_TAG_DELIMITER:
+                               delim += !!u;
+                               break;
+                       case HID_LOCAL_ITEM_TAG_USAGE:
+                               hid_scan_usage(hid, u);
+                               break;
+                       case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM:
+                               u_min = u;
+                               break;
+                       case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM:
+                               u_max = u;
+                               for (u = u_min; u <= u_max; u++)
+                                       hid_scan_usage(hid, u);
+                               break;
+                       }
+               }
+       }
+
+       return 0;
+}
+
 /**
  * hid_parse_report - parse device report
  *
@@ -2171,6 +2223,16 @@ int hid_add_device(struct hid_device *hdev)
        if (!hdev->dev_rdesc)
                return -ENODEV;
 
+       /*
+        * Scan generic devices for group information
+        */
+       if (hid_ignore_special_drivers ||
+           !hid_match_id(hdev, hid_have_special_driver)) {
+               ret = hid_scan_report(hdev);
+               if (ret)
+                       hid_warn(hdev, "bad device descriptor (%d)\n", ret);
+       }
+
        /* XXX hack, any other cleaner solution after the driver core
         * is converted to allow more than 20 bytes as the device name? */
        dev_set_name(&hdev->dev, "%04X:%04X:%04X.%04X", hdev->bus,
index a0e27ddb887d915670291de43819b574eab24886..f45d33d0c121323b771b316844a5aed2a4cd91e3 100644 (file)
@@ -324,6 +324,11 @@ struct hid_item {
 #define HID_QUIRK_NO_IGNORE                    0x40000000
 #define HID_QUIRK_NO_INPUT_SYNC                        0x80000000
 
+/*
+ * HID device groups
+ */
+#define HID_GROUP_GENERIC                      0x0001
+
 /*
  * This is the global environment of the parser. This information is
  * persistent for main-items. The global environment can be saved and