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