/** @file wlan_mac_ibss.c * @brief Station * * This contains code for the 802.11 IBSS node (ad hoc). * * @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 SDK includes #include "stdio.h" #include "stdlib.h" #include "string.h" #include "xil_cache.h" // WLAN includes #include "wlan_platform_common.h" #include "wlan_platform_high.h" #include "wlan_mac_802_11_defs.h" #include "wlan_mac_queue.h" #include "wlan_mac_event_log.h" #include "wlan_mac_entries.h" #include "wlan_mac_ltg.h" #include "wlan_mac_high.h" #include "wlan_mac_packet_types.h" #include "wlan_mac_eth_util.h" #include "wlan_mac_scan.h" #include "ascii_characters.h" #include "wlan_mac_schedule.h" #include "wlan_mac_dl_list.h" #include "wlan_mac_network_info.h" #include "wlan_mac_station_info.h" #include "wlan_mac_ibss.h" #include "wlan_mac_mgmt_tags.h" #include "wlan_mac_common.h" #include "wlan_mac_pkt_buf_util.h" // WLAN Exp includes #include "wlan_exp.h" #include "wlan_exp_common.h" #include "wlan_exp_node.h" #include "wlan_exp_node_ibss.h" #include "wlan_exp_transport.h" #include "wlan_exp_user.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 #define WLAN_DEFAULT_SCAN_TIMEOUT_USEC_MIN 4000000 #define WLAN_DEFAULT_SCAN_TIMEOUT_USEC_MAX 8000000 // WLAN_DEFAULT_USE_HT // // The WLAN_DEFAULT_USE_HT define will set the default unicast TX phy mode // to: 1 --> HTMF or 0 --> NONHT. It will also be used as the default // value for the HT_CAPABLE capability of the BSS in configure_bss() when // moving from a NULL to a non-NULL BSS and the ht_capable parameter is not // specified. This parameter only affects how the MAC selects the phy_mode // value for transmissions. It does not affect the underlying PHY support for // Tx/Rx of HTMF waveforms. #define WLAN_DEFAULT_USE_HT 1 /*********************** Global Variable Definitions *************************/ // Static QIDs u16 mcast_qid; u16 mgmt_qid; // If you want this station to try to associate to a known IBSS at boot, type // the string here. Otherwise, let it be an empty string. static char default_ssid[SSID_LEN_MAX + 1] = "MANGO-IBSS"; // static char default_ssid[SSID_LEN_MAX + 1] = ""; // Top level IBSS state network_info_t* active_network_info; // Tx queue variables; u32 max_queue_size; volatile u8 pause_data_queue; // MAC address static u8 wlan_mac_addr[MAC_ADDR_LEN]; // Beacon configuration static beacon_txrx_config_t gl_beacon_txrx_config; // 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 void send_probe_req(); void ibss_set_beacon_ts_update_mode(u32 enable); /******************************** 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(); u64 scan_start_timestamp; u64 scan_duration; u8 locally_administered_addr[MAC_ADDR_LEN]; dl_list* ssid_match_list = NULL; dl_entry* temp_dl_entry = NULL; network_info_t* temp_network_info = NULL; bss_config_t bss_config; u32 update_mask; // Print initial message to UART xil_printf("\f"); xil_printf("----- Mango 802.11 Reference Design -----\n"); xil_printf("----- v1.8.0 ----------------------------\n"); xil_printf("----- wlan_mac_ibss ---------------------\n"); xil_printf("Compiled %s %s\n\n", __DATE__, __TIME__); wlan_mac_common_malloc_init(); // Initialize the maximum TX queue size max_queue_size = MAX_TX_QUEUE_LEN; // Unpause the queue pause_data_queue = 0; // Initialize beacon configuration gl_beacon_txrx_config.ts_update_mode = FUTURE_ONLY_UPDATE; bzero(gl_beacon_txrx_config.bssid_match, MAC_ADDR_LEN); gl_beacon_txrx_config.beacon_tx_mode = NO_BEACON_TX; // Initialize the utility library wlan_mac_high_init(); // Open static queues mcast_qid = queue_open((void*)wlan_null_callback, 0, max_queue_size); mgmt_qid = queue_open((void*)wlan_null_callback, 0, max_queue_size); // Get the device info platform_common_dev_info = wlan_platform_common_get_dev_info(); // IBSS is not currently a member of BSS configure_bss(NULL, 0); wlan_platform_high_userio_disp_status(USERIO_DISP_STATUS_APPLICATION_ROLE, APPLICATION_ROLE_IBSS); // 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_uart_rx_callback((void*) uart_rx); wlan_mac_high_set_poll_tx_queues_callback((void*) poll_tx_queues); #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_tx_probe_request_callback((void*) send_probe_req); 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); #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((void*) ibss_set_beacon_ts_update_mode); 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_IBSS, __DATE__, __TIME__); #endif // CPU Low will pass HW information to CPU High as part of the boot process // - Get necessary HW information 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); #if WLAN_SW_CONFIG_ENABLE_LOGGING // Reset the event log event_log_reset(); #endif //WLAN_SW_CONFIG_ENABLE_LOGGING // Print Station information to the terminal xil_printf("------------------------\n"); xil_printf("WLAN MAC IBSS boot complete.\n"); #ifdef WLAN_USE_UART_MENU xil_printf("\nPress the Esc key in your terminal to access the UART menu\n"); #endif // Start the interrupts wlan_platform_intc_set_state(INTERRUPTS_ENABLED); // If there is a default SSID and the DIP switch allows it, start an active scan // - Uses default scan parameters if ((strlen(default_ssid) > 0) && ((wlan_platform_userio_get_state() & USERIO_INPUT_MASK_SW_3) == 0)) { // To prevent multiple IBSS nodes from scanning and then all creating the same network simply // because they were powered on at the same time, we randomize the amount of time spent scanning // for a network between [WLAN_DEFAULT_SCAN_TIMEOUT_USEC_MIN, WLAN_DEFAULT_SCAN_TIMEOUT_USEC_MAX] scan_duration = WLAN_DEFAULT_SCAN_TIMEOUT_USEC_MIN + (( (1000*(u64)rand()) / ((u64)RAND_MAX) ) * (WLAN_DEFAULT_SCAN_TIMEOUT_USEC_MAX - WLAN_DEFAULT_SCAN_TIMEOUT_USEC_MIN)) / 1000; scan_start_timestamp = get_system_time_usec(); wlan_mac_scan_start(); while (((get_system_time_usec() < (scan_start_timestamp + scan_duration))) && (temp_dl_entry == NULL)) { // Only try to find a match if the IBSS has completed at least one full scan if (wlan_mac_scan_get_num_scans() > 0) { ssid_match_list = wlan_mac_high_find_network_info_SSID(default_ssid); if (ssid_match_list->length > 0) { // Join the first entry in the list // - This could be modified in the future to use some other selection, // for example RX power. // temp_dl_entry = ssid_match_list->first; } } } wlan_mac_scan_stop(); // Set the BSSID / SSID / Channel based on whether the scan was successful if (temp_dl_entry != NULL) { // Found an existing network matching the default SSID. Adopt that network's BSS configuration xil_printf("Found existing %s network. Matching BSS settings.\n", default_ssid); temp_network_info = (network_info_t*)(temp_dl_entry->data); bss_config = temp_network_info->bss_config; } else { // Did not find an existing network matching the default SSID. Create default BSS configuration xil_printf("Unable to find '%s' IBSS. Creating new network.\n", default_ssid); // Use node's wlan_mac_addr as BSSID // - Raise the bit identifying this address as locally administered memcpy(locally_administered_addr, wlan_mac_addr, MAC_ADDR_LEN); locally_administered_addr[0] |= MAC_ADDR_MSB_MASK_LOCAL; memcpy(bss_config.bssid, locally_administered_addr, MAC_ADDR_LEN); strncpy(bss_config.ssid, default_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.beacon_interval = WLAN_DEFAULT_BSS_CONFIG_BEACON_INTERVAL; bss_config.ht_capable = WLAN_DEFAULT_BSS_CONFIG_HT_CAPABLE; } // Set the rest of the bss_config fields update_mask = (BSS_FIELD_MASK_BSSID | BSS_FIELD_MASK_CHAN | BSS_FIELD_MASK_SSID | BSS_FIELD_MASK_BEACON_INTERVAL | BSS_FIELD_MASK_HT_CAPABLE); // Set the BSS configuration configure_bss(&bss_config, update_mask); } // Schedule Events wlan_mac_schedule_add_event(SCHEDULE_ID_COARSE, ASSOCIATION_CHECK_INTERVAL_US, SCHEDULE_REPEAT_FOREVER, (void*)remove_inactive_station_infos); while(1){ wlan_mac_poll(); } // Unreachable, but non-void return keeps the compiler happy return WLAN_FAILURE; } /*****************************************************************************/ /** * @brief Send probe requet * * This function is part of the scan infrastructure and will be called whenever * the node needs to send a probe request. * * @param None * @return None * *****************************************************************************/ void send_probe_req(){ u16 tx_length; dl_entry* tx_queue_entry; tx_80211_queue_buffer_t* tx_queue_buffer; volatile scan_parameters_t* scan_parameters = wlan_mac_scan_get_parameters(); // Check out queue element for packet tx_queue_entry = queue_checkout(); // Create probe request if(tx_queue_entry != NULL){ tx_queue_buffer = (tx_80211_queue_buffer_t*)(tx_queue_entry->data); tx_length = wlan_create_probe_req_frame(tx_queue_buffer->pkt, (u8*)bcast_addr, wlan_mac_addr, (u8*)bcast_addr, scan_parameters->ssid); // Fill in metadata tx_queue_buffer->flags = 0; 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_create((u8*)bcast_addr); // Put the packet in the queue wlan_mac_enqueue_wireless_tx(mgmt_qid, tx_queue_entry); // Poll the TX queues to possibly send the packet poll_tx_queues(PKT_BUF_GROUP_GENERAL); } } /*****************************************************************************/ /** * @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 // IBSS 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 Poll Tx queues to select next available packet to transmit * *****************************************************************************/ #define NUM_QUEUE_GROUPS 2 typedef enum queue_group_t{ MGMT_QGRP, DATA_QGRP } queue_group_t; /*****************************************************************************/ /** * @brief Poll Tx queues to select next available packet to transmit * * This function will attempt to completely fill all Tx packet buffers in the * PKT_BUF_GROUP_GENERAL group. Dequeueing occurs 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 probe responses. * 2) Data frames will be dequeued round-robin for station for which packets are enqueued. * Multicast frames are treated like their own station for the purposes of this policy. * *****************************************************************************/ void poll_tx_queues(){ interrupt_state_t curr_interrupt_state; dl_entry* tx_queue_entry; u32 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(i = 0; i < (active_network_info->members.length + 1); 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); 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); } 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) 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; int iter = active_network_info->members.length; // Purge all data transmit queues wlan_mac_purge_wireless_tx(mcast_qid); // Broadcast Queue wlan_mac_purge_wireless_tx(mgmt_qid); // Unicast Management queue if(active_network_info != NULL){ 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); // Each unicast queue curr_station_info_entry = dl_entry_next(curr_station_info_entry); } } } /*****************************************************************************/ /** * @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){ ethernet_header_t* eth_hdr; tx_80211_queue_buffer_t* tx_queue_buffer; station_info_t* station_info = NULL; station_info_entry_t* station_info_entry; u32 queue_id; if(active_network_info != NULL){ 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; //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, WLAN_ETH_ENCAP_FLAGS_OVERWRITE_PYLD_ADDRS ); if(tx_queue_buffer){ tx_queue_buffer->flags = 0; wlan_create_data_frame_header(tx_queue_buffer->pkt, eth_hdr->dest_mac_addr, wlan_mac_addr, active_network_info->bss_config.bssid, 0); if( wlan_addr_mcast(eth_hdr->dest_mac_addr) ){ // Fill in metadata queue_id = mcast_qid; tx_queue_buffer->flags = 0; // 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); } else { station_info_entry = station_info_find_by_addr(eth_hdr->dest_mac_addr, &active_network_info->members); if(station_info_entry != NULL){ station_info = (station_info_t*)station_info_entry->data; } else { // Add station info // - Set ht_capable argument to the HT_CAPABLE capability of the BSS. Given that the node does not know // the HT capabilities of the new station, it is reasonable to assume that they are the same as the BSS. // station_info = network_info_add_member(active_network_info, eth_hdr->dest_mac_addr, max_queue_size); if(station_info != NULL){ if(active_network_info->bss_config.ht_capable){ station_info->capabilities |= STATION_INFO_CAPABILITIES_HT_CAPABLE; } else { station_info->capabilities &= ~STATION_INFO_CAPABILITIES_HT_CAPABLE; } station_info->flags |= STATION_INFO_FLAG_KEEP; time_hr_min_sec_t time_hr_min_sec = wlan_mac_time_to_hr_min_sec(get_system_time_usec()); xil_printf("*%dh:%02dm:%02ds* IBSS 0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x added to BSS\n", time_hr_min_sec.hr, time_hr_min_sec.min, time_hr_min_sec.sec, eth_hdr->dest_mac_addr[0], eth_hdr->dest_mac_addr[1], eth_hdr->dest_mac_addr[2], eth_hdr->dest_mac_addr[3], eth_hdr->dest_mac_addr[4], eth_hdr->dest_mac_addr[5]); } wlan_platform_high_userio_disp_status(USERIO_DISP_STATUS_MEMBER_LIST_UPDATE, active_network_info->members.length); } if( station_info == NULL ){ queue_id = mcast_qid; } else { queue_id = station_info->QID; } // Fill in metadata tx_queue_buffer->flags = TX_80211_QUEUE_BUFFER_FLAGS_FILL_DURATION; tx_queue_buffer->station_info = station_info; } } else { // Ethernet encapsulation failed, so we have nothing to send return 0; } if(queue_length(queue_id) < max_queue_size){ // Put the packet in the queue wlan_mac_enqueue_wireless_tx(queue_id, tx_queue_buffer->pyld_queue_hdr.dle); } else { // Packet was not successfully enqueued return 0; } // Packet was successfully enqueued return 1; } else { // Packet was not successfully enqueued return 0; } } /*****************************************************************************/ /** * @brief Process received MPDUs * * This callback function will process all the received MPDUs.. * * @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; void* mac_payload = (u8*)pkt_buf_addr + PHY_RX_PKT_BUF_MPDU_OFFSET; u8* mac_payload_ptr_u8 = (u8*)mac_payload; mac_header_80211* rx_80211_header = (mac_header_80211*)((void *)mac_payload_ptr_u8); u16 rx_seq; dl_entry* tx_queue_entry; tx_80211_queue_buffer_t* tx_queue_buffer; u8 unicast_to_me; u8 to_multicast; u8 send_response = 0; u32 tx_length; u32 return_val = 0; u16 length = rx_frame_info->phy_details.length; // 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 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; } } // Update the association information if(active_network_info != NULL){ if(wlan_addr_eq(rx_80211_header->address_3, active_network_info->bss_config.bssid)){ if( station_info != NULL && station_info_is_member(&active_network_info->members, station_info) == 0 ){ // Add station info // - Set ht_capable argument to the HT_CAPABLE capability of the BSS. Given that the node does not know // the HT capabilities of the new station, it is reasonable to assume that they are the same as the BSS. // // Note: we do not need the returned station_info_t* from this function since it is guaranteed to match // the "station_info" argument to the mpdu_rx_process function network_info_add_member(active_network_info, rx_80211_header->address_2, max_queue_size); // Open a queue for this station for outgoing unicast DATA frames if(active_network_info->bss_config.ht_capable){ station_info->capabilities |= STATION_INFO_CAPABILITIES_HT_CAPABLE; } else { station_info->capabilities &= ~STATION_INFO_CAPABILITIES_HT_CAPABLE; } station_info->flags |= STATION_INFO_FLAG_KEEP; time_hr_min_sec_t time_hr_min_sec = wlan_mac_time_to_hr_min_sec(get_system_time_usec()); xil_printf("*%dh:%02dm:%02ds* IBSS 0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x added to BSS\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(station_info != NULL) { // Check if this was a duplicate reception // - Received seq num matched previously received seq num for this STA if( return_val & MAC_RX_CALLBACK_RETURN_FLAG_DUP) { // Finish the function 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 // - If the STA is associated with the AP and this is from the DS, then transmit over the wired network // if(active_network_info != NULL){ if(wlan_addr_eq(rx_80211_header->address_3, active_network_info->bss_config.bssid)) { // MPDU is flagged as destined to the DS - send it for de-encapsulation and Ethernet Tx (if appropriate) #if WLAN_SW_CONFIG_ENABLE_ETH_BRIDGE wlan_eth_decap_and_send(mac_payload, NULL, rx_80211_header->address_2, length, WLAN_ETH_ENCAP_FLAGS_OVERWRITE_PYLD_ADDRS); #endif } } break; //--------------------------------------------------------------------- case (MAC_FRAME_CTRL1_SUBTYPE_PROBE_REQ): if(active_network_info != NULL){ if(wlan_addr_eq(rx_80211_header->address_3, bcast_addr)) { mac_payload_ptr_u8 += sizeof(mac_header_80211); // Loop through tagged parameters while(((u32)mac_payload_ptr_u8 - (u32)mac_payload)<= (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((mac_payload_ptr_u8[1]==0) || (memcmp(mac_payload_ptr_u8+2, (u8*)default_ssid, mac_payload_ptr_u8[1])==0)) { // Broadcast SSID or my SSID - send unicast probe response 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(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, active_network_info->bss_config.bssid, 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; //--------------------------------------------------------------------- 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; } } // Finish the function 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: return return_val; } /*****************************************************************************/ /** * @brief Check the time since the station has interacted with another station * * * @param None * @return None *****************************************************************************/ void remove_inactive_station_infos() { u64 time_since_last_activity; station_info_t* curr_station_info; station_info_entry_t* curr_station_info_entry; station_info_entry_t* next_station_info_entry; if(active_network_info != NULL){ next_station_info_entry = (station_info_entry_t*)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)){ wlan_mac_purge_wireless_tx(curr_station_info->QID); network_info_remove_member( active_network_info, curr_station_info->addr ); wlan_platform_high_userio_disp_status(USERIO_DISP_STATUS_MEMBER_LIST_UPDATE, active_network_info->members.length); time_hr_min_sec_t time_hr_min_sec = wlan_mac_time_to_hr_min_sec(get_system_time_usec()); xil_printf("*%dh:%02dm:%02ds* IBSS 0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x removed from BSS\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]); curr_station_info->flags &= ~STATION_INFO_FLAG_KEEP; } } } } #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_t 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; u32 min_ltg_payload_length; station_info_entry_t* station_info_entry = NULL; station_info_t* station_info = NULL; u8* addr_da; 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; u8* addr_ra; u16 duration; 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; // 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){ 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; is_multicast = wlan_addr_mcast(addr_da); if(is_multicast){ queue_id = mcast_qid; } else { flags |= TX_80211_QUEUE_BUFFER_FLAGS_FILL_DURATION; station_info_entry = station_info_find_by_addr(addr_da, &active_network_info->members); if(station_info_entry != NULL){ station_info = (station_info_t*)(station_info_entry->data); queue_id = station_info->QID; } else { //Unlike the AP, this isn't necessarily a criteria for giving up on this LTG event. //In the IBSS, it's possible that there simply wasn't room in the heap for a station_info, //but we should still send it a packet. We'll use the multi-cast queue as a catch-all queue for these frames. queue_id = mcast_qid; } } 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; is_multicast = wlan_addr_mcast(addr_da); if(is_multicast){ queue_id = mcast_qid; } else { flags |= TX_80211_QUEUE_BUFFER_FLAGS_FILL_DURATION; station_info_entry = station_info_find_by_addr(addr_da, &active_network_info->members); if(station_info_entry != NULL){ station_info = (station_info_t*)(station_info_entry->data); queue_id = station_info->QID; } else { //Unlike the AP, this isn't necessarily a criteria for giving up on this LTG event. //In the IBSS, it's possible that there simply wasn't room in the heap for a station_info, //but we should still send it a packet. We'll use the multi-cast queue as a catch-all queue for these frames. queue_id = mcast_qid; } } 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->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; } if(is_multicast == 0){ station_info_entry = station_info_find_by_addr(addr_da, &active_network_info->members); if(station_info_entry == NULL){ // Add station info // - Set ht_capable argument to the HT_CAPABLE capability of the BSS. Given that the node does not know // the HT capabilities of the new station, it is reasonable to assume that they are the same as the BSS. // station_info = network_info_add_member(active_network_info, addr_da, max_queue_size); if(station_info != NULL){ if(active_network_info->bss_config.ht_capable){ station_info->capabilities |= STATION_INFO_CAPABILITIES_HT_CAPABLE; } else { station_info->capabilities &= ~STATION_INFO_CAPABILITIES_HT_CAPABLE; } station_info->flags |= STATION_INFO_FLAG_KEEP; time_hr_min_sec_t time_hr_min_sec = wlan_mac_time_to_hr_min_sec(get_system_time_usec()); xil_printf("*%dh:%02dm:%02ds* IBSS 0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x added to BSS\n", time_hr_min_sec.hr, time_hr_min_sec.min, time_hr_min_sec.sec, addr_da[0], addr_da[1], addr_da[2], addr_da[3], addr_da[4], addr_da[5]); } wlan_platform_high_userio_disp_status(USERIO_DISP_STATUS_MEMBER_LIST_UPDATE, active_network_info->members.length); } } 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, active_network_info->bss_config.bssid, 0, 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->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; tx_queue_buffer->flags = flags; // 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->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 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(active_network_info){ // Re-apply any Beacon Tx configurations wlan_mac_high_config_txrx_beacon(&gl_beacon_txrx_config); } } /*****************************************************************************/ /** * *****************************************************************************/ 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; 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. 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 ((bss_config->bssid[0] & MAC_ADDR_MSB_MASK_LOCAL ) == 0) { // In the IBSS implementation, the BSSID provided must be locally generated 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; } } if (update_mask & BSS_FIELD_MASK_DTIM_PERIOD) { //IBSS DTIM is not supported at this time. Any attempt to update this field //will return an error return_status |= BSS_CONFIG_FAILURE_DTIM_PERIOD_INVALID; } } 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("Leaving 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 association network_info_remove_member(active_network_info, curr_station_info->addr); time_hr_min_sec_t time_hr_min_sec = wlan_mac_time_to_hr_min_sec(get_system_time_usec()); xil_printf("*%dh:%02dm:%02ds* IBSS 0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x removed from BSS\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]); 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); } // Inform the MAC High Framework to no longer will keep this BSS Info. This will // allow it to be overwritten in the future to make space for new BSS Infos. active_network_info->flags &= ~NETWORK_FLAGS_KEEP; // Set "active_bss_info" to NULL // - All functions must be able to handle active_bss_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 // 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_bss_info is guaranteed to be NULL at this point in the code // bss_config 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 bss_info 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_IBSS); 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_bss_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; update_beacon_template = 1; send_beacon_config_to_low = 1; } if (update_mask & BSS_FIELD_MASK_HT_CAPABLE) { // In an IBSS network, the node does not know the HT capabilities of any of // the peer nodes in the BSS. Therefore, when station infos are added to // track peer nodes, the node assumes that the peer node's HT capabilities // matches that of the BSS. Given that changing the BSS capabilities does // not invalidate that assumption, the IBSS node does not update any station // info HT capabilities when the BSS capabilities change. Therefore, // changing the BSS HT_CAPABLE capabilities only affects what is advertised in // the IBSS beacons. Also, it should not change any of the default TX params // since the IBSS node 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. if (update_beacon_template) { default_beacon_tx_params = wlan_mac_get_default_tx_params(mcast_mgmt); 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 ){} } // 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) { 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 = IBSS_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; wlan_mac_high_config_txrx_beacon(&gl_beacon_txrx_config); } // Print new IBSS information xil_printf("IBSS 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 beacon MAC time update mode * * @param u32 enable - Enable / Disable MAC time update from beacons * @return None * *****************************************************************************/ void ibss_set_beacon_ts_update_mode(u32 enable){ if (enable) { gl_beacon_txrx_config.ts_update_mode = FUTURE_ONLY_UPDATE; } else { gl_beacon_txrx_config.ts_update_mode = NEVER_UPDATE; } // Push beacon configuration to CPU_LOW wlan_mac_high_config_txrx_beacon(&gl_beacon_txrx_config); } /*****************************************************************************/ /** * @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 IBSS user command: 0x%x\n", cmd_id); } break; } return resp_sent; } #endif