Skip to content

Commit

Permalink
iwlwifi: implement remain-on-channel
Browse files Browse the repository at this point in the history
For device supporting PAN/P2P, use the PAN
context to implement the remain-on-channel
operation using device offloads so that the
filters in the device will be programmed
correctly -- otherwise we cannot receive
any probe request frames during off-channel
periods.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
  • Loading branch information
Johannes Berg authored and Wey-Yi Guy committed Jan 21, 2011
1 parent 9d4dea7 commit 9b9190d
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 2 deletions.
6 changes: 5 additions & 1 deletion drivers/net/wireless/iwlwifi/iwl-agn-hcmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,11 @@ static int iwlagn_set_pan_params(struct iwl_priv *priv)
cmd.slots[0].type = 0; /* BSS */
cmd.slots[1].type = 1; /* PAN */

if (ctx_bss->vif && ctx_pan->vif) {
if (priv->_agn.hw_roc_channel) {
/* both contexts must be used for this to happen */
slot1 = priv->_agn.hw_roc_duration;
slot0 = 20;
} else if (ctx_bss->vif && ctx_pan->vif) {
int bcnint = ctx_pan->vif->bss_conf.beacon_int;
int dtim = ctx_pan->vif->bss_conf.dtim_period ?: 1;

Expand Down
17 changes: 17 additions & 0 deletions drivers/net/wireless/iwlwifi/iwl-agn-rxon.c
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,23 @@ int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
/* always get timestamp with Rx frame */
ctx->staging.flags |= RXON_FLG_TSF2HOST_MSK;

if (ctx->ctxid == IWL_RXON_CTX_PAN && priv->_agn.hw_roc_channel) {
struct ieee80211_channel *chan = priv->_agn.hw_roc_channel;

iwl_set_rxon_channel(priv, chan, ctx);
iwl_set_flags_for_band(priv, ctx, chan->band, NULL);
ctx->staging.filter_flags |=
RXON_FILTER_ASSOC_MSK |
RXON_FILTER_PROMISC_MSK |
RXON_FILTER_CTL2HOST_MSK;
ctx->staging.dev_type = RXON_DEV_TYPE_P2P;
new_assoc = true;

if (memcmp(&ctx->staging, &ctx->active,
sizeof(ctx->staging)) == 0)
return 0;
}

if ((ctx->vif && ctx->vif->bss_conf.use_short_slot) ||
!(ctx->staging.flags & RXON_FLG_BAND_24G_MSK))
ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK;
Expand Down
9 changes: 8 additions & 1 deletion drivers/net/wireless/iwlwifi/iwl-agn-tx.c
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,14 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
unsigned long flags;
bool is_agg = false;

if (info->control.vif)
/*
* If the frame needs to go out off-channel, then
* we'll have put the PAN context to that channel,
* so make the frame go out there.
*/
if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)
ctx = &priv->contexts[IWL_RXON_CTX_PAN];
else if (info->control.vif)
ctx = iwl_rxon_ctx_from_vif(info->control.vif);

spin_lock_irqsave(&priv->lock, flags);
Expand Down
94 changes: 94 additions & 0 deletions drivers/net/wireless/iwlwifi/iwl-agn.c
Original file line number Diff line number Diff line change
Expand Up @@ -3208,6 +3208,8 @@ static int iwl_mac_setup_register(struct iwl_priv *priv,
hw->wiphy->interface_modes |= ctx->exclusive_interface_modes;
}

hw->wiphy->max_remain_on_channel_duration = 1000;

hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY |
WIPHY_FLAG_DISABLE_BEACON_HINTS;

Expand Down Expand Up @@ -3726,6 +3728,95 @@ void iwlagn_mac_flush(struct ieee80211_hw *hw, bool drop)
IWL_DEBUG_MAC80211(priv, "leave\n");
}

static void iwlagn_disable_roc(struct iwl_priv *priv)
{
struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_PAN];
struct ieee80211_channel *chan = ACCESS_ONCE(priv->hw->conf.channel);

lockdep_assert_held(&priv->mutex);

if (!ctx->is_active)
return;

ctx->staging.dev_type = RXON_DEV_TYPE_2STA;
ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
iwl_set_rxon_channel(priv, chan, ctx);
iwl_set_flags_for_band(priv, ctx, chan->band, NULL);

priv->_agn.hw_roc_channel = NULL;

iwlagn_commit_rxon(priv, ctx);

ctx->is_active = false;
}

static void iwlagn_bg_roc_done(struct work_struct *work)
{
struct iwl_priv *priv = container_of(work, struct iwl_priv,
_agn.hw_roc_work.work);

mutex_lock(&priv->mutex);
ieee80211_remain_on_channel_expired(priv->hw);
iwlagn_disable_roc(priv);
mutex_unlock(&priv->mutex);
}

static int iwl_mac_remain_on_channel(struct ieee80211_hw *hw,
struct ieee80211_channel *channel,
enum nl80211_channel_type channel_type,
int duration)
{
struct iwl_priv *priv = hw->priv;
int err = 0;

if (!(priv->valid_contexts & BIT(IWL_RXON_CTX_PAN)))
return -EOPNOTSUPP;

if (!(priv->contexts[IWL_RXON_CTX_PAN].interface_modes &
BIT(NL80211_IFTYPE_P2P_CLIENT)))
return -EOPNOTSUPP;

mutex_lock(&priv->mutex);

if (priv->contexts[IWL_RXON_CTX_PAN].is_active ||
test_bit(STATUS_SCAN_HW, &priv->status)) {
err = -EBUSY;
goto out;
}

priv->contexts[IWL_RXON_CTX_PAN].is_active = true;
priv->_agn.hw_roc_channel = channel;
priv->_agn.hw_roc_chantype = channel_type;
priv->_agn.hw_roc_duration = DIV_ROUND_UP(duration * 1000, 1024);
iwlagn_commit_rxon(priv, &priv->contexts[IWL_RXON_CTX_PAN]);
queue_delayed_work(priv->workqueue, &priv->_agn.hw_roc_work,
msecs_to_jiffies(duration + 20));

msleep(20);
ieee80211_ready_on_channel(priv->hw);

out:
mutex_unlock(&priv->mutex);

return err;
}

static int iwl_mac_cancel_remain_on_channel(struct ieee80211_hw *hw)
{
struct iwl_priv *priv = hw->priv;

if (!(priv->valid_contexts & BIT(IWL_RXON_CTX_PAN)))
return -EOPNOTSUPP;

cancel_delayed_work_sync(&priv->_agn.hw_roc_work);

mutex_lock(&priv->mutex);
iwlagn_disable_roc(priv);
mutex_unlock(&priv->mutex);

return 0;
}

/*****************************************************************************
*
* driver setup and teardown
Expand All @@ -3747,6 +3838,7 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv)
INIT_WORK(&priv->bt_runtime_config, iwl_bg_bt_runtime_config);
INIT_DELAYED_WORK(&priv->init_alive_start, iwl_bg_init_alive_start);
INIT_DELAYED_WORK(&priv->alive_start, iwl_bg_alive_start);
INIT_DELAYED_WORK(&priv->_agn.hw_roc_work, iwlagn_bg_roc_done);

iwl_setup_scan_deferred_work(priv);

Expand Down Expand Up @@ -3915,6 +4007,8 @@ struct ieee80211_ops iwlagn_hw_ops = {
.channel_switch = iwlagn_mac_channel_switch,
.flush = iwlagn_mac_flush,
.tx_last_beacon = iwl_mac_tx_last_beacon,
.remain_on_channel = iwl_mac_remain_on_channel,
.cancel_remain_on_channel = iwl_mac_cancel_remain_on_channel,
};
#endif

Expand Down
6 changes: 6 additions & 0 deletions drivers/net/wireless/iwlwifi/iwl-dev.h
Original file line number Diff line number Diff line change
Expand Up @@ -1488,6 +1488,12 @@ struct iwl_priv {
struct list_head notif_waits;
spinlock_t notif_wait_lock;
wait_queue_head_t notif_waitq;

/* remain-on-channel offload support */
struct ieee80211_channel *hw_roc_channel;
struct delayed_work hw_roc_work;
enum nl80211_channel_type hw_roc_chantype;
int hw_roc_duration;
} _agn;
#endif
};
Expand Down

0 comments on commit 9b9190d

Please sign in to comment.