mac80211: support VHT association
Determine the VHT channel from the AP's VHT operation IE (if present) and configure the hardware to that channel if it is supported. If channel contexts cause a channel to not be usable, try a smaller bandwidth. Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
		
					parent
					
						
							
								9f5e8f6efc
							
						
					
				
			
			
				commit
				
					
						f2d9d270c1
					
				
			
		
					 3 changed files with 313 additions and 72 deletions
				
			
		| 
						 | 
				
			
			@ -1212,6 +1212,21 @@ struct ieee80211_vht_cap {
 | 
			
		|||
	struct ieee80211_vht_mcs_info supp_mcs;
 | 
			
		||||
} __packed;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * enum ieee80211_vht_chanwidth - VHT channel width
 | 
			
		||||
 * @IEEE80211_VHT_CHANWIDTH_USE_HT: use the HT operation IE to
 | 
			
		||||
 *	determine the channel width (20 or 40 MHz)
 | 
			
		||||
 * @IEEE80211_VHT_CHANWIDTH_80MHZ: 80 MHz bandwidth
 | 
			
		||||
 * @IEEE80211_VHT_CHANWIDTH_160MHZ: 160 MHz bandwidth
 | 
			
		||||
 * @IEEE80211_VHT_CHANWIDTH_80P80MHZ: 80+80 MHz bandwidth
 | 
			
		||||
 */
 | 
			
		||||
enum ieee80211_vht_chanwidth {
 | 
			
		||||
	IEEE80211_VHT_CHANWIDTH_USE_HT		= 0,
 | 
			
		||||
	IEEE80211_VHT_CHANWIDTH_80MHZ		= 1,
 | 
			
		||||
	IEEE80211_VHT_CHANWIDTH_160MHZ		= 2,
 | 
			
		||||
	IEEE80211_VHT_CHANWIDTH_80P80MHZ	= 3,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * struct ieee80211_vht_operation - VHT operation IE
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -371,6 +371,8 @@ enum ieee80211_sta_flags {
 | 
			
		|||
	IEEE80211_STA_RESET_SIGNAL_AVE	= BIT(9),
 | 
			
		||||
	IEEE80211_STA_DISABLE_40MHZ	= BIT(10),
 | 
			
		||||
	IEEE80211_STA_DISABLE_VHT	= BIT(11),
 | 
			
		||||
	IEEE80211_STA_DISABLE_80P80MHZ	= BIT(12),
 | 
			
		||||
	IEEE80211_STA_DISABLE_160MHZ	= BIT(13),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ieee80211_mgd_auth_data {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -354,6 +354,16 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
 | 
			
		|||
	/* determine capability flags */
 | 
			
		||||
	cap = vht_cap.cap;
 | 
			
		||||
 | 
			
		||||
	if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_80P80MHZ) {
 | 
			
		||||
		cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
 | 
			
		||||
		cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_160MHZ) {
 | 
			
		||||
		cap &= ~IEEE80211_VHT_CAP_SHORT_GI_160;
 | 
			
		||||
		cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* reserve and fill IE */
 | 
			
		||||
	pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
 | 
			
		||||
	ieee80211_ie_build_vht_cap(pos, &vht_cap, cap);
 | 
			
		||||
| 
						 | 
				
			
			@ -543,6 +553,10 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
 | 
			
		|||
		offset = noffset;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (WARN_ON_ONCE((ifmgd->flags & IEEE80211_STA_DISABLE_HT) &&
 | 
			
		||||
			 !(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)))
 | 
			
		||||
		ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
 | 
			
		||||
 | 
			
		||||
	if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT))
 | 
			
		||||
		ieee80211_add_ht_ie(sdata, skb, assoc_data->ap_ht_param,
 | 
			
		||||
				    sband, chan, sdata->smps_mode);
 | 
			
		||||
| 
						 | 
				
			
			@ -3183,35 +3197,87 @@ int ieee80211_max_network_latency(struct notifier_block *nb,
 | 
			
		|||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
 | 
			
		||||
				  struct cfg80211_bss *cbss)
 | 
			
		||||
static u32 chandef_downgrade(struct cfg80211_chan_def *c)
 | 
			
		||||
{
 | 
			
		||||
	struct ieee80211_local *local = sdata->local;
 | 
			
		||||
	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 | 
			
		||||
	int ht_cfreq;
 | 
			
		||||
	enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
 | 
			
		||||
	const u8 *ht_oper_ie;
 | 
			
		||||
	const struct ieee80211_ht_operation *ht_oper = NULL;
 | 
			
		||||
	struct ieee80211_supported_band *sband;
 | 
			
		||||
	struct cfg80211_chan_def chandef;
 | 
			
		||||
	u32 ret;
 | 
			
		||||
	int tmp;
 | 
			
		||||
 | 
			
		||||
	sband = local->hw.wiphy->bands[cbss->channel->band];
 | 
			
		||||
 | 
			
		||||
	ifmgd->flags &= ~IEEE80211_STA_DISABLE_40MHZ;
 | 
			
		||||
 | 
			
		||||
	if (sband->ht_cap.ht_supported) {
 | 
			
		||||
		ht_oper_ie = cfg80211_find_ie(WLAN_EID_HT_OPERATION,
 | 
			
		||||
					      cbss->information_elements,
 | 
			
		||||
					      cbss->len_information_elements);
 | 
			
		||||
		if (ht_oper_ie && ht_oper_ie[1] >= sizeof(*ht_oper))
 | 
			
		||||
			ht_oper = (void *)(ht_oper_ie + 2);
 | 
			
		||||
	switch (c->width) {
 | 
			
		||||
	case NL80211_CHAN_WIDTH_20:
 | 
			
		||||
		c->width = NL80211_CHAN_WIDTH_20_NOHT;
 | 
			
		||||
		ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
 | 
			
		||||
		break;
 | 
			
		||||
	case NL80211_CHAN_WIDTH_40:
 | 
			
		||||
		c->width = NL80211_CHAN_WIDTH_20;
 | 
			
		||||
		c->center_freq1 = c->chan->center_freq;
 | 
			
		||||
		ret = IEEE80211_STA_DISABLE_40MHZ |
 | 
			
		||||
		      IEEE80211_STA_DISABLE_VHT;
 | 
			
		||||
		break;
 | 
			
		||||
	case NL80211_CHAN_WIDTH_80:
 | 
			
		||||
		tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
 | 
			
		||||
		/* n_P40 */
 | 
			
		||||
		tmp /= 2;
 | 
			
		||||
		/* freq_P40 */
 | 
			
		||||
		c->center_freq1 = c->center_freq1 - 20 + 40 * tmp;
 | 
			
		||||
		c->width = NL80211_CHAN_WIDTH_40;
 | 
			
		||||
		ret = IEEE80211_STA_DISABLE_VHT;
 | 
			
		||||
		break;
 | 
			
		||||
	case NL80211_CHAN_WIDTH_80P80:
 | 
			
		||||
		c->center_freq2 = 0;
 | 
			
		||||
		c->width = NL80211_CHAN_WIDTH_80;
 | 
			
		||||
		ret = IEEE80211_STA_DISABLE_80P80MHZ |
 | 
			
		||||
		      IEEE80211_STA_DISABLE_160MHZ;
 | 
			
		||||
		break;
 | 
			
		||||
	case NL80211_CHAN_WIDTH_160:
 | 
			
		||||
		/* n_P20 */
 | 
			
		||||
		tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
 | 
			
		||||
		/* n_P80 */
 | 
			
		||||
		tmp /= 4;
 | 
			
		||||
		c->center_freq1 = c->center_freq1 - 40 + 80 * tmp;
 | 
			
		||||
		c->width = NL80211_CHAN_WIDTH_80;
 | 
			
		||||
		ret = IEEE80211_STA_DISABLE_80P80MHZ |
 | 
			
		||||
		      IEEE80211_STA_DISABLE_160MHZ;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
	case NL80211_CHAN_WIDTH_20_NOHT:
 | 
			
		||||
		WARN_ON_ONCE(1);
 | 
			
		||||
		c->width = NL80211_CHAN_WIDTH_20_NOHT;
 | 
			
		||||
		ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (ht_oper) {
 | 
			
		||||
	WARN_ON_ONCE(!cfg80211_chandef_valid(c));
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static u32
 | 
			
		||||
ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
 | 
			
		||||
			     struct ieee80211_supported_band *sband,
 | 
			
		||||
			     struct ieee80211_channel *channel,
 | 
			
		||||
			     const struct ieee80211_ht_operation *ht_oper,
 | 
			
		||||
			     const struct ieee80211_vht_operation *vht_oper,
 | 
			
		||||
			     struct cfg80211_chan_def *chandef)
 | 
			
		||||
{
 | 
			
		||||
	struct cfg80211_chan_def vht_chandef;
 | 
			
		||||
	u32 ht_cfreq, ret;
 | 
			
		||||
 | 
			
		||||
	chandef->chan = channel;
 | 
			
		||||
	chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
 | 
			
		||||
	chandef->center_freq1 = channel->center_freq;
 | 
			
		||||
	chandef->center_freq2 = 0;
 | 
			
		||||
 | 
			
		||||
	if (!ht_oper || !sband->ht_cap.ht_supported) {
 | 
			
		||||
		ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	chandef->width = NL80211_CHAN_WIDTH_20;
 | 
			
		||||
 | 
			
		||||
	ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan,
 | 
			
		||||
							  cbss->channel->band);
 | 
			
		||||
						  channel->band);
 | 
			
		||||
	/* check that channel matches the right operating channel */
 | 
			
		||||
		if (cbss->channel->center_freq != ht_cfreq) {
 | 
			
		||||
	if (channel->center_freq != ht_cfreq) {
 | 
			
		||||
		/*
 | 
			
		||||
		 * It's possible that some APs are confused here;
 | 
			
		||||
		 * Netgear WNDR3700 sometimes reports 4 higher than
 | 
			
		||||
| 
						 | 
				
			
			@ -3221,68 +3287,226 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
 | 
			
		|||
		 */
 | 
			
		||||
		sdata_info(sdata,
 | 
			
		||||
			   "Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n",
 | 
			
		||||
				   cbss->channel->center_freq,
 | 
			
		||||
				   ht_cfreq, ht_oper->primary_chan,
 | 
			
		||||
				   cbss->channel->band);
 | 
			
		||||
			ht_oper = NULL;
 | 
			
		||||
		}
 | 
			
		||||
			   channel->center_freq, ht_cfreq,
 | 
			
		||||
			   ht_oper->primary_chan, channel->band);
 | 
			
		||||
		ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (ht_oper) {
 | 
			
		||||
		/*
 | 
			
		||||
		 * cfg80211 already verified that the channel itself can
 | 
			
		||||
		 * be used, but it didn't check that we can do the right
 | 
			
		||||
		 * HT type, so do that here as well. If HT40 isn't allowed
 | 
			
		||||
		 * on this channel, disable 40 MHz operation.
 | 
			
		||||
		 */
 | 
			
		||||
		const u8 *ht_cap_ie;
 | 
			
		||||
		const struct ieee80211_ht_cap *ht_cap;
 | 
			
		||||
		u8 chains = 1;
 | 
			
		||||
 | 
			
		||||
		channel_type = NL80211_CHAN_HT20;
 | 
			
		||||
 | 
			
		||||
	/* check 40 MHz support, if we have it */
 | 
			
		||||
	if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
 | 
			
		||||
			switch (ht_oper->ht_param &
 | 
			
		||||
					IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
 | 
			
		||||
		switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
 | 
			
		||||
		case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
 | 
			
		||||
				if (cbss->channel->flags &
 | 
			
		||||
						IEEE80211_CHAN_NO_HT40PLUS)
 | 
			
		||||
					ifmgd->flags |=
 | 
			
		||||
						IEEE80211_STA_DISABLE_40MHZ;
 | 
			
		||||
				else
 | 
			
		||||
					channel_type = NL80211_CHAN_HT40PLUS;
 | 
			
		||||
			chandef->width = NL80211_CHAN_WIDTH_40;
 | 
			
		||||
			chandef->center_freq1 += 10;
 | 
			
		||||
			break;
 | 
			
		||||
		case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
 | 
			
		||||
				if (cbss->channel->flags &
 | 
			
		||||
						IEEE80211_CHAN_NO_HT40MINUS)
 | 
			
		||||
					ifmgd->flags |=
 | 
			
		||||
						IEEE80211_STA_DISABLE_40MHZ;
 | 
			
		||||
				else
 | 
			
		||||
					channel_type = NL80211_CHAN_HT40MINUS;
 | 
			
		||||
			chandef->width = NL80211_CHAN_WIDTH_40;
 | 
			
		||||
			chandef->center_freq1 -= 10;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		/* 40 MHz (and 80 MHz) must be supported for VHT */
 | 
			
		||||
		ret = IEEE80211_STA_DISABLE_VHT;
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!vht_oper || !sband->vht_cap.vht_supported) {
 | 
			
		||||
		ret = IEEE80211_STA_DISABLE_VHT;
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vht_chandef.chan = channel;
 | 
			
		||||
	vht_chandef.center_freq1 =
 | 
			
		||||
		ieee80211_channel_to_frequency(vht_oper->center_freq_seg1_idx,
 | 
			
		||||
					       channel->band);
 | 
			
		||||
	vht_chandef.center_freq2 = 0;
 | 
			
		||||
 | 
			
		||||
	if (vht_oper->center_freq_seg2_idx)
 | 
			
		||||
		vht_chandef.center_freq2 =
 | 
			
		||||
			ieee80211_channel_to_frequency(
 | 
			
		||||
				vht_oper->center_freq_seg2_idx,
 | 
			
		||||
				channel->band);
 | 
			
		||||
 | 
			
		||||
	switch (vht_oper->chan_width) {
 | 
			
		||||
	case IEEE80211_VHT_CHANWIDTH_USE_HT:
 | 
			
		||||
		vht_chandef.width = chandef->width;
 | 
			
		||||
		break;
 | 
			
		||||
	case IEEE80211_VHT_CHANWIDTH_80MHZ:
 | 
			
		||||
		vht_chandef.width = NL80211_CHAN_WIDTH_80;
 | 
			
		||||
		break;
 | 
			
		||||
	case IEEE80211_VHT_CHANWIDTH_160MHZ:
 | 
			
		||||
		vht_chandef.width = NL80211_CHAN_WIDTH_160;
 | 
			
		||||
		break;
 | 
			
		||||
	case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
 | 
			
		||||
		vht_chandef.width = NL80211_CHAN_WIDTH_80P80;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		sdata_info(sdata,
 | 
			
		||||
			   "AP VHT operation IE has invalid channel width (%d), disable VHT\n",
 | 
			
		||||
			   vht_oper->chan_width);
 | 
			
		||||
		ret = IEEE80211_STA_DISABLE_VHT;
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!cfg80211_chandef_valid(&vht_chandef)) {
 | 
			
		||||
		sdata_info(sdata,
 | 
			
		||||
			   "AP VHT information is invalid, disable VHT\n");
 | 
			
		||||
		ret = IEEE80211_STA_DISABLE_VHT;
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (cfg80211_chandef_identical(chandef, &vht_chandef)) {
 | 
			
		||||
		ret = 0;
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!cfg80211_chandef_compatible(chandef, &vht_chandef)) {
 | 
			
		||||
		sdata_info(sdata,
 | 
			
		||||
			   "AP VHT information doesn't match HT, disable VHT\n");
 | 
			
		||||
		ret = IEEE80211_STA_DISABLE_VHT;
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	*chandef = vht_chandef;
 | 
			
		||||
 | 
			
		||||
	ret = 0;
 | 
			
		||||
 | 
			
		||||
	while (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
 | 
			
		||||
					IEEE80211_CHAN_DISABLED)) {
 | 
			
		||||
		if (WARN_ON(chandef->width == NL80211_CHAN_WIDTH_20_NOHT)) {
 | 
			
		||||
			ret = IEEE80211_STA_DISABLE_HT |
 | 
			
		||||
			      IEEE80211_STA_DISABLE_VHT;
 | 
			
		||||
			goto out;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ret = chandef_downgrade(chandef);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (chandef->width != vht_chandef.width)
 | 
			
		||||
		sdata_info(sdata,
 | 
			
		||||
			   "local regulatory prevented using AP HT/VHT configuration, downgraded\n");
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
	WARN_ON_ONCE(!cfg80211_chandef_valid(chandef));
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static u8 ieee80211_ht_vht_rx_chains(struct ieee80211_sub_if_data *sdata,
 | 
			
		||||
				     struct cfg80211_bss *cbss)
 | 
			
		||||
{
 | 
			
		||||
	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 | 
			
		||||
	const u8 *ht_cap_ie, *vht_cap_ie;
 | 
			
		||||
	const struct ieee80211_ht_cap *ht_cap;
 | 
			
		||||
	const struct ieee80211_vht_cap *vht_cap;
 | 
			
		||||
	u8 chains = 1;
 | 
			
		||||
 | 
			
		||||
	if (ifmgd->flags & IEEE80211_STA_DISABLE_HT)
 | 
			
		||||
		return chains;
 | 
			
		||||
 | 
			
		||||
	ht_cap_ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY,
 | 
			
		||||
				     cbss->information_elements,
 | 
			
		||||
				     cbss->len_information_elements);
 | 
			
		||||
	if (ht_cap_ie && ht_cap_ie[1] >= sizeof(*ht_cap)) {
 | 
			
		||||
		ht_cap = (void *)(ht_cap_ie + 2);
 | 
			
		||||
		chains = ieee80211_mcs_to_chains(&ht_cap->mcs);
 | 
			
		||||
		/*
 | 
			
		||||
		 * TODO: use "Tx Maximum Number Spatial Streams Supported" and
 | 
			
		||||
		 *	 "Tx Unequal Modulation Supported" fields.
 | 
			
		||||
		 */
 | 
			
		||||
	}
 | 
			
		||||
		sdata->needed_rx_chains = min(chains, local->rx_chains);
 | 
			
		||||
	} else {
 | 
			
		||||
		sdata->needed_rx_chains = 1;
 | 
			
		||||
		sdata->u.mgd.flags |= IEEE80211_STA_DISABLE_HT;
 | 
			
		||||
 | 
			
		||||
	if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT)
 | 
			
		||||
		return chains;
 | 
			
		||||
 | 
			
		||||
	vht_cap_ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY,
 | 
			
		||||
				      cbss->information_elements,
 | 
			
		||||
				      cbss->len_information_elements);
 | 
			
		||||
	if (vht_cap_ie && vht_cap_ie[1] >= sizeof(*vht_cap)) {
 | 
			
		||||
		u8 nss;
 | 
			
		||||
		u16 tx_mcs_map;
 | 
			
		||||
 | 
			
		||||
		vht_cap = (void *)(vht_cap_ie + 2);
 | 
			
		||||
		tx_mcs_map = le16_to_cpu(vht_cap->supp_mcs.tx_mcs_map);
 | 
			
		||||
		for (nss = 8; nss > 0; nss--) {
 | 
			
		||||
			if (((tx_mcs_map >> (2 * (nss - 1))) & 3) !=
 | 
			
		||||
					IEEE80211_VHT_MCS_NOT_SUPPORTED)
 | 
			
		||||
				break;
 | 
			
		||||
		}
 | 
			
		||||
		/* TODO: use "Tx Highest Supported Long GI Data Rate" field? */
 | 
			
		||||
		chains = max(chains, nss);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return chains;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
 | 
			
		||||
				  struct cfg80211_bss *cbss)
 | 
			
		||||
{
 | 
			
		||||
	struct ieee80211_local *local = sdata->local;
 | 
			
		||||
	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 | 
			
		||||
	const struct ieee80211_ht_operation *ht_oper = NULL;
 | 
			
		||||
	const struct ieee80211_vht_operation *vht_oper = NULL;
 | 
			
		||||
	struct ieee80211_supported_band *sband;
 | 
			
		||||
	struct cfg80211_chan_def chandef;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	sband = local->hw.wiphy->bands[cbss->channel->band];
 | 
			
		||||
 | 
			
		||||
	ifmgd->flags &= ~(IEEE80211_STA_DISABLE_40MHZ |
 | 
			
		||||
			  IEEE80211_STA_DISABLE_80P80MHZ |
 | 
			
		||||
			  IEEE80211_STA_DISABLE_160MHZ);
 | 
			
		||||
 | 
			
		||||
	if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT) &&
 | 
			
		||||
	    sband->ht_cap.ht_supported) {
 | 
			
		||||
		const u8 *ht_oper_ie;
 | 
			
		||||
 | 
			
		||||
		ht_oper_ie = cfg80211_find_ie(WLAN_EID_HT_OPERATION,
 | 
			
		||||
					      cbss->information_elements,
 | 
			
		||||
					      cbss->len_information_elements);
 | 
			
		||||
		if (ht_oper_ie && ht_oper_ie[1] >= sizeof(*ht_oper))
 | 
			
		||||
			ht_oper = (void *)(ht_oper_ie + 2);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT) &&
 | 
			
		||||
	    sband->vht_cap.vht_supported) {
 | 
			
		||||
		const u8 *vht_oper_ie;
 | 
			
		||||
 | 
			
		||||
		vht_oper_ie = cfg80211_find_ie(WLAN_EID_VHT_OPERATION,
 | 
			
		||||
					       cbss->information_elements,
 | 
			
		||||
					       cbss->len_information_elements);
 | 
			
		||||
		if (vht_oper_ie && vht_oper_ie[1] >= sizeof(*vht_oper))
 | 
			
		||||
			vht_oper = (void *)(vht_oper_ie + 2);
 | 
			
		||||
		if (vht_oper && !ht_oper) {
 | 
			
		||||
			vht_oper = NULL;
 | 
			
		||||
			sdata_info(sdata,
 | 
			
		||||
				   "AP advertised VHT without HT, disabling both\n");
 | 
			
		||||
			sdata->flags |= IEEE80211_STA_DISABLE_HT;
 | 
			
		||||
			sdata->flags |= IEEE80211_STA_DISABLE_VHT;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ifmgd->flags |= ieee80211_determine_chantype(sdata, sband,
 | 
			
		||||
						     cbss->channel,
 | 
			
		||||
						     ht_oper, vht_oper,
 | 
			
		||||
						     &chandef);
 | 
			
		||||
 | 
			
		||||
	sdata->needed_rx_chains = min(ieee80211_ht_vht_rx_chains(sdata, cbss),
 | 
			
		||||
				      local->rx_chains);
 | 
			
		||||
 | 
			
		||||
	/* will change later if needed */
 | 
			
		||||
	sdata->smps_mode = IEEE80211_SMPS_OFF;
 | 
			
		||||
 | 
			
		||||
	ieee80211_vif_release_channel(sdata);
 | 
			
		||||
	cfg80211_chandef_create(&chandef, cbss->channel, channel_type);
 | 
			
		||||
	return ieee80211_vif_use_channel(sdata, &chandef,
 | 
			
		||||
	/*
 | 
			
		||||
	 * If this fails (possibly due to channel context sharing
 | 
			
		||||
	 * on incompatible channels, e.g. 80+80 and 160 sharing the
 | 
			
		||||
	 * same control channel) try to use a smaller bandwidth.
 | 
			
		||||
	 */
 | 
			
		||||
	ret = ieee80211_vif_use_channel(sdata, &chandef,
 | 
			
		||||
					IEEE80211_CHANCTX_SHARED);
 | 
			
		||||
	while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT)
 | 
			
		||||
		ifmgd->flags |= chandef_downgrade(&chandef);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue