aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArend van Spriel <arend@broadcom.com>2012-09-19 16:21:08 -0400
committerJohn W. Linville <linville@tuxdriver.com>2012-09-24 15:02:05 -0400
commite58060723c91a2345318fe809fc99945776a7a25 (patch)
treeb8ee657827455c87f9847c0d672edb8ff84c0750
parent108a4bee9db5d50e99b3aea349cbc987cc857b32 (diff)
brcmfmac: introduce scheduled scan support
This change add support for NL80211 scheduled scan. This may be used to offload scanning to the device, which may give the host opportunity to sleep. The newer versions of wpa_supplicant have support for this functionality. Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com> Reviewed-by: Hante Meuleman <meuleman@broadcom.com> Signed-off-by: Arend van Spriel <arend@broadcom.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c306
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h82
2 files changed, 387 insertions, 1 deletions
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
index 97ed75f6e277..daaac4e2658d 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
@@ -36,6 +36,18 @@
36#include "dhd.h" 36#include "dhd.h"
37#include "wl_cfg80211.h" 37#include "wl_cfg80211.h"
38 38
39#define BRCMF_SCAN_IE_LEN_MAX 2048
40#define BRCMF_PNO_VERSION 2
41#define BRCMF_PNO_TIME 30
42#define BRCMF_PNO_REPEAT 4
43#define BRCMF_PNO_FREQ_EXPO_MAX 3
44#define BRCMF_PNO_MAX_PFN_COUNT 16
45#define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT 6
46#define BRCMF_PNO_HIDDEN_BIT 2
47#define BRCMF_PNO_WPA_AUTH_ANY 0xFFFFFFFF
48#define BRCMF_PNO_SCAN_COMPLETE 1
49#define BRCMF_PNO_SCAN_INCOMPLETE 0
50
39#define BRCMF_ASSOC_PARAMS_FIXED_SIZE \ 51#define BRCMF_ASSOC_PARAMS_FIXED_SIZE \
40 (sizeof(struct brcmf_assoc_params_le) - sizeof(u16)) 52 (sizeof(struct brcmf_assoc_params_le) - sizeof(u16))
41 53
@@ -830,7 +842,17 @@ brcmf_notify_escan_complete(struct brcmf_cfg80211_priv *cfg_priv,
830 if (err) 842 if (err)
831 WL_ERR("Scan abort failed\n"); 843 WL_ERR("Scan abort failed\n");
832 } 844 }
833 if (scan_request) { 845 /*
846 * e-scan can be initiated by scheduled scan
847 * which takes precedence.
848 */
849 if (cfg_priv->sched_escan) {
850 WL_SCAN("scheduled scan completed\n");
851 cfg_priv->sched_escan = false;
852 if (!aborted)
853 cfg80211_sched_scan_results(cfg_to_wiphy(cfg_priv));
854 brcmf_set_mpc(ndev, 1);
855 } else if (scan_request) {
834 WL_SCAN("ESCAN Completed scan: %s\n", 856 WL_SCAN("ESCAN Completed scan: %s\n",
835 aborted ? "Aborted" : "Done"); 857 aborted ? "Aborted" : "Done");
836 cfg80211_scan_done(scan_request, aborted); 858 cfg80211_scan_done(scan_request, aborted);
@@ -3229,6 +3251,269 @@ brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)
3229 3251
3230} 3252}
3231 3253
3254/*
3255 * PFN result doesn't have all the info which are
3256 * required by the supplicant
3257 * (For e.g IEs) Do a target Escan so that sched scan results are reported
3258 * via wl_inform_single_bss in the required format. Escan does require the
3259 * scan request in the form of cfg80211_scan_request. For timebeing, create
3260 * cfg80211_scan_request one out of the received PNO event.
3261 */
3262static s32
3263brcmf_notify_sched_scan_results(struct brcmf_cfg80211_priv *cfg_priv,
3264 struct net_device *ndev,
3265 const struct brcmf_event_msg *e, void *data)
3266{
3267 struct brcmf_pno_net_info_le *netinfo, *netinfo_start;
3268 struct cfg80211_scan_request *request = NULL;
3269 struct cfg80211_ssid *ssid = NULL;
3270 struct ieee80211_channel *channel = NULL;
3271 struct wiphy *wiphy = cfg_to_wiphy(cfg_priv);
3272 int err = 0;
3273 int channel_req = 0;
3274 int band = 0;
3275 struct brcmf_pno_scanresults_le *pfn_result;
3276 u32 result_count;
3277 u32 status;
3278
3279 WL_SCAN("Enter\n");
3280
3281 if (e->event_type == cpu_to_be32(BRCMF_E_PFN_NET_LOST)) {
3282 WL_SCAN("PFN NET LOST event. Do Nothing\n");
3283 return 0;
3284 }
3285
3286 pfn_result = (struct brcmf_pno_scanresults_le *)data;
3287 result_count = le32_to_cpu(pfn_result->count);
3288 status = le32_to_cpu(pfn_result->status);
3289
3290 /*
3291 * PFN event is limited to fit 512 bytes so we may get
3292 * multiple NET_FOUND events. For now place a warning here.
3293 */
3294 WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE);
3295 WL_SCAN("PFN NET FOUND event. count: %d\n", result_count);
3296 if (result_count > 0) {
3297 int i;
3298
3299 request = kzalloc(sizeof(*request), GFP_KERNEL);
3300 ssid = kzalloc(sizeof(*ssid) * result_count, GFP_KERNEL);
3301 channel = kzalloc(sizeof(*channel) * result_count, GFP_KERNEL);
3302 if (!request || !ssid || !channel) {
3303 err = -ENOMEM;
3304 goto out_err;
3305 }
3306
3307 request->wiphy = wiphy;
3308 data += sizeof(struct brcmf_pno_scanresults_le);
3309 netinfo_start = (struct brcmf_pno_net_info_le *)data;
3310
3311 for (i = 0; i < result_count; i++) {
3312 netinfo = &netinfo_start[i];
3313 if (!netinfo) {
3314 WL_ERR("Invalid netinfo ptr. index: %d\n", i);
3315 err = -EINVAL;
3316 goto out_err;
3317 }
3318
3319 WL_SCAN("SSID:%s Channel:%d\n",
3320 netinfo->SSID, netinfo->channel);
3321 memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len);
3322 ssid[i].ssid_len = netinfo->SSID_len;
3323 request->n_ssids++;
3324
3325 channel_req = netinfo->channel;
3326 if (channel_req <= CH_MAX_2G_CHANNEL)
3327 band = NL80211_BAND_2GHZ;
3328 else
3329 band = NL80211_BAND_5GHZ;
3330 channel[i].center_freq =
3331 ieee80211_channel_to_frequency(channel_req,
3332 band);
3333 channel[i].band = band;
3334 channel[i].flags |= IEEE80211_CHAN_NO_HT40;
3335 request->channels[i] = &channel[i];
3336 request->n_channels++;
3337 }
3338
3339 /* assign parsed ssid array */
3340 if (request->n_ssids)
3341 request->ssids = &ssid[0];
3342
3343 if (test_bit(WL_STATUS_SCANNING, &cfg_priv->status)) {
3344 /* Abort any on-going scan */
3345 brcmf_abort_scanning(cfg_priv);
3346 }
3347
3348 set_bit(WL_STATUS_SCANNING, &cfg_priv->status);
3349 err = brcmf_do_escan(cfg_priv, wiphy, ndev, request);
3350 if (err) {
3351 clear_bit(WL_STATUS_SCANNING, &cfg_priv->status);
3352 goto out_err;
3353 }
3354 cfg_priv->sched_escan = true;
3355 cfg_priv->scan_request = request;
3356 } else {
3357 WL_ERR("FALSE PNO Event. (pfn_count == 0)\n");
3358 goto out_err;
3359 }
3360
3361 kfree(ssid);
3362 kfree(channel);
3363 kfree(request);
3364 return 0;
3365
3366out_err:
3367 kfree(ssid);
3368 kfree(channel);
3369 kfree(request);
3370 cfg80211_sched_scan_stopped(wiphy);
3371 return err;
3372}
3373
3374#ifndef CONFIG_BRCMISCAN
3375static int brcmf_dev_pno_clean(struct net_device *ndev)
3376{
3377 char iovbuf[128];
3378 int ret;
3379
3380 /* Disable pfn */
3381 ret = brcmf_dev_intvar_set(ndev, "pfn", 0);
3382 if (ret == 0) {
3383 /* clear pfn */
3384 ret = brcmf_dev_iovar_setbuf(ndev, "pfnclear", NULL, 0,
3385 iovbuf, sizeof(iovbuf));
3386 }
3387 if (ret < 0)
3388 WL_ERR("failed code %d\n", ret);
3389
3390 return ret;
3391}
3392
3393static int brcmf_dev_pno_config(struct net_device *ndev)
3394{
3395 struct brcmf_pno_param_le pfn_param;
3396 char iovbuf[128];
3397
3398 memset(&pfn_param, 0, sizeof(pfn_param));
3399 pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION);
3400
3401 /* set extra pno params */
3402 pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT);
3403 pfn_param.repeat = BRCMF_PNO_REPEAT;
3404 pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX;
3405
3406 /* set up pno scan fr */
3407 pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME);
3408
3409 return brcmf_dev_iovar_setbuf(ndev, "pfn_set",
3410 &pfn_param, sizeof(pfn_param),
3411 iovbuf, sizeof(iovbuf));
3412}
3413
3414static int
3415brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
3416 struct net_device *ndev,
3417 struct cfg80211_sched_scan_request *request)
3418{
3419 char iovbuf[128];
3420 struct brcmf_cfg80211_priv *cfg_priv = wiphy_priv(wiphy);
3421 struct brcmf_pno_net_param_le pfn;
3422 int i;
3423 int ret = 0;
3424
3425 WL_SCAN("Enter n_match_sets:%d n_ssids:%d\n",
3426 request->n_match_sets, request->n_ssids);
3427 if (test_bit(WL_STATUS_SCANNING, &cfg_priv->status)) {
3428 WL_ERR("Scanning already : status (%lu)\n", cfg_priv->status);
3429 return -EAGAIN;
3430 }
3431
3432 if (!request || !request->n_ssids || !request->n_match_sets) {
3433 WL_ERR("Invalid sched scan req!! n_ssids:%d\n",
3434 request->n_ssids);
3435 return -EINVAL;
3436 }
3437
3438 if (request->n_ssids > 0) {
3439 for (i = 0; i < request->n_ssids; i++) {
3440 /* Active scan req for ssids */
3441 WL_SCAN(">>> Active scan req for ssid (%s)\n",
3442 request->ssids[i].ssid);
3443
3444 /*
3445 * match_set ssids is a supert set of n_ssid list,
3446 * so we need not add these set seperately.
3447 */
3448 }
3449 }
3450
3451 if (request->n_match_sets > 0) {
3452 /* clean up everything */
3453 ret = brcmf_dev_pno_clean(ndev);
3454 if (ret < 0) {
3455 WL_ERR("failed error=%d\n", ret);
3456 return ret;
3457 }
3458
3459 /* configure pno */
3460 ret = brcmf_dev_pno_config(ndev);
3461 if (ret < 0) {
3462 WL_ERR("PNO setup failed!! ret=%d\n", ret);
3463 return -EINVAL;
3464 }
3465
3466 /* configure each match set */
3467 for (i = 0; i < request->n_match_sets; i++) {
3468 struct cfg80211_ssid *ssid;
3469 u32 ssid_len;
3470
3471 ssid = &request->match_sets[i].ssid;
3472 ssid_len = ssid->ssid_len;
3473
3474 if (!ssid_len) {
3475 WL_ERR("skip broadcast ssid\n");
3476 continue;
3477 }
3478 pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN);
3479 pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY);
3480 pfn.wsec = cpu_to_le32(0);
3481 pfn.infra = cpu_to_le32(1);
3482 pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT);
3483 pfn.ssid.SSID_len = cpu_to_le32(ssid_len);
3484 memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len);
3485 ret = brcmf_dev_iovar_setbuf(ndev, "pfn_add",
3486 &pfn, sizeof(pfn),
3487 iovbuf, sizeof(iovbuf));
3488 WL_SCAN(">>> PNO filter %s for ssid (%s)\n",
3489 ret == 0 ? "set" : "failed",
3490 ssid->ssid);
3491 }
3492 /* Enable the PNO */
3493 if (brcmf_dev_intvar_set(ndev, "pfn", 1) < 0) {
3494 WL_ERR("PNO enable failed!! ret=%d\n", ret);
3495 return -EINVAL;
3496 }
3497 } else {
3498 return -EINVAL;
3499 }
3500
3501 return 0;
3502}
3503
3504static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
3505 struct net_device *ndev)
3506{
3507 struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);
3508
3509 WL_SCAN("enter\n");
3510 brcmf_dev_pno_clean(ndev);
3511 if (cfg_priv->sched_escan)
3512 brcmf_notify_escan_complete(cfg_priv, ndev, true, true);
3513 return 0;
3514}
3515#endif /* CONFIG_BRCMISCAN */
3516
3232#ifdef CONFIG_NL80211_TESTMODE 3517#ifdef CONFIG_NL80211_TESTMODE
3233static int brcmf_cfg80211_testmode(struct wiphy *wiphy, void *data, int len) 3518static int brcmf_cfg80211_testmode(struct wiphy *wiphy, void *data, int len)
3234{ 3519{
@@ -3271,6 +3556,11 @@ static struct cfg80211_ops wl_cfg80211_ops = {
3271 .set_pmksa = brcmf_cfg80211_set_pmksa, 3556 .set_pmksa = brcmf_cfg80211_set_pmksa,
3272 .del_pmksa = brcmf_cfg80211_del_pmksa, 3557 .del_pmksa = brcmf_cfg80211_del_pmksa,
3273 .flush_pmksa = brcmf_cfg80211_flush_pmksa, 3558 .flush_pmksa = brcmf_cfg80211_flush_pmksa,
3559#ifndef CONFIG_BRCMISCAN
3560 /* scheduled scan need e-scan, which is mutual exclusive with i-scan */
3561 .sched_scan_start = brcmf_cfg80211_sched_scan_start,
3562 .sched_scan_stop = brcmf_cfg80211_sched_scan_stop,
3563#endif
3274#ifdef CONFIG_NL80211_TESTMODE 3564#ifdef CONFIG_NL80211_TESTMODE
3275 .testmode_cmd = brcmf_cfg80211_testmode 3565 .testmode_cmd = brcmf_cfg80211_testmode
3276#endif 3566#endif
@@ -3292,6 +3582,17 @@ static s32 brcmf_mode_to_nl80211_iftype(s32 mode)
3292 return err; 3582 return err;
3293} 3583}
3294 3584
3585static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
3586{
3587#ifndef CONFIG_BRCMFISCAN
3588 /* scheduled scan settings */
3589 wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
3590 wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
3591 wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
3592 wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
3593#endif
3594}
3595
3295static struct wireless_dev *brcmf_alloc_wdev(s32 sizeof_iface, 3596static struct wireless_dev *brcmf_alloc_wdev(s32 sizeof_iface,
3296 struct device *ndev) 3597 struct device *ndev)
3297{ 3598{
@@ -3330,6 +3631,7 @@ static struct wireless_dev *brcmf_alloc_wdev(s32 sizeof_iface,
3330 * save mode 3631 * save mode
3331 * by default 3632 * by default
3332 */ 3633 */
3634 brcmf_wiphy_pno_params(wdev->wiphy);
3333 err = wiphy_register(wdev->wiphy); 3635 err = wiphy_register(wdev->wiphy);
3334 if (err < 0) { 3636 if (err < 0) {
3335 WL_ERR("Could not register wiphy device (%d)\n", err); 3637 WL_ERR("Could not register wiphy device (%d)\n", err);
@@ -3732,6 +4034,7 @@ static void brcmf_init_eloop_handler(struct brcmf_cfg80211_event_loop *el)
3732 el->handler[BRCMF_E_ROAM] = brcmf_notify_roaming_status; 4034 el->handler[BRCMF_E_ROAM] = brcmf_notify_roaming_status;
3733 el->handler[BRCMF_E_MIC_ERROR] = brcmf_notify_mic_status; 4035 el->handler[BRCMF_E_MIC_ERROR] = brcmf_notify_mic_status;
3734 el->handler[BRCMF_E_SET_SSID] = brcmf_notify_connect_status; 4036 el->handler[BRCMF_E_SET_SSID] = brcmf_notify_connect_status;
4037 el->handler[BRCMF_E_PFN_NET_FOUND] = brcmf_notify_sched_scan_results;
3735} 4038}
3736 4039
3737static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_priv *cfg_priv) 4040static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_priv *cfg_priv)
@@ -4103,6 +4406,7 @@ static s32 brcmf_dongle_eventmsg(struct net_device *ndev)
4103 setbit(eventmask, BRCMF_E_JOIN_START); 4406 setbit(eventmask, BRCMF_E_JOIN_START);
4104 setbit(eventmask, BRCMF_E_SCAN_COMPLETE); 4407 setbit(eventmask, BRCMF_E_SCAN_COMPLETE);
4105 setbit(eventmask, BRCMF_E_ESCAN_RESULT); 4408 setbit(eventmask, BRCMF_E_ESCAN_RESULT);
4409 setbit(eventmask, BRCMF_E_PFN_NET_FOUND);
4106 4410
4107 brcmf_c_mkiovar("event_msgs", eventmask, BRCMF_EVENTING_MASK_LEN, 4411 brcmf_c_mkiovar("event_msgs", eventmask, BRCMF_EVENTING_MASK_LEN,
4108 iovbuf, sizeof(iovbuf)); 4412 iovbuf, sizeof(iovbuf));
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h
index 3b2129738d30..b5c3fd942f59 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h
@@ -295,6 +295,87 @@ struct escan_info {
295 struct net_device *ndev; 295 struct net_device *ndev;
296}; 296};
297 297
298/**
299 * struct brcmf_pno_param_le - PNO scan configuration parameters
300 *
301 * @version: PNO parameters version.
302 * @scan_freq: scan frequency.
303 * @lost_network_timeout: #sec. to declare discovered network as lost.
304 * @flags: Bit field to control features of PFN such as sort criteria auto
305 * enable switch and background scan.
306 * @rssi_margin: Margin to avoid jitter for choosing a PFN based on RSSI sort
307 * criteria.
308 * @bestn: number of best networks in each scan.
309 * @mscan: number of scans recorded.
310 * @repeat: minimum number of scan intervals before scan frequency changes
311 * in adaptive scan.
312 * @exp: exponent of 2 for maximum scan interval.
313 * @slow_freq: slow scan period.
314 */
315struct brcmf_pno_param_le {
316 __le32 version;
317 __le32 scan_freq;
318 __le32 lost_network_timeout;
319 __le16 flags;
320 __le16 rssi_margin;
321 u8 bestn;
322 u8 mscan;
323 u8 repeat;
324 u8 exp;
325 __le32 slow_freq;
326};
327
328/**
329 * struct brcmf_pno_net_param_le - scan parameters per preferred network.
330 *
331 * @ssid: ssid name and its length.
332 * @flags: bit2: hidden.
333 * @infra: BSS vs IBSS.
334 * @auth: Open vs Closed.
335 * @wpa_auth: WPA type.
336 * @wsec: wsec value.
337 */
338struct brcmf_pno_net_param_le {
339 struct brcmf_ssid_le ssid;
340 __le32 flags;
341 __le32 infra;
342 __le32 auth;
343 __le32 wpa_auth;
344 __le32 wsec;
345};
346
347/**
348 * struct brcmf_pno_net_info_le - information per found network.
349 *
350 * @bssid: BSS network identifier.
351 * @channel: channel number only.
352 * @SSID_len: length of ssid.
353 * @SSID: ssid characters.
354 * @RSSI: receive signal strength (in dBm).
355 * @timestamp: age in seconds.
356 */
357struct brcmf_pno_net_info_le {
358 u8 bssid[ETH_ALEN];
359 u8 channel;
360 u8 SSID_len;
361 u8 SSID[32];
362 __le16 RSSI;
363 __le16 timestamp;
364};
365
366/**
367 * struct brcmf_pno_scanresults_le - result returned in PNO NET FOUND event.
368 *
369 * @version: PNO version identifier.
370 * @status: indicates completion status of PNO scan.
371 * @count: amount of brcmf_pno_net_info_le entries appended.
372 */
373struct brcmf_pno_scanresults_le {
374 __le32 version;
375 __le32 status;
376 __le32 count;
377};
378
298/* dongle private data of cfg80211 interface */ 379/* dongle private data of cfg80211 interface */
299struct brcmf_cfg80211_priv { 380struct brcmf_cfg80211_priv {
300 struct wireless_dev *wdev; /* representing wl cfg80211 device */ 381 struct wireless_dev *wdev; /* representing wl cfg80211 device */
@@ -325,6 +406,7 @@ struct brcmf_cfg80211_priv {
325 bool iscan_on; /* iscan on/off switch */ 406 bool iscan_on; /* iscan on/off switch */
326 bool iscan_kickstart; /* indicate iscan already started */ 407 bool iscan_kickstart; /* indicate iscan already started */
327 bool active_scan; /* current scan mode */ 408 bool active_scan; /* current scan mode */
409 bool sched_escan; /* e-scan for scheduled scan support running */
328 bool ibss_starter; /* indicates this sta is ibss starter */ 410 bool ibss_starter; /* indicates this sta is ibss starter */
329 bool link_up; /* link/connection up flag */ 411 bool link_up; /* link/connection up flag */
330 bool pwr_save; /* indicate whether dongle to support 412 bool pwr_save; /* indicate whether dongle to support