Merge branch 'x86-platform-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[pandora-kernel.git] / drivers / staging / ath6kl / wlan / src / wlan_node.c
1 //------------------------------------------------------------------------------
2 // <copyright file="wlan_node.c" company="Atheros">
3 //    Copyright (c) 2004-2010 Atheros Corporation.  All rights reserved.
4 // 
5 //
6 // Permission to use, copy, modify, and/or distribute this software for any
7 // purpose with or without fee is hereby granted, provided that the above
8 // copyright notice and this permission notice appear in all copies.
9 //
10 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 //
18 //
19 //------------------------------------------------------------------------------
20 //==============================================================================
21 // IEEE 802.11 node handling support.
22 //
23 // Author(s): ="Atheros"
24 //==============================================================================
25 #include <a_config.h>
26 #include <athdefs.h>
27 #include <a_types.h>
28 #include <a_osapi.h>
29 #define ATH_MODULE_NAME wlan
30 #include <a_debug.h>
31 #include "htc.h"
32 #include "htc_api.h"
33 #include <wmi.h>
34 #include <ieee80211.h>
35 #include <wlan_api.h>
36 #include <wmi_api.h>
37 #include <ieee80211_node.h>
38
39 #define ATH_DEBUG_WLAN ATH_DEBUG_MAKE_MODULE_MASK(0)
40
41 #ifdef ATH_DEBUG_MODULE
42
43 static ATH_DEBUG_MASK_DESCRIPTION wlan_debug_desc[] = {
44     { ATH_DEBUG_WLAN , "General WLAN Node Tracing"},
45 };
46
47 ATH_DEBUG_INSTANTIATE_MODULE_VAR(wlan,
48                                  "wlan",
49                                  "WLAN Node Management",
50                                  ATH_DEBUG_MASK_DEFAULTS,
51                                  ATH_DEBUG_DESCRIPTION_COUNT(wlan_debug_desc),
52                                  wlan_debug_desc);
53                                  
54 #endif
55
56 #ifdef THREAD_X
57 static void wlan_node_timeout(A_ATH_TIMER arg);
58 #endif
59
60 static bss_t * _ieee80211_find_node (struct ieee80211_node_table *nt,
61                                      const A_UINT8 *macaddr);
62
63 bss_t *
64 wlan_node_alloc(struct ieee80211_node_table *nt, int wh_size)
65 {
66     bss_t *ni;
67
68     ni = A_MALLOC_NOWAIT(sizeof(bss_t));
69
70     if (ni != NULL) {
71         if (wh_size)
72         {
73         ni->ni_buf = A_MALLOC_NOWAIT(wh_size);
74         if (ni->ni_buf == NULL) {
75             A_FREE(ni);
76             ni = NULL;
77             return ni;
78         }
79         }
80     } else {
81         return ni;
82     }
83
84     /* Make sure our lists are clean */
85     ni->ni_list_next = NULL;
86     ni->ni_list_prev = NULL;
87     ni->ni_hash_next = NULL;
88     ni->ni_hash_prev = NULL;
89
90     //
91     // ni_scangen never initialized before and during suspend/resume of winmobile,
92     // that some junk has been stored in this, due to this scan list didn't properly updated
93     //
94     ni->ni_scangen   = 0;
95
96 #ifdef OS_ROAM_MANAGEMENT
97     ni->ni_si_gen    = 0;
98 #endif
99
100     return ni;
101 }
102
103 void
104 wlan_node_free(bss_t *ni)
105 {
106     if (ni->ni_buf != NULL) {
107         A_FREE(ni->ni_buf);
108     }
109     A_FREE(ni);
110 }
111
112 void
113 wlan_setup_node(struct ieee80211_node_table *nt, bss_t *ni,
114                 const A_UINT8 *macaddr)
115 {
116     int hash;
117     A_UINT32 timeoutValue = 0;
118
119     A_MEMCPY(ni->ni_macaddr, macaddr, IEEE80211_ADDR_LEN);
120     hash = IEEE80211_NODE_HASH (macaddr);
121     ieee80211_node_initref (ni);     /* mark referenced */
122
123     timeoutValue = nt->nt_nodeAge;
124
125     ni->ni_tstamp = A_GET_MS (timeoutValue);
126     ni->ni_actcnt = WLAN_NODE_INACT_CNT;
127
128     IEEE80211_NODE_LOCK_BH(nt);
129
130     /* Insert at the end of the node list */
131     ni->ni_list_next = NULL;
132     ni->ni_list_prev = nt->nt_node_last;
133     if(nt->nt_node_last != NULL)
134     {
135         nt->nt_node_last->ni_list_next = ni;
136     }
137     nt->nt_node_last = ni;
138     if(nt->nt_node_first == NULL)
139     {
140         nt->nt_node_first = ni;
141     }
142
143     /* Insert into the hash list i.e. the bucket */
144     if((ni->ni_hash_next = nt->nt_hash[hash]) != NULL)
145     {
146         nt->nt_hash[hash]->ni_hash_prev = ni;
147     }
148     ni->ni_hash_prev = NULL;
149     nt->nt_hash[hash] = ni;
150
151 #ifdef THREAD_X
152     if (!nt->isTimerArmed) {
153         A_TIMEOUT_MS(&nt->nt_inact_timer, timeoutValue, 0);
154         nt->isTimerArmed = TRUE;
155     }
156 #endif
157
158     IEEE80211_NODE_UNLOCK_BH(nt);
159 }
160
161 static bss_t *
162 _ieee80211_find_node(struct ieee80211_node_table *nt,
163     const A_UINT8 *macaddr)
164 {
165     bss_t *ni;
166     int hash;
167
168     IEEE80211_NODE_LOCK_ASSERT(nt);
169
170     hash = IEEE80211_NODE_HASH(macaddr);
171     for(ni = nt->nt_hash[hash]; ni; ni = ni->ni_hash_next) {
172         if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) {
173             ieee80211_node_incref(ni);  /* mark referenced */
174             return ni;
175         }
176     }
177     return NULL;
178 }
179
180 bss_t *
181 wlan_find_node(struct ieee80211_node_table *nt, const A_UINT8 *macaddr)
182 {
183     bss_t *ni;
184
185     IEEE80211_NODE_LOCK(nt);
186     ni = _ieee80211_find_node(nt, macaddr);
187     IEEE80211_NODE_UNLOCK(nt);
188     return ni;
189 }
190
191 /*
192  * Reclaim a node.  If this is the last reference count then
193  * do the normal free work.  Otherwise remove it from the node
194  * table and mark it gone by clearing the back-reference.
195  */
196 void
197 wlan_node_reclaim(struct ieee80211_node_table *nt, bss_t *ni)
198 {
199     IEEE80211_NODE_LOCK(nt);
200
201     if(ni->ni_list_prev == NULL)
202     {
203         /* First in list so fix the list head */
204         nt->nt_node_first = ni->ni_list_next;
205     }
206     else
207     {
208         ni->ni_list_prev->ni_list_next = ni->ni_list_next;
209     }
210
211     if(ni->ni_list_next == NULL)
212     {
213         /* Last in list so fix list tail */
214         nt->nt_node_last = ni->ni_list_prev;
215     }
216     else
217     {
218         ni->ni_list_next->ni_list_prev = ni->ni_list_prev;
219     }
220
221     if(ni->ni_hash_prev == NULL)
222     {
223         /* First in list so fix the list head */
224         int hash;
225         hash = IEEE80211_NODE_HASH(ni->ni_macaddr);
226         nt->nt_hash[hash] = ni->ni_hash_next;
227     }
228     else
229     {
230         ni->ni_hash_prev->ni_hash_next = ni->ni_hash_next;
231     }
232
233     if(ni->ni_hash_next != NULL)
234     {
235         ni->ni_hash_next->ni_hash_prev = ni->ni_hash_prev;
236     }
237     wlan_node_free(ni);
238
239     IEEE80211_NODE_UNLOCK(nt);
240 }
241
242 static void
243 wlan_node_dec_free(bss_t *ni)
244 {
245     if (ieee80211_node_dectestref(ni)) {
246         wlan_node_free(ni);
247     }
248 }
249
250 void
251 wlan_free_allnodes(struct ieee80211_node_table *nt)
252 {
253     bss_t *ni;
254
255     while ((ni = nt->nt_node_first) != NULL) {
256         wlan_node_reclaim(nt, ni);
257     }
258 }
259
260 void
261 wlan_iterate_nodes(struct ieee80211_node_table *nt, wlan_node_iter_func *f,
262                    void *arg)
263 {
264     bss_t *ni;
265     A_UINT32 gen;
266
267     gen = ++nt->nt_scangen;
268
269     IEEE80211_NODE_LOCK(nt);
270     for (ni = nt->nt_node_first; ni; ni = ni->ni_list_next) {
271         if (ni->ni_scangen != gen) {
272             ni->ni_scangen = gen;
273             (void) ieee80211_node_incref(ni);
274             (*f)(arg, ni);
275             wlan_node_dec_free(ni);
276         }
277     }
278     IEEE80211_NODE_UNLOCK(nt);
279 }
280
281 /*
282  * Node table support.
283  */
284 void
285 wlan_node_table_init(void *wmip, struct ieee80211_node_table *nt)
286 {
287     int i;
288
289     AR_DEBUG_PRINTF(ATH_DEBUG_WLAN, ("node table = 0x%lx\n", (unsigned long)nt));
290     IEEE80211_NODE_LOCK_INIT(nt);
291
292     A_REGISTER_MODULE_DEBUG_INFO(wlan);
293     
294     nt->nt_node_first = nt->nt_node_last = NULL;
295     for(i = 0; i < IEEE80211_NODE_HASHSIZE; i++)
296     {
297         nt->nt_hash[i] = NULL;
298     }
299
300 #ifdef THREAD_X
301     A_INIT_TIMER(&nt->nt_inact_timer, wlan_node_timeout, nt);
302     nt->isTimerArmed = FALSE;
303 #endif
304     nt->nt_wmip = wmip;
305     nt->nt_nodeAge = WLAN_NODE_INACT_TIMEOUT_MSEC;
306
307     //
308     // nt_scangen never initialized before and during suspend/resume of winmobile, 
309     // that some junk has been stored in this, due to this scan list didn't properly updated
310     //
311     nt->nt_scangen   = 0;
312
313 #ifdef OS_ROAM_MANAGEMENT
314     nt->nt_si_gen    = 0;
315 #endif
316 }
317
318 void
319 wlan_set_nodeage(struct ieee80211_node_table *nt, A_UINT32 nodeAge)
320 {
321     nt->nt_nodeAge = nodeAge;
322     return;
323 }
324 void
325 wlan_refresh_inactive_nodes (struct ieee80211_node_table *nt)
326 {
327 #ifdef THREAD_X
328     bss_t *bss, *nextBss;
329     A_UINT8 myBssid[IEEE80211_ADDR_LEN], reArmTimer = FALSE;
330
331     wmi_get_current_bssid(nt->nt_wmip, myBssid);
332
333     bss = nt->nt_node_first;
334     while (bss != NULL)
335     {
336         nextBss = bss->ni_list_next;
337         if (A_MEMCMP(myBssid, bss->ni_macaddr, sizeof(myBssid)) != 0)
338         {
339                /*
340                 * free up all but the current bss - if set
341                 */
342                 wlan_node_reclaim(nt, bss);
343
344         }
345         bss = nextBss;
346     }
347 #else
348     bss_t *bss, *nextBss;
349     A_UINT8 myBssid[IEEE80211_ADDR_LEN];
350     A_UINT32 timeoutValue = 0;
351     A_UINT32 now = A_GET_MS(0);
352     timeoutValue = nt->nt_nodeAge;
353
354     wmi_get_current_bssid(nt->nt_wmip, myBssid);
355
356     bss = nt->nt_node_first;
357     while (bss != NULL)
358     {
359         nextBss = bss->ni_list_next;
360         if (A_MEMCMP(myBssid, bss->ni_macaddr, sizeof(myBssid)) != 0)
361         {
362
363             if (bss->ni_tstamp <= now || --bss->ni_actcnt == 0)
364             {
365                /*
366                 * free up all but the current bss - if set
367                 */
368                 wlan_node_reclaim(nt, bss);
369             }
370         }
371         bss = nextBss;
372     }
373 #endif
374 }
375
376 #ifdef THREAD_X
377 static void
378 wlan_node_timeout (A_ATH_TIMER arg)
379 {
380     struct ieee80211_node_table *nt = (struct ieee80211_node_table *)arg;
381     bss_t *bss, *nextBss;
382     A_UINT8 myBssid[IEEE80211_ADDR_LEN], reArmTimer = FALSE;
383     A_UINT32 timeoutValue = 0;
384
385     timeoutValue = nt->nt_nodeAge;
386
387     wmi_get_current_bssid(nt->nt_wmip, myBssid);
388
389     bss = nt->nt_node_first;
390     while (bss != NULL)
391     {
392         nextBss = bss->ni_list_next;
393         if (A_MEMCMP(myBssid, bss->ni_macaddr, sizeof(myBssid)) != 0)
394         {
395
396             if (bss->ni_tstamp <= A_GET_MS(0))
397             {
398                /*
399                 * free up all but the current bss - if set
400                 */
401                 wlan_node_reclaim(nt, bss);
402             }
403             else
404             {
405                 /*
406                  * Re-arm timer, only when we have a bss other than
407                  * current bss AND it is not aged-out.
408                  */
409                 reArmTimer = TRUE;
410             }
411         }
412         bss = nextBss;
413     }
414
415     if (reArmTimer)
416         A_TIMEOUT_MS (&nt->nt_inact_timer, timeoutValue, 0);
417
418     nt->isTimerArmed = reArmTimer;
419 }
420 #endif
421
422 void
423 wlan_node_table_cleanup(struct ieee80211_node_table *nt)
424 {
425 #ifdef THREAD_X
426     A_UNTIMEOUT(&nt->nt_inact_timer);
427     A_DELETE_TIMER(&nt->nt_inact_timer);
428 #endif
429     wlan_free_allnodes(nt);
430     IEEE80211_NODE_LOCK_DESTROY(nt);
431 }
432
433 bss_t *
434 wlan_find_Ssidnode (struct ieee80211_node_table *nt, A_UCHAR *pSsid,
435                     A_UINT32 ssidLength, A_BOOL bIsWPA2, A_BOOL bMatchSSID)
436 {
437     bss_t   *ni = NULL;
438     A_UCHAR *pIESsid = NULL;
439
440     IEEE80211_NODE_LOCK (nt);
441
442     for (ni = nt->nt_node_first; ni; ni = ni->ni_list_next) {
443         pIESsid = ni->ni_cie.ie_ssid;
444         if (pIESsid[1] <= 32) {
445
446             // Step 1 : Check SSID
447             if (0x00 == memcmp (pSsid, &pIESsid[2], ssidLength)) {
448
449                 //
450                 // Step 2.1 : Check MatchSSID is TRUE, if so, return Matched SSID
451                 // Profile, otherwise check whether WPA2 or WPA
452                 //
453                 if (TRUE == bMatchSSID) {
454                     ieee80211_node_incref (ni);  /* mark referenced */
455                     IEEE80211_NODE_UNLOCK (nt);
456                     return ni;
457                 }
458
459                 // Step 2 : if SSID matches, check WPA or WPA2
460                 if (TRUE == bIsWPA2 && NULL != ni->ni_cie.ie_rsn) {
461                     ieee80211_node_incref (ni);  /* mark referenced */
462                     IEEE80211_NODE_UNLOCK (nt);
463                     return ni;
464                 }
465                 if (FALSE == bIsWPA2 && NULL != ni->ni_cie.ie_wpa) {
466                     ieee80211_node_incref(ni);  /* mark referenced */
467                     IEEE80211_NODE_UNLOCK (nt);
468                     return ni;
469                 }
470             }
471         }
472     }
473
474     IEEE80211_NODE_UNLOCK (nt);
475
476     return NULL;
477 }
478
479 void
480 wlan_node_return (struct ieee80211_node_table *nt, bss_t *ni)
481 {
482     IEEE80211_NODE_LOCK (nt);
483     wlan_node_dec_free (ni);
484     IEEE80211_NODE_UNLOCK (nt);
485 }
486
487 void
488 wlan_node_remove_core (struct ieee80211_node_table *nt, bss_t *ni)
489 {
490     if(ni->ni_list_prev == NULL)
491     {
492         /* First in list so fix the list head */
493         nt->nt_node_first = ni->ni_list_next;
494     }
495     else
496     {
497         ni->ni_list_prev->ni_list_next = ni->ni_list_next;
498     }
499
500     if(ni->ni_list_next == NULL)
501     {
502         /* Last in list so fix list tail */
503         nt->nt_node_last = ni->ni_list_prev;
504     }
505     else
506     {
507         ni->ni_list_next->ni_list_prev = ni->ni_list_prev;
508     }
509
510     if(ni->ni_hash_prev == NULL)
511     {
512         /* First in list so fix the list head */
513         int hash;
514         hash = IEEE80211_NODE_HASH(ni->ni_macaddr);
515         nt->nt_hash[hash] = ni->ni_hash_next;
516     }
517     else
518     {
519         ni->ni_hash_prev->ni_hash_next = ni->ni_hash_next;
520     }
521
522     if(ni->ni_hash_next != NULL)
523     {
524         ni->ni_hash_next->ni_hash_prev = ni->ni_hash_prev;
525     }
526 }
527
528 bss_t *
529 wlan_node_remove(struct ieee80211_node_table *nt, A_UINT8 *bssid)
530 {
531     bss_t *bss, *nextBss;
532
533     IEEE80211_NODE_LOCK(nt);
534
535     bss = nt->nt_node_first;
536
537     while (bss != NULL)
538     {
539         nextBss = bss->ni_list_next;
540
541         if (A_MEMCMP(bssid, bss->ni_macaddr, 6) == 0)
542         {
543             wlan_node_remove_core (nt, bss);
544             IEEE80211_NODE_UNLOCK(nt);
545             return bss;
546         }
547
548         bss = nextBss;
549     }
550
551     IEEE80211_NODE_UNLOCK(nt);
552     return NULL;
553 }
554
555 bss_t *
556 wlan_find_matching_Ssidnode (struct ieee80211_node_table *nt, A_UCHAR *pSsid,
557                     A_UINT32 ssidLength, A_UINT32 dot11AuthMode, A_UINT32 authMode,
558                    A_UINT32 pairwiseCryptoType, A_UINT32 grpwiseCryptoTyp)
559 {
560     bss_t   *ni = NULL;
561     bss_t   *best_ni = NULL;
562     A_UCHAR *pIESsid = NULL;
563
564     IEEE80211_NODE_LOCK (nt);
565
566     for (ni = nt->nt_node_first; ni; ni = ni->ni_list_next) {
567         pIESsid = ni->ni_cie.ie_ssid;
568         if (pIESsid[1] <= 32) {
569
570             // Step 1 : Check SSID
571             if (0x00 == memcmp (pSsid, &pIESsid[2], ssidLength)) {
572
573                 if (ni->ni_cie.ie_capInfo & 0x10)
574                 {
575
576                     if ((NULL != ni->ni_cie.ie_rsn) && (WPA2_PSK_AUTH == authMode))
577                     {
578                         /* WPA2 */
579                         if (NULL == best_ni)
580                         {
581                             best_ni = ni;
582                         }
583                         else if (ni->ni_rssi > best_ni->ni_rssi)
584                         {
585                             best_ni = ni;
586                         }
587                     }
588                     else if ((NULL != ni->ni_cie.ie_wpa) && (WPA_PSK_AUTH == authMode))
589                     {
590                         /* WPA */
591                         if (NULL == best_ni)
592                         {
593                             best_ni = ni;
594                         }
595                         else if (ni->ni_rssi > best_ni->ni_rssi)
596                         {
597                             best_ni = ni;
598                         }
599                     }
600                     else if (WEP_CRYPT == pairwiseCryptoType)
601                     {
602                         /* WEP */
603                         if (NULL == best_ni)
604                         {
605                             best_ni = ni;
606                         }
607                         else if (ni->ni_rssi > best_ni->ni_rssi)
608                         {
609                             best_ni = ni;
610                         }
611                     }
612                 }
613                 else
614                 {
615                     /* open AP */
616                     if ((OPEN_AUTH == authMode) && (NONE_CRYPT == pairwiseCryptoType))
617                     {
618                         if (NULL == best_ni)
619                         {
620                             best_ni = ni;
621                         }
622                         else if (ni->ni_rssi > best_ni->ni_rssi)
623                         {
624                             best_ni = ni;
625                         }
626                     }
627                 }
628             }
629         }
630     }
631
632     IEEE80211_NODE_UNLOCK (nt);
633
634     return best_ni;
635 }
636