diff options
Diffstat (limited to 'net/mac80211/status.c')
| -rw-r--r-- | net/mac80211/status.c | 195 | 
1 files changed, 178 insertions, 17 deletions
diff --git a/net/mac80211/status.c b/net/mac80211/status.c index a88e3bf17e9d..ab8ba5835ca0 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -254,14 +254,22 @@ static void ieee80211_set_bar_pending(struct sta_info *sta, u8 tid, u16 ssn)  	tid_tx->bar_pending = true;  } -static int ieee80211_tx_radiotap_len(struct ieee80211_tx_info *info) +static int ieee80211_tx_radiotap_len(struct ieee80211_tx_info *info, +				     struct ieee80211_tx_status *status)  {  	int len = sizeof(struct ieee80211_radiotap_header);  	/* IEEE80211_RADIOTAP_RATE rate */ -	if (info->status.rates[0].idx >= 0 && -	    !(info->status.rates[0].flags & (IEEE80211_TX_RC_MCS | -					     IEEE80211_TX_RC_VHT_MCS))) +	if (status && status->rate && !(status->rate->flags & +					(RATE_INFO_FLAGS_MCS | +					 RATE_INFO_FLAGS_DMG | +					 RATE_INFO_FLAGS_EDMG | +					 RATE_INFO_FLAGS_VHT_MCS | +					 RATE_INFO_FLAGS_HE_MCS))) +		len += 2; +	else if (info->status.rates[0].idx >= 0 && +		 !(info->status.rates[0].flags & +		   (IEEE80211_TX_RC_MCS | IEEE80211_TX_RC_VHT_MCS)))  		len += 2;  	/* IEEE80211_RADIOTAP_TX_FLAGS */ @@ -272,7 +280,14 @@ static int ieee80211_tx_radiotap_len(struct ieee80211_tx_info *info)  	/* IEEE80211_RADIOTAP_MCS  	 * IEEE80211_RADIOTAP_VHT */ -	if (info->status.rates[0].idx >= 0) { +	if (status && status->rate) { +		if (status->rate->flags & RATE_INFO_FLAGS_MCS) +			len += 3; +		else if (status->rate->flags & RATE_INFO_FLAGS_VHT_MCS) +			len = ALIGN(len, 2) + 12; +		else if (status->rate->flags & RATE_INFO_FLAGS_HE_MCS) +			len = ALIGN(len, 2) + 12; +	} else if (info->status.rates[0].idx >= 0) {  		if (info->status.rates[0].flags & IEEE80211_TX_RC_MCS)  			len += 3;  		else if (info->status.rates[0].flags & IEEE80211_TX_RC_VHT_MCS) @@ -286,12 +301,14 @@ static void  ieee80211_add_tx_radiotap_header(struct ieee80211_local *local,  				 struct ieee80211_supported_band *sband,  				 struct sk_buff *skb, int retry_count, -				 int rtap_len, int shift) +				 int rtap_len, int shift, +				 struct ieee80211_tx_status *status)  {  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;  	struct ieee80211_radiotap_header *rthdr;  	unsigned char *pos; +	u16 legacy_rate = 0;  	u16 txflags;  	rthdr = skb_push(skb, rtap_len); @@ -310,14 +327,23 @@ ieee80211_add_tx_radiotap_header(struct ieee80211_local *local,  	 */  	/* IEEE80211_RADIOTAP_RATE */ -	if (info->status.rates[0].idx >= 0 && -	    !(info->status.rates[0].flags & (IEEE80211_TX_RC_MCS | -					     IEEE80211_TX_RC_VHT_MCS))) { -		u16 rate; +	if (status && status->rate) { +		if (!(status->rate->flags & (RATE_INFO_FLAGS_MCS | +					     RATE_INFO_FLAGS_DMG | +					     RATE_INFO_FLAGS_EDMG | +					     RATE_INFO_FLAGS_VHT_MCS | +					     RATE_INFO_FLAGS_HE_MCS))) +			legacy_rate = status->rate->legacy; +	} else if (info->status.rates[0].idx >= 0 && +		 !(info->status.rates[0].flags & (IEEE80211_TX_RC_MCS | +						  IEEE80211_TX_RC_VHT_MCS))) +		legacy_rate = +			sband->bitrates[info->status.rates[0].idx].bitrate; + +	if (legacy_rate) {  		rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE); -		rate = sband->bitrates[info->status.rates[0].idx].bitrate; -		*pos = DIV_ROUND_UP(rate, 5 * (1 << shift)); +		*pos = DIV_ROUND_UP(legacy_rate, 5 * (1 << shift));  		/* padding for tx flags */  		pos += 2;  	} @@ -341,7 +367,140 @@ ieee80211_add_tx_radiotap_header(struct ieee80211_local *local,  	*pos = retry_count;  	pos++; -	if (info->status.rates[0].idx < 0) +	if (status && status->rate && +	    (status->rate->flags & RATE_INFO_FLAGS_MCS)) { +		rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_MCS); +		pos[0] = IEEE80211_RADIOTAP_MCS_HAVE_MCS | +			 IEEE80211_RADIOTAP_MCS_HAVE_GI | +			 IEEE80211_RADIOTAP_MCS_HAVE_BW; +		if (status->rate->flags & RATE_INFO_FLAGS_SHORT_GI) +			pos[1] |= IEEE80211_RADIOTAP_MCS_SGI; +		if (status->rate->bw == RATE_INFO_BW_40) +			pos[1] |= IEEE80211_RADIOTAP_MCS_BW_40; +		pos[2] = status->rate->mcs; +		pos += 3; +	} else if (status && status->rate && +		   (status->rate->flags & RATE_INFO_FLAGS_VHT_MCS)) { +		u16 known = local->hw.radiotap_vht_details & +			(IEEE80211_RADIOTAP_VHT_KNOWN_GI | +			 IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH); + +		rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_VHT); + +		/* required alignment from rthdr */ +		pos = (u8 *)rthdr + ALIGN(pos - (u8 *)rthdr, 2); + +		/* u16 known - IEEE80211_RADIOTAP_VHT_KNOWN_* */ +		put_unaligned_le16(known, pos); +		pos += 2; + +		/* u8 flags - IEEE80211_RADIOTAP_VHT_FLAG_* */ +		if (status->rate->flags & RATE_INFO_FLAGS_SHORT_GI) +			*pos |= IEEE80211_RADIOTAP_VHT_FLAG_SGI; +		pos++; + +		/* u8 bandwidth */ +		switch (status->rate->bw) { +		case RATE_INFO_BW_160: +			*pos = 11; +			break; +		case RATE_INFO_BW_80: +			*pos = 4; +			break; +		case RATE_INFO_BW_40: +			*pos = 1; +			break; +		default: +			*pos = 0; +			break; +		} +		pos++; + +		/* u8 mcs_nss[4] */ +		*pos = (status->rate->mcs << 4) | status->rate->nss; +		pos += 4; + +		/* u8 coding */ +		pos++; +		/* u8 group_id */ +		pos++; +		/* u16 partial_aid */ +		pos += 2; +	} else if (status && status->rate && +		   (status->rate->flags & RATE_INFO_FLAGS_HE_MCS)) { +		struct ieee80211_radiotap_he *he; + +		rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_HE); + +		/* required alignment from rthdr */ +		pos = (u8 *)rthdr + ALIGN(pos - (u8 *)rthdr, 2); +		he = (struct ieee80211_radiotap_he *)pos; + +		he->data1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_FORMAT_SU | +					IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN | +					IEEE80211_RADIOTAP_HE_DATA1_DATA_DCM_KNOWN | +					IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN); + +		he->data2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN); + +#define HE_PREP(f, val) le16_encode_bits(val, IEEE80211_RADIOTAP_HE_##f) + +		he->data6 |= HE_PREP(DATA6_NSTS, status->rate->nss); + +#define CHECK_GI(s) \ +	BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_DATA5_GI_##s != \ +	(int)NL80211_RATE_INFO_HE_GI_##s) + +		CHECK_GI(0_8); +		CHECK_GI(1_6); +		CHECK_GI(3_2); + +		he->data3 |= HE_PREP(DATA3_DATA_MCS, status->rate->mcs); +		he->data3 |= HE_PREP(DATA3_DATA_DCM, status->rate->he_dcm); + +		he->data5 |= HE_PREP(DATA5_GI, status->rate->he_gi); + +		switch (status->rate->bw) { +		case RATE_INFO_BW_20: +			he->data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC, +					     IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_20MHZ); +			break; +		case RATE_INFO_BW_40: +			he->data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC, +					     IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_40MHZ); +			break; +		case RATE_INFO_BW_80: +			he->data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC, +					     IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_80MHZ); +			break; +		case RATE_INFO_BW_160: +			he->data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC, +					     IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_160MHZ); +			break; +		case RATE_INFO_BW_HE_RU: +#define CHECK_RU_ALLOC(s) \ +	BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_##s##T != \ +	NL80211_RATE_INFO_HE_RU_ALLOC_##s + 4) + +			CHECK_RU_ALLOC(26); +			CHECK_RU_ALLOC(52); +			CHECK_RU_ALLOC(106); +			CHECK_RU_ALLOC(242); +			CHECK_RU_ALLOC(484); +			CHECK_RU_ALLOC(996); +			CHECK_RU_ALLOC(2x996); + +			he->data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC, +					     status->rate->he_ru_alloc + 4); +			break; +		default: +			WARN_ONCE(1, "Invalid SU BW %d\n", status->rate->bw); +		} + +		pos += sizeof(struct ieee80211_radiotap_he); +	} + +	if ((status && status->rate) || info->status.rates[0].idx < 0)  		return;  	/* IEEE80211_RADIOTAP_MCS @@ -645,7 +804,8 @@ static int ieee80211_tx_get_rates(struct ieee80211_hw *hw,  void ieee80211_tx_monitor(struct ieee80211_local *local, struct sk_buff *skb,  			  struct ieee80211_supported_band *sband, -			  int retry_count, int shift, bool send_to_cooked) +			  int retry_count, int shift, bool send_to_cooked, +			  struct ieee80211_tx_status *status)  {  	struct sk_buff *skb2;  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -654,14 +814,14 @@ void ieee80211_tx_monitor(struct ieee80211_local *local, struct sk_buff *skb,  	int rtap_len;  	/* send frame to monitor interfaces now */ -	rtap_len = ieee80211_tx_radiotap_len(info); +	rtap_len = ieee80211_tx_radiotap_len(info, status);  	if (WARN_ON_ONCE(skb_headroom(skb) < rtap_len)) {  		pr_err("ieee80211_tx_status: headroom too small\n");  		dev_kfree_skb(skb);  		return;  	}  	ieee80211_add_tx_radiotap_header(local, sband, skb, retry_count, -					 rtap_len, shift); +					 rtap_len, shift, status);  	/* XXX: is this sufficient for BPF? */  	skb_reset_mac_header(skb); @@ -901,7 +1061,8 @@ static void __ieee80211_tx_status(struct ieee80211_hw *hw,  	}  	/* send to monitor interfaces */ -	ieee80211_tx_monitor(local, skb, sband, retry_count, shift, send_to_cooked); +	ieee80211_tx_monitor(local, skb, sband, retry_count, shift, +			     send_to_cooked, status);  }  void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)  |