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