/** @file wlan_mac_station_info.c * @brief Station Information Metadata Subsystem * * This contains code tracking metadata about stations. * * @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" #include "xil_types.h" #include "stdlib.h" #include "stdio.h" #include "wlan_platform_high.h" #include "string.h" #include "wlan_mac_pkt_buf_util.h" #include "wlan_mac_high.h" #include "wlan_mac_station_info.h" #include "wlan_mac_dl_list.h" #include "wlan_mac_802_11_defs.h" #include "wlan_mac_schedule.h" #include "wlan_mac_addr_filter.h" #include "wlan_mac_network_info.h" #include "wlan_platform_common.h" #include "wlan_mac_common.h" #include "wlan_common_types.h" #include "wlan_mac_eth_util.h" /*********************** Global Variable Definitions *************************/ extern platform_high_dev_info_t platform_high_dev_info; /*************************** Variable Definitions ****************************/ static dl_list station_info_free; ///< Free station_info_t /// The counts_txrx_list is stored chronologically from .first being oldest /// and .last being newest. The "find" function search from last to first /// to minimize search time for new BSSes you hear from often. static dl_list station_info_list; ///< Filled station_info_t // Default Transmission Parameters typedef struct default_tx_params_t{ tx_params_t unicast_mgmt; tx_params_t unicast_data; tx_params_t multicast_mgmt; tx_params_t multicast_data; } default_tx_params_t; static default_tx_params_t default_tx_params; /*************************** Functions Prototypes ****************************/ station_info_entry_t* station_info_find_oldest(); /******************************** Functions **********************************/ void station_info_init() { u32 i; u32 num_station_info; station_info_entry_t* station_info_entry_base; // Set sane default Tx params. These will be overwritten by the user application tx_params_t tx_params = { .phy = { .mcs = 0, .phy_mode = PHY_MODE_NONHT, .antenna_mode = TX_ANTMODE_SISO_ANTA, .power = 15 }, .mac = { .flags = 0 } }; wlan_mac_set_default_tx_params(unicast_data, &tx_params); 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); dl_list_init(&station_info_free); dl_list_init(&station_info_list); // Clear the memory in the dram used for bss_infos bzero((void*)STATION_INFO_BUFFER_BASE, STATION_INFO_BUFFER_SIZE); // The number of elements we can initialize is limited by the smaller of two values: // (1) The number of dl_entry structs we can squeeze into STATION_INFO_DL_ENTRY_MEM_SIZE // (2) The number of station_info_t structs we can squeeze into STATION_INFO_BUFFER_SIZE num_station_info = WLAN_MIN(STATION_INFO_DL_ENTRY_MEM_SIZE/sizeof(station_info_entry_t), STATION_INFO_BUFFER_SIZE/sizeof(station_info_t)); // At boot, every dl_entry buffer descriptor is free // To set up the doubly linked list, we exploit the fact that we know the starting state is sequential. // This matrix addressing is not safe once the queue is used. The insert/remove helper functions should be used station_info_entry_base = (station_info_entry_t*)(STATION_INFO_DL_ENTRY_MEM_BASE); for (i = 0; i < num_station_info; i++) { station_info_entry_base[i].data = (station_info_t*)(STATION_INFO_BUFFER_BASE + (i*sizeof(station_info_t))); dl_entry_insertEnd(&station_info_free, (dl_entry*)&(station_info_entry_base[i])); } xil_printf("Station Info list (len %d) placed in DRAM: using %d kB\n", num_station_info, (num_station_info*sizeof(station_info_t))/1024); return; } void station_info_init_finish(){ //Will be called after interrupts have been started. Safe to use scheduler now. wlan_mac_schedule_add_event(SCHEDULE_ID_COARSE, 10000000, SCHEDULE_REPEAT_FOREVER, (void*)station_info_timestamp_check); } inline station_info_t* station_info_txreport_process(void* pkt_buf_addr, wlan_mac_low_tx_details_t* wlan_mac_low_tx_details) { tx_frame_info_t* tx_frame_info = (tx_frame_info_t*)pkt_buf_addr; mac_header_80211* tx_80211_header = (mac_header_80211*)((u8*)tx_frame_info + PHY_TX_PKT_BUF_MPDU_OFFSET); station_info_t* curr_station_info; u64 curr_system_time = get_system_time_usec(); #if WLAN_SW_CONFIG_ENABLE_TXRX_COUNTS station_txrx_counts_t* curr_txrx_counts; txrx_counts_sub_t* txrx_counts_sub; u8 pkt_type; pkt_type = (tx_80211_header->frame_control_1 & MAC_FRAME_CTRL1_MASK_TYPE); #endif curr_station_info = station_info_create(tx_80211_header->address_1); if(curr_station_info == NULL) return NULL; // By this point in the function, curr_station_info is guaranteed to be pointing to a valid station_info_t struct // that we should update with this reception. // Update the latest TXRX time curr_station_info->latest_txrx_timestamp = curr_system_time; if(wlan_mac_low_tx_details->flags & TX_DETAILS_FLAGS_RECEIVED_RESPONSE){ curr_station_info->latest_rx_timestamp = curr_system_time; } #if WLAN_SW_CONFIG_ENABLE_TXRX_COUNTS curr_txrx_counts = &(curr_station_info->txrx_counts); switch(pkt_type){ default: //Unknown type return curr_station_info; break; case MAC_FRAME_CTRL1_TYPE_DATA: txrx_counts_sub = &(curr_txrx_counts->data); break; case MAC_FRAME_CTRL1_TYPE_MGMT: txrx_counts_sub = &(curr_txrx_counts->mgmt); break; } (txrx_counts_sub->tx_num_attempts)++; #endif return curr_station_info; } inline station_info_t* station_info_posttx_process(void* pkt_buf_addr) { tx_frame_info_t* tx_frame_info = (tx_frame_info_t*)pkt_buf_addr; mac_header_80211* tx_80211_header = (mac_header_80211*)((u8*)tx_frame_info + PHY_TX_PKT_BUF_MPDU_OFFSET); station_info_t* curr_station_info; #if WLAN_SW_CONFIG_ENABLE_TXRX_COUNTS station_txrx_counts_t* curr_txrx_counts; txrx_counts_sub_t* txrx_counts_sub; u8 pkt_type; pkt_type = (tx_80211_header->frame_control_1 & MAC_FRAME_CTRL1_MASK_TYPE); #endif curr_station_info = station_info_create(tx_80211_header->address_1); if(curr_station_info == NULL) return NULL; #if WLAN_SW_CONFIG_ENABLE_TXRX_COUNTS curr_txrx_counts = &(curr_station_info->txrx_counts); switch(pkt_type){ default: //Unknown type return curr_station_info; break; case MAC_FRAME_CTRL1_TYPE_DATA: txrx_counts_sub = &(curr_txrx_counts->data); break; case MAC_FRAME_CTRL1_TYPE_MGMT: txrx_counts_sub = &(curr_txrx_counts->mgmt); break; } (txrx_counts_sub->tx_num_packets_total)++; (txrx_counts_sub->tx_num_bytes_total) += (tx_frame_info->length); if((tx_frame_info->tx_result) == TX_FRAME_INFO_RESULT_SUCCESS){ (txrx_counts_sub->tx_num_packets_success)++; (txrx_counts_sub->tx_num_bytes_success) += tx_frame_info->length; } #endif return curr_station_info; } inline station_info_t* station_info_postrx_process(void* pkt_buf_addr) { 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; mac_header_80211* rx_80211_header = (mac_header_80211*)((void *)mac_payload); station_info_t* curr_station_info = NULL; u8 pkt_type; u64 curr_system_time = get_system_time_usec(); pkt_type = (rx_80211_header->frame_control_1 & MAC_FRAME_CTRL1_MASK_TYPE); if ( (rx_frame_info->flags & RX_FRAME_INFO_FLAGS_FCS_GOOD) && (pkt_type != MAC_FRAME_CTRL1_TYPE_CTRL)) { // We will only consider good FCS receptions in our counts. We cannot // trust the address bytes themselves if the FCS is in error. Furthermore, // control frames will not be considered for counts either since the CTS // and ACK frames have no addr2 field. curr_station_info = station_info_create(rx_80211_header->address_2); if(curr_station_info == NULL) return NULL; // If this reception is HTMF, we have a pretty good indication that this device // is capable of also receiving HTMF waveforms. We'll update its flags accordingly if(rx_frame_info->phy_details.phy_mode == PHY_MODE_HTMF) curr_station_info->capabilities |= STATION_INFO_CAPABILITIES_HT_CAPABLE; // By this point in the function, curr_station_info is guaranteed to be pointing to a valid station_info_t struct // that we should update with this reception. // Update the latest TXRX time curr_station_info->latest_txrx_timestamp = curr_system_time; // Update the latest RX time curr_station_info->latest_rx_timestamp = curr_system_time; } return curr_station_info; } void station_info_rx_process_hostname( void* pkt_buf_addr, station_info_t* station_info ){ // Some DHCP frames contain a hostname that makes it easier to recognize a station_info_t // as a particular device on the network. This is purely informational and has no effect // on standard 802.11 operations. // To minimize confusion, we should avoid inserting a hostname into a station_info_t // for devices that have multiple hosts upstream of them (e.g. an AP). We will only extract // the hostname from DHCP packets where the transmitter address (addr2) of the 802.11 header // matches the hardware MAC address inside the DHCP payload itself. This will not be the case // for AP transmissions. mac_header_80211* rx80211_hdr = (mac_header_80211*)((u8*)pkt_buf_addr + PHY_RX_PKT_BUF_MPDU_OFFSET); llc_header_t* llc_hdr; u16 pre_llc_offset; ipv4_header_t* ip_hdr; dhcp_packet* dhcp; udp_header_t* udp; u8* eth_mid_ptr; u8 is_dhcp_req; u8 continue_loop; switch(rx80211_hdr->frame_control_1){ case MAC_FRAME_CTRL1_SUBTYPE_QOSDATA: pre_llc_offset = sizeof(qos_control); break; case MAC_FRAME_CTRL1_SUBTYPE_DATA: pre_llc_offset = 0; break; default: // Unrecognized type return; break; } llc_hdr = (llc_header_t*)((u8*)rx80211_hdr + sizeof(mac_header_80211) + pre_llc_offset); if(llc_hdr->type == ETH_TYPE_IP){ ip_hdr = (ipv4_header_t*)((u8*)rx80211_hdr + sizeof(mac_header_80211) + sizeof(llc_header_t) + pre_llc_offset ); if (ip_hdr->protocol == IPV4_PROT_UDP) { udp = (udp_header_t*)((void*)ip_hdr + 4*((u8)(ip_hdr->version_ihl) & 0xF)); // All Bootstrap Protocol packets contain the client MAC address as part of the // payload. For STA de-encapsulation, we need to replace the wireless MAC address // of the STA with the wired MAC address of the client if ((Xil_Ntohs(udp->src_port) == UDP_SRC_PORT_BOOTPC) || (Xil_Ntohs(udp->src_port) == UDP_SRC_PORT_BOOTPS)) { // Disable the checksum since this will change the bytes in the packet udp->checksum = 0; dhcp = (dhcp_packet*)((u8*)udp + sizeof(udp_header_t)); if (Xil_Ntohl(dhcp->magic_cookie) == DHCP_MAGIC_COOKIE) { eth_mid_ptr = (u8*)((void*)dhcp + sizeof(dhcp_packet)); // Iterate over all tagged parameters in the DHCP request, looking for the hostname parameter // NOTE: Stop after 20 tagged parameters (handles case of mal-formed DHCP packets missing END tag) continue_loop = 20; while(continue_loop) { continue_loop--; switch(eth_mid_ptr[0]) { case DHCP_OPTION_TAG_TYPE: if((eth_mid_ptr[2] == DHCP_OPTION_TYPE_DISCOVER) || (eth_mid_ptr[2] == DHCP_OPTION_TYPE_REQUEST)) { is_dhcp_req = 1; } break; case DHCP_HOST_NAME: if (is_dhcp_req && wlan_addr_eq(dhcp->chaddr, rx80211_hdr->address_2) ) { if(station_info != NULL) { // rx_frame_info has pointer to STA entry in association table - fill in that entry's hostname field // Zero out the hostname field of the station_info // NOTE: This will effectively Null-terminate the string bzero(station_info->hostname, STATION_INFO_HOSTNAME_MAXLEN+1); // Copy the string from the DHCP payload into the hostname field memcpy(station_info->hostname, &(eth_mid_ptr[2]), WLAN_MIN(STATION_INFO_HOSTNAME_MAXLEN, eth_mid_ptr[1])); } } break; case DHCP_OPTION_END: continue_loop = 0; break; } // END switch(DHCP tag type) // Increment by size of current tagged parameter eth_mid_ptr += (2+eth_mid_ptr[1]); } // END iterate over DHCP tags } // END is DHCP valid } // END is DHCP } // END is UDP } } #if WLAN_SW_CONFIG_ENABLE_TXRX_COUNTS void station_info_rx_process_counts(void* pkt_buf_addr, station_info_t* station_info, u32 option_flags) { 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; mac_header_80211* rx_80211_header = (mac_header_80211*)((void *)mac_payload); station_txrx_counts_t* curr_txrx_counts; txrx_counts_sub_t* txrx_counts_sub; u8 pkt_type; u16 length = rx_frame_info->phy_details.length; pkt_type = (rx_80211_header->frame_control_1 & MAC_FRAME_CTRL1_MASK_TYPE); if ( (station_info != NULL) && (rx_frame_info->flags & RX_FRAME_INFO_FLAGS_FCS_GOOD) && (pkt_type != MAC_FRAME_CTRL1_TYPE_CTRL)) { // We will only consider good FCS receptions in our counts. We cannot // trust the address bytes themselves if the FCS is in error. Furthermore, // control frames will not be considered for counts either since the CTS // and ACK frames have no addr2 field. curr_txrx_counts = &(station_info->txrx_counts); switch(pkt_type){ default: //Unknown type return; break; case MAC_FRAME_CTRL1_TYPE_DATA: txrx_counts_sub = &(curr_txrx_counts->data); break; case MAC_FRAME_CTRL1_TYPE_MGMT: txrx_counts_sub = &(curr_txrx_counts->mgmt); break; } (txrx_counts_sub->rx_num_packets_total)++; (txrx_counts_sub->rx_num_bytes_total) += (length - WLAN_PHY_FCS_NBYTES - sizeof(mac_header_80211)); if( (option_flags & RX_PROCESS_COUNTS_OPTION_FLAG_IS_DUPLICATE) == 0){ //Unique reception (txrx_counts_sub->rx_num_packets)++; (txrx_counts_sub->rx_num_bytes) += (length - WLAN_PHY_FCS_NBYTES - sizeof(mac_header_80211)); } } } #endif void station_info_print(dl_list* list, u32 option_flags){ // list == NULL : Print all in flat station_info_list // list !- NULL : Print only in provided list int iter; u32 i; dl_list* local_list; dl_entry* curr_dl_entry; station_info_t* curr_station_info; #if WLAN_SW_CONFIG_ENABLE_TXRX_COUNTS station_txrx_counts_t* curr_txrx_counts; #endif if(list != NULL){ local_list = list; } else { local_list = &station_info_list; } i = 0; iter = local_list->length; curr_dl_entry = local_list->first; // Print the header xil_printf("************************ Station Information *************************\n"); while ((curr_dl_entry != NULL) && (iter-- > 0)) { curr_station_info = (station_info_t*)(curr_dl_entry->data); xil_printf("%d: [%d] %02x-%02x-%02x-%02x-%02x-%02x ", i, curr_station_info->ID, 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]); if((curr_station_info->flags & STATION_INFO_FLAG_KEEP)){ xil_printf("(KEEP)\n"); } else { xil_printf("\n"); } xil_printf(" Num Linked Queue Buffer: %d\n", curr_station_info->num_linked_queue_buffer); xil_printf(" Data Tx MCS: %d\n", curr_station_info->tx_params_data.phy.mcs); xil_printf(" Data Tx PHY mode: %d\n", curr_station_info->tx_params_data.phy.phy_mode); xil_printf(" Data Tx power: %d\n", curr_station_info->tx_params_data.phy.power); xil_printf(" Data Tx antenna_mode: 0x%x\n", curr_station_info->tx_params_data.phy.antenna_mode); xil_printf(" Management Tx MCS: %d\n", curr_station_info->tx_params_mgmt.phy.mcs); xil_printf(" Management Tx PHY mode: %d\n", curr_station_info->tx_params_mgmt.phy.phy_mode); xil_printf(" Management Tx power: %d\n", curr_station_info->tx_params_mgmt.phy.power); xil_printf(" Management Tx antenna_mode: 0x%x\n", curr_station_info->tx_params_mgmt.phy.antenna_mode); #if WLAN_SW_CONFIG_ENABLE_TXRX_COUNTS if(option_flags & STATION_INFO_PRINT_OPTION_FLAG_INCLUDE_COUNTS){ curr_txrx_counts = &(curr_station_info->txrx_counts); xil_printf(" Data Rx Num Bytes: %d\n", (u32)(curr_txrx_counts->data.rx_num_bytes)); xil_printf(" Data Rx Num Bytes Total: %d\n", (u32)(curr_txrx_counts->data.rx_num_bytes_total)); xil_printf(" Data Tx Num Bytes Success: %d\n", (u32)(curr_txrx_counts->data.tx_num_bytes_success)); xil_printf(" Data Tx Num Bytes Total: %d\n", (u32)(curr_txrx_counts->data.tx_num_bytes_total)); xil_printf(" Data Rx Num Packets: %d\n", (u32)(curr_txrx_counts->data.rx_num_packets)); xil_printf(" Data Rx Num Packets Total: %d\n", (u32)(curr_txrx_counts->data.rx_num_packets_total)); xil_printf(" Data Tx Num Packets Success: %d\n", (u32)(curr_txrx_counts->data.tx_num_packets_success)); xil_printf(" Data Tx Num Packets Total: %d\n", (u32)(curr_txrx_counts->data.tx_num_packets_total)); xil_printf(" Data Tx Num Attempts: %d\n", (u32)(curr_txrx_counts->data.tx_num_attempts)); xil_printf(" Mgmt. Rx Num Bytes: %d\n", (u32)(curr_txrx_counts->mgmt.rx_num_bytes)); xil_printf(" Mgmt. Rx Num Bytes Total: %d\n", (u32)(curr_txrx_counts->mgmt.rx_num_bytes_total)); xil_printf(" Mgmt. Tx Num Bytes Success: %d\n", (u32)(curr_txrx_counts->mgmt.tx_num_bytes_success)); xil_printf(" Mgmt. Tx Num Bytes Total: %d\n", (u32)(curr_txrx_counts->mgmt.tx_num_bytes_total)); xil_printf(" Mgmt. Rx Num Packets: %d\n", (u32)(curr_txrx_counts->mgmt.rx_num_packets)); xil_printf(" Mgmt. Rx Num Packets Total: %d\n", (u32)(curr_txrx_counts->mgmt.rx_num_packets_total)); xil_printf(" Mgmt. Tx Num Packets Success: %d\n", (u32)(curr_txrx_counts->mgmt.tx_num_packets_success)); xil_printf(" Mgmt. Tx Num Packets Total: %d\n", (u32)(curr_txrx_counts->mgmt.tx_num_packets_total)); xil_printf(" Mgmt. Tx Num Attempts: %d\n", (u32)(curr_txrx_counts->mgmt.tx_num_attempts)); } #endif xil_printf(" Last update: %d msec ago\n", (u32)((get_system_time_usec() - curr_station_info->latest_txrx_timestamp)/1000)); curr_dl_entry = dl_entry_next(curr_dl_entry); i++; } } void station_info_reset_all_counts_txrx(){ //This function will return all counts to 0. #if WLAN_SW_CONFIG_ENABLE_TXRX_COUNTS int iter; u32 i; dl_entry* curr_dl_entry; station_info_t* curr_station_info; station_txrx_counts_t* curr_txrx_counts; i = 0; iter = station_info_list.length; curr_dl_entry = station_info_list.last; while ((curr_dl_entry != NULL) && (iter-- > 0)) { curr_station_info = (station_info_t*)(curr_dl_entry->data); curr_txrx_counts = &(curr_station_info->txrx_counts); bzero(&(curr_txrx_counts->data), sizeof(txrx_counts_sub_t)); bzero(&(curr_txrx_counts->mgmt), sizeof(txrx_counts_sub_t)); curr_dl_entry = dl_entry_prev(curr_dl_entry); i++; } #endif } void station_info_timestamp_check() { dl_entry* curr_dl_entry; station_info_t* curr_station_info; curr_dl_entry = station_info_list.first; while(curr_dl_entry != NULL){ curr_station_info = (station_info_t*)(curr_dl_entry->data); if((get_system_time_usec() - curr_station_info->latest_txrx_timestamp) > STATION_INFO_TIMEOUT_USEC){ if( ((curr_station_info->flags & STATION_INFO_FLAG_KEEP) == 0) && (curr_station_info->num_linked_queue_buffer <= 0) ){ station_info_clear(curr_station_info); dl_entry_remove(&station_info_list, curr_dl_entry); station_info_checkin(curr_dl_entry); } } else { // Nothing after this entry is older, so it's safe to quit return; } curr_dl_entry = dl_entry_next(curr_dl_entry); } } station_info_entry_t* station_info_checkout(){ station_info_entry_t* entry; if(station_info_free.length > 0){ entry = ((station_info_entry_t*)(station_info_free.first)); dl_entry_remove(&station_info_free,station_info_free.first); return entry; } else { return NULL; } } void station_info_checkin(dl_entry* entry){ dl_entry_insertEnd(&station_info_free, (dl_entry*)entry); return; } station_info_entry_t* station_info_find_by_id(u32 id, dl_list* list){ int iter; station_info_entry_t* curr_station_info_entry; station_info_t* curr_station_info; dl_list* list_to_search; if(list == NULL){ // Error. "List" must be provided. ID has no meaning for the // flat "station_info_list" global. xil_printf("Error: station_info_find_by_id must be provided with a list argument\n"); return NULL; } else { list_to_search = list; } iter = list_to_search->length; curr_station_info_entry = (station_info_entry_t*)(list_to_search->last); while ((curr_station_info_entry != NULL) && (iter-- > 0)) { curr_station_info = curr_station_info_entry->data; if (curr_station_info->ID == (int)id) { return curr_station_info_entry; } curr_station_info_entry = dl_entry_prev(curr_station_info_entry); } return NULL; } station_info_entry_t* station_info_find_by_addr(u8* addr, dl_list* list){ int iter; station_info_entry_t* curr_station_info_entry; dl_list* list_to_search; if(list == NULL){ //Optional "list" argument not provided. Search will occur over // flat "station_info_list" global. list_to_search = &station_info_list; } else { list_to_search = list; } iter = list_to_search->length; curr_station_info_entry = (station_info_entry_t*)(list_to_search->last); while ((curr_station_info_entry != NULL) && (iter-- > 0)) { if (wlan_addr_eq(addr, curr_station_info_entry->addr)) { return curr_station_info_entry; } curr_station_info_entry = dl_entry_prev(curr_station_info_entry); } return NULL; } station_info_entry_t* station_info_find_oldest(){ int iter; station_info_entry_t* curr_station_info_entry; station_info_t* station_info; iter = station_info_list.length; curr_station_info_entry = (station_info_entry_t*)(station_info_list.first); while ((curr_station_info_entry != NULL) && (iter-- > 0)) { station_info = curr_station_info_entry->data; if (((station_info->flags & STATION_INFO_FLAG_KEEP) == 0) && (station_info->num_linked_queue_buffer <= 0)) { return curr_station_info_entry; } curr_station_info_entry = dl_entry_next(curr_station_info_entry); } return NULL; } // Function will create a station_info_t and make sure that the address is unique // in the flat station_info_list. // station_info_t* station_info_create(u8* addr){ station_info_entry_t* curr_station_info_entry; station_info_t* curr_station_info = NULL; curr_station_info_entry = station_info_find_by_addr(addr, NULL); if (curr_station_info_entry != NULL){ // Get the Tx/Rx Counts from the entry curr_station_info = curr_station_info_entry->data; // Remove the entry from the info list so it can be added back later dl_entry_remove(&station_info_list, (dl_entry*)curr_station_info_entry); } else { // Have not seen this addr before; attempt to grab a new dl_entry // struct from the free pool curr_station_info_entry = station_info_checkout(); if (curr_station_info_entry == NULL){ // No free dl_entry; Re-allocate the oldest entry in the filled list curr_station_info_entry = station_info_find_oldest(); if (curr_station_info_entry != NULL) { dl_entry_remove(&station_info_list, (dl_entry*)curr_station_info_entry); } else { xil_printf("Cannot create station_info.\n"); return NULL; } } // Get the Station Info from the entry curr_station_info = curr_station_info_entry->data; // Clear any old information station_info_clear(curr_station_info); // Copy the addr to the station_info_t memcpy(curr_station_info->addr, addr, MAC_ADDR_LEN); // Copy the addr to the station_info_entry_t memcpy(curr_station_info_entry->addr, addr, MAC_ADDR_LEN); // Set default tx_params_t for management and data frames if (wlan_addr_mcast(addr)){ curr_station_info->tx_params_data = default_tx_params.multicast_data; curr_station_info->tx_params_mgmt = default_tx_params.multicast_mgmt; } else { curr_station_info->tx_params_data = default_tx_params.unicast_data; curr_station_info->tx_params_mgmt = default_tx_params.unicast_mgmt; } } // Update the timestamp // TODO: This behavior is debatable. The idea here is that, since this // entry was just created, whoever called this function probably does // not want the framework to automatically purge it the next time // station_info_timestamp_check() is called. This shouldn't happen // if care is taken to either: // (1) Update the timestamp after this function is called and before // an interrupt context is left. // (2) Set the STATION_INFO_FLAG_KEEP bit to 1 after this function // is called and before an interrupt context is left. // This update is just for extra safety and makes it more difficult // to accidentally have the framework delete this struct. curr_station_info->latest_txrx_timestamp = get_system_time_usec(); // Insert the updated entry into the network list dl_entry_insertEnd(&station_info_list, (dl_entry*)curr_station_info_entry); return curr_station_info; } /** * @brief Reset List of Stations * * Reset all Station Infos except ones flagged to be kept * * @param None * @return None */ void station_info_reset_all(){ dl_entry* next_dl_entry = station_info_list.first; dl_entry* curr_dl_entry; station_info_t* curr_station_info; int iter = station_info_list.length; while( (next_dl_entry != NULL) && (iter-- > 0) ){ curr_dl_entry = next_dl_entry; next_dl_entry = dl_entry_next(curr_dl_entry); curr_station_info = (station_info_t*)(curr_dl_entry->data); if( ((curr_station_info->flags & STATION_INFO_FLAG_KEEP) == 0) && (curr_station_info->num_linked_queue_buffer <= 0)){ station_info_clear(curr_station_info); dl_entry_remove(&station_info_list, curr_dl_entry); station_info_checkin(curr_dl_entry); } } } #if WLAN_SW_CONFIG_ENABLE_TXRX_COUNTS /** * @brief Zero all Counts * * Zero all counts * * @param None * @return None */ void txrx_counts_zero_all(){ dl_entry* next_dl_entry = station_info_list.first; dl_entry* curr_dl_entry; station_info_t* curr_station_info; station_txrx_counts_t* curr_txrx_counts; int iter = station_info_list.length; while( (next_dl_entry != NULL) && (iter-- > 0) ){ curr_dl_entry = next_dl_entry; next_dl_entry = dl_entry_next(curr_dl_entry); curr_station_info = (station_info_t*)(curr_dl_entry->data); curr_txrx_counts = &(curr_station_info->txrx_counts); station_info_clear_txrx_counts(curr_txrx_counts); } } void station_info_clear_txrx_counts(station_txrx_counts_t* txrx_counts){ bzero(txrx_counts, sizeof(station_txrx_counts_t)); } #endif void station_info_clear(station_info_t* station_info){ if (station_info != NULL){ bzero(station_info, sizeof(station_info_t)); //Certain parameters treat zero as a valid value. These // parameters are manually updated here to indicate this // station_info_t is new. station_info->latest_rx_seq = 0xFFFF; station_info->QID = -1; station_info->ID = -1; station_info->latest_rx_timestamp = (u64)-1; station_info->latest_txrx_timestamp = (u64)-1; } } inline dl_list* station_info_get_list(){ return &station_info_list; } station_info_t* station_info_add_to_list(dl_list* app_station_info_list, u8* addr){ station_info_entry_t* entry = NULL; station_info_t* station_info = NULL; // Search for the station_info inside the provided list entry = station_info_find_by_addr(addr, app_station_info_list); if(entry != NULL){ station_info = (station_info_t*)(entry->data); // This addr is already tied to an list entry. We'll just pass this call // the pointer to that entry back to the calling function without creating a new entry } else { // This addr is new, so we'll have to add an entry into the list entry = wlan_mac_high_malloc(sizeof(station_info_entry_t)); if(entry == NULL){ return NULL; } memcpy(entry->addr, addr, MAC_ADDR_LEN); station_info = station_info_create(addr); if(station_info == NULL){ wlan_mac_high_free(entry); return NULL; } bzero(&(station_info->rate_info),sizeof(rate_selection_info_t)); station_info->rate_info.rate_selection_scheme = RATE_SELECTION_SCHEME_STATIC; // Populate the entry entry->data = (void*)station_info; // Add it to the list dl_entry_insertEnd(app_station_info_list, (dl_entry*)entry); } return station_info; } int station_info_remove_from_list(dl_list* app_station_info_list, u8* addr){ station_info_entry_t* entry; entry = station_info_find_by_addr(addr, app_station_info_list); if(entry == NULL){ // This addr doesn't refer to any station currently in the list, // so there is nothing to remove. We'll return an error to let the calling // function know that something is wrong. return WLAN_FAILURE; } else { // Remove station from the list; dl_entry_remove(app_station_info_list, (dl_entry*)entry); wlan_mac_high_free(entry); return WLAN_SUCCESS; } } /** * @brief Is the provided station a valid member of the provided list * * Function will check that the provided station is part of the dl_list provided as the first argument * * @param dl_list* station_info_list * - Pointer to list of Station Info structs * @param station_info * station * - Station info pointer to check * @return u8 * - 0 - Station is not a member of the provided list * - 1 - Station is a member of the provided listp_ * */ u8 station_info_is_member(dl_list* app_station_info_list, station_info_t* station_info){ dl_entry* curr_station_info_entry; station_info_t* station_info_temp; int iter = app_station_info_list->length; curr_station_info_entry = app_station_info_list->first; while( (curr_station_info_entry != NULL) && (iter-- > 0) ){ station_info_temp = (station_info_t*)(curr_station_info_entry->data); if(station_info == station_info_temp){ return 1; } curr_station_info_entry = dl_entry_next(curr_station_info_entry); } return 0; } tx_params_t wlan_mac_get_default_tx_params(default_tx_param_sel_t default_tx_param_sel){ tx_params_t ret; switch(default_tx_param_sel){ case unicast_mgmt: ret = default_tx_params.unicast_mgmt; break; case unicast_data: ret = default_tx_params.unicast_data; break; case mcast_mgmt: ret = default_tx_params.multicast_mgmt; break; case mcast_data: ret = default_tx_params.multicast_data; break; } return ret; } void wlan_mac_set_default_tx_params(default_tx_param_sel_t default_tx_param_sel, tx_params_t* tx_params){ switch(default_tx_param_sel){ case unicast_mgmt: default_tx_params.unicast_mgmt = *tx_params; break; case unicast_data: default_tx_params.unicast_data = *tx_params; break; case mcast_mgmt: default_tx_params.multicast_mgmt = *tx_params; break; case mcast_data: default_tx_params.multicast_data = *tx_params; break; } } void wlan_mac_reapply_default_tx_params(){ station_info_entry_t* station_info_entry; station_info_t* station_info; int iter; station_info_entry = (station_info_entry_t*)(station_info_list.first); iter = station_info_list.length; while( (station_info_entry != NULL) && (iter-- > 0) ){ station_info = station_info_entry->data; if (wlan_addr_mcast(station_info_entry->addr)){ station_info->tx_params_data = default_tx_params.multicast_data; station_info->tx_params_mgmt = default_tx_params.multicast_mgmt; } else { station_info->tx_params_data = default_tx_params.unicast_data; station_info->tx_params_mgmt = default_tx_params.unicast_mgmt; } station_info_entry = (station_info_entry_t*)dl_entry_next((dl_entry*)station_info_entry); } // Update the beacon template if it's being used. For projects like STA, this function will not result in any behavior changes wlan_mac_high_update_beacon_tx_params(&(default_tx_params.multicast_mgmt)); }