summaryrefslogtreecommitdiffstats
path: root/drivers/staging/ath6kl/wlan/src/wlan_node.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/ath6kl/wlan/src/wlan_node.c')
-rw-r--r--drivers/staging/ath6kl/wlan/src/wlan_node.c636
1 files changed, 636 insertions, 0 deletions
diff --git a/drivers/staging/ath6kl/wlan/src/wlan_node.c b/drivers/staging/ath6kl/wlan/src/wlan_node.c
new file mode 100644
index 0000000..6ec4e48
--- /dev/null
+++ b/drivers/staging/ath6kl/wlan/src/wlan_node.c
@@ -0,0 +1,636 @@
+//------------------------------------------------------------------------------
+// <copyright file="wlan_node.c" company="Atheros">
+// 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 <a_config.h>
+#include <athdefs.h>
+#include <a_types.h>
+#include <a_osapi.h>
+#define ATH_MODULE_NAME wlan
+#include <a_debug.h>
+#include "htc.h"
+#include "htc_api.h"
+#include <wmi.h>
+#include <ieee80211.h>
+#include <wlan_api.h>
+#include <wmi_api.h>
+#include <ieee80211_node.h>
+
+#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;
+}
+
OpenPOWER on IntegriCloud