• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2015 The chromimn OS Authros. 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
5
6from usb import core
7from usb import util as usb_util
8from autotest_lib.client.cros.cellular.mbim_compliance import mbim_errors
9from autotest_lib.client.cros.cellular.mbim_compliance import \
10        mbim_descriptor_cache
11
12# Device types.
13DEVICE_TYPE_UNKNOWN = 0
14DEVICE_TYPE_MBIM = 1
15DEVICE_TYPE_NCM_MBIM = 2
16# MBIM Communication interface codes
17INTERFACE_MBIM_CLASS = 0x02
18INTERFACE_MBIM_SUBCLASS = 0x0E
19INTERFACE_MBIM_PROTOCOL = 0x00
20
21
22class MbimDeviceContext:
23    """ Context of device under test. """
24
25    def __init__(self, id_vendor=None, id_product=None):
26        """
27        Initialize the MBIM modem device test context.
28
29        @param id_vendor: Specific vendor ID for the modem to be tested.
30        @param id_product: Specific product ID for the modem to be tested.
31
32        """
33        # Find the device to be tested
34        self._device = self._find_device(id_vendor, id_product)
35        # Set the device vendor/product ID in the test context
36        self._id_vendor = self._device.idVendor
37        self._id_product = self._device.idProduct
38
39        # TODO(mcchou): Generalize the order of running sequence and tests by
40        # extracting the information retrieval logic as utility functions.
41        # These utility functions will be used by |get_descriptors_sequence| and
42        # DES_xx tests. Instead of retrieving information from DES_xx tests,
43        # the information should be obtained from |get_descriptors_sequence|.
44
45        # Once a device has been discovered, and its USB descriptors have been
46        # parsed, this property determines whether the discovered device is an
47        # MBIM only function (DEVICE_TYPE_MBIM) or an NCM/MBIM combined function
48        # (DEVICE_TYPE_NCM_MBIM). The other |*_interface| properties are
49        # determined accordingly.
50        self.device_type = DEVICE_TYPE_UNKNOWN
51
52        # The USB descriptor for the communication interface for the modem. This
53        # descirptor corresponds to the alternate setting of the interface over
54        # which mbim control command can be transferred.
55        self.mbim_communication_interface = None
56
57        # The USB descriptor for the communication interface for the modem. This
58        # descriptor corresponds to the alternate setting of the interface over
59        # which ncm control command can be transferred.
60        self.ncm_communication_interface = None
61
62        # The USB descriptor for the CDC Data interface for the modem. This
63        # descriptor corresponds to the alternate setting of the interface over
64        # which no data can be transferred.
65        self.no_data_data_interface = None
66
67        # The USB descriptor for the CDC Data interface for the modem. This
68        # descriptor corresponds to the alternate setting of the interface over
69        # which MBIM data must be transferred.
70        self.mbim_data_interface = None
71
72        # The USB descriptor for the CDC Data interface for the modem. This
73        # descriptor corresponds to the alternate setting of the interface over
74        # which NCM data must be transferred.
75        self.ncm_data_interface = None
76
77        # The USB descriptor for the MBIM functional settings for the modem.
78        # This descriptor corresponds to the MBIM functional descriptor in the
79        # MBIM communication interface settings.
80        self.mbim_functional = None
81
82        # The USB descriptor for the interrupt endpoint. This descriptor
83        # corresponds to the interrupt endpoint in the MBIM communication
84        # interface where MBIM control messages are sent and received.
85        self.interrupt_endpoint = None
86
87
88    def _find_device(self, id_vendor, id_product):
89        """
90        Find and initialize the MBIM modem device under consideration.
91
92        @param id_vendor: Specific vendor ID for the modem to be tested.
93        @param id_product: Specific product ID for the modem to be tested.
94        @returns The PyUSB handle to the device.
95
96        """
97        # If a specific device VID/PID is sent, we'll use that info to find
98        # the modem, else we'll try to find any MBIM CDC device attached
99        if id_vendor is not None and id_product is not None:
100            device = core.find(idVendor=id_vendor, idProduct=id_product)
101            if device is None:
102                mbim_errors.log_and_raise(
103                        mbim_errors.MBIMComplianceFrameworkError,
104                        'Device not found with VID: %04X, PID: %04X. ' % (
105                                id_vendor, id_product))
106        else:
107            # Find device based on the communication class interface descriptor
108            devices = core.find(
109                    find_all=1,
110                    custom_match=(lambda device: self._device_interface_matcher(
111                            device,
112                            interface_class=INTERFACE_MBIM_CLASS,
113                            interface_subclass=INTERFACE_MBIM_SUBCLASS,
114                            interface_protocol=INTERFACE_MBIM_PROTOCOL)))
115            if not devices:
116                mbim_errors.log_and_raise(
117                        mbim_errors.MBIMComplianceFrameworkError,
118                        'MBIM device not found. ')
119            elif len(devices) > 1:
120                mbim_errors.log_and_raise(
121                        mbim_errors.MBIMComplianceFrameworkError,
122                        'More than one MBIM device found: %d. ' %
123                        len(devices))
124            else:
125                device = devices[0]
126        return device
127
128
129    def _device_interface_matcher(self,
130                                  device,
131                                  interface_class,
132                                  interface_subclass,
133                                  interface_protocol):
134        """
135        Find the USB device with a specific set of interface parameters.
136
137        Go thru all the USB configurations and find an interface
138        descriptor that matches the specified class, subclass and
139        protocol.
140
141        @param device: USB device under consideration.
142        @param interface_class: Class ID to be matched in Interface
143                                descriptor.
144        @param interface_sub_class: Sub class ID to be matched in
145                                    Interface descriptor.
146        @param interface_protocol: Protocol ID to be matched in
147                                   Interface descriptor.
148        @returns True if the device's interface descriptor matches,
149                 False otherwise.
150        """
151        for cfg in device:
152            interface = usb_util.find_descriptor(
153                    cfg,
154                    bInterfaceClass=interface_class,
155                    bInterfaceSubClass=interface_subclass,
156                    bInterfaceProtocol=interface_protocol)
157            if interface is not None:
158                return True
159        return False
160
161
162    def update_descriptor_cache(self, descriptors):
163        """
164        Fetch and store the MBIM descriptor cache into the test context.
165
166        @param descriptors: Raw descriptor set obtained from the device.
167                            Type: Array of |usb_descriptors.Descriptor| objects.
168
169        """
170        self.descriptor_cache = (
171                mbim_descriptor_cache.MbimDescriptorCache(descriptors))
172        if self.descriptor_cache.is_mbim_only:
173            self.device_type = DEVICE_TYPE_MBIM
174        else:
175            self.device_type = DEVICE_TYPE_NCM_MBIM
176
177
178    @property
179    def id_vendor(self):
180        """
181        Refer to the idVendor for the device under test.
182
183        @returns The value of idVendor.
184
185        """
186        return self._id_vendor
187
188
189    @property
190    def id_product(self):
191        """
192        Refer to the idProduct for the device under test.
193
194        @returns The value of idProduct.
195
196        """
197        return self._id_product
198
199
200    @property
201    def device(self):
202        """
203        Refer to the device under test.
204
205        @returns The usb.core.Device object.
206
207        """
208        return self._device
209