Changes between Version 15 and Version 16 of 802.11/wlan_exp/Extending


Ignore:
Timestamp:
Dec 16, 2015, 10:51:40 PM (8 years ago)
Author:
welsh
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • 802.11/wlan_exp/Extending

    v15 v16  
    99The 802.11 Reference Design Experiments Framework provides a variety of commands to control and observe WARP v3 nodes in real time. However many experiments will require additional commands, especially when users extend the reference code with new behaviors.
    1010
    11 Starting in 802.11 Reference Design v1.4 we have greatly simplified the process of implementing extensions to the experiments framework. Adding wlan_exp commands was more complicated in previous versions; refer to the archived [wiki:../HowToAddCommand HowToAddCommand] page for details on adding commands to versions before v1.4.
     11Starting in 802.11 Reference Design v1.4, we have greatly simplified the process of implementing extensions to the experiments framework. Adding wlan_exp commands was more complicated in previous versions; refer to the archived [wiki:../HowToAddCommand HowToAddCommand] page for details on adding commands to versions before v1.4.
    1212
    1313There are two kinds of framework extensions:
     
    2424 * Command arguments: an optional list of {{{u32}}} arguments supplied by the Python code that is passed to the C code handler
    2525
    26 The command ID is used by the C code to select which code block should process the command payload. The Python and C code must use matching lists of command IDs. All user command IDs must be 24-bit integers (i.e. in [0, 2^24^ -1]). The framework does not reserve any command ID values in this range.
     26The command ID is used by the C code to select which code block should process the command payload. The Python and C code must use matching lists of command IDs. All user command IDs must be 24-bit integers (i.e. in [0, 2^24^ -1]). The framework does not reserve any command ID values in this range for user commands.
    2727
    2828The command arguments are an optional payload supplied by the Python script. The wlan_exp framework sends this payload to the node with no modifications. The arguments must be constructed as a list of unsigned 32-bit integers ({{{u32}}} values). The Python and C code must implement complementary argument construction/parsing functions. The argument list may be omitted if a command requires no payload.
     
    3333
    3434{{{#!python
    35 #No command arguments, no response payload
     35# No command arguments, no response payload
    3636my_node.send_user_command(CMDID_USER_MYCMD, args=None)
    3737
    38 #3 arguments, no response payload
     38# 3 arguments, no response payload
    3939my_node.send_user_command(CMDID_USER_MYCMD, args=[ARG0 ARG1 ARG2])
    4040
    41 #2 arguments, with response payload
     41# 2 arguments, with response payload
    4242cmd_resp = my_node.send_user_command(CMDID_USER_MYCMD, args=[ARG0 ARG1])
    4343}}}
     
    4949The 802.11 MAC code running on the WARP node handles user commands at two levels.
    5050
    51  * '''Framework''': if the command behavior is common to all MAC applications, the command is handled in the {{{process_user_cmd()}}} function in {{{wlan_exp_user.c}}}. You should modify {{{process_user_cmd()}}} to handle any new framework-level commands required for your application. Any command IDs not handled in {{{process_user_cmd()}}} will be passed through to the MAC application.
    52 
    53  * '''MAC Application''': if a command behavior depends on the upper-level MAC application, the command is handled in the {{{wlan_exp_process_user_cmd()}}} callback function in the top-level MAC. You should modify the {{{wlan_exp_process_user_cmd()}}} function in the {{{wlan_mac_ap.c}}}, {{{wlan_mac_sta.c}}} and {{{wlan_mac_ibss.c}}} files to implement any MAC-specific command behaviors.
     51 * '''Framework''': If the command behavior is common to all MAC applications, the command is handled in the {{{process_user_cmd()}}} function in {{{wlan_exp_user.c}}}. You should modify {{{process_user_cmd()}}} to handle any new framework-level commands required for your application. Any command IDs not handled in {{{process_user_cmd()}}} will be passed through to the MAC application.
     52
     53 * '''MAC Application''': If a command behavior depends on the upper-level MAC application, the command is handled in the {{{wlan_exp_process_user_cmd()}}} callback function in the top-level MAC. You should modify the {{{wlan_exp_process_user_cmd()}}} function in the {{{wlan_mac_ap.c}}}, {{{wlan_mac_sta.c}}} and {{{wlan_mac_ibss.c}}} files to implement any MAC-specific command behaviors.
    5454
    5555Command handlers in these functions share a common structure:
    5656 * Examine the command ID and determine which code block ({{{case}}}) should handle the command
    57  * Implement the required command behavior
    58  * If a response payload is required, populate the response values into the {{{response->args}}} array
     57 * Implement the required command behavior using any supplied arguments in the {{{cmd_args_32}}} array
     58 * If a response payload is required, populate the response values into the {{{resp_args_32}}} array
    5959 * Update the response header fields for response length ({{{resp_hdr->length}}}) and number of response arguments ({{{resp_hdr->num_args}}})
    6060
    61 The wlan_exp framework will encapsulate the command response payload in an Ethernet fame and return it to the calling Python script for processing.
     61The wlan_exp framework will encapsulate the command response payload in an Ethernet frame and return it to the calling Python script for processing.  One thing to note is that a command response payload is limited to {{{max_resp_len}}} values.
    6262
    6363'''Interrupt Safety'''[[BR]]
     
    7171interrupt_state_t curr_interrupt_state;
    7272
    73 //Record the current interrupt enable state, then disable all interrupts
     73// Record the current interrupt enable state, then disable all interrupts
    7474curr_interrupt_state = wlan_mac_high_interrupt_stop();
    7575
    7676/*
    77 * Do any interrupt-sensitive processing here
    78 */
    79 
    80 //Restore the interrupt enable state
     77 * Do any interrupt-sensitive processing here
     78 */
     79
     80// Restore the interrupt enable state
    8181wlan_mac_high_interrupt_restore_state(curr_interrupt_state);
    8282}}}
    8383
    84 
    85 
    86 
    87 '''TODO:'''
    88  * Endianness
     84'''Endianness'''[[BR]]
     85
     86Both the C code on the node and the Python code on the host operate on little-endian data.  However, the wlan_exp transport requires command and response arguments to be big-endian for transfer within the Ethernet frame.  Xilinx provides two functions to assist in converting data between big-endian and little-endian: 
     87 * {{{Xil_Ntohl()}}} - "Network to Host" conversion (i.e. converts big-endian to little-endian)
     88 * {{{Xil_Htonl()}}} - "Host to Network" conversion (i.e. converts little-endian to big-endian)
     89
     90Therefore, in order to use command arguments within your C code, you must convert each argument from big-endian to little-endian:
     91
     92{{{#!c
     93arg_0       = Xil_Ntohl(cmd_args_32[0]);              // Swap endianness of command argument
     94}}}
     95
     96Similarly, in order to send response arguments to the host, you must convert each argument from little-endian to big-endian:
     97
     98{{{#!c
     99resp_args_32[resp_index++] = Xil_Htonl(status);       // Swap endianness of response arguments
     100}}}
    89101
    90102=== Example Command: Reading Tx Queue Status ===
    91103
    92 This example shows how to implement a custom wlan_exp command, including the required Python and C code. The example command retrieves the status of the Tx queues from the WARP node. The upper-level MAC responds to the command with the number of packets currently enqueued in each Tx queue. Because each upper-level MAC (AP, STA, IBSS) handles Tx queues differently the command handler is implemented in the {{{wlan_exp_user_ap_process_cmd()}}} function of the top-level MAC.
     104This example shows how to implement a custom wlan_exp command, including the required Python and C code. The example command retrieves the status of the transmit (Tx) queues from the WARP node. The upper-level MAC responds to the command with the number of packets currently enqueued in each Tx queue. Because each upper-level MAC (AP, STA, IBSS) handles Tx queues differently the command handler is implemented in the {{{wlan_exp_user_ap_process_cmd()}}} function of the top-level MAC.
    93105
    94106'''Python Code'''[[BR]]
     
    98110{{{#!python
    99111
    100 #Define a command ID (must be 24-bit integer)
     112# Define a command ID (must be 24-bit integer)
    101113CMDID_USER_TX_QUEUE_STATUS = 100
    102114
    103 #Define method for sending command and processing the response
     115# Define method for sending command and processing the response
    104116def get_tx_queue_status(node, queue_id=None):
    105117
    106118    if(queue_id is not None):
    107         #Arguments must be a list of ints - listify/cast the scaler queue_id
     119        # Arguments must be a list of ints - listify/cast the scaler queue_id
    108120        args = [int(queue_id)]
    109121    else:
     
    112124    resp = node.send_user_command(CMDID_USER_TX_QUEUE_STATUS, args)
    113125
    114     #Response should be list of ints with 2 values per queue
    115     # Each pair of response values is (queue_id, queue_occupancy)
    116     # Sanity check the response, then convert to a list of 2-tuples and return
     126    # Response should be list of ints with 2 values per queue
     127    #   - Each pair of response values is (queue_id, queue_occupancy)
     128    #   - Sanity check the response, then convert to a list of 2-tuples and return
    117129    if(len(resp) % 2 != 0):
    118130        print('ERROR: Tx Queue Status command response lenth ({0} values) was not even!'.format(len(resp)))
    119131        return
    120132    else:
    121         #Group each pair of response values into 2-tuple and return list of 2-tuples
     133        # Group each pair of response values into 2-tuple and return list of 2-tuples
    122134        ret = zip(*[iter(resp)]*2)
    123135
     
    131143'''C Code'''[[BR]]
    132144
    133 Each upper-level MAC application implements its own callback function for handling application-specific user commands. By default these functions are empty. You must modify the C code to implement the behaviors required for your custom command.
    134 
    135 Modify the {{{wlan_exp_user_ap_process_cmd()}}} function in {{{wlan_mac_ap.c}}}. Start by defining some local variables at the top of the function:
    136 
    137 {{{#!c
    138 
     145Each upper-level MAC application implements its own callback function for handling application-specific user commands. By default these functions are empty. You must modify the C code to implement the behaviors required for your custom command.  The code below is for the AP.  It is left to the user to implement similar functionality the other MAC applications so that the command works on all types of nodes.
     146
     147Modify the {{{wlan_exp_user_ap_process_cmd()}}} function in {{{wlan_mac_ap.c}}}. Start by defining the command ID before the function:
     148
     149{{{#!c
    139150#define CMDID_USER_TX_QUEUE_STATUS 100
    140 int i;
    141 u32 req_queue_id;
    142 u32 q_id, q_occ;
    143 dl_entry* curr_station_info_entry;
    144 station_info* curr_station_info;
    145151}}}
    146152
     
    149155
    150156{{{#!c
    151     case CMDID_USER_TX_QUEUE_STATUS:
     157    case CMDID_USER_TX_QUEUE_STATUS: {
     158        int            iter;
     159        u32            req_queue_id;
     160        u32            q_id, q_occ;
     161        dl_entry     * curr_station_info_entry;
     162        station_info * curr_station_info;
     163
    152164        xil_printf("Got Tx queue status cmd\n");
    153165
     
    162174                xil_printf("No queue ID requested - returning status of all queues\n");
    163175
    164                 //Gather status of all Tx queues:
    165                 // Multicast queue (MCAST_QID)
    166                 // Managment queue (MANAGEMENT_QID)
    167                 // Queue per AID
     176                // Gather status of all Tx queues:
     177                //   Multicast queue (MCAST_QID)
     178                //   Managment queue (MANAGEMENT_QID)
     179                //   Queue per AID
    168180                resp_args_32[resp_index++] = Xil_Htonl(MCAST_QID);
    169181                resp_args_32[resp_index++] = Xil_Htonl(queue_num_queued(MCAST_QID));
     
    172184
    173185                if(my_bss_info->associated_stations.length > 0) {
    174                         curr_station_info_entry = NULL;
    175 
    176                         //Iterating over the dl_list is potentially dangerous, as the list itself might change
     186                        iter                    = my_bss_info->associated_stations.length;
     187                        curr_station_info_entry = my_bss_info->associated_stations.first;
     188
     189                        // Iterating over the dl_list is potentially dangerous, as the list itself might change
    177190                        // if this function is interrupted. We protect against this by iterating over (at most)
    178                         // the number of entries in the list at the time this loop starts. The iteration variable i
     191                        // the number of entries in the list at the time this loop starts. The iteration variable
    179192                        // is *not* used for indexing the list - we still traverse entries with entry.next
    180                         for(i=0; i<my_bss_info->associated_stations.length; i++) {
    181 
    182                                         if(curr_station_info_entry == NULL) {
    183                                                 //First iteration - get the head of the dl_list
    184                                                 curr_station_info_entry = my_bss_info->associated_stations.first;
    185                                         } else {
    186                                                 //Subsequent iteration - get the next entry in the dl_list
    187                                                 curr_station_info_entry = dl_entry_next(curr_station_info_entry);
    188                                         }
    189 
    190                                         //Get the station info pointer from the list entry
     193                        while((cur_station_info_entry != NULL) && (iter-- > 0)) {
     194
     195                                        // Get the station info pointer from the list entry
    191196                                        curr_station_info = (station_info*)(curr_station_info_entry->data);
    192197
    193                                         //Get the queue ID and queue occupancy
     198                                        // Get the queue ID and queue occupancy
    194199                                        q_id = AID_TO_QID(curr_station_info->AID);
    195200                                        q_occ = queue_num_queued(q_id);
    196201                                        xil_printf("Q: %2d %3d\n", q_id, q_occ);
    197202
    198 
    199                                         //Add the queue info to the response payload
     203                                        // Add the queue info to the response payload
    200204                                        resp_args_32[resp_index++] = Xil_Htonl(q_id);
    201205                                resp_args_32[resp_index++] = Xil_Htonl(q_occ);
    202206
    203                                         //Break out of the for loop on the last station info entry or
    204                                 // if our response is already reached the max size allowed by the framework
    205                                         if( (curr_station_info_entry == my_bss_info->associated_stations.last) ||
    206                                                 (dl_entry_next(curr_station_info_entry) == NULL) ||
    207                                                 (resp_index >= max_resp_len) ) {
     207                                        // Get the next entry in the dl_list
     208                                        curr_station_info_entry = dl_entry_next(curr_station_info_entry);
     209
     210                                        // Break out of the for loop if our response is already reached the max size allowed
     211                                // by the framework
     212                                        if(resp_index >= max_resp_len) {
    208213                                                break;
    209214                                        }
     
    212217        }
    213218
    214         //All done - resp_args_32 now contains queue status, res_index is length of response arguments array
    215         // Copy these values into the response header struct
     219        // All done - resp_args_32 now contains queue status, res_index is length of response arguments array
     220        // Update the response header struct with the length of the response and the number of arguments
    216221        resp_hdr->length  += (resp_index * sizeof(resp_args_32[0]));
    217222        resp_hdr->num_args = resp_index;
    218 
    219         break;
     223    }
     224    break;
    220225}}}
    221226
     
    225230{{{#!python
    226231
    227 #Assume n0 is a wlan_exp node object that has already been initialized
     232# Assume n0 is a wlan_exp node object that has already been initialized
    228233q_stat = get_tx_queue_status(n0)
    229234
    230 #Print the queue status response
     235# Print the queue status response
    231236if(q_stat):
    232237  print('Tx Queue Status for node {0}'.format(n0.sn_str))
     
    246251
    247252'''Write-Only'''[[BR]]
    248 A Python script can only write parameters in CPU Low. The script cannot read any data directly from CPU Low. This is by design. The experiments framework C code runs in CPU High. CPU High communicates with CPU Low via the IPC Mailbox. This inter-CPU communication is asynchronous. CPU Low can take an arbitrarily long time to service any new mailbox message from CPU High. The experiments framework cannot block processing in CPU High waiting
     253A Python script can only write parameters in CPU Low. The script cannot read any data directly from CPU Low. This is by design. The experiments framework C code runs in CPU High. CPU High communicates with CPU Low via the IPC Mailbox. This inter-CPU communication is asynchronous. CPU Low can take an arbitrarily long time to service any new mailbox message from CPU High. The experiments framework cannot block processing in CPU High waiting for a response from CPU Low.
    249254
    250255=== Adding Parameters ===
     
    255260We suggest you set the 4 MSB of any new parameters to {{{0xF}}}. We will never use this range for parameters in the reference code. Do this by defining your new parameters with the form:
    256261{{{
    257 //C code
     262// C code
    258263#define CMD_PARAM_LOW_PARAM_NEW_PARAM0 0xF00000000
    259264#define CMD_PARAM_LOW_PARAM_NEW_PARAM1 0xF00000001
    260 //etc...
    261 
    262 #Python code
     265// etc...
     266
     267# Python code
    263268CMD_PARAM_LOW_PARAM_NEW_PARAM0 = 0xF00000000
    264269CMD_PARAM_LOW_PARAM_NEW_PARAM1 = 0xF00000001
    265 #etc...
     270# etc...
    266271}}}
    267272
     
    274279
    275280CPU Low handles Low Parameter messages in two places.
    276  * '''Framework''': the MAC Low Framework handles parameters in the {{{case IPC_MBOX_LOW_PARAM:}}} clause of the IPC mailbox reception handler. This code is responsible for any framework or PHY parameters which are used by any low-level MAC. In the Reference Design code this handler is implemented in the {{{wlan_mac_low_process_ipc_msg()}}} function in [browser:ReferenceDesigns/w3_802.11/c/wlan_mac_low_framework/wlan_mac_low.c#L544 wlan_mac_low.c].
    277 
    278  * '''MAC''': the lower MAC handles parameters specific to the MAC application. Each lower-level MAC implements a callback function which processes any MAc-specific parameters. The framework executes this callback for any parameter not handled by the framework itself. The Reference Design code implements these callbacks in {{{wlan_dcf_process_low_param()}}} in [browser:ReferenceDesigns/w3_802.11/c/wlan_mac_low_dcf/wlan_mac_dcf.c#L1423 wlan_mac_dcf.c] and {{{wlan_nomac_process_low_param()}}} in [browser:ReferenceDesigns/w3_802.11/c/wlan_mac_low_nomac/wlan_mac_nomac.c#L296 wlan_mac_nomac.c].
     281 * '''Framework''': the MAC Low Framework handles parameters in the {{{case IPC_MBOX_LOW_PARAM:}}} clause of the IPC mailbox reception handler. This code is responsible for any framework or PHY parameters which are used by any low-level MAC. In the Reference Design code this handler is implemented in the {{{wlan_mac_low_process_ipc_msg()}}} function in [browser:ReferenceDesigns/w3_802.11/c/wlan_mac_low_framework/wlan_mac_low.c?rev=4956#L519 wlan_mac_low.c].
     282
     283 * '''MAC''': the lower MAC handles parameters specific to the MAC application. Each lower-level MAC implements a callback function which processes any MAC-specific parameters. The framework executes this callback for any parameter not handled by the framework itself. The Reference Design code implements these callbacks in the {{{process_low_param()}}} function in [browser:ReferenceDesigns/w3_802.11/c/wlan_mac_low_dcf/wlan_mac_dcf.c?rev=4933#L1428 wlan_mac_dcf.c] and [browser:ReferenceDesigns/w3_802.11/c/wlan_mac_low_nomac/wlan_mac_nomac.c?rev=4932#L300 wlan_mac_nomac.c].
    279284
    280285When adding a new parameter you should add a new {{{case}}} to either the framework handler or the MAC code's handler.