Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+5 −1
Original line number Original line Diff line number Diff line
@@ -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;


+17 −0
Original line number Original line Diff line number Diff line
@@ -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;
+8 −1
Original line number Original line Diff line number Diff line
@@ -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);
+94 −0
Original line number Original line Diff line number Diff line
@@ -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)
	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)
	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 = {
	.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


+6 −0
Original line number Original line Diff line number Diff line
@@ -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
	};
	};