Skip to content

Commit 58e7515

Browse files
Nicolas Saenz Juliennebentiss
authored andcommitted
HID: core: move Usage Page concatenation to Main item
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: Nicolas Saenz Julienne <[email protected]> Reviewed-by: Terry Junge <[email protected]> Signed-off-by: Benjamin Tissoires <[email protected]>
1 parent c6400e5 commit 58e7515

File tree

2 files changed

+25
-12
lines changed

2 files changed

+25
-12
lines changed

drivers/hid/hid-core.c

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -218,13 +218,14 @@ static unsigned hid_lookup_collection(struct hid_parser *parser, unsigned type)
218218
* Add a usage to the temporary parser table.
219219
*/
220220

221-
static int hid_add_usage(struct hid_parser *parser, unsigned usage)
221+
static int hid_add_usage(struct hid_parser *parser, unsigned usage, u8 size)
222222
{
223223
if (parser->local.usage_index >= HID_MAX_USAGES) {
224224
hid_err(parser->device, "usage index exceeded\n");
225225
return -1;
226226
}
227227
parser->local.usage[parser->local.usage_index] = usage;
228+
parser->local.usage_size[parser->local.usage_index] = size;
228229
parser->local.collection_index[parser->local.usage_index] =
229230
parser->collection_stack_ptr ?
230231
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)
486487
return 0;
487488
}
488489

489-
if (item->size <= 2)
490-
data = (parser->global.usage_page << 16) + data;
491-
492-
return hid_add_usage(parser, data);
490+
return hid_add_usage(parser, data, item->size);
493491

494492
case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM:
495493

@@ -498,9 +496,6 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
498496
return 0;
499497
}
500498

501-
if (item->size <= 2)
502-
data = (parser->global.usage_page << 16) + data;
503-
504499
parser->local.usage_minimum = data;
505500
return 0;
506501

@@ -511,9 +506,6 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
511506
return 0;
512507
}
513508

514-
if (item->size <= 2)
515-
data = (parser->global.usage_page << 16) + data;
516-
517509
count = data - parser->local.usage_minimum;
518510
if (count + parser->local.usage_index >= HID_MAX_USAGES) {
519511
/*
@@ -533,7 +525,7 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
533525
}
534526

535527
for (n = parser->local.usage_minimum; n <= data; n++)
536-
if (hid_add_usage(parser, n)) {
528+
if (hid_add_usage(parser, n, item->size)) {
537529
dbg_hid("hid_add_usage failed\n");
538530
return -1;
539531
}
@@ -547,6 +539,22 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
547539
return 0;
548540
}
549541

542+
/*
543+
* Concatenate Usage Pages into Usages where relevant:
544+
* As per specification, 6.2.2.8: "When the parser encounters a main item it
545+
* concatenates the last declared Usage Page with a Usage to form a complete
546+
* usage value."
547+
*/
548+
549+
static void hid_concatenate_usage_page(struct hid_parser *parser)
550+
{
551+
int i;
552+
553+
for (i = 0; i < parser->local.usage_index; i++)
554+
if (parser->local.usage_size[i] <= 2)
555+
parser->local.usage[i] += parser->global.usage_page << 16;
556+
}
557+
550558
/*
551559
* Process a main item.
552560
*/
@@ -556,6 +564,8 @@ static int hid_parser_main(struct hid_parser *parser, struct hid_item *item)
556564
__u32 data;
557565
int ret;
558566

567+
hid_concatenate_usage_page(parser);
568+
559569
data = item_udata(item);
560570

561571
switch (item->tag) {
@@ -765,6 +775,8 @@ static int hid_scan_main(struct hid_parser *parser, struct hid_item *item)
765775
__u32 data;
766776
int i;
767777

778+
hid_concatenate_usage_page(parser);
779+
768780
data = item_udata(item);
769781

770782
switch (item->tag) {

include/linux/hid.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,7 @@ struct hid_global {
417417

418418
struct hid_local {
419419
unsigned usage[HID_MAX_USAGES]; /* usage array */
420+
u8 usage_size[HID_MAX_USAGES]; /* usage size array */
420421
unsigned collection_index[HID_MAX_USAGES]; /* collection index array */
421422
unsigned usage_index;
422423
unsigned usage_minimum;

0 commit comments

Comments
 (0)