OSDN Git Service

Merge Linux 4.4.206-rc1 into 10
[sagit-ice-cold/kernel_xiaomi_msm8998.git] / drivers / hid / hid-core.c
index fd8cd41..6eaab5c 100644 (file)
@@ -197,6 +197,18 @@ static unsigned hid_lookup_collection(struct hid_parser *parser, unsigned type)
 }
 
 /*
+ * Concatenate usage which defines 16 bits or less with the
+ * currently defined usage page to form a 32 bit usage
+ */
+
+static void complete_usage(struct hid_parser *parser, unsigned int index)
+{
+       parser->local.usage[index] &= 0xFFFF;
+       parser->local.usage[index] |=
+               (parser->global.usage_page & 0xFFFF) << 16;
+}
+
+/*
  * Add a usage to the temporary parser table.
  */
 
@@ -207,6 +219,14 @@ static int hid_add_usage(struct hid_parser *parser, unsigned usage, u8 size)
                return -1;
        }
        parser->local.usage[parser->local.usage_index] = usage;
+
+       /*
+        * If Usage item only includes usage id, concatenate it with
+        * currently defined usage page
+        */
+       if (size <= 2)
+               complete_usage(parser, parser->local.usage_index);
+
        parser->local.usage_size[parser->local.usage_index] = size;
        parser->local.collection_index[parser->local.usage_index] =
                parser->collection_stack_ptr ?
@@ -346,6 +366,8 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
 
        case HID_GLOBAL_ITEM_TAG_USAGE_PAGE:
                parser->global.usage_page = item_udata(item);
+               if (parser->local.usage_page_preceding == 1)
+                       parser->local.usage_page_preceding = 2;
                return 0;
 
        case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM:
@@ -523,13 +545,32 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
  * usage value."
  */
 
-static void hid_concatenate_usage_page(struct hid_parser *parser)
+static void hid_concatenate_last_usage_page(struct hid_parser *parser)
 {
        int i;
+       unsigned int usage_page;
+       unsigned int current_page;
 
-       for (i = 0; i < parser->local.usage_index; i++)
-               if (parser->local.usage_size[i] <= 2)
-                       parser->local.usage[i] += parser->global.usage_page << 16;
+       if (!parser->local.usage_index)
+               return;
+
+       usage_page = parser->global.usage_page;
+
+       /*
+        * Concatenate usage page again only if last declared Usage Page
+        * has not been already used in previous usages concatenation
+        */
+       for (i = parser->local.usage_index - 1; i >= 0; i--) {
+               if (parser->local.usage_size[i] > 2)
+                       /* Ignore extended usages */
+                       continue;
+
+               current_page = parser->local.usage[i] >> 16;
+               if (current_page == usage_page)
+                       break;
+
+               complete_usage(parser, i);
+       }
 }
 
 /*
@@ -541,7 +582,7 @@ static int hid_parser_main(struct hid_parser *parser, struct hid_item *item)
        __u32 data;
        int ret;
 
-       hid_concatenate_usage_page(parser);
+       hid_concatenate_last_usage_page(parser);
 
        data = item_udata(item);
 
@@ -756,7 +797,7 @@ static int hid_scan_main(struct hid_parser *parser, struct hid_item *item)
        __u32 data;
        int i;
 
-       hid_concatenate_usage_page(parser);
+       hid_concatenate_last_usage_page(parser);
 
        data = item_udata(item);
 
@@ -959,6 +1000,7 @@ int hid_open_report(struct hid_device *device)
        __u8 *start;
        __u8 *buf;
        __u8 *end;
+       __u8 *next;
        int ret;
        static int (*dispatch_type[])(struct hid_parser *parser,
                                      struct hid_item *item) = {
@@ -1012,7 +1054,8 @@ int hid_open_report(struct hid_device *device)
        device->collection_size = HID_DEFAULT_NUM_COLLECTIONS;
 
        ret = -EINVAL;
-       while ((start = fetch_item(start, end, &item)) != NULL) {
+       while ((next = fetch_item(start, end, &item)) != NULL) {
+               start = next;
 
                if (item.format != HID_ITEM_FORMAT_SHORT) {
                        hid_err(device, "unexpected long global item\n");
@@ -1041,7 +1084,8 @@ int hid_open_report(struct hid_device *device)
                }
        }
 
-       hid_err(device, "item fetching failed at offset %d\n", (int)(end - start));
+       hid_err(device, "item fetching failed at offset %u/%u\n",
+               size - (unsigned int)(end - start), size);
 err:
        vfree(parser);
        hid_close_report(device);