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

Commit 04ecd257 authored by Johannes Berg's avatar Johannes Berg
Browse files

mac80211: track needed RX chains for channel contexts



On each channel that the device is operating on, it
may need to listen using one or more chains depending
on the SMPS settings of the interfaces using it. The
previous channel context changes completely removed
this ability (before, it was available as the SMPS
mode).

Add per-context tracking of the required static and
dynamic RX chains and notify the driver on changes.
To achieve this, track the chains and SMPS mode used
on each virtual interface and update the channel
context whenever this changes.

Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 55de908a
Loading
Loading
Loading
Loading
+14 −1
Original line number Diff line number Diff line
@@ -146,9 +146,11 @@ struct ieee80211_low_level_stats {
/**
 * enum ieee80211_chanctx_change - change flag for channel context
 * @IEEE80211_CHANCTX_CHANGE_CHANNEL_TYPE: The channel type was changed
 * @IEEE80211_CHANCTX_CHANGE_RX_CHAINS: The number of RX chains changed
 */
enum ieee80211_chanctx_change {
	IEEE80211_CHANCTX_CHANGE_CHANNEL_TYPE	= BIT(0),
	IEEE80211_CHANCTX_CHANGE_RX_CHAINS	= BIT(1),
};

/**
@@ -159,6 +161,11 @@ enum ieee80211_chanctx_change {
 *
 * @channel: the channel to tune to
 * @channel_type: the channel (HT) type
 * @rx_chains_static: The number of RX chains that must always be
 *	active on the channel to receive MIMO transmissions
 * @rx_chains_dynamic: The number of RX chains that must be enabled
 *	after RTS/CTS handshake to receive SMPS MIMO transmissions;
 *	this will always be >= @rx_chains_always.
 * @drv_priv: data area for driver use, will always be aligned to
 *	sizeof(void *), size is determined in hw information.
 */
@@ -166,6 +173,8 @@ struct ieee80211_chanctx_conf {
	struct ieee80211_channel *channel;
	enum nl80211_channel_type channel_type;

	u8 rx_chains_static, rx_chains_dynamic;

	u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *))));
};

@@ -820,6 +829,8 @@ enum ieee80211_conf_flags {
 * @IEEE80211_CONF_CHANGE_RETRY_LIMITS: retry limits changed
 * @IEEE80211_CONF_CHANGE_IDLE: Idle flag changed
 * @IEEE80211_CONF_CHANGE_SMPS: Spatial multiplexing powersave mode changed
 *	Note that this is only valid if channel contexts are not used,
 *	otherwise each channel context has the number of chains listed.
 */
enum ieee80211_conf_changed {
	IEEE80211_CONF_CHANGE_SMPS		= BIT(1),
@@ -885,7 +896,9 @@ enum ieee80211_smps_mode {
 *
 * @smps_mode: spatial multiplexing powersave mode; note that
 *	%IEEE80211_SMPS_STATIC is used when the device is not
 *	configured for an HT channel
 *	configured for an HT channel.
 *	Note that this is only valid if channel contexts are not used,
 *	otherwise each channel context has the number of chains listed.
 */
struct ieee80211_conf {
	u32 flags;
+11 −4
Original line number Diff line number Diff line
@@ -884,6 +884,10 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
	if (old)
		return -EALREADY;

	/* TODO: make hostapd tell us what it wants */
	sdata->smps_mode = IEEE80211_SMPS_OFF;
	sdata->needed_rx_chains = sdata->local->rx_chains;

	err = ieee80211_vif_use_channel(sdata, params->channel,
					params->channel_type,
					IEEE80211_CHANCTX_SHARED);
@@ -1673,6 +1677,10 @@ static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev,
	if (err)
		return err;

	/* can mesh use other SMPS modes? */
	sdata->smps_mode = IEEE80211_SMPS_OFF;
	sdata->needed_rx_chains = sdata->local->rx_chains;

	err = ieee80211_vif_use_channel(sdata, setup->channel,
					setup->channel_type,
					IEEE80211_CHANCTX_SHARED);
@@ -2052,13 +2060,12 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,

	/*
	 * If not associated, or current association is not an HT
	 * association, there's no need to send an action frame.
	 * association, there's no need to do anything, just store
	 * the new value until we associate.
	 */
	if (!sdata->u.mgd.associated ||
	    sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT) {
		ieee80211_recalc_smps(sdata->local);
	    sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT)
		return 0;
	}

	ap = sdata->u.mgd.associated->bssid;

+89 −1
Original line number Diff line number Diff line
@@ -118,6 +118,8 @@ ieee80211_new_chanctx(struct ieee80211_local *local,

	ctx->conf.channel = channel;
	ctx->conf.channel_type = channel_type;
	ctx->conf.rx_chains_static = 1;
	ctx->conf.rx_chains_dynamic = 1;
	ctx->mode = mode;

	if (!local->use_chanctx) {
@@ -222,8 +224,10 @@ static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata,

	drv_unassign_vif_chanctx(local, sdata, ctx);

	if (ctx->refcount > 0)
	if (ctx->refcount > 0) {
		ieee80211_recalc_chanctx_chantype(sdata->local, ctx);
		ieee80211_recalc_smps_chanctx(local, ctx);
	}
}

static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
@@ -246,6 +250,89 @@ static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
		ieee80211_free_chanctx(local, ctx);
}

void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
				   struct ieee80211_chanctx *chanctx)
{
	struct ieee80211_sub_if_data *sdata;
	u8 rx_chains_static, rx_chains_dynamic;

	lockdep_assert_held(&local->chanctx_mtx);

	rx_chains_static = 1;
	rx_chains_dynamic = 1;

	rcu_read_lock();
	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
		u8 needed_static, needed_dynamic;

		if (!ieee80211_sdata_running(sdata))
			continue;

		if (rcu_access_pointer(sdata->vif.chanctx_conf) !=
						&chanctx->conf)
			continue;

		switch (sdata->vif.type) {
		case NL80211_IFTYPE_P2P_DEVICE:
			continue;
		case NL80211_IFTYPE_STATION:
			if (!sdata->u.mgd.associated)
				continue;
			break;
		case NL80211_IFTYPE_AP_VLAN:
			continue;
		case NL80211_IFTYPE_AP:
		case NL80211_IFTYPE_ADHOC:
		case NL80211_IFTYPE_WDS:
		case NL80211_IFTYPE_MESH_POINT:
			break;
		default:
			WARN_ON_ONCE(1);
		}

		switch (sdata->smps_mode) {
		default:
			WARN_ONCE(1, "Invalid SMPS mode %d\n",
				  sdata->smps_mode);
			/* fall through */
		case IEEE80211_SMPS_OFF:
			needed_static = sdata->needed_rx_chains;
			needed_dynamic = sdata->needed_rx_chains;
			break;
		case IEEE80211_SMPS_DYNAMIC:
			needed_static = 1;
			needed_dynamic = sdata->needed_rx_chains;
			break;
		case IEEE80211_SMPS_STATIC:
			needed_static = 1;
			needed_dynamic = 1;
			break;
		}

		rx_chains_static = max(rx_chains_static, needed_static);
		rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic);
	}
	rcu_read_unlock();

	if (!local->use_chanctx) {
		if (rx_chains_static > 1)
			local->smps_mode = IEEE80211_SMPS_OFF;
		else if (rx_chains_dynamic > 1)
			local->smps_mode = IEEE80211_SMPS_DYNAMIC;
		else
			local->smps_mode = IEEE80211_SMPS_STATIC;
		ieee80211_hw_config(local, 0);
	}

	if (rx_chains_static == chanctx->conf.rx_chains_static &&
	    rx_chains_dynamic == chanctx->conf.rx_chains_dynamic)
		return;

	chanctx->conf.rx_chains_static = rx_chains_static;
	chanctx->conf.rx_chains_dynamic = rx_chains_dynamic;
	drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS);
}

int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
			      struct ieee80211_channel *channel,
			      enum nl80211_channel_type channel_type,
@@ -278,6 +365,7 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
		goto out;
	}

	ieee80211_recalc_smps_chanctx(local, ctx);
 out:
	mutex_unlock(&local->chanctx_mtx);
	return ret;
+1 −1
Original line number Diff line number Diff line
@@ -217,7 +217,7 @@ static ssize_t ieee80211_if_fmt_smps(const struct ieee80211_sub_if_data *sdata,

	return snprintf(buf, buflen, "request: %s\nused: %s\n",
			smps_modes[sdata->u.mgd.req_smps],
			smps_modes[sdata->u.mgd.ap_smps]);
			smps_modes[sdata->smps_mode]);
}

static ssize_t ieee80211_if_parse_smps(struct ieee80211_sub_if_data *sdata,
+3 −0
Original line number Diff line number Diff line
@@ -1132,6 +1132,9 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
	changed |= BSS_CHANGED_HT;
	ieee80211_bss_info_change_notify(sdata, changed);

	sdata->smps_mode = IEEE80211_SMPS_OFF;
	sdata->needed_rx_chains = sdata->local->rx_chains;

	ieee80211_queue_work(&sdata->local->hw, &sdata->work);

	return 0;
Loading