# Copyright 2015 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. from autotest_lib.client.cros.cellular.mbim_compliance import mbim_errors from autotest_lib.client.cros.cellular.mbim_compliance import usb_descriptors class MbimDescriptorCache(object): """ Class used to store a cache of the most frequently used MBIM descriptors. This caching of descriptors avoids frequent access to the device for any control information. """ def __init__(self, descriptors): """ Store the the relevant descriptors from the list of descriptors. @param descriptors: Raw descriptor set obtained from the device. Type: Array of |usb_descriptors.Descriptor| objects. """ if self._check_ncm_mbim_device(descriptors): self._update_ncm_mbim_cache(descriptors) else: self._update_mbim_cache(descriptors) def _store_in_cache(self, descriptors, mbim_communication_interface, ncm_communication_interface, no_data_data_interface, mbim_data_interface, ncm_data_interface): """ Store the MBIM/NCM interface descriptors into the |device_context| and also fetch the MBIM funnction descriptor, interrrupt endpoint descriptor and bulk endpoint descriptors. @param descriptors: Raw descriptor set obtained from the device. Type: Array of |usb_descriptors.Descriptor| objects. @param mbim_communication_interface: MBIM communication interface descriptor object. @param ncm_communication_interface: NCM communication interface descriptor object if the device supports NCM/MBIM. @param no_data_data_interface: MBIM/NCM data interface object. To be set when not being used for any active data transfer. @param mbim_data_interface: MBIM data interface object. To be set when being used for any active MBIM NTB data transfer. @param ncm_data_interface: NCM data interface object. To be set when being used for any active NCM NTB data transfer. """ # Fetch the MBIM function descriptor mbim_communication_interface_bundle = ( usb_descriptors.get_descriptor_bundle( descriptors, mbim_communication_interface)) mbim_descriptors = usb_descriptors.filter_descriptors( usb_descriptors.MBIMFunctionalDescriptor, mbim_communication_interface_bundle) if not mbim_descriptors: mbim_errors.log_and_raise( mbim_errors.MBIMComplianceTestError, 'No MBIM functional descriptor found') # Fetch the MBIM interrupt enpoint interrupt_endpoint_descriptors = usb_descriptors.filter_descriptors( usb_descriptors.EndpointDescriptor, mbim_communication_interface_bundle) if not interrupt_endpoint_descriptors: mbim_errors.log_and_raise( mbim_errors.MBIMComplianceTestError, 'No MBIM Interrupt Endpoint descriptor found') # Fetch the MBIM bulk-in/out endpoints mbim_data_interface_bundle = ( usb_descriptors.get_descriptor_bundle( descriptors, mbim_data_interface)) bulk_endpoint_descriptors = usb_descriptors.filter_descriptors( usb_descriptors.EndpointDescriptor, mbim_data_interface_bundle) if len(bulk_endpoint_descriptors) != 2: mbim_errors.log_and_raise( mbim_errors.MBIMComplianceTestError, 'MBIM Bulk-In/Bulk-Out Endpoint descriptors not found') # Update with MBIM function settings. self.ncm_communication_interface = ncm_communication_interface self.mbim_communication_interface = mbim_communication_interface self.no_data_data_interface = no_data_data_interface self.ncm_data_interface = ncm_data_interface self.mbim_data_interface = mbim_data_interface self.mbim_functional = mbim_descriptors[0] self.interrupt_endpoint = interrupt_endpoint_descriptors[0] for endpoint in bulk_endpoint_descriptors: # Check for MSB bit to determine if it is a # BULK-OUT vs BULK-IN endpoint if endpoint.bEndpointAddress < 0x80: self.bulk_out_endpoint = endpoint else: self.bulk_in_endpoint = endpoint def _update_mbim_cache(self, descriptors): """ Parse and cache given raw |descriptors| as MBIM descriptors. """ self.is_mbim_only = True # Fetch the MBIM communication interface interfaces = usb_descriptors.filter_descriptors( usb_descriptors.InterfaceDescriptor, descriptors) mbim_communication_interfaces = ( usb_descriptors.filter_interface_descriptors( interfaces, usb_descriptors.MBIM_ONLY_COMMUNICATION_INTERFACE)) if not mbim_communication_interfaces: mbim_errors.log_and_raise( mbim_errors.MBIMComplianceTestError, 'No MBIM communication interface descriptor found') # Fetch the MBIM no_data data interface no_data_data_interfaces = ( usb_descriptors.filter_interface_descriptors( interfaces, usb_descriptors.MBIM_ONLY_DATA_INTERFACE_NO_DATA)) if not no_data_data_interfaces: mbim_errors.log_and_raise( mbim_errors.MBIMComplianceTestError, 'No No_Data data interface descriptor found') # Fetch the MBIM data interface mbim_data_interfaces = ( usb_descriptors.filter_interface_descriptors( interfaces, usb_descriptors.MBIM_ONLY_DATA_INTERFACE_MBIM)) if not mbim_data_interfaces: mbim_errors.log_and_raise( mbim_errors.MBIMComplianceTestError, 'No MBIM data interface descriptor found') # Store the info in our |device_context| cache self._store_in_cache(descriptors, mbim_communication_interfaces[0], None, no_data_data_interfaces[0], mbim_data_interfaces[0], None) def _update_ncm_mbim_cache(self, descriptors): """ Parse and cache given raw |descriptors| as NCM + MBIM descriptors. """ self.is_mbim_only = False # Fetch the NCM communication interface interfaces = usb_descriptors.filter_descriptors( usb_descriptors.InterfaceDescriptor, descriptors) ncm_communication_interfaces = ( usb_descriptors.filter_interface_descriptors( interfaces, usb_descriptors.NCM_MBIM_COMMUNICATION_INTERFACE_NCM)) # Fetch the MBIM communication interface mbim_communication_interfaces = ( usb_descriptors.filter_interface_descriptors( interfaces, usb_descriptors.NCM_MBIM_COMMUNICATION_INTERFACE_MBIM)) if not mbim_communication_interfaces: mbim_errors.log_and_raise( mbim_errors.MBIMComplianceTestError, 'No MBIM communication interface descriptor found') # Fetch the NCM + MBIM no_data data interface no_data_data_interfaces = ( usb_descriptors.filter_interface_descriptors( interfaces, usb_descriptors.NCM_MBIM_DATA_INTERFACE_NO_DATA)) if not no_data_data_interfaces: mbim_errors.log_and_raise( mbim_errors.MBIMComplianceTestError, 'No No_Data data interface descriptor found') # Fetch the NCM data interface ncm_data_interfaces = ( usb_descriptors.filter_interface_descriptors( interfaces, usb_descriptors.NCM_MBIM_DATA_INTERFACE_NCM)) if not ncm_data_interfaces: mbim_errors.log_and_raise( mbim_errors.MBIMComplianceTestError, 'No NCM data interface descriptor found') # Fetch the MBIM data interface mbim_data_interfaces = ( usb_descriptors.filter_interface_descriptors( interfaces, usb_descriptors.NCM_MBIM_DATA_INTERFACE_MBIM)) if not mbim_data_interfaces: mbim_errors.log_and_raise( mbim_errors.MBIMComplianceTestError, 'No MBIM data interface descriptor found') # Store the info in our |device_context| cache self._store_in_cache(descriptors, mbim_communication_interfaces[0], ncm_communication_interfaces[0], no_data_data_interfaces[0], mbim_data_interfaces[0], ncm_data_interfaces[0]) def _check_ncm_mbim_device(self, descriptors): """ Checks whether the connected device supports NCM + MBIM or MBIM only. @returns True if the device supports NCM + MBIM, else False """ # Only a dual NCM/MBIM device has an NCM communication interface # in its descriptors interfaces = usb_descriptors.filter_descriptors( usb_descriptors.InterfaceDescriptor, descriptors) ncm_communication_interfaces = ( usb_descriptors.filter_interface_descriptors( interfaces, usb_descriptors.NCM_MBIM_COMMUNICATION_INTERFACE_NCM)) return bool(ncm_communication_interfaces)