# Copyright 2015 The chromimn OS Authros. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. from usb import core from usb import util as usb_util from autotest_lib.client.cros.cellular.mbim_compliance import mbim_errors from autotest_lib.client.cros.cellular.mbim_compliance import \ mbim_descriptor_cache # Device types. DEVICE_TYPE_UNKNOWN = 0 DEVICE_TYPE_MBIM = 1 DEVICE_TYPE_NCM_MBIM = 2 # MBIM Communication interface codes INTERFACE_MBIM_CLASS = 0x02 INTERFACE_MBIM_SUBCLASS = 0x0E INTERFACE_MBIM_PROTOCOL = 0x00 class MbimDeviceContext: """ Context of device under test. """ def __init__(self, id_vendor=None, id_product=None): """ Initialize the MBIM modem device test context. @param id_vendor: Specific vendor ID for the modem to be tested. @param id_product: Specific product ID for the modem to be tested. """ # Find the device to be tested self._device = self._find_device(id_vendor, id_product) # Set the device vendor/product ID in the test context self._id_vendor = self._device.idVendor self._id_product = self._device.idProduct # TODO(mcchou): Generalize the order of running sequence and tests by # extracting the information retrieval logic as utility functions. # These utility functions will be used by |get_descriptors_sequence| and # DES_xx tests. Instead of retrieving information from DES_xx tests, # the information should be obtained from |get_descriptors_sequence|. # Once a device has been discovered, and its USB descriptors have been # parsed, this property determines whether the discovered device is an # MBIM only function (DEVICE_TYPE_MBIM) or an NCM/MBIM combined function # (DEVICE_TYPE_NCM_MBIM). The other |*_interface| properties are # determined accordingly. self.device_type = DEVICE_TYPE_UNKNOWN # The USB descriptor for the communication interface for the modem. This # descirptor corresponds to the alternate setting of the interface over # which mbim control command can be transferred. self.mbim_communication_interface = None # The USB descriptor for the communication interface for the modem. This # descriptor corresponds to the alternate setting of the interface over # which ncm control command can be transferred. self.ncm_communication_interface = None # The USB descriptor for the CDC Data interface for the modem. This # descriptor corresponds to the alternate setting of the interface over # which no data can be transferred. self.no_data_data_interface = None # The USB descriptor for the CDC Data interface for the modem. This # descriptor corresponds to the alternate setting of the interface over # which MBIM data must be transferred. self.mbim_data_interface = None # The USB descriptor for the CDC Data interface for the modem. This # descriptor corresponds to the alternate setting of the interface over # which NCM data must be transferred. self.ncm_data_interface = None # The USB descriptor for the MBIM functional settings for the modem. # This descriptor corresponds to the MBIM functional descriptor in the # MBIM communication interface settings. self.mbim_functional = None # The USB descriptor for the interrupt endpoint. This descriptor # corresponds to the interrupt endpoint in the MBIM communication # interface where MBIM control messages are sent and received. self.interrupt_endpoint = None def _find_device(self, id_vendor, id_product): """ Find and initialize the MBIM modem device under consideration. @param id_vendor: Specific vendor ID for the modem to be tested. @param id_product: Specific product ID for the modem to be tested. @returns The PyUSB handle to the device. """ # If a specific device VID/PID is sent, we'll use that info to find # the modem, else we'll try to find any MBIM CDC device attached if id_vendor is not None and id_product is not None: device = core.find(idVendor=id_vendor, idProduct=id_product) if device is None: mbim_errors.log_and_raise( mbim_errors.MBIMComplianceFrameworkError, 'Device not found with VID: %04X, PID: %04X. ' % ( id_vendor, id_product)) else: # Find device based on the communication class interface descriptor devices = core.find( find_all=1, custom_match=(lambda device: self._device_interface_matcher( device, interface_class=INTERFACE_MBIM_CLASS, interface_subclass=INTERFACE_MBIM_SUBCLASS, interface_protocol=INTERFACE_MBIM_PROTOCOL))) if not devices: mbim_errors.log_and_raise( mbim_errors.MBIMComplianceFrameworkError, 'MBIM device not found. ') elif len(devices) > 1: mbim_errors.log_and_raise( mbim_errors.MBIMComplianceFrameworkError, 'More than one MBIM device found: %d. ' % len(devices)) else: device = devices[0] return device def _device_interface_matcher(self, device, interface_class, interface_subclass, interface_protocol): """ Find the USB device with a specific set of interface parameters. Go thru all the USB configurations and find an interface descriptor that matches the specified class, subclass and protocol. @param device: USB device under consideration. @param interface_class: Class ID to be matched in Interface descriptor. @param interface_sub_class: Sub class ID to be matched in Interface descriptor. @param interface_protocol: Protocol ID to be matched in Interface descriptor. @returns True if the device's interface descriptor matches, False otherwise. """ for cfg in device: interface = usb_util.find_descriptor( cfg, bInterfaceClass=interface_class, bInterfaceSubClass=interface_subclass, bInterfaceProtocol=interface_protocol) if interface is not None: return True return False def update_descriptor_cache(self, descriptors): """ Fetch and store the MBIM descriptor cache into the test context. @param descriptors: Raw descriptor set obtained from the device. Type: Array of |usb_descriptors.Descriptor| objects. """ self.descriptor_cache = ( mbim_descriptor_cache.MbimDescriptorCache(descriptors)) if self.descriptor_cache.is_mbim_only: self.device_type = DEVICE_TYPE_MBIM else: self.device_type = DEVICE_TYPE_NCM_MBIM @property def id_vendor(self): """ Refer to the idVendor for the device under test. @returns The value of idVendor. """ return self._id_vendor @property def id_product(self): """ Refer to the idProduct for the device under test. @returns The value of idProduct. """ return self._id_product @property def device(self): """ Refer to the device under test. @returns The usb.core.Device object. """ return self._device