/** @file wlan_mac_high.c * @brief Top-level WLAN MAC High Framework * * This contains the top-level code for accessing the WLAN MAC High Framework. * * @copyright Copyright 2014-2019, Mango Communications. All rights reserved. * Distributed under the Mango Communications Reference Design License * See LICENSE.txt included in the design archive or * at http://mangocomm.com/802.11/license * * This file is part of the Mango 802.11 Reference Design (https://mangocomm.com/802.11) */ /***************************** Include Files *********************************/ #include "wlan_mac_high_sw_config.h" // Xilinx Includes #include "stdlib.h" #include "malloc.h" #include "xil_exception.h" #include "xaxicdma.h" // WLAN Includes #include "wlan_mac_common.h" #include "wlan_platform_common.h" #include "wlan_platform_high.h" #include "wlan_mac_dl_list.h" #include "wlan_mac_mailbox_util.h" #include "wlan_mac_pkt_buf_util.h" #include "wlan_mac_802_11_defs.h" #include "wlan_mac_high.h" #include "wlan_mac_packet_types.h" #include "wlan_mac_queue.h" #include "wlan_mac_eth_util.h" #include "wlan_mac_ltg.h" #include "wlan_mac_entries.h" #include "wlan_mac_event_log.h" #include "wlan_mac_schedule.h" #include "wlan_mac_addr_filter.h" #include "wlan_mac_network_info.h" #include "wlan_mac_station_info.h" #include "wlan_exp_common.h" #include "wlan_exp_node.h" #include "wlan_exp_transport.h" #include "wlan_mac_scan.h" #include "wlan_mac_high_mailbox_util.h" /*********************** Global Variable Definitions *************************/ // Constants const u8 bcast_addr[MAC_ADDR_LEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; const u8 zero_addr[MAC_ADDR_LEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // HW structures platform_high_dev_info_t platform_high_dev_info; platform_common_dev_info_t platform_common_dev_info; wlan_mac_hw_info_t wlan_mac_hw_info; XAxiCdma cdma_inst; ///< Central DMA instance // Callback function pointers volatile function_ptr_t press_pb_0_callback; ///< User callback for pressing pushbutton 0 volatile function_ptr_t release_pb_0_callback; ///< User callback for releasing pushbutton 0 volatile function_ptr_t press_pb_1_callback; ///< User callback for pressing pushbutton 1 volatile function_ptr_t release_pb_1_callback; ///< User callback for releasing pushbutton 1 volatile function_ptr_t press_pb_2_callback; ///< User callback for pressing pushbutton 2 volatile function_ptr_t release_pb_2_callback; ///< User callback for releasing pushbutton 2 volatile function_ptr_t uart_callback; ///< User callback for UART reception volatile function_ptr_t mpdu_tx_high_done_callback; ///< User callback for lower-level message that MPDU transmission is complete volatile function_ptr_t mpdu_tx_low_done_callback; ///< User callback for lower-level message that MPDU transmission is complete volatile function_ptr_t mpdu_rx_callback; ///< User callback for lower-level message that MPDU reception is ready for processing volatile function_ptr_t tx_poll_callback; ///< User callback when higher-level framework is ready to send a packet to low volatile function_ptr_t beacon_tx_done_callback; ///< User callback for low-level message that a Beacon transmission is complete volatile function_ptr_t mpdu_tx_dequeue_callback; ///< User callback for higher-level framework dequeuing a packet volatile function_ptr_t cpu_low_reboot_callback; ///< User callback for CPU_LOW boot volatile function_ptr_t tx_queue_state_change_callback; ///< User callback for Tx queue state changes // CPU_LOW parameters that MAC High Framework tracks and is responsible for re-applying // in the event of a CPU_LOW reboot. volatile u32 low_param_channel; volatile u32 low_param_dsss_en; volatile u8 low_param_rx_ant_mode; volatile s8 low_param_tx_ctrl_pow; volatile s8 low_param_radio_tx_pow; volatile u32 low_param_rx_filter; volatile u32 low_param_random_seed; // Node information volatile u8 dram_present; ///< Indication variable for whether DRAM SODIMM is present on this hardware // Status information wlan_mac_hw_info_t* hw_info; static volatile u32 cpu_low_status; ///< Tracking variable for lower-level CPU status // CPU Low Register Read Buffer static volatile u32* cpu_low_reg_read_buffer; static volatile u8 cpu_low_reg_read_buffer_status; #define CPU_LOW_REG_READ_BUFFER_STATUS_READY 1 #define CPU_LOW_REG_READ_BUFFER_STATUS_NOT_READY 0 // Memory Allocation Debugging static volatile u32 num_malloc; ///< Tracking variable for number of times malloc has been called static volatile u32 num_free; ///< Tracking variable for number of times free has been called static volatile u32 num_realloc; ///< Tracking variable for number of times realloc has been called /******************************** Functions **********************************/ /** * @brief Initialize MAC High Framework * * This function initializes the MAC High Framework by setting * up the hardware and other subsystems in the framework. * * @param None * @return None */ void wlan_mac_high_init(){ int Status; u32 i; tx_frame_info_t* tx_frame_info; rx_frame_info_t* rx_frame_info; #if WLAN_SW_CONFIG_ENABLE_LOGGING u32 log_size; #endif //WLAN_SW_CONFIG_ENABLE_LOGGING XAxiCdma_Config* cdma_cfg_ptr; platform_high_dev_info = wlan_platform_high_get_dev_info(); platform_common_dev_info = wlan_platform_common_get_dev_info(); wlan_mac_hw_info = wlan_platform_get_hw_info(); // Check that right shift works correctly // Issue with -Os in Xilinx SDK 14.7 if (wlan_mac_high_right_shift_test() != 0) { wlan_platform_high_userio_disp_status(USERIO_DISP_STATUS_CPU_ERROR, WLAN_ERROR_CODE_RIGHT_SHIFT); } // Sanity check memory map of aux. BRAM and DRAM //Aux. BRAM Check Status = (TX_QUEUE_DL_ENTRY_MEM_HIGH < BSS_INFO_DL_ENTRY_MEM_BASE) && (BSS_INFO_DL_ENTRY_MEM_HIGH < STATION_INFO_DL_ENTRY_MEM_BASE ) && (STATION_INFO_DL_ENTRY_MEM_HIGH <= CALC_MEM_HIGH_ADDR(platform_high_dev_info.aux_bram_baseaddr, platform_high_dev_info.aux_bram_size)); if(Status != 1){ xil_printf("Error: Overlap detected in Aux. BRAM. Check address assignments\n"); } //DRAM Check Status = (TX_QUEUE_BUFFER_HIGH < BSS_INFO_BUFFER_BASE) && (BSS_INFO_BUFFER_HIGH < STATION_INFO_BUFFER_BASE) && (STATION_INFO_BUFFER_HIGH < USER_SCRATCH_BASE) && (USER_SCRATCH_HIGH < EVENT_LOG_BASE) && (EVENT_LOG_HIGH <= CALC_MEM_HIGH_ADDR(platform_high_dev_info.dram_baseaddr, platform_high_dev_info.dram_size)); if(Status != 1){ xil_printf("Error: Overlap detected in DRAM. Check address assignments\n"); } // *************************************************** // Initialize libraries // *************************************************** // Initialize mailbox wlan_mac_high_init_mailbox(); // Initialize packet buffers init_pkt_buf(); // Initialize the HW info structure init_mac_hw_info(); hw_info = get_mac_hw_info(); xil_printf("------------------------\n"); xil_printf("Initializing MAC High Framework...\n"); xil_printf(" WLAN MAC Address: %02x:%02x:%02x:%02x:%02x:%02x\n", hw_info->hw_addr_wlan[0], hw_info->hw_addr_wlan[1], hw_info->hw_addr_wlan[2], hw_info->hw_addr_wlan[3], hw_info->hw_addr_wlan[4], hw_info->hw_addr_wlan[5]); xil_printf(" Serial Number: %s-%05d\n", hw_info->serial_number_prefix, hw_info->serial_number); // *************************************************** // Initialize callbacks and global state variables // *************************************************** press_pb_0_callback = (function_ptr_t)wlan_null_callback; release_pb_0_callback = (function_ptr_t)wlan_null_callback; press_pb_1_callback = (function_ptr_t)wlan_null_callback; release_pb_1_callback = (function_ptr_t)wlan_null_callback; press_pb_2_callback = (function_ptr_t)wlan_null_callback; release_pb_2_callback = (function_ptr_t)wlan_null_callback; uart_callback = (function_ptr_t)wlan_null_callback; mpdu_rx_callback = (function_ptr_t)wlan_null_callback; mpdu_tx_high_done_callback = (function_ptr_t)wlan_null_callback; mpdu_tx_low_done_callback = (function_ptr_t)wlan_null_callback; beacon_tx_done_callback = (function_ptr_t)wlan_null_callback; tx_poll_callback = (function_ptr_t)wlan_null_callback; mpdu_tx_dequeue_callback = (function_ptr_t)wlan_null_callback; cpu_low_reboot_callback = (function_ptr_t)wlan_null_callback; tx_queue_state_change_callback = (function_ptr_t)wlan_null_callback; num_malloc = 0; num_realloc = 0; num_free = 0; low_param_channel = 0xFFFFFFFF; low_param_dsss_en = 0xFFFFFFFF; low_param_rx_ant_mode = 0xFF; low_param_tx_ctrl_pow = -127; low_param_radio_tx_pow = -127; low_param_rx_filter = 0xFFFFFFFF; low_param_random_seed = 0xFFFFFFFF; cpu_low_reg_read_buffer = NULL; // *************************************************** // Initialize Transmit Packet Buffers // *************************************************** for(i = 0; i < NUM_TX_PKT_BUFS; i++){ tx_frame_info = (tx_frame_info_t*)CALC_PKT_BUF_ADDR(platform_common_dev_info.tx_pkt_buf_baseaddr, i); switch(i){ case TX_PKT_BUF_MPDU_1: case TX_PKT_BUF_MPDU_2: case TX_PKT_BUF_MPDU_3: case TX_PKT_BUF_MPDU_4: case TX_PKT_BUF_MPDU_5: case TX_PKT_BUF_MPDU_6: switch(tx_frame_info->tx_pkt_buf_state){ case TX_PKT_BUF_UNINITIALIZED: case TX_PKT_BUF_HIGH_CTRL: // Buffer already clean on boot or reboot case TX_PKT_BUF_DONE: // CPU High rebooted while CPU Low finished old Tx // Ignore the packet buffer contents and clean up default: // Something went wrong if tx_pkt_buf_state is something // other than one of the tx_pkt_buf_state_t enums. We'll // attempt to resolve the problem by explicitly setting // the state. force_lock_tx_pkt_buf(i); tx_frame_info->tx_pkt_buf_state = TX_PKT_BUF_HIGH_CTRL; break; case TX_PKT_BUF_READY: case TX_PKT_BUF_LOW_CTRL: // CPU High rebooted after submitting packet for transmission // Will be handled by CPU Low, either because CPU Low is about // to transmit or just rebooted and will clean up break; } break; case TX_PKT_BUF_BEACON: unlock_tx_pkt_buf(TX_PKT_BUF_BEACON); tx_frame_info->tx_pkt_buf_state = TX_PKT_BUF_HIGH_CTRL; break; case TX_PKT_BUF_RTS: case TX_PKT_BUF_ACK_CTS: unlock_tx_pkt_buf(i); break; default: break; } } // *************************************************** // Initialize Receive Packet Buffers // *************************************************** for(i = 0; i < NUM_RX_PKT_BUFS; i++){ rx_frame_info = (rx_frame_info_t*)CALC_PKT_BUF_ADDR(platform_common_dev_info.rx_pkt_buf_baseaddr, i); switch(rx_frame_info->rx_pkt_buf_state){ case RX_PKT_BUF_UNINITIALIZED: case RX_PKT_BUF_LOW_CTRL: //CPU_LOW will initialize break; case RX_PKT_BUF_HIGH_CTRL: case RX_PKT_BUF_READY: // CPU HIGH rebooted after CPU Low submitted packet for de-encap/logging // Release lock and reset state // Note: this will not cause CPU_LOW to re-lock this packet buffer. // The effects of this are subtle. CPU_LOW will see that the buffer // Is under LOW_CTRL and will assume it has a mutex lock. It will // fill the packet buffer all while the mutex is unlocked. Once the // state transitions to READY and is passed to CPU_HIGH, this ambiguous // state will be resolved. default: // Something went wrong if rx_pkt_buf_state is something // other than one of the rx_pkt_buf_state_t enums. We'll // attempt to resolve the problem by explicitly setting // the state. unlock_rx_pkt_buf(i); rx_frame_info->rx_pkt_buf_state = RX_PKT_BUF_LOW_CTRL; break; } } // *************************************************** // Initialize CDMA, GPIO, and UART drivers // *************************************************** // Initialize the central DMA (CDMA) driver cdma_cfg_ptr = XAxiCdma_LookupConfig(platform_high_dev_info.cdma_dev_id); Status = XAxiCdma_CfgInitialize(&cdma_inst, cdma_cfg_ptr, cdma_cfg_ptr->BaseAddress); if (Status != XST_SUCCESS) { wlan_printf(PL_ERROR, "ERROR: Could not initialize CDMA: %d\n", Status); } XAxiCdma_IntrDisable(&cdma_inst, XAXICDMA_XR_IRQ_ALL_MASK); xil_printf("------------------------\n"); xil_printf("Testing DRAM...\n"); // If the CPU hangs at this point there is probably a problem with the DRAM... if(wlan_mac_high_memory_test() != 0 ){ xil_printf("A working DRAM SODIMM has not been detected on this board.\n"); xil_printf("The 802.11 Reference Design requires at least 1GB of DRAM.\n"); xil_printf("This CPU will now halt.\n"); wlan_platform_high_userio_disp_status(USERIO_DISP_STATUS_CPU_ERROR, WLAN_ERROR_CODE_DRAM_NOT_PRESENT); } // *************************************************** // Initialize various subsystems in the MAC High Framework // *************************************************** queue_init(); #if WLAN_SW_CONFIG_ENABLE_LOGGING // The event_list lives in DRAM immediately following the queue payloads. if(MAX_EVENT_LOG == -1){ log_size = EVENT_LOG_SIZE; } else { log_size = WLAN_MIN(EVENT_LOG_SIZE, (u32)MAX_EVENT_LOG ); } event_log_init( (void*)EVENT_LOG_BASE, log_size ); #endif //WLAN_SW_CONFIG_ENABLE_LOGGING network_info_init(); station_info_init(); station_info_t* station_info = station_info_create((u8*)bcast_addr); station_info->flags |= STATION_INFO_FLAG_KEEP; #if WLAN_SW_CONFIG_ENABLE_ETH_BRIDGE wlan_eth_util_init(); #endif wlan_mac_schedule_init(); #if WLAN_SW_CONFIG_ENABLE_LTG wlan_mac_ltg_sched_init(); #endif //WLAN_SW_CONFIG_ENABLE_LTG wlan_mac_addr_filter_init(); wlan_mac_scan_init(); //Non-blocking request for CPU_LOW to send its state. This handles the case that //CPU_HIGH reboots some point after CPU_LOW had already booted. wlan_mac_high_request_low_state(); wlan_mac_high_set_radio_channel(1); // Set a sane default channel. The top-level project (AP/STA/IBSS/etc) is free to change this // Initialize Interrupts wlan_mac_high_interrupt_init(); } /** * @brief Initialize MAC High Framework's Interrupts * * This function initializes sets up the interrupt subsystem * of the MAC High Framework. * * @param None * @return WLAN_SUCCESS or WLAN_FAILURE */ int wlan_mac_high_interrupt_init() { int Result; // *************************************************** // Connect interrupt devices "owned" by wlan_mac_high // *************************************************** // *************************************************** // Connect interrupt devices in other subsystems // *************************************************** Result = wlan_platform_high_init(); if (Result != WLAN_SUCCESS) { wlan_printf(PL_ERROR,"Failed to set up Ethernet interrupt\n"); return Result; } Result = setup_mailbox_interrupt(); if (Result != WLAN_SUCCESS) { wlan_printf(PL_ERROR, "Failed to set up wlan_lib mailbox interrupt\n"); return WLAN_FAILURE; } // *************************************************** // Enable MicroBlaze exceptions // *************************************************** Xil_ExceptionInit(); Xil_ExceptionEnable(); // Finish setting up any subsystems that were waiting on interrupts to be configured network_info_init_finish(); return WLAN_SUCCESS; } void wlan_mac_high_uart_rx_callback(u8 rxByte){ uart_callback(rxByte); } void wlan_mac_high_userio_inputs_callback(u32 userio_state, userio_input_mask_t userio_delta){ switch(userio_delta){ case USERIO_INPUT_MASK_PB_0: if(userio_state){ press_pb_0_callback(); } else { release_pb_0_callback(); } break; case USERIO_INPUT_MASK_PB_1: if(userio_state){ press_pb_1_callback(); } else { release_pb_1_callback(); } break; case USERIO_INPUT_MASK_PB_2: if(userio_state){ press_pb_2_callback(); } else { release_pb_2_callback(); } break; default: //Not implemented break; } } void wlan_mac_high_sw_intr_callback(u32 intr_state){ #if WLAN_SW_CONFIG_ENABLE_ETH_BRIDGE if(intr_state & SW_INTR_ID_PORTAL_ETH_RX){ wlan_poll_eth_rx_queue(); } if(intr_state & SW_INTR_ID_PORTAL_ETH_TX){ wlan_poll_eth_tx_queue(); } #endif #if WLAN_SW_CONFIG_ENABLE_WLAN_EXP if(intr_state & SW_INTR_ID_WLAN_EXP_ETH_TX){ wlan_exp_handle_tx_queue(); } #endif } void wlan_mac_high_set_press_pb_0_callback(function_ptr_t callback){ press_pb_0_callback = callback; } void wlan_mac_high_set_release_pb_0_callback(function_ptr_t callback){ release_pb_0_callback = callback; } void wlan_mac_high_set_press_pb_1_callback(function_ptr_t callback){ press_pb_1_callback = callback; } void wlan_mac_high_set_release_pb_1_callback(function_ptr_t callback){ release_pb_1_callback = callback; } void wlan_mac_high_set_press_pb_2_callback(function_ptr_t callback){ press_pb_2_callback = callback; } void wlan_mac_high_set_release_pb_2_callback(function_ptr_t callback){ release_pb_2_callback = callback; } /** * @brief Set UART Reception Callback * * Tells the framework which function should be called when * a byte is received from UART. * * @param function_ptr_t callback * - Pointer to callback function * @return None * */ void wlan_mac_high_set_uart_rx_callback(function_ptr_t callback){ uart_callback = callback; } /** * @brief Set MPDU Transmission Complete Callback (High-level) * * Tells the framework which function should be called when * the lower-level CPU confirms that a high-level MPDU transmission has * completed. * * @param function_ptr_t callback * - Pointer to callback function * @return None * * @note This callback is not executed for individual retransmissions. * It is instead only executed after a chain of retransmissions is complete * either through the reception of an ACK or the number of retransmissions * reaching the maximum number of retries specified by the MPDU's * tx_frame_info metadata. * */ void wlan_mac_high_set_mpdu_tx_high_done_callback(function_ptr_t callback){ mpdu_tx_high_done_callback = callback; } /** * @brief Set MPDU Transmission Complete Callback (Low-level) * * Tells the framework which function should be called when * the lower-level CPU confirms that a low-level MPDU transmission has * completed. * * @param function_ptr_t callback * - Pointer to callback function * @return None * */ void wlan_mac_high_set_mpdu_tx_low_done_callback(function_ptr_t callback){ mpdu_tx_low_done_callback = callback; } void wlan_mac_high_set_beacon_tx_done_callback(function_ptr_t callback){ beacon_tx_done_callback = callback; } /** * @brief Set MPDU Reception Callback * * Tells the framework which function should be called when * the lower-level CPU receives a valid MPDU frame. * * @param function_ptr_t callback * - Pointer to callback function * @return None * */ void wlan_mac_high_set_mpdu_rx_callback(function_ptr_t callback){ mpdu_rx_callback = callback; } /** * @brief Set Poll Tx Queue Callback * * Tells the framework which function should be called whenever * the framework knows that CPU_LOW is ready to send a new packet. * * @param function_ptr_t callback * - Pointer to callback function * @return None * */ void wlan_mac_high_set_poll_tx_queues_callback(function_ptr_t callback){ tx_poll_callback = callback; } /** * @brief Set MPDU Dequeue Callback * * Tells the framework which function should be called when * a packet is dequeued and about to be passed to the * lower-level CPU. * * @param function_ptr_t callback * - Pointer to callback function * @return None * */ void wlan_mac_high_set_mpdu_dequeue_callback(function_ptr_t callback){ mpdu_tx_dequeue_callback = callback; } /** * @brief Set CPU_LOW Reboot Callback * * Tells the framework which function should be called when * CPU_LOW boots or reboots. This allows a high-level application * to restore any parameters it previously set in CPU_LOW * * @param function_ptr_t callback * - Pointer to callback function * @return None * */ void wlan_mac_high_set_cpu_low_reboot_callback(function_ptr_t callback){ cpu_low_reboot_callback = callback; } /** * @brief Set Tx queue state change callback * * Tells the framework which function should be called when * the Tx queue for a network member changes state. * * @param function_ptr_t callback * - Pointer to callback function * @return None * */ void wlan_mac_high_set_tx_queue_state_change_callback(function_ptr_t callback){ tx_queue_state_change_callback = callback; } /** * @brief Display Memory Allocation Information * * This function is a wrapper around a call to mallinfo(). It prints * the information returned by mallinfo() to aid in the debugging of * memory leaks and other dynamic memory allocation issues. * * @param None * @return None * */ void wlan_mac_high_display_mallinfo(){ struct mallinfo mi; mi = mallinfo(); xil_printf("\n"); xil_printf("--- Malloc Info ---\n"); xil_printf("Summary:\n"); xil_printf(" num_malloc: %d\n", num_malloc); xil_printf(" num_realloc: %d\n", num_realloc); xil_printf(" num_free: %d\n", num_free); xil_printf(" num_malloc-num_free: %d\n", (int)num_malloc - (int)num_free); xil_printf(" System: %d bytes\n", mi.arena); xil_printf(" Total Allocated Space: %d bytes\n", mi.uordblks); xil_printf(" Total Free Space: %d bytes\n", mi.fordblks); #if 0 xil_printf("Details:\n"); xil_printf(" arena: %d\n", mi.arena); xil_printf(" ordblks: %d\n", mi.ordblks); xil_printf(" smblks: %d\n", mi.smblks); xil_printf(" hblks: %d\n", mi.hblks); xil_printf(" hblkhd: %d\n", mi.hblkhd); xil_printf(" usmblks: %d\n", mi.usmblks); xil_printf(" fsmblks: %d\n", mi.fsmblks); xil_printf(" uordblks: %d\n", mi.uordblks); xil_printf(" fordblks: %d\n", mi.fordblks); xil_printf(" keepcost: %d\n", mi.keepcost); #endif } /** * @brief Dynamically Allocate Memory * * This function wraps malloc() and uses its same API. * * @param u32 size * - Number of bytes that should be allocated * @return void* * - Memory address of allocation if the allocation was successful * - NULL if the allocation was unsuccessful * * @note The purpose of this function is to funnel all memory allocations through one place in * code to enable easier debugging of memory leaks when they occur. This function also updates * a variable maintained by the framework to track the number of memory allocations and prints * this value, along with the other data from wlan_mac_high_display_mallinfo() in the event that * malloc() fails to allocate the requested size. * */ void* wlan_mac_high_malloc(u32 size){ void* return_value; return_value = malloc(size); if(return_value == NULL){ xil_printf("malloc error. Try increasing heap size in linker script.\n"); wlan_mac_high_display_mallinfo(); } else { #if 0 xil_printf("MALLOC - 0x%08x %d\n", return_value, size); #endif num_malloc++; } return return_value; } /** * @brief Dynamically Allocate and Initialize Memory * * This function wraps wlan_mac_high_malloc() and uses its same API. If successfully allocated, * this function will explicitly zero-initialize the allocated memory. * * @param u32 size * - Number of bytes that should be allocated * @return void* * - Memory address of allocation if the allocation was successful * - NULL if the allocation was unsuccessful * * @see wlan_mac_high_malloc() * */ void* wlan_mac_high_calloc(u32 size){ //This is just a simple wrapper around calloc to aid in debugging memory leak issues void* return_value; return_value = wlan_mac_high_malloc(size); if(return_value == NULL){ } else { memset(return_value, 0 , size); } return return_value; } /** * @brief Dynamically Reallocate Memory * * This function wraps realloc() and uses its same API. * * @param void* addr * - Address of dynamically allocated array that should be reallocated * @param u32 size * - Number of bytes that should be allocated * @return void* * - Memory address of allocation if the allocation was successful * - NULL if the allocation was unsuccessful * * @note The purpose of this function is to funnel all memory allocations through one place in * code to enable easier debugging of memory leaks when they occur. This function also updates * a variable maintained by the framework to track the number of memory allocations and prints * this value, along with the other data from wlan_mac_high_display_mallinfo() in the event that * realloc() fails to allocate the requested size. * */ void* wlan_mac_high_realloc(void* addr, u32 size){ void* return_value; return_value = realloc(addr, size); if(return_value == NULL){ xil_printf("realloc error. Try increasing heap size in linker script.\n"); wlan_mac_high_display_mallinfo(); } else { #if 0 xil_printf("REALLOC - 0x%08x %d\n", return_value, size); #endif num_realloc++; } return return_value; } /** * @brief Free Dynamically Allocated Memory * * This function wraps free() and uses its same API. * * @param void* addr * - Address of dynamically allocated array that should be freed * @return None * * @note The purpose of this function is to funnel all memory freeing through one place in * code to enable easier debugging of memory leaks when they occur. This function also updates * a variable maintained by the framework to track the number of memory frees. * */ void wlan_mac_high_free(void* addr){ #if 0 xil_printf("FREE - 0x%08x\n", addr); #endif free(addr); num_free++; } /** * @brief Test DDR3 SODIMM Memory Module * * This function tests the integrity of the DDR3 SODIMM module attached to the hardware * by performing various write and read tests. Note, this function will destory contents * in DRAM, so it should only be called immediately after booting. * * @param None * @return int * - WLAN_SUCCESS for memory test pass * - WLAN_FAILURE for memory test fail */ int wlan_mac_high_memory_test() { volatile u8 i,j; volatile u8 test_u8; volatile u16 test_u16; volatile u32 test_u32; volatile u64 test_u64; volatile u8 readback_u8; volatile u16 readback_u16; volatile u32 readback_u32; volatile u64 readback_u64; volatile void* memory_ptr; for(i=0; i<6; i++) { // Jump by 100MB steps memory_ptr = (void*)((u8*)platform_high_dev_info.dram_baseaddr + (i*100*1024*1024)); // Test all types at multiple byte offsets to verify unaligned accesses succeed for(j=0; j<3; j++) { test_u8 = rand()&0xFF; test_u16 = rand()&0xFFFF; test_u32 = rand()&0xFFFFFFFF; test_u64 = (((u64)rand()&0xFFFFFFFF)<<32) + ((u64)rand()&0xFFFFFFFF); *((u8*)memory_ptr) = test_u8; readback_u8 = *((u8*)memory_ptr); if(readback_u8!= test_u8){ xil_printf("0x%08x: %2x = %2x\n", memory_ptr, readback_u8, test_u8); xil_printf("DRAM Failure: Addr: 0x%08x -- Unable to verify write of u8\n",memory_ptr); return WLAN_FAILURE; } *((u16*)memory_ptr) = test_u16; readback_u16 = *((u16*)memory_ptr); if(readback_u16 != test_u16){ xil_printf("0x%08x: %4x = %4x\n", memory_ptr, readback_u16, test_u16); xil_printf("DRAM Failure: Addr: 0x%08x -- Unable to verify write of u16\n",memory_ptr); return WLAN_FAILURE; } *((u32*)memory_ptr) = test_u32; readback_u32 = *((u32*)memory_ptr); if(readback_u32 != test_u32){ xil_printf("0x%08x: %8x = %8x\n", memory_ptr, readback_u32, test_u32); xil_printf("DRAM Failure: Addr: 0x%08x -- Unable to verify write of u32\n",memory_ptr); return WLAN_FAILURE; } // Disabling u64 test for now - we've observed Zynq ARM is unable to do unaligned // 64-bit DRAM read/write, even when 8/16/32-bit unaligned accesses succeed // FIXME: figure out why ARM fails when MB succeeds using the same memory controller and interface #ifdef __MICROBLAZE__ *((u64*)memory_ptr) = test_u64; readback_u64 = *((u64*)memory_ptr); if(readback_u64!= test_u64){ xil_printf("DRAM Failure: Addr: 0x%08x -- Unable to verify write of u64\n",memory_ptr); return WLAN_FAILURE; } #endif memory_ptr++; } } return WLAN_SUCCESS; } /** * @brief Test Right Shift Operator * * This function tests the compiler right shift operator. This is due to a bug in * the Xilinx 14.7 toolchain when the '-Os' flag is used during compilation. Please * see: http://warpproject.org/forums/viewtopic.php?id=2472 for more information. * * @param None * @return int * - WLAN_SUCCESS for right shift test pass * - WLAN_FAILURE for right shift test fail */ int wlan_mac_high_right_shift_test(){ u8 val_3, val_2, val_1, val_0; volatile u32 test_val = 0xFEDCBA98; volatile u8* test_array = (u8 *)&test_val; val_3 = (u8)((test_val & 0xFF000000) >> 24); val_2 = (u8)((test_val & 0x00FF0000) >> 16); val_1 = (u8)((test_val & 0x0000FF00) >> 8); val_0 = (u8)((test_val & 0x000000FF) >> 0); if ((val_3 != test_array[3]) || (val_2 != test_array[2]) || (val_1 != test_array[1]) || (val_0 != test_array[0])) { xil_printf("Right shift operator is not operating correctly in this toolchain.\n"); xil_printf("Please use Xilinx 14.4 or an optimization level other than '-Os'\n"); xil_printf("See http://warpproject.org/forums/viewtopic.php?id=2472 for more info.\n"); return WLAN_FAILURE; } return WLAN_SUCCESS; } /** * @brief Start Central DMA Transfer * * This function wraps the XAxiCdma call for a CDMA memory transfer and mimics the well-known * API of memcpy(). This function does not block once the transfer is started. * * @param void* dest * - Pointer to destination address where bytes should be copied * @param void* src * - Pointer to source address from where bytes should be copied * @param u32 size * - Number of bytes that should be copied * @return int * - WLAN_SUCCESS for success of submission * - WLAN_FAILURE for submission failure * * @note This function will block until any existing CDMA transfer is complete. It is therefore * safe to call this function successively as each call will wait on the preceeding call. * */ int wlan_mac_high_cdma_start_transfer(void* dest, void* src, u32 size){ //This is a wrapper function around the central DMA simple transfer call. It's arguments //are intended to be similar to memcpy. Note: This function does not block on the transfer. int return_value; interrupt_state_t prev_interrupt_state; prev_interrupt_state = wlan_platform_intc_stop(); if( size == 0 ){ xil_printf("CDMA Error: size argument must be > 0\n"); xil_printf("0x%08x -> 0x%08x: %d\n", src, dest, size); return WLAN_FAILURE; } wlan_mac_high_cdma_finish_transfer(); return_value = XAxiCdma_SimpleTransfer(&cdma_inst, (u32)src, (u32)dest, size, NULL, NULL); if(return_value != 0){ xil_printf("CDMA Error: code %d, (0x%08x,0x%08x,%d)\n", return_value, dest,src,size); } if(return_value == XST_SUCCESS){ return_value = WLAN_SUCCESS; } else { return_value = WLAN_FAILURE; } wlan_platform_intc_set_state(prev_interrupt_state); return return_value; } /** * @brief Finish Central DMA Transfer * * This function will block until an ongoing CDMA transfer is complete. * If there is no CDMA transfer underway when this function is called, it * returns immediately. * * @param None * @return None * */ void wlan_mac_high_cdma_finish_transfer(){ while(XAxiCdma_IsBusy(&cdma_inst)) {} return; } /** * @brief Transmit MPDU * * This function passes off an MPDU to the lower-level processor for transmission. * */ void wlan_mac_high_mpdu_transmit(tx_80211_queue_buffer_t* tx_queue_buffer, int tx_pkt_buf) { wlan_ipc_msg_t ipc_msg_to_low; tx_frame_info_t* tx_frame_info; mac_header_80211* header; u8 frame_control_1; tx_params_t default_tx_params; u8 is_multicast; station_info_t* station_info_ptr; u8* copy_destination; station_info_ptr = tx_queue_buffer->station_info; tx_frame_info = (tx_frame_info_t*)CALC_PKT_BUF_ADDR(platform_common_dev_info.tx_pkt_buf_baseaddr, tx_pkt_buf); header = (mac_header_80211*)(tx_queue_buffer->pkt); is_multicast = wlan_addr_mcast(header->address_1); // Call user code to notify it of dequeue mpdu_tx_dequeue_callback(tx_queue_buffer); copy_destination = (u8*)(((u8*)CALC_PKT_BUF_ADDR(platform_common_dev_info.tx_pkt_buf_baseaddr, tx_pkt_buf)) + sizeof(tx_frame_info_t) + PHY_TX_PKT_BUF_PHY_HDR_SIZE); // Copy section C1 with CDMA if(tx_queue_buffer->seg1_len > 0){ wlan_mac_high_cdma_start_transfer( copy_destination + tx_queue_buffer->seg0_len, tx_queue_buffer->pkt + tx_queue_buffer->seg1_offset, tx_queue_buffer->seg1_len ); } if(tx_queue_buffer->seg1_len > 0){ // Copy section C0 with memcpy memcpy( copy_destination, tx_queue_buffer->pkt, tx_queue_buffer->seg0_len ); } else { // Copy section C0 with CDMA wlan_mac_high_cdma_start_transfer( copy_destination, tx_queue_buffer->pkt, tx_queue_buffer->seg0_len ); } tx_frame_info->length = tx_queue_buffer->length; tx_frame_info->queue_info = tx_queue_buffer->tx_queue_details; tx_frame_info->flags = 0; if(tx_queue_buffer->flags & TX_80211_QUEUE_BUFFER_FLAGS_FILL_TIMESTAMP) tx_frame_info->flags |= TX_FRAME_INFO_FLAGS_FILL_TIMESTAMP; if(tx_queue_buffer->flags & TX_80211_QUEUE_BUFFER_FLAGS_FILL_DURATION) tx_frame_info->flags |= TX_FRAME_INFO_FLAGS_FILL_DURATION; if(tx_queue_buffer->flags & TX_80211_QUEUE_BUFFER_FLAGS_FILL_UNIQ_SEQ) tx_frame_info->flags |= TX_FRAME_INFO_FLAGS_FILL_UNIQ_SEQ; if((!is_multicast) & (!(tx_queue_buffer->flags & TX_80211_QUEUE_BUFFER_FLAGS_BYPASS_RECOVERY))){ tx_frame_info->flags |= TX_FRAME_INFO_FLAGS_REQ_TO; //TODO: Since TA can be anything in full generality, } // should we only raise this flag if TA = self? I'd // prefer not to add a 6-byte comparison here if we // can avoid it. tx_frame_info->unique_seq = 0; // Unique_seq will be filled in by CPU_LOW if( tx_queue_buffer->flags & TX_80211_QUEUE_BUFFER_FLAGS_TYPE_CTS ){ tx_frame_info->tx_details_type = TX_DETAILS_CTS; } else if( tx_queue_buffer->flags & TX_80211_QUEUE_BUFFER_FLAGS_TYPE_ACK ) { tx_frame_info->tx_details_type = TX_DETAILS_ACK; } else { // Note: this will be overwritten as TX_DETAILS_RTS_ONLY or TX_DETAILS_RTS_MPDU in the PHY report // if RTS/CTS enabled and the length of this packet is sufficiently large tx_frame_info->tx_details_type = TX_DETAILS_MPDU; } // Pull first byte from the payload of the packet so we can determine whether // it is a management or data type frame_control_1 = header->frame_control_1; //Note: header points to DRAM. We aren't relying on the bytes in the Tx packet buffer //that are currently being copied because that would be a race if(station_info_ptr == NULL){ // If there is no station_info tied to this enqueued packet, something must have gone wrong earlier // (e.g. there wasn't room in aux. BRAM to create another station_info_t). We should retrieve // default Tx parameters from the framework. if(is_multicast){ if( (frame_control_1 & MAC_FRAME_CTRL1_MASK_TYPE) == MAC_FRAME_CTRL1_TYPE_MGMT){ default_tx_params = wlan_mac_get_default_tx_params(mcast_mgmt); } else { default_tx_params = wlan_mac_get_default_tx_params(mcast_data); } } else { if( (frame_control_1 & MAC_FRAME_CTRL1_MASK_TYPE) == MAC_FRAME_CTRL1_TYPE_MGMT){ default_tx_params = wlan_mac_get_default_tx_params(unicast_mgmt); } else { default_tx_params = wlan_mac_get_default_tx_params(unicast_data); } } memcpy(&(tx_frame_info->params), &default_tx_params, sizeof(tx_params_t)); } else { if( (frame_control_1 & MAC_FRAME_CTRL1_MASK_TYPE) == MAC_FRAME_CTRL1_TYPE_MGMT){ memcpy(&(tx_frame_info->params), &(station_info_ptr->tx_params_mgmt), sizeof(tx_params_t)); } else { memcpy(&(tx_frame_info->params), &(station_info_ptr->tx_params_data), sizeof(tx_params_t)); } //Sanitize tx_params based on HT capability of STA wlan_mac_sanitize_tx_params(station_info_ptr, &(tx_frame_info->params)); } // Wait for transfer to finish wlan_mac_high_cdma_finish_transfer(); ipc_msg_to_low.msg_id = IPC_MBOX_MSG_ID(IPC_MBOX_TX_PKT_BUF_READY); ipc_msg_to_low.arg0 = tx_pkt_buf; ipc_msg_to_low.num_payload_words = 0; // Set the packet buffer state to READY tx_frame_info->tx_pkt_buf_state = TX_PKT_BUF_READY; // Note: at this point in the code, the packet buffer state has been modified to TX_PKT_BUF_READY, // yet we have not sent the IPC_MBOX_TX_PKT_BUF_READY message. If we happen to reboot here, // this packet buffer will be abandoned and won't be cleaned up in the boot process. This is a narrow // race in practice, but step-by-step debugging can accentuate the risk since there can be an arbitrary // amount of time spent in this window. if(unlock_tx_pkt_buf(tx_pkt_buf) == PKT_BUF_MUTEX_FAIL_NOT_LOCK_OWNER){ // The unlock failed because CPU_LOW currently has the mutex lock. We will not // submit a READY message for this packet. Instead, we'll drop it and revert // the state of the packet buffer to TX_PKT_BUF_HIGH_CTRL. CPU_LOW will unlock the // packet buffer when it is done transmitting from it or if it reboots. At that // point, we will clean up and be able to use this packet buffer again for future // transmissions. wlan_printf(PL_ERROR, "Error: unable to unlock tx pkt_buf %d\n",tx_pkt_buf); tx_frame_info->tx_pkt_buf_state = TX_PKT_BUF_HIGH_CTRL; } else { // We successfully unlocked the packet buffer or we failed to unlock it because // it was already unlocked. In either case, we can submit this READY message. write_mailbox_msg(&ipc_msg_to_low); } } /** * @brief WLAN MAC IPC processing function for CPU High * * Process IPC message from CPU low * * @param wlan_ipc_msg* msg * - Pointer to the IPC message * @param u32* ipc_msg_from_low_payload * - Pointer to the payload of the IPC message * @return None */ void wlan_mac_high_process_ipc_msg(wlan_ipc_msg_t* msg, u32* ipc_msg_from_low_payload) { u8 rx_pkt_buf; u8 tx_pkt_buf; rx_frame_info_t* rx_frame_info; tx_frame_info_t* tx_frame_info; // Determine what type of message this is switch(IPC_MBOX_MSG_ID_TO_MSG(msg->msg_id)) { //--------------------------------------------------------------------- case IPC_MBOX_TX_BEACON_DONE:{ wlan_mac_low_tx_details_t* tx_low_details; tx_low_entry* tx_low_event_log_entry = NULL; station_info_t* station_info; tx_pkt_buf = msg->arg0; if(tx_pkt_buf == TX_PKT_BUF_BEACON){ if(lock_tx_pkt_buf(tx_pkt_buf) != PKT_BUF_MUTEX_SUCCESS){ xil_printf("Error: CPU_LOW had lock on Beacon packet buffer during IPC_MBOX_TX_BEACON_DONE\n"); } else { tx_frame_info = (tx_frame_info_t*)CALC_PKT_BUF_ADDR(platform_common_dev_info.tx_pkt_buf_baseaddr, tx_pkt_buf); tx_low_details = (wlan_mac_low_tx_details_t*)(msg->payload_ptr); tx_frame_info->tx_pkt_buf_state = TX_PKT_BUF_HIGH_CTRL; //We will pass this completed transmission off to the Station Info subsystem station_info_posttx_process((void*)(CALC_PKT_BUF_ADDR(platform_common_dev_info.tx_pkt_buf_baseaddr, tx_pkt_buf))); #if WLAN_SW_CONFIG_ENABLE_LOGGING // Log the TX low tx_low_event_log_entry = wlan_exp_log_create_tx_low_entry(tx_frame_info, tx_low_details); #endif beacon_tx_done_callback( tx_frame_info, tx_low_details, tx_low_event_log_entry ); // Apply latest broadcast management tx_params in case that they have changed station_info = station_info_create((u8*)bcast_addr); tx_frame_info->params = station_info->tx_params_mgmt; tx_frame_info->tx_pkt_buf_state = TX_PKT_BUF_READY; if(unlock_tx_pkt_buf(tx_pkt_buf) != PKT_BUF_MUTEX_SUCCESS){ xil_printf("Error: Unable to unlock Beacon packet buffer during IPC_MBOX_TX_BEACON_DONE\n"); return; } } } else { xil_printf("Error: IPC_MBOX_TX_BEACON_DONE with invalid pkt buf index %d\n ", tx_pkt_buf); } } break; //--------------------------------------------------------------------- case IPC_MBOX_RX_PKT_BUF_READY: { // CPU Low has received an MPDU addressed to this node or to the broadcast address station_info_t* station_info; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-but-set-variable" // Depending on the state of WLAN_SW_CONFIG_ENABLE_TXRX_COUNTS, this variable // may be assigned but never used. We'll suppress the warning if it shows up. u32 mpdu_rx_process_flags; #pragma GCC diagnostic pop rx_common_entry* rx_event_log_entry = NULL; rx_pkt_buf = msg->arg0; if(rx_pkt_buf < NUM_RX_PKT_BUFS){ rx_frame_info = (rx_frame_info_t*)CALC_PKT_BUF_ADDR(platform_common_dev_info.rx_pkt_buf_baseaddr, rx_pkt_buf); switch(rx_frame_info->rx_pkt_buf_state){ case RX_PKT_BUF_READY: // Normal Rx process - buffer contains packet ready for de-encap and logging // Attempt to lock the indicated Rx pkt buf (CPU Low must unlock it before sending this msg) if(lock_rx_pkt_buf(rx_pkt_buf) != PKT_BUF_MUTEX_SUCCESS){ wlan_printf(PL_ERROR, "Error: unable to lock pkt_buf %d\n", rx_pkt_buf); } else { rx_frame_info->rx_pkt_buf_state = RX_PKT_BUF_HIGH_CTRL; //Before calling the user's callback, we'll pass this reception off to the BSS info subsystem so it can scrape for BSS metadata network_info_rx_process((void*)(CALC_PKT_BUF_ADDR(platform_common_dev_info.rx_pkt_buf_baseaddr, rx_pkt_buf))); //We will also pass this reception off to the Station Info subsystem station_info = station_info_postrx_process((void*)(CALC_PKT_BUF_ADDR(platform_common_dev_info.rx_pkt_buf_baseaddr, rx_pkt_buf))); #if WLAN_SW_CONFIG_ENABLE_LOGGING //Log this RX event rx_event_log_entry = wlan_exp_log_create_rx_entry(rx_frame_info); #endif // Call the RX callback function to process the received packet mpdu_rx_process_flags = mpdu_rx_callback((void*)(CALC_PKT_BUF_ADDR(platform_common_dev_info.rx_pkt_buf_baseaddr, rx_pkt_buf)), station_info, rx_event_log_entry); station_info_rx_process_hostname( (void*)(CALC_PKT_BUF_ADDR(platform_common_dev_info.rx_pkt_buf_baseaddr, rx_pkt_buf)), station_info ); #if WLAN_SW_CONFIG_ENABLE_TXRX_COUNTS if( (mpdu_rx_process_flags & MAC_RX_CALLBACK_RETURN_FLAG_NO_COUNTS) == 0 ){ if(mpdu_rx_process_flags & MAC_RX_CALLBACK_RETURN_FLAG_DUP){ station_info_rx_process_counts((void*)(CALC_PKT_BUF_ADDR(platform_common_dev_info.rx_pkt_buf_baseaddr, rx_pkt_buf)), station_info, RX_PROCESS_COUNTS_OPTION_FLAG_IS_DUPLICATE); } else { station_info_rx_process_counts((void*)(CALC_PKT_BUF_ADDR(platform_common_dev_info.rx_pkt_buf_baseaddr, rx_pkt_buf)), station_info, 0); } } #endif if(unlock_rx_pkt_buf(rx_pkt_buf) != PKT_BUF_MUTEX_SUCCESS){ wlan_printf(PL_ERROR, "Error: unable to unlock rx pkt_buf %d\n", rx_pkt_buf); } // Free up the rx_pkt_buf rx_frame_info->rx_pkt_buf_state = RX_PKT_BUF_LOW_CTRL; } break; default: case RX_PKT_BUF_HIGH_CTRL: // Don't de-encap - just clean up and return rx_frame_info->rx_pkt_buf_state = RX_PKT_BUF_LOW_CTRL; case RX_PKT_BUF_UNINITIALIZED: case RX_PKT_BUF_LOW_CTRL: if(unlock_rx_pkt_buf(rx_pkt_buf) == PKT_BUF_MUTEX_SUCCESS){ wlan_printf(PL_ERROR, "Error: state mismatch; CPU_HIGH owned the lock on rx pkt_buf %d\n", rx_pkt_buf); } break; } } else { xil_printf("Error: IPC_MBOX_RX_MPDU_READY with invalid pkt buf index %d\n ", rx_pkt_buf); } } break; //--------------------------------------------------------------------- case IPC_MBOX_PHY_TX_REPORT: { wlan_mac_low_tx_details_t* tx_low_details; tx_low_entry* tx_low_event_log_entry = NULL; station_info_t* station_info; tx_pkt_buf = msg->arg0; if(tx_pkt_buf < NUM_TX_PKT_BUFS){ tx_frame_info = (tx_frame_info_t*)CALC_PKT_BUF_ADDR(platform_common_dev_info.tx_pkt_buf_baseaddr, tx_pkt_buf); tx_low_details = (wlan_mac_low_tx_details_t*)(msg->payload_ptr); #if WLAN_SW_CONFIG_ENABLE_LOGGING tx_low_event_log_entry = wlan_exp_log_create_tx_low_entry(tx_frame_info, tx_low_details); #endif //WLAN_SW_CONFIG_ENABLE_LOGGING station_info = station_info_txreport_process((void*)(CALC_PKT_BUF_ADDR(platform_common_dev_info.tx_pkt_buf_baseaddr, tx_pkt_buf)), tx_low_details); mpdu_tx_low_done_callback(tx_frame_info, station_info, tx_low_details, tx_low_event_log_entry); } } break; //--------------------------------------------------------------------- case IPC_MBOX_TX_PKT_BUF_DONE: { // CPU Low has finished the Tx process for the previously submitted-accepted frame // CPU High should do any necessary post-processing, then recycle the packet buffer // tx_high_entry* tx_high_event_log_entry = NULL; station_info_t* station_info; tx_pkt_buf = msg->arg0; if(tx_pkt_buf < NUM_TX_PKT_BUFS){ tx_frame_info = (tx_frame_info_t*)CALC_PKT_BUF_ADDR(platform_common_dev_info.tx_pkt_buf_baseaddr, tx_pkt_buf); switch(tx_frame_info->tx_pkt_buf_state){ case TX_PKT_BUF_DONE: // Expected state after CPU Low finishes Tx // Run normal post-tx processing // Lock this packet buffer if(lock_tx_pkt_buf(tx_pkt_buf) != PKT_BUF_MUTEX_SUCCESS){ xil_printf("Error: DONE Lock Tx Pkt Buf State Mismatch\n"); tx_frame_info->tx_pkt_buf_state = TX_PKT_BUF_HIGH_CTRL; return; } //We can now attempt to dequeue any pending transmissions before we fully process //this done message. tx_poll_callback(); //We will pass this completed transmission off to the Station Info subsystem station_info = station_info_posttx_process((void*)(CALC_PKT_BUF_ADDR(platform_common_dev_info.tx_pkt_buf_baseaddr, tx_pkt_buf))); #if WLAN_SW_CONFIG_ENABLE_LOGGING // Log the high-level transmission and call the application callback tx_high_event_log_entry = wlan_exp_log_create_tx_high_entry(tx_frame_info); #endif //WLAN_SW_CONFIG_ENABLE_LOGGING mpdu_tx_high_done_callback(tx_frame_info, station_info, tx_high_event_log_entry); tx_frame_info->tx_pkt_buf_state = TX_PKT_BUF_HIGH_CTRL; break; // Something has gone wrong - TX_DONE message disagrees // with state of Tx pkt buf case TX_PKT_BUF_UNINITIALIZED: case TX_PKT_BUF_HIGH_CTRL: // CPU High probably rebooted, initialized Tx pkt buffers // then got TX_DONE message from pre-reboot // Ignore the contents, force-lock the buffer, and // leave it TX_PKT_BUF_HIGH_CTRL, will be used by future ping-pong rotation force_lock_tx_pkt_buf(tx_pkt_buf); tx_frame_info->tx_pkt_buf_state = TX_PKT_BUF_HIGH_CTRL; case TX_PKT_BUF_READY: case TX_PKT_BUF_LOW_CTRL: //CPU Low will clean up // Unlikely CPU High holds lock, but unlock just in case unlock_tx_pkt_buf(tx_pkt_buf); break; } } else { xil_printf("Error: IPC_MBOX_TX_PKT_BUF_DONE with invalid pkt buf index %d\n ", tx_pkt_buf); } } break; //--------------------------------------------------------------------- case IPC_MBOX_CPU_STATUS: // CPU low's status // // cpu_low_status isn't needed to process this IPC message since there is an explicit // reason provided in the arg0 field of the message. However, we'll copy the information // into the global in case any future process wants record of it. cpu_low_status = ipc_msg_from_low_payload[0]; switch( msg->arg0 ){ case CPU_STATUS_REASON_EXCEPTION: wlan_printf(PL_ERROR, "ERROR: An unrecoverable exception has occurred in CPU_LOW, halting...\n"); wlan_printf(PL_ERROR, " Reason code: %d\n", ipc_msg_from_low_payload[1]); wlan_platform_high_userio_disp_status(USERIO_DISP_STATUS_CPU_ERROR, WLAN_ERROR_CPU_STOP); break; case CPU_STATUS_REASON_BOOTED: // Notify the high-level project that CPU_LOW has rebooted cpu_low_reboot_callback(ipc_msg_from_low_payload[1]); // Set any of low parameters that have been modified and // the MAC High Framework is responsible for tracking if(low_param_channel != 0xFFFFFFFF) wlan_mac_high_set_radio_channel(low_param_channel); if(low_param_dsss_en != 0xFFFFFFFF) wlan_mac_high_set_dsss(low_param_dsss_en); if(low_param_rx_ant_mode != 0xFF) wlan_mac_high_set_rx_ant_mode(low_param_rx_ant_mode); if(low_param_tx_ctrl_pow != -127) wlan_mac_high_set_tx_ctrl_power(low_param_tx_ctrl_pow); if(low_param_radio_tx_pow != -127) wlan_mac_high_set_radio_tx_power(low_param_radio_tx_pow); if(low_param_rx_filter != 0xFFFFFFFF) wlan_mac_high_set_rx_filter_mode(low_param_rx_filter); if(low_param_random_seed != 0xFFFFFFFF) wlan_mac_high_set_srand(low_param_random_seed); // Attempt to dequeue and fill any available Tx packet buffers tx_poll_callback(); break; case CPU_STATUS_REASON_RESPONSE: // Set the CPU_LOW wlan_exp type #if WLAN_SW_CONFIG_ENABLE_WLAN_EXP { compilation_details_t* compilation_details = (compilation_details_t*)(&ipc_msg_from_low_payload[2]); wlan_exp_node_set_type_low(ipc_msg_from_low_payload[1], compilation_details->compilation_date, compilation_details->compilation_time); } #endif default: break; } break; //--------------------------------------------------------------------- case IPC_MBOX_MEM_READ_WRITE: // Memory Read / Write message // - Allows CPU High to read / write arbitrary memory locations in CPU low // if(cpu_low_reg_read_buffer != NULL){ memcpy( (u8*)cpu_low_reg_read_buffer, (u8*)ipc_msg_from_low_payload, (msg->num_payload_words) * sizeof(u32)); cpu_low_reg_read_buffer_status = CPU_LOW_REG_READ_BUFFER_STATUS_READY; } else { wlan_printf(PL_ERROR, "ERROR: Received low-level register buffer from CPU_LOW and was not expecting it.\n"); } break; //--------------------------------------------------------------------- case IPC_MBOX_LOW_PARAM: // CPU Low Parameter IPC message // - Processes any CPU Low Parameter IPC messages sent to CPU High // - This is an error condition. CPU High should never expect this IPC message // // NOTE: This is due to the fact that IPC messages in CPU low can take an infinitely long amount of // to return given that the sending and receiving of wireless data takes precedent. Therefore, // it is not good to try to return values from CPU low since there is no guarantee when the values // will be available. // wlan_printf(PL_ERROR, "ERROR: Received low-level parameter buffer from CPU_LOW and was not expecting it.\n"); break; //--------------------------------------------------------------------- default: wlan_printf(PL_ERROR, "ERROR: Unknown IPC message type %d\n", IPC_MBOX_MSG_ID_TO_MSG(msg->msg_id)); break; } } /** * @brief Set Random Seed * * Send an IPC message to CPU Low to set the Random Seed * * @param u32 seed * - Random number generator seed * @return None */ void wlan_mac_high_set_srand(u32 seed) { wlan_ipc_msg_t ipc_msg_to_low; u32 ipc_msg_to_low_payload = seed; //Set tracking global low_param_random_seed = seed; // Send message to CPU Low ipc_msg_to_low.msg_id = IPC_MBOX_MSG_ID(IPC_MBOX_LOW_RANDOM_SEED); ipc_msg_to_low.num_payload_words = 1; ipc_msg_to_low.payload_ptr = &(ipc_msg_to_low_payload); write_mailbox_msg(&ipc_msg_to_low); } /** * @brief Convert BSS Channel Specification to Radio Channel * * Converts a BSS channel specification to a radio channel * for use in wlan_mac_high_set_radio_channel. When extended * to support HT40, this function will grow more complex. * * @param chan_spec_t* chan_spec * - Pointer to BSS Channel Specification * @return None */ u8 wlan_mac_high_bss_channel_spec_to_radio_chan(chan_spec_t chan_spec) { return chan_spec.chan_pri; } /** * @brief Set Radio Channel * * Send an IPC message to CPU Low to set the MAC Channel * * @param u32 mac_channel * - 802.11 Channel to set * @return None */ void wlan_mac_high_set_radio_channel(u32 mac_channel) { wlan_ipc_msg_t ipc_msg_to_low; u32 ipc_msg_to_low_payload = mac_channel; if(wlan_verify_channel(mac_channel) == WLAN_SUCCESS){ //Set tracking global low_param_channel = mac_channel; // Send message to CPU Low ipc_msg_to_low.msg_id = IPC_MBOX_MSG_ID(IPC_MBOX_CONFIG_CHANNEL); ipc_msg_to_low.num_payload_words = 1; ipc_msg_to_low.payload_ptr = &(ipc_msg_to_low_payload); write_mailbox_msg(&ipc_msg_to_low); } else { xil_printf("Channel %d not allowed\n", mac_channel); } } void wlan_mac_high_enable_mcast_buffering(u8 enable){ wlan_ipc_msg_t ipc_msg_to_low; // Send message to CPU Low ipc_msg_to_low.msg_id = IPC_MBOX_MSG_ID(IPC_MBOX_MCAST_BUFFER_ENABLE); ipc_msg_to_low.num_payload_words = 0; ipc_msg_to_low.arg0 = enable; write_mailbox_msg(&ipc_msg_to_low); } void wlan_mac_high_config_txrx_beacon(beacon_txrx_config_t* beacon_txrx_config){ wlan_ipc_msg_t ipc_msg_to_low; // Send message to CPU Low ipc_msg_to_low.msg_id = IPC_MBOX_MSG_ID(IPC_MBOX_TXRX_BEACON_CONFIG); ipc_msg_to_low.num_payload_words = sizeof(beacon_txrx_config_t)/sizeof(u32); ipc_msg_to_low.payload_ptr = (u32*)beacon_txrx_config; write_mailbox_msg(&ipc_msg_to_low); } /** * @brief Set Rx Antenna Mode * * Send an IPC message to CPU Low to set the Rx antenna mode. * * @param u8 ant_mode * - Antenna mode selection * @return None */ void wlan_mac_high_set_rx_ant_mode(u8 ant_mode) { wlan_ipc_msg_t ipc_msg_to_low; u32 ipc_msg_to_low_payload = (u32)ant_mode; //Sanity check input switch(ant_mode){ case RX_ANTMODE_SISO_ANTA: case RX_ANTMODE_SISO_ANTB: case RX_ANTMODE_SISO_ANTC: case RX_ANTMODE_SISO_ANTD: case RX_ANTMODE_SISO_SELDIV_2ANT: //Set tracking global low_param_rx_ant_mode = ant_mode; break; default: xil_printf("Error: unsupported antenna mode %x\n", ant_mode); return; break; } // Send message to CPU Low ipc_msg_to_low.msg_id = IPC_MBOX_MSG_ID(IPC_MBOX_CONFIG_RX_ANT_MODE); ipc_msg_to_low.num_payload_words = 1; ipc_msg_to_low.payload_ptr = &(ipc_msg_to_low_payload); write_mailbox_msg(&ipc_msg_to_low); } /** * @brief Set Tx Control Packet Power * * Send an IPC message to CPU Low to set the Tx control packet power * * @param s8 pow * - Tx control packet power in dBm * @return None */ void wlan_mac_high_set_tx_ctrl_power(s8 pow) { wlan_ipc_msg_t ipc_msg_to_low; u32 ipc_msg_to_low_payload = (u32)pow; //Set tracking global low_param_tx_ctrl_pow = pow; // Send message to CPU Low ipc_msg_to_low.msg_id = IPC_MBOX_MSG_ID(IPC_MBOX_CONFIG_TX_CTRL_POW); ipc_msg_to_low.num_payload_words = 1; ipc_msg_to_low.payload_ptr = &(ipc_msg_to_low_payload); write_mailbox_msg(&ipc_msg_to_low); } /** * @brief Set Tx Control Packet Power * * Send an IPC message to CPU Low to set the radio's Tx power; applies to * platforms which do not support per-packet Tx power control * * @param s8 pow * - Tx power in dBm * @return None */ void wlan_mac_high_set_radio_tx_power(s8 pow) { wlan_ipc_msg_t ipc_msg_to_low; u32 ipc_msg_to_low_payload = (u32)pow; //Set tracking global low_param_radio_tx_pow = pow; // Send message to CPU Low ipc_msg_to_low.msg_id = IPC_MBOX_MSG_ID(IPC_MBOX_SET_RADIO_TX_POWER); ipc_msg_to_low.num_payload_words = 1; ipc_msg_to_low.payload_ptr = &(ipc_msg_to_low_payload); write_mailbox_msg(&ipc_msg_to_low); } /** * @brief Set Rx Filter * * Send an IPC message to CPU Low to set the filter for receptions. This will * allow or disallow different packets from being passed up to CPU_High * * @param filter_mode * - RX_FILTER_FCS_GOOD * - RX_FILTER_FCS_ALL * - RX_FILTER_ADDR_STANDARD (unicast to me or multicast) * - RX_FILTER_ADDR_ALL_MPDU (all MPDU frames to any address) * - RX_FILTER_ADDR_ALL (all observed frames, including control) * * @note FCS and ADDR filter selections must be bit-wise ORed together. For example, * wlan_mac_high_set_rx_filter_mode(RX_FILTER_FCS_ALL | RX_FILTER_ADDR_ALL) * * @return None */ void wlan_mac_high_set_rx_filter_mode(u32 filter_mode) { wlan_ipc_msg_t ipc_msg_to_low; u32 ipc_msg_to_low_payload = (u32)filter_mode; //Set tracking global low_param_rx_filter = filter_mode; // Send message to CPU Low ipc_msg_to_low.msg_id = IPC_MBOX_MSG_ID(IPC_MBOX_CONFIG_RX_FILTER); ipc_msg_to_low.num_payload_words = 1; ipc_msg_to_low.payload_ptr = &(ipc_msg_to_low_payload); write_mailbox_msg(&ipc_msg_to_low); } /** * @brief Write a memory location in CPU low * * Send an IPC message to CPU Low to write the given data * * @param u32 num_words - Number of words in the message payload * @param u32* payload - Pointer to the message payload * * @return int - WLAN_SUCCESS or WLAN_FAILURE */ int wlan_mac_high_write_low_mem(u32 num_words, u32* payload) { wlan_ipc_msg_t ipc_msg_to_low; if (num_words > MAILBOX_MSG_MAX_NUM_WORDS) { xil_printf("ERROR: attempted to send LOW_PARAM IPC message with oversize payload! (%d > %d)\n", num_words, MAILBOX_MSG_MAX_NUM_WORDS); return WLAN_FAILURE; } // Send message to CPU Low ipc_msg_to_low.msg_id = IPC_MBOX_MSG_ID(IPC_MBOX_MEM_READ_WRITE); ipc_msg_to_low.num_payload_words = num_words; ipc_msg_to_low.arg0 = IPC_REG_WRITE_MODE; ipc_msg_to_low.payload_ptr = payload; write_mailbox_msg(&ipc_msg_to_low); return WLAN_SUCCESS; } /** * @brief Read a memory location in CPU low * * Send an IPC message to CPU Low to read the given data * * @param u32 num_words - Number of words to read from CPU low * @param u32 baseaddr - Base address of the data to read from CPU low * @param u32* payload - Pointer to the buffer to be populated with data * * @return int - WLAN_SUCCESS or WLAN_FAILURE */ int wlan_mac_high_read_low_mem(u32 num_words, u32 baseaddr, u32* payload) { u64 start_timestamp; wlan_ipc_msg_t ipc_msg_to_low; ipc_reg_read_write_t ipc_msg_to_low_payload; if(wlan_platform_intc_get_state() == INTERRUPTS_ENABLED) { // Send message to CPU Low ipc_msg_to_low.msg_id = IPC_MBOX_MSG_ID(IPC_MBOX_MEM_READ_WRITE); ipc_msg_to_low.num_payload_words = sizeof(ipc_reg_read_write_t) / sizeof(u32); ipc_msg_to_low.arg0 = IPC_REG_READ_MODE; ipc_msg_to_low.payload_ptr = (u32*)(&(ipc_msg_to_low_payload)); ipc_msg_to_low_payload.baseaddr = baseaddr; ipc_msg_to_low_payload.num_words = num_words; // Set the read buffer to the payload pointer cpu_low_reg_read_buffer = payload; cpu_low_reg_read_buffer_status = CPU_LOW_REG_READ_BUFFER_STATUS_NOT_READY; write_mailbox_msg(&ipc_msg_to_low); // Get start time start_timestamp = get_system_time_usec(); // Wait for CPU low to finish the read or timeout to occur while(cpu_low_reg_read_buffer_status != CPU_LOW_REG_READ_BUFFER_STATUS_READY){ if ((get_system_time_usec() - start_timestamp) > WLAN_EXP_CPU_LOW_DATA_REQ_TIMEOUT) { xil_printf("Error: Reading CPU_LOW memory timed out\n"); // Reset the read buffer cpu_low_reg_read_buffer = NULL; return WLAN_FAILURE; } } // Reset the read buffer cpu_low_reg_read_buffer = NULL; } else { xil_printf("ERROR (wlan_mac_high_read_low_mem): cannot read low memory when interrupts are disabled\n"); return WLAN_FAILURE; } return WLAN_SUCCESS; } /** * @brief Write a parameter in CPU low * * Send an IPC message to CPU Low to write the given parameter * * @param u32 num_words - Number of words in the message payload * @param u32 * payload - Pointer to the message payload (includes parameter ID) * * @return int - WLAN_SUCCESS or WLAN_FAILURE */ int wlan_mac_high_write_low_param(u32 num_words, u32* payload) { wlan_ipc_msg_t ipc_msg_to_low; if (num_words > MAILBOX_MSG_MAX_NUM_WORDS) { xil_printf("ERROR: attempted to send LOW_PARAM IPC message with oversize payload! (%d > %d)\n", num_words, MAILBOX_MSG_MAX_NUM_WORDS); return WLAN_FAILURE; } // Send message to CPU Low ipc_msg_to_low.msg_id = IPC_MBOX_MSG_ID(IPC_MBOX_LOW_PARAM); ipc_msg_to_low.num_payload_words = num_words; ipc_msg_to_low.arg0 = IPC_REG_WRITE_MODE; ipc_msg_to_low.payload_ptr = payload; write_mailbox_msg(&ipc_msg_to_low); return WLAN_SUCCESS; } /** * @brief Enable/Disable DSSS * * Send an IPC message to CPU Low to set the DSSS value * * @param u32 dsss_value * - DSSS Enable/Disable value * @return None */ void wlan_mac_high_set_dsss(u32 dsss_value) { wlan_ipc_msg_t ipc_msg_to_low; u32 ipc_msg_to_low_payload = dsss_value; //Set tracking global low_param_dsss_en = dsss_value; // Send message to CPU Low ipc_msg_to_low.msg_id = IPC_MBOX_MSG_ID(IPC_MBOX_CONFIG_DSSS_EN); ipc_msg_to_low.num_payload_words = 1; ipc_msg_to_low.payload_ptr = &(ipc_msg_to_low_payload); write_mailbox_msg(&ipc_msg_to_low); } /** * @brief Get CPU low's state * * Send an IPC message to CPU Low to get its state * * @param None * @return None */ void wlan_mac_high_request_low_state(){ wlan_ipc_msg_t ipc_msg_to_low; // Send message to CPU Low ipc_msg_to_low.msg_id = IPC_MBOX_MSG_ID(IPC_MBOX_CPU_STATUS); ipc_msg_to_low.num_payload_words = 0; ipc_msg_to_low.arg0 = (u8)CPU_STATUS_REASON_BOOTED; write_mailbox_msg(&ipc_msg_to_low); } /** * @brief Check that CPU low is initialized * * @param None * @return int * - 0 if CPU low is not initialized * - 1 if CPU low is initialized */ int wlan_mac_high_is_cpu_low_initialized(){ wlan_mac_high_ipc_rx(); return ( (cpu_low_status & CPU_STATUS_INITIALIZED) != 0 ); } /** * @brief Check the number of available tx packet buffers for an available group * * @param pkt_buf_group_t pkt_buf_group * @return int * - number of tx packet buffers that can be filled */ inline u8 wlan_mac_num_tx_pkt_buf_available(pkt_buf_group_t pkt_buf_group) { u8 i, num_empty, num_low_owned; tx_frame_info_t* tx_frame_info; num_empty = 0; num_low_owned = 0; for( i = 0; i < NUM_TX_PKT_BUF_MPDU; i++ ) { tx_frame_info = (tx_frame_info_t*)CALC_PKT_BUF_ADDR(platform_common_dev_info.tx_pkt_buf_baseaddr, i); if( tx_frame_info->tx_pkt_buf_state == TX_PKT_BUF_HIGH_CTRL ) { num_empty++; } if( (tx_frame_info->queue_info.pkt_buf_group == pkt_buf_group) && ( (tx_frame_info->tx_pkt_buf_state == TX_PKT_BUF_READY) || (tx_frame_info->tx_pkt_buf_state == TX_PKT_BUF_LOW_CTRL))) { num_low_owned++; } } // The first requirement for being allowed to dequeue is that there is at least one empty packet buffer. // The second requirement for being allowed to dequeue is that no more than one packet buffer is currently // in the TX_PKT_BUF_READY or TX_PKT_BUF_LOW_CTRL for pkt_buf_group of PKT_BUF_GROUP_GENERAL and no more than two // for PKT_BUF_GROUP_DTIM_MCAST if(num_empty == 0) { return 0; } if(pkt_buf_group==PKT_BUF_GROUP_GENERAL) { if(num_low_owned > 2) { // Should never happen - clip to 2 to restore sanity from here on xil_printf("WARNING: wlan_mac_num_tx_pkt_buf_available found %d GENERAL buffers owned by low!\n", num_low_owned); num_low_owned = 2; } // Return 1 (if one buffer is already filled) or 2 (if both can be filled) return WLAN_MIN(num_empty, (2 - num_low_owned)); } else if(pkt_buf_group==PKT_BUF_GROUP_DTIM_MCAST) { if(num_low_owned > 3) { // Should never happen - clip to 3 to restore sanity from here on xil_printf("WARNING: wlan_mac_num_tx_pkt_buf_available found %d DTIM_MCAST buffers owned by low!\n", num_low_owned); num_low_owned = 3; } // Return 3 if all buffers are available, otherwise 1 or 2 return WLAN_MIN(num_empty, (3 - num_low_owned)); } else { // Invalid packet buffer group return 0; } } /** * @brief Return the index of the next free transmit packet buffer * * @param None * @return int * - packet buffer index of free, now-locked packet buffer * - -1 if there are no free Tx packet buffers */ int wlan_mac_high_get_empty_tx_packet_buffer(){ //TODO: Debate: This function assumes that it is currently safe to take control // of an empty Tx packet buffer. In other words, it is the responsibility of the // the calling function to ensure that wlan_mac_is_tx_pkt_buf_available() == 1. // For extra safety, we could call wlan_mac_is_tx_pkt_buf_available() here at the // expensive of another search through the three packet buffers. u8 i; int pkt_buf_sel = -1; for( i = 0; i < NUM_TX_PKT_BUF_MPDU; i++ ){ if( ((tx_frame_info_t*)CALC_PKT_BUF_ADDR(platform_common_dev_info.tx_pkt_buf_baseaddr, i))->tx_pkt_buf_state == TX_PKT_BUF_HIGH_CTRL ){ pkt_buf_sel = i; break; } } return pkt_buf_sel; } /** * @brief Determine if Packet is LTG * This function inspects the payload of the packet provided as an argument * and searches for the LTG-specific LLC header. If it finds such a header, * it returns a 1. * * @param None * @return u8 * - 1 if LTG * - 0 if not LTG */ u8 wlan_mac_high_is_pkt_ltg(void* mac_payload, u16 length){ mac_header_80211* hdr_80211; llc_header_t* llc_hdr; hdr_80211 = (mac_header_80211*)((void*)mac_payload); if((hdr_80211->frame_control_1 & 0xF) == MAC_FRAME_CTRL1_TYPE_DATA) { //Check if this is an encrypted packet. If it is, we can't trust any of the MPDU //payload bytes for further classification if(hdr_80211->frame_control_2 & MAC_FRAME_CTRL2_FLAG_PROTECTED){ return 0; } llc_hdr = (llc_header_t*)((u8*)mac_payload + sizeof(mac_header_80211)); if(length < (sizeof(mac_header_80211) + sizeof(llc_header_t) + WLAN_PHY_FCS_NBYTES)){ // This was a DATA packet, but it wasn't long enough to have an LLC header. return 0; } else { if(llc_hdr->type == LLC_TYPE_WLAN_LTG){ return 1; } } } return 0; } /** * @brief Configure Beacon Transmissions * * This function will create a beacon and inform CPU_LOW to transmit it periodically. * */ int wlan_mac_high_configure_beacon_tx_template(u8* addr1, u8* addr2, u8* addr3, network_info_t* network_info, tx_params_t* tx_params_ptr, u8 flags) { u16 tx_length; tx_frame_info_t* tx_frame_info = (tx_frame_info_t*)CALC_PKT_BUF_ADDR(platform_common_dev_info.tx_pkt_buf_baseaddr, TX_PKT_BUF_BEACON); if(lock_tx_pkt_buf(TX_PKT_BUF_BEACON) != PKT_BUF_MUTEX_SUCCESS){ xil_printf("Error: CPU_LOW had lock on Beacon packet buffer during initial configuration\n"); return WLAN_FAILURE; } // Fill in the data tx_length = wlan_create_beacon_frame( (u8*)(tx_frame_info)+PHY_TX_PKT_BUF_MPDU_OFFSET, addr1, addr2, addr3, network_info); bzero(tx_frame_info, sizeof(tx_frame_info_t)); // Set up frame info data tx_frame_info->queue_info.enqueue_timestamp = get_mac_time_usec(); tx_frame_info->length = tx_length; tx_frame_info->flags = flags; tx_frame_info->queue_info.id = 0xFF; tx_frame_info->queue_info.pkt_buf_group = PKT_BUF_GROUP_OTHER; tx_frame_info->queue_info.occupancy = 0; // Unique_seq will be filled in by CPU_LOW tx_frame_info->unique_seq = 0; memcpy(&(tx_frame_info->params), tx_params_ptr, sizeof(tx_params_t)); tx_frame_info->tx_pkt_buf_state = TX_PKT_BUF_READY; if(unlock_tx_pkt_buf(TX_PKT_BUF_BEACON) != PKT_BUF_MUTEX_SUCCESS){ xil_printf("Error: Unable to unlock Beacon packet buffer during initial configuration\n"); return WLAN_FAILURE; } return WLAN_SUCCESS; } /** * @brief Update Beacon TX parameters * * This function will update the beacon template in the packet buffer with the * new TX parameters. * * This function should be used inside a while loop in order to make sure that * the update is successful: * while (wlan_mac_high_update_beacon_tx_params(&default_multicast_mgmt_tx_params) != 0) {} * * @param tx_params_ptr - Pointer to tx_params_t structure with new parameters * @return status - WLAN_SUCCESS or WLAN_FAILURE */ int wlan_mac_high_update_beacon_tx_params(tx_params_t* tx_params_ptr) { tx_frame_info_t* tx_frame_info = (tx_frame_info_t*)CALC_PKT_BUF_ADDR(platform_common_dev_info.tx_pkt_buf_baseaddr, TX_PKT_BUF_BEACON); if (lock_tx_pkt_buf(TX_PKT_BUF_BEACON) != PKT_BUF_MUTEX_SUCCESS) { xil_printf("Error: CPU_LOW had lock on Beacon packet buffer during initial configuration\n"); return WLAN_FAILURE; } memcpy(&(tx_frame_info->params), tx_params_ptr, sizeof(tx_params_t)); if (unlock_tx_pkt_buf(TX_PKT_BUF_BEACON) != PKT_BUF_MUTEX_SUCCESS) { xil_printf("Error: Unable to unlock Beacon packet buffer during initial configuration\n"); return WLAN_FAILURE; } return WLAN_SUCCESS; } void wlan_mac_sanitize_tx_params(station_info_t* station_info, tx_params_t* tx_params){ // Adjust MCS and PHY_MODE based upon STATION_INFO_CAPABILITIES_HT_CAPABLE flag // Requested HT MCS: 0 1 2 3 4 5 6 7 // Actual NONHT MCS: 0 2 3 4 5 6 7 7 if (station_info->capabilities & STATION_INFO_CAPABILITIES_HT_CAPABLE) { // Station is capable of HTMF waveforms -- no need to modify tx_params_ret } else { if (tx_params->phy.phy_mode == PHY_MODE_HTMF) { // Requested rate was HT, adjust the MCS corresponding to the table above tx_params->phy.phy_mode = PHY_MODE_NONHT; if ((tx_params->phy.mcs == 0) || (tx_params->phy.mcs == 7)) { // Do nothing - HT MCS[0,7] map to NONHT[0,7] } else { tx_params->phy.mcs = tx_params->phy.mcs + 1; } } else { // Requested rate was non-HT, so do not adjust MCS } } return; } inline void wlan_mac_poll(){ // Poll the platform in case any I/O is not interrupt-based wlan_platform_high_poll(); #if WLAN_SW_CONFIG_ENABLE_WLAN_EXP // Poll received, filtered UDP frames for wlan_exp wlan_exp_transport_poll(); #endif } int wlan_mac_enqueue_wireless_tx(u16 queue_id, dl_entry* queue_entry){ int status; if (queue_enqueue_allowed(queue_id) == 0){ xil_printf("ERROR (wlan_mac_enqueue_wireless_tx): queue_id %d enqueue rejected\n", queue_id); return WLAN_FAILURE; } // Update the occupancy of the tx queue for the tx_queue_element // NOTE: This is the best place to record this value since it will catch all cases. However, // when populating the tx_frame_info, be careful to not overwrite this value. Also, the // occupancy value includes itself. // ((tx_80211_queue_buffer_t*)(queue_entry->data))->tx_queue_details.enqueue_timestamp = get_mac_time_usec(); ((tx_80211_queue_buffer_t*)(queue_entry->data))->tx_queue_details.occupancy = queue_length(queue_id)+1;//include this entry, which has not been added yet ((tx_80211_queue_buffer_t*)(queue_entry->data))->tx_queue_details.id = queue_id; //Increment the num_linked_queue_buffer field in the attached station_info_t. This will prevent // the framework from removing the station_info_t out from underneath us while this // packet is enqueued. ((tx_80211_queue_buffer_t*)(queue_entry->data))->station_info->num_linked_queue_buffer++; status = queue_enqueue(queue_id, queue_entry); // Poll the TX queues to see if anything needs to be transmitted tx_poll_callback(); return status; } void wlan_mac_transmit_wireless(dl_entry* queue_entry){ tx_80211_queue_buffer_t* tx_queue_buffer; int tx_pkt_buf = -1; tx_pkt_buf = wlan_mac_high_get_empty_tx_packet_buffer(); if (queue_entry == NULL) return; if( tx_pkt_buf != -1 ){ // Transmit the queue_entry // NOTE: This copies all the contents of the queue element to the // packet buffer so that the queue element can be safely returned // to the free pool tx_queue_buffer = (tx_80211_queue_buffer_t*)(queue_entry->data); // TODO: flush data cache here if caching is enabled - otherwise // the CMDA from DRAM will read stale DRAM contents // This is not the only place in our code that requires data cache flush/invalidation // Xil_DCacheFlushRange((unsigned int)tx_queue_buffer, 4096); wlan_mac_high_mpdu_transmit(tx_queue_buffer, tx_pkt_buf); //FIXME: there were two num_linked_queue_buffer decrements - one commented out as of this FIXME, leaving this // here in case it's related to the ongoing deauthenticate_station/wlan_mac_enqueue_wireless_tx bug search // Decrement the number of linked queue buffers in the station_info_t so the framework's // cleanup process can remove it later //tx_queue_buffer->station_info->num_linked_queue_buffer--; // Decrement the num_linked_queue_buffer field in the attached station_info_t. If this was the // last queued packet for this station, this will allow the framework to recycle this // station_info_t if it also has not been flagged as something to keep. ((tx_80211_queue_buffer_t*)(queue_entry->data))->station_info->num_linked_queue_buffer--; } else { xil_printf("Error in transmit_checkin(): no free Tx packet buffers. Packet was freed without being sent\n"); } // Check in the queue entry because it is no longer being used queue_checkin(queue_entry); } void wlan_mac_purge_wireless_tx(u16 queue_id){ tx_80211_queue_buffer_t* tx_queue_buffer; u32 num_queued; u32 i; volatile interrupt_state_t prev_interrupt_state; num_queued = queue_length(queue_id); prev_interrupt_state = wlan_platform_intc_stop(); if (num_queued > 0) { for (i = 0; i < num_queued; i++) { tx_queue_buffer = (tx_80211_queue_buffer_t*)queue_retrieve_buffer_from_index(queue_id, i); tx_queue_buffer->station_info->num_linked_queue_buffer--; } } queue_purge(queue_id); // Re-enable interrupts wlan_platform_intc_set_state(prev_interrupt_state); return; }