diff options
author | Andrei Otcheretianski <andrei.otcheretianski@intel.com> | 2013-07-21 10:23:59 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2013-08-16 06:12:24 -0400 |
commit | a20fd398666dbf5cdee3fe97722350cc10619c84 (patch) | |
tree | a70433b643ebb44a00a611b1c297e901144435a4 /drivers/net/wireless/iwlwifi | |
parent | eafe25e0afaf45a4e38f9b3560ac774a2395c695 (diff) |
iwlwifi: mvm: Implement CQM offloading
Use beacon statistics notification to track RSSI.
Notify mac80211 when the tresholds are crossed.
The roaming treshold is configured to be
equal to cqm_thold. If the beacon filtering command
is not supported by fw fall back and use mac80211
mechanism.
Signed-off-by: Andrei Otcheretianski <andrei.otcheretianski@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'drivers/net/wireless/iwlwifi')
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/debugfs.c | 2 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/fw-api.h | 2 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/mac80211.c | 22 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/mvm.h | 20 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/power.c | 37 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/rx.c | 61 |
6 files changed, 133 insertions, 11 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c index 14811a583d2b..c67b17aa7dfb 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c | |||
@@ -920,7 +920,7 @@ static ssize_t iwl_dbgfs_bf_params_read(struct file *file, | |||
920 | }; | 920 | }; |
921 | 921 | ||
922 | iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd); | 922 | iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd); |
923 | if (mvmvif->bf_enabled) | 923 | if (mvmvif->bf_data.bf_enabled) |
924 | cmd.bf_enable_beacon_filter = cpu_to_le32(1); | 924 | cmd.bf_enable_beacon_filter = cpu_to_le32(1); |
925 | else | 925 | else |
926 | cmd.bf_enable_beacon_filter = 0; | 926 | cmd.bf_enable_beacon_filter = 0; |
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h index b1047102ea47..66264cc5a016 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h | |||
@@ -1321,7 +1321,7 @@ struct mvm_statistics_general { | |||
1321 | struct mvm_statistics_general_common common; | 1321 | struct mvm_statistics_general_common common; |
1322 | __le32 beacon_filtered; | 1322 | __le32 beacon_filtered; |
1323 | __le32 missed_beacons; | 1323 | __le32 missed_beacons; |
1324 | __s8 beacon_filter_everage_energy; | 1324 | __s8 beacon_filter_average_energy; |
1325 | __s8 beacon_filter_reason; | 1325 | __s8 beacon_filter_reason; |
1326 | __s8 beacon_filter_current_energy; | 1326 | __s8 beacon_filter_current_energy; |
1327 | __s8 beacon_filter_reserved; | 1327 | __s8 beacon_filter_reserved; |
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 66803b99cca8..692d2ea211e8 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c | |||
@@ -575,7 +575,8 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, | |||
575 | vif->type == NL80211_IFTYPE_STATION && !vif->p2p && | 575 | vif->type == NL80211_IFTYPE_STATION && !vif->p2p && |
576 | mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BF_UPDATED){ | 576 | mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BF_UPDATED){ |
577 | mvm->bf_allowed_vif = mvmvif; | 577 | mvm->bf_allowed_vif = mvmvif; |
578 | vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER; | 578 | vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | |
579 | IEEE80211_VIF_SUPPORTS_CQM_RSSI; | ||
579 | } | 580 | } |
580 | 581 | ||
581 | /* | 582 | /* |
@@ -615,7 +616,8 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, | |||
615 | out_free_bf: | 616 | out_free_bf: |
616 | if (mvm->bf_allowed_vif == mvmvif) { | 617 | if (mvm->bf_allowed_vif == mvmvif) { |
617 | mvm->bf_allowed_vif = NULL; | 618 | mvm->bf_allowed_vif = NULL; |
618 | vif->driver_flags &= ~IEEE80211_VIF_BEACON_FILTER; | 619 | vif->driver_flags &= ~(IEEE80211_VIF_BEACON_FILTER | |
620 | IEEE80211_VIF_SUPPORTS_CQM_RSSI); | ||
619 | } | 621 | } |
620 | out_remove_mac: | 622 | out_remove_mac: |
621 | mvmvif->phy_ctxt = NULL; | 623 | mvmvif->phy_ctxt = NULL; |
@@ -681,7 +683,8 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, | |||
681 | 683 | ||
682 | if (mvm->bf_allowed_vif == mvmvif) { | 684 | if (mvm->bf_allowed_vif == mvmvif) { |
683 | mvm->bf_allowed_vif = NULL; | 685 | mvm->bf_allowed_vif = NULL; |
684 | vif->driver_flags &= ~IEEE80211_VIF_BEACON_FILTER; | 686 | vif->driver_flags &= ~(IEEE80211_VIF_BEACON_FILTER | |
687 | IEEE80211_VIF_SUPPORTS_CQM_RSSI); | ||
685 | } | 688 | } |
686 | 689 | ||
687 | iwl_mvm_vif_dbgfs_clean(mvm, vif); | 690 | iwl_mvm_vif_dbgfs_clean(mvm, vif); |
@@ -799,6 +802,10 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, | |||
799 | if (ret) | 802 | if (ret) |
800 | IWL_ERR(mvm, "failed to update quotas\n"); | 803 | IWL_ERR(mvm, "failed to update quotas\n"); |
801 | } | 804 | } |
805 | |||
806 | /* reset rssi values */ | ||
807 | mvmvif->bf_data.ave_beacon_signal = 0; | ||
808 | |||
802 | if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD)) { | 809 | if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD)) { |
803 | /* Workaround for FW bug, otherwise FW disables device | 810 | /* Workaround for FW bug, otherwise FW disables device |
804 | * power save upon disassociation | 811 | * power save upon disassociation |
@@ -825,6 +832,15 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, | |||
825 | bss_conf->txpower); | 832 | bss_conf->txpower); |
826 | iwl_mvm_set_tx_power(mvm, vif, bss_conf->txpower); | 833 | iwl_mvm_set_tx_power(mvm, vif, bss_conf->txpower); |
827 | } | 834 | } |
835 | |||
836 | if (changes & BSS_CHANGED_CQM) { | ||
837 | IWL_DEBUG_MAC80211(mvm, "cqm info_changed"); | ||
838 | /* reset cqm events tracking */ | ||
839 | mvmvif->bf_data.last_cqm_event = 0; | ||
840 | ret = iwl_mvm_update_beacon_filter(mvm, vif); | ||
841 | if (ret) | ||
842 | IWL_ERR(mvm, "failed to update CQM thresholds\n"); | ||
843 | } | ||
828 | } | 844 | } |
829 | 845 | ||
830 | static int iwl_mvm_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif) | 846 | static int iwl_mvm_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif) |
diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 014d77931c56..ee46dea69509 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h | |||
@@ -233,6 +233,21 @@ enum iwl_mvm_smps_type_request { | |||
233 | }; | 233 | }; |
234 | 234 | ||
235 | /** | 235 | /** |
236 | * struct iwl_mvm_vif_bf_data - beacon filtering related data | ||
237 | * @bf_enabled: indicates if beacon filtering is enabled | ||
238 | * @ba_enabled: indicated if beacon abort is enabled | ||
239 | * @last_beacon_signal: last beacon rssi signal in dbm | ||
240 | * @ave_beacon_signal: average beacon signal | ||
241 | * @last_cqm_event: rssi of the last cqm event | ||
242 | */ | ||
243 | struct iwl_mvm_vif_bf_data { | ||
244 | bool bf_enabled; | ||
245 | bool ba_enabled; | ||
246 | s8 ave_beacon_signal; | ||
247 | s8 last_cqm_event; | ||
248 | }; | ||
249 | |||
250 | /** | ||
236 | * struct iwl_mvm_vif - data per Virtual Interface, it is a MAC context | 251 | * struct iwl_mvm_vif - data per Virtual Interface, it is a MAC context |
237 | * @id: between 0 and 3 | 252 | * @id: between 0 and 3 |
238 | * @color: to solve races upon MAC addition and removal | 253 | * @color: to solve races upon MAC addition and removal |
@@ -257,8 +272,7 @@ struct iwl_mvm_vif { | |||
257 | bool uploaded; | 272 | bool uploaded; |
258 | bool ap_active; | 273 | bool ap_active; |
259 | bool monitor_active; | 274 | bool monitor_active; |
260 | /* indicate whether beacon filtering is enabled */ | 275 | struct iwl_mvm_vif_bf_data bf_data; |
261 | bool bf_enabled; | ||
262 | 276 | ||
263 | u32 ap_beacon_time; | 277 | u32 ap_beacon_time; |
264 | 278 | ||
@@ -758,6 +772,8 @@ int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm, | |||
758 | struct iwl_beacon_filter_cmd *cmd); | 772 | struct iwl_beacon_filter_cmd *cmd); |
759 | int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm, | 773 | int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm, |
760 | struct ieee80211_vif *vif, bool enable); | 774 | struct ieee80211_vif *vif, bool enable); |
775 | int iwl_mvm_update_beacon_filter(struct iwl_mvm *mvm, | ||
776 | struct ieee80211_vif *vif); | ||
761 | 777 | ||
762 | /* SMPS */ | 778 | /* SMPS */ |
763 | void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif, | 779 | void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif, |
diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/iwlwifi/mvm/power.c index 9c9b5bafb577..a5529b85de8b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/power.c +++ b/drivers/net/wireless/iwlwifi/mvm/power.c | |||
@@ -110,6 +110,23 @@ int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm, | |||
110 | return ret; | 110 | return ret; |
111 | } | 111 | } |
112 | 112 | ||
113 | static | ||
114 | void iwl_mvm_beacon_filter_set_cqm_params(struct iwl_mvm *mvm, | ||
115 | struct ieee80211_vif *vif, | ||
116 | struct iwl_beacon_filter_cmd *cmd) | ||
117 | { | ||
118 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | ||
119 | |||
120 | if (vif->bss_conf.cqm_rssi_thold) { | ||
121 | cmd->bf_energy_delta = | ||
122 | cpu_to_le32(vif->bss_conf.cqm_rssi_hyst); | ||
123 | /* fw uses an absolute value for this */ | ||
124 | cmd->bf_roaming_state = | ||
125 | cpu_to_le32(-vif->bss_conf.cqm_rssi_thold); | ||
126 | } | ||
127 | cmd->ba_enable_beacon_abort = cpu_to_le32(mvmvif->bf_data.ba_enabled); | ||
128 | } | ||
129 | |||
113 | int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm, | 130 | int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm, |
114 | struct ieee80211_vif *vif, bool enable) | 131 | struct ieee80211_vif *vif, bool enable) |
115 | { | 132 | { |
@@ -120,12 +137,14 @@ int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm, | |||
120 | .ba_enable_beacon_abort = cpu_to_le32(enable), | 137 | .ba_enable_beacon_abort = cpu_to_le32(enable), |
121 | }; | 138 | }; |
122 | 139 | ||
123 | if (!mvmvif->bf_enabled) | 140 | if (!mvmvif->bf_data.bf_enabled) |
124 | return 0; | 141 | return 0; |
125 | 142 | ||
126 | if (mvm->cur_ucode == IWL_UCODE_WOWLAN) | 143 | if (mvm->cur_ucode == IWL_UCODE_WOWLAN) |
127 | cmd.ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_D3); | 144 | cmd.ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_D3); |
128 | 145 | ||
146 | mvmvif->bf_data.ba_enabled = enable; | ||
147 | iwl_mvm_beacon_filter_set_cqm_params(mvm, vif, &cmd); | ||
129 | iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd); | 148 | iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd); |
130 | return iwl_mvm_beacon_filter_send_cmd(mvm, &cmd); | 149 | return iwl_mvm_beacon_filter_send_cmd(mvm, &cmd); |
131 | } | 150 | } |
@@ -510,11 +529,12 @@ int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, | |||
510 | vif->type != NL80211_IFTYPE_STATION || vif->p2p) | 529 | vif->type != NL80211_IFTYPE_STATION || vif->p2p) |
511 | return 0; | 530 | return 0; |
512 | 531 | ||
532 | iwl_mvm_beacon_filter_set_cqm_params(mvm, vif, &cmd); | ||
513 | iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd); | 533 | iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd); |
514 | ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd); | 534 | ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd); |
515 | 535 | ||
516 | if (!ret) | 536 | if (!ret) |
517 | mvmvif->bf_enabled = true; | 537 | mvmvif->bf_data.bf_enabled = true; |
518 | 538 | ||
519 | return ret; | 539 | return ret; |
520 | } | 540 | } |
@@ -533,11 +553,22 @@ int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, | |||
533 | ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd); | 553 | ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd); |
534 | 554 | ||
535 | if (!ret) | 555 | if (!ret) |
536 | mvmvif->bf_enabled = false; | 556 | mvmvif->bf_data.bf_enabled = false; |
537 | 557 | ||
538 | return ret; | 558 | return ret; |
539 | } | 559 | } |
540 | 560 | ||
561 | int iwl_mvm_update_beacon_filter(struct iwl_mvm *mvm, | ||
562 | struct ieee80211_vif *vif) | ||
563 | { | ||
564 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | ||
565 | |||
566 | if (!mvmvif->bf_data.bf_enabled) | ||
567 | return 0; | ||
568 | |||
569 | return iwl_mvm_enable_beacon_filter(mvm, vif); | ||
570 | } | ||
571 | |||
541 | const struct iwl_mvm_power_ops pm_mac_ops = { | 572 | const struct iwl_mvm_power_ops pm_mac_ops = { |
542 | .power_update_mode = iwl_mvm_power_mac_update_mode, | 573 | .power_update_mode = iwl_mvm_power_mac_update_mode, |
543 | .power_disable = iwl_mvm_power_mac_disable, | 574 | .power_disable = iwl_mvm_power_mac_disable, |
diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c index ee6547d22287..2a8cb5a60535 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/iwlwifi/mvm/rx.c | |||
@@ -396,11 +396,62 @@ static void iwl_mvm_update_rx_statistics(struct iwl_mvm *mvm, | |||
396 | memcpy(&mvm->rx_stats, &stats->rx, sizeof(struct mvm_statistics_rx)); | 396 | memcpy(&mvm->rx_stats, &stats->rx, sizeof(struct mvm_statistics_rx)); |
397 | } | 397 | } |
398 | 398 | ||
399 | struct iwl_mvm_stat_data { | ||
400 | struct iwl_notif_statistics *stats; | ||
401 | struct iwl_mvm *mvm; | ||
402 | }; | ||
403 | |||
404 | static void iwl_mvm_stat_iterator(void *_data, u8 *mac, | ||
405 | struct ieee80211_vif *vif) | ||
406 | { | ||
407 | struct iwl_mvm_stat_data *data = _data; | ||
408 | struct iwl_notif_statistics *stats = data->stats; | ||
409 | struct iwl_mvm *mvm = data->mvm; | ||
410 | int sig = -stats->general.beacon_filter_average_energy; | ||
411 | int last_event; | ||
412 | int thold = vif->bss_conf.cqm_rssi_thold; | ||
413 | int hyst = vif->bss_conf.cqm_rssi_hyst; | ||
414 | u16 id = le32_to_cpu(stats->rx.general.mac_id); | ||
415 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | ||
416 | |||
417 | if (mvmvif->id != id) | ||
418 | return; | ||
419 | |||
420 | if (vif->type != NL80211_IFTYPE_STATION) | ||
421 | return; | ||
422 | |||
423 | mvmvif->bf_data.ave_beacon_signal = sig; | ||
424 | |||
425 | if (!(vif->driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)) | ||
426 | return; | ||
427 | |||
428 | /* CQM Notification */ | ||
429 | last_event = mvmvif->bf_data.last_cqm_event; | ||
430 | if (thold && sig < thold && (last_event == 0 || | ||
431 | sig < last_event - hyst)) { | ||
432 | mvmvif->bf_data.last_cqm_event = sig; | ||
433 | IWL_DEBUG_RX(mvm, "cqm_iterator cqm low %d\n", | ||
434 | sig); | ||
435 | ieee80211_cqm_rssi_notify( | ||
436 | vif, | ||
437 | NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, | ||
438 | GFP_KERNEL); | ||
439 | } else if (sig > thold && | ||
440 | (last_event == 0 || sig > last_event + hyst)) { | ||
441 | mvmvif->bf_data.last_cqm_event = sig; | ||
442 | IWL_DEBUG_RX(mvm, "cqm_iterator cqm high %d\n", | ||
443 | sig); | ||
444 | ieee80211_cqm_rssi_notify( | ||
445 | vif, | ||
446 | NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, | ||
447 | GFP_KERNEL); | ||
448 | } | ||
449 | } | ||
450 | |||
399 | /* | 451 | /* |
400 | * iwl_mvm_rx_statistics - STATISTICS_NOTIFICATION handler | 452 | * iwl_mvm_rx_statistics - STATISTICS_NOTIFICATION handler |
401 | * | 453 | * |
402 | * TODO: This handler is implemented partially. | 454 | * TODO: This handler is implemented partially. |
403 | * It only gets the NIC's temperature. | ||
404 | */ | 455 | */ |
405 | int iwl_mvm_rx_statistics(struct iwl_mvm *mvm, | 456 | int iwl_mvm_rx_statistics(struct iwl_mvm *mvm, |
406 | struct iwl_rx_cmd_buffer *rxb, | 457 | struct iwl_rx_cmd_buffer *rxb, |
@@ -409,6 +460,10 @@ int iwl_mvm_rx_statistics(struct iwl_mvm *mvm, | |||
409 | struct iwl_rx_packet *pkt = rxb_addr(rxb); | 460 | struct iwl_rx_packet *pkt = rxb_addr(rxb); |
410 | struct iwl_notif_statistics *stats = (void *)&pkt->data; | 461 | struct iwl_notif_statistics *stats = (void *)&pkt->data; |
411 | struct mvm_statistics_general_common *common = &stats->general.common; | 462 | struct mvm_statistics_general_common *common = &stats->general.common; |
463 | struct iwl_mvm_stat_data data = { | ||
464 | .stats = stats, | ||
465 | .mvm = mvm, | ||
466 | }; | ||
412 | 467 | ||
413 | if (mvm->temperature != le32_to_cpu(common->temperature)) { | 468 | if (mvm->temperature != le32_to_cpu(common->temperature)) { |
414 | mvm->temperature = le32_to_cpu(common->temperature); | 469 | mvm->temperature = le32_to_cpu(common->temperature); |
@@ -416,5 +471,9 @@ int iwl_mvm_rx_statistics(struct iwl_mvm *mvm, | |||
416 | } | 471 | } |
417 | iwl_mvm_update_rx_statistics(mvm, stats); | 472 | iwl_mvm_update_rx_statistics(mvm, stats); |
418 | 473 | ||
474 | ieee80211_iterate_active_interfaces(mvm->hw, | ||
475 | IEEE80211_IFACE_ITER_NORMAL, | ||
476 | iwl_mvm_stat_iterator, | ||
477 | &data); | ||
419 | return 0; | 478 | return 0; |
420 | } | 479 | } |