diff options
author | David Spinadel <david.spinadel@intel.com> | 2013-08-28 02:29:43 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2013-10-02 12:00:39 -0400 |
commit | 35a000b7c1bbd81631097539567f24a272a2fa0f (patch) | |
tree | 7194fb17baac16320fd90f150f8d3838022d2a64 /drivers/net/wireless/iwlwifi/mvm | |
parent | 20f1a5deb67f00cef89d63fb957a940c7f976cf3 (diff) |
iwlwifi: mvm: support sched scan if supported by the fw
Add support for scheduled scan according to firmware support.
Signed-off-by: David Spinadel <david.spinadel@intel.com>
Reviewed-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'drivers/net/wireless/iwlwifi/mvm')
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h | 34 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/fw-api.h | 1 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/mac80211.c | 58 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/mvm.h | 18 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/ops.c | 8 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/scan.c | 418 |
6 files changed, 533 insertions, 4 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h index 83cb9b992ea4..c3782b48ded1 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h | |||
@@ -356,6 +356,7 @@ struct iwl_scan_complete_notif { | |||
356 | /* scan offload */ | 356 | /* scan offload */ |
357 | #define IWL_MAX_SCAN_CHANNELS 40 | 357 | #define IWL_MAX_SCAN_CHANNELS 40 |
358 | #define IWL_SCAN_MAX_BLACKLIST_LEN 64 | 358 | #define IWL_SCAN_MAX_BLACKLIST_LEN 64 |
359 | #define IWL_SCAN_SHORT_BLACKLIST_LEN 16 | ||
359 | #define IWL_SCAN_MAX_PROFILES 11 | 360 | #define IWL_SCAN_MAX_PROFILES 11 |
360 | #define SCAN_OFFLOAD_PROBE_REQ_SIZE 512 | 361 | #define SCAN_OFFLOAD_PROBE_REQ_SIZE 512 |
361 | 362 | ||
@@ -368,6 +369,12 @@ struct iwl_scan_complete_notif { | |||
368 | #define IWL_FULL_SCAN_MULTIPLIER 5 | 369 | #define IWL_FULL_SCAN_MULTIPLIER 5 |
369 | #define IWL_FAST_SCHED_SCAN_ITERATIONS 3 | 370 | #define IWL_FAST_SCHED_SCAN_ITERATIONS 3 |
370 | 371 | ||
372 | enum scan_framework_client { | ||
373 | SCAN_CLIENT_SCHED_SCAN = BIT(0), | ||
374 | SCAN_CLIENT_NETDETECT = BIT(1), | ||
375 | SCAN_CLIENT_ASSET_TRACKING = BIT(2), | ||
376 | }; | ||
377 | |||
371 | /** | 378 | /** |
372 | * struct iwl_scan_offload_cmd - SCAN_REQUEST_FIXED_PART_API_S_VER_6 | 379 | * struct iwl_scan_offload_cmd - SCAN_REQUEST_FIXED_PART_API_S_VER_6 |
373 | * @scan_flags: see enum iwl_scan_flags | 380 | * @scan_flags: see enum iwl_scan_flags |
@@ -449,11 +456,12 @@ struct iwl_scan_offload_cfg { | |||
449 | * iwl_scan_offload_blacklist - SCAN_OFFLOAD_BLACKLIST_S | 456 | * iwl_scan_offload_blacklist - SCAN_OFFLOAD_BLACKLIST_S |
450 | * @ssid: MAC address to filter out | 457 | * @ssid: MAC address to filter out |
451 | * @reported_rssi: AP rssi reported to the host | 458 | * @reported_rssi: AP rssi reported to the host |
459 | * @client_bitmap: clients ignore this entry - enum scan_framework_client | ||
452 | */ | 460 | */ |
453 | struct iwl_scan_offload_blacklist { | 461 | struct iwl_scan_offload_blacklist { |
454 | u8 ssid[ETH_ALEN]; | 462 | u8 ssid[ETH_ALEN]; |
455 | u8 reported_rssi; | 463 | u8 reported_rssi; |
456 | u8 reserved; | 464 | u8 client_bitmap; |
457 | } __packed; | 465 | } __packed; |
458 | 466 | ||
459 | enum iwl_scan_offload_network_type { | 467 | enum iwl_scan_offload_network_type { |
@@ -475,6 +483,7 @@ enum iwl_scan_offload_band_selection { | |||
475 | * @aut_alg: authentication olgorithm to match - bitmap | 483 | * @aut_alg: authentication olgorithm to match - bitmap |
476 | * @network_type: enum iwl_scan_offload_network_type | 484 | * @network_type: enum iwl_scan_offload_network_type |
477 | * @band_selection: enum iwl_scan_offload_band_selection | 485 | * @band_selection: enum iwl_scan_offload_band_selection |
486 | * @client_bitmap: clients waiting for match - enum scan_framework_client | ||
478 | */ | 487 | */ |
479 | struct iwl_scan_offload_profile { | 488 | struct iwl_scan_offload_profile { |
480 | u8 ssid_index; | 489 | u8 ssid_index; |
@@ -482,7 +491,8 @@ struct iwl_scan_offload_profile { | |||
482 | u8 auth_alg; | 491 | u8 auth_alg; |
483 | u8 network_type; | 492 | u8 network_type; |
484 | u8 band_selection; | 493 | u8 band_selection; |
485 | u8 reserved[3]; | 494 | u8 client_bitmap; |
495 | u8 reserved[2]; | ||
486 | } __packed; | 496 | } __packed; |
487 | 497 | ||
488 | /** | 498 | /** |
@@ -491,13 +501,18 @@ struct iwl_scan_offload_profile { | |||
491 | * @profiles: profiles to search for match | 501 | * @profiles: profiles to search for match |
492 | * @blacklist_len: length of blacklist | 502 | * @blacklist_len: length of blacklist |
493 | * @num_profiles: num of profiles in the list | 503 | * @num_profiles: num of profiles in the list |
504 | * @match_notify: clients waiting for match found notification | ||
505 | * @pass_match: clients waiting for the results | ||
506 | * @active_clients: active clients bitmap - enum scan_framework_client | ||
494 | */ | 507 | */ |
495 | struct iwl_scan_offload_profile_cfg { | 508 | struct iwl_scan_offload_profile_cfg { |
496 | struct iwl_scan_offload_blacklist blacklist[IWL_SCAN_MAX_BLACKLIST_LEN]; | ||
497 | struct iwl_scan_offload_profile profiles[IWL_SCAN_MAX_PROFILES]; | 509 | struct iwl_scan_offload_profile profiles[IWL_SCAN_MAX_PROFILES]; |
498 | u8 blacklist_len; | 510 | u8 blacklist_len; |
499 | u8 num_profiles; | 511 | u8 num_profiles; |
500 | u8 reserved[2]; | 512 | u8 match_notify; |
513 | u8 pass_match; | ||
514 | u8 active_clients; | ||
515 | u8 reserved[3]; | ||
501 | } __packed; | 516 | } __packed; |
502 | 517 | ||
503 | /** | 518 | /** |
@@ -560,4 +575,15 @@ struct iwl_scan_offload_complete { | |||
560 | u8 reserved; | 575 | u8 reserved; |
561 | } __packed; | 576 | } __packed; |
562 | 577 | ||
578 | /** | ||
579 | * iwl_sched_scan_results - SCAN_OFFLOAD_MATCH_FOUND_NTF_API_S_VER_1 | ||
580 | * @ssid_bitmap: SSIDs indexes found in this iteration | ||
581 | * @client_bitmap: clients that are active and wait for this notification | ||
582 | */ | ||
583 | struct iwl_sched_scan_results { | ||
584 | __le16 ssid_bitmap; | ||
585 | u8 client_bitmap; | ||
586 | u8 reserved; | ||
587 | }; | ||
588 | |||
563 | #endif | 589 | #endif |
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h index 3c833acad686..c28af8ac3d5f 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h | |||
@@ -132,6 +132,7 @@ enum { | |||
132 | SCAN_OFFLOAD_COMPLETE = 0x6D, | 132 | SCAN_OFFLOAD_COMPLETE = 0x6D, |
133 | SCAN_OFFLOAD_UPDATE_PROFILES_CMD = 0x6E, | 133 | SCAN_OFFLOAD_UPDATE_PROFILES_CMD = 0x6E, |
134 | SCAN_OFFLOAD_CONFIG_CMD = 0x6f, | 134 | SCAN_OFFLOAD_CONFIG_CMD = 0x6f, |
135 | MATCH_FOUND_NOTIFICATION = 0xd9, | ||
135 | 136 | ||
136 | /* Phy */ | 137 | /* Phy */ |
137 | PHY_CONFIGURATION_CMD = 0x6a, | 138 | PHY_CONFIGURATION_CMD = 0x6a, |
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 0340299b4b63..8c619151c467 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c | |||
@@ -239,6 +239,15 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) | |||
239 | else | 239 | else |
240 | hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; | 240 | hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; |
241 | 241 | ||
242 | if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_SCHED_SCAN) { | ||
243 | hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; | ||
244 | hw->wiphy->max_sched_scan_ssids = PROBE_OPTION_MAX; | ||
245 | hw->wiphy->max_match_sets = IWL_SCAN_MAX_PROFILES; | ||
246 | /* we create the 802.11 header and zero length SSID IE. */ | ||
247 | hw->wiphy->max_sched_scan_ie_len = | ||
248 | SCAN_OFFLOAD_PROBE_REQ_SIZE - 24 - 2; | ||
249 | } | ||
250 | |||
242 | hw->wiphy->features |= NL80211_FEATURE_P2P_GO_CTWIN | | 251 | hw->wiphy->features |= NL80211_FEATURE_P2P_GO_CTWIN | |
243 | NL80211_FEATURE_P2P_GO_OPPPS; | 252 | NL80211_FEATURE_P2P_GO_OPPPS; |
244 | 253 | ||
@@ -1202,6 +1211,53 @@ static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw, | |||
1202 | mutex_unlock(&mvm->mutex); | 1211 | mutex_unlock(&mvm->mutex); |
1203 | } | 1212 | } |
1204 | 1213 | ||
1214 | static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw, | ||
1215 | struct ieee80211_vif *vif, | ||
1216 | struct cfg80211_sched_scan_request *req, | ||
1217 | struct ieee80211_sched_scan_ies *ies) | ||
1218 | { | ||
1219 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); | ||
1220 | int ret; | ||
1221 | |||
1222 | mutex_lock(&mvm->mutex); | ||
1223 | |||
1224 | if (mvm->scan_status != IWL_MVM_SCAN_NONE) { | ||
1225 | IWL_DEBUG_SCAN(mvm, | ||
1226 | "SCHED SCAN request during internal scan - abort\n"); | ||
1227 | ret = -EBUSY; | ||
1228 | goto out; | ||
1229 | } | ||
1230 | |||
1231 | mvm->scan_status = IWL_MVM_SCAN_SCHED; | ||
1232 | |||
1233 | ret = iwl_mvm_config_sched_scan(mvm, vif, req, ies); | ||
1234 | if (ret) | ||
1235 | goto err; | ||
1236 | |||
1237 | ret = iwl_mvm_config_sched_scan_profiles(mvm, req); | ||
1238 | if (ret) | ||
1239 | goto err; | ||
1240 | |||
1241 | ret = iwl_mvm_sched_scan_start(mvm, req); | ||
1242 | if (!ret) | ||
1243 | goto out; | ||
1244 | err: | ||
1245 | mvm->scan_status = IWL_MVM_SCAN_NONE; | ||
1246 | out: | ||
1247 | mutex_unlock(&mvm->mutex); | ||
1248 | return ret; | ||
1249 | } | ||
1250 | |||
1251 | static void iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw, | ||
1252 | struct ieee80211_vif *vif) | ||
1253 | { | ||
1254 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); | ||
1255 | |||
1256 | mutex_lock(&mvm->mutex); | ||
1257 | iwl_mvm_sched_scan_stop(mvm); | ||
1258 | mutex_unlock(&mvm->mutex); | ||
1259 | } | ||
1260 | |||
1205 | static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, | 1261 | static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, |
1206 | enum set_key_cmd cmd, | 1262 | enum set_key_cmd cmd, |
1207 | struct ieee80211_vif *vif, | 1263 | struct ieee80211_vif *vif, |
@@ -1671,6 +1727,8 @@ struct ieee80211_ops iwl_mvm_hw_ops = { | |||
1671 | .set_rts_threshold = iwl_mvm_mac_set_rts_threshold, | 1727 | .set_rts_threshold = iwl_mvm_mac_set_rts_threshold, |
1672 | .conf_tx = iwl_mvm_mac_conf_tx, | 1728 | .conf_tx = iwl_mvm_mac_conf_tx, |
1673 | .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx, | 1729 | .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx, |
1730 | .sched_scan_start = iwl_mvm_mac_sched_scan_start, | ||
1731 | .sched_scan_stop = iwl_mvm_mac_sched_scan_stop, | ||
1674 | .set_key = iwl_mvm_mac_set_key, | 1732 | .set_key = iwl_mvm_mac_set_key, |
1675 | .update_tkip_key = iwl_mvm_mac_update_tkip_key, | 1733 | .update_tkip_key = iwl_mvm_mac_update_tkip_key, |
1676 | .remain_on_channel = iwl_mvm_roc, | 1734 | .remain_on_channel = iwl_mvm_roc, |
diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index d7b0aeea380c..a56c1b8f5493 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h | |||
@@ -339,6 +339,7 @@ iwl_mvm_vif_from_mac80211(struct ieee80211_vif *vif) | |||
339 | enum iwl_scan_status { | 339 | enum iwl_scan_status { |
340 | IWL_MVM_SCAN_NONE, | 340 | IWL_MVM_SCAN_NONE, |
341 | IWL_MVM_SCAN_OS, | 341 | IWL_MVM_SCAN_OS, |
342 | IWL_MVM_SCAN_SCHED, | ||
342 | }; | 343 | }; |
343 | 344 | ||
344 | /** | 345 | /** |
@@ -696,6 +697,23 @@ int iwl_mvm_rx_scan_complete(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, | |||
696 | struct iwl_device_cmd *cmd); | 697 | struct iwl_device_cmd *cmd); |
697 | void iwl_mvm_cancel_scan(struct iwl_mvm *mvm); | 698 | void iwl_mvm_cancel_scan(struct iwl_mvm *mvm); |
698 | 699 | ||
700 | /* Scheduled scan */ | ||
701 | int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm, | ||
702 | struct iwl_rx_cmd_buffer *rxb, | ||
703 | struct iwl_device_cmd *cmd); | ||
704 | int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm, | ||
705 | struct ieee80211_vif *vif, | ||
706 | struct cfg80211_sched_scan_request *req, | ||
707 | struct ieee80211_sched_scan_ies *ies); | ||
708 | int iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm, | ||
709 | struct cfg80211_sched_scan_request *req); | ||
710 | int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, | ||
711 | struct cfg80211_sched_scan_request *req); | ||
712 | void iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm); | ||
713 | int iwl_mvm_rx_sched_scan_results(struct iwl_mvm *mvm, | ||
714 | struct iwl_rx_cmd_buffer *rxb, | ||
715 | struct iwl_device_cmd *cmd); | ||
716 | |||
699 | /* MVM debugfs */ | 717 | /* MVM debugfs */ |
700 | #ifdef CONFIG_IWLWIFI_DEBUGFS | 718 | #ifdef CONFIG_IWLWIFI_DEBUGFS |
701 | int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir); | 719 | int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir); |
diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 8cd5f32cfba3..a9ed45708bc2 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c | |||
@@ -224,6 +224,10 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { | |||
224 | 224 | ||
225 | RX_HANDLER(SCAN_REQUEST_CMD, iwl_mvm_rx_scan_response, false), | 225 | RX_HANDLER(SCAN_REQUEST_CMD, iwl_mvm_rx_scan_response, false), |
226 | RX_HANDLER(SCAN_COMPLETE_NOTIFICATION, iwl_mvm_rx_scan_complete, false), | 226 | RX_HANDLER(SCAN_COMPLETE_NOTIFICATION, iwl_mvm_rx_scan_complete, false), |
227 | RX_HANDLER(SCAN_OFFLOAD_COMPLETE, | ||
228 | iwl_mvm_rx_scan_offload_complete_notif, false), | ||
229 | RX_HANDLER(MATCH_FOUND_NOTIFICATION, iwl_mvm_rx_sched_scan_results, | ||
230 | false), | ||
227 | 231 | ||
228 | RX_HANDLER(RADIO_VERSION_NOTIFICATION, iwl_mvm_rx_radio_ver, false), | 232 | RX_HANDLER(RADIO_VERSION_NOTIFICATION, iwl_mvm_rx_radio_ver, false), |
229 | RX_HANDLER(CARD_STATE_NOTIFICATION, iwl_mvm_rx_card_state_notif, false), | 233 | RX_HANDLER(CARD_STATE_NOTIFICATION, iwl_mvm_rx_card_state_notif, false), |
@@ -266,6 +270,7 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = { | |||
266 | CMD(REMOVE_STA), | 270 | CMD(REMOVE_STA), |
267 | CMD(LQ_CMD), | 271 | CMD(LQ_CMD), |
268 | CMD(SCAN_OFFLOAD_CONFIG_CMD), | 272 | CMD(SCAN_OFFLOAD_CONFIG_CMD), |
273 | CMD(MATCH_FOUND_NOTIFICATION), | ||
269 | CMD(SCAN_OFFLOAD_REQUEST_CMD), | 274 | CMD(SCAN_OFFLOAD_REQUEST_CMD), |
270 | CMD(SCAN_OFFLOAD_ABORT_CMD), | 275 | CMD(SCAN_OFFLOAD_ABORT_CMD), |
271 | CMD(SCAN_OFFLOAD_COMPLETE), | 276 | CMD(SCAN_OFFLOAD_COMPLETE), |
@@ -717,6 +722,9 @@ static void iwl_mvm_nic_restart(struct iwl_mvm *mvm) | |||
717 | case IWL_MVM_SCAN_OS: | 722 | case IWL_MVM_SCAN_OS: |
718 | ieee80211_scan_completed(mvm->hw, true); | 723 | ieee80211_scan_completed(mvm->hw, true); |
719 | break; | 724 | break; |
725 | case IWL_MVM_SCAN_SCHED: | ||
726 | ieee80211_sched_scan_stopped(mvm->hw); | ||
727 | break; | ||
720 | } | 728 | } |
721 | 729 | ||
722 | if (mvm->restart_fw > 0) | 730 | if (mvm->restart_fw > 0) |
diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index 0dc626f34927..778dcd9320fe 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c | |||
@@ -391,6 +391,21 @@ int iwl_mvm_rx_scan_complete(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, | |||
391 | return 0; | 391 | return 0; |
392 | } | 392 | } |
393 | 393 | ||
394 | int iwl_mvm_rx_sched_scan_results(struct iwl_mvm *mvm, | ||
395 | struct iwl_rx_cmd_buffer *rxb, | ||
396 | struct iwl_device_cmd *cmd) | ||
397 | { | ||
398 | struct iwl_rx_packet *pkt = rxb_addr(rxb); | ||
399 | struct iwl_sched_scan_results *notif = (void *)pkt->data; | ||
400 | |||
401 | if (notif->client_bitmap & SCAN_CLIENT_SCHED_SCAN) { | ||
402 | IWL_DEBUG_SCAN(mvm, "Scheduled scan results\n"); | ||
403 | ieee80211_sched_scan_results(mvm->hw); | ||
404 | } | ||
405 | |||
406 | return 0; | ||
407 | } | ||
408 | |||
394 | static bool iwl_mvm_scan_abort_notif(struct iwl_notif_wait_data *notif_wait, | 409 | static bool iwl_mvm_scan_abort_notif(struct iwl_notif_wait_data *notif_wait, |
395 | struct iwl_rx_packet *pkt, void *data) | 410 | struct iwl_rx_packet *pkt, void *data) |
396 | { | 411 | { |
@@ -451,3 +466,406 @@ void iwl_mvm_cancel_scan(struct iwl_mvm *mvm) | |||
451 | out_remove_notif: | 466 | out_remove_notif: |
452 | iwl_remove_notification(&mvm->notif_wait, &wait_scan_abort); | 467 | iwl_remove_notification(&mvm->notif_wait, &wait_scan_abort); |
453 | } | 468 | } |
469 | |||
470 | int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm, | ||
471 | struct iwl_rx_cmd_buffer *rxb, | ||
472 | struct iwl_device_cmd *cmd) | ||
473 | { | ||
474 | struct iwl_rx_packet *pkt = rxb_addr(rxb); | ||
475 | struct iwl_scan_offload_complete *scan_notif = (void *)pkt->data; | ||
476 | |||
477 | IWL_DEBUG_SCAN(mvm, "Scheduled scan completed, status %s\n", | ||
478 | scan_notif->status == IWL_SCAN_OFFLOAD_COMPLETED ? | ||
479 | "completed" : "aborted"); | ||
480 | |||
481 | mvm->scan_status = IWL_MVM_SCAN_NONE; | ||
482 | ieee80211_sched_scan_stopped(mvm->hw); | ||
483 | |||
484 | return 0; | ||
485 | } | ||
486 | |||
487 | static void iwl_scan_offload_build_tx_cmd(struct iwl_mvm *mvm, | ||
488 | struct ieee80211_vif *vif, | ||
489 | struct ieee80211_sched_scan_ies *ies, | ||
490 | enum ieee80211_band band, | ||
491 | struct iwl_tx_cmd *cmd, | ||
492 | u8 *data) | ||
493 | { | ||
494 | u16 cmd_len; | ||
495 | |||
496 | cmd->tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL); | ||
497 | cmd->life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE); | ||
498 | cmd->sta_id = mvm->aux_sta.sta_id; | ||
499 | |||
500 | cmd->rate_n_flags = iwl_mvm_scan_rate_n_flags(mvm, band, false); | ||
501 | |||
502 | cmd_len = iwl_mvm_fill_probe_req((struct ieee80211_mgmt *)data, | ||
503 | vif->addr, | ||
504 | 1, NULL, 0, | ||
505 | ies->ie[band], ies->len[band], | ||
506 | SCAN_OFFLOAD_PROBE_REQ_SIZE); | ||
507 | cmd->len = cpu_to_le16(cmd_len); | ||
508 | } | ||
509 | |||
510 | static void iwl_build_scan_cmd(struct iwl_mvm *mvm, | ||
511 | struct ieee80211_vif *vif, | ||
512 | struct cfg80211_sched_scan_request *req, | ||
513 | struct iwl_scan_offload_cmd *scan) | ||
514 | { | ||
515 | scan->channel_count = | ||
516 | mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels + | ||
517 | mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels; | ||
518 | scan->quiet_time = cpu_to_le16(IWL_ACTIVE_QUIET_TIME); | ||
519 | scan->quiet_plcp_th = cpu_to_le16(IWL_PLCP_QUIET_THRESH); | ||
520 | scan->good_CRC_th = IWL_GOOD_CRC_TH_DEFAULT; | ||
521 | scan->rx_chain = iwl_mvm_scan_rx_chain(mvm); | ||
522 | scan->max_out_time = cpu_to_le32(200 * 1024); | ||
523 | scan->suspend_time = iwl_mvm_scan_suspend_time(vif); | ||
524 | scan->filter_flags |= cpu_to_le32(MAC_FILTER_ACCEPT_GRP | | ||
525 | MAC_FILTER_IN_BEACON); | ||
526 | scan->scan_type = cpu_to_le32(SCAN_TYPE_BACKGROUND); | ||
527 | scan->rep_count = cpu_to_le32(1); | ||
528 | } | ||
529 | |||
530 | static int iwl_ssid_exist(u8 *ssid, u8 ssid_len, struct iwl_ssid_ie *ssid_list) | ||
531 | { | ||
532 | int i; | ||
533 | |||
534 | for (i = 0; i < PROBE_OPTION_MAX; i++) { | ||
535 | if (!ssid_list[i].len) | ||
536 | break; | ||
537 | if (ssid_list[i].len == ssid_len && | ||
538 | !memcmp(ssid_list->ssid, ssid, ssid_len)) | ||
539 | return i; | ||
540 | } | ||
541 | return -1; | ||
542 | } | ||
543 | |||
544 | static void iwl_scan_offload_build_ssid(struct cfg80211_sched_scan_request *req, | ||
545 | struct iwl_scan_offload_cmd *scan, | ||
546 | u32 *ssid_bitmap) | ||
547 | { | ||
548 | int i, j; | ||
549 | int index; | ||
550 | |||
551 | /* | ||
552 | * copy SSIDs from match list. | ||
553 | * iwl_config_sched_scan_profiles() uses the order of these ssids to | ||
554 | * config match list. | ||
555 | */ | ||
556 | for (i = 0; i < req->n_match_sets && i < PROBE_OPTION_MAX; i++) { | ||
557 | scan->direct_scan[i].id = WLAN_EID_SSID; | ||
558 | scan->direct_scan[i].len = req->match_sets[i].ssid.ssid_len; | ||
559 | memcpy(scan->direct_scan[i].ssid, req->match_sets[i].ssid.ssid, | ||
560 | scan->direct_scan[i].len); | ||
561 | } | ||
562 | |||
563 | /* add SSIDs from scan SSID list */ | ||
564 | *ssid_bitmap = 0; | ||
565 | for (j = 0; j < req->n_ssids && i < PROBE_OPTION_MAX; j++) { | ||
566 | index = iwl_ssid_exist(req->ssids[j].ssid, | ||
567 | req->ssids[j].ssid_len, | ||
568 | scan->direct_scan); | ||
569 | if (index < 0) { | ||
570 | if (!req->ssids[j].ssid_len) | ||
571 | continue; | ||
572 | scan->direct_scan[i].id = WLAN_EID_SSID; | ||
573 | scan->direct_scan[i].len = req->ssids[j].ssid_len; | ||
574 | memcpy(scan->direct_scan[i].ssid, req->ssids[j].ssid, | ||
575 | scan->direct_scan[i].len); | ||
576 | *ssid_bitmap |= BIT(i + 1); | ||
577 | i++; | ||
578 | } else { | ||
579 | *ssid_bitmap |= BIT(index + 1); | ||
580 | } | ||
581 | } | ||
582 | } | ||
583 | |||
584 | static void iwl_build_channel_cfg(struct iwl_mvm *mvm, | ||
585 | struct cfg80211_sched_scan_request *req, | ||
586 | struct iwl_scan_channel_cfg *channels, | ||
587 | enum ieee80211_band band, | ||
588 | int *head, int *tail, | ||
589 | u32 ssid_bitmap) | ||
590 | { | ||
591 | struct ieee80211_supported_band *s_band; | ||
592 | int n_probes = req->n_ssids; | ||
593 | int n_channels = req->n_channels; | ||
594 | u8 active_dwell, passive_dwell; | ||
595 | int i, j, index = 0; | ||
596 | bool partial; | ||
597 | |||
598 | /* | ||
599 | * We have to configure all supported channels, even if we don't want to | ||
600 | * scan on them, but we have to send channels in the order that we want | ||
601 | * to scan. So add requested channels to head of the list and others to | ||
602 | * the end. | ||
603 | */ | ||
604 | active_dwell = iwl_mvm_get_active_dwell(band, n_probes); | ||
605 | passive_dwell = iwl_mvm_get_passive_dwell(band); | ||
606 | s_band = &mvm->nvm_data->bands[band]; | ||
607 | |||
608 | for (i = 0; i < s_band->n_channels && *head <= *tail; i++) { | ||
609 | partial = false; | ||
610 | for (j = 0; j < n_channels; j++) | ||
611 | if (s_band->channels[i].center_freq == | ||
612 | req->channels[j]->center_freq) { | ||
613 | index = *head; | ||
614 | (*head)++; | ||
615 | /* | ||
616 | * Channels that came with the request will be | ||
617 | * in partial scan . | ||
618 | */ | ||
619 | partial = true; | ||
620 | break; | ||
621 | } | ||
622 | if (!partial) { | ||
623 | index = *tail; | ||
624 | (*tail)--; | ||
625 | } | ||
626 | channels->channel_number[index] = | ||
627 | cpu_to_le16(ieee80211_frequency_to_channel( | ||
628 | s_band->channels[i].center_freq)); | ||
629 | channels->dwell_time[index][0] = active_dwell; | ||
630 | channels->dwell_time[index][1] = passive_dwell; | ||
631 | |||
632 | channels->iter_count[index] = cpu_to_le16(1); | ||
633 | channels->iter_interval[index] = 0; | ||
634 | |||
635 | if (!(s_band->channels[i].flags & IEEE80211_CHAN_PASSIVE_SCAN)) | ||
636 | channels->type[index] |= | ||
637 | cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_ACTIVE); | ||
638 | |||
639 | channels->type[index] |= | ||
640 | cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_FULL); | ||
641 | if (partial) | ||
642 | channels->type[index] |= | ||
643 | cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_PARTIAL); | ||
644 | |||
645 | if (s_band->channels[i].flags & IEEE80211_CHAN_NO_HT40) | ||
646 | channels->type[index] |= | ||
647 | cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_NARROW); | ||
648 | |||
649 | /* scan for all SSIDs from req->ssids */ | ||
650 | channels->type[index] |= cpu_to_le32(ssid_bitmap); | ||
651 | } | ||
652 | } | ||
653 | |||
654 | int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm, | ||
655 | struct ieee80211_vif *vif, | ||
656 | struct cfg80211_sched_scan_request *req, | ||
657 | struct ieee80211_sched_scan_ies *ies) | ||
658 | { | ||
659 | int supported_bands = 0; | ||
660 | int band_2ghz = mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels; | ||
661 | int band_5ghz = mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels; | ||
662 | int head = 0; | ||
663 | int tail = band_2ghz + band_5ghz; | ||
664 | u32 ssid_bitmap; | ||
665 | int cmd_len; | ||
666 | int ret; | ||
667 | |||
668 | struct iwl_scan_offload_cfg *scan_cfg; | ||
669 | struct iwl_host_cmd cmd = { | ||
670 | .id = SCAN_OFFLOAD_CONFIG_CMD, | ||
671 | .flags = CMD_SYNC, | ||
672 | }; | ||
673 | |||
674 | lockdep_assert_held(&mvm->mutex); | ||
675 | |||
676 | if (band_2ghz) | ||
677 | supported_bands++; | ||
678 | if (band_5ghz) | ||
679 | supported_bands++; | ||
680 | |||
681 | cmd_len = sizeof(struct iwl_scan_offload_cfg) + | ||
682 | supported_bands * SCAN_OFFLOAD_PROBE_REQ_SIZE; | ||
683 | |||
684 | scan_cfg = kzalloc(cmd_len, GFP_KERNEL); | ||
685 | if (!scan_cfg) | ||
686 | return -ENOMEM; | ||
687 | |||
688 | iwl_build_scan_cmd(mvm, vif, req, &scan_cfg->scan_cmd); | ||
689 | scan_cfg->scan_cmd.len = cpu_to_le16(cmd_len); | ||
690 | |||
691 | iwl_scan_offload_build_ssid(req, &scan_cfg->scan_cmd, &ssid_bitmap); | ||
692 | /* build tx frames for supported bands */ | ||
693 | if (band_2ghz) { | ||
694 | iwl_scan_offload_build_tx_cmd(mvm, vif, ies, | ||
695 | IEEE80211_BAND_2GHZ, | ||
696 | &scan_cfg->scan_cmd.tx_cmd[0], | ||
697 | scan_cfg->data); | ||
698 | iwl_build_channel_cfg(mvm, req, &scan_cfg->channel_cfg, | ||
699 | IEEE80211_BAND_2GHZ, &head, &tail, | ||
700 | ssid_bitmap); | ||
701 | } | ||
702 | if (band_5ghz) { | ||
703 | iwl_scan_offload_build_tx_cmd(mvm, vif, ies, | ||
704 | IEEE80211_BAND_5GHZ, | ||
705 | &scan_cfg->scan_cmd.tx_cmd[1], | ||
706 | scan_cfg->data + | ||
707 | SCAN_OFFLOAD_PROBE_REQ_SIZE); | ||
708 | iwl_build_channel_cfg(mvm, req, &scan_cfg->channel_cfg, | ||
709 | IEEE80211_BAND_5GHZ, &head, &tail, | ||
710 | ssid_bitmap); | ||
711 | } | ||
712 | |||
713 | cmd.data[0] = scan_cfg; | ||
714 | cmd.len[0] = cmd_len; | ||
715 | cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY; | ||
716 | |||
717 | IWL_DEBUG_SCAN(mvm, "Sending scheduled scan config\n"); | ||
718 | |||
719 | ret = iwl_mvm_send_cmd(mvm, &cmd); | ||
720 | kfree(scan_cfg); | ||
721 | return ret; | ||
722 | } | ||
723 | |||
724 | int iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm, | ||
725 | struct cfg80211_sched_scan_request *req) | ||
726 | { | ||
727 | struct iwl_scan_offload_profile *profile; | ||
728 | struct iwl_scan_offload_profile_cfg *profile_cfg; | ||
729 | struct iwl_scan_offload_blacklist *blacklist; | ||
730 | struct iwl_host_cmd cmd = { | ||
731 | .id = SCAN_OFFLOAD_UPDATE_PROFILES_CMD, | ||
732 | .flags = CMD_SYNC, | ||
733 | .len[1] = sizeof(*profile_cfg), | ||
734 | .dataflags[0] = IWL_HCMD_DFL_NOCOPY, | ||
735 | .dataflags[1] = IWL_HCMD_DFL_NOCOPY, | ||
736 | }; | ||
737 | int blacklist_len; | ||
738 | int i; | ||
739 | int ret; | ||
740 | |||
741 | if (WARN_ON(req->n_match_sets > IWL_SCAN_MAX_PROFILES)) | ||
742 | return -EIO; | ||
743 | |||
744 | if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_SHORT_BL) | ||
745 | blacklist_len = IWL_SCAN_SHORT_BLACKLIST_LEN; | ||
746 | else | ||
747 | blacklist_len = IWL_SCAN_MAX_BLACKLIST_LEN; | ||
748 | |||
749 | blacklist = kzalloc(sizeof(*blacklist) * blacklist_len, GFP_KERNEL); | ||
750 | if (!blacklist) | ||
751 | return -ENOMEM; | ||
752 | |||
753 | profile_cfg = kzalloc(sizeof(*profile_cfg), GFP_KERNEL); | ||
754 | if (!profile_cfg) { | ||
755 | ret = -ENOMEM; | ||
756 | goto free_blacklist; | ||
757 | } | ||
758 | |||
759 | cmd.data[0] = blacklist; | ||
760 | cmd.len[0] = sizeof(*blacklist) * blacklist_len; | ||
761 | cmd.data[1] = profile_cfg; | ||
762 | |||
763 | /* No blacklist configuration */ | ||
764 | |||
765 | profile_cfg->num_profiles = req->n_match_sets; | ||
766 | profile_cfg->active_clients = SCAN_CLIENT_SCHED_SCAN; | ||
767 | profile_cfg->pass_match = SCAN_CLIENT_SCHED_SCAN; | ||
768 | profile_cfg->match_notify = SCAN_CLIENT_SCHED_SCAN; | ||
769 | |||
770 | for (i = 0; i < req->n_match_sets; i++) { | ||
771 | profile = &profile_cfg->profiles[i]; | ||
772 | profile->ssid_index = i; | ||
773 | /* Support any cipher and auth algorithm */ | ||
774 | profile->unicast_cipher = 0xff; | ||
775 | profile->auth_alg = 0xff; | ||
776 | profile->network_type = IWL_NETWORK_TYPE_ANY; | ||
777 | profile->band_selection = IWL_SCAN_OFFLOAD_SELECT_ANY; | ||
778 | profile->client_bitmap = SCAN_CLIENT_SCHED_SCAN; | ||
779 | } | ||
780 | |||
781 | IWL_DEBUG_SCAN(mvm, "Sending scheduled scan profile config\n"); | ||
782 | |||
783 | ret = iwl_mvm_send_cmd(mvm, &cmd); | ||
784 | kfree(profile_cfg); | ||
785 | free_blacklist: | ||
786 | kfree(blacklist); | ||
787 | |||
788 | return ret; | ||
789 | } | ||
790 | |||
791 | int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, | ||
792 | struct cfg80211_sched_scan_request *req) | ||
793 | { | ||
794 | struct iwl_scan_offload_req scan_req = { | ||
795 | .watchdog = IWL_SCHED_SCAN_WATCHDOG, | ||
796 | |||
797 | .schedule_line[0].iterations = IWL_FAST_SCHED_SCAN_ITERATIONS, | ||
798 | .schedule_line[0].delay = req->interval / 1000, | ||
799 | .schedule_line[0].full_scan_mul = 1, | ||
800 | |||
801 | .schedule_line[1].iterations = 0xff, | ||
802 | .schedule_line[1].delay = req->interval / 1000, | ||
803 | .schedule_line[1].full_scan_mul = IWL_FULL_SCAN_MULTIPLIER, | ||
804 | }; | ||
805 | |||
806 | if (req->n_match_sets && req->match_sets[0].ssid.ssid_len) { | ||
807 | IWL_DEBUG_SCAN(mvm, | ||
808 | "Sending scheduled scan with filtering, filter len %d\n", | ||
809 | req->n_match_sets); | ||
810 | scan_req.flags |= | ||
811 | cpu_to_le16(IWL_SCAN_OFFLOAD_FLAG_FILTER_SSID); | ||
812 | } else { | ||
813 | IWL_DEBUG_SCAN(mvm, | ||
814 | "Sending Scheduled scan without filtering\n"); | ||
815 | } | ||
816 | |||
817 | return iwl_mvm_send_cmd_pdu(mvm, SCAN_OFFLOAD_REQUEST_CMD, CMD_SYNC, | ||
818 | sizeof(scan_req), &scan_req); | ||
819 | } | ||
820 | |||
821 | static int iwl_mvm_send_sched_scan_abort(struct iwl_mvm *mvm) | ||
822 | { | ||
823 | int ret; | ||
824 | struct iwl_host_cmd cmd = { | ||
825 | .id = SCAN_OFFLOAD_ABORT_CMD, | ||
826 | .flags = CMD_SYNC, | ||
827 | }; | ||
828 | u32 status; | ||
829 | |||
830 | /* Exit instantly with error when device is not ready | ||
831 | * to receive scan abort command or it does not perform | ||
832 | * scheduled scan currently */ | ||
833 | if (mvm->scan_status != IWL_MVM_SCAN_SCHED) | ||
834 | return -EIO; | ||
835 | |||
836 | ret = iwl_mvm_send_cmd_status(mvm, &cmd, &status); | ||
837 | if (ret) | ||
838 | return ret; | ||
839 | |||
840 | if (status != CAN_ABORT_STATUS) { | ||
841 | /* | ||
842 | * The scan abort will return 1 for success or | ||
843 | * 2 for "failure". A failure condition can be | ||
844 | * due to simply not being in an active scan which | ||
845 | * can occur if we send the scan abort before the | ||
846 | * microcode has notified us that a scan is completed. | ||
847 | */ | ||
848 | IWL_DEBUG_SCAN(mvm, "SCAN OFFLOAD ABORT ret %d.\n", status); | ||
849 | ret = -EIO; | ||
850 | } | ||
851 | |||
852 | return ret; | ||
853 | } | ||
854 | |||
855 | void iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm) | ||
856 | { | ||
857 | int ret; | ||
858 | |||
859 | lockdep_assert_held(&mvm->mutex); | ||
860 | |||
861 | if (mvm->scan_status != IWL_MVM_SCAN_SCHED) { | ||
862 | IWL_DEBUG_SCAN(mvm, "No offloaded scan to stop\n"); | ||
863 | return; | ||
864 | } | ||
865 | |||
866 | ret = iwl_mvm_send_sched_scan_abort(mvm); | ||
867 | if (ret) | ||
868 | IWL_DEBUG_SCAN(mvm, "Send stop offload scan failed %d\n", ret); | ||
869 | else | ||
870 | IWL_DEBUG_SCAN(mvm, "Successfully sent stop offload scan\n"); | ||
871 | } | ||