mac80211: stop toggling IEEE80211_HT_CAP_SUP_WIDTH_20_40
For VHT, many more bandwidth changes are possible. As a first step, stop toggling the IEEE80211_HT_CAP_SUP_WIDTH_20_40 flag in the HT capabilities and instead introduce a bandwidth field indicating the currently usable bandwidth to transmit to the station. Of course, make all drivers use it. To achieve this, make ieee80211_ht_cap_ie_to_sta_ht_cap() get the station as an argument, rather than the new capabilities, so it can set up the new bandwidth field. If the station is a VHT station and VHT bandwidth is in use, also set the bandwidth accordingly. Doing this allows us to get rid of the supports_40mhz flag as the HT capabilities now reflect the true capability instead of the current setting. While at it, also fix ieee80211_ht_cap_ie_to_sta_ht_cap() to not ignore HT cap overrides when MCS TX isn't supported (not that it really happens...) Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
		
					parent
					
						
							
								4a34215ef7
							
						
					
				
			
			
				commit
				
					
						e1a0c6b3a4
					
				
			
		
					 27 changed files with 184 additions and 158 deletions
				
			
		|  | @ -1204,7 +1204,7 @@ static u8 ath_rc_build_ht_caps(struct ath_softc *sc, struct ieee80211_sta *sta) | ||||||
| 			caps |= WLAN_RC_TS_FLAG | WLAN_RC_DS_FLAG; | 			caps |= WLAN_RC_TS_FLAG | WLAN_RC_DS_FLAG; | ||||||
| 		else if (sta->ht_cap.mcs.rx_mask[1]) | 		else if (sta->ht_cap.mcs.rx_mask[1]) | ||||||
| 			caps |= WLAN_RC_DS_FLAG; | 			caps |= WLAN_RC_DS_FLAG; | ||||||
| 		if (sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) { | 		if (sta->bandwidth >= IEEE80211_STA_RX_BW_40) { | ||||||
| 			caps |= WLAN_RC_40_FLAG; | 			caps |= WLAN_RC_40_FLAG; | ||||||
| 			if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) | 			if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) | ||||||
| 				caps |= WLAN_RC_SGI_FLAG; | 				caps |= WLAN_RC_SGI_FLAG; | ||||||
|  |  | ||||||
|  | @ -338,7 +338,7 @@ int iwl_sta_update_ht(struct iwl_priv *priv, struct iwl_rxon_context *ctx, | ||||||
| 
 | 
 | ||||||
| bool iwl_is_ht40_tx_allowed(struct iwl_priv *priv, | bool iwl_is_ht40_tx_allowed(struct iwl_priv *priv, | ||||||
| 			    struct iwl_rxon_context *ctx, | 			    struct iwl_rxon_context *ctx, | ||||||
| 			    struct ieee80211_sta_ht_cap *ht_cap); | 			    struct ieee80211_sta *sta); | ||||||
| 
 | 
 | ||||||
| static inline int iwl_sta_id(struct ieee80211_sta *sta) | static inline int iwl_sta_id(struct ieee80211_sta *sta) | ||||||
| { | { | ||||||
|  |  | ||||||
|  | @ -1305,7 +1305,7 @@ static int rs_switch_to_mimo2(struct iwl_priv *priv, | ||||||
| 	tbl->max_search = IWL_MAX_SEARCH; | 	tbl->max_search = IWL_MAX_SEARCH; | ||||||
| 	rate_mask = lq_sta->active_mimo2_rate; | 	rate_mask = lq_sta->active_mimo2_rate; | ||||||
| 
 | 
 | ||||||
| 	if (iwl_is_ht40_tx_allowed(priv, ctx, &sta->ht_cap)) | 	if (iwl_is_ht40_tx_allowed(priv, ctx, sta)) | ||||||
| 		tbl->is_ht40 = 1; | 		tbl->is_ht40 = 1; | ||||||
| 	else | 	else | ||||||
| 		tbl->is_ht40 = 0; | 		tbl->is_ht40 = 0; | ||||||
|  | @ -1361,7 +1361,7 @@ static int rs_switch_to_mimo3(struct iwl_priv *priv, | ||||||
| 	tbl->max_search = IWL_MAX_11N_MIMO3_SEARCH; | 	tbl->max_search = IWL_MAX_11N_MIMO3_SEARCH; | ||||||
| 	rate_mask = lq_sta->active_mimo3_rate; | 	rate_mask = lq_sta->active_mimo3_rate; | ||||||
| 
 | 
 | ||||||
| 	if (iwl_is_ht40_tx_allowed(priv, ctx, &sta->ht_cap)) | 	if (iwl_is_ht40_tx_allowed(priv, ctx, sta)) | ||||||
| 		tbl->is_ht40 = 1; | 		tbl->is_ht40 = 1; | ||||||
| 	else | 	else | ||||||
| 		tbl->is_ht40 = 0; | 		tbl->is_ht40 = 0; | ||||||
|  | @ -1410,7 +1410,7 @@ static int rs_switch_to_siso(struct iwl_priv *priv, | ||||||
| 	tbl->max_search = IWL_MAX_SEARCH; | 	tbl->max_search = IWL_MAX_SEARCH; | ||||||
| 	rate_mask = lq_sta->active_siso_rate; | 	rate_mask = lq_sta->active_siso_rate; | ||||||
| 
 | 
 | ||||||
| 	if (iwl_is_ht40_tx_allowed(priv, ctx, &sta->ht_cap)) | 	if (iwl_is_ht40_tx_allowed(priv, ctx, sta)) | ||||||
| 		tbl->is_ht40 = 1; | 		tbl->is_ht40 = 1; | ||||||
| 	else | 	else | ||||||
| 		tbl->is_ht40 = 0; | 		tbl->is_ht40 = 0; | ||||||
|  |  | ||||||
|  | @ -173,7 +173,7 @@ int iwl_send_add_sta(struct iwl_priv *priv, | ||||||
| 
 | 
 | ||||||
| bool iwl_is_ht40_tx_allowed(struct iwl_priv *priv, | bool iwl_is_ht40_tx_allowed(struct iwl_priv *priv, | ||||||
| 			    struct iwl_rxon_context *ctx, | 			    struct iwl_rxon_context *ctx, | ||||||
| 			    struct ieee80211_sta_ht_cap *ht_cap) | 			    struct ieee80211_sta *sta) | ||||||
| { | { | ||||||
| 	if (!ctx->ht.enabled || !ctx->ht.is_40mhz) | 	if (!ctx->ht.enabled || !ctx->ht.is_40mhz) | ||||||
| 		return false; | 		return false; | ||||||
|  | @ -183,20 +183,11 @@ bool iwl_is_ht40_tx_allowed(struct iwl_priv *priv, | ||||||
| 		return false; | 		return false; | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| 	/*
 | 	/* special case for RXON */ | ||||||
| 	 * Remainder of this function checks ht_cap, but if it's | 	if (!sta) | ||||||
| 	 * NULL then we can do HT40 (special case for RXON) |  | ||||||
| 	 */ |  | ||||||
| 	if (!ht_cap) |  | ||||||
| 		return true; | 		return true; | ||||||
| 
 | 
 | ||||||
| 	if (!ht_cap->ht_supported) | 	return sta->bandwidth >= IEEE80211_STA_RX_BW_40; | ||||||
| 		return false; |  | ||||||
| 
 |  | ||||||
| 	if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) |  | ||||||
| 		return false; |  | ||||||
| 
 |  | ||||||
| 	return true; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void iwl_sta_calc_ht_flags(struct iwl_priv *priv, | static void iwl_sta_calc_ht_flags(struct iwl_priv *priv, | ||||||
|  | @ -246,7 +237,7 @@ static void iwl_sta_calc_ht_flags(struct iwl_priv *priv, | ||||||
| 	*flags |= cpu_to_le32( | 	*flags |= cpu_to_le32( | ||||||
| 		(u32)sta_ht_inf->ampdu_density << STA_FLG_AGG_MPDU_DENSITY_POS); | 		(u32)sta_ht_inf->ampdu_density << STA_FLG_AGG_MPDU_DENSITY_POS); | ||||||
| 
 | 
 | ||||||
| 	if (iwl_is_ht40_tx_allowed(priv, ctx, &sta->ht_cap)) | 	if (iwl_is_ht40_tx_allowed(priv, ctx, sta)) | ||||||
| 		*flags |= STA_FLG_HT40_EN_MSK; | 		*flags |= STA_FLG_HT40_EN_MSK; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1209,23 +1209,9 @@ static s32 rs_get_best_rate(struct iwl_mvm *mvm, | ||||||
| 	return new_rate; | 	return new_rate; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static bool iwl_is_ht40_tx_allowed(struct iwl_mvm *mvm, | static bool iwl_is_ht40_tx_allowed(struct ieee80211_sta *sta) | ||||||
| 			    struct ieee80211_sta_ht_cap *ht_cap) |  | ||||||
| { | { | ||||||
| 	/*
 | 	return sta->bandwidth >= IEEE80211_STA_RX_BW_40; | ||||||
| 	 * Remainder of this function checks ht_cap, but if it's |  | ||||||
| 	 * NULL then we can do HT40 (special case for RXON) |  | ||||||
| 	 */ |  | ||||||
| 	if (!ht_cap) |  | ||||||
| 		return true; |  | ||||||
| 
 |  | ||||||
| 	if (!ht_cap->ht_supported) |  | ||||||
| 		return false; |  | ||||||
| 
 |  | ||||||
| 	if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) |  | ||||||
| 		return false; |  | ||||||
| 
 |  | ||||||
| 	return true; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  | @ -1258,7 +1244,7 @@ static int rs_switch_to_mimo2(struct iwl_mvm *mvm, | ||||||
| 	tbl->max_search = IWL_MAX_SEARCH; | 	tbl->max_search = IWL_MAX_SEARCH; | ||||||
| 	rate_mask = lq_sta->active_mimo2_rate; | 	rate_mask = lq_sta->active_mimo2_rate; | ||||||
| 
 | 
 | ||||||
| 	if (iwl_is_ht40_tx_allowed(mvm, &sta->ht_cap)) | 	if (iwl_is_ht40_tx_allowed(sta)) | ||||||
| 		tbl->is_ht40 = 1; | 		tbl->is_ht40 = 1; | ||||||
| 	else | 	else | ||||||
| 		tbl->is_ht40 = 0; | 		tbl->is_ht40 = 0; | ||||||
|  | @ -1311,7 +1297,7 @@ static int rs_switch_to_mimo3(struct iwl_mvm *mvm, | ||||||
| 	tbl->max_search = IWL_MAX_11N_MIMO3_SEARCH; | 	tbl->max_search = IWL_MAX_11N_MIMO3_SEARCH; | ||||||
| 	rate_mask = lq_sta->active_mimo3_rate; | 	rate_mask = lq_sta->active_mimo3_rate; | ||||||
| 
 | 
 | ||||||
| 	if (iwl_is_ht40_tx_allowed(mvm, &sta->ht_cap)) | 	if (iwl_is_ht40_tx_allowed(sta)) | ||||||
| 		tbl->is_ht40 = 1; | 		tbl->is_ht40 = 1; | ||||||
| 	else | 	else | ||||||
| 		tbl->is_ht40 = 0; | 		tbl->is_ht40 = 0; | ||||||
|  | @ -1356,7 +1342,7 @@ static int rs_switch_to_siso(struct iwl_mvm *mvm, | ||||||
| 	tbl->max_search = IWL_MAX_SEARCH; | 	tbl->max_search = IWL_MAX_SEARCH; | ||||||
| 	rate_mask = lq_sta->active_siso_rate; | 	rate_mask = lq_sta->active_siso_rate; | ||||||
| 
 | 
 | ||||||
| 	if (iwl_is_ht40_tx_allowed(mvm, &sta->ht_cap)) | 	if (iwl_is_ht40_tx_allowed(sta)) | ||||||
| 		tbl->is_ht40 = 1; | 		tbl->is_ht40 = 1; | ||||||
| 	else | 	else | ||||||
| 		tbl->is_ht40 = 0; | 		tbl->is_ht40 = 0; | ||||||
|  |  | ||||||
|  | @ -523,8 +523,8 @@ static void _rtl_query_shortgi(struct ieee80211_hw *hw, | ||||||
| 	if (mac->opmode == NL80211_IFTYPE_STATION) | 	if (mac->opmode == NL80211_IFTYPE_STATION) | ||||||
| 		bw_40 = mac->bw_40; | 		bw_40 = mac->bw_40; | ||||||
| 	else if (mac->opmode == NL80211_IFTYPE_AP || | 	else if (mac->opmode == NL80211_IFTYPE_AP || | ||||||
| 		mac->opmode == NL80211_IFTYPE_ADHOC) | 		 mac->opmode == NL80211_IFTYPE_ADHOC) | ||||||
| 		bw_40 = sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40; | 		bw_40 = sta->bandwidth >= IEEE80211_STA_RX_BW_40; | ||||||
| 
 | 
 | ||||||
| 	if (bw_40 && sgi_40) | 	if (bw_40 && sgi_40) | ||||||
| 		tcb_desc->use_shortgi = true; | 		tcb_desc->use_shortgi = true; | ||||||
|  | @ -634,8 +634,7 @@ static void _rtl_query_bandwidth_mode(struct ieee80211_hw *hw, | ||||||
| 		return; | 		return; | ||||||
| 	if (mac->opmode == NL80211_IFTYPE_AP || | 	if (mac->opmode == NL80211_IFTYPE_AP || | ||||||
| 	    mac->opmode == NL80211_IFTYPE_ADHOC) { | 	    mac->opmode == NL80211_IFTYPE_ADHOC) { | ||||||
| 		if (!(sta->ht_cap.ht_supported) || | 		if (sta->bandwidth == IEEE80211_STA_RX_BW_20) | ||||||
| 		    !(sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) |  | ||||||
| 			return; | 			return; | ||||||
| 	} else if (mac->opmode == NL80211_IFTYPE_STATION) { | 	} else if (mac->opmode == NL80211_IFTYPE_STATION) { | ||||||
| 		if (!mac->bw_40 || !(sta->ht_cap.ht_supported)) | 		if (!mac->bw_40 || !(sta->ht_cap.ht_supported)) | ||||||
|  |  | ||||||
|  | @ -116,9 +116,8 @@ static void _rtl_rc_rate_set_series(struct rtl_priv *rtlpriv, | ||||||
| 		if (txrc->short_preamble) | 		if (txrc->short_preamble) | ||||||
| 			rate->flags |= IEEE80211_TX_RC_USE_SHORT_PREAMBLE; | 			rate->flags |= IEEE80211_TX_RC_USE_SHORT_PREAMBLE; | ||||||
| 		if (mac->opmode == NL80211_IFTYPE_AP || | 		if (mac->opmode == NL80211_IFTYPE_AP || | ||||||
| 			mac->opmode == NL80211_IFTYPE_ADHOC) { | 		    mac->opmode == NL80211_IFTYPE_ADHOC) { | ||||||
| 			if (sta && (sta->ht_cap.cap & | 			if (sta && (sta->bandwidth >= IEEE80211_STA_RX_BW_40)) | ||||||
| 			    IEEE80211_HT_CAP_SUP_WIDTH_20_40)) |  | ||||||
| 				rate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; | 				rate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; | ||||||
| 		} else { | 		} else { | ||||||
| 			if (mac->bw_40) | 			if (mac->bw_40) | ||||||
|  |  | ||||||
|  | @ -1846,9 +1846,9 @@ static void rtl92ce_update_hal_rate_mask(struct ieee80211_hw *hw, | ||||||
| 	struct rtl_sta_info *sta_entry = NULL; | 	struct rtl_sta_info *sta_entry = NULL; | ||||||
| 	u32 ratr_bitmap; | 	u32 ratr_bitmap; | ||||||
| 	u8 ratr_index; | 	u8 ratr_index; | ||||||
| 	u8 curtxbw_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) | 	u8 curtxbw_40mhz = (sta->bandwidth >= IEEE80211_STA_RX_BW_40) ? 1 : 0; | ||||||
| 				? 1 : 0; | 	u8 curshortgi_40mhz = curtxbw_40mhz && | ||||||
| 	u8 curshortgi_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) ? | 			      (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) ? | ||||||
| 				1 : 0; | 				1 : 0; | ||||||
| 	u8 curshortgi_20mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ? | 	u8 curshortgi_20mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ? | ||||||
| 				1 : 0; | 				1 : 0; | ||||||
|  |  | ||||||
|  | @ -626,8 +626,7 @@ void rtl92ce_tx_fill_desc(struct ieee80211_hw *hw, | ||||||
| 	} else if (mac->opmode == NL80211_IFTYPE_AP || | 	} else if (mac->opmode == NL80211_IFTYPE_AP || | ||||||
| 		mac->opmode == NL80211_IFTYPE_ADHOC) { | 		mac->opmode == NL80211_IFTYPE_ADHOC) { | ||||||
| 		if (sta) | 		if (sta) | ||||||
| 			bw_40 = sta->ht_cap.cap & | 			bw_40 = sta->bandwidth >= IEEE80211_STA_RX_BW_40; | ||||||
| 				IEEE80211_HT_CAP_SUP_WIDTH_20_40; |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	seq_number = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4; | 	seq_number = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4; | ||||||
|  |  | ||||||
|  | @ -1970,8 +1970,7 @@ static void rtl92de_update_hal_rate_mask(struct ieee80211_hw *hw, | ||||||
| 	struct rtl_sta_info *sta_entry = NULL; | 	struct rtl_sta_info *sta_entry = NULL; | ||||||
| 	u32 ratr_bitmap; | 	u32 ratr_bitmap; | ||||||
| 	u8 ratr_index; | 	u8 ratr_index; | ||||||
| 	u8 curtxbw_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) | 	u8 curtxbw_40mhz = (sta->bandwidth >= IEEE80211_STA_RX_BW_40) ? 1 : 0; | ||||||
| 							? 1 : 0; |  | ||||||
| 	u8 curshortgi_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) ? | 	u8 curshortgi_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) ? | ||||||
| 							1 : 0; | 							1 : 0; | ||||||
| 	u8 curshortgi_20mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ? | 	u8 curshortgi_20mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ? | ||||||
|  |  | ||||||
|  | @ -574,8 +574,7 @@ void rtl92de_tx_fill_desc(struct ieee80211_hw *hw, | ||||||
| 	} else if (mac->opmode == NL80211_IFTYPE_AP || | 	} else if (mac->opmode == NL80211_IFTYPE_AP || | ||||||
| 		mac->opmode == NL80211_IFTYPE_ADHOC) { | 		mac->opmode == NL80211_IFTYPE_ADHOC) { | ||||||
| 		if (sta) | 		if (sta) | ||||||
| 			bw_40 = sta->ht_cap.cap & | 			bw_40 = sta->bandwidth >= IEEE80211_STA_RX_BW_40; | ||||||
| 				IEEE80211_HT_CAP_SUP_WIDTH_20_40; |  | ||||||
| 	} | 	} | ||||||
| 	seq_number = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4; | 	seq_number = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4; | ||||||
| 	rtl_get_tcb_desc(hw, info, sta, skb, ptcb_desc); | 	rtl_get_tcb_desc(hw, info, sta, skb, ptcb_desc); | ||||||
|  |  | ||||||
|  | @ -2085,8 +2085,7 @@ static void rtl92se_update_hal_rate_mask(struct ieee80211_hw *hw, | ||||||
| 	struct rtl_sta_info *sta_entry = NULL; | 	struct rtl_sta_info *sta_entry = NULL; | ||||||
| 	u32 ratr_bitmap; | 	u32 ratr_bitmap; | ||||||
| 	u8 ratr_index = 0; | 	u8 ratr_index = 0; | ||||||
| 	u8 curtxbw_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) | 	u8 curtxbw_40mhz = (sta->bandwidth >= IEEE80211_STA_RX_BW_40) ? 1 : 0; | ||||||
| 				? 1 : 0; |  | ||||||
| 	u8 curshortgi_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) ? | 	u8 curshortgi_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) ? | ||||||
| 				1 : 0; | 				1 : 0; | ||||||
| 	u8 curshortgi_20mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ? | 	u8 curshortgi_20mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ? | ||||||
|  |  | ||||||
|  | @ -621,8 +621,7 @@ void rtl92se_tx_fill_desc(struct ieee80211_hw *hw, | ||||||
| 	} else if (mac->opmode == NL80211_IFTYPE_AP || | 	} else if (mac->opmode == NL80211_IFTYPE_AP || | ||||||
| 		mac->opmode == NL80211_IFTYPE_ADHOC) { | 		mac->opmode == NL80211_IFTYPE_ADHOC) { | ||||||
| 		if (sta) | 		if (sta) | ||||||
| 			bw_40 = sta->ht_cap.cap & | 			bw_40 = sta->bandwidth >= IEEE80211_STA_RX_BW_40; | ||||||
| 				    IEEE80211_HT_CAP_SUP_WIDTH_20_40; |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	seq_number = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4; | 	seq_number = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4; | ||||||
|  |  | ||||||
|  | @ -1866,8 +1866,7 @@ static void rtl8723ae_update_hal_rate_mask(struct ieee80211_hw *hw, | ||||||
| 	struct rtl_sta_info *sta_entry = NULL; | 	struct rtl_sta_info *sta_entry = NULL; | ||||||
| 	u32 ratr_bitmap; | 	u32 ratr_bitmap; | ||||||
| 	u8 ratr_index; | 	u8 ratr_index; | ||||||
| 	u8 curtxbw_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) | 	u8 curtxbw_40mhz = (sta->bandwidth >= IEEE80211_STA_RX_BW_40) ? 1 : 0; | ||||||
| 				? 1 : 0; |  | ||||||
| 	u8 curshortgi_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) ? | 	u8 curshortgi_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) ? | ||||||
| 				1 : 0; | 				1 : 0; | ||||||
| 	u8 curshortgi_20mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ? | 	u8 curshortgi_20mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ? | ||||||
|  |  | ||||||
|  | @ -395,8 +395,7 @@ void rtl8723ae_tx_fill_desc(struct ieee80211_hw *hw, | ||||||
| 	} else if (mac->opmode == NL80211_IFTYPE_AP || | 	} else if (mac->opmode == NL80211_IFTYPE_AP || | ||||||
| 		mac->opmode == NL80211_IFTYPE_ADHOC) { | 		mac->opmode == NL80211_IFTYPE_ADHOC) { | ||||||
| 		if (sta) | 		if (sta) | ||||||
| 			bw_40 = sta->ht_cap.cap & | 			bw_40 = sta->bandwidth >= IEEE80211_STA_RX_BW_40; | ||||||
| 				IEEE80211_HT_CAP_SUP_WIDTH_20_40; |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	seq_number = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4; | 	seq_number = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4; | ||||||
|  |  | ||||||
|  | @ -1374,7 +1374,7 @@ static void wl18xx_sta_rc_update(struct wl1271 *wl, | ||||||
| 				 struct ieee80211_sta *sta, | 				 struct ieee80211_sta *sta, | ||||||
| 				 u32 changed) | 				 u32 changed) | ||||||
| { | { | ||||||
| 	bool wide = sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40; | 	bool wide = sta->bandwidth >= IEEE80211_STA_RX_BW_40; | ||||||
| 
 | 
 | ||||||
| 	wl1271_debug(DEBUG_MAC80211, "mac80211 sta_rc_update wide %d", wide); | 	wl1271_debug(DEBUG_MAC80211, "mac80211 sta_rc_update wide %d", wide); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1196,6 +1196,24 @@ enum ieee80211_sta_state { | ||||||
| 	IEEE80211_STA_AUTHORIZED, | 	IEEE80211_STA_AUTHORIZED, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * enum ieee80211_sta_rx_bandwidth - station RX bandwidth | ||||||
|  |  * @IEEE80211_STA_RX_BW_20: station can only receive 20 MHz | ||||||
|  |  * @IEEE80211_STA_RX_BW_40: station can receive up to 40 MHz | ||||||
|  |  * @IEEE80211_STA_RX_BW_80: station can receive up to 80 MHz | ||||||
|  |  * @IEEE80211_STA_RX_BW_160: station can receive up to 160 MHz | ||||||
|  |  *	(including 80+80 MHz) | ||||||
|  |  * | ||||||
|  |  * Implementation note: 20 must be zero to be initialized | ||||||
|  |  *	correctly, the values must be sorted. | ||||||
|  |  */ | ||||||
|  | enum ieee80211_sta_rx_bandwidth { | ||||||
|  | 	IEEE80211_STA_RX_BW_20 = 0, | ||||||
|  | 	IEEE80211_STA_RX_BW_40, | ||||||
|  | 	IEEE80211_STA_RX_BW_80, | ||||||
|  | 	IEEE80211_STA_RX_BW_160, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * struct ieee80211_sta - station table entry |  * struct ieee80211_sta - station table entry | ||||||
|  * |  * | ||||||
|  | @ -1218,6 +1236,7 @@ enum ieee80211_sta_state { | ||||||
|  * @uapsd_queues: bitmap of queues configured for uapsd. Only valid |  * @uapsd_queues: bitmap of queues configured for uapsd. Only valid | ||||||
|  *	if wme is supported. |  *	if wme is supported. | ||||||
|  * @max_sp: max Service Period. Only valid if wme is supported. |  * @max_sp: max Service Period. Only valid if wme is supported. | ||||||
|  |  * @bandwidth: current bandwidth the station can receive with | ||||||
|  */ |  */ | ||||||
| struct ieee80211_sta { | struct ieee80211_sta { | ||||||
| 	u32 supp_rates[IEEE80211_NUM_BANDS]; | 	u32 supp_rates[IEEE80211_NUM_BANDS]; | ||||||
|  | @ -1228,6 +1247,7 @@ struct ieee80211_sta { | ||||||
| 	bool wme; | 	bool wme; | ||||||
| 	u8 uapsd_queues; | 	u8 uapsd_queues; | ||||||
| 	u8 max_sp; | 	u8 max_sp; | ||||||
|  | 	enum ieee80211_sta_rx_bandwidth bandwidth; | ||||||
| 
 | 
 | ||||||
| 	/* must be last */ | 	/* must be last */ | ||||||
| 	u8 drv_priv[0] __aligned(sizeof(void *)); | 	u8 drv_priv[0] __aligned(sizeof(void *)); | ||||||
|  | @ -2086,7 +2106,9 @@ enum ieee80211_frame_release_type { | ||||||
|  * enum ieee80211_rate_control_changed - flags to indicate what changed |  * enum ieee80211_rate_control_changed - flags to indicate what changed | ||||||
|  * |  * | ||||||
|  * @IEEE80211_RC_BW_CHANGED: The bandwidth that can be used to transmit |  * @IEEE80211_RC_BW_CHANGED: The bandwidth that can be used to transmit | ||||||
|  *	to this station changed. |  *	to this station changed. The actual bandwidth is in the station | ||||||
|  |  *	information -- for HT20/40 the IEEE80211_HT_CAP_SUP_WIDTH_20_40 | ||||||
|  |  *	flag changes, for HT and VHT the bandwidth field changes. | ||||||
|  * @IEEE80211_RC_SMPS_CHANGED: The SMPS state of the station changed. |  * @IEEE80211_RC_SMPS_CHANGED: The SMPS state of the station changed. | ||||||
|  * @IEEE80211_RC_SUPP_RATES_CHANGED: The supported rate set of this peer |  * @IEEE80211_RC_SUPP_RATES_CHANGED: The supported rate set of this peer | ||||||
|  *	changed (in IBSS mode) due to discovering more information about |  *	changed (in IBSS mode) due to discovering more information about | ||||||
|  |  | ||||||
|  | @ -1252,8 +1252,7 @@ static int sta_apply_parameters(struct ieee80211_local *local, | ||||||
| 
 | 
 | ||||||
| 	if (params->ht_capa) | 	if (params->ht_capa) | ||||||
| 		ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, | 		ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, | ||||||
| 						  params->ht_capa, | 						  params->ht_capa, sta); | ||||||
| 						  &sta->sta.ht_cap); |  | ||||||
| 
 | 
 | ||||||
| 	if (params->vht_capa) | 	if (params->vht_capa) | ||||||
| 		ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, | 		ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, | ||||||
|  |  | ||||||
|  | @ -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); | 	u8 *smask = (u8 *)(&sdata->u.mgd.ht_capa_mask.mcs.rx_mask); | ||||||
| 	int i; | 	int i; | ||||||
| 
 | 
 | ||||||
|  | 	if (!ht_cap->ht_supported) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
| 	if (sdata->vif.type != NL80211_IFTYPE_STATION) { | 	if (sdata->vif.type != NL80211_IFTYPE_STATION) { | ||||||
| 		/* AP interfaces call this code when adding new stations,
 | 		/* AP interfaces call this code when adding new stations,
 | ||||||
| 		 * so just silently ignore non station interfaces. | 		 * so just silently ignore non station interfaces. | ||||||
|  | @ -89,22 +92,23 @@ 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_supported_band *sband, | ||||||
| 				       struct ieee80211_ht_cap *ht_cap_ie, | 				       struct ieee80211_ht_cap *ht_cap_ie, | ||||||
| 				       struct ieee80211_sta_ht_cap *ht_cap) | 				       struct sta_info *sta) | ||||||
| { | { | ||||||
|  | 	struct ieee80211_sta_ht_cap ht_cap; | ||||||
| 	u8 ampdu_info, tx_mcs_set_cap; | 	u8 ampdu_info, tx_mcs_set_cap; | ||||||
| 	int i, max_tx_streams; | 	int i, max_tx_streams; | ||||||
|  | 	bool changed; | ||||||
|  | 	enum ieee80211_sta_rx_bandwidth bw; | ||||||
| 
 | 
 | ||||||
| 	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) | 	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 | 	 * The bits listed in this expression should be | ||||||
|  | @ -112,7 +116,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 | 	 * advertises more then we can't use those thus | ||||||
| 	 * we mask them out. | 	 * 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 | | 		(sband->ht_cap.cap | | ||||||
| 		 ~(IEEE80211_HT_CAP_LDPC_CODING | | 		 ~(IEEE80211_HT_CAP_LDPC_CODING | | ||||||
| 		   IEEE80211_HT_CAP_SUP_WIDTH_20_40 | | 		   IEEE80211_HT_CAP_SUP_WIDTH_20_40 | | ||||||
|  | @ -121,44 +125,30 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, | ||||||
| 		   IEEE80211_HT_CAP_SGI_40 | | 		   IEEE80211_HT_CAP_SGI_40 | | ||||||
| 		   IEEE80211_HT_CAP_DSSSCCK40)); | 		   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 | 	 * The STBC bits are asymmetric -- if we don't have | ||||||
| 	 * TX then mask out the peer's RX and vice versa. | 	 * TX then mask out the peer's RX and vice versa. | ||||||
| 	 */ | 	 */ | ||||||
| 	if (!(sband->ht_cap.cap & IEEE80211_HT_CAP_TX_STBC)) | 	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)) | 	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; | 	ampdu_info = ht_cap_ie->ampdu_params_info; | ||||||
| 	ht_cap->ampdu_factor = | 	ht_cap.ampdu_factor = | ||||||
| 		ampdu_info & IEEE80211_HT_AMPDU_PARM_FACTOR; | 		ampdu_info & IEEE80211_HT_AMPDU_PARM_FACTOR; | ||||||
| 	ht_cap->ampdu_density = | 	ht_cap.ampdu_density = | ||||||
| 		(ampdu_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >> 2; | 		(ampdu_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >> 2; | ||||||
| 
 | 
 | ||||||
| 	/* own MCS TX capabilities */ | 	/* own MCS TX capabilities */ | ||||||
| 	tx_mcs_set_cap = sband->ht_cap.mcs.tx_params; | 	tx_mcs_set_cap = sband->ht_cap.mcs.tx_params; | ||||||
| 
 | 
 | ||||||
| 	/* Copy peer MCS TX capabilities, the driver might need them. */ | 	/* 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? */ | 	/* can we TX with MCS rates? */ | ||||||
| 	if (!(tx_mcs_set_cap & IEEE80211_HT_MCS_TX_DEFINED)) | 	if (!(tx_mcs_set_cap & IEEE80211_HT_MCS_TX_DEFINED)) | ||||||
| 		return; | 		goto apply; | ||||||
| 
 | 
 | ||||||
| 	/* Counting from 0, therefore +1 */ | 	/* Counting from 0, therefore +1 */ | ||||||
| 	if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_RX_DIFF) | 	if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_RX_DIFF) | ||||||
|  | @ -176,25 +166,53 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, | ||||||
| 	 * - remainder are multiple spatial streams using unequal modulation | 	 * - remainder are multiple spatial streams using unequal modulation | ||||||
| 	 */ | 	 */ | ||||||
| 	for (i = 0; i < max_tx_streams; i++) | 	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]; | 			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) | 	if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION) | ||||||
| 		for (i = IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE; | 		for (i = IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE; | ||||||
| 		     i < IEEE80211_HT_MCS_MASK_LEN; i++) | 		     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] & | 				sband->ht_cap.mcs.rx_mask[i] & | ||||||
| 					ht_cap_ie->mcs.rx_mask[i]; | 					ht_cap_ie->mcs.rx_mask[i]; | ||||||
| 
 | 
 | ||||||
| 	/* handle MCS rate 32 too */ | 	/* handle MCS rate 32 too */ | ||||||
| 	if (sband->ht_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1) | 	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 | 	 * If user has specified capability over-rides, take care | ||||||
| 	 * of that here. | 	 * 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; | ||||||
|  | 
 | ||||||
|  | 	return changed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, | void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, | ||||||
|  |  | ||||||
|  | @ -496,33 +496,26 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, | ||||||
| 		if (sta && elems->ht_operation && elems->ht_cap_elem && | 		if (sta && elems->ht_operation && elems->ht_cap_elem && | ||||||
| 		    sdata->u.ibss.channel_type != NL80211_CHAN_NO_HT) { | 		    sdata->u.ibss.channel_type != NL80211_CHAN_NO_HT) { | ||||||
| 			/* we both use HT */ | 			/* we both use HT */ | ||||||
| 			struct ieee80211_sta_ht_cap sta_ht_cap_new; | 			struct ieee80211_ht_cap htcap_ie; | ||||||
| 			struct cfg80211_chan_def chandef; | 			struct cfg80211_chan_def chandef; | ||||||
| 
 | 
 | ||||||
| 			ieee80211_ht_oper_to_chandef(channel, | 			ieee80211_ht_oper_to_chandef(channel, | ||||||
| 						     elems->ht_operation, | 						     elems->ht_operation, | ||||||
| 						     &chandef); | 						     &chandef); | ||||||
| 
 | 
 | ||||||
| 			ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, | 			memcpy(&htcap_ie, elems->ht_cap_elem, sizeof(htcap_ie)); | ||||||
| 							  elems->ht_cap_elem, |  | ||||||
| 							  &sta_ht_cap_new); |  | ||||||
| 
 | 
 | ||||||
| 			/*
 | 			/*
 | ||||||
| 			 * fall back to HT20 if we don't use or use | 			 * fall back to HT20 if we don't use or use | ||||||
| 			 * the other extension channel | 			 * the other extension channel | ||||||
| 			 */ | 			 */ | ||||||
| 			if (chandef.width != NL80211_CHAN_WIDTH_40 || | 			if (cfg80211_get_chandef_type(&chandef) != | ||||||
| 			    cfg80211_get_chandef_type(&chandef) != |  | ||||||
| 						sdata->u.ibss.channel_type) | 						sdata->u.ibss.channel_type) | ||||||
| 				sta_ht_cap_new.cap &= | 				htcap_ie.cap_info &= | ||||||
| 					~IEEE80211_HT_CAP_SUP_WIDTH_20_40; | 					cpu_to_le16(~IEEE80211_HT_CAP_SUP_WIDTH_20_40); | ||||||
| 
 | 
 | ||||||
| 			if (memcmp(&sta->sta.ht_cap, &sta_ht_cap_new, | 			rates_updated |= ieee80211_ht_cap_ie_to_sta_ht_cap( | ||||||
| 				   sizeof(sta_ht_cap_new))) { | 						sdata, sband, &htcap_ie, sta); | ||||||
| 				memcpy(&sta->sta.ht_cap, &sta_ht_cap_new, |  | ||||||
| 				       sizeof(sta_ht_cap_new)); |  | ||||||
| 				rates_updated = true; |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (sta && rates_updated) { | 		if (sta && rates_updated) { | ||||||
|  |  | ||||||
|  | @ -1384,10 +1384,10 @@ void ieee80211_purge_tx_queue(struct ieee80211_hw *hw, | ||||||
| /* HT */ | /* HT */ | ||||||
| void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata, | void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata, | ||||||
| 				     struct ieee80211_sta_ht_cap *ht_cap); | 				     struct ieee80211_sta_ht_cap *ht_cap); | ||||||
| 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_supported_band *sband, | ||||||
| 				       struct ieee80211_ht_cap *ht_cap_ie, | 				       struct ieee80211_ht_cap *ht_cap_ie, | ||||||
| 				       struct ieee80211_sta_ht_cap *ht_cap); | 				       struct sta_info *sta); | ||||||
| void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, | void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, | ||||||
| 			  const u8 *da, u16 tid, | 			  const u8 *da, u16 tid, | ||||||
| 			  u16 initiator, u16 reason_code); | 			  u16 initiator, u16 reason_code); | ||||||
|  | @ -1431,6 +1431,7 @@ void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, | ||||||
| 					 struct ieee80211_supported_band *sband, | 					 struct ieee80211_supported_band *sband, | ||||||
| 					 struct ieee80211_vht_cap *vht_cap_ie, | 					 struct ieee80211_vht_cap *vht_cap_ie, | ||||||
| 					 struct sta_info *sta); | 					 struct sta_info *sta); | ||||||
|  | enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta); | ||||||
| 
 | 
 | ||||||
| /* Spectrum management */ | /* Spectrum management */ | ||||||
| void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, | void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, | ||||||
|  |  | ||||||
|  | @ -373,8 +373,7 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata, | ||||||
| 	if (elems->ht_cap_elem && | 	if (elems->ht_cap_elem && | ||||||
| 	    sdata->vif.bss_conf.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) | 	    sdata->vif.bss_conf.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) | ||||||
| 		ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, | 		ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, | ||||||
| 						  elems->ht_cap_elem, | 						  elems->ht_cap_elem, sta); | ||||||
| 						  &sta->sta.ht_cap); |  | ||||||
| 	else | 	else | ||||||
| 		memset(&sta->sta.ht_cap, 0, sizeof(sta->sta.ht_cap)); | 		memset(&sta->sta.ht_cap, 0, sizeof(sta->sta.ht_cap)); | ||||||
| 
 | 
 | ||||||
|  | @ -383,8 +382,7 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata, | ||||||
| 
 | 
 | ||||||
| 		if (!(elems->ht_operation->ht_param & | 		if (!(elems->ht_operation->ht_param & | ||||||
| 		      IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) | 		      IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) | ||||||
| 			sta->sta.ht_cap.cap &= | 			sta->sta.bandwidth = IEEE80211_STA_RX_BW_20; | ||||||
| 					    ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; |  | ||||||
| 		ieee80211_ht_oper_to_chandef(sdata->vif.bss_conf.chandef.chan, | 		ieee80211_ht_oper_to_chandef(sdata->vif.bss_conf.chandef.chan, | ||||||
| 					     elems->ht_operation, &chandef); | 					     elems->ht_operation, &chandef); | ||||||
| 		if (sta->ch_width != chandef.width) | 		if (sta->ch_width != chandef.width) | ||||||
|  |  | ||||||
|  | @ -219,19 +219,20 @@ static u32 ieee80211_config_ht_tx(struct ieee80211_sub_if_data *sdata, | ||||||
| 	mutex_lock(&local->sta_mtx); | 	mutex_lock(&local->sta_mtx); | ||||||
| 	sta = sta_info_get(sdata, bssid); | 	sta = sta_info_get(sdata, bssid); | ||||||
| 
 | 
 | ||||||
| 	WARN_ON_ONCE(!sta); | 	if (WARN_ON_ONCE(!sta)) { | ||||||
|  | 		mutex_unlock(&local->sta_mtx); | ||||||
|  | 		return changed; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	if (sta && !sta->supports_40mhz) | 	if (!(sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) | ||||||
| 		disable_40 = true; | 		disable_40 = true; | ||||||
| 
 | 
 | ||||||
| 	if (sta && (!reconfig || | 	if (!reconfig || | ||||||
| 		    (disable_40 != !(sta->sta.ht_cap.cap & | 	    disable_40 != (sta->sta.bandwidth < IEEE80211_STA_RX_BW_40)) { | ||||||
| 					IEEE80211_HT_CAP_SUP_WIDTH_20_40)))) { |  | ||||||
| 
 |  | ||||||
| 		if (disable_40) | 		if (disable_40) | ||||||
| 			sta->sta.ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; | 			sta->sta.bandwidth = IEEE80211_STA_RX_BW_20; | ||||||
| 		else | 		else | ||||||
| 			sta->sta.ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; | 			sta->sta.bandwidth = ieee80211_sta_cur_vht_bw(sta); | ||||||
| 
 | 
 | ||||||
| 		rate_control_rate_update(local, sband, sta, | 		rate_control_rate_update(local, sband, sta, | ||||||
| 					 IEEE80211_RC_BW_CHANGED); | 					 IEEE80211_RC_BW_CHANGED); | ||||||
|  | @ -2210,10 +2211,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, | ||||||
| 
 | 
 | ||||||
| 	if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) | 	if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) | ||||||
| 		ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, | 		ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, | ||||||
| 				elems.ht_cap_elem, &sta->sta.ht_cap); | 						  elems.ht_cap_elem, sta); | ||||||
| 
 |  | ||||||
| 	sta->supports_40mhz = |  | ||||||
| 		sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40; |  | ||||||
| 
 | 
 | ||||||
| 	if (elems.vht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) | 	if (elems.vht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) | ||||||
| 		ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, | 		ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, | ||||||
|  |  | ||||||
|  | @ -848,8 +848,6 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, | ||||||
| 		IEEE80211_HT_CAP_SM_PS_SHIFT; | 		IEEE80211_HT_CAP_SM_PS_SHIFT; | ||||||
| 
 | 
 | ||||||
| 	for (i = 0; i < ARRAY_SIZE(mi->groups); i++) { | 	for (i = 0; i < ARRAY_SIZE(mi->groups); i++) { | ||||||
| 		u16 req = 0; |  | ||||||
| 
 |  | ||||||
| 		mi->groups[i].supported = 0; | 		mi->groups[i].supported = 0; | ||||||
| 		if (i == MINSTREL_CCK_GROUP) { | 		if (i == MINSTREL_CCK_GROUP) { | ||||||
| 			minstrel_ht_update_cck(mp, mi, sband, sta); | 			minstrel_ht_update_cck(mp, mi, sband, sta); | ||||||
|  | @ -857,16 +855,17 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_SHORT_GI) { | 		if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_SHORT_GI) { | ||||||
| 			if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) | 			if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) { | ||||||
| 				req |= IEEE80211_HT_CAP_SGI_40; | 				if (!(sta_cap & IEEE80211_HT_CAP_SGI_40)) | ||||||
| 			else | 					continue; | ||||||
| 				req |= IEEE80211_HT_CAP_SGI_20; | 			} else { | ||||||
|  | 				if (!(sta_cap & IEEE80211_HT_CAP_SGI_20)) | ||||||
|  | 					continue; | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) | 		if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH && | ||||||
| 			req |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; | 		    sta->bandwidth < IEEE80211_STA_RX_BW_40) | ||||||
| 
 |  | ||||||
| 		if ((sta_cap & req) != req) |  | ||||||
| 			continue; | 			continue; | ||||||
| 
 | 
 | ||||||
| 		/* Mark MCS > 7 as unsupported if STA is in static SMPS mode */ | 		/* Mark MCS > 7 as unsupported if STA is in static SMPS mode */ | ||||||
|  |  | ||||||
|  | @ -2410,25 +2410,20 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) | ||||||
| 		case WLAN_HT_ACTION_NOTIFY_CHANWIDTH: { | 		case WLAN_HT_ACTION_NOTIFY_CHANWIDTH: { | ||||||
| 			struct ieee80211_supported_band *sband; | 			struct ieee80211_supported_band *sband; | ||||||
| 			u8 chanwidth = mgmt->u.action.u.ht_notify_cw.chanwidth; | 			u8 chanwidth = mgmt->u.action.u.ht_notify_cw.chanwidth; | ||||||
| 			bool old_40mhz, new_40mhz; | 			enum ieee80211_sta_rx_bandwidth new_bw; | ||||||
| 
 | 
 | ||||||
| 			/* If it doesn't support 40 MHz it can't change ... */ | 			/* If it doesn't support 40 MHz it can't change ... */ | ||||||
| 			if (!rx->sta->supports_40mhz) | 			if (!(rx->sta->sta.ht_cap.cap & | ||||||
|  | 					IEEE80211_HT_CAP_SUP_WIDTH_20_40)) | ||||||
| 				goto handled; | 				goto handled; | ||||||
| 
 | 
 | ||||||
| 			old_40mhz = rx->sta->sta.ht_cap.cap & | 			if (chanwidth == IEEE80211_HT_CHANWIDTH_20MHZ) | ||||||
| 					IEEE80211_HT_CAP_SUP_WIDTH_20_40; | 				new_bw = IEEE80211_STA_RX_BW_20; | ||||||
| 			new_40mhz = chanwidth == IEEE80211_HT_CHANWIDTH_ANY; |  | ||||||
| 
 |  | ||||||
| 			if (old_40mhz == new_40mhz) |  | ||||||
| 				goto handled; |  | ||||||
| 
 |  | ||||||
| 			if (new_40mhz) |  | ||||||
| 				rx->sta->sta.ht_cap.cap |= |  | ||||||
| 					IEEE80211_HT_CAP_SUP_WIDTH_20_40; |  | ||||||
| 			else | 			else | ||||||
| 				rx->sta->sta.ht_cap.cap &= | 				new_bw = ieee80211_sta_cur_vht_bw(rx->sta); | ||||||
| 					~IEEE80211_HT_CAP_SUP_WIDTH_20_40; | 
 | ||||||
|  | 			if (rx->sta->sta.bandwidth == new_bw) | ||||||
|  | 				goto handled; | ||||||
| 
 | 
 | ||||||
| 			sband = rx->local->hw.wiphy->bands[status->band]; | 			sband = rx->local->hw.wiphy->bands[status->band]; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -296,8 +296,6 @@ struct sta_ampdu_mlme { | ||||||
|  * @sta: station information we share with the driver |  * @sta: station information we share with the driver | ||||||
|  * @sta_state: duplicates information about station state (for debug) |  * @sta_state: duplicates information about station state (for debug) | ||||||
|  * @beacon_loss_count: number of times beacon loss has triggered |  * @beacon_loss_count: number of times beacon loss has triggered | ||||||
|  * @supports_40mhz: tracks whether the station advertised 40 MHz support |  | ||||||
|  *	as we overwrite its HT parameters with the currently used value |  | ||||||
|  * @rcu_head: RCU head used for freeing this station struct |  * @rcu_head: RCU head used for freeing this station struct | ||||||
|  */ |  */ | ||||||
| struct sta_info { | struct sta_info { | ||||||
|  | @ -403,8 +401,6 @@ struct sta_info { | ||||||
| 	unsigned int lost_packets; | 	unsigned int lost_packets; | ||||||
| 	unsigned int beacon_loss_count; | 	unsigned int beacon_loss_count; | ||||||
| 
 | 
 | ||||||
| 	bool supports_40mhz; |  | ||||||
| 
 |  | ||||||
| 	/* keep last! */ | 	/* keep last! */ | ||||||
| 	struct ieee80211_sta sta; | 	struct ieee80211_sta sta; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -27,6 +27,10 @@ void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, | ||||||
| 	if (!vht_cap_ie || !sband->vht_cap.vht_supported) | 	if (!vht_cap_ie || !sband->vht_cap.vht_supported) | ||||||
| 		return; | 		return; | ||||||
| 
 | 
 | ||||||
|  | 	/* A VHT STA must support 40 MHz */ | ||||||
|  | 	if (!(sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
| 	vht_cap->vht_supported = true; | 	vht_cap->vht_supported = true; | ||||||
| 
 | 
 | ||||||
| 	vht_cap->cap = le32_to_cpu(vht_cap_ie->vht_cap_info); | 	vht_cap->cap = le32_to_cpu(vht_cap_ie->vht_cap_info); | ||||||
|  | @ -34,4 +38,39 @@ void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, | ||||||
| 	/* Copy peer MCS info, the driver might need them. */ | 	/* Copy peer MCS info, the driver might need them. */ | ||||||
| 	memcpy(&vht_cap->vht_mcs, &vht_cap_ie->supp_mcs, | 	memcpy(&vht_cap->vht_mcs, &vht_cap_ie->supp_mcs, | ||||||
| 	       sizeof(struct ieee80211_vht_mcs_info)); | 	       sizeof(struct ieee80211_vht_mcs_info)); | ||||||
|  | 
 | ||||||
|  | 	sta->sta.bandwidth = ieee80211_sta_cur_vht_bw(sta); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta) | ||||||
|  | { | ||||||
|  | 	struct ieee80211_sub_if_data *sdata = sta->sdata; | ||||||
|  | 	u32 cap = sta->sta.vht_cap.cap; | ||||||
|  | 
 | ||||||
|  | 	if (!sta->sta.vht_cap.vht_supported) | ||||||
|  | 		return sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? | ||||||
|  | 				IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20; | ||||||
|  | 
 | ||||||
|  | 	/* TODO: handle VHT opmode notification data */ | ||||||
|  | 
 | ||||||
|  | 	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: | ||||||
|  | 	case NL80211_CHAN_WIDTH_40: | ||||||
|  | 		return sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? | ||||||
|  | 				IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20; | ||||||
|  | 	case NL80211_CHAN_WIDTH_160: | ||||||
|  | 		if (cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) | ||||||
|  | 			return IEEE80211_STA_RX_BW_160; | ||||||
|  | 		/* fall through */ | ||||||
|  | 	case NL80211_CHAN_WIDTH_80P80: | ||||||
|  | 		if (cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) | ||||||
|  | 			return IEEE80211_STA_RX_BW_160; | ||||||
|  | 		/* fall through */ | ||||||
|  | 	case NL80211_CHAN_WIDTH_80: | ||||||
|  | 		return IEEE80211_STA_RX_BW_80; | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Johannes Berg
				Johannes Berg