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
5import logging
6import traceback
7
8import common
9from autotest_lib.client.common_lib import error
10
11
12class MBIMComplianceError(error.TestFail):
13    """ Base class for all errors overtly raised in the suite. """
14    pass
15
16
17class MBIMComplianceFrameworkError(MBIMComplianceError):
18    """
19    Errors raised by any of the framework code.
20
21    These errors are raised by code that is not part of a test / sequence /
22    assertion.
23
24    """
25    pass
26
27
28class MBIMComplianceChannelError(MBIMComplianceError):
29    """ Errors raised in the MBIM communication channel. """
30    pass
31
32
33class MBIMComplianceControlMessageError(MBIMComplianceError):
34    """ Errors raised in the MBIM control module. """
35    pass
36
37
38class MBIMComplianceDataTransferError(MBIMComplianceError):
39    """ Errors raised in the MBIM data transfer module. """
40    pass
41
42
43class MBIMComplianceNtbError(MBIMComplianceError):
44    """ Errors raised in the MBIM NTB module. """
45    pass
46
47
48class MBIMComplianceTestError(MBIMComplianceError):
49    """ Errors raised by compliance suite tests. """
50    pass
51
52
53class MBIMComplianceSequenceError(MBIMComplianceError):
54    """ Errors raised by compliance suite sequences. """
55    pass
56
57
58class MBIMComplianceAssertionError(MBIMComplianceError):
59    """ Errors raised by compliance suite assertions. """
60
61    MBIM_ASSERTIONS = {
62            # This key should not be used directly.
63            # Raise |MBIMComplianceGenericAssertionError| instead.
64            'no_code': '',
65
66            # Assertion group: 3.x.x#x
67            'mbim1.0:3.2.1#1': 'Functions that implement both NCM 1.0 and MBIM '
68                               'shall provide two alternate settings for the '
69                               'Communication Interface.',
70            'mbim1.0:3.2.1#2': 'For alternate setting 0 of the Communication '
71                               'Interface of an NCM/MBIM function: interface, '
72                               'functional and endpoint descriptors shall be '
73                               'constructed according to the rules given in '
74                               '[USBNCM10].',
75            'mbim1.0:3.2.1#3': 'For alternate setting 1 of the Communication '
76                               'Interface of an NCM/MBIM function: interface, '
77                               'functional and endpoint descriptors shall be '
78                               'constructed according to the rules given in '
79                               '[MBIM 1.0] section 6.',
80            'mbim1.0:3.2.1#4': 'When alternate setting 0 of the Communiation'
81                               'Interface of an NCM/MBIM function is selected, '
82                               'the function shall operator according to the '
83                               'NCM rules given in [USBNCM10].',
84            'mbim1.0:3.2.1#5': 'When alternate setting 1 of the Communiation'
85                               'Interface of an NCM/MBIM function is selected, '
86                               'the function shall operator according to the '
87                               'MBIM rules given in [MBIM1.0].',
88            'mbim1.0:3.2.2.1#1': 'If an Interface Association Descriptor is '
89                                 'used to form an NCM/MBIM function, its '
90                                 'interface class, subclass, and protocol '
91                                 'codes shall match those given in alternate '
92                                 'setting 0 of the Communication Interface. ',
93            'mbim1.0:3.2.2.4#1': 'Functions that implement both NCM 1.0 and '
94                                 'MBIM (an "NCM/MBIM function") shall provide '
95                                 'three alternate settings for the Data '
96                                 'Interface.',
97            'mbim1.0:3.2.2.4#2': 'For an NCM/MBIM function, the Data Interface '
98                                 'descriptors for alternate settings 0 and 1 '
99                                 'must have bInterfaceSubClass == 00h, and '
100                                 'bInterfaceProtocol == 01h.',
101            'mbim1.0:3.2.2.4#3': 'For an NCM/MBIM function, the Data Interface '
102                                 'descriptor for alternate setting 2 must have '
103                                 'bInterfaceSubClass == 00h, and '
104                                 'bInterfaceProtocol == 02h.',
105            'mbim1.0:3.2.2.4#4': 'For an NCM/MBIM function there must be no '
106                                 'endpoints for alternate setting 0 of the '
107                                 'Data Interface. For each of the other two '
108                                 'alternate settings (1 and 2) there must be '
109                                 'exactly two endpoints: one Bulk IN and one '
110                                 'Bulk OUT.',
111
112            # Assertion group: 6.x#x
113            'mbim1.0:6.1#1': 'If an Interface Association Descriptor (IAD) is '
114                             'provided for the MBIM function, the IAD and the '
115                             'mandatory CDC Union Functional Descriptor '
116                             'specified for the MBIM function shall group '
117                             'together the same interfaces.',
118            'mbim1.0:6.1#2': 'If an Interface Association Descriptor (IAD) is '
119                             'provided for the MBIM only function, its '
120                             'interface class, subclass, and protocol codes '
121                             'shall match those given in the Communication '
122                             'Interface descriptor.',
123            'mbim1.0:6.3#1': 'The descriptor for alternate setting 0 of the '
124                             'Communication Interface of an MBIM only function '
125                             'shall have bInterfaceClass == 02h, '
126                             'bInterfaceSubClass == 0Eh, and '
127                             'bInterfaceProtocol == 00h.',
128            'mbim1.0:6.3#2': 'MBIM Communication Interface description shall '
129                             'include the following functional descriptors: '
130                             'CDC Header Functional Descriptor, CDC Union '
131                             'Functional Descriptor, and MBIM Functional '
132                             'Descriptor. Refer to Table 6.2 of [USBMBIM10].',
133            'mbim1.0:6.3#3': 'CDC Header Functional Descriptor shall appear '
134                             'before CDC Union Functional Descriptor and '
135                             'before MBIM Functional Descriptor.',
136            'mbim1.0:6.3#4': 'CDC Union Functional Descriptor for an MBIM '
137                             'function shall group together the MBIM '
138                             'Communication Interface and the MBIM Data '
139                             'Interface.',
140            'mbim1.0:6.3#5': 'The class-specific descriptors must be followed '
141                             'by an Interrupt IN endpoint descriptor.',
142            'mbim1.0:6.4#1': 'Field wMaxControlMessage of MBIM Functional '
143                             'Descriptor must not be smaller than 64.',
144            'mbim1.0:6.4#2': 'Field bNumberFilters of MBIM Functional '
145                             'Descriptor must not be smaller than 16.',
146            'mbim1.0:6.4#3': 'Field bMaxFilterSize of MBIM Functional '
147                             'Descriptor must not exceed 192.',
148            'mbim1.0:6.4#4': 'Field wMaxSegmentSize of MBIM Functional '
149                             'Descriptor must not be smaller than 2048.',
150            'mbim1.0:6.4#5': 'Field bFunctionLength of MBIM Functional '
151                             'Descriptor must be 12 representing the size of '
152                             'the descriptor.',
153            'mbim1.0:6.4#6': 'Field bcdMBIMVersion of MBIM Functional '
154                             'Descriptor must be 0x0100 in little endian '
155                             'format.',
156            'mbim1.0:6.4#7': 'Field bmNetworkCapabilities of MBIM Functional '
157                             'Descriptor should have the following bits set to '
158                             'zero: D0, D1, D2, D4, D6 and D7.',
159            'mbim1.0:6.5#1': 'If MBIM Extended Functional Descriptor is '
160                             'provided, it must appear after MBIM Functional '
161                             'Descriptor.',
162            'mbim1.0:6.5#2': 'Field bFunctionLength of MBIM Extended '
163                             'Functional Descriptor must be 8 representing the '
164                             'size of the descriptor.',
165            'mbim1.0:6.5#3': 'Field bcdMBIMEFDVersion of MBIM Extended '
166                             'Functional Descriptor must be 0x0100 in little '
167                             'endian format.',
168            'mbim1.0:6.5#4': 'Field bMaxOutstandingCommandMessages of MBIM '
169                             'Extended Functional Descriptor shall be greater '
170                             'than 0.',
171            'mbim1.0:6.6#1': 'The Data Interface for an MBIM only function '
172                             'shall provide two alternate settings.',
173            'mbim1.0:6.6#2': 'The first alternate setting for the Data '
174                             'Interface of an MBIM only function (the default '
175                             'interface setting, alternate setting 0) shall '
176                             'include no endpoints.',
177            'mbim1.0:6.6#3': 'The second alternate setting for the Data '
178                             'Interface of an MBIM only function (alternate '
179                             'setting 1) is used for normal operation, and '
180                             'shall include one Bulk IN endpoint and one Bulk '
181                             'OUT endpoint.',
182            'mbim1.0:6.6#4': 'For an MBIM only function the Data Interface '
183                             'descriptors for alternate settings 0 and 1 must '
184                             'have bInterfaceSubClass == 00h, and '
185                             'bInterfaceProtocol == 02h. Refer to Table 6.4 of '
186                             '[USBMBIM10].',
187
188            # Assertion Groups: 7.x.x#x
189            'mbim1.0:7#1':   'To distinguish among the data streams, the last '
190                             'character of the dwSignature in the NDP16 header '
191                             'shall be coded with the index SessionId specified'
192                             ' by the host in the MBIM_CID_CONNECT. The first '
193                             'three symbols are encoded as ASCII characters in '
194                             'little-endian form plus a last byte in HEX '
195                             '(binary) format: "IPS"<SessionId>.',
196            'mbim1.0:7#3':   'To distinguish among the data streams, the last '
197                             'character of the dwSignature in the NDP32 header '
198                             'shall be coded with the index SessionId specified'
199                             ' by the host in the MBIM_CID_CONNECT. The first '
200                             'three symbols are encoded as ASCII characters in '
201                             'little-endian form plus a last byte in HEX '
202                             '(binary) format: "ips"<SessionId>.',
203
204            # Assertion Groups: 8.x.x#x
205            'mbim1.0:8.1.2#2': 'The function must use a separate '
206                               'GET_ENCAPSULATED_RESPONSE transfer for each '
207                               'control message it has to send to the host.',
208            'mbim1.0:8.1.2#3': 'The function must send a RESPONSE_AVAILABLE '
209                               'notification for each available fragment of '
210                               'ENCAPSULATED_RESPONSE to be read from the '
211                               'default pipe.',
212
213            # Assertion Groups: 9.x#x, 9.x.x and 9.x.x#x
214            'mbim1.0:9.1#1':   'For notifications, the TransactionId must be '
215                               'set to 0 by the function.',
216            'mbim1.0:9.1#2':   'MessageLength in MBIM_MESSAGE_HEADER must be >='
217                               ' 0x0C.',
218            'mbim1.0:9.2':     'Function should fragment responses based on '
219                               'MaxControlTransfer value from MBIM_OPEN_MSG.',
220            'mbim1.0:9.3.1#1': 'In case MBIM_OPEN_MSG message is sent to a '
221                               'function that is already opened, the function '
222                               'shall interpret this as that the host and the '
223                               'function are out of synchronization. The '
224                               'function shall then perform the actions '
225                               'dictated by the MBIM_CLOSE_MSG before it '
226                               'performs the actions dictated by this '
227                               'command.The function shall not send the '
228                               'MBIM_CLOSE_DONE when the transition to the '
229                               'Closed state has been completed. Only the '
230                               ' MBIM_OPEN_DONE message is sent upon '
231                               'successful completion of this message.',
232            'mbim1.0:9.3.2#1': 'Between the host\'s sending MBIM_CLOSE_MSG '
233                               'message and the function\'s completing the '
234                               'request (acknowledged with MBIM_CLOSE_DONE), '
235                               'the function shall ignore any MBIM control '
236                               'messages it receives on the control plane or '
237                               'the data on the bulk pipes.',
238            'mbim1.0:9.3.2#2': 'The function shall not send any MBIM control '
239                               'messages on the control plane or data on the '
240                               'bulk pipes after completing '
241                               'MBIM_CLOSE_MSG message (acknowledging it with '
242                               'the MBIM_CLOSE_DONE message) with one '
243                               'exception and that is MBIM_ERROR_NOT_OPENED.',
244            'mbim1.0:9.3.2#3': 'On MBIM_CLOSE_MSG, any active context between '
245                               'the function and the host shall be terminated ',
246            'mbim1.0:9.3.4#2': 'An MBIM_FUNCTION_ERROR_MSG shall not make use '
247                               'of a DataBuffer, so it cannot send any data '
248                               'payload.',
249            'mbim1.0:9.3.4#3': 'MBIM_ERROR_FRAGMENT_OUT_OF_SEQUENCE shall be '
250                               'sent by the function if it detects a fragmented'
251                               ' message out of sequence.',
252            'mbim1.0:9.3.4.2#2': 'For MBIM_ERROR_FRAGMENT_OUT_OF_SEQUENCE, the'
253                               ' TransactionId of the responding message must '
254                               'match the TransactionId in the faulty '
255                               'fragmented sequence.',
256            'mbim1.0:9.3.4.2#3': 'In case of an out of a sequence error, the '
257                               'function shall discard all the packets with '
258                               'the same TransactionId as the faulty message '
259                               'sequence.',
260            'mbim1.0:9.3.4.2#4': 'If the function gets one more message that '
261                               'is out of order for the same TransactionId, it '
262                               'shall send a new error message with the same '
263                               'TransactionId once more.',
264            'mbim1.0:9.4.1#1': 'The function shall respond to the '
265                               'MBIM_OPEN_MSG message with an MBIM_OPEN_DONE '
266                               'message in which the TransactionId must match '
267                               'the TransactionId in the MBIM_OPEN_MSG.',
268            'mbim1.0:9.4.1#2': 'The Status field of MBIM_OPEN_DONE shall be '
269                               'set to MBIM_STATUS_SUCCESS if the function '
270                               'initialized successfully.',
271            'mbim1.0:9.4.2#1': 'The function shall respond to the '
272                               'MBIM_CLOSE_MSG message with an '
273                               'MBIM_CLOSE_DONE message in which the '
274                               'TransactionId must match the TransactionId in '
275                               'the MBIM_CLOSE_MSG.',
276            'mbim1.0:9.4.2#2': 'The Status field of MBIM_CLOSE_DONE shall '
277                               'always be set to MBIM_STATUS_SUCCESS.',
278            'mbim1.0:9.4.3': 'The function shall respond to '
279                             'the MBIM_COMMAND_MSG message with an '
280                             'MBIM_COMMAND_DONE message in which the '
281                             'TransactionId must match the TransactionId in '
282                             'the MBIM_COMMAND_MSG.',
283            'mbim1.0:9.4.5#1': 'If the CID is successful, the function shall '
284                               'set the Status field to MBIM_STATUS_SUCCESS '
285                               'in the MBIM_COMMAND_DONE.',
286            'mbim1.0:9.4.5#2': 'If the function does not implement the CID, '
287                               'then the function shall fail the request with '
288                               'MBIM_STATUS_NO_DEVICE_SUPPORT.',
289            'mbim1.0:9.4.5#3': 'If the Status field returned to the host is '
290                               'not equal to MBIM_STATUS_SUCCESS, the function '
291                               'must set the Information BufferLength to 0, '
292                               'indicating an empty InformationBuffer except '
293                               'the following CIDs: MBIM_CID_REGISTER_STATE, '
294                               'MBIM_CID_PACKET_SERVICE, MBIM_CID_CONNECT, '
295                               'MBIM_CID_SERVICE_ACTIVATION.',
296            'mbim1.0:9.5#1':   'Function should transmit fragmented message to '
297                               'host without intermixing fragments from other '
298                               'messages.',
299            'mbim1.0:10.3#2':  'The function shall reject incoming messages '
300                               'that dont follow the rules for variable-length'
301                               ' encoding by setting '
302                               'MBIM_STATUS_INVALID_PARAMETERS as the status '
303                               'code in the MBIM_COMMAND_DONE message.',
304            'mbim1.0:10.5.1.3#1': 'Functions that support CDMA must specify '
305                               'MBIMCtrlCapsCdmaMobileIP or '
306                               'MBIMCtrlCapsCdmaSimpleIP or both flags to '
307                               'inform the host about the type of IP that the '
308                               'function supports.',
309
310            # NCM Assertion group: 3.x.x#x
311            'ncm1.0:3.2.1#1':  'The first four bytes in NTH16 shall be '
312                               '0x484D434E in little-endian format ("NCMH").',
313            'ncm1.0:3.2.1#2':  'wHeaderLength value in NTH16 shall be 0x000C.',
314            'ncm1.0:3.2.1#3':  'wSequence in NTH16 shall be set to zero by the '
315                               'function in the first NTB transferred after '
316                               'every "function reset" event.',
317            'ncm1.0:3.2.1#4':  'wSequence value in NTH16 shall be incremented '
318                               'for every NTB subsequent transfer.',
319            'ncm1.0:3.2.1#5':  'NTB size (IN) shall not exceed dwNtbInMaxSize.',
320            'ncm1.0:3.2.1#6':  'wNdpIndex value in NTH16 must be a multiple of '
321                               '4, and must be >= 0x000C, in little endian.',
322            'ncm1.0:3.2.2#1':  'The first four bytes in NTH32 shall be '
323                               '0x686D636E in little-endian format ("ncmh").',
324            'ncm1.0:3.2.2#2':  'wHeaderLength value in NTH32 shall be 0x0010.',
325            'ncm1.0:3.2.2#3':  'wSequence in NTH32 shall be set to zero by the '
326                               'function in the first NTB transferred after '
327                               'every "function reset" event.',
328            'ncm1.0:3.2.2#4':  'wSequence value in NTH32 shall be incremented '
329                               'for every NTB subsequent transfer.',
330            'ncm1.0:3.2.2#5':  'NTB size (IN) shall not exceed dwNtbInMaxSize.',
331            'ncm1.0:3.2.2#6':  'dwNdpIndex value in NTH32 must be a multiple of'
332                               ' 4, and must be >= 0x0010, in little endian.',
333            'ncm1.0:3.3.1#1':  'wLength value in the NDP16 must be a multiple '
334                               'of 4, and must be at least 16d (0x0010).',
335            'ncm1.0:3.3.1#2':  'wDatagramIndex[0] value in NDP16 must be >= '
336                               '0x000C (because it must point past the NTH16).',
337            'ncm1.0:3.3.1#3':  'wDatagramLength[0] value in NDP16 must be >= '
338                               '20d if datagram payload is IPv4 and >= 40d if '
339                               'datagram payload is IPv6.',
340            'ncm1.0:3.3.1#4':  'wDatagramIndex[(wLength-8)/4 - 1] value in '
341                               'NDP16 must be zero.',
342            'ncm1.0:3.3.1#5':  'wDatagramLength[(wLength-8)/4 - 1] value in '
343                               'NDP16 must be zero.',
344            'ncm1.0:3.3.2#1':  'wLength value in the NDP32 must be a multiple '
345                               'of 8, and must be at least 16d (0x0020).',
346            'ncm1.0:3.3.2#2':  'dwDatagramIndex[0] value in NDP32 must be >= '
347                               '0x0010 (because it must point past the NTH32).',
348            'ncm1.0:3.3.2#3':  'dwDatagramLength[0] value in NDP32 must be >= '
349                               '20d if datagram payload is IPv4 and >= 40d if '
350                               'datagram payload is IPv6.',
351            'ncm1.0:3.3.2#4':  'dwDatagramIndex[(wLength-8)/4 - 1] value in '
352                               'NDP32 must be zero.',
353            'ncm1.0:3.3.2#5':  'dwDatagramLength[(wLength-8)/4 - 1] value in '
354                               'NDP32 must be zero.',
355    }
356
357    def __init__(self, assertion_id, error_string=None):
358        """
359        @param assertion_id: A str that must be a key in the MBIM_ASSERTIONS map
360                defined in this class.
361        @param error_string: An optional str to be appended to the error
362                description.
363
364        For example,
365            MBIMComplianceAssertionError('mbim1.0:3.2.1#1')
366            raises an error associated with assertion [MBIM 1.0]-3.2.1#1
367
368        """
369        if assertion_id not in self.MBIM_ASSERTIONS:
370            log_and_raise(MBIMComplianceFrameworkError,
371                          'Unknown assertion id "%s"' % assertion_id)
372
373        message = '[%s]: %s' % (assertion_id,
374                                self.MBIM_ASSERTIONS[assertion_id])
375        if error_string:
376            message += ': %s' % error_string
377
378        super(MBIMComplianceAssertionError, self).__init__(message)
379
380
381class MBIMComplianceGenericAssertionError(MBIMComplianceAssertionError):
382    """ Assertion errors that don't map directly to an MBIM assertion. """
383    def __init__(self, error_string):
384        """
385        @param error_string: A description of the error.
386        """
387        super(MBIMComplianceGenericAssertionError, self).__init__(
388                'no_code',
389                error_string)
390
391
392def log_and_raise(error_class, *args):
393    """
394    Log and raise an error.
395
396    This function should be used to raise all errors.
397
398    @param error_class: An Exception subclass to raise.
399    @param *args: Arguments to be passed to the error class constructor.
400    @raises: |error_class|.
401
402    """
403    error_object = error_class(*args)
404    logging.error(error_object)
405    trace = traceback.format_stack()
406    # Get rid of the current frame from trace
407    trace = trace[:len(trace)-1]
408    logging.error('Traceback:\n' + ''.join(trace))
409    raise error_object
410