1# Copyright 2015 The Chromium OS Authors. 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
5from autotest_lib.client.cros.cellular.mbim_compliance import mbim_errors
6from autotest_lib.client.cros.cellular.mbim_compliance import usb_descriptors
7
8
9class MbimDescriptorCache(object):
10    """
11    Class used to store a cache of the most frequently used MBIM descriptors.
12    This caching of descriptors avoids frequent access to the device for
13    any control information.
14
15    """
16
17    def __init__(self, descriptors):
18        """
19        Store the the relevant descriptors from the list of descriptors.
20
21        @param descriptors: Raw descriptor set obtained from the device.
22                            Type: Array of |usb_descriptors.Descriptor| objects.
23
24        """
25        if self._check_ncm_mbim_device(descriptors):
26            self._update_ncm_mbim_cache(descriptors)
27        else:
28            self._update_mbim_cache(descriptors)
29
30
31    def _store_in_cache(self,
32                        descriptors,
33                        mbim_communication_interface,
34                        ncm_communication_interface,
35                        no_data_data_interface,
36                        mbim_data_interface,
37                        ncm_data_interface):
38        """
39        Store the MBIM/NCM interface descriptors into the |device_context| and
40        also fetch the MBIM funnction descriptor, interrrupt endpoint
41        descriptor and bulk endpoint descriptors.
42
43        @param descriptors: Raw descriptor set obtained from the device.
44                Type: Array of |usb_descriptors.Descriptor| objects.
45        @param mbim_communication_interface: MBIM communication interface
46                descriptor object.
47        @param ncm_communication_interface: NCM communication interface
48                descriptor object if the device supports NCM/MBIM.
49        @param no_data_data_interface: MBIM/NCM data interface object. To be set
50                when not being used for any active data transfer.
51        @param mbim_data_interface: MBIM data interface object. To be set
52                when being used for any active MBIM NTB data transfer.
53        @param ncm_data_interface: NCM data interface object. To be set
54                when being used for any active NCM NTB data transfer.
55
56        """
57        # Fetch the MBIM function descriptor
58        mbim_communication_interface_bundle = (
59                usb_descriptors.get_descriptor_bundle(
60                        descriptors, mbim_communication_interface))
61        mbim_descriptors = usb_descriptors.filter_descriptors(
62                usb_descriptors.MBIMFunctionalDescriptor,
63                mbim_communication_interface_bundle)
64        if not mbim_descriptors:
65            mbim_errors.log_and_raise(
66                    mbim_errors.MBIMComplianceTestError,
67                    'No MBIM functional descriptor found')
68
69        # Fetch the MBIM interrupt enpoint
70        interrupt_endpoint_descriptors = usb_descriptors.filter_descriptors(
71                usb_descriptors.EndpointDescriptor,
72                mbim_communication_interface_bundle)
73        if not interrupt_endpoint_descriptors:
74            mbim_errors.log_and_raise(
75                    mbim_errors.MBIMComplianceTestError,
76                    'No MBIM Interrupt Endpoint descriptor found')
77
78        # Fetch the MBIM bulk-in/out endpoints
79        mbim_data_interface_bundle = (
80                usb_descriptors.get_descriptor_bundle(
81                        descriptors, mbim_data_interface))
82        bulk_endpoint_descriptors = usb_descriptors.filter_descriptors(
83                usb_descriptors.EndpointDescriptor,
84                mbim_data_interface_bundle)
85        if len(bulk_endpoint_descriptors) != 2:
86            mbim_errors.log_and_raise(
87                    mbim_errors.MBIMComplianceTestError,
88                    'MBIM Bulk-In/Bulk-Out Endpoint descriptors not found')
89
90        # Update with MBIM function settings.
91        self.ncm_communication_interface = ncm_communication_interface
92        self.mbim_communication_interface = mbim_communication_interface
93        self.no_data_data_interface = no_data_data_interface
94        self.ncm_data_interface = ncm_data_interface
95        self.mbim_data_interface = mbim_data_interface
96        self.mbim_functional = mbim_descriptors[0]
97        self.interrupt_endpoint = interrupt_endpoint_descriptors[0]
98        for endpoint in bulk_endpoint_descriptors:
99            # Check for MSB bit to determine if it is a
100            # BULK-OUT vs BULK-IN endpoint
101            if endpoint.bEndpointAddress < 0x80:
102                self.bulk_out_endpoint = endpoint
103            else:
104                self.bulk_in_endpoint = endpoint
105
106
107    def _update_mbim_cache(self, descriptors):
108        """
109        Parse and cache given raw |descriptors| as MBIM descriptors.
110
111        """
112        self.is_mbim_only = True
113
114        # Fetch the MBIM communication interface
115        interfaces = usb_descriptors.filter_descriptors(
116                usb_descriptors.InterfaceDescriptor, descriptors)
117        mbim_communication_interfaces = (
118                usb_descriptors.filter_interface_descriptors(
119                        interfaces,
120                        usb_descriptors.MBIM_ONLY_COMMUNICATION_INTERFACE))
121        if not mbim_communication_interfaces:
122            mbim_errors.log_and_raise(
123                    mbim_errors.MBIMComplianceTestError,
124                    'No MBIM communication interface descriptor found')
125
126        # Fetch the MBIM no_data data interface
127        no_data_data_interfaces = (
128                usb_descriptors.filter_interface_descriptors(
129                        interfaces,
130                        usb_descriptors.MBIM_ONLY_DATA_INTERFACE_NO_DATA))
131        if not no_data_data_interfaces:
132            mbim_errors.log_and_raise(
133                    mbim_errors.MBIMComplianceTestError,
134                    'No No_Data data interface descriptor found')
135        # Fetch the MBIM data interface
136        mbim_data_interfaces = (
137                usb_descriptors.filter_interface_descriptors(
138                        interfaces,
139                        usb_descriptors.MBIM_ONLY_DATA_INTERFACE_MBIM))
140        if not mbim_data_interfaces:
141            mbim_errors.log_and_raise(
142                    mbim_errors.MBIMComplianceTestError,
143                    'No MBIM data interface descriptor found')
144
145        # Store the info in our |device_context| cache
146        self._store_in_cache(descriptors,
147                             mbim_communication_interfaces[0],
148                             None,
149                             no_data_data_interfaces[0],
150                             mbim_data_interfaces[0],
151                             None)
152
153
154    def _update_ncm_mbim_cache(self, descriptors):
155        """
156        Parse and cache given raw |descriptors| as NCM + MBIM descriptors.
157
158        """
159        self.is_mbim_only = False
160
161        # Fetch the NCM communication interface
162        interfaces = usb_descriptors.filter_descriptors(
163                usb_descriptors.InterfaceDescriptor, descriptors)
164        ncm_communication_interfaces = (
165                usb_descriptors.filter_interface_descriptors(
166                        interfaces,
167                        usb_descriptors.NCM_MBIM_COMMUNICATION_INTERFACE_NCM))
168
169        # Fetch the MBIM communication interface
170        mbim_communication_interfaces = (
171                usb_descriptors.filter_interface_descriptors(
172                        interfaces,
173                        usb_descriptors.NCM_MBIM_COMMUNICATION_INTERFACE_MBIM))
174        if not mbim_communication_interfaces:
175            mbim_errors.log_and_raise(
176                    mbim_errors.MBIMComplianceTestError,
177                    'No MBIM communication interface descriptor found')
178
179        # Fetch the NCM + MBIM no_data data interface
180        no_data_data_interfaces = (
181                usb_descriptors.filter_interface_descriptors(
182                        interfaces,
183                        usb_descriptors.NCM_MBIM_DATA_INTERFACE_NO_DATA))
184        if not no_data_data_interfaces:
185            mbim_errors.log_and_raise(
186                    mbim_errors.MBIMComplianceTestError,
187                    'No No_Data data interface descriptor found')
188        # Fetch the NCM data interface
189        ncm_data_interfaces = (
190                usb_descriptors.filter_interface_descriptors(
191                        interfaces,
192                        usb_descriptors.NCM_MBIM_DATA_INTERFACE_NCM))
193        if not ncm_data_interfaces:
194            mbim_errors.log_and_raise(
195                    mbim_errors.MBIMComplianceTestError,
196                    'No NCM data interface descriptor found')
197        # Fetch the MBIM data interface
198        mbim_data_interfaces = (
199                usb_descriptors.filter_interface_descriptors(
200                        interfaces,
201                        usb_descriptors.NCM_MBIM_DATA_INTERFACE_MBIM))
202        if not mbim_data_interfaces:
203            mbim_errors.log_and_raise(
204                    mbim_errors.MBIMComplianceTestError,
205                    'No MBIM data interface descriptor found')
206
207        # Store the info in our |device_context| cache
208        self._store_in_cache(descriptors,
209                             mbim_communication_interfaces[0],
210                             ncm_communication_interfaces[0],
211                             no_data_data_interfaces[0],
212                             mbim_data_interfaces[0],
213                             ncm_data_interfaces[0])
214
215
216    def _check_ncm_mbim_device(self, descriptors):
217        """
218        Checks whether the connected device supports NCM + MBIM or MBIM only.
219
220        @returns True if the device supports NCM + MBIM, else False
221
222        """
223        # Only a dual NCM/MBIM device has an NCM communication interface
224        # in its descriptors
225        interfaces = usb_descriptors.filter_descriptors(
226                usb_descriptors.InterfaceDescriptor, descriptors)
227        ncm_communication_interfaces = (
228                usb_descriptors.filter_interface_descriptors(
229                        interfaces,
230                        usb_descriptors.NCM_MBIM_COMMUNICATION_INTERFACE_NCM))
231        return bool(ncm_communication_interfaces)
232