diff options
Diffstat (limited to 'net/mac80211/wme.c')
| -rw-r--r-- | net/mac80211/wme.c | 151 | 
1 files changed, 99 insertions, 52 deletions
diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index 58e75bbc1f9..d51422c778d 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -52,6 +52,51 @@ static int wme_downgrade_ac(struct sk_buff *skb)  	}  } +static u16 ieee80211_downgrade_queue(struct ieee80211_sub_if_data *sdata, +				     struct sk_buff *skb) +{ +	/* in case we are a client verify acm is not set for this ac */ +	while (unlikely(sdata->wmm_acm & BIT(skb->priority))) { +		if (wme_downgrade_ac(skb)) { +			/* +			 * This should not really happen. The AP has marked all +			 * lower ACs to require admission control which is not +			 * a reasonable configuration. Allow the frame to be +			 * transmitted using AC_BK as a workaround. +			 */ +			break; +		} +	} + +	/* look up which queue to use for frames with this 1d tag */ +	return ieee802_1d_to_ac[skb->priority]; +} + +/* Indicate which queue to use for this fully formed 802.11 frame */ +u16 ieee80211_select_queue_80211(struct ieee80211_sub_if_data *sdata, +				 struct sk_buff *skb, +				 struct ieee80211_hdr *hdr) +{ +	struct ieee80211_local *local = sdata->local; +	u8 *p; + +	if (local->hw.queues < IEEE80211_NUM_ACS) +		return 0; + +	if (!ieee80211_is_data(hdr->frame_control)) { +		skb->priority = 7; +		return ieee802_1d_to_ac[skb->priority]; +	} +	if (!ieee80211_is_data_qos(hdr->frame_control)) { +		skb->priority = 0; +		return ieee802_1d_to_ac[skb->priority]; +	} + +	p = ieee80211_get_qos_ctl(hdr); +	skb->priority = *p & IEEE80211_QOS_CTL_TAG1D_MASK; + +	return ieee80211_downgrade_queue(sdata, skb); +}  /* Indicate which queue to use. */  u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, @@ -59,26 +104,23 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,  {  	struct ieee80211_local *local = sdata->local;  	struct sta_info *sta = NULL; -	u32 sta_flags = 0;  	const u8 *ra = NULL;  	bool qos = false; +	struct mac80211_qos_map *qos_map; -	if (local->hw.queues < 4 || skb->len < 6) { +	if (local->hw.queues < IEEE80211_NUM_ACS || skb->len < 6) {  		skb->priority = 0; /* required for correct WPA/11i MIC */ -		return min_t(u16, local->hw.queues - 1, -			     ieee802_1d_to_ac[skb->priority]); +		return 0;  	}  	rcu_read_lock();  	switch (sdata->vif.type) {  	case NL80211_IFTYPE_AP_VLAN: -		rcu_read_lock();  		sta = rcu_dereference(sdata->u.vlan.sta); -		if (sta) -			sta_flags = get_sta_flags(sta); -		rcu_read_unlock(); -		if (sta) +		if (sta) { +			qos = test_sta_flag(sta, WLAN_STA_WME);  			break; +		}  	case NL80211_IFTYPE_AP:  		ra = skb->data;  		break; @@ -87,11 +129,7 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,  		break;  #ifdef CONFIG_MAC80211_MESH  	case NL80211_IFTYPE_MESH_POINT: -		/* -		 * XXX: This is clearly broken ... but already was before, -		 * because ieee80211_fill_mesh_addresses() would clear A1 -		 * except for multicast addresses. -		 */ +		qos = true;  		break;  #endif  	case NL80211_IFTYPE_STATION: @@ -107,62 +145,71 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,  	if (!sta && ra && !is_multicast_ether_addr(ra)) {  		sta = sta_info_get(sdata, ra);  		if (sta) -			sta_flags = get_sta_flags(sta); +			qos = test_sta_flag(sta, WLAN_STA_WME);  	} - -	if (sta_flags & WLAN_STA_WME) -		qos = true; -  	rcu_read_unlock();  	if (!qos) {  		skb->priority = 0; /* required for correct WPA/11i MIC */ -		return ieee802_1d_to_ac[skb->priority]; +		return IEEE80211_AC_BE; +	} + +	if (skb->protocol == sdata->control_port_protocol) { +		skb->priority = 7; +		return ieee80211_downgrade_queue(sdata, skb);  	}  	/* use the data classifier to determine what 802.1d tag the  	 * data frame has */ -	skb->priority = cfg80211_classify8021d(skb); +	rcu_read_lock(); +	qos_map = rcu_dereference(sdata->qos_map); +	skb->priority = cfg80211_classify8021d(skb, qos_map ? +					       &qos_map->qos_map : NULL); +	rcu_read_unlock(); -	return ieee80211_downgrade_queue(local, skb); +	return ieee80211_downgrade_queue(sdata, skb);  } -u16 ieee80211_downgrade_queue(struct ieee80211_local *local, -			      struct sk_buff *skb) +/** + * ieee80211_set_qos_hdr - Fill in the QoS header if there is one. + * + * @sdata: local subif + * @skb: packet to be updated + */ +void ieee80211_set_qos_hdr(struct ieee80211_sub_if_data *sdata, +			   struct sk_buff *skb)  { -	/* in case we are a client verify acm is not set for this ac */ -	while (unlikely(local->wmm_acm & BIT(skb->priority))) { -		if (wme_downgrade_ac(skb)) { -			/* -			 * This should not really happen. The AP has marked all -			 * lower ACs to require admission control which is not -			 * a reasonable configuration. Allow the frame to be -			 * transmitted using AC_BK as a workaround. -			 */ -			break; -		} -	} +	struct ieee80211_hdr *hdr = (void *)skb->data; +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); +	u8 *p; +	u8 ack_policy, tid; -	/* look up which queue to use for frames with this 1d tag */ -	return ieee802_1d_to_ac[skb->priority]; -} +	if (!ieee80211_is_data_qos(hdr->frame_control)) +		return; -void ieee80211_set_qos_hdr(struct ieee80211_local *local, struct sk_buff *skb) -{ -	struct ieee80211_hdr *hdr = (void *)skb->data; +	p = ieee80211_get_qos_ctl(hdr); +	tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; -	/* Fill in the QoS header if there is one. */ -	if (ieee80211_is_data_qos(hdr->frame_control)) { -		u8 *p = ieee80211_get_qos_ctl(hdr); -		u8 ack_policy = 0, tid; +	/* preserve EOSP bit */ +	ack_policy = *p & IEEE80211_QOS_CTL_EOSP; -		tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; +	if (is_multicast_ether_addr(hdr->addr1) || +	    sdata->noack_map & BIT(tid)) { +		ack_policy |= IEEE80211_QOS_CTL_ACK_POLICY_NOACK; +		info->flags |= IEEE80211_TX_CTL_NO_ACK; +	} -		if (unlikely(local->wifi_wme_noack_test)) -			ack_policy |= QOS_CONTROL_ACK_POLICY_NOACK << -					QOS_CONTROL_ACK_POLICY_SHIFT; -		/* qos header is 2 bytes, second reserved */ -		*p++ = ack_policy | tid; +	/* qos header is 2 bytes */ +	*p++ = ack_policy | tid; +	if (ieee80211_vif_is_mesh(&sdata->vif)) { +		/* preserve RSPI and Mesh PS Level bit */ +		*p &= ((IEEE80211_QOS_CTL_RSPI | +			IEEE80211_QOS_CTL_MESH_PS_LEVEL) >> 8); + +		/* Nulls don't have a mesh header (frame body) */ +		if (!ieee80211_is_qos_nullfunc(hdr->frame_control)) +			*p |= (IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT >> 8); +	} else {  		*p = 0;  	}  }  | 
