diff --git a/src/add-ons/kernel/drivers/network/wlan/Jamfile b/src/add-ons/kernel/drivers/network/wlan/Jamfile index e19ceeed18..f3b1c150f7 100644 --- a/src/add-ons/kernel/drivers/network/wlan/Jamfile +++ b/src/add-ons/kernel/drivers/network/wlan/Jamfile @@ -8,7 +8,6 @@ SubInclude HAIKU_TOP src add-ons kernel drivers network wlan iprowifi2100 ; SubInclude HAIKU_TOP src add-ons kernel drivers network wlan marvell88w8335 ; # FreeBSD 9.3 drivers -SubInclude HAIKU_TOP src add-ons kernel drivers network wlan atheroswifi ; SubInclude HAIKU_TOP src add-ons kernel drivers network wlan broadcom43xx ; SubInclude HAIKU_TOP src add-ons kernel drivers network wlan marvell88w8363 ; SubInclude HAIKU_TOP src add-ons kernel drivers network wlan ralinkwifi ; @@ -16,9 +15,10 @@ SubInclude HAIKU_TOP src add-ons kernel drivers network wlan wavelanwifi ; # FreeBSD 11.1 drivers SubInclude HAIKU_TOP src add-ons kernel drivers network wlan aironetwifi ; -SubInclude HAIKU_TOP src add-ons kernel drivers network wlan iprowifi2200 ; +SubInclude HAIKU_TOP src add-ons kernel drivers network wlan atheroswifi ; SubInclude HAIKU_TOP src add-ons kernel drivers network wlan iprowifi3945 ; SubInclude HAIKU_TOP src add-ons kernel drivers network wlan iprowifi4965 ; # FreeBSD 11.2 drivers SubInclude HAIKU_TOP src add-ons kernel drivers network wlan idualwifi7260 ; +SubInclude HAIKU_TOP src add-ons kernel drivers network wlan iprowifi2200 ; diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/Jamfile b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/Jamfile index e1fb7d85ba..84f948ee44 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/Jamfile +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/Jamfile @@ -1,14 +1,14 @@ SubDir HAIKU_TOP src add-ons kernel drivers network wlan atheroswifi ; -UseHeaders [ FDirName $(HAIKU_TOP) src libs compat freebsd_network compat ] +UseHeaders [ FDirName $(HAIKU_TOP) src libs compat freebsd11_network compat ] : true ; -UseHeaders [ FDirName $(HAIKU_TOP) src libs compat freebsd_wlan ] : true ; +UseHeaders [ FDirName $(HAIKU_TOP) src libs compat freebsd11_wlan ] : true ; UsePrivateHeaders net system ; UsePrivateKernelHeaders ; -SubDirCcFlags [ FDefines _KERNEL=1 FBSD_DRIVER=1 _XOPEN_SOURCE ] - -Wno-format - -Wno-unused +SubDirCcFlags [ FDefines _KERNEL=1 FBSD_DRIVER=1 _XOPEN_SOURCE ] + -Wno-format + -Wno-unused -Wno-uninitialized ; UseHeaders [ FDirName $(SUBDIR) ] : true ; @@ -20,18 +20,32 @@ Depends atheroswifi : atheroswifi_hal.o ; Depends atheroswifi : atheroswifi_rate.o ; KernelAddon atheroswifi : + glue.c + if_ath.c + if_ath_beacon.c + if_ath_btcoex.c + if_ath_btcoex_mci.c + if_ath_descdma.c + if_ath_ioctl.c if_ath_pci.c + if_ath_led.c + if_ath_lna_div.c if_ath_keycache.c if_ath_sysctl.c + if_ath_rx.c + if_ath_rx_edma.c + if_ath_spectral.c + if_ath_tdma.c if_ath_tx.c + if_ath_tx_edma.c if_ath_tx_ht.c - glue.c + atheroswifi_hal.o atheroswifi_rate.o : - libfreebsd_wlan.a - libfreebsd_network.a + libfreebsd11_wlan.a + libfreebsd11_network.a ; SEARCH_SOURCE += [ FDirName $(SUBDIR) dev ath ath_hal ] ; @@ -119,11 +133,15 @@ KernelMergeObject atheroswifi_hal.o : ar5416_recv.c ar5416_reset.c ar5416_xmit.c + ar5416_btcoex.c + ar5416_radar.c + ar5416_spectral.c # RF backend for 5416 and 9160 ar2133.c # AR9001 and AR9002 support + ar9285_btcoex.c ar9130_attach.c ar9130_eeprom.c ar9130_phy.c diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ah_osdep.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ah_osdep.c index c9a7a261af..900e071f8d 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ah_osdep.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ah_osdep.c @@ -26,7 +26,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ah_osdep.c 293111 2016-01-03 17:58:11Z adrian $ */ #include "opt_ah.h" @@ -38,12 +38,16 @@ #include #include #include +#include +#include +#include #include #include /* XXX for ether_sprintf */ #include +#include /* * WiSoC boards overload the bus tag with information about the @@ -59,6 +63,23 @@ #define BUSTAG(ah) ((ah)->ah_st) #endif +/* + * This lock is used to seralise register access for chips which have + * problems w/ SMP CPUs issuing concurrent PCI transactions. + * + * XXX This is a global lock for now; it should be pushed to + * a per-device lock in some platform-independent fashion. + */ +#ifndef __HAIKU__ +struct mtx ah_regser_mtx; +MTX_SYSINIT(ah_regser, &ah_regser_mtx, "Atheros register access mutex", + MTX_SPIN); +#else +spinlock ah_regser_mtx = B_SPINLOCK_INITIALIZER; +#define mtx_lock_spin(a) acquire_spinlock(a) +#define mtx_unlock_spin(a) release_spinlock(a) +#endif + extern void ath_hal_printf(struct ath_hal *, const char*, ...) __printflike(2,3); extern void ath_hal_vprintf(struct ath_hal *, const char*, __va_list) @@ -81,9 +102,8 @@ static SYSCTL_NODE(_hw_ath, OID_AUTO, hal, CTLFLAG_RD, 0, #ifdef AH_DEBUG int ath_hal_debug = 0; -SYSCTL_INT(_hw_ath_hal, OID_AUTO, debug, CTLFLAG_RW, &ath_hal_debug, +SYSCTL_INT(_hw_ath_hal, OID_AUTO, debug, CTLFLAG_RWTUN, &ath_hal_debug, 0, "Atheros HAL debugging printfs"); -TUNABLE_INT("hw.ath.hal.debug", &ath_hal_debug); #endif /* AH_DEBUG */ static MALLOC_DEFINE(M_ATH_HAL, "ath_hal", "ath hal data"); @@ -123,13 +143,29 @@ ath_hal_ether_sprintf(const u_int8_t *mac) #ifdef AH_DEBUG -/* This must match the definition in ath_hal/ah_debug.h */ -#define HAL_DEBUG_UNMASKABLE 0xf0000000 +/* + * XXX This is highly relevant only for the AR5416 and later + * PCI/PCIe NICs. It'll need adjustment for other hardware + * variations. + */ +static int +ath_hal_reg_whilst_asleep(struct ath_hal *ah, uint32_t reg) +{ + + if (reg >= 0x4000 && reg < 0x5000) + return (1); + if (reg >= 0x6000 && reg < 0x7000) + return (1); + if (reg >= 0x7000 && reg < 0x8000) + return (1); + return (0); +} + void DO_HALDEBUG(struct ath_hal *ah, u_int mask, const char* fmt, ...) { if ((mask == HAL_DEBUG_UNMASKABLE) || - (ah->ah_config.ah_debug & mask) || + (ah != NULL && ah->ah_config.ah_debug & mask) || (ath_hal_debug & mask)) { __va_list ap; va_start(ap, fmt); @@ -240,22 +276,32 @@ ath_hal_reg_write(struct ath_hal *ah, u_int32_t reg, u_int32_t val) bus_space_tag_t tag = BUSTAG(ah); bus_space_handle_t h = ah->ah_sh; +#ifdef AH_DEBUG + /* Debug - complain if we haven't fully waken things up */ + if (! ath_hal_reg_whilst_asleep(ah, reg) && + ah->ah_powerMode != HAL_PM_AWAKE) { + ath_hal_printf(ah, "%s: reg=0x%08x, val=0x%08x, pm=%d\n", + __func__, reg, val, ah->ah_powerMode); + } +#endif + if (ath_hal_alq) { struct ale *ale = ath_hal_alq_get(ah); if (ale) { struct athregrec *r = (struct athregrec *) ale->ae_data; + r->threadid = curthread->td_tid; r->op = OP_WRITE; r->reg = reg; r->val = val; alq_post(ath_hal_alq, ale); } } -#if _BYTE_ORDER == _BIG_ENDIAN - if (OS_REG_UNSWAPPED(reg)) - bus_space_write_4(tag, h, reg, val); - else -#endif - bus_space_write_stream_4(tag, h, reg, val); + if (ah->ah_config.ah_serialise_reg_war) + mtx_lock_spin(&ah_regser_mtx); + bus_space_write_4(tag, h, reg, val); + OS_BUS_BARRIER_REG(ah, reg, OS_BUS_BARRIER_WRITE); + if (ah->ah_config.ah_serialise_reg_war) + mtx_unlock_spin(&ah_regser_mtx); } u_int32_t @@ -265,16 +311,26 @@ ath_hal_reg_read(struct ath_hal *ah, u_int32_t reg) bus_space_handle_t h = ah->ah_sh; u_int32_t val; -#if _BYTE_ORDER == _BIG_ENDIAN - if (OS_REG_UNSWAPPED(reg)) - val = bus_space_read_4(tag, h, reg); - else +#ifdef AH_DEBUG + /* Debug - complain if we haven't fully waken things up */ + if (! ath_hal_reg_whilst_asleep(ah, reg) && + ah->ah_powerMode != HAL_PM_AWAKE) { + ath_hal_printf(ah, "%s: reg=0x%08x, pm=%d\n", + __func__, reg, ah->ah_powerMode); + } #endif - val = bus_space_read_stream_4(tag, h, reg); + + if (ah->ah_config.ah_serialise_reg_war) + mtx_lock_spin(&ah_regser_mtx); + OS_BUS_BARRIER_REG(ah, reg, OS_BUS_BARRIER_READ); + val = bus_space_read_4(tag, h, reg); + if (ah->ah_config.ah_serialise_reg_war) + mtx_unlock_spin(&ah_regser_mtx); if (ath_hal_alq) { struct ale *ale = ath_hal_alq_get(ah); if (ale) { struct athregrec *r = (struct athregrec *) ale->ae_data; + r->threadid = curthread->td_tid; r->op = OP_READ; r->reg = reg; r->val = val; @@ -291,6 +347,7 @@ OS_MARK(struct ath_hal *ah, u_int id, u_int32_t v) struct ale *ale = ath_hal_alq_get(ah); if (ale) { struct athregrec *r = (struct athregrec *) ale->ae_data; + r->threadid = curthread->td_tid; r->op = OP_MARK; r->reg = id; r->val = v; @@ -298,7 +355,8 @@ OS_MARK(struct ath_hal *ah, u_int id, u_int32_t v) } } } -#elif defined(AH_DEBUG) || defined(AH_REGOPS_FUNC) +#else /* AH_DEBUG_ALQ */ + /* * Memory-mapped device register read/write. These are here * as routines when debugging support is enabled and/or when @@ -316,12 +374,21 @@ ath_hal_reg_write(struct ath_hal *ah, u_int32_t reg, u_int32_t val) bus_space_tag_t tag = BUSTAG(ah); bus_space_handle_t h = ah->ah_sh; -#if _BYTE_ORDER == _BIG_ENDIAN - if (OS_REG_UNSWAPPED(reg)) - bus_space_write_4(tag, h, reg, val); - else +#ifdef AH_DEBUG + /* Debug - complain if we haven't fully waken things up */ + if (! ath_hal_reg_whilst_asleep(ah, reg) && + ah->ah_powerMode != HAL_PM_AWAKE) { + ath_hal_printf(ah, "%s: reg=0x%08x, val=0x%08x, pm=%d\n", + __func__, reg, val, ah->ah_powerMode); + } #endif - bus_space_write_stream_4(tag, h, reg, val); + + if (ah->ah_config.ah_serialise_reg_war) + mtx_lock_spin(&ah_regser_mtx); + bus_space_write_4(tag, h, reg, val); + OS_BUS_BARRIER_REG(ah, reg, OS_BUS_BARRIER_WRITE); + if (ah->ah_config.ah_serialise_reg_war) + mtx_unlock_spin(&ah_regser_mtx); } u_int32_t @@ -331,15 +398,24 @@ ath_hal_reg_read(struct ath_hal *ah, u_int32_t reg) bus_space_handle_t h = ah->ah_sh; u_int32_t val; -#if _BYTE_ORDER == _BIG_ENDIAN - if (OS_REG_UNSWAPPED(reg)) - val = bus_space_read_4(tag, h, reg); - else +#ifdef AH_DEBUG + /* Debug - complain if we haven't fully waken things up */ + if (! ath_hal_reg_whilst_asleep(ah, reg) && + ah->ah_powerMode != HAL_PM_AWAKE) { + ath_hal_printf(ah, "%s: reg=0x%08x, pm=%d\n", + __func__, reg, ah->ah_powerMode); + } #endif - val = bus_space_read_stream_4(tag, h, reg); + + if (ah->ah_config.ah_serialise_reg_war) + mtx_lock_spin(&ah_regser_mtx); + OS_BUS_BARRIER_REG(ah, reg, OS_BUS_BARRIER_READ); + val = bus_space_read_4(tag, h, reg); + if (ah->ah_config.ah_serialise_reg_war) + mtx_unlock_spin(&ah_regser_mtx); return val; } -#endif /* AH_DEBUG || AH_REGOPS_FUNC */ +#endif /* AH_DEBUG_ALQ */ #ifdef AH_ASSERT void diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ah_osdep.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ah_osdep.h index 99b94c4f4f..b37b4bb6c5 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ah_osdep.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ah_osdep.h @@ -26,7 +26,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ah_osdep.h 293111 2016-01-03 17:58:11Z adrian $ */ #ifndef _ATH_AH_OSDEP_H_ #define _ATH_AH_OSDEP_H_ @@ -48,6 +48,12 @@ typedef void *HAL_SOFTC; typedef bus_space_tag_t HAL_BUS_TAG; typedef bus_space_handle_t HAL_BUS_HANDLE; +/* + * Although the underlying hardware may support 64 bit DMA, the + * current Atheros hardware only supports 32 bit addressing. + */ +typedef uint32_t HAL_DMA_ADDR; + /* * Linker set writearounds for chip and RF backend registration. */ @@ -63,6 +69,7 @@ typedef bus_space_handle_t HAL_BUS_HANDLE; #define OS_INLINE __inline #define OS_MEMZERO(_a, _n) bzero((_a), (_n)) #define OS_MEMCPY(_d, _s, _n) memcpy(_d,_s,_n) +#define OS_MEMCMP(_a, _b, _l) memcmp((_a), (_b), (_l)) #define abs(_a) __builtin_abs(_a) @@ -75,6 +82,10 @@ struct ath_hal; * domain registers are not byte swapped! Thus, on big-endian * platforms we have to explicitly byte-swap those registers. * OS_REG_UNSWAPPED identifies the registers that need special handling. + * + * This is not currently used by the FreeBSD HAL osdep code; the HAL + * currently does not configure hardware byteswapping for register space + * accesses and instead does it through the FreeBSD bus space code. */ #if _BYTE_ORDER == _BIG_ENDIAN #define OS_REG_UNSWAPPED(_reg) \ @@ -85,51 +96,49 @@ struct ath_hal; #endif /* _BYTE_ORDER */ /* - * Register read/write operations are either handled through - * platform-dependent routines (or when debugging is enabled - * with AH_DEBUG); or they are inline expanded using the macros - * defined below. + * For USB/SDIO support (where access latencies are quite high); + * some write accesses may be buffered and then flushed when + * either a read is done, or an explicit flush is done. + * + * These are simply placeholders for now. + */ +#define OS_REG_WRITE_BUFFER_ENABLE(_ah) \ + do { } while (0) +#define OS_REG_WRITE_BUFFER_DISABLE(_ah) \ + do { } while (0) +#define OS_REG_WRITE_BUFFER_FLUSH(_ah) \ + do { } while (0) + +/* + * Read and write barriers. Some platforms require more strongly ordered + * operations and unfortunately most of the HAL is written assuming everything + * is either an x86 or the bus layer will do the barriers for you. + * + * Read barriers should occur before each read, and write barriers + * occur after each write. + * + * Later on for SDIO/USB parts we will methodize this and make them no-ops; + * register accesses will go via USB commands. + */ +#define OS_BUS_BARRIER_READ BUS_SPACE_BARRIER_READ +#define OS_BUS_BARRIER_WRITE BUS_SPACE_BARRIER_WRITE +#define OS_BUS_BARRIER_RW \ + (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE) +#define OS_BUS_BARRIER(_ah, _start, _len, _t) \ + bus_space_barrier((bus_space_tag_t)(_ah)->ah_st, \ + (bus_space_handle_t)(_ah)->ah_sh, (_start), (_len), (_t)) +#define OS_BUS_BARRIER_REG(_ah, _reg, _t) \ + OS_BUS_BARRIER((_ah), (_reg), 4, (_t)) + +/* + * Register read/write operations are handled through + * platform-dependent routines. */ -#if defined(AH_DEBUG) || defined(AH_REGOPS_FUNC) || defined(AH_DEBUG_ALQ) #define OS_REG_WRITE(_ah, _reg, _val) ath_hal_reg_write(_ah, _reg, _val) #define OS_REG_READ(_ah, _reg) ath_hal_reg_read(_ah, _reg) extern void ath_hal_reg_write(struct ath_hal *ah, u_int reg, u_int32_t val); extern u_int32_t ath_hal_reg_read(struct ath_hal *ah, u_int reg); -#else -/* - * The hardware registers are native little-endian byte order. - * Big-endian hosts are handled by enabling hardware byte-swap - * of register reads and writes at reset. But the PCI clock - * domain registers are not byte swapped! Thus, on big-endian - * platforms we have to explicitly byte-swap those registers. - * Most of this code is collapsed at compile time because the - * register values are constants. - */ -#if _BYTE_ORDER == _BIG_ENDIAN -#define OS_REG_WRITE(_ah, _reg, _val) do { \ - if (OS_REG_UNSWAPPED(_reg)) \ - bus_space_write_4((bus_space_tag_t)(_ah)->ah_st, \ - (bus_space_handle_t)(_ah)->ah_sh, (_reg), (_val)); \ - else \ - bus_space_write_stream_4((bus_space_tag_t)(_ah)->ah_st, \ - (bus_space_handle_t)(_ah)->ah_sh, (_reg), (_val)); \ -} while (0) -#define OS_REG_READ(_ah, _reg) \ - (OS_REG_UNSWAPPED(_reg) ? \ - bus_space_read_4((bus_space_tag_t)(_ah)->ah_st, \ - (bus_space_handle_t)(_ah)->ah_sh, (_reg)) : \ - bus_space_read_stream_4((bus_space_tag_t)(_ah)->ah_st, \ - (bus_space_handle_t)(_ah)->ah_sh, (_reg))) -#else /* _BYTE_ORDER == _LITTLE_ENDIAN */ -#define OS_REG_WRITE(_ah, _reg, _val) \ - bus_space_write_4((bus_space_tag_t)(_ah)->ah_st, \ - (bus_space_handle_t)(_ah)->ah_sh, (_reg), (_val)) -#define OS_REG_READ(_ah, _reg) \ - bus_space_read_4((bus_space_tag_t)(_ah)->ah_st, \ - (bus_space_handle_t)(_ah)->ah_sh, (_reg)) -#endif /* _BYTE_ORDER */ -#endif /* AH_DEBUG || AH_REGFUNC || AH_DEBUG_ALQ */ #ifdef AH_DEBUG_ALQ extern void OS_MARK(struct ath_hal *, u_int id, u_int32_t value); diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_dfs/null/dfs_null.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_dfs/null/dfs_null.c index 75574b4e4a..f91eadbd15 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_dfs/null/dfs_null.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_dfs/null/dfs_null.c @@ -26,14 +26,15 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_dfs/null/dfs_null.c 298939 2016-05-02 19:56:48Z pfg $ */ #include -__FBSDID("$FreeBSD$"); +__FBSDID("$FreeBSD: releng/11.1/sys/dev/ath/ath_dfs/null/dfs_null.c 298939 2016-05-02 19:56:48Z pfg $"); /* * This implements an empty DFS module. */ +#include "opt_ath.h" #include "opt_inet.h" #include "opt_wlan.h" @@ -42,6 +43,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -52,6 +54,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include /* XXX for ether_sprintf */ @@ -80,7 +83,7 @@ __FBSDID("$FreeBSD$"); int ath_dfs_attach(struct ath_softc *sc) { - return 1; + return (1); } /* @@ -89,32 +92,96 @@ ath_dfs_attach(struct ath_softc *sc) int ath_dfs_detach(struct ath_softc *sc) { - return 1; + return (1); } /* - * Enable radar check + * Enable radar check. Return 1 if the driver should + * enable radar PHY errors, or 0 if not. */ -void +int ath_dfs_radar_enable(struct ath_softc *sc, struct ieee80211_channel *chan) { +#if 0 + HAL_PHYERR_PARAM pe; + + /* Check if the hardware supports radar reporting */ + /* XXX TODO: migrate HAL_CAP_RADAR/HAL_CAP_AR to somewhere public! */ + if (ath_hal_getcapability(sc->sc_ah, + HAL_CAP_PHYDIAG, 0, NULL) != HAL_OK) + return (0); + /* Check if the current channel is radar-enabled */ if (! IEEE80211_IS_CHAN_DFS(chan)) - return; + return (0); + + /* Fetch the default parameters */ + memset(&pe, '\0', sizeof(pe)); + if (! ath_hal_getdfsdefaultthresh(sc->sc_ah, &pe)) + return (0); + + /* Enable radar PHY error reporting */ + sc->sc_dodfs = 1; + + /* Tell the hardware to enable radar reporting */ + pe.pe_enabled = 1; + + /* Flip on extension channel events only if doing HT40 */ + if (IEEE80211_IS_CHAN_HT40(chan)) + pe.pe_extchannel = 1; + else + pe.pe_extchannel = 0; + + ath_hal_enabledfs(sc->sc_ah, &pe); + + /* + * Disable strong signal fast diversity - needed for + * AR5212 and similar PHYs for reliable short pulse + * duration. + */ + (void) ath_hal_setcapability(sc->sc_ah, HAL_CAP_DIVERSITY, 2, 0, NULL); + + return (1); +#else + return (0); +#endif +} + +/* + * Explicity disable radar reporting. + * + * Return 0 if it was disabled, < 0 on error. + */ +int +ath_dfs_radar_disable(struct ath_softc *sc) +{ +#if 0 + HAL_PHYERR_PARAM pe; + + (void) ath_hal_getdfsthresh(sc->sc_ah, &pe); + pe.pe_enabled = 0; + (void) ath_hal_enabledfs(sc->sc_ah, &pe); + return (0); +#else + return (0); +#endif } /* * Process DFS related PHY errors + * + * The mbuf is not "ours" and if we want a copy, we have + * to take a copy. It'll be freed after this function returns. */ void -ath_dfs_process_phy_err(struct ath_softc *sc, const char *buf, +ath_dfs_process_phy_err(struct ath_softc *sc, struct mbuf *m, uint64_t tsf, struct ath_rx_status *rxstat) { } /* - * Process the radar events and determine whether a DFS event has occured. + * Process the radar events and determine whether a DFS event has occurred. * * This is designed to run outside of the RX processing path. * The RX path will call ath_dfs_tasklet_needed() to see whether @@ -124,7 +191,7 @@ int ath_dfs_process_radar_event(struct ath_softc *sc, struct ieee80211_channel *chan) { - return 0; + return (0); } /* @@ -137,7 +204,7 @@ ath_dfs_process_radar_event(struct ath_softc *sc, int ath_dfs_tasklet_needed(struct ath_softc *sc, struct ieee80211_channel *chan) { - return 0; + return (0); } /* @@ -214,7 +281,7 @@ bad: free(indata, M_TEMP); if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL) free(outdata, M_TEMP); - return error; + return (error); } /* @@ -224,5 +291,5 @@ int ath_dfs_get_thresholds(struct ath_softc *sc, HAL_PHYERR_PARAM *param) { ath_hal_getdfsthresh(sc->sc_ah, param); - return 1; + return (1); } diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah.c index 4055079dc7..b8d401cede 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ah.c 298939 2016-05-02 19:56:48Z pfg $ */ #include "opt_ah.h" @@ -24,6 +24,7 @@ #include "ah_eeprom.h" /* for 5ghz fast clock flag */ #include "ar5416/ar5416reg.h" /* NB: includes ar5212reg.h */ +#include "ar9003/ar9300_devid.h" /* linker set of registered chips */ OS_SET_DECLARE(ah_chips, struct ath_hal_chip); @@ -54,7 +55,9 @@ ath_hal_probe(uint16_t vendorid, uint16_t devid) */ struct ath_hal* ath_hal_attach(uint16_t devid, HAL_SOFTC sc, - HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, HAL_STATUS *error) + HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, + HAL_OPS_CONFIG *ah_config, + HAL_STATUS *error) { struct ath_hal_chip * const *pchip; @@ -65,7 +68,8 @@ ath_hal_attach(uint16_t devid, HAL_SOFTC sc, /* XXX don't have vendorid, assume atheros one works */ if (chip->probe(ATHEROS_VENDOR_ID, devid) == AH_NULL) continue; - ah = chip->attach(devid, sc, st, sh, eepromdata, error); + ah = chip->attach(devid, sc, st, sh, eepromdata, ah_config, + error); if (ah != AH_NULL) { /* copy back private state to public area */ ah->ah_devid = AH_PRIVATE(ah)->ah_devid; @@ -87,38 +91,60 @@ ath_hal_mac_name(struct ath_hal *ah) switch (ah->ah_macVersion) { case AR_SREV_VERSION_CRETE: case AR_SREV_VERSION_MAUI_1: - return "5210"; + return "AR5210"; case AR_SREV_VERSION_MAUI_2: case AR_SREV_VERSION_OAHU: - return "5211"; + return "AR5211"; case AR_SREV_VERSION_VENICE: - return "5212"; + return "AR5212"; case AR_SREV_VERSION_GRIFFIN: - return "2413"; + return "AR2413"; case AR_SREV_VERSION_CONDOR: - return "5424"; + return "AR5424"; case AR_SREV_VERSION_EAGLE: - return "5413"; + return "AR5413"; case AR_SREV_VERSION_COBRA: - return "2415"; - case AR_SREV_2425: - return "2425"; - case AR_SREV_2417: - return "2417"; + return "AR2415"; + case AR_SREV_2425: /* Swan */ + return "AR2425"; + case AR_SREV_2417: /* Nala */ + return "AR2417"; case AR_XSREV_VERSION_OWL_PCI: - return "5416"; + return "AR5416"; case AR_XSREV_VERSION_OWL_PCIE: - return "5418"; + return "AR5418"; case AR_XSREV_VERSION_HOWL: - return "9130"; + return "AR9130"; case AR_XSREV_VERSION_SOWL: - return "9160"; + return "AR9160"; case AR_XSREV_VERSION_MERLIN: - return "9280"; + if (AH_PRIVATE(ah)->ah_ispcie) + return "AR9280"; + return "AR9220"; case AR_XSREV_VERSION_KITE: - return "9285"; + return "AR9285"; case AR_XSREV_VERSION_KIWI: - return "9287"; + if (AH_PRIVATE(ah)->ah_ispcie) + return "AR9287"; + return "AR9227"; + case AR_SREV_VERSION_AR9380: + if (ah->ah_macRev >= AR_SREV_REVISION_AR9580_10) + return "AR9580"; + return "AR9380"; + case AR_SREV_VERSION_AR9460: + return "AR9460"; + case AR_SREV_VERSION_AR9330: + return "AR9330"; + case AR_SREV_VERSION_AR9340: + return "AR9340"; + case AR_SREV_VERSION_QCA9550: + return "QCA9550"; + case AR_SREV_VERSION_AR9485: + return "AR9485"; + case AR_SREV_VERSION_QCA9565: + return "QCA9565"; + case AR_SREV_VERSION_QCA9530: + return "QCA9530"; } return "????"; } @@ -271,36 +297,42 @@ ath_hal_pkt_txtime(struct ath_hal *ah, const HAL_RATE_TABLE *rates, uint32_t fra /* 11n frame - extract out the number of spatial streams */ numStreams = HT_RC_2_STREAMS(rc); - KASSERT(numStreams == 1 || numStreams == 2, ("number of spatial streams needs to be 1 or 2: MCS rate 0x%x!", rateix)); + KASSERT(numStreams > 0 && numStreams <= 4, + ("number of spatial streams needs to be 1..3: MCS rate 0x%x!", + rateix)); return ath_computedur_ht(frameLen, rc, numStreams, isht40, shortPreamble); } +static const uint16_t ht20_bps[32] = { + 26, 52, 78, 104, 156, 208, 234, 260, + 52, 104, 156, 208, 312, 416, 468, 520, + 78, 156, 234, 312, 468, 624, 702, 780, + 104, 208, 312, 416, 624, 832, 936, 1040 +}; +static const uint16_t ht40_bps[32] = { + 54, 108, 162, 216, 324, 432, 486, 540, + 108, 216, 324, 432, 648, 864, 972, 1080, + 162, 324, 486, 648, 972, 1296, 1458, 1620, + 216, 432, 648, 864, 1296, 1728, 1944, 2160 +}; + /* * Calculate the transmit duration of an 11n frame. - * This only works for MCS0->MCS15. */ uint32_t -ath_computedur_ht(uint32_t frameLen, uint16_t rate, int streams, HAL_BOOL isht40, - HAL_BOOL isShortGI) +ath_computedur_ht(uint32_t frameLen, uint16_t rate, int streams, + HAL_BOOL isht40, HAL_BOOL isShortGI) { - static const uint16_t ht20_bps[16] = { - 26, 52, 78, 104, 156, 208, 234, 260, - 52, 104, 156, 208, 312, 416, 468, 520 - }; - static const uint16_t ht40_bps[16] = { - 54, 108, 162, 216, 324, 432, 486, 540, - 108, 216, 324, 432, 648, 864, 972, 1080, - }; uint32_t bitsPerSymbol, numBits, numSymbols, txTime; KASSERT(rate & IEEE80211_RATE_MCS, ("not mcs %d", rate)); - KASSERT((rate &~ IEEE80211_RATE_MCS) < 16, ("bad mcs 0x%x", rate)); + KASSERT((rate &~ IEEE80211_RATE_MCS) < 31, ("bad mcs 0x%x", rate)); if (isht40) - bitsPerSymbol = ht40_bps[rate & 0xf]; + bitsPerSymbol = ht40_bps[rate & 0x1f]; else - bitsPerSymbol = ht20_bps[rate & 0xf]; + bitsPerSymbol = ht20_bps[rate & 0x1f]; numBits = OFDM_PLCP_BITS + (frameLen << 3); numSymbols = howmany(numBits, bitsPerSymbol); if (isShortGI) @@ -330,7 +362,7 @@ ath_hal_computetxtime(struct ath_hal *ah, kbps = rates->info[rateix].rateKbps; /* - * index can be invalid duting dynamic Turbo transitions. + * index can be invalid during dynamic Turbo transitions. * XXX */ if (kbps == 0) @@ -394,6 +426,50 @@ ath_hal_computetxtime(struct ath_hal *ah, return txTime; } +int +ath_hal_get_curmode(struct ath_hal *ah, const struct ieee80211_channel *chan) +{ + /* + * Pick a default mode at bootup. A channel change is inevitable. + */ + if (!chan) + return HAL_MODE_11NG_HT20; + + if (IEEE80211_IS_CHAN_TURBO(chan)) + return HAL_MODE_TURBO; + + /* check for NA_HT before plain A, since IS_CHAN_A includes NA_HT */ + if (IEEE80211_IS_CHAN_5GHZ(chan) && IEEE80211_IS_CHAN_HT20(chan)) + return HAL_MODE_11NA_HT20; + if (IEEE80211_IS_CHAN_5GHZ(chan) && IEEE80211_IS_CHAN_HT40U(chan)) + return HAL_MODE_11NA_HT40PLUS; + if (IEEE80211_IS_CHAN_5GHZ(chan) && IEEE80211_IS_CHAN_HT40D(chan)) + return HAL_MODE_11NA_HT40MINUS; + if (IEEE80211_IS_CHAN_A(chan)) + return HAL_MODE_11A; + + /* check for NG_HT before plain G, since IS_CHAN_G includes NG_HT */ + if (IEEE80211_IS_CHAN_2GHZ(chan) && IEEE80211_IS_CHAN_HT20(chan)) + return HAL_MODE_11NG_HT20; + if (IEEE80211_IS_CHAN_2GHZ(chan) && IEEE80211_IS_CHAN_HT40U(chan)) + return HAL_MODE_11NG_HT40PLUS; + if (IEEE80211_IS_CHAN_2GHZ(chan) && IEEE80211_IS_CHAN_HT40D(chan)) + return HAL_MODE_11NG_HT40MINUS; + + /* + * XXX For FreeBSD, will this work correctly given the DYN + * chan mode (OFDM+CCK dynamic) ? We have pure-G versions DYN-BG.. + */ + if (IEEE80211_IS_CHAN_G(chan)) + return HAL_MODE_11G; + if (IEEE80211_IS_CHAN_B(chan)) + return HAL_MODE_11B; + + HALASSERT(0); + return HAL_MODE_11NG_HT20; +} + + typedef enum { WIRELESS_MODE_11a = 0, WIRELESS_MODE_TURBO = 1, @@ -444,6 +520,13 @@ ath_hal_mac_clks(struct ath_hal *ah, u_int usecs) clks <<= 1; } else clks = usecs * CLOCK_RATE[WIRELESS_MODE_11b]; + + /* Compensate for half/quarter rate */ + if (c != AH_NULL && IEEE80211_IS_CHAN_HALF(c)) + clks = clks / 2; + else if (c != AH_NULL && IEEE80211_IS_CHAN_QUARTER(c)) + clks = clks / 4; + return clks; } @@ -615,6 +698,10 @@ ath_hal_getcapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type, return pCap->hal4AddrAggrSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_EXT_CHAN_DFS: return pCap->halExtChanDfsSupport ? HAL_OK : HAL_ENOTSUPP; + case HAL_CAP_RX_STBC: + return pCap->halRxStbcSupport ? HAL_OK : HAL_ENOTSUPP; + case HAL_CAP_TX_STBC: + return pCap->halTxStbcSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_COMBINED_RADAR_RSSI: return pCap->halUseCombinedRadarRssi ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_AUTO_SLEEP: @@ -626,13 +713,47 @@ ath_hal_getcapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type, case HAL_CAP_REG_FLAG: *result = AH_PRIVATE(ah)->ah_currentRDext; return HAL_OK; + case HAL_CAP_ENHANCED_DMA_SUPPORT: + return pCap->halEnhancedDmaSupport ? HAL_OK : HAL_ENOTSUPP; + case HAL_CAP_NUM_TXMAPS: + *result = pCap->halNumTxMaps; + return HAL_OK; + case HAL_CAP_TXDESCLEN: + *result = pCap->halTxDescLen; + return HAL_OK; + case HAL_CAP_TXSTATUSLEN: + *result = pCap->halTxStatusLen; + return HAL_OK; + case HAL_CAP_RXSTATUSLEN: + *result = pCap->halRxStatusLen; + return HAL_OK; + case HAL_CAP_RXFIFODEPTH: + switch (capability) { + case HAL_RX_QUEUE_HP: + *result = pCap->halRxHpFifoDepth; + return HAL_OK; + case HAL_RX_QUEUE_LP: + *result = pCap->halRxLpFifoDepth; + return HAL_OK; + default: + return HAL_ENOTSUPP; + } + case HAL_CAP_RXBUFSIZE: + case HAL_CAP_NUM_MR_RETRIES: + *result = pCap->halNumMRRetries; + return HAL_OK; case HAL_CAP_BT_COEX: return pCap->halBtCoexSupport ? HAL_OK : HAL_ENOTSUPP; + case HAL_CAP_SPECTRAL_SCAN: + return pCap->halSpectralScanSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_HT20_SGI: return pCap->halHTSGI20Support ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_RXTSTAMP_PREC: /* rx desc tstamp precision (bits) */ *result = pCap->halTstampPrecision; return HAL_OK; + case HAL_CAP_ANT_DIV_COMB: /* AR9285/AR9485 LNA diversity */ + return pCap->halAntDivCombSupport ? HAL_OK : HAL_ENOTSUPP; + case HAL_CAP_ENHANCED_DFS_SUPPORT: return pCap->halEnhancedDfsSupport ? HAL_OK : HAL_ENOTSUPP; @@ -659,6 +780,17 @@ ath_hal_getcapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type, return pCap->halHasRxSelfLinkedTail ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_LONG_RXDESC_TSF: /* 32 bit TSF in RX descriptor? */ return pCap->halHasLongRxDescTsf ? HAL_OK : HAL_ENOTSUPP; + case HAL_CAP_BB_READ_WAR: /* Baseband read WAR */ + return pCap->halHasBBReadWar? HAL_OK : HAL_ENOTSUPP; + case HAL_CAP_SERIALISE_WAR: /* PCI register serialisation */ + return pCap->halSerialiseRegWar ? HAL_OK : HAL_ENOTSUPP; + case HAL_CAP_MFP: /* Management frame protection setting */ + *result = pCap->halMfpSupport; + return HAL_OK; + case HAL_CAP_RX_LNA_MIXING: /* Hardware uses an RX LNA mixer to map 2 antennas to a 1 stream receiver */ + return pCap->halRxUsingLnaMixing ? HAL_OK : HAL_ENOTSUPP; + case HAL_CAP_DO_MYBEACON: /* Hardware supports filtering my-beacons */ + return pCap->halRxDoMyBeacon ? HAL_OK : HAL_ENOTSUPP; default: return HAL_EINVAL; } @@ -721,10 +853,11 @@ ath_hal_getregdump(struct ath_hal *ah, const HAL_REGRANGE *regs, int i; for (i = 0; space >= 2*sizeof(uint32_t); i++) { - u_int r = regs[i].start; - u_int e = regs[i].end; - *dp++ = (r<<16) | e; - space -= sizeof(uint32_t); + uint32_t r = regs[i].start; + uint32_t e = regs[i].end; + *dp++ = r; + *dp++ = e; + space -= 2*sizeof(uint32_t); do { *dp++ = OS_REG_READ(ah, r); r += sizeof(uint32_t); @@ -748,6 +881,7 @@ ath_hal_getdiagstate(struct ath_hal *ah, int request, const void *args, uint32_t argsize, void **result, uint32_t *resultsize) { + switch (request) { case HAL_DIAG_REVS: *result = &AH_PRIVATE(ah)->ah_devid; @@ -805,6 +939,10 @@ ath_hal_getdiagstate(struct ath_hal *ah, int request, } else return AH_FALSE; return AH_TRUE; + case HAL_DIAG_CHANSURVEY: + *result = &AH_PRIVATE(ah)->ah_chansurvey; + *resultsize = sizeof(HAL_CHANNEL_SURVEY); + return AH_TRUE; } return AH_FALSE; } @@ -940,7 +1078,7 @@ ath_hal_getChanNoise(struct ath_hal *ah, const struct ieee80211_channel *chan) * populated with values from NOISE_FLOOR[] + ath_hal_getNfAdjust(). * * The caller must supply ctl/ext NF arrays which are at least - * AH_MIMO_MAX_CHAINS entries long. + * AH_MAX_CHAINS entries long. */ int ath_hal_get_mimo_chan_noise(struct ath_hal *ah, @@ -956,7 +1094,7 @@ ath_hal_get_mimo_chan_noise(struct ath_hal *ah, HALDEBUG(ah, HAL_DEBUG_NFCAL, "%s: invalid channel %u/0x%x; no mapping\n", __func__, chan->ic_freq, chan->ic_flags); - for (i = 0; i < AH_MIMO_MAX_CHAINS; i++) { + for (i = 0; i < AH_MAX_CHAINS; i++) { nf_ctl[i] = nf_ext[i] = 0; } return 0; @@ -964,7 +1102,7 @@ ath_hal_get_mimo_chan_noise(struct ath_hal *ah, /* Return 0 if there's no valid MIMO values (yet) */ if (! (ichan->privFlags & CHANNEL_MIMO_NF_VALID)) { - for (i = 0; i < AH_MIMO_MAX_CHAINS; i++) { + for (i = 0; i < AH_MAX_CHAINS; i++) { nf_ctl[i] = nf_ext[i] = 0; } return 0; @@ -977,7 +1115,7 @@ ath_hal_get_mimo_chan_noise(struct ath_hal *ah, * stations which have a very low RSSI, below the * 'normalised' NF values in NOISE_FLOOR[]. */ - for (i = 0; i < AH_MIMO_MAX_CHAINS; i++) { + for (i = 0; i < AH_MAX_CHAINS; i++) { nf_ctl[i] = nf_ext[i] = NOISE_FLOOR[mode] + ath_hal_getNfAdjust(ah, ichan); } @@ -996,7 +1134,7 @@ ath_hal_get_mimo_chan_noise(struct ath_hal *ah, * don't "wrap" when RSSI is less than the "adjusted" NF value. * ("Adjust" here is via ichan->noiseFloorAdjust.) */ - for (i = 0; i < AH_MIMO_MAX_CHAINS; i++) { + for (i = 0; i < AH_MAX_CHAINS; i++) { nf_ctl[i] = ichan->noiseFloorCtl[i] + ath_hal_getNfAdjust(ah, ichan); nf_ext[i] = ichan->noiseFloorExt[i] + ath_hal_getNfAdjust(ah, ichan); } @@ -1258,3 +1396,74 @@ ath_hal_getcca(struct ath_hal *ah) return 1; return ((diag & 0x500000) == 0); } + +/* + * This routine is only needed when supporting EEPROM-in-RAM setups + * (eg embedded SoCs and on-board PCI/PCIe devices.) + */ +/* NB: This is in 16 bit words; not bytes */ +/* XXX This doesn't belong here! */ +#define ATH_DATA_EEPROM_SIZE 2048 + +HAL_BOOL +ath_hal_EepromDataRead(struct ath_hal *ah, u_int off, uint16_t *data) +{ + if (ah->ah_eepromdata == AH_NULL) { + HALDEBUG(ah, HAL_DEBUG_ANY, "%s: no eeprom data!\n", __func__); + return AH_FALSE; + } + if (off > ATH_DATA_EEPROM_SIZE) { + HALDEBUG(ah, HAL_DEBUG_ANY, "%s: offset %x > %x\n", + __func__, off, ATH_DATA_EEPROM_SIZE); + return AH_FALSE; + } + (*data) = ah->ah_eepromdata[off]; + return AH_TRUE; +} + +/* + * Do a 2GHz specific MHz->IEEE based on the hardware + * frequency. + * + * This is the unmapped frequency which is programmed into the hardware. + */ +int +ath_hal_mhz2ieee_2ghz(struct ath_hal *ah, int freq) +{ + + if (freq == 2484) + return 14; + if (freq < 2484) + return ((int) freq - 2407) / 5; + else + return 15 + ((freq - 2512) / 20); +} + +/* + * Clear the current survey data. + * + * This should be done during a channel change. + */ +void +ath_hal_survey_clear(struct ath_hal *ah) +{ + + OS_MEMZERO(&AH_PRIVATE(ah)->ah_chansurvey, + sizeof(AH_PRIVATE(ah)->ah_chansurvey)); +} + +/* + * Add a sample to the channel survey. + */ +void +ath_hal_survey_add_sample(struct ath_hal *ah, HAL_SURVEY_SAMPLE *hs) +{ + HAL_CHANNEL_SURVEY *cs; + + cs = &AH_PRIVATE(ah)->ah_chansurvey; + + OS_MEMCPY(&cs->samples[cs->cur_sample], hs, sizeof(*hs)); + cs->samples[cs->cur_sample].seq_num = cs->cur_seq; + cs->cur_sample = (cs->cur_sample + 1) % CHANNEL_SURVEY_SAMPLE_COUNT; + cs->cur_seq++; +} diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah.h index f4254c14fa..08cd53a661 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah.h @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ah.h 301043 2016-05-31 16:08:06Z adrian $ */ #ifndef _ATH_AH_H_ @@ -35,7 +35,7 @@ * This is intended to be used by various statistics gathering operations * (NF, RSSI, EVM). */ -#define AH_MIMO_MAX_CHAINS 3 +#define AH_MAX_CHAINS 3 #define AH_MIMO_MAX_EVM_PILOTS 6 /* @@ -73,6 +73,7 @@ typedef enum { HAL_EINPROGRESS = 15, /* Operation incomplete */ HAL_EEBADREG = 16, /* EEPROM invalid regulatory contents */ HAL_EEBADCC = 17, /* EEPROM invalid country code */ + HAL_INV_PMODE = 18, /* Couldn't bring out of sleep state */ } HAL_STATUS; typedef enum { @@ -109,7 +110,7 @@ typedef enum { HAL_CAP_TPC_ACK = 26, /* ack txpower with per-packet tpc */ HAL_CAP_TPC_CTS = 27, /* cts txpower with per-packet tpc */ HAL_CAP_11D = 28, /* 11d beacon support for changing cc */ - + HAL_CAP_PCIE_PS = 29, HAL_CAP_HT = 30, /* hardware can support HT */ HAL_CAP_GTXTO = 31, /* hardware supports global tx timeout */ HAL_CAP_FAST_CC = 32, /* hardware supports fast channel change */ @@ -118,7 +119,9 @@ typedef enum { HAL_CAP_NUM_GPIO_PINS = 36, /* number of GPIO pins */ HAL_CAP_CST = 38, /* hardware supports carrier sense timeout */ - + HAL_CAP_RIFS_RX = 39, + HAL_CAP_RIFS_TX = 40, + HAL_CAP_FORCE_PPM = 41, HAL_CAP_RTS_AGGR_LIMIT = 42, /* aggregation limit with RTS */ HAL_CAP_4ADDR_AGGR = 43, /* hardware is capable of 4addr aggregation */ HAL_CAP_DFS_DMN = 44, /* current DFS domain */ @@ -130,25 +133,73 @@ typedef enum { HAL_CAP_MBSSID_AGGR_SUPPORT = 49, /* Support for mBSSID Aggregation */ HAL_CAP_SPLIT_4KB_TRANS = 50, /* hardware supports descriptors straddling a 4k page boundary */ HAL_CAP_REG_FLAG = 51, /* Regulatory domain flags */ + HAL_CAP_BB_RIFS_HANG = 52, + HAL_CAP_RIFS_RX_ENABLED = 53, + HAL_CAP_BB_DFS_HANG = 54, + + HAL_CAP_RX_STBC = 58, + HAL_CAP_TX_STBC = 59, HAL_CAP_BT_COEX = 60, /* hardware is capable of bluetooth coexistence */ + HAL_CAP_DYNAMIC_SMPS = 61, /* Dynamic MIMO Power Save hardware support */ + + HAL_CAP_DS = 67, /* 2 stream */ + HAL_CAP_BB_RX_CLEAR_STUCK_HANG = 68, + HAL_CAP_MAC_HANG = 69, /* can MAC hang */ + HAL_CAP_MFP = 70, /* Management Frame Protection in hardware */ + + HAL_CAP_TS = 72, /* 3 stream */ + + HAL_CAP_ENHANCED_DMA_SUPPORT = 75, /* DMA FIFO support */ + HAL_CAP_NUM_TXMAPS = 76, /* Number of buffers in a transmit descriptor */ + HAL_CAP_TXDESCLEN = 77, /* Length of transmit descriptor */ + HAL_CAP_TXSTATUSLEN = 78, /* Length of transmit status descriptor */ + HAL_CAP_RXSTATUSLEN = 79, /* Length of transmit status descriptor */ + HAL_CAP_RXFIFODEPTH = 80, /* Receive hardware FIFO depth */ + HAL_CAP_RXBUFSIZE = 81, /* Receive Buffer Length */ + HAL_CAP_NUM_MR_RETRIES = 82, /* limit on multirate retries */ + HAL_CAP_OL_PWRCTRL = 84, /* Open loop TX power control */ + HAL_CAP_SPECTRAL_SCAN = 90, /* Hardware supports spectral scan */ + + HAL_CAP_BB_PANIC_WATCHDOG = 92, HAL_CAP_HT20_SGI = 96, /* hardware supports HT20 short GI */ + HAL_CAP_LDPC = 99, + HAL_CAP_RXTSTAMP_PREC = 100, /* rx desc tstamp precision (bits) */ + + HAL_CAP_ANT_DIV_COMB = 105, /* Enable antenna diversity/combining */ + HAL_CAP_PHYRESTART_CLR_WAR = 106, /* in some cases, clear phy restart to fix bb hang */ + HAL_CAP_ENTERPRISE_MODE = 107, /* Enterprise mode features */ + HAL_CAP_LDPCWAR = 108, + HAL_CAP_CHANNEL_SWITCH_TIME_USEC = 109, /* Channel change time, usec */ + HAL_CAP_ENABLE_APM = 110, /* APM enabled */ + HAL_CAP_PCIE_LCR_EXTSYNC_EN = 111, + HAL_CAP_PCIE_LCR_OFFSET = 112, + HAL_CAP_ENHANCED_DFS_SUPPORT = 117, /* hardware supports enhanced DFS */ + HAL_CAP_MCI = 118, + HAL_CAP_SMARTANTENNA = 119, + HAL_CAP_TRAFFIC_FAST_RECOVER = 120, + HAL_CAP_TX_DIVERSITY = 121, + HAL_CAP_CRDC = 122, /* The following are private to the FreeBSD HAL (224 onward) */ HAL_CAP_INTMIT = 229, /* interference mitigation */ HAL_CAP_RXORN_FATAL = 230, /* HAL_INT_RXORN treated as fatal */ HAL_CAP_BB_HANG = 235, /* can baseband hang */ - HAL_CAP_MAC_HANG = 236, /* can MAC hang */ HAL_CAP_INTRMASK = 237, /* bitmask of supported interrupts */ HAL_CAP_BSSIDMATCH = 238, /* hardware has disable bssid match */ HAL_CAP_STREAMS = 239, /* how many 802.11n spatial streams are available */ HAL_CAP_RXDESC_SELFLINK = 242, /* support a self-linked tail RX descriptor */ HAL_CAP_LONG_RXDESC_TSF = 243, /* hardware supports 32bit TSF in RX descriptor */ + HAL_CAP_BB_READ_WAR = 244, /* baseband read WAR */ + HAL_CAP_SERIALISE_WAR = 245, /* serialise register access on PCI */ + HAL_CAP_ENFORCE_TXOP = 246, /* Enforce TXOP if supported */ + HAL_CAP_RX_LNA_MIXING = 247, /* RX hardware uses LNA mixing */ + HAL_CAP_DO_MYBEACON = 248, /* Supports HAL_RX_FILTER_MYBEACON */ } HAL_CAPABILITY_TYPE; /* @@ -178,10 +229,26 @@ typedef enum { HAL_TX_QUEUE_CAB = 3, /* "crap after beacon" xmit q */ HAL_TX_QUEUE_UAPSD = 4, /* u-apsd power save xmit q */ HAL_TX_QUEUE_PSPOLL = 5, /* power save poll xmit q */ + HAL_TX_QUEUE_CFEND = 6, + HAL_TX_QUEUE_PAPRD = 7, } HAL_TX_QUEUE; #define HAL_NUM_TX_QUEUES 10 /* max possible # of queues */ +/* + * Receive queue types. These are used to tag + * each transmit queue in the hardware and to identify a set + * of transmit queues for operations such as start/stop dma. + */ +typedef enum { + HAL_RX_QUEUE_HP = 0, /* high priority recv queue */ + HAL_RX_QUEUE_LP = 1, /* low priority recv queue */ +} HAL_RX_QUEUE; + +#define HAL_NUM_RX_QUEUES 2 /* max possible # of queues */ + +#define HAL_TXFIFO_DEPTH 8 /* transmit fifo depth */ + /* * Transmit queue subtype. These map directly to * WME Access Categories (except for UPSD). Refer @@ -338,6 +405,7 @@ typedef enum { HAL_RX_FILTER_PROM = 0x00000020, /* Promiscuous mode */ HAL_RX_FILTER_PROBEREQ = 0x00000080, /* Allow probe request frames */ HAL_RX_FILTER_PHYERR = 0x00000100, /* Allow phy errors */ + HAL_RX_FILTER_MYBEACON = 0x00000200, /* Filter beacons other than mine */ HAL_RX_FILTER_COMPBAR = 0x00000400, /* Allow compressed BAR */ HAL_RX_FILTER_COMP_BA = 0x00000800, /* Allow compressed blockack */ HAL_RX_FILTER_PHYRADAR = 0x00002000, /* Allow phy radar errors */ @@ -346,7 +414,7 @@ typedef enum { /* Allow all mcast/bcast frames */ /* - * Magic RX filter flags that aren't targetting hardware bits + * Magic RX filter flags that aren't targeting hardware bits * but instead the HAL sets individual bits - eg PHYERR will result * in OFDM/CCK timing error frames being received. */ @@ -360,6 +428,23 @@ typedef enum { HAL_PM_UNDEFINED = 3 } HAL_POWER_MODE; +/* + * Enterprise mode flags + */ +#define AH_ENT_DUAL_BAND_DISABLE 0x00000001 +#define AH_ENT_CHAIN2_DISABLE 0x00000002 +#define AH_ENT_5MHZ_DISABLE 0x00000004 +#define AH_ENT_10MHZ_DISABLE 0x00000008 +#define AH_ENT_49GHZ_DISABLE 0x00000010 +#define AH_ENT_LOOPBACK_DISABLE 0x00000020 +#define AH_ENT_TPC_PERF_DISABLE 0x00000040 +#define AH_ENT_MIN_PKT_SIZE_DISABLE 0x00000080 +#define AH_ENT_SPECTRAL_PRECISION 0x00000300 +#define AH_ENT_SPECTRAL_PRECISION_S 8 +#define AH_ENT_RTSCTS_DELIM_WAR 0x00010000 + +#define AH_FIRST_DESC_NDELIMS 60 + /* * NOTE WELL: * These are mapped to take advantage of the common locations for many of @@ -371,18 +456,24 @@ typedef enum { */ typedef enum { HAL_INT_RX = 0x00000001, /* Non-common mapping */ - HAL_INT_RXDESC = 0x00000002, + HAL_INT_RXDESC = 0x00000002, /* Legacy mapping */ + HAL_INT_RXERR = 0x00000004, + HAL_INT_RXHP = 0x00000001, /* EDMA */ + HAL_INT_RXLP = 0x00000002, /* EDMA */ HAL_INT_RXNOFRM = 0x00000008, HAL_INT_RXEOL = 0x00000010, HAL_INT_RXORN = 0x00000020, HAL_INT_TX = 0x00000040, /* Non-common mapping */ HAL_INT_TXDESC = 0x00000080, HAL_INT_TIM_TIMER= 0x00000100, + HAL_INT_MCI = 0x00000200, + HAL_INT_BBPANIC = 0x00000400, HAL_INT_TXURN = 0x00000800, HAL_INT_MIB = 0x00001000, HAL_INT_RXPHY = 0x00004000, HAL_INT_RXKCM = 0x00008000, HAL_INT_SWBA = 0x00010000, + HAL_INT_BRSSI = 0x00020000, HAL_INT_BMISS = 0x00040000, HAL_INT_BNR = 0x00100000, HAL_INT_TIM = 0x00200000, /* Non-common mapping */ @@ -392,6 +483,8 @@ typedef enum { HAL_INT_CABEND = 0x02000000, /* Non-common mapping */ HAL_INT_TSFOOR = 0x04000000, /* Non-common mapping */ HAL_INT_TBTT = 0x08000000, /* Non-common mapping */ + /* Atheros ref driver has a generic timer interrupt now..*/ + HAL_INT_GENTIMER = 0x08000000, /* Non-common mapping */ HAL_INT_CST = 0x10000000, /* Non-common mapping */ HAL_INT_GTT = 0x20000000, /* Non-common mapping */ HAL_INT_FATAL = 0x40000000, /* Non-common mapping */ @@ -414,18 +507,86 @@ typedef enum { | HAL_INT_RXKCM | HAL_INT_SWBA | HAL_INT_BMISS + | HAL_INT_BRSSI | HAL_INT_BNR | HAL_INT_GPIO, } HAL_INT; +/* + * MSI vector assignments + */ typedef enum { - HAL_GPIO_MUX_OUTPUT = 0, - HAL_GPIO_MUX_PCIE_ATTENTION_LED = 1, - HAL_GPIO_MUX_PCIE_POWER_LED = 2, - HAL_GPIO_MUX_TX_FRAME = 3, - HAL_GPIO_MUX_RX_CLEAR_EXTERNAL = 4, - HAL_GPIO_MUX_MAC_NETWORK_LED = 5, - HAL_GPIO_MUX_MAC_POWER_LED = 6 + HAL_MSIVEC_MISC = 0, + HAL_MSIVEC_TX = 1, + HAL_MSIVEC_RXLP = 2, + HAL_MSIVEC_RXHP = 3, +} HAL_MSIVEC; + +typedef enum { + HAL_INT_LINE = 0, + HAL_INT_MSI = 1, +} HAL_INT_TYPE; + +/* For interrupt mitigation registers */ +typedef enum { + HAL_INT_RX_FIRSTPKT=0, + HAL_INT_RX_LASTPKT, + HAL_INT_TX_FIRSTPKT, + HAL_INT_TX_LASTPKT, + HAL_INT_THRESHOLD +} HAL_INT_MITIGATION; + +/* XXX this is duplicate information! */ +typedef struct { + u_int32_t cyclecnt_diff; /* delta cycle count */ + u_int32_t rxclr_cnt; /* rx clear count */ + u_int32_t extrxclr_cnt; /* ext chan rx clear count */ + u_int32_t txframecnt_diff; /* delta tx frame count */ + u_int32_t rxframecnt_diff; /* delta rx frame count */ + u_int32_t listen_time; /* listen time in msec - time for which ch is free */ + u_int32_t ofdmphyerr_cnt; /* OFDM err count since last reset */ + u_int32_t cckphyerr_cnt; /* CCK err count since last reset */ + u_int32_t ofdmphyerrcnt_diff; /* delta OFDM Phy Error Count */ + HAL_BOOL valid; /* if the stats are valid*/ +} HAL_ANISTATS; + +typedef struct { + u_int8_t txctl_offset; + u_int8_t txctl_numwords; + u_int8_t txstatus_offset; + u_int8_t txstatus_numwords; + + u_int8_t rxctl_offset; + u_int8_t rxctl_numwords; + u_int8_t rxstatus_offset; + u_int8_t rxstatus_numwords; + + u_int8_t macRevision; +} HAL_DESC_INFO; + +typedef enum { + HAL_GPIO_OUTPUT_MUX_AS_OUTPUT = 0, + HAL_GPIO_OUTPUT_MUX_PCIE_ATTENTION_LED = 1, + HAL_GPIO_OUTPUT_MUX_PCIE_POWER_LED = 2, + HAL_GPIO_OUTPUT_MUX_MAC_NETWORK_LED = 3, + HAL_GPIO_OUTPUT_MUX_MAC_POWER_LED = 4, + HAL_GPIO_OUTPUT_MUX_AS_WLAN_ACTIVE = 5, + HAL_GPIO_OUTPUT_MUX_AS_TX_FRAME = 6, + + HAL_GPIO_OUTPUT_MUX_AS_MCI_WLAN_DATA, + HAL_GPIO_OUTPUT_MUX_AS_MCI_WLAN_CLK, + HAL_GPIO_OUTPUT_MUX_AS_MCI_BT_DATA, + HAL_GPIO_OUTPUT_MUX_AS_MCI_BT_CLK, + HAL_GPIO_OUTPUT_MUX_AS_WL_IN_TX, + HAL_GPIO_OUTPUT_MUX_AS_WL_IN_RX, + HAL_GPIO_OUTPUT_MUX_AS_BT_IN_TX, + HAL_GPIO_OUTPUT_MUX_AS_BT_IN_RX, + HAL_GPIO_OUTPUT_MUX_AS_RUCKUS_STROBE, + HAL_GPIO_OUTPUT_MUX_AS_RUCKUS_DATA, + HAL_GPIO_OUTPUT_MUX_AS_SMARTANT_CTRL0, + HAL_GPIO_OUTPUT_MUX_AS_SMARTANT_CTRL1, + HAL_GPIO_OUTPUT_MUX_AS_SMARTANT_CTRL2, + HAL_GPIO_OUTPUT_MUX_NUM_ENTRIES } HAL_GPIO_MUX_TYPE; typedef enum { @@ -434,6 +595,15 @@ typedef enum { HAL_GPIO_INTR_DISABLE = 2 } HAL_GPIO_INTR_TYPE; +typedef struct halCounters { + u_int32_t tx_frame_count; + u_int32_t rx_frame_count; + u_int32_t rx_clear_count; + u_int32_t cycle_count; + u_int8_t is_rx_active; // true (1) or false (0) + u_int8_t is_tx_active; // true (1) or false (0) +} HAL_COUNTERS; + typedef enum { HAL_RFGAIN_INACTIVE = 0, HAL_RFGAIN_READ_REQUESTED = 1, @@ -456,6 +626,17 @@ typedef struct { uint32_t beacons; } HAL_MIB_STATS; +/* + * These bits represent what's in ah_currentRDext. + */ +typedef enum { + REG_EXT_FCC_MIDBAND = 0, + REG_EXT_JAPAN_MIDBAND = 1, + REG_EXT_FCC_DFS_HT40 = 2, + REG_EXT_JAPAN_NONDFS_HT40 = 3, + REG_EXT_JAPAN_DFS_HT40 = 4 +} REG_EXT_BITMAP; + enum { HAL_MODE_11A = 0x001, /* 11a channels */ HAL_MODE_TURBO = 0x002, /* 11a turbo-only channels */ @@ -483,7 +664,7 @@ enum { typedef struct { int rateCount; /* NB: for proper padding */ - uint8_t rateCodeToIndex[144]; /* back mapping */ + uint8_t rateCodeToIndex[256]; /* back mapping */ struct { uint8_t valid; /* valid for rate control use */ uint8_t phy; /* CCK/OFDM/XR */ @@ -497,12 +678,12 @@ typedef struct { * rate; used for dur. calcs */ uint16_t lpAckDuration; /* long preamble ACK duration */ uint16_t spAckDuration; /* short preamble ACK duration*/ - } info[32]; + } info[64]; } HAL_RATE_TABLE; typedef struct { u_int rs_count; /* number of valid entries */ - uint8_t rs_rates[32]; /* rates */ + uint8_t rs_rates[64]; /* rates */ } HAL_RATE_SET; /* @@ -515,13 +696,16 @@ typedef enum { typedef struct { u_int Tries; - u_int Rate; + u_int Rate; /* hardware rate code */ + u_int RateIndex; /* rate series table index */ u_int PktDuration; u_int ChSel; u_int RateFlags; #define HAL_RATESERIES_RTS_CTS 0x0001 /* use rts/cts w/this series */ #define HAL_RATESERIES_2040 0x0002 /* use ext channel for series */ #define HAL_RATESERIES_HALFGI 0x0004 /* use half-gi for series */ +#define HAL_RATESERIES_STBC 0x0008 /* use STBC for series */ + u_int tx_power_cap; /* in 1/2 dBm units XXX TODO */ } HAL_11N_RATE_SERIES; typedef enum { @@ -545,6 +729,11 @@ typedef enum { HAL_RX_CLEAR_EXT_LOW = 0x2, /* force extension channel to appear busy */ } HAL_HT_RXCLEAR; +typedef enum { + HAL_FREQ_BAND_5GHZ = 0, + HAL_FREQ_BAND_2GHZ = 1, +} HAL_FREQ_BAND; + /* * Antenna switch control. By default antenna selection * enables multiple (2) antenna use. To force use of the @@ -564,15 +753,33 @@ typedef enum { HAL_M_MONITOR = 8 /* Monitor mode */ } HAL_OPMODE; +typedef enum { + HAL_RESET_NORMAL = 0, /* Do normal reset */ + HAL_RESET_BBPANIC = 1, /* Reset because of BB panic */ + HAL_RESET_FORCE_COLD = 2, /* Force full reset */ +} HAL_RESET_TYPE; + typedef struct { uint8_t kv_type; /* one of HAL_CIPHER */ - uint8_t kv_pad; + uint8_t kv_apsd; /* Mask for APSD enabled ACs */ uint16_t kv_len; /* length in bits */ uint8_t kv_val[16]; /* enough for 128-bit keys */ uint8_t kv_mic[8]; /* TKIP MIC key */ uint8_t kv_txmic[8]; /* TKIP TX MIC key (optional) */ } HAL_KEYVAL; +/* + * This is the TX descriptor field which marks the key padding requirement. + * The naming is unfortunately unclear. + */ +#define AH_KEYTYPE_MASK 0x0F +typedef enum { + HAL_KEY_TYPE_CLEAR, + HAL_KEY_TYPE_WEP, + HAL_KEY_TYPE_AES, + HAL_KEY_TYPE_TKIP, +} HAL_KEY_TYPE; + typedef enum { HAL_CIPHER_WEP = 0, HAL_CIPHER_AES_OCB = 1, @@ -601,9 +808,16 @@ typedef struct { uint32_t bs_nexttbtt; /* next beacon in TU */ uint32_t bs_nextdtim; /* next DTIM in TU */ uint32_t bs_intval; /* beacon interval+flags */ +/* + * HAL_BEACON_PERIOD, HAL_BEACON_ENA and HAL_BEACON_RESET_TSF + * are all 1:1 correspondances with the pre-11n chip AR_BEACON + * register. + */ #define HAL_BEACON_PERIOD 0x0000ffff /* beacon interval period */ +#define HAL_BEACON_PERIOD_TU8 0x0007ffff /* beacon interval, tu/8 */ #define HAL_BEACON_ENA 0x00800000 /* beacon xmit enable */ #define HAL_BEACON_RESET_TSF 0x01000000 /* clear TSF */ +#define HAL_TSFOOR_THRESHOLD 0x00004240 /* TSF OOR thresh (16k uS) */ uint32_t bs_dtimperiod; uint16_t bs_cfpperiod; /* CFP period in TU */ uint16_t bs_cfpmaxduration; /* max CFP duration in TU */ @@ -611,6 +825,7 @@ typedef struct { uint16_t bs_timoffset; /* byte offset to TIM bitmap */ uint16_t bs_bmissthreshold; /* beacon miss threshold */ uint32_t bs_sleepduration; /* max sleep duration */ + uint32_t bs_tsfoor_threshold; /* TSF out of range threshold */ } HAL_BEACON_STATE; /* @@ -641,6 +856,49 @@ typedef struct { #define HAL_RSSI_EP_MULTIPLIER (1<<7) /* pow2 to optimize out * and / */ +/* + * This is the ANI state and MIB stats. + * + * It's used by the HAL modules to keep state /and/ by the debug ioctl + * to fetch ANI information. + */ +typedef struct { + uint32_t ast_ani_niup; /* ANI increased noise immunity */ + uint32_t ast_ani_nidown; /* ANI decreased noise immunity */ + uint32_t ast_ani_spurup; /* ANI increased spur immunity */ + uint32_t ast_ani_spurdown;/* ANI descreased spur immunity */ + uint32_t ast_ani_ofdmon; /* ANI OFDM weak signal detect on */ + uint32_t ast_ani_ofdmoff;/* ANI OFDM weak signal detect off */ + uint32_t ast_ani_cckhigh;/* ANI CCK weak signal threshold high */ + uint32_t ast_ani_ccklow; /* ANI CCK weak signal threshold low */ + uint32_t ast_ani_stepup; /* ANI increased first step level */ + uint32_t ast_ani_stepdown;/* ANI decreased first step level */ + uint32_t ast_ani_ofdmerrs;/* ANI cumulative ofdm phy err count */ + uint32_t ast_ani_cckerrs;/* ANI cumulative cck phy err count */ + uint32_t ast_ani_reset; /* ANI parameters zero'd for non-STA */ + uint32_t ast_ani_lzero; /* ANI listen time forced to zero */ + uint32_t ast_ani_lneg; /* ANI listen time calculated < 0 */ + HAL_MIB_STATS ast_mibstats; /* MIB counter stats */ + HAL_NODE_STATS ast_nodestats; /* Latest rssi stats from driver */ +} HAL_ANI_STATS; + +typedef struct { + uint8_t noiseImmunityLevel; + uint8_t spurImmunityLevel; + uint8_t firstepLevel; + uint8_t ofdmWeakSigDetectOff; + uint8_t cckWeakSigThreshold; + uint32_t listenTime; + + /* NB: intentionally ordered so data exported to user space is first */ + uint32_t txFrameCount; /* Last txFrameCount */ + uint32_t rxFrameCount; /* Last rx Frame count */ + uint32_t cycleCount; /* Last cycleCount + (to detect wrap-around) */ + uint32_t ofdmPhyErrCount;/* OFDM err count since last reset */ + uint32_t cckPhyErrCount; /* CCK err count since last reset */ +} HAL_ANI_STATE; + struct ath_desc; struct ath_tx_status; struct ath_rx_status; @@ -658,7 +916,11 @@ typedef struct { uint32_t tx_busy; uint32_t rx_busy; uint32_t chan_busy; + uint32_t ext_chan_busy; uint32_t cycle_count; + /* XXX TODO */ + uint32_t ofdm_phyerr_count; + uint32_t cck_phyerr_count; } HAL_SURVEY_SAMPLE; /* @@ -691,8 +953,11 @@ typedef enum { HAL_ANI_SPUR_IMMUNITY_LEVEL = 5, /* set level */ HAL_ANI_MODE = 6, /* 0 => manual, 1 => auto (XXX do not change) */ HAL_ANI_PHYERR_RESET = 7, /* reset phy error stats */ + HAL_ANI_MRC_CCK = 8, } HAL_ANI_CMD; +#define HAL_ANI_ALL 0xffffffff + /* * This is the layout of the ANI INTMIT capability. * @@ -731,10 +996,27 @@ typedef struct { */ int32_t pe_extchannel; /* Enable DFS on ext channel */ int32_t pe_enabled; /* Whether radar detection is enabled */ + int32_t pe_enrelpwr; + int32_t pe_en_relstep_check; } HAL_PHYERR_PARAM; #define HAL_PHYERR_PARAM_NOVAL 65535 -#define HAL_PHYERR_PARAM_ENABLE 0x8000 /* Enable/Disable if applicable */ + +typedef struct { + u_int16_t ss_fft_period; /* Skip interval for FFT reports */ + u_int16_t ss_period; /* Spectral scan period */ + u_int16_t ss_count; /* # of reports to return from ss_active */ + u_int16_t ss_short_report;/* Set to report ony 1 set of FFT results */ + u_int8_t radar_bin_thresh_sel; /* strong signal radar FFT threshold configuration */ + u_int16_t ss_spectral_pri; /* are we doing a noise power cal ? */ + int8_t ss_nf_cal[AH_MAX_CHAINS*2]; /* nf calibrated values for ctl+ext from eeprom */ + int8_t ss_nf_pwr[AH_MAX_CHAINS*2]; /* nf pwr values for ctl+ext from eeprom */ + int32_t ss_nf_temp_data; /* temperature data taken during nf scan */ + int ss_enabled; + int ss_active; +} HAL_SPECTRAL_PARAM; +#define HAL_SPECTRAL_PARAM_NOVAL 0xFFFF +#define HAL_SPECTRAL_PARAM_ENABLE 0x8000 /* Enable/Disable if applicable */ /* * DFS operating mode flags. @@ -746,6 +1028,39 @@ typedef enum { HAL_DFS_MKK4_DOMAIN = 3, /* Japan dfs domain */ } HAL_DFS_DOMAIN; + +/* + * MFP decryption options for initializing the MAC. + */ +typedef enum { + HAL_MFP_QOSDATA = 0, /* Decrypt MFP frames like QoS data frames. All chips before Merlin. */ + HAL_MFP_PASSTHRU, /* Don't decrypt MFP frames at all. Passthrough */ + HAL_MFP_HW_CRYPTO /* hardware decryption enabled. Merlin can do it. */ +} HAL_MFP_OPT_T; + +/* LNA config supported */ +typedef enum { + HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2 = 0, + HAL_ANT_DIV_COMB_LNA2 = 1, + HAL_ANT_DIV_COMB_LNA1 = 2, + HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2 = 3, +} HAL_ANT_DIV_COMB_LNA_CONF; + +typedef struct { + u_int8_t main_lna_conf; + u_int8_t alt_lna_conf; + u_int8_t fast_div_bias; + u_int8_t main_gaintb; + u_int8_t alt_gaintb; + u_int8_t antdiv_configgroup; + int8_t lna1_lna2_delta; +} HAL_ANT_COMB_CONFIG; + +#define DEFAULT_ANTDIV_CONFIG_GROUP 0x00 +#define HAL_ANTDIV_CONFIG_GROUP_1 0x01 +#define HAL_ANTDIV_CONFIG_GROUP_2 0x02 +#define HAL_ANTDIV_CONFIG_GROUP_3 0x03 + /* * Flag for setting QUIET period */ @@ -770,6 +1085,48 @@ struct hal_dfs_event { }; typedef struct hal_dfs_event HAL_DFS_EVENT; +/* + * Generic Timer domain + */ +typedef enum { + HAL_GEN_TIMER_TSF = 0, + HAL_GEN_TIMER_TSF2, + HAL_GEN_TIMER_TSF_ANY +} HAL_GEN_TIMER_DOMAIN; + +/* + * BT Co-existence definitions + */ +#include "ath_hal/ah_btcoex.h" + +struct hal_bb_panic_info { + u_int32_t status; + u_int32_t tsf; + u_int32_t phy_panic_wd_ctl1; + u_int32_t phy_panic_wd_ctl2; + u_int32_t phy_gen_ctrl; + u_int32_t rxc_pcnt; + u_int32_t rxf_pcnt; + u_int32_t txf_pcnt; + u_int32_t cycles; + u_int32_t wd; + u_int32_t det; + u_int32_t rdar; + u_int32_t r_odfm; + u_int32_t r_cck; + u_int32_t t_odfm; + u_int32_t t_cck; + u_int32_t agc; + u_int32_t src; +}; + +/* Serialize Register Access Mode */ +typedef enum { + SER_REG_MODE_OFF = 0, + SER_REG_MODE_ON = 1, + SER_REG_MODE_AUTO = 2, +} SER_REG_MODE; + typedef struct { int ah_debug; /* only used if AH_DEBUG is defined */ @@ -779,6 +1136,51 @@ typedef struct int ah_dma_beacon_response_time;/* in TU's */ int ah_sw_beacon_response_time; /* in TU's */ int ah_additional_swba_backoff; /* in TU's */ + int ah_force_full_reset; /* force full chip reset rather then warm reset */ + int ah_serialise_reg_war; /* force serialisation of register IO */ + + /* XXX these don't belong here, they're just for the ar9300 HAL port effort */ + int ath_hal_desc_tpc; /* Per-packet TPC */ + int ath_hal_sta_update_tx_pwr_enable; /* GreenTX */ + int ath_hal_sta_update_tx_pwr_enable_S1; /* GreenTX */ + int ath_hal_sta_update_tx_pwr_enable_S2; /* GreenTX */ + int ath_hal_sta_update_tx_pwr_enable_S3; /* GreenTX */ + + /* I'm not sure what the default values for these should be */ + int ath_hal_pll_pwr_save; + int ath_hal_pcie_power_save_enable; + int ath_hal_intr_mitigation_rx; + int ath_hal_intr_mitigation_tx; + + int ath_hal_pcie_clock_req; +#define AR_PCIE_PLL_PWRSAVE_CONTROL (1<<0) +#define AR_PCIE_PLL_PWRSAVE_ON_D3 (1<<1) +#define AR_PCIE_PLL_PWRSAVE_ON_D0 (1<<2) + + int ath_hal_pcie_waen; + int ath_hal_pcie_ser_des_write; + + /* these are important for correct AR9300 behaviour */ + int ath_hal_ht_enable; /* needs to be enabled for AR9300 HT */ + int ath_hal_diversity_control; + int ath_hal_antenna_switch_swap; + int ath_hal_ext_lna_ctl_gpio; + int ath_hal_spur_mode; + int ath_hal_6mb_ack; /* should set this to 1 for 11a/11na? */ + int ath_hal_enable_msi; /* enable MSI interrupts (needed?) */ + int ath_hal_beacon_filter_interval; /* ok to be 0 for now? */ + + /* For now, set this to 0 - net80211 needs to know about hardware MFP support */ + int ath_hal_mfp_support; + + int ath_hal_enable_ani; /* should set this.. */ + int ath_hal_cwm_ignore_ext_cca; + int ath_hal_show_bb_panic; + int ath_hal_ant_ctrl_comm2g_switch_enable; + int ath_hal_ext_atten_margin_cfg; + int ath_hal_min_gainidx; + int ath_hal_war70c; + uint32_t ath_hal_mci_config; } HAL_OPS_CONFIG; /* @@ -809,6 +1211,12 @@ struct ath_hal { uint16_t *ah_eepromdata; /* eeprom buffer, if needed */ + uint32_t ah_intrstate[8]; /* last int state */ + uint32_t ah_syncstate; /* last sync intr state */ + + /* Current powerstate from HAL calls */ + HAL_POWER_MODE ah_powerMode; + HAL_OPS_CONFIG ah_config; const HAL_RATE_TABLE *__ahdecl(*ah_getRateTable)(struct ath_hal *, u_int mode); @@ -817,10 +1225,13 @@ struct ath_hal { /* Reset functions */ HAL_BOOL __ahdecl(*ah_reset)(struct ath_hal *, HAL_OPMODE, struct ieee80211_channel *, - HAL_BOOL bChannelChange, HAL_STATUS *status); + HAL_BOOL bChannelChange, + HAL_RESET_TYPE resetType, + HAL_STATUS *status); HAL_BOOL __ahdecl(*ah_phyDisable)(struct ath_hal *); HAL_BOOL __ahdecl(*ah_disable)(struct ath_hal *); - void __ahdecl(*ah_configPCIE)(struct ath_hal *, HAL_BOOL restore); + void __ahdecl(*ah_configPCIE)(struct ath_hal *, HAL_BOOL restore, + HAL_BOOL power_off); void __ahdecl(*ah_disablePCIE)(struct ath_hal *); void __ahdecl(*ah_setPCUConfig)(struct ath_hal *); HAL_BOOL __ahdecl(*ah_perCalibration)(struct ath_hal*, @@ -865,7 +1276,8 @@ struct ath_hal { u_int txRate2, u_int txTries2, u_int txRate3, u_int txTries3); HAL_BOOL __ahdecl(*ah_fillTxDesc)(struct ath_hal *, struct ath_desc *, - u_int segLen, HAL_BOOL firstSeg, + HAL_DMA_ADDR *bufAddrList, uint32_t *segLenList, + u_int descId, u_int qcuId, HAL_BOOL firstSeg, HAL_BOOL lastSeg, const struct ath_desc *); HAL_STATUS __ahdecl(*ah_procTxDesc)(struct ath_hal *, struct ath_desc *, struct ath_tx_status *); @@ -873,10 +1285,20 @@ struct ath_hal { void __ahdecl(*ah_reqTxIntrDesc)(struct ath_hal *, struct ath_desc*); HAL_BOOL __ahdecl(*ah_getTxCompletionRates)(struct ath_hal *, const struct ath_desc *ds, int *rates, int *tries); + void __ahdecl(*ah_setTxDescLink)(struct ath_hal *ah, void *ds, + uint32_t link); + void __ahdecl(*ah_getTxDescLink)(struct ath_hal *ah, void *ds, + uint32_t *link); + void __ahdecl(*ah_getTxDescLinkPtr)(struct ath_hal *ah, void *ds, + uint32_t **linkptr); + void __ahdecl(*ah_setupTxStatusRing)(struct ath_hal *, + void *ts_start, uint32_t ts_paddr_start, + uint16_t size); + void __ahdecl(*ah_getTxRawTxDesc)(struct ath_hal *, u_int32_t *); /* Receive Functions */ - uint32_t __ahdecl(*ah_getRxDP)(struct ath_hal*); - void __ahdecl(*ah_setRxDP)(struct ath_hal*, uint32_t rxdp); + uint32_t __ahdecl(*ah_getRxDP)(struct ath_hal*, HAL_RX_QUEUE); + void __ahdecl(*ah_setRxDP)(struct ath_hal*, uint32_t rxdp, HAL_RX_QUEUE); void __ahdecl(*ah_enableReceive)(struct ath_hal*); HAL_BOOL __ahdecl(*ah_stopDmaReceive)(struct ath_hal*); void __ahdecl(*ah_startPcuReceive)(struct ath_hal*); @@ -902,9 +1324,6 @@ struct ath_hal { const struct ieee80211_channel *); void __ahdecl(*ah_procMibEvent)(struct ath_hal *, const HAL_NODE_STATS *); - void __ahdecl(*ah_rxAntCombDiversity)(struct ath_hal *, - struct ath_rx_status *, - unsigned long, int); /* Misc Functions */ HAL_STATUS __ahdecl(*ah_getCapability)(struct ath_hal *, @@ -934,6 +1353,7 @@ struct ath_hal { void __ahdecl(*ah_gpioSetIntr)(struct ath_hal*, u_int, uint32_t); uint32_t __ahdecl(*ah_getTsf32)(struct ath_hal*); uint64_t __ahdecl(*ah_getTsf64)(struct ath_hal*); + void __ahdecl(*ah_setTsf64)(struct ath_hal *, uint64_t); void __ahdecl(*ah_resetTsf)(struct ath_hal*); HAL_BOOL __ahdecl(*ah_detectCardPresent)(struct ath_hal*); void __ahdecl(*ah_updateMibCounters)(struct ath_hal*, @@ -959,17 +1379,32 @@ struct ath_hal { HAL_STATUS __ahdecl(*ah_setQuiet)(struct ath_hal *ah, uint32_t period, uint32_t duration, uint32_t nextStart, HAL_QUIET_FLAG flag); + void __ahdecl(*ah_setChainMasks)(struct ath_hal *, + uint32_t, uint32_t); /* DFS functions */ void __ahdecl(*ah_enableDfs)(struct ath_hal *ah, HAL_PHYERR_PARAM *pe); void __ahdecl(*ah_getDfsThresh)(struct ath_hal *ah, HAL_PHYERR_PARAM *pe); + HAL_BOOL __ahdecl(*ah_getDfsDefaultThresh)(struct ath_hal *ah, + HAL_PHYERR_PARAM *pe); HAL_BOOL __ahdecl(*ah_procRadarEvent)(struct ath_hal *ah, struct ath_rx_status *rxs, uint64_t fulltsf, const char *buf, HAL_DFS_EVENT *event); HAL_BOOL __ahdecl(*ah_isFastClockEnabled)(struct ath_hal *ah); + /* Spectral Scan functions */ + void __ahdecl(*ah_spectralConfigure)(struct ath_hal *ah, + HAL_SPECTRAL_PARAM *sp); + void __ahdecl(*ah_spectralGetConfig)(struct ath_hal *ah, + HAL_SPECTRAL_PARAM *sp); + void __ahdecl(*ah_spectralStart)(struct ath_hal *); + void __ahdecl(*ah_spectralStop)(struct ath_hal *); + HAL_BOOL __ahdecl(*ah_spectralIsEnabled)(struct ath_hal *); + HAL_BOOL __ahdecl(*ah_spectralIsActive)(struct ath_hal *); + /* XXX getNfPri() and getNfExt() */ + /* Key Cache Functions */ uint32_t __ahdecl(*ah_getKeyCacheSize)(struct ath_hal*); HAL_BOOL __ahdecl(*ah_resetKeyCacheEntry)(struct ath_hal*, uint16_t); @@ -1001,9 +1436,12 @@ struct ath_hal { /* 802.11n Functions */ HAL_BOOL __ahdecl(*ah_chainTxDesc)(struct ath_hal *, - struct ath_desc *, u_int, u_int, HAL_PKT_TYPE, - u_int, HAL_CIPHER, uint8_t, u_int, HAL_BOOL, - HAL_BOOL); + struct ath_desc *, + HAL_DMA_ADDR *bufAddrList, + uint32_t *segLenList, + u_int, u_int, HAL_PKT_TYPE, + u_int, HAL_CIPHER, uint8_t, HAL_BOOL, + HAL_BOOL, HAL_BOOL); HAL_BOOL __ahdecl(*ah_setupFirstTxDesc)(struct ath_hal *, struct ath_desc *, u_int, u_int, u_int, u_int, u_int, u_int, u_int, u_int); @@ -1012,12 +1450,32 @@ struct ath_hal { void __ahdecl(*ah_set11nRateScenario)(struct ath_hal *, struct ath_desc *, u_int, u_int, HAL_11N_RATE_SERIES [], u_int, u_int); + + /* + * The next 4 (set11ntxdesc -> set11naggrlast) are specific + * to the EDMA HAL. Descriptors are chained together by + * using filltxdesc (not ChainTxDesc) and then setting the + * aggregate flags appropriately using first/middle/last. + */ + void __ahdecl(*ah_set11nTxDesc)(struct ath_hal *, + void *, u_int, HAL_PKT_TYPE, u_int, u_int, + u_int); + void __ahdecl(*ah_set11nAggrFirst)(struct ath_hal *, + struct ath_desc *, u_int, u_int); void __ahdecl(*ah_set11nAggrMiddle)(struct ath_hal *, struct ath_desc *, u_int); + void __ahdecl(*ah_set11nAggrLast)(struct ath_hal *, + struct ath_desc *); void __ahdecl(*ah_clr11nAggr)(struct ath_hal *, struct ath_desc *); void __ahdecl(*ah_set11nBurstDuration)(struct ath_hal *, struct ath_desc *, u_int); + void __ahdecl(*ah_set11nVirtMoreFrag)(struct ath_hal *, + struct ath_desc *, u_int); + + HAL_BOOL __ahdecl(*ah_getMibCycleCounts) (struct ath_hal *, + HAL_SURVEY_SAMPLE *); + uint32_t __ahdecl(*ah_get11nExtBusy)(struct ath_hal *); void __ahdecl(*ah_set11nMac2040)(struct ath_hal *, HAL_HT_MACMODE); @@ -1030,6 +1488,40 @@ struct ath_hal { HAL_BOOL __ahdecl(*ah_getPendingInterrupts)(struct ath_hal*, HAL_INT*); HAL_INT __ahdecl(*ah_getInterrupts)(struct ath_hal*); HAL_INT __ahdecl(*ah_setInterrupts)(struct ath_hal*, HAL_INT); + + /* Bluetooth Coexistence functions */ + void __ahdecl(*ah_btCoexSetInfo)(struct ath_hal *, + HAL_BT_COEX_INFO *); + void __ahdecl(*ah_btCoexSetConfig)(struct ath_hal *, + HAL_BT_COEX_CONFIG *); + void __ahdecl(*ah_btCoexSetQcuThresh)(struct ath_hal *, + int); + void __ahdecl(*ah_btCoexSetWeights)(struct ath_hal *, + uint32_t); + void __ahdecl(*ah_btCoexSetBmissThresh)(struct ath_hal *, + uint32_t); + void __ahdecl(*ah_btCoexSetParameter)(struct ath_hal *, + uint32_t, uint32_t); + void __ahdecl(*ah_btCoexDisable)(struct ath_hal *); + int __ahdecl(*ah_btCoexEnable)(struct ath_hal *); + + /* Bluetooth MCI methods */ + void __ahdecl(*ah_btMciSetup)(struct ath_hal *, + uint32_t, void *, uint16_t, uint32_t); + HAL_BOOL __ahdecl(*ah_btMciSendMessage)(struct ath_hal *, + uint8_t, uint32_t, uint32_t *, uint8_t, + HAL_BOOL, HAL_BOOL); + uint32_t __ahdecl(*ah_btMciGetInterrupt)(struct ath_hal *, + uint32_t *, uint32_t *); + uint32_t __ahdecl(*ah_btMciState)(struct ath_hal *, + uint32_t, uint32_t *); + void __ahdecl(*ah_btMciDetach)(struct ath_hal *); + + /* LNA diversity configuration */ + void __ahdecl(*ah_divLnaConfGet)(struct ath_hal *, + HAL_ANT_COMB_CONFIG *); + void __ahdecl(*ah_divLnaConfSet)(struct ath_hal *, + HAL_ANT_COMB_CONFIG *); }; /* @@ -1053,7 +1545,8 @@ extern const char *__ahdecl ath_hal_probe(uint16_t vendorid, uint16_t devid); * be returned if the status parameter is non-zero. */ extern struct ath_hal * __ahdecl ath_hal_attach(uint16_t devid, HAL_SOFTC, - HAL_BUS_TAG, HAL_BUS_HANDLE, uint16_t *eepromdata, HAL_STATUS* status); + HAL_BUS_TAG, HAL_BUS_HANDLE, uint16_t *eepromdata, + HAL_OPS_CONFIG *ah_config, HAL_STATUS* status); extern const char *ath_hal_mac_name(struct ath_hal *); extern const char *ath_hal_rf_name(struct ath_hal *); @@ -1121,6 +1614,12 @@ extern void __ahdecl ath_hal_process_noisefloor(struct ath_hal *ah); */ extern u_int __ahdecl ath_hal_getwirelessmodes(struct ath_hal*); +/* + * Get the HAL wireless mode for the given channel. + */ +extern int ath_hal_get_curmode(struct ath_hal *ah, + const struct ieee80211_channel *chan); + /* * Calculate the packet TX time for a legacy or 11n frame */ @@ -1156,4 +1655,20 @@ void __ahdecl ath_hal_setcca(struct ath_hal *ah, int ena); */ int __ahdecl ath_hal_getcca(struct ath_hal *ah); +/* + * Read EEPROM data from ah_eepromdata + */ +HAL_BOOL __ahdecl ath_hal_EepromDataRead(struct ath_hal *ah, + u_int off, uint16_t *data); + +/* + * For now, simply pass through MFP frames. + */ +static inline u_int32_t +ath_hal_get_mfp_qos(struct ath_hal *ah) +{ + //return AH_PRIVATE(ah)->ah_mfp_qos; + return HAL_MFP_QOSDATA; +} + #endif /* _ATH_AH_H_ */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_btcoex.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_btcoex.h new file mode 100644 index 0000000000..5c91f3a6c8 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_btcoex.h @@ -0,0 +1,523 @@ +/* + * Copyright (c) 2014 Qualcomm Atheros, Inc. + * All Rights Reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ah_btcoex.h 301303 2016-06-04 07:28:09Z adrian $ + */ +#ifndef __ATH_HAL_BTCOEX_H__ +#define __ATH_HAL_BTCOEX_H__ + +/* + * General BT coexistence definitions. + */ +typedef enum { + HAL_BT_MODULE_CSR_BC4 = 0, /* CSR BlueCore v4 */ + HAL_BT_MODULE_JANUS = 1, /* Kite + Valkyrie combo */ + HAL_BT_MODULE_HELIUS = 2, /* Kiwi + Valkyrie combo */ + HAL_MAX_BT_MODULES +} HAL_BT_MODULE; + +typedef struct { + HAL_BT_MODULE bt_module; + u_int8_t bt_coex_config; + u_int8_t bt_gpio_bt_active; + u_int8_t bt_gpio_bt_priority; + u_int8_t bt_gpio_wlan_active; + u_int8_t bt_active_polarity; + HAL_BOOL bt_single_ant; + u_int8_t bt_isolation; +} HAL_BT_COEX_INFO; + +typedef enum { + HAL_BT_COEX_MODE_LEGACY = 0, /* legacy rx_clear mode */ + HAL_BT_COEX_MODE_UNSLOTTED = 1, /* untimed/unslotted mode */ + HAL_BT_COEX_MODE_SLOTTED = 2, /* slotted mode */ + HAL_BT_COEX_MODE_DISALBED = 3, /* coexistence disabled */ +} HAL_BT_COEX_MODE; + +typedef enum { + HAL_BT_COEX_CFG_NONE, /* No bt coex enabled */ + HAL_BT_COEX_CFG_2WIRE_2CH, /* 2-wire with 2 chains */ + HAL_BT_COEX_CFG_2WIRE_CH1, /* 2-wire with ch1 */ + HAL_BT_COEX_CFG_2WIRE_CH0, /* 2-wire with ch0 */ + HAL_BT_COEX_CFG_3WIRE, /* 3-wire */ + HAL_BT_COEX_CFG_MCI /* MCI */ +} HAL_BT_COEX_CFG; + +typedef enum { + HAL_BT_COEX_SET_ACK_PWR = 0, /* Change ACK power setting */ + HAL_BT_COEX_LOWER_TX_PWR, /* Change transmit power */ + HAL_BT_COEX_ANTENNA_DIVERSITY, /* Enable RX diversity for Kite */ + HAL_BT_COEX_MCI_MAX_TX_PWR, /* Set max tx power for concurrent tx */ + HAL_BT_COEX_MCI_FTP_STOMP_RX, /* Use a different weight for stomp low */ +} HAL_BT_COEX_SET_PARAMETER; + +/* + * MCI specific coexistence definitions. + */ + +#define HAL_BT_COEX_FLAG_LOW_ACK_PWR 0x00000001 +#define HAL_BT_COEX_FLAG_LOWER_TX_PWR 0x00000002 +/* Check Rx Diversity is allowed */ +#define HAL_BT_COEX_FLAG_ANT_DIV_ALLOW 0x00000004 +/* Check Diversity is on or off */ +#define HAL_BT_COEX_FLAG_ANT_DIV_ENABLE 0x00000008 + +#define HAL_BT_COEX_ANTDIV_CONTROL1_ENABLE 0x0b +/* main: LNA1, alt: LNA2 */ +#define HAL_BT_COEX_ANTDIV_CONTROL2_ENABLE 0x09 +#define HAL_BT_COEX_ANTDIV_CONTROL1_FIXED_A 0x04 +#define HAL_BT_COEX_ANTDIV_CONTROL2_FIXED_A 0x09 +#define HAL_BT_COEX_ANTDIV_CONTROL1_FIXED_B 0x02 +#define HAL_BT_COEX_ANTDIV_CONTROL2_FIXED_B 0x06 + +#define HAL_BT_COEX_ISOLATION_FOR_NO_COEX 30 + +#define HAL_BT_COEX_ANT_DIV_SWITCH_COM 0x66666666 + +#define HAL_BT_COEX_HELIUS_CHAINMASK 0x02 + +#define HAL_BT_COEX_LOW_ACK_POWER 0x0 +#define HAL_BT_COEX_HIGH_ACK_POWER 0x3f3f3f + +typedef enum { + HAL_BT_COEX_NO_STOMP = 0, + HAL_BT_COEX_STOMP_ALL, + HAL_BT_COEX_STOMP_LOW, + HAL_BT_COEX_STOMP_NONE, + HAL_BT_COEX_STOMP_ALL_FORCE, + HAL_BT_COEX_STOMP_LOW_FORCE, + HAL_BT_COEX_STOMP_AUDIO, +} HAL_BT_COEX_STOMP_TYPE; + +typedef struct { + /* extend rx_clear after tx/rx to protect the burst (in usec). */ + u_int8_t bt_time_extend; + + /* + * extend rx_clear as long as txsm is + * transmitting or waiting for ack. + */ + HAL_BOOL bt_txstate_extend; + + /* + * extend rx_clear so that when tx_frame + * is asserted, rx_clear will drop. + */ + HAL_BOOL bt_txframe_extend; + + /* + * coexistence mode + */ + HAL_BT_COEX_MODE bt_mode; + + /* + * treat BT high priority traffic as + * a quiet collision + */ + HAL_BOOL bt_quiet_collision; + + /* + * invert rx_clear as WLAN_ACTIVE + */ + HAL_BOOL bt_rxclear_polarity; + + /* + * slotted mode only. indicate the time in usec + * from the rising edge of BT_ACTIVE to the time + * BT_PRIORITY can be sampled to indicate priority. + */ + u_int8_t bt_priority_time; + + /* + * slotted mode only. indicate the time in usec + * from the rising edge of BT_ACTIVE to the time + * BT_PRIORITY can be sampled to indicate tx/rx and + * BT_FREQ is sampled. + */ + u_int8_t bt_first_slot_time; + + /* + * slotted mode only. rx_clear and bt_ant decision + * will be held the entire time that BT_ACTIVE is asserted, + * otherwise the decision is made before every slot boundary. + */ + HAL_BOOL bt_hold_rxclear; +} HAL_BT_COEX_CONFIG; + +#define HAL_BT_COEX_FLAG_LOW_ACK_PWR 0x00000001 +#define HAL_BT_COEX_FLAG_LOWER_TX_PWR 0x00000002 +#define HAL_BT_COEX_FLAG_ANT_DIV_ALLOW 0x00000004 /* Check Rx Diversity is allowed */ +#define HAL_BT_COEX_FLAG_ANT_DIV_ENABLE 0x00000008 /* Check Diversity is on or off */ +#define HAL_BT_COEX_FLAG_MCI_MAX_TX_PWR 0x00000010 +#define HAL_BT_COEX_FLAG_MCI_FTP_STOMP_RX 0x00000020 + +#define HAL_MCI_FLAG_DISABLE_TIMESTAMP 0x00000001 /* Disable time stamp */ + +typedef enum mci_message_header { + MCI_LNA_CTRL = 0x10, /* len = 0 */ + MCI_CONT_NACK = 0x20, /* len = 0 */ + MCI_CONT_INFO = 0x30, /* len = 4 */ + MCI_CONT_RST = 0x40, /* len = 0 */ + MCI_SCHD_INFO = 0x50, /* len = 16 */ + MCI_CPU_INT = 0x60, /* len = 4 */ + MCI_SYS_WAKING = 0x70, /* len = 0 */ + MCI_GPM = 0x80, /* len = 16 */ + MCI_LNA_INFO = 0x90, /* len = 1 */ + MCI_LNA_STATE = 0x94, + MCI_LNA_TAKE = 0x98, + MCI_LNA_TRANS = 0x9c, + MCI_SYS_SLEEPING = 0xa0, /* len = 0 */ + MCI_REQ_WAKE = 0xc0, /* len = 0 */ + MCI_DEBUG_16 = 0xfe, /* len = 2 */ + MCI_REMOTE_RESET = 0xff /* len = 16 */ +} MCI_MESSAGE_HEADER; + +/* Default remote BT device MCI COEX version */ +#define MCI_GPM_COEX_MAJOR_VERSION_DEFAULT 3 +#define MCI_GPM_COEX_MINOR_VERSION_DEFAULT 0 +/* Local WLAN MCI COEX version */ +#define MCI_GPM_COEX_MAJOR_VERSION_WLAN 3 +#define MCI_GPM_COEX_MINOR_VERSION_WLAN 0 + +typedef enum mci_gpm_subtype { + MCI_GPM_BT_CAL_REQ = 0, + MCI_GPM_BT_CAL_GRANT = 1, + MCI_GPM_BT_CAL_DONE = 2, + MCI_GPM_WLAN_CAL_REQ = 3, + MCI_GPM_WLAN_CAL_GRANT = 4, + MCI_GPM_WLAN_CAL_DONE = 5, + MCI_GPM_COEX_AGENT = 0x0C, + MCI_GPM_RSVD_PATTERN = 0xFE, + MCI_GPM_RSVD_PATTERN32 = 0xFEFEFEFE, + MCI_GPM_BT_DEBUG = 0xFF +} MCI_GPM_SUBTYPE_T; + +typedef enum mci_gpm_coex_opcode { + MCI_GPM_COEX_VERSION_QUERY = 0, + MCI_GPM_COEX_VERSION_RESPONSE = 1, + MCI_GPM_COEX_STATUS_QUERY = 2, + MCI_GPM_COEX_HALT_BT_GPM = 3, + MCI_GPM_COEX_WLAN_CHANNELS = 4, + MCI_GPM_COEX_BT_PROFILE_INFO = 5, + MCI_GPM_COEX_BT_STATUS_UPDATE = 6, + MCI_GPM_COEX_BT_UPDATE_FLAGS = 7 +} MCI_GPM_COEX_OPCODE_T; + +typedef enum mci_gpm_coex_query_type { + /* WLAN information */ + MCI_GPM_COEX_QUERY_WLAN_ALL_INFO = 0x01, + /* BT information */ + MCI_GPM_COEX_QUERY_BT_ALL_INFO = 0x01, + MCI_GPM_COEX_QUERY_BT_TOPOLOGY = 0x02, + MCI_GPM_COEX_QUERY_BT_DEBUG = 0x04 +} MCI_GPM_COEX_QUERY_TYPE_T; + +typedef enum mci_gpm_coex_halt_bt_gpm { + MCI_GPM_COEX_BT_GPM_UNHALT = 0, + MCI_GPM_COEX_BT_GPM_HALT = 1 +} MCI_GPM_COEX_HALT_BT_GPM_T; + +typedef enum mci_gpm_coex_profile_type { + MCI_GPM_COEX_PROFILE_UNKNOWN = 0, + MCI_GPM_COEX_PROFILE_RFCOMM = 1, + MCI_GPM_COEX_PROFILE_A2DP = 2, + MCI_GPM_COEX_PROFILE_HID = 3, + MCI_GPM_COEX_PROFILE_BNEP = 4, + MCI_GPM_COEX_PROFILE_VOICE = 5, + MCI_GPM_COEX_PROFILE_MAX +} MCI_GPM_COEX_PROFILE_TYPE_T; + +typedef enum mci_gpm_coex_profile_state { + MCI_GPM_COEX_PROFILE_STATE_END = 0, + MCI_GPM_COEX_PROFILE_STATE_START = 1 +} MCI_GPM_COEX_PROFILE_STATE_T; + +typedef enum mci_gpm_coex_profile_role { + MCI_GPM_COEX_PROFILE_SLAVE = 0, + MCI_GPM_COEX_PROFILE_MASTER = 1 +} MCI_GPM_COEX_PROFILE_ROLE_T; + +typedef enum mci_gpm_coex_bt_status_type { + MCI_GPM_COEX_BT_NONLINK_STATUS = 0, + MCI_GPM_COEX_BT_LINK_STATUS = 1 +} MCI_GPM_COEX_BT_STATUS_TYPE_T; + +typedef enum mci_gpm_coex_bt_status_state { + MCI_GPM_COEX_BT_NORMAL_STATUS = 0, + MCI_GPM_COEX_BT_CRITICAL_STATUS = 1 +} MCI_GPM_COEX_BT_STATUS_STATE_T; + +#define MCI_GPM_INVALID_PROFILE_HANDLE 0xff + +typedef enum mci_gpm_coex_bt_updata_flags_op { + MCI_GPM_COEX_BT_FLAGS_READ = 0x00, + MCI_GPM_COEX_BT_FLAGS_SET = 0x01, + MCI_GPM_COEX_BT_FLAGS_CLEAR = 0x02 +} MCI_GPM_COEX_BT_FLAGS_OP_T; + +/* MCI GPM/Coex opcode/type definitions */ +enum { + MCI_GPM_COEX_W_GPM_PAYLOAD = 1, + MCI_GPM_COEX_B_GPM_TYPE = 4, + MCI_GPM_COEX_B_GPM_OPCODE = 5, + /* MCI_GPM_WLAN_CAL_REQ, MCI_GPM_WLAN_CAL_DONE */ + MCI_GPM_WLAN_CAL_W_SEQUENCE = 2, + /* MCI_GPM_COEX_VERSION_QUERY */ + /* MCI_GPM_COEX_VERSION_RESPONSE */ + MCI_GPM_COEX_B_MAJOR_VERSION = 6, + MCI_GPM_COEX_B_MINOR_VERSION = 7, + /* MCI_GPM_COEX_STATUS_QUERY */ + MCI_GPM_COEX_B_BT_BITMAP = 6, + MCI_GPM_COEX_B_WLAN_BITMAP = 7, + /* MCI_GPM_COEX_HALT_BT_GPM */ + MCI_GPM_COEX_B_HALT_STATE = 6, + /* MCI_GPM_COEX_WLAN_CHANNELS */ + MCI_GPM_COEX_B_CHANNEL_MAP = 6, + /* MCI_GPM_COEX_BT_PROFILE_INFO */ + MCI_GPM_COEX_B_PROFILE_TYPE = 6, + MCI_GPM_COEX_B_PROFILE_LINKID = 7, + MCI_GPM_COEX_B_PROFILE_STATE = 8, + MCI_GPM_COEX_B_PROFILE_ROLE = 9, + MCI_GPM_COEX_B_PROFILE_RATE = 10, + MCI_GPM_COEX_B_PROFILE_VOTYPE = 11, + MCI_GPM_COEX_H_PROFILE_T = 12, + MCI_GPM_COEX_B_PROFILE_W = 14, + MCI_GPM_COEX_B_PROFILE_A = 15, + /* MCI_GPM_COEX_BT_STATUS_UPDATE */ + MCI_GPM_COEX_B_STATUS_TYPE = 6, + MCI_GPM_COEX_B_STATUS_LINKID = 7, + MCI_GPM_COEX_B_STATUS_STATE = 8, + /* MCI_GPM_COEX_BT_UPDATE_FLAGS */ + MCI_GPM_COEX_B_BT_FLAGS_OP = 10, + MCI_GPM_COEX_W_BT_FLAGS = 6 +}; + +#define MCI_GPM_RECYCLE(_p_gpm) \ + { \ + *(((u_int32_t *)(_p_gpm)) + MCI_GPM_COEX_W_GPM_PAYLOAD) = MCI_GPM_RSVD_PATTERN32; \ + } +#define MCI_GPM_TYPE(_p_gpm) \ + (*(((u_int8_t *)(_p_gpm)) + MCI_GPM_COEX_B_GPM_TYPE) & 0xff) +#define MCI_GPM_OPCODE(_p_gpm) \ + (*(((u_int8_t *)(_p_gpm)) + MCI_GPM_COEX_B_GPM_OPCODE) & 0xff) + +#define MCI_GPM_SET_CAL_TYPE(_p_gpm, _cal_type) \ + { \ + *(((u_int8_t *)(_p_gpm)) + MCI_GPM_COEX_B_GPM_TYPE) = (_cal_type) & 0xff; \ + } +#define MCI_GPM_SET_TYPE_OPCODE(_p_gpm, _type, _opcode) \ + { \ + *(((u_int8_t *)(_p_gpm)) + MCI_GPM_COEX_B_GPM_TYPE) = (_type) & 0xff; \ + *(((u_int8_t *)(_p_gpm)) + MCI_GPM_COEX_B_GPM_OPCODE) = (_opcode) & 0xff; \ + } +#define MCI_GPM_IS_CAL_TYPE(_type) ((_type) <= MCI_GPM_WLAN_CAL_DONE) + +#define MCI_NUM_BT_CHANNELS 79 + +#define MCI_GPM_SET_CHANNEL_BIT(_p_gpm, _bt_chan) \ + { \ + if (_bt_chan < MCI_NUM_BT_CHANNELS) { \ + *(((u_int8_t *)(_p_gpm)) + MCI_GPM_COEX_B_CHANNEL_MAP + \ + (_bt_chan / 8)) |= 1 << (_bt_chan & 7); \ + } \ + } + +#define MCI_GPM_CLR_CHANNEL_BIT(_p_gpm, _bt_chan) \ + { \ + if (_bt_chan < MCI_NUM_BT_CHANNELS) { \ + *(((u_int8_t *)(_p_gpm)) + MCI_GPM_COEX_B_CHANNEL_MAP + \ + (_bt_chan / 8)) &= ~(1 << (_bt_chan & 7)); \ + } \ + } + +#define HAL_MCI_INTERRUPT_SW_MSG_DONE 0x00000001 +#define HAL_MCI_INTERRUPT_CPU_INT_MSG 0x00000002 +#define HAL_MCI_INTERRUPT_RX_CHKSUM_FAIL 0x00000004 +#define HAL_MCI_INTERRUPT_RX_INVALID_HDR 0x00000008 +#define HAL_MCI_INTERRUPT_RX_HW_MSG_FAIL 0x00000010 +#define HAL_MCI_INTERRUPT_RX_SW_MSG_FAIL 0x00000020 +#define HAL_MCI_INTERRUPT_TX_HW_MSG_FAIL 0x00000080 +#define HAL_MCI_INTERRUPT_TX_SW_MSG_FAIL 0x00000100 +#define HAL_MCI_INTERRUPT_RX_MSG 0x00000200 +#define HAL_MCI_INTERRUPT_REMOTE_SLEEP_UPDATE 0x00000400 +#define HAL_MCI_INTERRUPT_CONT_INFO_TIMEOUT 0x80000000 +#define HAL_MCI_INTERRUPT_MSG_FAIL_MASK ( HAL_MCI_INTERRUPT_RX_HW_MSG_FAIL | \ + HAL_MCI_INTERRUPT_RX_SW_MSG_FAIL | \ + HAL_MCI_INTERRUPT_TX_HW_MSG_FAIL | \ + HAL_MCI_INTERRUPT_TX_SW_MSG_FAIL ) + +#define HAL_MCI_INTERRUPT_RX_MSG_REMOTE_RESET 0x00000001 +#define HAL_MCI_INTERRUPT_RX_MSG_LNA_CONTROL 0x00000002 +#define HAL_MCI_INTERRUPT_RX_MSG_CONT_NACK 0x00000004 +#define HAL_MCI_INTERRUPT_RX_MSG_CONT_INFO 0x00000008 +#define HAL_MCI_INTERRUPT_RX_MSG_CONT_RST 0x00000010 +#define HAL_MCI_INTERRUPT_RX_MSG_SCHD_INFO 0x00000020 +#define HAL_MCI_INTERRUPT_RX_MSG_CPU_INT 0x00000040 +#define HAL_MCI_INTERRUPT_RX_MSG_GPM 0x00000100 +#define HAL_MCI_INTERRUPT_RX_MSG_LNA_INFO 0x00000200 +#define HAL_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING 0x00000400 +#define HAL_MCI_INTERRUPT_RX_MSG_SYS_WAKING 0x00000800 +#define HAL_MCI_INTERRUPT_RX_MSG_REQ_WAKE 0x00001000 +#define HAL_MCI_INTERRUPT_RX_MSG_MONITOR (HAL_MCI_INTERRUPT_RX_MSG_LNA_CONTROL | \ + HAL_MCI_INTERRUPT_RX_MSG_LNA_INFO | \ + HAL_MCI_INTERRUPT_RX_MSG_CONT_NACK | \ + HAL_MCI_INTERRUPT_RX_MSG_CONT_INFO | \ + HAL_MCI_INTERRUPT_RX_MSG_CONT_RST) + +typedef enum mci_bt_state { + MCI_BT_SLEEP, + MCI_BT_AWAKE, + MCI_BT_CAL_START, + MCI_BT_CAL +} MCI_BT_STATE_T; + +/* Type of state query */ +typedef enum mci_state_type { + HAL_MCI_STATE_ENABLE, + HAL_MCI_STATE_INIT_GPM_OFFSET, + HAL_MCI_STATE_NEXT_GPM_OFFSET, + HAL_MCI_STATE_LAST_GPM_OFFSET, + HAL_MCI_STATE_BT, + HAL_MCI_STATE_SET_BT_SLEEP, + HAL_MCI_STATE_SET_BT_AWAKE, + HAL_MCI_STATE_SET_BT_CAL_START, + HAL_MCI_STATE_SET_BT_CAL, + HAL_MCI_STATE_LAST_SCHD_MSG_OFFSET, + HAL_MCI_STATE_REMOTE_SLEEP, + HAL_MCI_STATE_CONT_RSSI_POWER, + HAL_MCI_STATE_CONT_PRIORITY, + HAL_MCI_STATE_CONT_TXRX, + HAL_MCI_STATE_RESET_REQ_WAKE, + HAL_MCI_STATE_SEND_WLAN_COEX_VERSION, + HAL_MCI_STATE_SET_BT_COEX_VERSION, + HAL_MCI_STATE_SEND_WLAN_CHANNELS, + HAL_MCI_STATE_SEND_VERSION_QUERY, + HAL_MCI_STATE_SEND_STATUS_QUERY, + HAL_MCI_STATE_NEED_FLUSH_BT_INFO, + HAL_MCI_STATE_SET_CONCUR_TX_PRI, + HAL_MCI_STATE_RECOVER_RX, + HAL_MCI_STATE_NEED_FTP_STOMP, + HAL_MCI_STATE_NEED_TUNING, + HAL_MCI_STATE_SHARED_CHAIN_CONCUR_TX, + HAL_MCI_STATE_DEBUG, + HAL_MCI_STATE_MAX +} HAL_MCI_STATE_TYPE; + +#define HAL_MCI_STATE_DEBUG_REQ_BT_DEBUG 1 + +#define HAL_MCI_BT_MCI_FLAGS_UPDATE_CORR 0x00000002 +#define HAL_MCI_BT_MCI_FLAGS_UPDATE_HDR 0x00000004 +#define HAL_MCI_BT_MCI_FLAGS_UPDATE_PLD 0x00000008 +#define HAL_MCI_BT_MCI_FLAGS_LNA_CTRL 0x00000010 +#define HAL_MCI_BT_MCI_FLAGS_DEBUG 0x00000020 +#define HAL_MCI_BT_MCI_FLAGS_SCHED_MSG 0x00000040 +#define HAL_MCI_BT_MCI_FLAGS_CONT_MSG 0x00000080 +#define HAL_MCI_BT_MCI_FLAGS_COEX_GPM 0x00000100 +#define HAL_MCI_BT_MCI_FLAGS_CPU_INT_MSG 0x00000200 +#define HAL_MCI_BT_MCI_FLAGS_MCI_MODE 0x00000400 +#define HAL_MCI_BT_MCI_FLAGS_EGRET_MODE 0x00000800 +#define HAL_MCI_BT_MCI_FLAGS_JUPITER_MODE 0x00001000 +#define HAL_MCI_BT_MCI_FLAGS_OTHER 0x00010000 + +#define HAL_MCI_DEFAULT_BT_MCI_FLAGS 0x00011dde +/* + HAL_MCI_BT_MCI_FLAGS_UPDATE_CORR = 1 + HAL_MCI_BT_MCI_FLAGS_UPDATE_HDR = 1 + HAL_MCI_BT_MCI_FLAGS_UPDATE_PLD = 1 + HAL_MCI_BT_MCI_FLAGS_LNA_CTRL = 1 + HAL_MCI_BT_MCI_FLAGS_DEBUG = 0 + HAL_MCI_BT_MCI_FLAGS_SCHED_MSG = 1 + HAL_MCI_BT_MCI_FLAGS_CONT_MSG = 1 + HAL_MCI_BT_MCI_FLAGS_COEX_GPM = 1 + HAL_MCI_BT_MCI_FLAGS_CPU_INT_MSG = 0 + HAL_MCI_BT_MCI_FLAGS_MCI_MODE = 1 + HAL_MCI_BT_MCI_FLAGS_EGRET_MODE = 1 + HAL_MCI_BT_MCI_FLAGS_JUPITER_MODE = 1 + HAL_MCI_BT_MCI_FLAGS_OTHER = 1 +*/ + +#define HAL_MCI_TOGGLE_BT_MCI_FLAGS \ + ( HAL_MCI_BT_MCI_FLAGS_UPDATE_CORR | \ + HAL_MCI_BT_MCI_FLAGS_UPDATE_HDR | \ + HAL_MCI_BT_MCI_FLAGS_UPDATE_PLD | \ + HAL_MCI_BT_MCI_FLAGS_MCI_MODE ) + +#define HAL_MCI_2G_FLAGS_CLEAR_MASK 0x00000000 +#define HAL_MCI_2G_FLAGS_SET_MASK HAL_MCI_TOGGLE_BT_MCI_FLAGS +#define HAL_MCI_2G_FLAGS HAL_MCI_DEFAULT_BT_MCI_FLAGS + +#define HAL_MCI_5G_FLAGS_CLEAR_MASK HAL_MCI_TOGGLE_BT_MCI_FLAGS +#define HAL_MCI_5G_FLAGS_SET_MASK 0x00000000 +#define HAL_MCI_5G_FLAGS (HAL_MCI_DEFAULT_BT_MCI_FLAGS & \ + ~HAL_MCI_TOGGLE_BT_MCI_FLAGS) + +#define HAL_MCI_GPM_NOMORE 0 +#define HAL_MCI_GPM_MORE 1 +#define HAL_MCI_GPM_INVALID 0xffffffff + +#define ATH_AIC_MAX_BT_CHANNEL 79 + +/* + * Default value for Jupiter is 0x00002201 + * Default value for Aphrodite is 0x00002282 + */ +#define ATH_MCI_CONFIG_CONCUR_TX 0x00000003 +#define ATH_MCI_CONFIG_MCI_OBS_MCI 0x00000004 +#define ATH_MCI_CONFIG_MCI_OBS_TXRX 0x00000008 +#define ATH_MCI_CONFIG_MCI_OBS_BT 0x00000010 +#define ATH_MCI_CONFIG_DISABLE_MCI_CAL 0x00000020 +#define ATH_MCI_CONFIG_DISABLE_OSLA 0x00000040 +#define ATH_MCI_CONFIG_DISABLE_FTP_STOMP 0x00000080 +#define ATH_MCI_CONFIG_AGGR_THRESH 0x00000700 +#define ATH_MCI_CONFIG_AGGR_THRESH_S 8 +#define ATH_MCI_CONFIG_DISABLE_AGGR_THRESH 0x00000800 +#define ATH_MCI_CONFIG_CLK_DIV 0x00003000 +#define ATH_MCI_CONFIG_CLK_DIV_S 12 +#define ATH_MCI_CONFIG_DISABLE_TUNING 0x00004000 +#define ATH_MCI_CONFIG_DISABLE_AIC 0x00008000 +#define ATH_MCI_CONFIG_AIC_CAL_NUM_CHAN 0x007f0000 +#define ATH_MCI_CONFIG_AIC_CAL_NUM_CHAN_S 16 +#define ATH_MCI_CONFIG_NO_QUIET_ACK 0x00800000 +#define ATH_MCI_CONFIG_NO_QUIET_ACK_S 23 +#define ATH_MCI_CONFIG_ANT_ARCH 0x07000000 +#define ATH_MCI_CONFIG_ANT_ARCH_S 24 +#define ATH_MCI_CONFIG_FORCE_QUIET_ACK 0x08000000 +#define ATH_MCI_CONFIG_FORCE_QUIET_ACK_S 27 +#define ATH_MCI_CONFIG_FORCE_2CHAIN_ACK 0x10000000 +#define ATH_MCI_CONFIG_MCI_STAT_DBG 0x20000000 +#define ATH_MCI_CONFIG_MCI_WEIGHT_DBG 0x40000000 +#define ATH_MCI_CONFIG_DISABLE_MCI 0x80000000 + +#define ATH_MCI_CONFIG_MCI_OBS_MASK ( ATH_MCI_CONFIG_MCI_OBS_MCI | \ + ATH_MCI_CONFIG_MCI_OBS_TXRX | \ + ATH_MCI_CONFIG_MCI_OBS_BT ) +#define ATH_MCI_CONFIG_MCI_OBS_GPIO 0x0000002F + +#define ATH_MCI_ANT_ARCH_1_ANT_PA_LNA_NON_SHARED 0x00 +#define ATH_MCI_ANT_ARCH_1_ANT_PA_LNA_SHARED 0x01 +#define ATH_MCI_ANT_ARCH_2_ANT_PA_LNA_NON_SHARED 0x02 +#define ATH_MCI_ANT_ARCH_2_ANT_PA_LNA_SHARED 0x03 +#define ATH_MCI_ANT_ARCH_3_ANT 0x04 + +#define MCI_ANT_ARCH_PA_LNA_SHARED(c) \ + ((MS(c, ATH_MCI_CONFIG_ANT_ARCH) == ATH_MCI_ANT_ARCH_1_ANT_PA_LNA_SHARED) || \ + (MS(c, ATH_MCI_CONFIG_ANT_ARCH) == ATH_MCI_ANT_ARCH_2_ANT_PA_LNA_SHARED)) + +#define ATH_MCI_CONCUR_TX_SHARED_CHN 0x01 +#define ATH_MCI_CONCUR_TX_UNSHARED_CHN 0x02 +#define ATH_MCI_CONCUR_TX_DEBUG 0x03 + +#endif diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_debug.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_debug.h index f1678eb48e..fe4374be15 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_debug.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_debug.h @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ah_debug.h 249131 2013-04-05 07:41:47Z adrian $ */ #ifndef _ATH_AH_DEBUG_H_ #define _ATH_AH_DEBUG_H_ @@ -47,8 +47,15 @@ enum { HAL_DEBUG_DIVERSITY = 0x00100000, /* diversity debugging */ HAL_DEBUG_DFS = 0x00200000, /* DFS debugging */ HAL_DEBUG_HANG = 0x00400000, /* BB/MAC hang debugging */ + HAL_DEBUG_CALIBRATE = 0x00800000, /* setup calibration */ + HAL_DEBUG_POWER_MGMT = 0x01000000, /* power calibration */ + HAL_DEBUG_CHANNEL = 0x02000000, + HAL_DEBUG_QUEUE = 0x04000000, + HAL_DEBUG_PRINT_REG = 0x08000000, + HAL_DEBUG_FCS_RTT = 0x10000000, + HAL_DEBUG_BT_COEX = 0x20000000, - HAL_DEBUG_UNMASKABLE = 0xf0000000, /* always printed */ + HAL_DEBUG_UNMASKABLE = 0x80000000, /* always printed */ HAL_DEBUG_ANY = 0xffffffff }; #endif /* _ATH_AH_DEBUG_H_ */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_decode.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_decode.h index eb764af9ac..9421ac1d1b 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_decode.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_decode.h @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ah_decode.h 269749 2014-08-09 09:13:10Z adrian $ */ #ifndef _ATH_AH_DECODE_H_ #define _ATH_AH_DECODE_H_ @@ -29,6 +29,7 @@ * existing file. */ struct athregrec { + uint32_t threadid; uint32_t op : 8, reg : 24; uint32_t val; @@ -52,6 +53,8 @@ enum { AH_MARK_ANI_POLL, /* ar*AniReset, listen time */ AH_MARK_ANI_CONTROL, /* ar*AniReset, cmd */ AH_MARK_RX_CTL, /* RX DMA control */ + AH_MARK_CHIP_POWER, /* chip power control, mode */ + AH_MARK_CHIP_POWER_DONE, /* chip power control done, status */ }; enum { @@ -60,6 +63,7 @@ enum { AH_MARK_RX_CTL_DMA_START, AH_MARK_RX_CTL_DMA_STOP, AH_MARK_RX_CTL_DMA_STOP_ERR, + AH_MARK_RX_CTL_DMA_STOP_OK, }; #endif /* _ATH_AH_DECODE_H_ */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_desc.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_desc.h index 4651fc2d7c..1385b328be 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_desc.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_desc.h @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ah_desc.h 301041 2016-05-31 16:05:54Z adrian $ */ #ifndef _DEV_ATH_DESC_H @@ -22,6 +22,19 @@ #include "opt_ah.h" /* NB: required for AH_SUPPORT_AR5416 */ +/* + * For now, define this for the structure definitions. + * Because of how the HAL / driver module currently builds, + * it's not very feasible to build the module without + * this defined. The rest of the code (eg in the driver + * body) can work fine with these fields being uninitialised; + * they'll be initialised to 0 anyway. + */ + +#ifndef AH_SUPPORT_AR5416 +#define AH_SUPPORT_AR5416 1 +#endif + /* * Transmit descriptor status. This structure is filled * in only after the tx descriptor process method finds a @@ -44,15 +57,19 @@ struct ath_tx_status { uint8_t ts_finaltsi; /* final transmit series index */ #ifdef AH_SUPPORT_AR5416 /* 802.11n status */ - uint8_t ts_flags; /* misc flags */ - int8_t ts_rssi_ctl[3]; /* tx ack RSSI [ctl, chain 0-2] */ - int8_t ts_rssi_ext[3]; /* tx ack RSSI [ext, chain 0-2] */ + uint8_t ts_flags; /* misc flags */ + uint8_t ts_queue_id; /* AR9300: TX queue id */ + uint8_t ts_desc_id; /* AR9300: TX descriptor id */ + uint8_t ts_tid; /* TID */ /* #define ts_rssi ts_rssi_combined */ - uint32_t ts_ba_low; /* blockack bitmap low */ - uint32_t ts_ba_high; /* blockack bitmap high */ - uint32_t ts_evm0; /* evm bytes */ - uint32_t ts_evm1; - uint32_t ts_evm2; + uint32_t ts_ba_low; /* blockack bitmap low */ + uint32_t ts_ba_high; /* blockack bitmap high */ + uint32_t ts_evm0; /* evm bytes */ + uint32_t ts_evm1; + uint32_t ts_evm2; + int8_t ts_rssi_ctl[3]; /* tx ack RSSI [ctl, chain 0-2] */ + int8_t ts_rssi_ext[3]; /* tx ack RSSI [ext, chain 0-2] */ + uint8_t ts_pad[2]; #endif /* AH_SUPPORT_AR5416 */ }; @@ -108,11 +125,12 @@ struct ath_rx_status { int8_t rs_rssi_ext[3]; /* rx frame RSSI [ext, chain 0-2] */ uint8_t rs_isaggr; /* is part of the aggregate */ uint8_t rs_moreaggr; /* more frames in aggr to follow */ + uint16_t rs_flags; /* misc flags */ uint8_t rs_num_delims; /* number of delims in aggr */ - uint8_t rs_flags; /* misc flags */ + uint8_t rs_spare0; /* padding */ uint32_t rs_evm0; /* evm bytes */ uint32_t rs_evm1; - uint32_t rs_evm2; + uint32_t rs_evm2; uint32_t rs_evm3; /* needed for ar9300 and later */ uint32_t rs_evm4; /* needed for ar9300 and later */ #endif /* AH_SUPPORT_AR5416 */ @@ -124,16 +142,46 @@ struct ath_rx_status { #define HAL_RXERR_FIFO 0x04 /* fifo overrun */ #define HAL_RXERR_DECRYPT 0x08 /* non-Michael decrypt error */ #define HAL_RXERR_MIC 0x10 /* Michael MIC decrypt error */ +#define HAL_RXERR_INCOMP 0x20 /* Rx Desc processing is incomplete */ +#define HAL_RXERR_KEYMISS 0x40 /* Key not found in keycache */ /* bits found in rs_flags */ -#define HAL_RX_MORE 0x01 /* more descriptors follow */ -#define HAL_RX_MORE_AGGR 0x02 /* more frames in aggr */ -#define HAL_RX_GI 0x04 /* full gi */ -#define HAL_RX_2040 0x08 /* 40 Mhz */ -#define HAL_RX_DELIM_CRC_PRE 0x10 /* crc error in delimiter pre */ -#define HAL_RX_DELIM_CRC_POST 0x20 /* crc error in delim after */ -#define HAL_RX_DECRYPT_BUSY 0x40 /* decrypt was too slow */ -#define HAL_RX_HI_RX_CHAIN 0x80 /* SM power save: hi Rx chain control */ +#define HAL_RX_MORE 0x0001 /* more descriptors follow */ +#define HAL_RX_MORE_AGGR 0x0002 /* more frames in aggr */ +#define HAL_RX_GI 0x0004 /* full gi */ +#define HAL_RX_2040 0x0008 /* 40 Mhz */ +#define HAL_RX_DELIM_CRC_PRE 0x0010 /* crc error in delimiter pre */ +#define HAL_RX_DELIM_CRC_POST 0x0020 /* crc error in delim after */ +#define HAL_RX_DECRYPT_BUSY 0x0040 /* decrypt was too slow */ +#define HAL_RX_HI_RX_CHAIN 0x0080 /* SM power save: hi Rx chain control */ +#define HAL_RX_IS_APSD 0x0100 /* Is ASPD trigger frame */ +#define HAL_RX_STBC 0x0200 /* Is an STBC frame */ + +/* + * This is the format of RSSI[2] on the AR9285/AR9485. + * It encodes the LNA configuration information. + * + * For boards with an external diversity antenna switch, + * HAL_RX_LNA_EXTCFG encodes which configuration was + * used (antenna 1 or antenna 2.) This feeds into the + * switch table and ensures that the given antenna was + * connected to an LNA. + */ +#define HAL_RX_LNA_LNACFG 0x80 /* 1 = main LNA config used, 0 = ALT */ +#define HAL_RX_LNA_EXTCFG 0x40 /* 0 = external diversity ant1, 1 = ant2 */ +#define HAL_RX_LNA_CFG_USED 0x30 /* 2 bits; LNA config used on RX */ +#define HAL_RX_LNA_CFG_USED_S 4 +#define HAL_RX_LNA_CFG_MAIN 0x0c /* 2 bits; "Main" LNA config */ +#define HAL_RX_LNA_CFG_ALT 0x02 /* 2 bits; "Alt" LNA config */ + +/* + * This is the format of RSSI_EXT[2] on the AR9285/AR9485. + * It encodes the switch table configuration and fast diversity + * value. + */ +#define HAL_RX_LNA_FASTDIV 0x40 /* 1 = fast diversity measurement done */ +#define HAL_RX_LNA_SWITCH_0 0x30 /* 2 bits; sw_0[1:0] */ +#define HAL_RX_LNA_SWITCH_COM 0x0f /* 4 bits, sw_com[3:0] */ enum { HAL_PHYERR_UNDERRUN = 0, /* Transmit underrun */ @@ -164,6 +212,8 @@ enum { HAL_PHYERR_HT_CRC_ERROR = 34, /* */ HAL_PHYERR_HT_LENGTH_ILLEGAL = 35, /* */ HAL_PHYERR_HT_RATE_ILLEGAL = 36, /* */ + + HAL_PHYERR_SPECTRAL = 38, }; /* value found in rs_keyix to mark invalid entries */ @@ -200,6 +250,12 @@ struct ath_desc { uint32_t ds_hw[HAL_DESC_HW_SIZE]; /* opaque h/w region */ }; +struct ath_desc_txedma { + uint32_t ds_info; + uint32_t ds_link; + uint32_t ds_hw[21]; /* includes buf/len */ +}; + struct ath_desc_status { union { struct ath_tx_status tx;/* xmit status */ @@ -211,6 +267,7 @@ struct ath_desc_status { #define ds_rxstat ds_us.rx /* flags passed to tx descriptor setup methods */ +/* This is a uint16_t field in ath_buf, just be warned! */ #define HAL_TXDESC_CLRDMASK 0x0001 /* clear destination filter mask */ #define HAL_TXDESC_NOACK 0x0002 /* don't wait for ACK */ #define HAL_TXDESC_RTSENA 0x0004 /* enable RTS */ @@ -222,6 +279,9 @@ struct ath_desc_status { #define HAL_TXDESC_EXT_ONLY 0x0080 /* send on ext channel only (11n) */ #define HAL_TXDESC_EXT_AND_CTL 0x0100 /* send on ext + ctl channels (11n) */ #define HAL_TXDESC_VMF 0x0200 /* virtual more frag */ +#define HAL_TXDESC_LOWRXCHAIN 0x0400 /* switch to low RX chain */ +#define HAL_TXDESC_LDPC 0x1000 /* Set LDPC TX for all rates */ +#define HAL_TXDESC_HWTS 0x2000 /* Request Azimuth Timestamp in TX payload */ /* flags passed to rx descriptor setup methods */ #define HAL_RXDESC_INTREQ 0x0020 /* enable per-descriptor interrupt */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_devid.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_devid.h index c7a98dda4f..13f4c357d1 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_devid.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_devid.h @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ah_devid.h 291413 2015-11-28 00:27:16Z adrian $ */ #ifndef _DEV_ATH_DEVID_H_ @@ -73,16 +73,29 @@ /* AR5416 compatible devid's */ #define AR5416_DEVID_PCI 0x0023 /* AR5416 PCI (MB/CB) Owl */ -#define AR5416_DEVID_PCIE 0x0024 /* AR5416 PCI-E (XB) Owl */ +#define AR5416_DEVID_PCIE 0x0024 /* AR5418 PCI-E (XB) Owl */ #define AR5416_AR9130_DEVID 0x000b /* AR9130 SoC WiMAC */ #define AR9160_DEVID_PCI 0x0027 /* AR9160 PCI Sowl */ #define AR9280_DEVID_PCI 0x0029 /* AR9280 PCI Merlin */ -#define AR9280_DEVID_PCIE 0x002a /* AR9280 PCI-E Merlin */ +#define AR9280_DEVID_PCIE 0x002a /* AR9220 PCI-E Merlin */ #define AR9285_DEVID_PCIE 0x002b /* AR9285 PCI-E Kite */ #define AR2427_DEVID_PCIE 0x002c /* AR2427 PCI-E w/ 802.11n bonded out */ #define AR9287_DEVID_PCI 0x002d /* AR9227 PCI Kiwi */ #define AR9287_DEVID_PCIE 0x002e /* AR9287 PCI-E Kiwi */ +/* AR9300 */ +#define AR9300_DEVID_AR9380_PCIE 0x0030 +#define AR9300_DEVID_EMU_PCIE 0xabcd +#define AR9300_DEVID_AR9340 0x0031 +#define AR9300_DEVID_AR9485_PCIE 0x0032 +#define AR9300_DEVID_AR9580_PCIE 0x0033 +#define AR9300_DEVID_AR946X_PCIE 0x0034 +#define AR9300_DEVID_AR9330 0x0035 +#define AR9300_DEVID_QCA9565 0x0036 +#define AR9300_DEVID_AR1111_PCIE 0x0037 +#define AR9300_DEVID_QCA955X 0x0039 +#define AR9300_DEVID_QCA953X 0x003d /* Honey Bee */ + #define AR_SUBVENDOR_ID_NOG 0x0e11 /* No 11G subvendor ID */ #define AR_SUBVENDOR_ID_NEW_A 0x7065 /* Update device to new RD */ #endif /* _DEV_ATH_DEVID_H */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_diagcodes.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_diagcodes.h index 1e29b2cebe..0863e07cbc 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_diagcodes.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_diagcodes.h @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ah_diagcodes.h 239629 2012-08-24 00:17:39Z adrian $ */ #ifndef _ATH_AH_DIAGCODES_H_ #define _ATH_AH_DIAGCODES_H_ @@ -63,6 +63,10 @@ enum { HAL_DIAG_CHECK_HANGS = 32, /* check h/w hangs */ HAL_DIAG_SETREGS = 33, /* write registers */ HAL_DIAG_CHANSURVEY = 34, /* channel survey */ + HAL_DIAG_PRINT_REG = 35, + HAL_DIAG_PRINT_REG_ALL = 36, + HAL_DIAG_CHANNELS = 37, + HAL_DIAG_PRINT_REG_COUNTER = 38, }; #endif /* _ATH_AH_DIAGCODES_H_ */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom.h index b77fb6427c..a2f5e0b259 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom.h @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ah_eeprom.h 239637 2012-08-24 01:14:00Z adrian $ */ #ifndef _ATH_AH_EEPROM_H_ #define _ATH_AH_EEPROM_H_ @@ -105,6 +105,10 @@ enum { AR_EEP_TEMPSENSE_SLOPE, /* int8_t* */ AR_EEP_TEMPSENSE_SLOPE_PAL_ON, /* int8_t* */ AR_EEP_FRAC_N_5G, /* uint8_t* */ + + /* New fields for AR9300 and later */ + AR_EEP_DRIVE_STRENGTH, + AR_EEP_PAPRD_ENABLED, }; typedef struct { @@ -127,6 +131,30 @@ typedef struct { #define CTL_2GHT40 7 #define CTL_5GHT40 8 +/* XXX must match what FCC/MKK/ETSI are defined as in ah_regdomain.h */ +#define HAL_REG_DMN_MASK 0xf0 +#define HAL_REGDMN_FCC 0x10 +#define HAL_REGDMN_MKK 0x40 +#define HAL_REGDMN_ETSI 0x30 + +#define is_reg_dmn_fcc(reg_dmn) \ + (((reg_dmn & HAL_REG_DMN_MASK) == HAL_REGDMN_FCC) ? 1 : 0) +#define is_reg_dmn_etsi(reg_dmn) \ + (((reg_dmn & HAL_REG_DMN_MASK) == HAL_REGDMN_ETSI) ? 1 : 0) +#define is_reg_dmn_mkk(reg_dmn) \ + (((reg_dmn & HAL_REG_DMN_MASK) == HAL_REGDMN_MKK) ? 1 : 0) + +#define AR_EEPROM_EEREGCAP_EN_FCC_MIDBAND 0x0040 +#define AR_EEPROM_EEREGCAP_EN_KK_U1_EVEN 0x0080 +#define AR_EEPROM_EEREGCAP_EN_KK_U2 0x0100 +#define AR_EEPROM_EEREGCAP_EN_KK_MIDBAND 0x0200 +#define AR_EEPROM_EEREGCAP_EN_KK_U1_ODD 0x0400 +#define AR_EEPROM_EEREGCAP_EN_KK_NEW_11A 0x0800 + +/* regulatory capabilities prior to eeprom version 4.0 */ +#define AR_EEPROM_EEREGCAP_EN_KK_U1_ODD_PRE4_0 0x4000 +#define AR_EEPROM_EEREGCAP_EN_KK_NEW_11A_PRE4_0 0x8000 + #define AR_NO_SPUR 0x8000 /* XXX exposed to chip code */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_9287.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_9287.c index 099fe34b2b..39c941e4a8 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_9287.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_9287.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ah_eeprom_9287.c 239704 2012-08-26 04:26:49Z adrian $ */ #include "opt_ah.h" @@ -310,12 +310,12 @@ ath_hal_9287EepromAttach(struct ath_hal *ah) "%s Error reading Eeprom MAGIC\n", __func__); return HAL_EEREAD; } - } - HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s Eeprom Magic = 0x%x\n", - __func__, magic); - if (magic != AR5416_EEPROM_MAGIC) { - HALDEBUG(ah, HAL_DEBUG_ANY, "Bad magic number\n"); - return HAL_EEMAGIC; + HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s Eeprom Magic = 0x%x\n", + __func__, magic); + if (magic != AR5416_EEPROM_MAGIC) { + HALDEBUG(ah, HAL_DEBUG_ANY, "Bad magic number\n"); + return HAL_EEMAGIC; + } } ee = ath_hal_malloc(sizeof(HAL_EEPROM_9287)); diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_9287.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_9287.h index ff8080aa3a..396bace83e 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_9287.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_9287.h @@ -13,7 +13,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ah_eeprom_9287.h 223615 2011-06-28 00:01:55Z adrian $ */ #ifndef __AH_EEPROM_9287_H__ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_v1.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_v1.c index 8b4664fc98..59e24c6b99 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_v1.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_v1.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ah_eeprom_v1.c 221896 2011-05-14 15:12:02Z adrian $ */ #include "opt_ah.h" diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_v1.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_v1.h index 22fce8a092..508c01471f 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_v1.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_v1.h @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ah_eeprom_v1.h 204644 2010-03-03 17:32:32Z rpaulo $ */ #ifndef _ATH_AH_EEPROM_V1_H_ #define _ATH_AH_EEPROM_V1_H_ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_v14.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_v14.c index 37e973c169..86c5327891 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_v14.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_v14.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ah_eeprom_v14.c 224519 2011-07-30 13:45:12Z adrian $ */ #include "opt_ah.h" diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_v14.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_v14.h index 7b2c898ce6..6a2dd6090a 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_v14.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_v14.h @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ah_eeprom_v14.h 224519 2011-07-30 13:45:12Z adrian $ */ #ifndef _AH_EEPROM_V14_H_ #define _AH_EEPROM_V14_H_ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_v3.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_v3.c index 3a7b0cc222..9114da8e58 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_v3.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_v3.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ah_eeprom_v3.c 221896 2011-05-14 15:12:02Z adrian $ */ #include "opt_ah.h" diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_v3.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_v3.h index cde9a10af3..eb36cec5c2 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_v3.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_v3.h @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ah_eeprom_v3.h 204644 2010-03-03 17:32:32Z rpaulo $ */ #ifndef _ATH_AH_EEPROM_V3_H_ #define _ATH_AH_EEPROM_V3_H_ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_v4k.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_v4k.c index 12cb67e437..65914f4631 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_v4k.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_v4k.c @@ -15,7 +15,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ah_eeprom_v4k.c 234508 2012-04-20 21:56:13Z adrian $ */ #include "opt_ah.h" diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_v4k.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_v4k.h index 3487de4724..3af57d6883 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_v4k.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_eeprom_v4k.h @@ -15,7 +15,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ah_eeprom_v4k.h 220946 2011-04-22 10:57:46Z adrian $ */ #ifndef _AH_EEPROM_V4K_H_ #define _AH_EEPROM_V4K_H_ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_internal.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_internal.h index 79184bd236..5b86461824 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_internal.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_internal.h @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ah_internal.h 291469 2015-11-30 06:26:59Z adrian $ */ #ifndef _ATH_AH_INTERAL_H_ #define _ATH_AH_INTERAL_H_ @@ -30,6 +30,10 @@ #include #include "opt_ah.h" /* needed for AH_SUPPORT_AR5416 */ +#ifndef AH_SUPPORT_AR5416 +#define AH_SUPPORT_AR5416 1 +#endif + #ifndef NBBY #define NBBY 8 /* number of bits/byte */ #endif @@ -46,8 +50,8 @@ #endif typedef struct { - uint16_t start; /* first register */ - uint16_t end; /* ending register or zero */ + uint32_t start; /* first register */ + uint32_t end; /* ending register or zero */ } HAL_REGRANGE; typedef struct { @@ -74,6 +78,11 @@ typedef enum { HAL_CAP_AR = 1, /* AR capability */ } HAL_PHYDIAG_CAPS; +/* + * Enable/disable strong signal fast diversity + */ +#define HAL_CAP_STRONG_DIV 2 + /* * Each chip or class of chips registers to offer support. */ @@ -82,6 +91,7 @@ struct ath_hal_chip { const char *(*probe)(uint16_t vendorid, uint16_t devid); struct ath_hal *(*attach)(uint16_t devid, HAL_SOFTC, HAL_BUS_TAG, HAL_BUS_HANDLE, uint16_t *eepromdata, + HAL_OPS_CONFIG *ah, HAL_STATUS *error); }; #ifndef AH_CHIP @@ -127,6 +137,41 @@ struct ath_hal_rf *ath_hal_rfprobe(struct ath_hal *ah, HAL_STATUS *ecode); #define AH_MAXCHAN 96 #endif +#define HAL_NF_CAL_HIST_LEN_FULL 5 +#define HAL_NF_CAL_HIST_LEN_SMALL 1 +#define HAL_NUM_NF_READINGS 6 /* 3 chains * (ctl + ext) */ +#define HAL_NF_LOAD_DELAY 1000 + +/* + * PER_CHAN doesn't work for now, as it looks like the device layer + * has to pre-populate the per-channel list with nominal values. + */ +//#define ATH_NF_PER_CHAN 1 + +typedef struct { + u_int8_t curr_index; + int8_t invalidNFcount; /* TO DO: REMOVE THIS! */ + int16_t priv_nf[HAL_NUM_NF_READINGS]; +} HAL_NFCAL_BASE; + +typedef struct { + HAL_NFCAL_BASE base; + int16_t nf_cal_buffer[HAL_NF_CAL_HIST_LEN_FULL][HAL_NUM_NF_READINGS]; +} HAL_NFCAL_HIST_FULL; + +typedef struct { + HAL_NFCAL_BASE base; + int16_t nf_cal_buffer[HAL_NF_CAL_HIST_LEN_SMALL][HAL_NUM_NF_READINGS]; +} HAL_NFCAL_HIST_SMALL; + +#ifdef ATH_NF_PER_CHAN +typedef HAL_NFCAL_HIST_FULL HAL_CHAN_NFCAL_HIST; +#define AH_HOME_CHAN_NFCAL_HIST(ah, ichan) (ichan ? &ichan->nf_cal_hist: NULL) +#else +typedef HAL_NFCAL_HIST_SMALL HAL_CHAN_NFCAL_HIST; +#define AH_HOME_CHAN_NFCAL_HIST(ah, ichan) (&AH_PRIVATE(ah)->nf_cal_hist) +#endif /* ATH_NF_PER_CHAN */ + /* * Internal per-channel state. These are found * using ic_devdata in the ieee80211_channel. @@ -144,10 +189,16 @@ typedef struct { int16_t rawNoiseFloor; int16_t noiseFloorAdjust; #ifdef AH_SUPPORT_AR5416 - int16_t noiseFloorCtl[AH_MIMO_MAX_CHAINS]; - int16_t noiseFloorExt[AH_MIMO_MAX_CHAINS]; + int16_t noiseFloorCtl[AH_MAX_CHAINS]; + int16_t noiseFloorExt[AH_MAX_CHAINS]; #endif /* AH_SUPPORT_AR5416 */ uint16_t mainSpur; /* cached spur value for this channel */ + + /*XXX TODO: make these part of privFlags */ + uint8_t paprd_done:1, /* 1: PAPRD DONE, 0: PAPRD Cal not done */ + paprd_table_write_done:1; /* 1: DONE, 0: Cal data write not done */ + int one_time_cals_done; + HAL_CHAN_NFCAL_HIST nf_cal_hist; } HAL_CHANNEL_INTERNAL; /* channel requires noise floor check */ @@ -208,8 +259,32 @@ typedef struct { halBssidMatchSupport : 1, hal4kbSplitTransSupport : 1, halHasRxSelfLinkedTail : 1, - halSupportsFastClock5GHz : 1, /* Hardware supports 5ghz fast clock; check eeprom/channel before using */ - halHasLongRxDescTsf : 1; + halSupportsFastClock5GHz : 1, + halHasLongRxDescTsf : 1, + halHasBBReadWar : 1, + halSerialiseRegWar : 1, + halMciSupport : 1, + halRxTxAbortSupport : 1, + halPaprdEnabled : 1, + halHasUapsdSupport : 1, + halWpsPushButtonSupport : 1, + halBtCoexApsmWar : 1, + halGenTimerSupport : 1, + halLDPCSupport : 1, + halHwBeaconProcSupport : 1, + halEnhancedDmaSupport : 1; + uint32_t halIsrRacSupport : 1, + halApmEnable : 1, + halIntrMitigation : 1, + hal49GhzSupport : 1, + halAntDivCombSupport : 1, + halAntDivCombSupportOrg : 1, + halRadioRetentionSupport : 1, + halSpectralScanSupport : 1, + halRxUsingLnaMixing : 1, + halRxDoMyBeacon : 1, + halHwUapsdTrig : 1; + uint32_t halWirelessModes; uint16_t halTotalQueues; uint16_t halKeyCacheSize; @@ -225,10 +300,32 @@ typedef struct { uint32_t halIntrMask; uint8_t halTxStreams; uint8_t halRxStreams; + HAL_MFP_OPT_T halMfpSupport; + + /* AR9300 HAL porting capabilities */ + int hal_paprd_enabled; + int hal_pcie_lcr_offset; + int hal_pcie_lcr_extsync_en; + int halNumTxMaps; + int halTxDescLen; + int halTxStatusLen; + int halRxStatusLen; + int halRxHpFifoDepth; + int halRxLpFifoDepth; + uint32_t halRegCap; /* XXX needed? */ + int halNumMRRetries; + int hal_ani_poll_interval; + int hal_channel_switch_time_usec; } HAL_CAPABILITIES; struct regDomain; +/* + * Definitions for ah_flags in ath_hal_private + */ +#define AH_USE_EEPROM 0x1 +#define AH_IS_HB63 0x2 + /* * The ``private area'' follows immediately after the ``public area'' * in the data structure returned by ath_hal_attach. Private data are @@ -288,7 +385,9 @@ struct ath_hal_private { uint16_t ah_phyRev; /* PHY revision */ uint16_t ah_analog5GhzRev; /* 2GHz radio revision */ uint16_t ah_analog2GhzRev; /* 5GHz radio revision */ + uint32_t ah_flags; /* misc flags */ uint8_t ah_ispcie; /* PCIE, special treatment */ + uint8_t ah_devType; /* card type - CB, PCI, PCIe */ HAL_OPMODE ah_opmode; /* operating mode from reset */ const struct ieee80211_channel *ah_curchan;/* operating channel */ @@ -297,6 +396,7 @@ struct ath_hal_private { int16_t ah_powerLimit; /* tx power cap */ uint16_t ah_maxPowerLevel; /* calculated max tx power */ u_int ah_tpScale; /* tx power scale factor */ + u_int16_t ah_extraTxPow; /* low rates extra-txpower */ uint32_t ah_11nCompat; /* 11n compat controls */ /* @@ -321,6 +421,14 @@ struct ath_hal_private { */ uint32_t ah_fatalState[6]; /* AR_ISR+shadow regs */ int ah_rxornIsFatal; /* how to treat HAL_INT_RXORN */ + + /* Only used if ATH_NF_PER_CHAN is defined */ + HAL_NFCAL_HIST_FULL nf_cal_hist; + + /* + * Channel survey history - current channel only. + */ + HAL_CHANNEL_SURVEY ah_chansurvey; /* channel survey */ }; #define AH_PRIVATE(_ah) ((struct ath_hal_private *)(_ah)) @@ -349,13 +457,20 @@ struct ath_hal_private { AH_PRIVATE(_ah)->ah_getNfAdjust(_ah, _c) #define ath_hal_getNoiseFloor(_ah, _nfArray) \ AH_PRIVATE(_ah)->ah_getNoiseFloor(_ah, _nfArray) -#define ath_hal_configPCIE(_ah, _reset) \ - (_ah)->ah_configPCIE(_ah, _reset) +#define ath_hal_configPCIE(_ah, _reset, _poweroff) \ + (_ah)->ah_configPCIE(_ah, _reset, _poweroff) #define ath_hal_disablePCIE(_ah) \ (_ah)->ah_disablePCIE(_ah) #define ath_hal_setInterrupts(_ah, _mask) \ (_ah)->ah_setInterrupts(_ah, _mask) +#define ath_hal_isrfkillenabled(_ah) \ + (ath_hal_getcapability(_ah, HAL_CAP_RFSILENT, 1, AH_NULL) == HAL_OK) +#define ath_hal_enable_rfkill(_ah, _v) \ + ath_hal_setcapability(_ah, HAL_CAP_RFSILENT, 1, _v, AH_NULL) +#define ath_hal_hasrfkill_int(_ah) \ + (ath_hal_getcapability(_ah, HAL_CAP_RFSILENT, 3, AH_NULL) == HAL_OK) + #define ath_hal_eepromDetach(_ah) do { \ if (AH_PRIVATE(_ah)->ah_eepromDetach != AH_NULL) \ AH_PRIVATE(_ah)->ah_eepromDetach(_ah); \ @@ -472,10 +587,23 @@ isBigEndian(void) OS_REG_WRITE(_a, _r, OS_REG_READ(_a, _r) | (_f)) #define OS_REG_CLR_BIT(_a, _r, _f) \ OS_REG_WRITE(_a, _r, OS_REG_READ(_a, _r) &~ (_f)) +#define OS_REG_IS_BIT_SET(_a, _r, _f) \ + ((OS_REG_READ(_a, _r) & (_f)) != 0) +#define OS_REG_RMW_FIELD_ALT(_a, _r, _f, _v) \ + OS_REG_WRITE(_a, _r, \ + (OS_REG_READ(_a, _r) &~(_f<<_f##_S)) | \ + (((_v) << _f##_S) & (_f<<_f##_S))) +#define OS_REG_READ_FIELD(_a, _r, _f) \ + (((OS_REG_READ(_a, _r) & _f) >> _f##_S)) +#define OS_REG_READ_FIELD_ALT(_a, _r, _f) \ + ((OS_REG_READ(_a, _r) >> (_f##_S))&(_f)) /* Analog register writes may require a delay between each one (eg Merlin?) */ #define OS_A_REG_RMW_FIELD(_a, _r, _f, _v) \ - do { OS_REG_WRITE(_a, _r, (OS_REG_READ(_a, _r) &~ (_f)) | (((_v) << _f##_S) & (_f))) ; OS_DELAY(100); } while (0) + do { OS_REG_WRITE(_a, _r, (OS_REG_READ(_a, _r) &~ (_f)) | \ + (((_v) << _f##_S) & (_f))) ; OS_DELAY(100); } while (0) +#define OS_A_REG_WRITE(_a, _r, _v) \ + do { OS_REG_WRITE(_a, _r, _v); OS_DELAY(100); } while (0) /* wait for the register contents to have the specified value */ extern HAL_BOOL ath_hal_wait(struct ath_hal *, u_int reg, @@ -503,26 +631,15 @@ extern void ath_hal_free(void *); extern int ath_hal_debug; /* Global debug flags */ /* - * This is used for global debugging, when ahp doesn't yet have the - * related debugging state. For example, during probe/attach. - */ -#define HALDEBUG_G(_ah, __m, ...) \ - do { \ - if ((__m) == HAL_DEBUG_UNMASKABLE || \ - ath_hal_debug & (__m)) { \ - DO_HALDEBUG((_ah), (__m), __VA_ARGS__); \ - } \ - } while (0); - -/* - * This is used for local debugging, when ahp isn't NULL and - * thus may have debug flags set. + * The typecast is purely because some callers will pass in + * AH_NULL directly rather than using a NULL ath_hal pointer. */ #define HALDEBUG(_ah, __m, ...) \ do { \ if ((__m) == HAL_DEBUG_UNMASKABLE || \ ath_hal_debug & (__m) || \ - (_ah)->ah_config.ah_debug & (__m)) { \ + ((_ah) != NULL && \ + ((struct ath_hal *) (_ah))->ah_config.ah_debug & (__m))) { \ DO_HALDEBUG((_ah), (__m), __VA_ARGS__); \ } \ } while(0); @@ -531,7 +648,6 @@ extern void DO_HALDEBUG(struct ath_hal *ah, u_int mask, const char* fmt, ...) __printflike(3,4); #else #define HALDEBUG(_ah, __m, ...) -#define HALDEBUG_G(_ah, __m, ...) #endif /* AH_DEBUG */ /* @@ -630,6 +746,46 @@ extern HAL_BOOL ath_hal_setcapability(struct ath_hal *ah, /* The diagnostic codes used to be internally defined here -adrian */ #include "ah_diagcodes.h" +/* + * The AR5416 and later HALs have MAC and baseband hang checking. + */ +typedef struct { + uint32_t hang_reg_offset; + uint32_t hang_val; + uint32_t hang_mask; + uint32_t hang_offset; +} hal_hw_hang_check_t; + +typedef struct { + uint32_t dma_dbg_3; + uint32_t dma_dbg_4; + uint32_t dma_dbg_5; + uint32_t dma_dbg_6; +} mac_dbg_regs_t; + +typedef enum { + dcu_chain_state = 0x1, + dcu_complete_state = 0x2, + qcu_state = 0x4, + qcu_fsp_ok = 0x8, + qcu_fsp_state = 0x10, + qcu_stitch_state = 0x20, + qcu_fetch_state = 0x40, + qcu_complete_state = 0x80 +} hal_mac_hangs_t; + +typedef struct { + int states; + uint8_t dcu_chain_state; + uint8_t dcu_complete_state; + uint8_t qcu_state; + uint8_t qcu_fsp_ok; + uint8_t qcu_fsp_state; + uint8_t qcu_stitch_state; + uint8_t qcu_fetch_state; + uint8_t qcu_complete_state; +} hal_mac_hang_check_t; + enum { HAL_BB_HANG_DFS = 0x0001, HAL_BB_HANG_RIFS = 0x0002, @@ -649,6 +805,17 @@ enum { | HAL_MAC_HANG_UNKNOWN, }; +/* Merge these with above */ +typedef enum hal_hw_hangs { + HAL_DFS_BB_HANG_WAR = 0x1, + HAL_RIFS_BB_HANG_WAR = 0x2, + HAL_RX_STUCK_LOW_BB_HANG_WAR = 0x4, + HAL_MAC_HANG_WAR = 0x8, + HAL_PHYRESTART_CLR_WAR = 0x10, + HAL_MAC_HANG_DETECTED = 0x40000000, + HAL_BB_HANG_DETECTED = 0x80000000 +} hal_hw_hangs_t; + /* * Device revision information. */ @@ -830,5 +997,51 @@ extern int16_t ath_ee_interpolate(uint16_t target, uint16_t srcLeft, AH_PRIVATE((_ah))->ah_caps.halSupportsFastClock5GHz && \ ath_hal_eepromGetFlag((_ah), AR_EEP_FSTCLK_5G)) +/* + * Fetch the maximum regulatory domain power for the given channel + * in 1/2dBm steps. + */ +static inline int +ath_hal_get_twice_max_regpower(struct ath_hal_private *ahp, + const HAL_CHANNEL_INTERNAL *ichan, const struct ieee80211_channel *chan) +{ + struct ath_hal *ah = &ahp->h; + + if (! chan) { + ath_hal_printf(ah, "%s: called with chan=NULL!\n", __func__); + return (0); + } + return (chan->ic_maxpower); +} + +/* + * Get the maximum antenna gain allowed, in 1/2dBm steps. + */ +static inline int +ath_hal_getantennaallowed(struct ath_hal *ah, + const struct ieee80211_channel *chan) +{ + + if (! chan) + return (0); + + return (chan->ic_maxantgain); +} + +/* + * Map the given 2GHz channel to an IEEE number. + */ +extern int ath_hal_mhz2ieee_2ghz(struct ath_hal *, int freq); + +/* + * Clear the channel survey data. + */ +extern void ath_hal_survey_clear(struct ath_hal *ah); + +/* + * Add a sample to the channel survey data. + */ +extern void ath_hal_survey_add_sample(struct ath_hal *ah, + HAL_SURVEY_SAMPLE *hs); #endif /* _ATH_AH_INTERAL_H_ */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_regdomain.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_regdomain.c index 7298c6bee8..789938664d 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_regdomain.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_regdomain.c @@ -15,7 +15,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ah_regdomain.c 300267 2016-05-20 06:06:21Z adrian $ */ #include "opt_ah.h" @@ -35,7 +35,7 @@ */ /* used throughout this file... */ -#define N(a) (sizeof (a) / sizeof (a[0])) +#define N(a) nitems(a) #define HAL_MODE_11A_TURBO HAL_MODE_108A #define HAL_MODE_11G_TURBO HAL_MODE_108G @@ -99,30 +99,32 @@ #include "ah_regdomain/ah_rd_domains.h" static const struct cmode modes[] = { - { HAL_MODE_TURBO, IEEE80211_CHAN_ST }, - { HAL_MODE_11A, IEEE80211_CHAN_A }, - { HAL_MODE_11B, IEEE80211_CHAN_B }, - { HAL_MODE_11G, IEEE80211_CHAN_G }, - { HAL_MODE_11G_TURBO, IEEE80211_CHAN_108G }, - { HAL_MODE_11A_TURBO, IEEE80211_CHAN_108A }, + { HAL_MODE_TURBO, IEEE80211_CHAN_ST, ®Dmn5GhzTurboFreq[0] }, + { HAL_MODE_11A, IEEE80211_CHAN_A, ®Dmn5GhzFreq[0] }, + { HAL_MODE_11B, IEEE80211_CHAN_B, ®Dmn2GhzFreq[0] }, + { HAL_MODE_11G, IEEE80211_CHAN_G, ®Dmn2Ghz11gFreq[0] }, + { HAL_MODE_11G_TURBO, IEEE80211_CHAN_108G, ®Dmn2Ghz11gTurboFreq[0] }, + { HAL_MODE_11A_TURBO, IEEE80211_CHAN_108A, ®Dmn5GhzTurboFreq[0] }, { HAL_MODE_11A_QUARTER_RATE, - IEEE80211_CHAN_A | IEEE80211_CHAN_QUARTER }, + IEEE80211_CHAN_A | IEEE80211_CHAN_QUARTER, ®Dmn5GhzFreq[0] }, { HAL_MODE_11A_HALF_RATE, - IEEE80211_CHAN_A | IEEE80211_CHAN_HALF }, + IEEE80211_CHAN_A | IEEE80211_CHAN_HALF, ®Dmn5GhzFreq[0] }, { HAL_MODE_11G_QUARTER_RATE, - IEEE80211_CHAN_G | IEEE80211_CHAN_QUARTER }, + IEEE80211_CHAN_G | IEEE80211_CHAN_QUARTER, ®Dmn2Ghz11gFreq[0] }, { HAL_MODE_11G_HALF_RATE, - IEEE80211_CHAN_G | IEEE80211_CHAN_HALF }, - { HAL_MODE_11NG_HT20, IEEE80211_CHAN_G | IEEE80211_CHAN_HT20 }, + IEEE80211_CHAN_G | IEEE80211_CHAN_HALF, ®Dmn2Ghz11gFreq[0] }, + { HAL_MODE_11NG_HT20, + IEEE80211_CHAN_G | IEEE80211_CHAN_HT20, ®Dmn2Ghz11gFreq[0] }, { HAL_MODE_11NG_HT40PLUS, - IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U }, + IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U, ®Dmn2Ghz11gFreq[0] }, { HAL_MODE_11NG_HT40MINUS, - IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D }, - { HAL_MODE_11NA_HT20, IEEE80211_CHAN_A | IEEE80211_CHAN_HT20 }, + IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D, ®Dmn2Ghz11gFreq[0] }, + { HAL_MODE_11NA_HT20, + IEEE80211_CHAN_A | IEEE80211_CHAN_HT20, ®Dmn5GhzFreq[0] }, { HAL_MODE_11NA_HT40PLUS, - IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U }, + IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U, ®Dmn5GhzFreq[0] }, { HAL_MODE_11NA_HT40MINUS, - IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D }, + IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D, ®Dmn5GhzFreq[0] }, }; static void ath_hal_update_dfsdomain(struct ath_hal *ah); @@ -169,7 +171,12 @@ isEepromValid(struct ath_hal *ah) if (regDomainPairs[i].regDmnEnum == rd) return AH_TRUE; } - HALDEBUG_G(ah, HAL_DEBUG_REGDOMAIN, + + if (rd == FCC_UBNT) { + return AH_TRUE; + } + + HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: invalid regulatory domain/country code 0x%x\n", __func__, rd); return AH_FALSE; } @@ -353,6 +360,235 @@ getregstate(struct ath_hal *ah, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn, return HAL_OK; } +static uint64_t * +getchannelBM(u_int mode, REG_DOMAIN *rd) +{ + switch (mode) { + case HAL_MODE_11B: + return (rd->chan11b); + case HAL_MODE_11G_QUARTER_RATE: + return (rd->chan11g_quarter); + case HAL_MODE_11G_HALF_RATE: + return (rd->chan11g_half); + case HAL_MODE_11G: + case HAL_MODE_11NG_HT20: + case HAL_MODE_11NG_HT40PLUS: + case HAL_MODE_11NG_HT40MINUS: + return (rd->chan11g); + case HAL_MODE_11G_TURBO: + return (rd->chan11g_turbo); + case HAL_MODE_11A_QUARTER_RATE: + return (rd->chan11a_quarter); + case HAL_MODE_11A_HALF_RATE: + return (rd->chan11a_half); + case HAL_MODE_11A: + case HAL_MODE_11NA_HT20: + case HAL_MODE_11NA_HT40PLUS: + case HAL_MODE_11NA_HT40MINUS: + return (rd->chan11a); + case HAL_MODE_TURBO: + return (rd->chan11a_turbo); + case HAL_MODE_11A_TURBO: + return (rd->chan11a_dyn_turbo); + default: + return (AH_NULL); + } +} + +static void +setchannelflags(struct ieee80211_channel *c, REG_DMN_FREQ_BAND *fband, + REG_DOMAIN *rd) +{ + if (fband->usePassScan & rd->pscan) + c->ic_flags |= IEEE80211_CHAN_PASSIVE; + if (fband->useDfs & rd->dfsMask) + c->ic_flags |= IEEE80211_CHAN_DFS; + if (IEEE80211_IS_CHAN_5GHZ(c) && (rd->flags & DISALLOW_ADHOC_11A)) + c->ic_flags |= IEEE80211_CHAN_NOADHOC; + if (IEEE80211_IS_CHAN_TURBO(c) && + (rd->flags & DISALLOW_ADHOC_11A_TURB)) + c->ic_flags |= IEEE80211_CHAN_NOADHOC; + if (rd->flags & NO_HOSTAP) + c->ic_flags |= IEEE80211_CHAN_NOHOSTAP; + if (rd->flags & LIMIT_FRAME_4MS) + c->ic_flags |= IEEE80211_CHAN_4MSXMIT; + if (rd->flags & NEED_NFC) + c->ic_flags |= CHANNEL_NFCREQUIRED; +} + +static int +addchan(struct ath_hal *ah, struct ieee80211_channel chans[], + u_int maxchans, int *nchans, uint16_t freq, uint32_t flags, + REG_DMN_FREQ_BAND *fband, REG_DOMAIN *rd) +{ + struct ieee80211_channel *c; + + if (*nchans >= maxchans) + return (HAL_ENOMEM); + + c = &chans[(*nchans)++]; + c->ic_freq = freq; + c->ic_flags = flags; + setchannelflags(c, fband, rd); + c->ic_maxregpower = fband->powerDfs; + ath_hal_getpowerlimits(ah, c); + c->ic_maxantgain = fband->antennaMax; + + return (0); +} + +static int +copychan_prev(struct ath_hal *ah, struct ieee80211_channel chans[], + u_int maxchans, int *nchans, uint16_t freq) +{ + struct ieee80211_channel *c; + + if (*nchans == 0) + return (HAL_EINVAL); + + if (*nchans >= maxchans) + return (HAL_ENOMEM); + + c = &chans[(*nchans)++]; + c[0] = c[-1]; + c->ic_freq = freq; + /* XXX is it needed here? */ + ath_hal_getpowerlimits(ah, c); + + return (0); +} + +static int +add_chanlist_band(struct ath_hal *ah, struct ieee80211_channel chans[], + int maxchans, int *nchans, uint16_t freq_lo, uint16_t freq_hi, int step, + uint32_t flags, REG_DMN_FREQ_BAND *fband, REG_DOMAIN *rd) +{ + uint16_t freq = freq_lo; + int error; + + if (freq_hi < freq_lo) + return (0); + + error = addchan(ah, chans, maxchans, nchans, freq, flags, fband, rd); + for (freq += step; freq <= freq_hi && error == 0; freq += step) + error = copychan_prev(ah, chans, maxchans, nchans, freq); + + return (error); +} + +static void +adj_freq_ht40(u_int mode, int *low_adj, int *hi_adj, int *channelSep) +{ + + *low_adj = *hi_adj = *channelSep = 0; + switch (mode) { + case HAL_MODE_11NA_HT40PLUS: + *channelSep = 40; + /* FALLTHROUGH */ + case HAL_MODE_11NG_HT40PLUS: + *hi_adj = -20; + break; + case HAL_MODE_11NA_HT40MINUS: + *channelSep = 40; + /* FALLTHROUGH */ + case HAL_MODE_11NG_HT40MINUS: + *low_adj = 20; + break; + } +} + +static void +add_chanlist_mode(struct ath_hal *ah, struct ieee80211_channel chans[], + u_int maxchans, int *nchans, const struct cmode *cm, REG_DOMAIN *rd, + HAL_BOOL enableExtendedChannels) +{ + uint64_t *channelBM; + uint16_t freq_lo, freq_hi; + int b, error, low_adj, hi_adj, channelSep; + + if (!ath_hal_getChannelEdges(ah, cm->flags, &freq_lo, &freq_hi)) { + /* channel not supported by hardware, skip it */ + HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, + "%s: channels 0x%x not supported by hardware\n", + __func__, cm->flags); + return; + } + + channelBM = getchannelBM(cm->mode, rd); + if (isChanBitMaskZero(channelBM)) + return; + + /* + * Setup special handling for HT40 channels; e.g. + * 5G HT40 channels require 40Mhz channel separation. + */ + adj_freq_ht40(cm->mode, &low_adj, &hi_adj, &channelSep); + + for (b = 0; b < 64*BMLEN; b++) { + REG_DMN_FREQ_BAND *fband; + uint16_t bfreq_lo, bfreq_hi; + int step; + + if (!IS_BIT_SET(b, channelBM)) + continue; + fband = &cm->freqs[b]; + + if ((fband->usePassScan & IS_ECM_CHAN) && + !enableExtendedChannels) { + HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, + "skip ecm channels\n"); + continue; + } +#if 0 + if ((fband->useDfs & rd->dfsMask) && + (cm->flags & IEEE80211_CHAN_HT40)) { + /* NB: DFS and HT40 don't mix */ + HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, + "skip HT40 chan, DFS required\n"); + continue; + } +#endif + bfreq_lo = MAX(fband->lowChannel + low_adj, freq_lo); + bfreq_hi = MIN(fband->highChannel + hi_adj, freq_hi); + if (fband->channelSep >= channelSep) + step = fband->channelSep; + else + step = roundup(channelSep, fband->channelSep); + + error = add_chanlist_band(ah, chans, maxchans, nchans, + bfreq_lo, bfreq_hi, step, cm->flags, fband, rd); + if (error != 0) { + HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, + "%s: too many channels for channel table\n", + __func__); + return; + } + } +} + +static u_int +getmodesmask(struct ath_hal *ah, REG_DOMAIN *rd5GHz, u_int modeSelect) +{ +#define HAL_MODE_11A_ALL \ + (HAL_MODE_11A | HAL_MODE_11A_TURBO | HAL_MODE_TURBO | \ + HAL_MODE_11A_QUARTER_RATE | HAL_MODE_11A_HALF_RATE) + u_int modesMask; + + /* get modes that HW is capable of */ + modesMask = ath_hal_getWirelessModes(ah); + modesMask &= modeSelect; + /* optimize work below if no 11a channels */ + if (isChanBitMaskZero(rd5GHz->chan11a) && + (modesMask & HAL_MODE_11A_ALL)) { + HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, + "%s: disallow all 11a\n", __func__); + modesMask &= ~HAL_MODE_11A_ALL; + } + + return (modesMask); +#undef HAL_MODE_11A_ALL +} + /* * Construct the channel list for the specified regulatory config. */ @@ -364,16 +600,9 @@ getchannels(struct ath_hal *ah, COUNTRY_CODE_TO_ENUM_RD **pcountry, REG_DOMAIN **prd2GHz, REG_DOMAIN **prd5GHz) { -#define CHANNEL_HALF_BW 10 -#define CHANNEL_QUARTER_BW 5 -#define HAL_MODE_11A_ALL \ - (HAL_MODE_11A | HAL_MODE_11A_TURBO | HAL_MODE_TURBO | \ - HAL_MODE_11A_QUARTER_RATE | HAL_MODE_11A_HALF_RATE) REG_DOMAIN *rd5GHz, *rd2GHz; - u_int modesAvail; + u_int modesMask; const struct cmode *cm; - struct ieee80211_channel *ic; - int next, b; HAL_STATUS status; HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u regDmn 0x%x mode 0x%x%s\n", @@ -384,206 +613,43 @@ getchannels(struct ath_hal *ah, if (status != HAL_OK) return status; - /* get modes that HW is capable of */ - modesAvail = ath_hal_getWirelessModes(ah); - /* optimize work below if no 11a channels */ - if (isChanBitMaskZero(rd5GHz->chan11a) && - (modesAvail & HAL_MODE_11A_ALL)) { - HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, - "%s: disallow all 11a\n", __func__); - modesAvail &= ~HAL_MODE_11A_ALL; - } + modesMask = getmodesmask(ah, rd5GHz, modeSelect); + /* XXX error? */ + if (modesMask == 0) + goto done; - next = 0; - ic = &chans[0]; for (cm = modes; cm < &modes[N(modes)]; cm++) { - uint16_t c, c_hi, c_lo; - uint64_t *channelBM = AH_NULL; - REG_DMN_FREQ_BAND *fband = AH_NULL,*freqs; - int low_adj, hi_adj, channelSep, lastc; - uint32_t rdflags; - uint64_t dfsMask; - uint64_t pscan; + REG_DOMAIN *rd; - if ((cm->mode & modeSelect) == 0) { + if ((cm->mode & modesMask) == 0) { HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: skip mode 0x%x flags 0x%x\n", __func__, cm->mode, cm->flags); continue; } - if ((cm->mode & modesAvail) == 0) { - HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, - "%s: !avail mode 0x%x (0x%x) flags 0x%x\n", - __func__, modesAvail, cm->mode, cm->flags); - continue; - } - if (!ath_hal_getChannelEdges(ah, cm->flags, &c_lo, &c_hi)) { - /* channel not supported by hardware, skip it */ - HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, - "%s: channels 0x%x not supported by hardware\n", - __func__,cm->flags); - continue; - } - switch (cm->mode) { - case HAL_MODE_TURBO: - case HAL_MODE_11A_TURBO: - rdflags = rd5GHz->flags; - dfsMask = rd5GHz->dfsMask; - pscan = rd5GHz->pscan; - if (cm->mode == HAL_MODE_TURBO) - channelBM = rd5GHz->chan11a_turbo; - else - channelBM = rd5GHz->chan11a_dyn_turbo; - freqs = ®Dmn5GhzTurboFreq[0]; - break; - case HAL_MODE_11G_TURBO: - rdflags = rd2GHz->flags; - dfsMask = rd2GHz->dfsMask; - pscan = rd2GHz->pscan; - channelBM = rd2GHz->chan11g_turbo; - freqs = ®Dmn2Ghz11gTurboFreq[0]; - break; - case HAL_MODE_11A: - case HAL_MODE_11A_HALF_RATE: - case HAL_MODE_11A_QUARTER_RATE: - case HAL_MODE_11NA_HT20: - case HAL_MODE_11NA_HT40PLUS: - case HAL_MODE_11NA_HT40MINUS: - rdflags = rd5GHz->flags; - dfsMask = rd5GHz->dfsMask; - pscan = rd5GHz->pscan; - if (cm->mode == HAL_MODE_11A_HALF_RATE) - channelBM = rd5GHz->chan11a_half; - else if (cm->mode == HAL_MODE_11A_QUARTER_RATE) - channelBM = rd5GHz->chan11a_quarter; - else - channelBM = rd5GHz->chan11a; - freqs = ®Dmn5GhzFreq[0]; - break; - case HAL_MODE_11B: - case HAL_MODE_11G: - case HAL_MODE_11G_HALF_RATE: - case HAL_MODE_11G_QUARTER_RATE: - case HAL_MODE_11NG_HT20: - case HAL_MODE_11NG_HT40PLUS: - case HAL_MODE_11NG_HT40MINUS: - rdflags = rd2GHz->flags; - dfsMask = rd2GHz->dfsMask; - pscan = rd2GHz->pscan; - if (cm->mode == HAL_MODE_11G_HALF_RATE) - channelBM = rd2GHz->chan11g_half; - else if (cm->mode == HAL_MODE_11G_QUARTER_RATE) - channelBM = rd2GHz->chan11g_quarter; - else if (cm->mode == HAL_MODE_11B) - channelBM = rd2GHz->chan11b; - else - channelBM = rd2GHz->chan11g; - if (cm->mode == HAL_MODE_11B) - freqs = ®Dmn2GhzFreq[0]; - else - freqs = ®Dmn2Ghz11gFreq[0]; - break; - default: - HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, - "%s: Unkonwn HAL mode 0x%x\n", __func__, cm->mode); - continue; - } - if (isChanBitMaskZero(channelBM)) - continue; - /* - * Setup special handling for HT40 channels; e.g. - * 5G HT40 channels require 40Mhz channel separation. - */ - hi_adj = (cm->mode == HAL_MODE_11NA_HT40PLUS || - cm->mode == HAL_MODE_11NG_HT40PLUS) ? -20 : 0; - low_adj = (cm->mode == HAL_MODE_11NA_HT40MINUS || - cm->mode == HAL_MODE_11NG_HT40MINUS) ? 20 : 0; - channelSep = (cm->mode == HAL_MODE_11NA_HT40PLUS || - cm->mode == HAL_MODE_11NA_HT40MINUS) ? 40 : 0; - for (b = 0; b < 64*BMLEN; b++) { - if (!IS_BIT_SET(b, channelBM)) - continue; - fband = &freqs[b]; - lastc = 0; - - for (c = fband->lowChannel + low_adj; - c <= fband->highChannel + hi_adj; - c += fband->channelSep) { - if (!(c_lo <= c && c <= c_hi)) { - HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, - "%s: c %u out of range [%u..%u]\n", - __func__, c, c_lo, c_hi); - continue; - } - if (next >= maxchans){ - HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, - "%s: too many channels for channel table\n", - __func__); - goto done; - } - if ((fband->usePassScan & IS_ECM_CHAN) && - !enableExtendedChannels) { - HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, - "skip ecm channel\n"); - continue; - } -#if 0 - if ((fband->useDfs & dfsMask) && - (cm->flags & IEEE80211_CHAN_HT40)) { - /* NB: DFS and HT40 don't mix */ - HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, - "skip HT40 chan, DFS required\n"); - continue; - } -#endif - /* - * Make sure that channel separation - * meets the requirement. - */ - if (lastc && channelSep && - (c-lastc) < channelSep) - continue; - lastc = c; - - OS_MEMZERO(ic, sizeof(*ic)); - ic->ic_freq = c; - ic->ic_flags = cm->flags; - ic->ic_maxregpower = fband->powerDfs; - ath_hal_getpowerlimits(ah, ic); - ic->ic_maxantgain = fband->antennaMax; - if (fband->usePassScan & pscan) - ic->ic_flags |= IEEE80211_CHAN_PASSIVE; - if (fband->useDfs & dfsMask) - ic->ic_flags |= IEEE80211_CHAN_DFS; - if (IEEE80211_IS_CHAN_5GHZ(ic) && - (rdflags & DISALLOW_ADHOC_11A)) - ic->ic_flags |= IEEE80211_CHAN_NOADHOC; - if (IEEE80211_IS_CHAN_TURBO(ic) && - (rdflags & DISALLOW_ADHOC_11A_TURB)) - ic->ic_flags |= IEEE80211_CHAN_NOADHOC; - if (rdflags & NO_HOSTAP) - ic->ic_flags |= IEEE80211_CHAN_NOHOSTAP; - if (rdflags & LIMIT_FRAME_4MS) - ic->ic_flags |= IEEE80211_CHAN_4MSXMIT; - if (rdflags & NEED_NFC) - ic->ic_flags |= CHANNEL_NFCREQUIRED; - - ic++, next++; - } + if (cm->flags & IEEE80211_CHAN_5GHZ) + rd = rd5GHz; + else if (cm->flags & IEEE80211_CHAN_2GHZ) + rd = rd2GHz; + else { + ath_hal_printf(ah, "%s: Unkonwn HAL flags 0x%x\n", + __func__, cm->flags); + return HAL_EINVAL; } + + add_chanlist_mode(ah, chans, maxchans, nchans, cm, + rd, enableExtendedChannels); + if (*nchans >= maxchans) + goto done; } done: - *nchans = next; /* NB: pcountry set above by getregstate */ if (prd2GHz != AH_NULL) *prd2GHz = rd2GHz; if (prd5GHz != AH_NULL) *prd5GHz = rd5GHz; return HAL_OK; -#undef HAL_MODE_11A_ALL -#undef CHANNEL_HALF_BW -#undef CHANNEL_QUARTER_BW } /* @@ -613,7 +679,9 @@ ath_hal_mapgsm(int sku, int freq) return 1544 + freq; if (sku == SKU_SR9) return 3344 - freq; - HALDEBUG_G(AH_NULL, HAL_DEBUG_ANY, + if (sku == SKU_XC900M) + return 1517 + freq; + HALDEBUG(AH_NULL, HAL_DEBUG_ANY, "%s: cannot map freq %u unknown gsm sku %u\n", __func__, freq, sku); return freq; @@ -727,6 +795,7 @@ ath_hal_set_channels(struct ath_hal *ah, case SKU_SR9: case SKU_XR9: case SKU_GZ901: + case SKU_XC900M: /* * Map 900MHz sku's. The frequencies will be mapped * according to the sku to compensate for the down-converter. diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_regdomain.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_regdomain.h index e17e79a4e6..3ff84fbf86 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_regdomain.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_regdomain.h @@ -15,7 +15,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ah_regdomain.h 300246 2016-05-19 23:00:30Z avos $ */ #ifndef __AH_REGDOMAIN_H__ #define __AH_REGDOMAIN_H__ @@ -157,7 +157,8 @@ typedef struct regDomain { } REG_DOMAIN; struct cmode { - u_int mode; - u_int flags; + u_int mode; + u_int flags; + REG_DMN_FREQ_BAND *freqs; }; #endif diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_regdomain/ah_rd_ctry.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_regdomain/ah_rd_ctry.h index af4824c3b5..d308f3bfbd 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_regdomain/ah_rd_ctry.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_regdomain/ah_rd_ctry.h @@ -15,7 +15,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ah_regdomain/ah_rd_ctry.h 224226 2011-07-20 12:46:58Z adrian $ */ #ifndef __AH_REGDOMAIN_CTRY_H__ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_regdomain/ah_rd_domains.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_regdomain/ah_rd_domains.h index b8b8b2b8e9..071b27e426 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_regdomain/ah_rd_domains.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_regdomain/ah_rd_domains.h @@ -15,7 +15,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ah_regdomain/ah_rd_domains.h 248677 2013-03-24 04:42:56Z adrian $ */ #ifndef __AH_REGDOMAIN_DOMAINS_H__ @@ -741,6 +741,31 @@ static REG_DOMAIN regDomains[] = { WG1_2467_2467), .chan11g_turbo = BM1(T3_2437_2437)}, + {.regDmnEnum = WORC_WORLD, + .conformanceTestLimit = NO_CTL, + .dfsMask = DFS_FCC3 | DFS_ETSI, + .pscan = PSCAN_WWR, + .flags = ADHOC_PER_11D, + .chan11a = BM4(W1_5260_5320, + W1_5180_5240, + W1_5745_5825, + W1_5500_5700), + .chan11b = BM7(W1_2412_2412, + W1_2437_2442, + W1_2462_2462, + W1_2472_2472, + W1_2417_2432, + W1_2447_2457, + W1_2467_2467), + .chan11g = BM7(WG1_2412_2412, + WG1_2437_2442, + WG1_2462_2462, + WG1_2472_2472, + WG1_2417_2432, + WG1_2447_2457, + WG1_2467_2467), + .chan11g_turbo = BM1(T3_2437_2437)}, + {.regDmnEnum = NULL1, .conformanceTestLimit = NO_CTL, } diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_regdomain/ah_rd_freqbands.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_regdomain/ah_rd_freqbands.h index 79d418f3c7..2f23597c1e 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_regdomain/ah_rd_freqbands.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_regdomain/ah_rd_freqbands.h @@ -15,7 +15,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ah_regdomain/ah_rd_freqbands.h 219442 2011-03-10 03:13:56Z adrian $ */ #ifndef __AH_REGDOMAIN_FREQBANDS_H__ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_regdomain/ah_rd_regenum.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_regdomain/ah_rd_regenum.h index bc569cb00a..44d83e1c38 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_regdomain/ah_rd_regenum.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_regdomain/ah_rd_regenum.h @@ -15,7 +15,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ah_regdomain/ah_rd_regenum.h 262375 2014-02-23 18:07:17Z hiren $ */ #ifndef __AH_REGDOMAIN_REGENUM_H__ @@ -51,6 +51,7 @@ enum { FCC2_FCCA = 0x20, /* Canada */ FCC2_WORLD = 0x21, /* Australia & HK */ FCC2_ETSIC = 0x22, + FCC_UBNT = 0x2A, /* Ubiquity PicoStation M2HP */ FRANCE_RES = 0x31, /* Legacy France for OEM */ FCC3_FCCA = 0x3A, /* USA & Canada w/5470 band, 11h, DFS enabled */ FCC3_WORLD = 0x3B, /* USA & Canada w/5470 band, 11h, DFS enabled */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_regdomain/ah_rd_regmap.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_regdomain/ah_rd_regmap.h index b1b8edddd3..7f5fc845cb 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_regdomain/ah_rd_regmap.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_regdomain/ah_rd_regmap.h @@ -15,7 +15,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ah_regdomain/ah_rd_regmap.h 248677 2013-03-24 04:42:56Z adrian $ */ #ifndef __AH_REGDOMAIN_REGMAP_H__ @@ -129,6 +129,7 @@ static REG_DMN_PAIR_MAPPING regDomainPairs[] = { {WOR9_WORLD, WOR9_WORLD, WOR9_WORLD, DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, NO_REQ, PSCAN_DEFER, CTRY_DEFAULT }, {WORA_WORLD, WORA_WORLD, WORA_WORLD, DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, NO_REQ, PSCAN_DEFER, CTRY_DEFAULT }, {WORB_WORLD, WORB_WORLD, WORB_WORLD, DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, NO_REQ, PSCAN_DEFER, CTRY_DEFAULT }, + {WORC_WORLD, WORC_WORLD, WORC_WORLD, NO_REQ, NO_REQ, PSCAN_DEFER, CTRY_DEFAULT }, }; #endif diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_soc.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_soc.h index ca75ae9a61..b575d5e521 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_soc.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ah_soc.h @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ah_soc.h 204644 2010-03-03 17:32:32Z rpaulo $ */ #ifndef _ATH_AH_SOC_H_ #define _ATH_AH_SOC_H_ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210.h index 2471853156..f0a3c205ef 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210.h @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5210/ar5210.h 290612 2015-11-09 15:59:42Z adrian $ */ #ifndef _ATH_AR5210_H_ #define _ATH_AR5210_H_ @@ -108,7 +108,6 @@ struct ath_hal_5210 { uint32_t ah_txDescInterruptMask; uint32_t ah_txEolInterruptMask; uint32_t ah_txUrnInterruptMask; - HAL_POWER_MODE ah_powerMode; uint8_t ah_bssid[IEEE80211_ADDR_LEN]; HAL_TX_QUEUE_INFO ah_txq[HAL_NUM_TX_QUEUES]; /* beacon+cab+data */ /* @@ -121,6 +120,8 @@ struct ath_hal_5210 { u_int ah_slottime; /* user-specified slot time */ u_int ah_acktimeout; /* user-specified ack timeout */ u_int ah_ctstimeout; /* user-specified cts timeout */ + + uint16_t ah_associd; /* association id */ }; #define AH5210(ah) ((struct ath_hal_5210 *)(ah)) @@ -128,7 +129,8 @@ struct ath_hal; extern void ar5210Detach(struct ath_hal *ah); extern HAL_BOOL ar5210Reset(struct ath_hal *, HAL_OPMODE, - struct ieee80211_channel *, HAL_BOOL bChannelChange, HAL_STATUS *); + struct ieee80211_channel *, HAL_BOOL bChannelChange, + HAL_RESET_TYPE, HAL_STATUS *); extern void ar5210SetPCUConfig(struct ath_hal *); extern HAL_BOOL ar5210PhyDisable(struct ath_hal *); extern HAL_BOOL ar5210Disable(struct ath_hal *); @@ -171,7 +173,8 @@ extern HAL_BOOL ar5210SetupXTxDesc(struct ath_hal *, struct ath_desc *, u_int txRate2, u_int txRetries2, u_int txRate3, u_int txRetries3); extern HAL_BOOL ar5210FillTxDesc(struct ath_hal *, struct ath_desc *, - u_int segLen, HAL_BOOL firstSeg, HAL_BOOL lastSeg, + HAL_DMA_ADDR *bufAddrList, uint32_t *segLenList, + u_int descId, u_int qcuId, HAL_BOOL firstSeg, HAL_BOOL lastSeg, const struct ath_desc *ds0); extern HAL_STATUS ar5210ProcTxDesc(struct ath_hal *, struct ath_desc *, struct ath_tx_status *); @@ -179,9 +182,15 @@ extern void ar5210GetTxIntrQueue(struct ath_hal *ah, uint32_t *); extern void ar5210IntrReqTxDesc(struct ath_hal *ah, struct ath_desc *); extern HAL_BOOL ar5210GetTxCompletionRates(struct ath_hal *ah, const struct ath_desc *, int *rates, int *tries); +extern void ar5210SetTxDescLink(struct ath_hal *ah, void *ds, + uint32_t link); +extern void ar5210GetTxDescLink(struct ath_hal *ah, void *ds, + uint32_t *link); +extern void ar5210GetTxDescLinkPtr(struct ath_hal *ah, void *ds, + uint32_t **linkptr); -extern uint32_t ar5210GetRxDP(struct ath_hal *); -extern void ar5210SetRxDP(struct ath_hal *, uint32_t rxdp); +extern uint32_t ar5210GetRxDP(struct ath_hal *, HAL_RX_QUEUE); +extern void ar5210SetRxDP(struct ath_hal *, uint32_t rxdp, HAL_RX_QUEUE); extern void ar5210EnableReceive(struct ath_hal *); extern HAL_BOOL ar5210StopDmaReceive(struct ath_hal *); extern void ar5210StartPcuReceive(struct ath_hal *); @@ -249,6 +258,13 @@ extern HAL_BOOL ar5210SetCapability(struct ath_hal *, HAL_CAPABILITY_TYPE, extern HAL_BOOL ar5210GetDiagState(struct ath_hal *ah, int request, const void *args, uint32_t argsize, void **result, uint32_t *resultsize); +extern uint32_t ar5210Get11nExtBusy(struct ath_hal *); +extern HAL_BOOL ar5210GetMibCycleCounts(struct ath_hal *, + HAL_SURVEY_SAMPLE *); +extern void ar5210SetChainMasks(struct ath_hal *, uint32_t, uint32_t); +extern void ar5210EnableDfs(struct ath_hal *, HAL_PHYERR_PARAM *); +extern void ar5210GetDfsThresh(struct ath_hal *, HAL_PHYERR_PARAM *); +extern void ar5210UpdateDiagReg(struct ath_hal *ah, uint32_t val); extern u_int ar5210GetKeyCacheSize(struct ath_hal *); extern HAL_BOOL ar5210IsKeyCacheEntryValid(struct ath_hal *, uint16_t); diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_attach.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_attach.c index 79c305511e..9320ebe54e 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_attach.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_attach.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5210/ar5210_attach.c 272292 2014-09-30 03:19:29Z adrian $ */ #include "opt_ah.h" @@ -33,7 +33,8 @@ static HAL_BOOL ar5210GetChannelEdges(struct ath_hal *, static HAL_BOOL ar5210GetChipPowerLimits(struct ath_hal *ah, struct ieee80211_channel *chan); -static void ar5210ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore); +static void ar5210ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore, + HAL_BOOL power_on); static void ar5210DisablePCIE(struct ath_hal *ah); static const struct ath_hal_private ar5210hal = {{ @@ -74,6 +75,9 @@ static const struct ath_hal_private ar5210hal = {{ .ah_getTxIntrQueue = ar5210GetTxIntrQueue, .ah_reqTxIntrDesc = ar5210IntrReqTxDesc, .ah_getTxCompletionRates = ar5210GetTxCompletionRates, + .ah_setTxDescLink = ar5210SetTxDescLink, + .ah_getTxDescLink = ar5210GetTxDescLink, + .ah_getTxDescLinkPtr = ar5210GetTxDescLinkPtr, /* RX Functions */ .ah_getRxDP = ar5210GetRxDP, @@ -129,8 +133,15 @@ static const struct ath_hal_private ar5210hal = {{ .ah_getAckCTSRate = ar5210GetAckCTSRate, .ah_setCTSTimeout = ar5210SetCTSTimeout, .ah_getCTSTimeout = ar5210GetCTSTimeout, - .ah_setDecompMask = ar5210SetDecompMask, - .ah_setCoverageClass = ar5210SetCoverageClass, + .ah_setDecompMask = ar5210SetDecompMask, + .ah_setCoverageClass = ar5210SetCoverageClass, + .ah_get11nExtBusy = ar5210Get11nExtBusy, + .ah_getMibCycleCounts = ar5210GetMibCycleCounts, + .ah_setChainMasks = ar5210SetChainMasks, + .ah_enableDfs = ar5210EnableDfs, + .ah_getDfsThresh = ar5210GetDfsThresh, + /* XXX procRadarEvent */ + /* XXX isFastClockEnabled */ /* Key Cache Functions */ .ah_getKeyCacheSize = ar5210GetKeyCacheSize, @@ -172,7 +183,7 @@ static HAL_BOOL ar5210FillCapabilityInfo(struct ath_hal *ah); */ static struct ath_hal * ar5210Attach(uint16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, - uint16_t *eepromdata, HAL_STATUS *status) + uint16_t *eepromdata, HAL_OPS_CONFIG *ah_config, HAL_STATUS *status) { #define N(a) (sizeof(a)/sizeof(a[0])) struct ath_hal_5210 *ahp; @@ -182,14 +193,14 @@ ar5210Attach(uint16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, HAL_STATUS ecode; int i; - HALDEBUG_G(AH_NULL, HAL_DEBUG_ATTACH, + HALDEBUG(AH_NULL, HAL_DEBUG_ATTACH, "%s: devid 0x%x sc %p st %p sh %p\n", __func__, devid, sc, (void*) st, (void*) sh); /* NB: memory is returned zero'd */ ahp = ath_hal_malloc(sizeof (struct ath_hal_5210)); if (ahp == AH_NULL) { - HALDEBUG_G(AH_NULL, HAL_DEBUG_ANY, + HALDEBUG(AH_NULL, HAL_DEBUG_ANY, "%s: no memory for state block\n", __func__); ecode = HAL_ENOMEM; goto bad; @@ -208,7 +219,7 @@ ar5210Attach(uint16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, AH_PRIVATE(ah)->ah_powerLimit = AR5210_MAX_RATE_POWER; AH_PRIVATE(ah)->ah_tpScale = HAL_TP_SCALE_MAX; /* no scaling */ - ahp->ah_powerMode = HAL_PM_UNDEFINED; + ah->ah_powerMode = HAL_PM_UNDEFINED; ahp->ah_staId1Defaults = 0; ahp->ah_rssiThr = INIT_RSSI_THR; ahp->ah_sifstime = (u_int) -1; @@ -326,7 +337,7 @@ ar5210GetChipPowerLimits(struct ath_hal *ah, struct ieee80211_channel *chan) } static void -ar5210ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore) +ar5210ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore, HAL_BOOL power_off) { } @@ -351,6 +362,8 @@ ar5210FillCapabilityInfo(struct ath_hal *ah) pCap->halSleepAfterBeaconBroken = AH_TRUE; pCap->halPSPollBroken = AH_FALSE; + pCap->halNumMRRetries = 1; /* No hardware MRR support */ + pCap->halNumTxMaps = 1; /* Single TX ptr per descr */ pCap->halTotalQueues = HAL_NUM_TX_QUEUES; pCap->halKeyCacheSize = 64; @@ -359,6 +372,12 @@ ar5210FillCapabilityInfo(struct ath_hal *ah) pCap->halChanHalfRate = AH_FALSE; pCap->halChanQuarterRate = AH_FALSE; + /* + * RSSI uses the combined field; some 11n NICs may use + * the control chain RSSI. + */ + pCap->halUseCombinedRadarRssi = AH_TRUE; + if (ath_hal_eepromGetFlag(ah, AR_EEP_RFKILL)) { /* * Setup initial rfsilent settings based on the EEPROM diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_beacon.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_beacon.c index e12b039723..333e0c1adb 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_beacon.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_beacon.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5210/ar5210_beacon.c 225444 2011-09-08 01:23:05Z adrian $ */ #include "opt_ah.h" diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_interrupts.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_interrupts.c index 96b1a2cb19..18ca48eaa6 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_interrupts.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_interrupts.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5210/ar5210_interrupts.c 192397 2009-05-19 17:35:15Z sam $ */ #include "opt_ah.h" diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_keycache.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_keycache.c index 87fb06706d..a8354be149 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_keycache.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_keycache.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5210/ar5210_keycache.c 204644 2010-03-03 17:32:32Z rpaulo $ */ #include "opt_ah.h" diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_misc.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_misc.c index 1c1573c4df..d4c3762e0b 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_misc.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_misc.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5210/ar5210_misc.c 264899 2014-04-24 23:11:18Z adrian $ */ #include "opt_ah.h" @@ -315,6 +315,7 @@ ar5210WriteAssocid(struct ath_hal *ah, const uint8_t *bssid, uint16_t assocId) /* XXX save bssid for possible re-use on reset */ OS_MEMCPY(ahp->ah_bssid, bssid, IEEE80211_ADDR_LEN); + ahp->ah_associd = assocId; OS_REG_WRITE(ah, AR_BSS_ID0, LE_READ_4(ahp->ah_bssid)); OS_REG_WRITE(ah, AR_BSS_ID1, LE_READ_2(ahp->ah_bssid+4) | ((assocId & 0x3fff)<ah_diagreg = setting & 0x6; /* ACK+CTS */ #endif - OS_REG_WRITE(ah, AR_DIAG_SW, AH_PRIVATE(ah)->ah_diagreg); + ar5210UpdateDiagReg(ah, AH_PRIVATE(ah)->ah_diagreg); return AH_TRUE; case HAL_CAP_RXORN_FATAL: /* HAL_INT_RXORN treated as fatal */ return AH_FALSE; /* NB: disallow */ @@ -646,3 +649,55 @@ ar5210GetDiagState(struct ath_hal *ah, int request, return ath_hal_getdiagstate(ah, request, args, argsize, result, resultsize); } + +/* + * Return what percentage of the extension channel is busy. + * This is always disabled for AR5210 series NICs. + */ +uint32_t +ar5210Get11nExtBusy(struct ath_hal *ah) +{ + + return (0); +} + +/* + * There's no channel survey support for the AR5210. + */ +HAL_BOOL +ar5210GetMibCycleCounts(struct ath_hal *ah, HAL_SURVEY_SAMPLE *hsample) +{ + + return (AH_FALSE); +} + +void +ar5210SetChainMasks(struct ath_hal *ah, uint32_t txchainmask, + uint32_t rxchainmask) +{ +} + +void +ar5210EnableDfs(struct ath_hal *ah, HAL_PHYERR_PARAM *pe) +{ +} + +void +ar5210GetDfsThresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe) +{ +} + +/* + * Update the diagnostic register. + * + * This merges in the diagnostic register setting with the default + * value, which may or may not involve disabling hardware encryption. + */ +void +ar5210UpdateDiagReg(struct ath_hal *ah, uint32_t val) +{ + + /* Disable all hardware encryption */ + val |= AR_DIAG_SW_DIS_CRYPTO; + OS_REG_WRITE(ah, AR_DIAG_SW, val); +} diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_phy.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_phy.c index 0455d652d7..c9a37ffd1a 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_phy.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_phy.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5210/ar5210_phy.c 191022 2009-04-13 21:01:08Z sam $ */ #include "opt_ah.h" diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_power.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_power.c index 1c6909b559..767c2d878b 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_power.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_power.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5210/ar5210_power.c 265112 2014-04-30 02:03:13Z adrian $ */ #include "opt_ah.h" @@ -93,7 +93,6 @@ ar5210SetPowerModeSleep(struct ath_hal *ah, int setChip) HAL_BOOL ar5210SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip) { - struct ath_hal_5210 *ahp = AH5210(ah); #ifdef AH_DEBUG static const char* modes[] = { "AWAKE", @@ -105,25 +104,30 @@ ar5210SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip) int status = AH_TRUE; HALDEBUG(ah, HAL_DEBUG_POWER, "%s: %s -> %s (%s)\n", __func__, - modes[ahp->ah_powerMode], modes[mode], + modes[ah->ah_powerMode], modes[mode], setChip ? "set chip " : ""); switch (mode) { case HAL_PM_AWAKE: + if (setChip) + ah->ah_powerMode = mode; status = ar5210SetPowerModeAwake(ah, setChip); break; case HAL_PM_FULL_SLEEP: ar5210SetPowerModeSleep(ah, setChip); + if (setChip) + ah->ah_powerMode = mode; break; case HAL_PM_NETWORK_SLEEP: ar5210SetPowerModeAuto(ah, setChip); + if (setChip) + ah->ah_powerMode = mode; break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unknown power mode %u\n", __func__, mode); return AH_FALSE; } - ahp->ah_powerMode = mode; - return status; + return status; } HAL_POWER_MODE diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_recv.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_recv.c index bdb4d57cad..03654aafec 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_recv.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_recv.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5210/ar5210_recv.c 243317 2012-11-19 23:42:46Z adrian $ */ #include "opt_ah.h" @@ -30,8 +30,10 @@ * Get the RXDP. */ uint32_t -ar5210GetRxDP(struct ath_hal *ah) +ar5210GetRxDP(struct ath_hal *ah, HAL_RX_QUEUE qtype) { + + HALASSERT(qtype == HAL_RX_QUEUE_HP); return OS_REG_READ(ah, AR_RXDP); } @@ -39,8 +41,10 @@ ar5210GetRxDP(struct ath_hal *ah) * Set the RxDP. */ void -ar5210SetRxDP(struct ath_hal *ah, uint32_t rxdp) +ar5210SetRxDP(struct ath_hal *ah, uint32_t rxdp, HAL_RX_QUEUE qtype) { + + HALASSERT(qtype == HAL_RX_QUEUE_HP); OS_REG_WRITE(ah, AR_RXDP, rxdp); } @@ -82,7 +86,7 @@ ar5210StopDmaReceive(struct ath_hal *ah) void ar5210StartPcuReceive(struct ath_hal *ah) { - OS_REG_WRITE(ah, AR_DIAG_SW, + ar5210UpdateDiagReg(ah, OS_REG_READ(ah, AR_DIAG_SW) & ~(AR_DIAG_SW_DIS_RX)); } @@ -92,7 +96,7 @@ ar5210StartPcuReceive(struct ath_hal *ah) void ar5210StopPcuReceive(struct ath_hal *ah) { - OS_REG_WRITE(ah, AR_DIAG_SW, + ar5210UpdateDiagReg(ah, OS_REG_READ(ah, AR_DIAG_SW) | AR_DIAG_SW_DIS_RX); } diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_reset.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_reset.c index a798f84c98..04f3f7fa7c 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_reset.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_reset.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5210/ar5210_reset.c 290612 2015-11-09 15:59:42Z adrian $ */ #include "opt_ah.h" @@ -69,6 +69,7 @@ static void ar5210SetOperatingMode(struct ath_hal *, int opmode); HAL_BOOL ar5210Reset(struct ath_hal *ah, HAL_OPMODE opmode, struct ieee80211_channel *chan, HAL_BOOL bChannelChange, + HAL_RESET_TYPE resetType, HAL_STATUS *status) { #define N(a) (sizeof (a) /sizeof (a[0])) @@ -152,8 +153,12 @@ ar5210Reset(struct ath_hal *ah, HAL_OPMODE opmode, /* Restore previous led state */ OS_REG_WRITE(ah, AR_PCICFG, OS_REG_READ(ah, AR_PCICFG) | ledstate); +#if 0 OS_REG_WRITE(ah, AR_BSS_ID0, LE_READ_4(ahp->ah_bssid)); OS_REG_WRITE(ah, AR_BSS_ID1, LE_READ_2(ahp->ah_bssid + 4)); +#endif + /* BSSID, association id, ps-poll */ + ar5210WriteAssocid(ah, ahp->ah_bssid, ahp->ah_associd); OS_REG_WRITE(ah, AR_TXDP0, 0); OS_REG_WRITE(ah, AR_TXDP1, 0); @@ -190,7 +195,7 @@ ar5210Reset(struct ath_hal *ah, HAL_OPMODE opmode, OS_REG_WRITE(ah, AR_CLR_TMASK, 1); OS_REG_WRITE(ah, AR_TRIG_LEV, 1); /* minimum */ - OS_REG_WRITE(ah, AR_DIAG_SW, 0); + ar5210UpdateDiagReg(ah, 0); OS_REG_WRITE(ah, AR_CFP_PERIOD, 0); OS_REG_WRITE(ah, AR_TIMER0, 0); /* next beacon time */ @@ -285,7 +290,7 @@ ar5210Reset(struct ath_hal *ah, HAL_OPMODE opmode, if (ahp->ah_ctstimeout != (u_int) -1) ar5210SetCTSTimeout(ah, ahp->ah_ctstimeout); if (AH_PRIVATE(ah)->ah_diagreg != 0) - OS_REG_WRITE(ah, AR_DIAG_SW, AH_PRIVATE(ah)->ah_diagreg); + ar5210UpdateDiagReg(ah, AH_PRIVATE(ah)->ah_diagreg); AH_PRIVATE(ah)->ah_opmode = opmode; /* record operating mode */ @@ -454,7 +459,7 @@ ar5210PerCalibrationN(struct ath_hal *ah, if (ichan == AH_NULL) return AH_FALSE; /* Disable tx and rx */ - OS_REG_WRITE(ah, AR_DIAG_SW, + ar5210UpdateDiagReg(ah, OS_REG_READ(ah, AR_DIAG_SW) | (AR_DIAG_SW_DIS_TX | AR_DIAG_SW_DIS_RX)); /* Disable Beacon Enable */ @@ -551,7 +556,7 @@ ar5210PerCalibrationN(struct ath_hal *ah, } /* Clear tx and rx disable bit */ - OS_REG_WRITE(ah, AR_DIAG_SW, + ar5210UpdateDiagReg(ah, OS_REG_READ(ah, AR_DIAG_SW) & ~(AR_DIAG_SW_DIS_TX | AR_DIAG_SW_DIS_RX)); /* Re-enable Beacons */ @@ -594,12 +599,10 @@ ar5210SetResetReg(struct ath_hal *ah, uint32_t resetMask, u_int delay) if ((resetMask & AR_RC_RMAC) == 0) { if (isBigEndian()) { /* - * Set CFG, little-endian for register - * and descriptor accesses. + * Set CFG, little-endian for descriptor accesses. */ - mask = INIT_CONFIG_STATUS | - AR_CFG_SWTD | AR_CFG_SWRD | AR_CFG_SWRG; - OS_REG_WRITE(ah, AR_CFG, LE_READ_4(&mask)); + mask = INIT_CONFIG_STATUS | AR_CFG_SWTD | AR_CFG_SWRD; + OS_REG_WRITE(ah, AR_CFG, mask); } else OS_REG_WRITE(ah, AR_CFG, INIT_CONFIG_STATUS); } diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_xmit.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_xmit.c index f7c6030681..5b402c942d 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_xmit.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210_xmit.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5210/ar5210_xmit.c 243174 2012-11-17 02:39:37Z adrian $ */ #include "opt_ah.h" @@ -518,7 +518,8 @@ ar5210SetupTxDesc(struct ath_hal *ah, struct ath_desc *ds, ads->ds_ctl1 = 0; if (flags & HAL_TXDESC_RTSENA) { ads->ds_ctl0 |= AR_RTSCTSEnable; - ads->ds_ctl1 |= rtsctsDuration & AR_RTSDuration; + ads->ds_ctl1 |= (rtsctsDuration << AR_RTSDuration_S) + & AR_RTSDuration; } return AH_TRUE; } @@ -546,13 +547,17 @@ ar5210IntrReqTxDesc(struct ath_hal *ah, struct ath_desc *ds) HAL_BOOL ar5210FillTxDesc(struct ath_hal *ah, struct ath_desc *ds, - u_int segLen, HAL_BOOL firstSeg, HAL_BOOL lastSeg, + HAL_DMA_ADDR *bufAddrList, uint32_t *segLenList, u_int descId, + u_int qcuId, HAL_BOOL firstSeg, HAL_BOOL lastSeg, const struct ath_desc *ds0) { struct ar5210_desc *ads = AR5210DESC(ds); + uint32_t segLen = segLenList[0]; HALASSERT((segLen &~ AR_BufLen) == 0); + ds->ds_data = bufAddrList[0]; + if (firstSeg) { /* * First descriptor, don't clobber xmit control data @@ -630,3 +635,36 @@ ar5210GetTxCompletionRates(struct ath_hal *ah, const struct ath_desc *ds0, int * { return AH_FALSE; } + +/* + * Set the TX descriptor link pointer + */ +void +ar5210SetTxDescLink(struct ath_hal *ah, void *ds, uint32_t link) +{ + struct ar5210_desc *ads = AR5210DESC(ds); + + ads->ds_link = link; +} + +/* + * Get the TX descriptor link pointer + */ +void +ar5210GetTxDescLink(struct ath_hal *ah, void *ds, uint32_t *link) +{ + struct ar5210_desc *ads = AR5210DESC(ds); + + *link = ads->ds_link; +} + +/* + * Get a pointer to the TX descriptor link pointer + */ +void +ar5210GetTxDescLinkPtr(struct ath_hal *ah, void *ds, uint32_t **linkptr) +{ + struct ar5210_desc *ads = AR5210DESC(ds); + + *linkptr = &ads->ds_link; +} diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210desc.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210desc.h index 5634480b80..ee1a51dbb5 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210desc.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210desc.h @@ -14,13 +14,11 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5210/ar5210desc.h 243173 2012-11-17 02:39:09Z adrian $ */ #ifndef _DEV_ATH_AR5210DESC_H #define _DEV_ATH_AR5210DESC_H -#include "ah_desc.h" - /* * Defintions for the DMA descriptors used by the Atheros * AR5210/AR5211 and AR5110 Wireless Lan controller parts. @@ -57,6 +55,7 @@ struct ar5210_desc { #define AR_ClearDestMask 0x01000000 /* Clear destination mask bit */ #define AR_AntModeXmit 0x02000000 /* TX antenna seslection */ #define AR_FrmType 0x1c000000 /* frame type indication */ +#define AR_FrmType_S 26 #define AR_Frm_Normal 0x00000000 /* normal frame */ #define AR_Frm_ATIM 0x04000000 /* ATIM frame */ #define AR_Frm_PSPOLL 0x08000000 /* PS poll frame */ @@ -71,6 +70,7 @@ struct ar5210_desc { #define AR_EncryptKeyIdx 0x0007e000 /* ecnrypt key table index */ #define AR_EncryptKeyIdx_S 13 #define AR_RTSDuration 0xfff80000 /* lower 13bit of duration */ +#define AR_RTSDuration_S 19 /* RX ds_ctl1 */ /* AR_BufLen 0x00000fff data buffer length */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210phy.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210phy.h index 4ab3025d95..f9ccd28c66 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210phy.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210phy.h @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5210/ar5210phy.h 204644 2010-03-03 17:32:32Z rpaulo $ */ #ifndef _DEV_ATH_AR5210PHY_H #define _DEV_ATH_AR5210PHY_H diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210reg.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210reg.h index 738210743f..aaf9c69749 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210reg.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5210reg.h @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5210/ar5210reg.h 243317 2012-11-19 23:42:46Z adrian $ */ #ifndef _DEV_ATH_AR5210REG_H #define _DEV_ATH_AR5210REG_H @@ -385,6 +385,7 @@ #define AR_DIAG_SW_SCVRAM_SEED 0x0003f800 /* fixed scrambler seed */ #define AR_DIAG_SW_DIS_SEQ_INC 0x00040000 /* seq increment disable */ #define AR_DIAG_SW_FRAME_NV0 0x00080000 /* accept frame vers != 0 */ +#define AR_DIAG_SW_DIS_CRYPTO (AR_DIAG_SW_DIS_ENC | AR_DIAG_SW_DIS_DEC) #define AR_DIAG_SW_BITS \ "\20\1DIS_WEP_ACK\2DIS_ACK\3DIS_CTS\4DIS_ENC\5DIS_DEC\6DIS_TX"\ "\7DIS_RX\10LOOP_BACK\11CORR_FCS\12CHAN_INFO\13EN_SCRAM_SEED"\ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5k_0007.ini b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5k_0007.ini index b536483b2c..876daf862c 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5k_0007.ini +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5210/ar5k_0007.ini @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5210/ar5k_0007.ini 204644 2010-03-03 17:32:32Z rpaulo $ */ /* crete register init */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211.h index 51acd38862..1166d0c486 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211.h @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5211/ar5211.h 290612 2015-11-09 15:59:42Z adrian $ */ #ifndef _ATH_AR5211_H_ #define _ATH_AR5211_H_ @@ -119,7 +119,6 @@ struct ath_hal_5211 { uint32_t ah_txEolInterruptMask; uint32_t ah_txUrnInterruptMask; HAL_TX_QUEUE_INFO ah_txq[HAL_NUM_TX_QUEUES]; - HAL_POWER_MODE ah_powerMode; HAL_ANT_SETTING ah_diversityControl; /* antenna setting */ uint32_t ah_calibrationTime; HAL_BOOL ah_bIQCalibration; @@ -148,6 +147,7 @@ extern void ar5211Detach(struct ath_hal *); extern HAL_BOOL ar5211Reset(struct ath_hal *, HAL_OPMODE, struct ieee80211_channel *, HAL_BOOL bChannelChange, + HAL_RESET_TYPE, HAL_STATUS *); extern HAL_BOOL ar5211PhyDisable(struct ath_hal *); extern HAL_BOOL ar5211Disable(struct ath_hal *); @@ -196,7 +196,8 @@ extern HAL_BOOL ar5211SetupXTxDesc(struct ath_hal *, struct ath_desc *, u_int txRate2, u_int txRetries2, u_int txRate3, u_int txRetries3); extern HAL_BOOL ar5211FillTxDesc(struct ath_hal *, struct ath_desc *, - u_int segLen, HAL_BOOL firstSeg, HAL_BOOL lastSeg, + HAL_DMA_ADDR *bufAddrList, uint32_t *segLenList, + u_int descId, u_int qcuId, HAL_BOOL firstSeg, HAL_BOOL lastSeg, const struct ath_desc *ds0); extern HAL_STATUS ar5211ProcTxDesc(struct ath_hal *, struct ath_desc *, struct ath_tx_status *); @@ -204,9 +205,15 @@ extern void ar5211GetTxIntrQueue(struct ath_hal *ah, uint32_t *); extern void ar5211IntrReqTxDesc(struct ath_hal *ah, struct ath_desc *); extern HAL_BOOL ar5211GetTxCompletionRates(struct ath_hal *ah, const struct ath_desc *ds0, int *rates, int *tries); +extern void ar5211SetTxDescLink(struct ath_hal *ah, void *ds, + uint32_t link); +extern void ar5211GetTxDescLink(struct ath_hal *ah, void *ds, + uint32_t *link); +extern void ar5211GetTxDescLinkPtr(struct ath_hal *ah, void *ds, + uint32_t **linkptr); -extern uint32_t ar5211GetRxDP(struct ath_hal *); -extern void ar5211SetRxDP(struct ath_hal *, uint32_t rxdp); +extern uint32_t ar5211GetRxDP(struct ath_hal *, HAL_RX_QUEUE); +extern void ar5211SetRxDP(struct ath_hal *, uint32_t rxdp, HAL_RX_QUEUE); extern void ar5211EnableReceive(struct ath_hal *); extern HAL_BOOL ar5211StopDmaReceive(struct ath_hal *); extern void ar5211StartPcuReceive(struct ath_hal *); @@ -276,6 +283,13 @@ extern HAL_BOOL ar5211SetCapability(struct ath_hal *, HAL_CAPABILITY_TYPE, extern HAL_BOOL ar5211GetDiagState(struct ath_hal *ah, int request, const void *args, uint32_t argsize, void **result, uint32_t *resultsize); +extern uint32_t ar5211Get11nExtBusy(struct ath_hal *); +extern HAL_BOOL ar5211GetMibCycleCounts(struct ath_hal *, + HAL_SURVEY_SAMPLE *); +extern void ar5211SetChainMasks(struct ath_hal *ah, uint32_t, uint32_t); + +extern void ar5211EnableDfs(struct ath_hal *, HAL_PHYERR_PARAM *); +extern void ar5211GetDfsThresh(struct ath_hal *, HAL_PHYERR_PARAM *); extern u_int ar5211GetKeyCacheSize(struct ath_hal *); extern HAL_BOOL ar5211IsKeyCacheEntryValid(struct ath_hal *, uint16_t); diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_attach.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_attach.c index a0c42b2bf7..4bde425da9 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_attach.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_attach.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5211/ar5211_attach.c 272292 2014-09-30 03:19:29Z adrian $ */ #include "opt_ah.h" @@ -33,7 +33,8 @@ static HAL_BOOL ar5211GetChannelEdges(struct ath_hal *ah, static HAL_BOOL ar5211GetChipPowerLimits(struct ath_hal *ah, struct ieee80211_channel *chan); -static void ar5211ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore); +static void ar5211ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore, + HAL_BOOL power_off); static void ar5211DisablePCIE(struct ath_hal *ah); static const struct ath_hal_private ar5211hal = {{ @@ -74,6 +75,9 @@ static const struct ath_hal_private ar5211hal = {{ .ah_getTxIntrQueue = ar5211GetTxIntrQueue, .ah_reqTxIntrDesc = ar5211IntrReqTxDesc, .ah_getTxCompletionRates = ar5211GetTxCompletionRates, + .ah_setTxDescLink = ar5211SetTxDescLink, + .ah_getTxDescLink = ar5211GetTxDescLink, + .ah_getTxDescLinkPtr = ar5211GetTxDescLinkPtr, /* RX Functions */ .ah_getRxDP = ar5211GetRxDP, @@ -129,8 +133,15 @@ static const struct ath_hal_private ar5211hal = {{ .ah_getAckCTSRate = ar5211GetAckCTSRate, .ah_setCTSTimeout = ar5211SetCTSTimeout, .ah_getCTSTimeout = ar5211GetCTSTimeout, - .ah_setDecompMask = ar5211SetDecompMask, - .ah_setCoverageClass = ar5211SetCoverageClass, + .ah_setDecompMask = ar5211SetDecompMask, + .ah_setCoverageClass = ar5211SetCoverageClass, + .ah_get11nExtBusy = ar5211Get11nExtBusy, + .ah_getMibCycleCounts = ar5211GetMibCycleCounts, + .ah_setChainMasks = ar5211SetChainMasks, + .ah_enableDfs = ar5211EnableDfs, + .ah_getDfsThresh = ar5211GetDfsThresh, + /* XXX procRadarEvent */ + /* XXX isFastClockEnabled */ /* Key Cache Functions */ .ah_getKeyCacheSize = ar5211GetKeyCacheSize, @@ -192,7 +203,7 @@ ar5211GetRadioRev(struct ath_hal *ah) static struct ath_hal * ar5211Attach(uint16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, - HAL_STATUS *status) + HAL_OPS_CONFIG *ah_config, HAL_STATUS *status) { #define N(a) (sizeof(a)/sizeof(a[0])) struct ath_hal_5211 *ahp; @@ -201,13 +212,13 @@ ar5211Attach(uint16_t devid, HAL_SOFTC sc, uint16_t eeval; HAL_STATUS ecode; - HALDEBUG_G(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n", + HALDEBUG(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n", __func__, sc, (void*) st, (void*) sh); /* NB: memory is returned zero'd */ ahp = ath_hal_malloc(sizeof (struct ath_hal_5211)); if (ahp == AH_NULL) { - HALDEBUG_G(AH_NULL, HAL_DEBUG_ANY, + HALDEBUG(AH_NULL, HAL_DEBUG_ANY, "%s: cannot allocate memory for state block\n", __func__); ecode = HAL_ENOMEM; goto bad; @@ -449,7 +460,7 @@ ar5211GetChipPowerLimits(struct ath_hal *ah, struct ieee80211_channel *chan) } static void -ar5211ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore) +ar5211ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore, HAL_BOOL power_off) { } @@ -486,6 +497,8 @@ ar5211FillCapabilityInfo(struct ath_hal *ah) pCap->halSleepAfterBeaconBroken = AH_TRUE; pCap->halPSPollBroken = AH_TRUE; pCap->halVEOLSupport = AH_TRUE; + pCap->halNumMRRetries = 1; /* No hardware MRR support */ + pCap->halNumTxMaps = 1; /* Single TX ptr per descr */ pCap->halTotalQueues = HAL_NUM_TX_QUEUES; pCap->halKeyCacheSize = 128; @@ -494,6 +507,12 @@ ar5211FillCapabilityInfo(struct ath_hal *ah) pCap->halChanHalfRate = AH_FALSE; pCap->halChanQuarterRate = AH_FALSE; + /* + * RSSI uses the combined field; some 11n NICs may use + * the control chain RSSI. + */ + pCap->halUseCombinedRadarRssi = AH_TRUE; + if (ath_hal_eepromGetFlag(ah, AR_EEP_RFKILL) && ath_hal_eepromGet(ah, AR_EEP_RFSILENT, &ahpriv->ah_rfsilent) == HAL_OK) { /* NB: enabled by default */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_beacon.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_beacon.c index b2775d0376..78774047b3 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_beacon.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_beacon.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5211/ar5211_beacon.c 225444 2011-09-08 01:23:05Z adrian $ */ #include "opt_ah.h" diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_interrupts.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_interrupts.c index 4c20ca1b5a..8b4d69a001 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_interrupts.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_interrupts.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5211/ar5211_interrupts.c 192397 2009-05-19 17:35:15Z sam $ */ #include "opt_ah.h" diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_keycache.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_keycache.c index 3e0f922a4f..34f21a91e2 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_keycache.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_keycache.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5211/ar5211_keycache.c 204644 2010-03-03 17:32:32Z rpaulo $ */ #include "opt_ah.h" diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_misc.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_misc.c index 3b27062294..f6f36a8ef3 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_misc.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_misc.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5211/ar5211_misc.c 247286 2013-02-25 22:42:43Z adrian $ */ #include "opt_ah.h" @@ -688,3 +688,40 @@ ar5211GetDiagState(struct ath_hal *ah, int request, } return AH_FALSE; } + +/* + * Return what percentage of the extension channel is busy. + * This is always disabled for AR5211 series NICs. + */ +uint32_t +ar5211Get11nExtBusy(struct ath_hal *ah) +{ + return (0); +} + + +/* + * There's no channel survey support for the AR5211. + */ +HAL_BOOL +ar5211GetMibCycleCounts(struct ath_hal *ah, HAL_SURVEY_SAMPLE *hsample) +{ + + return (AH_FALSE); +} + +void +ar5211SetChainMasks(struct ath_hal *ah, uint32_t txchainmask, + uint32_t rxchainmask) +{ +} + +void +ar5211EnableDfs(struct ath_hal *ah, HAL_PHYERR_PARAM *pe) +{ +} + +void +ar5211GetDfsThresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe) +{ +} diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_phy.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_phy.c index d224732c08..8b77c4ed80 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_phy.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_phy.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5211/ar5211_phy.c 191022 2009-04-13 21:01:08Z sam $ */ #include "opt_ah.h" diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_power.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_power.c index 776cfb32b5..c34e842e7c 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_power.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_power.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5211/ar5211_power.c 265112 2014-04-30 02:03:13Z adrian $ */ #include "opt_ah.h" @@ -95,7 +95,6 @@ ar5211SetPowerModeNetworkSleep(struct ath_hal *ah, int setChip) HAL_BOOL ar5211SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip) { - struct ath_hal_5211 *ahp = AH5211(ah); #ifdef AH_DEBUG static const char* modes[] = { "AWAKE", @@ -107,25 +106,30 @@ ar5211SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip) int status = AH_TRUE; HALDEBUG(ah, HAL_DEBUG_POWER, "%s: %s -> %s (%s)\n", __func__, - modes[ahp->ah_powerMode], modes[mode], + modes[ah->ah_powerMode], modes[mode], setChip ? "set chip " : ""); switch (mode) { case HAL_PM_AWAKE: + if (setChip) + ah->ah_powerMode = mode; status = ar5211SetPowerModeAwake(ah, setChip); break; case HAL_PM_FULL_SLEEP: ar5211SetPowerModeSleep(ah, setChip); + if (setChip) + ah->ah_powerMode = mode; break; case HAL_PM_NETWORK_SLEEP: ar5211SetPowerModeNetworkSleep(ah, setChip); + if (setChip) + ah->ah_powerMode = mode; break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unknown power mode %u\n", __func__, mode); return AH_FALSE; } - ahp->ah_powerMode = mode; - return status; + return status; } HAL_POWER_MODE diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_recv.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_recv.c index 733559e7a5..7313dd0e26 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_recv.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_recv.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5211/ar5211_recv.c 238278 2012-07-09 07:19:11Z adrian $ */ #include "opt_ah.h" @@ -30,8 +30,10 @@ * Get the RXDP. */ uint32_t -ar5211GetRxDP(struct ath_hal *ah) +ar5211GetRxDP(struct ath_hal *ah, HAL_RX_QUEUE qtype) { + + HALASSERT(qtype == HAL_RX_QUEUE_HP); return OS_REG_READ(ah, AR_RXDP); } @@ -39,8 +41,10 @@ ar5211GetRxDP(struct ath_hal *ah) * Set the RxDP. */ void -ar5211SetRxDP(struct ath_hal *ah, uint32_t rxdp) +ar5211SetRxDP(struct ath_hal *ah, uint32_t rxdp, HAL_RX_QUEUE qtype) { + + HALASSERT(qtype == HAL_RX_QUEUE_HP); OS_REG_WRITE(ah, AR_RXDP, rxdp); HALASSERT(OS_REG_READ(ah, AR_RXDP) == rxdp); } diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_reset.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_reset.c index d2501811a1..883cc7e076 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_reset.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_reset.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5211/ar5211_reset.c 298939 2016-05-02 19:56:48Z pfg $ */ #include "opt_ah.h" @@ -154,6 +154,7 @@ static void ar5211SetOperatingMode(struct ath_hal *, int opmode); HAL_BOOL ar5211Reset(struct ath_hal *ah, HAL_OPMODE opmode, struct ieee80211_channel *chan, HAL_BOOL bChannelChange, + HAL_RESET_TYPE resetType, HAL_STATUS *status) { uint32_t softLedCfg, softLedState; @@ -764,12 +765,10 @@ ar5211SetResetReg(struct ath_hal *ah, uint32_t resetMask) if ((resetMask & AR_RC_MAC) == 0) { if (isBigEndian()) { /* - * Set CFG, little-endian for register - * and descriptor accesses. + * Set CFG, little-endian for descriptor accesses. */ - mask = INIT_CONFIG_STATUS | - AR_CFG_SWTD | AR_CFG_SWRD | AR_CFG_SWRG; - OS_REG_WRITE(ah, AR_CFG, LE_READ_4(&mask)); + mask = INIT_CONFIG_STATUS | AR_CFG_SWTD | AR_CFG_SWRD; + OS_REG_WRITE(ah, AR_CFG, mask); } else OS_REG_WRITE(ah, AR_CFG, INIT_CONFIG_STATUS); } @@ -914,7 +913,7 @@ getNoiseFloorThresh(struct ath_hal *ah, const struct ieee80211_channel *chan, } /* - * Read the NF and check it against the noise floor threshhold + * Read the NF and check it against the noise floor threshold * * Returns: TRUE if the NF is good */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_xmit.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_xmit.c index e1e7f73bf2..cf07df572e 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_xmit.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211_xmit.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5211/ar5211_xmit.c 239051 2012-08-05 10:12:27Z adrian $ */ #include "opt_ah.h" @@ -577,10 +577,14 @@ ar5211IntrReqTxDesc(struct ath_hal *ah, struct ath_desc *ds) HAL_BOOL ar5211FillTxDesc(struct ath_hal *ah, struct ath_desc *ds, - u_int segLen, HAL_BOOL firstSeg, HAL_BOOL lastSeg, + HAL_DMA_ADDR *bufAddrList, uint32_t *segLenList, u_int qcuId, + u_int descId, HAL_BOOL firstSeg, HAL_BOOL lastSeg, const struct ath_desc *ds0) { struct ar5211_desc *ads = AR5211DESC(ds); + uint32_t segLen = segLenList[0]; + + ds->ds_data = bufAddrList[0]; HALASSERT((segLen &~ AR_BufLen) == 0); @@ -671,3 +675,27 @@ ar5211GetTxCompletionRates(struct ath_hal *ah, const struct ath_desc *ds0, int * return AH_FALSE; } + +void +ar5211SetTxDescLink(struct ath_hal *ah, void *ds, uint32_t link) +{ + struct ar5211_desc *ads = AR5211DESC(ds); + + ads->ds_link = link; +} + +void +ar5211GetTxDescLink(struct ath_hal *ah, void *ds, uint32_t *link) +{ + struct ar5211_desc *ads = AR5211DESC(ds); + + *link = ads->ds_link; +} + +void +ar5211GetTxDescLinkPtr(struct ath_hal *ah, void *ds, uint32_t **linkptr) +{ + struct ar5211_desc *ads = AR5211DESC(ds); + + *linkptr = &ads->ds_link; +} diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211desc.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211desc.h index 4528447dee..e6aaf87164 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211desc.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211desc.h @@ -14,13 +14,11 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5211/ar5211desc.h 243249 2012-11-18 20:41:46Z adrian $ */ #ifndef _DEV_ATH_AR5211DESC_H #define _DEV_ATH_AR5211DESC_H -#include "ah_desc.h" - /* * Defintions for the DMA descriptors used by the Atheros * AR5211 and AR5110 Wireless Lan controller parts. diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211phy.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211phy.h index bf5b67e51a..5a35efa54a 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211phy.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211phy.h @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5211/ar5211phy.h 204644 2010-03-03 17:32:32Z rpaulo $ */ #ifndef _DEV_ATH_AR5211PHY_H #define _DEV_ATH_AR5211PHY_H diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211reg.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211reg.h index 93d4fc5d9b..baf0915bfb 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211reg.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/ar5211reg.h @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5211/ar5211reg.h 228980 2011-12-30 02:58:37Z dim $ */ #ifndef _DEV_ATH_AR5211REG_H #define _DEV_ATH_AR5211REG_H diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/boss.ini b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/boss.ini old mode 100755 new mode 100644 index e054436d74..074db4f5b7 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/boss.ini +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5211/boss.ini @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5211/boss.ini 245952 2013-01-26 22:08:21Z pfg $ */ /* Auto Generated PCI Register Writes. Created: 09/12/02 */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar2316.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar2316.c index 9bfc482f84..d9560ae8cb 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar2316.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar2316.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5212/ar2316.c 298939 2016-05-02 19:56:48Z pfg $ */ #include "opt_ah.h" @@ -373,9 +373,9 @@ ar2316getGainBoundariesAndPdadcsForPowers(struct ath_hal *ah, uint16_t channel, * change pwr_I_0 to signed 5-bits. */ int16_t Pmin_t2[MAX_NUM_PDGAINS_PER_CHANNEL]; - /* to accomodate -ve power levels later on. */ + /* to accommodate -ve power levels later on. */ int16_t Pmax_t2[MAX_NUM_PDGAINS_PER_CHANNEL]; - /* to accomodate -ve power levels later on */ + /* to accommodate -ve power levels later on */ uint16_t numVpd = 0; uint16_t Vpd_step; int16_t tmpVal ; diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar2317.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar2317.c index fdf88e3724..b433f2c6a1 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar2317.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar2317.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5212/ar2317.c 298939 2016-05-02 19:56:48Z pfg $ */ #include "opt_ah.h" @@ -351,9 +351,9 @@ ar2317getGainBoundariesAndPdadcsForPowers(struct ath_hal *ah, uint16_t channel, * change pwr_I_0 to signed 5-bits. */ int16_t Pmin_t2[MAX_NUM_PDGAINS_PER_CHANNEL]; - /* to accomodate -ve power levels later on. */ + /* to accommodate -ve power levels later on. */ int16_t Pmax_t2[MAX_NUM_PDGAINS_PER_CHANNEL]; - /* to accomodate -ve power levels later on */ + /* to accommodate -ve power levels later on */ uint16_t numVpd = 0; uint16_t Vpd_step; int16_t tmpVal ; diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar2413.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar2413.c index e06944461a..0f205eebf4 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar2413.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar2413.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5212/ar2413.c 298939 2016-05-02 19:56:48Z pfg $ */ #include "opt_ah.h" @@ -367,9 +367,9 @@ ar2413getGainBoundariesAndPdadcsForPowers(struct ath_hal *ah, uint16_t channel, * change pwr_I_0 to signed 5-bits. */ int16_t Pmin_t2[MAX_NUM_PDGAINS_PER_CHANNEL]; - /* to accomodate -ve power levels later on. */ + /* to accommodate -ve power levels later on. */ int16_t Pmax_t2[MAX_NUM_PDGAINS_PER_CHANNEL]; - /* to accomodate -ve power levels later on */ + /* to accommodate -ve power levels later on */ uint16_t numVpd = 0; uint16_t Vpd_step; int16_t tmpVal ; diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar2425.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar2425.c index e487489346..1055fddedd 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar2425.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar2425.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5212/ar2425.c 298939 2016-05-02 19:56:48Z pfg $ */ #include "opt_ah.h" @@ -372,9 +372,9 @@ ar2425getGainBoundariesAndPdadcsForPowers(struct ath_hal *ah, uint16_t channel, * change pwr_I_0 to signed 5-bits. */ static int16_t Pmin_t2[MAX_NUM_PDGAINS_PER_CHANNEL]; - /* to accomodate -ve power levels later on. */ + /* to accommodate -ve power levels later on. */ static int16_t Pmax_t2[MAX_NUM_PDGAINS_PER_CHANNEL]; - /* to accomodate -ve power levels later on */ + /* to accommodate -ve power levels later on */ uint16_t numVpd = 0; uint16_t Vpd_step; int16_t tmpVal ; diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5111.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5111.c index f9fd3f3b26..1a3206992b 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5111.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5111.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5212/ar5111.c 187831 2009-01-28 18:00:22Z sam $ */ #include "opt_ah.h" diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5112.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5112.c index 10030682a2..d8d18e6716 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5112.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5112.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5212/ar5112.c 225883 2011-09-30 05:17:57Z adrian $ */ #include "opt_ah.h" @@ -611,7 +611,7 @@ getFullPwrTable(uint16_t numPcdacs, uint16_t *pcdacs, int16_t *power, int16_t ma uint16_t idxR = 1; if (numPcdacs < 2) { - HALDEBUG_G(AH_NULL, HAL_DEBUG_ANY, + HALDEBUG(AH_NULL, HAL_DEBUG_ANY, "%s: at least 2 pcdac values needed [%d]\n", __func__, numPcdacs); return AH_FALSE; diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212.h index 8c4a1bb59e..51642e263e 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212.h @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5212/ar5212.h 290612 2015-11-09 15:59:42Z adrian $ */ #ifndef _ATH_AR5212_H_ #define _ATH_AR5212_H_ @@ -200,6 +200,7 @@ struct ar5212AniState { #define HAL_ANI_ENA 0x00000001 /* ANI operation enabled */ #define HAL_RSSI_ANI_ENA 0x00000002 /* rssi-based processing ena'd*/ +#if 0 struct ar5212Stats { uint32_t ast_ani_niup; /* ANI increased noise immunity */ uint32_t ast_ani_nidown; /* ANI decreased noise immunity */ @@ -219,6 +220,7 @@ struct ar5212Stats { HAL_MIB_STATS ast_mibstats; /* MIB counter stats */ HAL_NODE_STATS ast_nodestats; /* Latest rssi stats from driver */ }; +#endif /* * NF Cal history buffer @@ -252,12 +254,13 @@ struct ath_hal_5212 { uint8_t ah_macaddr[IEEE80211_ADDR_LEN]; uint8_t ah_bssid[IEEE80211_ADDR_LEN]; uint8_t ah_bssidmask[IEEE80211_ADDR_LEN]; + uint16_t ah_assocId; /* * Runtime state. */ uint32_t ah_maskReg; /* copy of AR_IMR */ - struct ar5212Stats ah_stats; /* various statistics */ + HAL_ANI_STATS ah_stats; /* various statistics */ RF_HAL_FUNCS *ah_rfHal; uint32_t ah_txDescMask; /* mask for TXDESC */ uint32_t ah_txOkInterruptMask; @@ -269,7 +272,6 @@ struct ath_hal_5212 { uint32_t ah_intrTxqs; /* tx q interrupt state */ /* decomp mask array */ uint8_t ah_decompMask[HAL_DECOMP_MASK_SIZE]; - HAL_POWER_MODE ah_powerMode; HAL_ANT_SETTING ah_antControl; /* antenna setting */ HAL_BOOL ah_diversity; /* fast diversity setting */ enum { @@ -333,6 +335,19 @@ struct ath_hal_5212 { uint8_t ah_txTrigLev; /* current Tx trigger level */ uint8_t ah_maxTxTrigLev; /* max tx trigger level */ + + /* + * Channel Tx, Rx, Rx Clear State + */ + uint32_t ah_cycleCount; + uint32_t ah_ctlBusy; + uint32_t ah_rxBusy; + uint32_t ah_txBusy; + uint32_t ah_rx_chainmask; + uint32_t ah_tx_chainmask; + + /* Used to return ANI statistics to the diagnostic API */ + HAL_ANI_STATS ext_ani_stats; }; #define AH5212(_ah) ((struct ath_hal_5212 *)(_ah)) @@ -509,14 +524,17 @@ extern HAL_BOOL ar5212GetDiagState(struct ath_hal *ah, int request, void **result, uint32_t *resultsize); extern HAL_STATUS ar5212SetQuiet(struct ath_hal *ah, uint32_t period, uint32_t duration, uint32_t nextStart, HAL_QUIET_FLAG flag); +extern HAL_BOOL ar5212GetMibCycleCounts(struct ath_hal *, + HAL_SURVEY_SAMPLE *); +extern void ar5212SetChainMasks(struct ath_hal *, uint32_t, uint32_t); extern HAL_BOOL ar5212SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip); extern HAL_POWER_MODE ar5212GetPowerMode(struct ath_hal *ah); extern HAL_BOOL ar5212GetPowerStatus(struct ath_hal *ah); -extern uint32_t ar5212GetRxDP(struct ath_hal *ath); -extern void ar5212SetRxDP(struct ath_hal *ah, uint32_t rxdp); +extern uint32_t ar5212GetRxDP(struct ath_hal *ath, HAL_RX_QUEUE); +extern void ar5212SetRxDP(struct ath_hal *ah, uint32_t rxdp, HAL_RX_QUEUE); extern void ar5212EnableReceive(struct ath_hal *ah); extern HAL_BOOL ar5212StopDmaReceive(struct ath_hal *ah); extern void ar5212StartPcuReceive(struct ath_hal *ah); @@ -535,7 +553,7 @@ extern HAL_STATUS ar5212ProcRxDesc(struct ath_hal *ah, struct ath_desc *, extern HAL_BOOL ar5212Reset(struct ath_hal *ah, HAL_OPMODE opmode, struct ieee80211_channel *chan, HAL_BOOL bChannelChange, - HAL_STATUS *status); + HAL_RESET_TYPE, HAL_STATUS *status); extern HAL_BOOL ar5212SetChannel(struct ath_hal *, const struct ieee80211_channel *); extern void ar5212SetOperatingMode(struct ath_hal *ah, int opmode); @@ -590,7 +608,8 @@ extern HAL_BOOL ar5212SetupXTxDesc(struct ath_hal *, struct ath_desc *, u_int txRate2, u_int txRetries2, u_int txRate3, u_int txRetries3); extern HAL_BOOL ar5212FillTxDesc(struct ath_hal *ah, struct ath_desc *ds, - u_int segLen, HAL_BOOL firstSeg, HAL_BOOL lastSeg, + HAL_DMA_ADDR *bufAddrList, uint32_t *segLenList, + u_int descId, u_int qcuId, HAL_BOOL firstSeg, HAL_BOOL lastSeg, const struct ath_desc *ds0); extern HAL_STATUS ar5212ProcTxDesc(struct ath_hal *ah, struct ath_desc *, struct ath_tx_status *); @@ -598,6 +617,12 @@ extern void ar5212GetTxIntrQueue(struct ath_hal *ah, uint32_t *); extern void ar5212IntrReqTxDesc(struct ath_hal *ah, struct ath_desc *); extern HAL_BOOL ar5212GetTxCompletionRates(struct ath_hal *ah, const struct ath_desc *ds0, int *rates, int *tries); +extern void ar5212SetTxDescLink(struct ath_hal *ah, void *ds, + uint32_t link); +extern void ar5212GetTxDescLink(struct ath_hal *ah, void *ds, + uint32_t *link); +extern void ar5212GetTxDescLinkPtr(struct ath_hal *ah, void *ds, + uint32_t **linkptr); extern const HAL_RATE_TABLE *ar5212GetRateTable(struct ath_hal *, u_int mode); @@ -605,7 +630,7 @@ extern void ar5212AniAttach(struct ath_hal *, const struct ar5212AniParams *, const struct ar5212AniParams *, HAL_BOOL ena); extern void ar5212AniDetach(struct ath_hal *); extern struct ar5212AniState *ar5212AniGetCurrentState(struct ath_hal *); -extern struct ar5212Stats *ar5212AniGetCurrentStats(struct ath_hal *); +extern HAL_ANI_STATS *ar5212AniGetCurrentStats(struct ath_hal *); extern HAL_BOOL ar5212AniControl(struct ath_hal *, HAL_ANI_CMD cmd, int param); extern HAL_BOOL ar5212AniSetParams(struct ath_hal *, const struct ar5212AniParams *, const struct ar5212AniParams *); @@ -622,10 +647,13 @@ extern void ar5212AniReset(struct ath_hal *, const struct ieee80211_channel *, extern HAL_BOOL ar5212IsNFCalInProgress(struct ath_hal *ah); extern HAL_BOOL ar5212WaitNFCalComplete(struct ath_hal *ah, int i); extern void ar5212EnableDfs(struct ath_hal *ah, HAL_PHYERR_PARAM *pe); +extern HAL_BOOL ar5212GetDfsDefaultThresh(struct ath_hal *ah, + HAL_PHYERR_PARAM *pe); extern void ar5212GetDfsThresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe); extern HAL_BOOL ar5212ProcessRadarEvent(struct ath_hal *ah, struct ath_rx_status *rxs, uint64_t fulltsf, const char *buf, HAL_DFS_EVENT *event); extern HAL_BOOL ar5212IsFastClockEnabled(struct ath_hal *ah); +extern uint32_t ar5212Get11nExtBusy(struct ath_hal *ah); #endif /* _ATH_AR5212_H_ */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212.ini b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212.ini index 608f91f375..f8cc99229f 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212.ini +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212.ini @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5212/ar5212.ini 204644 2010-03-03 17:32:32Z rpaulo $ */ /* Auto Generated PCI Register Writes. Created: 09/01/04 */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_ani.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_ani.c index b101306a63..d2b139d98f 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_ani.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_ani.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5212/ar5212_ani.c 280940 2015-04-01 03:42:46Z adrian $ */ #include "opt_ah.h" @@ -110,7 +110,7 @@ ar5212AniGetCurrentState(struct ath_hal *ah) /* * Return the current statistics. */ -struct ar5212Stats * +HAL_ANI_STATS * ar5212AniGetCurrentStats(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); @@ -865,16 +865,31 @@ static int32_t ar5212AniGetListenTime(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); - struct ar5212AniState *aniState; - uint32_t txFrameCount, rxFrameCount, cycleCount; - int32_t listenTime; + struct ar5212AniState *aniState = NULL; + int32_t listenTime = 0; + int good; + HAL_SURVEY_SAMPLE hs; - txFrameCount = OS_REG_READ(ah, AR_TFCNT); - rxFrameCount = OS_REG_READ(ah, AR_RFCNT); - cycleCount = OS_REG_READ(ah, AR_CCCNT); + /* + * We shouldn't see ah_curchan be NULL, but just in case.. + */ + if (AH_PRIVATE(ah)->ah_curchan == AH_NULL) { + ath_hal_printf(ah, "%s: ah_curchan = NULL?\n", __func__); + return (0); + } - aniState = ahp->ah_curani; - if (aniState->cycleCount == 0 || aniState->cycleCount > cycleCount) { + /* + * Fetch the current statistics, squirrel away the current + * sample, bump the sequence/sample counter. + */ + OS_MEMZERO(&hs, sizeof(hs)); + good = ar5212GetMibCycleCounts(ah, &hs); + ath_hal_survey_add_sample(ah, &hs); + + if (ANI_ENA(ah)) + aniState = ahp->ah_curani; + + if (good == AH_FALSE) { /* * Cycle counter wrap (or initial call); it's not possible * to accurately calculate a value because the registers @@ -882,15 +897,29 @@ ar5212AniGetListenTime(struct ath_hal *ah) */ listenTime = 0; ahp->ah_stats.ast_ani_lzero++; - } else { - int32_t ccdelta = cycleCount - aniState->cycleCount; - int32_t rfdelta = rxFrameCount - aniState->rxFrameCount; - int32_t tfdelta = txFrameCount - aniState->txFrameCount; + } else if (ANI_ENA(ah)) { + /* + * Only calculate and update the cycle count if we have + * an ANI state. + */ + int32_t ccdelta = + AH5212(ah)->ah_cycleCount - aniState->cycleCount; + int32_t rfdelta = + AH5212(ah)->ah_rxBusy - aniState->rxFrameCount; + int32_t tfdelta = + AH5212(ah)->ah_txBusy - aniState->txFrameCount; listenTime = (ccdelta - rfdelta - tfdelta) / CLOCK_RATE; } - aniState->cycleCount = cycleCount; - aniState->txFrameCount = txFrameCount; - aniState->rxFrameCount = rxFrameCount; + + /* + * Again, only update ANI state if we have it. + */ + if (ANI_ENA(ah)) { + aniState->cycleCount = AH5212(ah)->ah_cycleCount; + aniState->rxFrameCount = AH5212(ah)->ah_rxBusy; + aniState->txFrameCount = AH5212(ah)->ah_txBusy; + } + return listenTime; } @@ -956,13 +985,15 @@ ar5212AniPoll(struct ath_hal *ah, const struct ieee80211_channel *chan) const struct ar5212AniParams *params; int32_t listenTime; + /* Always update from the MIB, for statistics gathering */ + listenTime = ar5212AniGetListenTime(ah); + /* XXX can aniState be null? */ if (aniState == AH_NULL) return; if (!ANI_ENA(ah)) return; - listenTime = ar5212AniGetListenTime(ah); if (listenTime < 0) { ahp->ah_stats.ast_ani_lneg++; /* restart ANI period if listenTime is invalid */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_attach.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_attach.c index eaceaba766..a72f68a916 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_attach.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_attach.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5212/ar5212_attach.c 305614 2016-09-08 15:05:25Z pfg $ */ #include "opt_ah.h" @@ -29,7 +29,8 @@ #define AH_5212_COMMON #include "ar5212/ar5212.ini" -static void ar5212ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore); +static void ar5212ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore, + HAL_BOOL power_off); static void ar5212DisablePCIE(struct ath_hal *ah); static const struct ath_hal_private ar5212hal = {{ @@ -70,6 +71,9 @@ static const struct ath_hal_private ar5212hal = {{ .ah_getTxIntrQueue = ar5212GetTxIntrQueue, .ah_reqTxIntrDesc = ar5212IntrReqTxDesc, .ah_getTxCompletionRates = ar5212GetTxCompletionRates, + .ah_setTxDescLink = ar5212SetTxDescLink, + .ah_getTxDescLink = ar5212GetTxDescLink, + .ah_getTxDescLinkPtr = ar5212GetTxDescLinkPtr, /* RX Functions */ .ah_getRxDP = ar5212GetRxDP, @@ -107,6 +111,7 @@ static const struct ath_hal_private ar5212hal = {{ .ah_gpioSetIntr = ar5212GpioSetIntr, .ah_getTsf32 = ar5212GetTsf32, .ah_getTsf64 = ar5212GetTsf64, + .ah_setTsf64 = ar5212SetTsf64, .ah_resetTsf = ar5212ResetTsf, .ah_detectCardPresent = ar5212DetectCardPresent, .ah_updateMibCounters = ar5212UpdateMibCounters, @@ -125,15 +130,19 @@ static const struct ath_hal_private ar5212hal = {{ .ah_getAckCTSRate = ar5212GetAckCTSRate, .ah_setCTSTimeout = ar5212SetCTSTimeout, .ah_getCTSTimeout = ar5212GetCTSTimeout, - .ah_setDecompMask = ar5212SetDecompMask, - .ah_setCoverageClass = ar5212SetCoverageClass, + .ah_setDecompMask = ar5212SetDecompMask, + .ah_setCoverageClass = ar5212SetCoverageClass, .ah_setQuiet = ar5212SetQuiet, + .ah_getMibCycleCounts = ar5212GetMibCycleCounts, + .ah_setChainMasks = ar5212SetChainMasks, /* DFS Functions */ .ah_enableDfs = ar5212EnableDfs, .ah_getDfsThresh = ar5212GetDfsThresh, + .ah_getDfsDefaultThresh = ar5212GetDfsDefaultThresh, .ah_procRadarEvent = ar5212ProcessRadarEvent, .ah_isFastClockEnabled = ar5212IsFastClockEnabled, + .ah_get11nExtBusy = ar5212Get11nExtBusy, /* Key Cache Functions */ .ah_getKeyCacheSize = ar5212GetKeyCacheSize, @@ -261,8 +270,8 @@ ar5212InitState(struct ath_hal_5212 *ahp, uint16_t devid, HAL_SOFTC sc, ahp->ah_acktimeout = (u_int) -1; ahp->ah_ctstimeout = (u_int) -1; ahp->ah_sifstime = (u_int) -1; - ahp->ah_txTrigLev = INIT_TX_FIFO_THRESHOLD, - ahp->ah_maxTxTrigLev = MAX_TX_FIFO_THRESHOLD, + ahp->ah_txTrigLev = INIT_TX_FIFO_THRESHOLD; + ahp->ah_maxTxTrigLev = MAX_TX_FIFO_THRESHOLD; OS_MEMCPY(&ahp->ah_bssidmask, defbssidmask, IEEE80211_ADDR_LEN); #undef N @@ -308,7 +317,7 @@ ar5212IsMacSupported(uint8_t macVersion, uint8_t macRev) static struct ath_hal * ar5212Attach(uint16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, - HAL_STATUS *status) + HAL_OPS_CONFIG *ah_config, HAL_STATUS *status) { #define AH_EEPROM_PROTECT(ah) \ (AH_PRIVATE(ah)->ah_ispcie)? AR_EEPROM_PROTECT_PCIE : AR_EEPROM_PROTECT) @@ -319,13 +328,13 @@ ar5212Attach(uint16_t devid, HAL_SOFTC sc, uint16_t eeval; HAL_STATUS ecode; - HALDEBUG_G(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n", + HALDEBUG(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n", __func__, sc, (void*) st, (void*) sh); /* NB: memory is returned zero'd */ ahp = ath_hal_malloc(sizeof (struct ath_hal_5212)); if (ahp == AH_NULL) { - HALDEBUG_G(AH_NULL, HAL_DEBUG_ANY, + HALDEBUG(AH_NULL, HAL_DEBUG_ANY, "%s: cannot allocate memory for state block\n", __func__); *status = HAL_ENOMEM; return AH_NULL; @@ -368,7 +377,7 @@ ar5212Attach(uint16_t devid, HAL_SOFTC sc, if (AH_PRIVATE(ah)->ah_ispcie) { /* XXX: build flag to disable this? */ - ath_hal_configPCIE(ah, AH_FALSE); + ath_hal_configPCIE(ah, AH_FALSE, AH_FALSE); } if (!ar5212ChipTest(ah)) { @@ -664,7 +673,7 @@ ar5212GetChannelEdges(struct ath_hal *ah, * XXX Clean up the magic numbers. */ static void -ar5212ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore) +ar5212ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore, HAL_BOOL power_off) { OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fc00); OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924); @@ -779,7 +788,29 @@ ar5212FillCapabilityInfo(struct ath_hal *ah) else pCap->halHigh2GhzChan = 2732; - pCap->halLow5GhzChan = 4915; + /* + * For AR5111 version < 4, the lowest centre frequency supported is + * 5130MHz. For AR5111 version 4, the 4.9GHz channels are supported + * but only in 10MHz increments. + * + * In addition, the programming method is wrong - it uses the IEEE + * channel number to calculate the frequency, rather than the + * channel centre. Since half/quarter rates re-use some of the + * 5GHz channel IEEE numbers, this will result in a badly programmed + * synth. + * + * Until the relevant support is written, just limit lower frequency + * support for AR5111 so things aren't incorrectly programmed. + * + * XXX It's also possible this code doesn't correctly limit the + * centre frequencies of potential channels; this is very important + * for half/quarter rate! + */ + if (AH_RADIO_MAJOR(ah) == AR_RAD5111_SREV_MAJOR) { + pCap->halLow5GhzChan = 5120; /* XXX lowest centre = 5130MHz */ + } else { + pCap->halLow5GhzChan = 4915; + } pCap->halHigh5GhzChan = 6100; pCap->halCipherCkipSupport = AH_FALSE; @@ -818,6 +849,8 @@ ar5212FillCapabilityInfo(struct ath_hal *ah) pCap->halTurboGSupport = pCap->halWirelessModes & HAL_MODE_108G; pCap->halPSPollBroken = AH_TRUE; /* XXX fixed in later revs? */ + pCap->halNumMRRetries = 4; /* Hardware supports 4 MRR */ + pCap->halNumTxMaps = 1; /* Single TX ptr per descr */ pCap->halVEOLSupport = AH_TRUE; pCap->halBssIdMaskSupport = AH_TRUE; pCap->halMcastKeySrchSupport = AH_TRUE; @@ -839,6 +872,12 @@ ar5212FillCapabilityInfo(struct ath_hal *ah) pCap->halChanHalfRate = AH_TRUE; pCap->halChanQuarterRate = AH_TRUE; + /* + * RSSI uses the combined field; some 11n NICs may use + * the control chain RSSI. + */ + pCap->halUseCombinedRadarRssi = AH_TRUE; + if (ath_hal_eepromGetFlag(ah, AR_EEP_RFKILL) && ath_hal_eepromGet(ah, AR_EEP_RFSILENT, &ahpriv->ah_rfsilent) == HAL_OK) { /* NB: enabled by default */ @@ -846,7 +885,7 @@ ar5212FillCapabilityInfo(struct ath_hal *ah) pCap->halRfSilentSupport = AH_TRUE; } - /* NB: this is a guess, noone seems to know the answer */ + /* NB: this is a guess, no one seems to know the answer */ ahpriv->ah_rxornIsFatal = (AH_PRIVATE(ah)->ah_macVersion < AR_SREV_VERSION_VENICE); diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_beacon.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_beacon.c index 1b4b342bc4..6c493df249 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_beacon.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_beacon.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5212/ar5212_beacon.c 243588 2012-11-27 02:18:41Z adrian $ */ #include "opt_ah.h" @@ -44,11 +44,21 @@ ar5212GetNextTBTT(struct ath_hal *ah) void ar5212SetBeaconTimers(struct ath_hal *ah, const HAL_BEACON_TIMERS *bt) { + struct ath_hal_5212 *ahp = AH5212(ah); - OS_REG_WRITE(ah, AR_TIMER0, bt->bt_nexttbtt); - OS_REG_WRITE(ah, AR_TIMER1, bt->bt_nextdba); - OS_REG_WRITE(ah, AR_TIMER2, bt->bt_nextswba); - OS_REG_WRITE(ah, AR_TIMER3, bt->bt_nextatim); + /* + * Limit the timers to their specific resolutions: + * + * + Timer 0 - 0..15 0xffff TU + * + Timer 1 - 0..18 0x7ffff TU/8 + * + Timer 2 - 0..24 0x1ffffff TU/8 + * + Timer 3 - 0..15 0xffff TU + */ + OS_REG_WRITE(ah, AR_TIMER0, bt->bt_nexttbtt & 0xffff); + OS_REG_WRITE(ah, AR_TIMER1, bt->bt_nextdba & 0x7ffff); + OS_REG_WRITE(ah, AR_TIMER2, bt->bt_nextswba & 0x1ffffff); + /* XXX force nextatim to be non-zero? */ + OS_REG_WRITE(ah, AR_TIMER3, bt->bt_nextatim & 0xffff); /* * Set the Beacon register after setting all timers. */ @@ -65,6 +75,7 @@ ar5212SetBeaconTimers(struct ath_hal *ah, const HAL_BEACON_TIMERS *bt) OS_REG_WRITE(ah, AR_BEACON, AR_BEACON_RESET_TSF); } OS_REG_WRITE(ah, AR_BEACON, bt->bt_intval); + ahp->ah_beaconInterval = (bt->bt_intval & HAL_BEACON_PERIOD); } /* diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_eeprom.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_eeprom.c index f4b67715b4..d369d30397 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_eeprom.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_eeprom.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5212/ar5212_eeprom.c 204644 2010-03-03 17:32:32Z rpaulo $ */ #include "opt_ah.h" diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_gpio.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_gpio.c index b2363af928..d69de9a163 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_gpio.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_gpio.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5212/ar5212_gpio.c 188974 2009-02-24 00:12:16Z sam $ */ #include "opt_ah.h" diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_interrupts.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_interrupts.c index 47f9e7400f..30e2bb77e1 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_interrupts.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_interrupts.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5212/ar5212_interrupts.c 201758 2010-01-07 21:01:37Z mbr $ */ #include "opt_ah.h" diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_keycache.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_keycache.c index bf7db4942b..ac44e4e2ce 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_keycache.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_keycache.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5212/ar5212_keycache.c 218483 2011-02-09 15:23:16Z adrian $ */ #include "opt_ah.h" diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_misc.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_misc.c index e1eab2c6cc..f6710c6d46 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_misc.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_misc.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5212/ar5212_misc.c 281128 2015-04-06 01:12:53Z adrian $ */ #include "opt_ah.h" @@ -218,8 +218,9 @@ ar5212WriteAssocid(struct ath_hal *ah, const uint8_t *bssid, uint16_t assocId) { struct ath_hal_5212 *ahp = AH5212(ah); - /* XXX save bssid for possible re-use on reset */ + /* save bssid for possible re-use on reset */ OS_MEMCPY(ahp->ah_bssid, bssid, IEEE80211_ADDR_LEN); + ahp->ah_assocId = assocId; OS_REG_WRITE(ah, AR_BSS_ID0, LE_READ_4(ahp->ah_bssid)); OS_REG_WRITE(ah, AR_BSS_ID1, LE_READ_2(ahp->ah_bssid+4) | ((assocId & 0x3fff)<ah_diversity ? HAL_OK : HAL_ENXIO; + case HAL_CAP_STRONG_DIV: + *result = OS_REG_READ(ah, AR_PHY_RESTART); + *result = MS(*result, AR_PHY_RESTART_DIV_GC); + return HAL_OK; } return HAL_EINVAL; case HAL_CAP_DIAG: @@ -949,16 +954,34 @@ ar5212SetCapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type, OS_REG_WRITE(ah, AR_MISC_MODE, OS_REG_READ(ah, AR_MISC_MODE) | ahp->ah_miscMode); return AH_TRUE; case HAL_CAP_DIVERSITY: - if (ahp->ah_phyPowerOn) { - v = OS_REG_READ(ah, AR_PHY_CCK_DETECT); - if (setting) - v |= AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV; - else - v &= ~AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV; - OS_REG_WRITE(ah, AR_PHY_CCK_DETECT, v); + switch (capability) { + case 0: + return AH_FALSE; + case 1: /* setting */ + if (ahp->ah_phyPowerOn) { + if (capability == HAL_CAP_STRONG_DIV) { + v = OS_REG_READ(ah, AR_PHY_CCK_DETECT); + if (setting) + v |= AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV; + else + v &= ~AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV; + OS_REG_WRITE(ah, AR_PHY_CCK_DETECT, v); + } + } + ahp->ah_diversity = (setting != 0); + return AH_TRUE; + + case HAL_CAP_STRONG_DIV: + if (! ahp->ah_phyPowerOn) + return AH_FALSE; + v = OS_REG_READ(ah, AR_PHY_RESTART); + v &= ~AR_PHY_RESTART_DIV_GC; + v |= SM(setting, AR_PHY_RESTART_DIV_GC); + OS_REG_WRITE(ah, AR_PHY_RESTART, v); + return AH_TRUE; + default: + return AH_FALSE; } - ahp->ah_diversity = (setting != 0); - return AH_TRUE; case HAL_CAP_DIAG: /* hardware diagnostic support */ /* * NB: could split this up into virtual capabilities, @@ -1029,6 +1052,7 @@ ar5212GetDiagState(struct ath_hal *ah, int request, void **result, uint32_t *resultsize) { struct ath_hal_5212 *ahp = AH5212(ah); + HAL_ANI_STATS *astats; (void) ahp; if (ath_hal_getdiagstate(ah, request, args, argsize, result, resultsize)) @@ -1060,9 +1084,16 @@ ar5212GetDiagState(struct ath_hal *ah, int request, 0 : sizeof(struct ar5212AniState); return AH_TRUE; case HAL_DIAG_ANI_STATS: - *result = ar5212AniGetCurrentStats(ah); - *resultsize = (*result == AH_NULL) ? - 0 : sizeof(struct ar5212Stats); + OS_MEMZERO(&ahp->ext_ani_stats, sizeof(ahp->ext_ani_stats)); + astats = ar5212AniGetCurrentStats(ah); + if (astats == NULL) { + *result = NULL; + *resultsize = 0; + } else { + OS_MEMCPY(&ahp->ext_ani_stats, astats, sizeof(HAL_ANI_STATS)); + *result = &ahp->ext_ani_stats; + *resultsize = sizeof(ahp->ext_ani_stats); + } return AH_TRUE; case HAL_DIAG_ANI_CMD: if (argsize != 2*sizeof(uint32_t)) @@ -1089,6 +1120,7 @@ ar5212GetDiagState(struct ath_hal *ah, int request, return AH_FALSE; return ar5212AniSetParams(ah, args, args); } + break; } return AH_FALSE; } @@ -1154,7 +1186,120 @@ ar5212EnableDfs(struct ath_hal *ah, HAL_PHYERR_PARAM *pe) val &= ~AR_PHY_RADAR_0_INBAND; val |= SM(pe->pe_inband, AR_PHY_RADAR_0_INBAND); } - OS_REG_WRITE(ah, AR_PHY_RADAR_0, val | AR_PHY_RADAR_0_ENA); + if (pe->pe_enabled) + val |= AR_PHY_RADAR_0_ENA; + else + val &= ~ AR_PHY_RADAR_0_ENA; + + if (IS_5413(ah)) { + + if (pe->pe_blockradar == 1) + OS_REG_SET_BIT(ah, AR_PHY_RADAR_2, + AR_PHY_RADAR_2_BLOCKOFDMWEAK); + else + OS_REG_CLR_BIT(ah, AR_PHY_RADAR_2, + AR_PHY_RADAR_2_BLOCKOFDMWEAK); + + if (pe->pe_en_relstep_check == 1) + OS_REG_SET_BIT(ah, AR_PHY_RADAR_2, + AR_PHY_RADAR_2_ENRELSTEPCHK); + else + OS_REG_CLR_BIT(ah, AR_PHY_RADAR_2, + AR_PHY_RADAR_2_ENRELSTEPCHK); + + if (pe->pe_usefir128 == 1) + OS_REG_SET_BIT(ah, AR_PHY_RADAR_2, + AR_PHY_RADAR_2_USEFIR128); + else + OS_REG_CLR_BIT(ah, AR_PHY_RADAR_2, + AR_PHY_RADAR_2_USEFIR128); + + if (pe->pe_enmaxrssi == 1) + OS_REG_SET_BIT(ah, AR_PHY_RADAR_2, + AR_PHY_RADAR_2_ENMAXRSSI); + else + OS_REG_CLR_BIT(ah, AR_PHY_RADAR_2, + AR_PHY_RADAR_2_ENMAXRSSI); + + if (pe->pe_enrelpwr == 1) + OS_REG_SET_BIT(ah, AR_PHY_RADAR_2, + AR_PHY_RADAR_2_ENRELPWRCHK); + else + OS_REG_CLR_BIT(ah, AR_PHY_RADAR_2, + AR_PHY_RADAR_2_ENRELPWRCHK); + + if (pe->pe_relpwr != HAL_PHYERR_PARAM_NOVAL) + OS_REG_RMW_FIELD(ah, AR_PHY_RADAR_2, + AR_PHY_RADAR_2_RELPWR, pe->pe_relpwr); + + if (pe->pe_relstep != HAL_PHYERR_PARAM_NOVAL) + OS_REG_RMW_FIELD(ah, AR_PHY_RADAR_2, + AR_PHY_RADAR_2_RELSTEP, pe->pe_relstep); + + if (pe->pe_maxlen != HAL_PHYERR_PARAM_NOVAL) + OS_REG_RMW_FIELD(ah, AR_PHY_RADAR_2, + AR_PHY_RADAR_2_MAXLEN, pe->pe_maxlen); + } + + OS_REG_WRITE(ah, AR_PHY_RADAR_0, val); +} + +/* + * Parameters for the AR5212 PHY. + */ +#define AR5212_DFS_FIRPWR -35 +#define AR5212_DFS_RRSSI 20 +#define AR5212_DFS_HEIGHT 14 +#define AR5212_DFS_PRSSI 6 +#define AR5212_DFS_INBAND 4 + +/* + * Default parameters for the AR5413 PHY. + */ +#define AR5413_DFS_FIRPWR -34 +#define AR5413_DFS_RRSSI 20 +#define AR5413_DFS_HEIGHT 10 +#define AR5413_DFS_PRSSI 15 +#define AR5413_DFS_INBAND 6 +#define AR5413_DFS_RELPWR 8 +#define AR5413_DFS_RELSTEP 31 +#define AR5413_DFS_MAXLEN 255 + +HAL_BOOL +ar5212GetDfsDefaultThresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe) +{ + + if (IS_5413(ah)) { + pe->pe_firpwr = AR5413_DFS_FIRPWR; + pe->pe_rrssi = AR5413_DFS_RRSSI; + pe->pe_height = AR5413_DFS_HEIGHT; + pe->pe_prssi = AR5413_DFS_PRSSI; + pe->pe_inband = AR5413_DFS_INBAND; + pe->pe_relpwr = AR5413_DFS_RELPWR; + pe->pe_relstep = AR5413_DFS_RELSTEP; + pe->pe_maxlen = AR5413_DFS_MAXLEN; + pe->pe_usefir128 = 0; + pe->pe_blockradar = 1; + pe->pe_enmaxrssi = 1; + pe->pe_enrelpwr = 1; + pe->pe_en_relstep_check = 0; + } else { + pe->pe_firpwr = AR5212_DFS_FIRPWR; + pe->pe_rrssi = AR5212_DFS_RRSSI; + pe->pe_height = AR5212_DFS_HEIGHT; + pe->pe_prssi = AR5212_DFS_PRSSI; + pe->pe_inband = AR5212_DFS_INBAND; + pe->pe_relpwr = 0; + pe->pe_relstep = 0; + pe->pe_maxlen = 0; + pe->pe_usefir128 = 0; + pe->pe_blockradar = 0; + pe->pe_enmaxrssi = 0; + pe->pe_enrelpwr = 0; + pe->pe_en_relstep_check = 0; + } + + return (AH_TRUE); } void @@ -1171,11 +1316,31 @@ ar5212GetDfsThresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe) pe->pe_height = MS(val, AR_PHY_RADAR_0_HEIGHT); pe->pe_prssi = MS(val, AR_PHY_RADAR_0_PRSSI); pe->pe_inband = MS(val, AR_PHY_RADAR_0_INBAND); + pe->pe_enabled = !! (val & AR_PHY_RADAR_0_ENA); pe->pe_relpwr = 0; pe->pe_relstep = 0; pe->pe_maxlen = 0; + pe->pe_usefir128 = 0; + pe->pe_blockradar = 0; + pe->pe_enmaxrssi = 0; + pe->pe_enrelpwr = 0; + pe->pe_en_relstep_check = 0; pe->pe_extchannel = AH_FALSE; + + if (IS_5413(ah)) { + val = OS_REG_READ(ah, AR_PHY_RADAR_2); + pe->pe_relpwr = !! MS(val, AR_PHY_RADAR_2_RELPWR); + pe->pe_relstep = !! MS(val, AR_PHY_RADAR_2_RELSTEP); + pe->pe_maxlen = !! MS(val, AR_PHY_RADAR_2_MAXLEN); + + pe->pe_usefir128 = !! (val & AR_PHY_RADAR_2_USEFIR128); + pe->pe_blockradar = !! (val & AR_PHY_RADAR_2_BLOCKOFDMWEAK); + pe->pe_enmaxrssi = !! (val & AR_PHY_RADAR_2_ENMAXRSSI); + pe->pe_enrelpwr = !! (val & AR_PHY_RADAR_2_ENRELPWRCHK); + pe->pe_en_relstep_check = + !! (val & AR_PHY_RADAR_2_ENRELSTEPCHK); + } } /* @@ -1232,3 +1397,63 @@ ar5212IsFastClockEnabled(struct ath_hal *ah) { return AH_FALSE; } + +/* + * Return what percentage of the extension channel is busy. + * This is always disabled for AR5212 series NICs. + */ +uint32_t +ar5212Get11nExtBusy(struct ath_hal *ah) +{ + return 0; +} + +/* + * Channel survey support. + */ +HAL_BOOL +ar5212GetMibCycleCounts(struct ath_hal *ah, HAL_SURVEY_SAMPLE *hsample) +{ + struct ath_hal_5212 *ahp = AH5212(ah); + u_int32_t good = AH_TRUE; + + /* XXX freeze/unfreeze mib counters */ + uint32_t rc = OS_REG_READ(ah, AR_RCCNT); + uint32_t rf = OS_REG_READ(ah, AR_RFCNT); + uint32_t tf = OS_REG_READ(ah, AR_TFCNT); + uint32_t cc = OS_REG_READ(ah, AR_CCCNT); /* read cycles last */ + + if (ahp->ah_cycleCount == 0 || ahp->ah_cycleCount > cc) { + /* + * Cycle counter wrap (or initial call); it's not possible + * to accurately calculate a value because the registers + * right shift rather than wrap--so punt and return 0. + */ + HALDEBUG(ah, HAL_DEBUG_ANY, + "%s: cycle counter wrap. ExtBusy = 0\n", __func__); + good = AH_FALSE; + } else { + hsample->cycle_count = cc - ahp->ah_cycleCount; + hsample->chan_busy = rc - ahp->ah_ctlBusy; + hsample->ext_chan_busy = 0; + hsample->rx_busy = rf - ahp->ah_rxBusy; + hsample->tx_busy = tf - ahp->ah_txBusy; + } + + /* + * Keep a copy of the MIB results so the next sample has something + * to work from. + */ + ahp->ah_cycleCount = cc; + ahp->ah_rxBusy = rf; + ahp->ah_ctlBusy = rc; + ahp->ah_txBusy = tf; + + return (good); +} + +void +ar5212SetChainMasks(struct ath_hal *ah, uint32_t tx_chainmask, + uint32_t rx_chainmask) +{ +} diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_phy.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_phy.c index ac55be6055..6f1222738c 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_phy.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_phy.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5212/ar5212_phy.c 191022 2009-04-13 21:01:08Z sam $ */ #include "opt_ah.h" diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_power.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_power.c index 3f755bdcfe..59bd544399 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_power.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_power.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5212/ar5212_power.c 298939 2016-05-02 19:56:48Z pfg $ */ #include "opt_ah.h" @@ -51,7 +51,7 @@ ar5212SetPowerModeAwake(struct ath_hal *ah, int setChip) * which when blindly written back with OS_REG_RMW_FIELD * enables the MIB interrupt for the sleep performance * counters. This can result in an interrupt storm when - * ANI is in operation as noone knows to turn off the MIB + * ANI is in operation as no one knows to turn off the MIB * interrupt cause. */ scr = OS_REG_READ(ah, AR_SCR); @@ -119,7 +119,6 @@ ar5212SetPowerModeNetworkSleep(struct ath_hal *ah, int setChip) HAL_BOOL ar5212SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip) { - struct ath_hal_5212 *ahp = AH5212(ah); #ifdef AH_DEBUG static const char* modes[] = { "AWAKE", @@ -131,24 +130,29 @@ ar5212SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip) int status = AH_TRUE; HALDEBUG(ah, HAL_DEBUG_POWER, "%s: %s -> %s (%s)\n", __func__, - modes[ahp->ah_powerMode], modes[mode], + modes[ah->ah_powerMode], modes[mode], setChip ? "set chip " : ""); switch (mode) { case HAL_PM_AWAKE: + if (setChip) + ah->ah_powerMode = mode; status = ar5212SetPowerModeAwake(ah, setChip); break; case HAL_PM_FULL_SLEEP: ar5212SetPowerModeSleep(ah, setChip); + if (setChip) + ah->ah_powerMode = mode; break; case HAL_PM_NETWORK_SLEEP: ar5212SetPowerModeNetworkSleep(ah, setChip); + if (setChip) + ah->ah_powerMode = mode; break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unknown power mode %u\n", __func__, mode); return AH_FALSE; } - ahp->ah_powerMode = mode; return status; } diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_recv.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_recv.c index db4f93d430..241ae87c7d 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_recv.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_recv.c @@ -14,12 +14,13 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5212/ar5212_recv.c 243169 2012-11-17 02:02:36Z adrian $ */ #include "opt_ah.h" #include "ah.h" #include "ah_internal.h" +#include "ah_desc.h" #include "ar5212/ar5212.h" #include "ar5212/ar5212reg.h" @@ -29,8 +30,10 @@ * Get the RXDP. */ uint32_t -ar5212GetRxDP(struct ath_hal *ath) +ar5212GetRxDP(struct ath_hal *ath, HAL_RX_QUEUE qtype) { + + HALASSERT(qtype == HAL_RX_QUEUE_HP); return OS_REG_READ(ath, AR_RXDP); } @@ -38,8 +41,10 @@ ar5212GetRxDP(struct ath_hal *ath) * Set the RxDP. */ void -ar5212SetRxDP(struct ath_hal *ah, uint32_t rxdp) +ar5212SetRxDP(struct ath_hal *ah, uint32_t rxdp, HAL_RX_QUEUE qtype) { + + HALASSERT(qtype == HAL_RX_QUEUE_HP); OS_REG_WRITE(ah, AR_RXDP, rxdp); HALASSERT(OS_REG_READ(ah, AR_RXDP) == rxdp); } @@ -272,6 +277,14 @@ ar5212ProcRxDesc(struct ath_hal *ah, struct ath_desc *ds, rs->rs_antenna = MS(ads->ds_rxstatus0, AR_RcvAntenna); rs->rs_more = (ads->ds_rxstatus0 & AR_More) ? 1 : 0; + /* + * The AR5413 (at least) sometimes sets both AR_CRCErr and + * AR_PHYErr when reporting radar pulses. In this instance + * set HAL_RXERR_PHY as well as HAL_RXERR_CRC and + * let the driver layer figure out what to do. + * + * See PR kern/169362. + */ if ((ads->ds_rxstatus1 & AR_FrmRcvOK) == 0) { /* * These four bits should not be set together. The @@ -282,9 +295,7 @@ ar5212ProcRxDesc(struct ath_hal *ah, struct ath_desc *ds, * Consequently we filter them out here so we don't * confuse and/or complicate drivers. */ - if (ads->ds_rxstatus1 & AR_CRCErr) - rs->rs_status |= HAL_RXERR_CRC; - else if (ads->ds_rxstatus1 & AR_PHYErr) { + if (ads->ds_rxstatus1 & AR_PHYErr) { u_int phyerr; rs->rs_status |= HAL_RXERR_PHY; @@ -293,7 +304,11 @@ ar5212ProcRxDesc(struct ath_hal *ah, struct ath_desc *ds, if (!AH5212(ah)->ah_hasHwPhyCounters && phyerr != HAL_PHYERR_RADAR) ar5212AniPhyErrReport(ah, rs); - } else if (ads->ds_rxstatus1 & AR_DecryptCRCErr) + } + + if (ads->ds_rxstatus1 & AR_CRCErr) + rs->rs_status |= HAL_RXERR_CRC; + else if (ads->ds_rxstatus1 & AR_DecryptCRCErr) rs->rs_status |= HAL_RXERR_DECRYPT; else if (ads->ds_rxstatus1 & AR_MichaelErr) rs->rs_status |= HAL_RXERR_MIC; diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_reset.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_reset.c index 8e6341a04c..ac896aba6c 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_reset.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_reset.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5212/ar5212_reset.c 298939 2016-05-02 19:56:48Z pfg $ */ #include "opt_ah.h" @@ -95,6 +95,16 @@ write_common(struct ath_hal *ah, const HAL_INI_ARRAY *ia, #define IS_DISABLE_FAST_ADC_CHAN(x) (((x) == 2462) || ((x) == 2467)) +/* + * XXX NDIS 5.x code had MAX_RESET_WAIT set to 2000 for AP code + * and 10 for Client code + */ +#define MAX_RESET_WAIT 10 + +#define TX_QUEUEPEND_CHECK 1 +#define TX_ENABLE_CHECK 2 +#define RX_ENABLE_CHECK 4 + /* * Places the device in and out of reset and then places sane * values in the registers based on EEPROM config, initialization @@ -106,7 +116,9 @@ write_common(struct ath_hal *ah, const HAL_INI_ARRAY *ia, HAL_BOOL ar5212Reset(struct ath_hal *ah, HAL_OPMODE opmode, struct ieee80211_channel *chan, - HAL_BOOL bChannelChange, HAL_STATUS *status) + HAL_BOOL bChannelChange, + HAL_RESET_TYPE resetType, + HAL_STATUS *status) { #define N(a) (sizeof (a) / sizeof (a[0])) #define FAIL(_code) do { ecode = _code; goto bad; } while (0) @@ -185,6 +197,10 @@ ar5212Reset(struct ath_hal *ah, HAL_OPMODE opmode, saveFrameSeqCount = OS_REG_READ(ah, AR_D_SEQNUM); } else saveFrameSeqCount = 0; /* NB: silence compiler */ + + /* Blank the channel survey statistics */ + ath_hal_survey_clear(ah); + #if 0 /* * XXX disable for now; this appears to sometimes cause OFDM @@ -428,9 +444,10 @@ ar5212Reset(struct ath_hal *ah, HAL_OPMODE opmode, /* Restore previous antenna */ OS_REG_WRITE(ah, AR_DEF_ANTENNA, saveDefAntenna); - /* then our BSSID */ + /* then our BSSID and associate id */ OS_REG_WRITE(ah, AR_BSS_ID0, LE_READ_4(ahp->ah_bssid)); - OS_REG_WRITE(ah, AR_BSS_ID1, LE_READ_2(ahp->ah_bssid + 4)); + OS_REG_WRITE(ah, AR_BSS_ID1, LE_READ_2(ahp->ah_bssid + 4) | + (ahp->ah_assocId & 0x3fff) << AR_BSS_ID1_AID_S); /* Restore bmiss rssi & count thresholds */ OS_REG_WRITE(ah, AR_RSSI_THR, ahp->ah_rssiThr); @@ -1104,6 +1121,76 @@ ar5212ResetCalValid(struct ath_hal *ah, const struct ieee80211_channel *chan) return AH_TRUE; } +/************************************************************** + * ar5212MacStop + * + * Disables all active QCUs and ensure that the mac is in a + * quiessence state. + */ +static HAL_BOOL +ar5212MacStop(struct ath_hal *ah) +{ + HAL_BOOL status; + uint32_t count; + uint32_t pendFrameCount; + uint32_t macStateFlag; + uint32_t queue; + + status = AH_FALSE; + + /* Disable Rx Operation ***********************************/ + OS_REG_SET_BIT(ah, AR_CR, AR_CR_RXD); + + /* Disable TX Operation ***********************************/ +#ifdef NOT_YET + ar5212SetTxdpInvalid(ah); +#endif + OS_REG_SET_BIT(ah, AR_Q_TXD, AR_Q_TXD_M); + + /* Polling operation for completion of disable ************/ + macStateFlag = TX_ENABLE_CHECK | RX_ENABLE_CHECK; + + for (count = 0; count < MAX_RESET_WAIT; count++) { + if (macStateFlag & RX_ENABLE_CHECK) { + if (!OS_REG_IS_BIT_SET(ah, AR_CR, AR_CR_RXE)) { + macStateFlag &= ~RX_ENABLE_CHECK; + } + } + + if (macStateFlag & TX_ENABLE_CHECK) { + if (!OS_REG_IS_BIT_SET(ah, AR_Q_TXE, AR_Q_TXE_M)) { + macStateFlag &= ~TX_ENABLE_CHECK; + macStateFlag |= TX_QUEUEPEND_CHECK; + } + } + if (macStateFlag & TX_QUEUEPEND_CHECK) { + pendFrameCount = 0; + for (queue = 0; queue < AR_NUM_DCU; queue++) { + pendFrameCount += OS_REG_READ(ah, + AR_Q0_STS + (queue * 4)) & + AR_Q_STS_PEND_FR_CNT; + } + if (pendFrameCount == 0) { + macStateFlag &= ~TX_QUEUEPEND_CHECK; + } + } + if (macStateFlag == 0) { + status = AH_TRUE; + break; + } + OS_DELAY(50); + } + + if (status != AH_TRUE) { + HALDEBUG(ah, HAL_DEBUG_RESET, + "%s:Failed to stop the MAC state 0x%x\n", + __func__, macStateFlag); + } + + return status; +} + + /* * Write the given reset bit mask into the reset register */ @@ -1113,12 +1200,75 @@ ar5212SetResetReg(struct ath_hal *ah, uint32_t resetMask) uint32_t mask = resetMask ? resetMask : ~0; HAL_BOOL rt; - /* XXX ar5212MacStop & co. */ - + /* Never reset the PCIE core */ if (AH_PRIVATE(ah)->ah_ispcie) { resetMask &= ~AR_RC_PCI; } + if (resetMask & (AR_RC_MAC | AR_RC_PCI)) { + /* + * To ensure that the driver can reset the + * MAC, wake up the chip + */ + rt = ar5212SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE); + + if (rt != AH_TRUE) { + return rt; + } + + /* + * Disable interrupts + */ + OS_REG_WRITE(ah, AR_IER, AR_IER_DISABLE); + OS_REG_READ(ah, AR_IER); + + if (ar5212MacStop(ah) != AH_TRUE) { + /* + * Failed to stop the MAC gracefully; let's be more forceful then + */ + + /* need some delay before flush any pending MMR writes */ + OS_DELAY(15); + OS_REG_READ(ah, AR_RXDP); + + resetMask |= AR_RC_MAC | AR_RC_BB; + /* _Never_ reset PCI Express core */ + if (! AH_PRIVATE(ah)->ah_ispcie) { + resetMask |= AR_RC_PCI; + } +#if 0 + /* + * Flush the park address of the PCI controller + */ + /* Read PCI slot information less than Hainan revision */ + if (AH_PRIVATE(ah)->ah_bustype == HAL_BUS_TYPE_PCI) { + if (!IS_5112_REV5_UP(ah)) { +#define PCI_COMMON_CONFIG_STATUS 0x06 + u_int32_t i; + u_int16_t reg16; + + for (i = 0; i < 32; i++) { + ath_hal_read_pci_config_space(ah, + PCI_COMMON_CONFIG_STATUS, + ®16, sizeof(reg16)); + } + } +#undef PCI_COMMON_CONFIG_STATUS + } +#endif + } else { + /* + * MAC stopped gracefully; no need to warm-reset the PCI bus + */ + + resetMask &= ~AR_RC_PCI; + + /* need some delay before flush any pending MMR writes */ + OS_DELAY(15); + OS_REG_READ(ah, AR_RXDP); + } + } + (void) OS_REG_READ(ah, AR_RXDP);/* flush any pending MMR writes */ OS_REG_WRITE(ah, AR_RC, resetMask); OS_DELAY(15); /* need to wait at least 128 clocks @@ -1129,14 +1279,13 @@ ar5212SetResetReg(struct ath_hal *ah, uint32_t resetMask) if ((resetMask & AR_RC_MAC) == 0) { if (isBigEndian()) { /* - * Set CFG, little-endian for register - * and descriptor accesses. + * Set CFG, little-endian for descriptor accesses. */ - mask = INIT_CONFIG_STATUS | AR_CFG_SWRD | AR_CFG_SWRG; + mask = INIT_CONFIG_STATUS | AR_CFG_SWRD; #ifndef AH_NEED_DESC_SWAP mask |= AR_CFG_SWTD; #endif - OS_REG_WRITE(ah, AR_CFG, LE_READ_4(&mask)); + OS_REG_WRITE(ah, AR_CFG, mask); } else OS_REG_WRITE(ah, AR_CFG, INIT_CONFIG_STATUS); if (ar5212SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) @@ -1236,7 +1385,7 @@ ar5212GetNfHistMid(const int16_t calData[AR512_NF_CAL_HIST_MAX]) } /* - * Read the NF and check it against the noise floor threshhold + * Read the NF and check it against the noise floor threshold */ int16_t ar5212GetNf(struct ath_hal *ah, struct ieee80211_channel *chan) @@ -2459,6 +2608,12 @@ ar5212GetTargetPowers(struct ath_hal *ah, const struct ieee80211_channel *chan, powInfo[ixlo].twicePwr54, powInfo[ixhi].twicePwr54); } +static uint32_t +udiff(uint32_t u, uint32_t v) +{ + return (u >= v ? u - v : v - u); +} + /* * Search a list for a specified value v that is within * EEP_DELTA of the search values. Return the closest @@ -2493,7 +2648,7 @@ ar5212GetLowerUpperValues(uint16_t v, uint16_t *lp, uint16_t listSize, * If value is close to the current value of the list * then target is not between values, it is one of the values */ - if (abs(lp[0] * EEP_SCALE - target) < EEP_DELTA) { + if (udiff(lp[0] * EEP_SCALE, target) < EEP_DELTA) { *vlo = *vhi = lp[0]; return; } diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_rfgain.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_rfgain.c index f9fbc9ca5c..50c41c2527 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_rfgain.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_rfgain.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5212/ar5212_rfgain.c 188197 2009-02-05 21:13:31Z sam $ */ #include "opt_ah.h" diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_xmit.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_xmit.c index 3c6ddc0035..38ce4f01af 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_xmit.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212_xmit.c @@ -14,12 +14,13 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5212/ar5212_xmit.c 243169 2012-11-17 02:02:36Z adrian $ */ #include "opt_ah.h" #include "ah.h" #include "ah_internal.h" +#include "ah_desc.h" #include "ar5212/ar5212.h" #include "ar5212/ar5212reg.h" @@ -263,6 +264,7 @@ ar5212ReleaseTxQueue(struct ath_hal *ah, u_int q) * Assumes: * phwChannel has been set to point to the current channel */ +#define TU_TO_USEC(_tu) ((_tu) << 10) HAL_BOOL ar5212ResetTxQueue(struct ath_hal *ah, u_int q) { @@ -270,7 +272,7 @@ ar5212ResetTxQueue(struct ath_hal *ah, u_int q) HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps; const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan; HAL_TX_QUEUE_INFO *qi; - uint32_t cwMin, chanCwMin, value, qmisc, dmisc; + uint32_t cwMin, chanCwMin, qmisc, dmisc; if (q >= pCap->halTotalQueues) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid queue num %u\n", @@ -409,17 +411,40 @@ ar5212ResetTxQueue(struct ath_hal *ah, u_int q) | AR_Q_MISC_CBR_INCR_DIS1 | AR_Q_MISC_CBR_INCR_DIS0; - if (!qi->tqi_readyTime) { + if (qi->tqi_readyTime) { + HALDEBUG(ah, HAL_DEBUG_TXQUEUE, + "%s: using tqi_readyTime\n", __func__); + OS_REG_WRITE(ah, AR_QRDYTIMECFG(q), + SM(qi->tqi_readyTime, AR_Q_RDYTIMECFG_INT) | + AR_Q_RDYTIMECFG_ENA); + } else { + int value; /* * NB: don't set default ready time if driver * has explicitly specified something. This is * here solely for backwards compatibility. */ - value = (ahp->ah_beaconInterval + /* + * XXX for now, hard-code a CAB interval of 70% + * XXX of the total beacon interval. + */ + + value = (ahp->ah_beaconInterval * 70 / 100) - (ah->ah_config.ah_sw_beacon_response_time - - ah->ah_config.ah_dma_beacon_response_time) - - ah->ah_config.ah_additional_swba_backoff) * 1024; - OS_REG_WRITE(ah, AR_QRDYTIMECFG(q), value | AR_Q_RDYTIMECFG_ENA); + + ah->ah_config.ah_dma_beacon_response_time) + - ah->ah_config.ah_additional_swba_backoff; + /* + * XXX Ensure it isn't too low - nothing lower + * XXX than 10 TU + */ + if (value < 10) + value = 10; + HALDEBUG(ah, HAL_DEBUG_TXQUEUE, + "%s: defaulting to rdytime = %d uS\n", + __func__, value); + OS_REG_WRITE(ah, AR_QRDYTIMECFG(q), + SM(TU_TO_USEC(value), AR_Q_RDYTIMECFG_INT) | + AR_Q_RDYTIMECFG_ENA); } dmisc |= SM(AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL, AR_D_MISC_ARB_LOCKOUT_CNTRL); @@ -481,6 +506,7 @@ ar5212ResetTxQueue(struct ath_hal *ah, u_int q) return AH_TRUE; } +#undef TU_TO_USEC /* * Get the TXDP for the specified queue @@ -777,13 +803,17 @@ ar5212IntrReqTxDesc(struct ath_hal *ah, struct ath_desc *ds) HAL_BOOL ar5212FillTxDesc(struct ath_hal *ah, struct ath_desc *ds, - u_int segLen, HAL_BOOL firstSeg, HAL_BOOL lastSeg, + HAL_DMA_ADDR *bufAddrList, uint32_t *segLenList, u_int qcuId, + u_int descId, HAL_BOOL firstSeg, HAL_BOOL lastSeg, const struct ath_desc *ds0) { struct ar5212_desc *ads = AR5212DESC(ds); + uint32_t segLen = segLenList[0]; HALASSERT((segLen &~ AR_BufLen) == 0); + ds->ds_data = bufAddrList[0]; + if (firstSeg) { /* * First descriptor, don't clobber xmit control data @@ -796,12 +826,14 @@ ar5212FillTxDesc(struct ath_hal *ah, struct ath_desc *ds, * copy the multi-rate transmit parameters from * the first frame for processing on completion. */ - ads->ds_ctl0 = 0; ads->ds_ctl1 = segLen; #ifdef AH_NEED_DESC_SWAP + ads->ds_ctl0 = __bswap32(AR5212DESC_CONST(ds0)->ds_ctl0) + & AR_TxInterReq; ads->ds_ctl2 = __bswap32(AR5212DESC_CONST(ds0)->ds_ctl2); ads->ds_ctl3 = __bswap32(AR5212DESC_CONST(ds0)->ds_ctl3); #else + ads->ds_ctl0 = AR5212DESC_CONST(ds0)->ds_ctl0 & AR_TxInterReq; ads->ds_ctl2 = AR5212DESC_CONST(ds0)->ds_ctl2; ads->ds_ctl3 = AR5212DESC_CONST(ds0)->ds_ctl3; #endif @@ -809,7 +841,12 @@ ar5212FillTxDesc(struct ath_hal *ah, struct ath_desc *ds, /* * Intermediate descriptor in a multi-descriptor frame. */ - ads->ds_ctl0 = 0; +#ifdef AH_NEED_DESC_SWAP + ads->ds_ctl0 = __bswap32(AR5212DESC_CONST(ds0)->ds_ctl0) + & AR_TxInterReq; +#else + ads->ds_ctl0 = AR5212DESC_CONST(ds0)->ds_ctl0 & AR_TxInterReq; +#endif ads->ds_ctl1 = segLen | AR_More; ads->ds_ctl2 = 0; ads->ds_ctl3 = 0; @@ -939,3 +976,27 @@ ar5212GetTxCompletionRates(struct ath_hal *ah, const struct ath_desc *ds0, int * return AH_TRUE; } + +void +ar5212SetTxDescLink(struct ath_hal *ah, void *ds, uint32_t link) +{ + struct ar5212_desc *ads = AR5212DESC(ds); + + ads->ds_link = link; +} + +void +ar5212GetTxDescLink(struct ath_hal *ah, void *ds, uint32_t *link) +{ + struct ar5212_desc *ads = AR5212DESC(ds); + + *link = ads->ds_link; +} + +void +ar5212GetTxDescLinkPtr(struct ath_hal *ah, void *ds, uint32_t **linkptr) +{ + struct ar5212_desc *ads = AR5212DESC(ds); + + *linkptr = &ads->ds_link; +} diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212desc.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212desc.h index c6af68b7b7..0e1c09f3c4 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212desc.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212desc.h @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5212/ar5212desc.h 243168 2012-11-17 02:00:33Z adrian $ */ #ifndef _ATH_AR5212_DESC_H_ #define _ATH_AR5212_DESC_H_ @@ -22,7 +22,6 @@ /* * Hardware-specific descriptor structures. */ -#include "ah_desc.h" /* * AR5212-specific tx/rx descriptor definition. diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212phy.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212phy.h index 12cd3dfa29..8776602d58 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212phy.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212phy.h @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5212/ar5212phy.h 239801 2012-08-29 03:58:13Z adrian $ */ #ifndef _DEV_ATH_AR5212PHY_H_ #define _DEV_ATH_AR5212PHY_H_ @@ -221,6 +221,19 @@ #define AR_PHY_RADAR_0_FIRPWR 0x7F000000 /* Radar firpwr threshold */ #define AR_PHY_RADAR_0_FIRPWR_S 24 +/* ar5413 specific */ +#define AR_PHY_RADAR_2 0x9958 /* radar detection settings */ +#define AR_PHY_RADAR_2_ENRELSTEPCHK 0x00002000 /* Enable using max rssi */ +#define AR_PHY_RADAR_2_ENMAXRSSI 0x00004000 /* Enable using max rssi */ +#define AR_PHY_RADAR_2_BLOCKOFDMWEAK 0x00008000 /* En block OFDM weak sig as radar */ +#define AR_PHY_RADAR_2_USEFIR128 0x00400000 /* En measuring pwr over 128 cycles */ +#define AR_PHY_RADAR_2_ENRELPWRCHK 0x00800000 /* Enable using max rssi */ +#define AR_PHY_RADAR_2_MAXLEN 0x000000FF /* Max Pulse duration threshold */ +#define AR_PHY_RADAR_2_MAXLEN_S 0 +#define AR_PHY_RADAR_2_RELSTEP 0x00001F00 /* Pulse relative step threshold */ +#define AR_PHY_RADAR_2_RELSTEP_S 8 +#define AR_PHY_RADAR_2_RELPWR 0x003F0000 /* pulse relative power threshold */ +#define AR_PHY_RADAR_2_RELPWR_S 16 #define AR_PHY_SIGMA_DELTA 0x996C /* AR5312 only */ #define AR_PHY_SIGMA_DELTA_ADC_SEL 0x00000003 diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212reg.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212reg.h index 15c1a58f7d..e06d63b8e6 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212reg.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5212reg.h @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5212/ar5212reg.h 226489 2011-10-18 03:17:06Z adrian $ */ #ifndef _DEV_ATH_AR5212REG_H_ #define _DEV_ATH_AR5212REG_H_ @@ -94,7 +94,9 @@ #define AR_QTXDP(_i) (AR_Q0_TXDP + ((_i)<<2)) #define AR_Q_TXE 0x0840 /* MAC Transmit Queue enable */ +#define AR_Q_TXE_M 0x000003FF /* Mask for TXE (QCU 0-9) */ #define AR_Q_TXD 0x0880 /* MAC Transmit Queue disable */ +#define AR_Q_TXD_M 0x000003FF /* Mask for TXD (QCU 0-9) */ #define AR_Q0_CBRCFG 0x08c0 /* MAC CBR configuration */ #define AR_Q1_CBRCFG 0x08c4 /* MAC CBR configuration */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5311reg.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5311reg.h index b79370ebba..bdd01fbf13 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5311reg.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5311reg.h @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5212/ar5311reg.h 204644 2010-03-03 17:32:32Z rpaulo $ */ #ifndef _DEV_ATH_AR5311REG_H_ #define _DEV_ATH_AR5311REG_H_ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5413.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5413.c index ab6cc65008..1613ac7cb4 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5413.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5212/ar5413.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5212/ar5413.c 298939 2016-05-02 19:56:48Z pfg $ */ #include "opt_ah.h" @@ -411,9 +411,9 @@ ar5413getGainBoundariesAndPdadcsForPowers(struct ath_hal *ah, uint16_t channel, * change pwr_I_0 to signed 5-bits. */ int16_t Pmin_t2[MAX_NUM_PDGAINS_PER_CHANNEL]; - /* to accomodate -ve power levels later on. */ + /* to accommodate -ve power levels later on. */ int16_t Pmax_t2[MAX_NUM_PDGAINS_PER_CHANNEL]; - /* to accomodate -ve power levels later on */ + /* to accommodate -ve power levels later on */ uint16_t numVpd = 0; uint16_t Vpd_step; int16_t tmpVal ; diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312.h index 0fb9b750c3..1adb4c6d3b 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312.h @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5312/ar5312.h 290612 2015-11-09 15:59:42Z adrian $ */ #ifndef _ATH_AR5312_H_ #define _ATH_AR5312_H_ @@ -62,8 +62,10 @@ extern void ar5312SetupClock(struct ath_hal *ah, HAL_OPMODE opmode); extern void ar5312RestoreClock(struct ath_hal *ah, HAL_OPMODE opmode); extern void ar5312DumpState(struct ath_hal *ah); extern HAL_BOOL ar5312Reset(struct ath_hal *ah, HAL_OPMODE opmode, - struct ieee80211_channel *chan, - HAL_BOOL bChannelChange, HAL_STATUS *status); + struct ieee80211_channel *chan, + HAL_BOOL bChannelChange, + HAL_RESET_TYPE resetType, + HAL_STATUS *status); extern HAL_BOOL ar5312ChipReset(struct ath_hal *ah, struct ieee80211_channel *chan); extern HAL_BOOL ar5312SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312_attach.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312_attach.c index d1689d59fc..4339e7bb41 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312_attach.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312_attach.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5312/ar5312_attach.c 272292 2014-09-30 03:19:29Z adrian $ */ #include "opt_ah.h" @@ -62,7 +62,7 @@ ar5312AniSetup(struct ath_hal *ah) static struct ath_hal * ar5312Attach(uint16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, - HAL_STATUS *status) + HAL_OPS_CONFIG *ah_config, HAL_STATUS *status) { struct ath_hal_5212 *ahp = AH_NULL; struct ath_hal *ah; @@ -71,13 +71,13 @@ ar5312Attach(uint16_t devid, HAL_SOFTC sc, uint16_t eeval; HAL_STATUS ecode; - HALDEBUG_G(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n", + HALDEBUG(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n", __func__, sc, st, (void*) sh); /* NB: memory is returned zero'd */ ahp = ath_hal_malloc(sizeof (struct ath_hal_5212)); if (ahp == AH_NULL) { - HALDEBUG_G(AH_NULL, HAL_DEBUG_ANY, + HALDEBUG(AH_NULL, HAL_DEBUG_ANY, "%s: cannot allocate memory for state block\n", __func__); *status = HAL_ENOMEM; return AH_NULL; diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312_eeprom.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312_eeprom.c index 7a84232e0b..ec44d71583 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312_eeprom.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312_eeprom.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5312/ar5312_eeprom.c 204644 2010-03-03 17:32:32Z rpaulo $ */ #include "opt_ah.h" diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312_gpio.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312_gpio.c index 20a575dd20..4b7765c7b9 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312_gpio.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312_gpio.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5312/ar5312_gpio.c 188974 2009-02-24 00:12:16Z sam $ */ #include "opt_ah.h" diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312_interrupts.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312_interrupts.c index 0a1545c075..6c07a1e7dd 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312_interrupts.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312_interrupts.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5312/ar5312_interrupts.c 204644 2010-03-03 17:32:32Z rpaulo $ */ #include "opt_ah.h" diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312_misc.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312_misc.c index 3d85ece7d5..a7f4707c63 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312_misc.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312_misc.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5312/ar5312_misc.c 204644 2010-03-03 17:32:32Z rpaulo $ */ #include "opt_ah.h" diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312_power.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312_power.c index 94a0f1c64b..d16e9fa407 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312_power.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312_power.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5312/ar5312_power.c 262969 2014-03-10 06:03:35Z adrian $ */ #include "opt_ah.h" @@ -71,7 +71,6 @@ ar5312SetPowerModeNetworkSleep(struct ath_hal *ah, int setChip) HAL_BOOL ar5312SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip) { - struct ath_hal_5212 *ahp = AH5212(ah); #ifdef AH_DEBUG static const char* modes[] = { "AWAKE", @@ -83,7 +82,7 @@ ar5312SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip) int status = AH_TRUE; HALDEBUG(ah, HAL_DEBUG_POWER, "%s: %s -> %s (%s)\n", __func__, - modes[ahp->ah_powerMode], modes[mode], + modes[ah->ah_powerMode], modes[mode], setChip ? "set chip " : ""); switch (mode) { case HAL_PM_AWAKE: @@ -100,7 +99,7 @@ ar5312SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip) __func__, mode); return AH_FALSE; } - ahp->ah_powerMode = mode; + ah->ah_powerMode = mode; return status; } diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312_reset.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312_reset.c index dda3fafcd6..d506bf74fb 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312_reset.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312_reset.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5312/ar5312_reset.c 290612 2015-11-09 15:59:42Z adrian $ */ #include "opt_ah.h" @@ -88,7 +88,9 @@ write_common(struct ath_hal *ah, const HAL_INI_ARRAY *ia, HAL_BOOL ar5312Reset(struct ath_hal *ah, HAL_OPMODE opmode, struct ieee80211_channel *chan, - HAL_BOOL bChannelChange, HAL_STATUS *status) + HAL_BOOL bChannelChange, + HAL_RESET_TYPE resetType, + HAL_STATUS *status) { #define N(a) (sizeof (a) / sizeof (a[0])) #define FAIL(_code) do { ecode = _code; goto bad; } while (0) @@ -740,8 +742,7 @@ ar5312SetResetReg(struct ath_hal *ah, uint32_t resetMask) if ((resetMask & AR_RC_MAC) == 0) { if (isBigEndian()) { /* - * Set CFG, little-endian for register - * and descriptor accesses. + * Set CFG, little-endian for descriptor accesses. */ #ifdef AH_NEED_DESC_SWAP mask = INIT_CONFIG_STATUS | AR_CFG_SWRD; diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312phy.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312phy.h index fcb8564c77..83cc88c770 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312phy.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312phy.h @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5312/ar5312phy.h 204644 2010-03-03 17:32:32Z rpaulo $ */ #ifndef _DEV_ATH_AR5312PHY_H_ #define _DEV_ATH_AR5312PHY_H_ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312reg.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312reg.h index bdfeffa966..7407bb06ab 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312reg.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5312reg.h @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5312/ar5312reg.h 204644 2010-03-03 17:32:32Z rpaulo $ */ #ifndef _DEV_ATH_AR5312REG_H_ #define _DEV_ATH_AR5312REG_H_ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5315_gpio.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5315_gpio.c index 4c04c86db0..9c37430d58 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5315_gpio.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5312/ar5315_gpio.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5312/ar5315_gpio.c 188974 2009-02-24 00:12:16Z sam $ */ #include "opt_ah.h" diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar2133.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar2133.c index f25c108267..e0134faf50 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar2133.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar2133.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5416/ar2133.c 241195 2012-10-04 15:42:45Z adrian $ */ #include "opt_ah.h" @@ -163,6 +163,33 @@ ar2133SetChannel(struct ath_hal *ah, const struct ieee80211_channel *chan) OS_REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, txctl &~ AR_PHY_CCK_TX_CTRL_JAPAN); } + /* + * Handle programming the RF synth for odd frequencies in the + * 4.9->5GHz range. This matches the programming from the + * later model 802.11abg RF synths. + * + * This interoperates on the quarter rate channels with the + * AR5112 and later RF synths. Please note that the synthesiser + * isn't able to completely accurately represent these frequencies + * (as the resolution in this reference is 2.5MHz) and thus it will + * be slightly "off centre." This matches the same slightly + * incorrect * centre frequency behaviour that the AR5112 and later + * channel selection code has. + * + * This is disabled because it hasn't been tested for regulatory + * compliance and neither have the NICs which would use it. + * So if you enable this code, you must first ensure that you've + * re-certified the NICs in question beforehand or you will be + * violating your local regulatory rules and breaking the law. + */ +#if 0 + } else if (((freq % 5) == 2) && (freq <= 5435)) { + freq = freq - 2; + channelSel = ath_hal_reverseBits( + (uint32_t) (((freq - 4800) * 10) / 25 + 1), 8); + /* XXX what about for Howl/Sowl? */ + aModeRefSel = ath_hal_reverseBits(0, 2); +#endif } else if ((freq % 20) == 0 && freq >= 5120) { channelSel = ath_hal_reverseBits(((freq - 4800) / 20 << 2), 8); if (AR_SREV_HOWL(ah) || AR_SREV_SOWL_10_OR_LATER(ah)) @@ -179,7 +206,8 @@ ar2133SetChannel(struct ath_hal *ah, const struct ieee80211_channel *chan) channelSel = ath_hal_reverseBits((freq - 4800) / 5, 8); aModeRefSel = ath_hal_reverseBits(1, 2); } else { - HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel %u MHz\n", + HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, + "%s: invalid channel %u MHz\n", __func__, freq); return AH_FALSE; } @@ -549,3 +577,11 @@ ar2133RfAttach(struct ath_hal *ah, HAL_STATUS *status) return AH_TRUE; } + +static HAL_BOOL +ar2133Probe(struct ath_hal *ah) +{ + return (AR_SREV_OWL(ah) || AR_SREV_HOWL(ah) || AR_SREV_SOWL(ah)); +} + +AH_RF(RF2133, ar2133Probe, ar2133RfAttach); diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416.h index a0f5deead8..3e8e032c57 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416.h @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5416/ar5416.h 290616 2015-11-09 20:22:59Z garga $ */ #ifndef _ATH_AR5416_H_ #define _ATH_AR5416_H_ @@ -107,16 +107,21 @@ struct ath_hal_5416 { void (*ah_initPLL) (struct ath_hal *ah, const struct ieee80211_channel *chan); + /* bluetooth coexistence operations */ + void (*ah_btCoexSetDiversity)(struct ath_hal *ah); + u_int ah_globaltxtimeout; /* global tx timeout */ u_int ah_gpioMask; int ah_hangs; /* h/w hangs state */ uint8_t ah_keytype[AR5416_KEYTABLE_SIZE]; /* - * Extension Channel Rx Clear State + * Primary/Extension Channel Tx, Rx, Rx Clear State */ uint32_t ah_cycleCount; uint32_t ah_ctlBusy; uint32_t ah_extBusy; + uint32_t ah_rxBusy; + uint32_t ah_txBusy; uint32_t ah_rx_chainmask; uint32_t ah_tx_chainmask; @@ -127,9 +132,36 @@ struct ath_hal_5416 { struct ar5416NfLimits nf_2g; struct ar5416NfLimits nf_5g; + /* + * TX power configuration related structures + */ int initPDADC; + int ah_ht40PowerIncForPdadc; + int16_t ah_ratesArray[Ar5416RateSize]; int ah_need_an_top2_fixup; /* merlin or later chips that may need this workaround */ + + /* + * Bluetooth coexistence static setup according to the registry + */ + HAL_BT_MODULE ah_btModule; /* Bluetooth module identifier */ + uint8_t ah_btCoexConfigType; /* BT coex configuration */ + uint8_t ah_btActiveGpioSelect; /* GPIO pin for BT_ACTIVE */ + uint8_t ah_btPriorityGpioSelect; /* GPIO pin for BT_PRIORITY */ + uint8_t ah_wlanActiveGpioSelect; /* GPIO pin for WLAN_ACTIVE */ + uint8_t ah_btActivePolarity; /* Polarity of BT_ACTIVE */ + HAL_BOOL ah_btCoexSingleAnt; /* Single or dual antenna configuration */ + uint8_t ah_btWlanIsolation; /* Isolation between BT and WLAN in dB */ + + /* + * Bluetooth coexistence runtime settings + */ + HAL_BOOL ah_btCoexEnabled; /* If Bluetooth coexistence is enabled */ + uint32_t ah_btCoexMode; /* Register setting for AR_BT_COEX_MODE */ + uint32_t ah_btCoexBTWeight; /* Register setting for AR_BT_COEX_WEIGHT */ + uint32_t ah_btCoexWLANWeight; /* Register setting for AR_BT_COEX_WEIGHT */ + uint32_t ah_btCoexMode2; /* Register setting for AR_BT_COEX_MODE2 */ + uint32_t ah_btCoexFlag; /* Special tuning flags for BT coex */ }; #define AH5416(_ah) ((struct ath_hal_5416 *)(_ah)) @@ -171,6 +203,22 @@ extern void ar5416SetStaBeaconTimers(struct ath_hal *ah, const HAL_BEACON_STATE *); extern uint64_t ar5416GetNextTBTT(struct ath_hal *); +/* ar5416_btcoex.c */ +extern void ar5416SetBTCoexInfo(struct ath_hal *ah, + HAL_BT_COEX_INFO *btinfo); +extern void ar5416BTCoexConfig(struct ath_hal *ah, + HAL_BT_COEX_CONFIG *btconf); +extern void ar5416BTCoexAntennaDiversity(struct ath_hal *ah); +extern void ar5416BTCoexSetQcuThresh(struct ath_hal *ah, int qnum); +extern void ar5416BTCoexSetWeights(struct ath_hal *ah, uint32_t stompType); +extern void ar5416BTCoexSetupBmissThresh(struct ath_hal *ah, + uint32_t thresh); +extern void ar5416BTCoexSetParameter(struct ath_hal *ah, uint32_t type, + uint32_t value); +extern void ar5416BTCoexDisable(struct ath_hal *ah); +extern int ar5416BTCoexEnable(struct ath_hal *ah); +extern void ar5416InitBTCoex(struct ath_hal *ah); + extern HAL_BOOL ar5416EepromRead(struct ath_hal *, u_int off, uint16_t *data); extern HAL_BOOL ar5416EepromWrite(struct ath_hal *, u_int off, uint16_t data); @@ -190,9 +238,13 @@ extern void ar5416SetLedState(struct ath_hal *ah, HAL_LED_STATE state); extern uint64_t ar5416GetTsf64(struct ath_hal *ah); extern void ar5416SetTsf64(struct ath_hal *ah, uint64_t tsf64); extern void ar5416ResetTsf(struct ath_hal *ah); +extern uint32_t ar5416GetCurRssi(struct ath_hal *ah); extern HAL_BOOL ar5416SetAntennaSwitch(struct ath_hal *, HAL_ANT_SETTING); extern HAL_BOOL ar5416SetDecompMask(struct ath_hal *, uint16_t, int); extern void ar5416SetCoverageClass(struct ath_hal *, uint8_t, int); +extern HAL_BOOL ar5416GetMibCycleCounts(struct ath_hal *ah, + HAL_SURVEY_SAMPLE *hsample); +extern void ar5416SetChainMasks(struct ath_hal *ah, uint32_t, uint32_t); extern uint32_t ar5416Get11nExtBusy(struct ath_hal *ah); extern void ar5416Set11nMac2040(struct ath_hal *ah, HAL_HT_MACMODE mode); extern HAL_HT_RXCLEAR ar5416Get11nRxClear(struct ath_hal *ah); @@ -201,18 +253,34 @@ extern HAL_STATUS ar5416SetQuiet(struct ath_hal *ah, uint32_t period, uint32_t duration, uint32_t nextStart, HAL_QUIET_FLAG flag); extern HAL_STATUS ar5416GetCapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type, uint32_t capability, uint32_t *result); +extern HAL_BOOL ar5416SetCapability(struct ath_hal *ah, + HAL_CAPABILITY_TYPE type, uint32_t capability, uint32_t val, + HAL_STATUS *status); extern HAL_BOOL ar5416GetDiagState(struct ath_hal *ah, int request, const void *args, uint32_t argsize, void **result, uint32_t *resultsize); extern HAL_BOOL ar5416SetRifsDelay(struct ath_hal *ah, const struct ieee80211_channel *chan, HAL_BOOL enable); + extern void ar5416EnableDfs(struct ath_hal *ah, HAL_PHYERR_PARAM *pe); +extern HAL_BOOL ar5416GetDfsDefaultThresh(struct ath_hal *ah, + HAL_PHYERR_PARAM *pe); extern void ar5416GetDfsThresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe); extern HAL_BOOL ar5416ProcessRadarEvent(struct ath_hal *ah, struct ath_rx_status *rxs, uint64_t fulltsf, const char *buf, HAL_DFS_EVENT *event); extern HAL_BOOL ar5416IsFastClockEnabled(struct ath_hal *ah); +/* ar9280_spectral.c */ +extern void ar5416ConfigureSpectralScan(struct ath_hal *ah, HAL_SPECTRAL_PARAM *ss); +extern void ar5416GetSpectralParams(struct ath_hal *ah, HAL_SPECTRAL_PARAM *ss); +extern HAL_BOOL ar5416IsSpectralActive(struct ath_hal *ah); +extern HAL_BOOL ar5416IsSpectralEnabled(struct ath_hal *ah); +extern void ar5416StartSpectralScan(struct ath_hal *ah); +extern void ar5416StopSpectralScan(struct ath_hal *ah); +extern uint32_t ar5416GetSpectralConfig(struct ath_hal *ah); +extern void ar5416RestoreSpectralConfig(struct ath_hal *ah, uint32_t restoreval); + extern HAL_BOOL ar5416SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip); extern HAL_POWER_MODE ar5416GetPowerMode(struct ath_hal *ah); @@ -224,6 +292,7 @@ extern HAL_BOOL ar5416SetKeyCacheEntry(struct ath_hal *ah, uint16_t entry, extern uint32_t ar5416GetRxFilter(struct ath_hal *ah); extern void ar5416SetRxFilter(struct ath_hal *ah, uint32_t bits); +extern HAL_BOOL ar5416StopDmaReceive(struct ath_hal *ah); extern void ar5416StartPcuReceive(struct ath_hal *ah); extern void ar5416StopPcuReceive(struct ath_hal *ah); extern HAL_BOOL ar5416SetupRxDesc(struct ath_hal *, @@ -234,7 +303,9 @@ extern HAL_STATUS ar5416ProcRxDesc(struct ath_hal *ah, struct ath_desc *, extern HAL_BOOL ar5416Reset(struct ath_hal *ah, HAL_OPMODE opmode, struct ieee80211_channel *chan, - HAL_BOOL bChannelChange, HAL_STATUS *status); + HAL_BOOL bChannelChange, + HAL_RESET_TYPE, + HAL_STATUS *status); extern HAL_BOOL ar5416PhyDisable(struct ath_hal *ah); extern HAL_RFGAIN ar5416GetRfgain(struct ath_hal *ah); extern HAL_BOOL ar5416Disable(struct ath_hal *ah); @@ -313,7 +384,8 @@ extern HAL_BOOL ar5416SetupXTxDesc(struct ath_hal *, struct ath_desc *, u_int txRate2, u_int txRetries2, u_int txRate3, u_int txRetries3); extern HAL_BOOL ar5416FillTxDesc(struct ath_hal *ah, struct ath_desc *ds, - u_int segLen, HAL_BOOL firstSeg, HAL_BOOL lastSeg, + HAL_DMA_ADDR *bufAddrList, uint32_t *segLenList, + u_int descId, u_int qcuId, HAL_BOOL firstSeg, HAL_BOOL lastSeg, const struct ath_desc *ds0); extern HAL_STATUS ar5416ProcTxDesc(struct ath_hal *ah, struct ath_desc *, struct ath_tx_status *); @@ -325,9 +397,10 @@ extern int ar5416SetupTxQueue(struct ath_hal *ah, HAL_TX_QUEUE type, const HAL_TXQ_INFO *qInfo); extern HAL_BOOL ar5416ChainTxDesc(struct ath_hal *ah, struct ath_desc *ds, + HAL_DMA_ADDR *bufAddrList, uint32_t *segLenList, u_int pktLen, u_int hdrLen, HAL_PKT_TYPE type, u_int keyIx, - HAL_CIPHER cipher, uint8_t delims, u_int segLen, HAL_BOOL firstSeg, - HAL_BOOL lastSeg); + HAL_CIPHER cipher, uint8_t delims, + HAL_BOOL firstSeg, HAL_BOOL lastSeg, HAL_BOOL lastAggr); extern HAL_BOOL ar5416SetupFirstTxDesc(struct ath_hal *ah, struct ath_desc *ds, u_int aggrLen, u_int flags, u_int txPower, u_int txRate0, u_int txTries0, u_int antMode, u_int rtsctsRate, u_int rtsctsDuration); @@ -338,8 +411,15 @@ extern u_int ar5416GetGlobalTxTimeout(struct ath_hal *ah); extern void ar5416Set11nRateScenario(struct ath_hal *ah, struct ath_desc *ds, u_int durUpdateEn, u_int rtsctsRate, HAL_11N_RATE_SERIES series[], u_int nseries, u_int flags); + +extern void ar5416Set11nAggrFirst(struct ath_hal *ah, struct ath_desc *ds, + u_int aggrLen, u_int numDelims); extern void ar5416Set11nAggrMiddle(struct ath_hal *ah, struct ath_desc *ds, u_int numDelims); +extern void ar5416Set11nAggrLast(struct ath_hal *ah, struct ath_desc *ds); extern void ar5416Clr11nAggr(struct ath_hal *ah, struct ath_desc *ds); +extern void ar5416Set11nVirtualMoreFrag(struct ath_hal *ah, + struct ath_desc *ds, u_int vmf); + extern void ar5416Set11nBurstDuration(struct ath_hal *ah, struct ath_desc *ds, u_int burstDuration); extern const HAL_RATE_TABLE *ar5416GetRateTable(struct ath_hal *, u_int mode); diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416.ini b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416.ini index 7f8c7934c0..4bb34bf2b6 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416.ini +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416.ini @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5416/ar5416.ini 225420 2011-09-06 10:49:05Z adrian $ */ /* Auto Generated PCI Register Writes. Created: 09/20/06 */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_ani.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_ani.c index 82e00ed977..3135afb08a 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_ani.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_ani.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5416/ar5416_ani.c 280828 2015-03-29 21:50:21Z adrian $ */ #include "opt_ah.h" @@ -227,7 +227,7 @@ ar5416AniControl(struct ath_hal *ah, HAL_ANI_CMD cmd, int param) u_int level = param; HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_NOISE_IMMUNITY_LEVEL: set level = %d\n", __func__, level); - if (level >= params->maxNoiseImmunityLevel) { + if (level > params->maxNoiseImmunityLevel) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: immunity level out of range (%u > %u)\n", __func__, level, params->maxNoiseImmunityLevel); @@ -314,7 +314,7 @@ ar5416AniControl(struct ath_hal *ah, HAL_ANI_CMD cmd, int param) u_int level = param; HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_FIRSTEP_LEVEL: level = %d\n", __func__, level); - if (level >= params->maxFirstepLevel) { + if (level > params->maxFirstepLevel) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: firstep level out of range (%u > %u)\n", __func__, level, params->maxFirstepLevel); @@ -333,7 +333,7 @@ ar5416AniControl(struct ath_hal *ah, HAL_ANI_CMD cmd, int param) u_int level = param; HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_SPUR_IMMUNITY_LEVEL: level = %d\n", __func__, level); - if (level >= params->maxSpurImmunityLevel) { + if (level > params->maxSpurImmunityLevel) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: spur immunity level out of range (%u > %u)\n", __func__, level, params->maxSpurImmunityLevel); @@ -379,20 +379,30 @@ ar5416AniOfdmErrTrigger(struct ath_hal *ah) aniState = ahp->ah_curani; params = aniState->params; /* First, raise noise immunity level, up to max */ - if ((AH5416(ah)->ah_ani_function & (1 << HAL_ANI_NOISE_IMMUNITY_LEVEL)) && - (aniState->noiseImmunityLevel+1 < params->maxNoiseImmunityLevel)) { - ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, - aniState->noiseImmunityLevel + 1); - return; + if (aniState->noiseImmunityLevel+1 < params->maxNoiseImmunityLevel) { + if (ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, + aniState->noiseImmunityLevel + 1)) + return; } /* then, raise spur immunity level, up to max */ - if ((AH5416(ah)->ah_ani_function & (1 << HAL_ANI_SPUR_IMMUNITY_LEVEL)) && - (aniState->spurImmunityLevel+1 < params->maxSpurImmunityLevel)) { - ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, - aniState->spurImmunityLevel + 1); - return; + if (aniState->spurImmunityLevel+1 < params->maxSpurImmunityLevel) { + if (ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, + aniState->spurImmunityLevel + 1)) + return; } + /* + * In the case of AP mode operation, we cannot bucketize beacons + * according to RSSI. Instead, raise Firstep level, up to max, and + * simply return. + */ + if (AH_PRIVATE(ah)->ah_opmode == HAL_M_HOSTAP) { + if (aniState->firstepLevel < params->maxFirstepLevel) { + if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, + aniState->firstepLevel + 1)) + return; + } + } if (ANI_ENA_RSSI(ah)) { int32_t rssi = BEACON_RSSI(ahp); if (rssi > params->rssiThrHigh) { @@ -412,10 +422,10 @@ ar5416AniOfdmErrTrigger(struct ath_hal *ah) * If weak sig detect is already off, as last resort, * raise firstep level */ - if (aniState->firstepLevel+1 < params->maxFirstepLevel) { - ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, - aniState->firstepLevel + 1); - return; + if (aniState->firstepLevel < params->maxFirstepLevel) { + if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, + aniState->firstepLevel + 1)) + return; } } else if (rssi > params->rssiThrLow) { /* @@ -426,10 +436,10 @@ ar5416AniOfdmErrTrigger(struct ath_hal *ah) ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, AH_TRUE); - if (aniState->firstepLevel+1 < params->maxFirstepLevel) - ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, - aniState->firstepLevel + 1); - return; + if (aniState->firstepLevel < params->maxFirstepLevel) + if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, + aniState->firstepLevel + 1)) + return; } else { /* * Beacon rssi is low, if in 11b/g mode, turn off ofdm @@ -442,9 +452,9 @@ ar5416AniOfdmErrTrigger(struct ath_hal *ah) HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, AH_FALSE); if (aniState->firstepLevel > 0) - ar5416AniControl(ah, - HAL_ANI_FIRSTEP_LEVEL, 0); - return; + if (ar5416AniControl(ah, + HAL_ANI_FIRSTEP_LEVEL, 0)) + return; } } } @@ -480,7 +490,7 @@ ar5416AniCckErrTrigger(struct ath_hal *ah) * Beacon signal in mid and high range, * raise firstep level. */ - if (aniState->firstepLevel+1 < params->maxFirstepLevel) + if (aniState->firstepLevel < params->maxFirstepLevel) ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, aniState->firstepLevel + 1); } else { @@ -577,6 +587,16 @@ ar5416AniReset(struct ath_hal *ah, const struct ieee80211_channel *chan, goto finish; } + /* + * Use a restrictive set of ANI parameters for hostap mode. + */ + if (opmode == HAL_M_HOSTAP) { + if (IEEE80211_IS_CHAN_2GHZ(chan)) + AH5416(ah)->ah_ani_function = + HAL_ANI_SPUR_IMMUNITY_LEVEL | HAL_ANI_FIRSTEP_LEVEL; + else + AH5416(ah)->ah_ani_function = 0; + } /* * Automatic processing is done only in station mode right now. @@ -606,7 +626,7 @@ ar5416AniReset(struct ath_hal *ah, const struct ieee80211_channel *chan, ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 0); ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 0); ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, - AH_TRUE); + AH_FALSE); ar5416AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR, AH_FALSE); ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 0); ichan->privFlags |= CHANNEL_ANI_SETUP; @@ -710,6 +730,19 @@ ar5416AniLowerImmunity(struct ath_hal *ah) aniState = ahp->ah_curani; params = aniState->params; + + /* + * In the case of AP mode operation, we cannot bucketize beacons + * according to RSSI. Instead, lower Firstep level, down to min, and + * simply return. + */ + if (AH_PRIVATE(ah)->ah_opmode == HAL_M_HOSTAP) { + if (aniState->firstepLevel > 0) { + if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, + aniState->firstepLevel - 1)) + return; + } + } if (ANI_ENA_RSSI(ah)) { int32_t rssi = BEACON_RSSI(ahp); if (rssi > params->rssiThrHigh) { @@ -724,41 +757,41 @@ ar5416AniLowerImmunity(struct ath_hal *ah) * detection or lower firstep level. */ if (aniState->ofdmWeakSigDetectOff) { - ar5416AniControl(ah, + if (ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, - AH_TRUE); - return; + AH_TRUE)) + return; } if (aniState->firstepLevel > 0) { - ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, - aniState->firstepLevel - 1); - return; + if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, + aniState->firstepLevel - 1)) + return; } } else { /* * Beacon rssi is low, reduce firstep level. */ if (aniState->firstepLevel > 0) { - ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, - aniState->firstepLevel - 1); - return; + if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, + aniState->firstepLevel - 1)) + return; } } } /* then lower spur immunity level, down to zero */ if (aniState->spurImmunityLevel > 0) { - ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, - aniState->spurImmunityLevel - 1); - return; + if (ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, + aniState->spurImmunityLevel - 1)) + return; } /* * if all else fails, lower noise immunity level down to a min value * zero for now */ if (aniState->noiseImmunityLevel > 0) { - ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, - aniState->noiseImmunityLevel - 1); - return; + if (ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, + aniState->noiseImmunityLevel - 1)) + return; } } @@ -771,21 +804,41 @@ ar5416AniLowerImmunity(struct ath_hal *ah) * deducting the cycles spent tx'ing and rx'ing from the total * cycle count since our last call. A return value <0 indicates * an invalid/inconsistent time. + * + * This may be called with ANI disabled; in which case simply keep + * the statistics and don't write to the aniState pointer. + * + * XXX TODO: Make this cleaner! */ static int32_t ar5416AniGetListenTime(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); - struct ar5212AniState *aniState; - uint32_t txFrameCount, rxFrameCount, cycleCount; - int32_t listenTime; + struct ar5212AniState *aniState = NULL; + int32_t listenTime = 0; + int good; + HAL_SURVEY_SAMPLE hs; - txFrameCount = OS_REG_READ(ah, AR_TFCNT); - rxFrameCount = OS_REG_READ(ah, AR_RFCNT); - cycleCount = OS_REG_READ(ah, AR_CCCNT); + /* + * We shouldn't see ah_curchan be NULL, but just in case.. + */ + if (AH_PRIVATE(ah)->ah_curchan == AH_NULL) { + ath_hal_printf(ah, "%s: ah_curchan = NULL?\n", __func__); + return (0); + } - aniState = ahp->ah_curani; - if (aniState->cycleCount == 0 || aniState->cycleCount > cycleCount) { + /* + * Fetch the current statistics, squirrel away the current + * sample. + */ + OS_MEMZERO(&hs, sizeof(hs)); + good = ar5416GetMibCycleCounts(ah, &hs); + ath_hal_survey_add_sample(ah, &hs); + + if (ANI_ENA(ah)) + aniState = ahp->ah_curani; + + if (good == AH_FALSE) { /* * Cycle counter wrap (or initial call); it's not possible * to accurately calculate a value because the registers @@ -793,15 +846,29 @@ ar5416AniGetListenTime(struct ath_hal *ah) */ listenTime = 0; ahp->ah_stats.ast_ani_lzero++; - } else { - int32_t ccdelta = cycleCount - aniState->cycleCount; - int32_t rfdelta = rxFrameCount - aniState->rxFrameCount; - int32_t tfdelta = txFrameCount - aniState->txFrameCount; + } else if (ANI_ENA(ah)) { + /* + * Only calculate and update the cycle count if we have + * an ANI state. + */ + int32_t ccdelta = + AH5416(ah)->ah_cycleCount - aniState->cycleCount; + int32_t rfdelta = + AH5416(ah)->ah_rxBusy - aniState->rxFrameCount; + int32_t tfdelta = + AH5416(ah)->ah_txBusy - aniState->txFrameCount; listenTime = (ccdelta - rfdelta - tfdelta) / CLOCK_RATE; } - aniState->cycleCount = cycleCount; - aniState->txFrameCount = txFrameCount; - aniState->rxFrameCount = rxFrameCount; + + /* + * Again, only update ANI state if we have it. + */ + if (ANI_ENA(ah)) { + aniState->cycleCount = AH5416(ah)->ah_cycleCount; + aniState->rxFrameCount = AH5416(ah)->ah_rxBusy; + aniState->txFrameCount = AH5416(ah)->ah_txBusy; + } + return listenTime; } @@ -865,16 +932,21 @@ ar5416AniPoll(struct ath_hal *ah, const struct ieee80211_channel *chan) const struct ar5212AniParams *params; int32_t listenTime; + /* Always update from the MIB, for statistics gathering */ + listenTime = ar5416AniGetListenTime(ah); + /* XXX can aniState be null? */ if (aniState == AH_NULL) return; + if (!ANI_ENA(ah)) return; - listenTime = ar5416AniGetListenTime(ah); if (listenTime < 0) { ahp->ah_stats.ast_ani_lneg++; /* restart ANI period if listenTime is invalid */ + HALDEBUG(ah, HAL_DEBUG_ANI, "%s: invalid listenTime\n", + __func__); ar5416AniRestart(ah, aniState); } /* XXX beware of overflow? */ @@ -894,6 +966,8 @@ ar5416AniPoll(struct ath_hal *ah, const struct ieee80211_channel *chan) aniState->cckPhyErrCount <= aniState->listenTime * params->cckTrigLow/1000) ar5416AniLowerImmunity(ah); + HALDEBUG(ah, HAL_DEBUG_ANI, "%s: lower immunity\n", + __func__); ar5416AniRestart(ah, aniState); } else if (aniState->listenTime > params->period) { updateMIBStats(ah, aniState); @@ -909,7 +983,7 @@ ar5416AniPoll(struct ath_hal *ah, const struct ieee80211_channel *chan) params->cckTrigHigh / 1000) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: CCK err %u listenTime %u\n", __func__, - aniState->ofdmPhyErrCount, aniState->listenTime); + aniState->cckPhyErrCount, aniState->listenTime); ar5416AniCckErrTrigger(ah); ar5416AniRestart(ah, aniState); } diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_attach.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_attach.c index 2ad88a0a77..cc7aa281a7 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_attach.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_attach.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c 305614 2016-09-08 15:05:25Z pfg $ */ #include "opt_ah.h" @@ -30,7 +30,9 @@ #include "ar5416/ar5416.ini" -static void ar5416ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore); +static void ar5416ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore, + HAL_BOOL power_off); +static void ar5416DisablePCIE(struct ath_hal *ah); static void ar5416WriteIni(struct ath_hal *ah, const struct ieee80211_channel *chan); static void ar5416SpurMitigate(struct ath_hal *ah, @@ -45,8 +47,8 @@ ar5416AniSetup(struct ath_hal *ah) .coarseHigh = { -14, -14, -14, -14, -12 }, .coarseLow = { -64, -64, -64, -64, -70 }, .firpwr = { -78, -78, -78, -78, -80 }, - .maxSpurImmunityLevel = 2, - .cycPwrThr1 = { 2, 4, 6 }, + .maxSpurImmunityLevel = 7, + .cycPwrThr1 = { 2, 4, 6, 8, 10, 12, 14, 16 }, .maxFirstepLevel = 2, /* levels 0..2 */ .firstep = { 0, 4, 8 }, .ofdmTrigHigh = 500, @@ -99,9 +101,10 @@ ar5416InitState(struct ath_hal_5416 *ahp5416, uint16_t devid, HAL_SOFTC sc, ah->ah_phyDisable = ar5416PhyDisable; ah->ah_disable = ar5416Disable; ah->ah_configPCIE = ar5416ConfigPCIE; + ah->ah_disablePCIE = ar5416DisablePCIE; ah->ah_perCalibration = ar5416PerCalibration; - ah->ah_perCalibrationN = ar5416PerCalibrationN, - ah->ah_resetCalValid = ar5416ResetCalValid, + ah->ah_perCalibrationN = ar5416PerCalibrationN; + ah->ah_resetCalValid = ar5416ResetCalValid; ah->ah_setTxPowerLimit = ar5416SetTxPowerLimit; ah->ah_setTxPower = ar5416SetTransmitPower; ah->ah_setBoardValues = ar5416SetBoardValues; @@ -119,6 +122,7 @@ ar5416InitState(struct ath_hal_5416 *ahp5416, uint16_t devid, HAL_SOFTC sc, /* Receive Functions */ ah->ah_getRxFilter = ar5416GetRxFilter; ah->ah_setRxFilter = ar5416SetRxFilter; + ah->ah_stopDmaReceive = ar5416StopDmaReceive; ah->ah_startPcuReceive = ar5416StartPcuReceive; ah->ah_stopPcuReceive = ar5416StopPcuReceive; ah->ah_setupRxDesc = ar5416SetupRxDesc; @@ -129,6 +133,7 @@ ar5416InitState(struct ath_hal_5416 *ahp5416, uint16_t devid, HAL_SOFTC sc, /* Misc Functions */ ah->ah_getCapability = ar5416GetCapability; + ah->ah_setCapability = ar5416SetCapability; ah->ah_getDiagState = ar5416GetDiagState; ah->ah_setLedState = ar5416SetLedState; ah->ah_gpioCfgOutput = ar5416GpioCfgOutput; @@ -137,12 +142,15 @@ ar5416InitState(struct ath_hal_5416 *ahp5416, uint16_t devid, HAL_SOFTC sc, ah->ah_gpioSet = ar5416GpioSet; ah->ah_gpioSetIntr = ar5416GpioSetIntr; ah->ah_getTsf64 = ar5416GetTsf64; + ah->ah_setTsf64 = ar5416SetTsf64; ah->ah_resetTsf = ar5416ResetTsf; ah->ah_getRfGain = ar5416GetRfgain; ah->ah_setAntennaSwitch = ar5416SetAntennaSwitch; ah->ah_setDecompMask = ar5416SetDecompMask; ah->ah_setCoverageClass = ar5416SetCoverageClass; ah->ah_setQuiet = ar5416SetQuiet; + ah->ah_getMibCycleCounts = ar5416GetMibCycleCounts; + ah->ah_setChainMasks = ar5416SetChainMasks; ah->ah_resetKeyCacheEntry = ar5416ResetKeyCacheEntry; ah->ah_setKeyCacheEntry = ar5416SetKeyCacheEntry; @@ -150,9 +158,18 @@ ar5416InitState(struct ath_hal_5416 *ahp5416, uint16_t devid, HAL_SOFTC sc, /* DFS Functions */ ah->ah_enableDfs = ar5416EnableDfs; ah->ah_getDfsThresh = ar5416GetDfsThresh; + ah->ah_getDfsDefaultThresh = ar5416GetDfsDefaultThresh; ah->ah_procRadarEvent = ar5416ProcessRadarEvent; ah->ah_isFastClockEnabled = ar5416IsFastClockEnabled; + /* Spectral Scan Functions */ + ah->ah_spectralConfigure = ar5416ConfigureSpectralScan; + ah->ah_spectralGetConfig = ar5416GetSpectralParams; + ah->ah_spectralStart = ar5416StartSpectralScan; + ah->ah_spectralStop = ar5416StopSpectralScan; + ah->ah_spectralIsEnabled = ar5416IsSpectralEnabled; + ah->ah_spectralIsActive = ar5416IsSpectralActive; + /* Power Management Functions */ ah->ah_setPowerMode = ar5416SetPowerMode; @@ -168,19 +185,33 @@ ar5416InitState(struct ath_hal_5416 *ahp5416, uint16_t devid, HAL_SOFTC sc, ah->ah_setupFirstTxDesc = ar5416SetupFirstTxDesc; ah->ah_setupLastTxDesc = ar5416SetupLastTxDesc; ah->ah_set11nRateScenario = ar5416Set11nRateScenario; + ah->ah_set11nAggrFirst = ar5416Set11nAggrFirst; ah->ah_set11nAggrMiddle = ar5416Set11nAggrMiddle; + ah->ah_set11nAggrLast = ar5416Set11nAggrLast; ah->ah_clr11nAggr = ar5416Clr11nAggr; ah->ah_set11nBurstDuration = ar5416Set11nBurstDuration; ah->ah_get11nExtBusy = ar5416Get11nExtBusy; ah->ah_set11nMac2040 = ar5416Set11nMac2040; ah->ah_get11nRxClear = ar5416Get11nRxClear; ah->ah_set11nRxClear = ar5416Set11nRxClear; + ah->ah_set11nVirtMoreFrag = ar5416Set11nVirtualMoreFrag; /* Interrupt functions */ ah->ah_isInterruptPending = ar5416IsInterruptPending; ah->ah_getPendingInterrupts = ar5416GetPendingInterrupts; ah->ah_setInterrupts = ar5416SetInterrupts; + /* Bluetooth Coexistence functions */ + ah->ah_btCoexSetInfo = ar5416SetBTCoexInfo; + ah->ah_btCoexSetConfig = ar5416BTCoexConfig; + ah->ah_btCoexSetQcuThresh = ar5416BTCoexSetQcuThresh; + ah->ah_btCoexSetWeights = ar5416BTCoexSetWeights; + ah->ah_btCoexSetBmissThresh = ar5416BTCoexSetupBmissThresh; + ah->ah_btCoexSetParameter = ar5416BTCoexSetParameter; + ah->ah_btCoexDisable = ar5416BTCoexDisable; + ah->ah_btCoexEnable = ar5416BTCoexEnable; + AH5416(ah)->ah_btCoexSetDiversity = ar5416BTCoexAntennaDiversity; + ahp->ah_priv.ah_getWirelessModes= ar5416GetWirelessModes; ahp->ah_priv.ah_eepromRead = ar5416EepromRead; #ifdef AH_SUPPORT_WRITE_EEPROM @@ -212,8 +243,37 @@ ar5416InitState(struct ath_hal_5416 *ahp5416, uint16_t devid, HAL_SOFTC sc, /* Enable all ANI functions to begin with */ AH5416(ah)->ah_ani_function = 0xffffffff; - /* Set overridable ANI methods */ - AH5212(ah)->ah_aniControl = ar5416AniControl; + /* Set overridable ANI methods */ + AH5212(ah)->ah_aniControl = ar5416AniControl; + + /* + * Default FIFO Trigger levels + * + * These define how filled the TX FIFO needs to be before + * the baseband begins to be given some data. + * + * To be paranoid, we ensure that the TX trigger level always + * has at least enough space for two TX DMA to occur. + * The TX DMA size is currently hard-coded to AR_TXCFG_DMASZ_128B. + * That means we need to leave at least 256 bytes available in + * the TX DMA FIFO. + */ +#define AR_FTRIG_512B 0x00000080 // 5 bits total + /* + * AR9285/AR9271 have half the size TX FIFO compared to + * other devices + */ + if (AR_SREV_KITE(ah) || AR_SREV_9271(ah)) { + AH5212(ah)->ah_txTrigLev = (AR_FTRIG_256B >> AR_FTRIG_S); + AH5212(ah)->ah_maxTxTrigLev = ((2048 / 64) - 1); + } else { + AH5212(ah)->ah_txTrigLev = (AR_FTRIG_512B >> AR_FTRIG_S); + AH5212(ah)->ah_maxTxTrigLev = ((4096 / 64) - 1); + } +#undef AR_FTRIG_512B + + /* And now leave some headspace - 256 bytes */ + AH5212(ah)->ah_maxTxTrigLev -= 4; } uint32_t @@ -237,7 +297,7 @@ ar5416GetRadioRev(struct ath_hal *ah) static struct ath_hal * ar5416Attach(uint16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, - HAL_STATUS *status) + HAL_OPS_CONFIG *ah_config, HAL_STATUS *status) { struct ath_hal_5416 *ahp5416; struct ath_hal_5212 *ahp; @@ -246,7 +306,7 @@ ar5416Attach(uint16_t devid, HAL_SOFTC sc, HAL_STATUS ecode; HAL_BOOL rfStatus; - HALDEBUG_G(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n", + HALDEBUG(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n", __func__, sc, (void*) st, (void*) sh); /* NB: memory is returned zero'd */ @@ -255,7 +315,7 @@ ar5416Attach(uint16_t devid, HAL_SOFTC sc, sizeof(ar5416Addac) ); if (ahp5416 == AH_NULL) { - HALDEBUG_G(AH_NULL, HAL_DEBUG_ANY, + HALDEBUG(AH_NULL, HAL_DEBUG_ANY, "%s: cannot allocate memory for state block\n", __func__); *status = HAL_ENOMEM; return AH_NULL; @@ -296,16 +356,11 @@ ar5416Attach(uint16_t devid, HAL_SOFTC sc, HAL_INI_INIT(&AH5416(ah)->ah_ini_addac, ar5416Addac, 2); if (! IS_5416V2_2(ah)) { /* Owl 2.1/2.0 */ -#ifndef __HAIKU__ - ath_hal_printf(ah, "[ath] Enabling CLKDRV workaround for AR5416 < v2.2\n"); -#endif struct ini { uint32_t *data; /* NB: !const */ int rows, cols; }; -#ifdef __HAIKU__ ath_hal_printf(ah, "[ath] Enabling CLKDRV workaround for AR5416 < v2.2\n"); -#endif /* override CLKDRV value */ OS_MEMCPY(&AH5416(ah)[1], ar5416Addac, sizeof(ar5416Addac)); AH5416(ah)->ah_ini_addac.data = (uint32_t *) &AH5416(ah)[1]; @@ -342,7 +397,7 @@ ar5416Attach(uint16_t devid, HAL_SOFTC sc, OS_REG_WRITE(ah, AR_PHY(0), 0x00000007); /* Read Radio Chip Rev Extract */ - AH_PRIVATE(ah)->ah_analog5GhzRev = ar5212GetRadioRev(ah); + AH_PRIVATE(ah)->ah_analog5GhzRev = ar5416GetRadioRev(ah); switch (AH_PRIVATE(ah)->ah_analog5GhzRev & AR_RADIO_SREV_MAJOR) { case AR_RAD5122_SREV_MAJOR: /* Fowl: 5G/2x2 */ case AR_RAD2122_SREV_MAJOR: /* Fowl: 2+5G/2x2 */ @@ -457,20 +512,71 @@ void ar5416AttachPCIE(struct ath_hal *ah) { if (AH_PRIVATE(ah)->ah_ispcie) - ath_hal_configPCIE(ah, AH_FALSE); + ath_hal_configPCIE(ah, AH_FALSE, AH_FALSE); else ath_hal_disablePCIE(ah); } static void -ar5416ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore) +ar5416ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore, HAL_BOOL power_off) { - if (AH_PRIVATE(ah)->ah_ispcie && !restore) { + + /* This is only applicable for AR5418 (AR5416 PCIe) */ + if (! AH_PRIVATE(ah)->ah_ispcie) + return; + + if (! restore) { ath_hal_ini_write(ah, &AH5416(ah)->ah_ini_pcieserdes, 1, 0); OS_DELAY(1000); - OS_REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA); - OS_REG_WRITE(ah, AR_WA, AR_WA_DEFAULT); } + + if (power_off) { /* Power-off */ + /* clear bit 19 to disable L1 */ + OS_REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA); + } else { /* Power-on */ + /* Set default WAR values for Owl */ + OS_REG_WRITE(ah, AR_WA, AR_WA_DEFAULT); + + /* set bit 19 to allow forcing of pcie core into L1 state */ + OS_REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA); + } +} + +/* + * Disable PCIe PHY if PCIe isn't used. + */ +static void +ar5416DisablePCIE(struct ath_hal *ah) +{ + + /* PCIe? Don't */ + if (AH_PRIVATE(ah)->ah_ispcie) + return; + + /* .. Only applicable for AR5416v2 or later */ + if (! (AR_SREV_OWL(ah) && AR_SREV_OWL_20_OR_LATER(ah))) + return; + + OS_REG_WRITE_BUFFER_ENABLE(ah); + + /* + * Disable the PCIe PHY. + */ + OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fc00); + OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924); + OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x28000029); + OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x57160824); + OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x25980579); + OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x00000000); + OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x1aaabe40); + OS_REG_WRITE(ah, AR_PCIE_SERDES, 0xbe105554); + OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x000e1007); + + /* Load the new settings */ + OS_REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000); + + OS_REG_WRITE_BUFFER_FLUSH(ah); + OS_REG_WRITE_BUFFER_DISABLE(ah); } static void @@ -524,7 +630,7 @@ ar5416WriteIni(struct ath_hal *ah, const struct ieee80211_channel *chan) } /* - * Convert to baseband spur frequency given input channel frequency + * Convert to baseband spur frequency given input channel frequency * and compute register settings below. */ @@ -583,6 +689,8 @@ ar5416SpurMitigate(struct ath_hal *ah, const struct ieee80211_channel *chan) AR_PHY_TIMING_CTRL4_ENABLE_CHAN_MASK | AR_PHY_TIMING_CTRL4_ENABLE_PILOT_MASK); + OS_REG_WRITE_BUFFER_ENABLE(ah); + OS_REG_WRITE(ah, AR_PHY_TIMING_CTRL4_CHAIN(0), new); new = (AR_PHY_SPUR_REG_MASK_RATE_CNTL | @@ -767,6 +875,9 @@ ar5416SpurMitigate(struct ath_hal *ah, const struct ieee80211_channel *chan) | (mask_p[47] << 2) | (mask_p[46] << 0); OS_REG_WRITE(ah, AR_PHY_BIN_MASK2_4, tmp_mask); OS_REG_WRITE(ah, AR_PHY_MASK2_P_61_45, tmp_mask); + + OS_REG_WRITE_BUFFER_FLUSH(ah); + OS_REG_WRITE_BUFFER_DISABLE(ah); } /* @@ -780,7 +891,7 @@ ar5416FillCapabilityInfo(struct ath_hal *ah) struct ath_hal_private *ahpriv = AH_PRIVATE(ah); HAL_CAPABILITIES *pCap = &ahpriv->ah_caps; uint16_t val; - + /* Construct wireless mode from EEPROM */ pCap->halWirelessModes = 0; if (ath_hal_eepromGetFlag(ah, AR_EEP_AMODE)) { @@ -826,18 +937,21 @@ ar5416FillCapabilityInfo(struct ath_hal *ah) pCap->halCompressSupport = AH_FALSE; pCap->halBurstSupport = AH_TRUE; - pCap->halFastFramesSupport = AH_FALSE; /* XXX? */ + pCap->halFastFramesSupport = AH_TRUE; pCap->halChapTuningSupport = AH_TRUE; pCap->halTurboPrimeSupport = AH_TRUE; pCap->halTurboGSupport = pCap->halWirelessModes & HAL_MODE_108G; pCap->halPSPollBroken = AH_TRUE; /* XXX fixed in later revs? */ + pCap->halNumMRRetries = 4; /* Hardware supports 4 MRR */ + pCap->halNumTxMaps = 1; /* Single TX ptr per descr */ pCap->halVEOLSupport = AH_TRUE; pCap->halBssIdMaskSupport = AH_TRUE; pCap->halMcastKeySrchSupport = AH_TRUE; /* Works on AR5416 and later */ pCap->halTsfAddSupport = AH_TRUE; pCap->hal4AddrAggrSupport = AH_FALSE; /* Broken in Owl */ + pCap->halSpectralScanSupport = AH_FALSE; /* AR9280 and later */ if (ath_hal_eepromGet(ah, AR_EEP_MAXQCU, &val) == HAL_OK) pCap->halTotalQueues = val; @@ -849,9 +963,9 @@ ar5416FillCapabilityInfo(struct ath_hal *ah) else pCap->halKeyCacheSize = AR5416_KEYTABLE_SIZE; - /* XXX not needed */ - pCap->halChanHalfRate = AH_FALSE; /* XXX ? */ - pCap->halChanQuarterRate = AH_FALSE; /* XXX ? */ + /* XXX Which chips? */ + pCap->halChanHalfRate = AH_TRUE; + pCap->halChanQuarterRate = AH_TRUE; pCap->halTstampPrecision = 32; pCap->halHwPhyCounterSupport = AH_TRUE; @@ -868,7 +982,7 @@ ar5416FillCapabilityInfo(struct ath_hal *ah) ; pCap->halFastCCSupport = AH_TRUE; - pCap->halNumGpioPins = 6; + pCap->halNumGpioPins = 14; pCap->halWowSupport = AH_FALSE; pCap->halWowMatchPatternExact = AH_FALSE; pCap->halBtCoexSupport = AH_FALSE; /* XXX need support */ @@ -887,6 +1001,16 @@ ar5416FillCapabilityInfo(struct ath_hal *ah) /* AR5416 may have 3 antennas but is a 2x2 stream device */ pCap->halTxStreams = 2; pCap->halRxStreams = 2; + + /* + * If the TX or RX chainmask has less than 2 chains active, + * mark it as a 1-stream device for the relevant stream. + */ + if (owl_get_ntxchains(pCap->halTxChainMask) == 1) + pCap->halTxStreams = 1; + /* XXX Eww */ + if (owl_get_ntxchains(pCap->halRxChainMask) == 1) + pCap->halRxStreams = 1; pCap->halRtsAggrLimit = 8*1024; /* Owl 2.0 limit */ pCap->halMbssidAggrSupport = AH_FALSE; /* Broken on Owl */ pCap->halForcePpmSupport = AH_TRUE; @@ -897,6 +1021,12 @@ ar5416FillCapabilityInfo(struct ath_hal *ah) pCap->halEnhancedDfsSupport = AH_FALSE; /* Hardware supports 32 bit TSF values in the RX descriptor */ pCap->halHasLongRxDescTsf = AH_TRUE; + /* + * BB Read WAR: this is only for AR5008/AR9001 NICs + * It is also set individually in the AR91xx attach functions. + */ + if (AR_SREV_OWL(ah)) + pCap->halHasBBReadWar = AH_TRUE; if (ath_hal_eepromGetFlag(ah, AR_EEP_RFKILL) && ath_hal_eepromGet(ah, AR_EEP_RFSILENT, &ahpriv->ah_rfsilent) == HAL_OK) { @@ -905,17 +1035,41 @@ ar5416FillCapabilityInfo(struct ath_hal *ah) pCap->halRfSilentSupport = AH_TRUE; } + /* + * The MAC will mark frames as RXed if there's a descriptor + * to write them to. So if it hits a self-linked final descriptor, + * it'll keep ACKing frames even though they're being silently + * dropped. Thus, this particular feature of the driver can't + * be used for 802.11n devices. + */ ahpriv->ah_rxornIsFatal = AH_FALSE; + /* + * If it's a PCI NIC, ask the HAL OS layer to serialise + * register access, or SMP machines may cause the hardware + * to hang. This is applicable to AR5416 and AR9220; I'm not + * sure about AR9160 or AR9227. + */ + if (! AH_PRIVATE(ah)->ah_ispcie) + pCap->halSerialiseRegWar = 1; + + /* + * AR5416 and later NICs support MYBEACON filtering. + */ + pCap->halRxDoMyBeacon = AH_TRUE; + return AH_TRUE; } static const char* ar5416Probe(uint16_t vendorid, uint16_t devid) { - if (vendorid == ATHEROS_VENDOR_ID && - (devid == AR5416_DEVID_PCI || devid == AR5416_DEVID_PCIE)) - return "Atheros 5416"; + if (vendorid == ATHEROS_VENDOR_ID) { + if (devid == AR5416_DEVID_PCI) + return "Atheros 5416"; + if (devid == AR5416_DEVID_PCIE) + return "Atheros 5418"; + } return AH_NULL; } AH_CHIP(AR5416, ar5416Probe, ar5416Attach); diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_beacon.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_beacon.c index 8b61e14658..4cedd05aad 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_beacon.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_beacon.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5416/ar5416_beacon.c 265031 2014-04-27 23:35:05Z adrian $ */ #include "opt_ah.h" @@ -46,6 +46,7 @@ void ar5416SetBeaconTimers(struct ath_hal *ah, const HAL_BEACON_TIMERS *bt) { uint32_t bperiod; + struct ath_hal_5212 *ahp = AH5212(ah); OS_REG_WRITE(ah, AR_NEXT_TBTT, TU_TO_USEC(bt->bt_nexttbtt)); OS_REG_WRITE(ah, AR_NEXT_DBA, ONE_EIGHTH_TU_TO_USEC(bt->bt_nextdba)); @@ -53,6 +54,7 @@ ar5416SetBeaconTimers(struct ath_hal *ah, const HAL_BEACON_TIMERS *bt) OS_REG_WRITE(ah, AR_NEXT_NDP, TU_TO_USEC(bt->bt_nextatim)); bperiod = TU_TO_USEC(bt->bt_intval & HAL_BEACON_PERIOD); + ahp->ah_beaconInterval = bt->bt_intval & HAL_BEACON_PERIOD; OS_REG_WRITE(ah, AR5416_BEACON_PERIOD, bperiod); OS_REG_WRITE(ah, AR_DBA_PERIOD, bperiod); OS_REG_WRITE(ah, AR_SWBA_PERIOD, bperiod); @@ -195,6 +197,25 @@ ar5416SetStaBeaconTimers(struct ath_hal *ah, const HAL_BEACON_STATE *bs) * beacon jitter; cab timeout is max time to wait for cab * after seeing the last DTIM or MORE CAB bit */ + +/* + * I've bumped these to 30TU for now. + * + * Some APs (AR933x/AR934x?) in 2GHz especially seem to not always + * transmit beacon frames at exactly the right times and with it set + * to 10TU, the NIC starts not waking up at the right times to hear + * these slightly-larger-jitering beacons. It also never recovers + * from that (it doesn't resync? I'm not sure.) + * + * So for now bump this to 30TU. Ideally we'd cap this based on + * the beacon interval so the sum of CAB+BEACON timeouts never + * exceeded the beacon interval. + * + * Now, since we're doing all the math in the ath(4) driver in TU + * rather than TSF, we may be seeing the result of dumb rounding + * errors causing the jitter to actually be a much bigger problem. + * I'll have to investigate that with a fine tooth comb. + */ #define CAB_TIMEOUT_VAL 10 /* in TU */ #define BEACON_TIMEOUT_VAL 10 /* in TU */ #define SLEEP_SLOP 3 /* in TU */ @@ -246,6 +267,13 @@ ar5416SetStaBeaconTimers(struct ath_hal *ah, const HAL_BEACON_STATE *bs) OS_REG_SET_BIT(ah, AR_TIMER_MODE, AR_TIMER_MODE_TBTT | AR_TIMER_MODE_TIM | AR_TIMER_MODE_DTIM); + +#define HAL_TSFOOR_THRESHOLD 0x00004240 /* TSF OOR threshold (16k us) */ + + /* TSF out of range threshold */ +// OS_REG_WRITE(ah, AR_TSFOOR_THRESHOLD, bs->bs_tsfoor_threshold); + OS_REG_WRITE(ah, AR_TSFOOR_THRESHOLD, HAL_TSFOOR_THRESHOLD); + HALDEBUG(ah, HAL_DEBUG_BEACON, "%s: next DTIM %d\n", __func__, bs->bs_nextdtim); HALDEBUG(ah, HAL_DEBUG_BEACON, "%s: next beacon %d\n", diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_btcoex.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_btcoex.c new file mode 100644 index 0000000000..398c5006a9 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_btcoex.c @@ -0,0 +1,398 @@ +/* + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2005 Atheros Communications, Inc. + * Copyright (c) 2008-2010, Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5416/ar5416_btcoex.c 301304 2016-06-04 07:28:36Z adrian $ + */ + +#include "opt_ah.h" + +#include "ah.h" +#include "ah_internal.h" +#include "ah_devid.h" +#ifdef AH_DEBUG +#include "ah_desc.h" /* NB: for HAL_PHYERR* */ +#endif + +#include "ar5416/ar5416.h" +#include "ar5416/ar5416reg.h" +#include "ar5416/ar5416phy.h" +#include "ar5416/ar5416desc.h" /* AR5416_CONTTXMODE */ +#include "ar5416/ar5416_btcoex.h" + +void +ar5416SetBTCoexInfo(struct ath_hal *ah, HAL_BT_COEX_INFO *btinfo) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + ahp->ah_btModule = btinfo->bt_module; + ahp->ah_btCoexConfigType = btinfo->bt_coex_config; + ahp->ah_btActiveGpioSelect = btinfo->bt_gpio_bt_active; + ahp->ah_btPriorityGpioSelect = btinfo->bt_gpio_bt_priority; + ahp->ah_wlanActiveGpioSelect = btinfo->bt_gpio_wlan_active; + ahp->ah_btActivePolarity = btinfo->bt_active_polarity; + ahp->ah_btCoexSingleAnt = btinfo->bt_single_ant; + ahp->ah_btWlanIsolation = btinfo->bt_isolation; +} + +void +ar5416BTCoexConfig(struct ath_hal *ah, HAL_BT_COEX_CONFIG *btconf) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + HAL_BOOL rxClearPolarity = btconf->bt_rxclear_polarity; + + /* + * For Kiwi and Osprey, the polarity of rx_clear is active high. + * The bt_rxclear_polarity flag from ath(4) needs to be inverted. + */ + if (AR_SREV_KIWI(ah)) { + rxClearPolarity = !btconf->bt_rxclear_polarity; + } + + ahp->ah_btCoexMode = (ahp->ah_btCoexMode & AR_BT_QCU_THRESH) | + SM(btconf->bt_time_extend, AR_BT_TIME_EXTEND) | + SM(btconf->bt_txstate_extend, AR_BT_TXSTATE_EXTEND) | + SM(btconf->bt_txframe_extend, AR_BT_TX_FRAME_EXTEND) | + SM(btconf->bt_mode, AR_BT_MODE) | + SM(btconf->bt_quiet_collision, AR_BT_QUIET) | + SM(rxClearPolarity, AR_BT_RX_CLEAR_POLARITY) | + SM(btconf->bt_priority_time, AR_BT_PRIORITY_TIME) | + SM(btconf->bt_first_slot_time, AR_BT_FIRST_SLOT_TIME); + + ahp->ah_btCoexMode2 |= SM(btconf->bt_hold_rxclear, + AR_BT_HOLD_RX_CLEAR); + + if (ahp->ah_btCoexSingleAnt == AH_FALSE) { + /* Enable ACK to go out even though BT has higher priority. */ + ahp->ah_btCoexMode2 |= AR_BT_DISABLE_BT_ANT; + } +} + +void +ar5416BTCoexSetQcuThresh(struct ath_hal *ah, int qnum) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + ahp->ah_btCoexMode |= SM(qnum, AR_BT_QCU_THRESH); +} + +void +ar5416BTCoexSetWeights(struct ath_hal *ah, u_int32_t stompType) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + if (AR_SREV_KIWI_10_OR_LATER(ah)) { + /* TODO: TX RX separate is not enabled. */ + switch (stompType) { + case HAL_BT_COEX_STOMP_AUDIO: + /* XXX TODO */ + case HAL_BT_COEX_STOMP_ALL: + ahp->ah_btCoexBTWeight = AR5416_BT_WGHT; + ahp->ah_btCoexWLANWeight = AR5416_STOMP_ALL_WLAN_WGHT; + break; + case HAL_BT_COEX_STOMP_LOW: + ahp->ah_btCoexBTWeight = AR5416_BT_WGHT; + ahp->ah_btCoexWLANWeight = AR5416_STOMP_LOW_WLAN_WGHT; + break; + case HAL_BT_COEX_STOMP_ALL_FORCE: + ahp->ah_btCoexBTWeight = AR5416_BT_WGHT; + ahp->ah_btCoexWLANWeight = + AR5416_STOMP_ALL_FORCE_WLAN_WGHT; + break; + case HAL_BT_COEX_STOMP_LOW_FORCE: + ahp->ah_btCoexBTWeight = AR5416_BT_WGHT; + ahp->ah_btCoexWLANWeight = + AR5416_STOMP_LOW_FORCE_WLAN_WGHT; + break; + case HAL_BT_COEX_STOMP_NONE: + case HAL_BT_COEX_NO_STOMP: + ahp->ah_btCoexBTWeight = AR5416_BT_WGHT; + ahp->ah_btCoexWLANWeight = AR5416_STOMP_NONE_WLAN_WGHT; + break; + default: + /* There is a forceWeight from registry */ + ahp->ah_btCoexBTWeight = stompType & 0xffff; + ahp->ah_btCoexWLANWeight = stompType >> 16; + break; + } + } else { + switch (stompType) { + case HAL_BT_COEX_STOMP_AUDIO: + /* XXX TODO */ + case HAL_BT_COEX_STOMP_ALL: + ahp->ah_btCoexBTWeight = AR5416_BT_WGHT; + ahp->ah_btCoexWLANWeight = AR5416_STOMP_ALL_WLAN_WGHT; + break; + case HAL_BT_COEX_STOMP_LOW: + ahp->ah_btCoexBTWeight = AR5416_BT_WGHT; + ahp->ah_btCoexWLANWeight = AR5416_STOMP_LOW_WLAN_WGHT; + break; + case HAL_BT_COEX_STOMP_ALL_FORCE: + ahp->ah_btCoexBTWeight = AR5416_BT_WGHT; + ahp->ah_btCoexWLANWeight = + AR5416_STOMP_ALL_FORCE_WLAN_WGHT; + break; + case HAL_BT_COEX_STOMP_LOW_FORCE: + ahp->ah_btCoexBTWeight = AR5416_BT_WGHT; + ahp->ah_btCoexWLANWeight = + AR5416_STOMP_LOW_FORCE_WLAN_WGHT; + break; + case HAL_BT_COEX_STOMP_NONE: + case HAL_BT_COEX_NO_STOMP: + ahp->ah_btCoexBTWeight = AR5416_BT_WGHT; + ahp->ah_btCoexWLANWeight = AR5416_STOMP_NONE_WLAN_WGHT; + break; + default: + /* There is a forceWeight from registry */ + ahp->ah_btCoexBTWeight = stompType & 0xffff; + ahp->ah_btCoexWLANWeight = stompType >> 16; + break; + } + } +} + +void +ar5416BTCoexSetupBmissThresh(struct ath_hal *ah, u_int32_t thresh) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + ahp->ah_btCoexMode2 |= SM(thresh, AR_BT_BCN_MISS_THRESH); +} + +/* + * There is no antenna diversity for Owl, Kiwi, etc. + * + * Kite will override this particular method. + */ +void +ar5416BTCoexAntennaDiversity(struct ath_hal *ah) +{ +} + +void +ar5416BTCoexSetParameter(struct ath_hal *ah, u_int32_t type, u_int32_t value) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + switch (type) { + case HAL_BT_COEX_SET_ACK_PWR: + if (value) { + ahp->ah_btCoexFlag |= HAL_BT_COEX_FLAG_LOW_ACK_PWR; + OS_REG_WRITE(ah, AR_TPC, HAL_BT_COEX_LOW_ACK_POWER); + } else { + ahp->ah_btCoexFlag &= ~HAL_BT_COEX_FLAG_LOW_ACK_PWR; + OS_REG_WRITE(ah, AR_TPC, HAL_BT_COEX_HIGH_ACK_POWER); + } + break; + case HAL_BT_COEX_ANTENNA_DIVERSITY: + /* This is overridden for Kite */ + break; +#if 0 + case HAL_BT_COEX_LOWER_TX_PWR: + if (value) { + if ((ahp->ah_btCoexFlag & HAL_BT_COEX_FLAG_LOWER_TX_PWR) == 0) { + ahp->ah_btCoexFlag |= HAL_BT_COEX_FLAG_LOWER_TX_PWR; + AH_PRIVATE(ah)->ah_config.ath_hal_desc_tpc = 1; + ar5416SetTxPowerLimit(ah, AH_PRIVATE(ah)->ah_power_limit, AH_PRIVATE(ah)->ah_extra_txpow, 0); + } + } + else { + if (ahp->ah_btCoexFlag & HAL_BT_COEX_FLAG_LOWER_TX_PWR) { + ahp->ah_btCoexFlag &= ~HAL_BT_COEX_FLAG_LOWER_TX_PWR; + AH_PRIVATE(ah)->ah_config.ath_hal_desc_tpc = 0; + ar5416SetTxPowerLimit(ah, AH_PRIVATE(ah)->ah_power_limit, AH_PRIVATE(ah)->ah_extra_txpow, 0); + } + } + break; +#endif + default: + break; + } +} + +void +ar5416BTCoexDisable(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + /* Always drive rx_clear_external output as 0 */ + ar5416GpioSet(ah, ahp->ah_wlanActiveGpioSelect, 0); + ar5416GpioCfgOutput(ah, ahp->ah_wlanActiveGpioSelect, + HAL_GPIO_OUTPUT_MUX_AS_OUTPUT); + + if (AR_SREV_9271(ah)) { + /* + * Set wlanActiveGpio to input when disabling BT-COEX to + * reduce power consumption + */ + ar5416GpioCfgInput(ah, ahp->ah_wlanActiveGpioSelect); + } + + if (ahp->ah_btCoexSingleAnt == AH_TRUE) { + OS_REG_RMW_FIELD(ah, AR_QUIET1, AR_QUIET1_QUIET_ACK_CTS_ENABLE, + 1); + OS_REG_RMW_FIELD(ah, AR_MISC_MODE, AR_PCU_BT_ANT_PREVENT_RX, + 0); + } + + OS_REG_WRITE(ah, AR_BT_COEX_MODE, AR_BT_QUIET | AR_BT_MODE); + OS_REG_WRITE(ah, AR_BT_COEX_WEIGHT, 0); + if (AR_SREV_KIWI_10_OR_LATER(ah)) + OS_REG_WRITE(ah, AR_BT_COEX_WEIGHT2, 0); + OS_REG_WRITE(ah, AR_BT_COEX_MODE2, 0); + + ahp->ah_btCoexEnabled = AH_FALSE; +} + +int +ar5416BTCoexEnable(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + /* Program coex mode and weight registers to actually enable coex */ + OS_REG_WRITE(ah, AR_BT_COEX_MODE, ahp->ah_btCoexMode); + OS_REG_WRITE(ah, AR_BT_COEX_WEIGHT, + SM(ahp->ah_btCoexWLANWeight & 0xFFFF, AR_BT_WL_WGHT) | + SM(ahp->ah_btCoexBTWeight & 0xFFFF, AR_BT_BT_WGHT)); + if (AR_SREV_KIWI_10_OR_LATER(ah)) { + OS_REG_WRITE(ah, AR_BT_COEX_WEIGHT2, + SM(ahp->ah_btCoexWLANWeight >> 16, AR_BT_WL_WGHT)); + } + OS_REG_WRITE(ah, AR_BT_COEX_MODE2, ahp->ah_btCoexMode2); + + /* Added Select GPIO5~8 instaed SPI */ + if (AR_SREV_9271(ah)) { + uint32_t val; + + val = OS_REG_READ(ah, AR9271_CLOCK_CONTROL); + val &= 0xFFFFFEFF; + OS_REG_WRITE(ah, AR9271_CLOCK_CONTROL, val); + } + + if (ahp->ah_btCoexFlag & HAL_BT_COEX_FLAG_LOW_ACK_PWR) + OS_REG_WRITE(ah, AR_TPC, HAL_BT_COEX_LOW_ACK_POWER); + else + OS_REG_WRITE(ah, AR_TPC, HAL_BT_COEX_HIGH_ACK_POWER); + + if (ahp->ah_btCoexSingleAnt == AH_TRUE) { + OS_REG_RMW_FIELD(ah, AR_QUIET1, + AR_QUIET1_QUIET_ACK_CTS_ENABLE, 1); + /* XXX should update miscMode? */ + OS_REG_RMW_FIELD(ah, AR_MISC_MODE, + AR_PCU_BT_ANT_PREVENT_RX, 1); + } else { + OS_REG_RMW_FIELD(ah, AR_QUIET1, + AR_QUIET1_QUIET_ACK_CTS_ENABLE, 1); + /* XXX should update miscMode? */ + OS_REG_RMW_FIELD(ah, AR_MISC_MODE, + AR_PCU_BT_ANT_PREVENT_RX, 0); + } + + if (ahp->ah_btCoexConfigType == HAL_BT_COEX_CFG_3WIRE) { + /* For 3-wire, configure the desired GPIO port for rx_clear */ + ar5416GpioCfgOutput(ah, ahp->ah_wlanActiveGpioSelect, + HAL_GPIO_OUTPUT_MUX_AS_WLAN_ACTIVE); + } else { + /* + * For 2-wire, configure the desired GPIO port + * for TX_FRAME output + */ + ar5416GpioCfgOutput(ah, ahp->ah_wlanActiveGpioSelect, + HAL_GPIO_OUTPUT_MUX_AS_TX_FRAME); + } + + /* + * Enable a weak pull down on BT_ACTIVE. + * When BT device is disabled, BT_ACTIVE might be floating. + */ + OS_REG_RMW(ah, AR_GPIO_PDPU, + (0x2 << (ahp->ah_btActiveGpioSelect * 2)), + (0x3 << (ahp->ah_btActiveGpioSelect * 2))); + + ahp->ah_btCoexEnabled = AH_TRUE; + + return (0); +} + +void +ar5416InitBTCoex(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + HALDEBUG(ah, HAL_DEBUG_BT_COEX, + "%s: called; configType=%d\n", + __func__, + ahp->ah_btCoexConfigType); + + if (ahp->ah_btCoexConfigType == HAL_BT_COEX_CFG_3WIRE) { + OS_REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, + (AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_BB | + AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB)); + + /* + * Set input mux for bt_prority_async and + * bt_active_async to GPIO pins + */ + OS_REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1, + AR_GPIO_INPUT_MUX1_BT_ACTIVE, + ahp->ah_btActiveGpioSelect); + OS_REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1, + AR_GPIO_INPUT_MUX1_BT_PRIORITY, + ahp->ah_btPriorityGpioSelect); + + /* + * Configure the desired GPIO ports for input + */ + ar5416GpioCfgInput(ah, ahp->ah_btActiveGpioSelect); + ar5416GpioCfgInput(ah, ahp->ah_btPriorityGpioSelect); + + /* + * Configure the antenna diversity setup. + * It's a no-op for AR9287; AR9285 overrides this + * as required. + */ + AH5416(ah)->ah_btCoexSetDiversity(ah); + + if (ahp->ah_btCoexEnabled) + ar5416BTCoexEnable(ah); + else + ar5416BTCoexDisable(ah); + } else if (ahp->ah_btCoexConfigType != HAL_BT_COEX_CFG_NONE) { + /* 2-wire */ + if (ahp->ah_btCoexEnabled) { + /* Connect bt_active_async to baseband */ + OS_REG_CLR_BIT(ah, AR_GPIO_INPUT_EN_VAL, + (AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_DEF | + AR_GPIO_INPUT_EN_VAL_BT_FREQUENCY_DEF)); + OS_REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, + AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB); + + /* + * Set input mux for bt_prority_async and + * bt_active_async to GPIO pins + */ + OS_REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1, + AR_GPIO_INPUT_MUX1_BT_ACTIVE, + ahp->ah_btActiveGpioSelect); + + /* Configure the desired GPIO ports for input */ + ar5416GpioCfgInput(ah, ahp->ah_btActiveGpioSelect); + + /* Enable coexistence on initialization */ + ar5416BTCoexEnable(ah); + } + } +} diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_btcoex.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_btcoex.h new file mode 100644 index 0000000000..2a8eca54f0 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_btcoex.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2011 Atheros Communications, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5416/ar5416_btcoex.h 237621 2012-06-27 03:00:29Z adrian $ + */ + +#ifndef __ATH_AR5416_BTCOEX_H__ +#define __ATH_AR5416_BTCOEX_H__ +/* + * Weight table configurations. + */ +#define AR5416_BT_WGHT 0xff55 +#define AR5416_STOMP_ALL_WLAN_WGHT 0xfcfc +#define AR5416_STOMP_LOW_WLAN_WGHT 0xa8a8 +#define AR5416_STOMP_NONE_WLAN_WGHT 0x0000 +#define AR5416_STOMP_ALL_FORCE_WLAN_WGHT 0xffff // Stomp BT even when WLAN is idle +#define AR5416_STOMP_LOW_FORCE_WLAN_WGHT 0xaaaa // Stomp BT even when WLAN is idle + +#endif /* __ATH_AR5416_BTCOEX_H__ */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_cal.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_cal.c index d51417f499..200b42b894 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_cal.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_cal.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5416/ar5416_cal.c 298939 2016-05-02 19:56:48Z pfg $ */ #include "opt_ah.h" @@ -663,7 +663,7 @@ ar5416LoadNF(struct ath_hal *ah, const struct ieee80211_channel *chan) * by the median we just loaded. This will be initial (and max) value * of next noise floor calibration the baseband does. */ - for (i = 0; i < AR5416_NUM_NF_READINGS; i ++) + for (i = 0; i < AR5416_NUM_NF_READINGS; i ++) { /* Don't write to EXT radio CCA registers unless in HT/40 mode */ /* XXX this check should really be cleaner! */ @@ -676,6 +676,7 @@ ar5416LoadNF(struct ath_hal *ah, const struct ieee80211_channel *chan) val |= (((uint32_t)(-50) << 1) & 0x1ff); OS_REG_WRITE(ah, ar5416_cca_regs[i], val); } + } } /* @@ -771,7 +772,7 @@ ar5416SanitizeNF(struct ath_hal *ah, int16_t *nf) /* - * Read the NF and check it against the noise floor threshhold + * Read the NF and check it against the noise floor threshold * * Return 0 if the NF calibration hadn't finished, 0 if it was * invalid, or > 0 for a valid NF reading. diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_cal.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_cal.h index a4f19e592c..df2d412e21 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_cal.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_cal.h @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5416/ar5416_cal.h 219480 2011-03-11 11:35:36Z adrian $ */ #ifndef _ATH_AR5416_CAL_H_ #define _ATH_AR5416_CAL_H_ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_cal_adcdc.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_cal_adcdc.c index 9083d3ab65..dd0a5a1cda 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_cal_adcdc.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_cal_adcdc.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5416/ar5416_cal_adcdc.c 203158 2010-01-29 10:07:17Z rpaulo $ */ #include "opt_ah.h" diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_cal_adcgain.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_cal_adcgain.c index 4af1ca4d67..3bffc2ec0b 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_cal_adcgain.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_cal_adcgain.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5416/ar5416_cal_adcgain.c 203158 2010-01-29 10:07:17Z rpaulo $ */ #include "opt_ah.h" diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_cal_iq.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_cal_iq.c index 939ba127ce..23b4d0fa9d 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_cal_iq.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_cal_iq.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5416/ar5416_cal_iq.c 211306 2010-08-14 15:28:15Z adrian $ */ #include "opt_ah.h" diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_eeprom.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_eeprom.c index 2947726d8e..74789d0562 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_eeprom.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_eeprom.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5416/ar5416_eeprom.c 203158 2010-01-29 10:07:17Z rpaulo $ */ #include "opt_ah.h" diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_gpio.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_gpio.c index dc56d15114..a0dff15231 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_gpio.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_gpio.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5416/ar5416_gpio.c 237611 2012-06-26 22:16:53Z adrian $ */ #include "opt_ah.h" @@ -35,7 +35,10 @@ static void cfgOutputMux(struct ath_hal *ah, uint32_t gpio, uint32_t type) { int addr; - uint32_t gpio_shift, reg; + uint32_t gpio_shift, tmp; + + HALDEBUG(ah, HAL_DEBUG_GPIO, "%s: gpio=%d, type=%d\n", + __func__, gpio, type); /* each MUX controls 6 GPIO pins */ if (gpio > 11) @@ -61,12 +64,17 @@ cfgOutputMux(struct ath_hal *ah, uint32_t gpio, uint32_t type) * will never be used. So it should be fine that bit 4 won't be * able to recover. */ - reg = OS_REG_READ(ah, addr); - if (addr == AR_GPIO_OUTPUT_MUX1 && !AR_SREV_MERLIN_20_OR_LATER(ah)) - reg = ((reg & 0x1F0) << 1) | (reg & ~0x1F0); - reg &= ~(0x1f << gpio_shift); - reg |= type << gpio_shift; - OS_REG_WRITE(ah, addr, reg); + if (AR_SREV_MERLIN_20_OR_LATER(ah) || + (addr != AR_GPIO_OUTPUT_MUX1)) { + OS_REG_RMW(ah, addr, (type << gpio_shift), + (0x1f << gpio_shift)); + } else { + tmp = OS_REG_READ(ah, addr); + tmp = ((tmp & 0x1F0) << 1) | (tmp & ~0x1F0); + tmp &= ~(0x1f << gpio_shift); + tmp |= type << gpio_shift; + OS_REG_WRITE(ah, addr, tmp); + } } /* @@ -77,20 +85,51 @@ ar5416GpioCfgOutput(struct ath_hal *ah, uint32_t gpio, HAL_GPIO_MUX_TYPE type) { uint32_t gpio_shift, reg; +#define N(a) (sizeof(a) / sizeof(a[0])) + + /* + * This table maps the HAL GPIO pins to the actual hardware + * values. + */ + static const u_int32_t MuxSignalConversionTable[] = { + AR_GPIO_OUTPUT_MUX_AS_OUTPUT, + AR_GPIO_OUTPUT_MUX_AS_PCIE_ATTENTION_LED, + AR_GPIO_OUTPUT_MUX_AS_PCIE_POWER_LED, + AR_GPIO_OUTPUT_MUX_AS_MAC_NETWORK_LED, + AR_GPIO_OUTPUT_MUX_AS_PCIE_POWER_LED, + AR_GPIO_OUTPUT_MUX_AS_RX_CLEAR_EXTERNAL, + AR_GPIO_OUTPUT_MUX_AS_TX_FRAME, + }; + HALASSERT(gpio < AH_PRIVATE(ah)->ah_caps.halNumGpioPins); - /* NB: type maps directly to hardware */ - cfgOutputMux(ah, gpio, type); - gpio_shift = gpio << 1; /* 2 bits per output mode */ + HALDEBUG(ah, HAL_DEBUG_GPIO, + "%s: gpio=%d, type=%d\n", __func__, gpio, type); + /* + * Convert HAL signal type definitions to hardware-specific values. + */ + if (type >= N(MuxSignalConversionTable)) { + ath_hal_printf(ah, "%s: mux %d is invalid!\n", + __func__, + type); + return AH_FALSE; + } + cfgOutputMux(ah, gpio, MuxSignalConversionTable[type]); + + /* 2 bits per output mode */ + gpio_shift = gpio << 1; + + /* Always drive, rather than tristate/drive low/drive high */ reg = OS_REG_READ(ah, AR_GPIO_OE_OUT); reg &= ~(AR_GPIO_OE_OUT_DRV << gpio_shift); reg |= AR_GPIO_OE_OUT_DRV_ALL << gpio_shift; OS_REG_WRITE(ah, AR_GPIO_OE_OUT, reg); return AH_TRUE; +#undef N } - + /* * Configure GPIO Input lines */ @@ -101,6 +140,8 @@ ar5416GpioCfgInput(struct ath_hal *ah, uint32_t gpio) HALASSERT(gpio < AH_PRIVATE(ah)->ah_caps.halNumGpioPins); + HALDEBUG(ah, HAL_DEBUG_GPIO, "%s: gpio=%d\n", __func__, gpio); + /* TODO: configure input mux for AR5416 */ /* If configured as input, set output to tristate */ gpio_shift = gpio << 1; @@ -122,13 +163,15 @@ ar5416GpioSet(struct ath_hal *ah, uint32_t gpio, uint32_t val) uint32_t reg; HALASSERT(gpio < AH_PRIVATE(ah)->ah_caps.halNumGpioPins); + HALDEBUG(ah, HAL_DEBUG_GPIO, + "%s: gpio=%d, val=%d\n", __func__, gpio, val); reg = OS_REG_READ(ah, AR_GPIO_IN_OUT); if (val & 1) reg |= AR_GPIO_BIT(gpio); - else + else reg &= ~AR_GPIO_BIT(gpio); - OS_REG_WRITE(ah, AR_GPIO_IN_OUT, reg); + OS_REG_WRITE(ah, AR_GPIO_IN_OUT, reg); return AH_TRUE; } @@ -146,6 +189,8 @@ ar5416GpioGet(struct ath_hal *ah, uint32_t gpio) * Read output value for all gpio's, shift it, * and verify whether the specific bit is set. */ + if (AR_SREV_KIWI_10_OR_LATER(ah)) + bits = MS(OS_REG_READ(ah, AR_GPIO_IN_OUT), AR9287_GPIO_IN_VAL); if (AR_SREV_KITE_10_OR_LATER(ah)) bits = MS(OS_REG_READ(ah, AR_GPIO_IN_OUT), AR9285_GPIO_IN_VAL); else if (AR_SREV_MERLIN_10_OR_LATER(ah)) @@ -165,6 +210,8 @@ ar5416GpioSetIntr(struct ath_hal *ah, u_int gpio, uint32_t ilevel) uint32_t val, mask; HALASSERT(gpio < AH_PRIVATE(ah)->ah_caps.halNumGpioPins); + HALDEBUG(ah, HAL_DEBUG_GPIO, + "%s: gpio=%d, ilevel=%d\n", __func__, gpio, ilevel); if (ilevel == HAL_GPIO_INTR_DISABLE) { val = MS(OS_REG_READ(ah, AR_INTR_ASYNC_ENABLE), diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_interrupts.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_interrupts.c index 03d3544a89..47ecf92000 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_interrupts.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_interrupts.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5416/ar5416_interrupts.c 265029 2014-04-27 23:31:42Z adrian $ */ #include "opt_ah.h" @@ -67,7 +67,16 @@ ar5416IsInterruptPending(struct ath_hal *ah) HAL_BOOL ar5416GetPendingInterrupts(struct ath_hal *ah, HAL_INT *masked) { - uint32_t isr, isr0, isr1, sync_cause = 0; + uint32_t isr, isr0, isr1, sync_cause = 0, o_sync_cause = 0; + HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps; + +#ifdef AH_INTERRUPT_DEBUGGING + /* + * Blank the interrupt debugging area regardless. + */ + bzero(&ah->ah_intrstate, sizeof(ah->ah_intrstate)); + ah->ah_syncstate = 0; +#endif /* * Verify there's a mac interrupt and the RTC is on. @@ -81,7 +90,10 @@ ar5416GetPendingInterrupts(struct ath_hal *ah, HAL_INT *masked) isr = OS_REG_READ(ah, AR_ISR); else isr = 0; - sync_cause = OS_REG_READ(ah, AR_INTR_SYNC_CAUSE); +#ifdef AH_INTERRUPT_DEBUGGING + ah->ah_syncstate = +#endif + o_sync_cause = sync_cause = OS_REG_READ(ah, AR_INTR_SYNC_CAUSE); sync_cause &= AR_INTR_SYNC_DEFAULT; *masked = 0; @@ -89,6 +101,16 @@ ar5416GetPendingInterrupts(struct ath_hal *ah, HAL_INT *masked) return AH_FALSE; } +#ifdef AH_INTERRUPT_DEBUGGING + ah->ah_intrstate[0] = isr; + ah->ah_intrstate[1] = OS_REG_READ(ah, AR_ISR_S0); + ah->ah_intrstate[2] = OS_REG_READ(ah, AR_ISR_S1); + ah->ah_intrstate[3] = OS_REG_READ(ah, AR_ISR_S2); + ah->ah_intrstate[4] = OS_REG_READ(ah, AR_ISR_S3); + ah->ah_intrstate[5] = OS_REG_READ(ah, AR_ISR_S4); + ah->ah_intrstate[6] = OS_REG_READ(ah, AR_ISR_S5); +#endif + if (isr != 0) { struct ath_hal_5212 *ahp = AH5212(ah); uint32_t mask2; @@ -110,48 +132,102 @@ ar5416GetPendingInterrupts(struct ath_hal *ah, HAL_INT *masked) mask2 |= HAL_INT_CST; if (isr2 & AR_ISR_S2_TSFOOR) mask2 |= HAL_INT_TSFOOR; + + /* + * Don't mask out AR_BCNMISC; instead mask + * out what causes it. + */ + OS_REG_WRITE(ah, AR_ISR_S2, isr2); + isr &= ~AR_ISR_BCNMISC; } - isr = OS_REG_READ(ah, AR_ISR_RAC); if (isr == 0xffffffff) { *masked = 0; return AH_FALSE; } *masked = isr & HAL_INT_COMMON; - if (isr & (AR_ISR_RXOK | AR_ISR_RXERR)) - *masked |= HAL_INT_RX; - if (isr & (AR_ISR_TXOK | AR_ISR_TXDESC | AR_ISR_TXERR | AR_ISR_TXEOL)) { - *masked |= HAL_INT_TX; - isr0 = OS_REG_READ(ah, AR_ISR_S0_S); - ahp->ah_intrTxqs |= MS(isr0, AR_ISR_S0_QCU_TXOK); - ahp->ah_intrTxqs |= MS(isr0, AR_ISR_S0_QCU_TXDESC); - isr1 = OS_REG_READ(ah, AR_ISR_S1_S); - ahp->ah_intrTxqs |= MS(isr1, AR_ISR_S1_QCU_TXERR); - ahp->ah_intrTxqs |= MS(isr1, AR_ISR_S1_QCU_TXEOL); - } - if (AR_SREV_MERLIN(ah) || AR_SREV_KITE(ah)) { - uint32_t isr5; - isr5 = OS_REG_READ(ah, AR_ISR_S5_S); - if (isr5 & AR_ISR_S5_TIM_TIMER) - *masked |= HAL_INT_TIM_TIMER; - } - - /* Interrupt Mitigation on AR5416 */ -#ifdef AH_AR5416_INTERRUPT_MITIGATION if (isr & (AR_ISR_RXMINTR | AR_ISR_RXINTM)) *masked |= HAL_INT_RX; if (isr & (AR_ISR_TXMINTR | AR_ISR_TXINTM)) *masked |= HAL_INT_TX; + + /* + * When doing RX interrupt mitigation, the RXOK bit is set + * in AR_ISR even if the relevant bit in AR_IMR is clear. + * Since this interrupt may be due to another source, don't + * just automatically set HAL_INT_RX if it's set, otherwise + * we could prematurely service the RX queue. + * + * In some cases, the driver can even handle all the RX + * frames just before the mitigation interrupt fires. + * The subsequent RX processing trip will then end up + * processing 0 frames. + */ +#ifdef AH_AR5416_INTERRUPT_MITIGATION + if (isr & AR_ISR_RXERR) + *masked |= HAL_INT_RX; +#else + if (isr & (AR_ISR_RXOK | AR_ISR_RXERR)) + *masked |= HAL_INT_RX; #endif + + if (isr & (AR_ISR_TXOK | AR_ISR_TXDESC | AR_ISR_TXERR | + AR_ISR_TXEOL)) { + *masked |= HAL_INT_TX; + + isr0 = OS_REG_READ(ah, AR_ISR_S0); + OS_REG_WRITE(ah, AR_ISR_S0, isr0); + isr1 = OS_REG_READ(ah, AR_ISR_S1); + OS_REG_WRITE(ah, AR_ISR_S1, isr1); + + /* + * Don't clear the primary ISR TX bits, clear + * what causes them (S0/S1.) + */ + isr &= ~(AR_ISR_TXOK | AR_ISR_TXDESC | + AR_ISR_TXERR | AR_ISR_TXEOL); + + ahp->ah_intrTxqs |= MS(isr0, AR_ISR_S0_QCU_TXOK); + ahp->ah_intrTxqs |= MS(isr0, AR_ISR_S0_QCU_TXDESC); + ahp->ah_intrTxqs |= MS(isr1, AR_ISR_S1_QCU_TXERR); + ahp->ah_intrTxqs |= MS(isr1, AR_ISR_S1_QCU_TXEOL); + } + + if ((isr & AR_ISR_GENTMR) || (! pCap->halAutoSleepSupport)) { + uint32_t isr5; + isr5 = OS_REG_READ(ah, AR_ISR_S5); + OS_REG_WRITE(ah, AR_ISR_S5, isr5); + isr &= ~AR_ISR_GENTMR; + + if (! pCap->halAutoSleepSupport) + if (isr5 & AR_ISR_S5_TIM_TIMER) + *masked |= HAL_INT_TIM_TIMER; + } *masked |= mask2; } + /* + * Since we're not using AR_ISR_RAC, clear the status bits + * for handled interrupts here. For bits whose interrupt + * source is a secondary register, those bits should've been + * masked out - instead of those bits being written back, + * their source (ie, the secondary status registers) should + * be cleared. That way there are no race conditions with + * new triggers coming in whilst they've been read/cleared. + */ + OS_REG_WRITE(ah, AR_ISR, isr); + /* Flush previous write */ + OS_REG_READ(ah, AR_ISR); + if (AR_SREV_HOWL(ah)) return AH_TRUE; if (sync_cause != 0) { + HALDEBUG(ah, HAL_DEBUG_INTERRUPT, "%s: sync_cause=0x%x\n", + __func__, + o_sync_cause); if (sync_cause & (AR_INTR_SYNC_HOST1_FATAL | AR_INTR_SYNC_HOST1_PERR)) { *masked |= HAL_INT_FATAL; } @@ -216,18 +292,12 @@ ar5416SetInterrupts(struct ath_hal *ah, HAL_INT ints) * Overwrite default mask if Interrupt mitigation * is specified for AR5416 */ - mask = ints & HAL_INT_COMMON; - if (ints & HAL_INT_TX) - mask |= AR_IMR_TXMINTR | AR_IMR_TXINTM; if (ints & HAL_INT_RX) mask |= AR_IMR_RXERR | AR_IMR_RXMINTR | AR_IMR_RXINTM; - if (ints & HAL_INT_TX) { - if (ahp->ah_txErrInterruptMask) - mask |= AR_IMR_TXERR; - if (ahp->ah_txEolInterruptMask) - mask |= AR_IMR_TXEOL; - } #else + if (ints & HAL_INT_RX) + mask |= AR_IMR_RXOK | AR_IMR_RXERR | AR_IMR_RXDESC; +#endif if (ints & HAL_INT_TX) { if (ahp->ah_txOkInterruptMask) mask |= AR_IMR_TXOK; @@ -237,10 +307,9 @@ ar5416SetInterrupts(struct ath_hal *ah, HAL_INT ints) mask |= AR_IMR_TXDESC; if (ahp->ah_txEolInterruptMask) mask |= AR_IMR_TXEOL; + if (ahp->ah_txUrnInterruptMask) + mask |= AR_IMR_TXURN; } - if (ints & HAL_INT_RX) - mask |= AR_IMR_RXOK | AR_IMR_RXERR | AR_IMR_RXDESC; -#endif if (ints & (HAL_INT_BMISC)) { mask |= AR_IMR_BCNMISC; if (ints & HAL_INT_TIM) @@ -268,6 +337,9 @@ ar5416SetInterrupts(struct ath_hal *ah, HAL_INT ints) /* Write the new IMR and store off our SW copy. */ HALDEBUG(ah, HAL_DEBUG_INTERRUPT, "%s: new IMR 0x%x\n", __func__, mask); OS_REG_WRITE(ah, AR_IMR, mask); + /* Flush write */ + (void) OS_REG_READ(ah, AR_IMR); + mask = OS_REG_READ(ah, AR_IMR_S2) & ~(AR_IMR_S2_TIM | AR_IMR_S2_DTIM | AR_IMR_S2_DTIMSYNC | diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_keycache.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_keycache.c index 533fe03e22..79bc5e9a55 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_keycache.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_keycache.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5416/ar5416_keycache.c 203158 2010-01-29 10:07:17Z rpaulo $ */ #include "opt_ah.h" diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_misc.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_misc.c index 68c4117436..fa7409b19d 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_misc.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_misc.c @@ -14,21 +14,21 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5416/ar5416_misc.c 250856 2013-05-21 14:28:05Z adrian $ */ #include "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ah_devid.h" -#ifdef AH_DEBUG #include "ah_desc.h" /* NB: for HAL_PHYERR* */ -#endif #include "ar5416/ar5416.h" #include "ar5416/ar5416reg.h" #include "ar5416/ar5416phy.h" +#include "ah_eeprom_v14.h" /* for owl_get_ntxchains() */ + /* * Return the wireless modes (a,b,g,n,t) supported by hardware. * @@ -75,21 +75,29 @@ ar5416SetLedState(struct ath_hal *ah, HAL_LED_STATE state) AR_MAC_LED_ASSOC_NONE, AR_MAC_LED_ASSOC_NONE, }; - uint32_t bits; if (AR_SREV_HOWL(ah)) return; - bits = OS_REG_READ(ah, AR_MAC_LED); - bits = (bits &~ AR_MAC_LED_MODE) - | SM(AR_MAC_LED_MODE_POWON, AR_MAC_LED_MODE) -#if 1 - | SM(AR_MAC_LED_MODE_NETON, AR_MAC_LED_MODE) -#endif - ; - bits = (bits &~ AR_MAC_LED_ASSOC) - | SM(ledbits[state & 0x7], AR_MAC_LED_ASSOC); - OS_REG_WRITE(ah, AR_MAC_LED, bits); + /* + * Set the blink operating mode. + */ + OS_REG_RMW_FIELD(ah, AR_MAC_LED, + AR_MAC_LED_ASSOC, ledbits[state & 0x7]); + + /* XXX Blink slow mode? */ + /* XXX Blink threshold? */ + /* XXX Blink sleep hystersis? */ + + /* + * Set the LED blink configuration to be proportional + * to the current TX and RX filter bytes. (Ie, RX'ed + * frames that don't match the filter are ignored.) + * This means that higher TX/RX throughput will result + * in the blink rate increasing. + */ + OS_REG_RMW_FIELD(ah, AR_MAC_LED, AR_MAC_LED_MODE, + AR_MAC_LED_MODE_PROP); } /* @@ -120,9 +128,36 @@ ar5416GetTsf64(struct ath_hal *ah) return (((uint64_t) u32) << 32) | ((uint64_t) low2); } +/* + * Update the TSF. + * + * The full TSF is only updated once the upper 32 bits have + * been written. Writing only the lower 32 bits of the TSF + * will not actually correctly update the TSF. + * + * The #if 0'ed code is to check whether the previous TSF + * reset or write has completed before writing to the + * TSF. Strictly speaking, it should be also checked before + * reading the TSF as the write/reset may not have completed. + */ void ar5416SetTsf64(struct ath_hal *ah, uint64_t tsf64) { + /* XXX check if this is correct! */ +#if 0 + int i; + uint32_t v; + + for (i = 0; i < 10; i++) { + v = OS_REG_READ(ah, AR_SLP32_MODE); + if ((v & AR_SLP32_TSF_WRITE_STATUS) == 0) + break; + OS_DELAY(10); + } + if (i == 10) + ath_hal_printf(ah, "%s: couldn't slew things right!\n", __func__); +#endif + OS_REG_WRITE(ah, AR_TSF_L32, tsf64 & 0xffffffff); OS_REG_WRITE(ah, AR_TSF_U32, (tsf64 >> 32) & 0xffffffff); } @@ -145,6 +180,14 @@ ar5416ResetTsf(struct ath_hal *ah) OS_REG_WRITE(ah, AR_RESET_TSF, AR_RESET_TSF_ONCE); } +uint32_t +ar5416GetCurRssi(struct ath_hal *ah) +{ + if (AR_SREV_OWL(ah)) + return (OS_REG_READ(ah, AR_PHY_CURRENT_RSSI) & 0xff); + return (OS_REG_READ(ah, AR9130_PHY_CURRENT_RSSI) & 0xff); +} + HAL_BOOL ar5416SetAntennaSwitch(struct ath_hal *ah, HAL_ANT_SETTING settings) { @@ -162,13 +205,76 @@ ar5416SetDecompMask(struct ath_hal *ah, uint16_t keyidx, int en) void ar5416SetCoverageClass(struct ath_hal *ah, uint8_t coverageclass, int now) { - AH_PRIVATE(ah)->ah_coverageClass = coverageclass; + + ar5212SetCoverageClass(ah, coverageclass, now); +} + +/* + * Return the busy for rx_frame, rx_clear, and tx_frame + */ +HAL_BOOL +ar5416GetMibCycleCounts(struct ath_hal *ah, HAL_SURVEY_SAMPLE *hsample) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + u_int32_t good = AH_TRUE; + + /* XXX freeze/unfreeze mib counters */ + uint32_t rc = OS_REG_READ(ah, AR_RCCNT); + uint32_t ec = OS_REG_READ(ah, AR_EXTRCCNT); + uint32_t rf = OS_REG_READ(ah, AR_RFCNT); + uint32_t tf = OS_REG_READ(ah, AR_TFCNT); + uint32_t cc = OS_REG_READ(ah, AR_CCCNT); /* read cycles last */ + + if (ahp->ah_cycleCount == 0 || ahp->ah_cycleCount > cc) { + /* + * Cycle counter wrap (or initial call); it's not possible + * to accurately calculate a value because the registers + * right shift rather than wrap--so punt and return 0. + */ + HALDEBUG(ah, HAL_DEBUG_ANY, + "%s: cycle counter wrap. ExtBusy = 0\n", __func__); + good = AH_FALSE; + } else { + hsample->cycle_count = cc - ahp->ah_cycleCount; + hsample->chan_busy = rc - ahp->ah_ctlBusy; + hsample->ext_chan_busy = ec - ahp->ah_extBusy; + hsample->rx_busy = rf - ahp->ah_rxBusy; + hsample->tx_busy = tf - ahp->ah_txBusy; + } + + /* + * Keep a copy of the MIB results so the next sample has something + * to work from. + */ + ahp->ah_cycleCount = cc; + ahp->ah_rxBusy = rf; + ahp->ah_ctlBusy = rc; + ahp->ah_txBusy = tf; + ahp->ah_extBusy = ec; + + return (good); +} + +/* + * Setup the TX/RX chainmasks - this needs to be done before a call + * to the reset method as it doesn't update the hardware. + */ +void +ar5416SetChainMasks(struct ath_hal *ah, uint32_t tx_chainmask, + uint32_t rx_chainmask) +{ + HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps; + + AH5416(ah)->ah_tx_chainmask = tx_chainmask & pCap->halTxChainMask; + AH5416(ah)->ah_rx_chainmask = rx_chainmask & pCap->halRxChainMask; } /* * Return approximation of extension channel busy over an time interval * 0% (clear) -> 100% (busy) * + * XXX TODO: update this to correctly sample all the counters, + * rather than a subset of it. */ uint32_t ar5416Get11nExtBusy(struct ath_hal *ah) @@ -278,7 +384,7 @@ ar5416Get11nRxClear(struct ath_hal *ah) rxclear |= HAL_RX_CLEAR_CTL_LOW; } /* extension channel */ - if (val & AR_DIAG_RXCLEAR_CTL_LOW) { + if (val & AR_DIAG_RXCLEAR_EXT_LOW) { rxclear |= HAL_RX_CLEAR_EXT_LOW; } return rxclear; @@ -359,12 +465,64 @@ ar5416GetCapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type, HAL_OK : HAL_ENOTSUPP; case HAL_CAP_DIVERSITY: /* disable classic fast diversity */ return HAL_ENXIO; + case HAL_CAP_ENFORCE_TXOP: + if (capability == 0) + return (HAL_OK); + if (capability != 1) + return (HAL_ENOTSUPP); + (*result) = + !! (AH5212(ah)->ah_miscMode & AR_PCU_TXOP_TBTT_LIMIT_ENA); + return (HAL_OK); default: break; } return ar5212GetCapability(ah, type, capability, result); } +HAL_BOOL +ar5416SetCapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type, + u_int32_t capability, u_int32_t setting, HAL_STATUS *status) +{ + HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps; + + switch (type) { + case HAL_CAP_RX_CHAINMASK: + setting &= ath_hal_eepromGet(ah, AR_EEP_RXMASK, NULL); + pCap->halRxChainMask = setting; + if (owl_get_ntxchains(setting) > 2) + pCap->halRxStreams = 2; + else + pCap->halRxStreams = 1; + return AH_TRUE; + case HAL_CAP_TX_CHAINMASK: + setting &= ath_hal_eepromGet(ah, AR_EEP_TXMASK, NULL); + pCap->halTxChainMask = setting; + if (owl_get_ntxchains(setting) > 2) + pCap->halTxStreams = 2; + else + pCap->halTxStreams = 1; + return AH_TRUE; + case HAL_CAP_ENFORCE_TXOP: + if (capability != 1) + return AH_FALSE; + if (setting) { + AH5212(ah)->ah_miscMode + |= AR_PCU_TXOP_TBTT_LIMIT_ENA; + OS_REG_SET_BIT(ah, AR_MISC_MODE, + AR_PCU_TXOP_TBTT_LIMIT_ENA); + } else { + AH5212(ah)->ah_miscMode + &= ~AR_PCU_TXOP_TBTT_LIMIT_ENA; + OS_REG_CLR_BIT(ah, AR_MISC_MODE, + AR_PCU_TXOP_TBTT_LIMIT_ENA); + } + return AH_TRUE; + default: + break; + } + return ar5212SetCapability(ah, type, capability, setting, status); +} + static int ar5416DetectMacHang(struct ath_hal *ah); static int ar5416DetectBBHang(struct ath_hal *ah); @@ -400,36 +558,6 @@ ar5416GetDiagState(struct ath_hal *ah, int request, args, argsize, result, resultsize); } -typedef struct { - uint32_t dma_dbg_3; - uint32_t dma_dbg_4; - uint32_t dma_dbg_5; - uint32_t dma_dbg_6; -} mac_dbg_regs_t; - -typedef enum { - dcu_chain_state = 0x1, - dcu_complete_state = 0x2, - qcu_state = 0x4, - qcu_fsp_ok = 0x8, - qcu_fsp_state = 0x10, - qcu_stitch_state = 0x20, - qcu_fetch_state = 0x40, - qcu_complete_state = 0x80 -} hal_mac_hangs_t; - -typedef struct { - int states; - uint8_t dcu_chain_state; - uint8_t dcu_complete_state; - uint8_t qcu_state; - uint8_t qcu_fsp_ok; - uint8_t qcu_fsp_state; - uint8_t qcu_stitch_state; - uint8_t qcu_fetch_state; - uint8_t qcu_complete_state; -} hal_mac_hang_check_t; - HAL_BOOL ar5416SetRifsDelay(struct ath_hal *ah, const struct ieee80211_channel *chan, HAL_BOOL enable) @@ -624,157 +752,3 @@ ar5416DetectBBHang(struct ath_hal *ah) #undef N } #undef NUM_STATUS_READS - -/* - * Get the radar parameter values and return them in the pe - * structure - */ -void -ar5416GetDfsThresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe) -{ - uint32_t val, temp; - - val = OS_REG_READ(ah, AR_PHY_RADAR_0); - - temp = MS(val,AR_PHY_RADAR_0_FIRPWR); - temp |= 0xFFFFFF80; - pe->pe_firpwr = temp; - pe->pe_rrssi = MS(val, AR_PHY_RADAR_0_RRSSI); - pe->pe_height = MS(val, AR_PHY_RADAR_0_HEIGHT); - pe->pe_prssi = MS(val, AR_PHY_RADAR_0_PRSSI); - pe->pe_inband = MS(val, AR_PHY_RADAR_0_INBAND); - - val = OS_REG_READ(ah, AR_PHY_RADAR_1); - temp = val & AR_PHY_RADAR_1_RELPWR_ENA; - pe->pe_relpwr = MS(val, AR_PHY_RADAR_1_RELPWR_THRESH); - if (temp) - pe->pe_relpwr |= HAL_PHYERR_PARAM_ENABLE; - temp = val & AR_PHY_RADAR_1_RELSTEP_CHECK; - pe->pe_relstep = MS(val, AR_PHY_RADAR_1_RELSTEP_THRESH); - if (temp) - pe->pe_enabled = 1; - else - pe->pe_enabled = 0; - - pe->pe_maxlen = MS(val, AR_PHY_RADAR_1_MAXLEN); - pe->pe_extchannel = !! (OS_REG_READ(ah, AR_PHY_RADAR_EXT) & - AR_PHY_RADAR_EXT_ENA); - - pe->pe_usefir128 = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) & - AR_PHY_RADAR_1_USE_FIR128); - pe->pe_blockradar = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) & - AR_PHY_RADAR_1_BLOCK_CHECK); - pe->pe_enmaxrssi = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) & - AR_PHY_RADAR_1_MAX_RRSSI); -} - -/* - * Enable radar detection and set the radar parameters per the - * values in pe - */ -void -ar5416EnableDfs(struct ath_hal *ah, HAL_PHYERR_PARAM *pe) -{ - uint32_t val; - - val = OS_REG_READ(ah, AR_PHY_RADAR_0); - - if (pe->pe_firpwr != HAL_PHYERR_PARAM_NOVAL) { - val &= ~AR_PHY_RADAR_0_FIRPWR; - val |= SM(pe->pe_firpwr, AR_PHY_RADAR_0_FIRPWR); - } - if (pe->pe_rrssi != HAL_PHYERR_PARAM_NOVAL) { - val &= ~AR_PHY_RADAR_0_RRSSI; - val |= SM(pe->pe_rrssi, AR_PHY_RADAR_0_RRSSI); - } - if (pe->pe_height != HAL_PHYERR_PARAM_NOVAL) { - val &= ~AR_PHY_RADAR_0_HEIGHT; - val |= SM(pe->pe_height, AR_PHY_RADAR_0_HEIGHT); - } - if (pe->pe_prssi != HAL_PHYERR_PARAM_NOVAL) { - val &= ~AR_PHY_RADAR_0_PRSSI; - val |= SM(pe->pe_prssi, AR_PHY_RADAR_0_PRSSI); - } - if (pe->pe_inband != HAL_PHYERR_PARAM_NOVAL) { - val &= ~AR_PHY_RADAR_0_INBAND; - val |= SM(pe->pe_inband, AR_PHY_RADAR_0_INBAND); - } - - /*Enable FFT data*/ - val |= AR_PHY_RADAR_0_FFT_ENA; - - OS_REG_WRITE(ah, AR_PHY_RADAR_0, val | AR_PHY_RADAR_0_ENA); - - if (pe->pe_usefir128 == 1) - OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_USE_FIR128); - else if (pe->pe_usefir128 == 0) - OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_USE_FIR128); - - if (pe->pe_enmaxrssi == 1) - OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_MAX_RRSSI); - else if (pe->pe_enmaxrssi == 0) - OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_MAX_RRSSI); - - if (pe->pe_blockradar == 1) - OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_BLOCK_CHECK); - else if (pe->pe_blockradar == 0) - OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_BLOCK_CHECK); - - if (pe->pe_maxlen != HAL_PHYERR_PARAM_NOVAL) { - val = OS_REG_READ(ah, AR_PHY_RADAR_1); - val &= ~AR_PHY_RADAR_1_MAXLEN; - val |= SM(pe->pe_maxlen, AR_PHY_RADAR_1_MAXLEN); - OS_REG_WRITE(ah, AR_PHY_RADAR_1, val); - } - - /* - * Enable HT/40 if the upper layer asks; - * it should check the channel is HT/40 and HAL_CAP_EXT_CHAN_DFS - * is available. - */ - if (pe->pe_extchannel == 1) - OS_REG_SET_BIT(ah, AR_PHY_RADAR_EXT, AR_PHY_RADAR_EXT_ENA); - else if (pe->pe_extchannel == 0) - OS_REG_CLR_BIT(ah, AR_PHY_RADAR_EXT, AR_PHY_RADAR_EXT_ENA); - - if (pe->pe_relstep != HAL_PHYERR_PARAM_NOVAL) { - val = OS_REG_READ(ah, AR_PHY_RADAR_1); - val &= ~AR_PHY_RADAR_1_RELSTEP_THRESH; - val |= SM(pe->pe_relstep, AR_PHY_RADAR_1_RELSTEP_THRESH); - OS_REG_WRITE(ah, AR_PHY_RADAR_1, val); - } - if (pe->pe_relpwr != HAL_PHYERR_PARAM_NOVAL) { - val = OS_REG_READ(ah, AR_PHY_RADAR_1); - val &= ~AR_PHY_RADAR_1_RELPWR_THRESH; - val |= SM(pe->pe_relpwr, AR_PHY_RADAR_1_RELPWR_THRESH); - OS_REG_WRITE(ah, AR_PHY_RADAR_1, val); - } -} - -/* - * Extract the radar event information from the given phy error. - * - * Returns AH_TRUE if the phy error was actually a phy error, - * AH_FALSE if the phy error wasn't a phy error. - */ -HAL_BOOL -ar5416ProcessRadarEvent(struct ath_hal *ah, struct ath_rx_status *rxs, - uint64_t fulltsf, const char *buf, HAL_DFS_EVENT *event) -{ - /* - * For now, this isn't implemented. - */ - return AH_FALSE; -} - -/* - * Return whether fast-clock is currently enabled for this - * channel. - */ -HAL_BOOL -ar5416IsFastClockEnabled(struct ath_hal *ah) -{ - struct ath_hal_private *ahp = AH_PRIVATE(ah); - - return IS_5GHZ_FAST_CLOCK_EN(ah, ahp->ah_curchan); -} diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_phy.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_phy.c index 1cfd9644d2..911030a1ee 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_phy.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_phy.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5416/ar5416_phy.c 218908 2011-02-21 05:10:34Z adrian $ */ #include "opt_ah.h" diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_power.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_power.c index 2d8c7f9727..bc5cf889de 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_power.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_power.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5416/ar5416_power.c 265112 2014-04-30 02:03:13Z adrian $ */ #include "opt_ah.h" @@ -50,6 +50,7 @@ ar5416SetPowerModeAwake(struct ath_hal *ah, int setChip) & AR_RTC_PM_STATUS_M) == AR_RTC_STATUS_SHUTDOWN) { if (!ar5416SetResetReg(ah, HAL_RESET_POWER_ON)) goto bad; + AH5416(ah)->ah_initPLL(ah, AH_NULL); } if (AR_SREV_HOWL(ah)) @@ -123,7 +124,6 @@ ar5416SetPowerModeNetworkSleep(struct ath_hal *ah, int setChip) HAL_BOOL ar5416SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip) { - struct ath_hal_5212 *ahp = AH5212(ah); #ifdef AH_DEBUG static const char* modes[] = { "AWAKE", @@ -133,27 +133,35 @@ ar5416SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip) }; #endif int status = AH_TRUE; + +#if 0 if (!setChip) return AH_TRUE; +#endif HALDEBUG(ah, HAL_DEBUG_POWER, "%s: %s -> %s (%s)\n", __func__, - modes[ahp->ah_powerMode], modes[mode], setChip ? "set chip " : ""); + modes[ah->ah_powerMode], modes[mode], setChip ? "set chip " : ""); switch (mode) { case HAL_PM_AWAKE: + if (setChip) + ah->ah_powerMode = mode; status = ar5416SetPowerModeAwake(ah, setChip); break; case HAL_PM_FULL_SLEEP: ar5416SetPowerModeSleep(ah, setChip); + if (setChip) + ah->ah_powerMode = mode; break; case HAL_PM_NETWORK_SLEEP: ar5416SetPowerModeNetworkSleep(ah, setChip); + if (setChip) + ah->ah_powerMode = mode; break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unknown power mode 0x%x\n", __func__, mode); return AH_FALSE; } - ahp->ah_powerMode = mode; return status; } diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_radar.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_radar.c new file mode 100644 index 0000000000..232bd085ab --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_radar.c @@ -0,0 +1,432 @@ +/* + * Copyright (c) 2010-2011 Atheros Communications, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5416/ar5416_radar.c 297793 2016-04-10 23:07:00Z pfg $ + */ +#include "opt_ah.h" + +#include "ah.h" +#include "ah_internal.h" +#include "ah_devid.h" +#include "ah_desc.h" /* NB: for HAL_PHYERR* */ + +#include "ar5416/ar5416.h" +#include "ar5416/ar5416reg.h" +#include "ar5416/ar5416phy.h" + +#include "ah_eeprom_v14.h" /* for owl_get_ntxchains() */ + +/* + * These are default parameters for the AR5416 and + * later 802.11n NICs. They simply enable some + * radar pulse event generation. + * + * These are very likely not valid for the AR5212 era + * NICs. + * + * Since these define signal sizing and threshold + * parameters, they may need changing based on the + * specific antenna and receive amplifier + * configuration. + */ +#define AR5416_DFS_FIRPWR -33 +#define AR5416_DFS_RRSSI 20 +#define AR5416_DFS_HEIGHT 10 +#define AR5416_DFS_PRSSI 15 +#define AR5416_DFS_INBAND 15 +#define AR5416_DFS_RELPWR 8 +#define AR5416_DFS_RELSTEP 12 +#define AR5416_DFS_MAXLEN 255 + +HAL_BOOL +ar5416GetDfsDefaultThresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe) +{ + + /* + * These are general examples of the parameter values + * to use when configuring radar pulse detection for + * the AR5416, AR91xx, AR92xx NICs. They are only + * for testing and do require tuning depending upon the + * hardware and deployment specifics. + */ + pe->pe_firpwr = AR5416_DFS_FIRPWR; + pe->pe_rrssi = AR5416_DFS_RRSSI; + pe->pe_height = AR5416_DFS_HEIGHT; + pe->pe_prssi = AR5416_DFS_PRSSI; + pe->pe_inband = AR5416_DFS_INBAND; + pe->pe_relpwr = AR5416_DFS_RELPWR; + pe->pe_relstep = AR5416_DFS_RELSTEP; + pe->pe_maxlen = AR5416_DFS_MAXLEN; + + return (AH_TRUE); +} + +/* + * Get the radar parameter values and return them in the pe + * structure + */ +void +ar5416GetDfsThresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe) +{ + uint32_t val, temp; + + val = OS_REG_READ(ah, AR_PHY_RADAR_0); + + temp = MS(val,AR_PHY_RADAR_0_FIRPWR); + temp |= 0xFFFFFF80; + pe->pe_firpwr = temp; + pe->pe_rrssi = MS(val, AR_PHY_RADAR_0_RRSSI); + pe->pe_height = MS(val, AR_PHY_RADAR_0_HEIGHT); + pe->pe_prssi = MS(val, AR_PHY_RADAR_0_PRSSI); + pe->pe_inband = MS(val, AR_PHY_RADAR_0_INBAND); + + /* RADAR_1 values */ + val = OS_REG_READ(ah, AR_PHY_RADAR_1); + pe->pe_relpwr = MS(val, AR_PHY_RADAR_1_RELPWR_THRESH); + pe->pe_relstep = MS(val, AR_PHY_RADAR_1_RELSTEP_THRESH); + pe->pe_maxlen = MS(val, AR_PHY_RADAR_1_MAXLEN); + + pe->pe_extchannel = !! (OS_REG_READ(ah, AR_PHY_RADAR_EXT) & + AR_PHY_RADAR_EXT_ENA); + + pe->pe_usefir128 = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) & + AR_PHY_RADAR_1_USE_FIR128); + pe->pe_blockradar = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) & + AR_PHY_RADAR_1_BLOCK_CHECK); + pe->pe_enmaxrssi = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) & + AR_PHY_RADAR_1_MAX_RRSSI); + pe->pe_enabled = !! + (OS_REG_READ(ah, AR_PHY_RADAR_0) & AR_PHY_RADAR_0_ENA); + pe->pe_enrelpwr = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) & + AR_PHY_RADAR_1_RELPWR_ENA); + pe->pe_en_relstep_check = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) & + AR_PHY_RADAR_1_RELSTEP_CHECK); +} + +/* + * Enable radar detection and set the radar parameters per the + * values in pe + */ +void +ar5416EnableDfs(struct ath_hal *ah, HAL_PHYERR_PARAM *pe) +{ + uint32_t val; + + val = OS_REG_READ(ah, AR_PHY_RADAR_0); + + if (pe->pe_firpwr != HAL_PHYERR_PARAM_NOVAL) { + val &= ~AR_PHY_RADAR_0_FIRPWR; + val |= SM(pe->pe_firpwr, AR_PHY_RADAR_0_FIRPWR); + } + if (pe->pe_rrssi != HAL_PHYERR_PARAM_NOVAL) { + val &= ~AR_PHY_RADAR_0_RRSSI; + val |= SM(pe->pe_rrssi, AR_PHY_RADAR_0_RRSSI); + } + if (pe->pe_height != HAL_PHYERR_PARAM_NOVAL) { + val &= ~AR_PHY_RADAR_0_HEIGHT; + val |= SM(pe->pe_height, AR_PHY_RADAR_0_HEIGHT); + } + if (pe->pe_prssi != HAL_PHYERR_PARAM_NOVAL) { + val &= ~AR_PHY_RADAR_0_PRSSI; + val |= SM(pe->pe_prssi, AR_PHY_RADAR_0_PRSSI); + } + if (pe->pe_inband != HAL_PHYERR_PARAM_NOVAL) { + val &= ~AR_PHY_RADAR_0_INBAND; + val |= SM(pe->pe_inband, AR_PHY_RADAR_0_INBAND); + } + + /*Enable FFT data*/ + val |= AR_PHY_RADAR_0_FFT_ENA; + OS_REG_WRITE(ah, AR_PHY_RADAR_0, val); + + /* Implicitly enable */ + if (pe->pe_enabled == 1) + OS_REG_SET_BIT(ah, AR_PHY_RADAR_0, AR_PHY_RADAR_0_ENA); + else if (pe->pe_enabled == 0) + OS_REG_CLR_BIT(ah, AR_PHY_RADAR_0, AR_PHY_RADAR_0_ENA); + + if (pe->pe_usefir128 == 1) + OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_USE_FIR128); + else if (pe->pe_usefir128 == 0) + OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_USE_FIR128); + + if (pe->pe_enmaxrssi == 1) + OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_MAX_RRSSI); + else if (pe->pe_enmaxrssi == 0) + OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_MAX_RRSSI); + + if (pe->pe_blockradar == 1) + OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_BLOCK_CHECK); + else if (pe->pe_blockradar == 0) + OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_BLOCK_CHECK); + + if (pe->pe_relstep != HAL_PHYERR_PARAM_NOVAL) { + val = OS_REG_READ(ah, AR_PHY_RADAR_1); + val &= ~AR_PHY_RADAR_1_RELSTEP_THRESH; + val |= SM(pe->pe_relstep, AR_PHY_RADAR_1_RELSTEP_THRESH); + OS_REG_WRITE(ah, AR_PHY_RADAR_1, val); + } + if (pe->pe_relpwr != HAL_PHYERR_PARAM_NOVAL) { + val = OS_REG_READ(ah, AR_PHY_RADAR_1); + val &= ~AR_PHY_RADAR_1_RELPWR_THRESH; + val |= SM(pe->pe_relpwr, AR_PHY_RADAR_1_RELPWR_THRESH); + OS_REG_WRITE(ah, AR_PHY_RADAR_1, val); + } + + if (pe->pe_en_relstep_check == 1) + OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, + AR_PHY_RADAR_1_RELSTEP_CHECK); + else if (pe->pe_en_relstep_check == 0) + OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, + AR_PHY_RADAR_1_RELSTEP_CHECK); + + if (pe->pe_enrelpwr == 1) + OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, + AR_PHY_RADAR_1_RELPWR_ENA); + else if (pe->pe_enrelpwr == 0) + OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, + AR_PHY_RADAR_1_RELPWR_ENA); + + if (pe->pe_maxlen != HAL_PHYERR_PARAM_NOVAL) { + val = OS_REG_READ(ah, AR_PHY_RADAR_1); + val &= ~AR_PHY_RADAR_1_MAXLEN; + val |= SM(pe->pe_maxlen, AR_PHY_RADAR_1_MAXLEN); + OS_REG_WRITE(ah, AR_PHY_RADAR_1, val); + } + + /* + * Enable HT/40 if the upper layer asks; + * it should check the channel is HT/40 and HAL_CAP_EXT_CHAN_DFS + * is available. + */ + if (pe->pe_extchannel == 1) + OS_REG_SET_BIT(ah, AR_PHY_RADAR_EXT, AR_PHY_RADAR_EXT_ENA); + else if (pe->pe_extchannel == 0) + OS_REG_CLR_BIT(ah, AR_PHY_RADAR_EXT, AR_PHY_RADAR_EXT_ENA); +} + +/* + * Extract the radar event information from the given phy error. + * + * Returns AH_TRUE if the phy error was actually a phy error, + * AH_FALSE if the phy error wasn't a phy error. + */ + +/* Flags for pulse_bw_info */ +#define PRI_CH_RADAR_FOUND 0x01 +#define EXT_CH_RADAR_FOUND 0x02 +#define EXT_CH_RADAR_EARLY_FOUND 0x04 + +HAL_BOOL +ar5416ProcessRadarEvent(struct ath_hal *ah, struct ath_rx_status *rxs, + uint64_t fulltsf, const char *buf, HAL_DFS_EVENT *event) +{ + HAL_BOOL doDfsExtCh; + HAL_BOOL doDfsEnhanced; + HAL_BOOL doDfsCombinedRssi; + + uint8_t rssi = 0, ext_rssi = 0; + uint8_t pulse_bw_info = 0, pulse_length_ext = 0, pulse_length_pri = 0; + uint32_t dur = 0; + int pri_found = 1, ext_found = 0; + int early_ext = 0; + int is_dc = 0; + uint16_t datalen; /* length from the RX status field */ + + /* Check whether the given phy error is a radar event */ + if ((rxs->rs_phyerr != HAL_PHYERR_RADAR) && + (rxs->rs_phyerr != HAL_PHYERR_FALSE_RADAR_EXT)) { + return AH_FALSE; + } + + /* Grab copies of the capabilities; just to make the code clearer */ + doDfsExtCh = AH_PRIVATE(ah)->ah_caps.halExtChanDfsSupport; + doDfsEnhanced = AH_PRIVATE(ah)->ah_caps.halEnhancedDfsSupport; + doDfsCombinedRssi = AH_PRIVATE(ah)->ah_caps.halUseCombinedRadarRssi; + + datalen = rxs->rs_datalen; + + /* If hardware supports it, use combined RSSI, else use chain 0 RSSI */ + if (doDfsCombinedRssi) + rssi = (uint8_t) rxs->rs_rssi; + else + rssi = (uint8_t) rxs->rs_rssi_ctl[0]; + + /* Set this; but only use it if doDfsExtCh is set */ + ext_rssi = (uint8_t) rxs->rs_rssi_ext[0]; + + /* Cap it at 0 if the RSSI is a negative number */ + if (rssi & 0x80) + rssi = 0; + + if (ext_rssi & 0x80) + ext_rssi = 0; + + /* + * Fetch the relevant data from the frame + */ + if (doDfsExtCh) { + if (datalen < 3) + return AH_FALSE; + + /* Last three bytes of the frame are of interest */ + pulse_length_pri = *(buf + datalen - 3); + pulse_length_ext = *(buf + datalen - 2); + pulse_bw_info = *(buf + datalen - 1); + HALDEBUG(ah, HAL_DEBUG_DFS, "%s: rssi=%d, ext_rssi=%d, pulse_length_pri=%d," + " pulse_length_ext=%d, pulse_bw_info=%x\n", + __func__, rssi, ext_rssi, pulse_length_pri, pulse_length_ext, + pulse_bw_info); + } else { + /* The pulse width is byte 0 of the data */ + if (datalen >= 1) + dur = ((uint8_t) buf[0]) & 0xff; + else + dur = 0; + + if (dur == 0 && rssi == 0) { + HALDEBUG(ah, HAL_DEBUG_DFS, "%s: dur and rssi are 0\n", __func__); + return AH_FALSE; + } + + HALDEBUG(ah, HAL_DEBUG_DFS, "%s: rssi=%d, dur=%d\n", __func__, rssi, dur); + + /* Single-channel only */ + pri_found = 1; + ext_found = 0; + } + + /* + * If doing extended channel data, pulse_bw_info must + * have one of the flags set. + */ + if (doDfsExtCh && pulse_bw_info == 0x0) + return AH_FALSE; + + /* + * If the extended channel data is available, calculate + * which to pay attention to. + */ + if (doDfsExtCh) { + /* If pulse is on DC, take the larger duration of the two */ + if ((pulse_bw_info & EXT_CH_RADAR_FOUND) && + (pulse_bw_info & PRI_CH_RADAR_FOUND)) { + is_dc = 1; + if (pulse_length_ext > pulse_length_pri) { + dur = pulse_length_ext; + pri_found = 0; + ext_found = 1; + } else { + dur = pulse_length_pri; + pri_found = 1; + ext_found = 0; + } + } else if (pulse_bw_info & EXT_CH_RADAR_EARLY_FOUND) { + dur = pulse_length_ext; + pri_found = 0; + ext_found = 1; + early_ext = 1; + } else if (pulse_bw_info & PRI_CH_RADAR_FOUND) { + dur = pulse_length_pri; + pri_found = 1; + ext_found = 0; + } else if (pulse_bw_info & EXT_CH_RADAR_FOUND) { + dur = pulse_length_ext; + pri_found = 0; + ext_found = 1; + } + + } + + /* + * For enhanced DFS (Merlin and later), pulse_bw_info has + * implications for selecting the correct RSSI value. + */ + if (doDfsEnhanced) { + switch (pulse_bw_info & 0x03) { + case 0: + /* No radar? */ + rssi = 0; + break; + case PRI_CH_RADAR_FOUND: + /* Radar in primary channel */ + /* Cannot use ctrl channel RSSI if ext channel is stronger */ + if (ext_rssi >= (rssi + 3)) { + rssi = 0; + } + break; + case EXT_CH_RADAR_FOUND: + /* Radar in extended channel */ + /* Cannot use ext channel RSSI if ctrl channel is stronger */ + if (rssi >= (ext_rssi + 12)) { + rssi = 0; + } else { + rssi = ext_rssi; + } + break; + case (PRI_CH_RADAR_FOUND | EXT_CH_RADAR_FOUND): + /* When both are present, use stronger one */ + if (rssi < ext_rssi) + rssi = ext_rssi; + break; + } + } + + /* + * If not doing enhanced DFS, choose the ext channel if + * it is stronger than the main channel + */ + if (doDfsExtCh && !doDfsEnhanced) { + if ((ext_rssi > rssi) && (ext_rssi < 128)) + rssi = ext_rssi; + } + + /* + * XXX what happens if the above code decides the RSSI + * XXX wasn't valid, an sets it to 0? + */ + + /* + * Fill out dfs_event structure. + */ + event->re_full_ts = fulltsf; + event->re_ts = rxs->rs_tstamp; + event->re_rssi = rssi; + event->re_dur = dur; + + event->re_flags = 0; + if (pri_found) + event->re_flags |= HAL_DFS_EVENT_PRICH; + if (ext_found) + event->re_flags |= HAL_DFS_EVENT_EXTCH; + if (early_ext) + event->re_flags |= HAL_DFS_EVENT_EXTEARLY; + if (is_dc) + event->re_flags |= HAL_DFS_EVENT_ISDC; + + return AH_TRUE; +} + +/* + * Return whether fast-clock is currently enabled for this + * channel. + */ +HAL_BOOL +ar5416IsFastClockEnabled(struct ath_hal *ah) +{ + struct ath_hal_private *ahp = AH_PRIVATE(ah); + + return IS_5GHZ_FAST_CLOCK_EN(ah, ahp->ah_curchan); +} diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_recv.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_recv.c index 289da427eb..3810c24a19 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_recv.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_recv.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5416/ar5416_recv.c 250346 2013-05-08 01:11:25Z adrian $ */ #include "opt_ah.h" @@ -66,6 +66,40 @@ ar5416SetRxFilter(struct ath_hal *ah, u_int32_t bits) } } +/* + * Stop Receive at the DMA engine + */ +HAL_BOOL +ar5416StopDmaReceive(struct ath_hal *ah) +{ + HAL_BOOL status; + + OS_MARK(ah, AH_MARK_RX_CTL, AH_MARK_RX_CTL_DMA_STOP); + OS_REG_WRITE(ah, AR_CR, AR_CR_RXD); /* Set receive disable bit */ + if (!ath_hal_wait(ah, AR_CR, AR_CR_RXE, 0)) { + OS_MARK(ah, AH_MARK_RX_CTL, AH_MARK_RX_CTL_DMA_STOP_ERR); +#ifdef AH_DEBUG + ath_hal_printf(ah, "%s: dma failed to stop in 10ms\n" + "AR_CR=0x%08x\nAR_DIAG_SW=0x%08x\n", + __func__, + OS_REG_READ(ah, AR_CR), + OS_REG_READ(ah, AR_DIAG_SW)); +#endif + status = AH_FALSE; + } else { + status = AH_TRUE; + } + + /* + * XXX Is this to flush whatever is in a FIFO somewhere? + * XXX If so, what should the correct behaviour should be? + */ + if (AR_SREV_9100(ah)) + OS_DELAY(3000); + + return (status); +} + /* * Start receive at the PCU engine */ @@ -107,7 +141,6 @@ ar5416SetupRxDesc(struct ath_hal *ah, struct ath_desc *ds, uint32_t size, u_int flags) { struct ar5416_desc *ads = AR5416DESC(ds); - HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps; HALASSERT((size &~ AR_BufLen) == 0); @@ -119,8 +152,7 @@ ar5416SetupRxDesc(struct ath_hal *ah, struct ath_desc *ds, ads->ds_rxstatus8 &= ~AR_RxDone; /* clear the rest of the status fields */ - if (! pCap->halAutoSleepSupport) - OS_MEMZERO(&(ads->u), sizeof(ads->u)); + OS_MEMZERO(&(ads->u), sizeof(ads->u)); return AH_TRUE; } @@ -177,6 +209,14 @@ ar5416ProcRxDesc(struct ath_hal *ah, struct ath_desc *ds, if (ads->ds_rxstatus3 & AR_2040) rs->rs_flags |= HAL_RX_2040; + /* + * Only the AR9280 and later chips support STBC RX, so + * ensure we only set this bit for those chips. + */ + if (AR_SREV_MERLIN_10_OR_LATER(ah) + && ads->ds_rxstatus3 & AR_STBCFrame) + rs->rs_flags |= HAL_RX_STBC; + if (ads->ds_rxstatus8 & AR_PreDelimCRCErr) rs->rs_flags |= HAL_RX_DELIM_CRC_PRE; if (ads->ds_rxstatus8 & AR_PostDelimCRCErr) @@ -196,15 +236,40 @@ ar5416ProcRxDesc(struct ath_hal *ah, struct ath_desc *ds, * Consequently we filter them out here so we don't * confuse and/or complicate drivers. */ - if (ads->ds_rxstatus8 & AR_CRCErr) - rs->rs_status |= HAL_RXERR_CRC; - else if (ads->ds_rxstatus8 & AR_PHYErr) { + + /* + * The AR5416 sometimes sets both AR_CRCErr and AR_PHYErr + * when reporting radar pulses. In this instance + * set HAL_RXERR_PHY as well as HAL_RXERR_CRC and + * let the driver layer figure out what to do. + * + * See PR kern/169362. + */ + if (ads->ds_rxstatus8 & AR_PHYErr) { u_int phyerr; - rs->rs_status |= HAL_RXERR_PHY; + /* + * Packets with OFDM_RESTART on post delimiter are CRC OK and + * usable and MAC ACKs them. + * To avoid packet from being lost, we remove the PHY Err flag + * so that driver layer does not drop them. + */ phyerr = MS(ads->ds_rxstatus8, AR_PHYErrCode); - rs->rs_phyerr = phyerr; - } else if (ads->ds_rxstatus8 & AR_DecryptCRCErr) + + if ((phyerr == HAL_PHYERR_OFDM_RESTART) && + (ads->ds_rxstatus8 & AR_PostDelimCRCErr)) { + ath_hal_printf(ah, + "%s: OFDM_RESTART on post-delim CRC error\n", + __func__); + rs->rs_phyerr = 0; + } else { + rs->rs_status |= HAL_RXERR_PHY; + rs->rs_phyerr = phyerr; + } + } + if (ads->ds_rxstatus8 & AR_CRCErr) + rs->rs_status |= HAL_RXERR_CRC; + else if (ads->ds_rxstatus8 & AR_DecryptCRCErr) rs->rs_status |= HAL_RXERR_DECRYPT; else if (ads->ds_rxstatus8 & AR_MichaelErr) rs->rs_status |= HAL_RXERR_MIC; diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_reset.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_reset.c index 112d966954..007787fc9a 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_reset.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_reset.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c 290612 2015-11-09 15:59:42Z adrian $ */ #include "opt_ah.h" @@ -44,7 +44,6 @@ static void ar5416InitBB(struct ath_hal *ah, const struct ieee80211_channel *); static void ar5416InitIMR(struct ath_hal *ah, HAL_OPMODE opmode); static void ar5416InitQoS(struct ath_hal *ah); static void ar5416InitUserSettings(struct ath_hal *ah); -static void ar5416UpdateChainMasks(struct ath_hal *ah, HAL_BOOL is_ht); static void ar5416OverrideIni(struct ath_hal *ah, const struct ieee80211_channel *); #if 0 @@ -62,6 +61,8 @@ static HAL_BOOL ar5416SetPowerPerRateTable(struct ath_hal *ah, uint16_t powerLimit); static void ar5416Set11nRegs(struct ath_hal *ah, const struct ieee80211_channel *chan); static void ar5416MarkPhyInactive(struct ath_hal *ah); +static void ar5416SetIFSTiming(struct ath_hal *ah, + const struct ieee80211_channel *chan); /* * Places the device in and out of reset and then places sane @@ -74,7 +75,9 @@ static void ar5416MarkPhyInactive(struct ath_hal *ah); HAL_BOOL ar5416Reset(struct ath_hal *ah, HAL_OPMODE opmode, struct ieee80211_channel *chan, - HAL_BOOL bChannelChange, HAL_STATUS *status) + HAL_BOOL bChannelChange, + HAL_RESET_TYPE resetType, + HAL_STATUS *status) { #define N(a) (sizeof (a) / sizeof (a[0])) #define FAIL(_code) do { ecode = _code; goto bad; } while (0) @@ -118,7 +121,11 @@ ar5416Reset(struct ath_hal *ah, HAL_OPMODE opmode, } HALASSERT(AH_PRIVATE(ah)->ah_eeversion >= AR_EEPROM_VER14_1); + /* Blank the channel survey statistics */ + ath_hal_survey_clear(ah); + /* XXX Turn on fast channel change for 5416 */ + /* * Preserve the bmiss rssi threshold and count threshold * across resets @@ -132,7 +139,20 @@ ar5416Reset(struct ath_hal *ah, HAL_OPMODE opmode, * Preserve the antenna on a channel change */ saveDefAntenna = OS_REG_READ(ah, AR_DEF_ANTENNA); - if (saveDefAntenna == 0) /* XXX magic constants */ + + /* + * Don't do this for the AR9285 - it breaks RX for single + * antenna designs when diversity is disabled. + * + * I'm not sure what this was working around; it may be + * something to do with the AR5416. Certainly this register + * isn't supposed to be used by the MIMO chips for anything + * except for defining the default antenna when an external + * phase array / smart antenna is connected. + * + * See PR: kern/179269 . + */ + if ((! AR_SREV_KITE(ah)) && saveDefAntenna == 0) /* XXX magic constants */ saveDefAntenna = 1; /* Save hardware flag before chip reset clears the register */ @@ -146,7 +166,9 @@ ar5416Reset(struct ath_hal *ah, HAL_OPMODE opmode, /* For chips on which the RTC reset is done, save TSF before it gets cleared */ if (AR_SREV_HOWL(ah) || - (AR_SREV_MERLIN(ah) && ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL))) + (AR_SREV_MERLIN(ah) && + ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) || + (ah->ah_config.ah_force_full_reset)) tsf = ar5416GetTsf64(ah); /* Mark PHY as inactive; marked active in ar5416InitBB() */ @@ -202,11 +224,6 @@ ar5416Reset(struct ath_hal *ah, HAL_OPMODE opmode, HALDEBUG(ah, HAL_DEBUG_RESET, ">>>2 %s: AR_PHY_ADC_CTL=0x%x\n", __func__, OS_REG_READ(ah,AR_PHY_ADC_CTL)); - /* - * Setup ah_tx_chainmask / ah_rx_chainmask before we fiddle - * with enabling the TX/RX radio chains. - */ - ar5416UpdateChainMasks(ah, IEEE80211_IS_CHAN_HT(chan)); /* * This routine swaps the analog chains - it should be done * before any radio register twiddling is done. @@ -275,12 +292,19 @@ ar5416Reset(struct ath_hal *ah, HAL_OPMODE opmode, ar5416StartTsf2(ah); #endif + /* + * Enable Bluetooth Coexistence if it's enabled. + */ + if (AH5416(ah)->ah_btCoexConfigType != HAL_BT_COEX_CFG_NONE) + ar5416InitBTCoex(ah); + /* Restore previous antenna */ OS_REG_WRITE(ah, AR_DEF_ANTENNA, saveDefAntenna); - /* then our BSSID */ + /* then our BSSID and associate id */ OS_REG_WRITE(ah, AR_BSS_ID0, LE_READ_4(ahp->ah_bssid)); - OS_REG_WRITE(ah, AR_BSS_ID1, LE_READ_2(ahp->ah_bssid + 4)); + OS_REG_WRITE(ah, AR_BSS_ID1, LE_READ_2(ahp->ah_bssid + 4) | + (ahp->ah_assocId & 0x3fff) << AR_BSS_ID1_AID_S); /* Restore bmiss rssi & count thresholds */ OS_REG_WRITE(ah, AR_RSSI_THR, ahp->ah_rssiThr); @@ -304,11 +328,26 @@ ar5416Reset(struct ath_hal *ah, HAL_OPMODE opmode, ah->ah_resetTxQueue(ah, i); ar5416InitIMR(ah, opmode); - ar5212SetCoverageClass(ah, AH_PRIVATE(ah)->ah_coverageClass, 1); + ar5416SetCoverageClass(ah, AH_PRIVATE(ah)->ah_coverageClass, 1); ar5416InitQoS(ah); /* This may override the AR_DIAG_SW register */ ar5416InitUserSettings(ah); + /* XXX this won't work for AR9287! */ + if (IEEE80211_IS_CHAN_HALF(chan) || IEEE80211_IS_CHAN_QUARTER(chan)) { + ar5416SetIFSTiming(ah, chan); +#if 0 + /* + * AR5413? + * Force window_length for 1/2 and 1/4 rate channels, + * the ini file sets this to zero otherwise. + */ + OS_REG_RMW_FIELD(ah, AR_PHY_FRAME_CTL, + AR_PHY_FRAME_CTL_WINLEN, 3); + } +#endif + } + if (AR_SREV_KIWI_13_OR_LATER(ah)) { /* * Enable ASYNC FIFO @@ -357,13 +396,33 @@ ar5416Reset(struct ath_hal *ah, HAL_OPMODE opmode, */ OS_REG_WRITE(ah, AR_OBS, 8); -#ifdef AH_AR5416_INTERRUPT_MITIGATION + /* + * Disable the "general" TX/RX mitigation timers. + */ OS_REG_WRITE(ah, AR_MIRT, 0); - OS_REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_LAST, 500); - OS_REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_FIRST, 2000); - OS_REG_RMW_FIELD(ah, AR_TIMT, AR_TIMT_LAST, 300); - OS_REG_RMW_FIELD(ah, AR_TIMT, AR_TIMT_FIRST, 750); +#ifdef AH_AR5416_INTERRUPT_MITIGATION + /* + * This initialises the RX interrupt mitigation timers. + * + * The mitigation timers begin at idle and are triggered + * upon the RXOK of a single frame (or sub-frame, for A-MPDU.) + * Then, the RX mitigation interrupt will fire: + * + * + 250uS after the last RX'ed frame, or + * + 700uS after the first RX'ed frame + * + * Thus, the LAST field dictates the extra latency + * induced by the RX mitigation method and the FIRST + * field dictates how long to delay before firing an + * RX mitigation interrupt. + * + * Please note this only seems to be for RXOK frames; + * not CRC or PHY error frames. + * + */ + OS_REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_LAST, 250); + OS_REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_FIRST, 700); #endif ar5416InitBB(ah, chan); @@ -527,6 +586,10 @@ ar5416InitDMA(struct ath_hal *ah) /* * let mac dma writes be in 128 byte chunks */ + /* + * XXX If you change this, you must change the headroom + * assigned in ah_maxTxTrigLev - see ar5416InitState(). + */ OS_REG_WRITE(ah, AR_RXCFG, (OS_REG_READ(ah, AR_RXCFG) & ~AR_RXCFG_DMASZ_MASK) | AR_RXCFG_DMASZ_128B); @@ -610,11 +673,11 @@ ar5416InitIMR(struct ath_hal *ah, HAL_OPMODE opmode) | AR_IMR_BCNMISC; #ifdef AH_AR5416_INTERRUPT_MITIGATION - ahp->ah_maskReg |= AR_IMR_TXINTM | AR_IMR_RXINTM - | AR_IMR_TXMINTR | AR_IMR_RXMINTR; + ahp->ah_maskReg |= AR_IMR_RXINTM | AR_IMR_RXMINTR; #else - ahp->ah_maskReg |= AR_IMR_TXOK | AR_IMR_RXOK; + ahp->ah_maskReg |= AR_IMR_RXOK; #endif + ahp->ah_maskReg |= AR_IMR_TXOK; if (opmode == HAL_M_HOSTAP) ahp->ah_maskReg |= AR_IMR_MIB; @@ -666,7 +729,8 @@ ar5416InitUserSettings(struct ath_hal *ah) /* Restore user-specified settings */ if (ahp->ah_miscMode != 0) - OS_REG_WRITE(ah, AR_MISC_MODE, OS_REG_READ(ah, AR_MISC_MODE) | ahp->ah_miscMode); + OS_REG_WRITE(ah, AR_MISC_MODE, OS_REG_READ(ah, AR_MISC_MODE) + | ahp->ah_miscMode); if (ahp->ah_sifstime != (u_int) -1) ar5212SetSifsTime(ah, ahp->ah_sifstime); if (ahp->ah_slottime != (u_int) -1) @@ -701,6 +765,7 @@ ar5416SetRfMode(struct ath_hal *ah, const struct ieee80211_channel *chan) rfMode |= IEEE80211_IS_CHAN_5GHZ(chan) ? AR_PHY_MODE_RF5GHZ : AR_PHY_MODE_RF2GHZ; } + OS_REG_WRITE(ah, AR_PHY_MODE, rfMode); } @@ -712,12 +777,15 @@ ar5416ChipReset(struct ath_hal *ah, const struct ieee80211_channel *chan) { OS_MARK(ah, AH_MARK_CHIPRESET, chan ? chan->ic_freq : 0); /* - * Warm reset is optimistic. + * Warm reset is optimistic for open-loop TX power control. */ if (AR_SREV_MERLIN(ah) && ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) { if (!ar5416SetResetReg(ah, HAL_RESET_POWER_ON)) return AH_FALSE; + } else if (ah->ah_config.ah_force_full_reset) { + if (!ar5416SetResetReg(ah, HAL_RESET_POWER_ON)) + return AH_FALSE; } else { if (!ar5416SetResetReg(ah, HAL_RESET_WARM)) return AH_FALSE; @@ -959,6 +1027,14 @@ ar5416WriteTxPowerRateRegisters(struct ath_hal *ah, | POW_SM(ratesArray[rateDupCck], 0) ); } + + /* + * Set max power to 30 dBm and, optionally, + * enable TPC in tx descriptors. + */ + OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE_MAX, MAX_RATE_POWER | + (AH5212(ah)->ah_tpcEnabled ? AR_PHY_POWER_TX_RATE_MAX_TPC_ENABLE : 0)); +#undef POW_SM } @@ -973,12 +1049,11 @@ ar5416SetTransmitPower(struct ath_hal *ah, const struct ieee80211_channel *chan, uint16_t *rfXpdGain) { #define N(a) (sizeof (a) / sizeof (a[0])) +#define POW_SM(_r, _s) (((_r) & 0x3f) << (_s)) MODAL_EEP_HEADER *pModal; struct ath_hal_5212 *ahp = AH5212(ah); - int16_t ratesArray[Ar5416RateSize]; int16_t txPowerIndexOffset = 0; - uint8_t ht40PowerIncForPdadc = 2; int i; uint16_t cfgCtl; @@ -991,8 +1066,13 @@ ar5416SetTransmitPower(struct ath_hal *ah, HALASSERT(AH_PRIVATE(ah)->ah_eeversion >= AR_EEPROM_VER14_1); + /* + * Default to 2, is overridden based on the EEPROM version / value. + */ + AH5416(ah)->ah_ht40PowerIncForPdadc = 2; + /* Setup info for the actual eeprom */ - OS_MEMZERO(ratesArray, sizeof(ratesArray)); + OS_MEMZERO(AH5416(ah)->ah_ratesArray, sizeof(AH5416(ah)->ah_ratesArray)); cfgCtl = ath_hal_getctl(ah, chan); powerLimit = chan->ic_maxregpower * 2; twiceAntennaReduction = chan->ic_maxantgain; @@ -1002,11 +1082,12 @@ ar5416SetTransmitPower(struct ath_hal *ah, __func__,chan->ic_freq, cfgCtl ); if (IS_EEP_MINOR_V2(ah)) { - ht40PowerIncForPdadc = pModal->ht40PowerIncForPdadc; + AH5416(ah)->ah_ht40PowerIncForPdadc = pModal->ht40PowerIncForPdadc; } if (!ar5416SetPowerPerRateTable(ah, pEepData, chan, - &ratesArray[0],cfgCtl, + &AH5416(ah)->ah_ratesArray[0], + cfgCtl, twiceAntennaReduction, twiceMaxRegulatoryPower, powerLimit)) { HALDEBUG(ah, HAL_DEBUG_ANY, @@ -1020,14 +1101,15 @@ ar5416SetTransmitPower(struct ath_hal *ah, return AH_FALSE; } - maxPower = AH_MAX(ratesArray[rate6mb], ratesArray[rateHt20_0]); + maxPower = AH_MAX(AH5416(ah)->ah_ratesArray[rate6mb], + AH5416(ah)->ah_ratesArray[rateHt20_0]); if (IEEE80211_IS_CHAN_2GHZ(chan)) { - maxPower = AH_MAX(maxPower, ratesArray[rate1l]); + maxPower = AH_MAX(maxPower, AH5416(ah)->ah_ratesArray[rate1l]); } if (IEEE80211_IS_CHAN_HT40(chan)) { - maxPower = AH_MAX(maxPower, ratesArray[rateHt40_0]); + maxPower = AH_MAX(maxPower, AH5416(ah)->ah_ratesArray[rateHt40_0]); } ahp->ah_tx6PowerInHalfDbm = maxPower; @@ -1038,10 +1120,11 @@ ar5416SetTransmitPower(struct ath_hal *ah, * txPowerIndexOffset is set by the SetPowerTable() call - * adjust the rate table (0 offset if rates EEPROM not loaded) */ - for (i = 0; i < N(ratesArray); i++) { - ratesArray[i] = (int16_t)(txPowerIndexOffset + ratesArray[i]); - if (ratesArray[i] > AR5416_MAX_RATE_POWER) - ratesArray[i] = AR5416_MAX_RATE_POWER; + for (i = 0; i < N(AH5416(ah)->ah_ratesArray); i++) { + AH5416(ah)->ah_ratesArray[i] = + (int16_t)(txPowerIndexOffset + AH5416(ah)->ah_ratesArray[i]); + if (AH5416(ah)->ah_ratesArray[i] > AR5416_MAX_RATE_POWER) + AH5416(ah)->ah_ratesArray[i] = AR5416_MAX_RATE_POWER; } #ifdef AH_EEPROM_DUMP @@ -1052,7 +1135,7 @@ ar5416SetTransmitPower(struct ath_hal *ah, * this debugging; the values won't necessarily be what's being * programmed into the hardware. */ - ar5416PrintPowerPerRate(ah, ratesArray); + ar5416PrintPowerPerRate(ah, AH5416(ah)->ah_ratesArray); #endif /* @@ -1068,16 +1151,16 @@ ar5416SetTransmitPower(struct ath_hal *ah, &pwr_table_offset); /* Underflow power gets clamped at raw value 0 */ /* Overflow power gets camped at AR5416_MAX_RATE_POWER */ - for (i = 0; i < N(ratesArray); i++) { + for (i = 0; i < N(AH5416(ah)->ah_ratesArray); i++) { /* * + pwr_table_offset is in dBm * + ratesArray is in 1/2 dBm */ - ratesArray[i] -= (pwr_table_offset * 2); - if (ratesArray[i] < 0) - ratesArray[i] = 0; - else if (ratesArray[i] > AR5416_MAX_RATE_POWER) - ratesArray[i] = AR5416_MAX_RATE_POWER; + AH5416(ah)->ah_ratesArray[i] -= (pwr_table_offset * 2); + if (AH5416(ah)->ah_ratesArray[i] < 0) + AH5416(ah)->ah_ratesArray[i] = 0; + else if (AH5416(ah)->ah_ratesArray[i] > AR5416_MAX_RATE_POWER) + AH5416(ah)->ah_ratesArray[i] = AR5416_MAX_RATE_POWER; } } @@ -1104,9 +1187,9 @@ ar5416SetTransmitPower(struct ath_hal *ah, int cck_ofdm_delta = 2; int i; for (i = 0; i < N(adj); i++) { - ratesArray[adj[i]] -= cck_ofdm_delta; - if (ratesArray[adj[i]] < 0) - ratesArray[adj[i]] = 0; + AH5416(ah)->ah_ratesArray[adj[i]] -= cck_ofdm_delta; + if (AH5416(ah)->ah_ratesArray[adj[i]] < 0) + AH5416(ah)->ah_ratesArray[adj[i]] = 0; } } @@ -1118,18 +1201,20 @@ ar5416SetTransmitPower(struct ath_hal *ah, * XXX handle overflow/too high power level? */ if (IEEE80211_IS_CHAN_HT40(chan)) { - ratesArray[rateHt40_0] += ht40PowerIncForPdadc; - ratesArray[rateHt40_1] += ht40PowerIncForPdadc; - ratesArray[rateHt40_2] += ht40PowerIncForPdadc; - ratesArray[rateHt40_3] += ht40PowerIncForPdadc; - ratesArray[rateHt40_4] += ht40PowerIncForPdadc; - ratesArray[rateHt40_5] += ht40PowerIncForPdadc; - ratesArray[rateHt40_6] += ht40PowerIncForPdadc; - ratesArray[rateHt40_7] += ht40PowerIncForPdadc; + AH5416(ah)->ah_ratesArray[rateHt40_0] += + AH5416(ah)->ah_ht40PowerIncForPdadc; + AH5416(ah)->ah_ratesArray[rateHt40_1] += + AH5416(ah)->ah_ht40PowerIncForPdadc; + AH5416(ah)->ah_ratesArray[rateHt40_2] += AH5416(ah)->ah_ht40PowerIncForPdadc; + AH5416(ah)->ah_ratesArray[rateHt40_3] += AH5416(ah)->ah_ht40PowerIncForPdadc; + AH5416(ah)->ah_ratesArray[rateHt40_4] += AH5416(ah)->ah_ht40PowerIncForPdadc; + AH5416(ah)->ah_ratesArray[rateHt40_5] += AH5416(ah)->ah_ht40PowerIncForPdadc; + AH5416(ah)->ah_ratesArray[rateHt40_6] += AH5416(ah)->ah_ht40PowerIncForPdadc; + AH5416(ah)->ah_ratesArray[rateHt40_7] += AH5416(ah)->ah_ht40PowerIncForPdadc; } /* Write the TX power rate registers */ - ar5416WriteTxPowerRateRegisters(ah, chan, ratesArray); + ar5416WriteTxPowerRateRegisters(ah, chan, AH5416(ah)->ah_ratesArray); /* Write the Power subtraction for dynamic chain changing, for per-packet powertx */ OS_REG_WRITE(ah, AR_PHY_POWER_TX_SUB, @@ -1148,7 +1233,8 @@ ar5416SetTransmitPower(struct ath_hal *ah, HAL_RFGAIN ar5416GetRfgain(struct ath_hal *ah) { - return HAL_RFGAIN_INACTIVE; + + return (HAL_RFGAIN_INACTIVE); } /* @@ -1157,13 +1243,14 @@ ar5416GetRfgain(struct ath_hal *ah) HAL_BOOL ar5416Disable(struct ath_hal *ah) { - if (!ar5212SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) + + if (!ar5416SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) return AH_FALSE; if (! ar5416SetResetReg(ah, HAL_RESET_COLD)) return AH_FALSE; AH5416(ah)->ah_initPLL(ah, AH_NULL); - return AH_TRUE; + return (AH_TRUE); } /* @@ -1175,11 +1262,12 @@ ar5416Disable(struct ath_hal *ah) HAL_BOOL ar5416PhyDisable(struct ath_hal *ah) { + if (! ar5416SetResetReg(ah, HAL_RESET_WARM)) return AH_FALSE; AH5416(ah)->ah_initPLL(ah, AH_NULL); - return AH_TRUE; + return (AH_TRUE); } /* @@ -1188,6 +1276,12 @@ ar5416PhyDisable(struct ath_hal *ah) HAL_BOOL ar5416SetResetReg(struct ath_hal *ah, uint32_t type) { + /* + * Set force wake + */ + OS_REG_WRITE(ah, AR_RTC_FORCE_WAKE, + AR_RTC_FORCE_WAKE_EN | AR_RTC_FORCE_WAKE_ON_INT); + switch (type) { case HAL_RESET_POWER_ON: return ar5416SetResetPowerOn(ah); @@ -1218,10 +1312,15 @@ ar5416SetResetPowerOn(struct ath_hal *ah) AR_RTC_FORCE_WAKE_EN | AR_RTC_FORCE_WAKE_ON_INT); /* - * RTC reset and clear + * PowerOn reset can be used in open loop power control or failure recovery. + * If we do RTC reset while DMA is still running, hardware may corrupt memory. + * Therefore, we need to reset AHB first to stop DMA. */ if (! AR_SREV_HOWL(ah)) OS_REG_WRITE(ah, AR_RC, AR_RC_AHB); + /* + * RTC reset and clear + */ OS_REG_WRITE(ah, AR_RTC_RESET, 0); OS_DELAY(20); @@ -1272,6 +1371,11 @@ ar5416SetReset(struct ath_hal *ah, int type) #endif /* AH_SUPPORT_AR9130 */ /* * Reset AHB + * + * (In case the last interrupt source was a bus timeout.) + * XXX TODO: this is not the way to do it! It should be recorded + * XXX by the interrupt handler and passed _into_ the + * XXX reset path routine so this occurs. */ tmpReg = OS_REG_READ(ah, AR_INTR_SYNC_CAUSE); if (tmpReg & (AR_INTR_SYNC_LOCAL_TIMEOUT|AR_INTR_SYNC_RADM_CPL_TIMEOUT)) { @@ -1327,16 +1431,15 @@ ar5416SetReset(struct ath_hal *ah, int type) if (type == HAL_RESET_COLD) { if (isBigEndian()) { /* - * Set CFG, little-endian for register - * and descriptor accesses. + * Set CFG, little-endian for descriptor accesses. */ - mask = INIT_CONFIG_STATUS | AR_CFG_SWRD | AR_CFG_SWRG; + mask = INIT_CONFIG_STATUS | AR_CFG_SWRD; #ifndef AH_NEED_DESC_SWAP mask |= AR_CFG_SWTD; #endif HALDEBUG(ah, HAL_DEBUG_RESET, "%s Applying descriptor swap\n", __func__); - OS_REG_WRITE(ah, AR_CFG, LE_READ_4(&mask)); + OS_REG_WRITE(ah, AR_CFG, mask); } else OS_REG_WRITE(ah, AR_CFG, INIT_CONFIG_STATUS); } @@ -1394,31 +1497,6 @@ ar5416RestoreChainMask(struct ath_hal *ah) } } -/* - * Update the chainmask based on the current channel configuration. - * - * XXX ath9k checks bluetooth co-existence here - * XXX ath9k checks whether the current state is "off-channel". - * XXX ath9k sticks the hardware into 1x1 mode for legacy; - * we're going to leave multi-RX on for multi-path cancellation. - */ -static void -ar5416UpdateChainMasks(struct ath_hal *ah, HAL_BOOL is_ht) -{ - struct ath_hal_private *ahpriv = AH_PRIVATE(ah); - HAL_CAPABILITIES *pCap = &ahpriv->ah_caps; - - if (is_ht) { - AH5416(ah)->ah_tx_chainmask = pCap->halTxChainMask; - } else { - AH5416(ah)->ah_tx_chainmask = 1; - } - AH5416(ah)->ah_rx_chainmask = pCap->halRxChainMask; - HALDEBUG(ah, HAL_DEBUG_RESET, "TX chainmask: 0x%x; RX chainmask: 0x%x\n", - AH5416(ah)->ah_tx_chainmask, - AH5416(ah)->ah_rx_chainmask); -} - void ar5416InitPLL(struct ath_hal *ah, const struct ieee80211_channel *chan) { @@ -1453,6 +1531,7 @@ ar5416SetDefGainValues(struct ath_hal *ah, const struct ar5416eeprom *eep, uint8_t txRxAttenLocal, int regChainOffset, int i) { + if (IS_EEP_MINOR_V3(ah)) { txRxAttenLocal = pModal->txRxAttenCh[i]; @@ -1649,7 +1728,7 @@ ar5416SetBoardValues(struct ath_hal *ah, const struct ieee80211_channel *chan) eep->baseEepHeader.desiredScaleCCK); } - return AH_TRUE; + return (AH_TRUE); } /* @@ -1683,7 +1762,8 @@ ar5416SetRatesArrayFromTargetPower(struct ath_hal *ah, /* Set rates Array from collected data */ ratesArray[rate6mb] = ratesArray[rate9mb] = ratesArray[rate12mb] = - ratesArray[rate18mb] = ratesArray[rate24mb] = targetPowerOfdm->tPow2x[0]; + ratesArray[rate18mb] = ratesArray[rate24mb] = + targetPowerOfdm->tPow2x[0]; ratesArray[rate36mb] = targetPowerOfdm->tPow2x[1]; ratesArray[rate48mb] = targetPowerOfdm->tPow2x[2]; ratesArray[rate54mb] = targetPowerOfdm->tPow2x[3]; @@ -2587,7 +2667,7 @@ ar5416OverrideIni(struct ath_hal *ah, const struct ieee80211_channel *chan) if (!AR_SREV_9271(ah)) val &= ~AR_PCU_MISC_MODE2_HWWAR1; - if (AR_SREV_KIWI_11_OR_LATER(ah)) + if (AR_SREV_KIWI_10_OR_LATER(ah)) val = val & (~AR_PCU_MISC_MODE2_HWWAR2); OS_REG_WRITE(ah, AR_PCU_MISC_MODE2, val); @@ -2687,3 +2767,125 @@ ar5416MarkPhyInactive(struct ath_hal *ah) { OS_REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS); } + +#define AR5416_IFS_SLOT_FULL_RATE_40 0x168 /* 9 us half, 40 MHz core clock (9*40) */ +#define AR5416_IFS_SLOT_HALF_RATE_40 0x104 /* 13 us half, 20 MHz core clock (13*20) */ +#define AR5416_IFS_SLOT_QUARTER_RATE_40 0xD2 /* 21 us quarter, 10 MHz core clock (21*10) */ + +#define AR5416_IFS_EIFS_FULL_RATE_40 0xE60 /* (74 + (2 * 9)) * 40MHz core clock */ +#define AR5416_IFS_EIFS_HALF_RATE_40 0xDAC /* (149 + (2 * 13)) * 20MHz core clock */ +#define AR5416_IFS_EIFS_QUARTER_RATE_40 0xD48 /* (298 + (2 * 21)) * 10MHz core clock */ + +#define AR5416_IFS_SLOT_FULL_RATE_44 0x18c /* 9 us half, 44 MHz core clock (9*44) */ +#define AR5416_IFS_SLOT_HALF_RATE_44 0x11e /* 13 us half, 22 MHz core clock (13*22) */ +#define AR5416_IFS_SLOT_QUARTER_RATE_44 0xe7 /* 21 us quarter, 11 MHz core clock (21*11) */ + +#define AR5416_IFS_EIFS_FULL_RATE_44 0xfd0 /* (74 + (2 * 9)) * 44MHz core clock */ +#define AR5416_IFS_EIFS_HALF_RATE_44 0xf0a /* (149 + (2 * 13)) * 22MHz core clock */ +#define AR5416_IFS_EIFS_QUARTER_RATE_44 0xe9c /* (298 + (2 * 21)) * 11MHz core clock */ + +#define AR5416_INIT_USEC_40 40 +#define AR5416_HALF_RATE_USEC_40 19 /* ((40 / 2) - 1 ) */ +#define AR5416_QUARTER_RATE_USEC_40 9 /* ((40 / 4) - 1 ) */ + +#define AR5416_INIT_USEC_44 44 +#define AR5416_HALF_RATE_USEC_44 21 /* ((44 / 2) - 1 ) */ +#define AR5416_QUARTER_RATE_USEC_44 10 /* ((44 / 4) - 1 ) */ + + +/* XXX What should these be for 40/44MHz clocks (and half/quarter) ? */ +#define AR5416_RX_NON_FULL_RATE_LATENCY 63 +#define AR5416_TX_HALF_RATE_LATENCY 108 +#define AR5416_TX_QUARTER_RATE_LATENCY 216 + +/* + * Adjust various register settings based on half/quarter rate clock setting. + * This includes: + * + * + USEC, TX/RX latency, + * + IFS params: slot, eifs, misc etc. + * + * TODO: + * + * + Verify which other registers need to be tweaked; + * + Verify the behaviour of this for 5GHz fast and non-fast clock mode; + * + This just plain won't work for long distance links - the coverage class + * code isn't aware of the slot/ifs/ACK/RTS timeout values that need to + * change; + * + Verify whether the 32KHz USEC value needs to be kept for the 802.11n + * series chips? + * + Calculate/derive values for 2GHz, 5GHz, 5GHz fast clock + */ +static void +ar5416SetIFSTiming(struct ath_hal *ah, const struct ieee80211_channel *chan) +{ + uint32_t txLat, rxLat, usec, slot, refClock, eifs, init_usec; + int clk_44 = 0; + + HALASSERT(IEEE80211_IS_CHAN_HALF(chan) || + IEEE80211_IS_CHAN_QUARTER(chan)); + + /* 2GHz and 5GHz fast clock - 44MHz; else 40MHz */ + if (IEEE80211_IS_CHAN_2GHZ(chan)) + clk_44 = 1; + else if (IEEE80211_IS_CHAN_5GHZ(chan) && + IS_5GHZ_FAST_CLOCK_EN(ah, chan)) + clk_44 = 1; + + /* XXX does this need save/restoring for the 11n chips? */ + refClock = OS_REG_READ(ah, AR_USEC) & AR_USEC_USEC32; + + /* + * XXX This really should calculate things, not use + * hard coded values! Ew. + */ + if (IEEE80211_IS_CHAN_HALF(chan)) { + if (clk_44) { + slot = AR5416_IFS_SLOT_HALF_RATE_44; + rxLat = AR5416_RX_NON_FULL_RATE_LATENCY << + AR5416_USEC_RX_LAT_S; + txLat = AR5416_TX_HALF_RATE_LATENCY << + AR5416_USEC_TX_LAT_S; + usec = AR5416_HALF_RATE_USEC_44; + eifs = AR5416_IFS_EIFS_HALF_RATE_44; + init_usec = AR5416_INIT_USEC_44 >> 1; + } else { + slot = AR5416_IFS_SLOT_HALF_RATE_40; + rxLat = AR5416_RX_NON_FULL_RATE_LATENCY << + AR5416_USEC_RX_LAT_S; + txLat = AR5416_TX_HALF_RATE_LATENCY << + AR5416_USEC_TX_LAT_S; + usec = AR5416_HALF_RATE_USEC_40; + eifs = AR5416_IFS_EIFS_HALF_RATE_40; + init_usec = AR5416_INIT_USEC_40 >> 1; + } + } else { /* quarter rate */ + if (clk_44) { + slot = AR5416_IFS_SLOT_QUARTER_RATE_44; + rxLat = AR5416_RX_NON_FULL_RATE_LATENCY << + AR5416_USEC_RX_LAT_S; + txLat = AR5416_TX_QUARTER_RATE_LATENCY << + AR5416_USEC_TX_LAT_S; + usec = AR5416_QUARTER_RATE_USEC_44; + eifs = AR5416_IFS_EIFS_QUARTER_RATE_44; + init_usec = AR5416_INIT_USEC_44 >> 2; + } else { + slot = AR5416_IFS_SLOT_QUARTER_RATE_40; + rxLat = AR5416_RX_NON_FULL_RATE_LATENCY << + AR5416_USEC_RX_LAT_S; + txLat = AR5416_TX_QUARTER_RATE_LATENCY << + AR5416_USEC_TX_LAT_S; + usec = AR5416_QUARTER_RATE_USEC_40; + eifs = AR5416_IFS_EIFS_QUARTER_RATE_40; + init_usec = AR5416_INIT_USEC_40 >> 2; + } + } + + /* XXX verify these! */ + OS_REG_WRITE(ah, AR_USEC, (usec | refClock | txLat | rxLat)); + OS_REG_WRITE(ah, AR_D_GBL_IFS_SLOT, slot); + OS_REG_WRITE(ah, AR_D_GBL_IFS_EIFS, eifs); + OS_REG_RMW_FIELD(ah, AR_D_GBL_IFS_MISC, + AR_D_GBL_IFS_MISC_USEC_DURATION, init_usec); +} + diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_spectral.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_spectral.c new file mode 100644 index 0000000000..fc0eb4b7cf --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_spectral.c @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, All Rights Reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5416/ar5416_spectral.c 298939 2016-05-02 19:56:48Z pfg $ + */ +#include "opt_ah.h" + +#include "ah.h" +#include "ah_internal.h" +#include "ah_devid.h" +#include "ah_desc.h" /* NB: for HAL_PHYERR* */ + +#include "ar5416/ar5416.h" +#include "ar5416/ar5416reg.h" +#include "ar5416/ar5416phy.h" + +/* + * Default AR9280 spectral scan parameters + */ +#define AR5416_SPECTRAL_SCAN_ENA 0 +#define AR5416_SPECTRAL_SCAN_ACTIVE 0 +#define AR5416_SPECTRAL_SCAN_FFT_PERIOD 8 +#define AR5416_SPECTRAL_SCAN_PERIOD 1 +#define AR5416_SPECTRAL_SCAN_COUNT 16 //used to be 128 +#define AR5416_SPECTRAL_SCAN_SHORT_REPEAT 1 + +/* constants */ +#define MAX_RADAR_RSSI_THRESH 0x3f +#define MAX_RADAR_HEIGHT 0x3f +#define ENABLE_ALL_PHYERR 0xffffffff + +static void ar5416DisableRadar(struct ath_hal *ah); +static void ar5416PrepSpectralScan(struct ath_hal *ah); + +static void +ar5416DisableRadar(struct ath_hal *ah) +{ + uint32_t val; + + // Enable radar FFT + val = OS_REG_READ(ah, AR_PHY_RADAR_0); + val |= AR_PHY_RADAR_0_FFT_ENA; + + // set radar detect thresholds to max to effectively disable radar + val &= ~AR_PHY_RADAR_0_RRSSI; + val |= SM(MAX_RADAR_RSSI_THRESH, AR_PHY_RADAR_0_RRSSI); + + val &= ~AR_PHY_RADAR_0_HEIGHT; + val |= SM(MAX_RADAR_HEIGHT, AR_PHY_RADAR_0_HEIGHT); + + val &= ~(AR_PHY_RADAR_0_ENA); + OS_REG_WRITE(ah, AR_PHY_RADAR_0, val); + + // disable extension radar detect + val = OS_REG_READ(ah, AR_PHY_RADAR_EXT); + OS_REG_WRITE(ah, AR_PHY_RADAR_EXT, val & ~AR_PHY_RADAR_EXT_ENA); + + val = OS_REG_READ(ah, AR_RX_FILTER); + val |= (1<<13); + OS_REG_WRITE(ah, AR_RX_FILTER, val); +} + +static void +ar5416PrepSpectralScan(struct ath_hal *ah) +{ + + ar5416DisableRadar(ah); + OS_REG_WRITE(ah, AR_PHY_ERR, ENABLE_ALL_PHYERR); +} + +void +ar5416ConfigureSpectralScan(struct ath_hal *ah, HAL_SPECTRAL_PARAM *ss) +{ + uint32_t val; + + ar5416PrepSpectralScan(ah); + + val = OS_REG_READ(ah, AR_PHY_SPECTRAL_SCAN); + + if (ss->ss_fft_period != HAL_SPECTRAL_PARAM_NOVAL) { + val &= ~AR_PHY_SPECTRAL_SCAN_FFT_PERIOD; + val |= SM(ss->ss_fft_period, AR_PHY_SPECTRAL_SCAN_FFT_PERIOD); + } + + if (ss->ss_period != HAL_SPECTRAL_PARAM_NOVAL) { + val &= ~AR_PHY_SPECTRAL_SCAN_PERIOD; + val |= SM(ss->ss_period, AR_PHY_SPECTRAL_SCAN_PERIOD); + } + + if (ss->ss_period != HAL_SPECTRAL_PARAM_NOVAL) { + val &= ~AR_PHY_SPECTRAL_SCAN_PERIOD; + val |= SM(ss->ss_period, AR_PHY_SPECTRAL_SCAN_PERIOD); + } + + /* This section is different for Kiwi and Merlin */ + if (AR_SREV_MERLIN(ah) ) { + if (ss->ss_count != HAL_SPECTRAL_PARAM_NOVAL) { + val &= ~AR_PHY_SPECTRAL_SCAN_COUNT; + val |= SM(ss->ss_count, AR_PHY_SPECTRAL_SCAN_COUNT); + } + + if (ss->ss_short_report == AH_TRUE) { + val |= AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT; + } else if (ss->ss_short_report != HAL_SPECTRAL_PARAM_NOVAL) { + val &= ~AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT; + } + } else { + if (ss->ss_count != HAL_SPECTRAL_PARAM_NOVAL) { + /* + * In Merlin, for continuous scan, scan_count = 128. + * In case of Kiwi, this value should be 0 + */ + if (ss->ss_count == 128) + ss->ss_count = 0; + val &= ~AR_PHY_SPECTRAL_SCAN_COUNT_KIWI; + val |= SM(ss->ss_count, AR_PHY_SPECTRAL_SCAN_COUNT_KIWI); + } + + if (ss->ss_short_report == AH_TRUE) { + val |= AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT_KIWI; + } else if (ss->ss_short_report != HAL_SPECTRAL_PARAM_NOVAL) { + val &= ~AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT_KIWI; + } + + //Select the mask to be same as before + val |= AR_PHY_SPECTRAL_SCAN_PHYERR_MASK_SELECT_KIWI; + } + // Enable spectral scan + OS_REG_WRITE(ah, AR_PHY_SPECTRAL_SCAN, val | AR_PHY_SPECTRAL_SCAN_ENA); + + ar5416GetSpectralParams(ah, ss); +} + +/* + * Get the spectral parameter values and return them in the pe + * structure + */ +void +ar5416GetSpectralParams(struct ath_hal *ah, HAL_SPECTRAL_PARAM *ss) +{ + uint32_t val; + + val = OS_REG_READ(ah, AR_PHY_SPECTRAL_SCAN); + + ss->ss_fft_period = MS(val, AR_PHY_SPECTRAL_SCAN_FFT_PERIOD); + ss->ss_period = MS(val, AR_PHY_SPECTRAL_SCAN_PERIOD); + if (AR_SREV_MERLIN(ah) ) { + ss->ss_count = MS(val, AR_PHY_SPECTRAL_SCAN_COUNT); + ss->ss_short_report = MS(val, AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT); + } else { + ss->ss_count = MS(val, AR_PHY_SPECTRAL_SCAN_COUNT_KIWI); + ss->ss_short_report = MS(val, AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT_KIWI); + } + val = OS_REG_READ(ah, AR_PHY_RADAR_1); + ss->radar_bin_thresh_sel = MS(val, AR_PHY_RADAR_1_BIN_THRESH_SELECT); +} + +HAL_BOOL +ar5416IsSpectralActive(struct ath_hal *ah) +{ + uint32_t val; + + val = OS_REG_READ(ah, AR_PHY_SPECTRAL_SCAN); + return MS(val, AR_PHY_SPECTRAL_SCAN_ACTIVE); +} + +HAL_BOOL +ar5416IsSpectralEnabled(struct ath_hal *ah) +{ + uint32_t val; + + val = OS_REG_READ(ah, AR_PHY_SPECTRAL_SCAN); + return MS(val,AR_PHY_SPECTRAL_SCAN_ENA); +} + +void +ar5416StartSpectralScan(struct ath_hal *ah) +{ + uint32_t val; + + ar5416PrepSpectralScan(ah); + + // Activate spectral scan + val = OS_REG_READ(ah, AR_PHY_SPECTRAL_SCAN); + val |= AR_PHY_SPECTRAL_SCAN_ENA; + val |= AR_PHY_SPECTRAL_SCAN_ACTIVE; + OS_REG_WRITE(ah, AR_PHY_SPECTRAL_SCAN, val); + val = OS_REG_READ(ah, AR_PHY_SPECTRAL_SCAN); + val = OS_REG_READ(ah, AR_PHY_ERR_MASK_REG); + OS_REG_WRITE(ah, AR_PHY_ERR_MASK_REG, val | AR_PHY_ERR_RADAR); +} + +void +ar5416StopSpectralScan(struct ath_hal *ah) +{ + uint32_t val; + val = OS_REG_READ(ah, AR_PHY_SPECTRAL_SCAN); + + // Deactivate spectral scan + val &= ~AR_PHY_SPECTRAL_SCAN_ENA; + val &= ~AR_PHY_SPECTRAL_SCAN_ACTIVE; + OS_REG_WRITE(ah, AR_PHY_SPECTRAL_SCAN, val); + val = OS_REG_READ(ah, AR_PHY_SPECTRAL_SCAN); + val = OS_REG_READ(ah, AR_PHY_ERR_MASK_REG) & (~AR_PHY_ERR_RADAR); + OS_REG_WRITE(ah, AR_PHY_ERR_MASK_REG, val); +} + +uint32_t +ar5416GetSpectralConfig(struct ath_hal *ah) +{ + uint32_t val; + + val = OS_REG_READ(ah, AR_PHY_SPECTRAL_SCAN); + return val; +} + +void +ar5416RestoreSpectralConfig(struct ath_hal *ah, uint32_t restoreval) +{ + uint32_t curval; + + ar5416PrepSpectralScan(ah); + + curval = OS_REG_READ(ah, AR_PHY_SPECTRAL_SCAN); + + if (restoreval != curval) { + restoreval |= AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT; + OS_REG_WRITE(ah, AR_PHY_SPECTRAL_SCAN, restoreval); + } + return; +} + diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_xmit.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_xmit.c index 20791e8a5a..d29615b693 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_xmit.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416_xmit.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5416/ar5416_xmit.c 301042 2016-05-31 16:07:15Z adrian $ */ #include "opt_ah.h" @@ -135,6 +135,7 @@ ar5416StopTxDma(struct ath_hal *ah, u_int q) #define set11nRateFlags(_series, _index) \ ((_series)[_index].RateFlags & HAL_RATESERIES_2040 ? AR_2040_##_index : 0) \ |((_series)[_index].RateFlags & HAL_RATESERIES_HALFGI ? AR_GI##_index : 0) \ + |((_series)[_index].RateFlags & HAL_RATESERIES_STBC ? AR_STBC##_index : 0) \ |SM((_series)[_index].ChSel, AR_ChainSel##_index) /* @@ -154,6 +155,169 @@ ar5416StopTxDma(struct ath_hal *ah, u_int q) /* NB: accept HT rates */ #define isValidTxRate(_r) ((1<<((_r) & 0x7f)) & VALID_TX_RATES) +static inline int +ar5416RateToRateTable(struct ath_hal *ah, uint8_t rate, HAL_BOOL is_ht40) +{ + + /* + * Handle the non-MCS rates + */ + switch (rate) { + case /* 1 Mb */ 0x1b: + case /* 1 MbS*/ 0x1b | 0x4: + return (AH5416(ah)->ah_ratesArray[rate1l]); + case /* 2 Mb */ 0x1a: + return (AH5416(ah)->ah_ratesArray[rate2l]); + case /* 2 MbS*/ 0x1a | 0x4: + return (AH5416(ah)->ah_ratesArray[rate2s]); + case /* 5.5 Mb */ 0x19: + return (AH5416(ah)->ah_ratesArray[rate5_5l]); + case /* 5.5 MbS*/ 0x19 | 0x4: + return (AH5416(ah)->ah_ratesArray[rate5_5s]); + case /* 11 Mb */ 0x18: + return (AH5416(ah)->ah_ratesArray[rate11l]); + case /* 11 MbS*/ 0x18 | 0x4: + return (AH5416(ah)->ah_ratesArray[rate11s]); + } + + /* OFDM rates */ + switch (rate) { + case /* 6 Mb */ 0x0b: + return (AH5416(ah)->ah_ratesArray[rate6mb]); + case /* 9 Mb */ 0x0f: + return (AH5416(ah)->ah_ratesArray[rate9mb]); + case /* 12 Mb */ 0x0a: + return (AH5416(ah)->ah_ratesArray[rate12mb]); + case /* 18 Mb */ 0x0e: + return (AH5416(ah)->ah_ratesArray[rate18mb]); + case /* 24 Mb */ 0x09: + return (AH5416(ah)->ah_ratesArray[rate24mb]); + case /* 36 Mb */ 0x0d: + return (AH5416(ah)->ah_ratesArray[rate36mb]); + case /* 48 Mb */ 0x08: + return (AH5416(ah)->ah_ratesArray[rate48mb]); + case /* 54 Mb */ 0x0c: + return (AH5416(ah)->ah_ratesArray[rate54mb]); + } + + /* + * Handle HT20/HT40 - we only have to do MCS0-7; + * there's no stream differences. + */ + if ((rate & 0x80) && is_ht40) { + return (AH5416(ah)->ah_ratesArray[rateHt40_0 + (rate & 0x7)]); + } else if (rate & 0x80) { + return (AH5416(ah)->ah_ratesArray[rateHt20_0 + (rate & 0x7)]); + } + + /* XXX default (eg XR, bad bad person!) */ + return (AH5416(ah)->ah_ratesArray[rate6mb]); +} + +/* + * Return the TX power to be used for the given rate/chains/TX power. + * + * There are a bunch of tweaks to make to a given TX power based on + * the current configuration, so... + */ +static uint16_t +ar5416GetTxRatePower(struct ath_hal *ah, uint8_t rate, uint8_t tx_chainmask, + uint16_t txPower, HAL_BOOL is_ht40) +{ + int n_txpower, max_txpower; + const int cck_ofdm_delta = 2; +#define EEP_MINOR(_ah) \ + (AH_PRIVATE(_ah)->ah_eeversion & AR5416_EEP_VER_MINOR_MASK) +#define IS_EEP_MINOR_V2(_ah) (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_2) + + /* Take a copy ; we may underflow and thus need to clamp things */ + n_txpower = txPower; + + /* HT40? Need to adjust the TX power by this */ + if (is_ht40) + n_txpower += AH5416(ah)->ah_ht40PowerIncForPdadc; + + /* + * Merlin? Offset the target TX power offset - it defaults to + * starting at -5.0dBm, but that can change! + * + * Kiwi/Kite? Always -5.0dBm offset. + */ + if (AR_SREV_KIWI_10_OR_LATER(ah)) { + n_txpower -= (AR5416_PWR_TABLE_OFFSET_DB * 2); + } else if (AR_SREV_MERLIN_20_OR_LATER(ah)) { + int8_t pwr_table_offset = 0; + /* This is in dBm, convert to 1/2 dBm */ + (void) ath_hal_eepromGet(ah, AR_EEP_PWR_TABLE_OFFSET, + &pwr_table_offset); + n_txpower -= (pwr_table_offset * 2); + } + + /* + * If Open-loop TX power control is used, the CCK rates need + * to be offset by that. + * + * Rates: 2S, 2L, 1S, 1L, 5.5S, 5.5L + * + * XXX Odd, we don't have a PHY table entry for long preamble + * 1mbit CCK? + */ + if (AR_SREV_MERLIN_20_OR_LATER(ah) && + ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) { + + if (rate == 0x19 || rate == 0x1a || rate == 0x1b || + rate == (0x19 | 0x04) || rate == (0x1a | 0x04) || + rate == (0x1b | 0x04)) { + n_txpower -= cck_ofdm_delta; + } + } + + /* + * We're now offset by the same amount that the static maximum + * PHY power tables are. So, clamp the value based on that rate. + */ + max_txpower = ar5416RateToRateTable(ah, rate, is_ht40); +#if 0 + ath_hal_printf(ah, "%s: n_txpower = %d, max_txpower = %d, " + "rate = 0x%x , is_ht40 = %d\n", + __func__, + n_txpower, + max_txpower, + rate, + is_ht40); +#endif + n_txpower = MIN(max_txpower, n_txpower); + + /* + * We don't have to offset the TX power for two or three + * chain operation here - it's done by the AR_PHY_POWER_TX_SUB + * register setting via the EEPROM. + * + * So for vendors that programmed the maximum target power assuming + * that 2/3 chains are always on, things will just plain work. + * (They won't reach that target power if only one chain is on, but + * that's a different problem.) + */ + + /* Over/underflow? Adjust */ + if (n_txpower < 0) + n_txpower = 0; + else if (n_txpower > 63) + n_txpower = 63; + + /* + * For some odd reason the AR9160 with txpower=0 results in a + * much higher (max?) TX power. So, if it's a chipset before + * AR9220/AR9280, just clamp the minimum value at 1. + */ + if ((! AR_SREV_MERLIN_10_OR_LATER(ah)) && (n_txpower == 0)) + n_txpower = 1; + + return (n_txpower); +#undef EEP_MINOR +#undef IS_EEP_MINOR_V2 +} + HAL_BOOL ar5416SetupTxDesc(struct ath_hal *ah, struct ath_desc *ds, u_int pktLen, @@ -186,6 +350,17 @@ ar5416SetupTxDesc(struct ath_hal *ah, struct ath_desc *ds, if (txPower > 63) txPower = 63; + /* + * XXX For now, just assume that this isn't a HT40 frame. + * It'll get over-ridden by the multi-rate TX power setup. + */ + if (AH5212(ah)->ah_tpcEnabled) { + txPower = ar5416GetTxRatePower(ah, txRate0, + ahp->ah_tx_chainmask, + txPower, + AH_FALSE); + } + ads->ds_ctl0 = (pktLen & AR_FrameLen) | (txPower << AR_XmitPower_S) | (flags & HAL_TXDESC_VEOL ? AR_VEOL : 0) @@ -194,6 +369,7 @@ ar5416SetupTxDesc(struct ath_hal *ah, struct ath_desc *ds, ; ads->ds_ctl1 = (type << AR_FrameType_S) | (flags & HAL_TXDESC_NOACK ? AR_NoAck : 0) + | (flags & HAL_TXDESC_HWTS ? AR_InsertTS : 0) ; ads->ds_ctl2 = SM(txTries0, AR_XmitDataTries0) | (flags & HAL_TXDESC_DURENA ? AR_DurUpdateEn : 0) @@ -237,6 +413,8 @@ ar5416SetupTxDesc(struct ath_hal *ah, struct ath_desc *ds, * Set the TX antenna to 0 for Kite * To preserve existing behaviour, also set the TPC bits to 0; * when TPC is enabled these should be filled in appropriately. + * + * XXX TODO: when doing TPC, set the TX power up appropriately? */ if (AR_SREV_KITE(ah)) { ads->ds_ctl8 = SM(0, AR_AntCtl0); @@ -274,15 +452,23 @@ ar5416SetupXTxDesc(struct ath_hal *ah, struct ath_desc *ds, return AH_TRUE; } +/* + * XXX TODO: Figure out if AR_InsertTS is required on all sub-frames + * of a TX descriptor. + */ HAL_BOOL ar5416FillTxDesc(struct ath_hal *ah, struct ath_desc *ds, - u_int segLen, HAL_BOOL firstSeg, HAL_BOOL lastSeg, + HAL_DMA_ADDR *bufAddrList, uint32_t *segLenList, u_int descId, + u_int qcuId, HAL_BOOL firstSeg, HAL_BOOL lastSeg, const struct ath_desc *ds0) { struct ar5416_desc *ads = AR5416DESC(ds); + uint32_t segLen = segLenList[0]; HALASSERT((segLen &~ AR_BufLen) == 0); + ds->ds_data = bufAddrList[0]; + if (firstSeg) { /* * First descriptor, don't clobber xmit control data @@ -295,20 +481,33 @@ ar5416FillTxDesc(struct ath_hal *ah, struct ath_desc *ds, * copy the multi-rate transmit parameters from * the first frame for processing on completion. */ - ads->ds_ctl0 = 0; ads->ds_ctl1 = segLen; #ifdef AH_NEED_DESC_SWAP + ads->ds_ctl0 = __bswap32(AR5416DESC_CONST(ds0)->ds_ctl0) + & AR_TxIntrReq; ads->ds_ctl2 = __bswap32(AR5416DESC_CONST(ds0)->ds_ctl2); ads->ds_ctl3 = __bswap32(AR5416DESC_CONST(ds0)->ds_ctl3); + /* ctl6 - we only need encrtype; the rest are blank */ + ads->ds_ctl6 = __bswap32(AR5416DESC_CONST(ds0)->ds_ctl6 & AR_EncrType); #else + ads->ds_ctl0 = AR5416DESC_CONST(ds0)->ds_ctl0 & AR_TxIntrReq; ads->ds_ctl2 = AR5416DESC_CONST(ds0)->ds_ctl2; ads->ds_ctl3 = AR5416DESC_CONST(ds0)->ds_ctl3; + /* ctl6 - we only need encrtype; the rest are blank */ + ads->ds_ctl6 = AR5416DESC_CONST(ds0)->ds_ctl6 & AR_EncrType; #endif } else { /* !firstSeg && !lastSeg */ /* * Intermediate descriptor in a multi-descriptor frame. */ - ads->ds_ctl0 = 0; +#ifdef AH_NEED_DESC_SWAP + ads->ds_ctl0 = __bswap32(AR5416DESC_CONST(ds0)->ds_ctl0) + & AR_TxIntrReq; + ads->ds_ctl6 = __bswap32(AR5416DESC_CONST(ds0)->ds_ctl6 & AR_EncrType); +#else + ads->ds_ctl0 = AR5416DESC_CONST(ds0)->ds_ctl0 & AR_TxIntrReq; + ads->ds_ctl6 = AR5416DESC_CONST(ds0)->ds_ctl6 & AR_EncrType; +#endif ads->ds_ctl1 = segLen | AR_TxMore; ads->ds_ctl2 = 0; ads->ds_ctl3 = 0; @@ -318,42 +517,73 @@ ar5416FillTxDesc(struct ath_hal *ah, struct ath_desc *ds, return AH_TRUE; } +/* + * NB: cipher is no longer used, it's calculated. + */ HAL_BOOL ar5416ChainTxDesc(struct ath_hal *ah, struct ath_desc *ds, + HAL_DMA_ADDR *bufAddrList, + uint32_t *segLenList, u_int pktLen, u_int hdrLen, HAL_PKT_TYPE type, u_int keyIx, HAL_CIPHER cipher, uint8_t delims, - u_int segLen, HAL_BOOL firstSeg, - HAL_BOOL lastSeg) + HAL_BOOL lastSeg, + HAL_BOOL lastAggr) { struct ar5416_desc *ads = AR5416DESC(ds); uint32_t *ds_txstatus = AR5416_DS_TXSTATUS(ah,ads); struct ath_hal_5416 *ahp = AH5416(ah); + u_int segLen = segLenList[0]; int isaggr = 0; + uint32_t last_aggr = 0; (void) hdrLen; (void) ah; HALASSERT((segLen &~ AR_BufLen) == 0); + ds->ds_data = bufAddrList[0]; HALASSERT(isValidPktType(type)); if (type == HAL_PKT_TYPE_AMPDU) { type = HAL_PKT_TYPE_NORMAL; isaggr = 1; + if (lastAggr == AH_FALSE) + last_aggr = AR_MoreAggr; } - if (!firstSeg) { - OS_MEMZERO(ds->ds_hw, AR5416_DESC_TX_CTL_SZ); - } + /* + * Since this function is called before any of the other + * descriptor setup functions (at least in this particular + * 802.11n aggregation implementation), always bzero() the + * descriptor. Previously this would be done for all but + * the first segment. + * XXX TODO: figure out why; perhaps I'm using this slightly + * XXX incorrectly. + */ + OS_MEMZERO(ds->ds_hw, AR5416_DESC_TX_CTL_SZ); + /* + * Note: VEOL should only be for the last descriptor in the chain. + */ ads->ds_ctl0 = (pktLen & AR_FrameLen); + + /* + * For aggregates: + * + IsAggr must be set for all descriptors of all subframes of + * the aggregate + * + MoreAggr must be set for all descriptors of all subframes + * of the aggregate EXCEPT the last subframe; + * + MoreAggr must be _CLEAR_ for all descrpitors of the last + * subframe of the aggregate. + */ ads->ds_ctl1 = (type << AR_FrameType_S) - | (isaggr ? (AR_IsAggr | AR_MoreAggr) : 0); + | (isaggr ? (AR_IsAggr | last_aggr) : 0); + ads->ds_ctl2 = 0; ads->ds_ctl3 = 0; if (keyIx != HAL_TXKEYIX_INVALID) { @@ -362,7 +592,7 @@ ar5416ChainTxDesc(struct ath_hal *ah, struct ath_desc *ds, ads->ds_ctl0 |= AR_DestIdxValid; } - ads->ds_ctl6 = SM(ahp->ah_keytype[cipher], AR_EncrType); + ads->ds_ctl6 |= SM(ahp->ah_keytype[keyIx], AR_EncrType); if (isaggr) { ads->ds_ctl6 |= SM(delims, AR_PadDelim); } @@ -463,7 +693,6 @@ ar5416SetupLastTxDesc(struct ath_hal *ah, struct ath_desc *ds, ads->ds_ctl2 = AR5416DESC_CONST(ds0)->ds_ctl2; ads->ds_ctl3 = AR5416DESC_CONST(ds0)->ds_ctl3; #endif - return AH_TRUE; } @@ -504,6 +733,7 @@ ar5416ProcTxDesc(struct ath_hal *ah, /* Update software copies of the HW status */ ts->ts_seqnum = MS(ds_txstatus[9], AR_SeqNum); ts->ts_tstamp = AR_SendTimestamp(ds_txstatus); + ts->ts_tid = MS(ds_txstatus[9], AR_TxTid); ts->ts_status = 0; if (ds_txstatus[1] & AR_ExcessiveRetries) @@ -583,7 +813,7 @@ ar5416ProcTxDesc(struct ath_hal *ah, } /* - * These fields are not used. Zero these to preserve compatability + * These fields are not used. Zero these to preserve compatibility * with existing drivers. */ ts->ts_virtcol = MS(ads->ds_ctl1, AR_VirtRetryCnt); @@ -620,6 +850,26 @@ ar5416GetGlobalTxTimeout(struct ath_hal *ah) return MS(OS_REG_READ(ah, AR_GTXTO), AR_GTXTO_TIMEOUT_LIMIT); } +#define HT_RC_2_MCS(_rc) ((_rc) & 0x0f) +static const u_int8_t baDurationDelta[] = { + 24, // 0: BPSK + 12, // 1: QPSK 1/2 + 12, // 2: QPSK 3/4 + 4, // 3: 16-QAM 1/2 + 4, // 4: 16-QAM 3/4 + 4, // 5: 64-QAM 2/3 + 4, // 6: 64-QAM 3/4 + 4, // 7: 64-QAM 5/6 + 24, // 8: BPSK + 12, // 9: QPSK 1/2 + 12, // 10: QPSK 3/4 + 4, // 11: 16-QAM 1/2 + 4, // 12: 16-QAM 3/4 + 4, // 13: 64-QAM 2/3 + 4, // 14: 64-QAM 3/4 + 4, // 15: 64-QAM 5/6 +}; + void ar5416Set11nRateScenario(struct ath_hal *ah, struct ath_desc *ds, u_int durUpdateEn, u_int rtsctsRate, @@ -631,20 +881,6 @@ ar5416Set11nRateScenario(struct ath_hal *ah, struct ath_desc *ds, HALASSERT(nseries == 4); (void)nseries; - /* - * XXX since the upper layers doesn't know the current chainmask - * XXX setup, just override its decisions here. - * XXX The upper layers need to be taught this! - */ - if (series[0].Tries != 0) - series[0].ChSel = AH5416(ah)->ah_tx_chainmask; - if (series[1].Tries != 0) - series[1].ChSel = AH5416(ah)->ah_tx_chainmask; - if (series[2].Tries != 0) - series[2].ChSel = AH5416(ah)->ah_tx_chainmask; - if (series[3].Tries != 0) - series[3].ChSel = AH5416(ah)->ah_tx_chainmask; - /* * Only one of RTS and CTS enable must be set. * If a frame has both set, just do RTS protection - @@ -689,6 +925,88 @@ ar5416Set11nRateScenario(struct ath_hal *ah, struct ath_desc *ds, | set11nRateFlags(series, 2) | set11nRateFlags(series, 3) | SM(rtsctsRate, AR_RTSCTSRate); + + /* + * Doing per-packet TPC - update the TX power for the first + * field; program in the other series. + */ + if (AH5212(ah)->ah_tpcEnabled) { + uint32_t ds_ctl0; + uint16_t txPower; + + /* Modify the tx power field for rate 0 */ + txPower = ar5416GetTxRatePower(ah, series[0].Rate, + series[0].ChSel, + series[0].tx_power_cap, + !! (series[0].RateFlags & HAL_RATESERIES_2040)); + ds_ctl0 = ads->ds_ctl0 & ~AR_XmitPower; + ds_ctl0 |= (txPower << AR_XmitPower_S); + ads->ds_ctl0 = ds_ctl0; + + /* + * Override the whole descriptor field for each TX power. + * + * This will need changing if we ever support antenna control + * programming. + */ + txPower = ar5416GetTxRatePower(ah, series[1].Rate, + series[1].ChSel, + series[1].tx_power_cap, + !! (series[1].RateFlags & HAL_RATESERIES_2040)); + ads->ds_ctl9 = SM(0, AR_AntCtl1) | SM(txPower, AR_XmitPower1); + + txPower = ar5416GetTxRatePower(ah, series[2].Rate, + series[2].ChSel, + series[2].tx_power_cap, + !! (series[2].RateFlags & HAL_RATESERIES_2040)); + ads->ds_ctl10 = SM(0, AR_AntCtl2) | SM(txPower, AR_XmitPower2); + + txPower = ar5416GetTxRatePower(ah, series[3].Rate, + series[3].ChSel, + series[3].tx_power_cap, + !! (series[3].RateFlags & HAL_RATESERIES_2040)); + ads->ds_ctl11 = SM(0, AR_AntCtl3) | SM(txPower, AR_XmitPower3); + } +} + +/* + * Note: this should be called before calling ar5416SetBurstDuration() + * (if it is indeed called) in order to ensure that the burst duration + * is correctly updated with the BA delta workaround. + */ +void +ar5416Set11nAggrFirst(struct ath_hal *ah, struct ath_desc *ds, u_int aggrLen, + u_int numDelims) +{ + struct ar5416_desc *ads = AR5416DESC(ds); + uint32_t flags; + uint32_t burstDur; + uint8_t rate; + + ads->ds_ctl1 |= (AR_IsAggr | AR_MoreAggr); + + ads->ds_ctl6 &= ~(AR_AggrLen | AR_PadDelim); + ads->ds_ctl6 |= SM(aggrLen, AR_AggrLen); + ads->ds_ctl6 |= SM(numDelims, AR_PadDelim); + + if (! AR_SREV_MERLIN_10_OR_LATER(ah)) { + /* + * XXX It'd be nice if I were passed in the rate scenario + * at this point.. + */ + rate = MS(ads->ds_ctl3, AR_XmitRate0); + flags = ads->ds_ctl0 & (AR_CTSEnable | AR_RTSEnable); + /* + * WAR - MAC assumes normal ACK time instead of + * block ACK while computing packet duration. + * Add this delta to the burst duration in the descriptor. + */ + if (flags && (ads->ds_ctl1 & AR_IsAggr)) { + burstDur = baDurationDelta[HT_RC_2_MCS(rate)]; + ads->ds_ctl2 &= ~(AR_BurstDur); + ads->ds_ctl2 |= SM(burstDur, AR_BurstDur); + } + } } void @@ -710,6 +1028,16 @@ ar5416Set11nAggrMiddle(struct ath_hal *ah, struct ath_desc *ds, u_int numDelims) ds_txstatus[9] &= ~AR_TxDone; } +void +ar5416Set11nAggrLast(struct ath_hal *ah, struct ath_desc *ds) +{ + struct ar5416_desc *ads = AR5416DESC(ds); + + ads->ds_ctl1 |= AR_IsAggr; + ads->ds_ctl1 &= ~AR_MoreAggr; + ads->ds_ctl6 &= ~AR_PadDelim; +} + void ar5416Clr11nAggr(struct ath_hal *ah, struct ath_desc *ds) { @@ -720,14 +1048,47 @@ ar5416Clr11nAggr(struct ath_hal *ah, struct ath_desc *ds) ads->ds_ctl6 &= ~AR_AggrLen; } +void +ar5416Set11nVirtualMoreFrag(struct ath_hal *ah, struct ath_desc *ds, + u_int vmf) +{ + struct ar5416_desc *ads = AR5416DESC(ds); + if (vmf) + ads->ds_ctl0 |= AR_VirtMoreFrag; + else + ads->ds_ctl0 &= ~AR_VirtMoreFrag; +} + +/* + * Program the burst duration, with the included BA delta if it's + * applicable. + */ void ar5416Set11nBurstDuration(struct ath_hal *ah, struct ath_desc *ds, u_int burstDuration) { struct ar5416_desc *ads = AR5416DESC(ds); + uint32_t burstDur = 0; + uint8_t rate; + + if (! AR_SREV_MERLIN_10_OR_LATER(ah)) { + /* + * XXX It'd be nice if I were passed in the rate scenario + * at this point.. + */ + rate = MS(ads->ds_ctl3, AR_XmitDataTries0); + /* + * WAR - MAC assumes normal ACK time instead of + * block ACK while computing packet duration. + * Add this delta to the burst duration in the descriptor. + */ + if (ads->ds_ctl1 & AR_IsAggr) { + burstDur = baDurationDelta[HT_RC_2_MCS(rate)]; + } + } ads->ds_ctl2 &= ~AR_BurstDur; - ads->ds_ctl2 |= SM(burstDuration, AR_BurstDur); + ads->ds_ctl2 |= SM(burstDur + burstDuration, AR_BurstDur); } /* @@ -879,6 +1240,7 @@ setTxQInterrupts(struct ath_hal *ah, HAL_TX_QUEUE_INFO *qi) * Assumes: * phwChannel has been set to point to the current channel */ +#define TU_TO_USEC(_tu) ((_tu) << 10) HAL_BOOL ar5416ResetTxQueue(struct ath_hal *ah, u_int q) { @@ -886,7 +1248,7 @@ ar5416ResetTxQueue(struct ath_hal *ah, u_int q) HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps; const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan; HAL_TX_QUEUE_INFO *qi; - uint32_t cwMin, chanCwMin, value, qmisc, dmisc; + uint32_t cwMin, chanCwMin, qmisc, dmisc; if (q >= pCap->halTotalQueues) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid queue num %u\n", @@ -956,7 +1318,8 @@ ar5416ResetTxQueue(struct ath_hal *ah, u_int q) if (qi->tqi_cbrOverflowLimit) qmisc |= AR_Q_MISC_CBR_EXP_CNTR_LIMIT; } - if (qi->tqi_readyTime) { + + if (qi->tqi_readyTime && (qi->tqi_type != HAL_TX_QUEUE_CAB)) { OS_REG_WRITE(ah, AR_QRDYTIMECFG(q), SM(qi->tqi_readyTime, AR_Q_RDYTIMECFG_INT) | AR_Q_RDYTIMECFG_ENA); @@ -1027,18 +1390,46 @@ ar5416ResetTxQueue(struct ath_hal *ah, u_int q) qmisc |= AR_Q_MISC_FSP_DBA_GATED | AR_Q_MISC_CBR_INCR_DIS1 | AR_Q_MISC_CBR_INCR_DIS0; - - if (!qi->tqi_readyTime) { + HALDEBUG(ah, HAL_DEBUG_TXQUEUE, "%s: CAB: tqi_readyTime = %d\n", + __func__, qi->tqi_readyTime); + if (qi->tqi_readyTime) { + HALDEBUG(ah, HAL_DEBUG_TXQUEUE, + "%s: using tqi_readyTime\n", __func__); + OS_REG_WRITE(ah, AR_QRDYTIMECFG(q), + SM(qi->tqi_readyTime, AR_Q_RDYTIMECFG_INT) | + AR_Q_RDYTIMECFG_ENA); + } else { + int value; /* * NB: don't set default ready time if driver * has explicitly specified something. This is * here solely for backwards compatibility. */ - value = (ahp->ah_beaconInterval - - (ah->ah_config.ah_sw_beacon_response_time - - ah->ah_config.ah_dma_beacon_response_time) - - ah->ah_config.ah_additional_swba_backoff) * 1024; - OS_REG_WRITE(ah, AR_QRDYTIMECFG(q), value | AR_Q_RDYTIMECFG_ENA); + /* + * XXX for now, hard-code a CAB interval of 70% + * XXX of the total beacon interval. + * + * XXX This keeps Merlin and later based MACs + * XXX quite a bit happier (stops stuck beacons, + * XXX which I gather is because of such a long + * XXX cabq time.) + */ + value = (ahp->ah_beaconInterval * 50 / 100) + - ah->ah_config.ah_additional_swba_backoff + - ah->ah_config.ah_sw_beacon_response_time + + ah->ah_config.ah_dma_beacon_response_time; + /* + * XXX Ensure it isn't too low - nothing lower + * XXX than 10 TU + */ + if (value < 10) + value = 10; + HALDEBUG(ah, HAL_DEBUG_TXQUEUE, + "%s: defaulting to rdytime = %d uS\n", + __func__, value); + OS_REG_WRITE(ah, AR_QRDYTIMECFG(q), + SM(TU_TO_USEC(value), AR_Q_RDYTIMECFG_INT) | + AR_Q_RDYTIMECFG_ENA); } dmisc |= SM(AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL, AR_D_MISC_ARB_LOCKOUT_CNTRL); @@ -1106,3 +1497,4 @@ ar5416ResetTxQueue(struct ath_hal *ah, u_int q) return AH_TRUE; } +#undef TU_TO_USEC diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416desc.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416desc.h index 26ff387b8e..f2d05bf00b 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416desc.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416desc.h @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5416/ar5416desc.h 250346 2013-05-08 01:11:25Z adrian $ */ #ifndef _ATH_AR5416_DESC_H_ #define _ATH_AR5416_DESC_H_ @@ -301,6 +301,8 @@ struct ar5416_desc { #define AR_FinalTxIdx_S 21 #define AR_TxStatusRsvd82 0x01800000 #define AR_PowerMgmt 0x02000000 +#define AR_TxTid 0xf0000000 +#define AR_TxTid_S 28 #define AR_TxStatusRsvd83 0xfc000000 /*********** @@ -355,6 +357,7 @@ struct ar5416_desc { #define AR_RxStatusRsvd30 0xfffff800 /* Owl 2.x only */ #define AR_DupFrame 0x00000004 +#define AR_STBCFrame 0x00000008 #define AR_RxAntenna 0xffffff00 #define AR_RxAntenna_S 8 diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416phy.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416phy.h index d7a5e0b22b..6c716b0a4b 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416phy.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416phy.h @@ -14,13 +14,80 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5416/ar5416phy.h 244943 2013-01-02 00:38:01Z adrian $ */ #ifndef _DEV_ATH_AR5416PHY_H_ #define _DEV_ATH_AR5416PHY_H_ #include "ar5212/ar5212phy.h" +#define AR_BT_COEX_MODE 0x8170 +#define AR_BT_TIME_EXTEND 0x000000ff +#define AR_BT_TIME_EXTEND_S 0 +#define AR_BT_TXSTATE_EXTEND 0x00000100 +#define AR_BT_TXSTATE_EXTEND_S 8 +#define AR_BT_TX_FRAME_EXTEND 0x00000200 +#define AR_BT_TX_FRAME_EXTEND_S 9 +#define AR_BT_MODE 0x00000c00 +#define AR_BT_MODE_S 10 +#define AR_BT_QUIET 0x00001000 +#define AR_BT_QUIET_S 12 +#define AR_BT_QCU_THRESH 0x0001e000 +#define AR_BT_QCU_THRESH_S 13 +#define AR_BT_RX_CLEAR_POLARITY 0x00020000 +#define AR_BT_RX_CLEAR_POLARITY_S 17 +#define AR_BT_PRIORITY_TIME 0x00fc0000 +#define AR_BT_PRIORITY_TIME_S 18 +#define AR_BT_FIRST_SLOT_TIME 0xff000000 +#define AR_BT_FIRST_SLOT_TIME_S 24 + +#define AR_BT_COEX_WEIGHT 0x8174 +#define AR_BT_BT_WGHT 0x0000ffff +#define AR_BT_BT_WGHT_S 0 +#define AR_BT_WL_WGHT 0xffff0000 +#define AR_BT_WL_WGHT_S 16 + +#define AR_BT_COEX_MODE2 0x817c +#define AR_BT_BCN_MISS_THRESH 0x000000ff +#define AR_BT_BCN_MISS_THRESH_S 0 +#define AR_BT_BCN_MISS_CNT 0x0000ff00 +#define AR_BT_BCN_MISS_CNT_S 8 +#define AR_BT_HOLD_RX_CLEAR 0x00010000 +#define AR_BT_HOLD_RX_CLEAR_S 16 +#define AR_BT_DISABLE_BT_ANT 0x00100000 +#define AR_BT_DISABLE_BT_ANT_S 20 + +#define AR_PHY_SPECTRAL_SCAN 0x9910 +#define AR_PHY_SPECTRAL_SCAN_ENA 0x00000001 +#define AR_PHY_SPECTRAL_SCAN_ENA_S 0 +#define AR_PHY_SPECTRAL_SCAN_ACTIVE 0x00000002 +#define AR_PHY_SPECTRAL_SCAN_ACTIVE_S 1 +#define AR_PHY_SPECTRAL_SCAN_FFT_PERIOD 0x000000F0 +#define AR_PHY_SPECTRAL_SCAN_FFT_PERIOD_S 4 +#define AR_PHY_SPECTRAL_SCAN_PERIOD 0x0000FF00 +#define AR_PHY_SPECTRAL_SCAN_PERIOD_S 8 + +/* Scan count and Short repeat flags are different for Kiwi and Merlin */ +#define AR_PHY_SPECTRAL_SCAN_COUNT 0x00FF0000 +#define AR_PHY_SPECTRAL_SCAN_COUNT_S 16 +#define AR_PHY_SPECTRAL_SCAN_COUNT_KIWI 0x0FFF0000 +#define AR_PHY_SPECTRAL_SCAN_COUNT_KIWI_S 16 + +#define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT 0x01000000 +#define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT_S 24 +#define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT_KIWI 0x10000000 +#define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT_KIWI_S 28 + +/* + * Kiwi only, bit 30 is used to set the error type, if set it is 0x5 (HAL_PHYERR_RADAR) + * Else it is 38 (new error type) + */ +#define AR_PHY_SPECTRAL_SCAN_PHYERR_MASK_SELECT_KIWI 0x40000000 /* Spectral Error select bit mask */ +#define AR_PHY_SPECTRAL_SCAN_PHYERR_MASK_SELECT_KIWI_S 30 /* Spectral Error select bit 30 */ + +#define AR_PHY_SPECTRAL_SCAN_PRIORITY_SELECT_KIWI 0x20000000 /* Spectral Error select bit mask */ +#define AR_PHY_SPECTRAL_SCAN_PRIORITY_SELECT_SELECT_KIWI_S 29 /* Spectral Error select bit 30 */ + /* For AR_PHY_RADAR0 */ #define AR_PHY_RADAR_0_FFT_ENA 0x80000000 @@ -28,6 +95,8 @@ #define AR_PHY_RADAR_EXT_ENA 0x00004000 #define AR_PHY_RADAR_1 0x9958 +#define AR_PHY_RADAR_1_BIN_THRESH_SELECT 0x07000000 +#define AR_PHY_RADAR_1_BIN_THRESH_SELECT_S 24 #define AR_PHY_RADAR_1_RELPWR_ENA 0x00800000 #define AR_PHY_RADAR_1_USE_FIR128 0x00400000 #define AR_PHY_RADAR_1_RELPWR_THRESH 0x003F0000 @@ -121,12 +190,6 @@ #define AR_PHY_EXT_MINCCA_PWR_S 23 #define AR_PHY_EXT_CCA_THRESH62 0x007F0000 #define AR_PHY_EXT_CCA_THRESH62_S 16 -/* - * This duplicates AR_PHY_EXT_CCA_CYCPWR_THR1; it reads more like - * an ANI register this way. - */ -#define AR_PHY_EXT_TIMING5_CYCPWR_THR1 0x0000FE00 -#define AR_PHY_EXT_TIMING5_CYCPWR_THR1_S 9 #define AR9280_PHY_EXT_MINCCA_PWR 0x01FF0000 #define AR9280_PHY_EXT_MINCCA_PWR_S 16 @@ -156,8 +219,14 @@ #define AR_PHY_CAL_MEAS_0(_i) (0x9c10 + ((_i) << 12)) #define AR_PHY_CAL_MEAS_1(_i) (0x9c14 + ((_i) << 12)) #define AR_PHY_CAL_MEAS_2(_i) (0x9c18 + ((_i) << 12)) +/* This is AR9130 and later */ #define AR_PHY_CAL_MEAS_3(_i) (0x9c1c + ((_i) << 12)) +/* + * AR5416 still uses AR_PHY(263) for current RSSI; + * AR9130 and later uses AR_PHY(271). + */ +#define AR9130_PHY_CURRENT_RSSI 0x9c3c /* rssi of current frame rx'd */ #define AR_PHY_CCA 0x9864 #define AR_PHY_MINCCA_PWR 0x0FF80000 diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416reg.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416reg.h index c18c26f90f..007ac40b34 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416reg.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar5416/ar5416reg.h @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar5416/ar5416reg.h 298939 2016-05-02 19:56:48Z pfg $ */ #ifndef _DEV_ATH_AR5416REG_H #define _DEV_ATH_AR5416REG_H @@ -47,14 +47,58 @@ #define AR_GPIO_IN_OUT 0x4048 /* GPIO input/output register */ #define AR_GPIO_OE_OUT 0x404c /* GPIO output enable register */ #define AR_GPIO_INTR_POL 0x4050 /* GPIO interrupt polarity */ + #define AR_GPIO_INPUT_EN_VAL 0x4054 /* GPIO input enable and value */ +#define AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_DEF 0x00000004 +#define AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_S 2 +#define AR_GPIO_INPUT_EN_VAL_BT_FREQUENCY_DEF 0x00000008 +#define AR_GPIO_INPUT_EN_VAL_BT_FREQUENCY_S 3 +#define AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_DEF 0x00000010 +#define AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_S 4 +#define AR_GPIO_INPUT_EN_VAL_RFSILENT_DEF 0x00000080 +#define AR_GPIO_INPUT_EN_VAL_RFSILENT_DEF_S 7 +#define AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_BB 0x00000400 +#define AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_BB_S 10 +#define AR_GPIO_INPUT_EN_VAL_BT_FREQUENCY_BB 0x00000800 +#define AR_GPIO_INPUT_EN_VAL_BT_FREQUENCY_BB_S 11 +#define AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB 0x00001000 +#define AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB_S 12 +#define AR_GPIO_INPUT_EN_VAL_RFSILENT_BB 0x00008000 +#define AR_GPIO_INPUT_EN_VAL_RFSILENT_BB_S 15 +#define AR_GPIO_RTC_RESET_OVERRIDE_ENABLE 0x00010000 +#define AR_GPIO_JTAG_DISABLE 0x00020000 + #define AR_GPIO_INPUT_MUX1 0x4058 +#define AR_GPIO_INPUT_MUX1_BT_PRIORITY 0x00000f00 +#define AR_GPIO_INPUT_MUX1_BT_PRIORITY_S 8 +#define AR_GPIO_INPUT_MUX1_BT_FREQUENCY 0x0000f000 +#define AR_GPIO_INPUT_MUX1_BT_FREQUENCY_S 12 +#define AR_GPIO_INPUT_MUX1_BT_ACTIVE 0x000f0000 +#define AR_GPIO_INPUT_MUX1_BT_ACTIVE_S 16 + #define AR_GPIO_INPUT_MUX2 0x405c +#define AR_GPIO_INPUT_MUX2_CLK25 0x0000000f +#define AR_GPIO_INPUT_MUX2_CLK25_S 0 +#define AR_GPIO_INPUT_MUX2_RFSILENT 0x000000f0 +#define AR_GPIO_INPUT_MUX2_RFSILENT_S 4 +#define AR_GPIO_INPUT_MUX2_RTC_RESET 0x00000f00 +#define AR_GPIO_INPUT_MUX2_RTC_RESET_S 8 + #define AR_GPIO_OUTPUT_MUX1 0x4060 #define AR_GPIO_OUTPUT_MUX2 0x4064 #define AR_GPIO_OUTPUT_MUX3 0x4068 + +#define AR_GPIO_OUTPUT_MUX_AS_OUTPUT 0 +#define AR_GPIO_OUTPUT_MUX_AS_PCIE_ATTENTION_LED 1 +#define AR_GPIO_OUTPUT_MUX_AS_PCIE_POWER_LED 2 +#define AR_GPIO_OUTPUT_MUX_AS_TX_FRAME 3 +#define AR_GPIO_OUTPUT_MUX_AS_RX_CLEAR_EXTERNAL 4 +#define AR_GPIO_OUTPUT_MUX_AS_MAC_NETWORK_LED 5 +#define AR_GPIO_OUTPUT_MUX_AS_MAC_POWER_LED 6 + #define AR_EEPROM_STATUS_DATA 0x407c #define AR_OBS 0x4080 +#define AR_GPIO_PDPU 0x4088 #ifdef AH_SUPPORT_AR9130 #define AR_RTC_BASE 0x20000 @@ -79,6 +123,12 @@ #define AR_RTC_DERIVED_CLK_PERIOD_S 1 #endif /* AH_SUPPORT_AR9130 */ +/* AR_USEC: 0x801c */ +#define AR5416_USEC_TX_LAT 0x007FC000 /* tx latency to start of SIGNAL (usec) */ +#define AR5416_USEC_TX_LAT_S 14 /* tx latency to start of SIGNAL (usec) */ +#define AR5416_USEC_RX_LAT 0x1F800000 /* rx latency to start of SIGNAL (usec) */ +#define AR5416_USEC_RX_LAT_S 23 /* rx latency to start of SIGNAL (usec) */ + #define AR_RESET_TSF 0x8020 /* @@ -95,6 +145,7 @@ #define AR_TSFOOR_THRESHOLD 0x813c #define AR_PHY_ERR_3 0x8168 #define AR_PHY_ERR_MASK_3 0x816c /* mask for AR_PHY_ERR_3 */ +#define AR_BT_COEX_WEIGHT2 0x81c4 #define AR_TXOP_X 0x81ec /* txop for legacy non-qos */ #define AR_TXOP_0_3 0x81f0 /* txop for various tid's */ #define AR_TXOP_4_7 0x81f4 @@ -128,6 +179,7 @@ #define AR_2040_MODE 0x8318 #define AR_EXTRCCNT 0x8328 /* extension channel rx clear count */ #define AR_SELFGEN_MASK 0x832c /* rx and cal chain masks */ +#define AR_PHY_ERR_MASK_REG 0x8338 #define AR_PCU_TXBUF_CTRL 0x8340 #define AR_PCU_MISC_MODE2 0x8344 @@ -203,27 +255,32 @@ #define AR_MAC_LED_MODE_POWON 5 /* Power LED on (s/w control) */ #define AR_MAC_LED_MODE_NETON 6 /* Network LED on (s/w control) */ #define AR_MAC_LED_ASSOC 0x00000c00 -#define AR_MAC_LED_ASSOC_NONE 0x00000000 /* STA is not associated or trying */ -#define AR_MAC_LED_ASSOC_ACTIVE 0x00000400 /* STA is associated */ -#define AR_MAC_LED_ASSOC_PEND 0x00000800 /* STA is trying to associate */ +#define AR_MAC_LED_ASSOC_NONE 0x0 /* STA is not associated or trying */ +#define AR_MAC_LED_ASSOC_ACTIVE 0x1 /* STA is associated */ +#define AR_MAC_LED_ASSOC_PEND 0x2 /* STA is trying to associate */ #define AR_MAC_LED_ASSOC_S 10 +#define AR_WA_BIT6 0x00000040 +#define AR_WA_BIT7 0x00000080 +#define AR_WA_D3_L1_DISABLE 0x00004000 /* */ #define AR_WA_UNTIE_RESET_EN 0x00008000 /* ena PCI reset to POR */ #define AR_WA_RESET_EN 0x00040000 /* ena AR_WA_UNTIE_RESET_EN */ #define AR_WA_ANALOG_SHIFT 0x00100000 #define AR_WA_POR_SHORT 0x00200000 /* PCIE phy reset control */ +#define AR_WA_BIT22 0x00400000 +#define AR_WA_BIT23 0x00800000 #define AR_WA_DEFAULT 0x0000073f -#define AR9280_WA_DEFAULT 0x0040073f +#define AR9280_WA_DEFAULT 0x0040073b /* disable bit 2, see commit */ #define AR9285_WA_DEFAULT 0x004a05cb #define AR_PCIE_PM_CTRL_ENA 0x00080000 #define AR_AHB_EXACT_WR_EN 0x00000000 /* write exact bytes */ -#define AR_AHB_BUF_WR_EN 0x00000001 /* buffer write upto cacheline*/ +#define AR_AHB_BUF_WR_EN 0x00000001 /* buffer write up to cacheline*/ #define AR_AHB_EXACT_RD_EN 0x00000000 /* read exact bytes */ -#define AR_AHB_CACHELINE_RD_EN 0x00000002 /* read upto end of cacheline */ -#define AR_AHB_PREFETCH_RD_EN 0x00000004 /* prefetch upto page boundary*/ +#define AR_AHB_CACHELINE_RD_EN 0x00000002 /* read up to end of cacheline */ +#define AR_AHB_PREFETCH_RD_EN 0x00000004 /* prefetch up to page boundary*/ #define AR_AHB_PAGE_SIZE_1K 0x00000000 /* set page-size as 1k */ #define AR_AHB_PAGE_SIZE_2K 0x00000008 /* set page-size as 2k */ #define AR_AHB_PAGE_SIZE_4K 0x00000010 /* set page-size as 4k */ @@ -250,6 +307,7 @@ /* Interrupts */ #define AR_ISR_TXMINTR 0x00080000 /* Maximum interrupt tx rate */ #define AR_ISR_RXMINTR 0x01000000 /* Maximum interrupt rx rate */ +#define AR_ISR_GENTMR 0x10000000 /* OR of generic timer bits in S5 */ #define AR_ISR_TXINTM 0x40000000 /* Tx int after mitigation */ #define AR_ISR_RXINTM 0x80000000 /* Rx int after mitigation */ @@ -259,7 +317,13 @@ #define AR_ISR_S5 0x0098 #define AR_ISR_S5_S 0x00d8 -#define AR_ISR_S5_TIM_TIMER 0x00000010 +#define AR_ISR_S5_GENTIMER7 0x00000080 // Mask for timer 7 trigger +#define AR_ISR_S5_TIM_TIMER 0x00000010 // TIM Timer ISR +#define AR_ISR_S5_DTIM_TIMER 0x00000020 // DTIM Timer ISR +#define AR_ISR_S5_GENTIMER_TRIG 0x0000FF80 // ISR for generic timer trigger 7-15 +#define AR_ISR_S5_GENTIMER_TRIG_S 0 +#define AR_ISR_S5_GENTIMER_THRESH 0xFF800000 // ISR for generic timer threshold 7-15 +#define AR_ISR_S5_GENTIMER_THRESH_S 16 #define AR_INTR_SPURIOUS 0xffffffff #define AR_INTR_RTC_IRQ 0x00000001 /* rtc in shutdown state */ @@ -412,10 +476,10 @@ /* Sleep control */ #define AR5416_SLEEP1_ASSUME_DTIM 0x00080000 #define AR5416_SLEEP1_CAB_TIMEOUT 0xFFE00000 /* Cab timeout (TU) */ -#define AR5416_SLEEP1_CAB_TIMEOUT_S 22 +#define AR5416_SLEEP1_CAB_TIMEOUT_S 21 #define AR5416_SLEEP2_BEACON_TIMEOUT 0xFFE00000 /* Beacon timeout (TU)*/ -#define AR5416_SLEEP2_BEACON_TIMEOUT_S 22 +#define AR5416_SLEEP2_BEACON_TIMEOUT_S 21 /* Sleep Registers */ #define AR_SLP32_HALFCLK_LATENCY 0x000FFFFF /* rising <-> falling edge */ @@ -452,9 +516,12 @@ #define AR_PCU_MISS_BCN_IN_SLEEP 0x00004000 /* count bmiss's when sleeping */ #define AR_PCU_BUG_12306_FIX_ENA 0x00020000 /* use rx_clear to count sifs */ #define AR_PCU_FORCE_QUIET_COLL 0x00040000 /* kill xmit for channel change */ -#define AR_PCU_TBTT_PROTECT 0x00200000 /* no xmit upto tbtt+20 uS */ +#define AR_PCU_BT_ANT_PREVENT_RX 0x00100000 +#define AR_PCU_BT_ANT_PREVENT_RX_S 20 +#define AR_PCU_TBTT_PROTECT 0x00200000 /* no xmit up to tbtt+20 uS */ #define AR_PCU_CLEAR_VMF 0x01000000 /* clear vmf mode (fast cc)*/ #define AR_PCU_CLEAR_BA_VALID 0x04000000 /* clear ba state */ +#define AR_PCU_SEL_EVM 0x08000000 /* select EVM data or PLCP header */ #define AR_PCU_MISC_MODE2_MGMT_CRYPTO_ENABLE 0x00000002 #define AR_PCU_MISC_MODE2_NO_CRYPTO_FOR_NON_DATA_PKT 0x00000004 @@ -497,6 +564,8 @@ #define AR928X_GPIO_IN_VAL_S 10 #define AR9285_GPIO_IN_VAL 0x00FFF000 #define AR9285_GPIO_IN_VAL_S 12 +#define AR9287_GPIO_IN_VAL 0x003FF800 +#define AR9287_GPIO_IN_VAL_S 11 #define AR_GPIO_OE_OUT_DRV 0x3 /* 2 bit mask shifted by 2*bitpos */ #define AR_GPIO_OE_OUT_DRV_NO 0x0 /* tristate */ @@ -534,6 +603,25 @@ #define AR_EEPROM_STATUS_DATA_PROT_ACCESS 0x00040000 #define AR_EEPROM_STATUS_DATA_ABSENT_ACCESS 0x00080000 +/* K2 (9271) */ +#define AR9271_CLOCK_CONTROL 0x50040 +#define AR9271_CLOCK_SELECTION_22 0x0 +#define AR9271_CLOCK_SELECTION_88 0x1 +#define AR9271_CLOCK_SELECTION_44 0x2 +#define AR9271_CLOCK_SELECTION_117 0x4 +#define AR9271_CLOCK_SELECTION_OSC_40 0x6 +#define AR9271_CLOCK_SELECTION_RTC 0x7 +#define AR9271_SPI_SEL 0x100 +#define AR9271_UART_SEL 0x200 + +#define AR9271_RESET_POWER_DOWN_CONTROL 0x50044 +#define AR9271_RADIO_RF_RST 0x20 +#define AR9271_GATE_MAC_CTL 0x4000 +#define AR9271_MAIN_PLL_PWD_CTL 0x40000 + +#define AR9271_CLKMISC 0x4090 +#define AR9271_OSC_to_10M_EN 0x00000001 + /* * AR5212 defines the MAC revision mask as 0xF, but both ath9k and * the Atheros HAL define it as 0x7. @@ -605,10 +693,10 @@ #define AR_XSREV_REVISION_KITE_11 1 /* Kite 1.1 */ #define AR_XSREV_REVISION_KITE_12 2 /* Kite 1.2 */ #define AR_XSREV_VERSION_KIWI 0x180 /* Kiwi (AR9287) */ -#define AR_XSREV_REVISION_KIWI_10 0 -#define AR_XSREV_REVISION_KIWI_11 1 -#define AR_XSREV_REVISION_KIWI_12 2 -#define AR_XSREV_REVISION_KIWI_13 3 +#define AR_XSREV_REVISION_KIWI_10 0 /* Kiwi 1.0 */ +#define AR_XSREV_REVISION_KIWI_11 1 /* Kiwi 1.1 */ +#define AR_XSREV_REVISION_KIWI_12 2 /* Kiwi 1.2 */ +#define AR_XSREV_REVISION_KIWI_13 3 /* Kiwi 1.3 */ /* Owl (AR5416) */ #define AR_SREV_OWL(_ah) \ @@ -694,6 +782,10 @@ #define AR_SREV_KIWI(_ah) \ (AH_PRIVATE((_ah))->ah_macVersion == AR_XSREV_VERSION_KIWI) +#define AR_SREV_KIWI_10_OR_LATER(_ah) \ + (AH_PRIVATE((_ah))->ah_macVersion >= AR_XSREV_VERSION_KIWI) + +/* XXX TODO: make these handle macVersion > Kiwi */ #define AR_SREV_KIWI_11_OR_LATER(_ah) \ (AR_SREV_KIWI(_ah) && \ AH_PRIVATE((_ah))->ah_macRev >= AR_XSREV_REVISION_KIWI_11) diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9130.ini b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9130.ini index 0bd9f8e0a8..559128fb03 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9130.ini +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9130.ini @@ -13,7 +13,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9001/ar9130.ini 221163 2011-04-28 12:47:40Z adrian $ */ static const uint32_t ar5416Modes_9100[][6] = { diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9130_attach.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9130_attach.c index 518ed85fe8..0ae97e77c5 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9130_attach.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9130_attach.c @@ -15,7 +15,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9001/ar9130_attach.c 272292 2014-09-30 03:19:29Z adrian $ */ #include "opt_ah.h" @@ -69,7 +69,9 @@ static HAL_BOOL ar9130FillCapabilityInfo(struct ath_hal *ah); */ static struct ath_hal * ar9130Attach(uint16_t devid, HAL_SOFTC sc, - HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, HAL_STATUS *status) + HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, + HAL_OPS_CONFIG *ah_config, + HAL_STATUS *status) { struct ath_hal_5416 *ahp5416; struct ath_hal_5212 *ahp; @@ -78,13 +80,13 @@ ar9130Attach(uint16_t devid, HAL_SOFTC sc, HAL_STATUS ecode; HAL_BOOL rfStatus; - HALDEBUG_G(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n", + HALDEBUG(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n", __func__, sc, (void*) st, (void*) sh); /* NB: memory is returned zero'd */ ahp5416 = ath_hal_malloc(sizeof (struct ath_hal_5416)); if (ahp5416 == AH_NULL) { - HALDEBUG_G(AH_NULL, HAL_DEBUG_ANY, + HALDEBUG(AH_NULL, HAL_DEBUG_ANY, "%s: cannot allocate memory for state block\n", __func__); *status = HAL_ENOMEM; return AH_NULL; @@ -119,11 +121,11 @@ ar9130Attach(uint16_t devid, HAL_SOFTC sc, /* * Use the "local" EEPROM data given to us by the higher layers. - * This is a private copy out of system flash. The Linux ath9k - * commit for the initial AR9130 support mentions MMIO flash - * access is "unreliable." -adrian + * This is a private copy out of system flash. + * By this stage the SoC SPI flash may have disabled the memory- + * mapping and rely purely on port-based SPI IO. */ - AH_PRIVATE((ah))->ah_eepromRead = ar9130EepromRead; + AH_PRIVATE((ah))->ah_eepromRead = ath_hal_EepromDataRead; AH_PRIVATE((ah))->ah_eepromWrite = NULL; ah->ah_eepromdata = eepromdata; @@ -298,6 +300,15 @@ ar9130FillCapabilityInfo(struct ath_hal *ah) */ pCap->halMbssidAggrSupport = AH_FALSE; pCap->hal4AddrAggrSupport = AH_TRUE; + /* BB Read WAR */ + pCap->halHasBBReadWar = AH_TRUE; + + /* + * Implement the PLL/config changes needed for half/quarter + * rates before re-enabling them here. + */ + pCap->halChanHalfRate = AH_FALSE; + pCap->halChanQuarterRate = AH_FALSE; return AH_TRUE; } diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9130_eeprom.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9130_eeprom.c index 465e1739a9..9afd494412 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9130_eeprom.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9130_eeprom.c @@ -14,30 +14,5 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9001/ar9130_eeprom.c 230147 2012-01-15 19:22:34Z adrian $ */ -#include "opt_ah.h" - -#include "ah.h" -#include "ah_internal.h" - -#include "ar9001/ar9130_eeprom.h" - -/* XXX this shouldn't be done here */ -/* This is in 16 bit words; not bytes -adrian */ -#define ATH_DATA_EEPROM_SIZE 2048 - -HAL_BOOL -ar9130EepromRead(struct ath_hal *ah, u_int off, uint16_t *data) -{ - if (ah->ah_eepromdata == AH_NULL) { - HALDEBUG(ah, HAL_DEBUG_ANY, "%s: no eeprom data!\n", __func__); - return AH_FALSE; - } - if (off > ATH_DATA_EEPROM_SIZE) { - HALDEBUG(ah, HAL_DEBUG_ANY, "ar9130EepromRead: offset %x > %x\n", off, ATH_DATA_EEPROM_SIZE); - return AH_FALSE; - } - (*data) = ah->ah_eepromdata[off]; - return AH_TRUE; -} diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9130_eeprom.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9130_eeprom.h index 54556f6e14..9031f3f773 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9130_eeprom.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9130_eeprom.h @@ -14,11 +14,9 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9001/ar9130_eeprom.h 230147 2012-01-15 19:22:34Z adrian $ */ #ifndef __AR9130_EEPROM_H__ #define __AR9130_EEPROM_H__ -extern HAL_BOOL ar9130EepromRead(struct ath_hal *ah, u_int off, uint16_t *data); - #endif diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9130_phy.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9130_phy.c index 61ee0a2341..9c229348db 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9130_phy.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9130_phy.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9001/ar9130_phy.c 242412 2012-10-31 21:14:25Z adrian $ */ #include "opt_ah.h" @@ -33,6 +33,9 @@ ar9130InitPLL(struct ath_hal *ah, const struct ieee80211_channel *chan) uint32_t pll; + /* + * XXX TODO: support half/quarter rates + */ if (chan && IEEE80211_IS_CHAN_5GHZ(chan)) pll = 0x1450; else diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9130_phy.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9130_phy.h index 516df9277f..f1c35a9bd1 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9130_phy.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9130_phy.h @@ -15,7 +15,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9001/ar9130_phy.h 221163 2011-04-28 12:47:40Z adrian $ */ #ifndef __AR9130_PHY_H__ #define __AR9130_PHY_H__ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9130reg.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9130reg.h index c289c32e2c..e737f48599 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9130reg.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9130reg.h @@ -15,7 +15,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9001/ar9130reg.h 221163 2011-04-28 12:47:40Z adrian $ */ #ifndef __AR9130REG_H__ #define __AR9130REG_H__ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9160.ini b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9160.ini index 963b0006e0..2a0b76ef25 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9160.ini +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9160.ini @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9001/ar9160.ini 220293 2011-04-03 11:59:52Z adrian $ */ /* Auto Generated PCI Register Writes. Created: 05/22/08 */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9160_attach.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9160_attach.c index 2234eb35bf..f87f798bd3 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9160_attach.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9001/ar9160_attach.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9001/ar9160_attach.c 272292 2014-09-30 03:19:29Z adrian $ */ #include "opt_ah.h" @@ -68,8 +68,8 @@ ar9160AniSetup(struct ath_hal *ah) .coarseHigh = { -14, -14, -14, -14, -12 }, .coarseLow = { -64, -64, -64, -64, -70 }, .firpwr = { -78, -78, -78, -78, -80 }, - .maxSpurImmunityLevel = 2, - .cycPwrThr1 = { 2, 4, 6 }, + .maxSpurImmunityLevel = 7, + .cycPwrThr1 = { 2, 4, 6, 8, 10, 12, 14, 16 }, .maxFirstepLevel = 2, /* levels 0..2 */ .firstep = { 0, 4, 8 }, .ofdmTrigHigh = 500, @@ -114,6 +114,7 @@ ar9160InitPLL(struct ath_hal *ah, const struct ieee80211_channel *chan) static struct ath_hal * ar9160Attach(uint16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, + HAL_OPS_CONFIG *ah_config, HAL_STATUS *status) { struct ath_hal_5416 *ahp5416; @@ -123,13 +124,13 @@ ar9160Attach(uint16_t devid, HAL_SOFTC sc, HAL_STATUS ecode; HAL_BOOL rfStatus; - HALDEBUG_G(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n", + HALDEBUG(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n", __func__, sc, (void*) st, (void*) sh); /* NB: memory is returned zero'd */ ahp5416 = ath_hal_malloc(sizeof (struct ath_hal_5416)); if (ahp5416 == AH_NULL) { - HALDEBUG_G(AH_NULL, HAL_DEBUG_ANY, + HALDEBUG(AH_NULL, HAL_DEBUG_ANY, "%s: cannot allocate memory for state block\n", __func__); *status = HAL_ENOMEM; return AH_NULL; @@ -183,7 +184,7 @@ ar9160Attach(uint16_t devid, HAL_SOFTC sc, HAL_INI_INIT(&AH5416(ah)->ah_ini_bank1, ar9160Bank1, 2); HAL_INI_INIT(&AH5416(ah)->ah_ini_bank2, ar9160Bank2, 2); HAL_INI_INIT(&AH5416(ah)->ah_ini_bank3, ar9160Bank3, 3); - HAL_INI_INIT(&AH5416(ah)->ah_ini_bank6, ar9160Bank6, 3); + HAL_INI_INIT(&AH5416(ah)->ah_ini_bank6, ar9160Bank6TPC, 3); HAL_INI_INIT(&AH5416(ah)->ah_ini_bank7, ar9160Bank7, 2); if (AR_SREV_SOWL_11(ah)) HAL_INI_INIT(&AH5416(ah)->ah_ini_addac, ar9160Addac_1_1, 2); @@ -320,6 +321,8 @@ ar9160FillCapabilityInfo(struct ath_hal *ah) pCap->halAutoSleepSupport = AH_FALSE; /* XXX? */ pCap->halMbssidAggrSupport = AH_TRUE; pCap->hal4AddrAggrSupport = AH_TRUE; + /* BB Read WAR */ + pCap->halHasBBReadWar = AH_TRUE; /* AR9160 is a 2x2 stream device */ pCap->halTxStreams = 2; diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9002phy.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9002phy.h index 3d837cfcf5..2e496e5e20 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9002phy.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9002phy.h @@ -22,7 +22,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9002/ar9002phy.h 220590 2011-04-13 04:40:59Z adrian $ */ #ifndef __ATH_AR9002PHY_H__ #define __ATH_AR9002PHY_H__ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9280.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9280.c index 99fd1d790d..0a18f3f5d7 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9280.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9280.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9002/ar9280.c 241195 2012-10-04 15:42:45Z adrian $ */ #include "opt_ah.h" @@ -112,7 +112,17 @@ ar9280SetChannel(struct ath_hal *ah, const struct ieee80211_channel *chan) switch (frac_n_5g) { case 0: - if ((freq % 20) == 0) { + /* + * Enable fractional mode for half/quarter rate + * channels. + * + * This is from the Linux ath9k code, rather than + * the Atheros HAL code. + */ + if (IEEE80211_IS_CHAN_QUARTER(chan) || + IEEE80211_IS_CHAN_HALF(chan)) + aModeRefSel = 0; + else if ((freq % 20) == 0) { aModeRefSel = 3; } else if ((freq % 10) == 0) { aModeRefSel = 2; @@ -122,9 +132,63 @@ ar9280SetChannel(struct ath_hal *ah, const struct ieee80211_channel *chan) default: aModeRefSel = 0; /* Enable 2G (fractional) mode for channels which are 5MHz spaced */ - fracMode = 1; - refDivA = 1; - channelSel = (freq * 0x8000)/15; + + /* + * Workaround for talking on PSB non-5MHz channels; + * the pre-Merlin chips only had a 2.5MHz channel + * spacing so some channels aren't reachable. + + * + * This interoperates on the quarter rate channels + * with the AR5112 and later RF synths. Please note + * that the synthesiser isn't able to completely + * accurately represent these frequencies (as the + * resolution in this reference is 2.5MHz) and thus + * it will be slightly "off centre." This matches + * the same slightly incorrect centre frequency + * behaviour that the AR5112 and later channel + * selection code has. + * + * This also interoperates with the AR5416 + * synthesiser modification for programming + * fractional frequencies in 5GHz mode. However + * that modification is also disabled by default. + * + * This is disabled because it hasn't been tested for + * regulatory compliance and neither have the NICs + * which would use it. So if you enable this code, + * you must first ensure that you've re-certified the + * NICs in question beforehand or you will be + * violating your local regulatory rules and breaking + * the law. + */ +#if 0 + if (freq % 5 == 0) { +#endif + /* Normal */ + fracMode = 1; + refDivA = 1; + channelSel = (freq * 0x8000)/15; +#if 0 + } else { + /* Offset by 500KHz */ + uint32_t f, ch, ch2; + + fracMode = 1; + refDivA = 1; + + /* Calculate the "adjusted" frequency */ + f = freq - 2; + ch = (((f - 4800) * 10) / 25) + 1; + + ch2 = ((ch * 25) / 5) + 9600; + channelSel = (ch2 * 0x4000) / 15; + //ath_hal_printf(ah, + // "%s: freq=%d, ch=%d, ch2=%d, " + // "channelSel=%d\n", + // __func__, freq, ch, ch2, channelSel); + } +#endif /* RefDivA setting */ OS_A_REG_RMW_FIELD(ah, AR_AN_SYNTH9, @@ -384,3 +448,11 @@ ar9280RfAttach(struct ath_hal *ah, HAL_STATUS *status) return AH_TRUE; } + +static HAL_BOOL +ar9280RfProbe(struct ath_hal *ah) +{ + return (AR_SREV_MERLIN(ah)); +} + +AH_RF(RF9280, ar9280RfProbe, ar9280RfAttach); diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9280.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9280.h index e383918abb..dd5e67040a 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9280.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9280.h @@ -13,7 +13,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9002/ar9280.h 224243 2011-07-21 08:35:10Z adrian $ */ #ifndef _ATH_AR9280_H_ #define _ATH_AR9280_H_ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9280_attach.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9280_attach.c index 4f4b8baf2b..40daf50cdd 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9280_attach.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9280_attach.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9002/ar9280_attach.c 272292 2014-09-30 03:19:29Z adrian $ */ #include "opt_ah.h" @@ -61,7 +61,9 @@ static const HAL_PERCAL_DATA ar9280_adc_init_dc_cal = { .calPostProc = ar5416AdcDcCalibration }; -static void ar9280ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore); +static void ar9280ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore, + HAL_BOOL power_off); +static void ar9280DisablePCIE(struct ath_hal *ah); static HAL_BOOL ar9280FillCapabilityInfo(struct ath_hal *ah); static void ar9280WriteIni(struct ath_hal *ah, const struct ieee80211_channel *chan); @@ -80,8 +82,8 @@ ar9280AniSetup(struct ath_hal *ah) .coarseHigh = { -14, -14, -14, -14, -12 }, .coarseLow = { -64, -64, -64, -64, -70 }, .firpwr = { -78, -78, -78, -78, -80 }, - .maxSpurImmunityLevel = 2, - .cycPwrThr1 = { 2, 4, 6 }, + .maxSpurImmunityLevel = 7, + .cycPwrThr1 = { 2, 4, 6, 8, 10, 12, 14, 16 }, .maxFirstepLevel = 2, /* levels 0..2 */ .firstep = { 0, 4, 8 }, .ofdmTrigHigh = 500, @@ -112,6 +114,10 @@ ar9280InitPLL(struct ath_hal *ah, const struct ieee80211_channel *chan) * Else, set PLL to 0x2850 to prevent reset-to-reset variation */ pll = IS_5GHZ_FAST_CLOCK_EN(ah, chan) ? 0x142c : 0x2850; + if (IEEE80211_IS_CHAN_HALF(chan)) + pll |= SM(0x1, AR_RTC_SOWL_PLL_CLKSEL); + else if (IEEE80211_IS_CHAN_QUARTER(chan)) + pll |= SM(0x2, AR_RTC_SOWL_PLL_CLKSEL); } else if (AR_SREV_MERLIN_10_OR_LATER(ah)) { pll = SM(0x5, AR_RTC_SOWL_PLL_REFDIV); if (chan != AH_NULL) { @@ -142,6 +148,7 @@ ar9280InitPLL(struct ath_hal *ah, const struct ieee80211_channel *chan) static struct ath_hal * ar9280Attach(uint16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, + HAL_OPS_CONFIG *ah_config, HAL_STATUS *status) { struct ath_hal_9280 *ahp9280; @@ -153,13 +160,13 @@ ar9280Attach(uint16_t devid, HAL_SOFTC sc, int8_t pwr_table_offset; uint8_t pwr; - HALDEBUG_G(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n", + HALDEBUG(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n", __func__, sc, (void*) st, (void*) sh); /* NB: memory is returned zero'd */ ahp9280 = ath_hal_malloc(sizeof (struct ath_hal_9280)); if (ahp9280 == AH_NULL) { - HALDEBUG_G(AH_NULL, HAL_DEBUG_ANY, + HALDEBUG(AH_NULL, HAL_DEBUG_ANY, "%s: cannot allocate memory for state block\n", __func__); *status = HAL_ENOMEM; return AH_NULL; @@ -169,12 +176,25 @@ ar9280Attach(uint16_t devid, HAL_SOFTC sc, ar5416InitState(AH5416(ah), devid, sc, st, sh, status); + /* + * Use the "local" EEPROM data given to us by the higher layers. + * This is a private copy out of system flash. The Linux ath9k + * commit for the initial AR9130 support mentions MMIO flash + * access is "unreliable." -adrian + */ + if (eepromdata != AH_NULL) { + AH_PRIVATE((ah))->ah_eepromRead = ath_hal_EepromDataRead; + AH_PRIVATE((ah))->ah_eepromWrite = NULL; + ah->ah_eepromdata = eepromdata; + } + /* XXX override with 9280 specific state */ /* override 5416 methods for our needs */ AH5416(ah)->ah_initPLL = ar9280InitPLL; ah->ah_setAntennaSwitch = ar9280SetAntennaSwitch; ah->ah_configPCIE = ar9280ConfigPCIE; + ah->ah_disablePCIE = ar9280DisablePCIE; AH5416(ah)->ah_cal.iqCalData.calData = &ar9280_iq_cal; AH5416(ah)->ah_cal.adcGainCalData.calData = &ar9280_adc_gain_cal; @@ -403,14 +423,70 @@ bad: } static void -ar9280ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore) +ar9280ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore, HAL_BOOL power_off) { + uint32_t val; + if (AH_PRIVATE(ah)->ah_ispcie && !restore) { ath_hal_ini_write(ah, &AH5416(ah)->ah_ini_pcieserdes, 1, 0); OS_DELAY(1000); - OS_REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA); - OS_REG_WRITE(ah, AR_WA, AR9280_WA_DEFAULT); } + + + /* + * Set PCIe workaround bits + * + * NOTE: + * + * In Merlin and Kite, bit 14 in WA register (disable L1) should only + * be set when device enters D3 and be cleared when device comes back + * to D0. + */ + if (power_off) { /* Power-off */ + OS_REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA); + + val = OS_REG_READ(ah, AR_WA); + + /* + * Disable bit 6 and 7 before entering D3 to prevent + * system hang. + */ + val &= ~(AR_WA_BIT6 | AR_WA_BIT7); + + /* + * XXX Not sure, is specified in the reference HAL. + */ + val |= AR_WA_BIT22; + + /* + * See above: set AR_WA_D3_L1_DISABLE when entering D3 state. + * + * XXX The reference HAL does it this way - it only sets + * AR_WA_D3_L1_DISABLE if it's set in AR9280_WA_DEFAULT, + * which it (currently) isn't. So the following statement + * is currently a NOP. + */ + if (AR9280_WA_DEFAULT & AR_WA_D3_L1_DISABLE) + val |= AR_WA_D3_L1_DISABLE; + + OS_REG_WRITE(ah, AR_WA, val); + } else { /* Power-on */ + val = AR9280_WA_DEFAULT; + + /* + * See note above: make sure L1_DISABLE is not set. + */ + val &= (~AR_WA_D3_L1_DISABLE); + OS_REG_WRITE(ah, AR_WA, val); + + /* set bit 19 to allow forcing of pcie core into L1 state */ + OS_REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA); + } +} + +static void +ar9280DisablePCIE(struct ath_hal *ah) +{ } static void @@ -809,10 +885,6 @@ ar9280FillCapabilityInfo(struct ath_hal *ah) #if 0 pCap->halWowMatchPatternDword = AH_TRUE; #endif - /* AR9280 is a 2x2 stream device */ - pCap->halTxStreams = 2; - pCap->halRxStreams = 2; - pCap->halCSTSupport = AH_TRUE; pCap->halRifsRxSupport = AH_TRUE; pCap->halRifsTxSupport = AH_TRUE; @@ -829,6 +901,7 @@ ar9280FillCapabilityInfo(struct ath_hal *ah) pCap->halHasRxSelfLinkedTail = AH_FALSE; pCap->halMbssidAggrSupport = AH_TRUE; pCap->hal4AddrAggrSupport = AH_TRUE; + pCap->halSpectralScanSupport = AH_TRUE; if (AR_SREV_MERLIN_20(ah)) { pCap->halPSPollBroken = AH_FALSE; @@ -892,9 +965,12 @@ ar9280SetAntennaSwitch(struct ath_hal *ah, HAL_ANT_SETTING settings) static const char* ar9280Probe(uint16_t vendorid, uint16_t devid) { - if (vendorid == ATHEROS_VENDOR_ID && - (devid == AR9280_DEVID_PCI || devid == AR9280_DEVID_PCIE)) - return "Atheros 9280"; + if (vendorid == ATHEROS_VENDOR_ID) { + if (devid == AR9280_DEVID_PCI) + return "Atheros 9220"; + if (devid == AR9280_DEVID_PCIE) + return "Atheros 9280"; + } return AH_NULL; } AH_CHIP(AR9280, ar9280Probe, ar9280Attach); diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9280_olc.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9280_olc.c index 361930bf4b..45d7ddfa23 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9280_olc.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9280_olc.c @@ -22,7 +22,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9002/ar9280_olc.c 298939 2016-05-02 19:56:48Z pfg $ */ #include "opt_ah.h" @@ -328,7 +328,7 @@ ar9280SetPowerCalTable(struct ath_hal *ah, struct ar5416eeprom *pEepData, * uses this to calculate the PDADC delta during * calibration ; 0 here effectively stops the * temperature compensation calibration from - * occuring. + * occurring. */ AH5416(ah)->initPDADC = 0; } diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9280_olc.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9280_olc.h index e08639c0ca..1c9990ee25 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9280_olc.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9280_olc.h @@ -22,7 +22,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9002/ar9280_olc.h 219393 2011-03-08 06:59:59Z adrian $ */ #ifndef __ATH_AR9280_OLC_H__ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9280v1.ini b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9280v1.ini index 96e0f44149..bc7b048970 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9280v1.ini +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9280v1.ini @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9002/ar9280v1.ini 217631 2011-01-20 09:03:40Z adrian $ */ /* Auto Generated PCI Register Writes. Created: 10/12/07 */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9280v2.ini b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9280v2.ini index 60286ec0a5..66ee2e2749 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9280v2.ini +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9280v2.ini @@ -13,7 +13,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9002/ar9280v2.ini 217751 2011-01-23 14:30:35Z adrian $ */ static const uint32_t ar9280Modes_v2[][6] = { diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285.c index f1851ccc91..0ee02e71c5 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9002/ar9285.c 228517 2011-12-15 00:59:11Z adrian $ */ #include "opt_ah.h" @@ -77,3 +77,11 @@ ar9285RfAttach(struct ath_hal *ah, HAL_STATUS *status) return AH_TRUE; } + +static HAL_BOOL +ar9285RfProbe(struct ath_hal *ah) +{ + return (AR_SREV_KITE(ah)); +} + +AH_RF(RF9285, ar9285RfProbe, ar9285RfAttach); diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285.h index 3f6b48143c..ebbbddfd76 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285.h @@ -13,56 +13,19 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9002/ar9285.h 251655 2013-06-12 14:52:57Z adrian $ */ #ifndef _ATH_AR9285_H_ #define _ATH_AR9285_H_ #include "ar5416/ar5416.h" -enum ar9285_ant_div_comb_lna_conf { - ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2 = 0, - ATH_ANT_DIV_COMB_LNA2 = 1, - ATH_ANT_DIV_COMB_LNA1 = 2, - ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2 = 3, -}; - -struct ar9285_ant_comb { - uint16_t count; - uint16_t total_pkt_count; - HAL_BOOL scan; - HAL_BOOL scan_not_start; - int main_total_rssi; - int alt_total_rssi; - int alt_recv_cnt; - int main_recv_cnt; - int rssi_lna1; - int rssi_lna2; - int rssi_add; - int rssi_sub; - int rssi_first; - int rssi_second; - int rssi_third; - HAL_BOOL alt_good; - int quick_scan_cnt; - int main_conf; - enum ar9285_ant_div_comb_lna_conf first_quick_scan_conf; - enum ar9285_ant_div_comb_lna_conf second_quick_scan_conf; - int first_bias; - int second_bias; - HAL_BOOL first_ratio; - HAL_BOOL second_ratio; - unsigned long scan_start_time; -}; - struct ath_hal_9285 { struct ath_hal_5416 ah_5416; HAL_INI_ARRAY ah_ini_txgain; HAL_INI_ARRAY ah_ini_rxgain; - struct ar9285_ant_comb ant_comb; /* Kite Antenna comb/diversity */ - struct { int32_t prev_offset; /* Previous value of PA offset value */ int8_t max_skipcount; /* Max No. of times PACAL can be skipped */ @@ -78,7 +41,6 @@ struct ath_hal_9285 { #define AR_PHY_CCA_MIN_GOOD_VAL_9285_2GHZ -127 #define AR_PHY_CCA_MAX_GOOD_VAL_9285_2GHZ -108 -HAL_BOOL ar9285SetAntennaSwitch(struct ath_hal *, HAL_ANT_SETTING); HAL_BOOL ar9285RfAttach(struct ath_hal *, HAL_STATUS *); extern HAL_BOOL ar9285SetTransmitPower(struct ath_hal *, @@ -86,4 +48,9 @@ extern HAL_BOOL ar9285SetTransmitPower(struct ath_hal *, extern HAL_BOOL ar9285SetBoardValues(struct ath_hal *, const struct ieee80211_channel *); +/* ar9285_btcoex.h */ +extern void ar9285BTCoexAntennaDiversity(struct ath_hal *ah); +extern void ar9285BTCoexSetParameter(struct ath_hal *ah, + u_int32_t value, u_int32_t type); + #endif /* _ATH_AR9285_H_ */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285.ini b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285.ini index ef0ff2de89..e189472845 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285.ini +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285.ini @@ -13,7 +13,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9002/ar9285.ini 217631 2011-01-20 09:03:40Z adrian $ */ /* AR9285 Revsion 10 */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285_attach.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285_attach.c index 02c7b9892a..9a130a429c 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285_attach.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285_attach.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9002/ar9285_attach.c 272292 2014-09-30 03:19:29Z adrian $ */ #include "opt_ah.h" @@ -66,7 +66,9 @@ static const HAL_PERCAL_DATA ar9280_adc_init_dc_cal = { .calPostProc = ar5416AdcDcCalibration }; -static void ar9285ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore); +static void ar9285ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore, + HAL_BOOL power_off); +static void ar9285DisablePCIE(struct ath_hal *ah); static HAL_BOOL ar9285FillCapabilityInfo(struct ath_hal *ah); static void ar9285WriteIni(struct ath_hal *ah, const struct ieee80211_channel *chan); @@ -85,8 +87,8 @@ ar9285AniSetup(struct ath_hal *ah) .coarseHigh = { -14, -14, -14, -14, -12 }, .coarseLow = { -64, -64, -64, -64, -70 }, .firpwr = { -78, -78, -78, -78, -80 }, - .maxSpurImmunityLevel = 2, - .cycPwrThr1 = { 2, 4, 6 }, + .maxSpurImmunityLevel = 7, + .cycPwrThr1 = { 2, 4, 6, 8, 10, 12, 14, 16 }, .maxFirstepLevel = 2, /* levels 0..2 */ .firstep = { 0, 4, 8 }, .ofdmTrigHigh = 500, @@ -103,12 +105,35 @@ ar9285AniSetup(struct ath_hal *ah) ar5416AniAttach(ah, &aniparams, &aniparams, AH_TRUE); } +static const char * ar9285_lna_conf[] = { + "LNA1-LNA2", + "LNA2", + "LNA1", + "LNA1+LNA2", +}; + +static void +ar9285_eeprom_print_diversity_settings(struct ath_hal *ah) +{ + const HAL_EEPROM_v4k *ee = AH_PRIVATE(ah)->ah_eeprom; + const MODAL_EEP4K_HEADER *pModal = &ee->ee_base.modalHeader; + + ath_hal_printf(ah, "[ath] AR9285 Main LNA config: %s\n", + ar9285_lna_conf[(pModal->antdiv_ctl2 >> 2) & 0x3]); + ath_hal_printf(ah, "[ath] AR9285 Alt LNA config: %s\n", + ar9285_lna_conf[pModal->antdiv_ctl2 & 0x3]); + ath_hal_printf(ah, "[ath] LNA diversity %s, Diversity %s\n", + ((pModal->antdiv_ctl1 & 0x1) ? "enabled" : "disabled"), + ((pModal->antdiv_ctl1 & 0x8) ? "enabled" : "disabled")); +} + /* * Attach for an AR9285 part. */ static struct ath_hal * ar9285Attach(uint16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, + HAL_OPS_CONFIG *ah_config, HAL_STATUS *status) { struct ath_hal_9285 *ahp9285; @@ -118,13 +143,13 @@ ar9285Attach(uint16_t devid, HAL_SOFTC sc, HAL_STATUS ecode; HAL_BOOL rfStatus; - HALDEBUG_G(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n", + HALDEBUG(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n", __func__, sc, (void*) st, (void*) sh); /* NB: memory is returned zero'd */ ahp9285 = ath_hal_malloc(sizeof (struct ath_hal_9285)); if (ahp9285 == AH_NULL) { - HALDEBUG_G(AH_NULL, HAL_DEBUG_ANY, + HALDEBUG(AH_NULL, HAL_DEBUG_ANY, "%s: cannot allocate memory for state block\n", __func__); *status = HAL_ENOMEM; return AH_NULL; @@ -134,14 +159,30 @@ ar9285Attach(uint16_t devid, HAL_SOFTC sc, ar5416InitState(AH5416(ah), devid, sc, st, sh, status); - /* XXX override with 9285 specific state */ - /* override 5416 methods for our needs */ + /* + * Use the "local" EEPROM data given to us by the higher layers. + * This is a private copy out of system flash. The Linux ath9k + * commit for the initial AR9130 support mentions MMIO flash + * access is "unreliable." -adrian + */ + if (eepromdata != AH_NULL) { + AH_PRIVATE(ah)->ah_eepromRead = ath_hal_EepromDataRead; + AH_PRIVATE(ah)->ah_eepromWrite = NULL; + ah->ah_eepromdata = eepromdata; + } + + /* override with 9285 specific state */ AH5416(ah)->ah_initPLL = ar9280InitPLL; + AH5416(ah)->ah_btCoexSetDiversity = ar9285BTCoexAntennaDiversity; ah->ah_setAntennaSwitch = ar9285SetAntennaSwitch; ah->ah_configPCIE = ar9285ConfigPCIE; + ah->ah_disablePCIE = ar9285DisablePCIE; ah->ah_setTxPower = ar9285SetTransmitPower; ah->ah_setBoardValues = ar9285SetBoardValues; + ah->ah_btCoexSetParameter = ar9285BTCoexSetParameter; + ah->ah_divLnaConfGet = ar9285_antdiv_comb_conf_get; + ah->ah_divLnaConfSet = ar9285_antdiv_comb_conf_set; AH5416(ah)->ah_cal.iqCalData.calData = &ar9280_iq_cal; AH5416(ah)->ah_cal.adcGainCalData.calData = &ar9280_adc_gain_cal; @@ -293,10 +334,16 @@ ar9285Attach(uint16_t devid, HAL_SOFTC sc, goto bad; } + /* + * Print out the EEPROM antenna configuration mapping. + * Some devices have a hard-coded LNA configuration profile; + * others enable diversity. + */ + ar9285_eeprom_print_diversity_settings(ah); + /* Print out whether the EEPROM settings enable AR9285 diversity */ if (ar9285_check_div_comb(ah)) { ath_hal_printf(ah, "[ath] Enabling diversity for Kite\n"); - ah->ah_rxAntCombDiversity = ar9285_ant_comb_scan; } /* Disable 11n for the AR2427 */ @@ -350,14 +397,92 @@ bad: } static void -ar9285ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore) +ar9285ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore, HAL_BOOL power_off) { + uint32_t val; + + /* + * This workaround needs some integration work with the HAL + * config parameters and the if_ath_pci.c glue. + * Specifically, read the value of the PCI register 0x70c + * (4 byte PCI config space register) and store it in ath_hal_war70c. + * Then if it's non-zero, the below WAR would override register + * 0x570c upon suspend/resume. + */ +#if 0 + if (AR_SREV_9285E_20(ah)) { + val = AH_PRIVATE(ah)->ah_config.ath_hal_war70c; + if (val) { + val &= 0xffff00ff; + val |= 0x6f00; + OS_REG_WRITE(ah, 0x570c, val); + } + } +#endif + if (AH_PRIVATE(ah)->ah_ispcie && !restore) { ath_hal_ini_write(ah, &AH5416(ah)->ah_ini_pcieserdes, 1, 0); OS_DELAY(1000); - OS_REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA); - OS_REG_WRITE(ah, AR_WA, AR9285_WA_DEFAULT); } + + /* + * Set PCIe workaround bits + * + * NOTE: + * + * In Merlin and Kite, bit 14 in WA register (disable L1) should only + * be set when device enters D3 and be cleared when device comes back + * to D0. + */ + if (power_off) { /* Power-off */ + OS_REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA); + + val = OS_REG_READ(ah, AR_WA); + + /* + * Disable bit 6 and 7 before entering D3 to prevent + * system hang. + */ + val &= ~(AR_WA_BIT6 | AR_WA_BIT7); + + /* + * See above: set AR_WA_D3_L1_DISABLE when entering D3 state. + * + * XXX The reference HAL does it this way - it only sets + * AR_WA_D3_L1_DISABLE if it's set in AR9280_WA_DEFAULT, + * which it (currently) isn't. So the following statement + * is currently a NOP. + */ + if (AR9285_WA_DEFAULT & AR_WA_D3_L1_DISABLE) + val |= AR_WA_D3_L1_DISABLE; + + if (AR_SREV_9285E_20(ah)) + val |= AR_WA_BIT23; + + OS_REG_WRITE(ah, AR_WA, val); + } else { /* Power-on */ + val = AR9285_WA_DEFAULT; + /* + * See note above: make sure L1_DISABLE is not set. + */ + val &= (~AR_WA_D3_L1_DISABLE); + + /* Software workaroud for ASPM system hang. */ + val |= (AR_WA_BIT6 | AR_WA_BIT7); + + if (AR_SREV_9285E_20(ah)) + val |= AR_WA_BIT23; + + OS_REG_WRITE(ah, AR_WA, val); + + /* set bit 19 to allow forcing of pcie core into L1 state */ + OS_REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA); + } +} + +static void +ar9285DisablePCIE(struct ath_hal *ah) +{ } static void @@ -411,13 +536,16 @@ ar9285FillCapabilityInfo(struct ath_hal *ah) pCap->halTxStreams = 1; pCap->halRxStreams = 1; + if (ar9285_check_div_comb(ah)) + pCap->halAntDivCombSupport = AH_TRUE; + pCap->halCSTSupport = AH_TRUE; pCap->halRifsRxSupport = AH_TRUE; pCap->halRifsTxSupport = AH_TRUE; pCap->halRtsAggrLimit = 64*1024; /* 802.11n max */ pCap->halExtChanDfsSupport = AH_TRUE; pCap->halUseCombinedRadarRssi = AH_TRUE; -#if 0 +#if 1 /* XXX bluetooth */ pCap->halBtCoexSupport = AH_TRUE; #endif @@ -427,6 +555,8 @@ ar9285FillCapabilityInfo(struct ath_hal *ah) pCap->halHasRxSelfLinkedTail = AH_FALSE; pCap->halMbssidAggrSupport = AH_TRUE; pCap->hal4AddrAggrSupport = AH_TRUE; + pCap->halSpectralScanSupport = AH_TRUE; + pCap->halRxUsingLnaMixing = AH_TRUE; if (AR_SREV_KITE_12_OR_LATER(ah)) pCap->halPSPollBroken = AH_FALSE; diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285_btcoex.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285_btcoex.c new file mode 100644 index 0000000000..63b0e9a062 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285_btcoex.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2005 Atheros Communications, Inc. + * Copyright (c) 2008-2010, Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9002/ar9285_btcoex.c 251483 2013-06-07 05:17:58Z adrian $ + */ + +#include "opt_ah.h" + +#include "ah.h" +#include "ah_internal.h" +#include "ah_devid.h" +#ifdef AH_DEBUG +#include "ah_desc.h" /* NB: for HAL_PHYERR* */ +#endif + +#include "ar5416/ar5416.h" +#include "ar5416/ar5416reg.h" +#include "ar5416/ar5416phy.h" +#include "ar5416/ar5416desc.h" /* AR5416_CONTTXMODE */ + +#include "ar9002/ar9285phy.h" +#include "ar9002/ar9285.h" + +/* + * This is specific to Kite. + * + * Kiwi and others don't have antenna diversity like this. + */ +void +ar9285BTCoexAntennaDiversity(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + u_int32_t regVal; + u_int8_t ant_div_control1, ant_div_control2; + + HALDEBUG(ah, HAL_DEBUG_BT_COEX, + "%s: btCoexFlag: ALLOW=%d, ENABLE=%d\n", + __func__, + !! (ahp->ah_btCoexFlag & HAL_BT_COEX_FLAG_ANT_DIV_ALLOW), + !! (ahp->ah_btCoexFlag & HAL_BT_COEX_FLAG_ANT_DIV_ENABLE)); + + if ((ahp->ah_btCoexFlag & HAL_BT_COEX_FLAG_ANT_DIV_ALLOW) || + (AH5212(ah)->ah_diversity != HAL_ANT_VARIABLE)) { + if ((ahp->ah_btCoexFlag & HAL_BT_COEX_FLAG_ANT_DIV_ENABLE) && + (AH5212(ah)->ah_antControl == HAL_ANT_VARIABLE)) { + /* Enable antenna diversity */ + ant_div_control1 = HAL_BT_COEX_ANTDIV_CONTROL1_ENABLE; + ant_div_control2 = HAL_BT_COEX_ANTDIV_CONTROL2_ENABLE; + + /* Don't disable BT ant to allow BB to control SWCOM */ + ahp->ah_btCoexMode2 &= (~(AR_BT_DISABLE_BT_ANT)); + OS_REG_WRITE(ah, AR_BT_COEX_MODE2, ahp->ah_btCoexMode2); + + /* Program the correct SWCOM table */ + OS_REG_WRITE(ah, AR_PHY_SWITCH_COM, + HAL_BT_COEX_ANT_DIV_SWITCH_COM); + OS_REG_RMW(ah, AR_PHY_SWITCH_CHAIN_0, 0, 0xf0000000); + } else if (AH5212(ah)->ah_antControl == HAL_ANT_FIXED_B) { + /* Disable antenna diversity. Use antenna B(LNA2) only. */ + ant_div_control1 = HAL_BT_COEX_ANTDIV_CONTROL1_FIXED_B; + ant_div_control2 = HAL_BT_COEX_ANTDIV_CONTROL2_FIXED_B; + + /* Disable BT ant to allow concurrent BT and WLAN receive */ + ahp->ah_btCoexMode2 |= AR_BT_DISABLE_BT_ANT; + OS_REG_WRITE(ah, AR_BT_COEX_MODE2, ahp->ah_btCoexMode2); + + /* + * Program SWCOM table to make sure RF switch always parks + * at WLAN side + */ + OS_REG_WRITE(ah, AR_PHY_SWITCH_COM, + HAL_BT_COEX_ANT_DIV_SWITCH_COM); + OS_REG_RMW(ah, AR_PHY_SWITCH_CHAIN_0, 0x60000000, 0xf0000000); + } else { + /* Disable antenna diversity. Use antenna A(LNA1) only */ + ant_div_control1 = HAL_BT_COEX_ANTDIV_CONTROL1_FIXED_A; + ant_div_control2 = HAL_BT_COEX_ANTDIV_CONTROL2_FIXED_A; + + /* Disable BT ant to allow concurrent BT and WLAN receive */ + ahp->ah_btCoexMode2 |= AR_BT_DISABLE_BT_ANT; + OS_REG_WRITE(ah, AR_BT_COEX_MODE2, ahp->ah_btCoexMode2); + + /* + * Program SWCOM table to make sure RF switch always + * parks at BT side + */ + OS_REG_WRITE(ah, AR_PHY_SWITCH_COM, 0); + OS_REG_RMW(ah, AR_PHY_SWITCH_CHAIN_0, 0, 0xf0000000); + } + + regVal = OS_REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL); + regVal &= (~(AR_PHY_9285_ANT_DIV_CTL_ALL)); + /* + * Clear ant_fast_div_bias [14:9] since for Janus the main LNA is + * always LNA1. + */ + regVal &= (~(AR_PHY_9285_FAST_DIV_BIAS)); + + regVal |= SM(ant_div_control1, AR_PHY_9285_ANT_DIV_CTL); + regVal |= SM(ant_div_control2, AR_PHY_9285_ANT_DIV_ALT_LNACONF); + regVal |= SM((ant_div_control2 >> 2), AR_PHY_9285_ANT_DIV_MAIN_LNACONF); + regVal |= SM((ant_div_control1 >> 1), AR_PHY_9285_ANT_DIV_ALT_GAINTB); + regVal |= SM((ant_div_control1 >> 2), AR_PHY_9285_ANT_DIV_MAIN_GAINTB); + OS_REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regVal); + + regVal = OS_REG_READ(ah, AR_PHY_CCK_DETECT); + regVal &= (~AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV); + regVal |= SM((ant_div_control1 >> 3), + AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV); + OS_REG_WRITE(ah, AR_PHY_CCK_DETECT, regVal); + } +} + +void +ar9285BTCoexSetParameter(struct ath_hal *ah, u_int32_t type, u_int32_t value) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + switch (type) { + case HAL_BT_COEX_ANTENNA_DIVERSITY: + if (AR_SREV_KITE(ah)) { + ahp->ah_btCoexFlag |= HAL_BT_COEX_FLAG_ANT_DIV_ALLOW; + if (value) + ahp->ah_btCoexFlag |= + HAL_BT_COEX_FLAG_ANT_DIV_ENABLE; + else + ahp->ah_btCoexFlag &= + ~HAL_BT_COEX_FLAG_ANT_DIV_ENABLE; + ar9285BTCoexAntennaDiversity(ah); + } + break; + default: + ar5416BTCoexSetParameter(ah, type, value); + break; + } +} + + diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285_cal.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285_cal.c index a743e21ef1..f9fc291b1f 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285_cal.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285_cal.c @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9002/ar9285_cal.c 221806 2011-05-12 10:11:24Z adrian $ */ #include "opt_ah.h" #include "ah.h" diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285_cal.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285_cal.h index c6ecc4190d..af2e4e3573 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285_cal.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285_cal.h @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9002/ar9285_cal.h 219481 2011-03-11 11:58:54Z adrian $ */ #ifndef __AR9285_CAL_H__ #define __AR9285_CAL_H__ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285_diversity.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285_diversity.c index 84c7682aa3..1102cf3772 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285_diversity.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285_diversity.c @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9002/ar9285_diversity.c 251655 2013-06-12 14:52:57Z adrian $ */ #include "opt_ah.h" @@ -33,608 +33,13 @@ #include "ah_eeprom_v4k.h" #include "ar9002/ar9280.h" -#include "ar9002/ar9285_diversity.h" #include "ar9002/ar9285.h" #include "ar5416/ar5416reg.h" #include "ar5416/ar5416phy.h" #include "ar9002/ar9285phy.h" #include "ar9002/ar9285_phy.h" - -/* Linux compability macros */ -/* - * XXX these don't handle rounding, underflow, overflow, wrapping! - */ -#define msecs_to_jiffies(a) ( (a) * hz / 1000 ) -#define time_after(a, b) ( (long) (b) - (long) (a) < 0 ) - -static HAL_BOOL -ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta, int mindelta, - int main_rssi_avg, int alt_rssi_avg, int pkt_count) -{ - return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) && - (alt_rssi_avg > main_rssi_avg + maxdelta)) || - (alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50); -} - -static void -ath_lnaconf_alt_good_scan(struct ar9285_ant_comb *antcomb, - struct ar9285_antcomb_conf ant_conf, int main_rssi_avg) -{ - antcomb->quick_scan_cnt = 0; - - if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA2) - antcomb->rssi_lna2 = main_rssi_avg; - else if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA1) - antcomb->rssi_lna1 = main_rssi_avg; - - switch ((ant_conf.main_lna_conf << 4) | ant_conf.alt_lna_conf) { - case (0x10): /* LNA2 A-B */ - antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; - antcomb->first_quick_scan_conf = - ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; - antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1; - break; - case (0x20): /* LNA1 A-B */ - antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; - antcomb->first_quick_scan_conf = - ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; - antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2; - break; - case (0x21): /* LNA1 LNA2 */ - antcomb->main_conf = ATH_ANT_DIV_COMB_LNA2; - antcomb->first_quick_scan_conf = - ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; - antcomb->second_quick_scan_conf = - ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; - break; - case (0x12): /* LNA2 LNA1 */ - antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1; - antcomb->first_quick_scan_conf = - ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; - antcomb->second_quick_scan_conf = - ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; - break; - case (0x13): /* LNA2 A+B */ - antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; - antcomb->first_quick_scan_conf = - ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; - antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1; - break; - case (0x23): /* LNA1 A+B */ - antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; - antcomb->first_quick_scan_conf = - ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; - antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2; - break; - default: - break; - } -} - -static void -ath_select_ant_div_from_quick_scan(struct ar9285_ant_comb *antcomb, - struct ar9285_antcomb_conf *div_ant_conf, int main_rssi_avg, - int alt_rssi_avg, int alt_ratio) -{ - /* alt_good */ - switch (antcomb->quick_scan_cnt) { - case 0: - /* set alt to main, and alt to first conf */ - div_ant_conf->main_lna_conf = antcomb->main_conf; - div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf; - break; - case 1: - /* set alt to main, and alt to first conf */ - div_ant_conf->main_lna_conf = antcomb->main_conf; - div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf; - antcomb->rssi_first = main_rssi_avg; - antcomb->rssi_second = alt_rssi_avg; - - if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) { - /* main is LNA1 */ - if (ath_is_alt_ant_ratio_better(alt_ratio, - ATH_ANT_DIV_COMB_LNA1_DELTA_HI, - ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, - main_rssi_avg, alt_rssi_avg, - antcomb->total_pkt_count)) - antcomb->first_ratio = AH_TRUE; - else - antcomb->first_ratio = AH_FALSE; - } else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) { - if (ath_is_alt_ant_ratio_better(alt_ratio, - ATH_ANT_DIV_COMB_LNA1_DELTA_MID, - ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, - main_rssi_avg, alt_rssi_avg, - antcomb->total_pkt_count)) - antcomb->first_ratio = AH_TRUE; - else - antcomb->first_ratio = AH_FALSE; - } else { - if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) && - (alt_rssi_avg > main_rssi_avg + - ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) || - (alt_rssi_avg > main_rssi_avg)) && - (antcomb->total_pkt_count > 50)) - antcomb->first_ratio = AH_TRUE; - else - antcomb->first_ratio = AH_FALSE; - } - break; - case 2: - antcomb->alt_good = AH_FALSE; - antcomb->scan_not_start = AH_FALSE; - antcomb->scan = AH_FALSE; - antcomb->rssi_first = main_rssi_avg; - antcomb->rssi_third = alt_rssi_avg; - - if (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) - antcomb->rssi_lna1 = alt_rssi_avg; - else if (antcomb->second_quick_scan_conf == - ATH_ANT_DIV_COMB_LNA2) - antcomb->rssi_lna2 = alt_rssi_avg; - else if (antcomb->second_quick_scan_conf == - ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2) { - if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) - antcomb->rssi_lna2 = main_rssi_avg; - else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) - antcomb->rssi_lna1 = main_rssi_avg; - } - - if (antcomb->rssi_lna2 > antcomb->rssi_lna1 + - ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA) - div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2; - else - div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1; - - if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) { - if (ath_is_alt_ant_ratio_better(alt_ratio, - ATH_ANT_DIV_COMB_LNA1_DELTA_HI, - ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, - main_rssi_avg, alt_rssi_avg, - antcomb->total_pkt_count)) - antcomb->second_ratio = AH_TRUE; - else - antcomb->second_ratio = AH_FALSE; - } else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) { - if (ath_is_alt_ant_ratio_better(alt_ratio, - ATH_ANT_DIV_COMB_LNA1_DELTA_MID, - ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, - main_rssi_avg, alt_rssi_avg, - antcomb->total_pkt_count)) - antcomb->second_ratio = AH_TRUE; - else - antcomb->second_ratio = AH_FALSE; - } else { - if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) && - (alt_rssi_avg > main_rssi_avg + - ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) || - (alt_rssi_avg > main_rssi_avg)) && - (antcomb->total_pkt_count > 50)) - antcomb->second_ratio = AH_TRUE; - else - antcomb->second_ratio = AH_FALSE; - } - - /* set alt to the conf with maximun ratio */ - if (antcomb->first_ratio && antcomb->second_ratio) { - if (antcomb->rssi_second > antcomb->rssi_third) { - /* first alt*/ - if ((antcomb->first_quick_scan_conf == - ATH_ANT_DIV_COMB_LNA1) || - (antcomb->first_quick_scan_conf == - ATH_ANT_DIV_COMB_LNA2)) - /* Set alt LNA1 or LNA2*/ - if (div_ant_conf->main_lna_conf == - ATH_ANT_DIV_COMB_LNA2) - div_ant_conf->alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - else - div_ant_conf->alt_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - else - /* Set alt to A+B or A-B */ - div_ant_conf->alt_lna_conf = - antcomb->first_quick_scan_conf; - } else if ((antcomb->second_quick_scan_conf == - ATH_ANT_DIV_COMB_LNA1) || - (antcomb->second_quick_scan_conf == - ATH_ANT_DIV_COMB_LNA2)) { - /* Set alt LNA1 or LNA2 */ - if (div_ant_conf->main_lna_conf == - ATH_ANT_DIV_COMB_LNA2) - div_ant_conf->alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - else - div_ant_conf->alt_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - } else { - /* Set alt to A+B or A-B */ - div_ant_conf->alt_lna_conf = - antcomb->second_quick_scan_conf; - } - } else if (antcomb->first_ratio) { - /* first alt */ - if ((antcomb->first_quick_scan_conf == - ATH_ANT_DIV_COMB_LNA1) || - (antcomb->first_quick_scan_conf == - ATH_ANT_DIV_COMB_LNA2)) - /* Set alt LNA1 or LNA2 */ - if (div_ant_conf->main_lna_conf == - ATH_ANT_DIV_COMB_LNA2) - div_ant_conf->alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - else - div_ant_conf->alt_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - else - /* Set alt to A+B or A-B */ - div_ant_conf->alt_lna_conf = - antcomb->first_quick_scan_conf; - } else if (antcomb->second_ratio) { - /* second alt */ - if ((antcomb->second_quick_scan_conf == - ATH_ANT_DIV_COMB_LNA1) || - (antcomb->second_quick_scan_conf == - ATH_ANT_DIV_COMB_LNA2)) - /* Set alt LNA1 or LNA2 */ - if (div_ant_conf->main_lna_conf == - ATH_ANT_DIV_COMB_LNA2) - div_ant_conf->alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - else - div_ant_conf->alt_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - else - /* Set alt to A+B or A-B */ - div_ant_conf->alt_lna_conf = - antcomb->second_quick_scan_conf; - } else { - /* main is largest */ - if ((antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) || - (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2)) - /* Set alt LNA1 or LNA2 */ - if (div_ant_conf->main_lna_conf == - ATH_ANT_DIV_COMB_LNA2) - div_ant_conf->alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - else - div_ant_conf->alt_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - else - /* Set alt to A+B or A-B */ - div_ant_conf->alt_lna_conf = antcomb->main_conf; - } - break; - default: - break; - } -} - -static void -ath_ant_div_conf_fast_divbias(struct ar9285_antcomb_conf *ant_conf) -{ - /* Adjust the fast_div_bias based on main and alt lna conf */ - switch ((ant_conf->main_lna_conf << 4) | ant_conf->alt_lna_conf) { - case (0x01): /* A-B LNA2 */ - ant_conf->fast_div_bias = 0x3b; - break; - case (0x02): /* A-B LNA1 */ - ant_conf->fast_div_bias = 0x3d; - break; - case (0x03): /* A-B A+B */ - ant_conf->fast_div_bias = 0x1; - break; - case (0x10): /* LNA2 A-B */ - ant_conf->fast_div_bias = 0x7; - break; - case (0x12): /* LNA2 LNA1 */ - ant_conf->fast_div_bias = 0x2; - break; - case (0x13): /* LNA2 A+B */ - ant_conf->fast_div_bias = 0x7; - break; - case (0x20): /* LNA1 A-B */ - ant_conf->fast_div_bias = 0x6; - break; - case (0x21): /* LNA1 LNA2 */ - ant_conf->fast_div_bias = 0x0; - break; - case (0x23): /* LNA1 A+B */ - ant_conf->fast_div_bias = 0x6; - break; - case (0x30): /* A+B A-B */ - ant_conf->fast_div_bias = 0x1; - break; - case (0x31): /* A+B LNA2 */ - ant_conf->fast_div_bias = 0x3b; - break; - case (0x32): /* A+B LNA1 */ - ant_conf->fast_div_bias = 0x3d; - break; - default: - break; - } -} - -/* Antenna diversity and combining */ -void -ar9285_ant_comb_scan(struct ath_hal *ah, struct ath_rx_status *rs, - unsigned long ticks, int hz) -{ - struct ar9285_antcomb_conf div_ant_conf; - struct ar9285_ant_comb *antcomb = &AH9285(ah)->ant_comb; - int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set; - int curr_main_set, curr_bias; - int main_rssi = rs->rs_rssi_ctl[0]; - int alt_rssi = rs->rs_rssi_ctl[1]; - int rx_ant_conf, main_ant_conf, alt_ant_conf; - HAL_BOOL short_scan = AH_FALSE; - - rx_ant_conf = (rs->rs_rssi_ctl[2] >> 4) & ATH_ANT_RX_MASK; - main_ant_conf = (rs->rs_rssi_ctl[2] >> 2) & ATH_ANT_RX_MASK; - alt_ant_conf = (rs->rs_rssi_ctl[2] >> 0) & ATH_ANT_RX_MASK; - -#if 0 - HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: RSSI %d/%d, conf %x/%x, rxconf %x, LNA: %d; ANT: %d; FastDiv: %d\n", - __func__, main_rssi, alt_rssi, main_ant_conf,alt_ant_conf, rx_ant_conf, - !!(rs->rs_rssi_ctl[2] & 0x80), !!(rs->rs_rssi_ctl[2] & 0x40), !!(rs->rs_rssi_ext[2] & 0x40)); -#endif - - if (! ar9285_check_div_comb(ah)) - return; - - if (AH5212(ah)->ah_diversity == AH_FALSE) - return; - -#if 0 - HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: main: %d, alt: %d, rx_ant_conf: %x, main_ant_conf: %x\n", - __func__, main_rssi, alt_rssi, rx_ant_conf, main_ant_conf); -#endif - - /* Record packet only when alt_rssi is positive */ - if (main_rssi > 0 && alt_rssi > 0) { - antcomb->total_pkt_count++; - antcomb->main_total_rssi += main_rssi; - antcomb->alt_total_rssi += alt_rssi; - if (main_ant_conf == rx_ant_conf) - antcomb->main_recv_cnt++; - else - antcomb->alt_recv_cnt++; - } - - /* Short scan check */ - if (antcomb->scan && antcomb->alt_good) { - if (time_after(ticks, antcomb->scan_start_time + - msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR))) - short_scan = AH_TRUE; - else - if (antcomb->total_pkt_count == - ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) { - alt_ratio = ((antcomb->alt_recv_cnt * 100) / - antcomb->total_pkt_count); - if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO) - short_scan = AH_TRUE; - } - } - - if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) || - rs->rs_moreaggr) && !short_scan) - return; - - if (antcomb->total_pkt_count) { - alt_ratio = ((antcomb->alt_recv_cnt * 100) / - antcomb->total_pkt_count); - main_rssi_avg = (antcomb->main_total_rssi / - antcomb->total_pkt_count); - alt_rssi_avg = (antcomb->alt_total_rssi / - antcomb->total_pkt_count); - } - - ar9285_antdiv_comb_conf_get(ah, &div_ant_conf); - curr_alt_set = div_ant_conf.alt_lna_conf; - curr_main_set = div_ant_conf.main_lna_conf; - curr_bias = div_ant_conf.fast_div_bias; - - antcomb->count++; - - if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) { - if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) { - ath_lnaconf_alt_good_scan(antcomb, div_ant_conf, - main_rssi_avg); - antcomb->alt_good = AH_TRUE; - } else { - antcomb->alt_good = AH_FALSE; - } - - antcomb->count = 0; - antcomb->scan = AH_TRUE; - antcomb->scan_not_start = AH_TRUE; - } - - if (!antcomb->scan) { - if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) { - if (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) { - /* Switch main and alt LNA */ - div_ant_conf.main_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - } else if (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) { - div_ant_conf.main_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - } - - goto div_comb_done; - } else if ((curr_alt_set != ATH_ANT_DIV_COMB_LNA1) && - (curr_alt_set != ATH_ANT_DIV_COMB_LNA2)) { - /* Set alt to another LNA */ - if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - - goto div_comb_done; - } - - if ((alt_rssi_avg < (main_rssi_avg + - ATH_ANT_DIV_COMB_LNA1_LNA2_DELTA))) - goto div_comb_done; - } - - if (!antcomb->scan_not_start) { - switch (curr_alt_set) { - case ATH_ANT_DIV_COMB_LNA2: - antcomb->rssi_lna2 = alt_rssi_avg; - antcomb->rssi_lna1 = main_rssi_avg; - antcomb->scan = AH_TRUE; - /* set to A+B */ - div_ant_conf.main_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; - break; - case ATH_ANT_DIV_COMB_LNA1: - antcomb->rssi_lna1 = alt_rssi_avg; - antcomb->rssi_lna2 = main_rssi_avg; - antcomb->scan = AH_TRUE; - /* set to A+B */ - div_ant_conf.main_lna_conf = ATH_ANT_DIV_COMB_LNA2; - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; - break; - case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2: - antcomb->rssi_add = alt_rssi_avg; - antcomb->scan = AH_TRUE; - /* set to A-B */ - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; - break; - case ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2: - antcomb->rssi_sub = alt_rssi_avg; - antcomb->scan = AH_FALSE; - if (antcomb->rssi_lna2 > - (antcomb->rssi_lna1 + - ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) { - /* use LNA2 as main LNA */ - if ((antcomb->rssi_add > antcomb->rssi_lna1) && - (antcomb->rssi_add > antcomb->rssi_sub)) { - /* set to A+B */ - div_ant_conf.main_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; - } else if (antcomb->rssi_sub > - antcomb->rssi_lna1) { - /* set to A-B */ - div_ant_conf.main_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; - } else { - /* set to LNA1 */ - div_ant_conf.main_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - } - } else { - /* use LNA1 as main LNA */ - if ((antcomb->rssi_add > antcomb->rssi_lna2) && - (antcomb->rssi_add > antcomb->rssi_sub)) { - /* set to A+B */ - div_ant_conf.main_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; - } else if (antcomb->rssi_sub > - antcomb->rssi_lna1) { - /* set to A-B */ - div_ant_conf.main_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; - } else { - /* set to LNA2 */ - div_ant_conf.main_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - } - } - break; - default: - break; - } - } else { - if (!antcomb->alt_good) { - antcomb->scan_not_start = AH_FALSE; - /* Set alt to another LNA */ - if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) { - div_ant_conf.main_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - } else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) { - div_ant_conf.main_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - } - goto div_comb_done; - } - } - - ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf, - main_rssi_avg, alt_rssi_avg, - alt_ratio); - - antcomb->quick_scan_cnt++; - -div_comb_done: - ath_ant_div_conf_fast_divbias(&div_ant_conf); - - ar9285_antdiv_comb_conf_set(ah, &div_ant_conf); - - HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: total_pkt_count=%d\n", - __func__, antcomb->total_pkt_count); - - HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: main_total_rssi=%d\n", - __func__, antcomb->main_total_rssi); - HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: alt_total_rssi=%d\n", - __func__, antcomb->alt_total_rssi); - - HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: main_rssi_avg=%d\n", - __func__, main_rssi_avg); - HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: alt_alt_rssi_avg=%d\n", - __func__, alt_rssi_avg); - - HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: main_recv_cnt=%d\n", - __func__, antcomb->main_recv_cnt); - HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: alt_recv_cnt=%d\n", - __func__, antcomb->alt_recv_cnt); - -// if (curr_alt_set != div_ant_conf.alt_lna_conf) - HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: lna_conf: %x -> %x\n", - __func__, curr_alt_set, div_ant_conf.alt_lna_conf); -// if (curr_main_set != div_ant_conf.main_lna_conf) - HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: main_lna_conf: %x -> %x\n", - __func__, curr_main_set, div_ant_conf.main_lna_conf); -// if (curr_bias != div_ant_conf.fast_div_bias) - HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: fast_div_bias: %x -> %x\n", - __func__, curr_bias, div_ant_conf.fast_div_bias); - - antcomb->scan_start_time = ticks; - antcomb->total_pkt_count = 0; - antcomb->main_total_rssi = 0; - antcomb->alt_total_rssi = 0; - antcomb->main_recv_cnt = 0; - antcomb->alt_recv_cnt = 0; -} +#include "ar9002/ar9285_diversity.h" /* * Set the antenna switch to control RX antenna diversity. @@ -697,8 +102,8 @@ ar9285SetAntennaSwitch(struct ath_hal *ah, HAL_ANT_SETTING settings) /* Diversity disabled, RX = LNA1 */ HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: HAL_ANT_FIXED_A\n", __func__); - regVal |= SM(ATH_ANT_DIV_COMB_LNA2, AR_PHY_9285_ANT_DIV_ALT_LNACONF); - regVal |= SM(ATH_ANT_DIV_COMB_LNA1, AR_PHY_9285_ANT_DIV_MAIN_LNACONF); + regVal |= SM(HAL_ANT_DIV_COMB_LNA2, AR_PHY_9285_ANT_DIV_ALT_LNACONF); + regVal |= SM(HAL_ANT_DIV_COMB_LNA1, AR_PHY_9285_ANT_DIV_MAIN_LNACONF); regVal |= SM(AR_PHY_9285_ANT_DIV_GAINTB_0, AR_PHY_9285_ANT_DIV_ALT_GAINTB); regVal |= SM(AR_PHY_9285_ANT_DIV_GAINTB_1, AR_PHY_9285_ANT_DIV_MAIN_GAINTB); } @@ -706,8 +111,8 @@ ar9285SetAntennaSwitch(struct ath_hal *ah, HAL_ANT_SETTING settings) /* Diversity disabled, RX = LNA2 */ HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: HAL_ANT_FIXED_B\n", __func__); - regVal |= SM(ATH_ANT_DIV_COMB_LNA1, AR_PHY_9285_ANT_DIV_ALT_LNACONF); - regVal |= SM(ATH_ANT_DIV_COMB_LNA2, AR_PHY_9285_ANT_DIV_MAIN_LNACONF); + regVal |= SM(HAL_ANT_DIV_COMB_LNA1, AR_PHY_9285_ANT_DIV_ALT_LNACONF); + regVal |= SM(HAL_ANT_DIV_COMB_LNA2, AR_PHY_9285_ANT_DIV_MAIN_LNACONF); regVal |= SM(AR_PHY_9285_ANT_DIV_GAINTB_1, AR_PHY_9285_ANT_DIV_ALT_GAINTB); regVal |= SM(AR_PHY_9285_ANT_DIV_GAINTB_0, AR_PHY_9285_ANT_DIV_MAIN_GAINTB); } @@ -740,8 +145,8 @@ ar9285SetAntennaSwitch(struct ath_hal *ah, HAL_ANT_SETTING settings) __func__); regVal = OS_REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL); regVal &= (~(AR_PHY_9285_ANT_DIV_MAIN_LNACONF | AR_PHY_9285_ANT_DIV_ALT_LNACONF)); - regVal |= (ATH_ANT_DIV_COMB_LNA1 << AR_PHY_9285_ANT_DIV_MAIN_LNACONF_S); - regVal |= (ATH_ANT_DIV_COMB_LNA2 << AR_PHY_9285_ANT_DIV_ALT_LNACONF_S); + regVal |= (HAL_ANT_DIV_COMB_LNA1 << AR_PHY_9285_ANT_DIV_MAIN_LNACONF_S); + regVal |= (HAL_ANT_DIV_COMB_LNA2 << AR_PHY_9285_ANT_DIV_ALT_LNACONF_S); regVal &= (~(AR_PHY_9285_FAST_DIV_BIAS)); regVal |= (0 << AR_PHY_9285_FAST_DIV_BIAS_S); OS_REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regVal); diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285_diversity.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285_diversity.h index 220db1e812..2621acc1b7 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285_diversity.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285_diversity.h @@ -23,31 +23,11 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9002/ar9285_diversity.h 251655 2013-06-12 14:52:57Z adrian $ */ #ifndef __AR9285_DIVERSITY_H__ #define __AR9285_DIVERSITY_H__ -/* Antenna diversity/combining */ -#define ATH_ANT_RX_CURRENT_SHIFT 4 -#define ATH_ANT_RX_MAIN_SHIFT 2 -#define ATH_ANT_RX_MASK 0x3 - -#define ATH_ANT_DIV_COMB_SHORT_SCAN_INTR 50 -#define ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT 0x100 -#define ATH_ANT_DIV_COMB_MAX_PKTCOUNT 0x200 -#define ATH_ANT_DIV_COMB_INIT_COUNT 95 -#define ATH_ANT_DIV_COMB_MAX_COUNT 100 -#define ATH_ANT_DIV_COMB_ALT_ANT_RATIO 30 -#define ATH_ANT_DIV_COMB_ALT_ANT_RATIO2 20 - -#define ATH_ANT_DIV_COMB_LNA1_LNA2_DELTA -3 -#define ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA -1 -#define ATH_ANT_DIV_COMB_LNA1_DELTA_HI -4 -#define ATH_ANT_DIV_COMB_LNA1_DELTA_MID -2 -#define ATH_ANT_DIV_COMB_LNA1_DELTA_LOW 2 - -extern void ar9285_ant_comb_scan(struct ath_hal *ah, struct ath_rx_status *rs, - unsigned long ticks, int hz); +extern HAL_BOOL ar9285SetAntennaSwitch(struct ath_hal *, HAL_ANT_SETTING); #endif diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285_phy.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285_phy.c index e4e73d47f0..841c4f5aba 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285_phy.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285_phy.c @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9002/ar9285_phy.c 251656 2013-06-12 15:18:10Z adrian $ */ #include "opt_ah.h" @@ -40,8 +40,7 @@ #include "ar9002/ar9285_phy.h" void -ar9285_antdiv_comb_conf_get(struct ath_hal *ah, - struct ar9285_antcomb_conf *antconf) +ar9285_antdiv_comb_conf_get(struct ath_hal *ah, HAL_ANT_COMB_CONFIG *antconf) { uint32_t regval; @@ -52,11 +51,11 @@ ar9285_antdiv_comb_conf_get(struct ath_hal *ah, AR_PHY_9285_ANT_DIV_ALT_LNACONF_S; antconf->fast_div_bias = (regval & AR_PHY_9285_FAST_DIV_BIAS) >> AR_PHY_9285_FAST_DIV_BIAS_S; + antconf->antdiv_configgroup = DEFAULT_ANTDIV_CONFIG_GROUP; } void -ar9285_antdiv_comb_conf_set(struct ath_hal *ah, - struct ar9285_antcomb_conf *antconf) +ar9285_antdiv_comb_conf_set(struct ath_hal *ah, HAL_ANT_COMB_CONFIG *antconf) { uint32_t regval; @@ -87,8 +86,10 @@ ar9285_check_div_comb(struct ath_hal *ah) HAL_EEPROM_v4k *ee = AH_PRIVATE(ah)->ah_eeprom; const MODAL_EEP4K_HEADER *pModal = &ee->ee_base.modalHeader; +#if 0 /* For now, simply disable this until it's better debugged. -adrian */ return AH_FALSE; +#endif if (! AR_SREV_KITE(ah)) return AH_FALSE; diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285_phy.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285_phy.h index b2367185f1..c953943eca 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285_phy.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285_phy.h @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9002/ar9285_phy.h 251643 2013-06-12 06:01:53Z adrian $ */ #ifndef __AR9285_PHY_H__ #define __AR9285_PHY_H__ @@ -31,16 +31,11 @@ /* * Manipulate AR9285 antenna diversity configuration */ -struct ar9285_antcomb_conf { - uint8_t main_lna_conf; - uint8_t alt_lna_conf; - uint8_t fast_div_bias; -}; extern void ar9285_antdiv_comb_conf_set(struct ath_hal *ah, - struct ar9285_antcomb_conf *antconf); + HAL_ANT_COMB_CONFIG *antconf); extern void ar9285_antdiv_comb_conf_get(struct ath_hal *ah, - struct ar9285_antcomb_conf *antconf); + HAL_ANT_COMB_CONFIG *antconf); extern HAL_BOOL ar9285_check_div_comb(struct ath_hal *ah); #endif diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285_reset.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285_reset.c index 40bdaf8e85..19a9f58a9d 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285_reset.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285_reset.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9002/ar9285_reset.c 251655 2013-06-12 14:52:57Z adrian $ */ /* @@ -38,8 +38,9 @@ #include "ar9002/ar9002phy.h" #include "ar9002/ar9285phy.h" #include "ar9002/ar9285an.h" +#include "ar9002/ar9285_diversity.h" -/* Eeprom versioning macros. Returns true if the version is equal or newer than the ver specified */ +/* Eeprom versioning macros. Returns true if the version is equal or newer than the ver specified */ #define EEP_MINOR(_ah) \ (AH_PRIVATE(_ah)->ah_eeversion & AR5416_EEP_VER_MINOR_MASK) #define IS_EEP_MINOR_V2(_ah) (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_2) @@ -51,16 +52,16 @@ #define RTC_PLL_SETTLE_DELAY 1000 /* 1 ms */ static HAL_BOOL ar9285SetPowerPerRateTable(struct ath_hal *ah, - struct ar5416eeprom_4k *pEepData, + struct ar5416eeprom_4k *pEepData, const struct ieee80211_channel *chan, int16_t *ratesArray, uint16_t cfgCtl, uint16_t AntennaReduction, - uint16_t twiceMaxRegulatoryPower, + uint16_t twiceMaxRegulatoryPower, uint16_t powerLimit); static HAL_BOOL ar9285SetPowerCalTable(struct ath_hal *ah, struct ar5416eeprom_4k *pEepData, const struct ieee80211_channel *chan, int16_t *pTxPowerIndexOffset); -static void ar9285GetGainBoundariesAndPdadcs(struct ath_hal *ah, +static void ar9285GetGainBoundariesAndPdadcs(struct ath_hal *ah, const struct ieee80211_channel *chan, CAL_DATA_PER_FREQ_4K *pRawDataSet, uint8_t * bChans, uint16_t availPiers, uint16_t tPdGainOverlap, int16_t *pMinCalPower, @@ -76,11 +77,9 @@ ar9285SetTransmitPower(struct ath_hal *ah, MODAL_EEP4K_HEADER *pModal; struct ath_hal_5212 *ahp = AH5212(ah); - int16_t ratesArray[Ar5416RateSize]; int16_t txPowerIndexOffset = 0; - uint8_t ht40PowerIncForPdadc = 2; int i; - + uint16_t cfgCtl; uint16_t powerLimit; uint16_t twiceAntennaReduction; @@ -91,22 +90,24 @@ ar9285SetTransmitPower(struct ath_hal *ah, HALASSERT(AH_PRIVATE(ah)->ah_eeversion >= AR_EEPROM_VER14_1); + AH5416(ah)->ah_ht40PowerIncForPdadc = 2; + /* Setup info for the actual eeprom */ - OS_MEMZERO(ratesArray, sizeof(ratesArray)); + OS_MEMZERO(AH5416(ah)->ah_ratesArray, sizeof(AH5416(ah)->ah_ratesArray)); cfgCtl = ath_hal_getctl(ah, chan); powerLimit = chan->ic_maxregpower * 2; twiceAntennaReduction = chan->ic_maxantgain; - twiceMaxRegulatoryPower = AH_MIN(MAX_RATE_POWER, AH_PRIVATE(ah)->ah_powerLimit); + twiceMaxRegulatoryPower = AH_MIN(MAX_RATE_POWER, AH_PRIVATE(ah)->ah_powerLimit); pModal = &pEepData->modalHeader; HALDEBUG(ah, HAL_DEBUG_RESET, "%s Channel=%u CfgCtl=%u\n", - __func__,chan->ic_freq, cfgCtl ); - + __func__,chan->ic_freq, cfgCtl ); + if (IS_EEP_MINOR_V2(ah)) { - ht40PowerIncForPdadc = pModal->ht40PowerIncForPdadc; + AH5416(ah)->ah_ht40PowerIncForPdadc = pModal->ht40PowerIncForPdadc; } - + if (!ar9285SetPowerPerRateTable(ah, pEepData, chan, - &ratesArray[0],cfgCtl, + &AH5416(ah)->ah_ratesArray[0],cfgCtl, twiceAntennaReduction, twiceMaxRegulatoryPower, powerLimit)) { HALDEBUG(ah, HAL_DEBUG_ANY, @@ -119,15 +120,16 @@ ar9285SetTransmitPower(struct ath_hal *ah, __func__); return AH_FALSE; } - - maxPower = AH_MAX(ratesArray[rate6mb], ratesArray[rateHt20_0]); - maxPower = AH_MAX(maxPower, ratesArray[rate1l]); + + maxPower = AH_MAX(AH5416(ah)->ah_ratesArray[rate6mb], + AH5416(ah)->ah_ratesArray[rateHt20_0]); + maxPower = AH_MAX(maxPower, AH5416(ah)->ah_ratesArray[rate1l]); if (IEEE80211_IS_CHAN_HT40(chan)) { - maxPower = AH_MAX(maxPower, ratesArray[rateHt40_0]); + maxPower = AH_MAX(maxPower, AH5416(ah)->ah_ratesArray[rateHt40_0]); } - ahp->ah_tx6PowerInHalfDbm = maxPower; + ahp->ah_tx6PowerInHalfDbm = maxPower; AH_PRIVATE(ah)->ah_maxPowerLevel = maxPower; ahp->ah_txPowerIndexOffset = txPowerIndexOffset; @@ -135,18 +137,18 @@ ar9285SetTransmitPower(struct ath_hal *ah, * txPowerIndexOffset is set by the SetPowerTable() call - * adjust the rate table (0 offset if rates EEPROM not loaded) */ - for (i = 0; i < N(ratesArray); i++) { - ratesArray[i] = (int16_t)(txPowerIndexOffset + ratesArray[i]); + for (i = 0; i < N(AH5416(ah)->ah_ratesArray); i++) { + AH5416(ah)->ah_ratesArray[i] = (int16_t)(txPowerIndexOffset + AH5416(ah)->ah_ratesArray[i]); /* -5 dBm offset for Merlin and later; this includes Kite */ - ratesArray[i] -= AR5416_PWR_TABLE_OFFSET_DB * 2; - if (ratesArray[i] > AR5416_MAX_RATE_POWER) - ratesArray[i] = AR5416_MAX_RATE_POWER; - if (ratesArray[i] < 0) - ratesArray[i] = 0; + AH5416(ah)->ah_ratesArray[i] -= AR5416_PWR_TABLE_OFFSET_DB * 2; + if (AH5416(ah)->ah_ratesArray[i] > AR5416_MAX_RATE_POWER) + AH5416(ah)->ah_ratesArray[i] = AR5416_MAX_RATE_POWER; + if (AH5416(ah)->ah_ratesArray[i] < 0) + AH5416(ah)->ah_ratesArray[i] = 0; } #ifdef AH_EEPROM_DUMP - ar5416PrintPowerPerRate(ah, ratesArray); + ar5416PrintPowerPerRate(ah, AH5416(ah)->ah_ratesArray); #endif /* @@ -157,18 +159,26 @@ ar9285SetTransmitPower(struct ath_hal *ah, * XXX handle overflow/too high power level? */ if (IEEE80211_IS_CHAN_HT40(chan)) { - ratesArray[rateHt40_0] += ht40PowerIncForPdadc; - ratesArray[rateHt40_1] += ht40PowerIncForPdadc; - ratesArray[rateHt40_2] += ht40PowerIncForPdadc; - ratesArray[rateHt40_3] += ht40PowerIncForPdadc; - ratesArray[rateHt40_4] += ht40PowerIncForPdadc; - ratesArray[rateHt40_5] += ht40PowerIncForPdadc; - ratesArray[rateHt40_6] += ht40PowerIncForPdadc; - ratesArray[rateHt40_7] += ht40PowerIncForPdadc; + AH5416(ah)->ah_ratesArray[rateHt40_0] += + AH5416(ah)->ah_ht40PowerIncForPdadc; + AH5416(ah)->ah_ratesArray[rateHt40_1] += + AH5416(ah)->ah_ht40PowerIncForPdadc; + AH5416(ah)->ah_ratesArray[rateHt40_2] += + AH5416(ah)->ah_ht40PowerIncForPdadc; + AH5416(ah)->ah_ratesArray[rateHt40_3] += + AH5416(ah)->ah_ht40PowerIncForPdadc; + AH5416(ah)->ah_ratesArray[rateHt40_4] += + AH5416(ah)->ah_ht40PowerIncForPdadc; + AH5416(ah)->ah_ratesArray[rateHt40_5] += + AH5416(ah)->ah_ht40PowerIncForPdadc; + AH5416(ah)->ah_ratesArray[rateHt40_6] += + AH5416(ah)->ah_ht40PowerIncForPdadc; + AH5416(ah)->ah_ratesArray[rateHt40_7] += + AH5416(ah)->ah_ht40PowerIncForPdadc; } /* Write the TX power rate registers */ - ar5416WriteTxPowerRateRegisters(ah, chan, ratesArray); + ar5416WriteTxPowerRateRegisters(ah, chan, AH5416(ah)->ah_ratesArray); return AH_TRUE; #undef POW_SM @@ -352,13 +362,8 @@ ar9285SetBoardValues(struct ath_hal *ah, const struct ieee80211_channel *chan) if (AR_SREV_9271(ah) || AR_SREV_KITE(ah)) { uint8_t bb_desired_scale = (pModal->bb_scale_smrt_antenna & EEP_4K_BB_DESIRED_SCALE_MASK); if ((eep->baseEepHeader.txGainType == 0) && (bb_desired_scale != 0)) { -#ifndef __HAIKU__ - ath_hal_printf(ah, "[ath]: adjusting cck tx gain factor\n"); -#endif uint32_t pwrctrl, mask, clr; -#ifdef __HAIKU__ ath_hal_printf(ah, "[ath]: adjusting cck tx gain factor\n"); -#endif mask = (1<<0) | (1<<5) | (1<<10) | (1<<15) | (1<<20) | (1<<25); pwrctrl = mask * bb_desired_scale; @@ -391,7 +396,7 @@ static HAL_BOOL ar9285SetPowerPerRateTable(struct ath_hal *ah, struct ar5416eeprom_4k *pEepData, const struct ieee80211_channel *chan, int16_t *ratesArray, uint16_t cfgCtl, - uint16_t AntennaReduction, + uint16_t AntennaReduction, uint16_t twiceMaxRegulatoryPower, uint16_t powerLimit) { @@ -428,7 +433,7 @@ ar9285SetPowerPerRateTable(struct ath_hal *ah, struct ar5416eeprom_4k *pEepData, /* XXX setup for 5212 use (really used?) */ ath_hal_eepromSet(ah, AR_EEP_ANTGAINMAX_2, twiceLargestAntenna); - /* + /* * scaledPower is the minimum of the user input power level and * the regulatory allowed power level */ @@ -484,7 +489,7 @@ ar9285SetPowerPerRateTable(struct ath_hal *ah, struct ar5416eeprom_4k *pEepData, /* compare test group from regulatory channel list with test mode from pCtlMode list */ if ((((cfgCtl & ~CTL_MODE_M) | (pCtlMode[ctlMode] & CTL_MODE_M)) == pEepData->ctlIndex[i]) || - (((cfgCtl & ~CTL_MODE_M) | (pCtlMode[ctlMode] & CTL_MODE_M)) == + (((cfgCtl & ~CTL_MODE_M) | (pCtlMode[ctlMode] & CTL_MODE_M)) == ((pEepData->ctlIndex[i] & CTL_MODE_M) | SD_NO_CTL))) { rep = &(pEepData->ctlData[i]); twiceMinEdgePower = ar5416GetMaxEdgePower(freq, @@ -571,12 +576,12 @@ ar9285SetPowerCalTable(struct ath_hal *ah, struct ar5416eeprom_4k *pEepData, uint32_t regChainOffset; OS_MEMZERO(xpdGainValues, sizeof(xpdGainValues)); - + xpdMask = pEepData->modalHeader.xpdGain; if (IS_EEP_MINOR_V2(ah)) { pdGainOverlap_t2 = pEepData->modalHeader.pdGainOverlap; - } else { + } else { pdGainOverlap_t2 = (uint16_t)(MS(OS_REG_READ(ah, AR_PHY_TPCRG5), AR_PHY_TPCRG5_PD_GAIN_OVERLAP)); } @@ -595,7 +600,7 @@ ar9285SetPowerCalTable(struct ath_hal *ah, struct ar5416eeprom_4k *pEepData, numXpdGain++; } } - + /* Write the detector gain biases and their number */ ar5416WriteDetectorGainBiases(ah, numXpdGain, xpdGainValues); @@ -616,7 +621,7 @@ ar9285SetPowerCalTable(struct ath_hal *ah, struct ar5416eeprom_4k *pEepData, * negative or greater than 0. Need to offset the power * values by the amount of minPower for griffin */ - ar5416SetGainBoundariesClosedLoop(ah, i, pdGainOverlap_t2, gainBoundaries); + ar5416SetGainBoundariesClosedLoop(ah, i, pdGainOverlap_t2, gainBoundaries); } /* Write the power values into the baseband power table */ @@ -629,7 +634,7 @@ ar9285SetPowerCalTable(struct ath_hal *ah, struct ar5416eeprom_4k *pEepData, } static void -ar9285GetGainBoundariesAndPdadcs(struct ath_hal *ah, +ar9285GetGainBoundariesAndPdadcs(struct ath_hal *ah, const struct ieee80211_channel *chan, CAL_DATA_PER_FREQ_4K *pRawDataSet, uint8_t * bChans, uint16_t availPiers, diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285an.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285an.h index 3b80938e3a..0276c039de 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285an.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285an.h @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9002/ar9285an.h 221806 2011-05-12 10:11:24Z adrian $ */ #ifndef __AR9285_AN_H__ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285phy.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285phy.h index f823053652..fca1971179 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285phy.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285phy.h @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9002/ar9285phy.h 219481 2011-03-11 11:58:54Z adrian $ */ #ifndef __ATH_AR9285PHY_H__ #define __ATH_AR9285PHY_H__ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285v2.ini b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285v2.ini index 9126dc4fc8..06a7983b0f 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285v2.ini +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9285v2.ini @@ -13,7 +13,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9002/ar9285v2.ini 217811 2011-01-25 05:36:29Z adrian $ */ static const u_int32_t ar9285Modes_v2[][6] = { diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287.c index 9b874b36bd..a174a4c5c3 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9002/ar9287.c 228517 2011-12-15 00:59:11Z adrian $ */ #include "opt_ah.h" @@ -390,3 +390,11 @@ ar9287RfAttach(struct ath_hal *ah, HAL_STATUS *status) return AH_TRUE; } + +static HAL_BOOL +ar9287RfProbe(struct ath_hal *ah) +{ + return (AR_SREV_KIWI(ah)); +} + +AH_RF(RF9287, ar9287RfProbe, ar9287RfAttach); diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287.h index 90d25ed28f..273a61ddb5 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287.h @@ -13,7 +13,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9002/ar9287.h 228833 2011-12-23 04:05:39Z adrian $ */ #ifndef _ATH_AR9287_H_ @@ -45,9 +45,7 @@ struct ath_hal_9287 { #define AH9287(_ah) ((struct ath_hal_9287 *)(_ah)) #define AR9287_DEFAULT_RXCHAINMASK 3 -#define AR9285_DEFAULT_RXCHAINMASK 1 #define AR9287_DEFAULT_TXCHAINMASK 3 -#define AR9285_DEFAULT_TXCHAINMASK 1 #define AR_PHY_CCA_NOM_VAL_9287_2GHZ -112 #define AR_PHY_CCA_NOM_VAL_9287_5GHZ -112 diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287.ini b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287.ini index 7f4ca05190..25384b9f44 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287.ini +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287.ini @@ -13,7 +13,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9002/ar9287.ini 222301 2011-05-26 09:15:33Z adrian $ */ static const uint32_t ar9287Modes_9287_1_1[][6] = { diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287_attach.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287_attach.c index 78400f52f9..59323ddb72 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287_attach.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287_attach.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9002/ar9287_attach.c 272292 2014-09-30 03:19:29Z adrian $ */ #include "opt_ah.h" @@ -65,7 +65,9 @@ static const HAL_PERCAL_DATA ar9287_adc_init_dc_cal = { .calPostProc = ar5416AdcDcCalibration }; -static void ar9287ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore); +static void ar9287ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore, + HAL_BOOL power_off); +static void ar9287DisablePCIE(struct ath_hal *ah); static HAL_BOOL ar9287FillCapabilityInfo(struct ath_hal *ah); static void ar9287WriteIni(struct ath_hal *ah, const struct ieee80211_channel *chan); @@ -76,7 +78,7 @@ ar9287AniSetup(struct ath_hal *ah) /* * These are the parameters from the AR5416 ANI code; * they likely need quite a bit of adjustment for the - * AR9280. + * AR9287. */ static const struct ar5212AniParams aniparams = { .maxNoiseImmunityLevel = 4, /* levels 0..4 */ @@ -84,8 +86,8 @@ ar9287AniSetup(struct ath_hal *ah) .coarseHigh = { -14, -14, -14, -14, -12 }, .coarseLow = { -64, -64, -64, -64, -70 }, .firpwr = { -78, -78, -78, -78, -80 }, - .maxSpurImmunityLevel = 2, - .cycPwrThr1 = { 2, 4, 6 }, + .maxSpurImmunityLevel = 7, + .cycPwrThr1 = { 2, 4, 6, 8, 10, 12, 14, 16 }, .maxFirstepLevel = 2, /* levels 0..2 */ .firstep = { 0, 4, 8 }, .ofdmTrigHigh = 500, @@ -109,6 +111,7 @@ ar9287AniSetup(struct ath_hal *ah) static struct ath_hal * ar9287Attach(uint16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, + HAL_OPS_CONFIG *ah_config, HAL_STATUS *status) { struct ath_hal_9287 *ahp9287; @@ -119,13 +122,13 @@ ar9287Attach(uint16_t devid, HAL_SOFTC sc, HAL_BOOL rfStatus; int8_t pwr_table_offset; - HALDEBUG_G(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n", + HALDEBUG(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n", __func__, sc, (void*) st, (void*) sh); /* NB: memory is returned zero'd */ ahp9287 = ath_hal_malloc(sizeof (struct ath_hal_9287)); if (ahp9287 == AH_NULL) { - HALDEBUG_G(AH_NULL, HAL_DEBUG_ANY, + HALDEBUG(AH_NULL, HAL_DEBUG_ANY, "%s: cannot allocate memory for state block\n", __func__); *status = HAL_ENOMEM; return AH_NULL; @@ -135,12 +138,20 @@ ar9287Attach(uint16_t devid, HAL_SOFTC sc, ar5416InitState(AH5416(ah), devid, sc, st, sh, status); + if (eepromdata != AH_NULL) { + AH_PRIVATE(ah)->ah_eepromRead = ath_hal_EepromDataRead; + AH_PRIVATE(ah)->ah_eepromWrite = NULL; + ah->ah_eepromdata = eepromdata; + } + + /* XXX override with 9280 specific state */ /* override 5416 methods for our needs */ AH5416(ah)->ah_initPLL = ar9280InitPLL; ah->ah_setAntennaSwitch = ar9287SetAntennaSwitch; ah->ah_configPCIE = ar9287ConfigPCIE; + ah->ah_disablePCIE = ar9287DisablePCIE; AH5416(ah)->ah_cal.iqCalData.calData = &ar9287_iq_cal; AH5416(ah)->ah_cal.adcGainCalData.calData = &ar9287_adc_gain_cal; @@ -357,16 +368,23 @@ bad: } static void -ar9287ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore) +ar9287ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore, HAL_BOOL power_off) { if (AH_PRIVATE(ah)->ah_ispcie && !restore) { ath_hal_ini_write(ah, &AH5416(ah)->ah_ini_pcieserdes, 1, 0); OS_DELAY(1000); OS_REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA); - OS_REG_WRITE(ah, AR_WA, AR9285_WA_DEFAULT); /* Yes, Kiwi uses the Kite PCIe PHY WA */ + /* Yes, Kiwi uses the Kite PCIe PHY WA */ + OS_REG_WRITE(ah, AR_WA, AR9285_WA_DEFAULT); } } +static void +ar9287DisablePCIE(struct ath_hal *ah) +{ + /* XXX TODO */ +} + static void ar9287WriteIni(struct ath_hal *ah, const struct ieee80211_channel *chan) { @@ -402,13 +420,6 @@ ar9287WriteIni(struct ath_hal *ah, const struct ieee80211_channel *chan) regWrites = ath_hal_ini_write(ah, &AH5212(ah)->ah_ini_common, 1, regWrites); } -#define AR_BASE_FREQ_2GHZ 2300 -#define AR_BASE_FREQ_5GHZ 4900 -#define AR_SPUR_FEEQ_BOUND_HT40 19 -#define AR_SPUR_FEEQ_BOUND_HT20 10 - - - /* * Fill all software cached or static hardware state information. * Return failure if capabilities are to come from EEPROM and @@ -443,6 +454,7 @@ ar9287FillCapabilityInfo(struct ath_hal *ah) /* Disable this so Block-ACK works correctly */ pCap->halHasRxSelfLinkedTail = AH_FALSE; pCap->halPSPollBroken = AH_FALSE; + pCap->halSpectralScanSupport = AH_TRUE; /* Hardware supports (at least) single-stream STBC TX/RX */ pCap->halRxStbcSupport = 1; @@ -460,7 +472,7 @@ ar9287FillCapabilityInfo(struct ath_hal *ah) * This has been disabled - having the HAL flip chainmasks on/off * when attempting to implement 11n disrupts things. For now, just * leave this flipped off and worry about implementing TX diversity - * for legacy and MCS0-7 when 11n is fully functioning. + * for legacy and MCS0-15 when 11n is fully functioning. */ HAL_BOOL ar9287SetAntennaSwitch(struct ath_hal *ah, HAL_ANT_SETTING settings) @@ -471,9 +483,12 @@ ar9287SetAntennaSwitch(struct ath_hal *ah, HAL_ANT_SETTING settings) static const char* ar9287Probe(uint16_t vendorid, uint16_t devid) { - if (vendorid == ATHEROS_VENDOR_ID && - (devid == AR9287_DEVID_PCI || devid == AR9287_DEVID_PCIE)) - return "Atheros 9287"; + if (vendorid == ATHEROS_VENDOR_ID) { + if (devid == AR9287_DEVID_PCI) + return "Atheros 9227"; + if (devid == AR9287_DEVID_PCIE) + return "Atheros 9287"; + } return AH_NULL; } AH_CHIP(AR9287, ar9287Probe, ar9287Attach); diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287_cal.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287_cal.c index d5024b0623..e9f9a74028 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287_cal.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287_cal.c @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9002/ar9287_cal.c 222301 2011-05-26 09:15:33Z adrian $ */ #include "opt_ah.h" #include "ah.h" diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287_cal.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287_cal.h index 1a7cda2559..99c1f05edd 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287_cal.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287_cal.h @@ -22,7 +22,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9002/ar9287_cal.h 222301 2011-05-26 09:15:33Z adrian $ */ #ifndef __AR9287_CAL_H__ #define __AR9287_CAL_H__ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287_olc.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287_olc.c index cbbe017667..482d816e46 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287_olc.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287_olc.c @@ -22,7 +22,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9002/ar9287_olc.c 222314 2011-05-26 16:52:37Z adrian $ */ #include "opt_ah.h" diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287_olc.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287_olc.h index ff21ce6da5..222d3a6c8d 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287_olc.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287_olc.h @@ -13,7 +13,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9002/ar9287_olc.h 222308 2011-05-26 14:29:05Z adrian $ */ #ifndef __AR9287_OLC_H__ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287_reset.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287_reset.c index a799ba0da9..57f3d740ee 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287_reset.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287_reset.c @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9002/ar9287_reset.c 249580 2013-04-17 07:31:53Z adrian $ */ #include "opt_ah.h" @@ -45,70 +45,70 @@ static void ar9287SetPowerCalTable(struct ath_hal *ah, const struct ieee80211_channel *chan, int16_t *pTxPowerIndexOffset) { - struct cal_data_op_loop_ar9287 *pRawDatasetOpenLoop; - uint8_t *pCalBChans = NULL; - uint16_t pdGainOverlap_t2; - uint16_t numPiers = 0, i; - uint16_t numXpdGain, xpdMask; - uint16_t xpdGainValues[AR5416_NUM_PD_GAINS] = {0, 0, 0, 0}; - uint32_t regChainOffset; + struct cal_data_op_loop_ar9287 *pRawDatasetOpenLoop; + uint8_t *pCalBChans = NULL; + uint16_t pdGainOverlap_t2; + uint16_t numPiers = 0, i; + uint16_t numXpdGain, xpdMask; + uint16_t xpdGainValues[AR5416_NUM_PD_GAINS] = {0, 0, 0, 0}; + uint32_t regChainOffset; HAL_EEPROM_9287 *ee = AH_PRIVATE(ah)->ah_eeprom; - struct ar9287_eeprom *pEepData = &ee->ee_base; + struct ar9287_eeprom *pEepData = &ee->ee_base; - xpdMask = pEepData->modalHeader.xpdGain; + xpdMask = pEepData->modalHeader.xpdGain; - if ((pEepData->baseEepHeader.version & AR9287_EEP_VER_MINOR_MASK) >= - AR9287_EEP_MINOR_VER_2) - pdGainOverlap_t2 = pEepData->modalHeader.pdGainOverlap; - else - pdGainOverlap_t2 = (uint16_t)(MS(OS_REG_READ(ah, AR_PHY_TPCRG5), - AR_PHY_TPCRG5_PD_GAIN_OVERLAP)); + if ((pEepData->baseEepHeader.version & AR9287_EEP_VER_MINOR_MASK) >= + AR9287_EEP_MINOR_VER_2) + pdGainOverlap_t2 = pEepData->modalHeader.pdGainOverlap; + else + pdGainOverlap_t2 = (uint16_t)(MS(OS_REG_READ(ah, AR_PHY_TPCRG5), + AR_PHY_TPCRG5_PD_GAIN_OVERLAP)); /* Note: Kiwi should only be 2ghz.. */ - if (IEEE80211_IS_CHAN_2GHZ(chan)) { - pCalBChans = pEepData->calFreqPier2G; - numPiers = AR9287_NUM_2G_CAL_PIERS; - pRawDatasetOpenLoop = (struct cal_data_op_loop_ar9287 *)pEepData->calPierData2G[0]; - AH5416(ah)->initPDADC = pRawDatasetOpenLoop->vpdPdg[0][0]; - } - numXpdGain = 0; + if (IEEE80211_IS_CHAN_2GHZ(chan)) { + pCalBChans = pEepData->calFreqPier2G; + numPiers = AR9287_NUM_2G_CAL_PIERS; + pRawDatasetOpenLoop = (struct cal_data_op_loop_ar9287 *)pEepData->calPierData2G[0]; + AH5416(ah)->initPDADC = pRawDatasetOpenLoop->vpdPdg[0][0]; + } + numXpdGain = 0; - /* Calculate the value of xpdgains from the xpdGain Mask */ - for (i = 1; i <= AR5416_PD_GAINS_IN_MASK; i++) { - if ((xpdMask >> (AR5416_PD_GAINS_IN_MASK - i)) & 1) { - if (numXpdGain >= AR5416_NUM_PD_GAINS) - break; - xpdGainValues[numXpdGain] = - (uint16_t)(AR5416_PD_GAINS_IN_MASK-i); - numXpdGain++; - } - } + /* Calculate the value of xpdgains from the xpdGain Mask */ + for (i = 1; i <= AR5416_PD_GAINS_IN_MASK; i++) { + if ((xpdMask >> (AR5416_PD_GAINS_IN_MASK - i)) & 1) { + if (numXpdGain >= AR5416_NUM_PD_GAINS) + break; + xpdGainValues[numXpdGain] = + (uint16_t)(AR5416_PD_GAINS_IN_MASK-i); + numXpdGain++; + } + } - OS_REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_NUM_PD_GAIN, - (numXpdGain - 1) & 0x3); - OS_REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_1, - xpdGainValues[0]); - OS_REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_2, - xpdGainValues[1]); - OS_REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_3, - xpdGainValues[2]); + OS_REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_NUM_PD_GAIN, + (numXpdGain - 1) & 0x3); + OS_REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_1, + xpdGainValues[0]); + OS_REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_2, + xpdGainValues[1]); + OS_REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_3, + xpdGainValues[2]); - for (i = 0; i < AR9287_MAX_CHAINS; i++) { - regChainOffset = i * 0x1000; + for (i = 0; i < AR9287_MAX_CHAINS; i++) { + regChainOffset = i * 0x1000; - if (pEepData->baseEepHeader.txMask & (1 << i)) { - int8_t txPower; - pRawDatasetOpenLoop = - (struct cal_data_op_loop_ar9287 *)pEepData->calPierData2G[i]; - ar9287olcGetTxGainIndex(ah, chan, - pRawDatasetOpenLoop, - pCalBChans, numPiers, - &txPower); - ar9287olcSetPDADCs(ah, txPower, i); - } - } + if (pEepData->baseEepHeader.txMask & (1 << i)) { + int8_t txPower; + pRawDatasetOpenLoop = + (struct cal_data_op_loop_ar9287 *)pEepData->calPierData2G[i]; + ar9287olcGetTxGainIndex(ah, chan, + pRawDatasetOpenLoop, + pCalBChans, numPiers, + &txPower); + ar9287olcSetPDADCs(ah, txPower, i); + } + } - *pTxPowerIndexOffset = 0; + *pTxPowerIndexOffset = 0; } @@ -329,41 +329,44 @@ ar9287SetTransmitPower(struct ath_hal *ah, const struct ieee80211_channel *chan, uint16_t *rfXpdGain) { #define POW_SM(_r, _s) (((_r) & 0x3f) << (_s)) -#define N(a) (sizeof (a) / sizeof (a[0])) +#define N(a) (sizeof (a) / sizeof (a[0])) const struct modal_eep_ar9287_header *pModal; struct ath_hal_5212 *ahp = AH5212(ah); - int16_t ratesArray[Ar5416RateSize]; - int16_t txPowerIndexOffset = 0; - uint8_t ht40PowerIncForPdadc = 2; - int i; + int16_t txPowerIndexOffset = 0; + int i; - uint16_t cfgCtl; - uint16_t powerLimit; - uint16_t twiceAntennaReduction; - uint16_t twiceMaxRegulatoryPower; - int16_t maxPower; + uint16_t cfgCtl; + uint16_t powerLimit; + uint16_t twiceAntennaReduction; + uint16_t twiceMaxRegulatoryPower; + int16_t maxPower; HAL_EEPROM_9287 *ee = AH_PRIVATE(ah)->ah_eeprom; struct ar9287_eeprom *pEepData = &ee->ee_base; + AH5416(ah)->ah_ht40PowerIncForPdadc = 2; + /* Setup info for the actual eeprom */ - OS_MEMZERO(ratesArray, sizeof(ratesArray)); + OS_MEMZERO(AH5416(ah)->ah_ratesArray, + sizeof(AH5416(ah)->ah_ratesArray)); cfgCtl = ath_hal_getctl(ah, chan); powerLimit = chan->ic_maxregpower * 2; twiceAntennaReduction = chan->ic_maxantgain; - twiceMaxRegulatoryPower = AH_MIN(MAX_RATE_POWER, AH_PRIVATE(ah)->ah_powerLimit); + twiceMaxRegulatoryPower = AH_MIN(MAX_RATE_POWER, + AH_PRIVATE(ah)->ah_powerLimit); pModal = &pEepData->modalHeader; HALDEBUG(ah, HAL_DEBUG_RESET, "%s Channel=%u CfgCtl=%u\n", __func__,chan->ic_freq, cfgCtl ); /* XXX Assume Minor is v2 or later */ - ht40PowerIncForPdadc = pModal->ht40PowerIncForPdadc; + AH5416(ah)->ah_ht40PowerIncForPdadc = pModal->ht40PowerIncForPdadc; /* Fetch per-rate power table for the given channel */ if (! ar9287SetPowerPerRateTable(ah, pEepData, chan, - &ratesArray[0],cfgCtl, - twiceAntennaReduction, - twiceMaxRegulatoryPower, powerLimit)) { + &AH5416(ah)->ah_ratesArray[0], + cfgCtl, + twiceAntennaReduction, + twiceMaxRegulatoryPower, powerLimit)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unable to set tx power per rate table\n", __func__); return AH_FALSE; @@ -373,11 +376,14 @@ ar9287SetTransmitPower(struct ath_hal *ah, ar9287SetPowerCalTable(ah, chan, &txPowerIndexOffset); /* Calculate maximum power level */ - maxPower = AH_MAX(ratesArray[rate6mb], ratesArray[rateHt20_0]); - maxPower = AH_MAX(maxPower, ratesArray[rate1l]); + maxPower = AH_MAX(AH5416(ah)->ah_ratesArray[rate6mb], + AH5416(ah)->ah_ratesArray[rateHt20_0]); + maxPower = AH_MAX(maxPower, + AH5416(ah)->ah_ratesArray[rate1l]); if (IEEE80211_IS_CHAN_HT40(chan)) - maxPower = AH_MAX(maxPower, ratesArray[rateHt40_0]); + maxPower = AH_MAX(maxPower, + AH5416(ah)->ah_ratesArray[rateHt40_0]); ahp->ah_tx6PowerInHalfDbm = maxPower; AH_PRIVATE(ah)->ah_maxPowerLevel = maxPower; @@ -388,18 +394,20 @@ ar9287SetTransmitPower(struct ath_hal *ah, * adjust the rate table (0 offset if rates EEPROM not loaded) */ /* XXX what about the pwrTableOffset? */ - for (i = 0; i < N(ratesArray); i++) { - ratesArray[i] = (int16_t)(txPowerIndexOffset + ratesArray[i]); + for (i = 0; i < N(AH5416(ah)->ah_ratesArray); i++) { + AH5416(ah)->ah_ratesArray[i] = + (int16_t)(txPowerIndexOffset + + AH5416(ah)->ah_ratesArray[i]); /* -5 dBm offset for Merlin and later; this includes Kiwi */ - ratesArray[i] -= AR5416_PWR_TABLE_OFFSET_DB * 2; - if (ratesArray[i] > AR5416_MAX_RATE_POWER) - ratesArray[i] = AR5416_MAX_RATE_POWER; - if (ratesArray[i] < 0) - ratesArray[i] = 0; + AH5416(ah)->ah_ratesArray[i] -= AR5416_PWR_TABLE_OFFSET_DB * 2; + if (AH5416(ah)->ah_ratesArray[i] > AR5416_MAX_RATE_POWER) + AH5416(ah)->ah_ratesArray[i] = AR5416_MAX_RATE_POWER; + if (AH5416(ah)->ah_ratesArray[i] < 0) + AH5416(ah)->ah_ratesArray[i] = 0; } #ifdef AH_EEPROM_DUMP - ar5416PrintPowerPerRate(ah, ratesArray); + ar5416PrintPowerPerRate(ah, AH5416(ah)->ah_ratesArray); #endif /* @@ -410,18 +418,26 @@ ar9287SetTransmitPower(struct ath_hal *ah, * XXX handle overflow/too high power level? */ if (IEEE80211_IS_CHAN_HT40(chan)) { - ratesArray[rateHt40_0] += ht40PowerIncForPdadc; - ratesArray[rateHt40_1] += ht40PowerIncForPdadc; - ratesArray[rateHt40_2] += ht40PowerIncForPdadc; - ratesArray[rateHt40_3] += ht40PowerIncForPdadc; - ratesArray[rateHt40_4] += ht40PowerIncForPdadc; - ratesArray[rateHt40_5] += ht40PowerIncForPdadc; - ratesArray[rateHt40_6] += ht40PowerIncForPdadc; - ratesArray[rateHt40_7] += ht40PowerIncForPdadc; + AH5416(ah)->ah_ratesArray[rateHt40_0] += + AH5416(ah)->ah_ht40PowerIncForPdadc; + AH5416(ah)->ah_ratesArray[rateHt40_1] += + AH5416(ah)->ah_ht40PowerIncForPdadc; + AH5416(ah)->ah_ratesArray[rateHt40_2] += + AH5416(ah)->ah_ht40PowerIncForPdadc; + AH5416(ah)->ah_ratesArray[rateHt40_3] += + AH5416(ah)->ah_ht40PowerIncForPdadc; + AH5416(ah)->ah_ratesArray[rateHt40_4] += + AH5416(ah)->ah_ht40PowerIncForPdadc; + AH5416(ah)->ah_ratesArray[rateHt40_5] += + AH5416(ah)->ah_ht40PowerIncForPdadc; + AH5416(ah)->ah_ratesArray[rateHt40_6] += + AH5416(ah)->ah_ht40PowerIncForPdadc; + AH5416(ah)->ah_ratesArray[rateHt40_7] += + AH5416(ah)->ah_ht40PowerIncForPdadc; } /* Write the TX power rate registers */ - ar5416WriteTxPowerRateRegisters(ah, chan, ratesArray); + ar5416WriteTxPowerRateRegisters(ah, chan, AH5416(ah)->ah_ratesArray); return AH_TRUE; #undef POW_SM @@ -476,7 +492,8 @@ ar9287SetBoardValues(struct ath_hal *ah, const struct ieee80211_channel *chan) pModal->antCtrlChain[i]); OS_REG_WRITE(ah, AR_PHY_TIMING_CTRL4_CHAIN(0) + regChainOffset, - (OS_REG_READ(ah, AR_PHY_TIMING_CTRL4_CHAIN(0) + regChainOffset) + (OS_REG_READ(ah, AR_PHY_TIMING_CTRL4_CHAIN(0) + + regChainOffset) & ~(AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF | AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF)) | SM(pModal->iqCalICh[i], @@ -500,7 +517,6 @@ ar9287SetBoardValues(struct ath_hal *ah, const struct ieee80211_channel *chan) pModal->rxTxMarginCh[i]); } - if (IEEE80211_IS_CHAN_HT40(chan)) OS_REG_RMW_FIELD(ah, AR_PHY_SETTLING, AR_PHY_SETTLING_SWITCH, pModal->swSettleHt40); @@ -539,8 +555,8 @@ ar9287SetBoardValues(struct ath_hal *ah, const struct ieee80211_channel *chan) SM(pModal->ob_qam, AR9287_AN_RF2G3_OB_QAM) | SM(pModal->ob_pal_off, AR9287_AN_RF2G3_OB_PAL_OFF)); - OS_REG_WRITE(ah, AR9287_AN_RF2G3_CH0, regval); - OS_DELAY(100); /* analog write */ + /* Analog write - requires a 100usec delay */ + OS_A_REG_WRITE(ah, AR9287_AN_RF2G3_CH0, regval); regval = OS_REG_READ(ah, AR9287_AN_RF2G3_CH1); regval &= ~(AR9287_AN_RF2G3_DB1 | @@ -556,8 +572,7 @@ ar9287SetBoardValues(struct ath_hal *ah, const struct ieee80211_channel *chan) SM(pModal->ob_qam, AR9287_AN_RF2G3_OB_QAM) | SM(pModal->ob_pal_off, AR9287_AN_RF2G3_OB_PAL_OFF)); - OS_REG_WRITE(ah, AR9287_AN_RF2G3_CH1, regval); - OS_DELAY(100); /* analog write */ + OS_A_REG_WRITE(ah, AR9287_AN_RF2G3_CH1, regval); OS_REG_RMW_FIELD(ah, AR_PHY_RF_CTL2, AR_PHY_TX_FRAME_TO_DATA_START, pModal->txFrameToDataStart); diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287_reset.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287_reset.h index 679fb8cfc8..16a80f1320 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287_reset.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287_reset.h @@ -13,7 +13,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9002/ar9287_reset.h 222301 2011-05-26 09:15:33Z adrian $ */ #ifndef __AR9287_RESET_H__ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287an.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287an.h index ba7a92cbaa..110bfe694d 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287an.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287an.h @@ -13,7 +13,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9002/ar9287an.h 222301 2011-05-26 09:15:33Z adrian $ */ #ifndef __AR9287AN_H__ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287phy.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287phy.h index 8f28194e44..0abdc3af92 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287phy.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9002/ar9287phy.h @@ -13,7 +13,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9002/ar9287phy.h 222301 2011-05-26 09:15:33Z adrian $ */ #ifndef __AR9287PHY_H__ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9003/ar9300_btcoex.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9003/ar9300_btcoex.h new file mode 100644 index 0000000000..7de454aab2 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9003/ar9300_btcoex.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2011 Atheros Communications, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9003/ar9300_btcoex.h 237611 2012-06-26 22:16:53Z adrian $ + */ + +#ifndef __ATH_AR9300_BTCOEX_H__ +#define __ATH_AR9300_BTCOEX_H__ +/* + * Weight table configurations. + */ +#define AR9300_BT_WGHT 0xcccc4444 +#define AR9300_STOMP_ALL_WLAN_WGHT0 0xfffffff0 +#define AR9300_STOMP_ALL_WLAN_WGHT1 0xfffffff0 +#define AR9300_STOMP_LOW_WLAN_WGHT0 0x88888880 +#define AR9300_STOMP_LOW_WLAN_WGHT1 0x88888880 +#define AR9300_STOMP_NONE_WLAN_WGHT0 0x00000000 +#define AR9300_STOMP_NONE_WLAN_WGHT1 0x00000000 +/* Stomp BT even when WLAN is idle */ +#define AR9300_STOMP_ALL_FORCE_WLAN_WGHT0 0xffffffff +#define AR9300_STOMP_ALL_FORCE_WLAN_WGHT1 0xffffffff +/* Stomp BT even when WLAN is idle */ +#define AR9300_STOMP_LOW_FORCE_WLAN_WGHT0 0x88888888 +#define AR9300_STOMP_LOW_FORCE_WLAN_WGHT1 0x88888888 + +#define JUPITER_STOMP_ALL_WLAN_WGHT0 0x00007d00 +#define JUPITER_STOMP_ALL_WLAN_WGHT1 0x7d7d7d00 +#define JUPITER_STOMP_ALL_WLAN_WGHT2 0x7d7d7d00 +#define JUPITER_STOMP_ALL_WLAN_WGHT3 0x7d7d7d7d +#define JUPITER_STOMP_LOW_WLAN_WGHT0 0x00007d00 +#define JUPITER_STOMP_LOW_WLAN_WGHT1 0x7d3b3b00 +#define JUPITER_STOMP_LOW_WLAN_WGHT2 0x3b3b3b00 +#define JUPITER_STOMP_LOW_WLAN_WGHT3 0x3b3b3b3b +#define JUPITER_STOMP_NONE_WLAN_WGHT0 0x00007d00 +#define JUPITER_STOMP_NONE_WLAN_WGHT1 0x7d000000 +#define JUPITER_STOMP_NONE_WLAN_WGHT2 0x00000000 +#define JUPITER_STOMP_NONE_WLAN_WGHT3 0x00000000 +#define JUPITER_STOMP_ALL_FORCE_WLAN_WGHT0 0x00007d7d +#define JUPITER_STOMP_ALL_FORCE_WLAN_WGHT1 0x7d7d7d00 +#define JUPITER_STOMP_ALL_FORCE_WLAN_WGHT2 0x7d7d7d7d +#define JUPITER_STOMP_ALL_FORCE_WLAN_WGHT3 0x7d7d7d7d +#define JUPITER_STOMP_LOW_FORCE_WLAN_WGHT0 0x00003b3b +#define JUPITER_STOMP_LOW_FORCE_WLAN_WGHT1 0x3b3b3b00 +#define JUPITER_STOMP_LOW_FORCE_WLAN_WGHT2 0x3b3b3b3b +#define JUPITER_STOMP_LOW_FORCE_WLAN_WGHT3 0x3b3b3b3b + +#endif /* __ATH_AR9300_BTCOEX_H__ */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9003/ar9300_devid.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9003/ar9300_devid.h new file mode 100644 index 0000000000..4751f5cf14 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_hal/ar9003/ar9300_devid.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2012, Qualcomm Atheros, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the following conditions are met: + * 1. The materials contained herein are unmodified and are used + * unmodified. + * 2. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following NO + * ''WARRANTY'' disclaimer below (''Disclaimer''), without + * modification. + * 3. Redistributions in binary form must reproduce at minimum a + * disclaimer similar to the Disclaimer below and any redistribution + * must be conditioned upon including a substantially similar + * Disclaimer requirement for further binary redistribution. + * 4. Neither the names of the above-listed copyright holders nor the + * names of any contributors may be used to endorse or promote + * product derived from this software without specific prior written + * permission. + * + * 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. + * + * $FreeBSD: releng/11.1/sys/dev/ath/ath_hal/ar9003/ar9300_devid.h 291418 2015-11-28 06:50:09Z adrian $ + * + */ +#ifndef __AR9300_DEVID_H__ +#define __AR9300_DEVID_H__ + +/* + * AR9380 HAL device IDs. + */ + +/* + * MAC Version and Revision + */ +#define AR_SREV_VERSION_AR9380 0x1C0 +#define AR_SREV_VERSION_AR9580 0x1C0 +#define AR_SREV_VERSION_AR9460 0x280 +#define AR_SREV_VERSION_QCA9565 0x2c0 + +#define AR_SREV_VERSION_AR9330 0x200 +#define AR_SREV_VERSION_AR9340 0x300 +#define AR_SREV_VERSION_QCA9550 0x400 +#define AR_SREV_VERSION_AR9485 0x240 +#define AR_SREV_VERSION_QCA9530 0x500 + +#define AR_SREV_REVISION_AR9380_10 0 /* AR9380 1.0 */ +#define AR_SREV_REVISION_AR9380_20 2 /* AR9380 2.0/2.1 */ +#define AR_SREV_REVISION_AR9380_22 3 /* AR9380 2.2 */ +#define AR_SREV_REVISION_AR9580_10 4 /* AR9580/Peacock 1.0 */ + +#define AR_SREV_REVISION_AR9330_10 0 /* AR9330 1.0 */ +#define AR_SREV_REVISION_AR9330_11 1 /* AR9330 1.1 */ +#define AR_SREV_REVISION_AR9330_12 2 /* AR9330 1.2 */ +#define AR_SREV_REVISION_AR9330_11_MASK 0xf /* AR9330 1.1 revision mask */ + +#define AR_SREV_REVISION_AR9485_10 0 /* AR9485 1.0 */ +#define AR_SREV_REVISION_AR9485_11 1 /* AR9485 1.1 */ + +#define AR_SREV_REVISION_AR9340_10 0 /* AR9340 1.0 */ +#define AR_SREV_REVISION_AR9340_11 1 /* AR9340 1.1 */ +#define AR_SREV_REVISION_AR9340_12 2 /* AR9340 1.2 */ +#define AR_SREV_REVISION_AR9340_MASK 0xf /* AR9340 revision mask */ + +#define AR_SREV_REVISION_AR9460_10 0 /* AR946x 1.0 */ + +#endif /* __AR9300_DEVID_H__ */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_rate/amrr/amrr.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_rate/amrr/amrr.c index 5fee76e301..c5fa539afa 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_rate/amrr/amrr.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_rate/amrr/amrr.c @@ -37,7 +37,7 @@ */ #include -__FBSDID("$FreeBSD$"); +__FBSDID("$FreeBSD: releng/11.1/sys/dev/ath/ath_rate/amrr/amrr.c 238633 2012-07-20 01:36:02Z adrian $"); /* * AMRR rate control. See: @@ -45,6 +45,7 @@ __FBSDID("$FreeBSD$"); * "IEEE 802.11 Rate Adaptation: A Practical Approach" by * Mathieu Lacage, Hossein Manshaei, Thierry Turletti */ +#include "opt_ath.h" #include "opt_inet.h" #include "opt_wlan.h" @@ -122,19 +123,21 @@ ath_rate_findrate(struct ath_softc *sc, struct ath_node *an, */ void ath_rate_getxtxrates(struct ath_softc *sc, struct ath_node *an, - uint8_t rix0, uint8_t *rix, uint8_t *try) + uint8_t rix0, struct ath_rc_series *rc) { struct amrr_node *amn = ATH_NODE_AMRR(an); -/* rix[0] = amn->amn_tx_rate0; */ - rix[1] = amn->amn_tx_rate1; - rix[2] = amn->amn_tx_rate2; - rix[3] = amn->amn_tx_rate3; + rc[0].flags = rc[1].flags = rc[2].flags = rc[3].flags = 0; - try[0] = amn->amn_tx_try0; - try[1] = amn->amn_tx_try1; - try[2] = amn->amn_tx_try2; - try[3] = amn->amn_tx_try3; + rc[0].rix = amn->amn_tx_rate0; + rc[1].rix = amn->amn_tx_rate1; + rc[2].rix = amn->amn_tx_rate2; + rc[3].rix = amn->amn_tx_rate3; + + rc[0].tries = amn->amn_tx_try0; + rc[1].tries = amn->amn_tx_try1; + rc[2].tries = amn->amn_tx_try2; + rc[3].tries = amn->amn_tx_try3; } @@ -153,10 +156,10 @@ ath_rate_setupxtxdesc(struct ath_softc *sc, struct ath_node *an, void ath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an, - const struct ath_buf *bf) + const struct ath_rc_series *rc, const struct ath_tx_status *ts, + int frame_size, int nframes, int nbad) { struct amrr_node *amn = ATH_NODE_AMRR(an); - const struct ath_tx_status *ts = &bf->bf_status.ds_txstat; int sr = ts->ts_shortretry; int lr = ts->ts_longretry; int retry_count = sr + lr; @@ -418,6 +421,14 @@ ath_rate_ctl(void *arg, struct ieee80211_node *ni) } } +static int +ath_rate_fetch_node_stats(struct ath_softc *sc, struct ath_node *an, + struct ath_rateioctl *re) +{ + + return (EINVAL); +} + static void ath_rate_sysctlattach(struct ath_softc *sc) { diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_rate/amrr/amrr.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_rate/amrr/amrr.h index c97a00712c..e43457f1d5 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_rate/amrr/amrr.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_rate/amrr/amrr.h @@ -34,7 +34,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_rate/amrr/amrr.h 178354 2008-04-20 20:35:46Z sam $ */ #ifndef _DEV_ATH_RATE_AMRR_H diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_rate/onoe/onoe.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_rate/onoe/onoe.c index 77236adb28..ea4a4b18e7 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_rate/onoe/onoe.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_rate/onoe/onoe.c @@ -28,11 +28,12 @@ */ #include -__FBSDID("$FreeBSD$"); +__FBSDID("$FreeBSD: releng/11.1/sys/dev/ath/ath_rate/onoe/onoe.c 238633 2012-07-20 01:36:02Z adrian $"); /* * Atsushi Onoe's rate control algorithm. */ +#include "opt_ath.h" #include "opt_inet.h" #include "opt_wlan.h" @@ -130,19 +131,21 @@ ath_rate_findrate(struct ath_softc *sc, struct ath_node *an, */ void ath_rate_getxtxrates(struct ath_softc *sc, struct ath_node *an, - uint8_t rix0, uint8_t *rix, uint8_t *try) + uint8_t rix0, struct ath_rc_series *rc) { struct onoe_node *on = ATH_NODE_ONOE(an); -/* rix[0] = on->on_tx_rate0; */ - rix[1] = on->on_tx_rate1; - rix[2] = on->on_tx_rate2; - rix[3] = on->on_tx_rate3; + rc[0].flags = rc[1].flags = rc[2].flags = rc[3].flags = 0; - try[0] = on->on_tx_try0; - try[1] = 2; - try[2] = 2; - try[3] = 2; + rc[0].rix = on->on_tx_rate0; + rc[1].rix = on->on_tx_rate1; + rc[2].rix = on->on_tx_rate2; + rc[3].rix = on->on_tx_rate3; + + rc[0].tries = on->on_tx_try0; + rc[1].tries = 2; + rc[2].tries = 2; + rc[3].tries = 2; } void @@ -160,10 +163,10 @@ ath_rate_setupxtxdesc(struct ath_softc *sc, struct ath_node *an, void ath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an, - const struct ath_buf *bf) + const struct ath_rc_series *rc, const struct ath_tx_status *ts, + int frame_size, int nframes, int nbad) { struct onoe_node *on = ATH_NODE_ONOE(an); - const struct ath_tx_status *ts = &bf->bf_status.ds_txstat; if (ts->ts_status == 0) on->on_tx_ok++; @@ -404,6 +407,14 @@ ath_rate_sysctlattach(struct ath_softc *sc) "rate control: # good periods before raising rate"); } +static int +ath_rate_fetch_node_stats(struct ath_softc *sc, struct ath_node *an, + struct ath_rateioctl *re) +{ + + return (EINVAL); +} + struct ath_ratectrl * ath_rate_attach(struct ath_softc *sc) { diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_rate/onoe/onoe.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_rate/onoe/onoe.h index 27bbe94920..8ef704b1f1 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_rate/onoe/onoe.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_rate/onoe/onoe.h @@ -26,7 +26,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_rate/onoe/onoe.h 178354 2008-04-20 20:35:46Z sam $ */ /* diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_rate/sample/sample.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_rate/sample/sample.c index 27bb98cf87..259bd26a41 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_rate/sample/sample.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_rate/sample/sample.c @@ -36,19 +36,22 @@ */ #include -__FBSDID("$FreeBSD$"); +__FBSDID("$FreeBSD: releng/11.1/sys/dev/ath/ath_rate/sample/sample.c 287197 2015-08-27 08:56:39Z glebius $"); /* * John Bicket's SampleRate control algorithm. */ +#include "opt_ath.h" #include "opt_inet.h" #include "opt_wlan.h" +#include "opt_ah.h" #include #include #include #include #include +#include #include #include @@ -59,6 +62,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include /* XXX for ether_sprintf */ @@ -103,8 +107,6 @@ __FBSDID("$FreeBSD$"); static void ath_rate_ctl_reset(struct ath_softc *, struct ieee80211_node *); -static const int packet_size_bins[NUM_PACKET_SIZE_BINS] = { 250, 1600 }; - static __inline int size_to_bin(int size) { @@ -126,12 +128,6 @@ size_to_bin(int size) return NUM_PACKET_SIZE_BINS-1; } -static __inline int -bin_to_size(int index) -{ - return packet_size_bins[index]; -} - void ath_rate_node_init(struct ath_softc *sc, struct ath_node *an) { @@ -146,6 +142,8 @@ ath_rate_node_cleanup(struct ath_softc *sc, struct ath_node *an) static int dot11rate(const HAL_RATE_TABLE *rt, int rix) { + if (rix < 0) + return -1; return rt->info[rix].phy == IEEE80211_T_HT ? rt->info[rix].dot11Rate : (rt->info[rix].dot11Rate & IEEE80211_RATE_VAL) / 2; } @@ -153,6 +151,8 @@ dot11rate(const HAL_RATE_TABLE *rt, int rix) static const char * dot11rate_label(const HAL_RATE_TABLE *rt, int rix) { + if (rix < 0) + return ""; return rt->info[rix].phy == IEEE80211_T_HT ? "MCS" : "Mb "; } @@ -165,12 +165,13 @@ pick_best_rate(struct ath_node *an, const HAL_RATE_TABLE *rt, int size_bin, int require_acked_before) { struct sample_node *sn = ATH_NODE_SAMPLE(an); - int best_rate_rix, best_rate_tt; - uint32_t mask; - int rix, tt; + int best_rate_rix, best_rate_tt, best_rate_pct; + uint64_t mask; + int rix, tt, pct; best_rate_rix = 0; best_rate_tt = 0; + best_rate_pct = 0; for (mask = sn->ratemask, rix = 0; mask != 0; mask >>= 1, rix++) { if ((mask & 1) == 0) /* not a supported rate */ continue; @@ -187,13 +188,54 @@ pick_best_rate(struct ath_node *an, const HAL_RATE_TABLE *rt, !sn->stats[size_bin][rix].packets_acked)) continue; + /* Calculate percentage if possible */ + if (sn->stats[size_bin][rix].total_packets > 0) { + pct = sn->stats[size_bin][rix].ewma_pct; + } else { + /* XXX for now, assume 95% ok */ + pct = 95; + } + /* don't use a bit-rate that has been failing */ if (sn->stats[size_bin][rix].successive_failures > 3) continue; - if (best_rate_tt == 0 || tt < best_rate_tt) { - best_rate_tt = tt; - best_rate_rix = rix; + /* + * For HT, Don't use a bit rate that is much more + * lossy than the best. + * + * XXX this isn't optimal; it's just designed to + * eliminate rates that are going to be obviously + * worse. + */ + if (an->an_node.ni_flags & IEEE80211_NODE_HT) { + if (best_rate_pct > (pct + 50)) + continue; + } + + /* + * For non-MCS rates, use the current average txtime for + * comparison. + */ + if (! (an->an_node.ni_flags & IEEE80211_NODE_HT)) { + if (best_rate_tt == 0 || tt <= best_rate_tt) { + best_rate_tt = tt; + best_rate_rix = rix; + best_rate_pct = pct; + } + } + + /* + * Since 2 stream rates have slightly higher TX times, + * allow a little bit of leeway. This should later + * be abstracted out and properly handled. + */ + if (an->an_node.ni_flags & IEEE80211_NODE_HT) { + if (best_rate_tt == 0 || (tt * 8 <= best_rate_tt * 10)) { + best_rate_tt = tt; + best_rate_rix = rix; + best_rate_pct = pct; + } } } return (best_rate_tt ? best_rate_rix : -1); @@ -211,7 +253,7 @@ pick_sample_rate(struct sample_softc *ssc , struct ath_node *an, struct sample_node *sn = ATH_NODE_SAMPLE(an); int current_rix, rix; unsigned current_tt; - uint32_t mask; + uint64_t mask; current_rix = sn->current_rix[size_bin]; if (current_rix < 0) { @@ -223,39 +265,68 @@ pick_sample_rate(struct sample_softc *ssc , struct ath_node *an, current_tt = sn->stats[size_bin][current_rix].average_tx_time; rix = sn->last_sample_rix[size_bin]+1; /* next sample rate */ - mask = sn->ratemask &~ (1<ratemask &~ ((uint64_t) 1<= rt->rateCount) rix = 0; continue; } + /* + * The following code stops trying to sample + * non-MCS rates when speaking to an MCS node. + * However, at least for CCK rates in 2.4GHz mode, + * the non-MCS rates MAY actually provide better + * PER at the very far edge of reception. + * + * However! Until ath_rate_form_aggr() grows + * some logic to not form aggregates if the + * selected rate is non-MCS, this won't work. + * + * So don't disable this code until you've taught + * ath_rate_form_aggr() to drop out if any of + * the selected rates are non-MCS. + */ +#if 1 /* if the node is HT and the rate isn't HT, don't bother sample */ if ((an->an_node.ni_flags & IEEE80211_NODE_HT) && (rt->info[rix].phy != IEEE80211_T_HT)) { - mask &= ~(1<stats[size_bin][rix].perfect_tx_time > current_tt) { - mask &= ~(1<stats[size_bin][rix].successive_failures > ssc->max_successive_failures && ticks - sn->stats[size_bin][rix].last_tx < ssc->stale_failure_timeout) { - mask &= ~(1<an_node.ni_flags & IEEE80211_NODE_HT) { + if (rix < (current_rix - 3) || + rix > (current_rix + 3)) { + mask &= ~((uint64_t) 1< 11M for non-HT rates */ if (! (an->an_node.ni_flags & IEEE80211_NODE_HT)) { if (DOT11RATE(rix) > 2*11 && rix > current_rix + 2) { - mask &= ~(1<info[ix].dot11Rate & IEEE80211_RATE_VAL) +#define MCS(ix) (rt->info[ix].dot11Rate | IEEE80211_RATE_MCS) +#define RATE(ix) (DOT11RATE(ix) / 2) + int rix = -1; + const HAL_RATE_TABLE *rt = sc->sc_currates; + struct sample_node *sn = ATH_NODE_SAMPLE(an); + const int size_bin = size_to_bin(frameLen); + + /* no packet has been sent successfully yet */ + for (rix = rt->rateCount-1; rix > 0; rix--) { + if ((sn->ratemask & ((uint64_t) 1<info[rix].phy == IEEE80211_T_HT) + continue; + + /* + * Pick the highest rate <= 36 Mbps + * that hasn't failed. + */ + if (DOT11RATE(rix) <= 72 && + sn->stats[size_bin][rix].successive_failures == 0) { + break; + } + } + return rix; +#undef RATE +#undef MCS +#undef DOT11RATE +} + +/* + * Pick a HT rate to begin using. + * + * Don't use any non-HT rates; only consider HT rates. + */ +static int +ath_rate_pick_seed_rate_ht(struct ath_softc *sc, struct ath_node *an, + int frameLen) +{ +#define DOT11RATE(ix) (rt->info[ix].dot11Rate & IEEE80211_RATE_VAL) +#define MCS(ix) (rt->info[ix].dot11Rate | IEEE80211_RATE_MCS) +#define RATE(ix) (DOT11RATE(ix) / 2) + int rix = -1, ht_rix = -1; + const HAL_RATE_TABLE *rt = sc->sc_currates; + struct sample_node *sn = ATH_NODE_SAMPLE(an); + const int size_bin = size_to_bin(frameLen); + + /* no packet has been sent successfully yet */ + for (rix = rt->rateCount-1; rix > 0; rix--) { + /* Skip rates we can't use */ + if ((sn->ratemask & ((uint64_t) 1<info[rix].phy == IEEE80211_T_HT) + ht_rix = rix; + + /* Skip non-HT rates */ + if (rt->info[rix].phy != IEEE80211_T_HT) + continue; + + /* + * Pick a medium-speed rate regardless of stream count + * which has not seen any failures. Higher rates may fail; + * we'll try them later. + */ + if (((MCS(rix) & 0x7) <= 4) && + sn->stats[size_bin][rix].successive_failures == 0) { + break; + } + } + + /* + * If all the MCS rates have successive failures, rix should be + * > 0; otherwise use the lowest MCS rix (hopefully MCS 0.) + */ + return MAX(rix, ht_rix); +#undef RATE +#undef MCS +#undef DOT11RATE +} void @@ -327,8 +488,7 @@ ath_rate_findrate(struct ath_softc *sc, struct ath_node *an, #define RATE(ix) (DOT11RATE(ix) / 2) struct sample_node *sn = ATH_NODE_SAMPLE(an); struct sample_softc *ssc = ATH_SOFTC_SAMPLE(sc); - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211com *ic = &sc->sc_ic; const HAL_RATE_TABLE *rt = sc->sc_currates; const int size_bin = size_to_bin(frameLen); int rix, mrr, best_rix, change_rates; @@ -336,14 +496,24 @@ ath_rate_findrate(struct ath_softc *sc, struct ath_node *an, ath_rate_update_static_rix(sc, &an->an_node); + if (sn->currates != sc->sc_currates) { + device_printf(sc->sc_dev, "%s: currates != sc_currates!\n", + __func__); + rix = 0; + *try0 = ATH_TXMAXTRY; + goto done; + } + if (sn->static_rix != -1) { rix = sn->static_rix; *try0 = ATH_TXMAXTRY; goto done; } - /* XXX TODO: this doesn't know about 11gn vs 11g protection; teach it */ - mrr = sc->sc_mrretry && !(ic->ic_flags & IEEE80211_F_USEPROT); + mrr = sc->sc_mrretry; + /* XXX check HT protmode too */ + if (mrr && (ic->ic_flags & IEEE80211_F_USEPROT && !sc->sc_mrrprot)) + mrr = 0; best_rix = pick_best_rate(an, rt, size_bin, !mrr); if (best_rix >= 0) { @@ -358,9 +528,14 @@ ath_rate_findrate(struct ath_softc *sc, struct ath_node *an, if (sn->sample_tt[size_bin] < average_tx_time * (sn->packets_since_sample[size_bin]*ssc->sample_rate/100)) { rix = pick_sample_rate(ssc, an, rt, size_bin); IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL, - &an->an_node, "size %u sample rate %d current rate %d", - bin_to_size(size_bin), RATE(rix), - RATE(sn->current_rix[size_bin])); + &an->an_node, "att %d sample_tt %d size %u sample rate %d %s current rate %d %s", + average_tx_time, + sn->sample_tt[size_bin], + bin_to_size(size_bin), + dot11rate(rt, rix), + dot11rate_label(rt, rix), + dot11rate(rt, sn->current_rix[size_bin]), + dot11rate_label(rt, sn->current_rix[size_bin])); if (rix != sn->current_rix[size_bin]) { sn->current_sample_rix[size_bin] = rix; } else { @@ -371,29 +546,58 @@ ath_rate_findrate(struct ath_softc *sc, struct ath_node *an, change_rates = 0; if (!sn->packets_sent[size_bin] || best_rix == -1) { /* no packet has been sent successfully yet */ - for (rix = rt->rateCount-1; rix > 0; rix--) { - if ((sn->ratemask & (1<stats[size_bin][rix].successive_failures == 0) { - break; - } - } change_rates = 1; - best_rix = rix; + if (an->an_node.ni_flags & IEEE80211_NODE_HT) + best_rix = + ath_rate_pick_seed_rate_ht(sc, an, frameLen); + else + best_rix = + ath_rate_pick_seed_rate_legacy(sc, an, frameLen); } else if (sn->packets_sent[size_bin] < 20) { /* let the bit-rate switch quickly during the first few packets */ + IEEE80211_NOTE(an->an_node.ni_vap, + IEEE80211_MSG_RATECTL, &an->an_node, + "%s: switching quickly..", __func__); change_rates = 1; } else if (ticks - ssc->min_switch > sn->ticks_since_switch[size_bin]) { /* min_switch seconds have gone by */ + IEEE80211_NOTE(an->an_node.ni_vap, + IEEE80211_MSG_RATECTL, &an->an_node, + "%s: min_switch %d > ticks_since_switch %d..", + __func__, ticks - ssc->min_switch, sn->ticks_since_switch[size_bin]); change_rates = 1; - } else if (2*average_tx_time < sn->stats[size_bin][sn->current_rix[size_bin]].average_tx_time) { + } else if ((! (an->an_node.ni_flags & IEEE80211_NODE_HT)) && + (2*average_tx_time < sn->stats[size_bin][sn->current_rix[size_bin]].average_tx_time)) { /* the current bit-rate is twice as slow as the best one */ + IEEE80211_NOTE(an->an_node.ni_vap, + IEEE80211_MSG_RATECTL, &an->an_node, + "%s: 2x att (= %d) < cur_rix att %d", + __func__, + 2 * average_tx_time, sn->stats[size_bin][sn->current_rix[size_bin]].average_tx_time); change_rates = 1; + } else if ((an->an_node.ni_flags & IEEE80211_NODE_HT)) { + int cur_rix = sn->current_rix[size_bin]; + int cur_att = sn->stats[size_bin][cur_rix].average_tx_time; + /* + * If the node is HT, upgrade it if the MCS rate is + * higher and the average tx time is within 20% of + * the current rate. It can fail a little. + * + * This is likely not optimal! + */ +#if 0 + printf("cur rix/att %x/%d, best rix/att %x/%d\n", + MCS(cur_rix), cur_att, MCS(best_rix), average_tx_time); +#endif + if ((MCS(best_rix) > MCS(cur_rix)) && + (average_tx_time * 8) <= (cur_att * 10)) { + IEEE80211_NOTE(an->an_node.ni_vap, + IEEE80211_MSG_RATECTL, &an->an_node, + "%s: HT: best_rix 0x%d > cur_rix 0x%x, average_tx_time %d, cur_att %d", + __func__, + MCS(best_rix), MCS(cur_rix), average_tx_time, cur_att); + change_rates = 1; + } } sn->packets_since_sample[size_bin]++; @@ -428,6 +632,20 @@ ath_rate_findrate(struct ath_softc *sc, struct ath_node *an, } *try0 = mrr ? sn->sched[rix].t0 : ATH_TXMAXTRY; done: + + /* + * This bug totally sucks and should be fixed. + * + * For now though, let's not panic, so we can start to figure + * out how to better reproduce it. + */ + if (rix < 0 || rix >= rt->rateCount) { + printf("%s: ERROR: rix %d out of bounds (rateCount=%d)\n", + __func__, + rix, + rt->rateCount); + rix = 0; /* XXX just default for now */ + } KASSERT(rix >= 0 && rix < rt->rateCount, ("rix is %d", rix)); *rix0 = rix; @@ -445,22 +663,25 @@ done: */ void ath_rate_getxtxrates(struct ath_softc *sc, struct ath_node *an, - uint8_t rix0, uint8_t *rix, uint8_t *try) + uint8_t rix0, struct ath_rc_series *rc) { struct sample_node *sn = ATH_NODE_SAMPLE(an); const struct txschedule *sched = &sn->sched[rix0]; - KASSERT(rix0 == sched->r0, ("rix0 (%x) != sched->r0 (%x)!\n", rix0, sched->r0)); + KASSERT(rix0 == sched->r0, ("rix0 (%x) != sched->r0 (%x)!\n", + rix0, sched->r0)); -/* rix[0] = sched->r0; */ - rix[1] = sched->r1; - rix[2] = sched->r2; - rix[3] = sched->r3; + rc[0].flags = rc[1].flags = rc[2].flags = rc[3].flags = 0; - try[0] = sched->t0; - try[1] = sched->t1; - try[2] = sched->t2; - try[3] = sched->t3; + rc[0].rix = sched->r0; + rc[1].rix = sched->r1; + rc[2].rix = sched->r2; + rc[3].rix = sched->r3; + + rc[0].tries = sched->t0; + rc[1].tries = sched->t1; + rc[2].tries = sched->t2; + rc[3].tries = sched->t3; } void @@ -495,14 +716,19 @@ update_stats(struct ath_softc *sc, struct ath_node *an, int rix1, int tries1, int rix2, int tries2, int rix3, int tries3, - int short_tries, int tries, int status) + int short_tries, int tries, int status, + int nframes, int nbad) { struct sample_node *sn = ATH_NODE_SAMPLE(an); struct sample_softc *ssc = ATH_SOFTC_SAMPLE(sc); +#ifdef IEEE80211_DEBUG + const HAL_RATE_TABLE *rt = sc->sc_currates; +#endif const int size_bin = size_to_bin(frame_size); const int size = bin_to_size(size_bin); int tt, tries_so_far; int is_ht40 = (an->an_node.ni_chw == 40); + int pct; if (!IS_RATE_DEFINED(sn, rix0)) return; @@ -537,7 +763,7 @@ update_stats(struct ath_softc *sc, struct ath_node *an, /* just average the first few packets */ int avg_tx = sn->stats[size_bin][rix0].average_tx_time; int packets = sn->stats[size_bin][rix0].total_packets; - sn->stats[size_bin][rix0].average_tx_time = (tt+(avg_tx*packets))/(packets+1); + sn->stats[size_bin][rix0].average_tx_time = (tt+(avg_tx*packets))/(packets+nframes); } else { /* use a ewma */ sn->stats[size_bin][rix0].average_tx_time = @@ -545,67 +771,106 @@ update_stats(struct ath_softc *sc, struct ath_node *an, (tt * (100 - ssc->smoothing_rate))) / 100; } - if (status != 0) { + /* + * XXX Don't mark the higher bit rates as also having failed; as this + * unfortunately stops those rates from being tasted when trying to + * TX. This happens with 11n aggregation. + */ + if (nframes == nbad) { +#if 0 int y; - sn->stats[size_bin][rix0].successive_failures++; +#endif + sn->stats[size_bin][rix0].successive_failures += nbad; +#if 0 for (y = size_bin+1; y < NUM_PACKET_SIZE_BINS; y++) { /* * Also say larger packets failed since we * assume if a small packet fails at a * bit-rate then a larger one will also. */ - sn->stats[y][rix0].successive_failures++; + sn->stats[y][rix0].successive_failures += nbad; sn->stats[y][rix0].last_tx = ticks; sn->stats[y][rix0].tries += tries; - sn->stats[y][rix0].total_packets++; + sn->stats[y][rix0].total_packets += nframes; } +#endif } else { - sn->stats[size_bin][rix0].packets_acked++; + sn->stats[size_bin][rix0].packets_acked += (nframes - nbad); sn->stats[size_bin][rix0].successive_failures = 0; } sn->stats[size_bin][rix0].tries += tries; sn->stats[size_bin][rix0].last_tx = ticks; - sn->stats[size_bin][rix0].total_packets++; + sn->stats[size_bin][rix0].total_packets += nframes; + + /* update EWMA for this rix */ + + /* Calculate percentage based on current rate */ + if (nframes == 0) + nframes = nbad = 1; + pct = ((nframes - nbad) * 1000) / nframes; + + if (sn->stats[size_bin][rix0].total_packets < + ssc->smoothing_minpackets) { + /* just average the first few packets */ + int a_pct = (sn->stats[size_bin][rix0].packets_acked * 1000) / + (sn->stats[size_bin][rix0].total_packets); + sn->stats[size_bin][rix0].ewma_pct = a_pct; + } else { + /* use a ewma */ + sn->stats[size_bin][rix0].ewma_pct = + ((sn->stats[size_bin][rix0].ewma_pct * ssc->smoothing_rate) + + (pct * (100 - ssc->smoothing_rate))) / 100; + } + if (rix0 == sn->current_sample_rix[size_bin]) { IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL, &an->an_node, -"%s: size %d %s sample rate %d tries (%d/%d) tt %d avg_tt (%d/%d)", +"%s: size %d %s sample rate %d %s tries (%d/%d) tt %d avg_tt (%d/%d) nfrm %d nbad %d", __func__, size, status ? "FAIL" : "OK", - rix0, short_tries, tries, tt, + dot11rate(rt, rix0), + dot11rate_label(rt, rix0), + short_tries, tries, tt, sn->stats[size_bin][rix0].average_tx_time, - sn->stats[size_bin][rix0].perfect_tx_time); + sn->stats[size_bin][rix0].perfect_tx_time, + nframes, nbad); sn->sample_tt[size_bin] = tt; sn->current_sample_rix[size_bin] = -1; } } static void -badrate(struct ifnet *ifp, int series, int hwrate, int tries, int status) +badrate(struct ath_softc *sc, int series, int hwrate, int tries, int status) { - if_printf(ifp, "bad series%d hwrate 0x%x, tries %u ts_status 0x%x\n", + + device_printf(sc->sc_dev, + "bad series%d hwrate 0x%x, tries %u ts_status 0x%x\n", series, hwrate, tries, status); } void ath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an, - const struct ath_buf *bf) + const struct ath_rc_series *rc, const struct ath_tx_status *ts, + int frame_size, int nframes, int nbad) { - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211com *ic = &sc->sc_ic; struct sample_node *sn = ATH_NODE_SAMPLE(an); - const struct ath_tx_status *ts = &bf->bf_status.ds_txstat; - const struct ath_desc *ds0 = &bf->bf_desc[0]; - int final_rix, short_tries, long_tries, frame_size; + int final_rix, short_tries, long_tries; const HAL_RATE_TABLE *rt = sc->sc_currates; + int status = ts->ts_status; int mrr; final_rix = rt->rateCodeToIndex[ts->ts_rate]; short_tries = ts->ts_shortretry; long_tries = ts->ts_longretry + 1; - frame_size = ds0->ds_ctl0 & 0x0fff; /* low-order 12 bits of ds_ctl0 */ + + if (nframes == 0) { + device_printf(sc->sc_dev, "%s: nframes=0?\n", __func__); + return; + } + if (frame_size == 0) /* NB: should not happen */ frame_size = 1500; @@ -615,63 +880,74 @@ ath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an, "%s: size %d %s rate/try %d/%d no rates yet", __func__, bin_to_size(size_to_bin(frame_size)), - ts->ts_status ? "FAIL" : "OK", + status ? "FAIL" : "OK", short_tries, long_tries); return; } - mrr = sc->sc_mrretry && !(ic->ic_flags & IEEE80211_F_USEPROT); + mrr = sc->sc_mrretry; + /* XXX check HT protmode too */ + if (mrr && (ic->ic_flags & IEEE80211_F_USEPROT && !sc->sc_mrrprot)) + mrr = 0; + if (!mrr || ts->ts_finaltsi == 0) { if (!IS_RATE_DEFINED(sn, final_rix)) { - badrate(ifp, 0, ts->ts_rate, long_tries, ts->ts_status); + device_printf(sc->sc_dev, + "%s: ts_rate=%d ts_finaltsi=%d, final_rix=%d\n", + __func__, ts->ts_rate, ts->ts_finaltsi, final_rix); + badrate(sc, 0, ts->ts_rate, long_tries, status); return; } /* * Only one rate was used; optimize work. */ IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL, - &an->an_node, "%s: size %d (%d bytes) %s rate/try %d %s/%d/%d", + &an->an_node, "%s: size %d (%d bytes) %s rate/short/long %d %s/%d/%d nframes/nbad [%d/%d]", __func__, bin_to_size(size_to_bin(frame_size)), frame_size, - ts->ts_status ? "FAIL" : "OK", - dot11rate(rt, final_rix), dot11rate_label(rt, final_rix), short_tries, long_tries); + status ? "FAIL" : "OK", + dot11rate(rt, final_rix), dot11rate_label(rt, final_rix), + short_tries, long_tries, nframes, nbad); update_stats(sc, an, frame_size, final_rix, long_tries, 0, 0, 0, 0, 0, 0, - short_tries, long_tries, ts->ts_status); + short_tries, long_tries, status, + nframes, nbad); + } else { - int hwrates[4], tries[4], rix[4]; int finalTSIdx = ts->ts_finaltsi; int i; /* * Process intermediate rates that failed. */ - ath_hal_gettxcompletionrates(sc->sc_ah, ds0, hwrates, tries); - - for (i = 0; i < 4; i++) { - rix[i] = rt->rateCodeToIndex[hwrates[i]]; - } IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL, &an->an_node, -"%s: size %d (%d bytes) finaltsidx %d tries %d %s rate/try [%d %s/%d %d %s/%d %d %s/%d %d %s/%d]", +"%s: size %d (%d bytes) finaltsidx %d short %d long %d %s rate/try [%d %s/%d %d %s/%d %d %s/%d %d %s/%d] nframes/nbad [%d/%d]", __func__, bin_to_size(size_to_bin(frame_size)), frame_size, finalTSIdx, - long_tries, - ts->ts_status ? "FAIL" : "OK", - dot11rate(rt, rix[0]), dot11rate_label(rt, rix[0]), tries[0], - dot11rate(rt, rix[1]), dot11rate_label(rt, rix[1]), tries[1], - dot11rate(rt, rix[2]), dot11rate_label(rt, rix[2]), tries[2], - dot11rate(rt, rix[3]), dot11rate_label(rt, rix[3]), tries[3]); + short_tries, + long_tries, + status ? "FAIL" : "OK", + dot11rate(rt, rc[0].rix), + dot11rate_label(rt, rc[0].rix), rc[0].tries, + dot11rate(rt, rc[1].rix), + dot11rate_label(rt, rc[1].rix), rc[1].tries, + dot11rate(rt, rc[2].rix), + dot11rate_label(rt, rc[2].rix), rc[2].tries, + dot11rate(rt, rc[3].rix), + dot11rate_label(rt, rc[3].rix), rc[3].tries, + nframes, nbad); for (i = 0; i < 4; i++) { - if (tries[i] && !IS_RATE_DEFINED(sn, rix[i])) - badrate(ifp, 0, hwrates[i], tries[i], ts->ts_status); + if (rc[i].tries && !IS_RATE_DEFINED(sn, rc[i].rix)) + badrate(sc, 0, rc[i].ratecode, rc[i].tries, + status); } /* @@ -681,47 +957,51 @@ ath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an, * sample higher rates 1 try at a time doing so * may unfairly penalize them. */ - if (tries[0]) { - update_stats(sc, an, frame_size, - rix[0], tries[0], - rix[1], tries[1], - rix[2], tries[2], - rix[3], tries[3], - short_tries, long_tries, - long_tries > tries[0]); - long_tries -= tries[0]; + if (rc[0].tries) { + update_stats(sc, an, frame_size, + rc[0].rix, rc[0].tries, + rc[1].rix, rc[1].tries, + rc[2].rix, rc[2].tries, + rc[3].rix, rc[3].tries, + short_tries, long_tries, + long_tries > rc[0].tries, + nframes, nbad); + long_tries -= rc[0].tries; } - if (tries[1] && finalTSIdx > 0) { - update_stats(sc, an, frame_size, - rix[1], tries[1], - rix[2], tries[2], - rix[3], tries[3], - 0, 0, - short_tries, long_tries, - ts->ts_status); - long_tries -= tries[1]; + if (rc[1].tries && finalTSIdx > 0) { + update_stats(sc, an, frame_size, + rc[1].rix, rc[1].tries, + rc[2].rix, rc[2].tries, + rc[3].rix, rc[3].tries, + 0, 0, + short_tries, long_tries, + status, + nframes, nbad); + long_tries -= rc[1].tries; } - if (tries[2] && finalTSIdx > 1) { - update_stats(sc, an, frame_size, - rix[2], tries[2], - rix[3], tries[3], + if (rc[2].tries && finalTSIdx > 1) { + update_stats(sc, an, frame_size, + rc[2].rix, rc[2].tries, + rc[3].rix, rc[3].tries, 0, 0, 0, 0, - short_tries, long_tries, - ts->ts_status); - long_tries -= tries[2]; + short_tries, long_tries, + status, + nframes, nbad); + long_tries -= rc[2].tries; } - if (tries[3] && finalTSIdx > 2) { - update_stats(sc, an, frame_size, - rix[3], tries[3], + if (rc[3].tries && finalTSIdx > 2) { + update_stats(sc, an, frame_size, + rc[3].rix, rc[3].tries, 0, 0, 0, 0, 0, 0, - short_tries, long_tries, - ts->ts_status); + short_tries, long_tries, + status, + nframes, nbad); } } } @@ -766,6 +1046,7 @@ ath_rate_ctl_reset(struct ath_softc *sc, struct ieee80211_node *ni) KASSERT(sc->sc_curmode < IEEE80211_MODE_MAX+2, ("curmode %u", sc->sc_curmode)); + sn->sched = mrr_schedules[sc->sc_curmode]; KASSERT(sn->sched != NULL, ("no mrr schedule for mode %u", sc->sc_curmode)); @@ -773,6 +1054,8 @@ ath_rate_ctl_reset(struct ath_softc *sc, struct ieee80211_node *ni) sn->static_rix = -1; ath_rate_update_static_rix(sc, ni); + sn->currates = sc->sc_currates; + /* * Construct a bitmask of usable rates. This has all * negotiated rates minus those marked by the hal as @@ -790,7 +1073,7 @@ ath_rate_ctl_reset(struct ath_softc *sc, struct ieee80211_node *ni) continue; KASSERT(rix < SAMPLE_MAXRATES, ("mcs %u has rix %d", MCS(x), rix)); - sn->ratemask |= 1<ratemask |= (uint64_t) 1<ratemask |= 1<ratemask |= (uint64_t) 1<ni_vap, IEEE80211_MSG_RATECTL)) { - uint32_t mask; + uint64_t mask; ieee80211_note(ni->ni_vap, "[%6D] %s: size 1600 rate/tt", ni->ni_macaddr, ":", __func__); @@ -824,7 +1107,7 @@ ath_rate_ctl_reset(struct ath_softc *sc, struct ieee80211_node *ni) #endif for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) { int size = bin_to_size(y); - uint32_t mask; + uint64_t mask; sn->packets_sent[y] = 0; sn->current_sample_rix[y] = -1; @@ -844,6 +1127,7 @@ ath_rate_ctl_reset(struct ath_softc *sc, struct ieee80211_node *ni) sn->stats[y][rix].total_packets = 0; sn->stats[y][rix].packets_acked = 0; sn->stats[y][rix].last_tx = 0; + sn->stats[y][rix].ewma_pct = 0; sn->stats[y][rix].perfect_tx_time = calc_usecs_unicast_packet(sc, size, rix, 0, 0, @@ -872,27 +1156,120 @@ ath_rate_ctl_reset(struct ath_softc *sc, struct ieee80211_node *ni) #undef DOT11RATE } +/* + * Fetch the statistics for the given node. + * + * The ieee80211 node must be referenced and unlocked, however the ath_node + * must be locked. + * + * The main difference here is that we convert the rate indexes + * to 802.11 rates, or the userland output won't make much sense + * as it has no access to the rix table. + */ +int +ath_rate_fetch_node_stats(struct ath_softc *sc, struct ath_node *an, + struct ath_rateioctl *rs) +{ + struct sample_node *sn = ATH_NODE_SAMPLE(an); + const HAL_RATE_TABLE *rt = sc->sc_currates; + struct ath_rateioctl_tlv av; + struct ath_rateioctl_rt *tv; + int y; + int o = 0; + + ATH_NODE_LOCK_ASSERT(an); + + /* + * Ensure there's enough space for the statistics. + */ + if (rs->len < + sizeof(struct ath_rateioctl_tlv) + + sizeof(struct ath_rateioctl_rt) + + sizeof(struct ath_rateioctl_tlv) + + sizeof(struct sample_node)) { + device_printf(sc->sc_dev, "%s: len=%d, too short\n", + __func__, + rs->len); + return (EINVAL); + } + + /* + * Take a temporary copy of the sample node state so we can + * modify it before we copy it. + */ + tv = malloc(sizeof(struct ath_rateioctl_rt), M_TEMP, + M_NOWAIT | M_ZERO); + if (tv == NULL) { + return (ENOMEM); + } + + /* + * Populate the rate table mapping TLV. + */ + tv->nentries = rt->rateCount; + for (y = 0; y < rt->rateCount; y++) { + tv->ratecode[y] = rt->info[y].dot11Rate & IEEE80211_RATE_VAL; + if (rt->info[y].phy == IEEE80211_T_HT) + tv->ratecode[y] |= IEEE80211_RATE_MCS; + } + + o = 0; + /* + * First TLV - rate code mapping + */ + av.tlv_id = ATH_RATE_TLV_RATETABLE; + av.tlv_len = sizeof(struct ath_rateioctl_rt); + copyout(&av, rs->buf + o, sizeof(struct ath_rateioctl_tlv)); + o += sizeof(struct ath_rateioctl_tlv); + copyout(tv, rs->buf + o, sizeof(struct ath_rateioctl_rt)); + o += sizeof(struct ath_rateioctl_rt); + + /* + * Second TLV - sample node statistics + */ + av.tlv_id = ATH_RATE_TLV_SAMPLENODE; + av.tlv_len = sizeof(struct sample_node); + copyout(&av, rs->buf + o, sizeof(struct ath_rateioctl_tlv)); + o += sizeof(struct ath_rateioctl_tlv); + + /* + * Copy the statistics over to the provided buffer. + */ + copyout(sn, rs->buf + o, sizeof(struct sample_node)); + o += sizeof(struct sample_node); + + free(tv, M_TEMP); + + return (0); +} + static void sample_stats(void *arg, struct ieee80211_node *ni) { struct ath_softc *sc = arg; const HAL_RATE_TABLE *rt = sc->sc_currates; struct sample_node *sn = ATH_NODE_SAMPLE(ATH_NODE(ni)); - uint32_t mask; + uint64_t mask; int rix, y; - printf("\n[%s] refcnt %d static_rix %d ratemask 0x%x\n", + printf("\n[%s] refcnt %d static_rix (%d %s) ratemask 0x%jx\n", ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni), - sn->static_rix, sn->ratemask); + dot11rate(rt, sn->static_rix), + dot11rate_label(rt, sn->static_rix), + (uintmax_t)sn->ratemask); for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) { printf("[%4u] cur rix %d (%d %s) since switch: packets %d ticks %u\n", bin_to_size(y), sn->current_rix[y], dot11rate(rt, sn->current_rix[y]), dot11rate_label(rt, sn->current_rix[y]), sn->packets_since_switch[y], sn->ticks_since_switch[y]); - printf("[%4u] last sample %d cur sample %d packets sent %d\n", - bin_to_size(y), sn->last_sample_rix[y], - sn->current_sample_rix[y], sn->packets_sent[y]); + printf("[%4u] last sample (%d %s) cur sample (%d %s) packets sent %d\n", + bin_to_size(y), + dot11rate(rt, sn->last_sample_rix[y]), + dot11rate_label(rt, sn->last_sample_rix[y]), + dot11rate(rt, sn->current_sample_rix[y]), + dot11rate_label(rt, sn->current_sample_rix[y]), + sn->packets_sent[y]); printf("[%4u] packets since sample %d sample tt %u\n", bin_to_size(y), sn->packets_since_sample[y], sn->sample_tt[y]); @@ -903,13 +1280,16 @@ sample_stats(void *arg, struct ieee80211_node *ni) for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) { if (sn->stats[y][rix].total_packets == 0) continue; - printf("[%2u %s:%4u] %8d:%-8d (%3d%%) T %8d F %4d avg %5u last %u\n", + printf("[%2u %s:%4u] %8ju:%-8ju (%3d%%) (EWMA %3d.%1d%%) T %8ju F %4d avg %5u last %u\n", dot11rate(rt, rix), dot11rate_label(rt, rix), bin_to_size(y), - sn->stats[y][rix].total_packets, - sn->stats[y][rix].packets_acked, - (100*sn->stats[y][rix].packets_acked)/sn->stats[y][rix].total_packets, - sn->stats[y][rix].tries, + (uintmax_t) sn->stats[y][rix].total_packets, + (uintmax_t) sn->stats[y][rix].packets_acked, + (int) ((sn->stats[y][rix].packets_acked * 100ULL) / + sn->stats[y][rix].total_packets), + sn->stats[y][rix].ewma_pct / 10, + sn->stats[y][rix].ewma_pct % 10, + (uintmax_t) sn->stats[y][rix].tries, sn->stats[y][rix].successive_failures, sn->stats[y][rix].average_tx_time, ticks - sn->stats[y][rix].last_tx); @@ -921,8 +1301,7 @@ static int ath_rate_sysctl_stats(SYSCTL_HANDLER_ARGS) { struct ath_softc *sc = arg1; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211com *ic = &sc->sc_ic; int error, v; v = 0; @@ -995,7 +1374,7 @@ ath_rate_attach(struct ath_softc *sc) if (ssc == NULL) return NULL; ssc->arc.arc_space = sizeof(struct sample_node); - ssc->smoothing_rate = 95; /* ewma percentage ([0..99]) */ + ssc->smoothing_rate = 75; /* ewma percentage ([0..99]) */ ssc->smoothing_minpackets = 100 / (100 - ssc->smoothing_rate); ssc->sample_rate = 10; /* %time to try diff tx rates */ ssc->max_successive_failures = 3; /* threshold for rate sampling*/ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_rate/sample/sample.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_rate/sample/sample.h index b39e0be716..51362a3faa 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_rate/sample/sample.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_rate/sample/sample.h @@ -33,7 +33,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/ath_rate/sample/sample.h 287197 2015-08-27 08:56:39Z glebius $ */ /* @@ -51,15 +51,17 @@ struct sample_softc { int max_successive_failures; int stale_failure_timeout; /* how long to honor max_successive_failures */ int min_switch; /* min time between rate changes */ + int min_good_pct; /* min good percentage for a rate to be considered */ }; #define ATH_SOFTC_SAMPLE(sc) ((struct sample_softc *)sc->sc_rc) struct rate_stats { unsigned average_tx_time; int successive_failures; - int tries; - int total_packets; - int packets_acked; + uint64_t tries; + uint64_t total_packets; /* pkts total since assoc */ + uint64_t packets_acked; /* pkts acked since assoc */ + int ewma_pct; /* EWMA percentage */ unsigned perfect_tx_time; /* transmit time for 0 retries */ int last_tx; }; @@ -77,13 +79,23 @@ struct txschedule { */ #define NUM_PACKET_SIZE_BINS 2 +static const int packet_size_bins[NUM_PACKET_SIZE_BINS] = { 250, 1600 }; + +static inline int +bin_to_size(int index) +{ + return packet_size_bins[index]; +} + /* per-node state */ struct sample_node { int static_rix; /* rate index of fixed tx rate */ -#define SAMPLE_MAXRATES 32 /* NB: corresponds to hal info[32] */ - uint32_t ratemask; /* bit mask of valid rate indices */ +#define SAMPLE_MAXRATES 64 /* NB: corresponds to hal info[32] */ + uint64_t ratemask; /* bit mask of valid rate indices */ const struct txschedule *sched; /* tx schedule table */ + const HAL_RATE_TABLE *currates; + struct rate_stats stats[NUM_PACKET_SIZE_BINS][SAMPLE_MAXRATES]; int last_sample_rix[NUM_PACKET_SIZE_BINS]; @@ -97,8 +109,11 @@ struct sample_node { int packets_since_sample[NUM_PACKET_SIZE_BINS]; unsigned sample_tt[NUM_PACKET_SIZE_BINS]; }; + +#ifdef _KERNEL + #define ATH_NODE_SAMPLE(an) ((struct sample_node *)&(an)[1]) -#define IS_RATE_DEFINED(sn, rix) (((sn)->ratemask & (1<<(rix))) != 0) +#define IS_RATE_DEFINED(sn, rix) (((uint64_t) (sn)->ratemask & (1ULL<<((uint64_t) rix))) != 0) #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) @@ -119,8 +134,7 @@ static unsigned calc_usecs_unicast_packet(struct ath_softc *sc, int long_retries, int is_ht40) { const HAL_RATE_TABLE *rt = sc->sc_currates; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211com *ic = &sc->sc_ic; int rts, cts; unsigned t_slot = 20; @@ -221,4 +235,7 @@ static unsigned calc_usecs_unicast_packet(struct ath_softc *sc, } return tt; } + +#endif /* _KERNEL */ + #endif /* _DEV_ATH_RATE_SAMPLE_H */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_rate/sample/tx_schedules.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_rate/sample/tx_schedules.h index dafca9a396..07b7f5dfdf 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_rate/sample/tx_schedules.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/ath_rate/sample/tx_schedules.h @@ -38,9 +38,7 @@ #define __ATH_RATE_SAMPLE_TXSCHEDULES_H__ #include -#ifndef __HAIKU__ -__FBSDID("$FreeBSD$"); -#endif +__FBSDID("$FreeBSD: releng/11.1/sys/dev/ath/ath_rate/sample/tx_schedules.h 239286 2012-08-15 07:50:42Z adrian $"); #define A(_r) \ (((_r) == 6) ? 0 : (((_r) == 9) ? 1 : (((_r) == 12) ? 2 : \ @@ -65,6 +63,10 @@ static const struct txschedule series_11a[] = { (((_r) == 13) ? 16 : (((_r) == 26) ? 17 : (((_r) == 39) ? 18 : \ (((_r) == 52) ? 19 : (((_r) == 78) ? 20 : (((_r) == 104)? 21 : \ (((_r) == 117)? 22 : (((_r) == 130)? 23 : 0)))))))) +#define NA3(_r) \ + (((_r) == 19.5) ? 24 : (((_r) == 39) ? 25 : (((_r) == 58.5) ? 26 : \ + (((_r) == 78) ? 27 : (((_r) == 117) ? 28 : (((_r) == 156) ? 29 : \ + (((_r) == 175.5) ? 30 : (((_r) == 195)? 31 : 0)))))))) static const struct txschedule series_11na[] = { { 3,A( 6), 3,A( 6), 0,A( 6), 0,A( 6) }, /* 6Mb/s */ { 4,A( 9), 3,A( 6), 4,A( 6), 0,A( 6) }, /* 9Mb/s */ @@ -74,6 +76,9 @@ static const struct txschedule series_11na[] = { { 4,A(36), 3,A( 24), 4,A( 18), 2,A( 6) }, /* 36Mb/s */ { 4,A(48), 3,A( 36), 4,A( 24), 2,A(12) }, /* 48Mb/s */ { 4,A(54), 3,A( 48), 4,A( 36), 2,A(24) }, /* 54Mb/s */ + + /* 1 stream rates */ + { 3,NA1( 6.5), 3,NA1( 6.5), 0,NA1( 6.5), 0,NA1(6.5) }, /* 6.5Mb/s */ { 4,NA1( 13), 3,NA1( 6.5), 4,NA1( 6.5), 0,NA1(6.5) }, /* 13Mb/s */ { 4,NA1(19.5), 3,NA1( 6.5), 4,NA1( 6.5), 0,NA1(6.5) }, /*19.5Mb/s */ @@ -82,6 +87,9 @@ static const struct txschedule series_11na[] = { { 4,NA1( 52), 3,NA1( 39), 4,NA1( 26), 2,NA1(6.5) }, /* 52Mb/s */ { 4,NA1(58.5), 3,NA1( 52), 4,NA1( 39), 2,NA1( 13) }, /*58.5Mb/s */ { 4,NA1( 65), 3,NA1(58.5), 4,NA1( 52), 2,NA1( 13) }, /* 65Mb/s */ + + /* 2 stream rates */ + { 3,NA2( 13), 3,NA2( 13), 0,NA2( 13), 0,NA2( 13) }, /* 13Mb/s */ { 4,NA2( 26), 3,NA2( 13), 4,NA2( 13), 0,NA2( 13) }, /* 26Mb/s */ { 4,NA2( 39), 3,NA2( 26), 4,NA2( 13), 2,NA2( 13) }, /* 39Mb/s */ @@ -89,9 +97,21 @@ static const struct txschedule series_11na[] = { { 4,NA2( 78), 3,NA2( 52), 4,NA2( 39), 2,NA2( 13) }, /* 78Mb/s */ { 4,NA2( 104), 3,NA2( 78), 4,NA2( 52), 2,NA2( 13) }, /* 104Mb/s */ { 4,NA2( 117), 3,NA2( 104), 4,NA2( 78), 2,NA2( 26) }, /* 117Mb/s */ - { 4,NA2( 130), 3,NA2( 117), 4,NA2( 104), 2,NA2( 26) } /* 130Mb/s */ + { 4,NA2( 130), 3,NA2( 117), 4,NA2( 104), 2,NA2( 26) }, /* 130Mb/s */ + + /* 3 stream rates */ + + { 3,NA3(19.5), 3,NA3(19.5), 0,NA3(19.5), 0,NA3(19.5) }, /* 19Mb/s */ + { 3,NA3( 39), 3,NA3(19.5), 0,NA3(19.5), 0,NA3(19.5) }, /* 39Mb/s */ + { 3,NA3(58.5), 3,NA3( 39), 0,NA3(19.5), 0,NA3(19.5) }, /* 58Mb/s */ + { 3,NA3( 78), 3,NA3(58.5), 0,NA3( 39), 0,NA3(19.5) }, /* 78Mb/s */ + { 3,NA3( 117), 3,NA3( 78), 0,NA3(58.5), 0,NA3(19.5) }, /* 117Mb/s */ + { 3,NA3( 156), 3,NA3( 117), 0,NA3( 78), 0,NA3(19.5) }, /* 156Mb/s */ + { 3,NA3(175.5), 3,NA3( 156), 0,NA3( 117), 0,NA3( 39) }, /* 175Mb/s */ + { 3,NA3( 195), 3,NA3( 195), 0,NA3( 156), 0,NA3(58.5) }, /* 195Mb/s */ }; #undef A +#undef NA3 #undef NA2 #undef NA1 @@ -123,6 +143,11 @@ static const struct txschedule series_11g[] = { (((_r) == 13) ? 20 : (((_r) == 26) ? 21 : (((_r) == 39) ? 22 : \ (((_r) == 52) ? 23 : (((_r) == 78) ? 24 : (((_r) == 104) ? 25 : \ (((_r) == 117) ? 26 : (((_r) == 130)? 27 : 0)))))))) +#define NG3(_r) \ + (((_r) == 19.5) ? 28 : (((_r) == 39) ? 29 : (((_r) == 58.5) ? 30 : \ + (((_r) == 78) ? 31 : (((_r) == 117) ? 32 : (((_r) == 156) ? 33 : \ + (((_r) == 175.5) ? 34 : (((_r) == 195)? 35 : 0)))))))) + static const struct txschedule series_11ng[] = { { 3,G( 1), 3,G( 1), 0,G( 1), 0,G( 1) }, /* 1Mb/s */ { 4,G( 2), 3,G( 1), 4,G( 1), 0,G( 1) }, /* 2Mb/s */ @@ -136,6 +161,9 @@ static const struct txschedule series_11ng[] = { { 4,G(36), 3,G( 24), 4,G( 18), 2,G( 1) }, /* 36Mb/s */ { 4,G(48), 3,G( 36), 4,G( 24), 2,G( 1) }, /* 48Mb/s */ { 4,G(54), 3,G( 48), 4,G( 36), 2,G( 1) }, /* 54Mb/s */ + + /* 1 stream rates */ + { 3,NG1( 6.5), 3,NG1( 6.5), 0,NG1( 6.5), 0,NG1(6.5) }, /* 6.5Mb/s */ { 4,NG1( 13), 3,NG1( 6.5), 4,NG1( 6.5), 0,NG1(6.5) }, /* 13Mb/s */ { 4,NG1(19.5), 3,NG1( 6.5), 4,NG1( 6.5), 0,NG1(6.5) }, /*19.5Mb/s */ @@ -144,6 +172,9 @@ static const struct txschedule series_11ng[] = { { 4,NG1( 52), 3,NG1( 39), 4,NG1( 26), 2,NG1(6.5) }, /* 52Mb/s */ { 4,NG1(58.5), 3,NG1( 52), 4,NG1( 39), 2,NG1( 13) }, /*58.5Mb/s */ { 4,NG1( 65), 3,NG1(58.5), 4,NG1( 52), 2,NG1( 13) }, /* 65Mb/s */ + + /* 2 stream rates */ + { 3,NG2( 13), 3,NG2( 13), 0,NG2( 13), 0,NG2( 13) }, /* 13Mb/s */ { 4,NG2( 26), 3,NG2( 13), 4,NG2( 13), 0,NG2( 13) }, /* 26Mb/s */ { 4,NG2( 39), 3,NG2( 26), 4,NG2( 13), 2,NG2( 13) }, /* 39Mb/s */ @@ -151,9 +182,22 @@ static const struct txschedule series_11ng[] = { { 4,NG2( 78), 3,NG2( 52), 4,NG2( 39), 2,NG2( 13) }, /* 78Mb/s */ { 4,NG2( 104), 3,NG2( 78), 4,NG2( 52), 2,NG2( 13) }, /* 104Mb/s */ { 4,NG2( 117), 3,NG2( 104), 4,NG2( 78), 2,NG2( 26) }, /* 117Mb/s */ - { 4,NG2( 130), 3,NG2( 117), 4,NG2( 104), 2,NG2( 26) } /* 130Mb/s */ + { 4,NG2( 130), 3,NG2( 117), 4,NG2( 104), 2,NG2( 26) }, /* 130Mb/s */ + + /* 3 stream rates */ + + { 3,NG3(19.5), 3,NG3(19.5), 0,NG3(19.5), 0,NG3(19.5) }, /* 19Mb/s */ + { 3,NG3( 39), 3,NG3(19.5), 0,NG3(19.5), 0,NG3(19.5) }, /* 39Mb/s */ + { 3,NG3(58.5), 3,NG3( 39), 0,NG3(19.5), 0,NG3(19.5) }, /* 58Mb/s */ + { 3,NG3( 78), 3,NG3(58.5), 0,NG3( 39), 0,NG3(19.5) }, /* 78Mb/s */ + { 3,NG3( 117), 3,NG3( 78), 0,NG3(58.5), 0,NG3(19.5) }, /* 117Mb/s */ + { 3,NG3( 156), 3,NG3( 117), 0,NG3( 78), 0,NG3(19.5) }, /* 156Mb/s */ + { 3,NG3(175.5), 3,NG3( 156), 0,NG3( 117), 0,NG3( 39) }, /* 175Mb/s */ + { 3,NG3( 195), 3,NG3( 195), 0,NG3( 156), 0,NG3(58.5) }, /* 195Mb/s */ + }; #undef G +#undef NG3 #undef NG2 #undef NG1 diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath.c index 1afc34ec84..ee96aaee4a 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath.c @@ -28,7 +28,7 @@ */ #include -__FBSDID("$FreeBSD$"); +__FBSDID("$FreeBSD: releng/11.1/sys/dev/ath/if_ath.c 301181 2016-06-02 00:51:36Z adrian $"); /* * Driver for the Atheros Wireless LAN controller. @@ -39,6 +39,14 @@ __FBSDID("$FreeBSD$"); #include "opt_inet.h" #include "opt_ath.h" +/* + * This is needed for register operations which are performed + * by the driver - eg, calls to ath_hal_gettsf32(). + * + * It's also required for any AH_DEBUG checks in here, eg the + * module dependencies. + */ +#include "opt_ah.h" #include "opt_wlan.h" #include @@ -59,10 +67,13 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include +#include /* for mp_ncpus */ #include #include +#include #include #include #include @@ -92,15 +103,35 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include #ifdef ATH_TX99_DIAG #include #endif +#ifdef ATH_DEBUG_ALQ +#include +#endif + +/* + * Only enable this if you're working on PS-POLL support. + */ +#define ATH_SW_PSQ /* * ATH_BCBUF determines the number of vap's that can transmit @@ -123,67 +154,51 @@ static struct ieee80211vap *ath_vap_create(struct ieee80211com *, const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN]); static void ath_vap_delete(struct ieee80211vap *); -static void ath_init(void *); -static void ath_stop_locked(struct ifnet *); -static void ath_stop(struct ifnet *); -static void ath_start(struct ifnet *); +static int ath_init(struct ath_softc *); +static void ath_stop(struct ath_softc *); static int ath_reset_vap(struct ieee80211vap *, u_long); +static int ath_transmit(struct ieee80211com *, struct mbuf *); static int ath_media_change(struct ifnet *); static void ath_watchdog(void *); -static int ath_ioctl(struct ifnet *, u_long, caddr_t); +static void ath_parent(struct ieee80211com *); static void ath_fatal_proc(void *, int); static void ath_bmiss_vap(struct ieee80211vap *); static void ath_bmiss_proc(void *, int); static void ath_key_update_begin(struct ieee80211vap *); static void ath_key_update_end(struct ieee80211vap *); -static void ath_update_mcast(struct ifnet *); -static void ath_update_promisc(struct ifnet *); -static void ath_mode_init(struct ath_softc *); -static void ath_setslottime(struct ath_softc *); -static void ath_updateslot(struct ifnet *); -static int ath_beaconq_setup(struct ath_hal *); -static int ath_beacon_alloc(struct ath_softc *, struct ieee80211_node *); -static void ath_beacon_update(struct ieee80211vap *, int item); -static void ath_beacon_setup(struct ath_softc *, struct ath_buf *); -static void ath_beacon_proc(void *, int); -static struct ath_buf *ath_beacon_generate(struct ath_softc *, - struct ieee80211vap *); +static void ath_update_mcast_hw(struct ath_softc *); +static void ath_update_mcast(struct ieee80211com *); +static void ath_update_promisc(struct ieee80211com *); +static void ath_updateslot(struct ieee80211com *); static void ath_bstuck_proc(void *, int); -static void ath_beacon_return(struct ath_softc *, struct ath_buf *); -static void ath_beacon_free(struct ath_softc *); -static void ath_beacon_config(struct ath_softc *, struct ieee80211vap *); -static void ath_descdma_cleanup(struct ath_softc *sc, - struct ath_descdma *, ath_bufhead *); +static void ath_reset_proc(void *, int); static int ath_desc_alloc(struct ath_softc *); static void ath_desc_free(struct ath_softc *); static struct ieee80211_node *ath_node_alloc(struct ieee80211vap *, const uint8_t [IEEE80211_ADDR_LEN]); +static void ath_node_cleanup(struct ieee80211_node *); static void ath_node_free(struct ieee80211_node *); static void ath_node_getsignal(const struct ieee80211_node *, int8_t *, int8_t *); -static int ath_rxbuf_init(struct ath_softc *, struct ath_buf *); -static void ath_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, - int subtype, int rssi, int nf); -static void ath_setdefantenna(struct ath_softc *, u_int); -static void ath_rx_proc(void *, int); static void ath_txq_init(struct ath_softc *sc, struct ath_txq *, int); static struct ath_txq *ath_txq_setup(struct ath_softc*, int qtype, int subtype); static int ath_tx_setup(struct ath_softc *, int, int); -static int ath_wme_update(struct ieee80211com *); static void ath_tx_cleanupq(struct ath_softc *, struct ath_txq *); static void ath_tx_cleanup(struct ath_softc *); +static int ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq, + int dosched); static void ath_tx_proc_q0(void *, int); static void ath_tx_proc_q0123(void *, int); static void ath_tx_proc(void *, int); -static void ath_tx_draintxq(struct ath_softc *, struct ath_txq *); +static void ath_txq_sched_tasklet(void *, int); static int ath_chan_set(struct ath_softc *, struct ieee80211_channel *); -static void ath_draintxq(struct ath_softc *); -static void ath_stoprecv(struct ath_softc *); -static int ath_startrecv(struct ath_softc *); static void ath_chan_change(struct ath_softc *, struct ieee80211_channel *); static void ath_scan_start(struct ieee80211com *); static void ath_scan_end(struct ieee80211com *); static void ath_set_channel(struct ieee80211com *); +#ifdef ATH_ENABLE_11N +static void ath_update_chw(struct ieee80211com *); +#endif /* ATH_ENABLE_11N */ static void ath_calibrate(void *); static int ath_newstate(struct ieee80211vap *, enum ieee80211_state, int); static void ath_setup_stationkey(struct ieee80211_node *); @@ -194,7 +209,6 @@ static int ath_setregdomain(struct ieee80211com *, static void ath_getradiocaps(struct ieee80211com *, int, int *, struct ieee80211_channel []); static int ath_getchannels(struct ath_softc *); -static void ath_led_event(struct ath_softc *, int); static int ath_rate_setup(struct ath_softc *, u_int mode); static void ath_setcurmode(struct ath_softc *, enum ieee80211_phymode); @@ -202,32 +216,13 @@ static void ath_setcurmode(struct ath_softc *, enum ieee80211_phymode); static void ath_announce(struct ath_softc *); static void ath_dfs_tasklet(void *, int); +static void ath_node_powersave(struct ieee80211_node *, int); +static int ath_node_set_tim(struct ieee80211_node *, int); +static void ath_node_recv_pspoll(struct ieee80211_node *, struct mbuf *); #ifdef IEEE80211_SUPPORT_TDMA -static void ath_tdma_settimers(struct ath_softc *sc, u_int32_t nexttbtt, - u_int32_t bintval); -static void ath_tdma_bintvalsetup(struct ath_softc *sc, - const struct ieee80211_tdma_state *tdma); -static void ath_tdma_config(struct ath_softc *sc, struct ieee80211vap *vap); -static void ath_tdma_update(struct ieee80211_node *ni, - const struct ieee80211_tdma_param *tdma, int); -static void ath_tdma_beacon_send(struct ath_softc *sc, - struct ieee80211vap *vap); - -#define TDMA_EP_MULTIPLIER (1<<10) /* pow2 to optimize out * and / */ -#define TDMA_LPF_LEN 6 -#define TDMA_DUMMY_MARKER 0x127 -#define TDMA_EP_MUL(x, mul) ((x) * (mul)) -#define TDMA_IN(x) (TDMA_EP_MUL((x), TDMA_EP_MULTIPLIER)) -#define TDMA_LPF(x, y, len) \ - ((x != TDMA_DUMMY_MARKER) ? (((x) * ((len)-1) + (y)) / (len)) : (y)) -#define TDMA_SAMPLE(x, y) do { \ - x = TDMA_LPF((x), TDMA_IN(y), TDMA_LPF_LEN); \ -} while (0) -#define TDMA_EP_RND(x,mul) \ - ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul)) -#define TDMA_AVG(x) TDMA_EP_RND(x, TDMA_EP_MULTIPLIER) -#endif /* IEEE80211_SUPPORT_TDMA */ +#include +#endif SYSCTL_DECL(_hw_ath); @@ -245,20 +240,336 @@ static int ath_anicalinterval = 100; /* ANI calibration - 100 msec */ SYSCTL_INT(_hw_ath, OID_AUTO, anical, CTLFLAG_RW, &ath_anicalinterval, 0, "ANI calibration (msecs)"); -static int ath_rxbuf = ATH_RXBUF; /* # rx buffers to allocate */ -SYSCTL_INT(_hw_ath, OID_AUTO, rxbuf, CTLFLAG_RW, &ath_rxbuf, +int ath_rxbuf = ATH_RXBUF; /* # rx buffers to allocate */ +SYSCTL_INT(_hw_ath, OID_AUTO, rxbuf, CTLFLAG_RWTUN, &ath_rxbuf, 0, "rx buffers allocated"); -TUNABLE_INT("hw.ath.rxbuf", &ath_rxbuf); -static int ath_txbuf = ATH_TXBUF; /* # tx buffers to allocate */ -SYSCTL_INT(_hw_ath, OID_AUTO, txbuf, CTLFLAG_RW, &ath_txbuf, +int ath_txbuf = ATH_TXBUF; /* # tx buffers to allocate */ +SYSCTL_INT(_hw_ath, OID_AUTO, txbuf, CTLFLAG_RWTUN, &ath_txbuf, 0, "tx buffers allocated"); -TUNABLE_INT("hw.ath.txbuf", &ath_txbuf); +int ath_txbuf_mgmt = ATH_MGMT_TXBUF; /* # mgmt tx buffers to allocate */ +SYSCTL_INT(_hw_ath, OID_AUTO, txbuf_mgmt, CTLFLAG_RWTUN, &ath_txbuf_mgmt, + 0, "tx (mgmt) buffers allocated"); -static int ath_bstuck_threshold = 4; /* max missed beacons */ +int ath_bstuck_threshold = 4; /* max missed beacons */ SYSCTL_INT(_hw_ath, OID_AUTO, bstuck, CTLFLAG_RW, &ath_bstuck_threshold, 0, "max missed beacon xmits before chip reset"); -static MALLOC_DEFINE(M_ATHDEV, "athdev", "ath driver dma buffers"); +MALLOC_DEFINE(M_ATHDEV, "athdev", "ath driver dma buffers"); + +void +ath_legacy_attach_comp_func(struct ath_softc *sc) +{ + + /* + * Special case certain configurations. Note the + * CAB queue is handled by these specially so don't + * include them when checking the txq setup mask. + */ + switch (sc->sc_txqsetup &~ (1<sc_cabq->axq_qnum)) { + case 0x01: + TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc_q0, sc); + break; + case 0x0f: + TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc_q0123, sc); + break; + default: + TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc, sc); + break; + } +} + +/* + * Set the target power mode. + * + * If this is called during a point in time where + * the hardware is being programmed elsewhere, it will + * simply store it away and update it when all current + * uses of the hardware are completed. + */ +void +_ath_power_setpower(struct ath_softc *sc, int power_state, const char *file, int line) +{ + ATH_LOCK_ASSERT(sc); + + sc->sc_target_powerstate = power_state; + + DPRINTF(sc, ATH_DEBUG_PWRSAVE, "%s: (%s:%d) state=%d, refcnt=%d\n", + __func__, + file, + line, + power_state, + sc->sc_powersave_refcnt); + + if (sc->sc_powersave_refcnt == 0 && + power_state != sc->sc_cur_powerstate) { + sc->sc_cur_powerstate = power_state; + ath_hal_setpower(sc->sc_ah, power_state); + + /* + * If the NIC is force-awake, then set the + * self-gen frame state appropriately. + * + * If the nic is in network sleep or full-sleep, + * we let the above call leave the self-gen + * state as "sleep". + */ + if (sc->sc_cur_powerstate == HAL_PM_AWAKE && + sc->sc_target_selfgen_state != HAL_PM_AWAKE) { + ath_hal_setselfgenpower(sc->sc_ah, + sc->sc_target_selfgen_state); + } + } +} + +/* + * Set the current self-generated frames state. + * + * This is separate from the target power mode. The chip may be + * awake but the desired state is "sleep", so frames sent to the + * destination has PWRMGT=1 in the 802.11 header. The NIC also + * needs to know to set PWRMGT=1 in self-generated frames. + */ +void +_ath_power_set_selfgen(struct ath_softc *sc, int power_state, const char *file, int line) +{ + + ATH_LOCK_ASSERT(sc); + + DPRINTF(sc, ATH_DEBUG_PWRSAVE, "%s: (%s:%d) state=%d, refcnt=%d\n", + __func__, + file, + line, + power_state, + sc->sc_target_selfgen_state); + + sc->sc_target_selfgen_state = power_state; + + /* + * If the NIC is force-awake, then set the power state. + * Network-state and full-sleep will already transition it to + * mark self-gen frames as sleeping - and we can't + * guarantee the NIC is awake to program the self-gen frame + * setting anyway. + */ + if (sc->sc_cur_powerstate == HAL_PM_AWAKE) { + ath_hal_setselfgenpower(sc->sc_ah, power_state); + } +} + +/* + * Set the hardware power mode and take a reference. + * + * This doesn't update the target power mode in the driver; + * it just updates the hardware power state. + * + * XXX it should only ever force the hardware awake; it should + * never be called to set it asleep. + */ +void +_ath_power_set_power_state(struct ath_softc *sc, int power_state, const char *file, int line) +{ + ATH_LOCK_ASSERT(sc); + + DPRINTF(sc, ATH_DEBUG_PWRSAVE, "%s: (%s:%d) state=%d, refcnt=%d\n", + __func__, + file, + line, + power_state, + sc->sc_powersave_refcnt); + + sc->sc_powersave_refcnt++; + + if (power_state != sc->sc_cur_powerstate) { + ath_hal_setpower(sc->sc_ah, power_state); + sc->sc_cur_powerstate = power_state; + + /* + * Adjust the self-gen powerstate if appropriate. + */ + if (sc->sc_cur_powerstate == HAL_PM_AWAKE && + sc->sc_target_selfgen_state != HAL_PM_AWAKE) { + ath_hal_setselfgenpower(sc->sc_ah, + sc->sc_target_selfgen_state); + } + + } +} + +/* + * Restore the power save mode to what it once was. + * + * This will decrement the reference counter and once it hits + * zero, it'll restore the powersave state. + */ +void +_ath_power_restore_power_state(struct ath_softc *sc, const char *file, int line) +{ + + ATH_LOCK_ASSERT(sc); + + DPRINTF(sc, ATH_DEBUG_PWRSAVE, "%s: (%s:%d) refcnt=%d, target state=%d\n", + __func__, + file, + line, + sc->sc_powersave_refcnt, + sc->sc_target_powerstate); + + if (sc->sc_powersave_refcnt == 0) + device_printf(sc->sc_dev, "%s: refcnt=0?\n", __func__); + else + sc->sc_powersave_refcnt--; + + if (sc->sc_powersave_refcnt == 0 && + sc->sc_target_powerstate != sc->sc_cur_powerstate) { + sc->sc_cur_powerstate = sc->sc_target_powerstate; + ath_hal_setpower(sc->sc_ah, sc->sc_target_powerstate); + } + + /* + * Adjust the self-gen powerstate if appropriate. + */ + if (sc->sc_cur_powerstate == HAL_PM_AWAKE && + sc->sc_target_selfgen_state != HAL_PM_AWAKE) { + ath_hal_setselfgenpower(sc->sc_ah, + sc->sc_target_selfgen_state); + } + +} + +/* + * Configure the initial HAL configuration values based on bus + * specific parameters. + * + * Some PCI IDs and other information may need tweaking. + * + * XXX TODO: ath9k and the Atheros HAL only program comm2g_switch_enable + * if BT antenna diversity isn't enabled. + * + * So, let's also figure out how to enable BT diversity for AR9485. + */ +static void +ath_setup_hal_config(struct ath_softc *sc, HAL_OPS_CONFIG *ah_config) +{ + /* XXX TODO: only for PCI devices? */ + + if (sc->sc_pci_devinfo & (ATH_PCI_CUS198 | ATH_PCI_CUS230)) { + ah_config->ath_hal_ext_lna_ctl_gpio = 0x200; /* bit 9 */ + ah_config->ath_hal_ext_atten_margin_cfg = AH_TRUE; + ah_config->ath_hal_min_gainidx = AH_TRUE; + ah_config->ath_hal_ant_ctrl_comm2g_switch_enable = 0x000bbb88; + /* XXX low_rssi_thresh */ + /* XXX fast_div_bias */ + device_printf(sc->sc_dev, "configuring for %s\n", + (sc->sc_pci_devinfo & ATH_PCI_CUS198) ? + "CUS198" : "CUS230"); + } + + if (sc->sc_pci_devinfo & ATH_PCI_CUS217) + device_printf(sc->sc_dev, "CUS217 card detected\n"); + + if (sc->sc_pci_devinfo & ATH_PCI_CUS252) + device_printf(sc->sc_dev, "CUS252 card detected\n"); + + if (sc->sc_pci_devinfo & ATH_PCI_AR9565_1ANT) + device_printf(sc->sc_dev, "WB335 1-ANT card detected\n"); + + if (sc->sc_pci_devinfo & ATH_PCI_AR9565_2ANT) + device_printf(sc->sc_dev, "WB335 2-ANT card detected\n"); + + if (sc->sc_pci_devinfo & ATH_PCI_BT_ANT_DIV) + device_printf(sc->sc_dev, + "Bluetooth Antenna Diversity card detected\n"); + + if (sc->sc_pci_devinfo & ATH_PCI_KILLER) + device_printf(sc->sc_dev, "Killer Wireless card detected\n"); + +#if 0 + /* + * Some WB335 cards do not support antenna diversity. Since + * we use a hardcoded value for AR9565 instead of using the + * EEPROM/OTP data, remove the combining feature from + * the HW capabilities bitmap. + */ + if (sc->sc_pci_devinfo & (ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_AR9565_2ANT)) { + if (!(sc->sc_pci_devinfo & ATH9K_PCI_BT_ANT_DIV)) + pCap->hw_caps &= ~ATH9K_HW_CAP_ANT_DIV_COMB; + } + + if (sc->sc_pci_devinfo & ATH9K_PCI_BT_ANT_DIV) { + pCap->hw_caps |= ATH9K_HW_CAP_BT_ANT_DIV; + device_printf(sc->sc_dev, "Set BT/WLAN RX diversity capability\n"); + } +#endif + + if (sc->sc_pci_devinfo & ATH_PCI_D3_L1_WAR) { + ah_config->ath_hal_pcie_waen = 0x0040473b; + device_printf(sc->sc_dev, "Enable WAR for ASPM D3/L1\n"); + } + +#if 0 + if (sc->sc_pci_devinfo & ATH9K_PCI_NO_PLL_PWRSAVE) { + ah->config.no_pll_pwrsave = true; + device_printf(sc->sc_dev, "Disable PLL PowerSave\n"); + } +#endif + +} + +/* + * Attempt to fetch the MAC address from the kernel environment. + * + * Returns 0, macaddr in macaddr if successful; -1 otherwise. + */ +static int +ath_fetch_mac_kenv(struct ath_softc *sc, uint8_t *macaddr) +{ + char devid_str[32]; + int local_mac = 0; + char *local_macstr; + + /* + * Fetch from the kenv rather than using hints. + * + * Hints would be nice but the transition to dynamic + * hints/kenv doesn't happen early enough for this + * to work reliably (eg on anything embedded.) + */ + snprintf(devid_str, 32, "hint.%s.%d.macaddr", + device_get_name(sc->sc_dev), + device_get_unit(sc->sc_dev)); + +#ifndef __HAIKU__ + if ((local_macstr = kern_getenv(devid_str)) != NULL) { + uint32_t tmpmac[ETHER_ADDR_LEN]; + int count; + int i; + + /* Have a MAC address; should use it */ + device_printf(sc->sc_dev, + "Overriding MAC address from environment: '%s'\n", + local_macstr); + + /* Extract out the MAC address */ + count = sscanf(local_macstr, "%x%*c%x%*c%x%*c%x%*c%x%*c%x", + &tmpmac[0], &tmpmac[1], + &tmpmac[2], &tmpmac[3], + &tmpmac[4], &tmpmac[5]); + if (count == 6) { + /* Valid! */ + local_mac = 1; + for (i = 0; i < ETHER_ADDR_LEN; i++) + macaddr[i] = tmpmac[i]; + } + /* Done! */ + freeenv(local_macstr); + local_macstr = NULL; + } +#endif + + if (local_mac) + return (0); + return (-1); +} #define HAL_MODE_HT20 (HAL_MODE_11NG_HT20 | HAL_MODE_11NA_HT20) #define HAL_MODE_HT40 \ @@ -267,32 +578,33 @@ static MALLOC_DEFINE(M_ATHDEV, "athdev", "ath driver dma buffers"); int ath_attach(u_int16_t devid, struct ath_softc *sc) { - struct ifnet *ifp; - struct ieee80211com *ic; + struct ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = NULL; HAL_STATUS status; int error = 0, i; u_int wmodes; - uint8_t macaddr[IEEE80211_ADDR_LEN]; + int rx_chainmask, tx_chainmask; + HAL_OPS_CONFIG ah_config; DPRINTF(sc, ATH_DEBUG_ANY, "%s: devid 0x%x\n", __func__, devid); - ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); - if (ifp == NULL) { - device_printf(sc->sc_dev, "can not if_alloc()\n"); - error = ENOSPC; - goto bad; - } - ic = ifp->if_l2com; + ic->ic_softc = sc; + ic->ic_name = device_get_nameunit(sc->sc_dev); - /* set these up early for if_printf use */ - if_initname(ifp, device_get_name(sc->sc_dev), - device_get_unit(sc->sc_dev)); + /* + * Configure the initial configuration data. + * + * This is stuff that may be needed early during attach + * rather than done via configuration calls later. + */ + bzero(&ah_config, sizeof(ah_config)); + ath_setup_hal_config(sc, &ah_config); - ah = ath_hal_attach(devid, sc, sc->sc_st, sc->sc_sh, sc->sc_eepromdata, &status); + ah = ath_hal_attach(devid, sc, sc->sc_st, sc->sc_sh, + sc->sc_eepromdata, &ah_config, &status); if (ah == NULL) { - if_printf(ifp, "unable to attach hardware; HAL status %u\n", - status); + device_printf(sc->sc_dev, + "unable to attach hardware; HAL status %u\n", status); error = ENXIO; goto bad; } @@ -302,6 +614,25 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) sc->sc_debug = ath_debug; #endif + /* + * Setup the DMA/EDMA functions based on the current + * hardware support. + * + * This is required before the descriptors are allocated. + */ + if (ath_hal_hasedma(sc->sc_ah)) { + sc->sc_isedma = 1; + ath_recv_setup_edma(sc); + ath_xmit_setup_edma(sc); + } else { + ath_recv_setup_legacy(sc); + ath_xmit_setup_legacy(sc); + } + + if (ath_hal_hasmybeacon(sc->sc_ah)) { + sc->sc_do_mybeacon = 1; + } + /* * Check if the MAC has multi-rate retry support. * We do this by trying to setup a fake extended @@ -324,8 +655,9 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) */ sc->sc_keymax = ath_hal_keycachesize(ah); if (sc->sc_keymax > ATH_KEYMAX) { - if_printf(ifp, "Warning, using only %u of %u key cache slots\n", - ATH_KEYMAX, sc->sc_keymax); + device_printf(sc->sc_dev, + "Warning, using only %u of %u key cache slots\n", + ATH_KEYMAX, sc->sc_keymax); sc->sc_keymax = ATH_KEYMAX; } /* @@ -360,13 +692,31 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) ath_setcurmode(sc, IEEE80211_MODE_11A); /* - * Allocate tx+rx descriptors and populate the lists. + * Allocate TX descriptors and populate the lists. */ error = ath_desc_alloc(sc); if (error != 0) { - if_printf(ifp, "failed to allocate descriptors: %d\n", error); + device_printf(sc->sc_dev, + "failed to allocate TX descriptors: %d\n", error); goto bad; } + error = ath_txdma_setup(sc); + if (error != 0) { + device_printf(sc->sc_dev, + "failed to allocate TX descriptors: %d\n", error); + goto bad; + } + + /* + * Allocate RX descriptors and populate the lists. + */ + error = ath_rxdma_setup(sc); + if (error != 0) { + device_printf(sc->sc_dev, + "failed to allocate RX descriptors: %d\n", error); + goto bad; + } + callout_init_mtx(&sc->sc_cal_ch, &sc->sc_mtx, 0); callout_init_mtx(&sc->sc_wd_ch, &sc->sc_mtx, 0); @@ -374,12 +724,15 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) sc->sc_tq = taskqueue_create("ath_taskq", M_NOWAIT, taskqueue_thread_enqueue, &sc->sc_tq); - taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, - "%s taskq", ifp->if_xname); + taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq", + device_get_nameunit(sc->sc_dev)); - TASK_INIT(&sc->sc_rxtask, 0, ath_rx_proc, sc); + TASK_INIT(&sc->sc_rxtask, 0, sc->sc_rx.recv_tasklet, sc); TASK_INIT(&sc->sc_bmisstask, 0, ath_bmiss_proc, sc); TASK_INIT(&sc->sc_bstucktask,0, ath_bstuck_proc, sc); + TASK_INIT(&sc->sc_resettask,0, ath_reset_proc, sc); + TASK_INIT(&sc->sc_txqtask, 0, ath_txq_sched_tasklet, sc); + TASK_INIT(&sc->sc_fataltask, 0, ath_fatal_proc, sc); /* * Allocate hardware transmit queues: one queue for @@ -389,22 +742,24 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) * * XXX PS-Poll */ - sc->sc_bhalq = ath_beaconq_setup(ah); + sc->sc_bhalq = ath_beaconq_setup(sc); if (sc->sc_bhalq == (u_int) -1) { - if_printf(ifp, "unable to setup a beacon xmit queue!\n"); + device_printf(sc->sc_dev, + "unable to setup a beacon xmit queue!\n"); error = EIO; goto bad2; } sc->sc_cabq = ath_txq_setup(sc, HAL_TX_QUEUE_CAB, 0); if (sc->sc_cabq == NULL) { - if_printf(ifp, "unable to setup CAB xmit queue!\n"); + device_printf(sc->sc_dev, "unable to setup CAB xmit queue!\n"); error = EIO; goto bad2; } /* NB: insure BK queue is the lowest priority h/w queue */ if (!ath_tx_setup(sc, WME_AC_BK, HAL_WME_AC_BK)) { - if_printf(ifp, "unable to setup xmit queue for %s traffic!\n", - ieee80211_wme_acnames[WME_AC_BK]); + device_printf(sc->sc_dev, + "unable to setup xmit queue for %s traffic!\n", + ieee80211_wme_acnames[WME_AC_BK]); error = EIO; goto bad2; } @@ -428,21 +783,12 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) } /* - * Special case certain configurations. Note the - * CAB queue is handled by these specially so don't - * include them when checking the txq setup mask. + * Attach the TX completion function. + * + * The non-EDMA chips may have some special case optimisations; + * this method gives everyone a chance to attach cleanly. */ - switch (sc->sc_txqsetup &~ (1<sc_cabq->axq_qnum)) { - case 0x01: - TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc_q0, sc); - break; - case 0x0f: - TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc_q0123, sc); - break; - default: - TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc, sc); - break; - } + sc->sc_tx.xmit_attach_comp_func(sc); /* * Setup rate control. Some rate control modules @@ -459,7 +805,32 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) /* Attach DFS module */ if (! ath_dfs_attach(sc)) { - device_printf(sc->sc_dev, "%s: unable to attach DFS\n", __func__); + device_printf(sc->sc_dev, + "%s: unable to attach DFS\n", __func__); + error = EIO; + goto bad2; + } + + /* Attach spectral module */ + if (ath_spectral_attach(sc) < 0) { + device_printf(sc->sc_dev, + "%s: unable to attach spectral\n", __func__); + error = EIO; + goto bad2; + } + + /* Attach bluetooth coexistence module */ + if (ath_btcoex_attach(sc) < 0) { + device_printf(sc->sc_dev, + "%s: unable to attach bluetooth coexistence\n", __func__); + error = EIO; + goto bad2; + } + + /* Attach LNA diversity module */ + if (ath_lna_div_attach(sc) < 0) { + device_printf(sc->sc_dev, + "%s: unable to attach LNA diversity\n", __func__); error = EIO; goto bad2; } @@ -467,33 +838,36 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) /* Start DFS processing tasklet */ TASK_INIT(&sc->sc_dfstask, 0, ath_dfs_tasklet, sc); + /* Configure LED state */ sc->sc_blinking = 0; sc->sc_ledstate = 1; sc->sc_ledon = 0; /* low true */ sc->sc_ledidle = (2700*hz)/1000; /* 2.7sec */ - callout_init(&sc->sc_ledtimer, CALLOUT_MPSAFE); + callout_init(&sc->sc_ledtimer, 1); + + /* + * Don't setup hardware-based blinking. + * + * Although some NICs may have this configured in the + * default reset register values, the user may wish + * to alter which pins have which function. + * + * The reference driver attaches the MAC network LED to GPIO1 and + * the MAC power LED to GPIO2. However, the DWA-552 cardbus + * NIC has these reversed. + */ + sc->sc_hardled = (1 == 0); + sc->sc_led_net_pin = -1; + sc->sc_led_pwr_pin = -1; /* * Auto-enable soft led processing for IBM cards and for * 5211 minipci cards. Users can also manually enable/disable * support with a sysctl. */ sc->sc_softled = (devid == AR5212_DEVID_IBM || devid == AR5211_DEVID); - if (sc->sc_softled) { - ath_hal_gpioCfgOutput(ah, sc->sc_ledpin, - HAL_GPIO_MUX_MAC_NETWORK_LED); - ath_hal_gpioset(ah, sc->sc_ledpin, !sc->sc_ledon); - } + ath_led_config(sc); + ath_hal_setledstate(ah, HAL_LED_INIT); - ifp->if_softc = sc; - ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; - ifp->if_start = ath_start; - ifp->if_ioctl = ath_ioctl; - ifp->if_init = ath_init; - IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); - ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; - IFQ_SET_READY(&ifp->if_snd); - - ic->ic_ifp = ifp; /* XXX not right but it's not used anywhere important */ ic->ic_phytype = IEEE80211_T_OFDM; ic->ic_opmode = IEEE80211_M_STA; @@ -508,11 +882,15 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) | IEEE80211_C_SHPREAMBLE /* short preamble supported */ | IEEE80211_C_SHSLOT /* short slot time supported */ | IEEE80211_C_WPA /* capable of WPA1+WPA2 */ +#ifndef ATH_ENABLE_11N | IEEE80211_C_BGSCAN /* capable of bg scanning */ +#endif | IEEE80211_C_TXFRAG /* handle tx frags */ #ifdef ATH_ENABLE_DFS - | IEEE80211_C_DFS /* Enable DFS radar detection */ + | IEEE80211_C_DFS /* Enable radar detection */ #endif + | IEEE80211_C_PMGT /* Station side power mgmt */ + | IEEE80211_C_SWSLEEP ; /* * Query the hal to figure out h/w crypto support. @@ -596,6 +974,10 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) sc->sc_hastsfadd = ath_hal_hastsfadjust(ah); sc->sc_rxslink = ath_hal_self_linked_final_rxdesc(ah); sc->sc_rxtsf32 = ath_hal_has_long_rxdesc_tsf(ah); + sc->sc_hasenforcetxop = ath_hal_hasenforcetxop(ah); + sc->sc_rx_lnamixer = ath_hal_hasrxlnamixer(ah); + sc->sc_hasdivcomb = ath_hal_hasdivantcomb(ah); + if (ath_hal_hasfastframes(ah)) ic->ic_caps |= IEEE80211_C_FF; wmodes = ath_hal_getwirelessmodes(ah); @@ -609,10 +991,76 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) #endif /* - * The if_ath 11n support is completely not ready for normal use. - * Enabling this option will likely break everything and everything. - * Don't think of doing that unless you know what you're doing. + * TODO: enforce that at least this many frames are available + * in the txbuf list before allowing data frames (raw or + * otherwise) to be transmitted. */ + sc->sc_txq_data_minfree = 10; + /* + * Leave this as default to maintain legacy behaviour. + * Shortening the cabq/mcastq may end up causing some + * undesirable behaviour. + */ + sc->sc_txq_mcastq_maxdepth = ath_txbuf; + + /* + * How deep can the node software TX queue get whilst it's asleep. + */ + sc->sc_txq_node_psq_maxdepth = 16; + + /* + * Default the maximum queue depth for a given node + * to 1/4'th the TX buffers, or 64, whichever + * is larger. + */ + sc->sc_txq_node_maxdepth = MAX(64, ath_txbuf / 4); + + /* Enable CABQ by default */ + sc->sc_cabq_enable = 1; + + /* + * Allow the TX and RX chainmasks to be overridden by + * environment variables and/or device.hints. + * + * This must be done early - before the hardware is + * calibrated or before the 802.11n stream calculation + * is done. + */ + if (resource_int_value(device_get_name(sc->sc_dev), + device_get_unit(sc->sc_dev), "rx_chainmask", + &rx_chainmask) == 0) { + device_printf(sc->sc_dev, "Setting RX chainmask to 0x%x\n", + rx_chainmask); + (void) ath_hal_setrxchainmask(sc->sc_ah, rx_chainmask); + } + if (resource_int_value(device_get_name(sc->sc_dev), + device_get_unit(sc->sc_dev), "tx_chainmask", + &tx_chainmask) == 0) { + device_printf(sc->sc_dev, "Setting TX chainmask to 0x%x\n", + tx_chainmask); + (void) ath_hal_settxchainmask(sc->sc_ah, tx_chainmask); + } + + /* + * Query the TX/RX chainmask configuration. + * + * This is only relevant for 11n devices. + */ + ath_hal_getrxchainmask(ah, &sc->sc_rxchainmask); + ath_hal_gettxchainmask(ah, &sc->sc_txchainmask); + + /* + * Disable MRR with protected frames by default. + * Only 802.11n series NICs can handle this. + */ + sc->sc_mrrprot = 0; /* XXX should be a capability */ + + /* + * Query the enterprise mode information the HAL. + */ + if (ath_hal_getcapability(ah, HAL_CAP_ENTERPRISE_MODE, 0, + &sc->sc_ent_cfg) == HAL_OK) + sc->sc_use_ent = 1; #ifdef ATH_ENABLE_11N /* @@ -620,15 +1068,19 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) */ if (ath_hal_getcapability(ah, HAL_CAP_HT, 0, NULL) == HAL_OK && (wmodes & (HAL_MODE_HT20 | HAL_MODE_HT40))) { - int rxs, txs; + uint32_t rxs, txs; + uint32_t ldpc; device_printf(sc->sc_dev, "[HT] enabling HT modes\n"); - ic->ic_htcaps = IEEE80211_HTC_HT /* HT operation */ - | IEEE80211_HTC_AMPDU /* A-MPDU tx/rx */ - | IEEE80211_HTC_AMSDU /* A-MSDU tx/rx */ - | IEEE80211_HTCAP_MAXAMSDU_3839 /* max A-MSDU length */ - | IEEE80211_HTCAP_SMPS_OFF; /* SM power save off */ - ; + + sc->sc_mrrprot = 1; /* XXX should be a capability */ + + ic->ic_htcaps = IEEE80211_HTC_HT /* HT operation */ + | IEEE80211_HTC_AMPDU /* A-MPDU tx/rx */ + | IEEE80211_HTC_AMSDU /* A-MSDU tx/rx */ + | IEEE80211_HTCAP_MAXAMSDU_3839 + /* max A-MSDU length */ + | IEEE80211_HTCAP_SMPS_OFF; /* SM power save off */ /* * Enable short-GI for HT20 only if the hardware @@ -648,23 +1100,89 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) | IEEE80211_HTCAP_SHORTGI40; /* - * rx/tx stream is not currently used anywhere; it needs to be taken - * into account when negotiating which MCS rates it'll receive and + * TX/RX streams need to be taken into account when + * negotiating which MCS rates it'll receive and * what MCS rates are available for TX. */ - (void) ath_hal_getcapability(ah, HAL_CAP_STREAMS, 0, &rxs); - (void) ath_hal_getcapability(ah, HAL_CAP_STREAMS, 1, &txs); - - ath_hal_getrxchainmask(ah, &sc->sc_rxchainmask); - ath_hal_gettxchainmask(ah, &sc->sc_txchainmask); - + (void) ath_hal_getcapability(ah, HAL_CAP_STREAMS, 0, &txs); + (void) ath_hal_getcapability(ah, HAL_CAP_STREAMS, 1, &rxs); ic->ic_txstream = txs; ic->ic_rxstream = rxs; - device_printf(sc->sc_dev, "[HT] %d RX streams; %d TX streams\n", rxs, txs); + /* + * Setup TX and RX STBC based on what the HAL allows and + * the currently configured chainmask set. + * Ie - don't enable STBC TX if only one chain is enabled. + * STBC RX is fine on a single RX chain; it just won't + * provide any real benefit. + */ + if (ath_hal_getcapability(ah, HAL_CAP_RX_STBC, 0, + NULL) == HAL_OK) { + sc->sc_rx_stbc = 1; + device_printf(sc->sc_dev, + "[HT] 1 stream STBC receive enabled\n"); + ic->ic_htcaps |= IEEE80211_HTCAP_RXSTBC_1STREAM; + } + if (txs > 1 && ath_hal_getcapability(ah, HAL_CAP_TX_STBC, 0, + NULL) == HAL_OK) { + sc->sc_tx_stbc = 1; + device_printf(sc->sc_dev, + "[HT] 1 stream STBC transmit enabled\n"); + ic->ic_htcaps |= IEEE80211_HTCAP_TXSTBC; + } + + (void) ath_hal_getcapability(ah, HAL_CAP_RTS_AGGR_LIMIT, 1, + &sc->sc_rts_aggr_limit); + if (sc->sc_rts_aggr_limit != (64 * 1024)) + device_printf(sc->sc_dev, + "[HT] RTS aggregates limited to %d KiB\n", + sc->sc_rts_aggr_limit / 1024); + + /* + * LDPC + */ + if ((ath_hal_getcapability(ah, HAL_CAP_LDPC, 0, &ldpc)) + == HAL_OK && (ldpc == 1)) { + sc->sc_has_ldpc = 1; + device_printf(sc->sc_dev, + "[HT] LDPC transmit/receive enabled\n"); + ic->ic_htcaps |= IEEE80211_HTCAP_LDPC; + } + + + device_printf(sc->sc_dev, + "[HT] %d RX streams; %d TX streams\n", rxs, txs); } #endif + /* + * Initial aggregation settings. + */ + sc->sc_hwq_limit_aggr = ATH_AGGR_MIN_QDEPTH; + sc->sc_hwq_limit_nonaggr = ATH_NONAGGR_MIN_QDEPTH; + sc->sc_tid_hwq_lo = ATH_AGGR_SCHED_LOW; + sc->sc_tid_hwq_hi = ATH_AGGR_SCHED_HIGH; + sc->sc_aggr_limit = ATH_AGGR_MAXSIZE; + sc->sc_delim_min_pad = 0; + + /* + * Check if the hardware requires PCI register serialisation. + * Some of the Owl based MACs require this. + */ + if (mp_ncpus > 1 && + ath_hal_getcapability(ah, HAL_CAP_SERIALISE_WAR, + 0, NULL) == HAL_OK) { + sc->sc_ah->ah_config.ah_serialise_reg_war = 1; + device_printf(sc->sc_dev, + "Enabling register serialisation\n"); + } + + /* + * Initialise the deferred completed RX buffer list. + */ + TAILQ_INIT(&sc->sc_rx_rxlist[HAL_RX_QUEUE_HP]); + TAILQ_INIT(&sc->sc_rx_rxlist[HAL_RX_QUEUE_LP]); + /* * Indicate we need the 802.11 header padded to a * 32-bit boundary for 4-address and QoS frames. @@ -682,20 +1200,29 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) */ sc->sc_hasveol = ath_hal_hasveol(ah); - /* get mac address from hardware */ - ath_hal_getmac(ah, macaddr); + /* get mac address from kenv first, then hardware */ + if (ath_fetch_mac_kenv(sc, ic->ic_macaddr) == 0) { + /* Tell the HAL now about the new MAC */ + ath_hal_setmac(ah, ic->ic_macaddr); + } else { + ath_hal_getmac(ah, ic->ic_macaddr); + } + if (sc->sc_hasbmask) ath_hal_getbssidmask(ah, sc->sc_hwbssidmask); /* NB: used to size node table key mapping array */ ic->ic_max_keyix = sc->sc_keymax; /* call MI attach routine. */ - ieee80211_ifattach(ic, macaddr); + ieee80211_ifattach(ic); ic->ic_setregdomain = ath_setregdomain; ic->ic_getradiocaps = ath_getradiocaps; sc->sc_opmode = HAL_M_STA; /* override default methods */ + ic->ic_ioctl = ath_ioctl; + ic->ic_parent = ath_parent; + ic->ic_transmit = ath_transmit; ic->ic_newassoc = ath_newassoc; ic->ic_updateslot = ath_updateslot; ic->ic_wme.wme_update = ath_wme_update; @@ -707,16 +1234,61 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) ic->ic_node_alloc = ath_node_alloc; sc->sc_node_free = ic->ic_node_free; ic->ic_node_free = ath_node_free; + sc->sc_node_cleanup = ic->ic_node_cleanup; + ic->ic_node_cleanup = ath_node_cleanup; ic->ic_node_getsignal = ath_node_getsignal; ic->ic_scan_start = ath_scan_start; ic->ic_scan_end = ath_scan_end; ic->ic_set_channel = ath_set_channel; +#ifdef ATH_ENABLE_11N + /* 802.11n specific - but just override anyway */ + sc->sc_addba_request = ic->ic_addba_request; + sc->sc_addba_response = ic->ic_addba_response; + sc->sc_addba_stop = ic->ic_addba_stop; + sc->sc_bar_response = ic->ic_bar_response; + sc->sc_addba_response_timeout = ic->ic_addba_response_timeout; + ic->ic_addba_request = ath_addba_request; + ic->ic_addba_response = ath_addba_response; + ic->ic_addba_response_timeout = ath_addba_response_timeout; + ic->ic_addba_stop = ath_addba_stop; + ic->ic_bar_response = ath_bar_response; + + ic->ic_update_chw = ath_update_chw; +#endif /* ATH_ENABLE_11N */ + +#ifdef ATH_ENABLE_RADIOTAP_VENDOR_EXT + /* + * There's one vendor bitmap entry in the RX radiotap + * header; make sure that's taken into account. + */ + ieee80211_radiotap_attachv(ic, + &sc->sc_tx_th.wt_ihdr, sizeof(sc->sc_tx_th), 0, + ATH_TX_RADIOTAP_PRESENT, + &sc->sc_rx_th.wr_ihdr, sizeof(sc->sc_rx_th), 1, + ATH_RX_RADIOTAP_PRESENT); +#else + /* + * No vendor bitmap/extensions are present. + */ ieee80211_radiotap_attach(ic, &sc->sc_tx_th.wt_ihdr, sizeof(sc->sc_tx_th), ATH_TX_RADIOTAP_PRESENT, &sc->sc_rx_th.wr_ihdr, sizeof(sc->sc_rx_th), ATH_RX_RADIOTAP_PRESENT); +#endif /* ATH_ENABLE_RADIOTAP_VENDOR_EXT */ + + /* + * Setup the ALQ logging if required + */ +#ifdef ATH_DEBUG_ALQ + if_ath_alq_init(&sc->sc_alq, device_get_nameunit(sc->sc_dev)); + if_ath_alq_setcfg(&sc->sc_alq, + sc->sc_ah->ah_macVersion, + sc->sc_ah->ah_macRev, + sc->sc_ah->ah_phyRev, + sc->sc_ah->ah_magic); +#endif /* * Setup dynamic sysctl's now that country code and @@ -729,15 +1301,23 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) if (bootverbose) ieee80211_announce(ic); ath_announce(sc); + + /* + * Put it to sleep for now. + */ + ATH_LOCK(sc); + ath_power_setpower(sc, HAL_PM_FULL_SLEEP); + ATH_UNLOCK(sc); + return 0; bad2: ath_tx_cleanup(sc); ath_desc_free(sc); + ath_txdma_teardown(sc); + ath_rxdma_teardown(sc); bad: if (ah) ath_hal_detach(ah); - if (ifp != NULL) - if_free(ifp); sc->sc_invalid = 1; return error; } @@ -745,10 +1325,6 @@ bad: int ath_detach(struct ath_softc *sc) { - struct ifnet *ifp = sc->sc_ifp; - - DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n", - __func__, ifp->if_flags); /* * NB: the order of these is important: @@ -764,20 +1340,41 @@ ath_detach(struct ath_softc *sc) * it last * Other than that, it's straightforward... */ - ath_stop(ifp); - ieee80211_ifdetach(ifp->if_l2com); + + /* + * XXX Wake the hardware up first. ath_stop() will still + * wake it up first, but I'd rather do it here just to + * ensure it's awake. + */ + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ath_power_setpower(sc, HAL_PM_AWAKE); + + /* + * Stop things cleanly. + */ + ath_stop(sc); + ATH_UNLOCK(sc); + + ieee80211_ifdetach(&sc->sc_ic); taskqueue_free(sc->sc_tq); #ifdef ATH_TX99_DIAG if (sc->sc_tx99 != NULL) sc->sc_tx99->detach(sc->sc_tx99); #endif ath_rate_detach(sc->sc_rc); - +#ifdef ATH_DEBUG_ALQ + if_ath_alq_tidyup(&sc->sc_alq); +#endif + ath_lna_div_detach(sc); + ath_btcoex_detach(sc); + ath_spectral_detach(sc); ath_dfs_detach(sc); ath_desc_free(sc); + ath_txdma_teardown(sc); + ath_rxdma_teardown(sc); ath_tx_cleanup(sc); ath_hal_detach(sc->sc_ah); /* NB: sets chip in full sleep */ - if_free(ifp); return 0; } @@ -854,15 +1451,14 @@ ath_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac0[IEEE80211_ADDR_LEN]) { - struct ath_softc *sc = ic->ic_ifp->if_softc; + struct ath_softc *sc = ic->ic_softc; struct ath_vap *avp; struct ieee80211vap *vap; uint8_t mac[IEEE80211_ADDR_LEN]; int needbeacon, error; enum ieee80211_opmode ic_opmode; - avp = (struct ath_vap *) malloc(sizeof(struct ath_vap), - M_80211_VAP, M_WAITOK | M_ZERO); + avp = malloc(sizeof(struct ath_vap), M_80211_VAP, M_WAITOK | M_ZERO); needbeacon = 0; IEEE80211_ADDR_COPY(mac, mac0); @@ -948,7 +1544,7 @@ ath_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, /* * Check that a beacon buffer is available; the code below assumes it. */ - if (needbeacon & STAILQ_EMPTY(&sc->sc_bbuf)) { + if (needbeacon & TAILQ_EMPTY(&sc->sc_bbuf)) { device_printf(sc->sc_dev, "no beacon buffer available\n"); goto bad; } @@ -962,8 +1558,7 @@ ath_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, vap = &avp->av_vap; /* XXX can't hold mutex across if_alloc */ ATH_UNLOCK(sc); - error = ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, - bssid, mac); + error = ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid); ATH_LOCK(sc); if (error != 0) { device_printf(sc->sc_dev, "%s: error %d creating vap\n", @@ -988,6 +1583,15 @@ ath_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, avp->av_bmiss = vap->iv_bmiss; vap->iv_bmiss = ath_bmiss_vap; + avp->av_node_ps = vap->iv_node_ps; + vap->iv_node_ps = ath_node_powersave; + + avp->av_set_tim = vap->iv_set_tim; + vap->iv_set_tim = ath_node_set_tim; + + avp->av_recv_pspoll = vap->iv_recv_pspoll; + vap->iv_recv_pspoll = ath_node_recv_pspoll; + /* Set default parameters */ /* @@ -1010,8 +1614,8 @@ ath_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, * multicast frames. We know a beacon buffer is * available because we checked above. */ - avp->av_bcbuf = STAILQ_FIRST(&sc->sc_bbuf); - STAILQ_REMOVE_HEAD(&sc->sc_bbuf, bf_list); + avp->av_bcbuf = TAILQ_FIRST(&sc->sc_bbuf); + TAILQ_REMOVE(&sc->sc_bbuf, avp->av_bcbuf, bf_list); if (opmode != IEEE80211_M_IBSS || !sc->sc_hasveol) { /* * Assign the vap to a beacon xmit slot. As above @@ -1088,7 +1692,8 @@ ath_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, ATH_UNLOCK(sc); /* complete setup */ - ieee80211_vap_attach(vap, ath_media_change, ieee80211_media_status); + ieee80211_vap_attach(vap, ath_media_change, ieee80211_media_status, + mac); return vap; bad2: reclaim_address(sc, mac); @@ -1103,23 +1708,50 @@ static void ath_vap_delete(struct ieee80211vap *vap) { struct ieee80211com *ic = vap->iv_ic; - struct ifnet *ifp = ic->ic_ifp; - struct ath_softc *sc = ifp->if_softc; + struct ath_softc *sc = ic->ic_softc; struct ath_hal *ah = sc->sc_ah; struct ath_vap *avp = ATH_VAP(vap); - if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + + DPRINTF(sc, ATH_DEBUG_RESET, "%s: called\n", __func__); + if (sc->sc_running) { /* * Quiesce the hardware while we remove the vap. In * particular we need to reclaim all references to * the vap state by any frames pending on the tx queues. */ ath_hal_intrset(ah, 0); /* disable interrupts */ - ath_draintxq(sc); /* stop xmit side */ - ath_stoprecv(sc); /* stop recv side */ + /* XXX Do all frames from all vaps/nodes need draining here? */ + ath_stoprecv(sc, 1); /* stop recv side */ + ath_draintxq(sc, ATH_RESET_DEFAULT); /* stop hw xmit side */ } + /* .. leave the hardware awake for now. */ + ieee80211_vap_detach(vap); + + /* + * XXX Danger Will Robinson! Danger! + * + * Because ieee80211_vap_detach() can queue a frame (the station + * diassociate message?) after we've drained the TXQ and + * flushed the software TXQ, we will end up with a frame queued + * to a node whose vap is about to be freed. + * + * To work around this, flush the hardware/software again. + * This may be racy - the ath task may be running and the packet + * may be being scheduled between sw->hw txq. Tsk. + * + * TODO: figure out why a new node gets allocated somewhere around + * here (after the ath_tx_swq() call; and after an ath_stop() + * call!) + */ + + ath_draintxq(sc, ATH_RESET_DEFAULT); + ATH_LOCK(sc); /* * Reclaim beacon state. Note this must be done before @@ -1142,7 +1774,6 @@ ath_vap_delete(struct ieee80211vap *vap) * Reclaim any pending mcast frames for the vap. */ ath_tx_draintxq(sc, &avp->av_mcastq); - ATH_TXQ_LOCK_DESTROY(&avp->av_mcastq); } /* * Update bookkeeping. @@ -1167,17 +1798,16 @@ ath_vap_delete(struct ieee80211vap *vap) sc->sc_swbmiss = 0; } #endif - ATH_UNLOCK(sc); free(avp, M_80211_VAP); - if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + if (sc->sc_running) { /* * Restart rx+tx machines if still running (RUNNING will * be reset if we just destroyed the last vap). */ if (ath_startrecv(sc) != 0) - if_printf(ifp, "%s: unable to restart recv logic\n", - __func__); + device_printf(sc->sc_dev, + "%s: unable to restart recv logic\n", __func__); if (sc->sc_beacons) { /* restart beacons */ #ifdef IEEE80211_SUPPORT_TDMA if (sc->sc_tdma) @@ -1188,27 +1818,47 @@ ath_vap_delete(struct ieee80211vap *vap) } ath_hal_intrset(ah, sc->sc_imask); } + + /* Ok, let the hardware asleep. */ + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); } void ath_suspend(struct ath_softc *sc) { - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211com *ic = &sc->sc_ic; - DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n", - __func__, ifp->if_flags); + sc->sc_resume_up = ic->ic_nrunning != 0; - sc->sc_resume_up = (ifp->if_flags & IFF_UP) != 0; - if (ic->ic_opmode == IEEE80211_M_STA) - ath_stop(ifp); - else - ieee80211_suspend_all(ic); + ieee80211_suspend_all(ic); /* * NB: don't worry about putting the chip in low power * mode; pci will power off our socket on suspend and * CardBus detaches the device. + * + * XXX TODO: well, that's great, except for non-cardbus + * devices! */ + + /* + * XXX This doesn't wait until all pending taskqueue + * items and parallel transmit/receive/other threads + * are running! + */ + ath_hal_intrset(sc->sc_ah, 0); + taskqueue_block(sc->sc_tq); + + ATH_LOCK(sc); + callout_stop(&sc->sc_cal_ch); + ATH_UNLOCK(sc); + + /* + * XXX ensure sc_invalid is 1 + */ + + /* Disable the PCIe PHY, complete with workarounds */ + ath_hal_enablepcie(sc->sc_ah, 1, 1); } /* @@ -1220,60 +1870,110 @@ ath_suspend(struct ath_softc *sc) static void ath_reset_keycache(struct ath_softc *sc) { - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = sc->sc_ah; int i; + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); for (i = 0; i < sc->sc_keymax; i++) ath_hal_keyreset(ah, i); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); ieee80211_crypto_reload_keys(ic); } +/* + * Fetch the current chainmask configuration based on the current + * operating channel and options. + */ +static void +ath_update_chainmasks(struct ath_softc *sc, struct ieee80211_channel *chan) +{ + + /* + * Set TX chainmask to the currently configured chainmask; + * the TX chainmask depends upon the current operating mode. + */ + sc->sc_cur_rxchainmask = sc->sc_rxchainmask; + if (IEEE80211_IS_CHAN_HT(chan)) { + sc->sc_cur_txchainmask = sc->sc_txchainmask; + } else { + sc->sc_cur_txchainmask = 1; + } + + DPRINTF(sc, ATH_DEBUG_RESET, + "%s: TX chainmask is now 0x%x, RX is now 0x%x\n", + __func__, + sc->sc_cur_txchainmask, + sc->sc_cur_rxchainmask); +} + void ath_resume(struct ath_softc *sc) { - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = sc->sc_ah; HAL_STATUS status; - DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n", - __func__, ifp->if_flags); + ath_hal_enablepcie(ah, 0, 0); /* * Must reset the chip before we reload the * keycache as we were powered down on suspend. */ + ath_update_chainmasks(sc, + sc->sc_curchan != NULL ? sc->sc_curchan : ic->ic_curchan); + ath_hal_setchainmasks(sc->sc_ah, sc->sc_cur_txchainmask, + sc->sc_cur_rxchainmask); + + /* Ensure we set the current power state to on */ + ATH_LOCK(sc); + ath_power_setselfgen(sc, HAL_PM_AWAKE); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ath_power_setpower(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + ath_hal_reset(ah, sc->sc_opmode, sc->sc_curchan != NULL ? sc->sc_curchan : ic->ic_curchan, - AH_FALSE, &status); + AH_FALSE, HAL_RESET_NORMAL, &status); ath_reset_keycache(sc); + ATH_RX_LOCK(sc); + sc->sc_rx_stopped = 1; + sc->sc_rx_resetted = 1; + ATH_RX_UNLOCK(sc); + /* Let DFS at it in case it's a DFS channel */ ath_dfs_radar_enable(sc, ic->ic_curchan); - if (sc->sc_resume_up) { - if (ic->ic_opmode == IEEE80211_M_STA) { - ath_init(sc); - /* - * Program the beacon registers using the last rx'd - * beacon frame and enable sync on the next beacon - * we see. This should handle the case where we - * wakeup and find the same AP and also the case where - * we wakeup and need to roam. For the latter we - * should get bmiss events that trigger a roam. - */ - ath_beacon_config(sc, NULL); - sc->sc_syncbeacon = 1; - } else - ieee80211_resume_all(ic); - } - if (sc->sc_softled) { - ath_hal_gpioCfgOutput(ah, sc->sc_ledpin, - HAL_GPIO_MUX_MAC_NETWORK_LED); - ath_hal_gpioset(ah, sc->sc_ledpin, !sc->sc_ledon); - } + /* Let spectral at in case spectral is enabled */ + ath_spectral_enable(sc, ic->ic_curchan); + + /* + * Let bluetooth coexistence at in case it's needed for this channel + */ + ath_btcoex_enable(sc, ic->ic_curchan); + + /* + * If we're doing TDMA, enforce the TXOP limitation for chips that + * support it. + */ + if (sc->sc_hasenforcetxop && sc->sc_tdma) + ath_hal_setenforcetxop(sc->sc_ah, 1); + else + ath_hal_setenforcetxop(sc->sc_ah, 0); + + /* Restore the LED configuration */ + ath_led_config(sc); + ath_hal_setledstate(ah, HAL_LED_INIT); + + if (sc->sc_resume_up) + ieee80211_resume_all(ic); + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); /* XXX beacons ? */ } @@ -1281,12 +1981,10 @@ ath_resume(struct ath_softc *sc) void ath_shutdown(struct ath_softc *sc) { - struct ifnet *ifp = sc->sc_ifp; - DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n", - __func__, ifp->if_flags); - - ath_stop(ifp); + ATH_LOCK(sc); + ath_stop(sc); + ATH_UNLOCK(sc); /* NB: no point powering down chip as we're about to reboot */ } @@ -1297,9 +1995,25 @@ void ath_intr(void *arg) { struct ath_softc *sc = arg; - struct ifnet *ifp = sc->sc_ifp; struct ath_hal *ah = sc->sc_ah; HAL_INT status = 0; + uint32_t txqs; + + /* + * If we're inside a reset path, just print a warning and + * clear the ISR. The reset routine will finish it for us. + */ + ATH_PCU_LOCK(sc); + if (sc->sc_inreset_cnt) { + HAL_INT status; + ath_hal_getisr(ah, &status); /* clear ISR */ + ath_hal_intrset(ah, 0); /* disable further intr's */ + DPRINTF(sc, ATH_DEBUG_ANY, + "%s: in reset, ignoring: status=0x%x\n", + __func__, status); + ATH_PCU_UNLOCK(sc); + return; + } #if !defined(__HAIKU__) if (sc->sc_invalid) { @@ -1308,21 +2022,34 @@ ath_intr(void *arg) * Note this can happen early on if the IRQ is shared. */ DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid; ignored\n", __func__); + ATH_PCU_UNLOCK(sc); return; } - if (!ath_hal_intrpend(ah)) /* shared irq, not for us */ + if (!ath_hal_intrpend(ah)) { /* shared irq, not for us */ + ATH_PCU_UNLOCK(sc); return; + } #endif - if ((ifp->if_flags & IFF_UP) == 0 || - (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { + + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + + if (sc->sc_ic.ic_nrunning == 0 && sc->sc_running == 0) { HAL_INT status; - DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags 0x%x\n", - __func__, ifp->if_flags); + DPRINTF(sc, ATH_DEBUG_ANY, "%s: ic_nrunning %d sc_running %d\n", + __func__, sc->sc_ic.ic_nrunning, sc->sc_running); ath_hal_getisr(ah, &status); /* clear ISR */ ath_hal_intrset(ah, 0); /* disable further intr's */ + ATH_PCU_UNLOCK(sc); + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); return; } + /* * Figure out the reason(s) for the interrupt. Note * that the hal returns a pseudo-ISR that may include @@ -1332,19 +2059,61 @@ ath_intr(void *arg) #if defined(__HAIKU__) status = atomic_get((int32 *)&sc->sc_intr_status); #else - ath_hal_getisr(ah, &status); /* NB: clears ISR too */ + ath_hal_getisr(ah, &status); /* NB: clears ISR too */ #endif DPRINTF(sc, ATH_DEBUG_INTR, "%s: status 0x%x\n", __func__, status); + ATH_KTR(sc, ATH_KTR_INTERRUPTS, 1, "ath_intr: mask=0x%.8x", status); +#ifdef ATH_DEBUG_ALQ + if_ath_alq_post_intr(&sc->sc_alq, status, ah->ah_intrstate, + ah->ah_syncstate); +#endif /* ATH_DEBUG_ALQ */ +#ifdef ATH_KTR_INTR_DEBUG + ATH_KTR(sc, ATH_KTR_INTERRUPTS, 5, + "ath_intr: ISR=0x%.8x, ISR_S0=0x%.8x, ISR_S1=0x%.8x, ISR_S2=0x%.8x, ISR_S5=0x%.8x", + ah->ah_intrstate[0], + ah->ah_intrstate[1], + ah->ah_intrstate[2], + ah->ah_intrstate[3], + ah->ah_intrstate[6]); +#endif + + /* Squirrel away SYNC interrupt debugging */ + if (ah->ah_syncstate != 0) { + int i; + for (i = 0; i < 32; i++) + if (ah->ah_syncstate & (i << i)) + sc->sc_intr_stats.sync_intr[i]++; + } + status &= sc->sc_imask; /* discard unasked for bits */ /* Short-circuit un-handled interrupts */ - if (status == 0x0) - return; + if (status == 0x0) { + ATH_PCU_UNLOCK(sc); + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + return; + } + + /* + * Take a note that we're inside the interrupt handler, so + * the reset routines know to wait. + */ + sc->sc_intr_cnt++; + ATH_PCU_UNLOCK(sc); + + /* + * Handle the interrupt. We won't run concurrent with the reset + * or channel change routines as they'll wait for sc_intr_cnt + * to be 0 before continuing. + */ if (status & HAL_INT_FATAL) { sc->sc_stats.ast_hardware++; ath_hal_intrset(ah, 0); /* disable intr's until reset */ - ath_fatal_proc(sc, 0); + taskqueue_enqueue(sc->sc_tq, &sc->sc_fataltask); } else { if (status & HAL_INT_SWBA) { /* @@ -1356,7 +2125,7 @@ ath_intr(void *arg) #ifdef IEEE80211_SUPPORT_TDMA if (sc->sc_tdma) { if (sc->sc_tdmaswba == 0) { - struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); ath_tdma_beacon_send(sc, vap); @@ -1374,47 +2143,90 @@ ath_intr(void *arg) * traffic so any frames held on the staging * queue are aged and potentially flushed. */ - taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask); + sc->sc_rx.recv_sched(sc, 1); #endif } } if (status & HAL_INT_RXEOL) { - int imask = sc->sc_imask; + int imask; + ATH_KTR(sc, ATH_KTR_ERROR, 0, "ath_intr: RXEOL"); + if (! sc->sc_isedma) { + ATH_PCU_LOCK(sc); + /* + * NB: the hardware should re-read the link when + * RXE bit is written, but it doesn't work at + * least on older hardware revs. + */ + sc->sc_stats.ast_rxeol++; + /* + * Disable RXEOL/RXORN - prevent an interrupt + * storm until the PCU logic can be reset. + * In case the interface is reset some other + * way before "sc_kickpcu" is called, don't + * modify sc_imask - that way if it is reset + * by a call to ath_reset() somehow, the + * interrupt mask will be correctly reprogrammed. + */ + imask = sc->sc_imask; + imask &= ~(HAL_INT_RXEOL | HAL_INT_RXORN); + ath_hal_intrset(ah, imask); + /* + * Only blank sc_rxlink if we've not yet kicked + * the PCU. + * + * This isn't entirely correct - the correct solution + * would be to have a PCU lock and engage that for + * the duration of the PCU fiddling; which would include + * running the RX process. Otherwise we could end up + * messing up the RX descriptor chain and making the + * RX desc list much shorter. + */ + if (! sc->sc_kickpcu) + sc->sc_rxlink = NULL; + sc->sc_kickpcu = 1; + ATH_PCU_UNLOCK(sc); + } /* - * NB: the hardware should re-read the link when - * RXE bit is written, but it doesn't work at - * least on older hardware revs. - */ - sc->sc_stats.ast_rxeol++; - /* - * Disable RXEOL/RXORN - prevent an interrupt - * storm until the PCU logic can be reset. - * In case the interface is reset some other - * way before "sc_kickpcu" is called, don't - * modify sc_imask - that way if it is reset - * by a call to ath_reset() somehow, the - * interrupt mask will be correctly reprogrammed. - */ - imask &= ~(HAL_INT_RXEOL | HAL_INT_RXORN); - ath_hal_intrset(ah, imask); - /* - * Enqueue an RX proc, to handled whatever + * Enqueue an RX proc to handle whatever * is in the RX queue. - * This will then kick the PCU. + * This will then kick the PCU if required. */ - taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask); - sc->sc_rxlink = NULL; - sc->sc_kickpcu = 1; + sc->sc_rx.recv_sched(sc, 1); } if (status & HAL_INT_TXURN) { sc->sc_stats.ast_txurn++; /* bump tx trigger level */ ath_hal_updatetxtriglevel(ah, AH_TRUE); } - if (status & HAL_INT_RX) - taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask); - if (status & HAL_INT_TX) + /* + * Handle both the legacy and RX EDMA interrupt bits. + * Note that HAL_INT_RXLP is also HAL_INT_RXDESC. + */ + if (status & (HAL_INT_RX | HAL_INT_RXHP | HAL_INT_RXLP)) { + sc->sc_stats.ast_rx_intr++; + sc->sc_rx.recv_sched(sc, 1); + } + if (status & HAL_INT_TX) { + sc->sc_stats.ast_tx_intr++; + /* + * Grab all the currently set bits in the HAL txq bitmap + * and blank them. This is the only place we should be + * doing this. + */ + if (! sc->sc_isedma) { + ATH_PCU_LOCK(sc); + txqs = 0xffffffff; + ath_hal_gettxintrtxqs(sc->sc_ah, &txqs); + ATH_KTR(sc, ATH_KTR_INTERRUPTS, 3, + "ath_intr: TX; txqs=0x%08x, txq_active was 0x%08x, now 0x%08x", + txqs, + sc->sc_txq_active, + sc->sc_txq_active | txqs); + sc->sc_txq_active |= txqs; + ATH_PCU_UNLOCK(sc); + } taskqueue_enqueue(sc->sc_tq, &sc->sc_txtask); + } if (status & HAL_INT_BMISS) { sc->sc_stats.ast_bmiss++; taskqueue_enqueue(sc->sc_tq, &sc->sc_bmisstask); @@ -1425,6 +2237,7 @@ ath_intr(void *arg) sc->sc_stats.ast_tx_cst++; if (status & HAL_INT_MIB) { sc->sc_stats.ast_mib++; + ATH_PCU_LOCK(sc); /* * Disable interrupts until we service the MIB * interrupt; otherwise it will continue to fire. @@ -1435,25 +2248,50 @@ ath_intr(void *arg) * clear whatever condition caused the interrupt. */ ath_hal_mibevent(ah, &sc->sc_halstats); - ath_hal_intrset(ah, sc->sc_imask); + /* + * Don't reset the interrupt if we've just + * kicked the PCU, or we may get a nested + * RXEOL before the rxproc has had a chance + * to run. + */ + if (sc->sc_kickpcu == 0) + ath_hal_intrset(ah, sc->sc_imask); + ATH_PCU_UNLOCK(sc); } if (status & HAL_INT_RXORN) { /* NB: hal marks HAL_INT_FATAL when RXORN is fatal */ + ATH_KTR(sc, ATH_KTR_ERROR, 0, "ath_intr: RXORN"); sc->sc_stats.ast_rxorn++; } + if (status & HAL_INT_TSFOOR) { + device_printf(sc->sc_dev, "%s: TSFOOR\n", __func__); + sc->sc_syncbeacon = 1; + } + if (status & HAL_INT_MCI) { + ath_btcoex_mci_intr(sc); + } } + ATH_PCU_LOCK(sc); + sc->sc_intr_cnt--; + ATH_PCU_UNLOCK(sc); + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); } static void ath_fatal_proc(void *arg, int pending) { struct ath_softc *sc = arg; - struct ifnet *ifp = sc->sc_ifp; u_int32_t *state; u_int32_t len; void *sp; - if_printf(ifp, "hardware error; resetting\n"); + if (sc->sc_invalid) + return; + + device_printf(sc->sc_dev, "hardware error; resetting\n"); /* * Fatal errors are unrecoverable. Typically these * are caused by DMA errors. Collect h/w state from @@ -1462,16 +2300,18 @@ ath_fatal_proc(void *arg, int pending) if (ath_hal_getfatalstate(sc->sc_ah, &sp, &len)) { KASSERT(len >= 6*sizeof(u_int32_t), ("len %u bytes", len)); state = sp; - if_printf(ifp, "0x%08x 0x%08x 0x%08x, 0x%08x 0x%08x 0x%08x\n", - state[0], state[1] , state[2], state[3], - state[4], state[5]); + device_printf(sc->sc_dev, + "0x%08x 0x%08x 0x%08x, 0x%08x 0x%08x 0x%08x\n", state[0], + state[1] , state[2], state[3], state[4], state[5]); } - ath_reset(ifp); + ath_reset(sc, ATH_RESET_NOLOSS); } static void ath_bmiss_vap(struct ieee80211vap *vap) { + struct ath_softc *sc = vap->iv_ic->ic_softc; + /* * Workaround phantom bmiss interrupts by sanity-checking * the time of our last rx'd frame. If it is within the @@ -1480,11 +2320,20 @@ ath_bmiss_vap(struct ieee80211vap *vap) * be dispatched up for processing. Note this applies only * for h/w beacon miss events. */ + + /* + * XXX TODO: Just read the TSF during the interrupt path; + * that way we don't have to wake up again just to read it + * again. + */ + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + if ((vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) == 0) { - struct ifnet *ifp = vap->iv_ic->ic_ifp; - struct ath_softc *sc = ifp->if_softc; u_int64_t lastrx = sc->sc_lastrx; u_int64_t tsf = ath_hal_gettsf64(sc->sc_ah); + /* XXX should take a locked ref to iv_bss */ u_int bmisstimeout = vap->iv_bmissthreshold * vap->iv_bss->ni_intval * 1024; @@ -1496,13 +2345,33 @@ ath_bmiss_vap(struct ieee80211vap *vap) if (tsf - lastrx <= bmisstimeout) { sc->sc_stats.ast_bmiss_phantom++; + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + return; } } + + /* + * There's no need to keep the hardware awake during the call + * to av_bmiss(). + */ + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + /* + * Attempt to force a beacon resync. + */ + sc->sc_syncbeacon = 1; + ATH_VAP(vap)->av_bmiss(vap); } -static int +/* XXX this needs a force wakeup! */ +int ath_hal_gethangstate(struct ath_hal *ah, uint32_t mask, uint32_t *hangs) { uint32_t rsize; @@ -1519,16 +2388,37 @@ static void ath_bmiss_proc(void *arg, int pending) { struct ath_softc *sc = arg; - struct ifnet *ifp = sc->sc_ifp; uint32_t hangs; DPRINTF(sc, ATH_DEBUG_ANY, "%s: pending %u\n", __func__, pending); + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + + ath_beacon_miss(sc); + + /* + * Do a reset upon any becaon miss event. + * + * It may be a non-recognised RX clear hang which needs a reset + * to clear. + */ if (ath_hal_gethangstate(sc->sc_ah, 0xff, &hangs) && hangs != 0) { - if_printf(ifp, "bb hang detected (0x%x), resetting\n", hangs); - ath_reset(ifp); - } else - ieee80211_beacon_miss(ifp->if_l2com); + ath_reset(sc, ATH_RESET_NOLOSS); + device_printf(sc->sc_dev, + "bb hang detected (0x%x), resetting\n", hangs); + } else { + ath_reset(sc, ATH_RESET_NOLOSS); + ieee80211_beacon_miss(&sc->sc_ic); + } + + /* Force a beacon resync, in case they've drifted */ + sc->sc_syncbeacon = 1; + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); } /* @@ -1540,8 +2430,7 @@ ath_bmiss_proc(void *arg, int pending) static void ath_settkipmic(struct ath_softc *sc) { - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211com *ic = &sc->sc_ic; if ((ic->ic_cryptocaps & IEEE80211_CRYPTO_TKIP) && !sc->sc_wmetkipmic) { if (ic->ic_flags & IEEE80211_F_WME) { @@ -1554,24 +2443,27 @@ ath_settkipmic(struct ath_softc *sc) } } -static void -ath_init(void *arg) +static int +ath_init(struct ath_softc *sc) { - struct ath_softc *sc = (struct ath_softc *) arg; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = sc->sc_ah; HAL_STATUS status; - DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags 0x%x\n", - __func__, ifp->if_flags); + ATH_LOCK_ASSERT(sc); + + /* + * Force the sleep state awake. + */ + ath_power_setselfgen(sc, HAL_PM_AWAKE); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ath_power_setpower(sc, HAL_PM_AWAKE); - ATH_LOCK(sc); /* * Stop anything previously setup. This is safe * whether this is the first time through or not. */ - ath_stop_locked(ifp); + ath_stop(sc); /* * The basic interface to setting the hardware in a good @@ -1581,27 +2473,54 @@ ath_init(void *arg) * and then setup of the interrupt mask. */ ath_settkipmic(sc); - if (!ath_hal_reset(ah, sc->sc_opmode, ic->ic_curchan, AH_FALSE, &status)) { - if_printf(ifp, "unable to reset hardware; hal status %u\n", - status); - ATH_UNLOCK(sc); - return; + ath_update_chainmasks(sc, ic->ic_curchan); + ath_hal_setchainmasks(sc->sc_ah, sc->sc_cur_txchainmask, + sc->sc_cur_rxchainmask); + + if (!ath_hal_reset(ah, sc->sc_opmode, ic->ic_curchan, AH_FALSE, + HAL_RESET_NORMAL, &status)) { + device_printf(sc->sc_dev, + "unable to reset hardware; hal status %u\n", status); + return (ENODEV); } + + ATH_RX_LOCK(sc); + sc->sc_rx_stopped = 1; + sc->sc_rx_resetted = 1; + ATH_RX_UNLOCK(sc); + ath_chan_change(sc, ic->ic_curchan); /* Let DFS at it in case it's a DFS channel */ ath_dfs_radar_enable(sc, ic->ic_curchan); + /* Let spectral at in case spectral is enabled */ + ath_spectral_enable(sc, ic->ic_curchan); + + /* + * Let bluetooth coexistence at in case it's needed for this channel + */ + ath_btcoex_enable(sc, ic->ic_curchan); + + /* + * If we're doing TDMA, enforce the TXOP limitation for chips that + * support it. + */ + if (sc->sc_hasenforcetxop && sc->sc_tdma) + ath_hal_setenforcetxop(sc->sc_ah, 1); + else + ath_hal_setenforcetxop(sc->sc_ah, 0); + /* * Likewise this is set during reset so update * state cached in the driver. */ sc->sc_diversity = ath_hal_getdiversity(ah); - sc->sc_lastlongcal = 0; + sc->sc_lastlongcal = ticks; sc->sc_resetcal = 1; sc->sc_lastcalreset = 0; - sc->sc_lastani = 0; - sc->sc_lastshortcal = 0; + sc->sc_lastani = ticks; + sc->sc_lastshortcal = ticks; sc->sc_doresetcal = AH_FALSE; /* * Beacon timers were cleared here; give ath_newstate() @@ -1618,17 +2537,39 @@ ath_init(void *arg) * here except setup the interrupt mask. */ if (ath_startrecv(sc) != 0) { - if_printf(ifp, "unable to start recv logic\n"); - ATH_UNLOCK(sc); - return; + device_printf(sc->sc_dev, "unable to start recv logic\n"); + ath_power_restore_power_state(sc); + return (ENODEV); } /* * Enable interrupts. */ sc->sc_imask = HAL_INT_RX | HAL_INT_TX - | HAL_INT_RXEOL | HAL_INT_RXORN + | HAL_INT_RXORN | HAL_INT_TXURN | HAL_INT_FATAL | HAL_INT_GLOBAL; + + /* + * Enable RX EDMA bits. Note these overlap with + * HAL_INT_RX and HAL_INT_RXDESC respectively. + */ + if (sc->sc_isedma) + sc->sc_imask |= (HAL_INT_RXHP | HAL_INT_RXLP); + + /* + * If we're an EDMA NIC, we don't care about RXEOL. + * Writing a new descriptor in will simply restart + * RX DMA. + */ + if (! sc->sc_isedma) + sc->sc_imask |= HAL_INT_RXEOL; + + /* + * Enable MCI interrupt for MCI devices. + */ + if (sc->sc_btcoex_mci) + sc->sc_imask |= HAL_INT_MCI; + /* * Enable MIB interrupts when there are hardware phy counters. * Note we only do this (at the moment) for station mode. @@ -1636,6 +2577,15 @@ ath_init(void *arg) if (sc->sc_needmib && ic->ic_opmode == IEEE80211_M_STA) sc->sc_imask |= HAL_INT_MIB; + /* + * XXX add capability for this. + * + * If we're in STA mode (and maybe IBSS?) then register for + * TSFOOR interrupts. + */ + if (ic->ic_opmode == IEEE80211_M_STA) + sc->sc_imask |= HAL_INT_TSFOOR; + /* Enable global TX timeout and carrier sense timeout if available */ if (ath_hal_gtxto_supported(ah)) sc->sc_imask |= HAL_INT_GTT; @@ -1643,31 +2593,28 @@ ath_init(void *arg) DPRINTF(sc, ATH_DEBUG_RESET, "%s: imask=0x%x\n", __func__, sc->sc_imask); - ifp->if_drv_flags |= IFF_DRV_RUNNING; + sc->sc_running = 1; callout_reset(&sc->sc_wd_ch, hz, ath_watchdog, sc); ath_hal_intrset(ah, sc->sc_imask); - ATH_UNLOCK(sc); + ath_power_restore_power_state(sc); -#ifdef ATH_TX99_DIAG - if (sc->sc_tx99 != NULL) - sc->sc_tx99->start(sc->sc_tx99); - else -#endif - ieee80211_start_all(ic); /* start all vap's */ + return (0); } static void -ath_stop_locked(struct ifnet *ifp) +ath_stop(struct ath_softc *sc) { - struct ath_softc *sc = ifp->if_softc; struct ath_hal *ah = sc->sc_ah; - DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid %u if_flags 0x%x\n", - __func__, sc->sc_invalid, ifp->if_flags); - ATH_LOCK_ASSERT(sc); - if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + + /* + * Wake the hardware up before fiddling with it. + */ + ath_power_set_power_state(sc, HAL_PM_AWAKE); + + if (sc->sc_running) { /* * Shutdown the hardware and driver: * reset 802.11 state machine @@ -1689,7 +2636,7 @@ ath_stop_locked(struct ifnet *ifp) #endif callout_stop(&sc->sc_wd_ch); sc->sc_wd_timer = 0; - ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + sc->sc_running = 0; if (!sc->sc_invalid) { if (sc->sc_softled) { callout_stop(&sc->sc_ledtimer); @@ -1699,26 +2646,143 @@ ath_stop_locked(struct ifnet *ifp) } ath_hal_intrset(ah, 0); } - ath_draintxq(sc); + /* XXX we should stop RX regardless of whether it's valid */ if (!sc->sc_invalid) { - ath_stoprecv(sc); + ath_stoprecv(sc, 1); ath_hal_phydisable(ah); } else sc->sc_rxlink = NULL; + ath_draintxq(sc, ATH_RESET_DEFAULT); ath_beacon_free(sc); /* XXX not needed */ } + + /* And now, restore the current power state */ + ath_power_restore_power_state(sc); } +/* + * Wait until all pending TX/RX has completed. + * + * This waits until all existing transmit, receive and interrupts + * have completed. It's assumed that the caller has first + * grabbed the reset lock so it doesn't try to do overlapping + * chip resets. + */ +#define MAX_TXRX_ITERATIONS 100 +static void +ath_txrx_stop_locked(struct ath_softc *sc) +{ + int i = MAX_TXRX_ITERATIONS; + + ATH_UNLOCK_ASSERT(sc); + ATH_PCU_LOCK_ASSERT(sc); + + /* + * Sleep until all the pending operations have completed. + * + * The caller must ensure that reset has been incremented + * or the pending operations may continue being queued. + */ + while (sc->sc_rxproc_cnt || sc->sc_txproc_cnt || + sc->sc_txstart_cnt || sc->sc_intr_cnt) { + if (i <= 0) + break; + msleep(sc, &sc->sc_pcu_mtx, 0, "ath_txrx_stop", + msecs_to_ticks(10)); + i--; + } + + if (i <= 0) + device_printf(sc->sc_dev, + "%s: didn't finish after %d iterations\n", + __func__, MAX_TXRX_ITERATIONS); +} +#undef MAX_TXRX_ITERATIONS + +#if 0 +static void +ath_txrx_stop(struct ath_softc *sc) +{ + ATH_UNLOCK_ASSERT(sc); + ATH_PCU_UNLOCK_ASSERT(sc); + + ATH_PCU_LOCK(sc); + ath_txrx_stop_locked(sc); + ATH_PCU_UNLOCK(sc); +} +#endif + static void -ath_stop(struct ifnet *ifp) +ath_txrx_start(struct ath_softc *sc) { - struct ath_softc *sc = ifp->if_softc; - ATH_LOCK(sc); - ath_stop_locked(ifp); - ATH_UNLOCK(sc); + taskqueue_unblock(sc->sc_tq); } +/* + * Grab the reset lock, and wait around until no one else + * is trying to do anything with it. + * + * This is totally horrible but we can't hold this lock for + * long enough to do TX/RX or we end up with net80211/ip stack + * LORs and eventual deadlock. + * + * "dowait" signals whether to spin, waiting for the reset + * lock count to reach 0. This should (for now) only be used + * during the reset path, as the rest of the code may not + * be locking-reentrant enough to behave correctly. + * + * Another, cleaner way should be found to serialise all of + * these operations. + */ +#define MAX_RESET_ITERATIONS 25 +static int +ath_reset_grablock(struct ath_softc *sc, int dowait) +{ + int w = 0; + int i = MAX_RESET_ITERATIONS; + + ATH_PCU_LOCK_ASSERT(sc); + do { + if (sc->sc_inreset_cnt == 0) { + w = 1; + break; + } + if (dowait == 0) { + w = 0; + break; + } + ATH_PCU_UNLOCK(sc); + /* + * 1 tick is likely not enough time for long calibrations + * to complete. So we should wait quite a while. + */ + pause("ath_reset_grablock", msecs_to_ticks(100)); + i--; + ATH_PCU_LOCK(sc); + } while (i > 0); + + /* + * We always increment the refcounter, regardless + * of whether we succeeded to get it in an exclusive + * way. + */ + sc->sc_inreset_cnt++; + + if (i <= 0) + device_printf(sc->sc_dev, + "%s: didn't finish after %d iterations\n", + __func__, MAX_RESET_ITERATIONS); + + if (w == 0) + device_printf(sc->sc_dev, + "%s: warning, recursive reset path!\n", + __func__); + + return w; +} +#undef MAX_RESET_ITERATIONS + /* * Reset the hardware w/o losing operational state. This is * basically a more efficient way of doing ath_stop, ath_init, @@ -1727,28 +2791,108 @@ ath_stop(struct ifnet *ifp) * to reset or reload hardware state. */ int -ath_reset(struct ifnet *ifp) +ath_reset(struct ath_softc *sc, ATH_RESET_TYPE reset_type) { - struct ath_softc *sc = ifp->if_softc; - struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = sc->sc_ah; HAL_STATUS status; + int i; + + DPRINTF(sc, ATH_DEBUG_RESET, "%s: called\n", __func__); + + /* Ensure ATH_LOCK isn't held; ath_rx_proc can't be locked */ + ATH_PCU_UNLOCK_ASSERT(sc); + ATH_UNLOCK_ASSERT(sc); + + /* Try to (stop any further TX/RX from occurring */ + taskqueue_block(sc->sc_tq); + + /* + * Wake the hardware up. + */ + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + + ATH_PCU_LOCK(sc); + + /* + * Grab the reset lock before TX/RX is stopped. + * + * This is needed to ensure that when the TX/RX actually does finish, + * no further TX/RX/reset runs in parallel with this. + */ + if (ath_reset_grablock(sc, 1) == 0) { + device_printf(sc->sc_dev, "%s: concurrent reset! Danger!\n", + __func__); + } + + /* disable interrupts */ + ath_hal_intrset(ah, 0); + + /* + * Now, ensure that any in progress TX/RX completes before we + * continue. + */ + ath_txrx_stop_locked(sc); + + ATH_PCU_UNLOCK(sc); + + /* + * Regardless of whether we're doing a no-loss flush or + * not, stop the PCU and handle what's in the RX queue. + * That way frames aren't dropped which shouldn't be. + */ + ath_stoprecv(sc, (reset_type != ATH_RESET_NOLOSS)); + ath_rx_flush(sc); + + /* + * Should now wait for pending TX/RX to complete + * and block future ones from occurring. This needs to be + * done before the TX queue is drained. + */ + ath_draintxq(sc, reset_type); /* stop xmit side */ - ath_hal_intrset(ah, 0); /* disable interrupts */ - ath_draintxq(sc); /* stop xmit side */ - ath_stoprecv(sc); /* stop recv side */ ath_settkipmic(sc); /* configure TKIP MIC handling */ /* NB: indicate channel change so we do a full reset */ - if (!ath_hal_reset(ah, sc->sc_opmode, ic->ic_curchan, AH_TRUE, &status)) - if_printf(ifp, "%s: unable to reset hardware; hal status %u\n", - __func__, status); + ath_update_chainmasks(sc, ic->ic_curchan); + ath_hal_setchainmasks(sc->sc_ah, sc->sc_cur_txchainmask, + sc->sc_cur_rxchainmask); + if (!ath_hal_reset(ah, sc->sc_opmode, ic->ic_curchan, AH_TRUE, + HAL_RESET_NORMAL, &status)) + device_printf(sc->sc_dev, + "%s: unable to reset hardware; hal status %u\n", + __func__, status); sc->sc_diversity = ath_hal_getdiversity(ah); + ATH_RX_LOCK(sc); + sc->sc_rx_stopped = 1; + sc->sc_rx_resetted = 1; + ATH_RX_UNLOCK(sc); + /* Let DFS at it in case it's a DFS channel */ ath_dfs_radar_enable(sc, ic->ic_curchan); + /* Let spectral at in case spectral is enabled */ + ath_spectral_enable(sc, ic->ic_curchan); + + /* + * Let bluetooth coexistence at in case it's needed for this channel + */ + ath_btcoex_enable(sc, ic->ic_curchan); + + /* + * If we're doing TDMA, enforce the TXOP limitation for chips that + * support it. + */ + if (sc->sc_hasenforcetxop && sc->sc_tdma) + ath_hal_setenforcetxop(sc->sc_ah, 1); + else + ath_hal_setenforcetxop(sc->sc_ah, 0); + if (ath_startrecv(sc) != 0) /* restart recv */ - if_printf(ifp, "%s: unable to start recv logic\n", __func__); + device_printf(sc->sc_dev, + "%s: unable to start recv logic\n", __func__); /* * We may be doing a reset in response to an ioctl * that changes the channel so update any state that @@ -1763,9 +2907,69 @@ ath_reset(struct ifnet *ifp) #endif ath_beacon_config(sc, NULL); } - ath_hal_intrset(ah, sc->sc_imask); - ath_start(ifp); /* restart xmit */ + /* + * Release the reset lock and re-enable interrupts here. + * If an interrupt was being processed in ath_intr(), + * it would disable interrupts at this point. So we have + * to atomically enable interrupts and decrement the + * reset counter - this way ath_intr() doesn't end up + * disabling interrupts without a corresponding enable + * in the rest or channel change path. + * + * Grab the TX reference in case we need to transmit. + * That way a parallel transmit doesn't. + */ + ATH_PCU_LOCK(sc); + sc->sc_inreset_cnt--; + sc->sc_txstart_cnt++; + /* XXX only do this if sc_inreset_cnt == 0? */ + ath_hal_intrset(ah, sc->sc_imask); + ATH_PCU_UNLOCK(sc); + + /* + * TX and RX can be started here. If it were started with + * sc_inreset_cnt > 0, the TX and RX path would abort. + * Thus if this is a nested call through the reset or + * channel change code, TX completion will occur but + * RX completion and ath_start / ath_tx_start will not + * run. + */ + + /* Restart TX/RX as needed */ + ath_txrx_start(sc); + + /* XXX TODO: we need to hold the tx refcount here! */ + + /* Restart TX completion and pending TX */ + if (reset_type == ATH_RESET_NOLOSS) { + for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { + if (ATH_TXQ_SETUP(sc, i)) { + ATH_TXQ_LOCK(&sc->sc_txq[i]); + ath_txq_restart_dma(sc, &sc->sc_txq[i]); + ATH_TXQ_UNLOCK(&sc->sc_txq[i]); + + ATH_TX_LOCK(sc); + ath_txq_sched(sc, &sc->sc_txq[i]); + ATH_TX_UNLOCK(sc); + } + } + } + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + ATH_PCU_LOCK(sc); + sc->sc_txstart_cnt--; + ATH_PCU_UNLOCK(sc); + + /* Handle any frames in the TX queue */ + /* + * XXX should this be done by the caller, rather than + * ath_reset() ? + */ + ath_tx_kick(sc); /* restart xmit */ return 0; } @@ -1773,8 +2977,7 @@ static int ath_reset_vap(struct ieee80211vap *vap, u_long cmd) { struct ieee80211com *ic = vap->iv_ic; - struct ifnet *ifp = ic->ic_ifp; - struct ath_softc *sc = ifp->if_softc; + struct ath_softc *sc = ic->ic_softc; struct ath_hal *ah = sc->sc_ah; switch (cmd) { @@ -1788,141 +2991,450 @@ ath_reset_vap(struct ieee80211vap *vap, u_long cmd) ath_hal_settxpowlimit(ah, ic->ic_txpowlimit); return 0; } - return ath_reset(ifp); + /* XXX? Full or NOLOSS? */ + return ath_reset(sc, ATH_RESET_FULL); } struct ath_buf * -_ath_getbuf_locked(struct ath_softc *sc) +_ath_getbuf_locked(struct ath_softc *sc, ath_buf_type_t btype) { struct ath_buf *bf; ATH_TXBUF_LOCK_ASSERT(sc); - bf = STAILQ_FIRST(&sc->sc_txbuf); - if (bf != NULL && (bf->bf_flags & ATH_BUF_BUSY) == 0) - STAILQ_REMOVE_HEAD(&sc->sc_txbuf, bf_list); + if (btype == ATH_BUFTYPE_MGMT) + bf = TAILQ_FIRST(&sc->sc_txbuf_mgmt); else - bf = NULL; + bf = TAILQ_FIRST(&sc->sc_txbuf); + if (bf == NULL) { - DPRINTF(sc, ATH_DEBUG_XMIT, "%s: %s\n", __func__, - STAILQ_FIRST(&sc->sc_txbuf) == NULL ? - "out of xmit buffers" : "xmit buffer busy"); + sc->sc_stats.ast_tx_getnobuf++; + } else { + if (bf->bf_flags & ATH_BUF_BUSY) { + sc->sc_stats.ast_tx_getbusybuf++; + bf = NULL; + } } + + if (bf != NULL && (bf->bf_flags & ATH_BUF_BUSY) == 0) { + if (btype == ATH_BUFTYPE_MGMT) + TAILQ_REMOVE(&sc->sc_txbuf_mgmt, bf, bf_list); + else { + TAILQ_REMOVE(&sc->sc_txbuf, bf, bf_list); + sc->sc_txbuf_cnt--; + + /* + * This shuldn't happen; however just to be + * safe print a warning and fudge the txbuf + * count. + */ + if (sc->sc_txbuf_cnt < 0) { + device_printf(sc->sc_dev, + "%s: sc_txbuf_cnt < 0?\n", + __func__); + sc->sc_txbuf_cnt = 0; + } + } + } else + bf = NULL; + + if (bf == NULL) { + /* XXX should check which list, mgmt or otherwise */ + DPRINTF(sc, ATH_DEBUG_XMIT, "%s: %s\n", __func__, + TAILQ_FIRST(&sc->sc_txbuf) == NULL ? + "out of xmit buffers" : "xmit buffer busy"); + return NULL; + } + + /* XXX TODO: should do this at buffer list initialisation */ + /* XXX (then, ensure the buffer has the right flag set) */ + bf->bf_flags = 0; + if (btype == ATH_BUFTYPE_MGMT) + bf->bf_flags |= ATH_BUF_MGMT; + else + bf->bf_flags &= (~ATH_BUF_MGMT); + + /* Valid bf here; clear some basic fields */ + bf->bf_next = NULL; /* XXX just to be sure */ + bf->bf_last = NULL; /* XXX again, just to be sure */ + bf->bf_comp = NULL; /* XXX again, just to be sure */ + bzero(&bf->bf_state, sizeof(bf->bf_state)); + + /* + * Track the descriptor ID only if doing EDMA + */ + if (sc->sc_isedma) { + bf->bf_descid = sc->sc_txbuf_descid; + sc->sc_txbuf_descid++; + } + return bf; } +/* + * When retrying a software frame, buffers marked ATH_BUF_BUSY + * can't be thrown back on the queue as they could still be + * in use by the hardware. + * + * This duplicates the buffer, or returns NULL. + * + * The descriptor is also copied but the link pointers and + * the DMA segments aren't copied; this frame should thus + * be again passed through the descriptor setup/chain routines + * so the link is correct. + * + * The caller must free the buffer using ath_freebuf(). + */ struct ath_buf * -ath_getbuf(struct ath_softc *sc) +ath_buf_clone(struct ath_softc *sc, struct ath_buf *bf) +{ + struct ath_buf *tbf; + + tbf = ath_getbuf(sc, + (bf->bf_flags & ATH_BUF_MGMT) ? + ATH_BUFTYPE_MGMT : ATH_BUFTYPE_NORMAL); + if (tbf == NULL) + return NULL; /* XXX failure? Why? */ + + /* Copy basics */ + tbf->bf_next = NULL; + tbf->bf_nseg = bf->bf_nseg; + tbf->bf_flags = bf->bf_flags & ATH_BUF_FLAGS_CLONE; + tbf->bf_status = bf->bf_status; + tbf->bf_m = bf->bf_m; + tbf->bf_node = bf->bf_node; + KASSERT((bf->bf_node != NULL), ("%s: bf_node=NULL!", __func__)); + /* will be setup by the chain/setup function */ + tbf->bf_lastds = NULL; + /* for now, last == self */ + tbf->bf_last = tbf; + tbf->bf_comp = bf->bf_comp; + + /* NOTE: DMA segments will be setup by the setup/chain functions */ + + /* The caller has to re-init the descriptor + links */ + + /* + * Free the DMA mapping here, before we NULL the mbuf. + * We must only call bus_dmamap_unload() once per mbuf chain + * or behaviour is undefined. + */ + if (bf->bf_m != NULL) { + /* + * XXX is this POSTWRITE call required? + */ + bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); + } + + bf->bf_m = NULL; + bf->bf_node = NULL; + + /* Copy state */ + memcpy(&tbf->bf_state, &bf->bf_state, sizeof(bf->bf_state)); + + return tbf; +} + +struct ath_buf * +ath_getbuf(struct ath_softc *sc, ath_buf_type_t btype) { struct ath_buf *bf; ATH_TXBUF_LOCK(sc); - bf = _ath_getbuf_locked(sc); + bf = _ath_getbuf_locked(sc, btype); + /* + * If a mgmt buffer was requested but we're out of those, + * try requesting a normal one. + */ + if (bf == NULL && btype == ATH_BUFTYPE_MGMT) + bf = _ath_getbuf_locked(sc, ATH_BUFTYPE_NORMAL); + ATH_TXBUF_UNLOCK(sc); if (bf == NULL) { - struct ifnet *ifp = sc->sc_ifp; - DPRINTF(sc, ATH_DEBUG_XMIT, "%s: stop queue\n", __func__); sc->sc_stats.ast_tx_qstop++; - ifp->if_drv_flags |= IFF_DRV_OACTIVE; } - ATH_TXBUF_UNLOCK(sc); return bf; } -static void -ath_start(struct ifnet *ifp) +/* + * Transmit a single frame. + * + * net80211 will free the node reference if the transmit + * fails, so don't free the node reference here. + */ +static int +ath_transmit(struct ieee80211com *ic, struct mbuf *m) { - struct ath_softc *sc = ifp->if_softc; + struct ath_softc *sc = ic->ic_softc; struct ieee80211_node *ni; + struct mbuf *next; struct ath_buf *bf; - struct mbuf *m, *next; ath_bufhead frags; + int retval = 0; - if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || sc->sc_invalid) - return; - for (;;) { - /* - * Grab a TX buffer and associated resources. - */ - bf = ath_getbuf(sc); - if (bf == NULL) - break; - - IFQ_DEQUEUE(&ifp->if_snd, m); - if (m == NULL) { - ATH_TXBUF_LOCK(sc); - STAILQ_INSERT_HEAD(&sc->sc_txbuf, bf, bf_list); - ATH_TXBUF_UNLOCK(sc); - break; - } - ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; - /* - * Check for fragmentation. If this frame - * has been broken up verify we have enough - * buffers to send all the fragments so all - * go out or none... - */ - STAILQ_INIT(&frags); - if ((m->m_flags & M_FRAG) && - !ath_txfrag_setup(sc, &frags, m, ni)) { - DPRINTF(sc, ATH_DEBUG_XMIT, - "%s: out of txfrag buffers\n", __func__); - sc->sc_stats.ast_tx_nofrag++; - ifp->if_oerrors++; - ath_freetx(m); - goto bad; - } - ifp->if_opackets++; - nextfrag: - /* - * Pass the frame to the h/w for transmission. - * Fragmented frames have each frag chained together - * with m_nextpkt. We know there are sufficient ath_buf's - * to send all the frags because of work done by - * ath_txfrag_setup. We leave m_nextpkt set while - * calling ath_tx_start so it can use it to extend the - * the tx duration to cover the subsequent frag and - * so it can reclaim all the mbufs in case of an error; - * ath_tx_start clears m_nextpkt once it commits to - * handing the frame to the hardware. - */ - next = m->m_nextpkt; - if (ath_tx_start(sc, ni, bf, m)) { - bad: - ifp->if_oerrors++; - reclaim: - bf->bf_m = NULL; - bf->bf_node = NULL; - ATH_TXBUF_LOCK(sc); - STAILQ_INSERT_HEAD(&sc->sc_txbuf, bf, bf_list); - ath_txfrag_cleanup(sc, &frags, ni); - ATH_TXBUF_UNLOCK(sc); - if (ni != NULL) - ieee80211_free_node(ni); - continue; - } - if (next != NULL) { - /* - * Beware of state changing between frags. - * XXX check sta power-save state? - */ - if (ni->ni_vap->iv_state != IEEE80211_S_RUN) { - DPRINTF(sc, ATH_DEBUG_XMIT, - "%s: flush fragmented packet, state %s\n", - __func__, - ieee80211_state_name[ni->ni_vap->iv_state]); - ath_freetx(next); - goto reclaim; - } - m = next; - bf = STAILQ_FIRST(&frags); - KASSERT(bf != NULL, ("no buf for txfrag")); - STAILQ_REMOVE_HEAD(&frags, bf_list); - goto nextfrag; - } - - sc->sc_wd_timer = 5; + /* + * Tell the reset path that we're currently transmitting. + */ + ATH_PCU_LOCK(sc); + if (sc->sc_inreset_cnt > 0) { + DPRINTF(sc, ATH_DEBUG_XMIT, + "%s: sc_inreset_cnt > 0; bailing\n", __func__); + ATH_PCU_UNLOCK(sc); + sc->sc_stats.ast_tx_qstop++; + ATH_KTR(sc, ATH_KTR_TX, 0, "ath_start_task: OACTIVE, finish"); + return (ENOBUFS); /* XXX should be EINVAL or? */ } + sc->sc_txstart_cnt++; + ATH_PCU_UNLOCK(sc); + + /* Wake the hardware up already */ + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + + ATH_KTR(sc, ATH_KTR_TX, 0, "ath_transmit: start"); + /* + * Grab the TX lock - it's ok to do this here; we haven't + * yet started transmitting. + */ + ATH_TX_LOCK(sc); + + /* + * Node reference, if there's one. + */ + ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; + + /* + * Enforce how deep a node queue can get. + * + * XXX it would be nicer if we kept an mbuf queue per + * node and only whacked them into ath_bufs when we + * are ready to schedule some traffic from them. + * .. that may come later. + * + * XXX we should also track the per-node hardware queue + * depth so it is easy to limit the _SUM_ of the swq and + * hwq frames. Since we only schedule two HWQ frames + * at a time, this should be OK for now. + */ + if ((!(m->m_flags & M_EAPOL)) && + (ATH_NODE(ni)->an_swq_depth > sc->sc_txq_node_maxdepth)) { + sc->sc_stats.ast_tx_nodeq_overflow++; + retval = ENOBUFS; + goto finish; + } + + /* + * Check how many TX buffers are available. + * + * If this is for non-EAPOL traffic, just leave some + * space free in order for buffer cloning and raw + * frame transmission to occur. + * + * If it's for EAPOL traffic, ignore this for now. + * Management traffic will be sent via the raw transmit + * method which bypasses this check. + * + * This is needed to ensure that EAPOL frames during + * (re) keying have a chance to go out. + * + * See kern/138379 for more information. + */ + if ((!(m->m_flags & M_EAPOL)) && + (sc->sc_txbuf_cnt <= sc->sc_txq_data_minfree)) { + sc->sc_stats.ast_tx_nobuf++; + retval = ENOBUFS; + goto finish; + } + + /* + * Grab a TX buffer and associated resources. + * + * If it's an EAPOL frame, allocate a MGMT ath_buf. + * That way even with temporary buffer exhaustion due to + * the data path doesn't leave us without the ability + * to transmit management frames. + * + * Otherwise allocate a normal buffer. + */ + if (m->m_flags & M_EAPOL) + bf = ath_getbuf(sc, ATH_BUFTYPE_MGMT); + else + bf = ath_getbuf(sc, ATH_BUFTYPE_NORMAL); + + if (bf == NULL) { + /* + * If we failed to allocate a buffer, fail. + * + * We shouldn't fail normally, due to the check + * above. + */ + sc->sc_stats.ast_tx_nobuf++; + retval = ENOBUFS; + goto finish; + } + + /* + * At this point we have a buffer; so we need to free it + * if we hit any error conditions. + */ + + /* + * Check for fragmentation. If this frame + * has been broken up verify we have enough + * buffers to send all the fragments so all + * go out or none... + */ + TAILQ_INIT(&frags); + if ((m->m_flags & M_FRAG) && + !ath_txfrag_setup(sc, &frags, m, ni)) { + DPRINTF(sc, ATH_DEBUG_XMIT, + "%s: out of txfrag buffers\n", __func__); + sc->sc_stats.ast_tx_nofrag++; + if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); + /* + * XXXGL: is mbuf valid after ath_txfrag_setup? If yes, + * we shouldn't free it but return back. + */ + ieee80211_free_mbuf(m); + m = NULL; + goto bad; + } + + /* + * At this point if we have any TX fragments, then we will + * have bumped the node reference once for each of those. + */ + + /* + * XXX Is there anything actually _enforcing_ that the + * fragments are being transmitted in one hit, rather than + * being interleaved with other transmissions on that + * hardware queue? + * + * The ATH TX output lock is the only thing serialising this + * right now. + */ + + /* + * Calculate the "next fragment" length field in ath_buf + * in order to let the transmit path know enough about + * what to next write to the hardware. + */ + if (m->m_flags & M_FRAG) { + struct ath_buf *fbf = bf; + struct ath_buf *n_fbf = NULL; + struct mbuf *fm = m->m_nextpkt; + + /* + * We need to walk the list of fragments and set + * the next size to the following buffer. + * However, the first buffer isn't in the frag + * list, so we have to do some gymnastics here. + */ + TAILQ_FOREACH(n_fbf, &frags, bf_list) { + fbf->bf_nextfraglen = fm->m_pkthdr.len; + fbf = n_fbf; + fm = fm->m_nextpkt; + } + } + +nextfrag: + /* + * Pass the frame to the h/w for transmission. + * Fragmented frames have each frag chained together + * with m_nextpkt. We know there are sufficient ath_buf's + * to send all the frags because of work done by + * ath_txfrag_setup. We leave m_nextpkt set while + * calling ath_tx_start so it can use it to extend the + * the tx duration to cover the subsequent frag and + * so it can reclaim all the mbufs in case of an error; + * ath_tx_start clears m_nextpkt once it commits to + * handing the frame to the hardware. + * + * Note: if this fails, then the mbufs are freed but + * not the node reference. + * + * So, we now have to free the node reference ourselves here + * and return OK up to the stack. + */ + next = m->m_nextpkt; + if (ath_tx_start(sc, ni, bf, m)) { +bad: + if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); +reclaim: + bf->bf_m = NULL; + bf->bf_node = NULL; + ATH_TXBUF_LOCK(sc); + ath_returnbuf_head(sc, bf); + /* + * Free the rest of the node references and + * buffers for the fragment list. + */ + ath_txfrag_cleanup(sc, &frags, ni); + ATH_TXBUF_UNLOCK(sc); + + /* + * XXX: And free the node/return OK; ath_tx_start() may have + * modified the buffer. We currently have no way to + * signify that the mbuf was freed but there was an error. + */ + ieee80211_free_node(ni); + retval = 0; + goto finish; + } + + /* + * Check here if the node is in power save state. + */ + ath_tx_update_tim(sc, ni, 1); + + if (next != NULL) { + /* + * Beware of state changing between frags. + * XXX check sta power-save state? + */ + if (ni->ni_vap->iv_state != IEEE80211_S_RUN) { + DPRINTF(sc, ATH_DEBUG_XMIT, + "%s: flush fragmented packet, state %s\n", + __func__, + ieee80211_state_name[ni->ni_vap->iv_state]); + /* XXX dmamap */ + ieee80211_free_mbuf(next); + goto reclaim; + } + m = next; + bf = TAILQ_FIRST(&frags); + KASSERT(bf != NULL, ("no buf for txfrag")); + TAILQ_REMOVE(&frags, bf, bf_list); + goto nextfrag; + } + + /* + * Bump watchdog timer. + */ + sc->sc_wd_timer = 5; + +finish: + ATH_TX_UNLOCK(sc); + + /* + * Finished transmitting! + */ + ATH_PCU_LOCK(sc); + sc->sc_txstart_cnt--; + ATH_PCU_UNLOCK(sc); + + /* Sleep the hardware if required */ + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + ATH_KTR(sc, ATH_KTR_TX, 0, "ath_transmit: finished"); + + return (retval); } static int @@ -1942,171 +3454,114 @@ ath_media_change(struct ifnet *ifp) static void ath_key_update_begin(struct ieee80211vap *vap) { - struct ifnet *ifp = vap->iv_ic->ic_ifp; - struct ath_softc *sc = ifp->if_softc; + struct ath_softc *sc = vap->iv_ic->ic_softc; DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__); taskqueue_block(sc->sc_tq); - IF_LOCK(&ifp->if_snd); /* NB: doesn't block mgmt frames */ } static void ath_key_update_end(struct ieee80211vap *vap) { - struct ifnet *ifp = vap->iv_ic->ic_ifp; - struct ath_softc *sc = ifp->if_softc; + struct ath_softc *sc = vap->iv_ic->ic_softc; DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__); - IF_UNLOCK(&ifp->if_snd); taskqueue_unblock(sc->sc_tq); } -/* - * Calculate the receive filter according to the - * operating mode and state: - * - * o always accept unicast, broadcast, and multicast traffic - * o accept PHY error frames when hardware doesn't have MIB support - * to count and we need them for ANI (sta mode only until recently) - * and we are not scanning (ANI is disabled) - * NB: older hal's add rx filter bits out of sight and we need to - * blindly preserve them - * o probe request frames are accepted only when operating in - * hostap, adhoc, mesh, or monitor modes - * o enable promiscuous mode - * - when in monitor mode - * - if interface marked PROMISC (assumes bridge setting is filtered) - * o accept beacons: - * - when operating in station mode for collecting rssi data when - * the station is otherwise quiet, or - * - when operating in adhoc mode so the 802.11 layer creates - * node table entries for peers, - * - when scanning - * - when doing s/w beacon miss (e.g. for ap+sta) - * - when operating in ap mode in 11g to detect overlapping bss that - * require protection - * - when operating in mesh mode to detect neighbors - * o accept control frames: - * - when in monitor mode - * XXX HT protection for 11n - */ -static u_int32_t -ath_calcrxfilter(struct ath_softc *sc) -{ - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - u_int32_t rfilt; - - rfilt = HAL_RX_FILTER_UCAST | HAL_RX_FILTER_BCAST | HAL_RX_FILTER_MCAST; - if (!sc->sc_needmib && !sc->sc_scanning) - rfilt |= HAL_RX_FILTER_PHYERR; - if (ic->ic_opmode != IEEE80211_M_STA) - rfilt |= HAL_RX_FILTER_PROBEREQ; - /* XXX ic->ic_monvaps != 0? */ - if (ic->ic_opmode == IEEE80211_M_MONITOR || (ifp->if_flags & IFF_PROMISC)) - rfilt |= HAL_RX_FILTER_PROM; - if (ic->ic_opmode == IEEE80211_M_STA || - ic->ic_opmode == IEEE80211_M_IBSS || - sc->sc_swbmiss || sc->sc_scanning) - rfilt |= HAL_RX_FILTER_BEACON; - /* - * NB: We don't recalculate the rx filter when - * ic_protmode changes; otherwise we could do - * this only when ic_protmode != NONE. - */ - if (ic->ic_opmode == IEEE80211_M_HOSTAP && - IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) - rfilt |= HAL_RX_FILTER_BEACON; - - /* - * Enable hardware PS-POLL RX only for hostap mode; - * STA mode sends PS-POLL frames but never - * receives them. - */ - if (ath_hal_getcapability(sc->sc_ah, HAL_CAP_PSPOLL, - 0, NULL) == HAL_OK && - ic->ic_opmode == IEEE80211_M_HOSTAP) - rfilt |= HAL_RX_FILTER_PSPOLL; - - if (sc->sc_nmeshvaps) { - rfilt |= HAL_RX_FILTER_BEACON; - if (sc->sc_hasbmatch) - rfilt |= HAL_RX_FILTER_BSSID; - else - rfilt |= HAL_RX_FILTER_PROM; - } - if (ic->ic_opmode == IEEE80211_M_MONITOR) - rfilt |= HAL_RX_FILTER_CONTROL; - - if (sc->sc_dodfs) { - rfilt |= HAL_RX_FILTER_PHYRADAR; - } - - /* - * Enable RX of compressed BAR frames only when doing - * 802.11n. Required for A-MPDU. - */ - if (IEEE80211_IS_CHAN_HT(ic->ic_curchan)) - rfilt |= HAL_RX_FILTER_COMPBAR; - - DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x, %s if_flags 0x%x\n", - __func__, rfilt, ieee80211_opmode_name[ic->ic_opmode], ifp->if_flags); - return rfilt; -} - static void -ath_update_promisc(struct ifnet *ifp) +ath_update_promisc(struct ieee80211com *ic) { - struct ath_softc *sc = ifp->if_softc; + struct ath_softc *sc = ic->ic_softc; u_int32_t rfilt; /* configure rx filter */ + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); rfilt = ath_calcrxfilter(sc); ath_hal_setrxfilter(sc->sc_ah, rfilt); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x\n", __func__, rfilt); } +/* + * Driver-internal mcast update call. + * + * Assumes the hardware is already awake. + */ static void -ath_update_mcast(struct ifnet *ifp) +ath_update_mcast_hw(struct ath_softc *sc) { - struct ath_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; u_int32_t mfilt[2]; /* calculate and install multicast filter */ - if ((ifp->if_flags & IFF_ALLMULTI) == 0) { + if (ic->ic_allmulti == 0) { + struct ieee80211vap *vap; + struct ifnet *ifp; struct ifmultiaddr *ifma; + /* * Merge multicast addresses to form the hardware filter. */ mfilt[0] = mfilt[1] = 0; - if_maddr_rlock(ifp); /* XXX need some fiddling to remove? */ - TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { - caddr_t dl; - u_int32_t val; - u_int8_t pos; + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + ifp = vap->iv_ifp; + if_maddr_rlock(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + caddr_t dl; + uint32_t val; + uint8_t pos; - /* calculate XOR of eight 6bit values */ - dl = LLADDR((struct sockaddr_dl *) ifma->ifma_addr); - val = LE_READ_4(dl + 0); - pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; - val = LE_READ_4(dl + 3); - pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; - pos &= 0x3f; - mfilt[pos / 32] |= (1 << (pos % 32)); + /* calculate XOR of eight 6bit values */ + dl = LLADDR((struct sockaddr_dl *) + ifma->ifma_addr); + val = le32dec(dl + 0); + pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ + val; + val = le32dec(dl + 3); + pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ + val; + pos &= 0x3f; + mfilt[pos / 32] |= (1 << (pos % 32)); + } + if_maddr_runlock(ifp); } - if_maddr_runlock(ifp); } else mfilt[0] = mfilt[1] = ~0; + ath_hal_setmcastfilter(sc->sc_ah, mfilt[0], mfilt[1]); + DPRINTF(sc, ATH_DEBUG_MODE, "%s: MC filter %08x:%08x\n", __func__, mfilt[0], mfilt[1]); } +/* + * Called from the net80211 layer - force the hardware + * awake before operating. + */ static void +ath_update_mcast(struct ieee80211com *ic) +{ + struct ath_softc *sc = ic->ic_softc; + + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + + ath_update_mcast_hw(sc); + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); +} + +void ath_mode_init(struct ath_softc *sc) { - struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = sc->sc_ah; u_int32_t rfilt; @@ -2118,19 +3573,19 @@ ath_mode_init(struct ath_softc *sc) ath_hal_setopmode(ah); /* handle any link-level address change */ - ath_hal_setmac(ah, IF_LLADDR(ifp)); + ath_hal_setmac(ah, ic->ic_macaddr); /* calculate and install multicast filter */ - ath_update_mcast(ifp); + ath_update_mcast_hw(sc); } /* * Set the slot time based on the current setting. */ -static void +void ath_setslottime(struct ath_softc *sc) { - struct ieee80211com *ic = sc->sc_ifp->if_l2com; + struct ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = sc->sc_ah; u_int usec; @@ -2153,8 +3608,13 @@ ath_setslottime(struct ath_softc *sc) __func__, ic->ic_curchan->ic_freq, ic->ic_curchan->ic_flags, ic->ic_flags & IEEE80211_F_SHSLOT ? "short" : "long", usec); + /* Wake up the hardware first before updating the slot time */ + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); ath_hal_setslottime(ah, usec); + ath_power_restore_power_state(sc); sc->sc_updateslot = OK; + ATH_UNLOCK(sc); } /* @@ -2162,15 +3622,16 @@ ath_setslottime(struct ath_softc *sc) * slot time based on the current setting. */ static void -ath_updateslot(struct ifnet *ifp) +ath_updateslot(struct ieee80211com *ic) { - struct ath_softc *sc = ifp->if_softc; - struct ieee80211com *ic = ifp->if_l2com; + struct ath_softc *sc = ic->ic_softc; /* * When not coordinating the BSS, change the hardware * immediately. For other operation we defer the change * until beacon updates have propagated to the stations. + * + * XXX sc_updateslot isn't changed behind a lock? */ if (ic->ic_opmode == IEEE80211_M_HOSTAP || ic->ic_opmode == IEEE80211_M_MBSS) @@ -2179,498 +3640,40 @@ ath_updateslot(struct ifnet *ifp) ath_setslottime(sc); } -/* - * Setup a h/w transmit queue for beacons. - */ -static int -ath_beaconq_setup(struct ath_hal *ah) -{ - HAL_TXQ_INFO qi; - - memset(&qi, 0, sizeof(qi)); - qi.tqi_aifs = HAL_TXQ_USEDEFAULT; - qi.tqi_cwmin = HAL_TXQ_USEDEFAULT; - qi.tqi_cwmax = HAL_TXQ_USEDEFAULT; - /* NB: for dynamic turbo, don't enable any other interrupts */ - qi.tqi_qflags = HAL_TXQ_TXDESCINT_ENABLE; - return ath_hal_setuptxqueue(ah, HAL_TX_QUEUE_BEACON, &qi); -} - -/* - * Setup the transmit queue parameters for the beacon queue. - */ -static int -ath_beaconq_config(struct ath_softc *sc) -{ -#define ATH_EXPONENT_TO_VALUE(v) ((1<<(v))-1) - struct ieee80211com *ic = sc->sc_ifp->if_l2com; - struct ath_hal *ah = sc->sc_ah; - HAL_TXQ_INFO qi; - - ath_hal_gettxqueueprops(ah, sc->sc_bhalq, &qi); - if (ic->ic_opmode == IEEE80211_M_HOSTAP || - ic->ic_opmode == IEEE80211_M_MBSS) { - /* - * Always burst out beacon and CAB traffic. - */ - qi.tqi_aifs = ATH_BEACON_AIFS_DEFAULT; - qi.tqi_cwmin = ATH_BEACON_CWMIN_DEFAULT; - qi.tqi_cwmax = ATH_BEACON_CWMAX_DEFAULT; - } else { - struct wmeParams *wmep = - &ic->ic_wme.wme_chanParams.cap_wmeParams[WME_AC_BE]; - /* - * Adhoc mode; important thing is to use 2x cwmin. - */ - qi.tqi_aifs = wmep->wmep_aifsn; - qi.tqi_cwmin = 2*ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmin); - qi.tqi_cwmax = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmax); - } - - if (!ath_hal_settxqueueprops(ah, sc->sc_bhalq, &qi)) { - device_printf(sc->sc_dev, "unable to update parameters for " - "beacon hardware queue!\n"); - return 0; - } else { - ath_hal_resettxqueue(ah, sc->sc_bhalq); /* push to h/w */ - return 1; - } -#undef ATH_EXPONENT_TO_VALUE -} - -/* - * Allocate and setup an initial beacon frame. - */ -static int -ath_beacon_alloc(struct ath_softc *sc, struct ieee80211_node *ni) -{ - struct ieee80211vap *vap = ni->ni_vap; - struct ath_vap *avp = ATH_VAP(vap); - struct ath_buf *bf; - struct mbuf *m; - int error; - - bf = avp->av_bcbuf; - if (bf->bf_m != NULL) { - bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); - m_freem(bf->bf_m); - bf->bf_m = NULL; - } - if (bf->bf_node != NULL) { - ieee80211_free_node(bf->bf_node); - bf->bf_node = NULL; - } - - /* - * NB: the beacon data buffer must be 32-bit aligned; - * we assume the mbuf routines will return us something - * with this alignment (perhaps should assert). - */ - m = ieee80211_beacon_alloc(ni, &avp->av_boff); - if (m == NULL) { - device_printf(sc->sc_dev, "%s: cannot get mbuf\n", __func__); - sc->sc_stats.ast_be_nombuf++; - return ENOMEM; - } - error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m, - bf->bf_segs, &bf->bf_nseg, - BUS_DMA_NOWAIT); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: cannot map mbuf, bus_dmamap_load_mbuf_sg returns %d\n", - __func__, error); - m_freem(m); - return error; - } - - /* - * Calculate a TSF adjustment factor required for staggered - * beacons. Note that we assume the format of the beacon - * frame leaves the tstamp field immediately following the - * header. - */ - if (sc->sc_stagbeacons && avp->av_bslot > 0) { - uint64_t tsfadjust; - struct ieee80211_frame *wh; - - /* - * The beacon interval is in TU's; the TSF is in usecs. - * We figure out how many TU's to add to align the timestamp - * then convert to TSF units and handle byte swapping before - * inserting it in the frame. The hardware will then add this - * each time a beacon frame is sent. Note that we align vap's - * 1..N and leave vap 0 untouched. This means vap 0 has a - * timestamp in one beacon interval while the others get a - * timstamp aligned to the next interval. - */ - tsfadjust = ni->ni_intval * - (ATH_BCBUF - avp->av_bslot) / ATH_BCBUF; - tsfadjust = htole64(tsfadjust << 10); /* TU -> TSF */ - - DPRINTF(sc, ATH_DEBUG_BEACON, - "%s: %s beacons bslot %d intval %u tsfadjust %llu\n", - __func__, sc->sc_stagbeacons ? "stagger" : "burst", - avp->av_bslot, ni->ni_intval, - (long long unsigned) le64toh(tsfadjust)); - - wh = mtod(m, struct ieee80211_frame *); - memcpy(&wh[1], &tsfadjust, sizeof(tsfadjust)); - } - bf->bf_m = m; - bf->bf_node = ieee80211_ref_node(ni); - - return 0; -} - -/* - * Setup the beacon frame for transmit. - */ -static void -ath_beacon_setup(struct ath_softc *sc, struct ath_buf *bf) -{ -#define USE_SHPREAMBLE(_ic) \ - (((_ic)->ic_flags & (IEEE80211_F_SHPREAMBLE | IEEE80211_F_USEBARKER))\ - == IEEE80211_F_SHPREAMBLE) - struct ieee80211_node *ni = bf->bf_node; - struct ieee80211com *ic = ni->ni_ic; - struct mbuf *m = bf->bf_m; - struct ath_hal *ah = sc->sc_ah; - struct ath_desc *ds; - int flags, antenna; - const HAL_RATE_TABLE *rt; - u_int8_t rix, rate; - - DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s: m %p len %u\n", - __func__, m, m->m_len); - - /* setup descriptors */ - ds = bf->bf_desc; - - flags = HAL_TXDESC_NOACK; - if (ic->ic_opmode == IEEE80211_M_IBSS && sc->sc_hasveol) { - ds->ds_link = bf->bf_daddr; /* self-linked */ - flags |= HAL_TXDESC_VEOL; - /* - * Let hardware handle antenna switching. - */ - antenna = sc->sc_txantenna; - } else { - ds->ds_link = 0; - /* - * Switch antenna every 4 beacons. - * XXX assumes two antenna - */ - if (sc->sc_txantenna != 0) - antenna = sc->sc_txantenna; - else if (sc->sc_stagbeacons && sc->sc_nbcnvaps != 0) - antenna = ((sc->sc_stats.ast_be_xmit / sc->sc_nbcnvaps) & 4 ? 2 : 1); - else - antenna = (sc->sc_stats.ast_be_xmit & 4 ? 2 : 1); - } - - KASSERT(bf->bf_nseg == 1, - ("multi-segment beacon frame; nseg %u", bf->bf_nseg)); - ds->ds_data = bf->bf_segs[0].ds_addr; - /* - * Calculate rate code. - * XXX everything at min xmit rate - */ - rix = 0; - rt = sc->sc_currates; - rate = rt->info[rix].rateCode; - if (USE_SHPREAMBLE(ic)) - rate |= rt->info[rix].shortPreamble; - ath_hal_setuptxdesc(ah, ds - , m->m_len + IEEE80211_CRC_LEN /* frame length */ - , sizeof(struct ieee80211_frame)/* header length */ - , HAL_PKT_TYPE_BEACON /* Atheros packet type */ - , ni->ni_txpower /* txpower XXX */ - , rate, 1 /* series 0 rate/tries */ - , HAL_TXKEYIX_INVALID /* no encryption */ - , antenna /* antenna mode */ - , flags /* no ack, veol for beacons */ - , 0 /* rts/cts rate */ - , 0 /* rts/cts duration */ - ); - /* NB: beacon's BufLen must be a multiple of 4 bytes */ - ath_hal_filltxdesc(ah, ds - , roundup(m->m_len, 4) /* buffer length */ - , AH_TRUE /* first segment */ - , AH_TRUE /* last segment */ - , ds /* first descriptor */ - ); -#if 0 - ath_desc_swap(ds); -#endif -#undef USE_SHPREAMBLE -} - -static void -ath_beacon_update(struct ieee80211vap *vap, int item) -{ - struct ieee80211_beacon_offsets *bo = &ATH_VAP(vap)->av_boff; - - setbit(bo->bo_flags, item); -} - /* * Append the contents of src to dst; both queues * are assumed to be locked. */ -static void +void ath_txqmove(struct ath_txq *dst, struct ath_txq *src) { - STAILQ_CONCAT(&dst->axq_q, &src->axq_q); + + ATH_TXQ_LOCK_ASSERT(src); + ATH_TXQ_LOCK_ASSERT(dst); + + TAILQ_CONCAT(&dst->axq_q, &src->axq_q, bf_list); dst->axq_link = src->axq_link; src->axq_link = NULL; dst->axq_depth += src->axq_depth; + dst->axq_aggr_depth += src->axq_aggr_depth; src->axq_depth = 0; + src->axq_aggr_depth = 0; } /* - * Transmit a beacon frame at SWBA. Dynamic updates to the - * frame contents are done as needed and the slot time is - * also adjusted based on current state. + * Reset the hardware, with no loss. + * + * This can't be used for a general case reset. */ static void -ath_beacon_proc(void *arg, int pending) +ath_reset_proc(void *arg, int pending) { struct ath_softc *sc = arg; - struct ath_hal *ah = sc->sc_ah; - struct ieee80211vap *vap; - struct ath_buf *bf; - int slot, otherant; - uint32_t bfaddr; - DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s: pending %u\n", - __func__, pending); - /* - * Check if the previous beacon has gone out. If - * not don't try to post another, skip this period - * and wait for the next. Missed beacons indicate - * a problem and should not occur. If we miss too - * many consecutive beacons reset the device. - */ - if (ath_hal_numtxpending(ah, sc->sc_bhalq) != 0) { - sc->sc_bmisscount++; - sc->sc_stats.ast_be_missed++; - DPRINTF(sc, ATH_DEBUG_BEACON, - "%s: missed %u consecutive beacons\n", - __func__, sc->sc_bmisscount); - if (sc->sc_bmisscount >= ath_bstuck_threshold) - taskqueue_enqueue(sc->sc_tq, &sc->sc_bstucktask); - return; - } - if (sc->sc_bmisscount != 0) { - DPRINTF(sc, ATH_DEBUG_BEACON, - "%s: resume beacon xmit after %u misses\n", - __func__, sc->sc_bmisscount); - sc->sc_bmisscount = 0; - } - - if (sc->sc_stagbeacons) { /* staggered beacons */ - struct ieee80211com *ic = sc->sc_ifp->if_l2com; - uint32_t tsftu; - - tsftu = ath_hal_gettsf32(ah) >> 10; - /* XXX lintval */ - slot = ((tsftu % ic->ic_lintval) * ATH_BCBUF) / ic->ic_lintval; - vap = sc->sc_bslot[(slot+1) % ATH_BCBUF]; - bfaddr = 0; - if (vap != NULL && vap->iv_state >= IEEE80211_S_RUN) { - bf = ath_beacon_generate(sc, vap); - if (bf != NULL) - bfaddr = bf->bf_daddr; - } - } else { /* burst'd beacons */ - uint32_t *bflink = &bfaddr; - - for (slot = 0; slot < ATH_BCBUF; slot++) { - vap = sc->sc_bslot[slot]; - if (vap != NULL && vap->iv_state >= IEEE80211_S_RUN) { - bf = ath_beacon_generate(sc, vap); - if (bf != NULL) { - *bflink = bf->bf_daddr; - bflink = &bf->bf_desc->ds_link; - } - } - } - *bflink = 0; /* terminate list */ - } - - /* - * Handle slot time change when a non-ERP station joins/leaves - * an 11g network. The 802.11 layer notifies us via callback, - * we mark updateslot, then wait one beacon before effecting - * the change. This gives associated stations at least one - * beacon interval to note the state change. - */ - /* XXX locking */ - if (sc->sc_updateslot == UPDATE) { - sc->sc_updateslot = COMMIT; /* commit next beacon */ - sc->sc_slotupdate = slot; - } else if (sc->sc_updateslot == COMMIT && sc->sc_slotupdate == slot) - ath_setslottime(sc); /* commit change to h/w */ - - /* - * Check recent per-antenna transmit statistics and flip - * the default antenna if noticeably more frames went out - * on the non-default antenna. - * XXX assumes 2 anntenae - */ - if (!sc->sc_diversity && (!sc->sc_stagbeacons || slot == 0)) { - otherant = sc->sc_defant & 1 ? 2 : 1; - if (sc->sc_ant_tx[otherant] > sc->sc_ant_tx[sc->sc_defant] + 2) - ath_setdefantenna(sc, otherant); - sc->sc_ant_tx[1] = sc->sc_ant_tx[2] = 0; - } - - if (bfaddr != 0) { - /* - * Stop any current dma and put the new frame on the queue. - * This should never fail since we check above that no frames - * are still pending on the queue. - */ - if (!ath_hal_stoptxdma(ah, sc->sc_bhalq)) { - DPRINTF(sc, ATH_DEBUG_ANY, - "%s: beacon queue %u did not stop?\n", - __func__, sc->sc_bhalq); - } - /* NB: cabq traffic should already be queued and primed */ - ath_hal_puttxbuf(ah, sc->sc_bhalq, bfaddr); - ath_hal_txstart(ah, sc->sc_bhalq); - - sc->sc_stats.ast_be_xmit++; - } -} - -static struct ath_buf * -ath_beacon_generate(struct ath_softc *sc, struct ieee80211vap *vap) -{ - struct ath_vap *avp = ATH_VAP(vap); - struct ath_txq *cabq = sc->sc_cabq; - struct ath_buf *bf; - struct mbuf *m; - int nmcastq, error; - - KASSERT(vap->iv_state >= IEEE80211_S_RUN, - ("not running, state %d", vap->iv_state)); - KASSERT(avp->av_bcbuf != NULL, ("no beacon buffer")); - - /* - * Update dynamic beacon contents. If this returns - * non-zero then we need to remap the memory because - * the beacon frame changed size (probably because - * of the TIM bitmap). - */ - bf = avp->av_bcbuf; - m = bf->bf_m; - nmcastq = avp->av_mcastq.axq_depth; - if (ieee80211_beacon_update(bf->bf_node, &avp->av_boff, m, nmcastq)) { - /* XXX too conservative? */ - bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); - error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m, - bf->bf_segs, &bf->bf_nseg, - BUS_DMA_NOWAIT); - if (error != 0) { - if_printf(vap->iv_ifp, - "%s: bus_dmamap_load_mbuf_sg failed, error %u\n", - __func__, error); - return NULL; - } - } - if ((avp->av_boff.bo_tim[4] & 1) && cabq->axq_depth) { - DPRINTF(sc, ATH_DEBUG_BEACON, - "%s: cabq did not drain, mcastq %u cabq %u\n", - __func__, nmcastq, cabq->axq_depth); - sc->sc_stats.ast_cabq_busy++; - if (sc->sc_nvaps > 1 && sc->sc_stagbeacons) { - /* - * CABQ traffic from a previous vap is still pending. - * We must drain the q before this beacon frame goes - * out as otherwise this vap's stations will get cab - * frames from a different vap. - * XXX could be slow causing us to miss DBA - */ - ath_tx_draintxq(sc, cabq); - } - } - ath_beacon_setup(sc, bf); - bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE); - - /* - * Enable the CAB queue before the beacon queue to - * insure cab frames are triggered by this beacon. - */ - if (avp->av_boff.bo_tim[4] & 1) { - struct ath_hal *ah = sc->sc_ah; - - /* NB: only at DTIM */ - ATH_TXQ_LOCK(cabq); - ATH_TXQ_LOCK(&avp->av_mcastq); - if (nmcastq) { - struct ath_buf *bfm; - - /* - * Move frames from the s/w mcast q to the h/w cab q. - * XXX MORE_DATA bit - */ - bfm = STAILQ_FIRST(&avp->av_mcastq.axq_q); - if (cabq->axq_link != NULL) { - *cabq->axq_link = bfm->bf_daddr; - } else - ath_hal_puttxbuf(ah, cabq->axq_qnum, - bfm->bf_daddr); - ath_txqmove(cabq, &avp->av_mcastq); - - sc->sc_stats.ast_cabq_xmit += nmcastq; - } - /* NB: gated by beacon so safe to start here */ - ath_hal_txstart(ah, cabq->axq_qnum); - ATH_TXQ_UNLOCK(cabq); - ATH_TXQ_UNLOCK(&avp->av_mcastq); - } - return bf; -} - -static void -ath_beacon_start_adhoc(struct ath_softc *sc, struct ieee80211vap *vap) -{ - struct ath_vap *avp = ATH_VAP(vap); - struct ath_hal *ah = sc->sc_ah; - struct ath_buf *bf; - struct mbuf *m; - int error; - - KASSERT(avp->av_bcbuf != NULL, ("no beacon buffer")); - - /* - * Update dynamic beacon contents. If this returns - * non-zero then we need to remap the memory because - * the beacon frame changed size (probably because - * of the TIM bitmap). - */ - bf = avp->av_bcbuf; - m = bf->bf_m; - if (ieee80211_beacon_update(bf->bf_node, &avp->av_boff, m, 0)) { - /* XXX too conservative? */ - bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); - error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m, - bf->bf_segs, &bf->bf_nseg, - BUS_DMA_NOWAIT); - if (error != 0) { - if_printf(vap->iv_ifp, - "%s: bus_dmamap_load_mbuf_sg failed, error %u\n", - __func__, error); - return; - } - } - ath_beacon_setup(sc, bf); - bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE); - - /* NB: caller is known to have already stopped tx dma */ - ath_hal_puttxbuf(ah, sc->sc_bhalq, bf->bf_daddr); - ath_hal_txstart(ah, sc->sc_bhalq); +#if 0 + device_printf(sc->sc_dev, "%s: resetting\n", __func__); +#endif + ath_reset(sc, ATH_RESET_NOLOSS); } /* @@ -2680,437 +3683,24 @@ static void ath_bstuck_proc(void *arg, int pending) { struct ath_softc *sc = arg; - struct ifnet *ifp = sc->sc_ifp; + uint32_t hangs = 0; - if_printf(ifp, "stuck beacon; resetting (bmiss count %u)\n", - sc->sc_bmisscount); - sc->sc_stats.ast_bstuck++; - ath_reset(ifp); -} + if (ath_hal_gethangstate(sc->sc_ah, 0xff, &hangs) && hangs != 0) + device_printf(sc->sc_dev, "bb hang detected (0x%x)\n", hangs); -/* - * Reclaim beacon resources and return buffer to the pool. - */ -static void -ath_beacon_return(struct ath_softc *sc, struct ath_buf *bf) -{ - - if (bf->bf_m != NULL) { - bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); - m_freem(bf->bf_m); - bf->bf_m = NULL; - } - if (bf->bf_node != NULL) { - ieee80211_free_node(bf->bf_node); - bf->bf_node = NULL; - } - STAILQ_INSERT_TAIL(&sc->sc_bbuf, bf, bf_list); -} - -/* - * Reclaim beacon resources. - */ -static void -ath_beacon_free(struct ath_softc *sc) -{ - struct ath_buf *bf; - - STAILQ_FOREACH(bf, &sc->sc_bbuf, bf_list) { - if (bf->bf_m != NULL) { - bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); - m_freem(bf->bf_m); - bf->bf_m = NULL; - } - if (bf->bf_node != NULL) { - ieee80211_free_node(bf->bf_node); - bf->bf_node = NULL; - } - } -} - -/* - * Configure the beacon and sleep timers. - * - * When operating as an AP this resets the TSF and sets - * up the hardware to notify us when we need to issue beacons. - * - * When operating in station mode this sets up the beacon - * timers according to the timestamp of the last received - * beacon and the current TSF, configures PCF and DTIM - * handling, programs the sleep registers so the hardware - * will wakeup in time to receive beacons, and configures - * the beacon miss handling so we'll receive a BMISS - * interrupt when we stop seeing beacons from the AP - * we've associated with. - */ -static void -ath_beacon_config(struct ath_softc *sc, struct ieee80211vap *vap) -{ -#define TSF_TO_TU(_h,_l) \ - ((((u_int32_t)(_h)) << 22) | (((u_int32_t)(_l)) >> 10)) -#define FUDGE 2 - struct ath_hal *ah = sc->sc_ah; - struct ieee80211com *ic = sc->sc_ifp->if_l2com; - struct ieee80211_node *ni; - u_int32_t nexttbtt, intval, tsftu; - u_int64_t tsf; - - if (vap == NULL) - vap = TAILQ_FIRST(&ic->ic_vaps); /* XXX */ - ni = vap->iv_bss; - - /* extract tstamp from last beacon and convert to TU */ - nexttbtt = TSF_TO_TU(LE_READ_4(ni->ni_tstamp.data + 4), - LE_READ_4(ni->ni_tstamp.data)); - if (ic->ic_opmode == IEEE80211_M_HOSTAP || - ic->ic_opmode == IEEE80211_M_MBSS) { - /* - * For multi-bss ap/mesh support beacons are either staggered - * evenly over N slots or burst together. For the former - * arrange for the SWBA to be delivered for each slot. - * Slots that are not occupied will generate nothing. - */ - /* NB: the beacon interval is kept internally in TU's */ - intval = ni->ni_intval & HAL_BEACON_PERIOD; - if (sc->sc_stagbeacons) - intval /= ATH_BCBUF; - } else { - /* NB: the beacon interval is kept internally in TU's */ - intval = ni->ni_intval & HAL_BEACON_PERIOD; - } - if (nexttbtt == 0) /* e.g. for ap mode */ - nexttbtt = intval; - else if (intval) /* NB: can be 0 for monitor mode */ - nexttbtt = roundup(nexttbtt, intval); - DPRINTF(sc, ATH_DEBUG_BEACON, "%s: nexttbtt %u intval %u (%u)\n", - __func__, nexttbtt, intval, ni->ni_intval); - if (ic->ic_opmode == IEEE80211_M_STA && !sc->sc_swbmiss) { - HAL_BEACON_STATE bs; - int dtimperiod, dtimcount; - int cfpperiod, cfpcount; - - /* - * Setup dtim and cfp parameters according to - * last beacon we received (which may be none). - */ - dtimperiod = ni->ni_dtim_period; - if (dtimperiod <= 0) /* NB: 0 if not known */ - dtimperiod = 1; - dtimcount = ni->ni_dtim_count; - if (dtimcount >= dtimperiod) /* NB: sanity check */ - dtimcount = 0; /* XXX? */ - cfpperiod = 1; /* NB: no PCF support yet */ - cfpcount = 0; - /* - * Pull nexttbtt forward to reflect the current - * TSF and calculate dtim+cfp state for the result. - */ - tsf = ath_hal_gettsf64(ah); - tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; - do { - nexttbtt += intval; - if (--dtimcount < 0) { - dtimcount = dtimperiod - 1; - if (--cfpcount < 0) - cfpcount = cfpperiod - 1; - } - } while (nexttbtt < tsftu); - memset(&bs, 0, sizeof(bs)); - bs.bs_intval = intval; - bs.bs_nexttbtt = nexttbtt; - bs.bs_dtimperiod = dtimperiod*intval; - bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval; - bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod; - bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod; - bs.bs_cfpmaxduration = 0; -#if 0 - /* - * The 802.11 layer records the offset to the DTIM - * bitmap while receiving beacons; use it here to - * enable h/w detection of our AID being marked in - * the bitmap vector (to indicate frames for us are - * pending at the AP). - * XXX do DTIM handling in s/w to WAR old h/w bugs - * XXX enable based on h/w rev for newer chips - */ - bs.bs_timoffset = ni->ni_timoff; +#ifdef ATH_DEBUG_ALQ + if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_STUCK_BEACON)) + if_ath_alq_post(&sc->sc_alq, ATH_ALQ_STUCK_BEACON, 0, NULL); #endif - /* - * Calculate the number of consecutive beacons to miss - * before taking a BMISS interrupt. - * Note that we clamp the result to at most 10 beacons. - */ - bs.bs_bmissthreshold = vap->iv_bmissthreshold; - if (bs.bs_bmissthreshold > 10) - bs.bs_bmissthreshold = 10; - else if (bs.bs_bmissthreshold <= 0) - bs.bs_bmissthreshold = 1; - - /* - * Calculate sleep duration. The configuration is - * given in ms. We insure a multiple of the beacon - * period is used. Also, if the sleep duration is - * greater than the DTIM period then it makes senses - * to make it a multiple of that. - * - * XXX fixed at 100ms - */ - bs.bs_sleepduration = - roundup(IEEE80211_MS_TO_TU(100), bs.bs_intval); - if (bs.bs_sleepduration > bs.bs_dtimperiod) - bs.bs_sleepduration = roundup(bs.bs_sleepduration, bs.bs_dtimperiod); - - DPRINTF(sc, ATH_DEBUG_BEACON, - "%s: tsf %ju tsf:tu %u intval %u nexttbtt %u dtim %u nextdtim %u bmiss %u sleep %u cfp:period %u maxdur %u next %u timoffset %u\n" - , __func__ - , tsf, tsftu - , bs.bs_intval - , bs.bs_nexttbtt - , bs.bs_dtimperiod - , bs.bs_nextdtim - , bs.bs_bmissthreshold - , bs.bs_sleepduration - , bs.bs_cfpperiod - , bs.bs_cfpmaxduration - , bs.bs_cfpnext - , bs.bs_timoffset - ); - ath_hal_intrset(ah, 0); - ath_hal_beacontimers(ah, &bs); - sc->sc_imask |= HAL_INT_BMISS; - ath_hal_intrset(ah, sc->sc_imask); - } else { - ath_hal_intrset(ah, 0); - if (nexttbtt == intval) - intval |= HAL_BEACON_RESET_TSF; - if (ic->ic_opmode == IEEE80211_M_IBSS) { - /* - * In IBSS mode enable the beacon timers but only - * enable SWBA interrupts if we need to manually - * prepare beacon frames. Otherwise we use a - * self-linked tx descriptor and let the hardware - * deal with things. - */ - intval |= HAL_BEACON_ENA; - if (!sc->sc_hasveol) - sc->sc_imask |= HAL_INT_SWBA; - if ((intval & HAL_BEACON_RESET_TSF) == 0) { - /* - * Pull nexttbtt forward to reflect - * the current TSF. - */ - tsf = ath_hal_gettsf64(ah); - tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; - do { - nexttbtt += intval; - } while (nexttbtt < tsftu); - } - ath_beaconq_config(sc); - } else if (ic->ic_opmode == IEEE80211_M_HOSTAP || - ic->ic_opmode == IEEE80211_M_MBSS) { - /* - * In AP/mesh mode we enable the beacon timers - * and SWBA interrupts to prepare beacon frames. - */ - intval |= HAL_BEACON_ENA; - sc->sc_imask |= HAL_INT_SWBA; /* beacon prepare */ - ath_beaconq_config(sc); - } - ath_hal_beaconinit(ah, nexttbtt, intval); - sc->sc_bmisscount = 0; - ath_hal_intrset(ah, sc->sc_imask); - /* - * When using a self-linked beacon descriptor in - * ibss mode load it once here. - */ - if (ic->ic_opmode == IEEE80211_M_IBSS && sc->sc_hasveol) - ath_beacon_start_adhoc(sc, vap); - } - sc->sc_syncbeacon = 0; -#undef FUDGE -#undef TSF_TO_TU -} - -static void -ath_load_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) -{ - bus_addr_t *paddr = (bus_addr_t*) arg; - KASSERT(error == 0, ("error %u on bus_dma callback", error)); - *paddr = segs->ds_addr; -} - -static int -ath_descdma_setup(struct ath_softc *sc, - struct ath_descdma *dd, ath_bufhead *head, - const char *name, int nbuf, int ndesc) -{ -#define DS2PHYS(_dd, _ds) \ - ((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc)) -#define ATH_DESC_4KB_BOUND_CHECK(_daddr, _len) \ - ((((u_int32_t)(_daddr) & 0xFFF) > (0x1000 - (_len))) ? 1 : 0) - struct ifnet *ifp = sc->sc_ifp; - uint8_t *ds; - struct ath_buf *bf; - int i, bsize, error; - int desc_len; - - desc_len = sizeof(struct ath_desc); - - DPRINTF(sc, ATH_DEBUG_RESET, "%s: %s DMA: %u buffers %u desc/buf\n", - __func__, name, nbuf, ndesc); - - dd->dd_name = name; - dd->dd_desc_len = desc_len * nbuf * ndesc; + device_printf(sc->sc_dev, "stuck beacon; resetting (bmiss count %u)\n", + sc->sc_bmisscount); + sc->sc_stats.ast_bstuck++; /* - * Merlin work-around: - * Descriptors that cross the 4KB boundary can't be used. - * Assume one skipped descriptor per 4KB page. + * This assumes that there's no simultaneous channel mode change + * occurring. */ - if (! ath_hal_split4ktrans(sc->sc_ah)) { - int numdescpage = 4096 / (desc_len * ndesc); - dd->dd_desc_len = (nbuf / numdescpage + 1) * 4096; - } - - /* - * Setup DMA descriptor area. - */ - error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), /* parent */ - PAGE_SIZE, 0, /* alignment, bounds */ - BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ - BUS_SPACE_MAXADDR, /* highaddr */ - NULL, NULL, /* filter, filterarg */ - dd->dd_desc_len, /* maxsize */ - 1, /* nsegments */ - dd->dd_desc_len, /* maxsegsize */ - BUS_DMA_ALLOCNOW, /* flags */ - NULL, /* lockfunc */ - NULL, /* lockarg */ - &dd->dd_dmat); - if (error != 0) { - if_printf(ifp, "cannot allocate %s DMA tag\n", dd->dd_name); - return error; - } - - /* allocate descriptors */ - error = bus_dmamap_create(dd->dd_dmat, BUS_DMA_NOWAIT, &dd->dd_dmamap); - if (error != 0) { - if_printf(ifp, "unable to create dmamap for %s descriptors, " - "error %u\n", dd->dd_name, error); - goto fail0; - } - - error = bus_dmamem_alloc(dd->dd_dmat, (void**) &dd->dd_desc, - BUS_DMA_NOWAIT | BUS_DMA_COHERENT, - &dd->dd_dmamap); - if (error != 0) { - if_printf(ifp, "unable to alloc memory for %u %s descriptors, " - "error %u\n", nbuf * ndesc, dd->dd_name, error); - goto fail1; - } - - error = bus_dmamap_load(dd->dd_dmat, dd->dd_dmamap, - dd->dd_desc, dd->dd_desc_len, - ath_load_cb, &dd->dd_desc_paddr, - BUS_DMA_NOWAIT); - if (error != 0) { - if_printf(ifp, "unable to map %s descriptors, error %u\n", - dd->dd_name, error); - goto fail2; - } - - ds = (uint8_t *) dd->dd_desc; - DPRINTF(sc, ATH_DEBUG_RESET, "%s: %s DMA map: %p (%lu) -> %p (%lu)\n", - __func__, dd->dd_name, ds, (u_long) dd->dd_desc_len, - (caddr_t) dd->dd_desc_paddr, /*XXX*/ (u_long) dd->dd_desc_len); - - /* allocate rx buffers */ - bsize = sizeof(struct ath_buf) * nbuf; - bf = malloc(bsize, M_ATHDEV, M_NOWAIT | M_ZERO); - if (bf == NULL) { - if_printf(ifp, "malloc of %s buffers failed, size %u\n", - dd->dd_name, bsize); - goto fail3; - } - dd->dd_bufptr = bf; - - STAILQ_INIT(head); - for (i = 0; i < nbuf; i++, bf++, ds += (ndesc * desc_len)) { - bf->bf_desc = (struct ath_desc *) ds; - bf->bf_daddr = DS2PHYS(dd, ds); - if (! ath_hal_split4ktrans(sc->sc_ah)) { - /* - * Merlin WAR: Skip descriptor addresses which - * cause 4KB boundary crossing along any point - * in the descriptor. - */ - if (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr, - desc_len * ndesc)) { - /* Start at the next page */ - ds += 0x1000 - (bf->bf_daddr & 0xFFF); - bf->bf_desc = (struct ath_desc *) ds; - bf->bf_daddr = DS2PHYS(dd, ds); - } - } - error = bus_dmamap_create(sc->sc_dmat, BUS_DMA_NOWAIT, - &bf->bf_dmamap); - if (error != 0) { - if_printf(ifp, "unable to create dmamap for %s " - "buffer %u, error %u\n", dd->dd_name, i, error); - ath_descdma_cleanup(sc, dd, head); - return error; - } - STAILQ_INSERT_TAIL(head, bf, bf_list); - } - return 0; -fail3: - bus_dmamap_unload(dd->dd_dmat, dd->dd_dmamap); -fail2: - bus_dmamem_free(dd->dd_dmat, dd->dd_desc, dd->dd_dmamap); -fail1: - bus_dmamap_destroy(dd->dd_dmat, dd->dd_dmamap); -fail0: - bus_dma_tag_destroy(dd->dd_dmat); - memset(dd, 0, sizeof(*dd)); - return error; -#undef DS2PHYS -#undef ATH_DESC_4KB_BOUND_CHECK -} - -static void -ath_descdma_cleanup(struct ath_softc *sc, - struct ath_descdma *dd, ath_bufhead *head) -{ - struct ath_buf *bf; - struct ieee80211_node *ni; - - bus_dmamap_unload(dd->dd_dmat, dd->dd_dmamap); - bus_dmamem_free(dd->dd_dmat, dd->dd_desc, dd->dd_dmamap); - bus_dmamap_destroy(dd->dd_dmat, dd->dd_dmamap); - bus_dma_tag_destroy(dd->dd_dmat); - - STAILQ_FOREACH(bf, head, bf_list) { - if (bf->bf_m) { - m_freem(bf->bf_m); - bf->bf_m = NULL; - } - if (bf->bf_dmamap != NULL) { - bus_dmamap_destroy(sc->sc_dmat, bf->bf_dmamap); - bf->bf_dmamap = NULL; - } - ni = bf->bf_node; - bf->bf_node = NULL; - if (ni != NULL) { - /* - * Reclaim node reference. - */ - ieee80211_free_node(ni); - } - } - - STAILQ_INIT(head); - free(dd->dd_bufptr, M_ATHDEV); - memset(dd, 0, sizeof(*dd)); + ath_reset(sc, ATH_RESET_NOLOSS); } static int @@ -3118,23 +3708,32 @@ ath_desc_alloc(struct ath_softc *sc) { int error; - error = ath_descdma_setup(sc, &sc->sc_rxdma, &sc->sc_rxbuf, - "rx", ath_rxbuf, 1); - if (error != 0) - return error; - error = ath_descdma_setup(sc, &sc->sc_txdma, &sc->sc_txbuf, - "tx", ath_txbuf, ATH_TXDESC); + "tx", sc->sc_tx_desclen, ath_txbuf, ATH_MAX_SCATTER); if (error != 0) { - ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf); + return error; + } + sc->sc_txbuf_cnt = ath_txbuf; + + error = ath_descdma_setup(sc, &sc->sc_txdma_mgmt, &sc->sc_txbuf_mgmt, + "tx_mgmt", sc->sc_tx_desclen, ath_txbuf_mgmt, + ATH_TXDESC); + if (error != 0) { + ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf); return error; } + /* + * XXX mark txbuf_mgmt frames with ATH_BUF_MGMT, so the + * flag doesn't have to be set in ath_getbuf_locked(). + */ + error = ath_descdma_setup(sc, &sc->sc_bdma, &sc->sc_bbuf, - "beacon", ATH_BCBUF, 1); + "beacon", sc->sc_tx_desclen, ATH_BCBUF, 1); if (error != 0) { ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf); - ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf); + ath_descdma_cleanup(sc, &sc->sc_txdma_mgmt, + &sc->sc_txbuf_mgmt); return error; } return 0; @@ -3148,15 +3747,16 @@ ath_desc_free(struct ath_softc *sc) ath_descdma_cleanup(sc, &sc->sc_bdma, &sc->sc_bbuf); if (sc->sc_txdma.dd_desc_len != 0) ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf); - if (sc->sc_rxdma.dd_desc_len != 0) - ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf); + if (sc->sc_txdma_mgmt.dd_desc_len != 0) + ath_descdma_cleanup(sc, &sc->sc_txdma_mgmt, + &sc->sc_txbuf_mgmt); } static struct ieee80211_node * ath_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) { struct ieee80211com *ic = vap->iv_ic; - struct ath_softc *sc = ic->ic_ifp->if_softc; + struct ath_softc *sc = ic->ic_softc; const size_t space = sizeof(struct ath_node) + sc->sc_rc->arc_space; struct ath_node *an; @@ -3167,19 +3767,42 @@ ath_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) } ath_rate_node_init(sc, an); - DPRINTF(sc, ATH_DEBUG_NODE, "%s: an %p\n", __func__, an); + /* Setup the mutex - there's no associd yet so set the name to NULL */ + snprintf(an->an_name, sizeof(an->an_name), "%s: node %p", + device_get_nameunit(sc->sc_dev), an); + mtx_init(&an->an_mtx, an->an_name, NULL, MTX_DEF); + + /* XXX setup ath_tid */ + ath_tx_tid_init(sc, an); + + DPRINTF(sc, ATH_DEBUG_NODE, "%s: %6D: an %p\n", __func__, mac, ":", an); return &an->an_node; } +static void +ath_node_cleanup(struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ath_softc *sc = ic->ic_softc; + + DPRINTF(sc, ATH_DEBUG_NODE, "%s: %6D: an %p\n", __func__, + ni->ni_macaddr, ":", ATH_NODE(ni)); + + /* Cleanup ath_tid, free unused bufs, unlink bufs in TXQ */ + ath_tx_node_flush(sc, ATH_NODE(ni)); + ath_rate_node_cleanup(sc, ATH_NODE(ni)); + sc->sc_node_cleanup(ni); +} + static void ath_node_free(struct ieee80211_node *ni) { struct ieee80211com *ic = ni->ni_ic; - struct ath_softc *sc = ic->ic_ifp->if_softc; + struct ath_softc *sc = ic->ic_softc; - DPRINTF(sc, ATH_DEBUG_NODE, "%s: ni %p\n", __func__, ni); - - ath_rate_node_cleanup(sc, ATH_NODE(ni)); + DPRINTF(sc, ATH_DEBUG_NODE, "%s: %6D: an %p\n", __func__, + ni->ni_macaddr, ":", ATH_NODE(ni)); + mtx_destroy(&ATH_NODE(ni)->an_mtx); sc->sc_node_free(ni); } @@ -3187,7 +3810,7 @@ static void ath_node_getsignal(const struct ieee80211_node *ni, int8_t *rssi, int8_t *noise) { struct ieee80211com *ic = ni->ni_ic; - struct ath_softc *sc = ic->ic_ifp->if_softc; + struct ath_softc *sc = ic->ic_softc; struct ath_hal *ah = sc->sc_ah; *rssi = ic->ic_node_getrssi(ni); @@ -3197,196 +3820,10 @@ ath_node_getsignal(const struct ieee80211_node *ni, int8_t *rssi, int8_t *noise) *noise = -95; /* nominally correct */ } -static int -ath_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf) -{ - struct ath_hal *ah = sc->sc_ah; - int error; - struct mbuf *m; - struct ath_desc *ds; - - m = bf->bf_m; - if (m == NULL) { - /* - * NB: by assigning a page to the rx dma buffer we - * implicitly satisfy the Atheros requirement that - * this buffer be cache-line-aligned and sized to be - * multiple of the cache line size. Not doing this - * causes weird stuff to happen (for the 5210 at least). - */ - m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); - if (m == NULL) { - DPRINTF(sc, ATH_DEBUG_ANY, - "%s: no mbuf/cluster\n", __func__); - sc->sc_stats.ast_rx_nombuf++; - return ENOMEM; - } - m->m_pkthdr.len = m->m_len = m->m_ext.ext_size; - - error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, - bf->bf_dmamap, m, - bf->bf_segs, &bf->bf_nseg, - BUS_DMA_NOWAIT); - if (error != 0) { - DPRINTF(sc, ATH_DEBUG_ANY, - "%s: bus_dmamap_load_mbuf_sg failed; error %d\n", - __func__, error); - sc->sc_stats.ast_rx_busdma++; - m_freem(m); - return error; - } - KASSERT(bf->bf_nseg == 1, - ("multi-segment packet; nseg %u", bf->bf_nseg)); - bf->bf_m = m; - } - bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREREAD); - - /* - * Setup descriptors. For receive we always terminate - * the descriptor list with a self-linked entry so we'll - * not get overrun under high load (as can happen with a - * 5212 when ANI processing enables PHY error frames). - * - * To insure the last descriptor is self-linked we create - * each descriptor as self-linked and add it to the end. As - * each additional descriptor is added the previous self-linked - * entry is ``fixed'' naturally. This should be safe even - * if DMA is happening. When processing RX interrupts we - * never remove/process the last, self-linked, entry on the - * descriptor list. This insures the hardware always has - * someplace to write a new frame. - */ - /* - * 11N: we can no longer afford to self link the last descriptor. - * MAC acknowledges BA status as long as it copies frames to host - * buffer (or rx fifo). This can incorrectly acknowledge packets - * to a sender if last desc is self-linked. - */ - ds = bf->bf_desc; - if (sc->sc_rxslink) - ds->ds_link = bf->bf_daddr; /* link to self */ - else - ds->ds_link = 0; /* terminate the list */ - ds->ds_data = bf->bf_segs[0].ds_addr; - ath_hal_setuprxdesc(ah, ds - , m->m_len /* buffer size */ - , 0 - ); - - if (sc->sc_rxlink != NULL) - *sc->sc_rxlink = bf->bf_daddr; - sc->sc_rxlink = &ds->ds_link; - return 0; -} - -/* - * Extend 15-bit time stamp from rx descriptor to - * a full 64-bit TSF using the specified TSF. - */ -static __inline u_int64_t -ath_extend_tsf15(u_int32_t rstamp, u_int64_t tsf) -{ - if ((tsf & 0x7fff) < rstamp) - tsf -= 0x8000; - - return ((tsf &~ 0x7fff) | rstamp); -} - -/* - * Extend 32-bit time stamp from rx descriptor to - * a full 64-bit TSF using the specified TSF. - */ -static __inline u_int64_t -ath_extend_tsf32(u_int32_t rstamp, u_int64_t tsf) -{ - u_int32_t tsf_low = tsf & 0xffffffff; - u_int64_t tsf64 = (tsf & ~0xffffffffULL) | rstamp; - - if (rstamp > tsf_low && (rstamp - tsf_low > 0x10000000)) - tsf64 -= 0x100000000ULL; - - if (rstamp < tsf_low && (tsf_low - rstamp > 0x10000000)) - tsf64 += 0x100000000ULL; - - return tsf64; -} - -/* - * Extend the TSF from the RX descriptor to a full 64 bit TSF. - * Earlier hardware versions only wrote the low 15 bits of the - * TSF into the RX descriptor; later versions (AR5416 and up) - * include the 32 bit TSF value. - */ -static __inline u_int64_t -ath_extend_tsf(struct ath_softc *sc, u_int32_t rstamp, u_int64_t tsf) -{ - if (sc->sc_rxtsf32) - return ath_extend_tsf32(rstamp, tsf); - else - return ath_extend_tsf15(rstamp, tsf); -} - -/* - * Intercept management frames to collect beacon rssi data - * and to do ibss merges. - */ -static void -ath_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, - int subtype, int rssi, int nf) -{ - struct ieee80211vap *vap = ni->ni_vap; - struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; - - /* - * Call up first so subsequent work can use information - * potentially stored in the node (e.g. for ibss merge). - */ - ATH_VAP(vap)->av_recv_mgmt(ni, m, subtype, rssi, nf); - switch (subtype) { - case IEEE80211_FC0_SUBTYPE_BEACON: - /* update rssi statistics for use by the hal */ - ATH_RSSI_LPF(sc->sc_halstats.ns_avgbrssi, rssi); - if (sc->sc_syncbeacon && - ni == vap->iv_bss && vap->iv_state == IEEE80211_S_RUN) { - /* - * Resync beacon timers using the tsf of the beacon - * frame we just received. - */ - ath_beacon_config(sc, vap); - } - /* fall thru... */ - case IEEE80211_FC0_SUBTYPE_PROBE_RESP: - if (vap->iv_opmode == IEEE80211_M_IBSS && - vap->iv_state == IEEE80211_S_RUN) { - uint32_t rstamp = sc->sc_lastrs->rs_tstamp; - uint64_t tsf = ath_extend_tsf(sc, rstamp, - ath_hal_gettsf64(sc->sc_ah)); - /* - * Handle ibss merge as needed; check the tsf on the - * frame before attempting the merge. The 802.11 spec - * says the station should change it's bssid to match - * the oldest station with the same ssid, where oldest - * is determined by the tsf. Note that hardware - * reconfiguration happens through callback to - * ath_newstate as the state machine will go from - * RUN -> RUN when this happens. - */ - if (le64toh(ni->ni_tstamp.tsf) >= tsf) { - DPRINTF(sc, ATH_DEBUG_STATE, - "ibss merge, rstamp %u tsf %ju " - "tstamp %ju\n", rstamp, (uintmax_t)tsf, - (uintmax_t)ni->ni_tstamp.tsf); - (void) ieee80211_ibss_merge(ni); - } - } - break; - } -} - /* * Set the default antenna. */ -static void +void ath_setdefantenna(struct ath_softc *sc, u_int antenna) { struct ath_hal *ah = sc->sc_ah; @@ -3399,459 +3836,19 @@ ath_setdefantenna(struct ath_softc *sc, u_int antenna) sc->sc_rxotherant = 0; } -static void -ath_rx_tap(struct ifnet *ifp, struct mbuf *m, - const struct ath_rx_status *rs, u_int64_t tsf, int16_t nf) -{ -#define CHAN_HT20 htole32(IEEE80211_CHAN_HT20) -#define CHAN_HT40U htole32(IEEE80211_CHAN_HT40U) -#define CHAN_HT40D htole32(IEEE80211_CHAN_HT40D) -#define CHAN_HT (CHAN_HT20|CHAN_HT40U|CHAN_HT40D) - struct ath_softc *sc = ifp->if_softc; - const HAL_RATE_TABLE *rt; - uint8_t rix; - - rt = sc->sc_currates; - KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode)); - rix = rt->rateCodeToIndex[rs->rs_rate]; - sc->sc_rx_th.wr_rate = sc->sc_hwmap[rix].ieeerate; - sc->sc_rx_th.wr_flags = sc->sc_hwmap[rix].rxflags; -#ifdef AH_SUPPORT_AR5416 - sc->sc_rx_th.wr_chan_flags &= ~CHAN_HT; - if (sc->sc_rx_th.wr_rate & IEEE80211_RATE_MCS) { /* HT rate */ - struct ieee80211com *ic = ifp->if_l2com; - - if ((rs->rs_flags & HAL_RX_2040) == 0) - sc->sc_rx_th.wr_chan_flags |= CHAN_HT20; - else if (IEEE80211_IS_CHAN_HT40U(ic->ic_curchan)) - sc->sc_rx_th.wr_chan_flags |= CHAN_HT40U; - else - sc->sc_rx_th.wr_chan_flags |= CHAN_HT40D; - if ((rs->rs_flags & HAL_RX_GI) == 0) - sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_SHORTGI; - } -#endif - sc->sc_rx_th.wr_tsf = htole64(ath_extend_tsf(sc, rs->rs_tstamp, tsf)); - if (rs->rs_status & HAL_RXERR_CRC) - sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_BADFCS; - /* XXX propagate other error flags from descriptor */ - sc->sc_rx_th.wr_antnoise = nf; - sc->sc_rx_th.wr_antsignal = nf + rs->rs_rssi; - sc->sc_rx_th.wr_antenna = rs->rs_antenna; -#undef CHAN_HT -#undef CHAN_HT20 -#undef CHAN_HT40U -#undef CHAN_HT40D -} - -static void -ath_handle_micerror(struct ieee80211com *ic, - struct ieee80211_frame *wh, int keyix) -{ - struct ieee80211_node *ni; - - /* XXX recheck MIC to deal w/ chips that lie */ - /* XXX discard MIC errors on !data frames */ - ni = ieee80211_find_rxnode(ic, (const struct ieee80211_frame_min *) wh); - if (ni != NULL) { - ieee80211_notify_michael_failure(ni->ni_vap, wh, keyix); - ieee80211_free_node(ni); - } -} - -static void -ath_rx_proc(void *arg, int npending) -{ -#define PA2DESC(_sc, _pa) \ - ((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \ - ((_pa) - (_sc)->sc_rxdma.dd_desc_paddr))) - struct ath_softc *sc = arg; - struct ath_buf *bf; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct ath_hal *ah = sc->sc_ah; - struct ath_desc *ds; - struct ath_rx_status *rs; - struct mbuf *m; - struct ieee80211_node *ni; - int len, type, ngood; - HAL_STATUS status; - int16_t nf; - u_int64_t tsf; - - DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s: pending %u\n", __func__, npending); - ngood = 0; - nf = ath_hal_getchannoise(ah, sc->sc_curchan); - sc->sc_stats.ast_rx_noise = nf; - tsf = ath_hal_gettsf64(ah); - do { - bf = STAILQ_FIRST(&sc->sc_rxbuf); - if (sc->sc_rxslink && bf == NULL) { /* NB: shouldn't happen */ - if_printf(ifp, "%s: no buffer!\n", __func__); - break; - } else if (bf == NULL) { - /* - * End of List: - * this can happen for non-self-linked RX chains - */ - sc->sc_stats.ast_rx_hitqueueend++; - break; - } - m = bf->bf_m; - if (m == NULL) { /* NB: shouldn't happen */ - /* - * If mbuf allocation failed previously there - * will be no mbuf; try again to re-populate it. - */ - /* XXX make debug msg */ - if_printf(ifp, "%s: no mbuf!\n", __func__); - STAILQ_REMOVE_HEAD(&sc->sc_rxbuf, bf_list); - goto rx_next; - } - ds = bf->bf_desc; - if (ds->ds_link == bf->bf_daddr) { - /* NB: never process the self-linked entry at the end */ - sc->sc_stats.ast_rx_hitqueueend++; - break; - } - /* XXX sync descriptor memory */ - /* - * Must provide the virtual address of the current - * descriptor, the physical address, and the virtual - * address of the next descriptor in the h/w chain. - * This allows the HAL to look ahead to see if the - * hardware is done with a descriptor by checking the - * done bit in the following descriptor and the address - * of the current descriptor the DMA engine is working - * on. All this is necessary because of our use of - * a self-linked list to avoid rx overruns. - */ - rs = &bf->bf_status.ds_rxstat; - status = ath_hal_rxprocdesc(ah, ds, - bf->bf_daddr, PA2DESC(sc, ds->ds_link), rs); -#ifdef ATH_DEBUG - if (sc->sc_debug & ATH_DEBUG_RECV_DESC) - ath_printrxbuf(sc, bf, 0, status == HAL_OK); -#endif - if (status == HAL_EINPROGRESS) - break; - STAILQ_REMOVE_HEAD(&sc->sc_rxbuf, bf_list); - - /* These aren't specifically errors */ - if (rs->rs_flags & HAL_RX_GI) - sc->sc_stats.ast_rx_halfgi++; - if (rs->rs_flags & HAL_RX_2040) - sc->sc_stats.ast_rx_2040++; - if (rs->rs_flags & HAL_RX_DELIM_CRC_PRE) - sc->sc_stats.ast_rx_pre_crc_err++; - if (rs->rs_flags & HAL_RX_DELIM_CRC_POST) - sc->sc_stats.ast_rx_post_crc_err++; - if (rs->rs_flags & HAL_RX_DECRYPT_BUSY) - sc->sc_stats.ast_rx_decrypt_busy_err++; - if (rs->rs_flags & HAL_RX_HI_RX_CHAIN) - sc->sc_stats.ast_rx_hi_rx_chain++; - - if (rs->rs_status != 0) { - if (rs->rs_status & HAL_RXERR_CRC) - sc->sc_stats.ast_rx_crcerr++; - if (rs->rs_status & HAL_RXERR_FIFO) - sc->sc_stats.ast_rx_fifoerr++; - if (rs->rs_status & HAL_RXERR_PHY) { - sc->sc_stats.ast_rx_phyerr++; - /* Process DFS radar events */ - if ((rs->rs_phyerr == HAL_PHYERR_RADAR) || - (rs->rs_phyerr == HAL_PHYERR_FALSE_RADAR_EXT)) { - /* Since we're touching the frame data, sync it */ - bus_dmamap_sync(sc->sc_dmat, - bf->bf_dmamap, - BUS_DMASYNC_POSTREAD); - /* Now pass it to the radar processing code */ - ath_dfs_process_phy_err(sc, mtod(m, char *), tsf, rs); - } - - /* Be suitably paranoid about receiving phy errors out of the stats array bounds */ - if (rs->rs_phyerr < 64) - sc->sc_stats.ast_rx_phy[rs->rs_phyerr]++; - goto rx_error; /* NB: don't count in ierrors */ - } - if (rs->rs_status & HAL_RXERR_DECRYPT) { - /* - * Decrypt error. If the error occurred - * because there was no hardware key, then - * let the frame through so the upper layers - * can process it. This is necessary for 5210 - * parts which have no way to setup a ``clear'' - * key cache entry. - * - * XXX do key cache faulting - */ - if (rs->rs_keyix == HAL_RXKEYIX_INVALID) - goto rx_accept; - sc->sc_stats.ast_rx_badcrypt++; - } - if (rs->rs_status & HAL_RXERR_MIC) { - sc->sc_stats.ast_rx_badmic++; - /* - * Do minimal work required to hand off - * the 802.11 header for notification. - */ - /* XXX frag's and qos frames */ - len = rs->rs_datalen; - if (len >= sizeof (struct ieee80211_frame)) { - bus_dmamap_sync(sc->sc_dmat, - bf->bf_dmamap, - BUS_DMASYNC_POSTREAD); - ath_handle_micerror(ic, - mtod(m, struct ieee80211_frame *), - sc->sc_splitmic ? - rs->rs_keyix-32 : rs->rs_keyix); - } - } - ifp->if_ierrors++; -rx_error: - /* - * Cleanup any pending partial frame. - */ - if (sc->sc_rxpending != NULL) { - m_freem(sc->sc_rxpending); - sc->sc_rxpending = NULL; - } - /* - * When a tap is present pass error frames - * that have been requested. By default we - * pass decrypt+mic errors but others may be - * interesting (e.g. crc). - */ - if (ieee80211_radiotap_active(ic) && - (rs->rs_status & sc->sc_monpass)) { - bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, - BUS_DMASYNC_POSTREAD); - /* NB: bpf needs the mbuf length setup */ - len = rs->rs_datalen; - m->m_pkthdr.len = m->m_len = len; - ath_rx_tap(ifp, m, rs, tsf, nf); - ieee80211_radiotap_rx_all(ic, m); - } - /* XXX pass MIC errors up for s/w reclaculation */ - goto rx_next; - } -rx_accept: - /* - * Sync and unmap the frame. At this point we're - * committed to passing the mbuf somewhere so clear - * bf_m; this means a new mbuf must be allocated - * when the rx descriptor is setup again to receive - * another frame. - */ - bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, - BUS_DMASYNC_POSTREAD); - bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); - bf->bf_m = NULL; - - len = rs->rs_datalen; - m->m_len = len; - - if (rs->rs_more) { - /* - * Frame spans multiple descriptors; save - * it for the next completed descriptor, it - * will be used to construct a jumbogram. - */ - if (sc->sc_rxpending != NULL) { - /* NB: max frame size is currently 2 clusters */ - sc->sc_stats.ast_rx_toobig++; - m_freem(sc->sc_rxpending); - } - m->m_pkthdr.rcvif = ifp; - m->m_pkthdr.len = len; - sc->sc_rxpending = m; - goto rx_next; - } else if (sc->sc_rxpending != NULL) { - /* - * This is the second part of a jumbogram, - * chain it to the first mbuf, adjust the - * frame length, and clear the rxpending state. - */ - sc->sc_rxpending->m_next = m; - sc->sc_rxpending->m_pkthdr.len += len; - m = sc->sc_rxpending; - sc->sc_rxpending = NULL; - } else { - /* - * Normal single-descriptor receive; setup - * the rcvif and packet length. - */ - m->m_pkthdr.rcvif = ifp; - m->m_pkthdr.len = len; - } - - ifp->if_ipackets++; - sc->sc_stats.ast_ant_rx[rs->rs_antenna]++; - - /* - * Populate the rx status block. When there are bpf - * listeners we do the additional work to provide - * complete status. Otherwise we fill in only the - * material required by ieee80211_input. Note that - * noise setting is filled in above. - */ - if (ieee80211_radiotap_active(ic)) - ath_rx_tap(ifp, m, rs, tsf, nf); - - /* - * From this point on we assume the frame is at least - * as large as ieee80211_frame_min; verify that. - */ - if (len < IEEE80211_MIN_LEN) { - if (!ieee80211_radiotap_active(ic)) { - DPRINTF(sc, ATH_DEBUG_RECV, - "%s: short packet %d\n", __func__, len); - sc->sc_stats.ast_rx_tooshort++; - } else { - /* NB: in particular this captures ack's */ - ieee80211_radiotap_rx_all(ic, m); - } - m_freem(m); - goto rx_next; - } - - if (IFF_DUMPPKTS(sc, ATH_DEBUG_RECV)) { - const HAL_RATE_TABLE *rt = sc->sc_currates; - uint8_t rix = rt->rateCodeToIndex[rs->rs_rate]; - - ieee80211_dump_pkt(ic, mtod(m, caddr_t), len, - sc->sc_hwmap[rix].ieeerate, rs->rs_rssi); - } - - m_adj(m, -IEEE80211_CRC_LEN); - - /* - * Locate the node for sender, track state, and then - * pass the (referenced) node up to the 802.11 layer - * for its use. - */ - ni = ieee80211_find_rxnode_withkey(ic, - mtod(m, const struct ieee80211_frame_min *), - rs->rs_keyix == HAL_RXKEYIX_INVALID ? - IEEE80211_KEYIX_NONE : rs->rs_keyix); - sc->sc_lastrs = rs; - - if (rs->rs_isaggr) - sc->sc_stats.ast_rx_agg++; - - if (ni != NULL) { - /* - * Only punt packets for ampdu reorder processing for - * 11n nodes; net80211 enforces that M_AMPDU is only - * set for 11n nodes. - */ - if (ni->ni_flags & IEEE80211_NODE_HT) - m->m_flags |= M_AMPDU; - - /* - * Sending station is known, dispatch directly. - */ - type = ieee80211_input(ni, m, rs->rs_rssi, nf); - ieee80211_free_node(ni); - /* - * Arrange to update the last rx timestamp only for - * frames from our ap when operating in station mode. - * This assumes the rx key is always setup when - * associated. - */ - if (ic->ic_opmode == IEEE80211_M_STA && - rs->rs_keyix != HAL_RXKEYIX_INVALID) - ngood++; - } else { - type = ieee80211_input_all(ic, m, rs->rs_rssi, nf); - } - /* - * Track rx rssi and do any rx antenna management. - */ - ATH_RSSI_LPF(sc->sc_halstats.ns_avgrssi, rs->rs_rssi); - if (sc->sc_diversity) { - /* - * When using fast diversity, change the default rx - * antenna if diversity chooses the other antenna 3 - * times in a row. - */ - if (sc->sc_defant != rs->rs_antenna) { - if (++sc->sc_rxotherant >= 3) - ath_setdefantenna(sc, rs->rs_antenna); - } else - sc->sc_rxotherant = 0; - } - - /* Newer school diversity - kite specific for now */ - /* XXX perhaps migrate the normal diversity code to this? */ - if ((ah)->ah_rxAntCombDiversity) - (*(ah)->ah_rxAntCombDiversity)(ah, rs, ticks, hz); - - if (sc->sc_softled) { - /* - * Blink for any data frame. Otherwise do a - * heartbeat-style blink when idle. The latter - * is mainly for station mode where we depend on - * periodic beacon frames to trigger the poll event. - */ - if (type == IEEE80211_FC0_TYPE_DATA) { - const HAL_RATE_TABLE *rt = sc->sc_currates; - ath_led_event(sc, - rt->rateCodeToIndex[rs->rs_rate]); - } else if (ticks - sc->sc_ledevent >= sc->sc_ledidle) - ath_led_event(sc, 0); - } -rx_next: - STAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list); - } while (ath_rxbuf_init(sc, bf) == 0); - - /* rx signal state monitoring */ - ath_hal_rxmonitor(ah, &sc->sc_halstats, sc->sc_curchan); - if (ngood) - sc->sc_lastrx = tsf; - - /* Queue DFS tasklet if needed */ - if (ath_dfs_tasklet_needed(sc, sc->sc_curchan)) - taskqueue_enqueue(sc->sc_tq, &sc->sc_dfstask); - - /* - * Now that all the RX frames were handled that - * need to be handled, kick the PCU if there's - * been an RXEOL condition. - */ - if (sc->sc_kickpcu) { - sc->sc_kickpcu = 0; - ath_stoprecv(sc); - sc->sc_imask |= (HAL_INT_RXEOL | HAL_INT_RXORN); - if (ath_startrecv(sc) != 0) { - if_printf(ifp, - "%s: couldn't restart RX after RXEOL; resetting\n", - __func__); - ath_reset(ifp); - return; - } - ath_hal_intrset(ah, sc->sc_imask); - } - - if ((ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) { -#ifdef IEEE80211_SUPPORT_SUPERG - ieee80211_ff_age_all(ic, 100); -#endif - if (!IFQ_IS_EMPTY(&ifp->if_snd)) - ath_start(ifp); - } -#undef PA2DESC -} - static void ath_txq_init(struct ath_softc *sc, struct ath_txq *txq, int qnum) { txq->axq_qnum = qnum; txq->axq_ac = 0; txq->axq_depth = 0; + txq->axq_aggr_depth = 0; txq->axq_intrcnt = 0; txq->axq_link = NULL; - STAILQ_INIT(&txq->axq_q); + txq->axq_softc = sc; + TAILQ_INIT(&txq->axq_q); + TAILQ_INIT(&txq->axq_tidq); + TAILQ_INIT(&txq->fifo.axq_q); ATH_TXQ_LOCK_INIT(sc, txq); } @@ -3861,7 +3858,6 @@ ath_txq_init(struct ath_softc *sc, struct ath_txq *txq, int qnum) static struct ath_txq * ath_txq_setup(struct ath_softc *sc, int qtype, int subtype) { -#define N(a) (sizeof(a)/sizeof(a[0])) struct ath_hal *ah = sc->sc_ah; HAL_TXQ_INFO qi; int qnum; @@ -3883,7 +3879,13 @@ ath_txq_setup(struct ath_softc *sc, int qtype, int subtype) * up in which case the top half of the kernel may backup * due to a lack of tx descriptors. */ - qi.tqi_qflags = HAL_TXQ_TXEOLINT_ENABLE | HAL_TXQ_TXDESCINT_ENABLE; + if (sc->sc_isedma) + qi.tqi_qflags = HAL_TXQ_TXEOLINT_ENABLE | + HAL_TXQ_TXOKINT_ENABLE; + else + qi.tqi_qflags = HAL_TXQ_TXEOLINT_ENABLE | + HAL_TXQ_TXDESCINT_ENABLE; + qnum = ath_hal_setuptxqueue(ah, qtype, &qi); if (qnum == -1) { /* @@ -3892,10 +3894,10 @@ ath_txq_setup(struct ath_softc *sc, int qtype, int subtype) */ return NULL; } - if (qnum >= N(sc->sc_txq)) { + if (qnum >= nitems(sc->sc_txq)) { device_printf(sc->sc_dev, "hal qnum %u out of range, max %zu!\n", - qnum, N(sc->sc_txq)); + qnum, nitems(sc->sc_txq)); ath_hal_releasetxqueue(ah, qnum); return NULL; } @@ -3904,7 +3906,6 @@ ath_txq_setup(struct ath_softc *sc, int qtype, int subtype) sc->sc_txqsetup |= 1<sc_txq[qnum]; -#undef N } /* @@ -3919,12 +3920,11 @@ ath_txq_setup(struct ath_softc *sc, int qtype, int subtype) static int ath_tx_setup(struct ath_softc *sc, int ac, int haltype) { -#define N(a) (sizeof(a)/sizeof(a[0])) struct ath_txq *txq; - if (ac >= N(sc->sc_ac2q)) { + if (ac >= nitems(sc->sc_ac2q)) { device_printf(sc->sc_dev, "AC %u out of range, max %zu!\n", - ac, N(sc->sc_ac2q)); + ac, nitems(sc->sc_ac2q)); return 0; } txq = ath_txq_setup(sc, HAL_TX_QUEUE_DATA, haltype); @@ -3934,7 +3934,6 @@ ath_tx_setup(struct ath_softc *sc, int ac, int haltype) return 1; } else return 0; -#undef N } /* @@ -3944,9 +3943,7 @@ static int ath_txq_update(struct ath_softc *sc, int ac) { #define ATH_EXPONENT_TO_VALUE(v) ((1<sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211com *ic = &sc->sc_ic; struct ath_txq *txq = sc->sc_ac2q[ac]; struct wmeParams *wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[ac]; struct ath_hal *ah = sc->sc_ah; @@ -3977,16 +3974,21 @@ ath_txq_update(struct ath_softc *sc, int ac) qi.tqi_burstTime = qi.tqi_readyTime; } else { #endif + /* + * XXX shouldn't this just use the default flags + * used in the previous queue setup? + */ qi.tqi_qflags = HAL_TXQ_TXOKINT_ENABLE | HAL_TXQ_TXERRINT_ENABLE | HAL_TXQ_TXDESCINT_ENABLE | HAL_TXQ_TXURNINT_ENABLE + | HAL_TXQ_TXEOLINT_ENABLE ; qi.tqi_aifs = wmep->wmep_aifsn; qi.tqi_cwmin = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmin); qi.tqi_cwmax = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmax); qi.tqi_readyTime = 0; - qi.tqi_burstTime = ATH_TXOP_TO_US(wmep->wmep_txopLimit); + qi.tqi_burstTime = IEEE80211_TXOP_TO_US(wmep->wmep_txopLimit); #ifdef IEEE80211_SUPPORT_TDMA } #endif @@ -3997,25 +3999,23 @@ ath_txq_update(struct ath_softc *sc, int ac) qi.tqi_aifs, qi.tqi_cwmin, qi.tqi_cwmax, qi.tqi_burstTime); if (!ath_hal_settxqueueprops(ah, txq->axq_qnum, &qi)) { - if_printf(ifp, "unable to update hardware queue " - "parameters for %s traffic!\n", - ieee80211_wme_acnames[ac]); + device_printf(sc->sc_dev, "unable to update hardware queue " + "parameters for %s traffic!\n", ieee80211_wme_acnames[ac]); return 0; } else { ath_hal_resettxqueue(ah, txq->axq_qnum); /* push to h/w */ return 1; } -#undef ATH_TXOP_TO_US #undef ATH_EXPONENT_TO_VALUE } /* * Callback from the 802.11 layer to update WME parameters. */ -static int +int ath_wme_update(struct ieee80211com *ic) { - struct ath_softc *sc = ic->ic_ifp->if_softc; + struct ath_softc *sc = ic->ic_softc; return !ath_txq_update(sc, WME_AC_BE) || !ath_txq_update(sc, WME_AC_BK) || @@ -4031,8 +4031,8 @@ ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq) { ath_hal_releasetxqueue(sc->sc_ah, txq->axq_qnum); - ATH_TXQ_LOCK_DESTROY(txq); sc->sc_txqsetup &= ~(1<axq_qnum); + ATH_TXQ_LOCK_DESTROY(txq); } /* @@ -4061,147 +4061,333 @@ ath_tx_findrix(const struct ath_softc *sc, uint8_t rate) return (rix == 0xff ? 0 : rix); } +static void +ath_tx_update_stats(struct ath_softc *sc, struct ath_tx_status *ts, + struct ath_buf *bf) +{ + struct ieee80211_node *ni = bf->bf_node; + struct ieee80211com *ic = &sc->sc_ic; + int sr, lr, pri; + + if (ts->ts_status == 0) { + u_int8_t txant = ts->ts_antenna; + sc->sc_stats.ast_ant_tx[txant]++; + sc->sc_ant_tx[txant]++; + if (ts->ts_finaltsi != 0) + sc->sc_stats.ast_tx_altrate++; + pri = M_WME_GETAC(bf->bf_m); + if (pri >= WME_AC_VO) + ic->ic_wme.wme_hipri_traffic++; + if ((bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 0) + ni->ni_inact = ni->ni_inact_reload; + } else { + if (ts->ts_status & HAL_TXERR_XRETRY) + sc->sc_stats.ast_tx_xretries++; + if (ts->ts_status & HAL_TXERR_FIFO) + sc->sc_stats.ast_tx_fifoerr++; + if (ts->ts_status & HAL_TXERR_FILT) + sc->sc_stats.ast_tx_filtered++; + if (ts->ts_status & HAL_TXERR_XTXOP) + sc->sc_stats.ast_tx_xtxop++; + if (ts->ts_status & HAL_TXERR_TIMER_EXPIRED) + sc->sc_stats.ast_tx_timerexpired++; + + if (bf->bf_m->m_flags & M_FF) + sc->sc_stats.ast_ff_txerr++; + } + /* XXX when is this valid? */ + if (ts->ts_flags & HAL_TX_DESC_CFG_ERR) + sc->sc_stats.ast_tx_desccfgerr++; + /* + * This can be valid for successful frame transmission! + * If there's a TX FIFO underrun during aggregate transmission, + * the MAC will pad the rest of the aggregate with delimiters. + * If a BA is returned, the frame is marked as "OK" and it's up + * to the TX completion code to notice which frames weren't + * successfully transmitted. + */ + if (ts->ts_flags & HAL_TX_DATA_UNDERRUN) + sc->sc_stats.ast_tx_data_underrun++; + if (ts->ts_flags & HAL_TX_DELIM_UNDERRUN) + sc->sc_stats.ast_tx_delim_underrun++; + + sr = ts->ts_shortretry; + lr = ts->ts_longretry; + sc->sc_stats.ast_tx_shortretry += sr; + sc->sc_stats.ast_tx_longretry += lr; + +} + +/* + * The default completion. If fail is 1, this means + * "please don't retry the frame, and just return -1 status + * to the net80211 stack. + */ +void +ath_tx_default_comp(struct ath_softc *sc, struct ath_buf *bf, int fail) +{ + struct ath_tx_status *ts = &bf->bf_status.ds_txstat; + int st; + + if (fail == 1) + st = -1; + else + st = ((bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 0) ? + ts->ts_status : HAL_TXERR_XRETRY; + +#if 0 + if (bf->bf_state.bfs_dobaw) + device_printf(sc->sc_dev, + "%s: bf %p: seqno %d: dobaw should've been cleared!\n", + __func__, + bf, + SEQNO(bf->bf_state.bfs_seqno)); +#endif + if (bf->bf_next != NULL) + device_printf(sc->sc_dev, + "%s: bf %p: seqno %d: bf_next not NULL!\n", + __func__, + bf, + SEQNO(bf->bf_state.bfs_seqno)); + + /* + * Check if the node software queue is empty; if so + * then clear the TIM. + * + * This needs to be done before the buffer is freed as + * otherwise the node reference will have been released + * and the node may not actually exist any longer. + * + * XXX I don't like this belonging here, but it's cleaner + * to do it here right now then all the other places + * where ath_tx_default_comp() is called. + * + * XXX TODO: during drain, ensure that the callback is + * being called so we get a chance to update the TIM. + */ + if (bf->bf_node) { + ATH_TX_LOCK(sc); + ath_tx_update_tim(sc, bf->bf_node, 0); + ATH_TX_UNLOCK(sc); + } + + /* + * Do any tx complete callback. Note this must + * be done before releasing the node reference. + * This will free the mbuf, release the net80211 + * node and recycle the ath_buf. + */ + ath_tx_freebuf(sc, bf, st); +} + +/* + * Update rate control with the given completion status. + */ +void +ath_tx_update_ratectrl(struct ath_softc *sc, struct ieee80211_node *ni, + struct ath_rc_series *rc, struct ath_tx_status *ts, int frmlen, + int nframes, int nbad) +{ + struct ath_node *an; + + /* Only for unicast frames */ + if (ni == NULL) + return; + + an = ATH_NODE(ni); + ATH_NODE_UNLOCK_ASSERT(an); + + if ((ts->ts_status & HAL_TXERR_FILT) == 0) { + ATH_NODE_LOCK(an); + ath_rate_tx_complete(sc, an, rc, ts, frmlen, nframes, nbad); + ATH_NODE_UNLOCK(an); + } +} + +/* + * Process the completion of the given buffer. + * + * This calls the rate control update and then the buffer completion. + * This will either free the buffer or requeue it. In any case, the + * bf pointer should be treated as invalid after this function is called. + */ +void +ath_tx_process_buf_completion(struct ath_softc *sc, struct ath_txq *txq, + struct ath_tx_status *ts, struct ath_buf *bf) +{ + struct ieee80211_node *ni = bf->bf_node; + + ATH_TX_UNLOCK_ASSERT(sc); + ATH_TXQ_UNLOCK_ASSERT(txq); + + /* If unicast frame, update general statistics */ + if (ni != NULL) { + /* update statistics */ + ath_tx_update_stats(sc, ts, bf); + } + + /* + * Call the completion handler. + * The completion handler is responsible for + * calling the rate control code. + * + * Frames with no completion handler get the + * rate control code called here. + */ + if (bf->bf_comp == NULL) { + if ((ts->ts_status & HAL_TXERR_FILT) == 0 && + (bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 0) { + /* + * XXX assume this isn't an aggregate + * frame. + */ + ath_tx_update_ratectrl(sc, ni, + bf->bf_state.bfs_rc, ts, + bf->bf_state.bfs_pktlen, 1, + (ts->ts_status == 0 ? 0 : 1)); + } + ath_tx_default_comp(sc, bf, 0); + } else + bf->bf_comp(sc, bf, 0); +} + + + /* * Process completed xmit descriptors from the specified queue. + * Kick the packet scheduler if needed. This can occur from this + * particular task. */ static int -ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) +ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq, int dosched) { struct ath_hal *ah = sc->sc_ah; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct ath_buf *bf, *last; - struct ath_desc *ds, *ds0; + struct ath_buf *bf; + struct ath_desc *ds; struct ath_tx_status *ts; struct ieee80211_node *ni; - struct ath_node *an; - int sr, lr, pri, nacked; +#ifdef IEEE80211_SUPPORT_SUPERG + struct ieee80211com *ic = &sc->sc_ic; +#endif /* IEEE80211_SUPPORT_SUPERG */ + int nacked; HAL_STATUS status; DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: tx queue %u head %p link %p\n", __func__, txq->axq_qnum, (caddr_t)(uintptr_t) ath_hal_gettxbuf(sc->sc_ah, txq->axq_qnum), txq->axq_link); + + ATH_KTR(sc, ATH_KTR_TXCOMP, 4, + "ath_tx_processq: txq=%u head %p link %p depth %p", + txq->axq_qnum, + (caddr_t)(uintptr_t) ath_hal_gettxbuf(sc->sc_ah, txq->axq_qnum), + txq->axq_link, + txq->axq_depth); + nacked = 0; for (;;) { ATH_TXQ_LOCK(txq); txq->axq_intrcnt = 0; /* reset periodic desc intr count */ - bf = STAILQ_FIRST(&txq->axq_q); + bf = TAILQ_FIRST(&txq->axq_q); if (bf == NULL) { ATH_TXQ_UNLOCK(txq); break; } - ds0 = &bf->bf_desc[0]; - ds = &bf->bf_desc[bf->bf_nseg - 1]; + ds = bf->bf_lastds; /* XXX must be setup correctly! */ ts = &bf->bf_status.ds_txstat; + status = ath_hal_txprocdesc(ah, ds, ts); #ifdef ATH_DEBUG if (sc->sc_debug & ATH_DEBUG_XMIT_DESC) ath_printtxbuf(sc, bf, txq->axq_qnum, 0, status == HAL_OK); + else if ((sc->sc_debug & ATH_DEBUG_RESET) && (dosched == 0)) + ath_printtxbuf(sc, bf, txq->axq_qnum, 0, + status == HAL_OK); #endif +#ifdef ATH_DEBUG_ALQ + if (if_ath_alq_checkdebug(&sc->sc_alq, + ATH_ALQ_EDMA_TXSTATUS)) { + if_ath_alq_post(&sc->sc_alq, ATH_ALQ_EDMA_TXSTATUS, + sc->sc_tx_statuslen, + (char *) ds); + } +#endif + if (status == HAL_EINPROGRESS) { + ATH_KTR(sc, ATH_KTR_TXCOMP, 3, + "ath_tx_processq: txq=%u, bf=%p ds=%p, HAL_EINPROGRESS", + txq->axq_qnum, bf, ds); ATH_TXQ_UNLOCK(txq); break; } - ATH_TXQ_REMOVE_HEAD(txq, bf_list); -#ifdef IEEE80211_SUPPORT_TDMA + ATH_TXQ_REMOVE(txq, bf, bf_list); + + /* + * Sanity check. + */ + if (txq->axq_qnum != bf->bf_state.bfs_tx_queue) { + device_printf(sc->sc_dev, + "%s: TXQ=%d: bf=%p, bfs_tx_queue=%d\n", + __func__, + txq->axq_qnum, + bf, + bf->bf_state.bfs_tx_queue); + } + if (txq->axq_qnum != bf->bf_last->bf_state.bfs_tx_queue) { + device_printf(sc->sc_dev, + "%s: TXQ=%d: bf_last=%p, bfs_tx_queue=%d\n", + __func__, + txq->axq_qnum, + bf->bf_last, + bf->bf_last->bf_state.bfs_tx_queue); + } + +#if 0 if (txq->axq_depth > 0) { /* * More frames follow. Mark the buffer busy * so it's not re-used while the hardware may * still re-read the link field in the descriptor. + * + * Use the last buffer in an aggregate as that + * is where the hardware may be - intermediate + * descriptors won't be "busy". */ - bf->bf_flags |= ATH_BUF_BUSY; + bf->bf_last->bf_flags |= ATH_BUF_BUSY; } else -#else - if (txq->axq_depth == 0) -#endif txq->axq_link = NULL; - ATH_TXQ_UNLOCK(txq); +#else + bf->bf_last->bf_flags |= ATH_BUF_BUSY; +#endif + if (bf->bf_state.bfs_aggr) + txq->axq_aggr_depth--; ni = bf->bf_node; - if (ni != NULL) { - an = ATH_NODE(ni); - if (ts->ts_status == 0) { - u_int8_t txant = ts->ts_antenna; - sc->sc_stats.ast_ant_tx[txant]++; - sc->sc_ant_tx[txant]++; - if (ts->ts_finaltsi != 0) - sc->sc_stats.ast_tx_altrate++; - pri = M_WME_GETAC(bf->bf_m); - if (pri >= WME_AC_VO) - ic->ic_wme.wme_hipri_traffic++; - if ((bf->bf_txflags & HAL_TXDESC_NOACK) == 0) - ni->ni_inact = ni->ni_inact_reload; - } else { - if (ts->ts_status & HAL_TXERR_XRETRY) - sc->sc_stats.ast_tx_xretries++; - if (ts->ts_status & HAL_TXERR_FIFO) - sc->sc_stats.ast_tx_fifoerr++; - if (ts->ts_status & HAL_TXERR_FILT) - sc->sc_stats.ast_tx_filtered++; - if (ts->ts_status & HAL_TXERR_XTXOP) - sc->sc_stats.ast_tx_xtxop++; - if (ts->ts_status & HAL_TXERR_TIMER_EXPIRED) - sc->sc_stats.ast_tx_timerexpired++; - /* XXX HAL_TX_DATA_UNDERRUN */ - /* XXX HAL_TX_DELIM_UNDERRUN */ - - if (bf->bf_m->m_flags & M_FF) - sc->sc_stats.ast_ff_txerr++; - } - /* XXX when is this valid? */ - if (ts->ts_status & HAL_TX_DESC_CFG_ERR) - sc->sc_stats.ast_tx_desccfgerr++; - - sr = ts->ts_shortretry; - lr = ts->ts_longretry; - sc->sc_stats.ast_tx_shortretry += sr; - sc->sc_stats.ast_tx_longretry += lr; - /* - * Hand the descriptor to the rate control algorithm. - */ - if ((ts->ts_status & HAL_TXERR_FILT) == 0 && - (bf->bf_txflags & HAL_TXDESC_NOACK) == 0) { - /* - * If frame was ack'd update statistics, - * including the last rx time used to - * workaround phantom bmiss interrupts. - */ - if (ts->ts_status == 0) { - nacked++; - sc->sc_stats.ast_tx_rssi = ts->ts_rssi; - ATH_RSSI_LPF(sc->sc_halstats.ns_avgtxrssi, - ts->ts_rssi); - } - ath_rate_tx_complete(sc, an, bf); - } - /* - * Do any tx complete callback. Note this must - * be done before releasing the node reference. - */ - if (bf->bf_m->m_flags & M_TXCB) - ieee80211_process_callback(ni, bf->bf_m, - (bf->bf_txflags & HAL_TXDESC_NOACK) == 0 ? - ts->ts_status : HAL_TXERR_XRETRY); - ieee80211_free_node(ni); + ATH_KTR(sc, ATH_KTR_TXCOMP, 5, + "ath_tx_processq: txq=%u, bf=%p, ds=%p, ni=%p, ts_status=0x%08x", + txq->axq_qnum, bf, ds, ni, ts->ts_status); + /* + * If unicast frame was ack'd update RSSI, + * including the last rx time used to + * workaround phantom bmiss interrupts. + */ + if (ni != NULL && ts->ts_status == 0 && + ((bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 0)) { + nacked++; + sc->sc_stats.ast_tx_rssi = ts->ts_rssi; + ATH_RSSI_LPF(sc->sc_halstats.ns_avgtxrssi, + ts->ts_rssi); } - bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, - BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); + ATH_TXQ_UNLOCK(txq); - m_freem(bf->bf_m); - bf->bf_m = NULL; - bf->bf_node = NULL; + /* + * Update statistics and call completion + */ + ath_tx_process_buf_completion(sc, txq, ts, bf); - ATH_TXBUF_LOCK(sc); - last = STAILQ_LAST(&sc->sc_txbuf, ath_buf, bf_list); - if (last != NULL) - last->bf_flags &= ~ATH_BUF_BUSY; - STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); - ATH_TXBUF_UNLOCK(sc); + /* XXX at this point, bf and ni may be totally invalid */ } #ifdef IEEE80211_SUPPORT_SUPERG /* @@ -4210,16 +4396,22 @@ ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) if (txq->axq_depth <= 1) ieee80211_ff_flush(ic, txq->axq_ac); #endif + + /* Kick the software TXQ scheduler */ + if (dosched) { + ATH_TX_LOCK(sc); + ath_txq_sched(sc, txq); + ATH_TX_UNLOCK(sc); + } + + ATH_KTR(sc, ATH_KTR_TXCOMP, 1, + "ath_tx_processq: txq=%u: done", + txq->axq_qnum); + return nacked; } -static __inline int -txqactive(struct ath_hal *ah, int qnum) -{ - u_int32_t txqs = 1<sc_ifp; + uint32_t txqs; - if (txqactive(sc->sc_ah, 0) && ath_tx_processq(sc, &sc->sc_txq[0])) + ATH_PCU_LOCK(sc); + sc->sc_txproc_cnt++; + txqs = sc->sc_txq_active; + sc->sc_txq_active &= ~txqs; + ATH_PCU_UNLOCK(sc); + + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + + ATH_KTR(sc, ATH_KTR_TXCOMP, 1, + "ath_tx_proc_q0: txqs=0x%08x", txqs); + + if (TXQACTIVE(txqs, 0) && ath_tx_processq(sc, &sc->sc_txq[0], 1)) + /* XXX why is lastrx updated in tx code? */ sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah); - if (txqactive(sc->sc_ah, sc->sc_cabq->axq_qnum)) - ath_tx_processq(sc, sc->sc_cabq); - ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + if (TXQACTIVE(txqs, sc->sc_cabq->axq_qnum)) + ath_tx_processq(sc, sc->sc_cabq, 1); sc->sc_wd_timer = 0; if (sc->sc_softled) ath_led_event(sc, sc->sc_txrix); - ath_start(ifp); + ATH_PCU_LOCK(sc); + sc->sc_txproc_cnt--; + ATH_PCU_UNLOCK(sc); + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + ath_tx_kick(sc); } /* @@ -4252,33 +4465,53 @@ static void ath_tx_proc_q0123(void *arg, int npending) { struct ath_softc *sc = arg; - struct ifnet *ifp = sc->sc_ifp; int nacked; + uint32_t txqs; + + ATH_PCU_LOCK(sc); + sc->sc_txproc_cnt++; + txqs = sc->sc_txq_active; + sc->sc_txq_active &= ~txqs; + ATH_PCU_UNLOCK(sc); + + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + + ATH_KTR(sc, ATH_KTR_TXCOMP, 1, + "ath_tx_proc_q0123: txqs=0x%08x", txqs); /* * Process each active queue. */ nacked = 0; - if (txqactive(sc->sc_ah, 0)) - nacked += ath_tx_processq(sc, &sc->sc_txq[0]); - if (txqactive(sc->sc_ah, 1)) - nacked += ath_tx_processq(sc, &sc->sc_txq[1]); - if (txqactive(sc->sc_ah, 2)) - nacked += ath_tx_processq(sc, &sc->sc_txq[2]); - if (txqactive(sc->sc_ah, 3)) - nacked += ath_tx_processq(sc, &sc->sc_txq[3]); - if (txqactive(sc->sc_ah, sc->sc_cabq->axq_qnum)) - ath_tx_processq(sc, sc->sc_cabq); + if (TXQACTIVE(txqs, 0)) + nacked += ath_tx_processq(sc, &sc->sc_txq[0], 1); + if (TXQACTIVE(txqs, 1)) + nacked += ath_tx_processq(sc, &sc->sc_txq[1], 1); + if (TXQACTIVE(txqs, 2)) + nacked += ath_tx_processq(sc, &sc->sc_txq[2], 1); + if (TXQACTIVE(txqs, 3)) + nacked += ath_tx_processq(sc, &sc->sc_txq[3], 1); + if (TXQACTIVE(txqs, sc->sc_cabq->axq_qnum)) + ath_tx_processq(sc, sc->sc_cabq, 1); if (nacked) sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah); - ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; sc->sc_wd_timer = 0; if (sc->sc_softled) ath_led_event(sc, sc->sc_txrix); - ath_start(ifp); + ATH_PCU_LOCK(sc); + sc->sc_txproc_cnt--; + ATH_PCU_UNLOCK(sc); + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + ath_tx_kick(sc); } /* @@ -4288,35 +4521,323 @@ static void ath_tx_proc(void *arg, int npending) { struct ath_softc *sc = arg; - struct ifnet *ifp = sc->sc_ifp; int i, nacked; + uint32_t txqs; + + ATH_PCU_LOCK(sc); + sc->sc_txproc_cnt++; + txqs = sc->sc_txq_active; + sc->sc_txq_active &= ~txqs; + ATH_PCU_UNLOCK(sc); + + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + + ATH_KTR(sc, ATH_KTR_TXCOMP, 1, "ath_tx_proc: txqs=0x%08x", txqs); /* * Process each active queue. */ nacked = 0; for (i = 0; i < HAL_NUM_TX_QUEUES; i++) - if (ATH_TXQ_SETUP(sc, i) && txqactive(sc->sc_ah, i)) - nacked += ath_tx_processq(sc, &sc->sc_txq[i]); + if (ATH_TXQ_SETUP(sc, i) && TXQACTIVE(txqs, i)) + nacked += ath_tx_processq(sc, &sc->sc_txq[i], 1); if (nacked) sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah); - ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; sc->sc_wd_timer = 0; if (sc->sc_softled) ath_led_event(sc, sc->sc_txrix); - ath_start(ifp); + ATH_PCU_LOCK(sc); + sc->sc_txproc_cnt--; + ATH_PCU_UNLOCK(sc); + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + ath_tx_kick(sc); +} +#undef TXQACTIVE + +/* + * Deferred processing of TXQ rescheduling. + */ +static void +ath_txq_sched_tasklet(void *arg, int npending) +{ + struct ath_softc *sc = arg; + int i; + + /* XXX is skipping ok? */ + ATH_PCU_LOCK(sc); +#if 0 + if (sc->sc_inreset_cnt > 0) { + device_printf(sc->sc_dev, + "%s: sc_inreset_cnt > 0; skipping\n", __func__); + ATH_PCU_UNLOCK(sc); + return; + } +#endif + sc->sc_txproc_cnt++; + ATH_PCU_UNLOCK(sc); + + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + + ATH_TX_LOCK(sc); + for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { + if (ATH_TXQ_SETUP(sc, i)) { + ath_txq_sched(sc, &sc->sc_txq[i]); + } + } + ATH_TX_UNLOCK(sc); + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + ATH_PCU_LOCK(sc); + sc->sc_txproc_cnt--; + ATH_PCU_UNLOCK(sc); } +void +ath_returnbuf_tail(struct ath_softc *sc, struct ath_buf *bf) +{ + + ATH_TXBUF_LOCK_ASSERT(sc); + + if (bf->bf_flags & ATH_BUF_MGMT) + TAILQ_INSERT_TAIL(&sc->sc_txbuf_mgmt, bf, bf_list); + else { + TAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); + sc->sc_txbuf_cnt++; + if (sc->sc_txbuf_cnt > ath_txbuf) { + device_printf(sc->sc_dev, + "%s: sc_txbuf_cnt > %d?\n", + __func__, + ath_txbuf); + sc->sc_txbuf_cnt = ath_txbuf; + } + } +} + +void +ath_returnbuf_head(struct ath_softc *sc, struct ath_buf *bf) +{ + + ATH_TXBUF_LOCK_ASSERT(sc); + + if (bf->bf_flags & ATH_BUF_MGMT) + TAILQ_INSERT_HEAD(&sc->sc_txbuf_mgmt, bf, bf_list); + else { + TAILQ_INSERT_HEAD(&sc->sc_txbuf, bf, bf_list); + sc->sc_txbuf_cnt++; + if (sc->sc_txbuf_cnt > ATH_TXBUF) { + device_printf(sc->sc_dev, + "%s: sc_txbuf_cnt > %d?\n", + __func__, + ATH_TXBUF); + sc->sc_txbuf_cnt = ATH_TXBUF; + } + } +} + +/* + * Free the holding buffer if it exists + */ +void +ath_txq_freeholdingbuf(struct ath_softc *sc, struct ath_txq *txq) +{ + ATH_TXBUF_UNLOCK_ASSERT(sc); + ATH_TXQ_LOCK_ASSERT(txq); + + if (txq->axq_holdingbf == NULL) + return; + + txq->axq_holdingbf->bf_flags &= ~ATH_BUF_BUSY; + + ATH_TXBUF_LOCK(sc); + ath_returnbuf_tail(sc, txq->axq_holdingbf); + ATH_TXBUF_UNLOCK(sc); + + txq->axq_holdingbf = NULL; +} + +/* + * Add this buffer to the holding queue, freeing the previous + * one if it exists. + */ static void +ath_txq_addholdingbuf(struct ath_softc *sc, struct ath_buf *bf) +{ + struct ath_txq *txq; + + txq = &sc->sc_txq[bf->bf_state.bfs_tx_queue]; + + ATH_TXBUF_UNLOCK_ASSERT(sc); + ATH_TXQ_LOCK_ASSERT(txq); + + /* XXX assert ATH_BUF_BUSY is set */ + + /* XXX assert the tx queue is under the max number */ + if (bf->bf_state.bfs_tx_queue > HAL_NUM_TX_QUEUES) { + device_printf(sc->sc_dev, "%s: bf=%p: invalid tx queue (%d)\n", + __func__, + bf, + bf->bf_state.bfs_tx_queue); + bf->bf_flags &= ~ATH_BUF_BUSY; + ath_returnbuf_tail(sc, bf); + return; + } + ath_txq_freeholdingbuf(sc, txq); + txq->axq_holdingbf = bf; +} + +/* + * Return a buffer to the pool and update the 'busy' flag on the + * previous 'tail' entry. + * + * This _must_ only be called when the buffer is involved in a completed + * TX. The logic is that if it was part of an active TX, the previous + * buffer on the list is now not involved in a halted TX DMA queue, waiting + * for restart (eg for TDMA.) + * + * The caller must free the mbuf and recycle the node reference. + * + * XXX This method of handling busy / holding buffers is insanely stupid. + * It requires bf_state.bfs_tx_queue to be correctly assigned. It would + * be much nicer if buffers in the processq() methods would instead be + * always completed there (pushed onto a txq or ath_bufhead) so we knew + * exactly what hardware queue they came from in the first place. + */ +void +ath_freebuf(struct ath_softc *sc, struct ath_buf *bf) +{ + struct ath_txq *txq; + + txq = &sc->sc_txq[bf->bf_state.bfs_tx_queue]; + + KASSERT((bf->bf_node == NULL), ("%s: bf->bf_node != NULL\n", __func__)); + KASSERT((bf->bf_m == NULL), ("%s: bf->bf_m != NULL\n", __func__)); + + /* + * If this buffer is busy, push it onto the holding queue. + */ + if (bf->bf_flags & ATH_BUF_BUSY) { + ATH_TXQ_LOCK(txq); + ath_txq_addholdingbuf(sc, bf); + ATH_TXQ_UNLOCK(txq); + return; + } + + /* + * Not a busy buffer, so free normally + */ + ATH_TXBUF_LOCK(sc); + ath_returnbuf_tail(sc, bf); + ATH_TXBUF_UNLOCK(sc); +} + +/* + * This is currently used by ath_tx_draintxq() and + * ath_tx_tid_free_pkts(). + * + * It recycles a single ath_buf. + */ +void +ath_tx_freebuf(struct ath_softc *sc, struct ath_buf *bf, int status) +{ + struct ieee80211_node *ni = bf->bf_node; + struct mbuf *m0 = bf->bf_m; + + /* + * Make sure that we only sync/unload if there's an mbuf. + * If not (eg we cloned a buffer), the unload will have already + * occurred. + */ + if (bf->bf_m != NULL) { + bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); + } + + bf->bf_node = NULL; + bf->bf_m = NULL; + + /* Free the buffer, it's not needed any longer */ + ath_freebuf(sc, bf); + + /* Pass the buffer back to net80211 - completing it */ + ieee80211_tx_complete(ni, m0, status); +} + +static struct ath_buf * +ath_tx_draintxq_get_one(struct ath_softc *sc, struct ath_txq *txq) +{ + struct ath_buf *bf; + + ATH_TXQ_LOCK_ASSERT(txq); + + /* + * Drain the FIFO queue first, then if it's + * empty, move to the normal frame queue. + */ + bf = TAILQ_FIRST(&txq->fifo.axq_q); + if (bf != NULL) { + /* + * Is it the last buffer in this set? + * Decrement the FIFO counter. + */ + if (bf->bf_flags & ATH_BUF_FIFOEND) { + if (txq->axq_fifo_depth == 0) { + device_printf(sc->sc_dev, + "%s: Q%d: fifo_depth=0, fifo.axq_depth=%d?\n", + __func__, + txq->axq_qnum, + txq->fifo.axq_depth); + } else + txq->axq_fifo_depth--; + } + ATH_TXQ_REMOVE(&txq->fifo, bf, bf_list); + return (bf); + } + + /* + * Debugging! + */ + if (txq->axq_fifo_depth != 0 || txq->fifo.axq_depth != 0) { + device_printf(sc->sc_dev, + "%s: Q%d: fifo_depth=%d, fifo.axq_depth=%d\n", + __func__, + txq->axq_qnum, + txq->axq_fifo_depth, + txq->fifo.axq_depth); + } + + /* + * Now drain the pending queue. + */ + bf = TAILQ_FIRST(&txq->axq_q); + if (bf == NULL) { + txq->axq_link = NULL; + return (NULL); + } + ATH_TXQ_REMOVE(txq, bf, bf_list); + return (bf); +} + +void ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq) { #ifdef ATH_DEBUG struct ath_hal *ah = sc->sc_ah; #endif - struct ieee80211_node *ni; struct ath_buf *bf; u_int ix; @@ -4324,51 +4845,65 @@ ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq) * NB: this assumes output has been stopped and * we do not need to block ath_tx_proc */ - ATH_TXBUF_LOCK(sc); - bf = STAILQ_LAST(&sc->sc_txbuf, ath_buf, bf_list); - if (bf != NULL) - bf->bf_flags &= ~ATH_BUF_BUSY; - ATH_TXBUF_UNLOCK(sc); for (ix = 0;; ix++) { ATH_TXQ_LOCK(txq); - bf = STAILQ_FIRST(&txq->axq_q); + bf = ath_tx_draintxq_get_one(sc, txq); if (bf == NULL) { - txq->axq_link = NULL; ATH_TXQ_UNLOCK(txq); break; } - ATH_TXQ_REMOVE_HEAD(txq, bf_list); - ATH_TXQ_UNLOCK(txq); + if (bf->bf_state.bfs_aggr) + txq->axq_aggr_depth--; #ifdef ATH_DEBUG if (sc->sc_debug & ATH_DEBUG_RESET) { - struct ieee80211com *ic = sc->sc_ifp->if_l2com; + struct ieee80211com *ic = &sc->sc_ic; + int status = 0; - ath_printtxbuf(sc, bf, txq->axq_qnum, ix, - ath_hal_txprocdesc(ah, bf->bf_desc, + /* + * EDMA operation has a TX completion FIFO + * separate from the TX descriptor, so this + * method of checking the "completion" status + * is wrong. + */ + if (! sc->sc_isedma) { + status = (ath_hal_txprocdesc(ah, + bf->bf_lastds, &bf->bf_status.ds_txstat) == HAL_OK); + } + ath_printtxbuf(sc, bf, txq->axq_qnum, ix, status); ieee80211_dump_pkt(ic, mtod(bf->bf_m, const uint8_t *), bf->bf_m->m_len, 0, -1); } #endif /* ATH_DEBUG */ - bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); - ni = bf->bf_node; - bf->bf_node = NULL; - if (ni != NULL) { - /* - * Do any callback and reclaim the node reference. - */ - if (bf->bf_m->m_flags & M_TXCB) - ieee80211_process_callback(ni, bf->bf_m, -1); - ieee80211_free_node(ni); - } - m_freem(bf->bf_m); - bf->bf_m = NULL; + /* + * Since we're now doing magic in the completion + * functions, we -must- call it for aggregation + * destinations or BAW tracking will get upset. + */ + /* + * Clear ATH_BUF_BUSY; the completion handler + * will free the buffer. + */ + ATH_TXQ_UNLOCK(txq); bf->bf_flags &= ~ATH_BUF_BUSY; - - ATH_TXBUF_LOCK(sc); - STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); - ATH_TXBUF_UNLOCK(sc); + if (bf->bf_comp) + bf->bf_comp(sc, bf, 1); + else + ath_tx_default_comp(sc, bf, 1); } + + /* + * Free the holding buffer if it exists + */ + ATH_TXQ_LOCK(txq); + ath_txq_freeholdingbuf(sc, txq); + ATH_TXQ_UNLOCK(txq); + + /* + * Drain software queued frames which are on + * active TIDs. + */ + ath_tx_txq_drain(sc, txq); } static void @@ -4376,126 +4911,158 @@ ath_tx_stopdma(struct ath_softc *sc, struct ath_txq *txq) { struct ath_hal *ah = sc->sc_ah; - DPRINTF(sc, ATH_DEBUG_RESET, "%s: tx queue [%u] %p, link %p\n", - __func__, txq->axq_qnum, + ATH_TXQ_LOCK_ASSERT(txq); + + DPRINTF(sc, ATH_DEBUG_RESET, + "%s: tx queue [%u] %p, active=%d, hwpending=%d, flags 0x%08x, " + "link %p, holdingbf=%p\n", + __func__, + txq->axq_qnum, (caddr_t)(uintptr_t) ath_hal_gettxbuf(ah, txq->axq_qnum), - txq->axq_link); + (int) (!! ath_hal_txqenabled(ah, txq->axq_qnum)), + (int) ath_hal_numtxpending(ah, txq->axq_qnum), + txq->axq_flags, + txq->axq_link, + txq->axq_holdingbf); + (void) ath_hal_stoptxdma(ah, txq->axq_qnum); + /* We've stopped TX DMA, so mark this as stopped. */ + txq->axq_flags &= ~ATH_TXQ_PUTRUNNING; + +#ifdef ATH_DEBUG + if ((sc->sc_debug & ATH_DEBUG_RESET) + && (txq->axq_holdingbf != NULL)) { + ath_printtxbuf(sc, txq->axq_holdingbf, txq->axq_qnum, 0, 0); + } +#endif } -/* - * Drain the transmit queues and reclaim resources. - */ -static void -ath_draintxq(struct ath_softc *sc) +int +ath_stoptxdma(struct ath_softc *sc) { struct ath_hal *ah = sc->sc_ah; - struct ifnet *ifp = sc->sc_ifp; int i; /* XXX return value */ + if (sc->sc_invalid) + return 0; + if (!sc->sc_invalid) { /* don't touch the hardware if marked invalid */ DPRINTF(sc, ATH_DEBUG_RESET, "%s: tx queue [%u] %p, link %p\n", __func__, sc->sc_bhalq, (caddr_t)(uintptr_t) ath_hal_gettxbuf(ah, sc->sc_bhalq), NULL); + + /* stop the beacon queue */ (void) ath_hal_stoptxdma(ah, sc->sc_bhalq); - for (i = 0; i < HAL_NUM_TX_QUEUES; i++) - if (ATH_TXQ_SETUP(sc, i)) + + /* Stop the data queues */ + for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { + if (ATH_TXQ_SETUP(sc, i)) { + ATH_TXQ_LOCK(&sc->sc_txq[i]); ath_tx_stopdma(sc, &sc->sc_txq[i]); + ATH_TXQ_UNLOCK(&sc->sc_txq[i]); + } + } + } + + return 1; +} + +#ifdef ATH_DEBUG +void +ath_tx_dump(struct ath_softc *sc, struct ath_txq *txq) +{ + struct ath_hal *ah = sc->sc_ah; + struct ath_buf *bf; + int i = 0; + + if (! (sc->sc_debug & ATH_DEBUG_RESET)) + return; + + device_printf(sc->sc_dev, "%s: Q%d: begin\n", + __func__, txq->axq_qnum); + TAILQ_FOREACH(bf, &txq->axq_q, bf_list) { + ath_printtxbuf(sc, bf, txq->axq_qnum, i, + ath_hal_txprocdesc(ah, bf->bf_lastds, + &bf->bf_status.ds_txstat) == HAL_OK); + i++; + } + device_printf(sc->sc_dev, "%s: Q%d: end\n", + __func__, txq->axq_qnum); +} +#endif /* ATH_DEBUG */ + +/* + * Drain the transmit queues and reclaim resources. + */ +void +ath_legacy_tx_drain(struct ath_softc *sc, ATH_RESET_TYPE reset_type) +{ + struct ath_hal *ah = sc->sc_ah; + struct ath_buf *bf_last; + int i; + + (void) ath_stoptxdma(sc); + + /* + * Dump the queue contents + */ + for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { + /* + * XXX TODO: should we just handle the completed TX frames + * here, whether or not the reset is a full one or not? + */ + if (ATH_TXQ_SETUP(sc, i)) { +#ifdef ATH_DEBUG + if (sc->sc_debug & ATH_DEBUG_RESET) + ath_tx_dump(sc, &sc->sc_txq[i]); +#endif /* ATH_DEBUG */ + if (reset_type == ATH_RESET_NOLOSS) { + ath_tx_processq(sc, &sc->sc_txq[i], 0); + ATH_TXQ_LOCK(&sc->sc_txq[i]); + /* + * Free the holding buffer; DMA is now + * stopped. + */ + ath_txq_freeholdingbuf(sc, &sc->sc_txq[i]); + /* + * Setup the link pointer to be the + * _last_ buffer/descriptor in the list. + * If there's nothing in the list, set it + * to NULL. + */ + bf_last = ATH_TXQ_LAST(&sc->sc_txq[i], + axq_q_s); + if (bf_last != NULL) { + ath_hal_gettxdesclinkptr(ah, + bf_last->bf_lastds, + &sc->sc_txq[i].axq_link); + } else { + sc->sc_txq[i].axq_link = NULL; + } + ATH_TXQ_UNLOCK(&sc->sc_txq[i]); + } else + ath_tx_draintxq(sc, &sc->sc_txq[i]); + } } - for (i = 0; i < HAL_NUM_TX_QUEUES; i++) - if (ATH_TXQ_SETUP(sc, i)) - ath_tx_draintxq(sc, &sc->sc_txq[i]); #ifdef ATH_DEBUG if (sc->sc_debug & ATH_DEBUG_RESET) { - struct ath_buf *bf = STAILQ_FIRST(&sc->sc_bbuf); + struct ath_buf *bf = TAILQ_FIRST(&sc->sc_bbuf); if (bf != NULL && bf->bf_m != NULL) { ath_printtxbuf(sc, bf, sc->sc_bhalq, 0, - ath_hal_txprocdesc(ah, bf->bf_desc, + ath_hal_txprocdesc(ah, bf->bf_lastds, &bf->bf_status.ds_txstat) == HAL_OK); - ieee80211_dump_pkt(ifp->if_l2com, + ieee80211_dump_pkt(&sc->sc_ic, mtod(bf->bf_m, const uint8_t *), bf->bf_m->m_len, 0, -1); } } #endif /* ATH_DEBUG */ - ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; sc->sc_wd_timer = 0; } -/* - * Disable the receive h/w in preparation for a reset. - */ -static void -ath_stoprecv(struct ath_softc *sc) -{ -#define PA2DESC(_sc, _pa) \ - ((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \ - ((_pa) - (_sc)->sc_rxdma.dd_desc_paddr))) - struct ath_hal *ah = sc->sc_ah; - - ath_hal_stoppcurecv(ah); /* disable PCU */ - ath_hal_setrxfilter(ah, 0); /* clear recv filter */ - ath_hal_stopdmarecv(ah); /* disable DMA engine */ - DELAY(3000); /* 3ms is long enough for 1 frame */ -#ifdef ATH_DEBUG - if (sc->sc_debug & (ATH_DEBUG_RESET | ATH_DEBUG_FATAL)) { - struct ath_buf *bf; - u_int ix; - - printf("%s: rx queue %p, link %p\n", __func__, - (caddr_t)(uintptr_t) ath_hal_getrxbuf(ah), sc->sc_rxlink); - ix = 0; - STAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) { - struct ath_desc *ds = bf->bf_desc; - struct ath_rx_status *rs = &bf->bf_status.ds_rxstat; - HAL_STATUS status = ath_hal_rxprocdesc(ah, ds, - bf->bf_daddr, PA2DESC(sc, ds->ds_link), rs); - if (status == HAL_OK || (sc->sc_debug & ATH_DEBUG_FATAL)) - ath_printrxbuf(sc, bf, ix, status == HAL_OK); - ix++; - } - } -#endif - if (sc->sc_rxpending != NULL) { - m_freem(sc->sc_rxpending); - sc->sc_rxpending = NULL; - } - sc->sc_rxlink = NULL; /* just in case */ -#undef PA2DESC -} - -/* - * Enable the receive h/w following a reset. - */ -static int -ath_startrecv(struct ath_softc *sc) -{ - struct ath_hal *ah = sc->sc_ah; - struct ath_buf *bf; - - sc->sc_rxlink = NULL; - sc->sc_rxpending = NULL; - STAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) { - int error = ath_rxbuf_init(sc, bf); - if (error != 0) { - DPRINTF(sc, ATH_DEBUG_RECV, - "%s: ath_rxbuf_init failed %d\n", - __func__, error); - return error; - } - } - - bf = STAILQ_FIRST(&sc->sc_rxbuf); - ath_hal_putrxbuf(ah, bf->bf_daddr); - ath_hal_rxena(ah); /* enable recv descriptors */ - ath_mode_init(sc); /* set filters, etc. */ - ath_hal_startpcurecv(ah); /* re-enable PCU/DMA engine */ - return 0; -} - /* * Update internal state after a channel change. */ @@ -4523,9 +5090,32 @@ ath_chan_change(struct ath_softc *sc, struct ieee80211_channel *chan) static int ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan) { - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = sc->sc_ah; + int ret = 0; + + /* Treat this as an interface reset */ + ATH_PCU_UNLOCK_ASSERT(sc); + ATH_UNLOCK_ASSERT(sc); + + /* (Try to) stop TX/RX from occurring */ + taskqueue_block(sc->sc_tq); + + ATH_PCU_LOCK(sc); + + /* Disable interrupts */ + ath_hal_intrset(ah, 0); + + /* Stop new RX/TX/interrupt completion */ + if (ath_reset_grablock(sc, 1) == 0) { + device_printf(sc->sc_dev, "%s: concurrent reset! Danger!\n", + __func__); + } + + /* Stop pending RX/TX completion */ + ath_txrx_stop_locked(sc); + + ATH_PCU_UNLOCK(sc); DPRINTF(sc, ATH_DEBUG_RESET, "%s: %u (%u MHz, flags 0x%x)\n", __func__, ieee80211_chan2ieee(ic, chan), @@ -4538,28 +5128,68 @@ ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan) * hardware at the new frequency, and then re-enable * the relevant bits of the h/w. */ +#if 0 ath_hal_intrset(ah, 0); /* disable interrupts */ - ath_draintxq(sc); /* clear pending tx frames */ - ath_stoprecv(sc); /* turn off frame recv */ - if (!ath_hal_reset(ah, sc->sc_opmode, chan, AH_TRUE, &status)) { - if_printf(ifp, "%s: unable to reset " +#endif + ath_stoprecv(sc, 1); /* turn off frame recv */ + /* + * First, handle completed TX/RX frames. + */ + ath_rx_flush(sc); + ath_draintxq(sc, ATH_RESET_NOLOSS); + /* + * Next, flush the non-scheduled frames. + */ + ath_draintxq(sc, ATH_RESET_FULL); /* clear pending tx frames */ + + ath_update_chainmasks(sc, chan); + ath_hal_setchainmasks(sc->sc_ah, sc->sc_cur_txchainmask, + sc->sc_cur_rxchainmask); + if (!ath_hal_reset(ah, sc->sc_opmode, chan, AH_TRUE, + HAL_RESET_NORMAL, &status)) { + device_printf(sc->sc_dev, "%s: unable to reset " "channel %u (%u MHz, flags 0x%x), hal status %u\n", __func__, ieee80211_chan2ieee(ic, chan), chan->ic_freq, chan->ic_flags, status); - return EIO; + ret = EIO; + goto finish; } sc->sc_diversity = ath_hal_getdiversity(ah); + ATH_RX_LOCK(sc); + sc->sc_rx_stopped = 1; + sc->sc_rx_resetted = 1; + ATH_RX_UNLOCK(sc); + /* Let DFS at it in case it's a DFS channel */ - ath_dfs_radar_enable(sc, ic->ic_curchan); + ath_dfs_radar_enable(sc, chan); + + /* Let spectral at in case spectral is enabled */ + ath_spectral_enable(sc, chan); + + /* + * Let bluetooth coexistence at in case it's needed for this + * channel + */ + ath_btcoex_enable(sc, ic->ic_curchan); + + /* + * If we're doing TDMA, enforce the TXOP limitation for chips + * that support it. + */ + if (sc->sc_hasenforcetxop && sc->sc_tdma) + ath_hal_setenforcetxop(sc->sc_ah, 1); + else + ath_hal_setenforcetxop(sc->sc_ah, 0); /* * Re-enable rx framework. */ if (ath_startrecv(sc) != 0) { - if_printf(ifp, "%s: unable to restart recv logic\n", - __func__); - return EIO; + device_printf(sc->sc_dev, + "%s: unable to restart recv logic\n", __func__); + ret = EIO; + goto finish; } /* @@ -4584,9 +5214,22 @@ ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan) /* * Re-enable interrupts. */ +#if 0 ath_hal_intrset(ah, sc->sc_imask); +#endif } - return 0; + +finish: + ATH_PCU_LOCK(sc); + sc->sc_inreset_cnt--; + /* XXX only do this if sc_inreset_cnt == 0? */ + ath_hal_intrset(ah, sc->sc_imask); + ATH_PCU_UNLOCK(sc); + + ath_txrx_start(sc); + /* XXX ath_start? */ + + return ret; } /* @@ -4598,12 +5241,22 @@ ath_calibrate(void *arg) { struct ath_softc *sc = arg; struct ath_hal *ah = sc->sc_ah; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - HAL_BOOL longCal, isCalDone; + struct ieee80211com *ic = &sc->sc_ic; + HAL_BOOL longCal, isCalDone = AH_TRUE; HAL_BOOL aniCal, shortCal = AH_FALSE; int nextcal; + ATH_LOCK_ASSERT(sc); + + /* + * Force the hardware awake for ANI work. + */ + ath_power_set_power_state(sc, HAL_PM_AWAKE); + + /* Skip trying to do this if we're in reset */ + if (sc->sc_inreset_cnt) + goto restart; + if (ic->ic_flags & IEEE80211_F_SCAN) /* defer, off channel */ goto restart; longCal = (ticks - sc->sc_lastlongcal >= ath_longcalinterval*hz); @@ -4629,7 +5282,12 @@ ath_calibrate(void *arg) DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: rfgain change\n", __func__); sc->sc_stats.ast_per_rfgain++; - ath_reset(ifp); + sc->sc_resetcal = 0; + sc->sc_doresetcal = AH_TRUE; + taskqueue_enqueue(sc->sc_tq, &sc->sc_resettask); + callout_reset(&sc->sc_cal_ch, 1, ath_calibrate, sc); + ath_power_restore_power_state(sc); + return; } /* * If this long cal is after an idle period, then @@ -4646,6 +5304,7 @@ ath_calibrate(void *arg) /* Only call if we're doing a short/long cal, not for ANI calibration */ if (shortCal || longCal) { + isCalDone = AH_FALSE; if (ath_hal_calibrateN(ah, sc->sc_curchan, longCal, &isCalDone)) { if (longCal) { /* @@ -4699,53 +5358,95 @@ restart: __func__); /* NB: don't rearm timer */ } + /* + * Restore power state now that we're done. + */ + ath_power_restore_power_state(sc); } static void ath_scan_start(struct ieee80211com *ic) { - struct ifnet *ifp = ic->ic_ifp; - struct ath_softc *sc = ifp->if_softc; + struct ath_softc *sc = ic->ic_softc; struct ath_hal *ah = sc->sc_ah; u_int32_t rfilt; /* XXX calibration timer? */ + /* XXXGL: is constant ieee80211broadcastaddr a correct choice? */ + ATH_LOCK(sc); sc->sc_scanning = 1; sc->sc_syncbeacon = 0; rfilt = ath_calcrxfilter(sc); + ATH_UNLOCK(sc); + + ATH_PCU_LOCK(sc); ath_hal_setrxfilter(ah, rfilt); - ath_hal_setassocid(ah, ifp->if_broadcastaddr, 0); + ath_hal_setassocid(ah, ieee80211broadcastaddr, 0); + ATH_PCU_UNLOCK(sc); DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s aid 0\n", - __func__, rfilt, ether_sprintf(ifp->if_broadcastaddr)); + __func__, rfilt, ether_sprintf(ieee80211broadcastaddr)); } static void ath_scan_end(struct ieee80211com *ic) { - struct ifnet *ifp = ic->ic_ifp; - struct ath_softc *sc = ifp->if_softc; + struct ath_softc *sc = ic->ic_softc; struct ath_hal *ah = sc->sc_ah; u_int32_t rfilt; + ATH_LOCK(sc); sc->sc_scanning = 0; rfilt = ath_calcrxfilter(sc); + ATH_UNLOCK(sc); + + ATH_PCU_LOCK(sc); ath_hal_setrxfilter(ah, rfilt); ath_hal_setassocid(ah, sc->sc_curbssid, sc->sc_curaid); ath_hal_process_noisefloor(ah); + ATH_PCU_UNLOCK(sc); DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s aid 0x%x\n", __func__, rfilt, ether_sprintf(sc->sc_curbssid), sc->sc_curaid); } +#ifdef ATH_ENABLE_11N +/* + * For now, just do a channel change. + * + * Later, we'll go through the hard slog of suspending tx/rx, changing rate + * control state and resetting the hardware without dropping frames out + * of the queue. + * + * The unfortunate trouble here is making absolutely sure that the + * channel width change has propagated enough so the hardware + * absolutely isn't handed bogus frames for it's current operating + * mode. (Eg, 40MHz frames in 20MHz mode.) Since TX and RX can and + * does occur in parallel, we need to make certain we've blocked + * any further ongoing TX (and RX, that can cause raw TX) + * before we do this. + */ +static void +ath_update_chw(struct ieee80211com *ic) +{ + struct ath_softc *sc = ic->ic_softc; + + DPRINTF(sc, ATH_DEBUG_STATE, "%s: called\n", __func__); + ath_set_channel(ic); +} +#endif /* ATH_ENABLE_11N */ + static void ath_set_channel(struct ieee80211com *ic) { - struct ifnet *ifp = ic->ic_ifp; - struct ath_softc *sc = ifp->if_softc; + struct ath_softc *sc = ic->ic_softc; + + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); (void) ath_chan_set(sc, ic->ic_curchan); /* @@ -4754,8 +5455,11 @@ ath_set_channel(struct ieee80211com *ic) * beacon timers. Note that since we only hear beacons in * sta/ibss mode this has no effect in other operating modes. */ + ATH_LOCK(sc); if (!sc->sc_scanning && ic->ic_curchan == ic->ic_bsschan) sc->sc_syncbeacon = 1; + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); } /* @@ -4780,13 +5484,15 @@ static int ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct ieee80211com *ic = vap->iv_ic; - struct ath_softc *sc = ic->ic_ifp->if_softc; + struct ath_softc *sc = ic->ic_softc; struct ath_vap *avp = ATH_VAP(vap); struct ath_hal *ah = sc->sc_ah; struct ieee80211_node *ni = NULL; int i, error, stamode; u_int32_t rfilt; int csa_run_transition = 0; + enum ieee80211_state ostate = vap->iv_state; + static const HAL_LED_STATE leds[] = { HAL_LED_INIT, /* IEEE80211_S_INIT */ HAL_LED_SCAN, /* IEEE80211_S_SCAN */ @@ -4799,13 +5505,46 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) }; DPRINTF(sc, ATH_DEBUG_STATE, "%s: %s -> %s\n", __func__, - ieee80211_state_name[vap->iv_state], + ieee80211_state_name[ostate], ieee80211_state_name[nstate]); - if (vap->iv_state == IEEE80211_S_CSA && nstate == IEEE80211_S_RUN) + /* + * net80211 _should_ have the comlock asserted at this point. + * There are some comments around the calls to vap->iv_newstate + * which indicate that it (newstate) may end up dropping the + * lock. This and the subsequent lock assert check after newstate + * are an attempt to catch these and figure out how/why. + */ + IEEE80211_LOCK_ASSERT(ic); + + /* Before we touch the hardware - wake it up */ + ATH_LOCK(sc); + /* + * If the NIC is in anything other than SLEEP state, + * we need to ensure that self-generated frames are + * set for PWRMGT=0. Otherwise we may end up with + * strange situations. + * + * XXX TODO: is this actually the case? :-) + */ + if (nstate != IEEE80211_S_SLEEP) + ath_power_setselfgen(sc, HAL_PM_AWAKE); + + /* + * Now, wake the thing up. + */ + ath_power_set_power_state(sc, HAL_PM_AWAKE); + + /* + * And stop the calibration callout whilst we have + * ATH_LOCK held. + */ + callout_stop(&sc->sc_cal_ch); + ATH_UNLOCK(sc); + + if (ostate == IEEE80211_S_CSA && nstate == IEEE80211_S_RUN) csa_run_transition = 1; - callout_drain(&sc->sc_cal_ch); ath_hal_setledstate(ah, leds[nstate]); /* set LED */ if (nstate == IEEE80211_S_SCAN) { @@ -4815,6 +5554,13 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) * [re]setup beacons. Unblock the task q thread so * deferred interrupt processing is done. */ + + /* Ensure we stay awake during scan */ + ATH_LOCK(sc); + ath_power_setselfgen(sc, HAL_PM_AWAKE); + ath_power_setpower(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + ath_hal_intrset(ah, sc->sc_imask &~ (HAL_INT_SWBA | HAL_INT_BMISS)); sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS); @@ -4822,11 +5568,16 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) taskqueue_unblock(sc->sc_tq); } - ni = vap->iv_bss; + ni = ieee80211_ref_node(vap->iv_bss); rfilt = ath_calcrxfilter(sc); stamode = (vap->iv_opmode == IEEE80211_M_STA || vap->iv_opmode == IEEE80211_M_AHDEMO || vap->iv_opmode == IEEE80211_M_IBSS); + + /* + * XXX Dont need to do this (and others) if we've transitioned + * from SLEEP->RUN. + */ if (stamode && nstate == IEEE80211_S_RUN) { sc->sc_curaid = ni->ni_associd; IEEE80211_ADDR_COPY(sc->sc_curbssid, ni->ni_bssid); @@ -4851,9 +5602,16 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) if (error != 0) goto bad; + /* + * See above: ensure av_newstate() doesn't drop the lock + * on us. + */ + IEEE80211_LOCK_ASSERT(ic); + if (nstate == IEEE80211_S_RUN) { /* NB: collect bss node again, it may have changed */ - ni = vap->iv_bss; + ieee80211_free_node(ni); + ni = ieee80211_ref_node(vap->iv_bss); DPRINTF(sc, ATH_DEBUG_STATE, "%s(RUN): iv_flags 0x%08x bintvl %d bssid %s " @@ -4915,10 +5673,36 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) * force a beacon update so we pick up a lack of * beacons from an AP in CAC and thus force a * scan. + * + * And, there's also corner cases here where + * after a scan, the AP may have disappeared. + * In that case, we may not receive an actual + * beacon to update the beacon timer and thus we + * won't get notified of the missing beacons. */ - sc->sc_syncbeacon = 1; - if (csa_run_transition) - ath_beacon_config(sc, vap); + if (ostate != IEEE80211_S_RUN && + ostate != IEEE80211_S_SLEEP) { + DPRINTF(sc, ATH_DEBUG_BEACON, + "%s: STA; syncbeacon=1\n", __func__); + sc->sc_syncbeacon = 1; + + if (csa_run_transition) + ath_beacon_config(sc, vap); + + /* + * PR: kern/175227 + * + * Reconfigure beacons during reset; as otherwise + * we won't get the beacon timers reprogrammed + * after a reset and thus we won't pick up a + * beacon miss interrupt. + * + * Hopefully we'll see a beacon before the BMISS + * timer fires (too often), leading to a STA + * disassociation. + */ + sc->sc_beacons = 1; + } break; case IEEE80211_M_MONITOR: /* @@ -4944,6 +5728,14 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) sc->sc_halstats.ns_avgbrssi = ATH_RSSI_DUMMY_MARKER; sc->sc_halstats.ns_avgrssi = ATH_RSSI_DUMMY_MARKER; sc->sc_halstats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER; + + /* + * Force awake for RUN mode. + */ + ATH_LOCK(sc); + ath_power_setselfgen(sc, HAL_PM_AWAKE); + ath_power_setpower(sc, HAL_PM_AWAKE); + /* * Finally, start any timers and the task q thread * (in case we didn't go through SCAN state). @@ -4955,6 +5747,8 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: calibration disabled\n", __func__); } + ATH_UNLOCK(sc); + taskqueue_unblock(sc->sc_tq); } else if (nstate == IEEE80211_S_INIT) { /* @@ -4974,8 +5768,43 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) #ifdef IEEE80211_SUPPORT_TDMA ath_hal_setcca(ah, AH_TRUE); #endif + } else if (nstate == IEEE80211_S_SLEEP) { + /* We're going to sleep, so transition appropriately */ + /* For now, only do this if we're a single STA vap */ + if (sc->sc_nvaps == 1 && + vap->iv_opmode == IEEE80211_M_STA) { + DPRINTF(sc, ATH_DEBUG_BEACON, "%s: syncbeacon=%d\n", __func__, sc->sc_syncbeacon); + ATH_LOCK(sc); + /* + * Always at least set the self-generated + * frame config to set PWRMGT=1. + */ + ath_power_setselfgen(sc, HAL_PM_NETWORK_SLEEP); + + /* + * If we're not syncing beacons, transition + * to NETWORK_SLEEP. + * + * We stay awake if syncbeacon > 0 in case + * we need to listen for some beacons otherwise + * our beacon timer config may be wrong. + */ + if (sc->sc_syncbeacon == 0) { + ath_power_setpower(sc, HAL_PM_NETWORK_SLEEP); + } + ATH_UNLOCK(sc); + } } bad: + ieee80211_free_node(ni); + + /* + * Restore the power state - either to what it was, or + * to network_sleep if it's alright. + */ + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); return error; } @@ -4991,9 +5820,10 @@ static void ath_setup_stationkey(struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; - struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; + struct ath_softc *sc = vap->iv_ic->ic_softc; ieee80211_keyix keyix, rxkeyix; + /* XXX should take a locked ref to vap->iv_bss */ if (!ath_key_alloc(vap, &ni->ni_ucastkey, &keyix, &rxkeyix)) { /* * Key cache is full; we'll fall back to doing @@ -5009,7 +5839,7 @@ ath_setup_stationkey(struct ieee80211_node *ni) ni->ni_ucastkey.wk_flags |= IEEE80211_KEY_DEVKEY; IEEE80211_ADDR_COPY(ni->ni_ucastkey.wk_macaddr, ni->ni_macaddr); /* NB: this will create a pass-thru key entry */ - ath_keyset(sc, &ni->ni_ucastkey, vap->iv_bss); + ath_keyset(sc, vap, &ni->ni_ucastkey, vap->iv_bss); } } @@ -5023,24 +5853,59 @@ ath_newassoc(struct ieee80211_node *ni, int isnew) { struct ath_node *an = ATH_NODE(ni); struct ieee80211vap *vap = ni->ni_vap; - struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; + struct ath_softc *sc = vap->iv_ic->ic_softc; const struct ieee80211_txparam *tp = ni->ni_txparms; an->an_mcastrix = ath_tx_findrix(sc, tp->mcastrate); an->an_mgmtrix = ath_tx_findrix(sc, tp->mgmtrate); + DPRINTF(sc, ATH_DEBUG_NODE, "%s: %6D: reassoc; isnew=%d, is_powersave=%d\n", + __func__, + ni->ni_macaddr, + ":", + isnew, + an->an_is_powersave); + + ATH_NODE_LOCK(an); ath_rate_newassoc(sc, an, isnew); + ATH_NODE_UNLOCK(an); + if (isnew && (vap->iv_flags & IEEE80211_F_PRIVACY) == 0 && sc->sc_hasclrkey && ni->ni_ucastkey.wk_keyix == IEEE80211_KEYIX_NONE) ath_setup_stationkey(ni); + + /* + * If we're reassociating, make sure that any paused queues + * get unpaused. + * + * Now, we may have frames in the hardware queue for this node. + * So if we are reassociating and there are frames in the queue, + * we need to go through the cleanup path to ensure that they're + * marked as non-aggregate. + */ + if (! isnew) { + DPRINTF(sc, ATH_DEBUG_NODE, + "%s: %6D: reassoc; is_powersave=%d\n", + __func__, + ni->ni_macaddr, + ":", + an->an_is_powersave); + + /* XXX for now, we can't hold the lock across assoc */ + ath_tx_node_reassoc(sc, an); + + /* XXX for now, we can't hold the lock across wakeup */ + if (an->an_is_powersave) + ath_tx_node_wakeup(sc, an); + } } static int ath_setregdomain(struct ieee80211com *ic, struct ieee80211_regdomain *reg, int nchans, struct ieee80211_channel chans[]) { - struct ath_softc *sc = ic->ic_ifp->if_softc; + struct ath_softc *sc = ic->ic_softc; struct ath_hal *ah = sc->sc_ah; HAL_STATUS status; @@ -5064,7 +5929,7 @@ static void ath_getradiocaps(struct ieee80211com *ic, int maxchans, int *nchans, struct ieee80211_channel chans[]) { - struct ath_softc *sc = ic->ic_ifp->if_softc; + struct ath_softc *sc = ic->ic_softc; struct ath_hal *ah = sc->sc_ah; DPRINTF(sc, ATH_DEBUG_REGDOMAIN, "%s: use rd %u cc %d\n", @@ -5079,8 +5944,7 @@ ath_getradiocaps(struct ieee80211com *ic, static int ath_getchannels(struct ath_softc *sc) { - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = sc->sc_ah; HAL_STATUS status; @@ -5090,8 +5954,9 @@ ath_getchannels(struct ath_softc *sc) status = ath_hal_init_channels(ah, ic->ic_channels, IEEE80211_CHAN_MAX, &ic->ic_nchans, HAL_MODE_ALL, CTRY_DEFAULT, SKU_NONE, AH_TRUE); if (status != HAL_OK) { - if_printf(ifp, "%s: unable to collect channel list from hal, " - "status %d\n", __func__, status); + device_printf(sc->sc_dev, + "%s: unable to collect channel list from hal, status %d\n", + __func__, status); return EINVAL; } (void) ath_hal_getregdomain(ah, &sc->sc_eerd); @@ -5114,49 +5979,6 @@ ath_getchannels(struct ath_softc *sc) return 0; } -static void -ath_led_done(void *arg) -{ - struct ath_softc *sc = arg; - - sc->sc_blinking = 0; -} - -/* - * Turn the LED off: flip the pin and then set a timer so no - * update will happen for the specified duration. - */ -static void -ath_led_off(void *arg) -{ - struct ath_softc *sc = arg; - - ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, !sc->sc_ledon); - callout_reset(&sc->sc_ledtimer, sc->sc_ledoff, ath_led_done, sc); -} - -/* - * Blink the LED according to the specified on/off times. - */ -static void -ath_led_blink(struct ath_softc *sc, int on, int off) -{ - DPRINTF(sc, ATH_DEBUG_LED, "%s: on %u off %u\n", __func__, on, off); - ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, sc->sc_ledon); - sc->sc_blinking = 1; - sc->sc_ledoff = off; - callout_reset(&sc->sc_ledtimer, on, ath_led_off, sc); -} - -static void -ath_led_event(struct ath_softc *sc, int rix) -{ - sc->sc_ledevent = ticks; /* time of last event */ - if (sc->sc_blinking) /* don't interrupt active blink */ - return; - ath_led_blink(sc, sc->sc_hwmap[rix].ledon, sc->sc_hwmap[rix].ledoff); -} - static int ath_rate_setup(struct ath_softc *sc, u_int mode) { @@ -5206,7 +6028,6 @@ ath_rate_setup(struct ath_softc *sc, u_int mode) static void ath_setcurmode(struct ath_softc *sc, enum ieee80211_phymode mode) { -#define N(a) (sizeof(a)/sizeof(a[0])) /* NB: on/off times from the Atheros NDIS driver, w/ permission */ static const struct { u_int rate; /* tx/rx 802.11 rate */ @@ -5243,7 +6064,7 @@ ath_setcurmode(struct ath_softc *sc, enum ieee80211_phymode mode) sc->sc_rixmap[ieeerate | IEEE80211_RATE_MCS] = i; } memset(sc->sc_hwmap, 0, sizeof(sc->sc_hwmap)); - for (i = 0; i < N(sc->sc_hwmap); i++) { + for (i = 0; i < nitems(sc->sc_hwmap); i++) { if (i >= rt->rateCount) { sc->sc_hwmap[i].ledon = (500 * hz) / 1000; sc->sc_hwmap[i].ledoff = (130 * hz) / 1000; @@ -5258,7 +6079,7 @@ ath_setcurmode(struct ath_softc *sc, enum ieee80211_phymode mode) rt->info[i].phy == IEEE80211_T_OFDM) sc->sc_hwmap[i].txflags |= IEEE80211_RADIOTAP_F_SHORTPRE; sc->sc_hwmap[i].rxflags = sc->sc_hwmap[i].txflags; - for (j = 0; j < N(blinkrates)-1; j++) + for (j = 0; j < nitems(blinkrates)-1; j++) if (blinkrates[j].rate == sc->sc_hwmap[i].ieeerate) break; /* NB: this uses the last entry if the rate isn't found */ @@ -5269,7 +6090,7 @@ ath_setcurmode(struct ath_softc *sc, enum ieee80211_phymode mode) sc->sc_currates = rt; sc->sc_curmode = mode; /* - * All protection frames are transmited at 2Mb/s for + * All protection frames are transmitted at 2Mb/s for * 11g, otherwise at 1Mb/s. */ if (mode == IEEE80211_MODE_11G) @@ -5277,116 +6098,66 @@ ath_setcurmode(struct ath_softc *sc, enum ieee80211_phymode mode) else sc->sc_protrix = ath_tx_findrix(sc, 2*1); /* NB: caller is responsible for resetting rate control state */ -#undef N } static void ath_watchdog(void *arg) { struct ath_softc *sc = arg; + struct ieee80211com *ic = &sc->sc_ic; + int do_reset = 0; + + ATH_LOCK_ASSERT(sc); if (sc->sc_wd_timer != 0 && --sc->sc_wd_timer == 0) { - struct ifnet *ifp = sc->sc_ifp; uint32_t hangs; + ath_power_set_power_state(sc, HAL_PM_AWAKE); + if (ath_hal_gethangstate(sc->sc_ah, 0xffff, &hangs) && hangs != 0) { - if_printf(ifp, "%s hang detected (0x%x)\n", + device_printf(sc->sc_dev, "%s hang detected (0x%x)\n", hangs & 0xff ? "bb" : "mac", hangs); } else - if_printf(ifp, "device timeout\n"); - ath_reset(ifp); - ifp->if_oerrors++; + device_printf(sc->sc_dev, "device timeout\n"); + do_reset = 1; + counter_u64_add(ic->ic_oerrors, 1); sc->sc_stats.ast_watchdog++; + + ath_power_restore_power_state(sc); } + + /* + * We can't hold the lock across the ath_reset() call. + * + * And since this routine can't hold a lock and sleep, + * do the reset deferred. + */ + if (do_reset) { + taskqueue_enqueue(sc->sc_tq, &sc->sc_resettask); + } + callout_schedule(&sc->sc_wd_ch, hz); } -#ifdef ATH_DIAGAPI -/* - * Diagnostic interface to the HAL. This is used by various - * tools to do things like retrieve register contents for - * debugging. The mechanism is intentionally opaque so that - * it can change frequently w/o concern for compatiblity. - */ -static int -ath_ioctl_diag(struct ath_softc *sc, struct ath_diag *ad) +static void +ath_parent(struct ieee80211com *ic) { - struct ath_hal *ah = sc->sc_ah; - u_int id = ad->ad_id & ATH_DIAG_ID; - void *indata = NULL; - void *outdata = NULL; - u_int32_t insize = ad->ad_in_size; - u_int32_t outsize = ad->ad_out_size; - int error = 0; + struct ath_softc *sc = ic->ic_softc; + int error = EDOOFUS; - if (ad->ad_id & ATH_DIAG_IN) { + ATH_LOCK(sc); + if (ic->ic_nrunning > 0) { /* - * Copy in data. + * To avoid rescanning another access point, + * do not call ath_init() here. Instead, + * only reflect promisc mode settings. */ - indata = malloc(insize, M_TEMP, M_NOWAIT); - if (indata == NULL) { - error = ENOMEM; - goto bad; - } - error = copyin(ad->ad_in_data, indata, insize); - if (error) - goto bad; - } - if (ad->ad_id & ATH_DIAG_DYN) { - /* - * Allocate a buffer for the results (otherwise the HAL - * returns a pointer to a buffer where we can read the - * results). Note that we depend on the HAL leaving this - * pointer for us to use below in reclaiming the buffer; - * may want to be more defensive. - */ - outdata = malloc(outsize, M_TEMP, M_NOWAIT); - if (outdata == NULL) { - error = ENOMEM; - goto bad; - } - } - if (ath_hal_getdiagstate(ah, id, indata, insize, &outdata, &outsize)) { - if (outsize < ad->ad_out_size) - ad->ad_out_size = outsize; - if (outdata != NULL) - error = copyout(outdata, ad->ad_out_data, - ad->ad_out_size); - } else { - error = EINVAL; - } -bad: - if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL) - free(indata, M_TEMP); - if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL) - free(outdata, M_TEMP); - return error; -} -#endif /* ATH_DIAGAPI */ - -static int -ath_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) -{ -#define IS_RUNNING(ifp) \ - ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING)) - struct ath_softc *sc = ifp->if_softc; - struct ieee80211com *ic = ifp->if_l2com; - struct ifreq *ifr = (struct ifreq *)data; - const HAL_RATE_TABLE *rt; - int error = 0; - - switch (cmd) { - case SIOCSIFFLAGS: - ATH_LOCK(sc); - if (IS_RUNNING(ifp)) { - /* - * To avoid rescanning another access point, - * do not call ath_init() here. Instead, - * only reflect promisc mode settings. - */ + if (sc->sc_running) { + ath_power_set_power_state(sc, HAL_PM_AWAKE); ath_mode_init(sc); - } else if (ifp->if_flags & IFF_UP) { + ath_power_restore_power_state(sc); + } else if (!sc->sc_invalid) { /* * Beware of being called during attach/detach * to reset promiscuous mode. In that case we @@ -5396,61 +6167,23 @@ ath_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) * torn down much of our state. There's * probably a better way to deal with this. */ - if (!sc->sc_invalid) - ath_init(sc); /* XXX lose error */ - } else { - ath_stop_locked(ifp); -#ifdef notyet - /* XXX must wakeup in places like ath_vap_delete */ - if (!sc->sc_invalid) - ath_hal_setpower(sc->sc_ah, HAL_PM_FULL_SLEEP); -#endif + error = ath_init(sc); } - ATH_UNLOCK(sc); - break; - case SIOCGIFMEDIA: - case SIOCSIFMEDIA: - error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); - break; - case SIOCGATHSTATS: - /* NB: embed these numbers to get a consistent view */ - sc->sc_stats.ast_tx_packets = ifp->if_opackets; - sc->sc_stats.ast_rx_packets = ifp->if_ipackets; - sc->sc_stats.ast_tx_rssi = ATH_RSSI(sc->sc_halstats.ns_avgtxrssi); - sc->sc_stats.ast_rx_rssi = ATH_RSSI(sc->sc_halstats.ns_avgrssi); -#ifdef IEEE80211_SUPPORT_TDMA - sc->sc_stats.ast_tdma_tsfadjp = TDMA_AVG(sc->sc_avgtsfdeltap); - sc->sc_stats.ast_tdma_tsfadjm = TDMA_AVG(sc->sc_avgtsfdeltam); -#endif - rt = sc->sc_currates; - sc->sc_stats.ast_tx_rate = - rt->info[sc->sc_txrix].dot11Rate &~ IEEE80211_RATE_BASIC; - if (rt->info[sc->sc_txrix].phy & IEEE80211_T_HT) - sc->sc_stats.ast_tx_rate |= IEEE80211_RATE_MCS; - return copyout(&sc->sc_stats, - ifr->ifr_data, sizeof (sc->sc_stats)); - case SIOCZATHSTATS: - error = priv_check(curthread, PRIV_DRIVER); - if (error == 0) - memset(&sc->sc_stats, 0, sizeof(sc->sc_stats)); - break; -#ifdef ATH_DIAGAPI - case SIOCGATHDIAG: - error = ath_ioctl_diag(sc, (struct ath_diag *) ifr); - break; - case SIOCGATHPHYERR: - error = ath_ioctl_phyerr(sc,(struct ath_diag*) ifr); - break; -#endif - case SIOCGIFADDR: - error = ether_ioctl(ifp, cmd, data); - break; - default: - error = EINVAL; - break; + } else { + ath_stop(sc); + if (!sc->sc_invalid) + ath_power_setpower(sc, HAL_PM_FULL_SLEEP); + } + ATH_UNLOCK(sc); + + if (error == 0) { +#ifdef ATH_TX99_DIAG + if (sc->sc_tx99 != NULL) + sc->sc_tx99->start(sc->sc_tx99); + else +#endif + ieee80211_start_all(ic); } - return error; -#undef IS_RUNNING } /* @@ -5459,386 +6192,39 @@ ath_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) static void ath_announce(struct ath_softc *sc) { - struct ifnet *ifp = sc->sc_ifp; struct ath_hal *ah = sc->sc_ah; - if_printf(ifp, "AR%s mac %d.%d RF%s phy %d.%d\n", + device_printf(sc->sc_dev, "%s mac %d.%d RF%s phy %d.%d\n", ath_hal_mac_name(ah), ah->ah_macVersion, ah->ah_macRev, ath_hal_rf_name(ah), ah->ah_phyRev >> 4, ah->ah_phyRev & 0xf); + device_printf(sc->sc_dev, "2GHz radio: 0x%.4x; 5GHz radio: 0x%.4x\n", + ah->ah_analog2GhzRev, ah->ah_analog5GhzRev); if (bootverbose) { int i; for (i = 0; i <= WME_AC_VO; i++) { struct ath_txq *txq = sc->sc_ac2q[i]; - if_printf(ifp, "Use hw queue %u for %s traffic\n", - txq->axq_qnum, ieee80211_wme_acnames[i]); + device_printf(sc->sc_dev, + "Use hw queue %u for %s traffic\n", + txq->axq_qnum, ieee80211_wme_acnames[i]); } - if_printf(ifp, "Use hw queue %u for CAB traffic\n", - sc->sc_cabq->axq_qnum); - if_printf(ifp, "Use hw queue %u for beacons\n", sc->sc_bhalq); + device_printf(sc->sc_dev, "Use hw queue %u for CAB traffic\n", + sc->sc_cabq->axq_qnum); + device_printf(sc->sc_dev, "Use hw queue %u for beacons\n", + sc->sc_bhalq); } if (ath_rxbuf != ATH_RXBUF) - if_printf(ifp, "using %u rx buffers\n", ath_rxbuf); + device_printf(sc->sc_dev, "using %u rx buffers\n", ath_rxbuf); if (ath_txbuf != ATH_TXBUF) - if_printf(ifp, "using %u tx buffers\n", ath_txbuf); + device_printf(sc->sc_dev, "using %u tx buffers\n", ath_txbuf); if (sc->sc_mcastkey && bootverbose) - if_printf(ifp, "using multicast key search\n"); + device_printf(sc->sc_dev, "using multicast key search\n"); } -#ifdef IEEE80211_SUPPORT_TDMA -static void -ath_tdma_settimers(struct ath_softc *sc, u_int32_t nexttbtt, u_int32_t bintval) -{ - struct ath_hal *ah = sc->sc_ah; - HAL_BEACON_TIMERS bt; - - bt.bt_intval = bintval | HAL_BEACON_ENA; - bt.bt_nexttbtt = nexttbtt; - bt.bt_nextdba = (nexttbtt<<3) - sc->sc_tdmadbaprep; - bt.bt_nextswba = (nexttbtt<<3) - sc->sc_tdmaswbaprep; - bt.bt_nextatim = nexttbtt+1; - /* Enables TBTT, DBA, SWBA timers by default */ - bt.bt_flags = 0; - ath_hal_beaconsettimers(ah, &bt); -} - -/* - * Calculate the beacon interval. This is periodic in the - * superframe for the bss. We assume each station is configured - * identically wrt transmit rate so the guard time we calculate - * above will be the same on all stations. Note we need to - * factor in the xmit time because the hardware will schedule - * a frame for transmit if the start of the frame is within - * the burst time. When we get hardware that properly kills - * frames in the PCU we can reduce/eliminate the guard time. - * - * Roundup to 1024 is so we have 1 TU buffer in the guard time - * to deal with the granularity of the nexttbtt timer. 11n MAC's - * with 1us timer granularity should allow us to reduce/eliminate - * this. - */ -static void -ath_tdma_bintvalsetup(struct ath_softc *sc, - const struct ieee80211_tdma_state *tdma) -{ - /* copy from vap state (XXX check all vaps have same value?) */ - sc->sc_tdmaslotlen = tdma->tdma_slotlen; - - sc->sc_tdmabintval = roundup((sc->sc_tdmaslotlen+sc->sc_tdmaguard) * - tdma->tdma_slotcnt, 1024); - sc->sc_tdmabintval >>= 10; /* TSF -> TU */ - if (sc->sc_tdmabintval & 1) - sc->sc_tdmabintval++; - - if (tdma->tdma_slot == 0) { - /* - * Only slot 0 beacons; other slots respond. - */ - sc->sc_imask |= HAL_INT_SWBA; - sc->sc_tdmaswba = 0; /* beacon immediately */ - } else { - /* XXX all vaps must be slot 0 or slot !0 */ - sc->sc_imask &= ~HAL_INT_SWBA; - } -} - -/* - * Max 802.11 overhead. This assumes no 4-address frames and - * the encapsulation done by ieee80211_encap (llc). We also - * include potential crypto overhead. - */ -#define IEEE80211_MAXOVERHEAD \ - (sizeof(struct ieee80211_qosframe) \ - + sizeof(struct llc) \ - + IEEE80211_ADDR_LEN \ - + IEEE80211_WEP_IVLEN \ - + IEEE80211_WEP_KIDLEN \ - + IEEE80211_WEP_CRCLEN \ - + IEEE80211_WEP_MICLEN \ - + IEEE80211_CRC_LEN) - -/* - * Setup initially for tdma operation. Start the beacon - * timers and enable SWBA if we are slot 0. Otherwise - * we wait for slot 0 to arrive so we can sync up before - * starting to transmit. - */ -static void -ath_tdma_config(struct ath_softc *sc, struct ieee80211vap *vap) -{ - struct ath_hal *ah = sc->sc_ah; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - const struct ieee80211_txparam *tp; - const struct ieee80211_tdma_state *tdma = NULL; - int rix; - - if (vap == NULL) { - vap = TAILQ_FIRST(&ic->ic_vaps); /* XXX */ - if (vap == NULL) { - if_printf(ifp, "%s: no vaps?\n", __func__); - return; - } - } - tp = vap->iv_bss->ni_txparms; - /* - * Calculate the guard time for each slot. This is the - * time to send a maximal-size frame according to the - * fixed/lowest transmit rate. Note that the interface - * mtu does not include the 802.11 overhead so we must - * tack that on (ath_hal_computetxtime includes the - * preamble and plcp in it's calculation). - */ - tdma = vap->iv_tdma; - if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) - rix = ath_tx_findrix(sc, tp->ucastrate); - else - rix = ath_tx_findrix(sc, tp->mcastrate); - /* XXX short preamble assumed */ - sc->sc_tdmaguard = ath_hal_computetxtime(ah, sc->sc_currates, - ifp->if_mtu + IEEE80211_MAXOVERHEAD, rix, AH_TRUE); - - ath_hal_intrset(ah, 0); - - ath_beaconq_config(sc); /* setup h/w beacon q */ - if (sc->sc_setcca) - ath_hal_setcca(ah, AH_FALSE); /* disable CCA */ - ath_tdma_bintvalsetup(sc, tdma); /* calculate beacon interval */ - ath_tdma_settimers(sc, sc->sc_tdmabintval, - sc->sc_tdmabintval | HAL_BEACON_RESET_TSF); - sc->sc_syncbeacon = 0; - - sc->sc_avgtsfdeltap = TDMA_DUMMY_MARKER; - sc->sc_avgtsfdeltam = TDMA_DUMMY_MARKER; - - ath_hal_intrset(ah, sc->sc_imask); - - DPRINTF(sc, ATH_DEBUG_TDMA, "%s: slot %u len %uus cnt %u " - "bsched %u guard %uus bintval %u TU dba prep %u\n", __func__, - tdma->tdma_slot, tdma->tdma_slotlen, tdma->tdma_slotcnt, - tdma->tdma_bintval, sc->sc_tdmaguard, sc->sc_tdmabintval, - sc->sc_tdmadbaprep); -} - -/* - * Update tdma operation. Called from the 802.11 layer - * when a beacon is received from the TDMA station operating - * in the slot immediately preceding us in the bss. Use - * the rx timestamp for the beacon frame to update our - * beacon timers so we follow their schedule. Note that - * by using the rx timestamp we implicitly include the - * propagation delay in our schedule. - */ -static void -ath_tdma_update(struct ieee80211_node *ni, - const struct ieee80211_tdma_param *tdma, int changed) -{ -#define TSF_TO_TU(_h,_l) \ - ((((u_int32_t)(_h)) << 22) | (((u_int32_t)(_l)) >> 10)) -#define TU_TO_TSF(_tu) (((u_int64_t)(_tu)) << 10) - struct ieee80211vap *vap = ni->ni_vap; - struct ieee80211com *ic = ni->ni_ic; - struct ath_softc *sc = ic->ic_ifp->if_softc; - struct ath_hal *ah = sc->sc_ah; - const HAL_RATE_TABLE *rt = sc->sc_currates; - u_int64_t tsf, rstamp, nextslot, nexttbtt; - u_int32_t txtime, nextslottu; - int32_t tudelta, tsfdelta; - const struct ath_rx_status *rs; - int rix; - - sc->sc_stats.ast_tdma_update++; - - /* - * Check for and adopt configuration changes. - */ - if (changed != 0) { - const struct ieee80211_tdma_state *ts = vap->iv_tdma; - - ath_tdma_bintvalsetup(sc, ts); - if (changed & TDMA_UPDATE_SLOTLEN) - ath_wme_update(ic); - - DPRINTF(sc, ATH_DEBUG_TDMA, - "%s: adopt slot %u slotcnt %u slotlen %u us " - "bintval %u TU\n", __func__, - ts->tdma_slot, ts->tdma_slotcnt, ts->tdma_slotlen, - sc->sc_tdmabintval); - - /* XXX right? */ - ath_hal_intrset(ah, sc->sc_imask); - /* NB: beacon timers programmed below */ - } - - /* extend rx timestamp to 64 bits */ - rs = sc->sc_lastrs; - tsf = ath_hal_gettsf64(ah); - rstamp = ath_extend_tsf(sc, rs->rs_tstamp, tsf); - /* - * The rx timestamp is set by the hardware on completing - * reception (at the point where the rx descriptor is DMA'd - * to the host). To find the start of our next slot we - * must adjust this time by the time required to send - * the packet just received. - */ - rix = rt->rateCodeToIndex[rs->rs_rate]; - txtime = ath_hal_computetxtime(ah, rt, rs->rs_datalen, rix, - rt->info[rix].shortPreamble); - /* NB: << 9 is to cvt to TU and /2 */ - nextslot = (rstamp - txtime) + (sc->sc_tdmabintval << 9); - nextslottu = TSF_TO_TU(nextslot>>32, nextslot) & HAL_BEACON_PERIOD; - - /* - * Retrieve the hardware NextTBTT in usecs - * and calculate the difference between what the - * other station thinks and what we have programmed. This - * lets us figure how to adjust our timers to match. The - * adjustments are done by pulling the TSF forward and possibly - * rewriting the beacon timers. - */ - nexttbtt = ath_hal_getnexttbtt(ah); - tsfdelta = (int32_t)((nextslot % TU_TO_TSF(HAL_BEACON_PERIOD + 1)) - nexttbtt); - - DPRINTF(sc, ATH_DEBUG_TDMA_TIMER, - "tsfdelta %d avg +%d/-%d\n", tsfdelta, - TDMA_AVG(sc->sc_avgtsfdeltap), TDMA_AVG(sc->sc_avgtsfdeltam)); - - if (tsfdelta < 0) { - TDMA_SAMPLE(sc->sc_avgtsfdeltap, 0); - TDMA_SAMPLE(sc->sc_avgtsfdeltam, -tsfdelta); - tsfdelta = -tsfdelta % 1024; - nextslottu++; - } else if (tsfdelta > 0) { - TDMA_SAMPLE(sc->sc_avgtsfdeltap, tsfdelta); - TDMA_SAMPLE(sc->sc_avgtsfdeltam, 0); - tsfdelta = 1024 - (tsfdelta % 1024); - nextslottu++; - } else { - TDMA_SAMPLE(sc->sc_avgtsfdeltap, 0); - TDMA_SAMPLE(sc->sc_avgtsfdeltam, 0); - } - tudelta = nextslottu - TSF_TO_TU(nexttbtt >> 32, nexttbtt); - - /* - * Copy sender's timetstamp into tdma ie so they can - * calculate roundtrip time. We submit a beacon frame - * below after any timer adjustment. The frame goes out - * at the next TBTT so the sender can calculate the - * roundtrip by inspecting the tdma ie in our beacon frame. - * - * NB: This tstamp is subtlely preserved when - * IEEE80211_BEACON_TDMA is marked (e.g. when the - * slot position changes) because ieee80211_add_tdma - * skips over the data. - */ - memcpy(ATH_VAP(vap)->av_boff.bo_tdma + - __offsetof(struct ieee80211_tdma_param, tdma_tstamp), - &ni->ni_tstamp.data, 8); -#if 0 - DPRINTF(sc, ATH_DEBUG_TDMA_TIMER, - "tsf %llu nextslot %llu (%d, %d) nextslottu %u nexttbtt %llu (%d)\n", - (unsigned long long) tsf, (unsigned long long) nextslot, - (int)(nextslot - tsf), tsfdelta, nextslottu, nexttbtt, tudelta); -#endif - /* - * Adjust the beacon timers only when pulling them forward - * or when going back by less than the beacon interval. - * Negative jumps larger than the beacon interval seem to - * cause the timers to stop and generally cause instability. - * This basically filters out jumps due to missed beacons. - */ - if (tudelta != 0 && (tudelta > 0 || -tudelta < sc->sc_tdmabintval)) { - ath_tdma_settimers(sc, nextslottu, sc->sc_tdmabintval); - sc->sc_stats.ast_tdma_timers++; - } - if (tsfdelta > 0) { - ath_hal_adjusttsf(ah, tsfdelta); - sc->sc_stats.ast_tdma_tsf++; - } - ath_tdma_beacon_send(sc, vap); /* prepare response */ -#undef TU_TO_TSF -#undef TSF_TO_TU -} - -/* - * Transmit a beacon frame at SWBA. Dynamic updates - * to the frame contents are done as needed. - */ -static void -ath_tdma_beacon_send(struct ath_softc *sc, struct ieee80211vap *vap) -{ - struct ath_hal *ah = sc->sc_ah; - struct ath_buf *bf; - int otherant; - - /* - * Check if the previous beacon has gone out. If - * not don't try to post another, skip this period - * and wait for the next. Missed beacons indicate - * a problem and should not occur. If we miss too - * many consecutive beacons reset the device. - */ - if (ath_hal_numtxpending(ah, sc->sc_bhalq) != 0) { - sc->sc_bmisscount++; - DPRINTF(sc, ATH_DEBUG_BEACON, - "%s: missed %u consecutive beacons\n", - __func__, sc->sc_bmisscount); - if (sc->sc_bmisscount >= ath_bstuck_threshold) - taskqueue_enqueue(sc->sc_tq, &sc->sc_bstucktask); - return; - } - if (sc->sc_bmisscount != 0) { - DPRINTF(sc, ATH_DEBUG_BEACON, - "%s: resume beacon xmit after %u misses\n", - __func__, sc->sc_bmisscount); - sc->sc_bmisscount = 0; - } - - /* - * Check recent per-antenna transmit statistics and flip - * the default antenna if noticeably more frames went out - * on the non-default antenna. - * XXX assumes 2 anntenae - */ - if (!sc->sc_diversity) { - otherant = sc->sc_defant & 1 ? 2 : 1; - if (sc->sc_ant_tx[otherant] > sc->sc_ant_tx[sc->sc_defant] + 2) - ath_setdefantenna(sc, otherant); - sc->sc_ant_tx[1] = sc->sc_ant_tx[2] = 0; - } - - bf = ath_beacon_generate(sc, vap); - if (bf != NULL) { - /* - * Stop any current dma and put the new frame on the queue. - * This should never fail since we check above that no frames - * are still pending on the queue. - */ - if (!ath_hal_stoptxdma(ah, sc->sc_bhalq)) { - DPRINTF(sc, ATH_DEBUG_ANY, - "%s: beacon queue %u did not stop?\n", - __func__, sc->sc_bhalq); - /* NB: the HAL still stops DMA, so proceed */ - } - ath_hal_puttxbuf(ah, sc->sc_bhalq, bf->bf_daddr); - ath_hal_txstart(ah, sc->sc_bhalq); - - sc->sc_stats.ast_be_xmit++; /* XXX per-vap? */ - - /* - * Record local TSF for our last send for use - * in arbitrating slot collisions. - */ - vap->iv_bss->ni_tstamp.tsf = ath_hal_gettsf64(ah); - } -} -#endif /* IEEE80211_SUPPORT_TDMA */ - static void ath_dfs_tasklet(void *p, int npending) { struct ath_softc *sc = (struct ath_softc *) p; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211com *ic = &sc->sc_ic; /* * If previous processing has found a radar event, @@ -5847,9 +6233,434 @@ ath_dfs_tasklet(void *p, int npending) */ if (ath_dfs_process_radar_event(sc, sc->sc_curchan)) { /* DFS event found, initiate channel change */ + /* + * XXX doesn't currently tell us whether the event + * XXX was found in the primary or extension + * XXX channel! + */ + IEEE80211_LOCK(ic); ieee80211_dfs_notify_radar(ic, sc->sc_curchan); + IEEE80211_UNLOCK(ic); } } +/* + * Enable/disable power save. This must be called with + * no TX driver locks currently held, so it should only + * be called from the RX path (which doesn't hold any + * TX driver locks.) + */ +static void +ath_node_powersave(struct ieee80211_node *ni, int enable) +{ +#ifdef ATH_SW_PSQ + struct ath_node *an = ATH_NODE(ni); + struct ieee80211com *ic = ni->ni_ic; + struct ath_softc *sc = ic->ic_softc; + struct ath_vap *avp = ATH_VAP(ni->ni_vap); + + /* XXX and no TXQ locks should be held here */ + + DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, "%s: %6D: enable=%d\n", + __func__, + ni->ni_macaddr, + ":", + !! enable); + + /* Suspend or resume software queue handling */ + if (enable) + ath_tx_node_sleep(sc, an); + else + ath_tx_node_wakeup(sc, an); + + /* Update net80211 state */ + avp->av_node_ps(ni, enable); +#else + struct ath_vap *avp = ATH_VAP(ni->ni_vap); + + /* Update net80211 state */ + avp->av_node_ps(ni, enable); +#endif/* ATH_SW_PSQ */ +} + +/* + * Notification from net80211 that the powersave queue state has + * changed. + * + * Since the software queue also may have some frames: + * + * + if the node software queue has frames and the TID state + * is 0, we set the TIM; + * + if the node and the stack are both empty, we clear the TIM bit. + * + If the stack tries to set the bit, always set it. + * + If the stack tries to clear the bit, only clear it if the + * software queue in question is also cleared. + * + * TODO: this is called during node teardown; so let's ensure this + * is all correctly handled and that the TIM bit is cleared. + * It may be that the node flush is called _AFTER_ the net80211 + * stack clears the TIM. + * + * Here is the racy part. Since it's possible >1 concurrent, + * overlapping TXes will appear complete with a TX completion in + * another thread, it's possible that the concurrent TIM calls will + * clash. We can't hold the node lock here because setting the + * TIM grabs the net80211 comlock and this may cause a LOR. + * The solution is either to totally serialise _everything_ at + * this point (ie, all TX, completion and any reset/flush go into + * one taskqueue) or a new "ath TIM lock" needs to be created that + * just wraps the driver state change and this call to avp->av_set_tim(). + * + * The same race exists in the net80211 power save queue handling + * as well. Since multiple transmitting threads may queue frames + * into the driver, as well as ps-poll and the driver transmitting + * frames (and thus clearing the psq), it's quite possible that + * a packet entering the PSQ and a ps-poll being handled will + * race, causing the TIM to be cleared and not re-set. + */ +static int +ath_node_set_tim(struct ieee80211_node *ni, int enable) +{ +#ifdef ATH_SW_PSQ + struct ieee80211com *ic = ni->ni_ic; + struct ath_softc *sc = ic->ic_softc; + struct ath_node *an = ATH_NODE(ni); + struct ath_vap *avp = ATH_VAP(ni->ni_vap); + int changed = 0; + + ATH_TX_LOCK(sc); + an->an_stack_psq = enable; + + /* + * This will get called for all operating modes, + * even if avp->av_set_tim is unset. + * It's currently set for hostap/ibss modes; but + * the same infrastructure is used for both STA + * and AP/IBSS node power save. + */ + if (avp->av_set_tim == NULL) { + ATH_TX_UNLOCK(sc); + return (0); + } + + /* + * If setting the bit, always set it here. + * If clearing the bit, only clear it if the + * software queue is also empty. + * + * If the node has left power save, just clear the TIM + * bit regardless of the state of the power save queue. + * + * XXX TODO: although atomics are used, it's quite possible + * that a race will occur between this and setting/clearing + * in another thread. TX completion will occur always in + * one thread, however setting/clearing the TIM bit can come + * from a variety of different process contexts! + */ + if (enable && an->an_tim_set == 1) { + DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, + "%s: %6D: enable=%d, tim_set=1, ignoring\n", + __func__, + ni->ni_macaddr, + ":", + enable); + ATH_TX_UNLOCK(sc); + } else if (enable) { + DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, + "%s: %6D: enable=%d, enabling TIM\n", + __func__, + ni->ni_macaddr, + ":", + enable); + an->an_tim_set = 1; + ATH_TX_UNLOCK(sc); + changed = avp->av_set_tim(ni, enable); + } else if (an->an_swq_depth == 0) { + /* disable */ + DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, + "%s: %6D: enable=%d, an_swq_depth == 0, disabling\n", + __func__, + ni->ni_macaddr, + ":", + enable); + an->an_tim_set = 0; + ATH_TX_UNLOCK(sc); + changed = avp->av_set_tim(ni, enable); + } else if (! an->an_is_powersave) { + /* + * disable regardless; the node isn't in powersave now + */ + DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, + "%s: %6D: enable=%d, an_pwrsave=0, disabling\n", + __func__, + ni->ni_macaddr, + ":", + enable); + an->an_tim_set = 0; + ATH_TX_UNLOCK(sc); + changed = avp->av_set_tim(ni, enable); + } else { + /* + * psq disable, node is currently in powersave, node + * software queue isn't empty, so don't clear the TIM bit + * for now. + */ + ATH_TX_UNLOCK(sc); + DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, + "%s: %6D: enable=%d, an_swq_depth > 0, ignoring\n", + __func__, + ni->ni_macaddr, + ":", + enable); + changed = 0; + } + + return (changed); +#else + struct ath_vap *avp = ATH_VAP(ni->ni_vap); + + /* + * Some operating modes don't set av_set_tim(), so don't + * update it here. + */ + if (avp->av_set_tim == NULL) + return (0); + + return (avp->av_set_tim(ni, enable)); +#endif /* ATH_SW_PSQ */ +} + +/* + * Set or update the TIM from the software queue. + * + * Check the software queue depth before attempting to do lock + * anything; that avoids trying to obtain the lock. Then, + * re-check afterwards to ensure nothing has changed in the + * meantime. + * + * set: This is designed to be called from the TX path, after + * a frame has been queued; to see if the swq > 0. + * + * clear: This is designed to be called from the buffer completion point + * (right now it's ath_tx_default_comp()) where the state of + * a software queue has changed. + * + * It makes sense to place it at buffer free / completion rather + * than after each software queue operation, as there's no real + * point in churning the TIM bit as the last frames in the software + * queue are transmitted. If they fail and we retry them, we'd + * just be setting the TIM bit again anyway. + */ +void +ath_tx_update_tim(struct ath_softc *sc, struct ieee80211_node *ni, + int enable) +{ +#ifdef ATH_SW_PSQ + struct ath_node *an; + struct ath_vap *avp; + + /* Don't do this for broadcast/etc frames */ + if (ni == NULL) + return; + + an = ATH_NODE(ni); + avp = ATH_VAP(ni->ni_vap); + + /* + * And for operating modes without the TIM handler set, let's + * just skip those. + */ + if (avp->av_set_tim == NULL) + return; + + ATH_TX_LOCK_ASSERT(sc); + + if (enable) { + if (an->an_is_powersave && + an->an_tim_set == 0 && + an->an_swq_depth != 0) { + DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, + "%s: %6D: swq_depth>0, tim_set=0, set!\n", + __func__, + ni->ni_macaddr, + ":"); + an->an_tim_set = 1; + (void) avp->av_set_tim(ni, 1); + } + } else { + /* + * Don't bother grabbing the lock unless the queue is empty. + */ + if (an->an_swq_depth != 0) + return; + + if (an->an_is_powersave && + an->an_stack_psq == 0 && + an->an_tim_set == 1 && + an->an_swq_depth == 0) { + DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, + "%s: %6D: swq_depth=0, tim_set=1, psq_set=0," + " clear!\n", + __func__, + ni->ni_macaddr, + ":"); + an->an_tim_set = 0; + (void) avp->av_set_tim(ni, 0); + } + } +#else + return; +#endif /* ATH_SW_PSQ */ +} + +/* + * Received a ps-poll frame from net80211. + * + * Here we get a chance to serve out a software-queued frame ourselves + * before we punt it to net80211 to transmit us one itself - either + * because there's traffic in the net80211 psq, or a NULL frame to + * indicate there's nothing else. + */ +static void +ath_node_recv_pspoll(struct ieee80211_node *ni, struct mbuf *m) +{ +#ifdef ATH_SW_PSQ + struct ath_node *an; + struct ath_vap *avp; + struct ieee80211com *ic = ni->ni_ic; + struct ath_softc *sc = ic->ic_softc; + int tid; + + /* Just paranoia */ + if (ni == NULL) + return; + + /* + * Unassociated (temporary node) station. + */ + if (ni->ni_associd == 0) + return; + + /* + * We do have an active node, so let's begin looking into it. + */ + an = ATH_NODE(ni); + avp = ATH_VAP(ni->ni_vap); + + /* + * For now, we just call the original ps-poll method. + * Once we're ready to flip this on: + * + * + Set leak to 1, as no matter what we're going to have + * to send a frame; + * + Check the software queue and if there's something in it, + * schedule the highest TID thas has traffic from this node. + * Then make sure we schedule the software scheduler to + * run so it picks up said frame. + * + * That way whatever happens, we'll at least send _a_ frame + * to the given node. + * + * Again, yes, it's crappy QoS if the node has multiple + * TIDs worth of traffic - but let's get it working first + * before we optimise it. + * + * Also yes, there's definitely latency here - we're not + * direct dispatching to the hardware in this path (and + * we're likely being called from the packet receive path, + * so going back into TX may be a little hairy!) but again + * I'd like to get this working first before optimising + * turn-around time. + */ + + ATH_TX_LOCK(sc); + + /* + * Legacy - we're called and the node isn't asleep. + * Immediately punt. + */ + if (! an->an_is_powersave) { + DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, + "%s: %6D: not in powersave?\n", + __func__, + ni->ni_macaddr, + ":"); + ATH_TX_UNLOCK(sc); + avp->av_recv_pspoll(ni, m); + return; + } + + /* + * We're in powersave. + * + * Leak a frame. + */ + an->an_leak_count = 1; + + /* + * Now, if there's no frames in the node, just punt to + * recv_pspoll. + * + * Don't bother checking if the TIM bit is set, we really + * only care if there are any frames here! + */ + if (an->an_swq_depth == 0) { + ATH_TX_UNLOCK(sc); + DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, + "%s: %6D: SWQ empty; punting to net80211\n", + __func__, + ni->ni_macaddr, + ":"); + avp->av_recv_pspoll(ni, m); + return; + } + + /* + * Ok, let's schedule the highest TID that has traffic + * and then schedule something. + */ + for (tid = IEEE80211_TID_SIZE - 1; tid >= 0; tid--) { + struct ath_tid *atid = &an->an_tid[tid]; + /* + * No frames? Skip. + */ + if (atid->axq_depth == 0) + continue; + ath_tx_tid_sched(sc, atid); + /* + * XXX we could do a direct call to the TXQ + * scheduler code here to optimise latency + * at the expense of a REALLY deep callstack. + */ + ATH_TX_UNLOCK(sc); + taskqueue_enqueue(sc->sc_tq, &sc->sc_txqtask); + DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, + "%s: %6D: leaking frame to TID %d\n", + __func__, + ni->ni_macaddr, + ":", + tid); + return; + } + + ATH_TX_UNLOCK(sc); + + /* + * XXX nothing in the TIDs at this point? Eek. + */ + DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, + "%s: %6D: TIDs empty, but ath_node showed traffic?!\n", + __func__, + ni->ni_macaddr, + ":"); + avp->av_recv_pspoll(ni, m); +#else + avp->av_recv_pspoll(ni, m); +#endif /* ATH_SW_PSQ */ +} + MODULE_VERSION(if_ath, 1); MODULE_DEPEND(if_ath, wlan, 1, 1, 1); /* 802.11 media layer */ +#if defined(IEEE80211_ALQ) || defined(AH_DEBUG_ALQ) || defined(ATH_DEBUG_ALQ) +MODULE_DEPEND(if_ath, alq, 1, 1, 1); +#endif diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_ahb.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_ahb.c index a2bca05d3c..94353f8177 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_ahb.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_ahb.c @@ -29,14 +29,17 @@ */ #include -__FBSDID("$FreeBSD$"); +__FBSDID("$FreeBSD: releng/11.1/sys/dev/ath/if_ath_ahb.c 288349 2015-09-29 03:37:17Z adrian $"); /* * AHB bus front-end for the Atheros Wireless LAN controller driver. */ +#include "opt_ath.h" + #include #include +#include #include #include #include @@ -53,6 +56,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include @@ -83,10 +87,28 @@ struct ath_ahb_softc { static int ath_ahb_probe(device_t dev) { + int vendor_id, device_id; const char* devname; - /* Atheros / ar9130 */ - devname = ath_hal_probe(VENDOR_ATHEROS, AR9130_DEVID); + /* + * Check if a device/vendor ID is provided in hints. + */ + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "vendor_id", &vendor_id) != 0) { + vendor_id = VENDOR_ATHEROS; + } + + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "device_id", &device_id) != 0) { + device_id = AR9130_DEVID; + } + + device_printf(dev, "Vendor=0x%04x, Device=0x%04x\n", + vendor_id & 0xffff, + device_id & 0xffff); + + /* Attempt to probe */ + devname = ath_hal_probe(vendor_id, device_id); if (devname != NULL) { device_set_desc(dev, devname); @@ -95,6 +117,14 @@ ath_ahb_probe(device_t dev) return ENXIO; } +static void +ath_ahb_intr(void *arg) +{ + /* XXX TODO: check if its ours! */ + ar71xx_device_flush_ddr(AR71XX_CPU_DDR_FLUSH_WMAC); + ath_intr(arg); +} + static int ath_ahb_attach(device_t dev) { @@ -103,7 +133,9 @@ ath_ahb_attach(device_t dev) int error = ENXIO; int rid; long eepromaddr; + int eepromsize; uint8_t *p; + int device_id, vendor_id; sc->sc_dev = dev; @@ -114,22 +146,45 @@ ath_ahb_attach(device_t dev) goto bad; } - if (resource_long_value(device_get_name(dev), device_get_unit(dev), - "eepromaddr", &eepromaddr) != 0) { + if (resource_long_value(device_get_name(dev), device_get_unit(dev), + "eepromaddr", &eepromaddr) != 0) { device_printf(dev, "cannot fetch 'eepromaddr' from hints\n"); goto bad0; - } + } + + /* + * The default EEPROM size is 2048 * 16 bit words. + * Later EEPROM/OTP/flash regions may be quite a bit bigger. + */ + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "eepromsize", &eepromsize) != 0) { + eepromsize = ATH_EEPROM_DATA_SIZE * 2; + } + rid = 0; - device_printf(sc->sc_dev, "eeprom @ %p\n", (void *) eepromaddr); - psc->sc_eeprom = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, (uintptr_t) eepromaddr, - (uintptr_t) eepromaddr + (uintptr_t) ((ATH_EEPROM_DATA_SIZE * 2) - 1), 0, RF_ACTIVE); + device_printf(sc->sc_dev, "eeprom @ %p (%d bytes)\n", + (void *) eepromaddr, eepromsize); + /* + * XXX this assumes that the parent device is the nexus + * and will just pass through requests for all of memory. + * + * Later on, when this has to attach off of the actual + * AHB, this won't work. + * + * Ideally this would be done in machdep code in mips/atheros/ + * and it'd expose the EEPROM via the firmware interface, + * so the ath/ath_ahb drivers can be loaded as modules + * after boot-time. + */ + psc->sc_eeprom = bus_alloc_resource(dev, SYS_RES_MEMORY, + &rid, (uintptr_t) eepromaddr, + (uintptr_t) eepromaddr + (uintptr_t) (eepromsize - 1), 0, RF_ACTIVE); if (psc->sc_eeprom == NULL) { device_printf(dev, "cannot map eeprom space\n"); goto bad0; } - /* XXX uintptr_t is a bandaid for ia64; to be fixed */ - sc->sc_st = (HAL_BUS_TAG)(uintptr_t) rman_get_bustag(psc->sc_sr); + sc->sc_st = (HAL_BUS_TAG) rman_get_bustag(psc->sc_sr); sc->sc_sh = (HAL_BUS_HANDLE) rman_get_bushandle(psc->sc_sr); /* * Mark device invalid so any interrupts (shared or otherwise) @@ -138,7 +193,7 @@ ath_ahb_attach(device_t dev) sc->sc_invalid = 1; /* Copy the EEPROM data out */ - sc->sc_eepromdata = malloc(ATH_EEPROM_DATA_SIZE * 2, M_TEMP, M_NOWAIT | M_ZERO); + sc->sc_eepromdata = malloc(eepromsize, M_TEMP, M_NOWAIT | M_ZERO); if (sc->sc_eepromdata == NULL) { device_printf(dev, "cannot allocate memory for eeprom data\n"); goto bad1; @@ -149,10 +204,10 @@ ath_ahb_attach(device_t dev) bus_space_read_multi_1( rman_get_bustag(psc->sc_eeprom), rman_get_bushandle(psc->sc_eeprom), - 0, (u_int8_t *) sc->sc_eepromdata, ATH_EEPROM_DATA_SIZE * 2); + 0, (u_int8_t *) sc->sc_eepromdata, eepromsize); #endif p = (void *) rman_get_bushandle(psc->sc_eeprom); - memcpy(sc->sc_eepromdata, p, ATH_EEPROM_DATA_SIZE * 2); + memcpy(sc->sc_eepromdata, p, eepromsize); /* * Arrange interrupt line. @@ -165,7 +220,7 @@ ath_ahb_attach(device_t dev) } if (bus_setup_intr(dev, psc->sc_irq, INTR_TYPE_NET | INTR_MPSAFE, - NULL, ath_intr, sc, &psc->sc_ih)) { + NULL, ath_ahb_intr, sc, &psc->sc_ih)) { device_printf(dev, "could not establish interrupt\n"); goto bad2; } @@ -189,12 +244,33 @@ ath_ahb_attach(device_t dev) goto bad3; } - ATH_LOCK_INIT(sc); + /* + * Check if a device/vendor ID is provided in hints. + */ + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "vendor_id", &vendor_id) != 0) { + vendor_id = VENDOR_ATHEROS; + } - error = ath_attach(AR9130_DEVID, sc); + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "device_id", &device_id) != 0) { + device_id = AR9130_DEVID; + } + + ATH_LOCK_INIT(sc); + ATH_PCU_LOCK_INIT(sc); + ATH_RX_LOCK_INIT(sc); + ATH_TX_LOCK_INIT(sc); + ATH_TXSTATUS_LOCK_INIT(sc); + + error = ath_attach(device_id, sc); if (error == 0) /* success */ return 0; + ATH_TXSTATUS_LOCK_DESTROY(sc); + ATH_RX_LOCK_DESTROY(sc); + ATH_TX_LOCK_DESTROY(sc); + ATH_PCU_LOCK_DESTROY(sc); ATH_LOCK_DESTROY(sc); bus_dma_tag_destroy(sc->sc_dmat); bad3: @@ -234,6 +310,10 @@ ath_ahb_detach(device_t dev) if (sc->sc_eepromdata) free(sc->sc_eepromdata, M_TEMP); + ATH_TXSTATUS_LOCK_DESTROY(sc); + ATH_RX_LOCK_DESTROY(sc); + ATH_TX_LOCK_DESTROY(sc); + ATH_PCU_LOCK_DESTROY(sc); ATH_LOCK_DESTROY(sc); return (0); @@ -286,6 +366,7 @@ static driver_t ath_ahb_driver = { }; static devclass_t ath_devclass; DRIVER_MODULE(ath, nexus, ath_ahb_driver, ath_devclass, 0, 0); +DRIVER_MODULE(ath, apb, ath_ahb_driver, ath_devclass, 0, 0); MODULE_VERSION(ath, 1); MODULE_DEPEND(ath, wlan, 1, 1, 1); /* 802.11 media layer */ MODULE_DEPEND(ath, if_ath, 1, 1, 1); /* if_ath driver */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_alq.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_alq.c new file mode 100644 index 0000000000..4281dadb3d --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_alq.c @@ -0,0 +1,195 @@ +/*- + * Copyright (c) 2012 Adrian Chadd + * 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. + * + * $FreeBSD: releng/11.1/sys/dev/ath/if_ath_alq.c 250618 2013-05-13 21:17:27Z adrian $ + */ +#include "opt_ah.h" +#include "opt_ath.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef ATH_DEBUG_ALQ +static struct ale * +if_ath_alq_get(struct if_ath_alq *alq, int len) +{ + struct ale *ale; + + if (alq->sc_alq_isactive == 0) + return (NULL); + + ale = alq_getn(alq->sc_alq_alq, len, ALQ_NOWAIT); + if (! ale) + alq->sc_alq_numlost++; + return (ale); +} + +void +if_ath_alq_init(struct if_ath_alq *alq, const char *devname) +{ + + bzero(alq, sizeof(*alq)); + + strncpy(alq->sc_alq_devname, devname, ATH_ALQ_DEVNAME_LEN); + printf("%s (%s): attached\n", __func__, alq->sc_alq_devname); + snprintf(alq->sc_alq_filename, ATH_ALQ_FILENAME_LEN, + "/tmp/ath_%s_alq.log", alq->sc_alq_devname); + + /* XXX too conservative, right? */ + alq->sc_alq_qsize = (64*1024); +} + +void +if_ath_alq_setcfg(struct if_ath_alq *alq, uint32_t macVer, + uint32_t macRev, uint32_t phyRev, uint32_t halMagic) +{ + + /* Store these in network order */ + alq->sc_alq_cfg.sc_mac_version = htobe32(macVer); + alq->sc_alq_cfg.sc_mac_revision = htobe32(macRev); + alq->sc_alq_cfg.sc_phy_rev = htobe32(phyRev); + alq->sc_alq_cfg.sc_hal_magic = htobe32(halMagic); +} + +void +if_ath_alq_tidyup(struct if_ath_alq *alq) +{ + + if_ath_alq_stop(alq); + printf("%s (%s): detached\n", __func__, alq->sc_alq_devname); + bzero(alq, sizeof(*alq)); +} + +int +if_ath_alq_start(struct if_ath_alq *alq) +{ + int error; + + if (alq->sc_alq_isactive) + return (0); + + /* + * Create a variable-length ALQ. + */ + error = alq_open(&alq->sc_alq_alq, alq->sc_alq_filename, + curthread->td_ucred, ALQ_DEFAULT_CMODE, + alq->sc_alq_qsize, 0); + + if (error != 0) { + printf("%s (%s): failed, err=%d\n", __func__, + alq->sc_alq_devname, error); + } else { + printf("%s (%s): opened\n", __func__, alq->sc_alq_devname); + alq->sc_alq_isactive = 1; + if_ath_alq_post(alq, ATH_ALQ_INIT_STATE, + sizeof (struct if_ath_alq_init_state), + (char *) &alq->sc_alq_cfg); + } + return (error); +} + +int +if_ath_alq_stop(struct if_ath_alq *alq) +{ + + if (alq->sc_alq_isactive == 0) + return (0); + + printf("%s (%s): closed\n", __func__, alq->sc_alq_devname); + + alq->sc_alq_isactive = 0; + alq_close(alq->sc_alq_alq); + alq->sc_alq_alq = NULL; + + return (0); +} + +/* + * Post a debug message to the ALQ. + * + * "len" is the size of the buf payload in bytes. + */ +void +if_ath_alq_post(struct if_ath_alq *alq, uint16_t op, uint16_t len, + const char *buf) +{ + struct if_ath_alq_hdr *ap; + struct ale *ale; + struct timeval tv; + + if (! if_ath_alq_checkdebug(alq, op)) + return; + + microtime(&tv); + + /* + * Enforce some semblence of sanity on 'len'. + * Although strictly speaking, any length is possible - + * just be conservative so things don't get out of hand. + */ + if (len > ATH_ALQ_PAYLOAD_LEN) + len = ATH_ALQ_PAYLOAD_LEN; + + ale = if_ath_alq_get(alq, len + sizeof(struct if_ath_alq_hdr)); + + if (ale == NULL) + return; + + ap = (struct if_ath_alq_hdr *) ale->ae_data; + ap->threadid = htobe64((uint64_t) curthread->td_tid); + ap->tstamp_sec = htobe32((uint32_t) tv.tv_sec); + ap->tstamp_usec = htobe32((uint32_t) tv.tv_usec); + ap->op = htobe16(op); + ap->len = htobe16(len); + + /* + * Copy the payload _after_ the header field. + */ + if (buf != NULL) { + memcpy(((char *) ap) + sizeof(struct if_ath_alq_hdr), + buf, + len); + } + + alq_post(alq->sc_alq_alq, ale); +} +#endif /* ATH_DEBUG */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_alq.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_alq.h new file mode 100644 index 0000000000..fbd8f354cc --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_alq.h @@ -0,0 +1,199 @@ +/*- + * Copyright (c) 2012 Adrian Chadd + * 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. + * + * $FreeBSD: releng/11.1/sys/dev/ath/if_ath_alq.h 301767 2016-06-09 22:01:05Z adrian $ + */ +#ifndef __IF_ATH_ALQ_H__ +#define __IF_ATH_ALQ_H__ + +#define ATH_ALQ_INIT_STATE 1 +struct if_ath_alq_init_state { + uint32_t sc_mac_version; + uint32_t sc_mac_revision; + uint32_t sc_phy_rev; + uint32_t sc_hal_magic; +}; + +#define ATH_ALQ_EDMA_TXSTATUS 2 +#define ATH_ALQ_EDMA_RXSTATUS 3 +#define ATH_ALQ_EDMA_TXDESC 4 + +#define ATH_ALQ_TDMA_BEACON_STATE 5 +struct if_ath_alq_tdma_beacon_state { + uint64_t rx_tsf; /* RX TSF of beacon frame */ + uint64_t beacon_tsf; /* TSF inside beacon frame */ + uint64_t tsf64; + uint64_t nextslot_tsf; + uint32_t nextslot_tu; + uint32_t txtime; +}; + +#define ATH_ALQ_TDMA_TIMER_CONFIG 6 +struct if_ath_alq_tdma_timer_config { + uint32_t tdma_slot; + uint32_t tdma_slotlen; + uint32_t tdma_slotcnt; + uint32_t tdma_bintval; + uint32_t tdma_guard; + uint32_t tdma_scbintval; + uint32_t tdma_dbaprep; +}; + +#define ATH_ALQ_TDMA_SLOT_CALC 7 +struct if_ath_alq_tdma_slot_calc { + uint64_t nexttbtt; + uint64_t next_slot; + int32_t tsfdelta; + int32_t avg_plus; + int32_t avg_minus; +}; + +#define ATH_ALQ_TDMA_TSF_ADJUST 8 +struct if_ath_alq_tdma_tsf_adjust { + uint64_t tsf64_old; + uint64_t tsf64_new; + int32_t tsfdelta; +}; + +#define ATH_ALQ_TDMA_TIMER_SET 9 +struct if_ath_alq_tdma_timer_set { + uint32_t bt_intval; + uint32_t bt_nexttbtt; + uint32_t bt_nextdba; + uint32_t bt_nextswba; + uint32_t bt_nextatim; + uint32_t bt_flags; + uint32_t sc_tdmadbaprep; + uint32_t sc_tdmaswbaprep; +}; + +#define ATH_ALQ_INTR_STATUS 10 +struct if_ath_alq_interrupt { + uint32_t intr_status; + uint32_t intr_state[8]; + uint32_t intr_syncstate; +}; + +#define ATH_ALQ_MIB_COUNTERS 11 +struct if_ath_alq_mib_counters { + uint32_t valid; + uint32_t tx_busy; + uint32_t rx_busy; + uint32_t chan_busy; + uint32_t ext_chan_busy; + uint32_t cycle_count; +}; + +#define ATH_ALQ_MISSED_BEACON 12 +#define ATH_ALQ_STUCK_BEACON 13 +#define ATH_ALQ_RESUME_BEACON 14 + +#define ATH_ALQ_TX_FIFO_PUSH 15 +struct if_ath_alq_tx_fifo_push { + uint32_t txq; + uint32_t nframes; + uint32_t fifo_depth; + uint32_t frame_cnt; +}; + +/* + * These will always be logged, regardless. + */ +#define ATH_ALQ_LOG_ALWAYS_MASK 0x00000001 + +#define ATH_ALQ_FILENAME_LEN 128 +#define ATH_ALQ_DEVNAME_LEN 32 + +struct if_ath_alq { + uint32_t sc_alq_debug; /* Debug flags to report */ + struct alq * sc_alq_alq; /* alq state */ + unsigned int sc_alq_qsize; /* queue size */ + unsigned int sc_alq_numlost; /* number of "lost" entries */ + int sc_alq_isactive; + char sc_alq_devname[ATH_ALQ_DEVNAME_LEN]; + char sc_alq_filename[ATH_ALQ_FILENAME_LEN]; + struct if_ath_alq_init_state sc_alq_cfg; +}; + +/* 128 bytes in total */ +#define ATH_ALQ_PAYLOAD_LEN 112 + +struct if_ath_alq_hdr { + uint64_t threadid; + uint32_t tstamp_sec; + uint32_t tstamp_usec; + uint16_t op; + uint16_t len; /* Length of (optional) payload */ +}; + +struct if_ath_alq_payload { + struct if_ath_alq_hdr hdr; + char payload[]; +}; + +#ifdef _KERNEL +static inline int +if_ath_alq_checkdebug(struct if_ath_alq *alq, uint16_t op) +{ + + return ((alq->sc_alq_debug | ATH_ALQ_LOG_ALWAYS_MASK) + & (1 << (op - 1))); +} + +extern void if_ath_alq_init(struct if_ath_alq *alq, const char *devname); +extern void if_ath_alq_setcfg(struct if_ath_alq *alq, uint32_t macVer, + uint32_t macRev, uint32_t phyRev, uint32_t halMagic); +extern void if_ath_alq_tidyup(struct if_ath_alq *alq); +extern int if_ath_alq_start(struct if_ath_alq *alq); +extern int if_ath_alq_stop(struct if_ath_alq *alq); +extern void if_ath_alq_post(struct if_ath_alq *alq, uint16_t op, + uint16_t len, const char *buf); + +/* XXX maybe doesn't belong here? */ +static inline void +if_ath_alq_post_intr(struct if_ath_alq *alq, uint32_t status, + uint32_t *state, uint32_t sync_state) +{ + int i; + struct if_ath_alq_interrupt intr; + + if (! if_ath_alq_checkdebug(alq, ATH_ALQ_INTR_STATUS)) + return; + + intr.intr_status = htobe32(status); + for (i = 0; i < 8; i++) + intr.intr_state[i] = htobe32(state[i]); + intr.intr_syncstate = htobe32(sync_state); + + if_ath_alq_post(alq, ATH_ALQ_INTR_STATUS, sizeof(intr), + (const char *) &intr); +} + +#endif /* _KERNEL */ + +#endif diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_beacon.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_beacon.c new file mode 100644 index 0000000000..b419824093 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_beacon.c @@ -0,0 +1,1188 @@ +/*- + * 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 +__FBSDID("$FreeBSD: releng/11.1/sys/dev/ath/if_ath_beacon.c 298359 2016-04-20 18:29:30Z avos $"); + +/* + * 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" +/* + * This is needed for register operations which are performed + * by the driver - eg, calls to ath_hal_gettsf32(). + * + * It's also required for any AH_DEBUG checks in here, eg the + * module dependencies. + */ +#include "opt_ah.h" +#include "opt_wlan.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for mp_ncpus */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#ifdef IEEE80211_SUPPORT_SUPERG +#include +#endif + +#include + +#ifdef INET +#include +#include +#endif + +#include + +#include +#include +#include +#include + +#ifdef ATH_TX99_DIAG +#include +#endif + +/* + * Setup a h/w transmit queue for beacons. + */ +int +ath_beaconq_setup(struct ath_softc *sc) +{ + struct ath_hal *ah = sc->sc_ah; + HAL_TXQ_INFO qi; + + memset(&qi, 0, sizeof(qi)); + qi.tqi_aifs = HAL_TXQ_USEDEFAULT; + qi.tqi_cwmin = HAL_TXQ_USEDEFAULT; + qi.tqi_cwmax = HAL_TXQ_USEDEFAULT; + /* NB: for dynamic turbo, don't enable any other interrupts */ + qi.tqi_qflags = HAL_TXQ_TXDESCINT_ENABLE; + if (sc->sc_isedma) + qi.tqi_qflags |= HAL_TXQ_TXOKINT_ENABLE | + HAL_TXQ_TXERRINT_ENABLE; + + return ath_hal_setuptxqueue(ah, HAL_TX_QUEUE_BEACON, &qi); +} + +/* + * Setup the transmit queue parameters for the beacon queue. + */ +int +ath_beaconq_config(struct ath_softc *sc) +{ +#define ATH_EXPONENT_TO_VALUE(v) ((1<<(v))-1) + struct ieee80211com *ic = &sc->sc_ic; + struct ath_hal *ah = sc->sc_ah; + HAL_TXQ_INFO qi; + + ath_hal_gettxqueueprops(ah, sc->sc_bhalq, &qi); + if (ic->ic_opmode == IEEE80211_M_HOSTAP || + ic->ic_opmode == IEEE80211_M_MBSS) { + /* + * Always burst out beacon and CAB traffic. + */ + qi.tqi_aifs = ATH_BEACON_AIFS_DEFAULT; + qi.tqi_cwmin = ATH_BEACON_CWMIN_DEFAULT; + qi.tqi_cwmax = ATH_BEACON_CWMAX_DEFAULT; + } else { + struct wmeParams *wmep = + &ic->ic_wme.wme_chanParams.cap_wmeParams[WME_AC_BE]; + /* + * Adhoc mode; important thing is to use 2x cwmin. + */ + qi.tqi_aifs = wmep->wmep_aifsn; + qi.tqi_cwmin = 2*ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmin); + qi.tqi_cwmax = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmax); + } + + if (!ath_hal_settxqueueprops(ah, sc->sc_bhalq, &qi)) { + device_printf(sc->sc_dev, "unable to update parameters for " + "beacon hardware queue!\n"); + return 0; + } else { + ath_hal_resettxqueue(ah, sc->sc_bhalq); /* push to h/w */ + return 1; + } +#undef ATH_EXPONENT_TO_VALUE +} + +/* + * Allocate and setup an initial beacon frame. + */ +int +ath_beacon_alloc(struct ath_softc *sc, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ath_vap *avp = ATH_VAP(vap); + struct ath_buf *bf; + struct mbuf *m; + int error; + + bf = avp->av_bcbuf; + DPRINTF(sc, ATH_DEBUG_NODE, "%s: bf_m=%p, bf_node=%p\n", + __func__, bf->bf_m, bf->bf_node); + if (bf->bf_m != NULL) { + bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); + m_freem(bf->bf_m); + bf->bf_m = NULL; + } + if (bf->bf_node != NULL) { + ieee80211_free_node(bf->bf_node); + bf->bf_node = NULL; + } + + /* + * NB: the beacon data buffer must be 32-bit aligned; + * we assume the mbuf routines will return us something + * with this alignment (perhaps should assert). + */ + m = ieee80211_beacon_alloc(ni); + if (m == NULL) { + device_printf(sc->sc_dev, "%s: cannot get mbuf\n", __func__); + sc->sc_stats.ast_be_nombuf++; + return ENOMEM; + } + error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m, + bf->bf_segs, &bf->bf_nseg, + BUS_DMA_NOWAIT); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: cannot map mbuf, bus_dmamap_load_mbuf_sg returns %d\n", + __func__, error); + m_freem(m); + return error; + } + + /* + * Calculate a TSF adjustment factor required for staggered + * beacons. Note that we assume the format of the beacon + * frame leaves the tstamp field immediately following the + * header. + */ + if (sc->sc_stagbeacons && avp->av_bslot > 0) { + uint64_t tsfadjust; + struct ieee80211_frame *wh; + + /* + * The beacon interval is in TU's; the TSF is in usecs. + * We figure out how many TU's to add to align the timestamp + * then convert to TSF units and handle byte swapping before + * inserting it in the frame. The hardware will then add this + * each time a beacon frame is sent. Note that we align vap's + * 1..N and leave vap 0 untouched. This means vap 0 has a + * timestamp in one beacon interval while the others get a + * timstamp aligned to the next interval. + */ + tsfadjust = ni->ni_intval * + (ATH_BCBUF - avp->av_bslot) / ATH_BCBUF; + tsfadjust = htole64(tsfadjust << 10); /* TU -> TSF */ + + DPRINTF(sc, ATH_DEBUG_BEACON, + "%s: %s beacons bslot %d intval %u tsfadjust %llu\n", + __func__, sc->sc_stagbeacons ? "stagger" : "burst", + avp->av_bslot, ni->ni_intval, + (long long unsigned) le64toh(tsfadjust)); + + wh = mtod(m, struct ieee80211_frame *); + memcpy(&wh[1], &tsfadjust, sizeof(tsfadjust)); + } + bf->bf_m = m; + bf->bf_node = ieee80211_ref_node(ni); + + return 0; +} + +/* + * Setup the beacon frame for transmit. + */ +static void +ath_beacon_setup(struct ath_softc *sc, struct ath_buf *bf) +{ +#define USE_SHPREAMBLE(_ic) \ + (((_ic)->ic_flags & (IEEE80211_F_SHPREAMBLE | IEEE80211_F_USEBARKER))\ + == IEEE80211_F_SHPREAMBLE) + struct ieee80211_node *ni = bf->bf_node; + struct ieee80211com *ic = ni->ni_ic; + struct mbuf *m = bf->bf_m; + struct ath_hal *ah = sc->sc_ah; + struct ath_desc *ds; + int flags, antenna; + const HAL_RATE_TABLE *rt; + u_int8_t rix, rate; + HAL_DMA_ADDR bufAddrList[4]; + uint32_t segLenList[4]; + HAL_11N_RATE_SERIES rc[4]; + + DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s: m %p len %u\n", + __func__, m, m->m_len); + + /* setup descriptors */ + ds = bf->bf_desc; + bf->bf_last = bf; + bf->bf_lastds = ds; + + flags = HAL_TXDESC_NOACK; + if (ic->ic_opmode == IEEE80211_M_IBSS && sc->sc_hasveol) { + /* self-linked descriptor */ + ath_hal_settxdesclink(sc->sc_ah, ds, bf->bf_daddr); + flags |= HAL_TXDESC_VEOL; + /* + * Let hardware handle antenna switching. + */ + antenna = sc->sc_txantenna; + } else { + ath_hal_settxdesclink(sc->sc_ah, ds, 0); + /* + * Switch antenna every 4 beacons. + * XXX assumes two antenna + */ + if (sc->sc_txantenna != 0) + antenna = sc->sc_txantenna; + else if (sc->sc_stagbeacons && sc->sc_nbcnvaps != 0) + antenna = ((sc->sc_stats.ast_be_xmit / sc->sc_nbcnvaps) & 4 ? 2 : 1); + else + antenna = (sc->sc_stats.ast_be_xmit & 4 ? 2 : 1); + } + + KASSERT(bf->bf_nseg == 1, + ("multi-segment beacon frame; nseg %u", bf->bf_nseg)); + + /* + * Calculate rate code. + * XXX everything at min xmit rate + */ + rix = 0; + rt = sc->sc_currates; + rate = rt->info[rix].rateCode; + if (USE_SHPREAMBLE(ic)) + rate |= rt->info[rix].shortPreamble; + ath_hal_setuptxdesc(ah, ds + , m->m_len + IEEE80211_CRC_LEN /* frame length */ + , sizeof(struct ieee80211_frame)/* header length */ + , HAL_PKT_TYPE_BEACON /* Atheros packet type */ + , ieee80211_get_node_txpower(ni) /* txpower XXX */ + , rate, 1 /* series 0 rate/tries */ + , HAL_TXKEYIX_INVALID /* no encryption */ + , antenna /* antenna mode */ + , flags /* no ack, veol for beacons */ + , 0 /* rts/cts rate */ + , 0 /* rts/cts duration */ + ); + + /* + * The EDMA HAL currently assumes that _all_ rate control + * settings are done in ath_hal_set11nratescenario(), rather + * than in ath_hal_setuptxdesc(). + */ + if (sc->sc_isedma) { + memset(&rc, 0, sizeof(rc)); + + rc[0].ChSel = sc->sc_txchainmask; + rc[0].Tries = 1; + rc[0].Rate = rt->info[rix].rateCode; + rc[0].RateIndex = rix; + rc[0].tx_power_cap = 0x3f; + rc[0].PktDuration = + ath_hal_computetxtime(ah, rt, roundup(m->m_len, 4), + rix, 0); + ath_hal_set11nratescenario(ah, ds, 0, 0, rc, 4, flags); + } + + /* NB: beacon's BufLen must be a multiple of 4 bytes */ + segLenList[0] = roundup(m->m_len, 4); + segLenList[1] = segLenList[2] = segLenList[3] = 0; + bufAddrList[0] = bf->bf_segs[0].ds_addr; + bufAddrList[1] = bufAddrList[2] = bufAddrList[3] = 0; + ath_hal_filltxdesc(ah, ds + , bufAddrList + , segLenList + , 0 /* XXX desc id */ + , sc->sc_bhalq /* hardware TXQ */ + , AH_TRUE /* first segment */ + , AH_TRUE /* last segment */ + , ds /* first descriptor */ + ); +#if 0 + ath_desc_swap(ds); +#endif +#undef USE_SHPREAMBLE +} + +void +ath_beacon_update(struct ieee80211vap *vap, int item) +{ + struct ieee80211_beacon_offsets *bo = &vap->iv_bcn_off; + + setbit(bo->bo_flags, item); +} + +/* + * Handle a beacon miss. + */ +void +ath_beacon_miss(struct ath_softc *sc) +{ + HAL_SURVEY_SAMPLE hs; + HAL_BOOL ret; + uint32_t hangs; + + bzero(&hs, sizeof(hs)); + + ret = ath_hal_get_mib_cycle_counts(sc->sc_ah, &hs); + + if (ath_hal_gethangstate(sc->sc_ah, 0xffff, &hangs) && hangs != 0) { + DPRINTF(sc, ATH_DEBUG_BEACON, + "%s: hang=0x%08x\n", + __func__, + hangs); + } + +#ifdef ATH_DEBUG_ALQ + if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_MISSED_BEACON)) + if_ath_alq_post(&sc->sc_alq, ATH_ALQ_MISSED_BEACON, 0, NULL); +#endif + + DPRINTF(sc, ATH_DEBUG_BEACON, + "%s: valid=%d, txbusy=%u, rxbusy=%u, chanbusy=%u, " + "extchanbusy=%u, cyclecount=%u\n", + __func__, + ret, + hs.tx_busy, + hs.rx_busy, + hs.chan_busy, + hs.ext_chan_busy, + hs.cycle_count); +} + +/* + * Transmit a beacon frame at SWBA. Dynamic updates to the + * frame contents are done as needed and the slot time is + * also adjusted based on current state. + */ +void +ath_beacon_proc(void *arg, int pending) +{ + struct ath_softc *sc = arg; + struct ath_hal *ah = sc->sc_ah; + struct ieee80211vap *vap; + struct ath_buf *bf; + int slot, otherant; + uint32_t bfaddr; + + DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s: pending %u\n", + __func__, pending); + /* + * Check if the previous beacon has gone out. If + * not don't try to post another, skip this period + * and wait for the next. Missed beacons indicate + * a problem and should not occur. If we miss too + * many consecutive beacons reset the device. + */ + if (ath_hal_numtxpending(ah, sc->sc_bhalq) != 0) { + sc->sc_bmisscount++; + sc->sc_stats.ast_be_missed++; + ath_beacon_miss(sc); + DPRINTF(sc, ATH_DEBUG_BEACON, + "%s: missed %u consecutive beacons\n", + __func__, sc->sc_bmisscount); + if (sc->sc_bmisscount >= ath_bstuck_threshold) + taskqueue_enqueue(sc->sc_tq, &sc->sc_bstucktask); + return; + } + if (sc->sc_bmisscount != 0) { + DPRINTF(sc, ATH_DEBUG_BEACON, + "%s: resume beacon xmit after %u misses\n", + __func__, sc->sc_bmisscount); + sc->sc_bmisscount = 0; +#ifdef ATH_DEBUG_ALQ + if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_RESUME_BEACON)) + if_ath_alq_post(&sc->sc_alq, ATH_ALQ_RESUME_BEACON, 0, NULL); +#endif + } + + if (sc->sc_stagbeacons) { /* staggered beacons */ + struct ieee80211com *ic = &sc->sc_ic; + uint32_t tsftu; + + tsftu = ath_hal_gettsf32(ah) >> 10; + /* XXX lintval */ + slot = ((tsftu % ic->ic_lintval) * ATH_BCBUF) / ic->ic_lintval; + vap = sc->sc_bslot[(slot+1) % ATH_BCBUF]; + bfaddr = 0; + if (vap != NULL && vap->iv_state >= IEEE80211_S_RUN) { + bf = ath_beacon_generate(sc, vap); + if (bf != NULL) + bfaddr = bf->bf_daddr; + } + } else { /* burst'd beacons */ + uint32_t *bflink = &bfaddr; + + for (slot = 0; slot < ATH_BCBUF; slot++) { + vap = sc->sc_bslot[slot]; + if (vap != NULL && vap->iv_state >= IEEE80211_S_RUN) { + bf = ath_beacon_generate(sc, vap); + /* + * XXX TODO: this should use settxdesclinkptr() + * otherwise it won't work for EDMA chipsets! + */ + if (bf != NULL) { + /* XXX should do this using the ds */ + *bflink = bf->bf_daddr; + ath_hal_gettxdesclinkptr(sc->sc_ah, + bf->bf_desc, &bflink); + } + } + } + /* + * XXX TODO: this should use settxdesclinkptr() + * otherwise it won't work for EDMA chipsets! + */ + *bflink = 0; /* terminate list */ + } + + /* + * Handle slot time change when a non-ERP station joins/leaves + * an 11g network. The 802.11 layer notifies us via callback, + * we mark updateslot, then wait one beacon before effecting + * the change. This gives associated stations at least one + * beacon interval to note the state change. + */ + /* XXX locking */ + if (sc->sc_updateslot == UPDATE) { + sc->sc_updateslot = COMMIT; /* commit next beacon */ + sc->sc_slotupdate = slot; + } else if (sc->sc_updateslot == COMMIT && sc->sc_slotupdate == slot) + ath_setslottime(sc); /* commit change to h/w */ + + /* + * Check recent per-antenna transmit statistics and flip + * the default antenna if noticeably more frames went out + * on the non-default antenna. + * XXX assumes 2 anntenae + */ + if (!sc->sc_diversity && (!sc->sc_stagbeacons || slot == 0)) { + otherant = sc->sc_defant & 1 ? 2 : 1; + if (sc->sc_ant_tx[otherant] > sc->sc_ant_tx[sc->sc_defant] + 2) + ath_setdefantenna(sc, otherant); + sc->sc_ant_tx[1] = sc->sc_ant_tx[2] = 0; + } + + /* Program the CABQ with the contents of the CABQ txq and start it */ + ATH_TXQ_LOCK(sc->sc_cabq); + ath_beacon_cabq_start(sc); + ATH_TXQ_UNLOCK(sc->sc_cabq); + + /* Program the new beacon frame if we have one for this interval */ + if (bfaddr != 0) { + /* + * Stop any current dma and put the new frame on the queue. + * This should never fail since we check above that no frames + * are still pending on the queue. + */ + if (! sc->sc_isedma) { + if (!ath_hal_stoptxdma(ah, sc->sc_bhalq)) { + DPRINTF(sc, ATH_DEBUG_ANY, + "%s: beacon queue %u did not stop?\n", + __func__, sc->sc_bhalq); + } + } + /* NB: cabq traffic should already be queued and primed */ + + ath_hal_puttxbuf(ah, sc->sc_bhalq, bfaddr); + ath_hal_txstart(ah, sc->sc_bhalq); + + sc->sc_stats.ast_be_xmit++; + } +} + +static void +ath_beacon_cabq_start_edma(struct ath_softc *sc) +{ + struct ath_buf *bf, *bf_last; + struct ath_txq *cabq = sc->sc_cabq; +#if 0 + struct ath_buf *bfi; + int i = 0; +#endif + + ATH_TXQ_LOCK_ASSERT(cabq); + + if (TAILQ_EMPTY(&cabq->axq_q)) + return; + bf = TAILQ_FIRST(&cabq->axq_q); + bf_last = TAILQ_LAST(&cabq->axq_q, axq_q_s); + + /* + * This is a dirty, dirty hack to push the contents of + * the cabq staging queue into the FIFO. + * + * This ideally should live in the EDMA code file + * and only push things into the CABQ if there's a FIFO + * slot. + * + * We can't treat this like a normal TX queue because + * in the case of multi-VAP traffic, we may have to flush + * the CABQ each new (staggered) beacon that goes out. + * But for non-staggered beacons, we could in theory + * handle multicast traffic for all VAPs in one FIFO + * push. Just keep all of this in mind if you're wondering + * how to correctly/better handle multi-VAP CABQ traffic + * with EDMA. + */ + + /* + * Is the CABQ FIFO free? If not, complain loudly and + * don't queue anything. Maybe we'll flush the CABQ + * traffic, maybe we won't. But that'll happen next + * beacon interval. + */ + if (cabq->axq_fifo_depth >= HAL_TXFIFO_DEPTH) { + device_printf(sc->sc_dev, + "%s: Q%d: CAB FIFO queue=%d?\n", + __func__, + cabq->axq_qnum, + cabq->axq_fifo_depth); + return; + } + + /* + * Ok, so here's the gymnastics reqiured to make this + * all sensible. + */ + + /* + * Tag the first/last buffer appropriately. + */ + bf->bf_flags |= ATH_BUF_FIFOPTR; + bf_last->bf_flags |= ATH_BUF_FIFOEND; + +#if 0 + i = 0; + TAILQ_FOREACH(bfi, &cabq->axq_q, bf_list) { + ath_printtxbuf(sc, bf, cabq->axq_qnum, i, 0); + i++; + } +#endif + + /* + * We now need to push this set of frames onto the tail + * of the FIFO queue. We don't adjust the aggregate + * count, only the queue depth counter(s). + * We also need to blank the link pointer now. + */ + TAILQ_CONCAT(&cabq->fifo.axq_q, &cabq->axq_q, bf_list); + cabq->axq_link = NULL; + cabq->fifo.axq_depth += cabq->axq_depth; + cabq->axq_depth = 0; + + /* Bump FIFO queue */ + cabq->axq_fifo_depth++; + + /* Push the first entry into the hardware */ + ath_hal_puttxbuf(sc->sc_ah, cabq->axq_qnum, bf->bf_daddr); + cabq->axq_flags |= ATH_TXQ_PUTRUNNING; + + /* NB: gated by beacon so safe to start here */ + ath_hal_txstart(sc->sc_ah, cabq->axq_qnum); + +} + +static void +ath_beacon_cabq_start_legacy(struct ath_softc *sc) +{ + struct ath_buf *bf; + struct ath_txq *cabq = sc->sc_cabq; + + ATH_TXQ_LOCK_ASSERT(cabq); + if (TAILQ_EMPTY(&cabq->axq_q)) + return; + bf = TAILQ_FIRST(&cabq->axq_q); + + /* Push the first entry into the hardware */ + ath_hal_puttxbuf(sc->sc_ah, cabq->axq_qnum, bf->bf_daddr); + cabq->axq_flags |= ATH_TXQ_PUTRUNNING; + + /* NB: gated by beacon so safe to start here */ + ath_hal_txstart(sc->sc_ah, cabq->axq_qnum); +} + +/* + * Start CABQ transmission - this assumes that all frames are prepped + * and ready in the CABQ. + */ +void +ath_beacon_cabq_start(struct ath_softc *sc) +{ + struct ath_txq *cabq = sc->sc_cabq; + + ATH_TXQ_LOCK_ASSERT(cabq); + + if (TAILQ_EMPTY(&cabq->axq_q)) + return; + + if (sc->sc_isedma) + ath_beacon_cabq_start_edma(sc); + else + ath_beacon_cabq_start_legacy(sc); +} + +struct ath_buf * +ath_beacon_generate(struct ath_softc *sc, struct ieee80211vap *vap) +{ + struct ath_vap *avp = ATH_VAP(vap); + struct ath_txq *cabq = sc->sc_cabq; + struct ath_buf *bf; + struct mbuf *m; + int nmcastq, error; + + KASSERT(vap->iv_state >= IEEE80211_S_RUN, + ("not running, state %d", vap->iv_state)); + KASSERT(avp->av_bcbuf != NULL, ("no beacon buffer")); + + /* + * Update dynamic beacon contents. If this returns + * non-zero then we need to remap the memory because + * the beacon frame changed size (probably because + * of the TIM bitmap). + */ + bf = avp->av_bcbuf; + m = bf->bf_m; + /* XXX lock mcastq? */ + nmcastq = avp->av_mcastq.axq_depth; + + if (ieee80211_beacon_update(bf->bf_node, m, nmcastq)) { + /* XXX too conservative? */ + bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); + error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m, + bf->bf_segs, &bf->bf_nseg, + BUS_DMA_NOWAIT); + if (error != 0) { + if_printf(vap->iv_ifp, + "%s: bus_dmamap_load_mbuf_sg failed, error %u\n", + __func__, error); + return NULL; + } + } + if ((vap->iv_bcn_off.bo_tim[4] & 1) && cabq->axq_depth) { + DPRINTF(sc, ATH_DEBUG_BEACON, + "%s: cabq did not drain, mcastq %u cabq %u\n", + __func__, nmcastq, cabq->axq_depth); + sc->sc_stats.ast_cabq_busy++; + if (sc->sc_nvaps > 1 && sc->sc_stagbeacons) { + /* + * CABQ traffic from a previous vap is still pending. + * We must drain the q before this beacon frame goes + * out as otherwise this vap's stations will get cab + * frames from a different vap. + * XXX could be slow causing us to miss DBA + */ + /* + * XXX TODO: this doesn't stop CABQ DMA - it assumes + * that since we're about to transmit a beacon, we've + * already stopped transmitting on the CABQ. But this + * doesn't at all mean that the CABQ DMA QCU will + * accept a new TXDP! So what, should we do a DMA + * stop? What if it fails? + * + * More thought is required here. + */ + /* + * XXX can we even stop TX DMA here? Check what the + * reference driver does for cabq for beacons, given + * that stopping TX requires RX is paused. + */ + ath_tx_draintxq(sc, cabq); + } + } + ath_beacon_setup(sc, bf); + bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE); + + /* + * Enable the CAB queue before the beacon queue to + * insure cab frames are triggered by this beacon. + */ + if (vap->iv_bcn_off.bo_tim[4] & 1) { + + /* NB: only at DTIM */ + ATH_TXQ_LOCK(&avp->av_mcastq); + if (nmcastq) { + struct ath_buf *bfm, *bfc_last; + + /* + * Move frames from the s/w mcast q to the h/w cab q. + * + * XXX TODO: if we chain together multiple VAPs + * worth of CABQ traffic, should we keep the + * MORE data bit set on the last frame of each + * intermediary VAP (ie, only clear the MORE + * bit of the last frame on the last vap?) + */ + bfm = TAILQ_FIRST(&avp->av_mcastq.axq_q); + ATH_TXQ_LOCK(cabq); + + /* + * If there's already a frame on the CABQ, we + * need to link to the end of the last frame. + * We can't use axq_link here because + * EDMA descriptors require some recalculation + * (checksum) to occur. + */ + bfc_last = ATH_TXQ_LAST(cabq, axq_q_s); + if (bfc_last != NULL) { + ath_hal_settxdesclink(sc->sc_ah, + bfc_last->bf_lastds, + bfm->bf_daddr); + } + ath_txqmove(cabq, &avp->av_mcastq); + ATH_TXQ_UNLOCK(cabq); + /* + * XXX not entirely accurate, in case a mcast + * queue frame arrived before we grabbed the TX + * lock. + */ + sc->sc_stats.ast_cabq_xmit += nmcastq; + } + ATH_TXQ_UNLOCK(&avp->av_mcastq); + } + return bf; +} + +void +ath_beacon_start_adhoc(struct ath_softc *sc, struct ieee80211vap *vap) +{ + struct ath_vap *avp = ATH_VAP(vap); + struct ath_hal *ah = sc->sc_ah; + struct ath_buf *bf; + struct mbuf *m; + int error; + + KASSERT(avp->av_bcbuf != NULL, ("no beacon buffer")); + + /* + * Update dynamic beacon contents. If this returns + * non-zero then we need to remap the memory because + * the beacon frame changed size (probably because + * of the TIM bitmap). + */ + bf = avp->av_bcbuf; + m = bf->bf_m; + if (ieee80211_beacon_update(bf->bf_node, m, 0)) { + /* XXX too conservative? */ + bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); + error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m, + bf->bf_segs, &bf->bf_nseg, + BUS_DMA_NOWAIT); + if (error != 0) { + if_printf(vap->iv_ifp, + "%s: bus_dmamap_load_mbuf_sg failed, error %u\n", + __func__, error); + return; + } + } + ath_beacon_setup(sc, bf); + bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE); + + /* NB: caller is known to have already stopped tx dma */ + ath_hal_puttxbuf(ah, sc->sc_bhalq, bf->bf_daddr); + ath_hal_txstart(ah, sc->sc_bhalq); +} + +/* + * Reclaim beacon resources and return buffer to the pool. + */ +void +ath_beacon_return(struct ath_softc *sc, struct ath_buf *bf) +{ + + DPRINTF(sc, ATH_DEBUG_NODE, "%s: free bf=%p, bf_m=%p, bf_node=%p\n", + __func__, bf, bf->bf_m, bf->bf_node); + if (bf->bf_m != NULL) { + bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); + m_freem(bf->bf_m); + bf->bf_m = NULL; + } + if (bf->bf_node != NULL) { + ieee80211_free_node(bf->bf_node); + bf->bf_node = NULL; + } + TAILQ_INSERT_TAIL(&sc->sc_bbuf, bf, bf_list); +} + +/* + * Reclaim beacon resources. + */ +void +ath_beacon_free(struct ath_softc *sc) +{ + struct ath_buf *bf; + + TAILQ_FOREACH(bf, &sc->sc_bbuf, bf_list) { + DPRINTF(sc, ATH_DEBUG_NODE, + "%s: free bf=%p, bf_m=%p, bf_node=%p\n", + __func__, bf, bf->bf_m, bf->bf_node); + if (bf->bf_m != NULL) { + bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); + m_freem(bf->bf_m); + bf->bf_m = NULL; + } + if (bf->bf_node != NULL) { + ieee80211_free_node(bf->bf_node); + bf->bf_node = NULL; + } + } +} + +/* + * Configure the beacon and sleep timers. + * + * When operating as an AP this resets the TSF and sets + * up the hardware to notify us when we need to issue beacons. + * + * When operating in station mode this sets up the beacon + * timers according to the timestamp of the last received + * beacon and the current TSF, configures PCF and DTIM + * handling, programs the sleep registers so the hardware + * will wakeup in time to receive beacons, and configures + * the beacon miss handling so we'll receive a BMISS + * interrupt when we stop seeing beacons from the AP + * we've associated with. + */ +void +ath_beacon_config(struct ath_softc *sc, struct ieee80211vap *vap) +{ +#define TSF_TO_TU(_h,_l) \ + ((((u_int32_t)(_h)) << 22) | (((u_int32_t)(_l)) >> 10)) +#define FUDGE 2 + struct ath_hal *ah = sc->sc_ah; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni; + u_int32_t nexttbtt, intval, tsftu; + u_int32_t nexttbtt_u8, intval_u8; + u_int64_t tsf, tsf_beacon; + + if (vap == NULL) + vap = TAILQ_FIRST(&ic->ic_vaps); /* XXX */ + /* + * Just ensure that we aren't being called when the last + * VAP is destroyed. + */ + if (vap == NULL) { + device_printf(sc->sc_dev, "%s: called with no VAPs\n", + __func__); + return; + } + + ni = ieee80211_ref_node(vap->iv_bss); + + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + + /* extract tstamp from last beacon and convert to TU */ + nexttbtt = TSF_TO_TU(le32dec(ni->ni_tstamp.data + 4), + le32dec(ni->ni_tstamp.data)); + + tsf_beacon = ((uint64_t) le32dec(ni->ni_tstamp.data + 4)) << 32; + tsf_beacon |= le32dec(ni->ni_tstamp.data); + + if (ic->ic_opmode == IEEE80211_M_HOSTAP || + ic->ic_opmode == IEEE80211_M_MBSS) { + /* + * For multi-bss ap/mesh support beacons are either staggered + * evenly over N slots or burst together. For the former + * arrange for the SWBA to be delivered for each slot. + * Slots that are not occupied will generate nothing. + */ + /* NB: the beacon interval is kept internally in TU's */ + intval = ni->ni_intval & HAL_BEACON_PERIOD; + if (sc->sc_stagbeacons) + intval /= ATH_BCBUF; + } else { + /* NB: the beacon interval is kept internally in TU's */ + intval = ni->ni_intval & HAL_BEACON_PERIOD; + } + if (nexttbtt == 0) /* e.g. for ap mode */ + nexttbtt = intval; + else if (intval) /* NB: can be 0 for monitor mode */ + nexttbtt = roundup(nexttbtt, intval); + DPRINTF(sc, ATH_DEBUG_BEACON, "%s: nexttbtt %u intval %u (%u)\n", + __func__, nexttbtt, intval, ni->ni_intval); + if (ic->ic_opmode == IEEE80211_M_STA && !sc->sc_swbmiss) { + HAL_BEACON_STATE bs; + int dtimperiod, dtimcount; + int cfpperiod, cfpcount; + + /* + * Setup dtim and cfp parameters according to + * last beacon we received (which may be none). + */ + dtimperiod = ni->ni_dtim_period; + if (dtimperiod <= 0) /* NB: 0 if not known */ + dtimperiod = 1; + dtimcount = ni->ni_dtim_count; + if (dtimcount >= dtimperiod) /* NB: sanity check */ + dtimcount = 0; /* XXX? */ + cfpperiod = 1; /* NB: no PCF support yet */ + cfpcount = 0; + /* + * Pull nexttbtt forward to reflect the current + * TSF and calculate dtim+cfp state for the result. + */ + tsf = ath_hal_gettsf64(ah); + tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; + + DPRINTF(sc, ATH_DEBUG_BEACON, + "%s: beacon tsf=%llu, hw tsf=%llu, nexttbtt=%u, tsftu=%u\n", + __func__, + (unsigned long long) tsf_beacon, + (unsigned long long) tsf, + nexttbtt, + tsftu); + DPRINTF(sc, ATH_DEBUG_BEACON, + "%s: beacon tsf=%llu, hw tsf=%llu, tsf delta=%lld\n", + __func__, + (unsigned long long) tsf_beacon, + (unsigned long long) tsf, + (long long) tsf - + (long long) tsf_beacon); + + DPRINTF(sc, ATH_DEBUG_BEACON, + "%s: nexttbtt=%llu, beacon tsf delta=%lld\n", + __func__, + (unsigned long long) nexttbtt, + (long long) ((long long) nexttbtt * 1024LL) - (long long) tsf_beacon); + + /* XXX cfpcount? */ + + if (nexttbtt > tsftu) { + uint32_t countdiff, oldtbtt, remainder; + + oldtbtt = nexttbtt; + remainder = (nexttbtt - tsftu) % intval; + nexttbtt = tsftu + remainder; + + countdiff = (oldtbtt - nexttbtt) / intval % dtimperiod; + if (dtimcount > countdiff) { + dtimcount -= countdiff; + } else { + dtimcount += dtimperiod - countdiff; + } + } else { //nexttbtt <= tsftu + uint32_t countdiff, oldtbtt, remainder; + + oldtbtt = nexttbtt; + remainder = (tsftu - nexttbtt) % intval; + nexttbtt = tsftu - remainder + intval; + countdiff = (nexttbtt - oldtbtt) / intval % dtimperiod; + if (dtimcount > countdiff) { + dtimcount -= countdiff; + } else { + dtimcount += dtimperiod - countdiff; + } + } + + DPRINTF(sc, ATH_DEBUG_BEACON, + "%s: adj nexttbtt=%llu, rx tsf delta=%lld\n", + __func__, + (unsigned long long) nexttbtt, + (long long) ((long long)nexttbtt * 1024LL) - (long long)tsf); + + memset(&bs, 0, sizeof(bs)); + bs.bs_intval = intval; + bs.bs_nexttbtt = nexttbtt; + bs.bs_dtimperiod = dtimperiod*intval; + bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval; + bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod; + bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod; + bs.bs_cfpmaxduration = 0; +#if 0 + /* + * The 802.11 layer records the offset to the DTIM + * bitmap while receiving beacons; use it here to + * enable h/w detection of our AID being marked in + * the bitmap vector (to indicate frames for us are + * pending at the AP). + * XXX do DTIM handling in s/w to WAR old h/w bugs + * XXX enable based on h/w rev for newer chips + */ + bs.bs_timoffset = ni->ni_timoff; +#endif + /* + * Calculate the number of consecutive beacons to miss + * before taking a BMISS interrupt. + * Note that we clamp the result to at most 10 beacons. + */ + bs.bs_bmissthreshold = vap->iv_bmissthreshold; + if (bs.bs_bmissthreshold > 10) + bs.bs_bmissthreshold = 10; + else if (bs.bs_bmissthreshold <= 0) + bs.bs_bmissthreshold = 1; + + /* + * Calculate sleep duration. The configuration is + * given in ms. We insure a multiple of the beacon + * period is used. Also, if the sleep duration is + * greater than the DTIM period then it makes senses + * to make it a multiple of that. + * + * XXX fixed at 100ms + */ + bs.bs_sleepduration = + roundup(IEEE80211_MS_TO_TU(100), bs.bs_intval); + if (bs.bs_sleepduration > bs.bs_dtimperiod) + bs.bs_sleepduration = roundup(bs.bs_sleepduration, bs.bs_dtimperiod); + + DPRINTF(sc, ATH_DEBUG_BEACON, + "%s: tsf %ju tsf:tu %u intval %u nexttbtt %u dtim %u " + "nextdtim %u bmiss %u sleep %u cfp:period %u " + "maxdur %u next %u timoffset %u\n" + , __func__ + , tsf + , tsftu + , bs.bs_intval + , bs.bs_nexttbtt + , bs.bs_dtimperiod + , bs.bs_nextdtim + , bs.bs_bmissthreshold + , bs.bs_sleepduration + , bs.bs_cfpperiod + , bs.bs_cfpmaxduration + , bs.bs_cfpnext + , bs.bs_timoffset + ); + ath_hal_intrset(ah, 0); + ath_hal_beacontimers(ah, &bs); + sc->sc_imask |= HAL_INT_BMISS; + ath_hal_intrset(ah, sc->sc_imask); + } else { + ath_hal_intrset(ah, 0); + if (nexttbtt == intval) + intval |= HAL_BEACON_RESET_TSF; + if (ic->ic_opmode == IEEE80211_M_IBSS) { + /* + * In IBSS mode enable the beacon timers but only + * enable SWBA interrupts if we need to manually + * prepare beacon frames. Otherwise we use a + * self-linked tx descriptor and let the hardware + * deal with things. + */ + intval |= HAL_BEACON_ENA; + if (!sc->sc_hasveol) + sc->sc_imask |= HAL_INT_SWBA; + if ((intval & HAL_BEACON_RESET_TSF) == 0) { + /* + * Pull nexttbtt forward to reflect + * the current TSF. + */ + tsf = ath_hal_gettsf64(ah); + tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; + do { + nexttbtt += intval; + } while (nexttbtt < tsftu); + } + ath_beaconq_config(sc); + } else if (ic->ic_opmode == IEEE80211_M_HOSTAP || + ic->ic_opmode == IEEE80211_M_MBSS) { + /* + * In AP/mesh mode we enable the beacon timers + * and SWBA interrupts to prepare beacon frames. + */ + intval |= HAL_BEACON_ENA; + sc->sc_imask |= HAL_INT_SWBA; /* beacon prepare */ + ath_beaconq_config(sc); + } + + /* + * Now dirty things because for now, the EDMA HAL has + * nexttbtt and intval is TU/8. + */ + if (sc->sc_isedma) { + nexttbtt_u8 = (nexttbtt << 3); + intval_u8 = (intval << 3); + if (intval & HAL_BEACON_ENA) + intval_u8 |= HAL_BEACON_ENA; + if (intval & HAL_BEACON_RESET_TSF) + intval_u8 |= HAL_BEACON_RESET_TSF; + ath_hal_beaconinit(ah, nexttbtt_u8, intval_u8); + } else + ath_hal_beaconinit(ah, nexttbtt, intval); + sc->sc_bmisscount = 0; + ath_hal_intrset(ah, sc->sc_imask); + /* + * When using a self-linked beacon descriptor in + * ibss mode load it once here. + */ + if (ic->ic_opmode == IEEE80211_M_IBSS && sc->sc_hasveol) + ath_beacon_start_adhoc(sc, vap); + } + ieee80211_free_node(ni); + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); +#undef FUDGE +#undef TSF_TO_TU +} diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_beacon.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_beacon.h new file mode 100644 index 0000000000..1de19a3242 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_beacon.h @@ -0,0 +1,54 @@ +/*- + * 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. + * + * $FreeBSD: releng/11.1/sys/dev/ath/if_ath_beacon.h 265115 2014-04-30 02:19:41Z adrian $ + */ +#ifndef __IF_ATH_BEACON_H__ +#define __IF_ATH_BEACON_H__ + +extern int ath_bstuck_threshold; + +extern int ath_beaconq_setup(struct ath_softc *sc); +extern int ath_beaconq_config(struct ath_softc *sc); +extern void ath_beacon_config(struct ath_softc *sc, + struct ieee80211vap *vap); +extern struct ath_buf * ath_beacon_generate(struct ath_softc *sc, + struct ieee80211vap *vap); +extern void ath_beacon_cabq_start(struct ath_softc *sc); +extern int ath_wme_update(struct ieee80211com *ic); +extern void ath_beacon_update(struct ieee80211vap *vap, int item); +extern void ath_beacon_start_adhoc(struct ath_softc *sc, + struct ieee80211vap *vap); +extern int ath_beacon_alloc(struct ath_softc *sc, struct ieee80211_node *ni); +extern void ath_beacon_return(struct ath_softc *sc, struct ath_buf *bf); +extern void ath_beacon_free(struct ath_softc *sc); +extern void ath_beacon_proc(void *arg, int pending); +extern void ath_beacon_miss(struct ath_softc *sc); + +#endif + diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_btcoex.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_btcoex.c new file mode 100644 index 0000000000..8171483f3f --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_btcoex.c @@ -0,0 +1,485 @@ +/*- + * Copyright (c) 2013 Adrian Chadd + * 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. + * + * $FreeBSD: releng/11.1/sys/dev/ath/if_ath_btcoex.c 301307 2016-06-04 08:56:30Z adrian $ + */ +#include +__FBSDID("$FreeBSD: releng/11.1/sys/dev/ath/if_ath_btcoex.c 301307 2016-06-04 08:56:30Z adrian $"); + +/* + * This implements some very basic bluetooth coexistence methods for + * the ath(4) hardware. + */ +#include "opt_ath.h" +#include "opt_inet.h" +#include "opt_wlan.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include /* XXX for ether_sprintf */ + +#include + +#include + +#ifdef INET +#include +#include +#endif + +#include +#include +#include + +MALLOC_DECLARE(M_ATHDEV); + +/* + * Initial AR9285 / (WB195) bluetooth coexistence settings, + * just for experimentation. + * + * Return 0 for OK; errno for error. + * + * XXX TODO: There needs to be a PCIe workaround to disable ASPM if + * bluetooth coexistence is enabled. + */ +static int +ath_btcoex_cfg_wb195(struct ath_softc *sc) +{ + HAL_BT_COEX_INFO btinfo; + HAL_BT_COEX_CONFIG btconfig; + struct ath_hal *ah = sc->sc_ah; + + if (! ath_hal_btcoex_supported(ah)) + return (EINVAL); + + bzero(&btinfo, sizeof(btinfo)); + bzero(&btconfig, sizeof(btconfig)); + + device_printf(sc->sc_dev, "Enabling WB195 BTCOEX\n"); + + btinfo.bt_module = HAL_BT_MODULE_JANUS; + btinfo.bt_coex_config = HAL_BT_COEX_CFG_3WIRE; + /* + * These are the three GPIO pins hooked up between the AR9285 and + * the AR3011. + */ + btinfo.bt_gpio_bt_active = 6; + btinfo.bt_gpio_bt_priority = 7; + btinfo.bt_gpio_wlan_active = 5; + btinfo.bt_active_polarity = 1; /* XXX not used */ + btinfo.bt_single_ant = 1; /* 1 antenna on ar9285 ? */ + btinfo.bt_isolation = 0; /* in dB, not used */ + + ath_hal_btcoex_set_info(ah, &btinfo); + + btconfig.bt_time_extend = 0; + btconfig.bt_txstate_extend = 1; /* true */ + btconfig.bt_txframe_extend = 1; /* true */ + btconfig.bt_mode = HAL_BT_COEX_MODE_SLOTTED; + btconfig.bt_quiet_collision = 1; /* true */ + btconfig.bt_rxclear_polarity = 1; /* true */ + btconfig.bt_priority_time = 2; + btconfig.bt_first_slot_time = 5; + btconfig.bt_hold_rxclear = 1; /* true */ + + ath_hal_btcoex_set_config(ah, &btconfig); + + /* + * Enable antenna diversity. + */ + ath_hal_btcoex_set_parameter(ah, HAL_BT_COEX_ANTENNA_DIVERSITY, 1); + + return (0); +} + +/* + * Initial AR9485 / (WB225) bluetooth coexistence settings, + * just for experimentation. + * + * Return 0 for OK; errno for error. + */ +static int +ath_btcoex_cfg_wb225(struct ath_softc *sc) +{ + HAL_BT_COEX_INFO btinfo; + HAL_BT_COEX_CONFIG btconfig; + struct ath_hal *ah = sc->sc_ah; + + if (! ath_hal_btcoex_supported(ah)) + return (EINVAL); + + bzero(&btinfo, sizeof(btinfo)); + bzero(&btconfig, sizeof(btconfig)); + + device_printf(sc->sc_dev, "Enabling WB225 BTCOEX\n"); + + btinfo.bt_module = HAL_BT_MODULE_JANUS; /* XXX not used? */ + btinfo.bt_coex_config = HAL_BT_COEX_CFG_3WIRE; + /* + * These are the three GPIO pins hooked up between the AR9485 and + * the bluetooth module. + */ + btinfo.bt_gpio_bt_active = 4; + btinfo.bt_gpio_bt_priority = 8; + btinfo.bt_gpio_wlan_active = 5; + + btinfo.bt_active_polarity = 1; /* XXX not used */ + btinfo.bt_single_ant = 1; /* 1 antenna on ar9285 ? */ + btinfo.bt_isolation = 0; /* in dB, not used */ + + ath_hal_btcoex_set_info(ah, &btinfo); + + btconfig.bt_time_extend = 0; + btconfig.bt_txstate_extend = 1; /* true */ + btconfig.bt_txframe_extend = 1; /* true */ + btconfig.bt_mode = HAL_BT_COEX_MODE_SLOTTED; + btconfig.bt_quiet_collision = 1; /* true */ + btconfig.bt_rxclear_polarity = 1; /* true */ + btconfig.bt_priority_time = 2; + btconfig.bt_first_slot_time = 5; + btconfig.bt_hold_rxclear = 1; /* true */ + + ath_hal_btcoex_set_config(ah, &btconfig); + + /* + * Enable antenna diversity. + */ + ath_hal_btcoex_set_parameter(ah, HAL_BT_COEX_ANTENNA_DIVERSITY, 1); + + return (0); +} + +static int +ath_btcoex_cfg_mci(struct ath_softc *sc, uint32_t mci_cfg, int do_btdiv) +{ + HAL_BT_COEX_INFO btinfo; + HAL_BT_COEX_CONFIG btconfig; + struct ath_hal *ah = sc->sc_ah; + + if (! ath_hal_btcoex_supported(ah)) + return (EINVAL); + + bzero(&btinfo, sizeof(btinfo)); + bzero(&btconfig, sizeof(btconfig)); + + sc->sc_ah->ah_config.ath_hal_mci_config = mci_cfg; + + if (ath_btcoex_mci_attach(sc) != 0) { + device_printf(sc->sc_dev, "Failed to setup btcoex\n"); + return (EINVAL); + } + + btinfo.bt_module = HAL_BT_MODULE_JANUS; /* XXX not used? */ + btinfo.bt_coex_config = HAL_BT_COEX_CFG_MCI; + + /* + * MCI uses a completely different interface to speak + * to the bluetooth module - it's a command based + * thing over a serial line, rather than + * state pins to/from the bluetooth module. + * + * So, the GPIO configuration, polarity, etc + * doesn't matter on MCI devices; it's just + * completely ignored by the HAL. + */ + btinfo.bt_gpio_bt_active = 4; + btinfo.bt_gpio_bt_priority = 8; + btinfo.bt_gpio_wlan_active = 5; + + btinfo.bt_active_polarity = 1; /* XXX not used */ + btinfo.bt_single_ant = 0; /* 2 antenna on WB335 */ + btinfo.bt_isolation = 0; /* in dB, not used */ + + ath_hal_btcoex_set_info(ah, &btinfo); + + btconfig.bt_time_extend = 0; + btconfig.bt_txstate_extend = 1; /* true */ + btconfig.bt_txframe_extend = 1; /* true */ + btconfig.bt_mode = HAL_BT_COEX_MODE_SLOTTED; + btconfig.bt_quiet_collision = 1; /* true */ + btconfig.bt_rxclear_polarity = 1; /* true */ + btconfig.bt_priority_time = 2; + btconfig.bt_first_slot_time = 5; + btconfig.bt_hold_rxclear = 1; /* true */ + + ath_hal_btcoex_set_config(ah, &btconfig); + + /* Enable */ + ath_hal_btcoex_enable(sc->sc_ah); + + /* Stomp */ + ath_hal_btcoex_set_weights(ah, HAL_BT_COEX_STOMP_NONE); + + /* + * Enable antenna diversity. + */ + ath_hal_btcoex_set_parameter(ah, HAL_BT_COEX_ANTENNA_DIVERSITY, + do_btdiv); + + return (0); +} + +/* + * Initial AR9462 / (WB222) bluetooth coexistence settings. + * + * Return 0 for OK; errno for error. + */ +static int +ath_btcoex_cfg_wb222(struct ath_softc *sc) +{ + + device_printf(sc->sc_dev, "Enabling WB222 BTCOEX\n"); + /* XXX from ath9k */ + return (ath_btcoex_cfg_mci(sc, 0x2201, 1)); +} + +/* + * Initial QCA9565 / (WB335B) bluetooth coexistence settings. + * + * Return 0 for OK; errno for error. + */ +static int +ath_btcoex_cfg_wb335b(struct ath_softc *sc) +{ + uint32_t flags; + int do_btdiv = 0; + + /* ath9k default */ + flags = 0xa4c1; + + /* 1-ant and 2-ant AR9565 */ + /* + * XXX TODO: ensure these actually make it down to the + * HAL correctly! + */ + if (sc->sc_pci_devinfo & ATH_PCI_AR9565_1ANT) { + flags &= ~ATH_MCI_CONFIG_ANT_ARCH; + flags |= ATH_MCI_ANT_ARCH_1_ANT_PA_LNA_SHARED << + ATH_MCI_CONFIG_ANT_ARCH_S; + } else if (sc->sc_pci_devinfo & ATH_PCI_AR9565_2ANT) { + flags &= ~ATH_MCI_CONFIG_ANT_ARCH; + flags |= ATH_MCI_ANT_ARCH_2_ANT_PA_LNA_NON_SHARED << + ATH_MCI_CONFIG_ANT_ARCH_S; + } + + if (sc->sc_pci_devinfo & ATH_PCI_BT_ANT_DIV) { + do_btdiv = 1; + } + + device_printf(sc->sc_dev, "Enabling WB335 BTCOEX\n"); + /* XXX from ath9k */ + return (ath_btcoex_cfg_mci(sc, flags, do_btdiv)); +} + +#if 0 +/* + * When using bluetooth coexistence, ASPM needs to be disabled + * otherwise the sleeping interferes with the bluetooth (USB) + * operation and the MAC sleep/wakeup hardware. + * + * The PCIe powersave routine also needs to not be called + * by the driver during suspend/resume, else things will get + * a little odd. Check Linux ath9k for more details. + */ +static int +ath_btcoex_aspm_wb195(struct ath_softc *sc) +{ + + /* XXX TODO: clear device ASPM L0S and L1 */ + /* XXX TODO: clear _parent_ ASPM L0S and L1 */ +} +#endif + +/* + * Methods which are required + */ + +/* + * Attach btcoex to the given interface + */ +int +ath_btcoex_attach(struct ath_softc *sc) +{ + int ret; + struct ath_hal *ah = sc->sc_ah; + const char *profname; + + /* + * No chipset bluetooth coexistence? Then do nothing. + */ + if (! ath_hal_btcoex_supported(ah)) + return (0); + + /* + * Look at the hints to determine which bluetooth + * profile to configure. + */ +#ifndef __HAIKU__ + ret = resource_string_value(device_get_name(sc->sc_dev), + device_get_unit(sc->sc_dev), + "btcoex_profile", + &profname); +#else + ret = 1; +#endif + if (ret != 0) { + /* nothing to do */ + return (0); + } + + if (strncmp(profname, "wb195", 5) == 0) { + ret = ath_btcoex_cfg_wb195(sc); + } else if (strncmp(profname, "wb222", 5) == 0) { + ret = ath_btcoex_cfg_wb222(sc); + } else if (strncmp(profname, "wb225", 5) == 0) { + ret = ath_btcoex_cfg_wb225(sc); + } else if (strncmp(profname, "wb335", 5) == 0) { + ret = ath_btcoex_cfg_wb335b(sc); + } else { + return (0); + } + + /* + * Propagate up failure from the actual attach phase. + */ + if (ret != 0) + return (ret); + + return (0); +} + +/* + * Detach btcoex from the given interface + */ +int +ath_btcoex_detach(struct ath_softc *sc) +{ + if (sc->sc_btcoex_mci) { + ath_btcoex_mci_detach(sc); + } + + return (0); +} + +/* + * Configure or disable bluetooth coexistence on the given channel. + * + * For AR9285/AR9287/AR9485, we'll never see a 5GHz channel, so we just + * assume bluetooth coexistence is always on. + * + * For AR9462, we may see a 5GHz channel; bluetooth coexistence should + * not be enabled on those channels. + */ +int +ath_btcoex_enable(struct ath_softc *sc, const struct ieee80211_channel *chan) +{ + if (sc->sc_btcoex_mci) { + ath_btcoex_mci_enable(sc, chan); + } + + return (0); +} + +/* + * Handle ioctl requests from the diagnostic interface. + * + * The initial part of this code resembles ath_ioctl_diag(); + * it's likely a good idea to reduce duplication between + * these two routines. + */ +int +ath_btcoex_ioctl(struct ath_softc *sc, struct ath_diag *ad) +{ + unsigned int id = ad->ad_id & ATH_DIAG_ID; + void *indata = NULL; + void *outdata = NULL; + u_int32_t insize = ad->ad_in_size; + u_int32_t outsize = ad->ad_out_size; + int error = 0; +// int val; + + if (ad->ad_id & ATH_DIAG_IN) { + /* + * Copy in data. + */ + indata = malloc(insize, M_TEMP, M_NOWAIT); + if (indata == NULL) { + error = ENOMEM; + goto bad; + } + error = copyin(ad->ad_in_data, indata, insize); + if (error) + goto bad; + } + if (ad->ad_id & ATH_DIAG_DYN) { + /* + * Allocate a buffer for the results (otherwise the HAL + * returns a pointer to a buffer where we can read the + * results). Note that we depend on the HAL leaving this + * pointer for us to use below in reclaiming the buffer; + * may want to be more defensive. + */ + outdata = malloc(outsize, M_TEMP, M_NOWAIT); + if (outdata == NULL) { + error = ENOMEM; + goto bad; + } + } + switch (id) { + default: + error = EINVAL; + } + if (outsize < ad->ad_out_size) + ad->ad_out_size = outsize; + if (outdata && copyout(outdata, ad->ad_out_data, ad->ad_out_size)) + error = EFAULT; +bad: + if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL) + free(indata, M_TEMP); + if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL) + free(outdata, M_TEMP); + return (error); +} + diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_btcoex.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_btcoex.h new file mode 100644 index 0000000000..c1a95250c2 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_btcoex.h @@ -0,0 +1,44 @@ +/*- + * Copyright (c) 2013 Adrian Chadd + * 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. + * + * $FreeBSD: releng/11.1/sys/dev/ath/if_ath_btcoex.h 301181 2016-06-02 00:51:36Z adrian $ + */ +#ifndef __IF_ATH_BTCOEX_H__ +#define __IF_ATH_BTCOEX_H__ + +typedef enum { + ATH_COEX_EVENT_BT_NOOP, +} ATH_BT_COEX_EVENT; + +extern int ath_btcoex_attach(struct ath_softc *sc); +extern int ath_btcoex_detach(struct ath_softc *sc); +extern int ath_btcoex_ioctl(struct ath_softc *sc, struct ath_diag *ad); +extern int ath_btcoex_enable(struct ath_softc *sc, + const struct ieee80211_channel *ch); + +#endif /* __IF_ATH_BTCOEX_H__ */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_btcoex_mci.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_btcoex_mci.c new file mode 100644 index 0000000000..cc9b4d0e16 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_btcoex_mci.c @@ -0,0 +1,655 @@ +/*- + * Copyright (c) 2014 Qualcomm Atheros, Inc. + * Copyright (c) 2016 Adrian Chadd + * 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. + * + * $FreeBSD: releng/11.1/sys/dev/ath/if_ath_btcoex_mci.c 301182 2016-06-02 01:59:41Z gnn $ + */ +#include +__FBSDID("$FreeBSD: releng/11.1/sys/dev/ath/if_ath_btcoex_mci.c 301182 2016-06-02 01:59:41Z gnn $"); + +/* + * This implements the MCI bluetooth coexistence handling. + */ +#include "opt_ath.h" +#include "opt_inet.h" +#include "opt_wlan.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include /* XXX for ether_sprintf */ + +#include + +#include + +#ifdef INET +#include +#include +#endif + +#include +#include +#include +#include + +#include + +MALLOC_DECLARE(M_ATHDEV); + +#define ATH_MCI_GPM_MAX_ENTRY 16 +#define ATH_MCI_GPM_BUF_SIZE (ATH_MCI_GPM_MAX_ENTRY * 16) +#define ATH_MCI_SCHED_BUF_SIZE (16 * 16) /* 16 entries, 4 dword each */ + +static void ath_btcoex_mci_update_wlan_channels(struct ath_softc *sc); + +int +ath_btcoex_mci_attach(struct ath_softc *sc) +{ + int buflen, error; + + buflen = ATH_MCI_GPM_BUF_SIZE + ATH_MCI_SCHED_BUF_SIZE; + error = ath_descdma_alloc_desc(sc, &sc->sc_btcoex.buf, NULL, + "MCI bufs", buflen, 1); + if (error != 0) { + device_printf(sc->sc_dev, "%s: failed to alloc MCI RAM\n", + __func__); + return (error); + } + + /* Yes, we're going to do bluetooth MCI coex */ + sc->sc_btcoex_mci = 1; + + /* Initialise the wlan channel mapping */ + sc->sc_btcoex.wlan_channels[0] = 0x00000000; + sc->sc_btcoex.wlan_channels[1] = 0xffffffff; + sc->sc_btcoex.wlan_channels[2] = 0xffffffff; + sc->sc_btcoex.wlan_channels[3] = 0x7fffffff; + + /* + * Ok, so the API is a bit odd. It assumes sched_addr is + * after gpm_addr, and it does math to figure out the right + * sched_buf pointer. + * + * So, set gpm_addr to buf, sched_addr to gpm_addr + ATH_MCI_GPM_BUF_SIZE, + * the HAL call with do (gpm_buf + (sched_addr - gpm_addr)) to + * set sched_buf, and we're "golden". + * + * Note, it passes in 'len' here (gpm_len) as + * ATH_MCI_GPM_BUF_SIZE >> 4. My guess is that it's 16 + * bytes per entry and we're storing 16 entries. + */ + sc->sc_btcoex.gpm_buf = (void *) sc->sc_btcoex.buf.dd_desc; + sc->sc_btcoex.sched_buf = sc->sc_btcoex.gpm_buf + + ATH_MCI_GPM_BUF_SIZE; + + sc->sc_btcoex.gpm_paddr = sc->sc_btcoex.buf.dd_desc_paddr; + sc->sc_btcoex.sched_paddr = sc->sc_btcoex.gpm_paddr + + ATH_MCI_GPM_BUF_SIZE; + + /* memset the gpm buffer with MCI_GPM_RSVD_PATTERN */ + memset(sc->sc_btcoex.gpm_buf, 0xfe, buflen); + + /* + * This is an unfortunate x86'ism in the HAL - the + * HAL code expects the passed in buffer to be + * coherent, and doesn't implement /any/ kind + * of buffer sync operations at all. + * + * So, this code will only work on dma coherent buffers + * and will behave poorly on non-coherent systems. + * Fixing this would require some HAL surgery so it + * actually /did/ the buffer flushing as appropriate. + */ + ath_hal_btcoex_mci_setup(sc->sc_ah, + sc->sc_btcoex.gpm_paddr, + sc->sc_btcoex.gpm_buf, + ATH_MCI_GPM_BUF_SIZE >> 4, + sc->sc_btcoex.sched_paddr); + + return (0); +} + +/* + * Detach btcoex from the given interface + */ +int +ath_btcoex_mci_detach(struct ath_softc *sc) +{ + + ath_hal_btcoex_mci_detach(sc->sc_ah); + ath_descdma_cleanup(sc, &sc->sc_btcoex.buf, NULL); + return (0); +} + +/* + * Configure or disable bluetooth coexistence on the given channel. + * + * For MCI, we just use the top-level enable/disable flag, and + * then the MCI reset / channel update path will configure things + * appropriately based on the current band. + */ +int +ath_btcoex_mci_enable(struct ath_softc *sc, + const struct ieee80211_channel *chan) +{ + + /* + * Always reconfigure stomp-all for now, so wlan wins. + * + * The default weights still don't allow beacons to win, + * so unless you set net.wlan.X.bmiss_max to something higher, + * net80211 will disconnect you during a HCI INQUIRY command. + * + * The longer-term solution is to dynamically adjust whether + * bmiss happens based on bluetooth requirements, and look at + * making the individual stomp bits configurable. + */ + ath_hal_btcoex_set_weights(sc->sc_ah, HAL_BT_COEX_STOMP_ALL); + + /* + * update wlan channels so the firmware knows what channels it + * can/can't use. + */ + ath_btcoex_mci_update_wlan_channels(sc); + + return (0); +} + +/* + * XXX TODO: turn into general btcoex, and then make this + * the MCI specific bits. + */ +static void +ath_btcoex_mci_event(struct ath_softc *sc, ATH_BT_COEX_EVENT nevent, + void *param) +{ + + if (! sc->sc_btcoex_mci) + return; + + /* + * Check whether we need to flush our local profile cache. + * If we do, then at (XXX TODO) we should flush our state, + * then wait for the MCI response with the updated profile list. + */ + if (ath_hal_btcoex_mci_state(sc->sc_ah, + HAL_MCI_STATE_NEED_FLUSH_BT_INFO, NULL) != 0) { + uint32_t data = 0; + + if (ath_hal_btcoex_mci_state(sc->sc_ah, + HAL_MCI_STATE_ENABLE, NULL) != 0) { + DPRINTF(sc, ATH_DEBUG_BTCOEX, + "(MCI) Flush BT profile\n"); + /* + * XXX TODO: flush profile state on the ath(4) + * driver side; subsequent messages will come + * through with the current list of active + * profiles. + */ + ath_hal_btcoex_mci_state(sc->sc_ah, + HAL_MCI_STATE_NEED_FLUSH_BT_INFO, &data); + ath_hal_btcoex_mci_state(sc->sc_ah, + HAL_MCI_STATE_SEND_STATUS_QUERY, NULL); + } + } + if (nevent == ATH_COEX_EVENT_BT_NOOP) { + DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) BT_NOOP\n"); + return; + } +} + +static void +ath_btcoex_mci_send_gpm(struct ath_softc *sc, uint32_t *payload) +{ + + ath_hal_btcoex_mci_send_message(sc->sc_ah, MCI_GPM, 0, payload, 16, + AH_FALSE, AH_TRUE); +} + +/* + * This starts a BT calibration. It requires a chip reset. + */ +static int +ath_btcoex_mci_bt_cal_do(struct ath_softc *sc, int tx_timeout, int rx_timeout) +{ + + device_printf(sc->sc_dev, "%s: TODO!\n", __func__); + return (0); +} + +static void +ath_btcoex_mci_cal_msg(struct ath_softc *sc, uint8_t opcode, + uint8_t *rx_payload) +{ + uint32_t payload[4] = {0, 0, 0, 0}; + + switch (opcode) { + case MCI_GPM_BT_CAL_REQ: + DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) receive BT_CAL_REQ\n"); + if (ath_hal_btcoex_mci_state(sc->sc_ah, HAL_MCI_STATE_BT, + NULL) == MCI_BT_AWAKE) { + ath_hal_btcoex_mci_state(sc->sc_ah, + HAL_MCI_STATE_SET_BT_CAL_START, NULL); + ath_btcoex_mci_bt_cal_do(sc, 1000, 1000); + } else { + DPRINTF(sc, ATH_DEBUG_BTCOEX, + "(MCI) State mismatches: %d\n", + ath_hal_btcoex_mci_state(sc->sc_ah, + HAL_MCI_STATE_BT, NULL)); + } + break; + case MCI_GPM_BT_CAL_DONE: + DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) receive BT_CAL_DONE\n"); + if (ath_hal_btcoex_mci_state(sc->sc_ah, HAL_MCI_STATE_BT, + NULL) == MCI_BT_CAL) { + DPRINTF(sc, ATH_DEBUG_BTCOEX, + "(MCI) ERROR ILLEGAL!\n"); + } else { + DPRINTF(sc, ATH_DEBUG_BTCOEX, + "(MCI) BT not in CAL state.\n"); + } + break; + case MCI_GPM_BT_CAL_GRANT: + DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) receive BT_CAL_GRANT\n"); + /* Send WLAN_CAL_DONE for now */ + DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) Send WLAN_CAL_DONE\n"); + MCI_GPM_SET_CAL_TYPE(payload, MCI_GPM_WLAN_CAL_DONE); + ath_btcoex_mci_send_gpm(sc, &payload[0]); + break; + default: + DPRINTF(sc, ATH_DEBUG_BTCOEX, + "(MCI) Unknown GPM CAL message.\n"); + break; + } +} + +/* + * Update the bluetooth channel map. + * + * This map tells the bluetooth device which bluetooth channels + * are available for data. + * + * For 5GHz, all channels are available. + * For 2GHz, the current wifi channel range is blocked out, + * and the rest are available. + * + * This narrows which frequencies are used by the device when + * it initiates a transfer, thus hopefully reducing the chances + * of collisions (both hopefully on the current device and + * other devices in the same channel.) + */ +static void +ath_btcoex_mci_update_wlan_channels(struct ath_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_channel *chan = ic->ic_curchan; + uint32_t channel_info[4] = + { 0x00000000, 0xffffffff, 0xffffffff, 0x7fffffff }; + int32_t wl_chan, bt_chan, bt_start = 0, bt_end = 79; + + /* BT channel frequency is 2402 + k, k = 0 ~ 78 */ + if (IEEE80211_IS_CHAN_2GHZ(chan)) { + wl_chan = chan->ic_freq - 2402; + if (IEEE80211_IS_CHAN_HT40U(chan)) { + bt_start = wl_chan - 10; + bt_end = wl_chan + 30; + } else if (IEEE80211_IS_CHAN_HT40D(chan)) { + bt_start = wl_chan - 30; + bt_end = wl_chan + 10; + } else { + /* Assume 20MHz */ + bt_start = wl_chan - 10; + bt_end = wl_chan + 10; + } + + bt_start -= 7; + bt_end += 7; + + if (bt_start < 0) { + bt_start = 0; + } + if (bt_end > MCI_NUM_BT_CHANNELS) { + bt_end = MCI_NUM_BT_CHANNELS; + } + DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) WLAN use channel %d\n", + chan->ic_freq); + DPRINTF(sc, ATH_DEBUG_BTCOEX, + "(MCI) mask BT channel %d - %d\n", bt_start, bt_end); + for (bt_chan = bt_start; bt_chan < bt_end; bt_chan++) { + MCI_GPM_CLR_CHANNEL_BIT(&channel_info[0], bt_chan); + } + } else { + DPRINTF(sc, ATH_DEBUG_BTCOEX, + "(MCI) WLAN not use any 2G channel, unmask all for BT\n"); + } + ath_hal_btcoex_mci_state(sc->sc_ah, HAL_MCI_STATE_SEND_WLAN_CHANNELS, + &channel_info[0]); +} + +static void +ath_btcoex_mci_coex_msg(struct ath_softc *sc, uint8_t opcode, + uint8_t *rx_payload) +{ + uint32_t version; + uint8_t major; + uint8_t minor; + uint32_t seq_num; + + switch (opcode) { + case MCI_GPM_COEX_VERSION_QUERY: + DPRINTF(sc, ATH_DEBUG_BTCOEX, + "(MCI) Recv GPM COEX Version Query.\n"); + version = ath_hal_btcoex_mci_state(sc->sc_ah, + HAL_MCI_STATE_SEND_WLAN_COEX_VERSION, NULL); + break; + + case MCI_GPM_COEX_VERSION_RESPONSE: + DPRINTF(sc, ATH_DEBUG_BTCOEX, + "(MCI) Recv GPM COEX Version Response.\n"); + major = *(rx_payload + MCI_GPM_COEX_B_MAJOR_VERSION); + minor = *(rx_payload + MCI_GPM_COEX_B_MINOR_VERSION); + DPRINTF(sc, ATH_DEBUG_BTCOEX, + "(MCI) BT Coex version: %d.%d\n", major, minor); + version = (major << 8) + minor; + version = ath_hal_btcoex_mci_state(sc->sc_ah, + HAL_MCI_STATE_SET_BT_COEX_VERSION, &version); + break; + + case MCI_GPM_COEX_STATUS_QUERY: + DPRINTF(sc, ATH_DEBUG_BTCOEX, + "(MCI) Recv GPM COEX Status Query = 0x%02x.\n", + *(rx_payload + MCI_GPM_COEX_B_WLAN_BITMAP)); + ath_hal_btcoex_mci_state(sc->sc_ah, + HAL_MCI_STATE_SEND_WLAN_CHANNELS, NULL); + break; + + case MCI_GPM_COEX_BT_PROFILE_INFO: + /* + * XXX TODO: here is where we'd parse active profile + * info and make driver/stack choices as appropriate. + */ + DPRINTF(sc, ATH_DEBUG_BTCOEX, + "(MCI) TODO: Recv GPM COEX BT_Profile_Info.\n"); + break; + + case MCI_GPM_COEX_BT_STATUS_UPDATE: + seq_num = *((uint32_t *)(rx_payload + 12)); + DPRINTF(sc, ATH_DEBUG_BTCOEX, + "(MCI) Recv GPM COEX BT_Status_Update: SEQ=%d\n", + seq_num); + break; + + default: + DPRINTF(sc, ATH_DEBUG_BTCOEX, + "(MCI) Unknown GPM COEX message = 0x%02x\n", opcode); + break; + } +} + +void +ath_btcoex_mci_intr(struct ath_softc *sc) +{ + uint32_t mciInt, mciIntRxMsg; + uint32_t offset, subtype, opcode; + uint32_t *pGpm; + uint32_t more_data = HAL_MCI_GPM_MORE; + int8_t value_dbm; + bool skip_gpm = false; + + DPRINTF(sc, ATH_DEBUG_BTCOEX, "%s: called\n", __func__); + + ath_hal_btcoex_mci_get_interrupt(sc->sc_ah, &mciInt, &mciIntRxMsg); + + if (ath_hal_btcoex_mci_state(sc->sc_ah, HAL_MCI_STATE_ENABLE, + NULL) == 0) { + ath_hal_btcoex_mci_state(sc->sc_ah, + HAL_MCI_STATE_INIT_GPM_OFFSET, NULL); + DPRINTF(sc, ATH_DEBUG_BTCOEX, + "(MCI) INTR but MCI_disabled\n"); + DPRINTF(sc, ATH_DEBUG_BTCOEX, + "(MCI) MCI interrupt: mciInt = 0x%x, mciIntRxMsg = 0x%x\n", + mciInt, mciIntRxMsg); + return; + } + + if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_REQ_WAKE) { + uint32_t payload4[4] = { 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffff00}; + + /* + * The following REMOTE_RESET and SYS_WAKING used to sent + * only when BT wake up. Now they are always sent, as a + * recovery method to reset BT MCI's RX alignment. + */ + DPRINTF(sc, ATH_DEBUG_BTCOEX, + "(MCI) 1. INTR Send REMOTE_RESET\n"); + ath_hal_btcoex_mci_send_message(sc->sc_ah, + MCI_REMOTE_RESET, 0, payload4, 16, AH_TRUE, AH_FALSE); + DPRINTF(sc, ATH_DEBUG_BTCOEX, + "(MCI) 1. INTR Send SYS_WAKING\n"); + ath_hal_btcoex_mci_send_message(sc->sc_ah, + MCI_SYS_WAKING, 0, NULL, 0, AH_TRUE, AH_FALSE); + + mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_REQ_WAKE; + ath_hal_btcoex_mci_state(sc->sc_ah, + HAL_MCI_STATE_RESET_REQ_WAKE, NULL); + + /* always do this for recovery and 2G/5G toggling and LNA_TRANS */ + DPRINTF(sc, ATH_DEBUG_BTCOEX, + "(MCI) 1. Set BT state to AWAKE.\n"); + ath_hal_btcoex_mci_state(sc->sc_ah, + HAL_MCI_STATE_SET_BT_AWAKE, NULL); + } + + /* Processing SYS_WAKING/SYS_SLEEPING */ + if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_SYS_WAKING) { + mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_SYS_WAKING; + if (ath_hal_btcoex_mci_state(sc->sc_ah, HAL_MCI_STATE_BT, + NULL) == MCI_BT_SLEEP) { + if (ath_hal_btcoex_mci_state(sc->sc_ah, + HAL_MCI_STATE_REMOTE_SLEEP, NULL) == MCI_BT_SLEEP) { + DPRINTF(sc, ATH_DEBUG_BTCOEX, + "(MCI) 2. BT stays in SLEEP mode.\n"); + } else { + DPRINTF(sc, ATH_DEBUG_BTCOEX, + "(MCI) 2. Set BT state to AWAKE.\n"); + ath_hal_btcoex_mci_state(sc->sc_ah, + HAL_MCI_STATE_SET_BT_AWAKE, NULL); + } + } else { + DPRINTF(sc, ATH_DEBUG_BTCOEX, + "(MCI) 2. BT stays in AWAKE mode.\n"); + } + } + + if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING) { + mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING; + if (ath_hal_btcoex_mci_state(sc->sc_ah, HAL_MCI_STATE_BT, + NULL) == MCI_BT_AWAKE) { + if (ath_hal_btcoex_mci_state(sc->sc_ah, + HAL_MCI_STATE_REMOTE_SLEEP, NULL) == MCI_BT_AWAKE) { + DPRINTF(sc, ATH_DEBUG_BTCOEX, + "(MCI) 3. BT stays in AWAKE mode.\n"); + } else { + DPRINTF(sc, ATH_DEBUG_BTCOEX, + "(MCI) 3. Set BT state to SLEEP.\n"); + ath_hal_btcoex_mci_state(sc->sc_ah, + HAL_MCI_STATE_SET_BT_SLEEP, NULL); + } + } else { + DPRINTF(sc, ATH_DEBUG_BTCOEX, + "(MCI) 3. BT stays in SLEEP mode.\n"); + } + } + + /* + * Recover from out-of-order / wrong-offset GPM messages. + */ + if ((mciInt & HAL_MCI_INTERRUPT_RX_INVALID_HDR) || + (mciInt & HAL_MCI_INTERRUPT_CONT_INFO_TIMEOUT)) { + DPRINTF(sc, ATH_DEBUG_BTCOEX, + "(MCI) MCI RX broken, skip GPM messages\n"); + ath_hal_btcoex_mci_state(sc->sc_ah, + HAL_MCI_STATE_RECOVER_RX, NULL); + skip_gpm = true; + } + + if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_SCHD_INFO) { + mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_SCHD_INFO; + offset = ath_hal_btcoex_mci_state(sc->sc_ah, + HAL_MCI_STATE_LAST_SCHD_MSG_OFFSET, NULL); + } + + /* + * Parse GPM messages. + */ + if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_GPM) { + mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_GPM; + + while (more_data == HAL_MCI_GPM_MORE) { + pGpm = (void *) sc->sc_btcoex.gpm_buf; + offset = ath_hal_btcoex_mci_state(sc->sc_ah, + HAL_MCI_STATE_NEXT_GPM_OFFSET, &more_data); + + if (offset == HAL_MCI_GPM_INVALID) + break; + pGpm += (offset >> 2); + /* + * The first DWORD is a timer. + * The real data starts from the second DWORD. + */ + subtype = MCI_GPM_TYPE(pGpm); + opcode = MCI_GPM_OPCODE(pGpm); + + if (!skip_gpm) { + if (MCI_GPM_IS_CAL_TYPE(subtype)) { + ath_btcoex_mci_cal_msg(sc, subtype, + (uint8_t*) pGpm); + } else { + switch (subtype) { + case MCI_GPM_COEX_AGENT: + ath_btcoex_mci_coex_msg(sc, + opcode, (uint8_t*) pGpm); + break; + case MCI_GPM_BT_DEBUG: + device_printf(sc->sc_dev, + "(MCI) TODO: GPM_BT_DEBUG!\n"); + break; + default: + DPRINTF(sc, ATH_DEBUG_BTCOEX, + "(MCI) Unknown GPM message.\n"); + break; + } + } + } + MCI_GPM_RECYCLE(pGpm); + } + } + + /* + * This is monitoring/management information messages, so the driver + * layer can hook in and dynamically adjust things like aggregation + * size, expected bluetooth/wifi traffic throughput, etc. + * + * None of that is done right now; it just passes off the values + * to the HAL so it can update its internal state as appropriate. + * This code just prints out the values for debugging purposes. + */ + if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_MONITOR) { + if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_LNA_CONTROL) { + mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_LNA_CONTROL; + DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) LNA_CONTROL\n"); + } + if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_LNA_INFO) { + mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_LNA_INFO; + DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) LNA_INFO\n"); + } + if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_CONT_INFO) { + value_dbm = ath_hal_btcoex_mci_state(sc->sc_ah, + HAL_MCI_STATE_CONT_RSSI_POWER, NULL); + + mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_CONT_INFO; + if (ath_hal_btcoex_mci_state(sc->sc_ah, + HAL_MCI_STATE_CONT_TXRX, NULL)) { + DPRINTF(sc, ATH_DEBUG_BTCOEX, + "(MCI) CONT_INFO: (tx) pri = %d, pwr = %d dBm\n", + ath_hal_btcoex_mci_state(sc->sc_ah, + HAL_MCI_STATE_CONT_PRIORITY, NULL), + value_dbm); + } else { + DPRINTF(sc, ATH_DEBUG_BTCOEX, + "(MCI) CONT_INFO: (rx) pri = %d, rssi = %d dBm\n", + ath_hal_btcoex_mci_state(sc->sc_ah, + HAL_MCI_STATE_CONT_PRIORITY, NULL), + value_dbm); + } + } + if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_CONT_NACK) { + mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_CONT_NACK; + DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) CONT_NACK\n"); + } + if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_CONT_RST) { + mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_CONT_RST; + DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) CONT_RST\n"); + } + } + + /* + * Recover the state engine if we hit an invalid header/timeout. + * This is the final part of GPT out-of-sync recovery. + */ + if ((mciInt & HAL_MCI_INTERRUPT_RX_INVALID_HDR) || + (mciInt & HAL_MCI_INTERRUPT_CONT_INFO_TIMEOUT)) { + ath_btcoex_mci_event(sc, ATH_COEX_EVENT_BT_NOOP, NULL); + mciInt &= ~(HAL_MCI_INTERRUPT_RX_INVALID_HDR | + HAL_MCI_INTERRUPT_CONT_INFO_TIMEOUT); + } + + if (mciIntRxMsg & 0xfffffffe) { + DPRINTF(sc, ATH_DEBUG_BTCOEX, + "(MCI) Not processed IntRxMsg = 0x%x\n", mciIntRxMsg); + } +} diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_btcoex_mci.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_btcoex_mci.h new file mode 100644 index 0000000000..90dd8596e3 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_btcoex_mci.h @@ -0,0 +1,42 @@ +/*- + * Copyright (c) 2016 Adrian Chadd + * 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. + * + * $FreeBSD: releng/11.1/sys/dev/ath/if_ath_btcoex_mci.h 301181 2016-06-02 00:51:36Z adrian $ + */ +#ifndef __IF_ATH_BTCOEX_MCI_H__ +#define __IF_ATH_BTCOEX_MCI_H__ + +#define ATH_MCI_NUM_BT_CHANNELS 79 + +extern int ath_btcoex_mci_attach(struct ath_softc *sc); +extern int ath_btcoex_mci_detach(struct ath_softc *sc); +extern int ath_btcoex_mci_enable(struct ath_softc *sc, + const struct ieee80211_channel *chan); +extern void ath_btcoex_mci_intr(struct ath_softc *sc); + +#endif /* __IF_ATH_BTCOEX_MCI_H__ */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_debug.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_debug.c index b5691acfb6..5528c2ac86 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_debug.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_debug.c @@ -28,7 +28,7 @@ */ #include -__FBSDID("$FreeBSD$"); +__FBSDID("$FreeBSD: releng/11.1/sys/dev/ath/if_ath_debug.c 267992 2014-06-28 03:56:17Z hselasky $"); #include "opt_inet.h" #include "opt_ath.h" @@ -89,12 +89,11 @@ __FBSDID("$FreeBSD$"); #ifdef ATH_DEBUG #include -int ath_debug = 0; +uint64_t ath_debug = 0; SYSCTL_DECL(_hw_ath); -SYSCTL_INT(_hw_ath, OID_AUTO, debug, CTLFLAG_RW, &ath_debug, +SYSCTL_QUAD(_hw_ath, OID_AUTO, debug, CTLFLAG_RWTUN, &ath_debug, 0, "control debugging printfs"); -TUNABLE_INT("hw.ath.debug", &ath_debug); void ath_printrxbuf(struct ath_softc *sc, const struct ath_buf *bf, @@ -118,39 +117,146 @@ ath_printrxbuf(struct ath_softc *sc, const struct ath_buf *bf, ds->ds_hw[2], ds->ds_hw[3], ds->ds_hw[4], ds->ds_hw[5], ds->ds_hw[6], ds->ds_hw[7], ds->ds_hw[8]); + } else if (ah->ah_magic == 0x19741014) { + printf(" %08x %08x %08x %08x %08x %08x %08x\n", + ds->ds_hw[2], ds->ds_hw[3], ds->ds_hw[4], + ds->ds_hw[5], ds->ds_hw[6], ds->ds_hw[7], + ds->ds_hw[8]); + + printf(" %08x %08x %08x %08x %08x %08x %08x\n", + ds->ds_hw[9], ds->ds_hw[10], ds->ds_hw[11], + ds->ds_hw[12], ds->ds_hw[13], ds->ds_hw[14], + ds->ds_hw[15]); } } } -void -ath_printtxbuf(struct ath_softc *sc, const struct ath_buf *bf, +static void +ath_printtxbuf_edma(struct ath_softc *sc, const struct ath_buf *first_bf, u_int qnum, u_int ix, int done) { - const struct ath_tx_status *ts = &bf->bf_status.ds_txstat; + const struct ath_tx_status *ts = + &first_bf->bf_last->bf_status.ds_txstat; + const struct ath_buf *bf = first_bf; + const char *ds; + const struct ath_desc_txedma *eds; + int i, n; + + printf("Q%u[%3u] (nseg=%d)", qnum, ix, bf->bf_nseg); + while (bf != NULL) { + /* + * XXX For now, assume the txmap size is 4. + */ + + /* + * Assume the TX map size is 4 for now and only walk + * the appropriate number of segments. + */ + n = ((bf->bf_nseg - 1) / 4) + 1; + + for (i = 0, ds = (const char *) bf->bf_desc; + i < n; + i ++, ds += sc->sc_tx_desclen) { + eds = (const struct ath_desc_txedma *) ds; + printf(" (DS.V:%p DS.P:%p) I: %08x L:%08x F:%04x%s\n", + eds, (const struct ath_desc *)bf->bf_daddr + i, + eds->ds_info, eds->ds_link, + bf->bf_state.bfs_txflags, + !done ? "" : (ts->ts_status == 0) ? " *" : " !"); + printf(" (D[0] = %08x(%08x), D[1] = %08x(%08x)\n", + eds->ds_hw[0], eds->ds_hw[1], + eds->ds_hw[2], eds->ds_hw[3]); + printf(" (D[2] = %08x(%08x), D[3] = %08x(%08x)\n", + eds->ds_hw[4], eds->ds_hw[5], + eds->ds_hw[6], eds->ds_hw[7]); + printf(" Seq: %d swtry: %d ADDBAW?: %d DOBAW?: %d\n", + bf->bf_state.bfs_seqno, + bf->bf_state.bfs_retries, + bf->bf_state.bfs_addedbaw, + bf->bf_state.bfs_dobaw); + printf(" %08x %08x %08x %08x %08x %08x\n", + eds->ds_hw[8], eds->ds_hw[9], + eds->ds_hw[10], eds->ds_hw[11], + eds->ds_hw[12], eds->ds_hw[13]); + printf(" %08x %08x %08x %08x %08x %08x %08x\n", + eds->ds_hw[14], eds->ds_hw[15], eds->ds_hw[16], + eds->ds_hw[17], eds->ds_hw[18], eds->ds_hw[19], + eds->ds_hw[20]); +#if 0 + printf(" %08x %08x %08x %08x %08x %08x %08x %08x\n", + ds->ds_hw[22],ds->ds_hw[23],ds->ds_hw[24], + ds->ds_hw[25],ds->ds_hw[26],ds->ds_hw[27], + ds->ds_hw[28], ds->ds_hw[29]); +#endif + } + printf(" [end]\n"); + bf = bf->bf_next; + } +} + +static void +ath_printtxbuf_legacy(struct ath_softc *sc, const struct ath_buf *first_bf, + u_int qnum, u_int ix, int done) +{ + const struct ath_tx_status *ts = &first_bf->bf_status.ds_txstat; + const struct ath_buf *bf = first_bf; struct ath_hal *ah = sc->sc_ah; const struct ath_desc *ds; int i; printf("Q%u[%3u]", qnum, ix); - for (i = 0, ds = bf->bf_desc; i < bf->bf_nseg; i++, ds++) { - printf(" (DS.V:%p DS.P:%p) L:%08x D:%08x F:04%x%s\n" - " %08x %08x %08x %08x %08x %08x\n", - ds, (const struct ath_desc *)bf->bf_daddr + i, - ds->ds_link, ds->ds_data, bf->bf_txflags, - !done ? "" : (ts->ts_status == 0) ? " *" : " !", - ds->ds_ctl0, ds->ds_ctl1, - ds->ds_hw[0], ds->ds_hw[1], ds->ds_hw[2], ds->ds_hw[3]); - if (ah->ah_magic == 0x20065416) { - printf(" %08x %08x %08x %08x %08x %08x %08x %08x\n", - ds->ds_hw[4], ds->ds_hw[5], ds->ds_hw[6], - ds->ds_hw[7], ds->ds_hw[8], ds->ds_hw[9], - ds->ds_hw[10],ds->ds_hw[11]); - printf(" %08x %08x %08x %08x %08x %08x %08x %08x\n", - ds->ds_hw[12],ds->ds_hw[13],ds->ds_hw[14], - ds->ds_hw[15],ds->ds_hw[16],ds->ds_hw[17], - ds->ds_hw[18], ds->ds_hw[19]); + while (bf != NULL) { + printf(" (bf=%p, lastds=%p)\n", bf, first_bf->bf_lastds); + printf(" Seq: %d swtry: %d ADDBAW?: %d DOBAW?: %d\n", + bf->bf_state.bfs_seqno, + bf->bf_state.bfs_retries, + bf->bf_state.bfs_addedbaw, + bf->bf_state.bfs_dobaw); + for (i = 0, ds = bf->bf_desc; i < bf->bf_nseg; i++, ds++) { + printf(" (DS.V:%p DS.P:%p) L:%08x D:%08x F:%04x%s\n", + ds, (const struct ath_desc *)bf->bf_daddr + i, + ds->ds_link, ds->ds_data, bf->bf_state.bfs_txflags, + !done ? "" : (ts->ts_status == 0) ? " *" : " !"); + printf(" %08x %08x %08x %08x %08x %08x\n", + ds->ds_ctl0, ds->ds_ctl1, + ds->ds_hw[0], ds->ds_hw[1], + ds->ds_hw[2], ds->ds_hw[3]); + if (ah->ah_magic == 0x20065416) { + printf(" %08x %08x %08x %08x %08x %08x %08x %08x\n", + ds->ds_hw[4], ds->ds_hw[5], ds->ds_hw[6], + ds->ds_hw[7], ds->ds_hw[8], ds->ds_hw[9], + ds->ds_hw[10],ds->ds_hw[11]); + printf(" %08x %08x %08x %08x %08x %08x %08x %08x\n", + ds->ds_hw[12],ds->ds_hw[13],ds->ds_hw[14], + ds->ds_hw[15],ds->ds_hw[16],ds->ds_hw[17], + ds->ds_hw[18], ds->ds_hw[19]); + } } + printf(" [end]\n"); + bf = bf->bf_next; } } +void +ath_printtxbuf(struct ath_softc *sc, const struct ath_buf *first_bf, + u_int qnum, u_int ix, int done) +{ + if (sc->sc_ah->ah_magic == 0x19741014) + ath_printtxbuf_edma(sc, first_bf, qnum, ix, done); + else + ath_printtxbuf_legacy(sc, first_bf, qnum, ix, done); +} + +void +ath_printtxstatbuf(struct ath_softc *sc, const struct ath_buf *first_bf, + const uint32_t *ds, u_int qnum, u_int ix, int done) +{ + + printf("Q%u[%3u] ", qnum, ix); + printf(" %08x %08x %08x %08x %08x %08x\n", + ds[0], ds[1], ds[2], ds[3], ds[4], ds[5]); + printf(" %08x %08x %08x %08x %08x\n", + ds[6], ds[7], ds[8], ds[9], ds[10]); +} + #endif /* ATH_DEBUG */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_debug.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_debug.h index c21914fe8d..33d9610e91 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_debug.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_debug.h @@ -26,7 +26,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/if_ath_debug.h 301008 2016-05-31 04:09:17Z adrian $ */ #ifndef __IF_ATH_DEBUG_H__ #define __IF_ATH_DEBUG_H__ @@ -34,41 +34,68 @@ #ifdef ATH_DEBUG enum { - ATH_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ - ATH_DEBUG_XMIT_DESC = 0x00000002, /* xmit descriptors */ - ATH_DEBUG_RECV = 0x00000004, /* basic recv operation */ - ATH_DEBUG_RECV_DESC = 0x00000008, /* recv descriptors */ - ATH_DEBUG_RATE = 0x00000010, /* rate control */ - ATH_DEBUG_RESET = 0x00000020, /* reset processing */ - ATH_DEBUG_MODE = 0x00000040, /* mode init/setup */ - ATH_DEBUG_BEACON = 0x00000080, /* beacon handling */ - ATH_DEBUG_WATCHDOG = 0x00000100, /* watchdog timeout */ - ATH_DEBUG_INTR = 0x00001000, /* ISR */ - ATH_DEBUG_TX_PROC = 0x00002000, /* tx ISR proc */ - ATH_DEBUG_RX_PROC = 0x00004000, /* rx ISR proc */ - ATH_DEBUG_BEACON_PROC = 0x00008000, /* beacon ISR proc */ - ATH_DEBUG_CALIBRATE = 0x00010000, /* periodic calibration */ - ATH_DEBUG_KEYCACHE = 0x00020000, /* key cache management */ - ATH_DEBUG_STATE = 0x00040000, /* 802.11 state transitions */ - ATH_DEBUG_NODE = 0x00080000, /* node management */ - ATH_DEBUG_LED = 0x00100000, /* led management */ - ATH_DEBUG_FF = 0x00200000, /* fast frames */ - ATH_DEBUG_DFS = 0x00400000, /* DFS processing */ - ATH_DEBUG_TDMA = 0x00800000, /* TDMA processing */ - ATH_DEBUG_TDMA_TIMER = 0x01000000, /* TDMA timer processing */ - ATH_DEBUG_REGDOMAIN = 0x02000000, /* regulatory processing */ - ATH_DEBUG_FATAL = 0x80000000, /* fatal errors */ - ATH_DEBUG_ANY = 0xffffffff + ATH_DEBUG_XMIT = 0x000000001ULL, /* basic xmit operation */ + ATH_DEBUG_XMIT_DESC = 0x000000002ULL, /* xmit descriptors */ + ATH_DEBUG_RECV = 0x000000004ULL, /* basic recv operation */ + ATH_DEBUG_RECV_DESC = 0x000000008ULL, /* recv descriptors */ + ATH_DEBUG_RATE = 0x000000010ULL, /* rate control */ + ATH_DEBUG_RESET = 0x000000020ULL, /* reset processing */ + ATH_DEBUG_MODE = 0x000000040ULL, /* mode init/setup */ + ATH_DEBUG_BEACON = 0x000000080ULL, /* beacon handling */ + ATH_DEBUG_WATCHDOG = 0x000000100ULL, /* watchdog timeout */ + ATH_DEBUG_INTR = 0x000001000ULL, /* ISR */ + ATH_DEBUG_TX_PROC = 0x000002000ULL, /* tx ISR proc */ + ATH_DEBUG_RX_PROC = 0x000004000ULL, /* rx ISR proc */ + ATH_DEBUG_BEACON_PROC = 0x000008000ULL, /* beacon ISR proc */ + ATH_DEBUG_CALIBRATE = 0x000010000ULL, /* periodic calibration */ + ATH_DEBUG_KEYCACHE = 0x000020000ULL, /* key cache management */ + ATH_DEBUG_STATE = 0x000040000ULL, /* 802.11 state transitions */ + ATH_DEBUG_NODE = 0x000080000ULL, /* node management */ + ATH_DEBUG_LED = 0x000100000ULL, /* led management */ + ATH_DEBUG_FF = 0x000200000ULL, /* fast frames */ + ATH_DEBUG_DFS = 0x000400000ULL, /* DFS processing */ + ATH_DEBUG_TDMA = 0x000800000ULL, /* TDMA processing */ + ATH_DEBUG_TDMA_TIMER = 0x001000000ULL, /* TDMA timer processing */ + ATH_DEBUG_REGDOMAIN = 0x002000000ULL, /* regulatory processing */ + ATH_DEBUG_SW_TX = 0x004000000ULL, /* per-packet software TX */ + ATH_DEBUG_SW_TX_BAW = 0x008000000ULL, /* BAW handling */ + ATH_DEBUG_SW_TX_CTRL = 0x010000000ULL, /* queue control */ + ATH_DEBUG_SW_TX_AGGR = 0x020000000ULL, /* aggregate TX */ + ATH_DEBUG_SW_TX_RETRIES = 0x040000000ULL, /* software TX retries */ + ATH_DEBUG_FATAL = 0x080000000ULL, /* fatal errors */ + ATH_DEBUG_SW_TX_BAR = 0x100000000ULL, /* BAR TX */ + ATH_DEBUG_EDMA_RX = 0x200000000ULL, /* RX EDMA state */ + ATH_DEBUG_SW_TX_FILT = 0x400000000ULL, /* SW TX FF */ + ATH_DEBUG_NODE_PWRSAVE = 0x800000000ULL, /* node powersave */ + ATH_DEBUG_DIVERSITY = 0x1000000000ULL, /* Diversity logic */ + ATH_DEBUG_PWRSAVE = 0x2000000000ULL, + ATH_DEBUG_BTCOEX = 0x4000000000ULL, /* BT Coex */ + + ATH_DEBUG_ANY = 0xffffffffffffffffULL }; -extern int ath_debug; +enum { + ATH_KTR_RXPROC = 0x00000001, + ATH_KTR_TXPROC = 0x00000002, + ATH_KTR_TXCOMP = 0x00000004, + ATH_KTR_SWQ = 0x00000008, + ATH_KTR_INTERRUPTS = 0x00000010, + ATH_KTR_ERROR = 0x00000020, + ATH_KTR_NODE = 0x00000040, + ATH_KTR_TX = 0x00000080, +}; -#define IFF_DUMPPKTS(sc, m) \ - ((sc->sc_debug & (m)) || \ - (sc->sc_ifp->if_flags & (IFF_DEBUG|IFF_LINK2)) == (IFF_DEBUG|IFF_LINK2)) -#define DPRINTF(sc, m, fmt, ...) do { \ +#define ATH_KTR(_sc, _km, _kf, ...) do { \ + if (sc->sc_ktrdebug & (_km)) \ + CTR##_kf(KTR_DEV, __VA_ARGS__); \ + } while (0) + +extern uint64_t ath_debug; + +#define IFF_DUMPPKTS(sc, m) (sc->sc_debug & (m)) +#define DPRINTF(sc, m, ...) do { \ if (sc->sc_debug & (m)) \ - device_printf(sc->sc_dev, fmt, __VA_ARGS__); \ + device_printf(sc->sc_dev, __VA_ARGS__); \ } while (0) #define KEYPRINTF(sc, ix, hk, mac) do { \ if (sc->sc_debug & ATH_DEBUG_KEYCACHE) \ @@ -79,9 +106,12 @@ extern void ath_printrxbuf(struct ath_softc *, const struct ath_buf *bf, u_int ix, int); extern void ath_printtxbuf(struct ath_softc *, const struct ath_buf *bf, u_int qnum, u_int ix, int done); +extern void ath_printtxstatbuf(struct ath_softc *sc, const struct ath_buf *bf, + const uint32_t *ds, u_int qnum, u_int ix, int done); #else /* ATH_DEBUG */ -#define IFF_DUMPPKTS(sc, m) \ - ((sc->sc_ifp->if_flags & (IFF_DEBUG|IFF_LINK2)) == (IFF_DEBUG|IFF_LINK2)) +#define ATH_KTR(_sc, _km, _kf, ...) do { } while (0) + +#define IFF_DUMPPKTS(sc, m) (0) #define DPRINTF(sc, m, fmt, ...) do { \ (void) sc; \ } while (0) diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_descdma.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_descdma.c new file mode 100644 index 0000000000..fa98a29aa9 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_descdma.c @@ -0,0 +1,442 @@ +/*- + * 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 +__FBSDID("$FreeBSD: releng/11.1/sys/dev/ath/if_ath_descdma.c 291233 2015-11-24 03:42:58Z adrian $"); + +/* + * 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" +/* + * This is needed for register operations which are performed + * by the driver - eg, calls to ath_hal_gettsf32(). + * + * It's also required for any AH_DEBUG checks in here, eg the + * module dependencies. + */ +#include "opt_ah.h" +#include "opt_wlan.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for mp_ncpus */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#ifdef IEEE80211_SUPPORT_SUPERG +#include +#endif +#ifdef IEEE80211_SUPPORT_TDMA +#include +#endif + +#include + +#ifdef INET +#include +#include +#endif + +#include +#include /* XXX for softled */ +#include + +#include +#include +#if 0 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif +#include + +MALLOC_DECLARE(M_ATHDEV); + +/* + * This is the descriptor setup / busdma memory intialisation and + * teardown routines. + */ + +static void +ath_load_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) +{ + bus_addr_t *paddr = (bus_addr_t*) arg; + KASSERT(error == 0, ("error %u on bus_dma callback", error)); + *paddr = segs->ds_addr; +} + +/* + * Allocate the descriptors and appropriate DMA tag/setup. + * + * For some situations (eg EDMA TX completion), there isn't a requirement + * for the ath_buf entries to be allocated. + */ +int +ath_descdma_alloc_desc(struct ath_softc *sc, + struct ath_descdma *dd, ath_bufhead *head, + const char *name, int ds_size, int ndesc) +{ +#define DS2PHYS(_dd, _ds) \ + ((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc)) +#define ATH_DESC_4KB_BOUND_CHECK(_daddr, _len) \ + ((((u_int32_t)(_daddr) & 0xFFF) > (0x1000 - (_len))) ? 1 : 0) + int error; + + dd->dd_descsize = ds_size; + + DPRINTF(sc, ATH_DEBUG_RESET, + "%s: %s DMA: %u desc, %d bytes per descriptor\n", + __func__, name, ndesc, dd->dd_descsize); + + dd->dd_name = name; + dd->dd_desc_len = dd->dd_descsize * ndesc; + + /* + * Merlin work-around: + * Descriptors that cross the 4KB boundary can't be used. + * Assume one skipped descriptor per 4KB page. + */ + if (! ath_hal_split4ktrans(sc->sc_ah)) { + int numpages = dd->dd_desc_len / 4096; + dd->dd_desc_len += ds_size * numpages; + } + + /* + * Setup DMA descriptor area. + * + * BUS_DMA_ALLOCNOW is not used; we never use bounce + * buffers for the descriptors themselves. + */ + error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), /* parent */ + PAGE_SIZE, 0, /* alignment, bounds */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + dd->dd_desc_len, /* maxsize */ + 1, /* nsegments */ + dd->dd_desc_len, /* maxsegsize */ + 0, /* flags */ + NULL, /* lockfunc */ + NULL, /* lockarg */ + &dd->dd_dmat); + if (error != 0) { + device_printf(sc->sc_dev, + "cannot allocate %s DMA tag\n", dd->dd_name); + return error; + } + + /* allocate descriptors */ + error = bus_dmamem_alloc(dd->dd_dmat, (void**) &dd->dd_desc, + BUS_DMA_NOWAIT | BUS_DMA_COHERENT, + &dd->dd_dmamap); + if (error != 0) { + device_printf(sc->sc_dev, + "unable to alloc memory for %u %s descriptors, error %u\n", + ndesc, dd->dd_name, error); + goto fail1; + } + + error = bus_dmamap_load(dd->dd_dmat, dd->dd_dmamap, + dd->dd_desc, dd->dd_desc_len, + ath_load_cb, &dd->dd_desc_paddr, + BUS_DMA_NOWAIT); + if (error != 0) { + device_printf(sc->sc_dev, + "unable to map %s descriptors, error %u\n", + dd->dd_name, error); + goto fail2; + } + + DPRINTF(sc, ATH_DEBUG_RESET, "%s: %s DMA map: %p (%lu) -> %p (%lu)\n", + __func__, dd->dd_name, (uint8_t *) dd->dd_desc, + (u_long) dd->dd_desc_len, (caddr_t) dd->dd_desc_paddr, + /*XXX*/ (u_long) dd->dd_desc_len); + + return (0); + +fail2: + bus_dmamem_free(dd->dd_dmat, dd->dd_desc, dd->dd_dmamap); +fail1: + bus_dma_tag_destroy(dd->dd_dmat); + memset(dd, 0, sizeof(*dd)); + return error; +#undef DS2PHYS +#undef ATH_DESC_4KB_BOUND_CHECK +} + +int +ath_descdma_setup(struct ath_softc *sc, + struct ath_descdma *dd, ath_bufhead *head, + const char *name, int ds_size, int nbuf, int ndesc) +{ +#define DS2PHYS(_dd, _ds) \ + ((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc)) +#define ATH_DESC_4KB_BOUND_CHECK(_daddr, _len) \ + ((((u_int32_t)(_daddr) & 0xFFF) > (0x1000 - (_len))) ? 1 : 0) + uint8_t *ds; + struct ath_buf *bf; + int i, bsize, error; + + /* Allocate descriptors */ + error = ath_descdma_alloc_desc(sc, dd, head, name, ds_size, + nbuf * ndesc); + + /* Assume any errors during allocation were dealt with */ + if (error != 0) { + return (error); + } + + ds = (uint8_t *) dd->dd_desc; + + /* allocate rx buffers */ + bsize = sizeof(struct ath_buf) * nbuf; + bf = malloc(bsize, M_ATHDEV, M_NOWAIT | M_ZERO); + if (bf == NULL) { + device_printf(sc->sc_dev, + "malloc of %s buffers failed, size %u\n", + dd->dd_name, bsize); + goto fail3; + } + dd->dd_bufptr = bf; + + TAILQ_INIT(head); + for (i = 0; i < nbuf; i++, bf++, ds += (ndesc * dd->dd_descsize)) { + bf->bf_desc = (struct ath_desc *) ds; + bf->bf_daddr = DS2PHYS(dd, ds); + if (! ath_hal_split4ktrans(sc->sc_ah)) { + /* + * Merlin WAR: Skip descriptor addresses which + * cause 4KB boundary crossing along any point + * in the descriptor. + */ + if (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr, + dd->dd_descsize)) { + /* Start at the next page */ + ds += 0x1000 - (bf->bf_daddr & 0xFFF); + bf->bf_desc = (struct ath_desc *) ds; + bf->bf_daddr = DS2PHYS(dd, ds); + } + } + error = bus_dmamap_create(sc->sc_dmat, BUS_DMA_NOWAIT, + &bf->bf_dmamap); + if (error != 0) { + device_printf(sc->sc_dev, "unable to create dmamap " + "for %s buffer %u, error %u\n", + dd->dd_name, i, error); + ath_descdma_cleanup(sc, dd, head); + return error; + } + bf->bf_lastds = bf->bf_desc; /* Just an initial value */ + TAILQ_INSERT_TAIL(head, bf, bf_list); + } + + /* + * XXX TODO: ensure that ds doesn't overflow the descriptor + * allocation otherwise weird stuff will occur and crash your + * machine. + */ + return 0; + /* XXX this should likely just call ath_descdma_cleanup() */ +fail3: + bus_dmamap_unload(dd->dd_dmat, dd->dd_dmamap); + bus_dmamem_free(dd->dd_dmat, dd->dd_desc, dd->dd_dmamap); + bus_dma_tag_destroy(dd->dd_dmat); + memset(dd, 0, sizeof(*dd)); + return error; +#undef DS2PHYS +#undef ATH_DESC_4KB_BOUND_CHECK +} + +/* + * Allocate ath_buf entries but no descriptor contents. + * + * This is for RX EDMA where the descriptors are the header part of + * the RX buffer. + */ +int +ath_descdma_setup_rx_edma(struct ath_softc *sc, + struct ath_descdma *dd, ath_bufhead *head, + const char *name, int nbuf, int rx_status_len) +{ + struct ath_buf *bf; + int i, bsize, error; + + DPRINTF(sc, ATH_DEBUG_RESET, "%s: %s DMA: %u buffers\n", + __func__, name, nbuf); + + dd->dd_name = name; + /* + * This is (mostly) purely for show. We're not allocating any actual + * descriptors here as EDMA RX has the descriptor be part + * of the RX buffer. + * + * However, dd_desc_len is used by ath_descdma_free() to determine + * whether we have already freed this DMA mapping. + */ + dd->dd_desc_len = rx_status_len * nbuf; + dd->dd_descsize = rx_status_len; + + /* allocate rx buffers */ + bsize = sizeof(struct ath_buf) * nbuf; + bf = malloc(bsize, M_ATHDEV, M_NOWAIT | M_ZERO); + if (bf == NULL) { + device_printf(sc->sc_dev, + "malloc of %s buffers failed, size %u\n", + dd->dd_name, bsize); + error = ENOMEM; + goto fail3; + } + dd->dd_bufptr = bf; + + TAILQ_INIT(head); + for (i = 0; i < nbuf; i++, bf++) { + bf->bf_desc = NULL; + bf->bf_daddr = 0; + bf->bf_lastds = NULL; /* Just an initial value */ + + error = bus_dmamap_create(sc->sc_dmat, BUS_DMA_NOWAIT, + &bf->bf_dmamap); + if (error != 0) { + device_printf(sc->sc_dev, "unable to create dmamap " + "for %s buffer %u, error %u\n", + dd->dd_name, i, error); + ath_descdma_cleanup(sc, dd, head); + return error; + } + TAILQ_INSERT_TAIL(head, bf, bf_list); + } + return 0; +fail3: + memset(dd, 0, sizeof(*dd)); + return error; +} + +void +ath_descdma_cleanup(struct ath_softc *sc, + struct ath_descdma *dd, ath_bufhead *head) +{ + struct ath_buf *bf; + struct ieee80211_node *ni; + int do_warning = 0; + + if (dd->dd_dmamap != 0) { + bus_dmamap_unload(dd->dd_dmat, dd->dd_dmamap); + bus_dmamem_free(dd->dd_dmat, dd->dd_desc, dd->dd_dmamap); + bus_dma_tag_destroy(dd->dd_dmat); + } + + if (head != NULL) { + TAILQ_FOREACH(bf, head, bf_list) { + if (bf->bf_m) { + /* + * XXX warn if there's buffers here. + * XXX it should have been freed by the + * owner! + */ + + if (do_warning == 0) { + do_warning = 1; + device_printf(sc->sc_dev, + "%s: %s: mbuf should've been" + " unmapped/freed!\n", + __func__, + dd->dd_name); + } + bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, + BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); + m_freem(bf->bf_m); + bf->bf_m = NULL; + } + if (bf->bf_dmamap != NULL) { + bus_dmamap_destroy(sc->sc_dmat, bf->bf_dmamap); + bf->bf_dmamap = NULL; + } + ni = bf->bf_node; + bf->bf_node = NULL; + if (ni != NULL) { + /* + * Reclaim node reference. + */ + ieee80211_free_node(ni); + } + } + } + + if (head != NULL) + TAILQ_INIT(head); + + if (dd->dd_bufptr != NULL) + free(dd->dd_bufptr, M_ATHDEV); + memset(dd, 0, sizeof(*dd)); +} diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_descdma.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_descdma.h new file mode 100644 index 0000000000..8d5381b0d9 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_descdma.h @@ -0,0 +1,46 @@ +/*- + * Copyright (c) 2015 Adrian Chadd + * 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. + * + * $FreeBSD: releng/11.1/sys/dev/ath/if_ath_descdma.h 291233 2015-11-24 03:42:58Z adrian $ + */ +#ifndef __IF_ATH_DESCDMA_H__ +#define __IF_ATH_DESCDMA_H__ + +extern int ath_descdma_alloc_desc(struct ath_softc *sc, + struct ath_descdma *dd, ath_bufhead *head, const char *name, + int ds_size, int ndesc); +extern int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, + ath_bufhead *head, const char *name, int ds_size, int nbuf, + int ndesc); +extern int ath_descdma_setup_rx_edma(struct ath_softc *sc, + struct ath_descdma *dd, ath_bufhead *head, const char *name, + int nbuf, int desclen); +extern void ath_descdma_cleanup(struct ath_softc *sc, + struct ath_descdma *dd, ath_bufhead *head); + +#endif diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_ioctl.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_ioctl.c new file mode 100644 index 0000000000..088536d18a --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_ioctl.c @@ -0,0 +1,307 @@ +/*- + * 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 +__FBSDID("$FreeBSD: releng/11.1/sys/dev/ath/if_ath_ioctl.c 298939 2016-05-02 19:56:48Z pfg $"); + +/* + * 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" +/* + * This is needed for register operations which are performed + * by the driver - eg, calls to ath_hal_gettsf32(). + * + * It's also required for any AH_DEBUG checks in here, eg the + * module dependencies. + */ +#include "opt_ah.h" +#include "opt_wlan.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for mp_ncpus */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#ifdef IEEE80211_SUPPORT_SUPERG +#include +#endif +#ifdef IEEE80211_SUPPORT_TDMA +#include +#endif + +#include + +#ifdef INET +#include +#include +#endif + +#include +#include /* XXX for softled */ +#include + +#include +#include +#include +#include +#include +#include + +#ifdef IEEE80211_SUPPORT_TDMA +#include +#endif + +#include + +/* + * ioctl() related pieces. + * + * Some subsystems (eg spectral, dfs) have their own ioctl method which + * we call. + */ + +/* + * Fetch the rate control statistics for the given node. + */ +static int +ath_ioctl_ratestats(struct ath_softc *sc, struct ath_rateioctl *rs) +{ + struct ath_node *an; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni; + int error = 0; + + /* Perform a lookup on the given node */ + ni = ieee80211_find_node(&ic->ic_sta, rs->is_u.macaddr); + if (ni == NULL) { + error = EINVAL; + goto bad; + } + + /* Lock the ath_node */ + an = ATH_NODE(ni); + ATH_NODE_LOCK(an); + + /* Fetch the rate control stats for this node */ + error = ath_rate_fetch_node_stats(sc, an, rs); + + /* No matter what happens here, just drop through */ + + /* Unlock the ath_node */ + ATH_NODE_UNLOCK(an); + + /* Unref the node */ + ieee80211_node_decref(ni); + +bad: + return (error); +} + +#ifdef ATH_DIAGAPI +/* + * Diagnostic interface to the HAL. This is used by various + * tools to do things like retrieve register contents for + * debugging. The mechanism is intentionally opaque so that + * it can change frequently w/o concern for compatibility. + */ +static int +ath_ioctl_diag(struct ath_softc *sc, struct ath_diag *ad) +{ + struct ath_hal *ah = sc->sc_ah; + u_int id = ad->ad_id & ATH_DIAG_ID; + void *indata = NULL; + void *outdata = NULL; + u_int32_t insize = ad->ad_in_size; + u_int32_t outsize = ad->ad_out_size; + int error = 0; + + if (ad->ad_id & ATH_DIAG_IN) { + /* + * Copy in data. + */ + indata = malloc(insize, M_TEMP, M_NOWAIT); + if (indata == NULL) { + error = ENOMEM; + goto bad; + } + error = copyin(ad->ad_in_data, indata, insize); + if (error) + goto bad; + } + if (ad->ad_id & ATH_DIAG_DYN) { + /* + * Allocate a buffer for the results (otherwise the HAL + * returns a pointer to a buffer where we can read the + * results). Note that we depend on the HAL leaving this + * pointer for us to use below in reclaiming the buffer; + * may want to be more defensive. + */ + outdata = malloc(outsize, M_TEMP, M_NOWAIT); + if (outdata == NULL) { + error = ENOMEM; + goto bad; + } + } + + + ATH_LOCK(sc); + if (id != HAL_DIAG_REGS) + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + + if (ath_hal_getdiagstate(ah, id, indata, insize, &outdata, &outsize)) { + if (outsize < ad->ad_out_size) + ad->ad_out_size = outsize; + if (outdata != NULL) + error = copyout(outdata, ad->ad_out_data, + ad->ad_out_size); + } else { + error = EINVAL; + } + + ATH_LOCK(sc); + if (id != HAL_DIAG_REGS) + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + +bad: + if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL) + free(indata, M_TEMP); + if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL) + free(outdata, M_TEMP); + return error; +} +#endif /* ATH_DIAGAPI */ + +int +ath_ioctl(struct ieee80211com *ic, u_long cmd, void *data) +{ + struct ifreq *ifr = data; + struct ath_softc *sc = ic->ic_softc; + + switch (cmd) { + case SIOCGATHSTATS: { + struct ieee80211vap *vap; + struct ifnet *ifp; + const HAL_RATE_TABLE *rt; + + /* NB: embed these numbers to get a consistent view */ + sc->sc_stats.ast_tx_packets = 0; + sc->sc_stats.ast_rx_packets = 0; + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + ifp = vap->iv_ifp; + sc->sc_stats.ast_tx_packets += ifp->if_get_counter(ifp, + IFCOUNTER_OPACKETS); + sc->sc_stats.ast_rx_packets += ifp->if_get_counter(ifp, + IFCOUNTER_IPACKETS); + } + sc->sc_stats.ast_tx_rssi = ATH_RSSI(sc->sc_halstats.ns_avgtxrssi); + sc->sc_stats.ast_rx_rssi = ATH_RSSI(sc->sc_halstats.ns_avgrssi); +#ifdef IEEE80211_SUPPORT_TDMA + sc->sc_stats.ast_tdma_tsfadjp = TDMA_AVG(sc->sc_avgtsfdeltap); + sc->sc_stats.ast_tdma_tsfadjm = TDMA_AVG(sc->sc_avgtsfdeltam); +#endif + rt = sc->sc_currates; + sc->sc_stats.ast_tx_rate = + rt->info[sc->sc_txrix].dot11Rate &~ IEEE80211_RATE_BASIC; + if (rt->info[sc->sc_txrix].phy & IEEE80211_T_HT) + sc->sc_stats.ast_tx_rate |= IEEE80211_RATE_MCS; + return copyout(&sc->sc_stats, + ifr->ifr_data, sizeof (sc->sc_stats)); + } + case SIOCGATHAGSTATS: + return copyout(&sc->sc_aggr_stats, + ifr->ifr_data, sizeof (sc->sc_aggr_stats)); + case SIOCZATHSTATS: { + int error; + + error = priv_check(curthread, PRIV_DRIVER); + if (error == 0) { + 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)); + } + return (error); + } +#ifdef ATH_DIAGAPI + case SIOCGATHDIAG: + return (ath_ioctl_diag(sc, data)); + case SIOCGATHPHYERR: + return (ath_ioctl_phyerr(sc, data)); +#endif + case SIOCGATHSPECTRAL: + return (ath_ioctl_spectral(sc, data)); + case SIOCGATHNODERATESTATS: + return (ath_ioctl_ratestats(sc, data)); + default: + /* + * This signals the net80211 layer that we didn't handle this + * ioctl. + */ + return (ENOTTY); + } +} + diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_ioctl.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_ioctl.h new file mode 100644 index 0000000000..bd1ad56cc4 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_ioctl.h @@ -0,0 +1,36 @@ +/*- + * Copyright (c) 2015 Adrian Chadd + * 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. + * + * $FreeBSD: releng/11.1/sys/dev/ath/if_ath_ioctl.h 291233 2015-11-24 03:42:58Z adrian $ + */ +#ifndef __IF_ATH_IOCTL_H__ +#define __IF_ATH_IOCTL_H__ + +extern int ath_ioctl(struct ieee80211com *ic, u_long cmd, void *data); + +#endif diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_keycache.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_keycache.c index 842f76627f..39aade4a61 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_keycache.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_keycache.c @@ -28,7 +28,7 @@ */ #include -__FBSDID("$FreeBSD$"); +__FBSDID("$FreeBSD: releng/11.1/sys/dev/ath/if_ath_keycache.c 288635 2015-10-03 21:48:27Z adrian $"); /* * Driver for the Atheros Wireless LAN controller. @@ -62,6 +62,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -77,6 +78,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #ifdef ATH_DEBUG static void @@ -178,10 +180,10 @@ ath_keyset_tkip(struct ath_softc *sc, const struct ieee80211_key *k, * cache slots for TKIP with hardware MIC support. */ int -ath_keyset(struct ath_softc *sc, const struct ieee80211_key *k, +ath_keyset(struct ath_softc *sc, struct ieee80211vap *vap, + const struct ieee80211_key *k, struct ieee80211_node *bss) { -#define N(a) (sizeof(a)/sizeof(a[0])) static const u_int8_t ciphermap[] = { HAL_CIPHER_WEP, /* IEEE80211_CIPHER_WEP */ HAL_CIPHER_TKIP, /* IEEE80211_CIPHER_TKIP */ @@ -196,6 +198,7 @@ ath_keyset(struct ath_softc *sc, const struct ieee80211_key *k, u_int8_t gmac[IEEE80211_ADDR_LEN]; const u_int8_t *mac; HAL_KEYVAL hk; + int ret; memset(&hk, 0, sizeof(hk)); /* @@ -204,7 +207,7 @@ ath_keyset(struct ath_softc *sc, const struct ieee80211_key *k, * so that rx frames have an entry to match. */ if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) == 0) { - KASSERT(cip->ic_cipher < N(ciphermap), + KASSERT(cip->ic_cipher < nitems(ciphermap), ("invalid cipher type %u", cip->ic_cipher)); hk.kv_type = ciphermap[cip->ic_cipher]; hk.kv_len = k->wk_keylen; @@ -212,7 +215,32 @@ ath_keyset(struct ath_softc *sc, const struct ieee80211_key *k, } else hk.kv_type = HAL_CIPHER_CLR; - if ((k->wk_flags & IEEE80211_KEY_GROUP) && sc->sc_mcastkey) { + /* + * If we're installing a clear cipher key and + * the hardware doesn't support that, just succeed. + * Leave it up to the net80211 layer to figure it out. + */ + if (hk.kv_type == HAL_CIPHER_CLR && sc->sc_hasclrkey == 0) { + return (1); + } + + /* + * XXX TODO: check this: + * + * Group keys on hardware that supports multicast frame + * key search should only be done in adhoc/hostap mode, + * not STA mode. + * + * XXX TODO: what about mesh, tdma? + */ +#if 0 + if ((vap->iv_opmode == IEEE80211_M_HOSTAP || + vap->iv_opmode == IEEE80211_M_IBSS) && +#else + if ( +#endif + (k->wk_flags & IEEE80211_KEY_GROUP) && + sc->sc_mcastkey) { /* * Group keys on hardware that supports multicast frame * key search use a MAC that is the sender's address with @@ -224,14 +252,19 @@ ath_keyset(struct ath_softc *sc, const struct ieee80211_key *k, } else mac = k->wk_macaddr; + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); if (hk.kv_type == HAL_CIPHER_TKIP && (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) { - return ath_keyset_tkip(sc, k, &hk, mac); + ret = ath_keyset_tkip(sc, k, &hk, mac); } else { KEYPRINTF(sc, k->wk_keyix, &hk, mac); - return ath_hal_keyset(ah, k->wk_keyix, &hk, mac); + ret = ath_hal_keyset(ah, k->wk_keyix, &hk, mac); } -#undef N + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + return (ret); } /* @@ -242,12 +275,11 @@ static u_int16_t key_alloc_2pair(struct ath_softc *sc, ieee80211_keyix *txkeyix, ieee80211_keyix *rxkeyix) { -#define N(a) (sizeof(a)/sizeof(a[0])) u_int i, keyix; KASSERT(sc->sc_splitmic, ("key cache !split")); /* XXX could optimize */ - for (i = 0; i < N(sc->sc_keymap)/4; i++) { + for (i = 0; i < nitems(sc->sc_keymap)/4; i++) { u_int8_t b = sc->sc_keymap[i]; if (b != 0xff) { /* @@ -286,7 +318,6 @@ key_alloc_2pair(struct ath_softc *sc, } DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: out of pair space\n", __func__); return 0; -#undef N } /* @@ -297,12 +328,11 @@ static u_int16_t key_alloc_pair(struct ath_softc *sc, ieee80211_keyix *txkeyix, ieee80211_keyix *rxkeyix) { -#define N(a) (sizeof(a)/sizeof(a[0])) u_int i, keyix; KASSERT(!sc->sc_splitmic, ("key cache split")); /* XXX could optimize */ - for (i = 0; i < N(sc->sc_keymap)/4; i++) { + for (i = 0; i < nitems(sc->sc_keymap)/4; i++) { u_int8_t b = sc->sc_keymap[i]; if (b != 0xff) { /* @@ -334,7 +364,6 @@ key_alloc_pair(struct ath_softc *sc, } DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: out of pair space\n", __func__); return 0; -#undef N } /* @@ -344,11 +373,18 @@ static int key_alloc_single(struct ath_softc *sc, ieee80211_keyix *txkeyix, ieee80211_keyix *rxkeyix) { -#define N(a) (sizeof(a)/sizeof(a[0])) u_int i, keyix; + if (sc->sc_hasclrkey == 0) { + /* + * Map to slot 0 for the AR5210. + */ + *txkeyix = *rxkeyix = 0; + return (1); + } + /* XXX try i,i+32,i+64,i+32+64 to minimize key pair conflicts */ - for (i = 0; i < N(sc->sc_keymap); i++) { + for (i = 0; i < nitems(sc->sc_keymap); i++) { u_int8_t b = sc->sc_keymap[i]; if (b != 0xff) { /* @@ -366,7 +402,6 @@ key_alloc_single(struct ath_softc *sc, } DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: out of space\n", __func__); return 0; -#undef N } /* @@ -382,7 +417,7 @@ int ath_key_alloc(struct ieee80211vap *vap, struct ieee80211_key *k, ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix) { - struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; + struct ath_softc *sc = vap->iv_ic->ic_softc; /* * Group key allocation must be handled specially for @@ -450,13 +485,15 @@ ath_key_alloc(struct ieee80211vap *vap, struct ieee80211_key *k, int ath_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k) { - struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; + struct ath_softc *sc = vap->iv_ic->ic_softc; struct ath_hal *ah = sc->sc_ah; const struct ieee80211_cipher *cip = k->wk_cipher; u_int keyix = k->wk_keyix; DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: delete key %u\n", __func__, keyix); + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); ath_hal_keyreset(ah, keyix); /* * Handle split tx/rx keying required for TKIP with h/w MIC. @@ -480,6 +517,8 @@ ath_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k) } } } + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); return 1; } @@ -488,10 +527,9 @@ ath_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k) * slot(s) must already have been allocated by ath_key_alloc. */ int -ath_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k, - const u_int8_t mac[IEEE80211_ADDR_LEN]) +ath_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k) { - struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; + struct ath_softc *sc = vap->iv_ic->ic_softc; - return ath_keyset(sc, k, vap->iv_bss); + return ath_keyset(sc, vap, k, vap->iv_bss); } diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_keycache.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_keycache.h index f1696cbd92..43e936a26b 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_keycache.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_keycache.h @@ -26,7 +26,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/if_ath_keycache.h 288635 2015-10-03 21:48:27Z adrian $ */ #ifndef __IF_ATH_CRYPTO_H__ @@ -35,9 +35,8 @@ extern int ath_key_alloc(struct ieee80211vap *, struct ieee80211_key *, ieee80211_keyix *, ieee80211_keyix *); extern int ath_key_delete(struct ieee80211vap *, const struct ieee80211_key *); -extern int ath_key_set(struct ieee80211vap *, const struct ieee80211_key *, - const u_int8_t mac[IEEE80211_ADDR_LEN]); -extern int ath_keyset(struct ath_softc *sc, const struct ieee80211_key *k, - struct ieee80211_node *bss); +extern int ath_key_set(struct ieee80211vap *, const struct ieee80211_key *); +extern int ath_keyset(struct ath_softc *sc, struct ieee80211vap *vap, + const struct ieee80211_key *k, struct ieee80211_node *bss); #endif diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_led.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_led.c new file mode 100644 index 0000000000..9abbf4c60c --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_led.c @@ -0,0 +1,199 @@ +/*- + * 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 +__FBSDID("$FreeBSD: releng/11.1/sys/dev/ath/if_ath_led.c 265115 2014-04-30 02:19:41Z adrian $"); + +/* + * 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" +/* + * This is needed for register operations which are performed + * by the driver - eg, calls to ath_hal_gettsf32(). + */ +#include "opt_ah.h" +#include "opt_wlan.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for mp_ncpus */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#ifdef IEEE80211_SUPPORT_SUPERG +#include +#endif +#ifdef IEEE80211_SUPPORT_TDMA +#include +#endif + +#include + +#ifdef INET +#include +#include +#endif + +#include +#include /* XXX for softled */ +#include + +#include +#include + +#include + +/* + * Software LED driver routines. + */ + +/* + * XXX TODO: move the LED sysctls here. + */ + + +/* + * Configure the hardware for software and LED blinking. + * The user may choose to configure part of each, depending upon the + * NIC being used. + * + * This requires the configuration to be set before this function + * is called. + */ +void +ath_led_config(struct ath_softc *sc) +{ + + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + + /* Software LED blinking - GPIO controlled LED */ + if (sc->sc_softled) { + ath_hal_gpioCfgOutput(sc->sc_ah, sc->sc_ledpin, + HAL_GPIO_OUTPUT_MUX_AS_OUTPUT); + ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, !sc->sc_ledon); + } + + /* Hardware LED blinking - MAC controlled LED */ + if (sc->sc_hardled) { + /* + * Only enable each LED if required. + * + * Some NICs only have one LED connected; others may + * have GPIO1/GPIO2 connected to other hardware. + */ + if (sc->sc_led_pwr_pin > 0) + ath_hal_gpioCfgOutput(sc->sc_ah, sc->sc_led_pwr_pin, + HAL_GPIO_OUTPUT_MUX_MAC_POWER_LED); + if (sc->sc_led_net_pin > 0) + ath_hal_gpioCfgOutput(sc->sc_ah, sc->sc_led_net_pin, + HAL_GPIO_OUTPUT_MUX_MAC_NETWORK_LED); + } + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); +} + +static void +ath_led_done(void *arg) +{ + struct ath_softc *sc = arg; + + sc->sc_blinking = 0; +} + +/* + * Turn the LED off: flip the pin and then set a timer so no + * update will happen for the specified duration. + */ +static void +ath_led_off(void *arg) +{ + struct ath_softc *sc = arg; + + ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, !sc->sc_ledon); + callout_reset(&sc->sc_ledtimer, sc->sc_ledoff, ath_led_done, sc); +} + +/* + * Blink the LED according to the specified on/off times. + */ +static void +ath_led_blink(struct ath_softc *sc, int on, int off) +{ + DPRINTF(sc, ATH_DEBUG_LED, "%s: on %u off %u\n", __func__, on, off); + ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, sc->sc_ledon); + sc->sc_blinking = 1; + sc->sc_ledoff = off; + callout_reset(&sc->sc_ledtimer, on, ath_led_off, sc); +} + +void +ath_led_event(struct ath_softc *sc, int rix) +{ + sc->sc_ledevent = ticks; /* time of last event */ + if (sc->sc_blinking) /* don't interrupt active blink */ + return; + ath_led_blink(sc, sc->sc_hwmap[rix].ledon, sc->sc_hwmap[rix].ledoff); +} diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_led.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_led.h new file mode 100644 index 0000000000..3713b744a0 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_led.h @@ -0,0 +1,37 @@ +/*- + * 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. + * + * $FreeBSD: releng/11.1/sys/dev/ath/if_ath_led.h 228888 2011-12-26 05:46:22Z adrian $ + */ +#ifndef __IF_ATH_LED_H__ +#define __IF_ATH_LED_H__ + +extern void ath_led_event(struct ath_softc *sc, int rix); +extern void ath_led_config(struct ath_softc *sc); + +#endif diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_lna_div.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_lna_div.c new file mode 100644 index 0000000000..ae98294ee5 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_lna_div.c @@ -0,0 +1,1018 @@ +/*- + * Copyright (c) 2013 Adrian Chadd + * 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. + * + * $FreeBSD: releng/11.1/sys/dev/ath/if_ath_lna_div.c 298939 2016-05-02 19:56:48Z pfg $ + */ +#include +__FBSDID("$FreeBSD: releng/11.1/sys/dev/ath/if_ath_lna_div.c 298939 2016-05-02 19:56:48Z pfg $"); + +/* + * This module handles LNA diversity for those chips which implement LNA + * mixing (AR9285/AR9485.) + */ +#include "opt_ath.h" +#include "opt_inet.h" +#include "opt_wlan.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include /* XXX for ether_sprintf */ + +#include + +#include + +#ifdef INET +#include +#include +#endif + +#include +#include +#include + +/* Linux compatibility macros */ +/* + * XXX these don't handle rounding, underflow, overflow, wrapping! + */ +#define msecs_to_jiffies(a) ( (a) * hz / 1000 ) + +/* + * Methods which are required + */ + +/* + * Attach the LNA diversity to the given interface + */ +int +ath_lna_div_attach(struct ath_softc *sc) +{ + struct if_ath_ant_comb_state *ss; + HAL_ANT_COMB_CONFIG div_ant_conf; + + /* Only do this if diversity is enabled */ + if (! ath_hal_hasdivantcomb(sc->sc_ah)) + return (0); + + ss = malloc(sizeof(struct if_ath_ant_comb_state), + M_TEMP, M_WAITOK | M_ZERO); + if (ss == NULL) { + device_printf(sc->sc_dev, "%s: failed to allocate\n", + __func__); + /* Don't fail at this point */ + return (0); + } + + /* Fetch the hardware configuration */ + OS_MEMZERO(&div_ant_conf, sizeof(div_ant_conf)); + ath_hal_div_comb_conf_get(sc->sc_ah, &div_ant_conf); + + /* Figure out what the hardware specific bits should be */ + if ((div_ant_conf.antdiv_configgroup == HAL_ANTDIV_CONFIG_GROUP_1) || + (div_ant_conf.antdiv_configgroup == HAL_ANTDIV_CONFIG_GROUP_2)) { + ss->lna1_lna2_delta = -9; + } else { + ss->lna1_lna2_delta = -3; + } + + /* Let's flip this on */ + sc->sc_lna_div = ss; + sc->sc_dolnadiv = 1; + + return (0); +} + +/* + * Detach the LNA diversity state from the given interface + */ +int +ath_lna_div_detach(struct ath_softc *sc) +{ + if (sc->sc_lna_div != NULL) { + free(sc->sc_lna_div, M_TEMP); + sc->sc_lna_div = NULL; + } + sc->sc_dolnadiv = 0; + return (0); +} + +/* + * Enable LNA diversity on the current channel if it's required. + */ +int +ath_lna_div_enable(struct ath_softc *sc, const struct ieee80211_channel *chan) +{ + + return (0); +} + +/* + * Handle ioctl requests from the diagnostic interface. + * + * The initial part of this code resembles ath_ioctl_diag(); + * it's likely a good idea to reduce duplication between + * these two routines. + */ +int +ath_lna_div_ioctl(struct ath_softc *sc, struct ath_diag *ad) +{ + unsigned int id = ad->ad_id & ATH_DIAG_ID; + void *indata = NULL; + void *outdata = NULL; + u_int32_t insize = ad->ad_in_size; + u_int32_t outsize = ad->ad_out_size; + int error = 0; +// int val; + + if (ad->ad_id & ATH_DIAG_IN) { + /* + * Copy in data. + */ + indata = malloc(insize, M_TEMP, M_NOWAIT); + if (indata == NULL) { + error = ENOMEM; + goto bad; + } + error = copyin(ad->ad_in_data, indata, insize); + if (error) + goto bad; + } + if (ad->ad_id & ATH_DIAG_DYN) { + /* + * Allocate a buffer for the results (otherwise the HAL + * returns a pointer to a buffer where we can read the + * results). Note that we depend on the HAL leaving this + * pointer for us to use below in reclaiming the buffer; + * may want to be more defensive. + */ + outdata = malloc(outsize, M_TEMP, M_NOWAIT); + if (outdata == NULL) { + error = ENOMEM; + goto bad; + } + } + switch (id) { + default: + error = EINVAL; + } + if (outsize < ad->ad_out_size) + ad->ad_out_size = outsize; + if (outdata && copyout(outdata, ad->ad_out_data, ad->ad_out_size)) + error = EFAULT; +bad: + if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL) + free(indata, M_TEMP); + if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL) + free(outdata, M_TEMP); + return (error); +} + +/* + * XXX need to low_rssi_thresh config from ath9k, to support CUS198 + * antenna diversity correctly. + */ +static HAL_BOOL +ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta, int mindelta, + int main_rssi_avg, int alt_rssi_avg, int pkt_count) +{ + return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) && + (alt_rssi_avg > main_rssi_avg + maxdelta)) || + (alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50); +} + +static void +ath_lnaconf_alt_good_scan(struct if_ath_ant_comb_state *antcomb, + HAL_ANT_COMB_CONFIG *ant_conf, int main_rssi_avg) +{ + antcomb->quick_scan_cnt = 0; + + if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA2) + antcomb->rssi_lna2 = main_rssi_avg; + else if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA1) + antcomb->rssi_lna1 = main_rssi_avg; + + switch ((ant_conf->main_lna_conf << 4) | ant_conf->alt_lna_conf) { + case (0x10): /* LNA2 A-B */ + antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2; + antcomb->first_quick_scan_conf = + HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2; + antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1; + break; + case (0x20): /* LNA1 A-B */ + antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2; + antcomb->first_quick_scan_conf = + HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2; + antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2; + break; + case (0x21): /* LNA1 LNA2 */ + antcomb->main_conf = HAL_ANT_DIV_COMB_LNA2; + antcomb->first_quick_scan_conf = + HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2; + antcomb->second_quick_scan_conf = + HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2; + break; + case (0x12): /* LNA2 LNA1 */ + antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1; + antcomb->first_quick_scan_conf = + HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2; + antcomb->second_quick_scan_conf = + HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2; + break; + case (0x13): /* LNA2 A+B */ + antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2; + antcomb->first_quick_scan_conf = + HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2; + antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1; + break; + case (0x23): /* LNA1 A+B */ + antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2; + antcomb->first_quick_scan_conf = + HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2; + antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2; + break; + default: + break; + } +} + +static void +ath_select_ant_div_from_quick_scan(struct if_ath_ant_comb_state *antcomb, + HAL_ANT_COMB_CONFIG *div_ant_conf, int main_rssi_avg, + int alt_rssi_avg, int alt_ratio) +{ + /* alt_good */ + switch (antcomb->quick_scan_cnt) { + case 0: + /* set alt to main, and alt to first conf */ + div_ant_conf->main_lna_conf = antcomb->main_conf; + div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf; + break; + case 1: + /* set alt to main, and alt to first conf */ + div_ant_conf->main_lna_conf = antcomb->main_conf; + div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf; + antcomb->rssi_first = main_rssi_avg; + antcomb->rssi_second = alt_rssi_avg; + + if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) { + /* main is LNA1 */ + if (ath_is_alt_ant_ratio_better(alt_ratio, + ATH_ANT_DIV_COMB_LNA1_DELTA_HI, + ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, + main_rssi_avg, alt_rssi_avg, + antcomb->total_pkt_count)) + antcomb->first_ratio = AH_TRUE; + else + antcomb->first_ratio = AH_FALSE; + } else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) { + if (ath_is_alt_ant_ratio_better(alt_ratio, + ATH_ANT_DIV_COMB_LNA1_DELTA_MID, + ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, + main_rssi_avg, alt_rssi_avg, + antcomb->total_pkt_count)) + antcomb->first_ratio = AH_TRUE; + else + antcomb->first_ratio = AH_FALSE; + } else { + if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) && + (alt_rssi_avg > main_rssi_avg + + ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) || + (alt_rssi_avg > main_rssi_avg)) && + (antcomb->total_pkt_count > 50)) + antcomb->first_ratio = AH_TRUE; + else + antcomb->first_ratio = AH_FALSE; + } + break; + case 2: + antcomb->alt_good = AH_FALSE; + antcomb->scan_not_start = AH_FALSE; + antcomb->scan = AH_FALSE; + antcomb->rssi_first = main_rssi_avg; + antcomb->rssi_third = alt_rssi_avg; + + if (antcomb->second_quick_scan_conf == HAL_ANT_DIV_COMB_LNA1) + antcomb->rssi_lna1 = alt_rssi_avg; + else if (antcomb->second_quick_scan_conf == + HAL_ANT_DIV_COMB_LNA2) + antcomb->rssi_lna2 = alt_rssi_avg; + else if (antcomb->second_quick_scan_conf == + HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2) { + if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) + antcomb->rssi_lna2 = main_rssi_avg; + else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) + antcomb->rssi_lna1 = main_rssi_avg; + } + + if (antcomb->rssi_lna2 > antcomb->rssi_lna1 + + ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA) + div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA2; + else + div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA1; + + if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) { + if (ath_is_alt_ant_ratio_better(alt_ratio, + ATH_ANT_DIV_COMB_LNA1_DELTA_HI, + ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, + main_rssi_avg, alt_rssi_avg, + antcomb->total_pkt_count)) + antcomb->second_ratio = AH_TRUE; + else + antcomb->second_ratio = AH_FALSE; + } else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) { + if (ath_is_alt_ant_ratio_better(alt_ratio, + ATH_ANT_DIV_COMB_LNA1_DELTA_MID, + ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, + main_rssi_avg, alt_rssi_avg, + antcomb->total_pkt_count)) + antcomb->second_ratio = AH_TRUE; + else + antcomb->second_ratio = AH_FALSE; + } else { + if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) && + (alt_rssi_avg > main_rssi_avg + + ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) || + (alt_rssi_avg > main_rssi_avg)) && + (antcomb->total_pkt_count > 50)) + antcomb->second_ratio = AH_TRUE; + else + antcomb->second_ratio = AH_FALSE; + } + + /* set alt to the conf with maximun ratio */ + if (antcomb->first_ratio && antcomb->second_ratio) { + if (antcomb->rssi_second > antcomb->rssi_third) { + /* first alt*/ + if ((antcomb->first_quick_scan_conf == + HAL_ANT_DIV_COMB_LNA1) || + (antcomb->first_quick_scan_conf == + HAL_ANT_DIV_COMB_LNA2)) + /* Set alt LNA1 or LNA2*/ + if (div_ant_conf->main_lna_conf == + HAL_ANT_DIV_COMB_LNA2) + div_ant_conf->alt_lna_conf = + HAL_ANT_DIV_COMB_LNA1; + else + div_ant_conf->alt_lna_conf = + HAL_ANT_DIV_COMB_LNA2; + else + /* Set alt to A+B or A-B */ + div_ant_conf->alt_lna_conf = + antcomb->first_quick_scan_conf; + } else if ((antcomb->second_quick_scan_conf == + HAL_ANT_DIV_COMB_LNA1) || + (antcomb->second_quick_scan_conf == + HAL_ANT_DIV_COMB_LNA2)) { + /* Set alt LNA1 or LNA2 */ + if (div_ant_conf->main_lna_conf == + HAL_ANT_DIV_COMB_LNA2) + div_ant_conf->alt_lna_conf = + HAL_ANT_DIV_COMB_LNA1; + else + div_ant_conf->alt_lna_conf = + HAL_ANT_DIV_COMB_LNA2; + } else { + /* Set alt to A+B or A-B */ + div_ant_conf->alt_lna_conf = + antcomb->second_quick_scan_conf; + } + } else if (antcomb->first_ratio) { + /* first alt */ + if ((antcomb->first_quick_scan_conf == + HAL_ANT_DIV_COMB_LNA1) || + (antcomb->first_quick_scan_conf == + HAL_ANT_DIV_COMB_LNA2)) + /* Set alt LNA1 or LNA2 */ + if (div_ant_conf->main_lna_conf == + HAL_ANT_DIV_COMB_LNA2) + div_ant_conf->alt_lna_conf = + HAL_ANT_DIV_COMB_LNA1; + else + div_ant_conf->alt_lna_conf = + HAL_ANT_DIV_COMB_LNA2; + else + /* Set alt to A+B or A-B */ + div_ant_conf->alt_lna_conf = + antcomb->first_quick_scan_conf; + } else if (antcomb->second_ratio) { + /* second alt */ + if ((antcomb->second_quick_scan_conf == + HAL_ANT_DIV_COMB_LNA1) || + (antcomb->second_quick_scan_conf == + HAL_ANT_DIV_COMB_LNA2)) + /* Set alt LNA1 or LNA2 */ + if (div_ant_conf->main_lna_conf == + HAL_ANT_DIV_COMB_LNA2) + div_ant_conf->alt_lna_conf = + HAL_ANT_DIV_COMB_LNA1; + else + div_ant_conf->alt_lna_conf = + HAL_ANT_DIV_COMB_LNA2; + else + /* Set alt to A+B or A-B */ + div_ant_conf->alt_lna_conf = + antcomb->second_quick_scan_conf; + } else { + /* main is largest */ + if ((antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) || + (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2)) + /* Set alt LNA1 or LNA2 */ + if (div_ant_conf->main_lna_conf == + HAL_ANT_DIV_COMB_LNA2) + div_ant_conf->alt_lna_conf = + HAL_ANT_DIV_COMB_LNA1; + else + div_ant_conf->alt_lna_conf = + HAL_ANT_DIV_COMB_LNA2; + else + /* Set alt to A+B or A-B */ + div_ant_conf->alt_lna_conf = antcomb->main_conf; + } + break; + default: + break; + } +} + +static void +ath_ant_adjust_fast_divbias(struct if_ath_ant_comb_state *antcomb, + int alt_ratio, int alt_ant_ratio_th, u_int config_group, + HAL_ANT_COMB_CONFIG *pdiv_ant_conf) +{ + + if (config_group == HAL_ANTDIV_CONFIG_GROUP_1) { + switch ((pdiv_ant_conf->main_lna_conf << 4) + | pdiv_ant_conf->alt_lna_conf) { + case (0x01): //A-B LNA2 + pdiv_ant_conf->fast_div_bias = 0x1; + pdiv_ant_conf->main_gaintb = 0; + pdiv_ant_conf->alt_gaintb = 0; + break; + case (0x02): //A-B LNA1 + pdiv_ant_conf->fast_div_bias = 0x1; + pdiv_ant_conf->main_gaintb = 0; + pdiv_ant_conf->alt_gaintb = 0; + break; + case (0x03): //A-B A+B + pdiv_ant_conf->fast_div_bias = 0x1; + pdiv_ant_conf->main_gaintb = 0; + pdiv_ant_conf->alt_gaintb = 0; + break; + case (0x10): //LNA2 A-B + if ((antcomb->scan == 0) + && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) { + pdiv_ant_conf->fast_div_bias = 0x3f; + } else { + pdiv_ant_conf->fast_div_bias = 0x1; + } + pdiv_ant_conf->main_gaintb = 0; + pdiv_ant_conf->alt_gaintb = 0; + break; + case (0x12): //LNA2 LNA1 + pdiv_ant_conf->fast_div_bias = 0x1; + pdiv_ant_conf->main_gaintb = 0; + pdiv_ant_conf->alt_gaintb = 0; + break; + case (0x13): //LNA2 A+B + if ((antcomb->scan == 0) + && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) { + pdiv_ant_conf->fast_div_bias = 0x3f; + } else { + pdiv_ant_conf->fast_div_bias = 0x1; + } + pdiv_ant_conf->main_gaintb = 0; + pdiv_ant_conf->alt_gaintb = 0; + break; + case (0x20): //LNA1 A-B + if ((antcomb->scan == 0) + && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) { + pdiv_ant_conf->fast_div_bias = 0x3f; + } else { + pdiv_ant_conf->fast_div_bias = 0x1; + } + pdiv_ant_conf->main_gaintb = 0; + pdiv_ant_conf->alt_gaintb = 0; + break; + case (0x21): //LNA1 LNA2 + pdiv_ant_conf->fast_div_bias = 0x1; + pdiv_ant_conf->main_gaintb = 0; + pdiv_ant_conf->alt_gaintb = 0; + break; + case (0x23): //LNA1 A+B + if ((antcomb->scan == 0) + && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) { + pdiv_ant_conf->fast_div_bias = 0x3f; + } else { + pdiv_ant_conf->fast_div_bias = 0x1; + } + pdiv_ant_conf->main_gaintb = 0; + pdiv_ant_conf->alt_gaintb = 0; + break; + case (0x30): //A+B A-B + pdiv_ant_conf->fast_div_bias = 0x1; + pdiv_ant_conf->main_gaintb = 0; + pdiv_ant_conf->alt_gaintb = 0; + break; + case (0x31): //A+B LNA2 + pdiv_ant_conf->fast_div_bias = 0x1; + pdiv_ant_conf->main_gaintb = 0; + pdiv_ant_conf->alt_gaintb = 0; + break; + case (0x32): //A+B LNA1 + pdiv_ant_conf->fast_div_bias = 0x1; + pdiv_ant_conf->main_gaintb = 0; + pdiv_ant_conf->alt_gaintb = 0; + break; + default: + break; + } + } else if (config_group == HAL_ANTDIV_CONFIG_GROUP_2) { + switch ((pdiv_ant_conf->main_lna_conf << 4) + | pdiv_ant_conf->alt_lna_conf) { + case (0x01): //A-B LNA2 + pdiv_ant_conf->fast_div_bias = 0x1; + pdiv_ant_conf->main_gaintb = 0; + pdiv_ant_conf->alt_gaintb = 0; + break; + case (0x02): //A-B LNA1 + pdiv_ant_conf->fast_div_bias = 0x1; + pdiv_ant_conf->main_gaintb = 0; + pdiv_ant_conf->alt_gaintb = 0; + break; + case (0x03): //A-B A+B + pdiv_ant_conf->fast_div_bias = 0x1; + pdiv_ant_conf->main_gaintb = 0; + pdiv_ant_conf->alt_gaintb = 0; + break; + case (0x10): //LNA2 A-B + if ((antcomb->scan == 0) + && (alt_ratio > alt_ant_ratio_th)) { + pdiv_ant_conf->fast_div_bias = 0x1; + } else { + pdiv_ant_conf->fast_div_bias = 0x2; + } + pdiv_ant_conf->main_gaintb = 0; + pdiv_ant_conf->alt_gaintb = 0; + break; + case (0x12): //LNA2 LNA1 + pdiv_ant_conf->fast_div_bias = 0x1; + pdiv_ant_conf->main_gaintb = 0; + pdiv_ant_conf->alt_gaintb = 0; + break; + case (0x13): //LNA2 A+B + if ((antcomb->scan == 0) + && (alt_ratio > alt_ant_ratio_th)) { + pdiv_ant_conf->fast_div_bias = 0x1; + } else { + pdiv_ant_conf->fast_div_bias = 0x2; + } + pdiv_ant_conf->main_gaintb = 0; + pdiv_ant_conf->alt_gaintb = 0; + break; + case (0x20): //LNA1 A-B + if ((antcomb->scan == 0) + && (alt_ratio > alt_ant_ratio_th)) { + pdiv_ant_conf->fast_div_bias = 0x1; + } else { + pdiv_ant_conf->fast_div_bias = 0x2; + } + pdiv_ant_conf->main_gaintb = 0; + pdiv_ant_conf->alt_gaintb = 0; + break; + case (0x21): //LNA1 LNA2 + pdiv_ant_conf->fast_div_bias = 0x1; + pdiv_ant_conf->main_gaintb = 0; + pdiv_ant_conf->alt_gaintb = 0; + break; + case (0x23): //LNA1 A+B + if ((antcomb->scan == 0) + && (alt_ratio > alt_ant_ratio_th)) { + pdiv_ant_conf->fast_div_bias = 0x1; + } else { + pdiv_ant_conf->fast_div_bias = 0x2; + } + pdiv_ant_conf->main_gaintb = 0; + pdiv_ant_conf->alt_gaintb = 0; + break; + case (0x30): //A+B A-B + pdiv_ant_conf->fast_div_bias = 0x1; + pdiv_ant_conf->main_gaintb = 0; + pdiv_ant_conf->alt_gaintb = 0; + break; + case (0x31): //A+B LNA2 + pdiv_ant_conf->fast_div_bias = 0x1; + pdiv_ant_conf->main_gaintb = 0; + pdiv_ant_conf->alt_gaintb = 0; + break; + case (0x32): //A+B LNA1 + pdiv_ant_conf->fast_div_bias = 0x1; + pdiv_ant_conf->main_gaintb = 0; + pdiv_ant_conf->alt_gaintb = 0; + break; + default: + break; + } + } else { /* DEFAULT_ANTDIV_CONFIG_GROUP */ + switch ((pdiv_ant_conf->main_lna_conf << 4) | pdiv_ant_conf->alt_lna_conf) { + case (0x01): //A-B LNA2 + pdiv_ant_conf->fast_div_bias = 0x3b; + break; + case (0x02): //A-B LNA1 + pdiv_ant_conf->fast_div_bias = 0x3d; + break; + case (0x03): //A-B A+B + pdiv_ant_conf->fast_div_bias = 0x1; + break; + case (0x10): //LNA2 A-B + pdiv_ant_conf->fast_div_bias = 0x7; + break; + case (0x12): //LNA2 LNA1 + pdiv_ant_conf->fast_div_bias = 0x2; + break; + case (0x13): //LNA2 A+B + pdiv_ant_conf->fast_div_bias = 0x7; + break; + case (0x20): //LNA1 A-B + pdiv_ant_conf->fast_div_bias = 0x6; + break; + case (0x21): //LNA1 LNA2 + pdiv_ant_conf->fast_div_bias = 0x0; + break; + case (0x23): //LNA1 A+B + pdiv_ant_conf->fast_div_bias = 0x6; + break; + case (0x30): //A+B A-B + pdiv_ant_conf->fast_div_bias = 0x1; + break; + case (0x31): //A+B LNA2 + pdiv_ant_conf->fast_div_bias = 0x3b; + break; + case (0x32): //A+B LNA1 + pdiv_ant_conf->fast_div_bias = 0x3d; + break; + default: + break; + } + } +} + +/* + * AR9485/AR933x TODO: + * + Select a ratio based on whether RSSI is low or not; but I need + * to figure out what "low_rssi_th" is sourced from. + * + What's ath_ant_div_comb_alt_check() in the reference driver do? + * + .. and there's likely a bunch of other things to include in this. + */ + +/* Antenna diversity and combining */ +void +ath_lna_rx_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs, + unsigned long ticks, int thz) +{ + HAL_ANT_COMB_CONFIG div_ant_conf; + struct if_ath_ant_comb_state *antcomb = sc->sc_lna_div; + int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set; + int curr_main_set, curr_bias; + int main_rssi = rs->rs_rssi_ctl[0]; + int alt_rssi = rs->rs_rssi_ctl[1]; + int rx_ant_conf, main_ant_conf, alt_ant_conf; + HAL_BOOL short_scan = AH_FALSE; + + rx_ant_conf = (rs->rs_rssi_ctl[2] >> 4) & ATH_ANT_RX_MASK; + main_ant_conf = (rs->rs_rssi_ctl[2] >> 2) & ATH_ANT_RX_MASK; + alt_ant_conf = (rs->rs_rssi_ctl[2] >> 0) & ATH_ANT_RX_MASK; + +#if 0 + DPRINTF(sc, ATH_DEBUG_DIVERSITY, + "%s: RSSI %d/%d, conf %x/%x, rxconf %x, LNA: %d; ANT: %d; " + "FastDiv: %d\n", + __func__, + main_rssi, + alt_rssi, + main_ant_conf, + alt_ant_conf, + rx_ant_conf, + !!(rs->rs_rssi_ctl[2] & 0x80), + !!(rs->rs_rssi_ctl[2] & 0x40), + !!(rs->rs_rssi_ext[2] & 0x40)); +#endif + + /* + * If LNA diversity combining isn't enabled, don't run this. + */ + if (! sc->sc_dolnadiv) + return; + + /* + * XXX this is ugly, but the HAL code attaches the + * LNA diversity to the TX antenna settings. + * I don't know why. + */ + if (sc->sc_txantenna != HAL_ANT_VARIABLE) + return; + + /* Record packet only when alt_rssi is positive */ + if (main_rssi > 0 && alt_rssi > 0) { + antcomb->total_pkt_count++; + antcomb->main_total_rssi += main_rssi; + antcomb->alt_total_rssi += alt_rssi; + if (main_ant_conf == rx_ant_conf) + antcomb->main_recv_cnt++; + else + antcomb->alt_recv_cnt++; + } + + /* Short scan check */ + if (antcomb->scan && antcomb->alt_good) { + if (ieee80211_time_after(ticks, antcomb->scan_start_time + + msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR))) + short_scan = AH_TRUE; + else + if (antcomb->total_pkt_count == + ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) { + alt_ratio = ((antcomb->alt_recv_cnt * 100) / + antcomb->total_pkt_count); + if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO) + short_scan = AH_TRUE; + } + } + +#if 0 + DPRINTF(sc, ATH_DEBUG_DIVERSITY, + "%s: total pkt=%d, aggr=%d, short_scan=%d\n", + __func__, + antcomb->total_pkt_count, + !! (rs->rs_moreaggr), + !! (short_scan)); +#endif + + if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) || + rs->rs_moreaggr) && !short_scan) + return; + + if (antcomb->total_pkt_count) { + alt_ratio = ((antcomb->alt_recv_cnt * 100) / + antcomb->total_pkt_count); + main_rssi_avg = (antcomb->main_total_rssi / + antcomb->total_pkt_count); + alt_rssi_avg = (antcomb->alt_total_rssi / + antcomb->total_pkt_count); + } + + OS_MEMZERO(&div_ant_conf, sizeof(div_ant_conf)); + + ath_hal_div_comb_conf_get(sc->sc_ah, &div_ant_conf); + curr_alt_set = div_ant_conf.alt_lna_conf; + curr_main_set = div_ant_conf.main_lna_conf; + curr_bias = div_ant_conf.fast_div_bias; + + antcomb->count++; + + if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) { + if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) { + ath_lnaconf_alt_good_scan(antcomb, &div_ant_conf, + main_rssi_avg); + antcomb->alt_good = AH_TRUE; + } else { + antcomb->alt_good = AH_FALSE; + } + + antcomb->count = 0; + antcomb->scan = AH_TRUE; + antcomb->scan_not_start = AH_TRUE; + } + + if (!antcomb->scan) { + if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) { + if (curr_alt_set == HAL_ANT_DIV_COMB_LNA2) { + /* Switch main and alt LNA */ + div_ant_conf.main_lna_conf = + HAL_ANT_DIV_COMB_LNA2; + div_ant_conf.alt_lna_conf = + HAL_ANT_DIV_COMB_LNA1; + } else if (curr_alt_set == HAL_ANT_DIV_COMB_LNA1) { + div_ant_conf.main_lna_conf = + HAL_ANT_DIV_COMB_LNA1; + div_ant_conf.alt_lna_conf = + HAL_ANT_DIV_COMB_LNA2; + } + + goto div_comb_done; + } else if ((curr_alt_set != HAL_ANT_DIV_COMB_LNA1) && + (curr_alt_set != HAL_ANT_DIV_COMB_LNA2)) { + /* Set alt to another LNA */ + if (curr_main_set == HAL_ANT_DIV_COMB_LNA2) + div_ant_conf.alt_lna_conf = + HAL_ANT_DIV_COMB_LNA1; + else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1) + div_ant_conf.alt_lna_conf = + HAL_ANT_DIV_COMB_LNA2; + + goto div_comb_done; + } + + if ((alt_rssi_avg < (main_rssi_avg + + antcomb->lna1_lna2_delta))) + goto div_comb_done; + } + + if (!antcomb->scan_not_start) { + switch (curr_alt_set) { + case HAL_ANT_DIV_COMB_LNA2: + antcomb->rssi_lna2 = alt_rssi_avg; + antcomb->rssi_lna1 = main_rssi_avg; + antcomb->scan = AH_TRUE; + /* set to A+B */ + div_ant_conf.main_lna_conf = + HAL_ANT_DIV_COMB_LNA1; + div_ant_conf.alt_lna_conf = + HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2; + break; + case HAL_ANT_DIV_COMB_LNA1: + antcomb->rssi_lna1 = alt_rssi_avg; + antcomb->rssi_lna2 = main_rssi_avg; + antcomb->scan = AH_TRUE; + /* set to A+B */ + div_ant_conf.main_lna_conf = HAL_ANT_DIV_COMB_LNA2; + div_ant_conf.alt_lna_conf = + HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2; + break; + case HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2: + antcomb->rssi_add = alt_rssi_avg; + antcomb->scan = AH_TRUE; + /* set to A-B */ + div_ant_conf.alt_lna_conf = + HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2; + break; + case HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2: + antcomb->rssi_sub = alt_rssi_avg; + antcomb->scan = AH_FALSE; + if (antcomb->rssi_lna2 > + (antcomb->rssi_lna1 + + ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) { + /* use LNA2 as main LNA */ + if ((antcomb->rssi_add > antcomb->rssi_lna1) && + (antcomb->rssi_add > antcomb->rssi_sub)) { + /* set to A+B */ + div_ant_conf.main_lna_conf = + HAL_ANT_DIV_COMB_LNA2; + div_ant_conf.alt_lna_conf = + HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2; + } else if (antcomb->rssi_sub > + antcomb->rssi_lna1) { + /* set to A-B */ + div_ant_conf.main_lna_conf = + HAL_ANT_DIV_COMB_LNA2; + div_ant_conf.alt_lna_conf = + HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2; + } else { + /* set to LNA1 */ + div_ant_conf.main_lna_conf = + HAL_ANT_DIV_COMB_LNA2; + div_ant_conf.alt_lna_conf = + HAL_ANT_DIV_COMB_LNA1; + } + } else { + /* use LNA1 as main LNA */ + if ((antcomb->rssi_add > antcomb->rssi_lna2) && + (antcomb->rssi_add > antcomb->rssi_sub)) { + /* set to A+B */ + div_ant_conf.main_lna_conf = + HAL_ANT_DIV_COMB_LNA1; + div_ant_conf.alt_lna_conf = + HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2; + } else if (antcomb->rssi_sub > + antcomb->rssi_lna1) { + /* set to A-B */ + div_ant_conf.main_lna_conf = + HAL_ANT_DIV_COMB_LNA1; + div_ant_conf.alt_lna_conf = + HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2; + } else { + /* set to LNA2 */ + div_ant_conf.main_lna_conf = + HAL_ANT_DIV_COMB_LNA1; + div_ant_conf.alt_lna_conf = + HAL_ANT_DIV_COMB_LNA2; + } + } + break; + default: + break; + } + } else { + if (!antcomb->alt_good) { + antcomb->scan_not_start = AH_FALSE; + /* Set alt to another LNA */ + if (curr_main_set == HAL_ANT_DIV_COMB_LNA2) { + div_ant_conf.main_lna_conf = + HAL_ANT_DIV_COMB_LNA2; + div_ant_conf.alt_lna_conf = + HAL_ANT_DIV_COMB_LNA1; + } else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1) { + div_ant_conf.main_lna_conf = + HAL_ANT_DIV_COMB_LNA1; + div_ant_conf.alt_lna_conf = + HAL_ANT_DIV_COMB_LNA2; + } + goto div_comb_done; + } + } + + ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf, + main_rssi_avg, alt_rssi_avg, + alt_ratio); + + antcomb->quick_scan_cnt++; + +div_comb_done: +#if 0 + ath_ant_div_conf_fast_divbias(&div_ant_conf); +#endif + + ath_ant_adjust_fast_divbias(antcomb, + alt_ratio, + ATH_ANT_DIV_COMB_ALT_ANT_RATIO, + div_ant_conf.antdiv_configgroup, + &div_ant_conf); + + ath_hal_div_comb_conf_set(sc->sc_ah, &div_ant_conf); + + DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: total_pkt_count=%d\n", + __func__, antcomb->total_pkt_count); + + DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_total_rssi=%d\n", + __func__, antcomb->main_total_rssi); + DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_total_rssi=%d\n", + __func__, antcomb->alt_total_rssi); + + DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_rssi_avg=%d\n", + __func__, main_rssi_avg); + DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_alt_rssi_avg=%d\n", + __func__, alt_rssi_avg); + + DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_recv_cnt=%d\n", + __func__, antcomb->main_recv_cnt); + DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_recv_cnt=%d\n", + __func__, antcomb->alt_recv_cnt); + +// if (curr_alt_set != div_ant_conf.alt_lna_conf) + DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: lna_conf: %x -> %x\n", + __func__, curr_alt_set, div_ant_conf.alt_lna_conf); +// if (curr_main_set != div_ant_conf.main_lna_conf) + DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_lna_conf: %x -> %x\n", + __func__, curr_main_set, div_ant_conf.main_lna_conf); +// if (curr_bias != div_ant_conf.fast_div_bias) + DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: fast_div_bias: %x -> %x\n", + __func__, curr_bias, div_ant_conf.fast_div_bias); + + antcomb->scan_start_time = ticks; + antcomb->total_pkt_count = 0; + antcomb->main_total_rssi = 0; + antcomb->alt_total_rssi = 0; + antcomb->main_recv_cnt = 0; + antcomb->alt_recv_cnt = 0; +} + diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_lna_div.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_lna_div.h new file mode 100644 index 0000000000..f0cdeaefb1 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_lna_div.h @@ -0,0 +1,89 @@ +/*- + * Copyright (c) 2013 Adrian Chadd + * 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. + * + * $FreeBSD: releng/11.1/sys/dev/ath/if_ath_lna_div.h 251730 2013-06-14 03:42:10Z adrian $ + */ +#ifndef __IF_ATH_LNA_DIV_H__ +#define __IF_ATH_LNA_DIV_H__ + +#define ATH_ANT_RX_CURRENT_SHIFT 4 +#define ATH_ANT_RX_MAIN_SHIFT 2 +#define ATH_ANT_RX_MASK 0x3 + +#define ATH_ANT_DIV_COMB_SHORT_SCAN_INTR 50 +#define ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT 0x100 +#define ATH_ANT_DIV_COMB_MAX_PKTCOUNT 0x200 +#define ATH_ANT_DIV_COMB_INIT_COUNT 95 +#define ATH_ANT_DIV_COMB_MAX_COUNT 100 +#define ATH_ANT_DIV_COMB_ALT_ANT_RATIO 30 +#define ATH_ANT_DIV_COMB_ALT_ANT_RATIO2 20 + +#define ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA -1 +#define ATH_ANT_DIV_COMB_LNA1_DELTA_HI -4 +#define ATH_ANT_DIV_COMB_LNA1_DELTA_MID -2 +#define ATH_ANT_DIV_COMB_LNA1_DELTA_LOW 2 + +struct if_ath_ant_comb_state { + uint16_t count; + uint16_t total_pkt_count; + HAL_BOOL scan; + HAL_BOOL scan_not_start; + int main_total_rssi; + int alt_total_rssi; + int alt_recv_cnt; + int main_recv_cnt; + int rssi_lna1; + int rssi_lna2; + int rssi_add; + int rssi_sub; + int rssi_first; + int rssi_second; + int rssi_third; + HAL_BOOL alt_good; + int quick_scan_cnt; + int main_conf; + HAL_ANT_DIV_COMB_LNA_CONF first_quick_scan_conf; + HAL_ANT_DIV_COMB_LNA_CONF second_quick_scan_conf; + int first_bias; + int second_bias; + HAL_BOOL first_ratio; + HAL_BOOL second_ratio; + unsigned long scan_start_time; + int lna1_lna2_delta; +}; + +extern int ath_lna_div_attach(struct ath_softc *sc); +extern int ath_lna_div_detach(struct ath_softc *sc); +extern int ath_lna_div_ioctl(struct ath_softc *sc, struct ath_diag *ad); +extern int ath_lna_div_enable(struct ath_softc *sc, + const struct ieee80211_channel *ch); + +extern void ath_lna_rx_comb_scan(struct ath_softc *sc, + struct ath_rx_status *rs, unsigned long ticks, int thz); + +#endif /* __IF_ATH_LNA_DIV_H__ */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_misc.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_misc.h index 35feea212e..f6531ab70f 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_misc.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_misc.h @@ -26,7 +26,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/if_ath_misc.h 298359 2016-04-20 18:29:30Z avos $ */ #ifndef __IF_ATH_MISC_H__ #define __IF_ATH_MISC_H__ @@ -39,20 +39,104 @@ * and into something else. */ -/* unaligned little endian access */ -#define LE_READ_2(p) \ - ((u_int16_t) \ - ((((u_int8_t *)(p))[0] ) | (((u_int8_t *)(p))[1] << 8))) -#define LE_READ_4(p) \ - ((u_int32_t) \ - ((((u_int8_t *)(p))[0] ) | (((u_int8_t *)(p))[1] << 8) | \ - (((u_int8_t *)(p))[2] << 16) | (((u_int8_t *)(p))[3] << 24))) +extern int ath_rxbuf; +extern int ath_txbuf; +extern int ath_txbuf_mgmt; extern int ath_tx_findrix(const struct ath_softc *sc, uint8_t rate); -extern struct ath_buf * ath_getbuf(struct ath_softc *sc); -extern struct ath_buf * _ath_getbuf_locked(struct ath_softc *sc); +extern struct ath_buf * ath_getbuf(struct ath_softc *sc, + ath_buf_type_t btype); +extern struct ath_buf * _ath_getbuf_locked(struct ath_softc *sc, + ath_buf_type_t btype); +extern struct ath_buf * ath_buf_clone(struct ath_softc *sc, + struct ath_buf *bf); +/* XXX change this to NULL the buffer pointer? */ +extern void ath_freebuf(struct ath_softc *sc, struct ath_buf *bf); +extern void ath_returnbuf_head(struct ath_softc *sc, struct ath_buf *bf); +extern void ath_returnbuf_tail(struct ath_softc *sc, struct ath_buf *bf); -extern int ath_reset(struct ifnet *); +extern int ath_reset(struct ath_softc *, ATH_RESET_TYPE); +extern void ath_tx_default_comp(struct ath_softc *sc, struct ath_buf *bf, + int fail); +extern void ath_tx_update_ratectrl(struct ath_softc *sc, + struct ieee80211_node *ni, struct ath_rc_series *rc, + struct ath_tx_status *ts, int frmlen, int nframes, int nbad); + +extern int ath_hal_gethangstate(struct ath_hal *ah, uint32_t mask, + uint32_t *hangs); + +extern void ath_tx_freebuf(struct ath_softc *sc, struct ath_buf *bf, + int status); +extern void ath_txq_freeholdingbuf(struct ath_softc *sc, + struct ath_txq *txq); + +extern void ath_txqmove(struct ath_txq *dst, struct ath_txq *src); + +extern void ath_mode_init(struct ath_softc *sc); + +extern void ath_setdefantenna(struct ath_softc *sc, u_int antenna); + +extern void ath_setslottime(struct ath_softc *sc); + +extern void ath_legacy_attach_comp_func(struct ath_softc *sc); + +extern void ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq); + +extern void ath_legacy_tx_drain(struct ath_softc *sc, + ATH_RESET_TYPE reset_type); + +extern void ath_tx_process_buf_completion(struct ath_softc *sc, + struct ath_txq *txq, struct ath_tx_status *ts, struct ath_buf *bf); + +extern int ath_stoptxdma(struct ath_softc *sc); + +extern void ath_tx_update_tim(struct ath_softc *sc, + struct ieee80211_node *ni, int enable); + +/* + * This is only here so that the RX proc function can call it. + * It's very likely that the "start TX after RX" call should be + * done via something in if_ath.c, moving "rx tasklet" into + * if_ath.c and do the ath_start() call there. Once that's done, + * we can kill this. + */ +extern void ath_start(struct ifnet *ifp); +extern void ath_start_task(void *arg, int npending); + +extern void ath_tx_dump(struct ath_softc *sc, struct ath_txq *txq); + +/* + * Power state tracking. + */ +extern void _ath_power_setpower(struct ath_softc *sc, int power_state, const char *file, int line); +extern void _ath_power_set_selfgen(struct ath_softc *sc, int power_state, const char *file, int line); +extern void _ath_power_set_power_state(struct ath_softc *sc, int power_state, const char *file, int line); +extern void _ath_power_restore_power_state(struct ath_softc *sc, const char *file, int line); + +#define ath_power_setpower(sc, ps) _ath_power_setpower(sc, ps, __FILE__, __LINE__) +#define ath_power_setselfgen(sc, ps) _ath_power_set_selfgen(sc, ps, __FILE__, __LINE__) +#define ath_power_set_power_state(sc, ps) _ath_power_set_power_state(sc, ps, __FILE__, __LINE__) +#define ath_power_restore_power_state(sc) _ath_power_restore_power_state(sc, __FILE__, __LINE__) + +/* + * Kick the frame TX task. + */ +static inline void +ath_tx_kick(struct ath_softc *sc) +{ + + /* XXX NULL for now */ +} + +/* + * Kick the software TX queue task. + */ +static inline void +ath_tx_swq_kick(struct ath_softc *sc) +{ + + taskqueue_enqueue(sc->sc_tq, &sc->sc_txqtask); +} #endif diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_pci.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_pci.c index e0428d780c..0c90f06f63 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_pci.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_pci.c @@ -28,14 +28,16 @@ */ #include -__FBSDID("$FreeBSD$"); +__FBSDID("$FreeBSD: releng/11.1/sys/dev/ath/if_ath_pci.c 298939 2016-05-02 19:56:48Z pfg $"); /* * PCI/Cardbus front-end for the Atheros Wireless LAN controller driver. */ +#include "opt_ath.h" #include -#include +#include +#include #include #include #include @@ -48,10 +50,11 @@ __FBSDID("$FreeBSD$"); #include #include - + #include #include #include +#include #include @@ -60,6 +63,12 @@ __FBSDID("$FreeBSD$"); #include #include +/* For EEPROM firmware */ +#ifdef ATH_EEPROM_FIRMWARE +#include +#include +#endif /* ATH_EEPROM_FIRMWARE */ + /* * PCI glue. */ @@ -71,8 +80,143 @@ struct ath_pci_softc { void *sc_ih; /* interrupt handler */ }; +/* + * XXX eventually this should be some system level definition + * so modules will have probe/attach information like USB. + * But for now.. + */ +struct pci_device_id { + int vendor_id; + int device_id; + + int sub_vendor_id; + int sub_device_id; + + int driver_data; + + int match_populated:1; + int match_vendor_id:1; + int match_device_id:1; + int match_sub_vendor_id:1; + int match_sub_device_id:1; +}; + +#define PCI_VDEVICE(v, s) \ + .vendor_id = (v), \ + .device_id = (s), \ + .match_populated = 1, \ + .match_vendor_id = 1, \ + .match_device_id = 1 + +#define PCI_DEVICE_SUB(v, d, dv, ds) \ + .match_populated = 1, \ + .vendor_id = (v), .match_vendor_id = 1, \ + .device_id = (d), .match_device_id = 1, \ + .sub_vendor_id = (dv), .match_sub_vendor_id = 1, \ + .sub_device_id = (ds), .match_sub_device_id = 1 + +#define PCI_VENDOR_ID_ATHEROS 0x168c +#define PCI_VENDOR_ID_SAMSUNG 0x144d +#define PCI_VENDOR_ID_AZWAVE 0x1a3b +#define PCI_VENDOR_ID_FOXCONN 0x105b +#define PCI_VENDOR_ID_ATTANSIC 0x1969 +#define PCI_VENDOR_ID_ASUSTEK 0x1043 +#define PCI_VENDOR_ID_DELL 0x1028 +#define PCI_VENDOR_ID_QMI 0x1a32 +#define PCI_VENDOR_ID_LENOVO 0x17aa +#define PCI_VENDOR_ID_HP 0x103c + +#include "if_ath_pci_devlist.h" + +/* + * Attempt to find a match for the given device in + * the given device table. + * + * Returns the device structure or NULL if no matching + * PCI device is found. + */ +static const struct pci_device_id * +ath_pci_probe_device(device_t dev, const struct pci_device_id *dev_table, int nentries) +{ + int i; + int vendor_id, device_id; + int sub_vendor_id, sub_device_id; + + vendor_id = pci_get_vendor(dev); + device_id = pci_get_device(dev); + sub_vendor_id = pci_get_subvendor(dev); + sub_device_id = pci_get_subdevice(dev); + + for (i = 0; i < nentries; i++) { + /* Don't match on non-populated (eg empty) entries */ + if (! dev_table[i].match_populated) + continue; + + if (dev_table[i].match_vendor_id && + (dev_table[i].vendor_id != vendor_id)) + continue; + if (dev_table[i].match_device_id && + (dev_table[i].device_id != device_id)) + continue; + if (dev_table[i].match_sub_vendor_id && + (dev_table[i].sub_vendor_id != sub_vendor_id)) + continue; + if (dev_table[i].match_sub_device_id && + (dev_table[i].sub_device_id != sub_device_id)) + continue; + + /* Match */ + return (&dev_table[i]); + } + + return (NULL); +} + #define BS_BAR 0x10 #define PCIR_RETRY_TIMEOUT 0x41 +#define PCIR_CFG_PMCSR 0x48 + +#define DEFAULT_CACHESIZE 32 + +static void +ath_pci_setup(device_t dev) +{ + uint8_t cz; + + /* XXX TODO: need to override the _system_ saved copies of this */ + + /* + * If the cache line size is 0, force it to a reasonable + * value. + */ + cz = pci_read_config(dev, PCIR_CACHELNSZ, 1); + if (cz == 0) { + pci_write_config(dev, PCIR_CACHELNSZ, + DEFAULT_CACHESIZE / 4, 1); + } + + /* Override the system latency timer */ + pci_write_config(dev, PCIR_LATTIMER, 0xa8, 1); + + /* If a PCI NIC, force wakeup */ +#ifdef ATH_PCI_WAKEUP_WAR + /* XXX TODO: don't do this for non-PCI (ie, PCIe, Cardbus!) */ + if (1) { + uint16_t pmcsr; + pmcsr = pci_read_config(dev, PCIR_CFG_PMCSR, 2); + pmcsr |= 3; + pci_write_config(dev, PCIR_CFG_PMCSR, pmcsr, 2); + pmcsr &= ~3; + pci_write_config(dev, PCIR_CFG_PMCSR, pmcsr, 2); + } +#endif + + /* + * Disable retry timeout to keep PCI Tx retries from + * interfering with C3 CPU state. + */ + pci_write_config(dev, PCIR_RETRY_TIMEOUT, 0, 1); +} static int ath_pci_probe(device_t dev) @@ -94,21 +238,30 @@ ath_pci_attach(device_t dev) struct ath_softc *sc = &psc->sc_sc; int error = ENXIO; int rid; +#ifdef ATH_EEPROM_FIRMWARE + const struct firmware *fw = NULL; + const char *buf; +#endif + const struct pci_device_id *pd; sc->sc_dev = dev; + /* Do this lookup anyway; figure out what to do with it later */ + pd = ath_pci_probe_device(dev, ath_pci_id_table, nitems(ath_pci_id_table)); + if (pd) + sc->sc_pci_devinfo = pd->driver_data; + /* * Enable bus mastering. */ pci_enable_busmaster(dev); /* - * Disable retry timeout to keep PCI Tx retries from - * interfering with C3 CPU state. + * Setup other PCI bus configuration parameters. */ - pci_write_config(dev, PCIR_RETRY_TIMEOUT, 0, 1); + ath_pci_setup(dev); - /* + /* * Setup memory-mapping of PCI registers. */ rid = BS_BAR; @@ -118,8 +271,7 @@ ath_pci_attach(device_t dev) device_printf(dev, "cannot map register space\n"); goto bad; } - /* XXX uintptr_t is a bandaid for ia64; to be fixed */ - sc->sc_st = (HAL_BUS_TAG)(uintptr_t) rman_get_bustag(psc->sc_sr); + sc->sc_st = (HAL_BUS_TAG) rman_get_bustag(psc->sc_sr); sc->sc_sh = (HAL_BUS_HANDLE) rman_get_bushandle(psc->sc_sr); /* * Mark device invalid so any interrupts (shared or otherwise) @@ -127,6 +279,12 @@ ath_pci_attach(device_t dev) */ sc->sc_invalid = 1; + ATH_LOCK_INIT(sc); + ATH_PCU_LOCK_INIT(sc); + ATH_RX_LOCK_INIT(sc); + ATH_TX_LOCK_INIT(sc); + ATH_TXSTATUS_LOCK_INIT(sc); + /* * Arrange interrupt line. */ @@ -163,13 +321,44 @@ ath_pci_attach(device_t dev) goto bad3; } - ATH_LOCK_INIT(sc); +#ifdef ATH_EEPROM_FIRMWARE + /* + * If there's an EEPROM firmware image, load that in. + */ + if (resource_string_value(device_get_name(dev), device_get_unit(dev), + "eeprom_firmware", &buf) == 0) { + if (bootverbose) + device_printf(dev, "%s: looking up firmware @ '%s'\n", + __func__, buf); + + fw = firmware_get(buf); + if (fw == NULL) { + device_printf(dev, "%s: couldn't find firmware\n", + __func__); + goto bad4; + } + + device_printf(dev, "%s: EEPROM firmware @ %p\n", + __func__, fw->data); + sc->sc_eepromdata = + malloc(fw->datasize, M_TEMP, M_WAITOK | M_ZERO); + if (! sc->sc_eepromdata) { + device_printf(dev, "%s: can't malloc eepromdata\n", + __func__); + goto bad4; + } + memcpy(sc->sc_eepromdata, fw->data, fw->datasize); + firmware_put(fw, 0); + } +#endif /* ATH_EEPROM_FIRMWARE */ error = ath_attach(pci_get_device(dev), sc); if (error == 0) /* success */ return 0; - ATH_LOCK_DESTROY(sc); +#ifdef ATH_EEPROM_FIRMWARE +bad4: +#endif bus_dma_tag_destroy(sc->sc_dmat); bad3: bus_teardown_intr(dev, psc->sc_irq, psc->sc_ih); @@ -177,6 +366,13 @@ bad2: bus_release_resource(dev, SYS_RES_IRQ, 0, psc->sc_irq); bad1: bus_release_resource(dev, SYS_RES_MEMORY, BS_BAR, psc->sc_sr); + + ATH_TXSTATUS_LOCK_DESTROY(sc); + ATH_PCU_LOCK_DESTROY(sc); + ATH_RX_LOCK_DESTROY(sc); + ATH_TX_LOCK_DESTROY(sc); + ATH_LOCK_DESTROY(sc); + bad: return (error); } @@ -190,6 +386,11 @@ ath_pci_detach(device_t dev) /* check if device was removed */ sc->sc_invalid = !bus_child_present(dev); + /* + * Do a config read to clear pre-existing pci error status. + */ + (void) pci_read_config(dev, PCIR_COMMAND, 4); + ath_detach(sc); bus_generic_detach(dev); @@ -199,6 +400,13 @@ ath_pci_detach(device_t dev) bus_dma_tag_destroy(sc->sc_dmat); bus_release_resource(dev, SYS_RES_MEMORY, BS_BAR, psc->sc_sr); + if (sc->sc_eepromdata) + free(sc->sc_eepromdata, M_TEMP); + + ATH_TXSTATUS_LOCK_DESTROY(sc); + ATH_PCU_LOCK_DESTROY(sc); + ATH_RX_LOCK_DESTROY(sc); + ATH_TX_LOCK_DESTROY(sc); ATH_LOCK_DESTROY(sc); return (0); @@ -228,6 +436,11 @@ ath_pci_resume(device_t dev) { struct ath_pci_softc *psc = device_get_softc(dev); + /* + * Suspend/resume resets the PCI configuration space. + */ + ath_pci_setup(dev); + ath_resume(&psc->sc_sc); return (0); diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_pci_devlist.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_pci_devlist.h new file mode 100644 index 0000000000..cae84b47a4 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_pci_devlist.h @@ -0,0 +1,669 @@ +/*- + * Copyright (c) 2014 Qualcomm Atheros. + * 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. + * + * $FreeBSD: releng/11.1/sys/dev/ath/if_ath_pci_devlist.h 272295 2014-09-30 05:50:34Z adrian $ + */ + +static const struct pci_device_id ath_pci_id_table[] = { + { PCI_VDEVICE(PCI_VENDOR_ID_ATHEROS, 0x0023) }, /* PCI */ + { PCI_VDEVICE(PCI_VENDOR_ID_ATHEROS, 0x0024) }, /* PCI-E */ + { PCI_VDEVICE(PCI_VENDOR_ID_ATHEROS, 0x0027) }, /* PCI */ + { PCI_VDEVICE(PCI_VENDOR_ID_ATHEROS, 0x0029) }, /* PCI */ + { PCI_VDEVICE(PCI_VENDOR_ID_ATHEROS, 0x002A) }, /* PCI-E */ + + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x002A, + PCI_VENDOR_ID_AZWAVE, + 0x1C71), + .driver_data = ATH_PCI_D3_L1_WAR }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x002A, + PCI_VENDOR_ID_FOXCONN, + 0xE01F), + .driver_data = ATH_PCI_D3_L1_WAR }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x002A, + 0x11AD, /* LITEON */ + 0x6632), + .driver_data = ATH_PCI_D3_L1_WAR }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x002A, + 0x11AD, /* LITEON */ + 0x6642), + .driver_data = ATH_PCI_D3_L1_WAR }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x002A, + PCI_VENDOR_ID_QMI, + 0x0306), + .driver_data = ATH_PCI_D3_L1_WAR }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x002A, + 0x185F, /* WNC */ + 0x309D), + .driver_data = ATH_PCI_D3_L1_WAR }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x002A, + 0x10CF, /* Fujitsu */ + 0x147C), + .driver_data = ATH_PCI_D3_L1_WAR }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x002A, + 0x10CF, /* Fujitsu */ + 0x147D), + .driver_data = ATH_PCI_D3_L1_WAR }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x002A, + 0x10CF, /* Fujitsu */ + 0x1536), + .driver_data = ATH_PCI_D3_L1_WAR }, + + /* AR9285 card for Asus */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x002B, + PCI_VENDOR_ID_AZWAVE, + 0x2C37), + .driver_data = ATH_PCI_BT_ANT_DIV }, + + { PCI_VDEVICE(PCI_VENDOR_ID_ATHEROS, 0x002B) }, /* PCI-E */ + { PCI_VDEVICE(PCI_VENDOR_ID_ATHEROS, 0x002C) }, /* PCI-E 802.11n bonded out */ + { PCI_VDEVICE(PCI_VENDOR_ID_ATHEROS, 0x002D) }, /* PCI */ + { PCI_VDEVICE(PCI_VENDOR_ID_ATHEROS, 0x002E) }, /* PCI-E */ + + /* Killer Wireless (3x3) */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0030, + 0x1A56, + 0x2000), + .driver_data = ATH_PCI_KILLER }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0030, + 0x1A56, + 0x2001), + .driver_data = ATH_PCI_KILLER }, + + { PCI_VDEVICE(PCI_VENDOR_ID_ATHEROS, 0x0030) }, /* PCI-E AR9300 */ + + /* PCI-E CUS198 */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_AZWAVE, + 0x2086), + .driver_data = ATH_PCI_CUS198 | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_AZWAVE, + 0x1237), + .driver_data = ATH_PCI_CUS198 | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_AZWAVE, + 0x2126), + .driver_data = ATH_PCI_CUS198 | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_AZWAVE, + 0x126A), + .driver_data = ATH_PCI_CUS198 | ATH_PCI_BT_ANT_DIV }, + + /* PCI-E CUS230 */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_AZWAVE, + 0x2152), + .driver_data = ATH_PCI_CUS230 | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_FOXCONN, + 0xE075), + .driver_data = ATH_PCI_CUS230 | ATH_PCI_BT_ANT_DIV }, + + /* WB225 */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_ATHEROS, + 0x3119), + .driver_data = ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_ATHEROS, + 0x3122), + .driver_data = ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + 0x185F, /* WNC */ + 0x3119), + .driver_data = ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + 0x185F, /* WNC */ + 0x3027), + .driver_data = ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_SAMSUNG, + 0x4105), + .driver_data = ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_SAMSUNG, + 0x4106), + .driver_data = ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_SAMSUNG, + 0x410D), + .driver_data = ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_SAMSUNG, + 0x410E), + .driver_data = ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_SAMSUNG, + 0x410F), + .driver_data = ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_SAMSUNG, + 0xC706), + .driver_data = ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_SAMSUNG, + 0xC680), + .driver_data = ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_SAMSUNG, + 0xC708), + .driver_data = ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_LENOVO, + 0x3218), + .driver_data = ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_LENOVO, + 0x3219), + .driver_data = ATH_PCI_BT_ANT_DIV }, + + /* AR9485 cards with PLL power-save disabled by default. */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_AZWAVE, + 0x2C97), + .driver_data = ATH_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_AZWAVE, + 0x2100), + .driver_data = ATH_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + 0x1C56, /* ASKEY */ + 0x4001), + .driver_data = ATH_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + 0x11AD, /* LITEON */ + 0x6627), + .driver_data = ATH_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + 0x11AD, /* LITEON */ + 0x6628), + .driver_data = ATH_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_FOXCONN, + 0xE04E), + .driver_data = ATH_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_FOXCONN, + 0xE04F), + .driver_data = ATH_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + 0x144F, /* ASKEY */ + 0x7197), + .driver_data = ATH_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + 0x1B9A, /* XAVI */ + 0x2000), + .driver_data = ATH_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + 0x1B9A, /* XAVI */ + 0x2001), + .driver_data = ATH_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_AZWAVE, + 0x1186), + .driver_data = ATH_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_AZWAVE, + 0x1F86), + .driver_data = ATH_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_AZWAVE, + 0x1195), + .driver_data = ATH_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_AZWAVE, + 0x1F95), + .driver_data = ATH_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + 0x1B9A, /* XAVI */ + 0x1C00), + .driver_data = ATH_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + 0x1B9A, /* XAVI */ + 0x1C01), + .driver_data = ATH_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_ASUSTEK, + 0x850D), + .driver_data = ATH_PCI_NO_PLL_PWRSAVE }, + + { PCI_VDEVICE(PCI_VENDOR_ID_ATHEROS, 0x0032) }, /* PCI-E AR9485 */ + { PCI_VDEVICE(PCI_VENDOR_ID_ATHEROS, 0x0033) }, /* PCI-E AR9580 */ + + /* PCI-E CUS217 */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + PCI_VENDOR_ID_AZWAVE, + 0x2116), + .driver_data = ATH_PCI_CUS217 }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + 0x11AD, /* LITEON */ + 0x6661), + .driver_data = ATH_PCI_CUS217 }, + + /* AR9462 with WoW support */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + PCI_VENDOR_ID_ATHEROS, + 0x3117), + .driver_data = ATH_PCI_WOW }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + PCI_VENDOR_ID_LENOVO, + 0x3214), + .driver_data = ATH_PCI_WOW }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + PCI_VENDOR_ID_ATTANSIC, + 0x0091), + .driver_data = ATH_PCI_WOW }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + PCI_VENDOR_ID_AZWAVE, + 0x2110), + .driver_data = ATH_PCI_WOW }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + PCI_VENDOR_ID_ASUSTEK, + 0x850E), + .driver_data = ATH_PCI_WOW }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + 0x11AD, /* LITEON */ + 0x6631), + .driver_data = ATH_PCI_WOW }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + 0x11AD, /* LITEON */ + 0x6641), + .driver_data = ATH_PCI_WOW }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + PCI_VENDOR_ID_HP, + 0x1864), + .driver_data = ATH_PCI_WOW }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + 0x14CD, /* USI */ + 0x0063), + .driver_data = ATH_PCI_WOW }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + 0x14CD, /* USI */ + 0x0064), + .driver_data = ATH_PCI_WOW }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + 0x10CF, /* Fujitsu */ + 0x1783), + .driver_data = ATH_PCI_WOW }, + + /* Killer Wireless (2x2) */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0030, + 0x1A56, + 0x2003), + .driver_data = ATH_PCI_KILLER }, + + { PCI_VDEVICE(PCI_VENDOR_ID_ATHEROS, 0x0034) }, /* PCI-E AR9462 */ + { PCI_VDEVICE(PCI_VENDOR_ID_ATHEROS, 0x0037) }, /* PCI-E AR1111/AR9485 */ + + /* CUS252 */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_ATHEROS, + 0x3028), + .driver_data = ATH_PCI_CUS252 | + ATH_PCI_AR9565_2ANT | + ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_AZWAVE, + 0x2176), + .driver_data = ATH_PCI_CUS252 | + ATH_PCI_AR9565_2ANT | + ATH_PCI_BT_ANT_DIV }, + + /* WB335 1-ANT */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_FOXCONN, + 0xE068), + .driver_data = ATH_PCI_AR9565_1ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x185F, /* WNC */ + 0xA119), + .driver_data = ATH_PCI_AR9565_1ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0632), + .driver_data = ATH_PCI_AR9565_1ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x06B2), + .driver_data = ATH_PCI_AR9565_1ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0842), + .driver_data = ATH_PCI_AR9565_1ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x6671), + .driver_data = ATH_PCI_AR9565_1ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x1B9A, /* XAVI */ + 0x2811), + .driver_data = ATH_PCI_AR9565_1ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x1B9A, /* XAVI */ + 0x2812), + .driver_data = ATH_PCI_AR9565_1ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x1B9A, /* XAVI */ + 0x28A1), + .driver_data = ATH_PCI_AR9565_1ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_AZWAVE, + 0x218A), + .driver_data = ATH_PCI_AR9565_1ANT }, + + /* WB335 1-ANT / Antenna Diversity */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_ATHEROS, + 0x3025), + .driver_data = ATH_PCI_AR9565_1ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_ATHEROS, + 0x3026), + .driver_data = ATH_PCI_AR9565_1ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_ATHEROS, + 0x302B), + .driver_data = ATH_PCI_AR9565_1ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_FOXCONN, + 0xE069), + .driver_data = ATH_PCI_AR9565_1ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x185F, /* WNC */ + 0x3028), + .driver_data = ATH_PCI_AR9565_1ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0622), + .driver_data = ATH_PCI_AR9565_1ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0672), + .driver_data = ATH_PCI_AR9565_1ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0662), + .driver_data = ATH_PCI_AR9565_1ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x06A2), + .driver_data = ATH_PCI_AR9565_1ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0682), + .driver_data = ATH_PCI_AR9565_1ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_AZWAVE, + 0x213A), + .driver_data = ATH_PCI_AR9565_1ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_HP, + 0x18E3), + .driver_data = ATH_PCI_AR9565_1ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_HP, + 0x217F), + .driver_data = ATH_PCI_AR9565_1ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_HP, + 0x2005), + .driver_data = ATH_PCI_AR9565_1ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_DELL, + 0x020C), + .driver_data = ATH_PCI_AR9565_1ANT | ATH_PCI_BT_ANT_DIV }, + + /* WB335 2-ANT / Antenna-Diversity */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_SAMSUNG, + 0x411A), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_SAMSUNG, + 0x411B), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_SAMSUNG, + 0x411C), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_SAMSUNG, + 0x411D), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_SAMSUNG, + 0x411E), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_ATHEROS, + 0x3027), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_ATHEROS, + 0x302C), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0642), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0652), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0612), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0832), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0692), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_AZWAVE, + 0x2130), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_AZWAVE, + 0x213B), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_AZWAVE, + 0x2182), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x144F, /* ASKEY */ + 0x7202), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x1B9A, /* XAVI */ + 0x2810), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x1B9A, /* XAVI */ + 0x28A2), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x185F, /* WNC */ + 0x3027), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x185F, /* WNC */ + 0xA120), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_FOXCONN, + 0xE07F), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_FOXCONN, + 0xE081), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_LENOVO, + 0x3026), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_LENOVO, + 0x4026), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_ASUSTEK, + 0x85F2), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_DELL, + 0x020E), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + + /* PCI-E AR9565 (WB335) */ + { PCI_VDEVICE(PCI_VENDOR_ID_ATHEROS, 0x0036), + .driver_data = ATH_PCI_BT_ANT_DIV }, + + { 0 } +}; + diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_rx.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_rx.c new file mode 100644 index 0000000000..d39d6a566c --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_rx.c @@ -0,0 +1,1477 @@ +/*- + * 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 +__FBSDID("$FreeBSD: releng/11.1/sys/dev/ath/if_ath_rx.c 298939 2016-05-02 19:56:48Z pfg $"); + +/* + * 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" +/* + * This is needed for register operations which are performed + * by the driver - eg, calls to ath_hal_gettsf32(). + * + * It's also required for any AH_DEBUG checks in here, eg the + * module dependencies. + */ +#include "opt_ah.h" +#include "opt_wlan.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for mp_ncpus */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#ifdef IEEE80211_SUPPORT_SUPERG +#include +#endif +#ifdef IEEE80211_SUPPORT_TDMA +#include +#endif + +#include + +#ifdef INET +#include +#include +#endif + +#include +#include /* XXX for softled */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef ATH_TX99_DIAG +#include +#endif + +#ifdef ATH_DEBUG_ALQ +#include +#endif + +#include + +/* + * Calculate the receive filter according to the + * operating mode and state: + * + * o always accept unicast, broadcast, and multicast traffic + * o accept PHY error frames when hardware doesn't have MIB support + * to count and we need them for ANI (sta mode only until recently) + * and we are not scanning (ANI is disabled) + * NB: older hal's add rx filter bits out of sight and we need to + * blindly preserve them + * o probe request frames are accepted only when operating in + * hostap, adhoc, mesh, or monitor modes + * o enable promiscuous mode + * - when in monitor mode + * - if interface marked PROMISC (assumes bridge setting is filtered) + * o accept beacons: + * - when operating in station mode for collecting rssi data when + * the station is otherwise quiet, or + * - when operating in adhoc mode so the 802.11 layer creates + * node table entries for peers, + * - when scanning + * - when doing s/w beacon miss (e.g. for ap+sta) + * - when operating in ap mode in 11g to detect overlapping bss that + * require protection + * - when operating in mesh mode to detect neighbors + * o accept control frames: + * - when in monitor mode + * XXX HT protection for 11n + */ +u_int32_t +ath_calcrxfilter(struct ath_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + u_int32_t rfilt; + + rfilt = HAL_RX_FILTER_UCAST | HAL_RX_FILTER_BCAST | HAL_RX_FILTER_MCAST; + if (!sc->sc_needmib && !sc->sc_scanning) + rfilt |= HAL_RX_FILTER_PHYERR; + if (ic->ic_opmode != IEEE80211_M_STA) + rfilt |= HAL_RX_FILTER_PROBEREQ; + /* XXX ic->ic_monvaps != 0? */ + if (ic->ic_opmode == IEEE80211_M_MONITOR || ic->ic_promisc > 0) + rfilt |= HAL_RX_FILTER_PROM; + + /* + * Only listen to all beacons if we're scanning. + * + * Otherwise we only really need to hear beacons from + * our own BSSID. + * + * IBSS? software beacon miss? Just receive all beacons. + * We need to hear beacons/probe requests from everyone so + * we can merge ibss. + */ + if (ic->ic_opmode == IEEE80211_M_IBSS || sc->sc_swbmiss) { + rfilt |= HAL_RX_FILTER_BEACON; + } else if (ic->ic_opmode == IEEE80211_M_STA) { + if (sc->sc_do_mybeacon && ! sc->sc_scanning) { + rfilt |= HAL_RX_FILTER_MYBEACON; + } else { /* scanning, non-mybeacon chips */ + rfilt |= HAL_RX_FILTER_BEACON; + } + } + + /* + * NB: We don't recalculate the rx filter when + * ic_protmode changes; otherwise we could do + * this only when ic_protmode != NONE. + */ + if (ic->ic_opmode == IEEE80211_M_HOSTAP && + IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) + rfilt |= HAL_RX_FILTER_BEACON; + + /* + * Enable hardware PS-POLL RX only for hostap mode; + * STA mode sends PS-POLL frames but never + * receives them. + */ + if (ath_hal_getcapability(sc->sc_ah, HAL_CAP_PSPOLL, + 0, NULL) == HAL_OK && + ic->ic_opmode == IEEE80211_M_HOSTAP) + rfilt |= HAL_RX_FILTER_PSPOLL; + + if (sc->sc_nmeshvaps) { + rfilt |= HAL_RX_FILTER_BEACON; + if (sc->sc_hasbmatch) + rfilt |= HAL_RX_FILTER_BSSID; + else + rfilt |= HAL_RX_FILTER_PROM; + } + if (ic->ic_opmode == IEEE80211_M_MONITOR) + rfilt |= HAL_RX_FILTER_CONTROL; + + /* + * Enable RX of compressed BAR frames only when doing + * 802.11n. Required for A-MPDU. + */ + if (IEEE80211_IS_CHAN_HT(ic->ic_curchan)) + rfilt |= HAL_RX_FILTER_COMPBAR; + + /* + * Enable radar PHY errors if requested by the + * DFS module. + */ + if (sc->sc_dodfs) + rfilt |= HAL_RX_FILTER_PHYRADAR; + + /* + * Enable spectral PHY errors if requested by the + * spectral module. + */ + if (sc->sc_dospectral) + rfilt |= HAL_RX_FILTER_PHYRADAR; + + DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x, %s\n", + __func__, rfilt, ieee80211_opmode_name[ic->ic_opmode]); + return rfilt; +} + +static int +ath_legacy_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf) +{ + struct ath_hal *ah = sc->sc_ah; + int error; + struct mbuf *m; + struct ath_desc *ds; + + /* XXX TODO: ATH_RX_LOCK_ASSERT(sc); */ + + m = bf->bf_m; + if (m == NULL) { + /* + * NB: by assigning a page to the rx dma buffer we + * implicitly satisfy the Atheros requirement that + * this buffer be cache-line-aligned and sized to be + * multiple of the cache line size. Not doing this + * causes weird stuff to happen (for the 5210 at least). + */ + m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + if (m == NULL) { + DPRINTF(sc, ATH_DEBUG_ANY, + "%s: no mbuf/cluster\n", __func__); + sc->sc_stats.ast_rx_nombuf++; + return ENOMEM; + } + m->m_pkthdr.len = m->m_len = m->m_ext.ext_size; + + error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, + bf->bf_dmamap, m, + bf->bf_segs, &bf->bf_nseg, + BUS_DMA_NOWAIT); + if (error != 0) { + DPRINTF(sc, ATH_DEBUG_ANY, + "%s: bus_dmamap_load_mbuf_sg failed; error %d\n", + __func__, error); + sc->sc_stats.ast_rx_busdma++; + m_freem(m); + return error; + } + KASSERT(bf->bf_nseg == 1, + ("multi-segment packet; nseg %u", bf->bf_nseg)); + bf->bf_m = m; + } + bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREREAD); + + /* + * Setup descriptors. For receive we always terminate + * the descriptor list with a self-linked entry so we'll + * not get overrun under high load (as can happen with a + * 5212 when ANI processing enables PHY error frames). + * + * To insure the last descriptor is self-linked we create + * each descriptor as self-linked and add it to the end. As + * each additional descriptor is added the previous self-linked + * entry is ``fixed'' naturally. This should be safe even + * if DMA is happening. When processing RX interrupts we + * never remove/process the last, self-linked, entry on the + * descriptor list. This insures the hardware always has + * someplace to write a new frame. + */ + /* + * 11N: we can no longer afford to self link the last descriptor. + * MAC acknowledges BA status as long as it copies frames to host + * buffer (or rx fifo). This can incorrectly acknowledge packets + * to a sender if last desc is self-linked. + */ + ds = bf->bf_desc; + if (sc->sc_rxslink) + ds->ds_link = bf->bf_daddr; /* link to self */ + else + ds->ds_link = 0; /* terminate the list */ + ds->ds_data = bf->bf_segs[0].ds_addr; + ath_hal_setuprxdesc(ah, ds + , m->m_len /* buffer size */ + , 0 + ); + + if (sc->sc_rxlink != NULL) + *sc->sc_rxlink = bf->bf_daddr; + sc->sc_rxlink = &ds->ds_link; + return 0; +} + +/* + * Intercept management frames to collect beacon rssi data + * and to do ibss merges. + */ +void +ath_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, + int subtype, const struct ieee80211_rx_stats *rxs, int rssi, int nf) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ath_softc *sc = vap->iv_ic->ic_softc; + uint64_t tsf_beacon_old, tsf_beacon; + uint64_t nexttbtt; + int64_t tsf_delta; + int32_t tsf_delta_bmiss; + int32_t tsf_remainder; + uint64_t tsf_beacon_target; + int tsf_intval; + + tsf_beacon_old = ((uint64_t) le32dec(ni->ni_tstamp.data + 4)) << 32; + tsf_beacon_old |= le32dec(ni->ni_tstamp.data); + +#define TU_TO_TSF(_tu) (((u_int64_t)(_tu)) << 10) + tsf_intval = 1; + if (ni->ni_intval > 0) { + tsf_intval = TU_TO_TSF(ni->ni_intval); + } +#undef TU_TO_TSF + + /* + * Call up first so subsequent work can use information + * potentially stored in the node (e.g. for ibss merge). + */ + ATH_VAP(vap)->av_recv_mgmt(ni, m, subtype, rxs, rssi, nf); + switch (subtype) { + case IEEE80211_FC0_SUBTYPE_BEACON: + + /* + * Only do the following processing if it's for + * the current BSS. + * + * In scan and IBSS mode we receive all beacons, + * which means we need to filter out stuff + * that isn't for us or we'll end up constantly + * trying to sync / merge to BSSes that aren't + * actually us. + */ + if (IEEE80211_ADDR_EQ(ni->ni_bssid, vap->iv_bss->ni_bssid)) { + /* update rssi statistics for use by the hal */ + /* XXX unlocked check against vap->iv_bss? */ + ATH_RSSI_LPF(sc->sc_halstats.ns_avgbrssi, rssi); + + + tsf_beacon = ((uint64_t) le32dec(ni->ni_tstamp.data + 4)) << 32; + tsf_beacon |= le32dec(ni->ni_tstamp.data); + + nexttbtt = ath_hal_getnexttbtt(sc->sc_ah); + + /* + * Let's calculate the delta and remainder, so we can see + * if the beacon timer from the AP is varying by more than + * a few TU. (Which would be a huge, huge problem.) + */ + tsf_delta = (long long) tsf_beacon - (long long) tsf_beacon_old; + + tsf_delta_bmiss = tsf_delta / tsf_intval; + + /* + * If our delta is greater than half the beacon interval, + * let's round the bmiss value up to the next beacon + * interval. Ie, we're running really, really early + * on the next beacon. + */ + if (tsf_delta % tsf_intval > (tsf_intval / 2)) + tsf_delta_bmiss ++; + + tsf_beacon_target = tsf_beacon_old + + (((unsigned long long) tsf_delta_bmiss) * (long long) tsf_intval); + + /* + * The remainder using '%' is between 0 .. intval-1. + * If we're actually running too fast, then the remainder + * will be some large number just under intval-1. + * So we need to look at whether we're running + * before or after the target beacon interval + * and if we are, modify how we do the remainder + * calculation. + */ + if (tsf_beacon < tsf_beacon_target) { + tsf_remainder = + -(tsf_intval - ((tsf_beacon - tsf_beacon_old) % tsf_intval)); + } else { + tsf_remainder = (tsf_beacon - tsf_beacon_old) % tsf_intval; + } + + DPRINTF(sc, ATH_DEBUG_BEACON, "%s: old_tsf=%llu, new_tsf=%llu, target_tsf=%llu, delta=%lld, bmiss=%d, remainder=%d\n", + __func__, + (unsigned long long) tsf_beacon_old, + (unsigned long long) tsf_beacon, + (unsigned long long) tsf_beacon_target, + (long long) tsf_delta, + tsf_delta_bmiss, + tsf_remainder); + + DPRINTF(sc, ATH_DEBUG_BEACON, "%s: tsf=%llu, nexttbtt=%llu, delta=%d\n", + __func__, + (unsigned long long) tsf_beacon, + (unsigned long long) nexttbtt, + (int32_t) tsf_beacon - (int32_t) nexttbtt + tsf_intval); + + /* We only do syncbeacon on STA VAPs; not on IBSS */ + if (vap->iv_opmode == IEEE80211_M_STA && + sc->sc_syncbeacon && + ni == vap->iv_bss && + (vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP)) { + DPRINTF(sc, ATH_DEBUG_BEACON, + "%s: syncbeacon=1; syncing\n", + __func__); + /* + * Resync beacon timers using the tsf of the beacon + * frame we just received. + */ + ath_beacon_config(sc, vap); + sc->sc_syncbeacon = 0; + } + } + + /* fall thru... */ + case IEEE80211_FC0_SUBTYPE_PROBE_RESP: + if (vap->iv_opmode == IEEE80211_M_IBSS && + vap->iv_state == IEEE80211_S_RUN && + ieee80211_ibss_merge_check(ni)) { + uint32_t rstamp = sc->sc_lastrs->rs_tstamp; + uint64_t tsf = ath_extend_tsf(sc, rstamp, + ath_hal_gettsf64(sc->sc_ah)); + /* + * Handle ibss merge as needed; check the tsf on the + * frame before attempting the merge. The 802.11 spec + * says the station should change it's bssid to match + * the oldest station with the same ssid, where oldest + * is determined by the tsf. Note that hardware + * reconfiguration happens through callback to + * ath_newstate as the state machine will go from + * RUN -> RUN when this happens. + */ + if (le64toh(ni->ni_tstamp.tsf) >= tsf) { + DPRINTF(sc, ATH_DEBUG_STATE, + "ibss merge, rstamp %u tsf %ju " + "tstamp %ju\n", rstamp, (uintmax_t)tsf, + (uintmax_t)ni->ni_tstamp.tsf); + (void) ieee80211_ibss_merge(ni); + } + } + break; + } +} + +#ifdef ATH_ENABLE_RADIOTAP_VENDOR_EXT +static void +ath_rx_tap_vendor(struct ath_softc *sc, struct mbuf *m, + const struct ath_rx_status *rs, u_int64_t tsf, int16_t nf) +{ + + /* Fill in the extension bitmap */ + sc->sc_rx_th.wr_ext_bitmap = htole32(1 << ATH_RADIOTAP_VENDOR_HEADER); + + /* Fill in the vendor header */ + sc->sc_rx_th.wr_vh.vh_oui[0] = 0x7f; + sc->sc_rx_th.wr_vh.vh_oui[1] = 0x03; + sc->sc_rx_th.wr_vh.vh_oui[2] = 0x00; + + /* XXX what should this be? */ + sc->sc_rx_th.wr_vh.vh_sub_ns = 0; + sc->sc_rx_th.wr_vh.vh_skip_len = + htole16(sizeof(struct ath_radiotap_vendor_hdr)); + + /* General version info */ + sc->sc_rx_th.wr_v.vh_version = 1; + + sc->sc_rx_th.wr_v.vh_rx_chainmask = sc->sc_rxchainmask; + + /* rssi */ + sc->sc_rx_th.wr_v.rssi_ctl[0] = rs->rs_rssi_ctl[0]; + sc->sc_rx_th.wr_v.rssi_ctl[1] = rs->rs_rssi_ctl[1]; + sc->sc_rx_th.wr_v.rssi_ctl[2] = rs->rs_rssi_ctl[2]; + sc->sc_rx_th.wr_v.rssi_ext[0] = rs->rs_rssi_ext[0]; + sc->sc_rx_th.wr_v.rssi_ext[1] = rs->rs_rssi_ext[1]; + sc->sc_rx_th.wr_v.rssi_ext[2] = rs->rs_rssi_ext[2]; + + /* evm */ + sc->sc_rx_th.wr_v.evm[0] = rs->rs_evm0; + sc->sc_rx_th.wr_v.evm[1] = rs->rs_evm1; + sc->sc_rx_th.wr_v.evm[2] = rs->rs_evm2; + /* These are only populated from the AR9300 or later */ + sc->sc_rx_th.wr_v.evm[3] = rs->rs_evm3; + sc->sc_rx_th.wr_v.evm[4] = rs->rs_evm4; + + /* direction */ + sc->sc_rx_th.wr_v.vh_flags = ATH_VENDOR_PKT_RX; + + /* RX rate */ + sc->sc_rx_th.wr_v.vh_rx_hwrate = rs->rs_rate; + + /* RX flags */ + sc->sc_rx_th.wr_v.vh_rs_flags = rs->rs_flags; + + if (rs->rs_isaggr) + sc->sc_rx_th.wr_v.vh_flags |= ATH_VENDOR_PKT_ISAGGR; + if (rs->rs_moreaggr) + sc->sc_rx_th.wr_v.vh_flags |= ATH_VENDOR_PKT_MOREAGGR; + + /* phyerr info */ + if (rs->rs_status & HAL_RXERR_PHY) { + sc->sc_rx_th.wr_v.vh_phyerr_code = rs->rs_phyerr; + sc->sc_rx_th.wr_v.vh_flags |= ATH_VENDOR_PKT_RXPHYERR; + } else { + sc->sc_rx_th.wr_v.vh_phyerr_code = 0xff; + } + sc->sc_rx_th.wr_v.vh_rs_status = rs->rs_status; + sc->sc_rx_th.wr_v.vh_rssi = rs->rs_rssi; +} +#endif /* ATH_ENABLE_RADIOTAP_VENDOR_EXT */ + +static void +ath_rx_tap(struct ath_softc *sc, struct mbuf *m, + const struct ath_rx_status *rs, u_int64_t tsf, int16_t nf) +{ +#define CHAN_HT20 htole32(IEEE80211_CHAN_HT20) +#define CHAN_HT40U htole32(IEEE80211_CHAN_HT40U) +#define CHAN_HT40D htole32(IEEE80211_CHAN_HT40D) +#define CHAN_HT (CHAN_HT20|CHAN_HT40U|CHAN_HT40D) + const HAL_RATE_TABLE *rt; + uint8_t rix; + + rt = sc->sc_currates; + KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode)); + rix = rt->rateCodeToIndex[rs->rs_rate]; + sc->sc_rx_th.wr_rate = sc->sc_hwmap[rix].ieeerate; + sc->sc_rx_th.wr_flags = sc->sc_hwmap[rix].rxflags; +#ifdef AH_SUPPORT_AR5416 + sc->sc_rx_th.wr_chan_flags &= ~CHAN_HT; + if (rs->rs_status & HAL_RXERR_PHY) { + /* + * PHY error - make sure the channel flags + * reflect the actual channel configuration, + * not the received frame. + */ + if (IEEE80211_IS_CHAN_HT40U(sc->sc_curchan)) + sc->sc_rx_th.wr_chan_flags |= CHAN_HT40U; + else if (IEEE80211_IS_CHAN_HT40D(sc->sc_curchan)) + sc->sc_rx_th.wr_chan_flags |= CHAN_HT40D; + else if (IEEE80211_IS_CHAN_HT20(sc->sc_curchan)) + sc->sc_rx_th.wr_chan_flags |= CHAN_HT20; + } else if (sc->sc_rx_th.wr_rate & IEEE80211_RATE_MCS) { /* HT rate */ + struct ieee80211com *ic = &sc->sc_ic; + + if ((rs->rs_flags & HAL_RX_2040) == 0) + sc->sc_rx_th.wr_chan_flags |= CHAN_HT20; + else if (IEEE80211_IS_CHAN_HT40U(ic->ic_curchan)) + sc->sc_rx_th.wr_chan_flags |= CHAN_HT40U; + else + sc->sc_rx_th.wr_chan_flags |= CHAN_HT40D; + if ((rs->rs_flags & HAL_RX_GI) == 0) + sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_SHORTGI; + } + +#endif + sc->sc_rx_th.wr_tsf = htole64(ath_extend_tsf(sc, rs->rs_tstamp, tsf)); + if (rs->rs_status & HAL_RXERR_CRC) + sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_BADFCS; + /* XXX propagate other error flags from descriptor */ + sc->sc_rx_th.wr_antnoise = nf; + sc->sc_rx_th.wr_antsignal = nf + rs->rs_rssi; + sc->sc_rx_th.wr_antenna = rs->rs_antenna; +#undef CHAN_HT +#undef CHAN_HT20 +#undef CHAN_HT40U +#undef CHAN_HT40D +} + +static void +ath_handle_micerror(struct ieee80211com *ic, + struct ieee80211_frame *wh, int keyix) +{ + struct ieee80211_node *ni; + + /* XXX recheck MIC to deal w/ chips that lie */ + /* XXX discard MIC errors on !data frames */ + ni = ieee80211_find_rxnode(ic, (const struct ieee80211_frame_min *) wh); + if (ni != NULL) { + ieee80211_notify_michael_failure(ni->ni_vap, wh, keyix); + ieee80211_free_node(ni); + } +} + +/* + * Process a single packet. + * + * The mbuf must already be synced, unmapped and removed from bf->bf_m + * by this stage. + * + * The mbuf must be consumed by this routine - either passed up the + * net80211 stack, put on the holding queue, or freed. + */ +int +ath_rx_pkt(struct ath_softc *sc, struct ath_rx_status *rs, HAL_STATUS status, + uint64_t tsf, int nf, HAL_RX_QUEUE qtype, struct ath_buf *bf, + struct mbuf *m) +{ + uint64_t rstamp; + int len, type; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni; + int is_good = 0; + struct ath_rx_edma *re = &sc->sc_rxedma[qtype]; + + /* + * Calculate the correct 64 bit TSF given + * the TSF64 register value and rs_tstamp. + */ + rstamp = ath_extend_tsf(sc, rs->rs_tstamp, tsf); + + /* These aren't specifically errors */ +#ifdef AH_SUPPORT_AR5416 + if (rs->rs_flags & HAL_RX_GI) + sc->sc_stats.ast_rx_halfgi++; + if (rs->rs_flags & HAL_RX_2040) + sc->sc_stats.ast_rx_2040++; + if (rs->rs_flags & HAL_RX_DELIM_CRC_PRE) + sc->sc_stats.ast_rx_pre_crc_err++; + if (rs->rs_flags & HAL_RX_DELIM_CRC_POST) + sc->sc_stats.ast_rx_post_crc_err++; + if (rs->rs_flags & HAL_RX_DECRYPT_BUSY) + sc->sc_stats.ast_rx_decrypt_busy_err++; + if (rs->rs_flags & HAL_RX_HI_RX_CHAIN) + sc->sc_stats.ast_rx_hi_rx_chain++; + if (rs->rs_flags & HAL_RX_STBC) + sc->sc_stats.ast_rx_stbc++; +#endif /* AH_SUPPORT_AR5416 */ + + if (rs->rs_status != 0) { + if (rs->rs_status & HAL_RXERR_CRC) + sc->sc_stats.ast_rx_crcerr++; + if (rs->rs_status & HAL_RXERR_FIFO) + sc->sc_stats.ast_rx_fifoerr++; + if (rs->rs_status & HAL_RXERR_PHY) { + sc->sc_stats.ast_rx_phyerr++; + /* Process DFS radar events */ + if ((rs->rs_phyerr == HAL_PHYERR_RADAR) || + (rs->rs_phyerr == HAL_PHYERR_FALSE_RADAR_EXT)) { + /* Now pass it to the radar processing code */ + ath_dfs_process_phy_err(sc, m, rstamp, rs); + } + + /* Be suitably paranoid about receiving phy errors out of the stats array bounds */ + if (rs->rs_phyerr < 64) + sc->sc_stats.ast_rx_phy[rs->rs_phyerr]++; + goto rx_error; /* NB: don't count in ierrors */ + } + if (rs->rs_status & HAL_RXERR_DECRYPT) { + /* + * Decrypt error. If the error occurred + * because there was no hardware key, then + * let the frame through so the upper layers + * can process it. This is necessary for 5210 + * parts which have no way to setup a ``clear'' + * key cache entry. + * + * XXX do key cache faulting + */ + if (rs->rs_keyix == HAL_RXKEYIX_INVALID) + goto rx_accept; + sc->sc_stats.ast_rx_badcrypt++; + } + /* + * Similar as above - if the failure was a keymiss + * just punt it up to the upper layers for now. + */ + if (rs->rs_status & HAL_RXERR_KEYMISS) { + sc->sc_stats.ast_rx_keymiss++; + goto rx_accept; + } + if (rs->rs_status & HAL_RXERR_MIC) { + sc->sc_stats.ast_rx_badmic++; + /* + * Do minimal work required to hand off + * the 802.11 header for notification. + */ + /* XXX frag's and qos frames */ + len = rs->rs_datalen; + if (len >= sizeof (struct ieee80211_frame)) { + ath_handle_micerror(ic, + mtod(m, struct ieee80211_frame *), + sc->sc_splitmic ? + rs->rs_keyix-32 : rs->rs_keyix); + } + } + counter_u64_add(ic->ic_ierrors, 1); +rx_error: + /* + * Cleanup any pending partial frame. + */ + if (re->m_rxpending != NULL) { + m_freem(re->m_rxpending); + re->m_rxpending = NULL; + } + /* + * When a tap is present pass error frames + * that have been requested. By default we + * pass decrypt+mic errors but others may be + * interesting (e.g. crc). + */ + if (ieee80211_radiotap_active(ic) && + (rs->rs_status & sc->sc_monpass)) { + /* NB: bpf needs the mbuf length setup */ + len = rs->rs_datalen; + m->m_pkthdr.len = m->m_len = len; + ath_rx_tap(sc, m, rs, rstamp, nf); +#ifdef ATH_ENABLE_RADIOTAP_VENDOR_EXT + ath_rx_tap_vendor(sc, m, rs, rstamp, nf); +#endif /* ATH_ENABLE_RADIOTAP_VENDOR_EXT */ + ieee80211_radiotap_rx_all(ic, m); + } + /* XXX pass MIC errors up for s/w reclaculation */ + m_freem(m); m = NULL; + goto rx_next; + } +rx_accept: + len = rs->rs_datalen; + m->m_len = len; + + if (rs->rs_more) { + /* + * Frame spans multiple descriptors; save + * it for the next completed descriptor, it + * will be used to construct a jumbogram. + */ + if (re->m_rxpending != NULL) { + /* NB: max frame size is currently 2 clusters */ + sc->sc_stats.ast_rx_toobig++; + m_freem(re->m_rxpending); + } + m->m_pkthdr.len = len; + re->m_rxpending = m; + m = NULL; + goto rx_next; + } else if (re->m_rxpending != NULL) { + /* + * This is the second part of a jumbogram, + * chain it to the first mbuf, adjust the + * frame length, and clear the rxpending state. + */ + re->m_rxpending->m_next = m; + re->m_rxpending->m_pkthdr.len += len; + m = re->m_rxpending; + re->m_rxpending = NULL; + } else { + /* + * Normal single-descriptor receive; setup packet length. + */ + m->m_pkthdr.len = len; + } + + /* + * Validate rs->rs_antenna. + * + * Some users w/ AR9285 NICs have reported crashes + * here because rs_antenna field is bogusly large. + * Let's enforce the maximum antenna limit of 8 + * (and it shouldn't be hard coded, but that's a + * separate problem) and if there's an issue, print + * out an error and adjust rs_antenna to something + * sensible. + * + * This code should be removed once the actual + * root cause of the issue has been identified. + * For example, it may be that the rs_antenna + * field is only valid for the last frame of + * an aggregate and it just happens that it is + * "mostly" right. (This is a general statement - + * the majority of the statistics are only valid + * for the last frame in an aggregate. + */ + if (rs->rs_antenna > 7) { + device_printf(sc->sc_dev, "%s: rs_antenna > 7 (%d)\n", + __func__, rs->rs_antenna); +#ifdef ATH_DEBUG + ath_printrxbuf(sc, bf, 0, status == HAL_OK); +#endif /* ATH_DEBUG */ + rs->rs_antenna = 0; /* XXX better than nothing */ + } + + /* + * If this is an AR9285/AR9485, then the receive and LNA + * configuration is stored in RSSI[2] / EXTRSSI[2]. + * We can extract this out to build a much better + * receive antenna profile. + * + * Yes, this just blurts over the above RX antenna field + * for now. It's fine, the AR9285 doesn't really use + * that. + * + * Later on we should store away the fine grained LNA + * information and keep separate counters just for + * that. It'll help when debugging the AR9285/AR9485 + * combined diversity code. + */ + if (sc->sc_rx_lnamixer) { + rs->rs_antenna = 0; + + /* Bits 0:1 - the LNA configuration used */ + rs->rs_antenna |= + ((rs->rs_rssi_ctl[2] & HAL_RX_LNA_CFG_USED) + >> HAL_RX_LNA_CFG_USED_S); + + /* Bit 2 - the external RX antenna switch */ + if (rs->rs_rssi_ctl[2] & HAL_RX_LNA_EXTCFG) + rs->rs_antenna |= 0x4; + } + + sc->sc_stats.ast_ant_rx[rs->rs_antenna]++; + + /* + * Populate the rx status block. When there are bpf + * listeners we do the additional work to provide + * complete status. Otherwise we fill in only the + * material required by ieee80211_input. Note that + * noise setting is filled in above. + */ + if (ieee80211_radiotap_active(ic)) { + ath_rx_tap(sc, m, rs, rstamp, nf); +#ifdef ATH_ENABLE_RADIOTAP_VENDOR_EXT + ath_rx_tap_vendor(sc, m, rs, rstamp, nf); +#endif /* ATH_ENABLE_RADIOTAP_VENDOR_EXT */ + } + + /* + * From this point on we assume the frame is at least + * as large as ieee80211_frame_min; verify that. + */ + if (len < IEEE80211_MIN_LEN) { + if (!ieee80211_radiotap_active(ic)) { + DPRINTF(sc, ATH_DEBUG_RECV, + "%s: short packet %d\n", __func__, len); + sc->sc_stats.ast_rx_tooshort++; + } else { + /* NB: in particular this captures ack's */ + ieee80211_radiotap_rx_all(ic, m); + } + m_freem(m); m = NULL; + goto rx_next; + } + + if (IFF_DUMPPKTS(sc, ATH_DEBUG_RECV)) { + const HAL_RATE_TABLE *rt = sc->sc_currates; + uint8_t rix = rt->rateCodeToIndex[rs->rs_rate]; + + ieee80211_dump_pkt(ic, mtod(m, caddr_t), len, + sc->sc_hwmap[rix].ieeerate, rs->rs_rssi); + } + + m_adj(m, -IEEE80211_CRC_LEN); + + /* + * Locate the node for sender, track state, and then + * pass the (referenced) node up to the 802.11 layer + * for its use. + */ + ni = ieee80211_find_rxnode_withkey(ic, + mtod(m, const struct ieee80211_frame_min *), + rs->rs_keyix == HAL_RXKEYIX_INVALID ? + IEEE80211_KEYIX_NONE : rs->rs_keyix); + sc->sc_lastrs = rs; + +#ifdef AH_SUPPORT_AR5416 + if (rs->rs_isaggr) + sc->sc_stats.ast_rx_agg++; +#endif /* AH_SUPPORT_AR5416 */ + + if (ni != NULL) { + /* + * Only punt packets for ampdu reorder processing for + * 11n nodes; net80211 enforces that M_AMPDU is only + * set for 11n nodes. + */ + if (ni->ni_flags & IEEE80211_NODE_HT) + m->m_flags |= M_AMPDU; + + /* + * Sending station is known, dispatch directly. + */ + type = ieee80211_input(ni, m, rs->rs_rssi, nf); + ieee80211_free_node(ni); + m = NULL; + /* + * Arrange to update the last rx timestamp only for + * frames from our ap when operating in station mode. + * This assumes the rx key is always setup when + * associated. + */ + if (ic->ic_opmode == IEEE80211_M_STA && + rs->rs_keyix != HAL_RXKEYIX_INVALID) + is_good = 1; + } else { + type = ieee80211_input_all(ic, m, rs->rs_rssi, nf); + m = NULL; + } + + /* + * At this point we have passed the frame up the stack; thus + * the mbuf is no longer ours. + */ + + /* + * Track rx rssi and do any rx antenna management. + */ + ATH_RSSI_LPF(sc->sc_halstats.ns_avgrssi, rs->rs_rssi); + if (sc->sc_diversity) { + /* + * When using fast diversity, change the default rx + * antenna if diversity chooses the other antenna 3 + * times in a row. + */ + if (sc->sc_defant != rs->rs_antenna) { + if (++sc->sc_rxotherant >= 3) + ath_setdefantenna(sc, rs->rs_antenna); + } else + sc->sc_rxotherant = 0; + } + + /* Handle slow diversity if enabled */ + if (sc->sc_dolnadiv) { + ath_lna_rx_comb_scan(sc, rs, ticks, hz); + } + + if (sc->sc_softled) { + /* + * Blink for any data frame. Otherwise do a + * heartbeat-style blink when idle. The latter + * is mainly for station mode where we depend on + * periodic beacon frames to trigger the poll event. + */ + if (type == IEEE80211_FC0_TYPE_DATA) { + const HAL_RATE_TABLE *rt = sc->sc_currates; + ath_led_event(sc, + rt->rateCodeToIndex[rs->rs_rate]); + } else if (ticks - sc->sc_ledevent >= sc->sc_ledidle) + ath_led_event(sc, 0); + } +rx_next: + /* + * Debugging - complain if we didn't NULL the mbuf pointer + * here. + */ + if (m != NULL) { + device_printf(sc->sc_dev, + "%s: mbuf %p should've been freed!\n", + __func__, + m); + } + return (is_good); +} + +#define ATH_RX_MAX 128 + +/* + * XXX TODO: break out the "get buffers" from "call ath_rx_pkt()" like + * the EDMA code does. + * + * XXX TODO: then, do all of the RX list management stuff inside + * ATH_RX_LOCK() so we don't end up potentially racing. The EDMA + * code is doing it right. + */ +static void +ath_rx_proc(struct ath_softc *sc, int resched) +{ +#define PA2DESC(_sc, _pa) \ + ((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \ + ((_pa) - (_sc)->sc_rxdma.dd_desc_paddr))) + struct ath_buf *bf; + struct ath_hal *ah = sc->sc_ah; +#ifdef IEEE80211_SUPPORT_SUPERG + struct ieee80211com *ic = &sc->sc_ic; +#endif + struct ath_desc *ds; + struct ath_rx_status *rs; + struct mbuf *m; + int ngood; + HAL_STATUS status; + int16_t nf; + u_int64_t tsf; + int npkts = 0; + int kickpcu = 0; + int ret; + + /* XXX we must not hold the ATH_LOCK here */ + ATH_UNLOCK_ASSERT(sc); + ATH_PCU_UNLOCK_ASSERT(sc); + + ATH_PCU_LOCK(sc); + sc->sc_rxproc_cnt++; + kickpcu = sc->sc_kickpcu; + ATH_PCU_UNLOCK(sc); + + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + + DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s: called\n", __func__); + ngood = 0; + nf = ath_hal_getchannoise(ah, sc->sc_curchan); + sc->sc_stats.ast_rx_noise = nf; + tsf = ath_hal_gettsf64(ah); + do { + /* + * Don't process too many packets at a time; give the + * TX thread time to also run - otherwise the TX + * latency can jump by quite a bit, causing throughput + * degredation. + */ + if (!kickpcu && npkts >= ATH_RX_MAX) + break; + + bf = TAILQ_FIRST(&sc->sc_rxbuf); + if (sc->sc_rxslink && bf == NULL) { /* NB: shouldn't happen */ + device_printf(sc->sc_dev, "%s: no buffer!\n", __func__); + break; + } else if (bf == NULL) { + /* + * End of List: + * this can happen for non-self-linked RX chains + */ + sc->sc_stats.ast_rx_hitqueueend++; + break; + } + m = bf->bf_m; + if (m == NULL) { /* NB: shouldn't happen */ + /* + * If mbuf allocation failed previously there + * will be no mbuf; try again to re-populate it. + */ + /* XXX make debug msg */ + device_printf(sc->sc_dev, "%s: no mbuf!\n", __func__); + TAILQ_REMOVE(&sc->sc_rxbuf, bf, bf_list); + goto rx_proc_next; + } + ds = bf->bf_desc; + if (ds->ds_link == bf->bf_daddr) { + /* NB: never process the self-linked entry at the end */ + sc->sc_stats.ast_rx_hitqueueend++; + break; + } + /* XXX sync descriptor memory */ + /* + * Must provide the virtual address of the current + * descriptor, the physical address, and the virtual + * address of the next descriptor in the h/w chain. + * This allows the HAL to look ahead to see if the + * hardware is done with a descriptor by checking the + * done bit in the following descriptor and the address + * of the current descriptor the DMA engine is working + * on. All this is necessary because of our use of + * a self-linked list to avoid rx overruns. + */ + rs = &bf->bf_status.ds_rxstat; + status = ath_hal_rxprocdesc(ah, ds, + bf->bf_daddr, PA2DESC(sc, ds->ds_link), rs); +#ifdef ATH_DEBUG + if (sc->sc_debug & ATH_DEBUG_RECV_DESC) + ath_printrxbuf(sc, bf, 0, status == HAL_OK); +#endif + +#ifdef ATH_DEBUG_ALQ + if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_EDMA_RXSTATUS)) + if_ath_alq_post(&sc->sc_alq, ATH_ALQ_EDMA_RXSTATUS, + sc->sc_rx_statuslen, (char *) ds); +#endif /* ATH_DEBUG_ALQ */ + + if (status == HAL_EINPROGRESS) + break; + + TAILQ_REMOVE(&sc->sc_rxbuf, bf, bf_list); + npkts++; + + /* + * Process a single frame. + */ + bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); + bf->bf_m = NULL; + if (ath_rx_pkt(sc, rs, status, tsf, nf, HAL_RX_QUEUE_HP, bf, m)) + ngood++; +rx_proc_next: + /* + * If there's a holding buffer, insert that onto + * the RX list; the hardware is now definitely not pointing + * to it now. + */ + ret = 0; + if (sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf != NULL) { + TAILQ_INSERT_TAIL(&sc->sc_rxbuf, + sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf, + bf_list); + ret = ath_rxbuf_init(sc, + sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf); + } + /* + * Next, throw our buffer into the holding entry. The hardware + * may use the descriptor to read the link pointer before + * DMAing the next descriptor in to write out a packet. + */ + sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf = bf; + } while (ret == 0); + + /* rx signal state monitoring */ + ath_hal_rxmonitor(ah, &sc->sc_halstats, sc->sc_curchan); + if (ngood) + sc->sc_lastrx = tsf; + + ATH_KTR(sc, ATH_KTR_RXPROC, 2, "ath_rx_proc: npkts=%d, ngood=%d", npkts, ngood); + /* Queue DFS tasklet if needed */ + if (resched && ath_dfs_tasklet_needed(sc, sc->sc_curchan)) + taskqueue_enqueue(sc->sc_tq, &sc->sc_dfstask); + + /* + * Now that all the RX frames were handled that + * need to be handled, kick the PCU if there's + * been an RXEOL condition. + */ + if (resched && kickpcu) { + ATH_PCU_LOCK(sc); + ATH_KTR(sc, ATH_KTR_ERROR, 0, "ath_rx_proc: kickpcu"); + device_printf(sc->sc_dev, "%s: kickpcu; handled %d packets\n", + __func__, npkts); + + /* + * Go through the process of fully tearing down + * the RX buffers and reinitialising them. + * + * There's a hardware bug that causes the RX FIFO + * to get confused under certain conditions and + * constantly write over the same frame, leading + * the RX driver code here to get heavily confused. + */ + /* + * XXX Has RX DMA stopped enough here to just call + * ath_startrecv()? + * XXX Do we need to use the holding buffer to restart + * RX DMA by appending entries to the final + * descriptor? Quite likely. + */ +#if 1 + ath_startrecv(sc); +#else + /* + * Disabled for now - it'd be nice to be able to do + * this in order to limit the amount of CPU time spent + * reinitialising the RX side (and thus minimise RX + * drops) however there's a hardware issue that + * causes things to get too far out of whack. + */ + /* + * XXX can we hold the PCU lock here? + * Are there any net80211 buffer calls involved? + */ + bf = TAILQ_FIRST(&sc->sc_rxbuf); + ath_hal_putrxbuf(ah, bf->bf_daddr, HAL_RX_QUEUE_HP); + ath_hal_rxena(ah); /* enable recv descriptors */ + ath_mode_init(sc); /* set filters, etc. */ + ath_hal_startpcurecv(ah); /* re-enable PCU/DMA engine */ +#endif + + ath_hal_intrset(ah, sc->sc_imask); + sc->sc_kickpcu = 0; + ATH_PCU_UNLOCK(sc); + } + +#ifdef IEEE80211_SUPPORT_SUPERG + if (resched) + ieee80211_ff_age_all(ic, 100); +#endif + + /* + * Put the hardware to sleep again if we're done with it. + */ + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + /* + * If we hit the maximum number of frames in this round, + * reschedule for another immediate pass. This gives + * the TX and TX completion routines time to run, which + * will reduce latency. + */ + if (npkts >= ATH_RX_MAX) + sc->sc_rx.recv_sched(sc, resched); + + ATH_PCU_LOCK(sc); + sc->sc_rxproc_cnt--; + ATH_PCU_UNLOCK(sc); +} +#undef PA2DESC +#undef ATH_RX_MAX + +/* + * Only run the RX proc if it's not already running. + * Since this may get run as part of the reset/flush path, + * the task can't clash with an existing, running tasklet. + */ +static void +ath_legacy_rx_tasklet(void *arg, int npending) +{ + struct ath_softc *sc = arg; + + ATH_KTR(sc, ATH_KTR_RXPROC, 1, "ath_rx_proc: pending=%d", npending); + DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s: pending %u\n", __func__, npending); + ATH_PCU_LOCK(sc); + if (sc->sc_inreset_cnt > 0) { + device_printf(sc->sc_dev, + "%s: sc_inreset_cnt > 0; skipping\n", __func__); + ATH_PCU_UNLOCK(sc); + return; + } + ATH_PCU_UNLOCK(sc); + + ath_rx_proc(sc, 1); +} + +static void +ath_legacy_flushrecv(struct ath_softc *sc) +{ + + ath_rx_proc(sc, 0); +} + +static void +ath_legacy_flush_rxpending(struct ath_softc *sc) +{ + + /* XXX ATH_RX_LOCK_ASSERT(sc); */ + + if (sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending != NULL) { + m_freem(sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending); + sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending = NULL; + } + if (sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending != NULL) { + m_freem(sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending); + sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending = NULL; + } +} + +static int +ath_legacy_flush_rxholdbf(struct ath_softc *sc) +{ + struct ath_buf *bf; + + /* XXX ATH_RX_LOCK_ASSERT(sc); */ + /* + * If there are RX holding buffers, free them here and return + * them to the list. + * + * XXX should just verify that bf->bf_m is NULL, as it must + * be at this point! + */ + bf = sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf; + if (bf != NULL) { + if (bf->bf_m != NULL) + m_freem(bf->bf_m); + bf->bf_m = NULL; + TAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list); + (void) ath_rxbuf_init(sc, bf); + } + sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf = NULL; + + bf = sc->sc_rxedma[HAL_RX_QUEUE_LP].m_holdbf; + if (bf != NULL) { + if (bf->bf_m != NULL) + m_freem(bf->bf_m); + bf->bf_m = NULL; + TAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list); + (void) ath_rxbuf_init(sc, bf); + } + sc->sc_rxedma[HAL_RX_QUEUE_LP].m_holdbf = NULL; + + return (0); +} + +/* + * Disable the receive h/w in preparation for a reset. + */ +static void +ath_legacy_stoprecv(struct ath_softc *sc, int dodelay) +{ +#define PA2DESC(_sc, _pa) \ + ((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \ + ((_pa) - (_sc)->sc_rxdma.dd_desc_paddr))) + struct ath_hal *ah = sc->sc_ah; + + ATH_RX_LOCK(sc); + + ath_hal_stoppcurecv(ah); /* disable PCU */ + ath_hal_setrxfilter(ah, 0); /* clear recv filter */ + ath_hal_stopdmarecv(ah); /* disable DMA engine */ + /* + * TODO: see if this particular DELAY() is required; it may be + * masking some missing FIFO flush or DMA sync. + */ +#if 0 + if (dodelay) +#endif + DELAY(3000); /* 3ms is long enough for 1 frame */ +#ifdef ATH_DEBUG + if (sc->sc_debug & (ATH_DEBUG_RESET | ATH_DEBUG_FATAL)) { + struct ath_buf *bf; + u_int ix; + + device_printf(sc->sc_dev, + "%s: rx queue %p, link %p\n", + __func__, + (caddr_t)(uintptr_t) ath_hal_getrxbuf(ah, HAL_RX_QUEUE_HP), + sc->sc_rxlink); + ix = 0; + TAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) { + struct ath_desc *ds = bf->bf_desc; + struct ath_rx_status *rs = &bf->bf_status.ds_rxstat; + HAL_STATUS status = ath_hal_rxprocdesc(ah, ds, + bf->bf_daddr, PA2DESC(sc, ds->ds_link), rs); + if (status == HAL_OK || (sc->sc_debug & ATH_DEBUG_FATAL)) + ath_printrxbuf(sc, bf, ix, status == HAL_OK); + ix++; + } + } +#endif + + (void) ath_legacy_flush_rxpending(sc); + (void) ath_legacy_flush_rxholdbf(sc); + + sc->sc_rxlink = NULL; /* just in case */ + + ATH_RX_UNLOCK(sc); +#undef PA2DESC +} + +/* + * XXX TODO: something was calling startrecv without calling + * stoprecv. Let's figure out what/why. It was showing up + * as a mbuf leak (rxpending) and ath_buf leak (holdbf.) + */ + +/* + * Enable the receive h/w following a reset. + */ +static int +ath_legacy_startrecv(struct ath_softc *sc) +{ + struct ath_hal *ah = sc->sc_ah; + struct ath_buf *bf; + + ATH_RX_LOCK(sc); + + /* + * XXX should verify these are already all NULL! + */ + sc->sc_rxlink = NULL; + (void) ath_legacy_flush_rxpending(sc); + (void) ath_legacy_flush_rxholdbf(sc); + + /* + * Re-chain all of the buffers in the RX buffer list. + */ + TAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) { + int error = ath_rxbuf_init(sc, bf); + if (error != 0) { + DPRINTF(sc, ATH_DEBUG_RECV, + "%s: ath_rxbuf_init failed %d\n", + __func__, error); + return error; + } + } + + bf = TAILQ_FIRST(&sc->sc_rxbuf); + ath_hal_putrxbuf(ah, bf->bf_daddr, HAL_RX_QUEUE_HP); + ath_hal_rxena(ah); /* enable recv descriptors */ + ath_mode_init(sc); /* set filters, etc. */ + ath_hal_startpcurecv(ah); /* re-enable PCU/DMA engine */ + + ATH_RX_UNLOCK(sc); + return 0; +} + +static int +ath_legacy_dma_rxsetup(struct ath_softc *sc) +{ + int error; + + error = ath_descdma_setup(sc, &sc->sc_rxdma, &sc->sc_rxbuf, + "rx", sizeof(struct ath_desc), ath_rxbuf, 1); + if (error != 0) + return (error); + + return (0); +} + +static int +ath_legacy_dma_rxteardown(struct ath_softc *sc) +{ + + if (sc->sc_rxdma.dd_desc_len != 0) + ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf); + return (0); +} + +static void +ath_legacy_recv_sched(struct ath_softc *sc, int dosched) +{ + + taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask); +} + +static void +ath_legacy_recv_sched_queue(struct ath_softc *sc, HAL_RX_QUEUE q, + int dosched) +{ + + taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask); +} + +void +ath_recv_setup_legacy(struct ath_softc *sc) +{ + + /* Sensible legacy defaults */ + /* + * XXX this should be changed to properly support the + * exact RX descriptor size for each HAL. + */ + sc->sc_rx_statuslen = sizeof(struct ath_desc); + + sc->sc_rx.recv_start = ath_legacy_startrecv; + sc->sc_rx.recv_stop = ath_legacy_stoprecv; + sc->sc_rx.recv_flush = ath_legacy_flushrecv; + sc->sc_rx.recv_tasklet = ath_legacy_rx_tasklet; + sc->sc_rx.recv_rxbuf_init = ath_legacy_rxbuf_init; + + sc->sc_rx.recv_setup = ath_legacy_dma_rxsetup; + sc->sc_rx.recv_teardown = ath_legacy_dma_rxteardown; + sc->sc_rx.recv_sched = ath_legacy_recv_sched; + sc->sc_rx.recv_sched_queue = ath_legacy_recv_sched_queue; +} diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_rx.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_rx.h new file mode 100644 index 0000000000..87bafb9431 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_rx.h @@ -0,0 +1,66 @@ +/*- + * 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. + * + * $FreeBSD: releng/11.1/sys/dev/ath/if_ath_rx.h 283535 2015-05-25 16:37:41Z adrian $ + */ +#ifndef __IF_ATH_RX_H__ +#define __IF_ATH_RX_H__ + +extern u_int32_t ath_calcrxfilter(struct ath_softc *sc); +extern void ath_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, + int subtype, const struct ieee80211_rx_stats *rxs, + int rssi, int nf); + +#define ath_stoprecv(_sc, _dodelay) \ + (_sc)->sc_rx.recv_stop((_sc), (_dodelay)) +#define ath_startrecv(_sc) \ + (_sc)->sc_rx.recv_start((_sc)) +#define ath_rx_flush(_sc) \ + (_sc)->sc_rx.recv_flush((_sc)) +#define ath_rxbuf_init(_sc, _bf) \ + (_sc)->sc_rx.recv_rxbuf_init((_sc), (_bf)) +#define ath_rxdma_setup(_sc) \ + (_sc)->sc_rx.recv_setup(_sc) +#define ath_rxdma_teardown(_sc) \ + (_sc)->sc_rx.recv_teardown(_sc) + +#if 0 +extern int ath_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf); +extern void ath_rx_tasklet(void *arg, int npending); +extern void ath_rx_proc(struct ath_softc *sc, int resched); +extern void ath_stoprecv(struct ath_softc *sc, int dodelay); +extern int ath_startrecv(struct ath_softc *sc); +#endif + +extern int ath_rx_pkt(struct ath_softc *sc, struct ath_rx_status *rs, + HAL_STATUS status, uint64_t tsf, int nf, HAL_RX_QUEUE qtype, + struct ath_buf *bf, struct mbuf *m); + +extern void ath_recv_setup_legacy(struct ath_softc *sc); + +#endif diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_rx_edma.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_rx_edma.c new file mode 100644 index 0000000000..e856a9f7d7 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_rx_edma.c @@ -0,0 +1,1008 @@ +/*- + * Copyright (c) 2012 Adrian Chadd + * 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 +__FBSDID("$FreeBSD: releng/11.1/sys/dev/ath/if_ath_rx_edma.c 301994 2016-06-17 17:01:32Z adrian $"); + +/* + * 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" +/* + * This is needed for register operations which are performed + * by the driver - eg, calls to ath_hal_gettsf32(). + * + * It's also required for any AH_DEBUG checks in here, eg the + * module dependencies. + */ +#include "opt_ah.h" +#include "opt_wlan.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for mp_ncpus */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#ifdef IEEE80211_SUPPORT_SUPERG +#include +#endif +#ifdef IEEE80211_SUPPORT_TDMA +#include +#endif + +#include + +#ifdef INET +#include +#include +#endif + +#include +#include /* XXX for softled */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef ATH_TX99_DIAG +#include +#endif + +#include + +#ifdef ATH_DEBUG_ALQ +#include +#endif + +/* + * some general macros + */ +#define INCR(_l, _sz) (_l) ++; (_l) &= ((_sz) - 1) +#define DECR(_l, _sz) (_l) --; (_l) &= ((_sz) - 1) + +MALLOC_DECLARE(M_ATHDEV); + +/* + * XXX TODO: + * + * + Make sure the FIFO is correctly flushed and reinitialised + * through a reset; + * + Verify multi-descriptor frames work! + * + There's a "memory use after free" which needs to be tracked down + * and fixed ASAP. I've seen this in the legacy path too, so it + * may be a generic RX path issue. + */ + +/* + * XXX shuffle the function orders so these pre-declarations aren't + * required! + */ +static int ath_edma_rxfifo_alloc(struct ath_softc *sc, HAL_RX_QUEUE qtype, + int nbufs); +static int ath_edma_rxfifo_flush(struct ath_softc *sc, HAL_RX_QUEUE qtype); +static void ath_edma_rxbuf_free(struct ath_softc *sc, struct ath_buf *bf); +static void ath_edma_recv_proc_queue(struct ath_softc *sc, + HAL_RX_QUEUE qtype, int dosched); +static int ath_edma_recv_proc_deferred_queue(struct ath_softc *sc, + HAL_RX_QUEUE qtype, int dosched); + +static void +ath_edma_stoprecv(struct ath_softc *sc, int dodelay) +{ + struct ath_hal *ah = sc->sc_ah; + + ATH_RX_LOCK(sc); + + ath_hal_stoppcurecv(ah); + ath_hal_setrxfilter(ah, 0); + + /* + * + */ + if (ath_hal_stopdmarecv(ah) == AH_TRUE) + sc->sc_rx_stopped = 1; + + /* + * Give the various bus FIFOs (not EDMA descriptor FIFO) + * time to finish flushing out data. + */ + DELAY(3000); + + /* Flush RX pending for each queue */ + /* XXX should generic-ify this */ + if (sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending) { + m_freem(sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending); + sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending = NULL; + } + + if (sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending) { + m_freem(sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending); + sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending = NULL; + } + ATH_RX_UNLOCK(sc); +} + +/* + * Re-initialise the FIFO given the current buffer contents. + * Specifically, walk from head -> tail, pushing the FIFO contents + * back into the FIFO. + */ +static void +ath_edma_reinit_fifo(struct ath_softc *sc, HAL_RX_QUEUE qtype) +{ + struct ath_rx_edma *re = &sc->sc_rxedma[qtype]; + struct ath_buf *bf; + int i, j; + + ATH_RX_LOCK_ASSERT(sc); + + i = re->m_fifo_head; + for (j = 0; j < re->m_fifo_depth; j++) { + bf = re->m_fifo[i]; + DPRINTF(sc, ATH_DEBUG_EDMA_RX, + "%s: Q%d: pos=%i, addr=0x%jx\n", + __func__, + qtype, + i, + (uintmax_t)bf->bf_daddr); + ath_hal_putrxbuf(sc->sc_ah, bf->bf_daddr, qtype); + INCR(i, re->m_fifolen); + } + + /* Ensure this worked out right */ + if (i != re->m_fifo_tail) { + device_printf(sc->sc_dev, "%s: i (%d) != tail! (%d)\n", + __func__, + i, + re->m_fifo_tail); + } +} + +/* + * Start receive. + */ +static int +ath_edma_startrecv(struct ath_softc *sc) +{ + struct ath_hal *ah = sc->sc_ah; + + ATH_RX_LOCK(sc); + + /* + * Sanity check - are we being called whilst RX + * isn't stopped? If so, we may end up pushing + * too many entries into the RX FIFO and + * badness occurs. + */ + + /* Enable RX FIFO */ + ath_hal_rxena(ah); + + /* + * In theory the hardware has been initialised, right? + */ + if (sc->sc_rx_resetted == 1) { + DPRINTF(sc, ATH_DEBUG_EDMA_RX, + "%s: Re-initing HP FIFO\n", __func__); + ath_edma_reinit_fifo(sc, HAL_RX_QUEUE_HP); + DPRINTF(sc, ATH_DEBUG_EDMA_RX, + "%s: Re-initing LP FIFO\n", __func__); + ath_edma_reinit_fifo(sc, HAL_RX_QUEUE_LP); + sc->sc_rx_resetted = 0; + } else { + device_printf(sc->sc_dev, + "%s: called without resetting chip?\n", + __func__); + } + + /* Add up to m_fifolen entries in each queue */ + /* + * These must occur after the above write so the FIFO buffers + * are pushed/tracked in the same order as the hardware will + * process them. + * + * XXX TODO: is this really necessary? We should've stopped + * the hardware already and reinitialised it, so it's a no-op. + */ + ath_edma_rxfifo_alloc(sc, HAL_RX_QUEUE_HP, + sc->sc_rxedma[HAL_RX_QUEUE_HP].m_fifolen); + + ath_edma_rxfifo_alloc(sc, HAL_RX_QUEUE_LP, + sc->sc_rxedma[HAL_RX_QUEUE_LP].m_fifolen); + + ath_mode_init(sc); + ath_hal_startpcurecv(ah); + + /* + * We're now doing RX DMA! + */ + sc->sc_rx_stopped = 0; + + ATH_RX_UNLOCK(sc); + + return (0); +} + +static void +ath_edma_recv_sched_queue(struct ath_softc *sc, HAL_RX_QUEUE qtype, + int dosched) +{ + + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + + ath_edma_recv_proc_queue(sc, qtype, dosched); + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask); +} + +static void +ath_edma_recv_sched(struct ath_softc *sc, int dosched) +{ + + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + + ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_HP, dosched); + ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_LP, dosched); + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask); +} + +static void +ath_edma_recv_flush(struct ath_softc *sc) +{ + + DPRINTF(sc, ATH_DEBUG_RECV, "%s: called\n", __func__); + + ATH_PCU_LOCK(sc); + sc->sc_rxproc_cnt++; + ATH_PCU_UNLOCK(sc); + + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + + /* + * Flush any active frames from FIFO -> deferred list + */ + ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_HP, 0); + ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_LP, 0); + + /* + * Process what's in the deferred queue + */ + /* + * XXX: If we read the tsf/channoise here and then pass it in, + * we could restore the power state before processing + * the deferred queue. + */ + ath_edma_recv_proc_deferred_queue(sc, HAL_RX_QUEUE_HP, 0); + ath_edma_recv_proc_deferred_queue(sc, HAL_RX_QUEUE_LP, 0); + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + ATH_PCU_LOCK(sc); + sc->sc_rxproc_cnt--; + ATH_PCU_UNLOCK(sc); +} + +/* + * Process frames from the current queue into the deferred queue. + */ +static void +ath_edma_recv_proc_queue(struct ath_softc *sc, HAL_RX_QUEUE qtype, + int dosched) +{ + struct ath_rx_edma *re = &sc->sc_rxedma[qtype]; + struct ath_rx_status *rs; + struct ath_desc *ds; + struct ath_buf *bf; + struct mbuf *m; + struct ath_hal *ah = sc->sc_ah; + uint64_t tsf; + uint16_t nf; + int npkts = 0; + + tsf = ath_hal_gettsf64(ah); + nf = ath_hal_getchannoise(ah, sc->sc_curchan); + sc->sc_stats.ast_rx_noise = nf; + + ATH_RX_LOCK(sc); + +#if 1 + if (sc->sc_rx_resetted == 1) { + /* + * XXX We shouldn't ever be scheduled if + * receive has been stopped - so complain + * loudly! + */ + device_printf(sc->sc_dev, + "%s: sc_rx_resetted=1! Bad!\n", + __func__); + ATH_RX_UNLOCK(sc); + return; + } +#endif + + do { + bf = re->m_fifo[re->m_fifo_head]; + /* This shouldn't occur! */ + if (bf == NULL) { + device_printf(sc->sc_dev, "%s: Q%d: NULL bf?\n", + __func__, + qtype); + break; + } + m = bf->bf_m; + ds = bf->bf_desc; + + /* + * Sync descriptor memory - this also syncs the buffer for us. + * EDMA descriptors are in cached memory. + */ + bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + rs = &bf->bf_status.ds_rxstat; + bf->bf_rxstatus = ath_hal_rxprocdesc(ah, ds, bf->bf_daddr, + NULL, rs); + if (bf->bf_rxstatus == HAL_EINPROGRESS) + break; +#ifdef ATH_DEBUG + if (sc->sc_debug & ATH_DEBUG_RECV_DESC) + ath_printrxbuf(sc, bf, 0, bf->bf_rxstatus == HAL_OK); +#endif /* ATH_DEBUG */ +#ifdef ATH_DEBUG_ALQ + if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_EDMA_RXSTATUS)) + if_ath_alq_post(&sc->sc_alq, ATH_ALQ_EDMA_RXSTATUS, + sc->sc_rx_statuslen, (char *) ds); +#endif /* ATH_DEBUG */ + + /* + * Completed descriptor. + */ + DPRINTF(sc, ATH_DEBUG_EDMA_RX, + "%s: Q%d: completed!\n", __func__, qtype); + npkts++; + + /* + * We've been synced already, so unmap. + */ + bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); + + /* + * Remove the FIFO entry and place it on the completion + * queue. + */ + re->m_fifo[re->m_fifo_head] = NULL; + TAILQ_INSERT_TAIL(&sc->sc_rx_rxlist[qtype], bf, bf_list); + + /* Bump the descriptor FIFO stats */ + INCR(re->m_fifo_head, re->m_fifolen); + re->m_fifo_depth--; + /* XXX check it doesn't fall below 0 */ + } while (re->m_fifo_depth > 0); + + /* Append some more fresh frames to the FIFO */ + if (dosched) + ath_edma_rxfifo_alloc(sc, qtype, re->m_fifolen); + + ATH_RX_UNLOCK(sc); + + /* rx signal state monitoring */ + ath_hal_rxmonitor(ah, &sc->sc_halstats, sc->sc_curchan); + + ATH_KTR(sc, ATH_KTR_INTERRUPTS, 1, + "ath edma rx proc: npkts=%d\n", + npkts); + + return; +} + +/* + * Flush the deferred queue. + * + * This destructively flushes the deferred queue - it doesn't + * call the wireless stack on each mbuf. + */ +static void +ath_edma_flush_deferred_queue(struct ath_softc *sc) +{ + struct ath_buf *bf; + + ATH_RX_LOCK_ASSERT(sc); + + /* Free in one set, inside the lock */ + while (! TAILQ_EMPTY(&sc->sc_rx_rxlist[HAL_RX_QUEUE_LP])) { + bf = TAILQ_FIRST(&sc->sc_rx_rxlist[HAL_RX_QUEUE_LP]); + TAILQ_REMOVE(&sc->sc_rx_rxlist[HAL_RX_QUEUE_LP], bf, bf_list); + /* Free the buffer/mbuf */ + ath_edma_rxbuf_free(sc, bf); + } + while (! TAILQ_EMPTY(&sc->sc_rx_rxlist[HAL_RX_QUEUE_HP])) { + bf = TAILQ_FIRST(&sc->sc_rx_rxlist[HAL_RX_QUEUE_HP]); + TAILQ_REMOVE(&sc->sc_rx_rxlist[HAL_RX_QUEUE_HP], bf, bf_list); + /* Free the buffer/mbuf */ + ath_edma_rxbuf_free(sc, bf); + } +} + +static int +ath_edma_recv_proc_deferred_queue(struct ath_softc *sc, HAL_RX_QUEUE qtype, + int dosched) +{ + int ngood = 0; + uint64_t tsf; + struct ath_buf *bf, *next; + struct ath_rx_status *rs; + int16_t nf; + ath_bufhead rxlist; + struct mbuf *m; + + TAILQ_INIT(&rxlist); + + nf = ath_hal_getchannoise(sc->sc_ah, sc->sc_curchan); + /* + * XXX TODO: the NF/TSF should be stamped on the bufs themselves, + * otherwise we may end up adding in the wrong values if this + * is delayed too far.. + */ + tsf = ath_hal_gettsf64(sc->sc_ah); + + /* Copy the list over */ + ATH_RX_LOCK(sc); + TAILQ_CONCAT(&rxlist, &sc->sc_rx_rxlist[qtype], bf_list); + ATH_RX_UNLOCK(sc); + + /* Handle the completed descriptors */ + /* + * XXX is this SAFE call needed? The ath_buf entries + * aren't modified by ath_rx_pkt, right? + */ + TAILQ_FOREACH_SAFE(bf, &rxlist, bf_list, next) { + /* + * Skip the RX descriptor status - start at the data offset + */ + m_adj(bf->bf_m, sc->sc_rx_statuslen); + + /* Handle the frame */ + + rs = &bf->bf_status.ds_rxstat; + m = bf->bf_m; + bf->bf_m = NULL; + if (ath_rx_pkt(sc, rs, bf->bf_rxstatus, tsf, nf, qtype, bf, m)) + ngood++; + } + + if (ngood) { + sc->sc_lastrx = tsf; + } + + ATH_KTR(sc, ATH_KTR_INTERRUPTS, 1, + "ath edma rx deferred proc: ngood=%d\n", + ngood); + + /* Free in one set, inside the lock */ + ATH_RX_LOCK(sc); + while (! TAILQ_EMPTY(&rxlist)) { + bf = TAILQ_FIRST(&rxlist); + TAILQ_REMOVE(&rxlist, bf, bf_list); + /* Free the buffer/mbuf */ + ath_edma_rxbuf_free(sc, bf); + } + ATH_RX_UNLOCK(sc); + + return (ngood); +} + +static void +ath_edma_recv_tasklet(void *arg, int npending) +{ + struct ath_softc *sc = (struct ath_softc *) arg; +#ifdef IEEE80211_SUPPORT_SUPERG + struct ieee80211com *ic = &sc->sc_ic; +#endif + + DPRINTF(sc, ATH_DEBUG_EDMA_RX, "%s: called; npending=%d\n", + __func__, + npending); + + ATH_PCU_LOCK(sc); + if (sc->sc_inreset_cnt > 0) { + device_printf(sc->sc_dev, "%s: sc_inreset_cnt > 0; skipping\n", + __func__); + ATH_PCU_UNLOCK(sc); + return; + } + sc->sc_rxproc_cnt++; + ATH_PCU_UNLOCK(sc); + + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + + ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_HP, 1); + ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_LP, 1); + + ath_edma_recv_proc_deferred_queue(sc, HAL_RX_QUEUE_HP, 1); + ath_edma_recv_proc_deferred_queue(sc, HAL_RX_QUEUE_LP, 1); + + /* + * XXX: If we read the tsf/channoise here and then pass it in, + * we could restore the power state before processing + * the deferred queue. + */ + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + +#ifdef IEEE80211_SUPPORT_SUPERG + ieee80211_ff_age_all(ic, 100); +#endif + if (ath_dfs_tasklet_needed(sc, sc->sc_curchan)) + taskqueue_enqueue(sc->sc_tq, &sc->sc_dfstask); + + ATH_PCU_LOCK(sc); + sc->sc_rxproc_cnt--; + ATH_PCU_UNLOCK(sc); +} + +/* + * Allocate an RX mbuf for the given ath_buf and initialise + * it for EDMA. + * + * + Allocate a 4KB mbuf; + * + Setup the DMA map for the given buffer; + * + Return that. + */ +static int +ath_edma_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf) +{ + + struct mbuf *m; + int error; + int len; + + ATH_RX_LOCK_ASSERT(sc); + + m = m_getm(NULL, sc->sc_edma_bufsize, M_NOWAIT, MT_DATA); + if (! m) + return (ENOBUFS); /* XXX ?*/ + + /* XXX warn/enforce alignment */ + + len = m->m_ext.ext_size; +#if 0 + device_printf(sc->sc_dev, "%s: called: m=%p, size=%d, mtod=%p\n", + __func__, + m, + len, + mtod(m, char *)); +#endif + + m->m_pkthdr.len = m->m_len = m->m_ext.ext_size; + + /* + * Populate ath_buf fields. + */ + bf->bf_desc = mtod(m, struct ath_desc *); + bf->bf_lastds = bf->bf_desc; /* XXX only really for TX? */ + bf->bf_m = m; + + /* + * Zero the descriptor and ensure it makes it out to the + * bounce buffer if one is required. + * + * XXX PREWRITE will copy the whole buffer; we only needed it + * to sync the first 32 DWORDS. Oh well. + */ + memset(bf->bf_desc, '\0', sc->sc_rx_statuslen); + + /* + * Create DMA mapping. + */ + error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, + bf->bf_dmamap, m, bf->bf_segs, &bf->bf_nseg, BUS_DMA_NOWAIT); + + if (error != 0) { + device_printf(sc->sc_dev, "%s: failed; error=%d\n", + __func__, + error); + m_freem(m); + return (error); + } + + /* + * Set daddr to the physical mapping page. + */ + bf->bf_daddr = bf->bf_segs[0].ds_addr; + + /* + * Prepare for the upcoming read. + * + * We need to both sync some data into the buffer (the zero'ed + * descriptor payload) and also prepare for the read that's going + * to occur. + */ + bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + /* Finish! */ + return (0); +} + +/* + * Allocate a RX buffer. + */ +static struct ath_buf * +ath_edma_rxbuf_alloc(struct ath_softc *sc) +{ + struct ath_buf *bf; + int error; + + ATH_RX_LOCK_ASSERT(sc); + + /* Allocate buffer */ + bf = TAILQ_FIRST(&sc->sc_rxbuf); + /* XXX shouldn't happen upon startup? */ + if (bf == NULL) { + device_printf(sc->sc_dev, "%s: nothing on rxbuf?!\n", + __func__); + return (NULL); + } + + /* Remove it from the free list */ + TAILQ_REMOVE(&sc->sc_rxbuf, bf, bf_list); + + /* Assign RX mbuf to it */ + error = ath_edma_rxbuf_init(sc, bf); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: bf=%p, rxbuf alloc failed! error=%d\n", + __func__, + bf, + error); + TAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list); + return (NULL); + } + + return (bf); +} + +static void +ath_edma_rxbuf_free(struct ath_softc *sc, struct ath_buf *bf) +{ + + ATH_RX_LOCK_ASSERT(sc); + + /* + * Only unload the frame if we haven't consumed + * the mbuf via ath_rx_pkt(). + */ + if (bf->bf_m) { + bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); + m_freem(bf->bf_m); + bf->bf_m = NULL; + } + + /* XXX lock? */ + TAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list); +} + +/* + * Allocate up to 'n' entries and push them onto the hardware FIFO. + * + * Return how many entries were successfully pushed onto the + * FIFO. + */ +static int +ath_edma_rxfifo_alloc(struct ath_softc *sc, HAL_RX_QUEUE qtype, int nbufs) +{ + struct ath_rx_edma *re = &sc->sc_rxedma[qtype]; + struct ath_buf *bf; + int i; + + ATH_RX_LOCK_ASSERT(sc); + + /* + * Allocate buffers until the FIFO is full or nbufs is reached. + */ + for (i = 0; i < nbufs && re->m_fifo_depth < re->m_fifolen; i++) { + /* Ensure the FIFO is already blank, complain loudly! */ + if (re->m_fifo[re->m_fifo_tail] != NULL) { + device_printf(sc->sc_dev, + "%s: Q%d: fifo[%d] != NULL (%p)\n", + __func__, + qtype, + re->m_fifo_tail, + re->m_fifo[re->m_fifo_tail]); + + /* Free the slot */ + ath_edma_rxbuf_free(sc, re->m_fifo[re->m_fifo_tail]); + re->m_fifo_depth--; + /* XXX check it's not < 0 */ + re->m_fifo[re->m_fifo_tail] = NULL; + } + + bf = ath_edma_rxbuf_alloc(sc); + /* XXX should ensure the FIFO is not NULL? */ + if (bf == NULL) { + device_printf(sc->sc_dev, + "%s: Q%d: alloc failed: i=%d, nbufs=%d?\n", + __func__, + qtype, + i, + nbufs); + break; + } + + re->m_fifo[re->m_fifo_tail] = bf; + + /* Write to the RX FIFO */ + DPRINTF(sc, ATH_DEBUG_EDMA_RX, + "%s: Q%d: putrxbuf=%p (0x%jx)\n", + __func__, + qtype, + bf->bf_desc, + (uintmax_t) bf->bf_daddr); + ath_hal_putrxbuf(sc->sc_ah, bf->bf_daddr, qtype); + + re->m_fifo_depth++; + INCR(re->m_fifo_tail, re->m_fifolen); + } + + /* + * Return how many were allocated. + */ + DPRINTF(sc, ATH_DEBUG_EDMA_RX, "%s: Q%d: nbufs=%d, nalloced=%d\n", + __func__, + qtype, + nbufs, + i); + return (i); +} + +static int +ath_edma_rxfifo_flush(struct ath_softc *sc, HAL_RX_QUEUE qtype) +{ + struct ath_rx_edma *re = &sc->sc_rxedma[qtype]; + int i; + + ATH_RX_LOCK_ASSERT(sc); + + for (i = 0; i < re->m_fifolen; i++) { + if (re->m_fifo[i] != NULL) { +#ifdef ATH_DEBUG + struct ath_buf *bf = re->m_fifo[i]; + + if (sc->sc_debug & ATH_DEBUG_RECV_DESC) + ath_printrxbuf(sc, bf, 0, HAL_OK); +#endif + ath_edma_rxbuf_free(sc, re->m_fifo[i]); + re->m_fifo[i] = NULL; + re->m_fifo_depth--; + } + } + + if (re->m_rxpending != NULL) { + m_freem(re->m_rxpending); + re->m_rxpending = NULL; + } + re->m_fifo_head = re->m_fifo_tail = re->m_fifo_depth = 0; + + return (0); +} + +/* + * Setup the initial RX FIFO structure. + */ +static int +ath_edma_setup_rxfifo(struct ath_softc *sc, HAL_RX_QUEUE qtype) +{ + struct ath_rx_edma *re = &sc->sc_rxedma[qtype]; + + ATH_RX_LOCK_ASSERT(sc); + + if (! ath_hal_getrxfifodepth(sc->sc_ah, qtype, &re->m_fifolen)) { + device_printf(sc->sc_dev, "%s: qtype=%d, failed\n", + __func__, + qtype); + return (-EINVAL); + } + + if (bootverbose) + device_printf(sc->sc_dev, + "%s: type=%d, FIFO depth = %d entries\n", + __func__, + qtype, + re->m_fifolen); + + /* Allocate ath_buf FIFO array, pre-zero'ed */ + re->m_fifo = malloc(sizeof(struct ath_buf *) * re->m_fifolen, + M_ATHDEV, + M_NOWAIT | M_ZERO); + if (re->m_fifo == NULL) { + device_printf(sc->sc_dev, "%s: malloc failed\n", + __func__); + return (-ENOMEM); + } + + /* + * Set initial "empty" state. + */ + re->m_rxpending = NULL; + re->m_fifo_head = re->m_fifo_tail = re->m_fifo_depth = 0; + + return (0); +} + +static int +ath_edma_rxfifo_free(struct ath_softc *sc, HAL_RX_QUEUE qtype) +{ + struct ath_rx_edma *re = &sc->sc_rxedma[qtype]; + + device_printf(sc->sc_dev, "%s: called; qtype=%d\n", + __func__, + qtype); + + free(re->m_fifo, M_ATHDEV); + + return (0); +} + +static int +ath_edma_dma_rxsetup(struct ath_softc *sc) +{ + int error; + + /* + * Create RX DMA tag and buffers. + */ + error = ath_descdma_setup_rx_edma(sc, &sc->sc_rxdma, &sc->sc_rxbuf, + "rx", ath_rxbuf, sc->sc_rx_statuslen); + if (error != 0) + return error; + + ATH_RX_LOCK(sc); + (void) ath_edma_setup_rxfifo(sc, HAL_RX_QUEUE_HP); + (void) ath_edma_setup_rxfifo(sc, HAL_RX_QUEUE_LP); + ATH_RX_UNLOCK(sc); + + return (0); +} + +static int +ath_edma_dma_rxteardown(struct ath_softc *sc) +{ + + ATH_RX_LOCK(sc); + ath_edma_flush_deferred_queue(sc); + ath_edma_rxfifo_flush(sc, HAL_RX_QUEUE_HP); + ath_edma_rxfifo_free(sc, HAL_RX_QUEUE_HP); + + ath_edma_rxfifo_flush(sc, HAL_RX_QUEUE_LP); + ath_edma_rxfifo_free(sc, HAL_RX_QUEUE_LP); + ATH_RX_UNLOCK(sc); + + /* Free RX ath_buf */ + /* Free RX DMA tag */ + if (sc->sc_rxdma.dd_desc_len != 0) + ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf); + + return (0); +} + +void +ath_recv_setup_edma(struct ath_softc *sc) +{ + + /* Set buffer size to 4k */ + sc->sc_edma_bufsize = 4096; + + /* Fetch EDMA field and buffer sizes */ + (void) ath_hal_getrxstatuslen(sc->sc_ah, &sc->sc_rx_statuslen); + + /* Configure the hardware with the RX buffer size */ + (void) ath_hal_setrxbufsize(sc->sc_ah, sc->sc_edma_bufsize - + sc->sc_rx_statuslen); + + if (bootverbose) { + device_printf(sc->sc_dev, "RX status length: %d\n", + sc->sc_rx_statuslen); + device_printf(sc->sc_dev, "RX buffer size: %d\n", + sc->sc_edma_bufsize); + } + + sc->sc_rx.recv_stop = ath_edma_stoprecv; + sc->sc_rx.recv_start = ath_edma_startrecv; + sc->sc_rx.recv_flush = ath_edma_recv_flush; + sc->sc_rx.recv_tasklet = ath_edma_recv_tasklet; + sc->sc_rx.recv_rxbuf_init = ath_edma_rxbuf_init; + + sc->sc_rx.recv_setup = ath_edma_dma_rxsetup; + sc->sc_rx.recv_teardown = ath_edma_dma_rxteardown; + + sc->sc_rx.recv_sched = ath_edma_recv_sched; + sc->sc_rx.recv_sched_queue = ath_edma_recv_sched_queue; +} diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_rx_edma.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_rx_edma.h new file mode 100644 index 0000000000..3eac600d08 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_rx_edma.h @@ -0,0 +1,36 @@ +/*- + * Copyright (c) 2012 Adrian Chadd + * 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. + * + * $FreeBSD: releng/11.1/sys/dev/ath/if_ath_rx_edma.h 238055 2012-07-03 06:59:12Z adrian $ + */ +#ifndef __IF_ATH_RX_EDMA_H__ +#define __IF_ATH_RX_EDMA_H__ + +extern void ath_recv_setup_edma(struct ath_softc *sc); + +#endif diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_spectral.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_spectral.c new file mode 100644 index 0000000000..680d2ac659 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_spectral.c @@ -0,0 +1,290 @@ +/*- + * Copyright (c) 2013 Adrian Chadd + * 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. + * + * $FreeBSD: releng/11.1/sys/dev/ath/if_ath_spectral.c 257176 2013-10-26 17:58:36Z glebius $ + */ +#include +__FBSDID("$FreeBSD: releng/11.1/sys/dev/ath/if_ath_spectral.c 257176 2013-10-26 17:58:36Z glebius $"); + +/* + * Implement some basic spectral scan control logic. + */ +#include "opt_ath.h" +#include "opt_inet.h" +#include "opt_wlan.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include /* XXX for ether_sprintf */ + +#include + +#include + +#ifdef INET +#include +#include +#endif + +#include +#include + +#include + +struct ath_spectral_state { + HAL_SPECTRAL_PARAM spectral_state; + + /* + * Should we enable spectral scan upon + * each network interface reset/change? + * + * This is intended to allow spectral scan + * frame reporting during channel scans. + * + * Later on it can morph into a larger + * scale config method where it pushes + * a "channel scan" config into the hardware + * rather than just the spectral_state + * config. + */ + int spectral_enable_after_reset; +}; + +/* + * Methods which are required + */ + +/* + * Attach spectral to the given interface + */ +int +ath_spectral_attach(struct ath_softc *sc) +{ + struct ath_spectral_state *ss; + + /* + * If spectral isn't supported, don't error - just + * quietly complete. + */ + if (! ath_hal_spectral_supported(sc->sc_ah)) + return (0); + + ss = malloc(sizeof(struct ath_spectral_state), + M_TEMP, M_WAITOK | M_ZERO); + + if (ss == NULL) { + device_printf(sc->sc_dev, "%s: failed to alloc memory\n", + __func__); + return (-ENOMEM); + } + + sc->sc_spectral = ss; + + (void) ath_hal_spectral_get_config(sc->sc_ah, &ss->spectral_state); + + return (0); +} + +/* + * Detach spectral from the given interface + */ +int +ath_spectral_detach(struct ath_softc *sc) +{ + + if (! ath_hal_spectral_supported(sc->sc_ah)) + return (0); + + if (sc->sc_spectral != NULL) { + free(sc->sc_spectral, M_TEMP); + } + return (0); +} + +/* + * Check whether spectral needs enabling and if so, + * flip it on. + */ +int +ath_spectral_enable(struct ath_softc *sc, struct ieee80211_channel *ch) +{ + struct ath_spectral_state *ss = sc->sc_spectral; + + /* Default to disable spectral PHY reporting */ + sc->sc_dospectral = 0; + + if (ss == NULL) + return (0); + + if (ss->spectral_enable_after_reset) { + ath_hal_spectral_configure(sc->sc_ah, + &ss->spectral_state); + (void) ath_hal_spectral_start(sc->sc_ah); + sc->sc_dospectral = 1; + } + return (0); +} + +/* + * Handle ioctl requests from the diagnostic interface. + * + * The initial part of this code resembles ath_ioctl_diag(); + * it's likely a good idea to reduce duplication between + * these two routines. + */ +int +ath_ioctl_spectral(struct ath_softc *sc, struct ath_diag *ad) +{ + unsigned int id = ad->ad_id & ATH_DIAG_ID; + void *indata = NULL; + void *outdata = NULL; + u_int32_t insize = ad->ad_in_size; + u_int32_t outsize = ad->ad_out_size; + int error = 0; + HAL_SPECTRAL_PARAM peout; + HAL_SPECTRAL_PARAM *pe; + struct ath_spectral_state *ss = sc->sc_spectral; + int val; + + if (! ath_hal_spectral_supported(sc->sc_ah)) + return (EINVAL); + + if (ad->ad_id & ATH_DIAG_IN) { + /* + * Copy in data. + */ + indata = malloc(insize, M_TEMP, M_NOWAIT); + if (indata == NULL) { + error = ENOMEM; + goto bad; + } + error = copyin(ad->ad_in_data, indata, insize); + if (error) + goto bad; + } + if (ad->ad_id & ATH_DIAG_DYN) { + /* + * Allocate a buffer for the results (otherwise the HAL + * returns a pointer to a buffer where we can read the + * results). Note that we depend on the HAL leaving this + * pointer for us to use below in reclaiming the buffer; + * may want to be more defensive. + */ + outdata = malloc(outsize, M_TEMP, M_NOWAIT); + if (outdata == NULL) { + error = ENOMEM; + goto bad; + } + } + switch (id) { + case SPECTRAL_CONTROL_GET_PARAMS: + memset(&peout, 0, sizeof(peout)); + outsize = sizeof(HAL_SPECTRAL_PARAM); + ath_hal_spectral_get_config(sc->sc_ah, &peout); + pe = (HAL_SPECTRAL_PARAM *) outdata; + memcpy(pe, &peout, sizeof(*pe)); + break; + case SPECTRAL_CONTROL_SET_PARAMS: + if (insize < sizeof(HAL_SPECTRAL_PARAM)) { + error = EINVAL; + break; + } + pe = (HAL_SPECTRAL_PARAM *) indata; + ath_hal_spectral_configure(sc->sc_ah, pe); + /* Save a local copy of the updated parameters */ + ath_hal_spectral_get_config(sc->sc_ah, + &ss->spectral_state); + break; + case SPECTRAL_CONTROL_START: + ath_hal_spectral_configure(sc->sc_ah, + &ss->spectral_state); + (void) ath_hal_spectral_start(sc->sc_ah); + sc->sc_dospectral = 1; + /* XXX need to update the PHY mask in the driver */ + break; + case SPECTRAL_CONTROL_STOP: + (void) ath_hal_spectral_stop(sc->sc_ah); + sc->sc_dospectral = 0; + /* XXX need to update the PHY mask in the driver */ + break; + case SPECTRAL_CONTROL_ENABLE_AT_RESET: + if (insize < sizeof(int)) { + device_printf(sc->sc_dev, "%d != %d\n", + insize, + (int) sizeof(int)); + error = EINVAL; + break; + } + if (indata == NULL) { + device_printf(sc->sc_dev, "indata=NULL\n"); + error = EINVAL; + break; + } + val = * ((int *) indata); + if (val == 0) + ss->spectral_enable_after_reset = 0; + else + ss->spectral_enable_after_reset = 1; + break; + case SPECTRAL_CONTROL_ENABLE: + /* XXX TODO */ + case SPECTRAL_CONTROL_DISABLE: + /* XXX TODO */ + break; + default: + error = EINVAL; + } + if (outsize < ad->ad_out_size) + ad->ad_out_size = outsize; + if (outdata && copyout(outdata, ad->ad_out_data, ad->ad_out_size)) + error = EFAULT; +bad: + if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL) + free(indata, M_TEMP); + if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL) + free(outdata, M_TEMP); + return (error); +} + diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_spectral.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_spectral.h new file mode 100644 index 0000000000..ea309e69c4 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_spectral.h @@ -0,0 +1,40 @@ +/*- + * Copyright (c) 2013 Adrian Chadd + * 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. + * + * $FreeBSD: releng/11.1/sys/dev/ath/if_ath_spectral.h 244951 2013-01-02 03:59:02Z adrian $ + */ +#ifndef __IF_ATH_SPECTRAL_H__ +#define __IF_ATH_SPECTRAL_H__ + +extern int ath_spectral_attach(struct ath_softc *sc); +extern int ath_spectral_detach(struct ath_softc *sc); +extern int ath_ioctl_spectral(struct ath_softc *sc, struct ath_diag *ad); +extern int ath_spectral_enable(struct ath_softc *sc, + struct ieee80211_channel *ch); + +#endif /* __IF_ATH_SPECTRAL_H__ */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_sysctl.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_sysctl.c index 4c60275bb0..7966b2a29e 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_sysctl.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_sysctl.c @@ -28,7 +28,7 @@ */ #include -__FBSDID("$FreeBSD$"); +__FBSDID("$FreeBSD: releng/11.1/sys/dev/ath/if_ath_sysctl.c 301766 2016-06-09 21:59:36Z adrian $"); /* * Driver for the Atheros Wireless LAN controller. @@ -62,6 +62,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -90,6 +91,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -98,43 +100,86 @@ __FBSDID("$FreeBSD$"); #include #endif +#ifdef ATH_DEBUG_ALQ +#include +#endif + static int ath_sysctl_slottime(SYSCTL_HANDLER_ARGS) { struct ath_softc *sc = arg1; - u_int slottime = ath_hal_getslottime(sc->sc_ah); + u_int slottime; int error; + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + slottime = ath_hal_getslottime(sc->sc_ah); + ATH_UNLOCK(sc); + error = sysctl_handle_int(oidp, &slottime, 0, req); if (error || !req->newptr) - return error; - return !ath_hal_setslottime(sc->sc_ah, slottime) ? EINVAL : 0; + goto finish; + + error = !ath_hal_setslottime(sc->sc_ah, slottime) ? EINVAL : 0; + +finish: + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + return error; } static int ath_sysctl_acktimeout(SYSCTL_HANDLER_ARGS) { struct ath_softc *sc = arg1; - u_int acktimeout = ath_hal_getacktimeout(sc->sc_ah); + u_int acktimeout; int error; + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + acktimeout = ath_hal_getacktimeout(sc->sc_ah); + ATH_UNLOCK(sc); + error = sysctl_handle_int(oidp, &acktimeout, 0, req); if (error || !req->newptr) - return error; - return !ath_hal_setacktimeout(sc->sc_ah, acktimeout) ? EINVAL : 0; + goto finish; + + error = !ath_hal_setacktimeout(sc->sc_ah, acktimeout) ? EINVAL : 0; + +finish: + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + return (error); } static int ath_sysctl_ctstimeout(SYSCTL_HANDLER_ARGS) { struct ath_softc *sc = arg1; - u_int ctstimeout = ath_hal_getctstimeout(sc->sc_ah); + u_int ctstimeout; int error; + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ctstimeout = ath_hal_getctstimeout(sc->sc_ah); + ATH_UNLOCK(sc); + error = sysctl_handle_int(oidp, &ctstimeout, 0, req); if (error || !req->newptr) - return error; - return !ath_hal_setctstimeout(sc->sc_ah, ctstimeout) ? EINVAL : 0; + goto finish; + + error = !ath_hal_setctstimeout(sc->sc_ah, ctstimeout) ? EINVAL : 0; + +finish: + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + return (error); } static int @@ -151,10 +196,7 @@ ath_sysctl_softled(SYSCTL_HANDLER_ARGS) if (softled != sc->sc_softled) { if (softled) { /* NB: handle any sc_ledpin change */ - ath_hal_gpioCfgOutput(sc->sc_ah, sc->sc_ledpin, - HAL_GPIO_MUX_MAC_NETWORK_LED); - ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, - !sc->sc_ledon); + ath_led_config(sc); } sc->sc_softled = softled; } @@ -174,27 +216,53 @@ ath_sysctl_ledpin(SYSCTL_HANDLER_ARGS) if (ledpin != sc->sc_ledpin) { sc->sc_ledpin = ledpin; if (sc->sc_softled) { - ath_hal_gpioCfgOutput(sc->sc_ah, sc->sc_ledpin, - HAL_GPIO_MUX_MAC_NETWORK_LED); - ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, - !sc->sc_ledon); + 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); + u_int txantenna; int error; + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + + txantenna = ath_hal_getantennaswitch(sc->sc_ah); + 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; + if (txantenna < HAL_ANT_VARIABLE || txantenna > HAL_ANT_FIXED_B) { + error = EINVAL; + goto finish; + } ath_hal_setantennaswitch(sc->sc_ah, txantenna); /* * NB: with the switch locked this isn't meaningful, @@ -203,36 +271,67 @@ ath_sysctl_txantenna(SYSCTL_HANDLER_ARGS) */ sc->sc_txantenna = txantenna; } - return error; + +finish: + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + return (error); } static int ath_sysctl_rxantenna(SYSCTL_HANDLER_ARGS) { struct ath_softc *sc = arg1; - u_int defantenna = ath_hal_getdefantenna(sc->sc_ah); + u_int defantenna; int error; + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + defantenna = ath_hal_getdefantenna(sc->sc_ah); + ATH_UNLOCK(sc); + error = sysctl_handle_int(oidp, &defantenna, 0, req); if (!error && req->newptr) ath_hal_setdefantenna(sc->sc_ah, defantenna); - return error; + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + return (error); } static int ath_sysctl_diversity(SYSCTL_HANDLER_ARGS) { struct ath_softc *sc = arg1; - u_int diversity = ath_hal_getdiversity(sc->sc_ah); + u_int diversity; int error; + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + + diversity = ath_hal_getdiversity(sc->sc_ah); + error = sysctl_handle_int(oidp, &diversity, 0, req); if (error || !req->newptr) - return error; - if (!ath_hal_setdiversity(sc->sc_ah, diversity)) - return EINVAL; + goto finish; + if (!ath_hal_setdiversity(sc->sc_ah, diversity)) { + error = EINVAL; + goto finish; + } sc->sc_diversity = diversity; - return 0; + error = 0; + +finish: + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + return (error); } static int @@ -242,60 +341,213 @@ ath_sysctl_diag(SYSCTL_HANDLER_ARGS) u_int32_t diag; int error; - if (!ath_hal_getdiag(sc->sc_ah, &diag)) - return EINVAL; + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + + if (!ath_hal_getdiag(sc->sc_ah, &diag)) { + error = EINVAL; + goto finish; + } + error = sysctl_handle_int(oidp, &diag, 0, req); if (error || !req->newptr) - return error; - return !ath_hal_setdiag(sc->sc_ah, diag) ? EINVAL : 0; + goto finish; + error = !ath_hal_setdiag(sc->sc_ah, diag) ? EINVAL : 0; + +finish: + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + return (error); } 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; + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + (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) : 0; + goto finish; + + error = !ath_hal_settpscale(sc->sc_ah, scale) ? EINVAL : + (sc->sc_running) ? ath_reset(sc, ATH_RESET_NOLOSS) : 0; + +finish: + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + return (error); } static int ath_sysctl_tpc(SYSCTL_HANDLER_ARGS) { struct ath_softc *sc = arg1; - u_int tpc = ath_hal_gettpc(sc->sc_ah); + u_int tpc; int error; + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + + tpc = ath_hal_gettpc(sc->sc_ah); + error = sysctl_handle_int(oidp, &tpc, 0, req); if (error || !req->newptr) - return error; - return !ath_hal_settpc(sc->sc_ah, tpc) ? EINVAL : 0; + goto finish; + error = !ath_hal_settpc(sc->sc_ah, tpc) ? EINVAL : 0; + +finish: + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + return (error); } 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); + u_int rfkill; int error; + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + + rfkill = ath_hal_getrfkill(ah); + error = sysctl_handle_int(oidp, &rfkill, 0, req); + if (error || !req->newptr) + goto finish; + if (rfkill == ath_hal_getrfkill(ah)) { /* unchanged */ + error = 0; + goto finish; + } + if (!ath_hal_setrfkill(ah, rfkill)) { + error = EINVAL; + goto finish; + } + error = sc->sc_running ? ath_reset(sc, ATH_RESET_FULL) : 0; + +finish: + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + return (error); +} + +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 (rfkill == ath_hal_getrfkill(ah)) /* unchanged */ + + if (param != 1) return 0; - if (!ath_hal_setrfkill(ah, rfkill)) - return EINVAL; - return (ifp->if_drv_flags & IFF_DRV_RUNNING) ? ath_reset(ifp) : 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)) { + 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, + sc->sc_txq[i].axq_aggr_depth, + 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; m_pending=%p, m_holdbf=%p\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, + sc->sc_rxedma[i].m_rxpending, + sc->sc_rxedma[i].m_holdbf); + } + 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 @@ -305,15 +557,36 @@ ath_sysctl_rfsilent(SYSCTL_HANDLER_ARGS) u_int rfsilent; int error; + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + (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; + goto finish; + if (!ath_hal_setrfsilent(sc->sc_ah, rfsilent)) { + error = EINVAL; + goto finish; + } + /* + * Earlier chips (< AR5212) have up to 8 GPIO + * pins exposed. + * + * AR5416 and later chips have many more GPIO + * pins (up to 16) so the mask is expanded to + * four bits. + */ + sc->sc_rfsilentpin = rfsilent & 0x3c; sc->sc_rfsilentpol = (rfsilent & 0x2) != 0; - return 0; + error = 0; + +finish: + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + return (error); } static int @@ -323,11 +596,22 @@ ath_sysctl_tpack(SYSCTL_HANDLER_ARGS) u_int32_t tpack; int error; + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + (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; + goto finish; + error = !ath_hal_settpack(sc->sc_ah, tpack) ? EINVAL : 0; + +finish: + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + return (error); } static int @@ -337,11 +621,23 @@ ath_sysctl_tpcts(SYSCTL_HANDLER_ARGS) u_int32_t tpcts; int error; + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + (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; + goto finish; + + error = !ath_hal_settpcts(sc->sc_ah, tpcts) ? EINVAL : 0; + +finish: + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + return (error); } static int @@ -350,25 +646,38 @@ ath_sysctl_intmit(SYSCTL_HANDLER_ARGS) struct ath_softc *sc = arg1; int intmit, error; + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + intmit = ath_hal_getintmit(sc->sc_ah); error = sysctl_handle_int(oidp, &intmit, 0, req); if (error || !req->newptr) - return error; + goto finish; /* reusing error; 1 here means "good"; 0 means "fail" */ error = ath_hal_setintmit(sc->sc_ah, intmit); - if (! error) - return EINVAL; + if (! error) { + error = EINVAL; + goto finish; + } /* * 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); + if (sc->sc_running) + ath_reset(sc, ATH_RESET_NOLOSS); - return 0; + error = 0; + +finish: + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + return (error); } #ifdef IEEE80211_SUPPORT_TDMA @@ -387,6 +696,115 @@ ath_sysctl_setcca(SYSCTL_HANDLER_ARGS) } #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(sc->sc_tq, &sc->sc_bstucktask); + val = 0; + return 0; +} + +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; + + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + + /* Do a hang check */ + if (!ath_hal_getdiagstate(ah, HAL_DIAG_CHECK_HANGS, + &mask, sizeof(mask), + (void *) &sp, &rsize)) { + error = 0; + goto finish; + } + + device_printf(sc->sc_dev, "%s: sp=0x%08x\n", __func__, *sp); + + val = 0; + error = 0; +finish: + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + return (error); +} + +#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) { @@ -401,10 +819,15 @@ ath_sysctlattach(struct ath_softc *sc) "regdomain", CTLFLAG_RD, &sc->sc_eerd, 0, "EEPROM regdomain code"); #ifdef ATH_DEBUG - SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, - "debug", CTLFLAG_RW, &sc->sc_debug, 0, + 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)"); @@ -414,6 +837,7 @@ ath_sysctlattach(struct ath_softc *sc) 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"); @@ -426,6 +850,18 @@ ath_sysctlattach(struct ath_softc *sc) 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"); @@ -465,6 +901,19 @@ ath_sysctlattach(struct ath_softc *sc) "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", ""); + + 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, @@ -474,6 +923,53 @@ ath_sysctlattach(struct ath_softc *sc) 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"); + +#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; @@ -495,6 +991,10 @@ ath_sysctlattach(struct ath_softc *sc) ath_sysctl_setcca, "I", "enable CCA control"); } #endif + +#ifdef ATH_DEBUG_ALQ + ath_sysctl_alq_attach(sc); +#endif } static int @@ -510,6 +1010,9 @@ ath_sysctl_clearstats(SYSCTL_HANDLER_ARGS) 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; } @@ -531,6 +1034,26 @@ ath_sysctl_stats_attach_rxphyerr(struct ath_softc *sc, struct sysctl_oid_list *p } } +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) { @@ -729,9 +1252,54 @@ ath_sysctl_stats_attach(struct ath_softc *sc) &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, ""); + 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_tx_nodeq_overflow", + CTLFLAG_RD, &sc->sc_stats.ast_tx_nodeq_overflow, 0, + "tx dropped 'cuz nodeq overflow"); + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_stbc", + CTLFLAG_RD, &sc->sc_stats.ast_rx_stbc, 0, + "Number of STBC frames received"); + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_stbc", + CTLFLAG_RD, &sc->sc_stats.ast_tx_stbc, 0, + "Number of STBC frames transmitted"); + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_ldpc", + CTLFLAG_RD, &sc->sc_stats.ast_tx_ldpc, 0, + "Number of LDPC frames transmitted"); + /* 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); } /* @@ -772,4 +1340,16 @@ ath_sysctl_hal_attach(struct ath_softc *sc) 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"); } diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_sysctl.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_sysctl.h index 1fef2be224..2536f09f41 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_sysctl.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_sysctl.h @@ -26,7 +26,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/if_ath_sysctl.h 223459 2011-06-23 02:38:36Z adrian $ */ #ifndef __IF_ATH_SYSCTL_H__ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_tdma.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_tdma.c new file mode 100644 index 0000000000..8628d0b227 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_tdma.c @@ -0,0 +1,686 @@ +/*- + * 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 +__FBSDID("$FreeBSD: releng/11.1/sys/dev/ath/if_ath_tdma.c 288095 2015-09-22 06:34:07Z adrian $"); + +/* + * 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" +/* + * This is needed for register operations which are performed + * by the driver - eg, calls to ath_hal_gettsf32(). + * + * It's also required for any AH_DEBUG checks in here, eg the + * module dependencies. + */ +#include "opt_ah.h" +#include "opt_wlan.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for mp_ncpus */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#ifdef IEEE80211_SUPPORT_SUPERG +#include +#endif +#ifdef IEEE80211_SUPPORT_TDMA +#include +#endif + +#include + +#ifdef INET +#include +#include +#endif + +#include +#include /* XXX for softled */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef ATH_TX99_DIAG +#include +#endif + +#ifdef ATH_DEBUG_ALQ +#include +#endif + +#ifdef IEEE80211_SUPPORT_TDMA +#include + +static void ath_tdma_settimers(struct ath_softc *sc, u_int32_t nexttbtt, + u_int32_t bintval); +static void ath_tdma_bintvalsetup(struct ath_softc *sc, + const struct ieee80211_tdma_state *tdma); +#endif /* IEEE80211_SUPPORT_TDMA */ + +#ifdef IEEE80211_SUPPORT_TDMA +static void +ath_tdma_settimers(struct ath_softc *sc, u_int32_t nexttbtt, u_int32_t bintval) +{ + struct ath_hal *ah = sc->sc_ah; + HAL_BEACON_TIMERS bt; + + bt.bt_intval = bintval | HAL_BEACON_ENA; + bt.bt_nexttbtt = nexttbtt; + bt.bt_nextdba = (nexttbtt<<3) - sc->sc_tdmadbaprep; + bt.bt_nextswba = (nexttbtt<<3) - sc->sc_tdmaswbaprep; + bt.bt_nextatim = nexttbtt+1; + /* Enables TBTT, DBA, SWBA timers by default */ + bt.bt_flags = 0; +#if 0 + DPRINTF(sc, ATH_DEBUG_TDMA_TIMER, + "%s: intval=%d (0x%08x) nexttbtt=%u (0x%08x), nextdba=%u (0x%08x), nextswba=%u (0x%08x),nextatim=%u (0x%08x)\n", + __func__, + bt.bt_intval, + bt.bt_intval, + bt.bt_nexttbtt, + bt.bt_nexttbtt, + bt.bt_nextdba, + bt.bt_nextdba, + bt.bt_nextswba, + bt.bt_nextswba, + bt.bt_nextatim, + bt.bt_nextatim); +#endif + +#ifdef ATH_DEBUG_ALQ + if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_TDMA_TIMER_SET)) { + struct if_ath_alq_tdma_timer_set t; + t.bt_intval = htobe32(bt.bt_intval); + t.bt_nexttbtt = htobe32(bt.bt_nexttbtt); + t.bt_nextdba = htobe32(bt.bt_nextdba); + t.bt_nextswba = htobe32(bt.bt_nextswba); + t.bt_nextatim = htobe32(bt.bt_nextatim); + t.bt_flags = htobe32(bt.bt_flags); + t.sc_tdmadbaprep = htobe32(sc->sc_tdmadbaprep); + t.sc_tdmaswbaprep = htobe32(sc->sc_tdmaswbaprep); + if_ath_alq_post(&sc->sc_alq, ATH_ALQ_TDMA_TIMER_SET, + sizeof(t), (char *) &t); + } +#endif + + DPRINTF(sc, ATH_DEBUG_TDMA_TIMER, + "%s: nexttbtt=%u (0x%08x), nexttbtt tsf=%lld (0x%08llx)\n", + __func__, + bt.bt_nexttbtt, + bt.bt_nexttbtt, + (long long) ( ((u_int64_t) (bt.bt_nexttbtt)) << 10), + (long long) ( ((u_int64_t) (bt.bt_nexttbtt)) << 10)); + ath_hal_beaconsettimers(ah, &bt); +} + +/* + * Calculate the beacon interval. This is periodic in the + * superframe for the bss. We assume each station is configured + * identically wrt transmit rate so the guard time we calculate + * above will be the same on all stations. Note we need to + * factor in the xmit time because the hardware will schedule + * a frame for transmit if the start of the frame is within + * the burst time. When we get hardware that properly kills + * frames in the PCU we can reduce/eliminate the guard time. + * + * Roundup to 1024 is so we have 1 TU buffer in the guard time + * to deal with the granularity of the nexttbtt timer. 11n MAC's + * with 1us timer granularity should allow us to reduce/eliminate + * this. + */ +static void +ath_tdma_bintvalsetup(struct ath_softc *sc, + const struct ieee80211_tdma_state *tdma) +{ + /* copy from vap state (XXX check all vaps have same value?) */ + sc->sc_tdmaslotlen = tdma->tdma_slotlen; + + sc->sc_tdmabintval = roundup((sc->sc_tdmaslotlen+sc->sc_tdmaguard) * + tdma->tdma_slotcnt, 1024); + sc->sc_tdmabintval >>= 10; /* TSF -> TU */ + if (sc->sc_tdmabintval & 1) + sc->sc_tdmabintval++; + + if (tdma->tdma_slot == 0) { + /* + * Only slot 0 beacons; other slots respond. + */ + sc->sc_imask |= HAL_INT_SWBA; + sc->sc_tdmaswba = 0; /* beacon immediately */ + } else { + /* XXX all vaps must be slot 0 or slot !0 */ + sc->sc_imask &= ~HAL_INT_SWBA; + } +} + +/* + * Max 802.11 overhead. This assumes no 4-address frames and + * the encapsulation done by ieee80211_encap (llc). We also + * include potential crypto overhead. + */ +#define IEEE80211_MAXOVERHEAD \ + (sizeof(struct ieee80211_qosframe) \ + + sizeof(struct llc) \ + + IEEE80211_ADDR_LEN \ + + IEEE80211_WEP_IVLEN \ + + IEEE80211_WEP_KIDLEN \ + + IEEE80211_WEP_CRCLEN \ + + IEEE80211_WEP_MICLEN \ + + IEEE80211_CRC_LEN) + +/* + * Setup initially for tdma operation. Start the beacon + * timers and enable SWBA if we are slot 0. Otherwise + * we wait for slot 0 to arrive so we can sync up before + * starting to transmit. + */ +void +ath_tdma_config(struct ath_softc *sc, struct ieee80211vap *vap) +{ + struct ath_hal *ah = sc->sc_ah; + struct ieee80211com *ic = &sc->sc_ic; + const struct ieee80211_txparam *tp; + const struct ieee80211_tdma_state *tdma = NULL; + int rix; + + if (vap == NULL) { + vap = TAILQ_FIRST(&ic->ic_vaps); /* XXX */ + if (vap == NULL) { + device_printf(sc->sc_dev, "%s: no vaps?\n", __func__); + return; + } + } + /* XXX should take a locked ref to iv_bss */ + tp = vap->iv_bss->ni_txparms; + /* + * Calculate the guard time for each slot. This is the + * time to send a maximal-size frame according to the + * fixed/lowest transmit rate. Note that the interface + * mtu does not include the 802.11 overhead so we must + * tack that on (ath_hal_computetxtime includes the + * preamble and plcp in it's calculation). + */ + tdma = vap->iv_tdma; + if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) + rix = ath_tx_findrix(sc, tp->ucastrate); + else + rix = ath_tx_findrix(sc, tp->mcastrate); + + /* + * If the chip supports enforcing TxOP on transmission, + * we can just delete the guard window. It isn't at all required. + */ + if (sc->sc_hasenforcetxop) { + sc->sc_tdmaguard = 0; + } else { + /* XXX short preamble assumed */ + /* XXX non-11n rate assumed */ + sc->sc_tdmaguard = ath_hal_computetxtime(ah, sc->sc_currates, + vap->iv_ifp->if_mtu + IEEE80211_MAXOVERHEAD, rix, AH_TRUE); + } + + ath_hal_intrset(ah, 0); + + ath_beaconq_config(sc); /* setup h/w beacon q */ + if (sc->sc_setcca) + ath_hal_setcca(ah, AH_FALSE); /* disable CCA */ + ath_tdma_bintvalsetup(sc, tdma); /* calculate beacon interval */ + ath_tdma_settimers(sc, sc->sc_tdmabintval, + sc->sc_tdmabintval | HAL_BEACON_RESET_TSF); + sc->sc_syncbeacon = 0; + + sc->sc_avgtsfdeltap = TDMA_DUMMY_MARKER; + sc->sc_avgtsfdeltam = TDMA_DUMMY_MARKER; + + ath_hal_intrset(ah, sc->sc_imask); + + DPRINTF(sc, ATH_DEBUG_TDMA, "%s: slot %u len %uus cnt %u " + "bsched %u guard %uus bintval %u TU dba prep %u\n", __func__, + tdma->tdma_slot, tdma->tdma_slotlen, tdma->tdma_slotcnt, + tdma->tdma_bintval, sc->sc_tdmaguard, sc->sc_tdmabintval, + sc->sc_tdmadbaprep); + +#ifdef ATH_DEBUG_ALQ + if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_TDMA_TIMER_CONFIG)) { + struct if_ath_alq_tdma_timer_config t; + + t.tdma_slot = htobe32(tdma->tdma_slot); + t.tdma_slotlen = htobe32(tdma->tdma_slotlen); + t.tdma_slotcnt = htobe32(tdma->tdma_slotcnt); + t.tdma_bintval = htobe32(tdma->tdma_bintval); + t.tdma_guard = htobe32(sc->sc_tdmaguard); + t.tdma_scbintval = htobe32(sc->sc_tdmabintval); + t.tdma_dbaprep = htobe32(sc->sc_tdmadbaprep); + + if_ath_alq_post(&sc->sc_alq, ATH_ALQ_TDMA_TIMER_CONFIG, + sizeof(t), (char *) &t); + } +#endif /* ATH_DEBUG_ALQ */ +} + +/* + * Update tdma operation. Called from the 802.11 layer + * when a beacon is received from the TDMA station operating + * in the slot immediately preceding us in the bss. Use + * the rx timestamp for the beacon frame to update our + * beacon timers so we follow their schedule. Note that + * by using the rx timestamp we implicitly include the + * propagation delay in our schedule. + * + * XXX TODO: since the changes for the AR5416 and later chips + * involved changing the TSF/TU calculations, we need to make + * sure that various calculations wrap consistently. + * + * A lot of the problems stemmed from the calculations wrapping + * at 65,535 TU. Since a lot of the math is still being done in + * TU, please audit it to ensure that when the TU values programmed + * into the timers wrap at (2^31)-1 TSF, all the various terms + * wrap consistently. + */ +void +ath_tdma_update(struct ieee80211_node *ni, + const struct ieee80211_tdma_param *tdma, int changed) +{ +#define TSF_TO_TU(_h,_l) \ + ((((u_int32_t)(_h)) << 22) | (((u_int32_t)(_l)) >> 10)) +#define TU_TO_TSF(_tu) (((u_int64_t)(_tu)) << 10) + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + struct ath_softc *sc = ic->ic_softc; + struct ath_hal *ah = sc->sc_ah; + const HAL_RATE_TABLE *rt = sc->sc_currates; + u_int64_t tsf, rstamp, nextslot, nexttbtt, nexttbtt_full; + u_int32_t txtime, nextslottu; + int32_t tudelta, tsfdelta; + const struct ath_rx_status *rs; + int rix; + + sc->sc_stats.ast_tdma_update++; + + /* + * Check for and adopt configuration changes. + */ + if (changed != 0) { + const struct ieee80211_tdma_state *ts = vap->iv_tdma; + + ath_tdma_bintvalsetup(sc, ts); + if (changed & TDMA_UPDATE_SLOTLEN) + ath_wme_update(ic); + + DPRINTF(sc, ATH_DEBUG_TDMA, + "%s: adopt slot %u slotcnt %u slotlen %u us " + "bintval %u TU\n", __func__, + ts->tdma_slot, ts->tdma_slotcnt, ts->tdma_slotlen, + sc->sc_tdmabintval); + + /* XXX right? */ + ath_hal_intrset(ah, sc->sc_imask); + /* NB: beacon timers programmed below */ + } + + /* extend rx timestamp to 64 bits */ + rs = sc->sc_lastrs; + tsf = ath_hal_gettsf64(ah); + rstamp = ath_extend_tsf(sc, rs->rs_tstamp, tsf); + /* + * The rx timestamp is set by the hardware on completing + * reception (at the point where the rx descriptor is DMA'd + * to the host). To find the start of our next slot we + * must adjust this time by the time required to send + * the packet just received. + */ + rix = rt->rateCodeToIndex[rs->rs_rate]; + + /* + * To calculate the packet duration for legacy rates, we + * only need the rix and preamble. + * + * For 11n non-aggregate frames, we also need the channel + * width and short/long guard interval. + * + * For 11n aggregate frames, the required hacks are a little + * more subtle. You need to figure out the frame duration + * for each frame, including the delimiters. However, when + * a frame isn't received successfully, we won't hear it + * (unless you enable reception of CRC errored frames), so + * your duration calculation is going to be off. + * + * However, we can assume that the beacon frames won't be + * transmitted as aggregate frames, so we should be okay. + * Just add a check to ensure that we aren't handed something + * bad. + * + * For ath_hal_pkt_txtime() - for 11n rates, shortPreamble is + * actually short guard interval. For legacy rates, + * it's short preamble. + */ + txtime = ath_hal_pkt_txtime(ah, rt, rs->rs_datalen, + rix, + !! (rs->rs_flags & HAL_RX_2040), + (rix & 0x80) ? + (! (rs->rs_flags & HAL_RX_GI)) : rt->info[rix].shortPreamble); + /* NB: << 9 is to cvt to TU and /2 */ + nextslot = (rstamp - txtime) + (sc->sc_tdmabintval << 9); + + /* + * For 802.11n chips: nextslottu needs to be the full TSF space, + * not just 0..65535 TU. + */ + nextslottu = TSF_TO_TU(nextslot>>32, nextslot); + /* + * Retrieve the hardware NextTBTT in usecs + * and calculate the difference between what the + * other station thinks and what we have programmed. This + * lets us figure how to adjust our timers to match. The + * adjustments are done by pulling the TSF forward and possibly + * rewriting the beacon timers. + */ + /* + * The logic here assumes the nexttbtt counter is in TSF + * but the prr-11n NICs are in TU. The HAL shifts them + * to TSF but there's two important differences: + * + * + The TU->TSF values have 0's for the low 9 bits, and + * + The counter wraps at TU_TO_TSF(HAL_BEACON_PERIOD + 1) for + * the pre-11n NICs, but not for the 11n NICs. + * + * So for now, just make sure the nexttbtt value we get + * matches the second issue or once nexttbtt exceeds this + * value, tsfdelta ends up becoming very negative and all + * of the adjustments get very messed up. + */ + + /* + * We need to track the full nexttbtt rather than having it + * truncated at HAL_BEACON_PERIOD, as programming the + * nexttbtt (and related) registers for the 11n chips is + * actually going to take the full 32 bit space, rather than + * just 0..65535 TU. + */ + nexttbtt_full = ath_hal_getnexttbtt(ah); + nexttbtt = nexttbtt_full % (TU_TO_TSF(HAL_BEACON_PERIOD + 1)); + tsfdelta = (int32_t)((nextslot % TU_TO_TSF(HAL_BEACON_PERIOD + 1)) - nexttbtt); + + DPRINTF(sc, ATH_DEBUG_TDMA_TIMER, + "rs->rstamp %llu rstamp %llu tsf %llu txtime %d, nextslot %llu, " + "nextslottu %d, nextslottume %d\n", + (unsigned long long) rs->rs_tstamp, + (unsigned long long) rstamp, + (unsigned long long) tsf, txtime, + (unsigned long long) nextslot, + nextslottu, TSF_TO_TU(nextslot >> 32, nextslot)); + DPRINTF(sc, ATH_DEBUG_TDMA, + " beacon tstamp: %llu (0x%016llx)\n", + (unsigned long long) le64toh(ni->ni_tstamp.tsf), + (unsigned long long) le64toh(ni->ni_tstamp.tsf)); + + DPRINTF(sc, ATH_DEBUG_TDMA_TIMER, + "nexttbtt %llu (0x%08llx) tsfdelta %d avg +%d/-%d\n", + (unsigned long long) nexttbtt, + (long long) nexttbtt, + tsfdelta, + TDMA_AVG(sc->sc_avgtsfdeltap), TDMA_AVG(sc->sc_avgtsfdeltam)); + + if (tsfdelta < 0) { + TDMA_SAMPLE(sc->sc_avgtsfdeltap, 0); + TDMA_SAMPLE(sc->sc_avgtsfdeltam, -tsfdelta); + tsfdelta = -tsfdelta % 1024; + nextslottu++; + } else if (tsfdelta > 0) { + TDMA_SAMPLE(sc->sc_avgtsfdeltap, tsfdelta); + TDMA_SAMPLE(sc->sc_avgtsfdeltam, 0); + tsfdelta = 1024 - (tsfdelta % 1024); + nextslottu++; + } else { + TDMA_SAMPLE(sc->sc_avgtsfdeltap, 0); + TDMA_SAMPLE(sc->sc_avgtsfdeltam, 0); + } + tudelta = nextslottu - TSF_TO_TU(nexttbtt_full >> 32, nexttbtt_full); + +#ifdef ATH_DEBUG_ALQ + if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_TDMA_BEACON_STATE)) { + struct if_ath_alq_tdma_beacon_state t; + t.rx_tsf = htobe64(rstamp); + t.beacon_tsf = htobe64(le64toh(ni->ni_tstamp.tsf)); + t.tsf64 = htobe64(tsf); + t.nextslot_tsf = htobe64(nextslot); + t.nextslot_tu = htobe32(nextslottu); + t.txtime = htobe32(txtime); + if_ath_alq_post(&sc->sc_alq, ATH_ALQ_TDMA_BEACON_STATE, + sizeof(t), (char *) &t); + } + + if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_TDMA_SLOT_CALC)) { + struct if_ath_alq_tdma_slot_calc t; + + t.nexttbtt = htobe64(nexttbtt_full); + t.next_slot = htobe64(nextslot); + t.tsfdelta = htobe32(tsfdelta); + t.avg_plus = htobe32(TDMA_AVG(sc->sc_avgtsfdeltap)); + t.avg_minus = htobe32(TDMA_AVG(sc->sc_avgtsfdeltam)); + + if_ath_alq_post(&sc->sc_alq, ATH_ALQ_TDMA_SLOT_CALC, + sizeof(t), (char *) &t); + } +#endif + + /* + * Copy sender's timetstamp into tdma ie so they can + * calculate roundtrip time. We submit a beacon frame + * below after any timer adjustment. The frame goes out + * at the next TBTT so the sender can calculate the + * roundtrip by inspecting the tdma ie in our beacon frame. + * + * NB: This tstamp is subtlely preserved when + * IEEE80211_BEACON_TDMA is marked (e.g. when the + * slot position changes) because ieee80211_add_tdma + * skips over the data. + */ + memcpy(vap->iv_bcn_off.bo_tdma + + __offsetof(struct ieee80211_tdma_param, tdma_tstamp), + &ni->ni_tstamp.data, 8); +#if 0 + DPRINTF(sc, ATH_DEBUG_TDMA_TIMER, + "tsf %llu nextslot %llu (%d, %d) nextslottu %u nexttbtt %llu (%d)\n", + (unsigned long long) tsf, (unsigned long long) nextslot, + (int)(nextslot - tsf), tsfdelta, nextslottu, nexttbtt, tudelta); +#endif + /* + * Adjust the beacon timers only when pulling them forward + * or when going back by less than the beacon interval. + * Negative jumps larger than the beacon interval seem to + * cause the timers to stop and generally cause instability. + * This basically filters out jumps due to missed beacons. + */ + if (tudelta != 0 && (tudelta > 0 || -tudelta < sc->sc_tdmabintval)) { + DPRINTF(sc, ATH_DEBUG_TDMA_TIMER, + "%s: calling ath_tdma_settimers; nextslottu=%d, bintval=%d\n", + __func__, + nextslottu, + sc->sc_tdmabintval); + ath_tdma_settimers(sc, nextslottu, sc->sc_tdmabintval); + sc->sc_stats.ast_tdma_timers++; + } + if (tsfdelta > 0) { + uint64_t tsf; + + /* XXX should just teach ath_hal_adjusttsf() to do this */ + tsf = ath_hal_gettsf64(ah); + ath_hal_settsf64(ah, tsf + tsfdelta); + DPRINTF(sc, ATH_DEBUG_TDMA_TIMER, + "%s: calling ath_hal_adjusttsf: TSF=%llu, tsfdelta=%d\n", + __func__, + (unsigned long long) tsf, + tsfdelta); + +#ifdef ATH_DEBUG_ALQ + if (if_ath_alq_checkdebug(&sc->sc_alq, + ATH_ALQ_TDMA_TSF_ADJUST)) { + struct if_ath_alq_tdma_tsf_adjust t; + + t.tsfdelta = htobe32(tsfdelta); + t.tsf64_old = htobe64(tsf); + t.tsf64_new = htobe64(tsf + tsfdelta); + if_ath_alq_post(&sc->sc_alq, ATH_ALQ_TDMA_TSF_ADJUST, + sizeof(t), (char *) &t); + } +#endif /* ATH_DEBUG_ALQ */ + sc->sc_stats.ast_tdma_tsf++; + } + ath_tdma_beacon_send(sc, vap); /* prepare response */ +#undef TU_TO_TSF +#undef TSF_TO_TU +} + +/* + * Transmit a beacon frame at SWBA. Dynamic updates + * to the frame contents are done as needed. + */ +void +ath_tdma_beacon_send(struct ath_softc *sc, struct ieee80211vap *vap) +{ + struct ath_hal *ah = sc->sc_ah; + struct ath_buf *bf; + int otherant; + + /* + * Check if the previous beacon has gone out. If + * not don't try to post another, skip this period + * and wait for the next. Missed beacons indicate + * a problem and should not occur. If we miss too + * many consecutive beacons reset the device. + */ + if (ath_hal_numtxpending(ah, sc->sc_bhalq) != 0) { + sc->sc_bmisscount++; + DPRINTF(sc, ATH_DEBUG_BEACON, + "%s: missed %u consecutive beacons\n", + __func__, sc->sc_bmisscount); + if (sc->sc_bmisscount >= ath_bstuck_threshold) + taskqueue_enqueue(sc->sc_tq, &sc->sc_bstucktask); + return; + } + if (sc->sc_bmisscount != 0) { + DPRINTF(sc, ATH_DEBUG_BEACON, + "%s: resume beacon xmit after %u misses\n", + __func__, sc->sc_bmisscount); + sc->sc_bmisscount = 0; + } + + /* + * Check recent per-antenna transmit statistics and flip + * the default antenna if noticeably more frames went out + * on the non-default antenna. + * XXX assumes 2 anntenae + */ + if (!sc->sc_diversity) { + otherant = sc->sc_defant & 1 ? 2 : 1; + if (sc->sc_ant_tx[otherant] > sc->sc_ant_tx[sc->sc_defant] + 2) + ath_setdefantenna(sc, otherant); + sc->sc_ant_tx[1] = sc->sc_ant_tx[2] = 0; + } + + bf = ath_beacon_generate(sc, vap); + /* XXX We don't do cabq traffic, but just for completeness .. */ + ATH_TXQ_LOCK(sc->sc_cabq); + ath_beacon_cabq_start(sc); + ATH_TXQ_UNLOCK(sc->sc_cabq); + + if (bf != NULL) { + /* + * Stop any current dma and put the new frame on the queue. + * This should never fail since we check above that no frames + * are still pending on the queue. + */ + if ((! sc->sc_isedma) && + (! ath_hal_stoptxdma(ah, sc->sc_bhalq))) { + DPRINTF(sc, ATH_DEBUG_ANY, + "%s: beacon queue %u did not stop?\n", + __func__, sc->sc_bhalq); + /* NB: the HAL still stops DMA, so proceed */ + } + ath_hal_puttxbuf(ah, sc->sc_bhalq, bf->bf_daddr); + ath_hal_txstart(ah, sc->sc_bhalq); + + sc->sc_stats.ast_be_xmit++; /* XXX per-vap? */ + + /* + * Record local TSF for our last send for use + * in arbitrating slot collisions. + */ + /* XXX should take a locked ref to iv_bss */ + vap->iv_bss->ni_tstamp.tsf = ath_hal_gettsf64(ah); + } +} +#endif /* IEEE80211_SUPPORT_TDMA */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_tdma.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_tdma.h new file mode 100644 index 0000000000..fbb74e3147 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_tdma.h @@ -0,0 +1,55 @@ +/*- + * 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. + * + * $FreeBSD: releng/11.1/sys/dev/ath/if_ath_tdma.h 235679 2012-05-20 02:49:42Z adrian $ + */ +#ifndef __IF_ATH_TDMA_H__ +#define __IF_ATH_TDMA_H__ + +#define TDMA_EP_MULTIPLIER (1<<10) /* pow2 to optimize out * and / */ +#define TDMA_LPF_LEN 6 +#define TDMA_DUMMY_MARKER 0x127 +#define TDMA_EP_MUL(x, mul) ((x) * (mul)) +#define TDMA_IN(x) (TDMA_EP_MUL((x), TDMA_EP_MULTIPLIER)) +#define TDMA_LPF(x, y, len) \ + ((x != TDMA_DUMMY_MARKER) ? (((x) * ((len)-1) + (y)) / (len)) : (y)) +#define TDMA_SAMPLE(x, y) do { \ + x = TDMA_LPF((x), TDMA_IN(y), TDMA_LPF_LEN); \ +} while (0) +#define TDMA_EP_RND(x,mul) \ + ((((x)%(mul)) >= ((mul)/2)) ? \ + ((x) + ((mul) - 1)) / (mul) : (x)/(mul)) +#define TDMA_AVG(x) TDMA_EP_RND(x, TDMA_EP_MULTIPLIER) + +extern void ath_tdma_config(struct ath_softc *sc, struct ieee80211vap *vap); +extern void ath_tdma_update(struct ieee80211_node *ni, + const struct ieee80211_tdma_param *tdma, int changed); +extern void ath_tdma_beacon_send(struct ath_softc *sc, + struct ieee80211vap *vap); + +#endif diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_tsf.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_tsf.h new file mode 100644 index 0000000000..3ba79a93f8 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_tsf.h @@ -0,0 +1,81 @@ +/*- + * 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. + * + * $FreeBSD: releng/11.1/sys/dev/ath/if_ath_tsf.h 235676 2012-05-20 02:05:10Z adrian $ + */ +#ifndef __IF_ATH_TSF_H__ +#define __IF_ATH_TSF_H__ + +/* + * Extend 15-bit time stamp from rx descriptor to + * a full 64-bit TSF using the specified TSF. + */ +static __inline u_int64_t +ath_extend_tsf15(u_int32_t rstamp, u_int64_t tsf) +{ + if ((tsf & 0x7fff) < rstamp) + tsf -= 0x8000; + + return ((tsf &~ 0x7fff) | rstamp); +} + +/* + * Extend 32-bit time stamp from rx descriptor to + * a full 64-bit TSF using the specified TSF. + */ +static __inline u_int64_t +ath_extend_tsf32(u_int32_t rstamp, u_int64_t tsf) +{ + u_int32_t tsf_low = tsf & 0xffffffff; + u_int64_t tsf64 = (tsf & ~0xffffffffULL) | rstamp; + + if (rstamp > tsf_low && (rstamp - tsf_low > 0x10000000)) + tsf64 -= 0x100000000ULL; + + if (rstamp < tsf_low && (tsf_low - rstamp > 0x10000000)) + tsf64 += 0x100000000ULL; + + return tsf64; +} + +/* + * Extend the TSF from the RX descriptor to a full 64 bit TSF. + * Earlier hardware versions only wrote the low 15 bits of the + * TSF into the RX descriptor; later versions (AR5416 and up) + * include the 32 bit TSF value. + */ +static __inline u_int64_t +ath_extend_tsf(struct ath_softc *sc, u_int32_t rstamp, u_int64_t tsf) +{ + if (sc->sc_rxtsf32) + return ath_extend_tsf32(rstamp, tsf); + else + return ath_extend_tsf15(rstamp, tsf); +} + +#endif diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_tx.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_tx.c index 618c932e1e..ad19250d5b 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_tx.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_tx.c @@ -1,5 +1,6 @@ /*- * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting + * Copyright (c) 2010-2012 Adrian Chadd, Xenion Pty Ltd * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,7 +29,7 @@ */ #include -__FBSDID("$FreeBSD$"); +__FBSDID("$FreeBSD: releng/11.1/sys/dev/ath/if_ath_tx.c 301181 2016-06-02 00:51:36Z adrian $"); /* * Driver for the Atheros Wireless LAN controller. @@ -58,10 +59,12 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include +#include #include #include #include @@ -77,6 +80,7 @@ __FBSDID("$FreeBSD$"); #ifdef IEEE80211_SUPPORT_TDMA #include #endif +#include #include @@ -99,13 +103,138 @@ __FBSDID("$FreeBSD$"); #include #include +#ifdef ATH_DEBUG_ALQ +#include +#endif + +/* + * How many retries to perform in software + */ +#define SWMAX_RETRIES 10 + +/* + * What queue to throw the non-QoS TID traffic into + */ +#define ATH_NONQOS_TID_AC WME_AC_VO + +#if 0 +static int ath_tx_node_is_asleep(struct ath_softc *sc, struct ath_node *an); +#endif +static int ath_tx_ampdu_pending(struct ath_softc *sc, struct ath_node *an, + int tid); +static int ath_tx_ampdu_running(struct ath_softc *sc, struct ath_node *an, + int tid); +static ieee80211_seq ath_tx_tid_seqno_assign(struct ath_softc *sc, + struct ieee80211_node *ni, struct ath_buf *bf, struct mbuf *m0); +static int ath_tx_action_frame_override_queue(struct ath_softc *sc, + struct ieee80211_node *ni, struct mbuf *m0, int *tid); +static struct ath_buf * +ath_tx_retry_clone(struct ath_softc *sc, struct ath_node *an, + struct ath_tid *tid, struct ath_buf *bf); + +#ifdef ATH_DEBUG_ALQ +void +ath_tx_alq_post(struct ath_softc *sc, struct ath_buf *bf_first) +{ + struct ath_buf *bf; + int i, n; + const char *ds; + + /* XXX we should skip out early if debugging isn't enabled! */ + bf = bf_first; + + while (bf != NULL) { + /* XXX should ensure bf_nseg > 0! */ + if (bf->bf_nseg == 0) + break; + n = ((bf->bf_nseg - 1) / sc->sc_tx_nmaps) + 1; + for (i = 0, ds = (const char *) bf->bf_desc; + i < n; + i++, ds += sc->sc_tx_desclen) { + if_ath_alq_post(&sc->sc_alq, + ATH_ALQ_EDMA_TXDESC, + sc->sc_tx_desclen, + ds); + } + bf = bf->bf_next; + } +} +#endif /* ATH_DEBUG_ALQ */ + /* * Whether to use the 11n rate scenario functions or not */ static inline int ath_tx_is_11n(struct ath_softc *sc) { - return (sc->sc_ah->ah_magic == 0x20065416); + return ((sc->sc_ah->ah_magic == 0x20065416) || + (sc->sc_ah->ah_magic == 0x19741014)); +} + +/* + * Obtain the current TID from the given frame. + * + * Non-QoS frames need to go into TID 16 (IEEE80211_NONQOS_TID.) + * This has implications for which AC/priority the packet is placed + * in. + */ +static int +ath_tx_gettid(struct ath_softc *sc, const struct mbuf *m0) +{ + const struct ieee80211_frame *wh; + int pri = M_WME_GETAC(m0); + + wh = mtod(m0, const struct ieee80211_frame *); + if (! IEEE80211_QOS_HAS_SEQ(wh)) + return IEEE80211_NONQOS_TID; + else + return WME_AC_TO_TID(pri); +} + +static void +ath_tx_set_retry(struct ath_softc *sc, struct ath_buf *bf) +{ + struct ieee80211_frame *wh; + + wh = mtod(bf->bf_m, struct ieee80211_frame *); + /* Only update/resync if needed */ + if (bf->bf_state.bfs_isretried == 0) { + wh->i_fc[1] |= IEEE80211_FC1_RETRY; + bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, + BUS_DMASYNC_PREWRITE); + } + bf->bf_state.bfs_isretried = 1; + bf->bf_state.bfs_retries ++; +} + +/* + * Determine what the correct AC queue for the given frame + * should be. + * + * This code assumes that the TIDs map consistently to + * the underlying hardware (or software) ath_txq. + * Since the sender may try to set an AC which is + * arbitrary, non-QoS TIDs may end up being put on + * completely different ACs. There's no way to put a + * TID into multiple ath_txq's for scheduling, so + * for now we override the AC/TXQ selection and set + * non-QOS TID frames into the BE queue. + * + * This may be completely incorrect - specifically, + * some management frames may end up out of order + * compared to the QoS traffic they're controlling. + * I'll look into this later. + */ +static int +ath_tx_getac(struct ath_softc *sc, const struct mbuf *m0) +{ + const struct ieee80211_frame *wh; + int pri = M_WME_GETAC(m0); + wh = mtod(m0, const struct ieee80211_frame *); + if (IEEE80211_QOS_HAS_SEQ(wh)) + return pri; + + return ATH_NONQOS_TID_AC; } void @@ -116,10 +245,10 @@ ath_txfrag_cleanup(struct ath_softc *sc, ATH_TXBUF_LOCK_ASSERT(sc); - STAILQ_FOREACH_SAFE(bf, frags, bf_list, next) { + TAILQ_FOREACH_SAFE(bf, frags, bf_list, next) { /* NB: bf assumed clean */ - STAILQ_REMOVE_HEAD(frags, bf_list); - STAILQ_INSERT_HEAD(&sc->sc_txbuf, bf, bf_list); + TAILQ_REMOVE(frags, bf, bf_list); + ath_returnbuf_head(sc, bf); ieee80211_node_decref(ni); } } @@ -138,33 +267,20 @@ ath_txfrag_setup(struct ath_softc *sc, ath_bufhead *frags, ATH_TXBUF_LOCK(sc); for (m = m0->m_nextpkt; m != NULL; m = m->m_nextpkt) { - bf = _ath_getbuf_locked(sc); + /* XXX non-management? */ + bf = _ath_getbuf_locked(sc, ATH_BUFTYPE_NORMAL); if (bf == NULL) { /* out of buffers, cleanup */ + DPRINTF(sc, ATH_DEBUG_XMIT, "%s: no buffer?\n", + __func__); ath_txfrag_cleanup(sc, frags, ni); break; } ieee80211_node_incref(ni); - STAILQ_INSERT_TAIL(frags, bf, bf_list); + TAILQ_INSERT_TAIL(frags, bf, bf_list); } ATH_TXBUF_UNLOCK(sc); - return !STAILQ_EMPTY(frags); -} - -/* - * Reclaim mbuf resources. For fragmented frames we - * need to claim each frag chained with m_nextpkt. - */ -void -ath_freetx(struct mbuf *m) -{ - struct mbuf *next; - - do { - next = m->m_nextpkt; - m->m_nextpkt = NULL; - m_freem(m); - } while ((m = next) != NULL); + return !TAILQ_EMPTY(frags); } static int @@ -182,10 +298,10 @@ ath_tx_dmasetup(struct ath_softc *sc, struct ath_buf *bf, struct mbuf *m0) BUS_DMA_NOWAIT); if (error == EFBIG) { /* XXX packet requires too many descriptors */ - bf->bf_nseg = ATH_TXDESC+1; + bf->bf_nseg = ATH_MAX_SCATTER + 1; } else if (error != 0) { sc->sc_stats.ast_tx_busdma++; - ath_freetx(m0); + ieee80211_free_mbuf(m0); return error; } /* @@ -193,11 +309,11 @@ ath_tx_dmasetup(struct ath_softc *sc, struct ath_buf *bf, struct mbuf *m0) * require too many TX descriptors. We try to convert * the latter to a cluster. */ - if (bf->bf_nseg > ATH_TXDESC) { /* too many desc's, linearize */ + if (bf->bf_nseg > ATH_MAX_SCATTER) { /* too many desc's, linearize */ sc->sc_stats.ast_tx_linear++; - m = m_collapse(m0, M_NOWAIT, ATH_TXDESC); + m = m_collapse(m0, M_NOWAIT, ATH_MAX_SCATTER); if (m == NULL) { - ath_freetx(m0); + ieee80211_free_mbuf(m0); sc->sc_stats.ast_tx_nombuf++; return ENOMEM; } @@ -207,14 +323,14 @@ ath_tx_dmasetup(struct ath_softc *sc, struct ath_buf *bf, struct mbuf *m0) BUS_DMA_NOWAIT); if (error != 0) { sc->sc_stats.ast_tx_busdma++; - ath_freetx(m0); + ieee80211_free_mbuf(m0); return error; } - KASSERT(bf->bf_nseg <= ATH_TXDESC, + KASSERT(bf->bf_nseg <= ATH_MAX_SCATTER, ("too many segments after defrag; nseg %u", bf->bf_nseg)); } else if (bf->bf_nseg == 0) { /* null packet, discard */ sc->sc_stats.ast_tx_nodata++; - ath_freetx(m0); + ieee80211_free_mbuf(m0); return EIO; } DPRINTF(sc, ATH_DEBUG_XMIT, "%s: m %p len %u\n", @@ -225,44 +341,394 @@ ath_tx_dmasetup(struct ath_softc *sc, struct ath_buf *bf, struct mbuf *m0) return 0; } +/* + * Chain together segments+descriptors for a frame - 11n or otherwise. + * + * For aggregates, this is called on each frame in the aggregate. + */ static void -ath_tx_chaindesclist(struct ath_softc *sc, struct ath_txq *txq, struct ath_buf *bf) +ath_tx_chaindesclist(struct ath_softc *sc, struct ath_desc *ds0, + struct ath_buf *bf, int is_aggr, int is_first_subframe, + int is_last_subframe) { struct ath_hal *ah = sc->sc_ah; - struct ath_desc *ds, *ds0; - int i; + char *ds; + int i, bp, dsp; + HAL_DMA_ADDR bufAddrList[4]; + uint32_t segLenList[4]; + int numTxMaps = 1; + int isFirstDesc = 1; + + /* + * XXX There's txdma and txdma_mgmt; the descriptor + * sizes must match. + */ + struct ath_descdma *dd = &sc->sc_txdma; /* * Fillin the remainder of the descriptor info. */ - ds0 = ds = bf->bf_desc; - for (i = 0; i < bf->bf_nseg; i++, ds++) { - ds->ds_data = bf->bf_segs[i].ds_addr; - if (i == bf->bf_nseg - 1) - ds->ds_link = 0; - else - ds->ds_link = bf->bf_daddr + sizeof(*ds) * (i + 1); - ath_hal_filltxdesc(ah, ds - , bf->bf_segs[i].ds_len /* segment length */ - , i == 0 /* first segment */ - , i == bf->bf_nseg - 1 /* last segment */ - , ds0 /* first descriptor */ - ); - DPRINTF(sc, ATH_DEBUG_XMIT, - "%s: %d: %08x %08x %08x %08x %08x %08x\n", - __func__, i, ds->ds_link, ds->ds_data, - ds->ds_ctl0, ds->ds_ctl1, ds->ds_hw[0], ds->ds_hw[1]); - } + /* + * We need the number of TX data pointers in each descriptor. + * EDMA and later chips support 4 TX buffers per descriptor; + * previous chips just support one. + */ + numTxMaps = sc->sc_tx_nmaps; + + /* + * For EDMA and later chips ensure the TX map is fully populated + * before advancing to the next descriptor. + */ + ds = (char *) bf->bf_desc; + bp = dsp = 0; + bzero(bufAddrList, sizeof(bufAddrList)); + bzero(segLenList, sizeof(segLenList)); + for (i = 0; i < bf->bf_nseg; i++) { + bufAddrList[bp] = bf->bf_segs[i].ds_addr; + segLenList[bp] = bf->bf_segs[i].ds_len; + bp++; + + /* + * Go to the next segment if this isn't the last segment + * and there's space in the current TX map. + */ + if ((i != bf->bf_nseg - 1) && (bp < numTxMaps)) + continue; + + /* + * Last segment or we're out of buffer pointers. + */ + bp = 0; + + if (i == bf->bf_nseg - 1) + ath_hal_settxdesclink(ah, (struct ath_desc *) ds, 0); + else + ath_hal_settxdesclink(ah, (struct ath_desc *) ds, + bf->bf_daddr + dd->dd_descsize * (dsp + 1)); + + /* + * XXX This assumes that bfs_txq is the actual destination + * hardware queue at this point. It may not have been + * assigned, it may actually be pointing to the multicast + * software TXQ id. These must be fixed! + */ + ath_hal_filltxdesc(ah, (struct ath_desc *) ds + , bufAddrList + , segLenList + , bf->bf_descid /* XXX desc id */ + , bf->bf_state.bfs_tx_queue + , isFirstDesc /* first segment */ + , i == bf->bf_nseg - 1 /* last segment */ + , (struct ath_desc *) ds0 /* first descriptor */ + ); + + /* + * Make sure the 11n aggregate fields are cleared. + * + * XXX TODO: this doesn't need to be called for + * aggregate frames; as it'll be called on all + * sub-frames. Since the descriptors are in + * non-cacheable memory, this leads to some + * rather slow writes on MIPS/ARM platforms. + */ + if (ath_tx_is_11n(sc)) + ath_hal_clr11n_aggr(sc->sc_ah, (struct ath_desc *) ds); + + /* + * If 11n is enabled, set it up as if it's an aggregate + * frame. + */ + if (is_last_subframe) { + ath_hal_set11n_aggr_last(sc->sc_ah, + (struct ath_desc *) ds); + } else if (is_aggr) { + /* + * This clears the aggrlen field; so + * the caller needs to call set_aggr_first()! + * + * XXX TODO: don't call this for the first + * descriptor in the first frame in an + * aggregate! + */ + ath_hal_set11n_aggr_middle(sc->sc_ah, + (struct ath_desc *) ds, + bf->bf_state.bfs_ndelim); + } + isFirstDesc = 0; + bf->bf_lastds = (struct ath_desc *) ds; + + /* + * Don't forget to skip to the next descriptor. + */ + ds += sc->sc_tx_desclen; + dsp++; + + /* + * .. and don't forget to blank these out! + */ + bzero(bufAddrList, sizeof(bufAddrList)); + bzero(segLenList, sizeof(segLenList)); + } + bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE); } +/* + * Set the rate control fields in the given descriptor based on + * the bf_state fields and node state. + * + * The bfs fields should already be set with the relevant rate + * control information, including whether MRR is to be enabled. + * + * Since the FreeBSD HAL currently sets up the first TX rate + * in ath_hal_setuptxdesc(), this will setup the MRR + * conditionally for the pre-11n chips, and call ath_buf_set_rate + * unconditionally for 11n chips. These require the 11n rate + * scenario to be set if MCS rates are enabled, so it's easier + * to just always call it. The caller can then only set rates 2, 3 + * and 4 if multi-rate retry is needed. + */ static void -ath_tx_handoff(struct ath_softc *sc, struct ath_txq *txq, struct ath_buf *bf) +ath_tx_set_ratectrl(struct ath_softc *sc, struct ieee80211_node *ni, + struct ath_buf *bf) +{ + struct ath_rc_series *rc = bf->bf_state.bfs_rc; + + /* If mrr is disabled, blank tries 1, 2, 3 */ + if (! bf->bf_state.bfs_ismrr) + rc[1].tries = rc[2].tries = rc[3].tries = 0; + +#if 0 + /* + * If NOACK is set, just set ntries=1. + */ + else if (bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) { + rc[1].tries = rc[2].tries = rc[3].tries = 0; + rc[0].tries = 1; + } +#endif + + /* + * Always call - that way a retried descriptor will + * have the MRR fields overwritten. + * + * XXX TODO: see if this is really needed - setting up + * the first descriptor should set the MRR fields to 0 + * for us anyway. + */ + if (ath_tx_is_11n(sc)) { + ath_buf_set_rate(sc, ni, bf); + } else { + ath_hal_setupxtxdesc(sc->sc_ah, bf->bf_desc + , rc[1].ratecode, rc[1].tries + , rc[2].ratecode, rc[2].tries + , rc[3].ratecode, rc[3].tries + ); + } +} + +/* + * Setup segments+descriptors for an 11n aggregate. + * bf_first is the first buffer in the aggregate. + * The descriptor list must already been linked together using + * bf->bf_next. + */ +static void +ath_tx_setds_11n(struct ath_softc *sc, struct ath_buf *bf_first) +{ + struct ath_buf *bf, *bf_prev = NULL; + struct ath_desc *ds0 = bf_first->bf_desc; + + DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: nframes=%d, al=%d\n", + __func__, bf_first->bf_state.bfs_nframes, + bf_first->bf_state.bfs_al); + + bf = bf_first; + + if (bf->bf_state.bfs_txrate0 == 0) + DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: bf=%p, txrate0=%d\n", + __func__, bf, 0); + if (bf->bf_state.bfs_rc[0].ratecode == 0) + DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: bf=%p, rix0=%d\n", + __func__, bf, 0); + + /* + * Setup all descriptors of all subframes - this will + * call ath_hal_set11naggrmiddle() on every frame. + */ + while (bf != NULL) { + DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, + "%s: bf=%p, nseg=%d, pktlen=%d, seqno=%d\n", + __func__, bf, bf->bf_nseg, bf->bf_state.bfs_pktlen, + SEQNO(bf->bf_state.bfs_seqno)); + + /* + * Setup the initial fields for the first descriptor - all + * the non-11n specific stuff. + */ + ath_hal_setuptxdesc(sc->sc_ah, bf->bf_desc + , bf->bf_state.bfs_pktlen /* packet length */ + , bf->bf_state.bfs_hdrlen /* header length */ + , bf->bf_state.bfs_atype /* Atheros packet type */ + , bf->bf_state.bfs_txpower /* txpower */ + , bf->bf_state.bfs_txrate0 + , bf->bf_state.bfs_try0 /* series 0 rate/tries */ + , bf->bf_state.bfs_keyix /* key cache index */ + , bf->bf_state.bfs_txantenna /* antenna mode */ + , bf->bf_state.bfs_txflags | HAL_TXDESC_INTREQ /* flags */ + , bf->bf_state.bfs_ctsrate /* rts/cts rate */ + , bf->bf_state.bfs_ctsduration /* rts/cts duration */ + ); + + /* + * First descriptor? Setup the rate control and initial + * aggregate header information. + */ + if (bf == bf_first) { + /* + * setup first desc with rate and aggr info + */ + ath_tx_set_ratectrl(sc, bf->bf_node, bf); + } + + /* + * Setup the descriptors for a multi-descriptor frame. + * This is both aggregate and non-aggregate aware. + */ + ath_tx_chaindesclist(sc, ds0, bf, + 1, /* is_aggr */ + !! (bf == bf_first), /* is_first_subframe */ + !! (bf->bf_next == NULL) /* is_last_subframe */ + ); + + if (bf == bf_first) { + /* + * Initialise the first 11n aggregate with the + * aggregate length and aggregate enable bits. + */ + ath_hal_set11n_aggr_first(sc->sc_ah, + ds0, + bf->bf_state.bfs_al, + bf->bf_state.bfs_ndelim); + } + + /* + * Link the last descriptor of the previous frame + * to the beginning descriptor of this frame. + */ + if (bf_prev != NULL) + ath_hal_settxdesclink(sc->sc_ah, bf_prev->bf_lastds, + bf->bf_daddr); + + /* Save a copy so we can link the next descriptor in */ + bf_prev = bf; + bf = bf->bf_next; + } + + /* + * Set the first descriptor bf_lastds field to point to + * the last descriptor in the last subframe, that's where + * the status update will occur. + */ + bf_first->bf_lastds = bf_prev->bf_lastds; + + /* + * And bf_last in the first descriptor points to the end of + * the aggregate list. + */ + bf_first->bf_last = bf_prev; + + /* + * For non-AR9300 NICs, which require the rate control + * in the final descriptor - let's set that up now. + * + * This is because the filltxdesc() HAL call doesn't + * populate the last segment with rate control information + * if firstSeg is also true. For non-aggregate frames + * that is fine, as the first frame already has rate control + * info. But if the last frame in an aggregate has one + * descriptor, both firstseg and lastseg will be true and + * the rate info isn't copied. + * + * This is inefficient on MIPS/ARM platforms that have + * non-cachable memory for TX descriptors, but we'll just + * make do for now. + * + * As to why the rate table is stashed in the last descriptor + * rather than the first descriptor? Because proctxdesc() + * is called on the final descriptor in an MPDU or A-MPDU - + * ie, the one that gets updated by the hardware upon + * completion. That way proctxdesc() doesn't need to know + * about the first _and_ last TX descriptor. + */ + ath_hal_setuplasttxdesc(sc->sc_ah, bf_prev->bf_lastds, ds0); + + DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: end\n", __func__); +} + +/* + * Hand-off a frame to the multicast TX queue. + * + * This is a software TXQ which will be appended to the CAB queue + * during the beacon setup code. + * + * XXX TODO: since the AR9300 EDMA TX queue support wants the QCU ID + * as part of the TX descriptor, bf_state.bfs_tx_queue must be updated + * with the actual hardware txq, or all of this will fall apart. + * + * XXX It may not be a bad idea to just stuff the QCU ID into bf_state + * and retire bfs_tx_queue; then make sure the CABQ QCU ID is populated + * correctly. + */ +static void +ath_tx_handoff_mcast(struct ath_softc *sc, struct ath_txq *txq, + struct ath_buf *bf) +{ + ATH_TX_LOCK_ASSERT(sc); + + KASSERT((bf->bf_flags & ATH_BUF_BUSY) == 0, + ("%s: busy status 0x%x", __func__, bf->bf_flags)); + + /* + * Ensure that the tx queue is the cabq, so things get + * mapped correctly. + */ + if (bf->bf_state.bfs_tx_queue != sc->sc_cabq->axq_qnum) { + DPRINTF(sc, ATH_DEBUG_XMIT, + "%s: bf=%p, bfs_tx_queue=%d, axq_qnum=%d\n", + __func__, bf, bf->bf_state.bfs_tx_queue, + txq->axq_qnum); + } + + ATH_TXQ_LOCK(txq); + if (ATH_TXQ_LAST(txq, axq_q_s) != NULL) { + struct ath_buf *bf_last = ATH_TXQ_LAST(txq, axq_q_s); + struct ieee80211_frame *wh; + + /* mark previous frame */ + wh = mtod(bf_last->bf_m, struct ieee80211_frame *); + wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA; + bus_dmamap_sync(sc->sc_dmat, bf_last->bf_dmamap, + BUS_DMASYNC_PREWRITE); + + /* link descriptor */ + ath_hal_settxdesclink(sc->sc_ah, + bf_last->bf_lastds, + bf->bf_daddr); + } + ATH_TXQ_INSERT_TAIL(txq, bf, bf_list); + ATH_TXQ_UNLOCK(txq); +} + +/* + * Hand-off packet to a hardware queue. + */ +static void +ath_tx_handoff_hw(struct ath_softc *sc, struct ath_txq *txq, + struct ath_buf *bf) { struct ath_hal *ah = sc->sc_ah; - - /* Fill in the details in the descriptor list */ - ath_tx_chaindesclist(sc, txq, bf); + struct ath_buf *bf_first; /* * Insert the frame on the outbound list and pass it on @@ -272,107 +738,240 @@ ath_tx_handoff(struct ath_softc *sc, struct ath_txq *txq, struct ath_buf *bf) * the SWBA handler since frames only go out on DTIM and * to avoid possible races. */ - ATH_TXQ_LOCK(txq); + ATH_TX_LOCK_ASSERT(sc); KASSERT((bf->bf_flags & ATH_BUF_BUSY) == 0, - ("busy status 0x%x", bf->bf_flags)); - if (txq->axq_qnum != ATH_TXQ_SWQ) { -#ifdef IEEE80211_SUPPORT_TDMA - int qbusy; + ("%s: busy status 0x%x", __func__, bf->bf_flags)); + KASSERT(txq->axq_qnum != ATH_TXQ_SWQ, + ("ath_tx_handoff_hw called for mcast queue")); - ATH_TXQ_INSERT_TAIL(txq, bf, bf_list); - qbusy = ath_hal_txqenabled(ah, txq->axq_qnum); - if (txq->axq_link == NULL) { - /* - * Be careful writing the address to TXDP. If - * the tx q is enabled then this write will be - * ignored. Normally this is not an issue but - * when tdma is in use and the q is beacon gated - * this race can occur. If the q is busy then - * defer the work to later--either when another - * packet comes along or when we prepare a beacon - * frame at SWBA. - */ - if (!qbusy) { - ath_hal_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr); - txq->axq_flags &= ~ATH_TXQ_PUTPENDING; - DPRINTF(sc, ATH_DEBUG_XMIT, - "%s: TXDP[%u] = %p (%p) depth %d\n", - __func__, txq->axq_qnum, - (caddr_t)bf->bf_daddr, bf->bf_desc, - txq->axq_depth); - } else { - txq->axq_flags |= ATH_TXQ_PUTPENDING; - DPRINTF(sc, ATH_DEBUG_TDMA | ATH_DEBUG_XMIT, - "%s: Q%u busy, defer enable\n", __func__, - txq->axq_qnum); - } - } else { - *txq->axq_link = bf->bf_daddr; - DPRINTF(sc, ATH_DEBUG_XMIT, - "%s: link[%u](%p)=%p (%p) depth %d\n", __func__, - txq->axq_qnum, txq->axq_link, - (caddr_t)bf->bf_daddr, bf->bf_desc, txq->axq_depth); - if ((txq->axq_flags & ATH_TXQ_PUTPENDING) && !qbusy) { - /* - * The q was busy when we previously tried - * to write the address of the first buffer - * in the chain. Since it's not busy now - * handle this chore. We are certain the - * buffer at the front is the right one since - * axq_link is NULL only when the buffer list - * is/was empty. - */ - ath_hal_puttxbuf(ah, txq->axq_qnum, - STAILQ_FIRST(&txq->axq_q)->bf_daddr); - txq->axq_flags &= ~ATH_TXQ_PUTPENDING; - DPRINTF(sc, ATH_DEBUG_TDMA | ATH_DEBUG_XMIT, - "%s: Q%u restarted\n", __func__, - txq->axq_qnum); - } - } -#else - ATH_TXQ_INSERT_TAIL(txq, bf, bf_list); - if (txq->axq_link == NULL) { - ath_hal_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr); - DPRINTF(sc, ATH_DEBUG_XMIT, - "%s: TXDP[%u] = %p (%p) depth %d\n", - __func__, txq->axq_qnum, - (caddr_t)bf->bf_daddr, bf->bf_desc, - txq->axq_depth); - } else { - *txq->axq_link = bf->bf_daddr; - DPRINTF(sc, ATH_DEBUG_XMIT, - "%s: link[%u](%p)=%p (%p) depth %d\n", __func__, - txq->axq_qnum, txq->axq_link, - (caddr_t)bf->bf_daddr, bf->bf_desc, txq->axq_depth); - } -#endif /* IEEE80211_SUPPORT_TDMA */ - txq->axq_link = &bf->bf_desc[bf->bf_nseg - 1].ds_link; - ath_hal_txstart(ah, txq->axq_qnum); - } else { - if (txq->axq_link != NULL) { - struct ath_buf *last = ATH_TXQ_LAST(txq); - struct ieee80211_frame *wh; - - /* mark previous frame */ - wh = mtod(last->bf_m, struct ieee80211_frame *); - wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA; - bus_dmamap_sync(sc->sc_dmat, last->bf_dmamap, - BUS_DMASYNC_PREWRITE); - - /* link descriptor */ - *txq->axq_link = bf->bf_daddr; - } - ATH_TXQ_INSERT_TAIL(txq, bf, bf_list); - txq->axq_link = &bf->bf_desc[bf->bf_nseg - 1].ds_link; + /* + * XXX We should instead just verify that sc_txstart_cnt + * or ath_txproc_cnt > 0. That would mean that + * the reset is going to be waiting for us to complete. + */ + if (sc->sc_txproc_cnt == 0 && sc->sc_txstart_cnt == 0) { + device_printf(sc->sc_dev, + "%s: TX dispatch without holding txcount/txstart refcnt!\n", + __func__); } + + /* + * XXX .. this is going to cause the hardware to get upset; + * so we really should find some way to drop or queue + * things. + */ + + ATH_TXQ_LOCK(txq); + + /* + * XXX TODO: if there's a holdingbf, then + * ATH_TXQ_PUTRUNNING should be clear. + * + * If there is a holdingbf and the list is empty, + * then axq_link should be pointing to the holdingbf. + * + * Otherwise it should point to the last descriptor + * in the last ath_buf. + * + * In any case, we should really ensure that we + * update the previous descriptor link pointer to + * this descriptor, regardless of all of the above state. + * + * For now this is captured by having axq_link point + * to either the holdingbf (if the TXQ list is empty) + * or the end of the list (if the TXQ list isn't empty.) + * I'd rather just kill axq_link here and do it as above. + */ + + /* + * Append the frame to the TX queue. + */ + ATH_TXQ_INSERT_TAIL(txq, bf, bf_list); + ATH_KTR(sc, ATH_KTR_TX, 3, + "ath_tx_handoff: non-tdma: txq=%u, add bf=%p " + "depth=%d", + txq->axq_qnum, + bf, + txq->axq_depth); + + /* + * If there's a link pointer, update it. + * + * XXX we should replace this with the above logic, just + * to kill axq_link with fire. + */ + if (txq->axq_link != NULL) { + *txq->axq_link = bf->bf_daddr; + DPRINTF(sc, ATH_DEBUG_XMIT, + "%s: link[%u](%p)=%p (%p) depth %d\n", __func__, + txq->axq_qnum, txq->axq_link, + (caddr_t)bf->bf_daddr, bf->bf_desc, + txq->axq_depth); + ATH_KTR(sc, ATH_KTR_TX, 5, + "ath_tx_handoff: non-tdma: link[%u](%p)=%p (%p) " + "lastds=%d", + txq->axq_qnum, txq->axq_link, + (caddr_t)bf->bf_daddr, bf->bf_desc, + bf->bf_lastds); + } + + /* + * If we've not pushed anything into the hardware yet, + * push the head of the queue into the TxDP. + * + * Once we've started DMA, there's no guarantee that + * updating the TxDP with a new value will actually work. + * So we just don't do that - if we hit the end of the list, + * we keep that buffer around (the "holding buffer") and + * re-start DMA by updating the link pointer of _that_ + * descriptor and then restart DMA. + */ + if (! (txq->axq_flags & ATH_TXQ_PUTRUNNING)) { + bf_first = TAILQ_FIRST(&txq->axq_q); + txq->axq_flags |= ATH_TXQ_PUTRUNNING; + ath_hal_puttxbuf(ah, txq->axq_qnum, bf_first->bf_daddr); + DPRINTF(sc, ATH_DEBUG_XMIT, + "%s: TXDP[%u] = %p (%p) depth %d\n", + __func__, txq->axq_qnum, + (caddr_t)bf_first->bf_daddr, bf_first->bf_desc, + txq->axq_depth); + ATH_KTR(sc, ATH_KTR_TX, 5, + "ath_tx_handoff: TXDP[%u] = %p (%p) " + "lastds=%p depth %d", + txq->axq_qnum, + (caddr_t)bf_first->bf_daddr, bf_first->bf_desc, + bf_first->bf_lastds, + txq->axq_depth); + } + + /* + * Ensure that the bf TXQ matches this TXQ, so later + * checking and holding buffer manipulation is sane. + */ + if (bf->bf_state.bfs_tx_queue != txq->axq_qnum) { + DPRINTF(sc, ATH_DEBUG_XMIT, + "%s: bf=%p, bfs_tx_queue=%d, axq_qnum=%d\n", + __func__, bf, bf->bf_state.bfs_tx_queue, + txq->axq_qnum); + } + + /* + * Track aggregate queue depth. + */ + if (bf->bf_state.bfs_aggr) + txq->axq_aggr_depth++; + + /* + * Update the link pointer. + */ + ath_hal_gettxdesclinkptr(ah, bf->bf_lastds, &txq->axq_link); + + /* + * Start DMA. + * + * If we wrote a TxDP above, DMA will start from here. + * + * If DMA is running, it'll do nothing. + * + * If the DMA engine hit the end of the QCU list (ie LINK=NULL, + * or VEOL) then it stops at the last transmitted write. + * We then append a new frame by updating the link pointer + * in that descriptor and then kick TxE here; it will re-read + * that last descriptor and find the new descriptor to transmit. + * + * This is why we keep the holding descriptor around. + */ + ath_hal_txstart(ah, txq->axq_qnum); ATH_TXQ_UNLOCK(txq); + ATH_KTR(sc, ATH_KTR_TX, 1, + "ath_tx_handoff: txq=%u, txstart", txq->axq_qnum); +} + +/* + * Restart TX DMA for the given TXQ. + * + * This must be called whether the queue is empty or not. + */ +static void +ath_legacy_tx_dma_restart(struct ath_softc *sc, struct ath_txq *txq) +{ + struct ath_buf *bf, *bf_last; + + ATH_TXQ_LOCK_ASSERT(txq); + + /* XXX make this ATH_TXQ_FIRST */ + bf = TAILQ_FIRST(&txq->axq_q); + bf_last = ATH_TXQ_LAST(txq, axq_q_s); + + if (bf == NULL) + return; + + DPRINTF(sc, ATH_DEBUG_RESET, + "%s: Q%d: bf=%p, bf_last=%p, daddr=0x%08x\n", + __func__, + txq->axq_qnum, + bf, + bf_last, + (uint32_t) bf->bf_daddr); + +#ifdef ATH_DEBUG + if (sc->sc_debug & ATH_DEBUG_RESET) + ath_tx_dump(sc, txq); +#endif + + /* + * This is called from a restart, so DMA is known to be + * completely stopped. + */ + KASSERT((!(txq->axq_flags & ATH_TXQ_PUTRUNNING)), + ("%s: Q%d: called with PUTRUNNING=1\n", + __func__, + txq->axq_qnum)); + + ath_hal_puttxbuf(sc->sc_ah, txq->axq_qnum, bf->bf_daddr); + txq->axq_flags |= ATH_TXQ_PUTRUNNING; + + ath_hal_gettxdesclinkptr(sc->sc_ah, bf_last->bf_lastds, + &txq->axq_link); + ath_hal_txstart(sc->sc_ah, txq->axq_qnum); +} + +/* + * Hand off a packet to the hardware (or mcast queue.) + * + * The relevant hardware txq should be locked. + */ +static void +ath_legacy_xmit_handoff(struct ath_softc *sc, struct ath_txq *txq, + struct ath_buf *bf) +{ + ATH_TX_LOCK_ASSERT(sc); + +#ifdef ATH_DEBUG_ALQ + if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_EDMA_TXDESC)) + ath_tx_alq_post(sc, bf); +#endif + + if (txq->axq_qnum == ATH_TXQ_SWQ) + ath_tx_handoff_mcast(sc, txq, bf); + else + ath_tx_handoff_hw(sc, txq, bf); } static int ath_tx_tag_crypto(struct ath_softc *sc, struct ieee80211_node *ni, - struct mbuf *m0, int iswep, int isfrag, int *hdrlen, int *pktlen, int *keyix) + struct mbuf *m0, int iswep, int isfrag, int *hdrlen, int *pktlen, + int *keyix) { + DPRINTF(sc, ATH_DEBUG_XMIT, + "%s: hdrlen=%d, pktlen=%d, isfrag=%d, iswep=%d, m0=%p\n", + __func__, + *hdrlen, + *pktlen, + isfrag, + iswep, + m0); + if (iswep) { const struct ieee80211_cipher *cip; struct ieee80211_key *k; @@ -390,7 +989,7 @@ ath_tx_tag_crypto(struct ath_softc *sc, struct ieee80211_node *ni, * 802.11 layer counts failures and provides * debugging/diagnostics. */ - return 0; + return (0); } /* * Adjust the packet + header lengths for the crypto @@ -417,12 +1016,142 @@ ath_tx_tag_crypto(struct ath_softc *sc, struct ieee80211_node *ni, } else (*keyix) = HAL_TXKEYIX_INVALID; - return 1; + return (1); +} + +/* + * Calculate whether interoperability protection is required for + * this frame. + * + * This requires the rate control information be filled in, + * as the protection requirement depends upon the current + * operating mode / PHY. + */ +static void +ath_tx_calc_protection(struct ath_softc *sc, struct ath_buf *bf) +{ + struct ieee80211_frame *wh; + uint8_t rix; + uint16_t flags; + int shortPreamble; + const HAL_RATE_TABLE *rt = sc->sc_currates; + struct ieee80211com *ic = &sc->sc_ic; + + flags = bf->bf_state.bfs_txflags; + rix = bf->bf_state.bfs_rc[0].rix; + shortPreamble = bf->bf_state.bfs_shpream; + wh = mtod(bf->bf_m, struct ieee80211_frame *); + + /* + * If 802.11g protection is enabled, determine whether + * to use RTS/CTS or just CTS. Note that this is only + * done for OFDM unicast frames. + */ + if ((ic->ic_flags & IEEE80211_F_USEPROT) && + rt->info[rix].phy == IEEE80211_T_OFDM && + (flags & HAL_TXDESC_NOACK) == 0) { + bf->bf_state.bfs_doprot = 1; + /* XXX fragments must use CCK rates w/ protection */ + if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) { + flags |= HAL_TXDESC_RTSENA; + } else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) { + flags |= HAL_TXDESC_CTSENA; + } + /* + * For frags it would be desirable to use the + * highest CCK rate for RTS/CTS. But stations + * farther away may detect it at a lower CCK rate + * so use the configured protection rate instead + * (for now). + */ + sc->sc_stats.ast_tx_protect++; + } + + /* + * If 11n protection is enabled and it's a HT frame, + * enable RTS. + * + * XXX ic_htprotmode or ic_curhtprotmode? + * XXX should it_htprotmode only matter if ic_curhtprotmode + * XXX indicates it's not a HT pure environment? + */ + if ((ic->ic_htprotmode == IEEE80211_PROT_RTSCTS) && + rt->info[rix].phy == IEEE80211_T_HT && + (flags & HAL_TXDESC_NOACK) == 0) { + flags |= HAL_TXDESC_RTSENA; + sc->sc_stats.ast_tx_htprotect++; + } + bf->bf_state.bfs_txflags = flags; +} + +/* + * Update the frame duration given the currently selected rate. + * + * This also updates the frame duration value, so it will require + * a DMA flush. + */ +static void +ath_tx_calc_duration(struct ath_softc *sc, struct ath_buf *bf) +{ + struct ieee80211_frame *wh; + uint8_t rix; + uint16_t flags; + int shortPreamble; + struct ath_hal *ah = sc->sc_ah; + const HAL_RATE_TABLE *rt = sc->sc_currates; + int isfrag = bf->bf_m->m_flags & M_FRAG; + + flags = bf->bf_state.bfs_txflags; + rix = bf->bf_state.bfs_rc[0].rix; + shortPreamble = bf->bf_state.bfs_shpream; + wh = mtod(bf->bf_m, struct ieee80211_frame *); + + /* + * Calculate duration. This logically belongs in the 802.11 + * layer but it lacks sufficient information to calculate it. + */ + if ((flags & HAL_TXDESC_NOACK) == 0 && + (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_CTL) { + u_int16_t dur; + if (shortPreamble) + dur = rt->info[rix].spAckDuration; + else + dur = rt->info[rix].lpAckDuration; + if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) { + dur += dur; /* additional SIFS+ACK */ + /* + * Include the size of next fragment so NAV is + * updated properly. The last fragment uses only + * the ACK duration + * + * XXX TODO: ensure that the rate lookup for each + * fragment is the same as the rate used by the + * first fragment! + */ + dur += ath_hal_computetxtime(ah, + rt, + bf->bf_nextfraglen, + rix, shortPreamble); + } + if (isfrag) { + /* + * Force hardware to use computed duration for next + * fragment by disabling multi-rate retry which updates + * duration based on the multi-rate duration table. + */ + bf->bf_state.bfs_ismrr = 0; + bf->bf_state.bfs_try0 = ATH_TXMGTTRY; + /* XXX update bfs_rc[0].try? */ + } + + /* Update the duration field itself */ + *(u_int16_t *)wh->i_dur = htole16(dur); + } } static uint8_t ath_tx_get_rtscts_rate(struct ath_hal *ah, const HAL_RATE_TABLE *rt, - int rix, int cix, int shortPreamble) + int cix, int shortPreamble) { uint8_t ctsrate; @@ -439,10 +1168,9 @@ ath_tx_get_rtscts_rate(struct ath_hal *ah, const HAL_RATE_TABLE *rt, if (shortPreamble) ctsrate |= rt->info[cix].shortPreamble; - return ctsrate; + return (ctsrate); } - /* * Calculate the RTS/CTS duration for legacy frames. */ @@ -457,7 +1185,7 @@ ath_tx_calc_ctsduration(struct ath_hal *ah, int rix, int cix, if (rt->info[cix].phy == IEEE80211_T_HT) { printf("%s: HT rate where it shouldn't be (0x%x)\n", __func__, rt->info[cix].rateCode); - return -1; + return (-1); } /* @@ -485,39 +1213,346 @@ ath_tx_calc_ctsduration(struct ath_hal *ah, int rix, int cix, ctsduration += rt->info[rix].lpAckDuration; } - return ctsduration; + return (ctsduration); } -int -ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf, - struct mbuf *m0) +/* + * Update the given ath_buf with updated rts/cts setup and duration + * values. + * + * To support rate lookups for each software retry, the rts/cts rate + * and cts duration must be re-calculated. + * + * This function assumes the RTS/CTS flags have been set as needed; + * mrr has been disabled; and the rate control lookup has been done. + * + * XXX TODO: MRR need only be disabled for the pre-11n NICs. + * XXX The 11n NICs support per-rate RTS/CTS configuration. + */ +static void +ath_tx_set_rtscts(struct ath_softc *sc, struct ath_buf *bf) +{ + uint16_t ctsduration = 0; + uint8_t ctsrate = 0; + uint8_t rix = bf->bf_state.bfs_rc[0].rix; + uint8_t cix = 0; + const HAL_RATE_TABLE *rt = sc->sc_currates; + + /* + * No RTS/CTS enabled? Don't bother. + */ + if ((bf->bf_state.bfs_txflags & + (HAL_TXDESC_RTSENA | HAL_TXDESC_CTSENA)) == 0) { + /* XXX is this really needed? */ + bf->bf_state.bfs_ctsrate = 0; + bf->bf_state.bfs_ctsduration = 0; + return; + } + + /* + * If protection is enabled, use the protection rix control + * rate. Otherwise use the rate0 control rate. + */ + if (bf->bf_state.bfs_doprot) + rix = sc->sc_protrix; + else + rix = bf->bf_state.bfs_rc[0].rix; + + /* + * If the raw path has hard-coded ctsrate0 to something, + * use it. + */ + if (bf->bf_state.bfs_ctsrate0 != 0) + cix = ath_tx_findrix(sc, bf->bf_state.bfs_ctsrate0); + else + /* Control rate from above */ + cix = rt->info[rix].controlRate; + + /* Calculate the rtscts rate for the given cix */ + ctsrate = ath_tx_get_rtscts_rate(sc->sc_ah, rt, cix, + bf->bf_state.bfs_shpream); + + /* The 11n chipsets do ctsduration calculations for you */ + if (! ath_tx_is_11n(sc)) + ctsduration = ath_tx_calc_ctsduration(sc->sc_ah, rix, cix, + bf->bf_state.bfs_shpream, bf->bf_state.bfs_pktlen, + rt, bf->bf_state.bfs_txflags); + + /* Squirrel away in ath_buf */ + bf->bf_state.bfs_ctsrate = ctsrate; + bf->bf_state.bfs_ctsduration = ctsduration; + + /* + * Must disable multi-rate retry when using RTS/CTS. + */ + if (!sc->sc_mrrprot) { + bf->bf_state.bfs_ismrr = 0; + bf->bf_state.bfs_try0 = + bf->bf_state.bfs_rc[0].tries = ATH_TXMGTTRY; /* XXX ew */ + } +} + +/* + * Setup the descriptor chain for a normal or fast-frame + * frame. + * + * XXX TODO: extend to include the destination hardware QCU ID. + * Make sure that is correct. Make sure that when being added + * to the mcastq, the CABQ QCUID is set or things will get a bit + * odd. + */ +static void +ath_tx_setds(struct ath_softc *sc, struct ath_buf *bf) +{ + struct ath_desc *ds = bf->bf_desc; + struct ath_hal *ah = sc->sc_ah; + + if (bf->bf_state.bfs_txrate0 == 0) + DPRINTF(sc, ATH_DEBUG_XMIT, + "%s: bf=%p, txrate0=%d\n", __func__, bf, 0); + + ath_hal_setuptxdesc(ah, ds + , bf->bf_state.bfs_pktlen /* packet length */ + , bf->bf_state.bfs_hdrlen /* header length */ + , bf->bf_state.bfs_atype /* Atheros packet type */ + , bf->bf_state.bfs_txpower /* txpower */ + , bf->bf_state.bfs_txrate0 + , bf->bf_state.bfs_try0 /* series 0 rate/tries */ + , bf->bf_state.bfs_keyix /* key cache index */ + , bf->bf_state.bfs_txantenna /* antenna mode */ + , bf->bf_state.bfs_txflags /* flags */ + , bf->bf_state.bfs_ctsrate /* rts/cts rate */ + , bf->bf_state.bfs_ctsduration /* rts/cts duration */ + ); + + /* + * This will be overriden when the descriptor chain is written. + */ + bf->bf_lastds = ds; + bf->bf_last = bf; + + /* Set rate control and descriptor chain for this frame */ + ath_tx_set_ratectrl(sc, bf->bf_node, bf); + ath_tx_chaindesclist(sc, ds, bf, 0, 0, 0); +} + +/* + * Do a rate lookup. + * + * This performs a rate lookup for the given ath_buf only if it's required. + * Non-data frames and raw frames don't require it. + * + * This populates the primary and MRR entries; MRR values are + * then disabled later on if something requires it (eg RTS/CTS on + * pre-11n chipsets. + * + * This needs to be done before the RTS/CTS fields are calculated + * as they may depend upon the rate chosen. + */ +static void +ath_tx_do_ratelookup(struct ath_softc *sc, struct ath_buf *bf) +{ + uint8_t rate, rix; + int try0; + + if (! bf->bf_state.bfs_doratelookup) + return; + + /* Get rid of any previous state */ + bzero(bf->bf_state.bfs_rc, sizeof(bf->bf_state.bfs_rc)); + + ATH_NODE_LOCK(ATH_NODE(bf->bf_node)); + ath_rate_findrate(sc, ATH_NODE(bf->bf_node), bf->bf_state.bfs_shpream, + bf->bf_state.bfs_pktlen, &rix, &try0, &rate); + + /* In case MRR is disabled, make sure rc[0] is setup correctly */ + bf->bf_state.bfs_rc[0].rix = rix; + bf->bf_state.bfs_rc[0].ratecode = rate; + bf->bf_state.bfs_rc[0].tries = try0; + + if (bf->bf_state.bfs_ismrr && try0 != ATH_TXMAXTRY) + ath_rate_getxtxrates(sc, ATH_NODE(bf->bf_node), rix, + bf->bf_state.bfs_rc); + ATH_NODE_UNLOCK(ATH_NODE(bf->bf_node)); + + sc->sc_txrix = rix; /* for LED blinking */ + sc->sc_lastdatarix = rix; /* for fast frames */ + bf->bf_state.bfs_try0 = try0; + bf->bf_state.bfs_txrate0 = rate; +} + +/* + * Update the CLRDMASK bit in the ath_buf if it needs to be set. + */ +static void +ath_tx_update_clrdmask(struct ath_softc *sc, struct ath_tid *tid, + struct ath_buf *bf) +{ + struct ath_node *an = ATH_NODE(bf->bf_node); + + ATH_TX_LOCK_ASSERT(sc); + + if (an->clrdmask == 1) { + bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK; + an->clrdmask = 0; + } +} + +/* + * Return whether this frame should be software queued or + * direct dispatched. + * + * When doing powersave, BAR frames should be queued but other management + * frames should be directly sent. + * + * When not doing powersave, stick BAR frames into the hardware queue + * so it goes out even though the queue is paused. + * + * For now, management frames are also software queued by default. + */ +static int +ath_tx_should_swq_frame(struct ath_softc *sc, struct ath_node *an, + struct mbuf *m0, int *queue_to_head) +{ + struct ieee80211_node *ni = &an->an_node; + struct ieee80211_frame *wh; + uint8_t type, subtype; + + wh = mtod(m0, struct ieee80211_frame *); + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; + + (*queue_to_head) = 0; + + /* If it's not in powersave - direct-dispatch BAR */ + if ((ATH_NODE(ni)->an_is_powersave == 0) + && type == IEEE80211_FC0_TYPE_CTL && + subtype == IEEE80211_FC0_SUBTYPE_BAR) { + DPRINTF(sc, ATH_DEBUG_SW_TX, + "%s: BAR: TX'ing direct\n", __func__); + return (0); + } else if ((ATH_NODE(ni)->an_is_powersave == 1) + && type == IEEE80211_FC0_TYPE_CTL && + subtype == IEEE80211_FC0_SUBTYPE_BAR) { + /* BAR TX whilst asleep; queue */ + DPRINTF(sc, ATH_DEBUG_SW_TX, + "%s: swq: TX'ing\n", __func__); + (*queue_to_head) = 1; + return (1); + } else if ((ATH_NODE(ni)->an_is_powersave == 1) + && (type == IEEE80211_FC0_TYPE_MGT || + type == IEEE80211_FC0_TYPE_CTL)) { + /* + * Other control/mgmt frame; bypass software queuing + * for now! + */ + DPRINTF(sc, ATH_DEBUG_XMIT, + "%s: %6D: Node is asleep; sending mgmt " + "(type=%d, subtype=%d)\n", + __func__, ni->ni_macaddr, ":", type, subtype); + return (0); + } else { + return (1); + } +} + + +/* + * Transmit the given frame to the hardware. + * + * The frame must already be setup; rate control must already have + * been done. + * + * XXX since the TXQ lock is being held here (and I dislike holding + * it for this long when not doing software aggregation), later on + * break this function into "setup_normal" and "xmit_normal". The + * lock only needs to be held for the ath_tx_handoff call. + * + * XXX we don't update the leak count here - if we're doing + * direct frame dispatch, we need to be able to do it without + * decrementing the leak count (eg multicast queue frames.) + */ +static void +ath_tx_xmit_normal(struct ath_softc *sc, struct ath_txq *txq, + struct ath_buf *bf) +{ + struct ath_node *an = ATH_NODE(bf->bf_node); + struct ath_tid *tid = &an->an_tid[bf->bf_state.bfs_tid]; + + ATH_TX_LOCK_ASSERT(sc); + + /* + * For now, just enable CLRDMASK. ath_tx_xmit_normal() does + * set a completion handler however it doesn't (yet) properly + * handle the strict ordering requirements needed for normal, + * non-aggregate session frames. + * + * Once this is implemented, only set CLRDMASK like this for + * frames that must go out - eg management/raw frames. + */ + bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK; + + /* Setup the descriptor before handoff */ + ath_tx_do_ratelookup(sc, bf); + ath_tx_calc_duration(sc, bf); + ath_tx_calc_protection(sc, bf); + ath_tx_set_rtscts(sc, bf); + ath_tx_rate_fill_rcflags(sc, bf); + ath_tx_setds(sc, bf); + + /* Track per-TID hardware queue depth correctly */ + tid->hwq_depth++; + + /* Assign the completion handler */ + bf->bf_comp = ath_tx_normal_comp; + + /* Hand off to hardware */ + ath_tx_handoff(sc, txq, bf); +} + +/* + * Do the basic frame setup stuff that's required before the frame + * is added to a software queue. + * + * All frames get mostly the same treatment and it's done once. + * Retransmits fiddle with things like the rate control setup, + * setting the retransmit bit in the packet; doing relevant DMA/bus + * syncing and relinking it (back) into the hardware TX queue. + * + * Note that this may cause the mbuf to be reallocated, so + * m0 may not be valid. + */ +static int +ath_tx_normal_setup(struct ath_softc *sc, struct ieee80211_node *ni, + struct ath_buf *bf, struct mbuf *m0, struct ath_txq *txq) { struct ieee80211vap *vap = ni->ni_vap; - struct ath_vap *avp = ATH_VAP(vap); struct ath_hal *ah = sc->sc_ah; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211com *ic = &sc->sc_ic; const struct chanAccParams *cap = &ic->ic_wme.wme_chanParams; int error, iswep, ismcast, isfrag, ismrr; - int keyix, hdrlen, pktlen, try0; - u_int8_t rix, txrate, ctsrate; - u_int8_t cix = 0xff; /* NB: silence compiler */ + int keyix, hdrlen, pktlen, try0 = 0; + u_int8_t rix = 0, txrate = 0; struct ath_desc *ds; - struct ath_txq *txq; struct ieee80211_frame *wh; - u_int subtype, flags, ctsduration; + u_int subtype, flags; HAL_PKT_TYPE atype; const HAL_RATE_TABLE *rt; HAL_BOOL shortPreamble; struct ath_node *an; u_int pri; - uint8_t try[4], rate[4]; - bzero(try, sizeof(try)); - bzero(rate, sizeof(rate)); + /* + * To ensure that both sequence numbers and the CCMP PN handling + * is "correct", make sure that the relevant TID queue is locked. + * Otherwise the CCMP PN and seqno may appear out of order, causing + * re-ordered frames to have out of order CCMP PN's, resulting + * in many, many frame drops. + */ + ATH_TX_LOCK_ASSERT(sc); wh = mtod(m0, struct ieee80211_frame *); - iswep = wh->i_fc[1] & IEEE80211_FC1_WEP; + iswep = wh->i_fc[1] & IEEE80211_FC1_PROTECTED; ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); isfrag = m0->m_flags & M_FRAG; hdrlen = ieee80211_anyhdrsize(wh); @@ -528,8 +1563,9 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf pktlen = m0->m_pkthdr.len - (hdrlen & 3); /* Handle encryption twiddling if needed */ - if (! ath_tx_tag_crypto(sc, ni, m0, iswep, isfrag, &hdrlen, &pktlen, &keyix)) { - ath_freetx(m0); + if (! ath_tx_tag_crypto(sc, ni, m0, iswep, isfrag, &hdrlen, + &pktlen, &keyix)) { + ieee80211_free_mbuf(m0); return EIO; } @@ -545,6 +1581,7 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf error = ath_tx_dmasetup(sc, bf, m0); if (error != 0) return error; + KASSERT((ni != NULL), ("%s: ni=NULL!", __func__)); bf->bf_node = ni; /* NB: held reference */ m0 = bf->bf_m; /* NB: may have changed */ wh = mtod(m0, struct ieee80211_frame *); @@ -568,7 +1605,8 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf } an = ATH_NODE(ni); - flags = HAL_TXDESC_CLRDMASK; /* XXX needed for crypto errs */ + //flags = HAL_TXDESC_CLRDMASK; /* XXX needed for crypto errs */ + flags = 0; ismrr = 0; /* default no multi-rate retry*/ pri = M_WME_GETAC(m0); /* honor classification */ /* XXX use txparams instead of fixed values */ @@ -624,33 +1662,58 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf txrate |= rt->info[rix].shortPreamble; try0 = ATH_TXMAXTRY; /* XXX?too many? */ } else { - ath_rate_findrate(sc, an, shortPreamble, pktlen, - &rix, &try0, &txrate); - sc->sc_txrix = rix; /* for LED blinking */ - sc->sc_lastdatarix = rix; /* for fast frames */ - if (try0 != ATH_TXMAXTRY) - ismrr = 1; + /* + * Do rate lookup on each TX, rather than using + * the hard-coded TX information decided here. + */ + ismrr = 1; + bf->bf_state.bfs_doratelookup = 1; } if (cap->cap_wmeParams[pri].wmep_noackPolicy) flags |= HAL_TXDESC_NOACK; break; default: - if_printf(ifp, "bogus frame type 0x%x (%s)\n", - wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK, __func__); + device_printf(sc->sc_dev, "bogus frame type 0x%x (%s)\n", + wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK, __func__); /* XXX statistic */ - ath_freetx(m0); + /* XXX free tx dmamap */ + ieee80211_free_mbuf(m0); return EIO; } - txq = sc->sc_ac2q[pri]; /* - * When servicing one or more stations in power-save mode - * (or) if there is some mcast data waiting on the mcast - * queue (to prevent out of order delivery) multicast - * frames must be buffered until after the beacon. + * There are two known scenarios where the frame AC doesn't match + * what the destination TXQ is. + * + * + non-QoS frames (eg management?) that the net80211 stack has + * assigned a higher AC to, but since it's a non-QoS TID, it's + * being thrown into TID 16. TID 16 gets the AC_BE queue. + * It's quite possible that management frames should just be + * direct dispatched to hardware rather than go via the software + * queue; that should be investigated in the future. There are + * some specific scenarios where this doesn't make sense, mostly + * surrounding ADDBA request/response - hence why that is special + * cased. + * + * + Multicast frames going into the VAP mcast queue. That shows up + * as "TXQ 11". + * + * This driver should eventually support separate TID and TXQ locking, + * allowing for arbitrary AC frames to appear on arbitrary software + * queues, being queued to the "correct" hardware queue when needed. */ - if (ismcast && (vap->iv_ps_sta || avp->av_mcastq.axq_depth)) - txq = &avp->av_mcastq; +#if 0 + if (txq != sc->sc_ac2q[pri]) { + DPRINTF(sc, ATH_DEBUG_XMIT, + "%s: txq=%p (%d), pri=%d, pri txq=%p (%d)\n", + __func__, + txq, + txq->axq_qnum, + pri, + sc->sc_ac2q[pri], + sc->sc_ac2q[pri]->axq_qnum); + } +#endif /* * Calculate miscellaneous flags. @@ -660,7 +1723,6 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf } else if (pktlen > vap->iv_rtsthreshold && (ni->ni_ath_flags & IEEE80211_NODE_FF) == 0) { flags |= HAL_TXDESC_RTSENA; /* RTS based on frame length */ - cix = rt->info[rix].controlRate; sc->sc_stats.ast_tx_rts++; } if (flags & HAL_TXDESC_NOACK) /* NB: avoid double counting */ @@ -670,137 +1732,21 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf DPRINTF(sc, ATH_DEBUG_TDMA, "%s: discard frame, ACK required w/ TDMA\n", __func__); sc->sc_stats.ast_tdma_ack++; - ath_freetx(m0); + /* XXX free tx dmamap */ + ieee80211_free_mbuf(m0); return EIO; } #endif - /* - * If 802.11g protection is enabled, determine whether - * to use RTS/CTS or just CTS. Note that this is only - * done for OFDM unicast frames. - */ - if ((ic->ic_flags & IEEE80211_F_USEPROT) && - rt->info[rix].phy == IEEE80211_T_OFDM && - (flags & HAL_TXDESC_NOACK) == 0) { - /* XXX fragments must use CCK rates w/ protection */ - if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) - flags |= HAL_TXDESC_RTSENA; - else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) - flags |= HAL_TXDESC_CTSENA; - if (isfrag) { - /* - * For frags it would be desirable to use the - * highest CCK rate for RTS/CTS. But stations - * farther away may detect it at a lower CCK rate - * so use the configured protection rate instead - * (for now). - */ - cix = rt->info[sc->sc_protrix].controlRate; - } else - cix = rt->info[sc->sc_protrix].controlRate; - sc->sc_stats.ast_tx_protect++; - } - #if 0 /* - * If 11n protection is enabled and it's a HT frame, - * enable RTS. - * - * XXX ic_htprotmode or ic_curhtprotmode? - * XXX should it_htprotmode only matter if ic_curhtprotmode - * XXX indicates it's not a HT pure environment? + * Placeholder: if you want to transmit with the azimuth + * timestamp in the end of the payload, here's where you + * should set the TXDESC field. */ - if ((ic->ic_htprotmode == IEEE80211_PROT_RTSCTS) && - rt->info[rix].phy == IEEE80211_T_HT && - (flags & HAL_TXDESC_NOACK) == 0) { - cix = rt->info[sc->sc_protrix].controlRate; - flags |= HAL_TXDESC_RTSENA; - sc->sc_stats.ast_tx_htprotect++; - } + flags |= HAL_TXDESC_HWTS; #endif - /* - * Calculate duration. This logically belongs in the 802.11 - * layer but it lacks sufficient information to calculate it. - */ - if ((flags & HAL_TXDESC_NOACK) == 0 && - (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_CTL) { - u_int16_t dur; - if (shortPreamble) - dur = rt->info[rix].spAckDuration; - else - dur = rt->info[rix].lpAckDuration; - if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) { - dur += dur; /* additional SIFS+ACK */ - KASSERT(m0->m_nextpkt != NULL, ("no fragment")); - /* - * Include the size of next fragment so NAV is - * updated properly. The last fragment uses only - * the ACK duration - */ - dur += ath_hal_computetxtime(ah, rt, - m0->m_nextpkt->m_pkthdr.len, - rix, shortPreamble); - } - if (isfrag) { - /* - * Force hardware to use computed duration for next - * fragment by disabling multi-rate retry which updates - * duration based on the multi-rate duration table. - */ - ismrr = 0; - try0 = ATH_TXMGTTRY; /* XXX? */ - } - *(u_int16_t *)wh->i_dur = htole16(dur); - } - - /* - * Calculate RTS/CTS rate and duration if needed. - */ - ctsduration = 0; - if (flags & (HAL_TXDESC_RTSENA|HAL_TXDESC_CTSENA)) { - ctsrate = ath_tx_get_rtscts_rate(ah, rt, rix, cix, shortPreamble); - - /* The 11n chipsets do ctsduration calculations for you */ - if (! ath_tx_is_11n(sc)) - ctsduration = ath_tx_calc_ctsduration(ah, rix, cix, shortPreamble, - pktlen, rt, flags); - /* - * Must disable multi-rate retry when using RTS/CTS. - */ - ismrr = 0; - try0 = ATH_TXMGTTRY; /* XXX */ - } else - ctsrate = 0; - - /* - * At this point we are committed to sending the frame - * and we don't need to look at m_nextpkt; clear it in - * case this frame is part of frag chain. - */ - m0->m_nextpkt = NULL; - - if (IFF_DUMPPKTS(sc, ATH_DEBUG_XMIT)) - ieee80211_dump_pkt(ic, mtod(m0, const uint8_t *), m0->m_len, - sc->sc_hwmap[rix].ieeerate, -1); - - if (ieee80211_radiotap_active_vap(vap)) { - u_int64_t tsf = ath_hal_gettsf64(ah); - - sc->sc_tx_th.wt_tsf = htole64(tsf); - sc->sc_tx_th.wt_flags = sc->sc_hwmap[rix].txflags; - if (iswep) - sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_WEP; - if (isfrag) - sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_FRAG; - sc->sc_tx_th.wt_rate = sc->sc_hwmap[rix].ieeerate; - sc->sc_tx_th.wt_txpower = ni->ni_txpower; - sc->sc_tx_th.wt_antenna = sc->sc_txantenna; - - ieee80211_radiotap_tx(vap, m0); - } - /* * Determine if a tx interrupt should be generated for * this descriptor. We take a tx interrupt to reap @@ -823,49 +1769,294 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf txq->axq_intrcnt = 0; } - if (ath_tx_is_11n(sc)) { - rate[0] = rix; - try[0] = try0; + /* This point forward is actual TX bits */ + + /* + * At this point we are committed to sending the frame + * and we don't need to look at m_nextpkt; clear it in + * case this frame is part of frag chain. + */ + m0->m_nextpkt = NULL; + + if (IFF_DUMPPKTS(sc, ATH_DEBUG_XMIT)) + ieee80211_dump_pkt(ic, mtod(m0, const uint8_t *), m0->m_len, + sc->sc_hwmap[rix].ieeerate, -1); + + if (ieee80211_radiotap_active_vap(vap)) { + u_int64_t tsf = ath_hal_gettsf64(ah); + + sc->sc_tx_th.wt_tsf = htole64(tsf); + sc->sc_tx_th.wt_flags = sc->sc_hwmap[rix].txflags; + if (iswep) + sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_WEP; + if (isfrag) + sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_FRAG; + sc->sc_tx_th.wt_rate = sc->sc_hwmap[rix].ieeerate; + sc->sc_tx_th.wt_txpower = ieee80211_get_node_txpower(ni); + sc->sc_tx_th.wt_antenna = sc->sc_txantenna; + + ieee80211_radiotap_tx(vap, m0); + } + + /* Blank the legacy rate array */ + bzero(&bf->bf_state.bfs_rc, sizeof(bf->bf_state.bfs_rc)); + + /* + * ath_buf_set_rate needs at least one rate/try to setup + * the rate scenario. + */ + bf->bf_state.bfs_rc[0].rix = rix; + bf->bf_state.bfs_rc[0].tries = try0; + bf->bf_state.bfs_rc[0].ratecode = txrate; + + /* Store the decided rate index values away */ + bf->bf_state.bfs_pktlen = pktlen; + bf->bf_state.bfs_hdrlen = hdrlen; + bf->bf_state.bfs_atype = atype; + bf->bf_state.bfs_txpower = ieee80211_get_node_txpower(ni); + bf->bf_state.bfs_txrate0 = txrate; + bf->bf_state.bfs_try0 = try0; + bf->bf_state.bfs_keyix = keyix; + bf->bf_state.bfs_txantenna = sc->sc_txantenna; + bf->bf_state.bfs_txflags = flags; + bf->bf_state.bfs_shpream = shortPreamble; + + /* XXX this should be done in ath_tx_setrate() */ + bf->bf_state.bfs_ctsrate0 = 0; /* ie, no hard-coded ctsrate */ + bf->bf_state.bfs_ctsrate = 0; /* calculated later */ + bf->bf_state.bfs_ctsduration = 0; + bf->bf_state.bfs_ismrr = ismrr; + + return 0; +} + +/* + * Queue a frame to the hardware or software queue. + * + * This can be called by the net80211 code. + * + * XXX what about locking? Or, push the seqno assign into the + * XXX aggregate scheduler so its serialised? + * + * XXX When sending management frames via ath_raw_xmit(), + * should CLRDMASK be set unconditionally? + */ +int +ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, + struct ath_buf *bf, struct mbuf *m0) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ath_vap *avp = ATH_VAP(vap); + int r = 0; + u_int pri; + int tid; + struct ath_txq *txq; + int ismcast; + const struct ieee80211_frame *wh; + int is_ampdu, is_ampdu_tx, is_ampdu_pending; + ieee80211_seq seqno; + uint8_t type, subtype; + int queue_to_head; + + ATH_TX_LOCK_ASSERT(sc); + + /* + * Determine the target hardware queue. + * + * For multicast frames, the txq gets overridden appropriately + * depending upon the state of PS. + * + * For any other frame, we do a TID/QoS lookup inside the frame + * to see what the TID should be. If it's a non-QoS frame, the + * AC and TID are overridden. The TID/TXQ code assumes the + * TID is on a predictable hardware TXQ, so we don't support + * having a node TID queued to multiple hardware TXQs. + * This may change in the future but would require some locking + * fudgery. + */ + pri = ath_tx_getac(sc, m0); + tid = ath_tx_gettid(sc, m0); + + txq = sc->sc_ac2q[pri]; + wh = mtod(m0, struct ieee80211_frame *); + ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; + + /* + * Enforce how deep the multicast queue can grow. + * + * XXX duplicated in ath_raw_xmit(). + */ + if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { + if (sc->sc_cabq->axq_depth + sc->sc_cabq->fifo.axq_depth + > sc->sc_txq_mcastq_maxdepth) { + sc->sc_stats.ast_tx_mcastq_overflow++; + m_freem(m0); + return (ENOBUFS); + } } /* - * Formulate first tx descriptor with tx controls. + * Enforce how deep the unicast queue can grow. + * + * If the node is in power save then we don't want + * the software queue to grow too deep, or a node may + * end up consuming all of the ath_buf entries. + * + * For now, only do this for DATA frames. + * + * We will want to cap how many management/control + * frames get punted to the software queue so it doesn't + * fill up. But the correct solution isn't yet obvious. + * In any case, this check should at least let frames pass + * that we are direct-dispatching. + * + * XXX TODO: duplicate this to the raw xmit path! */ - /* XXX check return value? */ - /* XXX is this ok to call for 11n descriptors? */ - /* XXX or should it go through the first, next, last 11n calls? */ - ath_hal_setuptxdesc(ah, ds - , pktlen /* packet length */ - , hdrlen /* header length */ - , atype /* Atheros packet type */ - , ni->ni_txpower /* txpower */ - , txrate, try0 /* series 0 rate/tries */ - , keyix /* key cache index */ - , sc->sc_txantenna /* antenna mode */ - , flags /* flags */ - , ctsrate /* rts/cts rate */ - , ctsduration /* rts/cts duration */ - ); - bf->bf_txflags = flags; + if (type == IEEE80211_FC0_TYPE_DATA && + ATH_NODE(ni)->an_is_powersave && + ATH_NODE(ni)->an_swq_depth > + sc->sc_txq_node_psq_maxdepth) { + sc->sc_stats.ast_tx_node_psq_overflow++; + m_freem(m0); + return (ENOBUFS); + } + + /* A-MPDU TX */ + is_ampdu_tx = ath_tx_ampdu_running(sc, ATH_NODE(ni), tid); + is_ampdu_pending = ath_tx_ampdu_pending(sc, ATH_NODE(ni), tid); + is_ampdu = is_ampdu_tx | is_ampdu_pending; + + DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: tid=%d, ac=%d, is_ampdu=%d\n", + __func__, tid, pri, is_ampdu); + + /* Set local packet state, used to queue packets to hardware */ + bf->bf_state.bfs_tid = tid; + bf->bf_state.bfs_tx_queue = txq->axq_qnum; + bf->bf_state.bfs_pri = pri; + +#if 1 /* - * Setup the multi-rate retry state only when we're - * going to use it. This assumes ath_hal_setuptxdesc - * initializes the descriptors (so we don't have to) - * when the hardware supports multi-rate retry and - * we don't use it. + * When servicing one or more stations in power-save mode + * (or) if there is some mcast data waiting on the mcast + * queue (to prevent out of order delivery) multicast frames + * must be bufferd until after the beacon. + * + * TODO: we should lock the mcastq before we check the length. */ - if (ismrr) { - if (ath_tx_is_11n(sc)) - ath_rate_getxtxrates(sc, an, rix, rate, try); - else - ath_rate_setupxtxdesc(sc, an, ds, shortPreamble, rix); - } + if (sc->sc_cabq_enable && ismcast && (vap->iv_ps_sta || avp->av_mcastq.axq_depth)) { + txq = &avp->av_mcastq; + /* + * Mark the frame as eventually belonging on the CAB + * queue, so the descriptor setup functions will + * correctly initialise the descriptor 'qcuId' field. + */ + bf->bf_state.bfs_tx_queue = sc->sc_cabq->axq_qnum; + } +#endif - if (ath_tx_is_11n(sc)) { - ath_buf_set_rate(sc, ni, bf, pktlen, flags, ctsrate, (atype == HAL_PKT_TYPE_PSPOLL), rate, try); - } + /* Do the generic frame setup */ + /* XXX should just bzero the bf_state? */ + bf->bf_state.bfs_dobaw = 0; - ath_tx_handoff(sc, txq, bf); + /* A-MPDU TX? Manually set sequence number */ + /* + * Don't do it whilst pending; the net80211 layer still + * assigns them. + */ + if (is_ampdu_tx) { + /* + * Always call; this function will + * handle making sure that null data frames + * don't get a sequence number from the current + * TID and thus mess with the BAW. + */ + seqno = ath_tx_tid_seqno_assign(sc, ni, bf, m0); + + /* + * Don't add QoS NULL frames to the BAW. + */ + if (IEEE80211_QOS_HAS_SEQ(wh) && + subtype != IEEE80211_FC0_SUBTYPE_QOS_NULL) { + bf->bf_state.bfs_dobaw = 1; + } + } + + /* + * If needed, the sequence number has been assigned. + * Squirrel it away somewhere easy to get to. + */ + bf->bf_state.bfs_seqno = M_SEQNO_GET(m0) << IEEE80211_SEQ_SEQ_SHIFT; + + /* Is ampdu pending? fetch the seqno and print it out */ + if (is_ampdu_pending) + DPRINTF(sc, ATH_DEBUG_SW_TX, + "%s: tid %d: ampdu pending, seqno %d\n", + __func__, tid, M_SEQNO_GET(m0)); + + /* This also sets up the DMA map */ + r = ath_tx_normal_setup(sc, ni, bf, m0, txq); + + if (r != 0) + goto done; + + /* At this point m0 could have changed! */ + m0 = bf->bf_m; + +#if 1 + /* + * If it's a multicast frame, do a direct-dispatch to the + * destination hardware queue. Don't bother software + * queuing it. + */ + /* + * If it's a BAR frame, do a direct dispatch to the + * destination hardware queue. Don't bother software + * queuing it, as the TID will now be paused. + * Sending a BAR frame can occur from the net80211 txa timer + * (ie, retries) or from the ath txtask (completion call.) + * It queues directly to hardware because the TID is paused + * at this point (and won't be unpaused until the BAR has + * either been TXed successfully or max retries has been + * reached.) + */ + /* + * Until things are better debugged - if this node is asleep + * and we're sending it a non-BAR frame, direct dispatch it. + * Why? Because we need to figure out what's actually being + * sent - eg, during reassociation/reauthentication after + * the node (last) disappeared whilst asleep, the driver should + * have unpaused/unsleep'ed the node. So until that is + * sorted out, use this workaround. + */ + if (txq == &avp->av_mcastq) { + DPRINTF(sc, ATH_DEBUG_SW_TX, + "%s: bf=%p: mcastq: TX'ing\n", __func__, bf); + bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK; + ath_tx_xmit_normal(sc, txq, bf); + } else if (ath_tx_should_swq_frame(sc, ATH_NODE(ni), m0, + &queue_to_head)) { + ath_tx_swq(sc, ni, txq, queue_to_head, bf); + } else { + bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK; + ath_tx_xmit_normal(sc, txq, bf); + } +#else + /* + * For now, since there's no software queue, + * direct-dispatch to the hardware. + */ + bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK; + /* + * Update the current leak count if + * we're leaking frames; and set the + * MORE flag as appropriate. + */ + ath_tx_leak_count_update(sc, tid, bf); + ath_tx_xmit_normal(sc, txq, bf); +#endif +done: return 0; } @@ -874,23 +2065,25 @@ ath_tx_raw_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf, struct mbuf *m0, const struct ieee80211_bpf_params *params) { - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = sc->sc_ah; struct ieee80211vap *vap = ni->ni_vap; int error, ismcast, ismrr; int keyix, hdrlen, pktlen, try0, txantenna; - u_int8_t rix, cix, txrate, ctsrate, rate1, rate2, rate3; + u_int8_t rix, txrate; struct ieee80211_frame *wh; - u_int flags, ctsduration; + u_int flags; HAL_PKT_TYPE atype; const HAL_RATE_TABLE *rt; struct ath_desc *ds; u_int pri; - uint8_t try[4], rate[4]; + int o_tid = -1; + int do_override; + uint8_t type, subtype; + int queue_to_head; + struct ath_node *an = ATH_NODE(ni); - bzero(try, sizeof(try)); - bzero(rate, sizeof(rate)); + ATH_TX_LOCK_ASSERT(sc); wh = mtod(m0, struct ieee80211_frame *); ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); @@ -902,61 +2095,101 @@ ath_tx_raw_start(struct ath_softc *sc, struct ieee80211_node *ni, /* XXX honor IEEE80211_BPF_DATAPAD */ pktlen = m0->m_pkthdr.len - (hdrlen & 3) + IEEE80211_CRC_LEN; + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; + + ATH_KTR(sc, ATH_KTR_TX, 2, + "ath_tx_raw_start: ni=%p, bf=%p, raw", ni, bf); + + DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: ismcast=%d\n", + __func__, ismcast); + + pri = params->ibp_pri & 3; + /* Override pri if the frame isn't a QoS one */ + if (! IEEE80211_QOS_HAS_SEQ(wh)) + pri = ath_tx_getac(sc, m0); + + /* XXX If it's an ADDBA, override the correct queue */ + do_override = ath_tx_action_frame_override_queue(sc, ni, m0, &o_tid); + + /* Map ADDBA to the correct priority */ + if (do_override) { +#if 0 + DPRINTF(sc, ATH_DEBUG_XMIT, + "%s: overriding tid %d pri %d -> %d\n", + __func__, o_tid, pri, TID_TO_WME_AC(o_tid)); +#endif + pri = TID_TO_WME_AC(o_tid); + } + /* Handle encryption twiddling if needed */ - if (! ath_tx_tag_crypto(sc, ni, m0, params->ibp_flags & IEEE80211_BPF_CRYPTO, 0, &hdrlen, &pktlen, &keyix)) { - ath_freetx(m0); + if (! ath_tx_tag_crypto(sc, ni, + m0, params->ibp_flags & IEEE80211_BPF_CRYPTO, 0, + &hdrlen, &pktlen, &keyix)) { + ieee80211_free_mbuf(m0); return EIO; } /* packet header may have moved, reset our local pointer */ wh = mtod(m0, struct ieee80211_frame *); + /* Do the generic frame setup */ + /* XXX should just bzero the bf_state? */ + bf->bf_state.bfs_dobaw = 0; + error = ath_tx_dmasetup(sc, bf, m0); if (error != 0) return error; m0 = bf->bf_m; /* NB: may have changed */ wh = mtod(m0, struct ieee80211_frame *); + KASSERT((ni != NULL), ("%s: ni=NULL!", __func__)); bf->bf_node = ni; /* NB: held reference */ + /* Always enable CLRDMASK for raw frames for now.. */ flags = HAL_TXDESC_CLRDMASK; /* XXX needed for crypto errs */ flags |= HAL_TXDESC_INTREQ; /* force interrupt */ if (params->ibp_flags & IEEE80211_BPF_RTS) flags |= HAL_TXDESC_RTSENA; - else if (params->ibp_flags & IEEE80211_BPF_CTS) + else if (params->ibp_flags & IEEE80211_BPF_CTS) { + /* XXX assume 11g/11n protection? */ + bf->bf_state.bfs_doprot = 1; flags |= HAL_TXDESC_CTSENA; + } /* XXX leave ismcast to injector? */ if ((params->ibp_flags & IEEE80211_BPF_NOACK) || ismcast) flags |= HAL_TXDESC_NOACK; rt = sc->sc_currates; KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode)); + + /* Fetch first rate information */ rix = ath_tx_findrix(sc, params->ibp_rate0); + try0 = params->ibp_try0; + + /* + * Override EAPOL rate as appropriate. + */ + if (m0->m_flags & M_EAPOL) { + /* XXX? maybe always use long preamble? */ + rix = an->an_mgmtrix; + try0 = ATH_TXMAXTRY; /* XXX?too many? */ + } + txrate = rt->info[rix].rateCode; if (params->ibp_flags & IEEE80211_BPF_SHORTPRE) txrate |= rt->info[rix].shortPreamble; sc->sc_txrix = rix; - try0 = params->ibp_try0; ismrr = (params->ibp_try1 != 0); txantenna = params->ibp_pri >> 2; if (txantenna == 0) /* XXX? */ txantenna = sc->sc_txantenna; - ctsduration = 0; - if (flags & (HAL_TXDESC_RTSENA|HAL_TXDESC_CTSENA)) { - cix = ath_tx_findrix(sc, params->ibp_ctsrate); - ctsrate = ath_tx_get_rtscts_rate(ah, rt, rix, cix, params->ibp_flags & IEEE80211_BPF_SHORTPRE); - /* The 11n chipsets do ctsduration calculations for you */ - if (! ath_tx_is_11n(sc)) - ctsduration = ath_tx_calc_ctsduration(ah, rix, cix, - params->ibp_flags & IEEE80211_BPF_SHORTPRE, pktlen, - rt, flags); - /* - * Must disable multi-rate retry when using RTS/CTS. - */ - ismrr = 0; /* XXX */ - } else - ctsrate = 0; + /* + * Since ctsrate is fixed, store it away for later + * use when the descriptor fields are being set. + */ + if (flags & (HAL_TXDESC_RTSENA|HAL_TXDESC_CTSENA)) + bf->bf_state.bfs_ctsrate0 = params->ibp_ctsrate; - pri = params->ibp_pri & 3; /* * NB: we mark all packets as type PSPOLL so the h/w won't * set the sequence number, duration, etc. @@ -972,12 +2205,13 @@ ath_tx_raw_start(struct ath_softc *sc, struct ieee80211_node *ni, sc->sc_tx_th.wt_tsf = htole64(tsf); sc->sc_tx_th.wt_flags = sc->sc_hwmap[rix].txflags; - if (wh->i_fc[1] & IEEE80211_FC1_WEP) + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_WEP; if (m0->m_flags & M_FRAG) sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_FRAG; sc->sc_tx_th.wt_rate = sc->sc_hwmap[rix].ieeerate; - sc->sc_tx_th.wt_txpower = ni->ni_txpower; + sc->sc_tx_th.wt_txpower = MIN(params->ibp_power, + ieee80211_get_node_txpower(ni)); sc->sc_tx_th.wt_antenna = sc->sc_txantenna; ieee80211_radiotap_tx(vap, m0); @@ -988,105 +2222,175 @@ ath_tx_raw_start(struct ath_softc *sc, struct ieee80211_node *ni, */ ds = bf->bf_desc; /* XXX check return value? */ - ath_hal_setuptxdesc(ah, ds - , pktlen /* packet length */ - , hdrlen /* header length */ - , atype /* Atheros packet type */ - , params->ibp_power /* txpower */ - , txrate, try0 /* series 0 rate/tries */ - , keyix /* key cache index */ - , txantenna /* antenna mode */ - , flags /* flags */ - , ctsrate /* rts/cts rate */ - , ctsduration /* rts/cts duration */ - ); - bf->bf_txflags = flags; - if (ath_tx_is_11n(sc)) { - rate[0] = ath_tx_findrix(sc, params->ibp_rate0); - try[0] = params->ibp_try0; + /* Store the decided rate index values away */ + bf->bf_state.bfs_pktlen = pktlen; + bf->bf_state.bfs_hdrlen = hdrlen; + bf->bf_state.bfs_atype = atype; + bf->bf_state.bfs_txpower = MIN(params->ibp_power, + ieee80211_get_node_txpower(ni)); + bf->bf_state.bfs_txrate0 = txrate; + bf->bf_state.bfs_try0 = try0; + bf->bf_state.bfs_keyix = keyix; + bf->bf_state.bfs_txantenna = txantenna; + bf->bf_state.bfs_txflags = flags; + bf->bf_state.bfs_shpream = + !! (params->ibp_flags & IEEE80211_BPF_SHORTPRE); - if (ismrr) { - /* Remember, rate[] is actually an array of rix's -adrian */ - rate[0] = ath_tx_findrix(sc, params->ibp_rate0); - rate[1] = ath_tx_findrix(sc, params->ibp_rate1); - rate[2] = ath_tx_findrix(sc, params->ibp_rate2); - rate[3] = ath_tx_findrix(sc, params->ibp_rate3); + /* Set local packet state, used to queue packets to hardware */ + bf->bf_state.bfs_tid = WME_AC_TO_TID(pri); + bf->bf_state.bfs_tx_queue = sc->sc_ac2q[pri]->axq_qnum; + bf->bf_state.bfs_pri = pri; - try[0] = params->ibp_try0; - try[1] = params->ibp_try1; - try[2] = params->ibp_try2; - try[3] = params->ibp_try3; - } - } else { - if (ismrr) { - rix = ath_tx_findrix(sc, params->ibp_rate1); - rate1 = rt->info[rix].rateCode; - if (params->ibp_flags & IEEE80211_BPF_SHORTPRE) - rate1 |= rt->info[rix].shortPreamble; - if (params->ibp_try2) { - rix = ath_tx_findrix(sc, params->ibp_rate2); - rate2 = rt->info[rix].rateCode; - if (params->ibp_flags & IEEE80211_BPF_SHORTPRE) - rate2 |= rt->info[rix].shortPreamble; - } else - rate2 = 0; - if (params->ibp_try3) { - rix = ath_tx_findrix(sc, params->ibp_rate3); - rate3 = rt->info[rix].rateCode; - if (params->ibp_flags & IEEE80211_BPF_SHORTPRE) - rate3 |= rt->info[rix].shortPreamble; - } else - rate3 = 0; - ath_hal_setupxtxdesc(ah, ds - , rate1, params->ibp_try1 /* series 1 */ - , rate2, params->ibp_try2 /* series 2 */ - , rate3, params->ibp_try3 /* series 3 */ - ); - } - } - - if (ath_tx_is_11n(sc)) { - /* - * notice that rix doesn't include any of the "magic" flags txrate - * does for communicating "other stuff" to the HAL. - */ - ath_buf_set_rate(sc, ni, bf, pktlen, flags, ctsrate, (atype == HAL_PKT_TYPE_PSPOLL), rate, try); + /* XXX this should be done in ath_tx_setrate() */ + bf->bf_state.bfs_ctsrate = 0; + bf->bf_state.bfs_ctsduration = 0; + bf->bf_state.bfs_ismrr = ismrr; + + /* Blank the legacy rate array */ + bzero(&bf->bf_state.bfs_rc, sizeof(bf->bf_state.bfs_rc)); + + bf->bf_state.bfs_rc[0].rix = rix; + bf->bf_state.bfs_rc[0].tries = try0; + bf->bf_state.bfs_rc[0].ratecode = txrate; + + if (ismrr) { + int rix; + + rix = ath_tx_findrix(sc, params->ibp_rate1); + bf->bf_state.bfs_rc[1].rix = rix; + bf->bf_state.bfs_rc[1].tries = params->ibp_try1; + + rix = ath_tx_findrix(sc, params->ibp_rate2); + bf->bf_state.bfs_rc[2].rix = rix; + bf->bf_state.bfs_rc[2].tries = params->ibp_try2; + + rix = ath_tx_findrix(sc, params->ibp_rate3); + bf->bf_state.bfs_rc[3].rix = rix; + bf->bf_state.bfs_rc[3].tries = params->ibp_try3; } + /* + * All the required rate control decisions have been made; + * fill in the rc flags. + */ + ath_tx_rate_fill_rcflags(sc, bf); /* NB: no buffered multicast in power save support */ - ath_tx_handoff(sc, sc->sc_ac2q[pri], bf); + + /* + * If we're overiding the ADDBA destination, dump directly + * into the hardware queue, right after any pending + * frames to that node are. + */ + DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: dooverride=%d\n", + __func__, do_override); + +#if 1 + /* + * Put addba frames in the right place in the right TID/HWQ. + */ + if (do_override) { + bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK; + /* + * XXX if it's addba frames, should we be leaking + * them out via the frame leak method? + * XXX for now let's not risk it; but we may wish + * to investigate this later. + */ + ath_tx_xmit_normal(sc, sc->sc_ac2q[pri], bf); + } else if (ath_tx_should_swq_frame(sc, ATH_NODE(ni), m0, + &queue_to_head)) { + /* Queue to software queue */ + ath_tx_swq(sc, ni, sc->sc_ac2q[pri], queue_to_head, bf); + } else { + bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK; + ath_tx_xmit_normal(sc, sc->sc_ac2q[pri], bf); + } +#else + /* Direct-dispatch to the hardware */ + bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK; + /* + * Update the current leak count if + * we're leaking frames; and set the + * MORE flag as appropriate. + */ + ath_tx_leak_count_update(sc, tid, bf); + ath_tx_xmit_normal(sc, sc->sc_ac2q[pri], bf); +#endif return 0; } +/* + * Send a raw frame. + * + * This can be called by net80211. + */ int ath_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; - struct ifnet *ifp = ic->ic_ifp; - struct ath_softc *sc = ifp->if_softc; + struct ath_softc *sc = ic->ic_softc; struct ath_buf *bf; - int error; + struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); + int error = 0; - if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || sc->sc_invalid) { - DPRINTF(sc, ATH_DEBUG_XMIT, "%s: discard frame, %s", __func__, - (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ? - "!running" : "invalid"); + ATH_PCU_LOCK(sc); + if (sc->sc_inreset_cnt > 0) { + DPRINTF(sc, ATH_DEBUG_XMIT, + "%s: sc_inreset_cnt > 0; bailing\n", __func__); + error = EIO; + ATH_PCU_UNLOCK(sc); + goto badbad; + } + sc->sc_txstart_cnt++; + ATH_PCU_UNLOCK(sc); + + /* Wake the hardware up already */ + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + + ATH_TX_LOCK(sc); + + if (!sc->sc_running || sc->sc_invalid) { + DPRINTF(sc, ATH_DEBUG_XMIT, "%s: discard frame, r/i: %d/%d", + __func__, sc->sc_running, sc->sc_invalid); m_freem(m); error = ENETDOWN; goto bad; } + + /* + * Enforce how deep the multicast queue can grow. + * + * XXX duplicated in ath_tx_start(). + */ + if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { + if (sc->sc_cabq->axq_depth + sc->sc_cabq->fifo.axq_depth + > sc->sc_txq_mcastq_maxdepth) { + sc->sc_stats.ast_tx_mcastq_overflow++; + error = ENOBUFS; + } + + if (error != 0) { + m_freem(m); + goto bad; + } + } + /* * Grab a TX buffer and associated resources. */ - bf = ath_getbuf(sc); + bf = ath_getbuf(sc, ATH_BUFTYPE_MGMT); if (bf == NULL) { sc->sc_stats.ast_tx_nobuf++; m_freem(m); error = ENOBUFS; goto bad; } + ATH_KTR(sc, ATH_KTR_TX, 3, "ath_raw_xmit: m=%p, params=%p, bf=%p\n", + m, params, bf); if (params == NULL) { /* @@ -1108,17 +2412,3804 @@ ath_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, } } sc->sc_wd_timer = 5; - ifp->if_opackets++; sc->sc_stats.ast_tx_raw++; + /* + * Update the TIM - if there's anything queued to the + * software queue and power save is enabled, we should + * set the TIM. + */ + ath_tx_update_tim(sc, ni, 1); + + ATH_TX_UNLOCK(sc); + + ATH_PCU_LOCK(sc); + sc->sc_txstart_cnt--; + ATH_PCU_UNLOCK(sc); + + + /* Put the hardware back to sleep if required */ + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + return 0; + bad2: + ATH_KTR(sc, ATH_KTR_TX, 3, "ath_raw_xmit: bad2: m=%p, params=%p, " + "bf=%p", + m, + params, + bf); ATH_TXBUF_LOCK(sc); - STAILQ_INSERT_HEAD(&sc->sc_txbuf, bf, bf_list); + ath_returnbuf_head(sc, bf); ATH_TXBUF_UNLOCK(sc); + bad: - ifp->if_oerrors++; + ATH_TX_UNLOCK(sc); + + ATH_PCU_LOCK(sc); + sc->sc_txstart_cnt--; + ATH_PCU_UNLOCK(sc); + + /* Put the hardware back to sleep if required */ + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + +badbad: + ATH_KTR(sc, ATH_KTR_TX, 2, "ath_raw_xmit: bad0: m=%p, params=%p", + m, params); sc->sc_stats.ast_tx_raw_fail++; - ieee80211_free_node(ni); + return error; } + +/* Some helper functions */ + +/* + * ADDBA (and potentially others) need to be placed in the same + * hardware queue as the TID/node it's relating to. This is so + * it goes out after any pending non-aggregate frames to the + * same node/TID. + * + * If this isn't done, the ADDBA can go out before the frames + * queued in hardware. Even though these frames have a sequence + * number -earlier- than the ADDBA can be transmitted (but + * no frames whose sequence numbers are after the ADDBA should + * be!) they'll arrive after the ADDBA - and the receiving end + * will simply drop them as being out of the BAW. + * + * The frames can't be appended to the TID software queue - it'll + * never be sent out. So these frames have to be directly + * dispatched to the hardware, rather than queued in software. + * So if this function returns true, the TXQ has to be + * overridden and it has to be directly dispatched. + * + * It's a dirty hack, but someone's gotta do it. + */ + +/* + * XXX doesn't belong here! + */ +static int +ieee80211_is_action(struct ieee80211_frame *wh) +{ + /* Type: Management frame? */ + if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != + IEEE80211_FC0_TYPE_MGT) + return 0; + + /* Subtype: Action frame? */ + if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) != + IEEE80211_FC0_SUBTYPE_ACTION) + return 0; + + return 1; +} + +#define MS(_v, _f) (((_v) & _f) >> _f##_S) +/* + * Return an alternate TID for ADDBA request frames. + * + * Yes, this likely should be done in the net80211 layer. + */ +static int +ath_tx_action_frame_override_queue(struct ath_softc *sc, + struct ieee80211_node *ni, + struct mbuf *m0, int *tid) +{ + struct ieee80211_frame *wh = mtod(m0, struct ieee80211_frame *); + struct ieee80211_action_ba_addbarequest *ia; + uint8_t *frm; + uint16_t baparamset; + + /* Not action frame? Bail */ + if (! ieee80211_is_action(wh)) + return 0; + + /* XXX Not needed for frames we send? */ +#if 0 + /* Correct length? */ + if (! ieee80211_parse_action(ni, m)) + return 0; +#endif + + /* Extract out action frame */ + frm = (u_int8_t *)&wh[1]; + ia = (struct ieee80211_action_ba_addbarequest *) frm; + + /* Not ADDBA? Bail */ + if (ia->rq_header.ia_category != IEEE80211_ACTION_CAT_BA) + return 0; + if (ia->rq_header.ia_action != IEEE80211_ACTION_BA_ADDBA_REQUEST) + return 0; + + /* Extract TID, return it */ + baparamset = le16toh(ia->rq_baparamset); + *tid = (int) MS(baparamset, IEEE80211_BAPS_TID); + + return 1; +} +#undef MS + +/* Per-node software queue operations */ + +/* + * Add the current packet to the given BAW. + * It is assumed that the current packet + * + * + fits inside the BAW; + * + already has had a sequence number allocated. + * + * Since the BAW status may be modified by both the ath task and + * the net80211/ifnet contexts, the TID must be locked. + */ +void +ath_tx_addto_baw(struct ath_softc *sc, struct ath_node *an, + struct ath_tid *tid, struct ath_buf *bf) +{ + int index, cindex; + struct ieee80211_tx_ampdu *tap; + + ATH_TX_LOCK_ASSERT(sc); + + if (bf->bf_state.bfs_isretried) + return; + + tap = ath_tx_get_tx_tid(an, tid->tid); + + if (! bf->bf_state.bfs_dobaw) { + DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, + "%s: dobaw=0, seqno=%d, window %d:%d\n", + __func__, SEQNO(bf->bf_state.bfs_seqno), + tap->txa_start, tap->txa_wnd); + } + + if (bf->bf_state.bfs_addedbaw) + DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, + "%s: re-added? tid=%d, seqno %d; window %d:%d; " + "baw head=%d tail=%d\n", + __func__, tid->tid, SEQNO(bf->bf_state.bfs_seqno), + tap->txa_start, tap->txa_wnd, tid->baw_head, + tid->baw_tail); + + /* + * Verify that the given sequence number is not outside of the + * BAW. Complain loudly if that's the case. + */ + if (! BAW_WITHIN(tap->txa_start, tap->txa_wnd, + SEQNO(bf->bf_state.bfs_seqno))) { + DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, + "%s: bf=%p: outside of BAW?? tid=%d, seqno %d; window %d:%d; " + "baw head=%d tail=%d\n", + __func__, bf, tid->tid, SEQNO(bf->bf_state.bfs_seqno), + tap->txa_start, tap->txa_wnd, tid->baw_head, + tid->baw_tail); + } + + /* + * ni->ni_txseqs[] is the currently allocated seqno. + * the txa state contains the current baw start. + */ + index = ATH_BA_INDEX(tap->txa_start, SEQNO(bf->bf_state.bfs_seqno)); + cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1); + DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, + "%s: tid=%d, seqno %d; window %d:%d; index=%d cindex=%d " + "baw head=%d tail=%d\n", + __func__, tid->tid, SEQNO(bf->bf_state.bfs_seqno), + tap->txa_start, tap->txa_wnd, index, cindex, tid->baw_head, + tid->baw_tail); + + +#if 0 + assert(tid->tx_buf[cindex] == NULL); +#endif + if (tid->tx_buf[cindex] != NULL) { + DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, + "%s: ba packet dup (index=%d, cindex=%d, " + "head=%d, tail=%d)\n", + __func__, index, cindex, tid->baw_head, tid->baw_tail); + DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, + "%s: BA bf: %p; seqno=%d ; new bf: %p; seqno=%d\n", + __func__, + tid->tx_buf[cindex], + SEQNO(tid->tx_buf[cindex]->bf_state.bfs_seqno), + bf, + SEQNO(bf->bf_state.bfs_seqno) + ); + } + tid->tx_buf[cindex] = bf; + + if (index >= ((tid->baw_tail - tid->baw_head) & + (ATH_TID_MAX_BUFS - 1))) { + tid->baw_tail = cindex; + INCR(tid->baw_tail, ATH_TID_MAX_BUFS); + } +} + +/* + * Flip the BAW buffer entry over from the existing one to the new one. + * + * When software retransmitting a (sub-)frame, it is entirely possible that + * the frame ath_buf is marked as BUSY and can't be immediately reused. + * In that instance the buffer is cloned and the new buffer is used for + * retransmit. We thus need to update the ath_buf slot in the BAW buf + * tracking array to maintain consistency. + */ +static void +ath_tx_switch_baw_buf(struct ath_softc *sc, struct ath_node *an, + struct ath_tid *tid, struct ath_buf *old_bf, struct ath_buf *new_bf) +{ + int index, cindex; + struct ieee80211_tx_ampdu *tap; + int seqno = SEQNO(old_bf->bf_state.bfs_seqno); + + ATH_TX_LOCK_ASSERT(sc); + + tap = ath_tx_get_tx_tid(an, tid->tid); + index = ATH_BA_INDEX(tap->txa_start, seqno); + cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1); + + /* + * Just warn for now; if it happens then we should find out + * about it. It's highly likely the aggregation session will + * soon hang. + */ + if (old_bf->bf_state.bfs_seqno != new_bf->bf_state.bfs_seqno) { + DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, + "%s: retransmitted buffer" + " has mismatching seqno's, BA session may hang.\n", + __func__); + DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, + "%s: old seqno=%d, new_seqno=%d\n", __func__, + old_bf->bf_state.bfs_seqno, new_bf->bf_state.bfs_seqno); + } + + if (tid->tx_buf[cindex] != old_bf) { + DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, + "%s: ath_buf pointer incorrect; " + " has m BA session may hang.\n", __func__); + DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, + "%s: old bf=%p, new bf=%p\n", __func__, old_bf, new_bf); + } + + tid->tx_buf[cindex] = new_bf; +} + +/* + * seq_start - left edge of BAW + * seq_next - current/next sequence number to allocate + * + * Since the BAW status may be modified by both the ath task and + * the net80211/ifnet contexts, the TID must be locked. + */ +static void +ath_tx_update_baw(struct ath_softc *sc, struct ath_node *an, + struct ath_tid *tid, const struct ath_buf *bf) +{ + int index, cindex; + struct ieee80211_tx_ampdu *tap; + int seqno = SEQNO(bf->bf_state.bfs_seqno); + + ATH_TX_LOCK_ASSERT(sc); + + tap = ath_tx_get_tx_tid(an, tid->tid); + index = ATH_BA_INDEX(tap->txa_start, seqno); + cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1); + + DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, + "%s: tid=%d, baw=%d:%d, seqno=%d, index=%d, cindex=%d, " + "baw head=%d, tail=%d\n", + __func__, tid->tid, tap->txa_start, tap->txa_wnd, seqno, index, + cindex, tid->baw_head, tid->baw_tail); + + /* + * If this occurs then we have a big problem - something else + * has slid tap->txa_start along without updating the BAW + * tracking start/end pointers. Thus the TX BAW state is now + * completely busted. + * + * But for now, since I haven't yet fixed TDMA and buffer cloning, + * it's quite possible that a cloned buffer is making its way + * here and causing it to fire off. Disable TDMA for now. + */ + if (tid->tx_buf[cindex] != bf) { + DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, + "%s: comp bf=%p, seq=%d; slot bf=%p, seqno=%d\n", + __func__, bf, SEQNO(bf->bf_state.bfs_seqno), + tid->tx_buf[cindex], + (tid->tx_buf[cindex] != NULL) ? + SEQNO(tid->tx_buf[cindex]->bf_state.bfs_seqno) : -1); + } + + tid->tx_buf[cindex] = NULL; + + while (tid->baw_head != tid->baw_tail && + !tid->tx_buf[tid->baw_head]) { + INCR(tap->txa_start, IEEE80211_SEQ_RANGE); + INCR(tid->baw_head, ATH_TID_MAX_BUFS); + } + DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, + "%s: tid=%d: baw is now %d:%d, baw head=%d\n", + __func__, tid->tid, tap->txa_start, tap->txa_wnd, tid->baw_head); +} + +static void +ath_tx_leak_count_update(struct ath_softc *sc, struct ath_tid *tid, + struct ath_buf *bf) +{ + struct ieee80211_frame *wh; + + ATH_TX_LOCK_ASSERT(sc); + + if (tid->an->an_leak_count > 0) { + wh = mtod(bf->bf_m, struct ieee80211_frame *); + + /* + * Update MORE based on the software/net80211 queue states. + */ + if ((tid->an->an_stack_psq > 0) + || (tid->an->an_swq_depth > 0)) + wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA; + else + wh->i_fc[1] &= ~IEEE80211_FC1_MORE_DATA; + + DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, + "%s: %6D: leak count = %d, psq=%d, swq=%d, MORE=%d\n", + __func__, + tid->an->an_node.ni_macaddr, + ":", + tid->an->an_leak_count, + tid->an->an_stack_psq, + tid->an->an_swq_depth, + !! (wh->i_fc[1] & IEEE80211_FC1_MORE_DATA)); + + /* + * Re-sync the underlying buffer. + */ + bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, + BUS_DMASYNC_PREWRITE); + + tid->an->an_leak_count --; + } +} + +static int +ath_tx_tid_can_tx_or_sched(struct ath_softc *sc, struct ath_tid *tid) +{ + + ATH_TX_LOCK_ASSERT(sc); + + if (tid->an->an_leak_count > 0) { + return (1); + } + if (tid->paused) + return (0); + return (1); +} + +/* + * Mark the current node/TID as ready to TX. + * + * This is done to make it easy for the software scheduler to + * find which nodes have data to send. + * + * The TXQ lock must be held. + */ +void +ath_tx_tid_sched(struct ath_softc *sc, struct ath_tid *tid) +{ + struct ath_txq *txq = sc->sc_ac2q[tid->ac]; + + ATH_TX_LOCK_ASSERT(sc); + + /* + * If we are leaking out a frame to this destination + * for PS-POLL, ensure that we allow scheduling to + * occur. + */ + if (! ath_tx_tid_can_tx_or_sched(sc, tid)) + return; /* paused, can't schedule yet */ + + if (tid->sched) + return; /* already scheduled */ + + tid->sched = 1; + +#if 0 + /* + * If this is a sleeping node we're leaking to, given + * it a higher priority. This is so bad for QoS it hurts. + */ + if (tid->an->an_leak_count) { + TAILQ_INSERT_HEAD(&txq->axq_tidq, tid, axq_qelem); + } else { + TAILQ_INSERT_TAIL(&txq->axq_tidq, tid, axq_qelem); + } +#endif + + /* + * We can't do the above - it'll confuse the TXQ software + * scheduler which will keep checking the _head_ TID + * in the list to see if it has traffic. If we queue + * a TID to the head of the list and it doesn't transmit, + * we'll check it again. + * + * So, get the rest of this leaking frames support working + * and reliable first and _then_ optimise it so they're + * pushed out in front of any other pending software + * queued nodes. + */ + TAILQ_INSERT_TAIL(&txq->axq_tidq, tid, axq_qelem); +} + +/* + * Mark the current node as no longer needing to be polled for + * TX packets. + * + * The TXQ lock must be held. + */ +static void +ath_tx_tid_unsched(struct ath_softc *sc, struct ath_tid *tid) +{ + struct ath_txq *txq = sc->sc_ac2q[tid->ac]; + + ATH_TX_LOCK_ASSERT(sc); + + if (tid->sched == 0) + return; + + tid->sched = 0; + TAILQ_REMOVE(&txq->axq_tidq, tid, axq_qelem); +} + +/* + * Assign a sequence number manually to the given frame. + * + * This should only be called for A-MPDU TX frames. + */ +static ieee80211_seq +ath_tx_tid_seqno_assign(struct ath_softc *sc, struct ieee80211_node *ni, + struct ath_buf *bf, struct mbuf *m0) +{ + struct ieee80211_frame *wh; + int tid, pri; + ieee80211_seq seqno; + uint8_t subtype; + + /* TID lookup */ + wh = mtod(m0, struct ieee80211_frame *); + pri = M_WME_GETAC(m0); /* honor classification */ + tid = WME_AC_TO_TID(pri); + DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: pri=%d, tid=%d, qos has seq=%d\n", + __func__, pri, tid, IEEE80211_QOS_HAS_SEQ(wh)); + + /* XXX Is it a control frame? Ignore */ + + /* Does the packet require a sequence number? */ + if (! IEEE80211_QOS_HAS_SEQ(wh)) + return -1; + + ATH_TX_LOCK_ASSERT(sc); + + /* + * Is it a QOS NULL Data frame? Give it a sequence number from + * the default TID (IEEE80211_NONQOS_TID.) + * + * The RX path of everything I've looked at doesn't include the NULL + * data frame sequence number in the aggregation state updates, so + * assigning it a sequence number there will cause a BAW hole on the + * RX side. + */ + subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; + if (subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL) { + /* XXX no locking for this TID? This is a bit of a problem. */ + seqno = ni->ni_txseqs[IEEE80211_NONQOS_TID]; + INCR(ni->ni_txseqs[IEEE80211_NONQOS_TID], IEEE80211_SEQ_RANGE); + } else { + /* Manually assign sequence number */ + seqno = ni->ni_txseqs[tid]; + INCR(ni->ni_txseqs[tid], IEEE80211_SEQ_RANGE); + } + *(uint16_t *)&wh->i_seq[0] = htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); + M_SEQNO_SET(m0, seqno); + + /* Return so caller can do something with it if needed */ + DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: -> seqno=%d\n", __func__, seqno); + return seqno; +} + +/* + * Attempt to direct dispatch an aggregate frame to hardware. + * If the frame is out of BAW, queue. + * Otherwise, schedule it as a single frame. + */ +static void +ath_tx_xmit_aggr(struct ath_softc *sc, struct ath_node *an, + struct ath_txq *txq, struct ath_buf *bf) +{ + struct ath_tid *tid = &an->an_tid[bf->bf_state.bfs_tid]; + struct ieee80211_tx_ampdu *tap; + + ATH_TX_LOCK_ASSERT(sc); + + tap = ath_tx_get_tx_tid(an, tid->tid); + + /* paused? queue */ + if (! ath_tx_tid_can_tx_or_sched(sc, tid)) { + ATH_TID_INSERT_HEAD(tid, bf, bf_list); + /* XXX don't sched - we're paused! */ + return; + } + + /* outside baw? queue */ + if (bf->bf_state.bfs_dobaw && + (! BAW_WITHIN(tap->txa_start, tap->txa_wnd, + SEQNO(bf->bf_state.bfs_seqno)))) { + ATH_TID_INSERT_HEAD(tid, bf, bf_list); + ath_tx_tid_sched(sc, tid); + return; + } + + /* + * This is a temporary check and should be removed once + * all the relevant code paths have been fixed. + * + * During aggregate retries, it's possible that the head + * frame will fail (which has the bfs_aggr and bfs_nframes + * fields set for said aggregate) and will be retried as + * a single frame. In this instance, the values should + * be reset or the completion code will get upset with you. + */ + if (bf->bf_state.bfs_aggr != 0 || bf->bf_state.bfs_nframes > 1) { + DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, + "%s: bfs_aggr=%d, bfs_nframes=%d\n", __func__, + bf->bf_state.bfs_aggr, bf->bf_state.bfs_nframes); + bf->bf_state.bfs_aggr = 0; + bf->bf_state.bfs_nframes = 1; + } + + /* Update CLRDMASK just before this frame is queued */ + ath_tx_update_clrdmask(sc, tid, bf); + + /* Direct dispatch to hardware */ + ath_tx_do_ratelookup(sc, bf); + ath_tx_calc_duration(sc, bf); + ath_tx_calc_protection(sc, bf); + ath_tx_set_rtscts(sc, bf); + ath_tx_rate_fill_rcflags(sc, bf); + ath_tx_setds(sc, bf); + + /* Statistics */ + sc->sc_aggr_stats.aggr_low_hwq_single_pkt++; + + /* Track per-TID hardware queue depth correctly */ + tid->hwq_depth++; + + /* Add to BAW */ + if (bf->bf_state.bfs_dobaw) { + ath_tx_addto_baw(sc, an, tid, bf); + bf->bf_state.bfs_addedbaw = 1; + } + + /* Set completion handler, multi-frame aggregate or not */ + bf->bf_comp = ath_tx_aggr_comp; + + /* + * Update the current leak count if + * we're leaking frames; and set the + * MORE flag as appropriate. + */ + ath_tx_leak_count_update(sc, tid, bf); + + /* Hand off to hardware */ + ath_tx_handoff(sc, txq, bf); +} + +/* + * Attempt to send the packet. + * If the queue isn't busy, direct-dispatch. + * If the queue is busy enough, queue the given packet on the + * relevant software queue. + */ +void +ath_tx_swq(struct ath_softc *sc, struct ieee80211_node *ni, + struct ath_txq *txq, int queue_to_head, struct ath_buf *bf) +{ + struct ath_node *an = ATH_NODE(ni); + struct ieee80211_frame *wh; + struct ath_tid *atid; + int pri, tid; + struct mbuf *m0 = bf->bf_m; + + ATH_TX_LOCK_ASSERT(sc); + + /* Fetch the TID - non-QoS frames get assigned to TID 16 */ + wh = mtod(m0, struct ieee80211_frame *); + pri = ath_tx_getac(sc, m0); + tid = ath_tx_gettid(sc, m0); + atid = &an->an_tid[tid]; + + DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: bf=%p, pri=%d, tid=%d, qos=%d\n", + __func__, bf, pri, tid, IEEE80211_QOS_HAS_SEQ(wh)); + + /* Set local packet state, used to queue packets to hardware */ + /* XXX potentially duplicate info, re-check */ + bf->bf_state.bfs_tid = tid; + bf->bf_state.bfs_tx_queue = txq->axq_qnum; + bf->bf_state.bfs_pri = pri; + + /* + * If the hardware queue isn't busy, queue it directly. + * If the hardware queue is busy, queue it. + * If the TID is paused or the traffic it outside BAW, software + * queue it. + * + * If the node is in power-save and we're leaking a frame, + * leak a single frame. + */ + if (! ath_tx_tid_can_tx_or_sched(sc, atid)) { + /* TID is paused, queue */ + DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: paused\n", __func__); + /* + * If the caller requested that it be sent at a high + * priority, queue it at the head of the list. + */ + if (queue_to_head) + ATH_TID_INSERT_HEAD(atid, bf, bf_list); + else + ATH_TID_INSERT_TAIL(atid, bf, bf_list); + } else if (ath_tx_ampdu_pending(sc, an, tid)) { + /* AMPDU pending; queue */ + DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: pending\n", __func__); + ATH_TID_INSERT_TAIL(atid, bf, bf_list); + /* XXX sched? */ + } else if (ath_tx_ampdu_running(sc, an, tid)) { + /* AMPDU running, attempt direct dispatch if possible */ + + /* + * Always queue the frame to the tail of the list. + */ + ATH_TID_INSERT_TAIL(atid, bf, bf_list); + + /* + * If the hardware queue isn't busy, direct dispatch + * the head frame in the list. Don't schedule the + * TID - let it build some more frames first? + * + * When running A-MPDU, always just check the hardware + * queue depth against the aggregate frame limit. + * We don't want to burst a large number of single frames + * out to the hardware; we want to aggressively hold back. + * + * Otherwise, schedule the TID. + */ + /* XXX TXQ locking */ + if (txq->axq_depth + txq->fifo.axq_depth < sc->sc_hwq_limit_aggr) { + bf = ATH_TID_FIRST(atid); + ATH_TID_REMOVE(atid, bf, bf_list); + + /* + * Ensure it's definitely treated as a non-AMPDU + * frame - this information may have been left + * over from a previous attempt. + */ + bf->bf_state.bfs_aggr = 0; + bf->bf_state.bfs_nframes = 1; + + /* Queue to the hardware */ + ath_tx_xmit_aggr(sc, an, txq, bf); + DPRINTF(sc, ATH_DEBUG_SW_TX, + "%s: xmit_aggr\n", + __func__); + } else { + DPRINTF(sc, ATH_DEBUG_SW_TX, + "%s: ampdu; swq'ing\n", + __func__); + + ath_tx_tid_sched(sc, atid); + } + /* + * If we're not doing A-MPDU, be prepared to direct dispatch + * up to both limits if possible. This particular corner + * case may end up with packet starvation between aggregate + * traffic and non-aggregate traffic: we want to ensure + * that non-aggregate stations get a few frames queued to the + * hardware before the aggregate station(s) get their chance. + * + * So if you only ever see a couple of frames direct dispatched + * to the hardware from a non-AMPDU client, check both here + * and in the software queue dispatcher to ensure that those + * non-AMPDU stations get a fair chance to transmit. + */ + /* XXX TXQ locking */ + } else if ((txq->axq_depth + txq->fifo.axq_depth < sc->sc_hwq_limit_nonaggr) && + (txq->axq_aggr_depth < sc->sc_hwq_limit_aggr)) { + /* AMPDU not running, attempt direct dispatch */ + DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: xmit_normal\n", __func__); + /* See if clrdmask needs to be set */ + ath_tx_update_clrdmask(sc, atid, bf); + + /* + * Update the current leak count if + * we're leaking frames; and set the + * MORE flag as appropriate. + */ + ath_tx_leak_count_update(sc, atid, bf); + + /* + * Dispatch the frame. + */ + ath_tx_xmit_normal(sc, txq, bf); + } else { + /* Busy; queue */ + DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: swq'ing\n", __func__); + ATH_TID_INSERT_TAIL(atid, bf, bf_list); + ath_tx_tid_sched(sc, atid); + } +} + +/* + * Only set the clrdmask bit if none of the nodes are currently + * filtered. + * + * XXX TODO: go through all the callers and check to see + * which are being called in the context of looping over all + * TIDs (eg, if all tids are being paused, resumed, etc.) + * That'll avoid O(n^2) complexity here. + */ +static void +ath_tx_set_clrdmask(struct ath_softc *sc, struct ath_node *an) +{ + int i; + + ATH_TX_LOCK_ASSERT(sc); + + for (i = 0; i < IEEE80211_TID_SIZE; i++) { + if (an->an_tid[i].isfiltered == 1) + return; + } + an->clrdmask = 1; +} + +/* + * Configure the per-TID node state. + * + * This likely belongs in if_ath_node.c but I can't think of anywhere + * else to put it just yet. + * + * This sets up the SLISTs and the mutex as appropriate. + */ +void +ath_tx_tid_init(struct ath_softc *sc, struct ath_node *an) +{ + int i, j; + struct ath_tid *atid; + + for (i = 0; i < IEEE80211_TID_SIZE; i++) { + atid = &an->an_tid[i]; + + /* XXX now with this bzer(), is the field 0'ing needed? */ + bzero(atid, sizeof(*atid)); + + TAILQ_INIT(&atid->tid_q); + TAILQ_INIT(&atid->filtq.tid_q); + atid->tid = i; + atid->an = an; + for (j = 0; j < ATH_TID_MAX_BUFS; j++) + atid->tx_buf[j] = NULL; + atid->baw_head = atid->baw_tail = 0; + atid->paused = 0; + atid->sched = 0; + atid->hwq_depth = 0; + atid->cleanup_inprogress = 0; + if (i == IEEE80211_NONQOS_TID) + atid->ac = ATH_NONQOS_TID_AC; + else + atid->ac = TID_TO_WME_AC(i); + } + an->clrdmask = 1; /* Always start by setting this bit */ +} + +/* + * Pause the current TID. This stops packets from being transmitted + * on it. + * + * Since this is also called from upper layers as well as the driver, + * it will get the TID lock. + */ +static void +ath_tx_tid_pause(struct ath_softc *sc, struct ath_tid *tid) +{ + + ATH_TX_LOCK_ASSERT(sc); + tid->paused++; + DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: [%6D]: tid=%d, paused = %d\n", + __func__, + tid->an->an_node.ni_macaddr, ":", + tid->tid, + tid->paused); +} + +/* + * Unpause the current TID, and schedule it if needed. + */ +static void +ath_tx_tid_resume(struct ath_softc *sc, struct ath_tid *tid) +{ + ATH_TX_LOCK_ASSERT(sc); + + /* + * There's some odd places where ath_tx_tid_resume() is called + * when it shouldn't be; this works around that particular issue + * until it's actually resolved. + */ + if (tid->paused == 0) { + device_printf(sc->sc_dev, + "%s: [%6D]: tid=%d, paused=0?\n", + __func__, + tid->an->an_node.ni_macaddr, ":", + tid->tid); + } else { + tid->paused--; + } + + DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, + "%s: [%6D]: tid=%d, unpaused = %d\n", + __func__, + tid->an->an_node.ni_macaddr, ":", + tid->tid, + tid->paused); + + if (tid->paused) + return; + + /* + * Override the clrdmask configuration for the next frame + * from this TID, just to get the ball rolling. + */ + ath_tx_set_clrdmask(sc, tid->an); + + if (tid->axq_depth == 0) + return; + + /* XXX isfiltered shouldn't ever be 0 at this point */ + if (tid->isfiltered == 1) { + DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: filtered?!\n", + __func__); + return; + } + + ath_tx_tid_sched(sc, tid); + + /* + * Queue the software TX scheduler. + */ + ath_tx_swq_kick(sc); +} + +/* + * Add the given ath_buf to the TID filtered frame list. + * This requires the TID be filtered. + */ +static void +ath_tx_tid_filt_addbuf(struct ath_softc *sc, struct ath_tid *tid, + struct ath_buf *bf) +{ + + ATH_TX_LOCK_ASSERT(sc); + + if (!tid->isfiltered) + DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, "%s: not filtered?!\n", + __func__); + + DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, "%s: bf=%p\n", __func__, bf); + + /* Set the retry bit and bump the retry counter */ + ath_tx_set_retry(sc, bf); + sc->sc_stats.ast_tx_swfiltered++; + + ATH_TID_FILT_INSERT_TAIL(tid, bf, bf_list); +} + +/* + * Handle a completed filtered frame from the given TID. + * This just enables/pauses the filtered frame state if required + * and appends the filtered frame to the filtered queue. + */ +static void +ath_tx_tid_filt_comp_buf(struct ath_softc *sc, struct ath_tid *tid, + struct ath_buf *bf) +{ + + ATH_TX_LOCK_ASSERT(sc); + + if (! tid->isfiltered) { + DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, "%s: tid=%d; filter transition\n", + __func__, tid->tid); + tid->isfiltered = 1; + ath_tx_tid_pause(sc, tid); + } + + /* Add the frame to the filter queue */ + ath_tx_tid_filt_addbuf(sc, tid, bf); +} + +/* + * Complete the filtered frame TX completion. + * + * If there are no more frames in the hardware queue, unpause/unfilter + * the TID if applicable. Otherwise we will wait for a node PS transition + * to unfilter. + */ +static void +ath_tx_tid_filt_comp_complete(struct ath_softc *sc, struct ath_tid *tid) +{ + struct ath_buf *bf; + int do_resume = 0; + + ATH_TX_LOCK_ASSERT(sc); + + if (tid->hwq_depth != 0) + return; + + DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, "%s: tid=%d, hwq=0, transition back\n", + __func__, tid->tid); + if (tid->isfiltered == 1) { + tid->isfiltered = 0; + do_resume = 1; + } + + /* XXX ath_tx_tid_resume() also calls ath_tx_set_clrdmask()! */ + ath_tx_set_clrdmask(sc, tid->an); + + /* XXX this is really quite inefficient */ + while ((bf = ATH_TID_FILT_LAST(tid, ath_bufhead_s)) != NULL) { + ATH_TID_FILT_REMOVE(tid, bf, bf_list); + ATH_TID_INSERT_HEAD(tid, bf, bf_list); + } + + /* And only resume if we had paused before */ + if (do_resume) + ath_tx_tid_resume(sc, tid); +} + +/* + * Called when a single (aggregate or otherwise) frame is completed. + * + * Returns 0 if the buffer could be added to the filtered list + * (cloned or otherwise), 1 if the buffer couldn't be added to the + * filtered list (failed clone; expired retry) and the caller should + * free it and handle it like a failure (eg by sending a BAR.) + * + * since the buffer may be cloned, bf must be not touched after this + * if the return value is 0. + */ +static int +ath_tx_tid_filt_comp_single(struct ath_softc *sc, struct ath_tid *tid, + struct ath_buf *bf) +{ + struct ath_buf *nbf; + int retval; + + ATH_TX_LOCK_ASSERT(sc); + + /* + * Don't allow a filtered frame to live forever. + */ + if (bf->bf_state.bfs_retries > SWMAX_RETRIES) { + sc->sc_stats.ast_tx_swretrymax++; + DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, + "%s: bf=%p, seqno=%d, exceeded retries\n", + __func__, + bf, + SEQNO(bf->bf_state.bfs_seqno)); + retval = 1; /* error */ + goto finish; + } + + /* + * A busy buffer can't be added to the retry list. + * It needs to be cloned. + */ + if (bf->bf_flags & ATH_BUF_BUSY) { + nbf = ath_tx_retry_clone(sc, tid->an, tid, bf); + DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, + "%s: busy buffer clone: %p -> %p\n", + __func__, bf, nbf); + } else { + nbf = bf; + } + + if (nbf == NULL) { + DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, + "%s: busy buffer couldn't be cloned (%p)!\n", + __func__, bf); + retval = 1; /* error */ + } else { + ath_tx_tid_filt_comp_buf(sc, tid, nbf); + retval = 0; /* ok */ + } +finish: + ath_tx_tid_filt_comp_complete(sc, tid); + + return (retval); +} + +static void +ath_tx_tid_filt_comp_aggr(struct ath_softc *sc, struct ath_tid *tid, + struct ath_buf *bf_first, ath_bufhead *bf_q) +{ + struct ath_buf *bf, *bf_next, *nbf; + + ATH_TX_LOCK_ASSERT(sc); + + bf = bf_first; + while (bf) { + bf_next = bf->bf_next; + bf->bf_next = NULL; /* Remove it from the aggr list */ + + /* + * Don't allow a filtered frame to live forever. + */ + if (bf->bf_state.bfs_retries > SWMAX_RETRIES) { + sc->sc_stats.ast_tx_swretrymax++; + DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, + "%s: tid=%d, bf=%p, seqno=%d, exceeded retries\n", + __func__, + tid->tid, + bf, + SEQNO(bf->bf_state.bfs_seqno)); + TAILQ_INSERT_TAIL(bf_q, bf, bf_list); + goto next; + } + + if (bf->bf_flags & ATH_BUF_BUSY) { + nbf = ath_tx_retry_clone(sc, tid->an, tid, bf); + DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, + "%s: tid=%d, busy buffer cloned: %p -> %p, seqno=%d\n", + __func__, tid->tid, bf, nbf, SEQNO(bf->bf_state.bfs_seqno)); + } else { + nbf = bf; + } + + /* + * If the buffer couldn't be cloned, add it to bf_q; + * the caller will free the buffer(s) as required. + */ + if (nbf == NULL) { + DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, + "%s: tid=%d, buffer couldn't be cloned! (%p) seqno=%d\n", + __func__, tid->tid, bf, SEQNO(bf->bf_state.bfs_seqno)); + TAILQ_INSERT_TAIL(bf_q, bf, bf_list); + } else { + ath_tx_tid_filt_comp_buf(sc, tid, nbf); + } +next: + bf = bf_next; + } + + ath_tx_tid_filt_comp_complete(sc, tid); +} + +/* + * Suspend the queue because we need to TX a BAR. + */ +static void +ath_tx_tid_bar_suspend(struct ath_softc *sc, struct ath_tid *tid) +{ + + ATH_TX_LOCK_ASSERT(sc); + + DPRINTF(sc, ATH_DEBUG_SW_TX_BAR, + "%s: tid=%d, bar_wait=%d, bar_tx=%d, called\n", + __func__, + tid->tid, + tid->bar_wait, + tid->bar_tx); + + /* We shouldn't be called when bar_tx is 1 */ + if (tid->bar_tx) { + DPRINTF(sc, ATH_DEBUG_SW_TX_BAR, + "%s: bar_tx is 1?!\n", __func__); + } + + /* If we've already been called, just be patient. */ + if (tid->bar_wait) + return; + + /* Wait! */ + tid->bar_wait = 1; + + /* Only one pause, no matter how many frames fail */ + ath_tx_tid_pause(sc, tid); +} + +/* + * We've finished with BAR handling - either we succeeded or + * failed. Either way, unsuspend TX. + */ +static void +ath_tx_tid_bar_unsuspend(struct ath_softc *sc, struct ath_tid *tid) +{ + + ATH_TX_LOCK_ASSERT(sc); + + DPRINTF(sc, ATH_DEBUG_SW_TX_BAR, + "%s: %6D: TID=%d, called\n", + __func__, + tid->an->an_node.ni_macaddr, + ":", + tid->tid); + + if (tid->bar_tx == 0 || tid->bar_wait == 0) { + DPRINTF(sc, ATH_DEBUG_SW_TX_BAR, + "%s: %6D: TID=%d, bar_tx=%d, bar_wait=%d: ?\n", + __func__, tid->an->an_node.ni_macaddr, ":", + tid->tid, tid->bar_tx, tid->bar_wait); + } + + tid->bar_tx = tid->bar_wait = 0; + ath_tx_tid_resume(sc, tid); +} + +/* + * Return whether we're ready to TX a BAR frame. + * + * Requires the TID lock be held. + */ +static int +ath_tx_tid_bar_tx_ready(struct ath_softc *sc, struct ath_tid *tid) +{ + + ATH_TX_LOCK_ASSERT(sc); + + if (tid->bar_wait == 0 || tid->hwq_depth > 0) + return (0); + + DPRINTF(sc, ATH_DEBUG_SW_TX_BAR, + "%s: %6D: TID=%d, bar ready\n", + __func__, + tid->an->an_node.ni_macaddr, + ":", + tid->tid); + + return (1); +} + +/* + * Check whether the current TID is ready to have a BAR + * TXed and if so, do the TX. + * + * Since the TID/TXQ lock can't be held during a call to + * ieee80211_send_bar(), we have to do the dirty thing of unlocking it, + * sending the BAR and locking it again. + * + * Eventually, the code to send the BAR should be broken out + * from this routine so the lock doesn't have to be reacquired + * just to be immediately dropped by the caller. + */ +static void +ath_tx_tid_bar_tx(struct ath_softc *sc, struct ath_tid *tid) +{ + struct ieee80211_tx_ampdu *tap; + + ATH_TX_LOCK_ASSERT(sc); + + DPRINTF(sc, ATH_DEBUG_SW_TX_BAR, + "%s: %6D: TID=%d, called\n", + __func__, + tid->an->an_node.ni_macaddr, + ":", + tid->tid); + + tap = ath_tx_get_tx_tid(tid->an, tid->tid); + + /* + * This is an error condition! + */ + if (tid->bar_wait == 0 || tid->bar_tx == 1) { + DPRINTF(sc, ATH_DEBUG_SW_TX_BAR, + "%s: %6D: TID=%d, bar_tx=%d, bar_wait=%d: ?\n", + __func__, tid->an->an_node.ni_macaddr, ":", + tid->tid, tid->bar_tx, tid->bar_wait); + return; + } + + /* Don't do anything if we still have pending frames */ + if (tid->hwq_depth > 0) { + DPRINTF(sc, ATH_DEBUG_SW_TX_BAR, + "%s: %6D: TID=%d, hwq_depth=%d, waiting\n", + __func__, + tid->an->an_node.ni_macaddr, + ":", + tid->tid, + tid->hwq_depth); + return; + } + + /* We're now about to TX */ + tid->bar_tx = 1; + + /* + * Override the clrdmask configuration for the next frame, + * just to get the ball rolling. + */ + ath_tx_set_clrdmask(sc, tid->an); + + /* + * Calculate new BAW left edge, now that all frames have either + * succeeded or failed. + * + * XXX verify this is _actually_ the valid value to begin at! + */ + DPRINTF(sc, ATH_DEBUG_SW_TX_BAR, + "%s: %6D: TID=%d, new BAW left edge=%d\n", + __func__, + tid->an->an_node.ni_macaddr, + ":", + tid->tid, + tap->txa_start); + + /* Try sending the BAR frame */ + /* We can't hold the lock here! */ + + ATH_TX_UNLOCK(sc); + if (ieee80211_send_bar(&tid->an->an_node, tap, tap->txa_start) == 0) { + /* Success? Now we wait for notification that it's done */ + ATH_TX_LOCK(sc); + return; + } + + /* Failure? For now, warn loudly and continue */ + ATH_TX_LOCK(sc); + DPRINTF(sc, ATH_DEBUG_SW_TX_BAR, + "%s: %6D: TID=%d, failed to TX BAR, continue!\n", + __func__, tid->an->an_node.ni_macaddr, ":", + tid->tid); + ath_tx_tid_bar_unsuspend(sc, tid); +} + +static void +ath_tx_tid_drain_pkt(struct ath_softc *sc, struct ath_node *an, + struct ath_tid *tid, ath_bufhead *bf_cq, struct ath_buf *bf) +{ + + ATH_TX_LOCK_ASSERT(sc); + + /* + * If the current TID is running AMPDU, update + * the BAW. + */ + if (ath_tx_ampdu_running(sc, an, tid->tid) && + bf->bf_state.bfs_dobaw) { + /* + * Only remove the frame from the BAW if it's + * been transmitted at least once; this means + * the frame was in the BAW to begin with. + */ + if (bf->bf_state.bfs_retries > 0) { + ath_tx_update_baw(sc, an, tid, bf); + bf->bf_state.bfs_dobaw = 0; + } +#if 0 + /* + * This has become a non-fatal error now + */ + if (! bf->bf_state.bfs_addedbaw) + DPRINTF(sc, ATH_DEBUG_SW_TX_BAW + "%s: wasn't added: seqno %d\n", + __func__, SEQNO(bf->bf_state.bfs_seqno)); +#endif + } + + /* Strip it out of an aggregate list if it was in one */ + bf->bf_next = NULL; + + /* Insert on the free queue to be freed by the caller */ + TAILQ_INSERT_TAIL(bf_cq, bf, bf_list); +} + +static void +ath_tx_tid_drain_print(struct ath_softc *sc, struct ath_node *an, + const char *pfx, struct ath_tid *tid, struct ath_buf *bf) +{ + struct ieee80211_node *ni = &an->an_node; + struct ath_txq *txq; + struct ieee80211_tx_ampdu *tap; + + txq = sc->sc_ac2q[tid->ac]; + tap = ath_tx_get_tx_tid(an, tid->tid); + + DPRINTF(sc, ATH_DEBUG_SW_TX | ATH_DEBUG_RESET, + "%s: %s: %6D: bf=%p: addbaw=%d, dobaw=%d, " + "seqno=%d, retry=%d\n", + __func__, + pfx, + ni->ni_macaddr, + ":", + bf, + bf->bf_state.bfs_addedbaw, + bf->bf_state.bfs_dobaw, + SEQNO(bf->bf_state.bfs_seqno), + bf->bf_state.bfs_retries); + DPRINTF(sc, ATH_DEBUG_SW_TX | ATH_DEBUG_RESET, + "%s: %s: %6D: bf=%p: txq[%d] axq_depth=%d, axq_aggr_depth=%d\n", + __func__, + pfx, + ni->ni_macaddr, + ":", + bf, + txq->axq_qnum, + txq->axq_depth, + txq->axq_aggr_depth); + DPRINTF(sc, ATH_DEBUG_SW_TX | ATH_DEBUG_RESET, + "%s: %s: %6D: bf=%p: tid txq_depth=%d hwq_depth=%d, bar_wait=%d, " + "isfiltered=%d\n", + __func__, + pfx, + ni->ni_macaddr, + ":", + bf, + tid->axq_depth, + tid->hwq_depth, + tid->bar_wait, + tid->isfiltered); + DPRINTF(sc, ATH_DEBUG_SW_TX | ATH_DEBUG_RESET, + "%s: %s: %6D: tid %d: " + "sched=%d, paused=%d, " + "incomp=%d, baw_head=%d, " + "baw_tail=%d txa_start=%d, ni_txseqs=%d\n", + __func__, + pfx, + ni->ni_macaddr, + ":", + tid->tid, + tid->sched, tid->paused, + tid->incomp, tid->baw_head, + tid->baw_tail, tap == NULL ? -1 : tap->txa_start, + ni->ni_txseqs[tid->tid]); + + /* XXX Dump the frame, see what it is? */ + if (IFF_DUMPPKTS(sc, ATH_DEBUG_XMIT)) + ieee80211_dump_pkt(ni->ni_ic, + mtod(bf->bf_m, const uint8_t *), + bf->bf_m->m_len, 0, -1); +} + +/* + * Free any packets currently pending in the software TX queue. + * + * This will be called when a node is being deleted. + * + * It can also be called on an active node during an interface + * reset or state transition. + * + * (From Linux/reference): + * + * TODO: For frame(s) that are in the retry state, we will reuse the + * sequence number(s) without setting the retry bit. The + * alternative is to give up on these and BAR the receiver's window + * forward. + */ +static void +ath_tx_tid_drain(struct ath_softc *sc, struct ath_node *an, + struct ath_tid *tid, ath_bufhead *bf_cq) +{ + struct ath_buf *bf; + struct ieee80211_tx_ampdu *tap; + struct ieee80211_node *ni = &an->an_node; + int t; + + tap = ath_tx_get_tx_tid(an, tid->tid); + + ATH_TX_LOCK_ASSERT(sc); + + /* Walk the queue, free frames */ + t = 0; + for (;;) { + bf = ATH_TID_FIRST(tid); + if (bf == NULL) { + break; + } + + if (t == 0) { + ath_tx_tid_drain_print(sc, an, "norm", tid, bf); +// t = 1; + } + + ATH_TID_REMOVE(tid, bf, bf_list); + ath_tx_tid_drain_pkt(sc, an, tid, bf_cq, bf); + } + + /* And now, drain the filtered frame queue */ + t = 0; + for (;;) { + bf = ATH_TID_FILT_FIRST(tid); + if (bf == NULL) + break; + + if (t == 0) { + ath_tx_tid_drain_print(sc, an, "filt", tid, bf); +// t = 1; + } + + ATH_TID_FILT_REMOVE(tid, bf, bf_list); + ath_tx_tid_drain_pkt(sc, an, tid, bf_cq, bf); + } + + /* + * Override the clrdmask configuration for the next frame + * in case there is some future transmission, just to get + * the ball rolling. + * + * This won't hurt things if the TID is about to be freed. + */ + ath_tx_set_clrdmask(sc, tid->an); + + /* + * Now that it's completed, grab the TID lock and update + * the sequence number and BAW window. + * Because sequence numbers have been assigned to frames + * that haven't been sent yet, it's entirely possible + * we'll be called with some pending frames that have not + * been transmitted. + * + * The cleaner solution is to do the sequence number allocation + * when the packet is first transmitted - and thus the "retries" + * check above would be enough to update the BAW/seqno. + */ + + /* But don't do it for non-QoS TIDs */ + if (tap) { +#if 1 + DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, + "%s: %6D: node %p: TID %d: sliding BAW left edge to %d\n", + __func__, + ni->ni_macaddr, + ":", + an, + tid->tid, + tap->txa_start); +#endif + ni->ni_txseqs[tid->tid] = tap->txa_start; + tid->baw_tail = tid->baw_head; + } +} + +/* + * Reset the TID state. This must be only called once the node has + * had its frames flushed from this TID, to ensure that no other + * pause / unpause logic can kick in. + */ +static void +ath_tx_tid_reset(struct ath_softc *sc, struct ath_tid *tid) +{ + +#if 0 + tid->bar_wait = tid->bar_tx = tid->isfiltered = 0; + tid->paused = tid->sched = tid->addba_tx_pending = 0; + tid->incomp = tid->cleanup_inprogress = 0; +#endif + + /* + * If we have a bar_wait set, we need to unpause the TID + * here. Otherwise once cleanup has finished, the TID won't + * have the right paused counter. + * + * XXX I'm not going through resume here - I don't want the + * node to be rescheuled just yet. This however should be + * methodized! + */ + if (tid->bar_wait) { + if (tid->paused > 0) { + tid->paused --; + } + } + + /* + * XXX same with a currently filtered TID. + * + * Since this is being called during a flush, we assume that + * the filtered frame list is actually empty. + * + * XXX TODO: add in a check to ensure that the filtered queue + * depth is actually 0! + */ + if (tid->isfiltered) { + if (tid->paused > 0) { + tid->paused --; + } + } + + /* + * Clear BAR, filtered frames, scheduled and ADDBA pending. + * The TID may be going through cleanup from the last association + * where things in the BAW are still in the hardware queue. + */ + tid->bar_wait = 0; + tid->bar_tx = 0; + tid->isfiltered = 0; + tid->sched = 0; + tid->addba_tx_pending = 0; + + /* + * XXX TODO: it may just be enough to walk the HWQs and mark + * frames for that node as non-aggregate; or mark the ath_node + * with something that indicates that aggregation is no longer + * occurring. Then we can just toss the BAW complaints and + * do a complete hard reset of state here - no pause, no + * complete counter, etc. + */ + +} + +/* + * Flush all software queued packets for the given node. + * + * This occurs when a completion handler frees the last buffer + * for a node, and the node is thus freed. This causes the node + * to be cleaned up, which ends up calling ath_tx_node_flush. + */ +void +ath_tx_node_flush(struct ath_softc *sc, struct ath_node *an) +{ + int tid; + ath_bufhead bf_cq; + struct ath_buf *bf; + + TAILQ_INIT(&bf_cq); + + ATH_KTR(sc, ATH_KTR_NODE, 1, "ath_tx_node_flush: flush node; ni=%p", + &an->an_node); + + ATH_TX_LOCK(sc); + DPRINTF(sc, ATH_DEBUG_NODE, + "%s: %6D: flush; is_powersave=%d, stack_psq=%d, tim=%d, " + "swq_depth=%d, clrdmask=%d, leak_count=%d\n", + __func__, + an->an_node.ni_macaddr, + ":", + an->an_is_powersave, + an->an_stack_psq, + an->an_tim_set, + an->an_swq_depth, + an->clrdmask, + an->an_leak_count); + + for (tid = 0; tid < IEEE80211_TID_SIZE; tid++) { + struct ath_tid *atid = &an->an_tid[tid]; + + /* Free packets */ + ath_tx_tid_drain(sc, an, atid, &bf_cq); + + /* Remove this tid from the list of active tids */ + ath_tx_tid_unsched(sc, atid); + + /* Reset the per-TID pause, BAR, etc state */ + ath_tx_tid_reset(sc, atid); + } + + /* + * Clear global leak count + */ + an->an_leak_count = 0; + ATH_TX_UNLOCK(sc); + + /* Handle completed frames */ + while ((bf = TAILQ_FIRST(&bf_cq)) != NULL) { + TAILQ_REMOVE(&bf_cq, bf, bf_list); + ath_tx_default_comp(sc, bf, 0); + } +} + +/* + * Drain all the software TXQs currently with traffic queued. + */ +void +ath_tx_txq_drain(struct ath_softc *sc, struct ath_txq *txq) +{ + struct ath_tid *tid; + ath_bufhead bf_cq; + struct ath_buf *bf; + + TAILQ_INIT(&bf_cq); + ATH_TX_LOCK(sc); + + /* + * Iterate over all active tids for the given txq, + * flushing and unsched'ing them + */ + while (! TAILQ_EMPTY(&txq->axq_tidq)) { + tid = TAILQ_FIRST(&txq->axq_tidq); + ath_tx_tid_drain(sc, tid->an, tid, &bf_cq); + ath_tx_tid_unsched(sc, tid); + } + + ATH_TX_UNLOCK(sc); + + while ((bf = TAILQ_FIRST(&bf_cq)) != NULL) { + TAILQ_REMOVE(&bf_cq, bf, bf_list); + ath_tx_default_comp(sc, bf, 0); + } +} + +/* + * Handle completion of non-aggregate session frames. + * + * This (currently) doesn't implement software retransmission of + * non-aggregate frames! + * + * Software retransmission of non-aggregate frames needs to obey + * the strict sequence number ordering, and drop any frames that + * will fail this. + * + * For now, filtered frames and frame transmission will cause + * all kinds of issues. So we don't support them. + * + * So anyone queuing frames via ath_tx_normal_xmit() or + * ath_tx_hw_queue_norm() must override and set CLRDMASK. + */ +void +ath_tx_normal_comp(struct ath_softc *sc, struct ath_buf *bf, int fail) +{ + struct ieee80211_node *ni = bf->bf_node; + struct ath_node *an = ATH_NODE(ni); + int tid = bf->bf_state.bfs_tid; + struct ath_tid *atid = &an->an_tid[tid]; + struct ath_tx_status *ts = &bf->bf_status.ds_txstat; + + /* The TID state is protected behind the TXQ lock */ + ATH_TX_LOCK(sc); + + DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: bf=%p: fail=%d, hwq_depth now %d\n", + __func__, bf, fail, atid->hwq_depth - 1); + + atid->hwq_depth--; + +#if 0 + /* + * If the frame was filtered, stick it on the filter frame + * queue and complain about it. It shouldn't happen! + */ + if ((ts->ts_status & HAL_TXERR_FILT) || + (ts->ts_status != 0 && atid->isfiltered)) { + DPRINTF(sc, ATH_DEBUG_SW_TX, + "%s: isfiltered=%d, ts_status=%d: huh?\n", + __func__, + atid->isfiltered, + ts->ts_status); + ath_tx_tid_filt_comp_buf(sc, atid, bf); + } +#endif + if (atid->isfiltered) + DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: filtered?!\n", __func__); + if (atid->hwq_depth < 0) + DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: hwq_depth < 0: %d\n", + __func__, atid->hwq_depth); + + /* If the TID is being cleaned up, track things */ + /* XXX refactor! */ + if (atid->cleanup_inprogress) { + atid->incomp--; + if (atid->incomp == 0) { + DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, + "%s: TID %d: cleaned up! resume!\n", + __func__, tid); + atid->cleanup_inprogress = 0; + ath_tx_tid_resume(sc, atid); + } + } + + /* + * If the queue is filtered, potentially mark it as complete + * and reschedule it as needed. + * + * This is required as there may be a subsequent TX descriptor + * for this end-node that has CLRDMASK set, so it's quite possible + * that a filtered frame will be followed by a non-filtered + * (complete or otherwise) frame. + * + * XXX should we do this before we complete the frame? + */ + if (atid->isfiltered) + ath_tx_tid_filt_comp_complete(sc, atid); + ATH_TX_UNLOCK(sc); + + /* + * punt to rate control if we're not being cleaned up + * during a hw queue drain and the frame wanted an ACK. + */ + if (fail == 0 && ((bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 0)) + ath_tx_update_ratectrl(sc, ni, bf->bf_state.bfs_rc, + ts, bf->bf_state.bfs_pktlen, + 1, (ts->ts_status == 0) ? 0 : 1); + + ath_tx_default_comp(sc, bf, fail); +} + +/* + * Handle cleanup of aggregate session packets that aren't + * an A-MPDU. + * + * There's no need to update the BAW here - the session is being + * torn down. + */ +static void +ath_tx_comp_cleanup_unaggr(struct ath_softc *sc, struct ath_buf *bf) +{ + struct ieee80211_node *ni = bf->bf_node; + struct ath_node *an = ATH_NODE(ni); + int tid = bf->bf_state.bfs_tid; + struct ath_tid *atid = &an->an_tid[tid]; + + DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: TID %d: incomp=%d\n", + __func__, tid, atid->incomp); + + ATH_TX_LOCK(sc); + atid->incomp--; + + /* XXX refactor! */ + if (bf->bf_state.bfs_dobaw) { + ath_tx_update_baw(sc, an, atid, bf); + if (!bf->bf_state.bfs_addedbaw) + DPRINTF(sc, ATH_DEBUG_SW_TX, + "%s: wasn't added: seqno %d\n", + __func__, SEQNO(bf->bf_state.bfs_seqno)); + } + + if (atid->incomp == 0) { + DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, + "%s: TID %d: cleaned up! resume!\n", + __func__, tid); + atid->cleanup_inprogress = 0; + ath_tx_tid_resume(sc, atid); + } + ATH_TX_UNLOCK(sc); + + ath_tx_default_comp(sc, bf, 0); +} + + +/* + * This as it currently stands is a bit dumb. Ideally we'd just + * fail the frame the normal way and have it permanently fail + * via the normal aggregate completion path. + */ +static void +ath_tx_tid_cleanup_frame(struct ath_softc *sc, struct ath_node *an, + int tid, struct ath_buf *bf_head, ath_bufhead *bf_cq) +{ + struct ath_tid *atid = &an->an_tid[tid]; + struct ath_buf *bf, *bf_next; + + ATH_TX_LOCK_ASSERT(sc); + + /* + * Remove this frame from the queue. + */ + ATH_TID_REMOVE(atid, bf_head, bf_list); + + /* + * Loop over all the frames in the aggregate. + */ + bf = bf_head; + while (bf != NULL) { + bf_next = bf->bf_next; /* next aggregate frame, or NULL */ + + /* + * If it's been added to the BAW we need to kick + * it out of the BAW before we continue. + * + * XXX if it's an aggregate, assert that it's in the + * BAW - we shouldn't have it be in an aggregate + * otherwise! + */ + if (bf->bf_state.bfs_addedbaw) { + ath_tx_update_baw(sc, an, atid, bf); + bf->bf_state.bfs_dobaw = 0; + } + + /* + * Give it the default completion handler. + */ + bf->bf_comp = ath_tx_normal_comp; + bf->bf_next = NULL; + + /* + * Add it to the list to free. + */ + TAILQ_INSERT_TAIL(bf_cq, bf, bf_list); + + /* + * Now advance to the next frame in the aggregate. + */ + bf = bf_next; + } +} + +/* + * Performs transmit side cleanup when TID changes from aggregated to + * unaggregated and during reassociation. + * + * For now, this just tosses everything from the TID software queue + * whether or not it has been retried and marks the TID as + * pending completion if there's anything for this TID queued to + * the hardware. + * + * The caller is responsible for pausing the TID and unpausing the + * TID if no cleanup was required. Otherwise the cleanup path will + * unpause the TID once the last hardware queued frame is completed. + */ +static void +ath_tx_tid_cleanup(struct ath_softc *sc, struct ath_node *an, int tid, + ath_bufhead *bf_cq) +{ + struct ath_tid *atid = &an->an_tid[tid]; + struct ath_buf *bf, *bf_next; + + ATH_TX_LOCK_ASSERT(sc); + + DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, + "%s: TID %d: called; inprogress=%d\n", __func__, tid, + atid->cleanup_inprogress); + + /* + * Move the filtered frames to the TX queue, before + * we run off and discard/process things. + */ + + /* XXX this is really quite inefficient */ + while ((bf = ATH_TID_FILT_LAST(atid, ath_bufhead_s)) != NULL) { + ATH_TID_FILT_REMOVE(atid, bf, bf_list); + ATH_TID_INSERT_HEAD(atid, bf, bf_list); + } + + /* + * Update the frames in the software TX queue: + * + * + Discard retry frames in the queue + * + Fix the completion function to be non-aggregate + */ + bf = ATH_TID_FIRST(atid); + while (bf) { + /* + * Grab the next frame in the list, we may + * be fiddling with the list. + */ + bf_next = TAILQ_NEXT(bf, bf_list); + + /* + * Free the frame and all subframes. + */ + ath_tx_tid_cleanup_frame(sc, an, tid, bf, bf_cq); + + /* + * Next frame! + */ + bf = bf_next; + } + + /* + * If there's anything in the hardware queue we wait + * for the TID HWQ to empty. + */ + if (atid->hwq_depth > 0) { + /* + * XXX how about we kill atid->incomp, and instead + * replace it with a macro that checks that atid->hwq_depth + * is 0? + */ + atid->incomp = atid->hwq_depth; + atid->cleanup_inprogress = 1; + } + + if (atid->cleanup_inprogress) + DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, + "%s: TID %d: cleanup needed: %d packets\n", + __func__, tid, atid->incomp); + + /* Owner now must free completed frames */ +} + +static struct ath_buf * +ath_tx_retry_clone(struct ath_softc *sc, struct ath_node *an, + struct ath_tid *tid, struct ath_buf *bf) +{ + struct ath_buf *nbf; + int error; + + /* + * Clone the buffer. This will handle the dma unmap and + * copy the node reference to the new buffer. If this + * works out, 'bf' will have no DMA mapping, no mbuf + * pointer and no node reference. + */ + nbf = ath_buf_clone(sc, bf); + +#if 0 + DPRINTF(sc, ATH_DEBUG_XMIT, "%s: ATH_BUF_BUSY; cloning\n", + __func__); +#endif + + if (nbf == NULL) { + /* Failed to clone */ + DPRINTF(sc, ATH_DEBUG_XMIT, + "%s: failed to clone a busy buffer\n", + __func__); + return NULL; + } + + /* Setup the dma for the new buffer */ + error = ath_tx_dmasetup(sc, nbf, nbf->bf_m); + if (error != 0) { + DPRINTF(sc, ATH_DEBUG_XMIT, + "%s: failed to setup dma for clone\n", + __func__); + /* + * Put this at the head of the list, not tail; + * that way it doesn't interfere with the + * busy buffer logic (which uses the tail of + * the list.) + */ + ATH_TXBUF_LOCK(sc); + ath_returnbuf_head(sc, nbf); + ATH_TXBUF_UNLOCK(sc); + return NULL; + } + + /* Update BAW if required, before we free the original buf */ + if (bf->bf_state.bfs_dobaw) + ath_tx_switch_baw_buf(sc, an, tid, bf, nbf); + + /* Free original buffer; return new buffer */ + ath_freebuf(sc, bf); + + return nbf; +} + +/* + * Handle retrying an unaggregate frame in an aggregate + * session. + * + * If too many retries occur, pause the TID, wait for + * any further retransmits (as there's no reason why + * non-aggregate frames in an aggregate session are + * transmitted in-order; they just have to be in-BAW) + * and then queue a BAR. + */ +static void +ath_tx_aggr_retry_unaggr(struct ath_softc *sc, struct ath_buf *bf) +{ + struct ieee80211_node *ni = bf->bf_node; + struct ath_node *an = ATH_NODE(ni); + int tid = bf->bf_state.bfs_tid; + struct ath_tid *atid = &an->an_tid[tid]; + struct ieee80211_tx_ampdu *tap; + + ATH_TX_LOCK(sc); + + tap = ath_tx_get_tx_tid(an, tid); + + /* + * If the buffer is marked as busy, we can't directly + * reuse it. Instead, try to clone the buffer. + * If the clone is successful, recycle the old buffer. + * If the clone is unsuccessful, set bfs_retries to max + * to force the next bit of code to free the buffer + * for us. + */ + if ((bf->bf_state.bfs_retries < SWMAX_RETRIES) && + (bf->bf_flags & ATH_BUF_BUSY)) { + struct ath_buf *nbf; + nbf = ath_tx_retry_clone(sc, an, atid, bf); + if (nbf) + /* bf has been freed at this point */ + bf = nbf; + else + bf->bf_state.bfs_retries = SWMAX_RETRIES + 1; + } + + if (bf->bf_state.bfs_retries >= SWMAX_RETRIES) { + DPRINTF(sc, ATH_DEBUG_SW_TX_RETRIES, + "%s: exceeded retries; seqno %d\n", + __func__, SEQNO(bf->bf_state.bfs_seqno)); + sc->sc_stats.ast_tx_swretrymax++; + + /* Update BAW anyway */ + if (bf->bf_state.bfs_dobaw) { + ath_tx_update_baw(sc, an, atid, bf); + if (! bf->bf_state.bfs_addedbaw) + DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, + "%s: wasn't added: seqno %d\n", + __func__, SEQNO(bf->bf_state.bfs_seqno)); + } + bf->bf_state.bfs_dobaw = 0; + + /* Suspend the TX queue and get ready to send the BAR */ + ath_tx_tid_bar_suspend(sc, atid); + + /* Send the BAR if there are no other frames waiting */ + if (ath_tx_tid_bar_tx_ready(sc, atid)) + ath_tx_tid_bar_tx(sc, atid); + + ATH_TX_UNLOCK(sc); + + /* Free buffer, bf is free after this call */ + ath_tx_default_comp(sc, bf, 0); + return; + } + + /* + * This increments the retry counter as well as + * sets the retry flag in the ath_buf and packet + * body. + */ + ath_tx_set_retry(sc, bf); + sc->sc_stats.ast_tx_swretries++; + + /* + * Insert this at the head of the queue, so it's + * retried before any current/subsequent frames. + */ + ATH_TID_INSERT_HEAD(atid, bf, bf_list); + ath_tx_tid_sched(sc, atid); + /* Send the BAR if there are no other frames waiting */ + if (ath_tx_tid_bar_tx_ready(sc, atid)) + ath_tx_tid_bar_tx(sc, atid); + + ATH_TX_UNLOCK(sc); +} + +/* + * Common code for aggregate excessive retry/subframe retry. + * If retrying, queues buffers to bf_q. If not, frees the + * buffers. + * + * XXX should unify this with ath_tx_aggr_retry_unaggr() + */ +static int +ath_tx_retry_subframe(struct ath_softc *sc, struct ath_buf *bf, + ath_bufhead *bf_q) +{ + struct ieee80211_node *ni = bf->bf_node; + struct ath_node *an = ATH_NODE(ni); + int tid = bf->bf_state.bfs_tid; + struct ath_tid *atid = &an->an_tid[tid]; + + ATH_TX_LOCK_ASSERT(sc); + + /* XXX clr11naggr should be done for all subframes */ + ath_hal_clr11n_aggr(sc->sc_ah, bf->bf_desc); + ath_hal_set11nburstduration(sc->sc_ah, bf->bf_desc, 0); + + /* ath_hal_set11n_virtualmorefrag(sc->sc_ah, bf->bf_desc, 0); */ + + /* + * If the buffer is marked as busy, we can't directly + * reuse it. Instead, try to clone the buffer. + * If the clone is successful, recycle the old buffer. + * If the clone is unsuccessful, set bfs_retries to max + * to force the next bit of code to free the buffer + * for us. + */ + if ((bf->bf_state.bfs_retries < SWMAX_RETRIES) && + (bf->bf_flags & ATH_BUF_BUSY)) { + struct ath_buf *nbf; + nbf = ath_tx_retry_clone(sc, an, atid, bf); + if (nbf) + /* bf has been freed at this point */ + bf = nbf; + else + bf->bf_state.bfs_retries = SWMAX_RETRIES + 1; + } + + if (bf->bf_state.bfs_retries >= SWMAX_RETRIES) { + sc->sc_stats.ast_tx_swretrymax++; + DPRINTF(sc, ATH_DEBUG_SW_TX_RETRIES, + "%s: max retries: seqno %d\n", + __func__, SEQNO(bf->bf_state.bfs_seqno)); + ath_tx_update_baw(sc, an, atid, bf); + if (!bf->bf_state.bfs_addedbaw) + DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, + "%s: wasn't added: seqno %d\n", + __func__, SEQNO(bf->bf_state.bfs_seqno)); + bf->bf_state.bfs_dobaw = 0; + return 1; + } + + ath_tx_set_retry(sc, bf); + sc->sc_stats.ast_tx_swretries++; + bf->bf_next = NULL; /* Just to make sure */ + + /* Clear the aggregate state */ + bf->bf_state.bfs_aggr = 0; + bf->bf_state.bfs_ndelim = 0; /* ??? needed? */ + bf->bf_state.bfs_nframes = 1; + + TAILQ_INSERT_TAIL(bf_q, bf, bf_list); + return 0; +} + +/* + * error pkt completion for an aggregate destination + */ +static void +ath_tx_comp_aggr_error(struct ath_softc *sc, struct ath_buf *bf_first, + struct ath_tid *tid) +{ + struct ieee80211_node *ni = bf_first->bf_node; + struct ath_node *an = ATH_NODE(ni); + struct ath_buf *bf_next, *bf; + ath_bufhead bf_q; + int drops = 0; + struct ieee80211_tx_ampdu *tap; + ath_bufhead bf_cq; + + TAILQ_INIT(&bf_q); + TAILQ_INIT(&bf_cq); + + /* + * Update rate control - all frames have failed. + * + * XXX use the length in the first frame in the series; + * XXX just so things are consistent for now. + */ + ath_tx_update_ratectrl(sc, ni, bf_first->bf_state.bfs_rc, + &bf_first->bf_status.ds_txstat, + bf_first->bf_state.bfs_pktlen, + bf_first->bf_state.bfs_nframes, bf_first->bf_state.bfs_nframes); + + ATH_TX_LOCK(sc); + tap = ath_tx_get_tx_tid(an, tid->tid); + sc->sc_stats.ast_tx_aggr_failall++; + + /* Retry all subframes */ + bf = bf_first; + while (bf) { + bf_next = bf->bf_next; + bf->bf_next = NULL; /* Remove it from the aggr list */ + sc->sc_stats.ast_tx_aggr_fail++; + if (ath_tx_retry_subframe(sc, bf, &bf_q)) { + drops++; + bf->bf_next = NULL; + TAILQ_INSERT_TAIL(&bf_cq, bf, bf_list); + } + bf = bf_next; + } + + /* Prepend all frames to the beginning of the queue */ + while ((bf = TAILQ_LAST(&bf_q, ath_bufhead_s)) != NULL) { + TAILQ_REMOVE(&bf_q, bf, bf_list); + ATH_TID_INSERT_HEAD(tid, bf, bf_list); + } + + /* + * Schedule the TID to be re-tried. + */ + ath_tx_tid_sched(sc, tid); + + /* + * send bar if we dropped any frames + * + * Keep the txq lock held for now, as we need to ensure + * that ni_txseqs[] is consistent (as it's being updated + * in the ifnet TX context or raw TX context.) + */ + if (drops) { + /* Suspend the TX queue and get ready to send the BAR */ + ath_tx_tid_bar_suspend(sc, tid); + } + + /* + * Send BAR if required + */ + if (ath_tx_tid_bar_tx_ready(sc, tid)) + ath_tx_tid_bar_tx(sc, tid); + + ATH_TX_UNLOCK(sc); + + /* Complete frames which errored out */ + while ((bf = TAILQ_FIRST(&bf_cq)) != NULL) { + TAILQ_REMOVE(&bf_cq, bf, bf_list); + ath_tx_default_comp(sc, bf, 0); + } +} + +/* + * Handle clean-up of packets from an aggregate list. + * + * There's no need to update the BAW here - the session is being + * torn down. + */ +static void +ath_tx_comp_cleanup_aggr(struct ath_softc *sc, struct ath_buf *bf_first) +{ + struct ath_buf *bf, *bf_next; + struct ieee80211_node *ni = bf_first->bf_node; + struct ath_node *an = ATH_NODE(ni); + int tid = bf_first->bf_state.bfs_tid; + struct ath_tid *atid = &an->an_tid[tid]; + + ATH_TX_LOCK(sc); + + /* update incomp */ + atid->incomp--; + + /* Update the BAW */ + bf = bf_first; + while (bf) { + /* XXX refactor! */ + if (bf->bf_state.bfs_dobaw) { + ath_tx_update_baw(sc, an, atid, bf); + if (!bf->bf_state.bfs_addedbaw) + DPRINTF(sc, ATH_DEBUG_SW_TX, + "%s: wasn't added: seqno %d\n", + __func__, SEQNO(bf->bf_state.bfs_seqno)); + } + bf = bf->bf_next; + } + + if (atid->incomp == 0) { + DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, + "%s: TID %d: cleaned up! resume!\n", + __func__, tid); + atid->cleanup_inprogress = 0; + ath_tx_tid_resume(sc, atid); + } + + /* Send BAR if required */ + /* XXX why would we send a BAR when transitioning to non-aggregation? */ + /* + * XXX TODO: we should likely just tear down the BAR state here, + * rather than sending a BAR. + */ + if (ath_tx_tid_bar_tx_ready(sc, atid)) + ath_tx_tid_bar_tx(sc, atid); + + ATH_TX_UNLOCK(sc); + + /* Handle frame completion as individual frames */ + bf = bf_first; + while (bf) { + bf_next = bf->bf_next; + bf->bf_next = NULL; + ath_tx_default_comp(sc, bf, 1); + bf = bf_next; + } +} + +/* + * Handle completion of an set of aggregate frames. + * + * Note: the completion handler is the last descriptor in the aggregate, + * not the last descriptor in the first frame. + */ +static void +ath_tx_aggr_comp_aggr(struct ath_softc *sc, struct ath_buf *bf_first, + int fail) +{ + //struct ath_desc *ds = bf->bf_lastds; + struct ieee80211_node *ni = bf_first->bf_node; + struct ath_node *an = ATH_NODE(ni); + int tid = bf_first->bf_state.bfs_tid; + struct ath_tid *atid = &an->an_tid[tid]; + struct ath_tx_status ts; + struct ieee80211_tx_ampdu *tap; + ath_bufhead bf_q; + ath_bufhead bf_cq; + int seq_st, tx_ok; + int hasba, isaggr; + uint32_t ba[2]; + struct ath_buf *bf, *bf_next; + int ba_index; + int drops = 0; + int nframes = 0, nbad = 0, nf; + int pktlen; + /* XXX there's too much on the stack? */ + struct ath_rc_series rc[ATH_RC_NUM]; + int txseq; + + DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: called; hwq_depth=%d\n", + __func__, atid->hwq_depth); + + /* + * Take a copy; this may be needed -after- bf_first + * has been completed and freed. + */ + ts = bf_first->bf_status.ds_txstat; + + TAILQ_INIT(&bf_q); + TAILQ_INIT(&bf_cq); + + /* The TID state is kept behind the TXQ lock */ + ATH_TX_LOCK(sc); + + atid->hwq_depth--; + if (atid->hwq_depth < 0) + DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: hwq_depth < 0: %d\n", + __func__, atid->hwq_depth); + + /* + * If the TID is filtered, handle completing the filter + * transition before potentially kicking it to the cleanup + * function. + * + * XXX this is duplicate work, ew. + */ + if (atid->isfiltered) + ath_tx_tid_filt_comp_complete(sc, atid); + + /* + * Punt cleanup to the relevant function, not our problem now + */ + if (atid->cleanup_inprogress) { + if (atid->isfiltered) + DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, + "%s: isfiltered=1, normal_comp?\n", + __func__); + ATH_TX_UNLOCK(sc); + ath_tx_comp_cleanup_aggr(sc, bf_first); + return; + } + + /* + * If the frame is filtered, transition to filtered frame + * mode and add this to the filtered frame list. + * + * XXX TODO: figure out how this interoperates with + * BAR, pause and cleanup states. + */ + if ((ts.ts_status & HAL_TXERR_FILT) || + (ts.ts_status != 0 && atid->isfiltered)) { + if (fail != 0) + DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, + "%s: isfiltered=1, fail=%d\n", __func__, fail); + ath_tx_tid_filt_comp_aggr(sc, atid, bf_first, &bf_cq); + + /* Remove from BAW */ + TAILQ_FOREACH_SAFE(bf, &bf_cq, bf_list, bf_next) { + if (bf->bf_state.bfs_addedbaw) + drops++; + if (bf->bf_state.bfs_dobaw) { + ath_tx_update_baw(sc, an, atid, bf); + if (!bf->bf_state.bfs_addedbaw) + DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, + "%s: wasn't added: seqno %d\n", + __func__, + SEQNO(bf->bf_state.bfs_seqno)); + } + bf->bf_state.bfs_dobaw = 0; + } + /* + * If any intermediate frames in the BAW were dropped when + * handling filtering things, send a BAR. + */ + if (drops) + ath_tx_tid_bar_suspend(sc, atid); + + /* + * Finish up by sending a BAR if required and freeing + * the frames outside of the TX lock. + */ + goto finish_send_bar; + } + + /* + * XXX for now, use the first frame in the aggregate for + * XXX rate control completion; it's at least consistent. + */ + pktlen = bf_first->bf_state.bfs_pktlen; + + /* + * Handle errors first! + * + * Here, handle _any_ error as a "exceeded retries" error. + * Later on (when filtered frames are to be specially handled) + * it'll have to be expanded. + */ +#if 0 + if (ts.ts_status & HAL_TXERR_XRETRY) { +#endif + if (ts.ts_status != 0) { + ATH_TX_UNLOCK(sc); + ath_tx_comp_aggr_error(sc, bf_first, atid); + return; + } + + tap = ath_tx_get_tx_tid(an, tid); + + /* + * extract starting sequence and block-ack bitmap + */ + /* XXX endian-ness of seq_st, ba? */ + seq_st = ts.ts_seqnum; + hasba = !! (ts.ts_flags & HAL_TX_BA); + tx_ok = (ts.ts_status == 0); + isaggr = bf_first->bf_state.bfs_aggr; + ba[0] = ts.ts_ba_low; + ba[1] = ts.ts_ba_high; + + /* + * Copy the TX completion status and the rate control + * series from the first descriptor, as it may be freed + * before the rate control code can get its grubby fingers + * into things. + */ + memcpy(rc, bf_first->bf_state.bfs_rc, sizeof(rc)); + + DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, + "%s: txa_start=%d, tx_ok=%d, status=%.8x, flags=%.8x, " + "isaggr=%d, seq_st=%d, hasba=%d, ba=%.8x, %.8x\n", + __func__, tap->txa_start, tx_ok, ts.ts_status, ts.ts_flags, + isaggr, seq_st, hasba, ba[0], ba[1]); + + /* + * The reference driver doesn't do this; it simply ignores + * this check in its entirety. + * + * I've seen this occur when using iperf to send traffic + * out tid 1 - the aggregate frames are all marked as TID 1, + * but the TXSTATUS has TID=0. So, let's just ignore this + * check. + */ +#if 0 + /* Occasionally, the MAC sends a tx status for the wrong TID. */ + if (tid != ts.ts_tid) { + DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: tid %d != hw tid %d\n", + __func__, tid, ts.ts_tid); + tx_ok = 0; + } +#endif + + /* AR5416 BA bug; this requires an interface reset */ + if (isaggr && tx_ok && (! hasba)) { + device_printf(sc->sc_dev, + "%s: AR5416 bug: hasba=%d; txok=%d, isaggr=%d, " + "seq_st=%d\n", + __func__, hasba, tx_ok, isaggr, seq_st); + /* XXX TODO: schedule an interface reset */ +#ifdef ATH_DEBUG + ath_printtxbuf(sc, bf_first, + sc->sc_ac2q[atid->ac]->axq_qnum, 0, 0); +#endif + } + + /* + * Walk the list of frames, figure out which ones were correctly + * sent and which weren't. + */ + bf = bf_first; + nf = bf_first->bf_state.bfs_nframes; + + /* bf_first is going to be invalid once this list is walked */ + bf_first = NULL; + + /* + * Walk the list of completed frames and determine + * which need to be completed and which need to be + * retransmitted. + * + * For completed frames, the completion functions need + * to be called at the end of this function as the last + * node reference may free the node. + * + * Finally, since the TXQ lock can't be held during the + * completion callback (to avoid lock recursion), + * the completion calls have to be done outside of the + * lock. + */ + while (bf) { + nframes++; + ba_index = ATH_BA_INDEX(seq_st, + SEQNO(bf->bf_state.bfs_seqno)); + bf_next = bf->bf_next; + bf->bf_next = NULL; /* Remove it from the aggr list */ + + DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, + "%s: checking bf=%p seqno=%d; ack=%d\n", + __func__, bf, SEQNO(bf->bf_state.bfs_seqno), + ATH_BA_ISSET(ba, ba_index)); + + if (tx_ok && ATH_BA_ISSET(ba, ba_index)) { + sc->sc_stats.ast_tx_aggr_ok++; + ath_tx_update_baw(sc, an, atid, bf); + bf->bf_state.bfs_dobaw = 0; + if (!bf->bf_state.bfs_addedbaw) + DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, + "%s: wasn't added: seqno %d\n", + __func__, SEQNO(bf->bf_state.bfs_seqno)); + bf->bf_next = NULL; + TAILQ_INSERT_TAIL(&bf_cq, bf, bf_list); + } else { + sc->sc_stats.ast_tx_aggr_fail++; + if (ath_tx_retry_subframe(sc, bf, &bf_q)) { + drops++; + bf->bf_next = NULL; + TAILQ_INSERT_TAIL(&bf_cq, bf, bf_list); + } + nbad++; + } + bf = bf_next; + } + + /* + * Now that the BAW updates have been done, unlock + * + * txseq is grabbed before the lock is released so we + * have a consistent view of what -was- in the BAW. + * Anything after this point will not yet have been + * TXed. + */ + txseq = tap->txa_start; + ATH_TX_UNLOCK(sc); + + if (nframes != nf) + DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, + "%s: num frames seen=%d; bf nframes=%d\n", + __func__, nframes, nf); + + /* + * Now we know how many frames were bad, call the rate + * control code. + */ + if (fail == 0) + ath_tx_update_ratectrl(sc, ni, rc, &ts, pktlen, nframes, + nbad); + + /* + * send bar if we dropped any frames + */ + if (drops) { + /* Suspend the TX queue and get ready to send the BAR */ + ATH_TX_LOCK(sc); + ath_tx_tid_bar_suspend(sc, atid); + ATH_TX_UNLOCK(sc); + } + + DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, + "%s: txa_start now %d\n", __func__, tap->txa_start); + + ATH_TX_LOCK(sc); + + /* Prepend all frames to the beginning of the queue */ + while ((bf = TAILQ_LAST(&bf_q, ath_bufhead_s)) != NULL) { + TAILQ_REMOVE(&bf_q, bf, bf_list); + ATH_TID_INSERT_HEAD(atid, bf, bf_list); + } + + /* + * Reschedule to grab some further frames. + */ + ath_tx_tid_sched(sc, atid); + + /* + * If the queue is filtered, re-schedule as required. + * + * This is required as there may be a subsequent TX descriptor + * for this end-node that has CLRDMASK set, so it's quite possible + * that a filtered frame will be followed by a non-filtered + * (complete or otherwise) frame. + * + * XXX should we do this before we complete the frame? + */ + if (atid->isfiltered) + ath_tx_tid_filt_comp_complete(sc, atid); + +finish_send_bar: + + /* + * Send BAR if required + */ + if (ath_tx_tid_bar_tx_ready(sc, atid)) + ath_tx_tid_bar_tx(sc, atid); + + ATH_TX_UNLOCK(sc); + + /* Do deferred completion */ + while ((bf = TAILQ_FIRST(&bf_cq)) != NULL) { + TAILQ_REMOVE(&bf_cq, bf, bf_list); + ath_tx_default_comp(sc, bf, 0); + } +} + +/* + * Handle completion of unaggregated frames in an ADDBA + * session. + * + * Fail is set to 1 if the entry is being freed via a call to + * ath_tx_draintxq(). + */ +static void +ath_tx_aggr_comp_unaggr(struct ath_softc *sc, struct ath_buf *bf, int fail) +{ + struct ieee80211_node *ni = bf->bf_node; + struct ath_node *an = ATH_NODE(ni); + int tid = bf->bf_state.bfs_tid; + struct ath_tid *atid = &an->an_tid[tid]; + struct ath_tx_status ts; + int drops = 0; + + /* + * Take a copy of this; filtering/cloning the frame may free the + * bf pointer. + */ + ts = bf->bf_status.ds_txstat; + + /* + * Update rate control status here, before we possibly + * punt to retry or cleanup. + * + * Do it outside of the TXQ lock. + */ + if (fail == 0 && ((bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 0)) + ath_tx_update_ratectrl(sc, ni, bf->bf_state.bfs_rc, + &bf->bf_status.ds_txstat, + bf->bf_state.bfs_pktlen, + 1, (ts.ts_status == 0) ? 0 : 1); + + /* + * This is called early so atid->hwq_depth can be tracked. + * This unfortunately means that it's released and regrabbed + * during retry and cleanup. That's rather inefficient. + */ + ATH_TX_LOCK(sc); + + if (tid == IEEE80211_NONQOS_TID) + DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: TID=16!\n", __func__); + + DPRINTF(sc, ATH_DEBUG_SW_TX, + "%s: bf=%p: tid=%d, hwq_depth=%d, seqno=%d\n", + __func__, bf, bf->bf_state.bfs_tid, atid->hwq_depth, + SEQNO(bf->bf_state.bfs_seqno)); + + atid->hwq_depth--; + if (atid->hwq_depth < 0) + DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: hwq_depth < 0: %d\n", + __func__, atid->hwq_depth); + + /* + * If the TID is filtered, handle completing the filter + * transition before potentially kicking it to the cleanup + * function. + */ + if (atid->isfiltered) + ath_tx_tid_filt_comp_complete(sc, atid); + + /* + * If a cleanup is in progress, punt to comp_cleanup; + * rather than handling it here. It's thus their + * responsibility to clean up, call the completion + * function in net80211, etc. + */ + if (atid->cleanup_inprogress) { + if (atid->isfiltered) + DPRINTF(sc, ATH_DEBUG_SW_TX, + "%s: isfiltered=1, normal_comp?\n", + __func__); + ATH_TX_UNLOCK(sc); + DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: cleanup_unaggr\n", + __func__); + ath_tx_comp_cleanup_unaggr(sc, bf); + return; + } + + /* + * XXX TODO: how does cleanup, BAR and filtered frame handling + * overlap? + * + * If the frame is filtered OR if it's any failure but + * the TID is filtered, the frame must be added to the + * filtered frame list. + * + * However - a busy buffer can't be added to the filtered + * list as it will end up being recycled without having + * been made available for the hardware. + */ + if ((ts.ts_status & HAL_TXERR_FILT) || + (ts.ts_status != 0 && atid->isfiltered)) { + int freeframe; + + if (fail != 0) + DPRINTF(sc, ATH_DEBUG_SW_TX, + "%s: isfiltered=1, fail=%d\n", + __func__, fail); + freeframe = ath_tx_tid_filt_comp_single(sc, atid, bf); + /* + * If freeframe=0 then bf is no longer ours; don't + * touch it. + */ + if (freeframe) { + /* Remove from BAW */ + if (bf->bf_state.bfs_addedbaw) + drops++; + if (bf->bf_state.bfs_dobaw) { + ath_tx_update_baw(sc, an, atid, bf); + if (!bf->bf_state.bfs_addedbaw) + DPRINTF(sc, ATH_DEBUG_SW_TX, + "%s: wasn't added: seqno %d\n", + __func__, SEQNO(bf->bf_state.bfs_seqno)); + } + bf->bf_state.bfs_dobaw = 0; + } + + /* + * If the frame couldn't be filtered, treat it as a drop and + * prepare to send a BAR. + */ + if (freeframe && drops) + ath_tx_tid_bar_suspend(sc, atid); + + /* + * Send BAR if required + */ + if (ath_tx_tid_bar_tx_ready(sc, atid)) + ath_tx_tid_bar_tx(sc, atid); + + ATH_TX_UNLOCK(sc); + /* + * If freeframe is set, then the frame couldn't be + * cloned and bf is still valid. Just complete/free it. + */ + if (freeframe) + ath_tx_default_comp(sc, bf, fail); + + return; + } + /* + * Don't bother with the retry check if all frames + * are being failed (eg during queue deletion.) + */ +#if 0 + if (fail == 0 && ts->ts_status & HAL_TXERR_XRETRY) { +#endif + if (fail == 0 && ts.ts_status != 0) { + ATH_TX_UNLOCK(sc); + DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: retry_unaggr\n", + __func__); + ath_tx_aggr_retry_unaggr(sc, bf); + return; + } + + /* Success? Complete */ + DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: TID=%d, seqno %d\n", + __func__, tid, SEQNO(bf->bf_state.bfs_seqno)); + if (bf->bf_state.bfs_dobaw) { + ath_tx_update_baw(sc, an, atid, bf); + bf->bf_state.bfs_dobaw = 0; + if (!bf->bf_state.bfs_addedbaw) + DPRINTF(sc, ATH_DEBUG_SW_TX, + "%s: wasn't added: seqno %d\n", + __func__, SEQNO(bf->bf_state.bfs_seqno)); + } + + /* + * If the queue is filtered, re-schedule as required. + * + * This is required as there may be a subsequent TX descriptor + * for this end-node that has CLRDMASK set, so it's quite possible + * that a filtered frame will be followed by a non-filtered + * (complete or otherwise) frame. + * + * XXX should we do this before we complete the frame? + */ + if (atid->isfiltered) + ath_tx_tid_filt_comp_complete(sc, atid); + + /* + * Send BAR if required + */ + if (ath_tx_tid_bar_tx_ready(sc, atid)) + ath_tx_tid_bar_tx(sc, atid); + + ATH_TX_UNLOCK(sc); + + ath_tx_default_comp(sc, bf, fail); + /* bf is freed at this point */ +} + +void +ath_tx_aggr_comp(struct ath_softc *sc, struct ath_buf *bf, int fail) +{ + if (bf->bf_state.bfs_aggr) + ath_tx_aggr_comp_aggr(sc, bf, fail); + else + ath_tx_aggr_comp_unaggr(sc, bf, fail); +} + +/* + * Schedule some packets from the given node/TID to the hardware. + * + * This is the aggregate version. + */ +void +ath_tx_tid_hw_queue_aggr(struct ath_softc *sc, struct ath_node *an, + struct ath_tid *tid) +{ + struct ath_buf *bf; + struct ath_txq *txq = sc->sc_ac2q[tid->ac]; + struct ieee80211_tx_ampdu *tap; + ATH_AGGR_STATUS status; + ath_bufhead bf_q; + + DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: tid=%d\n", __func__, tid->tid); + ATH_TX_LOCK_ASSERT(sc); + + /* + * XXX TODO: If we're called for a queue that we're leaking frames to, + * ensure we only leak one. + */ + + tap = ath_tx_get_tx_tid(an, tid->tid); + + if (tid->tid == IEEE80211_NONQOS_TID) + DPRINTF(sc, ATH_DEBUG_SW_TX, + "%s: called for TID=NONQOS_TID?\n", __func__); + + for (;;) { + status = ATH_AGGR_DONE; + + /* + * If the upper layer has paused the TID, don't + * queue any further packets. + * + * This can also occur from the completion task because + * of packet loss; but as its serialised with this code, + * it won't "appear" half way through queuing packets. + */ + if (! ath_tx_tid_can_tx_or_sched(sc, tid)) + break; + + bf = ATH_TID_FIRST(tid); + if (bf == NULL) { + break; + } + + /* + * If the packet doesn't fall within the BAW (eg a NULL + * data frame), schedule it directly; continue. + */ + if (! bf->bf_state.bfs_dobaw) { + DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, + "%s: non-baw packet\n", + __func__); + ATH_TID_REMOVE(tid, bf, bf_list); + + if (bf->bf_state.bfs_nframes > 1) + DPRINTF(sc, ATH_DEBUG_SW_TX, + "%s: aggr=%d, nframes=%d\n", + __func__, + bf->bf_state.bfs_aggr, + bf->bf_state.bfs_nframes); + + /* + * This shouldn't happen - such frames shouldn't + * ever have been queued as an aggregate in the + * first place. However, make sure the fields + * are correctly setup just to be totally sure. + */ + bf->bf_state.bfs_aggr = 0; + bf->bf_state.bfs_nframes = 1; + + /* Update CLRDMASK just before this frame is queued */ + ath_tx_update_clrdmask(sc, tid, bf); + + ath_tx_do_ratelookup(sc, bf); + ath_tx_calc_duration(sc, bf); + ath_tx_calc_protection(sc, bf); + ath_tx_set_rtscts(sc, bf); + ath_tx_rate_fill_rcflags(sc, bf); + ath_tx_setds(sc, bf); + ath_hal_clr11n_aggr(sc->sc_ah, bf->bf_desc); + + sc->sc_aggr_stats.aggr_nonbaw_pkt++; + + /* Queue the packet; continue */ + goto queuepkt; + } + + TAILQ_INIT(&bf_q); + + /* + * Do a rate control lookup on the first frame in the + * list. The rate control code needs that to occur + * before it can determine whether to TX. + * It's inaccurate because the rate control code doesn't + * really "do" aggregate lookups, so it only considers + * the size of the first frame. + */ + ath_tx_do_ratelookup(sc, bf); + bf->bf_state.bfs_rc[3].rix = 0; + bf->bf_state.bfs_rc[3].tries = 0; + + ath_tx_calc_duration(sc, bf); + ath_tx_calc_protection(sc, bf); + + ath_tx_set_rtscts(sc, bf); + ath_tx_rate_fill_rcflags(sc, bf); + + status = ath_tx_form_aggr(sc, an, tid, &bf_q); + + DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, + "%s: ath_tx_form_aggr() status=%d\n", __func__, status); + + /* + * No frames to be picked up - out of BAW + */ + if (TAILQ_EMPTY(&bf_q)) + break; + + /* + * This assumes that the descriptor list in the ath_bufhead + * are already linked together via bf_next pointers. + */ + bf = TAILQ_FIRST(&bf_q); + + if (status == ATH_AGGR_8K_LIMITED) + sc->sc_aggr_stats.aggr_rts_aggr_limited++; + + /* + * If it's the only frame send as non-aggregate + * assume that ath_tx_form_aggr() has checked + * whether it's in the BAW and added it appropriately. + */ + if (bf->bf_state.bfs_nframes == 1) { + DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, + "%s: single-frame aggregate\n", __func__); + + /* Update CLRDMASK just before this frame is queued */ + ath_tx_update_clrdmask(sc, tid, bf); + + bf->bf_state.bfs_aggr = 0; + bf->bf_state.bfs_ndelim = 0; + ath_tx_setds(sc, bf); + ath_hal_clr11n_aggr(sc->sc_ah, bf->bf_desc); + if (status == ATH_AGGR_BAW_CLOSED) + sc->sc_aggr_stats.aggr_baw_closed_single_pkt++; + else + sc->sc_aggr_stats.aggr_single_pkt++; + } else { + DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, + "%s: multi-frame aggregate: %d frames, " + "length %d\n", + __func__, bf->bf_state.bfs_nframes, + bf->bf_state.bfs_al); + bf->bf_state.bfs_aggr = 1; + sc->sc_aggr_stats.aggr_pkts[bf->bf_state.bfs_nframes]++; + sc->sc_aggr_stats.aggr_aggr_pkt++; + + /* Update CLRDMASK just before this frame is queued */ + ath_tx_update_clrdmask(sc, tid, bf); + + /* + * Calculate the duration/protection as required. + */ + ath_tx_calc_duration(sc, bf); + ath_tx_calc_protection(sc, bf); + + /* + * Update the rate and rtscts information based on the + * rate decision made by the rate control code; + * the first frame in the aggregate needs it. + */ + ath_tx_set_rtscts(sc, bf); + + /* + * Setup the relevant descriptor fields + * for aggregation. The first descriptor + * already points to the rest in the chain. + */ + ath_tx_setds_11n(sc, bf); + + } + queuepkt: + /* Set completion handler, multi-frame aggregate or not */ + bf->bf_comp = ath_tx_aggr_comp; + + if (bf->bf_state.bfs_tid == IEEE80211_NONQOS_TID) + DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: TID=16?\n", __func__); + + /* + * Update leak count and frame config if were leaking frames. + * + * XXX TODO: it should update all frames in an aggregate + * correctly! + */ + ath_tx_leak_count_update(sc, tid, bf); + + /* Punt to txq */ + ath_tx_handoff(sc, txq, bf); + + /* Track outstanding buffer count to hardware */ + /* aggregates are "one" buffer */ + tid->hwq_depth++; + + /* + * Break out if ath_tx_form_aggr() indicated + * there can't be any further progress (eg BAW is full.) + * Checking for an empty txq is done above. + * + * XXX locking on txq here? + */ + /* XXX TXQ locking */ + if (txq->axq_aggr_depth >= sc->sc_hwq_limit_aggr || + (status == ATH_AGGR_BAW_CLOSED || + status == ATH_AGGR_LEAK_CLOSED)) + break; + } +} + +/* + * Schedule some packets from the given node/TID to the hardware. + * + * XXX TODO: this routine doesn't enforce the maximum TXQ depth. + * It just dumps frames into the TXQ. We should limit how deep + * the transmit queue can grow for frames dispatched to the given + * TXQ. + * + * To avoid locking issues, either we need to own the TXQ lock + * at this point, or we need to pass in the maximum frame count + * from the caller. + */ +void +ath_tx_tid_hw_queue_norm(struct ath_softc *sc, struct ath_node *an, + struct ath_tid *tid) +{ + struct ath_buf *bf; + struct ath_txq *txq = sc->sc_ac2q[tid->ac]; + + DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: node %p: TID %d: called\n", + __func__, an, tid->tid); + + ATH_TX_LOCK_ASSERT(sc); + + /* Check - is AMPDU pending or running? then print out something */ + if (ath_tx_ampdu_pending(sc, an, tid->tid)) + DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: tid=%d, ampdu pending?\n", + __func__, tid->tid); + if (ath_tx_ampdu_running(sc, an, tid->tid)) + DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: tid=%d, ampdu running?\n", + __func__, tid->tid); + + for (;;) { + + /* + * If the upper layers have paused the TID, don't + * queue any further packets. + * + * XXX if we are leaking frames, make sure we decrement + * that counter _and_ we continue here. + */ + if (! ath_tx_tid_can_tx_or_sched(sc, tid)) + break; + + bf = ATH_TID_FIRST(tid); + if (bf == NULL) { + break; + } + + ATH_TID_REMOVE(tid, bf, bf_list); + + /* Sanity check! */ + if (tid->tid != bf->bf_state.bfs_tid) { + DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: bfs_tid %d !=" + " tid %d\n", __func__, bf->bf_state.bfs_tid, + tid->tid); + } + /* Normal completion handler */ + bf->bf_comp = ath_tx_normal_comp; + + /* + * Override this for now, until the non-aggregate + * completion handler correctly handles software retransmits. + */ + bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK; + + /* Update CLRDMASK just before this frame is queued */ + ath_tx_update_clrdmask(sc, tid, bf); + + /* Program descriptors + rate control */ + ath_tx_do_ratelookup(sc, bf); + ath_tx_calc_duration(sc, bf); + ath_tx_calc_protection(sc, bf); + ath_tx_set_rtscts(sc, bf); + ath_tx_rate_fill_rcflags(sc, bf); + ath_tx_setds(sc, bf); + + /* + * Update the current leak count if + * we're leaking frames; and set the + * MORE flag as appropriate. + */ + ath_tx_leak_count_update(sc, tid, bf); + + /* Track outstanding buffer count to hardware */ + /* aggregates are "one" buffer */ + tid->hwq_depth++; + + /* Punt to hardware or software txq */ + ath_tx_handoff(sc, txq, bf); + } +} + +/* + * Schedule some packets to the given hardware queue. + * + * This function walks the list of TIDs (ie, ath_node TIDs + * with queued traffic) and attempts to schedule traffic + * from them. + * + * TID scheduling is implemented as a FIFO, with TIDs being + * added to the end of the queue after some frames have been + * scheduled. + */ +void +ath_txq_sched(struct ath_softc *sc, struct ath_txq *txq) +{ + struct ath_tid *tid, *next, *last; + + ATH_TX_LOCK_ASSERT(sc); + + /* + * Don't schedule if the hardware queue is busy. + * This (hopefully) gives some more time to aggregate + * some packets in the aggregation queue. + * + * XXX It doesn't stop a parallel sender from sneaking + * in transmitting a frame! + */ + /* XXX TXQ locking */ + if (txq->axq_aggr_depth + txq->fifo.axq_depth >= sc->sc_hwq_limit_aggr) { + sc->sc_aggr_stats.aggr_sched_nopkt++; + return; + } + if (txq->axq_depth >= sc->sc_hwq_limit_nonaggr) { + sc->sc_aggr_stats.aggr_sched_nopkt++; + return; + } + + last = TAILQ_LAST(&txq->axq_tidq, axq_t_s); + + TAILQ_FOREACH_SAFE(tid, &txq->axq_tidq, axq_qelem, next) { + /* + * Suspend paused queues here; they'll be resumed + * once the addba completes or times out. + */ + DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: tid=%d, paused=%d\n", + __func__, tid->tid, tid->paused); + ath_tx_tid_unsched(sc, tid); + /* + * This node may be in power-save and we're leaking + * a frame; be careful. + */ + if (! ath_tx_tid_can_tx_or_sched(sc, tid)) { + goto loop_done; + } + if (ath_tx_ampdu_running(sc, tid->an, tid->tid)) + ath_tx_tid_hw_queue_aggr(sc, tid->an, tid); + else + ath_tx_tid_hw_queue_norm(sc, tid->an, tid); + + /* Not empty? Re-schedule */ + if (tid->axq_depth != 0) + ath_tx_tid_sched(sc, tid); + + /* + * Give the software queue time to aggregate more + * packets. If we aren't running aggregation then + * we should still limit the hardware queue depth. + */ + /* XXX TXQ locking */ + if (txq->axq_aggr_depth + txq->fifo.axq_depth >= sc->sc_hwq_limit_aggr) { + break; + } + if (txq->axq_depth >= sc->sc_hwq_limit_nonaggr) { + break; + } +loop_done: + /* + * If this was the last entry on the original list, stop. + * Otherwise nodes that have been rescheduled onto the end + * of the TID FIFO list will just keep being rescheduled. + * + * XXX What should we do about nodes that were paused + * but are pending a leaking frame in response to a ps-poll? + * They'll be put at the front of the list; so they'll + * prematurely trigger this condition! Ew. + */ + if (tid == last) + break; + } +} + +/* + * TX addba handling + */ + +/* + * Return net80211 TID struct pointer, or NULL for none + */ +struct ieee80211_tx_ampdu * +ath_tx_get_tx_tid(struct ath_node *an, int tid) +{ + struct ieee80211_node *ni = &an->an_node; + struct ieee80211_tx_ampdu *tap; + + if (tid == IEEE80211_NONQOS_TID) + return NULL; + + tap = &ni->ni_tx_ampdu[tid]; + return tap; +} + +/* + * Is AMPDU-TX running? + */ +static int +ath_tx_ampdu_running(struct ath_softc *sc, struct ath_node *an, int tid) +{ + struct ieee80211_tx_ampdu *tap; + + if (tid == IEEE80211_NONQOS_TID) + return 0; + + tap = ath_tx_get_tx_tid(an, tid); + if (tap == NULL) + return 0; /* Not valid; default to not running */ + + return !! (tap->txa_flags & IEEE80211_AGGR_RUNNING); +} + +/* + * Is AMPDU-TX negotiation pending? + */ +static int +ath_tx_ampdu_pending(struct ath_softc *sc, struct ath_node *an, int tid) +{ + struct ieee80211_tx_ampdu *tap; + + if (tid == IEEE80211_NONQOS_TID) + return 0; + + tap = ath_tx_get_tx_tid(an, tid); + if (tap == NULL) + return 0; /* Not valid; default to not pending */ + + return !! (tap->txa_flags & IEEE80211_AGGR_XCHGPEND); +} + +/* + * Is AMPDU-TX pending for the given TID? + */ + + +/* + * Method to handle sending an ADDBA request. + * + * We tap this so the relevant flags can be set to pause the TID + * whilst waiting for the response. + * + * XXX there's no timeout handler we can override? + */ +int +ath_addba_request(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, + int dialogtoken, int baparamset, int batimeout) +{ + struct ath_softc *sc = ni->ni_ic->ic_softc; + int tid = tap->txa_tid; + struct ath_node *an = ATH_NODE(ni); + struct ath_tid *atid = &an->an_tid[tid]; + + /* + * XXX danger Will Robinson! + * + * Although the taskqueue may be running and scheduling some more + * packets, these should all be _before_ the addba sequence number. + * However, net80211 will keep self-assigning sequence numbers + * until addba has been negotiated. + * + * In the past, these packets would be "paused" (which still works + * fine, as they're being scheduled to the driver in the same + * serialised method which is calling the addba request routine) + * and when the aggregation session begins, they'll be dequeued + * as aggregate packets and added to the BAW. However, now there's + * a "bf->bf_state.bfs_dobaw" flag, and this isn't set for these + * packets. Thus they never get included in the BAW tracking and + * this can cause the initial burst of packets after the addba + * negotiation to "hang", as they quickly fall outside the BAW. + * + * The "eventual" solution should be to tag these packets with + * dobaw. Although net80211 has given us a sequence number, + * it'll be "after" the left edge of the BAW and thus it'll + * fall within it. + */ + ATH_TX_LOCK(sc); + /* + * This is a bit annoying. Until net80211 HT code inherits some + * (any) locking, we may have this called in parallel BUT only + * one response/timeout will be called. Grr. + */ + if (atid->addba_tx_pending == 0) { + ath_tx_tid_pause(sc, atid); + atid->addba_tx_pending = 1; + } + ATH_TX_UNLOCK(sc); + + DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, + "%s: %6D: called; dialogtoken=%d, baparamset=%d, batimeout=%d\n", + __func__, + ni->ni_macaddr, + ":", + dialogtoken, baparamset, batimeout); + DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, + "%s: txa_start=%d, ni_txseqs=%d\n", + __func__, tap->txa_start, ni->ni_txseqs[tid]); + + return sc->sc_addba_request(ni, tap, dialogtoken, baparamset, + batimeout); +} + +/* + * Handle an ADDBA response. + * + * We unpause the queue so TX'ing can resume. + * + * Any packets TX'ed from this point should be "aggregate" (whether + * aggregate or not) so the BAW is updated. + * + * Note! net80211 keeps self-assigning sequence numbers until + * ampdu is negotiated. This means the initially-negotiated BAW left + * edge won't match the ni->ni_txseq. + * + * So, being very dirty, the BAW left edge is "slid" here to match + * ni->ni_txseq. + * + * What likely SHOULD happen is that all packets subsequent to the + * addba request should be tagged as aggregate and queued as non-aggregate + * frames; thus updating the BAW. For now though, I'll just slide the + * window. + */ +int +ath_addba_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, + int status, int code, int batimeout) +{ + struct ath_softc *sc = ni->ni_ic->ic_softc; + int tid = tap->txa_tid; + struct ath_node *an = ATH_NODE(ni); + struct ath_tid *atid = &an->an_tid[tid]; + int r; + + DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, + "%s: %6D: called; status=%d, code=%d, batimeout=%d\n", __func__, + ni->ni_macaddr, + ":", + status, code, batimeout); + + DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, + "%s: txa_start=%d, ni_txseqs=%d\n", + __func__, tap->txa_start, ni->ni_txseqs[tid]); + + /* + * Call this first, so the interface flags get updated + * before the TID is unpaused. Otherwise a race condition + * exists where the unpaused TID still doesn't yet have + * IEEE80211_AGGR_RUNNING set. + */ + r = sc->sc_addba_response(ni, tap, status, code, batimeout); + + ATH_TX_LOCK(sc); + atid->addba_tx_pending = 0; + /* + * XXX dirty! + * Slide the BAW left edge to wherever net80211 left it for us. + * Read above for more information. + */ + tap->txa_start = ni->ni_txseqs[tid]; + ath_tx_tid_resume(sc, atid); + ATH_TX_UNLOCK(sc); + return r; +} + + +/* + * Stop ADDBA on a queue. + * + * This can be called whilst BAR TX is currently active on the queue, + * so make sure this is unblocked before continuing. + */ +void +ath_addba_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) +{ + struct ath_softc *sc = ni->ni_ic->ic_softc; + int tid = tap->txa_tid; + struct ath_node *an = ATH_NODE(ni); + struct ath_tid *atid = &an->an_tid[tid]; + ath_bufhead bf_cq; + struct ath_buf *bf; + + DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: %6D: called\n", + __func__, + ni->ni_macaddr, + ":"); + + /* + * Pause TID traffic early, so there aren't any races + * Unblock the pending BAR held traffic, if it's currently paused. + */ + ATH_TX_LOCK(sc); + ath_tx_tid_pause(sc, atid); + if (atid->bar_wait) { + /* + * bar_unsuspend() expects bar_tx == 1, as it should be + * called from the TX completion path. This quietens + * the warning. It's cleared for us anyway. + */ + atid->bar_tx = 1; + ath_tx_tid_bar_unsuspend(sc, atid); + } + ATH_TX_UNLOCK(sc); + + /* There's no need to hold the TXQ lock here */ + sc->sc_addba_stop(ni, tap); + + /* + * ath_tx_tid_cleanup will resume the TID if possible, otherwise + * it'll set the cleanup flag, and it'll be unpaused once + * things have been cleaned up. + */ + TAILQ_INIT(&bf_cq); + ATH_TX_LOCK(sc); + + /* + * In case there's a followup call to this, only call it + * if we don't have a cleanup in progress. + * + * Since we've paused the queue above, we need to make + * sure we unpause if there's already a cleanup in + * progress - it means something else is also doing + * this stuff, so we don't need to also keep it paused. + */ + if (atid->cleanup_inprogress) { + ath_tx_tid_resume(sc, atid); + } else { + ath_tx_tid_cleanup(sc, an, tid, &bf_cq); + /* + * Unpause the TID if no cleanup is required. + */ + if (! atid->cleanup_inprogress) + ath_tx_tid_resume(sc, atid); + } + ATH_TX_UNLOCK(sc); + + /* Handle completing frames and fail them */ + while ((bf = TAILQ_FIRST(&bf_cq)) != NULL) { + TAILQ_REMOVE(&bf_cq, bf, bf_list); + ath_tx_default_comp(sc, bf, 1); + } + +} + +/* + * Handle a node reassociation. + * + * We may have a bunch of frames queued to the hardware; those need + * to be marked as cleanup. + */ +void +ath_tx_node_reassoc(struct ath_softc *sc, struct ath_node *an) +{ + struct ath_tid *tid; + int i; + ath_bufhead bf_cq; + struct ath_buf *bf; + + TAILQ_INIT(&bf_cq); + + ATH_TX_UNLOCK_ASSERT(sc); + + ATH_TX_LOCK(sc); + for (i = 0; i < IEEE80211_TID_SIZE; i++) { + tid = &an->an_tid[i]; + if (tid->hwq_depth == 0) + continue; + DPRINTF(sc, ATH_DEBUG_NODE, + "%s: %6D: TID %d: cleaning up TID\n", + __func__, + an->an_node.ni_macaddr, + ":", + i); + /* + * In case there's a followup call to this, only call it + * if we don't have a cleanup in progress. + */ + if (! tid->cleanup_inprogress) { + ath_tx_tid_pause(sc, tid); + ath_tx_tid_cleanup(sc, an, i, &bf_cq); + /* + * Unpause the TID if no cleanup is required. + */ + if (! tid->cleanup_inprogress) + ath_tx_tid_resume(sc, tid); + } + } + ATH_TX_UNLOCK(sc); + + /* Handle completing frames and fail them */ + while ((bf = TAILQ_FIRST(&bf_cq)) != NULL) { + TAILQ_REMOVE(&bf_cq, bf, bf_list); + ath_tx_default_comp(sc, bf, 1); + } +} + +/* + * Note: net80211 bar_timeout() doesn't call this function on BAR failure; + * it simply tears down the aggregation session. Ew. + * + * It however will call ieee80211_ampdu_stop() which will call + * ic->ic_addba_stop(). + * + * XXX This uses a hard-coded max BAR count value; the whole + * XXX BAR TX success or failure should be better handled! + */ +void +ath_bar_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, + int status) +{ + struct ath_softc *sc = ni->ni_ic->ic_softc; + int tid = tap->txa_tid; + struct ath_node *an = ATH_NODE(ni); + struct ath_tid *atid = &an->an_tid[tid]; + int attempts = tap->txa_attempts; + int old_txa_start; + + DPRINTF(sc, ATH_DEBUG_SW_TX_BAR, + "%s: %6D: called; txa_tid=%d, atid->tid=%d, status=%d, attempts=%d, txa_start=%d, txa_seqpending=%d\n", + __func__, + ni->ni_macaddr, + ":", + tap->txa_tid, + atid->tid, + status, + attempts, + tap->txa_start, + tap->txa_seqpending); + + /* Note: This may update the BAW details */ + /* + * XXX What if this does slide the BAW along? We need to somehow + * XXX either fix things when it does happen, or prevent the + * XXX seqpending value to be anything other than exactly what + * XXX the hell we want! + * + * XXX So for now, how I do this inside the TX lock for now + * XXX and just correct it afterwards? The below condition should + * XXX never happen and if it does I need to fix all kinds of things. + */ + ATH_TX_LOCK(sc); + old_txa_start = tap->txa_start; + sc->sc_bar_response(ni, tap, status); + if (tap->txa_start != old_txa_start) { + device_printf(sc->sc_dev, "%s: tid=%d; txa_start=%d, old=%d, adjusting\n", + __func__, + tid, + tap->txa_start, + old_txa_start); + } + tap->txa_start = old_txa_start; + ATH_TX_UNLOCK(sc); + + /* Unpause the TID */ + /* + * XXX if this is attempt=50, the TID will be downgraded + * XXX to a non-aggregate session. So we must unpause the + * XXX TID here or it'll never be done. + * + * Also, don't call it if bar_tx/bar_wait are 0; something + * has beaten us to the punch? (XXX figure out what?) + */ + if (status == 0 || attempts == 50) { + ATH_TX_LOCK(sc); + if (atid->bar_tx == 0 || atid->bar_wait == 0) + DPRINTF(sc, ATH_DEBUG_SW_TX_BAR, + "%s: huh? bar_tx=%d, bar_wait=%d\n", + __func__, + atid->bar_tx, atid->bar_wait); + else + ath_tx_tid_bar_unsuspend(sc, atid); + ATH_TX_UNLOCK(sc); + } +} + +/* + * This is called whenever the pending ADDBA request times out. + * Unpause and reschedule the TID. + */ +void +ath_addba_response_timeout(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap) +{ + struct ath_softc *sc = ni->ni_ic->ic_softc; + int tid = tap->txa_tid; + struct ath_node *an = ATH_NODE(ni); + struct ath_tid *atid = &an->an_tid[tid]; + + DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, + "%s: %6D: TID=%d, called; resuming\n", + __func__, + ni->ni_macaddr, + ":", + tid); + + ATH_TX_LOCK(sc); + atid->addba_tx_pending = 0; + ATH_TX_UNLOCK(sc); + + /* Note: This updates the aggregate state to (again) pending */ + sc->sc_addba_response_timeout(ni, tap); + + /* Unpause the TID; which reschedules it */ + ATH_TX_LOCK(sc); + ath_tx_tid_resume(sc, atid); + ATH_TX_UNLOCK(sc); +} + +/* + * Check if a node is asleep or not. + */ +int +ath_tx_node_is_asleep(struct ath_softc *sc, struct ath_node *an) +{ + + ATH_TX_LOCK_ASSERT(sc); + + return (an->an_is_powersave); +} + +/* + * Mark a node as currently "in powersaving." + * This suspends all traffic on the node. + * + * This must be called with the node/tx locks free. + * + * XXX TODO: the locking silliness below is due to how the node + * locking currently works. Right now, the node lock is grabbed + * to do rate control lookups and these are done with the TX + * queue lock held. This means the node lock can't be grabbed + * first here or a LOR will occur. + * + * Eventually (hopefully!) the TX path code will only grab + * the TXQ lock when transmitting and the ath_node lock when + * doing node/TID operations. There are other complications - + * the sched/unsched operations involve walking the per-txq + * 'active tid' list and this requires both locks to be held. + */ +void +ath_tx_node_sleep(struct ath_softc *sc, struct ath_node *an) +{ + struct ath_tid *atid; + struct ath_txq *txq; + int tid; + + ATH_TX_UNLOCK_ASSERT(sc); + + /* Suspend all traffic on the node */ + ATH_TX_LOCK(sc); + + if (an->an_is_powersave) { + DPRINTF(sc, ATH_DEBUG_XMIT, + "%s: %6D: node was already asleep!\n", + __func__, an->an_node.ni_macaddr, ":"); + ATH_TX_UNLOCK(sc); + return; + } + + for (tid = 0; tid < IEEE80211_TID_SIZE; tid++) { + atid = &an->an_tid[tid]; + txq = sc->sc_ac2q[atid->ac]; + + ath_tx_tid_pause(sc, atid); + } + + /* Mark node as in powersaving */ + an->an_is_powersave = 1; + + ATH_TX_UNLOCK(sc); +} + +/* + * Mark a node as currently "awake." + * This resumes all traffic to the node. + */ +void +ath_tx_node_wakeup(struct ath_softc *sc, struct ath_node *an) +{ + struct ath_tid *atid; + struct ath_txq *txq; + int tid; + + ATH_TX_UNLOCK_ASSERT(sc); + + ATH_TX_LOCK(sc); + + /* !? */ + if (an->an_is_powersave == 0) { + ATH_TX_UNLOCK(sc); + DPRINTF(sc, ATH_DEBUG_XMIT, + "%s: an=%p: node was already awake\n", + __func__, an); + return; + } + + /* Mark node as awake */ + an->an_is_powersave = 0; + /* + * Clear any pending leaked frame requests + */ + an->an_leak_count = 0; + + for (tid = 0; tid < IEEE80211_TID_SIZE; tid++) { + atid = &an->an_tid[tid]; + txq = sc->sc_ac2q[atid->ac]; + + ath_tx_tid_resume(sc, atid); + } + ATH_TX_UNLOCK(sc); +} + +static int +ath_legacy_dma_txsetup(struct ath_softc *sc) +{ + + /* nothing new needed */ + return (0); +} + +static int +ath_legacy_dma_txteardown(struct ath_softc *sc) +{ + + /* nothing new needed */ + return (0); +} + +void +ath_xmit_setup_legacy(struct ath_softc *sc) +{ + /* + * For now, just set the descriptor length to sizeof(ath_desc); + * worry about extracting the real length out of the HAL later. + */ + sc->sc_tx_desclen = sizeof(struct ath_desc); + sc->sc_tx_statuslen = sizeof(struct ath_desc); + sc->sc_tx_nmaps = 1; /* only one buffer per TX desc */ + + sc->sc_tx.xmit_setup = ath_legacy_dma_txsetup; + sc->sc_tx.xmit_teardown = ath_legacy_dma_txteardown; + sc->sc_tx.xmit_attach_comp_func = ath_legacy_attach_comp_func; + + sc->sc_tx.xmit_dma_restart = ath_legacy_tx_dma_restart; + sc->sc_tx.xmit_handoff = ath_legacy_xmit_handoff; + + sc->sc_tx.xmit_drain = ath_legacy_tx_drain; +} diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_tx.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_tx.h index e181f7a48d..2b9d61b34f 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_tx.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_tx.h @@ -26,12 +26,67 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/if_ath_tx.h 289162 2015-10-12 03:27:08Z adrian $ */ #ifndef __IF_ATH_TX_H__ #define __IF_ATH_TX_H__ -extern void ath_freetx(struct mbuf *m); +/* + * some general macros + */ +#define INCR(_l, _sz) (_l) ++; (_l) &= ((_sz) - 1) +/* + * return block-ack bitmap index given sequence and starting sequence + */ +#define ATH_BA_INDEX(_st, _seq) (((_seq) - (_st)) & (IEEE80211_SEQ_RANGE - 1)) + +#define WME_BA_BMP_SIZE 64 +#define WME_MAX_BA WME_BA_BMP_SIZE + +/* + * How 'busy' to try and keep the hardware txq + */ +#define ATH_AGGR_MIN_QDEPTH 2 +#define ATH_NONAGGR_MIN_QDEPTH 32 + +/* + * Watermark for scheduling TIDs in order to maximise aggregation. + * + * If hwq_depth is greater than this, don't schedule the TID + * for packet scheduling - the hardware is already busy servicing + * this TID. + * + * If hwq_depth is less than this, schedule the TID for packet + * scheduling in the completion handler. + */ +#define ATH_AGGR_SCHED_HIGH 4 +#define ATH_AGGR_SCHED_LOW 2 + +/* + * return whether a bit at index _n in bitmap _bm is set + * _sz is the size of the bitmap + */ +#define ATH_BA_ISSET(_bm, _n) (((_n) < (WME_BA_BMP_SIZE)) && \ + ((_bm)[(_n) >> 5] & (1 << ((_n) & 31)))) + + +/* extracting the seqno from buffer seqno */ +#define SEQNO(_a) ((_a) >> IEEE80211_SEQ_SEQ_SHIFT) + +/* + * Whether the current sequence number is within the + * BAW. + */ +#define BAW_WITHIN(_start, _bawsz, _seqno) \ + ((((_seqno) - (_start)) & 4095) < (_bawsz)) + +/* + * Maximum aggregate size + */ +#define ATH_AGGR_MAXSIZE 65530 + +extern void ath_tx_node_flush(struct ath_softc *sc, struct ath_node *an); +extern void ath_tx_txq_drain(struct ath_softc *sc, struct ath_txq *txq); extern void ath_txfrag_cleanup(struct ath_softc *sc, ath_bufhead *frags, struct ieee80211_node *ni); extern int ath_txfrag_setup(struct ath_softc *sc, ath_bufhead *frags, @@ -41,4 +96,73 @@ extern int ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, extern int ath_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params); +/* software queue stuff */ +extern void ath_tx_swq(struct ath_softc *sc, struct ieee80211_node *ni, + struct ath_txq *txq, int queue_to_head, struct ath_buf *bf); +extern void ath_tx_tid_init(struct ath_softc *sc, struct ath_node *an); +extern void ath_tx_tid_hw_queue_aggr(struct ath_softc *sc, struct ath_node *an, + struct ath_tid *tid); +extern void ath_tx_tid_hw_queue_norm(struct ath_softc *sc, struct ath_node *an, + struct ath_tid *tid); +extern void ath_txq_sched(struct ath_softc *sc, struct ath_txq *txq); +extern void ath_tx_normal_comp(struct ath_softc *sc, struct ath_buf *bf, + int fail); +extern void ath_tx_aggr_comp(struct ath_softc *sc, struct ath_buf *bf, + int fail); +extern void ath_tx_addto_baw(struct ath_softc *sc, struct ath_node *an, + struct ath_tid *tid, struct ath_buf *bf); +extern struct ieee80211_tx_ampdu * ath_tx_get_tx_tid(struct ath_node *an, + int tid); +extern void ath_tx_tid_sched(struct ath_softc *sc, struct ath_tid *tid); + +/* TX addba handling */ +extern int ath_addba_request(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap, int dialogtoken, + int baparamset, int batimeout); +extern int ath_addba_response(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap, int dialogtoken, + int code, int batimeout); +extern void ath_addba_stop(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap); +extern void ath_bar_response(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap, int status); +extern void ath_addba_response_timeout(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap); + +/* + * AP mode power save handling (of stations) + */ +extern void ath_tx_node_sleep(struct ath_softc *sc, struct ath_node *an); +extern void ath_tx_node_wakeup(struct ath_softc *sc, struct ath_node *an); +extern int ath_tx_node_is_asleep(struct ath_softc *sc, struct ath_node *an); +extern void ath_tx_node_reassoc(struct ath_softc *sc, struct ath_node *an); + +/* + * Hardware queue stuff + */ +extern void ath_tx_push_pending(struct ath_softc *sc, struct ath_txq *txq); + +/* + * Misc debugging stuff + */ +#ifdef ATH_DEBUG_ALQ +extern void ath_tx_alq_post(struct ath_softc *sc, struct ath_buf *bf_first); +#endif /* ATH_DEBUG_ALQ */ + +/* + * Setup path + */ +#define ath_txdma_setup(_sc) \ + (_sc)->sc_tx.xmit_setup(_sc) +#define ath_txdma_teardown(_sc) \ + (_sc)->sc_tx.xmit_teardown(_sc) +#define ath_txq_restart_dma(_sc, _txq) \ + (_sc)->sc_tx.xmit_dma_restart((_sc), (_txq)) +#define ath_tx_handoff(_sc, _txq, _bf) \ + (_sc)->sc_tx.xmit_handoff((_sc), (_txq), (_bf)) +#define ath_draintxq(_sc, _rtype) \ + (_sc)->sc_tx.xmit_drain((_sc), (_rtype)) + +extern void ath_xmit_setup_legacy(struct ath_softc *sc); + #endif diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_tx_edma.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_tx_edma.c new file mode 100644 index 0000000000..0532618df2 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_tx_edma.c @@ -0,0 +1,1036 @@ +/*- + * Copyright (c) 2012 Adrian Chadd + * 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 +__FBSDID("$FreeBSD: releng/11.1/sys/dev/ath/if_ath_tx_edma.c 302060 2016-06-21 15:38:20Z adrian $"); + +/* + * 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" +/* + * This is needed for register operations which are performed + * by the driver - eg, calls to ath_hal_gettsf32(). + * + * It's also required for any AH_DEBUG checks in here, eg the + * module dependencies. + */ +#include "opt_ah.h" +#include "opt_wlan.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for mp_ncpus */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#ifdef IEEE80211_SUPPORT_SUPERG +#include +#endif +#ifdef IEEE80211_SUPPORT_TDMA +#include +#endif + +#include + +#ifdef INET +#include +#include +#endif + +#include +#include /* XXX for softled */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef ATH_TX99_DIAG +#include +#endif + +#include + +#ifdef ATH_DEBUG_ALQ +#include +#endif + +/* + * some general macros + */ +#define INCR(_l, _sz) (_l) ++; (_l) &= ((_sz) - 1) +#define DECR(_l, _sz) (_l) --; (_l) &= ((_sz) - 1) + +/* + * XXX doesn't belong here, and should be tunable + */ +#define ATH_TXSTATUS_RING_SIZE 512 + +MALLOC_DECLARE(M_ATHDEV); + +static void ath_edma_tx_processq(struct ath_softc *sc, int dosched); + +#ifdef ATH_DEBUG_ALQ +static void +ath_tx_alq_edma_push(struct ath_softc *sc, int txq, int nframes, + int fifo_depth, int frame_cnt) +{ + struct if_ath_alq_tx_fifo_push aq; + + aq.txq = htobe32(txq); + aq.nframes = htobe32(nframes); + aq.fifo_depth = htobe32(fifo_depth); + aq.frame_cnt = htobe32(frame_cnt); + + if_ath_alq_post(&sc->sc_alq, ATH_ALQ_TX_FIFO_PUSH, + sizeof(aq), + (const char *) &aq); +} +#endif /* ATH_DEBUG_ALQ */ + +/* + * XXX TODO: push an aggregate as a single FIFO slot, even though + * it may not meet the TXOP for say, DBA-gated traffic in TDMA mode. + * + * The TX completion code handles a TX FIFO slot having multiple frames, + * aggregate or otherwise, but it may just make things easier to deal + * with. + * + * XXX TODO: track the number of aggregate subframes and put that in the + * push alq message. + */ +static void +ath_tx_edma_push_staging_list(struct ath_softc *sc, struct ath_txq *txq, + int limit) +{ + struct ath_buf *bf, *bf_last; + struct ath_buf *bfi, *bfp; + int i, sqdepth; + TAILQ_HEAD(axq_q_f_s, ath_buf) sq; + + ATH_TXQ_LOCK_ASSERT(txq); + + /* + * Don't bother doing any work if it's full. + */ + if (txq->axq_fifo_depth >= HAL_TXFIFO_DEPTH) + return; + + if (TAILQ_EMPTY(&txq->axq_q)) + return; + + TAILQ_INIT(&sq); + + /* + * First pass - walk sq, queue up to 'limit' entries, + * subtract them from the staging queue. + */ + sqdepth = 0; + for (i = 0; i < limit; i++) { + /* Grab the head entry */ + bf = ATH_TXQ_FIRST(txq); + if (bf == NULL) + break; + ATH_TXQ_REMOVE(txq, bf, bf_list); + + /* Queue it into our staging list */ + TAILQ_INSERT_TAIL(&sq, bf, bf_list); + + /* Ensure the flags are cleared */ + bf->bf_flags &= ~(ATH_BUF_FIFOPTR | ATH_BUF_FIFOEND); + sqdepth++; + } + + /* + * Ok, so now we have a staging list of up to 'limit' + * frames from the txq. Now let's wrap that up + * into its own list and pass that to the hardware + * as one FIFO entry. + */ + + bf = TAILQ_FIRST(&sq); + bf_last = TAILQ_LAST(&sq, axq_q_s); + + /* + * Ok, so here's the gymnastics reqiured to make this + * all sensible. + */ + + /* + * Tag the first/last buffer appropriately. + */ + bf->bf_flags |= ATH_BUF_FIFOPTR; + bf_last->bf_flags |= ATH_BUF_FIFOEND; + + /* + * Walk the descriptor list and link them appropriately. + */ + bfp = NULL; + TAILQ_FOREACH(bfi, &sq, bf_list) { + if (bfp != NULL) { + ath_hal_settxdesclink(sc->sc_ah, bfp->bf_lastds, + bfi->bf_daddr); + } + bfp = bfi; + } + + i = 0; + TAILQ_FOREACH(bfi, &sq, bf_list) { +#ifdef ATH_DEBUG + if (sc->sc_debug & ATH_DEBUG_XMIT_DESC) + ath_printtxbuf(sc, bfi, txq->axq_qnum, i, 0); +#endif/* ATH_DEBUG */ +#ifdef ATH_DEBUG_ALQ + if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_EDMA_TXDESC)) + ath_tx_alq_post(sc, bfi); +#endif /* ATH_DEBUG_ALQ */ + i++; + } + + /* + * We now need to push this set of frames onto the tail + * of the FIFO queue. We don't adjust the aggregate + * count, only the queue depth counter(s). + * We also need to blank the link pointer now. + */ + + TAILQ_CONCAT(&txq->fifo.axq_q, &sq, bf_list); + /* Bump total queue tracking in FIFO queue */ + txq->fifo.axq_depth += sqdepth; + + /* Bump FIFO queue */ + txq->axq_fifo_depth++; + DPRINTF(sc, ATH_DEBUG_XMIT, + "%s: queued %d packets; depth=%d, fifo depth=%d\n", + __func__, sqdepth, txq->fifo.axq_depth, txq->axq_fifo_depth); + + /* Push the first entry into the hardware */ + ath_hal_puttxbuf(sc->sc_ah, txq->axq_qnum, bf->bf_daddr); + + /* Push start on the DMA if it's not already started */ + ath_hal_txstart(sc->sc_ah, txq->axq_qnum); + +#ifdef ATH_DEBUG_ALQ + ath_tx_alq_edma_push(sc, txq->axq_qnum, sqdepth, + txq->axq_fifo_depth, + txq->fifo.axq_depth); +#endif /* ATH_DEBUG_ALQ */ +} + +#define TX_BATCH_SIZE 32 + +/* + * Push some frames into the TX FIFO if we have space. + */ +static void +ath_edma_tx_fifo_fill(struct ath_softc *sc, struct ath_txq *txq) +{ + + ATH_TXQ_LOCK_ASSERT(txq); + + DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: Q%d: called\n", + __func__, + txq->axq_qnum); + + /* + * For now, push up to 4 frames per TX FIFO slot. + * If more are in the hardware queue then they'll + * get populated when we try to send another frame + * or complete a frame - so at most there'll be + * 32 non-AMPDU frames per TXQ. + * + * Note that the hardware staging queue will limit + * how many frames in total we will have pushed into + * here. + * + * Later on, we'll want to push less frames into + * the TX FIFO since we don't want to necessarily + * fill tens or hundreds of milliseconds of potential + * frames. + * + * However, we need more frames right now because of + * how the MAC implements the frame scheduling policy. + * It only ungates a single FIFO entry at a time, + * and will run that until CHNTIME expires or the + * end of that FIFO entry descriptor list is reached. + * So for TDMA we suffer a big performance penalty - + * single TX FIFO entries mean the MAC only sends out + * one frame per DBA event, which turned out on average + * 6ms per TX frame. + * + * So, for aggregates it's okay - it'll push two at a + * time and this will just do them more efficiently. + * For non-aggregates it'll do 4 at a time, up to the + * non-aggr limit (non_aggr, which is 32.) They should + * be time based rather than a hard count, but I also + * do need sleep. + */ + + /* + * Do some basic, basic batching to the hardware + * queue. + * + * If we have TX_BATCH_SIZE entries in the staging + * queue, then let's try to send them all in one hit. + * + * Ensure we don't push more than TX_BATCH_SIZE worth + * in, otherwise we end up draining 8 slots worth of + * 32 frames into the hardware queue and then we don't + * attempt to push more frames in until we empty the + * FIFO. + */ + if (txq->axq_depth >= TX_BATCH_SIZE / 2 && + txq->fifo.axq_depth <= TX_BATCH_SIZE) { + ath_tx_edma_push_staging_list(sc, txq, TX_BATCH_SIZE); + } + + /* + * Aggregate check: if we have less than two FIFO slots + * busy and we have some aggregate frames, queue it. + * + * Now, ideally we'd just check to see if the scheduler + * has given us aggregate frames and push them into the FIFO + * as individual slots, as honestly we should just be pushing + * a single aggregate in as one FIFO slot. + * + * Let's do that next once I know this works. + */ + else if (txq->axq_aggr_depth > 0 && txq->axq_fifo_depth < 2) + ath_tx_edma_push_staging_list(sc, txq, TX_BATCH_SIZE); + + /* + * + * If we have less, and the TXFIFO isn't empty, let's + * wait until we've finished sending the FIFO. + * + * If we have less, and the TXFIFO is empty, then + * send them. + */ + else if (txq->axq_fifo_depth == 0) { + ath_tx_edma_push_staging_list(sc, txq, TX_BATCH_SIZE); + } +} + +/* + * Re-initialise the DMA FIFO with the current contents of + * said TXQ. + * + * This should only be called as part of the chip reset path, as it + * assumes the FIFO is currently empty. + */ +static void +ath_edma_dma_restart(struct ath_softc *sc, struct ath_txq *txq) +{ + struct ath_buf *bf; + int i = 0; + int fifostart = 1; + int old_fifo_depth; + + DPRINTF(sc, ATH_DEBUG_RESET, "%s: Q%d: called\n", + __func__, + txq->axq_qnum); + + ATH_TXQ_LOCK_ASSERT(txq); + + /* + * Let's log if the tracked FIFO depth doesn't match + * what we actually push in. + */ + old_fifo_depth = txq->axq_fifo_depth; + txq->axq_fifo_depth = 0; + + /* + * Walk the FIFO staging list, looking for "head" entries. + * Since we may have a partially completed list of frames, + * we push the first frame we see into the FIFO and re-mark + * it as the head entry. We then skip entries until we see + * FIFO end, at which point we get ready to push another + * entry into the FIFO. + */ + TAILQ_FOREACH(bf, &txq->fifo.axq_q, bf_list) { + /* + * If we're looking for FIFOEND and we haven't found + * it, skip. + * + * If we're looking for FIFOEND and we've found it, + * reset for another descriptor. + */ +#ifdef ATH_DEBUG + if (sc->sc_debug & ATH_DEBUG_XMIT_DESC) + ath_printtxbuf(sc, bf, txq->axq_qnum, i, 0); +#endif/* ATH_DEBUG */ +#ifdef ATH_DEBUG_ALQ + if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_EDMA_TXDESC)) + ath_tx_alq_post(sc, bf); +#endif /* ATH_DEBUG_ALQ */ + + if (fifostart == 0) { + if (bf->bf_flags & ATH_BUF_FIFOEND) + fifostart = 1; + continue; + } + + /* Make sure we're not overflowing the FIFO! */ + if (txq->axq_fifo_depth >= HAL_TXFIFO_DEPTH) { + device_printf(sc->sc_dev, + "%s: Q%d: more frames in the queue; FIFO depth=%d?!\n", + __func__, + txq->axq_qnum, + txq->axq_fifo_depth); + } + +#if 0 + DPRINTF(sc, ATH_DEBUG_RESET, + "%s: Q%d: depth=%d: pushing bf=%p; start=%d, end=%d\n", + __func__, + txq->axq_qnum, + txq->axq_fifo_depth, + bf, + !! (bf->bf_flags & ATH_BUF_FIFOPTR), + !! (bf->bf_flags & ATH_BUF_FIFOEND)); +#endif + + /* + * Set this to be the first buffer in the FIFO + * list - even if it's also the last buffer in + * a FIFO list! + */ + bf->bf_flags |= ATH_BUF_FIFOPTR; + + /* Push it into the FIFO and bump the FIFO count */ + ath_hal_puttxbuf(sc->sc_ah, txq->axq_qnum, bf->bf_daddr); + txq->axq_fifo_depth++; + + /* + * If this isn't the last entry either, let's + * clear fifostart so we continue looking for + * said last entry. + */ + if (! (bf->bf_flags & ATH_BUF_FIFOEND)) + fifostart = 0; + i++; + } + + /* Only bother starting the queue if there's something in it */ + if (i > 0) + ath_hal_txstart(sc->sc_ah, txq->axq_qnum); + + DPRINTF(sc, ATH_DEBUG_RESET, "%s: Q%d: FIFO depth was %d, is %d\n", + __func__, + txq->axq_qnum, + old_fifo_depth, + txq->axq_fifo_depth); + + /* And now, let's check! */ + if (txq->axq_fifo_depth != old_fifo_depth) { + device_printf(sc->sc_dev, + "%s: Q%d: FIFO depth should be %d, is %d\n", + __func__, + txq->axq_qnum, + old_fifo_depth, + txq->axq_fifo_depth); + } +} + +/* + * Hand off this frame to a hardware queue. + * + * Things are a bit hairy in the EDMA world. The TX FIFO is only + * 8 entries deep, so we need to keep track of exactly what we've + * pushed into the FIFO and what's just sitting in the TX queue, + * waiting to go out. + * + * So this is split into two halves - frames get appended to the + * TXQ; then a scheduler is called to push some frames into the + * actual TX FIFO. + */ +static void +ath_edma_xmit_handoff_hw(struct ath_softc *sc, struct ath_txq *txq, + struct ath_buf *bf) +{ + + ATH_TXQ_LOCK(txq); + + KASSERT((bf->bf_flags & ATH_BUF_BUSY) == 0, + ("%s: busy status 0x%x", __func__, bf->bf_flags)); + + /* + * XXX TODO: write a hard-coded check to ensure that + * the queue id in the TX descriptor matches txq->axq_qnum. + */ + + /* Update aggr stats */ + if (bf->bf_state.bfs_aggr) + txq->axq_aggr_depth++; + + /* Push and update frame stats */ + ATH_TXQ_INSERT_TAIL(txq, bf, bf_list); + + /* + * Finally, call the FIFO schedule routine to schedule some + * frames to the FIFO. + */ + ath_edma_tx_fifo_fill(sc, txq); + ATH_TXQ_UNLOCK(txq); +} + +/* + * Hand off this frame to a multicast software queue. + * + * The EDMA TX CABQ will get a list of chained frames, chained + * together using the next pointer. The single head of that + * particular queue is pushed to the hardware CABQ. + */ +static void +ath_edma_xmit_handoff_mcast(struct ath_softc *sc, struct ath_txq *txq, + struct ath_buf *bf) +{ + + ATH_TX_LOCK_ASSERT(sc); + KASSERT((bf->bf_flags & ATH_BUF_BUSY) == 0, + ("%s: busy status 0x%x", __func__, bf->bf_flags)); + + ATH_TXQ_LOCK(txq); + /* + * XXX this is mostly duplicated in ath_tx_handoff_mcast(). + */ + if (ATH_TXQ_LAST(txq, axq_q_s) != NULL) { + struct ath_buf *bf_last = ATH_TXQ_LAST(txq, axq_q_s); + struct ieee80211_frame *wh; + + /* mark previous frame */ + wh = mtod(bf_last->bf_m, struct ieee80211_frame *); + wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA; + + /* re-sync buffer to memory */ + bus_dmamap_sync(sc->sc_dmat, bf_last->bf_dmamap, + BUS_DMASYNC_PREWRITE); + + /* link descriptor */ + ath_hal_settxdesclink(sc->sc_ah, + bf_last->bf_lastds, + bf->bf_daddr); + } +#ifdef ATH_DEBUG_ALQ + if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_EDMA_TXDESC)) + ath_tx_alq_post(sc, bf); +#endif /* ATH_DEBUG_ALQ */ + ATH_TXQ_INSERT_TAIL(txq, bf, bf_list); + ATH_TXQ_UNLOCK(txq); +} + +/* + * Handoff this frame to the hardware. + * + * For the multicast queue, this will treat it as a software queue + * and append it to the list, after updating the MORE_DATA flag + * in the previous frame. The cabq processing code will ensure + * that the queue contents gets transferred over. + * + * For the hardware queues, this will queue a frame to the queue + * like before, then populate the FIFO from that. Since the + * EDMA hardware has 8 FIFO slots per TXQ, this ensures that + * frames such as management frames don't get prematurely dropped. + * + * This does imply that a similar flush-hwq-to-fifoq method will + * need to be called from the processq function, before the + * per-node software scheduler is called. + */ +static void +ath_edma_xmit_handoff(struct ath_softc *sc, struct ath_txq *txq, + struct ath_buf *bf) +{ + + DPRINTF(sc, ATH_DEBUG_XMIT_DESC, + "%s: called; bf=%p, txq=%p, qnum=%d\n", + __func__, + bf, + txq, + txq->axq_qnum); + + if (txq->axq_qnum == ATH_TXQ_SWQ) + ath_edma_xmit_handoff_mcast(sc, txq, bf); + else + ath_edma_xmit_handoff_hw(sc, txq, bf); +} + +static int +ath_edma_setup_txfifo(struct ath_softc *sc, int qnum) +{ + struct ath_tx_edma_fifo *te = &sc->sc_txedma[qnum]; + + te->m_fifo = malloc(sizeof(struct ath_buf *) * HAL_TXFIFO_DEPTH, + M_ATHDEV, + M_NOWAIT | M_ZERO); + if (te->m_fifo == NULL) { + device_printf(sc->sc_dev, "%s: malloc failed\n", + __func__); + return (-ENOMEM); + } + + /* + * Set initial "empty" state. + */ + te->m_fifo_head = te->m_fifo_tail = te->m_fifo_depth = 0; + + return (0); +} + +static int +ath_edma_free_txfifo(struct ath_softc *sc, int qnum) +{ + struct ath_tx_edma_fifo *te = &sc->sc_txedma[qnum]; + + /* XXX TODO: actually deref the ath_buf entries? */ + free(te->m_fifo, M_ATHDEV); + return (0); +} + +static int +ath_edma_dma_txsetup(struct ath_softc *sc) +{ + int error; + int i; + + error = ath_descdma_alloc_desc(sc, &sc->sc_txsdma, + NULL, "txcomp", sc->sc_tx_statuslen, ATH_TXSTATUS_RING_SIZE); + if (error != 0) + return (error); + + ath_hal_setuptxstatusring(sc->sc_ah, + (void *) sc->sc_txsdma.dd_desc, + sc->sc_txsdma.dd_desc_paddr, + ATH_TXSTATUS_RING_SIZE); + + for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { + ath_edma_setup_txfifo(sc, i); + } + + return (0); +} + +static int +ath_edma_dma_txteardown(struct ath_softc *sc) +{ + int i; + + for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { + ath_edma_free_txfifo(sc, i); + } + + ath_descdma_cleanup(sc, &sc->sc_txsdma, NULL); + return (0); +} + +/* + * Drain all TXQs, potentially after completing the existing completed + * frames. + */ +static void +ath_edma_tx_drain(struct ath_softc *sc, ATH_RESET_TYPE reset_type) +{ + int i; + + DPRINTF(sc, ATH_DEBUG_RESET, "%s: called\n", __func__); + + (void) ath_stoptxdma(sc); + + /* + * If reset type is noloss, the TX FIFO needs to be serviced + * and those frames need to be handled. + * + * Otherwise, just toss everything in each TX queue. + */ + if (reset_type == ATH_RESET_NOLOSS) { + ath_edma_tx_processq(sc, 0); + for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { + if (ATH_TXQ_SETUP(sc, i)) { + ATH_TXQ_LOCK(&sc->sc_txq[i]); + /* + * Free the holding buffer; DMA is now + * stopped. + */ + ath_txq_freeholdingbuf(sc, &sc->sc_txq[i]); + /* + * Reset the link pointer to NULL; there's + * no frames to chain DMA to. + */ + sc->sc_txq[i].axq_link = NULL; + ATH_TXQ_UNLOCK(&sc->sc_txq[i]); + } + } + } else { + for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { + if (ATH_TXQ_SETUP(sc, i)) + ath_tx_draintxq(sc, &sc->sc_txq[i]); + } + } + + /* XXX dump out the TX completion FIFO contents */ + + /* XXX dump out the frames */ + + sc->sc_wd_timer = 0; +} + +/* + * TX completion tasklet. + */ + +static void +ath_edma_tx_proc(void *arg, int npending) +{ + struct ath_softc *sc = (struct ath_softc *) arg; + +#if 0 + DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: called, npending=%d\n", + __func__, npending); +#endif + ath_edma_tx_processq(sc, 1); +} + +/* + * Process the TX status queue. + */ +static void +ath_edma_tx_processq(struct ath_softc *sc, int dosched) +{ + struct ath_hal *ah = sc->sc_ah; + HAL_STATUS status; + struct ath_tx_status ts; + struct ath_txq *txq; + struct ath_buf *bf; + struct ieee80211_node *ni; + int nacked = 0; + int idx; + int i; + +#ifdef ATH_DEBUG + /* XXX */ + uint32_t txstatus[32]; +#endif + + for (idx = 0; ; idx++) { + bzero(&ts, sizeof(ts)); + + ATH_TXSTATUS_LOCK(sc); +#ifdef ATH_DEBUG + ath_hal_gettxrawtxdesc(ah, txstatus); +#endif + status = ath_hal_txprocdesc(ah, NULL, (void *) &ts); + ATH_TXSTATUS_UNLOCK(sc); + + if (status == HAL_EINPROGRESS) + break; + +#ifdef ATH_DEBUG + if (sc->sc_debug & ATH_DEBUG_TX_PROC) + if (ts.ts_queue_id != sc->sc_bhalq) + ath_printtxstatbuf(sc, NULL, txstatus, ts.ts_queue_id, + idx, (status == HAL_OK)); +#endif + + /* + * If there is an error with this descriptor, continue + * processing. + * + * XXX TBD: log some statistics? + */ + if (status == HAL_EIO) { + device_printf(sc->sc_dev, "%s: invalid TX status?\n", + __func__); + break; + } + +#if defined(ATH_DEBUG_ALQ) && defined(ATH_DEBUG) + if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_EDMA_TXSTATUS)) + if_ath_alq_post(&sc->sc_alq, ATH_ALQ_EDMA_TXSTATUS, + sc->sc_tx_statuslen, + (char *) txstatus); +#endif /* ATH_DEBUG_ALQ */ + + /* + * At this point we have a valid status descriptor. + * The QID and descriptor ID (which currently isn't set) + * is part of the status. + * + * We then assume that the descriptor in question is the + * -head- of the given QID. Eventually we should verify + * this by using the descriptor ID. + */ + + /* + * The beacon queue is not currently a "real" queue. + * Frames aren't pushed onto it and the lock isn't setup. + * So skip it for now; the beacon handling code will + * free and alloc more beacon buffers as appropriate. + */ + if (ts.ts_queue_id == sc->sc_bhalq) + continue; + + txq = &sc->sc_txq[ts.ts_queue_id]; + + ATH_TXQ_LOCK(txq); + bf = ATH_TXQ_FIRST(&txq->fifo); + + /* + * Work around the situation where I'm seeing notifications + * for Q1 when no frames are available. That needs to be + * debugged but not by crashing _here_. + */ + if (bf == NULL) { + device_printf(sc->sc_dev, "%s: Q%d: empty?\n", + __func__, + ts.ts_queue_id); + ATH_TXQ_UNLOCK(txq); + continue; + } + + DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: Q%d, bf=%p, start=%d, end=%d\n", + __func__, + ts.ts_queue_id, bf, + !! (bf->bf_flags & ATH_BUF_FIFOPTR), + !! (bf->bf_flags & ATH_BUF_FIFOEND)); + + /* XXX TODO: actually output debugging info about this */ + +#if 0 + /* XXX assert the buffer/descriptor matches the status descid */ + if (ts.ts_desc_id != bf->bf_descid) { + device_printf(sc->sc_dev, + "%s: mismatched descid (qid=%d, tsdescid=%d, " + "bfdescid=%d\n", + __func__, + ts.ts_queue_id, + ts.ts_desc_id, + bf->bf_descid); + } +#endif + + /* This removes the buffer and decrements the queue depth */ + ATH_TXQ_REMOVE(&txq->fifo, bf, bf_list); + if (bf->bf_state.bfs_aggr) + txq->axq_aggr_depth--; + + /* + * If this was the end of a FIFO set, decrement FIFO depth + */ + if (bf->bf_flags & ATH_BUF_FIFOEND) + txq->axq_fifo_depth--; + + /* + * If this isn't the final buffer in a FIFO set, mark + * the buffer as busy so it goes onto the holding queue. + */ + if (! (bf->bf_flags & ATH_BUF_FIFOEND)) + bf->bf_flags |= ATH_BUF_BUSY; + + DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: Q%d: FIFO depth is now %d (%d)\n", + __func__, + txq->axq_qnum, + txq->axq_fifo_depth, + txq->fifo.axq_depth); + + /* XXX assert FIFO depth >= 0 */ + ATH_TXQ_UNLOCK(txq); + + /* + * Outside of the TX lock - if the buffer is end + * end buffer in this FIFO, we don't need a holding + * buffer any longer. + */ + if (bf->bf_flags & ATH_BUF_FIFOEND) { + ATH_TXQ_LOCK(txq); + ath_txq_freeholdingbuf(sc, txq); + ATH_TXQ_UNLOCK(txq); + } + + /* + * First we need to make sure ts_rate is valid. + * + * Pre-EDMA chips pass the whole TX descriptor to + * the proctxdesc function which will then fill out + * ts_rate based on the ts_finaltsi (final TX index) + * in the TX descriptor. However the TX completion + * FIFO doesn't have this information. So here we + * do a separate HAL call to populate that information. + * + * The same problem exists with ts_longretry. + * The FreeBSD HAL corrects ts_longretry in the HAL layer; + * the AR9380 HAL currently doesn't. So until the HAL + * is imported and this can be added, we correct for it + * here. + */ + /* XXX TODO */ + /* XXX faked for now. Ew. */ + if (ts.ts_finaltsi < 4) { + ts.ts_rate = + bf->bf_state.bfs_rc[ts.ts_finaltsi].ratecode; + switch (ts.ts_finaltsi) { + case 3: ts.ts_longretry += + bf->bf_state.bfs_rc[2].tries; + case 2: ts.ts_longretry += + bf->bf_state.bfs_rc[1].tries; + case 1: ts.ts_longretry += + bf->bf_state.bfs_rc[0].tries; + } + } else { + device_printf(sc->sc_dev, "%s: finaltsi=%d\n", + __func__, + ts.ts_finaltsi); + ts.ts_rate = bf->bf_state.bfs_rc[0].ratecode; + } + + /* + * XXX This is terrible. + * + * Right now, some code uses the TX status that is + * passed in here, but the completion handlers in the + * software TX path also use bf_status.ds_txstat. + * Ew. That should all go away. + * + * XXX It's also possible the rate control completion + * routine is called twice. + */ + memcpy(&bf->bf_status, &ts, sizeof(ts)); + + ni = bf->bf_node; + + /* Update RSSI */ + /* XXX duplicate from ath_tx_processq */ + if (ni != NULL && ts.ts_status == 0 && + ((bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 0)) { + nacked++; + sc->sc_stats.ast_tx_rssi = ts.ts_rssi; + ATH_RSSI_LPF(sc->sc_halstats.ns_avgtxrssi, + ts.ts_rssi); + } + + /* Handle frame completion and rate control update */ + ath_tx_process_buf_completion(sc, txq, &ts, bf); + + /* NB: bf is invalid at this point */ + } + + sc->sc_wd_timer = 0; + + /* + * XXX It's inefficient to do this if the FIFO queue is full, + * but there's no easy way right now to only populate + * the txq task for _one_ TXQ. This should be fixed. + */ + if (dosched) { + /* Attempt to schedule more hardware frames to the TX FIFO */ + for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { + if (ATH_TXQ_SETUP(sc, i)) { + ATH_TXQ_LOCK(&sc->sc_txq[i]); + ath_edma_tx_fifo_fill(sc, &sc->sc_txq[i]); + ATH_TXQ_UNLOCK(&sc->sc_txq[i]); + } + } + /* Kick software scheduler */ + ath_tx_swq_kick(sc); + } +} + +static void +ath_edma_attach_comp_func(struct ath_softc *sc) +{ + + TASK_INIT(&sc->sc_txtask, 0, ath_edma_tx_proc, sc); +} + +void +ath_xmit_setup_edma(struct ath_softc *sc) +{ + + /* Fetch EDMA field and buffer sizes */ + (void) ath_hal_gettxdesclen(sc->sc_ah, &sc->sc_tx_desclen); + (void) ath_hal_gettxstatuslen(sc->sc_ah, &sc->sc_tx_statuslen); + (void) ath_hal_getntxmaps(sc->sc_ah, &sc->sc_tx_nmaps); + + if (bootverbose) { + device_printf(sc->sc_dev, "TX descriptor length: %d\n", + sc->sc_tx_desclen); + device_printf(sc->sc_dev, "TX status length: %d\n", + sc->sc_tx_statuslen); + device_printf(sc->sc_dev, "TX buffers per descriptor: %d\n", + sc->sc_tx_nmaps); + } + + sc->sc_tx.xmit_setup = ath_edma_dma_txsetup; + sc->sc_tx.xmit_teardown = ath_edma_dma_txteardown; + sc->sc_tx.xmit_attach_comp_func = ath_edma_attach_comp_func; + + sc->sc_tx.xmit_dma_restart = ath_edma_dma_restart; + sc->sc_tx.xmit_handoff = ath_edma_xmit_handoff; + sc->sc_tx.xmit_drain = ath_edma_tx_drain; +} diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_tx_edma.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_tx_edma.h new file mode 100644 index 0000000000..668861688a --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_tx_edma.h @@ -0,0 +1,36 @@ +/*- + * Copyright (c) 2012 Adrian Chadd + * 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. + * + * $FreeBSD: releng/11.1/sys/dev/ath/if_ath_tx_edma.h 238710 2012-07-23 03:52:18Z adrian $ + */ +#ifndef __IF_ATH_TX_EDMA_H__ +#define __IF_ATH_TX_EDMA_H__ + +extern void ath_xmit_setup_edma(struct ath_softc *sc); + +#endif diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_tx_ht.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_tx_ht.c index 348a1499bb..d6ff9bea70 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_tx_ht.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_tx_ht.c @@ -28,7 +28,7 @@ */ #include -__FBSDID("$FreeBSD$"); +__FBSDID("$FreeBSD: releng/11.1/sys/dev/ath/if_ath_tx_ht.c 302392 2016-07-07 17:22:13Z adrian $"); #include "opt_inet.h" #include "opt_ath.h" @@ -86,78 +86,531 @@ __FBSDID("$FreeBSD$"); #include #endif +#include /* XXX for some support functions */ #include +#include +#include + +/* + * XXX net80211? + */ +#define IEEE80211_AMPDU_SUBFRAME_DEFAULT 32 + +#define ATH_AGGR_DELIM_SZ 4 /* delimiter size */ +#define ATH_AGGR_MINPLEN 256 /* in bytes, minimum packet length */ +/* number of delimiters for encryption padding */ +#define ATH_AGGR_ENCRYPTDELIM 10 + +/* + * returns delimiter padding required given the packet length + */ +#define ATH_AGGR_GET_NDELIM(_len) \ + (((((_len) + ATH_AGGR_DELIM_SZ) < ATH_AGGR_MINPLEN) ? \ + (ATH_AGGR_MINPLEN - (_len) - ATH_AGGR_DELIM_SZ) : 0) >> 2) + +#define PADBYTES(_len) ((4 - ((_len) % 4)) % 4) + +int ath_max_4ms_framelen[4][32] = { + [MCS_HT20] = { + 3212, 6432, 9648, 12864, 19300, 25736, 28952, 32172, + 6424, 12852, 19280, 25708, 38568, 51424, 57852, 64280, + 9628, 19260, 28896, 38528, 57792, 65532, 65532, 65532, + 12828, 25656, 38488, 51320, 65532, 65532, 65532, 65532, + }, + [MCS_HT20_SGI] = { + 3572, 7144, 10720, 14296, 21444, 28596, 32172, 35744, + 7140, 14284, 21428, 28568, 42856, 57144, 64288, 65532, + 10700, 21408, 32112, 42816, 64228, 65532, 65532, 65532, + 14256, 28516, 42780, 57040, 65532, 65532, 65532, 65532, + }, + [MCS_HT40] = { + 6680, 13360, 20044, 26724, 40092, 53456, 60140, 65532, + 13348, 26700, 40052, 53400, 65532, 65532, 65532, 65532, + 20004, 40008, 60016, 65532, 65532, 65532, 65532, 65532, + 26644, 53292, 65532, 65532, 65532, 65532, 65532, 65532, + }, + [MCS_HT40_SGI] = { + 7420, 14844, 22272, 29696, 44544, 59396, 65532, 65532, + 14832, 29668, 44504, 59340, 65532, 65532, 65532, 65532, + 22232, 44464, 65532, 65532, 65532, 65532, 65532, 65532, + 29616, 59232, 65532, 65532, 65532, 65532, 65532, 65532, + } +}; + +/* + * XXX should be in net80211 + */ +static int ieee80211_mpdudensity_map[] = { + 0, /* IEEE80211_HTCAP_MPDUDENSITY_NA */ + 25, /* IEEE80211_HTCAP_MPDUDENSITY_025 */ + 50, /* IEEE80211_HTCAP_MPDUDENSITY_05 */ + 100, /* IEEE80211_HTCAP_MPDUDENSITY_1 */ + 200, /* IEEE80211_HTCAP_MPDUDENSITY_2 */ + 400, /* IEEE80211_HTCAP_MPDUDENSITY_4 */ + 800, /* IEEE80211_HTCAP_MPDUDENSITY_8 */ + 1600, /* IEEE80211_HTCAP_MPDUDENSITY_16 */ +}; + +/* + * XXX should be in the HAL/net80211 ? + */ +#define BITS_PER_BYTE 8 +#define OFDM_PLCP_BITS 22 +#define HT_RC_2_MCS(_rc) ((_rc) & 0x7f) +#define HT_RC_2_STREAMS(_rc) ((((_rc) & 0x78) >> 3) + 1) +#define L_STF 8 +#define L_LTF 8 +#define L_SIG 4 +#define HT_SIG 8 +#define HT_STF 4 +#define HT_LTF(_ns) (4 * (_ns)) +#define SYMBOL_TIME(_ns) ((_ns) << 2) // ns * 4 us +#define SYMBOL_TIME_HALFGI(_ns) (((_ns) * 18 + 4) / 5) // ns * 3.6 us +#define NUM_SYMBOLS_PER_USEC(_usec) (_usec >> 2) +#define NUM_SYMBOLS_PER_USEC_HALFGI(_usec) (((_usec*5)-4)/18) +#define IS_HT_RATE(_rate) ((_rate) & 0x80) + +const uint32_t bits_per_symbol[][2] = { + /* 20MHz 40MHz */ + { 26, 54 }, // 0: BPSK + { 52, 108 }, // 1: QPSK 1/2 + { 78, 162 }, // 2: QPSK 3/4 + { 104, 216 }, // 3: 16-QAM 1/2 + { 156, 324 }, // 4: 16-QAM 3/4 + { 208, 432 }, // 5: 64-QAM 2/3 + { 234, 486 }, // 6: 64-QAM 3/4 + { 260, 540 }, // 7: 64-QAM 5/6 + { 52, 108 }, // 8: BPSK + { 104, 216 }, // 9: QPSK 1/2 + { 156, 324 }, // 10: QPSK 3/4 + { 208, 432 }, // 11: 16-QAM 1/2 + { 312, 648 }, // 12: 16-QAM 3/4 + { 416, 864 }, // 13: 64-QAM 2/3 + { 468, 972 }, // 14: 64-QAM 3/4 + { 520, 1080 }, // 15: 64-QAM 5/6 + { 78, 162 }, // 16: BPSK + { 156, 324 }, // 17: QPSK 1/2 + { 234, 486 }, // 18: QPSK 3/4 + { 312, 648 }, // 19: 16-QAM 1/2 + { 468, 972 }, // 20: 16-QAM 3/4 + { 624, 1296 }, // 21: 64-QAM 2/3 + { 702, 1458 }, // 22: 64-QAM 3/4 + { 780, 1620 }, // 23: 64-QAM 5/6 + { 104, 216 }, // 24: BPSK + { 208, 432 }, // 25: QPSK 1/2 + { 312, 648 }, // 26: QPSK 3/4 + { 416, 864 }, // 27: 16-QAM 1/2 + { 624, 1296 }, // 28: 16-QAM 3/4 + { 832, 1728 }, // 29: 64-QAM 2/3 + { 936, 1944 }, // 30: 64-QAM 3/4 + { 1040, 2160 }, // 31: 64-QAM 5/6 +}; + +/* + * Fill in the rate array information based on the current + * node configuration and the choices made by the rate + * selection code and ath_buf setup code. + * + * Later on, this may end up also being made by the + * rate control code, but for now it can live here. + * + * This needs to be called just before the packet is + * queued to the software queue or hardware queue, + * so all of the needed fields in bf_state are setup. + */ +void +ath_tx_rate_fill_rcflags(struct ath_softc *sc, struct ath_buf *bf) +{ + struct ieee80211_node *ni = bf->bf_node; + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + const HAL_RATE_TABLE *rt = sc->sc_currates; + struct ath_rc_series *rc = bf->bf_state.bfs_rc; + uint8_t rate; + int i; + int do_ldpc; + int do_stbc; + + /* + * We only do LDPC if the rate is 11n, both we and the + * receiver support LDPC and it's enabled. + * + * It's a global flag, not a per-try flag, so we clear + * it if any of the rate entries aren't 11n. + */ + do_ldpc = 0; + if ((ni->ni_vap->iv_htcaps & IEEE80211_HTCAP_LDPC) && + (ni->ni_htcap & IEEE80211_HTCAP_LDPC)) + do_ldpc = 1; + do_stbc = 0; + + for (i = 0; i < ATH_RC_NUM; i++) { + rc[i].flags = 0; + if (rc[i].tries == 0) + continue; + + rate = rt->info[rc[i].rix].rateCode; + + /* + * Only enable short preamble for legacy rates + */ + if ((! IS_HT_RATE(rate)) && bf->bf_state.bfs_shpream) + rate |= rt->info[rc[i].rix].shortPreamble; + + /* + * Save this, used by the TX and completion code + */ + rc[i].ratecode = rate; + + if (bf->bf_state.bfs_txflags & + (HAL_TXDESC_RTSENA | HAL_TXDESC_CTSENA)) + rc[i].flags |= ATH_RC_RTSCTS_FLAG; + + /* + * If we can't do LDPC, don't. + */ + if (! IS_HT_RATE(rate)) + do_ldpc = 0; + + /* Only enable shortgi, 2040, dual-stream if HT is set */ + if (IS_HT_RATE(rate)) { + rc[i].flags |= ATH_RC_HT_FLAG; + + if (ni->ni_chw == 40) + rc[i].flags |= ATH_RC_CW40_FLAG; + + if (ni->ni_chw == 40 && + ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40 && + ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40 && + vap->iv_flags_ht & IEEE80211_FHT_SHORTGI40) + rc[i].flags |= ATH_RC_SGI_FLAG; + + if (ni->ni_chw == 20 && + ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI20 && + ni->ni_htcap & IEEE80211_HTCAP_SHORTGI20 && + vap->iv_flags_ht & IEEE80211_FHT_SHORTGI20) + rc[i].flags |= ATH_RC_SGI_FLAG; + + /* + * If we have STBC TX enabled and the receiver + * can receive (at least) 1 stream STBC, AND it's + * MCS 0-7, AND we have at least two chains enabled, + * enable STBC. + * + * XXX TODO: .. and the rate is an 11n rate? + */ + if (ic->ic_htcaps & IEEE80211_HTCAP_TXSTBC && + ni->ni_vap->iv_flags_ht & IEEE80211_FHT_STBC_TX && + ni->ni_htcap & IEEE80211_HTCAP_RXSTBC_1STREAM && + (sc->sc_cur_txchainmask > 1) && + HT_RC_2_STREAMS(rate) == 1) { + rc[i].flags |= ATH_RC_STBC_FLAG; + do_stbc = 1; + } + + /* + * Dual / Triple stream rate? + */ + if (HT_RC_2_STREAMS(rate) == 2) + rc[i].flags |= ATH_RC_DS_FLAG; + else if (HT_RC_2_STREAMS(rate) == 3) + rc[i].flags |= ATH_RC_TS_FLAG; + } + + /* + * Calculate the maximum TX power cap for the current + * node. + */ + rc[i].tx_power_cap = ieee80211_get_node_txpower(ni); + + /* + * Calculate the maximum 4ms frame length based + * on the MCS rate, SGI and channel width flags. + */ + if ((rc[i].flags & ATH_RC_HT_FLAG) && + (HT_RC_2_MCS(rate) < 32)) { + int j; + if (rc[i].flags & ATH_RC_CW40_FLAG) { + if (rc[i].flags & ATH_RC_SGI_FLAG) + j = MCS_HT40_SGI; + else + j = MCS_HT40; + } else { + if (rc[i].flags & ATH_RC_SGI_FLAG) + j = MCS_HT20_SGI; + else + j = MCS_HT20; + } + rc[i].max4msframelen = + ath_max_4ms_framelen[j][HT_RC_2_MCS(rate)]; + } else + rc[i].max4msframelen = 0; + DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, + "%s: i=%d, rate=0x%x, flags=0x%x, max4ms=%d\n", + __func__, i, rate, rc[i].flags, rc[i].max4msframelen); + } + + /* + * LDPC is a global flag, so ... + */ + if (do_ldpc) { + bf->bf_state.bfs_txflags |= HAL_TXDESC_LDPC; + sc->sc_stats.ast_tx_ldpc++; + } + + if (do_stbc) { + sc->sc_stats.ast_tx_stbc++; + } +} + +/* + * Return the number of delimiters to be added to + * meet the minimum required mpdudensity. + * + * Caller should make sure that the rate is HT. + * + * TODO: is this delimiter calculation supposed to be the + * total frame length, the hdr length, the data length (including + * delimiters, padding, CRC, etc) or ? + * + * TODO: this should ensure that the rate control information + * HAS been setup for the first rate. + * + * TODO: ensure this is only called for MCS rates. + * + * TODO: enforce MCS < 31 + */ +static int +ath_compute_num_delims(struct ath_softc *sc, struct ath_buf *first_bf, + uint16_t pktlen) +{ + const HAL_RATE_TABLE *rt = sc->sc_currates; + struct ieee80211_node *ni = first_bf->bf_node; + struct ieee80211vap *vap = ni->ni_vap; + int ndelim, mindelim = 0; + int mpdudensity; /* in 1/100'th of a microsecond */ + uint8_t rc, rix, flags; + int width, half_gi; + uint32_t nsymbits, nsymbols; + uint16_t minlen; + + /* + * vap->iv_ampdu_density is a value, rather than the actual + * density. + */ + if (vap->iv_ampdu_density > IEEE80211_HTCAP_MPDUDENSITY_16) + mpdudensity = 1600; /* maximum density */ + else + mpdudensity = ieee80211_mpdudensity_map[vap->iv_ampdu_density]; + + /* Select standard number of delimiters based on frame length */ + ndelim = ATH_AGGR_GET_NDELIM(pktlen); + + /* + * If encryption is enabled, add extra delimiters to let the + * crypto hardware catch up. This could be tuned per-MAC and + * per-rate, but for now we'll simply assume encryption is + * always enabled. + * + * Also note that the Atheros reference driver inserts two + * delimiters by default for pre-AR9380 peers. This will + * include "that" required delimiter. + */ + ndelim += ATH_AGGR_ENCRYPTDELIM; + + /* + * For AR9380, there's a minimum number of delimeters + * required when doing RTS. + * + * XXX TODO: this is only needed if (a) RTS/CTS is enabled, and + * XXX (b) this is the first sub-frame in the aggregate. + */ + if (sc->sc_use_ent && (sc->sc_ent_cfg & AH_ENT_RTSCTS_DELIM_WAR) + && ndelim < AH_FIRST_DESC_NDELIMS) + ndelim = AH_FIRST_DESC_NDELIMS; + + /* + * If sc_delim_min_pad is non-zero, enforce it as the minimum + * pad delimiter count. + */ + if (sc->sc_delim_min_pad != 0) + ndelim = MAX(ndelim, sc->sc_delim_min_pad); + + DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, + "%s: pktlen=%d, ndelim=%d, mpdudensity=%d\n", + __func__, pktlen, ndelim, mpdudensity); + + /* + * If the MPDU density is 0, we can return here. + * Otherwise, we need to convert the desired mpdudensity + * into a byte length, based on the rate in the subframe. + */ + if (mpdudensity == 0) + return ndelim; + + /* + * Convert desired mpdu density from microeconds to bytes based + * on highest rate in rate series (i.e. first rate) to determine + * required minimum length for subframe. Take into account + * whether high rate is 20 or 40Mhz and half or full GI. + */ + rix = first_bf->bf_state.bfs_rc[0].rix; + rc = rt->info[rix].rateCode; + flags = first_bf->bf_state.bfs_rc[0].flags; + width = !! (flags & ATH_RC_CW40_FLAG); + half_gi = !! (flags & ATH_RC_SGI_FLAG); + + /* + * mpdudensity is in 1/100th of a usec, so divide by 100 + */ + if (half_gi) + nsymbols = NUM_SYMBOLS_PER_USEC_HALFGI(mpdudensity); + else + nsymbols = NUM_SYMBOLS_PER_USEC(mpdudensity); + nsymbols /= 100; + + if (nsymbols == 0) + nsymbols = 1; + + nsymbits = bits_per_symbol[HT_RC_2_MCS(rc)][width]; + minlen = (nsymbols * nsymbits) / BITS_PER_BYTE; + + /* + * Min length is the minimum frame length for the + * required MPDU density. + */ + if (pktlen < minlen) { + mindelim = (minlen - pktlen) / ATH_AGGR_DELIM_SZ; + ndelim = MAX(mindelim, ndelim); + } + + DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, + "%s: pktlen=%d, minlen=%d, rix=%x, rc=%x, width=%d, hgi=%d, ndelim=%d\n", + __func__, pktlen, minlen, rix, rc, width, half_gi, ndelim); + + return ndelim; +} + +/* + * Fetch the aggregation limit. + * + * It's the lowest of the four rate series 4ms frame length. + */ +static int +ath_get_aggr_limit(struct ath_softc *sc, struct ath_buf *bf) +{ + int amin = ATH_AGGR_MAXSIZE; + int i; + + if (sc->sc_aggr_limit > 0 && sc->sc_aggr_limit < ATH_AGGR_MAXSIZE) + amin = sc->sc_aggr_limit; + + for (i = 0; i < ATH_RC_NUM; i++) { + if (bf->bf_state.bfs_rc[i].tries == 0) + continue; + amin = MIN(amin, bf->bf_state.bfs_rc[i].max4msframelen); + } + + DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: max frame len= %d\n", + __func__, amin); + + return amin; +} /* * Setup a 11n rate series structure * * This should be called for both legacy and MCS rates. + * + * This uses the rate series stuf from ath_tx_rate_fill_rcflags(). + * + * It, along with ath_buf_set_rate, must be called -after- a burst + * or aggregate is setup. */ static void ath_rateseries_setup(struct ath_softc *sc, struct ieee80211_node *ni, - HAL_11N_RATE_SERIES *series, unsigned int pktlen, uint8_t *rix, - uint8_t *try, int flags) + struct ath_buf *bf, HAL_11N_RATE_SERIES *series) { -#define HT_RC_2_STREAMS(_rc) ((((_rc) & 0x78) >> 3) + 1) struct ieee80211com *ic = ni->ni_ic; struct ath_hal *ah = sc->sc_ah; HAL_BOOL shortPreamble = AH_FALSE; const HAL_RATE_TABLE *rt = sc->sc_currates; int i; + int pktlen; + struct ath_rc_series *rc = bf->bf_state.bfs_rc; if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) shortPreamble = AH_TRUE; + /* + * If this is the first frame in an aggregate series, + * use the aggregate length. + */ + if (bf->bf_state.bfs_aggr) + pktlen = bf->bf_state.bfs_al; + else + pktlen = bf->bf_state.bfs_pktlen; + + /* + * XXX TODO: modify this routine to use the bfs_rc[x].flags + * XXX fields. + */ memset(series, 0, sizeof(HAL_11N_RATE_SERIES) * 4); - for (i = 0; i < 4; i++) { + for (i = 0; i < ATH_RC_NUM; i++) { /* Only set flags for actual TX attempts */ - if (try[i] == 0) + if (rc[i].tries == 0) continue; - series[i].Tries = try[i]; + series[i].Tries = rc[i].tries; /* - * XXX this isn't strictly correct - sc_txchainmask - * XXX isn't the currently active chainmask; - * XXX it's the interface chainmask at startup. - * XXX It's overridden in the HAL rate scenario function - * XXX for now. + * XXX TODO: When the NIC is capable of three stream TX, + * transmit 1/2 stream rates on two streams. + * + * This reduces the power consumption of the NIC and + * keeps it within the PCIe slot power limits. */ - series[i].ChSel = sc->sc_txchainmask; + series[i].ChSel = sc->sc_cur_txchainmask; - if (flags & (HAL_TXDESC_RTSENA | HAL_TXDESC_CTSENA)) + /* + * Setup rate and TX power cap for this series. + */ + series[i].Rate = rt->info[rc[i].rix].rateCode; + series[i].RateIndex = rc[i].rix; + series[i].tx_power_cap = rc[i].tx_power_cap; + + /* + * Enable RTS/CTS as appropriate. + */ + if (rc[i].flags & ATH_RC_RTSCTS_FLAG) series[i].RateFlags |= HAL_RATESERIES_RTS_CTS; /* - * Transmit 40MHz frames only if the node has negotiated - * it rather than whether the node is capable of it or not. - * It's subtly different in the hostap case. - */ - if (ni->ni_chw == 40) - series[i].RateFlags |= HAL_RATESERIES_2040; + * 11n rate? Update 11n flags. + */ + if (rc[i].flags & ATH_RC_HT_FLAG) { + if (rc[i].flags & ATH_RC_CW40_FLAG) + series[i].RateFlags |= HAL_RATESERIES_2040; + + if (rc[i].flags & ATH_RC_SGI_FLAG) + series[i].RateFlags |= HAL_RATESERIES_HALFGI; + + if (rc[i].flags & ATH_RC_STBC_FLAG) + series[i].RateFlags |= HAL_RATESERIES_STBC; + } /* - * Set short-GI only if the node has advertised it - * the channel width is suitable, and we support it. - * We don't currently have a "negotiated" set of bits - - * ni_htcap is what the remote end sends, not what this - * node is capable of. + * TODO: If we're all doing 11n rates then we can set LDPC. + * If we've been asked to /do/ LDPC but we are handed a + * legacy rate, then we should complain. Loudly. */ - if (ni->ni_chw == 40 && - ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40 && - ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40) - series[i].RateFlags |= HAL_RATESERIES_HALFGI; - if (ni->ni_chw == 20 && - ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI20 && - ni->ni_htcap & IEEE80211_HTCAP_SHORTGI20) - series[i].RateFlags |= HAL_RATESERIES_HALFGI; - - series[i].Rate = rt->info[rix[i]].rateCode; - - /* PktDuration doesn't include slot, ACK, RTS, etc timing - it's just the packet duration */ - if (series[i].Rate & IEEE80211_RATE_MCS) { + /* + * PktDuration doesn't include slot, ACK, RTS, etc timing - + * it's just the packet duration + */ + if (rc[i].flags & ATH_RC_HT_FLAG) { series[i].PktDuration = ath_computedur_ht(pktlen , series[i].Rate @@ -166,26 +619,28 @@ ath_rateseries_setup(struct ath_softc *sc, struct ieee80211_node *ni, , series[i].RateFlags & HAL_RATESERIES_HALFGI); } else { if (shortPreamble) - series[i].Rate |= rt->info[rix[i]].shortPreamble; + series[i].Rate |= + rt->info[rc[i].rix].shortPreamble; series[i].PktDuration = ath_hal_computetxtime(ah, - rt, pktlen, rix[i], shortPreamble); + rt, pktlen, rc[i].rix, shortPreamble); } } -#undef HT_RC_2_STREAMS } -#if 0 +#ifdef ATH_DEBUG static void -ath_rateseries_print(HAL_11N_RATE_SERIES *series) +ath_rateseries_print(struct ath_softc *sc, HAL_11N_RATE_SERIES *series) { int i; - for (i = 0; i < 4; i++) { - printf("series %d: rate %x; tries %d; pktDuration %d; chSel %d; rateFlags %x\n", + for (i = 0; i < ATH_RC_NUM; i++) { + device_printf(sc->sc_dev ,"series %d: rate %x; tries %d; " + "pktDuration %d; chSel %d; txpowcap %d, rateFlags %x\n", i, series[i].Rate, series[i].Tries, series[i].PktDuration, series[i].ChSel, + series[i].tx_power_cap, series[i].RateFlags); } } @@ -198,46 +653,287 @@ ath_rateseries_print(HAL_11N_RATE_SERIES *series) * This isn't useful for sending beacon frames, which has different needs * wrt what's passed into the rate scenario function. */ - void -ath_buf_set_rate(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf, - int pktlen, int flags, uint8_t ctsrate, int is_pspoll, uint8_t *rix, uint8_t *try) +ath_buf_set_rate(struct ath_softc *sc, struct ieee80211_node *ni, + struct ath_buf *bf) { HAL_11N_RATE_SERIES series[4]; struct ath_desc *ds = bf->bf_desc; - struct ath_desc *lastds = NULL; struct ath_hal *ah = sc->sc_ah; + int is_pspoll = (bf->bf_state.bfs_atype == HAL_PKT_TYPE_PSPOLL); + int ctsrate = bf->bf_state.bfs_ctsrate; + int flags = bf->bf_state.bfs_txflags; /* Setup rate scenario */ memset(&series, 0, sizeof(series)); - ath_rateseries_setup(sc, ni, series, pktlen, rix, try, flags); + ath_rateseries_setup(sc, ni, bf, series); - /* Enforce AR5416 aggregate limit - can't do RTS w/ an agg frame > 8k */ - - /* Enforce RTS and CTS are mutually exclusive */ - - /* Get a pointer to the last tx descriptor in the list */ - lastds = &bf->bf_desc[bf->bf_nseg - 1]; - -#if 0 - printf("pktlen: %d; flags 0x%x\n", pktlen, flags); - ath_rateseries_print(series); +#ifdef ATH_DEBUG + if (sc->sc_debug & ATH_DEBUG_XMIT) + ath_rateseries_print(sc, series); #endif /* Set rate scenario */ + /* + * Note: Don't allow hardware to override the duration on + * ps-poll packets. + */ ath_hal_set11nratescenario(ah, ds, !is_pspoll, /* whether to override the duration or not */ - /* don't allow hardware to override the duration on ps-poll packets */ ctsrate, /* rts/cts rate */ series, /* 11n rate series */ 4, /* number of series */ flags); - /* Setup the last descriptor in the chain */ - ath_hal_setuplasttxdesc(ah, lastds, ds); - /* Set burst duration */ - /* This should only be done if aggregate protection is enabled */ + /* + * This is only required when doing 11n burst, not aggregation + * ie, if there's a second frame in a RIFS or A-MPDU burst + * w/ >1 A-MPDU frame bursting back to back. + * Normal A-MPDU doesn't do bursting -between- aggregates. + * + * .. and it's highly likely this won't ever be implemented + */ //ath_hal_set11nburstduration(ah, ds, 8192); } + +/* + * Form an aggregate packet list. + * + * This function enforces the aggregate restrictions/requirements. + * + * These are: + * + * + The aggregate size maximum (64k for AR9160 and later, 8K for + * AR5416 when doing RTS frame protection.) + * + Maximum number of sub-frames for an aggregate + * + The aggregate delimiter size, giving MACs time to do whatever is + * needed before each frame + * + Enforce the BAW limit + * + * Each descriptor queued should have the DMA setup. + * The rate series, descriptor setup, linking, etc is all done + * externally. This routine simply chains them together. + * ath_tx_setds_11n() will take care of configuring the per- + * descriptor setup, and ath_buf_set_rate() will configure the + * rate control. + * + * The TID lock is required for the entirety of this function. + * + * If some code in another thread adds to the head of this + * list, very strange behaviour will occur. Since retransmission is the + * only reason this will occur, and this routine is designed to be called + * from within the scheduler task, it won't ever clash with the completion + * task. + * + * So if you want to call this from an upper layer context (eg, to direct- + * dispatch aggregate frames to the hardware), please keep this in mind. + */ +ATH_AGGR_STATUS +ath_tx_form_aggr(struct ath_softc *sc, struct ath_node *an, + struct ath_tid *tid, ath_bufhead *bf_q) +{ + //struct ieee80211_node *ni = &an->an_node; + struct ath_buf *bf, *bf_first = NULL, *bf_prev = NULL; + int nframes = 0; + uint16_t aggr_limit = 0, al = 0, bpad = 0, al_delta, h_baw; + struct ieee80211_tx_ampdu *tap; + int status = ATH_AGGR_DONE; + int prev_frames = 0; /* XXX for AR5416 burst, not done here */ + int prev_al = 0; /* XXX also for AR5416 burst */ + + ATH_TX_LOCK_ASSERT(sc); + + tap = ath_tx_get_tx_tid(an, tid->tid); + if (tap == NULL) { + status = ATH_AGGR_ERROR; + goto finish; + } + + h_baw = tap->txa_wnd / 2; + + for (;;) { + bf = ATH_TID_FIRST(tid); + if (bf_first == NULL) + bf_first = bf; + if (bf == NULL) { + status = ATH_AGGR_DONE; + break; + } else { + /* + * It's the first frame; + * set the aggregation limit based on the + * rate control decision that has been made. + */ + aggr_limit = ath_get_aggr_limit(sc, bf_first); + } + + /* Set this early just so things don't get confused */ + bf->bf_next = NULL; + + /* + * If the frame doesn't have a sequence number that we're + * tracking in the BAW (eg NULL QOS data frame), we can't + * aggregate it. Stop the aggregation process; the sender + * can then TX what's in the list thus far and then + * TX the frame individually. + */ + if (! bf->bf_state.bfs_dobaw) { + status = ATH_AGGR_NONAGGR; + break; + } + + /* + * If any of the rates are non-HT, this packet + * can't be aggregated. + * XXX TODO: add a bf_state flag which gets marked + * if any active rate is non-HT. + */ + + /* + * do not exceed aggregation limit + */ + al_delta = ATH_AGGR_DELIM_SZ + bf->bf_state.bfs_pktlen; + if (nframes && + (aggr_limit < (al + bpad + al_delta + prev_al))) { + status = ATH_AGGR_LIMITED; + break; + } + + /* + * If RTS/CTS is set on the first frame, enforce + * the RTS aggregate limit. + */ + if (bf_first->bf_state.bfs_txflags & + (HAL_TXDESC_CTSENA | HAL_TXDESC_RTSENA)) { + if (nframes && + (sc->sc_rts_aggr_limit < + (al + bpad + al_delta + prev_al))) { + status = ATH_AGGR_8K_LIMITED; + break; + } + } + + /* + * Do not exceed subframe limit. + */ + if ((nframes + prev_frames) >= MIN((h_baw), + IEEE80211_AMPDU_SUBFRAME_DEFAULT)) { + status = ATH_AGGR_LIMITED; + break; + } + + /* + * If the current frame has an RTS/CTS configuration + * that differs from the first frame, override the + * subsequent frame with this config. + */ + if (bf != bf_first) { + bf->bf_state.bfs_txflags &= + ~ (HAL_TXDESC_RTSENA | HAL_TXDESC_CTSENA); + bf->bf_state.bfs_txflags |= + bf_first->bf_state.bfs_txflags & + (HAL_TXDESC_RTSENA | HAL_TXDESC_CTSENA); + } + + /* + * If the packet has a sequence number, do not + * step outside of the block-ack window. + */ + if (! BAW_WITHIN(tap->txa_start, tap->txa_wnd, + SEQNO(bf->bf_state.bfs_seqno))) { + status = ATH_AGGR_BAW_CLOSED; + break; + } + + /* + * this packet is part of an aggregate. + */ + ATH_TID_REMOVE(tid, bf, bf_list); + + /* The TID lock is required for the BAW update */ + ath_tx_addto_baw(sc, an, tid, bf); + bf->bf_state.bfs_addedbaw = 1; + + /* + * XXX enforce ACK for aggregate frames (this needs to be + * XXX handled more gracefully? + */ + if (bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) { + device_printf(sc->sc_dev, + "%s: HAL_TXDESC_NOACK set for an aggregate frame?\n", + __func__); + bf->bf_state.bfs_txflags &= (~HAL_TXDESC_NOACK); + } + + /* + * Add the now owned buffer (which isn't + * on the software TXQ any longer) to our + * aggregate frame list. + */ + TAILQ_INSERT_TAIL(bf_q, bf, bf_list); + nframes ++; + + /* Completion handler */ + bf->bf_comp = ath_tx_aggr_comp; + + /* + * add padding for previous frame to aggregation length + */ + al += bpad + al_delta; + + /* + * Calculate delimiters needed for the current frame + */ + bf->bf_state.bfs_ndelim = + ath_compute_num_delims(sc, bf_first, + bf->bf_state.bfs_pktlen); + + /* + * Calculate the padding needed from this set of delimiters, + * used when calculating if the next frame will fit in + * the aggregate. + */ + bpad = PADBYTES(al_delta) + (bf->bf_state.bfs_ndelim << 2); + + /* + * Chain the buffers together + */ + if (bf_prev) + bf_prev->bf_next = bf; + bf_prev = bf; + + /* + * If we're leaking frames, just return at this point; + * we've queued a single frame and we don't want to add + * any more. + */ + if (tid->an->an_leak_count) { + status = ATH_AGGR_LEAK_CLOSED; + break; + } + +#if 0 + /* + * terminate aggregation on a small packet boundary + */ + if (bf->bf_state.bfs_pktlen < ATH_AGGR_MINPLEN) { + status = ATH_AGGR_SHORTPKT; + break; + } +#endif + + } + +finish: + /* + * Just in case the list was empty when we tried to + * dequeue a packet .. + */ + if (bf_first) { + bf_first->bf_state.bfs_al = al; + bf_first->bf_state.bfs_nframes = nframes; + } + return status; +} diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_tx_ht.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_tx_ht.h index cf16c46d81..6b33a85ef1 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_tx_ht.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_ath_tx_ht.h @@ -26,14 +26,38 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/if_ath_tx_ht.h 250665 2013-05-15 18:33:05Z adrian $ */ #ifndef __IF_ATH_TX_HT_H__ #define __IF_ATH_TX_HT_H__ +enum { + MCS_HT20, + MCS_HT20_SGI, + MCS_HT40, + MCS_HT40_SGI, +}; + +typedef enum { + ATH_AGGR_DONE, + ATH_AGGR_BAW_CLOSED, + ATH_AGGR_LIMITED, + ATH_AGGR_SHORTPKT, + ATH_AGGR_8K_LIMITED, + ATH_AGGR_ERROR, + ATH_AGGR_NONAGGR, + ATH_AGGR_LEAK_CLOSED, +} ATH_AGGR_STATUS; + +extern int ath_max_4ms_framelen[4][32]; + +extern void ath_tx_rate_fill_rcflags(struct ath_softc *sc, struct ath_buf *bf); + extern void ath_buf_set_rate(struct ath_softc *sc, - struct ieee80211_node *ni, struct ath_buf *bf, - int pktlen, int flags, uint8_t ctsrate, int is_pspoll, - uint8_t *rix, uint8_t *try); + struct ieee80211_node *ni, struct ath_buf *bf); + +extern ATH_AGGR_STATUS + ath_tx_form_aggr(struct ath_softc *sc, struct ath_node *an, + struct ath_tid *tid, ath_bufhead *bf_q); #endif diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_athdfs.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_athdfs.h index a3f27f90e9..473db08b8c 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_athdfs.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_athdfs.h @@ -26,16 +26,17 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/if_athdfs.h 244946 2013-01-02 01:36:10Z adrian $ */ #ifndef __IF_ATHDFS_H__ #define __IF_ATHDFS_H__ extern int ath_dfs_attach(struct ath_softc *sc); extern int ath_dfs_detach(struct ath_softc *sc); -extern void ath_dfs_radar_enable(struct ath_softc *, +extern int ath_dfs_radar_enable(struct ath_softc *, struct ieee80211_channel *chan); -extern void ath_dfs_process_phy_err(struct ath_softc *sc, const char *buf, +extern int ath_dfs_radar_disable(struct ath_softc *sc); +extern void ath_dfs_process_phy_err(struct ath_softc *sc, struct mbuf *m, uint64_t tsf, struct ath_rx_status *rxstat); extern int ath_dfs_process_radar_event(struct ath_softc *sc, struct ieee80211_channel *chan); diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_athioctl.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_athioctl.h index 4b50d0d826..f99959fa67 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_athioctl.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_athioctl.h @@ -26,7 +26,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/if_athioctl.h 298760 2016-04-29 01:51:27Z adrian $ */ /* @@ -35,6 +35,21 @@ #ifndef _DEV_ATH_ATHIOCTL_H #define _DEV_ATH_ATHIOCTL_H +struct ath_tx_aggr_stats { + u_int32_t aggr_pkts[64]; + u_int32_t aggr_single_pkt; + u_int32_t aggr_nonbaw_pkt; + u_int32_t aggr_aggr_pkt; + u_int32_t aggr_baw_closed_single_pkt; + u_int32_t aggr_low_hwq_single_pkt; + u_int32_t aggr_sched_nopkt; + u_int32_t aggr_rts_aggr_limited; +}; + +struct ath_intr_stats { + u_int32_t sync_intr[32]; +}; + struct ath_stats { u_int32_t ast_watchdog; /* device reset by watchdog */ u_int32_t ast_hardware; /* fatal hardware error interrupts */ @@ -134,11 +149,31 @@ struct ath_stats { u_int32_t ast_tx_xtxop; /* tx exceeded TXOP */ u_int32_t ast_tx_timerexpired; /* tx exceeded TX_TIMER */ u_int32_t ast_tx_desccfgerr; /* tx desc cfg error */ - u_int32_t ast_pad[13]; + u_int32_t ast_tx_swretries; /* software TX retries */ + u_int32_t ast_tx_swretrymax; /* software TX retry max limit reach */ + u_int32_t ast_tx_data_underrun; + u_int32_t ast_tx_delim_underrun; + u_int32_t ast_tx_aggr_failall; /* aggregate TX failed in its entirety */ + u_int32_t ast_tx_getnobuf; + u_int32_t ast_tx_getbusybuf; + u_int32_t ast_tx_intr; + u_int32_t ast_rx_intr; + u_int32_t ast_tx_aggr_ok; /* aggregate TX ok */ + u_int32_t ast_tx_aggr_fail; /* aggregate TX failed */ + u_int32_t ast_tx_mcastq_overflow; /* multicast queue overflow */ + u_int32_t ast_rx_keymiss; + u_int32_t ast_tx_swfiltered; + u_int32_t ast_tx_node_psq_overflow; + u_int32_t ast_rx_stbc; /* RX STBC frame */ + u_int32_t ast_tx_nodeq_overflow; /* node sw queue overflow */ + u_int32_t ast_tx_ldpc; /* TX LDPC frame */ + u_int32_t ast_tx_stbc; /* TX STBC frame */ + u_int32_t ast_pad[10]; }; #define SIOCGATHSTATS _IOWR('i', 137, struct ifreq) #define SIOCZATHSTATS _IOWR('i', 139, struct ifreq) +#define SIOCGATHAGSTATS _IOWR('i', 141, struct ifreq) struct ath_diag { char ad_name[IFNAMSIZ]; /* if name, e.g. "ath0" */ @@ -156,10 +191,57 @@ struct ath_diag { #define SIOCGATHDIAG _IOWR('i', 138, struct ath_diag) #define SIOCGATHPHYERR _IOWR('i', 140, struct ath_diag) + +/* + * The rate control ioctl has to support multiple potential rate + * control classes. For now, instead of trying to support an + * abstraction for this in the API, let's just use a TLV + * representation for the payload and let userspace sort it out. + */ +struct ath_rateioctl_tlv { + uint16_t tlv_id; + uint16_t tlv_len; /* length excluding TLV header */ +}; + +/* + * This is purely the six byte MAC address. + */ +#define ATH_RATE_TLV_MACADDR 0xaab0 + +/* + * The rate control modules may decide to push a mapping table + * of rix -> net80211 ratecode as part of the update. + */ +#define ATH_RATE_TLV_RATETABLE_NENTRIES 64 +struct ath_rateioctl_rt { + uint16_t nentries; + uint16_t pad[1]; + uint8_t ratecode[ATH_RATE_TLV_RATETABLE_NENTRIES]; +}; +#define ATH_RATE_TLV_RATETABLE 0xaab1 + +/* + * This is the sample node statistics structure. + * More in ath_rate/sample/sample.h. + */ +#define ATH_RATE_TLV_SAMPLENODE 0xaab2 + +struct ath_rateioctl { + char if_name[IFNAMSIZ]; /* if name */ + union { + uint8_t macaddr[IEEE80211_ADDR_LEN]; + uint64_t pad; + } is_u; + uint32_t len; + caddr_t buf; +}; +#define SIOCGATHNODERATESTATS _IOWR('i', 149, struct ath_rateioctl) +#define SIOCGATHRATESTATS _IOWR('i', 150, struct ath_rateioctl) + /* * Radio capture format. */ -#define ATH_RX_RADIOTAP_PRESENT ( \ +#define ATH_RX_RADIOTAP_PRESENT_BASE ( \ (1 << IEEE80211_RADIOTAP_TSFT) | \ (1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_RATE) | \ @@ -169,8 +251,95 @@ struct ath_diag { (1 << IEEE80211_RADIOTAP_XCHANNEL) | \ 0) +#ifdef ATH_ENABLE_RADIOTAP_VENDOR_EXT +#define ATH_RX_RADIOTAP_PRESENT \ + (ATH_RX_RADIOTAP_PRESENT_BASE | \ + (1 << IEEE80211_RADIOTAP_VENDOREXT) | \ + (1 << IEEE80211_RADIOTAP_EXT) | \ + 0) +#else +#define ATH_RX_RADIOTAP_PRESENT ATH_RX_RADIOTAP_PRESENT_BASE +#endif /* ATH_ENABLE_RADIOTAP_PRESENT */ + +#ifdef ATH_ENABLE_RADIOTAP_VENDOR_EXT +/* + * This is higher than the vendor bitmap used inside + * the Atheros reference codebase. + */ + +/* Bit 8 */ +#define ATH_RADIOTAP_VENDOR_HEADER 8 + +/* + * Using four chains makes all the fields in the + * per-chain info header be 4-byte aligned. + */ +#define ATH_RADIOTAP_MAX_CHAINS 4 + +/* + * AR9380 and later chips are 3x3, which requires + * 5 EVM DWORDs in HT40 mode. + */ +#define ATH_RADIOTAP_MAX_EVM 5 + +/* + * The vendor radiotap header data needs to be: + * + * + Aligned to a 4 byte address + * + .. so all internal fields are 4 bytes aligned; + * + .. and no 64 bit fields are allowed. + * + * So padding is required to ensure this is the case. + * + * Note that because of the lack of alignment with the + * vendor header (6 bytes), the first field must be + * two bytes so it can be accessed by alignment-strict + * platform (eg MIPS.) + */ +struct ath_radiotap_vendor_hdr { /* 30 bytes */ + uint8_t vh_version; /* 1 */ + uint8_t vh_rx_chainmask; /* 1 */ + + /* At this point it should be 4 byte aligned */ + uint32_t evm[ATH_RADIOTAP_MAX_EVM]; /* 5 * 4 = 20 */ + + uint8_t rssi_ctl[ATH_RADIOTAP_MAX_CHAINS]; /* 4 */ + uint8_t rssi_ext[ATH_RADIOTAP_MAX_CHAINS]; /* 4 */ + + uint8_t vh_phyerr_code; /* Phy error code, or 0xff */ + uint8_t vh_rs_status; /* RX status */ + uint8_t vh_rssi; /* Raw RSSI */ + uint8_t vh_flags; /* General flags */ +#define ATH_VENDOR_PKT_RX 0x01 +#define ATH_VENDOR_PKT_TX 0x02 +#define ATH_VENDOR_PKT_RXPHYERR 0x04 +#define ATH_VENDOR_PKT_ISAGGR 0x08 +#define ATH_VENDOR_PKT_MOREAGGR 0x10 + + uint8_t vh_rx_hwrate; /* hardware RX ratecode */ + uint8_t vh_rs_flags; /* RX HAL flags */ + uint8_t vh_pad[2]; /* pad to DWORD boundary */ +} __packed; +#endif /* ATH_ENABLE_RADIOTAP_VENDOR_EXT */ + struct ath_rx_radiotap_header { struct ieee80211_radiotap_header wr_ihdr; + +#ifdef ATH_ENABLE_RADIOTAP_VENDOR_EXT + /* Vendor extension header bitmap */ + uint32_t wr_ext_bitmap; /* 4 */ + + /* + * This padding is needed because: + * + the radiotap header is 8 bytes; + * + the extension bitmap is 4 bytes; + * + the tsf is 8 bytes, so it must start on an 8 byte + * boundary. + */ + uint32_t wr_pad1; +#endif /* ATH_ENABLE_RADIOTAP_VENDOR_EXT */ + + /* Normal radiotap fields */ u_int64_t wr_tsf; u_int8_t wr_flags; u_int8_t wr_rate; @@ -182,6 +351,26 @@ struct ath_rx_radiotap_header { u_int16_t wr_chan_freq; u_int8_t wr_chan_ieee; int8_t wr_chan_maxpow; + +#ifdef ATH_ENABLE_RADIOTAP_VENDOR_EXT + /* + * Vendor header section, as required by the + * presence of the vendor extension bit and bitmap + * entry. + * + * XXX This must be aligned to a 4 byte address? + * XXX or 8 byte address? + */ + struct ieee80211_radiotap_vendor_header wr_vh; /* 6 bytes */ + + /* + * Because of the lack of alignment enforced by the above + * header, this vendor section won't be aligned in any + * useful way. So, this will include a two-byte version + * value which will force the structure to be 4-byte aligned. + */ + struct ath_radiotap_vendor_hdr wr_v; +#endif /* ATH_ENABLE_RADIOTAP_VENDOR_EXT */ } __packed; #define ATH_TX_RADIOTAP_PRESENT ( \ @@ -236,4 +425,28 @@ struct ath_tx_radiotap_header { #define DFS_PARAM_ENABLE 32 #define DFS_PARAM_EN_EXTCH 33 +/* + * Spectral ioctl parameter types + */ +#define SPECTRAL_PARAM_FFT_PERIOD 1 +#define SPECTRAL_PARAM_SS_PERIOD 2 +#define SPECTRAL_PARAM_SS_COUNT 3 +#define SPECTRAL_PARAM_SS_SHORT_RPT 4 +#define SPECTRAL_PARAM_ENABLED 5 +#define SPECTRAL_PARAM_ACTIVE 6 + +/* + * Spectral control parameters + */ +#define SIOCGATHSPECTRAL _IOWR('i', 151, struct ath_diag) + +#define SPECTRAL_CONTROL_ENABLE 2 +#define SPECTRAL_CONTROL_DISABLE 3 +#define SPECTRAL_CONTROL_START 4 +#define SPECTRAL_CONTROL_STOP 5 +#define SPECTRAL_CONTROL_GET_PARAMS 6 +#define SPECTRAL_CONTROL_SET_PARAMS 7 +#define SPECTRAL_CONTROL_ENABLE_AT_RESET 8 +#define SPECTRAL_CONTROL_DISABLE_AT_RESET 9 + #endif /* _DEV_ATH_ATHIOCTL_H */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_athrate.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_athrate.h index 55b99a2d9a..5a170b61d7 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_athrate.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_athrate.h @@ -27,7 +27,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/if_athrate.h 249578 2013-04-17 07:21:30Z adrian $ */ #ifndef _ATH_RATECTRL_H_ #define _ATH_RATECTRL_H_ @@ -77,6 +77,24 @@ struct ath_ratectrl { struct ath_ratectrl *ath_rate_attach(struct ath_softc *); void ath_rate_detach(struct ath_ratectrl *); +#define ATH_RC_NUM 4 + +#define ATH_RC_DS_FLAG 0x01 /* dual-stream rate */ +#define ATH_RC_CW40_FLAG 0x02 /* use HT40 */ +#define ATH_RC_SGI_FLAG 0x04 /* use short-GI */ +#define ATH_RC_HT_FLAG 0x08 /* use HT */ +#define ATH_RC_RTSCTS_FLAG 0x10 /* enable RTS/CTS protection */ +#define ATH_RC_STBC_FLAG 0x20 /* enable STBC */ +#define ATH_RC_TS_FLAG 0x40 /* triple-stream rate */ + +struct ath_rc_series { + uint8_t rix; /* ratetable index, not rate code */ + uint8_t ratecode; /* hardware rate code */ + uint8_t tries; + uint8_t tx_power_cap; + uint16_t flags; + uint16_t max4msframelen; +}; /* * State storage handling. @@ -105,7 +123,7 @@ void ath_rate_newassoc(struct ath_softc *, struct ath_node *, * Return the four TX rate index and try counts for the current data packet. */ void ath_rate_getxtxrates(struct ath_softc *sc, struct ath_node *an, - uint8_t rix0, uint8_t *rix, uint8_t *try); + uint8_t rix0, struct ath_rc_series *rc); /* * Return the transmit info for a data packet. If multi-rate state @@ -127,8 +145,24 @@ void ath_rate_setupxtxdesc(struct ath_softc *, struct ath_node *, * supplied transmit descriptor. The routine is invoked both * for packets that were successfully sent and for those that * failed (consult the descriptor for details). + * + * For A-MPDU frames, nframes and nbad indicate how many frames + * were in the aggregate, and how many failed. */ struct ath_buf; void ath_rate_tx_complete(struct ath_softc *, struct ath_node *, - const struct ath_buf *); + const struct ath_rc_series *, const struct ath_tx_status *, + int pktlen, int nframes, int nbad); + +/* + * Fetch the global rate control statistics. + */ +int ath_rate_fetch_stats(struct ath_softc *sc, struct ath_rateioctl *rs); + +/* + * Fetch the per-node statistics. + */ +int ath_rate_fetch_node_stats(struct ath_softc *sc, struct ath_node *an, + struct ath_rateioctl *rs); + #endif /* _ATH_RATECTRL_H_ */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_athvar.h b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_athvar.h index b8153ef05f..9fa438870f 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_athvar.h +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/dev/ath/if_athvar.h @@ -26,7 +26,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * - * $FreeBSD$ + * $FreeBSD: releng/11.1/sys/dev/ath/if_athvar.h 302100 2016-06-23 00:54:14Z adrian $ */ /* @@ -35,14 +35,27 @@ #ifndef _DEV_ATH_ATHVAR_H #define _DEV_ATH_ATHVAR_H +#include + #include #include #include #include #include +#ifdef ATH_DEBUG_ALQ +#include +#endif #define ATH_TIMEOUT 1000 +/* + * There is a separate TX ath_buf pool for management frames. + * This ensures that management frames such as probe responses + * and BAR frames can be transmitted during periods of high + * TX activity. + */ +#define ATH_MGMT_TXBUF 32 + /* * 802.11n requires more TX and RX buffers to do AMPDU. */ @@ -64,10 +77,29 @@ #define ATH_TXMGTTRY 4 /* xmit attempts for mgt/ctl frames */ #define ATH_TXINTR_PERIOD 5 /* max number of batched tx descriptors */ -#define ATH_BEACON_AIFS_DEFAULT 0 /* default aifs for ap beacon q */ +#define ATH_BEACON_AIFS_DEFAULT 1 /* default aifs for ap beacon q */ #define ATH_BEACON_CWMIN_DEFAULT 0 /* default cwmin for ap beacon q */ #define ATH_BEACON_CWMAX_DEFAULT 0 /* default cwmax for ap beacon q */ +/* + * The following bits can be set during the PCI (and perhaps non-PCI + * later) device probe path. + * + * It controls some of the driver and HAL behaviour. + */ + +#define ATH_PCI_CUS198 0x0001 +#define ATH_PCI_CUS230 0x0002 +#define ATH_PCI_CUS217 0x0004 +#define ATH_PCI_CUS252 0x0008 +#define ATH_PCI_WOW 0x0010 +#define ATH_PCI_BT_ANT_DIV 0x0020 +#define ATH_PCI_D3_L1_WAR 0x0040 +#define ATH_PCI_AR9565_1ANT 0x0080 +#define ATH_PCI_AR9565_2ANT 0x0100 +#define ATH_PCI_NO_PLL_PWRSAVE 0x0200 +#define ATH_PCI_KILLER 0x0400 + /* * The key cache is used for h/w cipher state and also for * tracking station state such as the current tx antenna. @@ -83,12 +115,93 @@ struct taskqueue; struct kthread; struct ath_buf; +#define ATH_TID_MAX_BUFS (2 * IEEE80211_AGGR_BAWMAX) + +/* + * Per-TID state + * + * Note that TID 16 (WME_NUM_TID+1) is for handling non-QoS frames. + */ +struct ath_tid { + TAILQ_HEAD(,ath_buf) tid_q; /* pending buffers */ + struct ath_node *an; /* pointer to parent */ + int tid; /* tid */ + int ac; /* which AC gets this traffic */ + int hwq_depth; /* how many buffers are on HW */ + u_int axq_depth; /* SW queue depth */ + + struct { + TAILQ_HEAD(,ath_buf) tid_q; /* filtered queue */ + u_int axq_depth; /* SW queue depth */ + } filtq; + + /* + * Entry on the ath_txq; when there's traffic + * to send + */ + TAILQ_ENTRY(ath_tid) axq_qelem; + int sched; + int paused; /* >0 if the TID has been paused */ + + /* + * These are flags - perhaps later collapse + * down to a single uint32_t ? + */ + int addba_tx_pending; /* TX ADDBA pending */ + int bar_wait; /* waiting for BAR */ + int bar_tx; /* BAR TXed */ + int isfiltered; /* is this node currently filtered */ + + /* + * Is the TID being cleaned up after a transition + * from aggregation to non-aggregation? + * When this is set to 1, this TID will be paused + * and no further traffic will be queued until all + * the hardware packets pending for this TID have been + * TXed/completed; at which point (non-aggregation) + * traffic will resume being TXed. + */ + int cleanup_inprogress; + /* + * How many hardware-queued packets are + * waiting to be cleaned up. + * This is only valid if cleanup_inprogress is 1. + */ + int incomp; + + /* + * The following implements a ring representing + * the frames in the current BAW. + * To avoid copying the array content each time + * the BAW is moved, the baw_head/baw_tail point + * to the current BAW begin/end; when the BAW is + * shifted the head/tail of the array are also + * appropriately shifted. + */ + /* active tx buffers, beginning at current BAW */ + struct ath_buf *tx_buf[ATH_TID_MAX_BUFS]; + /* where the baw head is in the array */ + int baw_head; + /* where the BAW tail is in the array */ + int baw_tail; +}; + /* driver-specific node state */ struct ath_node { struct ieee80211_node an_node; /* base class */ u_int8_t an_mgmtrix; /* min h/w rate index */ u_int8_t an_mcastrix; /* mcast h/w rate index */ + uint32_t an_is_powersave; /* node is sleeping */ + uint32_t an_stack_psq; /* net80211 psq isn't empty */ + uint32_t an_tim_set; /* TIM has been set */ struct ath_buf *an_ff_buf[WME_NUM_AC]; /* ff staging area */ + struct ath_tid an_tid[IEEE80211_TID_SIZE]; /* per-TID state */ + char an_name[32]; /* eg "wlan0_a1" */ + struct mtx an_mtx; /* protecting the rate control state */ + uint32_t an_swq_depth; /* how many SWQ packets for this + node */ + int clrdmask; /* has clrdmask been set */ + uint32_t an_leak_count; /* How many frames to leak during pause */ /* variable-length rate control state follows */ }; #define ATH_NODE(ni) ((struct ath_node *)(ni)) @@ -108,24 +221,101 @@ struct ath_node { ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul)) #define ATH_RSSI(x) ATH_EP_RND(x, HAL_RSSI_EP_MULTIPLIER) +typedef enum { + ATH_BUFTYPE_NORMAL = 0, + ATH_BUFTYPE_MGMT = 1, +} ath_buf_type_t; + struct ath_buf { - STAILQ_ENTRY(ath_buf) bf_list; + TAILQ_ENTRY(ath_buf) bf_list; + struct ath_buf * bf_next; /* next buffer in the aggregate */ int bf_nseg; - uint16_t bf_txflags; /* tx descriptor flags */ + HAL_STATUS bf_rxstatus; uint16_t bf_flags; /* status flags (below) */ + uint16_t bf_descid; /* 16 bit descriptor ID */ struct ath_desc *bf_desc; /* virtual addr of desc */ struct ath_desc_status bf_status; /* tx/rx status */ bus_addr_t bf_daddr; /* physical addr of desc */ bus_dmamap_t bf_dmamap; /* DMA map for mbuf chain */ struct mbuf *bf_m; /* mbuf for buf */ struct ieee80211_node *bf_node; /* pointer to the node */ + struct ath_desc *bf_lastds; /* last descriptor for comp status */ + struct ath_buf *bf_last; /* last buffer in aggregate, or self for non-aggregate */ bus_size_t bf_mapsize; #define ATH_MAX_SCATTER ATH_TXDESC /* max(tx,rx,beacon) desc's */ bus_dma_segment_t bf_segs[ATH_MAX_SCATTER]; -}; -typedef STAILQ_HEAD(, ath_buf) ath_bufhead; + uint32_t bf_nextfraglen; /* length of next fragment */ + /* Completion function to call on TX complete (fail or not) */ + /* + * "fail" here is set to 1 if the queue entries were removed + * through a call to ath_tx_draintxq(). + */ + void(* bf_comp) (struct ath_softc *sc, struct ath_buf *bf, int fail); + + /* This state is kept to support software retries and aggregation */ + struct { + uint16_t bfs_seqno; /* sequence number of this packet */ + uint16_t bfs_ndelim; /* number of delims for padding */ + + uint8_t bfs_retries; /* retry count */ + uint8_t bfs_tid; /* packet TID (or TID_MAX for no QoS) */ + uint8_t bfs_nframes; /* number of frames in aggregate */ + uint8_t bfs_pri; /* packet AC priority */ + uint8_t bfs_tx_queue; /* destination hardware TX queue */ + + u_int32_t bfs_aggr:1, /* part of aggregate? */ + bfs_aggrburst:1, /* part of aggregate burst? */ + bfs_isretried:1, /* retried frame? */ + bfs_dobaw:1, /* actually check against BAW? */ + bfs_addedbaw:1, /* has been added to the BAW */ + bfs_shpream:1, /* use short preamble */ + bfs_istxfrag:1, /* is fragmented */ + bfs_ismrr:1, /* do multi-rate TX retry */ + bfs_doprot:1, /* do RTS/CTS based protection */ + bfs_doratelookup:1; /* do rate lookup before each TX */ + + /* + * These fields are passed into the + * descriptor setup functions. + */ + + /* Make this an 8 bit value? */ + HAL_PKT_TYPE bfs_atype; /* packet type */ + + uint32_t bfs_pktlen; /* length of this packet */ + + uint16_t bfs_hdrlen; /* length of this packet header */ + uint16_t bfs_al; /* length of aggregate */ + + uint16_t bfs_txflags; /* HAL (tx) descriptor flags */ + uint8_t bfs_txrate0; /* first TX rate */ + uint8_t bfs_try0; /* first try count */ + + uint16_t bfs_txpower; /* tx power */ + uint8_t bfs_ctsrate0; /* Non-zero - use this as ctsrate */ + uint8_t bfs_ctsrate; /* CTS rate */ + + /* 16 bit? */ + int32_t bfs_keyix; /* crypto key index */ + int32_t bfs_txantenna; /* TX antenna config */ + + /* Make this an 8 bit value? */ + enum ieee80211_protmode bfs_protmode; + + /* 16 bit? */ + uint32_t bfs_ctsduration; /* CTS duration (pre-11n NICs) */ + struct ath_rc_series bfs_rc[ATH_RC_NUM]; /* non-11n TX series */ + } bf_state; +}; +typedef TAILQ_HEAD(ath_bufhead_s, ath_buf) ath_bufhead; + +#define ATH_BUF_MGMT 0x00000001 /* (tx) desc is a mgmt desc */ #define ATH_BUF_BUSY 0x00000002 /* (tx) desc owned by h/w */ +#define ATH_BUF_FIFOEND 0x00000004 +#define ATH_BUF_FIFOPTR 0x00000008 + +#define ATH_BUF_FLAGS_CLONE (ATH_BUF_MGMT) /* * DMA state for tx/rx descriptors. @@ -133,6 +323,7 @@ typedef STAILQ_HEAD(, ath_buf) ath_bufhead; struct ath_descdma { const char* dd_name; struct ath_desc *dd_desc; /* descriptors */ + int dd_descsize; /* size of single descriptor */ bus_addr_t dd_desc_paddr; /* physical addr of dd_desc */ bus_size_t dd_desc_len; /* size of dd_desc */ bus_dma_segment_t dd_dseg; @@ -151,64 +342,224 @@ struct ath_descdma { * hardware queue). */ struct ath_txq { + struct ath_softc *axq_softc; /* Needed for scheduling */ u_int axq_qnum; /* hardware q number */ #define ATH_TXQ_SWQ (HAL_NUM_TX_QUEUES+1) /* qnum for s/w only queue */ u_int axq_ac; /* WME AC */ u_int axq_flags; -#define ATH_TXQ_PUTPENDING 0x0001 /* ath_hal_puttxbuf pending */ +//#define ATH_TXQ_PUTPENDING 0x0001 /* ath_hal_puttxbuf pending */ +#define ATH_TXQ_PUTRUNNING 0x0002 /* ath_hal_puttxbuf has been called */ u_int axq_depth; /* queue depth (stat only) */ + u_int axq_aggr_depth; /* how many aggregates are queued */ u_int axq_intrcnt; /* interrupt count */ u_int32_t *axq_link; /* link ptr in last TX desc */ - STAILQ_HEAD(, ath_buf) axq_q; /* transmit queue */ + TAILQ_HEAD(axq_q_s, ath_buf) axq_q; /* transmit queue */ struct mtx axq_lock; /* lock on q and link */ + + /* + * This is the FIFO staging buffer when doing EDMA. + * + * For legacy chips, we just push the head pointer to + * the hardware and we ignore this list. + * + * For EDMA, the staging buffer is treated as normal; + * when it's time to push a list of frames to the hardware + * we move that list here and we stamp buffers with + * flags to identify the beginning/end of that particular + * FIFO entry. + */ + struct { + TAILQ_HEAD(axq_q_f_s, ath_buf) axq_q; + u_int axq_depth; /* how many frames (1 per legacy, 1 per A-MPDU list) are in the FIFO queue */ + } fifo; + u_int axq_fifo_depth; /* how many FIFO slots are active */ + + /* + * XXX the holdingbf field is protected by the TXBUF lock + * for now, NOT the TXQ lock. + * + * Architecturally, it would likely be better to move + * the holdingbf field to a separate array in ath_softc + * just to highlight that it's not protected by the normal + * TX path lock. + */ + struct ath_buf *axq_holdingbf; /* holding TX buffer */ char axq_name[12]; /* e.g. "ath0_txq4" */ + + /* Per-TID traffic queue for software -> hardware TX */ + /* + * This is protected by the general TX path lock, not (for now) + * by the TXQ lock. + */ + TAILQ_HEAD(axq_t_s,ath_tid) axq_tidq; }; #define ATH_TXQ_LOCK_INIT(_sc, _tq) do { \ - snprintf((_tq)->axq_name, sizeof((_tq)->axq_name), "%s_txq%u", \ - device_get_nameunit((_sc)->sc_dev), (_tq)->axq_qnum); \ - mtx_init(&(_tq)->axq_lock, (_tq)->axq_name, NULL, MTX_DEF); \ -} while (0) + snprintf((_tq)->axq_name, sizeof((_tq)->axq_name), "%s_txq%u", \ + device_get_nameunit((_sc)->sc_dev), (_tq)->axq_qnum); \ + mtx_init(&(_tq)->axq_lock, (_tq)->axq_name, NULL, MTX_DEF); \ + } while (0) #define ATH_TXQ_LOCK_DESTROY(_tq) mtx_destroy(&(_tq)->axq_lock) #define ATH_TXQ_LOCK(_tq) mtx_lock(&(_tq)->axq_lock) #define ATH_TXQ_UNLOCK(_tq) mtx_unlock(&(_tq)->axq_lock) #define ATH_TXQ_LOCK_ASSERT(_tq) mtx_assert(&(_tq)->axq_lock, MA_OWNED) +#define ATH_TXQ_UNLOCK_ASSERT(_tq) mtx_assert(&(_tq)->axq_lock, \ + MA_NOTOWNED) -#define ATH_TXQ_INSERT_TAIL(_tq, _elm, _field) do { \ - STAILQ_INSERT_TAIL(&(_tq)->axq_q, (_elm), _field); \ + +#define ATH_NODE_LOCK(_an) mtx_lock(&(_an)->an_mtx) +#define ATH_NODE_UNLOCK(_an) mtx_unlock(&(_an)->an_mtx) +#define ATH_NODE_LOCK_ASSERT(_an) mtx_assert(&(_an)->an_mtx, MA_OWNED) +#define ATH_NODE_UNLOCK_ASSERT(_an) mtx_assert(&(_an)->an_mtx, \ + MA_NOTOWNED) + +/* + * These are for the hardware queue. + */ +#define ATH_TXQ_INSERT_HEAD(_tq, _elm, _field) do { \ + TAILQ_INSERT_HEAD(&(_tq)->axq_q, (_elm), _field); \ (_tq)->axq_depth++; \ } while (0) -#define ATH_TXQ_REMOVE_HEAD(_tq, _field) do { \ - STAILQ_REMOVE_HEAD(&(_tq)->axq_q, _field); \ +#define ATH_TXQ_INSERT_TAIL(_tq, _elm, _field) do { \ + TAILQ_INSERT_TAIL(&(_tq)->axq_q, (_elm), _field); \ + (_tq)->axq_depth++; \ +} while (0) +#define ATH_TXQ_REMOVE(_tq, _elm, _field) do { \ + TAILQ_REMOVE(&(_tq)->axq_q, _elm, _field); \ (_tq)->axq_depth--; \ } while (0) -/* NB: this does not do the "head empty check" that STAILQ_LAST does */ -#define ATH_TXQ_LAST(_tq) \ - ((struct ath_buf *)(void *) \ - ((char *)((_tq)->axq_q.stqh_last) - __offsetof(struct ath_buf, bf_list))) +#define ATH_TXQ_FIRST(_tq) TAILQ_FIRST(&(_tq)->axq_q) +#define ATH_TXQ_LAST(_tq, _field) TAILQ_LAST(&(_tq)->axq_q, _field) + +/* + * These are for the TID software queue. + */ +#define ATH_TID_INSERT_HEAD(_tq, _elm, _field) do { \ + TAILQ_INSERT_HEAD(&(_tq)->tid_q, (_elm), _field); \ + (_tq)->axq_depth++; \ + (_tq)->an->an_swq_depth++; \ +} while (0) +#define ATH_TID_INSERT_TAIL(_tq, _elm, _field) do { \ + TAILQ_INSERT_TAIL(&(_tq)->tid_q, (_elm), _field); \ + (_tq)->axq_depth++; \ + (_tq)->an->an_swq_depth++; \ +} while (0) +#define ATH_TID_REMOVE(_tq, _elm, _field) do { \ + TAILQ_REMOVE(&(_tq)->tid_q, _elm, _field); \ + (_tq)->axq_depth--; \ + (_tq)->an->an_swq_depth--; \ +} while (0) +#define ATH_TID_FIRST(_tq) TAILQ_FIRST(&(_tq)->tid_q) +#define ATH_TID_LAST(_tq, _field) TAILQ_LAST(&(_tq)->tid_q, _field) + +/* + * These are for the TID filtered frame queue + */ +#define ATH_TID_FILT_INSERT_HEAD(_tq, _elm, _field) do { \ + TAILQ_INSERT_HEAD(&(_tq)->filtq.tid_q, (_elm), _field); \ + (_tq)->axq_depth++; \ + (_tq)->an->an_swq_depth++; \ +} while (0) +#define ATH_TID_FILT_INSERT_TAIL(_tq, _elm, _field) do { \ + TAILQ_INSERT_TAIL(&(_tq)->filtq.tid_q, (_elm), _field); \ + (_tq)->axq_depth++; \ + (_tq)->an->an_swq_depth++; \ +} while (0) +#define ATH_TID_FILT_REMOVE(_tq, _elm, _field) do { \ + TAILQ_REMOVE(&(_tq)->filtq.tid_q, _elm, _field); \ + (_tq)->axq_depth--; \ + (_tq)->an->an_swq_depth--; \ +} while (0) +#define ATH_TID_FILT_FIRST(_tq) TAILQ_FIRST(&(_tq)->filtq.tid_q) +#define ATH_TID_FILT_LAST(_tq, _field) TAILQ_LAST(&(_tq)->filtq.tid_q,_field) struct ath_vap { struct ieee80211vap av_vap; /* base class */ int av_bslot; /* beacon slot index */ struct ath_buf *av_bcbuf; /* beacon buffer */ - struct ieee80211_beacon_offsets av_boff;/* dynamic update state */ struct ath_txq av_mcastq; /* buffered mcast s/w queue */ void (*av_recv_mgmt)(struct ieee80211_node *, - struct mbuf *, int, int, int); + struct mbuf *, int, + const struct ieee80211_rx_stats *, int, int); int (*av_newstate)(struct ieee80211vap *, enum ieee80211_state, int); void (*av_bmiss)(struct ieee80211vap *); + void (*av_node_ps)(struct ieee80211_node *, int); + int (*av_set_tim)(struct ieee80211_node *, int); + void (*av_recv_pspoll)(struct ieee80211_node *, + struct mbuf *); }; #define ATH_VAP(vap) ((struct ath_vap *)(vap)) struct taskqueue; struct ath_tx99; +/* + * Whether to reset the TX/RX queue with or without + * a queue flush. + */ +typedef enum { + ATH_RESET_DEFAULT = 0, + ATH_RESET_NOLOSS = 1, + ATH_RESET_FULL = 2, +} ATH_RESET_TYPE; + +struct ath_rx_methods { + void (*recv_sched_queue)(struct ath_softc *sc, + HAL_RX_QUEUE q, int dosched); + void (*recv_sched)(struct ath_softc *sc, int dosched); + void (*recv_stop)(struct ath_softc *sc, int dodelay); + int (*recv_start)(struct ath_softc *sc); + void (*recv_flush)(struct ath_softc *sc); + void (*recv_tasklet)(void *arg, int npending); + int (*recv_rxbuf_init)(struct ath_softc *sc, + struct ath_buf *bf); + int (*recv_setup)(struct ath_softc *sc); + int (*recv_teardown)(struct ath_softc *sc); +}; + +/* + * Represent the current state of the RX FIFO. + */ +struct ath_rx_edma { + struct ath_buf **m_fifo; + int m_fifolen; + int m_fifo_head; + int m_fifo_tail; + int m_fifo_depth; + struct mbuf *m_rxpending; + struct ath_buf *m_holdbf; +}; + +struct ath_tx_edma_fifo { + struct ath_buf **m_fifo; + int m_fifolen; + int m_fifo_head; + int m_fifo_tail; + int m_fifo_depth; +}; + +struct ath_tx_methods { + int (*xmit_setup)(struct ath_softc *sc); + int (*xmit_teardown)(struct ath_softc *sc); + void (*xmit_attach_comp_func)(struct ath_softc *sc); + + void (*xmit_dma_restart)(struct ath_softc *sc, + struct ath_txq *txq); + void (*xmit_handoff)(struct ath_softc *sc, + struct ath_txq *txq, struct ath_buf *bf); + void (*xmit_drain)(struct ath_softc *sc, + ATH_RESET_TYPE reset_type); +}; + struct ath_softc { - struct ifnet *sc_ifp; /* interface common */ - struct ath_stats sc_stats; /* interface statistics */ - int sc_debug; + struct ieee80211com sc_ic; + struct ath_stats sc_stats; /* device statistics */ + struct ath_tx_aggr_stats sc_aggr_stats; + struct ath_intr_stats sc_intr_stats; + uint64_t sc_debug; + uint64_t sc_ktrdebug; int sc_nvaps; /* # vaps */ int sc_nstavaps; /* # station vaps */ int sc_nmeshvaps; /* # mbss vaps */ @@ -216,20 +567,56 @@ struct ath_softc { u_int8_t sc_nbssid0; /* # vap's using base mac */ uint32_t sc_bssidmask; /* bssid mask */ + struct ath_rx_methods sc_rx; + struct ath_rx_edma sc_rxedma[HAL_NUM_RX_QUEUES]; /* HP/LP queues */ + ath_bufhead sc_rx_rxlist[HAL_NUM_RX_QUEUES]; /* deferred RX completion */ + struct ath_tx_methods sc_tx; + struct ath_tx_edma_fifo sc_txedma[HAL_NUM_TX_QUEUES]; + + /* + * This is (currently) protected by the TX queue lock; + * it should migrate to a separate lock later + * so as to minimise contention. + */ + ath_bufhead sc_txbuf_list; + + int sc_rx_statuslen; + int sc_tx_desclen; + int sc_tx_statuslen; + int sc_tx_nmaps; /* Number of TX maps */ + int sc_edma_bufsize; + int sc_rx_stopped; /* XXX only for EDMA */ + int sc_rx_resetted; /* XXX only for EDMA */ + + void (*sc_node_cleanup)(struct ieee80211_node *); void (*sc_node_free)(struct ieee80211_node *); device_t sc_dev; HAL_BUS_TAG sc_st; /* bus space tag */ HAL_BUS_HANDLE sc_sh; /* bus space handle */ bus_dma_tag_t sc_dmat; /* bus DMA tag */ struct mtx sc_mtx; /* master lock (recursive) */ + struct mtx sc_pcu_mtx; /* PCU access mutex */ + char sc_pcu_mtx_name[32]; + struct mtx sc_rx_mtx; /* RX access mutex */ + char sc_rx_mtx_name[32]; + struct mtx sc_tx_mtx; /* TX handling/comp mutex */ + char sc_tx_mtx_name[32]; + struct mtx sc_tx_ic_mtx; /* TX queue mutex */ + char sc_tx_ic_mtx_name[32]; struct taskqueue *sc_tq; /* private task queue */ struct ath_hal *sc_ah; /* Atheros HAL */ struct ath_ratectrl *sc_rc; /* tx rate control support */ struct ath_tx99 *sc_tx99; /* tx99 adjunct state */ void (*sc_setdefantenna)(struct ath_softc *, u_int); - unsigned int sc_invalid : 1,/* disable hardware accesses */ + + /* + * First set of flags. + */ + uint32_t sc_invalid : 1,/* disable hardware accesses */ sc_mrretry : 1,/* multi-rate retry support */ + sc_mrrprot : 1,/* MRR + protection support */ sc_softled : 1,/* enable LED gpio status */ + sc_hardled : 1,/* enable MAC LED status */ sc_splitmic : 1,/* split TKIP MIC keys */ sc_needmib : 1,/* enable MIB stats intr */ sc_diversity: 1,/* enable rx diversity */ @@ -255,8 +642,30 @@ struct ath_softc { sc_setcca : 1,/* set/clr CCA with TDMA */ sc_resetcal : 1,/* reset cal state next trip */ sc_rxslink : 1,/* do self-linked final descriptor */ - sc_kickpcu : 1,/* kick PCU RX on next RX proc */ - sc_rxtsf32 : 1;/* RX dec TSF is 32 bits */ + sc_rxtsf32 : 1,/* RX dec TSF is 32 bits */ + sc_isedma : 1,/* supports EDMA */ + sc_do_mybeacon : 1; /* supports mybeacon */ + + /* + * Second set of flags. + */ + u_int32_t sc_running : 1, /* initialized */ + sc_use_ent : 1, + sc_rx_stbc : 1, + sc_tx_stbc : 1, + sc_has_ldpc : 1, + sc_hasenforcetxop : 1, /* support enforce TxOP */ + sc_hasdivcomb : 1, /* RX diversity combining */ + sc_rx_lnamixer : 1, /* RX using LNA mixing */ + sc_btcoex_mci : 1; /* MCI bluetooth coex */ + + int sc_cabq_enable; /* Enable cabq transmission */ + + /* + * Enterprise mode configuration for AR9380 and later chipsets. + */ + uint32_t sc_ent_cfg; + uint32_t sc_eerd; /* regdomain from EEPROM */ uint32_t sc_eecc; /* country code from EEPROM */ /* rate tables */ @@ -282,10 +691,32 @@ struct ath_softc { u_int sc_fftxqmin; /* min frames before staging */ u_int sc_fftxqmax; /* max frames before drop */ u_int sc_txantenna; /* tx antenna (fixed or auto) */ + HAL_INT sc_imask; /* interrupt mask copy */ + + /* + * These are modified in the interrupt handler as well as + * the task queues and other contexts. Thus these must be + * protected by a mutex, or they could clash. + * + * For now, access to these is behind the ATH_LOCK, + * just to save time. + */ + uint32_t sc_txq_active; /* bitmap of active TXQs */ + uint32_t sc_kickpcu; /* whether to kick the PCU */ + uint32_t sc_rxproc_cnt; /* In RX processing */ + uint32_t sc_txproc_cnt; /* In TX processing */ + uint32_t sc_txstart_cnt; /* In TX output (raw/start) */ + uint32_t sc_inreset_cnt; /* In active reset/chanchange */ + uint32_t sc_txrx_cnt; /* refcount on stop/start'ing TX */ + uint32_t sc_intr_cnt; /* refcount on interrupt handling */ + u_int sc_keymax; /* size of key cache */ u_int8_t sc_keymap[ATH_KEYBYTES];/* key use bit map */ + /* + * Software based LED blinking + */ u_int sc_ledpin; /* GPIO pin for driving LED */ u_int sc_ledon; /* pin setting for LED on */ u_int sc_ledidle; /* idle polling interval */ @@ -294,12 +725,17 @@ struct ath_softc { u_int16_t sc_ledoff; /* off time for current blink */ struct callout sc_ledtimer; /* led off timer */ + /* + * Hardware based LED blinking + */ + int sc_led_pwr_pin; /* MAC power LED GPIO pin */ + int sc_led_net_pin; /* MAC network LED GPIO pin */ + u_int sc_rfsilentpin; /* GPIO pin for rfkill int */ u_int sc_rfsilentpol; /* pin setting for rfkill on */ struct ath_descdma sc_rxdma; /* RX descriptors */ ath_bufhead sc_rxbuf; /* receive buffer */ - struct mbuf *sc_rxpending; /* pending receive data */ u_int32_t *sc_rxlink; /* link ptr in last RX desc */ struct task sc_rxtask; /* rx int processing */ u_int8_t sc_defant; /* current default antenna */ @@ -311,14 +747,25 @@ struct ath_softc { u_int sc_monpass; /* frames to pass in mon.mode */ struct ath_descdma sc_txdma; /* TX descriptors */ + uint16_t sc_txbuf_descid; ath_bufhead sc_txbuf; /* transmit buffer */ + int sc_txbuf_cnt; /* how many buffers avail */ + struct ath_descdma sc_txdma_mgmt; /* mgmt TX descriptors */ + ath_bufhead sc_txbuf_mgmt; /* mgmt transmit buffer */ + struct ath_descdma sc_txsdma; /* EDMA TX status desc's */ struct mtx sc_txbuflock; /* txbuf lock */ char sc_txname[12]; /* e.g. "ath0_buf" */ u_int sc_txqsetup; /* h/w queues setup */ u_int sc_txintrperiod;/* tx interrupt batching */ struct ath_txq sc_txq[HAL_NUM_TX_QUEUES]; - struct ath_txq *sc_ac2q[5]; /* WME AC -> h/w q map */ + struct ath_txq *sc_ac2q[5]; /* WME AC -> h/w q map */ struct task sc_txtask; /* tx int processing */ + struct task sc_txqtask; /* tx proc processing */ + + struct ath_descdma sc_txcompdma; /* TX EDMA completion */ + struct mtx sc_txcomplock; /* TX EDMA completion lock */ + char sc_txcompname[12]; /* eg ath0_txcomp */ + int sc_wd_timer; /* count down for wd timer */ struct callout sc_wd_ch; /* tx watchdog timer */ struct ath_tx_radiotap_header sc_tx_th; @@ -332,6 +779,8 @@ struct ath_softc { struct ath_txq *sc_cabq; /* tx q for cab frames */ struct task sc_bmisstask; /* bmiss int processing */ struct task sc_bstucktask; /* stuck beacon processing */ + struct task sc_resettask; /* interface reset task */ + struct task sc_fataltask; /* fatal task */ enum { OK, /* no change needed */ UPDATE, /* update pending */ @@ -357,17 +806,126 @@ struct ath_softc { u_int32_t sc_avgtsfdeltap;/* TDMA slot adjust (+) */ u_int32_t sc_avgtsfdeltam;/* TDMA slot adjust (-) */ uint16_t *sc_eepromdata; /* Local eeprom data, if AR9100 */ - int sc_txchainmask; /* currently configured TX chainmask */ - int sc_rxchainmask; /* currently configured RX chainmask */ + uint32_t sc_txchainmask; /* hardware TX chainmask */ + uint32_t sc_rxchainmask; /* hardware RX chainmask */ + uint32_t sc_cur_txchainmask; /* currently configured TX chainmask */ + uint32_t sc_cur_rxchainmask; /* currently configured RX chainmask */ + uint32_t sc_rts_aggr_limit; /* TX limit on RTS aggregates */ + int sc_aggr_limit; /* TX limit on all aggregates */ + int sc_delim_min_pad; /* Minimum delimiter count */ + + /* Queue limits */ + + /* + * To avoid queue starvation in congested conditions, + * these parameters tune the maximum number of frames + * queued to the data/mcastq before they're dropped. + * + * This is to prevent: + * + a single destination overwhelming everything, including + * management/multicast frames; + * + multicast frames overwhelming everything (when the + * air is sufficiently busy that cabq can't drain.) + * + A node in powersave shouldn't be allowed to exhaust + * all available mbufs; + * + * These implement: + * + data_minfree is the maximum number of free buffers + * overall to successfully allow a data frame. + * + * + mcastq_maxdepth is the maximum depth allowed of the cabq. + */ + int sc_txq_node_maxdepth; + int sc_txq_data_minfree; + int sc_txq_mcastq_maxdepth; + int sc_txq_node_psq_maxdepth; + + /* + * Software queue twiddles + * + * hwq_limit_nonaggr: + * when to begin limiting non-aggregate frames to the + * hardware queue, regardless of the TID. + * hwq_limit_aggr: + * when to begin limiting A-MPDU frames to the + * hardware queue, regardless of the TID. + * tid_hwq_lo: how low the per-TID hwq count has to be before the + * TID will be scheduled again + * tid_hwq_hi: how many frames to queue to the HWQ before the TID + * stops being scheduled. + */ + int sc_hwq_limit_nonaggr; + int sc_hwq_limit_aggr; + int sc_tid_hwq_lo; + int sc_tid_hwq_hi; /* DFS related state */ void *sc_dfs; /* Used by an optional DFS module */ int sc_dodfs; /* Whether to enable DFS rx filter bits */ struct task sc_dfstask; /* DFS processing task */ + /* Spectral related state */ + void *sc_spectral; + int sc_dospectral; + + /* LNA diversity related state */ + void *sc_lna_div; + int sc_dolnadiv; + #if defined(__HAIKU__) HAL_INT sc_intr_status; #endif + + /* ALQ */ +#ifdef ATH_DEBUG_ALQ + struct if_ath_alq sc_alq; +#endif + + /* TX AMPDU handling */ + int (*sc_addba_request)(struct ieee80211_node *, + struct ieee80211_tx_ampdu *, int, int, int); + int (*sc_addba_response)(struct ieee80211_node *, + struct ieee80211_tx_ampdu *, int, int, int); + void (*sc_addba_stop)(struct ieee80211_node *, + struct ieee80211_tx_ampdu *); + void (*sc_addba_response_timeout) + (struct ieee80211_node *, + struct ieee80211_tx_ampdu *); + void (*sc_bar_response)(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap, + int status); + + /* + * Powersave state tracking. + * + * target/cur powerstate is the chip power state. + * target selfgen state is the self-generated frames + * state. The chip can be awake but transmitted frames + * can have the PWRMGT bit set to 1 so the destination + * thinks the node is asleep. + */ + HAL_POWER_MODE sc_target_powerstate; + HAL_POWER_MODE sc_target_selfgen_state; + + HAL_POWER_MODE sc_cur_powerstate; + + int sc_powersave_refcnt; + + /* ATH_PCI_* flags */ + uint32_t sc_pci_devinfo; + + /* BT coex */ + struct { + struct ath_descdma buf; + + /* gpm/sched buffer, saved pointers */ + char *sched_buf; + bus_addr_t sched_paddr; + char *gpm_buf; + bus_addr_t gpm_paddr; + + uint32_t wlan_channels[4]; + } sc_btcoex; }; #define ATH_LOCK_INIT(_sc) \ @@ -377,6 +935,84 @@ struct ath_softc { #define ATH_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define ATH_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define ATH_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) +#define ATH_UNLOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_NOTOWNED) + +/* + * The TX lock is non-reentrant and serialises the TX frame send + * and completion operations. + */ +#define ATH_TX_LOCK_INIT(_sc) do {\ + snprintf((_sc)->sc_tx_mtx_name, \ + sizeof((_sc)->sc_tx_mtx_name), \ + "%s TX lock", \ + device_get_nameunit((_sc)->sc_dev)); \ + mtx_init(&(_sc)->sc_tx_mtx, (_sc)->sc_tx_mtx_name, \ + NULL, MTX_DEF); \ + } while (0) +#define ATH_TX_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_tx_mtx) +#define ATH_TX_LOCK(_sc) mtx_lock(&(_sc)->sc_tx_mtx) +#define ATH_TX_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_tx_mtx) +#define ATH_TX_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_tx_mtx, \ + MA_OWNED) +#define ATH_TX_UNLOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_tx_mtx, \ + MA_NOTOWNED) +#define ATH_TX_TRYLOCK(_sc) (mtx_owned(&(_sc)->sc_tx_mtx) != 0 && \ + mtx_trylock(&(_sc)->sc_tx_mtx)) + +/* + * The PCU lock is non-recursive and should be treated as a spinlock. + * Although currently the interrupt code is run in netisr context and + * doesn't require this, this may change in the future. + * Please keep this in mind when protecting certain code paths + * with the PCU lock. + * + * The PCU lock is used to serialise access to the PCU so things such + * as TX, RX, state change (eg channel change), channel reset and updates + * from interrupt context (eg kickpcu, txqactive bits) do not clash. + * + * Although the current single-thread taskqueue mechanism protects the + * majority of these situations by simply serialising them, there are + * a few others which occur at the same time. These include the TX path + * (which only acquires ATH_LOCK when recycling buffers to the free list), + * ath_set_channel, the channel scanning API and perhaps quite a bit more. + */ +#define ATH_PCU_LOCK_INIT(_sc) do {\ + snprintf((_sc)->sc_pcu_mtx_name, \ + sizeof((_sc)->sc_pcu_mtx_name), \ + "%s PCU lock", \ + device_get_nameunit((_sc)->sc_dev)); \ + mtx_init(&(_sc)->sc_pcu_mtx, (_sc)->sc_pcu_mtx_name, \ + NULL, MTX_DEF); \ + } while (0) +#define ATH_PCU_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_pcu_mtx) +#define ATH_PCU_LOCK(_sc) mtx_lock(&(_sc)->sc_pcu_mtx) +#define ATH_PCU_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_pcu_mtx) +#define ATH_PCU_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_pcu_mtx, \ + MA_OWNED) +#define ATH_PCU_UNLOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_pcu_mtx, \ + MA_NOTOWNED) + +/* + * The RX lock is primarily a(nother) workaround to ensure that the + * RX FIFO/list isn't modified by various execution paths. + * Even though RX occurs in a single context (the ath taskqueue), the + * RX path can be executed via various reset/channel change paths. + */ +#define ATH_RX_LOCK_INIT(_sc) do {\ + snprintf((_sc)->sc_rx_mtx_name, \ + sizeof((_sc)->sc_rx_mtx_name), \ + "%s RX lock", \ + device_get_nameunit((_sc)->sc_dev)); \ + mtx_init(&(_sc)->sc_rx_mtx, (_sc)->sc_rx_mtx_name, \ + NULL, MTX_DEF); \ + } while (0) +#define ATH_RX_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_rx_mtx) +#define ATH_RX_LOCK(_sc) mtx_lock(&(_sc)->sc_rx_mtx) +#define ATH_RX_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_rx_mtx) +#define ATH_RX_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_rx_mtx, \ + MA_OWNED) +#define ATH_RX_UNLOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_rx_mtx, \ + MA_NOTOWNED) #define ATH_TXQ_SETUP(sc, i) ((sc)->sc_txqsetup & (1<sc_txbuflock) #define ATH_TXBUF_LOCK_ASSERT(_sc) \ mtx_assert(&(_sc)->sc_txbuflock, MA_OWNED) +#define ATH_TXBUF_UNLOCK_ASSERT(_sc) \ + mtx_assert(&(_sc)->sc_txbuflock, MA_NOTOWNED) + +#define ATH_TXSTATUS_LOCK_INIT(_sc) do { \ + snprintf((_sc)->sc_txcompname, sizeof((_sc)->sc_txcompname), \ + "%s_buf", \ + device_get_nameunit((_sc)->sc_dev)); \ + mtx_init(&(_sc)->sc_txcomplock, (_sc)->sc_txcompname, NULL, \ + MTX_DEF); \ +} while (0) +#define ATH_TXSTATUS_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_txcomplock) +#define ATH_TXSTATUS_LOCK(_sc) mtx_lock(&(_sc)->sc_txcomplock) +#define ATH_TXSTATUS_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_txcomplock) +#define ATH_TXSTATUS_LOCK_ASSERT(_sc) \ + mtx_assert(&(_sc)->sc_txcomplock, MA_OWNED) int ath_attach(u_int16_t, struct ath_softc *); int ath_detach(struct ath_softc *); @@ -403,8 +1054,9 @@ void ath_intr(void *); */ #define ath_hal_detach(_ah) \ ((*(_ah)->ah_detach)((_ah))) -#define ath_hal_reset(_ah, _opmode, _chan, _outdoor, _pstatus) \ - ((*(_ah)->ah_reset)((_ah), (_opmode), (_chan), (_outdoor), (_pstatus))) +#define ath_hal_reset(_ah, _opmode, _chan, _fullreset, _resettype, _pstatus) \ + ((*(_ah)->ah_reset)((_ah), (_opmode), (_chan), (_fullreset), \ + (_resettype), (_pstatus))) #define ath_hal_macversion(_ah) \ (((_ah)->ah_macVersion << 4) | ((_ah)->ah_macRev)) #define ath_hal_getratetable(_ah, _mode) \ @@ -429,6 +1081,8 @@ void ath_intr(void *); ((*(_ah)->ah_updateTxTrigLevel)((_ah), (_inc))) #define ath_hal_setpower(_ah, _mode) \ ((*(_ah)->ah_setPowerMode)((_ah), (_mode), AH_TRUE)) +#define ath_hal_setselfgenpower(_ah, _mode) \ + ((*(_ah)->ah_setPowerMode)((_ah), (_mode), AH_FALSE)) #define ath_hal_keycachesize(_ah) \ ((*(_ah)->ah_getKeyCacheSize)((_ah))) #define ath_hal_keyreset(_ah, _ix) \ @@ -447,14 +1101,16 @@ void ath_intr(void *); ((*(_ah)->ah_setMulticastFilter)((_ah), (_mfilt0), (_mfilt1))) #define ath_hal_waitforbeacon(_ah, _bf) \ ((*(_ah)->ah_waitForBeaconDone)((_ah), (_bf)->bf_daddr)) -#define ath_hal_putrxbuf(_ah, _bufaddr) \ - ((*(_ah)->ah_setRxDP)((_ah), (_bufaddr))) +#define ath_hal_putrxbuf(_ah, _bufaddr, _rxq) \ + ((*(_ah)->ah_setRxDP)((_ah), (_bufaddr), (_rxq))) /* NB: common across all chips */ #define AR_TSF_L32 0x804c /* MAC local clock lower 32 bits */ #define ath_hal_gettsf32(_ah) \ OS_REG_READ(_ah, AR_TSF_L32) #define ath_hal_gettsf64(_ah) \ ((*(_ah)->ah_getTsf64)((_ah))) +#define ath_hal_settsf64(_ah, _val) \ + ((*(_ah)->ah_setTsf64)((_ah), (_val))) #define ath_hal_resettsf(_ah) \ ((*(_ah)->ah_resetTsf)((_ah))) #define ath_hal_rxena(_ah) \ @@ -465,8 +1121,8 @@ void ath_intr(void *); ((*(_ah)->ah_getTxDP)((_ah), (_q))) #define ath_hal_numtxpending(_ah, _q) \ ((*(_ah)->ah_numTxPending)((_ah), (_q))) -#define ath_hal_getrxbuf(_ah) \ - ((*(_ah)->ah_getRxDP)((_ah))) +#define ath_hal_getrxbuf(_ah, _rxq) \ + ((*(_ah)->ah_getRxDP)((_ah), (_rxq))) #define ath_hal_txstart(_ah, _q) \ ((*(_ah)->ah_startTxDma)((_ah), (_q))) #define ath_hal_setchannel(_ah, _chan) \ @@ -647,26 +1303,80 @@ void ath_intr(void *); #define ath_hal_settpcts(_ah, _tpcts) \ ath_hal_setcapability(_ah, HAL_CAP_TPC_CTS, 0, _tpcts, NULL) #define ath_hal_hasintmit(_ah) \ - (ath_hal_getcapability(_ah, HAL_CAP_INTMIT, HAL_CAP_INTMIT_PRESENT, NULL) == HAL_OK) + (ath_hal_getcapability(_ah, HAL_CAP_INTMIT, \ + HAL_CAP_INTMIT_PRESENT, NULL) == HAL_OK) #define ath_hal_getintmit(_ah) \ - (ath_hal_getcapability(_ah, HAL_CAP_INTMIT, HAL_CAP_INTMIT_ENABLE, NULL) == HAL_OK) + (ath_hal_getcapability(_ah, HAL_CAP_INTMIT, \ + HAL_CAP_INTMIT_ENABLE, NULL) == HAL_OK) #define ath_hal_setintmit(_ah, _v) \ - ath_hal_setcapability(_ah, HAL_CAP_INTMIT, HAL_CAP_INTMIT_ENABLE, _v, NULL) + ath_hal_setcapability(_ah, HAL_CAP_INTMIT, \ + HAL_CAP_INTMIT_ENABLE, _v, NULL) +#define ath_hal_hasmybeacon(_ah) \ + (ath_hal_getcapability(_ah, HAL_CAP_DO_MYBEACON, 1, NULL) == HAL_OK) + +#define ath_hal_hasenforcetxop(_ah) \ + (ath_hal_getcapability(_ah, HAL_CAP_ENFORCE_TXOP, 0, NULL) == HAL_OK) +#define ath_hal_getenforcetxop(_ah) \ + (ath_hal_getcapability(_ah, HAL_CAP_ENFORCE_TXOP, 1, NULL) == HAL_OK) +#define ath_hal_setenforcetxop(_ah, _v) \ + ath_hal_setcapability(_ah, HAL_CAP_ENFORCE_TXOP, 1, _v, NULL) + +#define ath_hal_hasrxlnamixer(_ah) \ + (ath_hal_getcapability(_ah, HAL_CAP_RX_LNA_MIXING, 0, NULL) == HAL_OK) + +#define ath_hal_hasdivantcomb(_ah) \ + (ath_hal_getcapability(_ah, HAL_CAP_ANT_DIV_COMB, 0, NULL) == HAL_OK) +#define ath_hal_hasldpc(_ah) \ + (ath_hal_getcapability(_ah, HAL_CAP_LDPC, 0, NULL) == HAL_OK) +#define ath_hal_hasldpcwar(_ah) \ + (ath_hal_getcapability(_ah, HAL_CAP_LDPCWAR, 0, NULL) == HAL_OK) + +/* EDMA definitions */ +#define ath_hal_hasedma(_ah) \ + (ath_hal_getcapability(_ah, HAL_CAP_ENHANCED_DMA_SUPPORT, \ + 0, NULL) == HAL_OK) +#define ath_hal_getrxfifodepth(_ah, _qtype, _req) \ + (ath_hal_getcapability(_ah, HAL_CAP_RXFIFODEPTH, _qtype, _req) \ + == HAL_OK) +#define ath_hal_getntxmaps(_ah, _req) \ + (ath_hal_getcapability(_ah, HAL_CAP_NUM_TXMAPS, 0, _req) \ + == HAL_OK) +#define ath_hal_gettxdesclen(_ah, _req) \ + (ath_hal_getcapability(_ah, HAL_CAP_TXDESCLEN, 0, _req) \ + == HAL_OK) +#define ath_hal_gettxstatuslen(_ah, _req) \ + (ath_hal_getcapability(_ah, HAL_CAP_TXSTATUSLEN, 0, _req) \ + == HAL_OK) +#define ath_hal_getrxstatuslen(_ah, _req) \ + (ath_hal_getcapability(_ah, HAL_CAP_RXSTATUSLEN, 0, _req) \ + == HAL_OK) +#define ath_hal_setrxbufsize(_ah, _req) \ + (ath_hal_setcapability(_ah, HAL_CAP_RXBUFSIZE, 0, _req, NULL) \ + == HAL_OK) + #define ath_hal_getchannoise(_ah, _c) \ ((*(_ah)->ah_getChanNoise)((_ah), (_c))) + +/* 802.11n HAL methods */ #define ath_hal_getrxchainmask(_ah, _prxchainmask) \ (ath_hal_getcapability(_ah, HAL_CAP_RX_CHAINMASK, 0, _prxchainmask)) #define ath_hal_gettxchainmask(_ah, _ptxchainmask) \ (ath_hal_getcapability(_ah, HAL_CAP_TX_CHAINMASK, 0, _ptxchainmask)) +#define ath_hal_setrxchainmask(_ah, _rx) \ + (ath_hal_setcapability(_ah, HAL_CAP_RX_CHAINMASK, 1, _rx, NULL)) +#define ath_hal_settxchainmask(_ah, _tx) \ + (ath_hal_setcapability(_ah, HAL_CAP_TX_CHAINMASK, 1, _tx, NULL)) #define ath_hal_split4ktrans(_ah) \ - (ath_hal_getcapability(_ah, HAL_CAP_SPLIT_4KB_TRANS, 0, NULL) == HAL_OK) + (ath_hal_getcapability(_ah, HAL_CAP_SPLIT_4KB_TRANS, \ + 0, NULL) == HAL_OK) #define ath_hal_self_linked_final_rxdesc(_ah) \ - (ath_hal_getcapability(_ah, HAL_CAP_RXDESC_SELFLINK, 0, NULL) == HAL_OK) + (ath_hal_getcapability(_ah, HAL_CAP_RXDESC_SELFLINK, \ + 0, NULL) == HAL_OK) #define ath_hal_gtxto_supported(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_GTXTO, 0, NULL) == HAL_OK) #define ath_hal_has_long_rxdesc_tsf(_ah) \ - (ath_hal_getcapability(_ah, HAL_CAP_LONG_RXDESC_TSF, 0, NULL) == HAL_OK) - + (ath_hal_getcapability(_ah, HAL_CAP_LONG_RXDESC_TSF, \ + 0, NULL) == HAL_OK) #define ath_hal_setuprxdesc(_ah, _ds, _size, _intreq) \ ((*(_ah)->ah_setupRxDesc)((_ah), (_ds), (_size), (_intreq))) #define ath_hal_rxprocdesc(_ah, _ds, _dspa, _dsnext, _rs) \ @@ -681,33 +1391,73 @@ void ath_intr(void *); _txr1, _txtr1, _txr2, _txtr2, _txr3, _txtr3) \ ((*(_ah)->ah_setupXTxDesc)((_ah), (_ds), \ (_txr1), (_txtr1), (_txr2), (_txtr2), (_txr3), (_txtr3))) -#define ath_hal_filltxdesc(_ah, _ds, _l, _first, _last, _ds0) \ - ((*(_ah)->ah_fillTxDesc)((_ah), (_ds), (_l), (_first), (_last), (_ds0))) +#define ath_hal_filltxdesc(_ah, _ds, _b, _l, _did, _qid, _first, _last, _ds0) \ + ((*(_ah)->ah_fillTxDesc)((_ah), (_ds), (_b), (_l), (_did), (_qid), \ + (_first), (_last), (_ds0))) #define ath_hal_txprocdesc(_ah, _ds, _ts) \ ((*(_ah)->ah_procTxDesc)((_ah), (_ds), (_ts))) #define ath_hal_gettxintrtxqs(_ah, _txqs) \ ((*(_ah)->ah_getTxIntrQueue)((_ah), (_txqs))) #define ath_hal_gettxcompletionrates(_ah, _ds, _rates, _tries) \ ((*(_ah)->ah_getTxCompletionRates)((_ah), (_ds), (_rates), (_tries))) +#define ath_hal_settxdesclink(_ah, _ds, _link) \ + ((*(_ah)->ah_setTxDescLink)((_ah), (_ds), (_link))) +#define ath_hal_gettxdesclink(_ah, _ds, _link) \ + ((*(_ah)->ah_getTxDescLink)((_ah), (_ds), (_link))) +#define ath_hal_gettxdesclinkptr(_ah, _ds, _linkptr) \ + ((*(_ah)->ah_getTxDescLinkPtr)((_ah), (_ds), (_linkptr))) +#define ath_hal_setuptxstatusring(_ah, _tsstart, _tspstart, _size) \ + ((*(_ah)->ah_setupTxStatusRing)((_ah), (_tsstart), (_tspstart), \ + (_size))) +#define ath_hal_gettxrawtxdesc(_ah, _txstatus) \ + ((*(_ah)->ah_getTxRawTxDesc)((_ah), (_txstatus))) -#define ath_hal_chaintxdesc(_ah, _ds, _pktlen, _hdrlen, _type, _keyix, \ - _cipher, _delims, _seglen, _first, _last) \ - ((*(_ah)->ah_chainTxDesc((_ah), (_ds), (_pktlen), (_hdrlen), \ - (_type), (_keyix), (_cipher), (_delims), (_seglen), \ - (_first), (_last)))) #define ath_hal_setupfirsttxdesc(_ah, _ds, _aggrlen, _flags, _txpower, \ _txr0, _txtr0, _antm, _rcr, _rcd) \ ((*(_ah)->ah_setupFirstTxDesc)((_ah), (_ds), (_aggrlen), (_flags), \ (_txpower), (_txr0), (_txtr0), (_antm), (_rcr), (_rcd))) +#define ath_hal_chaintxdesc(_ah, _ds, _bl, _sl, _pktlen, _hdrlen, _type, \ + _keyix, _cipher, _delims, _first, _last, _lastaggr) \ + ((*(_ah)->ah_chainTxDesc)((_ah), (_ds), (_bl), (_sl), \ + (_pktlen), (_hdrlen), (_type), (_keyix), (_cipher), (_delims), \ + (_first), (_last), (_lastaggr))) #define ath_hal_setuplasttxdesc(_ah, _ds, _ds0) \ ((*(_ah)->ah_setupLastTxDesc)((_ah), (_ds), (_ds0))) + #define ath_hal_set11nratescenario(_ah, _ds, _dur, _rt, _series, _ns, _flags) \ ((*(_ah)->ah_set11nRateScenario)((_ah), (_ds), (_dur), (_rt), \ (_series), (_ns), (_flags))) -#define ath_hal_set11naggrmiddle(_ah, _ds, _num) \ - ((*(_ah)->ah_set11nAggrMiddle((_ah), (_ds), (_num)))) + +#define ath_hal_set11n_aggr_first(_ah, _ds, _len, _num) \ + ((*(_ah)->ah_set11nAggrFirst)((_ah), (_ds), (_len), (_num))) +#define ath_hal_set11n_aggr_middle(_ah, _ds, _num) \ + ((*(_ah)->ah_set11nAggrMiddle)((_ah), (_ds), (_num))) +#define ath_hal_set11n_aggr_last(_ah, _ds) \ + ((*(_ah)->ah_set11nAggrLast)((_ah), (_ds))) + #define ath_hal_set11nburstduration(_ah, _ds, _dur) \ ((*(_ah)->ah_set11nBurstDuration)((_ah), (_ds), (_dur))) +#define ath_hal_clr11n_aggr(_ah, _ds) \ + ((*(_ah)->ah_clr11nAggr)((_ah), (_ds))) +#define ath_hal_set11n_virtmorefrag(_ah, _ds, _v) \ + ((*(_ah)->ah_set11nVirtMoreFrag)((_ah), (_ds), (_v))) + +#define ath_hal_gpioCfgOutput(_ah, _gpio, _type) \ + ((*(_ah)->ah_gpioCfgOutput)((_ah), (_gpio), (_type))) +#define ath_hal_gpioset(_ah, _gpio, _b) \ + ((*(_ah)->ah_gpioSet)((_ah), (_gpio), (_b))) +#define ath_hal_gpioget(_ah, _gpio) \ + ((*(_ah)->ah_gpioGet)((_ah), (_gpio))) +#define ath_hal_gpiosetintr(_ah, _gpio, _b) \ + ((*(_ah)->ah_gpioSetIntr)((_ah), (_gpio), (_b))) + +/* + * PCIe suspend/resume/poweron/poweroff related macros + */ +#define ath_hal_enablepcie(_ah, _restore, _poweroff) \ + ((*(_ah)->ah_configPCIE)((_ah), (_restore), (_poweroff))) +#define ath_hal_disablepcie(_ah) \ + ((*(_ah)->ah_disablePCIE)((_ah))) /* * This is badly-named; you need to set the correct parameters @@ -719,21 +1469,66 @@ void ath_intr(void *); ((*(_ah)->ah_enableDfs)((_ah), (_param))) #define ath_hal_getdfsthresh(_ah, _param) \ ((*(_ah)->ah_getDfsThresh)((_ah), (_param))) +#define ath_hal_getdfsdefaultthresh(_ah, _param) \ + ((*(_ah)->ah_getDfsDefaultThresh)((_ah), (_param))) #define ath_hal_procradarevent(_ah, _rxs, _fulltsf, _buf, _event) \ - ((*(_ah)->ah_procRadarEvent)((_ah), (_rxs), (_fulltsf), (_buf), (_event))) + ((*(_ah)->ah_procRadarEvent)((_ah), (_rxs), (_fulltsf), \ + (_buf), (_event))) #define ath_hal_is_fast_clock_enabled(_ah) \ ((*(_ah)->ah_isFastClockEnabled)((_ah))) - -#define ath_hal_gpioCfgOutput(_ah, _gpio, _type) \ - ((*(_ah)->ah_gpioCfgOutput)((_ah), (_gpio), (_type))) -#define ath_hal_gpioset(_ah, _gpio, _b) \ - ((*(_ah)->ah_gpioSet)((_ah), (_gpio), (_b))) -#define ath_hal_gpioget(_ah, _gpio) \ - ((*(_ah)->ah_gpioGet)((_ah), (_gpio))) -#define ath_hal_gpiosetintr(_ah, _gpio, _b) \ - ((*(_ah)->ah_gpioSetIntr)((_ah), (_gpio), (_b))) - -#define ath_hal_radar_wait(_ah, _chan) \ +#define ath_hal_radar_wait(_ah, _chan) \ ((*(_ah)->ah_radarWait)((_ah), (_chan))) +#define ath_hal_get_mib_cycle_counts(_ah, _sample) \ + ((*(_ah)->ah_getMibCycleCounts)((_ah), (_sample))) +#define ath_hal_get_chan_ext_busy(_ah) \ + ((*(_ah)->ah_get11nExtBusy)((_ah))) +#define ath_hal_setchainmasks(_ah, _txchainmask, _rxchainmask) \ + ((*(_ah)->ah_setChainMasks)((_ah), (_txchainmask), (_rxchainmask))) + +#define ath_hal_spectral_supported(_ah) \ + (ath_hal_getcapability(_ah, HAL_CAP_SPECTRAL_SCAN, 0, NULL) == HAL_OK) +#define ath_hal_spectral_get_config(_ah, _p) \ + ((*(_ah)->ah_spectralGetConfig)((_ah), (_p))) +#define ath_hal_spectral_configure(_ah, _p) \ + ((*(_ah)->ah_spectralConfigure)((_ah), (_p))) +#define ath_hal_spectral_start(_ah) \ + ((*(_ah)->ah_spectralStart)((_ah))) +#define ath_hal_spectral_stop(_ah) \ + ((*(_ah)->ah_spectralStop)((_ah))) + +#define ath_hal_btcoex_supported(_ah) \ + (ath_hal_getcapability(_ah, HAL_CAP_BT_COEX, 0, NULL) == HAL_OK) +#define ath_hal_btcoex_set_info(_ah, _info) \ + ((*(_ah)->ah_btCoexSetInfo)((_ah), (_info))) +#define ath_hal_btcoex_set_config(_ah, _cfg) \ + ((*(_ah)->ah_btCoexSetConfig)((_ah), (_cfg))) +#define ath_hal_btcoex_set_qcu_thresh(_ah, _qcuid) \ + ((*(_ah)->ah_btCoexSetQcuThresh)((_ah), (_qcuid))) +#define ath_hal_btcoex_set_weights(_ah, _weight) \ + ((*(_ah)->ah_btCoexSetWeights)((_ah), (_weight))) +#define ath_hal_btcoex_set_bmiss_thresh(_ah, _thr) \ + ((*(_ah)->ah_btCoexSetBmissThresh)((_ah), (_thr))) +#define ath_hal_btcoex_set_parameter(_ah, _attrib, _val) \ + ((*(_ah)->ah_btCoexSetParameter)((_ah), (_attrib), (_val))) +#define ath_hal_btcoex_enable(_ah) \ + ((*(_ah)->ah_btCoexEnable)((_ah))) +#define ath_hal_btcoex_disable(_ah) \ + ((*(_ah)->ah_btCoexDisable)((_ah))) + +#define ath_hal_btcoex_mci_setup(_ah, _gp, _gb, _gl, _sp) \ + ((*(_ah)->ah_btMciSetup)((_ah), (_gp), (_gb), (_gl), (_sp))) +#define ath_hal_btcoex_mci_send_message(_ah, _h, _f, _p, _l, _wd, _cbt) \ + ((*(_ah)->ah_btMciSendMessage)((_ah), (_h), (_f), (_p), (_l), (_wd), (_cbt))) +#define ath_hal_btcoex_mci_get_interrupt(_ah, _mi, _mm) \ + ((*(_ah)->ah_btMciGetInterrupt)((_ah), (_mi), (_mm))) +#define ath_hal_btcoex_mci_state(_ah, _st, _pd) \ + ((*(_ah)->ah_btMciState)((_ah), (_st), (_pd))) +#define ath_hal_btcoex_mci_detach(_ah) \ + ((*(_ah)->ah_btMciDetach)((_ah))) + +#define ath_hal_div_comb_conf_get(_ah, _conf) \ + ((*(_ah)->ah_divLnaConfGet)((_ah), (_conf))) +#define ath_hal_div_comb_conf_set(_ah, _conf) \ + ((*(_ah)->ah_divLnaConfSet)((_ah), (_conf))) #endif /* _DEV_ATH_ATHVAR_H */ diff --git a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/glue.c b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/glue.c index 0b1eaf75e7..eb94691905 100644 --- a/src/add-ons/kernel/drivers/network/wlan/atheroswifi/glue.c +++ b/src/add-ons/kernel/drivers/network/wlan/atheroswifi/glue.c @@ -17,7 +17,7 @@ HAIKU_FBSD_WLAN_DRIVER_GLUE(atheroswifi, ath_pci, pci) NO_HAIKU_FBSD_MII_DRIVER(); -HAIKU_DRIVER_REQUIREMENTS(FBSD_TASKQUEUES | FBSD_WLAN); +HAIKU_DRIVER_REQUIREMENTS(FBSD_WLAN); int