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 request 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 15import math 16 17from autotest_lib.client.cros.cellular.mbim_compliance import mbim_constants 18from autotest_lib.client.cros.cellular.mbim_compliance import mbim_errors 19from autotest_lib.client.cros.cellular.mbim_compliance import mbim_message 20 21 22class MBIMControlMessageRequest(mbim_message.MBIMControlMessage): 23 """ MBIMMessage Request Message base class. """ 24 MESSAGE_TYPE = mbim_message.MESSAGE_TYPE_REQUEST 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', mbim_message.FIELD_TYPE_TRANSACTION_ID)) 28 29 30class MBIMOpen(MBIMControlMessageRequest): 31 """ The class for MBIM_OPEN_MSG. """ 32 33 _FIELDS = (('I', 'max_control_transfer', ''),) 34 _DEFAULTS = {'message_type': mbim_constants.MBIM_OPEN_MSG} 35 36 37class MBIMClose(MBIMControlMessageRequest): 38 """ The class for MBIM_CLOSE_MSG. """ 39 40 _DEFAULTS = {'message_type': mbim_constants.MBIM_CLOSE_MSG} 41 42 43class MBIMCommandSecondary(MBIMControlMessageRequest): 44 """ The class for MBIM_COMMAND_MSG. """ 45 46 _FIELDS = (('I', 'total_fragments', mbim_message.FIELD_TYPE_NUM_FRAGMENTS), 47 ('I', 'current_fragment', '')) 48 49 50class MBIMCommand(MBIMControlMessageRequest): 51 """ The class for MBIM_COMMAND_MSG. """ 52 53 _FIELDS = (('I', 'total_fragments', mbim_message.FIELD_TYPE_NUM_FRAGMENTS), 54 ('I', 'current_fragment', ''), 55 ('16s', 'device_service_id', mbim_message.FIELD_TYPE_PAYLOAD_ID), 56 ('I', 'cid', mbim_message.FIELD_TYPE_PAYLOAD_ID), 57 ('I', 'command_type', ''), 58 ('I', 'information_buffer_length', 59 mbim_message.FIELD_TYPE_PAYLOAD_LEN)) 60 _DEFAULTS = {'message_type': mbim_constants.MBIM_COMMAND_MSG, 61 'total_fragments': 0x00000001, 62 'current_fragment': 0x00000000, 63 'information_buffer_length': 0} 64 _SECONDARY_FRAGMENT = MBIMCommandSecondary 65 66 67class MBIMHostError(MBIMControlMessageRequest): 68 """ The class for MBIM_ERROR_MSG. """ 69 70 _FIELDS = (('I', 'error_status_code', ''),) 71 _DEFAULTS = {'message_type': mbim_constants.MBIM_HOST_ERROR_MSG} 72 73 74def fragment_request_packets(message, max_fragment_length): 75 """ 76 Fragments request messages into a multiple fragment packets if the total 77 message length is greater than the |max_fragment_length| specified by the 78 device. 79 80 It splits the payload_buffer fields into the primary and secondary 81 fragments. 82 83 @param message: Monolithic message object. 84 @param max_fragment_length: Max length of each fragment expected by device. 85 @returns List of fragmented packets. 86 87 """ 88 packets = [] 89 # We may need to go up the message heirarchy level before fragmenting. So, 90 # we need to recreate the primary fragment using the parent class. 91 primary_frag_class = message.__class__.find_primary_parent_fragment() 92 secondary_frag_class = primary_frag_class.get_secondary_fragment() 93 if not secondary_frag_class: 94 mbim_errors.log_and_raise( 95 mbim_errors.MBIMComplianceControlMessageError, 96 'No secondary fragment class defined') 97 # Let's recreate the primary frag object from the raw data of the 98 # initial message. 99 raw_data = message.create_raw_data() 100 message = primary_frag_class(raw_data=raw_data) 101 102 # Calculate the number of fragments we need. We divide the |payload_bufer| 103 # between 1 primary and |num_fragments| secondary fragments. 104 primary_struct_len = primary_frag_class.get_struct_len(get_all=True) 105 secondary_struct_len = secondary_frag_class.get_struct_len(get_all=True) 106 total_length = message.get_total_len() 107 total_payload_length = message.get_payload_len() 108 num_fragments = 1 109 remaining_payload_length = total_payload_length 110 remaining_payload_buffer = message.payload_buffer 111 112 primary_frag_length = max_fragment_length 113 primary_payload_length = primary_frag_length - primary_struct_len 114 remaining_payload_length -= primary_payload_length 115 num_fragments += int( 116 math.ceil(remaining_payload_length / 117 float(max_fragment_length - secondary_struct_len))) 118 119 # Truncate the payload of the primary message 120 primary_message = message.copy( 121 current_fragment=0, 122 total_fragments=num_fragments, 123 message_length=primary_frag_length) 124 primary_message.payload_buffer = ( 125 remaining_payload_buffer[:primary_payload_length]) 126 packet = primary_message.create_raw_data() 127 remaining_payload_buffer = ( 128 remaining_payload_buffer[primary_payload_length:]) 129 packets.append(packet) 130 131 # Field values for secondary fragments are taken from the primary fragment 132 # field values. 133 args_list = {name : getattr(primary_message, name) 134 for name in secondary_frag_class.get_field_names(get_all=True)} 135 del args_list['message_length'] 136 args_list['total_fragments'] = num_fragments 137 for fragment_num in range(1, num_fragments): 138 secondary_frag_length = min( 139 max_fragment_length, 140 remaining_payload_length + secondary_struct_len) 141 secondary_payload_length = secondary_frag_length - secondary_struct_len 142 remaining_payload_length -= secondary_payload_length 143 args_list['current_fragment'] = fragment_num 144 args_list['payload_buffer'] = ( 145 remaining_payload_buffer[:secondary_payload_length]) 146 secondary_message = secondary_frag_class(**args_list) 147 packet = secondary_message.create_raw_data() 148 remaining_payload_buffer = ( 149 remaining_payload_buffer[secondary_payload_length:]) 150 packets.append(packet) 151 logging.debug('Fragmented request-> Fragments: %d, Total len: %d, ' 152 'Max Frag length: %d', num_fragments, total_length, 153 max_fragment_length) 154 return packets 155 156 157def generate_request_packets(message, max_fragment_length): 158 """ 159 Generates raw data corresponding to the incoming message request object. 160 161 @param message: One of the defined MBIM request messages. 162 @param max_fragment_length: Max length of each fragment expected by device. 163 @returns Tuple of (packets, message), 164 packets: List of raw byte array packets. 165 166 """ 167 if message.MESSAGE_TYPE != mbim_message.MESSAGE_TYPE_REQUEST: 168 mbim_errors.log_and_raise( 169 mbim_errors.MBIMComplianceControlMessageError, 170 'Not a valid request message (%s)' % message.__name__) 171 message_class = message.__class__ 172 if message.message_length < max_fragment_length: 173 packet = message.create_raw_data() 174 packets = [packet] 175 else: 176 packets = fragment_request_packets(message, max_fragment_length) 177 logging.debug("Request Message generated: %s", message) 178 return packets 179