b12484597f8f1239919e045b34e1cd6ab5904c92
[openwrt.git] / target / linux / s3c24xx / files-2.6.31 / drivers / ar6000 / wlan / wlan_node.c
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
50 static void wlan_node_timeout(A_ATH_TIMER arg);
51 static bss_t * _ieee80211_find_node(struct ieee80211_node_table *nt,
52                                     const A_UINT8 *macaddr);
53
54 bss_t *
55 wlan_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
87 void
88 wlan_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
96 void
97 wlan_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
138 static 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
157 bss_t *
158 wlan_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  */
173 void
174 wlan_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
219 static void
220 wlan_node_dec_free(bss_t *ni)
221 {
222     if (ieee80211_node_dectestref(ni)) {
223         wlan_node_free(ni);
224     }
225 }
226
227 void
228 wlan_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
237 void
238 wlan_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  */
261 void
262 wlan_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
279 static void
280 wlan_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
320 void
321 wlan_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
329 bss_t *
330 wlan_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
365 void
366 wlan_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 }