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

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