source: ReferenceDesigns/w3_802.11/c/wlan_mac_low_framework/wlan_mac_low.c

Last change on this file was 6281, checked in by chunter, 4 weeks ago

added an explicit clear of WLAN_MAC_CTRL_MASK_CCA_IGNORE_PHY_CS and WLAN_MAC_CTRL_MASK_CCA_IGNORE_NAV bits in WLAN_MAC_REG_CONTROL so we don't have to rely on default state

File size: 57.6 KB
Line 
1/** @file wlan_mac_low.c
2 *  @brief Low-level WLAN MAC High Framework
3 *
4 *  This contains the low-level code for accessing the WLAN MAC Low Framework.
5 *
6 *  @copyright Copyright 2014-2018, Mango Communications. All rights reserved.
7 *          Distributed under the Mango Communications Reference Design License
8 *              See LICENSE.txt included in the design archive or
9 *              at http://mangocomm.com/802.11/license
10 *
11 *  This file is part of the Mango 802.11 Reference Design (https://mangocomm.com/802.11)
12 */
13/***************************** Include Files *********************************/
14
15// Xilinx / Standard library includes
16#include <xil_io.h>
17#include <xio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <xstatus.h>
21
22// WLAN includes
23#include "xparameters.h"
24#include "wlan_platform_common.h"
25#include "wlan_platform_low.h"
26#include "wlan_mac_mailbox_util.h"
27#include "wlan_mac_802_11_defs.h"
28#include "wlan_phy_util.h"
29#include "wlan_mac_low.h"
30#include "wlan_mac_common.h"
31#include "wlan_mac_pkt_buf_util.h"
32#include "wlan_platform_debug_hdr.h"
33
34// WLAN Exp includes
35#include "wlan_exp.h"
36
37
38/*************************** Constant Definitions ****************************/
39
40#define DBG_PRINT  0
41
42
43/*********************** Global Variable Definitions *************************/
44
45/*************************** Functions Prototypes ****************************/
46
47
48/*************************** Variable Definitions ****************************/
49static volatile phy_samp_rate_t gl_phy_samp_rate; ///< Current PHY sampling rate
50static volatile u32 mac_param_chan; ///< Current channel of the lower-level MAC
51static volatile u8 mac_param_band; ///< Current band of the lower-level MAC
52static volatile u8 mac_param_dsss_en; ///< Enable / Disable DSSS when possible
53static volatile s8 mac_param_ctrl_tx_pow; ///< Current transmit power (dBm) for control packets
54static volatile u32 mac_param_rx_filter; ///< Current filter applied to packet receptions
55static volatile u8 rx_pkt_buf; ///< Current receive buffer of the lower-level MAC
56
57static u32 cpu_low_status; ///< Status flags that are reported to upper-level MAC
58static u32 cpu_low_type; ///< wlan_exp CPU_LOW type that is reported to upper-level MAC
59static compilation_details_t cpu_low_compilation_details;
60
61// Common Platform Device Info
62platform_common_dev_info_t platform_common_dev_info;
63
64static wlan_ipc_msg_t ipc_msg_from_high; ///< Buffer for incoming IPC messages
65static u32 ipc_msg_from_high_payload[MAILBOX_MSG_MAX_NUM_WORDS]; ///< Buffer for payload of incoming IPC messages
66
67// Callback function pointers
68static function_ptr_t frame_rx_callback;
69static function_ptr_t beacon_txrx_config_callback;
70static function_ptr_t mcast_buffer_enable_callback;
71static function_ptr_t mactime_change_callback;
72static function_ptr_t sample_rate_change_callback;
73static function_ptr_t handle_tx_pkt_buf_ready;
74static function_ptr_t ipc_low_param_callback;
75
76// Unique transmit sequence number
77static volatile u64 unique_seq;
78
79// Constant LUTs for MCS
80static const u16 mcs_to_n_dbps_nonht_lut[] = {24, 36, 48, 72, 96, 144, 192, 216};
81static const u16 mcs_to_n_dbps_htmf_lut[] = {26, 52, 78, 104, 156, 208, 234, 260};
82
83/******************************** Functions **********************************/
84
85
86/*****************************************************************************/
87/**
88 * @brief Initialize MAC Low Framework
89 *
90 * This function initializes the MAC Low Framework by setting
91 * up the hardware and other subsystems in the framework.
92 *
93 * @param   type                - Lower-level MAC type
94 * @param   compilation_details - compilation_details_t struct from low-level application
95 * @return  int                 - Initialization status (0 = success)
96 */
97int wlan_mac_low_init(u32 type, compilation_details_t compilation_details){
98    u32 status;
99    rx_frame_info_t* rx_frame_info;
100    tx_frame_info_t* tx_frame_info;
101    u32 i;
102
103    /**********************************************************************************
104     * Initialize the low platform first - this must happen before the low application
105     *  attempts to use any hardware resources
106     **********************************************************************************/
107    status = wlan_platform_common_init();
108    if(status != 0) {
109        xil_printf("Error in wlan_platform_common_init()! Exiting\n");
110        return -1;
111    }
112    status = wlan_platform_low_init();
113
114    if(status != 0) {
115        xil_printf("Error in wlan_platform_low_init()! Exiting\n");
116        return -1;
117    }
118    /**********************************************************************************/
119
120    // Get the device info
121    platform_common_dev_info = wlan_platform_common_get_dev_info();
122
123    /**********************************************************************************
124     * Initialize the MAC and PHY cores - this must happen before the low application
125     *  attempts any wireless Tx/Rx operations
126     * These calls will reset the MAC and PHY cores, safely interrupting any ongoing
127     *  Tx/Rx events and clearing old MAC state that may remain from a previous boot
128     **********************************************************************************/
129    wlan_phy_init();
130
131    // Set a default physical CS threshold of -62dBm (18.3.10.6)
132    wlan_platform_enable_phy_cs(1);
133
134    wlan_mac_hw_init();
135
136
137    mac_param_dsss_en = 1;
138    mac_param_band = CHAN_BAND_24GHz;
139    mac_param_ctrl_tx_pow = 10;
140    cpu_low_status = 0;
141    cpu_low_type = type;
142    cpu_low_compilation_details = compilation_details;
143
144    unique_seq = 0;
145
146    //Set the TU Target to the max value
147    wlan_mac_set_tu_target(0xFFFFFFFFFFFFFFFFULL);
148
149    mac_param_rx_filter      = (RX_FILTER_FCS_ALL | RX_FILTER_HDR_ALL);
150
151    frame_rx_callback            = (function_ptr_t) wlan_null_callback;
152    ipc_low_param_callback       = (function_ptr_t) wlan_null_callback;
153    beacon_txrx_config_callback  = (function_ptr_t) wlan_null_callback;
154    mcast_buffer_enable_callback = (function_ptr_t) wlan_null_callback;
155    mactime_change_callback      = (function_ptr_t) wlan_null_callback;
156    sample_rate_change_callback  = (function_ptr_t) wlan_null_callback;
157    handle_tx_pkt_buf_ready      = (function_ptr_t) wlan_null_callback;
158
159    // Initialize mailbox
160    init_mailbox();
161
162    // Initialize packet buffers
163    init_pkt_buf();
164
165    // ***************************************************
166    // Initialize Transmit Packet Buffers
167    // ***************************************************
168    for(i = 0; i < NUM_TX_PKT_BUFS; i++){
169        tx_frame_info = (tx_frame_info_t*)CALC_PKT_BUF_ADDR(platform_common_dev_info.tx_pkt_buf_baseaddr, i);
170        switch(i){
171            case TX_PKT_BUF_MPDU_1:
172            case TX_PKT_BUF_MPDU_2:
173            case TX_PKT_BUF_MPDU_3:
174                switch(tx_frame_info->tx_pkt_buf_state){
175                    case TX_PKT_BUF_UNINITIALIZED:
176                    case TX_PKT_BUF_HIGH_CTRL:
177                        // CPU High will initialize
178                    break;
179                    case TX_PKT_BUF_READY:
180                    case TX_PKT_BUF_DONE:
181                        // CPU Low rebooted after finishing old Tx
182                        // No way to know if CPU Low sent TX_DONE(p) message - must reset p.state here
183                        //  Two potential races:
184                        //   -CPU High just rebooted and will also attempt setting p.state=TX_PKT_BUF_HIGH_CTRL
185                        //      No problem if both CPUs set state to TX_PKT_BUF_HIGH_CTRL
186                        //   -CPU High did not reboot and will attempt tx_done_handler(p)
187                        //      If p.state=TX_PKT_BUF_HIGH_CTRL when tx_done_handler(p) runs, CPU High will fail gracefully
188                        //      If p.state set to TX_PKT_BUF_HIGH_CTRL during tx_done_handler(p), CPU High will succeed normally
189                    case TX_PKT_BUF_LOW_CTRL:
190                        // CPU Low rebooted after CPU High submitted packet for Tx
191                        //  Release lock and reset state
192                        //  CPU High will find this TX_PKT_BUF_HIGH_CTRL buffer in next ping/pong update
193                    default:
194                        // Something went wrong if tx_pkt_buf_state is something
195                        // other than one of the tx_pkt_buf_state_t enums. We'll
196                        // attempt to resolve the problem by explicitly setting
197                        // the state.
198                        tx_frame_info->tx_pkt_buf_state = TX_PKT_BUF_HIGH_CTRL;
199                        unlock_tx_pkt_buf(i);
200                    break;
201                }
202            break;
203            case TX_PKT_BUF_BEACON:
204                unlock_tx_pkt_buf(i);
205            break;
206            case TX_PKT_BUF_RTS:
207            case TX_PKT_BUF_ACK_CTS:
208                force_lock_tx_pkt_buf(i);
209                tx_frame_info->tx_pkt_buf_state = TX_PKT_BUF_LOW_CTRL;
210            default:
211            break;
212        }
213    }
214    // ***************************************************
215    // Initialize Receive Packet Buffers
216    // ***************************************************
217    for(i = 0; i < NUM_RX_PKT_BUFS; i++){
218        rx_frame_info = (rx_frame_info_t*)CALC_PKT_BUF_ADDR(platform_common_dev_info.rx_pkt_buf_baseaddr, i);
219        switch(rx_frame_info->rx_pkt_buf_state){
220           case RX_PKT_BUF_UNINITIALIZED:
221           case RX_PKT_BUF_LOW_CTRL:
222           default:
223                // Something went wrong if rx_pkt_buf_state is something
224                // other than one of the rx_pkt_buf_state_t enums. We'll
225                // attempt to resolve the problem by explicitly setting
226                // the state.
227               force_lock_rx_pkt_buf(i);
228               rx_frame_info->rx_pkt_buf_state = RX_PKT_BUF_LOW_CTRL;
229           break;
230           case RX_PKT_BUF_HIGH_CTRL:
231           case RX_PKT_BUF_READY:
232               // CPU Low rebooted after submitting packet for de-encapsulation/logging
233               //  Will be handled by CPU High, either because CPU High is about
234               //  to de-encapsulate/log p or just rebooted and will clean up
235           break;
236        }
237    }
238
239    // Create IPC message to receive into
240    ipc_msg_from_high.payload_ptr = &(ipc_msg_from_high_payload[0]);
241
242    // Point the PHY to an empty Rx Pkt Buffer
243    wlan_mac_low_lock_empty_rx_pkt_buf();
244
245    // Move the PHY's starting address into the packet buffers by PHY_XX_PKT_BUF_PHY_HDR_OFFSET.
246    // This accounts for the metadata located at the front of every packet buffer (Xx_mpdu_info)
247    wlan_phy_rx_pkt_buf_phy_hdr_offset(PHY_RX_PKT_BUF_PHY_HDR_OFFSET);
248    wlan_phy_tx_pkt_buf_phy_hdr_offset(PHY_TX_PKT_BUF_PHY_HDR_OFFSET);
249
250
251    // Unpause MAC Tx Controllers
252    wlan_mac_pause_tx_ctrl_A(0);
253    wlan_mac_pause_tx_ctrl_C(0);
254    wlan_mac_pause_tx_ctrl_D(0);
255
256    // Initialize the HW info structure
257    init_mac_hw_info();
258
259    // Set the NAV ignore addr to this HW address
260    wlan_mac_low_set_nav_check_addr(get_mac_hw_addr_wlan());
261
262    return 0;
263}
264
265
266
267/*****************************************************************************/
268/**
269 * @brief Finish Initializing MAC Low Framework
270 *
271 * This function finishes the initialization and notifies the upper-level
272 * MAC that it has finished booting.
273 *
274 * @param   None
275 * @return  None
276 */
277void wlan_mac_low_init_finish(){
278
279    //Set the default PHY sample rate to 20 MSps
280    set_phy_samp_rate(PHY_20M);
281
282    // Update the CPU Low status
283    cpu_low_status |= CPU_STATUS_INITIALIZED;
284
285    wlan_mac_low_send_status(CPU_STATUS_REASON_BOOTED);
286
287}
288
289void wlan_mac_low_send_status(u8 cpu_status_reason){
290    wlan_ipc_msg_t ipc_msg_to_high;
291    u32 ipc_msg_to_high_payload[2+(sizeof(compilation_details_t)/sizeof(u32))];
292
293    // Send a message to other processor to say that this processor is initialized and ready
294    ipc_msg_to_high.msg_id            = IPC_MBOX_MSG_ID(IPC_MBOX_CPU_STATUS);
295    ipc_msg_to_high.arg0              = cpu_status_reason;
296    ipc_msg_to_high.num_payload_words = 2+(sizeof(compilation_details_t)/sizeof(u32));
297    ipc_msg_to_high.payload_ptr       = &(ipc_msg_to_high_payload[0]);
298    ipc_msg_to_high_payload[0]        = cpu_low_status;
299    ipc_msg_to_high_payload[1]        = cpu_low_type;
300    memcpy((u8*)&(ipc_msg_to_high_payload[2]), (u8*)&cpu_low_compilation_details, sizeof(compilation_details_t));
301
302    write_mailbox_msg(&ipc_msg_to_high);
303}
304
305
306/*****************************************************************************/
307/**
308 * @brief Set the PHY Sampling Rate
309 *
310 * This function should be called to switch the PHY sampling rate between
311 * 10/20/40 MSps.
312 *
313 * @param   phy_samp_rate_t phy_samp_rate
314 * @return  None
315 */
316void set_phy_samp_rate(phy_samp_rate_t phy_samp_rate) {
317
318    // Check sample rate argument
319    //     - Must be in [PHY_10M, PHY_20M, PHY_40M]
320    if (!((phy_samp_rate == PHY_10M) || (phy_samp_rate == PHY_20M) || (phy_samp_rate == PHY_40M))) {
321        return;
322    }
323
324    // Set global sample rate variable
325    gl_phy_samp_rate = phy_samp_rate;
326
327    // Call the platform's set_samp_rate first
328    wlan_platform_low_set_samp_rate(phy_samp_rate);
329
330    // DSSS Rx only supported at 20Msps
331    switch(phy_samp_rate){
332        case PHY_10M:
333        case PHY_40M:
334            // Always disable DSSS when PHY sample rate is not 20 MSps
335            wlan_phy_DSSS_rx_disable();
336        break;
337        case PHY_20M:
338            // Enable DSSS if global variable indicates it should be enabled and RF band allows it
339            if ((mac_param_dsss_en) && (mac_param_band == CHAN_BAND_24GHz)) {
340                wlan_phy_DSSS_rx_enable();
341            }
342        break;
343    }
344
345    // Call user callback so it can deal with any changes that need to happen due to a change in sampling rate
346    sample_rate_change_callback(gl_phy_samp_rate);
347
348}
349
350/*****************************************************************************/
351/**
352 * @brief Initialize the DCF Hardware Core
353 *
354 * This function initializes the DCF hardware core.
355 *
356 * @param   None
357 * @return  None
358 */
359void wlan_mac_hw_init(){
360
361    // Reset the MAC core - this clears any stale state in the Tx controllers, NAV counter, backoff counters, etc.
362    wlan_mac_reset(1);
363
364    // Enable blocking of the Rx PHY following good-FCS receptions and bad-FCS receptions
365    //     BLOCK_RX_ON_VALID_RXEND will block the Rx PHY on all RX_END events following valid RX_START events
366    //     This allows the wlan_exp framework to count and log bad FCS receptions
367    //
368    REG_SET_BITS(WLAN_MAC_REG_CONTROL, WLAN_MAC_CTRL_MASK_BLOCK_RX_ON_TX);
369
370    // Cleaar the ignore bits for CS
371    REG_CLEAR_BITS(WLAN_MAC_REG_CONTROL, (WLAN_MAC_CTRL_MASK_CCA_IGNORE_PHY_CS | WLAN_MAC_CTRL_MASK_CCA_IGNORE_NAV));
372
373    // Enable the NAV counter
374    REG_CLEAR_BITS(WLAN_MAC_REG_CONTROL, (WLAN_MAC_CTRL_MASK_DISABLE_NAV));
375
376    // Set sane defaults for MAC timing values. These will be overwritten by
377    // low-level applications that need to specify these times (e.g. the DCF)
378    wlan_mac_set_slot(9*10);
379    wlan_mac_set_DIFS(28*10);
380    wlan_mac_set_TxDIFS((28*10) - (platform_common_dev_info.tx_analog_latency_100ns));
381    wlan_mac_postTx_timer1_en(0);
382    wlan_mac_postRx_timer2_en(0);
383    wlan_mac_set_NAV_adj(0*10);
384    wlan_mac_set_EIFS(88*10);
385
386    // Set the TU target to 2^32-1 (max value) and hold TU_LATCH in reset
387    //  MAC Low application should re-enabled if needed
388    wlan_mac_set_tu_target(0xFFFFFFFF);
389    wlan_mac_reset_tu_target_latch(1);
390
391    // Clear any stale Rx events
392    wlan_mac_hw_clear_rx_started();
393
394    // Clear the reset
395    wlan_mac_reset(0);
396
397    return;
398}
399
400
401
402/*****************************************************************************/
403/**
404 * @brief Send Exception to Upper-Level MAC
405 *
406 * This function generates an IPC message for the upper-level MAC
407 * to tell it that something has gone wrong
408 *
409 * @param u32 reason
410 *  - reason code for the exception
411 * @return None
412 */
413inline void wlan_mac_low_send_exception(u32 reason){
414    wlan_ipc_msg_t ipc_msg_to_high;
415    u32 ipc_msg_to_high_payload[2];
416
417    // Update CPU Low status
418    cpu_low_status |= CPU_STATUS_EXCEPTION;
419
420    // Send an exception to CPU_HIGH along with a reason
421    ipc_msg_to_high.msg_id            = IPC_MBOX_MSG_ID(IPC_MBOX_CPU_STATUS);
422    ipc_msg_to_high.arg0              = (u8)CPU_STATUS_REASON_EXCEPTION;
423    ipc_msg_to_high.num_payload_words = 2;
424    ipc_msg_to_high.payload_ptr       = &(ipc_msg_to_high_payload[0]);
425    ipc_msg_to_high_payload[0]        = cpu_low_status;
426    ipc_msg_to_high_payload[1]        = reason;
427
428    write_mailbox_msg(&ipc_msg_to_high);
429
430    // Set the Hex display with the reason code and flash the LEDs
431    wlan_platform_low_userio_disp_status(USERIO_DISP_STATUS_CPU_ERROR,reason);
432}
433
434/*****************************************************************************/
435/**
436 * @brief Poll the Receive Frame Start
437 *
438 * This function will poll the hardware to see if the PHY is currently receiving or
439 * has finished receiving a packet.  This will then dispatch the current RX packet
440 * buffer and PHY details to the frame_rx_callback(). The callback is responsible for
441 * updating the current Rx packet buffer, typically required if the received packet
442 * is passed to CPU High for further processing.
443 *
444 * @param   None
445 * @return  u32              - Status (See MAC Polling defines in wlan_mac_low.h)
446 */
447inline u32 wlan_mac_low_poll_frame_rx(){
448    phy_rx_details_t phy_details;
449    u8 active_rx_ant;
450
451    volatile u32 mac_hw_status;
452    volatile u32 phy_hdr_params;
453
454    void* pkt_buf_addr = (void *) CALC_PKT_BUF_ADDR(platform_common_dev_info.rx_pkt_buf_baseaddr, rx_pkt_buf);
455    rx_frame_info_t* rx_frame_info = (rx_frame_info_t *) pkt_buf_addr;
456
457    int i = 0;
458
459    u32 return_status = 0;
460
461    // Read the MAC/PHY status
462    mac_hw_status = wlan_mac_get_status();
463
464    // Check if PHY has started a new reception
465    if(mac_hw_status & WLAN_MAC_STATUS_MASK_RX_PHY_STARTED) {
466
467        // Fill in rx_frame_info_t metadata
468        active_rx_ant = (wlan_phy_rx_get_active_rx_ant());
469
470        rx_frame_info->flags          = 0;
471        rx_frame_info->channel        = wlan_mac_low_get_active_channel();
472        rx_frame_info->phy_samp_rate  = (u8)wlan_mac_low_get_phy_samp_rate();
473        rx_frame_info->timestamp      = wlan_mac_low_get_rx_start_timestamp();
474        rx_frame_info->timestamp_frac = wlan_mac_low_get_rx_start_timestamp_frac();
475        rx_frame_info->ant_mode       = active_rx_ant;
476        rx_frame_info->rx_gain_index  = wlan_platform_get_rx_pkt_gain(active_rx_ant);
477        rx_frame_info->rx_power       = wlan_platform_get_rx_pkt_pwr(active_rx_ant);
478
479
480        // Check whether this is an OFDM or DSSS Rx
481        if(wlan_mac_get_rx_phy_sel() == WLAN_MAC_PHY_RX_PHY_HDR_PHY_SEL_DSSS) {
482            // DSSS Rx - PHY Rx length is already valid, other params unused for DSSS
483            phy_details.phy_mode = PHY_MODE_DSSS;
484
485            // Strip off extra pre-MAC-header bytes used in DSSS frames; this adjustment allows the next
486            //     function to treat OFDM and DSSS payloads the same
487            phy_details.length   = wlan_mac_get_rx_phy_length() - 5;
488            phy_details.mcs      = wlan_mac_get_rx_phy_mcs();
489
490            rx_frame_info->cfo_est        = 0;;
491            rx_frame_info->phy_details    = phy_details;
492
493
494            // Call the user callback to handle this Rx, capture return value
495            return_status |= FRAME_RX_RET_STATUS_RECEIVED_PKT;
496            return_status |= frame_rx_callback(rx_pkt_buf, &phy_details);
497
498        } else {
499            // OFDM Rx - must wait for valid PHY header
500            // Order of operations is critical here
501            //  1) Read status first
502            //  2) Read PHY header register second
503            //  3) Check for complete PHY header - continue if complete
504            //  4) Else check for early PHY reset - quit if reset
505
506            while (1) {
507                mac_hw_status = wlan_mac_get_status();
508                phy_hdr_params = wlan_mac_get_rx_phy_hdr_params();
509
510                if(i++ > 1000000) {
511                    xil_printf("Stuck in OFDM Rx PHY hdr check!\n");
512                    xil_printf(" MAC HW Status: 0x%08x\n", wlan_mac_get_status());
513                    xil_printf(" Rx Hdr Params: 0x%08x\n", wlan_mac_get_rx_phy_hdr_params());
514                    xil_printf(" Rx PHY Status: 0x%08x\n", Xil_In32(WLAN_RX_STATUS));
515                }
516
517                if(phy_hdr_params & WLAN_MAC_PHY_RX_PHY_HDR_READY) {
518                    // Rx PHY received enough bytes to decode PHY header
519                    //  Exit loop and check PHY header params
520                    break;
521                }
522                if((mac_hw_status & WLAN_MAC_STATUS_MASK_RX_PHY_ACTIVE) == 0) {
523                    // Rx PHY went idle before asserting RX_PHY_HDR_READY
524                    //  This only happens if the PHY is reset externally, possible if MAC starts a Tx during Rx
525                    //  Only option is to reset RX_STARTED and wait for next Rx
526
527                    // There is a 1-cycle race in this case, because RX_END asserts 1 cycle before RX_PHY_HDR_READY in the
528                    //  case of an invalid HT-SIG. The invalid HT-SIG generates an RX_END_ERROR which causes
529                    //  RX_END to assert. The simple workaround used below is to re-read phy_hdr_params one last time
530                    //  before concluding that the Rx PHY was reset unexpectedly
531                    break;
532                }
533            }
534
535            //  Re-read phy_hdr_params to resolve 1-cycle ambiguity in case of HT-SIG error
536            phy_hdr_params = wlan_mac_get_rx_phy_hdr_params();
537
538            // Decide how to handle this waveform
539            if(phy_hdr_params & WLAN_MAC_PHY_RX_PHY_HDR_READY) {
540                // Received PHY header - decide whether to call MAC callback
541                if( (phy_hdr_params & WLAN_MAC_PHY_RX_PHY_HDR_MASK_UNSUPPORTED) ||
542                    (wlan_mac_get_rx_phy_mode() > 0x2) ) {
543                    // Valid HT-SIG but unsupported waveform
544                    //  Rx PHY will hold ACTIVE until last samp but will not write payload
545                    //  HT-SIG fields (MCS, length) can be safely read here if desired if PHY mode == 0x2 (HTMF)
546                    // Or detected VHT waveform (not supported), did not attempt decoding VHT-SIG
547                    //xil_printf("Quitting - WLAN_MAC_PHY_RX_PHY_HDR_MASK_UNSUPPORTED (MCS = %d, Length = %d)", wlan_mac_get_rx_phy_mcs(), wlan_mac_get_rx_phy_length());
548
549                } else if(phy_hdr_params & WLAN_MAC_PHY_RX_PHY_HDR_MASK_RX_ERROR) {
550                    // Invalid HT-SIG (CRC error, invalid RESERVED or TAIL bits, invalid LENGTH, etc)
551                    //  Rx PHY has already released ACTIVE and will not write payload
552                    //  HT-SIG fields (MCS, length) should not be trusted in this case
553                    //xil_printf("Quitting - WLAN_MAC_PHY_RX_PHY_HDR_MASK_RX_ERROR");
554
555                } else {
556                    // NONHT waveform or HTMF waveform with supported HT-SIG - PHY will write payload
557                    //  Call lower MAC Rx callback
558                    //  Callback can safely return anytime (before or after RX_END)
559
560                    phy_details.phy_mode = wlan_mac_get_rx_phy_mode();
561                    phy_details.length   = wlan_mac_get_rx_phy_length();
562                    phy_details.mcs      = wlan_mac_get_rx_phy_mcs();
563
564                    rx_frame_info->cfo_est        = wlan_phy_rx_get_cfo_est();
565                    rx_frame_info->phy_details    = phy_details;
566
567                    return_status |= FRAME_RX_RET_STATUS_RECEIVED_PKT;
568                    return_status |= frame_rx_callback(rx_pkt_buf, &phy_details);
569                }
570            } else {
571                // PHY went idle before PHY_HDR_DONE, probably due to external reset
572                //  The Rx PHY can be reset from software (only used in wlan_phy_init()) or hardware.
573                //  The hardware reset is asserted by the MAC core during Tx. Asserting Tx during Rx is
574                //  impossible with the normal DCF code, as packet det is treated as a busy medium. With a
575                //  custom MAC implementation that allows Tx during Rx this code block will catch the
576                //  unexpected reset events.
577
578                // PHY header cannot be trusted in this case - do nothing and return
579
580            }//END if(PHY_HDR_DONE)
581        } //END if(OFDM Rx)
582
583        // Clear the MAC status register RX_STARTED bit unless the MAC application requested it stay asserted
584        //  By this point the framework and MAC are typically done with the current waveform, however it was handled.
585        //  The MAC application can request the RX_STARTED latch stay asserted by returning the FRAME_RX_RET_SKIP_RX_STARTED_RESET flag
586        //  In this case the application *must* clear the latch directly whenever it completes its Rx processing
587        if((return_status & FRAME_RX_RET_SKIP_RX_STARTED_RESET) == 0) wlan_mac_hw_clear_rx_started();
588
589    } //END if(PHY_RX_STARTED)
590
591    return return_status;
592}
593
594/*****************************************************************************/
595/**
596 * @brief Poll for IPC Receptions
597 *
598 * This function is a non-blocking poll for IPC receptions from the upper-level MAC.
599 *
600 * @param   None
601 * @return  int             - 0 when mailbox was empty, 1 when one message was processed
602 */
603inline int wlan_mac_low_poll_ipc_rx(){
604    // Poll mailbox read msg
605    if (read_mailbox_msg(&ipc_msg_from_high) == IPC_MBOX_SUCCESS) {
606        wlan_mac_low_process_ipc_msg(&ipc_msg_from_high);
607        return 1;
608    }
609    return 0;
610}
611
612
613/*****************************************************************************/
614/**
615 * @brief Process IPC Reception
616 *
617 * This is an internal function to the WLAN MAC Low framework to process
618 * received IPC messages and call the appropriate callback.
619 *
620 * @param   None
621 * @return  None
622 */
623void wlan_mac_low_process_ipc_msg(wlan_ipc_msg_t * msg){
624    wlan_ipc_msg_t ipc_msg_to_high;
625
626    switch(IPC_MBOX_MSG_ID_TO_MSG(msg->msg_id)){
627        //---------------------------------------------------------------------
628        case IPC_MBOX_TX_PKT_BUF_READY: {
629            u8 tx_pkt_buf;
630            tx_pkt_buf = msg->arg0;
631
632            if(tx_pkt_buf < NUM_TX_PKT_BUFS){
633
634                // Lock and change state of packet buffer
635                wlan_mac_low_lock_tx_pkt_buf(tx_pkt_buf);
636
637                // Message is an indication that a Tx Pkt Buf needs processing
638                handle_tx_pkt_buf_ready(tx_pkt_buf);
639                // TODO: check return status and inform CPU_HIGH of error?
640
641            }
642        }
643        break;
644
645        //---------------------------------------------------------------------
646        case IPC_MBOX_SET_MAC_TIME:
647            switch(msg->arg0){
648                default:
649                case 0:
650                    //Payload is an absolute MAC time that must be applied
651                    set_mac_time_usec( *(u64*)(msg->payload_ptr) );
652                    mactime_change_callback( (*(s64*)(msg->payload_ptr))-((s64)get_mac_time_usec()) );
653                break;
654                case 1:
655                    //Payload is a MAC time delta that must be applied
656                    apply_mac_time_delta_usec( *(s64*)(msg->payload_ptr));
657                    mactime_change_callback( *(s64*)(msg->payload_ptr));
658                break;
659            }
660        break;
661
662        //---------------------------------------------------------------------
663        case IPC_MBOX_TXRX_BEACON_CONFIG: {
664            beacon_txrx_config_callback(msg->payload_ptr);
665        }
666        break;
667
668        //---------------------------------------------------------------------
669        case IPC_MBOX_CPU_STATUS: {
670            if(msg->arg0 == (u8)CPU_STATUS_REASON_BOOTED){
671                // If CPU_HIGH just booted, we should re-inform it of our CPU status
672                wlan_mac_low_send_status(CPU_STATUS_REASON_RESPONSE);
673            }
674        }
675        break;
676
677        //---------------------------------------------------------------------
678        case IPC_MBOX_MEM_READ_WRITE: {
679            switch(msg->arg0){
680                case IPC_REG_WRITE_MODE: {
681
682                    u32* payload_to_write = (u32*)((u8*)ipc_msg_from_high_payload + sizeof(ipc_reg_read_write_t));
683
684                    // IMPORTANT: this memcpy assumes the payload provided by CPU high is ready as-is
685                    //     Any byte swapping (i.e. for payloads that arrive over Ethernet) *must* be performed
686                    //     before the payload is passed to this function
687
688                    // Implement memcpy with only 32-bit writes, avoids byte-select issues in Sysgen AXI slaves
689                    int word_idx;
690                    u32 num_words = ((ipc_reg_read_write_t*)ipc_msg_from_high_payload)->num_words;
691                    u32 start_addr = (((ipc_reg_read_write_t*)ipc_msg_from_high_payload)->baseaddr) & 0xFFFFFFFC; //force 2LSB=0
692
693                    for(word_idx = 0; word_idx < num_words; word_idx++) {
694                        // Increment target address by 4 bytes per iteration
695                        // payload_to_write already a u32 pointer, so use count-by-1 array index to access u32 words
696                        Xil_Out32((start_addr + word_idx*4), payload_to_write[word_idx]);
697                    }
698                }
699                break;
700
701                case IPC_REG_READ_MODE: {
702
703                    /*
704                    xil_printf("\nCPU Low Read:\n");
705                    xil_printf(" Addr: 0x%08x\n", (u32*)((ipc_reg_read_write_t*)ipc_msg_from_high_payload)->baseaddr);
706                    xil_printf(" N Wrds: %d\n", ((ipc_reg_read_write_t*)ipc_msg_from_high_payload)->num_words);
707
708                    xil_printf("Mem[0x%08x] = 0x%08x\n",
709                            (u32*)((ipc_reg_read_write_t*)ipc_msg_from_high_payload)->baseaddr,
710                            Xil_In32((u32*)((ipc_reg_read_write_t*)ipc_msg_from_high_payload)->baseaddr));
711                    */
712                    ipc_msg_to_high.msg_id            = IPC_MBOX_MSG_ID(IPC_MBOX_MEM_READ_WRITE);
713                    ipc_msg_to_high.num_payload_words = ((ipc_reg_read_write_t*)ipc_msg_from_high_payload)->num_words;
714                    ipc_msg_to_high.payload_ptr       = (u32*)((ipc_reg_read_write_t*)ipc_msg_from_high_payload)->baseaddr;
715
716                    write_mailbox_msg(&ipc_msg_to_high);
717                }
718                break;
719            }
720        }
721        break;
722
723        //---------------------------------------------------------------------------------------
724        case IPC_MBOX_LOW_PARAM: {
725            switch(msg->arg0){
726                case IPC_REG_WRITE_MODE: {
727                    switch(ipc_msg_from_high_payload[0]){
728
729                        case LOW_PARAM_PHY_SAMPLE_RATE: {
730                            set_phy_samp_rate(ipc_msg_from_high_payload[1]);
731                        }
732                        break;
733
734                        case LOW_PARAM_DSSS_PKT_DET_THRESH: {
735                            // Two u32 values:
736                            //  payload[1]: Correlation threshold
737                            //  payload[2]: Energy threshold
738
739                            xil_printf("DSSS Pkt Det Thresh: (%d, %d, %d)\n", ipc_msg_from_high_payload[1], ipc_msg_from_high_payload[2], ipc_msg_from_high_payload[3]);
740
741                            //wlan_phy_rx_pktDet_autoCorr_dsss_cfg(corr_thresh, energy_thresh)
742                            //  corr_thresh: UFix8_6
743                            //  energy_thresh: UFix10_0
744                            wlan_phy_rx_pktDet_autoCorr_dsss_cfg(ipc_msg_from_high_payload[1], ipc_msg_from_high_payload[2]);
745
746                            if(ipc_msg_from_high_payload[3] == 1){
747                                //Require packet detection before DSSS Rx
748                                REG_SET_BITS(WLAN_RX_REG_CFG, (WLAN_RX_REG_CFG_DSSS_RX_REQ_PKT_DET));
749                            } else {
750                                //Do not require packet detection before DSSS Rx
751                                REG_CLEAR_BITS(WLAN_RX_REG_CFG, (WLAN_RX_REG_CFG_DSSS_RX_REQ_PKT_DET));
752                            }
753                        }
754                        break;
755
756                        case LOW_PARAM_OFDM_PKT_DET_THRESH: {
757                            // Two u32 values:
758                            //  payload[1]: Correlation threshold
759                            //  payload[2]: Energy threshold
760                            //  payload[3]: Minimum duration
761
762                            xil_printf("OFDM Pkt Det Config: corr_thesh=%d, energy_thresh=%d, min_dur=%d, post_wait=%d, require_pkt_det = %d\n", ipc_msg_from_high_payload[1], ipc_msg_from_high_payload[2], ipc_msg_from_high_payload[3], ipc_msg_from_high_payload[4], ipc_msg_from_high_payload[5]);
763
764                            //wlan_phy_rx_pktDet_autoCorr_ofdm_cfg(corr_thresh, energy_thresh, min_dur, post_wait)
765                            //  corr_thresh: UFix8_7
766                            //  energy_thresh: UFix16_15
767                            //  min_dur: UFix4_0
768                            //  post-det-reset: UFix4_0
769                            wlan_phy_rx_pktDet_autoCorr_ofdm_cfg(ipc_msg_from_high_payload[1], ipc_msg_from_high_payload[2], ipc_msg_from_high_payload[3], ipc_msg_from_high_payload[4]);
770
771                            if(ipc_msg_from_high_payload[5] == 1){
772                                //Require packet detection before DSSS Rx
773                                REG_SET_BITS(WLAN_RX_REG_CFG, (WLAN_RX_REG_CFG_OFDM_RX_REQ_PKT_DET));
774                            } else {
775                                //Do not require packet detection before DSSS Rx
776                                REG_CLEAR_BITS(WLAN_RX_REG_CFG, (WLAN_RX_REG_CFG_OFDM_RX_REQ_PKT_DET));
777                            }
778                        }
779                        break;
780
781                        case LOW_PARAM_OFDM_RX_EN: {
782                            // One u32 value:
783                            // payload[1]: Zero=Disable OFDM Rx, Non-zero: Enable OFDM Rx
784
785                            xil_printf("OFDM_RX_EN: ");
786                            if(ipc_msg_from_high_payload[1]) {
787                                xil_printf("Enabling OFDM Rx\n");
788                                REG_CLEAR_BITS(WLAN_RX_REG_CFG, WLAN_RX_REG_CFG_DISABLE_OFDM_RX);
789                            } else {
790                                xil_printf("Disabling OFDM Rx\n");
791                                REG_SET_BITS(WLAN_RX_REG_CFG, WLAN_RX_REG_CFG_DISABLE_OFDM_RX);
792                            }
793                        }
794                        break;
795
796                        default: {
797                            // Low framework doesn't recognize this low param ID - call the application
798                            //  and platform handlers to process with this param write
799                            ipc_low_param_callback(IPC_REG_WRITE_MODE, ipc_msg_from_high_payload);
800                            wlan_platform_low_param_handler(IPC_REG_WRITE_MODE, ipc_msg_from_high_payload);
801                        }
802                        break;
803                    }
804                }
805                break;
806
807                case IPC_REG_READ_MODE: {
808                    // Read Mode is not supported
809                    //
810                    // NOTE:  This is due to the fact that IPC messages in CPU low can take an infinitely long amount of
811                    //     to return given that the sending and receiving of wireless data takes precedent.  Therefore,
812                    //     it is not good to try to return values from CPU low since there is no guarantee when the values
813                    //     will be available.
814                    //
815                    u32 ret_val = 0;
816
817                    ipc_msg_to_high.msg_id            = IPC_MBOX_MSG_ID(IPC_MBOX_LOW_PARAM);
818                    ipc_msg_to_high.num_payload_words = 0;
819                    ipc_msg_to_high.payload_ptr       = (u32 *)&ret_val;
820
821                    write_mailbox_msg(&ipc_msg_to_high);
822                }
823                break;
824            }
825        }
826        break;
827
828        //---------------------------------------------------------------------
829        case IPC_MBOX_MCAST_BUFFER_ENABLE: {
830            mcast_buffer_enable_callback(msg->arg0);
831        }
832        break;
833
834        //---------------------------------------------------------------------
835        case IPC_MBOX_CONFIG_CHANNEL: {
836            wlan_mac_low_set_radio_channel(ipc_msg_from_high_payload[0]);
837        }
838        break;
839
840        //---------------------------------------------------------------------
841        case IPC_MBOX_LOW_RANDOM_SEED: {
842            srand(ipc_msg_from_high_payload[0]);
843        }
844        break;
845
846        //---------------------------------------------------------------------
847        case IPC_MBOX_CONFIG_TX_CTRL_POW: {
848            mac_param_ctrl_tx_pow = (s8)ipc_msg_from_high_payload[0];
849        }
850        break;
851
852        //---------------------------------------------------------------------
853        case IPC_MBOX_CONFIG_RX_FILTER: {
854            u32 filter_mode_hi = (u32)ipc_msg_from_high_payload[0];
855            u32 filter_mode_lo = 0;
856
857            if((filter_mode_hi & RX_FILTER_FCS_MASK) == RX_FILTER_FCS_NOCHANGE){
858                filter_mode_lo |= (mac_param_rx_filter & RX_FILTER_FCS_MASK);
859            } else {
860                filter_mode_lo |= (filter_mode_hi & RX_FILTER_FCS_MASK);
861            }
862
863            if((filter_mode_hi & RX_FILTER_HDR_NOCHANGE) == RX_FILTER_HDR_NOCHANGE){
864                filter_mode_lo |= (mac_param_rx_filter & RX_FILTER_HDR_NOCHANGE);
865            } else {
866                filter_mode_lo |= (filter_mode_hi & RX_FILTER_HDR_NOCHANGE);
867            }
868
869            mac_param_rx_filter = filter_mode_lo;
870        }
871        break;
872
873        //---------------------------------------------------------------------
874        case IPC_MBOX_CONFIG_RX_ANT_MODE: {
875            wlan_rx_config_ant_mode(ipc_msg_from_high_payload[0]);
876        }
877        break;
878
879        //---------------------------------------------------------------------
880        case IPC_MBOX_CONFIG_DSSS_EN: {
881            if (ipc_msg_from_high_payload[0] == 1) {
882                // xil_printf("Enabling DSSS\n");
883                wlan_mac_low_DSSS_rx_enable();
884            } else {
885                // xil_printf("Disabling DSSS\n");
886                wlan_mac_low_DSSS_rx_disable();
887            }
888        }
889        break;
890
891        //---------------------------------------------------------------------
892        case IPC_MBOX_SET_RADIO_TX_POWER: {
893            s8 pwr = (s8)ipc_msg_from_high_payload[0];
894            wlan_platform_set_radio_tx_power(pwr);
895        }
896        break;
897
898
899    }
900}
901
902inline u32 wlan_mac_low_lock_tx_pkt_buf(u16 tx_pkt_buf){
903    u32 is_locked, owner;
904    u32 iter = 0;
905    tx_frame_info_t* tx_frame_info;
906
907    tx_frame_info = (tx_frame_info_t*)CALC_PKT_BUF_ADDR(platform_common_dev_info.tx_pkt_buf_baseaddr, tx_pkt_buf);
908
909    if( tx_frame_info->flags & TX_FRAME_INFO_FLAGS_WAIT_FOR_LOCK ){
910        // This packet buffer has been flagged to ensure that CPU_LOW will wait until a mutex lock
911        // is achieved before proceeding (as opposed to aborting and raising an error). The implicit
912        // contract in this flag is that CPU_HIGH will only briefly lock the packet buffer while updating
913        // its contents.
914        if(tx_frame_info->tx_pkt_buf_state == TX_PKT_BUF_DONE){
915            return PREPARE_FRAME_TRANSMIT_ERROR_UNEXPECTED_PKT_BUF_STATE;
916        }
917
918        while(lock_tx_pkt_buf(tx_pkt_buf) != PKT_BUF_MUTEX_SUCCESS){
919            // This frame was flagged with
920            if(iter > 1000000) {
921                xil_printf("ERROR (wlan_mac_low_lock_tx_pkt_buf): stuck waiting for CPU High to unlock Tx pkt buf %d\n", tx_pkt_buf);
922            }
923            else {
924                iter++;
925            }
926        }
927
928        if(tx_frame_info->tx_pkt_buf_state != TX_PKT_BUF_READY){
929            return PREPARE_FRAME_TRANSMIT_ERROR_UNEXPECTED_PKT_BUF_STATE;
930        }
931
932    } else {
933        // This packet buffer should be lockable and there is no need for CPU_LOW to wait on a mutex
934        // lock if it is not. In that case, CPU_LOW should print an error and quit this function.
935
936        if(tx_frame_info->tx_pkt_buf_state != TX_PKT_BUF_READY){
937            if(tx_frame_info->tx_pkt_buf_state == TX_PKT_BUF_LOW_CTRL ){
938                // CPU Low responsible for any LOW_CTRL buffers
939                //  Don't transmit - just clean up and return
940                tx_frame_info->tx_pkt_buf_state = TX_PKT_BUF_HIGH_CTRL;
941            }
942            //  CPU High will handle it eventually
943            //  Ensure CPU Low doesn't own lock then return
944            unlock_tx_pkt_buf(tx_pkt_buf);
945            return PREPARE_FRAME_TRANSMIT_ERROR_UNEXPECTED_PKT_BUF_STATE;
946        }
947
948        // Attempt to lock the packet buffer. If this fails, we will not wait for it to succeed. Something
949        // has gone wrong and we should print an error.
950        if(lock_tx_pkt_buf(tx_pkt_buf) != PKT_BUF_MUTEX_SUCCESS){
951            wlan_printf(PL_ERROR, "Error: unable to lock TX pkt_buf %d\n", tx_pkt_buf);
952            get_tx_pkt_buf_status(tx_pkt_buf, &is_locked, &owner);
953            wlan_printf(PL_ERROR, "    TX pkt_buf %d status: isLocked = %d, owner = %d\n", tx_pkt_buf, is_locked, owner);
954            tx_frame_info->tx_pkt_buf_state = TX_PKT_BUF_HIGH_CTRL;
955            return PREPARE_FRAME_TRANSMIT_ERROR_LOCK_FAIL;
956        }
957    }
958    // By this point in the function, we have verified that the packet buffer was in an expected state
959    // and have successfully locked the mutex.
960
961    tx_frame_info->tx_pkt_buf_state = TX_PKT_BUF_LOW_CTRL;
962    return 0;
963}
964
965/*****************************************************************************/
966/**
967 * @brief Set the radio channel
968 *
969 * This function will set the radio channel for CPU LOW
970 *
971 * @param   channel     - Radio channel
972 * @return  None
973 *
974 */
975int wlan_mac_low_set_radio_channel(u32 channel){
976
977    if (wlan_verify_channel(channel) == XST_SUCCESS) {
978    mac_param_chan = channel;
979
980        if(mac_param_chan <= 14){
981            mac_param_band = CHAN_BAND_24GHz;
982
983            // Enable DSSS if global variable indicates it should be enabled and PHY sample rate allows it
984            if ((mac_param_dsss_en) && (gl_phy_samp_rate == PHY_20M)) {
985                wlan_phy_DSSS_rx_enable();
986            }
987        } else {
988            mac_param_band = CHAN_BAND_5GHz;
989
990            // Always disable DSSS when in the 5 GHZ band
991            wlan_phy_DSSS_rx_disable();
992        }
993
994        wlan_mac_reset_NAV_counter();
995
996        return wlan_platform_low_set_radio_channel(channel);
997
998    } else {
999        xil_printf("Invalid channel selection %d\n", channel);
1000        return -1;
1001    }
1002}
1003
1004
1005
1006/*****************************************************************************/
1007/**
1008 * @brief Enable / Disable DSSS RX
1009 *
1010 * DSSS RX must be disabled when in the 5 GHz band or when the PHY sample rate
1011 * is not 20 MSps.  However, the low framework will maintain what the state
1012 * should be when in the 2.4 GHz band and the PHY sample rate is 20 MSps
1013 *
1014 * @param   None
1015 * @return  None
1016 *
1017 */
1018void wlan_mac_low_DSSS_rx_enable() {
1019    mac_param_dsss_en = 1;
1020
1021    // Only enable DSSS if in 2.4 GHz band and phy sample rate is 20
1022    if ((mac_param_band == CHAN_BAND_24GHz) && (gl_phy_samp_rate == PHY_20M)) {
1023        wlan_phy_DSSS_rx_enable();
1024    }
1025}
1026
1027
1028void wlan_mac_low_DSSS_rx_disable() {
1029    mac_param_dsss_en = 0;
1030    wlan_phy_DSSS_rx_disable();
1031}
1032
1033int wlan_mac_low_finish_frame_transmit(u16 tx_pkt_buf){
1034    // This function should only be called on a packet buffer whose state is
1035    // TX_PKT_BUF_LOW_CTRL and is currently locked by CPU_LOW
1036    int return_value = 0;
1037    tx_frame_info_t* tx_frame_info;
1038    u32 is_locked, owner;
1039    wlan_ipc_msg_t ipc_msg_to_high;
1040
1041
1042    if(tx_pkt_buf >= NUM_TX_PKT_BUFS){
1043        xil_printf("Error: Tx Pkt Buf index exceeds NUM_TX_PKT_BUFS\n");
1044        return_value = -1;
1045        return return_value;
1046    }
1047
1048    tx_frame_info = (tx_frame_info_t*)CALC_PKT_BUF_ADDR(platform_common_dev_info.tx_pkt_buf_baseaddr, tx_pkt_buf);
1049
1050    switch(tx_frame_info->tx_pkt_buf_state){
1051        case TX_PKT_BUF_LOW_CTRL:
1052            get_tx_pkt_buf_status(tx_pkt_buf, &is_locked, &owner);
1053
1054            if( (is_locked == 0) || (owner != XPAR_CPU_ID)){
1055                wlan_printf(PL_ERROR, "TX pkt_buf %d is not locked by CPU_LOW\n", tx_pkt_buf);
1056
1057                get_tx_pkt_buf_status(tx_pkt_buf, &is_locked, &owner);
1058                tx_frame_info->tx_pkt_buf_state = TX_PKT_BUF_HIGH_CTRL;
1059
1060                return_value = -1;
1061
1062            } else {
1063                //Record the time when we completed this MPDU
1064                tx_frame_info->timestamp_done = get_mac_time_usec();
1065
1066                tx_frame_info->tx_pkt_buf_state = TX_PKT_BUF_DONE;
1067
1068                // Note: at this point in the code, the packet buffer state has been modified to TX_PKT_BUF_DONE,
1069                // yet we have not sent the IPC_MBOX_TX_PKT_BUF_DONE message. If we happen to reboot here,
1070                // this packet buffer will be abandoned and won't be cleaned up in the boot process. This is a narrow
1071                // race in practice, but step-by-step debugging can accentuate the risk since there can be an arbitrary
1072                // amount of time spent in this window.
1073
1074                //Revert the state of the packet buffer and return control to CPU High
1075                if(unlock_tx_pkt_buf(tx_pkt_buf) != PKT_BUF_MUTEX_SUCCESS){
1076                    wlan_printf(PL_ERROR, "Error: unable to unlock TX pkt_buf %d\n", tx_pkt_buf);
1077                    wlan_mac_low_send_exception(WLAN_ERROR_CODE_CPU_LOW_TX_MUTEX);
1078                    tx_frame_info->tx_pkt_buf_state = TX_PKT_BUF_HIGH_CTRL;
1079                } else {
1080                    ipc_msg_to_high.msg_id =  IPC_MBOX_MSG_ID(IPC_MBOX_TX_PKT_BUF_DONE);
1081
1082                    ipc_msg_to_high.num_payload_words = 0;
1083                    ipc_msg_to_high.payload_ptr = NULL;
1084
1085                    ipc_msg_to_high.arg0 = tx_pkt_buf;
1086                    write_mailbox_msg(&ipc_msg_to_high);
1087                }
1088            }
1089        break;
1090        // ---- Something went wrong - packet buffer in unexpected state ----
1091        default:
1092        case TX_PKT_BUF_READY:
1093            // CPU Low responsible for any LOW_CTRL buffers
1094            //  Don't transmit - just clean up and return
1095            tx_frame_info->tx_pkt_buf_state = TX_PKT_BUF_HIGH_CTRL;
1096        case TX_PKT_BUF_UNINITIALIZED:
1097        case TX_PKT_BUF_DONE:
1098        case TX_PKT_BUF_HIGH_CTRL:
1099            //  CPU High will handle it eventually
1100            //  Ensure CPU Low doesn't own lock then return
1101            unlock_tx_pkt_buf(tx_pkt_buf);
1102        break;
1103    }
1104    return return_value;
1105}
1106
1107int wlan_mac_low_prepare_frame_transmit(u16 tx_pkt_buf){
1108    tx_frame_info_t* tx_frame_info;
1109    mac_header_80211* tx_80211_header;
1110    ltg_packet_id_t* pkt_id;
1111    if(tx_pkt_buf >= NUM_TX_PKT_BUFS){
1112        xil_printf("Error: Tx Pkt Buf index exceeds NUM_TX_PKT_BUFS\n");
1113        return PREPARE_FRAME_TRANSMIT_ERROR_INVALID_PKT_BUF;
1114    }
1115
1116    tx_frame_info = (tx_frame_info_t*)CALC_PKT_BUF_ADDR(platform_common_dev_info.tx_pkt_buf_baseaddr, tx_pkt_buf);
1117
1118    tx_frame_info->timestamp_accept = get_mac_time_usec();
1119
1120    // Get pointer to start of MAC header in packet buffer
1121    tx_80211_header = (mac_header_80211*)(CALC_PKT_BUF_ADDR(platform_common_dev_info.tx_pkt_buf_baseaddr, tx_pkt_buf)+PHY_TX_PKT_BUF_MPDU_OFFSET);
1122
1123    // Mark this packet buffer as prepared
1124    tx_frame_info->flags |= TX_FRAME_INFO_FLAGS_PKT_BUF_PREPARED;
1125
1126    // Insert sequence number here
1127    tx_80211_header->sequence_control = ((tx_80211_header->sequence_control) & 0xF) | ( (unique_seq&0xFFF)<<4 );
1128
1129    // Insert unique sequence into tx_frame_info
1130    tx_frame_info->unique_seq = unique_seq;
1131
1132    if((tx_frame_info->flags) & TX_FRAME_INFO_FLAGS_FILL_UNIQ_SEQ){
1133        // Fill unique sequence number into LTG payload
1134        pkt_id = (ltg_packet_id_t*)((u8*)tx_80211_header + sizeof(mac_header_80211));
1135        pkt_id->unique_seq = unique_seq;
1136    }
1137
1138    //Increment the global unique sequence number
1139    unique_seq++;
1140    return 0;
1141}
1142
1143void wlan_mac_low_send_low_tx_details(u8 pkt_buf, wlan_mac_low_tx_details_t* low_tx_details){
1144    wlan_ipc_msg_t ipc_msg_to_high;
1145
1146    ipc_msg_to_high.payload_ptr = (u32*)low_tx_details;
1147    ipc_msg_to_high.arg0 = pkt_buf;
1148    ipc_msg_to_high.num_payload_words = (sizeof(wlan_mac_low_tx_details_t) / sizeof(u32));
1149
1150    ipc_msg_to_high.msg_id =  IPC_MBOX_MSG_ID(IPC_MBOX_PHY_TX_REPORT);
1151    write_mailbox_msg(&ipc_msg_to_high);
1152    return;
1153}
1154
1155
1156
1157/*****************************************************************************/
1158/**
1159 * @brief Notify upper-level MAC of frame reception
1160 *
1161 * Sends an IPC message to the upper-level MAC to notify it that a frame has been
1162 * received and is ready to be processed
1163 *
1164 * @param   None
1165 * @return  None
1166 *
1167 * @note This function assumes it is called in the same context where rx_pkt_buf is still valid.
1168 */
1169void wlan_mac_low_frame_ipc_send(){
1170    wlan_ipc_msg_t ipc_msg_to_high;
1171
1172    ipc_msg_to_high.msg_id            = IPC_MBOX_MSG_ID(IPC_MBOX_RX_PKT_BUF_READY);
1173    ipc_msg_to_high.num_payload_words = 0;
1174    ipc_msg_to_high.arg0              = rx_pkt_buf;
1175
1176    write_mailbox_msg(&ipc_msg_to_high);
1177}
1178
1179
1180
1181/*****************************************************************************/
1182/**
1183 * @brief Set Frame Reception Callback
1184 *
1185 * Tells the framework which function should be called when the PHY begins processing a frame reception
1186 *
1187 * @param   callback         - Pointer to callback function
1188 * @return  None
1189 */
1190inline void wlan_mac_low_set_frame_rx_callback(function_ptr_t callback){
1191    frame_rx_callback = callback;
1192}
1193
1194inline void wlan_mac_low_set_sample_rate_change_callback(function_ptr_t callback){
1195    sample_rate_change_callback = callback;
1196}
1197
1198inline void wlan_mac_low_set_handle_tx_pkt_buf_ready(function_ptr_t callback){
1199    handle_tx_pkt_buf_ready = callback;
1200}
1201
1202inline void wlan_mac_low_set_beacon_txrx_config_callback(function_ptr_t callback){
1203    beacon_txrx_config_callback = callback;
1204}
1205
1206inline void wlan_mac_low_set_mcast_buffer_enable_callback(function_ptr_t callback){
1207    mcast_buffer_enable_callback = callback;
1208}
1209
1210inline void wlan_mac_low_set_mactime_change_callback(function_ptr_t callback){
1211    mactime_change_callback = callback;
1212}
1213
1214/*****************************************************************************/
1215/**
1216 * @brief Set IPC_MBOX_LOW_PARAM Callback
1217 *
1218 * Tells the framework which function should be called when an ipc message is received for
1219 * the IPC_MBOX_LOW_PARAM command.
1220 *
1221 * @param   callback         - Pointer to callback function
1222 * @return  None
1223 */
1224void wlan_mac_low_set_ipc_low_param_callback(function_ptr_t callback){
1225    ipc_low_param_callback = callback;
1226}
1227
1228
1229
1230/*****************************************************************************/
1231/**
1232 * @brief Various Getter Methods
1233 *
1234 * These functions will get parameters from the low framework.
1235 *
1236 * @param   None
1237 * @return  (see individual function)
1238 */
1239inline u32 wlan_mac_low_get_active_channel(){
1240    return mac_param_chan;
1241}
1242
1243
1244inline s8 wlan_mac_low_get_current_ctrl_tx_pow(){
1245    return mac_param_ctrl_tx_pow;
1246}
1247
1248
1249inline u32 wlan_mac_low_get_current_rx_filter(){
1250    return mac_param_rx_filter;
1251}
1252
1253
1254inline phy_samp_rate_t wlan_mac_low_get_phy_samp_rate() {
1255    return gl_phy_samp_rate;
1256}
1257
1258
1259
1260/*****************************************************************************/
1261/**
1262 * @brief Get the Rx Start Microsecond Timestamp
1263 *
1264 * This function returns the Rx start timestamp of the system
1265 *
1266 * @param   None
1267 * @return  u64              - microsecond timestamp
1268 */
1269inline u64 wlan_mac_low_get_rx_start_timestamp() {
1270    u32 timestamp_high_u32;
1271    u32 timestamp_low_u32;
1272    u64 timestamp_u64;
1273
1274    // RX_START timestamp is captured once per reception - no race condition between 32-bit reads
1275    timestamp_high_u32 = Xil_In32(WLAN_MAC_REG_RX_TIMESTAMP_MSB);
1276    timestamp_low_u32 = Xil_In32(WLAN_MAC_REG_RX_TIMESTAMP_LSB);
1277    timestamp_u64 = (((u64)timestamp_high_u32)<<32) + ((u64)timestamp_low_u32);
1278
1279    return timestamp_u64;
1280}
1281
1282
1283
1284/*****************************************************************************/
1285/**
1286 * @brief Get the Tx Start Microsecond Timestamp
1287 *
1288 * This function returns the Tx start timestamp of the system
1289 *
1290 * @param   None
1291 * @return  u64              - microsecond timestamp
1292 */
1293inline u64 wlan_mac_low_get_tx_start_timestamp() {
1294    u32 timestamp_high_u32;
1295    u32 timestamp_low_u32;
1296    u64 timestamp_u64;
1297
1298    // TX_START timestamp is captured once per transmission - no race condition between 32-bit reads
1299    timestamp_high_u32 = Xil_In32(WLAN_MAC_REG_TX_TIMESTAMP_MSB);
1300    timestamp_low_u32 = Xil_In32(WLAN_MAC_REG_TX_TIMESTAMP_LSB);
1301    timestamp_u64 = (((u64)timestamp_high_u32)<<32) + ((u64)timestamp_low_u32);
1302
1303    return timestamp_u64;
1304}
1305
1306
1307/*****************************************************************************/
1308/**
1309 * @brief Sets the node's MAC address in the MAC core's NAV logic
1310 *
1311 * @param   addr - pointer to 6-byte MAC address
1312 * @return  None
1313 */
1314void wlan_mac_low_set_nav_check_addr(u8* addr) {
1315    Xil_Out32(WLAN_MAC_REG_NAV_CHECK_ADDR_1, *((u32*)&(addr[0])) );
1316    Xil_Out32(WLAN_MAC_REG_NAV_CHECK_ADDR_2, *((u32*)&(addr[4])) );
1317}
1318
1319/*****************************************************************************/
1320/**
1321 * @brief Search for and Lock Empty Packet Buffer (Blocking)
1322 *
1323 * This is a blocking function for finding and locking an empty rx packet buffer. The low framework
1324 * calls this function after passing a new wireless reception up to CPU High for processing. CPU High
1325 * must unlock Rx packet buffers after processing the received packet. This function loops over all Rx
1326 * packet buffers until it finds one that has been unlocked by CPU High.
1327 *
1328 * By design this function prints a message if it fails to unlock the oldest packet buffer. When this
1329 * occurs it indicates that CPU Low has outrun CPU High, a situation that leads to dropped wireless
1330 * receptions with high probability. The node recovers gracefully from this condition and will
1331 * continue processing new Rx events after CPU High catches up. But seeing this message in the UART
1332 * for CPU Low is a strong indicator the CPU High code is not keeping up with wireless receptions.
1333 *
1334 * @param   None
1335 * @return  None
1336 *
1337 * @note    This function assumes it is called in the same context where rx_pkt_buf is still valid.
1338 */
1339inline void wlan_mac_low_lock_empty_rx_pkt_buf(){
1340    // This function blocks until it safely finds a packet buffer for the PHY RX to store a future reception
1341    rx_frame_info_t* rx_frame_info;
1342    u32 i = 1;
1343
1344    while(1) {
1345        //rx_pkt_buf is the global shared by all contexts which deal with wireless Rx
1346        // Rx packet buffers are used in order. Thus incrementing rx_pkt_buf should
1347        // select the "oldest" packet buffer, the one that is most likely to have already
1348        // been processed and released by CPU High
1349        rx_pkt_buf = (rx_pkt_buf+1) % NUM_RX_PKT_BUFS;
1350        rx_frame_info    = (rx_frame_info_t*) CALC_PKT_BUF_ADDR(platform_common_dev_info.rx_pkt_buf_baseaddr, rx_pkt_buf);
1351
1352        if((rx_frame_info->rx_pkt_buf_state) == RX_PKT_BUF_LOW_CTRL){
1353
1354            if(lock_rx_pkt_buf(rx_pkt_buf) == PKT_BUF_MUTEX_SUCCESS){
1355            // By default Rx pkt buffers are not zeroed out, to save the performance penalty of bzero'ing 2KB
1356            //     However zeroing out the pkt buffer can be helpful when debugging Rx MAC/PHY behaviors
1357            // bzero((void *)(RX_PKT_BUF_TO_ADDR(rx_pkt_buf)), 2048);
1358
1359            // Set the OFDM and DSSS PHYs to use the same Rx pkt buffer
1360            wlan_phy_rx_pkt_buf_ofdm(rx_pkt_buf);
1361            wlan_phy_rx_pkt_buf_dsss(rx_pkt_buf);
1362
1363            if (i > 1) { xil_printf("found in %d iterations.\n", i); }
1364
1365            return;
1366            } else {
1367                xil_printf("Error: unable to lock Rx pkt_buf %d despite RX_PKT_BUF_LOW_CTRL\n", rx_pkt_buf);
1368                unlock_rx_pkt_buf(rx_pkt_buf);
1369        }
1370        }
1371
1372        if (i == 1) { xil_printf("Searching for empty packet buff ... "); }
1373        i++;
1374    }
1375}
1376
1377
1378/*****************************************************************************/
1379/**
1380 * @brief Finish PHY Reception
1381 *
1382 * This function polls the MAC status register until the Rx PHY goes idle. The
1383 * return value indicates whether the just-completed reception was good
1384 * (no Rx errors and matching checksum) or bad
1385 *
1386 * @param   None
1387 * @return  u32              - FCS status (RX_MPDU_STATE_FCS_GOOD or RX_MPDU_STATE_FCS_BAD)
1388 */
1389inline u32 wlan_mac_hw_rx_finish() {
1390    u32 mac_hw_status;
1391    int i = 0;
1392
1393    // Wait for the packet to finish - Rx PHY is "active" if the demod/decoding pipeline is
1394    //  still writing bytes to the packet buffer. The FCS result is not known until the last
1395    //  payload byte has been written. The RX_PHY_ACTIVE bit might de-assert before this happens
1396    //  for some lengths/rates/bandwidths. This allows RX_END to start the SIFS timer at the right
1397    //  time independent of payload-specific PHY latencies.
1398    do{
1399        mac_hw_status = wlan_mac_get_status();
1400        if(i++>1000000) {
1401            xil_printf("Stuck in wlan_mac_hw_rx_finish!\n", mac_hw_status);
1402            xil_printf(" MAC HW Status: 0x%08x\n", wlan_mac_get_status());
1403            xil_printf(" Rx Hdr Params: 0x%08x\n", wlan_mac_get_rx_phy_hdr_params());
1404            xil_printf(" Rx PHY Status: 0x%08x\n", Xil_In32(WLAN_RX_STATUS));
1405            xil_printf(" Last Rx Data : 0x%08x\n", Xil_In32(WLAN_MAC_REG_LATEST_RX_BYTE));
1406        }
1407        // Exit this loop when either:
1408        //  -The Rx PHY has finished received AND has finished writing its payload
1409        //  -The Rx PHY got reset mid-Rx and will not be writing a payload
1410        } while( (mac_hw_status & (WLAN_MAC_STATUS_MASK_RX_PHY_ACTIVE | WLAN_MAC_STATUS_MASK_RX_PHY_WRITING_PAYLOAD))
1411                 && ((mac_hw_status & WLAN_MAC_STATUS_MASK_RX_END_ERROR) == 0));
1412
1413    // Check RX_END_ERROR and FCS
1414    if( (mac_hw_status & WLAN_MAC_STATUS_MASK_RX_FCS_GOOD) &&
1415       ((mac_hw_status & WLAN_MAC_STATUS_MASK_RX_END_ERROR) == 0)) {
1416        return 1;
1417
1418    } else {
1419        return 0;
1420
1421    }
1422}
1423
1424
1425
1426/*****************************************************************************/
1427/**
1428 * @brief Force reset backoff counter in MAC hardware
1429 */
1430inline void wlan_mac_reset_backoff_counter() {
1431    Xil_Out32(WLAN_MAC_REG_CONTROL, Xil_In32(WLAN_MAC_REG_CONTROL) | WLAN_MAC_CTRL_MASK_RESET_A_BACKOFF);
1432    Xil_Out32(WLAN_MAC_REG_CONTROL, Xil_In32(WLAN_MAC_REG_CONTROL) & ~WLAN_MAC_CTRL_MASK_RESET_A_BACKOFF);
1433}
1434
1435
1436
1437/*****************************************************************************/
1438/**
1439 * @brief Force reset NAV counter in MAC hardware
1440 */
1441inline void wlan_mac_reset_NAV_counter() {
1442    Xil_Out32(WLAN_MAC_REG_CONTROL, Xil_In32(WLAN_MAC_REG_CONTROL) | WLAN_MAC_CTRL_MASK_RESET_NAV);
1443    Xil_Out32(WLAN_MAC_REG_CONTROL, Xil_In32(WLAN_MAC_REG_CONTROL) & ~WLAN_MAC_CTRL_MASK_RESET_NAV);
1444}
1445
1446
1447
1448/*****************************************************************************/
1449/**
1450 * @brief Convert MCS to number of data bits per symbol
1451 */
1452inline u16 wlan_mac_low_mcs_to_n_dbps(u8 mcs, u8 phy_mode) {
1453    if(phy_mode == PHY_MODE_NONHT && mcs < (sizeof(mcs_to_n_dbps_nonht_lut)/sizeof(mcs_to_n_dbps_nonht_lut[0]))) {
1454        return mcs_to_n_dbps_nonht_lut[mcs];
1455    } else if(phy_mode == PHY_MODE_HTMF && mcs < (sizeof(mcs_to_n_dbps_htmf_lut)/sizeof(mcs_to_n_dbps_htmf_lut[0]))) {
1456        return mcs_to_n_dbps_htmf_lut[mcs];
1457    } else {
1458        xil_printf("ERROR (wlan_mac_low_mcs_to_n_dbps): Invalid PHY_MODE (%d) or MCS (%d)\n", phy_mode, mcs);
1459        return 1; // N_DBPS used as denominator, so better not return 0
1460    }
1461}
1462
1463
1464
1465/*****************************************************************************/
1466/**
1467 * @brief Convert MCS to Control Response MCS
1468 */
1469inline u8 wlan_mac_low_mcs_to_ctrl_resp_mcs(u8 mcs, u8 phy_mode){
1470    // Returns the fastest NON-HT half-rate MCS lower than the provided MCS and no larger that 24Mbps.
1471    //  Valid return values are [0, 2, 4]
1472    u8 return_value = 0;
1473
1474    if(phy_mode == PHY_MODE_NONHT){
1475        return_value = mcs;
1476        if(return_value > 4){ return_value = 4; }
1477        if(return_value % 2){ return_value--;   }
1478    } else if(phy_mode == PHY_MODE_HTMF) {
1479        switch(mcs){
1480            default:
1481            case 0:
1482                return_value = 0;
1483            break;
1484            case 1:
1485                return_value = 2;
1486            break;
1487            case 2:
1488                return_value = 2;
1489            break;
1490            case 3:
1491            case 4:
1492            case 5:
1493            case 6:
1494            case 7:
1495                return_value = 4;
1496            break;
1497        }
1498    }
1499    return return_value;
1500}
1501
1502inline void wlan_mac_hw_clear_rx_started() {
1503    wlan_mac_reset_rx_started(1);
1504    wlan_mac_reset_rx_started(0);
1505
1506    return;
1507}
1508
1509void wlan_mac_set_tu_target(u64 tu_target) {
1510    Xil_Out32(WLAN_MAC_REG_TU_TARGET_MSB, ((u32)(tu_target >> 32)));
1511    Xil_Out32(WLAN_MAC_REG_TU_TARGET_LSB, ((u32)(tu_target & 0xFFFFFFFF)));
1512}
1513
Note: See TracBrowser for help on using the repository browser.