usb_descriptors.py revision a5c8c671d35fd3c47da64f3452d721fa5c0685a8
1# Copyright (c) 2014 The Chromium OS 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 5import struct 6from collections import namedtuple 7 8from autotest_lib.client.cros.cellular.mbim_compliance import mbim_errors 9 10 11class DescriptorMeta(type): 12 """ 13 Metaclass for creating a USB descriptor class. 14 15 A derived descriptor class takes raw descriptor data as an array of unsigned 16 bytes via its constructor and parses the data into individual fields stored 17 as instance attributes. A derived class of |Descriptor| should specify the 18 following class attributes as part of the class definition: 19 20 DESCRIPTOR_TYPE: An unsigned 8-bit number specifying the descriptor 21 type. Except for |UnknownDescriptor|, all derived classes should specify 22 this attribute. This attribute can be inherited from a parent class. 23 24 DESCRIPTOR_SUBTYPE: An unsigned 8-bit number specifying the descriptor 25 subtype. Only descriptors have a bDescriptorSubtype field should specify 26 this attribute. 27 28 _FIELDS: A list of field definitions specified as a nested tuple. The 29 field definitions are ordered in the same way as the fields are present 30 in the USB descriptor. Each inner tuple is a field definition and 31 contains two elements. The first element specifies the format 32 character(s), which instructs |struct.unpack_from| how to extract the 33 field from the raw descriptor data. The second element specifies the 34 field name, which is also the attribute name used by an instance of the 35 derived descriptor class for storing the field. Each derived descriptor 36 class must define its own _FIELDS attribute, which must have 37 ('B', 'bLength'), ('B', 'bDescriptorType') as the first two entries. 38 39 """ 40 descriptor_classes = [] 41 42 def __new__(mcs, name, bases, attrs): 43 # The Descriptor base class, which inherits from 'object', is merely 44 # used to establish the class hierarchy and is never constructed from 45 # raw descriptor data. 46 if object in bases: 47 return super(DescriptorMeta, mcs).__new__(mcs, name, bases, attrs) 48 49 if '_FIELDS' not in attrs: 50 raise mbim_errors.MBIMComplianceFrameworkError( 51 '%s must define a _FIELDS attribute' % name) 52 53 field_formats, field_names = zip(*attrs['_FIELDS']) 54 # USB descriptor data are in the little-endian format. 55 data_format = '<' + ''.join(field_formats) 56 unpack_length = struct.calcsize(data_format) 57 58 def descriptor_class_new(cls, data): 59 """ 60 Creates a descriptor instance with the given descriptor data. 61 62 @param cls: The descriptor class of the instance to be created. 63 @param data: The raw descriptor data as an array of unsigned bytes. 64 @return The descriptor instance. 65 66 """ 67 data_length = len(data) 68 69 if unpack_length > data_length: 70 raise mbim_errors.MBIMComplianceFrameworkError( 71 'Expected %d or more bytes of descriptor data, got %d' % 72 (unpack_length, data_length)) 73 74 obj = super(cls, cls).__new__(cls, *struct.unpack_from(data_format, 75 data)) 76 setattr(obj, 'data', data) 77 78 descriptor_type = attrs.get('DESCRIPTOR_TYPE') 79 if (descriptor_type is not None and 80 descriptor_type != obj.bDescriptorType): 81 raise mbim_errors.MBIMComplianceFrameworkError( 82 'Expected descriptor type 0x%02X, got 0x%02X' % 83 (descriptor_type, obj.bDescriptorType)) 84 85 descriptor_subtype = attrs.get('DESCRIPTOR_SUBTYPE') 86 if (descriptor_subtype is not None and 87 descriptor_subtype != obj.bDescriptorSubtype): 88 raise mbim_errors.MBIMComplianceFrameworkError( 89 'Expected descriptor subtype 0x%02X, got 0x%02X' % 90 (descriptor_subtype, obj.bDescriptorSubtype)) 91 92 if data_length != obj.bLength: 93 raise mbim_errors.MBIMComplianceFrameworkError( 94 'Expected descriptor length %d, got %d' % 95 (data_length, obj.bLength)) 96 97 # TODO(benchan): We don't currently handle the case where 98 # |data_length| > |unpack_length|, which happens if the descriptor 99 # contains a variable length field (e.g. StringDescriptor). 100 101 return obj 102 103 attrs['__new__'] = descriptor_class_new 104 descriptor_class = namedtuple(name, field_names) 105 # Prepend the class created via namedtuple to |bases| in order to 106 # correctly resolve the __new__ method while preserving the class 107 # hierarchy. 108 cls = super(DescriptorMeta, mcs).__new__(mcs, name, 109 (descriptor_class,) + bases, 110 attrs) 111 # As Descriptor.__subclasses__() only reports its direct subclasses, 112 # we keep track of all subclasses of Descriptor using the 113 # |DescriptorMeta.descriptor_classes| attribute. 114 mcs.descriptor_classes.append(cls) 115 return cls 116 117 118class Descriptor(object): 119 """ 120 USB Descriptor base class. 121 122 This class should not be instantiated or used directly. 123 124 """ 125 __metaclass__ = DescriptorMeta 126 127 128class UnknownDescriptor(Descriptor): 129 """ 130 Unknown USB Descriptor. 131 132 This class is a catch-all descriptor for unsupported or unknown descriptor 133 types. 134 """ 135 _FIELDS = (('B', 'bLength'), 136 ('B', 'bDescriptorType')) 137 138 139class DeviceDescriptor(Descriptor): 140 """ Device Descriptor. """ 141 DESCRIPTOR_TYPE = 0x01 142 _FIELDS = (('B', 'bLength'), 143 ('B', 'bDescriptorType'), 144 ('H', 'bcdUSB'), 145 ('B', 'bDeviceClass'), 146 ('B', 'bDeviceSubClass'), 147 ('B', 'bDeviceProtocol'), 148 ('B', 'bMaxPacketSize0'), 149 ('H', 'idVendor'), 150 ('H', 'idProduct'), 151 ('H', 'bcdDevice'), 152 ('B', 'iManufacturer'), 153 ('B', 'iProduct'), 154 ('B', 'iSerialNumber'), 155 ('B', 'bNumConfigurations')) 156 157 158class ConfigurationDescriptor(Descriptor): 159 """ Configuration Descriptor. """ 160 DESCRIPTOR_TYPE = 0x02 161 _FIELDS = (('B', 'bLength'), 162 ('B', 'bDescriptorType'), 163 ('H', 'wTotalLength'), 164 ('B', 'bNumInterfaces'), 165 ('B', 'bConfigurationValue'), 166 ('B', 'iConfiguration'), 167 ('B', 'bmAttributes'), 168 ('B', 'bMaxPower')) 169 170 171class InterfaceDescriptor(Descriptor): 172 """ Interface Descriptor. """ 173 DESCRIPTOR_TYPE = 0x04 174 _FIELDS = (('B', 'bLength'), 175 ('B', 'bDescriptorType'), 176 ('B', 'bInterfaceNumber'), 177 ('B', 'bAlternateSetting'), 178 ('B', 'bNumEndpoints'), 179 ('B', 'bInterfaceClass'), 180 ('B', 'bInterfaceSubClass'), 181 ('B', 'bInterfaceProtocol'), 182 ('B', 'iInterface')) 183 184 185class EndpointDescriptor(Descriptor): 186 """ Endpoint Descriptor. """ 187 DESCRIPTOR_TYPE = 0x05 188 _FIELDS = (('B', 'bLength'), 189 ('B', 'bDescriptorType'), 190 ('B', 'bEndpointAddress'), 191 ('B', 'bmAttributes'), 192 ('H', 'wMaxPacketSize'), 193 ('B', 'bInterval')) 194 195 196class InterfaceAssociationDescriptor(Descriptor): 197 """ Interface Asscociation Descriptor. """ 198 DESCRIPTOR_TYPE = 0x0B 199 _FIELDS = (('B', 'bLength'), 200 ('B', 'bDescriptorType'), 201 ('B', 'bFirstInterface'), 202 ('B', 'bInterfaceCount'), 203 ('B', 'bFunctionClass'), 204 ('B', 'bFunctionSubClass'), 205 ('B', 'bFunctionProtocol'), 206 ('B', 'iFunction')) 207 208 209class FunctionalDescriptor(Descriptor): 210 """ Functional Descriptor. """ 211 DESCRIPTOR_TYPE = 0x24 212 _FIELDS = (('B', 'bLength'), 213 ('B', 'bDescriptorType'), 214 ('B', 'bDescriptorSubtype')) 215 216 217class HeaderFunctionalDescriptor(FunctionalDescriptor): 218 """ Header Functional Descriptor. """ 219 DESCRIPTOR_SUBTYPE = 0x00 220 _FIELDS = (('B', 'bLength'), 221 ('B', 'bDescriptorType'), 222 ('B', 'bDescriptorSubtype'), 223 ('H', 'bcdCDC')) 224 225 226class UnionFunctionalDescriptor(FunctionalDescriptor): 227 """ Union Functional Descriptor. """ 228 DESCRIPTOR_SUBTYPE = 0x06 229 _FIELDS = (('B', 'bLength'), 230 ('B', 'bDescriptorType'), 231 ('B', 'bDescriptorSubtype'), 232 ('B', 'bControlInterface'), 233 ('B', 'bSubordinateInterface0')) 234 235 236class MBIMFunctionalDescriptor(FunctionalDescriptor): 237 """ MBIM Functional Descriptor. """ 238 DESCRIPTOR_SUBTYPE = 0x1B 239 _FIELDS = (('B', 'bLength'), 240 ('B', 'bDescriptorType'), 241 ('B', 'bDescriptorSubtype'), 242 ('H', 'bcdMBIMVersion'), 243 ('H', 'wMaxControlMessage'), 244 ('B', 'bNumberFilters'), 245 ('B', 'bMaxFilterSize'), 246 ('H', 'wMaxSegmentSize'), 247 ('B', 'bmNetworkCapabilities')) 248 249 250class MBIMExtendedFunctionalDescriptor(FunctionalDescriptor): 251 """ MBIM Extended Functional Descriptor. """ 252 DESCRIPTOR_SUBTYPE = 0x1C 253 _FIELDS = (('B', 'bLength'), 254 ('B', 'bDescriptorType'), 255 ('B', 'bDescriptorSubtype'), 256 ('H', 'bcdMBIMExtendedVersion'), 257 ('B', 'bMaxOutstandingCommandMessages'), 258 ('H', 'wMTU')) 259 260 261class SuperSpeedEndpointCompanionDescriptor(Descriptor): 262 """ SuperSpeed Endpoint Companion Descriptor. """ 263 DESCRIPTOR_TYPE = 0x30 264 _FIELDS = (('B', 'bLength'), 265 ('B', 'bDescriptorType'), 266 ('B', 'bMaxBurst'), 267 ('B', 'bmAttributes'), 268 ('H', 'wBytesPerInterval')) 269 270 271class DescriptorParser(object): 272 """ 273 A class for extracting USB descriptors from raw descriptor data. 274 275 This class takes raw descriptor data as an array of unsigned bytes via its 276 constructor and provides an iterator interface to return individual USB 277 descriptors via instances derived from a subclass of Descriptor. 278 279 """ 280 _DESCRIPTOR_CLASS_MAP = { 281 (cls.DESCRIPTOR_TYPE, getattr(cls, 'DESCRIPTOR_SUBTYPE', None)): cls 282 for cls in DescriptorMeta.descriptor_classes 283 if hasattr(cls, 'DESCRIPTOR_TYPE') 284 } 285 286 def __init__(self, data): 287 self._data = data 288 self._data_length = len(data) 289 self._index = 0 290 291 def __iter__(self): 292 return self 293 294 def next(self): 295 """ 296 Returns the next descriptor found in the descriptor data. 297 298 @return An instance of a subclass of Descriptor. 299 @raise StopIteration if no more descriptor is found, 300 301 """ 302 if self._index >= self._data_length: 303 raise StopIteration 304 305 # Identify the descriptor class based on bDescriptorType, and if 306 # available, bDescriptorSubtype. The descriptor data has a standard 307 # layout as follows: 308 # self._data[self._index]: bLength 309 # self._data[self._index + 1]: bDescriptorType 310 # self._data[self._index + 2]: bDescriptorSubtype for some descriptors 311 descriptor_type, descriptor_subtype = None, None 312 if self._index + 1 < self._data_length: 313 descriptor_type = self._data[self._index + 1] 314 if self._index + 2 < self._data_length: 315 descriptor_subtype = self._data[self._index + 2] 316 317 descriptor_class = self._DESCRIPTOR_CLASS_MAP.get( 318 (descriptor_type, descriptor_subtype), None) 319 if descriptor_class is None: 320 descriptor_class = self._DESCRIPTOR_CLASS_MAP.get( 321 (descriptor_type, None), UnknownDescriptor) 322 323 next_index = self._index + self._data[self._index] 324 descriptor = descriptor_class(self._data[self._index:next_index]) 325 self._index = next_index 326 return descriptor 327