From 928e5db00860f8d3714d74827b7b0b637a167d4d Mon Sep 17 00:00:00 2001 From: Katherine Temkin Date: Sun, 11 Oct 2020 10:48:15 -0600 Subject: [PATCH] descriptors: extend to include some SuperSpeed content --- usb_protocol/emitters/__init__.py | 2 +- usb_protocol/emitters/descriptors/standard.py | 106 ++++++++++++++++-- usb_protocol/types/__init__.py | 32 +++--- usb_protocol/types/descriptors/standard.py | 48 ++++++-- 4 files changed, 158 insertions(+), 30 deletions(-) diff --git a/usb_protocol/emitters/__init__.py b/usb_protocol/emitters/__init__.py index 62639c9..b0da9c6 100644 --- a/usb_protocol/emitters/__init__.py +++ b/usb_protocol/emitters/__init__.py @@ -4,4 +4,4 @@ """ USB-related emitters. """ from .construct import emitter_for_format, ConstructEmitter -from .descriptors.standard import DeviceDescriptorCollection +from .descriptors.standard import DeviceDescriptorCollection, SuperSpeedDeviceDescriptorCollection diff --git a/usb_protocol/emitters/descriptors/standard.py b/usb_protocol/emitters/descriptors/standard.py index f7ff9ec..c17d09a 100644 --- a/usb_protocol/emitters/descriptors/standard.py +++ b/usb_protocol/emitters/descriptors/standard.py @@ -13,16 +13,20 @@ from ..descriptor import ComplexDescriptorEmitter from ...types import LanguageIDs from ...types.descriptors.standard import \ DeviceDescriptor, StringDescriptor, EndpointDescriptor, DeviceQualifierDescriptor, \ - ConfigurationDescriptor, InterfaceDescriptor, StandardDescriptorNumbers, StringLanguageDescriptor + ConfigurationDescriptor, InterfaceDescriptor, StandardDescriptorNumbers, StringLanguageDescriptor, \ + BinaryObjectStoreDescriptor, SuperSpeedEndpointCompanionDescriptor # Create our basic emitters... DeviceDescriptorEmitter = emitter_for_format(DeviceDescriptor) StringDescriptorEmitter = emitter_for_format(StringDescriptor) StringLanguageDescriptorEmitter = emitter_for_format(StringLanguageDescriptor) -EndpointDescriptorEmitter = emitter_for_format(EndpointDescriptor) DeviceQualifierDescriptor = emitter_for_format(DeviceQualifierDescriptor) +# ... our basic superspeed emitters ... +BinaryObjectStoreDescriptorEmitter = emitter_for_format(BinaryObjectStoreDescriptor) +SuperSpeedEndpointCompanionDescriptorEmitter = emitter_for_format(SuperSpeedEndpointCompanionDescriptor) + # ... convenience functions ... def get_string_descriptor(string): """ Generates a string descriptor for the relevant string. """ @@ -33,13 +37,37 @@ def get_string_descriptor(string): # ... and complex emitters. +class EndpointDescriptorEmitter(ComplexDescriptorEmitter): + """ Emitter that creates an InterfaceDescriptor. """ + + DESCRIPTOR_FORMAT = EndpointDescriptor + + @contextmanager + def SuperSpeedCompanion(self): + """ Context manager that allows addition of a SuperSpeed Companion to this endpoint descriptor. + + It can be used with a `with` statement; and yields an SuperSpeedEndpointCompanionDescriptorEmitter + that can be populated: + + with endpoint.SuperSpeedEndpointCompanion() as d: + d.bMaxBurst = 1 + + This adds the relevant descriptor, automatically. + """ + + descriptor = SuperSpeedEndpointCompanionDescriptorEmitter() + yield descriptor + + self.add_subordinate_descriptor(descriptor) + + class InterfaceDescriptorEmitter(ComplexDescriptorEmitter): """ Emitter that creates an InterfaceDescriptor. """ DESCRIPTOR_FORMAT = InterfaceDescriptor @contextmanager - def EndpointDescriptor(self): + def EndpointDescriptor(self, *, add_default_superspeed=False): """ Context manager that allows addition of a subordinate endpoint descriptor. It can be used with a `with` statement; and yields an EndpointDesriptorEmitter @@ -57,6 +85,11 @@ class InterfaceDescriptorEmitter(ComplexDescriptorEmitter): descriptor = EndpointDescriptorEmitter() yield descriptor + # If we're adding a default SuperSpeed extension, do so. + if add_default_superspeed: + with descriptor.SuperSpeedCompanion(): + pass + self.add_subordinate_descriptor(descriptor) @@ -263,15 +296,15 @@ class DeviceDescriptorCollection: def _ensure_has_language_descriptor(self): - """ Ensures that we have a language descriptor; adding one if necessary.""" + """ ensures that we have a language descriptor; adding one if necessary.""" - # If we're not automatically adding a language descriptor, we shouldn't do anything, + # if we're not automatically adding a language descriptor, we shouldn't do anything, # and we'll just ignore this. if not self._automatic_language_descriptor: return - # If we don't have a language descriptor, add our default one. - if not (StandardDescriptorNumbers.STRING, 0) in self._descriptors: + # if we don't have a language descriptor, add our default one. + if (StandardDescriptorNumbers.STRING, 0) not in self._descriptors: self.add_language_descriptor() @@ -297,6 +330,63 @@ class DeviceDescriptorCollection: return ((number, index, desc) for ((number, index), desc) in self._descriptors.items()) +class SuperSpeedDeviceDescriptorCollection(DeviceDescriptorCollection): + """ Object that builds a full collection of descriptors related to a given USB3 device. """ + + def __init__(self, automatic_descriptors=True): + """ + Parameters: + automatic_descriptors -- If set or not provided, certian required descriptors will be + be added if none exists. + """ + self._automatic_descriptors = automatic_descriptors + super().__init__(automatic_language_descriptor=automatic_descriptors) + + + @contextmanager + def BOSDescriptor(self): + """ Context manager that allows addition of a Binary Object Store descriptor. + + It can be used with a `with` statement; and yields an BinaryObjectStoreDescriptorEmitter + that can be populated: + + with collection.BOSDescriptor() as d: + [snip] + + This adds the relevant descriptor, automatically. Note that populating derived + fields such as bNumDeviceCaps aren't necessary; they'll be populated automatically. + """ + descriptor = BinaryObjectStoreDescriptorEmitter() + yield descriptor + + self.add_descriptor(descriptor) + + + def add_default_bos_descriptor(self): + """ Adds a default, empty BOS descriptor. """ + + descriptor = BinaryObjectStoreDescriptorEmitter() + self.add_descriptor(descriptor) + + + def _ensure_has_bos_descriptor(self): + """ Ensures that we have a BOS descriptor; adding one if necessary.""" + + # If we're not automatically adding a language descriptor, we shouldn't do anything, + # and we'll just ignore this. + if not self._automatic_descriptors: + return + + # If we don't have a language descriptor, add our default one. + if (StandardDescriptorNumbers.BOS, 0) not in self._descriptors: + self.add_default_bos_descriptor() + + + def __iter__(self): + """ Allow iterating over each of our descriptors; yields (index, value, descriptor). """ + self._ensure_has_bos_descriptor() + return super().__iter__() + class EmitterTests(unittest.TestCase): @@ -417,3 +507,5 @@ class EmitterTests(unittest.TestCase): if __name__ == "__main__": unittest.main() + + diff --git a/usb_protocol/types/__init__.py b/usb_protocol/types/__init__.py index 04548c7..4439157 100644 --- a/usb_protocol/types/__init__.py +++ b/usb_protocol/types/__init__.py @@ -593,20 +593,6 @@ class USBUsageType(IntEnum): IMPLICIT_FEEDBACK = 2 -class USBStandardRequests(IntEnum): - GET_STATUS = 0 - CLEAR_FEATURE = 1 - SET_FEATURE = 3 - SET_ADDRESS = 5 - GET_DESCRIPTOR = 6 - SET_DESCRIPTOR = 7 - GET_CONFIGURATION = 8 - SET_CONFIGURATION = 9 - GET_INTERFACE = 10 - SET_INTERFACE = 11 - SYNCH_FRAME = 12 - - class USBTransferType(IntEnum): CONTROL = 0 ISOCHRONOUS = 1 @@ -639,3 +625,21 @@ class USBStandardRequests(IntEnum): GET_INTERFACE = 10 SET_INTERFACE = 11 SYNCH_FRAME = 12 + + # USB3 only. + SET_ENCRYPTION = 13 + GET_ENCRYPTION = 14 + SET_HANDSHAKE = 15 + GET_HANDSHAKE = 16 + SET_CONNECTION = 17 + SET_SECURITY_DATA = 18 + GET_SECURITY_DATA = 19 + SET_WUSB_DATA = 20 + LOOPBACK_DATA_WRITE = 21 + LOOPBACK_DATA_READ = 22 + SET_INTERFACE_DS = 23 + SET_SEL = 48 + SET_ISOCH_DELAY = 49 + + + diff --git a/usb_protocol/types/descriptors/standard.py b/usb_protocol/types/descriptors/standard.py index 223de27..4acbd99 100644 --- a/usb_protocol/types/descriptors/standard.py +++ b/usb_protocol/types/descriptors/standard.py @@ -18,14 +18,25 @@ from ..descriptor import \ class StandardDescriptorNumbers(IntEnum): """ Numbers of our standard descriptors. """ - DEVICE = 1 - CONFIGURATION = 2 - STRING = 3 - INTERFACE = 4 - ENDPOINT = 5 - DEVICE_QUALIFIER = 6 - OTHER_SPEED_DESCRIPTOR = 7 - INTERFACE_POWER = 8 + DEVICE = 1 + CONFIGURATION = 2 + STRING = 3 + INTERFACE = 4 + ENDPOINT = 5 + DEVICE_QUALIFIER = 6 + OTHER_SPEED_DESCRIPTOR = 7 + OTHER_SPEED = 7 + INTERFACE_POWER = 8 + OTG = 9 + DEBUG = 10 + INTERFACE_ASSOCIATION = 11 + + # SuperSpeed only + BOS = 15 + DEVICE_CAPABILITY = 16 + SUPERSPEED_USB_ENDPOINT_COMPANION = 48 + SUPERSPEEDPLUS_ISOCHRONOUS_ENDPOINT_COMPANION = 49 + DeviceDescriptor = DescriptorFormat( @@ -46,6 +57,7 @@ DeviceDescriptor = DescriptorFormat( ) + ConfigurationDescriptor = DescriptorFormat( "bLength" / construct.Const(9, construct.Int8ul), "bDescriptorType" / DescriptorNumber(StandardDescriptorNumbers.CONFIGURATION), @@ -122,6 +134,26 @@ DeviceQualifierDescriptor = DescriptorFormat( ) +# +# SuperSpeed descriptors +# +BinaryObjectStoreDescriptor = DescriptorFormat( + "bLength" / construct.Const(0x5, construct.Int8ul), + "bDescriptorType" / DescriptorNumber(StandardDescriptorNumbers.BOS), + "wTotalLength" / DescriptorField("Total Length", default=5), + "bNumDeviceCaps" / DescriptorField("Device Capability Descriptors", default=0), +) + +SuperSpeedEndpointCompanionDescriptor = DescriptorFormat( + "bLength" / construct.Const(0x6, construct.Int8ul), + "bDescriptorType" / DescriptorNumber(StandardDescriptorNumbers.SUPERSPEED_USB_ENDPOINT_COMPANION), + "bMaxBurst" / DescriptorField("Maximum Burst Length", default=0), + "bmAttributes" / DescriptorField("Extended Attributes", default=0), + "wBytesPerInterval" / DescriptorField("Bytes Per Service Interval", default=0), +) + + + class DescriptorParserCases(unittest.TestCase): STRING_DESCRIPTOR = bytes([