diff options
Diffstat (limited to 'drivers/net/wireless/intel/iwlwifi/mvm/d3.c')
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 141 |
1 files changed, 114 insertions, 27 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 9e51843764ca..6ac40727541e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Linux Wireless <linuxwifi@intel.com> * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE @@ -104,9 +104,13 @@ void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw, struct inet6_ifaddr *ifa; int idx = 0; + memset(mvmvif->tentative_addrs, 0, sizeof(mvmvif->tentative_addrs)); + read_lock_bh(&idev->lock); list_for_each_entry(ifa, &idev->addr_list, if_list) { mvmvif->target_ipv6_addrs[idx] = ifa->addr; + if (ifa->flags & IFA_F_TENTATIVE) + __set_bit(idx, mvmvif->tentative_addrs); idx++; if (idx >= IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX) break; @@ -775,6 +779,9 @@ static int iwl_mvm_switch_to_d3(struct iwl_mvm *mvm) */ set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); + /* the fw is reset, so all the keys are cleared */ + memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table)); + mvm->ptk_ivlen = 0; mvm->ptk_icvlen = 0; mvm->ptk_ivlen = 0; @@ -797,6 +804,8 @@ iwl_mvm_get_wowlan_config(struct iwl_mvm *mvm, wowlan_config_cmd->is_11n_connection = ap_sta->ht_cap.ht_supported; + wowlan_config_cmd->flags = ENABLE_L3_FILTERING | + ENABLE_NBNS_FILTERING | ENABLE_DHCP_FILTERING; /* Query the last used seqno and set it */ ret = iwl_mvm_get_last_nonqos_seq(mvm, vif); @@ -846,15 +855,38 @@ iwl_mvm_get_wowlan_config(struct iwl_mvm *mvm, return 0; } +static void +iwl_mvm_iter_d0i3_ap_keys(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + void (*iter)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, + void *data), + void *data) +{ + struct ieee80211_sta *ap_sta; + + rcu_read_lock(); + + ap_sta = rcu_dereference(mvm->fw_id_to_mac_id[mvm->d0i3_ap_sta_id]); + if (IS_ERR_OR_NULL(ap_sta)) + goto out; + + ieee80211_iter_keys_rcu(mvm->hw, vif, iter, data); +out: + rcu_read_unlock(); +} + int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - bool configure_keys, + bool d0i3, u32 cmd_flags) { struct iwl_wowlan_kek_kck_material_cmd kek_kck_cmd = {}; struct iwl_wowlan_tkip_params_cmd tkip_cmd = {}; struct wowlan_key_data key_data = { - .configure_keys = configure_keys, + .configure_keys = !d0i3, .use_rsc_tsc = false, .tkip = &tkip_cmd, .use_tkip = false, @@ -867,15 +899,28 @@ int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm, return -ENOMEM; /* - * Note that currently we don't propagate cmd_flags - * to the iterator. In case of key_data.configure_keys, - * all the configured commands are SYNC, and - * iwl_mvm_wowlan_program_keys() will take care of - * locking/unlocking mvm->mutex. + * if we have to configure keys, call ieee80211_iter_keys(), + * as we need non-atomic context in order to take the + * required locks. + * for the d0i3 we can't use ieee80211_iter_keys(), as + * taking (almost) any mutex might result in deadlock. */ - ieee80211_iter_keys(mvm->hw, vif, - iwl_mvm_wowlan_program_keys, - &key_data); + if (!d0i3) { + /* + * Note that currently we don't propagate cmd_flags + * to the iterator. In case of key_data.configure_keys, + * all the configured commands are SYNC, and + * iwl_mvm_wowlan_program_keys() will take care of + * locking/unlocking mvm->mutex. + */ + ieee80211_iter_keys(mvm->hw, vif, + iwl_mvm_wowlan_program_keys, + &key_data); + } else { + iwl_mvm_iter_d0i3_ap_keys(mvm, vif, + iwl_mvm_wowlan_program_keys, + &key_data); + } if (key_data.error) { ret = -EIO; @@ -900,7 +945,8 @@ int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm, goto out; } - if (mvmvif->rekey_data.valid) { + /* configure rekey data only if offloaded rekey is supported (d3) */ + if (mvmvif->rekey_data.valid && !d0i3) { memset(&kek_kck_cmd, 0, sizeof(kek_kck_cmd)); memcpy(kek_kck_cmd.kck, mvmvif->rekey_data.kck, NL80211_KCK_LEN); @@ -917,6 +963,7 @@ int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm, if (ret) goto out; } + ret = 0; out: kfree(key_data.rsc_tsc); return ret; @@ -946,8 +993,11 @@ iwl_mvm_wowlan_config(struct iwl_mvm *mvm, * that isn't really a problem though. */ mutex_unlock(&mvm->mutex); - iwl_mvm_wowlan_config_key_params(mvm, vif, true, CMD_ASYNC); + ret = iwl_mvm_wowlan_config_key_params(mvm, vif, false, + CMD_ASYNC); mutex_lock(&mvm->mutex); + if (ret) + return ret; } ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, 0, @@ -960,7 +1010,7 @@ iwl_mvm_wowlan_config(struct iwl_mvm *mvm, if (ret) return ret; - ret = iwl_mvm_send_proto_offload(mvm, vif, false, 0); + ret = iwl_mvm_send_proto_offload(mvm, vif, false, true, 0); if (ret) return ret; @@ -1179,19 +1229,20 @@ remove_notif: int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_trans *trans = mvm->trans; int ret; /* make sure the d0i3 exit work is not pending */ flush_work(&mvm->d0i3_exit_work); - ret = iwl_trans_suspend(mvm->trans); + ret = iwl_trans_suspend(trans); if (ret) return ret; - mvm->trans->wowlan_d0i3 = wowlan->any; - if (mvm->trans->wowlan_d0i3) { - /* 'any' trigger means d0i3 usage */ - if (mvm->trans->d0i3_mode == IWL_D0I3_MODE_ON_SUSPEND) { + if (wowlan->any) { + trans->system_pm_mode = IWL_PLAT_PM_MODE_D0I3; + + if (iwl_mvm_enter_d0i3_on_suspend(mvm)) { ret = iwl_mvm_enter_d0i3_sync(mvm); if (ret) @@ -1202,11 +1253,13 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) __set_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags); mutex_unlock(&mvm->d0i3_suspend_mutex); - iwl_trans_d3_suspend(mvm->trans, false); + iwl_trans_d3_suspend(trans, false); return 0; } + trans->system_pm_mode = IWL_PLAT_PM_MODE_D3; + return __iwl_mvm_suspend(hw, wowlan, false); } @@ -1711,6 +1764,29 @@ out_unlock: return false; } +void iwl_mvm_d0i3_update_keys(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_wowlan_status *status) +{ + struct iwl_mvm_d3_gtk_iter_data gtkdata = { + .status = status, + }; + + /* + * rekey handling requires taking locks that can't be taken now. + * however, d0i3 doesn't offload rekey, so we're fine. + */ + if (WARN_ON_ONCE(status->num_of_gtk_rekeys)) + return; + + /* find last GTK that we used initially, if any */ + gtkdata.find_phase = true; + iwl_mvm_iter_d0i3_ap_keys(mvm, vif, iwl_mvm_d3_update_keys, >kdata); + + gtkdata.find_phase = false; + iwl_mvm_iter_d0i3_ap_keys(mvm, vif, iwl_mvm_d3_update_keys, >kdata); +} + struct iwl_mvm_nd_query_results { u32 matched_profiles; struct iwl_scan_offload_profile_match matches[IWL_SCAN_MAX_PROFILES]; @@ -1969,8 +2045,9 @@ static int iwl_mvm_resume_d0i3(struct iwl_mvm *mvm) { bool exit_now; enum iwl_d3_status d3_status; + struct iwl_trans *trans = mvm->trans; - iwl_trans_d3_resume(mvm->trans, &d3_status, false); + iwl_trans_d3_resume(trans, &d3_status, false); /* * make sure to clear D0I3_DEFER_WAKEUP before @@ -1987,9 +2064,9 @@ static int iwl_mvm_resume_d0i3(struct iwl_mvm *mvm) _iwl_mvm_exit_d0i3(mvm); } - iwl_trans_resume(mvm->trans); + iwl_trans_resume(trans); - if (mvm->trans->d0i3_mode == IWL_D0I3_MODE_ON_SUSPEND) { + if (iwl_mvm_enter_d0i3_on_suspend(mvm)) { int ret = iwl_mvm_exit_d0i3(mvm->hw->priv); if (ret) @@ -2005,12 +2082,16 @@ static int iwl_mvm_resume_d0i3(struct iwl_mvm *mvm) int iwl_mvm_resume(struct ieee80211_hw *hw) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + int ret; - /* 'any' trigger means d0i3 was used */ - if (hw->wiphy->wowlan_config->any) - return iwl_mvm_resume_d0i3(mvm); + if (mvm->trans->system_pm_mode == IWL_PLAT_PM_MODE_D0I3) + ret = iwl_mvm_resume_d0i3(mvm); else - return iwl_mvm_resume_d3(mvm); + ret = iwl_mvm_resume_d3(mvm); + + mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED; + + return ret; } void iwl_mvm_set_wakeup(struct ieee80211_hw *hw, bool enabled) @@ -2034,6 +2115,8 @@ static int iwl_mvm_d3_test_open(struct inode *inode, struct file *file) ieee80211_stop_queues(mvm->hw); synchronize_net(); + mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_D3; + /* start pseudo D3 */ rtnl_lock(); err = __iwl_mvm_suspend(mvm->hw, mvm->hw->wiphy->wowlan_config, true); @@ -2088,9 +2171,13 @@ static int iwl_mvm_d3_test_release(struct inode *inode, struct file *file) int remaining_time = 10; mvm->d3_test_active = false; + rtnl_lock(); __iwl_mvm_resume(mvm, true); rtnl_unlock(); + + mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED; + iwl_abort_notification_waits(&mvm->notif_wait); ieee80211_restart_hw(mvm->hw); |