Skip to content

Commit

Permalink
iwlwifi: implement switching iftype while up
Browse files Browse the repository at this point in the history
Implement switching the interface while an
interface is up in iwlwifi. Interfaces have
to stay on the context they were created on.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Johannes Berg authored and John W. Linville committed Nov 15, 2010
1 parent bd50a8a commit d4daaea
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 44 deletions.
1 change: 1 addition & 0 deletions drivers/net/wireless/iwlwifi/iwl-4965.c
Original file line number Diff line number Diff line change
Expand Up @@ -2589,6 +2589,7 @@ struct ieee80211_ops iwl4965_hw_ops = {
.stop = iwlagn_mac_stop,
.add_interface = iwl_mac_add_interface,
.remove_interface = iwl_mac_remove_interface,
.change_interface = iwl_mac_change_interface,
.config = iwl_legacy_mac_config,
.configure_filter = iwlagn_configure_filter,
.set_key = iwlagn_mac_set_key,
Expand Down
1 change: 1 addition & 0 deletions drivers/net/wireless/iwlwifi/iwl-agn.c
Original file line number Diff line number Diff line change
Expand Up @@ -3909,6 +3909,7 @@ struct ieee80211_ops iwlagn_hw_ops = {
.stop = iwlagn_mac_stop,
.add_interface = iwl_mac_add_interface,
.remove_interface = iwl_mac_remove_interface,
.change_interface = iwl_mac_change_interface,
.config = iwlagn_mac_config,
.configure_filter = iwlagn_configure_filter,
.set_key = iwlagn_mac_set_key,
Expand Down
168 changes: 124 additions & 44 deletions drivers/net/wireless/iwlwifi/iwl-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -1427,10 +1427,8 @@ int iwl_mac_tx_last_beacon(struct ieee80211_hw *hw)
}
EXPORT_SYMBOL_GPL(iwl_mac_tx_last_beacon);

static int iwl_set_mode(struct iwl_priv *priv, struct ieee80211_vif *vif)
static int iwl_set_mode(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
{
struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif);

iwl_connection_init_rx_config(priv, ctx);

if (priv->cfg->ops->hcmd->set_rxon_chain)
Expand All @@ -1439,12 +1437,49 @@ static int iwl_set_mode(struct iwl_priv *priv, struct ieee80211_vif *vif)
return iwlcore_commit_rxon(priv, ctx);
}

static int iwl_setup_interface(struct iwl_priv *priv,
struct iwl_rxon_context *ctx)
{
struct ieee80211_vif *vif = ctx->vif;
int err;

lockdep_assert_held(&priv->mutex);

/*
* This variable will be correct only when there's just
* a single context, but all code using it is for hardware
* that supports only one context.
*/
priv->iw_mode = vif->type;

ctx->is_active = true;

err = iwl_set_mode(priv, ctx);
if (err) {
if (!ctx->always_active)
ctx->is_active = false;
return err;
}

if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist &&
vif->type == NL80211_IFTYPE_ADHOC) {
/*
* pretend to have high BT traffic as long as we
* are operating in IBSS mode, as this will cause
* the rate scaling etc. to behave as intended.
*/
priv->bt_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_HIGH;
}

return 0;
}

int iwl_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{
struct iwl_priv *priv = hw->priv;
struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv;
struct iwl_rxon_context *tmp, *ctx = NULL;
int err = 0;
int err;

IWL_DEBUG_MAC80211(priv, "enter: type %d, addr %pM\n",
vif->type, vif->addr);
Expand Down Expand Up @@ -1486,36 +1521,11 @@ int iwl_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)

vif_priv->ctx = ctx;
ctx->vif = vif;
/*
* This variable will be correct only when there's just
* a single context, but all code using it is for hardware
* that supports only one context.
*/
priv->iw_mode = vif->type;

ctx->is_active = true;

err = iwl_set_mode(priv, vif);
if (err) {
if (!ctx->always_active)
ctx->is_active = false;
goto out_err;
}

if (priv->cfg->bt_params &&
priv->cfg->bt_params->advanced_bt_coexist &&
vif->type == NL80211_IFTYPE_ADHOC) {
/*
* pretend to have high BT traffic as long as we
* are operating in IBSS mode, as this will cause
* the rate scaling etc. to behave as intended.
*/
priv->bt_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_HIGH;
}

goto out;
err = iwl_setup_interface(priv, ctx);
if (!err)
goto out;

out_err:
ctx->vif = NULL;
priv->iw_mode = NL80211_IFTYPE_STATION;
out:
Expand All @@ -1526,27 +1536,24 @@ int iwl_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
}
EXPORT_SYMBOL(iwl_mac_add_interface);

void iwl_mac_remove_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
static void iwl_teardown_interface(struct iwl_priv *priv,
struct ieee80211_vif *vif,
bool mode_change)
{
struct iwl_priv *priv = hw->priv;
struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif);

IWL_DEBUG_MAC80211(priv, "enter\n");

mutex_lock(&priv->mutex);

WARN_ON(ctx->vif != vif);
ctx->vif = NULL;
lockdep_assert_held(&priv->mutex);

if (priv->scan_vif == vif) {
iwl_scan_cancel_timeout(priv, 200);
iwl_force_scan_end(priv);
}
iwl_set_mode(priv, vif);

if (!ctx->always_active)
ctx->is_active = false;
if (!mode_change) {
iwl_set_mode(priv, ctx);
if (!ctx->always_active)
ctx->is_active = false;
}

/*
* When removing the IBSS interface, overwrite the
Expand All @@ -1557,6 +1564,22 @@ void iwl_mac_remove_interface(struct ieee80211_hw *hw,
*/
if (vif->type == NL80211_IFTYPE_ADHOC)
priv->bt_traffic_load = priv->notif_bt_traffic_load;
}

void iwl_mac_remove_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
struct iwl_priv *priv = hw->priv;
struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif);

IWL_DEBUG_MAC80211(priv, "enter\n");

mutex_lock(&priv->mutex);

WARN_ON(ctx->vif != vif);
ctx->vif = NULL;

iwl_teardown_interface(priv, vif, false);

memset(priv->bssid, 0, ETH_ALEN);
mutex_unlock(&priv->mutex);
Expand Down Expand Up @@ -1908,6 +1931,63 @@ int iwl_force_reset(struct iwl_priv *priv, int mode, bool external)
return 0;
}

int iwl_mac_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
enum nl80211_iftype newtype, bool newp2p)
{
struct iwl_priv *priv = hw->priv;
struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif);
struct iwl_rxon_context *tmp;
u32 interface_modes;
int err;

newtype = ieee80211_iftype_p2p(newtype, newp2p);

mutex_lock(&priv->mutex);

interface_modes = ctx->interface_modes | ctx->exclusive_interface_modes;

if (!(interface_modes & BIT(newtype))) {
err = -EBUSY;
goto out;
}

if (ctx->exclusive_interface_modes & BIT(newtype)) {
for_each_context(priv, tmp) {
if (ctx == tmp)
continue;

if (!tmp->vif)
continue;

/*
* The current mode switch would be exclusive, but
* another context is active ... refuse the switch.
*/
err = -EBUSY;
goto out;
}
}

/* success */
iwl_teardown_interface(priv, vif, true);
vif->type = newtype;
err = iwl_setup_interface(priv, ctx);
WARN_ON(err);
/*
* We've switched internally, but submitting to the
* device may have failed for some reason. Mask this
* error, because otherwise mac80211 will not switch
* (and set the interface type back) and we'll be
* out of sync with it.
*/
err = 0;

out:
mutex_unlock(&priv->mutex);
return err;
}
EXPORT_SYMBOL(iwl_mac_change_interface);

/**
* iwl_bg_monitor_recover - Timer callback to check for stuck queue and recover
*
Expand Down
3 changes: 3 additions & 0 deletions drivers/net/wireless/iwlwifi/iwl-core.h
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,9 @@ int iwl_mac_add_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif);
void iwl_mac_remove_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif);
int iwl_mac_change_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
enum nl80211_iftype newtype, bool newp2p);
int iwl_alloc_txq_mem(struct iwl_priv *priv);
void iwl_free_txq_mem(struct iwl_priv *priv);
void iwlcore_tx_cmd_protection(struct iwl_priv *priv,
Expand Down
1 change: 1 addition & 0 deletions drivers/net/wireless/iwlwifi/iwl3945-base.c
Original file line number Diff line number Diff line change
Expand Up @@ -3832,6 +3832,7 @@ struct ieee80211_ops iwl3945_hw_ops = {
.stop = iwl3945_mac_stop,
.add_interface = iwl_mac_add_interface,
.remove_interface = iwl_mac_remove_interface,
.change_interface = iwl_mac_change_interface,
.config = iwl_legacy_mac_config,
.configure_filter = iwl3945_configure_filter,
.set_key = iwl3945_mac_set_key,
Expand Down

0 comments on commit d4daaea

Please sign in to comment.