1# Copyright 2015 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# All the MBIM_ONLY_* maps are filters for MBIM only function. These maps 11# specify the values of the fields which should be matched in the target 12# interface. 13MBIM_ONLY_COMMUNICATION_INTERFACE = {'bAlternateSetting': 0, 14 'bNumEndpoints': 1, 15 'bInterfaceClass': 0x02, 16 'bInterfaceSubClass': 0x0E, 17 'bInterfaceProtocol': 0x00} 18 19MBIM_ONLY_DATA_INTERFACE_NO_DATA = {'bAlternateSetting': 0, 20 'bNumEndpoints': 0, 21 'bInterfaceClass': 0x0A, 22 'bInterfaceSubClass': 0x00, 23 'bInterfaceProtocol': 0x02} 24 25MBIM_ONLY_DATA_INTERFACE_MBIM = {'bAlternateSetting': 1, 26 'bNumEndpoints': 2, 27 'bInterfaceClass': 0x0A, 28 'bInterfaceSubClass': 0x00, 29 'bInterfaceProtocol': 0x02} 30 31# All the NCM_MBIM_* maps are filters for NCM/MBIM function. These maps 32# specify the values of the fields which should be matched in the target 33# interface. 34NCM_MBIM_COMMUNICATION_INTERFACE_NCM = {'bAlternateSetting': 0, 35 'bNumEndpoints': 1, 36 'bInterfaceClass': 0x02, 37 'bInterfaceSubClass': 0x0D} 38 39NCM_MBIM_COMMUNICATION_INTERFACE_MBIM = {'bAlternateSetting': 1, 40 'bNumEndpoints': 1, 41 'bInterfaceClass': 0x02, 42 'bInterfaceSubClass': 0x0E, 43 'bInterfaceProtocol': 0x00} 44 45NCM_MBIM_DATA_INTERFACE_NO_DATA = {'bAlternateSetting': 0, 46 'bNumEndpoints': 0, 47 'bInterfaceClass': 0x0A, 48 'bInterfaceSubClass': 0x00, 49 'bInterfaceProtocol': 0x01} 50 51NCM_MBIM_DATA_INTERFACE_NCM = {'bAlternateSetting': 1, 52 'bNumEndpoints': 2, 53 'bInterfaceClass': 0x0A, 54 'bInterfaceSubClass': 0x00, 55 'bInterfaceProtocol': 0x01} 56 57NCM_MBIM_DATA_INTERFACE_MBIM = {'bAlternateSetting': 2, 58 'bNumEndpoints': 2, 59 'bInterfaceClass': 0x0A, 60 'bInterfaceSubClass': 0x00, 61 'bInterfaceProtocol': 0x02} 62 63 64class DescriptorMeta(type): 65 """ 66 Metaclass for creating a USB descriptor class. 67 68 A derived descriptor class takes raw descriptor data as an array of unsigned 69 bytes via its constructor and parses the data into individual fields stored 70 as instance attributes. A derived class of |Descriptor| should specify the 71 following class attributes as part of the class definition: 72 73 DESCRIPTOR_TYPE: An unsigned 8-bit number specifying the descriptor 74 type. Except for |UnknownDescriptor|, all derived classes should specify 75 this attribute. This attribute can be inherited from a parent class. 76 77 DESCRIPTOR_SUBTYPE: An unsigned 8-bit number specifying the descriptor 78 subtype. Only descriptors have a bDescriptorSubtype field should specify 79 this attribute. 80 81 _FIELDS: A list of field definitions specified as a nested tuple. The 82 field definitions are ordered in the same way as the fields are present 83 in the USB descriptor. Each inner tuple is a field definition and 84 contains two elements. The first element specifies the format 85 character(s), which instructs |struct.unpack_from| how to extract the 86 field from the raw descriptor data. The second element specifies the 87 field name, which is also the attribute name used by an instance of the 88 derived descriptor class for storing the field. Each derived descriptor 89 class must define its own _FIELDS attribute, which must have 90 ('B', 'bLength'), ('B', 'bDescriptorType') as the first two entries. 91 92 """ 93 descriptor_classes = [] 94 95 def __new__(mcs, name, bases, attrs): 96 # The Descriptor base class, which inherits from 'object', is merely 97 # used to establish the class hierarchy and is never constructed from 98 # raw descriptor data. 99 if object in bases: 100 return super(DescriptorMeta, mcs).__new__(mcs, name, bases, attrs) 101 102 if '_FIELDS' not in attrs: 103 raise mbim_errors.MBIMComplianceFrameworkError( 104 '%s must define a _FIELDS attribute' % name) 105 106 field_formats, field_names = zip(*attrs['_FIELDS']) 107 # USB descriptor data are in the little-endian format. 108 data_format = '<' + ''.join(field_formats) 109 unpack_length = struct.calcsize(data_format) 110 111 def descriptor_class_new(cls, data): 112 """ 113 Creates a descriptor instance with the given descriptor data. 114 115 @param cls: The descriptor class of the instance to be created. 116 @param data: The raw descriptor data as an array of unsigned bytes. 117 @returns The descriptor instance. 118 119 """ 120 data_length = len(data) 121 122 if unpack_length > data_length: 123 raise mbim_errors.MBIMComplianceFrameworkError( 124 'Expected %d or more bytes of descriptor data, got %d' % 125 (unpack_length, data_length)) 126 127 obj = super(cls, cls).__new__(cls, *struct.unpack_from(data_format, 128 data)) 129 setattr(obj, 'data', data) 130 131 descriptor_type = attrs.get('DESCRIPTOR_TYPE') 132 if (descriptor_type is not None and 133 descriptor_type != obj.bDescriptorType): 134 raise mbim_errors.MBIMComplianceFrameworkError( 135 'Expected descriptor type 0x%02X, got 0x%02X' % 136 (descriptor_type, obj.bDescriptorType)) 137 138 descriptor_subtype = attrs.get('DESCRIPTOR_SUBTYPE') 139 if (descriptor_subtype is not None and 140 descriptor_subtype != obj.bDescriptorSubtype): 141 raise mbim_errors.MBIMComplianceFrameworkError( 142 'Expected descriptor subtype 0x%02X, got 0x%02X' % 143 (descriptor_subtype, obj.bDescriptorSubtype)) 144 145 if data_length != obj.bLength: 146 raise mbim_errors.MBIMComplianceFrameworkError( 147 'Expected descriptor length %d, got %d' % 148 (data_length, obj.bLength)) 149 150 # TODO(benchan): We don't currently handle the case where 151 # |data_length| > |unpack_length|, which happens if the descriptor 152 # contains a variable length field (e.g. StringDescriptor). 153 154 return obj 155 156 attrs['__new__'] = descriptor_class_new 157 descriptor_class = namedtuple(name, field_names) 158 # Prepend the class created via namedtuple to |bases| in order to 159 # correctly resolve the __new__ method while preserving the class 160 # hierarchy. 161 cls = super(DescriptorMeta, mcs).__new__(mcs, name, 162 (descriptor_class,) + bases, 163 attrs) 164 # As Descriptor.__subclasses__() only reports its direct subclasses, 165 # we keep track of all subclasses of Descriptor using the 166 # |DescriptorMeta.descriptor_classes| attribute. 167 mcs.descriptor_classes.append(cls) 168 return cls 169 170 171class Descriptor(object): 172 """ 173 USB Descriptor base class. 174 175 This class should not be instantiated or used directly. 176 177 """ 178 __metaclass__ = DescriptorMeta 179 180 181class UnknownDescriptor(Descriptor): 182 """ 183 Unknown USB Descriptor. 184 185 This class is a catch-all descriptor for unsupported or unknown descriptor 186 types. 187 """ 188 _FIELDS = (('B', 'bLength'), 189 ('B', 'bDescriptorType')) 190 191 192class DeviceDescriptor(Descriptor): 193 """ Device Descriptor. """ 194 DESCRIPTOR_TYPE = 0x01 195 _FIELDS = (('B', 'bLength'), 196 ('B', 'bDescriptorType'), 197 ('H', 'bcdUSB'), 198 ('B', 'bDeviceClass'), 199 ('B', 'bDeviceSubClass'), 200 ('B', 'bDeviceProtocol'), 201 ('B', 'bMaxPacketSize0'), 202 ('H', 'idVendor'), 203 ('H', 'idProduct'), 204 ('H', 'bcdDevice'), 205 ('B', 'iManufacturer'), 206 ('B', 'iProduct'), 207 ('B', 'iSerialNumber'), 208 ('B', 'bNumConfigurations')) 209 210 211class ConfigurationDescriptor(Descriptor): 212 """ Configuration Descriptor. """ 213 DESCRIPTOR_TYPE = 0x02 214 _FIELDS = (('B', 'bLength'), 215 ('B', 'bDescriptorType'), 216 ('H', 'wTotalLength'), 217 ('B', 'bNumInterfaces'), 218 ('B', 'bConfigurationValue'), 219 ('B', 'iConfiguration'), 220 ('B', 'bmAttributes'), 221 ('B', 'bMaxPower')) 222 223 224class InterfaceDescriptor(Descriptor): 225 """ Interface Descriptor. """ 226 DESCRIPTOR_TYPE = 0x04 227 _FIELDS = (('B', 'bLength'), 228 ('B', 'bDescriptorType'), 229 ('B', 'bInterfaceNumber'), 230 ('B', 'bAlternateSetting'), 231 ('B', 'bNumEndpoints'), 232 ('B', 'bInterfaceClass'), 233 ('B', 'bInterfaceSubClass'), 234 ('B', 'bInterfaceProtocol'), 235 ('B', 'iInterface')) 236 237 238class EndpointDescriptor(Descriptor): 239 """ Endpoint Descriptor. """ 240 DESCRIPTOR_TYPE = 0x05 241 _FIELDS = (('B', 'bLength'), 242 ('B', 'bDescriptorType'), 243 ('B', 'bEndpointAddress'), 244 ('B', 'bmAttributes'), 245 ('H', 'wMaxPacketSize'), 246 ('B', 'bInterval')) 247 248 249class InterfaceAssociationDescriptor(Descriptor): 250 """ Interface Asscociation Descriptor. """ 251 DESCRIPTOR_TYPE = 0x0B 252 _FIELDS = (('B', 'bLength'), 253 ('B', 'bDescriptorType'), 254 ('B', 'bFirstInterface'), 255 ('B', 'bInterfaceCount'), 256 ('B', 'bFunctionClass'), 257 ('B', 'bFunctionSubClass'), 258 ('B', 'bFunctionProtocol'), 259 ('B', 'iFunction')) 260 261 262class FunctionalDescriptor(Descriptor): 263 """ Functional Descriptor. """ 264 DESCRIPTOR_TYPE = 0x24 265 _FIELDS = (('B', 'bLength'), 266 ('B', 'bDescriptorType'), 267 ('B', 'bDescriptorSubtype')) 268 269 270class HeaderFunctionalDescriptor(FunctionalDescriptor): 271 """ Header Functional Descriptor. """ 272 DESCRIPTOR_SUBTYPE = 0x00 273 _FIELDS = (('B', 'bLength'), 274 ('B', 'bDescriptorType'), 275 ('B', 'bDescriptorSubtype'), 276 ('H', 'bcdCDC')) 277 278 279class UnionFunctionalDescriptor(FunctionalDescriptor): 280 """ Union Functional Descriptor. """ 281 DESCRIPTOR_SUBTYPE = 0x06 282 _FIELDS = (('B', 'bLength'), 283 ('B', 'bDescriptorType'), 284 ('B', 'bDescriptorSubtype'), 285 ('B', 'bControlInterface'), 286 ('B', 'bSubordinateInterface0')) 287 288 289class MBIMFunctionalDescriptor(FunctionalDescriptor): 290 """ MBIM Functional Descriptor. """ 291 DESCRIPTOR_SUBTYPE = 0x1B 292 _FIELDS = (('B', 'bLength'), 293 ('B', 'bDescriptorType'), 294 ('B', 'bDescriptorSubtype'), 295 ('H', 'bcdMBIMVersion'), 296 ('H', 'wMaxControlMessage'), 297 ('B', 'bNumberFilters'), 298 ('B', 'bMaxFilterSize'), 299 ('H', 'wMaxSegmentSize'), 300 ('B', 'bmNetworkCapabilities')) 301 302 303class MBIMExtendedFunctionalDescriptor(FunctionalDescriptor): 304 """ MBIM Extended Functional Descriptor. """ 305 DESCRIPTOR_SUBTYPE = 0x1C 306 _FIELDS = (('B', 'bLength'), 307 ('B', 'bDescriptorType'), 308 ('B', 'bDescriptorSubtype'), 309 ('H', 'bcdMBIMExtendedVersion'), 310 ('B', 'bMaxOutstandingCommandMessages'), 311 ('H', 'wMTU')) 312 313 314class SuperSpeedEndpointCompanionDescriptor(Descriptor): 315 """ SuperSpeed Endpoint Companion Descriptor. """ 316 DESCRIPTOR_TYPE = 0x30 317 _FIELDS = (('B', 'bLength'), 318 ('B', 'bDescriptorType'), 319 ('B', 'bMaxBurst'), 320 ('B', 'bmAttributes'), 321 ('H', 'wBytesPerInterval')) 322 323 324class DescriptorParser(object): 325 """ 326 A class for extracting USB descriptors from raw descriptor data. 327 328 This class takes raw descriptor data as an array of unsigned bytes via its 329 constructor and provides an iterator interface to return individual USB 330 descriptors via instances derived from a subclass of Descriptor. 331 332 """ 333 _DESCRIPTOR_CLASS_MAP = { 334 (cls.DESCRIPTOR_TYPE, getattr(cls, 'DESCRIPTOR_SUBTYPE', None)): cls 335 for cls in DescriptorMeta.descriptor_classes 336 if hasattr(cls, 'DESCRIPTOR_TYPE') 337 } 338 339 def __init__(self, data): 340 self._data = data 341 self._data_length = len(data) 342 self._index = 0 343 # The position of each descriptor in the list. 344 self._descriptor_index = 0 345 346 def __iter__(self): 347 return self 348 349 def next(self): 350 """ 351 Returns the next descriptor found in the descriptor data. 352 353 @returns An instance of a subclass of Descriptor. 354 @raises StopIteration if no more descriptor is found, 355 356 """ 357 if self._index >= self._data_length: 358 raise StopIteration 359 360 # Identify the descriptor class based on bDescriptorType, and if 361 # available, bDescriptorSubtype. The descriptor data has a standard 362 # layout as follows: 363 # self._data[self._index]: bLength 364 # self._data[self._index + 1]: bDescriptorType 365 # self._data[self._index + 2]: bDescriptorSubtype for some descriptors 366 descriptor_type, descriptor_subtype = None, None 367 if self._index + 1 < self._data_length: 368 descriptor_type = self._data[self._index + 1] 369 if self._index + 2 < self._data_length: 370 descriptor_subtype = self._data[self._index + 2] 371 372 descriptor_class = self._DESCRIPTOR_CLASS_MAP.get( 373 (descriptor_type, descriptor_subtype), None) 374 if descriptor_class is None: 375 descriptor_class = self._DESCRIPTOR_CLASS_MAP.get( 376 (descriptor_type, None), UnknownDescriptor) 377 378 next_index = self._index + self._data[self._index] 379 descriptor = descriptor_class(self._data[self._index:next_index]) 380 self._index = next_index 381 descriptor.index = self._descriptor_index 382 self._descriptor_index += 1 383 return descriptor 384 385 386def filter_descriptors(descriptor_type, descriptors): 387 """ 388 Filter a list of descriptors based on the target |descriptor_type|. 389 390 @param descriptor_type: The target descriptor type. 391 @param descriptors: The list of functional descriptors. 392 Type: Array of |Descriptor| objects. 393 @returns A list of target descriptors. 394 395 """ 396 if not descriptors: 397 return [] 398 return filter(lambda descriptor: isinstance(descriptor, descriptor_type), 399 descriptors) 400 401 402def has_distinct_descriptors(descriptors): 403 """ 404 Check if there are distinct descriptors in the given list. 405 406 @param descriptors: The list of descriptors. 407 Type: Array of |Descriptor| objects. 408 @returns True if distinct descriptor are found, False otherwise. 409 410 """ 411 return not all(descriptor == descriptors[0] for descriptor in descriptors) 412 413 414def get_descriptor_bundle(descriptors, descriptor): 415 """ 416 Get the bundle for the |descriptor|. For example, if |descriptor| is of 417 inferface type, this bundle should include functional descriptors and 418 endpoint descriptors. 419 420 @param descriptors: A list of all descriptors. 421 Type: Array of |Descriptor| objects. 422 @param descriptor: The starting point of the bundle. 423 @returns The bundle for |descriptor|. 424 425 """ 426 index = descriptor.index + 1 427 while (index < len(descriptors) and 428 type(descriptor) != type(descriptors[index])): 429 index += 1 430 return descriptors[descriptor.index: index] 431 432 433def filter_interface_descriptors(descriptors, interface_type): 434 """ 435 Filter interface descriptors based on the values in fields. 436 437 @param descriptors: A list of interface descriptors. 438 Type: Array of |Descriptor| objects. 439 @param interface_type: A dictionary composed of pairs(field: value) to 440 match the target interface. 441 @returns A list of target interfaces. 442 443 """ 444 def _match_all_fields(interface): 445 """ 446 Match fields for a given interface descriptor. 447 448 The descriptor is matched based on the fields provided in 449 |interface_type|. 450 451 @param interface: An interface descriptor. 452 Type: |Descriptor| object. 453 @returns True if all fields match, False otherwise. 454 455 """ 456 for key, value in interface_type.iteritems(): 457 if (not hasattr(interface, key) or 458 getattr(interface, key) != value): 459 return False 460 return True 461 462 return filter(lambda descriptor: _match_all_fields(descriptor), 463 descriptors) 464 465 466def has_bulk_in_and_bulk_out(endpoints): 467 """ 468 Check if there are one bulk-in endpoint and one bulk-out endpoint. 469 470 @param endpoints: A list of endpoint descriptors. 471 Type: Array of |Descriptor| objects. 472 @returns True if there are one bulk-in and one bulk-out endpoint, False 473 otherwise. 474 """ 475 bulk_in, bulk_out = False, False 476 for endpoint in endpoints: 477 if (endpoint.bLength == 7 and 478 endpoint.bEndpointAddress < 0x80 and 479 endpoint.bmAttributes == 0x02): 480 bulk_out = True 481 elif (endpoint.bLength == 7 and 482 endpoint.bEndpointAddress >= 0x80 and 483 endpoint.bmAttributes == 0x02): 484 bulk_in = True 485 return bulk_in and bulk_out 486