diff options
author | Kalle Valo <kvalo@qca.qualcomm.com> | 2011-12-13 07:52:07 -0500 |
---|---|---|
committer | Kalle Valo <kvalo@qca.qualcomm.com> | 2011-12-13 08:03:49 -0500 |
commit | 10509f903ebb7d2a02571f30cb937dd923b023cf (patch) | |
tree | 6787da43aeb8eaecac6f9d82e640a639ceb3e20f /drivers/net/wireless/ath/ath6kl/cfg80211.c | |
parent | 277d90f4ba4b7ebb35b85a5d6c58dce2f1e1b58d (diff) |
ath6kl: implement scheduled scan
ath6kl firmware supports scheduled scan functionality with the wow ssid
filter. But the firmware does not send any events after scan results
so I had to add a timer which notifies about new scan results.
Sched scan needs firmware version 3.2.0.6 or later. If firmware doesn't
support sched scan the driver will not enable the feature.
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
Diffstat (limited to 'drivers/net/wireless/ath/ath6kl/cfg80211.c')
-rw-r--r-- | drivers/net/wireless/ath/ath6kl/cfg80211.c | 144 |
1 files changed, 144 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 074e2aabf41e..48d414acefee 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c | |||
@@ -123,6 +123,37 @@ static struct ieee80211_supported_band ath6kl_band_5ghz = { | |||
123 | 123 | ||
124 | #define CCKM_KRK_CIPHER_SUITE 0x004096ff /* use for KRK */ | 124 | #define CCKM_KRK_CIPHER_SUITE 0x004096ff /* use for KRK */ |
125 | 125 | ||
126 | /* returns true if scheduled scan was stopped */ | ||
127 | static bool __ath6kl_cfg80211_sscan_stop(struct ath6kl_vif *vif) | ||
128 | { | ||
129 | struct ath6kl *ar = vif->ar; | ||
130 | |||
131 | if (ar->state != ATH6KL_STATE_SCHED_SCAN) | ||
132 | return false; | ||
133 | |||
134 | del_timer_sync(&vif->sched_scan_timer); | ||
135 | |||
136 | ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, | ||
137 | ATH6KL_HOST_MODE_AWAKE); | ||
138 | |||
139 | ar->state = ATH6KL_STATE_ON; | ||
140 | |||
141 | return true; | ||
142 | } | ||
143 | |||
144 | static void ath6kl_cfg80211_sscan_disable(struct ath6kl_vif *vif) | ||
145 | { | ||
146 | struct ath6kl *ar = vif->ar; | ||
147 | bool stopped; | ||
148 | |||
149 | stopped = __ath6kl_cfg80211_sscan_stop(vif); | ||
150 | |||
151 | if (!stopped) | ||
152 | return; | ||
153 | |||
154 | cfg80211_sched_scan_stopped(ar->wiphy); | ||
155 | } | ||
156 | |||
126 | static int ath6kl_set_wpa_version(struct ath6kl_vif *vif, | 157 | static int ath6kl_set_wpa_version(struct ath6kl_vif *vif, |
127 | enum nl80211_wpa_versions wpa_version) | 158 | enum nl80211_wpa_versions wpa_version) |
128 | { | 159 | { |
@@ -383,6 +414,8 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, | |||
383 | struct ath6kl_vif *vif = netdev_priv(dev); | 414 | struct ath6kl_vif *vif = netdev_priv(dev); |
384 | int status; | 415 | int status; |
385 | 416 | ||
417 | ath6kl_cfg80211_sscan_disable(vif); | ||
418 | |||
386 | vif->sme_state = SME_CONNECTING; | 419 | vif->sme_state = SME_CONNECTING; |
387 | 420 | ||
388 | if (!ath6kl_cfg80211_ready(vif)) | 421 | if (!ath6kl_cfg80211_ready(vif)) |
@@ -710,6 +743,8 @@ static int ath6kl_cfg80211_disconnect(struct wiphy *wiphy, | |||
710 | ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: reason=%u\n", __func__, | 743 | ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: reason=%u\n", __func__, |
711 | reason_code); | 744 | reason_code); |
712 | 745 | ||
746 | ath6kl_cfg80211_sscan_disable(vif); | ||
747 | |||
713 | if (!ath6kl_cfg80211_ready(vif)) | 748 | if (!ath6kl_cfg80211_ready(vif)) |
714 | return -EIO; | 749 | return -EIO; |
715 | 750 | ||
@@ -812,6 +847,8 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, | |||
812 | if (!ath6kl_cfg80211_ready(vif)) | 847 | if (!ath6kl_cfg80211_ready(vif)) |
813 | return -EIO; | 848 | return -EIO; |
814 | 849 | ||
850 | ath6kl_cfg80211_sscan_disable(vif); | ||
851 | |||
815 | if (!ar->usr_bss_filter) { | 852 | if (!ar->usr_bss_filter) { |
816 | clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags); | 853 | clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags); |
817 | ret = ath6kl_wmi_bssfilter_cmd( | 854 | ret = ath6kl_wmi_bssfilter_cmd( |
@@ -837,6 +874,10 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, | |||
837 | request->ssids[i].ssid); | 874 | request->ssids[i].ssid); |
838 | } | 875 | } |
839 | 876 | ||
877 | /* | ||
878 | * FIXME: we should clear the IE in fw if it's not set so just | ||
879 | * remove the check altogether | ||
880 | */ | ||
840 | if (request->ie) { | 881 | if (request->ie) { |
841 | ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, | 882 | ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, |
842 | WMI_FRAME_PROBE_REQ, | 883 | WMI_FRAME_PROBE_REQ, |
@@ -1835,6 +1876,13 @@ int ath6kl_cfg80211_suspend(struct ath6kl *ar, | |||
1835 | 1876 | ||
1836 | break; | 1877 | break; |
1837 | 1878 | ||
1879 | case ATH6KL_CFG_SUSPEND_SCHED_SCAN: | ||
1880 | /* | ||
1881 | * Nothing needed for schedule scan, firmware is already in | ||
1882 | * wow mode and sleeping most of the time. | ||
1883 | */ | ||
1884 | break; | ||
1885 | |||
1838 | default: | 1886 | default: |
1839 | break; | 1887 | break; |
1840 | } | 1888 | } |
@@ -1883,6 +1931,9 @@ int ath6kl_cfg80211_resume(struct ath6kl *ar) | |||
1883 | } | 1931 | } |
1884 | break; | 1932 | break; |
1885 | 1933 | ||
1934 | case ATH6KL_STATE_SCHED_SCAN: | ||
1935 | break; | ||
1936 | |||
1886 | default: | 1937 | default: |
1887 | break; | 1938 | break; |
1888 | } | 1939 | } |
@@ -2329,6 +2380,90 @@ static void ath6kl_mgmt_frame_register(struct wiphy *wiphy, | |||
2329 | } | 2380 | } |
2330 | } | 2381 | } |
2331 | 2382 | ||
2383 | static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy, | ||
2384 | struct net_device *dev, | ||
2385 | struct cfg80211_sched_scan_request *request) | ||
2386 | { | ||
2387 | struct ath6kl *ar = ath6kl_priv(dev); | ||
2388 | struct ath6kl_vif *vif = netdev_priv(dev); | ||
2389 | u16 interval; | ||
2390 | int ret; | ||
2391 | u8 i; | ||
2392 | |||
2393 | if (ar->state != ATH6KL_STATE_ON) | ||
2394 | return -EIO; | ||
2395 | |||
2396 | if (vif->sme_state != SME_DISCONNECTED) | ||
2397 | return -EBUSY; | ||
2398 | |||
2399 | for (i = 0; i < ar->wiphy->max_sched_scan_ssids; i++) { | ||
2400 | ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, | ||
2401 | i, DISABLE_SSID_FLAG, | ||
2402 | 0, NULL); | ||
2403 | } | ||
2404 | |||
2405 | /* fw uses seconds, also make sure that it's >0 */ | ||
2406 | interval = max_t(u16, 1, request->interval / 1000); | ||
2407 | |||
2408 | ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, | ||
2409 | interval, interval, | ||
2410 | 10, 0, 0, 0, 3, 0, 0, 0); | ||
2411 | |||
2412 | if (request->n_ssids && request->ssids[0].ssid_len) { | ||
2413 | for (i = 0; i < request->n_ssids; i++) { | ||
2414 | ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, | ||
2415 | i, SPECIFIC_SSID_FLAG, | ||
2416 | request->ssids[i].ssid_len, | ||
2417 | request->ssids[i].ssid); | ||
2418 | } | ||
2419 | } | ||
2420 | |||
2421 | ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx, | ||
2422 | ATH6KL_WOW_MODE_ENABLE, | ||
2423 | WOW_FILTER_SSID, | ||
2424 | WOW_HOST_REQ_DELAY); | ||
2425 | if (ret) { | ||
2426 | ath6kl_warn("Failed to enable wow with ssid filter: %d\n", ret); | ||
2427 | return ret; | ||
2428 | } | ||
2429 | |||
2430 | /* this also clears IE in fw if it's not set */ | ||
2431 | ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, | ||
2432 | WMI_FRAME_PROBE_REQ, | ||
2433 | request->ie, request->ie_len); | ||
2434 | if (ret) { | ||
2435 | ath6kl_warn("Failed to set probe request IE for scheduled scan: %d", | ||
2436 | ret); | ||
2437 | return ret; | ||
2438 | } | ||
2439 | |||
2440 | ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, | ||
2441 | ATH6KL_HOST_MODE_ASLEEP); | ||
2442 | if (ret) { | ||
2443 | ath6kl_warn("Failed to enable host sleep mode for sched scan: %d\n", | ||
2444 | ret); | ||
2445 | return ret; | ||
2446 | } | ||
2447 | |||
2448 | ar->state = ATH6KL_STATE_SCHED_SCAN; | ||
2449 | |||
2450 | return ret; | ||
2451 | } | ||
2452 | |||
2453 | static int ath6kl_cfg80211_sscan_stop(struct wiphy *wiphy, | ||
2454 | struct net_device *dev) | ||
2455 | { | ||
2456 | struct ath6kl_vif *vif = netdev_priv(dev); | ||
2457 | bool stopped; | ||
2458 | |||
2459 | stopped = __ath6kl_cfg80211_sscan_stop(vif); | ||
2460 | |||
2461 | if (!stopped) | ||
2462 | return -EIO; | ||
2463 | |||
2464 | return 0; | ||
2465 | } | ||
2466 | |||
2332 | static const struct ieee80211_txrx_stypes | 2467 | static const struct ieee80211_txrx_stypes |
2333 | ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = { | 2468 | ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = { |
2334 | [NL80211_IFTYPE_STATION] = { | 2469 | [NL80211_IFTYPE_STATION] = { |
@@ -2386,10 +2521,14 @@ static struct cfg80211_ops ath6kl_cfg80211_ops = { | |||
2386 | .cancel_remain_on_channel = ath6kl_cancel_remain_on_channel, | 2521 | .cancel_remain_on_channel = ath6kl_cancel_remain_on_channel, |
2387 | .mgmt_tx = ath6kl_mgmt_tx, | 2522 | .mgmt_tx = ath6kl_mgmt_tx, |
2388 | .mgmt_frame_register = ath6kl_mgmt_frame_register, | 2523 | .mgmt_frame_register = ath6kl_mgmt_frame_register, |
2524 | .sched_scan_start = ath6kl_cfg80211_sscan_start, | ||
2525 | .sched_scan_stop = ath6kl_cfg80211_sscan_stop, | ||
2389 | }; | 2526 | }; |
2390 | 2527 | ||
2391 | void ath6kl_cfg80211_stop(struct ath6kl_vif *vif) | 2528 | void ath6kl_cfg80211_stop(struct ath6kl_vif *vif) |
2392 | { | 2529 | { |
2530 | ath6kl_cfg80211_sscan_disable(vif); | ||
2531 | |||
2393 | switch (vif->sme_state) { | 2532 | switch (vif->sme_state) { |
2394 | case SME_DISCONNECTED: | 2533 | case SME_DISCONNECTED: |
2395 | break; | 2534 | break; |
@@ -2546,6 +2685,8 @@ int ath6kl_register_ieee80211_hw(struct ath6kl *ar) | |||
2546 | wiphy->wowlan.pattern_min_len = 1; | 2685 | wiphy->wowlan.pattern_min_len = 1; |
2547 | wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE; | 2686 | wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE; |
2548 | 2687 | ||
2688 | wiphy->max_sched_scan_ssids = 10; | ||
2689 | |||
2549 | ret = wiphy_register(wiphy); | 2690 | ret = wiphy_register(wiphy); |
2550 | if (ret < 0) { | 2691 | if (ret < 0) { |
2551 | ath6kl_err("couldn't register wiphy device\n"); | 2692 | ath6kl_err("couldn't register wiphy device\n"); |
@@ -2565,6 +2706,9 @@ static int ath6kl_init_if_data(struct ath6kl_vif *vif) | |||
2565 | 2706 | ||
2566 | setup_timer(&vif->disconnect_timer, disconnect_timer_handler, | 2707 | setup_timer(&vif->disconnect_timer, disconnect_timer_handler, |
2567 | (unsigned long) vif->ndev); | 2708 | (unsigned long) vif->ndev); |
2709 | setup_timer(&vif->sched_scan_timer, ath6kl_wmi_sscan_timer, | ||
2710 | (unsigned long) vif); | ||
2711 | |||
2568 | set_bit(WMM_ENABLED, &vif->flags); | 2712 | set_bit(WMM_ENABLED, &vif->flags); |
2569 | spin_lock_init(&vif->if_lock); | 2713 | spin_lock_init(&vif->if_lock); |
2570 | 2714 | ||