Merge branch 'sh-latest' of git://git.kernel.org/pub/scm/linux/kernel/git/lethal...
[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_osapi.h>
28 #define ATH_MODULE_NAME wlan
29 #include <a_debug.h>
30 #include "htc.h"
31 #include "htc_api.h"
32 #include <wmi.h>
33 #include <ieee80211.h>
34 #include <wlan_api.h>
35 #include <wmi_api.h>
36 #include <ieee80211_node.h>
37
38 #define ATH_DEBUG_WLAN ATH_DEBUG_MAKE_MODULE_MASK(0)
39
40 #ifdef ATH_DEBUG_MODULE
41
42 static struct ath_debug_mask_description wlan_debug_desc[] = {
43     { ATH_DEBUG_WLAN , "General WLAN Node Tracing"},
44 };
45
46 ATH_DEBUG_INSTANTIATE_MODULE_VAR(wlan,
47                                  "wlan",
48                                  "WLAN Node Management",
49                                  ATH_DEBUG_MASK_DEFAULTS,
50                                  ATH_DEBUG_DESCRIPTION_COUNT(wlan_debug_desc),
51                                  wlan_debug_desc);
52                                  
53 #endif
54
55 #ifdef THREAD_X
56 static void wlan_node_timeout(unsigned long arg);
57 #endif
58
59 static bss_t * _ieee80211_find_node (struct ieee80211_node_table *nt,
60                                      const u8 *macaddr);
61
62 bss_t *
63 wlan_node_alloc(struct ieee80211_node_table *nt, int wh_size)
64 {
65     bss_t *ni;
66
67     ni = A_MALLOC_NOWAIT(sizeof(bss_t));
68
69     if (ni != NULL) {
70         if (wh_size)
71         {
72         ni->ni_buf = A_MALLOC_NOWAIT(wh_size);
73         if (ni->ni_buf == NULL) {
74             kfree(ni);
75             ni = NULL;
76             return ni;
77         }
78         }
79     } else {
80         return ni;
81     }
82
83     /* Make sure our lists are clean */
84     ni->ni_list_next = NULL;
85     ni->ni_list_prev = NULL;
86     ni->ni_hash_next = NULL;
87     ni->ni_hash_prev = NULL;
88
89     //
90     // ni_scangen never initialized before and during suspend/resume of winmobile,
91     // that some junk has been stored in this, due to this scan list didn't properly updated
92     //
93     ni->ni_scangen   = 0;
94
95 #ifdef OS_ROAM_MANAGEMENT
96     ni->ni_si_gen    = 0;
97 #endif
98
99     return ni;
100 }
101
102 void
103 wlan_node_free(bss_t *ni)
104 {
105     if (ni->ni_buf != NULL) {
106         kfree(ni->ni_buf);
107     }
108     kfree(ni);
109 }
110
111 void
112 wlan_setup_node(struct ieee80211_node_table *nt, bss_t *ni,
113                 const u8 *macaddr)
114 {
115     int hash;
116     u32 timeoutValue = 0;
117
118     memcpy(ni->ni_macaddr, macaddr, IEEE80211_ADDR_LEN);
119     hash = IEEE80211_NODE_HASH (macaddr);
120     ieee80211_node_initref (ni);     /* mark referenced */
121
122     timeoutValue = nt->nt_nodeAge;
123
124     ni->ni_tstamp = A_GET_MS (0);
125     ni->ni_actcnt = WLAN_NODE_INACT_CNT;
126
127     IEEE80211_NODE_LOCK_BH(nt);
128
129     /* Insert at the end of the node list */
130     ni->ni_list_next = NULL;
131     ni->ni_list_prev = nt->nt_node_last;
132     if(nt->nt_node_last != NULL)
133     {
134         nt->nt_node_last->ni_list_next = ni;
135     }
136     nt->nt_node_last = ni;
137     if(nt->nt_node_first == NULL)
138     {
139         nt->nt_node_first = ni;
140     }
141
142     /* Insert into the hash list i.e. the bucket */
143     if((ni->ni_hash_next = nt->nt_hash[hash]) != NULL)
144     {
145         nt->nt_hash[hash]->ni_hash_prev = ni;
146     }
147     ni->ni_hash_prev = NULL;
148     nt->nt_hash[hash] = ni;
149
150 #ifdef THREAD_X
151     if (!nt->isTimerArmed) {
152         A_TIMEOUT_MS(&nt->nt_inact_timer, timeoutValue, 0);
153         nt->isTimerArmed = true;
154     }
155 #endif
156
157     IEEE80211_NODE_UNLOCK_BH(nt);
158 }
159
160 static bss_t *
161 _ieee80211_find_node(struct ieee80211_node_table *nt,
162     const u8 *macaddr)
163 {
164     bss_t *ni;
165     int hash;
166
167     IEEE80211_NODE_LOCK_ASSERT(nt);
168
169     hash = IEEE80211_NODE_HASH(macaddr);
170     for(ni = nt->nt_hash[hash]; ni; ni = ni->ni_hash_next) {
171         if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) {
172             ieee80211_node_incref(ni);  /* mark referenced */
173             return ni;
174         }
175     }
176     return NULL;
177 }
178
179 bss_t *
180 wlan_find_node(struct ieee80211_node_table *nt, const u8 *macaddr)
181 {
182     bss_t *ni;
183
184     IEEE80211_NODE_LOCK(nt);
185     ni = _ieee80211_find_node(nt, macaddr);
186     IEEE80211_NODE_UNLOCK(nt);
187     return ni;
188 }
189
190 /*
191  * Reclaim a node.  If this is the last reference count then
192  * do the normal free work.  Otherwise remove it from the node
193  * table and mark it gone by clearing the back-reference.
194  */
195 void
196 wlan_node_reclaim(struct ieee80211_node_table *nt, bss_t *ni)
197 {
198     IEEE80211_NODE_LOCK(nt);
199
200     if(ni->ni_list_prev == NULL)
201     {
202         /* First in list so fix the list head */
203         nt->nt_node_first = ni->ni_list_next;
204     }
205     else
206     {
207         ni->ni_list_prev->ni_list_next = ni->ni_list_next;
208     }
209
210     if(ni->ni_list_next == NULL)
211     {
212         /* Last in list so fix list tail */
213         nt->nt_node_last = ni->ni_list_prev;
214     }
215     else
216     {
217         ni->ni_list_next->ni_list_prev = ni->ni_list_prev;
218     }
219
220     if(ni->ni_hash_prev == NULL)
221     {
222         /* First in list so fix the list head */
223         int hash;
224         hash = IEEE80211_NODE_HASH(ni->ni_macaddr);
225         nt->nt_hash[hash] = ni->ni_hash_next;
226     }
227     else
228     {
229         ni->ni_hash_prev->ni_hash_next = ni->ni_hash_next;
230     }
231
232     if(ni->ni_hash_next != NULL)
233     {
234         ni->ni_hash_next->ni_hash_prev = ni->ni_hash_prev;
235     }
236     wlan_node_free(ni);
237
238     IEEE80211_NODE_UNLOCK(nt);
239 }
240
241 static void
242 wlan_node_dec_free(bss_t *ni)
243 {
244     if (ieee80211_node_dectestref(ni)) {
245         wlan_node_free(ni);
246     }
247 }
248
249 void
250 wlan_free_allnodes(struct ieee80211_node_table *nt)
251 {
252     bss_t *ni;
253
254     while ((ni = nt->nt_node_first) != NULL) {
255         wlan_node_reclaim(nt, ni);
256     }
257 }
258
259 void
260 wlan_iterate_nodes(struct ieee80211_node_table *nt, wlan_node_iter_func *f,
261                    void *arg)
262 {
263     bss_t *ni;
264     u32 gen;
265
266     gen = ++nt->nt_scangen;
267
268     IEEE80211_NODE_LOCK(nt);
269     for (ni = nt->nt_node_first; ni; ni = ni->ni_list_next) {
270         if (ni->ni_scangen != gen) {
271             ni->ni_scangen = gen;
272             (void) ieee80211_node_incref(ni);
273             (*f)(arg, ni);
274             wlan_node_dec_free(ni);
275         }
276     }
277     IEEE80211_NODE_UNLOCK(nt);
278 }
279
280 /*
281  * Node table support.
282  */
283 void
284 wlan_node_table_init(void *wmip, struct ieee80211_node_table *nt)
285 {
286     int i;
287
288     AR_DEBUG_PRINTF(ATH_DEBUG_WLAN, ("node table = 0x%lx\n", (unsigned long)nt));
289     IEEE80211_NODE_LOCK_INIT(nt);
290
291     A_REGISTER_MODULE_DEBUG_INFO(wlan);
292     
293     nt->nt_node_first = nt->nt_node_last = NULL;
294     for(i = 0; i < IEEE80211_NODE_HASHSIZE; i++)
295     {
296         nt->nt_hash[i] = NULL;
297     }
298
299 #ifdef THREAD_X
300     A_INIT_TIMER(&nt->nt_inact_timer, wlan_node_timeout, nt);
301     nt->isTimerArmed = false;
302 #endif
303     nt->nt_wmip = wmip;
304     nt->nt_nodeAge = WLAN_NODE_INACT_TIMEOUT_MSEC;
305
306     //
307     // nt_scangen never initialized before and during suspend/resume of winmobile, 
308     // that some junk has been stored in this, due to this scan list didn't properly updated
309     //
310     nt->nt_scangen   = 0;
311
312 #ifdef OS_ROAM_MANAGEMENT
313     nt->nt_si_gen    = 0;
314 #endif
315 }
316
317 void
318 wlan_set_nodeage(struct ieee80211_node_table *nt, u32 nodeAge)
319 {
320     nt->nt_nodeAge = nodeAge;
321     return;
322 }
323 void
324 wlan_refresh_inactive_nodes (struct ieee80211_node_table *nt)
325 {
326 #ifdef THREAD_X
327     bss_t *bss, *nextBss;
328     u8 myBssid[IEEE80211_ADDR_LEN], reArmTimer = false;
329
330     wmi_get_current_bssid(nt->nt_wmip, myBssid);
331
332     bss = nt->nt_node_first;
333     while (bss != NULL)
334     {
335         nextBss = bss->ni_list_next;
336         if (memcmp(myBssid, bss->ni_macaddr, sizeof(myBssid)) != 0)
337         {
338                /*
339                 * free up all but the current bss - if set
340                 */
341                 wlan_node_reclaim(nt, bss);
342
343         }
344         bss = nextBss;
345     }
346 #else
347     bss_t *bss, *nextBss;
348     u8 myBssid[IEEE80211_ADDR_LEN];
349     u32 timeoutValue = 0;
350     u32 now = A_GET_MS(0);
351     timeoutValue = nt->nt_nodeAge;
352
353     wmi_get_current_bssid(nt->nt_wmip, myBssid);
354
355     bss = nt->nt_node_first;
356     while (bss != NULL)
357     {
358         nextBss = bss->ni_list_next;
359         if (memcmp(myBssid, bss->ni_macaddr, sizeof(myBssid)) != 0)
360         {
361
362             if (((now - bss->ni_tstamp) > timeoutValue)  || --bss->ni_actcnt == 0)
363             {
364                /*
365                 * free up all but the current bss - if set
366                 */
367                 wlan_node_reclaim(nt, bss);
368             }
369         }
370         bss = nextBss;
371     }
372 #endif
373 }
374
375 #ifdef THREAD_X
376 static void
377 wlan_node_timeout (unsigned long arg)
378 {
379     struct ieee80211_node_table *nt = (struct ieee80211_node_table *)arg;
380     bss_t *bss, *nextBss;
381     u8 myBssid[IEEE80211_ADDR_LEN], reArmTimer = false;
382     u32 timeoutValue = 0;
383     u32 now = A_GET_MS(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 (memcmp(myBssid, bss->ni_macaddr, sizeof(myBssid)) != 0)
394         {
395
396             if ((now - bss->ni_tstamp) > timeoutValue)
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, u8 *pSsid,
435                     u32 ssidLength, bool bIsWPA2, bool bMatchSSID)
436 {
437     bss_t   *ni = NULL;
438     u8 *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, u8 *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 (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, u8 *pSsid,
557                     u32 ssidLength, u32 dot11AuthMode, u32 authMode,
558                    u32 pairwiseCryptoType, u32 grpwiseCryptoTyp)
559 {
560     bss_t   *ni = NULL;
561     bss_t   *best_ni = NULL;
562     u8 *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