diff options
Diffstat (limited to 'net/mac80211/tx.c')
| -rw-r--r-- | net/mac80211/tx.c | 358 | 
1 files changed, 279 insertions, 79 deletions
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 3456c0486b4..1a252c606ad 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -19,6 +19,7 @@  #include <linux/bitmap.h>  #include <linux/rcupdate.h>  #include <linux/export.h> +#include <linux/time.h>  #include <net/net_namespace.h>  #include <net/ieee80211_radiotap.h>  #include <net/cfg80211.h> @@ -413,6 +414,9 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx)  	if (ieee80211_has_order(hdr->frame_control))  		return TX_CONTINUE; +	if (ieee80211_is_probe_req(hdr->frame_control)) +		return TX_CONTINUE; +  	if (tx->local->hw.flags & IEEE80211_HW_QUEUE_CONTROL)  		info->hw_queue = tx->sdata->vif.cab_queue; @@ -451,8 +455,7 @@ static int ieee80211_use_mfp(__le16 fc, struct sta_info *sta,  	if (sta == NULL || !test_sta_flag(sta, WLAN_STA_MFP))  		return 0; -	if (!ieee80211_is_robust_mgmt_frame((struct ieee80211_hdr *) -					    skb->data)) +	if (!ieee80211_is_robust_mgmt_frame(skb))  		return 0;  	return 1; @@ -474,11 +477,8 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)  		     !(info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER))) {  		int ac = skb_get_queue_mapping(tx->skb); -		/* only deauth, disassoc and action are bufferable MMPDUs */  		if (ieee80211_is_mgmt(hdr->frame_control) && -		    !ieee80211_is_deauth(hdr->frame_control) && -		    !ieee80211_is_disassoc(hdr->frame_control) && -		    !ieee80211_is_action(hdr->frame_control)) { +		    !ieee80211_is_bufferable_mmpdu(hdr->frame_control)) {  			info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER;  			return TX_CONTINUE;  		} @@ -487,6 +487,20 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)  		       sta->sta.addr, sta->sta.aid, ac);  		if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER)  			purge_old_ps_buffers(tx->local); + +		/* sync with ieee80211_sta_ps_deliver_wakeup */ +		spin_lock(&sta->ps_lock); +		/* +		 * STA woke up the meantime and all the frames on ps_tx_buf have +		 * been queued to pending queue. No reordering can happen, go +		 * ahead and Tx the packet. +		 */ +		if (!test_sta_flag(sta, WLAN_STA_PS_STA) && +		    !test_sta_flag(sta, WLAN_STA_PS_DRIVER)) { +			spin_unlock(&sta->ps_lock); +			return TX_CONTINUE; +		} +  		if (skb_queue_len(&sta->ps_tx_buf[ac]) >= STA_MAX_TX_BUFFER) {  			struct sk_buff *old = skb_dequeue(&sta->ps_tx_buf[ac]);  			ps_dbg(tx->sdata, @@ -499,7 +513,9 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)  		info->control.jiffies = jiffies;  		info->control.vif = &tx->sdata->vif;  		info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; +		info->flags &= ~IEEE80211_TX_TEMPORARY_FLAGS;  		skb_queue_tail(&sta->ps_tx_buf[ac], tx->skb); +		spin_unlock(&sta->ps_lock);  		if (!timer_pending(&local->sta_cleanup))  			mod_timer(&local->sta_cleanup, @@ -557,11 +573,12 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)  	if (unlikely(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT))  		tx->key = NULL; -	else if (tx->sta && (key = rcu_dereference(tx->sta->ptk))) +	else if (tx->sta && +		 (key = rcu_dereference(tx->sta->ptk[tx->sta->ptk_idx])))  		tx->key = key;  	else if (ieee80211_is_mgmt(hdr->frame_control) &&  		 is_multicast_ether_addr(hdr->addr1) && -		 ieee80211_is_robust_mgmt_frame(hdr) && +		 ieee80211_is_robust_mgmt_frame(tx->skb) &&  		 (key = rcu_dereference(tx->sdata->default_mgmt_key)))  		tx->key = key;  	else if (is_multicast_ether_addr(hdr->addr1) && @@ -576,12 +593,12 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)  		tx->key = NULL;  	else if (tx->skb->protocol == tx->sdata->control_port_protocol)  		tx->key = NULL; -	else if (ieee80211_is_robust_mgmt_frame(hdr) && +	else if (ieee80211_is_robust_mgmt_frame(tx->skb) &&  		 !(ieee80211_is_action(hdr->frame_control) &&  		   tx->sta && test_sta_flag(tx->sta, WLAN_STA_MFP)))  		tx->key = NULL;  	else if (ieee80211_is_mgmt(hdr->frame_control) && -		 !ieee80211_is_robust_mgmt_frame(hdr)) +		 !ieee80211_is_robust_mgmt_frame(tx->skb))  		tx->key = NULL;  	else {  		I802_DEBUG_INC(tx->local->tx_handlers_drop_unencrypted); @@ -840,15 +857,16 @@ static int ieee80211_fragment(struct ieee80211_tx_data *tx,  		rem -= fraglen;  		tmp = dev_alloc_skb(local->tx_headroom +  				    frag_threshold + -				    IEEE80211_ENCRYPT_HEADROOM + +				    tx->sdata->encrypt_headroom +  				    IEEE80211_ENCRYPT_TAILROOM);  		if (!tmp)  			return -ENOMEM;  		__skb_queue_tail(&tx->skbs, tmp); -		skb_reserve(tmp, local->tx_headroom + -				 IEEE80211_ENCRYPT_HEADROOM); +		skb_reserve(tmp, +			    local->tx_headroom + tx->sdata->encrypt_headroom); +  		/* copy control information */  		memcpy(tmp->cb, skb->cb, sizeof(tmp->cb)); @@ -871,7 +889,7 @@ static int ieee80211_fragment(struct ieee80211_tx_data *tx,  	}  	/* adjust first fragment's length */ -	skb->len = hdrlen + per_fragm; +	skb_trim(skb, hdrlen + per_fragm);  	return 0;  } @@ -1070,6 +1088,7 @@ static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx,  			queued = true;  			info->control.vif = &tx->sdata->vif;  			info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; +			info->flags &= ~IEEE80211_TX_TEMPORARY_FLAGS;  			__skb_queue_tail(&tid_tx->pending, skb);  			if (skb_queue_len(&tid_tx->pending) > STA_MAX_TX_BUFFER)  				purge_skb = __skb_dequeue(&tid_tx->pending); @@ -1120,7 +1139,8 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,  		tx->sta = rcu_dereference(sdata->u.vlan.sta);  		if (!tx->sta && sdata->dev->ieee80211_ptr->use_4addr)  			return TX_DROP; -	} else if (info->flags & IEEE80211_TX_CTL_INJECTED || +	} else if (info->flags & (IEEE80211_TX_CTL_INJECTED | +				  IEEE80211_TX_INTFL_NL80211_FRAME_TX) ||  		   tx->sdata->control_port_protocol == tx->skb->protocol) {  		tx->sta = sta_info_get_bss(sdata, hdr->addr1);  	} @@ -1366,6 +1386,35 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)  	return 0;  } +bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw, +			      struct ieee80211_vif *vif, struct sk_buff *skb, +			      int band, struct ieee80211_sta **sta) +{ +	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); +	struct ieee80211_tx_data tx; + +	if (ieee80211_tx_prepare(sdata, &tx, skb) == TX_DROP) +		return false; + +	info->band = band; +	info->control.vif = vif; +	info->hw_queue = vif->hw_queue[skb_get_queue_mapping(skb)]; + +	if (invoke_tx_handlers(&tx)) +		return false; + +	if (sta) { +		if (tx.sta) +			*sta = &tx.sta->sta; +		else +			*sta = NULL; +	} + +	return true; +} +EXPORT_SYMBOL(ieee80211_tx_prepare_skb); +  /*   * Returns false if the frame couldn't be transmitted but was queued instead.   */ @@ -1455,7 +1504,7 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,  	headroom = local->tx_headroom;  	if (may_encrypt) -		headroom += IEEE80211_ENCRYPT_HEADROOM; +		headroom += sdata->encrypt_headroom;  	headroom -= skb_headroom(skb);  	headroom = max_t(int, 0, headroom); @@ -1694,8 +1743,7 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,  	 * radar detection by itself. We can do that later by adding a  	 * monitor flag interfaces used for AP support.  	 */ -	if ((chan->flags & (IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_RADAR | -			    IEEE80211_CHAN_PASSIVE_SCAN))) +	if ((chan->flags & (IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_RADAR)))  		goto fail_rcu;  	ieee80211_xmit(sdata, skb, chan->band); @@ -1710,6 +1758,26 @@ fail:  	return NETDEV_TX_OK; /* meaning, we dealt with the skb */  } +/* + * Measure Tx frame arrival time for Tx latency statistics calculation + * A single Tx frame latency should be measured from when it is entering the + * Kernel until we receive Tx complete confirmation indication and the skb is + * freed. + */ +static void ieee80211_tx_latency_start_msrmnt(struct ieee80211_local *local, +					      struct sk_buff *skb) +{ +	struct timespec skb_arv; +	struct ieee80211_tx_latency_bin_ranges *tx_latency; + +	tx_latency = rcu_dereference(local->tx_latency); +	if (!tx_latency) +		return; + +	ktime_get_ts(&skb_arv); +	skb->tstamp = ktime_set(skb_arv.tv_sec, skb_arv.tv_nsec); +} +  /**   * ieee80211_subif_start_xmit - netif start_xmit function for Ethernet-type   * subinterfaces (wlan#, WDS, and VLAN interfaces) @@ -1760,6 +1828,9 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,  	rcu_read_lock(); +	/* Measure frame arrival for Tx latency statistics calculation */ +	ieee80211_tx_latency_start_msrmnt(local, skb); +  	switch (sdata->vif.type) {  	case NL80211_IFTYPE_AP_VLAN:  		sta = rcu_dereference(sdata->u.vlan.sta); @@ -1981,7 +2052,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,  	 * EAPOL frames from the local station.  	 */  	if (unlikely(!ieee80211_vif_is_mesh(&sdata->vif) && -		     !is_multicast_ether_addr(hdr.addr1) && !authorized && +		     !multicast && !authorized &&  		     (cpu_to_be16(ethertype) != sdata->control_port_protocol ||  		      !ether_addr_equal(sdata->vif.addr, skb->data + ETH_ALEN)))) {  #ifdef CONFIG_MAC80211_VERBOSE_DEBUG @@ -2079,7 +2150,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,  	 */  	if (head_need > 0 || skb_cloned(skb)) { -		head_need += IEEE80211_ENCRYPT_HEADROOM; +		head_need += sdata->encrypt_headroom;  		head_need += local->tx_headroom;  		head_need = max_t(int, 0, head_need);  		if (ieee80211_skb_resize(sdata, skb, head_need, true)) { @@ -2106,7 +2177,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,  	if (ieee80211_is_data_qos(fc)) {  		__le16 *qos_control; -		qos_control = (__le16*) skb_push(skb, 2); +		qos_control = (__le16 *) skb_push(skb, 2);  		memcpy(skb_push(skb, hdrlen - 2), &hdr, hdrlen - 2);  		/*  		 * Maybe we could actually set some fields here, for now just @@ -2257,7 +2328,8 @@ void ieee80211_tx_pending(unsigned long data)  /* functions for drivers to get certain frames */  static void __ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, -				       struct ps_data *ps, struct sk_buff *skb) +				       struct ps_data *ps, struct sk_buff *skb, +				       bool is_template)  {  	u8 *pos, *tim;  	int aid0 = 0; @@ -2268,13 +2340,14 @@ static void __ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,  	if (atomic_read(&ps->num_sta_ps) > 0)  		/* in the hope that this is faster than  		 * checking byte-for-byte */ -		have_bits = !bitmap_empty((unsigned long*)ps->tim, +		have_bits = !bitmap_empty((unsigned long *)ps->tim,  					  IEEE80211_MAX_AID+1); - -	if (ps->dtim_count == 0) -		ps->dtim_count = sdata->vif.bss_conf.dtim_period - 1; -	else -		ps->dtim_count--; +	if (!is_template) { +		if (ps->dtim_count == 0) +			ps->dtim_count = sdata->vif.bss_conf.dtim_period - 1; +		else +			ps->dtim_count--; +	}  	tim = pos = (u8 *) skb_put(skb, 6);  	*pos++ = WLAN_EID_TIM; @@ -2320,7 +2393,8 @@ static void __ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,  }  static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, -				    struct ps_data *ps, struct sk_buff *skb) +				    struct ps_data *ps, struct sk_buff *skb, +				    bool is_template)  {  	struct ieee80211_local *local = sdata->local; @@ -2332,60 +2406,92 @@ static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,  	 * of the tim bitmap in mac80211 and the driver.  	 */  	if (local->tim_in_locked_section) { -		__ieee80211_beacon_add_tim(sdata, ps, skb); +		__ieee80211_beacon_add_tim(sdata, ps, skb, is_template);  	} else {  		spin_lock_bh(&local->tim_lock); -		__ieee80211_beacon_add_tim(sdata, ps, skb); +		__ieee80211_beacon_add_tim(sdata, ps, skb, is_template);  		spin_unlock_bh(&local->tim_lock);  	}  	return 0;  } -void ieee80211_csa_finish(struct ieee80211_vif *vif) -{ -	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); - -	ieee80211_queue_work(&sdata->local->hw, -			     &sdata->csa_finalize_work); -} -EXPORT_SYMBOL(ieee80211_csa_finish); - -static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata, -				 struct beacon_data *beacon) +static void ieee80211_set_csa(struct ieee80211_sub_if_data *sdata, +			      struct beacon_data *beacon)  {  	struct probe_resp *resp; -	int counter_offset_beacon = sdata->csa_counter_offset_beacon; -	int counter_offset_presp = sdata->csa_counter_offset_presp; +	u8 *beacon_data; +	size_t beacon_data_len; +	int i; +	u8 count = sdata->csa_current_counter; -	/* warn if the driver did not check for/react to csa completeness */ -	if (WARN_ON(((u8 *)beacon->tail)[counter_offset_beacon] == 0)) +	switch (sdata->vif.type) { +	case NL80211_IFTYPE_AP: +		beacon_data = beacon->tail; +		beacon_data_len = beacon->tail_len; +		break; +	case NL80211_IFTYPE_ADHOC: +		beacon_data = beacon->head; +		beacon_data_len = beacon->head_len; +		break; +	case NL80211_IFTYPE_MESH_POINT: +		beacon_data = beacon->head; +		beacon_data_len = beacon->head_len; +		break; +	default:  		return; +	} + +	for (i = 0; i < IEEE80211_MAX_CSA_COUNTERS_NUM; ++i) { +		u16 counter_offset_beacon = +			sdata->csa_counter_offset_beacon[i]; +		u16 counter_offset_presp = sdata->csa_counter_offset_presp[i]; -	((u8 *)beacon->tail)[counter_offset_beacon]--; +		if (counter_offset_beacon) { +			if (WARN_ON(counter_offset_beacon >= beacon_data_len)) +				return; -	if (sdata->vif.type == NL80211_IFTYPE_AP && -	    counter_offset_presp) { -		rcu_read_lock(); -		resp = rcu_dereference(sdata->u.ap.probe_resp); +			beacon_data[counter_offset_beacon] = count; +		} -		/* if nl80211 accepted the offset, this should not happen. */ -		if (WARN_ON(!resp)) { +		if (sdata->vif.type == NL80211_IFTYPE_AP && +		    counter_offset_presp) { +			rcu_read_lock(); +			resp = rcu_dereference(sdata->u.ap.probe_resp); + +			/* If nl80211 accepted the offset, this should +			 * not happen. +			 */ +			if (WARN_ON(!resp)) { +				rcu_read_unlock(); +				return; +			} +			resp->data[counter_offset_presp] = count;  			rcu_read_unlock(); -			return;  		} -		resp->data[counter_offset_presp]--; -		rcu_read_unlock();  	}  } +u8 ieee80211_csa_update_counter(struct ieee80211_vif *vif) +{ +	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + +	sdata->csa_current_counter--; + +	/* the counter should never reach 0 */ +	WARN_ON(!sdata->csa_current_counter); + +	return sdata->csa_current_counter; +} +EXPORT_SYMBOL(ieee80211_csa_update_counter); +  bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)  {  	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);  	struct beacon_data *beacon = NULL;  	u8 *beacon_data;  	size_t beacon_data_len; -	int counter_beacon = sdata->csa_counter_offset_beacon; +	int counter_beacon = sdata->csa_counter_offset_beacon[0];  	int ret = false;  	if (!ieee80211_sdata_running(sdata)) @@ -2400,6 +2506,24 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)  			goto out;  		beacon_data = beacon->tail;  		beacon_data_len = beacon->tail_len; +	} else if (vif->type == NL80211_IFTYPE_ADHOC) { +		struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; + +		beacon = rcu_dereference(ifibss->presp); +		if (!beacon) +			goto out; + +		beacon_data = beacon->head; +		beacon_data_len = beacon->head_len; +	} else if (vif->type == NL80211_IFTYPE_MESH_POINT) { +		struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + +		beacon = rcu_dereference(ifmsh->beacon); +		if (!beacon) +			goto out; + +		beacon_data = beacon->head; +		beacon_data_len = beacon->head_len;  	} else {  		WARN_ON(1);  		goto out; @@ -2408,7 +2532,7 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)  	if (WARN_ON(counter_beacon > beacon_data_len))  		goto out; -	if (beacon_data[counter_beacon] == 0) +	if (beacon_data[counter_beacon] == 1)  		ret = true;   out:  	rcu_read_unlock(); @@ -2417,9 +2541,11 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)  }  EXPORT_SYMBOL(ieee80211_csa_is_complete); -struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, -					 struct ieee80211_vif *vif, -					 u16 *tim_offset, u16 *tim_length) +static struct sk_buff * +__ieee80211_beacon_get(struct ieee80211_hw *hw, +		       struct ieee80211_vif *vif, +		       struct ieee80211_mutable_offsets *offs, +		       bool is_template)  {  	struct ieee80211_local *local = hw_to_local(hw);  	struct sk_buff *skb = NULL; @@ -2428,6 +2554,7 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,  	enum ieee80211_band band;  	struct ieee80211_tx_rate_control txrc;  	struct ieee80211_chanctx_conf *chanctx_conf; +	int csa_off_base = 0;  	rcu_read_lock(); @@ -2437,18 +2564,20 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,  	if (!ieee80211_sdata_running(sdata) || !chanctx_conf)  		goto out; -	if (tim_offset) -		*tim_offset = 0; -	if (tim_length) -		*tim_length = 0; +	if (offs) +		memset(offs, 0, sizeof(*offs));  	if (sdata->vif.type == NL80211_IFTYPE_AP) {  		struct ieee80211_if_ap *ap = &sdata->u.ap;  		struct beacon_data *beacon = rcu_dereference(ap->beacon);  		if (beacon) { -			if (sdata->vif.csa_active) -				ieee80211_update_csa(sdata, beacon); +			if (sdata->vif.csa_active) { +				if (!is_template) +					ieee80211_csa_update_counter(vif); + +				ieee80211_set_csa(sdata, beacon); +			}  			/*  			 * headroom, head length, @@ -2456,7 +2585,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,  			 */  			skb = dev_alloc_skb(local->tx_headroom +  					    beacon->head_len + -					    beacon->tail_len + 256); +					    beacon->tail_len + 256 + +					    local->hw.extra_beacon_tailroom);  			if (!skb)  				goto out; @@ -2464,12 +2594,16 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,  			memcpy(skb_put(skb, beacon->head_len), beacon->head,  			       beacon->head_len); -			ieee80211_beacon_add_tim(sdata, &ap->ps, skb); +			ieee80211_beacon_add_tim(sdata, &ap->ps, skb, +						 is_template); + +			if (offs) { +				offs->tim_offset = beacon->head_len; +				offs->tim_length = skb->len - beacon->head_len; -			if (tim_offset) -				*tim_offset = beacon->head_len; -			if (tim_length) -				*tim_length = skb->len - beacon->head_len; +				/* for AP the csa offsets are from tail */ +				csa_off_base = skb->len; +			}  			if (beacon->tail)  				memcpy(skb_put(skb, beacon->tail_len), @@ -2484,7 +2618,15 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,  		if (!presp)  			goto out; -		skb = dev_alloc_skb(local->tx_headroom + presp->head_len); +		if (sdata->vif.csa_active) { +			if (!is_template) +				ieee80211_csa_update_counter(vif); + +			ieee80211_set_csa(sdata, presp); +		} + +		skb = dev_alloc_skb(local->tx_headroom + presp->head_len + +				    local->hw.extra_beacon_tailroom);  		if (!skb)  			goto out;  		skb_reserve(skb, local->tx_headroom); @@ -2501,25 +2643,57 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,  		if (!bcn)  			goto out; +		if (sdata->vif.csa_active) { +			if (!is_template) +				/* TODO: For mesh csa_counter is in TU, so +				 * decrementing it by one isn't correct, but +				 * for now we leave it consistent with overall +				 * mac80211's behavior. +				 */ +				ieee80211_csa_update_counter(vif); + +			ieee80211_set_csa(sdata, bcn); +		} +  		if (ifmsh->sync_ops) -			ifmsh->sync_ops->adjust_tbtt( -						sdata); +			ifmsh->sync_ops->adjust_tbtt(sdata, bcn);  		skb = dev_alloc_skb(local->tx_headroom +  				    bcn->head_len +  				    256 + /* TIM IE */ -				    bcn->tail_len); +				    bcn->tail_len + +				    local->hw.extra_beacon_tailroom);  		if (!skb)  			goto out;  		skb_reserve(skb, local->tx_headroom);  		memcpy(skb_put(skb, bcn->head_len), bcn->head, bcn->head_len); -		ieee80211_beacon_add_tim(sdata, &ifmsh->ps, skb); +		ieee80211_beacon_add_tim(sdata, &ifmsh->ps, skb, is_template); + +		if (offs) { +			offs->tim_offset = bcn->head_len; +			offs->tim_length = skb->len - bcn->head_len; +		} +  		memcpy(skb_put(skb, bcn->tail_len), bcn->tail, bcn->tail_len);  	} else {  		WARN_ON(1);  		goto out;  	} +	/* CSA offsets */ +	if (offs) { +		int i; + +		for (i = 0; i < IEEE80211_MAX_CSA_COUNTERS_NUM; i++) { +			u16 csa_off = sdata->csa_counter_offset_beacon[i]; + +			if (!csa_off) +				continue; + +			offs->csa_counter_offs[i] = csa_off_base + csa_off; +		} +	} +  	band = chanctx_conf->def.chan->band;  	info = IEEE80211_SKB_CB(skb); @@ -2550,6 +2724,32 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,   out:  	rcu_read_unlock();  	return skb; + +} + +struct sk_buff * +ieee80211_beacon_get_template(struct ieee80211_hw *hw, +			      struct ieee80211_vif *vif, +			      struct ieee80211_mutable_offsets *offs) +{ +	return __ieee80211_beacon_get(hw, vif, offs, true); +} +EXPORT_SYMBOL(ieee80211_beacon_get_template); + +struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, +					 struct ieee80211_vif *vif, +					 u16 *tim_offset, u16 *tim_length) +{ +	struct ieee80211_mutable_offsets offs = {}; +	struct sk_buff *bcn = __ieee80211_beacon_get(hw, vif, &offs, false); + +	if (tim_offset) +		*tim_offset = offs.tim_offset; + +	if (tim_length) +		*tim_length = offs.tim_length; + +	return bcn;  }  EXPORT_SYMBOL(ieee80211_beacon_get_tim); @@ -2787,7 +2987,7 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,  				cpu_to_le16(IEEE80211_FCTL_MOREDATA);  		} -		if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) +		if (sdata->vif.type == NL80211_IFTYPE_AP)  			sdata = IEEE80211_DEV_TO_SUB_IF(skb->dev);  		if (!ieee80211_tx_prepare(sdata, &tx, skb))  			break;  | 
