//------------------------------------------------------------------------------ // // Copyright (c) 2004-2010 Atheros Corporation. All rights reserved. // // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice appear in all copies. // // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // // //------------------------------------------------------------------------------ //============================================================================== // IEEE 802.11 node handling support. // // Author(s): ="Atheros" //============================================================================== #include #include #include #include #define ATH_MODULE_NAME wlan #include #include "htc.h" #include "htc_api.h" #include #include #include #include #include #define ATH_DEBUG_WLAN ATH_DEBUG_MAKE_MODULE_MASK(0) #ifdef ATH_DEBUG_MODULE static ATH_DEBUG_MASK_DESCRIPTION wlan_debug_desc[] = { { ATH_DEBUG_WLAN , "General WLAN Node Tracing"}, }; ATH_DEBUG_INSTANTIATE_MODULE_VAR(wlan, "wlan", "WLAN Node Management", ATH_DEBUG_MASK_DEFAULTS, ATH_DEBUG_DESCRIPTION_COUNT(wlan_debug_desc), wlan_debug_desc); #endif #ifdef THREAD_X static void wlan_node_timeout(A_ATH_TIMER arg); #endif static bss_t * _ieee80211_find_node (struct ieee80211_node_table *nt, const A_UINT8 *macaddr); bss_t * wlan_node_alloc(struct ieee80211_node_table *nt, int wh_size) { bss_t *ni; ni = A_MALLOC_NOWAIT(sizeof(bss_t)); if (ni != NULL) { if (wh_size) { ni->ni_buf = A_MALLOC_NOWAIT(wh_size); if (ni->ni_buf == NULL) { A_FREE(ni); ni = NULL; return ni; } } } else { return ni; } /* Make sure our lists are clean */ ni->ni_list_next = NULL; ni->ni_list_prev = NULL; ni->ni_hash_next = NULL; ni->ni_hash_prev = NULL; // // ni_scangen never initialized before and during suspend/resume of winmobile, // that some junk has been stored in this, due to this scan list didn't properly updated // ni->ni_scangen = 0; #ifdef OS_ROAM_MANAGEMENT ni->ni_si_gen = 0; #endif return ni; } void wlan_node_free(bss_t *ni) { if (ni->ni_buf != NULL) { A_FREE(ni->ni_buf); } A_FREE(ni); } void wlan_setup_node(struct ieee80211_node_table *nt, bss_t *ni, const A_UINT8 *macaddr) { int hash; A_UINT32 timeoutValue = 0; A_MEMCPY(ni->ni_macaddr, macaddr, IEEE80211_ADDR_LEN); hash = IEEE80211_NODE_HASH (macaddr); ieee80211_node_initref (ni); /* mark referenced */ timeoutValue = nt->nt_nodeAge; ni->ni_tstamp = A_GET_MS (timeoutValue); ni->ni_actcnt = WLAN_NODE_INACT_CNT; IEEE80211_NODE_LOCK_BH(nt); /* Insert at the end of the node list */ ni->ni_list_next = NULL; ni->ni_list_prev = nt->nt_node_last; if(nt->nt_node_last != NULL) { nt->nt_node_last->ni_list_next = ni; } nt->nt_node_last = ni; if(nt->nt_node_first == NULL) { nt->nt_node_first = ni; } /* Insert into the hash list i.e. the bucket */ if((ni->ni_hash_next = nt->nt_hash[hash]) != NULL) { nt->nt_hash[hash]->ni_hash_prev = ni; } ni->ni_hash_prev = NULL; nt->nt_hash[hash] = ni; #ifdef THREAD_X if (!nt->isTimerArmed) { A_TIMEOUT_MS(&nt->nt_inact_timer, timeoutValue, 0); nt->isTimerArmed = TRUE; } #endif IEEE80211_NODE_UNLOCK_BH(nt); } static bss_t * _ieee80211_find_node(struct ieee80211_node_table *nt, const A_UINT8 *macaddr) { bss_t *ni; int hash; IEEE80211_NODE_LOCK_ASSERT(nt); hash = IEEE80211_NODE_HASH(macaddr); for(ni = nt->nt_hash[hash]; ni; ni = ni->ni_hash_next) { if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) { ieee80211_node_incref(ni); /* mark referenced */ return ni; } } return NULL; } bss_t * wlan_find_node(struct ieee80211_node_table *nt, const A_UINT8 *macaddr) { bss_t *ni; IEEE80211_NODE_LOCK(nt); ni = _ieee80211_find_node(nt, macaddr); IEEE80211_NODE_UNLOCK(nt); return ni; } /* * Reclaim a node. If this is the last reference count then * do the normal free work. Otherwise remove it from the node * table and mark it gone by clearing the back-reference. */ void wlan_node_reclaim(struct ieee80211_node_table *nt, bss_t *ni) { IEEE80211_NODE_LOCK(nt); if(ni->ni_list_prev == NULL) { /* First in list so fix the list head */ nt->nt_node_first = ni->ni_list_next; } else { ni->ni_list_prev->ni_list_next = ni->ni_list_next; } if(ni->ni_list_next == NULL) { /* Last in list so fix list tail */ nt->nt_node_last = ni->ni_list_prev; } else { ni->ni_list_next->ni_list_prev = ni->ni_list_prev; } if(ni->ni_hash_prev == NULL) { /* First in list so fix the list head */ int hash; hash = IEEE80211_NODE_HASH(ni->ni_macaddr); nt->nt_hash[hash] = ni->ni_hash_next; } else { ni->ni_hash_prev->ni_hash_next = ni->ni_hash_next; } if(ni->ni_hash_next != NULL) { ni->ni_hash_next->ni_hash_prev = ni->ni_hash_prev; } wlan_node_free(ni); IEEE80211_NODE_UNLOCK(nt); } static void wlan_node_dec_free(bss_t *ni) { if (ieee80211_node_dectestref(ni)) { wlan_node_free(ni); } } void wlan_free_allnodes(struct ieee80211_node_table *nt) { bss_t *ni; while ((ni = nt->nt_node_first) != NULL) { wlan_node_reclaim(nt, ni); } } void wlan_iterate_nodes(struct ieee80211_node_table *nt, wlan_node_iter_func *f, void *arg) { bss_t *ni; A_UINT32 gen; gen = ++nt->nt_scangen; IEEE80211_NODE_LOCK(nt); for (ni = nt->nt_node_first; ni; ni = ni->ni_list_next) { if (ni->ni_scangen != gen) { ni->ni_scangen = gen; (void) ieee80211_node_incref(ni); (*f)(arg, ni); wlan_node_dec_free(ni); } } IEEE80211_NODE_UNLOCK(nt); } /* * Node table support. */ void wlan_node_table_init(void *wmip, struct ieee80211_node_table *nt) { int i; AR_DEBUG_PRINTF(ATH_DEBUG_WLAN, ("node table = 0x%lx\n", (unsigned long)nt)); IEEE80211_NODE_LOCK_INIT(nt); A_REGISTER_MODULE_DEBUG_INFO(wlan); nt->nt_node_first = nt->nt_node_last = NULL; for(i = 0; i < IEEE80211_NODE_HASHSIZE; i++) { nt->nt_hash[i] = NULL; } #ifdef THREAD_X A_INIT_TIMER(&nt->nt_inact_timer, wlan_node_timeout, nt); nt->isTimerArmed = FALSE; #endif nt->nt_wmip = wmip; nt->nt_nodeAge = WLAN_NODE_INACT_TIMEOUT_MSEC; // // nt_scangen never initialized before and during suspend/resume of winmobile, // that some junk has been stored in this, due to this scan list didn't properly updated // nt->nt_scangen = 0; #ifdef OS_ROAM_MANAGEMENT nt->nt_si_gen = 0; #endif } void wlan_set_nodeage(struct ieee80211_node_table *nt, A_UINT32 nodeAge) { nt->nt_nodeAge = nodeAge; return; } void wlan_refresh_inactive_nodes (struct ieee80211_node_table *nt) { #ifdef THREAD_X bss_t *bss, *nextBss; A_UINT8 myBssid[IEEE80211_ADDR_LEN], reArmTimer = FALSE; wmi_get_current_bssid(nt->nt_wmip, myBssid); bss = nt->nt_node_first; while (bss != NULL) { nextBss = bss->ni_list_next; if (A_MEMCMP(myBssid, bss->ni_macaddr, sizeof(myBssid)) != 0) { /* * free up all but the current bss - if set */ wlan_node_reclaim(nt, bss); } bss = nextBss; } #else bss_t *bss, *nextBss; A_UINT8 myBssid[IEEE80211_ADDR_LEN]; A_UINT32 timeoutValue = 0; A_UINT32 now = A_GET_MS(0); timeoutValue = nt->nt_nodeAge; wmi_get_current_bssid(nt->nt_wmip, myBssid); bss = nt->nt_node_first; while (bss != NULL) { nextBss = bss->ni_list_next; if (A_MEMCMP(myBssid, bss->ni_macaddr, sizeof(myBssid)) != 0) { if (bss->ni_tstamp <= now || --bss->ni_actcnt == 0) { /* * free up all but the current bss - if set */ wlan_node_reclaim(nt, bss); } } bss = nextBss; } #endif } #ifdef THREAD_X static void wlan_node_timeout (A_ATH_TIMER arg) { struct ieee80211_node_table *nt = (struct ieee80211_node_table *)arg; bss_t *bss, *nextBss; A_UINT8 myBssid[IEEE80211_ADDR_LEN], reArmTimer = FALSE; A_UINT32 timeoutValue = 0; timeoutValue = nt->nt_nodeAge; wmi_get_current_bssid(nt->nt_wmip, myBssid); bss = nt->nt_node_first; while (bss != NULL) { nextBss = bss->ni_list_next; if (A_MEMCMP(myBssid, bss->ni_macaddr, sizeof(myBssid)) != 0) { if (bss->ni_tstamp <= A_GET_MS(0)) { /* * free up all but the current bss - if set */ wlan_node_reclaim(nt, bss); } else { /* * Re-arm timer, only when we have a bss other than * current bss AND it is not aged-out. */ reArmTimer = TRUE; } } bss = nextBss; } if (reArmTimer) A_TIMEOUT_MS (&nt->nt_inact_timer, timeoutValue, 0); nt->isTimerArmed = reArmTimer; } #endif void wlan_node_table_cleanup(struct ieee80211_node_table *nt) { #ifdef THREAD_X A_UNTIMEOUT(&nt->nt_inact_timer); A_DELETE_TIMER(&nt->nt_inact_timer); #endif wlan_free_allnodes(nt); IEEE80211_NODE_LOCK_DESTROY(nt); } bss_t * wlan_find_Ssidnode (struct ieee80211_node_table *nt, A_UCHAR *pSsid, A_UINT32 ssidLength, A_BOOL bIsWPA2, A_BOOL bMatchSSID) { bss_t *ni = NULL; A_UCHAR *pIESsid = NULL; IEEE80211_NODE_LOCK (nt); for (ni = nt->nt_node_first; ni; ni = ni->ni_list_next) { pIESsid = ni->ni_cie.ie_ssid; if (pIESsid[1] <= 32) { // Step 1 : Check SSID if (0x00 == memcmp (pSsid, &pIESsid[2], ssidLength)) { // // Step 2.1 : Check MatchSSID is TRUE, if so, return Matched SSID // Profile, otherwise check whether WPA2 or WPA // if (TRUE == bMatchSSID) { ieee80211_node_incref (ni); /* mark referenced */ IEEE80211_NODE_UNLOCK (nt); return ni; } // Step 2 : if SSID matches, check WPA or WPA2 if (TRUE == bIsWPA2 && NULL != ni->ni_cie.ie_rsn) { ieee80211_node_incref (ni); /* mark referenced */ IEEE80211_NODE_UNLOCK (nt); return ni; } if (FALSE == bIsWPA2 && NULL != ni->ni_cie.ie_wpa) { ieee80211_node_incref(ni); /* mark referenced */ IEEE80211_NODE_UNLOCK (nt); return ni; } } } } IEEE80211_NODE_UNLOCK (nt); return NULL; } void wlan_node_return (struct ieee80211_node_table *nt, bss_t *ni) { IEEE80211_NODE_LOCK (nt); wlan_node_dec_free (ni); IEEE80211_NODE_UNLOCK (nt); } void wlan_node_remove_core (struct ieee80211_node_table *nt, bss_t *ni) { if(ni->ni_list_prev == NULL) { /* First in list so fix the list head */ nt->nt_node_first = ni->ni_list_next; } else { ni->ni_list_prev->ni_list_next = ni->ni_list_next; } if(ni->ni_list_next == NULL) { /* Last in list so fix list tail */ nt->nt_node_last = ni->ni_list_prev; } else { ni->ni_list_next->ni_list_prev = ni->ni_list_prev; } if(ni->ni_hash_prev == NULL) { /* First in list so fix the list head */ int hash; hash = IEEE80211_NODE_HASH(ni->ni_macaddr); nt->nt_hash[hash] = ni->ni_hash_next; } else { ni->ni_hash_prev->ni_hash_next = ni->ni_hash_next; } if(ni->ni_hash_next != NULL) { ni->ni_hash_next->ni_hash_prev = ni->ni_hash_prev; } } bss_t * wlan_node_remove(struct ieee80211_node_table *nt, A_UINT8 *bssid) { bss_t *bss, *nextBss; IEEE80211_NODE_LOCK(nt); bss = nt->nt_node_first; while (bss != NULL) { nextBss = bss->ni_list_next; if (A_MEMCMP(bssid, bss->ni_macaddr, 6) == 0) { wlan_node_remove_core (nt, bss); IEEE80211_NODE_UNLOCK(nt); return bss; } bss = nextBss; } IEEE80211_NODE_UNLOCK(nt); return NULL; } bss_t * wlan_find_matching_Ssidnode (struct ieee80211_node_table *nt, A_UCHAR *pSsid, A_UINT32 ssidLength, A_UINT32 dot11AuthMode, A_UINT32 authMode, A_UINT32 pairwiseCryptoType, A_UINT32 grpwiseCryptoTyp) { bss_t *ni = NULL; bss_t *best_ni = NULL; A_UCHAR *pIESsid = NULL; IEEE80211_NODE_LOCK (nt); for (ni = nt->nt_node_first; ni; ni = ni->ni_list_next) { pIESsid = ni->ni_cie.ie_ssid; if (pIESsid[1] <= 32) { // Step 1 : Check SSID if (0x00 == memcmp (pSsid, &pIESsid[2], ssidLength)) { if (ni->ni_cie.ie_capInfo & 0x10) { if ((NULL != ni->ni_cie.ie_rsn) && (WPA2_PSK_AUTH == authMode)) { /* WPA2 */ if (NULL == best_ni) { best_ni = ni; } else if (ni->ni_rssi > best_ni->ni_rssi) { best_ni = ni; } } else if ((NULL != ni->ni_cie.ie_wpa) && (WPA_PSK_AUTH == authMode)) { /* WPA */ if (NULL == best_ni) { best_ni = ni; } else if (ni->ni_rssi > best_ni->ni_rssi) { best_ni = ni; } } else if (WEP_CRYPT == pairwiseCryptoType) { /* WEP */ if (NULL == best_ni) { best_ni = ni; } else if (ni->ni_rssi > best_ni->ni_rssi) { best_ni = ni; } } } else { /* open AP */ if ((OPEN_AUTH == authMode) && (NONE_CRYPT == pairwiseCryptoType)) { if (NULL == best_ni) { best_ni = ni; } else if (ni->ni_rssi > best_ni->ni_rssi) { best_ni = ni; } } } } } } IEEE80211_NODE_UNLOCK (nt); return best_ni; }