aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/iwlwifi
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2011-01-06 11:07:10 -0500
committerWey-Yi Guy <wey-yi.w.guy@intel.com>2011-01-21 18:50:58 -0500
commit9b9190d9688ccf531a3a5dac84d7b9654a08bfc5 (patch)
tree7ae230ad4b4665dc2e84807e52735557f8e5637a /drivers/net/wireless/iwlwifi
parent9d4dea7259d2fccf447f20788300121cf1d014bb (diff)
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: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'drivers/net/wireless/iwlwifi')
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-hcmd.c6
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-rxon.c17
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-tx.c9
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn.c94
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-dev.h6
5 files changed, 130 insertions, 2 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-hcmd.c b/drivers/net/wireless/iwlwifi/iwl-agn-hcmd.c
index 366340f3fb0f..fa6cf2a3326d 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-hcmd.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-hcmd.c
@@ -305,7 +305,11 @@ static int iwlagn_set_pan_params(struct iwl_priv *priv)
305 cmd.slots[0].type = 0; /* BSS */ 305 cmd.slots[0].type = 0; /* BSS */
306 cmd.slots[1].type = 1; /* PAN */ 306 cmd.slots[1].type = 1; /* PAN */
307 307
308 if (ctx_bss->vif && ctx_pan->vif) { 308 if (priv->_agn.hw_roc_channel) {
309 /* both contexts must be used for this to happen */
310 slot1 = priv->_agn.hw_roc_duration;
311 slot0 = 20;
312 } else if (ctx_bss->vif && ctx_pan->vif) {
309 int bcnint = ctx_pan->vif->bss_conf.beacon_int; 313 int bcnint = ctx_pan->vif->bss_conf.beacon_int;
310 int dtim = ctx_pan->vif->bss_conf.dtim_period ?: 1; 314 int dtim = ctx_pan->vif->bss_conf.dtim_period ?: 1;
311 315
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c
index f693293b6bd1..2a4ff832fbb8 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c
@@ -156,6 +156,23 @@ int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
156 /* always get timestamp with Rx frame */ 156 /* always get timestamp with Rx frame */
157 ctx->staging.flags |= RXON_FLG_TSF2HOST_MSK; 157 ctx->staging.flags |= RXON_FLG_TSF2HOST_MSK;
158 158
159 if (ctx->ctxid == IWL_RXON_CTX_PAN && priv->_agn.hw_roc_channel) {
160 struct ieee80211_channel *chan = priv->_agn.hw_roc_channel;
161
162 iwl_set_rxon_channel(priv, chan, ctx);
163 iwl_set_flags_for_band(priv, ctx, chan->band, NULL);
164 ctx->staging.filter_flags |=
165 RXON_FILTER_ASSOC_MSK |
166 RXON_FILTER_PROMISC_MSK |
167 RXON_FILTER_CTL2HOST_MSK;
168 ctx->staging.dev_type = RXON_DEV_TYPE_P2P;
169 new_assoc = true;
170
171 if (memcmp(&ctx->staging, &ctx->active,
172 sizeof(ctx->staging)) == 0)
173 return 0;
174 }
175
159 if ((ctx->vif && ctx->vif->bss_conf.use_short_slot) || 176 if ((ctx->vif && ctx->vif->bss_conf.use_short_slot) ||
160 !(ctx->staging.flags & RXON_FLG_BAND_24G_MSK)) 177 !(ctx->staging.flags & RXON_FLG_BAND_24G_MSK))
161 ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; 178 ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK;
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
index 24a11b8f73bc..266490d8a397 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
@@ -539,7 +539,14 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
539 unsigned long flags; 539 unsigned long flags;
540 bool is_agg = false; 540 bool is_agg = false;
541 541
542 if (info->control.vif) 542 /*
543 * If the frame needs to go out off-channel, then
544 * we'll have put the PAN context to that channel,
545 * so make the frame go out there.
546 */
547 if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)
548 ctx = &priv->contexts[IWL_RXON_CTX_PAN];
549 else if (info->control.vif)
543 ctx = iwl_rxon_ctx_from_vif(info->control.vif); 550 ctx = iwl_rxon_ctx_from_vif(info->control.vif);
544 551
545 spin_lock_irqsave(&priv->lock, flags); 552 spin_lock_irqsave(&priv->lock, flags);
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
index dad9a63b72be..51e5ea4aeb2a 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
@@ -3208,6 +3208,8 @@ static int iwl_mac_setup_register(struct iwl_priv *priv,
3208 hw->wiphy->interface_modes |= ctx->exclusive_interface_modes; 3208 hw->wiphy->interface_modes |= ctx->exclusive_interface_modes;
3209 } 3209 }
3210 3210
3211 hw->wiphy->max_remain_on_channel_duration = 1000;
3212
3211 hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY | 3213 hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY |
3212 WIPHY_FLAG_DISABLE_BEACON_HINTS; 3214 WIPHY_FLAG_DISABLE_BEACON_HINTS;
3213 3215
@@ -3726,6 +3728,95 @@ done:
3726 IWL_DEBUG_MAC80211(priv, "leave\n"); 3728 IWL_DEBUG_MAC80211(priv, "leave\n");
3727} 3729}
3728 3730
3731static void iwlagn_disable_roc(struct iwl_priv *priv)
3732{
3733 struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_PAN];
3734 struct ieee80211_channel *chan = ACCESS_ONCE(priv->hw->conf.channel);
3735
3736 lockdep_assert_held(&priv->mutex);
3737
3738 if (!ctx->is_active)
3739 return;
3740
3741 ctx->staging.dev_type = RXON_DEV_TYPE_2STA;
3742 ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
3743 iwl_set_rxon_channel(priv, chan, ctx);
3744 iwl_set_flags_for_band(priv, ctx, chan->band, NULL);
3745
3746 priv->_agn.hw_roc_channel = NULL;
3747
3748 iwlagn_commit_rxon(priv, ctx);
3749
3750 ctx->is_active = false;
3751}
3752
3753static void iwlagn_bg_roc_done(struct work_struct *work)
3754{
3755 struct iwl_priv *priv = container_of(work, struct iwl_priv,
3756 _agn.hw_roc_work.work);
3757
3758 mutex_lock(&priv->mutex);
3759 ieee80211_remain_on_channel_expired(priv->hw);
3760 iwlagn_disable_roc(priv);
3761 mutex_unlock(&priv->mutex);
3762}
3763
3764static int iwl_mac_remain_on_channel(struct ieee80211_hw *hw,
3765 struct ieee80211_channel *channel,
3766 enum nl80211_channel_type channel_type,
3767 int duration)
3768{
3769 struct iwl_priv *priv = hw->priv;
3770 int err = 0;
3771
3772 if (!(priv->valid_contexts & BIT(IWL_RXON_CTX_PAN)))
3773 return -EOPNOTSUPP;
3774
3775 if (!(priv->contexts[IWL_RXON_CTX_PAN].interface_modes &
3776 BIT(NL80211_IFTYPE_P2P_CLIENT)))
3777 return -EOPNOTSUPP;
3778
3779 mutex_lock(&priv->mutex);
3780
3781 if (priv->contexts[IWL_RXON_CTX_PAN].is_active ||
3782 test_bit(STATUS_SCAN_HW, &priv->status)) {
3783 err = -EBUSY;
3784 goto out;
3785 }
3786
3787 priv->contexts[IWL_RXON_CTX_PAN].is_active = true;
3788 priv->_agn.hw_roc_channel = channel;
3789 priv->_agn.hw_roc_chantype = channel_type;
3790 priv->_agn.hw_roc_duration = DIV_ROUND_UP(duration * 1000, 1024);
3791 iwlagn_commit_rxon(priv, &priv->contexts[IWL_RXON_CTX_PAN]);
3792 queue_delayed_work(priv->workqueue, &priv->_agn.hw_roc_work,
3793 msecs_to_jiffies(duration + 20));
3794
3795 msleep(20);
3796 ieee80211_ready_on_channel(priv->hw);
3797
3798 out:
3799 mutex_unlock(&priv->mutex);
3800
3801 return err;
3802}
3803
3804static int iwl_mac_cancel_remain_on_channel(struct ieee80211_hw *hw)
3805{
3806 struct iwl_priv *priv = hw->priv;
3807
3808 if (!(priv->valid_contexts & BIT(IWL_RXON_CTX_PAN)))
3809 return -EOPNOTSUPP;
3810
3811 cancel_delayed_work_sync(&priv->_agn.hw_roc_work);
3812
3813 mutex_lock(&priv->mutex);
3814 iwlagn_disable_roc(priv);
3815 mutex_unlock(&priv->mutex);
3816
3817 return 0;
3818}
3819
3729/***************************************************************************** 3820/*****************************************************************************
3730 * 3821 *
3731 * driver setup and teardown 3822 * driver setup and teardown
@@ -3747,6 +3838,7 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv)
3747 INIT_WORK(&priv->bt_runtime_config, iwl_bg_bt_runtime_config); 3838 INIT_WORK(&priv->bt_runtime_config, iwl_bg_bt_runtime_config);
3748 INIT_DELAYED_WORK(&priv->init_alive_start, iwl_bg_init_alive_start); 3839 INIT_DELAYED_WORK(&priv->init_alive_start, iwl_bg_init_alive_start);
3749 INIT_DELAYED_WORK(&priv->alive_start, iwl_bg_alive_start); 3840 INIT_DELAYED_WORK(&priv->alive_start, iwl_bg_alive_start);
3841 INIT_DELAYED_WORK(&priv->_agn.hw_roc_work, iwlagn_bg_roc_done);
3750 3842
3751 iwl_setup_scan_deferred_work(priv); 3843 iwl_setup_scan_deferred_work(priv);
3752 3844
@@ -3915,6 +4007,8 @@ struct ieee80211_ops iwlagn_hw_ops = {
3915 .channel_switch = iwlagn_mac_channel_switch, 4007 .channel_switch = iwlagn_mac_channel_switch,
3916 .flush = iwlagn_mac_flush, 4008 .flush = iwlagn_mac_flush,
3917 .tx_last_beacon = iwl_mac_tx_last_beacon, 4009 .tx_last_beacon = iwl_mac_tx_last_beacon,
4010 .remain_on_channel = iwl_mac_remain_on_channel,
4011 .cancel_remain_on_channel = iwl_mac_cancel_remain_on_channel,
3918}; 4012};
3919#endif 4013#endif
3920 4014
diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h
index 6dd6508c93b0..6fa1383d72ec 100644
--- a/drivers/net/wireless/iwlwifi/iwl-dev.h
+++ b/drivers/net/wireless/iwlwifi/iwl-dev.h
@@ -1488,6 +1488,12 @@ struct iwl_priv {
1488 struct list_head notif_waits; 1488 struct list_head notif_waits;
1489 spinlock_t notif_wait_lock; 1489 spinlock_t notif_wait_lock;
1490 wait_queue_head_t notif_waitq; 1490 wait_queue_head_t notif_waitq;
1491
1492 /* remain-on-channel offload support */
1493 struct ieee80211_channel *hw_roc_channel;
1494 struct delayed_work hw_roc_work;
1495 enum nl80211_channel_type hw_roc_chantype;
1496 int hw_roc_duration;
1491 } _agn; 1497 } _agn;
1492#endif 1498#endif
1493 }; 1499 };