Skip to content

Commit 6c92c11

Browse files
committed
Track HID report offsets per report ID per USB spec
Per USB HID spec section 6.2.2.7, each report ID defines an independent report structure with its own bit offset tracking. Previously, offsets were reset when encountering a new report ID or at collection boundaries, causing incorrect parsing of descriptors with multiple reports. Changes: - Add report_offset_map_t to map report IDs to their offsets - Track offsets separately for each report ID in parser state - Remove incorrect offset resets on report ID change and collection end - Add helper functions for offset lookup and creation Local items still reset after Main items and Global items persist, matching spec requirements.
1 parent e317f16 commit 6c92c11

File tree

2 files changed

+39
-9
lines changed

2 files changed

+39
-9
lines changed

src/hid_parser.c

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,39 @@ uint32_t get_descriptor_value(uint8_t const *report, int size) {
3333
}
3434
}
3535

36+
uint32_t *get_or_create_report_offset(parser_state_t *parser, uint8_t report_id) {
37+
for (int i = 0; i < parser->num_report_offsets; i++) {
38+
if (parser->report_offsets[i].report_id == report_id) {
39+
return &parser->report_offsets[i].offset_in_bits;
40+
}
41+
}
42+
43+
if (parser->num_report_offsets < MAX_REPORTS) {
44+
parser->report_offsets[parser->num_report_offsets].report_id = report_id;
45+
parser->report_offsets[parser->num_report_offsets].offset_in_bits = 0;
46+
return &parser->report_offsets[parser->num_report_offsets++].offset_in_bits;
47+
}
48+
49+
return NULL;
50+
}
51+
52+
uint32_t get_current_offset(parser_state_t *parser) {
53+
uint32_t *offset = get_or_create_report_offset(parser, parser->report_id);
54+
return offset ? *offset : 0;
55+
}
56+
3657
void update_usage(parser_state_t *parser, int i) {
3758
/* If we don't have as many usages as elements, the usage for the previous element applies */
3859
if (i > 0 && i >= parser->usage_count && i < HID_MAX_USAGES)
3960
*(parser->p_usage + i) = *(parser->p_usage + i - 1);
4061
}
4162

4263
void store_element(parser_state_t *parser, report_val_t *val, int i, uint32_t data, uint16_t size, hid_interface_t *iface) {
64+
uint32_t current_offset = get_current_offset(parser);
65+
4366
*val = (report_val_t){
44-
.offset = parser->offset_in_bits,
45-
.offset_idx = parser->offset_in_bits >> 3,
67+
.offset = current_offset,
68+
.offset_idx = current_offset >> 3,
4669
.size = size,
4770

4871
.usage_max = parser->locals[RI_LOCAL_USAGE_MAX].val,
@@ -62,8 +85,6 @@ void store_element(parser_state_t *parser, report_val_t *val, int i, uint32_t da
6285

6386
void handle_global_item(parser_state_t *parser, item_t *item) {
6487
if (item->hdr.tag == RI_GLOBAL_REPORT_ID) {
65-
/* Reset offset for a new page */
66-
parser->offset_in_bits = 0;
6788
parser->report_id = item->val;
6889
}
6990

@@ -98,6 +119,10 @@ void handle_main_input(parser_state_t *parser, item_t *item, hid_interface_t *if
98119
count = 1;
99120
}
100121

122+
uint32_t *current_offset = get_or_create_report_offset(parser, parser->report_id);
123+
if (!current_offset)
124+
return;
125+
101126
for (int i = 0; i < count; i++) {
102127
update_usage(parser, i);
103128
store_element(parser, &val, i, item->val, size, iface);
@@ -106,7 +131,7 @@ void handle_main_input(parser_state_t *parser, item_t *item, hid_interface_t *if
106131
extract_data(iface, &val);
107132

108133
/* Iterate <count> times and increase offset by <size> amount, moving by <count> x <size> bits */
109-
parser->offset_in_bits += size;
134+
*current_offset += size;
110135
}
111136

112137
/* Advance the usage array pointer by global report count and reset the count variable */
@@ -117,9 +142,6 @@ void handle_main_input(parser_state_t *parser, item_t *item, hid_interface_t *if
117142
}
118143

119144
void handle_main_item(parser_state_t *parser, item_t *item, hid_interface_t *iface) {
120-
if (IS_BLOCK_END)
121-
parser->offset_in_bits = 0;
122-
123145
switch (item->hdr.tag) {
124146
case RI_MAIN_COLLECTION:
125147
parser->collection.start++;

src/include/hid_parser.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ typedef struct {
3838
uint8_t end;
3939
} collection_t;
4040

41+
/* Maps a report ID to its current bit offset */
42+
typedef struct {
43+
uint8_t report_id;
44+
uint32_t offset_in_bits;
45+
} report_offset_map_t;
46+
4147
/* Header byte is unpacked to size/type/tag using this struct */
4248
typedef struct TU_ATTR_PACKED {
4349
uint8_t size : 2;
@@ -149,13 +155,15 @@ typedef struct {
149155
int report_id; /* Report ID of the current section we're parsing */
150156

151157
uint32_t usage_count;
152-
uint32_t offset_in_bits;
153158
uint16_t usages[HID_MAX_USAGES];
154159
uint16_t *p_usage;
155160
uint16_t global_usage;
156161

157162
collection_t collection;
158163

164+
report_offset_map_t report_offsets[MAX_REPORTS];
165+
uint8_t num_report_offsets;
166+
159167
/* as tag is 4 bits, there can be 16 different tags in global header type */
160168
item_t globals[16];
161169

0 commit comments

Comments
 (0)