In preparation for supporting 11n TX/RX properly, allow for TX queue draining

and interface resets to be marked as ATH_RESET_DEFAULT, ATH_RESET_FULL,
ATH_RESET_NOLOSS.

Currently a reset is still a reset - ie, all tx/rx frames in the hardware
queues are purged. This means that those frames will be lost to the 11n TX
and RX aggregation state tracking, breaking AMPDU sessions.

The (eventual) new semantics:

* ATH_RESET_DEFAULT:
      full reset, this is the default for reset situations
      which I haven't yet figured out what they should be.
* ATH_RESET_FULL:
      A full reset - for things such as channel changes.
* ATH_RESET_NOLOSS:
      Don't flush TX/RX queues - handle pending RX frames and leave TX
      frames where they are; restart TX DMA from where it was.
This commit is contained in:
Adrian Chadd 2011-11-08 18:56:52 +00:00
parent 4afa805ec7
commit 517526efe8
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=227353
3 changed files with 21 additions and 17 deletions

View File

@ -184,7 +184,7 @@ static void ath_tx_proc_q0123(void *, int);
static void ath_tx_proc(void *, int);
static void ath_tx_draintxq(struct ath_softc *, struct ath_txq *);
static int ath_chan_set(struct ath_softc *, struct ieee80211_channel *);
static void ath_draintxq(struct ath_softc *);
static void ath_draintxq(struct ath_softc *, ATH_RESET_TYPE reset_type);
static void ath_stoprecv(struct ath_softc *);
static int ath_startrecv(struct ath_softc *);
static void ath_chan_change(struct ath_softc *, struct ieee80211_channel *);
@ -1123,7 +1123,8 @@ ath_vap_delete(struct ieee80211vap *vap)
* the vap state by any frames pending on the tx queues.
*/
ath_hal_intrset(ah, 0); /* disable interrupts */
ath_draintxq(sc); /* stop xmit side */
ath_draintxq(sc, ATH_RESET_DEFAULT); /* stop hw xmit side */
/* XXX Do all frames from all vaps/nodes need draining here? */
ath_stoprecv(sc); /* stop recv side */
}
@ -1507,7 +1508,7 @@ ath_fatal_proc(void *arg, int pending)
state[0], state[1] , state[2], state[3],
state[4], state[5]);
}
ath_reset(ifp);
ath_reset(ifp, ATH_RESET_NOLOSS);
}
static void
@ -1567,7 +1568,7 @@ ath_bmiss_proc(void *arg, int pending)
if (ath_hal_gethangstate(sc->sc_ah, 0xff, &hangs) && hangs != 0) {
if_printf(ifp, "bb hang detected (0x%x), resetting\n", hangs);
ath_reset(ifp);
ath_reset(ifp, ATH_RESET_NOLOSS);
} else
ieee80211_beacon_miss(ifp->if_l2com);
}
@ -1747,7 +1748,7 @@ ath_stop_locked(struct ifnet *ifp)
}
ath_hal_intrset(ah, 0);
}
ath_draintxq(sc);
ath_draintxq(sc, ATH_RESET_DEFAULT);
if (!sc->sc_invalid) {
ath_stoprecv(sc);
ath_hal_phydisable(ah);
@ -1775,7 +1776,7 @@ ath_stop(struct ifnet *ifp)
* to reset or reload hardware state.
*/
int
ath_reset(struct ifnet *ifp)
ath_reset(struct ifnet *ifp, ATH_RESET_TYPE reset_type)
{
struct ath_softc *sc = ifp->if_softc;
struct ieee80211com *ic = ifp->if_l2com;
@ -1783,7 +1784,7 @@ ath_reset(struct ifnet *ifp)
HAL_STATUS status;
ath_hal_intrset(ah, 0); /* disable interrupts */
ath_draintxq(sc); /* stop xmit side */
ath_draintxq(sc, reset_type); /* stop xmit side */
ath_stoprecv(sc); /* stop recv side */
ath_settkipmic(sc); /* configure TKIP MIC handling */
/* NB: indicate channel change so we do a full reset */
@ -1836,7 +1837,8 @@ ath_reset_vap(struct ieee80211vap *vap, u_long cmd)
ath_hal_settxpowlimit(ah, ic->ic_txpowlimit);
return 0;
}
return ath_reset(ifp);
/* XXX? Full or NOLOSS? */
return ath_reset(ifp, ATH_RESET_FULL);
}
struct ath_buf *
@ -2730,7 +2732,7 @@ ath_bstuck_proc(void *arg, int pending)
if_printf(ifp, "stuck beacon; resetting (bmiss count %u)\n",
sc->sc_bmisscount);
sc->sc_stats.ast_bstuck++;
ath_reset(ifp);
ath_reset(ifp, ATH_RESET_NOLOSS);
}
/*
@ -4484,7 +4486,7 @@ ath_tx_stopdma(struct ath_softc *sc, struct ath_txq *txq)
* Drain the transmit queues and reclaim resources.
*/
static void
ath_draintxq(struct ath_softc *sc)
ath_draintxq(struct ath_softc *sc, ATH_RESET_TYPE reset_type)
{
struct ath_hal *ah = sc->sc_ah;
struct ifnet *ifp = sc->sc_ifp;
@ -4636,7 +4638,7 @@ ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan)
* the relevant bits of the h/w.
*/
ath_hal_intrset(ah, 0); /* disable interrupts */
ath_draintxq(sc); /* clear pending tx frames */
ath_draintxq(sc, ATH_RESET_FULL); /* clear pending tx frames */
ath_stoprecv(sc); /* turn off frame recv */
if (!ath_hal_reset(ah, sc->sc_opmode, chan, AH_TRUE, &status)) {
if_printf(ifp, "%s: unable to reset "
@ -4726,7 +4728,7 @@ ath_calibrate(void *arg)
DPRINTF(sc, ATH_DEBUG_CALIBRATE,
"%s: rfgain change\n", __func__);
sc->sc_stats.ast_per_rfgain++;
ath_reset(ifp);
ath_reset(ifp, ATH_RESET_NOLOSS);
}
/*
* If this long cal is after an idle period, then
@ -5392,7 +5394,7 @@ ath_watchdog(void *arg)
hangs & 0xff ? "bb" : "mac", hangs);
} else
if_printf(ifp, "device timeout\n");
ath_reset(ifp);
ath_reset(ifp, ATH_RESET_NOLOSS);
ifp->if_oerrors++;
sc->sc_stats.ast_watchdog++;
}

View File

@ -53,6 +53,6 @@ extern int ath_tx_findrix(const struct ath_softc *sc, uint8_t rate);
extern struct ath_buf * ath_getbuf(struct ath_softc *sc);
extern struct ath_buf * _ath_getbuf_locked(struct ath_softc *sc);
extern int ath_reset(struct ifnet *);
extern int ath_reset(struct ifnet *, ATH_RESET_TYPE);
#endif

View File

@ -263,7 +263,8 @@ ath_sysctl_tpscale(SYSCTL_HANDLER_ARGS)
if (error || !req->newptr)
return error;
return !ath_hal_settpscale(sc->sc_ah, scale) ? EINVAL :
(ifp->if_drv_flags & IFF_DRV_RUNNING) ? ath_reset(ifp) : 0;
(ifp->if_drv_flags & IFF_DRV_RUNNING) ?
ath_reset(ifp, ATH_RESET_NOLOSS) : 0;
}
static int
@ -295,7 +296,8 @@ ath_sysctl_rfkill(SYSCTL_HANDLER_ARGS)
return 0;
if (!ath_hal_setrfkill(ah, rfkill))
return EINVAL;
return (ifp->if_drv_flags & IFF_DRV_RUNNING) ? ath_reset(ifp) : 0;
return (ifp->if_drv_flags & IFF_DRV_RUNNING) ?
ath_reset(ifp, ATH_RESET_FULL) : 0;
}
static int
@ -428,7 +430,7 @@ ath_sysctl_intmit(SYSCTL_HANDLER_ARGS)
* things in an inconsistent state.
*/
if (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING)
ath_reset(sc->sc_ifp);
ath_reset(sc->sc_ifp, ATH_RESET_NOLOSS);
return 0;
}