Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(239)

Unified Diff: tools/usb_gadget/descriptor.py

Issue 409883008: [usb_gadget p00] Classes for encoding USB descriptors. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | tools/usb_gadget/descriptor_test.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/usb_gadget/descriptor.py
diff --git a/tools/usb_gadget/descriptor.py b/tools/usb_gadget/descriptor.py
new file mode 100644
index 0000000000000000000000000000000000000000..a53a86cd5765b8cc4efd1962e22d4fcf95079c72
--- /dev/null
+++ b/tools/usb_gadget/descriptor.py
@@ -0,0 +1,359 @@
+"""USB descriptor generation utilities.
not at google - send to devlin 2014/07/23 00:05:44 copyright
Reilly Grant (use Gerrit) 2014/07/23 00:43:54 Done.
+
+Classes to represent and generate USB descriptors.
+"""
Ken Rockot(use gerrit already) 2014/07/23 00:03:48 Maybe this module should be usb_descriptors?
Reilly Grant (use Gerrit) 2014/07/23 00:43:54 Done.
+
+import struct
+
+import usb_const
Ken Rockot(use gerrit already) 2014/07/23 00:03:47 How about "usb_constants" for the module name?
Reilly Grant (use Gerrit) 2014/07/23 00:43:54 Done.
+
+
+class Field(object):
+
Ken Rockot(use gerrit already) 2014/07/23 00:03:47 A bit of documentation?
Reilly Grant (use Gerrit) 2014/07/23 00:43:54 Done.
+ def __init__(self, name, str_fmt, struct_fmt, required):
+ self.name = name
+ self.str_fmt = str_fmt
+ self.struct_fmt = struct_fmt
+ self.required = required
+
+ def Format(self, value):
+ return self.str_fmt.format(value)
+
+
+class Descriptor(object):
+ """Base class for USB descriptor types.
+
+ This class provides general functionality for creating object types that
+ represent USB descriptors. The AddField and related methods are used to
+ define the fields of each structure. Fields can then be set using keyword
+ arguments to the object constructor or by accessing properties on the object.
+ """
+
+ _fields = None
+
+ @classmethod
+ def AddField(cls, name, struct_fmt, str_fmt='{}', default=None):
+ """Adds a user-specified field to this descriptor.
+
+ Adds a field to the binary structure representing this descriptor. The field
+ can be set by passing a keyword argument name=... to the object constructor
+ will be accessible as foo.name on any instance.
+
+ If no default value is provided then the constructor will through an
+ exception if this field is not one of the provided keyword arguments.
+
+ Args:
+ name: String name of the field.
+ struct_fmt: Python 'struct' module format string for this field.
+ str_fmt: Python 'string' module format string for this field.
+ default: Default value.
+ """
+ if cls._fields is None:
+ cls._fields = []
+ cls._fields.append(Field(name, str_fmt, struct_fmt, default is None))
+
+ member_name = '_{}'.format(name)
+ def Setter(self, value):
+ setattr(self, member_name, value)
+ def Getter(self):
+ try:
+ return getattr(self, member_name)
+ except AttributeError:
+ assert default is not None
+ return default
+
+ setattr(cls, name, property(Getter, Setter))
+
+ @classmethod
+ def AddFixedField(cls, name, struct_fmt, value, str_fmt='{}'):
+ """Adds a constant field to this descriptor.
+
+ Adds a constant field to the binary structure representing this descriptor.
+ The field will be accessible as foo.name on any instance.
+
+ The value of this field may not be given as a constructor parameter or
+ set on an existing instance.
+
+ Args:
+ name: String name of the field.
+ struct_fmt: Python 'struct' module format string for this field.
+ value: Field value.
+ str_fmt: Python 'string' module format string for this field.
+ """
+ if cls._fields is None:
+ cls._fields = []
+ cls._fields.append(Field(name, str_fmt, struct_fmt, False))
+
+ def Setter(unused_self, unused_value):
+ raise RuntimeError('{} is a fixed field.'.format(name))
+ def Getter(unused_self):
+ return value
+
+ setattr(cls, name, property(Getter, Setter))
+
+ @classmethod
+ def AddComputedField(cls, name, struct_fmt, property_name, str_fmt='{}'):
+ """Adds a constant field to this descriptor.
+
+ Adds a field to the binary structure representing this descriptor whos value
+ is equal to an object property. The field will be accessible as foo.name on
+ any instance.
+
+ The value of this field may not be given as a constructor parameter or
+ set on an existing instance.
+
+ Args:
+ name: String name of the field.
+ struct_fmt: Python 'struct' module format string for this field.
+ property_name: Property to read.
+ str_fmt: Python 'string' module format string for this field.
+ """
+ if cls._fields is None:
+ cls._fields = []
+ cls._fields.append(Field(name, str_fmt, struct_fmt, False))
+
+ def Setter(unused_self, unused_value):
+ raise RuntimeError('{} is a computed field.'.format(name))
+ def Getter(self):
+ return getattr(self, property_name)
+
+ setattr(cls, name, property(Getter, Setter))
+
+ def __init__(self, **kwargs):
+ """Constructs a new instance of this descriptor.
+
+ All fields which do not have a default value and are not fixed or computed
+ from a property must be specified as keyword arguments.
+
+ Args:
+ **kwargs: Field values.
+
+ Raises:
+ TypeError: A required field was missing or an unexpected field was given.
+ """
+ fields = {field.name for field in self._fields}
+ required_fields = {field.name for field in self._fields if field.required}
+
+ for arg, value in kwargs.iteritems():
+ if arg not in fields:
+ raise TypeError('Unexpected field: {}'.format(arg))
+
+ setattr(self, arg, value)
+ required_fields.discard(arg)
+
+ if required_fields:
+ raise TypeError('Missing fields: {}'.format(', '.join(required_fields)))
+
+ @property
+ def fmt(self):
+ """Returns the Python 'struct' module format string for this descriptor."""
+ return '<{}'.format(''.join([field.struct_fmt for field in self._fields]))
+
+ @property
+ def struct_size(self):
+ """Returns the size of the struct defined by fmt."""
+ return struct.calcsize(self.fmt)
+
+ @property
+ def total_size(self):
+ """Returns the total size of this descriptor."""
+ return self.struct_size
+
+ def Encode(self):
+ """Returns the binary representation of this descriptor."""
+ values = [getattr(self, field.name) for field in self._fields]
+ return struct.pack(self.fmt, *values)
+
+ def __str__(self):
+ max_length = max(len(field.name) for field in self._fields)
+
+ return '{}:\n {}'.format(
+ self.__class__.__name__,
+ '\n '.join('{} {}'.format(
+ '{}:'.format(field.name).ljust(max_length+1),
+ field.Format(getattr(self, field.name))
+ ) for field in self._fields)
+ )
+
+
+class DeviceDescriptor(Descriptor):
+ """Represents a USB device descriptor."""
+
+ pass
+
+DeviceDescriptor.AddComputedField('bLength', 'B', 'struct_size')
Ken Rockot(use gerrit already) 2014/07/23 00:03:47 Might as well link to / reference any relevant sec
Reilly Grant (use Gerrit) 2014/07/23 00:43:54 Done.
+DeviceDescriptor.AddFixedField('bDescriptorType', 'B',
+ usb_const.DescriptorType.DEVICE)
+DeviceDescriptor.AddField('bcdUSB', 'H', default=0x0200, str_fmt='0x{:04X}')
+DeviceDescriptor.AddField('bDeviceClass', 'B',
+ default=usb_const.DeviceClass.PER_INTERFACE)
+DeviceDescriptor.AddField('bDeviceSubClass', 'B',
+ default=usb_const.DeviceSubClass.PER_INTERFACE)
+DeviceDescriptor.AddField('bDeviceProtocol', 'B',
+ default=usb_const.DeviceProtocol.PER_INTERFACE)
+DeviceDescriptor.AddField('bMaxPacketSize0', 'B', default=64)
+DeviceDescriptor.AddField('idVendor', 'H', str_fmt='0x{:04X}')
+DeviceDescriptor.AddField('idProduct', 'H', str_fmt='0x{:04X}')
+DeviceDescriptor.AddField('bcdDevice', 'H', str_fmt='0x{:04X}')
+DeviceDescriptor.AddField('iManufacturer', 'B', default=0)
+DeviceDescriptor.AddField('iProduct', 'B', default=0)
+DeviceDescriptor.AddField('iSerialNumber', 'B', default=0)
+DeviceDescriptor.AddField('bNumConfigurations', 'B', default=1)
+
+
+class DescriptorContainer(Descriptor):
Ken Rockot(use gerrit already) 2014/07/23 00:03:48 If this is established nomenclature in USB land, I
Reilly Grant (use Gerrit) 2014/07/23 00:43:54 "CompositeDescriptor" may confusing because "USB C
+ """Super-class for descriptors which contain more descriptors.
+
+ This class adds the ability for a descriptor to have an array of additional
+ descriptors which follow it.
+ """
+
+ def __init__(self, **kwargs):
+ super(DescriptorContainer, self).__init__(**kwargs)
+ self._descriptors = []
+
+ @property
+ def total_size(self):
+ return self.struct_size + sum([descriptor.total_size
+ for descriptor in self._descriptors])
+
+ def Add(self, descriptor):
+ self._descriptors.append(descriptor)
+
+ def Encode(self):
+ bufs = [super(DescriptorContainer, self).Encode()]
+ bufs.extend(descriptor.Encode() for descriptor in self._descriptors)
+ return ''.join(bufs)
+
+ def __str__(self):
+ return '{}\n{}'.format(super(DescriptorContainer, self).__str__(),
+ '\n'.join(str(descriptor)
+ for descriptor in self._descriptors))
+
+
+class ConfigurationDescriptor(DescriptorContainer):
+ """Represents a USB device configuration descriptor.
+
+ Configuration descriptors may have a number of interface descriptors.
+ """
+
+ def __init__(self, **kwargs):
+ super(ConfigurationDescriptor, self).__init__(**kwargs)
+ self._interfaces = {}
+
+ @property
+ def num_interfaces(self):
+ interface_numbers = {key[0] for key in self._interfaces.iterkeys()}
+ return len(interface_numbers)
+
+ def AddInterface(self, interface):
+ key = (interface.bInterfaceNumber, interface.bAlternateSetting)
+ if key in self._interfaces:
+ raise RuntimeError('Interface {} (alternate {}) already defined.'
+ .format(key[0], key[1]))
+ self._interfaces[key] = interface
+ self.Add(interface)
+
+ def GetInterfaces(self):
+ return self._interfaces.values()
+
+ConfigurationDescriptor.AddComputedField('bLength', 'B', 'struct_size')
+ConfigurationDescriptor.AddFixedField('bDescriptorType', 'B',
+ usb_const.DescriptorType.CONFIGURATION)
+ConfigurationDescriptor.AddComputedField('wTotalLength', 'H', 'total_size')
+ConfigurationDescriptor.AddComputedField('bNumInterfaces', 'B',
+ 'num_interfaces')
+ConfigurationDescriptor.AddField('bConfigurationValue', 'B', default=1)
+ConfigurationDescriptor.AddField('iConfiguration', 'B', default=0)
+ConfigurationDescriptor.AddField('bmAttributes', 'B', str_fmt='0x{:02X}')
+ConfigurationDescriptor.AddField('MaxPower', 'B')
+
+
+class InterfaceDescriptor(DescriptorContainer):
+ """Represents a USB interface descriptor.
+
+ Interface descriptors may have a number of endpoint descriptors.
+ """
+
+ def __init__(self, **kwargs):
+ super(InterfaceDescriptor, self).__init__(**kwargs)
+ self._endpoints = {}
+
+ @property
+ def num_endpoints(self):
+ return len(self._endpoints)
+
+ def AddEndpoint(self, endpoint):
+ if endpoint.bEndpointAddress in self._endpoints:
+ raise RuntimeError('Endpoint 0x{:02X} already defined on this interface.'
+ .format(endpoint.bEndpointAddress))
+ self._endpoints[endpoint.bEndpointAddress] = endpoint
+ self.Add(endpoint)
+
+ def GetEndpoints(self):
+ return self._endpoints.values()
+
+InterfaceDescriptor.AddComputedField('bLength', 'B', 'struct_size')
+InterfaceDescriptor.AddFixedField('bDescriptorType', 'B',
+ usb_const.DescriptorType.INTERFACE)
+InterfaceDescriptor.AddField('bInterfaceNumber', 'B')
+InterfaceDescriptor.AddField('bAlternateSetting', 'B', default=0)
+InterfaceDescriptor.AddComputedField('bNumEndpoints', 'B', 'num_endpoints')
+InterfaceDescriptor.AddField('bInterfaceClass', 'B',
+ default=usb_const.InterfaceClass.VENDOR)
+InterfaceDescriptor.AddField('bInterfaceSubClass', 'B',
+ default=usb_const.InterfaceSubClass.VENDOR)
+InterfaceDescriptor.AddField('bInterfaceProtocol', 'B',
+ default=usb_const.InterfaceProtocol.VENDOR)
+InterfaceDescriptor.AddField('iInterface', 'B', default=0)
+
+
+class EndpointDescriptor(Descriptor):
+ """Represents a USB endpoint descriptor."""
+
+ pass
+
+EndpointDescriptor.AddComputedField('bLength', 'B', 'struct_size')
+EndpointDescriptor.AddFixedField('bDescriptorType', 'B',
+ usb_const.DescriptorType.ENDPOINT)
+EndpointDescriptor.AddField('bEndpointAddress', 'B', str_fmt='0x{:02X}')
+EndpointDescriptor.AddField('bmAttributes', 'B', str_fmt='0x{:02X}')
+EndpointDescriptor.AddField('wMaxPacketSize', 'H')
+EndpointDescriptor.AddField('bInterval', 'B')
+
+
+class HidDescriptor(Descriptor):
+ """Represents a USB HID descriptor."""
+
+ def __init__(self, **kwargs):
+ super(HidDescriptor, self).__init__(**kwargs)
+ self._descriptors = []
+
+ def AddDescriptor(self, typ, length):
+ self._descriptors.append((typ, length))
+
+ @property
+ def struct_size(self):
+ return super(HidDescriptor, self).struct_size + self.num_descriptors * 3
+
+ @property
+ def num_descriptors(self):
+ return len(self._descriptors)
+
+ def Encode(self):
+ bufs = [super(HidDescriptor, self).Encode()]
+ bufs.extend(struct.pack('<BH', typ, length)
+ for typ, length in self._descriptors)
+ return ''.join(bufs)
+
+ def __str__(self):
+ return '{}\n{}'.format(
+ super(HidDescriptor, self).__str__(),
+ '\n'.join(' bDescriptorType: 0x{:02X}\n wDescriptorLength: {}'
+ .format(typ, length) for typ, length in self._descriptors))
+
+HidDescriptor.AddComputedField('bLength', 'B', 'struct_size')
+HidDescriptor.AddFixedField('bDescriptorType', 'B', 33)
+HidDescriptor.AddField('bcdHID', 'H', default=0x0111, str_fmt='0x{:04X}')
+HidDescriptor.AddField('bCountryCode', 'B', default=0)
+HidDescriptor.AddComputedField('bNumDescriptors', 'B', 'num_descriptors')
« no previous file with comments | « no previous file | tools/usb_gadget/descriptor_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698