| Index: tools/usb_gadget/usb_descriptors.py
|
| diff --git a/tools/usb_gadget/usb_descriptors.py b/tools/usb_gadget/usb_descriptors.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..454f77de372b56a8ce87661bcb833894ac511282
|
| --- /dev/null
|
| +++ b/tools/usb_gadget/usb_descriptors.py
|
| @@ -0,0 +1,380 @@
|
| +# Copyright 2014 The Chromium Authors. All rights reserved.
|
| +# Use of this source code is governed by a BSD-style license that can be
|
| +# found in the LICENSE file.
|
| +
|
| +"""USB descriptor generation utilities.
|
| +
|
| +Classes to represent and generate USB descriptors.
|
| +"""
|
| +
|
| +import struct
|
| +
|
| +import usb_constants
|
| +
|
| +
|
| +class Field(object):
|
| + """USB descriptor field information."""
|
| +
|
| + def __init__(self, name, str_fmt, struct_fmt, required):
|
| + """Define a new USB descriptor field.
|
| +
|
| + Args:
|
| + name: Name of the field.
|
| + str_fmt: Python 'string' module format string for this field.
|
| + struct_fmt: Python 'struct' module format string for this field.
|
| + required: Is this a required field?
|
| + """
|
| + 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):
|
| + """Standard Device Descriptor.
|
| +
|
| + See Universal Serial Bus Specification Revision 2.0 Table 9-8.
|
| + """
|
| + pass
|
| +
|
| +DeviceDescriptor.AddComputedField('bLength', 'B', 'struct_size')
|
| +DeviceDescriptor.AddFixedField('bDescriptorType', 'B',
|
| + usb_constants.DescriptorType.DEVICE)
|
| +DeviceDescriptor.AddField('bcdUSB', 'H', default=0x0200, str_fmt='0x{:04X}')
|
| +DeviceDescriptor.AddField('bDeviceClass', 'B',
|
| + default=usb_constants.DeviceClass.PER_INTERFACE)
|
| +DeviceDescriptor.AddField('bDeviceSubClass', 'B',
|
| + default=usb_constants.DeviceSubClass.PER_INTERFACE)
|
| +DeviceDescriptor.AddField('bDeviceProtocol', 'B',
|
| + default=usb_constants.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):
|
| + """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):
|
| + """Standard Configuration Descriptor.
|
| +
|
| + See Universal Serial Bus Specification Revision 2.0 Table 9-10.
|
| + """
|
| +
|
| + 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_constants.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):
|
| + """Standard Interface Descriptor.
|
| +
|
| + See Universal Serial Bus Specification Revision 2.0 Table 9-12.
|
| + """
|
| +
|
| + 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_constants.DescriptorType.INTERFACE)
|
| +InterfaceDescriptor.AddField('bInterfaceNumber', 'B')
|
| +InterfaceDescriptor.AddField('bAlternateSetting', 'B', default=0)
|
| +InterfaceDescriptor.AddComputedField('bNumEndpoints', 'B', 'num_endpoints')
|
| +InterfaceDescriptor.AddField('bInterfaceClass', 'B',
|
| + default=usb_constants.InterfaceClass.VENDOR)
|
| +InterfaceDescriptor.AddField('bInterfaceSubClass', 'B',
|
| + default=usb_constants.InterfaceSubClass.VENDOR)
|
| +InterfaceDescriptor.AddField('bInterfaceProtocol', 'B',
|
| + default=usb_constants.InterfaceProtocol.VENDOR)
|
| +InterfaceDescriptor.AddField('iInterface', 'B', default=0)
|
| +
|
| +
|
| +class EndpointDescriptor(Descriptor):
|
| + """Standard Endpoint Descriptor.
|
| +
|
| + See Universal Serial Bus Specification Revision 2.0 Table 9-13.
|
| + """
|
| + pass
|
| +
|
| +EndpointDescriptor.AddComputedField('bLength', 'B', 'struct_size')
|
| +EndpointDescriptor.AddFixedField('bDescriptorType', 'B',
|
| + usb_constants.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):
|
| + """HID Descriptor.
|
| +
|
| + See Device Class Definition for Human Interface Devices (HID) Version 1.11
|
| + section 6.2.1.
|
| + """
|
| +
|
| + 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')
|
|
|