-
Notifications
You must be signed in to change notification settings - Fork 33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add HID descriptors #6
Changes from 1 commit
cef2b8b
2e7f95f
2db2205
cbe13dd
8417f36
9e10dbd
bbc5f67
f292dc9
45b7a89
4a660f8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import unittest | ||
|
||
from contextlib import contextmanager | ||
|
||
from .. import emitter_for_format | ||
from ..descriptor import ComplexDescriptorEmitter | ||
from ...types.descriptors.hid import \ | ||
HIDDescriptor as HIDDescriptorType | ||
from ...types.descriptors.hid import * | ||
|
||
ReportDescriptorEmitter = emitter_for_format(ReportDescriptor) | ||
|
||
_hid_item_length = [ 0, 1, 2, 4 ] | ||
|
||
class HIDDescriptor(ComplexDescriptorEmitter): | ||
DESCRIPTOR_FORMAT = HIDDescriptorType | ||
|
||
def add_report(self, report_enum, *report_data): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where does report come from here? To me, when I talk about HID reports, they are the reports exchanged between the host and the device. This is actually a HID report descriptor item, I would rename it There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I do suppose this can be documented better. A HID report item consists of a 4 bit tag and a 2 bit type, however, tags are not quite consistent within types, so only the 6 bit combined tag/type value has any map-able meaning. Input/output/feature items are special, because while other report items pretty consistently store byte-oriented data within their additional data fields, input/output/feature items contain a 9-bit bitmap of flags (only 8 bits of which are supported within this PR, as setting the 9th bit will make things a bit of a mess). Input/output/feature items have convenience functions for this reason, as generating them is less trivial than other report items. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry for not explaining correctly. My point was not about the documentation, although that is always better, but about the naming of the method. I believe the name There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do the changes in 2e7f95f address this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah sorry, missed that. Yes! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would also rename |
||
hid_report = ReportDescriptorEmitter() | ||
report_len = _hid_item_length.index(len(report_data)) | ||
hid_report.bHeader = { | ||
"prefix": report_enum, | ||
"bSize": report_len | ||
} | ||
hid_report.data = report_data | ||
self._reports.append(hid_report) | ||
|
||
def add_input(self, | ||
data_constant = False, | ||
array_variable = True, | ||
absolute_relative = False, | ||
wrap = False, | ||
linear = False, | ||
preferred = True, | ||
null = False, | ||
volatile = False): | ||
item_flags = ItemFlags.build({ | ||
"data_constant": data_constant, | ||
"array_variable": array_variable, | ||
"absolute_relative": absolute_relative, | ||
"wrap": wrap, | ||
"linear": linear, | ||
"nPreferred": ~preferred, | ||
"null": null, | ||
"volatile": volatile, | ||
}) | ||
self.add_report(HIDPrefixes.INPUT, ord(item_flags)) | ||
|
||
def add_output(self, | ||
data_constant = False, | ||
array_variable = True, | ||
absolute_relative = False, | ||
wrap = False, | ||
linear = False, | ||
preferred = True, | ||
null = False, | ||
volatile = False): | ||
item_flags = ItemFlags.build({ | ||
"data_constant": data_constant, | ||
"array_variable": array_variable, | ||
"absolute_relative": absolute_relative, | ||
"wrap": wrap, | ||
"linear": linear, | ||
"nPreferred": ~preferred, | ||
"null": null, | ||
"volatile": volatile, | ||
}) | ||
self.add_report(HIDPrefixes.OUTPUT, ord(item_flags)) | ||
|
||
def __init__(self, parent_descriptor): | ||
super().__init__() | ||
# The HID Report Descriptor sits under a different USB Descriptor, | ||
# we need access to the descriptor root to create this. | ||
self._parent_descriptor = parent_descriptor | ||
self._reports = [] | ||
|
||
def _pre_emit(self): | ||
report_descriptor = [] | ||
for report in self._reports: | ||
if hasattr(report, "emit"): | ||
report_descriptor.append(report.emit()) | ||
else: | ||
report_descriptor.append(report) | ||
report_descriptor = b"".join(report_descriptor) | ||
descriptor_len = len(report_descriptor) | ||
self.wDescriptorLength = descriptor_len | ||
self._parent_descriptor.add_descriptor(report_descriptor, 0x22) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
# | ||
# This file is part of usb-protocol. | ||
# | ||
""" Structures describing Communications Device Class descriptors. """ | ||
|
||
import unittest | ||
from enum import IntEnum, unique | ||
|
||
import construct | ||
from construct import this, Default | ||
|
||
from .. import LanguageIDs | ||
from ..descriptor import \ | ||
DescriptorField, DescriptorNumber, DescriptorFormat, \ | ||
BCDFieldAdapter, DescriptorLength | ||
|
||
@unique | ||
class HIDPrefixes(IntEnum): | ||
# Main items | ||
INPUT = 0b1000_00 | ||
OUTPUT = 0b1001_00 | ||
FEATURE = 0b1011_00 | ||
COLLECTION = 0b1010_00 | ||
END_COLLECTION = 0b1100_00 | ||
# Global items | ||
USAGE_PAGE = 0b0000_01 | ||
LOGICAL_MIN = 0b0001_01 | ||
LOGICAL_MAX = 0b0010_01 | ||
PHYSICAL_MIN = 0b0011_01 | ||
PHYSICAL_MAX = 0b0100_01 | ||
UNIT_EXPONENT = 0b0101_01 | ||
UNIT = 0b0110_01 | ||
REPORT_SIZE = 0b0111_01 | ||
REPORT_ID = 0b1000_01 | ||
REPORT_COUNT = 0b1001_01 | ||
PUSH = 0b1010_01 | ||
POP = 0b1011_01 | ||
# Local Items | ||
USAGE = 0b0000_10 | ||
USAGE_MIN = 0b0001_10 | ||
USAGE_MAX = 0b0010_10 | ||
DESIGNATOR_IDX = 0b0011_10 | ||
DESIGNATOR_MIN = 0b0100_10 | ||
DESIGNATOR_MAX = 0b0101_10 | ||
STRING_IDX = 0b0111_10 | ||
STRING_MIN = 0b1000_10 | ||
STRING_MAX = 0b1001_10 | ||
DELIMITER = 0b1010_10 | ||
|
||
HIDDescriptor = DescriptorFormat( | ||
"bLength" / construct.Const(0x09, construct.Int8ul), | ||
"bDescriptorType" / DescriptorNumber(33), | ||
"bcdHID" / DescriptorField("HID Protocol Version", default=1.11), | ||
"bCountryCode" / DescriptorField("HID Device Language", default=0), | ||
"bNumDescriptors" / DescriptorField("Number of HID Descriptors", default=1), | ||
"bDescriptorType" / DescriptorField("HID Descriptor Type", default=34), | ||
"wDescriptorLength" / DescriptorField("HID Descriptor Length") | ||
# bDescriptorType and wDescriptorLength repeat bNumDescriptors times | ||
) | ||
|
||
_hid_item_length = [ 0, 1, 2, 4 ] | ||
ReportDescriptor = DescriptorFormat( | ||
"bHeader" / construct.BitStruct( | ||
# prefix technically consists of a 4 byte tag and a 2 byte type, | ||
# however, they're all listed together in the HID spec | ||
"prefix" / construct.Enum(construct.BitsInteger(6), HIDPrefixes), | ||
"bSize" / construct.BitsInteger(2), | ||
), | ||
"data" / construct.Byte[lambda ctx: _hid_item_length[ctx.bHeader.bSize]] | ||
) | ||
|
||
# Flags for INPUT/OUTPUT/FEATURE items. Named under one of the following conventions: | ||
# valA_valB: valA when 0, valB when 1 | ||
# flag: Flag disabled when 0, flag enabled when 1 | ||
# nFlag: Flag enabled when 0, flag disabled when 1 | ||
ItemFlags = construct.BitStruct( | ||
"volatile" / construct.Flag, | ||
"null" / construct.Flag, | ||
"nPreferred" / construct.Flag, | ||
"linear" / construct.Flag, | ||
"wrap" / construct.Flag, | ||
"absolute_relative" / construct.Flag, | ||
"array_variable" / construct.Flag, | ||
"data_constant" / construct.Flag, | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There seem to be a bunch of naming inconsistencies, here.
For example, this looks like it produces more than a HID descriptor (it also builds report descriptors), so perhaps this would be better called e.g. a
HIDDescriptorCollection
?