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

Side by Side Diff: tools/usb_gadget/usb_descriptors.py

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

Powered by Google App Engine
This is Rietveld 408576698