source: ReferenceDesigns/w3_802.11/python/wlan_exp/cmds.py

Last change on this file was 6320, checked in by chunter, 5 years ago

1.8.0 release wlan-exp

File size: 82.9 KB
Line 
1# -*- coding: utf-8 -*-
2"""
3------------------------------------------------------------------------------
4Mango 802.11 Reference Design Experiments Framework - Commands
5------------------------------------------------------------------------------
6License:   Copyright 2019 Mango Communications, Inc. All rights reserved.
7           Use and distribution subject to terms in LICENSE.txt
8------------------------------------------------------------------------------
9
10This module provides class definitions for all wlan_exp commands.
11
12"""
13
14import wlan_exp.transport.cmds as cmds
15import wlan_exp.transport.message as message
16import wlan_exp.transport.transport_eth_ip_udp as transport
17
18__all__ = [# Log command classes
19           'LogGetEvents', 'LogConfigure', 'LogGetStatus', 'LogGetCapacity', 
20           'LogAddExpInfoEntry',
21           # LTG classes
22           'LTGConfigure', 'LTGStart', 'LTGStop', 'LTGRemove', 'LTGStatus',
23           # Node command classes
24           'NodeResetState', 'NodeConfigure', 'NodeProcWLANMACAddr', 
25           'NodeProcTime', 'NodeSetLowToHighFilter', 'NodeProcRandomSeed', 
26           'NodeLowParam', 'NodeProcTxPower', 'NodeProcTxRate', 
27           'NodeProcTxAntMode', 'NodeProcRxAntMode',
28           # Scan command classes
29           'NodeProcScanParam', 'NodeProcScan', 
30           # Association command classes
31           'NodeConfigBSS', 'NodeDisassociate', 'NodeGetStationInfoList', 
32           'NodeGetNetworkInfo',
33           # Queue command classes
34           'PurgeAllTxQueues',
35           # AP command classes
36           'NodeAPConfigure', 'NodeAPAddAssociation', 'NodeAPSetAuthAddrFilter',
37           # STA command classes
38           'NodeSTASetAID', 'NodeSTAJoin', 'NodeSTAJoinStatus',
39           # IBSS command classes
40           # None
41           # User command classes
42           'UserSendCmd'
43          ]
44
45
46# Command IDs
47#  Each command is identified by a unique 24-bit integer ID
48#  The IDs defined here must match the corresponding definitions in
49#   wlan_exp_node.h
50CMDID_NODE_TYPE                                  = 0x000000
51CMDID_NODE_INFO                                  = 0x000001
52
53CMDID_NODE_RESET_STATE                           = 0x001000
54CMDID_NODE_CONFIGURE                             = 0x001001
55CMDID_NODE_CONFIG_BSS                            = 0x001002
56CMDID_NODE_TIME                                  = 0x001010
57CMDID_NODE_CHANNEL                               = 0x001011
58CMDID_NODE_TX_POWER                              = 0x001012
59CMDID_NODE_TX_RATE                               = 0x001013
60CMDID_NODE_TX_ANT_MODE                           = 0x001014
61CMDID_NODE_RX_ANT_MODE                           = 0x001015
62CMDID_NODE_LOW_TO_HIGH_FILTER                    = 0x001016
63CMDID_NODE_RANDOM_SEED                           = 0x001017
64CMDID_NODE_WLAN_MAC_ADDR                         = 0x001018
65CMDID_NODE_LOW_PARAM                             = 0x001020
66
67CMD_PARAM_WRITE                                  = 0x00000000
68CMD_PARAM_READ                                   = 0x00000001
69CMD_PARAM_RSVD                                   = 0xFFFFFFFF
70
71CMD_PARAM_SUCCESS                                = 0x00000000
72CMD_PARAM_WARNING                                = 0xF0000000
73CMD_PARAM_ERROR                                  = 0xFF000000
74
75#Note: the following are used as bit masks on the node side
76CMD_PARAM_TXPARAM_DATA                           = 0x00000001
77CMD_PARAM_TXPARAM_MGMT                           = 0x00000002
78CMD_PARAM_TXPARAM_CTRL                           = 0x00000004
79CMD_PARAM_TXPARAM_ALL                            = 0x00000007
80
81CMD_PARAM_TXPARAM_ADDR_NONE                      = 0x00000000
82CMD_PARAM_TXPARAM_ADDR_ALL_UNICAST               = 0x00000001
83CMD_PARAM_TXPARAM_ADDR_ALL_MULTICAST             = 0x00000002
84CMD_PARAM_TXPARAM_ADDR_ALL                       = 0x00000003
85CMD_PARAM_TXPARAM_ADDR_SINGLE                    = 0x00000004
86
87CMD_PARAM_NODE_CONFIG_ALL                        = 0xFFFFFFFF
88
89CMD_PARAM_NODE_RESET_FLAG_LOG                    = 0x00000001
90CMD_PARAM_NODE_RESET_FLAG_TXRX_COUNTS            = 0x00000002
91CMD_PARAM_NODE_RESET_FLAG_LTG                    = 0x00000004
92CMD_PARAM_NODE_RESET_FLAG_TX_QUEUES              = 0x00000008
93CMD_PARAM_NODE_RESET_FLAG_BSS                    = 0x00000010
94CMD_PARAM_NODE_RESET_FLAG_NETWORK_LIST           = 0x00000020
95CMD_PARAM_NODE_RESET_FLAG_STATION_INFO_LIST      = 0x00000040
96
97CMD_PARAM_NODE_CONFIG_FLAG_DSSS_ENABLE           = 0x00000001
98CMD_PARAM_NODE_CONFIG_FLAG_BEACON_TIME_UPDATE    = 0x00000002
99CMD_PARAM_NODE_CONFIG_FLAG_ETH_PORTAL            = 0x00000004
100CMD_PARAM_NODE_CONFIG_SET_WLAN_EXP_PRINT_LEVEL   = 0x80000000
101
102ERROR_CONFIG_BSS_BSSID_INVALID                   = 0x000001
103ERROR_CONFIG_BSS_BSSID_INSUFFICIENT_ARGUMENTS    = 0x000002
104ERROR_CONFIG_BSS_CHANNEL_INVALID                 = 0x000004
105ERROR_CONFIG_BSS_BEACON_INTERVAL_INVALID         = 0x000008
106ERROR_CONFIG_BSS_HT_CAPABLE_INVALID              = 0x000010
107ERROR_CONFIG_BSS_DTIM_PERIOD_INVALID             = 0x000020
108
109CMD_PARAM_TIME_ADD_TO_LOG                        = 0x00000002
110CMD_PARAM_RSVD_TIME                              = 0xFFFFFFFF
111
112TIME_TYPE_FLOAT                                  = 0x00000000
113TIME_TYPE_INT                                    = 0x00000001
114
115CMD_PARAM_RSVD_MAC_ADDR                          = 0x00000000
116
117CMD_PARAM_NODE_TX_POWER_LOW                      = 0x00000010
118CMD_PARAM_NODE_TX_POWER_ALL                      = 0x00000020
119
120CMD_PARAM_NODE_TX_ANT_ALL                        = 0x00000010
121
122CMD_PARAM_RX_FILTER_FCS_GOOD                     = 0x1000
123CMD_PARAM_RX_FILTER_FCS_ALL                      = 0x2000
124CMD_PARAM_RX_FILTER_FCS_NOCHANGE                 = 0xF000
125
126CMD_PARAM_RX_FILTER_HDR_ADDR_MATCH_MPDU          = 0x0001
127CMD_PARAM_RX_FILTER_HDR_ALL_MPDU                 = 0x0002
128CMD_PARAM_RX_FILTER_HDR_ALL                      = 0x0003
129CMD_PARAM_RX_FILTER_HDR_NOCHANGE                 = 0x0FFF
130
131CMD_PARAM_RANDOM_SEED_VALID                      = 0x00000001
132CMD_PARAM_RANDOM_SEED_RSVD                       = 0xFFFFFFFF
133
134# Low Param IDs -- in sync with wlan_mac_low.h
135CMD_PARAM_LOW_PARAM_PKT_DET_MIN_POWER            = 0x00000006
136CMD_PARAM_LOW_PARAM_PHY_SAMPLE_RATE              = 0x00000008
137CMD_PARAM_LOW_PARAM_DSSS_PKT_DET_THRESH          = 0x0000A000
138CMD_PARAM_LOW_PARAM_OFDM_PKT_DET_THRESH          = 0x0000B000
139CMD_PARAM_LOW_PARAM_OFDM_RX_EN                      = 0x0000C000
140
141CMD_PARAM_LOW_PARAM_DCF_RTS_THRESH               = 0x10000001
142CMD_PARAM_LOW_PARAM_DCF_DOT11SHORTRETRY          = 0x10000002
143CMD_PARAM_LOW_PARAM_DCF_DOT11LONGRETRY           = 0x10000003
144CMD_PARAM_LOW_PARAM_DCF_CW_EXP_MIN               = 0x10000005
145CMD_PARAM_LOW_PARAM_DCF_CW_EXP_MAX               = 0x10000006
146
147CMD_PARAM_NODE_MIN_MIN_PKT_DET_POWER_DBM         = -90
148CMD_PARAM_NODE_MAX_MIN_PKT_DET_POWER_DBM         = -30
149
150# LTG commands and defined values
151CMDID_LTG_CONFIG                                 = 0x002000
152CMDID_LTG_START                                  = 0x002001
153CMDID_LTG_STOP                                   = 0x002002
154CMDID_LTG_REMOVE                                 = 0x002003
155CMDID_LTG_STATUS                                 = 0x002004
156
157CMD_PARAM_LTG_ERROR                              = 0x000001
158
159CMD_PARAM_LTG_ALL_LTGS                           = 0xFFFFFFFF
160
161CMD_PARAM_LTG_CONFIG_FLAG_AUTOSTART              = 0x00000001
162
163CMD_PARAM_LTG_RUNNING                            = 0x00000001
164CMD_PARAM_LTG_STOPPED                            = 0x00000000
165
166
167# Log commands and defined values
168CMDID_LOG_CONFIG                                 = 0x003000
169CMDID_LOG_GET_STATUS                             = 0x003001
170CMDID_LOG_GET_CAPACITY                           = 0x003002
171CMDID_LOG_GET_ENTRIES                            = 0x003003
172CMDID_LOG_ADD_EXP_INFO_ENTRY                     = 0x003004
173
174CMD_PARAM_LOG_GET_ALL_ENTRIES                    = 0xFFFFFFFF
175
176CMD_PARAM_LOG_CONFIG_FLAG_LOGGING                = 0x00000001
177CMD_PARAM_LOG_CONFIG_FLAG_WRAP                   = 0x00000002
178CMD_PARAM_LOG_CONFIG_FLAG_LOG_PAYLOADS           = 0x00000004
179CMD_PARAM_LOG_CONFIG_FLAG_TXRX_MPDU              = 0x00000008
180CMD_PARAM_LOG_CONFIG_FLAG_TXRX_CTRL              = 0x00000010
181
182
183# Counts commands and defined values
184CMDID_COUNTS_GET_TXRX                            = 0x004001
185
186CMD_PARAM_COUNTS_CONFIG_FLAG_PROMISC             = 0x00000001
187
188CMD_PARAM_COUNTS_RETURN_ZEROED_IF_NONE           = 0x80000000
189
190
191# Queue commands and defined values
192CMDID_PURGE_ALL_TX_QUEUES                        = 0x005000
193
194
195# Scan commands and defined values
196CMDID_NODE_SCAN_PARAM                            = 0x006000
197CMDID_NODE_SCAN                                  = 0x006001
198
199CMD_PARAM_NODE_SCAN_ENABLE                       = 0x00000001
200CMD_PARAM_NODE_SCAN_DISABLE                      = 0x00000000
201
202
203# Association commands and defined values
204CMDID_GET_BSS_MEMBERS                            = 0x007001
205CMDID_GET_BSS_INFO                               = 0x007002
206CMDID_GET_STATION_INFO_LIST                      = 0x007003
207
208CMDID_NODE_DISASSOCIATE                          = 0x007010
209CMDID_NODE_ADD_ASSOCIATION                       = 0x007011
210ADD_ASSOCIATION_ERROR_MEMORY                   = 0x000001
211ADD_ASSOCIATION_ERROR_TOO_MANY_ASSOC             = 0x000002
212
213CMD_PARAM_ADD_ASSOCIATION_DISABLE_INACTIVITY_TIMEOUT = 0x00000001
214CMD_PARAM_ADD_ASSOCIATION_HT_CAPABLE_STA         = 0x00000004
215
216CMD_PARAM_GET_ALL_ASSOCIATED                     = 0x0000000000000000
217CMD_PARAM_GET_ALL_LIST_ENTRIES                   = 0x0000000000000000
218
219CMD_PARAM_MAX_SSID_LEN                           = 32
220CMD_PARAM_RSVD_CHANNEL                           = 0
221
222
223# AP commands and defined values
224CMDID_NODE_AP_CONFIG                             = 0x100000
225CMDID_NODE_AP_SET_AUTHENTICATION_ADDR_FILTER     = 0x100001
226
227CMD_PARAM_AP_CONFIG_FLAG_DTIM_MULTICAST_BUFFER   = 0x00000001
228
229
230# STA commands and defined values
231CMDID_NODE_STA_JOIN                              = 0x100000
232CMDID_NODE_STA_JOIN_STATUS                       = 0x100001
233CMDID_NODE_STA_SET_AID                           = 0x100002
234
235
236# IBSS commands and defined values
237
238
239# Developer commands and defined values
240CMDID_DEV_MEM_HIGH                               = 0xFFF000
241CMDID_DEV_MEM_LOW                                = 0xFFF001
242CMDID_DEV_EEPROM                                 = 0xFFF002
243
244
245# Local Constants
246_CMD_GROUP_NODE                                  = (cmds.GROUP_NODE << 24)
247_CMD_GROUP_USER                                  = (cmds.GROUP_USER << 24)
248
249
250#-----------------------------------------------------------------------------
251# Class Definitions for wlan_exp Commands
252#-----------------------------------------------------------------------------
253
254# TODO: move NODE_TYPE and NODE_INFO commands here when transport.node
255#  is finally stripped down to the bare essentials. For now these commands
256#  must be in transport.node to be accessed during node init, where the
257#  WlanExpTransportNodeFactory subclasses transport.node, not wlan_exp.node
258
259#--------------------------------------------
260# Log Commands
261#--------------------------------------------
262class LogGetEvents(message.BufferCmd):
263    """Command to retreive log data from a wlan_exp node"""
264    def __init__(self, size, start_byte=0):
265        command = _CMD_GROUP_NODE + CMDID_LOG_GET_ENTRIES
266
267        if (size == CMD_PARAM_LOG_GET_ALL_ENTRIES):
268            size = message.CMD_BUFFER_GET_SIZE_FROM_DATA
269
270        super(LogGetEvents, self).__init__(
271                command=command, start_byte=start_byte, size=size)
272
273    def process_resp(self, resp):
274        return resp
275
276# End Class
277
278
279class LogConfigure(message.Cmd):
280    """Command to configure the Event log.
281
282    Attributes (default state on the node is in CAPS):
283        log_enable           -- Enable the event log (TRUE/False)
284        log_wrap_enable      -- Enable event log wrapping (True/FALSE)
285        log_full_payloads    -- Record full Tx/Rx payloads in event log (True/FALSE)
286        log_txrx_mpdu        -- Enable Tx/Rx log entries for MPDU frames (TRUE/False)
287        log_txrx_ctrl        -- Enable Tx/Rx log entries for CTRL frames (TRUE/False)
288    """
289    def __init__(self, log_enable=None, log_wrap_enable=None,
290                       log_full_payloads=None, log_txrx_mpdu=None, 
291                       log_txrx_ctrl=None):
292        super(LogConfigure, self).__init__()
293        self.command = _CMD_GROUP_NODE + CMDID_LOG_CONFIG
294
295        flags = 0
296        mask  = 0
297
298        if log_enable is not None:
299            mask += CMD_PARAM_LOG_CONFIG_FLAG_LOGGING
300            if log_enable:
301                flags += CMD_PARAM_LOG_CONFIG_FLAG_LOGGING
302
303        if log_wrap_enable is not None:
304            mask += CMD_PARAM_LOG_CONFIG_FLAG_WRAP
305            if log_wrap_enable:
306                flags += CMD_PARAM_LOG_CONFIG_FLAG_WRAP
307
308        if log_full_payloads is not None:
309            mask += CMD_PARAM_LOG_CONFIG_FLAG_LOG_PAYLOADS
310            if log_full_payloads:
311                flags += CMD_PARAM_LOG_CONFIG_FLAG_LOG_PAYLOADS
312
313        if log_txrx_mpdu is not None:
314            mask += CMD_PARAM_LOG_CONFIG_FLAG_TXRX_MPDU
315            if log_txrx_mpdu:
316                flags += CMD_PARAM_LOG_CONFIG_FLAG_TXRX_MPDU
317
318        if log_txrx_ctrl is not None:
319            mask += CMD_PARAM_LOG_CONFIG_FLAG_TXRX_CTRL
320            if log_txrx_ctrl:
321                flags += CMD_PARAM_LOG_CONFIG_FLAG_TXRX_CTRL
322
323        self.add_args(flags)
324        self.add_args(mask)
325
326    def process_resp(self, resp):
327        pass
328
329# End Class
330
331
332class LogGetStatus(message.Cmd):
333    """Command to get the state information about the log."""
334    def __init__(self):
335        super(LogGetStatus, self).__init__()
336        self.command = _CMD_GROUP_NODE + CMDID_LOG_GET_STATUS
337
338    def process_resp(self, resp):
339        if resp.resp_is_valid(num_args=4):
340            args = resp.get_args()
341            return (args[0], args[1], args[2], args[3])
342        else:
343            return (0,0,0,0)
344
345# End Class
346
347
348class LogGetCapacity(message.Cmd):
349    """Command to get the log capacity and current use."""
350    def __init__(self):
351        super(LogGetCapacity, self).__init__()
352        self.command = _CMD_GROUP_NODE + CMDID_LOG_GET_CAPACITY
353
354    def process_resp(self, resp):
355        if resp.resp_is_valid(num_args=2):
356            args = resp.get_args()
357            return (args[0], args[1])
358        else:
359            return (0,0)
360
361# End Class
362
363
364class LogAddExpInfoEntry(message.Cmd):
365    """Command to write a EXP_INFO Log Entry to the node."""
366    def __init__(self, info_type, message):
367        super(LogAddExpInfoEntry, self).__init__()
368        self.command = _CMD_GROUP_NODE + CMDID_LOG_ADD_EXP_INFO_ENTRY
369       
370        # u32 Args:
371        #  0: info_type (arbitrary value, writte to log)
372        #  1: msg_len (length of payload message in bytes)
373
374        self.add_args(info_type)
375
376        if message is None:
377            self.add_args(0)
378        else:
379            msg_len = len(message)
380            if msg_len > 255:
381                raise Exception('ERROR: EXP_INFO log entry message (len {0}) too long - 255 bytex max'.format(msg_len))
382           
383            msg_bytes = bytearray(message, 'UTF-8')
384
385            self.add_args(msg_len)
386            self.set_payload(msg_bytes)
387
388    def process_resp(self, resp):
389        pass
390
391# End Class
392
393
394#--------------------------------------------
395# Local Traffic Generation (LTG) Commands
396#--------------------------------------------
397class LTGCommon(message.Cmd):
398    """Common code for LTG Commands."""
399    name = None
400
401    def __init__(self, ltg_id=None):
402        super(LTGCommon, self).__init__()
403
404        if ltg_id is not None:
405            if type(ltg_id) is not int:
406                raise TypeError("LTG ID must be an integer.")
407            self.add_args(ltg_id)
408        else:
409            self.add_args(CMD_PARAM_LTG_ALL_LTGS)
410
411    def process_resp(self, resp):
412        error_code    = CMD_PARAM_ERROR + CMD_PARAM_LTG_ERROR
413        error_msg     = "Could not {0} the LTG with that LTG ID.".format(self.name)
414        status_errors = { error_code : error_msg }
415
416        if resp.resp_is_valid(num_args=1,
417                              status_errors=status_errors,
418                              name='for the LTG {0} command'.format(self.name)):
419            args = resp.get_args()
420            return args[0]
421        else:
422            return CMD_PARAM_ERROR
423
424# End Class
425
426
427class LTGConfigure(message.Cmd):
428    """Command to configure an LTG with the given traffic flow to the
429    specified node.
430    """
431    name = 'configure'
432
433    def __init__(self, traffic_flow, auto_start=False):
434        super(LTGConfigure, self).__init__()
435        self.command = _CMD_GROUP_NODE + CMDID_LTG_CONFIG
436
437        flags = 0
438
439        if auto_start:
440            flags += CMD_PARAM_LTG_CONFIG_FLAG_AUTOSTART
441
442        # Args:
443        #  0: flags
444        #  1: LTG schedule type (unique value per LTG Schedule class)
445        self.add_args(flags)
446        self.add_args(traffic_flow.ltg_schedule.ltg_type)
447
448        # Add the serialized LTG flow config as the command payload
449        self.set_payload(bytearray(traffic_flow.serialize()))
450
451    def process_resp(self, resp):
452        error_code    = CMD_PARAM_ERROR + CMD_PARAM_LTG_ERROR
453        error_msg     = "\n\nERROR MESSAGE: Could not create the LTG.  Check that the node \n"
454        error_msg    += "has enough heap available to perform this operation. Commonly, \n"
455        error_msg    += "this can occur if LTGs are not removed (ie 'ltg_remove(ltg_id)') \n"
456        error_msg    += "when they are finished being used.\n"
457        status_errors = { error_code : error_msg }
458
459        if resp.resp_is_valid(num_args=2,
460                              status_errors=status_errors,
461                              name='for the LTG {0} command'.format(self.name)):
462            args = resp.get_args()
463            return args[1]
464        else:
465            return CMD_PARAM_ERROR
466
467# End Class
468
469
470class LTGStart(LTGCommon):
471    """Command to start an LTG. The LTG must have been previously configured. If no
472    ltg_id is provided the node will start all LTG instances that are currently configured.
473    """
474    name = 'start'
475
476    def __init__(self, ltg_id=None):
477        super(LTGStart, self).__init__(ltg_id)
478        self.command = _CMD_GROUP_NODE + CMDID_LTG_START
479
480# End Class
481
482
483class LTGStop(LTGCommon):
484    """Command to stop an LTG. The LTG must have been previously configured. If no
485    ltg_id is provided the node will stop all LTG instances that are currently configured.
486    """
487    name = 'stop'
488
489    def __init__(self, ltg_id=None):
490        super(LTGStop, self).__init__(ltg_id)
491        self.command = _CMD_GROUP_NODE + CMDID_LTG_STOP
492
493# End Class
494
495
496class LTGRemove(LTGCommon):
497    """Command to remove an LTG instance. If no ltg_id is provided the node will remove all
498    LTG instances that are currently configured.
499    """
500    name = 'remove'
501
502    def __init__(self, ltg_id=None):
503        super(LTGRemove, self).__init__(ltg_id)
504        self.command = _CMD_GROUP_NODE + CMDID_LTG_REMOVE
505
506# End Class
507
508
509class LTGStatus(message.Cmd):
510    """Command to get the status of the LTG."""
511    name        = 'status'
512
513    def __init__(self, ltg_id):
514        super(LTGStatus, self).__init__()
515        self.command = _CMD_GROUP_NODE + CMDID_LTG_STATUS
516
517        if type(ltg_id) is not int:
518            raise TypeError("LTG ID must be an integer.")
519        self.add_args(ltg_id)
520
521    def process_resp(self, resp):
522        error_code    = CMD_PARAM_ERROR + CMD_PARAM_LTG_ERROR
523        error_msg     = "\n\nERROR MESSAGE:  Could not find status for given LTG ID.\n"
524        error_msg    += "Please check that it has not been removed."
525        status_errors = { error_code : error_msg }
526
527        if resp.resp_is_valid(num_args=6,
528                              status_errors=status_errors,
529                              name='for the LTG {0} command'.format(self.name)):
530            args = resp.get_args()
531
532            start_timestamp = (2**32 * args[3]) + args[2]
533            stop_timestamp  = (2**32 * args[5]) + args[4]
534
535            return (True, (args[1] == CMD_PARAM_LTG_RUNNING), start_timestamp, stop_timestamp)
536        else:
537            return (False, False, 0, 0)
538
539# End Class
540
541
542
543#--------------------------------------------
544# Configure Node Attribute Commands
545#--------------------------------------------
546class NodeResetState(message.Cmd):
547    """Command to reset the state of a portion of the node defined by the flags.
548
549    Attributes:
550        flags -- [0] NODE_RESET_LOG
551                 [1] NODE_RESET_TXRX_STATS
552                 [2] NODE_RESET_LTG
553                 [3] NODE_RESET_TX_DATA_QUEUE
554                 [4] NODE_RESET_FLAG_BSS
555                 [5] NODE_RESET_FLAG_NETWORK_LIST
556    """
557    def __init__(self, flags):
558        super(NodeResetState, self).__init__()
559        self.command = _CMD_GROUP_NODE +  CMDID_NODE_RESET_STATE
560        self.add_args(flags)
561
562    def process_resp(self, resp):
563        pass
564
565# End Class
566
567
568class NodeConfigure(message.Cmd):
569    """Command to configure flag parameters on the node
570
571    Attributes:
572        dsss_enable (bool) -- Whether DSSS packets are received.
573        beacon_mac_time_update (bool) -- Whether MAC time is updated from beacons
574        print_level (int) -- Controls verbosity of wlan_exp prints to the node's UART
575    """
576    def __init__(self, dsss_enable=None, beacon_mac_time_update=None, portal_enable=None, print_level=None):
577        super(NodeConfigure, self).__init__()
578        self.command = _CMD_GROUP_NODE + CMDID_NODE_CONFIGURE
579
580        flags = 0
581        mask  = 0
582        level = 0
583
584        if dsss_enable is not None:
585            mask += CMD_PARAM_NODE_CONFIG_FLAG_DSSS_ENABLE
586            if dsss_enable:
587                flags += CMD_PARAM_NODE_CONFIG_FLAG_DSSS_ENABLE
588
589        if beacon_mac_time_update is not None:
590            mask += CMD_PARAM_NODE_CONFIG_FLAG_BEACON_TIME_UPDATE
591            if beacon_mac_time_update:
592                flags += CMD_PARAM_NODE_CONFIG_FLAG_BEACON_TIME_UPDATE
593               
594        if portal_enable is not None:
595            mask += CMD_PARAM_NODE_CONFIG_FLAG_ETH_PORTAL
596            if portal_enable:
597                flags += CMD_PARAM_NODE_CONFIG_FLAG_ETH_PORTAL
598
599        if print_level is not None:
600            if (type(print_level) is str):
601                import wlan_exp.util as util
602                level = CMD_PARAM_NODE_CONFIG_SET_WLAN_EXP_PRINT_LEVEL + util.phy_modes[print_level]
603            else:
604                level = CMD_PARAM_NODE_CONFIG_SET_WLAN_EXP_PRINT_LEVEL + print_level           
605
606        self.add_args(flags)
607        self.add_args(mask)
608        self.add_args(level)
609
610    def process_resp(self, resp):
611        pass
612
613# End Class
614
615
616class NodeProcWLANMACAddr(message.Cmd):
617    """Command to get / set the WLAN MAC Address on the node.
618
619    Attributes:
620        cmd           -- Sub-command to send over the command.  Valid values are:
621                           CMD_PARAM_READ
622                           CMD_PARAM_WRITE
623        wlan_mac_addr -- 48-bit MAC address to write (optional)
624    """
625    def __init__(self, cmd, wlan_mac_address=None):
626        super(NodeProcWLANMACAddr, self).__init__()
627        self.command  = _CMD_GROUP_NODE + CMDID_NODE_WLAN_MAC_ADDR
628
629        if (cmd == CMD_PARAM_WRITE):
630            self.add_args(cmd)
631            _add_mac_address_to_cmd(self, wlan_mac_address)
632
633        elif (cmd == CMD_PARAM_READ):
634            self.add_args(cmd)
635
636        else:
637            msg = "Unsupported command: {0}".format(cmd)
638            raise ValueError(msg)
639
640
641    def process_resp(self, resp):
642        error_code    = CMD_PARAM_ERROR
643        error_msg     = "Could not get / set the WLAN MAC address on the node"
644        status_errors = { error_code : error_msg }
645
646        if resp.resp_is_valid(num_args=3, status_errors=status_errors, name='from the WLAN Mac Address command'):
647            args = resp.get_args()
648            addr = (2**32 * args[1]) + args[2]
649        else:
650            addr = None
651
652        return addr
653
654# End Class
655
656
657class NodeProcTime(message.Cmd):
658    """Command to get / set the time on the node.
659
660    Attributes:
661        cmd       -- Sub-command to send over the command.  Valid values are:
662                       CMD_PARAM_READ
663                       CMD_PARAM_WRITE
664                       TIME_ADD_TO_LOG
665        node_time -- Time to set the on the node
666        time_id   -- ID to use identify the time command in the log.
667    """
668    def __init__(self, cmd, node_time, time_id=None):
669        super(NodeProcTime, self).__init__()
670        self.command  = _CMD_GROUP_NODE + CMDID_NODE_TIME
671
672        # Read the time as int microseconds
673        if (cmd == CMD_PARAM_READ):
674            self.add_args(CMD_PARAM_READ)
675            self.add_args(CMD_PARAM_RSVD_TIME)             # Reads do not need a time_id
676            self.add_args(CMD_PARAM_RSVD_TIME)
677            self.add_args(CMD_PARAM_RSVD_TIME)
678            self.add_args(CMD_PARAM_RSVD_TIME)
679            self.add_args(CMD_PARAM_RSVD_TIME)
680
681        # Write the time / Add time to log
682        else:
683            import time
684
685            # Set the command
686            if (cmd == CMD_PARAM_WRITE):
687                self.add_args(CMD_PARAM_WRITE)
688            else:
689                self.add_args(CMD_PARAM_TIME_ADD_TO_LOG)
690                node_time = None
691
692            # By default set the time_id to a random number between [0, 2^32)
693            if time_id is None:
694                import random
695                time_id = 2**32 * random.random()
696
697            self.add_args(int(time_id))
698
699            # Add the time
700            if node_time is not None:                     
701                self.add_args((int(node_time) & 0xFFFFFFFF))
702                self.add_args(((int(node_time) >> 32) & 0xFFFFFFFF))
703            else:
704                self.add_args(CMD_PARAM_RSVD_TIME)
705                self.add_args(CMD_PARAM_RSVD_TIME)
706           
707            # Get the current time on the host   
708            # Convert to int microseconds
709            hosttime = time.time()   
710            time_factor    = 6         # Python time functions uses float seconds
711            time_to_send   = int(round(hosttime, time_factor) * (10**time_factor))
712            self.add_args((time_to_send & 0xFFFFFFFF))
713            self.add_args(((time_to_send >> 32) & 0xFFFFFFFF))
714
715
716
717    def process_resp(self, resp):
718        error_code    = CMD_PARAM_ERROR
719        error_msg     = "Could not get / set the time on the node"
720        status_errors = { error_code : error_msg }
721
722        if resp.resp_is_valid(num_args=5, status_errors=status_errors, name='from the Time command'):
723            args = resp.get_args()
724            mac_time = (2**32 * args[2]) + args[1]
725            sys_time = (2**32 * args[4]) + args[3]
726        else:
727            mac_time = 0
728            sys_time = 0
729
730        return (mac_time, sys_time)
731
732# End Class
733
734
735class NodeSetLowToHighFilter(message.Cmd):
736    """Command to set the low to high filter on the node.
737
738    Attributes:
739        mac_header -- MAC header filter.  Values can be:
740                        'MPDU_TO_ME' -- Pass any unicast-to-me or multicast data or
741                                        management packet
742                        'ALL_MPDU'   -- Pass any data or management packet (no address filter)
743                        'ALL'        -- Pass any packet (no type or address filters)
744        FCS        -- FCS status filter.  Values can be:
745                        'GOOD'       -- Pass only packets with good checksum result
746                        'ALL'        -- Pass packets with any checksum result
747    """
748    def __init__(self, cmd, mac_header=None, fcs=None):
749        super(NodeSetLowToHighFilter, self).__init__()
750        self.command  = _CMD_GROUP_NODE + CMDID_NODE_LOW_TO_HIGH_FILTER
751
752        self.add_args(cmd)
753
754        rx_filter = 0
755
756        if mac_header is None:
757            rx_filter += CMD_PARAM_RX_FILTER_HDR_NOCHANGE
758        else:
759            mac_header = str(mac_header)
760            mac_header.upper()
761
762            if   (mac_header == 'MPDU_TO_ME'):
763                rx_filter += CMD_PARAM_RX_FILTER_HDR_ADDR_MATCH_MPDU
764            elif (mac_header == 'ALL_MPDU'):
765                rx_filter += CMD_PARAM_RX_FILTER_HDR_ALL_MPDU
766            elif (mac_header == 'ALL'):
767                rx_filter += CMD_PARAM_RX_FILTER_HDR_ALL
768            else:
769                msg  = "WARNING:  Not a valid mac_header value.\n"
770                msg += "    Provided:  {0}\n".format(mac_header)
771                msg += "    Requires:  ['MPDU_TO_ME', 'ALL_MPDU', 'ALL']"
772                print(msg)
773                rx_filter += CMD_PARAM_RX_FILTER_HDR_NOCHANGE
774
775        if fcs is None:
776            rx_filter += CMD_PARAM_RX_FILTER_FCS_NOCHANGE
777        else:
778            fcs = str(fcs)
779            fcs.upper()
780
781            if   (fcs == 'GOOD'):
782                rx_filter += CMD_PARAM_RX_FILTER_FCS_GOOD
783            elif (fcs == 'ALL'):
784                rx_filter += CMD_PARAM_RX_FILTER_FCS_ALL
785            else:
786                msg  = "WARNING: Not a valid fcs value.\n"
787                msg += "    Provided:  {0}\n".format(fcs)
788                msg += "    Requires:  ['GOOD', 'ALL']"
789                print(msg)
790                rx_filter += CMD_PARAM_RX_FILTER_FCS_NOCHANGE
791
792        self.add_args(rx_filter)
793
794
795    def process_resp(self, resp):
796        pass
797
798# End Class
799
800class NodeProcChannel(message.Cmd):
801    """Command to get / set the channel of the node.
802   
803    Attributes:
804        cmd       -- Sub-command to send over the command.  Valid values are:
805                       CMD_PARAM_READ
806                       CMD_PARAM_WRITE
807        channel   -- 802.11 Channel for the node.  Should be a valid channel defined
808                       in wlan_exp.util wlan_channel table.
809    """
810    channel  = None
811
812    def __init__(self, cmd, channel=None):
813        super(NodeProcChannel, self).__init__()
814        self.command = _CMD_GROUP_NODE + CMDID_NODE_CHANNEL
815
816        self.add_args(cmd)
817       
818        if channel is not None:
819            self.channel = _get_channel_number(channel)
820
821        if self.channel is not None:
822            self.add_args(self.channel)
823        else:
824            self.add_args(CMD_PARAM_RSVD_CHANNEL)
825
826   
827    def process_resp(self, resp):
828        import wlan_exp.util as util
829        error_code    = CMD_PARAM_ERROR
830        error_msg     = "Could not get / set the channel on the node"
831        status_errors = { error_code : error_msg }
832       
833        if resp.resp_is_valid(num_args=2, status_errors=status_errors, name='from the Channel command'):
834            args = resp.get_args()
835            if self.channel is not None:
836                if (args[1] != self.channel):
837                    msg  = "WARNING: Channel mismatch.\n"
838                    msg += "    Tried to set channel to {0}\n".format(self.channel)
839                    msg += "    Actually set channel to {0}\n".format(args[1])
840                    print(msg)
841            return util.get_channel_info(args[1])
842        else:
843            return None
844
845# End Class
846
847class NodeProcRandomSeed(message.Cmd):
848    """Command to set the random seed of the node.
849
850    If a seed is not provided, then the seed is not updated.
851   
852    Attributes:
853        cmd       -- Sub-command to send over the command.  Valid values are:
854                       CMD_PARAM_READ   (Not supported)
855                       CMD_PARAM_WRITE
856        high_seed -- Random number generator seed for CPU high
857        low_seed  -- Random number generator seed for CPU low
858
859    import random
860    high_seed = random.randint(0, 0xFFFFFFFF)   
861    """
862    def __init__(self, cmd, high_seed=None, low_seed=None):
863        super(NodeProcRandomSeed, self).__init__()
864        self.command = _CMD_GROUP_NODE + CMDID_NODE_RANDOM_SEED
865
866        if (cmd == CMD_PARAM_READ):
867            raise AttributeError("Read not supported for NodeProcRandomSeed.")
868
869        self.add_args(CMD_PARAM_WRITE)
870
871        if high_seed is not None:
872            self.add_args(CMD_PARAM_RANDOM_SEED_VALID)
873            self.add_args(high_seed)
874        else:
875            self.add_args(CMD_PARAM_RANDOM_SEED_RSVD)
876            self.add_args(CMD_PARAM_RANDOM_SEED_RSVD)
877
878        if low_seed is not None:
879            self.add_args(CMD_PARAM_RANDOM_SEED_VALID)
880            self.add_args(high_seed)
881        else:
882            self.add_args(CMD_PARAM_RANDOM_SEED_RSVD)
883            self.add_args(CMD_PARAM_RANDOM_SEED_RSVD)
884
885    def process_resp(self, resp):
886        pass
887
888# End Class
889
890
891class NodeLowParam(message.Cmd):
892    """Command to set parameter in CPU Low
893
894    Attributes:
895        cmd          -- Sub-command to send over the command.  Valid values are:
896                          CMD_PARAM_READ   (Not supported)
897                          CMD_PARAM_WRITE
898        param_id     -- ID of parameter to modify
899        param_values -- Scalar or list of u32 values to write
900
901    """
902    def __init__(self, cmd, param_id, param_values=None):
903        super(NodeLowParam, self).__init__()
904
905        self.command    = _CMD_GROUP_NODE + CMDID_NODE_LOW_PARAM
906
907        if (cmd == CMD_PARAM_READ):
908            raise AttributeError("Read not supported for NodeLowParam.")
909
910        # Caluculate the size of the entire message to CPU Low [PARAM_ID, ARGS[]]
911        size = 1
912        if param_values is not None:
913            try:
914                size += len(param_values)
915            except TypeError:
916                pass
917
918        self.add_args(cmd)
919        self.add_args(size)
920        self.add_args(param_id)
921
922        if param_values is not None:
923            try:
924                for v in param_values:
925                    self.add_args(v)
926            except TypeError:
927                self.add_args(param_values)
928
929    def process_resp(self, resp):
930        """ Message format:
931                respArgs32[0]   Status
932        """
933        error_code_base         = CMD_PARAM_ERROR
934        error_code_cw_min       = CMD_PARAM_ERROR + CMD_PARAM_LOW_PARAM_DCF_CW_EXP_MIN
935        error_code_cw_max       = CMD_PARAM_ERROR + CMD_PARAM_LOW_PARAM_DCF_CW_EXP_MAX
936
937        status_errors = {error_code_base      : "Error setting low parameter",                         
938                         error_code_cw_min    : "Could not set minimum contention window",
939                         error_code_cw_max    : "Could not set maximum contention window"}
940
941        resp.resp_is_valid(num_args=1, status_errors=status_errors, name='from Low Param command')
942
943        return None
944
945# End Class
946
947
948class NodeProcTxPower(message.Cmd):
949    """Command to set the transmit power of the node.
950
951    Attributes:
952        cmd        -- Sub-command to send over the command.  Valid values are:                       
953                       CMD_PARAM_WRITE
954
955        frame_type -- Valid values are:
956                       CMD_PARAM_TXPARAM_DATA
957                       CMD_PARAM_TXPARAM_MGMT
958                       CMD_PARAM_TXPARAM_CTRL
959                       CMD_PARAM_TXPARAM_ALL
960                       
961        update_default_unicast -- Valid values are:
962                       0
963                       1
964                       
965        update_default_multicast -- Valid values are:
966                       0
967                       1
968
969        power      -- Tuple:
970                       Transmit power (in dBm)
971                       Maximum transmit power supported by node (in dBm)
972                       Minimum transmit power supported by node (in dBm)
973                       
974        addr_sel    -- Valid values are:
975                       CMD_PARAM_TXPARAM_ADDR_NONE
976                       CMD_PARAM_TXPARAM_ADDR_ALL
977                       CMD_PARAM_TXPARAM_ADDR_ALL_UNICAST
978                       CMD_PARAM_TXPARAM_ADDR_ALL_MULTICAST
979                       
980        device     -- 802.11 device for which the rate is being set.
981    """
982    max_tx_power = None
983    min_tx_power = None
984
985    def __init__(self, cmd, frame_type, update_default_unicast, update_default_multicast, power, addr_sel, device=None):
986        super(NodeProcTxPower, self).__init__()
987        self.command = _CMD_GROUP_NODE + CMDID_NODE_TX_POWER
988        mac_address  = None
989
990        self.add_args(cmd)
991
992        self.add_args(self.check_frame_type(frame_type))
993       
994        self.add_args(self.check_update_default(update_default_unicast))
995       
996        self.add_args(self.check_update_default(update_default_multicast))
997
998        # Get max/min power
999        self.max_tx_power = power[1]
1000        self.min_tx_power = power[2]       
1001        self.add_args(self.check_power(power))
1002       
1003        self.add_args(self.check_addr_sel(addr_sel))
1004
1005        if device is not None:
1006            mac_address = device.wlan_mac_address
1007
1008        _add_mac_address_to_cmd(self, mac_address)
1009
1010
1011    def check_frame_type(self, frame_type):
1012        """Check if frame_type value is valid."""
1013        return_type = None
1014        valid_types = [('CMD_PARAM_TXPARAM_DATA', CMD_PARAM_TXPARAM_DATA),
1015                       ('CMD_PARAM_TXPARAM_MGMT', CMD_PARAM_TXPARAM_MGMT),
1016                       ('CMD_PARAM_TXPARAM_CTRL', CMD_PARAM_TXPARAM_CTRL),
1017                       ('CMD_PARAM_TXPARAM_ALL',  CMD_PARAM_TXPARAM_ALL)]
1018
1019        for tmp_type in valid_types:
1020            if (frame_type == tmp_type[1]):
1021                return_type = frame_type
1022                break
1023
1024        if return_type is not None:
1025            return return_type
1026        else:
1027            msg  = "The type must be one of: ["
1028            for tmp_type in valid_types:
1029                msg += "{0} ".format(tmp_type[0])
1030            msg += "]"
1031            raise ValueError(msg)
1032           
1033    def check_update_default(self, update_default):
1034        """Check if update_default is valid."""
1035        return_type = None
1036       
1037        if (update_default == 0) or (update_default == 1):
1038            return_type = update_default
1039           
1040        if return_type is not None:
1041            return return_type
1042        else:
1043            msg  = "update_default must be 0 or 1"         
1044            raise ValueError(msg)
1045
1046    def check_addr_sel(self, addr_sel):
1047        """Check if addr_sel value is valid."""
1048        return_type = None
1049        valid_types = [('CMD_PARAM_TXPARAM_ADDR_NONE', CMD_PARAM_TXPARAM_ADDR_NONE),
1050                       ('CMD_PARAM_TXPARAM_ADDR_ALL', CMD_PARAM_TXPARAM_ADDR_ALL),
1051                       ('CMD_PARAM_TXPARAM_ADDR_ALL_UNICAST', CMD_PARAM_TXPARAM_ADDR_ALL_UNICAST),
1052                       ('CMD_PARAM_TXPARAM_ADDR_ALL_MULTICAST', CMD_PARAM_TXPARAM_ADDR_ALL_MULTICAST),
1053                       ('CMD_PARAM_TXPARAM_ADDR_SINGLE', CMD_PARAM_TXPARAM_ADDR_SINGLE)]
1054
1055        for tmp_type in valid_types:
1056            if (addr_sel == tmp_type[1]):
1057                return_type = addr_sel
1058                break
1059
1060        if return_type is not None:
1061            return return_type
1062        else:
1063            msg  = "The type must be one of: ["
1064            for tmp_type in valid_types:
1065                msg += "{0} ".format(tmp_type[0])
1066            msg += "]"
1067            raise ValueError(msg)
1068
1069    def check_power(self, power):
1070        """Return a valid power (in dBm) from the power tuple."""
1071        if power is None:
1072            raise ValueError("Must supply value to set Tx power.")
1073           
1074        ret_val = power[0]
1075           
1076        if (power[0] > power[1]):
1077            msg  = "WARNING:  Requested power too high.\n"
1078            msg += "    Adjusting transmit power from {0} to {1}".format(power[0], power[1])
1079            print(msg)
1080            ret_val = power[1]
1081
1082        if (power[0] < power[2]):
1083            msg  = "WARNING:  Requested power too low. \n"
1084            msg += "    Adjusting transmit power from {0} to {1}".format(power[0], power[2])
1085            print(msg)
1086            ret_val = power[2]
1087
1088        # Shift the value so that there are only positive integers over the wire
1089        return (ret_val - power[2])
1090
1091
1092    def process_resp(self, resp):
1093        error_code    = CMD_PARAM_ERROR
1094        error_msg     = "Could not get / set the transmit power on the node"
1095        status_errors = { error_code : error_msg }
1096
1097        if resp.resp_is_valid(num_args=1, status_errors=status_errors, name='from Tx power command'):
1098            return None
1099        else:
1100            return None
1101
1102# End Class
1103
1104
1105class NodeProcTxRate(message.Cmd):
1106    """Command to the transmit power of the node.
1107
1108    Attributes:
1109        cmd        -- Sub-command to send over the command.  Valid values are:                       
1110                       CMD_PARAM_WRITE
1111
1112        frame_type -- Valid values are:
1113                       CMD_PARAM_TXPARAM_DATA
1114                       CMD_PARAM_TXPARAM_MGMT
1115                       CMD_PARAM_TXPARAM_CTRL
1116                       CMD_PARAM_TXPARAM_ALL
1117                       
1118        update_default_unicast -- Valid values are:
1119                       0
1120                       1
1121                       
1122        update_default_multicast -- Valid values are:
1123                       0
1124                       1
1125
1126        rate      -- Tuple:
1127                       mcs
1128                       phy_mode
1129                       
1130        addr_sel    -- Valid values are:
1131                       CMD_PARAM_TXPARAM_ADDR_NONE
1132                       CMD_PARAM_TXPARAM_ADDR_ALL
1133                       CMD_PARAM_TXPARAM_ADDR_ALL_UNICAST
1134                       CMD_PARAM_TXPARAM_ADDR_ALL_MULTICAST
1135                       
1136        device     -- 802.11 device for which the rate is being set.
1137    """
1138    max_tx_power = None
1139    min_tx_power = None
1140
1141    def __init__(self, cmd, frame_type, update_default_unicast, update_default_multicast, rate, addr_sel, device=None):
1142        super(NodeProcTxRate, self).__init__()
1143        self.command = _CMD_GROUP_NODE + CMDID_NODE_TX_RATE
1144        mac_address  = None
1145
1146        self.add_args(cmd)
1147
1148        self.add_args(self.check_frame_type(frame_type))
1149       
1150        self.add_args(self.check_update_default(update_default_unicast))
1151       
1152        self.add_args(self.check_update_default(update_default_multicast))
1153
1154        if (rate is not None):
1155            mcs      = rate[0]
1156            phy_mode = rate[1]
1157
1158        if (mcs is not None):
1159            self.add_args(mcs)
1160        else:
1161            self.add_args(0)
1162
1163        if (phy_mode is not None):
1164            if (type(phy_mode) is str):
1165                import wlan_exp.util as util
1166                self.add_args(util.phy_modes[phy_mode])
1167            else:
1168                self.add_args(phy_mode)
1169        else:
1170            self.add_args(0)
1171       
1172        self.add_args(self.check_addr_sel(addr_sel))
1173
1174        if device is not None:
1175            mac_address = device.wlan_mac_address
1176
1177        _add_mac_address_to_cmd(self, mac_address)
1178
1179
1180    def check_frame_type(self, frame_type):
1181        """Check if frame_type value is valid."""
1182        return_type = None
1183        valid_types = [('CMD_PARAM_TXPARAM_DATA', CMD_PARAM_TXPARAM_DATA),
1184                       ('CMD_PARAM_TXPARAM_MGMT', CMD_PARAM_TXPARAM_MGMT),
1185                       ('CMD_PARAM_TXPARAM_CTRL', CMD_PARAM_TXPARAM_CTRL),
1186                       ('CMD_PARAM_TXPARAM_ALL',  CMD_PARAM_TXPARAM_ALL)]
1187
1188        for tmp_type in valid_types:
1189            if (frame_type == tmp_type[1]):
1190                return_type = frame_type
1191                break
1192
1193        if return_type is not None:
1194            return return_type
1195        else:
1196            msg  = "The type must be one of: ["
1197            for tmp_type in valid_types:
1198                msg += "{0} ".format(tmp_type[0])
1199            msg += "]"
1200            raise ValueError(msg)
1201           
1202    def check_update_default(self, update_default):
1203        """Check if update_default is valid."""
1204        return_type = None
1205       
1206        if (update_default == 0) or (update_default == 1):
1207            return_type = update_default
1208           
1209        if return_type is not None:
1210            return return_type
1211        else:
1212            msg  = "update_default must be 0 or 1"         
1213            raise ValueError(msg)
1214
1215    def check_addr_sel(self, addr_sel):
1216        """Check if addr_sel value is valid."""
1217        return_type = None
1218        valid_types = [('CMD_PARAM_TXPARAM_ADDR_NONE', CMD_PARAM_TXPARAM_ADDR_NONE),
1219                       ('CMD_PARAM_TXPARAM_ADDR_ALL', CMD_PARAM_TXPARAM_ADDR_ALL),
1220                       ('CMD_PARAM_TXPARAM_ADDR_ALL_UNICAST', CMD_PARAM_TXPARAM_ADDR_ALL_UNICAST),
1221                       ('CMD_PARAM_TXPARAM_ADDR_ALL_MULTICAST', CMD_PARAM_TXPARAM_ADDR_ALL_MULTICAST),
1222                       ('CMD_PARAM_TXPARAM_ADDR_SINGLE', CMD_PARAM_TXPARAM_ADDR_SINGLE)]
1223
1224        for tmp_type in valid_types:
1225            if (addr_sel == tmp_type[1]):
1226                return_type = addr_sel
1227                break
1228
1229        if return_type is not None:
1230            return return_type
1231        else:
1232            msg  = "The type must be one of: ["
1233            for tmp_type in valid_types:
1234                msg += "{0} ".format(tmp_type[0])
1235            msg += "]"
1236            raise ValueError(msg)
1237
1238    def process_resp(self, resp):
1239        error_code    = CMD_PARAM_ERROR
1240        error_msg     = "Could not get / set the transmit power on the node"
1241        status_errors = { error_code : error_msg }
1242
1243        if resp.resp_is_valid(num_args=1, status_errors=status_errors, name='from Tx power command'):
1244            return None
1245        else:
1246            return None
1247
1248# End Class
1249
1250
1251class NodeProcTxAntMode(message.Cmd):
1252    """Command to set the transmit antenna mode of the node.
1253
1254    Attributes:
1255        cmd        -- Sub-command to send over the command.  Valid values are:                       
1256                       CMD_PARAM_WRITE
1257
1258        frame_type -- Valid values are:
1259                       CMD_PARAM_TXPARAM_DATA
1260                       CMD_PARAM_TXPARAM_MGMT
1261                       CMD_PARAM_TXPARAM_CTRL
1262                       CMD_PARAM_TXPARAM_ALL
1263                       
1264        update_default_unicast -- Valid values are:
1265                       0
1266                       1
1267                       
1268        update_default_multicast -- Valid values are:
1269                       0
1270                       1
1271
1272        ant_mode    -- Transmit antenna mode for the node   
1273                       
1274        addr_sel    -- Valid values are:
1275                       CMD_PARAM_TXPARAM_ADDR_NONE
1276                       CMD_PARAM_TXPARAM_ADDR_ALL
1277                       CMD_PARAM_TXPARAM_ADDR_ALL_UNICAST
1278                       CMD_PARAM_TXPARAM_ADDR_ALL_MULTICAST
1279                       
1280        device     -- 802.11 device for which the rate is being set.
1281    """
1282    max_tx_power = None
1283    min_tx_power = None
1284
1285    def __init__(self, cmd, frame_type, update_default_unicast, update_default_multicast, ant_mode, addr_sel, device=None):
1286        super(NodeProcTxAntMode, self).__init__()
1287        self.command = _CMD_GROUP_NODE + CMDID_NODE_TX_ANT_MODE
1288        mac_address  = None
1289
1290        self.add_args(cmd)
1291
1292        self.add_args(self.check_frame_type(frame_type))
1293       
1294        self.add_args(self.check_update_default(update_default_unicast))
1295       
1296        self.add_args(self.check_update_default(update_default_multicast))
1297
1298        if ant_mode is not None:
1299            self.add_args(self.check_ant_mode(ant_mode))
1300        else:
1301            self.add_args(0)
1302       
1303        self.add_args(self.check_addr_sel(addr_sel))
1304
1305        if device is not None:
1306            mac_address = device.wlan_mac_address
1307
1308        _add_mac_address_to_cmd(self, mac_address)
1309
1310
1311    def check_frame_type(self, frame_type):
1312        """Check if frame_type value is valid."""
1313        return_type = None
1314        valid_types = [('CMD_PARAM_TXPARAM_DATA', CMD_PARAM_TXPARAM_DATA),
1315                       ('CMD_PARAM_TXPARAM_MGMT', CMD_PARAM_TXPARAM_MGMT),
1316                       ('CMD_PARAM_TXPARAM_CTRL', CMD_PARAM_TXPARAM_CTRL),
1317                       ('CMD_PARAM_TXPARAM_ALL',  CMD_PARAM_TXPARAM_ALL)]
1318
1319        for tmp_type in valid_types:
1320            if (frame_type == tmp_type[1]):
1321                return_type = frame_type
1322                break
1323
1324        if return_type is not None:
1325            return return_type
1326        else:
1327            msg  = "The type must be one of: ["
1328            for tmp_type in valid_types:
1329                msg += "{0} ".format(tmp_type[0])
1330            msg += "]"
1331            raise ValueError(msg)
1332           
1333    def check_update_default(self, update_default):
1334        """Check if update_default is valid."""
1335        return_type = None
1336       
1337        if (update_default == 0) or (update_default == 1):
1338            return_type = update_default
1339           
1340        if return_type is not None:
1341            return return_type
1342        else:
1343            msg  = "update_default must be 0 or 1"         
1344            raise ValueError(msg)
1345
1346    def check_addr_sel(self, addr_sel):
1347        """Check if addr_sel value is valid."""
1348        return_type = None
1349        valid_types = [('CMD_PARAM_TXPARAM_ADDR_NONE', CMD_PARAM_TXPARAM_ADDR_NONE),
1350                       ('CMD_PARAM_TXPARAM_ADDR_ALL', CMD_PARAM_TXPARAM_ADDR_ALL),
1351                       ('CMD_PARAM_TXPARAM_ADDR_ALL_UNICAST', CMD_PARAM_TXPARAM_ADDR_ALL_UNICAST),
1352                       ('CMD_PARAM_TXPARAM_ADDR_ALL_MULTICAST', CMD_PARAM_TXPARAM_ADDR_ALL_MULTICAST),
1353                       ('CMD_PARAM_TXPARAM_ADDR_SINGLE', CMD_PARAM_TXPARAM_ADDR_SINGLE)]
1354
1355        for tmp_type in valid_types:
1356            if (addr_sel == tmp_type[1]):
1357                return_type = addr_sel
1358                break
1359
1360        if return_type is not None:
1361            return return_type
1362        else:
1363            msg  = "The type must be one of: ["
1364            for tmp_type in valid_types:
1365                msg += "{0} ".format(tmp_type[0])
1366            msg += "]"
1367            raise ValueError(msg)
1368
1369    def check_ant_mode(self, ant_mode):
1370        """Check the antenna mode to see if it is valid."""
1371        import wlan_exp.util as util
1372        try:
1373            return util.wlan_tx_ant_modes[ant_mode]
1374        except KeyError:
1375            msg  = "The antenna mode must be one of:  "
1376            for k in sorted(util.wlan_tx_ant_modes.keys()):
1377                msg += "'{0}' ".format(k)
1378            raise ValueError(msg)
1379
1380
1381    def process_resp(self, resp):
1382        error_code    = CMD_PARAM_ERROR
1383        error_msg     = "Could not get / set the transmit power on the node"
1384        status_errors = { error_code : error_msg }
1385
1386        if resp.resp_is_valid(num_args=1, status_errors=status_errors, name='from Tx power command'):
1387            return None
1388        else:
1389            return None
1390
1391# End Class
1392
1393
1394class NodeProcRxAntMode(message.Cmd):
1395    """Command to get / set the receive antenna mode of the node.
1396
1397    Attributes:
1398        cmd       -- Sub-command to send over the command.  Valid values are:
1399                       CMD_PARAM_READ
1400                       CMD_PARAM_WRITE
1401        ant_mode  -- Receive antenna mode for the node.  Checking is
1402                     done both in the command and on the node.  The current
1403                     antenna mode will be returned by the node.
1404    """
1405    def __init__(self, cmd, ant_mode=None):
1406        super(NodeProcRxAntMode, self).__init__()
1407        self.command = _CMD_GROUP_NODE + CMDID_NODE_RX_ANT_MODE
1408
1409        self.add_args(cmd)
1410        if ant_mode is not None:
1411            self.add_args(self.check_ant_mode(ant_mode))
1412        else:
1413            self.add_args(CMD_PARAM_NODE_CONFIG_ALL)
1414
1415
1416    def check_ant_mode(self, ant_mode):
1417        """Check the antenna mode to see if it is valid."""
1418        import wlan_exp.util as util
1419        try:
1420            return util.wlan_rx_ant_modes[ant_mode]
1421        except KeyError:
1422            msg  = "The antenna mode must be one of:  "
1423            for k in sorted(util.wlan_rx_ant_modes.keys()):
1424                msg += "'{0}' ".format(k)
1425            raise ValueError(msg)
1426
1427    def process_resp(self, resp):
1428        import wlan_exp.util as util
1429
1430        error_code    = CMD_PARAM_ERROR
1431        error_msg     = "Could get / set receive antenna mode of the node"
1432        status_errors = { error_code : error_msg }
1433
1434        if resp.resp_is_valid(num_args=2, status_errors=status_errors, name='from Rx antenna mode command'):
1435            args = resp.get_args()
1436           
1437            ant_mode = [k for k, v in util.wlan_rx_ant_modes.items() if v == args[1]]
1438
1439            if ant_mode:
1440                return ant_mode[0]
1441            else:
1442                msg  = "Invalid antenna mode returned by node: {0}\n".format(args[1])
1443                raise ValueError(msg)           
1444        else:
1445            return CMD_PARAM_ERROR
1446
1447# End Class
1448
1449
1450
1451#--------------------------------------------
1452# Scan Commands
1453#--------------------------------------------
1454class NodeProcScanParam(message.Cmd):
1455    """Command to configure the scan parameters
1456
1457    Attributes:
1458        cmd                      -- Command for Process Scan Param:
1459                                        CMD_PARAM_WRITE
1460        time_per_channel         -- Time (in float sec) to spend on each channel (optional)
1461        num_probe_tx_per_channel -- Time (in float sec) to wait between each probe transmission (optional)
1462        channel_list             -- List of channels to scan (optional)
1463        ssid                     -- SSID (optional)
1464    """
1465    time_type   = None
1466
1467    def __init__(self, cmd, time_per_channel=None, num_probe_tx_per_channel=None, channel_list=None, ssid=None):
1468        super(NodeProcScanParam, self).__init__()
1469        self.command = _CMD_GROUP_NODE + CMDID_NODE_SCAN_PARAM
1470
1471        if (cmd == CMD_PARAM_WRITE):
1472            self.add_args(cmd)
1473
1474            # Add the time_per_channel to the command
1475            #_add_time_to_cmd32(self, time_per_channel)
1476
1477            if time_per_channel is not None:
1478                # Convert to int microseconds
1479                # time_per_channel is forced to float in set_scan_parameters
1480                time_factor    = 6         # Python time functions uses float seconds
1481                time_to_send   = int(round(time_per_channel, time_factor) * (10**time_factor))               
1482       
1483                if (time_to_send > 0xFFFFFFFF):
1484                    time_to_send = 0xFFFFFFFF
1485                    print("WARNING:  Time value (in microseconds) exceeds 32-bits. Saturating to 0xFFFFFFFF.\n")
1486       
1487                self.add_args((time_to_send & 0xFFFFFFFF))
1488            else:
1489                self.add_args(CMD_PARAM_RSVD_TIME)
1490
1491
1492            # Add num_probe_tx_per_channel to the command
1493            if num_probe_tx_per_channel is not None:
1494                self.add_args(num_probe_tx_per_channel)
1495            else:
1496                self.add_args(CMD_PARAM_RSVD)
1497
1498            # Format the channel list
1499            if channel_list is not None:
1500                self.add_args(len(channel_list))
1501
1502                for channel in channel_list:
1503                    self.add_channel(channel)
1504            else:
1505                self.add_args(CMD_PARAM_RSVD)
1506
1507            # Add SSID
1508            #     - SSID should be added last to the command so that the corresponding
1509            #       C code does not have to compute the index of the next argument
1510            #       after the SSID
1511            if ssid is not None:
1512                _add_ssid_to_cmd(self, ssid)
1513            else:
1514                self.add_args(CMD_PARAM_RSVD)               
1515               
1516        else:
1517            msg = "Unsupported command: {0}".format(cmd)
1518            raise ValueError(msg)
1519
1520
1521    def add_channel(self, channel):
1522        """Internal method to add a channel"""
1523        import wlan_exp.util as util
1524       
1525        if channel not in util.wlan_channels:
1526            msg  = "Unknown channel:  {0}".format(channel)
1527            raise ValueError(msg)
1528
1529        self.add_args(channel)
1530       
1531
1532    def process_resp(self, resp):
1533        error_code    = CMD_PARAM_ERROR
1534        error_msg     = "Could not set scan parameters."
1535        status_errors = { error_code : error_msg }
1536
1537        if (resp.resp_is_valid(num_args=1, status_errors=status_errors, name='from Scan parameter command')):
1538            return True
1539        else:
1540            return False
1541
1542# End Class
1543
1544
1545class NodeProcScan(message.Cmd):
1546    """Command to enable / disable active scan
1547
1548    Attributes:
1549        enable -- Enabling (True) or disabling (False) active scan
1550    """
1551    def __init__(self, enable=None):
1552        super(NodeProcScan, self).__init__()
1553        self.command = _CMD_GROUP_NODE + CMDID_NODE_SCAN
1554
1555        if enable is None:       
1556            self.add_args(CMD_PARAM_RSVD)
1557        else:
1558            if enable:
1559                self.add_args(CMD_PARAM_NODE_SCAN_ENABLE)
1560            else:
1561                self.add_args(CMD_PARAM_NODE_SCAN_DISABLE)
1562
1563    def process_resp(self, resp):
1564        ret_val = False
1565       
1566        if (resp.resp_is_valid(num_args=2, name='from Scan command')):
1567            args   = resp.get_args()
1568            status = args[0]
1569           
1570            # Check status
1571            if (status & CMD_PARAM_ERROR):
1572                msg  = "ERROR:  Could not start scan:\n"
1573                msg += "    Node BSS must be None.  Use conifgure_bss(None) before\n"
1574                msg += "    starting scan.\n"
1575                print(msg)
1576                ret_val = False
1577            else:
1578                if (args[1] == 1):
1579                    ret_val = True
1580                else:
1581                    ret_val = False
1582       
1583        return ret_val
1584
1585# End Class
1586
1587
1588
1589#--------------------------------------------
1590# Association Commands
1591#--------------------------------------------
1592class NodeConfigBSS(message.Cmd):
1593    """Command to configure the BSS on the node.
1594
1595    Command assumes all parameters are appropriately validated and processed.
1596
1597    Attributes:
1598        bssid (int):  48-bit ID of the BSS either as a integer; A value of
1599            None will remove current BSS on the node (similar to node.reset(bss=True));
1600            A value of False will not update the current bssid.
1601        ssid (str):  SSID string (Must be 32 characters or less); A value of
1602            None will not update the current SSID.
1603        channel (int): Channel on which the BSS operates; A value of None will
1604            not update the current channel.
1605        beacon_interval (int): Integer number of beacon Time Units in [1, 65534]
1606            (http://en.wikipedia.org/wiki/TU_(Time_Unit); a TU is 1024 microseconds);
1607            A value of None will disable beacons;  A value of False will not
1608            update the current beacon interval.
1609        dtim_period (int): Integer number of beacon intervals between DTIMs
1610        ht_capable (bool):  TBD.  A value of None will not update the current
1611            value of HT capable.
1612    """
1613    bssid           = None
1614    ssid            = None
1615    channel         = None
1616    beacon_interval = None
1617    dtim_period     = None
1618    ht_capable      = None
1619   
1620   
1621    def __init__(self,  bssid=False, ssid=None, channel=None, beacon_interval=False, dtim_period=None, ht_capable=None):
1622        super(NodeConfigBSS, self).__init__()
1623        self.command = _CMD_GROUP_NODE + CMDID_NODE_CONFIG_BSS
1624       
1625        import wlan_exp.info as info   
1626
1627        # Create BSS Config struct
1628        bss_config_update = info.BSSConfigUpdate(bssid=bssid, ssid=ssid, channel=channel, 
1629                                    beacon_interval=beacon_interval, 
1630                                    dtim_period=dtim_period,
1631                                    ht_capable=ht_capable)
1632
1633        # Set local variables for error messages
1634        self.bssid           = bssid
1635        self.ssid            = ssid
1636        self.channel         = channel
1637        self.beacon_interval = beacon_interval
1638        self.dtim_period     = dtim_period
1639        self.ht_capable      = ht_capable
1640
1641        # Convert BSSConfigUpdate() to bytes for transfer over-the-wire
1642        cmd_payload = bytearray(bss_config_update.serialize())
1643
1644        # Add the raw struct bytes to the command
1645        self.set_payload(cmd_payload)
1646
1647    def process_resp(self, resp):
1648        # Check number of arguments
1649        if (resp.resp_is_valid(num_args=1, name='from config BSS command')):
1650            # All error checking is done by the individual implementations
1651            return resp.get_args()
1652        else:
1653            return False
1654
1655# End Class
1656
1657
1658class NodeDisassociate(message.Cmd):
1659    """Command to remove associations from the association table."""
1660    description = None
1661
1662    def __init__(self, device=None):
1663        super(NodeDisassociate, self).__init__()
1664        self.command = _CMD_GROUP_NODE + CMDID_NODE_DISASSOCIATE
1665
1666        if device is not None:
1667            self.description = device.description
1668            mac_address      = device.wlan_mac_address
1669        else:
1670            self.description = "All nodes"
1671            mac_address      = CMD_PARAM_GET_ALL_ASSOCIATED
1672
1673        _add_mac_address_to_cmd(self, mac_address)
1674
1675
1676    def process_resp(self, resp):
1677        error_code    = CMD_PARAM_ERROR
1678        error_msg     = "Could not disassociate from {0}.".format(self.description)
1679        status_errors = { error_code : error_msg }
1680
1681        resp.resp_is_valid(num_args=1, status_errors=status_errors,
1682                           name='from Disassociate command')
1683
1684# End Class
1685
1686
1687class NodeGetBSSMembers(message.BufferCmd):
1688    """Command to get BSS Members."""
1689    def __init__(self, node=None):
1690        super(NodeGetBSSMembers, self).__init__()
1691        self.command = _CMD_GROUP_NODE + CMDID_GET_BSS_MEMBERS
1692        self.timestamp_in_hdr = True
1693
1694        mac_address = CMD_PARAM_GET_ALL_ASSOCIATED
1695
1696        _add_mac_address_to_cmd(self, mac_address)
1697
1698
1699    def process_resp(self, resp):
1700        # Contains a Buffer of all station infos.  Need to convert to a list
1701        #   of StationInfo()
1702        import wlan_exp.info as info
1703
1704        index   = 0
1705        data    = resp.get_bytes()
1706        ret_val = info.deserialize_info_buffer(data[index:], "StationInfo()")
1707
1708        # Copy the buffer's retrieval timestamp (extracted from the list_retrieval_header)
1709        #  into each station_info dictionary
1710        for si in ret_val:
1711            si['retrieval_timestamp'] = resp.retrieval_timestamp
1712
1713        return ret_val
1714
1715# End Class
1716
1717class NodeGetStationInfoList(message.BufferCmd):
1718    """Command to get list of all Station Infos."""
1719    def __init__(self, mac_addr=None):
1720        super(NodeGetStationInfoList, self).__init__()
1721        self.command = _CMD_GROUP_NODE + CMDID_GET_STATION_INFO_LIST
1722        self.timestamp_in_hdr = True
1723
1724        if mac_addr:
1725            _add_mac_address_to_cmd(self, mac_addr)
1726        else:
1727            _add_mac_address_to_cmd(self, CMD_PARAM_GET_ALL_LIST_ENTRIES)
1728
1729
1730    def process_resp(self, resp):
1731        # Contains a Buffer of all station infos.  Need to convert to a list
1732        #   of StationInfo()
1733        import wlan_exp.info as info
1734
1735        index   = 0
1736        data    = resp.get_bytes()
1737        ret_val = info.deserialize_info_buffer(data[index:], "StationInfo()")
1738
1739        # Copy the buffer's retrieval timestamp (extracted from the list_retrieval_header)
1740        #  into each station_info dictionary
1741        for si in ret_val:
1742            si['retrieval_timestamp'] = resp.retrieval_timestamp
1743
1744        return ret_val
1745
1746# End Class
1747
1748class NodeGetNetworkInfo(message.BufferCmd):
1749    """Command to get the Network info for a given BSS ID."""
1750    def __init__(self, bssid=None):
1751        super(NodeGetNetworkInfo, self).__init__()
1752        self.command = _CMD_GROUP_NODE + CMDID_GET_BSS_INFO
1753        self.timestamp_in_hdr = True
1754
1755        if bssid is None:
1756            self.add_args(CMD_PARAM_RSVD)
1757            self.add_args(CMD_PARAM_RSVD)
1758        else:
1759            if type(bssid) is str:
1760                bssid = CMD_PARAM_GET_ALL_ASSOCIATED      # If BSSID is a string, then get all bss info
1761   
1762            _add_mac_address_to_cmd(self, bssid)
1763
1764
1765    def process_resp(self, resp):
1766        # Contains a Buffer of all Network infos.  Need to convert to a list of NetworkInfo()
1767        import wlan_exp.info as info
1768
1769        index   = 0
1770        data    = resp.get_bytes()
1771        ret_val = info.deserialize_info_buffer(data[index:], "NetworkInfo()")
1772
1773        # Copy the buffer's retrieval timestamp (extracted from the list_retrieval_header)
1774        #  into each network_info dictionary
1775        for ni in ret_val:
1776            ni['retrieval_timestamp'] = resp.retrieval_timestamp
1777
1778        return ret_val
1779
1780# End Class
1781
1782
1783
1784#--------------------------------------------
1785# Queue Commands
1786#--------------------------------------------
1787class PurgeAllTxQueues(message.Cmd):
1788    """Command to purge all data transmit queues on the node."""
1789    def __init__(self):
1790        super(PurgeAllTxQueues, self).__init__()
1791        self.command = _CMD_GROUP_NODE + CMDID_PURGE_ALL_TX_QUEUES
1792
1793    def process_resp(self, resp):
1794        pass
1795
1796# End Class
1797
1798
1799
1800#--------------------------------------------
1801# AP Specific Commands
1802#--------------------------------------------
1803class NodeAPConfigure(message.Cmd):
1804    """Command to configure the AP.
1805
1806    Attributes (default state on the node is in CAPS):
1807        dtim_multicast_buffering   -- Enable DTIM multicast buffering (TRUE/False)
1808    """
1809    def __init__(self, dtim_multicast_buffering=None):
1810        super(NodeAPConfigure, self).__init__()
1811        self.command = _CMD_GROUP_NODE + CMDID_NODE_AP_CONFIG
1812
1813        flags = 0
1814        mask  = 0
1815
1816        if dtim_multicast_buffering is not None:
1817            mask += CMD_PARAM_AP_CONFIG_FLAG_DTIM_MULTICAST_BUFFER
1818            if dtim_multicast_buffering:
1819                flags += CMD_PARAM_AP_CONFIG_FLAG_DTIM_MULTICAST_BUFFER
1820
1821        self.add_args(flags)
1822        self.add_args(mask)
1823
1824    def process_resp(self, resp):
1825        pass
1826
1827# End Class
1828
1829
1830class NodeAPAddAssociation(message.Cmd):
1831    """Command to add station to the association table on the AP.
1832
1833    Attributes:
1834        device        -- Device to add to the association table
1835        disable_timeout -- Disable the AP's inactivity check for this STA
1836    """
1837    description = None
1838
1839    def __init__(self, device, disable_timeout=None):
1840        super(NodeAPAddAssociation, self).__init__()
1841        self.command = _CMD_GROUP_NODE + CMDID_NODE_ADD_ASSOCIATION
1842
1843        flags = 0
1844        mask  = 0
1845
1846        if disable_timeout is not None:
1847            mask += CMD_PARAM_ADD_ASSOCIATION_DISABLE_INACTIVITY_TIMEOUT
1848            if disable_timeout:
1849                flags += CMD_PARAM_ADD_ASSOCIATION_DISABLE_INACTIVITY_TIMEOUT
1850
1851        # Set the station_info ht_capable flag based on the device's ht_capable attribute
1852        mask += CMD_PARAM_ADD_ASSOCIATION_HT_CAPABLE_STA
1853        if device.ht_capable:
1854          flags += CMD_PARAM_ADD_ASSOCIATION_HT_CAPABLE_STA
1855
1856        self.add_args(flags)
1857        self.add_args(mask)
1858
1859        self.description = device.description
1860        mac_address      = device.wlan_mac_address
1861
1862        _add_mac_address_to_cmd(self, mac_address)
1863
1864
1865    def process_resp(self, resp):
1866        error_code    = CMD_PARAM_ERROR
1867        error_msg     = "Could not add {0} to AP association table. ".format(self.description)
1868       
1869        args = resp.get_args()
1870        if (len(args) == 2):
1871            if(args[1] == ADD_ASSOCIATION_ERROR_TOO_MANY_ASSOC):
1872                error_msg += "AP has too many associations."
1873            elif(args[1] == ADD_ASSOCIATION_ERROR_MEMORY):
1874                error_msg += "AP has insufficient memory."
1875               
1876        status_errors = { error_code : error_msg }
1877
1878        if (resp.resp_is_valid(num_args=2, status_errors=status_errors,
1879                               name='from AP associate command')):
1880            args = resp.get_args()
1881            return args[1]
1882        else:
1883            return CMD_PARAM_ERROR
1884
1885# End Class
1886
1887
1888class NodeAPSetAuthAddrFilter(message.Cmd):
1889    """Command to set the authentication address filter on the node.
1890
1891    Attributes:
1892        allow  -- List of (address, mask) tuples that will be used to filter addresses
1893                  on the node.
1894
1895    For the mask, bits that are 0 are treated as "any" and bits that are 1 are
1896    treated as "must equal".  For the address, locations of one bits in the mask
1897    must match the incoming addresses to pass the filter.
1898    """
1899    def __init__(self, allow):
1900        super(NodeAPSetAuthAddrFilter, self).__init__()
1901        self.command = _CMD_GROUP_NODE + CMDID_NODE_AP_SET_AUTHENTICATION_ADDR_FILTER
1902
1903        length = len(allow)
1904
1905        if (length > 50):
1906            msg  = "Currently, the wlan_exp framework does not support more than "
1907            msg += "50 address ranges in the association address filter."
1908            raise AttributeError(msg)
1909
1910        self.add_args(CMD_PARAM_WRITE)
1911        self.add_args(length)
1912
1913        for addr_range in allow:
1914            self.add_args(((addr_range[0] >> 32) & 0xFFFF))
1915            self.add_args((addr_range[0] & 0xFFFFFFFF))
1916            self.add_args(((addr_range[1] >> 32) & 0xFFFF))
1917            self.add_args((addr_range[1] & 0xFFFFFFFF))
1918
1919
1920    def process_resp(self, resp):
1921        error_code    = CMD_PARAM_ERROR
1922        error_msg     = "Could not set authentication address filter on the node."
1923        status_errors = { error_code : error_msg }
1924
1925        resp.resp_is_valid(num_args=1, status_errors=status_errors,
1926                           name='from AP set association address filter command')
1927
1928# End Class
1929
1930
1931
1932#--------------------------------------------
1933# STA Specific Commands
1934#--------------------------------------------
1935class NodeSTASetAID(message.Cmd):
1936    """Command to get the AID of the node"""
1937    def __init__(self, aid):
1938        super(NodeSTASetAID, self).__init__()
1939        self.command = _CMD_GROUP_NODE + CMDID_NODE_STA_SET_AID
1940       
1941        if ((aid < 1) or (aid > 255)):
1942            raise AttributeError("AID value must be in [1, 255].")
1943       
1944        self.add_args(aid)
1945
1946
1947    def process_resp(self, resp):
1948        error_code    = CMD_PARAM_ERROR
1949        error_msg     = "Could not set the AID of node."
1950        status_errors = { error_code : error_msg }
1951
1952        if (resp.resp_is_valid(num_args=1, status_errors=status_errors, name='from set AID command')):
1953            return True
1954        else:
1955            return False
1956
1957# End Class
1958
1959
1960class NodeSTAJoin(message.Cmd):
1961    """Command to join a given BSS. The resulting behavior of the STA depends on which arguments
1962    are proived.
1963
1964    If only an SSID is provided the STA will start its scanning state machine to search for a BSS
1965    with a matching SSID and will attempt to join the first matching network.
1966
1967    If BSSID, SSID and channel are all provided the STA will immediately attempt joining the specified
1968    BSS without performing a scan.
1969
1970    Attributes:
1971        ssid     -- SSID of BSS to join.  If value is None, then this will
1972                    stop the join process
1973        bssid    -- (optional) BSSID of the BSS to join
1974        channel  -- (optional) Channel of BSS to join
1975    """
1976    def __init__(self, ssid, bssid=None, channel=None):
1977        super(NodeSTAJoin, self).__init__()
1978        self.command = _CMD_GROUP_NODE + CMDID_NODE_STA_JOIN
1979
1980        # Add BSSID
1981        #     - Command assumes that this is a valid bssid
1982        if bssid is None:
1983            bssid = None
1984        else:
1985            # Check that a channel is specified
1986            if channel is None:
1987                raise AttributeError("Join must specify both BSSID and channel if either BSSID or channel is specified.")
1988           
1989        _add_mac_address_to_cmd(self, bssid)
1990
1991        # Add Channel
1992        #     - Command assumes that this is a valid channel
1993        if channel is None:
1994            channel = 0
1995        else:
1996            # Check that a bssid is specified
1997            if bssid is None:
1998                raise AttributeError("Join must specify both BSSID and channel if either BSSID or channel is specified.")
1999
2000        self.add_args(channel)
2001
2002        # Add SSID
2003        #     - SSID should be added last to the command so that the corresponding
2004        #       C code does not have to compute the index of the next argument
2005        #       after the SSID
2006        if ssid is None:
2007            self.add_args(CMD_PARAM_RSVD)
2008        else:
2009            _add_ssid_to_cmd(self, ssid)
2010
2011
2012    def process_resp(self, resp):
2013        error_code    = CMD_PARAM_ERROR
2014        error_msg     = "Could not join the network."
2015        status_errors = { error_code : error_msg }
2016
2017        if (resp.resp_is_valid(num_args=1, status_errors=status_errors, name='from Join command')):
2018            return True
2019        else:
2020            return False
2021
2022# End Class
2023
2024
2025class NodeSTAJoinStatus(message.Cmd):
2026    """Command to get the join status of the node"""
2027    def __init__(self):
2028        super(NodeSTAJoinStatus, self).__init__()
2029        self.command = _CMD_GROUP_NODE + CMDID_NODE_STA_JOIN_STATUS
2030
2031
2032    def process_resp(self, resp):
2033        error_code    = CMD_PARAM_ERROR
2034        error_msg     = "Could not get join status of node."
2035        status_errors = { error_code : error_msg }
2036
2037        if (resp.resp_is_valid(num_args=2, status_errors=status_errors, name='from Join Status command')):
2038            args = resp.get_args()
2039            if (args[1] == 1):
2040                return True
2041            else:
2042                return False
2043        else:
2044            return False
2045
2046# End Class
2047
2048
2049
2050#--------------------------------------------
2051# IBSS Specific Commands
2052#--------------------------------------------
2053
2054
2055
2056#--------------------------------------------
2057# Memory Access Commands - For developer use only
2058#--------------------------------------------
2059class NodeMemAccess(message.Cmd):
2060    """Command to read/write memory in CPU High / CPU Low
2061
2062    Attributes:
2063        cmd       -- Sub-command to send over the command.  Valid values are:
2064                       CMD_PARAM_READ
2065                       CMD_PARAM_WRITE
2066        high      -- True for CPU_High access, False for CPU_Low
2067
2068        address   -- u32 memory address to read/write
2069
2070        values    -- When cmd==CMD_PARAM_WRITE, scalar or list of u32 values to write
2071                     When cmd==CMD_PARAM_READ, None
2072
2073        length    -- When cmd==CMD_PARAM_WRITE, None
2074                     When cmd==CMD_PARAM_READ, number of u32 values to read starting at address
2075    """
2076    _read_len = None
2077
2078    def __init__(self, cmd, high, address, values=None, length=None):
2079        super(NodeMemAccess, self).__init__()
2080        if (high):
2081            self.command = _CMD_GROUP_NODE + CMDID_DEV_MEM_HIGH
2082        else:
2083            self.command = _CMD_GROUP_NODE + CMDID_DEV_MEM_LOW
2084
2085        self.add_args(cmd)
2086        self.add_args(address)
2087        self.add_args(length)
2088
2089        if (cmd == CMD_PARAM_READ):
2090            self._read_len = length
2091
2092        elif (cmd == CMD_PARAM_WRITE):
2093            try:
2094                for v in values:
2095                    self.add_args(v)
2096            except TypeError:
2097                self.add_args(values)
2098
2099        else:
2100            raise Exception('ERROR: NodeMemAccess constructor arguments invalid')
2101
2102
2103    def process_resp(self, resp):
2104        error_code    = CMD_PARAM_ERROR
2105        error_msg     = "Memory access failed."
2106        status_errors = { error_code : error_msg }
2107       
2108        if (self._read_len is not None): 
2109            # Read command
2110            if (self.command == _CMD_GROUP_NODE + CMDID_DEV_MEM_HIGH):
2111                msg = "from CPU High Mem read command"
2112            else:
2113                msg = "from CPU Low Mem read command"
2114           
2115            if resp.resp_is_valid(num_args=(2 + self._read_len), status_errors=status_errors, name=msg):
2116                args = resp.get_args()
2117
2118                if (len(args) == 3):
2119                    return args[2]
2120                elif (len(args) > 3):
2121                    return args[2:]
2122                else:
2123                    raise Exception('ERROR: Invalid memory read response.  Num Args = {0}'.format(len(args)))
2124            else:
2125                return CMD_PARAM_ERROR
2126        else: 
2127            # Write command
2128            if (self.command == _CMD_GROUP_NODE + CMDID_DEV_MEM_HIGH):
2129                msg = "from CPU High Mem write command"
2130            else:
2131                msg = "from CPU Low Mem write command"
2132           
2133            if resp.resp_is_valid(num_args=1, status_errors=status_errors, name=msg):
2134                pass
2135            else:
2136                return CMD_PARAM_ERROR
2137
2138# End ClassNodeAPProcBeaconInterval
2139
2140
2141
2142#--------------------------------------------
2143# EEPROM Access Commands - For developer use only
2144#--------------------------------------------
2145class NodeEEPROMAccess(message.Cmd):
2146    """Command to read/write EEPROM
2147
2148    Attributes:
2149        cmd       -- Sub-command to send over the command.  Valid values are:
2150                       CMD_PARAM_READ
2151                       CMD_PARAM_WRITE
2152        address   -- u16 memory address to read/write
2153
2154        values    -- When cmd==CMD_PARAM_WRITE, scalar or list of u8 values to write
2155                     When cmd==CMD_PARAM_READ, None
2156
2157        length    -- When cmd==CMD_PARAM_WRITE, number of u8 values to write
2158                     When cmd==CMD_PARAM_READ, number of u8 values to read
2159                     
2160        on_board  -- True for "On Board" EEPROM access, False for "FMC" EEPROM access       
2161    """
2162    _read_len = None
2163
2164    def __init__(self, cmd, address, values=None, length=None, on_board=True):
2165        super(NodeEEPROMAccess, self).__init__()
2166        self.command = _CMD_GROUP_NODE + CMDID_DEV_EEPROM
2167       
2168        self.add_args(cmd)
2169        self.add_args(on_board)
2170        self.add_args(address)
2171        self.add_args(length)
2172
2173        if (cmd == CMD_PARAM_READ):
2174            self._read_len = length
2175
2176        elif (cmd == CMD_PARAM_WRITE):
2177            try:
2178                for v in values:
2179                    self.add_args(v)
2180            except TypeError:
2181                self.add_args(values)
2182
2183        else:
2184            raise Exception('ERROR: NodeEEPROMAccess constructor arguments invalid')
2185
2186
2187    def process_resp(self, resp):
2188        error_code    = CMD_PARAM_ERROR
2189        error_msg     = "Memory access failed."
2190        status_errors = { error_code : error_msg }
2191           
2192        if (self._read_len is not None):
2193            # Read command
2194            msg = "from EEPROM write command"
2195       
2196            if resp.resp_is_valid(num_args=(2 + self._read_len), status_errors=status_errors, name=msg):
2197                args = resp.get_args()
2198
2199                if (len(args) == 3):
2200                    return args[2]
2201                elif (len(args) > 3):
2202                    return args[2:]
2203                else:
2204                    raise Exception('ERROR: Invalid EEPROM read response:  Num Args = {0}'.format(len(args)))
2205            else:
2206                return CMD_PARAM_ERROR
2207        else: 
2208            # Write command
2209            msg = "from EEPROM write command"
2210       
2211            if resp.resp_is_valid(num_args=1, status_errors=status_errors, name=msg):
2212                pass
2213            else:
2214                return CMD_PARAM_ERROR
2215
2216# End Class
2217
2218
2219
2220#--------------------------------------------
2221# User Commands
2222#--------------------------------------------
2223class UserSendCmd(message.Cmd):
2224    """Command to send User Command to the node
2225
2226    Attributes:
2227        cmdid     -- User-defined Command ID
2228        args      -- Scalar or list of u32 arguments to write
2229    """
2230    def __init__(self, cmd_id, args=None):
2231        super(UserSendCmd, self).__init__()
2232
2233        self.command    = _CMD_GROUP_USER + cmd_id
2234
2235        if args is not None:
2236            try:
2237                for a in args:
2238                    self.add_args(a)
2239            except TypeError:
2240                self.add_args(args)
2241
2242
2243    def process_resp(self, resp):
2244        """ Message format:
2245                respArgs32[0]   Status
2246        """
2247        args = resp.get_args()
2248
2249        try:
2250            return args[0:]
2251        except:
2252            return None
2253
2254# End Class
2255
2256
2257
2258#--------------------------------------------
2259# Misc Helper methods
2260#--------------------------------------------
2261def _add_mac_address_to_cmd(cmd, mac_address):
2262    if mac_address is not None:
2263        if type(mac_address) is str:
2264            import wlan_exp.util as util
2265            mac_address = util.str_to_mac_addr(mac_address)
2266           
2267        cmd.add_args(((mac_address >> 32) & 0xFFFF))
2268        cmd.add_args((mac_address & 0xFFFFFFFF))
2269    else:
2270        cmd.add_args(CMD_PARAM_RSVD_MAC_ADDR)
2271        cmd.add_args(CMD_PARAM_RSVD_MAC_ADDR)
2272
2273# End def
2274
2275
2276def _add_ssid_to_cmd(cmd, ssid):
2277    """Internal method to add an ssid to the given command"""
2278    import struct
2279
2280    ssid_len = len(ssid)
2281
2282    if (ssid_len > CMD_PARAM_MAX_SSID_LEN):
2283        ssid_len = CMD_PARAM_MAX_SSID_LEN
2284        ssid     = ssid[:CMD_PARAM_MAX_SSID_LEN]
2285
2286        msg  = "WARNING:  Maximum SSID length is {0} ".format(CMD_PARAM_MAX_SSID_LEN)
2287        msg += "truncating to {0}.".format(ssid)
2288        print(msg)
2289
2290    cmd.add_args(ssid_len)
2291
2292    # Null-teriminate the string for C
2293    ssid    += "\0"
2294    ssid_buf = bytearray(ssid, 'UTF-8')
2295
2296    # Zero pad so that the ssid buffer is 32-bit aligned
2297    if ((len(ssid_buf) % 4) != 0):
2298        ssid_buf += bytearray(4 - (len(ssid_buf) % 4))
2299
2300    idx = 0
2301    while (idx < len(ssid_buf)):
2302        arg = struct.unpack_from('!I', ssid_buf[idx:idx+4])
2303        cmd.add_args(arg[0])
2304        idx += 4
2305
2306# End def
2307
2308def _get_ssid_from_resp(resp, status_errors):
2309    """Internal method to process an SSID from a response."""
2310    args       = resp.get_args()
2311    arg_length = len(args)
2312
2313    resp.resp_is_valid(num_args=arg_length, status_errors=status_errors,
2314                              name='while processing SSID')
2315
2316    # Actually check the number of arguments
2317    if(arg_length >= 2):
2318        length = args[1]
2319    else:
2320        raise Exception('ERROR: invalid response to process SSID - N_ARGS = {0}'.format(len(args)))
2321
2322    # Get the SSID from the response
2323    if (length > 0):
2324        import struct
2325        ssid_buffer = struct.pack('!%dI' % (arg_length - 2), *args[2:] )
2326        ssid_tuple  = struct.unpack_from('!%ds' % length, ssid_buffer)
2327
2328        # Python 3 vs 2 issue
2329        try:
2330            ssid    = str(ssid_tuple[0], encoding='UTF-8')
2331        except TypeError:
2332            ssid    = str(ssid_tuple[0])
2333    else:
2334        ssid        = ""
2335
2336    return ssid
2337
2338# End def
2339
2340def _get_channel_number(channel):
2341    """Internal method to get the channel number."""
2342    try:
2343        my_channel = channel['index']
2344    except (KeyError, TypeError):
2345        import wlan_exp.util as util
2346       
2347        tmp_chan = util.get_channel_info(channel)
2348       
2349        if tmp_chan is not None:
2350            my_channel = tmp_chan['channel']
2351        else:
2352            msg  = "Unknown channel:  {0}".format(channel)
2353            raise ValueError(msg)
2354
2355    return my_channel
2356
2357# End def
Note: See TracBrowser for help on using the repository browser.