usb_descriptors.py revision 5f1c94371a64b3196d4be9466099bb892df9b88e
1# Copyright 2014 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""USB descriptor generation utilities.
6
7Classes to represent and generate USB descriptors.
8"""
9
10import struct
11
12import hid_constants
13import usb_constants
14
15
16class Field(object):
17  """USB descriptor field information."""
18
19  def __init__(self, name, str_fmt, struct_fmt, required):
20    """Define a new USB descriptor field.
21
22    Args:
23      name: Name of the field.
24      str_fmt: Python 'string' module format string for this field.
25      struct_fmt: Python 'struct' module format string for this field.
26      required: Is this a required field?
27    """
28    self.name = name
29    self.str_fmt = str_fmt
30    self.struct_fmt = struct_fmt
31    self.required = required
32
33  def Format(self, value):
34    return self.str_fmt.format(value)
35
36
37class Descriptor(object):
38  """Base class for USB descriptor types.
39
40  This class provides general functionality for creating object types that
41  represent USB descriptors. The AddField and related methods are used to
42  define the fields of each structure. Fields can then be set using keyword
43  arguments to the object constructor or by accessing properties on the object.
44  """
45
46  _fields = None
47
48  @classmethod
49  def AddField(cls, name, struct_fmt, str_fmt='{}', default=None):
50    """Adds a user-specified field to this descriptor.
51
52    Adds a field to the binary structure representing this descriptor. The field
53    can be set by passing a keyword argument name=... to the object constructor
54    will be accessible as foo.name on any instance.
55
56    If no default value is provided then the constructor will through an
57    exception if this field is not one of the provided keyword arguments.
58
59    Args:
60      name: String name of the field.
61      struct_fmt: Python 'struct' module format string for this field.
62      str_fmt: Python 'string' module format string for this field.
63      default: Default value.
64    """
65    if cls._fields is None:
66      cls._fields = []
67    cls._fields.append(Field(name, str_fmt, struct_fmt, default is None))
68
69    member_name = '_{}'.format(name)
70    def Setter(self, value):
71      setattr(self, member_name, value)
72    def Getter(self):
73      try:
74        return getattr(self, member_name)
75      except AttributeError:
76        assert default is not None
77        return default
78
79    setattr(cls, name, property(Getter, Setter))
80
81  @classmethod
82  def AddFixedField(cls, name, struct_fmt, value, str_fmt='{}'):
83    """Adds a constant field to this descriptor.
84
85    Adds a constant field to the binary structure representing this descriptor.
86    The field will be accessible as foo.name on any instance.
87
88    The value of this field may not be given as a constructor parameter or
89    set on an existing instance.
90
91    Args:
92      name: String name of the field.
93      struct_fmt: Python 'struct' module format string for this field.
94      value: Field value.
95      str_fmt: Python 'string' module format string for this field.
96    """
97    if cls._fields is None:
98      cls._fields = []
99    cls._fields.append(Field(name, str_fmt, struct_fmt, False))
100
101    def Setter(unused_self, unused_value):
102      raise RuntimeError('{} is a fixed field.'.format(name))
103    def Getter(unused_self):
104      return value
105
106    setattr(cls, name, property(Getter, Setter))
107
108  @classmethod
109  def AddComputedField(cls, name, struct_fmt, property_name, str_fmt='{}'):
110    """Adds a constant field to this descriptor.
111
112    Adds a field to the binary structure representing this descriptor whos value
113    is equal to an object property. The field will be accessible as foo.name on
114    any instance.
115
116    The value of this field may not be given as a constructor parameter or
117    set on an existing instance.
118
119    Args:
120      name: String name of the field.
121      struct_fmt: Python 'struct' module format string for this field.
122      property_name: Property to read.
123      str_fmt: Python 'string' module format string for this field.
124    """
125    if cls._fields is None:
126      cls._fields = []
127    cls._fields.append(Field(name, str_fmt, struct_fmt, False))
128
129    def Setter(unused_self, unused_value):
130      raise RuntimeError('{} is a computed field.'.format(name))
131    def Getter(self):
132      return getattr(self, property_name)
133
134    setattr(cls, name, property(Getter, Setter))
135
136  def __init__(self, **kwargs):
137    """Constructs a new instance of this descriptor.
138
139    All fields which do not have a default value and are not fixed or computed
140    from a property must be specified as keyword arguments.
141
142    Args:
143      **kwargs: Field values.
144
145    Raises:
146      TypeError: A required field was missing or an unexpected field was given.
147    """
148    fields = {field.name for field in self._fields}
149    required_fields = {field.name for field in self._fields if field.required}
150
151    for arg, value in kwargs.iteritems():
152      if arg not in fields:
153        raise TypeError('Unexpected field: {}'.format(arg))
154
155      setattr(self, arg, value)
156      required_fields.discard(arg)
157
158    if required_fields:
159      raise TypeError('Missing fields: {}'.format(', '.join(required_fields)))
160
161  @property
162  def fmt(self):
163    """Returns the Python 'struct' module format string for this descriptor."""
164    return '<{}'.format(''.join([field.struct_fmt for field in self._fields]))
165
166  @property
167  def struct_size(self):
168    """Returns the size of the struct defined by fmt."""
169    return struct.calcsize(self.fmt)
170
171  @property
172  def total_size(self):
173    """Returns the total size of this descriptor."""
174    return self.struct_size
175
176  def Encode(self):
177    """Returns the binary representation of this descriptor."""
178    values = [getattr(self, field.name) for field in self._fields]
179    return struct.pack(self.fmt, *values)
180
181  def __str__(self):
182    max_length = max(len(field.name) for field in self._fields)
183
184    return '{}:\n  {}'.format(
185        self.__class__.__name__,
186        '\n  '.join('{} {}'.format(
187            '{}:'.format(field.name).ljust(max_length+1),
188            field.Format(getattr(self, field.name))
189        ) for field in self._fields)
190    )
191
192
193class DeviceDescriptor(Descriptor):
194  """Standard Device Descriptor.
195
196  See Universal Serial Bus Specification Revision 2.0 Table 9-8.
197  """
198  pass
199
200DeviceDescriptor.AddComputedField('bLength', 'B', 'struct_size')
201DeviceDescriptor.AddFixedField('bDescriptorType', 'B',
202                               usb_constants.DescriptorType.DEVICE)
203DeviceDescriptor.AddField('bcdUSB', 'H', default=0x0200, str_fmt='0x{:04X}')
204DeviceDescriptor.AddField('bDeviceClass', 'B',
205                          default=usb_constants.DeviceClass.PER_INTERFACE)
206DeviceDescriptor.AddField('bDeviceSubClass', 'B',
207                          default=usb_constants.DeviceSubClass.PER_INTERFACE)
208DeviceDescriptor.AddField('bDeviceProtocol', 'B',
209                          default=usb_constants.DeviceProtocol.PER_INTERFACE)
210DeviceDescriptor.AddField('bMaxPacketSize0', 'B', default=64)
211DeviceDescriptor.AddField('idVendor', 'H', str_fmt='0x{:04X}')
212DeviceDescriptor.AddField('idProduct', 'H', str_fmt='0x{:04X}')
213DeviceDescriptor.AddField('bcdDevice', 'H', str_fmt='0x{:04X}')
214DeviceDescriptor.AddField('iManufacturer', 'B', default=0)
215DeviceDescriptor.AddField('iProduct', 'B', default=0)
216DeviceDescriptor.AddField('iSerialNumber', 'B', default=0)
217DeviceDescriptor.AddField('bNumConfigurations', 'B', default=1)
218
219
220class DescriptorContainer(Descriptor):
221  """Super-class for descriptors which contain more descriptors.
222
223  This class adds the ability for a descriptor to have an array of additional
224  descriptors which follow it.
225  """
226
227  def __init__(self, **kwargs):
228    super(DescriptorContainer, self).__init__(**kwargs)
229    self._descriptors = []
230
231  @property
232  def total_size(self):
233    return self.struct_size + sum([descriptor.total_size
234                                   for descriptor in self._descriptors])
235
236  def Add(self, descriptor):
237    self._descriptors.append(descriptor)
238
239  def Encode(self):
240    bufs = [super(DescriptorContainer, self).Encode()]
241    bufs.extend(descriptor.Encode() for descriptor in self._descriptors)
242    return ''.join(bufs)
243
244  def __str__(self):
245    return '{}\n{}'.format(super(DescriptorContainer, self).__str__(),
246                           '\n'.join(str(descriptor)
247                                     for descriptor in self._descriptors))
248
249
250class ConfigurationDescriptor(DescriptorContainer):
251  """Standard Configuration Descriptor.
252
253  See Universal Serial Bus Specification Revision 2.0 Table 9-10.
254  """
255
256  def __init__(self, **kwargs):
257    super(ConfigurationDescriptor, self).__init__(**kwargs)
258    self._interfaces = {}
259
260  @property
261  def num_interfaces(self):
262    interface_numbers = {key[0] for key in self._interfaces.iterkeys()}
263    return len(interface_numbers)
264
265  def AddInterface(self, interface):
266    key = (interface.bInterfaceNumber, interface.bAlternateSetting)
267    if key in self._interfaces:
268      raise RuntimeError('Interface {} (alternate {}) already defined.'
269                         .format(key[0], key[1]))
270    self._interfaces[key] = interface
271    self.Add(interface)
272
273  def GetInterfaces(self):
274    return self._interfaces.values()
275
276ConfigurationDescriptor.AddComputedField('bLength', 'B', 'struct_size')
277ConfigurationDescriptor.AddFixedField(
278    'bDescriptorType', 'B', usb_constants.DescriptorType.CONFIGURATION)
279ConfigurationDescriptor.AddComputedField('wTotalLength', 'H', 'total_size')
280ConfigurationDescriptor.AddComputedField('bNumInterfaces', 'B',
281                                         'num_interfaces')
282ConfigurationDescriptor.AddField('bConfigurationValue', 'B', default=1)
283ConfigurationDescriptor.AddField('iConfiguration', 'B', default=0)
284ConfigurationDescriptor.AddField('bmAttributes', 'B', str_fmt='0x{:02X}')
285ConfigurationDescriptor.AddField('MaxPower', 'B')
286
287
288class InterfaceDescriptor(DescriptorContainer):
289  """Standard Interface Descriptor.
290
291  See Universal Serial Bus Specification Revision 2.0 Table 9-12.
292  """
293
294  def __init__(self, **kwargs):
295    super(InterfaceDescriptor, self).__init__(**kwargs)
296    self._endpoints = {}
297
298  @property
299  def num_endpoints(self):
300    return len(self._endpoints)
301
302  def AddEndpoint(self, endpoint):
303    if endpoint.bEndpointAddress in self._endpoints:
304      raise RuntimeError('Endpoint 0x{:02X} already defined on this interface.'
305                         .format(endpoint.bEndpointAddress))
306    self._endpoints[endpoint.bEndpointAddress] = endpoint
307    self.Add(endpoint)
308
309  def GetEndpoints(self):
310    return self._endpoints.values()
311
312InterfaceDescriptor.AddComputedField('bLength', 'B', 'struct_size')
313InterfaceDescriptor.AddFixedField('bDescriptorType', 'B',
314                                  usb_constants.DescriptorType.INTERFACE)
315InterfaceDescriptor.AddField('bInterfaceNumber', 'B')
316InterfaceDescriptor.AddField('bAlternateSetting', 'B', default=0)
317InterfaceDescriptor.AddComputedField('bNumEndpoints', 'B', 'num_endpoints')
318InterfaceDescriptor.AddField('bInterfaceClass', 'B',
319                             default=usb_constants.InterfaceClass.VENDOR)
320InterfaceDescriptor.AddField('bInterfaceSubClass', 'B',
321                             default=usb_constants.InterfaceSubClass.VENDOR)
322InterfaceDescriptor.AddField('bInterfaceProtocol', 'B',
323                             default=usb_constants.InterfaceProtocol.VENDOR)
324InterfaceDescriptor.AddField('iInterface', 'B', default=0)
325
326
327class EndpointDescriptor(Descriptor):
328  """Standard Endpoint Descriptor.
329
330  See Universal Serial Bus Specification Revision 2.0 Table 9-13.
331  """
332  pass
333
334EndpointDescriptor.AddComputedField('bLength', 'B', 'struct_size')
335EndpointDescriptor.AddFixedField('bDescriptorType', 'B',
336                                 usb_constants.DescriptorType.ENDPOINT)
337EndpointDescriptor.AddField('bEndpointAddress', 'B', str_fmt='0x{:02X}')
338EndpointDescriptor.AddField('bmAttributes', 'B', str_fmt='0x{:02X}')
339EndpointDescriptor.AddField('wMaxPacketSize', 'H')
340EndpointDescriptor.AddField('bInterval', 'B')
341
342
343class HidDescriptor(Descriptor):
344  """HID Descriptor.
345
346  See Device Class Definition for Human Interface Devices (HID) Version 1.11
347  section 6.2.1.
348  """
349
350  def __init__(self, **kwargs):
351    super(HidDescriptor, self).__init__(**kwargs)
352    self._descriptors = []
353
354  def AddDescriptor(self, typ, length):
355    self._descriptors.append((typ, length))
356
357  @property
358  def struct_size(self):
359    return super(HidDescriptor, self).struct_size + self.num_descriptors * 3
360
361  @property
362  def num_descriptors(self):
363    return len(self._descriptors)
364
365  def Encode(self):
366    bufs = [super(HidDescriptor, self).Encode()]
367    bufs.extend(struct.pack('<BH', typ, length)
368                for typ, length in self._descriptors)
369    return ''.join(bufs)
370
371  def __str__(self):
372    return '{}\n{}'.format(
373        super(HidDescriptor, self).__str__(),
374        '\n'.join('  bDescriptorType: 0x{:02X}\n  wDescriptorLength: {}'
375                  .format(typ, length) for typ, length in self._descriptors))
376
377HidDescriptor.AddComputedField('bLength', 'B', 'struct_size')
378HidDescriptor.AddFixedField('bDescriptorType', 'B',
379                            hid_constants.DescriptorType.HID)
380HidDescriptor.AddField('bcdHID', 'H', default=0x0111, str_fmt='0x{:04X}')
381HidDescriptor.AddField('bCountryCode', 'B', default=0)
382HidDescriptor.AddComputedField('bNumDescriptors', 'B', 'num_descriptors')
383