aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEliad Peller <eliad@wizery.com>2013-12-05 10:19:39 -0500
committerEmmanuel Grumbach <emmanuel.grumbach@intel.com>2014-02-03 15:23:41 -0500
commit37577fe2499a4d83c39910702959832baf589bab (patch)
treed97c34db553b0793ba44ab24ef35bb8525a1d8ce
parentb77f06d9eccb2edb1ef78c30eb6d38632ed4f196 (diff)
iwlwifi: mvm: get status on D0i3 exit
Schedule work to query the wakeup reasons, and disconnect in some cases (e.g. beacon loss). Signed-off-by: Eliad Peller <eliadx.peller@intel.com> Reviewed-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mac80211.c5
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mvm.h4
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/ops.c79
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/sta.c4
4 files changed, 88 insertions, 4 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index 827510ea7eab..59b5b7a80d12 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -547,6 +547,7 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
547 iwl_mvm_cleanup_iterator, mvm); 547 iwl_mvm_cleanup_iterator, mvm);
548 548
549 mvm->p2p_device_vif = NULL; 549 mvm->p2p_device_vif = NULL;
550 mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
550 551
551 iwl_mvm_reset_phy_ctxts(mvm); 552 iwl_mvm_reset_phy_ctxts(mvm);
552 memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table)); 553 memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table));
@@ -602,6 +603,7 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw)
602{ 603{
603 struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); 604 struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
604 605
606 flush_work(&mvm->d0i3_exit_work);
605 flush_work(&mvm->async_handlers_wk); 607 flush_work(&mvm->async_handlers_wk);
606 608
607 mutex_lock(&mvm->mutex); 609 mutex_lock(&mvm->mutex);
@@ -1216,6 +1218,9 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
1216 ret = iwl_mvm_rm_sta_id(mvm, vif, mvmvif->ap_sta_id); 1218 ret = iwl_mvm_rm_sta_id(mvm, vif, mvmvif->ap_sta_id);
1217 if (ret) 1219 if (ret)
1218 IWL_ERR(mvm, "failed to remove AP station\n"); 1220 IWL_ERR(mvm, "failed to remove AP station\n");
1221
1222 if (mvm->d0i3_ap_sta_id == mvmvif->ap_sta_id)
1223 mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
1219 mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; 1224 mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;
1220 /* remove quota for this interface */ 1225 /* remove quota for this interface */
1221 ret = iwl_mvm_update_quotas(mvm, NULL); 1226 ret = iwl_mvm_update_quotas(mvm, NULL);
diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h
index fe3896ca16e7..f3966078935c 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h
@@ -577,6 +577,10 @@ struct iwl_mvm {
577#endif 577#endif
578#endif 578#endif
579 579
580 /* d0i3 */
581 u8 d0i3_ap_sta_id;
582 struct work_struct d0i3_exit_work;
583
580 /* BT-Coex */ 584 /* BT-Coex */
581 u8 bt_kill_msk; 585 u8 bt_kill_msk;
582 struct iwl_bt_coex_profile_notif last_bt_notif; 586 struct iwl_bt_coex_profile_notif last_bt_notif;
diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c
index 12b81ca91954..4b7fa7a55c1c 100644
--- a/drivers/net/wireless/iwlwifi/mvm/ops.c
+++ b/drivers/net/wireless/iwlwifi/mvm/ops.c
@@ -326,6 +326,7 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
326 326
327/* this forward declaration can avoid to export the function */ 327/* this forward declaration can avoid to export the function */
328static void iwl_mvm_async_handlers_wk(struct work_struct *wk); 328static void iwl_mvm_async_handlers_wk(struct work_struct *wk);
329static void iwl_mvm_d0i3_exit_work(struct work_struct *wk);
329 330
330static u32 calc_min_backoff(struct iwl_trans *trans, const struct iwl_cfg *cfg) 331static u32 calc_min_backoff(struct iwl_trans *trans, const struct iwl_cfg *cfg)
331{ 332{
@@ -404,6 +405,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
404 INIT_WORK(&mvm->async_handlers_wk, iwl_mvm_async_handlers_wk); 405 INIT_WORK(&mvm->async_handlers_wk, iwl_mvm_async_handlers_wk);
405 INIT_WORK(&mvm->roc_done_wk, iwl_mvm_roc_done_wk); 406 INIT_WORK(&mvm->roc_done_wk, iwl_mvm_roc_done_wk);
406 INIT_WORK(&mvm->sta_drained_wk, iwl_mvm_sta_drained_wk); 407 INIT_WORK(&mvm->sta_drained_wk, iwl_mvm_sta_drained_wk);
408 INIT_WORK(&mvm->d0i3_exit_work, iwl_mvm_d0i3_exit_work);
407 409
408 SET_IEEE80211_DEV(mvm->hw, mvm->trans->dev); 410 SET_IEEE80211_DEV(mvm->hw, mvm->trans->dev);
409 411
@@ -819,10 +821,18 @@ static void iwl_mvm_cmd_queue_full(struct iwl_op_mode *op_mode)
819 iwl_mvm_nic_restart(mvm); 821 iwl_mvm_nic_restart(mvm);
820} 822}
821 823
824struct iwl_d0i3_iter_data {
825 struct iwl_mvm *mvm;
826 u8 ap_sta_id;
827 u8 vif_count;
828};
829
822static void iwl_mvm_enter_d0i3_iterator(void *_data, u8 *mac, 830static void iwl_mvm_enter_d0i3_iterator(void *_data, u8 *mac,
823 struct ieee80211_vif *vif) 831 struct ieee80211_vif *vif)
824{ 832{
825 struct iwl_mvm *mvm = _data; 833 struct iwl_d0i3_iter_data *data = _data;
834 struct iwl_mvm *mvm = data->mvm;
835 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
826 u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE; 836 u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE;
827 837
828 IWL_DEBUG_RPM(mvm, "entering D0i3 - vif %pM\n", vif->addr); 838 IWL_DEBUG_RPM(mvm, "entering D0i3 - vif %pM\n", vif->addr);
@@ -838,6 +848,8 @@ static void iwl_mvm_enter_d0i3_iterator(void *_data, u8 *mac,
838 * reconfigure them (we might want to use different 848 * reconfigure them (we might want to use different
839 * params later on, though). 849 * params later on, though).
840 */ 850 */
851 data->ap_sta_id = mvmvif->ap_sta_id;
852 data->vif_count++;
841} 853}
842 854
843static int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode) 855static int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode)
@@ -845,6 +857,9 @@ static int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode)
845 struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); 857 struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
846 u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE; 858 u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE;
847 int ret; 859 int ret;
860 struct iwl_d0i3_iter_data d0i3_iter_data = {
861 .mvm = mvm,
862 };
848 struct iwl_wowlan_config_cmd wowlan_config_cmd = { 863 struct iwl_wowlan_config_cmd wowlan_config_cmd = {
849 .wakeup_filter = cpu_to_le32(IWL_WOWLAN_WAKEUP_RX_FRAME | 864 .wakeup_filter = cpu_to_le32(IWL_WOWLAN_WAKEUP_RX_FRAME |
850 IWL_WOWLAN_WAKEUP_BEACON_MISS | 865 IWL_WOWLAN_WAKEUP_BEACON_MISS |
@@ -860,7 +875,13 @@ static int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode)
860 ieee80211_iterate_active_interfaces_atomic(mvm->hw, 875 ieee80211_iterate_active_interfaces_atomic(mvm->hw,
861 IEEE80211_IFACE_ITER_NORMAL, 876 IEEE80211_IFACE_ITER_NORMAL,
862 iwl_mvm_enter_d0i3_iterator, 877 iwl_mvm_enter_d0i3_iterator,
863 mvm); 878 &d0i3_iter_data);
879 if (d0i3_iter_data.vif_count == 1) {
880 mvm->d0i3_ap_sta_id = d0i3_iter_data.ap_sta_id;
881 } else {
882 WARN_ON_ONCE(d0i3_iter_data.vif_count > 1);
883 mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
884 }
864 885
865 ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, flags, 886 ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, flags,
866 sizeof(wowlan_config_cmd), 887 sizeof(wowlan_config_cmd),
@@ -887,6 +908,54 @@ static void iwl_mvm_exit_d0i3_iterator(void *_data, u8 *mac,
887 iwl_mvm_update_d0i3_power_mode(mvm, vif, false, flags); 908 iwl_mvm_update_d0i3_power_mode(mvm, vif, false, flags);
888} 909}
889 910
911static void iwl_mvm_d0i3_disconnect_iter(void *data, u8 *mac,
912 struct ieee80211_vif *vif)
913{
914 struct iwl_mvm *mvm = data;
915 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
916
917 if (vif->type == NL80211_IFTYPE_STATION && vif->bss_conf.assoc &&
918 mvm->d0i3_ap_sta_id == mvmvif->ap_sta_id)
919 ieee80211_connection_loss(vif);
920}
921
922static void iwl_mvm_d0i3_exit_work(struct work_struct *wk)
923{
924 struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, d0i3_exit_work);
925 struct iwl_host_cmd get_status_cmd = {
926 .id = WOWLAN_GET_STATUSES,
927 .flags = CMD_SYNC | CMD_HIGH_PRIO | CMD_WANT_SKB,
928 };
929 struct iwl_wowlan_status_v6 *status;
930 int ret;
931 u32 disconnection_reasons, wakeup_reasons;
932
933 mutex_lock(&mvm->mutex);
934 ret = iwl_mvm_send_cmd(mvm, &get_status_cmd);
935 if (ret)
936 goto out;
937
938 if (!get_status_cmd.resp_pkt)
939 goto out;
940
941 status = (void *)get_status_cmd.resp_pkt->data;
942 wakeup_reasons = le32_to_cpu(status->wakeup_reasons);
943
944 IWL_DEBUG_RPM(mvm, "wakeup reasons: 0x%x\n", wakeup_reasons);
945
946 disconnection_reasons =
947 IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON |
948 IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH;
949 if (wakeup_reasons & disconnection_reasons)
950 ieee80211_iterate_active_interfaces(
951 mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
952 iwl_mvm_d0i3_disconnect_iter, mvm);
953
954 iwl_free_resp(&get_status_cmd);
955out:
956 mutex_unlock(&mvm->mutex);
957}
958
890static int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode) 959static int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode)
891{ 960{
892 struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); 961 struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
@@ -898,13 +967,15 @@ static int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode)
898 967
899 ret = iwl_mvm_send_cmd_pdu(mvm, D0I3_END_CMD, flags, 0, NULL); 968 ret = iwl_mvm_send_cmd_pdu(mvm, D0I3_END_CMD, flags, 0, NULL);
900 if (ret) 969 if (ret)
901 return ret; 970 goto out;
902 971
903 ieee80211_iterate_active_interfaces_atomic(mvm->hw, 972 ieee80211_iterate_active_interfaces_atomic(mvm->hw,
904 IEEE80211_IFACE_ITER_NORMAL, 973 IEEE80211_IFACE_ITER_NORMAL,
905 iwl_mvm_exit_d0i3_iterator, 974 iwl_mvm_exit_d0i3_iterator,
906 mvm); 975 mvm);
907 return 0; 976out:
977 schedule_work(&mvm->d0i3_exit_work);
978 return ret;
908} 979}
909 980
910static const struct iwl_op_mode_ops iwl_mvm_ops = { 981static const struct iwl_op_mode_ops iwl_mvm_ops = {
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c
index af94f75c3999..fb416c5d4a63 100644
--- a/drivers/net/wireless/iwlwifi/mvm/sta.c
+++ b/drivers/net/wireless/iwlwifi/mvm/sta.c
@@ -522,6 +522,10 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm,
522 522
523 /* unassoc - go ahead - remove the AP STA now */ 523 /* unassoc - go ahead - remove the AP STA now */
524 mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; 524 mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;
525
526 /* clear d0i3_ap_sta_id if no longer relevant */
527 if (mvm->d0i3_ap_sta_id == mvm_sta->sta_id)
528 mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
525 } 529 }
526 530
527 /* 531 /*