/** @file wlan_mac_ap.c * @brief Access Point * * This contains code for the 802.11 Access Point. * * @copyright Copyright 2013-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) */ // Xilinx SDK includes #include "stdlib.h" #include "string.h" #include "xil_cache.h" // 802.11 ref design headers #include "wlan_mac_high_sw_config.h" #include "wlan_mac_common.h" #include "wlan_platform_common.h" #include "wlan_platform_high.h" #include "wlan_mac_high.h" #include "wlan_mac_ap.h" #include "wlan_mac_addr_filter.h" #include "wlan_mac_ltg.h" #include "wlan_mac_event_log.h" #include "wlan_mac_schedule.h" #include "wlan_mac_queue.h" #include "wlan_mac_802_11_defs.h" #include "wlan_mac_packet_types.h" #include "wlan_mac_network_info.h" #include "wlan_mac_station_info.h" #include "wlan_mac_mgmt_tags.h" #include "wlan_mac_eth_util.h" #include "wlan_mac_scan.h" #include "wlan_mac_pkt_buf_util.h" #include "wlan_mac_entries.h" #include "wlan_mac_dl_list.h" #include "wlan_exp.h" #include "wlan_exp_node.h" #include "wlan_exp_node_ap.h" #include "wlan_exp_transport.h" /*************************** Constant Definitions ****************************/ #define WLAN_DEFAULT_BSS_CONFIG_CHANNEL 1 #define WLAN_DEFAULT_BSS_CONFIG_DTIM_PERIOD 2 #define WLAN_DEFAULT_BSS_CONFIG_BEACON_INTERVAL 100 // The WLAN_DEFAULT_BSS_CONFIG_HT_CAPABLE define will set the default // unicast TX phy mode to: 1 --> HTMF or 0 --> NONHT. #define WLAN_DEFAULT_BSS_CONFIG_HT_CAPABLE 1 #define WLAN_DEFAULT_TX_PWR 15 #define WLAN_DEFAULT_TX_ANTENNA TX_ANTMODE_SISO_ANTA #define WLAN_DEFAULT_RX_ANTENNA RX_ANTMODE_SISO_ANTA /*********************** Global Variable Definitions *************************/ /*************************** Variable Definitions ****************************/ // SSID variables static char default_AP_SSID[] = "MANGO-AP"; // "active_network_info" is a pointer to the network_info that describes this AP. // Inside this structure is a dl_list of members. This is a list // of all stations that are currently associated with this AP. In the // 802.11-2012 Section 10.3 parlance, these stations are currently // in State 4 (Authenticated, Associated). As part of the association // process, we want to track stations as they transition through the // 10.3 states. To do this, we'll create a totally separate dl_list of // station_info that represent stations in State 2 // (Authenticated, Unassociated). Only members of this list will be allowed // to elevate to State 4 in the active_bss_info. network_info_t* active_network_info; dl_list authenticated_unassociated_stations; // Tx queue variables; u32 max_queue_size; volatile u8 pause_data_queue; // MAC address static u8 wlan_mac_addr[MAC_ADDR_LEN]; // Traffic Indication Map (TIM) State // These global structs must be protected against externing. Any // modifications of these structs should be done via an explicit // setter that also updates the beacon template. static volatile mgmt_tag_template_t* mgmt_tag_tim_template; static volatile u32 mgmt_tag_tim_update_schedule_id; // Beacon configuration static beacon_txrx_config_t gl_beacon_txrx_config; // Static QIDs u16 mcast_qid; u16 mgmt_qid; // DTIM Multicast Buffer u8 gl_dtim_mcast_buffer_enable; // Enable buffering of multicast packets until after DTIM transmission u8 gl_cpu_low_supports_dtim_mcast; // Flag to indicate if DTIM multicast buffering is supported by the low-level MAC // Common Platform Device Info platform_common_dev_info_t platform_common_dev_info; /*************************** Functions Prototypes ****************************/ #if WLAN_SW_CONFIG_ENABLE_WLAN_EXP int wlan_exp_process_user_cmd(cmd_resp_hdr_t* cmd_hdr, eth_tx_queue_buffer_t* eth_tx_queue_buffer); #endif /******************************** Functions **********************************/ int main(){ // Call the platform-supplied cpu_init() first to setup any // processor-specific settings to enable sane execution // of the platform and framework code below wlan_platform_cpu_high_init(); u32 update_mask; bss_config_t bss_config; xil_printf("\f"); xil_printf("----- Mango 802.11 Reference Design -----\n"); xil_printf("----- v1.8.0 ----------------------------\n"); xil_printf("----- wlan_mac_ap -----------------------\n"); xil_printf("Compiled %s %s\n\n", __DATE__, __TIME__); // Heap Initialization // The heap must be initialized before any use of malloc. This explicit // init handles the case of soft-reset of the MicroBlaze leaving stale // values in the heap RAM wlan_mac_common_malloc_init(); //Initialize the MAC framework wlan_mac_high_init(); // Get the device info platform_common_dev_info = wlan_platform_common_get_dev_info(); // Before any queue entries are used, calculate the maximum number of // allowed enqueues based on the number that are free max_queue_size = WLAN_MIN( queue_num_free(), MAX_TX_QUEUE_LEN ); // Open static queues for multicast and management frames // We will use a single queue for all multicast frames. The occupancy of this // queue will be reflecting in the TIM tag in beacons, so we must attach a // function callback to be notified when the queue changes mcast_qid = queue_open((void*)queue_state_change, 0, max_queue_size); // We will use a queue for unicast management frames that are not addressed // to associated stations (e.g. a unicast probe response to a station // performing an active scan). Because there is no AID fo an unassociated // station, we do not need to be notified of occupancy changes for this // queue. So, we will attach the default null callback in the framework. mgmt_qid = queue_open((void*)wlan_null_callback, 0, max_queue_size); pause_data_queue = 0; gl_cpu_low_supports_dtim_mcast = 0; //Set a sane default for the TIM tag byte offset gl_beacon_txrx_config.dtim_tag_byte_offset = 0; // AP does not currently advertise a BSS configure_bss(NULL, 0); // Initialize hex display to "No BSS" wlan_platform_high_userio_disp_status(USERIO_DISP_STATUS_MEMBER_LIST_UPDATE, 0xFF); // Set default Tx params // Set sane default Tx params. These will be overwritten by the user application tx_params_t tx_params = { .phy = { .mcs = 3, .phy_mode = PHY_MODE_HTMF, .antenna_mode = WLAN_DEFAULT_TX_ANTENNA, .power = WLAN_DEFAULT_TX_PWR }, .mac = { .flags = 0 } }; wlan_mac_set_default_tx_params(unicast_data, &tx_params); tx_params.phy.mcs = 0; tx_params.phy.phy_mode = PHY_MODE_NONHT; wlan_mac_set_default_tx_params(unicast_mgmt, &tx_params); wlan_mac_set_default_tx_params(mcast_data, &tx_params); wlan_mac_set_default_tx_params(mcast_mgmt, &tx_params); // Re-apply the defaults to any existing station_info_t structs that this AP // knows about wlan_mac_reapply_default_tx_params(); // Initialize callbacks #if WLAN_SW_CONFIG_ENABLE_ETH_BRIDGE wlan_mac_util_set_eth_rx_callback((void*)ethernet_receive); #endif wlan_mac_high_set_mpdu_rx_callback((void*)mpdu_rx_process); wlan_mac_high_set_press_pb_0_callback((void*)button_0_press); wlan_mac_high_set_release_pb_0_callback((void*)button_0_release); wlan_mac_high_set_uart_rx_callback((void*)uart_rx); wlan_mac_high_set_poll_tx_queues_callback((void*)poll_tx_queues); wlan_mac_high_set_mpdu_dequeue_callback((void*)mpdu_dequeue); #if WLAN_SW_CONFIG_ENABLE_LTG wlan_mac_ltg_sched_set_callback((void*)ltg_event); #endif //WLAN_SW_CONFIG_ENABLE_LTG wlan_mac_scan_set_state_change_callback((void*)process_scan_state_change); wlan_mac_high_set_cpu_low_reboot_callback((void*)handle_cpu_low_reboot); wlan_mac_high_set_tx_queue_state_change_callback((void*)queue_state_change); #if WLAN_SW_CONFIG_ENABLE_WLAN_EXP // NOTE: To use the WLAN Experiments Framework, it must be initialized after // CPU low has populated the hw_info structure in the MAC High framework. // Initialize WLAN Exp wlan_exp_node_init(); // Set WLAN Exp callbacks wlan_exp_set_process_node_cmd_callback((void *) process_wlan_exp_app_cmd); wlan_exp_set_purge_all_wireless_tx_queue_callback((void *) purge_all_wireless_tx_queue); wlan_exp_set_process_user_cmd_callback((void *) wlan_exp_process_user_cmd); // - wlan_exp_set_beacon_ts_update_mode_callback() currently not supported by the AP wlan_exp_set_process_config_bss_callback((void *) configure_bss); wlan_exp_set_active_network_info_getter_callback((void *) active_network_info_getter); // Set CPU_HIGH Type in wlan_exp's node_info struct; wlan_exp_node_set_type_high(APPLICATION_ROLE_AP, __DATE__, __TIME__); #endif memcpy((void*) &(wlan_mac_addr[0]), (void*) get_mac_hw_addr_wlan(), MAC_ADDR_LEN); // Set the at-boot MAC Time to 0 usec set_mac_time_usec(0); wlan_mac_high_set_radio_channel(WLAN_DEFAULT_BSS_CONFIG_CHANNEL); wlan_mac_high_set_rx_ant_mode(WLAN_DEFAULT_RX_ANTENNA); wlan_mac_high_set_tx_ctrl_power(WLAN_DEFAULT_TX_PWR); wlan_mac_high_set_radio_tx_power(WLAN_DEFAULT_TX_PWR); // Initialize TIM management tag that will be postpended to a beacon mgmt_tag_tim_update_schedule_id = SCHEDULE_ID_RESERVED_MAX; mgmt_tag_tim_template = NULL; // Periodic check for timed-out associations wlan_mac_schedule_add_event(SCHEDULE_ID_COARSE, ASSOCIATION_CHECK_INTERVAL_US, SCHEDULE_REPEAT_FOREVER, (void*)remove_inactive_station_infos); wlan_platform_high_userio_disp_status(USERIO_DISP_STATUS_APPLICATION_ROLE, APPLICATION_ROLE_AP); #if WLAN_SW_CONFIG_ENABLE_LOGGING // Reset the event log event_log_reset(); #endif //WLAN_SW_CONFIG_ENABLE_LOGGING // If the DIP switch allows it, set up BSS description if ((wlan_platform_userio_get_state() & USERIO_INPUT_MASK_SW_3) == 0) { memcpy(bss_config.bssid, wlan_mac_addr, MAC_ADDR_LEN); strncpy(bss_config.ssid, default_AP_SSID, SSID_LEN_MAX); bss_config.chan_spec.chan_pri = WLAN_DEFAULT_BSS_CONFIG_CHANNEL; bss_config.chan_spec.chan_type = CHAN_TYPE_BW20; bss_config.ht_capable = WLAN_DEFAULT_BSS_CONFIG_HT_CAPABLE; bss_config.beacon_interval = WLAN_DEFAULT_BSS_CONFIG_BEACON_INTERVAL; bss_config.dtim_period = WLAN_DEFAULT_BSS_CONFIG_DTIM_PERIOD; update_mask = (BSS_FIELD_MASK_BSSID | BSS_FIELD_MASK_CHAN | BSS_FIELD_MASK_SSID | BSS_FIELD_MASK_BEACON_INTERVAL | BSS_FIELD_MASK_HT_CAPABLE | BSS_FIELD_MASK_DTIM_PERIOD); configure_bss(&bss_config, update_mask); } gl_dtim_mcast_buffer_enable = 1; wlan_mac_high_enable_mcast_buffering(gl_dtim_mcast_buffer_enable); // Print AP information to the terminal xil_printf("------------------------\n"); xil_printf("WLAN MAC AP boot complete\n"); #ifdef WLAN_USE_UART_MENU xil_printf("\nPress the Esc key in your terminal to access the UART menu\n"); #endif // Finally enable all interrupts to start handling wireless and wired traffic wlan_platform_intc_set_state(INTERRUPTS_ENABLED); while(1) { wlan_mac_poll(); } // Unreachable, but non-void return keeps the compiler happy return WLAN_FAILURE; } /*****************************************************************************/ /** * @brief Handle state change in the network scanner * * This function is part of the scan infrastructure and will be called whenever * the scanner is started, stopped, paused, or resumed. This allows the STA * to revert the channel to a known-good state when the scanner has stopped and * also serves as notification to the project that it should stop dequeueing data * frames since it might be on a different channel than its intended recipient. * * @param None * @return None * *****************************************************************************/ void process_scan_state_change(scan_state_t scan_state){ // ------------------------------------------------------------------------ // Note on scanning: // // Currently, scanning should only be done with active_bss_info = NULL, ie the // node is not currently in a BSS. This is to avoid any corner cases. The // AP needs to do the following things to make scanning safe when active_bss_info // is not NULL: // // - Pause outgoing data queues // - Pause beacon transmissions in CPU_LOW // - Refuse to enqueue probe responses when a probe request is received off channel // - Pause dequeue of probe responses when off channel // - Note: Currently, this is difficult because probe responses share a // queue with probe requests which are needed for active scans // // ------------------------------------------------------------------------ switch(scan_state){ case SCAN_IDLE: case SCAN_PAUSED: pause_data_queue = 0; if(active_network_info != NULL){ wlan_mac_high_set_radio_channel( wlan_mac_high_bss_channel_spec_to_radio_chan(active_network_info->bss_config.chan_spec)); } break; case SCAN_RUNNING: pause_data_queue = 1; break; } } /*****************************************************************************/ /** * @brief Handle queue occupancy change * * Beacons must contain a TIM tag that dedicates one bit per associated station * that indicates whether any downlink traffic is enqueued for that station. * This allows stations to save power by dozing during beacon intervals and wake * up only when they know downlink traffic is ready for them * * This function can be attached to a queue so that is called whenever the queue * transitions from empty to 1 packet or vice versa. * * @param u32 aid - the framework will call this function with the callback * argument provided during queue_open(). This application * chooses to let that argument be the AID for that queue * @param u32 queue_len - 0 if the queue is now empty, 1 if the queue now has * a single entry * @return None *****************************************************************************/ void queue_state_change(u32 aid, u32 queue_len){ //queue_len will take on a value of 0 or 1 //and represents the state of the queue after the state //changes. if(mgmt_tag_tim_update_schedule_id != SCHEDULE_ID_RESERVED_MAX){ //We already have a pending full TIM state re-write scheduled, so we won't bother //with a per-queue change. return; } if(mgmt_tag_tim_template == NULL){ //The TIM tag is not present in the current beacon template. //We have no choice but to do a full TIM tag update and write. update_tim_tag_all(SCHEDULE_ID_RESERVED_MAX); } else { //There is a TIM tag that is already present. We can update //only the relevant byte that applies to this queue state //change update_tim_tag_aid(aid, queue_len); } } /*****************************************************************************/ /** * *****************************************************************************/ inline void update_tim_tag_aid(u8 aid, u8 bit_val_in){ // The intention of this function is to modify as little of an existing TIM // tag in the beacon template packet buffer as possible to reduce the amount // of time that CPU could be waiting on the packet buffer to be unlocked. // //Note: AID = 0 is invalid. And input of 0 to this function //indicates that the multicast bit in the tim_control byte should //be modified if DTIM mcast buffering is currently disabled. Otherwise, //CPU_LOW will maintain this bit. u32 existing_mgmt_tag_length = 0; // Size of the management tag that is currently in the packet buffer u8 tim_control = 0; u16 tim_byte_idx = 0; u8 tim_bit_idx = 0; u8 bit_val = (bit_val_in & 1); 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(active_network_info == NULL) return; if( ((aid==0) && (gl_dtim_mcast_buffer_enable == 1)) ) return; //First, we should determine whether a call to update_tim_tag_all_tag_all is scheduled //for some time in the future. If it is, we can just return immediately and let that //execution clean up any pending TIM state changes. if(mgmt_tag_tim_update_schedule_id != SCHEDULE_ID_RESERVED_MAX){ return; } if(mgmt_tag_tim_template == NULL){ //There currently isn't any TIM tag in the packet buffer, which means //that we need to restore the full state and not toggle a single //bit update_tim_tag_all(SCHEDULE_ID_RESERVED_MAX); return; } //There exists a TIM tag in the beacon. We should determine its length existing_mgmt_tag_length = mgmt_tag_tim_template->header.tag_length; tim_byte_idx = aid / 8; if((tim_byte_idx + 4U) > existing_mgmt_tag_length){ //The current byte we intend to modify is larger than the existing tag. In this case, //we fall back on the update_tim_tag_all function since we cannnot simply modify a single //bit. update_tim_tag_all(SCHEDULE_ID_RESERVED_MAX); return; } if( ((tx_frame_info->tx_pkt_buf_state != TX_PKT_BUF_READY) && (tx_frame_info->tx_pkt_buf_state != TX_PKT_BUF_HIGH_CTRL) ) || (lock_tx_pkt_buf(TX_PKT_BUF_BEACON) != PKT_BUF_MUTEX_SUCCESS)){ //Note: The order of operations in the above if() clause is important. If the tx_pkt_buf_state is not ready. //then we should not even attempt to lock the beacon template packet buffer. This behavior is ensured if //the tx_pkt_buf_state is checked on on the left-hand side of the || because the || operation is a sequence //point in C. //CPU_LOW currently has the beacon packet buffer locked, which means that it is actively //transmitting the beacon and it is not safe to modify the contents of the packet. We will //use the scheduler to call update_tim_tag_all() sometime later when it is likely that the //packet buffer is no longer locked. mgmt_tag_tim_update_schedule_id = wlan_mac_schedule_add_event(SCHEDULE_ID_FINE, (active_network_info->bss_config.beacon_interval * BSS_MICROSECONDS_IN_A_TU) / 4, 1, (void*)update_tim_tag_all); return; } // If we have made it this far into the function, then we know that the TIM tag currently // exists in the beacon template packet buffer, the mgmt_tag_tim_template is non-NULL, and // finally that the TIM tag is long enough that we can simply modify bit(s) that correspond // to the AID argument of the function. if(aid == 0){ if(bit_val){ tim_control |= 0x01; //Raise the multicast bit in the TIM control field mgmt_tag_tim_template->data[2] = tim_control; //TIM Control (top 7 bits are offset for partial map) mgmt_tag_tim_template->data[3] |= tim_control&1; //Per 10.2.1.3 in 802.11-2012: AID 0 is treated as //the multicast buffer state } else { tim_control = 0; mgmt_tag_tim_template->data[2] = tim_control; //TIM Control (top 7 bits are offset for partial map) mgmt_tag_tim_template->data[3] &= ~(tim_control&1); //Per 10.2.1.3 in 802.11-2012: AID 0 is treated as //the multicast buffer state } } else { if(bit_val){ mgmt_tag_tim_template->data[3+tim_byte_idx] |= 1<data[3+tim_byte_idx] &= ~(1<tx_pkt_buf_state != TX_PKT_BUF_READY) && (tx_frame_info->tx_pkt_buf_state != TX_PKT_BUF_HIGH_CTRL) ) || (lock_tx_pkt_buf(TX_PKT_BUF_BEACON) != PKT_BUF_MUTEX_SUCCESS)){ //Note: The order of operations in the above if() clause is important. If the tx_pkt_buf_state is not ready. //then we should not even attempt to lock the beacon template packet buffer. This behavior is ensured if //the tx_pkt_buf_state is checked on on the left-hand side of the || because the || operation is a sequence //point in C. //CPU_LOW currently has the beacon packet buffer locked, which means that it is actively //transmitting the beacon and it is not safe to modify the contents of the packet. We will //use the scheduler to call update_tim_tag_all() sometime later when it is likely that the //packet buffer is no longer locked. mgmt_tag_tim_update_schedule_id = wlan_mac_schedule_add_event(SCHEDULE_ID_FINE, (active_network_info->bss_config.beacon_interval * BSS_MICROSECONDS_IN_A_TU) / 4, 1, (void*)update_tim_tag_all); return; } if(mgmt_tag_tim_template != NULL){ //There exists a TIM tag in the beacon. We should determine its length existing_mgmt_tag_length = mgmt_tag_tim_template->header.tag_length; } //---------------------------------- // 1. We will refresh the full state of the TIM tag based upon queue occupancy //We'll start updating the TIM tag from the last associated station. //Since we know that the WLAN MAC High Framework keeps the dl_list of //associated stations in increasing AID order, we can use this final //station's AID to define the size of the TIM tag. curr_station_entry = (station_info_entry_t*)(active_network_info->members.last); if(curr_station_entry != NULL){ station_info = (station_info_t*)(curr_station_entry->data); next_mgmt_tag_length = 4 + ((station_info->ID) / 8); } else { next_mgmt_tag_length = 4; } if(mgmt_tag_tim_template == NULL){ //We need to add the tag to the end of the beacon template //and update the length field of the tx_frame_info. mgmt_tag_tim_template = (mgmt_tag_template_t*)((void*)(tx_frame_info) +PHY_TX_PKT_BUF_MPDU_OFFSET +tx_frame_info->length -WLAN_PHY_FCS_NBYTES); gl_beacon_txrx_config.dtim_tag_byte_offset = (u16)((u32)mgmt_tag_tim_template - (u32)tx_frame_info); mgmt_tag_tim_template->header.tag_element_id = MGMT_TAG_TIM; tx_frame_info->length += sizeof(mgmt_tag_header); } mgmt_tag_tim_template->header.tag_length = next_mgmt_tag_length; tim_control = 0; //The top 7 bits are an offset for the partial map if((gl_dtim_mcast_buffer_enable == 0) && (queue_length(mcast_qid)>0)){ //If mcast buffering is disabled, the AP is responsible for maintaining the //mcast bit in the TIM control tim_control |= 0x01; //Raise the multicast bit in the TIM control field } curr_station_entry = (station_info_entry_t*)(active_network_info->members.first); while(curr_station_entry != NULL){ station_info = (station_info_t*)(curr_station_entry->data); if(queue_length(station_info->QID)){ tim_next_byte_idx = (station_info->ID) / 8; if(tim_next_byte_idx > tim_byte_idx){ //We've moved on to a new octet. We should zero everything after the previous octet //up to and including the new octet. for(i = tim_byte_idx+1; i <= tim_next_byte_idx; i++){ mgmt_tag_tim_template->data[3+i] = 0; } } tim_bit_idx = (station_info->ID) % 8; tim_byte_idx = tim_next_byte_idx; //Raise the bit for this station in the TIM partial bitmap mgmt_tag_tim_template->data[3+tim_byte_idx] |= 1<data[0] = gl_beacon_txrx_config.dtim_count; //Note: this field is going to be maintained by CPU_LOW, so it doesn't matter // if we write anything to it. mgmt_tag_tim_template->data[1] = gl_beacon_txrx_config.dtim_period; mgmt_tag_tim_template->data[2] = tim_control; //TIM Control (top 7 bits are offset for partial map) if(gl_dtim_mcast_buffer_enable == 0){ mgmt_tag_tim_template->data[3] |= tim_control&1; //Per 10.2.1.3 in 802.11-2012: AID 0 is treated as //the multicast buffer state } tx_frame_info->length += (next_mgmt_tag_length - existing_mgmt_tag_length); if(unlock_tx_pkt_buf(TX_PKT_BUF_BEACON) != PKT_BUF_MUTEX_SUCCESS){ xil_printf("Error: Unable to unlock Beacon packet buffer during update_tim_tag_all\n"); } return; } /*****************************************************************************/ /** * @brief Poll Tx queues to select next available packet to transmit * * This function attempts to completely fill any available Tx packet buffers. If * DTIM multicast buffering is enabled, it will attempt to dequeue as many multicast * packets as possible such that PKT_BUF_GROUP_DTIM_MCAST packet buffer group is full * or the multicast queue is empty (whichever comes first). The PKT_BUF_GROUP_GENERAL * packet buffer group will be filled with frames with a nested round-robing policy: * 1) The function will alternate between dequeueing management and data frames in order * to prioritize time-critical management responses like authentication and assocation * response frames. * 2) Data frames will be dequeued round-robin for each associated station. If DTIM multicast * buffering is disabled, the multicast queue will be treated like an associated station in * this policy. * *****************************************************************************/ #define NUM_QUEUE_GROUPS 2 typedef enum queue_group_t{ MGMT_QGRP, DATA_QGRP } queue_group_t; void poll_tx_queues(){ interrupt_state_t curr_interrupt_state; dl_entry* tx_queue_entry; u32 u_i; s32 s_i; int num_pkt_bufs_avail; int poll_loop_cnt; // Remember the next group to poll between calls to this function // This implements the ping-pong poll between the MGMT_QGRP and DATA_QGRP groups static queue_group_t next_queue_group = MGMT_QGRP; queue_group_t curr_queue_group; // Remember the last queue polled between calls to this function // This implements the round-robin poll of queues in the DATA_QGRP group static station_info_entry_t* next_station_info_entry = NULL; station_info_entry_t* curr_station_info_entry; station_info_t* curr_station_info; // Stop interrupts for all processing below - this avoids many possible race conditions, // like new packets being enqueued or stations joining/leaving the BSS curr_interrupt_state = wlan_platform_intc_stop(); // First handle the general packet buffer group num_pkt_bufs_avail = wlan_mac_num_tx_pkt_buf_available(PKT_BUF_GROUP_GENERAL); // This loop will (at most) check every queue twice // This handles the case of a single non-empty queue needing to supply packets // for both GENERAL packet buffers poll_loop_cnt = 0; while((num_pkt_bufs_avail > 0) && (poll_loop_cnt < (2*NUM_QUEUE_GROUPS))) { poll_loop_cnt++; curr_queue_group = next_queue_group; if(curr_queue_group == MGMT_QGRP) { // Poll the management queue on this loop, data queues on next loop next_queue_group = DATA_QGRP; tx_queue_entry = queue_dequeue(mgmt_qid); if(tx_queue_entry) { // Update the packet buffer group ((tx_80211_queue_buffer_t*)(tx_queue_entry->data))->tx_queue_details.pkt_buf_group = PKT_BUF_GROUP_GENERAL; // Successfully dequeued a management packet - transmit and checkin wlan_mac_transmit_wireless(tx_queue_entry); //continue the loop num_pkt_bufs_avail--; continue; } } else { // Poll the data queues on this loop, management queue on next loop next_queue_group = MGMT_QGRP; if(pause_data_queue) { // Data queues are paused - skip any dequeue attempts and continue the loop continue; } for(u_i = 0; u_i < (active_network_info->members.length + 1); u_i++) { // Resume polling data queues from where we stopped on the previous call curr_station_info_entry = next_station_info_entry; // Loop through all associated stations' queues and the broadcast queue if(curr_station_info_entry == NULL) { next_station_info_entry = (station_info_entry_t*)(active_network_info->members.first); // Only check the multicast queue in this loop when DTIM Multicast Buffering is disabled if( gl_dtim_mcast_buffer_enable && gl_cpu_low_supports_dtim_mcast && (gl_beacon_txrx_config.beacon_tx_mode != NO_BEACON_TX)) { // DTIM multicast buffering is enabled - skip the multicast queue continue; } else { tx_queue_entry = queue_dequeue(mcast_qid); if(tx_queue_entry) { // Update the packet buffer group ((tx_80211_queue_buffer_t*)(tx_queue_entry->data))->tx_queue_details.pkt_buf_group = PKT_BUF_GROUP_GENERAL; // Successfully dequeued a management packet - transmit and checkin wlan_mac_transmit_wireless(tx_queue_entry); // Successfully dequeued a multicast packet - end the DATA_QGRP loop num_pkt_bufs_avail--; break; } } } else { // Check the queue for an associated station curr_station_info = (station_info_t*)(curr_station_info_entry->data); if( station_info_is_member(&active_network_info->members, curr_station_info)) { if(curr_station_info_entry == (station_info_entry_t*)(active_network_info->members.last)){ // We've reached the end of the table, so we wrap around to the beginning next_station_info_entry = NULL; } else { next_station_info_entry = dl_entry_next(curr_station_info_entry); } if((curr_station_info->ps_state & STATION_INFO_PS_STATE_DOZE) == 0) { tx_queue_entry = queue_dequeue(curr_station_info->QID); if(tx_queue_entry) { // Update the packet buffer group ((tx_80211_queue_buffer_t*)(tx_queue_entry->data))->tx_queue_details.pkt_buf_group = PKT_BUF_GROUP_GENERAL; // Successfully dequeued a management packet - transmit and checkin wlan_mac_transmit_wireless(tx_queue_entry); // Successfully dequeued a unicast packet for this station - end the DATA_QGRP loop num_pkt_bufs_avail--; break; } } } else { // This curr_station_info is invalid. Perhaps it was removed from // the association table before poll_tx_queues was called. We will // start the round robin checking back at broadcast. next_station_info_entry = NULL; } // END if(curr_station_info is BSS member) } // END if(multicast queue or station queue) } // END for loop over association table } // END if(MGMT or DATA queue group) } // END while(buffers available && keep polling) // Next handle the DTIM Multicast packet buffer group if(gl_dtim_mcast_buffer_enable && gl_cpu_low_supports_dtim_mcast && (gl_beacon_txrx_config.beacon_tx_mode != NO_BEACON_TX)) { num_pkt_bufs_avail = wlan_mac_num_tx_pkt_buf_available(PKT_BUF_GROUP_DTIM_MCAST); for(s_i=0; s_idata))->tx_queue_details.pkt_buf_group = PKT_BUF_GROUP_DTIM_MCAST; // Successfully dequeued a management packet - transmit and checkin wlan_mac_transmit_wireless(tx_queue_entry); } else { // Queue is empty - end this loop break; } } } wlan_platform_intc_set_state(curr_interrupt_state); } /*****************************************************************************/ /** * @brief Purges all packets from all Tx queues * * This function discards all currently en-queued packets awaiting transmission and returns all * queue entries to the free pool. * * This function does not discard packets already submitted to the lower-level MAC for transmission * * @param None * @return None *****************************************************************************/ void purge_all_wireless_tx_queue(){ station_info_entry_t* curr_station_info_entry; station_info_t* curr_station_info; if(active_network_info == NULL) return; int iter = active_network_info->members.length; // Purge all data transmit queues wlan_mac_purge_wireless_tx(mcast_qid); wlan_mac_purge_wireless_tx(mgmt_qid); curr_station_info_entry = (station_info_entry_t*)(active_network_info->members.first); while( (curr_station_info_entry != NULL) && (iter-- > 0)){ curr_station_info = curr_station_info_entry->data; wlan_mac_purge_wireless_tx(curr_station_info->QID); curr_station_info_entry = dl_entry_next(curr_station_info_entry); } } #if WLAN_SW_CONFIG_ENABLE_LTG /*****************************************************************************/ /** * @brief Callback to handle new Local Traffic Generator event * * This function is called when the LTG scheduler determines a traffic generator should create a new packet. The * behavior of this function depends entirely on the LTG payload parameters. * * The reference implementation defines 3 LTG payload types: * - LTG_PYLD_TYPE_FIXED: generate 1 fixed-length packet to single destination; callback_arg is pointer to ltg_pyld_fixed struct * - LTG_PYLD_TYPE_UNIFORM_RAND: generate 1 random-length packet to single destination; callback_arg is pointer to ltg_pyld_uniform_rand_t struct * - LTG_PYLD_TYPE_ALL_ASSOC_FIXED: generate 1 fixed-length packet to each associated station; callback_arg is pointer to ltg_pyld_all_assoc_fixed_t struct * * @param u32 id * - Unique ID of the previously created LTG * @param void* callback_arg * - Callback argument provided at LTG creation time; interpretation depends on LTG type * @return None *****************************************************************************/ void ltg_event(u32 id, void* callback_arg){ u32 payload_length; u8* addr_ra; u16 duration; u32 min_ltg_payload_length; station_info_t* station_info = NULL; station_info_entry_t* station_info_entry = NULL; u8 is_multicast; u8 queue_id; dl_entry* tx_queue_entry = NULL; tx_80211_queue_buffer_t* tx_queue_buffer = NULL; u8 continue_loop; u16 flags = TX_80211_QUEUE_BUFFER_FLAGS_FILL_UNIQ_SEQ; if(((ltg_pyld_hdr_t*)callback_arg)->type == LTG_PYLD_TYPE_CTRL_RESP){ tx_queue_entry = queue_checkout(); if(tx_queue_entry != NULL){ tx_queue_buffer = ((tx_80211_queue_buffer_t*)(tx_queue_entry->data)); addr_ra = ((ltg_pyld_ctrl_resp_t*)callback_arg)->addr_ra; duration = ((ltg_pyld_ctrl_resp_t*)callback_arg)->duration; if(((ltg_pyld_ctrl_resp_t*)callback_arg)->subtype == LTG_PYLD_CTRL_RESP_SUBTYPE_CTS){ payload_length = wlan_create_cts_frame(tx_queue_buffer->pkt, addr_ra, duration); tx_queue_buffer->flags = TX_80211_QUEUE_BUFFER_FLAGS_TYPE_CTS; } else { //LTG_PYLD_CTRL_RESP_SUBTYPE_ACK payload_length = wlan_create_ack_frame(tx_queue_buffer->pkt, addr_ra); tx_queue_buffer->flags = TX_80211_QUEUE_BUFFER_FLAGS_TYPE_ACK; } // Fill in metadata tx_queue_buffer->flags |= TX_80211_QUEUE_BUFFER_FLAGS_BYPASS_RECOVERY; tx_queue_buffer->length = payload_length; tx_queue_buffer->seg0_len = payload_length; tx_queue_buffer->seg1_len = 0; tx_queue_buffer->seg1_offset = 0; // With LTG_PYLD_TYPE_CTRL_RESP, we will transmit to the receive address (addr_ra) regardless of // whether that address is an AP that we are associated to. To do that, we need to make sure that // a station_info_t exists for that address tx_queue_buffer->station_info = station_info_create(addr_ra); wlan_mac_enqueue_wireless_tx(mgmt_qid, tx_queue_entry); } } else { if(active_network_info != NULL){ u8* addr_da; switch(((ltg_pyld_hdr_t*)callback_arg)->type){ case LTG_PYLD_TYPE_FIXED: payload_length = ((ltg_pyld_fixed_t*)callback_arg)->length; addr_da = ((ltg_pyld_fixed_t*)callback_arg)->addr_da; station_info = station_info_create(addr_da); is_multicast = wlan_addr_mcast(addr_da); if(is_multicast){ queue_id = mcast_qid; } else { flags |= TX_80211_QUEUE_BUFFER_FLAGS_FILL_DURATION; if(station_info_is_member(&active_network_info->members, station_info)){ queue_id = station_info->QID; } else { return; } } break; case LTG_PYLD_TYPE_UNIFORM_RAND: payload_length = (rand()%(((ltg_pyld_uniform_rand_t*)(callback_arg))->max_length - ((ltg_pyld_uniform_rand_t*)(callback_arg))->min_length))+((ltg_pyld_uniform_rand_t*)(callback_arg))->min_length; addr_da = ((ltg_pyld_fixed_t*)callback_arg)->addr_da; station_info = station_info_create(addr_da); is_multicast = wlan_addr_mcast(addr_da); if(is_multicast){ queue_id = mcast_qid; } else { flags |= TX_80211_QUEUE_BUFFER_FLAGS_FILL_DURATION; if(station_info_is_member(&active_network_info->members, station_info)){ queue_id = station_info->QID; } else { return; } } break; case LTG_PYLD_TYPE_ALL_ASSOC_FIXED: if(active_network_info->members.length > 0){ flags |= TX_80211_QUEUE_BUFFER_FLAGS_FILL_DURATION; station_info_entry = (station_info_entry_t*)(active_network_info->members.first); station_info = (station_info_t*)station_info_entry->data; addr_da = station_info_entry->addr; queue_id = station_info->QID; is_multicast = 0; payload_length = ((ltg_pyld_all_assoc_fixed_t*)callback_arg)->length; } else { return; } break; default: xil_printf("ERROR ltg_event: Unknown LTG Payload Type! (%d)\n", ((ltg_pyld_hdr_t*)callback_arg)->type); return; break; } do{ continue_loop = 0; if(queue_length(queue_id) < max_queue_size){ // Checkout 1 element from the queue; tx_queue_entry = queue_checkout(); if(tx_queue_entry != NULL){ // Create LTG packet tx_queue_buffer = ((tx_80211_queue_buffer_t*)(tx_queue_entry->data)); min_ltg_payload_length = wlan_create_ltg_frame(tx_queue_buffer->pkt, addr_da, wlan_mac_addr, wlan_mac_addr, MAC_FRAME_CTRL2_FLAG_FROM_DS, id); payload_length = WLAN_MAX(payload_length+sizeof(mac_header_80211)+WLAN_PHY_FCS_NBYTES, min_ltg_payload_length); // Fill in metadata tx_queue_buffer->flags = flags; tx_queue_buffer->length = payload_length; tx_queue_buffer->seg0_len = min_ltg_payload_length; tx_queue_buffer->seg1_len = 0; tx_queue_buffer->seg1_offset = 0; tx_queue_buffer->station_info = station_info; // Submit the new packet to the appropriate queue wlan_mac_enqueue_wireless_tx(queue_id, tx_queue_entry); } else { // There aren't any free queue elements right now. // As such, there probably isn't any point to continuing this callback. // We'll return and try again once it is called the next time. return; } } if(((ltg_pyld_hdr_t*)callback_arg)->type == LTG_PYLD_TYPE_ALL_ASSOC_FIXED){ station_info_entry = dl_entry_next(station_info_entry); if(station_info_entry != NULL){ station_info = (station_info_t*)station_info_entry->data; addr_da = station_info_entry->addr; queue_id = station_info->QID; is_multicast = 0; continue_loop = 1; } else { continue_loop = 0; } } else { continue_loop = 0; } } while(continue_loop == 1); } } } #endif //WLAN_SW_CONFIG_ENABLE_LTG /*****************************************************************************/ /** * @brief Callback to handle insertion of an Ethernet reception into the corresponding wireless Tx queue * * This function is called when a new Ethernet packet is received that must be transmitted via the wireless interface. * The packet must be encapsulated before it is passed to this function. Ethernet encapsulation is implemented in the mac_high framework. * *****************************************************************************/ int ethernet_receive(eth_rx_queue_buffer_t* eth_rx_queue_buffer){ tx_80211_queue_buffer_t* tx_queue_buffer; ethernet_header_t* eth_hdr; station_info_entry_t* entry; station_info_t* station_info; if(active_network_info == NULL) return 0; eth_hdr = (ethernet_header_t*)eth_rx_queue_buffer->pkt; if( (eth_hdr->ethertype != ETH_TYPE_ARP) && (eth_hdr->ethertype != ETH_TYPE_IP) ) return 0; // Determine how to send the packet if( wlan_addr_mcast(eth_hdr->dest_mac_addr) ) { // Send the multicast packet if(queue_length(mcast_qid) < max_queue_size){ //Encapsulate the packet // Note: the encapsulation function handles filling in the C0/C1 lengths and C1 offset for us tx_queue_buffer = wlan_eth_encap( eth_rx_queue_buffer, 0 ); tx_queue_buffer->flags = 0; wlan_create_data_frame_header(tx_queue_buffer->pkt, eth_hdr->dest_mac_addr, wlan_mac_addr, eth_hdr->src_mac_addr, MAC_FRAME_CTRL2_FLAG_FROM_DS); // Assign a station_info_t struct // Notes: (1) if one exists already, it will be adopted. // (2) if no heap exists for creating one, NULL is a valid // value for the station_info_t* tx_queue_buffer->station_info = station_info_create(eth_hdr->dest_mac_addr); // Put the packet in the queue wlan_mac_enqueue_wireless_tx(mcast_qid, eth_rx_queue_buffer->pyld_queue_hdr.dle); } else { // Packet was not successfully enqueued return 0; } } else { // Check associations // Is this packet meant for a station we are associated with? entry = station_info_find_by_addr(eth_hdr->dest_mac_addr, &active_network_info->members); if( entry != NULL ) { station_info = (station_info_t*)(entry->data); // Send the unicast packet if(queue_length(station_info->QID) < max_queue_size){ //Encapsulate the packet // Note: the encapsulation function handles filling in the C0/C1 lengths and C1 offset for us tx_queue_buffer = wlan_eth_encap( eth_rx_queue_buffer, 0 ); if(tx_queue_buffer){ // Fill in metadata tx_queue_buffer->flags = TX_80211_QUEUE_BUFFER_FLAGS_FILL_DURATION; wlan_create_data_frame_header(tx_queue_buffer->pkt, eth_hdr->dest_mac_addr, wlan_mac_addr, eth_hdr->src_mac_addr, MAC_FRAME_CTRL2_FLAG_FROM_DS); tx_queue_buffer->station_info = station_info; // Put the packet in the queue wlan_mac_enqueue_wireless_tx(station_info->QID, tx_queue_buffer->pyld_queue_hdr.dle); } else { return 0; } } else { // Packet was not successfully enqueued return 0; } } else { // Packet was not successfully enqueued return 0; } } // Packet successfully enqueued return 1; } //TODO: Create function to update beacon live fields (e.g. TIM bitmap) // We may need to formalize a beacon ping/pong handshake to avoid any races // created by CPU_HIGH modifying the beacon payload while the PHY is actively // reading bytes out of the packet buffer to create a waveform. /*****************************************************************************/ /** * @brief Check the time since the AP heard from each station * * This function will check the timestamp of the last reception from each station and send a * de-authentication packet to any stations that have timed out. * * @param None * @return None *****************************************************************************/ void remove_inactive_station_infos() { u32 aid; u64 time_since_last_activity; station_info_t* curr_station_info; dl_entry* curr_station_info_entry; dl_entry* next_station_info_entry; time_hr_min_sec_t time_hr_min_sec; if(active_network_info == NULL) return; next_station_info_entry = active_network_info->members.first; while(next_station_info_entry != NULL) { curr_station_info_entry = next_station_info_entry; next_station_info_entry = dl_entry_next(curr_station_info_entry); curr_station_info = (station_info_t*)(curr_station_info_entry->data); time_since_last_activity = (get_system_time_usec() - curr_station_info->latest_rx_timestamp); // De-authenticate the station if we have timed out and we have not disabled this check for the station if((time_since_last_activity > ASSOCIATION_TIMEOUT_US) && ((curr_station_info->flags & STATION_INFO_FLAG_DISABLE_ASSOC_CHECK) == 0)){ aid = deauthenticate_station( curr_station_info ); if (aid != 0) { xil_printf("\n\nDisassociation due to inactivity:\n"); } } } next_station_info_entry = authenticated_unassociated_stations.first; while(next_station_info_entry != NULL) { curr_station_info_entry = next_station_info_entry; next_station_info_entry = dl_entry_next(curr_station_info_entry); curr_station_info = (station_info_t*)(curr_station_info_entry->data); time_since_last_activity = (get_system_time_usec() - curr_station_info->latest_rx_timestamp); // De-authenticate the station if we have timed out and we have not disabled this check for the station if((time_since_last_activity > ASSOCIATION_TIMEOUT_US) && ((curr_station_info->flags & STATION_INFO_FLAG_DISABLE_ASSOC_CHECK) == 0)){ station_info_remove_from_list(&authenticated_unassociated_stations, curr_station_info->addr); time_hr_min_sec = wlan_mac_time_to_hr_min_sec(get_system_time_usec()); xil_printf("*%dh:%02dm:%02ds* STA %02x:%02x:%02x:%02x:%02x:%02x has disassociated\n", time_hr_min_sec.hr, time_hr_min_sec.min, time_hr_min_sec.sec, curr_station_info->addr[0], curr_station_info->addr[1], curr_station_info->addr[2], curr_station_info->addr[3], curr_station_info->addr[4], curr_station_info->addr[5]); // Remove the "keep" flag for this station_info so the framework can cleanup later. curr_station_info->flags &= ~STATION_INFO_FLAG_KEEP; } } } /*****************************************************************************/ /** * @brief Process received MPDUs * * This callback function will process all the received MPDUs. * * This function must implement the state machine that will allow a station to join the AP. * * @param void* pkt_buf_addr * - Packet buffer address; Contains the contents of the MPDU as well as other packet information from CPU low * @param station_info_t * station_info * - Pointer to metadata about the station from which this frame was received * @param rx_common_entry* rx_event_log_entry * - Pointer to the log entry created for this reception by the MAC High Framework * @return u32 flags * *****************************************************************************/ u32 mpdu_rx_process(void* pkt_buf_addr, station_info_t* station_info, rx_common_entry* rx_event_log_entry) { rx_frame_info_t* rx_frame_info = (rx_frame_info_t*)pkt_buf_addr; u8* rx_mac_payload = (u8*)pkt_buf_addr + PHY_RX_PKT_BUF_MPDU_OFFSET; u8* mac_payload_ptr_u8 = rx_mac_payload; // Make a copy of rx_mac_payload that can be modified while traversing through tagged parameters. mac_header_80211* rx_80211_header = (mac_header_80211*)(rx_mac_payload); mac_header_80211* tx_80211_header; u8 send_response = 0; u16 tx_length; u16 rx_seq; dl_entry* tx_queue_entry; tx_80211_queue_buffer_t* tx_queue_buffer; char ssid[SSID_LEN_MAX]; u8 ssid_length = 0; u8 sta_is_ht_capable = 0; u8 sta_is_associated = 0; u8 sta_is_authenticated = 0; station_info_entry_t* associated_station_entry = NULL; station_info_t* associated_station = NULL; station_info_t* auth_unassoc_station_info = NULL; u8 unicast_to_me; u8 to_multicast; u8 eth_send; u8 allow_auth = 0; time_hr_min_sec_t time_hr_min_sec; u16 rx_length = rx_frame_info->phy_details.length; u32 return_val = 0; // If this function was passed a CTRL frame (e.g., CTS, ACK), then we should just quit. // The only reason this occured was so that it could be logged in the line above. if((rx_80211_header->frame_control_1 & 0xF) == MAC_FRAME_CTRL1_TYPE_CTRL){ goto mpdu_rx_process_end; } // Determine if this STA is currently associated to the active_bss_info sta_is_associated = station_info_is_member(&active_network_info->members, station_info); sta_is_authenticated = (sta_is_associated || station_info_is_member(&authenticated_unassociated_stations, station_info)); // Determine destination of packet unicast_to_me = wlan_addr_eq(rx_80211_header->address_1, wlan_mac_addr); to_multicast = wlan_addr_mcast(rx_80211_header->address_1); // If the packet is good (ie good FCS) and it is destined for me, then process it if( rx_frame_info->flags & RX_FRAME_INFO_FLAGS_FCS_GOOD){ // Sequence number is 12 MSB of seq_control field rx_seq = ((rx_80211_header->sequence_control) >> 4) & 0xFFF; // Check if this was a duplicate reception // - Packet is unicast and directed towards me // - Packet has the RETRY bit set to 1 in the second frame control byte // - Received seq num matched previously received seq num for this STA if( (station_info != NULL) && unicast_to_me ){ if( ((rx_80211_header->frame_control_2) & MAC_FRAME_CTRL2_FLAG_RETRY) && (station_info->latest_rx_seq == rx_seq) ) { if(rx_event_log_entry != NULL){ rx_event_log_entry->flags |= RX_FLAGS_DUPLICATE; return_val |= MAC_RX_CALLBACK_RETURN_FLAG_DUP; } } else { station_info->latest_rx_seq = rx_seq; } } if( sta_is_associated ){ // Update PS state if((rx_80211_header->frame_control_2) & MAC_FRAME_CTRL2_FLAG_POWER_MGMT){ station_info->ps_state |= STATION_INFO_PS_STATE_DOZE; } else { station_info->ps_state &= ~STATION_INFO_PS_STATE_DOZE; poll_tx_queues(); } if( return_val & MAC_RX_CALLBACK_RETURN_FLAG_DUP ) { // Finish the function and do not de-encapsulate goto mpdu_rx_process_end; } } if(unicast_to_me || to_multicast){ // Process the packet switch(rx_80211_header->frame_control_1) { //--------------------------------------------------------------------- case MAC_FRAME_CTRL1_SUBTYPE_QOSDATA: case MAC_FRAME_CTRL1_SUBTYPE_DATA: // Data packet // - Determine if this packet is from an associated station // - Depending on the type and destination, transmit the packet wirelessly or over the wired network // if( sta_is_associated ){ // MPDU is flagged as destined to the DS if((rx_80211_header->frame_control_2) & MAC_FRAME_CTRL2_FLAG_TO_DS) { eth_send = 1; // Check if this is a multicast packet if(wlan_addr_mcast(rx_80211_header->address_3)){ // Send the data packet over wireless tx_queue_entry = queue_checkout(); if(tx_queue_entry != NULL){ tx_queue_buffer = (tx_80211_queue_buffer_t*)(tx_queue_entry->data); wlan_create_data_frame_header(tx_queue_buffer->pkt, rx_80211_header->address_3, wlan_mac_addr, rx_80211_header->address_2, MAC_FRAME_CTRL2_FLAG_FROM_DS); // Overwrite packet type with just-received packet type. This will allow // relaying of QOSDATA frames tx_80211_header = ((mac_header_80211*)(tx_queue_buffer->pkt)); tx_80211_header->frame_control_1 = rx_80211_header->frame_control_1; // Fill in the data wlan_mac_high_cdma_start_transfer(tx_queue_buffer->pkt + sizeof(mac_header_80211), rx_mac_payload + sizeof(mac_header_80211), rx_length - sizeof(mac_header_80211)); // Fill in the metadata tx_queue_buffer->flags = 0; tx_queue_buffer->length = rx_length; tx_queue_buffer->seg0_len = rx_length; tx_queue_buffer->seg1_len = 0; tx_queue_buffer->seg1_offset = 0; tx_queue_buffer->station_info = station_info_create(rx_80211_header->address_3); // Make sure the DMA transfer is complete wlan_mac_high_cdma_finish_transfer(); // Put the packet in the queue wlan_mac_enqueue_wireless_tx(mcast_qid, tx_queue_entry); } } else { // Packet is not a multicast packet. Check if it is destined for one of our stations if(active_network_info != NULL){ associated_station_entry = station_info_find_by_addr(rx_80211_header->address_3, &active_network_info->members); } if(associated_station_entry != NULL){ associated_station = (station_info_t*)(associated_station_entry->data); // Send the data packet over the wireless to our station tx_queue_entry = queue_checkout(); if(tx_queue_entry != NULL){ tx_queue_buffer = (tx_80211_queue_buffer_t*)(tx_queue_entry->data); wlan_create_data_frame_header(tx_queue_buffer->pkt, rx_80211_header->address_3, wlan_mac_addr, rx_80211_header->address_2, MAC_FRAME_CTRL2_FLAG_FROM_DS); // Overwrite packet type with just-received packet type. This will allow // relaying of QOSDATA frames tx_80211_header = ((mac_header_80211*)(tx_queue_buffer->pkt)); tx_80211_header->frame_control_1 = rx_80211_header->frame_control_1; // Fill in the data wlan_mac_high_cdma_start_transfer(tx_queue_buffer->pkt + sizeof(mac_header_80211), rx_mac_payload + sizeof(mac_header_80211), rx_length - sizeof(mac_header_80211)); // Fill in the metadata tx_queue_buffer->flags = TX_80211_QUEUE_BUFFER_FLAGS_FILL_DURATION; tx_queue_buffer->length = rx_length; tx_queue_buffer->seg0_len = rx_length; tx_queue_buffer->seg1_len = 0; tx_queue_buffer->seg1_offset = 0; tx_queue_buffer->station_info = associated_station; // Make sure the DMA transfer is complete wlan_mac_high_cdma_finish_transfer(); // Put the packet in the queue wlan_mac_enqueue_wireless_tx(associated_station->QID, tx_queue_entry); // Given we sent the packet wirelessly to our stations, if we do not allow Ethernet transmissions // of wireless transmissions, then do not send over Ethernet #ifndef ALLOW_ETH_TX_OF_WIRELESS_TX eth_send = 0; #endif } } } // Encapsulate the packet and send over the wired network if(eth_send){ #if WLAN_SW_CONFIG_ENABLE_ETH_BRIDGE wlan_eth_decap_and_send(rx_mac_payload, rx_80211_header->address_3, rx_80211_header->address_2, rx_length, 0); #endif } } } else { // Packet was not from an associated station // - Print a WARNING and send a de-authentication to trigger a re-association // // if(unicast_to_me){ // Received a data frame from a STA that claims to be associated with this AP but is not in the AP association table // Discard the MPDU and reply with a de-authentication frame to trigger re-association at the STA wlan_printf(PL_WARNING, "Data from non-associated station: [%x %x %x %x %x %x], issuing de-authentication\n", rx_80211_header->address_2[0],rx_80211_header->address_2[1],rx_80211_header->address_2[2],rx_80211_header->address_2[3],rx_80211_header->address_2[4],rx_80211_header->address_2[5]); wlan_printf(PL_WARNING, "Address 3: [%x %x %x %x %x %x]\n", rx_80211_header->address_3[0],rx_80211_header->address_3[1],rx_80211_header->address_3[2],rx_80211_header->address_3[3],rx_80211_header->address_3[4],rx_80211_header->address_3[5]); // Send de-authentication packet to the station tx_queue_entry = queue_checkout(); if(tx_queue_entry != NULL){ tx_queue_buffer = (tx_80211_queue_buffer_t*)(tx_queue_entry->data); tx_length = wlan_create_deauth_frame(tx_queue_buffer->pkt, rx_80211_header->address_2, wlan_mac_addr, wlan_mac_addr, DEAUTH_REASON_NONASSOCIATED_STA); // Fill in metadata tx_queue_buffer->length = tx_length; tx_queue_buffer->seg0_len = tx_length; tx_queue_buffer->seg1_len = 0; tx_queue_buffer->seg1_offset = 0; tx_queue_buffer->station_info = station_info; // Put the packet in the queue wlan_mac_enqueue_wireless_tx(mgmt_qid, tx_queue_entry); } } } // END if(associated_station != NULL) break; //--------------------------------------------------------------------- case (MAC_FRAME_CTRL1_SUBTYPE_PROBE_REQ): // Probe Request Packet // - Check that this packet is to the broadcast address // - Look at the tagged parameters // - Depending on the parameters, send a probe response // if(wlan_addr_eq(rx_80211_header->address_3, bcast_addr) || wlan_addr_eq(rx_80211_header->address_3, wlan_mac_addr)) { mac_payload_ptr_u8 += sizeof(mac_header_80211); // Loop through tagged parameters while(((u32)mac_payload_ptr_u8 - (u32)rx_mac_payload)<= (rx_length - WLAN_PHY_FCS_NBYTES)){ // What kind of tag is this? switch(mac_payload_ptr_u8[0]){ //----------------------------------------------------- case MGMT_TAG_SSID: // SSID parameter set if((active_network_info != NULL) && ((mac_payload_ptr_u8[1]==0) || // Wildcard SSID is zero-length ((mac_payload_ptr_u8[1] == strnlen(active_network_info->bss_config.ssid, SSID_LEN_MAX)) && // Check for equal lengths first (memcmp(mac_payload_ptr_u8+2, (u8*)active_network_info->bss_config.ssid, mac_payload_ptr_u8[1])==0)))) { // Send response if Probe Request contained wildcard SSID // or SSID matching the SSID of this AP's BSS send_response = 1; } break; //----------------------------------------------------- case MGMT_TAG_SUPPORTED_RATES: // Supported rates break; //----------------------------------------------------- case MGMT_TAG_EXTENDED_SUPPORTED_RATES: // Extended supported rates break; //----------------------------------------------------- case MGMT_TAG_DSSS_PARAMETER_SET: // DS Parameter set (e.g. channel) break; } // Move up to the next tag mac_payload_ptr_u8 += mac_payload_ptr_u8[1]+2; } if((active_network_info != NULL) && send_response) { // Create a probe response frame tx_queue_entry = queue_checkout(); if(tx_queue_entry != NULL){ tx_queue_buffer = (tx_80211_queue_buffer_t*)(tx_queue_entry->data); tx_length = wlan_create_probe_resp_frame( tx_queue_buffer->pkt, rx_80211_header->address_2, wlan_mac_addr, wlan_mac_addr, active_network_info ); // Fill in metadata tx_queue_buffer->flags = TX_80211_QUEUE_BUFFER_FLAGS_FILL_DURATION | TX_80211_QUEUE_BUFFER_FLAGS_FILL_TIMESTAMP; tx_queue_buffer->length = tx_length; tx_queue_buffer->seg0_len = tx_length; tx_queue_buffer->seg1_len = 0; tx_queue_buffer->seg1_offset = 0; tx_queue_buffer->station_info = station_info; // Put the packet in the queue wlan_mac_enqueue_wireless_tx(mgmt_qid, tx_queue_entry); } // Finish the function goto mpdu_rx_process_end; } } break; //--------------------------------------------------------------------- case (MAC_FRAME_CTRL1_SUBTYPE_AUTH): // Authentication Packet // - Check if authentication is allowed (note: if the station // - Potentially send authentication response // if( sta_is_associated || (wlan_addr_eq(rx_80211_header->address_3, wlan_mac_addr) && wlan_mac_addr_filter_is_allowed(rx_80211_header->address_2)) ) { mac_payload_ptr_u8 += sizeof(mac_header_80211); switch(((authentication_frame*)mac_payload_ptr_u8)->auth_algorithm ){ case AUTH_ALGO_OPEN_SYSTEM: allow_auth = 1; break; } } // Only send response if the packet was from a requester // if((active_network_info != NULL) && ((authentication_frame*)mac_payload_ptr_u8)->auth_sequence == AUTH_SEQ_REQ){ // Only send response if we are allowing authentication and there is room in the authenticated_unassociated_stations list. // Note: the station from which this MAC_FRAME_CTRL1_SUBTYPE_AUTH was received may already be in the authenticated_unassociated_stations // list, so this should be accounted for when comparing the list length to MAX_NUM_AUTH if(allow_auth && ( (authenticated_unassociated_stations.length-(sta_is_authenticated && (!sta_is_associated))) < MAX_NUM_AUTH ) ){ //Only proceed if: // (1) The authentication type was AUTH_ALGO_OPEN_SYSTEM // and // (2) The number of currently authenticated stations (not counting this station if it // is already authenticated) is less than MAX_NUM_AUTH if(sta_is_associated == 0){ // This station wasn't already authenticated/associated (state 4), so manually add it to the // authenticated/unassociated (state 2) list. // - Set ht_capable argument to 0. This will be updated with the correct value when the // node moves from the state 2 list during association. auth_unassoc_station_info = station_info_add_to_list(&authenticated_unassociated_stations, rx_80211_header->address_2); if(auth_unassoc_station_info != NULL){ auth_unassoc_station_info->flags |= STATION_INFO_FLAG_KEEP; time_hr_min_sec = wlan_mac_time_to_hr_min_sec(get_system_time_usec()); xil_printf("*%dh:%02dm:%02ds* STA %02x:%02x:%02x:%02x:%02x:%02x is now authenticated / unassociated\n", time_hr_min_sec.hr, time_hr_min_sec.min, time_hr_min_sec.sec, rx_80211_header->address_2[0], rx_80211_header->address_2[1], rx_80211_header->address_2[2], rx_80211_header->address_2[3], rx_80211_header->address_2[4], rx_80211_header->address_2[5]); } } // Create a successful authentication response frame tx_queue_entry = queue_checkout(); if(tx_queue_entry != NULL){ tx_queue_buffer = (tx_80211_queue_buffer_t*)(tx_queue_entry->data); tx_length = wlan_create_auth_frame( tx_queue_buffer->pkt, rx_80211_header->address_2, wlan_mac_addr, wlan_mac_addr, AUTH_ALGO_OPEN_SYSTEM, AUTH_SEQ_RESP, STATUS_SUCCESS); // Fill in metadata tx_queue_buffer->flags = TX_80211_QUEUE_BUFFER_FLAGS_FILL_DURATION; tx_queue_buffer->length = tx_length; tx_queue_buffer->seg0_len = tx_length; tx_queue_buffer->seg1_len = 0; tx_queue_buffer->seg1_offset = 0; tx_queue_buffer->station_info = station_info; // Put the packet in the queue wlan_mac_enqueue_wireless_tx(mgmt_qid, tx_queue_entry); } // Finish the function goto mpdu_rx_process_end; } else { // Create a unsuccessful authentication response frame tx_queue_entry = queue_checkout(); if(tx_queue_entry != NULL){ tx_queue_buffer = (tx_80211_queue_buffer_t*)(tx_queue_entry->data); tx_length = wlan_create_auth_frame( tx_queue_buffer->pkt, rx_80211_header->address_2, wlan_mac_addr, wlan_mac_addr, AUTH_ALGO_OPEN_SYSTEM, AUTH_SEQ_RESP, STATUS_AUTH_REJECT_UNSPECIFIED); // Fill in metadata tx_queue_buffer->flags = TX_80211_QUEUE_BUFFER_FLAGS_FILL_DURATION; tx_queue_buffer->length = tx_length; tx_queue_buffer->seg0_len = tx_length; tx_queue_buffer->seg1_len = 0; tx_queue_buffer->seg1_offset = 0; tx_queue_buffer->station_info = station_info; // Put the packet in the queue wlan_mac_enqueue_wireless_tx(mgmt_qid, tx_queue_entry); } } // Finish the function goto mpdu_rx_process_end; } break; //--------------------------------------------------------------------- case (MAC_FRAME_CTRL1_SUBTYPE_ASSOC_REQ): case (MAC_FRAME_CTRL1_SUBTYPE_REASSOC_REQ): { u32 reject_association = 1; if(station_info != NULL){ // Association Request / Re-association Request // Parse the tagged parameters mac_payload_ptr_u8 = mac_payload_ptr_u8 + sizeof(mac_header_80211) + sizeof(association_req_frame); while ((((u32)mac_payload_ptr_u8) - ((u32)rx_mac_payload)) < (rx_length - WLAN_PHY_FCS_NBYTES)) { // Parse each of the tags switch(mac_payload_ptr_u8[0]){ //------------------------------------------------- case MGMT_TAG_SSID: // SSID parameter set // strncpy(ssid, (char*)(&(mac_payload_ptr_u8[2])), SSID_LEN_MAX); ssid_length = WLAN_MIN(mac_payload_ptr_u8[1],SSID_LEN_MAX); break; //------------------------------------------------- case MGMT_TAG_HT_CAPABILITIES: // This station is capable of HT Tx and Rx sta_is_ht_capable = 1; break; } // Increment packet pointer to the next tag mac_payload_ptr_u8 += mac_payload_ptr_u8[1]+2; } // - Check if the packet is for this BSS. Necessary Criteria: // - RA is unicast and directed to this node's wlan_mac_address // - SSID string in payload of reception matches this BSS's SSID string // - BSSID matches this BSS's ID if( unicast_to_me && (strncmp(ssid, active_network_info->bss_config.ssid, ssid_length) == 0) && (active_network_info != NULL) && wlan_addr_eq(rx_80211_header->address_3, active_network_info->bss_config.bssid)) { // Check if we have authenticated this TA yet have not associated it. // Note: We cannot use the sta_is_authenticated boolean for this, as this also // includes fully associated stations. if( sta_is_associated || (active_network_info->members.length < MAX_NUM_ASSOC) ) reject_association = 0; if((reject_association == 0) && station_info_is_member(&authenticated_unassociated_stations, station_info)){ station_info_remove_from_list(&authenticated_unassociated_stations, rx_80211_header->address_2); // NOTE: The STATION_INFO_FLAG_KEEP bit will still be raised despite having removed the // station_info from the authenticated_unassociated_stations list. This is intentional, // as we are about to add the station_info the the fully associated list. // Add the station info // - Set ht_capable argument to 0. This will be updated below with the // correct value from the tagged parameters in the association request. if (network_info_add_member(active_network_info, rx_80211_header->address_2, max_queue_size)) { time_hr_min_sec = wlan_mac_time_to_hr_min_sec(get_system_time_usec()); xil_printf("*%dh:%02dm:%02ds* STA %02x:%02x:%02x:%02x:%02x:%02x is now associated\n", time_hr_min_sec.hr, time_hr_min_sec.min, time_hr_min_sec.sec, rx_80211_header->address_2[0], rx_80211_header->address_2[1], rx_80211_header->address_2[2], rx_80211_header->address_2[3], rx_80211_header->address_2[4], rx_80211_header->address_2[5]); } wlan_platform_high_userio_disp_status(USERIO_DISP_STATUS_MEMBER_LIST_UPDATE, active_network_info->members.length); } if( reject_association == 0 ) { // Zero the HT_CAPABLE flag if(sta_is_ht_capable){ station_info->capabilities |= STATION_INFO_CAPABILITIES_HT_CAPABLE; } else { station_info->capabilities &= ~STATION_INFO_CAPABILITIES_HT_CAPABLE; } // Do not allow Mango nodes to time out if(wlan_mac_addr_is_mango(rx_80211_header->address_2)){ station_info->flags |= STATION_INFO_FLAG_DISABLE_ASSOC_CHECK; } // // TODO: (Optional) Log association state change // // Create a successful association response frame tx_queue_entry = queue_checkout(); if(tx_queue_entry != NULL){ tx_queue_buffer = (tx_80211_queue_buffer_t*)(tx_queue_entry->data); tx_length = wlan_create_association_response_frame(tx_queue_buffer->pkt, rx_80211_header->address_2, wlan_mac_addr, active_network_info->bss_config.bssid, STATUS_SUCCESS, station_info->ID, active_network_info); // Fill in metadata tx_queue_buffer->flags = TX_80211_QUEUE_BUFFER_FLAGS_FILL_DURATION; tx_queue_buffer->length = tx_length; tx_queue_buffer->seg0_len = tx_length; tx_queue_buffer->seg1_len = 0; tx_queue_buffer->seg1_offset = 0; tx_queue_buffer->station_info = station_info; // Put the packet in the queue wlan_mac_enqueue_wireless_tx(station_info->QID, tx_queue_entry); } // Finish the function goto mpdu_rx_process_end; } else { // Create an unsuccessful association response frame tx_queue_entry = queue_checkout(); if(tx_queue_entry != NULL){ tx_queue_buffer = (tx_80211_queue_buffer_t*)(tx_queue_entry->data); tx_length = wlan_create_association_response_frame(tx_queue_buffer->pkt, rx_80211_header->address_2, wlan_mac_addr, active_network_info->bss_config.bssid, STATUS_REJECT_TOO_MANY_ASSOCIATIONS, 0, active_network_info); // Fill in metadata tx_queue_buffer->flags = TX_80211_QUEUE_BUFFER_FLAGS_FILL_DURATION; tx_queue_buffer->length = tx_length; tx_queue_buffer->seg0_len = tx_length; tx_queue_buffer->seg1_len = 0; tx_queue_buffer->seg1_offset = 0; tx_queue_buffer->station_info = station_info; // Put the packet in the queue wlan_mac_enqueue_wireless_tx(mgmt_qid, tx_queue_entry); } // Finish the function goto mpdu_rx_process_end; } } } } break; //--------------------------------------------------------------------- case (MAC_FRAME_CTRL1_SUBTYPE_DISASSOC): // Disassociation // - Log the association state change // - Remove the association and update the display // if(active_network_info != NULL){ // Note: Since we are no longer keeping this station_info, we should ensure it is present // in neither the fully associated list or the authenticated-only list if(sta_is_associated){ network_info_remove_member(active_network_info, rx_80211_header->address_2); } station_info_remove_from_list(&authenticated_unassociated_stations, rx_80211_header->address_2); time_hr_min_sec = wlan_mac_time_to_hr_min_sec(get_system_time_usec()); xil_printf("*%dh:%02dm:%02ds* STA %02x:%02x:%02x:%02x:%02x:%02x has disassociated\n", time_hr_min_sec.hr, time_hr_min_sec.min, time_hr_min_sec.sec, rx_80211_header->address_2[0], rx_80211_header->address_2[1], rx_80211_header->address_2[2], rx_80211_header->address_2[3], rx_80211_header->address_2[4], rx_80211_header->address_2[5]); // Lower the KEEP flag so that the station_info_ if(station_info != NULL) station_info->flags &= ~STATION_INFO_FLAG_KEEP; wlan_platform_high_userio_disp_status(USERIO_DISP_STATUS_MEMBER_LIST_UPDATE, active_network_info->members.length); } break; case (MAC_FRAME_CTRL1_SUBTYPE_NULLDATA): break; //--------------------------------------------------------------------- default: //This should be left as a verbose print. It occurs often when communicating with mobile devices since they tend to send //null data frames (type: DATA, subtype: 0x4) for power management reasons. wlan_printf(PL_VERBOSE, "Received unknown frame control type/subtype %x\n",rx_80211_header->frame_control_1); break; } } goto mpdu_rx_process_end; } else { // Process any Bad FCS packets goto mpdu_rx_process_end; } // Finish any processing for the RX MPDU process mpdu_rx_process_end: // Currently, asynchronous transmission of log entries is not supported // // if (rx_event_log_entry != NULL) { // wn_transmit_log_entry((void *)rx_event_log_entry); // } return return_val; } /*****************************************************************************/ /** * *****************************************************************************/ void mpdu_dequeue(tx_80211_queue_buffer_t* tx_queue_buffer){ mac_header_80211* header; station_info_t* station_info; header = (mac_header_80211*)(tx_queue_buffer->pkt); station_info = tx_queue_buffer->station_info; if(station_info->num_linked_queue_buffer > 1){ //If the is more data (in addition to this packet) queued for this station, we can let it know //in the frame_control_2 field. header->frame_control_2 |= MAC_FRAME_CTRL2_FLAG_MORE_DATA; } else { header->frame_control_2 &= ~MAC_FRAME_CTRL2_FLAG_MORE_DATA; } } /*****************************************************************************/ /** * @brief Deauthenticate given station in the Association Table * * Deauthenticate the station in the associations table * * @param station_info_t * station_info * - Station to be deauthenticated * @return u32 aid * - AID of the station that was deauthenticated; AID of 0 is reserved to indicate failure *****************************************************************************/ u32 deauthenticate_station( station_info_t* station_info ) { dl_entry* tx_queue_entry; tx_80211_queue_buffer_t* tx_queue_buffer; u32 tx_length; u32 aid; time_hr_min_sec_t time_hr_min_sec; if((active_network_info == NULL) || (station_info == NULL)){ return 0; } // Get the AID aid = station_info->ID; // Send De-authentication packet tx_queue_entry = queue_checkout(); if(tx_queue_entry != NULL){ tx_queue_buffer = (tx_80211_queue_buffer_t*)(tx_queue_entry->data); tx_length = wlan_create_deauth_frame(tx_queue_buffer->pkt, station_info->addr, wlan_mac_addr, wlan_mac_addr, DEAUTH_REASON_INACTIVITY); // Fill in metadata tx_queue_buffer->flags = TX_80211_QUEUE_BUFFER_FLAGS_FILL_DURATION; tx_queue_buffer->length = tx_length; tx_queue_buffer->seg0_len = tx_length; tx_queue_buffer->seg1_len = 0; tx_queue_buffer->seg1_offset = 0; tx_queue_buffer->station_info = station_info; // Put the packet in the queue wlan_mac_enqueue_wireless_tx(mgmt_qid, tx_queue_entry); // Purge any packets in the queue meant for this node wlan_mac_purge_wireless_tx(station_info->QID); } // // TODO: (Optional) Log association state change // // Remove this STA from association list network_info_remove_member(active_network_info, station_info->addr); time_hr_min_sec = wlan_mac_time_to_hr_min_sec(get_system_time_usec()); xil_printf("*%dh:%02dm:%02ds* STA %02x:%02x:%02x:%02x:%02x:%02x has disassociated\n", time_hr_min_sec.hr, time_hr_min_sec.min, time_hr_min_sec.sec, station_info->addr[0], station_info->addr[1], station_info->addr[2], station_info->addr[3], station_info->addr[4], station_info->addr[5]); // Remove the "keep" flag for this station_info so the framework can cleanup later. station_info->flags &= ~STATION_INFO_FLAG_KEEP; wlan_platform_high_userio_disp_status(USERIO_DISP_STATUS_MEMBER_LIST_UPDATE, active_network_info->members.length); return aid; } /*****************************************************************************/ /** * @brief Deauthenticate all stations in the Association Table * * Loop through all associations in the table and deauthenticate the stations * * @param None * @return None *****************************************************************************/ void deauthenticate_all_stations(){ interrupt_state_t curr_interrupt_state; station_info_t* curr_station_info; dl_entry* next_station_info_entry; dl_entry* curr_station_info_entry; int iter; if(active_network_info == NULL) return; iter = active_network_info->members.length; curr_interrupt_state = wlan_platform_intc_stop(); next_station_info_entry = active_network_info->members.first; // Deauthenticate all stations and remove from the association table // // NOTE: Cannot use a for loop for this iteration b/c we could remove // elements from the list. while( (next_station_info_entry != NULL) && (iter-- > 0)){ curr_station_info_entry = next_station_info_entry; next_station_info_entry = dl_entry_next(curr_station_info_entry); curr_station_info = (station_info_t*)(curr_station_info_entry->data); deauthenticate_station(curr_station_info); } wlan_platform_intc_set_state(curr_interrupt_state); } /*****************************************************************************/ /** * @brief Handle a reboot of CPU_LOW * * If CPU_LOW reboots, any parameters we had previously set in it will be lost. * This function is called to tell us that we should re-apply any previous * parameters we had set. * * @param u32 type - type of MAC running in CPU_LOW * @return None *****************************************************************************/ void handle_cpu_low_reboot(u32 type){ if(type & WLAN_EXP_LOW_SW_ID_DCF) { gl_cpu_low_supports_dtim_mcast = 1; } else { gl_cpu_low_supports_dtim_mcast = 0; } if(active_network_info != NULL){ // Re-apply any Beacon Tx configurations wlan_mac_high_config_txrx_beacon(&gl_beacon_txrx_config); // Re-apply DTIM mcast buffering settings wlan_mac_high_enable_mcast_buffering(gl_dtim_mcast_buffer_enable); } } /*****************************************************************************/ /** * *****************************************************************************/ u32 configure_bss(bss_config_t* bss_config, u32 update_mask){ u32 return_status = 0; u8 update_beacon_template = 0; u8 send_beacon_config_to_low = 0; u8 send_channel_switch_to_low = 0; network_info_t* local_network_info; interrupt_state_t curr_interrupt_state; station_info_t* curr_station_info; dl_entry* next_station_info_entry; dl_entry* curr_station_info_entry; int iter; time_hr_min_sec_t time_hr_min_sec; tx_params_t default_beacon_tx_params; //--------------------------------------------------------- // 1. Check for any invalid inputs or combination of inputs // First verify the requested update to the BSS configuration before // modifying anything. This will prevent a partial update of BSS // configuration with valid parameters before discovering an invalid // parameter. Furthermore, we can update the arguments to this function // with any relevant default values in the case that the arguments // are not updated directly by the call to this function and the // active_network_info is NULL, preventing us from inhering whatever // might be there. if (bss_config != NULL){ ////////////////// if (update_mask & BSS_FIELD_MASK_BSSID) { if (wlan_addr_eq(bss_config->bssid, zero_addr) == 0) { if ((active_network_info != NULL) && wlan_addr_eq(bss_config->bssid, active_network_info->bss_config.bssid)) { // The caller of this function claimed that it was updating the BSSID, // but the new BSSID matches the one already specified in active_bss_info. // Complete the rest of this function as if that bit in the update mask // were not set update_mask &= ~BSS_FIELD_MASK_BSSID; } else { // Changing the BSSID, perform necessary argument checks if (wlan_addr_eq(bss_config->bssid, wlan_mac_addr) == 0) { // In the AP implementation, the BSSID provided must be // the hardware MAC address of the node return_status |= BSS_CONFIG_FAILURE_BSSID_INVALID; } if (((update_mask & BSS_FIELD_MASK_SSID) == 0) || ((update_mask & BSS_FIELD_MASK_CHAN) == 0) || ((update_mask & BSS_FIELD_MASK_BEACON_INTERVAL) == 0)) { return_status |= BSS_CONFIG_FAILURE_BSSID_INSUFFICIENT_ARGUMENTS; } } } } else { if (active_network_info == NULL) { // Cannot update BSS without specifying BSSID return_status |= BSS_CONFIG_FAILURE_BSSID_INSUFFICIENT_ARGUMENTS; } } ////////////////// if (update_mask & BSS_FIELD_MASK_CHAN) { if (wlan_verify_channel( wlan_mac_high_bss_channel_spec_to_radio_chan(bss_config->chan_spec)) != WLAN_SUCCESS) { return_status |= BSS_CONFIG_FAILURE_CHANNEL_INVALID; } } ////////////////// if (update_mask & BSS_FIELD_MASK_BEACON_INTERVAL) { if ((bss_config->beacon_interval != BEACON_INTERVAL_NO_BEACON_TX) && (bss_config->beacon_interval < 10)) { return_status |= BSS_CONFIG_FAILURE_BEACON_INTERVAL_INVALID; } } ////////////////// if (update_mask & BSS_FIELD_MASK_HT_CAPABLE) { if (bss_config->ht_capable > 1) { return_status |= BSS_CONFIG_FAILURE_HT_CAPABLE_INVALID; } } else if(active_network_info == NULL) { // Adopt C default value bss_config->ht_capable = WLAN_DEFAULT_BSS_CONFIG_HT_CAPABLE; update_mask |= BSS_FIELD_MASK_HT_CAPABLE; } ////////////////// if (update_mask & BSS_FIELD_MASK_DTIM_PERIOD) { if(bss_config->dtim_period == 0){ return_status |= BSS_CONFIG_FAILURE_DTIM_PERIOD_INVALID; } } else if(active_network_info == NULL) { // Adopt C default value bss_config->dtim_period = WLAN_DEFAULT_BSS_CONFIG_DTIM_PERIOD; update_mask |= BSS_FIELD_MASK_DTIM_PERIOD; } } if (return_status == 0) { //--------------------------------------------------------- // 2. Apply BSS configuration changes // Now that the provided bss_config_t struct is valid, apply the changes. // Disable interrupts around these modifications to prevent state // changing out from underneath this context while the new BSS // configuration parameters are only partially updated. curr_interrupt_state = wlan_platform_intc_stop(); if ((bss_config == NULL) || (update_mask & BSS_FIELD_MASK_BSSID)) { // Adopting a new BSSID. This could mean either // 1) Shutting the BSS down // or 2) Shutting the BSS down and then starting a new BSS. // // In either case, first remove any station_info structs // that are members of the current active_bss_info and return to // a NULL active_bss_info state. // // This will not result in any OTA transmissions to the stations. if (active_network_info != NULL) { //Purge all tranmissions queues purge_all_wireless_tx_queue(); if ((bss_config == NULL) || ((update_mask & BSS_FIELD_MASK_BSSID) && wlan_addr_eq(bss_config->bssid, zero_addr)) ) { xil_printf("Terminating BSS\n"); } // Remove all associations next_station_info_entry = active_network_info->members.first; iter = active_network_info->members.length; while ((next_station_info_entry != NULL) && (iter-- > 0)) { curr_station_info_entry = next_station_info_entry; next_station_info_entry = dl_entry_next(curr_station_info_entry); curr_station_info = (station_info_t*)(curr_station_info_entry->data); // Remove the associations network_info_remove_member(active_network_info, curr_station_info->addr); time_hr_min_sec = wlan_mac_time_to_hr_min_sec(get_system_time_usec()); xil_printf("*%dh:%02dm:%02ds* STA %02x:%02x:%02x:%02x:%02x:%02x has disassociated\n", time_hr_min_sec.hr, time_hr_min_sec.min, time_hr_min_sec.sec, curr_station_info->addr[0], curr_station_info->addr[1], curr_station_info->addr[2], curr_station_info->addr[3], curr_station_info->addr[4], curr_station_info->addr[5]); // Lower KEEP flag so that the MAC High Framework can cleanup curr_station_info->flags &= ~STATION_INFO_FLAG_KEEP; // Update the hex display to show station was removed wlan_platform_high_userio_disp_status(USERIO_DISP_STATUS_MEMBER_LIST_UPDATE, active_network_info->members.length); } // Remove the bss_info from the network list // - When the AP "leaves" a BSS, the BSS actually no longer exists. Therefore, it should // not continue to exist in the network list. dl_list* network_info_list = wlan_mac_high_get_network_info_list(); network_info_entry_t* network_entry = wlan_mac_high_find_network_info_BSSID(active_network_info->bss_config.bssid); wlan_mac_high_clear_network_info(active_network_info); dl_entry_remove(network_info_list, (dl_entry*)network_entry); network_info_checkin(network_entry); // Set "active_network_info" to NULL // - All functions must be able to handle active_network_info = NULL active_network_info = NULL; // Disable beacons immediately ((tx_frame_info_t*)CALC_PKT_BUF_ADDR(platform_common_dev_info.tx_pkt_buf_baseaddr, TX_PKT_BUF_BEACON))->tx_pkt_buf_state = TX_PKT_BUF_HIGH_CTRL; gl_beacon_txrx_config.beacon_tx_mode = NO_BEACON_TX; bzero(gl_beacon_txrx_config.bssid_match, MAC_ADDR_LEN); wlan_mac_high_config_txrx_beacon(&gl_beacon_txrx_config); // Set hex display to "No BSS" wlan_platform_high_userio_disp_status(USERIO_DISP_STATUS_MEMBER_LIST_UPDATE, 0xFF); } // (bss_config_update == NULL) is one way to remove the BSS state of the node. This operation // was executed just above. Rather that continuing to check non-NULLness of bss_config_update // throughout the rest of this function, just re-enable interrupts and return early. if (bss_config == NULL) { wlan_platform_intc_set_state(curr_interrupt_state); return return_status; } // active_network_info is guaranteed to be NULL at this point in the code // bss_config_update is guaranteed to be non-NULL at this point in the code // Update BSS // - BSSID must not be zero_addr (reserved address) if (wlan_addr_eq(bss_config->bssid, zero_addr) == 0) { // Stop the scan state machine if it is running if (wlan_mac_scan_is_scanning()) { wlan_mac_scan_stop(); } // Create a new network_info_t or overwrite an existing one with matching BSSID. // Note: The wildcard SSID and 0-valued channel arguments are temporary. Because // of the error checking at the top of this function, the bss_config will // contain a valid SSID as well as channel. These fields will be updated // in step 3). local_network_info = wlan_mac_high_create_network_info(bss_config->bssid, "", 0); if (local_network_info != NULL) { local_network_info->flags |= NETWORK_FLAGS_KEEP; local_network_info->capabilities = (BSS_CAPABILITIES_ESS); active_network_info = local_network_info; } // Set hex display wlan_platform_high_userio_disp_status(USERIO_DISP_STATUS_MEMBER_LIST_UPDATE, active_network_info->members.length); } } //--------------------------------------------------------- // 3. Clean up // Now that active_network_info has been updated, CPU_HIGH can communicate // the changes to CPU_LOW so that the node is tuned to the correct channel, // send beacons at the correct interval, and update the beacon // template packet buffer. if (active_network_info != NULL) { if (update_mask & BSS_FIELD_MASK_CHAN) { active_network_info->bss_config.chan_spec = bss_config->chan_spec; send_channel_switch_to_low = 1; update_beacon_template = 1; } if (update_mask & BSS_FIELD_MASK_SSID) { strncpy(active_network_info->bss_config.ssid, bss_config->ssid, SSID_LEN_MAX); update_beacon_template = 1; } if (update_mask & BSS_FIELD_MASK_BEACON_INTERVAL) { active_network_info->bss_config.beacon_interval = bss_config->beacon_interval; memcpy(gl_beacon_txrx_config.bssid_match, active_network_info->bss_config.bssid, MAC_ADDR_LEN); if ((active_network_info->bss_config.beacon_interval == BEACON_INTERVAL_NO_BEACON_TX) || (active_network_info->bss_config.beacon_interval == BEACON_INTERVAL_UNKNOWN)) { ((tx_frame_info_t*)CALC_PKT_BUF_ADDR(platform_common_dev_info.tx_pkt_buf_baseaddr, TX_PKT_BUF_BEACON))->tx_pkt_buf_state = TX_PKT_BUF_HIGH_CTRL; gl_beacon_txrx_config.beacon_tx_mode = NO_BEACON_TX; } else { gl_beacon_txrx_config.beacon_tx_mode = AP_BEACON_TX; } gl_beacon_txrx_config.beacon_interval_tu = active_network_info->bss_config.beacon_interval; gl_beacon_txrx_config.beacon_template_pkt_buf = TX_PKT_BUF_BEACON; update_beacon_template = 1; send_beacon_config_to_low = 1; } if (update_mask & BSS_FIELD_MASK_DTIM_PERIOD) { gl_beacon_txrx_config.dtim_period = bss_config->dtim_period; active_network_info->bss_config.dtim_period = bss_config->dtim_period; send_beacon_config_to_low = 1; } if (update_mask & BSS_FIELD_MASK_HT_CAPABLE) { // Changing the BSS HT_CAPABLE capabilities only affects what is advertised in // the BSS beacons. It does not affect the HT capabilities of nodes in the // network. It should also not change any of the default TX params since the // AP is still capable of sending and receiving HT packets. active_network_info->bss_config.ht_capable = bss_config->ht_capable; // Update the beacon template to match capabilities update_beacon_template = 1; } // Update the beacon template // In the event that CPU_LOW currently has the beacon packet buffer locked, // block for now until it unlocks. This will guarantee that beacon are updated // before the function returns. default_beacon_tx_params = wlan_mac_get_default_tx_params(mcast_mgmt); if (update_beacon_template) { while( wlan_mac_high_configure_beacon_tx_template((u8*)bcast_addr, wlan_mac_addr, active_network_info->bss_config.bssid, active_network_info, &default_beacon_tx_params, TX_FRAME_INFO_FLAGS_FILL_TIMESTAMP | TX_FRAME_INFO_FLAGS_WAIT_FOR_LOCK) != 0 ){} mgmt_tag_tim_template = NULL; //Add the TIM tag update_tim_tag_all(SCHEDULE_ID_RESERVED_MAX); } // Update the channel if (send_channel_switch_to_low) { wlan_mac_high_set_radio_channel( wlan_mac_high_bss_channel_spec_to_radio_chan(active_network_info->bss_config.chan_spec)); } // Update Beacon configuration if (send_beacon_config_to_low) { wlan_mac_high_config_txrx_beacon(&gl_beacon_txrx_config); } // Print new BSS information xil_printf("BSS Details: \n"); xil_printf(" BSSID : %02x-%02x-%02x-%02x-%02x-%02x\n", active_network_info->bss_config.bssid[0],active_network_info->bss_config.bssid[1], active_network_info->bss_config.bssid[2],active_network_info->bss_config.bssid[3], active_network_info->bss_config.bssid[4],active_network_info->bss_config.bssid[5]); xil_printf(" SSID : %s\n", active_network_info->bss_config.ssid); xil_printf(" Channel : %d\n", wlan_mac_high_bss_channel_spec_to_radio_chan(active_network_info->bss_config.chan_spec)); if (active_network_info->bss_config.beacon_interval == BEACON_INTERVAL_NO_BEACON_TX) { xil_printf(" Beacon Interval: No Beacon Tx\n"); } else if (active_network_info->bss_config.beacon_interval == BEACON_INTERVAL_UNKNOWN) { xil_printf(" Beacon Interval: Unknown\n"); } else { xil_printf(" Beacon Interval: %d TU (%d us)\n", active_network_info->bss_config.beacon_interval, active_network_info->bss_config.beacon_interval*1024); } } // Restore interrupts after all BSS changes wlan_platform_intc_set_state(curr_interrupt_state); } return return_status; } /*****************************************************************************/ /** * @brief Callback to handle push of User I/O button 0 * * Reference implementation does nothing. * * @param None * @return None *****************************************************************************/ void button_0_press(){ return; } /*****************************************************************************/ /** * @brief Callback to handle release of User I/O button 0 * * Reference implementation does nothing. * * @param None * @return None *****************************************************************************/ void button_0_release(){ return; } /*****************************************************************************/ /** * @brief Accessor methods for global variables * * These functions will return pointers to global variables * * @param None * @return None *****************************************************************************/ dl_list* get_network_member_list(){ if(active_network_info != NULL){ return &(active_network_info->members); } else { return NULL; } } network_info_t* active_network_info_getter(){ return active_network_info; } #if WLAN_SW_CONFIG_ENABLE_WLAN_EXP // **************************************************************************** // Define MAC Specific User Commands // // NOTE: All User Command IDs (CMDID_*) must be a 24 bit unique number // //----------------------------------------------- // MAC Specific User Commands // // #define CMDID_USER_ 0x100000 //----------------------------------------------- // MAC Specific User Command Parameters // // #define CMD_PARAM_USER_ 0x00000000 /*****************************************************************************/ /** * Process User Commands * * This function is part of the WLAN Exp framework and will process the framework- * level user commands. This function intentionally does not implement any user * commands and it is left to the user to implement any needed functionality. By * default, any commands not processed in this function will print an error to the * UART. * * @return int - Status of the command: * NO_RESP_SENT - No response has been sent * RESP_SENT - A response has been sent * * @note See on-line documentation for more information: * https://warpproject.org/trac/wiki/802.11/wlan_exp/Extending * *****************************************************************************/ int wlan_exp_process_user_cmd(cmd_resp_hdr_t* cmd_hdr, eth_tx_queue_buffer_t* eth_tx_queue_buffer) { // // IMPORTANT ENDIAN NOTES: // - command // - header - Already endian swapped by the framework (safe to access directly) // - args - Must be endian swapped as necessary by code (framework does not know the contents of the command) // - response // - header - Will be endian swapped by the framework (safe to write directly) // - args - Must be endian swapped as necessary by code (framework does not know the contents of the response) // // Standard variables // // Used for accessing command arguments and constructing the command response header/payload // // NOTE: Some of the standard variables below have been commented out. This was to remove // compiler warnings for "unused variables" since the default implementation is empty. As // you add commands, you should un-comment the standard variables. // u32 resp_sent = NO_RESP_SENT; u32 cmd_id = CMD_TO_CMDID(cmd_hdr->cmd); #if 0 // Segment 0 length includes a fully formed command response header // because one was created with default values suitable for a responseless // acknowledgment. cmd_resp_hdr_t* resp_hdr = (cmd_resp_hdr_t*)(eth_tx_queue_buffer->seg0 + eth_tx_queue_buffer->seg0_len - sizeof(cmd_resp_hdr_t)); u32* cmd_args_32 = (u32*)((u8*)cmd_hdr + sizeof(cmd_resp_hdr_t)); #endif switch(cmd_id){ //----------------------------------------------------------------------------- // MAC Specific User Commands //----------------------------------------------------------------------------- // Template framework for a Command // // NOTE: The WLAN Exp framework assumes that the Over-the-Wire format of the data is // big endian. However, the node processes data using little endian. Therefore, // any data received from the host must be properly endian-swapped and similarly, // any data sent to the host must be properly endian-swapped. The built-in Xilinx // functions: Xil_Ntohl() (Network to Host) and Xil_Htonl() (Host to Network) are // used for this. // #if 0 //--------------------------------------------------------------------- case CMDID_USER_: { // Command Description // // Message format: // cmd_args_32[0:N] Document command arguments from the host // // Response format: // resp_args_32[0:M] Document response arguments from the node // // NOTE: Variables are declared above. // NOTE: Please take care of the endianness of the arguments (see comment above) // // Variables for template command int status; u32 arg_0; interrupt_state_t curr_interrupt_state; // Initialize variables status = CMD_PARAM_SUCCESS; arg_0 = Xil_Ntohl(cmd_args_32[0]); // Swap endianness of command argument // Do something with argument(s) xil_printf("Command argument 0: 0x%08x\n", arg_0); // If necessary, disable interrupts before processing the command // Interrupts must be disabled if the command implementation relies on any state that might // change during an interrupt service routine. See the user guide for more details // https://warpproject.org/trac/wiki/802.11/wlan_exp/Extending curr_interrupt_state = wlan_platform_intc_stop(); // Process command arguments and generate any response payload // NOTE: If interrupts were disabled above, take care to avoid any long-running code in this // block (i.e. avoid xil_printf()). When interrupts are disabled, CPU High is unable to // respond to CPU Low (ie CPU High will not send / receive packets) and execute scheduled // tasks, such as LTGs. // Re-enable interrupts before returning (only do this if wlan_platform_intc_stop() is called above) wlan_platform_intc_set_state(curr_interrupt_state); // Send response // NOTE: It is good practice to send a status as the first argument of the response. // This way it is easy to determine if the other data in the response is valid. // Predefined status values are: CMD_PARAM_SUCCESS, CMD_PARAM_ERROR // wlan_exp_add_u32_resp_arg(eth_tx_queue_buffer, resp_hdr, status); } break; #endif //--------------------------------------------------------------------- default: { wlan_exp_printf(WLAN_EXP_PRINT_ERROR, print_type_node, "Unknown AP user command: 0x%x\n", cmd_id); } break; } return resp_sent; } #endif