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

Last change on this file was 6133, checked in by chunter, 4 months ago

took another pass at cleaning up white space

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