Skip to content

Commit

Permalink
Add HID descriptors
Browse files Browse the repository at this point in the history
  • Loading branch information
TiltMeSenpai committed Aug 8, 2020
1 parent 26c9cf0 commit cef2b8b
Show file tree
Hide file tree
Showing 3 changed files with 179 additions and 4 deletions.
87 changes: 87 additions & 0 deletions usb_protocol/emitters/descriptors/hid.py
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):
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)
11 changes: 7 additions & 4 deletions usb_protocol/emitters/descriptors/standard.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,20 +177,23 @@ def get_index_for_string(self, string):
return index


def add_descriptor(self, descriptor, index=0):
def add_descriptor(self, descriptor, descriptor_type=None, index=0):
""" Adds a descriptor to our collection.
Parameters:
descriptor -- The descriptor to be added.
index -- The index of the relevant descriptor. Defaults to 0.
descriptor -- The descriptor to be added.
descriptor_type -- The type of the descriptor to be added. If `None`,
this is automatically derived from the descriptor contents.
index -- The index of the relevant descriptor. Defaults to 0.
"""

# If this is an emitter rather than a descriptor itself, convert it.
if hasattr(descriptor, 'emit'):
descriptor = descriptor.emit()

# Figure out the identifier (type + index) for this descriptor...
descriptor_type = descriptor[1]
if(descriptor_type == None):
descriptor_type = descriptor[1]
identifier = descriptor_type, index

# ... and store it.
Expand Down
85 changes: 85 additions & 0 deletions usb_protocol/types/descriptors/hid.py
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,
)

0 comments on commit cef2b8b

Please sign in to comment.