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
6
7import common
8from autotest_lib.client.bin import utils
9from autotest_lib.client.cros.cellular.mbim_compliance import mbim_channel
10from autotest_lib.client.cros.cellular.mbim_compliance \
11        import mbim_command_message
12from autotest_lib.client.cros.cellular.mbim_compliance import mbim_errors
13from autotest_lib.client.cros.cellular.mbim_compliance \
14        import mbim_message_request
15from autotest_lib.client.cros.cellular.mbim_compliance \
16        import mbim_message_response
17from autotest_lib.client.cros.cellular.mbim_compliance \
18        import mbim_test_base
19from autotest_lib.client.cros.cellular.mbim_compliance.sequences \
20        import get_descriptors_sequence
21from autotest_lib.client.cros.cellular.mbim_compliance.sequences \
22        import mbim_open_generic_sequence
23
24
25class cellular_MbimComplianceCM16(mbim_test_base.MbimTestBase):
26    """
27    CM_16 Validation of fragmented message transmission in case of multiple
28    fragmented messages.
29
30    This test verifies that fragmented messages sent from the function are not
31    intermixed. Note that this test is only applicable for devices that support
32    multiple outstanding commands.
33
34    Reference:
35        [1] Universal Serial Bus Communication Class MBIM Compliance Testing: 44
36        http://www.usb.org/developers/docs/devclass_docs/MBIM-Compliance-1.0.pdf
37    """
38    version = 1
39
40    def run_internal(self):
41        """ Run CM_16 test. """
42        # Precondition
43        desc_sequence = get_descriptors_sequence.GetDescriptorsSequence(
44                self.device_context)
45        descriptors = desc_sequence.run()
46        self.device_context.update_descriptor_cache(descriptors)
47        open_sequence = mbim_open_generic_sequence.MBIMOpenGenericSequence(
48                self.device_context)
49        open_sequence.run(max_control_transfer_size=64)
50
51        device_context = self.device_context
52        descriptor_cache = device_context.descriptor_cache
53        self.channel = mbim_channel.MBIMChannel(
54                device_context.device,
55                descriptor_cache.mbim_communication_interface.bInterfaceNumber,
56                descriptor_cache.interrupt_endpoint.bEndpointAddress,
57                device_context.max_control_transfer_size)
58
59        # Step 1
60        caps_command_message = mbim_command_message.MBIMDeviceCapsQuery()
61        caps_packets = mbim_message_request.generate_request_packets(
62                caps_command_message,
63                device_context.max_control_transfer_size)
64        self.caps_transaction_id = caps_command_message.transaction_id
65
66        # Step 2
67        services_command_message = (
68                mbim_command_message.MBIMDeviceServicesQuery())
69        services_packets = mbim_message_request.generate_request_packets(
70                services_command_message,
71                device_context.max_control_transfer_size)
72        self.services_transaction_id = services_command_message.transaction_id
73
74        # Transmit the messages now
75        self.channel.unidirectional_transaction(*caps_packets)
76        self.channel.unidirectional_transaction(*services_packets)
77
78        # Step 3
79        utils.poll_for_condition(
80                self._get_response_packets,
81                timeout=5,
82                exception=mbim_errors.MBIMComplianceChannelError(
83                        'Failed to retrieve the response packets to specific '
84                        'control messages.'))
85        self.channel.close()
86
87        caps_response_message = self.caps_response
88        services_response_message = self.services_response
89        is_caps_message_valid = isinstance(
90                caps_response_message,
91                mbim_command_message.MBIMDeviceCapsInfo)
92        is_services_message_valid = isinstance(
93                services_response_message,
94                mbim_command_message.MBIMDeviceServicesInfo)
95        if not ((is_caps_message_valid and is_services_message_valid) and
96                (caps_response_message.transaction_id ==
97                 caps_command_message.transaction_id) and
98                (caps_response_message.device_service_id ==
99                 caps_command_message.device_service_id) and
100                caps_response_message.cid == caps_command_message.cid and
101                (services_command_message.transaction_id ==
102                 services_response_message.transaction_id) and
103                (services_command_message.device_service_id ==
104                 services_response_message.device_service_id) and
105                services_command_message.cid == services_response_message.cid):
106            mbim_errors.log_and_raise(mbim_errors.MBIMComplianceAssertionError,
107                                      'mbim1.0:9.5#1')
108
109
110    def _get_response_packets(self):
111        """
112        Condition method for |poll_for_condition| to check the retrieval of
113        target packets.
114
115        @returns True if both caps response packet and services response packet
116                are received, False otherwise.
117
118        """
119        try:
120            packets = self.channel.get_outstanding_packets()
121        except mbim_errors.MBIMComplianceChannelError:
122            logging.debug("Error in receiving response fragments from the device")
123            mbim_errors.log_and_raise(mbim_errors.MBIMComplianceAssertionError,
124                                      'mbim1.0:9.5#1')
125        self.caps_response = None
126        self.services_response = None
127        for packet in packets:
128            try:
129                message_response = mbim_message_response.parse_response_packets(
130                        packet)
131            except mbim_errors.MBIMComplianceControlMessageError:
132                logging.debug("Error in parsing response fragments from the device")
133                mbim_errors.log_and_raise(
134                        mbim_errors.MBIMComplianceAssertionError,
135                        'mbim1.0:9.5#1')
136            if message_response.transaction_id == self.caps_transaction_id:
137                self.caps_response = message_response
138            elif (message_response.transaction_id ==
139                  self.services_transaction_id):
140                self.services_response = message_response
141            if self.caps_response and self.services_response:
142                return True
143        return False
144