Commit a30cdaf1 authored by Nicolas Saenz Julienne's avatar Nicolas Saenz Julienne Committed by Greg Kroah-Hartman

HID: core: move Usage Page concatenation to Main item

[ Upstream commit 58e75155 ]

As seen on some USB wireless keyboards manufactured by Primax, the HID
parser was using some assumptions that are not always true. In this case
it's s the fact that, inside the scope of a main item, an Usage Page
will always precede an Usage.

The spec is not pretty clear as 6.2.2.7 states "Any usage that follows
is interpreted as a Usage ID and concatenated with the Usage Page".
While 6.2.2.8 states "When the parser encounters a main item it
concatenates the last declared Usage Page with a Usage to form a
complete usage value." Being somewhat contradictory it was decided to
match Window's implementation, which follows 6.2.2.8.

In summary, the patch moves the Usage Page concatenation from the local
item parsing function to the main item parsing function.
Signed-off-by: default avatarNicolas Saenz Julienne <nsaenzjulienne@suse.de>
Reviewed-by: default avatarTerry Junge <terry.junge@poly.com>
Signed-off-by: default avatarBenjamin Tissoires <benjamin.tissoires@redhat.com>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent 653117ea
...@@ -218,13 +218,14 @@ static unsigned hid_lookup_collection(struct hid_parser *parser, unsigned type) ...@@ -218,13 +218,14 @@ static unsigned hid_lookup_collection(struct hid_parser *parser, unsigned type)
* Add a usage to the temporary parser table. * Add a usage to the temporary parser table.
*/ */
static int hid_add_usage(struct hid_parser *parser, unsigned usage) static int hid_add_usage(struct hid_parser *parser, unsigned usage, u8 size)
{ {
if (parser->local.usage_index >= HID_MAX_USAGES) { if (parser->local.usage_index >= HID_MAX_USAGES) {
hid_err(parser->device, "usage index exceeded\n"); hid_err(parser->device, "usage index exceeded\n");
return -1; return -1;
} }
parser->local.usage[parser->local.usage_index] = usage; parser->local.usage[parser->local.usage_index] = usage;
parser->local.usage_size[parser->local.usage_index] = size;
parser->local.collection_index[parser->local.usage_index] = parser->local.collection_index[parser->local.usage_index] =
parser->collection_stack_ptr ? parser->collection_stack_ptr ?
parser->collection_stack[parser->collection_stack_ptr - 1] : 0; parser->collection_stack[parser->collection_stack_ptr - 1] : 0;
...@@ -486,10 +487,7 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item) ...@@ -486,10 +487,7 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
return 0; return 0;
} }
if (item->size <= 2) return hid_add_usage(parser, data, item->size);
data = (parser->global.usage_page << 16) + data;
return hid_add_usage(parser, data);
case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM: case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM:
...@@ -498,9 +496,6 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item) ...@@ -498,9 +496,6 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
return 0; return 0;
} }
if (item->size <= 2)
data = (parser->global.usage_page << 16) + data;
parser->local.usage_minimum = data; parser->local.usage_minimum = data;
return 0; return 0;
...@@ -511,9 +506,6 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item) ...@@ -511,9 +506,6 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
return 0; return 0;
} }
if (item->size <= 2)
data = (parser->global.usage_page << 16) + data;
count = data - parser->local.usage_minimum; count = data - parser->local.usage_minimum;
if (count + parser->local.usage_index >= HID_MAX_USAGES) { if (count + parser->local.usage_index >= HID_MAX_USAGES) {
/* /*
...@@ -533,7 +525,7 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item) ...@@ -533,7 +525,7 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
} }
for (n = parser->local.usage_minimum; n <= data; n++) for (n = parser->local.usage_minimum; n <= data; n++)
if (hid_add_usage(parser, n)) { if (hid_add_usage(parser, n, item->size)) {
dbg_hid("hid_add_usage failed\n"); dbg_hid("hid_add_usage failed\n");
return -1; return -1;
} }
...@@ -547,6 +539,22 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item) ...@@ -547,6 +539,22 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
return 0; return 0;
} }
/*
* Concatenate Usage Pages into Usages where relevant:
* As per specification, 6.2.2.8: "When the parser encounters a main item it
* concatenates the last declared Usage Page with a Usage to form a complete
* usage value."
*/
static void hid_concatenate_usage_page(struct hid_parser *parser)
{
int i;
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;
}
/* /*
* Process a main item. * Process a main item.
*/ */
...@@ -556,6 +564,8 @@ static int hid_parser_main(struct hid_parser *parser, struct hid_item *item) ...@@ -556,6 +564,8 @@ static int hid_parser_main(struct hid_parser *parser, struct hid_item *item)
__u32 data; __u32 data;
int ret; int ret;
hid_concatenate_usage_page(parser);
data = item_udata(item); data = item_udata(item);
switch (item->tag) { switch (item->tag) {
...@@ -765,6 +775,8 @@ static int hid_scan_main(struct hid_parser *parser, struct hid_item *item) ...@@ -765,6 +775,8 @@ static int hid_scan_main(struct hid_parser *parser, struct hid_item *item)
__u32 data; __u32 data;
int i; int i;
hid_concatenate_usage_page(parser);
data = item_udata(item); data = item_udata(item);
switch (item->tag) { switch (item->tag) {
......
...@@ -417,6 +417,7 @@ struct hid_global { ...@@ -417,6 +417,7 @@ struct hid_global {
struct hid_local { struct hid_local {
unsigned usage[HID_MAX_USAGES]; /* usage array */ unsigned usage[HID_MAX_USAGES]; /* usage array */
u8 usage_size[HID_MAX_USAGES]; /* usage size array */
unsigned collection_index[HID_MAX_USAGES]; /* collection index array */ unsigned collection_index[HID_MAX_USAGES]; /* collection index array */
unsigned usage_index; unsigned usage_index;
unsigned usage_minimum; unsigned usage_minimum;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment