diff options
Diffstat (limited to 'sys/dev/ath/if_ath.c')
-rw-r--r-- | sys/dev/ath/if_ath.c | 162 |
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) |