Home | History | Annotate | Download | only in mbim_compliance
      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 """
      5 All of the MBIM request message type definitions are in this file. These
      6 definitions inherit from MBIMControlMessage.
      7 
      8 Reference:
      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 """
     14 import logging
     15 import math
     16 
     17 from autotest_lib.client.cros.cellular.mbim_compliance import mbim_constants
     18 from autotest_lib.client.cros.cellular.mbim_compliance import mbim_errors
     19 from autotest_lib.client.cros.cellular.mbim_compliance import mbim_message
     20 
     21 
     22 class 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 
     30 class 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 
     37 class MBIMClose(MBIMControlMessageRequest):
     38     """ The class for MBIM_CLOSE_MSG. """
     39 
     40     _DEFAULTS = {'message_type': mbim_constants.MBIM_CLOSE_MSG}
     41 
     42 
     43 class 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 
     50 class 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 
     67 class 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 
     74 def 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 
    157 def 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