diff --git a/usb_protocol/emitters/descriptors/standard.py b/usb_protocol/emitters/descriptors/standard.py index 4cbeb64..52bc2c7 100644 --- a/usb_protocol/emitters/descriptors/standard.py +++ b/usb_protocol/emitters/descriptors/standard.py @@ -11,10 +11,7 @@ from .. import emitter_for_format from ..descriptor import ComplexDescriptorEmitter from ...types import LanguageIDs -from ...types.descriptors.standard import \ - DeviceDescriptor, StringDescriptor, EndpointDescriptor, DeviceQualifierDescriptor, \ - ConfigurationDescriptor, InterfaceDescriptor, StandardDescriptorNumbers, StringLanguageDescriptor, \ - BinaryObjectStoreDescriptor, SuperSpeedEndpointCompanionDescriptor +from ...types.descriptors.standard import * # Create our basic emitters... @@ -24,8 +21,9 @@ StringLanguageDescriptorEmitter = emitter_for_format(StringLanguageDescriptor) DeviceQualifierDescriptor = emitter_for_format(DeviceQualifierDescriptor) # ... our basic superspeed emitters ... -BinaryObjectStoreDescriptorEmitter = emitter_for_format(BinaryObjectStoreDescriptor) -SuperSpeedEndpointCompanionDescriptorEmitter = emitter_for_format(SuperSpeedEndpointCompanionDescriptor) +USB2ExtensionDescriptorEmitter = emitter_for_format(USB2ExtensionDescriptor) +SuperSpeedUSBDeviceCapabilityDescriptorEmitter = emitter_for_format(SuperSpeedUSBDeviceCapabilityDescriptor) +SuperSpeedEndpointCompanionDescriptorEmitter = emitter_for_format(SuperSpeedEndpointCompanionDescriptor) # ... convenience functions ... def get_string_descriptor(string): @@ -327,6 +325,63 @@ class DeviceDescriptorCollection: return ((number, index, desc) for ((number, index), desc) in self._descriptors.items()) + + +class BinaryObjectStoreDescriptorEmitter(ComplexDescriptorEmitter): + """ Emitter that creates a BinaryObjectStore descriptor. """ + + DESCRIPTOR_FORMAT = BinaryObjectStoreDescriptor + + @contextmanager + def USB2Extension(self): + """ Context manager that allows addition of a USB 2.0 Extension to this Binary Object Store. + + It can be used with a `with` statement; and yields an USB2ExtensionDescriptorEmitter + that can be populated: + + with bos.USB2Extension() as e: + e.bmAttributes = 1 + + This adds the relevant descriptor, automatically. + """ + + descriptor = USB2ExtensionDescriptorEmitter() + yield descriptor + + self.add_subordinate_descriptor(descriptor) + + + @contextmanager + def SuperSpeedUSBDeviceCapability(self): + """ Context manager that allows addition of a SS Device Capability to this Binary Object Store. + + It can be used with a `with` statement; and yields an SuperSpeedUSBDeviceCapabilityDescriptorEmitter + that can be populated: + + with bos.SuperSpeedUSBDeviceCapability() as e: + e.wSpeedSupported = 0b1110 + e.bFunctionalitySupport = 1 + + This adds the relevant descriptor, automatically. + """ + + descriptor = SuperSpeedUSBDeviceCapabilityDescriptorEmitter() + yield descriptor + + self.add_subordinate_descriptor(descriptor) + + + def _pre_emit(self): + + # Figure out the total length of our descriptor, including subordinates. + subordinate_length = sum(len(sub) for sub in self._subordinates) + self.wTotalLength = subordinate_length + self.DESCRIPTOR_FORMAT.sizeof() + + # Count our subordinate descriptors, and update our internal count. + self.bNumDeviceCaps = len(self._subordinates) + + + class SuperSpeedDeviceDescriptorCollection(DeviceDescriptorCollection): """ Object that builds a full collection of descriptors related to a given USB3 device. """ @@ -362,7 +417,14 @@ class SuperSpeedDeviceDescriptorCollection(DeviceDescriptorCollection): def add_default_bos_descriptor(self): """ Adds a default, empty BOS descriptor. """ + # Create an empty BOS descriptor... descriptor = BinaryObjectStoreDescriptorEmitter() + + # ... populate our default required descriptors... + descriptor.add_subordinate_descriptor(USB2ExtensionDescriptorEmitter()) + descriptor.add_subordinate_descriptor(SuperSpeedUSBDeviceCapabilityDescriptorEmitter()) + + # ... and add it to our overall BOS descriptor. self.add_descriptor(descriptor) diff --git a/usb_protocol/types/descriptor.py b/usb_protocol/types/descriptor.py index 93aeec8..772396f 100644 --- a/usb_protocol/types/descriptor.py +++ b/usb_protocol/types/descriptor.py @@ -180,6 +180,16 @@ class DescriptorField(construct.Subconstruct): 'w' : construct.Int16ul, } + + LENGTH_TYPES = { + 1: construct.Int8ul, + 2: construct.Int16ul, + 3: construct.Int24ul, + 4: construct.Int32ul, + 8: construct.Int64ul + } + + @staticmethod def _get_prefix(name): """ Returns the lower-case prefix on a USB descriptor name. """ @@ -210,13 +220,20 @@ class DescriptorField(construct.Subconstruct): raise ValueError("field names must be formatted per the USB standard!") - def __init__(self, description="", default=None): + def __init__(self, description="", default=None, *, length=None): self.description = description self.default = default + self.length = length def __rtruediv__(self, field_name): - field_type = self._get_type_for_name(field_name) + # If we have a length, use it to figure out the type. + # Otherwise, extract the type from the prefix. (Using a length + # is useful for e.g. USB3 bitfields; which can span several bytes.) + if self.length is not None: + field_type = self.LENGTH_TYPES[self.length] + else: + field_type = self._get_type_for_name(field_name) if self.default is not None: field_type = construct.Default(field_type, self.default) diff --git a/usb_protocol/types/descriptors/standard.py b/usb_protocol/types/descriptors/standard.py index 25454dd..bdf53f1 100644 --- a/usb_protocol/types/descriptors/standard.py +++ b/usb_protocol/types/descriptors/standard.py @@ -43,6 +43,27 @@ class StandardDescriptorNumbers(IntEnum): SUPERSPEEDPLUS_ISOCHRONOUS_ENDPOINT_COMPANION = 49 +class DeviceCapabilityTypes(IntEnum): + """ Numbers for the SuperSpeed standard Device Capabilities. """ + + WIRELESS_USB = 1 + USB_2_EXTENSION = 2 + SUPERSPEED_USB = 3 + CONTAINER_ID = 4 + PLATFORM = 5 + POWER_DELIVERY_CAPABILITY = 6 + BATTERY_INFO_CAPABILITY = 7 + PD_CONSUMER_PORT_CAPABILITY = 8 + PD_PROVIDER_PORT_CAPABILITY = 9 + SUPERSPEED_PLUS = 10 + PRECISION_TIME_MEASUREMENT = 11 + WIRELESS_USB_EXTENSION = 12 + BILLBOARD = 13 + AUTHENTICATION = 14 + BILLBOARD_EXTENSION = 15 + CONFIGURATION_SUMMARY = 16 + + DeviceDescriptor = DescriptorFormat( "bLength" / construct.Const(0x12, construct.Int8ul), @@ -143,12 +164,31 @@ 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), + "bLength" / construct.Const(0x5, construct.Int8ul), + "bDescriptorType" / DescriptorNumber(StandardDescriptorNumbers.BOS), + "wTotalLength" / DescriptorField("Total Length", default=5), + "bNumDeviceCaps" / DescriptorField("Device Capability Descriptors", default=0), +) + +USB2ExtensionDescriptor = DescriptorFormat( + "bLength" / construct.Const(0x7, construct.Int8ul), + "bDescriptorType" / DescriptorNumber(StandardDescriptorNumbers.DEVICE_CAPABILITY), + "bDevCapabilityType" / construct.Const(DeviceCapabilityTypes.USB_2_EXTENSION, construct.Int8ul), + "bmAttributes" / DescriptorField("Attributes", default=0b10, length=4) ) +SuperSpeedUSBDeviceCapabilityDescriptor = DescriptorFormat( + "bLength" / construct.Const(0xA, construct.Int8ul), + "bDescriptorType" / DescriptorNumber(StandardDescriptorNumbers.DEVICE_CAPABILITY), + "bDevCapabilityType" / construct.Const(DeviceCapabilityTypes.SUPERSPEED_USB, construct.Int8ul), + "bmAttributes" / DescriptorField("Attributes", default=0), + "wSpeedsSupported" / DescriptorField("USB3 Speeds Supported", default=0b1000), + "bFunctionalitySupport" / DescriptorField("Lowest Speed with Full Support", default=3), + "bU1DevExitLat" / DescriptorField("U1 Exit Latency", default=0), + "wU2DevExitLat" / DescriptorField("U2 Exit Latency", default=0) +) + + SuperSpeedEndpointCompanionDescriptor = DescriptorFormat( "bLength" / construct.Const(0x6, construct.Int8ul), "bDescriptorType" / DescriptorNumber(StandardDescriptorNumbers.SUPERSPEED_USB_ENDPOINT_COMPANION),