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

Last change on this file was 6319, checked in by chunter, 2 years ago

1.8.0 release wlan-mac-se

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