Commit 9b9190d9 authored by Johannes Berg's avatar Johannes Berg Committed by Wey-Yi Guy

iwlwifi: implement remain-on-channel

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: default avatarJohannes Berg <johannes.berg@intel.com>
parent 9d4dea72
...@@ -305,7 +305,11 @@ static int iwlagn_set_pan_params(struct iwl_priv *priv) ...@@ -305,7 +305,11 @@ static int iwlagn_set_pan_params(struct iwl_priv *priv)
cmd.slots[0].type = 0; /* BSS */ cmd.slots[0].type = 0; /* BSS */
cmd.slots[1].type = 1; /* PAN */ 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 bcnint = ctx_pan->vif->bss_conf.beacon_int;
int dtim = ctx_pan->vif->bss_conf.dtim_period ?: 1; int dtim = ctx_pan->vif->bss_conf.dtim_period ?: 1;
......
...@@ -156,6 +156,23 @@ int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx) ...@@ -156,6 +156,23 @@ int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
/* always get timestamp with Rx frame */ /* always get timestamp with Rx frame */
ctx->staging.flags |= RXON_FLG_TSF2HOST_MSK; 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) || if ((ctx->vif && ctx->vif->bss_conf.use_short_slot) ||
!(ctx->staging.flags & RXON_FLG_BAND_24G_MSK)) !(ctx->staging.flags & RXON_FLG_BAND_24G_MSK))
ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK;
......
...@@ -539,7 +539,14 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) ...@@ -539,7 +539,14 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
unsigned long flags; unsigned long flags;
bool is_agg = false; 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); ctx = iwl_rxon_ctx_from_vif(info->control.vif);
spin_lock_irqsave(&priv->lock, flags); spin_lock_irqsave(&priv->lock, flags);
......
...@@ -3208,6 +3208,8 @@ static int iwl_mac_setup_register(struct iwl_priv *priv, ...@@ -3208,6 +3208,8 @@ static int iwl_mac_setup_register(struct iwl_priv *priv,
hw->wiphy->interface_modes |= ctx->exclusive_interface_modes; hw->wiphy->interface_modes |= ctx->exclusive_interface_modes;
} }
hw->wiphy->max_remain_on_channel_duration = 1000;
hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY | hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY |
WIPHY_FLAG_DISABLE_BEACON_HINTS; WIPHY_FLAG_DISABLE_BEACON_HINTS;
...@@ -3726,6 +3728,95 @@ void iwlagn_mac_flush(struct ieee80211_hw *hw, bool drop) ...@@ -3726,6 +3728,95 @@ void iwlagn_mac_flush(struct ieee80211_hw *hw, bool drop)
IWL_DEBUG_MAC80211(priv, "leave\n"); 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 * driver setup and teardown
...@@ -3747,6 +3838,7 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv) ...@@ -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_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->init_alive_start, iwl_bg_init_alive_start);
INIT_DELAYED_WORK(&priv->alive_start, iwl_bg_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); iwl_setup_scan_deferred_work(priv);
...@@ -3915,6 +4007,8 @@ struct ieee80211_ops iwlagn_hw_ops = { ...@@ -3915,6 +4007,8 @@ struct ieee80211_ops iwlagn_hw_ops = {
.channel_switch = iwlagn_mac_channel_switch, .channel_switch = iwlagn_mac_channel_switch,
.flush = iwlagn_mac_flush, .flush = iwlagn_mac_flush,
.tx_last_beacon = iwl_mac_tx_last_beacon, .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 #endif
......
...@@ -1488,6 +1488,12 @@ struct iwl_priv { ...@@ -1488,6 +1488,12 @@ struct iwl_priv {
struct list_head notif_waits; struct list_head notif_waits;
spinlock_t notif_wait_lock; spinlock_t notif_wait_lock;
wait_queue_head_t notif_waitq; 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; } _agn;
#endif #endif
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment