| 1 | {{{#!comment |
| 2 | [[Include(wiki:802.11/beta-note)]] |
| 3 | }}} |
| 4 | |
| 5 | [[TracNav(802.11/TOC)]] |
| 6 | |
| 7 | = WLAN Exp: How to add a Command = |
| 8 | |
| 9 | This tutorial will explain how to add a new command to the WLAN Exp framework. This will allow a user to create a custom command that can be called via the WLAN Experiments Framework. |
| 10 | |
| 11 | |
| 12 | == 802.11 Reference Design v1.3 and prior == |
| 13 | |
| 14 | '''Overview:''' |
| 15 | |
| 16 | 1. Edit wlan_exp/cmds.py with: |
| 17 | - Create new non-overlapping CMDID_ value |
| 18 | - Create new message.Cmd subclass, probably by copy/paste of existing simple command |
| 19 | - Add new subclass to __all__ |
| 20 | 1. Edit wlan_exp/node.py (or a subclass) with new command-specific node method |
| 21 | 1. Add new CMDID_ value to wlan_mac_high_framework/include/wlan_exp_node.h |
| 22 | 1. Edit wlan_mac_high_framework/wlan_exp_node.c with new case in giant node_processCmd() switch statement |
| 23 | 1. Possibly edit wlan_mac_[ap,sta,ibss].[c,h] for any MAC-specific responses to the new command |
| 24 | |
| 25 | |
| 26 | '''Edit wlan_exp/cmds.py:''' |
| 27 | |
| 28 | Add a new Command ID (CMDID_) value that does not overlap with any existing Command ID value. For example, we can add "CMDID_NODE_USER_CMD" to the file: |
| 29 | |
| 30 | {{{ |
| 31 | CMDID_NODE_WLAN_MAC_ADDR = 0x001018 |
| 32 | CMDID_NODE_LOW_PARAM = 0x001020 |
| 33 | CMDID_NODE_USER_INFO_CMD = 0x001030 |
| 34 | |
| 35 | CMD_PARAM_WRITE = 0x00000000 |
| 36 | CMD_PARAM_READ = 0x00000001 |
| 37 | }}} |
| 38 | |
| 39 | |
| 40 | Create a new message.Cmd sub-class. For example, this new class will take in an array of values and send them to the node. It will also take any values returned by the node and return them to the user: |
| 41 | |
| 42 | {{{ |
| 43 | class NodeUserInfoCommand(message.Cmd): |
| 44 | """Command to pass user information to/from the node |
| 45 | |
| 46 | Attributes: |
| 47 | values -- Scalar or list of u32 values to pass to the node |
| 48 | |
| 49 | """ |
| 50 | def __init__(self, values=None): |
| 51 | super(NodeUserCommand, self).__init__() |
| 52 | |
| 53 | self.command = _CMD_GROUP_NODE + CMDID_NDOE_USER_INFO_CMD |
| 54 | |
| 55 | # Caluculate the size in words of the values argument |
| 56 | size = 0 |
| 57 | |
| 58 | if values is not None: |
| 59 | size += len(values) |
| 60 | |
| 61 | # Add the size in words of the values arguments to the message |
| 62 | self.add_args(size) |
| 63 | |
| 64 | # Add the values to the message |
| 65 | if values is not None: |
| 66 | try: |
| 67 | for v in values: |
| 68 | self.add_args(v) |
| 69 | except TypeError: |
| 70 | self.add_args(values) |
| 71 | |
| 72 | def process_resp(self, resp): |
| 73 | """ Message format: |
| 74 | respArgs32[0] Status |
| 75 | respArgs32[1] Size in words of return values |
| 76 | respArgs32[2:N] Values |
| 77 | """ |
| 78 | # Create a hash of all possible error codes with respective error messages |
| 79 | error_code_base = CMD_PARAM_ERROR |
| 80 | |
| 81 | status_errors = { error_code_base : "Error Processing User Command" } |
| 82 | |
| 83 | # Get the arguments from the response |
| 84 | args = resp.get_args() |
| 85 | |
| 86 | # If the response is valid, then return the size and any values from the node |
| 87 | if resp.resp_is_valid(num_args=(args[1] + 2), status_errors=status_errors, name='from User Command command'): |
| 88 | return args[1:] |
| 89 | else: |
| 90 | return None |
| 91 | |
| 92 | # End Class |
| 93 | }}} |
| 94 | |
| 95 | |
| 96 | Add new subclass to __all__: |
| 97 | |
| 98 | |
| 99 | {{{ |
| 100 | __all__ = [# Log command classes |
| 101 | 'LogGetEvents', 'LogConfigure', 'LogGetStatus', 'LogGetCapacity', 'LogAddExpInfoEntry', |
| 102 | 'LogAddCountsTxRx', |
| 103 | # Counts command classes |
| 104 | 'CountsConfigure', 'CountsGetTxRx', |
| 105 | # LTG classes |
| 106 | 'LTGConfigure', 'LTGStart', 'LTGStop', 'LTGRemove', 'LTGStatus', |
| 107 | # Node command classes |
| 108 | 'NodeResetState', 'NodeConfigure', 'NodeProcWLANMACAddr', 'NodeProcTime', |
| 109 | 'NodeSetLowToHighFilter', 'NodeProcChannel', 'NodeProcRandomSeed', 'NodeLowParam', 'NodeUserInfoCommand', |
| 110 | ... |
| 111 | }}} |
| 112 | |
| 113 | |
| 114 | '''Edit wlan_exp/node.py (or a subclass)''' |
| 115 | |
| 116 | Add new command specific method to the node (or a subclass): |
| 117 | |
| 118 | {{{ |
| 119 | def send_user_cmd(self, values=None): |
| 120 | """Send information to / Receive information from a node |
| 121 | |
| 122 | Args: |
| 123 | values (list of int): Value(s) to send to the node |
| 124 | |
| 125 | Returns: |
| 126 | values (list of int): Value(s) received from the node |
| 127 | """ |
| 128 | if values is not None: |
| 129 | try: |
| 130 | v0 = values[0] |
| 131 | except TypeError: |
| 132 | v0 = values |
| 133 | |
| 134 | if ((type(v0) is not int) and (type(v0) is not long)) or (v0 >= 2**32): |
| 135 | raise Exception('ERROR: values must be scalar or iterable of ints in [0,2^32-1]!') |
| 136 | |
| 137 | try: |
| 138 | vals = list(values) |
| 139 | except TypeError: |
| 140 | vals = [values] |
| 141 | |
| 142 | return self.send_cmd(cmds.NodeUserInfoCommand(values=vals)) |
| 143 | }}} |
| 144 | |
| 145 | |
| 146 | '''Edit wlan_mac_high_framework/include/wlan_exp_node.h''' |
| 147 | |
| 148 | Add new Command ID (CMDID_) value to the C code. This value should be the same as the CMDID_ value defined in the Python code: |
| 149 | |
| 150 | {{{ |
| 151 | #define CMDID_NODE_WLAN_MAC_ADDR 0x001018 |
| 152 | #define CMDID_NODE_LOW_PARAM 0x001020 |
| 153 | #define CMDID_NODE_USER_INFO_CMD 0x001030 |
| 154 | |
| 155 | #define CMD_PARAM_WRITE_VAL 0x00000000 |
| 156 | #define CMD_PARAM_READ_VAL 0x00000001 |
| 157 | }}} |
| 158 | |
| 159 | |
| 160 | '''Edit wlan_mac_high_framework/wlan_exp_node.c''' |
| 161 | |
| 162 | Add command implementation with new case in the node_processCmd() switch statement: |
| 163 | |
| 164 | {{{ |
| 165 | //--------------------------------------------------------------------- |
| 166 | case CMDID_NODE_USER_INFO_CMD: |
| 167 | // Receive information from user / Send information to user |
| 168 | // |
| 169 | // Message format: |
| 170 | // cmd_args_32[0] Size in words of received values |
| 171 | // cmd_args_32[1:N] Values |
| 172 | // |
| 173 | // Response format: |
| 174 | // resp_args_32[0] Status |
| 175 | // resp_args_32[1] Size in words of values to send |
| 176 | // resp_args_32[3:M] Values |
| 177 | // |
| 178 | status = CMD_PARAM_SUCCESS; |
| 179 | size = Xil_Ntohl(cmd_args_32[0]); |
| 180 | temp = 0; |
| 181 | |
| 182 | // Byte swap all the value words in the message (in place) |
| 183 | for (i = 1; i < (size + 1); i++) { |
| 184 | cmd_args_32[i] = Xil_Ntohl(cmd_args_32[i]); |
| 185 | } |
| 186 | |
| 187 | |
| 188 | // |
| 189 | // ADD PROCESSING FOR COMMAND VALUES |
| 190 | // |
| 191 | // NOTE: The user can define any message structure needed. Modify the 'status' variable |
| 192 | // if there is an error (use CMD_PARAM_ERROR or any additional error codes the user adds). |
| 193 | // Modify the 'temp' variable to indicate the number of return values. Additional |
| 194 | // variables can be declared at the beginning of the function. |
| 195 | // |
| 196 | |
| 197 | // In this example, the command merely echos the values sent to the node. |
| 198 | // |
| 199 | // NOTE: Need to make sure the indexing is correct since the response values start at a different |
| 200 | // word than the received values. |
| 201 | // |
| 202 | for (i = 1; i < (size + 1); i++) { |
| 203 | resp_args_32[i+1] = cmd_args_32[i]; |
| 204 | } |
| 205 | |
| 206 | |
| 207 | // Send response |
| 208 | resp_args_32[0] = Xil_Htonl(status); |
| 209 | resp_args_32[1] = Xil_Htonl(temp); |
| 210 | |
| 211 | // Byte swap all the value words in the return message (in place) |
| 212 | for (i = 2; i < (temp + 2); i++) { |
| 213 | resp_args_32[i] = Xil_Htonl(resp_args_32[i]); |
| 214 | } |
| 215 | |
| 216 | resp_hdr->length += (resp_index * sizeof(resp_args_32)); |
| 217 | resp_hdr->num_args = resp_index; |
| 218 | break; |
| 219 | }}} |
| 220 | |
| 221 | |
| 222 | |