• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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