# 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