1# Copyright 2014 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
5import common
6from autotest_lib.client.cros.cellular.mbim_compliance.sequences \
7        import get_descriptors_sequence
8from autotest_lib.client.cros.cellular.mbim_compliance import mbim_errors
9from autotest_lib.client.cros.cellular.mbim_compliance \
10        import mbim_test_base
11from autotest_lib.client.cros.cellular.mbim_compliance import usb_descriptors
12
13
14class cellular_MbimComplianceDES02(mbim_test_base.MbimTestBase):
15    """
16    DES_02 Descriptors Validation for MBIM Only Functions
17
18    This test validates descriptors for MBIM only functions.
19
20    Reference:
21        [1] Universal Serial Bus Communication Class MBIM Compliance Testing: 26
22        http://www.usb.org/developers/docs/devclass_docs/MBIM-Compliance-1.0.pdf
23
24    """
25    version = 1
26
27    def run_internal(self):
28        """ Run the DES_02 test. """
29        # Precondition.
30        descriptors = get_descriptors_sequence.GetDescriptorsSequence(
31                self.device_context).run()
32
33        # Test step 1
34        # Get MBIM communication interface.
35        interfaces = usb_descriptors.filter_descriptors(
36                usb_descriptors.InterfaceDescriptor, descriptors)
37
38        mbim_communication_interfaces = (
39                usb_descriptors.filter_interface_descriptors(
40                        interfaces,
41                        usb_descriptors.MBIM_ONLY_COMMUNICATION_INTERFACE))
42
43        if not mbim_communication_interfaces:
44            mbim_errors.log_and_raise(mbim_errors.MBIMComplianceAssertionError,
45                                      'mbim1.0:6.3#1')
46
47        if len(mbim_communication_interfaces) > 1:
48            mbim_errors.log_and_raise(
49                    mbim_errors.MBIMComplianceGenericAssertionError,
50                    'Expected 1 mbim communication interface, got %d.' % (
51                            len(mbim_communication_interfaces)))
52        mbim_communication_interface = mbim_communication_interfaces[0]
53
54        # Test step 2
55        # Get header functional descriptor, union functional descriptor,
56        # MBIM functional descriptor and MBIM extended functional
57        # descriptor.
58        mbim_communication_interface_bundle = (
59                usb_descriptors.get_descriptor_bundle(
60                        descriptors, mbim_communication_interface))
61
62        header_descriptors = usb_descriptors.filter_descriptors(
63                usb_descriptors.HeaderFunctionalDescriptor,
64                mbim_communication_interface_bundle)
65        union_descriptors = usb_descriptors.filter_descriptors(
66                usb_descriptors.UnionFunctionalDescriptor,
67                mbim_communication_interface_bundle)
68        mbim_descriptors = usb_descriptors.filter_descriptors(
69                usb_descriptors.MBIMFunctionalDescriptor,
70                mbim_communication_interface_bundle)
71        mbim_extended_descriptors = usb_descriptors.filter_descriptors(
72                usb_descriptors.MBIMExtendedFunctionalDescriptor,
73                mbim_communication_interface_bundle)
74        if not(header_descriptors and union_descriptors and mbim_descriptors):
75            mbim_errors.log_and_raise(mbim_errors.MBIMComplianceAssertionError,
76                                      'mbim1.0:6.3#2')
77
78        # Test step 3
79        # Check header functional descriptor.
80        if usb_descriptors.has_distinct_descriptors(header_descriptors):
81            mbim_errors.log_and_raise(
82                    mbim_errors.MBIMComplianceGenericAssertionError,
83                    'Expected 1 unique header functional descriptor.')
84        header_descriptor = header_descriptors[0]
85        if not(header_descriptor.bDescriptorType == 0x24 and
86               header_descriptor.bDescriptorSubtype == 0x00 and
87               header_descriptor.bLength == 5 and
88               header_descriptor.bcdCDC >= 0x0120):
89            mbim_errors.log_and_raise(
90                mbim_errors.MBIMComplianceGenericAssertionError,
91                'Header functional descriptor: wrong value(s)')
92
93        # Test step 4
94        # Check union functional descriptor.
95        if usb_descriptors.has_distinct_descriptors(union_descriptors):
96            mbim_errors.log_and_raise(
97                    mbim_errors.MBIMComplianceGenerisAssertionError,
98                    'Expected 1 unique union functional descriptor.')
99        union_descriptor = union_descriptors[0]
100        if union_descriptor.index < header_descriptor.index:
101            mbim_errors.log_and_raise(mbim_errors.MBIMComplianceAssertionError,
102                                      'mbim1.0:6.3#3')
103
104        # Get CDC no data data interface.
105        no_data_data_interfaces = usb_descriptors.filter_interface_descriptors(
106                interfaces, usb_descriptors.MBIM_ONLY_DATA_INTERFACE_NO_DATA)
107        if not no_data_data_interfaces:
108            mbim_errors.log_and_raise(mbim_errors.MBIMComplianceAssertionError,
109                                      'mbim1.0:6.6#4')
110        if len(no_data_data_interfaces) > 1:
111            mbim_errors.log_and_raise(
112                    mbim_errors.MBIMComplianceGenericAssertionError,
113                    'Exactly 1 CDC data interface, got %d.' % (
114                            len(no_data_data_interfaces)))
115        no_data_data_interface = no_data_data_interfaces[0]
116        no_data_data_interface_bundle = usb_descriptors.get_descriptor_bundle(
117                descriptors, no_data_data_interface)
118        data_endpoint_descriptors = (
119                usb_descriptors.filter_descriptors(
120                        usb_descriptors.EndpointDescriptor,
121                        no_data_data_interface_bundle))
122        if data_endpoint_descriptors:
123            mbim_errors.log_and_raise(mbim_errors.MBIMComplianceAssertionError,
124                                      'mbim1.0:6.6#2')
125
126        # Get MBIM data interface.
127        mbim_data_interfaces = usb_descriptors.filter_interface_descriptors(
128                interfaces, usb_descriptors.MBIM_ONLY_DATA_INTERFACE_MBIM)
129        if not mbim_data_interfaces:
130            mbim_errors.log_and_raise(mbim_errors.MBIMComplianceAssertionError,
131                                      'mbim1.0:6.6#4')
132        if len(mbim_data_interfaces) > 1:
133            mbim_errors.log_and_raise(
134                    mbim_errors.MBIMComplianceGenericAssertionError,
135                    'Expected 1 MBIM data interface, got %d.' % (
136                            len(mbim_data_interfaces)))
137        mbim_data_interface = mbim_data_interfaces[0]
138
139        # Check if there are two endpoint descriptors.
140        if mbim_data_interface.bNumEndpoints != 2:
141            mbim_errors.log_and_raise(mbim_errors.MBIMComplianceAssertionError,
142                                      'mbim1.0:6.6#3.')
143
144        mbim_data_interface_bundle = usb_descriptors.get_descriptor_bundle(
145                descriptors, mbim_data_interface)
146        data_endpoint_descriptors = usb_descriptors.filter_descriptors(
147                usb_descriptors.EndpointDescriptor,
148                mbim_data_interface_bundle)
149
150        # Check the values of fields in endpoint descriptors.
151        # There should be one bulk OUT and one bulk IN.
152        if not usb_descriptors.has_bulk_in_and_bulk_out(
153                data_endpoint_descriptors):
154            mbim_errors.log_and_raise(mbim_errors.MBIMComplianceAssertionError,
155                                      'mbim1.0:6.6#3')
156
157        # MBIM cdc data interface should have both no data data interface and
158        # MBIM data interface. Therefore two interface numbers should be
159        # the same.
160        if (no_data_data_interface.bInterfaceNumber !=
161            mbim_data_interface.bInterfaceNumber):
162            mbim_errors.log_and_raise(mbim_errors.MBIMComplianceAssertionError,
163                                      'mbim1.0:6.6#1')
164
165        # Check the fields of union functional descriptor
166        if not(union_descriptor.bLength == 5 and
167               (union_descriptor.bControlInterface ==
168                mbim_communication_interface.bInterfaceNumber) and
169               (union_descriptor.bSubordinateInterface0 ==
170                mbim_data_interface.bInterfaceNumber)):
171            mbim_errors.log_and_raise(mbim_errors.MBIMComplianceAssertionError,
172                                      'mbim1.0:6.3#4')
173
174        # Test step 5
175        # Get MBIM functional descriptor.
176        if usb_descriptors.has_distinct_descriptors(mbim_descriptors):
177            mbim_errors.log_and_raise(
178                    mbim_errors.MBIMComplianceGenericAssertionError,
179                    'Expected 1 unique MBIM functional descriptor.')
180        mbim_descriptor = mbim_descriptors[0]
181
182        if mbim_descriptor.index < header_descriptor.index:
183            mbim_errors.log_and_raise(mbim_errors.MBIMComplianceAssertionError,
184                                      'mbim1.0:6.3#3')
185
186        if mbim_descriptor.bLength != 12:
187            mbim_errors.log_and_raise(mbim_errors.MBIMComplianceAssertionError,
188                                      'mbim1.0:6.4#5')
189
190        if mbim_descriptor.bcdMBIMVersion != 0x0100:
191            mbim_errors.log_and_raise(mbim_errors.MBIMComplianceAssertionError,
192                                      'mbim1.0:6.4#6')
193
194        if mbim_descriptor.wMaxControlMessage < 64:
195            mbim_errors.log_and_raise(mbim_errors.MBIMComplianceAssertionError,
196                                      'mbim1.0:6.4#1')
197
198        if mbim_descriptor.bNumberFilters < 16:
199            mbim_errors.log_and_raise(mbim_errors.MBIMComplianceAssertionError,
200                                      'mbim1.0:6.4#2')
201
202        if mbim_descriptor.bMaxFilterSize > 192:
203            mbim_errors.log_and_raise(mbim_errors.MBIMComplianceAssertionError,
204                                      'mbim1.0:6.4#3')
205
206        # TODO(mcchou): Most of vendors set wMaxSegmentSize to be less than
207        # 1500, so this assertion is skipped for now.
208        #
209        #if not mbim_descriptor.wMaxSegmentSize >= 2048:
210        #    mbim_errors.log_and_raise(mbim_errors.MBIMComplianceAssertionError,
211        #                              'mbim1.0:6.4#4')
212
213        # Use a byte as the mask to check if D0, D1, D2, D4, D6 and D7 are
214        # zeros.
215        if (mbim_descriptor.bmNetworkCapabilities & 0b11010111) > 0:
216            mbim_errors.log_and_raise(mbim_errors.MBIMComplianceAssertionError,
217                                      'mbim1.0:6.4#7')
218
219        # Test step 6
220        # Get MBIM extended functional descriptor, which is optional.
221        if len(mbim_extended_descriptors) >= 1:
222            if usb_descriptors.has_distinct_descriptors(
223                    mbim_extended_descriptors):
224                mbim_errors.log_and_raise(
225                        mbim_errors.MBIMComplianceGenerisAssertionError,
226                        'Expected 1 unique MBIM extended functional '
227                        'descriptor.')
228            mbim_extended_descriptor = mbim_extended_descriptors[0]
229
230            if mbim_extended_descriptor.index < mbim_descriptor.index:
231                mbim_errors.log_and_raise(
232                        mbim_errors.MBIMComplianceAssertionError,
233                        'mbim1.0:6.5#1')
234
235            if mbim_extended_descriptor.bLength != 8:
236                mbim_errors.log_and_raise(
237                        mbim_errors.MBIMComplianceAssertionError,
238                        'mbim1.0:6.5#2')
239
240            if mbim_extended_descriptor.bcdMBIMExtendedVersion != 0x0100:
241                mbim_errors.log_and_raise(
242                        mbim_errors.MBIMComplianceAssertionError,
243                        'mbim1.0:6.5#3')
244
245            if mbim_extended_descriptor.bMaxOutstandingCommandMessages == 0:
246                mbim_errors.log_and_raise(
247                        mbim_errors.MBIMComplianceAssertionError,
248                        'mbim1.0:6.5#4')
249
250        # Test step 7
251        # Get the first endpoint for the communication interface.
252        interrupt_endpoint_descriptors = usb_descriptors.filter_descriptors(
253                usb_descriptors.EndpointDescriptor,
254                mbim_communication_interface_bundle)
255
256        if len(interrupt_endpoint_descriptors) != 1:
257            mbim_errors.log_and_raise(
258                    mbim_errors.MBIMComplianceGenericAssertionError,
259                    'Expected 1 endpoint, got %d.' % (
260                            len(interrupt_endpoint_descriptors)))
261        interrupt_endpoint_descriptor = interrupt_endpoint_descriptors[0]
262        if not (interrupt_endpoint_descriptor.bDescriptorType == 0x05 and
263                interrupt_endpoint_descriptor.bLength == 7 and
264                interrupt_endpoint_descriptor.bEndpointAddress >= 0x80 and
265                interrupt_endpoint_descriptor.bmAttributes == 0x03):
266            mbim_errors.log_and_raise(mbim_errors.MBIMComplianceAssertionError,
267                                      'mbim1.0:6.3#5')
268
269        appear_before_functional_descriptors = False
270        if mbim_extended_descriptors:
271            if (mbim_extended_descriptor.index >
272                interrupt_endpoint_descriptor.index):
273                appear_before_functional_descriptors = True
274        else:
275            if (mbim_descriptor.index > interrupt_endpoint_descriptor.index or
276                union_descriptor.index > interrupt_endpoint_descriptor.index):
277                appear_before_functional_descriptors = True
278        if appear_before_functional_descriptors:
279            mbim_errors.log_and_raise(
280                    mbim_errors.MBIMComplianceGenericAssertionError,
281                    'All functional descriptors must appear before endpoint'
282                    'descriptors.')
283
284        # Test step 8
285        # Get interface association descriptor.
286        interface_association_descriptors = (
287                usb_descriptors.filter_descriptors(
288                        usb_descriptors.InterfaceAssociationDescriptor,
289                        descriptors))
290
291        if usb_descriptors.has_distinct_descriptors(
292                interface_association_descriptors):
293            mbim_errors.log_and_raise(
294                    mbim_errors.MBIMComplianceGenericAssertionError,
295                    'Expected 1 interface association descriptor, got %d.' % (
296                            len(interface_association_descriptors)))
297
298        for association_descriptor in interface_association_descriptors:
299            # Check interface association descriptor if one of the following
300            # condition is met:
301            # 1. bFirstInterface <= bControlInterface < (bFirstInterface +
302            #                                            bInterfaceCount)
303            # 2. bFirstInterface <= bSubordinateInterface0 < (
304            #            bFirstInterface + bInterfaceCount)
305            b_first_interface = association_descriptor.bFirstInterface
306            b_interface_count = association_descriptor.bInterfaceCount
307            b_control_interface = union_descriptor.bControlInterface
308            b_subordinate_interface_0 = (
309                    union_descriptor.bSubordinateInterface0)
310            check_inteface_association_descriptor = False
311
312            if ((b_first_interface <= b_control_interface < (
313                         b_first_interface + b_interface_count)) or
314                (b_first_interface <= b_subordinate_interface_0 < (
315                         b_first_interface + b_interface_count))):
316                check_interface_association_descriptor = True
317
318            if not check_interface_association_descriptor:
319                mbim_errors.log_and_raise(
320                        mbim_errors.MBIMComplianceAssertionError,
321                        'mbim1.0:6.1#1')
322
323            if check_interface_association_descriptor:
324                if not((b_first_interface == b_control_interface or
325                        b_first_interface == b_subordinate_interface_0) and
326                       (b_interface_count == 2) and
327                       (b_subordinate_interface_0 == b_control_interface + 1 or
328                        b_subordinate_interface_0 ==
329                        b_control_interface - 1) and
330                       (association_descriptor.bFunctionClass == 0x02) and
331                       (association_descriptor.bFunctionSubClass == 0x0E) and
332                       (association_descriptor.bFunctionProtocol == 0x00)):
333                    mbim_errors.log_and_raise(
334                            mbim_errors.MBIMComplianceAssertionError,
335                            'mbim1.0:6.1#2')
336
337    # End of run_internal().
338