1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16/*-------------------------------------------------------------------*/
17#include "includes.h"
18#include "scanmerge.h"
19#include "shlist.h"
20
21#define IS_HIDDEN_AP(a)	(((a)->ssid_len == 0) || ((a)->ssid[0] == '\0'))
22
23scan_ssid_t *scan_get_ssid( scan_result_t *res_ptr )
24{
25    static scan_ssid_t ssid_temp;
26#ifdef WPA_SUPPLICANT_VER_0_6_X
27    const u8 *res_ie;
28
29    res_ie = wpa_scan_get_ie(res_ptr, WLAN_EID_SSID);
30    if (!res_ie)
31        return NULL;
32    ssid_temp.ssid_len = (size_t)res_ie[1];
33    os_memcpy(ssid_temp.ssid, (res_ie + 2), ssid_temp.ssid_len);
34#else
35    ssid_temp.ssid_len = res_ptr->ssid_len;
36    os_memcpy(ssid_temp.ssid, res_ptr->ssid, ssid_temp.ssid_len);
37#endif
38    return &ssid_temp;
39}
40
41/*-----------------------------------------------------------------------------
42Routine Name: scan_init
43Routine Description: Inits scan merge list
44Arguments:
45   mydrv   - pointer to private driver data structure
46Return Value:
47-----------------------------------------------------------------------------*/
48void scan_init( struct wpa_driver_ti_data *mydrv )
49{
50    mydrv->last_scan = -1;
51    shListInitList(&(mydrv->scan_merge_list));
52}
53
54/*-----------------------------------------------------------------------------
55Routine Name: scan_free
56Routine Description: Frees scan structure private data
57Arguments:
58   ptr - pointer to private data structure
59Return Value:
60-----------------------------------------------------------------------------*/
61static void scan_free( void *ptr )
62{
63    os_free(ptr);
64}
65
66/*-----------------------------------------------------------------------------
67Routine Name: scan_exit
68Routine Description: Cleans scan merge list
69Arguments:
70   mydrv   - pointer to private driver data structure
71Return Value:
72-----------------------------------------------------------------------------*/
73void scan_exit( struct wpa_driver_ti_data *mydrv )
74{
75    shListDelAllItems(&(mydrv->scan_merge_list), scan_free);
76}
77
78/*-----------------------------------------------------------------------------
79Routine Name: scan_count
80Routine Description: Gives number of list elements
81Arguments:
82   mydrv   - pointer to private driver data structure
83Return Value: Number of elements in the list
84-----------------------------------------------------------------------------*/
85unsigned long scan_count( struct wpa_driver_ti_data *mydrv )
86{
87    return shListGetCount(&(mydrv->scan_merge_list));
88}
89
90/*-----------------------------------------------------------------------------
91Routine Name: scan_equal
92Routine Description: Compares bssid of scan result and scan merge structure
93Arguments:
94   val   - pointer to scan result structure
95   idata - pointer to scan merge structure
96Return Value: 1 - if equal, 0 - if not
97-----------------------------------------------------------------------------*/
98static int scan_equal( void *val,  void *idata )
99{
100    scan_ssid_t n_ssid, l_ssid, *p_ssid;
101    scan_result_t *new_res = (scan_result_t *)val;
102    scan_result_t *lst_res =
103               (scan_result_t *)(&(((scan_merge_t *)idata)->scanres));
104    int ret;
105    size_t len;
106
107    p_ssid = scan_get_ssid(new_res);
108    if (!p_ssid)
109        return 0;
110    os_memcpy(&n_ssid, p_ssid, sizeof(scan_ssid_t));
111    p_ssid = scan_get_ssid(lst_res);
112    if (!p_ssid)
113        return 0;
114    os_memcpy(&l_ssid, p_ssid, sizeof(scan_ssid_t));
115
116    len = (IS_HIDDEN_AP(&n_ssid) || IS_HIDDEN_AP(&l_ssid)) ?
117          0 : n_ssid.ssid_len;
118    ret = ((l_ssid.ssid_len != n_ssid.ssid_len) && (len != 0)) ||
119          (os_memcmp(new_res->bssid, lst_res->bssid, ETH_ALEN) ||
120           os_memcmp(n_ssid.ssid, l_ssid.ssid, len));
121    return !ret;
122}
123
124/*-----------------------------------------------------------------------------
125Routine Name: copy_scan_res
126Routine Description: copies scan result structure to scan merge list item
127Arguments:
128   dst - pointer to scan result structure in the list
129   src - source pointer to scan result structure
130Return Value: NONE
131-----------------------------------------------------------------------------*/
132void copy_scan_res( scan_result_t *dst, scan_result_t *src )
133{
134#ifdef WPA_SUPPLICANT_VER_0_5_X
135    if( IS_HIDDEN_AP(src) ) {
136        os_memcpy(src->ssid, dst->ssid, dst->ssid_len);
137        src->ssid_len = dst->ssid_len;
138    }
139#endif
140    os_memcpy(dst, src, sizeof(scan_result_t));
141}
142
143/*-----------------------------------------------------------------------------
144Routine Name: scan_add
145Routine Description: adds scan result structure to scan merge list
146Arguments:
147   head    - pointer to scan merge list head
148   res_ptr - pointer to scan result structure
149Return Value: Pointer to scan merge item
150-----------------------------------------------------------------------------*/
151static scan_merge_t *scan_add( SHLIST *head, scan_result_t *res_ptr )
152{
153    scan_merge_t *scan_ptr;
154    unsigned size = 0;
155
156#ifdef WPA_SUPPLICANT_VER_0_6_X
157    size += res_ptr->ie_len;
158#endif
159    scan_ptr = (scan_merge_t *)os_malloc(sizeof(scan_merge_t) + size);
160    if( !scan_ptr )
161        return( NULL );
162    os_memcpy(&(scan_ptr->scanres), res_ptr, sizeof(scan_result_t) + size);
163    scan_ptr->count = SCAN_MERGE_COUNT;
164    shListInsLastItem(head, (void *)scan_ptr);
165    return scan_ptr;
166}
167
168/*-----------------------------------------------------------------------------
169Routine Name: scan_find
170Routine Description: Looks for scan merge item in scan results array
171Arguments:
172   scan_ptr - pointer to scan merge item
173   results - pointer to scan results array
174   number_items - current number of items
175Return Value: 1 - if item was found, 0 - otherwise
176-----------------------------------------------------------------------------*/
177static int scan_find( scan_merge_t *scan_ptr, scan_result_t *results,
178                      unsigned int number_items )
179{
180    unsigned int i;
181
182    for(i=0;( i < number_items );i++) {
183        if( scan_equal(&(results[i]), scan_ptr) )
184            return 1;
185    }
186    return 0;
187}
188
189#ifdef WPA_SUPPLICANT_VER_0_6_X
190/*-----------------------------------------------------------------------------
191Routine Name: scan_dup
192Routine Description: Create copy of scan results entry
193Arguments:
194   res_ptr - pointer to scan result item
195Return Value: pointer to new scan result item, or NULL
196-----------------------------------------------------------------------------*/
197static scan_result_t *scan_dup( scan_result_t *res_ptr )
198{
199    unsigned size;
200    scan_result_t *new_ptr;
201
202    if (!res_ptr)
203        return NULL;
204
205    size = sizeof(scan_result_t) + res_ptr->ie_len;
206    new_ptr = os_malloc(size);
207    if (!new_ptr)
208        return NULL;
209    if (res_ptr) {
210        os_memcpy(new_ptr, res_ptr, size);
211    }
212    return new_ptr;
213}
214#endif
215
216/*-----------------------------------------------------------------------------
217Routine Name: scan_merge
218Routine Description: Merges current scan results with previous
219Arguments:
220   mydrv   - pointer to private driver data structure
221   results - pointer to scan results array
222   number_items - current number of items
223   max_size - maximum namber of items
224Return Value: Merged number of items
225-----------------------------------------------------------------------------*/
226#ifdef WPA_SUPPLICANT_VER_0_6_X
227unsigned int scan_merge( struct wpa_driver_ti_data *mydrv,
228                         scan_result_t **results, int force_flag,
229                         unsigned int number_items, unsigned int max_size )
230#else
231unsigned int scan_merge( struct wpa_driver_ti_data *mydrv,
232                         scan_result_t *results, int force_flag,
233                         unsigned int number_items, unsigned int max_size )
234#endif
235{
236    SHLIST *head = &(mydrv->scan_merge_list);
237    SHLIST *item, *del_item;
238    scan_result_t *res_ptr;
239    scan_merge_t *scan_ptr;
240    unsigned int i;
241
242    /* Prepare items for removal */
243    item = shListGetFirstItem(head);
244    while( item != NULL ) {
245        scan_ptr = (scan_merge_t *)(item->data);
246        if( scan_ptr->count != 0 )
247            scan_ptr->count--;
248        item = shListGetNextItem(head, item);
249    }
250
251    for(i=0;( i < number_items );i++) { /* Find/Add new items */
252#ifdef WPA_SUPPLICANT_VER_0_6_X
253        res_ptr = results[i];
254#else
255        res_ptr = &(results[i]);
256#endif
257        item = shListFindItem( head, res_ptr, scan_equal );
258        if( item ) {
259#ifdef WPA_SUPPLICANT_VER_0_6_X
260            scan_ssid_t *p_ssid;
261            scan_result_t *new_ptr;
262#endif
263            scan_ptr = (scan_merge_t *)(item->data);
264            copy_scan_res(&(scan_ptr->scanres), res_ptr);
265            scan_ptr->count = SCAN_MERGE_COUNT;
266#ifdef WPA_SUPPLICANT_VER_0_6_X
267	    p_ssid = scan_get_ssid(res_ptr);
268            if (p_ssid && IS_HIDDEN_AP(p_ssid)) {
269                new_ptr = scan_dup(res_ptr);
270                if (new_ptr) {
271                    results[i] = new_ptr;
272                    os_free(res_ptr);
273                }
274            }
275#endif
276        }
277        else {
278            scan_add(head, res_ptr);
279        }
280    }
281
282    item = shListGetFirstItem( head );  /* Add/Remove missing items */
283    while( item != NULL ) {
284        del_item = NULL;
285        scan_ptr = (scan_merge_t *)(item->data);
286        if( scan_ptr->count != SCAN_MERGE_COUNT ) {
287            if( !force_flag && ((scan_ptr->count == 0) ||
288                (mydrv->last_scan == SCAN_TYPE_NORMAL_ACTIVE)) ) {
289                del_item = item;
290            }
291            else {
292                if( number_items < max_size ) {
293#ifdef WPA_SUPPLICANT_VER_0_6_X
294                    res_ptr = scan_dup(&(scan_ptr->scanres));
295                    if (res_ptr) {
296                        results[number_items] = res_ptr;
297                        number_items++;
298                    }
299#else
300                    os_memcpy(&(results[number_items]),
301                          &(scan_ptr->scanres), sizeof(scan_result_t));
302                    number_items++;
303#endif
304                }
305            }
306        }
307        item = shListGetNextItem(head, item);
308        shListDelItem(head, del_item, scan_free);
309    }
310
311    return( number_items );
312}
313
314/*-----------------------------------------------------------------------------
315Routine Name: scan_get_by_bssid
316Routine Description: Gets scan_result pointer to item by bssid
317Arguments:
318   mydrv   - pointer to private driver data structure
319   bssid   - pointer to bssid value
320Return Value: pointer to scan_result item
321-----------------------------------------------------------------------------*/
322scan_result_t *scan_get_by_bssid( struct wpa_driver_ti_data *mydrv, u8 *bssid )
323{
324    SHLIST *head = &(mydrv->scan_merge_list);
325    SHLIST *item;
326    scan_result_t *cur_res;
327    scan_ssid_t *p_ssid;
328
329    item = shListGetFirstItem(head);
330    if( item == NULL )
331        return( NULL );
332    do {
333        cur_res = (scan_result_t *)&(((scan_merge_t *)(item->data))->scanres);
334        p_ssid = scan_get_ssid(cur_res);
335        if( (!os_memcmp(cur_res->bssid, bssid, ETH_ALEN)) &&
336            (!IS_HIDDEN_AP(p_ssid)) ) {
337            return( cur_res );
338        }
339        item = shListGetNextItem(head, item);
340    } while( item != NULL );
341
342    return( NULL );
343}
344