diff options
author | Johannes Berg <johannes.berg@intel.com> | 2011-01-06 11:07:10 -0500 |
---|---|---|
committer | Wey-Yi Guy <wey-yi.w.guy@intel.com> | 2011-01-21 18:50:58 -0500 |
commit | 9b9190d9688ccf531a3a5dac84d7b9654a08bfc5 (patch) | |
tree | 7ae230ad4b4665dc2e84807e52735557f8e5637a /drivers/net/wireless/iwlwifi | |
parent | 9d4dea7259d2fccf447f20788300121cf1d014bb (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.c | 6 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn-rxon.c | 17 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn-tx.c | 9 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn.c | 94 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-dev.h | 6 |
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 | ||
3731 | static 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 | |||
3753 | static 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 | |||
3764 | static 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 | |||
3804 | static 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 | }; |