source: arduino-1-6-7/trunk/fuentes/arduino-ide-amd64/hardware/arduino/avr/firmwares/wifishield/wifiHD/src/wl_cm.c @ 4837

Last change on this file since 4837 was 4837, checked in by daduve, 2 years ago

Adding new version

File size: 12.5 KB
Line 
1/* This source file is part of the ATMEL AVR-UC3-SoftwareFramework-1.7.0 Release */
2
3/*! \page License
4 * Copyright (C) 2009, H&D Wireless AB All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * 3. The name of H&D Wireless AB may not be used to endorse or promote products derived
17 * from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY H&D WIRELESS AB ``AS IS'' AND ANY EXPRESS OR IMPLIED
20 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY AND
22 * SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT,
23 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30#include "wl_cm.h"
31#include "util.h"
32#include <string.h>
33#include "debug.h"
34
35/** Roaming configuration parameters **/
36
37/*! The ROAMING_RSSI_THRESHOLD setting defines how bad the current
38 *  signal strength should be before we'll consider roaming to an AP
39 *  with better signal strength. The objective is to stay on the
40 *  current AP as long as the RSSI is decent, even if there are other
41 *  APs in the same BSS with better RSSI available. 
42 *  If ROAMING_RSSI_THRESHOLD is too high we might roam unecessarily. 
43 *  If ROAMING_RSSI_THRESHOLD is too low we might not roam in time to
44 *  avoid packet loss. This also impacts power consumption, staying
45 *  too long with an AP with poor RSSI will consume more power.
46 *  Unit is dBm.
47 */
48#define ROAMING_RSSI_THRESHOLD -65
49
50/*! The ROAMING_RSSI_DIFF setting defines how much better
51 *  than the currently associated AP a new AP must be before
52 *  we'll attempt to roam over to the new AP.
53 *  If ROAMING_RSSI_DIFF is too high it might be too hard
54 *  to roam (important if the STA is expected to move
55 *  quickly through different AP coverage areas).
56 *  If ROAMING_RSSI_DIFF is too low we might bounce between
57 *  two APs with similar signal strengths.
58 *  Unit is dBm.
59 */
60#define ROAMING_RSSI_DIFF 10
61
62# include "printf-stdarg.h"
63#include "ard_utils.h"
64#include "debug.h"
65
66/** \defgroup wl_cm Connection Manager
67 *
68 * These functions are used to configure and control the WiFi connetion
69 * manager.
70 *
71 *
72 *  @{
73 */
74
75struct cm_candidate {
76        struct wl_ssid_t ssid;
77        struct wl_mac_addr_t bssid;
78};
79
80struct cm {
81        cm_scan_cb_t *scan_cb;
82        cm_conn_cb_t *conn_cb;
83        cm_disconn_cb_t *disconn_cb;
84        void* ctx;
85        uint8_t enabled;
86        struct cm_candidate candidate;
87};
88
89
90/**
91 * This function can be modified to pick a network based on
92 * application specific criteria.
93 *
94 * If the SSID can not be found in the scan list it will be
95 * assumed to be a hidden SSID and the wl_connect() command
96 * will be called to attempt to probe for the network and
97 * connect to it.
98 */
99static struct wl_network_t*
100find_best_candidate(struct cm* cm)
101{
102        struct wl_network_list_t* netlist;
103        struct wl_network_t *best_net = NULL;
104        uint8_t i;
105       
106        if (wl_get_network_list(&netlist) != WL_SUCCESS)
107                return NULL;
108       
109        if (netlist->cnt == 0)
110                return NULL;
111
112        for (i = 0; i < netlist->cnt; i++) {
113                /* match on ssid */
114                if (cm->candidate.ssid.len)
115                        if (!equal_ssid(&cm->candidate.ssid, 
116                                        &netlist->net[i]->ssid))
117                                continue;
118
119                /* match bssid */
120                if (strncmp((char*) cm->candidate.bssid.octet, 
121                            "\xff\xff\xff\xff\xff\xff", 6))
122                        if (!equal_bssid(&cm->candidate.bssid, 
123                                         &netlist->net[i]->bssid))
124                                continue;
125                /* check for best rssi. */
126                if ( best_net && 
127                     ( best_net->rssi > netlist->net[i]->rssi) ) {
128                        continue;
129                }
130                best_net = netlist->net[i];
131        }
132
133        return best_net;
134}
135
136
137/**
138 *
139 */
140static void
141select_net(struct cm* cm)
142{
143        struct wl_network_t *candidate_net;
144        struct wl_network_t *current_net;
145        struct wl_ssid_t *ssid_p;
146
147        int ret;
148
149        /* Nothing to do */
150        if (0 == cm->candidate.ssid.len) {
151                return;
152        }
153       
154        current_net = wl_get_current_network();
155        candidate_net = find_best_candidate(cm);
156
157        /* Connected to the candidate? ... */
158        if ( current_net == candidate_net ) {
159                if ( current_net ) {
160                        /* ...yes, dont change. */
161                       
162                        return;
163                }
164        }
165
166        /* Roaming checks */
167        if (current_net && candidate_net) {
168                /* Are we changing BSSs? */
169                if ( equal_ssid(&candidate_net->ssid, 
170                                &current_net->ssid)) {
171
172                        /* ...no. Does the currently connected
173                         * net have a decent RSSI?...*/
174                        if ( current_net->rssi > ROAMING_RSSI_THRESHOLD ) {
175                                /* ...yes, stay with it. */
176                                return;
177                        }
178                        /* ...no. Does the candidate have
179                         * sufficiently better RSSI to
180                         * motivate a switch to it? */
181                        if ( candidate_net->rssi < current_net->rssi + 
182                             ROAMING_RSSI_DIFF) {
183                                return;
184                        }
185                        /* ...yes, try to roam to candidate_net */
186                        CM_DPRINTF("CM: Roaming from rssi %d to %d\n",
187                                   current_net->rssi,
188                                   candidate_net->rssi);
189                }
190        }
191        /* a candidate is found */
192        if (candidate_net) {
193                /* We connect to a specific bssid here because
194                 * find_best_candidate() might have picked a
195                 * particulare AP among many with the same SSID.
196                 * wl_connect() would pick one of them at random.
197                 */
198                ret = wl_connect_bssid(candidate_net->bssid);
199        }
200        /* no candidate found */
201        else {
202                CM_DPRINTF("CM: No candidate found for ssid \"%s\"\n",
203                           ssid2str(&cm->candidate.ssid));
204                /* Might be a hidden SSID so we try to connect to it.
205                 * wl_connect() will trigger a directed scan
206                 * for the SSID in this case.
207                 */
208                ssid_p = &cm->candidate.ssid;
209                ret = wl_connect(ssid_p->ssid, ssid_p->len);
210        }
211        switch (ret) {
212        case WL_SUCCESS :
213                return;
214        case WL_BUSY:
215                wl_disconnect();
216                return;
217        case WL_RETRY:
218                break;
219        default :
220                CM_DPRINTF("CM: failed to connect\n");
221                break;
222        } 
223               
224        /* some operation failed or no candidate found */
225        if (wl_scan() != WL_SUCCESS)
226                CM_DPRINTF("CM: failed to scan\n");               
227}
228
229
230/**
231 *
232 */
233static void 
234wl_scan_complete_cb(void* ctx)
235{
236        struct cm *cm = ctx;
237
238        CM_DPRINTF("CM: scan completed\n");
239
240        if (cm->scan_cb)
241                cm->scan_cb(cm->ctx);
242
243        if ( 0 == cm->enabled ) {
244                return;
245        }
246        select_net(cm);
247}
248
249/**
250 *
251 */
252static void 
253wl_media_connected_cb(void* ctx)
254{
255        struct cm *cm = ctx;
256        struct wl_network_t *net = wl_get_current_network();
257        CM_DPRINTF("CM: connected to %s\n", ssid2str(&net->ssid));
258        LINK_LED_ON();
259        ERROR_LED_OFF();
260        if (cm->conn_cb)
261                cm->conn_cb(net, cm->ctx);
262}
263
264
265/**
266 *
267 */
268static void 
269wl_conn_failure_cb(void* ctx)
270{
271        struct cm *cm = ctx;
272        CM_DPRINTF("CM: connect failed, scanning\n");
273        ERROR_LED_ON();
274        LINK_LED_OFF();
275       
276        if ( 0 == cm->enabled ) {
277                return;
278        }
279        if (wl_scan() != WL_SUCCESS)
280                /* should never happen */
281                CM_DPRINTF("CM: could not start scan after connect fail!\n");
282}
283
284
285/**
286 *
287 */
288static void 
289wl_conn_lost_cb(void* ctx)
290{
291        struct cm *cm = ctx;
292        CM_DPRINTF("CM: connection lost, scanning\n");
293        LINK_LED_OFF();
294        if (cm->disconn_cb)
295                cm->disconn_cb(cm->ctx);
296
297        if ( 0 == cm->enabled ) {
298                return;
299        }
300        if (wl_scan() != WL_SUCCESS)
301                /* should never happen */
302                CM_DPRINTF("CM: could not start scan after connect lost!\n");
303}
304
305
306/**
307 *
308 */
309static void
310wl_event_cb(struct wl_event_t event, void* ctx)
311{
312        struct cm *cm = ctx;
313
314        switch (event.id) {
315        case WL_EVENT_MEDIA_CONNECTED:
316                wl_media_connected_cb(cm);
317                break;
318               
319        case WL_EVENT_CONN_FAILURE:
320                wl_conn_failure_cb(cm);
321                break;
322               
323        case WL_EVENT_MEDIA_DISCONNECTED:
324                CM_DPRINTF("CM: disconnected\n");
325                wl_conn_lost_cb(cm);
326                break;
327
328        case WL_EVENT_SCAN_COMPLETE:
329                wl_scan_complete_cb(cm);
330                break;
331
332        default:
333                CM_DPRINTF("CM: unhandled event\n");
334        };
335}
336
337static struct cm *cm = NULL;
338
339
340/**
341 * Doesn't actually start the CM, just initializing. CM will run whenever
342 * an valid ssid is set through wl_cm_set_network() and wl_cm_start()
343 * has been called.
344 */
345wl_err_t
346wl_cm_init(cm_scan_cb_t scan_cb, 
347            cm_conn_cb_t conn_cb, 
348            cm_disconn_cb_t disconn_cb,
349            void* ctx)
350{
351        if (cm != NULL)
352                return WL_FAILURE;
353
354        cm = calloc(1, sizeof(struct cm));
355        if (cm == NULL) {
356                CM_DPRINTF("CM: out of memory\n");
357                return WL_FAILURE;
358        }
359       
360        if (wl_register_event_cb(wl_event_cb, cm) != WL_SUCCESS) {
361                CM_DPRINTF("CM: could not register event cb\n");
362                return WL_FAILURE;
363        }
364
365        cm->scan_cb = scan_cb;
366        cm->conn_cb = conn_cb;
367        cm->disconn_cb = disconn_cb;
368        cm->enabled = 0;
369        cm->ctx = ctx;
370       
371        CM_DPRINTF("CM: initialized\n");
372        return WL_SUCCESS;
373}
374
375wl_err_t 
376wl_cm_start(void) {
377        if (NULL == cm)
378                return WL_FAILURE;
379
380        cm->enabled = 1;
381        return WL_SUCCESS;
382}
383
384wl_err_t 
385wl_cm_stop(void) {
386        if (NULL == cm)
387                return WL_FAILURE;
388
389        cm->enabled = 0;
390        return WL_SUCCESS;
391}
392
393
394/**
395 * Set the desired network which the connection manager should try to
396 * connect to.
397 *
398 * The ssid and bssid of the desired network should be specified. The ssid and
399 * bssid will be matched against the networks found during scan. If any
400 * parameter is null, it will always match. If both parameters are null,
401 * the first found network will be chosen.
402 *
403 * @param ssid The ssid of the desired network. If null, any ssid will match.
404 * @param bssid The bssid of the desired network. If null, any bssid will match.
405 *         
406 */
407wl_err_t 
408wl_cm_set_network(struct wl_ssid_t *ssid, struct wl_mac_addr_t *bssid)
409{
410        if (cm == NULL)
411                return WL_FAILURE;
412           
413        if (ssid)
414                memcpy(&cm->candidate.ssid, ssid, sizeof(cm->candidate.ssid));
415        else 
416                cm->candidate.ssid.len = 0;
417       
418        if (bssid)
419                memcpy(&cm->candidate.bssid, bssid, 
420                       sizeof(cm->candidate.bssid));
421        else
422                memset(&cm->candidate.bssid, 0xff, sizeof(cm->candidate.bssid));
423
424        if (cm->candidate.ssid.len)
425                wl_scan();
426       
427        return WL_SUCCESS;
428}
429/*
430 *  @}
431 */
Note: See TracBrowser for help on using the repository browser.