Optionally associate reports with usages#88
Conversation
Add support for parsing usages into the report structure, which can be useful for certain HID devices, e.g. if they support LampArray functionality. - [x] Impacts functionality? - [ ] Impacts security? - [ ] Breaking change? - [ ] Includes tests? - [ ] Includes documentation?
|
@microsoft-github-policy-service agree company="TUXEDO Computers GmbH" |
| } | ||
|
|
||
| reports.push(Report { report_id: *id, size_in_bits: bit_position as usize, fields }); | ||
| let usage = report_data.first().and_then(|d| d.member_of.get(1)).map(|c| c.usage); |
There was a problem hiding this comment.
I'm not sure this solves for the general case. If you have lots of nested collections like:
Collection (app)
ReportId (1)
Collection (logical)
Collection (logical)
Collection (logical)
Usage(bar)
Collection (logical)
Usage(foo)
ReportSize(1)
ReportCount(1)
End
End
End
Usage(baz)
ReportSize(1)
ReportCount(1)
End
End
If I follow the code correctly (it's been a while since my head was in this space - so to be 100% confident I'd have to build the descriptor and test it), then the usage associated with the whole report here would be one of the inner collections wrapping the first report field, even though other fields in the same report are not associated with that collection.
I think what you want is something like "usage of the most deeply nested collection that all report fields are members of"? I think that's a bit trickier to compute; but I think something like this would work:
let usage = fields
.iter()
.filter(|f|!matches!(f, ReportField::Padding(_)))
.min_by_key(|f| match f {
ReportField::Variable(v) => v.member_of.len(),
ReportField::Array(a) => a.member_of.len(),
ReportField::Padding(_) => unreachable!(),
})
.and_then(|f| match f {
ReportField::Variable(v) => v.member_of.get(1),
ReportField::Array(a) => a.member_of.get(1),
ReportField::Padding(_) => unreachable!(),
})
.map(|c|c.usage);Also, I recommend giving a more specific name to the Report.usage field to better describe what it is capturing.
There was a problem hiding this comment.
I think what you want is something like "usage of the most deeply nested collection that all report fields are members of"? I think that's a bit trickier to compute; but I think something like this would work:
I think that is a correct description of what I want. To give an example, this is an example LampArray report from the HID spec:
0x05, 0x59, // USAGE_PAGE (LightingAndIllumination)
0x09, 0x01, // USAGE (LampArray)
0xa1, 0x01, // COLLECTION (Application)
0x85, 0x01, // REPORT_ID (1)
0x09, 0x02, // USAGE (LampArrayAttributesReport)
0xa1, 0x02, // COLLECTION (Logical)
0x09, 0x03, // USAGE (LampCount)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x27, 0xff, 0xff, 0x00, 0x00, // LOGICAL_MAXIMUM (65535)
0x75, 0x10, // REPORT_SIZE (16)
0x95, 0x01, // REPORT_COUNT (1)
0xb1, 0x03, // FEATURE (Cnst,Var,Abs)
0x09, 0x04, // USAGE (BoundingBoxWidthInMicrometers)
0x09, 0x05, // USAGE (BoundingBoxHeightInMicrometers)
0x09, 0x06, // USAGE (BoundingBoxDepthInMicrometers)
0x09, 0x07, // USAGE (LampArrayKind)
0x09, 0x08, // USAGE (MinUpdateIntervalInMicroseconds)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x27, 0xff, 0xff, 0xff, 0x7f, // LOGICAL_MAXIMUM (2147483647)
0x75, 0x20, // REPORT_SIZE (32)
0x95, 0x05, // REPORT_COUNT (5)
0xb1, 0x03, // FEATURE (Cnst,Var,Abs)
0xc0, // END_COLLECTION
...
As you can see, the REPORT_ID is directly followed by a USAGE before a nested collection is opened. If I'm not mistaken, it might even be the case that multiple such usages per report are possible.
In any case, parsing the descriptor without associating reports with the following usages it not very useful because the usage indicates which report is used for what.
I will have a closer look at the code you suggested and will check with my use-case over the next couple of days.
There was a problem hiding this comment.
Each field within the report has the full collection chain (i.e. the member_of vec that this code is using should also be available on each report field to the consumer) in addition to the usage for the field itself. On the consumer side, if you are looking at a particular report field within a report, you can evaluate the entire collection chain for that field and see if it has the usage you care about. For example, when processing a report field, you can write logic like "is this report field within a Collection that has LampArray Usage" just by inspecting the data associated with the report field as it is now. But that is per-field, rather than per-report.
I agree that it may be useful to have some notion of what an entire report's "usage" is, but the definition of what a report's "usage" remains a bit unclear (to me, anyway) because of the complicated way that collections can nest and span within and across reports.
The above "usage of the most deeply nested collection that all report fields [of a given report id] are a member of" is my attempt to reduce that to a concrete definition, but I'm open to other well-defined alternatives, if they are more useful.
For example, you could alternatively define a report's "usage" as the set of all usages associated with the report (either as field usages or on collections including or nested within the report), which would allow for membership tests (i.e. "is this Report associated with a "LampArray" usage?" becomes a test on of whether that usage is in the Report usages set).
In any case, I am open to adding usage info at the Report level, if we can settle on a good definition of what that is that works in the general case.
Add support for parsing usages into the
report structure, which can be useful
for certain HID devices, e.g. if they
support LampArray functionality.
How This Was Tested
I tested this with Microsoft RP2040MacropadHidSample on a suitable device: https://github.com/microsoft/RP2040MacropadHidSample
Integration Instructions
N/A