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 05:48:26 +00: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)) {
|
2012-08-14 22:34:22 +00:00
|
|
|
printf("HW TXQ %d: axq_depth=%d, axq_aggr_depth=%d, axq_fifo_depth=%d\n",
|
2011-11-08 02:12:11 +00:00
|
|
|
i,
|
|
|
|
sc->sc_txq[i].axq_depth,
|
2012-08-14 22:34:22 +00:00
|
|
|
sc->sc_txq[i].axq_aggr_depth,
|
|
|
|
sc->sc_txq[i].axq_fifo_depth);
|
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 00:51:53 +00: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 06:57:55 +00: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);
|
|
|
|
|
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-29 23:55:17 +00: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-29 23:55:17 +00: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-15 23:39:37 +00: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 20:35:56 +00: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,
|
|
|
|
"hwq_limit", CTLFLAG_RW, &sc->sc_hwq_limit, 0,
|
|
|
|
"");
|
|
|
|
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,
|
|
|
|
"");
|
|
|
|
|
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");
|
2012-06-14 00:51:53 +00:00
|
|
|
|
2012-03-10 04:14:04 +00:00
|
|
|
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");
|
|
|
|
|
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 07:23:37 +00: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 07:23:37 +00: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 15:59:07 +00: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 12:15:43 +00: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 14:06:18 +00: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 15:54:34 +00: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 16:09:05 +00: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 16:09:05 +00: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 16:09:05 +00: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 20:51:41 +00: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 10:14:17 +00:00
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_swfiltered", CTLFLAG_RD,
|
|
|
|
&sc->sc_stats.ast_tx_swfiltered, 0, "");
|
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 07:23:37 +00:00
|
|
|
|
|
|
|
/* Attach the interrupt statistics array */
|
|
|
|
ath_sysctl_stats_attach_intr(sc, child);
|
2011-03-02 16:03:19 +00:00
|
|
|
}
|
2011-06-23 02:38:36 +00: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 02:38:36 +00: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 02:38:36 +00:00
|
|
|
}
|