summaryrefslogtreecommitdiffstats
path: root/sys/dev/ath/if_ath.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/ath/if_ath.c')
-rw-r--r--sys/dev/ath/if_ath.c162
1 files changed, 159 insertions, 3 deletions
diff --git a/sys/dev/ath/if_ath.c b/sys/dev/ath/if_ath.c
index 961a8b7..04d723a 100644
--- a/sys/dev/ath/if_ath.c
+++ b/sys/dev/ath/if_ath.c
@@ -125,7 +125,7 @@ __FBSDID("$FreeBSD$");
/*
* Only enable this if you're working on PS-POLL support.
*/
-#undef ATH_SW_PSQ
+#define ATH_SW_PSQ
/*
* ATH_BCBUF determines the number of vap's that can transmit
@@ -212,6 +212,7 @@ static void ath_announce(struct ath_softc *);
static void ath_dfs_tasklet(void *, int);
static void ath_node_powersave(struct ieee80211_node *, int);
static int ath_node_set_tim(struct ieee80211_node *, int);
+static void ath_node_recv_pspoll(struct ieee80211_node *, struct mbuf *);
#ifdef IEEE80211_SUPPORT_TDMA
#include <dev/ath/if_ath_tdma.h>
@@ -695,6 +696,11 @@ ath_attach(u_int16_t devid, struct ath_softc *sc)
sc->sc_txq_mcastq_maxdepth = ath_txbuf;
/*
+ * How deep can the node software TX queue get whilst it's asleep.
+ */
+ sc->sc_txq_node_psq_maxdepth = 16;
+
+ /*
* Default the maximum queue depth for a given node
* to 1/4'th the TX buffers, or 64, whichever
* is larger.
@@ -1248,6 +1254,9 @@ ath_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit,
avp->av_set_tim = vap->iv_set_tim;
vap->iv_set_tim = ath_node_set_tim;
+ avp->av_recv_pspoll = vap->iv_recv_pspoll;
+ vap->iv_recv_pspoll = ath_node_recv_pspoll;
+
/* Set default parameters */
/*
@@ -6169,9 +6178,11 @@ ath_tx_update_tim(struct ath_softc *sc, struct ieee80211_node *ni,
an->an_tim_set == 1 &&
an->an_swq_depth == 0) {
DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE,
- "%s: an=%p, swq_depth=0, tim_set=1, psq_set=0,"
+ "%s: %6D: swq_depth=0, tim_set=1, psq_set=0,"
" clear!\n",
- __func__, an);
+ __func__,
+ ni->ni_macaddr,
+ ":");
an->an_tim_set = 0;
(void) avp->av_set_tim(ni, 0);
}
@@ -6181,6 +6192,151 @@ ath_tx_update_tim(struct ath_softc *sc, struct ieee80211_node *ni,
#endif /* ATH_SW_PSQ */
}
+/*
+ * Received a ps-poll frame from net80211.
+ *
+ * Here we get a chance to serve out a software-queued frame ourselves
+ * before we punt it to net80211 to transmit us one itself - either
+ * because there's traffic in the net80211 psq, or a NULL frame to
+ * indicate there's nothing else.
+ */
+static void
+ath_node_recv_pspoll(struct ieee80211_node *ni, struct mbuf *m)
+{
+#ifdef ATH_SW_PSQ
+ struct ath_node *an;
+ struct ath_vap *avp;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ath_softc *sc = ic->ic_ifp->if_softc;
+ int tid;
+
+ /* Just paranoia */
+ if (ni == NULL)
+ return;
+
+ /*
+ * Unassociated (temporary node) station.
+ */
+ if (ni->ni_associd == 0)
+ return;
+
+ /*
+ * We do have an active node, so let's begin looking into it.
+ */
+ an = ATH_NODE(ni);
+ avp = ATH_VAP(ni->ni_vap);
+
+ /*
+ * For now, we just call the original ps-poll method.
+ * Once we're ready to flip this on:
+ *
+ * + Set leak to 1, as no matter what we're going to have
+ * to send a frame;
+ * + Check the software queue and if there's something in it,
+ * schedule the highest TID thas has traffic from this node.
+ * Then make sure we schedule the software scheduler to
+ * run so it picks up said frame.
+ *
+ * That way whatever happens, we'll at least send _a_ frame
+ * to the given node.
+ *
+ * Again, yes, it's crappy QoS if the node has multiple
+ * TIDs worth of traffic - but let's get it working first
+ * before we optimise it.
+ *
+ * Also yes, there's definitely latency here - we're not
+ * direct dispatching to the hardware in this path (and
+ * we're likely being called from the packet receive path,
+ * so going back into TX may be a little hairy!) but again
+ * I'd like to get this working first before optimising
+ * turn-around time.
+ */
+
+ ATH_TX_LOCK(sc);
+
+ /*
+ * Legacy - we're called and the node isn't asleep.
+ * Immediately punt.
+ */
+ if (! an->an_is_powersave) {
+ device_printf(sc->sc_dev,
+ "%s: %6D: not in powersave?\n",
+ __func__,
+ ni->ni_macaddr,
+ ":");
+ ATH_TX_UNLOCK(sc);
+ avp->av_recv_pspoll(ni, m);
+ return;
+ }
+
+ /*
+ * We're in powersave.
+ *
+ * Leak a frame.
+ */
+ an->an_leak_count = 1;
+
+ /*
+ * Now, if there's no frames in the node, just punt to
+ * recv_pspoll.
+ *
+ * Don't bother checking if the TIM bit is set, we really
+ * only care if there are any frames here!
+ */
+ if (an->an_swq_depth == 0) {
+ ATH_TX_UNLOCK(sc);
+ DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE,
+ "%s: %6D: SWQ empty; punting to net80211\n",
+ __func__,
+ ni->ni_macaddr,
+ ":");
+ avp->av_recv_pspoll(ni, m);
+ return;
+ }
+
+ /*
+ * Ok, let's schedule the highest TID that has traffic
+ * and then schedule something.
+ */
+ for (tid = IEEE80211_TID_SIZE - 1; tid >= 0; tid--) {
+ struct ath_tid *atid = &an->an_tid[tid];
+ /*
+ * No frames? Skip.
+ */
+ if (atid->axq_depth == 0)
+ continue;
+ ath_tx_tid_sched(sc, atid);
+ /*
+ * XXX we could do a direct call to the TXQ
+ * scheduler code here to optimise latency
+ * at the expense of a REALLY deep callstack.
+ */
+ ATH_TX_UNLOCK(sc);
+ taskqueue_enqueue(sc->sc_tq, &sc->sc_txqtask);
+ DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE,
+ "%s: %6D: leaking frame to TID %d\n",
+ __func__,
+ ni->ni_macaddr,
+ ":",
+ tid);
+ return;
+ }
+
+ ATH_TX_UNLOCK(sc);
+
+ /*
+ * XXX nothing in the TIDs at this point? Eek.
+ */
+ device_printf(sc->sc_dev, "%s: %6D: TIDs empty, but ath_node showed traffic?!\n",
+ __func__,
+ ni->ni_macaddr,
+ ":");
+ avp->av_recv_pspoll(ni, m);
+#else
+ avp->av_recv_pspoll(ni, m);
+#endif /* ATH_SW_PSQ */
+}
+
MODULE_VERSION(if_ath, 1);
MODULE_DEPEND(if_ath, wlan, 1, 1, 1); /* 802.11 media layer */
#if defined(IEEE80211_ALQ) || defined(AH_DEBUG_ALQ)
OpenPOWER on IntegriCloud