linux-wireless.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC 0/5] mac80211 AP-side uAPSD support
@ 2011-09-06 20:27 Johannes Berg
  2011-09-06 20:27 ` [RFC 1/5] mac80211: split PS buffers into ACs Johannes Berg
                   ` (4 more replies)
  0 siblings, 5 replies; 8+ messages in thread
From: Johannes Berg @ 2011-09-06 20:27 UTC (permalink / raw)
  To: linux-wireless

Ok, here's a braindump in the form of patches for today.

Basically, adding uAPSD support has two difficulties:
 1) we need to split the TX PS buffering into ACs
 2) we need to appropriately open an SP when the peer
    asks for it

Both are complicated by the fact that drivers might
buffer packets, e.g. ath9k on its software aggregation
queues and iwlwifi on its HW queues. This warrants a
separate patch in this set, but the drivers will have
to follow and implement the new API properly.

Also included is another fix, and a patch to notify
mac80211 about the end of the SP (EOSP) "out of band",
i.e. not with a TX status notification. I don't think
this will be used by ath9k since it can add the EOSP
status flag to the right packet when adding the EOSP
bit in the QoS header, but iwlwifi will probably need
this -- I'm not quite sure yet.

Please comment!

NB: I wouldn't try to run this if I were you, unless
you're interested in adding support to a driver. Since
I don't have driver support for any of it yet I haven't
yet tested this at all. It compiles and seems to make
sense ;-)

johannes


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [RFC 1/5] mac80211: split PS buffers into ACs
  2011-09-06 20:27 [RFC 0/5] mac80211 AP-side uAPSD support Johannes Berg
@ 2011-09-06 20:27 ` Johannes Berg
  2011-09-06 20:27 ` [RFC 2/5] mac80211: allow releasing driver-buffered frames Johannes Berg
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 8+ messages in thread
From: Johannes Berg @ 2011-09-06 20:27 UTC (permalink / raw)
  To: linux-wireless

From: Johannes Berg <johannes.berg@intel.com>

For uAPSD support we'll need to have per-AC PS
buffers. As this is a major undertaking, split
the buffers before really adding support for
uAPSD. This already makes some reference to the
uapsd_queues variable, but for now that will
never be non-zero.

Since book-keeping is complicated, also change
the logic for keeping a maximum of frames only
and allow 64 frames per AC (up from 128 for a
station).

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 include/net/mac80211.h     |    1 
 net/mac80211/debugfs_sta.c |   10 +-
 net/mac80211/sta_info.c    |  191 ++++++++++++++++++++++++++++++++++-----------
 net/mac80211/sta_info.h    |   23 ++---
 net/mac80211/status.c      |   17 +++-
 net/mac80211/tx.c          |   42 +++++----
 6 files changed, 204 insertions(+), 80 deletions(-)

--- a/net/mac80211/sta_info.c	2011-09-06 16:48:40.000000000 +0200
+++ b/net/mac80211/sta_info.c	2011-09-06 22:25:36.000000000 +0200
@@ -309,8 +309,10 @@ struct sta_info *sta_info_alloc(struct i
 		 */
 		sta->timer_to_tid[i] = i;
 	}
-	skb_queue_head_init(&sta->ps_tx_buf);
-	skb_queue_head_init(&sta->tx_filtered);
+	for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+		skb_queue_head_init(&sta->ps_tx_buf[i]);
+		skb_queue_head_init(&sta->tx_filtered[i]);
+	}
 
 	for (i = 0; i < NUM_RX_DATA_QUEUES; i++)
 		sta->last_seq_ctrl[i] = cpu_to_le16(USHRT_MAX);
@@ -641,31 +643,77 @@ static inline void __bss_tim_clear(struc
 	bss->tim[aid / 8] &= ~(1 << (aid % 8));
 }
 
+static void ieee80211_tids_for_ac(int ac, int *tid1, int *tid2)
+{
+	/* If we ever support TIDs > 7, this obviously needs to be adjusted */
+	switch (ac) {
+	case IEEE80211_AC_VO:
+		*tid1 = 6;
+		*tid2 = 7;
+		break;
+	case IEEE80211_AC_VI:
+		*tid1 = 4;
+		*tid2 = 5;
+		break;
+	case IEEE80211_AC_BE:
+		*tid1 = 0;
+		*tid2 = 3;
+		break;
+	case IEEE80211_AC_BK:
+		*tid1 = 1;
+		*tid2 = 2;
+		break;
+	default:
+		WARN_ON(1);
+	}
+}
+
 void sta_info_recalc_tim(struct sta_info *sta)
 {
 	struct ieee80211_local *local = sta->local;
 	struct ieee80211_if_ap *bss = sta->sdata->bss;
 	unsigned long flags;
-	bool have_data;
+	bool indicate_tim = false;
+	u8 ignore_for_tim = sta->sta.uapsd_queues;
+	int ac;
 
 	/* No need to do anything if the driver does all */
 	if (local->hw.flags & IEEE80211_HW_AP_LINK_PS)
 		return;
 
-	have_data = test_sta_flags(sta, WLAN_STA_PS_DRIVER_BUF) ||
-		    !skb_queue_empty(&sta->tx_filtered) ||
-		    !skb_queue_empty(&sta->ps_tx_buf);
+	/*
+	 * If all ACs are delivery-enabled then we should build
+	 * the TIM bit for all ACs anyway; if only some are then
+	 * we ignore those and build the TIM bit using only the
+	 * non-enabled ones.
+	 */
+	if (ignore_for_tim == BIT(IEEE80211_NUM_ACS) - 1)
+		ignore_for_tim = 0;
+
+	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+		int tid1, tid2;
+
+		if (ignore_for_tim & BIT(ac))
+			continue;
+
+		ieee80211_tids_for_ac(ac, &tid1, &tid2);
+
+		indicate_tim |= test_bit(tid1, sta->driver_buffered_tids) ||
+				test_bit(tid2, sta->driver_buffered_tids) ||
+				!skb_queue_empty(&sta->tx_filtered[ac]) ||
+				!skb_queue_empty(&sta->ps_tx_buf[ac]);
+	}
 
 	spin_lock_irqsave(&local->sta_lock, flags);
 
-	if (have_data)
+	if (indicate_tim)
 		__bss_tim_set(bss, sta->sta.aid);
 	else
 		__bss_tim_clear(bss, sta->sta.aid);
 
 	if (local->ops->set_tim) {
 		local->tim_in_locked_section = true;
-		drv_set_tim(local, &sta->sta, have_data);
+		drv_set_tim(local, &sta->sta, indicate_tim);
 		local->tim_in_locked_section = false;
 	}
 
@@ -692,8 +740,8 @@ static bool sta_info_buffer_expired(stru
 }
 
 
-static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
-					     struct sta_info *sta)
+static bool sta_info_cleanup_expire_buffered_ac(struct ieee80211_local *local,
+						struct sta_info *sta, int ac)
 {
 	unsigned long flags;
 	struct sk_buff *skb;
@@ -706,13 +754,13 @@ static bool sta_info_cleanup_expire_buff
 	 * total_ps_buffered counter.
 	 */
 	for (;;) {
-		spin_lock_irqsave(&sta->tx_filtered.lock, flags);
-		skb = skb_peek(&sta->tx_filtered);
+		spin_lock_irqsave(&sta->tx_filtered[ac].lock, flags);
+		skb = skb_peek(&sta->tx_filtered[ac]);
 		if (sta_info_buffer_expired(sta, skb))
-			skb = __skb_dequeue(&sta->tx_filtered);
+			skb = __skb_dequeue(&sta->tx_filtered[ac]);
 		else
 			skb = NULL;
-		spin_unlock_irqrestore(&sta->tx_filtered.lock, flags);
+		spin_unlock_irqrestore(&sta->tx_filtered[ac].lock, flags);
 
 		/*
 		 * Frames are queued in order, so if this one
@@ -732,13 +780,13 @@ static bool sta_info_cleanup_expire_buff
 	 * buffered frames.
 	 */
 	for (;;) {
-		spin_lock_irqsave(&sta->ps_tx_buf.lock, flags);
-		skb = skb_peek(&sta->ps_tx_buf);
+		spin_lock_irqsave(&sta->ps_tx_buf[ac].lock, flags);
+		skb = skb_peek(&sta->ps_tx_buf[ac]);
 		if (sta_info_buffer_expired(sta, skb))
-			skb = __skb_dequeue(&sta->ps_tx_buf);
+			skb = __skb_dequeue(&sta->ps_tx_buf[ac]);
 		else
 			skb = NULL;
-		spin_unlock_irqrestore(&sta->ps_tx_buf.lock, flags);
+		spin_unlock_irqrestore(&sta->ps_tx_buf[ac].lock, flags);
 
 		/*
 		 * frames are queued in order, so if this one
@@ -768,8 +816,22 @@ static bool sta_info_cleanup_expire_buff
 	 * used to check whether the cleanup timer still needs to run,
 	 * if there are no frames we don't need to rearm the timer.
 	 */
-	return !(skb_queue_empty(&sta->ps_tx_buf) &&
-		 skb_queue_empty(&sta->tx_filtered));
+	return !(skb_queue_empty(&sta->ps_tx_buf[ac]) &&
+		 skb_queue_empty(&sta->tx_filtered[ac]));
+}
+
+static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
+					     struct sta_info *sta)
+{
+	bool have_buffered = false;
+	int ac;
+
+	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+		have_buffered |=
+			sta_info_cleanup_expire_buffered_ac(local, sta, ac);
+	}
+
+	return have_buffered;
 }
 
 static int __must_check __sta_info_destroy(struct sta_info *sta)
@@ -777,7 +839,7 @@ static int __must_check __sta_info_destr
 	struct ieee80211_local *local;
 	struct ieee80211_sub_if_data *sdata;
 	unsigned long flags;
-	int ret, i;
+	int ret, i, ac;
 
 	might_sleep();
 
@@ -814,9 +876,11 @@ static int __must_check __sta_info_destr
 
 	sta->dead = true;
 
-	local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf);
-	__skb_queue_purge(&sta->ps_tx_buf);
-	__skb_queue_purge(&sta->tx_filtered);
+	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+		local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]);
+		__skb_queue_purge(&sta->ps_tx_buf[ac]);
+		__skb_queue_purge(&sta->tx_filtered[ac]);
+	}
 
 	if (test_and_clear_sta_flags(sta,
 				WLAN_STA_PS_STA | WLAN_STA_PS_DRIVER)) {
@@ -1044,18 +1108,24 @@ void ieee80211_sta_ps_deliver_wakeup(str
 {
 	struct ieee80211_sub_if_data *sdata = sta->sdata;
 	struct ieee80211_local *local = sdata->local;
-	int sent, buffered;
+	int sent, buffered, ac;
+
+	BUILD_BUG_ON(sizeof(sta->driver_buffered_tids) != sizeof(unsigned long));
+	sta->driver_buffered_tids[0] = 0;
 
-	clear_sta_flags(sta, WLAN_STA_PS_DRIVER_BUF);
 	if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS))
 		drv_sta_notify(local, sdata, STA_NOTIFY_AWAKE, &sta->sta);
 
 	/* Send all buffered frames to the station */
-	sent = ieee80211_add_pending_skbs(local, &sta->tx_filtered);
-	buffered = ieee80211_add_pending_skbs_fn(local, &sta->ps_tx_buf,
-						 clear_sta_ps_flags, sta);
-	sent += buffered;
-	local->total_ps_buffered -= buffered;
+	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+		sent = ieee80211_add_pending_skbs(local, &sta->tx_filtered[ac]);
+		buffered = ieee80211_add_pending_skbs_fn(local,
+							 &sta->ps_tx_buf[ac],
+							 clear_sta_ps_flags,
+							 sta);
+		sent += buffered;
+		local->total_ps_buffered -= buffered;
+	}
 
 	sta_info_recalc_tim(sta);
 
@@ -1070,17 +1140,43 @@ void ieee80211_sta_ps_deliver_poll_respo
 {
 	struct ieee80211_sub_if_data *sdata = sta->sdata;
 	struct ieee80211_local *local = sdata->local;
-	struct sk_buff *skb;
-	int no_pending_pkts;
+	struct sk_buff *skb = NULL;
+	bool more_data = false;
+	int ac;
+	u8 ignore_for_response = sta->sta.uapsd_queues;
+
+	/*
+	 * If all ACs are delivery-enabled then we should reply
+	 * from any of them, if only some are enabled we reply
+	 * only from the non-enabled ones.
+	 */
+	if (ignore_for_response == BIT(IEEE80211_NUM_ACS) - 1)
+		ignore_for_response = 0;
+
+	/*
+	 * Get response frame and more data bit for it.
+	 */
+	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+		if (ignore_for_response & BIT(ac))
+			continue;
+
+		if (!skb) {
+			skb = skb_dequeue(&sta->tx_filtered[ac]);
+			if (!skb) {
+				skb = skb_dequeue(&sta->ps_tx_buf[ac]);
+				if (skb)
+					local->total_ps_buffered--;
+			}
+		}
 
-	skb = skb_dequeue(&sta->tx_filtered);
-	if (!skb) {
-		skb = skb_dequeue(&sta->ps_tx_buf);
-		if (skb)
-			local->total_ps_buffered--;
+		/* FIXME: take into account driver-buffered frames */
+
+		if (!skb_queue_empty(&sta->tx_filtered[ac]) ||
+		    !skb_queue_empty(&sta->ps_tx_buf[ac])) {
+			more_data = true;
+			break;
+		}
 	}
-	no_pending_pkts = skb_queue_empty(&sta->tx_filtered) &&
-		skb_queue_empty(&sta->ps_tx_buf);
 
 	if (skb) {
 		struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
@@ -1094,14 +1190,13 @@ void ieee80211_sta_ps_deliver_poll_respo
 		info->flags |= IEEE80211_TX_CTL_PSPOLL_RESPONSE;
 
 #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
-		printk(KERN_DEBUG "STA %pM aid %d: PS Poll (entries after %d)\n",
-		       sta->sta.addr, sta->sta.aid,
-		       skb_queue_len(&sta->ps_tx_buf));
+		printk(KERN_DEBUG "STA %pM aid %d: PS Poll\n",
+		       sta->sta.addr, sta->sta.aid);
 #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
 
 		/* Use MoreData flag to indicate whether there are more
 		 * buffered frames for this STA */
-		if (no_pending_pkts)
+		if (!more_data)
 			hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREDATA);
 		else
 			hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);
@@ -1143,10 +1238,14 @@ void ieee80211_sta_set_buffered(struct i
 {
 	struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
 
-	if (!buffered)
+	if (WARN_ON(tid >= STA_TID_NUM))
 		return;
 
-	set_sta_flags(sta, WLAN_STA_PS_DRIVER_BUF);
+	if (buffered)
+		set_bit(tid, sta->driver_buffered_tids);
+	else
+		clear_bit(tid, sta->driver_buffered_tids);
+
 	sta_info_recalc_tim(sta);
 }
 EXPORT_SYMBOL(ieee80211_sta_set_buffered);
--- a/net/mac80211/sta_info.h	2011-09-06 16:48:39.000000000 +0200
+++ b/net/mac80211/sta_info.h	2011-09-06 22:25:35.000000000 +0200
@@ -43,8 +43,6 @@
  *	be in the queues
  * @WLAN_STA_PSPOLL: Station sent PS-poll while driver was keeping
  *	station in power-save mode, reply when the driver unblocks.
- * @WLAN_STA_PS_DRIVER_BUF: Station has frames pending in driver internal
- *	buffers. Automatically cleared on station wake-up.
  */
 enum ieee80211_sta_info_flags {
 	WLAN_STA_AUTH		= 1<<0,
@@ -60,7 +58,6 @@ enum ieee80211_sta_info_flags {
 	WLAN_STA_BLOCK_BA	= 1<<11,
 	WLAN_STA_PS_DRIVER	= 1<<12,
 	WLAN_STA_PSPOLL		= 1<<13,
-	WLAN_STA_PS_DRIVER_BUF	= 1<<14,
 };
 
 #define STA_TID_NUM 16
@@ -202,11 +199,12 @@ struct sta_ampdu_mlme {
  * @drv_unblock_wk: used for driver PS unblocking
  * @listen_interval: listen interval of this station, when we're acting as AP
  * @flags: STA flags, see &enum ieee80211_sta_info_flags
- * @ps_tx_buf: buffer of frames to transmit to this station
- *	when it leaves power saving state
- * @tx_filtered: buffer of frames we already tried to transmit
- *	but were filtered by hardware due to STA having entered
- *	power saving state
+ * @ps_tx_buf: buffers (per AC) of frames to transmit to this station
+ *	when it leaves power saving state or polls
+ * @tx_filtered: buffers (per AC) of frames we already tried to
+ *	transmit but were filtered by hardware due to STA having
+ *	entered power saving state, these are also delivered to
+ *	the station when it leaves powersave or polls for frames
  * @rx_packets: Number of MSDUs received from this STA
  * @rx_bytes: Number of bytes received from this STA
  * @wep_weak_iv_count: number of weak WEP IVs received from this station
@@ -276,8 +274,9 @@ struct sta_info {
 	 * STA powersave frame queues, no more than the internal
 	 * locking required.
 	 */
-	struct sk_buff_head ps_tx_buf;
-	struct sk_buff_head tx_filtered;
+	struct sk_buff_head ps_tx_buf[IEEE80211_NUM_ACS];
+	struct sk_buff_head tx_filtered[IEEE80211_NUM_ACS];
+	unsigned long driver_buffered_tids[BITS_TO_LONGS(STA_TID_NUM)];
 
 	/* Updated from RX path only, no locking requirements */
 	unsigned long rx_packets, rx_bytes;
@@ -424,8 +423,8 @@ rcu_dereference_protected_tid_tx(struct
 #define STA_HASH(sta) (sta[5])
 
 
-/* Maximum number of frames to buffer per power saving station */
-#define STA_MAX_TX_BUFFER 128
+/* Maximum number of frames to buffer per power saving station per AC */
+#define STA_MAX_TX_BUFFER	64
 
 /* Minimum buffered frame expiry time. If STA uses listen interval that is
  * smaller than this value, the minimum value here is used instead. */
--- a/include/net/mac80211.h	2011-09-06 16:48:38.000000000 +0200
+++ b/include/net/mac80211.h	2011-09-06 22:25:36.000000000 +0200
@@ -109,6 +109,7 @@ enum ieee80211_ac_numbers {
 	IEEE80211_AC_BE		= 2,
 	IEEE80211_AC_BK		= 3,
 };
+#define IEEE80211_NUM_ACS	4
 
 /**
  * struct ieee80211_tx_queue_params - transmit queue configuration
--- a/net/mac80211/status.c	2011-09-06 16:48:40.000000000 +0200
+++ b/net/mac80211/status.c	2011-09-06 22:25:35.000000000 +0200
@@ -14,6 +14,7 @@
 #include "rate.h"
 #include "mesh.h"
 #include "led.h"
+#include "wme.h"
 
 
 void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw,
@@ -43,6 +44,8 @@ static void ieee80211_handle_filtered_fr
 					    struct sk_buff *skb)
 {
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_hdr *hdr = (void *)skb->data;
+	int ac;
 
 	/*
 	 * This skb 'survived' a round-trip through the driver, and
@@ -62,6 +65,14 @@ static void ieee80211_handle_filtered_fr
 
 	sta->tx_filtered_count++;
 
+	if (ieee80211_is_data_qos(hdr->frame_control)) {
+		int tid = *ieee80211_get_qos_ctl(hdr) &
+					IEEE80211_QOS_CTL_TID_MASK;
+		ac = ieee802_1d_to_ac[tid & 7];
+	} else {
+		ac = IEEE80211_AC_BE;
+	}
+
 	/*
 	 * Clear the TX filter mask for this STA when sending the next
 	 * packet. If the STA went to power save mode, this will happen
@@ -104,8 +115,8 @@ static void ieee80211_handle_filtered_fr
 	 *	unknown.
 	 */
 	if (test_sta_flags(sta, WLAN_STA_PS_STA) &&
-	    skb_queue_len(&sta->tx_filtered) < STA_MAX_TX_BUFFER) {
-		skb_queue_tail(&sta->tx_filtered, skb);
+	    skb_queue_len(&sta->tx_filtered[ac]) < STA_MAX_TX_BUFFER) {
+		skb_queue_tail(&sta->tx_filtered[ac], skb);
 		sta_info_recalc_tim(sta);
 
 		if (!timer_pending(&local->sta_cleanup))
@@ -127,7 +138,7 @@ static void ieee80211_handle_filtered_fr
 	if (net_ratelimit())
 		wiphy_debug(local->hw.wiphy,
 			    "dropped TX filtered frame, queue_len=%d PS=%d @%lu\n",
-			    skb_queue_len(&sta->tx_filtered),
+			    skb_queue_len(&sta->tx_filtered[ac]),
 			    !!test_sta_flags(sta, WLAN_STA_PS_STA), jiffies);
 #endif
 	dev_kfree_skb(skb);
--- a/net/mac80211/debugfs_sta.c	2011-09-06 16:48:05.000000000 +0200
+++ b/net/mac80211/debugfs_sta.c	2011-09-06 16:48:45.000000000 +0200
@@ -78,8 +78,14 @@ static ssize_t sta_num_ps_buf_frames_rea
 					  size_t count, loff_t *ppos)
 {
 	struct sta_info *sta = file->private_data;
-	return mac80211_format_buffer(userbuf, count, ppos, "%u\n",
-				      skb_queue_len(&sta->ps_tx_buf));
+	char buf[17*IEEE80211_NUM_ACS], *p = buf;
+	int ac;
+
+	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+		p += scnprintf(p, sizeof(buf)+buf-p, "AC%d: %d\n", ac,
+			       skb_queue_len(&sta->ps_tx_buf[ac]) +
+			       skb_queue_len(&sta->tx_filtered[ac]));
+	return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
 }
 STA_OPS(num_ps_buf_frames);
 
--- a/net/mac80211/tx.c	2011-09-06 16:48:39.000000000 +0200
+++ b/net/mac80211/tx.c	2011-09-06 22:25:35.000000000 +0200
@@ -343,13 +343,22 @@ static void purge_old_ps_buffers(struct
 		total += skb_queue_len(&ap->ps_bc_buf);
 	}
 
+	/*
+	 * Drop one frame from each station from the lowest-priority
+	 * AC that has frames at all.
+	 */
 	list_for_each_entry_rcu(sta, &local->sta_list, list) {
-		skb = skb_dequeue(&sta->ps_tx_buf);
-		if (skb) {
-			purged++;
-			dev_kfree_skb(skb);
+		int ac;
+
+		for (ac = IEEE80211_AC_BK; ac >= IEEE80211_AC_VO; ac--) {
+			skb = skb_dequeue(&sta->ps_tx_buf[ac]);
+			total += skb_queue_len(&sta->ps_tx_buf[ac]);
+			if (skb) {
+				purged++;
+				dev_kfree_skb(skb);
+				break;
+			}
 		}
-		total += skb_queue_len(&sta->ps_tx_buf);
 	}
 
 	rcu_read_unlock();
@@ -448,22 +457,21 @@ ieee80211_tx_h_unicast_ps_buf(struct iee
 
 	if (unlikely((staflags & (WLAN_STA_PS_STA | WLAN_STA_PS_DRIVER)) &&
 		     !(info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE))) {
+		int ac = skb_get_queue_mapping(tx->skb);
+
 #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
-		printk(KERN_DEBUG "STA %pM aid %d: PS buffer (entries "
-		       "before %d)\n",
-		       sta->sta.addr, sta->sta.aid,
-		       skb_queue_len(&sta->ps_tx_buf));
+		printk(KERN_DEBUG "STA %pM aid %d: PS buffer for AC %d\n",
+		       sta->sta.addr, sta->sta.aid, ac);
 #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
 		if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER)
 			purge_old_ps_buffers(tx->local);
-		if (skb_queue_len(&sta->ps_tx_buf) >= STA_MAX_TX_BUFFER) {
-			struct sk_buff *old = skb_dequeue(&sta->ps_tx_buf);
+		if (skb_queue_len(&sta->ps_tx_buf[ac]) >= STA_MAX_TX_BUFFER) {
+			struct sk_buff *old = skb_dequeue(&sta->ps_tx_buf[ac]);
 #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
-			if (net_ratelimit()) {
-				printk(KERN_DEBUG "%s: STA %pM TX "
-				       "buffer full - dropping oldest frame\n",
-				       tx->sdata->name, sta->sta.addr);
-			}
+			if (net_ratelimit())
+				printk(KERN_DEBUG "%s: STA %pM TX buffer for "
+				       "AC %d full - dropping oldest frame\n",
+				       tx->sdata->name, sta->sta.addr, ac);
 #endif
 			dev_kfree_skb(old);
 		} else
@@ -472,7 +480,7 @@ ieee80211_tx_h_unicast_ps_buf(struct iee
 		info->control.jiffies = jiffies;
 		info->control.vif = &tx->sdata->vif;
 		info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
-		skb_queue_tail(&sta->ps_tx_buf, tx->skb);
+		skb_queue_tail(&sta->ps_tx_buf[ac], tx->skb);
 
 		if (!timer_pending(&local->sta_cleanup))
 			mod_timer(&local->sta_cleanup,



^ permalink raw reply	[flat|nested] 8+ messages in thread

* [RFC 2/5] mac80211: allow releasing driver-buffered frames
  2011-09-06 20:27 [RFC 0/5] mac80211 AP-side uAPSD support Johannes Berg
  2011-09-06 20:27 ` [RFC 1/5] mac80211: split PS buffers into ACs Johannes Berg
@ 2011-09-06 20:27 ` Johannes Berg
  2011-09-06 20:27 ` [RFC 3/5] mac80211: implement uAPSD Johannes Berg
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 8+ messages in thread
From: Johannes Berg @ 2011-09-06 20:27 UTC (permalink / raw)
  To: linux-wireless

From: Johannes Berg <johannes.berg@intel.com>

If there are frames for a station buffered in
the driver, mac80211 announces those in the TIM
IE but there's no way to release them. Add new
API to release such frames and use it when the
station polls for a frame.

Since the API will soon also be used for uAPSD
it is easily extensible.

Note that before this change drivers announcing
driver-buffered frames in the TIM bit actually
will respond to a PS-Poll with a potentially
lower priority frame (if there are any frames
buffered in mac80211), after this patch a driver
that hasn't been changed will no longer respond
at all. This only affects ath9k, which will need
to be fixed to implement the new API.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 include/net/mac80211.h      |   29 +++++++++++++
 net/mac80211/driver-ops.h   |   15 ++++++
 net/mac80211/driver-trace.h |   34 +++++++++++++++
 net/mac80211/sta_info.c     |   97 +++++++++++++++++++++++++++++++++++---------
 4 files changed, 157 insertions(+), 18 deletions(-)

--- a/net/mac80211/sta_info.c	2011-09-06 17:24:37.000000000 +0200
+++ b/net/mac80211/sta_info.c	2011-09-06 17:45:22.000000000 +0200
@@ -1141,8 +1141,9 @@ void ieee80211_sta_ps_deliver_poll_respo
 	struct ieee80211_sub_if_data *sdata = sta->sdata;
 	struct ieee80211_local *local = sdata->local;
 	struct sk_buff *skb = NULL;
+	bool found = false;
 	bool more_data = false;
-	int ac;
+	int ac, driver_release_tid = -1;
 	u8 ignore_for_response = sta->sta.uapsd_queues;
 
 	/*
@@ -1157,27 +1158,74 @@ void ieee80211_sta_ps_deliver_poll_respo
 	 * Get response frame and more data bit for it.
 	 */
 	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+		int tid1, tid2;
+
 		if (ignore_for_response & BIT(ac))
 			continue;
 
-		if (!skb) {
-			skb = skb_dequeue(&sta->tx_filtered[ac]);
-			if (!skb) {
-				skb = skb_dequeue(&sta->ps_tx_buf[ac]);
+		ieee80211_tids_for_ac(ac, &tid1, &tid2);
+
+		if (!found) {
+			if (test_bit(tid2, sta->driver_buffered_tids)) {
+				found = true;
+				driver_release_tid = tid2;
+			} else if (test_bit(tid1, sta->driver_buffered_tids)) {
+				found = true;
+				driver_release_tid = tid1;
+			} else {
+				skb = skb_dequeue(&sta->tx_filtered[ac]);
+				if (!skb) {
+					skb = skb_dequeue(&sta->ps_tx_buf[ac]);
+					if (skb)
+						local->total_ps_buffered--;
+				}
 				if (skb)
-					local->total_ps_buffered--;
+					found = true;
 			}
 		}
 
-		/* FIXME: take into account driver-buffered frames */
-
-		if (!skb_queue_empty(&sta->tx_filtered[ac]) ||
+		/*
+		 * The last two pieces are obvious: if we have more frames
+		 * buffered (remember we already did the dequeue above) on
+		 * the queues for this AC, then there's more data.
+		 *
+		 * If the driver has some buffered on the *same* TID, we
+		 * don't know if it'll have more than 1 frame, so this one
+		 * determination it'll have to make later when it sets or
+		 * clears the more-data bit. If we know that there is more
+		 * data on the *other* TID though we can save it some of
+		 * the trouble and tell it that there's more data.
+		 *
+		 * So that's how we arrive at this condition -- we check
+		 * each TID only if it's not the one we're releasing from.
+		 */
+		if ((driver_release_tid != tid1 &&
+				test_bit(tid1, sta->driver_buffered_tids)) ||
+		    (driver_release_tid != tid2 &&
+		    		test_bit(tid2, sta->driver_buffered_tids)) ||
+		    !skb_queue_empty(&sta->tx_filtered[ac]) ||
 		    !skb_queue_empty(&sta->ps_tx_buf[ac])) {
 			more_data = true;
 			break;
 		}
 	}
 
+	if (!found) {
+#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
+		/*
+		 * FIXME: This can be the result of a race condition between
+		 *	  us expiring a frame and the station polling for it.
+		 *	  Should we send it a null-func frame indicating we
+		 *	  have nothing buffered for it?
+		 */
+		printk(KERN_DEBUG "%s: STA %pM sent PS Poll even "
+		       "though there are no buffered frames for it\n",
+		       sdata->name, sta->sta.addr);
+#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
+
+		return;
+	}
+
 	if (skb) {
 		struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 		struct ieee80211_hdr *hdr =
@@ -1204,18 +1252,31 @@ void ieee80211_sta_ps_deliver_poll_respo
 		ieee80211_add_pending_skb(local, skb);
 
 		sta_info_recalc_tim(sta);
-#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
+	} else if (WARN_ON(driver_release_tid == -1)) {
+		/* err ... ??? */
 	} else {
 		/*
-		 * FIXME: This can be the result of a race condition between
-		 *	  us expiring a frame and the station polling for it.
-		 *	  Should we send it a null-func frame indicating we
-		 *	  have nothing buffered for it?
+		 * We need to release a frame that is buffered somewhere in the
+		 * driver ... it'll have to handle that.
+		 * Note that, as per the comment above, it'll also have to see
+		 * if there is more than just one frame on the specific TID that
+		 * we're releasing from, and it needs to set the more-data bit
+		 * accordingly if we tell it that there's no more data. If we do
+		 * tell it there's more data, then of course the more-data bit
+		 * needs to be set anyway.
+		 */
+		drv_release_buffered_frames(local, sta, driver_release_tid,
+					    1, IEEE80211_FRAME_RELEASE_PSPOLL,
+					    more_data);
+
+		/*
+		 * Note that we don't recalculate the TIM bit here as it would
+		 * most likely have no effect at all unless the driver told us
+		 * that the TID became empty before returning here from the
+		 * release function.
+		 * Either way, however, when the driver tells us that the TID
+		 * became empty we'll do the TIM recalculation.
 		 */
-		printk(KERN_DEBUG "%s: STA %pM sent PS Poll even "
-		       "though there are no buffered frames for it\n",
-		       sdata->name, sta->sta.addr);
-#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
 	}
 }
 
--- a/include/net/mac80211.h	2011-09-06 17:24:37.000000000 +0200
+++ b/include/net/mac80211.h	2011-09-06 17:45:46.000000000 +0200
@@ -1602,6 +1602,14 @@ enum ieee80211_tx_sync_type {
 };
 
 /**
+ * enum ieee80211_frame_release_type - frame release reason
+ * @IEEE80211_FRAME_RELEASE_PSPOLL: frame released for PS-Poll
+ */
+enum ieee80211_frame_release_type {
+	IEEE80211_FRAME_RELEASE_PSPOLL,
+};
+
+/**
  * struct ieee80211_ops - callbacks from mac80211 to the driver
  *
  * This structure contains various callbacks that the driver may
@@ -1911,6 +1919,21 @@ enum ieee80211_tx_sync_type {
  *	The callback can sleep.
  * @rssi_callback: Notify driver when the average RSSI goes above/below
  *	thresholds that were registered previously. The callback can sleep.
+ *
+ * @release_buffered_frames: Release buffered frames according to the given
+ *	parameters. In the case where the driver buffers some frames for
+ *	sleeping stations mac80211 will use this callback to tell the driver
+ *	to release some frames, either for PS-poll or uAPSD.
+ *	Note that if the @more_data paramter is %false the driver must check
+ *	if there are more frames on the given TID, and if there are more than
+ *	the frames being released then it must still set the more-data bit in
+ *	the frame. If the @more_data parameter is %true, then of course the
+ *	more-data bit must always be set.
+ *	In the case this is used for uAPSD, the @num_frames parameter may be
+ *	bigger than one, but the driver may send fewer frames (it must send
+ *	at least one, however). In this case it is also responsible for
+ *	setting the EOSP flag in the QoS header of the frames.
+ *	This callback must be atomic.
  */
 struct ieee80211_ops {
 	void (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb);
@@ -2024,6 +2047,12 @@ struct ieee80211_ops {
 				const struct cfg80211_bitrate_mask *mask);
 	void (*rssi_callback)(struct ieee80211_hw *hw,
 			      enum ieee80211_rssi_event rssi_event);
+
+	void (*release_buffered_frames)(struct ieee80211_hw *hw,
+					struct ieee80211_sta *sta,
+					int tid, int num_frames,
+					enum ieee80211_frame_release_type reason,
+					bool more_data);
 };
 
 /**
--- a/net/mac80211/driver-ops.h	2011-09-06 17:24:37.000000000 +0200
+++ b/net/mac80211/driver-ops.h	2011-09-06 17:24:50.000000000 +0200
@@ -665,4 +665,19 @@ static inline void drv_rssi_callback(str
 		local->ops->rssi_callback(&local->hw, event);
 	trace_drv_return_void(local);
 }
+
+static inline void
+drv_release_buffered_frames(struct ieee80211_local *local,
+			    struct sta_info *sta, int tid, int num_frames,
+			    enum ieee80211_frame_release_type reason,
+			    bool more_data)
+{
+	trace_drv_release_buffered_frames(local, &sta->sta, tid, num_frames,
+					  reason, more_data);
+	if (local->ops->release_buffered_frames)
+		local->ops->release_buffered_frames(&local->hw, &sta->sta, tid,
+						    num_frames, reason,
+						    more_data);
+	trace_drv_return_void(local);
+}
 #endif /* __MAC80211_DRIVER_OPS */
--- a/net/mac80211/driver-trace.h	2011-09-06 17:24:37.000000000 +0200
+++ b/net/mac80211/driver-trace.h	2011-09-06 17:24:50.000000000 +0200
@@ -1117,6 +1117,40 @@ TRACE_EVENT(drv_rssi_callback,
 	)
 );
 
+TRACE_EVENT(drv_release_buffered_frames,
+	TP_PROTO(struct ieee80211_local *local,
+		 struct ieee80211_sta *sta,
+		 int tid, int num_frames,
+		 enum ieee80211_frame_release_type reason,
+		 bool more_data),
+
+	TP_ARGS(local, sta, tid, num_frames, reason, more_data),
+
+	TP_STRUCT__entry(
+		LOCAL_ENTRY
+		STA_ENTRY
+		__field(int, tid)
+		__field(int, num_frames)
+		__field(int, reason)
+		__field(bool, more_data)
+	),
+
+	TP_fast_assign(
+		LOCAL_ASSIGN;
+		STA_ASSIGN;
+		__entry->tid = tid;
+		__entry->num_frames = num_frames;
+		__entry->reason = reason;
+		__entry->more_data = more_data;
+	),
+
+	TP_printk(
+		LOCAL_PR_FMT STA_PR_FMT " TID:%d frames:%d reason:%d more:%d",
+		LOCAL_PR_ARG, STA_PR_ARG, __entry->tid, __entry->num_frames,
+		__entry->reason, __entry->more_data
+	)
+);
+
 /*
  * Tracing for API calls that drivers call.
  */



^ permalink raw reply	[flat|nested] 8+ messages in thread

* [RFC 3/5] mac80211: implement uAPSD
  2011-09-06 20:27 [RFC 0/5] mac80211 AP-side uAPSD support Johannes Berg
  2011-09-06 20:27 ` [RFC 1/5] mac80211: split PS buffers into ACs Johannes Berg
  2011-09-06 20:27 ` [RFC 2/5] mac80211: allow releasing driver-buffered frames Johannes Berg
@ 2011-09-06 20:27 ` Johannes Berg
  2011-09-07 11:41   ` Johannes Berg
  2011-09-06 20:27 ` [RFC 4/5] mac80211: reply only once to each PS-poll Johannes Berg
  2011-09-06 20:27 ` [RFC 5/5] mac80211: allow out-of-band EOSP notification Johannes Berg
  4 siblings, 1 reply; 8+ messages in thread
From: Johannes Berg @ 2011-09-06 20:27 UTC (permalink / raw)
  To: linux-wireless

From: Johannes Berg <johannes.berg@intel.com>

Add uAPSD support to mac80211. This is probably not
possible with all devices, so advertising it with
the cfg80211 flag will be left up to drivers that
want it.

Due to my previous patches it is now a fairly
straight-forward extension. Drivers need to have
accurate TX status reporting for the EOSP frame.
For drivers that buffer themselves, the provided
APIs allow releasing the right number of frames,
but then drivers need to set EOSP and more-data
themselves. This is documented in more detail in
the new code itself.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 drivers/net/wireless/iwlegacy/iwl-4965-tx.c |    2 
 drivers/net/wireless/iwlwifi/iwl-agn-tx.c   |    2 
 drivers/net/wireless/p54/txrx.c             |    2 
 include/net/mac80211.h                      |   22 ++-
 net/mac80211/rx.c                           |   91 +++++++++++-----
 net/mac80211/sta_info.c                     |  155 +++++++++++++++++++---------
 net/mac80211/sta_info.h                     |    8 +
 net/mac80211/status.c                       |    3 
 net/mac80211/tx.c                           |    2 
 9 files changed, 201 insertions(+), 86 deletions(-)

--- a/net/mac80211/rx.c	2011-09-06 21:40:08.000000000 +0200
+++ b/net/mac80211/rx.c	2011-09-06 22:05:21.000000000 +0200
@@ -1172,6 +1172,68 @@ int ieee80211_sta_ps_transition(struct i
 EXPORT_SYMBOL(ieee80211_sta_ps_transition);
 
 static ieee80211_rx_result debug_noinline
+ieee80211_rx_h_uapsd_and_pspoll(struct ieee80211_rx_data *rx)
+{
+	struct ieee80211_sub_if_data *sdata = rx->sdata;
+	struct ieee80211_hdr *hdr = (void *)rx->skb->data;
+	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
+	int tid, ac;
+
+	if (!rx->sta || !(status->rx_flags & IEEE80211_RX_RA_MATCH))
+		return RX_CONTINUE;
+
+	if (sdata->vif.type != NL80211_IFTYPE_AP &&
+	    sdata->vif.type != NL80211_IFTYPE_AP_VLAN)
+		return RX_CONTINUE;
+
+	/*
+	 * The device handles station powersave, so don't do anything about
+	 * uAPSD and PS-Poll frames (the latter shouldn't even come up from
+	 * it to mac80211 since they're handled.)
+	 */
+	if (sdata->local->hw.flags & IEEE80211_HW_AP_LINK_PS)
+		return RX_CONTINUE;
+
+	if (unlikely(ieee80211_is_pspoll(hdr->frame_control))) {
+		if (!test_sta_flags(rx->sta, WLAN_STA_PS_DRIVER))
+			ieee80211_sta_ps_deliver_poll_response(rx->sta);
+		else
+			set_sta_flags(rx->sta, WLAN_STA_PSPOLL);
+
+		/* Free PS Poll skb here instead of returning RX_DROP that would
+		 * count as an dropped frame. */
+		dev_kfree_skb(rx->skb);
+
+		return RX_QUEUED;
+	} else if (ieee80211_is_data_qos(hdr->frame_control) ||
+		   ieee80211_is_qos_nullfunc(hdr->frame_control)) {
+		tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
+		ac = ieee802_1d_to_ac[tid & 7];
+
+		/*
+		 * If this AC is not trigger-enabled do nothing.
+		 *
+		 * NB: This could/should check a separate bitmap of trigger-
+		 * enabled queues, but for now we only implement uAPSD w/o
+		 * TSPEC changes to the ACs, so they're always the same.
+		 */
+		if (!(rx->sta->sta.uapsd_queues & BIT(ac)))
+			return RX_CONTINUE;
+
+		/* if we are in a service period, do nothing */
+		if (test_sta_flags(rx->sta, WLAN_STA_SP))
+			return RX_CONTINUE;
+
+		if (!test_sta_flags(rx->sta, WLAN_STA_PS_DRIVER))
+			ieee80211_sta_ps_deliver_uapsd(rx->sta);
+		else
+			set_sta_flags(rx->sta, WLAN_STA_UAPSD);
+	}
+
+	return RX_CONTINUE;
+}
+
+static ieee80211_rx_result debug_noinline
 ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
 {
 	struct sta_info *sta = rx->sta;
@@ -1482,33 +1544,6 @@ ieee80211_rx_h_defragment(struct ieee802
 }
 
 static ieee80211_rx_result debug_noinline
-ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx)
-{
-	struct ieee80211_sub_if_data *sdata = rx->sdata;
-	__le16 fc = ((struct ieee80211_hdr *)rx->skb->data)->frame_control;
-	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
-
-	if (likely(!rx->sta || !ieee80211_is_pspoll(fc) ||
-		   !(status->rx_flags & IEEE80211_RX_RA_MATCH)))
-		return RX_CONTINUE;
-
-	if ((sdata->vif.type != NL80211_IFTYPE_AP) &&
-	    (sdata->vif.type != NL80211_IFTYPE_AP_VLAN))
-		return RX_DROP_UNUSABLE;
-
-	if (!test_sta_flags(rx->sta, WLAN_STA_PS_DRIVER))
-		ieee80211_sta_ps_deliver_poll_response(rx->sta);
-	else
-		set_sta_flags(rx->sta, WLAN_STA_PSPOLL);
-
-	/* Free PS Poll skb here instead of returning RX_DROP that would
-	 * count as an dropped frame. */
-	dev_kfree_skb(rx->skb);
-
-	return RX_QUEUED;
-}
-
-static ieee80211_rx_result debug_noinline
 ieee80211_rx_h_remove_qos_control(struct ieee80211_rx_data *rx)
 {
 	u8 *data = rx->skb->data;
@@ -2564,9 +2599,9 @@ static void ieee80211_rx_handlers(struct
 
 		CALL_RXH(ieee80211_rx_h_decrypt)
 		CALL_RXH(ieee80211_rx_h_check_more_data)
+		CALL_RXH(ieee80211_rx_h_uapsd_and_pspoll)
 		CALL_RXH(ieee80211_rx_h_sta_process)
 		CALL_RXH(ieee80211_rx_h_defragment)
-		CALL_RXH(ieee80211_rx_h_ps_poll)
 		CALL_RXH(ieee80211_rx_h_michael_mic_verify)
 		/* must be after MMIC verify so header is counted in MPDU mic */
 		CALL_RXH(ieee80211_rx_h_remove_qos_control)
--- a/net/mac80211/sta_info.c	2011-09-06 21:40:08.000000000 +0200
+++ b/net/mac80211/sta_info.c	2011-09-06 22:05:29.000000000 +0200
@@ -248,6 +248,9 @@ static void sta_unblock(struct work_stru
 	else if (test_and_clear_sta_flags(sta, WLAN_STA_PSPOLL)) {
 		clear_sta_flags(sta, WLAN_STA_PS_DRIVER);
 		ieee80211_sta_ps_deliver_poll_response(sta);
+	} else if (test_and_clear_sta_flags(sta, WLAN_STA_UAPSD)) {
+		clear_sta_flags(sta, WLAN_STA_PS_DRIVER);
+		ieee80211_sta_ps_deliver_uapsd(sta);
 	} else
 		clear_sta_flags(sta, WLAN_STA_PS_DRIVER);
 }
@@ -1136,31 +1139,27 @@ void ieee80211_sta_ps_deliver_wakeup(str
 #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
 }
 
-void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta)
+static void
+ieee80211_sta_ps_deliver_response(struct sta_info *sta,
+				  int n_frames, u8 ignored_acs,
+				  enum ieee80211_frame_release_type reason)
 {
 	struct ieee80211_sub_if_data *sdata = sta->sdata;
 	struct ieee80211_local *local = sdata->local;
-	struct sk_buff *skb = NULL;
 	bool found = false;
 	bool more_data = false;
 	int ac, driver_release_tid = -1;
-	u8 ignore_for_response = sta->sta.uapsd_queues;
+	struct sk_buff_head frames;
 
-	/*
-	 * If all ACs are delivery-enabled then we should reply
-	 * from any of them, if only some are enabled we reply
-	 * only from the non-enabled ones.
-	 */
-	if (ignore_for_response == BIT(IEEE80211_NUM_ACS) - 1)
-		ignore_for_response = 0;
+	__skb_queue_head_init(&frames);
 
 	/*
-	 * Get response frame and more data bit for it.
+	 * Get response frame(s) and more data bit for it.
 	 */
 	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
 		int tid1, tid2;
 
-		if (ignore_for_response & BIT(ac))
+		if (ignored_acs & BIT(ac))
 			continue;
 
 		ieee80211_tids_for_ac(ac, &tid1, &tid2);
@@ -1173,14 +1172,22 @@ void ieee80211_sta_ps_deliver_poll_respo
 				found = true;
 				driver_release_tid = tid1;
 			} else {
-				skb = skb_dequeue(&sta->tx_filtered[ac]);
-				if (!skb) {
-					skb = skb_dequeue(&sta->ps_tx_buf[ac]);
-					if (skb)
-						local->total_ps_buffered--;
-				}
-				if (skb)
+				struct sk_buff *skb;
+
+				while (n_frames > 0) {
+					skb = skb_dequeue(&sta->tx_filtered[ac]);
+					if (!skb) {
+						skb = skb_dequeue(
+							&sta->ps_tx_buf[ac]);
+						if (skb)
+							local->total_ps_buffered--;
+					}
+					if (!skb)
+						break;
+					n_frames--;
 					found = true;
+					__skb_queue_tail(&frames, skb);
+				}
 			}
 		}
 
@@ -1218,42 +1225,58 @@ void ieee80211_sta_ps_deliver_poll_respo
 		 *	  Should we send it a null-func frame indicating we
 		 *	  have nothing buffered for it?
 		 */
-		printk(KERN_DEBUG "%s: STA %pM sent PS Poll even "
-		       "though there are no buffered frames for it\n",
-		       sdata->name, sta->sta.addr);
+		if (reason == IEEE80211_FRAME_RELEASE_PSPOLL)
+			printk(KERN_DEBUG "%s: STA %pM sent PS Poll even "
+			       "though there are no buffered frames for it\n",
+			       sdata->name, sta->sta.addr);
 #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
 
 		return;
 	}
 
-	if (skb) {
-		struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-		struct ieee80211_hdr *hdr =
-			(struct ieee80211_hdr *) skb->data;
-
-		/*
-		 * Tell TX path to send this frame even though the STA may
-		 * still remain is PS mode after this frame exchange.
-		 */
-		info->flags |= IEEE80211_TX_CTL_PSPOLL_RESPONSE;
-
-#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
-		printk(KERN_DEBUG "STA %pM aid %d: PS Poll\n",
-		       sta->sta.addr, sta->sta.aid);
-#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
+	if (driver_release_tid == -1) {
+		struct sk_buff_head pending;
+		struct sk_buff *skb;
+
+		skb_queue_head_init(&pending);
+
+		while ((skb = __skb_dequeue(&frames))) {
+			struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+			struct ieee80211_hdr *hdr = (void *) skb->data;
+
+			/*
+			 * Tell TX path to send this frame even though the
+			 * STA may still remain is PS mode after this frame
+			 * exchange.
+			 */
+			info->flags |= IEEE80211_TX_CTL_POLL_RESPONSE;
+
+			/*
+			 * Use MoreData flag to indicate whether there are
+			 * more buffered frames for this STA
+			 */
+			if (!more_data)
+				hdr->frame_control &=
+					cpu_to_le16(~IEEE80211_FCTL_MOREDATA);
+			else
+				hdr->frame_control |=
+					cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+
+			if (reason == IEEE80211_FRAME_RELEASE_UAPSD &&
+			    skb_queue_empty(&frames)) {
+				/* set EOSP for the frame */
+				u8 *p = ieee80211_get_qos_ctl(hdr);
+				*p |= IEEE80211_QOS_CTL_EOSP;
+				info->flags |= IEEE80211_TX_STATUS_EOSP |
+					       IEEE80211_TX_CTL_REQ_TX_STATUS;
+			}
 
-		/* Use MoreData flag to indicate whether there are more
-		 * buffered frames for this STA */
-		if (!more_data)
-			hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREDATA);
-		else
-			hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+			__skb_queue_tail(&pending, skb);
+		}
 
-		ieee80211_add_pending_skb(local, skb);
+		ieee80211_add_pending_skbs(local, &pending);
 
 		sta_info_recalc_tim(sta);
-	} else if (WARN_ON(driver_release_tid == -1)) {
-		/* err ... ??? */
 	} else {
 		/*
 		 * We need to release a frame that is buffered somewhere in the
@@ -1266,8 +1289,7 @@ void ieee80211_sta_ps_deliver_poll_respo
 		 * needs to be set anyway.
 		 */
 		drv_release_buffered_frames(local, sta, driver_release_tid,
-					    1, IEEE80211_FRAME_RELEASE_PSPOLL,
-					    more_data);
+					    n_frames, reason, more_data);
 
 		/*
 		 * Note that we don't recalculate the TIM bit here as it would
@@ -1280,6 +1302,43 @@ void ieee80211_sta_ps_deliver_poll_respo
 	}
 }
 
+void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta)
+{
+	u8 ignore_for_response = sta->sta.uapsd_queues;
+
+	/*
+	 * If all ACs are delivery-enabled then we should reply
+	 * from any of them, if only some are enabled we reply
+	 * only from the non-enabled ones.
+	 */
+	if (ignore_for_response == BIT(IEEE80211_NUM_ACS) - 1)
+		ignore_for_response = 0;
+
+	ieee80211_sta_ps_deliver_response(sta, 1, ignore_for_response,
+					  IEEE80211_FRAME_RELEASE_PSPOLL);
+}
+
+void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta)
+{
+	u8 delivery_enabled = sta->sta.uapsd_queues;
+
+	/*
+	 * If we ever grow support for TSPEC this might happen if
+	 * the TSPEC update from hostapd comes in between a trigger
+	 * frame setting WLAN_STA_UAPSD in the RX path and this
+	 * actually getting called.
+	 */
+	if (!delivery_enabled)
+		return;
+
+	/* Ohh, finally, the service period starts :-) */
+	set_sta_flags(sta, WLAN_STA_SP);
+
+	ieee80211_sta_ps_deliver_response(sta, sta->sta.max_sp,
+					  ~delivery_enabled,
+					  IEEE80211_FRAME_RELEASE_UAPSD);
+}
+
 void ieee80211_sta_block_awake(struct ieee80211_hw *hw,
 			       struct ieee80211_sta *pubsta, bool block)
 {
--- a/net/mac80211/sta_info.h	2011-09-06 21:40:08.000000000 +0200
+++ b/net/mac80211/sta_info.h	2011-09-06 22:05:21.000000000 +0200
@@ -43,6 +43,11 @@
  *	be in the queues
  * @WLAN_STA_PSPOLL: Station sent PS-poll while driver was keeping
  *	station in power-save mode, reply when the driver unblocks.
+ * @WLAN_STA_UAPSD: Station requested unscheduled SP while driver was
+ *	keeping station in power-save mode, reply when the driver
+ *	unblocks the station.
+ * @WLAN_STA_SP: Station is in a service period, so don't try to
+ *	reply to other uAPSD trigger frames.
  */
 enum ieee80211_sta_info_flags {
 	WLAN_STA_AUTH		= 1<<0,
@@ -58,6 +63,8 @@ enum ieee80211_sta_info_flags {
 	WLAN_STA_BLOCK_BA	= 1<<11,
 	WLAN_STA_PS_DRIVER	= 1<<12,
 	WLAN_STA_PSPOLL		= 1<<13,
+	WLAN_STA_UAPSD		= 1<<14,
+	WLAN_STA_SP		= 1<<15,
 };
 
 #define STA_TID_NUM 16
@@ -528,5 +535,6 @@ void ieee80211_sta_expire(struct ieee802
 
 void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta);
 void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta);
+void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta);
 
 #endif /* STA_INFO_H */
--- a/drivers/net/wireless/iwlegacy/iwl-4965-tx.c	2011-09-06 21:40:08.000000000 +0200
+++ b/drivers/net/wireless/iwlegacy/iwl-4965-tx.c	2011-09-06 21:40:09.000000000 +0200
@@ -335,7 +335,7 @@ int iwl4965_tx_skb(struct iwl_priv *priv
 		sta_priv = (void *)sta->drv_priv;
 
 	if (sta_priv && sta_priv->asleep &&
-	    (info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE)) {
+	    (info->flags & IEEE80211_TX_CTL_POLL_RESPONSE)) {
 		/*
 		 * This sends an asynchronous command to the device,
 		 * but we can rely on it being processed before the
--- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c	2011-09-06 21:40:08.000000000 +0200
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c	2011-09-06 21:40:09.000000000 +0200
@@ -300,7 +300,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv,
 		sta_priv = (void *)info->control.sta->drv_priv;
 
 	if (sta_priv && sta_priv->asleep &&
-	    (info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE)) {
+	    (info->flags & IEEE80211_TX_CTL_POLL_RESPONSE)) {
 		/*
 		 * This sends an asynchronous command to the device,
 		 * but we can rely on it being processed before the
--- a/drivers/net/wireless/p54/txrx.c	2011-09-06 21:40:08.000000000 +0200
+++ b/drivers/net/wireless/p54/txrx.c	2011-09-06 21:40:09.000000000 +0200
@@ -685,7 +685,7 @@ static void p54_tx_80211_header(struct p
 	if (!(info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ))
 		*flags |= P54_HDR_FLAG_DATA_OUT_SEQNR;
 
-	if (info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE)
+	if (info->flags & IEEE80211_TX_CTL_POLL_RESPONSE)
 		*flags |= P54_HDR_FLAG_DATA_OUT_NOCANCEL;
 
 	if (info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT)
--- a/include/net/mac80211.h	2011-09-06 21:40:08.000000000 +0200
+++ b/include/net/mac80211.h	2011-09-06 22:05:21.000000000 +0200
@@ -331,9 +331,9 @@ struct ieee80211_bss_conf {
  *	used to indicate that a frame was already retried due to PS
  * @IEEE80211_TX_INTFL_DONT_ENCRYPT: completely internal to mac80211,
  *	used to indicate frame should not be encrypted
- * @IEEE80211_TX_CTL_PSPOLL_RESPONSE: (internal?)
- *	This frame is a response to a PS-poll frame and should be sent
- *	although the station is in powersave mode.
+ * @IEEE80211_TX_CTL_POLL_RESPONSE: This frame is a response to a poll
+ *	frame (PS-Poll or uAPSD) and should be sent although the station
+ *	is in powersave mode.
  * @IEEE80211_TX_CTL_MORE_FRAMES: More frames will be passed to the
  *	transmit function after the current frame, this can be used
  *	by drivers to kick the DMA queue only if unset or when the
@@ -356,6 +356,10 @@ struct ieee80211_bss_conf {
  * @IEEE80211_TX_INTFL_TKIP_MIC_FAILURE: Marks this packet to be used for TKIP
  *	testing. It will be sent out with incorrect Michael MIC key to allow
  *	TKIP countermeasures to be tested.
+ * @IEEE80211_TX_STATUS_EOSP: This packet marks the end of service period,
+ *	when its status is reported the service period ends. For frames in
+ *	an SP that mac80211 transmits, it is already set; for driver frames
+ *	the driver may set this flag.
  *
  * Note: If you have to add new flags to the enumeration, then don't
  *	 forget to update %IEEE80211_TX_TEMPORARY_FLAGS when necessary.
@@ -377,7 +381,7 @@ enum mac80211_tx_control_flags {
 	IEEE80211_TX_INTFL_NEED_TXPROCESSING	= BIT(14),
 	IEEE80211_TX_INTFL_RETRIED		= BIT(15),
 	IEEE80211_TX_INTFL_DONT_ENCRYPT		= BIT(16),
-	IEEE80211_TX_CTL_PSPOLL_RESPONSE	= BIT(17),
+	IEEE80211_TX_CTL_POLL_RESPONSE		= BIT(17),
 	IEEE80211_TX_CTL_MORE_FRAMES		= BIT(18),
 	IEEE80211_TX_INTFL_RETRANSMISSION	= BIT(19),
 	IEEE80211_TX_INTFL_HAS_RADIOTAP		= BIT(20),
@@ -386,6 +390,7 @@ enum mac80211_tx_control_flags {
 	IEEE80211_TX_CTL_STBC			= BIT(23) | BIT(24),
 	IEEE80211_TX_CTL_TX_OFFCHAN		= BIT(25),
 	IEEE80211_TX_INTFL_TKIP_MIC_FAILURE	= BIT(26),
+	IEEE80211_TX_STATUS_EOSP		= BIT(27),
 };
 
 #define IEEE80211_TX_CTL_STBC_SHIFT		23
@@ -399,7 +404,7 @@ enum mac80211_tx_control_flags {
 	IEEE80211_TX_CTL_SEND_AFTER_DTIM | IEEE80211_TX_CTL_AMPDU |	      \
 	IEEE80211_TX_STAT_TX_FILTERED |	IEEE80211_TX_STAT_ACK |		      \
 	IEEE80211_TX_STAT_AMPDU | IEEE80211_TX_STAT_AMPDU_NO_BACK |	      \
-	IEEE80211_TX_CTL_RATE_CTRL_PROBE | IEEE80211_TX_CTL_PSPOLL_RESPONSE | \
+	IEEE80211_TX_CTL_RATE_CTRL_PROBE | IEEE80211_TX_CTL_POLL_RESPONSE |   \
 	IEEE80211_TX_CTL_MORE_FRAMES | IEEE80211_TX_CTL_LDPC |		      \
 	IEEE80211_TX_CTL_STBC)
 
@@ -1604,9 +1609,12 @@ enum ieee80211_tx_sync_type {
 /**
  * enum ieee80211_frame_release_type - frame release reason
  * @IEEE80211_FRAME_RELEASE_PSPOLL: frame released for PS-Poll
+ * @IEEE80211_FRAME_RELEASE_UAPSD: frame(s) released due to
+ *	frame received on trigger-enabled AC
  */
 enum ieee80211_frame_release_type {
 	IEEE80211_FRAME_RELEASE_PSPOLL,
+	IEEE80211_FRAME_RELEASE_UAPSD,
 };
 
 /**
@@ -1932,7 +1940,9 @@ enum ieee80211_frame_release_type {
  *	In the case this is used for uAPSD, the @num_frames parameter may be
  *	bigger than one, but the driver may send fewer frames (it must send
  *	at least one, however). In this case it is also responsible for
- *	setting the EOSP flag in the QoS header of the frames.
+ *	setting the EOSP flag in the QoS header of the frames. Also, when the
+ *	service period ends, the driver must set %IEEE80211_TX_STATUS_EOSP
+ *	on the last frame in the SP.
  *	This callback must be atomic.
  */
 struct ieee80211_ops {
--- a/net/mac80211/tx.c	2011-09-06 21:40:08.000000000 +0200
+++ b/net/mac80211/tx.c	2011-09-06 21:40:09.000000000 +0200
@@ -456,7 +456,7 @@ ieee80211_tx_h_unicast_ps_buf(struct iee
 	staflags = get_sta_flags(sta);
 
 	if (unlikely((staflags & (WLAN_STA_PS_STA | WLAN_STA_PS_DRIVER)) &&
-		     !(info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE))) {
+		     !(info->flags & IEEE80211_TX_CTL_POLL_RESPONSE))) {
 		int ac = skb_get_queue_mapping(tx->skb);
 
 #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
--- a/net/mac80211/status.c	2011-09-06 21:40:08.000000000 +0200
+++ b/net/mac80211/status.c	2011-09-06 22:05:21.000000000 +0200
@@ -234,6 +234,9 @@ void ieee80211_tx_status(struct ieee8021
 		if (memcmp(hdr->addr2, sta->sdata->vif.addr, ETH_ALEN))
 			continue;
 
+		if (info->flags & IEEE80211_TX_STATUS_EOSP)
+			clear_sta_flags(sta, WLAN_STA_SP);
+
 		acked = !!(info->flags & IEEE80211_TX_STAT_ACK);
 		if (!acked && test_sta_flags(sta, WLAN_STA_PS_STA)) {
 			/*



^ permalink raw reply	[flat|nested] 8+ messages in thread

* [RFC 4/5] mac80211: reply only once to each PS-poll
  2011-09-06 20:27 [RFC 0/5] mac80211 AP-side uAPSD support Johannes Berg
                   ` (2 preceding siblings ...)
  2011-09-06 20:27 ` [RFC 3/5] mac80211: implement uAPSD Johannes Berg
@ 2011-09-06 20:27 ` Johannes Berg
  2011-09-07 11:42   ` Johannes Berg
  2011-09-06 20:27 ` [RFC 5/5] mac80211: allow out-of-band EOSP notification Johannes Berg
  4 siblings, 1 reply; 8+ messages in thread
From: Johannes Berg @ 2011-09-06 20:27 UTC (permalink / raw)
  To: linux-wireless

From: Johannes Berg <johannes.berg@intel.com>

If a PS-poll frame is retried (but was received)
there is no way to detect that since it has no
sequence number. As a consequence, the standard
asks us to not react to PS-poll frames until the
response to one made it out (was ACKed or lost).

Implement this by adding a new station flag that
indicates whether we're sending a response, and
a new status flag that indicates we should clear
the station flag again. This requires TX status
reporting for the frame, so request that.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 include/net/mac80211.h  |    6 ++++++
 net/mac80211/rx.c       |   10 ++++++----
 net/mac80211/sta_info.c |    7 +++++--
 net/mac80211/sta_info.h |    3 +++
 net/mac80211/status.c   |    2 ++
 5 files changed, 22 insertions(+), 6 deletions(-)

--- a/net/mac80211/rx.c	2011-09-06 22:09:52.000000000 +0200
+++ b/net/mac80211/rx.c	2011-09-06 22:13:05.000000000 +0200
@@ -1195,10 +1195,12 @@ ieee80211_rx_h_uapsd_and_pspoll(struct i
 		return RX_CONTINUE;
 
 	if (unlikely(ieee80211_is_pspoll(hdr->frame_control))) {
-		if (!test_sta_flags(rx->sta, WLAN_STA_PS_DRIVER))
-			ieee80211_sta_ps_deliver_poll_response(rx->sta);
-		else
-			set_sta_flags(rx->sta, WLAN_STA_PSPOLL);
+		if (!test_sta_flags(rx->sta, WLAN_STA_POLLED)) {
+			if (!test_sta_flags(rx->sta, WLAN_STA_PS_DRIVER))
+				ieee80211_sta_ps_deliver_poll_response(rx->sta);
+			else
+				set_sta_flags(rx->sta, WLAN_STA_PSPOLL);
+		}
 
 		/* Free PS Poll skb here instead of returning RX_DROP that would
 		 * count as an dropped frame. */
--- a/net/mac80211/sta_info.h	2011-09-06 22:09:52.000000000 +0200
+++ b/net/mac80211/sta_info.h	2011-09-06 22:23:19.000000000 +0200
@@ -48,6 +48,8 @@
  *	unblocks the station.
  * @WLAN_STA_SP: Station is in a service period, so don't try to
  *	reply to other uAPSD trigger frames.
+ * @WLAN_STA_POLLED: We're responding to a PS-Poll frame right now,
+ *	don't respond to retries as well.
  */
 enum ieee80211_sta_info_flags {
 	WLAN_STA_AUTH		= 1<<0,
@@ -65,6 +67,7 @@ enum ieee80211_sta_info_flags {
 	WLAN_STA_PSPOLL		= 1<<13,
 	WLAN_STA_UAPSD		= 1<<14,
 	WLAN_STA_SP		= 1<<15,
+	WLAN_STA_POLLED		= 1<<16,
 };
 
 #define STA_TID_NUM 16
--- a/include/net/mac80211.h	2011-09-06 22:09:52.000000000 +0200
+++ b/include/net/mac80211.h	2011-09-06 22:23:00.000000000 +0200
@@ -360,6 +360,11 @@ struct ieee80211_bss_conf {
  *	when its status is reported the service period ends. For frames in
  *	an SP that mac80211 transmits, it is already set; for driver frames
  *	the driver may set this flag.
+ * @IEEE80211_TX_STATUS_PSPOLL_RESP: This packet was a response to a PS-Poll
+ *	frame and thus marks the end of this PS-Poll. For frames that mac80211
+ *	releases from queues it is already set; for driver queued frames the
+ *	driver must set the flag and report TX status with it for a response
+ *	to a PS-poll.
  *
  * Note: If you have to add new flags to the enumeration, then don't
  *	 forget to update %IEEE80211_TX_TEMPORARY_FLAGS when necessary.
@@ -391,6 +396,7 @@ enum mac80211_tx_control_flags {
 	IEEE80211_TX_CTL_TX_OFFCHAN		= BIT(25),
 	IEEE80211_TX_INTFL_TKIP_MIC_FAILURE	= BIT(26),
 	IEEE80211_TX_STATUS_EOSP		= BIT(27),
+	IEEE80211_TX_STATUS_PSPOLL_RESP		= BIT(28),
 };
 
 #define IEEE80211_TX_CTL_STBC_SHIFT		23
--- a/net/mac80211/status.c	2011-09-06 22:09:52.000000000 +0200
+++ b/net/mac80211/status.c	2011-09-06 22:13:05.000000000 +0200
@@ -236,6 +236,8 @@ void ieee80211_tx_status(struct ieee8021
 
 		if (info->flags & IEEE80211_TX_STATUS_EOSP)
 			clear_sta_flags(sta, WLAN_STA_SP);
+		else if (info->flags & IEEE80211_TX_STATUS_PSPOLL_RESP)
+			clear_sta_flags(sta, WLAN_STA_POLLED);
 
 		acked = !!(info->flags & IEEE80211_TX_STAT_ACK);
 		if (!acked && test_sta_flags(sta, WLAN_STA_PS_STA)) {
--- a/net/mac80211/sta_info.c	2011-09-06 22:09:52.000000000 +0200
+++ b/net/mac80211/sta_info.c	2011-09-06 22:24:11.000000000 +0200
@@ -1262,8 +1262,11 @@ ieee80211_sta_ps_deliver_response(struct
 				hdr->frame_control |=
 					cpu_to_le16(IEEE80211_FCTL_MOREDATA);
 
-			if (reason == IEEE80211_FRAME_RELEASE_UAPSD &&
-			    skb_queue_empty(&frames)) {
+			if (reason == IEEE80211_FRAME_RELEASE_PSPOLL) {
+				info->flags |= IEEE80211_TX_STATUS_PSPOLL_RESP |
+					       IEEE80211_TX_CTL_REQ_TX_STATUS;
+			} else if (reason == IEEE80211_FRAME_RELEASE_UAPSD &&
+				   skb_queue_empty(&frames)) {
 				/* set EOSP for the frame */
 				u8 *p = ieee80211_get_qos_ctl(hdr);
 				*p |= IEEE80211_QOS_CTL_EOSP;



^ permalink raw reply	[flat|nested] 8+ messages in thread

* [RFC 5/5] mac80211: allow out-of-band EOSP notification
  2011-09-06 20:27 [RFC 0/5] mac80211 AP-side uAPSD support Johannes Berg
                   ` (3 preceding siblings ...)
  2011-09-06 20:27 ` [RFC 4/5] mac80211: reply only once to each PS-poll Johannes Berg
@ 2011-09-06 20:27 ` Johannes Berg
  4 siblings, 0 replies; 8+ messages in thread
From: Johannes Berg @ 2011-09-06 20:27 UTC (permalink / raw)
  To: linux-wireless

From: Johannes Berg <johannes.berg@intel.com>

iwlwifi has a separate EOSP notification from
the device, and to make use of that properly
it needs to be passed to mac80211. To be able
to mix with tx_status_irqsafe and rx_irqsafe
it also needs to be an "_irqsafe" version in
the sense that it goes through the tasklet,
the actual flag clearing would be IRQ-safe
but doing it directly would cause reordering
issues.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 include/net/mac80211.h     |   19 ++++++++++++++++++-
 net/mac80211/ieee80211_i.h |    5 +++++
 net/mac80211/main.c        |   13 +++++++++++++
 net/mac80211/sta_info.c    |   23 +++++++++++++++++++++++
 4 files changed, 59 insertions(+), 1 deletion(-)

--- a/net/mac80211/sta_info.c	2011-09-06 22:25:59.000000000 +0200
+++ b/net/mac80211/sta_info.c	2011-09-06 22:26:00.000000000 +0200
@@ -1356,6 +1356,29 @@ void ieee80211_sta_block_awake(struct ie
 }
 EXPORT_SYMBOL(ieee80211_sta_block_awake);
 
+void ieee80211_sta_eosp_irqsafe(struct ieee80211_sta *pubsta)
+{
+	struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
+	struct ieee80211_local *local = sta->local;
+	struct sk_buff *skb;
+	struct skb_eosp_msg_data *data;
+
+	skb = alloc_skb(0, GFP_ATOMIC);
+	if (!skb) {
+		/* too bad ... but race is better than loss */
+		clear_sta_flags(sta, WLAN_STA_SP);
+		return;
+	}
+
+	data = (void *)skb->cb;
+	memcpy(data->sta, pubsta->addr, ETH_ALEN);
+	memcpy(data->iface, sta->sdata->vif.addr, ETH_ALEN);
+	skb->pkt_type = IEEE80211_EOSP_MSG;
+	skb_queue_tail(&local->skb_queue, skb);
+	tasklet_schedule(&local->tasklet);
+}
+EXPORT_SYMBOL(ieee80211_sta_eosp_irqsafe);
+
 void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta,
 				u8 tid, bool buffered)
 {
--- a/include/net/mac80211.h	2011-09-06 22:25:59.000000000 +0200
+++ b/include/net/mac80211.h	2011-09-06 22:26:00.000000000 +0200
@@ -1948,7 +1948,8 @@ enum ieee80211_frame_release_type {
  *	at least one, however). In this case it is also responsible for
  *	setting the EOSP flag in the QoS header of the frames. Also, when the
  *	service period ends, the driver must set %IEEE80211_TX_STATUS_EOSP
- *	on the last frame in the SP.
+ *	on the last frame in the SP. Alternatively, it may call the function
+ *	ieee80211_sta_eosp_irqsafe() to inform mac80211 of the end of the SP.
  *	This callback must be atomic.
  */
 struct ieee80211_ops {
@@ -3072,6 +3073,22 @@ void ieee80211_sta_block_awake(struct ie
 			       struct ieee80211_sta *pubsta, bool block);
 
 /**
+ * ieee80211_sta_eosp - notify mac80211 about end of SP
+ * @pubsta: the station
+ *
+ * When a device transmits frames in a way that it can't
+ * tell us in the TX status about the EOSP, it must call
+ * this function instead.
+ *
+ * Note that there is no non-_irqsafe version right now as
+ * it wasn't needed, but just like _tx_status() and _rx()
+ * must not be mixed in irqsafe/non-irqsafe versions, this
+ * function must not be mixed with those either. Use the
+ * all irqsafe, or all non-irqsafe, don't mix!
+ */
+void ieee80211_sta_eosp_irqsafe(struct ieee80211_sta *pubsta);
+
+/**
  * ieee80211_iter_keys - iterate keys programmed into the device
  * @hw: pointer obtained from ieee80211_alloc_hw()
  * @vif: virtual interface to iterate, may be %NULL for all
--- a/net/mac80211/main.c	2011-09-06 22:25:32.000000000 +0200
+++ b/net/mac80211/main.c	2011-09-06 22:26:00.000000000 +0200
@@ -325,6 +325,8 @@ u32 ieee80211_reset_erp_info(struct ieee
 static void ieee80211_tasklet_handler(unsigned long data)
 {
 	struct ieee80211_local *local = (struct ieee80211_local *) data;
+	struct sta_info *sta, *tmp;
+	struct skb_eosp_msg_data *eosp_data;
 	struct sk_buff *skb;
 
 	while ((skb = skb_dequeue(&local->skb_queue)) ||
@@ -340,6 +342,17 @@ static void ieee80211_tasklet_handler(un
 			skb->pkt_type = 0;
 			ieee80211_tx_status(local_to_hw(local), skb);
 			break;
+		case IEEE80211_EOSP_MSG:
+			eosp_data = (void *)skb->cb;
+			for_each_sta_info(local, eosp_data->sta, sta, tmp) {
+				/* skip wrong virtual interface */
+				if (memcmp(eosp_data->iface,
+					   sta->sdata->vif.addr, ETH_ALEN))
+					continue;
+				clear_sta_flags(sta, WLAN_STA_SP);
+				break;
+			}
+			break;
 		default:
 			WARN(1, "mac80211: Packet is of unknown type %d\n",
 			     skb->pkt_type);
--- a/net/mac80211/ieee80211_i.h	2011-09-06 22:25:32.000000000 +0200
+++ b/net/mac80211/ieee80211_i.h	2011-09-06 22:26:00.000000000 +0200
@@ -661,6 +661,11 @@ enum sdata_queue_type {
 enum {
 	IEEE80211_RX_MSG	= 1,
 	IEEE80211_TX_STATUS_MSG	= 2,
+	IEEE80211_EOSP_MSG	= 3,
+};
+
+struct skb_eosp_msg_data {
+	u8 sta[ETH_ALEN], iface[ETH_ALEN];
 };
 
 enum queue_stop_reason {



^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [RFC 3/5] mac80211: implement uAPSD
  2011-09-06 20:27 ` [RFC 3/5] mac80211: implement uAPSD Johannes Berg
@ 2011-09-07 11:41   ` Johannes Berg
  0 siblings, 0 replies; 8+ messages in thread
From: Johannes Berg @ 2011-09-07 11:41 UTC (permalink / raw)
  To: linux-wireless


> +       ieee80211_sta_ps_deliver_response(sta, sta->sta.max_sp,
> +                                         ~delivery_enabled,
> +
> IEEE80211_FRAME_RELEASE_UAPSD);

This is bogus, sta.max_sp is in the QoS Info field format so needs to be
translated into # of frames.

> @@ -386,6 +390,7 @@ enum mac80211_tx_control_flags {
>  	IEEE80211_TX_CTL_STBC			= BIT(23) | BIT(24),
>  	IEEE80211_TX_CTL_TX_OFFCHAN		= BIT(25),
>  	IEEE80211_TX_INTFL_TKIP_MIC_FAILURE	= BIT(26),
> +	IEEE80211_TX_STATUS_EOSP		= BIT(27),
>  };
>  
>  #define IEEE80211_TX_CTL_STBC_SHIFT		23
> @@ -399,7 +404,7 @@ enum mac80211_tx_control_flags {
>  	IEEE80211_TX_CTL_SEND_AFTER_DTIM | IEEE80211_TX_CTL_AMPDU |	      \
>  	IEEE80211_TX_STAT_TX_FILTERED |	IEEE80211_TX_STAT_ACK |		      \
>  	IEEE80211_TX_STAT_AMPDU | IEEE80211_TX_STAT_AMPDU_NO_BACK |	      \
> -	IEEE80211_TX_CTL_RATE_CTRL_PROBE | IEEE80211_TX_CTL_PSPOLL_RESPONSE | \
> +	IEEE80211_TX_CTL_RATE_CTRL_PROBE | IEEE80211_TX_CTL_POLL_RESPONSE |   \
>  	IEEE80211_TX_CTL_MORE_FRAMES | IEEE80211_TX_CTL_LDPC |		      \
>  	IEEE80211_TX_CTL_STBC)

EOSP needs to be added to temporary flags too.

johannes


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [RFC 4/5] mac80211: reply only once to each PS-poll
  2011-09-06 20:27 ` [RFC 4/5] mac80211: reply only once to each PS-poll Johannes Berg
@ 2011-09-07 11:42   ` Johannes Berg
  0 siblings, 0 replies; 8+ messages in thread
From: Johannes Berg @ 2011-09-07 11:42 UTC (permalink / raw)
  To: linux-wireless


> @@ -391,6 +396,7 @@ enum mac80211_tx_control_flags {
>  	IEEE80211_TX_CTL_TX_OFFCHAN		= BIT(25),
>  	IEEE80211_TX_INTFL_TKIP_MIC_FAILURE	= BIT(26),
>  	IEEE80211_TX_STATUS_EOSP		= BIT(27),
> +	IEEE80211_TX_STATUS_PSPOLL_RESP		= BIT(28),

Also needs to be added to temporary flags.

johannes


^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2011-09-07 12:24 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-09-06 20:27 [RFC 0/5] mac80211 AP-side uAPSD support Johannes Berg
2011-09-06 20:27 ` [RFC 1/5] mac80211: split PS buffers into ACs Johannes Berg
2011-09-06 20:27 ` [RFC 2/5] mac80211: allow releasing driver-buffered frames Johannes Berg
2011-09-06 20:27 ` [RFC 3/5] mac80211: implement uAPSD Johannes Berg
2011-09-07 11:41   ` Johannes Berg
2011-09-06 20:27 ` [RFC 4/5] mac80211: reply only once to each PS-poll Johannes Berg
2011-09-07 11:42   ` Johannes Berg
2011-09-06 20:27 ` [RFC 5/5] mac80211: allow out-of-band EOSP notification Johannes Berg

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).