diff options
Diffstat (limited to 'net/mac80211/ht.c')
| -rw-r--r-- | net/mac80211/ht.c | 110 | 
1 files changed, 77 insertions, 33 deletions
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 61ac7c48ac0..0db25d4bb22 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -37,6 +37,9 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,  	u8 *smask = (u8 *)(&sdata->u.mgd.ht_capa_mask.mcs.rx_mask);  	int i; +	if (!ht_cap->ht_supported) +		return; +  	if (sdata->vif.type != NL80211_IFTYPE_STATION) {  		/* AP interfaces call this code when adding new stations,  		 * so just silently ignore non station interfaces. @@ -89,22 +92,24 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,  } -void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, +bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,  				       struct ieee80211_supported_band *sband, -				       struct ieee80211_ht_cap *ht_cap_ie, -				       struct ieee80211_sta_ht_cap *ht_cap) +				       const struct ieee80211_ht_cap *ht_cap_ie, +				       struct sta_info *sta)  { +	struct ieee80211_sta_ht_cap ht_cap;  	u8 ampdu_info, tx_mcs_set_cap;  	int i, max_tx_streams; +	bool changed; +	enum ieee80211_sta_rx_bandwidth bw; +	enum ieee80211_smps_mode smps_mode; -	BUG_ON(!ht_cap); - -	memset(ht_cap, 0, sizeof(*ht_cap)); +	memset(&ht_cap, 0, sizeof(ht_cap));  	if (!ht_cap_ie || !sband->ht_cap.ht_supported) -		return; +		goto apply; -	ht_cap->ht_supported = true; +	ht_cap.ht_supported = true;  	/*  	 * The bits listed in this expression should be @@ -112,7 +117,7 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,  	 * advertises more then we can't use those thus  	 * we mask them out.  	 */ -	ht_cap->cap = le16_to_cpu(ht_cap_ie->cap_info) & +	ht_cap.cap = le16_to_cpu(ht_cap_ie->cap_info) &  		(sband->ht_cap.cap |  		 ~(IEEE80211_HT_CAP_LDPC_CODING |  		   IEEE80211_HT_CAP_SUP_WIDTH_20_40 | @@ -121,44 +126,30 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,  		   IEEE80211_HT_CAP_SGI_40 |  		   IEEE80211_HT_CAP_DSSSCCK40)); -	/* Unset 40 MHz if we're not using a 40 MHz channel */ -	switch (sdata->vif.bss_conf.chandef.width) { -	case NL80211_CHAN_WIDTH_20_NOHT: -	case NL80211_CHAN_WIDTH_20: -		ht_cap->cap &= ~IEEE80211_HT_CAP_SGI_40; -		ht_cap->cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; -		break; -	case NL80211_CHAN_WIDTH_40: -	case NL80211_CHAN_WIDTH_80: -	case NL80211_CHAN_WIDTH_80P80: -	case NL80211_CHAN_WIDTH_160: -		break; -	} -  	/*  	 * The STBC bits are asymmetric -- if we don't have  	 * TX then mask out the peer's RX and vice versa.  	 */  	if (!(sband->ht_cap.cap & IEEE80211_HT_CAP_TX_STBC)) -		ht_cap->cap &= ~IEEE80211_HT_CAP_RX_STBC; +		ht_cap.cap &= ~IEEE80211_HT_CAP_RX_STBC;  	if (!(sband->ht_cap.cap & IEEE80211_HT_CAP_RX_STBC)) -		ht_cap->cap &= ~IEEE80211_HT_CAP_TX_STBC; +		ht_cap.cap &= ~IEEE80211_HT_CAP_TX_STBC;  	ampdu_info = ht_cap_ie->ampdu_params_info; -	ht_cap->ampdu_factor = +	ht_cap.ampdu_factor =  		ampdu_info & IEEE80211_HT_AMPDU_PARM_FACTOR; -	ht_cap->ampdu_density = +	ht_cap.ampdu_density =  		(ampdu_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >> 2;  	/* own MCS TX capabilities */  	tx_mcs_set_cap = sband->ht_cap.mcs.tx_params;  	/* Copy peer MCS TX capabilities, the driver might need them. */ -	ht_cap->mcs.tx_params = ht_cap_ie->mcs.tx_params; +	ht_cap.mcs.tx_params = ht_cap_ie->mcs.tx_params;  	/* can we TX with MCS rates? */  	if (!(tx_mcs_set_cap & IEEE80211_HT_MCS_TX_DEFINED)) -		return; +		goto apply;  	/* Counting from 0, therefore +1 */  	if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_RX_DIFF) @@ -176,25 +167,75 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,  	 * - remainder are multiple spatial streams using unequal modulation  	 */  	for (i = 0; i < max_tx_streams; i++) -		ht_cap->mcs.rx_mask[i] = +		ht_cap.mcs.rx_mask[i] =  			sband->ht_cap.mcs.rx_mask[i] & ht_cap_ie->mcs.rx_mask[i];  	if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION)  		for (i = IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE;  		     i < IEEE80211_HT_MCS_MASK_LEN; i++) -			ht_cap->mcs.rx_mask[i] = +			ht_cap.mcs.rx_mask[i] =  				sband->ht_cap.mcs.rx_mask[i] &  					ht_cap_ie->mcs.rx_mask[i];  	/* handle MCS rate 32 too */  	if (sband->ht_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1) -		ht_cap->mcs.rx_mask[32/8] |= 1; +		ht_cap.mcs.rx_mask[32/8] |= 1; + apply:  	/*  	 * If user has specified capability over-rides, take care  	 * of that here.  	 */ -	ieee80211_apply_htcap_overrides(sdata, ht_cap); +	ieee80211_apply_htcap_overrides(sdata, &ht_cap); + +	changed = memcmp(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap)); + +	memcpy(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap)); + +	switch (sdata->vif.bss_conf.chandef.width) { +	default: +		WARN_ON_ONCE(1); +		/* fall through */ +	case NL80211_CHAN_WIDTH_20_NOHT: +	case NL80211_CHAN_WIDTH_20: +		bw = IEEE80211_STA_RX_BW_20; +		break; +	case NL80211_CHAN_WIDTH_40: +	case NL80211_CHAN_WIDTH_80: +	case NL80211_CHAN_WIDTH_80P80: +	case NL80211_CHAN_WIDTH_160: +		bw = ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? +				IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20; +		break; +	} + +	if (bw != sta->sta.bandwidth) +		changed = true; +	sta->sta.bandwidth = bw; + +	sta->cur_max_bandwidth = +		ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? +				IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20; + +	switch ((ht_cap.cap & IEEE80211_HT_CAP_SM_PS) +			>> IEEE80211_HT_CAP_SM_PS_SHIFT) { +	case WLAN_HT_CAP_SM_PS_INVALID: +	case WLAN_HT_CAP_SM_PS_STATIC: +		smps_mode = IEEE80211_SMPS_STATIC; +		break; +	case WLAN_HT_CAP_SM_PS_DYNAMIC: +		smps_mode = IEEE80211_SMPS_DYNAMIC; +		break; +	case WLAN_HT_CAP_SM_PS_DISABLED: +		smps_mode = IEEE80211_SMPS_OFF; +		break; +	} + +	if (smps_mode != sta->sta.smps_mode) +		changed = true; +	sta->sta.smps_mode = smps_mode; + +	return changed;  }  void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, @@ -406,6 +447,9 @@ void ieee80211_request_smps(struct ieee80211_vif *vif,  	if (WARN_ON(smps_mode == IEEE80211_SMPS_OFF))  		smps_mode = IEEE80211_SMPS_AUTOMATIC; +	if (sdata->u.mgd.driver_smps_mode == smps_mode) +		return; +  	sdata->u.mgd.driver_smps_mode = smps_mode;  	ieee80211_queue_work(&sdata->local->hw,  |