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

Commit 266af4c7 authored by Johannes Berg's avatar Johannes Berg Committed by John W. Linville
Browse files

iwlagn: support off-channel TX



Add support to iwlagn for off-channel TX. The
microcode API for this is a bit strange in that
it uses a hacked-up scan command, so the scan
code needs to change quite a bit to accomodate
that and be able to send it out.

Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarWey-Yi Guy <wey-yi.w.guy@intel.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 808118cb
Loading
Loading
Loading
Loading
+103 −32
Original line number Diff line number Diff line
@@ -1115,6 +1115,18 @@ static int iwl_get_channels_for_scan(struct iwl_priv *priv,
	return added;
}

static int iwl_fill_offch_tx(struct iwl_priv *priv, void *data, size_t maxlen)
{
	struct sk_buff *skb = priv->_agn.offchan_tx_skb;

	if (skb->len < maxlen)
		maxlen = skb->len;

	memcpy(data, skb->data, maxlen);

	return maxlen;
}

int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
{
	struct iwl_host_cmd cmd = {
@@ -1157,17 +1169,25 @@ int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
	scan->quiet_plcp_th = IWL_PLCP_QUIET_THRESH;
	scan->quiet_time = IWL_ACTIVE_QUIET_TIME;

	if (iwl_is_any_associated(priv)) {
	if (priv->scan_type != IWL_SCAN_OFFCH_TX &&
	    iwl_is_any_associated(priv)) {
		u16 interval = 0;
		u32 extra;
		u32 suspend_time = 100;
		u32 scan_suspend_time = 100;

		IWL_DEBUG_INFO(priv, "Scanning while associated...\n");
		if (priv->is_internal_short_scan)
		switch (priv->scan_type) {
		case IWL_SCAN_OFFCH_TX:
			WARN_ON(1);
			break;
		case IWL_SCAN_RADIO_RESET:
			interval = 0;
		else
			break;
		case IWL_SCAN_NORMAL:
			interval = vif->bss_conf.beacon_int;
			break;
		}

		scan->suspend_time = 0;
		scan->max_out_time = cpu_to_le32(200 * 1024);
@@ -1180,11 +1200,18 @@ int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
		scan->suspend_time = cpu_to_le32(scan_suspend_time);
		IWL_DEBUG_SCAN(priv, "suspend_time 0x%X beacon interval %d\n",
			       scan_suspend_time, interval);
	} else if (priv->scan_type == IWL_SCAN_OFFCH_TX) {
		scan->suspend_time = 0;
		scan->max_out_time =
			cpu_to_le32(1024 * priv->_agn.offchan_tx_timeout);
	}

	if (priv->is_internal_short_scan) {
	switch (priv->scan_type) {
	case IWL_SCAN_RADIO_RESET:
		IWL_DEBUG_SCAN(priv, "Start internal passive scan.\n");
	} else if (priv->scan_request->n_ssids) {
		break;
	case IWL_SCAN_NORMAL:
		if (priv->scan_request->n_ssids) {
			int i, p = 0;
			IWL_DEBUG_SCAN(priv, "Kicking off active scan\n");
			for (i = 0; i < priv->scan_request->n_ssids; i++) {
@@ -1203,6 +1230,11 @@ int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
			is_active = true;
		} else
			IWL_DEBUG_SCAN(priv, "Start passive scan.\n");
		break;
	case IWL_SCAN_OFFCH_TX:
		IWL_DEBUG_SCAN(priv, "Start offchannel TX scan.\n");
		break;
	}

	scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK;
	scan->tx_cmd.sta_id = ctx->bcast_sta_id;
@@ -1300,38 +1332,77 @@ int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
	rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS;
	rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS;
	scan->rx_chain = cpu_to_le16(rx_chain);
	if (!priv->is_internal_short_scan) {
	switch (priv->scan_type) {
	case IWL_SCAN_NORMAL:
		cmd_len = iwl_fill_probe_req(priv,
					(struct ieee80211_mgmt *)scan->data,
					vif->addr,
					priv->scan_request->ie,
					priv->scan_request->ie_len,
					IWL_MAX_SCAN_SIZE - sizeof(*scan));
	} else {
		break;
	case IWL_SCAN_RADIO_RESET:
		/* use bcast addr, will not be transmitted but must be valid */
		cmd_len = iwl_fill_probe_req(priv,
					(struct ieee80211_mgmt *)scan->data,
					iwl_bcast_addr, NULL, 0,
					IWL_MAX_SCAN_SIZE - sizeof(*scan));

		break;
	case IWL_SCAN_OFFCH_TX:
		cmd_len = iwl_fill_offch_tx(priv, scan->data,
					    IWL_MAX_SCAN_SIZE
					     - sizeof(*scan)
					     - sizeof(struct iwl_scan_channel));
		scan->scan_flags |= IWL_SCAN_FLAGS_ACTION_FRAME_TX;
		break;
	default:
		BUG();
	}
	scan->tx_cmd.len = cpu_to_le16(cmd_len);

	scan->filter_flags |= (RXON_FILTER_ACCEPT_GRP_MSK |
			       RXON_FILTER_BCON_AWARE_MSK);

	if (priv->is_internal_short_scan) {
	switch (priv->scan_type) {
	case IWL_SCAN_RADIO_RESET:
		scan->channel_count =
			iwl_get_single_channel_for_scan(priv, vif, band,
				(void *)&scan->data[le16_to_cpu(
				scan->tx_cmd.len)]);
	} else {
				(void *)&scan->data[cmd_len]);
		break;
	case IWL_SCAN_NORMAL:
		scan->channel_count =
			iwl_get_channels_for_scan(priv, vif, band,
				is_active, n_probes,
				(void *)&scan->data[le16_to_cpu(
				scan->tx_cmd.len)]);
				(void *)&scan->data[cmd_len]);
		break;
	case IWL_SCAN_OFFCH_TX: {
		struct iwl_scan_channel *scan_ch;

		scan->channel_count = 1;

		scan_ch = (void *)&scan->data[cmd_len];
		scan_ch->type = SCAN_CHANNEL_TYPE_ACTIVE;
		scan_ch->channel =
			cpu_to_le16(priv->_agn.offchan_tx_chan->hw_value);
		scan_ch->active_dwell =
			cpu_to_le16(priv->_agn.offchan_tx_timeout);
		scan_ch->passive_dwell = 0;

		/* Set txpower levels to defaults */
		scan_ch->dsp_atten = 110;

		/* NOTE: if we were doing 6Mb OFDM for scans we'd use
		 * power level:
		 * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3;
		 */
		if (priv->_agn.offchan_tx_chan->band == IEEE80211_BAND_5GHZ)
			scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3;
		else
			scan_ch->tx_gain = ((1 << 5) | (5 << 3));
		}
		break;
	}

	if (scan->channel_count == 0) {
		IWL_DEBUG_SCAN(priv, "channel count %d\n", scan->channel_count);
		return -EIO;
+87 −0
Original line number Diff line number Diff line
@@ -2937,6 +2937,91 @@ static void iwl_bg_rx_replenish(struct work_struct *data)
	mutex_unlock(&priv->mutex);
}

static int iwl_mac_offchannel_tx(struct ieee80211_hw *hw, struct sk_buff *skb,
				 struct ieee80211_channel *chan,
				 enum nl80211_channel_type channel_type,
				 unsigned int wait)
{
	struct iwl_priv *priv = hw->priv;
	int ret;

	/* Not supported if we don't have PAN */
	if (!(priv->valid_contexts & BIT(IWL_RXON_CTX_PAN))) {
		ret = -EOPNOTSUPP;
		goto free;
	}

	/* Not supported on pre-P2P firmware */
	if (!(priv->contexts[IWL_RXON_CTX_PAN].interface_modes &
					BIT(NL80211_IFTYPE_P2P_CLIENT))) {
		ret = -EOPNOTSUPP;
		goto free;
	}

	mutex_lock(&priv->mutex);

	if (!priv->contexts[IWL_RXON_CTX_PAN].is_active) {
		/*
		 * If the PAN context is free, use the normal
		 * way of doing remain-on-channel offload + TX.
		 */
		ret = 1;
		goto out;
	}

	/* TODO: queue up if scanning? */
	if (test_bit(STATUS_SCANNING, &priv->status) ||
	    priv->_agn.offchan_tx_skb) {
		ret = -EBUSY;
		goto out;
	}

	/*
	 * max_scan_ie_len doesn't include the blank SSID or the header,
	 * so need to add that again here.
	 */
	if (skb->len > hw->wiphy->max_scan_ie_len + 24 + 2) {
		ret = -ENOBUFS;
		goto out;
	}

	priv->_agn.offchan_tx_skb = skb;
	priv->_agn.offchan_tx_timeout = wait;
	priv->_agn.offchan_tx_chan = chan;

	ret = iwl_scan_initiate(priv, priv->contexts[IWL_RXON_CTX_PAN].vif,
				IWL_SCAN_OFFCH_TX, chan->band);
	if (ret)
		priv->_agn.offchan_tx_skb = NULL;
 out:
	mutex_unlock(&priv->mutex);
 free:
	if (ret < 0)
		kfree_skb(skb);

	return ret;
}

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

	mutex_lock(&priv->mutex);

	if (!priv->_agn.offchan_tx_skb)
		return -EINVAL;

	priv->_agn.offchan_tx_skb = NULL;

	ret = iwl_scan_cancel_timeout(priv, 200);
	if (ret)
		ret = -EIO;
	mutex_unlock(&priv->mutex);

	return ret;
}

/*****************************************************************************
 *
 * mac80211 entry point functions
@@ -3815,6 +3900,8 @@ struct ieee80211_ops iwlagn_hw_ops = {
	.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,
	.offchannel_tx = iwl_mac_offchannel_tx,
	.offchannel_tx_cancel_wait = iwl_mac_offchannel_tx_cancel_wait,
};

static void iwl_hw_detect(struct iwl_priv *priv)
+7 −1
Original line number Diff line number Diff line
@@ -2964,9 +2964,15 @@ struct iwl3945_scan_cmd {
	u8 data[0];
} __packed;

enum iwl_scan_flags {
	/* BIT(0) currently unused */
	IWL_SCAN_FLAGS_ACTION_FRAME_TX	= BIT(1),
	/* bits 2-7 reserved */
};

struct iwl_scan_cmd {
	__le16 len;
	u8 reserved0;
	u8 scan_flags;		/* scan flags: see enum iwl_scan_flags */
	u8 channel_count;	/* # channels in channel list */
	__le16 quiet_time;	/* dwell only this # millisecs on quiet channel
				 * (only for active scan) */
+6 −0
Original line number Diff line number Diff line
@@ -63,6 +63,8 @@
#ifndef __iwl_core_h__
#define __iwl_core_h__

#include "iwl-dev.h"

/************************
 * forward declarations *
 ************************/
@@ -551,6 +553,10 @@ u16 iwl_get_passive_dwell_time(struct iwl_priv *priv,
			       struct ieee80211_vif *vif);
void iwl_setup_scan_deferred_work(struct iwl_priv *priv);
void iwl_cancel_scan_deferred_work(struct iwl_priv *priv);
int __must_check iwl_scan_initiate(struct iwl_priv *priv,
				   struct ieee80211_vif *vif,
				   enum iwl_scan_type scan_type,
				   enum ieee80211_band band);

/* For faster active scanning, scan will move to the next channel if fewer than
 * PLCP_QUIET_THRESH packets are heard on this channel within
+11 −1
Original line number Diff line number Diff line
@@ -1230,6 +1230,12 @@ struct iwl_rxon_context {
	} ht;
};

enum iwl_scan_type {
	IWL_SCAN_NORMAL,
	IWL_SCAN_RADIO_RESET,
	IWL_SCAN_OFFCH_TX,
};

struct iwl_priv {

	/* ieee device used by generic ieee processing code */
@@ -1290,7 +1296,7 @@ struct iwl_priv {
	enum ieee80211_band scan_band;
	struct cfg80211_scan_request *scan_request;
	struct ieee80211_vif *scan_vif;
	bool is_internal_short_scan;
	enum iwl_scan_type scan_type;
	u8 scan_tx_ant[IEEE80211_NUM_BANDS];
	u8 mgmt_tx_ant;

@@ -1504,6 +1510,10 @@ struct iwl_priv {
			struct delayed_work hw_roc_work;
			enum nl80211_channel_type hw_roc_chantype;
			int hw_roc_duration;

			struct sk_buff *offchan_tx_skb;
			int offchan_tx_timeout;
			struct ieee80211_channel *offchan_tx_chan;
		} _agn;
#endif
	};
Loading