freebsd-src/sys/dev/ath/if_ath_sysctl.c

1144 lines
40 KiB
C
Raw Normal View History

/*-
* 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>
#include <dev/ath/if_ath_led.h>
#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
#ifdef ATH_DEBUG_ALQ
#include <dev/ath/if_ath_alq.h>
#endif
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 */
ath_led_config(sc);
}
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) {
ath_led_config(sc);
}
}
return 0;
}
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;
}
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 :
(ifp->if_drv_flags & IFF_DRV_RUNNING) ?
ath_reset(ifp, ATH_RESET_NOLOSS) : 0;
}
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;
return (ifp->if_drv_flags & IFF_DRV_RUNNING) ?
ath_reset(ifp, ATH_RESET_FULL) : 0;
}
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, &param, 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);
printf("aggr single packet RTS aggr limited: %d\n",
sc->sc_aggr_stats.aggr_rts_aggr_limited);
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",
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);
}
}
i = t = 0;
ATH_TXBUF_LOCK(sc);
TAILQ_FOREACH(bf, &sc->sc_txbuf, bf_list) {
if (bf->bf_flags & ATH_BUF_BUSY) {
printf("Busy: %d\n", t);
i++;
}
t++;
}
ATH_TXBUF_UNLOCK(sc);
printf("Total TX buffers: %d; Total TX buffers busy: %d (%d)\n",
t, i, sc->sc_txbuf_cnt);
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);
ATH_RX_LOCK(sc);
for (i = 0; i < 2; i++) {
printf("%d: fifolen: %d/%d; head=%d; tail=%d\n",
i,
sc->sc_rxedma[i].m_fifo_depth,
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);
return 0;
}
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;
/* 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)
ath_reset(sc->sc_ifp, ATH_RESET_NOLOSS);
return 0;
}
#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 */
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;
}
#ifdef ATH_DEBUG_ALQ
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");
}
#endif /* ATH_DEBUG_ALQ */
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");
#ifdef ATH_DEBUG
SYSCTL_ADD_QUAD(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
"debug", CTLFLAG_RW, &sc->sc_debug,
"control debugging printfs");
#endif
#ifdef ATH_DEBUG_ALQ
SYSCTL_ADD_QUAD(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
"ktrdebug", CTLFLAG_RW, &sc->sc_ktrdebug,
"control debugging KTR");
#endif /* ATH_DEBUG_ALQ */
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)");
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)");
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");
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");
}
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", "");
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");
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
"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");
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,
"");
/* Aggregate length twiddles */
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
"aggr_limit", CTLFLAG_RW, &sc->sc_aggr_limit, 0,
"Maximum A-MPDU size, or 0 for 'default'");
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
"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");
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");
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");
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
#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-10 15:21:39 +00:00
#ifdef ATH_DEBUG_ALQ
ath_sysctl_alq_attach(sc);
#endif
}
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));
memset(&sc->sc_aggr_stats, 0, sizeof(sc->sc_aggr_stats));
memset(&sc->sc_intr_stats, 0, sizeof(sc->sc_intr_stats));
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, "");
}
}
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, "");
}
}
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");
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_hitqueueend", CTLFLAG_RD,
&sc->sc_stats.ast_rx_hitqueueend, 0, "RX hit queue end");
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_timeout", CTLFLAG_RD,
&sc->sc_stats.ast_tx_timeout, 0, "TX Global Timeout");
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_cst", CTLFLAG_RD,
&sc->sc_stats.ast_tx_cst, 0, "TX Carrier Sense Timeout");
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");
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, "");
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_aggr_failall", CTLFLAG_RD,
&sc->sc_stats.ast_tx_aggr_failall, 0,
"Number of aggregate TX failures (whole frame)");
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)");
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");
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");
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, "");
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_stbc",
CTLFLAG_RD, &sc->sc_stats.ast_rx_stbc, 0,
"Number of STBC frames received");
/* Attach the RX phy error array */
ath_sysctl_stats_attach_rxphyerr(sc, child);
/* Attach the interrupt statistics array */
ath_sysctl_stats_attach_intr(sc, child);
}
/*
* 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,
"Enable 2GHz AR5416 direction sensitivity bias adjust");
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");
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");
/*
* 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");
}