1/*- 2 * Copyright (c) 2001 Atsushi Onoe 3 * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting 4 * Copyright (c) 2004-2005 Atheros Communications 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission. 17 * 18 * Alternatively, this software may be distributed under the terms of the 19 * GNU General Public License ("GPL") version 2 as published by the Free 20 * Software Foundation. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 * 33 * $Id: //depot/sw/releases/olca2.0-GPL/host/wlan/src/wlan_node.c#1 $ 34 */ 35/* 36 * IEEE 802.11 node handling support. 37 */ 38#include <a_config.h> 39#include <athdefs.h> 40#include <a_types.h> 41#include <a_osapi.h> 42#include <a_debug.h> 43#include <ieee80211.h> 44#include <wlan_api.h> 45#include <ieee80211_node.h> 46#include <htc_api.h> 47#include <wmi.h> 48#include <wmi_api.h> 49 50static void wlan_node_timeout(A_ATH_TIMER arg); 51static bss_t * _ieee80211_find_node(struct ieee80211_node_table *nt, 52 const A_UINT8 *macaddr); 53 54bss_t * 55wlan_node_alloc(struct ieee80211_node_table *nt, int wh_size) 56{ 57 bss_t *ni; 58 59 ni = A_MALLOC_NOWAIT(sizeof(bss_t)); 60 61 if (ni != NULL) { 62 ni->ni_buf = A_MALLOC_NOWAIT(wh_size); 63 if (ni->ni_buf == NULL) { 64 A_FREE(ni); 65 ni = NULL; 66 return ni; 67 } 68 } else { 69 return ni; 70 } 71 72 /* Make sure our lists are clean */ 73 ni->ni_list_next = NULL; 74 ni->ni_list_prev = NULL; 75 ni->ni_hash_next = NULL; 76 ni->ni_hash_prev = NULL; 77 78 // 79 // ni_scangen never initialized before and during suspend/resume of winmobile, customer (LG/SEMCO) identified 80 // that some junk has been stored in this, due to this scan list didn't properly updated 81 // 82 ni->ni_scangen = 0; 83 84 return ni; 85} 86 87void 88wlan_node_free(bss_t *ni) 89{ 90 if (ni->ni_buf != NULL) { 91 A_FREE(ni->ni_buf); 92 } 93 A_FREE(ni); 94} 95 96void 97wlan_setup_node(struct ieee80211_node_table *nt, bss_t *ni, 98 const A_UINT8 *macaddr) 99{ 100 int hash; 101 102 A_MEMCPY(ni->ni_macaddr, macaddr, IEEE80211_ADDR_LEN); 103 hash = IEEE80211_NODE_HASH(macaddr); 104 ieee80211_node_initref(ni); /* mark referenced */ 105 106 ni->ni_tstamp = A_GET_MS(WLAN_NODE_INACT_TIMEOUT_MSEC); 107 IEEE80211_NODE_LOCK_BH(nt); 108 109 /* Insert at the end of the node list */ 110 ni->ni_list_next = NULL; 111 ni->ni_list_prev = nt->nt_node_last; 112 if(nt->nt_node_last != NULL) 113 { 114 nt->nt_node_last->ni_list_next = ni; 115 } 116 nt->nt_node_last = ni; 117 if(nt->nt_node_first == NULL) 118 { 119 nt->nt_node_first = ni; 120 } 121 122 /* Insert into the hash list i.e. the bucket */ 123 if((ni->ni_hash_next = nt->nt_hash[hash]) != NULL) 124 { 125 nt->nt_hash[hash]->ni_hash_prev = ni; 126 } 127 ni->ni_hash_prev = NULL; 128 nt->nt_hash[hash] = ni; 129 130 if (!nt->isTimerArmed) { 131 A_TIMEOUT_MS(&nt->nt_inact_timer, WLAN_NODE_INACT_TIMEOUT_MSEC, 0); 132 nt->isTimerArmed = TRUE; 133 } 134 135 IEEE80211_NODE_UNLOCK_BH(nt); 136} 137 138static bss_t * 139_ieee80211_find_node(struct ieee80211_node_table *nt, 140 const A_UINT8 *macaddr) 141{ 142 bss_t *ni; 143 int hash; 144 145 IEEE80211_NODE_LOCK_ASSERT(nt); 146 147 hash = IEEE80211_NODE_HASH(macaddr); 148 for(ni = nt->nt_hash[hash]; ni; ni = ni->ni_hash_next) { 149 if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) { 150 ieee80211_node_incref(ni); /* mark referenced */ 151 return ni; 152 } 153 } 154 return NULL; 155} 156 157bss_t * 158wlan_find_node(struct ieee80211_node_table *nt, const A_UINT8 *macaddr) 159{ 160 bss_t *ni; 161 162 IEEE80211_NODE_LOCK(nt); 163 ni = _ieee80211_find_node(nt, macaddr); 164 IEEE80211_NODE_UNLOCK(nt); 165 return ni; 166} 167 168/* 169 * Reclaim a node. If this is the last reference count then 170 * do the normal free work. Otherwise remove it from the node 171 * table and mark it gone by clearing the back-reference. 172 */ 173void 174wlan_node_reclaim(struct ieee80211_node_table *nt, bss_t *ni) 175{ 176 IEEE80211_NODE_LOCK(nt); 177 178 if(ni->ni_list_prev == NULL) 179 { 180 /* First in list so fix the list head */ 181 nt->nt_node_first = ni->ni_list_next; 182 } 183 else 184 { 185 ni->ni_list_prev->ni_list_next = ni->ni_list_next; 186 } 187 188 if(ni->ni_list_next == NULL) 189 { 190 /* Last in list so fix list tail */ 191 nt->nt_node_last = ni->ni_list_prev; 192 } 193 else 194 { 195 ni->ni_list_next->ni_list_prev = ni->ni_list_prev; 196 } 197 198 if(ni->ni_hash_prev == NULL) 199 { 200 /* First in list so fix the list head */ 201 int hash; 202 hash = IEEE80211_NODE_HASH(ni->ni_macaddr); 203 nt->nt_hash[hash] = ni->ni_hash_next; 204 } 205 else 206 { 207 ni->ni_hash_prev->ni_hash_next = ni->ni_hash_next; 208 } 209 210 if(ni->ni_hash_next != NULL) 211 { 212 ni->ni_hash_next->ni_hash_prev = ni->ni_hash_prev; 213 } 214 wlan_node_free(ni); 215 216 IEEE80211_NODE_UNLOCK(nt); 217} 218 219static void 220wlan_node_dec_free(bss_t *ni) 221{ 222 if (ieee80211_node_dectestref(ni)) { 223 wlan_node_free(ni); 224 } 225} 226 227void 228wlan_free_allnodes(struct ieee80211_node_table *nt) 229{ 230 bss_t *ni; 231 232 while ((ni = nt->nt_node_first) != NULL) { 233 wlan_node_reclaim(nt, ni); 234 } 235} 236 237void 238wlan_iterate_nodes(struct ieee80211_node_table *nt, wlan_node_iter_func *f, 239 void *arg) 240{ 241 bss_t *ni; 242 A_UINT32 gen; 243 244 gen = ++nt->nt_scangen; 245 246 IEEE80211_NODE_LOCK(nt); 247 for (ni = nt->nt_node_first; ni; ni = ni->ni_list_next) { 248 if (ni->ni_scangen != gen) { 249 ni->ni_scangen = gen; 250 (void) ieee80211_node_incref(ni); 251 (*f)(arg, ni); 252 wlan_node_dec_free(ni); 253 } 254 } 255 IEEE80211_NODE_UNLOCK(nt); 256} 257 258/* 259 * Node table support. 260 */ 261void 262wlan_node_table_init(void *wmip, struct ieee80211_node_table *nt) 263{ 264 int i; 265 266 AR_DEBUG_PRINTF(ATH_DEBUG_WLAN, ("node table = 0x%x\n", (A_UINT32)nt)); 267 IEEE80211_NODE_LOCK_INIT(nt); 268 269 nt->nt_node_first = nt->nt_node_last = NULL; 270 for(i = 0; i < IEEE80211_NODE_HASHSIZE; i++) 271 { 272 nt->nt_hash[i] = NULL; 273 } 274 A_INIT_TIMER(&nt->nt_inact_timer, wlan_node_timeout, nt); 275 nt->isTimerArmed = FALSE; 276 nt->nt_wmip = wmip; 277} 278 279static void 280wlan_node_timeout(A_ATH_TIMER arg) 281{ 282 struct ieee80211_node_table *nt = (struct ieee80211_node_table *)arg; 283 bss_t *bss, *nextBss; 284 A_UINT8 myBssid[IEEE80211_ADDR_LEN], reArmTimer = FALSE; 285 286 wmi_get_current_bssid(nt->nt_wmip, myBssid); 287 288 bss = nt->nt_node_first; 289 while (bss != NULL) 290 { 291 nextBss = bss->ni_list_next; 292 if (A_MEMCMP(myBssid, bss->ni_macaddr, sizeof(myBssid)) != 0) 293 { 294 295 if (bss->ni_tstamp <= A_GET_MS(0)) 296 { 297 /* 298 * free up all but the current bss - if set 299 */ 300 wlan_node_reclaim(nt, bss); 301 } 302 else 303 { 304 /* 305 * Re-arm timer, only when we have a bss other than 306 * current bss AND it is not aged-out. 307 */ 308 reArmTimer = TRUE; 309 } 310 } 311 bss = nextBss; 312 } 313 314 if(reArmTimer) 315 A_TIMEOUT_MS(&nt->nt_inact_timer, WLAN_NODE_INACT_TIMEOUT_MSEC, 0); 316 317 nt->isTimerArmed = reArmTimer; 318} 319 320void 321wlan_node_table_cleanup(struct ieee80211_node_table *nt) 322{ 323 A_UNTIMEOUT(&nt->nt_inact_timer); 324 A_DELETE_TIMER(&nt->nt_inact_timer); 325 wlan_free_allnodes(nt); 326 IEEE80211_NODE_LOCK_DESTROY(nt); 327} 328 329bss_t * 330wlan_find_Ssidnode (struct ieee80211_node_table *nt, A_UCHAR *pSsid, 331 A_UINT32 ssidLength, A_BOOL bIsWPA2) 332{ 333 bss_t *ni = NULL; 334 A_UCHAR *pIESsid = NULL; 335 336 IEEE80211_NODE_LOCK (nt); 337 338 for (ni = nt->nt_node_first; ni; ni = ni->ni_list_next) { 339 pIESsid = ni->ni_cie.ie_ssid; 340 if (pIESsid[1] <= 32) { 341 342 // Step 1 : Check SSID 343 if (0x00 == memcmp (pSsid, &pIESsid[2], ssidLength)) { 344 345 // Step 2 : if SSID matches, check WPA or WPA2 346 if (TRUE == bIsWPA2 && NULL != ni->ni_cie.ie_rsn) { 347 ieee80211_node_incref (ni); /* mark referenced */ 348 IEEE80211_NODE_UNLOCK (nt); 349 return ni; 350 } 351 if (FALSE == bIsWPA2 && NULL != ni->ni_cie.ie_wpa) { 352 ieee80211_node_incref(ni); /* mark referenced */ 353 IEEE80211_NODE_UNLOCK (nt); 354 return ni; 355 } 356 } 357 } 358 } 359 360 IEEE80211_NODE_UNLOCK (nt); 361 362 return NULL; 363} 364 365void 366wlan_node_return (struct ieee80211_node_table *nt, bss_t *ni) 367{ 368 IEEE80211_NODE_LOCK (nt); 369 wlan_node_dec_free (ni); 370 IEEE80211_NODE_UNLOCK (nt); 371} 372