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 6 from usb import core 7 from usb import util as usb_util 8 from autotest_lib.client.cros.cellular.mbim_compliance import mbim_errors 9 from autotest_lib.client.cros.cellular.mbim_compliance import \ 10 mbim_descriptor_cache 11 12 # Device types. 13 DEVICE_TYPE_UNKNOWN = 0 14 DEVICE_TYPE_MBIM = 1 15 DEVICE_TYPE_NCM_MBIM = 2 16 # MBIM Communication interface codes 17 INTERFACE_MBIM_CLASS = 0x02 18 INTERFACE_MBIM_SUBCLASS = 0x0E 19 INTERFACE_MBIM_PROTOCOL = 0x00 20 21 22 class 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