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""" 5All of the MBIM response message type definitions are in this file. These 6definitions inherit from MBIMControlMessage. 7 8Reference: 9 [1] Universal Serial Bus Communications Class Subclass Specification for 10 Mobile Broadband Interface Model 11 http://www.usb.org/developers/docs/devclass_docs/ 12 MBIM10Errata1_073013.zip 13""" 14import logging 15 16from autotest_lib.client.cros.cellular.mbim_compliance import mbim_constants 17from autotest_lib.client.cros.cellular.mbim_compliance import mbim_errors 18from autotest_lib.client.cros.cellular.mbim_compliance import mbim_message 19 20 21class MBIMControlMessageResponse(mbim_message.MBIMControlMessage): 22 """ MBIMMessage Response Message base class. """ 23 24 MESSAGE_TYPE = mbim_message.MESSAGE_TYPE_RESPONSE 25 _FIELDS = (('I', 'message_type', mbim_message.FIELD_TYPE_PAYLOAD_ID), 26 ('I', 'message_length', mbim_message.FIELD_TYPE_TOTAL_LEN), 27 ('I', 'transaction_id', '')) 28 29 30class MBIMOpenDone(MBIMControlMessageResponse): 31 """ The class for MBIM_OPEN_DONE. """ 32 33 _FIELDS = (('I', 'status_codes', ''),) 34 _IDENTIFIERS = {'message_type': mbim_constants.MBIM_OPEN_DONE} 35 36 37class MBIMCloseDone(MBIMControlMessageResponse): 38 """ The class for MBIM_CLOSE_DONE. """ 39 40 _FIELDS = (('I', 'status_codes', ''),) 41 _IDENTIFIERS = {'message_type': mbim_constants.MBIM_CLOSE_DONE} 42 43 44class MBIMCommandDoneSecondary(MBIMControlMessageResponse): 45 """ The class for MBIM_COMMAND_DONE. """ 46 47 _FIELDS = (('I', 'total_fragments', mbim_message.FIELD_TYPE_NUM_FRAGMENTS), 48 ('I', 'current_fragment', '')) 49 50 51class MBIMCommandDone(MBIMControlMessageResponse): 52 """ The class for MBIM_COMMAND_DONE. """ 53 54 _FIELDS = (('I', 'total_fragments', mbim_message.FIELD_TYPE_NUM_FRAGMENTS), 55 ('I', 'current_fragment', ''), 56 ('16s', 'device_service_id', mbim_message.FIELD_TYPE_PAYLOAD_ID), 57 ('I', 'cid', mbim_message.FIELD_TYPE_PAYLOAD_ID), 58 ('I', 'status_codes', ''), 59 ('I', 'information_buffer_length', 60 mbim_message.FIELD_TYPE_PAYLOAD_LEN)) 61 _IDENTIFIERS = {'message_type': mbim_constants.MBIM_COMMAND_DONE} 62 _SECONDARY_FRAGMENT = MBIMCommandDoneSecondary 63 64 65class MBIMIndicateStatusSecondary(MBIMControlMessageResponse): 66 """ The class for MBIM_INDICATE_STATUS_MSG. """ 67 68 _FIELDS = (('I', 'total_fragments', mbim_message.FIELD_TYPE_NUM_FRAGMENTS), 69 ('I', 'current_fragment', '')) 70 71 72class MBIMIndicateStatus(MBIMControlMessageResponse): 73 """ The class for MBIM_INDICATE_STATUS_MSG. """ 74 75 _FIELDS = (('I', 'total_fragments', mbim_message.FIELD_TYPE_NUM_FRAGMENTS), 76 ('I', 'current_fragment', ''), 77 ('16s', 'device_service_id', mbim_message.FIELD_TYPE_PAYLOAD_ID), 78 ('I', 'cid', mbim_message.FIELD_TYPE_PAYLOAD_ID), 79 ('I', 'information_buffer_length', 80 mbim_message.FIELD_TYPE_PAYLOAD_LEN)) 81 _IDENTIFIERS = {'message_type': mbim_constants.MBIM_INDICATE_STATUS_MSG} 82 _SECONDARY_FRAGMENT = MBIMIndicateStatusSecondary 83 84 85class MBIMFunctionError(MBIMControlMessageResponse): 86 """ The class for MBIM_FUNCTION_ERROR_MSG. """ 87 88 _FIELDS = (('I', 'error_status_code', ''),) 89 _IDENTIFIERS = {'message_type': mbim_constants.MBIM_FUNCTION_ERROR_MSG} 90 91 92def reassemble_response_packets(primary_fragment, secondary_packets): 93 """ 94 Reassembles fragmented response messages into a single object. 95 96 It parses all the secondary fragments as |secondary_frag_class| and 97 merges all the payload_buffer fields into the primary fragment. 98 99 @param primary_fragment: Primary fragment message object. 100 @param secondary_packets: Array of the raw byte array response received 101 from device. 102 @returns Reassembled Response Message object. 103 104 """ 105 secondary_frag_class = primary_fragment.get_secondary_fragment() 106 # Check if we can reassemble at this tree level or not. If there is 107 # no associated _SECONDARY_FRAG_CLASS, we need to go down the tree further 108 # to reassemble. 109 if not secondary_frag_class: 110 return None 111 112 for packet in secondary_packets: 113 secondary_fragment = secondary_frag_class(raw_data=packet) 114 primary_fragment.payload_buffer.extend( 115 secondary_fragment.payload_buffer) 116 117 payload_len = primary_fragment.get_payload_len() 118 num_fragments = primary_fragment.get_num_fragments() 119 if ((num_fragments != len(secondary_packets) + 1) or 120 (payload_len != len(primary_fragment.payload_buffer))): 121 mbim_errors.log_and_raise(mbim_errors.MBIMComplianceAssertionError, 122 'mbim1.0:9.2') 123 total_length = primary_fragment.calculate_total_len() 124 primary_fragment = primary_fragment.copy(message_length=total_length) 125 logging.debug('Reassembled response-> Fragments: %d, Payload length: %d', 126 num_fragments, payload_len) 127 return primary_fragment 128 129 130def parse_response_packets(packets): 131 """ 132 Parses the incoming raw data |packets| into corresponding message response 133 object. 134 135 The function starts the at the root of the message hierarchy tree 136 and then goes down the root to find the exact leaf node message class. If 137 there are multiple frgaments expected at any level, it will reassemble the 138 secondary fragments before proceeding. 139 140 @param packets: Array of the raw byte array response received from device. 141 @returns Response Message object. 142 143 """ 144 # Start with the root class for all responses and then go down the tree. 145 message_class = MBIMControlMessageResponse 146 parse_packets = packets 147 148 while message_class is not None: 149 first_packet = parse_packets[0] 150 message = message_class(raw_data=first_packet) 151 # If there are secondary fragments expected at this level, 152 # let's reassemble the payload together before traversing down the 153 # message heirarchy. 154 if len(parse_packets) > 1: 155 reassembled_message = reassemble_response_packets(message, 156 parse_packets[1:]) 157 if reassembled_message is not None: 158 message = reassembled_message 159 reassembled_packet = message.create_raw_data() 160 parse_packets = [reassembled_packet] 161 message_class = message.find_payload_class() 162 logging.debug("Response Message parsed: %s", message) 163 return message 164