2011-03-02 16:03:19 +00:00
|
|
|
/*-
|
|
|
|
* Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer,
|
|
|
|
* without modification.
|
|
|
|
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
|
|
|
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
|
|
|
|
* redistribution must be conditioned upon including a substantially
|
|
|
|
* similar Disclaimer requirement for further binary redistribution.
|
|
|
|
*
|
|
|
|
* NO WARRANTY
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
|
|
|
|
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
|
|
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
|
|
|
|
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
|
|
|
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
|
|
|
* THE POSSIBILITY OF SUCH DAMAGES.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/cdefs.h>
|
|
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Driver for the Atheros Wireless LAN controller.
|
|
|
|
*
|
|
|
|
* This software is derived from work of Atsushi Onoe; his contribution
|
|
|
|
* is greatly appreciated.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "opt_inet.h"
|
|
|
|
#include "opt_ath.h"
|
|
|
|
#include "opt_wlan.h"
|
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/sysctl.h>
|
|
|
|
#include <sys/mbuf.h>
|
|
|
|
#include <sys/malloc.h>
|
|
|
|
#include <sys/lock.h>
|
|
|
|
#include <sys/mutex.h>
|
|
|
|
#include <sys/kernel.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/sockio.h>
|
|
|
|
#include <sys/errno.h>
|
|
|
|
#include <sys/callout.h>
|
|
|
|
#include <sys/bus.h>
|
|
|
|
#include <sys/endian.h>
|
|
|
|
#include <sys/kthread.h>
|
|
|
|
#include <sys/taskqueue.h>
|
|
|
|
#include <sys/priv.h>
|
|
|
|
|
|
|
|
#include <machine/bus.h>
|
|
|
|
|
|
|
|
#include <net/if.h>
|
|
|
|
#include <net/if_dl.h>
|
|
|
|
#include <net/if_media.h>
|
|
|
|
#include <net/if_types.h>
|
|
|
|
#include <net/if_arp.h>
|
|
|
|
#include <net/ethernet.h>
|
|
|
|
#include <net/if_llc.h>
|
|
|
|
|
|
|
|
#include <net80211/ieee80211_var.h>
|
|
|
|
#include <net80211/ieee80211_regdomain.h>
|
|
|
|
#ifdef IEEE80211_SUPPORT_SUPERG
|
|
|
|
#include <net80211/ieee80211_superg.h>
|
|
|
|
#endif
|
|
|
|
#ifdef IEEE80211_SUPPORT_TDMA
|
|
|
|
#include <net80211/ieee80211_tdma.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <net/bpf.h>
|
|
|
|
|
|
|
|
#ifdef INET
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <netinet/if_ether.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <dev/ath/if_athvar.h>
|
|
|
|
#include <dev/ath/ath_hal/ah_devid.h> /* XXX for softled */
|
|
|
|
#include <dev/ath/ath_hal/ah_diagcodes.h>
|
|
|
|
|
|
|
|
#include <dev/ath/if_ath_debug.h>
|
2011-12-26 05:46:22 +00:00
|
|
|
#include <dev/ath/if_ath_led.h>
|
2011-03-02 16:03:19 +00:00
|
|
|
#include <dev/ath/if_ath_misc.h>
|
|
|
|
#include <dev/ath/if_ath_tx.h>
|
|
|
|
#include <dev/ath/if_ath_sysctl.h>
|
|
|
|
|
|
|
|
#ifdef ATH_TX99_DIAG
|
|
|
|
#include <dev/ath/ath_tx99/ath_tx99.h>
|
|
|
|
#endif
|
|
|
|
|
2012-11-08 23:11:59 +00:00
|
|
|
#ifdef ATH_DEBUG_ALQ
|
|
|
|
#include <dev/ath/if_ath_alq.h>
|
|
|
|
#endif
|
|
|
|
|
2011-03-02 16:03:19 +00:00
|
|
|
static int
|
|
|
|
ath_sysctl_slottime(SYSCTL_HANDLER_ARGS)
|
|
|
|
{
|
|
|
|
struct ath_softc *sc = arg1;
|
|
|
|
u_int slottime = ath_hal_getslottime(sc->sc_ah);
|
|
|
|
int error;
|
|
|
|
|
|
|
|
error = sysctl_handle_int(oidp, &slottime, 0, req);
|
|
|
|
if (error || !req->newptr)
|
|
|
|
return error;
|
|
|
|
return !ath_hal_setslottime(sc->sc_ah, slottime) ? EINVAL : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ath_sysctl_acktimeout(SYSCTL_HANDLER_ARGS)
|
|
|
|
{
|
|
|
|
struct ath_softc *sc = arg1;
|
|
|
|
u_int acktimeout = ath_hal_getacktimeout(sc->sc_ah);
|
|
|
|
int error;
|
|
|
|
|
|
|
|
error = sysctl_handle_int(oidp, &acktimeout, 0, req);
|
|
|
|
if (error || !req->newptr)
|
|
|
|
return error;
|
|
|
|
return !ath_hal_setacktimeout(sc->sc_ah, acktimeout) ? EINVAL : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ath_sysctl_ctstimeout(SYSCTL_HANDLER_ARGS)
|
|
|
|
{
|
|
|
|
struct ath_softc *sc = arg1;
|
|
|
|
u_int ctstimeout = ath_hal_getctstimeout(sc->sc_ah);
|
|
|
|
int error;
|
|
|
|
|
|
|
|
error = sysctl_handle_int(oidp, &ctstimeout, 0, req);
|
|
|
|
if (error || !req->newptr)
|
|
|
|
return error;
|
|
|
|
return !ath_hal_setctstimeout(sc->sc_ah, ctstimeout) ? EINVAL : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ath_sysctl_softled(SYSCTL_HANDLER_ARGS)
|
|
|
|
{
|
|
|
|
struct ath_softc *sc = arg1;
|
|
|
|
int softled = sc->sc_softled;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
error = sysctl_handle_int(oidp, &softled, 0, req);
|
|
|
|
if (error || !req->newptr)
|
|
|
|
return error;
|
|
|
|
softled = (softled != 0);
|
|
|
|
if (softled != sc->sc_softled) {
|
|
|
|
if (softled) {
|
|
|
|
/* NB: handle any sc_ledpin change */
|
2011-12-26 05:46:22 +00:00
|
|
|
ath_led_config(sc);
|
2011-03-02 16:03:19 +00:00
|
|
|
}
|
|
|
|
sc->sc_softled = softled;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ath_sysctl_ledpin(SYSCTL_HANDLER_ARGS)
|
|
|
|
{
|
|
|
|
struct ath_softc *sc = arg1;
|
|
|
|
int ledpin = sc->sc_ledpin;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
error = sysctl_handle_int(oidp, &ledpin, 0, req);
|
|
|
|
if (error || !req->newptr)
|
|
|
|
return error;
|
|
|
|
if (ledpin != sc->sc_ledpin) {
|
|
|
|
sc->sc_ledpin = ledpin;
|
|
|
|
if (sc->sc_softled) {
|
2011-12-26 05:46:22 +00:00
|
|
|
ath_led_config(sc);
|
2011-03-02 16:03:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
Flesh out configurable hardware based LED blinking.
The hardware (MAC) LED blinking involves a few things:
* Selecting which GPIO pins map to the MAC "power" and "network" lines;
* Configuring the MAC LED state (associated, scanning, idle);
* Configuring the MAC LED blinking type and speed.
The AR5416 HAL configures the normal blinking setup - ie, blink rate based
on TX/RX throughput. The default AR5212 HAL doesn't program in any
specific blinking type, but the default of 0 is the same.
This code introduces a few things:
* The hardware led override is configured via sysctl 'hardled';
* The MAC network and power LED GPIO lines can be set, or left at -1
if needed. This is intended to allow only one of the hardware MUX
entries to be configured (eg for PCIe cards which only have one LED
exposed.)
TODO:
* For AR2417, the software LED blinking involves software blinking the
Network LED. For the AR5416 and later, this can just be configured
as a GPIO output line. I'll chase that up with a subsequent commit.
* Add another software LED blink for "Link", separate from "activity",
which blinks based on the association state. This would make my
D-Link DWA-552 have consistent and useful LED behaviour (as they're
marked "Link" and "Activity."
* Don't expose the hardware LED override unless it's an AR5416 or later,
as the previous generation hardware doesn't have this multiplexing
setup.
2011-12-26 07:47:05 +00:00
|
|
|
static int
|
|
|
|
ath_sysctl_hardled(SYSCTL_HANDLER_ARGS)
|
|
|
|
{
|
|
|
|
struct ath_softc *sc = arg1;
|
|
|
|
int hardled = sc->sc_hardled;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
error = sysctl_handle_int(oidp, &hardled, 0, req);
|
|
|
|
if (error || !req->newptr)
|
|
|
|
return error;
|
|
|
|
hardled = (hardled != 0);
|
|
|
|
if (hardled != sc->sc_hardled) {
|
|
|
|
if (hardled) {
|
|
|
|
/* NB: handle any sc_ledpin change */
|
|
|
|
ath_led_config(sc);
|
|
|
|
}
|
|
|
|
sc->sc_hardled = hardled;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-03-02 16:03:19 +00:00
|
|
|
static int
|
|
|
|
ath_sysctl_txantenna(SYSCTL_HANDLER_ARGS)
|
|
|
|
{
|
|
|
|
struct ath_softc *sc = arg1;
|
|
|
|
u_int txantenna = ath_hal_getantennaswitch(sc->sc_ah);
|
|
|
|
int error;
|
|
|
|
|
|
|
|
error = sysctl_handle_int(oidp, &txantenna, 0, req);
|
|
|
|
if (!error && req->newptr) {
|
|
|
|
/* XXX assumes 2 antenna ports */
|
|
|
|
if (txantenna < HAL_ANT_VARIABLE || txantenna > HAL_ANT_FIXED_B)
|
|
|
|
return EINVAL;
|
|
|
|
ath_hal_setantennaswitch(sc->sc_ah, txantenna);
|
|
|
|
/*
|
|
|
|
* NB: with the switch locked this isn't meaningful,
|
|
|
|
* but set it anyway so things like radiotap get
|
|
|
|
* consistent info in their data.
|
|
|
|
*/
|
|
|
|
sc->sc_txantenna = txantenna;
|
|
|
|
}
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ath_sysctl_rxantenna(SYSCTL_HANDLER_ARGS)
|
|
|
|
{
|
|
|
|
struct ath_softc *sc = arg1;
|
|
|
|
u_int defantenna = ath_hal_getdefantenna(sc->sc_ah);
|
|
|
|
int error;
|
|
|
|
|
|
|
|
error = sysctl_handle_int(oidp, &defantenna, 0, req);
|
|
|
|
if (!error && req->newptr)
|
|
|
|
ath_hal_setdefantenna(sc->sc_ah, defantenna);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ath_sysctl_diversity(SYSCTL_HANDLER_ARGS)
|
|
|
|
{
|
|
|
|
struct ath_softc *sc = arg1;
|
|
|
|
u_int diversity = ath_hal_getdiversity(sc->sc_ah);
|
|
|
|
int error;
|
|
|
|
|
|
|
|
error = sysctl_handle_int(oidp, &diversity, 0, req);
|
|
|
|
if (error || !req->newptr)
|
|
|
|
return error;
|
|
|
|
if (!ath_hal_setdiversity(sc->sc_ah, diversity))
|
|
|
|
return EINVAL;
|
|
|
|
sc->sc_diversity = diversity;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ath_sysctl_diag(SYSCTL_HANDLER_ARGS)
|
|
|
|
{
|
|
|
|
struct ath_softc *sc = arg1;
|
|
|
|
u_int32_t diag;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if (!ath_hal_getdiag(sc->sc_ah, &diag))
|
|
|
|
return EINVAL;
|
|
|
|
error = sysctl_handle_int(oidp, &diag, 0, req);
|
|
|
|
if (error || !req->newptr)
|
|
|
|
return error;
|
|
|
|
return !ath_hal_setdiag(sc->sc_ah, diag) ? EINVAL : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ath_sysctl_tpscale(SYSCTL_HANDLER_ARGS)
|
|
|
|
{
|
|
|
|
struct ath_softc *sc = arg1;
|
|
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
|
|
u_int32_t scale;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
(void) ath_hal_gettpscale(sc->sc_ah, &scale);
|
|
|
|
error = sysctl_handle_int(oidp, &scale, 0, req);
|
|
|
|
if (error || !req->newptr)
|
|
|
|
return error;
|
|
|
|
return !ath_hal_settpscale(sc->sc_ah, scale) ? EINVAL :
|
2011-11-08 18:56:52 +00:00
|
|
|
(ifp->if_drv_flags & IFF_DRV_RUNNING) ?
|
|
|
|
ath_reset(ifp, ATH_RESET_NOLOSS) : 0;
|
2011-03-02 16:03:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ath_sysctl_tpc(SYSCTL_HANDLER_ARGS)
|
|
|
|
{
|
|
|
|
struct ath_softc *sc = arg1;
|
|
|
|
u_int tpc = ath_hal_gettpc(sc->sc_ah);
|
|
|
|
int error;
|
|
|
|
|
|
|
|
error = sysctl_handle_int(oidp, &tpc, 0, req);
|
|
|
|
if (error || !req->newptr)
|
|
|
|
return error;
|
|
|
|
return !ath_hal_settpc(sc->sc_ah, tpc) ? EINVAL : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ath_sysctl_rfkill(SYSCTL_HANDLER_ARGS)
|
|
|
|
{
|
|
|
|
struct ath_softc *sc = arg1;
|
|
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
|
|
struct ath_hal *ah = sc->sc_ah;
|
|
|
|
u_int rfkill = ath_hal_getrfkill(ah);
|
|
|
|
int error;
|
|
|
|
|
|
|
|
error = sysctl_handle_int(oidp, &rfkill, 0, req);
|
|
|
|
if (error || !req->newptr)
|
|
|
|
return error;
|
|
|
|
if (rfkill == ath_hal_getrfkill(ah)) /* unchanged */
|
|
|
|
return 0;
|
|
|
|
if (!ath_hal_setrfkill(ah, rfkill))
|
|
|
|
return EINVAL;
|
2011-11-08 18:56:52 +00:00
|
|
|
return (ifp->if_drv_flags & IFF_DRV_RUNNING) ?
|
|
|
|
ath_reset(ifp, ATH_RESET_FULL) : 0;
|
2011-03-02 16:03:19 +00:00
|
|
|
}
|
|
|
|
|
2011-11-08 02:12:11 +00:00
|
|
|
static int
|
|
|
|
ath_sysctl_txagg(SYSCTL_HANDLER_ARGS)
|
|
|
|
{
|
|
|
|
struct ath_softc *sc = arg1;
|
|
|
|
int i, t, param = 0;
|
|
|
|
int error;
|
|
|
|
struct ath_buf *bf;
|
|
|
|
|
|
|
|
error = sysctl_handle_int(oidp, ¶m, 0, req);
|
|
|
|
if (error || !req->newptr)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
if (param != 1)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
printf("no tx bufs (empty list): %d\n", sc->sc_stats.ast_tx_getnobuf);
|
|
|
|
printf("no tx bufs (was busy): %d\n", sc->sc_stats.ast_tx_getbusybuf);
|
|
|
|
|
|
|
|
printf("aggr single packet: %d\n",
|
|
|
|
sc->sc_aggr_stats.aggr_single_pkt);
|
|
|
|
printf("aggr single packet w/ BAW closed: %d\n",
|
|
|
|
sc->sc_aggr_stats.aggr_baw_closed_single_pkt);
|
|
|
|
printf("aggr non-baw packet: %d\n",
|
|
|
|
sc->sc_aggr_stats.aggr_nonbaw_pkt);
|
|
|
|
printf("aggr aggregate packet: %d\n",
|
|
|
|
sc->sc_aggr_stats.aggr_aggr_pkt);
|
|
|
|
printf("aggr single packet low hwq: %d\n",
|
|
|
|
sc->sc_aggr_stats.aggr_low_hwq_single_pkt);
|
2012-04-07 06:48:26 +01:00
|
|
|
printf("aggr single packet RTS aggr limited: %d\n",
|
|
|
|
sc->sc_aggr_stats.aggr_rts_aggr_limited);
|
2011-11-08 02:12:11 +00:00
|
|
|
printf("aggr sched, no work: %d\n",
|
|
|
|
sc->sc_aggr_stats.aggr_sched_nopkt);
|
|
|
|
for (i = 0; i < 64; i++) {
|
|
|
|
printf("%2d: %10d ", i, sc->sc_aggr_stats.aggr_pkts[i]);
|
|
|
|
if (i % 4 == 3)
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
|
|
|
|
if (ATH_TXQ_SETUP(sc, i)) {
|
Implement "holding buffers" per TX queue rather than globally.
When working on TDMA, Sam Leffler found that the MAC DMA hardware
would re-read the last TX descriptor when getting ready to transmit
the next one. Thus the whole ATH_BUF_BUSY came into existance -
the descriptor must be left alone (very specifically the link pointer
must be maintained) until the hardware has moved onto the next frame.
He saw this in TDMA because the MAC would be frequently stopping during
active transmit (ie, when it wasn't its turn to transmit.)
Fast-forward to today. It turns out that this is a problem not with
a single MAC DMA instance, but with each QCU (from 0->9). They each
maintain separate descriptor pointers and will re-read the last
descriptor when starting to transmit the next.
So when your AP is busy transmitting from multiple TX queues, you'll
(more) frequently see one QCU stopped, waiting for a higher-priority QCU
to finsh transmitting, before it'll go ahead and continue. If you mess
up the descriptor (ie by freeing it) then you're short of luck.
Thanks to rpaulo for sticking with me whilst I diagnosed this issue
that he was quite reliably triggering in his environment.
This is a reimplementation; it doesn't have anything in common with
the ath9k or the Qualcomm Atheros reference driver.
Now - it in theory doesn't apply on the EDMA chips, as long as you
push one complete frame into the FIFO at a time. But the MAC can DMA
from a list of frames pushed into the hardware queue (ie, you concat
'n' frames together with link pointers, and then push the head pointer
into the TXQ FIFO.) Since that's likely how I'm going to implement
CABQ handling in hostap mode, it's likely that I will end up teaching
the EDMA TX completion code about busy buffers, just to be "sure"
this doesn't creep up.
Tested - iperf ap->sta and sta->ap (with both sides running this code):
* AR5416 STA
* AR9160/AR9220 hostap
To validate that it doesn't break the EDMA (FIFO) chips:
* AR9380, AR9485, AR9462 STA
Using iperf with the -S <tos byte decimal value> to set the TCP client
side DSCP bits, mapping to different TIDs and thus different TX queues.
TODO:
* Make this work on the EDMA chips, if we end up pushing lists of frames
to the hardware (eg how we eventually will handle cabq in hostap/ibss
mode.)
2013-03-14 06:20:02 +00:00
|
|
|
printf("HW TXQ %d: axq_depth=%d, axq_aggr_depth=%d, "
|
|
|
|
"axq_fifo_depth=%d, holdingbf=%p\n",
|
2011-11-08 02:12:11 +00:00
|
|
|
i,
|
|
|
|
sc->sc_txq[i].axq_depth,
|
2012-08-14 23:34:22 +01:00
|
|
|
sc->sc_txq[i].axq_aggr_depth,
|
Implement "holding buffers" per TX queue rather than globally.
When working on TDMA, Sam Leffler found that the MAC DMA hardware
would re-read the last TX descriptor when getting ready to transmit
the next one. Thus the whole ATH_BUF_BUSY came into existance -
the descriptor must be left alone (very specifically the link pointer
must be maintained) until the hardware has moved onto the next frame.
He saw this in TDMA because the MAC would be frequently stopping during
active transmit (ie, when it wasn't its turn to transmit.)
Fast-forward to today. It turns out that this is a problem not with
a single MAC DMA instance, but with each QCU (from 0->9). They each
maintain separate descriptor pointers and will re-read the last
descriptor when starting to transmit the next.
So when your AP is busy transmitting from multiple TX queues, you'll
(more) frequently see one QCU stopped, waiting for a higher-priority QCU
to finsh transmitting, before it'll go ahead and continue. If you mess
up the descriptor (ie by freeing it) then you're short of luck.
Thanks to rpaulo for sticking with me whilst I diagnosed this issue
that he was quite reliably triggering in his environment.
This is a reimplementation; it doesn't have anything in common with
the ath9k or the Qualcomm Atheros reference driver.
Now - it in theory doesn't apply on the EDMA chips, as long as you
push one complete frame into the FIFO at a time. But the MAC can DMA
from a list of frames pushed into the hardware queue (ie, you concat
'n' frames together with link pointers, and then push the head pointer
into the TXQ FIFO.) Since that's likely how I'm going to implement
CABQ handling in hostap mode, it's likely that I will end up teaching
the EDMA TX completion code about busy buffers, just to be "sure"
this doesn't creep up.
Tested - iperf ap->sta and sta->ap (with both sides running this code):
* AR5416 STA
* AR9160/AR9220 hostap
To validate that it doesn't break the EDMA (FIFO) chips:
* AR9380, AR9485, AR9462 STA
Using iperf with the -S <tos byte decimal value> to set the TCP client
side DSCP bits, mapping to different TIDs and thus different TX queues.
TODO:
* Make this work on the EDMA chips, if we end up pushing lists of frames
to the hardware (eg how we eventually will handle cabq in hostap/ibss
mode.)
2013-03-14 06:20:02 +00:00
|
|
|
sc->sc_txq[i].axq_fifo_depth,
|
|
|
|
sc->sc_txq[i].axq_holdingbf);
|
2011-11-08 02:12:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
i = t = 0;
|
|
|
|
ATH_TXBUF_LOCK(sc);
|
2011-11-08 17:08:12 +00:00
|
|
|
TAILQ_FOREACH(bf, &sc->sc_txbuf, bf_list) {
|
2011-11-08 02:12:11 +00:00
|
|
|
if (bf->bf_flags & ATH_BUF_BUSY) {
|
|
|
|
printf("Busy: %d\n", t);
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
t++;
|
|
|
|
}
|
|
|
|
ATH_TXBUF_UNLOCK(sc);
|
2012-06-14 01:51:53 +01:00
|
|
|
printf("Total TX buffers: %d; Total TX buffers busy: %d (%d)\n",
|
|
|
|
t, i, sc->sc_txbuf_cnt);
|
2011-11-08 02:12:11 +00:00
|
|
|
|
2012-06-13 07:57:55 +01:00
|
|
|
i = t = 0;
|
|
|
|
ATH_TXBUF_LOCK(sc);
|
|
|
|
TAILQ_FOREACH(bf, &sc->sc_txbuf_mgmt, bf_list) {
|
|
|
|
if (bf->bf_flags & ATH_BUF_BUSY) {
|
|
|
|
printf("Busy: %d\n", t);
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
t++;
|
|
|
|
}
|
|
|
|
ATH_TXBUF_UNLOCK(sc);
|
|
|
|
printf("Total mgmt TX buffers: %d; Total mgmt TX buffers busy: %d\n",
|
|
|
|
t, i);
|
|
|
|
|
2013-03-18 01:12:36 +00:00
|
|
|
ATH_RX_LOCK(sc);
|
|
|
|
for (i = 0; i < 2; i++) {
|
2013-03-18 02:29:57 +00:00
|
|
|
printf("%d: fifolen: %d/%d; head=%d; tail=%d\n",
|
2013-03-18 01:12:36 +00:00
|
|
|
i,
|
2013-03-18 02:29:57 +00:00
|
|
|
sc->sc_rxedma[i].m_fifo_depth,
|
2013-03-18 01:12:36 +00:00
|
|
|
sc->sc_rxedma[i].m_fifolen,
|
|
|
|
sc->sc_rxedma[i].m_fifo_head,
|
|
|
|
sc->sc_rxedma[i].m_fifo_tail);
|
|
|
|
}
|
|
|
|
i = 0;
|
|
|
|
TAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) {
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
printf("Total RX buffers in free list: %d buffers\n",
|
|
|
|
i);
|
|
|
|
ATH_RX_UNLOCK(sc);
|
|
|
|
|
2011-11-08 02:12:11 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-03-02 16:03:19 +00:00
|
|
|
static int
|
|
|
|
ath_sysctl_rfsilent(SYSCTL_HANDLER_ARGS)
|
|
|
|
{
|
|
|
|
struct ath_softc *sc = arg1;
|
|
|
|
u_int rfsilent;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
(void) ath_hal_getrfsilent(sc->sc_ah, &rfsilent);
|
|
|
|
error = sysctl_handle_int(oidp, &rfsilent, 0, req);
|
|
|
|
if (error || !req->newptr)
|
|
|
|
return error;
|
|
|
|
if (!ath_hal_setrfsilent(sc->sc_ah, rfsilent))
|
|
|
|
return EINVAL;
|
|
|
|
sc->sc_rfsilentpin = rfsilent & 0x1c;
|
|
|
|
sc->sc_rfsilentpol = (rfsilent & 0x2) != 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ath_sysctl_tpack(SYSCTL_HANDLER_ARGS)
|
|
|
|
{
|
|
|
|
struct ath_softc *sc = arg1;
|
|
|
|
u_int32_t tpack;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
(void) ath_hal_gettpack(sc->sc_ah, &tpack);
|
|
|
|
error = sysctl_handle_int(oidp, &tpack, 0, req);
|
|
|
|
if (error || !req->newptr)
|
|
|
|
return error;
|
|
|
|
return !ath_hal_settpack(sc->sc_ah, tpack) ? EINVAL : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ath_sysctl_tpcts(SYSCTL_HANDLER_ARGS)
|
|
|
|
{
|
|
|
|
struct ath_softc *sc = arg1;
|
|
|
|
u_int32_t tpcts;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
(void) ath_hal_gettpcts(sc->sc_ah, &tpcts);
|
|
|
|
error = sysctl_handle_int(oidp, &tpcts, 0, req);
|
|
|
|
if (error || !req->newptr)
|
|
|
|
return error;
|
|
|
|
return !ath_hal_settpcts(sc->sc_ah, tpcts) ? EINVAL : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ath_sysctl_intmit(SYSCTL_HANDLER_ARGS)
|
|
|
|
{
|
|
|
|
struct ath_softc *sc = arg1;
|
|
|
|
int intmit, error;
|
|
|
|
|
|
|
|
intmit = ath_hal_getintmit(sc->sc_ah);
|
|
|
|
error = sysctl_handle_int(oidp, &intmit, 0, req);
|
|
|
|
if (error || !req->newptr)
|
|
|
|
return error;
|
2011-07-30 00:55:17 +01:00
|
|
|
|
|
|
|
/* reusing error; 1 here means "good"; 0 means "fail" */
|
|
|
|
error = ath_hal_setintmit(sc->sc_ah, intmit);
|
|
|
|
if (! error)
|
|
|
|
return EINVAL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reset the hardware here - disabling ANI in the HAL
|
|
|
|
* doesn't reset ANI related registers, so it'll leave
|
|
|
|
* things in an inconsistent state.
|
|
|
|
*/
|
|
|
|
if (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING)
|
2011-11-08 18:56:52 +00:00
|
|
|
ath_reset(sc->sc_ifp, ATH_RESET_NOLOSS);
|
2011-07-30 00:55:17 +01:00
|
|
|
|
|
|
|
return 0;
|
2011-03-02 16:03:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef IEEE80211_SUPPORT_TDMA
|
|
|
|
static int
|
|
|
|
ath_sysctl_setcca(SYSCTL_HANDLER_ARGS)
|
|
|
|
{
|
|
|
|
struct ath_softc *sc = arg1;
|
|
|
|
int setcca, error;
|
|
|
|
|
|
|
|
setcca = sc->sc_setcca;
|
|
|
|
error = sysctl_handle_int(oidp, &setcca, 0, req);
|
|
|
|
if (error || !req->newptr)
|
|
|
|
return error;
|
|
|
|
sc->sc_setcca = (setcca != 0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif /* IEEE80211_SUPPORT_TDMA */
|
|
|
|
|
2011-11-08 02:12:11 +00:00
|
|
|
static int
|
|
|
|
ath_sysctl_forcebstuck(SYSCTL_HANDLER_ARGS)
|
|
|
|
{
|
|
|
|
struct ath_softc *sc = arg1;
|
|
|
|
int val = 0;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
error = sysctl_handle_int(oidp, &val, 0, req);
|
|
|
|
if (error || !req->newptr)
|
|
|
|
return error;
|
|
|
|
if (val == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
taskqueue_enqueue_fast(sc->sc_tq, &sc->sc_bstucktask);
|
|
|
|
val = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
Implement frame (data) transmission using if_transmit(), rather than
if_start().
This removes the overlapping data path TX from occuring, which
solves quite a number of the potential TX queue races in ath(4).
It doesn't fix the net80211 layer TX queue races and it doesn't
fix the raw TX path yet, but it's an important step towards this.
This hasn't dropped the TX performance in my testing; primarily
because now the TX path can quickly queue frames and continue
along processing.
This involves a few rather deep changes:
* Use the ath_buf as a queue placeholder for now, as we need to be
able to support queuing a list of mbufs (ie, when transmitting
fragments) and m_nextpkt can't be used here (because it's what is
joining the fragments together)
* if_transmit() now simply allocates the ath_buf and queues it to
a driver TX staging queue.
* TX is now moved into a taskqueue function.
* The TX taskqueue function now dequeues and transmits frames.
* Fragments are handled correctly here - as the current API passes
the fragment list as one mbuf list (joined with m_nextpkt) through
to the driver if_transmit().
* For the couple of places where ath_start() may be called (mostly
from net80211 when starting the VAP up again), just reimplement
it using the new enqueue and taskqueue methods.
What I don't like (about this work and the TX code in general):
* I'm using the same lock for the staging TX queue management and the
actual TX. This isn't required; I'm just being slack.
* I haven't yet moved TX to a separate taskqueue (but the taskqueue is
created); it's easy enough to do this later if necessary. I just need
to make sure it's a higher priority queue, so TX has the same
behaviour as it used to (where it would preempt existing RX..)
* I need to re-review the TX path a little more and make sure that
ieee80211_node_*() functions aren't called within the TX lock.
When queueing, I should just push failed frames into a queue and
when I'm wrapping up the TX code, unlock the TX lock and
call ieee80211_node_free() on each.
* It would be nice if I could hold the TX lock for the entire
TX and TX completion, rather than this release/re-acquire behaviour.
But that requires that I shuffle around the TX completion code
to handle actual ath_buf free and net80211 callback/free outside
of the TX lock. That's one of my next projects.
* the ic_raw_xmit() path doesn't use this yet - so it still has
sequencing problems with parallel, overlapping calls to the
data path. I'll fix this later.
Tested:
* Hostap - AR9280, AR9220
* STA - AR5212, AR9280, AR5416
2013-01-15 18:01:23 +00:00
|
|
|
static int
|
|
|
|
ath_sysctl_hangcheck(SYSCTL_HANDLER_ARGS)
|
|
|
|
{
|
|
|
|
struct ath_softc *sc = arg1;
|
|
|
|
int val = 0;
|
|
|
|
int error;
|
|
|
|
uint32_t mask = 0xffffffff;
|
|
|
|
uint32_t *sp;
|
|
|
|
uint32_t rsize;
|
|
|
|
struct ath_hal *ah = sc->sc_ah;
|
|
|
|
|
|
|
|
error = sysctl_handle_int(oidp, &val, 0, req);
|
|
|
|
if (error || !req->newptr)
|
|
|
|
return error;
|
|
|
|
if (val == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Do a hang check */
|
|
|
|
if (!ath_hal_getdiagstate(ah, HAL_DIAG_CHECK_HANGS,
|
|
|
|
&mask, sizeof(mask),
|
|
|
|
(void *) &sp, &rsize))
|
|
|
|
return (0);
|
|
|
|
device_printf(sc->sc_dev, "%s: sp=0x%08x\n", __func__, *sp);
|
|
|
|
|
|
|
|
val = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
2012-11-08 18:11:31 +00:00
|
|
|
|
2012-11-08 23:11:59 +00:00
|
|
|
#ifdef ATH_DEBUG_ALQ
|
2012-11-08 18:11:31 +00:00
|
|
|
static int
|
|
|
|
ath_sysctl_alq_log(SYSCTL_HANDLER_ARGS)
|
|
|
|
{
|
|
|
|
struct ath_softc *sc = arg1;
|
|
|
|
int error, enable;
|
|
|
|
|
|
|
|
enable = (sc->sc_alq.sc_alq_isactive);
|
|
|
|
|
|
|
|
error = sysctl_handle_int(oidp, &enable, 0, req);
|
|
|
|
if (error || !req->newptr)
|
|
|
|
return (error);
|
|
|
|
else if (enable)
|
|
|
|
error = if_ath_alq_start(&sc->sc_alq);
|
|
|
|
else
|
|
|
|
error = if_ath_alq_stop(&sc->sc_alq);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Attach the ALQ debugging if required.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
ath_sysctl_alq_attach(struct ath_softc *sc)
|
|
|
|
{
|
|
|
|
struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
|
|
|
|
struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
|
|
|
|
struct sysctl_oid_list *child = SYSCTL_CHILDREN(tree);
|
|
|
|
|
|
|
|
tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "alq", CTLFLAG_RD,
|
|
|
|
NULL, "Atheros ALQ logging parameters");
|
|
|
|
child = SYSCTL_CHILDREN(tree);
|
|
|
|
|
|
|
|
SYSCTL_ADD_STRING(ctx, child, OID_AUTO, "filename",
|
|
|
|
CTLFLAG_RW, sc->sc_alq.sc_alq_filename, 0, "ALQ filename");
|
|
|
|
|
|
|
|
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"enable", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
|
|
|
|
ath_sysctl_alq_log, "I", "");
|
|
|
|
|
|
|
|
SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"debugmask", CTLFLAG_RW, &sc->sc_alq.sc_alq_debug, 0,
|
|
|
|
"ALQ debug mask");
|
|
|
|
|
|
|
|
SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"numlost", CTLFLAG_RW, &sc->sc_alq.sc_alq_numlost, 0,
|
|
|
|
"number lost");
|
|
|
|
}
|
2012-11-08 23:11:59 +00:00
|
|
|
#endif /* ATH_DEBUG_ALQ */
|
2012-11-08 18:11:31 +00:00
|
|
|
|
2011-03-02 16:03:19 +00:00
|
|
|
void
|
|
|
|
ath_sysctlattach(struct ath_softc *sc)
|
|
|
|
{
|
|
|
|
struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
|
|
|
|
struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
|
|
|
|
struct ath_hal *ah = sc->sc_ah;
|
|
|
|
|
|
|
|
SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"countrycode", CTLFLAG_RD, &sc->sc_eecc, 0,
|
|
|
|
"EEPROM country code");
|
|
|
|
SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"regdomain", CTLFLAG_RD, &sc->sc_eerd, 0,
|
|
|
|
"EEPROM regdomain code");
|
2012-11-13 19:45:13 +00:00
|
|
|
#ifdef ATH_DEBUG
|
2012-05-16 00:39:37 +01:00
|
|
|
SYSCTL_ADD_QUAD(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"debug", CTLFLAG_RW, &sc->sc_debug,
|
2011-03-02 16:03:19 +00:00
|
|
|
"control debugging printfs");
|
2012-11-13 19:45:13 +00:00
|
|
|
#endif
|
|
|
|
#ifdef ATH_DEBUG_ALQ
|
2012-09-24 21:35:56 +01:00
|
|
|
SYSCTL_ADD_QUAD(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"ktrdebug", CTLFLAG_RW, &sc->sc_ktrdebug,
|
|
|
|
"control debugging KTR");
|
2012-11-08 23:11:59 +00:00
|
|
|
#endif /* ATH_DEBUG_ALQ */
|
2011-03-02 16:03:19 +00:00
|
|
|
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"slottime", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
|
|
|
|
ath_sysctl_slottime, "I", "802.11 slot time (us)");
|
|
|
|
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"acktimeout", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
|
|
|
|
ath_sysctl_acktimeout, "I", "802.11 ACK timeout (us)");
|
|
|
|
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"ctstimeout", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
|
|
|
|
ath_sysctl_ctstimeout, "I", "802.11 CTS timeout (us)");
|
Flesh out configurable hardware based LED blinking.
The hardware (MAC) LED blinking involves a few things:
* Selecting which GPIO pins map to the MAC "power" and "network" lines;
* Configuring the MAC LED state (associated, scanning, idle);
* Configuring the MAC LED blinking type and speed.
The AR5416 HAL configures the normal blinking setup - ie, blink rate based
on TX/RX throughput. The default AR5212 HAL doesn't program in any
specific blinking type, but the default of 0 is the same.
This code introduces a few things:
* The hardware led override is configured via sysctl 'hardled';
* The MAC network and power LED GPIO lines can be set, or left at -1
if needed. This is intended to allow only one of the hardware MUX
entries to be configured (eg for PCIe cards which only have one LED
exposed.)
TODO:
* For AR2417, the software LED blinking involves software blinking the
Network LED. For the AR5416 and later, this can just be configured
as a GPIO output line. I'll chase that up with a subsequent commit.
* Add another software LED blink for "Link", separate from "activity",
which blinks based on the association state. This would make my
D-Link DWA-552 have consistent and useful LED behaviour (as they're
marked "Link" and "Activity."
* Don't expose the hardware LED override unless it's an AR5416 or later,
as the previous generation hardware doesn't have this multiplexing
setup.
2011-12-26 07:47:05 +00:00
|
|
|
|
2011-03-02 16:03:19 +00:00
|
|
|
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"softled", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
|
|
|
|
ath_sysctl_softled, "I", "enable/disable software LED support");
|
|
|
|
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"ledpin", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
|
|
|
|
ath_sysctl_ledpin, "I", "GPIO pin connected to LED");
|
|
|
|
SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"ledon", CTLFLAG_RW, &sc->sc_ledon, 0,
|
|
|
|
"setting to turn LED on");
|
|
|
|
SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"ledidle", CTLFLAG_RW, &sc->sc_ledidle, 0,
|
|
|
|
"idle time for inactivity LED (ticks)");
|
Flesh out configurable hardware based LED blinking.
The hardware (MAC) LED blinking involves a few things:
* Selecting which GPIO pins map to the MAC "power" and "network" lines;
* Configuring the MAC LED state (associated, scanning, idle);
* Configuring the MAC LED blinking type and speed.
The AR5416 HAL configures the normal blinking setup - ie, blink rate based
on TX/RX throughput. The default AR5212 HAL doesn't program in any
specific blinking type, but the default of 0 is the same.
This code introduces a few things:
* The hardware led override is configured via sysctl 'hardled';
* The MAC network and power LED GPIO lines can be set, or left at -1
if needed. This is intended to allow only one of the hardware MUX
entries to be configured (eg for PCIe cards which only have one LED
exposed.)
TODO:
* For AR2417, the software LED blinking involves software blinking the
Network LED. For the AR5416 and later, this can just be configured
as a GPIO output line. I'll chase that up with a subsequent commit.
* Add another software LED blink for "Link", separate from "activity",
which blinks based on the association state. This would make my
D-Link DWA-552 have consistent and useful LED behaviour (as they're
marked "Link" and "Activity."
* Don't expose the hardware LED override unless it's an AR5416 or later,
as the previous generation hardware doesn't have this multiplexing
setup.
2011-12-26 07:47:05 +00:00
|
|
|
|
|
|
|
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"hardled", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
|
|
|
|
ath_sysctl_hardled, "I", "enable/disable hardware LED support");
|
|
|
|
/* XXX Laziness - configure pins, then flip hardled off/on */
|
|
|
|
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"led_net_pin", CTLFLAG_RW, &sc->sc_led_net_pin, 0,
|
|
|
|
"MAC Network LED pin, or -1 to disable");
|
|
|
|
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"led_pwr_pin", CTLFLAG_RW, &sc->sc_led_pwr_pin, 0,
|
|
|
|
"MAC Power LED pin, or -1 to disable");
|
|
|
|
|
2011-03-02 16:03:19 +00:00
|
|
|
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"txantenna", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
|
|
|
|
ath_sysctl_txantenna, "I", "antenna switch");
|
|
|
|
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"rxantenna", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
|
|
|
|
ath_sysctl_rxantenna, "I", "default/rx antenna");
|
|
|
|
if (ath_hal_hasdiversity(ah))
|
|
|
|
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"diversity", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
|
|
|
|
ath_sysctl_diversity, "I", "antenna diversity");
|
|
|
|
sc->sc_txintrperiod = ATH_TXINTR_PERIOD;
|
|
|
|
SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"txintrperiod", CTLFLAG_RW, &sc->sc_txintrperiod, 0,
|
|
|
|
"tx descriptor batching");
|
|
|
|
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"diag", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
|
|
|
|
ath_sysctl_diag, "I", "h/w diagnostic control");
|
|
|
|
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"tpscale", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
|
|
|
|
ath_sysctl_tpscale, "I", "tx power scaling");
|
|
|
|
if (ath_hal_hastpc(ah)) {
|
|
|
|
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"tpc", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
|
|
|
|
ath_sysctl_tpc, "I", "enable/disable per-packet TPC");
|
|
|
|
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"tpack", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
|
|
|
|
ath_sysctl_tpack, "I", "tx power for ack frames");
|
|
|
|
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"tpcts", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
|
|
|
|
ath_sysctl_tpcts, "I", "tx power for cts frames");
|
|
|
|
}
|
|
|
|
if (ath_hal_hasrfsilent(ah)) {
|
|
|
|
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"rfsilent", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
|
|
|
|
ath_sysctl_rfsilent, "I", "h/w RF silent config");
|
|
|
|
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"rfkill", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
|
|
|
|
ath_sysctl_rfkill, "I", "enable/disable RF kill switch");
|
|
|
|
}
|
2011-11-08 02:12:11 +00:00
|
|
|
|
|
|
|
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"txagg", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
|
|
|
|
ath_sysctl_txagg, "I", "");
|
|
|
|
|
|
|
|
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"forcebstuck", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
|
|
|
|
ath_sysctl_forcebstuck, "I", "");
|
|
|
|
|
Implement frame (data) transmission using if_transmit(), rather than
if_start().
This removes the overlapping data path TX from occuring, which
solves quite a number of the potential TX queue races in ath(4).
It doesn't fix the net80211 layer TX queue races and it doesn't
fix the raw TX path yet, but it's an important step towards this.
This hasn't dropped the TX performance in my testing; primarily
because now the TX path can quickly queue frames and continue
along processing.
This involves a few rather deep changes:
* Use the ath_buf as a queue placeholder for now, as we need to be
able to support queuing a list of mbufs (ie, when transmitting
fragments) and m_nextpkt can't be used here (because it's what is
joining the fragments together)
* if_transmit() now simply allocates the ath_buf and queues it to
a driver TX staging queue.
* TX is now moved into a taskqueue function.
* The TX taskqueue function now dequeues and transmits frames.
* Fragments are handled correctly here - as the current API passes
the fragment list as one mbuf list (joined with m_nextpkt) through
to the driver if_transmit().
* For the couple of places where ath_start() may be called (mostly
from net80211 when starting the VAP up again), just reimplement
it using the new enqueue and taskqueue methods.
What I don't like (about this work and the TX code in general):
* I'm using the same lock for the staging TX queue management and the
actual TX. This isn't required; I'm just being slack.
* I haven't yet moved TX to a separate taskqueue (but the taskqueue is
created); it's easy enough to do this later if necessary. I just need
to make sure it's a higher priority queue, so TX has the same
behaviour as it used to (where it would preempt existing RX..)
* I need to re-review the TX path a little more and make sure that
ieee80211_node_*() functions aren't called within the TX lock.
When queueing, I should just push failed frames into a queue and
when I'm wrapping up the TX code, unlock the TX lock and
call ieee80211_node_free() on each.
* It would be nice if I could hold the TX lock for the entire
TX and TX completion, rather than this release/re-acquire behaviour.
But that requires that I shuffle around the TX completion code
to handle actual ath_buf free and net80211 callback/free outside
of the TX lock. That's one of my next projects.
* the ic_raw_xmit() path doesn't use this yet - so it still has
sequencing problems with parallel, overlapping calls to the
data path. I'll fix this later.
Tested:
* Hostap - AR9280, AR9220
* STA - AR5212, AR9280, AR5416
2013-01-15 18:01:23 +00:00
|
|
|
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"hangcheck", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
|
|
|
|
ath_sysctl_hangcheck, "I", "");
|
|
|
|
|
2011-03-02 16:03:19 +00:00
|
|
|
if (ath_hal_hasintmit(ah)) {
|
|
|
|
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"intmit", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
|
|
|
|
ath_sysctl_intmit, "I", "interference mitigation");
|
|
|
|
}
|
|
|
|
sc->sc_monpass = HAL_RXERR_DECRYPT | HAL_RXERR_MIC;
|
|
|
|
SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"monpass", CTLFLAG_RW, &sc->sc_monpass, 0,
|
|
|
|
"mask of error frames to pass when monitoring");
|
2011-11-08 02:12:11 +00:00
|
|
|
|
|
|
|
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
2013-05-21 19:13:57 +01:00
|
|
|
"hwq_limit_nonaggr", CTLFLAG_RW, &sc->sc_hwq_limit_nonaggr, 0,
|
|
|
|
"Hardware non-AMPDU queue depth before software-queuing TX frames");
|
|
|
|
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"hwq_limit_aggr", CTLFLAG_RW, &sc->sc_hwq_limit_aggr, 0,
|
|
|
|
"Hardware AMPDU queue depth before software-queuing TX frames");
|
2011-11-08 02:12:11 +00:00
|
|
|
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"tid_hwq_lo", CTLFLAG_RW, &sc->sc_tid_hwq_lo, 0,
|
|
|
|
"");
|
|
|
|
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"tid_hwq_hi", CTLFLAG_RW, &sc->sc_tid_hwq_hi, 0,
|
|
|
|
"");
|
|
|
|
|
2013-02-21 06:18:40 +00:00
|
|
|
/* Aggregate length twiddles */
|
|
|
|
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
2013-02-21 06:38:49 +00:00
|
|
|
"aggr_limit", CTLFLAG_RW, &sc->sc_aggr_limit, 0,
|
|
|
|
"Maximum A-MPDU size, or 0 for 'default'");
|
2013-02-21 06:18:40 +00:00
|
|
|
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
2013-02-21 06:38:49 +00:00
|
|
|
"rts_aggr_limit", CTLFLAG_RW, &sc->sc_rts_aggr_limit, 0,
|
|
|
|
"Maximum A-MPDU size for RTS-protected frames, or '0' "
|
|
|
|
"for default");
|
|
|
|
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"delim_min_pad", CTLFLAG_RW, &sc->sc_delim_min_pad, 0,
|
|
|
|
"Enforce a minimum number of delimiters per A-MPDU "
|
|
|
|
" sub-frame");
|
2013-02-21 06:18:40 +00:00
|
|
|
|
2012-03-10 04:14:04 +00:00
|
|
|
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"txq_data_minfree", CTLFLAG_RW, &sc->sc_txq_data_minfree,
|
|
|
|
0, "Minimum free buffers before adding a data frame"
|
|
|
|
" to the TX queue");
|
|
|
|
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"txq_mcastq_maxdepth", CTLFLAG_RW,
|
|
|
|
&sc->sc_txq_mcastq_maxdepth, 0,
|
|
|
|
"Maximum buffer depth for multicast/broadcast frames");
|
2013-05-07 08:52:18 +01:00
|
|
|
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"txq_node_maxdepth", CTLFLAG_RW,
|
|
|
|
&sc->sc_txq_node_maxdepth, 0,
|
|
|
|
"Maximum buffer depth for a single node");
|
2012-03-10 04:14:04 +00:00
|
|
|
|
Overhaul the TXQ locking (again!) as part of some beacon/cabq timing
related issues.
Moving the TX locking under one lock made things easier to progress on
but it had one important side-effect - it increased the latency when
handling CABQ setup when sending beacons.
This commit introduces a bunch of new changes and a few unrelated changs
that are just easier to lump in here.
The aim is to have the CABQ locking separate from other locking.
The CABQ transmit path in the beacon process thus doesn't have to grab
the general TX lock, reducing lock contention/latency and making it
more likely that we'll make the beacon TX timing.
The second half of this commit is the CABQ related setup changes needed
for sane looking EDMA CABQ support. Right now the EDMA TX code naively
assumes that only one frame (MPDU or A-MPDU) is being pushed into each
FIFO slot. For the CABQ this isn't true - a whole list of frames is
being pushed in - and thus CABQ handling breaks very quickly.
The aim here is to setup the CABQ list and then push _that list_ to
the hardware for transmission. I can then extend the EDMA TX code
to stamp that list as being "one" FIFO entry (likely by tagging the
last buffer in that list as "FIFO END") so the EDMA TX completion code
correctly tracks things.
Major:
* Migrate the per-TXQ add/removal locking back to per-TXQ, rather than
a single lock.
* Leave the software queue side of things under the ATH_TX_LOCK lock,
(continuing) to serialise things as they are.
* Add a new function which is called whenever there's a beacon miss,
to print out some debugging. This is primarily designed to help
me figure out if the beacon miss events are due to a noisy environment,
issues with the PHY/MAC, or other.
* Move the CABQ setup/enable to occur _after_ all the VAPs have been
looked at. This means that for multiple VAPS in bursted mode, the
CABQ gets primed once all VAPs are checked, rather than being primed
on the first VAP and then having frames appended after this.
Minor:
* Add a (disabled) twiddle to let me enable/disable cabq traffic.
It's primarily there to let me easily debug what's going on with beacon
and CABQ setup/traffic; there's some DMA engine hangs which I'm finally
trying to trace down.
* Clear bf_next when flushing frames; it should quieten some warnings
that show up when a node goes away.
Tested:
* AR9280, STA/hostap, up to 4 vaps (staggered)
* AR5416, STA/hostap, up to 4 vaps (staggered)
TODO:
* (Lots) more AR9380 and later testing, as I may have missed something here.
* Leverage this to fix CABQ hanling for AR9380 and later chips.
* Force bursted beaconing on the chips that default to staggered beacons and
ensure the CABQ stuff is all sane (eg, the MORE bits that aren't being
correctly set when chaining descriptors.)
2013-03-24 00:03:12 +00:00
|
|
|
#if 0
|
|
|
|
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"cabq_enable", CTLFLAG_RW,
|
|
|
|
&sc->sc_cabq_enable, 0,
|
|
|
|
"Whether to transmit on the CABQ or not");
|
|
|
|
#endif
|
|
|
|
|
2011-03-02 16:03:19 +00:00
|
|
|
#ifdef IEEE80211_SUPPORT_TDMA
|
|
|
|
if (ath_hal_macversion(ah) > 0x78) {
|
|
|
|
sc->sc_tdmadbaprep = 2;
|
|
|
|
SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"dbaprep", CTLFLAG_RW, &sc->sc_tdmadbaprep, 0,
|
|
|
|
"TDMA DBA preparation time");
|
|
|
|
sc->sc_tdmaswbaprep = 10;
|
|
|
|
SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"swbaprep", CTLFLAG_RW, &sc->sc_tdmaswbaprep, 0,
|
|
|
|
"TDMA SWBA preparation time");
|
|
|
|
SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"guardtime", CTLFLAG_RW, &sc->sc_tdmaguard, 0,
|
|
|
|
"TDMA slot guard time");
|
|
|
|
SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"superframe", CTLFLAG_RD, &sc->sc_tdmabintval, 0,
|
|
|
|
"TDMA calculated super frame");
|
|
|
|
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"setcca", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
|
|
|
|
ath_sysctl_setcca, "I", "enable CCA control");
|
|
|
|
}
|
|
|
|
#endif
|
2012-11-08 18:11:31 +00:00
|
|
|
|
2012-11-10 15:21:39 +00:00
|
|
|
#ifdef ATH_DEBUG_ALQ
|
2012-11-08 18:11:31 +00:00
|
|
|
ath_sysctl_alq_attach(sc);
|
|
|
|
#endif
|
2011-03-02 16:03:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ath_sysctl_clearstats(SYSCTL_HANDLER_ARGS)
|
|
|
|
{
|
|
|
|
struct ath_softc *sc = arg1;
|
|
|
|
int val = 0;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
error = sysctl_handle_int(oidp, &val, 0, req);
|
|
|
|
if (error || !req->newptr)
|
|
|
|
return error;
|
|
|
|
if (val == 0)
|
|
|
|
return 0; /* Not clearing the stats is still valid */
|
|
|
|
memset(&sc->sc_stats, 0, sizeof(sc->sc_stats));
|
2011-11-08 02:12:11 +00:00
|
|
|
memset(&sc->sc_aggr_stats, 0, sizeof(sc->sc_aggr_stats));
|
2012-04-10 08:23:37 +01:00
|
|
|
memset(&sc->sc_intr_stats, 0, sizeof(sc->sc_intr_stats));
|
2011-11-08 02:12:11 +00:00
|
|
|
|
2011-03-02 16:03:19 +00:00
|
|
|
val = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
ath_sysctl_stats_attach_rxphyerr(struct ath_softc *sc, struct sysctl_oid_list *parent)
|
|
|
|
{
|
|
|
|
struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
|
|
|
|
struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
|
|
|
|
struct sysctl_oid_list *child = SYSCTL_CHILDREN(tree);
|
|
|
|
int i;
|
|
|
|
char sn[8];
|
|
|
|
|
|
|
|
tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "rx_phy_err", CTLFLAG_RD, NULL, "Per-code RX PHY Errors");
|
|
|
|
child = SYSCTL_CHILDREN(tree);
|
|
|
|
for (i = 0; i < 64; i++) {
|
|
|
|
snprintf(sn, sizeof(sn), "%d", i);
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, sn, CTLFLAG_RD, &sc->sc_stats.ast_rx_phy[i], 0, "");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-10 08:23:37 +01:00
|
|
|
static void
|
|
|
|
ath_sysctl_stats_attach_intr(struct ath_softc *sc,
|
|
|
|
struct sysctl_oid_list *parent)
|
|
|
|
{
|
|
|
|
struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
|
|
|
|
struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
|
|
|
|
struct sysctl_oid_list *child = SYSCTL_CHILDREN(tree);
|
|
|
|
int i;
|
|
|
|
char sn[8];
|
|
|
|
|
|
|
|
tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "sync_intr",
|
|
|
|
CTLFLAG_RD, NULL, "Sync interrupt statistics");
|
|
|
|
child = SYSCTL_CHILDREN(tree);
|
|
|
|
for (i = 0; i < 32; i++) {
|
|
|
|
snprintf(sn, sizeof(sn), "%d", i);
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, sn, CTLFLAG_RD,
|
|
|
|
&sc->sc_intr_stats.sync_intr[i], 0, "");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-03-02 16:03:19 +00:00
|
|
|
void
|
|
|
|
ath_sysctl_stats_attach(struct ath_softc *sc)
|
|
|
|
{
|
|
|
|
struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
|
|
|
|
struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
|
|
|
|
struct sysctl_oid_list *child = SYSCTL_CHILDREN(tree);
|
|
|
|
|
|
|
|
/* Create "clear" node */
|
|
|
|
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"clear_stats", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
|
|
|
|
ath_sysctl_clearstats, "I", "clear stats");
|
|
|
|
|
|
|
|
/* Create stats node */
|
|
|
|
tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", CTLFLAG_RD,
|
|
|
|
NULL, "Statistics");
|
|
|
|
child = SYSCTL_CHILDREN(tree);
|
|
|
|
|
|
|
|
/* This was generated from if_athioctl.h */
|
|
|
|
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_watchdog", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_watchdog, 0, "device reset by watchdog");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_hardware", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_hardware, 0, "fatal hardware error interrupts");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_bmiss", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_bmiss, 0, "beacon miss interrupts");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_bmiss_phantom", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_bmiss_phantom, 0, "beacon miss interrupts");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_bstuck", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_bstuck, 0, "beacon stuck interrupts");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rxorn", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_rxorn, 0, "rx overrun interrupts");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rxeol", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_rxeol, 0, "rx eol interrupts");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_txurn", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_txurn, 0, "tx underrun interrupts");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_mib", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_mib, 0, "mib interrupts");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_intrcoal", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_intrcoal, 0, "interrupts coalesced");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_packets", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_packets, 0, "packet sent on the interface");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_mgmt", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_mgmt, 0, "management frames transmitted");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_discard", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_discard, 0, "frames discarded prior to assoc");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_qstop", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_qstop, 0, "output stopped 'cuz no buffer");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_encap", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_encap, 0, "tx encapsulation failed");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_nonode", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_nonode, 0, "tx failed 'cuz no node");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_nombuf", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_nombuf, 0, "tx failed 'cuz no mbuf");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_nomcl", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_nomcl, 0, "tx failed 'cuz no cluster");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_linear", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_linear, 0, "tx linearized to cluster");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_nodata", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_nodata, 0, "tx discarded empty frame");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_busdma", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_busdma, 0, "tx failed for dma resrcs");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_xretries", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_xretries, 0, "tx failed 'cuz too many retries");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_fifoerr", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_fifoerr, 0, "tx failed 'cuz FIFO underrun");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_filtered", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_filtered, 0, "tx failed 'cuz xmit filtered");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_shortretry", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_shortretry, 0, "tx on-chip retries (short)");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_longretry", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_longretry, 0, "tx on-chip retries (long)");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_badrate", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_badrate, 0, "tx failed 'cuz bogus xmit rate");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_noack", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_noack, 0, "tx frames with no ack marked");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_rts", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_rts, 0, "tx frames with rts enabled");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_cts", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_cts, 0, "tx frames with cts enabled");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_shortpre", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_shortpre, 0, "tx frames with short preamble");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_altrate", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_altrate, 0, "tx frames with alternate rate");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_protect", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_protect, 0, "tx frames with protection");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_ctsburst", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_ctsburst, 0, "tx frames with cts and bursting");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_ctsext", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_ctsext, 0, "tx frames with cts extension");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_nombuf", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_rx_nombuf, 0, "rx setup failed 'cuz no mbuf");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_busdma", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_rx_busdma, 0, "rx setup failed for dma resrcs");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_orn", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_rx_orn, 0, "rx failed 'cuz of desc overrun");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_crcerr", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_rx_crcerr, 0, "rx failed 'cuz of bad CRC");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_fifoerr", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_rx_fifoerr, 0, "rx failed 'cuz of FIFO overrun");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_badcrypt", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_rx_badcrypt, 0, "rx failed 'cuz decryption");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_badmic", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_rx_badmic, 0, "rx failed 'cuz MIC failure");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_phyerr", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_rx_phyerr, 0, "rx failed 'cuz of PHY err");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_tooshort", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_rx_tooshort, 0, "rx discarded 'cuz frame too short");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_toobig", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_rx_toobig, 0, "rx discarded 'cuz frame too large");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_packets", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_rx_packets, 0, "packet recv on the interface");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_mgt", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_rx_mgt, 0, "management frames received");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_ctl", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_rx_ctl, 0, "rx discarded 'cuz ctl frame");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_be_xmit", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_be_xmit, 0, "beacons transmitted");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_be_nombuf", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_be_nombuf, 0, "beacon setup failed 'cuz no mbuf");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_per_cal", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_per_cal, 0, "periodic calibration calls");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_per_calfail", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_per_calfail, 0, "periodic calibration failed");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_per_rfgain", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_per_rfgain, 0, "periodic calibration rfgain reset");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rate_calls", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_rate_calls, 0, "rate control checks");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rate_raise", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_rate_raise, 0, "rate control raised xmit rate");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rate_drop", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_rate_drop, 0, "rate control dropped xmit rate");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_ant_defswitch", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_ant_defswitch, 0, "rx/default antenna switches");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_ant_txswitch", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_ant_txswitch, 0, "tx antenna switches");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_cabq_xmit", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_cabq_xmit, 0, "cabq frames transmitted");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_cabq_busy", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_cabq_busy, 0, "cabq found busy");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_raw", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_raw, 0, "tx frames through raw api");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_ff_txok", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_ff_txok, 0, "fast frames tx'd successfully");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_ff_txerr", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_ff_txerr, 0, "fast frames tx'd w/ error");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_ff_rx", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_ff_rx, 0, "fast frames rx'd");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_ff_flush", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_ff_flush, 0, "fast frames flushed from staging q");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_qfull", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_qfull, 0, "tx dropped 'cuz of queue limit");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_nobuf", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_nobuf, 0, "tx dropped 'cuz no ath buffer");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tdma_update", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tdma_update, 0, "TDMA slot timing updates");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tdma_timers", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tdma_timers, 0, "TDMA slot update set beacon timers");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tdma_tsf", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tdma_tsf, 0, "TDMA slot update set TSF");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tdma_ack", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tdma_ack, 0, "TDMA tx failed 'cuz ACK required");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_raw_fail", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_raw_fail, 0, "raw tx failed 'cuz h/w down");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_nofrag", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_nofrag, 0, "tx dropped 'cuz no ath frag buffer");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_be_missed", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_be_missed, 0, "number of -missed- beacons");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_ani_cal", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_ani_cal, 0, "number of ANI polls");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_agg", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_rx_agg, 0, "number of aggregate frames received");
|
|
|
|
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_halfgi", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_rx_halfgi, 0, "number of frames received with half-GI");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_2040", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_rx_2040, 0, "number of HT/40 frames received");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_pre_crc_err", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_rx_pre_crc_err, 0, "number of delimeter-CRC errors detected");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_post_crc_err", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_rx_post_crc_err, 0, "number of post-delimiter CRC errors detected");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_decrypt_busy_err", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_rx_decrypt_busy_err, 0, "number of frames received w/ busy decrypt engine");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_hi_rx_chain", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_rx_hi_rx_chain, 0, "");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_htprotect", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_htprotect, 0, "HT tx frames with protection");
|
2011-03-29 16:59:07 +01:00
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_hitqueueend", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_rx_hitqueueend, 0, "RX hit queue end");
|
2011-04-18 13:15:43 +01:00
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_timeout", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_timeout, 0, "TX Global Timeout");
|
2011-04-18 15:06:18 +01:00
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_cst", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_cst, 0, "TX Carrier Sense Timeout");
|
2011-05-15 16:54:34 +01:00
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_xtxop", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_xtxop, 0, "TX exceeded TXOP");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_timerexpired", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_timerexpired, 0, "TX exceeded TX_TIMER register");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_desccfgerr", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_desccfgerr, 0, "TX Descriptor Cfg Error");
|
2011-10-26 17:09:05 +01:00
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_swretries", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_swretries, 0, "TX software retry count");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_swretrymax", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_swretrymax, 0, "TX software retry max reached");
|
|
|
|
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_data_underrun", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_data_underrun, 0, "");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_delim_underrun", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_delim_underrun, 0, "");
|
2011-11-23 05:00:25 +00:00
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_aggr_failall", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_aggr_failall, 0,
|
2011-10-26 17:09:05 +01:00
|
|
|
"Number of aggregate TX failures (whole frame)");
|
2011-11-23 05:00:25 +00:00
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_aggr_ok", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_aggr_ok, 0,
|
|
|
|
"Number of aggregate TX OK completions (subframe)");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_aggr_fail", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_aggr_fail, 0,
|
|
|
|
"Number of aggregate TX failures (subframe)");
|
2011-10-26 17:09:05 +01:00
|
|
|
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_intr", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_rx_intr, 0, "RX interrupts");
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_intr", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_intr, 0, "TX interrupts");
|
2012-03-10 04:14:04 +00:00
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_mcastq_overflow",
|
|
|
|
CTLFLAG_RD, &sc->sc_stats.ast_tx_mcastq_overflow, 0,
|
|
|
|
"Number of multicast frames exceeding maximum mcast queue depth");
|
2012-07-15 21:51:41 +01:00
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_keymiss", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_rx_keymiss, 0, "");
|
Implement my first cut at filtered frames in aggregation sessions.
The hardware can optionally "filter" frames if successive transmissions
to a given node (ie, "entry in the keycache") fail. That way the hardware
can implement a kind of early abort of all the other frames queued to
that destination, rather than simply trying to TX each frame to that
destination (and failing.)
The background:
* If a frame comes back as being filtered, the hardware didn't try to
TX it (or it was outside the TX burst opportunity.) So, take it as a hint
that some (but not all, see below) frames to the destination may be
filtered.
* If the CLRDMASK bit is set in a TX descriptor, the "filter to this
destination" bit in the keycache entry is cleared and TX to that host
will be unconditionally retried.
* Right now everything has the CLRDMASK bit set, so filtered frames
tend to be aggregates and frames that fall outside of the WME burst
window. It was a bit worse in the past as I had messed up the TX
flags and CLRDMASK wasn't being set on aggregate frames.
The annoying bits:
* It's easy (ish) to do for aggregate session frames - firstly, they
can be retried in any order as long as they're within the BAW, and
there's already a bunch of infrastructure tracking how many frames
the TID has queued to the hardware (tid->hwq_depth.) However, for
frames that bypassed the software queue, hwq_depth doesn't get
incremented. I'll fix that in a subsequent commit.
* For non-aggregate session frames, the only retries that can occur
are ones for sequence numbers that hvaen't successfully been TXed yet.
Since there's no re-ordering going on in non-aggregate sessions, if any
subsequent seqno frames make it out, any filtered frames before that
seqno need to be dropped.
Hence why this initially is just for aggregate session frames.
* Since there may be intermediary frames to the destination that
have CLRDMASK set - for example, any directly dispatched management
frames to that destination - it's possible that there will be some
filtered frames followed up by some non filtered frames. Thus,
it can't be assumed that once you see a filtered frame for the given
destination node, all subsequent frames for all TIDs will be filtered.
Ok, with that in mind:
* Create a per-TID filtered frame queue for frames that the hardware
returns as filtered.
* Track filtered frames per-tid, rather than per-node. It just makes
the locking much easier.
* When a filtered frame appears in the completion function, the node
transitions to "filtered", and all subsequent completed error frames
(filtered or otherwise) are put on the filtered frame queue. The TID
is paused once (during the transition from non-filtered to filtered).
* If a filtered frame retry count exceeds SWMAX_RETRIES, a BAR should be
sent.
* Once all the frames queued to the hardware for the given filtered frame
TID, transition back from filtered frame to non-filtered frame, which
means pre-pending all the filtered frames onto the head of the software
queue, clearing the filtered frame state and unpausing the TID.
Things get quite hairy around handling completion (aggr, non-aggr, norm,
direct-dispatched frames to a hardware queue); whether it's an "error",
"cleanup" or "BAR" state as well as filtered, which order to do things
in (eg do filtered BEFORE checking for BAR, as the filter completion
may be needed to actually transmit a BAR frame.)
This work has definitely reminded me that I have to tidy up all the locking
and remove some of the ridiculous lock/unlock/lock/unlock going on in the
completion functions.
It's also reminded me that I should really split out TID versus hardware TXQ
locking, even if the underlying locking is still the destination hardware TXQ.
Finally, this is all pre-requisite for working on AP mode power save support
(PS-POLL, uAPSD) as well as improving performance to misbehaving nodes (as
they can transition into filter mode, stopping any TX until everything has
caught up.)
Finally (ish) - this should also be done for non-aggregate sessions as
there are still plenty of laptops and mobile devices that don't speak
802.11n but do wish for stable, useful power save AP support where packets
aren't simply dropped. This requires software retransmission for
non-aggregate sessions to be implemented, which includes the caveats I've
mentioned above.
Finally finally - this doesn't yet do anything about the CLRDMASK bit in the
TX descriptor. That's still unconditionally set to 1. I'll debug the
current work (mostly ensuring I haven't busted up the hairy transitions
between BAR, filtered, error (all frames in an aggregate failing) and
cleanup (when transitioning from aggregation -> non-aggregation.))
Finally finally finally - this is all original work by yours truely, rather
than ported from the Atheros internal driver codebase or Linux ath9k.
Tested:
* AR9280, AR5416 in STA mode
* AR9280, AR9130 in hostap mode
* Lots and lots of iperf testing in very marginal and non-marginal conditions,
complete with inducing filtered frames + BAR TX conditions.
2012-09-18 11:14:17 +01:00
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_swfiltered", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_swfiltered, 0, "");
|
2013-05-08 02:11:25 +01:00
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_stbc",
|
|
|
|
CTLFLAG_RD, &sc->sc_stats.ast_rx_stbc, 0,
|
|
|
|
"Number of STBC frames received");
|
2012-03-10 04:14:04 +00:00
|
|
|
|
2011-03-02 16:03:19 +00:00
|
|
|
/* Attach the RX phy error array */
|
|
|
|
ath_sysctl_stats_attach_rxphyerr(sc, child);
|
2012-04-10 08:23:37 +01:00
|
|
|
|
|
|
|
/* Attach the interrupt statistics array */
|
|
|
|
ath_sysctl_stats_attach_intr(sc, child);
|
2011-03-02 16:03:19 +00:00
|
|
|
}
|
2011-06-23 03:38:36 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* This doesn't necessarily belong here (because it's HAL related, not
|
|
|
|
* driver related).
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
ath_sysctl_hal_attach(struct ath_softc *sc)
|
|
|
|
{
|
|
|
|
struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
|
|
|
|
struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
|
|
|
|
struct sysctl_oid_list *child = SYSCTL_CHILDREN(tree);
|
|
|
|
|
|
|
|
tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "hal", CTLFLAG_RD,
|
|
|
|
NULL, "Atheros HAL parameters");
|
|
|
|
child = SYSCTL_CHILDREN(tree);
|
|
|
|
|
|
|
|
sc->sc_ah->ah_config.ah_debug = 0;
|
|
|
|
SYSCTL_ADD_INT(ctx, child, OID_AUTO, "debug", CTLFLAG_RW,
|
|
|
|
&sc->sc_ah->ah_config.ah_debug, 0, "Atheros HAL debugging printfs");
|
|
|
|
|
|
|
|
sc->sc_ah->ah_config.ah_ar5416_biasadj = 0;
|
|
|
|
SYSCTL_ADD_INT(ctx, child, OID_AUTO, "ar5416_biasadj", CTLFLAG_RW,
|
|
|
|
&sc->sc_ah->ah_config.ah_ar5416_biasadj, 0,
|
2012-02-28 13:19:34 +00:00
|
|
|
"Enable 2GHz AR5416 direction sensitivity bias adjust");
|
2011-06-23 03:38:36 +01:00
|
|
|
|
|
|
|
sc->sc_ah->ah_config.ah_dma_beacon_response_time = 2;
|
|
|
|
SYSCTL_ADD_INT(ctx, child, OID_AUTO, "dma_brt", CTLFLAG_RW,
|
|
|
|
&sc->sc_ah->ah_config.ah_dma_beacon_response_time, 0,
|
|
|
|
"Atheros HAL DMA beacon response time");
|
|
|
|
|
|
|
|
sc->sc_ah->ah_config.ah_sw_beacon_response_time = 10;
|
|
|
|
SYSCTL_ADD_INT(ctx, child, OID_AUTO, "sw_brt", CTLFLAG_RW,
|
|
|
|
&sc->sc_ah->ah_config.ah_sw_beacon_response_time, 0,
|
|
|
|
"Atheros HAL software beacon response time");
|
|
|
|
|
|
|
|
sc->sc_ah->ah_config.ah_additional_swba_backoff = 0;
|
|
|
|
SYSCTL_ADD_INT(ctx, child, OID_AUTO, "swba_backoff", CTLFLAG_RW,
|
|
|
|
&sc->sc_ah->ah_config.ah_additional_swba_backoff, 0,
|
|
|
|
"Atheros HAL additional SWBA backoff time");
|
2011-11-09 05:30:24 +00:00
|
|
|
|
|
|
|
sc->sc_ah->ah_config.ah_force_full_reset = 0;
|
|
|
|
SYSCTL_ADD_INT(ctx, child, OID_AUTO, "force_full_reset", CTLFLAG_RW,
|
|
|
|
&sc->sc_ah->ah_config.ah_force_full_reset, 0,
|
|
|
|
"Force full chip reset rather than a warm reset");
|
2011-11-09 22:39:44 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* This is initialised by the driver.
|
|
|
|
*/
|
|
|
|
SYSCTL_ADD_INT(ctx, child, OID_AUTO, "serialise_reg_war", CTLFLAG_RW,
|
|
|
|
&sc->sc_ah->ah_config.ah_serialise_reg_war, 0,
|
|
|
|
"Force register access serialisation");
|
2011-06-23 03:38:36 +01:00
|
|
|
}
|