Index: head/sys/dev/ath/ah_osdep.c =================================================================== --- head/sys/dev/ath/ah_osdep.c (revision 318855) +++ head/sys/dev/ath/ah_osdep.c (revision 318856) @@ -1,422 +1,451 @@ /*- * Copyright (c) 2002-2008 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$ */ #include "opt_ah.h" #include #include #include #include #include #include #include #include #include #include #include +#include #include #include /* XXX for ether_sprintf */ #include #include /* * WiSoC boards overload the bus tag with information about the * board layout. We must extract the bus space tag from that * indirect structure. For everyone else the tag is passed in * directly. * XXX cache indirect ref privately */ #ifdef AH_SUPPORT_AR5312 #define BUSTAG(ah) \ ((bus_space_tag_t) ((struct ar531x_config *)((ah)->ah_st))->tag) #else #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. */ struct mtx ah_regser_mtx; MTX_SYSINIT(ah_regser, &ah_regser_mtx, "Atheros register access mutex", MTX_SPIN); 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) __printflike(2, 0); extern const char* ath_hal_ether_sprintf(const u_int8_t *mac); extern void *ath_hal_malloc(size_t); extern void ath_hal_free(void *); #ifdef AH_ASSERT extern void ath_hal_assert_failed(const char* filename, int lineno, const char* msg); #endif #ifdef AH_DEBUG extern void DO_HALDEBUG(struct ath_hal *ah, u_int mask, const char* fmt, ...); #endif /* AH_DEBUG */ /* NB: put this here instead of the driver to avoid circular references */ SYSCTL_NODE(_hw, OID_AUTO, ath, CTLFLAG_RD, 0, "Atheros driver parameters"); static SYSCTL_NODE(_hw_ath, OID_AUTO, hal, CTLFLAG_RD, 0, "Atheros HAL parameters"); #ifdef AH_DEBUG int ath_hal_debug = 0; SYSCTL_INT(_hw_ath_hal, OID_AUTO, debug, CTLFLAG_RWTUN, &ath_hal_debug, 0, "Atheros HAL debugging printfs"); #endif /* AH_DEBUG */ static MALLOC_DEFINE(M_ATH_HAL, "ath_hal", "ath hal data"); void* ath_hal_malloc(size_t size) { return malloc(size, M_ATH_HAL, M_NOWAIT | M_ZERO); } void ath_hal_free(void* p) { free(p, M_ATH_HAL); } void ath_hal_vprintf(struct ath_hal *ah, const char* fmt, va_list ap) { vprintf(fmt, ap); } void ath_hal_printf(struct ath_hal *ah, const char* fmt, ...) { va_list ap; va_start(ap, fmt); ath_hal_vprintf(ah, fmt, ap); va_end(ap); } const char* ath_hal_ether_sprintf(const u_int8_t *mac) { return ether_sprintf(mac); } #ifdef AH_DEBUG /* * 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 != NULL && ah->ah_config.ah_debug & mask) || (ath_hal_debug & mask)) { __va_list ap; va_start(ap, fmt); ath_hal_vprintf(ah, fmt, ap); va_end(ap); } } #undef HAL_DEBUG_UNMASKABLE #endif /* AH_DEBUG */ #ifdef AH_DEBUG_ALQ /* * ALQ register tracing support. * * Setting hw.ath.hal.alq=1 enables tracing of all register reads and * writes to the file /tmp/ath_hal.log. The file format is a simple * fixed-size array of records. When done logging set hw.ath.hal.alq=0 * and then decode the file with the arcode program (that is part of the * HAL). If you start+stop tracing the data will be appended to an * existing file. * * NB: doesn't handle multiple devices properly; only one DEVICE record * is emitted and the different devices are not identified. */ #include #include #include static struct alq *ath_hal_alq; static int ath_hal_alq_emitdev; /* need to emit DEVICE record */ static u_int ath_hal_alq_lost; /* count of lost records */ static char ath_hal_logfile[MAXPATHLEN] = "/tmp/ath_hal.log"; SYSCTL_STRING(_hw_ath_hal, OID_AUTO, alq_logfile, CTLFLAG_RW, &ath_hal_logfile, sizeof(kernelname), "Name of ALQ logfile"); static u_int ath_hal_alq_qsize = 64*1024; static int ath_hal_setlogging(int enable) { int error; if (enable) { error = alq_open(&ath_hal_alq, ath_hal_logfile, curthread->td_ucred, ALQ_DEFAULT_CMODE, sizeof (struct athregrec), ath_hal_alq_qsize); ath_hal_alq_lost = 0; ath_hal_alq_emitdev = 1; printf("ath_hal: logging to %s enabled\n", ath_hal_logfile); } else { if (ath_hal_alq) alq_close(ath_hal_alq); ath_hal_alq = NULL; printf("ath_hal: logging disabled\n"); error = 0; } return (error); } static int sysctl_hw_ath_hal_log(SYSCTL_HANDLER_ARGS) { int error, enable; enable = (ath_hal_alq != NULL); error = sysctl_handle_int(oidp, &enable, 0, req); if (error || !req->newptr) return (error); else return (ath_hal_setlogging(enable)); } SYSCTL_PROC(_hw_ath_hal, OID_AUTO, alq, CTLTYPE_INT|CTLFLAG_RW, 0, 0, sysctl_hw_ath_hal_log, "I", "Enable HAL register logging"); SYSCTL_INT(_hw_ath_hal, OID_AUTO, alq_size, CTLFLAG_RW, &ath_hal_alq_qsize, 0, "In-memory log size (#records)"); SYSCTL_INT(_hw_ath_hal, OID_AUTO, alq_lost, CTLFLAG_RW, &ath_hal_alq_lost, 0, "Register operations not logged"); static struct ale * ath_hal_alq_get(struct ath_hal *ah) { struct ale *ale; if (ath_hal_alq_emitdev) { ale = alq_get(ath_hal_alq, ALQ_NOWAIT); if (ale) { struct athregrec *r = (struct athregrec *) ale->ae_data; r->op = OP_DEVICE; r->reg = 0; r->val = ah->ah_devid; alq_post(ath_hal_alq, ale); ath_hal_alq_emitdev = 0; } else ath_hal_alq_lost++; } ale = alq_get(ath_hal_alq, ALQ_NOWAIT); if (!ale) ath_hal_alq_lost++; return ale; } void 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 (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 ath_hal_reg_read(struct ath_hal *ah, u_int32_t reg) { bus_space_tag_t tag = BUSTAG(ah); bus_space_handle_t h = ah->ah_sh; u_int32_t val; #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 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; alq_post(ath_hal_alq, ale); } } return val; } void OS_MARK(struct ath_hal *ah, u_int id, u_int32_t v) { 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_MARK; r->reg = id; r->val = v; alq_post(ath_hal_alq, ale); } } } #else /* AH_DEBUG_ALQ */ /* * Memory-mapped device register read/write. These are here * as routines when debugging support is enabled and/or when * explicitly configured to use function calls. The latter is * for architectures that might need to do something before * referencing memory (e.g. remap an i/o window). * * NB: see the comments in ah_osdep.h about byte-swapping register * reads and writes to understand what's going on below. */ void 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 (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 ath_hal_reg_read(struct ath_hal *ah, u_int32_t reg) { bus_space_tag_t tag = BUSTAG(ah); bus_space_handle_t h = ah->ah_sh; u_int32_t val; #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 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_ALQ */ #ifdef AH_ASSERT void ath_hal_assert_failed(const char* filename, int lineno, const char *msg) { printf("Atheros HAL assertion failure: %s: line %u: %s\n", filename, lineno, msg); panic("ath_hal_assert"); } #endif /* AH_ASSERT */ + +static int +ath_hal_modevent(module_t mod __unused, int type, void *data __unused) +{ + int error = 0; + + switch (type) { + case MOD_LOAD: + printf("[ath_hal] loaded\n"); + break; + + case MOD_UNLOAD: + printf("[ath_hal] unloaded\n"); + break; + + case MOD_SHUTDOWN: + break; + + default: + error = EOPNOTSUPP; + break; + + } + return (error); +} + +DEV_MODULE(ath_hal, ath_hal_modevent, NULL); +MODULE_VERSION(ath_hal, 1); Index: head/sys/dev/ath/ah_osdep_ar5210.c =================================================================== --- head/sys/dev/ath/ah_osdep_ar5210.c (nonexistent) +++ head/sys/dev/ath/ah_osdep_ar5210.c (revision 318856) @@ -0,0 +1,74 @@ +/*- + * Copyright 2017 Adrian Chadd . + * + * 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. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, 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 DAMAGE. + * + * $FreeBSD$ + * + */ + +#include "opt_ah.h" +#include "opt_wlan.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +extern struct ath_hal_chip AR5210_chip; + +static int +ath_hal_ar5210_modevent(module_t mod __unused, int type, void *data __unused) +{ + int error = 0; + + switch (type) { + case MOD_LOAD: + ath_hal_add_chip(&AR5210_chip); + printf("[ar5210] loaded\n"); + break; + + case MOD_UNLOAD: + ath_hal_remove_chip(&AR5210_chip); + printf("[ar5210] unloaded\n"); + break; + + case MOD_SHUTDOWN: + break; + + default: + error = EOPNOTSUPP; + break; + + } + return (error); +} + +DEV_MODULE(ath_hal_ar5210, ath_hal_ar5210_modevent, NULL); +MODULE_VERSION(ath_hal_ar5210, 1); +MODULE_DEPEND(ath_hal_ar5210, ath_hal, 1, 1, 1); Property changes on: head/sys/dev/ath/ah_osdep_ar5210.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/dev/ath/ah_osdep_ar5211.c =================================================================== --- head/sys/dev/ath/ah_osdep_ar5211.c (nonexistent) +++ head/sys/dev/ath/ah_osdep_ar5211.c (revision 318856) @@ -0,0 +1,74 @@ +/*- + * Copyright 2017 Adrian Chadd . + * + * 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. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, 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 DAMAGE. + * + * $FreeBSD$ + * + */ + +#include "opt_ah.h" +#include "opt_wlan.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +extern struct ath_hal_chip AR5211_chip; + +static int +ath_hal_ar5211_modevent(module_t mod __unused, int type, void *data __unused) +{ + int error = 0; + + switch (type) { + case MOD_LOAD: + ath_hal_add_chip(&AR5211_chip); + printf("[ar5211] loaded\n"); + break; + + case MOD_UNLOAD: + ath_hal_remove_chip(&AR5211_chip); + printf("[ar5211] unloaded\n"); + break; + + case MOD_SHUTDOWN: + break; + + default: + error = EOPNOTSUPP; + break; + + } + return (error); +} + +DEV_MODULE(ath_hal_ar5211, ath_hal_ar5211_modevent, NULL); +MODULE_VERSION(ath_hal_ar5211, 1); +MODULE_DEPEND(ath_hal_ar5211, ath_hal, 1, 1, 1); Property changes on: head/sys/dev/ath/ah_osdep_ar5211.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/dev/ath/ah_osdep_ar5212.c =================================================================== --- head/sys/dev/ath/ah_osdep_ar5212.c (nonexistent) +++ head/sys/dev/ath/ah_osdep_ar5212.c (revision 318856) @@ -0,0 +1,95 @@ +/*- + * Copyright 2017 Adrian Chadd . + * + * 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. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, 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 DAMAGE. + * + * $FreeBSD$ + * + */ + +#include "opt_ah.h" +#include "opt_wlan.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +extern struct ath_hal_chip AR5212_chip; +//extern struct ath_hal_rf RF2316_rf; +//extern struct ath_hal_rf RF2317_rf; +extern struct ath_hal_rf RF2413_rf; +extern struct ath_hal_rf RF2425_rf; +extern struct ath_hal_rf RF5111_rf; +extern struct ath_hal_rf RF5112_rf; +extern struct ath_hal_rf RF5413_rf; + +static int +ath_hal_ar5212_modevent(module_t mod __unused, int type, void *data __unused) +{ + int error = 0; + + switch (type) { + case MOD_LOAD: + ath_hal_add_chip(&AR5212_chip); +// ath_hal_add_rf(&RF2316_rf); +// ath_hal_add_rf(&RF2317_rf); + ath_hal_add_rf(&RF2413_rf); + ath_hal_add_rf(&RF2425_rf); + ath_hal_add_rf(&RF5111_rf); + ath_hal_add_rf(&RF5112_rf); + ath_hal_add_rf(&RF5413_rf); + printf("[ar5212] loaded\n"); + break; + + case MOD_UNLOAD: + ath_hal_remove_chip(&AR5212_chip); +// ath_hal_remove_rf(&RF2316_rf); +// ath_hal_remove_rf(&RF2317_rf); + ath_hal_remove_rf(&RF2413_rf); + ath_hal_remove_rf(&RF2425_rf); + ath_hal_remove_rf(&RF5111_rf); + ath_hal_remove_rf(&RF5112_rf); + ath_hal_remove_rf(&RF5413_rf); + printf("[ar5212] unloaded\n"); + break; + + case MOD_SHUTDOWN: + break; + + default: + error = EOPNOTSUPP; + break; + + } + return (error); +} + +DEV_MODULE(ath_hal_ar5212, ath_hal_ar5212_modevent, NULL); +MODULE_VERSION(ath_hal_ar5212, 1); +MODULE_DEPEND(ath_hal_ar5212, ath_hal, 1, 1, 1); Property changes on: head/sys/dev/ath/ah_osdep_ar5212.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/dev/ath/ah_osdep_ar5416.c =================================================================== --- head/sys/dev/ath/ah_osdep_ar5416.c (nonexistent) +++ head/sys/dev/ath/ah_osdep_ar5416.c (revision 318856) @@ -0,0 +1,103 @@ +/*- + * Copyright 2017 Adrian Chadd . + * + * 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. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, 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 DAMAGE. + * + * $FreeBSD$ + * + */ + +#include "opt_ah.h" +#include "opt_wlan.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +extern struct ath_hal_chip AR5416_chip; +extern struct ath_hal_chip AR9130_chip; +extern struct ath_hal_chip AR9160_chip; +extern struct ath_hal_chip AR9280_chip; +extern struct ath_hal_chip AR9285_chip; +extern struct ath_hal_chip AR9287_chip; + +extern struct ath_hal_rf RF2133_rf; +extern struct ath_hal_rf RF9280_rf; +extern struct ath_hal_rf RF9285_rf; +extern struct ath_hal_rf RF9287_rf; + +static int +ath_hal_ar5416_modevent(module_t mod __unused, int type, void *data __unused) +{ + int error = 0; + + switch (type) { + case MOD_LOAD: + ath_hal_add_chip(&AR5416_chip); + ath_hal_add_chip(&AR9130_chip); + ath_hal_add_chip(&AR9160_chip); + ath_hal_add_chip(&AR9280_chip); + ath_hal_add_chip(&AR9285_chip); + ath_hal_add_chip(&AR9287_chip); + ath_hal_add_rf(&RF2133_rf); + ath_hal_add_rf(&RF9280_rf); + ath_hal_add_rf(&RF9285_rf); + ath_hal_add_rf(&RF9287_rf); + printf("[ar5416] loaded\n"); + break; + + case MOD_UNLOAD: + ath_hal_remove_chip(&AR5416_chip); + ath_hal_remove_chip(&AR9130_chip); + ath_hal_remove_chip(&AR9160_chip); + ath_hal_remove_chip(&AR9280_chip); + ath_hal_remove_chip(&AR9285_chip); + ath_hal_remove_chip(&AR9287_chip); + ath_hal_remove_rf(&RF2133_rf); + ath_hal_remove_rf(&RF9280_rf); + ath_hal_remove_rf(&RF9285_rf); + ath_hal_remove_rf(&RF9287_rf); + printf("[ar5416] unloaded\n"); + break; + + case MOD_SHUTDOWN: + break; + + default: + error = EOPNOTSUPP; + break; + + } + return (error); +} + +DEV_MODULE(ath_hal_ar5416, ath_hal_ar5416_modevent, NULL); +MODULE_VERSION(ath_hal_ar5416, 1); +MODULE_DEPEND(ath_hal_ar5416, ath_hal, 1, 1, 1); +MODULE_DEPEND(ath_hal_ar5416, ath_hal_ar5212, 1, 1, 1); Property changes on: head/sys/dev/ath/ah_osdep_ar5416.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/dev/ath/ah_osdep_ar9300.c =================================================================== --- head/sys/dev/ath/ah_osdep_ar9300.c (nonexistent) +++ head/sys/dev/ath/ah_osdep_ar9300.c (revision 318856) @@ -0,0 +1,74 @@ +/*- + * Copyright 2017 Adrian Chadd . + * + * 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. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, 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 DAMAGE. + * + * $FreeBSD$ + * + */ + +#include "opt_ah.h" +#include "opt_wlan.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +extern struct ath_hal_chip AR9300_chip; + +static int +ath_hal_ar9300_modevent(module_t mod __unused, int type, void *data __unused) +{ + int error = 0; + + switch (type) { + case MOD_LOAD: + ath_hal_add_chip(&AR9300_chip); + printf("[ar9300] loaded\n"); + break; + + case MOD_UNLOAD: + ath_hal_remove_chip(&AR9300_chip); + printf("[ar9300] unloaded\n"); + break; + + case MOD_SHUTDOWN: + break; + + default: + error = EOPNOTSUPP; + break; + + } + return (error); +} + +DEV_MODULE(ath_hal_ar9300, ath_hal_ar9300_modevent, NULL); +MODULE_VERSION(ath_hal_ar9300, 1); +MODULE_DEPEND(ath_hal_ar9300, ath_hal, 1, 1, 1); Property changes on: head/sys/dev/ath/ah_osdep_ar9300.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/dev/ath/ath_dfs/null/dfs_null.c =================================================================== --- head/sys/dev/ath/ath_dfs/null/dfs_null.c (revision 318855) +++ head/sys/dev/ath/ath_dfs/null/dfs_null.c (revision 318856) @@ -1,295 +1,295 @@ /*- * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd * 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$ */ #include __FBSDID("$FreeBSD$"); /* * This implements an empty DFS module. */ #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 /* * Methods which are required */ /* * Attach DFS to the given interface */ int ath_dfs_attach(struct ath_softc *sc) { return (1); } /* * Detach DFS from the given interface */ int ath_dfs_detach(struct ath_softc *sc) { return (1); } /* * Enable radar check. Return 1 if the driver should * enable radar PHY errors, or 0 if not. */ int ath_dfs_radar_enable(struct ath_softc *sc, struct ieee80211_channel *chan) { -#if 0 +#if 1 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 (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 +#if 1 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, struct mbuf *m, uint64_t tsf, struct ath_rx_status *rxstat) { } /* * 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 * the task/callback running this routine needs to be called. */ int ath_dfs_process_radar_event(struct ath_softc *sc, struct ieee80211_channel *chan) { return (0); } /* * Determine whether the DFS check task needs to be queued. * * This is called in the RX task when the current batch of packets * have been received. It will return whether there are any radar * events for ath_dfs_process_radar_event() to handle. */ int ath_dfs_tasklet_needed(struct ath_softc *sc, 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_ioctl_phyerr(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_PHYERR_PARAM peout; HAL_PHYERR_PARAM *pe; 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 DFS_SET_THRESH: if (insize < sizeof(HAL_PHYERR_PARAM)) { error = EINVAL; break; } pe = (HAL_PHYERR_PARAM *) indata; ath_hal_enabledfs(sc->sc_ah, pe); break; case DFS_GET_THRESH: memset(&peout, 0, sizeof(peout)); outsize = sizeof(HAL_PHYERR_PARAM); ath_hal_getdfsthresh(sc->sc_ah, &peout); pe = (HAL_PHYERR_PARAM *) outdata; memcpy(pe, &peout, sizeof(*pe)); 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); } /* * Get the current DFS thresholds from the HAL */ int ath_dfs_get_thresholds(struct ath_softc *sc, HAL_PHYERR_PARAM *param) { ath_hal_getdfsthresh(sc->sc_ah, param); return (1); } Index: head/sys/dev/ath/ath_hal/ah.c =================================================================== --- head/sys/dev/ath/ath_hal/ah.c (revision 318855) +++ head/sys/dev/ath/ath_hal/ah.c (revision 318856) @@ -1,1515 +1,1590 @@ /* * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * Copyright (c) 2002-2008 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$ */ #include "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ah_devid.h" #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); +TAILQ_HEAD(, ath_hal_chip) ah_chip_list = TAILQ_HEAD_INITIALIZER(ah_chip_list); +int +ath_hal_add_chip(struct ath_hal_chip *ahc) +{ + + TAILQ_INSERT_TAIL(&ah_chip_list, ahc, node); + return (0); +} + +int +ath_hal_remove_chip(struct ath_hal_chip *ahc) +{ + + TAILQ_REMOVE(&ah_chip_list, ahc, node); + return (0); +} + /* * Check the set of registered chips to see if any recognize * the device as one they can support. */ const char* ath_hal_probe(uint16_t vendorid, uint16_t devid) { struct ath_hal_chip * const *pchip; + struct ath_hal_chip *pc; + /* Linker set */ OS_SET_FOREACH(pchip, ah_chips) { const char *name = (*pchip)->probe(vendorid, devid); if (name != AH_NULL) return name; } + + /* List */ + TAILQ_FOREACH(pc, &ah_chip_list, node) { + const char *name = pc->probe(vendorid, devid); + if (name != AH_NULL) + return name; + } + return AH_NULL; } /* * Attach detects device chip revisions, initializes the hwLayer * function list, reads EEPROM information, * selects reset vectors, and performs a short self test. * Any failures will return an error that should cause a hardware * disable. */ struct ath_hal* ath_hal_attach(uint16_t devid, HAL_SOFTC sc, 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; + struct ath_hal_chip *pc; OS_SET_FOREACH(pchip, ah_chips) { struct ath_hal_chip *chip = *pchip; struct ath_hal *ah; /* 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, ah_config, error); if (ah != AH_NULL) { /* copy back private state to public area */ ah->ah_devid = AH_PRIVATE(ah)->ah_devid; ah->ah_subvendorid = AH_PRIVATE(ah)->ah_subvendorid; ah->ah_macVersion = AH_PRIVATE(ah)->ah_macVersion; ah->ah_macRev = AH_PRIVATE(ah)->ah_macRev; ah->ah_phyRev = AH_PRIVATE(ah)->ah_phyRev; ah->ah_analog5GhzRev = AH_PRIVATE(ah)->ah_analog5GhzRev; ah->ah_analog2GhzRev = AH_PRIVATE(ah)->ah_analog2GhzRev; return ah; } } + + /* List */ + TAILQ_FOREACH(pc, &ah_chip_list, node) { + struct ath_hal_chip *chip = pc; + struct ath_hal *ah; + + /* 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, ah_config, + error); + if (ah != AH_NULL) { + /* copy back private state to public area */ + ah->ah_devid = AH_PRIVATE(ah)->ah_devid; + ah->ah_subvendorid = AH_PRIVATE(ah)->ah_subvendorid; + ah->ah_macVersion = AH_PRIVATE(ah)->ah_macVersion; + ah->ah_macRev = AH_PRIVATE(ah)->ah_macRev; + ah->ah_phyRev = AH_PRIVATE(ah)->ah_phyRev; + ah->ah_analog5GhzRev = AH_PRIVATE(ah)->ah_analog5GhzRev; + ah->ah_analog2GhzRev = AH_PRIVATE(ah)->ah_analog2GhzRev; + return ah; + } + } + return AH_NULL; } const char * ath_hal_mac_name(struct ath_hal *ah) { switch (ah->ah_macVersion) { case AR_SREV_VERSION_CRETE: case AR_SREV_VERSION_MAUI_1: return "AR5210"; case AR_SREV_VERSION_MAUI_2: case AR_SREV_VERSION_OAHU: return "AR5211"; case AR_SREV_VERSION_VENICE: return "AR5212"; case AR_SREV_VERSION_GRIFFIN: return "AR2413"; case AR_SREV_VERSION_CONDOR: return "AR5424"; case AR_SREV_VERSION_EAGLE: return "AR5413"; case AR_SREV_VERSION_COBRA: return "AR2415"; case AR_SREV_2425: /* Swan */ return "AR2425"; case AR_SREV_2417: /* Nala */ return "AR2417"; case AR_XSREV_VERSION_OWL_PCI: return "AR5416"; case AR_XSREV_VERSION_OWL_PCIE: return "AR5418"; case AR_XSREV_VERSION_HOWL: return "AR9130"; case AR_XSREV_VERSION_SOWL: return "AR9160"; case AR_XSREV_VERSION_MERLIN: if (AH_PRIVATE(ah)->ah_ispcie) return "AR9280"; return "AR9220"; case AR_XSREV_VERSION_KITE: return "AR9285"; case AR_XSREV_VERSION_KIWI: 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 "????"; } /* * Return the mask of available modes based on the hardware capabilities. */ u_int ath_hal_getwirelessmodes(struct ath_hal*ah) { return ath_hal_getWirelessModes(ah); } /* linker set of registered RF backends */ OS_SET_DECLARE(ah_rfs, struct ath_hal_rf); +TAILQ_HEAD(, ath_hal_rf) ah_rf_list = TAILQ_HEAD_INITIALIZER(ah_rf_list); +int +ath_hal_add_rf(struct ath_hal_rf *arf) +{ + + TAILQ_INSERT_TAIL(&ah_rf_list, arf, node); + return (0); +} + +int +ath_hal_remove_rf(struct ath_hal_rf *arf) +{ + + TAILQ_REMOVE(&ah_rf_list, arf, node); + return (0); +} + /* * Check the set of registered RF backends to see if * any recognize the device as one they can support. */ struct ath_hal_rf * ath_hal_rfprobe(struct ath_hal *ah, HAL_STATUS *ecode) { struct ath_hal_rf * const *prf; + struct ath_hal_rf * rf; OS_SET_FOREACH(prf, ah_rfs) { struct ath_hal_rf *rf = *prf; + if (rf->probe(ah)) + return rf; + } + + TAILQ_FOREACH(rf, &ah_rf_list, node) { if (rf->probe(ah)) return rf; } *ecode = HAL_ENOTSUPP; return AH_NULL; } const char * ath_hal_rf_name(struct ath_hal *ah) { switch (ah->ah_analog5GhzRev & AR_RADIO_SREV_MAJOR) { case 0: /* 5210 */ return "5110"; /* NB: made up */ case AR_RAD5111_SREV_MAJOR: case AR_RAD5111_SREV_PROD: return "5111"; case AR_RAD2111_SREV_MAJOR: return "2111"; case AR_RAD5112_SREV_MAJOR: case AR_RAD5112_SREV_2_0: case AR_RAD5112_SREV_2_1: return "5112"; case AR_RAD2112_SREV_MAJOR: case AR_RAD2112_SREV_2_0: case AR_RAD2112_SREV_2_1: return "2112"; case AR_RAD2413_SREV_MAJOR: return "2413"; case AR_RAD5413_SREV_MAJOR: return "5413"; case AR_RAD2316_SREV_MAJOR: return "2316"; case AR_RAD2317_SREV_MAJOR: return "2317"; case AR_RAD5424_SREV_MAJOR: return "5424"; case AR_RAD5133_SREV_MAJOR: return "5133"; case AR_RAD2133_SREV_MAJOR: return "2133"; case AR_RAD5122_SREV_MAJOR: return "5122"; case AR_RAD2122_SREV_MAJOR: return "2122"; } return "????"; } /* * Poll the register looking for a specific value. */ HAL_BOOL ath_hal_wait(struct ath_hal *ah, u_int reg, uint32_t mask, uint32_t val) { #define AH_TIMEOUT 1000 return ath_hal_waitfor(ah, reg, mask, val, AH_TIMEOUT); #undef AH_TIMEOUT } HAL_BOOL ath_hal_waitfor(struct ath_hal *ah, u_int reg, uint32_t mask, uint32_t val, uint32_t timeout) { int i; for (i = 0; i < timeout; i++) { if ((OS_REG_READ(ah, reg) & mask) == val) return AH_TRUE; OS_DELAY(10); } HALDEBUG(ah, HAL_DEBUG_REGIO | HAL_DEBUG_PHYIO, "%s: timeout on reg 0x%x: 0x%08x & 0x%08x != 0x%08x\n", __func__, reg, OS_REG_READ(ah, reg), mask, val); return AH_FALSE; } /* * Reverse the bits starting at the low bit for a value of * bit_count in size */ uint32_t ath_hal_reverseBits(uint32_t val, uint32_t n) { uint32_t retval; int i; for (i = 0, retval = 0; i < n; i++) { retval = (retval << 1) | (val & 1); val >>= 1; } return retval; } /* 802.11n related timing definitions */ #define OFDM_PLCP_BITS 22 #define HT_L_STF 8 #define HT_L_LTF 8 #define HT_L_SIG 4 #define HT_SIG 8 #define HT_STF 4 #define HT_LTF(n) ((n) * 4) #define HT_RC_2_MCS(_rc) ((_rc) & 0x1f) #define HT_RC_2_STREAMS(_rc) ((((_rc) & 0x78) >> 3) + 1) #define IS_HT_RATE(_rc) ( (_rc) & IEEE80211_RATE_MCS) /* * Calculate the duration of a packet whether it is 11n or legacy. */ uint32_t ath_hal_pkt_txtime(struct ath_hal *ah, const HAL_RATE_TABLE *rates, uint32_t frameLen, uint16_t rateix, HAL_BOOL isht40, HAL_BOOL shortPreamble, HAL_BOOL includeSifs) { uint8_t rc; int numStreams; rc = rates->info[rateix].rateCode; /* Legacy rate? Return the old way */ if (! IS_HT_RATE(rc)) return ath_hal_computetxtime(ah, rates, frameLen, rateix, shortPreamble, includeSifs); /* 11n frame - extract out the number of spatial streams */ numStreams = HT_RC_2_STREAMS(rc); KASSERT(numStreams > 0 && numStreams <= 4, ("number of spatial streams needs to be 1..3: MCS rate 0x%x!", rateix)); /* XXX TODO: Add SIFS */ 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. */ uint32_t ath_computedur_ht(uint32_t frameLen, uint16_t rate, int streams, HAL_BOOL isht40, HAL_BOOL isShortGI) { uint32_t bitsPerSymbol, numBits, numSymbols, txTime; KASSERT(rate & IEEE80211_RATE_MCS, ("not mcs %d", rate)); KASSERT((rate &~ IEEE80211_RATE_MCS) < 31, ("bad mcs 0x%x", rate)); if (isht40) bitsPerSymbol = ht40_bps[HT_RC_2_MCS(rate)]; else bitsPerSymbol = ht20_bps[HT_RC_2_MCS(rate)]; numBits = OFDM_PLCP_BITS + (frameLen << 3); numSymbols = howmany(numBits, bitsPerSymbol); if (isShortGI) txTime = ((numSymbols * 18) + 4) / 5; /* 3.6us */ else txTime = numSymbols * 4; /* 4us */ return txTime + HT_L_STF + HT_L_LTF + HT_L_SIG + HT_SIG + HT_STF + HT_LTF(streams); } /* * Compute the time to transmit a frame of length frameLen bytes * using the specified rate, phy, and short preamble setting. */ uint16_t ath_hal_computetxtime(struct ath_hal *ah, const HAL_RATE_TABLE *rates, uint32_t frameLen, uint16_t rateix, HAL_BOOL shortPreamble, HAL_BOOL includeSifs) { uint32_t bitsPerSymbol, numBits, numSymbols, phyTime, txTime; uint32_t kbps; /* Warn if this function is called for 11n rates; it should not be! */ if (IS_HT_RATE(rates->info[rateix].rateCode)) ath_hal_printf(ah, "%s: MCS rate? (index %d; hwrate 0x%x)\n", __func__, rateix, rates->info[rateix].rateCode); kbps = rates->info[rateix].rateKbps; /* * index can be invalid during dynamic Turbo transitions. * XXX */ if (kbps == 0) return 0; switch (rates->info[rateix].phy) { case IEEE80211_T_CCK: phyTime = CCK_PREAMBLE_BITS + CCK_PLCP_BITS; if (shortPreamble && rates->info[rateix].shortPreamble) phyTime >>= 1; numBits = frameLen << 3; txTime = phyTime + ((numBits * 1000)/kbps); if (includeSifs) txTime += CCK_SIFS_TIME; break; case IEEE80211_T_OFDM: bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME) / 1000; HALASSERT(bitsPerSymbol != 0); numBits = OFDM_PLCP_BITS + (frameLen << 3); numSymbols = howmany(numBits, bitsPerSymbol); txTime = OFDM_PREAMBLE_TIME + (numSymbols * OFDM_SYMBOL_TIME); if (includeSifs) txTime += OFDM_SIFS_TIME; break; case IEEE80211_T_OFDM_HALF: bitsPerSymbol = (kbps * OFDM_HALF_SYMBOL_TIME) / 1000; HALASSERT(bitsPerSymbol != 0); numBits = OFDM_HALF_PLCP_BITS + (frameLen << 3); numSymbols = howmany(numBits, bitsPerSymbol); txTime = OFDM_HALF_PREAMBLE_TIME + (numSymbols * OFDM_HALF_SYMBOL_TIME); if (includeSifs) txTime += OFDM_HALF_SIFS_TIME; break; case IEEE80211_T_OFDM_QUARTER: bitsPerSymbol = (kbps * OFDM_QUARTER_SYMBOL_TIME) / 1000; HALASSERT(bitsPerSymbol != 0); numBits = OFDM_QUARTER_PLCP_BITS + (frameLen << 3); numSymbols = howmany(numBits, bitsPerSymbol); txTime = OFDM_QUARTER_PREAMBLE_TIME + (numSymbols * OFDM_QUARTER_SYMBOL_TIME); if (includeSifs) txTime += OFDM_QUARTER_SIFS_TIME; break; case IEEE80211_T_TURBO: bitsPerSymbol = (kbps * TURBO_SYMBOL_TIME) / 1000; HALASSERT(bitsPerSymbol != 0); numBits = TURBO_PLCP_BITS + (frameLen << 3); numSymbols = howmany(numBits, bitsPerSymbol); txTime = TURBO_PREAMBLE_TIME + (numSymbols * TURBO_SYMBOL_TIME); if (includeSifs) txTime += TURBO_SIFS_TIME; break; default: HALDEBUG(ah, HAL_DEBUG_PHYIO, "%s: unknown phy %u (rate ix %u)\n", __func__, rates->info[rateix].phy, rateix); txTime = 0; break; } 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, WIRELESS_MODE_11b = 2, WIRELESS_MODE_11g = 3, WIRELESS_MODE_108g = 4, WIRELESS_MODE_MAX } WIRELESS_MODE; /* * XXX TODO: for some (?) chips, an 11b mode still runs at 11bg. * Maybe AR5211 has separate 11b and 11g only modes, so 11b is 22MHz * and 11g is 44MHz, but AR5416 and later run 11b in 11bg mode, right? */ static WIRELESS_MODE ath_hal_chan2wmode(struct ath_hal *ah, const struct ieee80211_channel *chan) { if (IEEE80211_IS_CHAN_B(chan)) return WIRELESS_MODE_11b; if (IEEE80211_IS_CHAN_G(chan)) return WIRELESS_MODE_11g; if (IEEE80211_IS_CHAN_108G(chan)) return WIRELESS_MODE_108g; if (IEEE80211_IS_CHAN_TURBO(chan)) return WIRELESS_MODE_TURBO; return WIRELESS_MODE_11a; } /* * Convert between microseconds and core system clocks. */ /* 11a Turbo 11b 11g 108g */ static const uint8_t CLOCK_RATE[] = { 40, 80, 22, 44, 88 }; #define CLOCK_FAST_RATE_5GHZ_OFDM 44 u_int ath_hal_mac_clks(struct ath_hal *ah, u_int usecs) { const struct ieee80211_channel *c = AH_PRIVATE(ah)->ah_curchan; u_int clks; /* NB: ah_curchan may be null when called attach time */ /* XXX merlin and later specific workaround - 5ghz fast clock is 44 */ if (c != AH_NULL && IS_5GHZ_FAST_CLOCK_EN(ah, c)) { clks = usecs * CLOCK_FAST_RATE_5GHZ_OFDM; if (IEEE80211_IS_CHAN_HT40(c)) clks <<= 1; } else if (c != AH_NULL) { clks = usecs * CLOCK_RATE[ath_hal_chan2wmode(ah, c)]; if (IEEE80211_IS_CHAN_HT40(c)) 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; } u_int ath_hal_mac_usec(struct ath_hal *ah, u_int clks) { uint64_t psec; psec = ath_hal_mac_psec(ah, clks); return (psec / 1000000); } /* * XXX TODO: half, quarter rates. */ uint64_t ath_hal_mac_psec(struct ath_hal *ah, u_int clks) { const struct ieee80211_channel *c = AH_PRIVATE(ah)->ah_curchan; uint64_t psec; /* NB: ah_curchan may be null when called attach time */ /* XXX merlin and later specific workaround - 5ghz fast clock is 44 */ if (c != AH_NULL && IS_5GHZ_FAST_CLOCK_EN(ah, c)) { psec = (clks * 1000000ULL) / CLOCK_FAST_RATE_5GHZ_OFDM; if (IEEE80211_IS_CHAN_HT40(c)) psec >>= 1; } else if (c != AH_NULL) { psec = (clks * 1000000ULL) / CLOCK_RATE[ath_hal_chan2wmode(ah, c)]; if (IEEE80211_IS_CHAN_HT40(c)) psec >>= 1; } else psec = (clks * 1000000ULL) / CLOCK_RATE[WIRELESS_MODE_11b]; return psec; } /* * Setup a h/w rate table's reverse lookup table and * fill in ack durations. This routine is called for * each rate table returned through the ah_getRateTable * method. The reverse lookup tables are assumed to be * initialized to zero (or at least the first entry). * We use this as a key that indicates whether or not * we've previously setup the reverse lookup table. * * XXX not reentrant, but shouldn't matter */ void ath_hal_setupratetable(struct ath_hal *ah, HAL_RATE_TABLE *rt) { #define N(a) (sizeof(a)/sizeof(a[0])) int i; if (rt->rateCodeToIndex[0] != 0) /* already setup */ return; for (i = 0; i < N(rt->rateCodeToIndex); i++) rt->rateCodeToIndex[i] = (uint8_t) -1; for (i = 0; i < rt->rateCount; i++) { uint8_t code = rt->info[i].rateCode; uint8_t cix = rt->info[i].controlRate; HALASSERT(code < N(rt->rateCodeToIndex)); rt->rateCodeToIndex[code] = i; HALASSERT((code | rt->info[i].shortPreamble) < N(rt->rateCodeToIndex)); rt->rateCodeToIndex[code | rt->info[i].shortPreamble] = i; /* * XXX for 11g the control rate to use for 5.5 and 11 Mb/s * depends on whether they are marked as basic rates; * the static tables are setup with an 11b-compatible * 2Mb/s rate which will work but is suboptimal */ rt->info[i].lpAckDuration = ath_hal_computetxtime(ah, rt, WLAN_CTRL_FRAME_SIZE, cix, AH_FALSE, AH_TRUE); rt->info[i].spAckDuration = ath_hal_computetxtime(ah, rt, WLAN_CTRL_FRAME_SIZE, cix, AH_TRUE, AH_TRUE); } #undef N } HAL_STATUS ath_hal_getcapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type, uint32_t capability, uint32_t *result) { const HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps; switch (type) { case HAL_CAP_REG_DMN: /* regulatory domain */ *result = AH_PRIVATE(ah)->ah_currentRD; return HAL_OK; case HAL_CAP_DFS_DMN: /* DFS Domain */ *result = AH_PRIVATE(ah)->ah_dfsDomain; return HAL_OK; case HAL_CAP_CIPHER: /* cipher handled in hardware */ case HAL_CAP_TKIP_MIC: /* handle TKIP MIC in hardware */ return HAL_ENOTSUPP; case HAL_CAP_TKIP_SPLIT: /* hardware TKIP uses split keys */ return HAL_ENOTSUPP; case HAL_CAP_PHYCOUNTERS: /* hardware PHY error counters */ return pCap->halHwPhyCounterSupport ? HAL_OK : HAL_ENXIO; case HAL_CAP_WME_TKIPMIC: /* hardware can do TKIP MIC when WMM is turned on */ return HAL_ENOTSUPP; case HAL_CAP_DIVERSITY: /* hardware supports fast diversity */ return HAL_ENOTSUPP; case HAL_CAP_KEYCACHE_SIZE: /* hardware key cache size */ *result = pCap->halKeyCacheSize; return HAL_OK; case HAL_CAP_NUM_TXQUEUES: /* number of hardware tx queues */ *result = pCap->halTotalQueues; return HAL_OK; case HAL_CAP_VEOL: /* hardware supports virtual EOL */ return pCap->halVEOLSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_PSPOLL: /* hardware PS-Poll support works */ return pCap->halPSPollBroken ? HAL_ENOTSUPP : HAL_OK; case HAL_CAP_COMPRESSION: return pCap->halCompressSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_BURST: return pCap->halBurstSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_FASTFRAME: return pCap->halFastFramesSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_DIAG: /* hardware diagnostic support */ *result = AH_PRIVATE(ah)->ah_diagreg; return HAL_OK; case HAL_CAP_TXPOW: /* global tx power limit */ switch (capability) { case 0: /* facility is supported */ return HAL_OK; case 1: /* current limit */ *result = AH_PRIVATE(ah)->ah_powerLimit; return HAL_OK; case 2: /* current max tx power */ *result = AH_PRIVATE(ah)->ah_maxPowerLevel; return HAL_OK; case 3: /* scale factor */ *result = AH_PRIVATE(ah)->ah_tpScale; return HAL_OK; } return HAL_ENOTSUPP; case HAL_CAP_BSSIDMASK: /* hardware supports bssid mask */ return pCap->halBssIdMaskSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_MCAST_KEYSRCH: /* multicast frame keycache search */ return pCap->halMcastKeySrchSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_TSF_ADJUST: /* hardware has beacon tsf adjust */ return HAL_ENOTSUPP; case HAL_CAP_RFSILENT: /* rfsilent support */ switch (capability) { case 0: /* facility is supported */ return pCap->halRfSilentSupport ? HAL_OK : HAL_ENOTSUPP; case 1: /* current setting */ return AH_PRIVATE(ah)->ah_rfkillEnabled ? HAL_OK : HAL_ENOTSUPP; case 2: /* rfsilent config */ *result = AH_PRIVATE(ah)->ah_rfsilent; return HAL_OK; } return HAL_ENOTSUPP; case HAL_CAP_11D: return HAL_OK; case HAL_CAP_HT: return pCap->halHTSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_GTXTO: return pCap->halGTTSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_FAST_CC: return pCap->halFastCCSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_TX_CHAINMASK: /* mask of TX chains supported */ *result = pCap->halTxChainMask; return HAL_OK; case HAL_CAP_RX_CHAINMASK: /* mask of RX chains supported */ *result = pCap->halRxChainMask; return HAL_OK; case HAL_CAP_NUM_GPIO_PINS: *result = pCap->halNumGpioPins; return HAL_OK; case HAL_CAP_CST: return pCap->halCSTSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_RTS_AGGR_LIMIT: *result = pCap->halRtsAggrLimit; return HAL_OK; case HAL_CAP_4ADDR_AGGR: 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: return pCap->halAutoSleepSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_MBSSID_AGGR_SUPPORT: return pCap->halMbssidAggrSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_SPLIT_4KB_TRANS: /* hardware handles descriptors straddling 4k page boundary */ return pCap->hal4kbSplitTransSupport ? HAL_OK : HAL_ENOTSUPP; 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->halRxTstampPrecision; 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; /* FreeBSD-specific entries for now */ case HAL_CAP_RXORN_FATAL: /* HAL_INT_RXORN treated as fatal */ return AH_PRIVATE(ah)->ah_rxornIsFatal ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_INTRMASK: /* mask of supported interrupts */ *result = pCap->halIntrMask; return HAL_OK; case HAL_CAP_BSSIDMATCH: /* hardware has disable bssid match */ return pCap->halBssidMatchSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_STREAMS: /* number of 11n spatial streams */ switch (capability) { case 0: /* TX */ *result = pCap->halTxStreams; return HAL_OK; case 1: /* RX */ *result = pCap->halRxStreams; return HAL_OK; default: return HAL_ENOTSUPP; } case HAL_CAP_RXDESC_SELFLINK: /* hardware supports self-linked final RX descriptors correctly */ return pCap->halHasRxSelfLinkedTail ? 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; case HAL_CAP_TXTSTAMP_PREC: /* tx desc tstamp precision (bits) */ *result = pCap->halTxTstampPrecision; return HAL_OK; default: return HAL_EINVAL; } } HAL_BOOL ath_hal_setcapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type, uint32_t capability, uint32_t setting, HAL_STATUS *status) { switch (type) { case HAL_CAP_TXPOW: switch (capability) { case 3: if (setting <= HAL_TP_SCALE_MIN) { AH_PRIVATE(ah)->ah_tpScale = setting; return AH_TRUE; } break; } break; case HAL_CAP_RFSILENT: /* rfsilent support */ /* * NB: allow even if halRfSilentSupport is false * in case the EEPROM is misprogrammed. */ switch (capability) { case 1: /* current setting */ AH_PRIVATE(ah)->ah_rfkillEnabled = (setting != 0); return AH_TRUE; case 2: /* rfsilent config */ /* XXX better done per-chip for validation? */ AH_PRIVATE(ah)->ah_rfsilent = setting; return AH_TRUE; } break; case HAL_CAP_REG_DMN: /* regulatory domain */ AH_PRIVATE(ah)->ah_currentRD = setting; return AH_TRUE; case HAL_CAP_RXORN_FATAL: /* HAL_INT_RXORN treated as fatal */ AH_PRIVATE(ah)->ah_rxornIsFatal = setting; return AH_TRUE; default: break; } if (status) *status = HAL_EINVAL; return AH_FALSE; } /* * Common support for getDiagState method. */ static u_int ath_hal_getregdump(struct ath_hal *ah, const HAL_REGRANGE *regs, void *dstbuf, int space) { uint32_t *dp = dstbuf; int i; for (i = 0; space >= 2*sizeof(uint32_t); i++) { 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); space -= sizeof(uint32_t); } while (r <= e && space >= sizeof(uint32_t)); } return (char *) dp - (char *) dstbuf; } static void ath_hal_setregs(struct ath_hal *ah, const HAL_REGWRITE *regs, int space) { while (space >= sizeof(HAL_REGWRITE)) { OS_REG_WRITE(ah, regs->addr, regs->value); regs++, space -= sizeof(HAL_REGWRITE); } } HAL_BOOL 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; *resultsize = sizeof(HAL_REVS); return AH_TRUE; case HAL_DIAG_REGS: *resultsize = ath_hal_getregdump(ah, args, *result,*resultsize); return AH_TRUE; case HAL_DIAG_SETREGS: ath_hal_setregs(ah, args, argsize); *resultsize = 0; return AH_TRUE; case HAL_DIAG_FATALERR: *result = &AH_PRIVATE(ah)->ah_fatalState[0]; *resultsize = sizeof(AH_PRIVATE(ah)->ah_fatalState); return AH_TRUE; case HAL_DIAG_EEREAD: if (argsize != sizeof(uint16_t)) return AH_FALSE; if (!ath_hal_eepromRead(ah, *(const uint16_t *)args, *result)) return AH_FALSE; *resultsize = sizeof(uint16_t); return AH_TRUE; #ifdef AH_PRIVATE_DIAG case HAL_DIAG_SETKEY: { const HAL_DIAG_KEYVAL *dk; if (argsize != sizeof(HAL_DIAG_KEYVAL)) return AH_FALSE; dk = (const HAL_DIAG_KEYVAL *)args; return ah->ah_setKeyCacheEntry(ah, dk->dk_keyix, &dk->dk_keyval, dk->dk_mac, dk->dk_xor); } case HAL_DIAG_RESETKEY: if (argsize != sizeof(uint16_t)) return AH_FALSE; return ah->ah_resetKeyCacheEntry(ah, *(const uint16_t *)args); #ifdef AH_SUPPORT_WRITE_EEPROM case HAL_DIAG_EEWRITE: { const HAL_DIAG_EEVAL *ee; if (argsize != sizeof(HAL_DIAG_EEVAL)) return AH_FALSE; ee = (const HAL_DIAG_EEVAL *)args; return ath_hal_eepromWrite(ah, ee->ee_off, ee->ee_data); } #endif /* AH_SUPPORT_WRITE_EEPROM */ #endif /* AH_PRIVATE_DIAG */ case HAL_DIAG_11NCOMPAT: if (argsize == 0) { *resultsize = sizeof(uint32_t); *((uint32_t *)(*result)) = AH_PRIVATE(ah)->ah_11nCompat; } else if (argsize == sizeof(uint32_t)) { AH_PRIVATE(ah)->ah_11nCompat = *(const uint32_t *)args; } 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; } /* * Set the properties of the tx queue with the parameters * from qInfo. */ HAL_BOOL ath_hal_setTxQProps(struct ath_hal *ah, HAL_TX_QUEUE_INFO *qi, const HAL_TXQ_INFO *qInfo) { uint32_t cw; if (qi->tqi_type == HAL_TX_QUEUE_INACTIVE) { HALDEBUG(ah, HAL_DEBUG_TXQUEUE, "%s: inactive queue\n", __func__); return AH_FALSE; } /* XXX validate parameters */ qi->tqi_ver = qInfo->tqi_ver; qi->tqi_subtype = qInfo->tqi_subtype; qi->tqi_qflags = qInfo->tqi_qflags; qi->tqi_priority = qInfo->tqi_priority; if (qInfo->tqi_aifs != HAL_TXQ_USEDEFAULT) qi->tqi_aifs = AH_MIN(qInfo->tqi_aifs, 255); else qi->tqi_aifs = INIT_AIFS; if (qInfo->tqi_cwmin != HAL_TXQ_USEDEFAULT) { cw = AH_MIN(qInfo->tqi_cwmin, 1024); /* make sure that the CWmin is of the form (2^n - 1) */ qi->tqi_cwmin = 1; while (qi->tqi_cwmin < cw) qi->tqi_cwmin = (qi->tqi_cwmin << 1) | 1; } else qi->tqi_cwmin = qInfo->tqi_cwmin; if (qInfo->tqi_cwmax != HAL_TXQ_USEDEFAULT) { cw = AH_MIN(qInfo->tqi_cwmax, 1024); /* make sure that the CWmax is of the form (2^n - 1) */ qi->tqi_cwmax = 1; while (qi->tqi_cwmax < cw) qi->tqi_cwmax = (qi->tqi_cwmax << 1) | 1; } else qi->tqi_cwmax = INIT_CWMAX; /* Set retry limit values */ if (qInfo->tqi_shretry != 0) qi->tqi_shretry = AH_MIN(qInfo->tqi_shretry, 15); else qi->tqi_shretry = INIT_SH_RETRY; if (qInfo->tqi_lgretry != 0) qi->tqi_lgretry = AH_MIN(qInfo->tqi_lgretry, 15); else qi->tqi_lgretry = INIT_LG_RETRY; qi->tqi_cbrPeriod = qInfo->tqi_cbrPeriod; qi->tqi_cbrOverflowLimit = qInfo->tqi_cbrOverflowLimit; qi->tqi_burstTime = qInfo->tqi_burstTime; qi->tqi_readyTime = qInfo->tqi_readyTime; switch (qInfo->tqi_subtype) { case HAL_WME_UPSD: if (qi->tqi_type == HAL_TX_QUEUE_DATA) qi->tqi_intFlags = HAL_TXQ_USE_LOCKOUT_BKOFF_DIS; break; default: break; /* NB: silence compiler */ } return AH_TRUE; } HAL_BOOL ath_hal_getTxQProps(struct ath_hal *ah, HAL_TXQ_INFO *qInfo, const HAL_TX_QUEUE_INFO *qi) { if (qi->tqi_type == HAL_TX_QUEUE_INACTIVE) { HALDEBUG(ah, HAL_DEBUG_TXQUEUE, "%s: inactive queue\n", __func__); return AH_FALSE; } qInfo->tqi_qflags = qi->tqi_qflags; qInfo->tqi_ver = qi->tqi_ver; qInfo->tqi_subtype = qi->tqi_subtype; qInfo->tqi_qflags = qi->tqi_qflags; qInfo->tqi_priority = qi->tqi_priority; qInfo->tqi_aifs = qi->tqi_aifs; qInfo->tqi_cwmin = qi->tqi_cwmin; qInfo->tqi_cwmax = qi->tqi_cwmax; qInfo->tqi_shretry = qi->tqi_shretry; qInfo->tqi_lgretry = qi->tqi_lgretry; qInfo->tqi_cbrPeriod = qi->tqi_cbrPeriod; qInfo->tqi_cbrOverflowLimit = qi->tqi_cbrOverflowLimit; qInfo->tqi_burstTime = qi->tqi_burstTime; qInfo->tqi_readyTime = qi->tqi_readyTime; return AH_TRUE; } /* 11a Turbo 11b 11g 108g */ static const int16_t NOISE_FLOOR[] = { -96, -93, -98, -96, -93 }; /* * Read the current channel noise floor and return. * If nf cal hasn't finished, channel noise floor should be 0 * and we return a nominal value based on band and frequency. * * NB: This is a private routine used by per-chip code to * implement the ah_getChanNoise method. */ int16_t ath_hal_getChanNoise(struct ath_hal *ah, const struct ieee80211_channel *chan) { HAL_CHANNEL_INTERNAL *ichan; ichan = ath_hal_checkchannel(ah, chan); if (ichan == AH_NULL) { HALDEBUG(ah, HAL_DEBUG_NFCAL, "%s: invalid channel %u/0x%x; no mapping\n", __func__, chan->ic_freq, chan->ic_flags); return 0; } if (ichan->rawNoiseFloor == 0) { WIRELESS_MODE mode = ath_hal_chan2wmode(ah, chan); HALASSERT(mode < WIRELESS_MODE_MAX); return NOISE_FLOOR[mode] + ath_hal_getNfAdjust(ah, ichan); } else return ichan->rawNoiseFloor + ichan->noiseFloorAdjust; } /* * Fetch the current setup of ctl/ext noise floor values. * * If the CHANNEL_MIMO_NF_VALID flag isn't set, the array is simply * populated with values from NOISE_FLOOR[] + ath_hal_getNfAdjust(). * * The caller must supply ctl/ext NF arrays which are at least * AH_MAX_CHAINS entries long. */ int ath_hal_get_mimo_chan_noise(struct ath_hal *ah, const struct ieee80211_channel *chan, int16_t *nf_ctl, int16_t *nf_ext) { #ifdef AH_SUPPORT_AR5416 HAL_CHANNEL_INTERNAL *ichan; int i; ichan = ath_hal_checkchannel(ah, chan); if (ichan == AH_NULL) { 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_MAX_CHAINS; i++) { nf_ctl[i] = nf_ext[i] = 0; } return 0; } /* Return 0 if there's no valid MIMO values (yet) */ if (! (ichan->privFlags & CHANNEL_MIMO_NF_VALID)) { for (i = 0; i < AH_MAX_CHAINS; i++) { nf_ctl[i] = nf_ext[i] = 0; } return 0; } if (ichan->rawNoiseFloor == 0) { WIRELESS_MODE mode = ath_hal_chan2wmode(ah, chan); HALASSERT(mode < WIRELESS_MODE_MAX); /* * See the comment below - this could cause issues for * stations which have a very low RSSI, below the * 'normalised' NF values in NOISE_FLOOR[]. */ for (i = 0; i < AH_MAX_CHAINS; i++) { nf_ctl[i] = nf_ext[i] = NOISE_FLOOR[mode] + ath_hal_getNfAdjust(ah, ichan); } return 1; } else { /* * The value returned here from a MIMO radio is presumed to be * "good enough" as a NF calculation. As RSSI values are calculated * against this, an adjusted NF may be higher than the RSSI value * returned from a vary weak station, resulting in an obscenely * high signal strength calculation being returned. * * This should be re-evaluated at a later date, along with any * signal strength calculations which are made. Quite likely the * RSSI values will need to be adjusted to ensure the calculations * don't "wrap" when RSSI is less than the "adjusted" NF value. * ("Adjust" here is via ichan->noiseFloorAdjust.) */ 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); } return 1; } #else return 0; #endif /* AH_SUPPORT_AR5416 */ } /* * Process all valid raw noise floors into the dBm noise floor values. * Though our device has no reference for a dBm noise floor, we perform * a relative minimization of NF's based on the lowest NF found across a * channel scan. */ void ath_hal_process_noisefloor(struct ath_hal *ah) { HAL_CHANNEL_INTERNAL *c; int16_t correct2, correct5; int16_t lowest2, lowest5; int i; /* * Find the lowest 2GHz and 5GHz noise floor values after adjusting * for statistically recorded NF/channel deviation. */ correct2 = lowest2 = 0; correct5 = lowest5 = 0; for (i = 0; i < AH_PRIVATE(ah)->ah_nchan; i++) { WIRELESS_MODE mode; int16_t nf; c = &AH_PRIVATE(ah)->ah_channels[i]; if (c->rawNoiseFloor >= 0) continue; /* XXX can't identify proper mode */ mode = IS_CHAN_5GHZ(c) ? WIRELESS_MODE_11a : WIRELESS_MODE_11g; nf = c->rawNoiseFloor + NOISE_FLOOR[mode] + ath_hal_getNfAdjust(ah, c); if (IS_CHAN_5GHZ(c)) { if (nf < lowest5) { lowest5 = nf; correct5 = NOISE_FLOOR[mode] - (c->rawNoiseFloor + ath_hal_getNfAdjust(ah, c)); } } else { if (nf < lowest2) { lowest2 = nf; correct2 = NOISE_FLOOR[mode] - (c->rawNoiseFloor + ath_hal_getNfAdjust(ah, c)); } } } /* Correct the channels to reach the expected NF value */ for (i = 0; i < AH_PRIVATE(ah)->ah_nchan; i++) { c = &AH_PRIVATE(ah)->ah_channels[i]; if (c->rawNoiseFloor >= 0) continue; /* Apply correction factor */ c->noiseFloorAdjust = ath_hal_getNfAdjust(ah, c) + (IS_CHAN_5GHZ(c) ? correct5 : correct2); HALDEBUG(ah, HAL_DEBUG_NFCAL, "%u raw nf %d adjust %d\n", c->channel, c->rawNoiseFloor, c->noiseFloorAdjust); } } /* * INI support routines. */ int ath_hal_ini_write(struct ath_hal *ah, const HAL_INI_ARRAY *ia, int col, int regWr) { int r; HALASSERT(col < ia->cols); for (r = 0; r < ia->rows; r++) { OS_REG_WRITE(ah, HAL_INI_VAL(ia, r, 0), HAL_INI_VAL(ia, r, col)); /* Analog shift register delay seems needed for Merlin - PR kern/154220 */ if (HAL_INI_VAL(ia, r, 0) >= 0x7800 && HAL_INI_VAL(ia, r, 0) < 0x7900) OS_DELAY(100); DMA_YIELD(regWr); } return regWr; } void ath_hal_ini_bank_setup(uint32_t data[], const HAL_INI_ARRAY *ia, int col) { int r; HALASSERT(col < ia->cols); for (r = 0; r < ia->rows; r++) data[r] = HAL_INI_VAL(ia, r, col); } int ath_hal_ini_bank_write(struct ath_hal *ah, const HAL_INI_ARRAY *ia, const uint32_t data[], int regWr) { int r; for (r = 0; r < ia->rows; r++) { OS_REG_WRITE(ah, HAL_INI_VAL(ia, r, 0), data[r]); DMA_YIELD(regWr); } return regWr; } /* * These are EEPROM board related routines which should likely live in * a helper library of some sort. */ /************************************************************** * ath_ee_getLowerUppderIndex * * Return indices surrounding the value in sorted integer lists. * Requirement: the input list must be monotonically increasing * and populated up to the list size * Returns: match is set if an index in the array matches exactly * or a the target is before or after the range of the array. */ HAL_BOOL ath_ee_getLowerUpperIndex(uint8_t target, uint8_t *pList, uint16_t listSize, uint16_t *indexL, uint16_t *indexR) { uint16_t i; /* * Check first and last elements for beyond ordered array cases. */ if (target <= pList[0]) { *indexL = *indexR = 0; return AH_TRUE; } if (target >= pList[listSize-1]) { *indexL = *indexR = (uint16_t)(listSize - 1); return AH_TRUE; } /* look for value being near or between 2 values in list */ for (i = 0; i < listSize - 1; i++) { /* * If value is close to the current value of the list * then target is not between values, it is one of the values */ if (pList[i] == target) { *indexL = *indexR = i; return AH_TRUE; } /* * Look for value being between current value and next value * if so return these 2 values */ if (target < pList[i + 1]) { *indexL = i; *indexR = (uint16_t)(i + 1); return AH_FALSE; } } HALASSERT(0); *indexL = *indexR = 0; return AH_FALSE; } /************************************************************** * ath_ee_FillVpdTable * * Fill the Vpdlist for indices Pmax-Pmin * Note: pwrMin, pwrMax and Vpdlist are all in dBm * 4 */ HAL_BOOL ath_ee_FillVpdTable(uint8_t pwrMin, uint8_t pwrMax, uint8_t *pPwrList, uint8_t *pVpdList, uint16_t numIntercepts, uint8_t *pRetVpdList) { uint16_t i, k; uint8_t currPwr = pwrMin; uint16_t idxL, idxR; HALASSERT(pwrMax > pwrMin); for (i = 0; i <= (pwrMax - pwrMin) / 2; i++) { ath_ee_getLowerUpperIndex(currPwr, pPwrList, numIntercepts, &(idxL), &(idxR)); if (idxR < 1) idxR = 1; /* extrapolate below */ if (idxL == numIntercepts - 1) idxL = (uint16_t)(numIntercepts - 2); /* extrapolate above */ if (pPwrList[idxL] == pPwrList[idxR]) k = pVpdList[idxL]; else k = (uint16_t)( ((currPwr - pPwrList[idxL]) * pVpdList[idxR] + (pPwrList[idxR] - currPwr) * pVpdList[idxL]) / (pPwrList[idxR] - pPwrList[idxL]) ); HALASSERT(k < 256); pRetVpdList[i] = (uint8_t)k; currPwr += 2; /* half dB steps */ } return AH_TRUE; } /************************************************************************** * ath_ee_interpolate * * Returns signed interpolated or the scaled up interpolated value */ int16_t ath_ee_interpolate(uint16_t target, uint16_t srcLeft, uint16_t srcRight, int16_t targetLeft, int16_t targetRight) { int16_t rv; if (srcRight == srcLeft) { rv = targetLeft; } else { rv = (int16_t)( ((target - srcLeft) * targetRight + (srcRight - target) * targetLeft) / (srcRight - srcLeft) ); } return rv; } /* * Adjust the TSF. */ void ath_hal_adjusttsf(struct ath_hal *ah, int32_t tsfdelta) { /* XXX handle wrap/overflow */ OS_REG_WRITE(ah, AR_TSF_L32, OS_REG_READ(ah, AR_TSF_L32) + tsfdelta); } /* * Enable or disable CCA. */ void ath_hal_setcca(struct ath_hal *ah, int ena) { /* * NB: fill me in; this is not provided by default because disabling * CCA in most locales violates regulatory. */ } /* * Get CCA setting. * * XXX TODO: turn this and the above function into methods * in case there are chipset differences in handling CCA. */ int ath_hal_getcca(struct ath_hal *ah) { u_int32_t diag; if (ath_hal_getcapability(ah, HAL_CAP_DIAG, 0, &diag) != HAL_OK) return 1; return ((diag & 0x500000) == 0); } /* * Set the current state of self-generated ACK and RTS/CTS frames. * * For correct DFS operation, the device should not even /ACK/ frames * that are sent to it during CAC or CSA. */ void ath_hal_set_dfs_cac_tx_quiet(struct ath_hal *ah, HAL_BOOL ena) { if (ah->ah_setDfsCacTxQuiet == NULL) return; ah->ah_setDfsCacTxQuiet(ah, ena); } /* * 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++; } Index: head/sys/dev/ath/ath_hal/ah_internal.h =================================================================== --- head/sys/dev/ath/ath_hal/ah_internal.h (revision 318855) +++ head/sys/dev/ath/ath_hal/ah_internal.h (revision 318856) @@ -1,1041 +1,1062 @@ /* * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * Copyright (c) 2002-2008 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$ */ #ifndef _ATH_AH_INTERAL_H_ #define _ATH_AH_INTERAL_H_ /* * Atheros Device Hardware Access Layer (HAL). * * Internal definitions. */ #define AH_NULL 0 #define AH_MIN(a,b) ((a)<(b)?(a):(b)) #define AH_MAX(a,b) ((a)>(b)?(a):(b)) #include +#include /* XXX for reasons */ #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 #ifndef roundup #define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) /* to any y */ #endif #ifndef howmany #define howmany(x, y) (((x)+((y)-1))/(y)) #endif #ifndef offsetof #define offsetof(type, field) ((size_t)(&((type *)0)->field)) #endif typedef struct { uint32_t start; /* first register */ uint32_t end; /* ending register or zero */ } HAL_REGRANGE; typedef struct { uint32_t addr; /* regiser address/offset */ uint32_t value; /* value to write */ } HAL_REGWRITE; /* * Transmit power scale factor. * * NB: This is not public because we want to discourage the use of * scaling; folks should use the tx power limit interface. */ typedef enum { HAL_TP_SCALE_MAX = 0, /* no scaling (default) */ HAL_TP_SCALE_50 = 1, /* 50% of max (-3 dBm) */ HAL_TP_SCALE_25 = 2, /* 25% of max (-6 dBm) */ HAL_TP_SCALE_12 = 3, /* 12% of max (-9 dBm) */ HAL_TP_SCALE_MIN = 4, /* min, but still on */ } HAL_TP_SCALE; typedef enum { HAL_CAP_RADAR = 0, /* Radar capability */ 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. + * + * Compiled-in versions will include a linker set to iterate through the + * linked in code. + * + * Modules will have to register HAL backends separately. */ struct ath_hal_chip { const char *name; 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); + TAILQ_ENTRY(ath_hal_chip) node; }; #ifndef AH_CHIP #define AH_CHIP(_name, _probe, _attach) \ -static struct ath_hal_chip _name##_chip = { \ +struct ath_hal_chip _name##_chip = { \ .name = #_name, \ .probe = _probe, \ - .attach = _attach \ + .attach = _attach, \ }; \ OS_DATA_SET(ah_chips, _name##_chip) #endif /* * Each RF backend registers to offer support; this is mostly * used by multi-chip 5212 solutions. Single-chip solutions * have a fixed idea about which RF to use. + * + * Compiled in versions will include this linker set to iterate through + * the linked in code. + * + * Modules will have to register RF backends separately. */ struct ath_hal_rf { const char *name; HAL_BOOL (*probe)(struct ath_hal *ah); HAL_BOOL (*attach)(struct ath_hal *ah, HAL_STATUS *ecode); + TAILQ_ENTRY(ath_hal_rf) node; }; #ifndef AH_RF #define AH_RF(_name, _probe, _attach) \ -static struct ath_hal_rf _name##_rf = { \ +struct ath_hal_rf _name##_rf = { \ .name = __STRING(_name), \ .probe = _probe, \ - .attach = _attach \ + .attach = _attach, \ }; \ OS_DATA_SET(ah_rfs, _name##_rf) #endif struct ath_hal_rf *ath_hal_rfprobe(struct ath_hal *ah, HAL_STATUS *ecode); /* * Maximum number of internal channels. Entries are per unique * frequency so this might be need to be increased to handle all * usage cases; typically no more than 32 are really needed but * dynamically allocating the data structures is a bit painful * right now. */ #ifndef AH_MAXCHAN #define AH_MAXCHAN 128 #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. */ typedef struct { uint16_t channel; /* h/w frequency, NB: may be mapped */ uint8_t privFlags; #define CHANNEL_IQVALID 0x01 /* IQ calibration valid */ #define CHANNEL_ANI_INIT 0x02 /* ANI state initialized */ #define CHANNEL_ANI_SETUP 0x04 /* ANI state setup */ #define CHANNEL_MIMO_NF_VALID 0x04 /* Mimo NF values are valid */ uint8_t calValid; /* bitmask of cal types */ int8_t iCoff; int8_t qCoff; int16_t rawNoiseFloor; int16_t noiseFloorAdjust; #ifdef AH_SUPPORT_AR5416 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 */ #define CHANNEL_NFCREQUIRED IEEE80211_CHAN_PRIV0 /* all full-width channels */ #define IEEE80211_CHAN_ALLFULL \ (IEEE80211_CHAN_ALL - (IEEE80211_CHAN_HALF | IEEE80211_CHAN_QUARTER)) #define IEEE80211_CHAN_ALLTURBOFULL \ (IEEE80211_CHAN_ALLTURBO - \ (IEEE80211_CHAN_HALF | IEEE80211_CHAN_QUARTER)) typedef struct { uint32_t halChanSpreadSupport : 1, halSleepAfterBeaconBroken : 1, halCompressSupport : 1, halBurstSupport : 1, halFastFramesSupport : 1, halChapTuningSupport : 1, halTurboGSupport : 1, halTurboPrimeSupport : 1, halMicAesCcmSupport : 1, halMicCkipSupport : 1, halMicTkipSupport : 1, halTkipMicTxRxKeySupport : 1, halCipherAesCcmSupport : 1, halCipherCkipSupport : 1, halCipherTkipSupport : 1, halPSPollBroken : 1, halVEOLSupport : 1, halBssIdMaskSupport : 1, halMcastKeySrchSupport : 1, halTsfAddSupport : 1, halChanHalfRate : 1, halChanQuarterRate : 1, halHTSupport : 1, halHTSGI20Support : 1, halRfSilentSupport : 1, halHwPhyCounterSupport : 1, halWowSupport : 1, halWowMatchPatternExact : 1, halAutoSleepSupport : 1, halFastCCSupport : 1, halBtCoexSupport : 1; uint32_t halRxStbcSupport : 1, halTxStbcSupport : 1, halGTTSupport : 1, halCSTSupport : 1, halRifsRxSupport : 1, halRifsTxSupport : 1, hal4AddrAggrSupport : 1, halExtChanDfsSupport : 1, halUseCombinedRadarRssi : 1, halForcePpmSupport : 1, halEnhancedPmSupport : 1, halEnhancedDfsSupport : 1, halMbssidAggrSupport : 1, halBssidMatchSupport : 1, hal4kbSplitTransSupport : 1, halHasRxSelfLinkedTail : 1, halSupportsFastClock5GHz : 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; uint16_t halLow5GhzChan, halHigh5GhzChan; uint16_t halLow2GhzChan, halHigh2GhzChan; int halTxTstampPrecision; int halRxTstampPrecision; int halRtsAggrLimit; uint8_t halTxChainMask; uint8_t halRxChainMask; uint8_t halNumGpioPins; uint8_t halNumAntCfg2GHz; uint8_t halNumAntCfg5GHz; 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 * used by device-independent code such as the regulatory domain support. * In general, code within the HAL should never depend on data in the * public area. Instead any public data needed internally should be * shadowed here. * * When declaring a device-specific ath_hal data structure this structure * is assumed to at the front; e.g. * * struct ath_hal_5212 { * struct ath_hal_private ah_priv; * ... * }; * * It might be better to manage the method pointers in this structure * using an indirect pointer to a read-only data structure but this would * disallow class-style method overriding. */ struct ath_hal_private { struct ath_hal h; /* public area */ /* NB: all methods go first to simplify initialization */ HAL_BOOL (*ah_getChannelEdges)(struct ath_hal*, uint16_t channelFlags, uint16_t *lowChannel, uint16_t *highChannel); u_int (*ah_getWirelessModes)(struct ath_hal*); HAL_BOOL (*ah_eepromRead)(struct ath_hal *, u_int off, uint16_t *data); HAL_BOOL (*ah_eepromWrite)(struct ath_hal *, u_int off, uint16_t data); HAL_BOOL (*ah_getChipPowerLimits)(struct ath_hal *, struct ieee80211_channel *); int16_t (*ah_getNfAdjust)(struct ath_hal *, const HAL_CHANNEL_INTERNAL*); void (*ah_getNoiseFloor)(struct ath_hal *, int16_t nfarray[]); void *ah_eeprom; /* opaque EEPROM state */ uint16_t ah_eeversion; /* EEPROM version */ void (*ah_eepromDetach)(struct ath_hal *); HAL_STATUS (*ah_eepromGet)(struct ath_hal *, int, void *); HAL_STATUS (*ah_eepromSet)(struct ath_hal *, int, int); uint16_t (*ah_getSpurChan)(struct ath_hal *, int, HAL_BOOL); HAL_BOOL (*ah_eepromDiag)(struct ath_hal *, int request, const void *args, uint32_t argsize, void **result, uint32_t *resultsize); /* * Device revision information. */ uint16_t ah_devid; /* PCI device ID */ uint16_t ah_subvendorid; /* PCI subvendor ID */ uint32_t ah_macVersion; /* MAC version id */ uint16_t ah_macRev; /* MAC revision */ 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 */ HAL_CAPABILITIES ah_caps; /* device capabilities */ uint32_t ah_diagreg; /* user-specified AR_DIAG_SW */ 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 */ /* * State for regulatory domain handling. */ HAL_REG_DOMAIN ah_currentRD; /* EEPROM regulatory domain */ HAL_REG_DOMAIN ah_currentRDext; /* EEPROM extended regdomain flags */ HAL_DFS_DOMAIN ah_dfsDomain; /* current DFS domain */ HAL_CHANNEL_INTERNAL ah_channels[AH_MAXCHAN]; /* private chan state */ u_int ah_nchan; /* valid items in ah_channels */ const struct regDomain *ah_rd2GHz; /* reg state for 2G band */ const struct regDomain *ah_rd5GHz; /* reg state for 5G band */ uint8_t ah_coverageClass; /* coverage class */ /* * RF Silent handling; setup according to the EEPROM. */ uint16_t ah_rfsilent; /* GPIO pin + polarity */ HAL_BOOL ah_rfkillEnabled; /* enable/disable RfKill */ /* * Diagnostic support for discriminating HIUERR reports. */ 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)) #define ath_hal_getChannelEdges(_ah, _cf, _lc, _hc) \ AH_PRIVATE(_ah)->ah_getChannelEdges(_ah, _cf, _lc, _hc) #define ath_hal_getWirelessModes(_ah) \ AH_PRIVATE(_ah)->ah_getWirelessModes(_ah) #define ath_hal_eepromRead(_ah, _off, _data) \ AH_PRIVATE(_ah)->ah_eepromRead(_ah, _off, _data) #define ath_hal_eepromWrite(_ah, _off, _data) \ AH_PRIVATE(_ah)->ah_eepromWrite(_ah, _off, _data) #define ath_hal_gpioCfgOutput(_ah, _gpio, _type) \ (_ah)->ah_gpioCfgOutput(_ah, _gpio, _type) #define ath_hal_gpioCfgInput(_ah, _gpio) \ (_ah)->ah_gpioCfgInput(_ah, _gpio) #define ath_hal_gpioGet(_ah, _gpio) \ (_ah)->ah_gpioGet(_ah, _gpio) #define ath_hal_gpioSet(_ah, _gpio, _val) \ (_ah)->ah_gpioSet(_ah, _gpio, _val) #define ath_hal_gpioSetIntr(_ah, _gpio, _ilevel) \ (_ah)->ah_gpioSetIntr(_ah, _gpio, _ilevel) #define ath_hal_getpowerlimits(_ah, _chan) \ AH_PRIVATE(_ah)->ah_getChipPowerLimits(_ah, _chan) #define ath_hal_getNfAdjust(_ah, _c) \ 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, _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); \ } while (0) #define ath_hal_eepromGet(_ah, _param, _val) \ AH_PRIVATE(_ah)->ah_eepromGet(_ah, _param, _val) #define ath_hal_eepromSet(_ah, _param, _val) \ AH_PRIVATE(_ah)->ah_eepromSet(_ah, _param, _val) #define ath_hal_eepromGetFlag(_ah, _param) \ (AH_PRIVATE(_ah)->ah_eepromGet(_ah, _param, AH_NULL) == HAL_OK) #define ath_hal_getSpurChan(_ah, _ix, _is2G) \ AH_PRIVATE(_ah)->ah_getSpurChan(_ah, _ix, _is2G) #define ath_hal_eepromDiag(_ah, _request, _a, _asize, _r, _rsize) \ AH_PRIVATE(_ah)->ah_eepromDiag(_ah, _request, _a, _asize, _r, _rsize) #ifndef _NET_IF_IEEE80211_H_ /* * Stuff that would naturally come from _ieee80211.h */ #define IEEE80211_ADDR_LEN 6 #define IEEE80211_WEP_IVLEN 3 /* 24bit */ #define IEEE80211_WEP_KIDLEN 1 /* 1 octet */ #define IEEE80211_WEP_CRCLEN 4 /* CRC-32 */ #define IEEE80211_CRC_LEN 4 #define IEEE80211_MAX_LEN (2300 + IEEE80211_CRC_LEN + \ (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_CRCLEN)) #endif /* _NET_IF_IEEE80211_H_ */ #define HAL_TXQ_USE_LOCKOUT_BKOFF_DIS 0x00000001 #define INIT_AIFS 2 #define INIT_CWMIN 15 #define INIT_CWMIN_11B 31 #define INIT_CWMAX 1023 #define INIT_SH_RETRY 10 #define INIT_LG_RETRY 10 #define INIT_SSH_RETRY 32 #define INIT_SLG_RETRY 32 typedef struct { uint32_t tqi_ver; /* HAL TXQ verson */ HAL_TX_QUEUE tqi_type; /* hw queue type*/ HAL_TX_QUEUE_SUBTYPE tqi_subtype; /* queue subtype, if applicable */ HAL_TX_QUEUE_FLAGS tqi_qflags; /* queue flags */ uint32_t tqi_priority; uint32_t tqi_aifs; /* aifs */ uint32_t tqi_cwmin; /* cwMin */ uint32_t tqi_cwmax; /* cwMax */ uint16_t tqi_shretry; /* frame short retry limit */ uint16_t tqi_lgretry; /* frame long retry limit */ uint32_t tqi_cbrPeriod; uint32_t tqi_cbrOverflowLimit; uint32_t tqi_burstTime; uint32_t tqi_readyTime; uint32_t tqi_physCompBuf; uint32_t tqi_intFlags; /* flags for internal use */ } HAL_TX_QUEUE_INFO; extern HAL_BOOL ath_hal_setTxQProps(struct ath_hal *ah, HAL_TX_QUEUE_INFO *qi, const HAL_TXQ_INFO *qInfo); extern HAL_BOOL ath_hal_getTxQProps(struct ath_hal *ah, HAL_TXQ_INFO *qInfo, const HAL_TX_QUEUE_INFO *qi); #define HAL_SPUR_VAL_MASK 0x3FFF #define HAL_SPUR_CHAN_WIDTH 87 #define HAL_BIN_WIDTH_BASE_100HZ 3125 #define HAL_BIN_WIDTH_TURBO_100HZ 6250 #define HAL_MAX_BINS_ALLOWED 28 #define IS_CHAN_5GHZ(_c) ((_c)->channel > 4900) #define IS_CHAN_2GHZ(_c) (!IS_CHAN_5GHZ(_c)) #define IS_CHAN_IN_PUBLIC_SAFETY_BAND(_c) ((_c) > 4940 && (_c) < 4990) /* * Deduce if the host cpu has big- or litt-endian byte order. */ static __inline__ int isBigEndian(void) { union { int32_t i; char c[4]; } u; u.i = 1; return (u.c[0] == 0); } /* unalligned little endian access */ #define LE_READ_2(p) \ ((uint16_t) \ ((((const uint8_t *)(p))[0] ) | (((const uint8_t *)(p))[1]<< 8))) #define LE_READ_4(p) \ ((uint32_t) \ ((((const uint8_t *)(p))[0] ) | (((const uint8_t *)(p))[1]<< 8) |\ (((const uint8_t *)(p))[2]<<16) | (((const uint8_t *)(p))[3]<<24))) /* * Register manipulation macros that expect bit field defines * to follow the convention that an _S suffix is appended for * a shift count, while the field mask has no suffix. */ #define SM(_v, _f) (((_v) << _f##_S) & (_f)) #define MS(_v, _f) (((_v) & (_f)) >> _f##_S) #define OS_REG_RMW(_a, _r, _set, _clr) \ OS_REG_WRITE(_a, _r, (OS_REG_READ(_a, _r) & ~(_clr)) | (_set)) #define OS_REG_RMW_FIELD(_a, _r, _f, _v) \ OS_REG_WRITE(_a, _r, \ (OS_REG_READ(_a, _r) &~ (_f)) | (((_v) << _f##_S) & (_f))) #define OS_REG_SET_BIT(_a, _r, _f) \ 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) #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, uint32_t mask, uint32_t val); extern HAL_BOOL ath_hal_waitfor(struct ath_hal *, u_int reg, uint32_t mask, uint32_t val, uint32_t timeout); /* return the first n bits in val reversed */ extern uint32_t ath_hal_reverseBits(uint32_t val, uint32_t n); /* printf interfaces */ 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) __printflike(2, 0); extern const char* ath_hal_ether_sprintf(const uint8_t *mac); /* allocate and free memory */ extern void *ath_hal_malloc(size_t); extern void ath_hal_free(void *); /* common debugging interfaces */ #ifdef AH_DEBUG #include "ah_debug.h" extern int ath_hal_debug; /* Global debug flags */ /* * 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) != NULL && \ ((struct ath_hal *) (_ah))->ah_config.ah_debug & (__m))) { \ DO_HALDEBUG((_ah), (__m), __VA_ARGS__); \ } \ } while(0); extern void DO_HALDEBUG(struct ath_hal *ah, u_int mask, const char* fmt, ...) __printflike(3,4); #else #define HALDEBUG(_ah, __m, ...) #endif /* AH_DEBUG */ /* * Register logging definitions shared with ardecode. */ #include "ah_decode.h" /* * Common assertion interface. Note: it is a bad idea to generate * an assertion failure for any recoverable event. Instead catch * the violation and, if possible, fix it up or recover from it; either * with an error return value or a diagnostic messages. System software * does not panic unless the situation is hopeless. */ #ifdef AH_ASSERT extern void ath_hal_assert_failed(const char* filename, int lineno, const char* msg); #define HALASSERT(_x) do { \ if (!(_x)) { \ ath_hal_assert_failed(__FILE__, __LINE__, #_x); \ } \ } while (0) #else #define HALASSERT(_x) #endif /* AH_ASSERT */ /* * Regulatory domain support. */ /* * Return the max allowed antenna gain and apply any regulatory * domain specific changes. */ u_int ath_hal_getantennareduction(struct ath_hal *ah, const struct ieee80211_channel *chan, u_int twiceGain); /* * Return the test group for the specific channel based on * the current regulatory setup. */ u_int ath_hal_getctl(struct ath_hal *, const struct ieee80211_channel *); /* * Map a public channel definition to the corresponding * internal data structure. This implicitly specifies * whether or not the specified channel is ok to use * based on the current regulatory domain constraints. */ #ifndef AH_DEBUG static OS_INLINE HAL_CHANNEL_INTERNAL * ath_hal_checkchannel(struct ath_hal *ah, const struct ieee80211_channel *c) { HAL_CHANNEL_INTERNAL *cc; HALASSERT(c->ic_devdata < AH_PRIVATE(ah)->ah_nchan); cc = &AH_PRIVATE(ah)->ah_channels[c->ic_devdata]; HALASSERT(c->ic_freq == cc->channel || IEEE80211_IS_CHAN_GSM(c)); return cc; } #else /* NB: non-inline version that checks state */ HAL_CHANNEL_INTERNAL *ath_hal_checkchannel(struct ath_hal *, const struct ieee80211_channel *); #endif /* AH_DEBUG */ /* * Return the h/w frequency for a channel. This may be * different from ic_freq if this is a GSM device that * takes 2.4GHz frequencies and down-converts them. */ static OS_INLINE uint16_t ath_hal_gethwchannel(struct ath_hal *ah, const struct ieee80211_channel *c) { return ath_hal_checkchannel(ah, c)->channel; } /* * Generic get/set capability support. Each chip overrides * this routine to support chip-specific capabilities. */ extern HAL_STATUS ath_hal_getcapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type, uint32_t capability, uint32_t *result); extern HAL_BOOL ath_hal_setcapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type, uint32_t capability, uint32_t setting, HAL_STATUS *status); /* 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, HAL_BB_HANG_RX_CLEAR = 0x0004, HAL_BB_HANG_UNKNOWN = 0x0080, HAL_MAC_HANG_SIG1 = 0x0100, HAL_MAC_HANG_SIG2 = 0x0200, HAL_MAC_HANG_UNKNOWN = 0x8000, HAL_BB_HANGS = HAL_BB_HANG_DFS | HAL_BB_HANG_RIFS | HAL_BB_HANG_RX_CLEAR | HAL_BB_HANG_UNKNOWN, HAL_MAC_HANGS = HAL_MAC_HANG_SIG1 | HAL_MAC_HANG_SIG2 | 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. */ typedef struct { uint16_t ah_devid; /* PCI device ID */ uint16_t ah_subvendorid; /* PCI subvendor ID */ uint32_t ah_macVersion; /* MAC version id */ uint16_t ah_macRev; /* MAC revision */ uint16_t ah_phyRev; /* PHY revision */ uint16_t ah_analog5GhzRev; /* 2GHz radio revision */ uint16_t ah_analog2GhzRev; /* 5GHz radio revision */ } HAL_REVS; /* * Argument payload for HAL_DIAG_SETKEY. */ typedef struct { HAL_KEYVAL dk_keyval; uint16_t dk_keyix; /* key index */ uint8_t dk_mac[IEEE80211_ADDR_LEN]; int dk_xor; /* XOR key data */ } HAL_DIAG_KEYVAL; /* * Argument payload for HAL_DIAG_EEWRITE. */ typedef struct { uint16_t ee_off; /* eeprom offset */ uint16_t ee_data; /* write data */ } HAL_DIAG_EEVAL; typedef struct { u_int offset; /* reg offset */ uint32_t val; /* reg value */ } HAL_DIAG_REGVAL; /* * 11n compatibility tweaks. */ #define HAL_DIAG_11N_SERVICES 0x00000003 #define HAL_DIAG_11N_SERVICES_S 0 #define HAL_DIAG_11N_TXSTOMP 0x0000000c #define HAL_DIAG_11N_TXSTOMP_S 2 typedef struct { int maxNoiseImmunityLevel; /* [0..4] */ int totalSizeDesired[5]; int coarseHigh[5]; int coarseLow[5]; int firpwr[5]; int maxSpurImmunityLevel; /* [0..7] */ int cycPwrThr1[8]; int maxFirstepLevel; /* [0..2] */ int firstep[3]; uint32_t ofdmTrigHigh; uint32_t ofdmTrigLow; int32_t cckTrigHigh; int32_t cckTrigLow; int32_t rssiThrLow; int32_t rssiThrHigh; int period; /* update listen period */ } HAL_ANI_PARAMS; extern HAL_BOOL ath_hal_getdiagstate(struct ath_hal *ah, int request, const void *args, uint32_t argsize, void **result, uint32_t *resultsize); /* * Setup a h/w rate table for use. */ extern void ath_hal_setupratetable(struct ath_hal *ah, HAL_RATE_TABLE *rt); /* * Common routine for implementing getChanNoise api. */ int16_t ath_hal_getChanNoise(struct ath_hal *, const struct ieee80211_channel *); /* * Initialization support. */ typedef struct { const uint32_t *data; int rows, cols; } HAL_INI_ARRAY; #define HAL_INI_INIT(_ia, _data, _cols) do { \ (_ia)->data = (const uint32_t *)(_data); \ (_ia)->rows = sizeof(_data) / sizeof((_data)[0]); \ (_ia)->cols = (_cols); \ } while (0) #define HAL_INI_VAL(_ia, _r, _c) \ ((_ia)->data[((_r)*(_ia)->cols) + (_c)]) /* * OS_DELAY() does a PIO READ on the PCI bus which allows * other cards' DMA reads to complete in the middle of our reset. */ #define DMA_YIELD(x) do { \ if ((++(x) % 64) == 0) \ OS_DELAY(1); \ } while (0) #define HAL_INI_WRITE_ARRAY(ah, regArray, col, regWr) do { \ int r; \ for (r = 0; r < N(regArray); r++) { \ OS_REG_WRITE(ah, (regArray)[r][0], (regArray)[r][col]); \ DMA_YIELD(regWr); \ } \ } while (0) #define HAL_INI_WRITE_BANK(ah, regArray, bankData, regWr) do { \ int r; \ for (r = 0; r < N(regArray); r++) { \ OS_REG_WRITE(ah, (regArray)[r][0], (bankData)[r]); \ DMA_YIELD(regWr); \ } \ } while (0) extern int ath_hal_ini_write(struct ath_hal *ah, const HAL_INI_ARRAY *ia, int col, int regWr); extern void ath_hal_ini_bank_setup(uint32_t data[], const HAL_INI_ARRAY *ia, int col); extern int ath_hal_ini_bank_write(struct ath_hal *ah, const HAL_INI_ARRAY *ia, const uint32_t data[], int regWr); #define CCK_SIFS_TIME 10 #define CCK_PREAMBLE_BITS 144 #define CCK_PLCP_BITS 48 #define OFDM_SIFS_TIME 16 #define OFDM_PREAMBLE_TIME 20 #define OFDM_PLCP_BITS 22 #define OFDM_SYMBOL_TIME 4 #define OFDM_HALF_SIFS_TIME 32 #define OFDM_HALF_PREAMBLE_TIME 40 #define OFDM_HALF_PLCP_BITS 22 #define OFDM_HALF_SYMBOL_TIME 8 #define OFDM_QUARTER_SIFS_TIME 64 #define OFDM_QUARTER_PREAMBLE_TIME 80 #define OFDM_QUARTER_PLCP_BITS 22 #define OFDM_QUARTER_SYMBOL_TIME 16 #define TURBO_SIFS_TIME 8 #define TURBO_PREAMBLE_TIME 14 #define TURBO_PLCP_BITS 22 #define TURBO_SYMBOL_TIME 4 #define WLAN_CTRL_FRAME_SIZE (2+2+6+4) /* ACK+FCS */ /* Generic EEPROM board value functions */ extern HAL_BOOL ath_ee_getLowerUpperIndex(uint8_t target, uint8_t *pList, uint16_t listSize, uint16_t *indexL, uint16_t *indexR); extern HAL_BOOL ath_ee_FillVpdTable(uint8_t pwrMin, uint8_t pwrMax, uint8_t *pPwrList, uint8_t *pVpdList, uint16_t numIntercepts, uint8_t *pRetVpdList); extern int16_t ath_ee_interpolate(uint16_t target, uint16_t srcLeft, uint16_t srcRight, int16_t targetLeft, int16_t targetRight); /* Whether 5ghz fast clock is needed */ /* * The chipset (Merlin, AR9300/later) should set the capability flag below; * this flag simply says that the hardware can do it, not that the EEPROM * says it can. * * Merlin 2.0/2.1 chips with an EEPROM version > 16 do 5ghz fast clock * if the relevant eeprom flag is set. * Merlin 2.0/2.1 chips with an EEPROM version <= 16 do 5ghz fast clock * by default. */ #define IS_5GHZ_FAST_CLOCK_EN(_ah, _c) \ (IEEE80211_IS_CHAN_5GHZ(_c) && \ 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); + +/* + * Chip registration - for modules. + */ +extern int ath_hal_add_chip(struct ath_hal_chip *ahc); +extern int ath_hal_remove_chip(struct ath_hal_chip *ahc); +extern int ath_hal_add_rf(struct ath_hal_rf *arf); +extern int ath_hal_remove_rf(struct ath_hal_rf *arf); #endif /* _ATH_AH_INTERAL_H_ */ Index: head/sys/dev/ath/ath_rate/sample/sample.c =================================================================== --- head/sys/dev/ath/ath_rate/sample/sample.c (revision 318855) +++ head/sys/dev/ath/ath_rate/sample/sample.c (revision 318856) @@ -1,1393 +1,1397 @@ /*- * Copyright (c) 2005 John Bicket * 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. * 3. Neither the names of the above-listed copyright holders nor the names * of any contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * 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$"); /* * 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 #include #include #include #include #include #include #include #include #include /* XXX for ether_sprintf */ #include #include #ifdef INET #include #include #endif #include #include #include #include /* * This file is an implementation of the SampleRate algorithm * in "Bit-rate Selection in Wireless Networks" * (http://www.pdos.lcs.mit.edu/papers/jbicket-ms.ps) * * SampleRate chooses the bit-rate it predicts will provide the most * throughput based on estimates of the expected per-packet * transmission time for each bit-rate. SampleRate periodically sends * packets at bit-rates other than the current one to estimate when * another bit-rate will provide better performance. SampleRate * switches to another bit-rate when its estimated per-packet * transmission time becomes smaller than the current bit-rate's. * SampleRate reduces the number of bit-rates it must sample by * eliminating those that could not perform better than the one * currently being used. SampleRate also stops probing at a bit-rate * if it experiences several successive losses. * * The difference between the algorithm in the thesis and the one in this * file is that the one in this file uses a ewma instead of a window. * * Also, this implementation tracks the average transmission time for * a few different packet sizes independently for each link. */ static void ath_rate_ctl_reset(struct ath_softc *, struct ieee80211_node *); static __inline int size_to_bin(int size) { #if NUM_PACKET_SIZE_BINS > 1 if (size <= packet_size_bins[0]) return 0; #endif #if NUM_PACKET_SIZE_BINS > 2 if (size <= packet_size_bins[1]) return 1; #endif #if NUM_PACKET_SIZE_BINS > 3 if (size <= packet_size_bins[2]) return 2; #endif #if NUM_PACKET_SIZE_BINS > 4 #error "add support for more packet sizes" #endif return NUM_PACKET_SIZE_BINS-1; } void ath_rate_node_init(struct ath_softc *sc, struct ath_node *an) { /* NB: assumed to be zero'd by caller */ } void 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; } 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 "; } /* * Return the rix with the lowest average_tx_time, * or -1 if all the average_tx_times are 0. */ static __inline int 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, 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; /* Don't pick a non-HT rate for a HT node */ if ((an->an_node.ni_flags & IEEE80211_NODE_HT) && (rt->info[rix].phy != IEEE80211_T_HT)) { continue; } tt = sn->stats[size_bin][rix].average_tx_time; if (tt <= 0 || (require_acked_before && !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; /* * 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); } /* * Pick a good "random" bit-rate to sample other than the current one. */ static __inline int pick_sample_rate(struct sample_softc *ssc , struct ath_node *an, const HAL_RATE_TABLE *rt, int size_bin) { #define DOT11RATE(ix) (rt->info[ix].dot11Rate & IEEE80211_RATE_VAL) #define MCS(ix) (rt->info[ix].dot11Rate | IEEE80211_RATE_MCS) struct sample_node *sn = ATH_NODE_SAMPLE(an); int current_rix, rix; unsigned current_tt; uint64_t mask; current_rix = sn->current_rix[size_bin]; if (current_rix < 0) { /* no successes yet, send at the lowest bit-rate */ /* XXX should return MCS0 if HT */ return 0; } 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 &~ ((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 &= ~((uint64_t) 1<stats[size_bin][rix].perfect_tx_time > current_tt) { mask &= ~((uint64_t) 1<stats[size_bin][rix].successive_failures > ssc->max_successive_failures && ticks - sn->stats[size_bin][rix].last_tx < ssc->stale_failure_timeout) { mask &= ~((uint64_t) 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 &= ~((uint64_t) 1<last_sample_rix[size_bin] = rix; return rix; } return current_rix; #undef DOT11RATE #undef MCS } static int ath_rate_get_static_rix(struct ath_softc *sc, const struct ieee80211_node *ni) { #define RATE(_ix) (ni->ni_rates.rs_rates[(_ix)] & IEEE80211_RATE_VAL) #define DOT11RATE(_ix) (rt->info[(_ix)].dot11Rate & IEEE80211_RATE_VAL) #define MCS(_ix) (ni->ni_htrates.rs_rates[_ix] | IEEE80211_RATE_MCS) const struct ieee80211_txparam *tp = ni->ni_txparms; int srate; /* Check MCS rates */ for (srate = ni->ni_htrates.rs_nrates - 1; srate >= 0; srate--) { if (MCS(srate) == tp->ucastrate) return sc->sc_rixmap[tp->ucastrate]; } /* Check legacy rates */ for (srate = ni->ni_rates.rs_nrates - 1; srate >= 0; srate--) { if (RATE(srate) == tp->ucastrate) return sc->sc_rixmap[tp->ucastrate]; } return -1; #undef RATE #undef DOT11RATE #undef MCS } static void ath_rate_update_static_rix(struct ath_softc *sc, struct ieee80211_node *ni) { struct ath_node *an = ATH_NODE(ni); const struct ieee80211_txparam *tp = ni->ni_txparms; struct sample_node *sn = ATH_NODE_SAMPLE(an); if (tp != NULL && tp->ucastrate != IEEE80211_FIXED_RATE_NONE) { /* * A fixed rate is to be used; ucastrate is the IEEE code * for this rate (sans basic bit). Check this against the * negotiated rate set for the node. Note the fixed rate * may not be available for various reasons so we only * setup the static rate index if the lookup is successful. */ sn->static_rix = ath_rate_get_static_rix(sc, ni); } else { sn->static_rix = -1; } } /* * Pick a non-HT rate to begin using. */ static int ath_rate_pick_seed_rate_legacy(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; 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 ath_rate_findrate(struct ath_softc *sc, struct ath_node *an, int shortPreamble, size_t frameLen, u_int8_t *rix0, int *try0, u_int8_t *txrate) { #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) struct sample_node *sn = ATH_NODE_SAMPLE(an); struct sample_softc *ssc = ATH_SOFTC_SAMPLE(sc); 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; unsigned average_tx_time; 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; } 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) { average_tx_time = sn->stats[size_bin][best_rix].average_tx_time; } else { average_tx_time = 0; } /* * Limit the time measuring the performance of other tx * rates to sample_rate% of the total transmission time. */ 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, "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 { sn->current_sample_rix[size_bin] = -1; } sn->packets_since_sample[size_bin] = 0; } else { change_rates = 0; if (!sn->packets_sent[size_bin] || best_rix == -1) { /* no packet has been sent successfully yet */ change_rates = 1; 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 ((! (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]++; if (change_rates) { if (best_rix != sn->current_rix[size_bin]) { IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL, &an->an_node, "%s: size %d switch rate %d (%d/%d) -> %d (%d/%d) after %d packets mrr %d", __func__, bin_to_size(size_bin), RATE(sn->current_rix[size_bin]), sn->stats[size_bin][sn->current_rix[size_bin]].average_tx_time, sn->stats[size_bin][sn->current_rix[size_bin]].perfect_tx_time, RATE(best_rix), sn->stats[size_bin][best_rix].average_tx_time, sn->stats[size_bin][best_rix].perfect_tx_time, sn->packets_since_switch[size_bin], mrr); } sn->packets_since_switch[size_bin] = 0; sn->current_rix[size_bin] = best_rix; sn->ticks_since_switch[size_bin] = ticks; /* * Set the visible txrate for this node. */ an->an_node.ni_txrate = (rt->info[best_rix].phy == IEEE80211_T_HT) ? MCS(best_rix) : DOT11RATE(best_rix); } rix = sn->current_rix[size_bin]; sn->packets_since_switch[size_bin]++; } *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; *txrate = rt->info[rix].rateCode | (shortPreamble ? rt->info[rix].shortPreamble : 0); sn->packets_sent[size_bin]++; #undef DOT11RATE #undef MCS #undef RATE } /* * Get the TX rates. Don't fiddle with short preamble flags for them; * the caller can do that. */ void ath_rate_getxtxrates(struct ath_softc *sc, struct ath_node *an, 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)); rc[0].flags = rc[1].flags = rc[2].flags = rc[3].flags = 0; 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 ath_rate_setupxtxdesc(struct ath_softc *sc, struct ath_node *an, struct ath_desc *ds, int shortPreamble, u_int8_t rix) { struct sample_node *sn = ATH_NODE_SAMPLE(an); const struct txschedule *sched = &sn->sched[rix]; const HAL_RATE_TABLE *rt = sc->sc_currates; uint8_t rix1, s1code, rix2, s2code, rix3, s3code; /* XXX precalculate short preamble tables */ rix1 = sched->r1; s1code = rt->info[rix1].rateCode | (shortPreamble ? rt->info[rix1].shortPreamble : 0); rix2 = sched->r2; s2code = rt->info[rix2].rateCode | (shortPreamble ? rt->info[rix2].shortPreamble : 0); rix3 = sched->r3; s3code = rt->info[rix3].rateCode | (shortPreamble ? rt->info[rix3].shortPreamble : 0); ath_hal_setupxtxdesc(sc->sc_ah, ds, s1code, sched->t1, /* series 1 */ s2code, sched->t2, /* series 2 */ s3code, sched->t3); /* series 3 */ } static void update_stats(struct ath_softc *sc, struct ath_node *an, int frame_size, int rix0, int tries0, int rix1, int tries1, int rix2, int tries2, int rix3, int tries3, 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; tt = calc_usecs_unicast_packet(sc, size, rix0, short_tries, MIN(tries0, tries) - 1, is_ht40); tries_so_far = tries0; if (tries1 && tries_so_far < tries) { if (!IS_RATE_DEFINED(sn, rix1)) return; tt += calc_usecs_unicast_packet(sc, size, rix1, short_tries, MIN(tries1 + tries_so_far, tries) - tries_so_far - 1, is_ht40); tries_so_far += tries1; } if (tries2 && tries_so_far < tries) { if (!IS_RATE_DEFINED(sn, rix2)) return; tt += calc_usecs_unicast_packet(sc, size, rix2, short_tries, MIN(tries2 + tries_so_far, tries) - tries_so_far - 1, is_ht40); tries_so_far += tries2; } if (tries3 && tries_so_far < tries) { if (!IS_RATE_DEFINED(sn, rix3)) return; tt += calc_usecs_unicast_packet(sc, size, rix3, short_tries, MIN(tries3 + tries_so_far, tries) - tries_so_far - 1, is_ht40); } if (sn->stats[size_bin][rix0].total_packets < ssc->smoothing_minpackets) { /* 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+nframes); } else { /* use a ewma */ sn->stats[size_bin][rix0].average_tx_time = ((sn->stats[size_bin][rix0].average_tx_time * ssc->smoothing_rate) + (tt * (100 - ssc->smoothing_rate))) / 100; } /* * 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. + * + * This is valid for higher CCK rates, higher OFDM rates, and higher + * HT rates within the current number of streams (eg MCS0..7, 8..15, + * etc.) */ if (nframes == nbad) { #if 0 int y; #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 += nbad; sn->stats[y][rix0].last_tx = ticks; sn->stats[y][rix0].tries += tries; sn->stats[y][rix0].total_packets += nframes; } #endif } else { 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 += 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 %s tries (%d/%d) tt %d avg_tt (%d/%d) nfrm %d nbad %d", __func__, size, status ? "FAIL" : "OK", 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, nframes, nbad); sn->sample_tt[size_bin] = tt; sn->current_sample_rix[size_bin] = -1; } } static void badrate(struct ath_softc *sc, int series, int hwrate, int tries, int status) { 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_rc_series *rc, const struct ath_tx_status *ts, int frame_size, int nframes, int nbad) { struct ieee80211com *ic = &sc->sc_ic; struct sample_node *sn = ATH_NODE_SAMPLE(an); 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; 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; if (sn->ratemask == 0) { IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL, &an->an_node, "%s: size %d %s rate/try %d/%d no rates yet", __func__, bin_to_size(size_to_bin(frame_size)), status ? "FAIL" : "OK", short_tries, long_tries); return; } 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)) { 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/short/long %d %s/%d/%d nframes/nbad [%d/%d]", __func__, bin_to_size(size_to_bin(frame_size)), frame_size, 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, status, nframes, nbad); } else { int finalTSIdx = ts->ts_finaltsi; int i; /* * Process intermediate rates that failed. */ IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL, &an->an_node, "%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, 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 (rc[i].tries && !IS_RATE_DEFINED(sn, rc[i].rix)) badrate(sc, 0, rc[i].ratecode, rc[i].tries, status); } /* * NB: series > 0 are not penalized for failure * based on the try counts under the assumption * that losses are often bursty and since we * sample higher rates 1 try at a time doing so * may unfairly penalize them. */ 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 (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 (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, status, nframes, nbad); long_tries -= rc[2].tries; } 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, status, nframes, nbad); } } } void ath_rate_newassoc(struct ath_softc *sc, struct ath_node *an, int isnew) { if (isnew) ath_rate_ctl_reset(sc, &an->an_node); } static const struct txschedule *mrr_schedules[IEEE80211_MODE_MAX+2] = { NULL, /* IEEE80211_MODE_AUTO */ series_11a, /* IEEE80211_MODE_11A */ series_11g, /* IEEE80211_MODE_11B */ series_11g, /* IEEE80211_MODE_11G */ NULL, /* IEEE80211_MODE_FH */ series_11a, /* IEEE80211_MODE_TURBO_A */ series_11g, /* IEEE80211_MODE_TURBO_G */ series_11a, /* IEEE80211_MODE_STURBO_A */ series_11na, /* IEEE80211_MODE_11NA */ series_11ng, /* IEEE80211_MODE_11NG */ series_half, /* IEEE80211_MODE_HALF */ series_quarter, /* IEEE80211_MODE_QUARTER */ }; /* * Initialize the tables for a node. */ static void ath_rate_ctl_reset(struct ath_softc *sc, struct ieee80211_node *ni) { #define RATE(_ix) (ni->ni_rates.rs_rates[(_ix)] & IEEE80211_RATE_VAL) #define DOT11RATE(_ix) (rt->info[(_ix)].dot11Rate & IEEE80211_RATE_VAL) #define MCS(_ix) (ni->ni_htrates.rs_rates[_ix] | IEEE80211_RATE_MCS) struct ath_node *an = ATH_NODE(ni); struct sample_node *sn = ATH_NODE_SAMPLE(an); const HAL_RATE_TABLE *rt = sc->sc_currates; int x, y, rix; KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode)); 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)); 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 * to be ignored for doing rate control. */ sn->ratemask = 0; /* MCS rates */ if (ni->ni_flags & IEEE80211_NODE_HT) { for (x = 0; x < ni->ni_htrates.rs_nrates; x++) { rix = sc->sc_rixmap[MCS(x)]; if (rix == 0xff) continue; /* skip rates marked broken by hal */ if (!rt->info[rix].valid) continue; KASSERT(rix < SAMPLE_MAXRATES, ("mcs %u has rix %d", MCS(x), rix)); sn->ratemask |= (uint64_t) 1<ni_rates.rs_nrates; x++) { rix = sc->sc_rixmap[RATE(x)]; if (rix == 0xff) continue; /* skip rates marked broken by hal */ if (!rt->info[rix].valid) continue; KASSERT(rix < SAMPLE_MAXRATES, ("rate %u has rix %d", RATE(x), rix)); sn->ratemask |= (uint64_t) 1<ni_vap, IEEE80211_MSG_RATECTL)) { uint64_t mask; ieee80211_note(ni->ni_vap, "[%6D] %s: size 1600 rate/tt", ni->ni_macaddr, ":", __func__); for (mask = sn->ratemask, rix = 0; mask != 0; mask >>= 1, rix++) { if ((mask & 1) == 0) continue; printf(" %d %s/%d", dot11rate(rt, rix), dot11rate_label(rt, rix), calc_usecs_unicast_packet(sc, 1600, rix, 0,0, (ni->ni_chw == 40))); } printf("\n"); } #endif for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) { int size = bin_to_size(y); uint64_t mask; sn->packets_sent[y] = 0; sn->current_sample_rix[y] = -1; sn->last_sample_rix[y] = 0; /* XXX start with first valid rate */ sn->current_rix[y] = ffs(sn->ratemask)-1; /* * Initialize the statistics buckets; these are * indexed by the rate code index. */ for (rix = 0, mask = sn->ratemask; mask != 0; rix++, mask >>= 1) { if ((mask & 1) == 0) /* not a valid rate */ continue; sn->stats[y][rix].successive_failures = 0; sn->stats[y][rix].tries = 0; 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, (ni->ni_chw == 40)); sn->stats[y][rix].average_tx_time = sn->stats[y][rix].perfect_tx_time; } } #if 0 /* XXX 0, num_rates-1 are wrong */ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, "%s: %d rates %d%sMbps (%dus)- %d%sMbps (%dus)", __func__, sn->num_rates, DOT11RATE(0)/2, DOT11RATE(0) % 1 ? ".5" : "", sn->stats[1][0].perfect_tx_time, DOT11RATE(sn->num_rates-1)/2, DOT11RATE(sn->num_rates-1) % 1 ? ".5" : "", sn->stats[1][sn->num_rates-1].perfect_tx_time ); #endif /* set the visible bit-rate */ if (sn->static_rix != -1) ni->ni_txrate = DOT11RATE(sn->static_rix); else ni->ni_txrate = RATE(0); #undef RATE #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)); uint64_t mask; int rix, y; printf("\n[%s] refcnt %d static_rix (%d %s) ratemask 0x%jx\n", ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni), 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 %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]); } for (mask = sn->ratemask, rix = 0; mask != 0; mask >>= 1, rix++) { if ((mask & 1) == 0) continue; for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) { if (sn->stats[y][rix].total_packets == 0) continue; 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), (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); } } } static int ath_rate_sysctl_stats(SYSCTL_HANDLER_ARGS) { struct ath_softc *sc = arg1; struct ieee80211com *ic = &sc->sc_ic; int error, v; v = 0; error = sysctl_handle_int(oidp, &v, 0, req); if (error || !req->newptr) return error; ieee80211_iterate_nodes(&ic->ic_sta, sample_stats, sc); return 0; } static int ath_rate_sysctl_smoothing_rate(SYSCTL_HANDLER_ARGS) { struct sample_softc *ssc = arg1; int rate, error; rate = ssc->smoothing_rate; error = sysctl_handle_int(oidp, &rate, 0, req); if (error || !req->newptr) return error; if (!(0 <= rate && rate < 100)) return EINVAL; ssc->smoothing_rate = rate; ssc->smoothing_minpackets = 100 / (100 - rate); return 0; } static int ath_rate_sysctl_sample_rate(SYSCTL_HANDLER_ARGS) { struct sample_softc *ssc = arg1; int rate, error; rate = ssc->sample_rate; error = sysctl_handle_int(oidp, &rate, 0, req); if (error || !req->newptr) return error; if (!(2 <= rate && rate <= 100)) return EINVAL; ssc->sample_rate = rate; return 0; } static void ath_rate_sysctlattach(struct ath_softc *sc, struct sample_softc *ssc) { struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "smoothing_rate", CTLTYPE_INT | CTLFLAG_RW, ssc, 0, ath_rate_sysctl_smoothing_rate, "I", "sample: smoothing rate for avg tx time (%%)"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "sample_rate", CTLTYPE_INT | CTLFLAG_RW, ssc, 0, ath_rate_sysctl_sample_rate, "I", "sample: percent air time devoted to sampling new rates (%%)"); /* XXX max_successive_failures, stale_failure_timeout, min_switch */ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "sample_stats", CTLTYPE_INT | CTLFLAG_RW, sc, 0, ath_rate_sysctl_stats, "I", "sample: print statistics"); } struct ath_ratectrl * ath_rate_attach(struct ath_softc *sc) { struct sample_softc *ssc; ssc = malloc(sizeof(struct sample_softc), M_DEVBUF, M_NOWAIT|M_ZERO); if (ssc == NULL) return NULL; ssc->arc.arc_space = sizeof(struct sample_node); 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*/ ssc->stale_failure_timeout = 10 * hz; /* 10 seconds */ ssc->min_switch = hz; /* 1 second */ ath_rate_sysctlattach(sc, ssc); return &ssc->arc; } void ath_rate_detach(struct ath_ratectrl *arc) { struct sample_softc *ssc = (struct sample_softc *) arc; free(ssc, M_DEVBUF); } Index: head/sys/dev/ath/if_ath.c =================================================================== --- head/sys/dev/ath/if_ath.c (revision 318855) +++ head/sys/dev/ath/if_ath.c (revision 318856) @@ -1,6996 +1,6999 @@ /*- * 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$"); /* * 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 #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 * beacons and also (currently) the number of vap's that can * have unique mac addresses/bssid. When staggering beacons * 4 is probably a good max as otherwise the beacons become * very closely spaced and there is limited time for cab q traffic * to go out. You can burst beacons instead but that is not good * for stations in power save and at some point you really want * another radio (and channel). * * The limit on the number of mac addresses is tied to our use of * the U/L bit and tracking addresses in a byte; it would be * worthwhile to allow more for applications like proxy sta. */ CTASSERT(ATH_BCBUF <= 8); static struct ieee80211vap *ath_vap_create(struct ieee80211com *, const char [IFNAMSIZ], int, enum ieee80211_opmode, int, const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN]); static void ath_vap_delete(struct ieee80211vap *); 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 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_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_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 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 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_txq_sched_tasklet(void *, int); static int ath_chan_set(struct ath_softc *, struct ieee80211_channel *); 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 int ath_set_quiet_ie(struct ieee80211_node *, uint8_t *); static void ath_calibrate(void *); static int ath_newstate(struct ieee80211vap *, enum ieee80211_state, int); static void ath_setup_stationkey(struct ieee80211_node *); static void ath_newassoc(struct ieee80211_node *, int); static int ath_setregdomain(struct ieee80211com *, struct ieee80211_regdomain *, int, struct ieee80211_channel []); static void ath_getradiocaps(struct ieee80211com *, int, int *, struct ieee80211_channel []); static int ath_getchannels(struct ath_softc *); static int ath_rate_setup(struct ath_softc *, u_int mode); 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 #include #endif SYSCTL_DECL(_hw_ath); /* XXX validate sysctl values */ static int ath_longcalinterval = 30; /* long cals every 30 secs */ SYSCTL_INT(_hw_ath, OID_AUTO, longcal, CTLFLAG_RW, &ath_longcalinterval, 0, "long chip calibration interval (secs)"); static int ath_shortcalinterval = 100; /* short cals every 100 ms */ SYSCTL_INT(_hw_ath, OID_AUTO, shortcal, CTLFLAG_RW, &ath_shortcalinterval, 0, "short chip calibration interval (msecs)"); static int ath_resetcalinterval = 20*60; /* reset cal state 20 mins */ SYSCTL_INT(_hw_ath, OID_AUTO, resetcal, CTLFLAG_RW, &ath_resetcalinterval, 0, "reset chip calibration results (secs)"); static int ath_anicalinterval = 100; /* ANI calibration - 100 msec */ SYSCTL_INT(_hw_ath, OID_AUTO, anical, CTLFLAG_RW, &ath_anicalinterval, 0, "ANI calibration (msecs)"); int ath_rxbuf = ATH_RXBUF; /* # rx buffers to allocate */ SYSCTL_INT(_hw_ath, OID_AUTO, rxbuf, CTLFLAG_RWTUN, &ath_rxbuf, 0, "rx buffers allocated"); int ath_txbuf = ATH_TXBUF; /* # tx buffers to allocate */ SYSCTL_INT(_hw_ath, OID_AUTO, txbuf, CTLFLAG_RWTUN, &ath_txbuf, 0, "tx buffers allocated"); 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"); 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"); 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. * * If the chip is going into network sleep or power off, then * we will wait until all uses of the chip are done before * going into network sleep or power off. * * If the chip is being programmed full-awake, then immediately * program it full-awake so we can actually stay awake rather than * the chip potentially going to sleep underneath us. */ void _ath_power_setpower(struct ath_softc *sc, int power_state, int selfgen, const char *file, int line) { ATH_LOCK_ASSERT(sc); DPRINTF(sc, ATH_DEBUG_PWRSAVE, "%s: (%s:%d) state=%d, refcnt=%d, target=%d, cur=%d\n", __func__, file, line, power_state, sc->sc_powersave_refcnt, sc->sc_target_powerstate, sc->sc_cur_powerstate); sc->sc_target_powerstate = power_state; /* * Don't program the chip into network sleep if the chip * is being programmed elsewhere. * * However, if the chip is being programmed /awake/, force * the chip awake so we stay awake. */ if ((sc->sc_powersave_refcnt == 0 || power_state == HAL_PM_AWAKE) && 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 (selfgen && 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++; /* * Only do the power state change if we're not programming * it elsewhere. */ 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)); 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; } if (local_mac) return (0); return (-1); } #define HAL_MODE_HT20 (HAL_MODE_11NG_HT20 | HAL_MODE_11NA_HT20) #define HAL_MODE_HT40 \ (HAL_MODE_11NG_HT40PLUS | HAL_MODE_11NG_HT40MINUS | \ HAL_MODE_11NA_HT40PLUS | HAL_MODE_11NA_HT40MINUS) int ath_attach(u_int16_t devid, struct ath_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = NULL; HAL_STATUS status; int error = 0, i; u_int wmodes; int rx_chainmask, tx_chainmask; HAL_OPS_CONFIG ah_config; DPRINTF(sc, ATH_DEBUG_ANY, "%s: devid 0x%x\n", __func__, devid); ic->ic_softc = sc; ic->ic_name = device_get_nameunit(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, &ah_config, &status); if (ah == NULL) { device_printf(sc->sc_dev, "unable to attach hardware; HAL status %u\n", status); error = ENXIO; goto bad; } sc->sc_ah = ah; sc->sc_invalid = 0; /* ready to go, enable interrupt handling */ #ifdef ATH_DEBUG sc->sc_debug = ath_debug; #endif /* * Force the chip awake during setup, just to keep * the HAL/driver power tracking happy. * * There are some methods (eg ath_hal_setmac()) * that poke the hardware. */ ATH_LOCK(sc); ath_power_setpower(sc, HAL_PM_AWAKE, 1); ATH_UNLOCK(sc); /* * 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 * descriptor. MAC's that don't have support will * return false w/o doing anything. MAC's that do * support it will return true w/o doing anything. */ sc->sc_mrretry = ath_hal_setupxtxdesc(ah, NULL, 0,0, 0,0, 0,0); /* * Check if the device has hardware counters for PHY * errors. If so we need to enable the MIB interrupt * so we can act on stat triggers. */ if (ath_hal_hwphycounters(ah)) sc->sc_needmib = 1; /* * Get the hardware key cache size. */ sc->sc_keymax = ath_hal_keycachesize(ah); if (sc->sc_keymax > ATH_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; } /* * Reset the key cache since some parts do not * reset the contents on initial power up. */ for (i = 0; i < sc->sc_keymax; i++) ath_hal_keyreset(ah, i); /* * Collect the default channel list. */ error = ath_getchannels(sc); if (error != 0) goto bad; /* * Setup rate tables for all potential media types. */ ath_rate_setup(sc, IEEE80211_MODE_11A); ath_rate_setup(sc, IEEE80211_MODE_11B); ath_rate_setup(sc, IEEE80211_MODE_11G); ath_rate_setup(sc, IEEE80211_MODE_TURBO_A); ath_rate_setup(sc, IEEE80211_MODE_TURBO_G); ath_rate_setup(sc, IEEE80211_MODE_STURBO_A); ath_rate_setup(sc, IEEE80211_MODE_11NA); ath_rate_setup(sc, IEEE80211_MODE_11NG); ath_rate_setup(sc, IEEE80211_MODE_HALF); ath_rate_setup(sc, IEEE80211_MODE_QUARTER); /* NB: setup here so ath_rate_update is happy */ ath_setcurmode(sc, IEEE80211_MODE_11A); /* * Allocate TX descriptors and populate the lists. */ error = ath_desc_alloc(sc); if (error != 0) { 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); ATH_TXBUF_LOCK_INIT(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", device_get_nameunit(sc->sc_dev)); 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 * beacon frames and one data queue for each QoS * priority. Note that the hal handles resetting * these queues at the needed time. * * XXX PS-Poll */ sc->sc_bhalq = ath_beaconq_setup(sc); if (sc->sc_bhalq == (u_int) -1) { 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) { 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)) { device_printf(sc->sc_dev, "unable to setup xmit queue for %s traffic!\n", ieee80211_wme_acnames[WME_AC_BK]); error = EIO; goto bad2; } if (!ath_tx_setup(sc, WME_AC_BE, HAL_WME_AC_BE) || !ath_tx_setup(sc, WME_AC_VI, HAL_WME_AC_VI) || !ath_tx_setup(sc, WME_AC_VO, HAL_WME_AC_VO)) { /* * Not enough hardware tx queues to properly do WME; * just punt and assign them all to the same h/w queue. * We could do a better job of this if, for example, * we allocate queues when we switch from station to * AP mode. */ if (sc->sc_ac2q[WME_AC_VI] != NULL) ath_tx_cleanupq(sc, sc->sc_ac2q[WME_AC_VI]); if (sc->sc_ac2q[WME_AC_BE] != NULL) ath_tx_cleanupq(sc, sc->sc_ac2q[WME_AC_BE]); sc->sc_ac2q[WME_AC_BE] = sc->sc_ac2q[WME_AC_BK]; sc->sc_ac2q[WME_AC_VI] = sc->sc_ac2q[WME_AC_BK]; sc->sc_ac2q[WME_AC_VO] = sc->sc_ac2q[WME_AC_BK]; } /* * Attach the TX completion function. * * The non-EDMA chips may have some special case optimisations; * this method gives everyone a chance to attach cleanly. */ sc->sc_tx.xmit_attach_comp_func(sc); /* * Setup rate control. Some rate control modules * call back to change the anntena state so expose * the necessary entry points. * XXX maybe belongs in struct ath_ratectrl? */ sc->sc_setdefantenna = ath_setdefantenna; sc->sc_rc = ath_rate_attach(sc); if (sc->sc_rc == NULL) { error = EIO; goto bad2; } /* Attach DFS module */ if (! ath_dfs_attach(sc)) { 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; } /* 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, 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); ath_led_config(sc); ath_hal_setledstate(ah, HAL_LED_INIT); /* XXX not right but it's not used anywhere important */ ic->ic_phytype = IEEE80211_T_OFDM; ic->ic_opmode = IEEE80211_M_STA; ic->ic_caps = IEEE80211_C_STA /* station mode */ | IEEE80211_C_IBSS /* ibss, nee adhoc, mode */ | IEEE80211_C_HOSTAP /* hostap mode */ | IEEE80211_C_MONITOR /* monitor mode */ | IEEE80211_C_AHDEMO /* adhoc demo mode */ | IEEE80211_C_WDS /* 4-address traffic works */ | IEEE80211_C_MBSS /* mesh point link mode */ | 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 radar detection */ #endif | IEEE80211_C_PMGT /* Station side power mgmt */ | IEEE80211_C_SWSLEEP ; /* * Query the hal to figure out h/w crypto support. */ if (ath_hal_ciphersupported(ah, HAL_CIPHER_WEP)) ic->ic_cryptocaps |= IEEE80211_CRYPTO_WEP; if (ath_hal_ciphersupported(ah, HAL_CIPHER_AES_OCB)) ic->ic_cryptocaps |= IEEE80211_CRYPTO_AES_OCB; if (ath_hal_ciphersupported(ah, HAL_CIPHER_AES_CCM)) ic->ic_cryptocaps |= IEEE80211_CRYPTO_AES_CCM; if (ath_hal_ciphersupported(ah, HAL_CIPHER_CKIP)) ic->ic_cryptocaps |= IEEE80211_CRYPTO_CKIP; if (ath_hal_ciphersupported(ah, HAL_CIPHER_TKIP)) { ic->ic_cryptocaps |= IEEE80211_CRYPTO_TKIP; /* * Check if h/w does the MIC and/or whether the * separate key cache entries are required to * handle both tx+rx MIC keys. */ if (ath_hal_ciphersupported(ah, HAL_CIPHER_MIC)) ic->ic_cryptocaps |= IEEE80211_CRYPTO_TKIPMIC; /* * If the h/w supports storing tx+rx MIC keys * in one cache slot automatically enable use. */ if (ath_hal_hastkipsplit(ah) || !ath_hal_settkipsplit(ah, AH_FALSE)) sc->sc_splitmic = 1; /* * If the h/w can do TKIP MIC together with WME then * we use it; otherwise we force the MIC to be done * in software by the net80211 layer. */ if (ath_hal_haswmetkipmic(ah)) sc->sc_wmetkipmic = 1; } sc->sc_hasclrkey = ath_hal_ciphersupported(ah, HAL_CIPHER_CLR); /* * Check for multicast key search support. */ if (ath_hal_hasmcastkeysearch(sc->sc_ah) && !ath_hal_getmcastkeysearch(sc->sc_ah)) { ath_hal_setmcastkeysearch(sc->sc_ah, 1); } sc->sc_mcastkey = ath_hal_getmcastkeysearch(ah); /* * Mark key cache slots associated with global keys * as in use. If we knew TKIP was not to be used we * could leave the +32, +64, and +32+64 slots free. */ for (i = 0; i < IEEE80211_WEP_NKID; i++) { setbit(sc->sc_keymap, i); setbit(sc->sc_keymap, i+64); if (sc->sc_splitmic) { setbit(sc->sc_keymap, i+32); setbit(sc->sc_keymap, i+32+64); } } /* * TPC support can be done either with a global cap or * per-packet support. The latter is not available on * all parts. We're a bit pedantic here as all parts * support a global cap. */ if (ath_hal_hastpc(ah) || ath_hal_hastxpowlimit(ah)) ic->ic_caps |= IEEE80211_C_TXPMGT; /* * Mark WME capability only if we have sufficient * hardware queues to do proper priority scheduling. */ if (sc->sc_ac2q[WME_AC_BE] != sc->sc_ac2q[WME_AC_BK]) ic->ic_caps |= IEEE80211_C_WME; /* * Check for misc other capabilities. */ if (ath_hal_hasbursting(ah)) ic->ic_caps |= IEEE80211_C_BURST; sc->sc_hasbmask = ath_hal_hasbssidmask(ah); sc->sc_hasbmatch = ath_hal_hasbssidmatch(ah); sc->sc_hastsfadd = ath_hal_hastsfadjust(ah); sc->sc_rxslink = ath_hal_self_linked_final_rxdesc(ah); /* XXX TODO: just make this a "store tx/rx timestamp length" operation */ if (ath_hal_get_rx_tsf_prec(ah, &i)) { if (i == 32) { sc->sc_rxtsf32 = 1; } if (bootverbose) device_printf(sc->sc_dev, "RX timestamp: %d bits\n", i); } if (ath_hal_get_tx_tsf_prec(ah, &i)) { if (bootverbose) device_printf(sc->sc_dev, "TX timestamp: %d bits\n", i); } sc->sc_hasenforcetxop = ath_hal_hasenforcetxop(ah); sc->sc_rx_lnamixer = ath_hal_hasrxlnamixer(ah); sc->sc_hasdivcomb = ath_hal_hasdivantcomb(ah); /* * 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. */ /* * XXX TODO: check reference driver and ath9k for what to do * here for WB335. I think we have to actually disable the * LNA div processing in the HAL and instead use the hard * coded values; and then use BT diversity. * * .. but also need to setup MCI too for WB335.. */ #if 0 if (sc->sc_pci_devinfo & (ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_AR9565_2ANT)) { device_printf(sc->sc_dev, "%s: WB335: disabling LNA mixer diversity\n", __func__); sc->sc_dolnadiv = 0; } #endif if (ath_hal_hasfastframes(ah)) ic->ic_caps |= IEEE80211_C_FF; wmodes = ath_hal_getwirelessmodes(ah); if (wmodes & (HAL_MODE_108G|HAL_MODE_TURBO)) ic->ic_caps |= IEEE80211_C_TURBOP; #ifdef IEEE80211_SUPPORT_TDMA if (ath_hal_macversion(ah) > 0x78) { ic->ic_caps |= IEEE80211_C_TDMA; /* capable of TDMA */ ic->ic_tdma_update = ath_tdma_update; } #endif /* * 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; /* * Shorten this to 64 packets, or 1/4 ath_txbuf, whichever * is smaller. * * Anything bigger can potentially see the cabq consume * almost all buffers, starving everything else, only to * see most fail to transmit in the given beacon interval. */ sc->sc_txq_mcastq_maxdepth = MIN(64, ath_txbuf / 4); /* * How deep can the node software TX queue get whilst it's asleep. */ sc->sc_txq_node_psq_maxdepth = 16; /* * Default the maximum queue to to 1/4'th the TX buffers, or * 64, whichever is smaller. */ sc->sc_txq_node_maxdepth = MIN(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 /* * Query HT capabilities */ if (ath_hal_getcapability(ah, HAL_CAP_HT, 0, NULL) == HAL_OK && (wmodes & (HAL_MODE_HT20 | HAL_MODE_HT40))) { uint32_t rxs, txs; uint32_t ldpc; device_printf(sc->sc_dev, "[HT] enabling HT modes\n"); 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 * advertises support. * Notably, anything earlier than the AR9287 doesn't. */ if ((ath_hal_getcapability(ah, HAL_CAP_HT20_SGI, 0, NULL) == HAL_OK) && (wmodes & HAL_MODE_HT20)) { device_printf(sc->sc_dev, "[HT] enabling short-GI in 20MHz mode\n"); ic->ic_htcaps |= IEEE80211_HTCAP_SHORTGI20; } if (wmodes & HAL_MODE_HT40) ic->ic_htcaps |= IEEE80211_HTCAP_CHWIDTH40 | IEEE80211_HTCAP_SHORTGI40; /* * 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, &txs); (void) ath_hal_getcapability(ah, HAL_CAP_STREAMS, 1, &rxs); ic->ic_txstream = txs; ic->ic_rxstream = rxs; /* * 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 | IEEE80211_HTC_TXLDPC; } 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. */ ic->ic_flags |= IEEE80211_F_DATAPAD; /* * Query the hal about antenna support. */ sc->sc_defant = ath_hal_getdefantenna(ah); /* * Not all chips have the VEOL support we want to * use with IBSS beacons; check here for it. */ sc->sc_hasveol = ath_hal_hasveol(ah); /* 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); 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; ic->ic_vap_create = ath_vap_create; ic->ic_vap_delete = ath_vap_delete; ic->ic_raw_xmit = ath_raw_xmit; ic->ic_update_mcast = ath_update_mcast; ic->ic_update_promisc = ath_update_promisc; 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 */ ic->ic_set_quiet = ath_set_quiet_ie; #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 * regdomain are available from the hal. */ ath_sysctlattach(sc); ath_sysctl_stats_attach(sc); ath_sysctl_hal_attach(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, 1); 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); sc->sc_invalid = 1; return error; } int ath_detach(struct ath_softc *sc) { /* * NB: the order of these is important: * o stop the chip so no more interrupts will fire * o call the 802.11 layer before detaching the hal to * insure callbacks into the driver to delete global * key cache entries can be handled * o free the taskqueue which drains any pending tasks * o reclaim the tx queue data structures after calling * the 802.11 layer as we'll get called back to reclaim * node state and potentially want to use them * o to cleanup the tx queues the hal is called, so detach * it last * Other than that, it's straightforward... */ /* * 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, 1); /* * 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 */ return 0; } /* * MAC address handling for multiple BSS on the same radio. * The first vap uses the MAC address from the EEPROM. For * subsequent vap's we set the U/L bit (bit 1) in the MAC * address and use the next six bits as an index. */ static void assign_address(struct ath_softc *sc, uint8_t mac[IEEE80211_ADDR_LEN], int clone) { int i; if (clone && sc->sc_hasbmask) { /* NB: we only do this if h/w supports multiple bssid */ for (i = 0; i < 8; i++) if ((sc->sc_bssidmask & (1<sc_bssidmask |= 1<sc_hwbssidmask[0] &= ~mac[0]; if (i == 0) sc->sc_nbssid0++; } static void reclaim_address(struct ath_softc *sc, const uint8_t mac[IEEE80211_ADDR_LEN]) { int i = mac[0] >> 2; uint8_t mask; if (i != 0 || --sc->sc_nbssid0 == 0) { sc->sc_bssidmask &= ~(1<sc_bssidmask & (1<sc_hwbssidmask[0] |= mask; } } /* * Assign a beacon xmit slot. We try to space out * assignments so when beacons are staggered the * traffic coming out of the cab q has maximal time * to go out before the next beacon is scheduled. */ static int assign_bslot(struct ath_softc *sc) { u_int slot, free; free = 0; for (slot = 0; slot < ATH_BCBUF; slot++) if (sc->sc_bslot[slot] == NULL) { if (sc->sc_bslot[(slot+1)%ATH_BCBUF] == NULL && sc->sc_bslot[(slot-1)%ATH_BCBUF] == NULL) return slot; free = slot; /* NB: keep looking for a double slot */ } return free; } static struct ieee80211vap * ath_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, enum ieee80211_opmode opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac0[IEEE80211_ADDR_LEN]) { 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 = malloc(sizeof(struct ath_vap), M_80211_VAP, M_WAITOK | M_ZERO); needbeacon = 0; IEEE80211_ADDR_COPY(mac, mac0); ATH_LOCK(sc); ic_opmode = opmode; /* default to opmode of new vap */ switch (opmode) { case IEEE80211_M_STA: if (sc->sc_nstavaps != 0) { /* XXX only 1 for now */ device_printf(sc->sc_dev, "only 1 sta vap supported\n"); goto bad; } if (sc->sc_nvaps) { /* * With multiple vaps we must fall back * to s/w beacon miss handling. */ flags |= IEEE80211_CLONE_NOBEACONS; } if (flags & IEEE80211_CLONE_NOBEACONS) { /* * Station mode w/o beacons are implemented w/ AP mode. */ ic_opmode = IEEE80211_M_HOSTAP; } break; case IEEE80211_M_IBSS: if (sc->sc_nvaps != 0) { /* XXX only 1 for now */ device_printf(sc->sc_dev, "only 1 ibss vap supported\n"); goto bad; } needbeacon = 1; break; case IEEE80211_M_AHDEMO: #ifdef IEEE80211_SUPPORT_TDMA if (flags & IEEE80211_CLONE_TDMA) { if (sc->sc_nvaps != 0) { device_printf(sc->sc_dev, "only 1 tdma vap supported\n"); goto bad; } needbeacon = 1; flags |= IEEE80211_CLONE_NOBEACONS; } /* fall thru... */ #endif case IEEE80211_M_MONITOR: if (sc->sc_nvaps != 0 && ic->ic_opmode != opmode) { /* * Adopt existing mode. Adding a monitor or ahdemo * vap to an existing configuration is of dubious * value but should be ok. */ /* XXX not right for monitor mode */ ic_opmode = ic->ic_opmode; } break; case IEEE80211_M_HOSTAP: case IEEE80211_M_MBSS: needbeacon = 1; break; case IEEE80211_M_WDS: if (sc->sc_nvaps != 0 && ic->ic_opmode == IEEE80211_M_STA) { device_printf(sc->sc_dev, "wds not supported in sta mode\n"); goto bad; } /* * Silently remove any request for a unique * bssid; WDS vap's always share the local * mac address. */ flags &= ~IEEE80211_CLONE_BSSID; if (sc->sc_nvaps == 0) ic_opmode = IEEE80211_M_HOSTAP; else ic_opmode = ic->ic_opmode; break; default: device_printf(sc->sc_dev, "unknown opmode %d\n", opmode); goto bad; } /* * Check that a beacon buffer is available; the code below assumes it. */ if (needbeacon & TAILQ_EMPTY(&sc->sc_bbuf)) { device_printf(sc->sc_dev, "no beacon buffer available\n"); goto bad; } /* STA, AHDEMO? */ if (opmode == IEEE80211_M_HOSTAP || opmode == IEEE80211_M_MBSS) { assign_address(sc, mac, flags & IEEE80211_CLONE_BSSID); ath_hal_setbssidmask(sc->sc_ah, sc->sc_hwbssidmask); } 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); ATH_LOCK(sc); if (error != 0) { device_printf(sc->sc_dev, "%s: error %d creating vap\n", __func__, error); goto bad2; } /* h/w crypto support */ vap->iv_key_alloc = ath_key_alloc; vap->iv_key_delete = ath_key_delete; vap->iv_key_set = ath_key_set; vap->iv_key_update_begin = ath_key_update_begin; vap->iv_key_update_end = ath_key_update_end; /* override various methods */ avp->av_recv_mgmt = vap->iv_recv_mgmt; vap->iv_recv_mgmt = ath_recv_mgmt; vap->iv_reset = ath_reset_vap; vap->iv_update_beacon = ath_beacon_update; avp->av_newstate = vap->iv_newstate; vap->iv_newstate = ath_newstate; 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 */ /* * Anything earlier than some AR9300 series MACs don't * support a smaller MPDU density. */ vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_8; /* * All NICs can handle the maximum size, however * AR5416 based MACs can only TX aggregates w/ RTS * protection when the total aggregate size is <= 8k. * However, for now that's enforced by the TX path. */ vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_64K; vap->iv_ampdu_limit = IEEE80211_HTCAP_MAXRXAMPDU_64K; avp->av_bslot = -1; if (needbeacon) { /* * Allocate beacon state and setup the q for buffered * multicast frames. We know a beacon buffer is * available because we checked above. */ 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 * this cannot fail to find a free one. */ avp->av_bslot = assign_bslot(sc); KASSERT(sc->sc_bslot[avp->av_bslot] == NULL, ("beacon slot %u not empty", avp->av_bslot)); sc->sc_bslot[avp->av_bslot] = vap; sc->sc_nbcnvaps++; } if (sc->sc_hastsfadd && sc->sc_nbcnvaps > 0) { /* * Multple vaps are to transmit beacons and we * have h/w support for TSF adjusting; enable * use of staggered beacons. */ sc->sc_stagbeacons = 1; } ath_txq_init(sc, &avp->av_mcastq, ATH_TXQ_SWQ); } ic->ic_opmode = ic_opmode; if (opmode != IEEE80211_M_WDS) { sc->sc_nvaps++; if (opmode == IEEE80211_M_STA) sc->sc_nstavaps++; if (opmode == IEEE80211_M_MBSS) sc->sc_nmeshvaps++; } switch (ic_opmode) { case IEEE80211_M_IBSS: sc->sc_opmode = HAL_M_IBSS; break; case IEEE80211_M_STA: sc->sc_opmode = HAL_M_STA; break; case IEEE80211_M_AHDEMO: #ifdef IEEE80211_SUPPORT_TDMA if (vap->iv_caps & IEEE80211_C_TDMA) { sc->sc_tdma = 1; /* NB: disable tsf adjust */ sc->sc_stagbeacons = 0; } /* * NB: adhoc demo mode is a pseudo mode; to the hal it's * just ap mode. */ /* fall thru... */ #endif case IEEE80211_M_HOSTAP: case IEEE80211_M_MBSS: sc->sc_opmode = HAL_M_HOSTAP; break; case IEEE80211_M_MONITOR: sc->sc_opmode = HAL_M_MONITOR; break; default: /* XXX should not happen */ break; } if (sc->sc_hastsfadd) { /* * Configure whether or not TSF adjust should be done. */ ath_hal_settsfadjust(sc->sc_ah, sc->sc_stagbeacons); } if (flags & IEEE80211_CLONE_NOBEACONS) { /* * Enable s/w beacon miss handling. */ sc->sc_swbmiss = 1; } ATH_UNLOCK(sc); /* complete setup */ ieee80211_vap_attach(vap, ath_media_change, ieee80211_media_status, mac); return vap; bad2: reclaim_address(sc, mac); ath_hal_setbssidmask(sc->sc_ah, sc->sc_hwbssidmask); bad: free(avp, M_80211_VAP); ATH_UNLOCK(sc); return NULL; } static void ath_vap_delete(struct ieee80211vap *vap) { struct ieee80211com *ic = vap->iv_ic; struct ath_softc *sc = ic->ic_softc; struct ath_hal *ah = sc->sc_ah; struct ath_vap *avp = ATH_VAP(vap); 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 */ /* 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 * the vap instance is reclaimed as we may have a reference * to it in the buffer for the beacon frame. */ if (avp->av_bcbuf != NULL) { if (avp->av_bslot != -1) { sc->sc_bslot[avp->av_bslot] = NULL; sc->sc_nbcnvaps--; } ath_beacon_return(sc, avp->av_bcbuf); avp->av_bcbuf = NULL; if (sc->sc_nbcnvaps == 0) { sc->sc_stagbeacons = 0; if (sc->sc_hastsfadd) ath_hal_settsfadjust(sc->sc_ah, 0); } /* * Reclaim any pending mcast frames for the vap. */ ath_tx_draintxq(sc, &avp->av_mcastq); } /* * Update bookkeeping. */ if (vap->iv_opmode == IEEE80211_M_STA) { sc->sc_nstavaps--; if (sc->sc_nstavaps == 0 && sc->sc_swbmiss) sc->sc_swbmiss = 0; } else if (vap->iv_opmode == IEEE80211_M_HOSTAP || vap->iv_opmode == IEEE80211_M_MBSS) { reclaim_address(sc, vap->iv_myaddr); ath_hal_setbssidmask(ah, sc->sc_hwbssidmask); if (vap->iv_opmode == IEEE80211_M_MBSS) sc->sc_nmeshvaps--; } if (vap->iv_opmode != IEEE80211_M_WDS) sc->sc_nvaps--; #ifdef IEEE80211_SUPPORT_TDMA /* TDMA operation ceases when the last vap is destroyed */ if (sc->sc_tdma && sc->sc_nvaps == 0) { sc->sc_tdma = 0; sc->sc_swbmiss = 0; } #endif free(avp, M_80211_VAP); 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) 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) ath_tdma_config(sc, NULL); else #endif ath_beacon_config(sc, NULL); } 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 ieee80211com *ic = &sc->sc_ic; sc->sc_resume_up = ic->ic_nrunning != 0; 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); } /* * Reset the key cache since some parts do not reset the * contents on resume. First we clear all entries, then * re-load keys that the 802.11 layer assumes are setup * in h/w. */ static void ath_reset_keycache(struct ath_softc *sc) { 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 ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = sc->sc_ah; HAL_STATUS status; 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, 1); ATH_UNLOCK(sc); ath_hal_reset(ah, sc->sc_opmode, sc->sc_curchan != NULL ? sc->sc_curchan : ic->ic_curchan, 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); /* 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 ? */ } void ath_shutdown(struct ath_softc *sc) { ATH_LOCK(sc); ath_stop(sc); ATH_UNLOCK(sc); /* NB: no point powering down chip as we're about to reboot */ } /* * Interrupt handler. Most of the actual processing is deferred. */ void ath_intr(void *arg) { struct ath_softc *sc = arg; 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 (sc->sc_invalid) { /* * The hardware is not ready/present, don't touch anything. * 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 */ ATH_PCU_UNLOCK(sc); return; } 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: 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 * bits we haven't explicitly enabled so we mask the * value to insure we only process bits we requested. */ ath_hal_getisr(ah, &status); /* NB: clears ISR too */ 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) { 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 */ taskqueue_enqueue(sc->sc_tq, &sc->sc_fataltask); } else { if (status & HAL_INT_SWBA) { /* * Software beacon alert--time to send a beacon. * Handle beacon transmission directly; deferring * this is too slow to meet timing constraints * under load. */ #ifdef IEEE80211_SUPPORT_TDMA if (sc->sc_tdma) { if (sc->sc_tdmaswba == 0) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); ath_tdma_beacon_send(sc, vap); sc->sc_tdmaswba = vap->iv_tdma->tdma_bintval; } else sc->sc_tdmaswba--; } else #endif { ath_beacon_proc(sc, 0); #ifdef IEEE80211_SUPPORT_SUPERG /* * Schedule the rx taskq in case there's no * traffic so any frames held on the staging * queue are aged and potentially flushed. */ sc->sc_rx.recv_sched(sc, 1); #endif } } if (status & HAL_INT_RXEOL) { 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); } /* * Enqueue an RX proc to handle whatever * is in the RX queue. * This will then kick the PCU if required. */ 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); } /* * 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); } if (status & HAL_INT_GTT) sc->sc_stats.ast_tx_timeout++; if (status & HAL_INT_CST) 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. */ ath_hal_intrset(ah, 0); /* * Let the hal handle the event. We assume it will * clear whatever condition caused the interrupt. */ ath_hal_mibevent(ah, &sc->sc_halstats); /* * 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) { /* out of range beacon - wake the chip up, * but don't modify self-gen frame config */ device_printf(sc->sc_dev, "%s: TSFOOR\n", __func__); sc->sc_syncbeacon = 1; ATH_LOCK(sc); ath_power_setpower(sc, HAL_PM_AWAKE, 0); ATH_UNLOCK(sc); } 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; u_int32_t *state; u_int32_t len; void *sp; 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 * the hal so we can diagnose what's going on. */ if (ath_hal_getfatalstate(sc->sc_ah, &sp, &len)) { KASSERT(len >= 6*sizeof(u_int32_t), ("len %u bytes", len)); state = sp; 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(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 * beacon miss interval then ignore the interrupt. If it's * truly a bmiss we'll get another interrupt soon and that'll * 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) { 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; DPRINTF(sc, ATH_DEBUG_BEACON, "%s: tsf %llu lastrx %lld (%llu) bmiss %u\n", __func__, (unsigned long long) tsf, (unsigned long long)(tsf - lastrx), (unsigned long long) lastrx, bmisstimeout); if (tsf - lastrx <= bmisstimeout) { sc->sc_stats.ast_bmiss_phantom++; ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); return; } } /* * Keep the hardware awake if it's asleep (and leave self-gen * frame config alone) until the next beacon, so we can resync * against the next beacon. * * This handles three common beacon miss cases in STA powersave mode - * (a) the beacon TBTT isnt a multiple of bintval; * (b) the beacon was missed; and * (c) the beacons are being delayed because the AP is busy and * isn't reliably able to meet its TBTT. */ ATH_LOCK(sc); ath_power_setpower(sc, HAL_PM_AWAKE, 0); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); DPRINTF(sc, ATH_DEBUG_BEACON, "%s: forced awake; force syncbeacon=1\n", __func__); /* * Attempt to force a beacon resync. */ sc->sc_syncbeacon = 1; ATH_VAP(vap)->av_bmiss(vap); } /* XXX this needs a force wakeup! */ int ath_hal_gethangstate(struct ath_hal *ah, uint32_t mask, uint32_t *hangs) { uint32_t rsize; void *sp; if (!ath_hal_getdiagstate(ah, HAL_DIAG_CHECK_HANGS, &mask, sizeof(mask), &sp, &rsize)) return 0; KASSERT(rsize == sizeof(uint32_t), ("resultsize %u", rsize)); *hangs = *(uint32_t *)sp; return 1; } static void ath_bmiss_proc(void *arg, int pending) { struct ath_softc *sc = arg; 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) { 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); } /* * Handle TKIP MIC setup to deal hardware that doesn't do MIC * calcs together with WME. If necessary disable the crypto * hardware and mark the 802.11 state so keys will be setup * with the MIC work done in software. */ static void ath_settkipmic(struct ath_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; if ((ic->ic_cryptocaps & IEEE80211_CRYPTO_TKIP) && !sc->sc_wmetkipmic) { if (ic->ic_flags & IEEE80211_F_WME) { ath_hal_settkipmic(sc->sc_ah, AH_FALSE); ic->ic_cryptocaps &= ~IEEE80211_CRYPTO_TKIPMIC; } else { ath_hal_settkipmic(sc->sc_ah, AH_TRUE); ic->ic_cryptocaps |= IEEE80211_CRYPTO_TKIPMIC; } } } static void ath_vap_clear_quiet_ie(struct ath_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap; struct ath_vap *avp; TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { avp = ATH_VAP(vap); /* Quiet time handling - ensure we resync */ memset(&avp->quiet_ie, 0, sizeof(avp->quiet_ie)); } } static int ath_init(struct ath_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = sc->sc_ah; HAL_STATUS status; 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, 1); /* * Stop anything previously setup. This is safe * whether this is the first time through or not. */ ath_stop(sc); /* * The basic interface to setting the hardware in a good * state is ``reset''. On return the hardware is known to * be powered up and with interrupts disabled. This must * be followed by initialization of the appropriate bits * and then setup of the interrupt mask. */ ath_settkipmic(sc); 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); /* Clear quiet IE state for each VAP */ ath_vap_clear_quiet_ie(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 = ticks; sc->sc_resetcal = 1; sc->sc_lastcalreset = 0; sc->sc_lastani = ticks; sc->sc_lastshortcal = ticks; sc->sc_doresetcal = AH_FALSE; /* * Beacon timers were cleared here; give ath_newstate() * a hint that the beacon timers should be poked when * things transition to the RUN state. */ sc->sc_beacons = 0; /* * Setup the hardware after reset: the key cache * is filled as needed and the receive engine is * set going. Frame transmit is handled entirely * in the frame output path; there's nothing to do * here except setup the interrupt mask. */ if (ath_startrecv(sc) != 0) { 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_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. */ 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; DPRINTF(sc, ATH_DEBUG_RESET, "%s: imask=0x%x\n", __func__, sc->sc_imask); sc->sc_running = 1; callout_reset(&sc->sc_wd_ch, hz, ath_watchdog, sc); ath_hal_intrset(ah, sc->sc_imask); ath_power_restore_power_state(sc); return (0); } static void ath_stop(struct ath_softc *sc) { struct ath_hal *ah = sc->sc_ah; ATH_LOCK_ASSERT(sc); /* * 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 * turn off timers * disable interrupts * turn off the radio * clear transmit machinery * clear receive machinery * drain and release tx queues * reclaim beacon resources * power down hardware * * Note that some of this work is not possible if the * hardware is gone (invalid). */ #ifdef ATH_TX99_DIAG if (sc->sc_tx99 != NULL) sc->sc_tx99->stop(sc->sc_tx99); #endif callout_stop(&sc->sc_wd_ch); sc->sc_wd_timer = 0; sc->sc_running = 0; if (!sc->sc_invalid) { if (sc->sc_softled) { callout_stop(&sc->sc_ledtimer); ath_hal_gpioset(ah, sc->sc_ledpin, !sc->sc_ledon); sc->sc_blinking = 0; } ath_hal_intrset(ah, 0); } /* XXX we should stop RX regardless of whether it's valid */ if (!sc->sc_invalid) { 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_txrx_start(struct ath_softc *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, * followed by state transitions to the current 802.11 * operational state. Used to recover from various errors and * to reset or reload hardware state. */ int ath_reset(struct ath_softc *sc, ATH_RESET_TYPE reset_type) { 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_settkipmic(sc); /* configure TKIP MIC handling */ /* NB: indicate channel change so we do a full reset */ 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); /* Quiet time handling - ensure we resync */ ath_vap_clear_quiet_ie(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 */ 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 * might change as a result. */ ath_chan_change(sc, ic->ic_curchan); if (sc->sc_beacons) { /* restart beacons */ #ifdef IEEE80211_SUPPORT_TDMA if (sc->sc_tdma) ath_tdma_config(sc, NULL); else #endif ath_beacon_config(sc, NULL); } /* * 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; } static int ath_reset_vap(struct ieee80211vap *vap, u_long cmd) { struct ieee80211com *ic = vap->iv_ic; struct ath_softc *sc = ic->ic_softc; struct ath_hal *ah = sc->sc_ah; switch (cmd) { case IEEE80211_IOC_TXPOWER: /* * If per-packet TPC is enabled, then we have nothing * to do; otherwise we need to force the global limit. * All this can happen directly; no need to reset. */ if (!ath_hal_gettpc(ah)) ath_hal_settxpowlimit(ah, ic->ic_txpowlimit); return 0; } /* XXX? Full or NOLOSS? */ return ath_reset(sc, ATH_RESET_FULL); } struct ath_buf * _ath_getbuf_locked(struct ath_softc *sc, ath_buf_type_t btype) { struct ath_buf *bf; ATH_TXBUF_LOCK_ASSERT(sc); if (btype == ATH_BUFTYPE_MGMT) bf = TAILQ_FIRST(&sc->sc_txbuf_mgmt); else bf = TAILQ_FIRST(&sc->sc_txbuf); if (bf == NULL) { 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_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, 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) { DPRINTF(sc, ATH_DEBUG_XMIT, "%s: stop queue\n", __func__); sc->sc_stats.ast_tx_qstop++; } return bf; } /* * 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 = ic->ic_softc; struct ieee80211_node *ni; struct mbuf *next; struct ath_buf *bf; ath_bufhead frags; int retval = 0; /* * 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 ath_media_change(struct ifnet *ifp) { int error = ieee80211_media_change(ifp); /* NB: only the fixed rate can change and that doesn't need a reset */ return (error == ENETRESET ? 0 : error); } /* * Block/unblock tx+rx processing while a key change is done. * We assume the caller serializes key management operations * so we only need to worry about synchronization with other * uses that originate in the driver. */ static void ath_key_update_begin(struct ieee80211vap *vap) { struct ath_softc *sc = vap->iv_ic->ic_softc; DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__); taskqueue_block(sc->sc_tq); } static void ath_key_update_end(struct ieee80211vap *vap) { struct ath_softc *sc = vap->iv_ic->ic_softc; DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__); taskqueue_unblock(sc->sc_tq); } static void ath_update_promisc(struct ieee80211com *ic) { 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_hw(struct ath_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; u_int32_t mfilt[2]; /* calculate and install multicast filter */ 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; 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 = 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); } } 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 ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = sc->sc_ah; u_int32_t rfilt; /* XXX power state? */ /* configure rx filter */ rfilt = ath_calcrxfilter(sc); ath_hal_setrxfilter(ah, rfilt); /* configure operational mode */ ath_hal_setopmode(ah); /* handle any link-level address change */ ath_hal_setmac(ah, ic->ic_macaddr); /* calculate and install multicast filter */ ath_update_mcast_hw(sc); } /* * Set the slot time based on the current setting. */ void ath_setslottime(struct ath_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = sc->sc_ah; u_int usec; if (IEEE80211_IS_CHAN_HALF(ic->ic_curchan)) usec = 13; else if (IEEE80211_IS_CHAN_QUARTER(ic->ic_curchan)) usec = 21; else if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) { /* honor short/long slot time only in 11g */ /* XXX shouldn't honor on pure g or turbo g channel */ if (ic->ic_flags & IEEE80211_F_SHSLOT) usec = HAL_SLOT_TIME_9; else usec = HAL_SLOT_TIME_20; } else usec = HAL_SLOT_TIME_9; DPRINTF(sc, ATH_DEBUG_RESET, "%s: chan %u MHz flags 0x%x %s slot, %u usec\n", __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); } /* * Callback from the 802.11 layer to update the * slot time based on the current setting. */ static void ath_updateslot(struct ieee80211com *ic) { 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) sc->sc_updateslot = UPDATE; else ath_setslottime(sc); } /* * Append the contents of src to dst; both queues * are assumed to be locked. */ void ath_txqmove(struct ath_txq *dst, struct ath_txq *src) { 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; } /* * Reset the hardware, with no loss. * * This can't be used for a general case reset. */ static void ath_reset_proc(void *arg, int pending) { struct ath_softc *sc = arg; #if 0 device_printf(sc->sc_dev, "%s: resetting\n", __func__); #endif ath_reset(sc, ATH_RESET_NOLOSS); } /* * Reset the hardware after detecting beacons have stopped. */ static void ath_bstuck_proc(void *arg, int pending) { struct ath_softc *sc = arg; uint32_t hangs = 0; if (ath_hal_gethangstate(sc->sc_ah, 0xff, &hangs) && hangs != 0) device_printf(sc->sc_dev, "bb hang detected (0x%x)\n", hangs); #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 device_printf(sc->sc_dev, "stuck beacon; resetting (bmiss count %u)\n", sc->sc_bmisscount); sc->sc_stats.ast_bstuck++; /* * This assumes that there's no simultaneous channel mode change * occurring. */ ath_reset(sc, ATH_RESET_NOLOSS); } static int ath_desc_alloc(struct ath_softc *sc) { int error; error = ath_descdma_setup(sc, &sc->sc_txdma, &sc->sc_txbuf, "tx", sc->sc_tx_desclen, ath_txbuf, ATH_MAX_SCATTER); if (error != 0) { 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", 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_txdma_mgmt, &sc->sc_txbuf_mgmt); return error; } return 0; } static void ath_desc_free(struct ath_softc *sc) { if (sc->sc_bdma.dd_desc_len != 0) 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_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_softc; const size_t space = sizeof(struct ath_node) + sc->sc_rc->arc_space; struct ath_node *an; an = malloc(space, M_80211_NODE, M_NOWAIT|M_ZERO); if (an == NULL) { /* XXX stat+msg */ return NULL; } ath_rate_node_init(sc, 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_softc; 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); } 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_softc; struct ath_hal *ah = sc->sc_ah; *rssi = ic->ic_node_getrssi(ni); if (ni->ni_chan != IEEE80211_CHAN_ANYC) *noise = ath_hal_getchannoise(ah, ni->ni_chan); else *noise = -95; /* nominally correct */ } /* * Set the default antenna. */ void ath_setdefantenna(struct ath_softc *sc, u_int antenna) { struct ath_hal *ah = sc->sc_ah; /* XXX block beacon interrupts */ ath_hal_setdefantenna(ah, antenna); if (sc->sc_defant != antenna) sc->sc_stats.ast_ant_defswitch++; sc->sc_defant = antenna; sc->sc_rxotherant = 0; } 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; 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); } /* * Setup a h/w transmit queue. */ static struct ath_txq * ath_txq_setup(struct ath_softc *sc, int qtype, int subtype) { struct ath_hal *ah = sc->sc_ah; HAL_TXQ_INFO qi; int qnum; memset(&qi, 0, sizeof(qi)); qi.tqi_subtype = subtype; qi.tqi_aifs = HAL_TXQ_USEDEFAULT; qi.tqi_cwmin = HAL_TXQ_USEDEFAULT; qi.tqi_cwmax = HAL_TXQ_USEDEFAULT; /* * Enable interrupts only for EOL and DESC conditions. * We mark tx descriptors to receive a DESC interrupt * when a tx queue gets deep; otherwise waiting for the * EOL to reap descriptors. Note that this is done to * reduce interrupt load and this only defers reaping * descriptors, never transmitting frames. Aside from * reducing interrupts this also permits more concurrency. * The only potential downside is if the tx queue backs * up in which case the top half of the kernel may backup * due to a lack of tx descriptors. */ 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) { /* * NB: don't print a message, this happens * normally on parts with too few tx queues */ return NULL; } if (qnum >= nitems(sc->sc_txq)) { device_printf(sc->sc_dev, "hal qnum %u out of range, max %zu!\n", qnum, nitems(sc->sc_txq)); ath_hal_releasetxqueue(ah, qnum); return NULL; } if (!ATH_TXQ_SETUP(sc, qnum)) { ath_txq_init(sc, &sc->sc_txq[qnum], qnum); sc->sc_txqsetup |= 1<sc_txq[qnum]; } /* * Setup a hardware data transmit queue for the specified * access control. The hal may not support all requested * queues in which case it will return a reference to a * previously setup queue. We record the mapping from ac's * to h/w queues for use by ath_tx_start and also track * the set of h/w queues being used to optimize work in the * transmit interrupt handler and related routines. */ static int ath_tx_setup(struct ath_softc *sc, int ac, int haltype) { struct ath_txq *txq; if (ac >= nitems(sc->sc_ac2q)) { device_printf(sc->sc_dev, "AC %u out of range, max %zu!\n", ac, nitems(sc->sc_ac2q)); return 0; } txq = ath_txq_setup(sc, HAL_TX_QUEUE_DATA, haltype); if (txq != NULL) { txq->axq_ac = ac; sc->sc_ac2q[ac] = txq; return 1; } else return 0; } /* * Update WME parameters for a transmit queue. */ static int ath_txq_update(struct ath_softc *sc, int ac) { #define ATH_EXPONENT_TO_VALUE(v) ((1<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; HAL_TXQ_INFO qi; ath_hal_gettxqueueprops(ah, txq->axq_qnum, &qi); #ifdef IEEE80211_SUPPORT_TDMA if (sc->sc_tdma) { /* * AIFS is zero so there's no pre-transmit wait. The * burst time defines the slot duration and is configured * through net80211. The QCU is setup to not do post-xmit * back off, lockout all lower-priority QCU's, and fire * off the DMA beacon alert timer which is setup based * on the slot configuration. */ qi.tqi_qflags = HAL_TXQ_TXOKINT_ENABLE | HAL_TXQ_TXERRINT_ENABLE | HAL_TXQ_TXURNINT_ENABLE | HAL_TXQ_TXEOLINT_ENABLE | HAL_TXQ_DBA_GATED | HAL_TXQ_BACKOFF_DISABLE | HAL_TXQ_ARB_LOCKOUT_GLOBAL ; qi.tqi_aifs = 0; /* XXX +dbaprep? */ qi.tqi_readyTime = sc->sc_tdmaslotlen; 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 = IEEE80211_TXOP_TO_US(wmep->wmep_txopLimit); #ifdef IEEE80211_SUPPORT_TDMA } #endif DPRINTF(sc, ATH_DEBUG_RESET, "%s: Q%u qflags 0x%x aifs %u cwmin %u cwmax %u burstTime %u\n", __func__, txq->axq_qnum, qi.tqi_qflags, qi.tqi_aifs, qi.tqi_cwmin, qi.tqi_cwmax, qi.tqi_burstTime); if (!ath_hal_settxqueueprops(ah, txq->axq_qnum, &qi)) { 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_EXPONENT_TO_VALUE } /* * Callback from the 802.11 layer to update WME parameters. */ int ath_wme_update(struct ieee80211com *ic) { struct ath_softc *sc = ic->ic_softc; return !ath_txq_update(sc, WME_AC_BE) || !ath_txq_update(sc, WME_AC_BK) || !ath_txq_update(sc, WME_AC_VI) || !ath_txq_update(sc, WME_AC_VO) ? EIO : 0; } /* * Reclaim resources for a setup queue. */ static void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq) { ath_hal_releasetxqueue(sc->sc_ah, txq->axq_qnum); sc->sc_txqsetup &= ~(1<axq_qnum); ATH_TXQ_LOCK_DESTROY(txq); } /* * Reclaim all tx queue resources. */ static void ath_tx_cleanup(struct ath_softc *sc) { int i; ATH_TXBUF_LOCK_DESTROY(sc); for (i = 0; i < HAL_NUM_TX_QUEUES; i++) if (ATH_TXQ_SETUP(sc, i)) ath_tx_cleanupq(sc, &sc->sc_txq[i]); } /* * Return h/w rate index for an IEEE rate (w/o basic rate bit) * using the current rates in sc_rixmap. */ int ath_tx_findrix(const struct ath_softc *sc, uint8_t rate) { int rix = sc->sc_rixmap[rate]; /* NB: return lowest rix for invalid 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++; /* XXX TODO: should do per-pri conuters */ 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, int dosched) { struct ath_hal *ah = sc->sc_ah; struct ath_buf *bf; struct ath_desc *ds; struct ath_tx_status *ts; struct ieee80211_node *ni; #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 = TAILQ_FIRST(&txq->axq_q); if (bf == NULL) { ATH_TXQ_UNLOCK(txq); break; } 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(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_last->bf_flags |= ATH_BUF_BUSY; } else txq->axq_link = NULL; #else bf->bf_last->bf_flags |= ATH_BUF_BUSY; #endif if (bf->bf_state.bfs_aggr) txq->axq_aggr_depth--; ni = bf->bf_node; 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); } ATH_TXQ_UNLOCK(txq); /* * Update statistics and call completion */ ath_tx_process_buf_completion(sc, txq, ts, bf); /* XXX at this point, bf and ni may be totally invalid */ } #ifdef IEEE80211_SUPPORT_SUPERG /* * Flush fast-frame staging queue when traffic slows. */ 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; } #define TXQACTIVE(t, q) ( (t) & (1 << (q))) /* * Deferred processing of transmit interrupt; special-cased * for a single hardware transmit queue (e.g. 5210 and 5211). */ static void ath_tx_proc_q0(void *arg, int npending) { struct ath_softc *sc = arg; 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_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(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_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); } /* * Deferred processing of transmit interrupt; special-cased * for four hardware queues, 0-3 (e.g. 5212 w/ WME support). */ static void ath_tx_proc_q0123(void *arg, int npending) { struct ath_softc *sc = arg; 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(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); sc->sc_wd_timer = 0; if (sc->sc_softled) ath_led_event(sc, sc->sc_txrix); 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); } /* * Deferred processing of transmit interrupt. */ static void ath_tx_proc(void *arg, int npending) { struct ath_softc *sc = arg; 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(txqs, i)) nacked += ath_tx_processq(sc, &sc->sc_txq[i], 1); if (nacked) sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah); sc->sc_wd_timer = 0; if (sc->sc_softled) ath_led_event(sc, sc->sc_txrix); 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 ath_buf *bf; u_int ix; /* * NB: this assumes output has been stopped and * we do not need to block ath_tx_proc */ for (ix = 0;; ix++) { ATH_TXQ_LOCK(txq); bf = ath_tx_draintxq_get_one(sc, txq); if (bf == NULL) { ATH_TXQ_UNLOCK(txq); break; } 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_ic; int status = 0; /* * 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 */ /* * 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; 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 ath_tx_stopdma(struct ath_softc *sc, struct ath_txq *txq) { struct ath_hal *ah = sc->sc_ah; 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), (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 } int ath_stoptxdma(struct ath_softc *sc) { struct ath_hal *ah = sc->sc_ah; 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); /* 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]); } } #ifdef ATH_DEBUG if (sc->sc_debug & ATH_DEBUG_RESET) { 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_lastds, &bf->bf_status.ds_txstat) == HAL_OK); ieee80211_dump_pkt(&sc->sc_ic, mtod(bf->bf_m, const uint8_t *), bf->bf_m->m_len, 0, -1); } } #endif /* ATH_DEBUG */ sc->sc_wd_timer = 0; } /* * Update internal state after a channel change. */ static void ath_chan_change(struct ath_softc *sc, struct ieee80211_channel *chan) { enum ieee80211_phymode mode; /* * Change channels and update the h/w rate map * if we're switching; e.g. 11a to 11b/g. */ mode = ieee80211_chan2mode(chan); if (mode != sc->sc_curmode) ath_setcurmode(sc, mode); sc->sc_curchan = chan; } /* * Set/change channels. If the channel is really being changed, * it's done by resetting the chip. To accomplish this we must * first cleanup any pending DMA, then restart stuff after a la * ath_init. */ static int ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan) { 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), chan->ic_freq, chan->ic_flags); if (chan != sc->sc_curchan) { HAL_STATUS status; /* * To switch channels clear any pending DMA operations; * wait long enough for the RX fifo to drain, reset the * 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 */ #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); 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); /* Quiet time handling - ensure we resync */ ath_vap_clear_quiet_ie(sc); /* Let DFS at it in case it's a DFS channel */ 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) { device_printf(sc->sc_dev, "%s: unable to restart recv logic\n", __func__); ret = EIO; goto finish; } /* * Change channels and update the h/w rate map * if we're switching; e.g. 11a to 11b/g. */ ath_chan_change(sc, chan); /* * Reset clears the beacon timers; reset them * here if needed. */ if (sc->sc_beacons) { /* restart beacons */ #ifdef IEEE80211_SUPPORT_TDMA if (sc->sc_tdma) ath_tdma_config(sc, NULL); else #endif ath_beacon_config(sc, NULL); } /* * Re-enable interrupts. */ #if 0 ath_hal_intrset(ah, sc->sc_imask); #endif } 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; } /* * Periodically recalibrate the PHY to account * for temperature/environment changes. */ static void ath_calibrate(void *arg) { struct ath_softc *sc = arg; struct ath_hal *ah = sc->sc_ah; 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); aniCal = (ticks - sc->sc_lastani >= ath_anicalinterval*hz/1000); if (sc->sc_doresetcal) shortCal = (ticks - sc->sc_lastshortcal >= ath_shortcalinterval*hz/1000); DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: shortCal=%d; longCal=%d; aniCal=%d\n", __func__, shortCal, longCal, aniCal); if (aniCal) { sc->sc_stats.ast_ani_cal++; sc->sc_lastani = ticks; ath_hal_ani_poll(ah, sc->sc_curchan); } if (longCal) { sc->sc_stats.ast_per_cal++; sc->sc_lastlongcal = ticks; if (ath_hal_getrfgain(ah) == HAL_RFGAIN_NEED_CHANGE) { /* * Rfgain is out of bounds, reset the chip * to load new gain values. */ DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: rfgain change\n", __func__); sc->sc_stats.ast_per_rfgain++; 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 * reset the data collection state so we start fresh. */ if (sc->sc_resetcal) { (void) ath_hal_calreset(ah, sc->sc_curchan); sc->sc_lastcalreset = ticks; sc->sc_lastshortcal = ticks; sc->sc_resetcal = 0; sc->sc_doresetcal = AH_TRUE; } } /* 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) { /* * Calibrate noise floor data again in case of change. */ ath_hal_process_noisefloor(ah); } } else { DPRINTF(sc, ATH_DEBUG_ANY, "%s: calibration of channel %u failed\n", __func__, sc->sc_curchan->ic_freq); sc->sc_stats.ast_per_calfail++; } if (shortCal) sc->sc_lastshortcal = ticks; } if (!isCalDone) { restart: /* * Use a shorter interval to potentially collect multiple * data samples required to complete calibration. Once * we're told the work is done we drop back to a longer * interval between requests. We're more aggressive doing * work when operating as an AP to improve operation right * after startup. */ sc->sc_lastshortcal = ticks; nextcal = ath_shortcalinterval*hz/1000; if (sc->sc_opmode != HAL_M_HOSTAP) nextcal *= 10; sc->sc_doresetcal = AH_TRUE; } else { /* nextcal should be the shortest time for next event */ nextcal = ath_longcalinterval*hz; if (sc->sc_lastcalreset == 0) sc->sc_lastcalreset = sc->sc_lastlongcal; else if (ticks - sc->sc_lastcalreset >= ath_resetcalinterval*hz) sc->sc_resetcal = 1; /* setup reset next trip */ sc->sc_doresetcal = AH_FALSE; } /* ANI calibration may occur more often than short/long/resetcal */ if (ath_anicalinterval > 0) nextcal = MIN(nextcal, ath_anicalinterval*hz/1000); if (nextcal != 0) { DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: next +%u (%sisCalDone)\n", __func__, nextcal, isCalDone ? "" : "!"); callout_reset(&sc->sc_cal_ch, nextcal, ath_calibrate, sc); } else { DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: calibration disabled\n", __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 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, 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(ieee80211broadcastaddr)); } static void ath_scan_end(struct ieee80211com *ic) { 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__); device_printf(sc->sc_dev, "%s: called\n", __func__); /* * XXX TODO: schedule a tasklet that stops things without freeing, * walks the now stopped TX queue(s) looking for frames to retry * as if we TX filtered them (whch may mean dropping non-ampdu frames!) * but okay) then place them back on the software queue so they * can have the rate control lookup done again. */ ath_set_channel(ic); } #endif /* ATH_ENABLE_11N */ /* * This is called by the beacon parsing routine in the receive * path to update the current quiet time information provided by * an AP. * * This is STA specific, it doesn't take the AP TBTT/beacon slot * offset into account. * * The quiet IE doesn't control the /now/ beacon interval - it * controls the upcoming beacon interval. So, when tbtt=1, * the quiet element programming shall be for the next beacon * interval. There's no tbtt=0 behaviour defined, so don't. * * Since we're programming the next quiet interval, we have * to keep in mind what we will see when the next beacon * is received with potentially a quiet IE. For example, if * quiet_period is 1, then we are always getting a quiet interval * each TBTT - so if we just program it in upon each beacon received, * it will constantly reflect the "next" TBTT and we will never * let the counter stay programmed correctly. * * So: * + the first time we see the quiet IE, program it and store * the details somewhere; * + if the quiet parameters don't change (ie, period/duration/offset) * then just leave the programming enabled; * + (we can "skip" beacons, so don't try to enforce tbttcount unless * you're willing to also do the skipped beacon math); * + if the quiet IE is removed, then halt quiet time. */ static int ath_set_quiet_ie(struct ieee80211_node *ni, uint8_t *ie) { struct ieee80211_quiet_ie *q; struct ieee80211vap *vap = ni->ni_vap; struct ath_vap *avp = ATH_VAP(vap); struct ieee80211com *ic = vap->iv_ic; struct ath_softc *sc = ic->ic_softc; if (vap->iv_opmode != IEEE80211_M_STA) return (0); /* Verify we have a quiet time IE */ if (ie == NULL) { DPRINTF(sc, ATH_DEBUG_QUIETIE, "%s: called; NULL IE, disabling\n", __func__); ath_hal_set_quiet(sc->sc_ah, 0, 0, 0, HAL_QUIET_DISABLE); memset(&avp->quiet_ie, 0, sizeof(avp->quiet_ie)); return (0); } /* If we do, verify it's actually legit */ if (ie[0] != IEEE80211_ELEMID_QUIET) return 0; if (ie[1] != 6) return 0; /* Note: this belongs in net80211, parsed out and everything */ q = (void *) ie; /* * Compare what we have stored to what we last saw. * If they're the same then don't program in anything. */ if ((q->period == avp->quiet_ie.period) && (le16dec(&q->duration) == le16dec(&avp->quiet_ie.duration)) && (le16dec(&q->offset) == le16dec(&avp->quiet_ie.offset))) return (0); DPRINTF(sc, ATH_DEBUG_QUIETIE, "%s: called; tbttcount=%d, period=%d, duration=%d, offset=%d\n", __func__, (int) q->tbttcount, (int) q->period, (int) le16dec(&q->duration), (int) le16dec(&q->offset)); /* * Don't program in garbage values. */ if ((le16dec(&q->duration) == 0) || (le16dec(&q->duration) >= ni->ni_intval)) { DPRINTF(sc, ATH_DEBUG_QUIETIE, "%s: invalid duration (%d)\n", __func__, le16dec(&q->duration)); return (0); } /* * Can have a 0 offset, but not a duration - so just check * they don't exceed the intval. */ if (le16dec(&q->duration) + le16dec(&q->offset) >= ni->ni_intval) { DPRINTF(sc, ATH_DEBUG_QUIETIE, "%s: invalid duration + offset (%d+%d)\n", __func__, le16dec(&q->duration), le16dec(&q->offset)); return (0); } if (q->tbttcount == 0) { DPRINTF(sc, ATH_DEBUG_QUIETIE, "%s: invalid tbttcount (0)\n", __func__); return (0); } if (q->period == 0) { DPRINTF(sc, ATH_DEBUG_QUIETIE, "%s: invalid period (0)\n", __func__); return (0); } /* * This is a new quiet time IE config, so wait until tbttcount * is equal to 1, and program it in. */ if (q->tbttcount == 1) { DPRINTF(sc, ATH_DEBUG_QUIETIE, "%s: programming\n", __func__); ath_hal_set_quiet(sc->sc_ah, q->period * ni->ni_intval, /* convert to TU */ le16dec(&q->duration), /* already in TU */ le16dec(&q->offset) + ni->ni_intval, HAL_QUIET_ENABLE | HAL_QUIET_ADD_CURRENT_TSF); /* * Note: no HAL_QUIET_ADD_SWBA_RESP_TIME; as this is for * STA mode */ /* Update local state */ memcpy(&avp->quiet_ie, ie, sizeof(struct ieee80211_quiet_ie)); } return (0); } static void ath_set_channel(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); (void) ath_chan_set(sc, ic->ic_curchan); /* * If we are returning to our bss channel then mark state * so the next recv'd beacon's tsf will be used to sync the * 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); } /* * Walk the vap list and check if there any vap's in RUN state. */ static int ath_isanyrunningvaps(struct ieee80211vap *this) { struct ieee80211com *ic = this->iv_ic; struct ieee80211vap *vap; IEEE80211_LOCK_ASSERT(ic); TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { if (vap != this && vap->iv_state >= IEEE80211_S_RUN) return 1; } return 0; } 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_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 */ HAL_LED_AUTH, /* IEEE80211_S_AUTH */ HAL_LED_ASSOC, /* IEEE80211_S_ASSOC */ HAL_LED_RUN, /* IEEE80211_S_CAC */ HAL_LED_RUN, /* IEEE80211_S_RUN */ HAL_LED_RUN, /* IEEE80211_S_CSA */ HAL_LED_RUN, /* IEEE80211_S_SLEEP */ }; DPRINTF(sc, ATH_DEBUG_STATE, "%s: %s -> %s\n", __func__, ieee80211_state_name[ostate], ieee80211_state_name[nstate]); /* * 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; ath_hal_setledstate(ah, leds[nstate]); /* set LED */ if (nstate == IEEE80211_S_SCAN) { /* * Scanning: turn off beacon miss and don't beacon. * Mark beacon state so when we reach RUN state we'll * [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, 1); 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); sc->sc_beacons = 0; taskqueue_unblock(sc->sc_tq); } 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); ath_hal_setassocid(ah, sc->sc_curbssid, sc->sc_curaid); } 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); ath_hal_setrxfilter(ah, rfilt); /* XXX is this to restore keycache on resume? */ if (vap->iv_opmode != IEEE80211_M_STA && (vap->iv_flags & IEEE80211_F_PRIVACY)) { for (i = 0; i < IEEE80211_WEP_NKID; i++) if (ath_hal_keyisvalid(ah, i)) ath_hal_keysetmac(ah, i, ni->ni_bssid); } /* * Invoke the parent method to do net80211 work. */ error = avp->av_newstate(vap, nstate, arg); if (error != 0) goto bad; /* * See above: ensure av_newstate() doesn't drop the lock * on us. */ IEEE80211_LOCK_ASSERT(ic); /* * XXX TODO: if nstate is _S_CAC, then we should disable * ACK processing until CAC is completed. */ /* * XXX TODO: if we're on a passive channel, then we should * not allow any ACKs or self-generated frames until we hear * a beacon. Unfortunately there isn't a notification from * net80211 so perhaps we could slot that particular check * into the mgmt receive path and just ensure that we clear * it on RX of beacons in passive mode (and only clear it * once, obviously.) */ /* * XXX TODO: net80211 should be tracking whether channels * have heard beacons and are thus considered "OK" for * transmitting - and then inform the driver about this * state change. That way if we hear an AP go quiet * (and nothing else is beaconing on a channel) the * channel can go back to being passive until another * beacon is heard. */ /* * XXX TODO: if nstate is _S_CAC, then we should disable * ACK processing until CAC is completed. */ /* * XXX TODO: if we're on a passive channel, then we should * not allow any ACKs or self-generated frames until we hear * a beacon. Unfortunately there isn't a notification from * net80211 so perhaps we could slot that particular check * into the mgmt receive path and just ensure that we clear * it on RX of beacons in passive mode (and only clear it * once, obviously.) */ /* * XXX TODO: net80211 should be tracking whether channels * have heard beacons and are thus considered "OK" for * transmitting - and then inform the driver about this * state change. That way if we hear an AP go quiet * (and nothing else is beaconing on a channel) the * channel can go back to being passive until another * beacon is heard. */ if (nstate == IEEE80211_S_RUN) { /* NB: collect bss node again, it may have changed */ 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 " "capinfo 0x%04x chan %d\n", __func__, vap->iv_flags, ni->ni_intval, ether_sprintf(ni->ni_bssid), ni->ni_capinfo, ieee80211_chan2ieee(ic, ic->ic_curchan)); switch (vap->iv_opmode) { #ifdef IEEE80211_SUPPORT_TDMA case IEEE80211_M_AHDEMO: if ((vap->iv_caps & IEEE80211_C_TDMA) == 0) break; /* fall thru... */ #endif case IEEE80211_M_HOSTAP: case IEEE80211_M_IBSS: case IEEE80211_M_MBSS: /* * TODO: Enable ACK processing (ie, clear AR_DIAG_ACK_DIS.) * For channels that are in CAC, we may have disabled * this during CAC to ensure we don't ACK frames * sent to us. */ /* * Allocate and setup the beacon frame. * * Stop any previous beacon DMA. This may be * necessary, for example, when an ibss merge * causes reconfiguration; there will be a state * transition from RUN->RUN that means we may * be called with beacon transmission active. */ ath_hal_stoptxdma(ah, sc->sc_bhalq); error = ath_beacon_alloc(sc, ni); if (error != 0) goto bad; /* * If joining an adhoc network defer beacon timer * configuration to the next beacon frame so we * have a current TSF to use. Otherwise we're * starting an ibss/bss so there's no need to delay; * if this is the first vap moving to RUN state, then * beacon state needs to be [re]configured. */ if (vap->iv_opmode == IEEE80211_M_IBSS && ni->ni_tstamp.tsf != 0) { sc->sc_syncbeacon = 1; } else if (!sc->sc_beacons) { #ifdef IEEE80211_SUPPORT_TDMA if (vap->iv_caps & IEEE80211_C_TDMA) ath_tdma_config(sc, vap); else #endif ath_beacon_config(sc, vap); sc->sc_beacons = 1; } break; case IEEE80211_M_STA: /* * Defer beacon timer configuration to the next * beacon frame so we have a current TSF to use * (any TSF collected when scanning is likely old). * However if it's due to a CSA -> RUN transition, * 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. */ if (ostate != IEEE80211_S_RUN && ostate != IEEE80211_S_SLEEP) { DPRINTF(sc, ATH_DEBUG_BEACON, "%s: STA; syncbeacon=1\n", __func__); sc->sc_syncbeacon = 1; /* Quiet time handling - ensure we resync */ memset(&avp->quiet_ie, 0, sizeof(avp->quiet_ie)); 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: /* * Monitor mode vaps have only INIT->RUN and RUN->RUN * transitions so we must re-enable interrupts here to * handle the case of a single monitor mode vap. */ ath_hal_intrset(ah, sc->sc_imask); break; case IEEE80211_M_WDS: break; default: break; } /* * Let the hal process statistics collected during a * scan so it can provide calibrated noise floor data. */ ath_hal_process_noisefloor(ah); /* * Reset rssi stats; maybe not the best place... */ 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, 1); /* * Finally, start any timers and the task q thread * (in case we didn't go through SCAN state). */ if (ath_longcalinterval != 0) { /* start periodic recalibration timer */ callout_reset(&sc->sc_cal_ch, 1, ath_calibrate, sc); } else { DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: calibration disabled\n", __func__); } ATH_UNLOCK(sc); taskqueue_unblock(sc->sc_tq); } else if (nstate == IEEE80211_S_INIT) { /* Quiet time handling - ensure we resync */ memset(&avp->quiet_ie, 0, sizeof(avp->quiet_ie)); /* * If there are no vaps left in RUN state then * shutdown host/driver operation: * o disable interrupts * o disable the task queue thread * o mark beacon processing as stopped */ if (!ath_isanyrunningvaps(vap)) { sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS); /* disable interrupts */ ath_hal_intrset(ah, sc->sc_imask &~ HAL_INT_GLOBAL); taskqueue_block(sc->sc_tq); sc->sc_beacons = 0; } #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, 1); } ATH_UNLOCK(sc); } } else if (nstate == IEEE80211_S_SCAN) { /* Quiet time handling - ensure we resync */ memset(&avp->quiet_ie, 0, sizeof(avp->quiet_ie)); } 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; } /* * Allocate a key cache slot to the station so we can * setup a mapping from key index to node. The key cache * slot is needed for managing antenna state and for * compression when stations do not use crypto. We do * it uniliaterally here; if crypto is employed this slot * will be reassigned. */ static void ath_setup_stationkey(struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; 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 * the more expensive lookup in software. Note * this also means no h/w compression. */ /* XXX msg+statistic */ } else { /* XXX locking? */ ni->ni_ucastkey.wk_keyix = keyix; ni->ni_ucastkey.wk_rxkeyix = rxkeyix; /* NB: must mark device key to get called back on delete */ 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, vap, &ni->ni_ucastkey, vap->iv_bss); } } /* * Setup driver-specific state for a newly associated node. * Note that we're called also on a re-associate, the isnew * param tells us if this is the first time or not. */ static void 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_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_softc; struct ath_hal *ah = sc->sc_ah; HAL_STATUS status; DPRINTF(sc, ATH_DEBUG_REGDOMAIN, "%s: rd %u cc %u location %c%s\n", __func__, reg->regdomain, reg->country, reg->location, reg->ecm ? " ecm" : ""); status = ath_hal_set_channels(ah, chans, nchans, reg->country, reg->regdomain); if (status != HAL_OK) { DPRINTF(sc, ATH_DEBUG_REGDOMAIN, "%s: failed, status %u\n", __func__, status); return EINVAL; /* XXX */ } return 0; } static void ath_getradiocaps(struct ieee80211com *ic, int maxchans, int *nchans, struct ieee80211_channel chans[]) { 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", __func__, SKU_DEBUG, CTRY_DEFAULT); /* XXX check return */ (void) ath_hal_getchannels(ah, chans, maxchans, nchans, HAL_MODE_ALL, CTRY_DEFAULT, SKU_DEBUG, AH_TRUE); } static int ath_getchannels(struct ath_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = sc->sc_ah; HAL_STATUS status; /* * Collect channel set based on EEPROM contents. */ 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) { 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); ath_hal_getcountrycode(ah, &sc->sc_eecc); /* NB: cannot fail */ /* XXX map Atheros sku's to net80211 SKU's */ /* XXX net80211 types too small */ ic->ic_regdomain.regdomain = (uint16_t) sc->sc_eerd; ic->ic_regdomain.country = (uint16_t) sc->sc_eecc; ic->ic_regdomain.isocc[0] = ' '; /* XXX don't know */ ic->ic_regdomain.isocc[1] = ' '; ic->ic_regdomain.ecm = 1; ic->ic_regdomain.location = 'I'; DPRINTF(sc, ATH_DEBUG_REGDOMAIN, "%s: eeprom rd %u cc %u (mapped rd %u cc %u) location %c%s\n", __func__, sc->sc_eerd, sc->sc_eecc, ic->ic_regdomain.regdomain, ic->ic_regdomain.country, ic->ic_regdomain.location, ic->ic_regdomain.ecm ? " ecm" : ""); return 0; } static int ath_rate_setup(struct ath_softc *sc, u_int mode) { struct ath_hal *ah = sc->sc_ah; const HAL_RATE_TABLE *rt; switch (mode) { case IEEE80211_MODE_11A: rt = ath_hal_getratetable(ah, HAL_MODE_11A); break; case IEEE80211_MODE_HALF: rt = ath_hal_getratetable(ah, HAL_MODE_11A_HALF_RATE); break; case IEEE80211_MODE_QUARTER: rt = ath_hal_getratetable(ah, HAL_MODE_11A_QUARTER_RATE); break; case IEEE80211_MODE_11B: rt = ath_hal_getratetable(ah, HAL_MODE_11B); break; case IEEE80211_MODE_11G: rt = ath_hal_getratetable(ah, HAL_MODE_11G); break; case IEEE80211_MODE_TURBO_A: rt = ath_hal_getratetable(ah, HAL_MODE_108A); break; case IEEE80211_MODE_TURBO_G: rt = ath_hal_getratetable(ah, HAL_MODE_108G); break; case IEEE80211_MODE_STURBO_A: rt = ath_hal_getratetable(ah, HAL_MODE_TURBO); break; case IEEE80211_MODE_11NA: rt = ath_hal_getratetable(ah, HAL_MODE_11NA_HT20); break; case IEEE80211_MODE_11NG: rt = ath_hal_getratetable(ah, HAL_MODE_11NG_HT20); break; default: DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid mode %u\n", __func__, mode); return 0; } sc->sc_rates[mode] = rt; return (rt != NULL); } static void ath_setcurmode(struct ath_softc *sc, enum ieee80211_phymode mode) { /* NB: on/off times from the Atheros NDIS driver, w/ permission */ static const struct { u_int rate; /* tx/rx 802.11 rate */ u_int16_t timeOn; /* LED on time (ms) */ u_int16_t timeOff; /* LED off time (ms) */ } blinkrates[] = { { 108, 40, 10 }, { 96, 44, 11 }, { 72, 50, 13 }, { 48, 57, 14 }, { 36, 67, 16 }, { 24, 80, 20 }, { 22, 100, 25 }, { 18, 133, 34 }, { 12, 160, 40 }, { 10, 200, 50 }, { 6, 240, 58 }, { 4, 267, 66 }, { 2, 400, 100 }, { 0, 500, 130 }, /* XXX half/quarter rates */ }; const HAL_RATE_TABLE *rt; int i, j; memset(sc->sc_rixmap, 0xff, sizeof(sc->sc_rixmap)); rt = sc->sc_rates[mode]; KASSERT(rt != NULL, ("no h/w rate set for phy mode %u", mode)); for (i = 0; i < rt->rateCount; i++) { uint8_t ieeerate = rt->info[i].dot11Rate & IEEE80211_RATE_VAL; if (rt->info[i].phy != IEEE80211_T_HT) sc->sc_rixmap[ieeerate] = i; else sc->sc_rixmap[ieeerate | IEEE80211_RATE_MCS] = i; } memset(sc->sc_hwmap, 0, sizeof(sc->sc_hwmap)); 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; continue; } sc->sc_hwmap[i].ieeerate = rt->info[i].dot11Rate & IEEE80211_RATE_VAL; if (rt->info[i].phy == IEEE80211_T_HT) sc->sc_hwmap[i].ieeerate |= IEEE80211_RATE_MCS; sc->sc_hwmap[i].txflags = IEEE80211_RADIOTAP_F_DATAPAD; if (rt->info[i].shortPreamble || 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 < 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 */ /* XXX beware of overlow */ sc->sc_hwmap[i].ledon = (blinkrates[j].timeOn * hz) / 1000; sc->sc_hwmap[i].ledoff = (blinkrates[j].timeOff * hz) / 1000; } sc->sc_currates = rt; sc->sc_curmode = mode; /* * All protection frames are transmitted at 2Mb/s for * 11g, otherwise at 1Mb/s. */ if (mode == IEEE80211_MODE_11G) sc->sc_protrix = ath_tx_findrix(sc, 2*2); else sc->sc_protrix = ath_tx_findrix(sc, 2*1); /* NB: caller is responsible for resetting rate control state */ } 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) { uint32_t hangs; ath_power_set_power_state(sc, HAL_PM_AWAKE); if (ath_hal_gethangstate(sc->sc_ah, 0xffff, &hangs) && hangs != 0) { device_printf(sc->sc_dev, "%s hang detected (0x%x)\n", hangs & 0xff ? "bb" : "mac", hangs); } else 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); } static void ath_parent(struct ieee80211com *ic) { struct ath_softc *sc = ic->ic_softc; int error = EDOOFUS; ATH_LOCK(sc); if (ic->ic_nrunning > 0) { /* * 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); 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 * will still be marked UP but not RUNNING. * However trying to re-init the interface * is the wrong thing to do as we've already * torn down much of our state. There's * probably a better way to deal with this. */ error = ath_init(sc); } } else { ath_stop(sc); if (!sc->sc_invalid) ath_power_setpower(sc, HAL_PM_FULL_SLEEP, 1); } 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); } } /* * Announce various information on device/driver attach. */ static void ath_announce(struct ath_softc *sc) { struct ath_hal *ah = sc->sc_ah; 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]; device_printf(sc->sc_dev, "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 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) device_printf(sc->sc_dev, "using %u rx buffers\n", ath_rxbuf); if (ath_txbuf != ATH_TXBUF) device_printf(sc->sc_dev, "using %u tx buffers\n", ath_txbuf); if (sc->sc_mcastkey && bootverbose) device_printf(sc->sc_dev, "using multicast key search\n"); } static void ath_dfs_tasklet(void *p, int npending) { struct ath_softc *sc = (struct ath_softc *) p; struct ieee80211com *ic = &sc->sc_ic; /* * If previous processing has found a radar event, * signal this to the net80211 layer to begin DFS * processing. */ if (ath_dfs_process_radar_event(sc, sc->sc_curchan)) { /* DFS event found, initiate channel change */ /* * XXX TODO: immediately disable ACK processing * on the current channel. This would be done * by setting AR_DIAG_ACK_DIS (AR5212; may be * different for others) until we are out of * CAC. */ /* * 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 */ +MODULE_VERSION(ath_main, 1); +MODULE_DEPEND(ath_main, wlan, 1, 1, 1); /* 802.11 media layer */ +MODULE_DEPEND(ath_main, ath_rate, 1, 1, 1); +MODULE_DEPEND(ath_main, ath_dfs, 1, 1, 1); +MODULE_DEPEND(ath_main, ath_hal, 1, 1, 1); #if defined(IEEE80211_ALQ) || defined(AH_DEBUG_ALQ) || defined(ATH_DEBUG_ALQ) -MODULE_DEPEND(if_ath, alq, 1, 1, 1); +MODULE_DEPEND(ath_main, alq, 1, 1, 1); #endif Index: head/sys/dev/ath/if_ath_ahb.c =================================================================== --- head/sys/dev/ath/if_ath_ahb.c (revision 318855) +++ head/sys/dev/ath/if_ath_ahb.c (revision 318856) @@ -1,350 +1,351 @@ /*- * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * Copyright (c) 2010-2011 Adrian Chadd, Xenion Pty Ltd * 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$"); /* * AHB bus front-end for the Atheros Wireless LAN controller driver. */ #include "opt_ath.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* For EEPROM firmware */ #ifdef ATH_EEPROM_FIRMWARE #include #include #endif /* ATH_EEPROM_FIRMWARE */ /* * bus glue. */ /* number of 16 bit words */ #define ATH_EEPROM_DATA_SIZE 2048 struct ath_ahb_softc { struct ath_softc sc_sc; struct resource *sc_sr; /* memory resource */ struct resource *sc_irq; /* irq resource */ void *sc_ih; /* interrupt handler */ }; #define VENDOR_ATHEROS 0x168c #define AR9130_DEVID 0x000b static int ath_ahb_probe(device_t dev) { int vendor_id, device_id; const char* devname; /* * 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); return BUS_PROBE_DEFAULT; } 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) { struct ath_ahb_softc *psc = device_get_softc(dev); struct ath_softc *sc = &psc->sc_sc; int error = ENXIO; int rid; int device_id, vendor_id; #ifdef ATH_EEPROM_FIRMWARE const struct firmware *fw = NULL; const char *buf; #endif sc->sc_dev = dev; rid = 0; psc->sc_sr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (psc->sc_sr == NULL) { device_printf(dev, "cannot map register space\n"); goto bad; } 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) * that arrive before the HAL is setup are discarded. */ sc->sc_invalid = 1; #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) { 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 bad1; } 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 bad1; } memcpy(sc->sc_eepromdata, fw->data, fw->datasize); firmware_put(fw, 0); } #endif /* ATH_EEPROM_FIRMWARE */ /* * Arrange interrupt line. */ rid = 0; psc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE|RF_ACTIVE); if (psc->sc_irq == NULL) { device_printf(dev, "could not map interrupt\n"); goto bad1; } if (bus_setup_intr(dev, psc->sc_irq, INTR_TYPE_NET | INTR_MPSAFE, NULL, ath_ahb_intr, sc, &psc->sc_ih)) { device_printf(dev, "could not establish interrupt\n"); goto bad2; } /* * Setup DMA descriptor area. */ if (bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */ 1, 0, /* alignment, bounds */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ 0x3ffff, /* maxsize XXX */ ATH_MAX_SCATTER, /* nsegments */ 0x3ffff, /* maxsegsize XXX */ BUS_DMA_ALLOCNOW, /* flags */ NULL, /* lockfunc */ NULL, /* lockarg */ &sc->sc_dmat)) { device_printf(dev, "cannot allocate DMA tag\n"); goto bad3; } /* * 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; } 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: bus_teardown_intr(dev, psc->sc_irq, psc->sc_ih); bad2: bus_release_resource(dev, SYS_RES_IRQ, 0, psc->sc_irq); bad1: bus_release_resource(dev, SYS_RES_MEMORY, 0, psc->sc_sr); bad: /* XXX?! */ if (sc->sc_eepromdata) free(sc->sc_eepromdata, M_TEMP); return (error); } static int ath_ahb_detach(device_t dev) { struct ath_ahb_softc *psc = device_get_softc(dev); struct ath_softc *sc = &psc->sc_sc; /* check if device was removed */ sc->sc_invalid = !bus_child_present(dev); ath_detach(sc); bus_generic_detach(dev); bus_teardown_intr(dev, psc->sc_irq, psc->sc_ih); bus_release_resource(dev, SYS_RES_IRQ, 0, psc->sc_irq); bus_dma_tag_destroy(sc->sc_dmat); bus_release_resource(dev, SYS_RES_MEMORY, 0, psc->sc_sr); /* XXX?! */ 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); } static int ath_ahb_shutdown(device_t dev) { struct ath_ahb_softc *psc = device_get_softc(dev); ath_shutdown(&psc->sc_sc); return (0); } static int ath_ahb_suspend(device_t dev) { struct ath_ahb_softc *psc = device_get_softc(dev); ath_suspend(&psc->sc_sc); return (0); } static int ath_ahb_resume(device_t dev) { struct ath_ahb_softc *psc = device_get_softc(dev); ath_resume(&psc->sc_sc); return (0); } static device_method_t ath_ahb_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ath_ahb_probe), DEVMETHOD(device_attach, ath_ahb_attach), DEVMETHOD(device_detach, ath_ahb_detach), DEVMETHOD(device_shutdown, ath_ahb_shutdown), DEVMETHOD(device_suspend, ath_ahb_suspend), DEVMETHOD(device_resume, ath_ahb_resume), { 0,0 } }; static driver_t ath_ahb_driver = { "ath", ath_ahb_methods, sizeof (struct ath_ahb_softc) }; 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 */ +DRIVER_MODULE(if_ath_ahb, nexus, ath_ahb_driver, ath_devclass, 0, 0); +DRIVER_MODULE(if_ath_ahb, apb, ath_ahb_driver, ath_devclass, 0, 0); +MODULE_VERSION(if_ath_ahb, 1); +MODULE_DEPEND(if_ath_ahb, wlan, 1, 1, 1); /* 802.11 media layer */ +MODULE_DEPEND(if_ath_ahb, ath_main, 1, 1, 1); /* if_ath driver */ +MODULE_DEPEND(if_ath_ahb, ath_hal, 1, 1, 1); /* ath HAL */ Index: head/sys/dev/ath/if_ath_dfs.c =================================================================== --- head/sys/dev/ath/if_ath_dfs.c (nonexistent) +++ head/sys/dev/ath/if_ath_dfs.c (revision 318856) @@ -0,0 +1,74 @@ +/*- + * Copyright (c) 2017 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$ + */ +#include "opt_ah.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int +ath_dfs_modevent(module_t mod __unused, int type, void *data __unused) +{ + int error = 0; + + switch (type) { + case MOD_LOAD: + printf("[ath_dfs] loaded\n"); + break; + + case MOD_UNLOAD: + printf("[ath_dfs] unloaded\n"); + break; + + case MOD_SHUTDOWN: + break; + + default: + error = EOPNOTSUPP; + break; + + } + return (error); +} + +DEV_MODULE(ath_dfs, ath_dfs_modevent, NULL); +MODULE_VERSION(ath_dfs, 1); +MODULE_DEPEND(ath_dfs, ath_hal, 1, 1, 1); +MODULE_DEPEND(ath_dfs, wlan, 1, 1, 1); Property changes on: head/sys/dev/ath/if_ath_dfs.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/dev/ath/if_ath_drv.c =================================================================== --- head/sys/dev/ath/if_ath_drv.c (nonexistent) +++ head/sys/dev/ath/if_ath_drv.c (revision 318856) @@ -0,0 +1,89 @@ +/*- + * Copyright (c) 2017 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$ + */ +#include "opt_ah.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * This implements the "old" style ath(4) module behaviour, which loaded the + * driver, HAL and PCI glue. + */ + +static int +ath_modevent(module_t mod __unused, int type, void *data __unused) +{ + int error = 0; + + switch (type) { + case MOD_LOAD: + printf("[ath] loaded\n"); + break; + + case MOD_UNLOAD: + printf("[ath] unloaded\n"); + break; + + case MOD_SHUTDOWN: + break; + + default: + error = EOPNOTSUPP; + break; + + } + return (error); +} + +DEV_MODULE(if_ath, ath_modevent, NULL); + +MODULE_VERSION(if_ath, 1); +MODULE_DEPEND(if_ath, ath_hal, 1, 1, 1); +MODULE_DEPEND(if_ath, ath_hal_ar5210, 1, 1, 1); +MODULE_DEPEND(if_ath, ath_hal_ar5211, 1, 1, 1); +MODULE_DEPEND(if_ath, ath_hal_ar5212, 1, 1, 1); +MODULE_DEPEND(if_ath, ath_hal_ar5416, 1, 1, 1); +MODULE_DEPEND(if_ath, ath_hal_ar9300, 1, 1, 1); +MODULE_DEPEND(if_ath, ath_rate, 1, 1, 1); +MODULE_DEPEND(if_ath, ath_dfs, 1, 1, 1); +MODULE_DEPEND(if_ath, wlan, 1, 1, 1); +MODULE_DEPEND(if_ath, ath_main, 1, 1, 1); +MODULE_DEPEND(if_ath, if_ath_pci, 1, 1, 1); Property changes on: head/sys/dev/ath/if_ath_drv.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/dev/ath/if_ath_pci.c =================================================================== --- head/sys/dev/ath/if_ath_pci.c (revision 318855) +++ head/sys/dev/ath/if_ath_pci.c (revision 318856) @@ -1,469 +1,470 @@ /*- * Copyright (c) 2002-2008 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$"); /* * PCI/Cardbus front-end for the Atheros Wireless LAN controller driver. */ #include "opt_ath.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* For EEPROM firmware */ #ifdef ATH_EEPROM_FIRMWARE #include #include #endif /* ATH_EEPROM_FIRMWARE */ /* * PCI glue. */ struct ath_pci_softc { struct ath_softc sc_sc; struct resource *sc_sr; /* memory resource */ struct resource *sc_irq; /* irq resource */ 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) { const char* devname; devname = ath_hal_probe(pci_get_vendor(dev), pci_get_device(dev)); if (devname != NULL) { device_set_desc(dev, devname); return BUS_PROBE_DEFAULT; } return ENXIO; } static int ath_pci_attach(device_t dev) { struct ath_pci_softc *psc = device_get_softc(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); /* * Setup other PCI bus configuration parameters. */ ath_pci_setup(dev); /* * Setup memory-mapping of PCI registers. */ rid = BS_BAR; psc->sc_sr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (psc->sc_sr == NULL) { device_printf(dev, "cannot map register space\n"); goto bad; } 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) * that arrive before the HAL is setup are discarded. */ 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. */ rid = 0; psc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE|RF_ACTIVE); if (psc->sc_irq == NULL) { device_printf(dev, "could not map interrupt\n"); goto bad1; } if (bus_setup_intr(dev, psc->sc_irq, INTR_TYPE_NET | INTR_MPSAFE, NULL, ath_intr, sc, &psc->sc_ih)) { device_printf(dev, "could not establish interrupt\n"); goto bad2; } /* * Setup DMA descriptor area. */ if (bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */ 1, 0, /* alignment, bounds */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ 0x3ffff, /* maxsize XXX */ ATH_MAX_SCATTER, /* nsegments */ 0x3ffff, /* maxsegsize XXX */ BUS_DMA_ALLOCNOW, /* flags */ NULL, /* lockfunc */ NULL, /* lockarg */ &sc->sc_dmat)) { device_printf(dev, "cannot allocate DMA tag\n"); goto bad3; } #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; #ifdef ATH_EEPROM_FIRMWARE bad4: #endif bus_dma_tag_destroy(sc->sc_dmat); bad3: bus_teardown_intr(dev, psc->sc_irq, psc->sc_ih); 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); } static int ath_pci_detach(device_t dev) { struct ath_pci_softc *psc = device_get_softc(dev); struct ath_softc *sc = &psc->sc_sc; /* 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); bus_teardown_intr(dev, psc->sc_irq, psc->sc_ih); bus_release_resource(dev, SYS_RES_IRQ, 0, psc->sc_irq); 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); } static int ath_pci_shutdown(device_t dev) { struct ath_pci_softc *psc = device_get_softc(dev); ath_shutdown(&psc->sc_sc); return (0); } static int ath_pci_suspend(device_t dev) { struct ath_pci_softc *psc = device_get_softc(dev); ath_suspend(&psc->sc_sc); return (0); } static int 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); } static device_method_t ath_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ath_pci_probe), DEVMETHOD(device_attach, ath_pci_attach), DEVMETHOD(device_detach, ath_pci_detach), DEVMETHOD(device_shutdown, ath_pci_shutdown), DEVMETHOD(device_suspend, ath_pci_suspend), DEVMETHOD(device_resume, ath_pci_resume), { 0,0 } }; static driver_t ath_pci_driver = { "ath", ath_pci_methods, sizeof (struct ath_pci_softc) }; static devclass_t ath_devclass; -DRIVER_MODULE(ath_pci, pci, ath_pci_driver, ath_devclass, 0, 0); -MODULE_VERSION(ath_pci, 1); -MODULE_DEPEND(ath_pci, wlan, 1, 1, 1); /* 802.11 media layer */ -MODULE_DEPEND(ath_pci, if_ath, 1, 1, 1); /* if_ath driver */ +DRIVER_MODULE(if_ath_pci, pci, ath_pci_driver, ath_devclass, 0, 0); +MODULE_VERSION(if_ath_pci, 1); +MODULE_DEPEND(if_ath_pci, wlan, 1, 1, 1); /* 802.11 media layer */ +MODULE_DEPEND(if_ath_pci, ath_main, 1, 1, 1); /* if_ath driver */ +MODULE_DEPEND(if_ath_pci, ath_hal, 1, 1, 1); /* ath HAL */ Index: head/sys/dev/ath/if_ath_rate.c =================================================================== --- head/sys/dev/ath/if_ath_rate.c (nonexistent) +++ head/sys/dev/ath/if_ath_rate.c (revision 318856) @@ -0,0 +1,74 @@ +/*- + * Copyright (c) 2017 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$ + */ +#include "opt_ah.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int +ath_rate_modevent(module_t mod __unused, int type, void *data __unused) +{ + int error = 0; + + switch (type) { + case MOD_LOAD: + printf("[ath_rate] loaded\n"); + break; + + case MOD_UNLOAD: + printf("[ath_rate] unloaded\n"); + break; + + case MOD_SHUTDOWN: + break; + + default: + error = EOPNOTSUPP; + break; + + } + return (error); +} + +DEV_MODULE(ath_rate, ath_rate_modevent, NULL); +MODULE_VERSION(ath_rate, 1); +MODULE_DEPEND(ath_rate, ath_hal, 1, 1, 1); +MODULE_DEPEND(ath_rate, wlan, 1, 1, 1); Property changes on: head/sys/dev/ath/if_ath_rate.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/modules/Makefile =================================================================== --- head/sys/modules/Makefile (revision 318855) +++ head/sys/modules/Makefile (revision 318856) @@ -1,806 +1,815 @@ # $FreeBSD$ SYSDIR?=${SRCTOP}/sys .include "${SYSDIR}/conf/kern.opts.mk" SUBDIR_PARALLEL= # Modules that include binary-only blobs of microcode should be selectable by # MK_SOURCELESS_UCODE option (see below). .if defined(MODULES_OVERRIDE) && !defined(ALL_MODULES) SUBDIR=${MODULES_OVERRIDE} .else SUBDIR= \ ${_3dfx} \ ${_3dfx_linux} \ ${_aac} \ ${_aacraid} \ accf_data \ accf_dns \ accf_http \ acl_nfs4 \ acl_posix1e \ ${_acpi} \ ae \ ${_aesni} \ age \ ${_agp} \ aha \ ahci \ ${_aic} \ aic7xxx \ alc \ ale \ alq \ ${_amd_ecc_inject} \ ${_amdsbwd} \ ${_amdtemp} \ amr \ ${_an} \ ${_aout} \ ${_apm} \ ${_arcmsr} \ ${_arcnet} \ ${_armv8crypto} \ ${_asmc} \ ata \ ath \ + ath_dfs \ + ath_hal \ + ath_hal_ar5210 \ + ath_hal_ar5211 \ + ath_hal_ar5212 \ + ath_hal_ar5416 \ + ath_hal_ar9300 \ + ath_main \ + ath_rate \ ath_pci \ ${_autofs} \ ${_auxio} \ ${_bce} \ bfe \ bge \ bhnd \ ${_bxe} \ ${_bios} \ ${_bktr} \ ${_bm} \ bnxt \ bridgestp \ bwi \ bwn \ bwn_pci \ ${_bytgpio} \ cam \ ${_cardbus} \ ${_carp} \ cas \ ${_cbb} \ cc \ cd9660 \ cd9660_iconv \ ${_ce} \ ${_cfi} \ chacha20 \ ${_chromebook_platform} \ ${_ciss} \ cloudabi \ ${_cloudabi32} \ ${_cloudabi64} \ ${_cm} \ ${_cmx} \ ${_coff} \ ${_coretemp} \ ${_cp} \ ${_cpsw} \ ${_cpuctl} \ ${_cpufreq} \ ${_crypto} \ ${_cryptodev} \ ${_cs} \ ${_ctau} \ ctl \ ${_cxgb} \ ${_cxgbe} \ dc \ dcons \ dcons_crom \ de \ ${_dpms} \ ${_dpt} \ ${_drm} \ ${_drm2} \ dummynet \ ${_ed} \ ${_efirt} \ ${_elink} \ ${_em} \ ${_ena} \ ${_ep} \ ${_epic} \ esp \ ${_et} \ evdev \ ${_ex} \ ${_exca} \ ext2fs \ fdc \ fdescfs \ ${_fe} \ filemon \ firewire \ firmware \ fuse \ ${_fxp} \ gem \ geom \ ${_glxiic} \ ${_glxsb} \ gpio \ hifn \ hme \ ${_hpt27xx} \ ${_hptiop} \ ${_hptmv} \ ${_hptnr} \ ${_hptrr} \ hwpmc \ ${_hwpmc_mips24k} \ ${_hwpmc_mips74k} \ ${_hyperv} \ i2c \ ${_ibcore} \ ${_ibcs2} \ ${_ichwd} \ ${_ida} \ if_bridge \ if_disc \ if_edsc \ ${_if_enc} \ if_epair \ ${_if_gif} \ ${_if_gre} \ ${_if_me} \ if_lagg \ ${_if_ndis} \ ${_if_stf} \ if_tap \ if_tun \ if_vlan \ if_vxlan \ ${_iir} \ imgact_binmisc \ ${_intelspi} \ ${_io} \ ${_ioat} \ ${_ipoib} \ ${_ipdivert} \ ${_ipfilter} \ ${_ipfw} \ ipfw_nat \ ${_ipfw_nat64} \ ${_ipfw_nptv6} \ ${_ipfw_pmod} \ ${_ipmi} \ ip6_mroute_mod \ ip_mroute_mod \ ${_ips} \ ${_ipsec} \ ${_ipw} \ ${_ipwfw} \ ${_isci} \ ${_iser} \ isp \ ${_ispfw} \ ${_iwi} \ ${_iwifw} \ ${_iwm} \ ${_iwmfw} \ ${_iwn} \ ${_iwnfw} \ ${_ix} \ ${_ixv} \ ${_ixgb} \ ${_ixl} \ ${_ixlv} \ jme \ joy \ kbdmux \ kgssapi \ kgssapi_krb5 \ khelp \ krpc \ ksyms \ le \ lge \ libalias \ libiconv \ libmbpool \ libmchain \ ${_linprocfs} \ ${_linsysfs} \ ${_linux} \ ${_linux_common} \ ${_linux64} \ linuxkpi \ lmc \ lpt \ mac_biba \ mac_bsdextended \ mac_ifoff \ mac_lomac \ mac_mls \ mac_none \ mac_partition \ mac_portacl \ mac_seeotheruids \ mac_stub \ mac_test \ malo \ md \ mdio \ mem \ mfi \ mii \ mlx \ ${_mlx4} \ ${_mlx4ib} \ ${_mlx4en} \ ${_mlx5} \ ${_mlx5en} \ ${_mly} \ mmc \ mmcsd \ mpr \ mps \ mpt \ mqueue \ mrsas \ msdosfs \ msdosfs_iconv \ ${_mse} \ msk \ ${_mthca} \ mvs \ mwl \ ${_mwlfw} \ mxge \ my \ ${_nandfs} \ ${_nandsim} \ ${_ncr} \ ${_nctgpio} \ ${_ncv} \ ${_ndis} \ ${_netgraph} \ ${_nfe} \ nfscl \ nfscommon \ nfsd \ nfslock \ nfslockd \ nfssvc \ nge \ nmdm \ ${_nsp} \ nullfs \ ${_ntb} \ ${_nvd} \ ${_nvme} \ ${_nvram} \ ${_nxge} \ oce \ otus \ ${_otusfw} \ ow \ ${_padlock} \ ${_padlock_rng} \ ${_pccard} \ ${_pcfclock} \ pcn \ ${_pf} \ ${_pflog} \ ${_pfsync} \ plip \ ${_pms} \ ppbus \ ppc \ ppi \ pps \ procfs \ proto \ pseudofs \ ${_pst} \ pty \ puc \ ${_qlxge} \ ${_qlxgb} \ ${_qlxgbe} \ ${_qlnx} \ ral \ ${_ralfw} \ ${_random_fortuna} \ ${_random_yarrow} \ ${_random_other} \ rc4 \ ${_rdma} \ ${_rdrand_rng} \ re \ rl \ rtwn \ rtwn_pci \ rtwn_usb \ ${_rtwnfw} \ ${_s3} \ ${_safe} \ ${_sbni} \ scc \ ${_scsi_low} \ sdhci \ ${_sdhci_acpi} \ sdhci_pci \ sem \ send \ ${_sf} \ ${_sfxge} \ sge \ siba_bwn \ siftr \ siis \ sis \ sk \ smbfs \ sn \ snp \ sound \ ${_speaker} \ spigen \ ${_splash} \ ${_sppp} \ ste \ ${_stg} \ stge \ ${_sym} \ ${_syscons} \ sysvipc \ tcp \ ${_ti} \ tl \ tmpfs \ ${_toecore} \ ${_tpm} \ trm \ ${_twa} \ twe \ tws \ tx \ ${_txp} \ uart \ ubsec \ udf \ udf_iconv \ ufs \ uinput \ unionfs \ usb \ ${_vesa} \ ${_virtio} \ vge \ ${_viawd} \ videomode \ vkbd \ ${_vmm} \ ${_vmware} \ ${_vpo} \ vr \ vte \ vx \ ${_vxge} \ wb \ ${_wbwd} \ ${_wi} \ wlan \ wlan_acl \ wlan_amrr \ wlan_ccmp \ wlan_rssadapt \ wlan_tkip \ wlan_wep \ wlan_xauth \ ${_wpi} \ ${_wpifw} \ ${_x86bios} \ ${_xe} \ xl \ zlib .if ${MK_AUTOFS} != "no" || defined(ALL_MODULES) _autofs= autofs .endif .if ${MK_CDDL} != "no" || defined(ALL_MODULES) .if (${MACHINE_CPUARCH} != "arm" || ${MACHINE_ARCH:Marmv6*} != "") && \ ${MACHINE_CPUARCH} != "mips" && \ ${MACHINE_CPUARCH} != "sparc64" SUBDIR+= dtrace .endif SUBDIR+= opensolaris .endif .if ${MK_CRYPT} != "no" || defined(ALL_MODULES) .if exists(${SRCTOP}/sys/opencrypto) _crypto= crypto _cryptodev= cryptodev _random_fortuna=random_fortuna _random_yarrow= random_yarrow _random_other= random_other .endif .endif .if ${MK_CUSE} != "no" || defined(ALL_MODULES) SUBDIR+= cuse .endif .if (${MK_INET_SUPPORT} != "no" || ${MK_INET6_SUPPORT} != "no") || \ defined(ALL_MODULES) _carp= carp _toecore= toecore _if_enc= if_enc _if_gif= if_gif _if_gre= if_gre _ipfw_pmod= ipfw_pmod .if ${MK_IPSEC_SUPPORT} != "no" _ipsec= ipsec .endif .endif .if (${MK_INET_SUPPORT} != "no" && ${MK_INET6_SUPPORT} != "no") || \ defined(ALL_MODULES) _if_stf= if_stf .endif .if ${MK_INET_SUPPORT} != "no" || defined(ALL_MODULES) _if_me= if_me _ipdivert= ipdivert _ipfw= ipfw .if ${MK_INET6_SUPPORT} != "no" || defined(ALL_MODULES) _ipfw_nat64= ipfw_nat64 .endif .endif .if ${MK_INET6_SUPPORT} != "no" || defined(ALL_MODULES) _ipfw_nptv6= ipfw_nptv6 .endif .if ${MK_IPFILTER} != "no" || defined(ALL_MODULES) _ipfilter= ipfilter .endif .if ${MK_ISCSI} != "no" || defined(ALL_MODULES) SUBDIR+= cfiscsi SUBDIR+= iscsi SUBDIR+= iscsi_initiator .endif .if ${MK_NAND} != "no" || defined(ALL_MODULES) _nandfs= nandfs _nandsim= nandsim .endif .if ${MK_NETGRAPH} != "no" || defined(ALL_MODULES) _netgraph= netgraph .endif .if (${MK_PF} != "no" && (${MK_INET_SUPPORT} != "no" || \ ${MK_INET6_SUPPORT} != "no")) || defined(ALL_MODULES) _pf= pf _pflog= pflog .if ${MK_INET_SUPPORT} != "no" _pfsync= pfsync .endif .endif .if ${MK_SOURCELESS_UCODE} != "no" _bce= bce _fxp= fxp _ispfw= ispfw _sf= sf _ti= ti _txp= txp .if ${MACHINE_CPUARCH} != "mips" _mwlfw= mwlfw _otusfw= otusfw _ralfw= ralfw _rtwnfw= rtwnfw .endif .endif .if ${MK_SOURCELESS_UCODE} != "no" && ${MACHINE_CPUARCH} != "arm" && \ ${MACHINE_CPUARCH} != "mips" && \ ${MACHINE_ARCH} != "powerpc" && ${MACHINE_ARCH} != "powerpcspe" && \ ${MACHINE_CPUARCH} != "riscv" _cxgbe= cxgbe .endif .if ${MK_TESTS} != "no" || defined(ALL_MODULES) SUBDIR+= tests .endif .if ${MK_ZFS} != "no" || defined(ALL_MODULES) SUBDIR+= zfs .endif .if (${MACHINE_CPUARCH} == "mips" && ${MACHINE_ARCH:Mmips64} == "") _hwpmc_mips24k= hwpmc_mips24k _hwpmc_mips74k= hwpmc_mips74k .endif .if ${MACHINE_CPUARCH} != "aarch64" && ${MACHINE_CPUARCH} != "arm" && \ ${MACHINE_CPUARCH} != "mips" && ${MACHINE_CPUARCH} != "powerpc" && \ ${MACHINE_CPUARCH} != "riscv" _syscons= syscons _vpo= vpo .endif .if ${MACHINE_CPUARCH} != "mips" # no BUS_SPACE_UNSPECIFIED # No barrier instruction support (specific to this driver) _sym= sym # intr_disable() is a macro, causes problems .if ${MK_SOURCELESS_UCODE} != "no" _cxgb= cxgb .endif .endif .if ${MACHINE_CPUARCH} == "aarch64" _armv8crypto= armv8crypto _em= em .endif .if ${MACHINE_CPUARCH} == "i386" || ${MACHINE_CPUARCH} == "amd64" _agp= agp _an= an _aout= aout _bios= bios _bktr= bktr _bxe= bxe _cardbus= cardbus _cbb= cbb _cpuctl= cpuctl _cpufreq= cpufreq _cs= cs _dpms= dpms _drm= drm _drm2= drm2 _ed= ed _em= em _ena= ena _ep= ep _et= et _exca= exca _fe= fe .if ${MK_OFED} != "no" || defined(ALL_MODULES) _ibcore= ibcore .endif _if_ndis= if_ndis _io= io .if ${MK_OFED} != "no" || defined(ALL_MODULES) _ipoib= ipoib _iser= iser .endif _ix= ix _ixv= ixv _linprocfs= linprocfs _linsysfs= linsysfs _linux= linux _nctgpio= nctgpio _ndis= ndis _pccard= pccard .if ${MK_OFED} != "no" || defined(ALL_MODULES) _rdma= rdma .endif _safe= safe _scsi_low= scsi_low _speaker= speaker _splash= splash _sppp= sppp _vmware= vmware _vxge= vxge _wbwd= wbwd _wi= wi _xe= xe _aac= aac _aacraid= aacraid _acpi= acpi .if ${MK_CRYPT} != "no" || defined(ALL_MODULES) _aesni= aesni .endif _amd_ecc_inject=amd_ecc_inject _amdsbwd= amdsbwd _amdtemp= amdtemp _arcmsr= arcmsr _asmc= asmc _bytgpio= bytgpio _ciss= ciss _chromebook_platform= chromebook_platform _cmx= cmx _coretemp= coretemp .if ${MK_SOURCELESS_HOST} != "no" _hpt27xx= hpt27xx .endif _hptiop= hptiop .if ${MK_SOURCELESS_HOST} != "no" _hptmv= hptmv _hptnr= hptnr _hptrr= hptrr .endif _hyperv= hyperv _ichwd= ichwd _ida= ida _iir= iir _intelspi= intelspi _ipmi= ipmi _ips= ips _isci= isci _ipw= ipw _iwi= iwi _iwm= iwm _iwn= iwn _ixgb= ixgb .if ${MK_SOURCELESS_UCODE} != "no" _ipwfw= ipwfw _iwifw= iwifw _iwmfw= iwmfw _iwnfw= iwnfw .endif _mlx4= mlx4 _mlx5= mlx5 .if (${MK_INET_SUPPORT} != "no" && ${MK_INET6_SUPPORT} != "no") || \ defined(ALL_MODULES) _mlx4en= mlx4en _mlx5en= mlx5en .endif .if ${MK_OFED} != "no" || defined(ALL_MODULES) _mlx4ib= mlx4ib .endif _mly= mly .if ${MK_OFED} != "no" || defined(ALL_MODULES) _mthca= mthca .endif _nfe= nfe _nvd= nvd _nvme= nvme _nvram= nvram _nxge= nxge .if ${MK_CRYPT} != "no" || defined(ALL_MODULES) _padlock= padlock _padlock_rng= padlock_rng _rdrand_rng= rdrand_rng .endif _s3= s3 _sdhci_acpi= sdhci_acpi _tpm= tpm _twa= twa _vesa= vesa _viawd= viawd _virtio= virtio _wpi= wpi .if ${MK_SOURCELESS_UCODE} != "no" _wpifw= wpifw .endif _x86bios= x86bios .endif .if ${MACHINE_CPUARCH} == "amd64" _efirt= efirt _ioat= ioat _ixl= ixl _ixlv= ixlv _linux64= linux64 _linux_common= linux_common _ntb= ntb _pms= pms _qlxge= qlxge _qlxgb= qlxgb _qlxgbe= qlxgbe _qlnx= qlnx _sfxge= sfxge .if ${MK_BHYVE} != "no" || defined(ALL_MODULES) _vmm= vmm .endif .endif .if ${MACHINE_CPUARCH} == "i386" # XXX some of these can move to the general case when de-i386'ed # XXX some of these can move now, but are untested on other architectures. _3dfx= 3dfx _3dfx_linux= 3dfx_linux _aic= aic _apm= apm _arcnet= arcnet .if ${MK_SOURCELESS_UCODE} != "no" _ce= ce .endif _coff= coff .if ${MK_SOURCELESS_UCODE} != "no" _cp= cp .endif _elink= elink _glxiic= glxiic _glxsb= glxsb #_ibcs2= ibcs2 _mse= mse _ncr= ncr _ncv= ncv _nsp= nsp _pcfclock= pcfclock _pst= pst _sbni= sbni _stg= stg _cm= cm .if ${MK_SOURCELESS_UCODE} != "no" _ctau= ctau .endif _dpt= dpt _ex= ex .endif .if ${MACHINE_CPUARCH} == "arm" _cfi= cfi _cpsw= cpsw .endif .if ${MACHINE_CPUARCH} == "powerpc" _agp= agp _an= an _bm= bm _cardbus= cardbus _cbb= cbb _cfi= cfi _cpufreq= cpufreq _drm= drm _exca= exca _pccard= pccard _wi= wi .endif .if ${MACHINE_ARCH} == "powerpc64" _drm2= drm2 .endif .if ${MACHINE_ARCH} == "powerpc64" || ${MACHINE_ARCH} == "powerpc" # Don't build powermac_nvram for powerpcspe, it's never supported. _nvram= powermac_nvram .endif .if ${MACHINE_CPUARCH} == "sparc64" _auxio= auxio _em= em _epic= epic .endif .if (${MACHINE_CPUARCH} == "amd64" || ${MACHINE_ARCH} == "armv6" || \ ${MACHINE_CPUARCH} == "i386") _cloudabi32= cloudabi32 .endif .if ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "amd64" _cloudabi64= cloudabi64 .endif .endif SUBDIR+=${MODULES_EXTRA} .for reject in ${WITHOUT_MODULES} SUBDIR:= ${SUBDIR:N${reject}} .endfor # Calling kldxref(8) for each module is expensive. .if !defined(NO_XREF) .MAKEFLAGS+= -DNO_XREF afterinstall: .PHONY @if type kldxref >/dev/null 2>&1; then \ ${ECHO} kldxref ${DESTDIR}${KMODDIR}; \ kldxref ${DESTDIR}${KMODDIR}; \ fi .endif .include "${SYSDIR}/conf/config.mk" SUBDIR:= ${SUBDIR:u:O} .include Index: head/sys/modules/ath/Makefile =================================================================== --- head/sys/modules/ath/Makefile (revision 318855) +++ head/sys/modules/ath/Makefile (revision 318856) @@ -1,168 +1,43 @@ # # Copyright (c) 2002-2008 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$ # -ATH_RATE?= sample # tx rate control algorithm - .PATH: ${SRCTOP}/sys/dev/ath .PATH: ${SRCTOP}/sys/dev/ath/ath_hal KMOD= if_ath -SRCS= if_ath.c if_ath_alq.c if_ath_debug.c if_ath_keycache.c if_ath_sysctl.c -SRCS+= if_ath_tx.c if_ath_tx_ht.c if_ath_led.c if_ath_rx.c if_ath_tdma.c -SRCS+= if_ath_beacon.c if_ath_rx_edma.c if_ath_tx_edma.c if_ath_spectral.c -SRCS+= if_ath_btcoex.c if_ath_btcoex_mci.c if_ath_lna_div.c if_ath_ioctl.c if_ath_descdma.c -# NB: v3 eeprom support used by both AR5211 and AR5212; just include it -SRCS+= ah_osdep.c ah.c ah_regdomain.c ah_eeprom_v3.c +SRCS= if_ath_drv.c + SRCS+= device_if.h bus_if.h pci_if.h opt_inet.h opt_ath.h opt_ah.h opt_wlan.h -# -# AR5210 support; these are first generation 11a-only devices. -# -.PATH: ${SRCTOP}/sys/dev/ath/ath_hal/ar5210 -SRCS+= ah_eeprom_v1.c \ - ar5210_attach.c ar5210_beacon.c ar5210_interrupts.c \ - ar5210_keycache.c ar5210_misc.c ar5210_phy.c ar5210_power.c \ - ar5210_recv.c ar5210_reset.c ar5210_xmit.c - -# -# AR5211 support; these are second generation 11b/g/a devices -# (but 11g was OFDM only and is not supported). -# -.PATH: ${SRCTOP}/sys/dev/ath/ath_hal/ar5211 -SRCS+= ar5211_attach.c ar5211_beacon.c ar5211_interrupts.c \ - ar5211_keycache.c ar5211_misc.c ar5211_phy.c ar5211_power.c \ - ar5211_recv.c ar5211_reset.c ar5211_xmit.c - -# -# AR5212 support; this covers all other pci/cardbus legacy parts. -# -.PATH: ${SRCTOP}/sys/dev/ath/ath_hal/ar5212 -SRCS+= ar5212_ani.c ar5212_attach.c ar5212_beacon.c ar5212_eeprom.c \ - ar5212_gpio.c ar5212_interrupts.c ar5212_keycache.c ar5212_misc.c \ - ar5212_phy.c ar5212_power.c ar5212_recv.c ar5212_reset.c \ - ar5212_rfgain.c ar5212_xmit.c -# RF backends -SRCS+= ar5111.c -SRCS+= ar5112.c -SRCS+= ar2413.c -SRCS+= ar2425.c -SRCS+= ar5413.c - -# -# AR5416, AR9130, AR9160, AR9220, AR9280, AR9285, AR9287 support. - -# Note enabling this support requires defining AH_SUPPORT_AR5416 -# in opt_ah.h so the 11n tx/rx descriptor format is handled. -# -# NB: 9160 depends on 5416 but 5416 does not require 9160 -# -# + 5416 (Owl) -.PATH: ${SRCTOP}/sys/dev/ath/ath_hal/ar5416 -SRCS+= ah_eeprom_v14.c ah_eeprom_v4k.c \ - ar5416_ani.c ar5416_attach.c ar5416_beacon.c ar5416_btcoex.c \ - ar5416_cal.c ar5416_cal_iq.c ar5416_cal_adcgain.c ar5416_cal_adcdc.c \ - ar5416_eeprom.c ar5416_gpio.c ar5416_interrupts.c ar5416_keycache.c \ - ar5416_misc.c ar5416_phy.c ar5416_power.c ar5416_radar.c ar5416_spectral.c \ - ar5416_recv.c ar5416_reset.c ar5416_xmit.c - -# RF backend for 5416, 9130 and 9160 -SRCS+= ar2133.c - -# + AR9160 (Sowl) -.PATH: ${SRCTOP}/sys/dev/ath/ath_hal/ar9001 -SRCS+= ar9160_attach.c - -# + AR9130 - (Sowl) - Embedded (AR913x SoC) -# -# This requires AH_SUPPORT_AR9130 in order to function as some -# register values have shifted for this chipset. Definig this however -# (currently) breaks non-AR9130 chipsets - since this is an embedded -# chipset and no other radios are glued to it, this shouldn't pose a -# problem. -SRCS+= ar9130_attach.c ar9130_eeprom.c ar9130_phy.c - -# AR9002 series chips -# + AR9220/AR9280 - Merlin -.PATH: ${SRCTOP}/sys/dev/ath/ath_hal/ar9002 -SRCS+= ar9280.c ar9280_attach.c ar9280_olc.c - -# + AR9285 - Kite -SRCS+= ar9285.c ar9285_reset.c ar9285_attach.c ar9285_cal.c ar9285_phy.c -SRCS+= ar9285_diversity.c ar9285_btcoex.c - -# + AR9287 - Kiwi -.PATH: ${SRCTOP}/sys/dev/ath/ath_hal -SRCS+= ah_eeprom_9287.c -.PATH: ${SRCTOP}/sys/dev/ath/ath_hal/ar9002 -SRCS+= ar9287.c ar9287_reset.c ar9287_attach.c ar9287_cal.c ar9287_olc.c - -# + AR9300 HAL -.PATH: ${SRCTOP}/sys/contrib/dev/ath/ath_hal/ar9300 -SRCS+= ar9300_interrupts.c ar9300_radar.c ar9300_ani.c ar9300_keycache.c -SRCS+= ar9300_radio.c ar9300_xmit.c ar9300_attach.c ar9300_mci.c ar9300_stub.c -SRCS+= ar9300_xmit_ds.c ar9300_beacon.c ar9300_misc.c ar9300_recv.c -SRCS+= ar9300_stub_funcs.c ar9300_eeprom.c ar9300_paprd.c ar9300_recv_ds.c -SRCS+= ar9300_freebsd.c ar9300_phy.c ar9300_reset.c ar9300_gpio.c -SRCS+= ar9300_power.c ar9300_timer.c ar9300_spectral.c - -# NB: rate control is bound to the driver by symbol names so only pick one -.if ${ATH_RATE} == "sample" -.PATH: ${SRCTOP}/sys/dev/ath/ath_rate/sample -SRCS+= sample.c -.elif ${ATH_RATE} == "onoe" -.PATH: ${SRCTOP}/sys/dev/ath/ath_rate/onoe -SRCS+= onoe.c -.elif ${ATH_RATE} == "amrr" -.PATH: ${SRCTOP}/sys/dev/ath/ath_rate/amrr -SRCS+= amrr.c -.endif - -# DFS -.PATH: ${SRCTOP}/sys/dev/ath/ath_dfs/null -SRCS+= dfs_null.c - CFLAGS+= -I. -I${SRCTOP}/sys/dev/ath -I${SRCTOP}/sys/dev/ath/ath_hal CFLAGS+= -I. -I${SRCTOP}/sys/contrib/dev/ath/ath_hal/ -.if !defined(KERNBUILDDIR) -opt_ah.h: - echo '#define AH_SUPPORT_AR5416 1' > $@ -.endif - .include - -CWARNFLAGS.ah_regdomain.c= ${NO_WSHIFT_COUNT_NEGATIVE} ${NO_WSHIFT_COUNT_OVERFLOW} -# XXX Work around clang warnings, until maintainer approves fix. -CWARNFLAGS.if_ath.c= ${NO_WSOMETIMES_UNINITIALIZED} - -# AR9300 HAL build overrides, as there's still some code to tidy up -CWARNFLAGS.ar9300_eeprom.c= ${NO_WCONSTANT_CONVERSION} -CWARNFLAGS.ar9300_reset.c= ${NO_WSOMETIMES_UNINITIALIZED} -Wno-unused-function Index: head/sys/modules/ath_dfs/Makefile =================================================================== --- head/sys/modules/ath_dfs/Makefile (nonexistent) +++ head/sys/modules/ath_dfs/Makefile (revision 318856) @@ -0,0 +1,47 @@ +# +# Copyright (c) 2002-2008 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$ +# + +.PATH: ${SRCTOP}/sys/dev/ath + +KMOD= ath_dfs + +SRCS= if_ath_dfs.c + +# DFS +.PATH: ${SRCTOP}/sys/dev/ath/ath_dfs/null +SRCS+= dfs_null.c + +SRCS+= device_if.h bus_if.h pci_if.h opt_inet.h opt_ath.h opt_ah.h opt_wlan.h + +CFLAGS+= -I. -I${SRCTOP}/sys/dev/ath -I${SRCTOP}/sys/dev/ath/ath_hal +CFLAGS+= -I. -I${SRCTOP}/sys/contrib/dev/ath/ath_hal/ + +.include Property changes on: head/sys/modules/ath_dfs/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/modules/ath_hal/Makefile =================================================================== --- head/sys/modules/ath_hal/Makefile (nonexistent) +++ head/sys/modules/ath_hal/Makefile (revision 318856) @@ -0,0 +1,47 @@ +# +# Copyright (c) 2002-2008 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$ +# + +.PATH: ${SRCTOP}/sys/dev/ath +.PATH: ${SRCTOP}/sys/dev/ath/ath_hal + +KMOD= ath_hal + +SRCS= ah_osdep.c ah.c ah_regdomain.c +SRCS+= ah_eeprom_v1.c ah_eeprom_v3.c ah_eeprom_v14.c ah_eeprom_v4k.c + +SRCS+= device_if.h bus_if.h pci_if.h opt_inet.h opt_ath.h opt_ah.h opt_wlan.h + +CFLAGS+= -I. -I${SRCTOP}/sys/dev/ath -I${SRCTOP}/sys/dev/ath/ath_hal +CFLAGS+= -I. -I${SRCTOP}/sys/contrib/dev/ath/ath_hal/ + +.include + +CWARNFLAGS.ah_regdomain.c= ${NO_WSHIFT_COUNT_NEGATIVE} ${NO_WSHIFT_COUNT_OVERFLOW} Property changes on: head/sys/modules/ath_hal/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/modules/ath_hal_ar5210/Makefile =================================================================== --- head/sys/modules/ath_hal_ar5210/Makefile (nonexistent) +++ head/sys/modules/ath_hal_ar5210/Makefile (revision 318856) @@ -0,0 +1,53 @@ +# +# Copyright (c) 2002-2008 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$ +# + +.PATH: ${SRCTOP}/sys/dev/ath + +KMOD= ath_hal_ar5210 + +# FreeBSD module loading pieces +.PATH: ${SRCTOP}/sys/dev/ath/ +SRCS= ah_osdep_ar5210.c + +# +# AR5210 support; these are first generation 11a-only devices. +# +.PATH: ${SRCTOP}/sys/dev/ath/ath_hal/ar5210 +SRCS+= ar5210_attach.c ar5210_beacon.c ar5210_interrupts.c \ + ar5210_keycache.c ar5210_misc.c ar5210_phy.c ar5210_power.c \ + ar5210_recv.c ar5210_reset.c ar5210_xmit.c + +SRCS+= device_if.h bus_if.h pci_if.h opt_inet.h opt_ath.h opt_ah.h opt_wlan.h + +CFLAGS+= -I. -I${SRCTOP}/sys/dev/ath -I${SRCTOP}/sys/dev/ath/ath_hal +CFLAGS+= -I. -I${SRCTOP}/sys/contrib/dev/ath/ath_hal/ + +.include Property changes on: head/sys/modules/ath_hal_ar5210/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/modules/ath_hal_ar5211/Makefile =================================================================== --- head/sys/modules/ath_hal_ar5211/Makefile (nonexistent) +++ head/sys/modules/ath_hal_ar5211/Makefile (revision 318856) @@ -0,0 +1,55 @@ +# +# Copyright (c) 2002-2008 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$ +# + +.PATH: ${SRCTOP}/sys/dev/ath +.PATH: ${SRCTOP}/sys/dev/ath/ath_hal + +KMOD= ath_hal_ar5211 + +# FreeBSD module loading pieces +.PATH: ${SRCTOP}/sys/dev/ath/ +SRCS= ah_osdep_ar5211.c + +# +# AR5211 support; these are second generation 11b/g/a devices +# (but 11g was OFDM only and is not supported). +# +.PATH: ${SRCTOP}/sys/dev/ath/ath_hal/ar5211 +SRCS+= ar5211_attach.c ar5211_beacon.c ar5211_interrupts.c \ + ar5211_keycache.c ar5211_misc.c ar5211_phy.c ar5211_power.c \ + ar5211_recv.c ar5211_reset.c ar5211_xmit.c + +SRCS+= device_if.h bus_if.h pci_if.h opt_inet.h opt_ath.h opt_ah.h opt_wlan.h + +CFLAGS+= -I. -I${SRCTOP}/sys/dev/ath -I${SRCTOP}/sys/dev/ath/ath_hal +CFLAGS+= -I. -I${SRCTOP}/sys/contrib/dev/ath/ath_hal/ + +.include Property changes on: head/sys/modules/ath_hal_ar5211/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/modules/ath_hal_ar5212/Makefile =================================================================== --- head/sys/modules/ath_hal_ar5212/Makefile (nonexistent) +++ head/sys/modules/ath_hal_ar5212/Makefile (revision 318856) @@ -0,0 +1,60 @@ +# +# Copyright (c) 2002-2008 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$ +# + +.PATH: ${SRCTOP}/sys/dev/ath +.PATH: ${SRCTOP}/sys/dev/ath/ath_hal + +KMOD= ath_hal_ar5212 + +# FreeBSD module loading pieces +.PATH: ${SRCTOP}/sys/dev/ath/ +SRCS= ah_osdep_ar5212.c + +# +# AR5212 support; this covers almost all other pci/cardbus legacy parts. +# +.PATH: ${SRCTOP}/sys/dev/ath/ath_hal/ar5212 +SRCS+= ar5212_ani.c ar5212_attach.c ar5212_beacon.c ar5212_eeprom.c \ + ar5212_gpio.c ar5212_interrupts.c ar5212_keycache.c ar5212_misc.c \ + ar5212_phy.c ar5212_power.c ar5212_recv.c ar5212_reset.c \ + ar5212_rfgain.c ar5212_xmit.c +SRCS+= device_if.h bus_if.h pci_if.h opt_inet.h opt_ath.h opt_ah.h opt_wlan.h +# RF backends +SRCS+= ar5111.c +SRCS+= ar5112.c +SRCS+= ar2413.c +SRCS+= ar2425.c +SRCS+= ar5413.c + +CFLAGS+= -I. -I${SRCTOP}/sys/dev/ath -I${SRCTOP}/sys/dev/ath/ath_hal +CFLAGS+= -I. -I${SRCTOP}/sys/contrib/dev/ath/ath_hal/ + +.include Property changes on: head/sys/modules/ath_hal_ar5212/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/modules/ath_hal_ar5416/Makefile =================================================================== --- head/sys/modules/ath_hal_ar5416/Makefile (nonexistent) +++ head/sys/modules/ath_hal_ar5416/Makefile (revision 318856) @@ -0,0 +1,94 @@ +# +# Copyright (c) 2002-2008 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$ +# + +.PATH: ${SRCTOP}/sys/dev/ath +.PATH: ${SRCTOP}/sys/dev/ath/ath_hal + +KMOD= ath_hal_ar5416 + +# FreeBSD module loading pieces +.PATH: ${SRCTOP}/sys/dev/ath/ +SRCS= ah_osdep_ar5416.c + +# +# AR5416, AR9130, AR9160, AR9220, AR9280, AR9285, AR9287 support. + +# Note enabling this support requires defining AH_SUPPORT_AR5416 +# in opt_ah.h so the 11n tx/rx descriptor format is handled. +# +# NB: 9160 depends on 5416 but 5416 does not require 9160 +# +# + 5416 (Owl) +.PATH: ${SRCTOP}/sys/dev/ath/ath_hal/ar5416 + +SRCS+= ar5416_ani.c ar5416_attach.c ar5416_beacon.c ar5416_btcoex.c \ + ar5416_cal.c ar5416_cal_iq.c ar5416_cal_adcgain.c ar5416_cal_adcdc.c \ + ar5416_eeprom.c ar5416_gpio.c ar5416_interrupts.c ar5416_keycache.c \ + ar5416_misc.c ar5416_phy.c ar5416_power.c ar5416_radar.c ar5416_spectral.c \ + ar5416_recv.c ar5416_reset.c ar5416_xmit.c + +SRCS+= device_if.h bus_if.h pci_if.h opt_inet.h opt_ath.h opt_ah.h opt_wlan.h + +# RF backend for 5416, 9130 and 9160 +SRCS+= ar2133.c + +# + AR9160 (Sowl) +.PATH: ${SRCTOP}/sys/dev/ath/ath_hal/ar9001 +SRCS+= ar9160_attach.c + +# + AR9130 - (Sowl) - Embedded (AR913x SoC) +# +# This requires AH_SUPPORT_AR9130 in order to function as some +# register values have shifted for this chipset. Definig this however +# (currently) breaks non-AR9130 chipsets - since this is an embedded +# chipset and no other radios are glued to it, this shouldn't pose a +# problem. +SRCS+= ar9130_attach.c ar9130_eeprom.c ar9130_phy.c + +# AR9002 series chips +# + AR9220/AR9280 - Merlin +.PATH: ${SRCTOP}/sys/dev/ath/ath_hal/ar9002 +SRCS+= ar9280.c ar9280_attach.c ar9280_olc.c + +# + AR9285 - Kite +SRCS+= ar9285.c ar9285_reset.c ar9285_attach.c ar9285_cal.c ar9285_phy.c +SRCS+= ar9285_diversity.c ar9285_btcoex.c + +# + AR9287 - Kiwi +.PATH: ${SRCTOP}/sys/dev/ath/ath_hal +SRCS+= ah_eeprom_9287.c +.PATH: ${SRCTOP}/sys/dev/ath/ath_hal/ar9002 +SRCS+= ar9287.c ar9287_reset.c ar9287_attach.c ar9287_cal.c ar9287_olc.c + +CFLAGS+= -I. -I${SRCTOP}/sys/dev/ath -I${SRCTOP}/sys/dev/ath/ath_hal +CFLAGS+= -I. -I${SRCTOP}/sys/contrib/dev/ath/ath_hal/ + +.include Property changes on: head/sys/modules/ath_hal_ar5416/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/modules/ath_hal_ar9300/Makefile =================================================================== --- head/sys/modules/ath_hal_ar9300/Makefile (nonexistent) +++ head/sys/modules/ath_hal_ar9300/Makefile (revision 318856) @@ -0,0 +1,59 @@ +# +# Copyright (c) 2002-2008 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$ +# + +.PATH: ${SRCTOP}/sys/dev/ath +.PATH: ${SRCTOP}/sys/dev/ath/ath_hal + +KMOD= ath_hal_ar9300 + +# FreeBSD module loading pieces +.PATH: ${SRCTOP}/sys/dev/ath/ +SRCS= ah_osdep_ar9300.c + +# + AR9300 HAL +.PATH: ${SRCTOP}/sys/contrib/dev/ath/ath_hal/ar9300 +SRCS+= ar9300_interrupts.c ar9300_radar.c ar9300_ani.c ar9300_keycache.c +SRCS+= ar9300_radio.c ar9300_xmit.c ar9300_attach.c ar9300_mci.c ar9300_stub.c +SRCS+= ar9300_xmit_ds.c ar9300_beacon.c ar9300_misc.c ar9300_recv.c +SRCS+= ar9300_stub_funcs.c ar9300_eeprom.c ar9300_paprd.c ar9300_recv_ds.c +SRCS+= ar9300_freebsd.c ar9300_phy.c ar9300_reset.c ar9300_gpio.c +SRCS+= ar9300_power.c ar9300_timer.c ar9300_spectral.c + +SRCS+= device_if.h bus_if.h pci_if.h opt_inet.h opt_ath.h opt_ah.h opt_wlan.h + +CFLAGS+= -I. -I${SRCTOP}/sys/dev/ath -I${SRCTOP}/sys/dev/ath/ath_hal +CFLAGS+= -I. -I${SRCTOP}/sys/contrib/dev/ath/ath_hal/ + +.include + +# AR9300 HAL build overrides, as there's still some code to tidy up +CWARNFLAGS.ar9300_eeprom.c= ${NO_WCONSTANT_CONVERSION} +CWARNFLAGS.ar9300_reset.c= ${NO_WSOMETIMES_UNINITIALIZED} -Wno-unused-function Property changes on: head/sys/modules/ath_hal_ar9300/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/modules/ath_main/Makefile =================================================================== --- head/sys/modules/ath_main/Makefile (nonexistent) +++ head/sys/modules/ath_main/Makefile (revision 318856) @@ -0,0 +1,51 @@ +# +# Copyright (c) 2002-2008 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$ +# + +ATH_RATE?= sample # tx rate control algorithm + +.PATH: ${SRCTOP}/sys/dev/ath +.PATH: ${SRCTOP}/sys/dev/ath/ath_hal + +KMOD= ath_main +SRCS= if_ath.c if_ath_alq.c if_ath_debug.c if_ath_keycache.c if_ath_sysctl.c +SRCS+= if_ath_tx.c if_ath_tx_ht.c if_ath_led.c if_ath_rx.c if_ath_tdma.c +SRCS+= if_ath_beacon.c if_ath_rx_edma.c if_ath_tx_edma.c if_ath_spectral.c +SRCS+= if_ath_btcoex.c if_ath_btcoex_mci.c if_ath_lna_div.c if_ath_ioctl.c if_ath_descdma.c + +SRCS+= device_if.h bus_if.h pci_if.h opt_inet.h opt_ath.h opt_ah.h opt_wlan.h + +CFLAGS+= -I. -I${SRCTOP}/sys/dev/ath -I${SRCTOP}/sys/dev/ath/ath_hal +CFLAGS+= -I. -I${SRCTOP}/sys/contrib/dev/ath/ath_hal/ + +.include + +# XXX Work around clang warnings, until maintainer approves fix. +CWARNFLAGS.if_ath.c= ${NO_WSOMETIMES_UNINITIALIZED} Property changes on: head/sys/modules/ath_main/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/modules/ath_rate/Makefile =================================================================== --- head/sys/modules/ath_rate/Makefile (nonexistent) +++ head/sys/modules/ath_rate/Makefile (revision 318856) @@ -0,0 +1,57 @@ +# +# Copyright (c) 2002-2008 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$ +# + +ATH_RATE?= sample # tx rate control algorithm + +.PATH: ${SRCTOP}/sys/dev/ath + +KMOD= ath_rate + +SRCS= if_ath_rate.c + +# NB: rate control is bound to the driver by symbol names so only pick one +.if ${ATH_RATE} == "sample" +.PATH: ${SRCTOP}/sys/dev/ath/ath_rate/sample +SRCS+= sample.c +.elif ${ATH_RATE} == "onoe" +.PATH: ${SRCTOP}/sys/dev/ath/ath_rate/onoe +SRCS+= onoe.c +.elif ${ATH_RATE} == "amrr" +.PATH: ${SRCTOP}/sys/dev/ath/ath_rate/amrr +SRCS+= amrr.c +.endif + +SRCS+= device_if.h bus_if.h pci_if.h opt_inet.h opt_ath.h opt_ah.h opt_wlan.h + +CFLAGS+= -I. -I${SRCTOP}/sys/dev/ath -I${SRCTOP}/sys/dev/ath/ath_hal +CFLAGS+= -I. -I${SRCTOP}/sys/contrib/dev/ath/ath_hal/ + +.include Property changes on: head/sys/modules/ath_rate/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property