diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/mvm/mac80211.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/mac80211.c | 154 |
1 files changed, 127 insertions, 27 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 72f82a33856..24cc5695733 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -2282,17 +2282,17 @@ static int iwl_mvm_cancel_roc(struct ieee80211_hw *hw) return 0; } -static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw, - struct ieee80211_chanctx_conf *ctx) +static int __iwl_mvm_add_chanctx(struct iwl_mvm *mvm, + struct ieee80211_chanctx_conf *ctx) { - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; struct iwl_mvm_phy_ctxt *phy_ctxt; int ret; + lockdep_assert_held(&mvm->mutex); + IWL_DEBUG_MAC80211(mvm, "Add channel context\n"); - mutex_lock(&mvm->mutex); phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm); if (!phy_ctxt) { ret = -ENOSPC; @@ -2310,19 +2310,40 @@ static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw, iwl_mvm_phy_ctxt_ref(mvm, phy_ctxt); *phy_ctxt_id = phy_ctxt->id; out: + return ret; +} + +static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + int ret; + + mutex_lock(&mvm->mutex); + ret = __iwl_mvm_add_chanctx(mvm, ctx); mutex_unlock(&mvm->mutex); + return ret; } +static void __iwl_mvm_remove_chanctx(struct iwl_mvm *mvm, + struct ieee80211_chanctx_conf *ctx) +{ + u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; + struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; + + lockdep_assert_held(&mvm->mutex); + + iwl_mvm_phy_ctxt_unref(mvm, phy_ctxt); +} + static void iwl_mvm_remove_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; - struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; mutex_lock(&mvm->mutex); - iwl_mvm_phy_ctxt_unref(mvm, phy_ctxt); + __iwl_mvm_remove_chanctx(mvm, ctx); mutex_unlock(&mvm->mutex); } @@ -2351,17 +2372,16 @@ static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw, mutex_unlock(&mvm->mutex); } -static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_chanctx_conf *ctx) +static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx) { - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int ret; - mutex_lock(&mvm->mutex); + lockdep_assert_held(&mvm->mutex); mvmvif->phy_ctxt = phy_ctxt; @@ -2378,18 +2398,18 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw, * (in bss_info_changed), similarly for IBSS. */ ret = 0; - goto out_unlock; + goto out; case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_MONITOR: break; default: ret = -EINVAL; - goto out_unlock; + goto out; } ret = iwl_mvm_binding_add_vif(mvm, vif); if (ret) - goto out_unlock; + goto out; /* * Power state must be updated before quotas, @@ -2414,32 +2434,43 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw, iwl_mvm_mac_ctxt_changed(mvm, vif, false); } - goto out_unlock; + goto out; - out_remove_binding: +out_remove_binding: iwl_mvm_binding_remove_vif(mvm, vif); iwl_mvm_power_update_mac(mvm); - out_unlock: - mutex_unlock(&mvm->mutex); +out: if (ret) mvmvif->phy_ctxt = NULL; return ret; } - -static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_chanctx_conf *ctx) +static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int ret; mutex_lock(&mvm->mutex); + ret = __iwl_mvm_assign_vif_chanctx(mvm, vif, ctx); + mutex_unlock(&mvm->mutex); + + return ret; +} + +static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + lockdep_assert_held(&mvm->mutex); iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data); switch (vif->type) { case NL80211_IFTYPE_ADHOC: - goto out_unlock; + goto out; case NL80211_IFTYPE_MONITOR: mvmvif->monitor_active = false; iwl_mvm_update_quotas(mvm, NULL); @@ -2447,7 +2478,7 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw, case NL80211_IFTYPE_AP: /* This part is triggered only during CSA */ if (!vif->csa_active || !mvmvif->ap_ibss_active) - goto out_unlock; + goto out; mvmvif->ap_ibss_active = false; iwl_mvm_update_quotas(mvm, NULL); @@ -2457,12 +2488,80 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw, iwl_mvm_binding_remove_vif(mvm, vif); -out_unlock: +out: mvmvif->phy_ctxt = NULL; iwl_mvm_power_update_mac(mvm); +} + +static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + + mutex_lock(&mvm->mutex); + __iwl_mvm_unassign_vif_chanctx(mvm, vif, ctx); mutex_unlock(&mvm->mutex); } +static int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif_chanctx_switch *vifs, + int n_vifs, + enum ieee80211_chanctx_switch_mode mode) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + int ret; + + /* we only support SWAP_CONTEXTS and with a single-vif right now */ + if (mode != CHANCTX_SWMODE_SWAP_CONTEXTS || n_vifs > 1) + return -EOPNOTSUPP; + + mutex_lock(&mvm->mutex); + __iwl_mvm_unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx); + __iwl_mvm_remove_chanctx(mvm, vifs[0].old_ctx); + + ret = __iwl_mvm_add_chanctx(mvm, vifs[0].new_ctx); + if (ret) { + IWL_ERR(mvm, "failed to add new_ctx during channel switch\n"); + goto out_reassign; + } + + ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].new_ctx); + if (ret) { + IWL_ERR(mvm, + "failed to assign new_ctx during channel switch\n"); + goto out_remove; + } + + goto out; + +out_remove: + __iwl_mvm_remove_chanctx(mvm, vifs[0].new_ctx); + +out_reassign: + ret = __iwl_mvm_add_chanctx(mvm, vifs[0].old_ctx); + if (ret) { + IWL_ERR(mvm, "failed to add old_ctx back after failure.\n"); + goto out_restart; + } + + ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx); + if (ret) { + IWL_ERR(mvm, "failed to reassign old_ctx after failure.\n"); + goto out_restart; + } + + goto out; + +out_restart: + /* things keep failing, better restart the hw */ + iwl_mvm_nic_restart(mvm, false); + +out: + mutex_unlock(&mvm->mutex); + return ret; +} + static int iwl_mvm_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set) @@ -2627,6 +2726,7 @@ const struct ieee80211_ops iwl_mvm_hw_ops = { .change_chanctx = iwl_mvm_change_chanctx, .assign_vif_chanctx = iwl_mvm_assign_vif_chanctx, .unassign_vif_chanctx = iwl_mvm_unassign_vif_chanctx, + .switch_vif_chanctx = iwl_mvm_switch_vif_chanctx, .start_ap = iwl_mvm_start_ap_ibss, .stop_ap = iwl_mvm_stop_ap_ibss, |