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

Commit 0aaffa9b authored by Johannes Berg's avatar Johannes Berg Committed by John W. Linville
Browse files

mac80211: improve HT channel handling



Currently, when one interface switches HT mode,
all others will follow along. This is clearly
undesirable, since the new one might switch to
no-HT while another one is operating in HT.

Address this issue by keeping track of the HT
mode per interface, and allowing only changes
that are compatible, i.e. switching into HT40+
is not possible when another interface is in
HT40-, in that case the second one needs to
fall back to HT20.

Also, to allow drivers to know what's going on,
store the per-interface HT mode (channel type)
in the virtual interface's bss_conf.

Signed-off-by: default avatarJohannes Berg <johannes@sipsolutions.net>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent f444de05
Loading
Loading
Loading
Loading
+10 −9
Original line number Diff line number Diff line
@@ -651,17 +651,17 @@ static void mac80211_hwsim_beacon(unsigned long arg)
	add_timer(&data->beacon_timer);
}


static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed)
{
	struct mac80211_hwsim_data *data = hw->priv;
	struct ieee80211_conf *conf = &hw->conf;
	static const char *chantypes[4] = {
static const char *hwsim_chantypes[] = {
	[NL80211_CHAN_NO_HT] = "noht",
	[NL80211_CHAN_HT20] = "ht20",
	[NL80211_CHAN_HT40MINUS] = "ht40-",
	[NL80211_CHAN_HT40PLUS] = "ht40+",
};

static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed)
{
	struct mac80211_hwsim_data *data = hw->priv;
	struct ieee80211_conf *conf = &hw->conf;
	static const char *smps_modes[IEEE80211_SMPS_NUM_MODES] = {
		[IEEE80211_SMPS_AUTOMATIC] = "auto",
		[IEEE80211_SMPS_OFF] = "off",
@@ -672,7 +672,7 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed)
	printk(KERN_DEBUG "%s:%s (freq=%d/%s idle=%d ps=%d smps=%s)\n",
	       wiphy_name(hw->wiphy), __func__,
	       conf->channel->center_freq,
	       chantypes[conf->channel_type],
	       hwsim_chantypes[conf->channel_type],
	       !!(conf->flags & IEEE80211_CONF_IDLE),
	       !!(conf->flags & IEEE80211_CONF_PS),
	       smps_modes[conf->smps_mode]);
@@ -760,9 +760,10 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw,
	}

	if (changed & BSS_CHANGED_HT) {
		printk(KERN_DEBUG "  %s: HT: op_mode=0x%x\n",
		printk(KERN_DEBUG "  %s: HT: op_mode=0x%x, chantype=%s\n",
		       wiphy_name(hw->wiphy),
		       info->ht_operation_mode);
		       info->ht_operation_mode,
		       hwsim_chantypes[info->channel_type]);
	}

	if (changed & BSS_CHANGED_BASIC_RATES) {
+4 −0
Original line number Diff line number Diff line
@@ -191,6 +191,9 @@ enum ieee80211_bss_change {
 *	the current band.
 * @bssid: The BSSID for this BSS
 * @enable_beacon: whether beaconing should be enabled or not
 * @channel_type: Channel type for this BSS -- the hardware might be
 *	configured for HT40+ while this BSS only uses no-HT, for
 *	example.
 * @ht_operation_mode: HT operation mode (like in &struct ieee80211_ht_info).
 *	This field is only valid when the channel type is one of the HT types.
 * @cqm_rssi_thold: Connection quality monitor RSSI threshold, a zero value
@@ -215,6 +218,7 @@ struct ieee80211_bss_conf {
	u16 ht_operation_mode;
	s32 cqm_rssi_thold;
	u32 cqm_rssi_hyst;
	enum nl80211_channel_type channel_type;
};

/**
+17 −6
Original line number Diff line number Diff line
@@ -1166,23 +1166,34 @@ static int ieee80211_set_channel(struct wiphy *wiphy,
				 enum nl80211_channel_type channel_type)
{
	struct ieee80211_local *local = wiphy_priv(wiphy);
	struct ieee80211_sub_if_data *sdata = NULL;

	if (netdev)
		sdata = IEEE80211_DEV_TO_SUB_IF(netdev);

	switch (ieee80211_get_channel_mode(local, NULL)) {
	case CHAN_MODE_HOPPING:
		return -EBUSY;
	case CHAN_MODE_FIXED:
		if (local->oper_channel == chan &&
		    local->oper_channel_type == channel_type)
			return 0;
		if (local->oper_channel != chan)
			return -EBUSY;
		if (!sdata && local->_oper_channel_type == channel_type)
			return 0;
		break;
	case CHAN_MODE_UNDEFINED:
		break;
	}

	local->oper_channel = chan;
	local->oper_channel_type = channel_type;

	return ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
	if (!ieee80211_set_channel_type(local, sdata, channel_type))
		return -EBUSY;

	ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
	if (sdata && sdata->vif.type != NL80211_IFTYPE_MONITOR)
		ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_HT);

	return 0;
}

#ifdef CONFIG_PM
@@ -1406,7 +1417,7 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
	 * association, there's no need to send an action frame.
	 */
	if (!sdata->u.mgd.associated ||
	    sdata->local->oper_channel_type == NL80211_CHAN_NO_HT) {
	    sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT) {
		mutex_lock(&sdata->local->iflist_mtx);
		ieee80211_recalc_smps(sdata->local, sdata);
		mutex_unlock(&sdata->local->iflist_mtx);
+70 −0
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@
 * mac80211 - channel management
 */

#include <linux/nl80211.h>
#include "ieee80211_i.h"

enum ieee80211_chan_mode
@@ -55,3 +56,72 @@ ieee80211_get_channel_mode(struct ieee80211_local *local,

	return mode;
}

bool ieee80211_set_channel_type(struct ieee80211_local *local,
				struct ieee80211_sub_if_data *sdata,
				enum nl80211_channel_type chantype)
{
	struct ieee80211_sub_if_data *tmp;
	enum nl80211_channel_type superchan = NL80211_CHAN_NO_HT;
	bool result;

	mutex_lock(&local->iflist_mtx);

	list_for_each_entry(tmp, &local->interfaces, list) {
		if (tmp == sdata)
			continue;

		if (!ieee80211_sdata_running(tmp))
			continue;

		switch (tmp->vif.bss_conf.channel_type) {
		case NL80211_CHAN_NO_HT:
		case NL80211_CHAN_HT20:
			superchan = tmp->vif.bss_conf.channel_type;
			break;
		case NL80211_CHAN_HT40PLUS:
			WARN_ON(superchan == NL80211_CHAN_HT40MINUS);
			superchan = NL80211_CHAN_HT40PLUS;
			break;
		case NL80211_CHAN_HT40MINUS:
			WARN_ON(superchan == NL80211_CHAN_HT40PLUS);
			superchan = NL80211_CHAN_HT40MINUS;
			break;
		}
	}

	switch (superchan) {
	case NL80211_CHAN_NO_HT:
	case NL80211_CHAN_HT20:
		/*
		 * allow any change that doesn't go to no-HT
		 * (if it already is no-HT no change is needed)
		 */
		if (chantype == NL80211_CHAN_NO_HT)
			break;
		superchan = chantype;
		break;
	case NL80211_CHAN_HT40PLUS:
	case NL80211_CHAN_HT40MINUS:
		/* allow smaller bandwidth and same */
		if (chantype == NL80211_CHAN_NO_HT)
			break;
		if (chantype == NL80211_CHAN_HT20)
			break;
		if (superchan == chantype)
			break;
		result = false;
		goto out;
	}

	local->_oper_channel_type = superchan;

	if (sdata)
		sdata->vif.bss_conf.channel_type = chantype;

	result = true;
 out:
	mutex_unlock(&local->iflist_mtx);

	return result;
}
+3 −2
Original line number Diff line number Diff line
@@ -102,7 +102,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
	sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0;

	local->oper_channel = chan;
	local->oper_channel_type = NL80211_CHAN_NO_HT;
	WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_NO_HT));
	ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);

	sband = local->hw.wiphy->bands[chan->band];
@@ -910,7 +910,8 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
	/* fix ourselves to that channel now already */
	if (params->channel_fixed) {
		sdata->local->oper_channel = params->channel;
		sdata->local->oper_channel_type = NL80211_CHAN_NO_HT;
		WARN_ON(!ieee80211_set_channel_type(sdata->local, sdata,
						    NL80211_CHAN_NO_HT));
	}

	if (params->ie) {
Loading