/* * Copyright (c) 2004-2011 Atheros Communications Inc. * * 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. */ #include "htc.h" #include "wmi.h" #include "debug.h" struct bss *wlan_node_alloc(int wh_size) { struct bss *ni; ni = kzalloc(sizeof(struct bss), GFP_ATOMIC); if ((ni != NULL) && wh_size) { ni->ni_buf = kmalloc(wh_size, GFP_ATOMIC); if (ni->ni_buf == NULL) { kfree(ni); return NULL; } } return ni; } void wlan_node_free(struct bss *ni) { kfree(ni->ni_buf); kfree(ni); } void wlan_setup_node(struct ath6kl_node_table *nt, struct bss *ni, const u8 *mac_addr) { int hash; memcpy(ni->ni_macaddr, mac_addr, ETH_ALEN); hash = ATH6KL_NODE_HASH(mac_addr); ni->ni_refcnt = 1; ni->ni_tstamp = jiffies_to_msecs(jiffies); ni->ni_actcnt = WLAN_NODE_INACT_CNT; spin_lock_bh(&nt->nt_nodelock); /* 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 */ ni->ni_hash_next = nt->nt_hash[hash]; if (ni->ni_hash_next != NULL) nt->nt_hash[hash]->ni_hash_prev = ni; ni->ni_hash_prev = NULL; nt->nt_hash[hash] = ni; spin_unlock_bh(&nt->nt_nodelock); } struct bss *wlan_find_node(struct ath6kl_node_table *nt, const u8 *mac_addr) { struct bss *ni, *found_ni = NULL; int hash; spin_lock_bh(&nt->nt_nodelock); hash = ATH6KL_NODE_HASH(mac_addr); for (ni = nt->nt_hash[hash]; ni; ni = ni->ni_hash_next) { if (memcmp(ni->ni_macaddr, mac_addr, ETH_ALEN) == 0) { ni->ni_refcnt++; found_ni = ni; break; } } spin_unlock_bh(&nt->nt_nodelock); return found_ni; } void wlan_node_reclaim(struct ath6kl_node_table *nt, struct bss *ni) { int hash; spin_lock_bh(&nt->nt_nodelock); if (ni->ni_list_prev == NULL) /* fix 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) /* 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 */ hash = ATH6KL_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); spin_unlock_bh(&nt->nt_nodelock); } static void wlan_node_dec_free(struct bss *ni) { if ((ni->ni_refcnt--) == 1) wlan_node_free(ni); } void wlan_free_allnodes(struct ath6kl_node_table *nt) { struct bss *ni; while ((ni = nt->nt_node_first) != NULL) wlan_node_reclaim(nt, ni); } void wlan_iterate_nodes(struct ath6kl_node_table *nt, void (*f) (void *arg, struct bss *), void *arg) { struct bss *ni; spin_lock_bh(&nt->nt_nodelock); for (ni = nt->nt_node_first; ni; ni = ni->ni_list_next) { ni->ni_refcnt++; (*f) (arg, ni); wlan_node_dec_free(ni); } spin_unlock_bh(&nt->nt_nodelock); } void wlan_node_table_init(void *wmi, struct ath6kl_node_table *nt) { ath6kl_dbg(ATH6KL_DBG_WLAN_NODE, "node table = 0x%lx\n", (unsigned long)nt); memset(nt, 0, sizeof(struct ath6kl_node_table)); spin_lock_init(&nt->nt_nodelock); nt->nt_wmi = wmi; nt->nt_node_age = WLAN_NODE_INACT_TIMEOUT_MSEC; } void wlan_refresh_inactive_nodes(struct ath6kl_node_table *nt) { struct bss *bss; u8 my_bssid[ETH_ALEN]; u32 now; ath6kl_wmi_get_current_bssid(nt->nt_wmi, my_bssid); now = jiffies_to_msecs(jiffies); bss = nt->nt_node_first; while (bss != NULL) { /* refresh all nodes except the current bss */ if (memcmp(my_bssid, bss->ni_macaddr, sizeof(my_bssid)) != 0) { if (((now - bss->ni_tstamp) > nt->nt_node_age) || --bss->ni_actcnt == 0) { wlan_node_reclaim(nt, bss); } } bss = bss->ni_list_next; } } void wlan_node_table_cleanup(struct ath6kl_node_table *nt) { wlan_free_allnodes(nt); } struct bss *wlan_find_ssid_node(struct ath6kl_node_table *nt, u8 * ssid, u32 ssid_len, bool is_wpa2, bool match_ssid) { struct bss *ni, *found_ni = NULL; u8 *ie_ssid; spin_lock_bh(&nt->nt_nodelock); for (ni = nt->nt_node_first; ni; ni = ni->ni_list_next) { ie_ssid = ni->ni_cie.ie_ssid; if ((ie_ssid[1] <= IEEE80211_MAX_SSID_LEN) && (memcmp(ssid, &ie_ssid[2], ssid_len) == 0)) { if (match_ssid || (is_wpa2 && ni->ni_cie.ie_rsn != NULL) || (!is_wpa2 && ni->ni_cie.ie_wpa != NULL)) { ni->ni_refcnt++; found_ni = ni; break; } } } spin_unlock_bh(&nt->nt_nodelock); return found_ni; } void wlan_node_return(struct ath6kl_node_table *nt, struct bss *ni) { spin_lock_bh(&nt->nt_nodelock); wlan_node_dec_free(ni); spin_unlock_bh(&nt->nt_nodelock); }