Search Linux Wireless

[PATCH 1/4] ath9k: Fix channel context transition

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Sujith Manoharan <c_manoha@xxxxxxxxxxxxxxxx>

In channel context mode, a nullfunc is sent with
the PM bit enabled when we switch to a new channel
context from the current one. But, when the scheduler
switches back to the old context, sending a nullfunc
with PM bit cleared has to be done only if there is
buffered traffic at the AP.

Currently, this is not done and a nullfunc is sent
for every transition. Fix this by parsing the TIM IE
for a received beacon and checking if there is buffered
traffic.

Since the beacon frame has to be parsed, move the location
of the ATH_CHANCTX_EVENT_BEACON_RECEIVED after the frame
has been processed - in ath_rx_tasklet().

Signed-off-by: Sujith Manoharan <c_manoha@xxxxxxxxxxxxxxxx>
---
 drivers/net/wireless/ath/ath9k/ath9k.h   | 10 ++++-
 drivers/net/wireless/ath/ath9k/channel.c | 66 +++++++++++++++++++++++++++++++-
 drivers/net/wireless/ath/ath9k/recv.c    | 12 +++---
 3 files changed, 78 insertions(+), 10 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index c690601..857e911 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -338,6 +338,7 @@ struct ath_chanctx {
 	/* do not dereference, use for comparison only */
 	struct ieee80211_vif *primary_sta;
 
+	struct sk_buff *beacon_skb;
 	struct ath_beacon_config beacon;
 	struct ath9k_hw_cal_data caldata;
 	struct timespec tsf_ts;
@@ -376,6 +377,7 @@ enum ath_chanctx_state {
 struct ath_chanctx_sched {
 	bool beacon_pending;
 	bool offchannel_pending;
+	bool tim_set;
 	enum ath_chanctx_state state;
 	u8 beacon_miss;
 
@@ -449,7 +451,9 @@ void ath9k_p2p_ps_timer(void *priv);
 void ath9k_chanctx_wake_queues(struct ath_softc *sc);
 void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx);
 
-void ath_chanctx_beacon_recv_ev(struct ath_softc *sc, u32 ts,
+void ath_chanctx_beacon_recv_ev(struct ath_softc *sc,
+				struct sk_buff *skb,
+				u32 ts,
 				enum ath_chanctx_event ev);
 void ath_chanctx_beacon_sent_ev(struct ath_softc *sc,
 				enum ath_chanctx_event ev);
@@ -478,7 +482,9 @@ static inline void ath9k_offchannel_init(struct ath_softc *sc)
 static inline void ath9k_deinit_channel_context(struct ath_softc *sc)
 {
 }
-static inline void ath_chanctx_beacon_recv_ev(struct ath_softc *sc, u32 ts,
+static inline void ath_chanctx_beacon_recv_ev(struct ath_softc *sc,
+					      struct sk_buff *skb,
+					      u32 ts,
 					      enum ath_chanctx_event ev)
 {
 }
diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c
index b369c48..e1602bc 100644
--- a/drivers/net/wireless/ath/ath9k/channel.c
+++ b/drivers/net/wireless/ath/ath9k/channel.c
@@ -296,6 +296,55 @@ static void ath_chanctx_setup_timer(struct ath_softc *sc, u32 tsf_time)
 	mod_timer(&sc->sched.timer, tsf_time);
 }
 
+/*
+ * This will be called only when multi-channel is enabled
+ * and since the max. number of interfaces is limited to two,
+ * a channel context is guaranteed to have only one interface.
+ * So, we can get the aid by iterating over the vif list in
+ * the current channel context.
+ */
+static void ath_chanctx_check_tim(struct ath_softc *sc,
+				  struct sk_buff *skb)
+{
+	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+	struct ieee80211_mgmt *mgmt;
+	struct ieee80211_vif *vif;
+	struct ath_vif *avp;
+	struct ieee80211_tim_ie *tim_ie;
+	const u8 *tim;
+	size_t ies_len;
+	u8 tim_len;
+	u8 *ies;
+
+	mgmt = (struct ieee80211_mgmt *)skb->data;
+	ies = mgmt->u.beacon.variable;
+	ies_len = (u8 *)skb_tail_pointer(skb) - ies;
+
+	tim = cfg80211_find_ie(WLAN_EID_TIM, ies, ies_len);
+	if (!tim)
+		return;
+
+	tim_len = tim[1];
+	tim_ie = (struct ieee80211_tim_ie *) &tim[2];
+
+	list_for_each_entry(avp, &sc->cur_chan->vifs, list) {
+		vif = avp->vif;
+
+		if (vif->type == NL80211_IFTYPE_STATION && vif->bss_conf.assoc) {
+			sc->sched.tim_set = ieee80211_check_tim(tim_ie,
+						tim_len, vif->bss_conf.aid);
+			if (sc->sched.tim_set) {
+				ath_dbg(common, CHAN_CTX,
+					"TIM bit set for aid: %d, vif: %pM\n",
+					vif->bss_conf.aid, vif->addr);
+			}
+			break;
+		}
+	}
+
+
+}
+
 void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
 		       enum ath_chanctx_event ev)
 {
@@ -427,7 +476,7 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
 			ath9k_hw_get_tsf_offset(&sc->cur_chan->tsf_ts, NULL);
 		tsf_time += ath9k_hw_gettsf32(ah);
 
-
+		ath_chanctx_check_tim(sc, sc->cur_chan->beacon_skb);
 		ath_chanctx_setup_timer(sc, tsf_time);
 		break;
 	case ATH_CHANCTX_EVENT_ASSOC:
@@ -521,10 +570,13 @@ void ath_chanctx_beacon_sent_ev(struct ath_softc *sc,
 		ath_chanctx_event(sc, NULL, ev);
 }
 
-void ath_chanctx_beacon_recv_ev(struct ath_softc *sc, u32 ts,
+void ath_chanctx_beacon_recv_ev(struct ath_softc *sc,
+				struct sk_buff *skb,
+				u32 ts,
 				enum ath_chanctx_event ev)
 {
 	sc->sched.next_tbtt = ts;
+	sc->cur_chan->beacon_skb = skb;
 	ath_chanctx_event(sc, NULL, ev);
 }
 
@@ -809,6 +861,16 @@ ath_chanctx_send_vif_ps_frame(struct ath_softc *sc, struct ath_vif *avp,
 		if (!vif->bss_conf.assoc)
 			return false;
 
+		/*
+		 * When we are coming out of PS, send a nullfunc
+		 * with PM bit cleared only when we know there is
+		 * buffered traffic at the AP.
+		 */
+		if (!powersave) {
+			if (!sc->sched.tim_set)
+				return false;
+		}
+
 		skb = ieee80211_nullfunc_get(sc->hw, vif);
 		if (!skb)
 			return false;
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index 2aaf233..1edf8cb 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -892,12 +892,6 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc,
 		return -EINVAL;
 	}
 
-	if (ath9k_is_chanctx_enabled()) {
-		if (rx_stats->is_mybeacon)
-			ath_chanctx_beacon_recv_ev(sc, rx_stats->rs_tstamp,
-					   ATH_CHANCTX_EVENT_BEACON_RECEIVED);
-	}
-
 	ath9k_cmn_process_rssi(common, hw, rx_stats, rx_status);
 
 	rx_status->band = ah->curchan->chan->band;
@@ -1117,6 +1111,12 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
 			ath_rx_ps(sc, skb, rs.is_mybeacon);
 		spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
 
+		if (ath9k_is_chanctx_enabled()) {
+			if (rs.is_mybeacon)
+				ath_chanctx_beacon_recv_ev(sc, skb, rs.rs_tstamp,
+					   ATH_CHANCTX_EVENT_BEACON_RECEIVED);
+		}
+
 		ath9k_antenna_check(sc, &rs);
 		ath9k_apply_ampdu_details(sc, &rs, rxs);
 		ath_debug_rate_stats(sc, &rs, skb);
-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Wireless Personal Area Network]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite Hiking]     [MIPS Linux]     [ARM Linux]     [Linux RAID]

  Powered by Linux