linux-wireless.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Jouni Malinen <jouni.malinen@atheros.com>
To: "John W. Linville" <linville@tuxdriver.com>
Cc: linux-wireless@vger.kernel.org,
	Jouni Malinen <jouni.malinen@atheros.com>
Subject: [PATCH 3/4] ath9k: Fix PS mode operation to receive buffered broadcast/multicast frames
Date: Thu, 14 May 2009 21:28:48 +0300	[thread overview]
Message-ID: <20090514183044.626021480@atheros.com> (raw)
In-Reply-To: 20090514182845.684003914@atheros.com

The previous implementation was moving back to NETWORK SLEEP state
immediately after receiving a Beacon frame. This means that we are
unlikely to receive all the buffered broadcast/multicast frames that
would be sent after DTIM Beacon frames. Fix this by parsing the Beacon
frame and remaining awake, if needed, to receive the buffered
broadcast/multicast frames. The last buffered frame will trigger the
move back into NETWORK SLEEP state.

If the last broadcast/multicast frame is not received properly (or if
the AP fails to send it), the next Beacon frame will work as a backup
trigger for returning into NETWORK SLEEP.

A new debug type, PS (debug=0x800 module parameter), is added to make
it easier to debug potential power save issues in the
future. Currently, this is only used for the Beacon frame and buffered
broadcast/multicast receiving.

Signed-off-by: Jouni Malinen <jouni.malinen@atheros.com>

---
 drivers/net/wireless/ath/ath9k/ath9k.h |    1 
 drivers/net/wireless/ath/ath9k/debug.h |    1 
 drivers/net/wireless/ath/ath9k/recv.c  |  119 +++++++++++++++++++++++++++++++--
 3 files changed, 114 insertions(+), 7 deletions(-)

--- wireless-testing.orig/drivers/net/wireless/ath/ath9k/ath9k.h	2009-05-14 21:04:12.000000000 +0300
+++ wireless-testing/drivers/net/wireless/ath/ath9k/ath9k.h	2009-05-14 21:12:24.000000000 +0300
@@ -519,6 +519,7 @@ struct ath_rfkill {
 #define SC_OP_LED_ON            BIT(13)
 #define SC_OP_SCANNING          BIT(14)
 #define SC_OP_TSF_RESET         BIT(15)
+#define SC_OP_WAIT_FOR_CAB      BIT(16)
 
 struct ath_bus_ops {
 	void		(*read_cachesize)(struct ath_softc *sc, int *csz);
--- wireless-testing.orig/drivers/net/wireless/ath/ath9k/recv.c	2009-05-14 21:12:22.000000000 +0300
+++ wireless-testing/drivers/net/wireless/ath/ath9k/recv.c	2009-05-14 21:12:24.000000000 +0300
@@ -473,6 +473,112 @@ void ath_flushrecv(struct ath_softc *sc)
 	spin_unlock_bh(&sc->rx.rxflushlock);
 }
 
+static bool ath_beacon_dtim_pending_cab(struct sk_buff *skb)
+{
+	/* Check whether the Beacon frame has DTIM indicating buffered bc/mc */
+	struct ieee80211_mgmt *mgmt;
+	u8 *pos, *end, id, elen;
+	struct ieee80211_tim_ie *tim;
+
+	mgmt = (struct ieee80211_mgmt *)skb->data;
+	pos = mgmt->u.beacon.variable;
+	end = skb->data + skb->len;
+
+	while (pos + 2 < end) {
+		id = *pos++;
+		elen = *pos++;
+		if (pos + elen > end)
+			break;
+
+		if (id == WLAN_EID_TIM) {
+			if (elen < sizeof(*tim))
+				break;
+			tim = (struct ieee80211_tim_ie *) pos;
+			if (tim->dtim_count != 0)
+				break;
+			return tim->bitmap_ctrl & 0x01;
+		}
+
+		pos += elen;
+	}
+
+	return false;
+}
+
+static void ath_rx_ps_back_to_sleep(struct ath_softc *sc)
+{
+	sc->sc_flags &= ~(SC_OP_WAIT_FOR_BEACON | SC_OP_WAIT_FOR_CAB);
+	if (sc->hw->conf.flags & IEEE80211_CONF_PS)
+		ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_NETWORK_SLEEP);
+}
+
+static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb)
+{
+	struct ieee80211_mgmt *mgmt;
+
+	if (skb->len < 24 + 8 + 2 + 2)
+		return;
+
+	mgmt = (struct ieee80211_mgmt *)skb->data;
+	if (memcmp(sc->curbssid, mgmt->bssid, ETH_ALEN) != 0)
+		return; /* not from our current AP */
+
+	if (!(sc->hw->conf.flags & IEEE80211_CONF_PS)) {
+		/* We are not in PS mode anymore; remain awake */
+		DPRINTF(sc, ATH_DBG_PS, "Not in PS mode anymore, remain "
+			"awake\n");
+		sc->sc_flags &= ~(SC_OP_WAIT_FOR_BEACON | SC_OP_WAIT_FOR_CAB);
+		return;
+	}
+
+	if (ath_beacon_dtim_pending_cab(skb)) {
+		/*
+		 * Remain awake waiting for buffered broadcast/multicast
+		 * frames.
+		 */
+		DPRINTF(sc, ATH_DBG_PS, "Received DTIM beacon indicating "
+			"buffered broadcast/multicast frame(s)\n");
+		sc->sc_flags |= SC_OP_WAIT_FOR_CAB;
+		return;
+	}
+
+	if (sc->sc_flags & SC_OP_WAIT_FOR_CAB) {
+		/*
+		 * This can happen if a broadcast frame is dropped or the AP
+		 * fails to send a frame indicating that all CAB frames have
+		 * been delivered.
+		 */
+		DPRINTF(sc, ATH_DBG_PS, "PS wait for CAB frames timed out\n");
+	}
+
+	/* No more broadcast/multicast frames to be received at this point. */
+	ath_rx_ps_back_to_sleep(sc);
+}
+
+static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb)
+{
+	struct ieee80211_hdr *hdr;
+
+	hdr = (struct ieee80211_hdr *)skb->data;
+
+	/* Process Beacon and CAB receive in PS state */
+	if (ieee80211_is_beacon(hdr->frame_control))
+		ath_rx_ps_beacon(sc, skb);
+	else if ((sc->sc_flags & SC_OP_WAIT_FOR_CAB) &&
+		 (ieee80211_is_data(hdr->frame_control) ||
+		  ieee80211_is_action(hdr->frame_control)) &&
+		 is_multicast_ether_addr(hdr->addr1) &&
+		 !ieee80211_has_moredata(hdr->frame_control)) {
+		DPRINTF(sc, ATH_DBG_PS, "All PS CAB frames received, back to "
+			"sleep\n");
+		/*
+		 * No more broadcast/multicast frames to be received at this
+		 * point.
+		 */
+		ath_rx_ps_back_to_sleep(sc);
+	}
+}
+
 static void ath_rx_send_to_mac80211(struct ath_softc *sc, struct sk_buff *skb,
 				    struct ieee80211_rx_status *rx_status)
 {
@@ -667,8 +773,6 @@ int ath_rx_tasklet(struct ath_softc *sc,
 			rx_status.flag &= ~RX_FLAG_DECRYPTED;
 		}
 
-		ath_rx_send_to_mac80211(sc, skb, &rx_status);
-
 		/* We will now give hardware our shiny new allocated skb */
 		bf->bf_mpdu = requeue_skb;
 		bf->bf_buf_addr = dma_map_single(sc->dev, requeue_skb->data,
@@ -680,6 +784,7 @@ int ath_rx_tasklet(struct ath_softc *sc,
 			bf->bf_mpdu = NULL;
 			DPRINTF(sc, ATH_DBG_FATAL,
 				"dma_mapping_error() on RX\n");
+			ath_rx_send_to_mac80211(sc, skb, &rx_status);
 			break;
 		}
 		bf->bf_dmacontext = bf->bf_buf_addr;
@@ -695,11 +800,11 @@ int ath_rx_tasklet(struct ath_softc *sc,
 			sc->rx.rxotherant = 0;
 		}
 
-		if (ieee80211_is_beacon(fc) &&
-				(sc->sc_flags & SC_OP_WAIT_FOR_BEACON)) {
-			sc->sc_flags &= ~SC_OP_WAIT_FOR_BEACON;
-			ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_NETWORK_SLEEP);
-		}
+		if (unlikely(sc->sc_flags & SC_OP_WAIT_FOR_BEACON))
+			ath_rx_ps(sc, skb);
+
+		ath_rx_send_to_mac80211(sc, skb, &rx_status);
+
 requeue:
 		list_move_tail(&bf->list, &sc->rx.rxbuf);
 		ath_rx_buf_link(sc, bf);
--- wireless-testing.orig/drivers/net/wireless/ath/ath9k/debug.h	2009-05-14 21:04:11.000000000 +0300
+++ wireless-testing/drivers/net/wireless/ath/ath9k/debug.h	2009-05-14 21:12:24.000000000 +0300
@@ -29,6 +29,7 @@ enum ATH_DEBUG {
 	ATH_DBG_BEACON		= 0x00000100,
 	ATH_DBG_CONFIG		= 0x00000200,
 	ATH_DBG_FATAL		= 0x00000400,
+	ATH_DBG_PS		= 0x00000800,
 	ATH_DBG_ANY		= 0xffffffff
 };
 

-- 

-- 
Jouni Malinen                                            PGP id EFC895FA

  parent reply	other threads:[~2009-05-14 18:30 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-05-14 18:28 [PATCH 0/4] ath9k: Power save mode fixes Jouni Malinen
2009-05-14 18:28 ` [PATCH 1/4] ath9k: Fix a check for multicast address for virtual wiphy Jouni Malinen
2009-05-14 18:28 ` [PATCH 2/4] ath9k: Clean up RX processing a bit Jouni Malinen
2009-05-14 18:28 ` Jouni Malinen [this message]
2009-05-14 18:28 ` [PATCH 4/4] ath9k: Move PS wakeup/restore calls from isr to tasklet Jouni Malinen
2009-05-15  6:47   ` Luis R. Rodriguez

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20090514183044.626021480@atheros.com \
    --to=jouni.malinen@atheros.com \
    --cc=linux-wireless@vger.kernel.org \
    --cc=linville@tuxdriver.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).