15591b213SSam Leffler /*- 210ad9a77SSam Leffler * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting 35591b213SSam Leffler * All rights reserved. 45591b213SSam Leffler * 55591b213SSam Leffler * Redistribution and use in source and binary forms, with or without 65591b213SSam Leffler * modification, are permitted provided that the following conditions 75591b213SSam Leffler * are met: 85591b213SSam Leffler * 1. Redistributions of source code must retain the above copyright 95591b213SSam Leffler * notice, this list of conditions and the following disclaimer, 105591b213SSam Leffler * without modification. 115591b213SSam Leffler * 2. Redistributions in binary form must reproduce at minimum a disclaimer 125591b213SSam Leffler * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 135591b213SSam Leffler * redistribution must be conditioned upon including a substantially 145591b213SSam Leffler * similar Disclaimer requirement for further binary redistribution. 155591b213SSam Leffler * 165591b213SSam Leffler * NO WARRANTY 175591b213SSam Leffler * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 185591b213SSam Leffler * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 195591b213SSam Leffler * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 205591b213SSam Leffler * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 215591b213SSam Leffler * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 225591b213SSam Leffler * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 235591b213SSam Leffler * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 245591b213SSam Leffler * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 255591b213SSam Leffler * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 265591b213SSam Leffler * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 275591b213SSam Leffler * THE POSSIBILITY OF SUCH DAMAGES. 285591b213SSam Leffler */ 295591b213SSam Leffler 305591b213SSam Leffler #include <sys/cdefs.h> 315591b213SSam Leffler __FBSDID("$FreeBSD$"); 325591b213SSam Leffler 335591b213SSam Leffler /* 345591b213SSam Leffler * Driver for the Atheros Wireless LAN controller. 355f3721d5SSam Leffler * 365f3721d5SSam Leffler * This software is derived from work of Atsushi Onoe; his contribution 375f3721d5SSam Leffler * is greatly appreciated. 385591b213SSam Leffler */ 395591b213SSam Leffler 405591b213SSam Leffler #include "opt_inet.h" 41a585a9a1SSam Leffler #include "opt_ath.h" 42584f7327SSam Leffler #include "opt_wlan.h" 435591b213SSam Leffler 445591b213SSam Leffler #include <sys/param.h> 455591b213SSam Leffler #include <sys/systm.h> 465591b213SSam Leffler #include <sys/sysctl.h> 475591b213SSam Leffler #include <sys/mbuf.h> 485591b213SSam Leffler #include <sys/malloc.h> 495591b213SSam Leffler #include <sys/lock.h> 505591b213SSam Leffler #include <sys/mutex.h> 515591b213SSam Leffler #include <sys/kernel.h> 525591b213SSam Leffler #include <sys/socket.h> 535591b213SSam Leffler #include <sys/sockio.h> 545591b213SSam Leffler #include <sys/errno.h> 555591b213SSam Leffler #include <sys/callout.h> 565591b213SSam Leffler #include <sys/bus.h> 575591b213SSam Leffler #include <sys/endian.h> 580bbf5441SSam Leffler #include <sys/kthread.h> 590bbf5441SSam Leffler #include <sys/taskqueue.h> 603fc21fedSSam Leffler #include <sys/priv.h> 615591b213SSam Leffler 625591b213SSam Leffler #include <machine/bus.h> 635591b213SSam Leffler 645591b213SSam Leffler #include <net/if.h> 655591b213SSam Leffler #include <net/if_dl.h> 665591b213SSam Leffler #include <net/if_media.h> 67fc74a9f9SBrooks Davis #include <net/if_types.h> 685591b213SSam Leffler #include <net/if_arp.h> 695591b213SSam Leffler #include <net/ethernet.h> 705591b213SSam Leffler #include <net/if_llc.h> 715591b213SSam Leffler 725591b213SSam Leffler #include <net80211/ieee80211_var.h> 7359efa8b5SSam Leffler #include <net80211/ieee80211_regdomain.h> 74339ccfb3SSam Leffler #ifdef IEEE80211_SUPPORT_SUPERG 75339ccfb3SSam Leffler #include <net80211/ieee80211_superg.h> 76339ccfb3SSam Leffler #endif 77584f7327SSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 7810ad9a77SSam Leffler #include <net80211/ieee80211_tdma.h> 7910ad9a77SSam Leffler #endif 805591b213SSam Leffler 815591b213SSam Leffler #include <net/bpf.h> 825591b213SSam Leffler 835591b213SSam Leffler #ifdef INET 845591b213SSam Leffler #include <netinet/in.h> 855591b213SSam Leffler #include <netinet/if_ether.h> 865591b213SSam Leffler #endif 875591b213SSam Leffler 885591b213SSam Leffler #include <dev/ath/if_athvar.h> 8933644623SSam Leffler #include <dev/ath/ath_hal/ah_devid.h> /* XXX for softled */ 900dbe9289SAdrian Chadd #include <dev/ath/ath_hal/ah_diagcodes.h> 915591b213SSam Leffler 925bc8125aSAdrian Chadd #include <dev/ath/if_ath_debug.h> 93b8e788a5SAdrian Chadd #include <dev/ath/if_ath_misc.h> 94b8e788a5SAdrian Chadd #include <dev/ath/if_ath_tx.h> 955bc8125aSAdrian Chadd 9686e07743SSam Leffler #ifdef ATH_TX99_DIAG 9786e07743SSam Leffler #include <dev/ath/ath_tx99/ath_tx99.h> 9886e07743SSam Leffler #endif 9986e07743SSam Leffler 100b032f27cSSam Leffler /* 101b032f27cSSam Leffler * ATH_BCBUF determines the number of vap's that can transmit 102b032f27cSSam Leffler * beacons and also (currently) the number of vap's that can 103b032f27cSSam Leffler * have unique mac addresses/bssid. When staggering beacons 104b032f27cSSam Leffler * 4 is probably a good max as otherwise the beacons become 105b032f27cSSam Leffler * very closely spaced and there is limited time for cab q traffic 106b032f27cSSam Leffler * to go out. You can burst beacons instead but that is not good 107b032f27cSSam Leffler * for stations in power save and at some point you really want 108b032f27cSSam Leffler * another radio (and channel). 109b032f27cSSam Leffler * 110b032f27cSSam Leffler * The limit on the number of mac addresses is tied to our use of 111b032f27cSSam Leffler * the U/L bit and tracking addresses in a byte; it would be 112b032f27cSSam Leffler * worthwhile to allow more for applications like proxy sta. 113b032f27cSSam Leffler */ 114b032f27cSSam Leffler CTASSERT(ATH_BCBUF <= 8); 115b032f27cSSam Leffler 116b032f27cSSam Leffler static struct ieee80211vap *ath_vap_create(struct ieee80211com *, 117b032f27cSSam Leffler const char name[IFNAMSIZ], int unit, int opmode, 118b032f27cSSam Leffler int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], 119b032f27cSSam Leffler const uint8_t mac[IEEE80211_ADDR_LEN]); 120b032f27cSSam Leffler static void ath_vap_delete(struct ieee80211vap *); 1215591b213SSam Leffler static void ath_init(void *); 122c42a7b7eSSam Leffler static void ath_stop_locked(struct ifnet *); 1235591b213SSam Leffler static void ath_stop(struct ifnet *); 1245591b213SSam Leffler static void ath_start(struct ifnet *); 125c42a7b7eSSam Leffler static int ath_reset(struct ifnet *); 126b032f27cSSam Leffler static int ath_reset_vap(struct ieee80211vap *, u_long); 1275591b213SSam Leffler static int ath_media_change(struct ifnet *); 1282e986da5SSam Leffler static void ath_watchdog(void *); 1295591b213SSam Leffler static int ath_ioctl(struct ifnet *, u_long, caddr_t); 1305591b213SSam Leffler static void ath_fatal_proc(void *, int); 131b032f27cSSam Leffler static void ath_bmiss_vap(struct ieee80211vap *); 1325591b213SSam Leffler static void ath_bmiss_proc(void *, int); 133d3ac945bSSam Leffler static int ath_keyset(struct ath_softc *, const struct ieee80211_key *, 134d3ac945bSSam Leffler struct ieee80211_node *); 135b032f27cSSam Leffler static int ath_key_alloc(struct ieee80211vap *, 136e6e547d5SSam Leffler struct ieee80211_key *, 137c1225b52SSam Leffler ieee80211_keyix *, ieee80211_keyix *); 138b032f27cSSam Leffler static int ath_key_delete(struct ieee80211vap *, 139c42a7b7eSSam Leffler const struct ieee80211_key *); 140b032f27cSSam Leffler static int ath_key_set(struct ieee80211vap *, const struct ieee80211_key *, 141c42a7b7eSSam Leffler const u_int8_t mac[IEEE80211_ADDR_LEN]); 142b032f27cSSam Leffler static void ath_key_update_begin(struct ieee80211vap *); 143b032f27cSSam Leffler static void ath_key_update_end(struct ieee80211vap *); 144b032f27cSSam Leffler static void ath_update_mcast(struct ifnet *); 145b032f27cSSam Leffler static void ath_update_promisc(struct ifnet *); 1465591b213SSam Leffler static void ath_mode_init(struct ath_softc *); 147c42a7b7eSSam Leffler static void ath_setslottime(struct ath_softc *); 148c42a7b7eSSam Leffler static void ath_updateslot(struct ifnet *); 14980d2765fSSam Leffler static int ath_beaconq_setup(struct ath_hal *); 1505591b213SSam Leffler static int ath_beacon_alloc(struct ath_softc *, struct ieee80211_node *); 151b032f27cSSam Leffler static void ath_beacon_update(struct ieee80211vap *, int item); 152c42a7b7eSSam Leffler static void ath_beacon_setup(struct ath_softc *, struct ath_buf *); 1535591b213SSam Leffler static void ath_beacon_proc(void *, int); 154b032f27cSSam Leffler static struct ath_buf *ath_beacon_generate(struct ath_softc *, 155b032f27cSSam Leffler struct ieee80211vap *); 156c42a7b7eSSam Leffler static void ath_bstuck_proc(void *, int); 157b032f27cSSam Leffler static void ath_beacon_return(struct ath_softc *, struct ath_buf *); 1585591b213SSam Leffler static void ath_beacon_free(struct ath_softc *); 159b032f27cSSam Leffler static void ath_beacon_config(struct ath_softc *, struct ieee80211vap *); 160c42a7b7eSSam Leffler static void ath_descdma_cleanup(struct ath_softc *sc, 161c42a7b7eSSam Leffler struct ath_descdma *, ath_bufhead *); 1625591b213SSam Leffler static int ath_desc_alloc(struct ath_softc *); 1635591b213SSam Leffler static void ath_desc_free(struct ath_softc *); 16438c208f8SSam Leffler static struct ieee80211_node *ath_node_alloc(struct ieee80211vap *, 16538c208f8SSam Leffler const uint8_t [IEEE80211_ADDR_LEN]); 166c42a7b7eSSam Leffler static void ath_node_free(struct ieee80211_node *); 16768e8e04eSSam Leffler static void ath_node_getsignal(const struct ieee80211_node *, 16868e8e04eSSam Leffler int8_t *, int8_t *); 1695591b213SSam Leffler static int ath_rxbuf_init(struct ath_softc *, struct ath_buf *); 170b032f27cSSam Leffler static void ath_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, 1715463c4a4SSam Leffler int subtype, int rssi, int nf); 172c42a7b7eSSam Leffler static void ath_setdefantenna(struct ath_softc *, u_int); 1735591b213SSam Leffler static void ath_rx_proc(void *, int); 174622b3fd2SSam Leffler static void ath_txq_init(struct ath_softc *sc, struct ath_txq *, int); 175c42a7b7eSSam Leffler static struct ath_txq *ath_txq_setup(struct ath_softc*, int qtype, int subtype); 176c42a7b7eSSam Leffler static int ath_tx_setup(struct ath_softc *, int, int); 177c42a7b7eSSam Leffler static int ath_wme_update(struct ieee80211com *); 178c42a7b7eSSam Leffler static void ath_tx_cleanupq(struct ath_softc *, struct ath_txq *); 179c42a7b7eSSam Leffler static void ath_tx_cleanup(struct ath_softc *); 180c42a7b7eSSam Leffler static void ath_tx_proc_q0(void *, int); 181c42a7b7eSSam Leffler static void ath_tx_proc_q0123(void *, int); 1825591b213SSam Leffler static void ath_tx_proc(void *, int); 183b032f27cSSam Leffler static void ath_tx_draintxq(struct ath_softc *, struct ath_txq *); 1845591b213SSam Leffler static int ath_chan_set(struct ath_softc *, struct ieee80211_channel *); 1855591b213SSam Leffler static void ath_draintxq(struct ath_softc *); 1865591b213SSam Leffler static void ath_stoprecv(struct ath_softc *); 1875591b213SSam Leffler static int ath_startrecv(struct ath_softc *); 188c42a7b7eSSam Leffler static void ath_chan_change(struct ath_softc *, struct ieee80211_channel *); 18968e8e04eSSam Leffler static void ath_scan_start(struct ieee80211com *); 19068e8e04eSSam Leffler static void ath_scan_end(struct ieee80211com *); 19168e8e04eSSam Leffler static void ath_set_channel(struct ieee80211com *); 1925591b213SSam Leffler static void ath_calibrate(void *); 193b032f27cSSam Leffler static int ath_newstate(struct ieee80211vap *, enum ieee80211_state, int); 194e8fd88a3SSam Leffler static void ath_setup_stationkey(struct ieee80211_node *); 195e9962332SSam Leffler static void ath_newassoc(struct ieee80211_node *, int); 196b032f27cSSam Leffler static int ath_setregdomain(struct ieee80211com *, 197b032f27cSSam Leffler struct ieee80211_regdomain *, int, 198b032f27cSSam Leffler struct ieee80211_channel []); 1995fe9f044SSam Leffler static void ath_getradiocaps(struct ieee80211com *, int, int *, 200b032f27cSSam Leffler struct ieee80211_channel []); 201b032f27cSSam Leffler static int ath_getchannels(struct ath_softc *); 2023e50ec2cSSam Leffler static void ath_led_event(struct ath_softc *, int); 2035591b213SSam Leffler 204c42a7b7eSSam Leffler static int ath_rate_setup(struct ath_softc *, u_int mode); 2055591b213SSam Leffler static void ath_setcurmode(struct ath_softc *, enum ieee80211_phymode); 206c42a7b7eSSam Leffler 207c42a7b7eSSam Leffler static void ath_sysctlattach(struct ath_softc *); 208c42a7b7eSSam Leffler static void ath_announce(struct ath_softc *); 209e8dabfbeSAdrian Chadd static void ath_sysctl_stats_attach(struct ath_softc *sc); 2105591b213SSam Leffler 211584f7327SSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 21210ad9a77SSam Leffler static void ath_tdma_settimers(struct ath_softc *sc, u_int32_t nexttbtt, 21310ad9a77SSam Leffler u_int32_t bintval); 21410ad9a77SSam Leffler static void ath_tdma_bintvalsetup(struct ath_softc *sc, 21510ad9a77SSam Leffler const struct ieee80211_tdma_state *tdma); 21610ad9a77SSam Leffler static void ath_tdma_config(struct ath_softc *sc, struct ieee80211vap *vap); 21710ad9a77SSam Leffler static void ath_tdma_update(struct ieee80211_node *ni, 2182bc3ce77SSam Leffler const struct ieee80211_tdma_param *tdma, int); 21910ad9a77SSam Leffler static void ath_tdma_beacon_send(struct ath_softc *sc, 22010ad9a77SSam Leffler struct ieee80211vap *vap); 22110ad9a77SSam Leffler 22210ad9a77SSam Leffler static __inline void 22310ad9a77SSam Leffler ath_hal_setcca(struct ath_hal *ah, int ena) 22410ad9a77SSam Leffler { 22510ad9a77SSam Leffler /* 22610ad9a77SSam Leffler * NB: fill me in; this is not provided by default because disabling 22710ad9a77SSam Leffler * CCA in most locales violates regulatory. 22810ad9a77SSam Leffler */ 22910ad9a77SSam Leffler } 23010ad9a77SSam Leffler 23110ad9a77SSam Leffler static __inline int 23210ad9a77SSam Leffler ath_hal_getcca(struct ath_hal *ah) 23310ad9a77SSam Leffler { 23410ad9a77SSam Leffler u_int32_t diag; 23510ad9a77SSam Leffler if (ath_hal_getcapability(ah, HAL_CAP_DIAG, 0, &diag) != HAL_OK) 23610ad9a77SSam Leffler return 1; 23710ad9a77SSam Leffler return ((diag & 0x500000) == 0); 23810ad9a77SSam Leffler } 23910ad9a77SSam Leffler 24010ad9a77SSam Leffler #define TDMA_EP_MULTIPLIER (1<<10) /* pow2 to optimize out * and / */ 24110ad9a77SSam Leffler #define TDMA_LPF_LEN 6 24210ad9a77SSam Leffler #define TDMA_DUMMY_MARKER 0x127 24310ad9a77SSam Leffler #define TDMA_EP_MUL(x, mul) ((x) * (mul)) 24410ad9a77SSam Leffler #define TDMA_IN(x) (TDMA_EP_MUL((x), TDMA_EP_MULTIPLIER)) 24510ad9a77SSam Leffler #define TDMA_LPF(x, y, len) \ 24610ad9a77SSam Leffler ((x != TDMA_DUMMY_MARKER) ? (((x) * ((len)-1) + (y)) / (len)) : (y)) 24710ad9a77SSam Leffler #define TDMA_SAMPLE(x, y) do { \ 24810ad9a77SSam Leffler x = TDMA_LPF((x), TDMA_IN(y), TDMA_LPF_LEN); \ 24910ad9a77SSam Leffler } while (0) 25010ad9a77SSam Leffler #define TDMA_EP_RND(x,mul) \ 25110ad9a77SSam Leffler ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul)) 25210ad9a77SSam Leffler #define TDMA_AVG(x) TDMA_EP_RND(x, TDMA_EP_MULTIPLIER) 253584f7327SSam Leffler #endif /* IEEE80211_SUPPORT_TDMA */ 25410ad9a77SSam Leffler 2555591b213SSam Leffler SYSCTL_DECL(_hw_ath); 2565591b213SSam Leffler 2575591b213SSam Leffler /* XXX validate sysctl values */ 2582dc7fcc4SSam Leffler static int ath_longcalinterval = 30; /* long cals every 30 secs */ 2592dc7fcc4SSam Leffler SYSCTL_INT(_hw_ath, OID_AUTO, longcal, CTLFLAG_RW, &ath_longcalinterval, 2602dc7fcc4SSam Leffler 0, "long chip calibration interval (secs)"); 2612dc7fcc4SSam Leffler static int ath_shortcalinterval = 100; /* short cals every 100 ms */ 2622dc7fcc4SSam Leffler SYSCTL_INT(_hw_ath, OID_AUTO, shortcal, CTLFLAG_RW, &ath_shortcalinterval, 2632dc7fcc4SSam Leffler 0, "short chip calibration interval (msecs)"); 2642dc7fcc4SSam Leffler static int ath_resetcalinterval = 20*60; /* reset cal state 20 mins */ 2652dc7fcc4SSam Leffler SYSCTL_INT(_hw_ath, OID_AUTO, resetcal, CTLFLAG_RW, &ath_resetcalinterval, 2662dc7fcc4SSam Leffler 0, "reset chip calibration results (secs)"); 267a108ab63SAdrian Chadd static int ath_anicalinterval = 100; /* ANI calibration - 100 msec */ 268a108ab63SAdrian Chadd SYSCTL_INT(_hw_ath, OID_AUTO, anical, CTLFLAG_RW, &ath_anicalinterval, 269a108ab63SAdrian Chadd 0, "ANI calibration (msecs)"); 2705591b213SSam Leffler 271e2d787faSSam Leffler static int ath_rxbuf = ATH_RXBUF; /* # rx buffers to allocate */ 272aaa70f2fSSam Leffler SYSCTL_INT(_hw_ath, OID_AUTO, rxbuf, CTLFLAG_RW, &ath_rxbuf, 273e2d787faSSam Leffler 0, "rx buffers allocated"); 274e2d787faSSam Leffler TUNABLE_INT("hw.ath.rxbuf", &ath_rxbuf); 275e2d787faSSam Leffler static int ath_txbuf = ATH_TXBUF; /* # tx buffers to allocate */ 276aaa70f2fSSam Leffler SYSCTL_INT(_hw_ath, OID_AUTO, txbuf, CTLFLAG_RW, &ath_txbuf, 277e2d787faSSam Leffler 0, "tx buffers allocated"); 278e2d787faSSam Leffler TUNABLE_INT("hw.ath.txbuf", &ath_txbuf); 279e2d787faSSam Leffler 280a32ac9d3SSam Leffler static int ath_bstuck_threshold = 4; /* max missed beacons */ 281a32ac9d3SSam Leffler SYSCTL_INT(_hw_ath, OID_AUTO, bstuck, CTLFLAG_RW, &ath_bstuck_threshold, 282a32ac9d3SSam Leffler 0, "max missed beacon xmits before chip reset"); 283a32ac9d3SSam Leffler 284c42a7b7eSSam Leffler MALLOC_DEFINE(M_ATHDEV, "athdev", "ath driver dma buffers"); 285c42a7b7eSSam Leffler 286*67397d39SAdrian Chadd #define HAL_MODE_HT20 (HAL_MODE_11NG_HT20 | HAL_MODE_11NA_HT20) 287*67397d39SAdrian Chadd #define HAL_MODE_HT40 \ 288*67397d39SAdrian Chadd (HAL_MODE_11NG_HT40PLUS | HAL_MODE_11NG_HT40MINUS | \ 289*67397d39SAdrian Chadd HAL_MODE_11NA_HT40PLUS | HAL_MODE_11NA_HT40MINUS) 2905591b213SSam Leffler int 2915591b213SSam Leffler ath_attach(u_int16_t devid, struct ath_softc *sc) 2925591b213SSam Leffler { 293fc74a9f9SBrooks Davis struct ifnet *ifp; 294b032f27cSSam Leffler struct ieee80211com *ic; 295fc74a9f9SBrooks Davis struct ath_hal *ah = NULL; 2965591b213SSam Leffler HAL_STATUS status; 297c42a7b7eSSam Leffler int error = 0, i; 298411373ebSSam Leffler u_int wmodes; 29929aca940SSam Leffler uint8_t macaddr[IEEE80211_ADDR_LEN]; 3005591b213SSam Leffler 301c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: devid 0x%x\n", __func__, devid); 3025591b213SSam Leffler 303b032f27cSSam Leffler ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); 304fc74a9f9SBrooks Davis if (ifp == NULL) { 305fc74a9f9SBrooks Davis device_printf(sc->sc_dev, "can not if_alloc()\n"); 306fc74a9f9SBrooks Davis error = ENOSPC; 307fc74a9f9SBrooks Davis goto bad; 308fc74a9f9SBrooks Davis } 309b032f27cSSam Leffler ic = ifp->if_l2com; 310fc74a9f9SBrooks Davis 3115591b213SSam Leffler /* set these up early for if_printf use */ 3129bf40edeSBrooks Davis if_initname(ifp, device_get_name(sc->sc_dev), 3139bf40edeSBrooks Davis device_get_unit(sc->sc_dev)); 3145591b213SSam Leffler 31588117a53SAdrian Chadd ah = ath_hal_attach(devid, sc, sc->sc_st, sc->sc_sh, sc->sc_eepromdata, &status); 3165591b213SSam Leffler if (ah == NULL) { 3175591b213SSam Leffler if_printf(ifp, "unable to attach hardware; HAL status %u\n", 3185591b213SSam Leffler status); 3195591b213SSam Leffler error = ENXIO; 3205591b213SSam Leffler goto bad; 3215591b213SSam Leffler } 3225591b213SSam Leffler sc->sc_ah = ah; 323b58b3803SSam Leffler sc->sc_invalid = 0; /* ready to go, enable interrupt handling */ 3243297be13SSam Leffler #ifdef ATH_DEBUG 3253297be13SSam Leffler sc->sc_debug = ath_debug; 3263297be13SSam Leffler #endif 3275591b213SSam Leffler 3285591b213SSam Leffler /* 329c42a7b7eSSam Leffler * Check if the MAC has multi-rate retry support. 330c42a7b7eSSam Leffler * We do this by trying to setup a fake extended 331c42a7b7eSSam Leffler * descriptor. MAC's that don't have support will 332c42a7b7eSSam Leffler * return false w/o doing anything. MAC's that do 333c42a7b7eSSam Leffler * support it will return true w/o doing anything. 334c42a7b7eSSam Leffler */ 335c42a7b7eSSam Leffler sc->sc_mrretry = ath_hal_setupxtxdesc(ah, NULL, 0,0, 0,0, 0,0); 336c42a7b7eSSam Leffler 337c42a7b7eSSam Leffler /* 338c42a7b7eSSam Leffler * Check if the device has hardware counters for PHY 339c42a7b7eSSam Leffler * errors. If so we need to enable the MIB interrupt 340c42a7b7eSSam Leffler * so we can act on stat triggers. 341c42a7b7eSSam Leffler */ 342c42a7b7eSSam Leffler if (ath_hal_hwphycounters(ah)) 343c42a7b7eSSam Leffler sc->sc_needmib = 1; 344c42a7b7eSSam Leffler 345c42a7b7eSSam Leffler /* 346c42a7b7eSSam Leffler * Get the hardware key cache size. 347c42a7b7eSSam Leffler */ 348c42a7b7eSSam Leffler sc->sc_keymax = ath_hal_keycachesize(ah); 349e8fd88a3SSam Leffler if (sc->sc_keymax > ATH_KEYMAX) { 350e8fd88a3SSam Leffler if_printf(ifp, "Warning, using only %u of %u key cache slots\n", 351e8fd88a3SSam Leffler ATH_KEYMAX, sc->sc_keymax); 352e8fd88a3SSam Leffler sc->sc_keymax = ATH_KEYMAX; 353c42a7b7eSSam Leffler } 354c42a7b7eSSam Leffler /* 355c42a7b7eSSam Leffler * Reset the key cache since some parts do not 356c42a7b7eSSam Leffler * reset the contents on initial power up. 357c42a7b7eSSam Leffler */ 358c42a7b7eSSam Leffler for (i = 0; i < sc->sc_keymax; i++) 359c42a7b7eSSam Leffler ath_hal_keyreset(ah, i); 360c42a7b7eSSam Leffler 361c42a7b7eSSam Leffler /* 362b032f27cSSam Leffler * Collect the default channel list. 3635591b213SSam Leffler */ 364b032f27cSSam Leffler error = ath_getchannels(sc); 3655591b213SSam Leffler if (error != 0) 3665591b213SSam Leffler goto bad; 3675591b213SSam Leffler 3685591b213SSam Leffler /* 3695591b213SSam Leffler * Setup rate tables for all potential media types. 3705591b213SSam Leffler */ 3715591b213SSam Leffler ath_rate_setup(sc, IEEE80211_MODE_11A); 3725591b213SSam Leffler ath_rate_setup(sc, IEEE80211_MODE_11B); 3735591b213SSam Leffler ath_rate_setup(sc, IEEE80211_MODE_11G); 374c42a7b7eSSam Leffler ath_rate_setup(sc, IEEE80211_MODE_TURBO_A); 375c42a7b7eSSam Leffler ath_rate_setup(sc, IEEE80211_MODE_TURBO_G); 37668e8e04eSSam Leffler ath_rate_setup(sc, IEEE80211_MODE_STURBO_A); 37768e8e04eSSam Leffler ath_rate_setup(sc, IEEE80211_MODE_11NA); 37868e8e04eSSam Leffler ath_rate_setup(sc, IEEE80211_MODE_11NG); 379724c193aSSam Leffler ath_rate_setup(sc, IEEE80211_MODE_HALF); 380724c193aSSam Leffler ath_rate_setup(sc, IEEE80211_MODE_QUARTER); 381aaa70f2fSSam Leffler 382c42a7b7eSSam Leffler /* NB: setup here so ath_rate_update is happy */ 383c42a7b7eSSam Leffler ath_setcurmode(sc, IEEE80211_MODE_11A); 3845591b213SSam Leffler 385c42a7b7eSSam Leffler /* 386c42a7b7eSSam Leffler * Allocate tx+rx descriptors and populate the lists. 387c42a7b7eSSam Leffler */ 3885591b213SSam Leffler error = ath_desc_alloc(sc); 3895591b213SSam Leffler if (error != 0) { 3905591b213SSam Leffler if_printf(ifp, "failed to allocate descriptors: %d\n", error); 3915591b213SSam Leffler goto bad; 3925591b213SSam Leffler } 3932e986da5SSam Leffler callout_init_mtx(&sc->sc_cal_ch, &sc->sc_mtx, 0); 3942e986da5SSam Leffler callout_init_mtx(&sc->sc_wd_ch, &sc->sc_mtx, 0); 3955591b213SSam Leffler 396f0b2a0beSSam Leffler ATH_TXBUF_LOCK_INIT(sc); 3975591b213SSam Leffler 3980bbf5441SSam Leffler sc->sc_tq = taskqueue_create("ath_taskq", M_NOWAIT, 3990bbf5441SSam Leffler taskqueue_thread_enqueue, &sc->sc_tq); 4000bbf5441SSam Leffler taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, 4010bbf5441SSam Leffler "%s taskq", ifp->if_xname); 4020bbf5441SSam Leffler 4035591b213SSam Leffler TASK_INIT(&sc->sc_rxtask, 0, ath_rx_proc, sc); 4045591b213SSam Leffler TASK_INIT(&sc->sc_bmisstask, 0, ath_bmiss_proc, sc); 405c42a7b7eSSam Leffler TASK_INIT(&sc->sc_bstucktask,0, ath_bstuck_proc, sc); 4065591b213SSam Leffler 4075591b213SSam Leffler /* 408c42a7b7eSSam Leffler * Allocate hardware transmit queues: one queue for 409c42a7b7eSSam Leffler * beacon frames and one data queue for each QoS 4104fa8d4efSDaniel Eischen * priority. Note that the hal handles resetting 411c42a7b7eSSam Leffler * these queues at the needed time. 412c42a7b7eSSam Leffler * 413c42a7b7eSSam Leffler * XXX PS-Poll 4145591b213SSam Leffler */ 41580d2765fSSam Leffler sc->sc_bhalq = ath_beaconq_setup(ah); 4165591b213SSam Leffler if (sc->sc_bhalq == (u_int) -1) { 4175591b213SSam Leffler if_printf(ifp, "unable to setup a beacon xmit queue!\n"); 418c42a7b7eSSam Leffler error = EIO; 419b28b4653SSam Leffler goto bad2; 4205591b213SSam Leffler } 421c42a7b7eSSam Leffler sc->sc_cabq = ath_txq_setup(sc, HAL_TX_QUEUE_CAB, 0); 422c42a7b7eSSam Leffler if (sc->sc_cabq == NULL) { 423c42a7b7eSSam Leffler if_printf(ifp, "unable to setup CAB xmit queue!\n"); 424c42a7b7eSSam Leffler error = EIO; 425c42a7b7eSSam Leffler goto bad2; 426c42a7b7eSSam Leffler } 427c42a7b7eSSam Leffler /* NB: insure BK queue is the lowest priority h/w queue */ 428c42a7b7eSSam Leffler if (!ath_tx_setup(sc, WME_AC_BK, HAL_WME_AC_BK)) { 429c42a7b7eSSam Leffler if_printf(ifp, "unable to setup xmit queue for %s traffic!\n", 430c42a7b7eSSam Leffler ieee80211_wme_acnames[WME_AC_BK]); 431c42a7b7eSSam Leffler error = EIO; 432c42a7b7eSSam Leffler goto bad2; 433c42a7b7eSSam Leffler } 434c42a7b7eSSam Leffler if (!ath_tx_setup(sc, WME_AC_BE, HAL_WME_AC_BE) || 435c42a7b7eSSam Leffler !ath_tx_setup(sc, WME_AC_VI, HAL_WME_AC_VI) || 436c42a7b7eSSam Leffler !ath_tx_setup(sc, WME_AC_VO, HAL_WME_AC_VO)) { 437c42a7b7eSSam Leffler /* 438c42a7b7eSSam Leffler * Not enough hardware tx queues to properly do WME; 439c42a7b7eSSam Leffler * just punt and assign them all to the same h/w queue. 440c42a7b7eSSam Leffler * We could do a better job of this if, for example, 441c42a7b7eSSam Leffler * we allocate queues when we switch from station to 442c42a7b7eSSam Leffler * AP mode. 443c42a7b7eSSam Leffler */ 444c42a7b7eSSam Leffler if (sc->sc_ac2q[WME_AC_VI] != NULL) 445c42a7b7eSSam Leffler ath_tx_cleanupq(sc, sc->sc_ac2q[WME_AC_VI]); 446c42a7b7eSSam Leffler if (sc->sc_ac2q[WME_AC_BE] != NULL) 447c42a7b7eSSam Leffler ath_tx_cleanupq(sc, sc->sc_ac2q[WME_AC_BE]); 448c42a7b7eSSam Leffler sc->sc_ac2q[WME_AC_BE] = sc->sc_ac2q[WME_AC_BK]; 449c42a7b7eSSam Leffler sc->sc_ac2q[WME_AC_VI] = sc->sc_ac2q[WME_AC_BK]; 450c42a7b7eSSam Leffler sc->sc_ac2q[WME_AC_VO] = sc->sc_ac2q[WME_AC_BK]; 451c42a7b7eSSam Leffler } 452c42a7b7eSSam Leffler 453c42a7b7eSSam Leffler /* 454c42a7b7eSSam Leffler * Special case certain configurations. Note the 455c42a7b7eSSam Leffler * CAB queue is handled by these specially so don't 456c42a7b7eSSam Leffler * include them when checking the txq setup mask. 457c42a7b7eSSam Leffler */ 458c42a7b7eSSam Leffler switch (sc->sc_txqsetup &~ (1<<sc->sc_cabq->axq_qnum)) { 459c42a7b7eSSam Leffler case 0x01: 460c42a7b7eSSam Leffler TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc_q0, sc); 461c42a7b7eSSam Leffler break; 462c42a7b7eSSam Leffler case 0x0f: 463c42a7b7eSSam Leffler TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc_q0123, sc); 464c42a7b7eSSam Leffler break; 465c42a7b7eSSam Leffler default: 466c42a7b7eSSam Leffler TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc, sc); 467c42a7b7eSSam Leffler break; 468c42a7b7eSSam Leffler } 469c42a7b7eSSam Leffler 470c42a7b7eSSam Leffler /* 471c42a7b7eSSam Leffler * Setup rate control. Some rate control modules 472c42a7b7eSSam Leffler * call back to change the anntena state so expose 473c42a7b7eSSam Leffler * the necessary entry points. 474c42a7b7eSSam Leffler * XXX maybe belongs in struct ath_ratectrl? 475c42a7b7eSSam Leffler */ 476c42a7b7eSSam Leffler sc->sc_setdefantenna = ath_setdefantenna; 477c42a7b7eSSam Leffler sc->sc_rc = ath_rate_attach(sc); 478c42a7b7eSSam Leffler if (sc->sc_rc == NULL) { 479c42a7b7eSSam Leffler error = EIO; 480c42a7b7eSSam Leffler goto bad2; 481c42a7b7eSSam Leffler } 482c42a7b7eSSam Leffler 4833e50ec2cSSam Leffler sc->sc_blinking = 0; 484c42a7b7eSSam Leffler sc->sc_ledstate = 1; 4853e50ec2cSSam Leffler sc->sc_ledon = 0; /* low true */ 4863e50ec2cSSam Leffler sc->sc_ledidle = (2700*hz)/1000; /* 2.7sec */ 4873e50ec2cSSam Leffler callout_init(&sc->sc_ledtimer, CALLOUT_MPSAFE); 488c42a7b7eSSam Leffler /* 489c42a7b7eSSam Leffler * Auto-enable soft led processing for IBM cards and for 490c42a7b7eSSam Leffler * 5211 minipci cards. Users can also manually enable/disable 491c42a7b7eSSam Leffler * support with a sysctl. 492c42a7b7eSSam Leffler */ 493c42a7b7eSSam Leffler sc->sc_softled = (devid == AR5212_DEVID_IBM || devid == AR5211_DEVID); 494c42a7b7eSSam Leffler if (sc->sc_softled) { 495869ff02eSSam Leffler ath_hal_gpioCfgOutput(ah, sc->sc_ledpin, 496869ff02eSSam Leffler HAL_GPIO_MUX_MAC_NETWORK_LED); 4973e50ec2cSSam Leffler ath_hal_gpioset(ah, sc->sc_ledpin, !sc->sc_ledon); 498c42a7b7eSSam Leffler } 4995591b213SSam Leffler 5005591b213SSam Leffler ifp->if_softc = sc; 5015591b213SSam Leffler ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; 5025591b213SSam Leffler ifp->if_start = ath_start; 5035591b213SSam Leffler ifp->if_ioctl = ath_ioctl; 5045591b213SSam Leffler ifp->if_init = ath_init; 505e50d35e6SMaxim Sobolev IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); 506e50d35e6SMaxim Sobolev ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; 507154b8df2SMax Laier IFQ_SET_READY(&ifp->if_snd); 5085591b213SSam Leffler 509c42a7b7eSSam Leffler ic->ic_ifp = ifp; 5105591b213SSam Leffler /* XXX not right but it's not used anywhere important */ 5115591b213SSam Leffler ic->ic_phytype = IEEE80211_T_OFDM; 5125591b213SSam Leffler ic->ic_opmode = IEEE80211_M_STA; 513c42a7b7eSSam Leffler ic->ic_caps = 514c43feedeSSam Leffler IEEE80211_C_STA /* station mode */ 515c43feedeSSam Leffler | IEEE80211_C_IBSS /* ibss, nee adhoc, mode */ 516fe32c3efSSam Leffler | IEEE80211_C_HOSTAP /* hostap mode */ 517fe32c3efSSam Leffler | IEEE80211_C_MONITOR /* monitor mode */ 5187a04dc27SSam Leffler | IEEE80211_C_AHDEMO /* adhoc demo mode */ 519b032f27cSSam Leffler | IEEE80211_C_WDS /* 4-address traffic works */ 52059aa14a9SRui Paulo | IEEE80211_C_MBSS /* mesh point link mode */ 521fe32c3efSSam Leffler | IEEE80211_C_SHPREAMBLE /* short preamble supported */ 522c42a7b7eSSam Leffler | IEEE80211_C_SHSLOT /* short slot time supported */ 523c42a7b7eSSam Leffler | IEEE80211_C_WPA /* capable of WPA1+WPA2 */ 52468e8e04eSSam Leffler | IEEE80211_C_BGSCAN /* capable of bg scanning */ 52568e8e04eSSam Leffler | IEEE80211_C_TXFRAG /* handle tx frags */ 52601e7e035SSam Leffler ; 527c42a7b7eSSam Leffler /* 528c42a7b7eSSam Leffler * Query the hal to figure out h/w crypto support. 529c42a7b7eSSam Leffler */ 530c42a7b7eSSam Leffler if (ath_hal_ciphersupported(ah, HAL_CIPHER_WEP)) 531b032f27cSSam Leffler ic->ic_cryptocaps |= IEEE80211_CRYPTO_WEP; 532c42a7b7eSSam Leffler if (ath_hal_ciphersupported(ah, HAL_CIPHER_AES_OCB)) 533b032f27cSSam Leffler ic->ic_cryptocaps |= IEEE80211_CRYPTO_AES_OCB; 534c42a7b7eSSam Leffler if (ath_hal_ciphersupported(ah, HAL_CIPHER_AES_CCM)) 535b032f27cSSam Leffler ic->ic_cryptocaps |= IEEE80211_CRYPTO_AES_CCM; 536c42a7b7eSSam Leffler if (ath_hal_ciphersupported(ah, HAL_CIPHER_CKIP)) 537b032f27cSSam Leffler ic->ic_cryptocaps |= IEEE80211_CRYPTO_CKIP; 538c42a7b7eSSam Leffler if (ath_hal_ciphersupported(ah, HAL_CIPHER_TKIP)) { 539b032f27cSSam Leffler ic->ic_cryptocaps |= IEEE80211_CRYPTO_TKIP; 540c42a7b7eSSam Leffler /* 541c42a7b7eSSam Leffler * Check if h/w does the MIC and/or whether the 542c42a7b7eSSam Leffler * separate key cache entries are required to 543c42a7b7eSSam Leffler * handle both tx+rx MIC keys. 544c42a7b7eSSam Leffler */ 545c42a7b7eSSam Leffler if (ath_hal_ciphersupported(ah, HAL_CIPHER_MIC)) 546b032f27cSSam Leffler ic->ic_cryptocaps |= IEEE80211_CRYPTO_TKIPMIC; 5475901d2d3SSam Leffler /* 5485901d2d3SSam Leffler * If the h/w supports storing tx+rx MIC keys 5495901d2d3SSam Leffler * in one cache slot automatically enable use. 5505901d2d3SSam Leffler */ 5515901d2d3SSam Leffler if (ath_hal_hastkipsplit(ah) || 5525901d2d3SSam Leffler !ath_hal_settkipsplit(ah, AH_FALSE)) 553c42a7b7eSSam Leffler sc->sc_splitmic = 1; 554b032f27cSSam Leffler /* 555b032f27cSSam Leffler * If the h/w can do TKIP MIC together with WME then 556b032f27cSSam Leffler * we use it; otherwise we force the MIC to be done 557b032f27cSSam Leffler * in software by the net80211 layer. 558b032f27cSSam Leffler */ 559b032f27cSSam Leffler if (ath_hal_haswmetkipmic(ah)) 560b032f27cSSam Leffler sc->sc_wmetkipmic = 1; 561c42a7b7eSSam Leffler } 562e8fd88a3SSam Leffler sc->sc_hasclrkey = ath_hal_ciphersupported(ah, HAL_CIPHER_CLR); 5639ac01d39SRui Paulo /* 5641ac5dac2SRui Paulo * Check for multicast key search support. 5659ac01d39SRui Paulo */ 5669ac01d39SRui Paulo if (ath_hal_hasmcastkeysearch(sc->sc_ah) && 5679ac01d39SRui Paulo !ath_hal_getmcastkeysearch(sc->sc_ah)) { 5689ac01d39SRui Paulo ath_hal_setmcastkeysearch(sc->sc_ah, 1); 5699ac01d39SRui Paulo } 570e8fd88a3SSam Leffler sc->sc_mcastkey = ath_hal_getmcastkeysearch(ah); 571c42a7b7eSSam Leffler /* 5725901d2d3SSam Leffler * Mark key cache slots associated with global keys 5735901d2d3SSam Leffler * as in use. If we knew TKIP was not to be used we 5745901d2d3SSam Leffler * could leave the +32, +64, and +32+64 slots free. 5755901d2d3SSam Leffler */ 5765901d2d3SSam Leffler for (i = 0; i < IEEE80211_WEP_NKID; i++) { 5775901d2d3SSam Leffler setbit(sc->sc_keymap, i); 5785901d2d3SSam Leffler setbit(sc->sc_keymap, i+64); 5795901d2d3SSam Leffler if (sc->sc_splitmic) { 5805901d2d3SSam Leffler setbit(sc->sc_keymap, i+32); 5815901d2d3SSam Leffler setbit(sc->sc_keymap, i+32+64); 5825901d2d3SSam Leffler } 5835901d2d3SSam Leffler } 5845901d2d3SSam Leffler /* 585c42a7b7eSSam Leffler * TPC support can be done either with a global cap or 586c42a7b7eSSam Leffler * per-packet support. The latter is not available on 587c42a7b7eSSam Leffler * all parts. We're a bit pedantic here as all parts 588c42a7b7eSSam Leffler * support a global cap. 589c42a7b7eSSam Leffler */ 590c59005e9SSam Leffler if (ath_hal_hastpc(ah) || ath_hal_hastxpowlimit(ah)) 591c42a7b7eSSam Leffler ic->ic_caps |= IEEE80211_C_TXPMGT; 592c42a7b7eSSam Leffler 593c42a7b7eSSam Leffler /* 594c42a7b7eSSam Leffler * Mark WME capability only if we have sufficient 595c42a7b7eSSam Leffler * hardware queues to do proper priority scheduling. 596c42a7b7eSSam Leffler */ 597c42a7b7eSSam Leffler if (sc->sc_ac2q[WME_AC_BE] != sc->sc_ac2q[WME_AC_BK]) 598c42a7b7eSSam Leffler ic->ic_caps |= IEEE80211_C_WME; 599c42a7b7eSSam Leffler /* 600e8fd88a3SSam Leffler * Check for misc other capabilities. 601c42a7b7eSSam Leffler */ 602c42a7b7eSSam Leffler if (ath_hal_hasbursting(ah)) 603c42a7b7eSSam Leffler ic->ic_caps |= IEEE80211_C_BURST; 604b032f27cSSam Leffler sc->sc_hasbmask = ath_hal_hasbssidmask(ah); 60559aa14a9SRui Paulo sc->sc_hasbmatch = ath_hal_hasbssidmatch(ah); 606b032f27cSSam Leffler sc->sc_hastsfadd = ath_hal_hastsfadjust(ah); 60768e8e04eSSam Leffler if (ath_hal_hasfastframes(ah)) 60868e8e04eSSam Leffler ic->ic_caps |= IEEE80211_C_FF; 60959efa8b5SSam Leffler wmodes = ath_hal_getwirelessmodes(ah); 610411373ebSSam Leffler if (wmodes & (HAL_MODE_108G|HAL_MODE_TURBO)) 61168e8e04eSSam Leffler ic->ic_caps |= IEEE80211_C_TURBOP; 612584f7327SSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 61310ad9a77SSam Leffler if (ath_hal_macversion(ah) > 0x78) { 61410ad9a77SSam Leffler ic->ic_caps |= IEEE80211_C_TDMA; /* capable of TDMA */ 61510ad9a77SSam Leffler ic->ic_tdma_update = ath_tdma_update; 61610ad9a77SSam Leffler } 61710ad9a77SSam Leffler #endif 618*67397d39SAdrian Chadd 619*67397d39SAdrian Chadd /* 620*67397d39SAdrian Chadd * The if_ath 11n support is completely not ready for normal use. 621*67397d39SAdrian Chadd * Enabling this option will likely break everything and everything. 622*67397d39SAdrian Chadd * Don't think of doing that unless you know what you're doing. 623*67397d39SAdrian Chadd */ 624*67397d39SAdrian Chadd 625*67397d39SAdrian Chadd #ifdef DO_ATH_11N 626*67397d39SAdrian Chadd /* 627*67397d39SAdrian Chadd * Query HT capabilities 628*67397d39SAdrian Chadd */ 629*67397d39SAdrian Chadd if (ath_hal_getcapability(ah, HAL_CAP_HT, 0, NULL) == HAL_OK && 630*67397d39SAdrian Chadd (wmodes & (HAL_MODE_HT20 | HAL_MODE_HT40))) { 631*67397d39SAdrian Chadd int rxs, txs; 632*67397d39SAdrian Chadd 633*67397d39SAdrian Chadd device_printf(sc->sc_dev, "[HT] enabling HT modes\n"); 634*67397d39SAdrian Chadd ic->ic_htcaps = IEEE80211_HTC_HT /* HT operation */ 635*67397d39SAdrian Chadd | IEEE80211_HTC_AMPDU /* A-MPDU tx/rx */ 636*67397d39SAdrian Chadd | IEEE80211_HTC_AMSDU /* A-MSDU tx/rx */ 637*67397d39SAdrian Chadd | IEEE80211_HTCAP_MAXAMSDU_3839 /* max A-MSDU length */ 638*67397d39SAdrian Chadd | IEEE80211_HTCAP_SHORTGI20 /* short GI in 20MHz */ 639*67397d39SAdrian Chadd | IEEE80211_HTCAP_SMPS_OFF; /* SM power save off */ 640*67397d39SAdrian Chadd ; 641*67397d39SAdrian Chadd 642*67397d39SAdrian Chadd if (wmodes & HAL_MODE_HT40) 643*67397d39SAdrian Chadd ic->ic_htcaps |= IEEE80211_HTCAP_CHWIDTH40 644*67397d39SAdrian Chadd | IEEE80211_HTCAP_SHORTGI40; 645*67397d39SAdrian Chadd 646*67397d39SAdrian Chadd /* 647*67397d39SAdrian Chadd * rx/tx stream is not currently used anywhere; it needs to be taken 648*67397d39SAdrian Chadd * into account when negotiating which MCS rates it'll receive and 649*67397d39SAdrian Chadd * what MCS rates are available for TX. 650*67397d39SAdrian Chadd */ 651*67397d39SAdrian Chadd (void) ath_hal_getcapability(ah, HAL_CAP_STREAMS, 0, &rxs); 652*67397d39SAdrian Chadd (void) ath_hal_getcapability(ah, HAL_CAP_STREAMS, 1, &txs); 653*67397d39SAdrian Chadd 654*67397d39SAdrian Chadd ath_hal_getrxchainmask(ah, &sc->sc_rxchainmask); 655*67397d39SAdrian Chadd ath_hal_gettxchainmask(ah, &sc->sc_txchainmask); 656*67397d39SAdrian Chadd 657*67397d39SAdrian Chadd ic->ic_txstream = txs; 658*67397d39SAdrian Chadd ic->ic_rxstream = rxs; 659*67397d39SAdrian Chadd 660*67397d39SAdrian Chadd device_printf(sc->sc_dev, "[HT] %d RX streams; %d TX streams\n", rxs, txs); 661*67397d39SAdrian Chadd } 662*67397d39SAdrian Chadd #endif 663*67397d39SAdrian Chadd 664c42a7b7eSSam Leffler /* 665c42a7b7eSSam Leffler * Indicate we need the 802.11 header padded to a 666c42a7b7eSSam Leffler * 32-bit boundary for 4-address and QoS frames. 667c42a7b7eSSam Leffler */ 668c42a7b7eSSam Leffler ic->ic_flags |= IEEE80211_F_DATAPAD; 669c42a7b7eSSam Leffler 670c42a7b7eSSam Leffler /* 671c42a7b7eSSam Leffler * Query the hal about antenna support. 672c42a7b7eSSam Leffler */ 673c42a7b7eSSam Leffler sc->sc_defant = ath_hal_getdefantenna(ah); 674c42a7b7eSSam Leffler 675c42a7b7eSSam Leffler /* 676c42a7b7eSSam Leffler * Not all chips have the VEOL support we want to 677c42a7b7eSSam Leffler * use with IBSS beacons; check here for it. 678c42a7b7eSSam Leffler */ 679c42a7b7eSSam Leffler sc->sc_hasveol = ath_hal_hasveol(ah); 6805591b213SSam Leffler 6815591b213SSam Leffler /* get mac address from hardware */ 68229aca940SSam Leffler ath_hal_getmac(ah, macaddr); 683b032f27cSSam Leffler if (sc->sc_hasbmask) 684b032f27cSSam Leffler ath_hal_getbssidmask(ah, sc->sc_hwbssidmask); 6855591b213SSam Leffler 686b032f27cSSam Leffler /* NB: used to size node table key mapping array */ 687b032f27cSSam Leffler ic->ic_max_keyix = sc->sc_keymax; 6885591b213SSam Leffler /* call MI attach routine. */ 68929aca940SSam Leffler ieee80211_ifattach(ic, macaddr); 690b032f27cSSam Leffler ic->ic_setregdomain = ath_setregdomain; 691b032f27cSSam Leffler ic->ic_getradiocaps = ath_getradiocaps; 692b032f27cSSam Leffler sc->sc_opmode = HAL_M_STA; 693b032f27cSSam Leffler 6945591b213SSam Leffler /* override default methods */ 695b032f27cSSam Leffler ic->ic_newassoc = ath_newassoc; 696b032f27cSSam Leffler ic->ic_updateslot = ath_updateslot; 697b032f27cSSam Leffler ic->ic_wme.wme_update = ath_wme_update; 698b032f27cSSam Leffler ic->ic_vap_create = ath_vap_create; 699b032f27cSSam Leffler ic->ic_vap_delete = ath_vap_delete; 700b032f27cSSam Leffler ic->ic_raw_xmit = ath_raw_xmit; 701b032f27cSSam Leffler ic->ic_update_mcast = ath_update_mcast; 702b032f27cSSam Leffler ic->ic_update_promisc = ath_update_promisc; 7035591b213SSam Leffler ic->ic_node_alloc = ath_node_alloc; 7041e774079SSam Leffler sc->sc_node_free = ic->ic_node_free; 7055591b213SSam Leffler ic->ic_node_free = ath_node_free; 70668e8e04eSSam Leffler ic->ic_node_getsignal = ath_node_getsignal; 70768e8e04eSSam Leffler ic->ic_scan_start = ath_scan_start; 70868e8e04eSSam Leffler ic->ic_scan_end = ath_scan_end; 70968e8e04eSSam Leffler ic->ic_set_channel = ath_set_channel; 7105591b213SSam Leffler 7115463c4a4SSam Leffler ieee80211_radiotap_attach(ic, 7125463c4a4SSam Leffler &sc->sc_tx_th.wt_ihdr, sizeof(sc->sc_tx_th), 7135463c4a4SSam Leffler ATH_TX_RADIOTAP_PRESENT, 7145463c4a4SSam Leffler &sc->sc_rx_th.wr_ihdr, sizeof(sc->sc_rx_th), 7155463c4a4SSam Leffler ATH_RX_RADIOTAP_PRESENT); 7165463c4a4SSam Leffler 7174866e6c2SSam Leffler /* 7184866e6c2SSam Leffler * Setup dynamic sysctl's now that country code and 7194866e6c2SSam Leffler * regdomain are available from the hal. 7204866e6c2SSam Leffler */ 7214866e6c2SSam Leffler ath_sysctlattach(sc); 722e8dabfbeSAdrian Chadd ath_sysctl_stats_attach(sc); 72373454c73SSam Leffler 724c42a7b7eSSam Leffler if (bootverbose) 725c42a7b7eSSam Leffler ieee80211_announce(ic); 726c42a7b7eSSam Leffler ath_announce(sc); 7275591b213SSam Leffler return 0; 728b28b4653SSam Leffler bad2: 729c42a7b7eSSam Leffler ath_tx_cleanup(sc); 730b28b4653SSam Leffler ath_desc_free(sc); 7315591b213SSam Leffler bad: 7325591b213SSam Leffler if (ah) 7335591b213SSam Leffler ath_hal_detach(ah); 734fc74a9f9SBrooks Davis if (ifp != NULL) 735fc74a9f9SBrooks Davis if_free(ifp); 7365591b213SSam Leffler sc->sc_invalid = 1; 7375591b213SSam Leffler return error; 7385591b213SSam Leffler } 7395591b213SSam Leffler 7405591b213SSam Leffler int 7415591b213SSam Leffler ath_detach(struct ath_softc *sc) 7425591b213SSam Leffler { 743fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 7445591b213SSam Leffler 745c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n", 746c42a7b7eSSam Leffler __func__, ifp->if_flags); 7475591b213SSam Leffler 748c42a7b7eSSam Leffler /* 749c42a7b7eSSam Leffler * NB: the order of these is important: 75071b85077SSam Leffler * o stop the chip so no more interrupts will fire 751c42a7b7eSSam Leffler * o call the 802.11 layer before detaching the hal to 752c42a7b7eSSam Leffler * insure callbacks into the driver to delete global 753c42a7b7eSSam Leffler * key cache entries can be handled 75471b85077SSam Leffler * o free the taskqueue which drains any pending tasks 755c42a7b7eSSam Leffler * o reclaim the tx queue data structures after calling 756c42a7b7eSSam Leffler * the 802.11 layer as we'll get called back to reclaim 757c42a7b7eSSam Leffler * node state and potentially want to use them 758c42a7b7eSSam Leffler * o to cleanup the tx queues the hal is called, so detach 759c42a7b7eSSam Leffler * it last 760c42a7b7eSSam Leffler * Other than that, it's straightforward... 761c42a7b7eSSam Leffler */ 76271b85077SSam Leffler ath_stop(ifp); 763b032f27cSSam Leffler ieee80211_ifdetach(ifp->if_l2com); 76471b85077SSam Leffler taskqueue_free(sc->sc_tq); 76586e07743SSam Leffler #ifdef ATH_TX99_DIAG 76686e07743SSam Leffler if (sc->sc_tx99 != NULL) 76786e07743SSam Leffler sc->sc_tx99->detach(sc->sc_tx99); 76886e07743SSam Leffler #endif 769c42a7b7eSSam Leffler ath_rate_detach(sc->sc_rc); 7705591b213SSam Leffler ath_desc_free(sc); 771c42a7b7eSSam Leffler ath_tx_cleanup(sc); 77271b85077SSam Leffler ath_hal_detach(sc->sc_ah); /* NB: sets chip in full sleep */ 773c4c6f08fSRuslan Ermilov if_free(ifp); 774f0b2a0beSSam Leffler 7755591b213SSam Leffler return 0; 7765591b213SSam Leffler } 7775591b213SSam Leffler 778b032f27cSSam Leffler /* 779b032f27cSSam Leffler * MAC address handling for multiple BSS on the same radio. 780b032f27cSSam Leffler * The first vap uses the MAC address from the EEPROM. For 781b032f27cSSam Leffler * subsequent vap's we set the U/L bit (bit 1) in the MAC 782b032f27cSSam Leffler * address and use the next six bits as an index. 783b032f27cSSam Leffler */ 784b032f27cSSam Leffler static void 785b032f27cSSam Leffler assign_address(struct ath_softc *sc, uint8_t mac[IEEE80211_ADDR_LEN], int clone) 786b032f27cSSam Leffler { 787b032f27cSSam Leffler int i; 788b032f27cSSam Leffler 789b032f27cSSam Leffler if (clone && sc->sc_hasbmask) { 790b032f27cSSam Leffler /* NB: we only do this if h/w supports multiple bssid */ 791b032f27cSSam Leffler for (i = 0; i < 8; i++) 792b032f27cSSam Leffler if ((sc->sc_bssidmask & (1<<i)) == 0) 793b032f27cSSam Leffler break; 794b032f27cSSam Leffler if (i != 0) 795b032f27cSSam Leffler mac[0] |= (i << 2)|0x2; 796b032f27cSSam Leffler } else 797b032f27cSSam Leffler i = 0; 798b032f27cSSam Leffler sc->sc_bssidmask |= 1<<i; 799b032f27cSSam Leffler sc->sc_hwbssidmask[0] &= ~mac[0]; 800b032f27cSSam Leffler if (i == 0) 801b032f27cSSam Leffler sc->sc_nbssid0++; 802b032f27cSSam Leffler } 803b032f27cSSam Leffler 804b032f27cSSam Leffler static void 805b032f27cSSam Leffler reclaim_address(struct ath_softc *sc, const uint8_t mac[IEEE80211_ADDR_LEN]) 806b032f27cSSam Leffler { 807b032f27cSSam Leffler int i = mac[0] >> 2; 808b032f27cSSam Leffler uint8_t mask; 809b032f27cSSam Leffler 810b032f27cSSam Leffler if (i != 0 || --sc->sc_nbssid0 == 0) { 811b032f27cSSam Leffler sc->sc_bssidmask &= ~(1<<i); 812b032f27cSSam Leffler /* recalculate bssid mask from remaining addresses */ 813b032f27cSSam Leffler mask = 0xff; 814b032f27cSSam Leffler for (i = 1; i < 8; i++) 815b032f27cSSam Leffler if (sc->sc_bssidmask & (1<<i)) 816b032f27cSSam Leffler mask &= ~((i<<2)|0x2); 817b032f27cSSam Leffler sc->sc_hwbssidmask[0] |= mask; 818b032f27cSSam Leffler } 819b032f27cSSam Leffler } 820b032f27cSSam Leffler 821b032f27cSSam Leffler /* 822b032f27cSSam Leffler * Assign a beacon xmit slot. We try to space out 823b032f27cSSam Leffler * assignments so when beacons are staggered the 824b032f27cSSam Leffler * traffic coming out of the cab q has maximal time 825b032f27cSSam Leffler * to go out before the next beacon is scheduled. 826b032f27cSSam Leffler */ 827b032f27cSSam Leffler static int 828b032f27cSSam Leffler assign_bslot(struct ath_softc *sc) 829b032f27cSSam Leffler { 830b032f27cSSam Leffler u_int slot, free; 831b032f27cSSam Leffler 832b032f27cSSam Leffler free = 0; 833b032f27cSSam Leffler for (slot = 0; slot < ATH_BCBUF; slot++) 834b032f27cSSam Leffler if (sc->sc_bslot[slot] == NULL) { 835b032f27cSSam Leffler if (sc->sc_bslot[(slot+1)%ATH_BCBUF] == NULL && 836b032f27cSSam Leffler sc->sc_bslot[(slot-1)%ATH_BCBUF] == NULL) 837b032f27cSSam Leffler return slot; 838b032f27cSSam Leffler free = slot; 839b032f27cSSam Leffler /* NB: keep looking for a double slot */ 840b032f27cSSam Leffler } 841b032f27cSSam Leffler return free; 842b032f27cSSam Leffler } 843b032f27cSSam Leffler 844b032f27cSSam Leffler static struct ieee80211vap * 845b032f27cSSam Leffler ath_vap_create(struct ieee80211com *ic, 846b032f27cSSam Leffler const char name[IFNAMSIZ], int unit, int opmode, int flags, 847b032f27cSSam Leffler const uint8_t bssid[IEEE80211_ADDR_LEN], 848b032f27cSSam Leffler const uint8_t mac0[IEEE80211_ADDR_LEN]) 849b032f27cSSam Leffler { 850b032f27cSSam Leffler struct ath_softc *sc = ic->ic_ifp->if_softc; 851b032f27cSSam Leffler struct ath_vap *avp; 852b032f27cSSam Leffler struct ieee80211vap *vap; 853b032f27cSSam Leffler uint8_t mac[IEEE80211_ADDR_LEN]; 854b032f27cSSam Leffler int ic_opmode, needbeacon, error; 855b032f27cSSam Leffler 856b032f27cSSam Leffler avp = (struct ath_vap *) malloc(sizeof(struct ath_vap), 857b032f27cSSam Leffler M_80211_VAP, M_WAITOK | M_ZERO); 858b032f27cSSam Leffler needbeacon = 0; 859b032f27cSSam Leffler IEEE80211_ADDR_COPY(mac, mac0); 860b032f27cSSam Leffler 861b032f27cSSam Leffler ATH_LOCK(sc); 862a8962181SSam Leffler ic_opmode = opmode; /* default to opmode of new vap */ 863b032f27cSSam Leffler switch (opmode) { 864b032f27cSSam Leffler case IEEE80211_M_STA: 865a8962181SSam Leffler if (sc->sc_nstavaps != 0) { /* XXX only 1 for now */ 866b032f27cSSam Leffler device_printf(sc->sc_dev, "only 1 sta vap supported\n"); 867b032f27cSSam Leffler goto bad; 868b032f27cSSam Leffler } 869b032f27cSSam Leffler if (sc->sc_nvaps) { 870b032f27cSSam Leffler /* 871a8962181SSam Leffler * With multiple vaps we must fall back 872a8962181SSam Leffler * to s/w beacon miss handling. 873b032f27cSSam Leffler */ 874b032f27cSSam Leffler flags |= IEEE80211_CLONE_NOBEACONS; 875b032f27cSSam Leffler } 876a8962181SSam Leffler if (flags & IEEE80211_CLONE_NOBEACONS) { 877a8962181SSam Leffler /* 878a8962181SSam Leffler * Station mode w/o beacons are implemented w/ AP mode. 879a8962181SSam Leffler */ 880b032f27cSSam Leffler ic_opmode = IEEE80211_M_HOSTAP; 881a8962181SSam Leffler } 882b032f27cSSam Leffler break; 883b032f27cSSam Leffler case IEEE80211_M_IBSS: 884b032f27cSSam Leffler if (sc->sc_nvaps != 0) { /* XXX only 1 for now */ 885b032f27cSSam Leffler device_printf(sc->sc_dev, 886b032f27cSSam Leffler "only 1 ibss vap supported\n"); 887b032f27cSSam Leffler goto bad; 888b032f27cSSam Leffler } 889b032f27cSSam Leffler needbeacon = 1; 890b032f27cSSam Leffler break; 891b032f27cSSam Leffler case IEEE80211_M_AHDEMO: 892584f7327SSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 89310ad9a77SSam Leffler if (flags & IEEE80211_CLONE_TDMA) { 894a8962181SSam Leffler if (sc->sc_nvaps != 0) { 895a8962181SSam Leffler device_printf(sc->sc_dev, 896a8962181SSam Leffler "only 1 tdma vap supported\n"); 897a8962181SSam Leffler goto bad; 898a8962181SSam Leffler } 89910ad9a77SSam Leffler needbeacon = 1; 90010ad9a77SSam Leffler flags |= IEEE80211_CLONE_NOBEACONS; 90110ad9a77SSam Leffler } 902b032f27cSSam Leffler /* fall thru... */ 90310ad9a77SSam Leffler #endif 904b032f27cSSam Leffler case IEEE80211_M_MONITOR: 905b032f27cSSam Leffler if (sc->sc_nvaps != 0 && ic->ic_opmode != opmode) { 906a8962181SSam Leffler /* 907a8962181SSam Leffler * Adopt existing mode. Adding a monitor or ahdemo 908a8962181SSam Leffler * vap to an existing configuration is of dubious 909a8962181SSam Leffler * value but should be ok. 910a8962181SSam Leffler */ 911b032f27cSSam Leffler /* XXX not right for monitor mode */ 912b032f27cSSam Leffler ic_opmode = ic->ic_opmode; 913a8962181SSam Leffler } 914b032f27cSSam Leffler break; 915b032f27cSSam Leffler case IEEE80211_M_HOSTAP: 91659aa14a9SRui Paulo case IEEE80211_M_MBSS: 917b032f27cSSam Leffler needbeacon = 1; 918a8962181SSam Leffler break; 919b032f27cSSam Leffler case IEEE80211_M_WDS: 920a8962181SSam Leffler if (sc->sc_nvaps != 0 && ic->ic_opmode == IEEE80211_M_STA) { 921b032f27cSSam Leffler device_printf(sc->sc_dev, 922b032f27cSSam Leffler "wds not supported in sta mode\n"); 923b032f27cSSam Leffler goto bad; 924b032f27cSSam Leffler } 925b032f27cSSam Leffler /* 926b032f27cSSam Leffler * Silently remove any request for a unique 927b032f27cSSam Leffler * bssid; WDS vap's always share the local 928b032f27cSSam Leffler * mac address. 929b032f27cSSam Leffler */ 930b032f27cSSam Leffler flags &= ~IEEE80211_CLONE_BSSID; 931a8962181SSam Leffler if (sc->sc_nvaps == 0) 932b032f27cSSam Leffler ic_opmode = IEEE80211_M_HOSTAP; 933a8962181SSam Leffler else 934a8962181SSam Leffler ic_opmode = ic->ic_opmode; 9357d261891SRui Paulo break; 936b032f27cSSam Leffler default: 937b032f27cSSam Leffler device_printf(sc->sc_dev, "unknown opmode %d\n", opmode); 938b032f27cSSam Leffler goto bad; 939b032f27cSSam Leffler } 940b032f27cSSam Leffler /* 941b032f27cSSam Leffler * Check that a beacon buffer is available; the code below assumes it. 942b032f27cSSam Leffler */ 943b032f27cSSam Leffler if (needbeacon & STAILQ_EMPTY(&sc->sc_bbuf)) { 944b032f27cSSam Leffler device_printf(sc->sc_dev, "no beacon buffer available\n"); 945b032f27cSSam Leffler goto bad; 946b032f27cSSam Leffler } 947b032f27cSSam Leffler 948b032f27cSSam Leffler /* STA, AHDEMO? */ 94959aa14a9SRui Paulo if (opmode == IEEE80211_M_HOSTAP || opmode == IEEE80211_M_MBSS) { 950b032f27cSSam Leffler assign_address(sc, mac, flags & IEEE80211_CLONE_BSSID); 951b032f27cSSam Leffler ath_hal_setbssidmask(sc->sc_ah, sc->sc_hwbssidmask); 952b032f27cSSam Leffler } 953b032f27cSSam Leffler 954b032f27cSSam Leffler vap = &avp->av_vap; 955b032f27cSSam Leffler /* XXX can't hold mutex across if_alloc */ 956b032f27cSSam Leffler ATH_UNLOCK(sc); 957b032f27cSSam Leffler error = ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, 958b032f27cSSam Leffler bssid, mac); 959b032f27cSSam Leffler ATH_LOCK(sc); 960b032f27cSSam Leffler if (error != 0) { 961b032f27cSSam Leffler device_printf(sc->sc_dev, "%s: error %d creating vap\n", 962b032f27cSSam Leffler __func__, error); 963b032f27cSSam Leffler goto bad2; 964b032f27cSSam Leffler } 965b032f27cSSam Leffler 966b032f27cSSam Leffler /* h/w crypto support */ 967b032f27cSSam Leffler vap->iv_key_alloc = ath_key_alloc; 968b032f27cSSam Leffler vap->iv_key_delete = ath_key_delete; 969b032f27cSSam Leffler vap->iv_key_set = ath_key_set; 970b032f27cSSam Leffler vap->iv_key_update_begin = ath_key_update_begin; 971b032f27cSSam Leffler vap->iv_key_update_end = ath_key_update_end; 972b032f27cSSam Leffler 973b032f27cSSam Leffler /* override various methods */ 974b032f27cSSam Leffler avp->av_recv_mgmt = vap->iv_recv_mgmt; 975b032f27cSSam Leffler vap->iv_recv_mgmt = ath_recv_mgmt; 976b032f27cSSam Leffler vap->iv_reset = ath_reset_vap; 977b032f27cSSam Leffler vap->iv_update_beacon = ath_beacon_update; 978b032f27cSSam Leffler avp->av_newstate = vap->iv_newstate; 979b032f27cSSam Leffler vap->iv_newstate = ath_newstate; 980b032f27cSSam Leffler avp->av_bmiss = vap->iv_bmiss; 981b032f27cSSam Leffler vap->iv_bmiss = ath_bmiss_vap; 982b032f27cSSam Leffler 983b032f27cSSam Leffler avp->av_bslot = -1; 984b032f27cSSam Leffler if (needbeacon) { 985b032f27cSSam Leffler /* 986b032f27cSSam Leffler * Allocate beacon state and setup the q for buffered 987b032f27cSSam Leffler * multicast frames. We know a beacon buffer is 988b032f27cSSam Leffler * available because we checked above. 989b032f27cSSam Leffler */ 990b032f27cSSam Leffler avp->av_bcbuf = STAILQ_FIRST(&sc->sc_bbuf); 991b032f27cSSam Leffler STAILQ_REMOVE_HEAD(&sc->sc_bbuf, bf_list); 992b032f27cSSam Leffler if (opmode != IEEE80211_M_IBSS || !sc->sc_hasveol) { 993b032f27cSSam Leffler /* 994b032f27cSSam Leffler * Assign the vap to a beacon xmit slot. As above 995b032f27cSSam Leffler * this cannot fail to find a free one. 996b032f27cSSam Leffler */ 997b032f27cSSam Leffler avp->av_bslot = assign_bslot(sc); 998b032f27cSSam Leffler KASSERT(sc->sc_bslot[avp->av_bslot] == NULL, 999b032f27cSSam Leffler ("beacon slot %u not empty", avp->av_bslot)); 1000b032f27cSSam Leffler sc->sc_bslot[avp->av_bslot] = vap; 1001b032f27cSSam Leffler sc->sc_nbcnvaps++; 1002b032f27cSSam Leffler } 1003b032f27cSSam Leffler if (sc->sc_hastsfadd && sc->sc_nbcnvaps > 0) { 1004b032f27cSSam Leffler /* 1005b032f27cSSam Leffler * Multple vaps are to transmit beacons and we 1006b032f27cSSam Leffler * have h/w support for TSF adjusting; enable 1007b032f27cSSam Leffler * use of staggered beacons. 1008b032f27cSSam Leffler */ 1009b032f27cSSam Leffler sc->sc_stagbeacons = 1; 1010b032f27cSSam Leffler } 1011b032f27cSSam Leffler ath_txq_init(sc, &avp->av_mcastq, ATH_TXQ_SWQ); 1012b032f27cSSam Leffler } 1013b032f27cSSam Leffler 1014b032f27cSSam Leffler ic->ic_opmode = ic_opmode; 1015b032f27cSSam Leffler if (opmode != IEEE80211_M_WDS) { 1016b032f27cSSam Leffler sc->sc_nvaps++; 1017b032f27cSSam Leffler if (opmode == IEEE80211_M_STA) 1018b032f27cSSam Leffler sc->sc_nstavaps++; 1019fe0dd789SSam Leffler if (opmode == IEEE80211_M_MBSS) 1020fe0dd789SSam Leffler sc->sc_nmeshvaps++; 1021b032f27cSSam Leffler } 1022b032f27cSSam Leffler switch (ic_opmode) { 1023b032f27cSSam Leffler case IEEE80211_M_IBSS: 1024b032f27cSSam Leffler sc->sc_opmode = HAL_M_IBSS; 1025b032f27cSSam Leffler break; 1026b032f27cSSam Leffler case IEEE80211_M_STA: 1027b032f27cSSam Leffler sc->sc_opmode = HAL_M_STA; 1028b032f27cSSam Leffler break; 1029b032f27cSSam Leffler case IEEE80211_M_AHDEMO: 1030584f7327SSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 103110ad9a77SSam Leffler if (vap->iv_caps & IEEE80211_C_TDMA) { 103210ad9a77SSam Leffler sc->sc_tdma = 1; 103310ad9a77SSam Leffler /* NB: disable tsf adjust */ 103410ad9a77SSam Leffler sc->sc_stagbeacons = 0; 103510ad9a77SSam Leffler } 103610ad9a77SSam Leffler /* 103710ad9a77SSam Leffler * NB: adhoc demo mode is a pseudo mode; to the hal it's 103810ad9a77SSam Leffler * just ap mode. 103910ad9a77SSam Leffler */ 104010ad9a77SSam Leffler /* fall thru... */ 104110ad9a77SSam Leffler #endif 1042b032f27cSSam Leffler case IEEE80211_M_HOSTAP: 104359aa14a9SRui Paulo case IEEE80211_M_MBSS: 1044b032f27cSSam Leffler sc->sc_opmode = HAL_M_HOSTAP; 1045b032f27cSSam Leffler break; 1046b032f27cSSam Leffler case IEEE80211_M_MONITOR: 1047b032f27cSSam Leffler sc->sc_opmode = HAL_M_MONITOR; 1048b032f27cSSam Leffler break; 1049b032f27cSSam Leffler default: 1050b032f27cSSam Leffler /* XXX should not happen */ 1051b032f27cSSam Leffler break; 1052b032f27cSSam Leffler } 1053b032f27cSSam Leffler if (sc->sc_hastsfadd) { 1054b032f27cSSam Leffler /* 1055b032f27cSSam Leffler * Configure whether or not TSF adjust should be done. 1056b032f27cSSam Leffler */ 1057b032f27cSSam Leffler ath_hal_settsfadjust(sc->sc_ah, sc->sc_stagbeacons); 1058b032f27cSSam Leffler } 105910ad9a77SSam Leffler if (flags & IEEE80211_CLONE_NOBEACONS) { 106010ad9a77SSam Leffler /* 106110ad9a77SSam Leffler * Enable s/w beacon miss handling. 106210ad9a77SSam Leffler */ 106310ad9a77SSam Leffler sc->sc_swbmiss = 1; 106410ad9a77SSam Leffler } 1065b032f27cSSam Leffler ATH_UNLOCK(sc); 1066b032f27cSSam Leffler 1067b032f27cSSam Leffler /* complete setup */ 1068b032f27cSSam Leffler ieee80211_vap_attach(vap, ath_media_change, ieee80211_media_status); 1069b032f27cSSam Leffler return vap; 1070b032f27cSSam Leffler bad2: 1071b032f27cSSam Leffler reclaim_address(sc, mac); 1072b032f27cSSam Leffler ath_hal_setbssidmask(sc->sc_ah, sc->sc_hwbssidmask); 1073b032f27cSSam Leffler bad: 1074b032f27cSSam Leffler free(avp, M_80211_VAP); 1075b032f27cSSam Leffler ATH_UNLOCK(sc); 1076b032f27cSSam Leffler return NULL; 1077b032f27cSSam Leffler } 1078b032f27cSSam Leffler 1079b032f27cSSam Leffler static void 1080b032f27cSSam Leffler ath_vap_delete(struct ieee80211vap *vap) 1081b032f27cSSam Leffler { 1082b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 1083b032f27cSSam Leffler struct ifnet *ifp = ic->ic_ifp; 1084b032f27cSSam Leffler struct ath_softc *sc = ifp->if_softc; 1085b032f27cSSam Leffler struct ath_hal *ah = sc->sc_ah; 1086b032f27cSSam Leffler struct ath_vap *avp = ATH_VAP(vap); 1087b032f27cSSam Leffler 1088b032f27cSSam Leffler if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 1089b032f27cSSam Leffler /* 1090b032f27cSSam Leffler * Quiesce the hardware while we remove the vap. In 1091b032f27cSSam Leffler * particular we need to reclaim all references to 1092b032f27cSSam Leffler * the vap state by any frames pending on the tx queues. 1093b032f27cSSam Leffler */ 1094b032f27cSSam Leffler ath_hal_intrset(ah, 0); /* disable interrupts */ 1095b032f27cSSam Leffler ath_draintxq(sc); /* stop xmit side */ 1096b032f27cSSam Leffler ath_stoprecv(sc); /* stop recv side */ 1097b032f27cSSam Leffler } 1098b032f27cSSam Leffler 1099b032f27cSSam Leffler ieee80211_vap_detach(vap); 1100b032f27cSSam Leffler ATH_LOCK(sc); 1101b032f27cSSam Leffler /* 1102b032f27cSSam Leffler * Reclaim beacon state. Note this must be done before 1103b032f27cSSam Leffler * the vap instance is reclaimed as we may have a reference 1104b032f27cSSam Leffler * to it in the buffer for the beacon frame. 1105b032f27cSSam Leffler */ 1106b032f27cSSam Leffler if (avp->av_bcbuf != NULL) { 1107b032f27cSSam Leffler if (avp->av_bslot != -1) { 1108b032f27cSSam Leffler sc->sc_bslot[avp->av_bslot] = NULL; 1109b032f27cSSam Leffler sc->sc_nbcnvaps--; 1110b032f27cSSam Leffler } 1111b032f27cSSam Leffler ath_beacon_return(sc, avp->av_bcbuf); 1112b032f27cSSam Leffler avp->av_bcbuf = NULL; 1113b032f27cSSam Leffler if (sc->sc_nbcnvaps == 0) { 1114b032f27cSSam Leffler sc->sc_stagbeacons = 0; 1115b032f27cSSam Leffler if (sc->sc_hastsfadd) 1116b032f27cSSam Leffler ath_hal_settsfadjust(sc->sc_ah, 0); 1117b032f27cSSam Leffler } 1118b032f27cSSam Leffler /* 1119b032f27cSSam Leffler * Reclaim any pending mcast frames for the vap. 1120b032f27cSSam Leffler */ 1121b032f27cSSam Leffler ath_tx_draintxq(sc, &avp->av_mcastq); 1122b032f27cSSam Leffler ATH_TXQ_LOCK_DESTROY(&avp->av_mcastq); 1123b032f27cSSam Leffler } 1124b032f27cSSam Leffler /* 1125b032f27cSSam Leffler * Update bookkeeping. 1126b032f27cSSam Leffler */ 1127b032f27cSSam Leffler if (vap->iv_opmode == IEEE80211_M_STA) { 1128b032f27cSSam Leffler sc->sc_nstavaps--; 1129b032f27cSSam Leffler if (sc->sc_nstavaps == 0 && sc->sc_swbmiss) 1130b032f27cSSam Leffler sc->sc_swbmiss = 0; 113159aa14a9SRui Paulo } else if (vap->iv_opmode == IEEE80211_M_HOSTAP || 113259aa14a9SRui Paulo vap->iv_opmode == IEEE80211_M_MBSS) { 1133b032f27cSSam Leffler reclaim_address(sc, vap->iv_myaddr); 1134b032f27cSSam Leffler ath_hal_setbssidmask(ah, sc->sc_hwbssidmask); 1135fe0dd789SSam Leffler if (vap->iv_opmode == IEEE80211_M_MBSS) 1136fe0dd789SSam Leffler sc->sc_nmeshvaps--; 1137b032f27cSSam Leffler } 1138b032f27cSSam Leffler if (vap->iv_opmode != IEEE80211_M_WDS) 1139b032f27cSSam Leffler sc->sc_nvaps--; 1140584f7327SSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 114110ad9a77SSam Leffler /* TDMA operation ceases when the last vap is destroyed */ 114210ad9a77SSam Leffler if (sc->sc_tdma && sc->sc_nvaps == 0) { 114310ad9a77SSam Leffler sc->sc_tdma = 0; 114410ad9a77SSam Leffler sc->sc_swbmiss = 0; 114510ad9a77SSam Leffler } 114610ad9a77SSam Leffler #endif 1147b032f27cSSam Leffler ATH_UNLOCK(sc); 1148b032f27cSSam Leffler free(avp, M_80211_VAP); 1149b032f27cSSam Leffler 1150b032f27cSSam Leffler if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 1151b032f27cSSam Leffler /* 1152b032f27cSSam Leffler * Restart rx+tx machines if still running (RUNNING will 1153b032f27cSSam Leffler * be reset if we just destroyed the last vap). 1154b032f27cSSam Leffler */ 1155b032f27cSSam Leffler if (ath_startrecv(sc) != 0) 1156b032f27cSSam Leffler if_printf(ifp, "%s: unable to restart recv logic\n", 1157b032f27cSSam Leffler __func__); 1158c89b957aSSam Leffler if (sc->sc_beacons) { /* restart beacons */ 1159c89b957aSSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 1160c89b957aSSam Leffler if (sc->sc_tdma) 1161c89b957aSSam Leffler ath_tdma_config(sc, NULL); 1162c89b957aSSam Leffler else 1163c89b957aSSam Leffler #endif 1164b032f27cSSam Leffler ath_beacon_config(sc, NULL); 1165c89b957aSSam Leffler } 1166b032f27cSSam Leffler ath_hal_intrset(ah, sc->sc_imask); 1167b032f27cSSam Leffler } 1168b032f27cSSam Leffler } 1169b032f27cSSam Leffler 11705591b213SSam Leffler void 11715591b213SSam Leffler ath_suspend(struct ath_softc *sc) 11725591b213SSam Leffler { 1173fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 1174d3ac945bSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 11755591b213SSam Leffler 1176c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n", 1177c42a7b7eSSam Leffler __func__, ifp->if_flags); 11785591b213SSam Leffler 1179d3ac945bSSam Leffler sc->sc_resume_up = (ifp->if_flags & IFF_UP) != 0; 1180d3ac945bSSam Leffler if (ic->ic_opmode == IEEE80211_M_STA) 11815591b213SSam Leffler ath_stop(ifp); 1182d3ac945bSSam Leffler else 1183d3ac945bSSam Leffler ieee80211_suspend_all(ic); 1184d3ac945bSSam Leffler /* 1185d3ac945bSSam Leffler * NB: don't worry about putting the chip in low power 1186d3ac945bSSam Leffler * mode; pci will power off our socket on suspend and 1187f29b8b7fSWarner Losh * CardBus detaches the device. 1188d3ac945bSSam Leffler */ 1189d3ac945bSSam Leffler } 1190d3ac945bSSam Leffler 1191d3ac945bSSam Leffler /* 1192d3ac945bSSam Leffler * Reset the key cache since some parts do not reset the 1193d3ac945bSSam Leffler * contents on resume. First we clear all entries, then 1194d3ac945bSSam Leffler * re-load keys that the 802.11 layer assumes are setup 1195d3ac945bSSam Leffler * in h/w. 1196d3ac945bSSam Leffler */ 1197d3ac945bSSam Leffler static void 1198d3ac945bSSam Leffler ath_reset_keycache(struct ath_softc *sc) 1199d3ac945bSSam Leffler { 1200d3ac945bSSam Leffler struct ifnet *ifp = sc->sc_ifp; 1201d3ac945bSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 1202d3ac945bSSam Leffler struct ath_hal *ah = sc->sc_ah; 1203d3ac945bSSam Leffler int i; 1204d3ac945bSSam Leffler 1205d3ac945bSSam Leffler for (i = 0; i < sc->sc_keymax; i++) 1206d3ac945bSSam Leffler ath_hal_keyreset(ah, i); 1207d3ac945bSSam Leffler ieee80211_crypto_reload_keys(ic); 12085591b213SSam Leffler } 12095591b213SSam Leffler 12105591b213SSam Leffler void 12115591b213SSam Leffler ath_resume(struct ath_softc *sc) 12125591b213SSam Leffler { 1213fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 1214d3ac945bSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 1215d3ac945bSSam Leffler struct ath_hal *ah = sc->sc_ah; 1216d3ac945bSSam Leffler HAL_STATUS status; 12175591b213SSam Leffler 1218c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n", 1219c42a7b7eSSam Leffler __func__, ifp->if_flags); 12205591b213SSam Leffler 1221d3ac945bSSam Leffler /* 1222d3ac945bSSam Leffler * Must reset the chip before we reload the 1223d3ac945bSSam Leffler * keycache as we were powered down on suspend. 1224d3ac945bSSam Leffler */ 1225054d7b69SSam Leffler ath_hal_reset(ah, sc->sc_opmode, 1226054d7b69SSam Leffler sc->sc_curchan != NULL ? sc->sc_curchan : ic->ic_curchan, 1227054d7b69SSam Leffler AH_FALSE, &status); 1228d3ac945bSSam Leffler ath_reset_keycache(sc); 1229d3ac945bSSam Leffler if (sc->sc_resume_up) { 1230d3ac945bSSam Leffler if (ic->ic_opmode == IEEE80211_M_STA) { 1231fc74a9f9SBrooks Davis ath_init(sc); 1232394f34a5SSam Leffler /* 1233394f34a5SSam Leffler * Program the beacon registers using the last rx'd 1234394f34a5SSam Leffler * beacon frame and enable sync on the next beacon 1235394f34a5SSam Leffler * we see. This should handle the case where we 1236394f34a5SSam Leffler * wakeup and find the same AP and also the case where 1237394f34a5SSam Leffler * we wakeup and need to roam. For the latter we 1238394f34a5SSam Leffler * should get bmiss events that trigger a roam. 1239394f34a5SSam Leffler */ 1240394f34a5SSam Leffler ath_beacon_config(sc, NULL); 1241394f34a5SSam Leffler sc->sc_syncbeacon = 1; 1242d3ac945bSSam Leffler } else 1243d3ac945bSSam Leffler ieee80211_resume_all(ic); 12445591b213SSam Leffler } 1245b50c8bdeSSam Leffler if (sc->sc_softled) { 1246869ff02eSSam Leffler ath_hal_gpioCfgOutput(ah, sc->sc_ledpin, 1247869ff02eSSam Leffler HAL_GPIO_MUX_MAC_NETWORK_LED); 1248d3ac945bSSam Leffler ath_hal_gpioset(ah, sc->sc_ledpin, !sc->sc_ledon); 1249b50c8bdeSSam Leffler } 12506b59f5e3SSam Leffler } 12515591b213SSam Leffler 12525591b213SSam Leffler void 12535591b213SSam Leffler ath_shutdown(struct ath_softc *sc) 12545591b213SSam Leffler { 1255fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 12565591b213SSam Leffler 1257c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n", 1258c42a7b7eSSam Leffler __func__, ifp->if_flags); 12595591b213SSam Leffler 12605591b213SSam Leffler ath_stop(ifp); 1261d3ac945bSSam Leffler /* NB: no point powering down chip as we're about to reboot */ 12625591b213SSam Leffler } 12635591b213SSam Leffler 1264c42a7b7eSSam Leffler /* 1265c42a7b7eSSam Leffler * Interrupt handler. Most of the actual processing is deferred. 1266c42a7b7eSSam Leffler */ 12675591b213SSam Leffler void 12685591b213SSam Leffler ath_intr(void *arg) 12695591b213SSam Leffler { 12705591b213SSam Leffler struct ath_softc *sc = arg; 1271fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 12725591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 12735591b213SSam Leffler HAL_INT status; 12745591b213SSam Leffler 12755591b213SSam Leffler if (sc->sc_invalid) { 12765591b213SSam Leffler /* 1277b58b3803SSam Leffler * The hardware is not ready/present, don't touch anything. 1278b58b3803SSam Leffler * Note this can happen early on if the IRQ is shared. 12795591b213SSam Leffler */ 1280c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid; ignored\n", __func__); 12815591b213SSam Leffler return; 12825591b213SSam Leffler } 1283fdd758d4SSam Leffler if (!ath_hal_intrpend(ah)) /* shared irq, not for us */ 1284fdd758d4SSam Leffler return; 128568e8e04eSSam Leffler if ((ifp->if_flags & IFF_UP) == 0 || 128668e8e04eSSam Leffler (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { 128768e8e04eSSam Leffler HAL_INT status; 128868e8e04eSSam Leffler 1289c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags 0x%x\n", 1290c42a7b7eSSam Leffler __func__, ifp->if_flags); 12915591b213SSam Leffler ath_hal_getisr(ah, &status); /* clear ISR */ 12925591b213SSam Leffler ath_hal_intrset(ah, 0); /* disable further intr's */ 12935591b213SSam Leffler return; 12945591b213SSam Leffler } 1295c42a7b7eSSam Leffler /* 1296c42a7b7eSSam Leffler * Figure out the reason(s) for the interrupt. Note 1297c42a7b7eSSam Leffler * that the hal returns a pseudo-ISR that may include 1298c42a7b7eSSam Leffler * bits we haven't explicitly enabled so we mask the 1299c42a7b7eSSam Leffler * value to insure we only process bits we requested. 1300c42a7b7eSSam Leffler */ 13015591b213SSam Leffler ath_hal_getisr(ah, &status); /* NB: clears ISR too */ 1302c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_INTR, "%s: status 0x%x\n", __func__, status); 1303ecddff40SSam Leffler status &= sc->sc_imask; /* discard unasked for bits */ 13045591b213SSam Leffler if (status & HAL_INT_FATAL) { 13055591b213SSam Leffler sc->sc_stats.ast_hardware++; 13065591b213SSam Leffler ath_hal_intrset(ah, 0); /* disable intr's until reset */ 130716c8acaaSSam Leffler ath_fatal_proc(sc, 0); 13085591b213SSam Leffler } else { 1309c42a7b7eSSam Leffler if (status & HAL_INT_SWBA) { 1310c42a7b7eSSam Leffler /* 1311c42a7b7eSSam Leffler * Software beacon alert--time to send a beacon. 1312c42a7b7eSSam Leffler * Handle beacon transmission directly; deferring 1313c42a7b7eSSam Leffler * this is too slow to meet timing constraints 1314c42a7b7eSSam Leffler * under load. 1315c42a7b7eSSam Leffler */ 1316584f7327SSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 131710ad9a77SSam Leffler if (sc->sc_tdma) { 131810ad9a77SSam Leffler if (sc->sc_tdmaswba == 0) { 131910ad9a77SSam Leffler struct ieee80211com *ic = ifp->if_l2com; 132010ad9a77SSam Leffler struct ieee80211vap *vap = 132110ad9a77SSam Leffler TAILQ_FIRST(&ic->ic_vaps); 132210ad9a77SSam Leffler ath_tdma_beacon_send(sc, vap); 132310ad9a77SSam Leffler sc->sc_tdmaswba = 132410ad9a77SSam Leffler vap->iv_tdma->tdma_bintval; 132510ad9a77SSam Leffler } else 132610ad9a77SSam Leffler sc->sc_tdmaswba--; 132710ad9a77SSam Leffler } else 132810ad9a77SSam Leffler #endif 1329339ccfb3SSam Leffler { 1330c42a7b7eSSam Leffler ath_beacon_proc(sc, 0); 1331339ccfb3SSam Leffler #ifdef IEEE80211_SUPPORT_SUPERG 1332339ccfb3SSam Leffler /* 1333339ccfb3SSam Leffler * Schedule the rx taskq in case there's no 1334339ccfb3SSam Leffler * traffic so any frames held on the staging 1335339ccfb3SSam Leffler * queue are aged and potentially flushed. 1336339ccfb3SSam Leffler */ 1337339ccfb3SSam Leffler taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask); 1338339ccfb3SSam Leffler #endif 1339339ccfb3SSam Leffler } 1340c42a7b7eSSam Leffler } 13415591b213SSam Leffler if (status & HAL_INT_RXEOL) { 13425591b213SSam Leffler /* 13435591b213SSam Leffler * NB: the hardware should re-read the link when 13445591b213SSam Leffler * RXE bit is written, but it doesn't work at 13455591b213SSam Leffler * least on older hardware revs. 13465591b213SSam Leffler */ 13475591b213SSam Leffler sc->sc_stats.ast_rxeol++; 13485591b213SSam Leffler sc->sc_rxlink = NULL; 13495591b213SSam Leffler } 13505591b213SSam Leffler if (status & HAL_INT_TXURN) { 13515591b213SSam Leffler sc->sc_stats.ast_txurn++; 13525591b213SSam Leffler /* bump tx trigger level */ 13535591b213SSam Leffler ath_hal_updatetxtriglevel(ah, AH_TRUE); 13545591b213SSam Leffler } 13555591b213SSam Leffler if (status & HAL_INT_RX) 13560bbf5441SSam Leffler taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask); 13575591b213SSam Leffler if (status & HAL_INT_TX) 13580bbf5441SSam Leffler taskqueue_enqueue(sc->sc_tq, &sc->sc_txtask); 13595591b213SSam Leffler if (status & HAL_INT_BMISS) { 13605591b213SSam Leffler sc->sc_stats.ast_bmiss++; 13610bbf5441SSam Leffler taskqueue_enqueue(sc->sc_tq, &sc->sc_bmisstask); 13625591b213SSam Leffler } 1363c42a7b7eSSam Leffler if (status & HAL_INT_MIB) { 1364c42a7b7eSSam Leffler sc->sc_stats.ast_mib++; 1365c42a7b7eSSam Leffler /* 1366c42a7b7eSSam Leffler * Disable interrupts until we service the MIB 1367c42a7b7eSSam Leffler * interrupt; otherwise it will continue to fire. 1368c42a7b7eSSam Leffler */ 1369c42a7b7eSSam Leffler ath_hal_intrset(ah, 0); 1370c42a7b7eSSam Leffler /* 1371c42a7b7eSSam Leffler * Let the hal handle the event. We assume it will 1372c42a7b7eSSam Leffler * clear whatever condition caused the interrupt. 1373c42a7b7eSSam Leffler */ 1374ffa2cab6SSam Leffler ath_hal_mibevent(ah, &sc->sc_halstats); 1375c42a7b7eSSam Leffler ath_hal_intrset(ah, sc->sc_imask); 1376c42a7b7eSSam Leffler } 13779c4fc1e8SSam Leffler if (status & HAL_INT_RXORN) { 13789c4fc1e8SSam Leffler /* NB: hal marks HAL_INT_FATAL when RXORN is fatal */ 13799c4fc1e8SSam Leffler sc->sc_stats.ast_rxorn++; 13809c4fc1e8SSam Leffler } 13815591b213SSam Leffler } 13825591b213SSam Leffler } 13835591b213SSam Leffler 13845591b213SSam Leffler static void 13855591b213SSam Leffler ath_fatal_proc(void *arg, int pending) 13865591b213SSam Leffler { 13875591b213SSam Leffler struct ath_softc *sc = arg; 1388fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 138916c8acaaSSam Leffler u_int32_t *state; 139016c8acaaSSam Leffler u_int32_t len; 139168e8e04eSSam Leffler void *sp; 13925591b213SSam Leffler 1393c42a7b7eSSam Leffler if_printf(ifp, "hardware error; resetting\n"); 139416c8acaaSSam Leffler /* 139516c8acaaSSam Leffler * Fatal errors are unrecoverable. Typically these 139616c8acaaSSam Leffler * are caused by DMA errors. Collect h/w state from 139716c8acaaSSam Leffler * the hal so we can diagnose what's going on. 139816c8acaaSSam Leffler */ 139968e8e04eSSam Leffler if (ath_hal_getfatalstate(sc->sc_ah, &sp, &len)) { 140016c8acaaSSam Leffler KASSERT(len >= 6*sizeof(u_int32_t), ("len %u bytes", len)); 140168e8e04eSSam Leffler state = sp; 140216c8acaaSSam Leffler if_printf(ifp, "0x%08x 0x%08x 0x%08x, 0x%08x 0x%08x 0x%08x\n", 140316c8acaaSSam Leffler state[0], state[1] , state[2], state[3], 140416c8acaaSSam Leffler state[4], state[5]); 140516c8acaaSSam Leffler } 1406c42a7b7eSSam Leffler ath_reset(ifp); 14075591b213SSam Leffler } 14085591b213SSam Leffler 14095591b213SSam Leffler static void 1410b032f27cSSam Leffler ath_bmiss_vap(struct ieee80211vap *vap) 14115591b213SSam Leffler { 141259fbb257SSam Leffler /* 141359fbb257SSam Leffler * Workaround phantom bmiss interrupts by sanity-checking 141459fbb257SSam Leffler * the time of our last rx'd frame. If it is within the 141559fbb257SSam Leffler * beacon miss interval then ignore the interrupt. If it's 141659fbb257SSam Leffler * truly a bmiss we'll get another interrupt soon and that'll 141759fbb257SSam Leffler * be dispatched up for processing. Note this applies only 141859fbb257SSam Leffler * for h/w beacon miss events. 141959fbb257SSam Leffler */ 142059fbb257SSam Leffler if ((vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) == 0) { 1421a7ace843SSam Leffler struct ifnet *ifp = vap->iv_ic->ic_ifp; 1422a7ace843SSam Leffler struct ath_softc *sc = ifp->if_softc; 1423d7736e13SSam Leffler u_int64_t lastrx = sc->sc_lastrx; 1424d7736e13SSam Leffler u_int64_t tsf = ath_hal_gettsf64(sc->sc_ah); 1425d7736e13SSam Leffler u_int bmisstimeout = 1426b032f27cSSam Leffler vap->iv_bmissthreshold * vap->iv_bss->ni_intval * 1024; 1427d7736e13SSam Leffler 1428d7736e13SSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON, 1429d7736e13SSam Leffler "%s: tsf %llu lastrx %lld (%llu) bmiss %u\n", 1430d7736e13SSam Leffler __func__, (unsigned long long) tsf, 1431d7736e13SSam Leffler (unsigned long long)(tsf - lastrx), 1432d7736e13SSam Leffler (unsigned long long) lastrx, bmisstimeout); 143359fbb257SSam Leffler 143459fbb257SSam Leffler if (tsf - lastrx <= bmisstimeout) { 1435d7736e13SSam Leffler sc->sc_stats.ast_bmiss_phantom++; 143659fbb257SSam Leffler return; 143759fbb257SSam Leffler } 143859fbb257SSam Leffler } 143959fbb257SSam Leffler ATH_VAP(vap)->av_bmiss(vap); 1440e585d188SSam Leffler } 1441b032f27cSSam Leffler 1442459bc4f0SSam Leffler static int 1443459bc4f0SSam Leffler ath_hal_gethangstate(struct ath_hal *ah, uint32_t mask, uint32_t *hangs) 1444459bc4f0SSam Leffler { 1445459bc4f0SSam Leffler uint32_t rsize; 1446459bc4f0SSam Leffler void *sp; 1447459bc4f0SSam Leffler 144825c96056SAdrian Chadd if (!ath_hal_getdiagstate(ah, HAL_DIAG_CHECK_HANGS, &mask, sizeof(mask), &sp, &rsize)) 1449459bc4f0SSam Leffler return 0; 1450459bc4f0SSam Leffler KASSERT(rsize == sizeof(uint32_t), ("resultsize %u", rsize)); 1451459bc4f0SSam Leffler *hangs = *(uint32_t *)sp; 1452459bc4f0SSam Leffler return 1; 1453459bc4f0SSam Leffler } 1454459bc4f0SSam Leffler 1455b032f27cSSam Leffler static void 1456b032f27cSSam Leffler ath_bmiss_proc(void *arg, int pending) 1457b032f27cSSam Leffler { 1458b032f27cSSam Leffler struct ath_softc *sc = arg; 1459b032f27cSSam Leffler struct ifnet *ifp = sc->sc_ifp; 1460459bc4f0SSam Leffler uint32_t hangs; 1461b032f27cSSam Leffler 1462b032f27cSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: pending %u\n", __func__, pending); 1463459bc4f0SSam Leffler 1464459bc4f0SSam Leffler if (ath_hal_gethangstate(sc->sc_ah, 0xff, &hangs) && hangs != 0) { 14654fa8d4efSDaniel Eischen if_printf(ifp, "bb hang detected (0x%x), resetting\n", hangs); 1466459bc4f0SSam Leffler ath_reset(ifp); 1467459bc4f0SSam Leffler } else 1468b032f27cSSam Leffler ieee80211_beacon_miss(ifp->if_l2com); 14695591b213SSam Leffler } 14705591b213SSam Leffler 1471724c193aSSam Leffler /* 1472b032f27cSSam Leffler * Handle TKIP MIC setup to deal hardware that doesn't do MIC 1473b032f27cSSam Leffler * calcs together with WME. If necessary disable the crypto 1474b032f27cSSam Leffler * hardware and mark the 802.11 state so keys will be setup 1475b032f27cSSam Leffler * with the MIC work done in software. 1476b032f27cSSam Leffler */ 1477b032f27cSSam Leffler static void 1478b032f27cSSam Leffler ath_settkipmic(struct ath_softc *sc) 1479b032f27cSSam Leffler { 1480b032f27cSSam Leffler struct ifnet *ifp = sc->sc_ifp; 1481b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 1482b032f27cSSam Leffler 1483b032f27cSSam Leffler if ((ic->ic_cryptocaps & IEEE80211_CRYPTO_TKIP) && !sc->sc_wmetkipmic) { 1484b032f27cSSam Leffler if (ic->ic_flags & IEEE80211_F_WME) { 1485b032f27cSSam Leffler ath_hal_settkipmic(sc->sc_ah, AH_FALSE); 1486b032f27cSSam Leffler ic->ic_cryptocaps &= ~IEEE80211_CRYPTO_TKIPMIC; 1487b032f27cSSam Leffler } else { 1488b032f27cSSam Leffler ath_hal_settkipmic(sc->sc_ah, AH_TRUE); 1489b032f27cSSam Leffler ic->ic_cryptocaps |= IEEE80211_CRYPTO_TKIPMIC; 1490b032f27cSSam Leffler } 1491b032f27cSSam Leffler } 1492b032f27cSSam Leffler } 1493b032f27cSSam Leffler 14945591b213SSam Leffler static void 14955591b213SSam Leffler ath_init(void *arg) 14965591b213SSam Leffler { 14975591b213SSam Leffler struct ath_softc *sc = (struct ath_softc *) arg; 1498fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 1499b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 15005591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 15015591b213SSam Leffler HAL_STATUS status; 15025591b213SSam Leffler 1503c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags 0x%x\n", 1504c42a7b7eSSam Leffler __func__, ifp->if_flags); 15055591b213SSam Leffler 1506f0b2a0beSSam Leffler ATH_LOCK(sc); 15075591b213SSam Leffler /* 15085591b213SSam Leffler * Stop anything previously setup. This is safe 15095591b213SSam Leffler * whether this is the first time through or not. 15105591b213SSam Leffler */ 1511c42a7b7eSSam Leffler ath_stop_locked(ifp); 15125591b213SSam Leffler 15135591b213SSam Leffler /* 15145591b213SSam Leffler * The basic interface to setting the hardware in a good 15155591b213SSam Leffler * state is ``reset''. On return the hardware is known to 15165591b213SSam Leffler * be powered up and with interrupts disabled. This must 15175591b213SSam Leffler * be followed by initialization of the appropriate bits 15185591b213SSam Leffler * and then setup of the interrupt mask. 15195591b213SSam Leffler */ 1520b032f27cSSam Leffler ath_settkipmic(sc); 152159efa8b5SSam Leffler if (!ath_hal_reset(ah, sc->sc_opmode, ic->ic_curchan, AH_FALSE, &status)) { 15225591b213SSam Leffler if_printf(ifp, "unable to reset hardware; hal status %u\n", 15235591b213SSam Leffler status); 1524b032f27cSSam Leffler ATH_UNLOCK(sc); 1525b032f27cSSam Leffler return; 15265591b213SSam Leffler } 1527b032f27cSSam Leffler ath_chan_change(sc, ic->ic_curchan); 15285591b213SSam Leffler 15295591b213SSam Leffler /* 1530c59005e9SSam Leffler * Likewise this is set during reset so update 1531c59005e9SSam Leffler * state cached in the driver. 1532c59005e9SSam Leffler */ 1533c59005e9SSam Leffler sc->sc_diversity = ath_hal_getdiversity(ah); 15342dc7fcc4SSam Leffler sc->sc_lastlongcal = 0; 15352dc7fcc4SSam Leffler sc->sc_resetcal = 1; 15362dc7fcc4SSam Leffler sc->sc_lastcalreset = 0; 1537a108ab63SAdrian Chadd sc->sc_lastani = 0; 1538a108ab63SAdrian Chadd sc->sc_lastshortcal = 0; 1539a108ab63SAdrian Chadd sc->sc_doresetcal = AH_FALSE; 1540c42a7b7eSSam Leffler 1541c42a7b7eSSam Leffler /* 15425591b213SSam Leffler * Setup the hardware after reset: the key cache 15435591b213SSam Leffler * is filled as needed and the receive engine is 15445591b213SSam Leffler * set going. Frame transmit is handled entirely 15455591b213SSam Leffler * in the frame output path; there's nothing to do 15465591b213SSam Leffler * here except setup the interrupt mask. 15475591b213SSam Leffler */ 15485591b213SSam Leffler if (ath_startrecv(sc) != 0) { 15495591b213SSam Leffler if_printf(ifp, "unable to start recv logic\n"); 1550b032f27cSSam Leffler ATH_UNLOCK(sc); 1551b032f27cSSam Leffler return; 15525591b213SSam Leffler } 15535591b213SSam Leffler 15545591b213SSam Leffler /* 15555591b213SSam Leffler * Enable interrupts. 15565591b213SSam Leffler */ 15575591b213SSam Leffler sc->sc_imask = HAL_INT_RX | HAL_INT_TX 15585591b213SSam Leffler | HAL_INT_RXEOL | HAL_INT_RXORN 15595591b213SSam Leffler | HAL_INT_FATAL | HAL_INT_GLOBAL; 1560c42a7b7eSSam Leffler /* 1561c42a7b7eSSam Leffler * Enable MIB interrupts when there are hardware phy counters. 1562c42a7b7eSSam Leffler * Note we only do this (at the moment) for station mode. 1563c42a7b7eSSam Leffler */ 1564c42a7b7eSSam Leffler if (sc->sc_needmib && ic->ic_opmode == IEEE80211_M_STA) 1565c42a7b7eSSam Leffler sc->sc_imask |= HAL_INT_MIB; 15665591b213SSam Leffler 156713f4c340SRobert Watson ifp->if_drv_flags |= IFF_DRV_RUNNING; 15682e986da5SSam Leffler callout_reset(&sc->sc_wd_ch, hz, ath_watchdog, sc); 1569b032f27cSSam Leffler ath_hal_intrset(ah, sc->sc_imask); 15705591b213SSam Leffler 1571b032f27cSSam Leffler ATH_UNLOCK(sc); 1572b032f27cSSam Leffler 157386e07743SSam Leffler #ifdef ATH_TX99_DIAG 157486e07743SSam Leffler if (sc->sc_tx99 != NULL) 157586e07743SSam Leffler sc->sc_tx99->start(sc->sc_tx99); 157686e07743SSam Leffler else 157786e07743SSam Leffler #endif 1578b032f27cSSam Leffler ieee80211_start_all(ic); /* start all vap's */ 15795591b213SSam Leffler } 15805591b213SSam Leffler 15815591b213SSam Leffler static void 1582c42a7b7eSSam Leffler ath_stop_locked(struct ifnet *ifp) 15835591b213SSam Leffler { 15845591b213SSam Leffler struct ath_softc *sc = ifp->if_softc; 15855591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 15865591b213SSam Leffler 1587c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid %u if_flags 0x%x\n", 1588c42a7b7eSSam Leffler __func__, sc->sc_invalid, ifp->if_flags); 15895591b213SSam Leffler 1590c42a7b7eSSam Leffler ATH_LOCK_ASSERT(sc); 159113f4c340SRobert Watson if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 15925591b213SSam Leffler /* 15935591b213SSam Leffler * Shutdown the hardware and driver: 1594c42a7b7eSSam Leffler * reset 802.11 state machine 15955591b213SSam Leffler * turn off timers 1596c42a7b7eSSam Leffler * disable interrupts 1597c42a7b7eSSam Leffler * turn off the radio 15985591b213SSam Leffler * clear transmit machinery 15995591b213SSam Leffler * clear receive machinery 16005591b213SSam Leffler * drain and release tx queues 16015591b213SSam Leffler * reclaim beacon resources 16025591b213SSam Leffler * power down hardware 16035591b213SSam Leffler * 16045591b213SSam Leffler * Note that some of this work is not possible if the 16055591b213SSam Leffler * hardware is gone (invalid). 16065591b213SSam Leffler */ 160786e07743SSam Leffler #ifdef ATH_TX99_DIAG 160886e07743SSam Leffler if (sc->sc_tx99 != NULL) 160986e07743SSam Leffler sc->sc_tx99->stop(sc->sc_tx99); 161086e07743SSam Leffler #endif 16112e986da5SSam Leffler callout_stop(&sc->sc_wd_ch); 16122e986da5SSam Leffler sc->sc_wd_timer = 0; 161313f4c340SRobert Watson ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 1614c42a7b7eSSam Leffler if (!sc->sc_invalid) { 16153e50ec2cSSam Leffler if (sc->sc_softled) { 16163e50ec2cSSam Leffler callout_stop(&sc->sc_ledtimer); 16173e50ec2cSSam Leffler ath_hal_gpioset(ah, sc->sc_ledpin, 16183e50ec2cSSam Leffler !sc->sc_ledon); 16193e50ec2cSSam Leffler sc->sc_blinking = 0; 16203e50ec2cSSam Leffler } 16215591b213SSam Leffler ath_hal_intrset(ah, 0); 1622c42a7b7eSSam Leffler } 16235591b213SSam Leffler ath_draintxq(sc); 1624c42a7b7eSSam Leffler if (!sc->sc_invalid) { 16255591b213SSam Leffler ath_stoprecv(sc); 1626c42a7b7eSSam Leffler ath_hal_phydisable(ah); 1627c42a7b7eSSam Leffler } else 16285591b213SSam Leffler sc->sc_rxlink = NULL; 1629b032f27cSSam Leffler ath_beacon_free(sc); /* XXX not needed */ 1630c42a7b7eSSam Leffler } 1631c42a7b7eSSam Leffler } 1632c42a7b7eSSam Leffler 1633c42a7b7eSSam Leffler static void 1634c42a7b7eSSam Leffler ath_stop(struct ifnet *ifp) 1635c42a7b7eSSam Leffler { 1636c42a7b7eSSam Leffler struct ath_softc *sc = ifp->if_softc; 1637c42a7b7eSSam Leffler 1638c42a7b7eSSam Leffler ATH_LOCK(sc); 1639c42a7b7eSSam Leffler ath_stop_locked(ifp); 1640f0b2a0beSSam Leffler ATH_UNLOCK(sc); 16415591b213SSam Leffler } 16425591b213SSam Leffler 16435591b213SSam Leffler /* 16445591b213SSam Leffler * Reset the hardware w/o losing operational state. This is 16455591b213SSam Leffler * basically a more efficient way of doing ath_stop, ath_init, 16465591b213SSam Leffler * followed by state transitions to the current 802.11 1647c42a7b7eSSam Leffler * operational state. Used to recover from various errors and 1648c42a7b7eSSam Leffler * to reset or reload hardware state. 16495591b213SSam Leffler */ 1650c42a7b7eSSam Leffler static int 1651c42a7b7eSSam Leffler ath_reset(struct ifnet *ifp) 16525591b213SSam Leffler { 1653c42a7b7eSSam Leffler struct ath_softc *sc = ifp->if_softc; 1654b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 16555591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 16565591b213SSam Leffler HAL_STATUS status; 16575591b213SSam Leffler 16585591b213SSam Leffler ath_hal_intrset(ah, 0); /* disable interrupts */ 16595591b213SSam Leffler ath_draintxq(sc); /* stop xmit side */ 16605591b213SSam Leffler ath_stoprecv(sc); /* stop recv side */ 1661b032f27cSSam Leffler ath_settkipmic(sc); /* configure TKIP MIC handling */ 16625591b213SSam Leffler /* NB: indicate channel change so we do a full reset */ 166359efa8b5SSam Leffler if (!ath_hal_reset(ah, sc->sc_opmode, ic->ic_curchan, AH_TRUE, &status)) 16645591b213SSam Leffler if_printf(ifp, "%s: unable to reset hardware; hal status %u\n", 16655591b213SSam Leffler __func__, status); 1666c59005e9SSam Leffler sc->sc_diversity = ath_hal_getdiversity(ah); 166768e8e04eSSam Leffler if (ath_startrecv(sc) != 0) /* restart recv */ 166868e8e04eSSam Leffler if_printf(ifp, "%s: unable to start recv logic\n", __func__); 1669c42a7b7eSSam Leffler /* 1670c42a7b7eSSam Leffler * We may be doing a reset in response to an ioctl 1671c42a7b7eSSam Leffler * that changes the channel so update any state that 1672c42a7b7eSSam Leffler * might change as a result. 1673c42a7b7eSSam Leffler */ 1674724c193aSSam Leffler ath_chan_change(sc, ic->ic_curchan); 1675c89b957aSSam Leffler if (sc->sc_beacons) { /* restart beacons */ 1676584f7327SSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 167710ad9a77SSam Leffler if (sc->sc_tdma) 167810ad9a77SSam Leffler ath_tdma_config(sc, NULL); 167910ad9a77SSam Leffler else 168010ad9a77SSam Leffler #endif 1681c89b957aSSam Leffler ath_beacon_config(sc, NULL); 168210ad9a77SSam Leffler } 1683c42a7b7eSSam Leffler ath_hal_intrset(ah, sc->sc_imask); 1684c42a7b7eSSam Leffler 1685c42a7b7eSSam Leffler ath_start(ifp); /* restart xmit */ 1686c42a7b7eSSam Leffler return 0; 16875591b213SSam Leffler } 16885591b213SSam Leffler 168968e8e04eSSam Leffler static int 1690b032f27cSSam Leffler ath_reset_vap(struct ieee80211vap *vap, u_long cmd) 1691b032f27cSSam Leffler { 16924b54a231SSam Leffler struct ieee80211com *ic = vap->iv_ic; 16934b54a231SSam Leffler struct ifnet *ifp = ic->ic_ifp; 16944b54a231SSam Leffler struct ath_softc *sc = ifp->if_softc; 16954b54a231SSam Leffler struct ath_hal *ah = sc->sc_ah; 16964b54a231SSam Leffler 16974b54a231SSam Leffler switch (cmd) { 16984b54a231SSam Leffler case IEEE80211_IOC_TXPOWER: 16994b54a231SSam Leffler /* 17004b54a231SSam Leffler * If per-packet TPC is enabled, then we have nothing 17014b54a231SSam Leffler * to do; otherwise we need to force the global limit. 17024b54a231SSam Leffler * All this can happen directly; no need to reset. 17034b54a231SSam Leffler */ 17044b54a231SSam Leffler if (!ath_hal_gettpc(ah)) 17054b54a231SSam Leffler ath_hal_settxpowlimit(ah, ic->ic_txpowlimit); 17064b54a231SSam Leffler return 0; 17074b54a231SSam Leffler } 17084b54a231SSam Leffler return ath_reset(ifp); 1709b032f27cSSam Leffler } 1710b032f27cSSam Leffler 1711b8e788a5SAdrian Chadd struct ath_buf * 171210ad9a77SSam Leffler _ath_getbuf_locked(struct ath_softc *sc) 171310ad9a77SSam Leffler { 171410ad9a77SSam Leffler struct ath_buf *bf; 171510ad9a77SSam Leffler 171610ad9a77SSam Leffler ATH_TXBUF_LOCK_ASSERT(sc); 171710ad9a77SSam Leffler 171810ad9a77SSam Leffler bf = STAILQ_FIRST(&sc->sc_txbuf); 171910ad9a77SSam Leffler if (bf != NULL && (bf->bf_flags & ATH_BUF_BUSY) == 0) 172010ad9a77SSam Leffler STAILQ_REMOVE_HEAD(&sc->sc_txbuf, bf_list); 172110ad9a77SSam Leffler else 172210ad9a77SSam Leffler bf = NULL; 172310ad9a77SSam Leffler if (bf == NULL) { 172410ad9a77SSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT, "%s: %s\n", __func__, 172510ad9a77SSam Leffler STAILQ_FIRST(&sc->sc_txbuf) == NULL ? 172610ad9a77SSam Leffler "out of xmit buffers" : "xmit buffer busy"); 172710ad9a77SSam Leffler } 172810ad9a77SSam Leffler return bf; 172910ad9a77SSam Leffler } 173010ad9a77SSam Leffler 1731b8e788a5SAdrian Chadd struct ath_buf * 173210ad9a77SSam Leffler ath_getbuf(struct ath_softc *sc) 173310ad9a77SSam Leffler { 173410ad9a77SSam Leffler struct ath_buf *bf; 173510ad9a77SSam Leffler 173610ad9a77SSam Leffler ATH_TXBUF_LOCK(sc); 173710ad9a77SSam Leffler bf = _ath_getbuf_locked(sc); 173810ad9a77SSam Leffler if (bf == NULL) { 173910ad9a77SSam Leffler struct ifnet *ifp = sc->sc_ifp; 174010ad9a77SSam Leffler 174110ad9a77SSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT, "%s: stop queue\n", __func__); 174210ad9a77SSam Leffler sc->sc_stats.ast_tx_qstop++; 174310ad9a77SSam Leffler ifp->if_drv_flags |= IFF_DRV_OACTIVE; 174410ad9a77SSam Leffler } 174510ad9a77SSam Leffler ATH_TXBUF_UNLOCK(sc); 174610ad9a77SSam Leffler return bf; 174710ad9a77SSam Leffler } 174810ad9a77SSam Leffler 17495591b213SSam Leffler static void 17505591b213SSam Leffler ath_start(struct ifnet *ifp) 17515591b213SSam Leffler { 17525591b213SSam Leffler struct ath_softc *sc = ifp->if_softc; 17535591b213SSam Leffler struct ieee80211_node *ni; 17545591b213SSam Leffler struct ath_buf *bf; 175568e8e04eSSam Leffler struct mbuf *m, *next; 175668e8e04eSSam Leffler ath_bufhead frags; 17575591b213SSam Leffler 175813f4c340SRobert Watson if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || sc->sc_invalid) 17595591b213SSam Leffler return; 17605591b213SSam Leffler for (;;) { 17615591b213SSam Leffler /* 17625591b213SSam Leffler * Grab a TX buffer and associated resources. 17635591b213SSam Leffler */ 176410ad9a77SSam Leffler bf = ath_getbuf(sc); 176510ad9a77SSam Leffler if (bf == NULL) 17665591b213SSam Leffler break; 17672b9411e2SSam Leffler 1768b032f27cSSam Leffler IFQ_DEQUEUE(&ifp->if_snd, m); 1769b032f27cSSam Leffler if (m == NULL) { 1770b032f27cSSam Leffler ATH_TXBUF_LOCK(sc); 177110ad9a77SSam Leffler STAILQ_INSERT_HEAD(&sc->sc_txbuf, bf, bf_list); 1772b032f27cSSam Leffler ATH_TXBUF_UNLOCK(sc); 1773b032f27cSSam Leffler break; 1774b032f27cSSam Leffler } 1775b032f27cSSam Leffler ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; 177668e8e04eSSam Leffler /* 177768e8e04eSSam Leffler * Check for fragmentation. If this frame 177868e8e04eSSam Leffler * has been broken up verify we have enough 177968e8e04eSSam Leffler * buffers to send all the fragments so all 178068e8e04eSSam Leffler * go out or none... 178168e8e04eSSam Leffler */ 1782339ccfb3SSam Leffler STAILQ_INIT(&frags); 178368e8e04eSSam Leffler if ((m->m_flags & M_FRAG) && 178468e8e04eSSam Leffler !ath_txfrag_setup(sc, &frags, m, ni)) { 178568e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT, 178668e8e04eSSam Leffler "%s: out of txfrag buffers\n", __func__); 178736c6be9aSSam Leffler sc->sc_stats.ast_tx_nofrag++; 17889cb93076SSam Leffler ifp->if_oerrors++; 178968e8e04eSSam Leffler ath_freetx(m); 179068e8e04eSSam Leffler goto bad; 179168e8e04eSSam Leffler } 1792339ccfb3SSam Leffler ifp->if_opackets++; 179368e8e04eSSam Leffler nextfrag: 179468e8e04eSSam Leffler /* 179568e8e04eSSam Leffler * Pass the frame to the h/w for transmission. 179668e8e04eSSam Leffler * Fragmented frames have each frag chained together 179768e8e04eSSam Leffler * with m_nextpkt. We know there are sufficient ath_buf's 179868e8e04eSSam Leffler * to send all the frags because of work done by 179968e8e04eSSam Leffler * ath_txfrag_setup. We leave m_nextpkt set while 180068e8e04eSSam Leffler * calling ath_tx_start so it can use it to extend the 180168e8e04eSSam Leffler * the tx duration to cover the subsequent frag and 180268e8e04eSSam Leffler * so it can reclaim all the mbufs in case of an error; 180368e8e04eSSam Leffler * ath_tx_start clears m_nextpkt once it commits to 180468e8e04eSSam Leffler * handing the frame to the hardware. 180568e8e04eSSam Leffler */ 180668e8e04eSSam Leffler next = m->m_nextpkt; 18075591b213SSam Leffler if (ath_tx_start(sc, ni, bf, m)) { 18085591b213SSam Leffler bad: 18095591b213SSam Leffler ifp->if_oerrors++; 1810c42a7b7eSSam Leffler reclaim: 181168e8e04eSSam Leffler bf->bf_m = NULL; 181268e8e04eSSam Leffler bf->bf_node = NULL; 1813c42a7b7eSSam Leffler ATH_TXBUF_LOCK(sc); 181410ad9a77SSam Leffler STAILQ_INSERT_HEAD(&sc->sc_txbuf, bf, bf_list); 181568e8e04eSSam Leffler ath_txfrag_cleanup(sc, &frags, ni); 1816c42a7b7eSSam Leffler ATH_TXBUF_UNLOCK(sc); 1817c42a7b7eSSam Leffler if (ni != NULL) 1818c42a7b7eSSam Leffler ieee80211_free_node(ni); 18195591b213SSam Leffler continue; 18205591b213SSam Leffler } 182168e8e04eSSam Leffler if (next != NULL) { 182268e8e04eSSam Leffler /* 182368e8e04eSSam Leffler * Beware of state changing between frags. 182468e8e04eSSam Leffler * XXX check sta power-save state? 182568e8e04eSSam Leffler */ 1826b032f27cSSam Leffler if (ni->ni_vap->iv_state != IEEE80211_S_RUN) { 182768e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT, 182868e8e04eSSam Leffler "%s: flush fragmented packet, state %s\n", 182968e8e04eSSam Leffler __func__, 1830b032f27cSSam Leffler ieee80211_state_name[ni->ni_vap->iv_state]); 183168e8e04eSSam Leffler ath_freetx(next); 183268e8e04eSSam Leffler goto reclaim; 183368e8e04eSSam Leffler } 183468e8e04eSSam Leffler m = next; 183568e8e04eSSam Leffler bf = STAILQ_FIRST(&frags); 183668e8e04eSSam Leffler KASSERT(bf != NULL, ("no buf for txfrag")); 183768e8e04eSSam Leffler STAILQ_REMOVE_HEAD(&frags, bf_list); 183868e8e04eSSam Leffler goto nextfrag; 183968e8e04eSSam Leffler } 18405591b213SSam Leffler 18412e986da5SSam Leffler sc->sc_wd_timer = 5; 18425591b213SSam Leffler } 18435591b213SSam Leffler } 18445591b213SSam Leffler 18455591b213SSam Leffler static int 18465591b213SSam Leffler ath_media_change(struct ifnet *ifp) 18475591b213SSam Leffler { 1848b032f27cSSam Leffler int error = ieee80211_media_change(ifp); 1849b032f27cSSam Leffler /* NB: only the fixed rate can change and that doesn't need a reset */ 1850b032f27cSSam Leffler return (error == ENETRESET ? 0 : error); 18515591b213SSam Leffler } 18525591b213SSam Leffler 1853a585a9a1SSam Leffler #ifdef ATH_DEBUG 1854c42a7b7eSSam Leffler static void 18555901d2d3SSam Leffler ath_keyprint(struct ath_softc *sc, const char *tag, u_int ix, 1856c42a7b7eSSam Leffler const HAL_KEYVAL *hk, const u_int8_t mac[IEEE80211_ADDR_LEN]) 18575591b213SSam Leffler { 1858c42a7b7eSSam Leffler static const char *ciphers[] = { 1859c42a7b7eSSam Leffler "WEP", 1860c42a7b7eSSam Leffler "AES-OCB", 1861c42a7b7eSSam Leffler "AES-CCM", 1862c42a7b7eSSam Leffler "CKIP", 1863c42a7b7eSSam Leffler "TKIP", 1864c42a7b7eSSam Leffler "CLR", 1865c42a7b7eSSam Leffler }; 1866c42a7b7eSSam Leffler int i, n; 18675591b213SSam Leffler 1868c42a7b7eSSam Leffler printf("%s: [%02u] %-7s ", tag, ix, ciphers[hk->kv_type]); 1869c42a7b7eSSam Leffler for (i = 0, n = hk->kv_len; i < n; i++) 1870c42a7b7eSSam Leffler printf("%02x", hk->kv_val[i]); 1871c42a7b7eSSam Leffler printf(" mac %s", ether_sprintf(mac)); 1872c42a7b7eSSam Leffler if (hk->kv_type == HAL_CIPHER_TKIP) { 18735901d2d3SSam Leffler printf(" %s ", sc->sc_splitmic ? "mic" : "rxmic"); 1874c42a7b7eSSam Leffler for (i = 0; i < sizeof(hk->kv_mic); i++) 1875c42a7b7eSSam Leffler printf("%02x", hk->kv_mic[i]); 18765901d2d3SSam Leffler if (!sc->sc_splitmic) { 18775901d2d3SSam Leffler printf(" txmic "); 18785901d2d3SSam Leffler for (i = 0; i < sizeof(hk->kv_txmic); i++) 18795901d2d3SSam Leffler printf("%02x", hk->kv_txmic[i]); 18805901d2d3SSam Leffler } 18812075afbaSSam Leffler } 1882c42a7b7eSSam Leffler printf("\n"); 1883c42a7b7eSSam Leffler } 1884c42a7b7eSSam Leffler #endif 1885c42a7b7eSSam Leffler 18865591b213SSam Leffler /* 1887c42a7b7eSSam Leffler * Set a TKIP key into the hardware. This handles the 1888c42a7b7eSSam Leffler * potential distribution of key state to multiple key 1889c42a7b7eSSam Leffler * cache slots for TKIP. 18905591b213SSam Leffler */ 1891c42a7b7eSSam Leffler static int 1892c42a7b7eSSam Leffler ath_keyset_tkip(struct ath_softc *sc, const struct ieee80211_key *k, 1893c42a7b7eSSam Leffler HAL_KEYVAL *hk, const u_int8_t mac[IEEE80211_ADDR_LEN]) 1894c42a7b7eSSam Leffler { 1895c42a7b7eSSam Leffler #define IEEE80211_KEY_XR (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV) 1896c42a7b7eSSam Leffler static const u_int8_t zerobssid[IEEE80211_ADDR_LEN]; 18978cec0ab9SSam Leffler struct ath_hal *ah = sc->sc_ah; 18988cec0ab9SSam Leffler 1899c42a7b7eSSam Leffler KASSERT(k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP, 1900c42a7b7eSSam Leffler ("got a non-TKIP key, cipher %u", k->wk_cipher->ic_cipher)); 1901c42a7b7eSSam Leffler if ((k->wk_flags & IEEE80211_KEY_XR) == IEEE80211_KEY_XR) { 19025901d2d3SSam Leffler if (sc->sc_splitmic) { 1903c42a7b7eSSam Leffler /* 1904c1225b52SSam Leffler * TX key goes at first index, RX key at the rx index. 1905c42a7b7eSSam Leffler * The hal handles the MIC keys at index+64. 1906c42a7b7eSSam Leffler */ 1907c42a7b7eSSam Leffler memcpy(hk->kv_mic, k->wk_txmic, sizeof(hk->kv_mic)); 1908c42a7b7eSSam Leffler KEYPRINTF(sc, k->wk_keyix, hk, zerobssid); 1909c42a7b7eSSam Leffler if (!ath_hal_keyset(ah, k->wk_keyix, hk, zerobssid)) 1910c42a7b7eSSam Leffler return 0; 1911c42a7b7eSSam Leffler 1912c42a7b7eSSam Leffler memcpy(hk->kv_mic, k->wk_rxmic, sizeof(hk->kv_mic)); 1913c42a7b7eSSam Leffler KEYPRINTF(sc, k->wk_keyix+32, hk, mac); 1914c42a7b7eSSam Leffler /* XXX delete tx key on failure? */ 1915c42a7b7eSSam Leffler return ath_hal_keyset(ah, k->wk_keyix+32, hk, mac); 19165901d2d3SSam Leffler } else { 19175901d2d3SSam Leffler /* 19185901d2d3SSam Leffler * Room for both TX+RX MIC keys in one key cache 19195901d2d3SSam Leffler * slot, just set key at the first index; the hal 1920b032f27cSSam Leffler * will handle the rest. 19215901d2d3SSam Leffler */ 19225901d2d3SSam Leffler memcpy(hk->kv_mic, k->wk_rxmic, sizeof(hk->kv_mic)); 19235901d2d3SSam Leffler memcpy(hk->kv_txmic, k->wk_txmic, sizeof(hk->kv_txmic)); 19245901d2d3SSam Leffler KEYPRINTF(sc, k->wk_keyix, hk, mac); 19255901d2d3SSam Leffler return ath_hal_keyset(ah, k->wk_keyix, hk, mac); 19265901d2d3SSam Leffler } 1927b032f27cSSam Leffler } else if (k->wk_flags & IEEE80211_KEY_XMIT) { 192872d9df0aSSam Leffler if (sc->sc_splitmic) { 192972d9df0aSSam Leffler /* 193072d9df0aSSam Leffler * NB: must pass MIC key in expected location when 193172d9df0aSSam Leffler * the keycache only holds one MIC key per entry. 193272d9df0aSSam Leffler */ 193372d9df0aSSam Leffler memcpy(hk->kv_mic, k->wk_txmic, sizeof(hk->kv_txmic)); 193472d9df0aSSam Leffler } else 1935b032f27cSSam Leffler memcpy(hk->kv_txmic, k->wk_txmic, sizeof(hk->kv_txmic)); 1936b032f27cSSam Leffler KEYPRINTF(sc, k->wk_keyix, hk, mac); 1937b032f27cSSam Leffler return ath_hal_keyset(ah, k->wk_keyix, hk, mac); 1938b032f27cSSam Leffler } else if (k->wk_flags & IEEE80211_KEY_RECV) { 1939b032f27cSSam Leffler memcpy(hk->kv_mic, k->wk_rxmic, sizeof(hk->kv_mic)); 1940e8fd88a3SSam Leffler KEYPRINTF(sc, k->wk_keyix, hk, mac); 1941e8fd88a3SSam Leffler return ath_hal_keyset(ah, k->wk_keyix, hk, mac); 1942c42a7b7eSSam Leffler } 1943c42a7b7eSSam Leffler return 0; 1944c42a7b7eSSam Leffler #undef IEEE80211_KEY_XR 1945c42a7b7eSSam Leffler } 1946c42a7b7eSSam Leffler 1947c42a7b7eSSam Leffler /* 1948c42a7b7eSSam Leffler * Set a net80211 key into the hardware. This handles the 1949c42a7b7eSSam Leffler * potential distribution of key state to multiple key 1950c42a7b7eSSam Leffler * cache slots for TKIP with hardware MIC support. 1951c42a7b7eSSam Leffler */ 1952c42a7b7eSSam Leffler static int 1953c42a7b7eSSam Leffler ath_keyset(struct ath_softc *sc, const struct ieee80211_key *k, 1954e8fd88a3SSam Leffler struct ieee80211_node *bss) 1955c42a7b7eSSam Leffler { 1956c42a7b7eSSam Leffler #define N(a) (sizeof(a)/sizeof(a[0])) 1957c42a7b7eSSam Leffler static const u_int8_t ciphermap[] = { 1958c42a7b7eSSam Leffler HAL_CIPHER_WEP, /* IEEE80211_CIPHER_WEP */ 1959c42a7b7eSSam Leffler HAL_CIPHER_TKIP, /* IEEE80211_CIPHER_TKIP */ 1960c42a7b7eSSam Leffler HAL_CIPHER_AES_OCB, /* IEEE80211_CIPHER_AES_OCB */ 1961c42a7b7eSSam Leffler HAL_CIPHER_AES_CCM, /* IEEE80211_CIPHER_AES_CCM */ 1962c42a7b7eSSam Leffler (u_int8_t) -1, /* 4 is not allocated */ 1963c42a7b7eSSam Leffler HAL_CIPHER_CKIP, /* IEEE80211_CIPHER_CKIP */ 1964c42a7b7eSSam Leffler HAL_CIPHER_CLR, /* IEEE80211_CIPHER_NONE */ 1965c42a7b7eSSam Leffler }; 1966c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 1967c42a7b7eSSam Leffler const struct ieee80211_cipher *cip = k->wk_cipher; 1968e8fd88a3SSam Leffler u_int8_t gmac[IEEE80211_ADDR_LEN]; 1969e8fd88a3SSam Leffler const u_int8_t *mac; 1970c42a7b7eSSam Leffler HAL_KEYVAL hk; 1971c42a7b7eSSam Leffler 1972c42a7b7eSSam Leffler memset(&hk, 0, sizeof(hk)); 1973c42a7b7eSSam Leffler /* 1974c42a7b7eSSam Leffler * Software crypto uses a "clear key" so non-crypto 1975c42a7b7eSSam Leffler * state kept in the key cache are maintained and 1976c42a7b7eSSam Leffler * so that rx frames have an entry to match. 1977c42a7b7eSSam Leffler */ 1978c42a7b7eSSam Leffler if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) == 0) { 1979c42a7b7eSSam Leffler KASSERT(cip->ic_cipher < N(ciphermap), 1980c42a7b7eSSam Leffler ("invalid cipher type %u", cip->ic_cipher)); 1981c42a7b7eSSam Leffler hk.kv_type = ciphermap[cip->ic_cipher]; 1982c42a7b7eSSam Leffler hk.kv_len = k->wk_keylen; 1983c42a7b7eSSam Leffler memcpy(hk.kv_val, k->wk_key, k->wk_keylen); 19848cec0ab9SSam Leffler } else 1985c42a7b7eSSam Leffler hk.kv_type = HAL_CIPHER_CLR; 1986c42a7b7eSSam Leffler 1987e8fd88a3SSam Leffler if ((k->wk_flags & IEEE80211_KEY_GROUP) && sc->sc_mcastkey) { 1988e8fd88a3SSam Leffler /* 1989e8fd88a3SSam Leffler * Group keys on hardware that supports multicast frame 19909ac01d39SRui Paulo * key search use a MAC that is the sender's address with 1991be976707SAdrian Chadd * the multicast bit set instead of the app-specified address. 1992e8fd88a3SSam Leffler */ 1993e8fd88a3SSam Leffler IEEE80211_ADDR_COPY(gmac, bss->ni_macaddr); 1994be976707SAdrian Chadd gmac[0] |= 0x01; 1995e8fd88a3SSam Leffler mac = gmac; 1996e8fd88a3SSam Leffler } else 1997d3ac945bSSam Leffler mac = k->wk_macaddr; 1998e8fd88a3SSam Leffler 1999c42a7b7eSSam Leffler if (hk.kv_type == HAL_CIPHER_TKIP && 20005901d2d3SSam Leffler (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) { 2001c42a7b7eSSam Leffler return ath_keyset_tkip(sc, k, &hk, mac); 2002c42a7b7eSSam Leffler } else { 2003c42a7b7eSSam Leffler KEYPRINTF(sc, k->wk_keyix, &hk, mac); 2004c42a7b7eSSam Leffler return ath_hal_keyset(ah, k->wk_keyix, &hk, mac); 20058cec0ab9SSam Leffler } 2006c42a7b7eSSam Leffler #undef N 20075591b213SSam Leffler } 20085591b213SSam Leffler 20095591b213SSam Leffler /* 2010c42a7b7eSSam Leffler * Allocate tx/rx key slots for TKIP. We allocate two slots for 2011c42a7b7eSSam Leffler * each key, one for decrypt/encrypt and the other for the MIC. 2012c42a7b7eSSam Leffler */ 2013c42a7b7eSSam Leffler static u_int16_t 2014c1225b52SSam Leffler key_alloc_2pair(struct ath_softc *sc, 2015c1225b52SSam Leffler ieee80211_keyix *txkeyix, ieee80211_keyix *rxkeyix) 2016c42a7b7eSSam Leffler { 2017c42a7b7eSSam Leffler #define N(a) (sizeof(a)/sizeof(a[0])) 2018c42a7b7eSSam Leffler u_int i, keyix; 2019c42a7b7eSSam Leffler 2020c42a7b7eSSam Leffler KASSERT(sc->sc_splitmic, ("key cache !split")); 2021c42a7b7eSSam Leffler /* XXX could optimize */ 2022c42a7b7eSSam Leffler for (i = 0; i < N(sc->sc_keymap)/4; i++) { 2023c42a7b7eSSam Leffler u_int8_t b = sc->sc_keymap[i]; 2024c42a7b7eSSam Leffler if (b != 0xff) { 2025c42a7b7eSSam Leffler /* 2026c42a7b7eSSam Leffler * One or more slots in this byte are free. 2027c42a7b7eSSam Leffler */ 2028c42a7b7eSSam Leffler keyix = i*NBBY; 2029c42a7b7eSSam Leffler while (b & 1) { 2030c42a7b7eSSam Leffler again: 2031c42a7b7eSSam Leffler keyix++; 2032c42a7b7eSSam Leffler b >>= 1; 2033c42a7b7eSSam Leffler } 2034c42a7b7eSSam Leffler /* XXX IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV */ 2035c42a7b7eSSam Leffler if (isset(sc->sc_keymap, keyix+32) || 2036c42a7b7eSSam Leffler isset(sc->sc_keymap, keyix+64) || 2037c42a7b7eSSam Leffler isset(sc->sc_keymap, keyix+32+64)) { 2038c42a7b7eSSam Leffler /* full pair unavailable */ 2039c42a7b7eSSam Leffler /* XXX statistic */ 2040c42a7b7eSSam Leffler if (keyix == (i+1)*NBBY) { 2041c42a7b7eSSam Leffler /* no slots were appropriate, advance */ 2042c42a7b7eSSam Leffler continue; 2043c42a7b7eSSam Leffler } 2044c42a7b7eSSam Leffler goto again; 2045c42a7b7eSSam Leffler } 2046c42a7b7eSSam Leffler setbit(sc->sc_keymap, keyix); 2047c42a7b7eSSam Leffler setbit(sc->sc_keymap, keyix+64); 2048c42a7b7eSSam Leffler setbit(sc->sc_keymap, keyix+32); 2049c42a7b7eSSam Leffler setbit(sc->sc_keymap, keyix+32+64); 2050c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_KEYCACHE, 2051c42a7b7eSSam Leffler "%s: key pair %u,%u %u,%u\n", 2052c42a7b7eSSam Leffler __func__, keyix, keyix+64, 2053c42a7b7eSSam Leffler keyix+32, keyix+32+64); 2054c1225b52SSam Leffler *txkeyix = keyix; 2055c1225b52SSam Leffler *rxkeyix = keyix+32; 2056c1225b52SSam Leffler return 1; 2057c42a7b7eSSam Leffler } 2058c42a7b7eSSam Leffler } 2059c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: out of pair space\n", __func__); 2060c1225b52SSam Leffler return 0; 2061c42a7b7eSSam Leffler #undef N 2062c42a7b7eSSam Leffler } 2063c42a7b7eSSam Leffler 2064c42a7b7eSSam Leffler /* 20655901d2d3SSam Leffler * Allocate tx/rx key slots for TKIP. We allocate two slots for 20665901d2d3SSam Leffler * each key, one for decrypt/encrypt and the other for the MIC. 20675901d2d3SSam Leffler */ 20685901d2d3SSam Leffler static u_int16_t 20695901d2d3SSam Leffler key_alloc_pair(struct ath_softc *sc, 20705901d2d3SSam Leffler ieee80211_keyix *txkeyix, ieee80211_keyix *rxkeyix) 20715901d2d3SSam Leffler { 20725901d2d3SSam Leffler #define N(a) (sizeof(a)/sizeof(a[0])) 20735901d2d3SSam Leffler u_int i, keyix; 20745901d2d3SSam Leffler 20755901d2d3SSam Leffler KASSERT(!sc->sc_splitmic, ("key cache split")); 20765901d2d3SSam Leffler /* XXX could optimize */ 20775901d2d3SSam Leffler for (i = 0; i < N(sc->sc_keymap)/4; i++) { 20785901d2d3SSam Leffler u_int8_t b = sc->sc_keymap[i]; 20795901d2d3SSam Leffler if (b != 0xff) { 20805901d2d3SSam Leffler /* 20815901d2d3SSam Leffler * One or more slots in this byte are free. 20825901d2d3SSam Leffler */ 20835901d2d3SSam Leffler keyix = i*NBBY; 20845901d2d3SSam Leffler while (b & 1) { 20855901d2d3SSam Leffler again: 20865901d2d3SSam Leffler keyix++; 20875901d2d3SSam Leffler b >>= 1; 20885901d2d3SSam Leffler } 20895901d2d3SSam Leffler if (isset(sc->sc_keymap, keyix+64)) { 20905901d2d3SSam Leffler /* full pair unavailable */ 20915901d2d3SSam Leffler /* XXX statistic */ 20925901d2d3SSam Leffler if (keyix == (i+1)*NBBY) { 20935901d2d3SSam Leffler /* no slots were appropriate, advance */ 20945901d2d3SSam Leffler continue; 20955901d2d3SSam Leffler } 20965901d2d3SSam Leffler goto again; 20975901d2d3SSam Leffler } 20985901d2d3SSam Leffler setbit(sc->sc_keymap, keyix); 20995901d2d3SSam Leffler setbit(sc->sc_keymap, keyix+64); 21005901d2d3SSam Leffler DPRINTF(sc, ATH_DEBUG_KEYCACHE, 21015901d2d3SSam Leffler "%s: key pair %u,%u\n", 21025901d2d3SSam Leffler __func__, keyix, keyix+64); 21035901d2d3SSam Leffler *txkeyix = *rxkeyix = keyix; 21045901d2d3SSam Leffler return 1; 21055901d2d3SSam Leffler } 21065901d2d3SSam Leffler } 21075901d2d3SSam Leffler DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: out of pair space\n", __func__); 21085901d2d3SSam Leffler return 0; 21095901d2d3SSam Leffler #undef N 21105901d2d3SSam Leffler } 21115901d2d3SSam Leffler 21125901d2d3SSam Leffler /* 2113c42a7b7eSSam Leffler * Allocate a single key cache slot. 2114c42a7b7eSSam Leffler */ 2115c1225b52SSam Leffler static int 2116c1225b52SSam Leffler key_alloc_single(struct ath_softc *sc, 2117c1225b52SSam Leffler ieee80211_keyix *txkeyix, ieee80211_keyix *rxkeyix) 2118c42a7b7eSSam Leffler { 2119c42a7b7eSSam Leffler #define N(a) (sizeof(a)/sizeof(a[0])) 2120c42a7b7eSSam Leffler u_int i, keyix; 2121c42a7b7eSSam Leffler 2122c42a7b7eSSam Leffler /* XXX try i,i+32,i+64,i+32+64 to minimize key pair conflicts */ 2123c42a7b7eSSam Leffler for (i = 0; i < N(sc->sc_keymap); i++) { 2124c42a7b7eSSam Leffler u_int8_t b = sc->sc_keymap[i]; 2125c42a7b7eSSam Leffler if (b != 0xff) { 2126c42a7b7eSSam Leffler /* 2127c42a7b7eSSam Leffler * One or more slots are free. 2128c42a7b7eSSam Leffler */ 2129c42a7b7eSSam Leffler keyix = i*NBBY; 2130c42a7b7eSSam Leffler while (b & 1) 2131c42a7b7eSSam Leffler keyix++, b >>= 1; 2132c42a7b7eSSam Leffler setbit(sc->sc_keymap, keyix); 2133c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: key %u\n", 2134c42a7b7eSSam Leffler __func__, keyix); 2135c1225b52SSam Leffler *txkeyix = *rxkeyix = keyix; 2136c1225b52SSam Leffler return 1; 2137c42a7b7eSSam Leffler } 2138c42a7b7eSSam Leffler } 2139c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: out of space\n", __func__); 2140c1225b52SSam Leffler return 0; 2141c42a7b7eSSam Leffler #undef N 2142c42a7b7eSSam Leffler } 2143c42a7b7eSSam Leffler 2144c42a7b7eSSam Leffler /* 2145c42a7b7eSSam Leffler * Allocate one or more key cache slots for a uniacst key. The 2146c42a7b7eSSam Leffler * key itself is needed only to identify the cipher. For hardware 2147c42a7b7eSSam Leffler * TKIP with split cipher+MIC keys we allocate two key cache slot 2148c42a7b7eSSam Leffler * pairs so that we can setup separate TX and RX MIC keys. Note 2149c42a7b7eSSam Leffler * that the MIC key for a TKIP key at slot i is assumed by the 2150c42a7b7eSSam Leffler * hardware to be at slot i+64. This limits TKIP keys to the first 2151c42a7b7eSSam Leffler * 64 entries. 2152c42a7b7eSSam Leffler */ 2153c42a7b7eSSam Leffler static int 2154e6e547d5SSam Leffler ath_key_alloc(struct ieee80211vap *vap, struct ieee80211_key *k, 2155c1225b52SSam Leffler ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix) 2156c42a7b7eSSam Leffler { 2157b032f27cSSam Leffler struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; 2158c42a7b7eSSam Leffler 2159c42a7b7eSSam Leffler /* 21608ca623d7SSam Leffler * Group key allocation must be handled specially for 21618ca623d7SSam Leffler * parts that do not support multicast key cache search 21628ca623d7SSam Leffler * functionality. For those parts the key id must match 21638ca623d7SSam Leffler * the h/w key index so lookups find the right key. On 21648ca623d7SSam Leffler * parts w/ the key search facility we install the sender's 21658ca623d7SSam Leffler * mac address (with the high bit set) and let the hardware 21668ca623d7SSam Leffler * find the key w/o using the key id. This is preferred as 21678ca623d7SSam Leffler * it permits us to support multiple users for adhoc and/or 21688ca623d7SSam Leffler * multi-station operation. 21698ca623d7SSam Leffler */ 21709ac01d39SRui Paulo if (k->wk_keyix != IEEE80211_KEYIX_NONE) { 21719ac01d39SRui Paulo /* 21729ac01d39SRui Paulo * Only global keys should have key index assigned. 21739ac01d39SRui Paulo */ 2174b032f27cSSam Leffler if (!(&vap->iv_nw_keys[0] <= k && 2175b032f27cSSam Leffler k < &vap->iv_nw_keys[IEEE80211_WEP_NKID])) { 21768ca623d7SSam Leffler /* should not happen */ 21778ca623d7SSam Leffler DPRINTF(sc, ATH_DEBUG_KEYCACHE, 21788ca623d7SSam Leffler "%s: bogus group key\n", __func__); 2179c1225b52SSam Leffler return 0; 21808ca623d7SSam Leffler } 21819ac01d39SRui Paulo if (vap->iv_opmode != IEEE80211_M_HOSTAP || 21829ac01d39SRui Paulo !(k->wk_flags & IEEE80211_KEY_GROUP) || 21839ac01d39SRui Paulo !sc->sc_mcastkey) { 21848ca623d7SSam Leffler /* 21858ca623d7SSam Leffler * XXX we pre-allocate the global keys so 21869ac01d39SRui Paulo * have no way to check if they've already 21879ac01d39SRui Paulo * been allocated. 21888ca623d7SSam Leffler */ 2189b032f27cSSam Leffler *keyix = *rxkeyix = k - vap->iv_nw_keys; 2190c1225b52SSam Leffler return 1; 21918ca623d7SSam Leffler } 21929ac01d39SRui Paulo /* 21939ac01d39SRui Paulo * Group key and device supports multicast key search. 21949ac01d39SRui Paulo */ 21959ac01d39SRui Paulo k->wk_keyix = IEEE80211_KEYIX_NONE; 21969ac01d39SRui Paulo } 21978ca623d7SSam Leffler 21988ca623d7SSam Leffler /* 2199c42a7b7eSSam Leffler * We allocate two pair for TKIP when using the h/w to do 2200c42a7b7eSSam Leffler * the MIC. For everything else, including software crypto, 2201c42a7b7eSSam Leffler * we allocate a single entry. Note that s/w crypto requires 2202c42a7b7eSSam Leffler * a pass-through slot on the 5211 and 5212. The 5210 does 2203c42a7b7eSSam Leffler * not support pass-through cache entries and we map all 2204c42a7b7eSSam Leffler * those requests to slot 0. 2205c42a7b7eSSam Leffler */ 2206c42a7b7eSSam Leffler if (k->wk_flags & IEEE80211_KEY_SWCRYPT) { 2207c1225b52SSam Leffler return key_alloc_single(sc, keyix, rxkeyix); 2208c42a7b7eSSam Leffler } else if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP && 22095901d2d3SSam Leffler (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) { 22105901d2d3SSam Leffler if (sc->sc_splitmic) 2211c1225b52SSam Leffler return key_alloc_2pair(sc, keyix, rxkeyix); 22125901d2d3SSam Leffler else 22135901d2d3SSam Leffler return key_alloc_pair(sc, keyix, rxkeyix); 2214c42a7b7eSSam Leffler } else { 2215c1225b52SSam Leffler return key_alloc_single(sc, keyix, rxkeyix); 2216c42a7b7eSSam Leffler } 2217c42a7b7eSSam Leffler } 2218c42a7b7eSSam Leffler 2219c42a7b7eSSam Leffler /* 2220c42a7b7eSSam Leffler * Delete an entry in the key cache allocated by ath_key_alloc. 2221c42a7b7eSSam Leffler */ 2222c42a7b7eSSam Leffler static int 2223b032f27cSSam Leffler ath_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k) 2224c42a7b7eSSam Leffler { 2225b032f27cSSam Leffler struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; 2226c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 2227c42a7b7eSSam Leffler const struct ieee80211_cipher *cip = k->wk_cipher; 2228c42a7b7eSSam Leffler u_int keyix = k->wk_keyix; 2229c42a7b7eSSam Leffler 2230c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: delete key %u\n", __func__, keyix); 2231c42a7b7eSSam Leffler 2232c42a7b7eSSam Leffler ath_hal_keyreset(ah, keyix); 2233c42a7b7eSSam Leffler /* 2234c42a7b7eSSam Leffler * Handle split tx/rx keying required for TKIP with h/w MIC. 2235c42a7b7eSSam Leffler */ 2236c42a7b7eSSam Leffler if (cip->ic_cipher == IEEE80211_CIPHER_TKIP && 2237c1225b52SSam Leffler (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && sc->sc_splitmic) 2238c42a7b7eSSam Leffler ath_hal_keyreset(ah, keyix+32); /* RX key */ 2239c42a7b7eSSam Leffler if (keyix >= IEEE80211_WEP_NKID) { 2240c42a7b7eSSam Leffler /* 2241c42a7b7eSSam Leffler * Don't touch keymap entries for global keys so 2242c42a7b7eSSam Leffler * they are never considered for dynamic allocation. 2243c42a7b7eSSam Leffler */ 2244c42a7b7eSSam Leffler clrbit(sc->sc_keymap, keyix); 2245c42a7b7eSSam Leffler if (cip->ic_cipher == IEEE80211_CIPHER_TKIP && 22465901d2d3SSam Leffler (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) { 2247c42a7b7eSSam Leffler clrbit(sc->sc_keymap, keyix+64); /* TX key MIC */ 22485901d2d3SSam Leffler if (sc->sc_splitmic) { 22495901d2d3SSam Leffler /* +32 for RX key, +32+64 for RX key MIC */ 22505901d2d3SSam Leffler clrbit(sc->sc_keymap, keyix+32); 22515901d2d3SSam Leffler clrbit(sc->sc_keymap, keyix+32+64); 22525901d2d3SSam Leffler } 2253c42a7b7eSSam Leffler } 2254c42a7b7eSSam Leffler } 2255c42a7b7eSSam Leffler return 1; 2256c42a7b7eSSam Leffler } 2257c42a7b7eSSam Leffler 2258c42a7b7eSSam Leffler /* 2259c42a7b7eSSam Leffler * Set the key cache contents for the specified key. Key cache 2260c42a7b7eSSam Leffler * slot(s) must already have been allocated by ath_key_alloc. 2261c42a7b7eSSam Leffler */ 2262c42a7b7eSSam Leffler static int 2263b032f27cSSam Leffler ath_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k, 2264c42a7b7eSSam Leffler const u_int8_t mac[IEEE80211_ADDR_LEN]) 2265c42a7b7eSSam Leffler { 2266b032f27cSSam Leffler struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; 2267c42a7b7eSSam Leffler 2268d3ac945bSSam Leffler return ath_keyset(sc, k, vap->iv_bss); 2269c42a7b7eSSam Leffler } 2270c42a7b7eSSam Leffler 2271c42a7b7eSSam Leffler /* 2272c42a7b7eSSam Leffler * Block/unblock tx+rx processing while a key change is done. 2273c42a7b7eSSam Leffler * We assume the caller serializes key management operations 2274c42a7b7eSSam Leffler * so we only need to worry about synchronization with other 2275c42a7b7eSSam Leffler * uses that originate in the driver. 2276c42a7b7eSSam Leffler */ 2277c42a7b7eSSam Leffler static void 2278b032f27cSSam Leffler ath_key_update_begin(struct ieee80211vap *vap) 2279c42a7b7eSSam Leffler { 2280b032f27cSSam Leffler struct ifnet *ifp = vap->iv_ic->ic_ifp; 2281c42a7b7eSSam Leffler struct ath_softc *sc = ifp->if_softc; 2282c42a7b7eSSam Leffler 2283c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__); 2284b032f27cSSam Leffler taskqueue_block(sc->sc_tq); 2285c42a7b7eSSam Leffler IF_LOCK(&ifp->if_snd); /* NB: doesn't block mgmt frames */ 2286c42a7b7eSSam Leffler } 2287c42a7b7eSSam Leffler 2288c42a7b7eSSam Leffler static void 2289b032f27cSSam Leffler ath_key_update_end(struct ieee80211vap *vap) 2290c42a7b7eSSam Leffler { 2291b032f27cSSam Leffler struct ifnet *ifp = vap->iv_ic->ic_ifp; 2292c42a7b7eSSam Leffler struct ath_softc *sc = ifp->if_softc; 2293c42a7b7eSSam Leffler 2294c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__); 2295c42a7b7eSSam Leffler IF_UNLOCK(&ifp->if_snd); 2296b032f27cSSam Leffler taskqueue_unblock(sc->sc_tq); 2297c42a7b7eSSam Leffler } 22985591b213SSam Leffler 22994bc0e754SSam Leffler /* 23004bc0e754SSam Leffler * Calculate the receive filter according to the 23014bc0e754SSam Leffler * operating mode and state: 23024bc0e754SSam Leffler * 23034bc0e754SSam Leffler * o always accept unicast, broadcast, and multicast traffic 2304b032f27cSSam Leffler * o accept PHY error frames when hardware doesn't have MIB support 2305411373ebSSam Leffler * to count and we need them for ANI (sta mode only until recently) 2306b032f27cSSam Leffler * and we are not scanning (ANI is disabled) 2307411373ebSSam Leffler * NB: older hal's add rx filter bits out of sight and we need to 2308411373ebSSam Leffler * blindly preserve them 23094bc0e754SSam Leffler * o probe request frames are accepted only when operating in 231059aa14a9SRui Paulo * hostap, adhoc, mesh, or monitor modes 2311b032f27cSSam Leffler * o enable promiscuous mode 2312b032f27cSSam Leffler * - when in monitor mode 2313b032f27cSSam Leffler * - if interface marked PROMISC (assumes bridge setting is filtered) 23144bc0e754SSam Leffler * o accept beacons: 23154bc0e754SSam Leffler * - when operating in station mode for collecting rssi data when 23164bc0e754SSam Leffler * the station is otherwise quiet, or 2317b032f27cSSam Leffler * - when operating in adhoc mode so the 802.11 layer creates 2318b032f27cSSam Leffler * node table entries for peers, 23194bc0e754SSam Leffler * - when scanning 2320b032f27cSSam Leffler * - when doing s/w beacon miss (e.g. for ap+sta) 2321b032f27cSSam Leffler * - when operating in ap mode in 11g to detect overlapping bss that 2322b032f27cSSam Leffler * require protection 232359aa14a9SRui Paulo * - when operating in mesh mode to detect neighbors 23246f48c956SSam Leffler * o accept control frames: 23256f48c956SSam Leffler * - when in monitor mode 2326b032f27cSSam Leffler * XXX HT protection for 11n 23274bc0e754SSam Leffler */ 23284bc0e754SSam Leffler static u_int32_t 232968e8e04eSSam Leffler ath_calcrxfilter(struct ath_softc *sc) 23304bc0e754SSam Leffler { 2331fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 2332b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 23334bc0e754SSam Leffler u_int32_t rfilt; 23344bc0e754SSam Leffler 2335b032f27cSSam Leffler rfilt = HAL_RX_FILTER_UCAST | HAL_RX_FILTER_BCAST | HAL_RX_FILTER_MCAST; 2336411373ebSSam Leffler if (!sc->sc_needmib && !sc->sc_scanning) 2337411373ebSSam Leffler rfilt |= HAL_RX_FILTER_PHYERR; 23384bc0e754SSam Leffler if (ic->ic_opmode != IEEE80211_M_STA) 23394bc0e754SSam Leffler rfilt |= HAL_RX_FILTER_PROBEREQ; 23405463c4a4SSam Leffler /* XXX ic->ic_monvaps != 0? */ 2341b032f27cSSam Leffler if (ic->ic_opmode == IEEE80211_M_MONITOR || (ifp->if_flags & IFF_PROMISC)) 23424bc0e754SSam Leffler rfilt |= HAL_RX_FILTER_PROM; 23434bc0e754SSam Leffler if (ic->ic_opmode == IEEE80211_M_STA || 234447db982fSSam Leffler ic->ic_opmode == IEEE80211_M_IBSS || 2345b032f27cSSam Leffler sc->sc_swbmiss || sc->sc_scanning) 2346b032f27cSSam Leffler rfilt |= HAL_RX_FILTER_BEACON; 2347b032f27cSSam Leffler /* 2348b032f27cSSam Leffler * NB: We don't recalculate the rx filter when 2349b032f27cSSam Leffler * ic_protmode changes; otherwise we could do 2350b032f27cSSam Leffler * this only when ic_protmode != NONE. 2351b032f27cSSam Leffler */ 2352b032f27cSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP && 2353b032f27cSSam Leffler IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) 23544bc0e754SSam Leffler rfilt |= HAL_RX_FILTER_BEACON; 2355fe0dd789SSam Leffler if (sc->sc_nmeshvaps) { 235659aa14a9SRui Paulo rfilt |= HAL_RX_FILTER_BEACON; 235759aa14a9SRui Paulo if (sc->sc_hasbmatch) 235859aa14a9SRui Paulo rfilt |= HAL_RX_FILTER_BSSID; 235959aa14a9SRui Paulo else 236059aa14a9SRui Paulo rfilt |= HAL_RX_FILTER_PROM; 236159aa14a9SRui Paulo } 23626f48c956SSam Leffler if (ic->ic_opmode == IEEE80211_M_MONITOR) 23636f48c956SSam Leffler rfilt |= HAL_RX_FILTER_CONTROL; 2364a83df4d3SAdrian Chadd if (IEEE80211_IS_CHAN_HT(ic->ic_curchan)) 2365a83df4d3SAdrian Chadd rfilt |= HAL_RX_FILTER_COMPBAR; 2366b032f27cSSam Leffler DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x, %s if_flags 0x%x\n", 2367b032f27cSSam Leffler __func__, rfilt, ieee80211_opmode_name[ic->ic_opmode], ifp->if_flags); 23684bc0e754SSam Leffler return rfilt; 2369b032f27cSSam Leffler } 2370b032f27cSSam Leffler 2371b032f27cSSam Leffler static void 2372b032f27cSSam Leffler ath_update_promisc(struct ifnet *ifp) 2373b032f27cSSam Leffler { 2374b032f27cSSam Leffler struct ath_softc *sc = ifp->if_softc; 2375b032f27cSSam Leffler u_int32_t rfilt; 2376b032f27cSSam Leffler 2377b032f27cSSam Leffler /* configure rx filter */ 2378b032f27cSSam Leffler rfilt = ath_calcrxfilter(sc); 2379b032f27cSSam Leffler ath_hal_setrxfilter(sc->sc_ah, rfilt); 2380b032f27cSSam Leffler 2381b032f27cSSam Leffler DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x\n", __func__, rfilt); 2382b032f27cSSam Leffler } 2383b032f27cSSam Leffler 2384b032f27cSSam Leffler static void 2385b032f27cSSam Leffler ath_update_mcast(struct ifnet *ifp) 2386b032f27cSSam Leffler { 2387b032f27cSSam Leffler struct ath_softc *sc = ifp->if_softc; 2388b032f27cSSam Leffler u_int32_t mfilt[2]; 2389b032f27cSSam Leffler 2390b032f27cSSam Leffler /* calculate and install multicast filter */ 2391b032f27cSSam Leffler if ((ifp->if_flags & IFF_ALLMULTI) == 0) { 2392b032f27cSSam Leffler struct ifmultiaddr *ifma; 2393b032f27cSSam Leffler /* 2394b032f27cSSam Leffler * Merge multicast addresses to form the hardware filter. 2395b032f27cSSam Leffler */ 2396b032f27cSSam Leffler mfilt[0] = mfilt[1] = 0; 2397eb956cd0SRobert Watson if_maddr_rlock(ifp); /* XXX need some fiddling to remove? */ 2398b032f27cSSam Leffler TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 2399b032f27cSSam Leffler caddr_t dl; 2400b032f27cSSam Leffler u_int32_t val; 2401b032f27cSSam Leffler u_int8_t pos; 2402b032f27cSSam Leffler 2403b032f27cSSam Leffler /* calculate XOR of eight 6bit values */ 2404b032f27cSSam Leffler dl = LLADDR((struct sockaddr_dl *) ifma->ifma_addr); 2405b032f27cSSam Leffler val = LE_READ_4(dl + 0); 2406b032f27cSSam Leffler pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; 2407b032f27cSSam Leffler val = LE_READ_4(dl + 3); 2408b032f27cSSam Leffler pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; 2409b032f27cSSam Leffler pos &= 0x3f; 2410b032f27cSSam Leffler mfilt[pos / 32] |= (1 << (pos % 32)); 2411b032f27cSSam Leffler } 2412eb956cd0SRobert Watson if_maddr_runlock(ifp); 2413b032f27cSSam Leffler } else 2414b032f27cSSam Leffler mfilt[0] = mfilt[1] = ~0; 2415b032f27cSSam Leffler ath_hal_setmcastfilter(sc->sc_ah, mfilt[0], mfilt[1]); 2416b032f27cSSam Leffler DPRINTF(sc, ATH_DEBUG_MODE, "%s: MC filter %08x:%08x\n", 2417b032f27cSSam Leffler __func__, mfilt[0], mfilt[1]); 24184bc0e754SSam Leffler } 24194bc0e754SSam Leffler 24205591b213SSam Leffler static void 24215591b213SSam Leffler ath_mode_init(struct ath_softc *sc) 24225591b213SSam Leffler { 2423fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 2424b032f27cSSam Leffler struct ath_hal *ah = sc->sc_ah; 2425b032f27cSSam Leffler u_int32_t rfilt; 24265591b213SSam Leffler 24274bc0e754SSam Leffler /* configure rx filter */ 242868e8e04eSSam Leffler rfilt = ath_calcrxfilter(sc); 24294bc0e754SSam Leffler ath_hal_setrxfilter(ah, rfilt); 24304bc0e754SSam Leffler 24315591b213SSam Leffler /* configure operational mode */ 2432c42a7b7eSSam Leffler ath_hal_setopmode(ah); 2433c42a7b7eSSam Leffler 243429aca940SSam Leffler /* handle any link-level address change */ 243529aca940SSam Leffler ath_hal_setmac(ah, IF_LLADDR(ifp)); 24365591b213SSam Leffler 24375591b213SSam Leffler /* calculate and install multicast filter */ 2438b032f27cSSam Leffler ath_update_mcast(ifp); 24395591b213SSam Leffler } 24405591b213SSam Leffler 2441c42a7b7eSSam Leffler /* 2442c42a7b7eSSam Leffler * Set the slot time based on the current setting. 2443c42a7b7eSSam Leffler */ 2444c42a7b7eSSam Leffler static void 2445c42a7b7eSSam Leffler ath_setslottime(struct ath_softc *sc) 2446c42a7b7eSSam Leffler { 2447b032f27cSSam Leffler struct ieee80211com *ic = sc->sc_ifp->if_l2com; 2448c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 2449aaa70f2fSSam Leffler u_int usec; 2450c42a7b7eSSam Leffler 2451aaa70f2fSSam Leffler if (IEEE80211_IS_CHAN_HALF(ic->ic_curchan)) 2452aaa70f2fSSam Leffler usec = 13; 2453aaa70f2fSSam Leffler else if (IEEE80211_IS_CHAN_QUARTER(ic->ic_curchan)) 2454aaa70f2fSSam Leffler usec = 21; 2455724c193aSSam Leffler else if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) { 2456724c193aSSam Leffler /* honor short/long slot time only in 11g */ 2457724c193aSSam Leffler /* XXX shouldn't honor on pure g or turbo g channel */ 2458724c193aSSam Leffler if (ic->ic_flags & IEEE80211_F_SHSLOT) 2459aaa70f2fSSam Leffler usec = HAL_SLOT_TIME_9; 2460aaa70f2fSSam Leffler else 2461aaa70f2fSSam Leffler usec = HAL_SLOT_TIME_20; 2462724c193aSSam Leffler } else 2463724c193aSSam Leffler usec = HAL_SLOT_TIME_9; 2464aaa70f2fSSam Leffler 2465aaa70f2fSSam Leffler DPRINTF(sc, ATH_DEBUG_RESET, 2466aaa70f2fSSam Leffler "%s: chan %u MHz flags 0x%x %s slot, %u usec\n", 2467aaa70f2fSSam Leffler __func__, ic->ic_curchan->ic_freq, ic->ic_curchan->ic_flags, 2468aaa70f2fSSam Leffler ic->ic_flags & IEEE80211_F_SHSLOT ? "short" : "long", usec); 2469aaa70f2fSSam Leffler 2470aaa70f2fSSam Leffler ath_hal_setslottime(ah, usec); 2471c42a7b7eSSam Leffler sc->sc_updateslot = OK; 2472c42a7b7eSSam Leffler } 2473c42a7b7eSSam Leffler 2474c42a7b7eSSam Leffler /* 2475c42a7b7eSSam Leffler * Callback from the 802.11 layer to update the 2476c42a7b7eSSam Leffler * slot time based on the current setting. 2477c42a7b7eSSam Leffler */ 2478c42a7b7eSSam Leffler static void 2479c42a7b7eSSam Leffler ath_updateslot(struct ifnet *ifp) 2480c42a7b7eSSam Leffler { 2481c42a7b7eSSam Leffler struct ath_softc *sc = ifp->if_softc; 2482b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 2483c42a7b7eSSam Leffler 2484c42a7b7eSSam Leffler /* 2485c42a7b7eSSam Leffler * When not coordinating the BSS, change the hardware 2486c42a7b7eSSam Leffler * immediately. For other operation we defer the change 2487c42a7b7eSSam Leffler * until beacon updates have propagated to the stations. 2488c42a7b7eSSam Leffler */ 248959aa14a9SRui Paulo if (ic->ic_opmode == IEEE80211_M_HOSTAP || 249059aa14a9SRui Paulo ic->ic_opmode == IEEE80211_M_MBSS) 2491c42a7b7eSSam Leffler sc->sc_updateslot = UPDATE; 2492c42a7b7eSSam Leffler else 2493c42a7b7eSSam Leffler ath_setslottime(sc); 2494c42a7b7eSSam Leffler } 2495c42a7b7eSSam Leffler 2496c42a7b7eSSam Leffler /* 249780d2765fSSam Leffler * Setup a h/w transmit queue for beacons. 249880d2765fSSam Leffler */ 249980d2765fSSam Leffler static int 250080d2765fSSam Leffler ath_beaconq_setup(struct ath_hal *ah) 250180d2765fSSam Leffler { 250280d2765fSSam Leffler HAL_TXQ_INFO qi; 250380d2765fSSam Leffler 250480d2765fSSam Leffler memset(&qi, 0, sizeof(qi)); 250580d2765fSSam Leffler qi.tqi_aifs = HAL_TXQ_USEDEFAULT; 250680d2765fSSam Leffler qi.tqi_cwmin = HAL_TXQ_USEDEFAULT; 250780d2765fSSam Leffler qi.tqi_cwmax = HAL_TXQ_USEDEFAULT; 25080f2e86fbSSam Leffler /* NB: for dynamic turbo, don't enable any other interrupts */ 2509bd5a9920SSam Leffler qi.tqi_qflags = HAL_TXQ_TXDESCINT_ENABLE; 251080d2765fSSam Leffler return ath_hal_setuptxqueue(ah, HAL_TX_QUEUE_BEACON, &qi); 251180d2765fSSam Leffler } 251280d2765fSSam Leffler 251380d2765fSSam Leffler /* 25140f2e86fbSSam Leffler * Setup the transmit queue parameters for the beacon queue. 25150f2e86fbSSam Leffler */ 25160f2e86fbSSam Leffler static int 25170f2e86fbSSam Leffler ath_beaconq_config(struct ath_softc *sc) 25180f2e86fbSSam Leffler { 25190f2e86fbSSam Leffler #define ATH_EXPONENT_TO_VALUE(v) ((1<<(v))-1) 2520b032f27cSSam Leffler struct ieee80211com *ic = sc->sc_ifp->if_l2com; 25210f2e86fbSSam Leffler struct ath_hal *ah = sc->sc_ah; 25220f2e86fbSSam Leffler HAL_TXQ_INFO qi; 25230f2e86fbSSam Leffler 25240f2e86fbSSam Leffler ath_hal_gettxqueueprops(ah, sc->sc_bhalq, &qi); 252559aa14a9SRui Paulo if (ic->ic_opmode == IEEE80211_M_HOSTAP || 252659aa14a9SRui Paulo ic->ic_opmode == IEEE80211_M_MBSS) { 25270f2e86fbSSam Leffler /* 25280f2e86fbSSam Leffler * Always burst out beacon and CAB traffic. 25290f2e86fbSSam Leffler */ 25300f2e86fbSSam Leffler qi.tqi_aifs = ATH_BEACON_AIFS_DEFAULT; 25310f2e86fbSSam Leffler qi.tqi_cwmin = ATH_BEACON_CWMIN_DEFAULT; 25320f2e86fbSSam Leffler qi.tqi_cwmax = ATH_BEACON_CWMAX_DEFAULT; 25330f2e86fbSSam Leffler } else { 25340f2e86fbSSam Leffler struct wmeParams *wmep = 25350f2e86fbSSam Leffler &ic->ic_wme.wme_chanParams.cap_wmeParams[WME_AC_BE]; 25360f2e86fbSSam Leffler /* 25370f2e86fbSSam Leffler * Adhoc mode; important thing is to use 2x cwmin. 25380f2e86fbSSam Leffler */ 25390f2e86fbSSam Leffler qi.tqi_aifs = wmep->wmep_aifsn; 25400f2e86fbSSam Leffler qi.tqi_cwmin = 2*ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmin); 25410f2e86fbSSam Leffler qi.tqi_cwmax = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmax); 25420f2e86fbSSam Leffler } 25430f2e86fbSSam Leffler 25440f2e86fbSSam Leffler if (!ath_hal_settxqueueprops(ah, sc->sc_bhalq, &qi)) { 25450f2e86fbSSam Leffler device_printf(sc->sc_dev, "unable to update parameters for " 25460f2e86fbSSam Leffler "beacon hardware queue!\n"); 25470f2e86fbSSam Leffler return 0; 25480f2e86fbSSam Leffler } else { 25490f2e86fbSSam Leffler ath_hal_resettxqueue(ah, sc->sc_bhalq); /* push to h/w */ 25500f2e86fbSSam Leffler return 1; 25510f2e86fbSSam Leffler } 25520f2e86fbSSam Leffler #undef ATH_EXPONENT_TO_VALUE 25530f2e86fbSSam Leffler } 25540f2e86fbSSam Leffler 25550f2e86fbSSam Leffler /* 2556c42a7b7eSSam Leffler * Allocate and setup an initial beacon frame. 2557c42a7b7eSSam Leffler */ 25585591b213SSam Leffler static int 25595591b213SSam Leffler ath_beacon_alloc(struct ath_softc *sc, struct ieee80211_node *ni) 25605591b213SSam Leffler { 2561b032f27cSSam Leffler struct ieee80211vap *vap = ni->ni_vap; 2562b032f27cSSam Leffler struct ath_vap *avp = ATH_VAP(vap); 25635591b213SSam Leffler struct ath_buf *bf; 25645591b213SSam Leffler struct mbuf *m; 2565c42a7b7eSSam Leffler int error; 25665591b213SSam Leffler 2567b032f27cSSam Leffler bf = avp->av_bcbuf; 2568b032f27cSSam Leffler if (bf->bf_m != NULL) { 2569b032f27cSSam Leffler bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 2570b032f27cSSam Leffler m_freem(bf->bf_m); 2571b032f27cSSam Leffler bf->bf_m = NULL; 2572c42a7b7eSSam Leffler } 2573b032f27cSSam Leffler if (bf->bf_node != NULL) { 2574b032f27cSSam Leffler ieee80211_free_node(bf->bf_node); 2575b032f27cSSam Leffler bf->bf_node = NULL; 2576b032f27cSSam Leffler } 2577b032f27cSSam Leffler 25785591b213SSam Leffler /* 25795591b213SSam Leffler * NB: the beacon data buffer must be 32-bit aligned; 25805591b213SSam Leffler * we assume the mbuf routines will return us something 25815591b213SSam Leffler * with this alignment (perhaps should assert). 25825591b213SSam Leffler */ 2583b032f27cSSam Leffler m = ieee80211_beacon_alloc(ni, &avp->av_boff); 25845591b213SSam Leffler if (m == NULL) { 2585b032f27cSSam Leffler device_printf(sc->sc_dev, "%s: cannot get mbuf\n", __func__); 25865591b213SSam Leffler sc->sc_stats.ast_be_nombuf++; 25875591b213SSam Leffler return ENOMEM; 25885591b213SSam Leffler } 2589f9e6219bSSam Leffler error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m, 2590f9e6219bSSam Leffler bf->bf_segs, &bf->bf_nseg, 25915591b213SSam Leffler BUS_DMA_NOWAIT); 2592b032f27cSSam Leffler if (error != 0) { 2593b032f27cSSam Leffler device_printf(sc->sc_dev, 2594b032f27cSSam Leffler "%s: cannot map mbuf, bus_dmamap_load_mbuf_sg returns %d\n", 2595b032f27cSSam Leffler __func__, error); 2596b032f27cSSam Leffler m_freem(m); 2597b032f27cSSam Leffler return error; 2598b032f27cSSam Leffler } 2599b032f27cSSam Leffler 2600b032f27cSSam Leffler /* 2601b032f27cSSam Leffler * Calculate a TSF adjustment factor required for staggered 2602b032f27cSSam Leffler * beacons. Note that we assume the format of the beacon 2603b032f27cSSam Leffler * frame leaves the tstamp field immediately following the 2604b032f27cSSam Leffler * header. 2605b032f27cSSam Leffler */ 2606b032f27cSSam Leffler if (sc->sc_stagbeacons && avp->av_bslot > 0) { 2607b032f27cSSam Leffler uint64_t tsfadjust; 2608b032f27cSSam Leffler struct ieee80211_frame *wh; 2609b032f27cSSam Leffler 2610b032f27cSSam Leffler /* 2611b032f27cSSam Leffler * The beacon interval is in TU's; the TSF is in usecs. 2612b032f27cSSam Leffler * We figure out how many TU's to add to align the timestamp 2613b032f27cSSam Leffler * then convert to TSF units and handle byte swapping before 2614b032f27cSSam Leffler * inserting it in the frame. The hardware will then add this 2615b032f27cSSam Leffler * each time a beacon frame is sent. Note that we align vap's 2616b032f27cSSam Leffler * 1..N and leave vap 0 untouched. This means vap 0 has a 2617b032f27cSSam Leffler * timestamp in one beacon interval while the others get a 2618b032f27cSSam Leffler * timstamp aligned to the next interval. 2619b032f27cSSam Leffler */ 2620b032f27cSSam Leffler tsfadjust = ni->ni_intval * 2621b032f27cSSam Leffler (ATH_BCBUF - avp->av_bslot) / ATH_BCBUF; 2622b032f27cSSam Leffler tsfadjust = htole64(tsfadjust << 10); /* TU -> TSF */ 2623b032f27cSSam Leffler 2624b032f27cSSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON, 2625b032f27cSSam Leffler "%s: %s beacons bslot %d intval %u tsfadjust %llu\n", 2626b032f27cSSam Leffler __func__, sc->sc_stagbeacons ? "stagger" : "burst", 26273627e321SSam Leffler avp->av_bslot, ni->ni_intval, 26283627e321SSam Leffler (long long unsigned) le64toh(tsfadjust)); 2629b032f27cSSam Leffler 2630b032f27cSSam Leffler wh = mtod(m, struct ieee80211_frame *); 2631b032f27cSSam Leffler memcpy(&wh[1], &tsfadjust, sizeof(tsfadjust)); 2632b032f27cSSam Leffler } 2633c42a7b7eSSam Leffler bf->bf_m = m; 2634f818612bSSam Leffler bf->bf_node = ieee80211_ref_node(ni); 2635b032f27cSSam Leffler 2636b032f27cSSam Leffler return 0; 26375591b213SSam Leffler } 2638c42a7b7eSSam Leffler 2639c42a7b7eSSam Leffler /* 2640c42a7b7eSSam Leffler * Setup the beacon frame for transmit. 2641c42a7b7eSSam Leffler */ 2642c42a7b7eSSam Leffler static void 2643c42a7b7eSSam Leffler ath_beacon_setup(struct ath_softc *sc, struct ath_buf *bf) 2644c42a7b7eSSam Leffler { 2645c42a7b7eSSam Leffler #define USE_SHPREAMBLE(_ic) \ 2646c42a7b7eSSam Leffler (((_ic)->ic_flags & (IEEE80211_F_SHPREAMBLE | IEEE80211_F_USEBARKER))\ 2647c42a7b7eSSam Leffler == IEEE80211_F_SHPREAMBLE) 2648c42a7b7eSSam Leffler struct ieee80211_node *ni = bf->bf_node; 2649c42a7b7eSSam Leffler struct ieee80211com *ic = ni->ni_ic; 2650c42a7b7eSSam Leffler struct mbuf *m = bf->bf_m; 2651c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 2652c42a7b7eSSam Leffler struct ath_desc *ds; 2653c42a7b7eSSam Leffler int flags, antenna; 265455f63772SSam Leffler const HAL_RATE_TABLE *rt; 265555f63772SSam Leffler u_int8_t rix, rate; 2656c42a7b7eSSam Leffler 26574a3ac3fcSSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s: m %p len %u\n", 2658c42a7b7eSSam Leffler __func__, m, m->m_len); 26595591b213SSam Leffler 26605591b213SSam Leffler /* setup descriptors */ 26615591b213SSam Leffler ds = bf->bf_desc; 26625591b213SSam Leffler 2663c42a7b7eSSam Leffler flags = HAL_TXDESC_NOACK; 2664c42a7b7eSSam Leffler if (ic->ic_opmode == IEEE80211_M_IBSS && sc->sc_hasveol) { 2665c42a7b7eSSam Leffler ds->ds_link = bf->bf_daddr; /* self-linked */ 2666c42a7b7eSSam Leffler flags |= HAL_TXDESC_VEOL; 2667c42a7b7eSSam Leffler /* 2668c42a7b7eSSam Leffler * Let hardware handle antenna switching. 2669c42a7b7eSSam Leffler */ 26704866e6c2SSam Leffler antenna = sc->sc_txantenna; 2671c42a7b7eSSam Leffler } else { 26725591b213SSam Leffler ds->ds_link = 0; 2673c42a7b7eSSam Leffler /* 2674c42a7b7eSSam Leffler * Switch antenna every 4 beacons. 2675c42a7b7eSSam Leffler * XXX assumes two antenna 2676c42a7b7eSSam Leffler */ 2677b032f27cSSam Leffler if (sc->sc_txantenna != 0) 2678b032f27cSSam Leffler antenna = sc->sc_txantenna; 2679b032f27cSSam Leffler else if (sc->sc_stagbeacons && sc->sc_nbcnvaps != 0) 2680b032f27cSSam Leffler antenna = ((sc->sc_stats.ast_be_xmit / sc->sc_nbcnvaps) & 4 ? 2 : 1); 2681b032f27cSSam Leffler else 2682b032f27cSSam Leffler antenna = (sc->sc_stats.ast_be_xmit & 4 ? 2 : 1); 2683c42a7b7eSSam Leffler } 2684c42a7b7eSSam Leffler 2685c42a7b7eSSam Leffler KASSERT(bf->bf_nseg == 1, 2686c42a7b7eSSam Leffler ("multi-segment beacon frame; nseg %u", bf->bf_nseg)); 26875591b213SSam Leffler ds->ds_data = bf->bf_segs[0].ds_addr; 26885591b213SSam Leffler /* 26895591b213SSam Leffler * Calculate rate code. 26905591b213SSam Leffler * XXX everything at min xmit rate 26915591b213SSam Leffler */ 2692b032f27cSSam Leffler rix = 0; 269355f63772SSam Leffler rt = sc->sc_currates; 269455f63772SSam Leffler rate = rt->info[rix].rateCode; 2695c42a7b7eSSam Leffler if (USE_SHPREAMBLE(ic)) 269655f63772SSam Leffler rate |= rt->info[rix].shortPreamble; 26975591b213SSam Leffler ath_hal_setuptxdesc(ah, ds 2698c42a7b7eSSam Leffler , m->m_len + IEEE80211_CRC_LEN /* frame length */ 26995591b213SSam Leffler , sizeof(struct ieee80211_frame)/* header length */ 27005591b213SSam Leffler , HAL_PKT_TYPE_BEACON /* Atheros packet type */ 2701c42a7b7eSSam Leffler , ni->ni_txpower /* txpower XXX */ 27025591b213SSam Leffler , rate, 1 /* series 0 rate/tries */ 27035591b213SSam Leffler , HAL_TXKEYIX_INVALID /* no encryption */ 2704c42a7b7eSSam Leffler , antenna /* antenna mode */ 2705c42a7b7eSSam Leffler , flags /* no ack, veol for beacons */ 27065591b213SSam Leffler , 0 /* rts/cts rate */ 27075591b213SSam Leffler , 0 /* rts/cts duration */ 27085591b213SSam Leffler ); 27095591b213SSam Leffler /* NB: beacon's BufLen must be a multiple of 4 bytes */ 27105591b213SSam Leffler ath_hal_filltxdesc(ah, ds 2711c42a7b7eSSam Leffler , roundup(m->m_len, 4) /* buffer length */ 27125591b213SSam Leffler , AH_TRUE /* first segment */ 27135591b213SSam Leffler , AH_TRUE /* last segment */ 2714c42a7b7eSSam Leffler , ds /* first descriptor */ 27155591b213SSam Leffler ); 2716b032f27cSSam Leffler #if 0 2717b032f27cSSam Leffler ath_desc_swap(ds); 2718b032f27cSSam Leffler #endif 2719c42a7b7eSSam Leffler #undef USE_SHPREAMBLE 27205591b213SSam Leffler } 27215591b213SSam Leffler 2722b105a069SSam Leffler static void 2723b032f27cSSam Leffler ath_beacon_update(struct ieee80211vap *vap, int item) 2724b105a069SSam Leffler { 2725b032f27cSSam Leffler struct ieee80211_beacon_offsets *bo = &ATH_VAP(vap)->av_boff; 2726b105a069SSam Leffler 2727b105a069SSam Leffler setbit(bo->bo_flags, item); 2728b105a069SSam Leffler } 2729b105a069SSam Leffler 2730c42a7b7eSSam Leffler /* 2731622b3fd2SSam Leffler * Append the contents of src to dst; both queues 2732622b3fd2SSam Leffler * are assumed to be locked. 2733622b3fd2SSam Leffler */ 2734622b3fd2SSam Leffler static void 2735622b3fd2SSam Leffler ath_txqmove(struct ath_txq *dst, struct ath_txq *src) 2736622b3fd2SSam Leffler { 2737622b3fd2SSam Leffler STAILQ_CONCAT(&dst->axq_q, &src->axq_q); 2738622b3fd2SSam Leffler dst->axq_link = src->axq_link; 2739622b3fd2SSam Leffler src->axq_link = NULL; 2740622b3fd2SSam Leffler dst->axq_depth += src->axq_depth; 2741622b3fd2SSam Leffler src->axq_depth = 0; 2742622b3fd2SSam Leffler } 2743622b3fd2SSam Leffler 2744622b3fd2SSam Leffler /* 2745c42a7b7eSSam Leffler * Transmit a beacon frame at SWBA. Dynamic updates to the 2746c42a7b7eSSam Leffler * frame contents are done as needed and the slot time is 2747c42a7b7eSSam Leffler * also adjusted based on current state. 2748c42a7b7eSSam Leffler */ 27495591b213SSam Leffler static void 27505591b213SSam Leffler ath_beacon_proc(void *arg, int pending) 27515591b213SSam Leffler { 27525591b213SSam Leffler struct ath_softc *sc = arg; 27535591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 2754b032f27cSSam Leffler struct ieee80211vap *vap; 2755b032f27cSSam Leffler struct ath_buf *bf; 2756b032f27cSSam Leffler int slot, otherant; 2757b032f27cSSam Leffler uint32_t bfaddr; 27585591b213SSam Leffler 2759c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s: pending %u\n", 2760c42a7b7eSSam Leffler __func__, pending); 2761c42a7b7eSSam Leffler /* 2762c42a7b7eSSam Leffler * Check if the previous beacon has gone out. If 2763c66c48cbSSam Leffler * not don't try to post another, skip this period 2764c66c48cbSSam Leffler * and wait for the next. Missed beacons indicate 2765c66c48cbSSam Leffler * a problem and should not occur. If we miss too 2766c66c48cbSSam Leffler * many consecutive beacons reset the device. 2767c42a7b7eSSam Leffler */ 2768c42a7b7eSSam Leffler if (ath_hal_numtxpending(ah, sc->sc_bhalq) != 0) { 2769c42a7b7eSSam Leffler sc->sc_bmisscount++; 27707ec4e6b8SAdrian Chadd sc->sc_stats.ast_be_missed++; 27714a3ac3fcSSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON, 2772c42a7b7eSSam Leffler "%s: missed %u consecutive beacons\n", 2773c42a7b7eSSam Leffler __func__, sc->sc_bmisscount); 2774a32ac9d3SSam Leffler if (sc->sc_bmisscount >= ath_bstuck_threshold) 27750bbf5441SSam Leffler taskqueue_enqueue(sc->sc_tq, &sc->sc_bstucktask); 2776c42a7b7eSSam Leffler return; 2777c42a7b7eSSam Leffler } 2778c42a7b7eSSam Leffler if (sc->sc_bmisscount != 0) { 2779c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON, 2780c42a7b7eSSam Leffler "%s: resume beacon xmit after %u misses\n", 2781c42a7b7eSSam Leffler __func__, sc->sc_bmisscount); 2782c42a7b7eSSam Leffler sc->sc_bmisscount = 0; 2783c42a7b7eSSam Leffler } 2784c42a7b7eSSam Leffler 2785b032f27cSSam Leffler if (sc->sc_stagbeacons) { /* staggered beacons */ 2786b032f27cSSam Leffler struct ieee80211com *ic = sc->sc_ifp->if_l2com; 2787b032f27cSSam Leffler uint32_t tsftu; 2788b032f27cSSam Leffler 2789b032f27cSSam Leffler tsftu = ath_hal_gettsf32(ah) >> 10; 2790b032f27cSSam Leffler /* XXX lintval */ 2791b032f27cSSam Leffler slot = ((tsftu % ic->ic_lintval) * ATH_BCBUF) / ic->ic_lintval; 2792b032f27cSSam Leffler vap = sc->sc_bslot[(slot+1) % ATH_BCBUF]; 2793b032f27cSSam Leffler bfaddr = 0; 2794309a3e45SSam Leffler if (vap != NULL && vap->iv_state >= IEEE80211_S_RUN) { 2795b032f27cSSam Leffler bf = ath_beacon_generate(sc, vap); 2796b032f27cSSam Leffler if (bf != NULL) 2797b032f27cSSam Leffler bfaddr = bf->bf_daddr; 2798b032f27cSSam Leffler } 2799b032f27cSSam Leffler } else { /* burst'd beacons */ 2800b032f27cSSam Leffler uint32_t *bflink = &bfaddr; 2801b032f27cSSam Leffler 2802b032f27cSSam Leffler for (slot = 0; slot < ATH_BCBUF; slot++) { 2803b032f27cSSam Leffler vap = sc->sc_bslot[slot]; 2804309a3e45SSam Leffler if (vap != NULL && vap->iv_state >= IEEE80211_S_RUN) { 2805b032f27cSSam Leffler bf = ath_beacon_generate(sc, vap); 2806b032f27cSSam Leffler if (bf != NULL) { 2807b032f27cSSam Leffler *bflink = bf->bf_daddr; 2808b032f27cSSam Leffler bflink = &bf->bf_desc->ds_link; 2809c42a7b7eSSam Leffler } 2810c42a7b7eSSam Leffler } 2811b032f27cSSam Leffler } 2812b032f27cSSam Leffler *bflink = 0; /* terminate list */ 2813622b3fd2SSam Leffler } 2814c42a7b7eSSam Leffler 2815c42a7b7eSSam Leffler /* 2816c42a7b7eSSam Leffler * Handle slot time change when a non-ERP station joins/leaves 2817c42a7b7eSSam Leffler * an 11g network. The 802.11 layer notifies us via callback, 2818c42a7b7eSSam Leffler * we mark updateslot, then wait one beacon before effecting 2819c42a7b7eSSam Leffler * the change. This gives associated stations at least one 2820c42a7b7eSSam Leffler * beacon interval to note the state change. 2821c42a7b7eSSam Leffler */ 2822c42a7b7eSSam Leffler /* XXX locking */ 2823b032f27cSSam Leffler if (sc->sc_updateslot == UPDATE) { 2824c42a7b7eSSam Leffler sc->sc_updateslot = COMMIT; /* commit next beacon */ 2825b032f27cSSam Leffler sc->sc_slotupdate = slot; 2826b032f27cSSam Leffler } else if (sc->sc_updateslot == COMMIT && sc->sc_slotupdate == slot) 2827c42a7b7eSSam Leffler ath_setslottime(sc); /* commit change to h/w */ 2828c42a7b7eSSam Leffler 2829c42a7b7eSSam Leffler /* 2830c42a7b7eSSam Leffler * Check recent per-antenna transmit statistics and flip 2831c42a7b7eSSam Leffler * the default antenna if noticeably more frames went out 2832c42a7b7eSSam Leffler * on the non-default antenna. 2833c42a7b7eSSam Leffler * XXX assumes 2 anntenae 2834c42a7b7eSSam Leffler */ 2835b032f27cSSam Leffler if (!sc->sc_diversity && (!sc->sc_stagbeacons || slot == 0)) { 2836c42a7b7eSSam Leffler otherant = sc->sc_defant & 1 ? 2 : 1; 2837c42a7b7eSSam Leffler if (sc->sc_ant_tx[otherant] > sc->sc_ant_tx[sc->sc_defant] + 2) 2838c42a7b7eSSam Leffler ath_setdefantenna(sc, otherant); 2839c42a7b7eSSam Leffler sc->sc_ant_tx[1] = sc->sc_ant_tx[2] = 0; 2840b032f27cSSam Leffler } 2841c42a7b7eSSam Leffler 2842b032f27cSSam Leffler if (bfaddr != 0) { 2843c42a7b7eSSam Leffler /* 2844c42a7b7eSSam Leffler * Stop any current dma and put the new frame on the queue. 2845c42a7b7eSSam Leffler * This should never fail since we check above that no frames 2846c42a7b7eSSam Leffler * are still pending on the queue. 2847c42a7b7eSSam Leffler */ 28485591b213SSam Leffler if (!ath_hal_stoptxdma(ah, sc->sc_bhalq)) { 2849c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, 2850c42a7b7eSSam Leffler "%s: beacon queue %u did not stop?\n", 2851c42a7b7eSSam Leffler __func__, sc->sc_bhalq); 28525591b213SSam Leffler } 2853b032f27cSSam Leffler /* NB: cabq traffic should already be queued and primed */ 2854b032f27cSSam Leffler ath_hal_puttxbuf(ah, sc->sc_bhalq, bfaddr); 2855b032f27cSSam Leffler ath_hal_txstart(ah, sc->sc_bhalq); 2856b032f27cSSam Leffler 2857b032f27cSSam Leffler sc->sc_stats.ast_be_xmit++; 2858b032f27cSSam Leffler } 2859b032f27cSSam Leffler } 2860b032f27cSSam Leffler 2861b032f27cSSam Leffler static struct ath_buf * 2862b032f27cSSam Leffler ath_beacon_generate(struct ath_softc *sc, struct ieee80211vap *vap) 2863b032f27cSSam Leffler { 2864b032f27cSSam Leffler struct ath_vap *avp = ATH_VAP(vap); 2865b032f27cSSam Leffler struct ath_txq *cabq = sc->sc_cabq; 2866b032f27cSSam Leffler struct ath_buf *bf; 2867b032f27cSSam Leffler struct mbuf *m; 2868b032f27cSSam Leffler int nmcastq, error; 2869b032f27cSSam Leffler 2870309a3e45SSam Leffler KASSERT(vap->iv_state >= IEEE80211_S_RUN, 2871b032f27cSSam Leffler ("not running, state %d", vap->iv_state)); 2872b032f27cSSam Leffler KASSERT(avp->av_bcbuf != NULL, ("no beacon buffer")); 2873b032f27cSSam Leffler 2874b032f27cSSam Leffler /* 2875b032f27cSSam Leffler * Update dynamic beacon contents. If this returns 2876b032f27cSSam Leffler * non-zero then we need to remap the memory because 2877b032f27cSSam Leffler * the beacon frame changed size (probably because 2878b032f27cSSam Leffler * of the TIM bitmap). 2879b032f27cSSam Leffler */ 2880b032f27cSSam Leffler bf = avp->av_bcbuf; 2881b032f27cSSam Leffler m = bf->bf_m; 2882b032f27cSSam Leffler nmcastq = avp->av_mcastq.axq_depth; 2883b032f27cSSam Leffler if (ieee80211_beacon_update(bf->bf_node, &avp->av_boff, m, nmcastq)) { 2884b032f27cSSam Leffler /* XXX too conservative? */ 2885b032f27cSSam Leffler bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 2886b032f27cSSam Leffler error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m, 2887b032f27cSSam Leffler bf->bf_segs, &bf->bf_nseg, 2888b032f27cSSam Leffler BUS_DMA_NOWAIT); 2889b032f27cSSam Leffler if (error != 0) { 2890b032f27cSSam Leffler if_printf(vap->iv_ifp, 2891b032f27cSSam Leffler "%s: bus_dmamap_load_mbuf_sg failed, error %u\n", 2892b032f27cSSam Leffler __func__, error); 2893b032f27cSSam Leffler return NULL; 2894b032f27cSSam Leffler } 2895b032f27cSSam Leffler } 2896b032f27cSSam Leffler if ((avp->av_boff.bo_tim[4] & 1) && cabq->axq_depth) { 2897b032f27cSSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON, 2898b032f27cSSam Leffler "%s: cabq did not drain, mcastq %u cabq %u\n", 2899b032f27cSSam Leffler __func__, nmcastq, cabq->axq_depth); 2900b032f27cSSam Leffler sc->sc_stats.ast_cabq_busy++; 2901b032f27cSSam Leffler if (sc->sc_nvaps > 1 && sc->sc_stagbeacons) { 2902b032f27cSSam Leffler /* 2903b032f27cSSam Leffler * CABQ traffic from a previous vap is still pending. 2904b032f27cSSam Leffler * We must drain the q before this beacon frame goes 2905b032f27cSSam Leffler * out as otherwise this vap's stations will get cab 2906b032f27cSSam Leffler * frames from a different vap. 2907b032f27cSSam Leffler * XXX could be slow causing us to miss DBA 2908b032f27cSSam Leffler */ 2909b032f27cSSam Leffler ath_tx_draintxq(sc, cabq); 2910b032f27cSSam Leffler } 2911b032f27cSSam Leffler } 2912b032f27cSSam Leffler ath_beacon_setup(sc, bf); 29135591b213SSam Leffler bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE); 29145591b213SSam Leffler 2915c42a7b7eSSam Leffler /* 2916c42a7b7eSSam Leffler * Enable the CAB queue before the beacon queue to 2917c42a7b7eSSam Leffler * insure cab frames are triggered by this beacon. 2918c42a7b7eSSam Leffler */ 2919b032f27cSSam Leffler if (avp->av_boff.bo_tim[4] & 1) { 2920b032f27cSSam Leffler struct ath_hal *ah = sc->sc_ah; 2921b032f27cSSam Leffler 2922f3af83f7SSam Leffler /* NB: only at DTIM */ 2923622b3fd2SSam Leffler ATH_TXQ_LOCK(cabq); 2924b032f27cSSam Leffler ATH_TXQ_LOCK(&avp->av_mcastq); 2925622b3fd2SSam Leffler if (nmcastq) { 2926622b3fd2SSam Leffler struct ath_buf *bfm; 2927622b3fd2SSam Leffler 2928622b3fd2SSam Leffler /* 2929622b3fd2SSam Leffler * Move frames from the s/w mcast q to the h/w cab q. 2930b032f27cSSam Leffler * XXX MORE_DATA bit 2931622b3fd2SSam Leffler */ 2932b032f27cSSam Leffler bfm = STAILQ_FIRST(&avp->av_mcastq.axq_q); 2933622b3fd2SSam Leffler if (cabq->axq_link != NULL) { 2934622b3fd2SSam Leffler *cabq->axq_link = bfm->bf_daddr; 2935622b3fd2SSam Leffler } else 2936622b3fd2SSam Leffler ath_hal_puttxbuf(ah, cabq->axq_qnum, 2937622b3fd2SSam Leffler bfm->bf_daddr); 2938b032f27cSSam Leffler ath_txqmove(cabq, &avp->av_mcastq); 2939622b3fd2SSam Leffler 2940622b3fd2SSam Leffler sc->sc_stats.ast_cabq_xmit += nmcastq; 2941622b3fd2SSam Leffler } 2942622b3fd2SSam Leffler /* NB: gated by beacon so safe to start here */ 2943622b3fd2SSam Leffler ath_hal_txstart(ah, cabq->axq_qnum); 2944622b3fd2SSam Leffler ATH_TXQ_UNLOCK(cabq); 2945b032f27cSSam Leffler ATH_TXQ_UNLOCK(&avp->av_mcastq); 2946622b3fd2SSam Leffler } 2947b032f27cSSam Leffler return bf; 2948b032f27cSSam Leffler } 2949b032f27cSSam Leffler 2950b032f27cSSam Leffler static void 2951b032f27cSSam Leffler ath_beacon_start_adhoc(struct ath_softc *sc, struct ieee80211vap *vap) 2952b032f27cSSam Leffler { 2953b032f27cSSam Leffler struct ath_vap *avp = ATH_VAP(vap); 2954b032f27cSSam Leffler struct ath_hal *ah = sc->sc_ah; 2955b032f27cSSam Leffler struct ath_buf *bf; 2956b032f27cSSam Leffler struct mbuf *m; 2957b032f27cSSam Leffler int error; 2958b032f27cSSam Leffler 2959b032f27cSSam Leffler KASSERT(avp->av_bcbuf != NULL, ("no beacon buffer")); 2960b032f27cSSam Leffler 2961b032f27cSSam Leffler /* 2962b032f27cSSam Leffler * Update dynamic beacon contents. If this returns 2963b032f27cSSam Leffler * non-zero then we need to remap the memory because 2964b032f27cSSam Leffler * the beacon frame changed size (probably because 2965b032f27cSSam Leffler * of the TIM bitmap). 2966b032f27cSSam Leffler */ 2967b032f27cSSam Leffler bf = avp->av_bcbuf; 2968b032f27cSSam Leffler m = bf->bf_m; 2969b032f27cSSam Leffler if (ieee80211_beacon_update(bf->bf_node, &avp->av_boff, m, 0)) { 2970b032f27cSSam Leffler /* XXX too conservative? */ 2971b032f27cSSam Leffler bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 2972b032f27cSSam Leffler error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m, 2973b032f27cSSam Leffler bf->bf_segs, &bf->bf_nseg, 2974b032f27cSSam Leffler BUS_DMA_NOWAIT); 2975b032f27cSSam Leffler if (error != 0) { 2976b032f27cSSam Leffler if_printf(vap->iv_ifp, 2977b032f27cSSam Leffler "%s: bus_dmamap_load_mbuf_sg failed, error %u\n", 2978b032f27cSSam Leffler __func__, error); 2979b032f27cSSam Leffler return; 2980b032f27cSSam Leffler } 2981b032f27cSSam Leffler } 2982b032f27cSSam Leffler ath_beacon_setup(sc, bf); 2983b032f27cSSam Leffler bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE); 2984b032f27cSSam Leffler 2985b032f27cSSam Leffler /* NB: caller is known to have already stopped tx dma */ 29865591b213SSam Leffler ath_hal_puttxbuf(ah, sc->sc_bhalq, bf->bf_daddr); 29875591b213SSam Leffler ath_hal_txstart(ah, sc->sc_bhalq); 29885591b213SSam Leffler } 29895591b213SSam Leffler 2990c42a7b7eSSam Leffler /* 2991c42a7b7eSSam Leffler * Reset the hardware after detecting beacons have stopped. 2992c42a7b7eSSam Leffler */ 2993c42a7b7eSSam Leffler static void 2994c42a7b7eSSam Leffler ath_bstuck_proc(void *arg, int pending) 2995c42a7b7eSSam Leffler { 2996c42a7b7eSSam Leffler struct ath_softc *sc = arg; 2997fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 2998c42a7b7eSSam Leffler 2999c42a7b7eSSam Leffler if_printf(ifp, "stuck beacon; resetting (bmiss count %u)\n", 3000c42a7b7eSSam Leffler sc->sc_bmisscount); 3001c2e34459SSam Leffler sc->sc_stats.ast_bstuck++; 3002c42a7b7eSSam Leffler ath_reset(ifp); 3003c42a7b7eSSam Leffler } 3004c42a7b7eSSam Leffler 3005c42a7b7eSSam Leffler /* 3006b032f27cSSam Leffler * Reclaim beacon resources and return buffer to the pool. 3007b032f27cSSam Leffler */ 3008b032f27cSSam Leffler static void 3009b032f27cSSam Leffler ath_beacon_return(struct ath_softc *sc, struct ath_buf *bf) 3010b032f27cSSam Leffler { 3011b032f27cSSam Leffler 3012b032f27cSSam Leffler if (bf->bf_m != NULL) { 3013b032f27cSSam Leffler bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 3014b032f27cSSam Leffler m_freem(bf->bf_m); 3015b032f27cSSam Leffler bf->bf_m = NULL; 3016b032f27cSSam Leffler } 3017b032f27cSSam Leffler if (bf->bf_node != NULL) { 3018b032f27cSSam Leffler ieee80211_free_node(bf->bf_node); 3019b032f27cSSam Leffler bf->bf_node = NULL; 3020b032f27cSSam Leffler } 3021b032f27cSSam Leffler STAILQ_INSERT_TAIL(&sc->sc_bbuf, bf, bf_list); 3022b032f27cSSam Leffler } 3023b032f27cSSam Leffler 3024b032f27cSSam Leffler /* 3025c42a7b7eSSam Leffler * Reclaim beacon resources. 3026c42a7b7eSSam Leffler */ 30275591b213SSam Leffler static void 30285591b213SSam Leffler ath_beacon_free(struct ath_softc *sc) 30295591b213SSam Leffler { 3030c42a7b7eSSam Leffler struct ath_buf *bf; 30315591b213SSam Leffler 3032f818612bSSam Leffler STAILQ_FOREACH(bf, &sc->sc_bbuf, bf_list) { 30335591b213SSam Leffler if (bf->bf_m != NULL) { 30345591b213SSam Leffler bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 30355591b213SSam Leffler m_freem(bf->bf_m); 30365591b213SSam Leffler bf->bf_m = NULL; 3037f818612bSSam Leffler } 3038f818612bSSam Leffler if (bf->bf_node != NULL) { 3039f818612bSSam Leffler ieee80211_free_node(bf->bf_node); 30405591b213SSam Leffler bf->bf_node = NULL; 30415591b213SSam Leffler } 30425591b213SSam Leffler } 3043f818612bSSam Leffler } 30445591b213SSam Leffler 30455591b213SSam Leffler /* 30465591b213SSam Leffler * Configure the beacon and sleep timers. 30475591b213SSam Leffler * 30485591b213SSam Leffler * When operating as an AP this resets the TSF and sets 30495591b213SSam Leffler * up the hardware to notify us when we need to issue beacons. 30505591b213SSam Leffler * 30515591b213SSam Leffler * When operating in station mode this sets up the beacon 30525591b213SSam Leffler * timers according to the timestamp of the last received 30535591b213SSam Leffler * beacon and the current TSF, configures PCF and DTIM 30545591b213SSam Leffler * handling, programs the sleep registers so the hardware 30555591b213SSam Leffler * will wakeup in time to receive beacons, and configures 30565591b213SSam Leffler * the beacon miss handling so we'll receive a BMISS 30575591b213SSam Leffler * interrupt when we stop seeing beacons from the AP 30585591b213SSam Leffler * we've associated with. 30595591b213SSam Leffler */ 30605591b213SSam Leffler static void 3061b032f27cSSam Leffler ath_beacon_config(struct ath_softc *sc, struct ieee80211vap *vap) 30625591b213SSam Leffler { 306380d939bfSSam Leffler #define TSF_TO_TU(_h,_l) \ 306480d939bfSSam Leffler ((((u_int32_t)(_h)) << 22) | (((u_int32_t)(_l)) >> 10)) 306580d939bfSSam Leffler #define FUDGE 2 30665591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 3067b032f27cSSam Leffler struct ieee80211com *ic = sc->sc_ifp->if_l2com; 3068b032f27cSSam Leffler struct ieee80211_node *ni; 306980d939bfSSam Leffler u_int32_t nexttbtt, intval, tsftu; 307080d939bfSSam Leffler u_int64_t tsf; 30715591b213SSam Leffler 3072b032f27cSSam Leffler if (vap == NULL) 3073b032f27cSSam Leffler vap = TAILQ_FIRST(&ic->ic_vaps); /* XXX */ 3074b032f27cSSam Leffler ni = vap->iv_bss; 3075b032f27cSSam Leffler 30768371372bSSam Leffler /* extract tstamp from last beacon and convert to TU */ 30778371372bSSam Leffler nexttbtt = TSF_TO_TU(LE_READ_4(ni->ni_tstamp.data + 4), 30788371372bSSam Leffler LE_READ_4(ni->ni_tstamp.data)); 307959aa14a9SRui Paulo if (ic->ic_opmode == IEEE80211_M_HOSTAP || 308059aa14a9SRui Paulo ic->ic_opmode == IEEE80211_M_MBSS) { 3081b032f27cSSam Leffler /* 308259aa14a9SRui Paulo * For multi-bss ap/mesh support beacons are either staggered 3083b032f27cSSam Leffler * evenly over N slots or burst together. For the former 3084b032f27cSSam Leffler * arrange for the SWBA to be delivered for each slot. 3085b032f27cSSam Leffler * Slots that are not occupied will generate nothing. 3086b032f27cSSam Leffler */ 30878371372bSSam Leffler /* NB: the beacon interval is kept internally in TU's */ 30884bacf7c1SSam Leffler intval = ni->ni_intval & HAL_BEACON_PERIOD; 3089b032f27cSSam Leffler if (sc->sc_stagbeacons) 3090b032f27cSSam Leffler intval /= ATH_BCBUF; 3091b032f27cSSam Leffler } else { 3092b032f27cSSam Leffler /* NB: the beacon interval is kept internally in TU's */ 3093b032f27cSSam Leffler intval = ni->ni_intval & HAL_BEACON_PERIOD; 3094b032f27cSSam Leffler } 3095a6c992f4SSam Leffler if (nexttbtt == 0) /* e.g. for ap mode */ 3096a6c992f4SSam Leffler nexttbtt = intval; 3097a6c992f4SSam Leffler else if (intval) /* NB: can be 0 for monitor mode */ 3098a6c992f4SSam Leffler nexttbtt = roundup(nexttbtt, intval); 3099a6c992f4SSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON, "%s: nexttbtt %u intval %u (%u)\n", 3100a6c992f4SSam Leffler __func__, nexttbtt, intval, ni->ni_intval); 3101b032f27cSSam Leffler if (ic->ic_opmode == IEEE80211_M_STA && !sc->sc_swbmiss) { 31025591b213SSam Leffler HAL_BEACON_STATE bs; 31038371372bSSam Leffler int dtimperiod, dtimcount; 31048371372bSSam Leffler int cfpperiod, cfpcount; 31055591b213SSam Leffler 31068371372bSSam Leffler /* 31078371372bSSam Leffler * Setup dtim and cfp parameters according to 31088371372bSSam Leffler * last beacon we received (which may be none). 31098371372bSSam Leffler */ 31108371372bSSam Leffler dtimperiod = ni->ni_dtim_period; 31118371372bSSam Leffler if (dtimperiod <= 0) /* NB: 0 if not known */ 31128371372bSSam Leffler dtimperiod = 1; 31138371372bSSam Leffler dtimcount = ni->ni_dtim_count; 31148371372bSSam Leffler if (dtimcount >= dtimperiod) /* NB: sanity check */ 31158371372bSSam Leffler dtimcount = 0; /* XXX? */ 31168371372bSSam Leffler cfpperiod = 1; /* NB: no PCF support yet */ 31178371372bSSam Leffler cfpcount = 0; 31188371372bSSam Leffler /* 31198371372bSSam Leffler * Pull nexttbtt forward to reflect the current 31208371372bSSam Leffler * TSF and calculate dtim+cfp state for the result. 31218371372bSSam Leffler */ 31228371372bSSam Leffler tsf = ath_hal_gettsf64(ah); 312380d939bfSSam Leffler tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; 31248371372bSSam Leffler do { 31258371372bSSam Leffler nexttbtt += intval; 31268371372bSSam Leffler if (--dtimcount < 0) { 31278371372bSSam Leffler dtimcount = dtimperiod - 1; 31288371372bSSam Leffler if (--cfpcount < 0) 31298371372bSSam Leffler cfpcount = cfpperiod - 1; 31308371372bSSam Leffler } 31318371372bSSam Leffler } while (nexttbtt < tsftu); 31325591b213SSam Leffler memset(&bs, 0, sizeof(bs)); 3133a6c992f4SSam Leffler bs.bs_intval = intval; 31345591b213SSam Leffler bs.bs_nexttbtt = nexttbtt; 31358371372bSSam Leffler bs.bs_dtimperiod = dtimperiod*intval; 31368371372bSSam Leffler bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval; 31378371372bSSam Leffler bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod; 31388371372bSSam Leffler bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod; 31398371372bSSam Leffler bs.bs_cfpmaxduration = 0; 31408371372bSSam Leffler #if 0 31415591b213SSam Leffler /* 3142c42a7b7eSSam Leffler * The 802.11 layer records the offset to the DTIM 3143c42a7b7eSSam Leffler * bitmap while receiving beacons; use it here to 3144c42a7b7eSSam Leffler * enable h/w detection of our AID being marked in 3145c42a7b7eSSam Leffler * the bitmap vector (to indicate frames for us are 3146c42a7b7eSSam Leffler * pending at the AP). 31478371372bSSam Leffler * XXX do DTIM handling in s/w to WAR old h/w bugs 31488371372bSSam Leffler * XXX enable based on h/w rev for newer chips 3149c42a7b7eSSam Leffler */ 3150c42a7b7eSSam Leffler bs.bs_timoffset = ni->ni_timoff; 31518371372bSSam Leffler #endif 3152c42a7b7eSSam Leffler /* 31535591b213SSam Leffler * Calculate the number of consecutive beacons to miss 315468e8e04eSSam Leffler * before taking a BMISS interrupt. 31555591b213SSam Leffler * Note that we clamp the result to at most 10 beacons. 31565591b213SSam Leffler */ 3157b032f27cSSam Leffler bs.bs_bmissthreshold = vap->iv_bmissthreshold; 31585591b213SSam Leffler if (bs.bs_bmissthreshold > 10) 31595591b213SSam Leffler bs.bs_bmissthreshold = 10; 31605591b213SSam Leffler else if (bs.bs_bmissthreshold <= 0) 31615591b213SSam Leffler bs.bs_bmissthreshold = 1; 31625591b213SSam Leffler 31635591b213SSam Leffler /* 31645591b213SSam Leffler * Calculate sleep duration. The configuration is 31655591b213SSam Leffler * given in ms. We insure a multiple of the beacon 31665591b213SSam Leffler * period is used. Also, if the sleep duration is 31675591b213SSam Leffler * greater than the DTIM period then it makes senses 31685591b213SSam Leffler * to make it a multiple of that. 31695591b213SSam Leffler * 31705591b213SSam Leffler * XXX fixed at 100ms 31715591b213SSam Leffler */ 31724bacf7c1SSam Leffler bs.bs_sleepduration = 31734bacf7c1SSam Leffler roundup(IEEE80211_MS_TO_TU(100), bs.bs_intval); 31745591b213SSam Leffler if (bs.bs_sleepduration > bs.bs_dtimperiod) 31755591b213SSam Leffler bs.bs_sleepduration = roundup(bs.bs_sleepduration, bs.bs_dtimperiod); 31765591b213SSam Leffler 3177c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON, 31788371372bSSam Leffler "%s: tsf %ju tsf:tu %u intval %u nexttbtt %u dtim %u nextdtim %u bmiss %u sleep %u cfp:period %u maxdur %u next %u timoffset %u\n" 31795591b213SSam Leffler , __func__ 31808371372bSSam Leffler , tsf, tsftu 31815591b213SSam Leffler , bs.bs_intval 31825591b213SSam Leffler , bs.bs_nexttbtt 31835591b213SSam Leffler , bs.bs_dtimperiod 31845591b213SSam Leffler , bs.bs_nextdtim 31855591b213SSam Leffler , bs.bs_bmissthreshold 31865591b213SSam Leffler , bs.bs_sleepduration 3187c42a7b7eSSam Leffler , bs.bs_cfpperiod 3188c42a7b7eSSam Leffler , bs.bs_cfpmaxduration 3189c42a7b7eSSam Leffler , bs.bs_cfpnext 3190c42a7b7eSSam Leffler , bs.bs_timoffset 3191c42a7b7eSSam Leffler ); 31925591b213SSam Leffler ath_hal_intrset(ah, 0); 3193c42a7b7eSSam Leffler ath_hal_beacontimers(ah, &bs); 31945591b213SSam Leffler sc->sc_imask |= HAL_INT_BMISS; 31955591b213SSam Leffler ath_hal_intrset(ah, sc->sc_imask); 31965591b213SSam Leffler } else { 31975591b213SSam Leffler ath_hal_intrset(ah, 0); 3198a6c992f4SSam Leffler if (nexttbtt == intval) 3199c42a7b7eSSam Leffler intval |= HAL_BEACON_RESET_TSF; 3200c42a7b7eSSam Leffler if (ic->ic_opmode == IEEE80211_M_IBSS) { 3201c42a7b7eSSam Leffler /* 3202c42a7b7eSSam Leffler * In IBSS mode enable the beacon timers but only 3203c42a7b7eSSam Leffler * enable SWBA interrupts if we need to manually 3204c42a7b7eSSam Leffler * prepare beacon frames. Otherwise we use a 3205c42a7b7eSSam Leffler * self-linked tx descriptor and let the hardware 3206c42a7b7eSSam Leffler * deal with things. 3207c42a7b7eSSam Leffler */ 3208c42a7b7eSSam Leffler intval |= HAL_BEACON_ENA; 3209c42a7b7eSSam Leffler if (!sc->sc_hasveol) 3210c42a7b7eSSam Leffler sc->sc_imask |= HAL_INT_SWBA; 321180d939bfSSam Leffler if ((intval & HAL_BEACON_RESET_TSF) == 0) { 321280d939bfSSam Leffler /* 321380d939bfSSam Leffler * Pull nexttbtt forward to reflect 321480d939bfSSam Leffler * the current TSF. 321580d939bfSSam Leffler */ 321680d939bfSSam Leffler tsf = ath_hal_gettsf64(ah); 321780d939bfSSam Leffler tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; 321880d939bfSSam Leffler do { 321980d939bfSSam Leffler nexttbtt += intval; 322080d939bfSSam Leffler } while (nexttbtt < tsftu); 322180d939bfSSam Leffler } 32220f2e86fbSSam Leffler ath_beaconq_config(sc); 322359aa14a9SRui Paulo } else if (ic->ic_opmode == IEEE80211_M_HOSTAP || 322459aa14a9SRui Paulo ic->ic_opmode == IEEE80211_M_MBSS) { 3225c42a7b7eSSam Leffler /* 322659aa14a9SRui Paulo * In AP/mesh mode we enable the beacon timers 322759aa14a9SRui Paulo * and SWBA interrupts to prepare beacon frames. 3228c42a7b7eSSam Leffler */ 3229c42a7b7eSSam Leffler intval |= HAL_BEACON_ENA; 32305591b213SSam Leffler sc->sc_imask |= HAL_INT_SWBA; /* beacon prepare */ 32310f2e86fbSSam Leffler ath_beaconq_config(sc); 3232c42a7b7eSSam Leffler } 3233c42a7b7eSSam Leffler ath_hal_beaconinit(ah, nexttbtt, intval); 3234c42a7b7eSSam Leffler sc->sc_bmisscount = 0; 32355591b213SSam Leffler ath_hal_intrset(ah, sc->sc_imask); 3236c42a7b7eSSam Leffler /* 3237c42a7b7eSSam Leffler * When using a self-linked beacon descriptor in 3238c42a7b7eSSam Leffler * ibss mode load it once here. 3239c42a7b7eSSam Leffler */ 3240c42a7b7eSSam Leffler if (ic->ic_opmode == IEEE80211_M_IBSS && sc->sc_hasveol) 3241b032f27cSSam Leffler ath_beacon_start_adhoc(sc, vap); 32425591b213SSam Leffler } 324380d939bfSSam Leffler sc->sc_syncbeacon = 0; 324480d939bfSSam Leffler #undef FUDGE 32458371372bSSam Leffler #undef TSF_TO_TU 32465591b213SSam Leffler } 32475591b213SSam Leffler 32485591b213SSam Leffler static void 32495591b213SSam Leffler ath_load_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 32505591b213SSam Leffler { 32515591b213SSam Leffler bus_addr_t *paddr = (bus_addr_t*) arg; 3252d77367bfSSam Leffler KASSERT(error == 0, ("error %u on bus_dma callback", error)); 32535591b213SSam Leffler *paddr = segs->ds_addr; 32545591b213SSam Leffler } 32555591b213SSam Leffler 32565591b213SSam Leffler static int 3257c42a7b7eSSam Leffler ath_descdma_setup(struct ath_softc *sc, 3258c42a7b7eSSam Leffler struct ath_descdma *dd, ath_bufhead *head, 3259c42a7b7eSSam Leffler const char *name, int nbuf, int ndesc) 3260c42a7b7eSSam Leffler { 3261c42a7b7eSSam Leffler #define DS2PHYS(_dd, _ds) \ 3262c42a7b7eSSam Leffler ((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc)) 3263fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 3264c42a7b7eSSam Leffler struct ath_desc *ds; 3265c42a7b7eSSam Leffler struct ath_buf *bf; 3266c42a7b7eSSam Leffler int i, bsize, error; 3267c42a7b7eSSam Leffler 3268c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_RESET, "%s: %s DMA: %u buffers %u desc/buf\n", 3269c42a7b7eSSam Leffler __func__, name, nbuf, ndesc); 3270c42a7b7eSSam Leffler 3271c42a7b7eSSam Leffler dd->dd_name = name; 3272c42a7b7eSSam Leffler dd->dd_desc_len = sizeof(struct ath_desc) * nbuf * ndesc; 3273c42a7b7eSSam Leffler 3274c42a7b7eSSam Leffler /* 3275c42a7b7eSSam Leffler * Setup DMA descriptor area. 3276c42a7b7eSSam Leffler */ 3277c2175ff5SMarius Strobl error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), /* parent */ 3278c42a7b7eSSam Leffler PAGE_SIZE, 0, /* alignment, bounds */ 3279c42a7b7eSSam Leffler BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 3280c42a7b7eSSam Leffler BUS_SPACE_MAXADDR, /* highaddr */ 3281c42a7b7eSSam Leffler NULL, NULL, /* filter, filterarg */ 3282c42a7b7eSSam Leffler dd->dd_desc_len, /* maxsize */ 3283c42a7b7eSSam Leffler 1, /* nsegments */ 32846ccb8ea7SSam Leffler dd->dd_desc_len, /* maxsegsize */ 3285c42a7b7eSSam Leffler BUS_DMA_ALLOCNOW, /* flags */ 3286c42a7b7eSSam Leffler NULL, /* lockfunc */ 3287c42a7b7eSSam Leffler NULL, /* lockarg */ 3288c42a7b7eSSam Leffler &dd->dd_dmat); 3289c42a7b7eSSam Leffler if (error != 0) { 3290c42a7b7eSSam Leffler if_printf(ifp, "cannot allocate %s DMA tag\n", dd->dd_name); 3291c42a7b7eSSam Leffler return error; 3292c42a7b7eSSam Leffler } 3293c42a7b7eSSam Leffler 3294c42a7b7eSSam Leffler /* allocate descriptors */ 3295c42a7b7eSSam Leffler error = bus_dmamap_create(dd->dd_dmat, BUS_DMA_NOWAIT, &dd->dd_dmamap); 3296c42a7b7eSSam Leffler if (error != 0) { 3297c42a7b7eSSam Leffler if_printf(ifp, "unable to create dmamap for %s descriptors, " 3298c42a7b7eSSam Leffler "error %u\n", dd->dd_name, error); 3299c42a7b7eSSam Leffler goto fail0; 3300c42a7b7eSSam Leffler } 3301c42a7b7eSSam Leffler 3302c42a7b7eSSam Leffler error = bus_dmamem_alloc(dd->dd_dmat, (void**) &dd->dd_desc, 33030553a01fSSam Leffler BUS_DMA_NOWAIT | BUS_DMA_COHERENT, 33040553a01fSSam Leffler &dd->dd_dmamap); 3305c42a7b7eSSam Leffler if (error != 0) { 3306c42a7b7eSSam Leffler if_printf(ifp, "unable to alloc memory for %u %s descriptors, " 3307c42a7b7eSSam Leffler "error %u\n", nbuf * ndesc, dd->dd_name, error); 3308c42a7b7eSSam Leffler goto fail1; 3309c42a7b7eSSam Leffler } 3310c42a7b7eSSam Leffler 3311c42a7b7eSSam Leffler error = bus_dmamap_load(dd->dd_dmat, dd->dd_dmamap, 3312c42a7b7eSSam Leffler dd->dd_desc, dd->dd_desc_len, 3313c42a7b7eSSam Leffler ath_load_cb, &dd->dd_desc_paddr, 3314c42a7b7eSSam Leffler BUS_DMA_NOWAIT); 3315c42a7b7eSSam Leffler if (error != 0) { 3316c42a7b7eSSam Leffler if_printf(ifp, "unable to map %s descriptors, error %u\n", 3317c42a7b7eSSam Leffler dd->dd_name, error); 3318c42a7b7eSSam Leffler goto fail2; 3319c42a7b7eSSam Leffler } 3320c42a7b7eSSam Leffler 3321c42a7b7eSSam Leffler ds = dd->dd_desc; 3322c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_RESET, "%s: %s DMA map: %p (%lu) -> %p (%lu)\n", 3323c42a7b7eSSam Leffler __func__, dd->dd_name, ds, (u_long) dd->dd_desc_len, 3324c42a7b7eSSam Leffler (caddr_t) dd->dd_desc_paddr, /*XXX*/ (u_long) dd->dd_desc_len); 3325c42a7b7eSSam Leffler 3326ebecf802SSam Leffler /* allocate rx buffers */ 3327c42a7b7eSSam Leffler bsize = sizeof(struct ath_buf) * nbuf; 3328c42a7b7eSSam Leffler bf = malloc(bsize, M_ATHDEV, M_NOWAIT | M_ZERO); 3329c42a7b7eSSam Leffler if (bf == NULL) { 3330c42a7b7eSSam Leffler if_printf(ifp, "malloc of %s buffers failed, size %u\n", 3331c42a7b7eSSam Leffler dd->dd_name, bsize); 3332c42a7b7eSSam Leffler goto fail3; 3333c42a7b7eSSam Leffler } 3334c42a7b7eSSam Leffler dd->dd_bufptr = bf; 3335c42a7b7eSSam Leffler 3336c42a7b7eSSam Leffler STAILQ_INIT(head); 3337c42a7b7eSSam Leffler for (i = 0; i < nbuf; i++, bf++, ds += ndesc) { 3338c42a7b7eSSam Leffler bf->bf_desc = ds; 3339c42a7b7eSSam Leffler bf->bf_daddr = DS2PHYS(dd, ds); 3340c42a7b7eSSam Leffler error = bus_dmamap_create(sc->sc_dmat, BUS_DMA_NOWAIT, 3341c42a7b7eSSam Leffler &bf->bf_dmamap); 3342c42a7b7eSSam Leffler if (error != 0) { 3343c42a7b7eSSam Leffler if_printf(ifp, "unable to create dmamap for %s " 3344c42a7b7eSSam Leffler "buffer %u, error %u\n", dd->dd_name, i, error); 3345c42a7b7eSSam Leffler ath_descdma_cleanup(sc, dd, head); 3346c42a7b7eSSam Leffler return error; 3347c42a7b7eSSam Leffler } 3348c42a7b7eSSam Leffler STAILQ_INSERT_TAIL(head, bf, bf_list); 3349c42a7b7eSSam Leffler } 3350c42a7b7eSSam Leffler return 0; 3351c42a7b7eSSam Leffler fail3: 3352c42a7b7eSSam Leffler bus_dmamap_unload(dd->dd_dmat, dd->dd_dmamap); 3353c42a7b7eSSam Leffler fail2: 3354c42a7b7eSSam Leffler bus_dmamem_free(dd->dd_dmat, dd->dd_desc, dd->dd_dmamap); 3355c42a7b7eSSam Leffler fail1: 3356c42a7b7eSSam Leffler bus_dmamap_destroy(dd->dd_dmat, dd->dd_dmamap); 3357c42a7b7eSSam Leffler fail0: 3358c42a7b7eSSam Leffler bus_dma_tag_destroy(dd->dd_dmat); 3359c42a7b7eSSam Leffler memset(dd, 0, sizeof(*dd)); 3360c42a7b7eSSam Leffler return error; 3361c42a7b7eSSam Leffler #undef DS2PHYS 3362c42a7b7eSSam Leffler } 3363c42a7b7eSSam Leffler 3364c42a7b7eSSam Leffler static void 3365c42a7b7eSSam Leffler ath_descdma_cleanup(struct ath_softc *sc, 3366c42a7b7eSSam Leffler struct ath_descdma *dd, ath_bufhead *head) 3367c42a7b7eSSam Leffler { 3368c42a7b7eSSam Leffler struct ath_buf *bf; 3369c42a7b7eSSam Leffler struct ieee80211_node *ni; 3370c42a7b7eSSam Leffler 3371c42a7b7eSSam Leffler bus_dmamap_unload(dd->dd_dmat, dd->dd_dmamap); 3372c42a7b7eSSam Leffler bus_dmamem_free(dd->dd_dmat, dd->dd_desc, dd->dd_dmamap); 3373c42a7b7eSSam Leffler bus_dmamap_destroy(dd->dd_dmat, dd->dd_dmamap); 3374c42a7b7eSSam Leffler bus_dma_tag_destroy(dd->dd_dmat); 3375c42a7b7eSSam Leffler 3376c42a7b7eSSam Leffler STAILQ_FOREACH(bf, head, bf_list) { 3377c42a7b7eSSam Leffler if (bf->bf_m) { 3378c42a7b7eSSam Leffler m_freem(bf->bf_m); 3379c42a7b7eSSam Leffler bf->bf_m = NULL; 3380c42a7b7eSSam Leffler } 3381c42a7b7eSSam Leffler if (bf->bf_dmamap != NULL) { 3382c42a7b7eSSam Leffler bus_dmamap_destroy(sc->sc_dmat, bf->bf_dmamap); 3383c42a7b7eSSam Leffler bf->bf_dmamap = NULL; 3384c42a7b7eSSam Leffler } 3385c42a7b7eSSam Leffler ni = bf->bf_node; 3386c42a7b7eSSam Leffler bf->bf_node = NULL; 3387c42a7b7eSSam Leffler if (ni != NULL) { 3388c42a7b7eSSam Leffler /* 3389c42a7b7eSSam Leffler * Reclaim node reference. 3390c42a7b7eSSam Leffler */ 3391c42a7b7eSSam Leffler ieee80211_free_node(ni); 3392c42a7b7eSSam Leffler } 3393c42a7b7eSSam Leffler } 3394c42a7b7eSSam Leffler 3395c42a7b7eSSam Leffler STAILQ_INIT(head); 3396c42a7b7eSSam Leffler free(dd->dd_bufptr, M_ATHDEV); 3397c42a7b7eSSam Leffler memset(dd, 0, sizeof(*dd)); 3398c42a7b7eSSam Leffler } 3399c42a7b7eSSam Leffler 3400c42a7b7eSSam Leffler static int 34015591b213SSam Leffler ath_desc_alloc(struct ath_softc *sc) 34025591b213SSam Leffler { 3403c42a7b7eSSam Leffler int error; 34045591b213SSam Leffler 3405c42a7b7eSSam Leffler error = ath_descdma_setup(sc, &sc->sc_rxdma, &sc->sc_rxbuf, 3406e2d787faSSam Leffler "rx", ath_rxbuf, 1); 34075591b213SSam Leffler if (error != 0) 34085591b213SSam Leffler return error; 34095591b213SSam Leffler 3410c42a7b7eSSam Leffler error = ath_descdma_setup(sc, &sc->sc_txdma, &sc->sc_txbuf, 3411e2d787faSSam Leffler "tx", ath_txbuf, ATH_TXDESC); 3412c42a7b7eSSam Leffler if (error != 0) { 3413c42a7b7eSSam Leffler ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf); 34145591b213SSam Leffler return error; 3415c42a7b7eSSam Leffler } 3416c42a7b7eSSam Leffler 3417c42a7b7eSSam Leffler error = ath_descdma_setup(sc, &sc->sc_bdma, &sc->sc_bbuf, 3418b032f27cSSam Leffler "beacon", ATH_BCBUF, 1); 3419c42a7b7eSSam Leffler if (error != 0) { 3420c42a7b7eSSam Leffler ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf); 3421c42a7b7eSSam Leffler ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf); 3422c42a7b7eSSam Leffler return error; 3423c42a7b7eSSam Leffler } 34245591b213SSam Leffler return 0; 34255591b213SSam Leffler } 34265591b213SSam Leffler 34275591b213SSam Leffler static void 34285591b213SSam Leffler ath_desc_free(struct ath_softc *sc) 34295591b213SSam Leffler { 34305591b213SSam Leffler 3431c42a7b7eSSam Leffler if (sc->sc_bdma.dd_desc_len != 0) 3432c42a7b7eSSam Leffler ath_descdma_cleanup(sc, &sc->sc_bdma, &sc->sc_bbuf); 3433c42a7b7eSSam Leffler if (sc->sc_txdma.dd_desc_len != 0) 3434c42a7b7eSSam Leffler ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf); 3435c42a7b7eSSam Leffler if (sc->sc_rxdma.dd_desc_len != 0) 3436c42a7b7eSSam Leffler ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf); 34375591b213SSam Leffler } 34385591b213SSam Leffler 34395591b213SSam Leffler static struct ieee80211_node * 344038c208f8SSam Leffler ath_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) 34415591b213SSam Leffler { 344238c208f8SSam Leffler struct ieee80211com *ic = vap->iv_ic; 3443c42a7b7eSSam Leffler struct ath_softc *sc = ic->ic_ifp->if_softc; 3444c42a7b7eSSam Leffler const size_t space = sizeof(struct ath_node) + sc->sc_rc->arc_space; 3445c42a7b7eSSam Leffler struct ath_node *an; 3446c42a7b7eSSam Leffler 3447c42a7b7eSSam Leffler an = malloc(space, M_80211_NODE, M_NOWAIT|M_ZERO); 3448c42a7b7eSSam Leffler if (an == NULL) { 3449c42a7b7eSSam Leffler /* XXX stat+msg */ 3450de5af704SSam Leffler return NULL; 34515591b213SSam Leffler } 3452c42a7b7eSSam Leffler ath_rate_node_init(sc, an); 34535591b213SSam Leffler 3454c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_NODE, "%s: an %p\n", __func__, an); 3455c42a7b7eSSam Leffler return &an->an_node; 3456c42a7b7eSSam Leffler } 3457c42a7b7eSSam Leffler 34585591b213SSam Leffler static void 3459c42a7b7eSSam Leffler ath_node_free(struct ieee80211_node *ni) 34605591b213SSam Leffler { 3461c42a7b7eSSam Leffler struct ieee80211com *ic = ni->ni_ic; 3462c42a7b7eSSam Leffler struct ath_softc *sc = ic->ic_ifp->if_softc; 34631e774079SSam Leffler 3464c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_NODE, "%s: ni %p\n", __func__, ni); 3465c42a7b7eSSam Leffler 3466c42a7b7eSSam Leffler ath_rate_node_cleanup(sc, ATH_NODE(ni)); 3467c42a7b7eSSam Leffler sc->sc_node_free(ni); 34685591b213SSam Leffler } 34695591b213SSam Leffler 347068e8e04eSSam Leffler static void 347168e8e04eSSam Leffler ath_node_getsignal(const struct ieee80211_node *ni, int8_t *rssi, int8_t *noise) 347268e8e04eSSam Leffler { 347368e8e04eSSam Leffler struct ieee80211com *ic = ni->ni_ic; 347468e8e04eSSam Leffler struct ath_softc *sc = ic->ic_ifp->if_softc; 347568e8e04eSSam Leffler struct ath_hal *ah = sc->sc_ah; 347668e8e04eSSam Leffler 3477b032f27cSSam Leffler *rssi = ic->ic_node_getrssi(ni); 347859efa8b5SSam Leffler if (ni->ni_chan != IEEE80211_CHAN_ANYC) 347959efa8b5SSam Leffler *noise = ath_hal_getchannoise(ah, ni->ni_chan); 348059efa8b5SSam Leffler else 348168e8e04eSSam Leffler *noise = -95; /* nominally correct */ 348268e8e04eSSam Leffler } 348368e8e04eSSam Leffler 34845591b213SSam Leffler static int 34855591b213SSam Leffler ath_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf) 34865591b213SSam Leffler { 34875591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 34885591b213SSam Leffler int error; 34895591b213SSam Leffler struct mbuf *m; 34905591b213SSam Leffler struct ath_desc *ds; 34915591b213SSam Leffler 34925591b213SSam Leffler m = bf->bf_m; 34935591b213SSam Leffler if (m == NULL) { 34945591b213SSam Leffler /* 34955591b213SSam Leffler * NB: by assigning a page to the rx dma buffer we 34965591b213SSam Leffler * implicitly satisfy the Atheros requirement that 34975591b213SSam Leffler * this buffer be cache-line-aligned and sized to be 34985591b213SSam Leffler * multiple of the cache line size. Not doing this 34995591b213SSam Leffler * causes weird stuff to happen (for the 5210 at least). 35005591b213SSam Leffler */ 35015591b213SSam Leffler m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); 35025591b213SSam Leffler if (m == NULL) { 3503c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, 3504c42a7b7eSSam Leffler "%s: no mbuf/cluster\n", __func__); 35055591b213SSam Leffler sc->sc_stats.ast_rx_nombuf++; 35065591b213SSam Leffler return ENOMEM; 35075591b213SSam Leffler } 35085591b213SSam Leffler m->m_pkthdr.len = m->m_len = m->m_ext.ext_size; 35095591b213SSam Leffler 3510f9e6219bSSam Leffler error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, 3511c42a7b7eSSam Leffler bf->bf_dmamap, m, 3512f9e6219bSSam Leffler bf->bf_segs, &bf->bf_nseg, 35135591b213SSam Leffler BUS_DMA_NOWAIT); 35145591b213SSam Leffler if (error != 0) { 3515c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, 3516f9e6219bSSam Leffler "%s: bus_dmamap_load_mbuf_sg failed; error %d\n", 3517c42a7b7eSSam Leffler __func__, error); 35185591b213SSam Leffler sc->sc_stats.ast_rx_busdma++; 3519b2792ff6SSam Leffler m_freem(m); 35205591b213SSam Leffler return error; 35215591b213SSam Leffler } 3522d77367bfSSam Leffler KASSERT(bf->bf_nseg == 1, 3523d77367bfSSam Leffler ("multi-segment packet; nseg %u", bf->bf_nseg)); 3524b2792ff6SSam Leffler bf->bf_m = m; 35255591b213SSam Leffler } 35265591b213SSam Leffler bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREREAD); 35275591b213SSam Leffler 352804e22a02SSam Leffler /* 352904e22a02SSam Leffler * Setup descriptors. For receive we always terminate 353004e22a02SSam Leffler * the descriptor list with a self-linked entry so we'll 353104e22a02SSam Leffler * not get overrun under high load (as can happen with a 3532c42a7b7eSSam Leffler * 5212 when ANI processing enables PHY error frames). 353304e22a02SSam Leffler * 353404e22a02SSam Leffler * To insure the last descriptor is self-linked we create 353504e22a02SSam Leffler * each descriptor as self-linked and add it to the end. As 353604e22a02SSam Leffler * each additional descriptor is added the previous self-linked 353704e22a02SSam Leffler * entry is ``fixed'' naturally. This should be safe even 353804e22a02SSam Leffler * if DMA is happening. When processing RX interrupts we 353904e22a02SSam Leffler * never remove/process the last, self-linked, entry on the 354004e22a02SSam Leffler * descriptor list. This insures the hardware always has 354104e22a02SSam Leffler * someplace to write a new frame. 354204e22a02SSam Leffler */ 35435591b213SSam Leffler ds = bf->bf_desc; 354404e22a02SSam Leffler ds->ds_link = bf->bf_daddr; /* link to self */ 35455591b213SSam Leffler ds->ds_data = bf->bf_segs[0].ds_addr; 35465591b213SSam Leffler ath_hal_setuprxdesc(ah, ds 35475591b213SSam Leffler , m->m_len /* buffer size */ 35485591b213SSam Leffler , 0 35495591b213SSam Leffler ); 35505591b213SSam Leffler 35515591b213SSam Leffler if (sc->sc_rxlink != NULL) 35525591b213SSam Leffler *sc->sc_rxlink = bf->bf_daddr; 35535591b213SSam Leffler sc->sc_rxlink = &ds->ds_link; 35545591b213SSam Leffler return 0; 35555591b213SSam Leffler } 35565591b213SSam Leffler 3557c42a7b7eSSam Leffler /* 355803ed599aSSam Leffler * Extend 15-bit time stamp from rx descriptor to 35597b0c77ecSSam Leffler * a full 64-bit TSF using the specified TSF. 356003ed599aSSam Leffler */ 356103ed599aSSam Leffler static __inline u_int64_t 35627b0c77ecSSam Leffler ath_extend_tsf(u_int32_t rstamp, u_int64_t tsf) 356303ed599aSSam Leffler { 356403ed599aSSam Leffler if ((tsf & 0x7fff) < rstamp) 356503ed599aSSam Leffler tsf -= 0x8000; 356603ed599aSSam Leffler return ((tsf &~ 0x7fff) | rstamp); 356703ed599aSSam Leffler } 356803ed599aSSam Leffler 356903ed599aSSam Leffler /* 3570c42a7b7eSSam Leffler * Intercept management frames to collect beacon rssi data 3571c42a7b7eSSam Leffler * and to do ibss merges. 3572c42a7b7eSSam Leffler */ 3573c42a7b7eSSam Leffler static void 3574b032f27cSSam Leffler ath_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, 35755463c4a4SSam Leffler int subtype, int rssi, int nf) 3576c42a7b7eSSam Leffler { 3577b032f27cSSam Leffler struct ieee80211vap *vap = ni->ni_vap; 3578b032f27cSSam Leffler struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; 3579c42a7b7eSSam Leffler 3580c42a7b7eSSam Leffler /* 3581c42a7b7eSSam Leffler * Call up first so subsequent work can use information 3582c42a7b7eSSam Leffler * potentially stored in the node (e.g. for ibss merge). 3583c42a7b7eSSam Leffler */ 35845463c4a4SSam Leffler ATH_VAP(vap)->av_recv_mgmt(ni, m, subtype, rssi, nf); 3585c42a7b7eSSam Leffler switch (subtype) { 3586c42a7b7eSSam Leffler case IEEE80211_FC0_SUBTYPE_BEACON: 3587c42a7b7eSSam Leffler /* update rssi statistics for use by the hal */ 3588ffa2cab6SSam Leffler ATH_RSSI_LPF(sc->sc_halstats.ns_avgbrssi, rssi); 358980d939bfSSam Leffler if (sc->sc_syncbeacon && 3590b032f27cSSam Leffler ni == vap->iv_bss && vap->iv_state == IEEE80211_S_RUN) { 359180d939bfSSam Leffler /* 359280d939bfSSam Leffler * Resync beacon timers using the tsf of the beacon 359380d939bfSSam Leffler * frame we just received. 359480d939bfSSam Leffler */ 3595b032f27cSSam Leffler ath_beacon_config(sc, vap); 359680d939bfSSam Leffler } 3597c42a7b7eSSam Leffler /* fall thru... */ 3598c42a7b7eSSam Leffler case IEEE80211_FC0_SUBTYPE_PROBE_RESP: 3599b032f27cSSam Leffler if (vap->iv_opmode == IEEE80211_M_IBSS && 3600b032f27cSSam Leffler vap->iv_state == IEEE80211_S_RUN) { 36017041d50cSBernhard Schmidt uint32_t rstamp = sc->sc_lastrs->rs_tstamp; 36027041d50cSBernhard Schmidt uint64_t tsf = ath_extend_tsf(rstamp, 36037b0c77ecSSam Leffler ath_hal_gettsf64(sc->sc_ah)); 3604c42a7b7eSSam Leffler /* 3605c42a7b7eSSam Leffler * Handle ibss merge as needed; check the tsf on the 3606c42a7b7eSSam Leffler * frame before attempting the merge. The 802.11 spec 3607c42a7b7eSSam Leffler * says the station should change it's bssid to match 3608c42a7b7eSSam Leffler * the oldest station with the same ssid, where oldest 3609f818612bSSam Leffler * is determined by the tsf. Note that hardware 3610f818612bSSam Leffler * reconfiguration happens through callback to 361103ed599aSSam Leffler * ath_newstate as the state machine will go from 361203ed599aSSam Leffler * RUN -> RUN when this happens. 3613c42a7b7eSSam Leffler */ 361403ed599aSSam Leffler if (le64toh(ni->ni_tstamp.tsf) >= tsf) { 361503ed599aSSam Leffler DPRINTF(sc, ATH_DEBUG_STATE, 361633d7d80cSTai-hwa Liang "ibss merge, rstamp %u tsf %ju " 361733d7d80cSTai-hwa Liang "tstamp %ju\n", rstamp, (uintmax_t)tsf, 361833d7d80cSTai-hwa Liang (uintmax_t)ni->ni_tstamp.tsf); 3619641b4d0bSSam Leffler (void) ieee80211_ibss_merge(ni); 3620c42a7b7eSSam Leffler } 362103ed599aSSam Leffler } 3622c42a7b7eSSam Leffler break; 3623c42a7b7eSSam Leffler } 3624c42a7b7eSSam Leffler } 3625c42a7b7eSSam Leffler 3626c42a7b7eSSam Leffler /* 3627c42a7b7eSSam Leffler * Set the default antenna. 3628c42a7b7eSSam Leffler */ 3629c42a7b7eSSam Leffler static void 3630c42a7b7eSSam Leffler ath_setdefantenna(struct ath_softc *sc, u_int antenna) 3631c42a7b7eSSam Leffler { 3632c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 3633c42a7b7eSSam Leffler 3634c42a7b7eSSam Leffler /* XXX block beacon interrupts */ 3635c42a7b7eSSam Leffler ath_hal_setdefantenna(ah, antenna); 3636c42a7b7eSSam Leffler if (sc->sc_defant != antenna) 3637c42a7b7eSSam Leffler sc->sc_stats.ast_ant_defswitch++; 3638c42a7b7eSSam Leffler sc->sc_defant = antenna; 3639c42a7b7eSSam Leffler sc->sc_rxotherant = 0; 3640c42a7b7eSSam Leffler } 3641c42a7b7eSSam Leffler 36425463c4a4SSam Leffler static void 3643b032f27cSSam Leffler ath_rx_tap(struct ifnet *ifp, struct mbuf *m, 364465f9edeeSSam Leffler const struct ath_rx_status *rs, u_int64_t tsf, int16_t nf) 36457b0c77ecSSam Leffler { 3646e387d629SSam Leffler #define CHAN_HT20 htole32(IEEE80211_CHAN_HT20) 3647e387d629SSam Leffler #define CHAN_HT40U htole32(IEEE80211_CHAN_HT40U) 3648e387d629SSam Leffler #define CHAN_HT40D htole32(IEEE80211_CHAN_HT40D) 364946d4d74cSSam Leffler #define CHAN_HT (CHAN_HT20|CHAN_HT40U|CHAN_HT40D) 3650b032f27cSSam Leffler struct ath_softc *sc = ifp->if_softc; 365146d4d74cSSam Leffler const HAL_RATE_TABLE *rt; 365246d4d74cSSam Leffler uint8_t rix; 36537b0c77ecSSam Leffler 365446d4d74cSSam Leffler rt = sc->sc_currates; 365546d4d74cSSam Leffler KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode)); 365646d4d74cSSam Leffler rix = rt->rateCodeToIndex[rs->rs_rate]; 365768e8e04eSSam Leffler sc->sc_rx_th.wr_rate = sc->sc_hwmap[rix].ieeerate; 36587b0c77ecSSam Leffler sc->sc_rx_th.wr_flags = sc->sc_hwmap[rix].rxflags; 365946d4d74cSSam Leffler #ifdef AH_SUPPORT_AR5416 3660e387d629SSam Leffler sc->sc_rx_th.wr_chan_flags &= ~CHAN_HT; 366146d4d74cSSam Leffler if (sc->sc_rx_th.wr_rate & IEEE80211_RATE_MCS) { /* HT rate */ 366259efa8b5SSam Leffler struct ieee80211com *ic = ifp->if_l2com; 366359efa8b5SSam Leffler 3664e387d629SSam Leffler if ((rs->rs_flags & HAL_RX_2040) == 0) 3665e387d629SSam Leffler sc->sc_rx_th.wr_chan_flags |= CHAN_HT20; 366659efa8b5SSam Leffler else if (IEEE80211_IS_CHAN_HT40U(ic->ic_curchan)) 3667e387d629SSam Leffler sc->sc_rx_th.wr_chan_flags |= CHAN_HT40U; 3668e387d629SSam Leffler else 3669e387d629SSam Leffler sc->sc_rx_th.wr_chan_flags |= CHAN_HT40D; 367068e8e04eSSam Leffler if ((rs->rs_flags & HAL_RX_GI) == 0) 3671e387d629SSam Leffler sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_SHORTGI; 367268e8e04eSSam Leffler } 367368e8e04eSSam Leffler #endif 367468e8e04eSSam Leffler sc->sc_rx_th.wr_tsf = htole64(ath_extend_tsf(rs->rs_tstamp, tsf)); 367565f9edeeSSam Leffler if (rs->rs_status & HAL_RXERR_CRC) 36767b0c77ecSSam Leffler sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_BADFCS; 36777b0c77ecSSam Leffler /* XXX propagate other error flags from descriptor */ 36787b0c77ecSSam Leffler sc->sc_rx_th.wr_antnoise = nf; 36795463c4a4SSam Leffler sc->sc_rx_th.wr_antsignal = nf + rs->rs_rssi; 368065f9edeeSSam Leffler sc->sc_rx_th.wr_antenna = rs->rs_antenna; 368146d4d74cSSam Leffler #undef CHAN_HT 3682e387d629SSam Leffler #undef CHAN_HT20 3683e387d629SSam Leffler #undef CHAN_HT40U 3684e387d629SSam Leffler #undef CHAN_HT40D 36857b0c77ecSSam Leffler } 36867b0c77ecSSam Leffler 36875591b213SSam Leffler static void 3688b032f27cSSam Leffler ath_handle_micerror(struct ieee80211com *ic, 3689b032f27cSSam Leffler struct ieee80211_frame *wh, int keyix) 3690b032f27cSSam Leffler { 3691b032f27cSSam Leffler struct ieee80211_node *ni; 3692b032f27cSSam Leffler 3693b032f27cSSam Leffler /* XXX recheck MIC to deal w/ chips that lie */ 3694b032f27cSSam Leffler /* XXX discard MIC errors on !data frames */ 3695b032f27cSSam Leffler ni = ieee80211_find_rxnode(ic, (const struct ieee80211_frame_min *) wh); 3696b032f27cSSam Leffler if (ni != NULL) { 3697b032f27cSSam Leffler ieee80211_notify_michael_failure(ni->ni_vap, wh, keyix); 3698b032f27cSSam Leffler ieee80211_free_node(ni); 3699b032f27cSSam Leffler } 3700b032f27cSSam Leffler } 3701b032f27cSSam Leffler 3702b032f27cSSam Leffler static void 37035591b213SSam Leffler ath_rx_proc(void *arg, int npending) 37045591b213SSam Leffler { 37058cec0ab9SSam Leffler #define PA2DESC(_sc, _pa) \ 3706c42a7b7eSSam Leffler ((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \ 3707c42a7b7eSSam Leffler ((_pa) - (_sc)->sc_rxdma.dd_desc_paddr))) 37085591b213SSam Leffler struct ath_softc *sc = arg; 37095591b213SSam Leffler struct ath_buf *bf; 3710fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 3711b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 37125591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 37135591b213SSam Leffler struct ath_desc *ds; 371465f9edeeSSam Leffler struct ath_rx_status *rs; 37155591b213SSam Leffler struct mbuf *m; 37160a915fadSSam Leffler struct ieee80211_node *ni; 3717d7736e13SSam Leffler int len, type, ngood; 37185591b213SSam Leffler u_int phyerr; 37195591b213SSam Leffler HAL_STATUS status; 37207b0c77ecSSam Leffler int16_t nf; 37217b0c77ecSSam Leffler u_int64_t tsf; 37225591b213SSam Leffler 3723c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s: pending %u\n", __func__, npending); 3724d7736e13SSam Leffler ngood = 0; 372559efa8b5SSam Leffler nf = ath_hal_getchannoise(ah, sc->sc_curchan); 372684784be1SSam Leffler sc->sc_stats.ast_rx_noise = nf; 37277b0c77ecSSam Leffler tsf = ath_hal_gettsf64(ah); 37285591b213SSam Leffler do { 3729c42a7b7eSSam Leffler bf = STAILQ_FIRST(&sc->sc_rxbuf); 37305591b213SSam Leffler if (bf == NULL) { /* NB: shouldn't happen */ 3731c42a7b7eSSam Leffler if_printf(ifp, "%s: no buffer!\n", __func__); 37325591b213SSam Leffler break; 37335591b213SSam Leffler } 3734b2792ff6SSam Leffler m = bf->bf_m; 3735b2792ff6SSam Leffler if (m == NULL) { /* NB: shouldn't happen */ 3736b2792ff6SSam Leffler /* 3737b2792ff6SSam Leffler * If mbuf allocation failed previously there 3738b2792ff6SSam Leffler * will be no mbuf; try again to re-populate it. 3739b2792ff6SSam Leffler */ 3740b2792ff6SSam Leffler /* XXX make debug msg */ 3741b2792ff6SSam Leffler if_printf(ifp, "%s: no mbuf!\n", __func__); 3742b2792ff6SSam Leffler STAILQ_REMOVE_HEAD(&sc->sc_rxbuf, bf_list); 3743b2792ff6SSam Leffler goto rx_next; 3744b2792ff6SSam Leffler } 374504e22a02SSam Leffler ds = bf->bf_desc; 374604e22a02SSam Leffler if (ds->ds_link == bf->bf_daddr) { 374704e22a02SSam Leffler /* NB: never process the self-linked entry at the end */ 374804e22a02SSam Leffler break; 374904e22a02SSam Leffler } 37508cec0ab9SSam Leffler /* XXX sync descriptor memory */ 37518cec0ab9SSam Leffler /* 37528cec0ab9SSam Leffler * Must provide the virtual address of the current 37538cec0ab9SSam Leffler * descriptor, the physical address, and the virtual 37548cec0ab9SSam Leffler * address of the next descriptor in the h/w chain. 37558cec0ab9SSam Leffler * This allows the HAL to look ahead to see if the 37568cec0ab9SSam Leffler * hardware is done with a descriptor by checking the 37578cec0ab9SSam Leffler * done bit in the following descriptor and the address 37588cec0ab9SSam Leffler * of the current descriptor the DMA engine is working 37598cec0ab9SSam Leffler * on. All this is necessary because of our use of 37608cec0ab9SSam Leffler * a self-linked list to avoid rx overruns. 37618cec0ab9SSam Leffler */ 376265f9edeeSSam Leffler rs = &bf->bf_status.ds_rxstat; 37638cec0ab9SSam Leffler status = ath_hal_rxprocdesc(ah, ds, 376465f9edeeSSam Leffler bf->bf_daddr, PA2DESC(sc, ds->ds_link), rs); 3765a585a9a1SSam Leffler #ifdef ATH_DEBUG 3766c42a7b7eSSam Leffler if (sc->sc_debug & ATH_DEBUG_RECV_DESC) 37676902009eSSam Leffler ath_printrxbuf(sc, bf, 0, status == HAL_OK); 37685591b213SSam Leffler #endif 37695591b213SSam Leffler if (status == HAL_EINPROGRESS) 37705591b213SSam Leffler break; 3771c42a7b7eSSam Leffler STAILQ_REMOVE_HEAD(&sc->sc_rxbuf, bf_list); 377268e8e04eSSam Leffler if (rs->rs_status != 0) { 377365f9edeeSSam Leffler if (rs->rs_status & HAL_RXERR_CRC) 37745591b213SSam Leffler sc->sc_stats.ast_rx_crcerr++; 377565f9edeeSSam Leffler if (rs->rs_status & HAL_RXERR_FIFO) 37765591b213SSam Leffler sc->sc_stats.ast_rx_fifoerr++; 377765f9edeeSSam Leffler if (rs->rs_status & HAL_RXERR_PHY) { 37785591b213SSam Leffler sc->sc_stats.ast_rx_phyerr++; 377965f9edeeSSam Leffler phyerr = rs->rs_phyerr & 0x1f; 37805591b213SSam Leffler sc->sc_stats.ast_rx_phy[phyerr]++; 378168e8e04eSSam Leffler goto rx_error; /* NB: don't count in ierrors */ 3782c42a7b7eSSam Leffler } 378365f9edeeSSam Leffler if (rs->rs_status & HAL_RXERR_DECRYPT) { 378485643802SSam Leffler /* 3785c42a7b7eSSam Leffler * Decrypt error. If the error occurred 3786c42a7b7eSSam Leffler * because there was no hardware key, then 3787c42a7b7eSSam Leffler * let the frame through so the upper layers 3788c42a7b7eSSam Leffler * can process it. This is necessary for 5210 3789c42a7b7eSSam Leffler * parts which have no way to setup a ``clear'' 3790c42a7b7eSSam Leffler * key cache entry. 3791c42a7b7eSSam Leffler * 3792c42a7b7eSSam Leffler * XXX do key cache faulting 379385643802SSam Leffler */ 379465f9edeeSSam Leffler if (rs->rs_keyix == HAL_RXKEYIX_INVALID) 3795c42a7b7eSSam Leffler goto rx_accept; 3796c42a7b7eSSam Leffler sc->sc_stats.ast_rx_badcrypt++; 37975591b213SSam Leffler } 379865f9edeeSSam Leffler if (rs->rs_status & HAL_RXERR_MIC) { 3799c42a7b7eSSam Leffler sc->sc_stats.ast_rx_badmic++; 3800c42a7b7eSSam Leffler /* 3801c42a7b7eSSam Leffler * Do minimal work required to hand off 38025463c4a4SSam Leffler * the 802.11 header for notification. 3803c42a7b7eSSam Leffler */ 3804c42a7b7eSSam Leffler /* XXX frag's and qos frames */ 380565f9edeeSSam Leffler len = rs->rs_datalen; 3806c42a7b7eSSam Leffler if (len >= sizeof (struct ieee80211_frame)) { 3807c42a7b7eSSam Leffler bus_dmamap_sync(sc->sc_dmat, 3808c42a7b7eSSam Leffler bf->bf_dmamap, 3809c42a7b7eSSam Leffler BUS_DMASYNC_POSTREAD); 3810b032f27cSSam Leffler ath_handle_micerror(ic, 3811c42a7b7eSSam Leffler mtod(m, struct ieee80211_frame *), 38120ab4040aSSam Leffler sc->sc_splitmic ? 3813b032f27cSSam Leffler rs->rs_keyix-32 : rs->rs_keyix); 3814c42a7b7eSSam Leffler } 3815c42a7b7eSSam Leffler } 3816c42a7b7eSSam Leffler ifp->if_ierrors++; 381768e8e04eSSam Leffler rx_error: 381868e8e04eSSam Leffler /* 381968e8e04eSSam Leffler * Cleanup any pending partial frame. 382068e8e04eSSam Leffler */ 382168e8e04eSSam Leffler if (sc->sc_rxpending != NULL) { 382268e8e04eSSam Leffler m_freem(sc->sc_rxpending); 382368e8e04eSSam Leffler sc->sc_rxpending = NULL; 382468e8e04eSSam Leffler } 3825c42a7b7eSSam Leffler /* 38267b0c77ecSSam Leffler * When a tap is present pass error frames 38277b0c77ecSSam Leffler * that have been requested. By default we 38287b0c77ecSSam Leffler * pass decrypt+mic errors but others may be 38297b0c77ecSSam Leffler * interesting (e.g. crc). 3830c42a7b7eSSam Leffler */ 38315463c4a4SSam Leffler if (ieee80211_radiotap_active(ic) && 383265f9edeeSSam Leffler (rs->rs_status & sc->sc_monpass)) { 38337b0c77ecSSam Leffler bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, 38347b0c77ecSSam Leffler BUS_DMASYNC_POSTREAD); 38357b0c77ecSSam Leffler /* NB: bpf needs the mbuf length setup */ 383665f9edeeSSam Leffler len = rs->rs_datalen; 38377b0c77ecSSam Leffler m->m_pkthdr.len = m->m_len = len; 38385463c4a4SSam Leffler ath_rx_tap(ifp, m, rs, tsf, nf); 38395463c4a4SSam Leffler ieee80211_radiotap_rx_all(ic, m); 38407b0c77ecSSam Leffler } 38417b0c77ecSSam Leffler /* XXX pass MIC errors up for s/w reclaculation */ 38425591b213SSam Leffler goto rx_next; 38435591b213SSam Leffler } 3844c42a7b7eSSam Leffler rx_accept: 3845c42a7b7eSSam Leffler /* 3846c42a7b7eSSam Leffler * Sync and unmap the frame. At this point we're 3847c42a7b7eSSam Leffler * committed to passing the mbuf somewhere so clear 3848c66c48cbSSam Leffler * bf_m; this means a new mbuf must be allocated 3849c42a7b7eSSam Leffler * when the rx descriptor is setup again to receive 3850c42a7b7eSSam Leffler * another frame. 3851c42a7b7eSSam Leffler */ 38525591b213SSam Leffler bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, 38535591b213SSam Leffler BUS_DMASYNC_POSTREAD); 38545591b213SSam Leffler bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 38555591b213SSam Leffler bf->bf_m = NULL; 3856c42a7b7eSSam Leffler 385765f9edeeSSam Leffler len = rs->rs_datalen; 385868e8e04eSSam Leffler m->m_len = len; 385968e8e04eSSam Leffler 386068e8e04eSSam Leffler if (rs->rs_more) { 386168e8e04eSSam Leffler /* 386268e8e04eSSam Leffler * Frame spans multiple descriptors; save 386368e8e04eSSam Leffler * it for the next completed descriptor, it 386468e8e04eSSam Leffler * will be used to construct a jumbogram. 386568e8e04eSSam Leffler */ 386668e8e04eSSam Leffler if (sc->sc_rxpending != NULL) { 386768e8e04eSSam Leffler /* NB: max frame size is currently 2 clusters */ 386868e8e04eSSam Leffler sc->sc_stats.ast_rx_toobig++; 386968e8e04eSSam Leffler m_freem(sc->sc_rxpending); 387068e8e04eSSam Leffler } 387168e8e04eSSam Leffler m->m_pkthdr.rcvif = ifp; 387268e8e04eSSam Leffler m->m_pkthdr.len = len; 387368e8e04eSSam Leffler sc->sc_rxpending = m; 387468e8e04eSSam Leffler goto rx_next; 387568e8e04eSSam Leffler } else if (sc->sc_rxpending != NULL) { 387668e8e04eSSam Leffler /* 387768e8e04eSSam Leffler * This is the second part of a jumbogram, 387868e8e04eSSam Leffler * chain it to the first mbuf, adjust the 387968e8e04eSSam Leffler * frame length, and clear the rxpending state. 388068e8e04eSSam Leffler */ 388168e8e04eSSam Leffler sc->sc_rxpending->m_next = m; 388268e8e04eSSam Leffler sc->sc_rxpending->m_pkthdr.len += len; 388368e8e04eSSam Leffler m = sc->sc_rxpending; 388468e8e04eSSam Leffler sc->sc_rxpending = NULL; 388568e8e04eSSam Leffler } else { 388668e8e04eSSam Leffler /* 388768e8e04eSSam Leffler * Normal single-descriptor receive; setup 388868e8e04eSSam Leffler * the rcvif and packet length. 388968e8e04eSSam Leffler */ 389068e8e04eSSam Leffler m->m_pkthdr.rcvif = ifp; 389168e8e04eSSam Leffler m->m_pkthdr.len = len; 389268e8e04eSSam Leffler } 389373454c73SSam Leffler 3894b032f27cSSam Leffler ifp->if_ipackets++; 389565f9edeeSSam Leffler sc->sc_stats.ast_ant_rx[rs->rs_antenna]++; 3896c42a7b7eSSam Leffler 38975463c4a4SSam Leffler /* 38985463c4a4SSam Leffler * Populate the rx status block. When there are bpf 38995463c4a4SSam Leffler * listeners we do the additional work to provide 39005463c4a4SSam Leffler * complete status. Otherwise we fill in only the 39015463c4a4SSam Leffler * material required by ieee80211_input. Note that 39025463c4a4SSam Leffler * noise setting is filled in above. 39035463c4a4SSam Leffler */ 39045463c4a4SSam Leffler if (ieee80211_radiotap_active(ic)) 39055463c4a4SSam Leffler ath_rx_tap(ifp, m, rs, tsf, nf); 39060a915fadSSam Leffler 39075591b213SSam Leffler /* 3908c42a7b7eSSam Leffler * From this point on we assume the frame is at least 3909c42a7b7eSSam Leffler * as large as ieee80211_frame_min; verify that. 39105591b213SSam Leffler */ 3911c42a7b7eSSam Leffler if (len < IEEE80211_MIN_LEN) { 39125463c4a4SSam Leffler if (!ieee80211_radiotap_active(ic)) { 39135463c4a4SSam Leffler DPRINTF(sc, ATH_DEBUG_RECV, 39145463c4a4SSam Leffler "%s: short packet %d\n", __func__, len); 3915c42a7b7eSSam Leffler sc->sc_stats.ast_rx_tooshort++; 39165463c4a4SSam Leffler } else { 39175463c4a4SSam Leffler /* NB: in particular this captures ack's */ 39185463c4a4SSam Leffler ieee80211_radiotap_rx_all(ic, m); 39195463c4a4SSam Leffler } 3920c42a7b7eSSam Leffler m_freem(m); 3921c42a7b7eSSam Leffler goto rx_next; 39225591b213SSam Leffler } 39230a915fadSSam Leffler 3924c42a7b7eSSam Leffler if (IFF_DUMPPKTS(sc, ATH_DEBUG_RECV)) { 392546d4d74cSSam Leffler const HAL_RATE_TABLE *rt = sc->sc_currates; 392646d4d74cSSam Leffler uint8_t rix = rt->rateCodeToIndex[rs->rs_rate]; 392746d4d74cSSam Leffler 392868e8e04eSSam Leffler ieee80211_dump_pkt(ic, mtod(m, caddr_t), len, 392946d4d74cSSam Leffler sc->sc_hwmap[rix].ieeerate, rs->rs_rssi); 3930c42a7b7eSSam Leffler } 3931c42a7b7eSSam Leffler 3932c42a7b7eSSam Leffler m_adj(m, -IEEE80211_CRC_LEN); 3933de5af704SSam Leffler 3934de5af704SSam Leffler /* 3935c42a7b7eSSam Leffler * Locate the node for sender, track state, and then 3936c42a7b7eSSam Leffler * pass the (referenced) node up to the 802.11 layer 3937c42a7b7eSSam Leffler * for its use. 3938c42a7b7eSSam Leffler */ 3939c1225b52SSam Leffler ni = ieee80211_find_rxnode_withkey(ic, 3940c1225b52SSam Leffler mtod(m, const struct ieee80211_frame_min *), 394165f9edeeSSam Leffler rs->rs_keyix == HAL_RXKEYIX_INVALID ? 394265f9edeeSSam Leffler IEEE80211_KEYIX_NONE : rs->rs_keyix); 39437041d50cSBernhard Schmidt sc->sc_lastrs = rs; 3944a83df4d3SAdrian Chadd /* tag AMPDU aggregates for reorder processing */ 394500fc8705SAdrian Chadd #if 0 3946a07e9ddbSAdrian Chadd /* 3947a07e9ddbSAdrian Chadd * Just make sure all frames are tagged for AMPDU reorder checking. 3948a07e9ddbSAdrian Chadd * As there seems to be some situations where single frames aren't 3949a07e9ddbSAdrian Chadd * matching a node but bump the seqno. This needs to be investigated. 3950a07e9ddbSAdrian Chadd */ 3951f673a810SAdrian Chadd m->m_flags |= M_AMPDU; 395200fc8705SAdrian Chadd #endif 3953a07e9ddbSAdrian Chadd 3954a07e9ddbSAdrian Chadd /* Keep statistics on the number of aggregate packets received */ 3955a07e9ddbSAdrian Chadd if (rs->rs_isaggr) 3956a07e9ddbSAdrian Chadd sc->sc_stats.ast_rx_agg++; 3957a07e9ddbSAdrian Chadd 3958a07e9ddbSAdrian Chadd if (ni != NULL) { 3959b032f27cSSam Leffler /* 396000fc8705SAdrian Chadd * Only punt packets for ampdu reorder processing for 11n nodes; 396100fc8705SAdrian Chadd * net80211 enforces that M_AMPDU is only set for 11n nodes. 396200fc8705SAdrian Chadd */ 396300fc8705SAdrian Chadd if (ni->ni_flags & IEEE80211_NODE_HT) 396400fc8705SAdrian Chadd m->m_flags |= M_AMPDU; 396500fc8705SAdrian Chadd 396600fc8705SAdrian Chadd /* 3967b032f27cSSam Leffler * Sending station is known, dispatch directly. 3968b032f27cSSam Leffler */ 39695463c4a4SSam Leffler type = ieee80211_input(ni, m, rs->rs_rssi, nf); 3970b032f27cSSam Leffler ieee80211_free_node(ni); 3971b032f27cSSam Leffler /* 3972b032f27cSSam Leffler * Arrange to update the last rx timestamp only for 3973b032f27cSSam Leffler * frames from our ap when operating in station mode. 3974b032f27cSSam Leffler * This assumes the rx key is always setup when 3975b032f27cSSam Leffler * associated. 3976b032f27cSSam Leffler */ 3977b032f27cSSam Leffler if (ic->ic_opmode == IEEE80211_M_STA && 3978b032f27cSSam Leffler rs->rs_keyix != HAL_RXKEYIX_INVALID) 3979b032f27cSSam Leffler ngood++; 3980b032f27cSSam Leffler } else { 39815463c4a4SSam Leffler type = ieee80211_input_all(ic, m, rs->rs_rssi, nf); 3982b032f27cSSam Leffler } 3983c42a7b7eSSam Leffler /* 3984c42a7b7eSSam Leffler * Track rx rssi and do any rx antenna management. 3985de5af704SSam Leffler */ 398665f9edeeSSam Leffler ATH_RSSI_LPF(sc->sc_halstats.ns_avgrssi, rs->rs_rssi); 3987c42a7b7eSSam Leffler if (sc->sc_diversity) { 3988c42a7b7eSSam Leffler /* 3989c42a7b7eSSam Leffler * When using fast diversity, change the default rx 3990c42a7b7eSSam Leffler * antenna if diversity chooses the other antenna 3 3991c42a7b7eSSam Leffler * times in a row. 3992c42a7b7eSSam Leffler */ 399365f9edeeSSam Leffler if (sc->sc_defant != rs->rs_antenna) { 3994c42a7b7eSSam Leffler if (++sc->sc_rxotherant >= 3) 399565f9edeeSSam Leffler ath_setdefantenna(sc, rs->rs_antenna); 3996c42a7b7eSSam Leffler } else 3997c42a7b7eSSam Leffler sc->sc_rxotherant = 0; 3998c42a7b7eSSam Leffler } 39993e50ec2cSSam Leffler if (sc->sc_softled) { 40003e50ec2cSSam Leffler /* 40013e50ec2cSSam Leffler * Blink for any data frame. Otherwise do a 40023e50ec2cSSam Leffler * heartbeat-style blink when idle. The latter 40033e50ec2cSSam Leffler * is mainly for station mode where we depend on 40043e50ec2cSSam Leffler * periodic beacon frames to trigger the poll event. 40053e50ec2cSSam Leffler */ 400631640eb7SSam Leffler if (type == IEEE80211_FC0_TYPE_DATA) { 400746d4d74cSSam Leffler const HAL_RATE_TABLE *rt = sc->sc_currates; 400846d4d74cSSam Leffler ath_led_event(sc, 400946d4d74cSSam Leffler rt->rateCodeToIndex[rs->rs_rate]); 40103e50ec2cSSam Leffler } else if (ticks - sc->sc_ledevent >= sc->sc_ledidle) 401146d4d74cSSam Leffler ath_led_event(sc, 0); 40123e50ec2cSSam Leffler } 40135591b213SSam Leffler rx_next: 4014c42a7b7eSSam Leffler STAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list); 40155591b213SSam Leffler } while (ath_rxbuf_init(sc, bf) == 0); 40165591b213SSam Leffler 4017c42a7b7eSSam Leffler /* rx signal state monitoring */ 401859efa8b5SSam Leffler ath_hal_rxmonitor(ah, &sc->sc_halstats, sc->sc_curchan); 4019d7736e13SSam Leffler if (ngood) 4020d7736e13SSam Leffler sc->sc_lastrx = tsf; 4021b5f4adb3SSam Leffler 4022339ccfb3SSam Leffler if ((ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) { 4023339ccfb3SSam Leffler #ifdef IEEE80211_SUPPORT_SUPERG 402404f19fd6SSam Leffler ieee80211_ff_age_all(ic, 100); 4025339ccfb3SSam Leffler #endif 4026339ccfb3SSam Leffler if (!IFQ_IS_EMPTY(&ifp->if_snd)) 4027cd196bb2SSam Leffler ath_start(ifp); 4028339ccfb3SSam Leffler } 40298cec0ab9SSam Leffler #undef PA2DESC 40305591b213SSam Leffler } 40315591b213SSam Leffler 4032622b3fd2SSam Leffler static void 4033622b3fd2SSam Leffler ath_txq_init(struct ath_softc *sc, struct ath_txq *txq, int qnum) 4034622b3fd2SSam Leffler { 4035622b3fd2SSam Leffler txq->axq_qnum = qnum; 4036339ccfb3SSam Leffler txq->axq_ac = 0; 4037622b3fd2SSam Leffler txq->axq_depth = 0; 4038622b3fd2SSam Leffler txq->axq_intrcnt = 0; 4039622b3fd2SSam Leffler txq->axq_link = NULL; 4040622b3fd2SSam Leffler STAILQ_INIT(&txq->axq_q); 4041622b3fd2SSam Leffler ATH_TXQ_LOCK_INIT(sc, txq); 4042622b3fd2SSam Leffler } 4043622b3fd2SSam Leffler 40445591b213SSam Leffler /* 4045c42a7b7eSSam Leffler * Setup a h/w transmit queue. 40465591b213SSam Leffler */ 4047c42a7b7eSSam Leffler static struct ath_txq * 4048c42a7b7eSSam Leffler ath_txq_setup(struct ath_softc *sc, int qtype, int subtype) 4049c42a7b7eSSam Leffler { 4050c42a7b7eSSam Leffler #define N(a) (sizeof(a)/sizeof(a[0])) 4051c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 4052c42a7b7eSSam Leffler HAL_TXQ_INFO qi; 4053c42a7b7eSSam Leffler int qnum; 4054c42a7b7eSSam Leffler 4055c42a7b7eSSam Leffler memset(&qi, 0, sizeof(qi)); 4056c42a7b7eSSam Leffler qi.tqi_subtype = subtype; 4057c42a7b7eSSam Leffler qi.tqi_aifs = HAL_TXQ_USEDEFAULT; 4058c42a7b7eSSam Leffler qi.tqi_cwmin = HAL_TXQ_USEDEFAULT; 4059c42a7b7eSSam Leffler qi.tqi_cwmax = HAL_TXQ_USEDEFAULT; 4060c42a7b7eSSam Leffler /* 4061c42a7b7eSSam Leffler * Enable interrupts only for EOL and DESC conditions. 4062c42a7b7eSSam Leffler * We mark tx descriptors to receive a DESC interrupt 4063c42a7b7eSSam Leffler * when a tx queue gets deep; otherwise waiting for the 4064c42a7b7eSSam Leffler * EOL to reap descriptors. Note that this is done to 4065c42a7b7eSSam Leffler * reduce interrupt load and this only defers reaping 4066c42a7b7eSSam Leffler * descriptors, never transmitting frames. Aside from 4067c42a7b7eSSam Leffler * reducing interrupts this also permits more concurrency. 4068c42a7b7eSSam Leffler * The only potential downside is if the tx queue backs 4069c42a7b7eSSam Leffler * up in which case the top half of the kernel may backup 4070c42a7b7eSSam Leffler * due to a lack of tx descriptors. 4071c42a7b7eSSam Leffler */ 4072bd5a9920SSam Leffler qi.tqi_qflags = HAL_TXQ_TXEOLINT_ENABLE | HAL_TXQ_TXDESCINT_ENABLE; 4073c42a7b7eSSam Leffler qnum = ath_hal_setuptxqueue(ah, qtype, &qi); 4074c42a7b7eSSam Leffler if (qnum == -1) { 4075c42a7b7eSSam Leffler /* 4076c42a7b7eSSam Leffler * NB: don't print a message, this happens 4077a614e076SSam Leffler * normally on parts with too few tx queues 4078c42a7b7eSSam Leffler */ 4079c42a7b7eSSam Leffler return NULL; 4080c42a7b7eSSam Leffler } 4081c42a7b7eSSam Leffler if (qnum >= N(sc->sc_txq)) { 40826891c875SPeter Wemm device_printf(sc->sc_dev, 40836891c875SPeter Wemm "hal qnum %u out of range, max %zu!\n", 4084c42a7b7eSSam Leffler qnum, N(sc->sc_txq)); 4085c42a7b7eSSam Leffler ath_hal_releasetxqueue(ah, qnum); 4086c42a7b7eSSam Leffler return NULL; 4087c42a7b7eSSam Leffler } 4088c42a7b7eSSam Leffler if (!ATH_TXQ_SETUP(sc, qnum)) { 4089622b3fd2SSam Leffler ath_txq_init(sc, &sc->sc_txq[qnum], qnum); 4090c42a7b7eSSam Leffler sc->sc_txqsetup |= 1<<qnum; 4091c42a7b7eSSam Leffler } 4092c42a7b7eSSam Leffler return &sc->sc_txq[qnum]; 4093c42a7b7eSSam Leffler #undef N 4094c42a7b7eSSam Leffler } 4095c42a7b7eSSam Leffler 4096c42a7b7eSSam Leffler /* 4097c42a7b7eSSam Leffler * Setup a hardware data transmit queue for the specified 4098c42a7b7eSSam Leffler * access control. The hal may not support all requested 4099c42a7b7eSSam Leffler * queues in which case it will return a reference to a 4100c42a7b7eSSam Leffler * previously setup queue. We record the mapping from ac's 4101c42a7b7eSSam Leffler * to h/w queues for use by ath_tx_start and also track 4102c42a7b7eSSam Leffler * the set of h/w queues being used to optimize work in the 4103c42a7b7eSSam Leffler * transmit interrupt handler and related routines. 4104c42a7b7eSSam Leffler */ 4105c42a7b7eSSam Leffler static int 4106c42a7b7eSSam Leffler ath_tx_setup(struct ath_softc *sc, int ac, int haltype) 4107c42a7b7eSSam Leffler { 4108c42a7b7eSSam Leffler #define N(a) (sizeof(a)/sizeof(a[0])) 4109c42a7b7eSSam Leffler struct ath_txq *txq; 4110c42a7b7eSSam Leffler 4111c42a7b7eSSam Leffler if (ac >= N(sc->sc_ac2q)) { 41126891c875SPeter Wemm device_printf(sc->sc_dev, "AC %u out of range, max %zu!\n", 4113c42a7b7eSSam Leffler ac, N(sc->sc_ac2q)); 4114c42a7b7eSSam Leffler return 0; 4115c42a7b7eSSam Leffler } 4116c42a7b7eSSam Leffler txq = ath_txq_setup(sc, HAL_TX_QUEUE_DATA, haltype); 4117c42a7b7eSSam Leffler if (txq != NULL) { 4118339ccfb3SSam Leffler txq->axq_ac = ac; 4119c42a7b7eSSam Leffler sc->sc_ac2q[ac] = txq; 4120c42a7b7eSSam Leffler return 1; 4121c42a7b7eSSam Leffler } else 4122c42a7b7eSSam Leffler return 0; 4123c42a7b7eSSam Leffler #undef N 4124c42a7b7eSSam Leffler } 4125c42a7b7eSSam Leffler 4126c42a7b7eSSam Leffler /* 4127c42a7b7eSSam Leffler * Update WME parameters for a transmit queue. 4128c42a7b7eSSam Leffler */ 4129c42a7b7eSSam Leffler static int 4130c42a7b7eSSam Leffler ath_txq_update(struct ath_softc *sc, int ac) 4131c42a7b7eSSam Leffler { 4132c42a7b7eSSam Leffler #define ATH_EXPONENT_TO_VALUE(v) ((1<<v)-1) 4133c42a7b7eSSam Leffler #define ATH_TXOP_TO_US(v) (v<<5) 4134b032f27cSSam Leffler struct ifnet *ifp = sc->sc_ifp; 4135b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 4136c42a7b7eSSam Leffler struct ath_txq *txq = sc->sc_ac2q[ac]; 4137c42a7b7eSSam Leffler struct wmeParams *wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[ac]; 4138c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 4139c42a7b7eSSam Leffler HAL_TXQ_INFO qi; 4140c42a7b7eSSam Leffler 4141c42a7b7eSSam Leffler ath_hal_gettxqueueprops(ah, txq->axq_qnum, &qi); 4142584f7327SSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 414310ad9a77SSam Leffler if (sc->sc_tdma) { 414410ad9a77SSam Leffler /* 414510ad9a77SSam Leffler * AIFS is zero so there's no pre-transmit wait. The 414610ad9a77SSam Leffler * burst time defines the slot duration and is configured 414709be6601SSam Leffler * through net80211. The QCU is setup to not do post-xmit 414810ad9a77SSam Leffler * back off, lockout all lower-priority QCU's, and fire 414910ad9a77SSam Leffler * off the DMA beacon alert timer which is setup based 415010ad9a77SSam Leffler * on the slot configuration. 415110ad9a77SSam Leffler */ 415210ad9a77SSam Leffler qi.tqi_qflags = HAL_TXQ_TXOKINT_ENABLE 415310ad9a77SSam Leffler | HAL_TXQ_TXERRINT_ENABLE 415410ad9a77SSam Leffler | HAL_TXQ_TXURNINT_ENABLE 415510ad9a77SSam Leffler | HAL_TXQ_TXEOLINT_ENABLE 415610ad9a77SSam Leffler | HAL_TXQ_DBA_GATED 415710ad9a77SSam Leffler | HAL_TXQ_BACKOFF_DISABLE 415810ad9a77SSam Leffler | HAL_TXQ_ARB_LOCKOUT_GLOBAL 415910ad9a77SSam Leffler ; 416010ad9a77SSam Leffler qi.tqi_aifs = 0; 416110ad9a77SSam Leffler /* XXX +dbaprep? */ 416210ad9a77SSam Leffler qi.tqi_readyTime = sc->sc_tdmaslotlen; 416310ad9a77SSam Leffler qi.tqi_burstTime = qi.tqi_readyTime; 416410ad9a77SSam Leffler } else { 416510ad9a77SSam Leffler #endif 416610ad9a77SSam Leffler qi.tqi_qflags = HAL_TXQ_TXOKINT_ENABLE 416710ad9a77SSam Leffler | HAL_TXQ_TXERRINT_ENABLE 416810ad9a77SSam Leffler | HAL_TXQ_TXDESCINT_ENABLE 416910ad9a77SSam Leffler | HAL_TXQ_TXURNINT_ENABLE 417010ad9a77SSam Leffler ; 4171c42a7b7eSSam Leffler qi.tqi_aifs = wmep->wmep_aifsn; 4172c42a7b7eSSam Leffler qi.tqi_cwmin = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmin); 4173c42a7b7eSSam Leffler qi.tqi_cwmax = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmax); 417410ad9a77SSam Leffler qi.tqi_readyTime = 0; 4175c42a7b7eSSam Leffler qi.tqi_burstTime = ATH_TXOP_TO_US(wmep->wmep_txopLimit); 4176584f7327SSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 417710ad9a77SSam Leffler } 417810ad9a77SSam Leffler #endif 417910ad9a77SSam Leffler 418010ad9a77SSam Leffler DPRINTF(sc, ATH_DEBUG_RESET, 418110ad9a77SSam Leffler "%s: Q%u qflags 0x%x aifs %u cwmin %u cwmax %u burstTime %u\n", 418210ad9a77SSam Leffler __func__, txq->axq_qnum, qi.tqi_qflags, 418310ad9a77SSam Leffler qi.tqi_aifs, qi.tqi_cwmin, qi.tqi_cwmax, qi.tqi_burstTime); 4184c42a7b7eSSam Leffler 4185c42a7b7eSSam Leffler if (!ath_hal_settxqueueprops(ah, txq->axq_qnum, &qi)) { 4186b032f27cSSam Leffler if_printf(ifp, "unable to update hardware queue " 4187c42a7b7eSSam Leffler "parameters for %s traffic!\n", 4188c42a7b7eSSam Leffler ieee80211_wme_acnames[ac]); 4189c42a7b7eSSam Leffler return 0; 4190c42a7b7eSSam Leffler } else { 4191c42a7b7eSSam Leffler ath_hal_resettxqueue(ah, txq->axq_qnum); /* push to h/w */ 4192c42a7b7eSSam Leffler return 1; 4193c42a7b7eSSam Leffler } 4194c42a7b7eSSam Leffler #undef ATH_TXOP_TO_US 4195c42a7b7eSSam Leffler #undef ATH_EXPONENT_TO_VALUE 4196c42a7b7eSSam Leffler } 4197c42a7b7eSSam Leffler 4198c42a7b7eSSam Leffler /* 4199c42a7b7eSSam Leffler * Callback from the 802.11 layer to update WME parameters. 4200c42a7b7eSSam Leffler */ 4201c42a7b7eSSam Leffler static int 4202c42a7b7eSSam Leffler ath_wme_update(struct ieee80211com *ic) 4203c42a7b7eSSam Leffler { 4204c42a7b7eSSam Leffler struct ath_softc *sc = ic->ic_ifp->if_softc; 4205c42a7b7eSSam Leffler 4206c42a7b7eSSam Leffler return !ath_txq_update(sc, WME_AC_BE) || 4207c42a7b7eSSam Leffler !ath_txq_update(sc, WME_AC_BK) || 4208c42a7b7eSSam Leffler !ath_txq_update(sc, WME_AC_VI) || 4209c42a7b7eSSam Leffler !ath_txq_update(sc, WME_AC_VO) ? EIO : 0; 4210c42a7b7eSSam Leffler } 4211c42a7b7eSSam Leffler 4212c42a7b7eSSam Leffler /* 4213c42a7b7eSSam Leffler * Reclaim resources for a setup queue. 4214c42a7b7eSSam Leffler */ 4215c42a7b7eSSam Leffler static void 4216c42a7b7eSSam Leffler ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq) 4217c42a7b7eSSam Leffler { 4218c42a7b7eSSam Leffler 4219c42a7b7eSSam Leffler ath_hal_releasetxqueue(sc->sc_ah, txq->axq_qnum); 4220c42a7b7eSSam Leffler ATH_TXQ_LOCK_DESTROY(txq); 4221c42a7b7eSSam Leffler sc->sc_txqsetup &= ~(1<<txq->axq_qnum); 4222c42a7b7eSSam Leffler } 4223c42a7b7eSSam Leffler 4224c42a7b7eSSam Leffler /* 4225c42a7b7eSSam Leffler * Reclaim all tx queue resources. 4226c42a7b7eSSam Leffler */ 4227c42a7b7eSSam Leffler static void 4228c42a7b7eSSam Leffler ath_tx_cleanup(struct ath_softc *sc) 4229c42a7b7eSSam Leffler { 4230c42a7b7eSSam Leffler int i; 4231c42a7b7eSSam Leffler 4232c42a7b7eSSam Leffler ATH_TXBUF_LOCK_DESTROY(sc); 4233c42a7b7eSSam Leffler for (i = 0; i < HAL_NUM_TX_QUEUES; i++) 4234c42a7b7eSSam Leffler if (ATH_TXQ_SETUP(sc, i)) 4235c42a7b7eSSam Leffler ath_tx_cleanupq(sc, &sc->sc_txq[i]); 4236c42a7b7eSSam Leffler } 42375591b213SSam Leffler 423899d258fdSSam Leffler /* 4239ab06fdf2SSam Leffler * Return h/w rate index for an IEEE rate (w/o basic rate bit) 4240ab06fdf2SSam Leffler * using the current rates in sc_rixmap. 42418b5341deSSam Leffler */ 4242b8e788a5SAdrian Chadd int 4243ab06fdf2SSam Leffler ath_tx_findrix(const struct ath_softc *sc, uint8_t rate) 42448b5341deSSam Leffler { 4245ab06fdf2SSam Leffler int rix = sc->sc_rixmap[rate]; 4246ab06fdf2SSam Leffler /* NB: return lowest rix for invalid rate */ 4247ab06fdf2SSam Leffler return (rix == 0xff ? 0 : rix); 42488b5341deSSam Leffler } 42498b5341deSSam Leffler 425068e8e04eSSam Leffler /* 4251c42a7b7eSSam Leffler * Process completed xmit descriptors from the specified queue. 4252c42a7b7eSSam Leffler */ 4253d7736e13SSam Leffler static int 4254c42a7b7eSSam Leffler ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) 42555591b213SSam Leffler { 42565591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 4257b032f27cSSam Leffler struct ifnet *ifp = sc->sc_ifp; 4258b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 425910ad9a77SSam Leffler struct ath_buf *bf, *last; 4260c4c3cb46SSam Leffler struct ath_desc *ds, *ds0; 426165f9edeeSSam Leffler struct ath_tx_status *ts; 42625591b213SSam Leffler struct ieee80211_node *ni; 42635591b213SSam Leffler struct ath_node *an; 4264d7736e13SSam Leffler int sr, lr, pri, nacked; 42655591b213SSam Leffler HAL_STATUS status; 42665591b213SSam Leffler 4267c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: tx queue %u head %p link %p\n", 4268c42a7b7eSSam Leffler __func__, txq->axq_qnum, 4269c42a7b7eSSam Leffler (caddr_t)(uintptr_t) ath_hal_gettxbuf(sc->sc_ah, txq->axq_qnum), 4270c42a7b7eSSam Leffler txq->axq_link); 4271d7736e13SSam Leffler nacked = 0; 42725591b213SSam Leffler for (;;) { 4273c42a7b7eSSam Leffler ATH_TXQ_LOCK(txq); 4274c42a7b7eSSam Leffler txq->axq_intrcnt = 0; /* reset periodic desc intr count */ 4275c42a7b7eSSam Leffler bf = STAILQ_FIRST(&txq->axq_q); 42765591b213SSam Leffler if (bf == NULL) { 4277c42a7b7eSSam Leffler ATH_TXQ_UNLOCK(txq); 42785591b213SSam Leffler break; 42795591b213SSam Leffler } 4280c4c3cb46SSam Leffler ds0 = &bf->bf_desc[0]; 42815591b213SSam Leffler ds = &bf->bf_desc[bf->bf_nseg - 1]; 428265f9edeeSSam Leffler ts = &bf->bf_status.ds_txstat; 428365f9edeeSSam Leffler status = ath_hal_txprocdesc(ah, ds, ts); 4284a585a9a1SSam Leffler #ifdef ATH_DEBUG 4285c42a7b7eSSam Leffler if (sc->sc_debug & ATH_DEBUG_XMIT_DESC) 42866902009eSSam Leffler ath_printtxbuf(sc, bf, txq->axq_qnum, 0, 42876902009eSSam Leffler status == HAL_OK); 42885591b213SSam Leffler #endif 42895591b213SSam Leffler if (status == HAL_EINPROGRESS) { 4290c42a7b7eSSam Leffler ATH_TXQ_UNLOCK(txq); 42915591b213SSam Leffler break; 42925591b213SSam Leffler } 4293c42a7b7eSSam Leffler ATH_TXQ_REMOVE_HEAD(txq, bf_list); 4294584f7327SSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 429510ad9a77SSam Leffler if (txq->axq_depth > 0) { 429610ad9a77SSam Leffler /* 429710ad9a77SSam Leffler * More frames follow. Mark the buffer busy 429810ad9a77SSam Leffler * so it's not re-used while the hardware may 429910ad9a77SSam Leffler * still re-read the link field in the descriptor. 430010ad9a77SSam Leffler */ 430110ad9a77SSam Leffler bf->bf_flags |= ATH_BUF_BUSY; 430210ad9a77SSam Leffler } else 430310ad9a77SSam Leffler #else 4304ebecf802SSam Leffler if (txq->axq_depth == 0) 430510ad9a77SSam Leffler #endif 43061539af1eSSam Leffler txq->axq_link = NULL; 4307c42a7b7eSSam Leffler ATH_TXQ_UNLOCK(txq); 43085591b213SSam Leffler 43095591b213SSam Leffler ni = bf->bf_node; 43105591b213SSam Leffler if (ni != NULL) { 4311c42a7b7eSSam Leffler an = ATH_NODE(ni); 431265f9edeeSSam Leffler if (ts->ts_status == 0) { 431365f9edeeSSam Leffler u_int8_t txant = ts->ts_antenna; 4314c42a7b7eSSam Leffler sc->sc_stats.ast_ant_tx[txant]++; 4315c42a7b7eSSam Leffler sc->sc_ant_tx[txant]++; 4316f6cbf16aSSam Leffler if (ts->ts_finaltsi != 0) 4317c42a7b7eSSam Leffler sc->sc_stats.ast_tx_altrate++; 4318c42a7b7eSSam Leffler pri = M_WME_GETAC(bf->bf_m); 4319c42a7b7eSSam Leffler if (pri >= WME_AC_VO) 4320c42a7b7eSSam Leffler ic->ic_wme.wme_hipri_traffic++; 4321ad80c0aaSSam Leffler if ((bf->bf_txflags & HAL_TXDESC_NOACK) == 0) 4322c42a7b7eSSam Leffler ni->ni_inact = ni->ni_inact_reload; 43235591b213SSam Leffler } else { 432465f9edeeSSam Leffler if (ts->ts_status & HAL_TXERR_XRETRY) 43255591b213SSam Leffler sc->sc_stats.ast_tx_xretries++; 432665f9edeeSSam Leffler if (ts->ts_status & HAL_TXERR_FIFO) 43275591b213SSam Leffler sc->sc_stats.ast_tx_fifoerr++; 432865f9edeeSSam Leffler if (ts->ts_status & HAL_TXERR_FILT) 43295591b213SSam Leffler sc->sc_stats.ast_tx_filtered++; 433068e8e04eSSam Leffler if (bf->bf_m->m_flags & M_FF) 433168e8e04eSSam Leffler sc->sc_stats.ast_ff_txerr++; 43325591b213SSam Leffler } 433365f9edeeSSam Leffler sr = ts->ts_shortretry; 433465f9edeeSSam Leffler lr = ts->ts_longretry; 43355591b213SSam Leffler sc->sc_stats.ast_tx_shortretry += sr; 43365591b213SSam Leffler sc->sc_stats.ast_tx_longretry += lr; 4337c42a7b7eSSam Leffler /* 4338c42a7b7eSSam Leffler * Hand the descriptor to the rate control algorithm. 4339c42a7b7eSSam Leffler */ 434065f9edeeSSam Leffler if ((ts->ts_status & HAL_TXERR_FILT) == 0 && 434180c07f23SSam Leffler (bf->bf_txflags & HAL_TXDESC_NOACK) == 0) { 4342d7736e13SSam Leffler /* 434384784be1SSam Leffler * If frame was ack'd update statistics, 434484784be1SSam Leffler * including the last rx time used to 434584784be1SSam Leffler * workaround phantom bmiss interrupts. 4346d7736e13SSam Leffler */ 434784784be1SSam Leffler if (ts->ts_status == 0) { 4348d7736e13SSam Leffler nacked++; 434984784be1SSam Leffler sc->sc_stats.ast_tx_rssi = ts->ts_rssi; 435084784be1SSam Leffler ATH_RSSI_LPF(sc->sc_halstats.ns_avgtxrssi, 435184784be1SSam Leffler ts->ts_rssi); 435284784be1SSam Leffler } 435365f9edeeSSam Leffler ath_rate_tx_complete(sc, an, bf); 4354d7736e13SSam Leffler } 43550a915fadSSam Leffler /* 435668e8e04eSSam Leffler * Do any tx complete callback. Note this must 435768e8e04eSSam Leffler * be done before releasing the node reference. 435868e8e04eSSam Leffler */ 435968e8e04eSSam Leffler if (bf->bf_m->m_flags & M_TXCB) 436068e8e04eSSam Leffler ieee80211_process_callback(ni, bf->bf_m, 436174eca0c2SSam Leffler (bf->bf_txflags & HAL_TXDESC_NOACK) == 0 ? 436274eca0c2SSam Leffler ts->ts_status : HAL_TXERR_XRETRY); 4363c42a7b7eSSam Leffler ieee80211_free_node(ni); 43645591b213SSam Leffler } 43655591b213SSam Leffler bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, 43665591b213SSam Leffler BUS_DMASYNC_POSTWRITE); 43675591b213SSam Leffler bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 436868e8e04eSSam Leffler 43695591b213SSam Leffler m_freem(bf->bf_m); 43705591b213SSam Leffler bf->bf_m = NULL; 43715591b213SSam Leffler bf->bf_node = NULL; 43725591b213SSam Leffler 4373f0b2a0beSSam Leffler ATH_TXBUF_LOCK(sc); 437410ad9a77SSam Leffler last = STAILQ_LAST(&sc->sc_txbuf, ath_buf, bf_list); 437510ad9a77SSam Leffler if (last != NULL) 437610ad9a77SSam Leffler last->bf_flags &= ~ATH_BUF_BUSY; 4377c42a7b7eSSam Leffler STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); 4378f0b2a0beSSam Leffler ATH_TXBUF_UNLOCK(sc); 43795591b213SSam Leffler } 4380339ccfb3SSam Leffler #ifdef IEEE80211_SUPPORT_SUPERG 438168e8e04eSSam Leffler /* 438268e8e04eSSam Leffler * Flush fast-frame staging queue when traffic slows. 438368e8e04eSSam Leffler */ 438468e8e04eSSam Leffler if (txq->axq_depth <= 1) 438504f19fd6SSam Leffler ieee80211_ff_flush(ic, txq->axq_ac); 4386339ccfb3SSam Leffler #endif 4387d7736e13SSam Leffler return nacked; 4388d7736e13SSam Leffler } 4389d7736e13SSam Leffler 4390d7736e13SSam Leffler static __inline int 4391d7736e13SSam Leffler txqactive(struct ath_hal *ah, int qnum) 4392d7736e13SSam Leffler { 4393e2815d69SSam Leffler u_int32_t txqs = 1<<qnum; 4394e2815d69SSam Leffler ath_hal_gettxintrtxqs(ah, &txqs); 43959760f8aeSSam Leffler return (txqs & (1<<qnum)); 4396c42a7b7eSSam Leffler } 4397c42a7b7eSSam Leffler 4398c42a7b7eSSam Leffler /* 4399c42a7b7eSSam Leffler * Deferred processing of transmit interrupt; special-cased 4400c42a7b7eSSam Leffler * for a single hardware transmit queue (e.g. 5210 and 5211). 4401c42a7b7eSSam Leffler */ 4402c42a7b7eSSam Leffler static void 4403c42a7b7eSSam Leffler ath_tx_proc_q0(void *arg, int npending) 4404c42a7b7eSSam Leffler { 4405c42a7b7eSSam Leffler struct ath_softc *sc = arg; 4406fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 4407c42a7b7eSSam Leffler 4408d7736e13SSam Leffler if (txqactive(sc->sc_ah, 0) && ath_tx_processq(sc, &sc->sc_txq[0])) 4409d7736e13SSam Leffler sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah); 4410d7736e13SSam Leffler if (txqactive(sc->sc_ah, sc->sc_cabq->axq_qnum)) 4411d7736e13SSam Leffler ath_tx_processq(sc, sc->sc_cabq); 441213f4c340SRobert Watson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 44132e986da5SSam Leffler sc->sc_wd_timer = 0; 44145591b213SSam Leffler 44153e50ec2cSSam Leffler if (sc->sc_softled) 441646d4d74cSSam Leffler ath_led_event(sc, sc->sc_txrix); 44173e50ec2cSSam Leffler 44185591b213SSam Leffler ath_start(ifp); 44195591b213SSam Leffler } 44205591b213SSam Leffler 44215591b213SSam Leffler /* 4422c42a7b7eSSam Leffler * Deferred processing of transmit interrupt; special-cased 4423c42a7b7eSSam Leffler * for four hardware queues, 0-3 (e.g. 5212 w/ WME support). 44245591b213SSam Leffler */ 44255591b213SSam Leffler static void 4426c42a7b7eSSam Leffler ath_tx_proc_q0123(void *arg, int npending) 4427c42a7b7eSSam Leffler { 4428c42a7b7eSSam Leffler struct ath_softc *sc = arg; 4429fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 4430d7736e13SSam Leffler int nacked; 4431c42a7b7eSSam Leffler 4432c42a7b7eSSam Leffler /* 4433c42a7b7eSSam Leffler * Process each active queue. 4434c42a7b7eSSam Leffler */ 4435d7736e13SSam Leffler nacked = 0; 4436d7736e13SSam Leffler if (txqactive(sc->sc_ah, 0)) 4437d7736e13SSam Leffler nacked += ath_tx_processq(sc, &sc->sc_txq[0]); 4438d7736e13SSam Leffler if (txqactive(sc->sc_ah, 1)) 4439d7736e13SSam Leffler nacked += ath_tx_processq(sc, &sc->sc_txq[1]); 4440d7736e13SSam Leffler if (txqactive(sc->sc_ah, 2)) 4441d7736e13SSam Leffler nacked += ath_tx_processq(sc, &sc->sc_txq[2]); 4442d7736e13SSam Leffler if (txqactive(sc->sc_ah, 3)) 4443d7736e13SSam Leffler nacked += ath_tx_processq(sc, &sc->sc_txq[3]); 4444d7736e13SSam Leffler if (txqactive(sc->sc_ah, sc->sc_cabq->axq_qnum)) 4445c42a7b7eSSam Leffler ath_tx_processq(sc, sc->sc_cabq); 4446d7736e13SSam Leffler if (nacked) 4447d7736e13SSam Leffler sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah); 4448c42a7b7eSSam Leffler 444913f4c340SRobert Watson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 44502e986da5SSam Leffler sc->sc_wd_timer = 0; 4451c42a7b7eSSam Leffler 44523e50ec2cSSam Leffler if (sc->sc_softled) 445346d4d74cSSam Leffler ath_led_event(sc, sc->sc_txrix); 44543e50ec2cSSam Leffler 4455c42a7b7eSSam Leffler ath_start(ifp); 4456c42a7b7eSSam Leffler } 4457c42a7b7eSSam Leffler 4458c42a7b7eSSam Leffler /* 4459c42a7b7eSSam Leffler * Deferred processing of transmit interrupt. 4460c42a7b7eSSam Leffler */ 4461c42a7b7eSSam Leffler static void 4462c42a7b7eSSam Leffler ath_tx_proc(void *arg, int npending) 4463c42a7b7eSSam Leffler { 4464c42a7b7eSSam Leffler struct ath_softc *sc = arg; 4465fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 4466d7736e13SSam Leffler int i, nacked; 4467c42a7b7eSSam Leffler 4468c42a7b7eSSam Leffler /* 4469c42a7b7eSSam Leffler * Process each active queue. 4470c42a7b7eSSam Leffler */ 4471d7736e13SSam Leffler nacked = 0; 4472c42a7b7eSSam Leffler for (i = 0; i < HAL_NUM_TX_QUEUES; i++) 4473d7736e13SSam Leffler if (ATH_TXQ_SETUP(sc, i) && txqactive(sc->sc_ah, i)) 4474d7736e13SSam Leffler nacked += ath_tx_processq(sc, &sc->sc_txq[i]); 4475d7736e13SSam Leffler if (nacked) 4476d7736e13SSam Leffler sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah); 4477c42a7b7eSSam Leffler 447813f4c340SRobert Watson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 44792e986da5SSam Leffler sc->sc_wd_timer = 0; 4480c42a7b7eSSam Leffler 44813e50ec2cSSam Leffler if (sc->sc_softled) 448246d4d74cSSam Leffler ath_led_event(sc, sc->sc_txrix); 44833e50ec2cSSam Leffler 4484c42a7b7eSSam Leffler ath_start(ifp); 4485c42a7b7eSSam Leffler } 4486c42a7b7eSSam Leffler 4487c42a7b7eSSam Leffler static void 4488c42a7b7eSSam Leffler ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq) 44895591b213SSam Leffler { 4490a585a9a1SSam Leffler #ifdef ATH_DEBUG 44915591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 4492d2f6ed15SSam Leffler #endif 449323428eafSSam Leffler struct ieee80211_node *ni; 44945591b213SSam Leffler struct ath_buf *bf; 44957a4c5ed9SSam Leffler u_int ix; 44965591b213SSam Leffler 4497c42a7b7eSSam Leffler /* 4498c42a7b7eSSam Leffler * NB: this assumes output has been stopped and 44995d61b5e8SSam Leffler * we do not need to block ath_tx_proc 4500c42a7b7eSSam Leffler */ 450110ad9a77SSam Leffler ATH_TXBUF_LOCK(sc); 450210ad9a77SSam Leffler bf = STAILQ_LAST(&sc->sc_txbuf, ath_buf, bf_list); 450310ad9a77SSam Leffler if (bf != NULL) 450410ad9a77SSam Leffler bf->bf_flags &= ~ATH_BUF_BUSY; 450510ad9a77SSam Leffler ATH_TXBUF_UNLOCK(sc); 45067a4c5ed9SSam Leffler for (ix = 0;; ix++) { 4507c42a7b7eSSam Leffler ATH_TXQ_LOCK(txq); 4508c42a7b7eSSam Leffler bf = STAILQ_FIRST(&txq->axq_q); 45095591b213SSam Leffler if (bf == NULL) { 4510ebecf802SSam Leffler txq->axq_link = NULL; 4511c42a7b7eSSam Leffler ATH_TXQ_UNLOCK(txq); 45125591b213SSam Leffler break; 45135591b213SSam Leffler } 4514c42a7b7eSSam Leffler ATH_TXQ_REMOVE_HEAD(txq, bf_list); 4515c42a7b7eSSam Leffler ATH_TXQ_UNLOCK(txq); 4516a585a9a1SSam Leffler #ifdef ATH_DEBUG 45174a3ac3fcSSam Leffler if (sc->sc_debug & ATH_DEBUG_RESET) { 4518b032f27cSSam Leffler struct ieee80211com *ic = sc->sc_ifp->if_l2com; 4519b032f27cSSam Leffler 45206902009eSSam Leffler ath_printtxbuf(sc, bf, txq->axq_qnum, ix, 452165f9edeeSSam Leffler ath_hal_txprocdesc(ah, bf->bf_desc, 452265f9edeeSSam Leffler &bf->bf_status.ds_txstat) == HAL_OK); 4523e40b6ab1SSam Leffler ieee80211_dump_pkt(ic, mtod(bf->bf_m, const uint8_t *), 45244a3ac3fcSSam Leffler bf->bf_m->m_len, 0, -1); 45254a3ac3fcSSam Leffler } 4526a585a9a1SSam Leffler #endif /* ATH_DEBUG */ 45275591b213SSam Leffler bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 452823428eafSSam Leffler ni = bf->bf_node; 45295591b213SSam Leffler bf->bf_node = NULL; 4530c42a7b7eSSam Leffler if (ni != NULL) { 453123428eafSSam Leffler /* 4532d50ea6acSSam Leffler * Do any callback and reclaim the node reference. 453323428eafSSam Leffler */ 4534d50ea6acSSam Leffler if (bf->bf_m->m_flags & M_TXCB) 4535d50ea6acSSam Leffler ieee80211_process_callback(ni, bf->bf_m, -1); 4536c42a7b7eSSam Leffler ieee80211_free_node(ni); 453723428eafSSam Leffler } 453868e8e04eSSam Leffler m_freem(bf->bf_m); 453968e8e04eSSam Leffler bf->bf_m = NULL; 454010ad9a77SSam Leffler bf->bf_flags &= ~ATH_BUF_BUSY; 454168e8e04eSSam Leffler 4542f0b2a0beSSam Leffler ATH_TXBUF_LOCK(sc); 4543c42a7b7eSSam Leffler STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); 4544f0b2a0beSSam Leffler ATH_TXBUF_UNLOCK(sc); 45455591b213SSam Leffler } 4546c42a7b7eSSam Leffler } 4547c42a7b7eSSam Leffler 4548c42a7b7eSSam Leffler static void 4549c42a7b7eSSam Leffler ath_tx_stopdma(struct ath_softc *sc, struct ath_txq *txq) 4550c42a7b7eSSam Leffler { 4551c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 4552c42a7b7eSSam Leffler 4553c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_RESET, "%s: tx queue [%u] %p, link %p\n", 4554c42a7b7eSSam Leffler __func__, txq->axq_qnum, 45556891c875SPeter Wemm (caddr_t)(uintptr_t) ath_hal_gettxbuf(ah, txq->axq_qnum), 45566891c875SPeter Wemm txq->axq_link); 45574a3ac3fcSSam Leffler (void) ath_hal_stoptxdma(ah, txq->axq_qnum); 4558c42a7b7eSSam Leffler } 4559c42a7b7eSSam Leffler 4560c42a7b7eSSam Leffler /* 4561c42a7b7eSSam Leffler * Drain the transmit queues and reclaim resources. 4562c42a7b7eSSam Leffler */ 4563c42a7b7eSSam Leffler static void 4564c42a7b7eSSam Leffler ath_draintxq(struct ath_softc *sc) 4565c42a7b7eSSam Leffler { 4566c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 4567fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 4568c42a7b7eSSam Leffler int i; 4569c42a7b7eSSam Leffler 4570c42a7b7eSSam Leffler /* XXX return value */ 4571c42a7b7eSSam Leffler if (!sc->sc_invalid) { 4572c42a7b7eSSam Leffler /* don't touch the hardware if marked invalid */ 45734a3ac3fcSSam Leffler DPRINTF(sc, ATH_DEBUG_RESET, "%s: tx queue [%u] %p, link %p\n", 45744a3ac3fcSSam Leffler __func__, sc->sc_bhalq, 45754a3ac3fcSSam Leffler (caddr_t)(uintptr_t) ath_hal_gettxbuf(ah, sc->sc_bhalq), 45764a3ac3fcSSam Leffler NULL); 4577c42a7b7eSSam Leffler (void) ath_hal_stoptxdma(ah, sc->sc_bhalq); 4578c42a7b7eSSam Leffler for (i = 0; i < HAL_NUM_TX_QUEUES; i++) 4579c42a7b7eSSam Leffler if (ATH_TXQ_SETUP(sc, i)) 4580c42a7b7eSSam Leffler ath_tx_stopdma(sc, &sc->sc_txq[i]); 4581c42a7b7eSSam Leffler } 4582c42a7b7eSSam Leffler for (i = 0; i < HAL_NUM_TX_QUEUES; i++) 4583c42a7b7eSSam Leffler if (ATH_TXQ_SETUP(sc, i)) 4584c42a7b7eSSam Leffler ath_tx_draintxq(sc, &sc->sc_txq[i]); 45854a3ac3fcSSam Leffler #ifdef ATH_DEBUG 45864a3ac3fcSSam Leffler if (sc->sc_debug & ATH_DEBUG_RESET) { 45874a3ac3fcSSam Leffler struct ath_buf *bf = STAILQ_FIRST(&sc->sc_bbuf); 45884a3ac3fcSSam Leffler if (bf != NULL && bf->bf_m != NULL) { 45896902009eSSam Leffler ath_printtxbuf(sc, bf, sc->sc_bhalq, 0, 459065f9edeeSSam Leffler ath_hal_txprocdesc(ah, bf->bf_desc, 459165f9edeeSSam Leffler &bf->bf_status.ds_txstat) == HAL_OK); 4592e40b6ab1SSam Leffler ieee80211_dump_pkt(ifp->if_l2com, 4593e40b6ab1SSam Leffler mtod(bf->bf_m, const uint8_t *), bf->bf_m->m_len, 4594e40b6ab1SSam Leffler 0, -1); 45954a3ac3fcSSam Leffler } 45964a3ac3fcSSam Leffler } 45974a3ac3fcSSam Leffler #endif /* ATH_DEBUG */ 459813f4c340SRobert Watson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 45992e986da5SSam Leffler sc->sc_wd_timer = 0; 46005591b213SSam Leffler } 46015591b213SSam Leffler 46025591b213SSam Leffler /* 46035591b213SSam Leffler * Disable the receive h/w in preparation for a reset. 46045591b213SSam Leffler */ 46055591b213SSam Leffler static void 46065591b213SSam Leffler ath_stoprecv(struct ath_softc *sc) 46075591b213SSam Leffler { 46088cec0ab9SSam Leffler #define PA2DESC(_sc, _pa) \ 4609c42a7b7eSSam Leffler ((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \ 4610c42a7b7eSSam Leffler ((_pa) - (_sc)->sc_rxdma.dd_desc_paddr))) 46115591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 46125591b213SSam Leffler 46135591b213SSam Leffler ath_hal_stoppcurecv(ah); /* disable PCU */ 46145591b213SSam Leffler ath_hal_setrxfilter(ah, 0); /* clear recv filter */ 46155591b213SSam Leffler ath_hal_stopdmarecv(ah); /* disable DMA engine */ 4616c42a7b7eSSam Leffler DELAY(3000); /* 3ms is long enough for 1 frame */ 4617a585a9a1SSam Leffler #ifdef ATH_DEBUG 4618c42a7b7eSSam Leffler if (sc->sc_debug & (ATH_DEBUG_RESET | ATH_DEBUG_FATAL)) { 46195591b213SSam Leffler struct ath_buf *bf; 46207a4c5ed9SSam Leffler u_int ix; 46215591b213SSam Leffler 4622e325e530SSam Leffler printf("%s: rx queue %p, link %p\n", __func__, 462330310634SPeter Wemm (caddr_t)(uintptr_t) ath_hal_getrxbuf(ah), sc->sc_rxlink); 46247a4c5ed9SSam Leffler ix = 0; 4625c42a7b7eSSam Leffler STAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) { 46268cec0ab9SSam Leffler struct ath_desc *ds = bf->bf_desc; 462765f9edeeSSam Leffler struct ath_rx_status *rs = &bf->bf_status.ds_rxstat; 4628c42a7b7eSSam Leffler HAL_STATUS status = ath_hal_rxprocdesc(ah, ds, 462965f9edeeSSam Leffler bf->bf_daddr, PA2DESC(sc, ds->ds_link), rs); 4630c42a7b7eSSam Leffler if (status == HAL_OK || (sc->sc_debug & ATH_DEBUG_FATAL)) 46316902009eSSam Leffler ath_printrxbuf(sc, bf, ix, status == HAL_OK); 46327a4c5ed9SSam Leffler ix++; 46335591b213SSam Leffler } 46345591b213SSam Leffler } 46355591b213SSam Leffler #endif 463668e8e04eSSam Leffler if (sc->sc_rxpending != NULL) { 463768e8e04eSSam Leffler m_freem(sc->sc_rxpending); 463868e8e04eSSam Leffler sc->sc_rxpending = NULL; 463968e8e04eSSam Leffler } 46405591b213SSam Leffler sc->sc_rxlink = NULL; /* just in case */ 46418cec0ab9SSam Leffler #undef PA2DESC 46425591b213SSam Leffler } 46435591b213SSam Leffler 46445591b213SSam Leffler /* 46455591b213SSam Leffler * Enable the receive h/w following a reset. 46465591b213SSam Leffler */ 46475591b213SSam Leffler static int 46485591b213SSam Leffler ath_startrecv(struct ath_softc *sc) 46495591b213SSam Leffler { 46505591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 46515591b213SSam Leffler struct ath_buf *bf; 46525591b213SSam Leffler 46535591b213SSam Leffler sc->sc_rxlink = NULL; 465468e8e04eSSam Leffler sc->sc_rxpending = NULL; 4655c42a7b7eSSam Leffler STAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) { 46565591b213SSam Leffler int error = ath_rxbuf_init(sc, bf); 46575591b213SSam Leffler if (error != 0) { 4658c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_RECV, 4659c42a7b7eSSam Leffler "%s: ath_rxbuf_init failed %d\n", 4660c42a7b7eSSam Leffler __func__, error); 46615591b213SSam Leffler return error; 46625591b213SSam Leffler } 46635591b213SSam Leffler } 46645591b213SSam Leffler 4665c42a7b7eSSam Leffler bf = STAILQ_FIRST(&sc->sc_rxbuf); 46665591b213SSam Leffler ath_hal_putrxbuf(ah, bf->bf_daddr); 46675591b213SSam Leffler ath_hal_rxena(ah); /* enable recv descriptors */ 46685591b213SSam Leffler ath_mode_init(sc); /* set filters, etc. */ 46695591b213SSam Leffler ath_hal_startpcurecv(ah); /* re-enable PCU/DMA engine */ 46705591b213SSam Leffler return 0; 46715591b213SSam Leffler } 46725591b213SSam Leffler 46735591b213SSam Leffler /* 4674c42a7b7eSSam Leffler * Update internal state after a channel change. 4675c42a7b7eSSam Leffler */ 4676c42a7b7eSSam Leffler static void 4677c42a7b7eSSam Leffler ath_chan_change(struct ath_softc *sc, struct ieee80211_channel *chan) 4678c42a7b7eSSam Leffler { 4679c42a7b7eSSam Leffler enum ieee80211_phymode mode; 4680c42a7b7eSSam Leffler 4681c42a7b7eSSam Leffler /* 4682c42a7b7eSSam Leffler * Change channels and update the h/w rate map 4683c42a7b7eSSam Leffler * if we're switching; e.g. 11a to 11b/g. 4684c42a7b7eSSam Leffler */ 468568e8e04eSSam Leffler mode = ieee80211_chan2mode(chan); 4686c42a7b7eSSam Leffler if (mode != sc->sc_curmode) 4687c42a7b7eSSam Leffler ath_setcurmode(sc, mode); 468859efa8b5SSam Leffler sc->sc_curchan = chan; 4689c42a7b7eSSam Leffler } 4690c42a7b7eSSam Leffler 4691c42a7b7eSSam Leffler /* 46925591b213SSam Leffler * Set/change channels. If the channel is really being changed, 46934fa8d4efSDaniel Eischen * it's done by resetting the chip. To accomplish this we must 46945591b213SSam Leffler * first cleanup any pending DMA, then restart stuff after a la 46955591b213SSam Leffler * ath_init. 46965591b213SSam Leffler */ 46975591b213SSam Leffler static int 46985591b213SSam Leffler ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan) 46995591b213SSam Leffler { 4700b032f27cSSam Leffler struct ifnet *ifp = sc->sc_ifp; 4701b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 47025591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 4703c42a7b7eSSam Leffler 470459efa8b5SSam Leffler DPRINTF(sc, ATH_DEBUG_RESET, "%s: %u (%u MHz, flags 0x%x)\n", 470559efa8b5SSam Leffler __func__, ieee80211_chan2ieee(ic, chan), 470659efa8b5SSam Leffler chan->ic_freq, chan->ic_flags); 470759efa8b5SSam Leffler if (chan != sc->sc_curchan) { 4708c42a7b7eSSam Leffler HAL_STATUS status; 47095591b213SSam Leffler /* 47105591b213SSam Leffler * To switch channels clear any pending DMA operations; 47115591b213SSam Leffler * wait long enough for the RX fifo to drain, reset the 47125591b213SSam Leffler * hardware at the new frequency, and then re-enable 47135591b213SSam Leffler * the relevant bits of the h/w. 47145591b213SSam Leffler */ 47155591b213SSam Leffler ath_hal_intrset(ah, 0); /* disable interrupts */ 47165591b213SSam Leffler ath_draintxq(sc); /* clear pending tx frames */ 47175591b213SSam Leffler ath_stoprecv(sc); /* turn off frame recv */ 471859efa8b5SSam Leffler if (!ath_hal_reset(ah, sc->sc_opmode, chan, AH_TRUE, &status)) { 4719b032f27cSSam Leffler if_printf(ifp, "%s: unable to reset " 472079649302SGavin Atkinson "channel %u (%u MHz, flags 0x%x), hal status %u\n", 472159efa8b5SSam Leffler __func__, ieee80211_chan2ieee(ic, chan), 472259efa8b5SSam Leffler chan->ic_freq, chan->ic_flags, status); 47235591b213SSam Leffler return EIO; 47245591b213SSam Leffler } 4725c59005e9SSam Leffler sc->sc_diversity = ath_hal_getdiversity(ah); 4726c42a7b7eSSam Leffler 47275591b213SSam Leffler /* 47285591b213SSam Leffler * Re-enable rx framework. 47295591b213SSam Leffler */ 47305591b213SSam Leffler if (ath_startrecv(sc) != 0) { 4731b032f27cSSam Leffler if_printf(ifp, "%s: unable to restart recv logic\n", 4732b032f27cSSam Leffler __func__); 47335591b213SSam Leffler return EIO; 47345591b213SSam Leffler } 47355591b213SSam Leffler 47365591b213SSam Leffler /* 47375591b213SSam Leffler * Change channels and update the h/w rate map 47385591b213SSam Leffler * if we're switching; e.g. 11a to 11b/g. 47395591b213SSam Leffler */ 4740c42a7b7eSSam Leffler ath_chan_change(sc, chan); 47410a915fadSSam Leffler 47420a915fadSSam Leffler /* 47430a915fadSSam Leffler * Re-enable interrupts. 47440a915fadSSam Leffler */ 47450a915fadSSam Leffler ath_hal_intrset(ah, sc->sc_imask); 47465591b213SSam Leffler } 47475591b213SSam Leffler return 0; 47485591b213SSam Leffler } 47495591b213SSam Leffler 47505591b213SSam Leffler /* 47515591b213SSam Leffler * Periodically recalibrate the PHY to account 47525591b213SSam Leffler * for temperature/environment changes. 47535591b213SSam Leffler */ 47545591b213SSam Leffler static void 47555591b213SSam Leffler ath_calibrate(void *arg) 47565591b213SSam Leffler { 47575591b213SSam Leffler struct ath_softc *sc = arg; 47585591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 47592dc7fcc4SSam Leffler struct ifnet *ifp = sc->sc_ifp; 47608d91de92SSam Leffler struct ieee80211com *ic = ifp->if_l2com; 47612dc7fcc4SSam Leffler HAL_BOOL longCal, isCalDone; 4762a108ab63SAdrian Chadd HAL_BOOL aniCal, shortCal = AH_FALSE; 47632dc7fcc4SSam Leffler int nextcal; 47645591b213SSam Leffler 47658d91de92SSam Leffler if (ic->ic_flags & IEEE80211_F_SCAN) /* defer, off channel */ 47668d91de92SSam Leffler goto restart; 47672dc7fcc4SSam Leffler longCal = (ticks - sc->sc_lastlongcal >= ath_longcalinterval*hz); 4768a108ab63SAdrian Chadd aniCal = (ticks - sc->sc_lastani >= ath_anicalinterval*hz/1000); 4769a108ab63SAdrian Chadd if (sc->sc_doresetcal) 4770a108ab63SAdrian Chadd shortCal = (ticks - sc->sc_lastshortcal >= ath_shortcalinterval*hz/1000); 4771a108ab63SAdrian Chadd 4772a108ab63SAdrian Chadd DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: shortCal=%d; longCal=%d; aniCal=%d\n", __func__, shortCal, longCal, aniCal); 4773a108ab63SAdrian Chadd if (aniCal) { 4774a108ab63SAdrian Chadd sc->sc_stats.ast_ani_cal++; 4775a108ab63SAdrian Chadd sc->sc_lastani = ticks; 4776a108ab63SAdrian Chadd ath_hal_ani_poll(ah, sc->sc_curchan); 4777a108ab63SAdrian Chadd } 4778a108ab63SAdrian Chadd 47792dc7fcc4SSam Leffler if (longCal) { 47805591b213SSam Leffler sc->sc_stats.ast_per_cal++; 47818197f57eSAdrian Chadd sc->sc_lastlongcal = ticks; 47825591b213SSam Leffler if (ath_hal_getrfgain(ah) == HAL_RFGAIN_NEED_CHANGE) { 47835591b213SSam Leffler /* 47845591b213SSam Leffler * Rfgain is out of bounds, reset the chip 47855591b213SSam Leffler * to load new gain values. 47865591b213SSam Leffler */ 4787370572d9SSam Leffler DPRINTF(sc, ATH_DEBUG_CALIBRATE, 4788370572d9SSam Leffler "%s: rfgain change\n", __func__); 47895591b213SSam Leffler sc->sc_stats.ast_per_rfgain++; 47902dc7fcc4SSam Leffler ath_reset(ifp); 47915591b213SSam Leffler } 47922dc7fcc4SSam Leffler /* 47932dc7fcc4SSam Leffler * If this long cal is after an idle period, then 47942dc7fcc4SSam Leffler * reset the data collection state so we start fresh. 47952dc7fcc4SSam Leffler */ 47962dc7fcc4SSam Leffler if (sc->sc_resetcal) { 479759efa8b5SSam Leffler (void) ath_hal_calreset(ah, sc->sc_curchan); 47982dc7fcc4SSam Leffler sc->sc_lastcalreset = ticks; 4799a108ab63SAdrian Chadd sc->sc_lastshortcal = ticks; 48002dc7fcc4SSam Leffler sc->sc_resetcal = 0; 4801a108ab63SAdrian Chadd sc->sc_doresetcal = AH_TRUE; 48022dc7fcc4SSam Leffler } 48032dc7fcc4SSam Leffler } 4804a108ab63SAdrian Chadd 4805a108ab63SAdrian Chadd /* Only call if we're doing a short/long cal, not for ANI calibration */ 4806a108ab63SAdrian Chadd if (shortCal || longCal) { 480759efa8b5SSam Leffler if (ath_hal_calibrateN(ah, sc->sc_curchan, longCal, &isCalDone)) { 48082dc7fcc4SSam Leffler if (longCal) { 48092dc7fcc4SSam Leffler /* 48102dc7fcc4SSam Leffler * Calibrate noise floor data again in case of change. 48112dc7fcc4SSam Leffler */ 48122dc7fcc4SSam Leffler ath_hal_process_noisefloor(ah); 48132dc7fcc4SSam Leffler } 48142dc7fcc4SSam Leffler } else { 4815c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, 4816c42a7b7eSSam Leffler "%s: calibration of channel %u failed\n", 481759efa8b5SSam Leffler __func__, sc->sc_curchan->ic_freq); 48185591b213SSam Leffler sc->sc_stats.ast_per_calfail++; 48195591b213SSam Leffler } 4820a108ab63SAdrian Chadd if (shortCal) 4821a108ab63SAdrian Chadd sc->sc_lastshortcal = ticks; 4822a108ab63SAdrian Chadd } 48232dc7fcc4SSam Leffler if (!isCalDone) { 48248d91de92SSam Leffler restart: 48257b0c77ecSSam Leffler /* 48262dc7fcc4SSam Leffler * Use a shorter interval to potentially collect multiple 48272dc7fcc4SSam Leffler * data samples required to complete calibration. Once 48282dc7fcc4SSam Leffler * we're told the work is done we drop back to a longer 48292dc7fcc4SSam Leffler * interval between requests. We're more aggressive doing 48302dc7fcc4SSam Leffler * work when operating as an AP to improve operation right 48312dc7fcc4SSam Leffler * after startup. 48327b0c77ecSSam Leffler */ 4833a108ab63SAdrian Chadd sc->sc_lastshortcal = ticks; 4834a108ab63SAdrian Chadd nextcal = ath_shortcalinterval*hz/1000; 48352dc7fcc4SSam Leffler if (sc->sc_opmode != HAL_M_HOSTAP) 48362dc7fcc4SSam Leffler nextcal *= 10; 4837a108ab63SAdrian Chadd sc->sc_doresetcal = AH_TRUE; 48382dc7fcc4SSam Leffler } else { 4839a108ab63SAdrian Chadd /* nextcal should be the shortest time for next event */ 48402dc7fcc4SSam Leffler nextcal = ath_longcalinterval*hz; 48412dc7fcc4SSam Leffler if (sc->sc_lastcalreset == 0) 48422dc7fcc4SSam Leffler sc->sc_lastcalreset = sc->sc_lastlongcal; 48432dc7fcc4SSam Leffler else if (ticks - sc->sc_lastcalreset >= ath_resetcalinterval*hz) 48442dc7fcc4SSam Leffler sc->sc_resetcal = 1; /* setup reset next trip */ 4845a108ab63SAdrian Chadd sc->sc_doresetcal = AH_FALSE; 4846bd5a9920SSam Leffler } 4847a108ab63SAdrian Chadd /* ANI calibration may occur more often than short/long/resetcal */ 4848a108ab63SAdrian Chadd if (ath_anicalinterval > 0) 4849a108ab63SAdrian Chadd nextcal = MIN(nextcal, ath_anicalinterval*hz/1000); 4850bd5a9920SSam Leffler 48512dc7fcc4SSam Leffler if (nextcal != 0) { 48522dc7fcc4SSam Leffler DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: next +%u (%sisCalDone)\n", 48532dc7fcc4SSam Leffler __func__, nextcal, isCalDone ? "" : "!"); 48542dc7fcc4SSam Leffler callout_reset(&sc->sc_cal_ch, nextcal, ath_calibrate, sc); 48552dc7fcc4SSam Leffler } else { 48562dc7fcc4SSam Leffler DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: calibration disabled\n", 48572dc7fcc4SSam Leffler __func__); 48582dc7fcc4SSam Leffler /* NB: don't rearm timer */ 48592dc7fcc4SSam Leffler } 48605591b213SSam Leffler } 48615591b213SSam Leffler 486268e8e04eSSam Leffler static void 486368e8e04eSSam Leffler ath_scan_start(struct ieee80211com *ic) 486468e8e04eSSam Leffler { 486568e8e04eSSam Leffler struct ifnet *ifp = ic->ic_ifp; 486668e8e04eSSam Leffler struct ath_softc *sc = ifp->if_softc; 486768e8e04eSSam Leffler struct ath_hal *ah = sc->sc_ah; 486868e8e04eSSam Leffler u_int32_t rfilt; 486968e8e04eSSam Leffler 487068e8e04eSSam Leffler /* XXX calibration timer? */ 487168e8e04eSSam Leffler 487268e8e04eSSam Leffler sc->sc_scanning = 1; 487368e8e04eSSam Leffler sc->sc_syncbeacon = 0; 487468e8e04eSSam Leffler rfilt = ath_calcrxfilter(sc); 487568e8e04eSSam Leffler ath_hal_setrxfilter(ah, rfilt); 487668e8e04eSSam Leffler ath_hal_setassocid(ah, ifp->if_broadcastaddr, 0); 487768e8e04eSSam Leffler 487868e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s aid 0\n", 487968e8e04eSSam Leffler __func__, rfilt, ether_sprintf(ifp->if_broadcastaddr)); 488068e8e04eSSam Leffler } 488168e8e04eSSam Leffler 488268e8e04eSSam Leffler static void 488368e8e04eSSam Leffler ath_scan_end(struct ieee80211com *ic) 488468e8e04eSSam Leffler { 488568e8e04eSSam Leffler struct ifnet *ifp = ic->ic_ifp; 488668e8e04eSSam Leffler struct ath_softc *sc = ifp->if_softc; 488768e8e04eSSam Leffler struct ath_hal *ah = sc->sc_ah; 488868e8e04eSSam Leffler u_int32_t rfilt; 488968e8e04eSSam Leffler 489068e8e04eSSam Leffler sc->sc_scanning = 0; 489168e8e04eSSam Leffler rfilt = ath_calcrxfilter(sc); 489268e8e04eSSam Leffler ath_hal_setrxfilter(ah, rfilt); 489368e8e04eSSam Leffler ath_hal_setassocid(ah, sc->sc_curbssid, sc->sc_curaid); 489468e8e04eSSam Leffler 489568e8e04eSSam Leffler ath_hal_process_noisefloor(ah); 489668e8e04eSSam Leffler 489768e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s aid 0x%x\n", 489868e8e04eSSam Leffler __func__, rfilt, ether_sprintf(sc->sc_curbssid), 489968e8e04eSSam Leffler sc->sc_curaid); 490068e8e04eSSam Leffler } 490168e8e04eSSam Leffler 490268e8e04eSSam Leffler static void 490368e8e04eSSam Leffler ath_set_channel(struct ieee80211com *ic) 490468e8e04eSSam Leffler { 490568e8e04eSSam Leffler struct ifnet *ifp = ic->ic_ifp; 490668e8e04eSSam Leffler struct ath_softc *sc = ifp->if_softc; 490768e8e04eSSam Leffler 490868e8e04eSSam Leffler (void) ath_chan_set(sc, ic->ic_curchan); 490968e8e04eSSam Leffler /* 491068e8e04eSSam Leffler * If we are returning to our bss channel then mark state 491168e8e04eSSam Leffler * so the next recv'd beacon's tsf will be used to sync the 491268e8e04eSSam Leffler * beacon timers. Note that since we only hear beacons in 491368e8e04eSSam Leffler * sta/ibss mode this has no effect in other operating modes. 491468e8e04eSSam Leffler */ 491568e8e04eSSam Leffler if (!sc->sc_scanning && ic->ic_curchan == ic->ic_bsschan) 491668e8e04eSSam Leffler sc->sc_syncbeacon = 1; 491768e8e04eSSam Leffler } 491868e8e04eSSam Leffler 4919b032f27cSSam Leffler /* 4920b032f27cSSam Leffler * Walk the vap list and check if there any vap's in RUN state. 4921b032f27cSSam Leffler */ 49225591b213SSam Leffler static int 4923b032f27cSSam Leffler ath_isanyrunningvaps(struct ieee80211vap *this) 49245591b213SSam Leffler { 4925b032f27cSSam Leffler struct ieee80211com *ic = this->iv_ic; 4926b032f27cSSam Leffler struct ieee80211vap *vap; 4927b032f27cSSam Leffler 4928b032f27cSSam Leffler IEEE80211_LOCK_ASSERT(ic); 4929b032f27cSSam Leffler 4930b032f27cSSam Leffler TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 4931309a3e45SSam Leffler if (vap != this && vap->iv_state >= IEEE80211_S_RUN) 4932b032f27cSSam Leffler return 1; 4933b032f27cSSam Leffler } 4934b032f27cSSam Leffler return 0; 4935b032f27cSSam Leffler } 4936b032f27cSSam Leffler 4937b032f27cSSam Leffler static int 4938b032f27cSSam Leffler ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 4939b032f27cSSam Leffler { 4940b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 4941b032f27cSSam Leffler struct ath_softc *sc = ic->ic_ifp->if_softc; 4942b032f27cSSam Leffler struct ath_vap *avp = ATH_VAP(vap); 494345bbf62fSSam Leffler struct ath_hal *ah = sc->sc_ah; 4944b032f27cSSam Leffler struct ieee80211_node *ni = NULL; 494568e8e04eSSam Leffler int i, error, stamode; 49465591b213SSam Leffler u_int32_t rfilt; 49475591b213SSam Leffler static const HAL_LED_STATE leds[] = { 49485591b213SSam Leffler HAL_LED_INIT, /* IEEE80211_S_INIT */ 49495591b213SSam Leffler HAL_LED_SCAN, /* IEEE80211_S_SCAN */ 49505591b213SSam Leffler HAL_LED_AUTH, /* IEEE80211_S_AUTH */ 49515591b213SSam Leffler HAL_LED_ASSOC, /* IEEE80211_S_ASSOC */ 495277d5e068SSam Leffler HAL_LED_RUN, /* IEEE80211_S_CAC */ 49535591b213SSam Leffler HAL_LED_RUN, /* IEEE80211_S_RUN */ 495477d5e068SSam Leffler HAL_LED_RUN, /* IEEE80211_S_CSA */ 495577d5e068SSam Leffler HAL_LED_RUN, /* IEEE80211_S_SLEEP */ 49565591b213SSam Leffler }; 49575591b213SSam Leffler 4958c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_STATE, "%s: %s -> %s\n", __func__, 4959b032f27cSSam Leffler ieee80211_state_name[vap->iv_state], 4960c42a7b7eSSam Leffler ieee80211_state_name[nstate]); 49615591b213SSam Leffler 49622e986da5SSam Leffler callout_drain(&sc->sc_cal_ch); 49635591b213SSam Leffler ath_hal_setledstate(ah, leds[nstate]); /* set LED */ 49645591b213SSam Leffler 4965b032f27cSSam Leffler if (nstate == IEEE80211_S_SCAN) { 496658769f58SSam Leffler /* 4967b032f27cSSam Leffler * Scanning: turn off beacon miss and don't beacon. 4968b032f27cSSam Leffler * Mark beacon state so when we reach RUN state we'll 4969b032f27cSSam Leffler * [re]setup beacons. Unblock the task q thread so 4970b032f27cSSam Leffler * deferred interrupt processing is done. 497158769f58SSam Leffler */ 4972b032f27cSSam Leffler ath_hal_intrset(ah, 4973b032f27cSSam Leffler sc->sc_imask &~ (HAL_INT_SWBA | HAL_INT_BMISS)); 49745591b213SSam Leffler sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS); 4975b032f27cSSam Leffler sc->sc_beacons = 0; 4976b032f27cSSam Leffler taskqueue_unblock(sc->sc_tq); 49775591b213SSam Leffler } 49785591b213SSam Leffler 4979b032f27cSSam Leffler ni = vap->iv_bss; 498068e8e04eSSam Leffler rfilt = ath_calcrxfilter(sc); 4981b032f27cSSam Leffler stamode = (vap->iv_opmode == IEEE80211_M_STA || 49827b916f89SSam Leffler vap->iv_opmode == IEEE80211_M_AHDEMO || 4983b032f27cSSam Leffler vap->iv_opmode == IEEE80211_M_IBSS); 498468e8e04eSSam Leffler if (stamode && nstate == IEEE80211_S_RUN) { 498568e8e04eSSam Leffler sc->sc_curaid = ni->ni_associd; 498668e8e04eSSam Leffler IEEE80211_ADDR_COPY(sc->sc_curbssid, ni->ni_bssid); 4987b032f27cSSam Leffler ath_hal_setassocid(ah, sc->sc_curbssid, sc->sc_curaid); 4988b032f27cSSam Leffler } 498968e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s aid 0x%x\n", 4990b032f27cSSam Leffler __func__, rfilt, ether_sprintf(sc->sc_curbssid), sc->sc_curaid); 499168e8e04eSSam Leffler ath_hal_setrxfilter(ah, rfilt); 499268e8e04eSSam Leffler 4993b032f27cSSam Leffler /* XXX is this to restore keycache on resume? */ 4994b032f27cSSam Leffler if (vap->iv_opmode != IEEE80211_M_STA && 4995b032f27cSSam Leffler (vap->iv_flags & IEEE80211_F_PRIVACY)) { 49965591b213SSam Leffler for (i = 0; i < IEEE80211_WEP_NKID; i++) 49975591b213SSam Leffler if (ath_hal_keyisvalid(ah, i)) 499868e8e04eSSam Leffler ath_hal_keysetmac(ah, i, ni->ni_bssid); 49995591b213SSam Leffler } 5000b032f27cSSam Leffler 5001b032f27cSSam Leffler /* 5002b032f27cSSam Leffler * Invoke the parent method to do net80211 work. 5003b032f27cSSam Leffler */ 5004b032f27cSSam Leffler error = avp->av_newstate(vap, nstate, arg); 5005b032f27cSSam Leffler if (error != 0) 5006b032f27cSSam Leffler goto bad; 5007c42a7b7eSSam Leffler 500868e8e04eSSam Leffler if (nstate == IEEE80211_S_RUN) { 5009b032f27cSSam Leffler /* NB: collect bss node again, it may have changed */ 5010b032f27cSSam Leffler ni = vap->iv_bss; 50115591b213SSam Leffler 5012b032f27cSSam Leffler DPRINTF(sc, ATH_DEBUG_STATE, 5013b032f27cSSam Leffler "%s(RUN): iv_flags 0x%08x bintvl %d bssid %s " 5014b032f27cSSam Leffler "capinfo 0x%04x chan %d\n", __func__, 5015b032f27cSSam Leffler vap->iv_flags, ni->ni_intval, ether_sprintf(ni->ni_bssid), 5016b032f27cSSam Leffler ni->ni_capinfo, ieee80211_chan2ieee(ic, ic->ic_curchan)); 5017b032f27cSSam Leffler 5018b032f27cSSam Leffler switch (vap->iv_opmode) { 5019584f7327SSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 502010ad9a77SSam Leffler case IEEE80211_M_AHDEMO: 502110ad9a77SSam Leffler if ((vap->iv_caps & IEEE80211_C_TDMA) == 0) 502210ad9a77SSam Leffler break; 502310ad9a77SSam Leffler /* fall thru... */ 502410ad9a77SSam Leffler #endif 5025e8fd88a3SSam Leffler case IEEE80211_M_HOSTAP: 5026e8fd88a3SSam Leffler case IEEE80211_M_IBSS: 502759aa14a9SRui Paulo case IEEE80211_M_MBSS: 50285591b213SSam Leffler /* 5029e8fd88a3SSam Leffler * Allocate and setup the beacon frame. 5030e8fd88a3SSam Leffler * 5031f818612bSSam Leffler * Stop any previous beacon DMA. This may be 5032f818612bSSam Leffler * necessary, for example, when an ibss merge 5033f818612bSSam Leffler * causes reconfiguration; there will be a state 5034f818612bSSam Leffler * transition from RUN->RUN that means we may 5035f818612bSSam Leffler * be called with beacon transmission active. 5036f818612bSSam Leffler */ 5037f818612bSSam Leffler ath_hal_stoptxdma(ah, sc->sc_bhalq); 5038b032f27cSSam Leffler 50395591b213SSam Leffler error = ath_beacon_alloc(sc, ni); 50405591b213SSam Leffler if (error != 0) 50415591b213SSam Leffler goto bad; 50427a04dc27SSam Leffler /* 504380d939bfSSam Leffler * If joining an adhoc network defer beacon timer 504480d939bfSSam Leffler * configuration to the next beacon frame so we 504580d939bfSSam Leffler * have a current TSF to use. Otherwise we're 5046b032f27cSSam Leffler * starting an ibss/bss so there's no need to delay; 5047b032f27cSSam Leffler * if this is the first vap moving to RUN state, then 5048b032f27cSSam Leffler * beacon state needs to be [re]configured. 50497a04dc27SSam Leffler */ 5050b032f27cSSam Leffler if (vap->iv_opmode == IEEE80211_M_IBSS && 5051b032f27cSSam Leffler ni->ni_tstamp.tsf != 0) { 505280d939bfSSam Leffler sc->sc_syncbeacon = 1; 5053b032f27cSSam Leffler } else if (!sc->sc_beacons) { 5054584f7327SSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 505510ad9a77SSam Leffler if (vap->iv_caps & IEEE80211_C_TDMA) 505610ad9a77SSam Leffler ath_tdma_config(sc, vap); 505710ad9a77SSam Leffler else 505810ad9a77SSam Leffler #endif 5059b032f27cSSam Leffler ath_beacon_config(sc, vap); 5060b032f27cSSam Leffler sc->sc_beacons = 1; 5061b032f27cSSam Leffler } 5062e8fd88a3SSam Leffler break; 5063e8fd88a3SSam Leffler case IEEE80211_M_STA: 5064e8fd88a3SSam Leffler /* 506580d939bfSSam Leffler * Defer beacon timer configuration to the next 506680d939bfSSam Leffler * beacon frame so we have a current TSF to use 506780d939bfSSam Leffler * (any TSF collected when scanning is likely old). 50687a04dc27SSam Leffler */ 506980d939bfSSam Leffler sc->sc_syncbeacon = 1; 5070e8fd88a3SSam Leffler break; 5071b032f27cSSam Leffler case IEEE80211_M_MONITOR: 5072b032f27cSSam Leffler /* 5073b032f27cSSam Leffler * Monitor mode vaps have only INIT->RUN and RUN->RUN 5074b032f27cSSam Leffler * transitions so we must re-enable interrupts here to 5075b032f27cSSam Leffler * handle the case of a single monitor mode vap. 5076b032f27cSSam Leffler */ 5077b032f27cSSam Leffler ath_hal_intrset(ah, sc->sc_imask); 5078b032f27cSSam Leffler break; 5079b032f27cSSam Leffler case IEEE80211_M_WDS: 5080b032f27cSSam Leffler break; 5081e8fd88a3SSam Leffler default: 5082e8fd88a3SSam Leffler break; 50835591b213SSam Leffler } 50845591b213SSam Leffler /* 50857b0c77ecSSam Leffler * Let the hal process statistics collected during a 50867b0c77ecSSam Leffler * scan so it can provide calibrated noise floor data. 50877b0c77ecSSam Leffler */ 50887b0c77ecSSam Leffler ath_hal_process_noisefloor(ah); 50897b0c77ecSSam Leffler /* 5090ffa2cab6SSam Leffler * Reset rssi stats; maybe not the best place... 5091ffa2cab6SSam Leffler */ 5092ffa2cab6SSam Leffler sc->sc_halstats.ns_avgbrssi = ATH_RSSI_DUMMY_MARKER; 5093ffa2cab6SSam Leffler sc->sc_halstats.ns_avgrssi = ATH_RSSI_DUMMY_MARKER; 5094ffa2cab6SSam Leffler sc->sc_halstats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER; 509545bbf62fSSam Leffler /* 5096b032f27cSSam Leffler * Finally, start any timers and the task q thread 5097b032f27cSSam Leffler * (in case we didn't go through SCAN state). 509845bbf62fSSam Leffler */ 50992dc7fcc4SSam Leffler if (ath_longcalinterval != 0) { 5100c42a7b7eSSam Leffler /* start periodic recalibration timer */ 51012dc7fcc4SSam Leffler callout_reset(&sc->sc_cal_ch, 1, ath_calibrate, sc); 51022dc7fcc4SSam Leffler } else { 51032dc7fcc4SSam Leffler DPRINTF(sc, ATH_DEBUG_CALIBRATE, 51042dc7fcc4SSam Leffler "%s: calibration disabled\n", __func__); 5105c42a7b7eSSam Leffler } 5106b032f27cSSam Leffler taskqueue_unblock(sc->sc_tq); 5107b032f27cSSam Leffler } else if (nstate == IEEE80211_S_INIT) { 5108b032f27cSSam Leffler /* 5109b032f27cSSam Leffler * If there are no vaps left in RUN state then 5110b032f27cSSam Leffler * shutdown host/driver operation: 5111b032f27cSSam Leffler * o disable interrupts 5112b032f27cSSam Leffler * o disable the task queue thread 5113b032f27cSSam Leffler * o mark beacon processing as stopped 5114b032f27cSSam Leffler */ 5115b032f27cSSam Leffler if (!ath_isanyrunningvaps(vap)) { 5116b032f27cSSam Leffler sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS); 5117b032f27cSSam Leffler /* disable interrupts */ 5118b032f27cSSam Leffler ath_hal_intrset(ah, sc->sc_imask &~ HAL_INT_GLOBAL); 5119b032f27cSSam Leffler taskqueue_block(sc->sc_tq); 5120b032f27cSSam Leffler sc->sc_beacons = 0; 5121b032f27cSSam Leffler } 5122584f7327SSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 512310ad9a77SSam Leffler ath_hal_setcca(ah, AH_TRUE); 512410ad9a77SSam Leffler #endif 5125b032f27cSSam Leffler } 51265591b213SSam Leffler bad: 51275591b213SSam Leffler return error; 51285591b213SSam Leffler } 51295591b213SSam Leffler 51305591b213SSam Leffler /* 5131e8fd88a3SSam Leffler * Allocate a key cache slot to the station so we can 5132e8fd88a3SSam Leffler * setup a mapping from key index to node. The key cache 5133e8fd88a3SSam Leffler * slot is needed for managing antenna state and for 5134e8fd88a3SSam Leffler * compression when stations do not use crypto. We do 5135e8fd88a3SSam Leffler * it uniliaterally here; if crypto is employed this slot 5136e8fd88a3SSam Leffler * will be reassigned. 5137e8fd88a3SSam Leffler */ 5138e8fd88a3SSam Leffler static void 5139e8fd88a3SSam Leffler ath_setup_stationkey(struct ieee80211_node *ni) 5140e8fd88a3SSam Leffler { 5141b032f27cSSam Leffler struct ieee80211vap *vap = ni->ni_vap; 5142b032f27cSSam Leffler struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; 5143c1225b52SSam Leffler ieee80211_keyix keyix, rxkeyix; 5144e8fd88a3SSam Leffler 5145b032f27cSSam Leffler if (!ath_key_alloc(vap, &ni->ni_ucastkey, &keyix, &rxkeyix)) { 5146e8fd88a3SSam Leffler /* 5147e8fd88a3SSam Leffler * Key cache is full; we'll fall back to doing 5148e8fd88a3SSam Leffler * the more expensive lookup in software. Note 5149e8fd88a3SSam Leffler * this also means no h/w compression. 5150e8fd88a3SSam Leffler */ 5151e8fd88a3SSam Leffler /* XXX msg+statistic */ 5152e8fd88a3SSam Leffler } else { 5153c1225b52SSam Leffler /* XXX locking? */ 5154e8fd88a3SSam Leffler ni->ni_ucastkey.wk_keyix = keyix; 5155c1225b52SSam Leffler ni->ni_ucastkey.wk_rxkeyix = rxkeyix; 515633052833SSam Leffler /* NB: must mark device key to get called back on delete */ 515733052833SSam Leffler ni->ni_ucastkey.wk_flags |= IEEE80211_KEY_DEVKEY; 5158d3ac945bSSam Leffler IEEE80211_ADDR_COPY(ni->ni_ucastkey.wk_macaddr, ni->ni_macaddr); 5159e8fd88a3SSam Leffler /* NB: this will create a pass-thru key entry */ 5160d3ac945bSSam Leffler ath_keyset(sc, &ni->ni_ucastkey, vap->iv_bss); 5161e8fd88a3SSam Leffler } 5162e8fd88a3SSam Leffler } 5163e8fd88a3SSam Leffler 5164e8fd88a3SSam Leffler /* 51655591b213SSam Leffler * Setup driver-specific state for a newly associated node. 51665591b213SSam Leffler * Note that we're called also on a re-associate, the isnew 51675591b213SSam Leffler * param tells us if this is the first time or not. 51685591b213SSam Leffler */ 51695591b213SSam Leffler static void 5170e9962332SSam Leffler ath_newassoc(struct ieee80211_node *ni, int isnew) 51715591b213SSam Leffler { 5172b032f27cSSam Leffler struct ath_node *an = ATH_NODE(ni); 5173b032f27cSSam Leffler struct ieee80211vap *vap = ni->ni_vap; 5174b032f27cSSam Leffler struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; 5175c62362cbSSam Leffler const struct ieee80211_txparam *tp = ni->ni_txparms; 51765591b213SSam Leffler 5177ab06fdf2SSam Leffler an->an_mcastrix = ath_tx_findrix(sc, tp->mcastrate); 5178ab06fdf2SSam Leffler an->an_mgmtrix = ath_tx_findrix(sc, tp->mgmtrate); 5179b032f27cSSam Leffler 5180b032f27cSSam Leffler ath_rate_newassoc(sc, an, isnew); 5181e8fd88a3SSam Leffler if (isnew && 5182b032f27cSSam Leffler (vap->iv_flags & IEEE80211_F_PRIVACY) == 0 && sc->sc_hasclrkey && 5183b032f27cSSam Leffler ni->ni_ucastkey.wk_keyix == IEEE80211_KEYIX_NONE) 5184e8fd88a3SSam Leffler ath_setup_stationkey(ni); 5185e8fd88a3SSam Leffler } 51865591b213SSam Leffler 51875591b213SSam Leffler static int 518859efa8b5SSam Leffler ath_setregdomain(struct ieee80211com *ic, struct ieee80211_regdomain *reg, 5189b032f27cSSam Leffler int nchans, struct ieee80211_channel chans[]) 5190b032f27cSSam Leffler { 5191b032f27cSSam Leffler struct ath_softc *sc = ic->ic_ifp->if_softc; 5192b032f27cSSam Leffler struct ath_hal *ah = sc->sc_ah; 519359efa8b5SSam Leffler HAL_STATUS status; 5194b032f27cSSam Leffler 5195033022a9SSam Leffler DPRINTF(sc, ATH_DEBUG_REGDOMAIN, 519659efa8b5SSam Leffler "%s: rd %u cc %u location %c%s\n", 519759efa8b5SSam Leffler __func__, reg->regdomain, reg->country, reg->location, 519859efa8b5SSam Leffler reg->ecm ? " ecm" : ""); 5199033022a9SSam Leffler 520059efa8b5SSam Leffler status = ath_hal_set_channels(ah, chans, nchans, 520159efa8b5SSam Leffler reg->country, reg->regdomain); 520259efa8b5SSam Leffler if (status != HAL_OK) { 520359efa8b5SSam Leffler DPRINTF(sc, ATH_DEBUG_REGDOMAIN, "%s: failed, status %u\n", 520459efa8b5SSam Leffler __func__, status); 520559efa8b5SSam Leffler return EINVAL; /* XXX */ 5206b032f27cSSam Leffler } 5207b032f27cSSam Leffler return 0; 5208b032f27cSSam Leffler } 5209b032f27cSSam Leffler 5210b032f27cSSam Leffler static void 5211b032f27cSSam Leffler ath_getradiocaps(struct ieee80211com *ic, 52125fe9f044SSam Leffler int maxchans, int *nchans, struct ieee80211_channel chans[]) 5213b032f27cSSam Leffler { 5214b032f27cSSam Leffler struct ath_softc *sc = ic->ic_ifp->if_softc; 5215b032f27cSSam Leffler struct ath_hal *ah = sc->sc_ah; 5216b032f27cSSam Leffler 521759efa8b5SSam Leffler DPRINTF(sc, ATH_DEBUG_REGDOMAIN, "%s: use rd %u cc %d\n", 521859efa8b5SSam Leffler __func__, SKU_DEBUG, CTRY_DEFAULT); 5219033022a9SSam Leffler 522059efa8b5SSam Leffler /* XXX check return */ 522159efa8b5SSam Leffler (void) ath_hal_getchannels(ah, chans, maxchans, nchans, 522259efa8b5SSam Leffler HAL_MODE_ALL, CTRY_DEFAULT, SKU_DEBUG, AH_TRUE); 5223033022a9SSam Leffler 5224b032f27cSSam Leffler } 5225b032f27cSSam Leffler 5226b032f27cSSam Leffler static int 5227b032f27cSSam Leffler ath_getchannels(struct ath_softc *sc) 5228b032f27cSSam Leffler { 5229b032f27cSSam Leffler struct ifnet *ifp = sc->sc_ifp; 5230b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 5231b032f27cSSam Leffler struct ath_hal *ah = sc->sc_ah; 523259efa8b5SSam Leffler HAL_STATUS status; 5233b032f27cSSam Leffler 5234b032f27cSSam Leffler /* 523559efa8b5SSam Leffler * Collect channel set based on EEPROM contents. 5236b032f27cSSam Leffler */ 523759efa8b5SSam Leffler status = ath_hal_init_channels(ah, ic->ic_channels, IEEE80211_CHAN_MAX, 523859efa8b5SSam Leffler &ic->ic_nchans, HAL_MODE_ALL, CTRY_DEFAULT, SKU_NONE, AH_TRUE); 523959efa8b5SSam Leffler if (status != HAL_OK) { 524059efa8b5SSam Leffler if_printf(ifp, "%s: unable to collect channel list from hal, " 524159efa8b5SSam Leffler "status %d\n", __func__, status); 524259efa8b5SSam Leffler return EINVAL; 524359efa8b5SSam Leffler } 5244ca876918SSam Leffler (void) ath_hal_getregdomain(ah, &sc->sc_eerd); 5245ca876918SSam Leffler ath_hal_getcountrycode(ah, &sc->sc_eecc); /* NB: cannot fail */ 524659efa8b5SSam Leffler /* XXX map Atheros sku's to net80211 SKU's */ 524759efa8b5SSam Leffler /* XXX net80211 types too small */ 524859efa8b5SSam Leffler ic->ic_regdomain.regdomain = (uint16_t) sc->sc_eerd; 524959efa8b5SSam Leffler ic->ic_regdomain.country = (uint16_t) sc->sc_eecc; 525059efa8b5SSam Leffler ic->ic_regdomain.isocc[0] = ' '; /* XXX don't know */ 525159efa8b5SSam Leffler ic->ic_regdomain.isocc[1] = ' '; 525259efa8b5SSam Leffler 5253b032f27cSSam Leffler ic->ic_regdomain.ecm = 1; 5254b032f27cSSam Leffler ic->ic_regdomain.location = 'I'; 5255033022a9SSam Leffler 5256033022a9SSam Leffler DPRINTF(sc, ATH_DEBUG_REGDOMAIN, 525759efa8b5SSam Leffler "%s: eeprom rd %u cc %u (mapped rd %u cc %u) location %c%s\n", 5258033022a9SSam Leffler __func__, sc->sc_eerd, sc->sc_eecc, 5259033022a9SSam Leffler ic->ic_regdomain.regdomain, ic->ic_regdomain.country, 526059efa8b5SSam Leffler ic->ic_regdomain.location, ic->ic_regdomain.ecm ? " ecm" : ""); 52615591b213SSam Leffler return 0; 52625591b213SSam Leffler } 52635591b213SSam Leffler 5264c42a7b7eSSam Leffler static void 52653e50ec2cSSam Leffler ath_led_done(void *arg) 5266c42a7b7eSSam Leffler { 52673e50ec2cSSam Leffler struct ath_softc *sc = arg; 52683e50ec2cSSam Leffler 52693e50ec2cSSam Leffler sc->sc_blinking = 0; 52703e50ec2cSSam Leffler } 5271c42a7b7eSSam Leffler 5272c42a7b7eSSam Leffler /* 52733e50ec2cSSam Leffler * Turn the LED off: flip the pin and then set a timer so no 52743e50ec2cSSam Leffler * update will happen for the specified duration. 5275c42a7b7eSSam Leffler */ 52763e50ec2cSSam Leffler static void 52773e50ec2cSSam Leffler ath_led_off(void *arg) 52783e50ec2cSSam Leffler { 52793e50ec2cSSam Leffler struct ath_softc *sc = arg; 52803e50ec2cSSam Leffler 52813e50ec2cSSam Leffler ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, !sc->sc_ledon); 52823e50ec2cSSam Leffler callout_reset(&sc->sc_ledtimer, sc->sc_ledoff, ath_led_done, sc); 5283c42a7b7eSSam Leffler } 52843e50ec2cSSam Leffler 52853e50ec2cSSam Leffler /* 52863e50ec2cSSam Leffler * Blink the LED according to the specified on/off times. 52873e50ec2cSSam Leffler */ 52883e50ec2cSSam Leffler static void 52893e50ec2cSSam Leffler ath_led_blink(struct ath_softc *sc, int on, int off) 52903e50ec2cSSam Leffler { 52913e50ec2cSSam Leffler DPRINTF(sc, ATH_DEBUG_LED, "%s: on %u off %u\n", __func__, on, off); 52923e50ec2cSSam Leffler ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, sc->sc_ledon); 52933e50ec2cSSam Leffler sc->sc_blinking = 1; 52943e50ec2cSSam Leffler sc->sc_ledoff = off; 52953e50ec2cSSam Leffler callout_reset(&sc->sc_ledtimer, on, ath_led_off, sc); 52963e50ec2cSSam Leffler } 52973e50ec2cSSam Leffler 52983e50ec2cSSam Leffler static void 529946d4d74cSSam Leffler ath_led_event(struct ath_softc *sc, int rix) 53003e50ec2cSSam Leffler { 53013e50ec2cSSam Leffler sc->sc_ledevent = ticks; /* time of last event */ 53023e50ec2cSSam Leffler if (sc->sc_blinking) /* don't interrupt active blink */ 53033e50ec2cSSam Leffler return; 530446d4d74cSSam Leffler ath_led_blink(sc, sc->sc_hwmap[rix].ledon, sc->sc_hwmap[rix].ledoff); 5305c42a7b7eSSam Leffler } 5306c42a7b7eSSam Leffler 53076c4612b9SSam Leffler static int 53086c4612b9SSam Leffler ath_rate_setup(struct ath_softc *sc, u_int mode) 53096c4612b9SSam Leffler { 53106c4612b9SSam Leffler struct ath_hal *ah = sc->sc_ah; 53116c4612b9SSam Leffler const HAL_RATE_TABLE *rt; 53126c4612b9SSam Leffler 53136c4612b9SSam Leffler switch (mode) { 53146c4612b9SSam Leffler case IEEE80211_MODE_11A: 53156c4612b9SSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_11A); 53166c4612b9SSam Leffler break; 5317724c193aSSam Leffler case IEEE80211_MODE_HALF: 5318aaa70f2fSSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_11A_HALF_RATE); 5319aaa70f2fSSam Leffler break; 5320724c193aSSam Leffler case IEEE80211_MODE_QUARTER: 5321aaa70f2fSSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_11A_QUARTER_RATE); 5322aaa70f2fSSam Leffler break; 53236c4612b9SSam Leffler case IEEE80211_MODE_11B: 53246c4612b9SSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_11B); 53256c4612b9SSam Leffler break; 53266c4612b9SSam Leffler case IEEE80211_MODE_11G: 53276c4612b9SSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_11G); 53286c4612b9SSam Leffler break; 53296c4612b9SSam Leffler case IEEE80211_MODE_TURBO_A: 533068e8e04eSSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_108A); 53316c4612b9SSam Leffler break; 53326c4612b9SSam Leffler case IEEE80211_MODE_TURBO_G: 53336c4612b9SSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_108G); 53346c4612b9SSam Leffler break; 533568e8e04eSSam Leffler case IEEE80211_MODE_STURBO_A: 533668e8e04eSSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_TURBO); 533768e8e04eSSam Leffler break; 533868e8e04eSSam Leffler case IEEE80211_MODE_11NA: 533968e8e04eSSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_11NA_HT20); 534068e8e04eSSam Leffler break; 534168e8e04eSSam Leffler case IEEE80211_MODE_11NG: 534268e8e04eSSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_11NG_HT20); 534368e8e04eSSam Leffler break; 53446c4612b9SSam Leffler default: 53456c4612b9SSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid mode %u\n", 53466c4612b9SSam Leffler __func__, mode); 53476c4612b9SSam Leffler return 0; 53486c4612b9SSam Leffler } 53496c4612b9SSam Leffler sc->sc_rates[mode] = rt; 5350aaa70f2fSSam Leffler return (rt != NULL); 53515591b213SSam Leffler } 53525591b213SSam Leffler 53535591b213SSam Leffler static void 53545591b213SSam Leffler ath_setcurmode(struct ath_softc *sc, enum ieee80211_phymode mode) 53555591b213SSam Leffler { 53563e50ec2cSSam Leffler #define N(a) (sizeof(a)/sizeof(a[0])) 53573e50ec2cSSam Leffler /* NB: on/off times from the Atheros NDIS driver, w/ permission */ 53583e50ec2cSSam Leffler static const struct { 53593e50ec2cSSam Leffler u_int rate; /* tx/rx 802.11 rate */ 53603e50ec2cSSam Leffler u_int16_t timeOn; /* LED on time (ms) */ 53613e50ec2cSSam Leffler u_int16_t timeOff; /* LED off time (ms) */ 53623e50ec2cSSam Leffler } blinkrates[] = { 53633e50ec2cSSam Leffler { 108, 40, 10 }, 53643e50ec2cSSam Leffler { 96, 44, 11 }, 53653e50ec2cSSam Leffler { 72, 50, 13 }, 53663e50ec2cSSam Leffler { 48, 57, 14 }, 53673e50ec2cSSam Leffler { 36, 67, 16 }, 53683e50ec2cSSam Leffler { 24, 80, 20 }, 53693e50ec2cSSam Leffler { 22, 100, 25 }, 53703e50ec2cSSam Leffler { 18, 133, 34 }, 53713e50ec2cSSam Leffler { 12, 160, 40 }, 53723e50ec2cSSam Leffler { 10, 200, 50 }, 53733e50ec2cSSam Leffler { 6, 240, 58 }, 53743e50ec2cSSam Leffler { 4, 267, 66 }, 53753e50ec2cSSam Leffler { 2, 400, 100 }, 53763e50ec2cSSam Leffler { 0, 500, 130 }, 5377724c193aSSam Leffler /* XXX half/quarter rates */ 53783e50ec2cSSam Leffler }; 53795591b213SSam Leffler const HAL_RATE_TABLE *rt; 53803e50ec2cSSam Leffler int i, j; 53815591b213SSam Leffler 53825591b213SSam Leffler memset(sc->sc_rixmap, 0xff, sizeof(sc->sc_rixmap)); 53835591b213SSam Leffler rt = sc->sc_rates[mode]; 53845591b213SSam Leffler KASSERT(rt != NULL, ("no h/w rate set for phy mode %u", mode)); 5385180f268dSSam Leffler for (i = 0; i < rt->rateCount; i++) { 5386180f268dSSam Leffler uint8_t ieeerate = rt->info[i].dot11Rate & IEEE80211_RATE_VAL; 5387180f268dSSam Leffler if (rt->info[i].phy != IEEE80211_T_HT) 5388180f268dSSam Leffler sc->sc_rixmap[ieeerate] = i; 5389180f268dSSam Leffler else 5390180f268dSSam Leffler sc->sc_rixmap[ieeerate | IEEE80211_RATE_MCS] = i; 5391180f268dSSam Leffler } 53921b1a8e41SSam Leffler memset(sc->sc_hwmap, 0, sizeof(sc->sc_hwmap)); 539346d4d74cSSam Leffler for (i = 0; i < N(sc->sc_hwmap); i++) { 539446d4d74cSSam Leffler if (i >= rt->rateCount) { 53953e50ec2cSSam Leffler sc->sc_hwmap[i].ledon = (500 * hz) / 1000; 53963e50ec2cSSam Leffler sc->sc_hwmap[i].ledoff = (130 * hz) / 1000; 539716b4851aSSam Leffler continue; 53983e50ec2cSSam Leffler } 53993e50ec2cSSam Leffler sc->sc_hwmap[i].ieeerate = 540046d4d74cSSam Leffler rt->info[i].dot11Rate & IEEE80211_RATE_VAL; 540146d4d74cSSam Leffler if (rt->info[i].phy == IEEE80211_T_HT) 540226041a14SSam Leffler sc->sc_hwmap[i].ieeerate |= IEEE80211_RATE_MCS; 5403d3be6f5bSSam Leffler sc->sc_hwmap[i].txflags = IEEE80211_RADIOTAP_F_DATAPAD; 540446d4d74cSSam Leffler if (rt->info[i].shortPreamble || 540546d4d74cSSam Leffler rt->info[i].phy == IEEE80211_T_OFDM) 5406d3be6f5bSSam Leffler sc->sc_hwmap[i].txflags |= IEEE80211_RADIOTAP_F_SHORTPRE; 54075463c4a4SSam Leffler sc->sc_hwmap[i].rxflags = sc->sc_hwmap[i].txflags; 54083e50ec2cSSam Leffler for (j = 0; j < N(blinkrates)-1; j++) 54093e50ec2cSSam Leffler if (blinkrates[j].rate == sc->sc_hwmap[i].ieeerate) 54103e50ec2cSSam Leffler break; 54113e50ec2cSSam Leffler /* NB: this uses the last entry if the rate isn't found */ 54123e50ec2cSSam Leffler /* XXX beware of overlow */ 54133e50ec2cSSam Leffler sc->sc_hwmap[i].ledon = (blinkrates[j].timeOn * hz) / 1000; 54143e50ec2cSSam Leffler sc->sc_hwmap[i].ledoff = (blinkrates[j].timeOff * hz) / 1000; 5415c42a7b7eSSam Leffler } 54165591b213SSam Leffler sc->sc_currates = rt; 54175591b213SSam Leffler sc->sc_curmode = mode; 54185591b213SSam Leffler /* 5419c42a7b7eSSam Leffler * All protection frames are transmited at 2Mb/s for 5420c42a7b7eSSam Leffler * 11g, otherwise at 1Mb/s. 54215591b213SSam Leffler */ 5422913a1ba1SSam Leffler if (mode == IEEE80211_MODE_11G) 5423ab06fdf2SSam Leffler sc->sc_protrix = ath_tx_findrix(sc, 2*2); 5424913a1ba1SSam Leffler else 5425ab06fdf2SSam Leffler sc->sc_protrix = ath_tx_findrix(sc, 2*1); 54264fa8d4efSDaniel Eischen /* NB: caller is responsible for resetting rate control state */ 54273e50ec2cSSam Leffler #undef N 54285591b213SSam Leffler } 54295591b213SSam Leffler 5430c42a7b7eSSam Leffler static void 54312e986da5SSam Leffler ath_watchdog(void *arg) 5432c42a7b7eSSam Leffler { 54332e986da5SSam Leffler struct ath_softc *sc = arg; 5434c42a7b7eSSam Leffler 54352e986da5SSam Leffler if (sc->sc_wd_timer != 0 && --sc->sc_wd_timer == 0) { 54362e986da5SSam Leffler struct ifnet *ifp = sc->sc_ifp; 5437459bc4f0SSam Leffler uint32_t hangs; 5438459bc4f0SSam Leffler 5439459bc4f0SSam Leffler if (ath_hal_gethangstate(sc->sc_ah, 0xffff, &hangs) && 5440459bc4f0SSam Leffler hangs != 0) { 5441459bc4f0SSam Leffler if_printf(ifp, "%s hang detected (0x%x)\n", 5442459bc4f0SSam Leffler hangs & 0xff ? "bb" : "mac", hangs); 5443459bc4f0SSam Leffler } else 5444c42a7b7eSSam Leffler if_printf(ifp, "device timeout\n"); 5445c42a7b7eSSam Leffler ath_reset(ifp); 5446c42a7b7eSSam Leffler ifp->if_oerrors++; 5447c42a7b7eSSam Leffler sc->sc_stats.ast_watchdog++; 5448c42a7b7eSSam Leffler } 54492e986da5SSam Leffler callout_schedule(&sc->sc_wd_ch, hz); 5450c42a7b7eSSam Leffler } 5451c42a7b7eSSam Leffler 5452a585a9a1SSam Leffler #ifdef ATH_DIAGAPI 5453c42a7b7eSSam Leffler /* 5454c42a7b7eSSam Leffler * Diagnostic interface to the HAL. This is used by various 5455c42a7b7eSSam Leffler * tools to do things like retrieve register contents for 5456c42a7b7eSSam Leffler * debugging. The mechanism is intentionally opaque so that 5457c42a7b7eSSam Leffler * it can change frequently w/o concern for compatiblity. 5458c42a7b7eSSam Leffler */ 5459c42a7b7eSSam Leffler static int 5460c42a7b7eSSam Leffler ath_ioctl_diag(struct ath_softc *sc, struct ath_diag *ad) 5461c42a7b7eSSam Leffler { 5462c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 5463c42a7b7eSSam Leffler u_int id = ad->ad_id & ATH_DIAG_ID; 5464c42a7b7eSSam Leffler void *indata = NULL; 5465c42a7b7eSSam Leffler void *outdata = NULL; 5466c42a7b7eSSam Leffler u_int32_t insize = ad->ad_in_size; 5467c42a7b7eSSam Leffler u_int32_t outsize = ad->ad_out_size; 5468c42a7b7eSSam Leffler int error = 0; 5469c42a7b7eSSam Leffler 5470c42a7b7eSSam Leffler if (ad->ad_id & ATH_DIAG_IN) { 5471c42a7b7eSSam Leffler /* 5472c42a7b7eSSam Leffler * Copy in data. 5473c42a7b7eSSam Leffler */ 5474c42a7b7eSSam Leffler indata = malloc(insize, M_TEMP, M_NOWAIT); 5475c42a7b7eSSam Leffler if (indata == NULL) { 5476c42a7b7eSSam Leffler error = ENOMEM; 5477c42a7b7eSSam Leffler goto bad; 5478c42a7b7eSSam Leffler } 5479c42a7b7eSSam Leffler error = copyin(ad->ad_in_data, indata, insize); 5480c42a7b7eSSam Leffler if (error) 5481c42a7b7eSSam Leffler goto bad; 5482c42a7b7eSSam Leffler } 5483c42a7b7eSSam Leffler if (ad->ad_id & ATH_DIAG_DYN) { 5484c42a7b7eSSam Leffler /* 5485c42a7b7eSSam Leffler * Allocate a buffer for the results (otherwise the HAL 5486c42a7b7eSSam Leffler * returns a pointer to a buffer where we can read the 5487c42a7b7eSSam Leffler * results). Note that we depend on the HAL leaving this 5488c42a7b7eSSam Leffler * pointer for us to use below in reclaiming the buffer; 5489c42a7b7eSSam Leffler * may want to be more defensive. 5490c42a7b7eSSam Leffler */ 5491c42a7b7eSSam Leffler outdata = malloc(outsize, M_TEMP, M_NOWAIT); 5492c42a7b7eSSam Leffler if (outdata == NULL) { 5493c42a7b7eSSam Leffler error = ENOMEM; 5494c42a7b7eSSam Leffler goto bad; 5495c42a7b7eSSam Leffler } 5496c42a7b7eSSam Leffler } 5497c42a7b7eSSam Leffler if (ath_hal_getdiagstate(ah, id, indata, insize, &outdata, &outsize)) { 5498c42a7b7eSSam Leffler if (outsize < ad->ad_out_size) 5499c42a7b7eSSam Leffler ad->ad_out_size = outsize; 5500c42a7b7eSSam Leffler if (outdata != NULL) 5501c42a7b7eSSam Leffler error = copyout(outdata, ad->ad_out_data, 5502c42a7b7eSSam Leffler ad->ad_out_size); 5503c42a7b7eSSam Leffler } else { 5504c42a7b7eSSam Leffler error = EINVAL; 5505c42a7b7eSSam Leffler } 5506c42a7b7eSSam Leffler bad: 5507c42a7b7eSSam Leffler if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL) 5508c42a7b7eSSam Leffler free(indata, M_TEMP); 5509c42a7b7eSSam Leffler if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL) 5510c42a7b7eSSam Leffler free(outdata, M_TEMP); 5511c42a7b7eSSam Leffler return error; 5512c42a7b7eSSam Leffler } 5513a585a9a1SSam Leffler #endif /* ATH_DIAGAPI */ 5514c42a7b7eSSam Leffler 5515c42a7b7eSSam Leffler static int 5516c42a7b7eSSam Leffler ath_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 5517c42a7b7eSSam Leffler { 5518c42a7b7eSSam Leffler #define IS_RUNNING(ifp) \ 551913f4c340SRobert Watson ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING)) 5520c42a7b7eSSam Leffler struct ath_softc *sc = ifp->if_softc; 5521b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 5522c42a7b7eSSam Leffler struct ifreq *ifr = (struct ifreq *)data; 552384784be1SSam Leffler const HAL_RATE_TABLE *rt; 5524c42a7b7eSSam Leffler int error = 0; 5525c42a7b7eSSam Leffler 5526c42a7b7eSSam Leffler switch (cmd) { 5527c42a7b7eSSam Leffler case SIOCSIFFLAGS: 552831a8c1edSAndrew Thompson ATH_LOCK(sc); 5529c42a7b7eSSam Leffler if (IS_RUNNING(ifp)) { 5530c42a7b7eSSam Leffler /* 5531c42a7b7eSSam Leffler * To avoid rescanning another access point, 5532c42a7b7eSSam Leffler * do not call ath_init() here. Instead, 5533c42a7b7eSSam Leffler * only reflect promisc mode settings. 5534c42a7b7eSSam Leffler */ 5535c42a7b7eSSam Leffler ath_mode_init(sc); 5536c42a7b7eSSam Leffler } else if (ifp->if_flags & IFF_UP) { 5537c42a7b7eSSam Leffler /* 5538c42a7b7eSSam Leffler * Beware of being called during attach/detach 5539c42a7b7eSSam Leffler * to reset promiscuous mode. In that case we 5540c42a7b7eSSam Leffler * will still be marked UP but not RUNNING. 5541c42a7b7eSSam Leffler * However trying to re-init the interface 5542c42a7b7eSSam Leffler * is the wrong thing to do as we've already 5543c42a7b7eSSam Leffler * torn down much of our state. There's 5544c42a7b7eSSam Leffler * probably a better way to deal with this. 5545c42a7b7eSSam Leffler */ 5546b032f27cSSam Leffler if (!sc->sc_invalid) 5547fc74a9f9SBrooks Davis ath_init(sc); /* XXX lose error */ 5548d3ac945bSSam Leffler } else { 5549c42a7b7eSSam Leffler ath_stop_locked(ifp); 5550d3ac945bSSam Leffler #ifdef notyet 5551d3ac945bSSam Leffler /* XXX must wakeup in places like ath_vap_delete */ 5552d3ac945bSSam Leffler if (!sc->sc_invalid) 5553d3ac945bSSam Leffler ath_hal_setpower(sc->sc_ah, HAL_PM_FULL_SLEEP); 5554d3ac945bSSam Leffler #endif 5555d3ac945bSSam Leffler } 555631a8c1edSAndrew Thompson ATH_UNLOCK(sc); 5557c42a7b7eSSam Leffler break; 5558b032f27cSSam Leffler case SIOCGIFMEDIA: 5559b032f27cSSam Leffler case SIOCSIFMEDIA: 5560b032f27cSSam Leffler error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); 5561b032f27cSSam Leffler break; 5562c42a7b7eSSam Leffler case SIOCGATHSTATS: 5563c42a7b7eSSam Leffler /* NB: embed these numbers to get a consistent view */ 5564c42a7b7eSSam Leffler sc->sc_stats.ast_tx_packets = ifp->if_opackets; 5565c42a7b7eSSam Leffler sc->sc_stats.ast_rx_packets = ifp->if_ipackets; 556684784be1SSam Leffler sc->sc_stats.ast_tx_rssi = ATH_RSSI(sc->sc_halstats.ns_avgtxrssi); 556784784be1SSam Leffler sc->sc_stats.ast_rx_rssi = ATH_RSSI(sc->sc_halstats.ns_avgrssi); 5568584f7327SSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 556910ad9a77SSam Leffler sc->sc_stats.ast_tdma_tsfadjp = TDMA_AVG(sc->sc_avgtsfdeltap); 557010ad9a77SSam Leffler sc->sc_stats.ast_tdma_tsfadjm = TDMA_AVG(sc->sc_avgtsfdeltam); 557110ad9a77SSam Leffler #endif 557284784be1SSam Leffler rt = sc->sc_currates; 557346d4d74cSSam Leffler /* XXX HT rates */ 557446d4d74cSSam Leffler sc->sc_stats.ast_tx_rate = 557546d4d74cSSam Leffler rt->info[sc->sc_txrix].dot11Rate &~ IEEE80211_RATE_BASIC; 5576c42a7b7eSSam Leffler return copyout(&sc->sc_stats, 5577c42a7b7eSSam Leffler ifr->ifr_data, sizeof (sc->sc_stats)); 55783fc21fedSSam Leffler case SIOCZATHSTATS: 55793fc21fedSSam Leffler error = priv_check(curthread, PRIV_DRIVER); 55803fc21fedSSam Leffler if (error == 0) 55813fc21fedSSam Leffler memset(&sc->sc_stats, 0, sizeof(sc->sc_stats)); 55823fc21fedSSam Leffler break; 5583a585a9a1SSam Leffler #ifdef ATH_DIAGAPI 5584c42a7b7eSSam Leffler case SIOCGATHDIAG: 5585c42a7b7eSSam Leffler error = ath_ioctl_diag(sc, (struct ath_diag *) ifr); 5586c42a7b7eSSam Leffler break; 5587a585a9a1SSam Leffler #endif 558831a8c1edSAndrew Thompson case SIOCGIFADDR: 5589b032f27cSSam Leffler error = ether_ioctl(ifp, cmd, data); 5590c42a7b7eSSam Leffler break; 559131a8c1edSAndrew Thompson default: 559231a8c1edSAndrew Thompson error = EINVAL; 559331a8c1edSAndrew Thompson break; 5594c42a7b7eSSam Leffler } 5595c42a7b7eSSam Leffler return error; 5596a614e076SSam Leffler #undef IS_RUNNING 5597c42a7b7eSSam Leffler } 5598c42a7b7eSSam Leffler 5599c42a7b7eSSam Leffler static int 5600c42a7b7eSSam Leffler ath_sysctl_slottime(SYSCTL_HANDLER_ARGS) 5601c42a7b7eSSam Leffler { 5602c42a7b7eSSam Leffler struct ath_softc *sc = arg1; 5603c42a7b7eSSam Leffler u_int slottime = ath_hal_getslottime(sc->sc_ah); 5604c42a7b7eSSam Leffler int error; 5605c42a7b7eSSam Leffler 5606c42a7b7eSSam Leffler error = sysctl_handle_int(oidp, &slottime, 0, req); 5607c42a7b7eSSam Leffler if (error || !req->newptr) 5608c42a7b7eSSam Leffler return error; 5609c42a7b7eSSam Leffler return !ath_hal_setslottime(sc->sc_ah, slottime) ? EINVAL : 0; 5610c42a7b7eSSam Leffler } 5611c42a7b7eSSam Leffler 5612c42a7b7eSSam Leffler static int 5613c42a7b7eSSam Leffler ath_sysctl_acktimeout(SYSCTL_HANDLER_ARGS) 5614c42a7b7eSSam Leffler { 5615c42a7b7eSSam Leffler struct ath_softc *sc = arg1; 5616c42a7b7eSSam Leffler u_int acktimeout = ath_hal_getacktimeout(sc->sc_ah); 5617c42a7b7eSSam Leffler int error; 5618c42a7b7eSSam Leffler 5619c42a7b7eSSam Leffler error = sysctl_handle_int(oidp, &acktimeout, 0, req); 5620c42a7b7eSSam Leffler if (error || !req->newptr) 5621c42a7b7eSSam Leffler return error; 5622c42a7b7eSSam Leffler return !ath_hal_setacktimeout(sc->sc_ah, acktimeout) ? EINVAL : 0; 5623c42a7b7eSSam Leffler } 5624c42a7b7eSSam Leffler 5625c42a7b7eSSam Leffler static int 5626c42a7b7eSSam Leffler ath_sysctl_ctstimeout(SYSCTL_HANDLER_ARGS) 5627c42a7b7eSSam Leffler { 5628c42a7b7eSSam Leffler struct ath_softc *sc = arg1; 5629c42a7b7eSSam Leffler u_int ctstimeout = ath_hal_getctstimeout(sc->sc_ah); 5630c42a7b7eSSam Leffler int error; 5631c42a7b7eSSam Leffler 5632c42a7b7eSSam Leffler error = sysctl_handle_int(oidp, &ctstimeout, 0, req); 5633c42a7b7eSSam Leffler if (error || !req->newptr) 5634c42a7b7eSSam Leffler return error; 5635c42a7b7eSSam Leffler return !ath_hal_setctstimeout(sc->sc_ah, ctstimeout) ? EINVAL : 0; 5636c42a7b7eSSam Leffler } 5637c42a7b7eSSam Leffler 5638c42a7b7eSSam Leffler static int 5639c42a7b7eSSam Leffler ath_sysctl_softled(SYSCTL_HANDLER_ARGS) 5640c42a7b7eSSam Leffler { 5641c42a7b7eSSam Leffler struct ath_softc *sc = arg1; 5642c42a7b7eSSam Leffler int softled = sc->sc_softled; 5643c42a7b7eSSam Leffler int error; 5644c42a7b7eSSam Leffler 5645c42a7b7eSSam Leffler error = sysctl_handle_int(oidp, &softled, 0, req); 5646c42a7b7eSSam Leffler if (error || !req->newptr) 5647c42a7b7eSSam Leffler return error; 56483e50ec2cSSam Leffler softled = (softled != 0); 5649c42a7b7eSSam Leffler if (softled != sc->sc_softled) { 56503e50ec2cSSam Leffler if (softled) { 56513e50ec2cSSam Leffler /* NB: handle any sc_ledpin change */ 5652869ff02eSSam Leffler ath_hal_gpioCfgOutput(sc->sc_ah, sc->sc_ledpin, 5653869ff02eSSam Leffler HAL_GPIO_MUX_MAC_NETWORK_LED); 56543e50ec2cSSam Leffler ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, 56553e50ec2cSSam Leffler !sc->sc_ledon); 56563e50ec2cSSam Leffler } 5657c42a7b7eSSam Leffler sc->sc_softled = softled; 5658c42a7b7eSSam Leffler } 5659c42a7b7eSSam Leffler return 0; 5660c42a7b7eSSam Leffler } 5661c42a7b7eSSam Leffler 5662c42a7b7eSSam Leffler static int 5663b298baf2SSam Leffler ath_sysctl_ledpin(SYSCTL_HANDLER_ARGS) 5664b298baf2SSam Leffler { 5665b298baf2SSam Leffler struct ath_softc *sc = arg1; 5666b298baf2SSam Leffler int ledpin = sc->sc_ledpin; 5667b298baf2SSam Leffler int error; 5668b298baf2SSam Leffler 5669b298baf2SSam Leffler error = sysctl_handle_int(oidp, &ledpin, 0, req); 5670b298baf2SSam Leffler if (error || !req->newptr) 5671b298baf2SSam Leffler return error; 5672b298baf2SSam Leffler if (ledpin != sc->sc_ledpin) { 5673b298baf2SSam Leffler sc->sc_ledpin = ledpin; 5674b298baf2SSam Leffler if (sc->sc_softled) { 5675869ff02eSSam Leffler ath_hal_gpioCfgOutput(sc->sc_ah, sc->sc_ledpin, 5676869ff02eSSam Leffler HAL_GPIO_MUX_MAC_NETWORK_LED); 5677b298baf2SSam Leffler ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, 5678b298baf2SSam Leffler !sc->sc_ledon); 5679b298baf2SSam Leffler } 5680b298baf2SSam Leffler } 5681b298baf2SSam Leffler return 0; 5682b298baf2SSam Leffler } 5683b298baf2SSam Leffler 5684b298baf2SSam Leffler static int 56858debcae4SSam Leffler ath_sysctl_txantenna(SYSCTL_HANDLER_ARGS) 56868debcae4SSam Leffler { 56878debcae4SSam Leffler struct ath_softc *sc = arg1; 56888debcae4SSam Leffler u_int txantenna = ath_hal_getantennaswitch(sc->sc_ah); 56898debcae4SSam Leffler int error; 56908debcae4SSam Leffler 56918debcae4SSam Leffler error = sysctl_handle_int(oidp, &txantenna, 0, req); 56928debcae4SSam Leffler if (!error && req->newptr) { 56938debcae4SSam Leffler /* XXX assumes 2 antenna ports */ 56948debcae4SSam Leffler if (txantenna < HAL_ANT_VARIABLE || txantenna > HAL_ANT_FIXED_B) 56958debcae4SSam Leffler return EINVAL; 56968debcae4SSam Leffler ath_hal_setantennaswitch(sc->sc_ah, txantenna); 56978debcae4SSam Leffler /* 56988debcae4SSam Leffler * NB: with the switch locked this isn't meaningful, 56998debcae4SSam Leffler * but set it anyway so things like radiotap get 57008debcae4SSam Leffler * consistent info in their data. 57018debcae4SSam Leffler */ 57028debcae4SSam Leffler sc->sc_txantenna = txantenna; 57038debcae4SSam Leffler } 57048debcae4SSam Leffler return error; 57058debcae4SSam Leffler } 57068debcae4SSam Leffler 57078debcae4SSam Leffler static int 5708c42a7b7eSSam Leffler ath_sysctl_rxantenna(SYSCTL_HANDLER_ARGS) 5709c42a7b7eSSam Leffler { 5710c42a7b7eSSam Leffler struct ath_softc *sc = arg1; 5711c42a7b7eSSam Leffler u_int defantenna = ath_hal_getdefantenna(sc->sc_ah); 5712c42a7b7eSSam Leffler int error; 5713c42a7b7eSSam Leffler 5714c42a7b7eSSam Leffler error = sysctl_handle_int(oidp, &defantenna, 0, req); 5715c42a7b7eSSam Leffler if (!error && req->newptr) 5716c42a7b7eSSam Leffler ath_hal_setdefantenna(sc->sc_ah, defantenna); 5717c42a7b7eSSam Leffler return error; 5718c42a7b7eSSam Leffler } 5719c42a7b7eSSam Leffler 5720c42a7b7eSSam Leffler static int 5721c42a7b7eSSam Leffler ath_sysctl_diversity(SYSCTL_HANDLER_ARGS) 5722c42a7b7eSSam Leffler { 5723c42a7b7eSSam Leffler struct ath_softc *sc = arg1; 5724c59005e9SSam Leffler u_int diversity = ath_hal_getdiversity(sc->sc_ah); 5725c42a7b7eSSam Leffler int error; 5726c42a7b7eSSam Leffler 5727c42a7b7eSSam Leffler error = sysctl_handle_int(oidp, &diversity, 0, req); 5728c42a7b7eSSam Leffler if (error || !req->newptr) 5729c42a7b7eSSam Leffler return error; 5730c59005e9SSam Leffler if (!ath_hal_setdiversity(sc->sc_ah, diversity)) 5731c59005e9SSam Leffler return EINVAL; 5732c42a7b7eSSam Leffler sc->sc_diversity = diversity; 5733c59005e9SSam Leffler return 0; 5734c42a7b7eSSam Leffler } 5735c42a7b7eSSam Leffler 5736c42a7b7eSSam Leffler static int 5737c42a7b7eSSam Leffler ath_sysctl_diag(SYSCTL_HANDLER_ARGS) 5738c42a7b7eSSam Leffler { 5739c42a7b7eSSam Leffler struct ath_softc *sc = arg1; 5740c42a7b7eSSam Leffler u_int32_t diag; 5741c42a7b7eSSam Leffler int error; 5742c42a7b7eSSam Leffler 5743c42a7b7eSSam Leffler if (!ath_hal_getdiag(sc->sc_ah, &diag)) 5744c42a7b7eSSam Leffler return EINVAL; 5745c42a7b7eSSam Leffler error = sysctl_handle_int(oidp, &diag, 0, req); 5746c42a7b7eSSam Leffler if (error || !req->newptr) 5747c42a7b7eSSam Leffler return error; 5748c42a7b7eSSam Leffler return !ath_hal_setdiag(sc->sc_ah, diag) ? EINVAL : 0; 5749c42a7b7eSSam Leffler } 5750c42a7b7eSSam Leffler 5751c42a7b7eSSam Leffler static int 5752c42a7b7eSSam Leffler ath_sysctl_tpscale(SYSCTL_HANDLER_ARGS) 5753c42a7b7eSSam Leffler { 5754c42a7b7eSSam Leffler struct ath_softc *sc = arg1; 5755fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 5756c42a7b7eSSam Leffler u_int32_t scale; 5757c42a7b7eSSam Leffler int error; 5758c42a7b7eSSam Leffler 5759ee7d6840SSam Leffler (void) ath_hal_gettpscale(sc->sc_ah, &scale); 5760c42a7b7eSSam Leffler error = sysctl_handle_int(oidp, &scale, 0, req); 5761c42a7b7eSSam Leffler if (error || !req->newptr) 5762c42a7b7eSSam Leffler return error; 576316d84e01SSam Leffler return !ath_hal_settpscale(sc->sc_ah, scale) ? EINVAL : 576416d84e01SSam Leffler (ifp->if_drv_flags & IFF_DRV_RUNNING) ? ath_reset(ifp) : 0; 5765c42a7b7eSSam Leffler } 5766c42a7b7eSSam Leffler 5767c42a7b7eSSam Leffler static int 5768c42a7b7eSSam Leffler ath_sysctl_tpc(SYSCTL_HANDLER_ARGS) 5769c42a7b7eSSam Leffler { 5770c42a7b7eSSam Leffler struct ath_softc *sc = arg1; 5771c42a7b7eSSam Leffler u_int tpc = ath_hal_gettpc(sc->sc_ah); 5772c42a7b7eSSam Leffler int error; 5773c42a7b7eSSam Leffler 5774c42a7b7eSSam Leffler error = sysctl_handle_int(oidp, &tpc, 0, req); 5775c42a7b7eSSam Leffler if (error || !req->newptr) 5776c42a7b7eSSam Leffler return error; 5777c42a7b7eSSam Leffler return !ath_hal_settpc(sc->sc_ah, tpc) ? EINVAL : 0; 5778c42a7b7eSSam Leffler } 5779c42a7b7eSSam Leffler 578017f3f177SSam Leffler static int 5781bd5a9920SSam Leffler ath_sysctl_rfkill(SYSCTL_HANDLER_ARGS) 5782bd5a9920SSam Leffler { 5783bd5a9920SSam Leffler struct ath_softc *sc = arg1; 578416d84e01SSam Leffler struct ifnet *ifp = sc->sc_ifp; 5785bd5a9920SSam Leffler struct ath_hal *ah = sc->sc_ah; 5786bd5a9920SSam Leffler u_int rfkill = ath_hal_getrfkill(ah); 5787bd5a9920SSam Leffler int error; 5788bd5a9920SSam Leffler 5789bd5a9920SSam Leffler error = sysctl_handle_int(oidp, &rfkill, 0, req); 5790bd5a9920SSam Leffler if (error || !req->newptr) 5791bd5a9920SSam Leffler return error; 5792bd5a9920SSam Leffler if (rfkill == ath_hal_getrfkill(ah)) /* unchanged */ 5793bd5a9920SSam Leffler return 0; 579416d84e01SSam Leffler if (!ath_hal_setrfkill(ah, rfkill)) 5795bd5a9920SSam Leffler return EINVAL; 579616d84e01SSam Leffler return (ifp->if_drv_flags & IFF_DRV_RUNNING) ? ath_reset(ifp) : 0; 5797bd5a9920SSam Leffler } 5798bd5a9920SSam Leffler 5799bd5a9920SSam Leffler static int 5800bd5a9920SSam Leffler ath_sysctl_rfsilent(SYSCTL_HANDLER_ARGS) 5801bd5a9920SSam Leffler { 5802bd5a9920SSam Leffler struct ath_softc *sc = arg1; 5803bd5a9920SSam Leffler u_int rfsilent; 5804bd5a9920SSam Leffler int error; 5805bd5a9920SSam Leffler 5806ee7d6840SSam Leffler (void) ath_hal_getrfsilent(sc->sc_ah, &rfsilent); 5807bd5a9920SSam Leffler error = sysctl_handle_int(oidp, &rfsilent, 0, req); 5808bd5a9920SSam Leffler if (error || !req->newptr) 5809bd5a9920SSam Leffler return error; 5810bd5a9920SSam Leffler if (!ath_hal_setrfsilent(sc->sc_ah, rfsilent)) 5811bd5a9920SSam Leffler return EINVAL; 5812bd5a9920SSam Leffler sc->sc_rfsilentpin = rfsilent & 0x1c; 5813bd5a9920SSam Leffler sc->sc_rfsilentpol = (rfsilent & 0x2) != 0; 5814bd5a9920SSam Leffler return 0; 5815bd5a9920SSam Leffler } 5816bd5a9920SSam Leffler 5817bd5a9920SSam Leffler static int 5818bd5a9920SSam Leffler ath_sysctl_tpack(SYSCTL_HANDLER_ARGS) 5819bd5a9920SSam Leffler { 5820bd5a9920SSam Leffler struct ath_softc *sc = arg1; 5821bd5a9920SSam Leffler u_int32_t tpack; 5822bd5a9920SSam Leffler int error; 5823bd5a9920SSam Leffler 5824ee7d6840SSam Leffler (void) ath_hal_gettpack(sc->sc_ah, &tpack); 5825bd5a9920SSam Leffler error = sysctl_handle_int(oidp, &tpack, 0, req); 5826bd5a9920SSam Leffler if (error || !req->newptr) 5827bd5a9920SSam Leffler return error; 5828bd5a9920SSam Leffler return !ath_hal_settpack(sc->sc_ah, tpack) ? EINVAL : 0; 5829bd5a9920SSam Leffler } 5830bd5a9920SSam Leffler 5831bd5a9920SSam Leffler static int 5832bd5a9920SSam Leffler ath_sysctl_tpcts(SYSCTL_HANDLER_ARGS) 5833bd5a9920SSam Leffler { 5834bd5a9920SSam Leffler struct ath_softc *sc = arg1; 5835bd5a9920SSam Leffler u_int32_t tpcts; 5836bd5a9920SSam Leffler int error; 5837bd5a9920SSam Leffler 5838ee7d6840SSam Leffler (void) ath_hal_gettpcts(sc->sc_ah, &tpcts); 5839bd5a9920SSam Leffler error = sysctl_handle_int(oidp, &tpcts, 0, req); 5840bd5a9920SSam Leffler if (error || !req->newptr) 5841bd5a9920SSam Leffler return error; 5842bd5a9920SSam Leffler return !ath_hal_settpcts(sc->sc_ah, tpcts) ? EINVAL : 0; 5843bd5a9920SSam Leffler } 5844bd5a9920SSam Leffler 5845577eaa60SSam Leffler static int 5846577eaa60SSam Leffler ath_sysctl_intmit(SYSCTL_HANDLER_ARGS) 5847577eaa60SSam Leffler { 5848577eaa60SSam Leffler struct ath_softc *sc = arg1; 5849577eaa60SSam Leffler int intmit, error; 5850577eaa60SSam Leffler 5851577eaa60SSam Leffler intmit = ath_hal_getintmit(sc->sc_ah); 5852577eaa60SSam Leffler error = sysctl_handle_int(oidp, &intmit, 0, req); 5853577eaa60SSam Leffler if (error || !req->newptr) 5854577eaa60SSam Leffler return error; 5855577eaa60SSam Leffler return !ath_hal_setintmit(sc->sc_ah, intmit) ? EINVAL : 0; 5856577eaa60SSam Leffler } 5857577eaa60SSam Leffler 5858584f7327SSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 58599c859a04SSam Leffler static int 58609c859a04SSam Leffler ath_sysctl_setcca(SYSCTL_HANDLER_ARGS) 58619c859a04SSam Leffler { 58629c859a04SSam Leffler struct ath_softc *sc = arg1; 58639c859a04SSam Leffler int setcca, error; 58649c859a04SSam Leffler 58659c859a04SSam Leffler setcca = sc->sc_setcca; 58669c859a04SSam Leffler error = sysctl_handle_int(oidp, &setcca, 0, req); 58679c859a04SSam Leffler if (error || !req->newptr) 58689c859a04SSam Leffler return error; 58699c859a04SSam Leffler sc->sc_setcca = (setcca != 0); 58709c859a04SSam Leffler return 0; 58719c859a04SSam Leffler } 5872584f7327SSam Leffler #endif /* IEEE80211_SUPPORT_TDMA */ 58739c859a04SSam Leffler 5874c42a7b7eSSam Leffler static void 5875c42a7b7eSSam Leffler ath_sysctlattach(struct ath_softc *sc) 5876c42a7b7eSSam Leffler { 5877c42a7b7eSSam Leffler struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); 5878c42a7b7eSSam Leffler struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); 5879c59005e9SSam Leffler struct ath_hal *ah = sc->sc_ah; 5880c42a7b7eSSam Leffler 58816dc7dc9aSMatthew D Fleming SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 5882ca876918SSam Leffler "countrycode", CTLFLAG_RD, &sc->sc_eecc, 0, 5883ca876918SSam Leffler "EEPROM country code"); 58846dc7dc9aSMatthew D Fleming SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 5885ca876918SSam Leffler "regdomain", CTLFLAG_RD, &sc->sc_eerd, 0, 5886ca876918SSam Leffler "EEPROM regdomain code"); 5887a585a9a1SSam Leffler #ifdef ATH_DEBUG 5888c42a7b7eSSam Leffler SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 5889c42a7b7eSSam Leffler "debug", CTLFLAG_RW, &sc->sc_debug, 0, 5890c42a7b7eSSam Leffler "control debugging printfs"); 5891d2f6ed15SSam Leffler #endif 5892c42a7b7eSSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 5893c42a7b7eSSam Leffler "slottime", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 5894c42a7b7eSSam Leffler ath_sysctl_slottime, "I", "802.11 slot time (us)"); 5895c42a7b7eSSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 5896c42a7b7eSSam Leffler "acktimeout", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 5897c42a7b7eSSam Leffler ath_sysctl_acktimeout, "I", "802.11 ACK timeout (us)"); 5898c42a7b7eSSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 5899c42a7b7eSSam Leffler "ctstimeout", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 5900c42a7b7eSSam Leffler ath_sysctl_ctstimeout, "I", "802.11 CTS timeout (us)"); 5901c42a7b7eSSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 5902c42a7b7eSSam Leffler "softled", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 5903c42a7b7eSSam Leffler ath_sysctl_softled, "I", "enable/disable software LED support"); 5904b298baf2SSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 5905b298baf2SSam Leffler "ledpin", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 5906b298baf2SSam Leffler ath_sysctl_ledpin, "I", "GPIO pin connected to LED"); 59076dc7dc9aSMatthew D Fleming SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 59083e50ec2cSSam Leffler "ledon", CTLFLAG_RW, &sc->sc_ledon, 0, 59093e50ec2cSSam Leffler "setting to turn LED on"); 59106dc7dc9aSMatthew D Fleming SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 59113e50ec2cSSam Leffler "ledidle", CTLFLAG_RW, &sc->sc_ledidle, 0, 59123e50ec2cSSam Leffler "idle time for inactivity LED (ticks)"); 59138debcae4SSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 59148debcae4SSam Leffler "txantenna", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 59158debcae4SSam Leffler ath_sysctl_txantenna, "I", "antenna switch"); 5916c42a7b7eSSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 5917c42a7b7eSSam Leffler "rxantenna", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 5918c42a7b7eSSam Leffler ath_sysctl_rxantenna, "I", "default/rx antenna"); 5919c59005e9SSam Leffler if (ath_hal_hasdiversity(ah)) 5920c42a7b7eSSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 5921c42a7b7eSSam Leffler "diversity", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 5922c42a7b7eSSam Leffler ath_sysctl_diversity, "I", "antenna diversity"); 5923c42a7b7eSSam Leffler sc->sc_txintrperiod = ATH_TXINTR_PERIOD; 59246dc7dc9aSMatthew D Fleming SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 5925c42a7b7eSSam Leffler "txintrperiod", CTLFLAG_RW, &sc->sc_txintrperiod, 0, 5926c42a7b7eSSam Leffler "tx descriptor batching"); 5927c42a7b7eSSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 5928c42a7b7eSSam Leffler "diag", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 5929c42a7b7eSSam Leffler ath_sysctl_diag, "I", "h/w diagnostic control"); 5930c42a7b7eSSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 5931c42a7b7eSSam Leffler "tpscale", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 5932c42a7b7eSSam Leffler ath_sysctl_tpscale, "I", "tx power scaling"); 5933bd5a9920SSam Leffler if (ath_hal_hastpc(ah)) { 5934c42a7b7eSSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 5935c42a7b7eSSam Leffler "tpc", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 5936c42a7b7eSSam Leffler ath_sysctl_tpc, "I", "enable/disable per-packet TPC"); 5937bd5a9920SSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 5938bd5a9920SSam Leffler "tpack", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 5939bd5a9920SSam Leffler ath_sysctl_tpack, "I", "tx power for ack frames"); 5940bd5a9920SSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 5941bd5a9920SSam Leffler "tpcts", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 5942bd5a9920SSam Leffler ath_sysctl_tpcts, "I", "tx power for cts frames"); 5943bd5a9920SSam Leffler } 5944bd5a9920SSam Leffler if (ath_hal_hasrfsilent(ah)) { 5945bd5a9920SSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 5946bd5a9920SSam Leffler "rfsilent", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 5947bd5a9920SSam Leffler ath_sysctl_rfsilent, "I", "h/w RF silent config"); 5948bd5a9920SSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 5949bd5a9920SSam Leffler "rfkill", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 5950bd5a9920SSam Leffler ath_sysctl_rfkill, "I", "enable/disable RF kill switch"); 5951bd5a9920SSam Leffler } 5952577eaa60SSam Leffler if (ath_hal_hasintmit(ah)) { 5953577eaa60SSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 5954577eaa60SSam Leffler "intmit", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 5955577eaa60SSam Leffler ath_sysctl_intmit, "I", "interference mitigation"); 5956577eaa60SSam Leffler } 59577b0c77ecSSam Leffler sc->sc_monpass = HAL_RXERR_DECRYPT | HAL_RXERR_MIC; 59586dc7dc9aSMatthew D Fleming SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 59597b0c77ecSSam Leffler "monpass", CTLFLAG_RW, &sc->sc_monpass, 0, 59607b0c77ecSSam Leffler "mask of error frames to pass when monitoring"); 5961584f7327SSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 596210ad9a77SSam Leffler if (ath_hal_macversion(ah) > 0x78) { 596310ad9a77SSam Leffler sc->sc_tdmadbaprep = 2; 5964240577c2SMatthew D Fleming SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 596510ad9a77SSam Leffler "dbaprep", CTLFLAG_RW, &sc->sc_tdmadbaprep, 0, 596610ad9a77SSam Leffler "TDMA DBA preparation time"); 596710ad9a77SSam Leffler sc->sc_tdmaswbaprep = 10; 5968240577c2SMatthew D Fleming SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 596910ad9a77SSam Leffler "swbaprep", CTLFLAG_RW, &sc->sc_tdmaswbaprep, 0, 597010ad9a77SSam Leffler "TDMA SWBA preparation time"); 5971240577c2SMatthew D Fleming SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 597210ad9a77SSam Leffler "guardtime", CTLFLAG_RW, &sc->sc_tdmaguard, 0, 597310ad9a77SSam Leffler "TDMA slot guard time"); 5974240577c2SMatthew D Fleming SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 597510ad9a77SSam Leffler "superframe", CTLFLAG_RD, &sc->sc_tdmabintval, 0, 597610ad9a77SSam Leffler "TDMA calculated super frame"); 59779c859a04SSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 59789c859a04SSam Leffler "setcca", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 59799c859a04SSam Leffler ath_sysctl_setcca, "I", "enable CCA control"); 598010ad9a77SSam Leffler } 598110ad9a77SSam Leffler #endif 5982c42a7b7eSSam Leffler } 5983c42a7b7eSSam Leffler 5984c42a7b7eSSam Leffler /* 5985c42a7b7eSSam Leffler * Announce various information on device/driver attach. 5986c42a7b7eSSam Leffler */ 5987c42a7b7eSSam Leffler static void 5988c42a7b7eSSam Leffler ath_announce(struct ath_softc *sc) 5989c42a7b7eSSam Leffler { 5990fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 5991c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 5992c42a7b7eSSam Leffler 5993498657cfSSam Leffler if_printf(ifp, "AR%s mac %d.%d RF%s phy %d.%d\n", 5994498657cfSSam Leffler ath_hal_mac_name(ah), ah->ah_macVersion, ah->ah_macRev, 5995498657cfSSam Leffler ath_hal_rf_name(ah), ah->ah_phyRev >> 4, ah->ah_phyRev & 0xf); 5996c42a7b7eSSam Leffler if (bootverbose) { 5997c42a7b7eSSam Leffler int i; 5998c42a7b7eSSam Leffler for (i = 0; i <= WME_AC_VO; i++) { 5999c42a7b7eSSam Leffler struct ath_txq *txq = sc->sc_ac2q[i]; 6000c42a7b7eSSam Leffler if_printf(ifp, "Use hw queue %u for %s traffic\n", 6001c42a7b7eSSam Leffler txq->axq_qnum, ieee80211_wme_acnames[i]); 6002c42a7b7eSSam Leffler } 6003c42a7b7eSSam Leffler if_printf(ifp, "Use hw queue %u for CAB traffic\n", 6004c42a7b7eSSam Leffler sc->sc_cabq->axq_qnum); 6005c42a7b7eSSam Leffler if_printf(ifp, "Use hw queue %u for beacons\n", sc->sc_bhalq); 6006c42a7b7eSSam Leffler } 6007e2d787faSSam Leffler if (ath_rxbuf != ATH_RXBUF) 6008e2d787faSSam Leffler if_printf(ifp, "using %u rx buffers\n", ath_rxbuf); 6009e2d787faSSam Leffler if (ath_txbuf != ATH_TXBUF) 6010e2d787faSSam Leffler if_printf(ifp, "using %u tx buffers\n", ath_txbuf); 60119ac01d39SRui Paulo if (sc->sc_mcastkey && bootverbose) 60129ac01d39SRui Paulo if_printf(ifp, "using multicast key search\n"); 6013c42a7b7eSSam Leffler } 601410ad9a77SSam Leffler 6015584f7327SSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 601610ad9a77SSam Leffler static __inline uint32_t 601710ad9a77SSam Leffler ath_hal_getnexttbtt(struct ath_hal *ah) 601810ad9a77SSam Leffler { 601910ad9a77SSam Leffler #define AR_TIMER0 0x8028 602010ad9a77SSam Leffler return OS_REG_READ(ah, AR_TIMER0); 602110ad9a77SSam Leffler } 602210ad9a77SSam Leffler 602310ad9a77SSam Leffler static __inline void 602410ad9a77SSam Leffler ath_hal_adjusttsf(struct ath_hal *ah, int32_t tsfdelta) 602510ad9a77SSam Leffler { 602610ad9a77SSam Leffler /* XXX handle wrap/overflow */ 602710ad9a77SSam Leffler OS_REG_WRITE(ah, AR_TSF_L32, OS_REG_READ(ah, AR_TSF_L32) + tsfdelta); 602810ad9a77SSam Leffler } 602910ad9a77SSam Leffler 603010ad9a77SSam Leffler static void 603110ad9a77SSam Leffler ath_tdma_settimers(struct ath_softc *sc, u_int32_t nexttbtt, u_int32_t bintval) 603210ad9a77SSam Leffler { 603310ad9a77SSam Leffler struct ath_hal *ah = sc->sc_ah; 603410ad9a77SSam Leffler HAL_BEACON_TIMERS bt; 603510ad9a77SSam Leffler 603610ad9a77SSam Leffler bt.bt_intval = bintval | HAL_BEACON_ENA; 603710ad9a77SSam Leffler bt.bt_nexttbtt = nexttbtt; 603810ad9a77SSam Leffler bt.bt_nextdba = (nexttbtt<<3) - sc->sc_tdmadbaprep; 603910ad9a77SSam Leffler bt.bt_nextswba = (nexttbtt<<3) - sc->sc_tdmaswbaprep; 604010ad9a77SSam Leffler bt.bt_nextatim = nexttbtt+1; 604110ad9a77SSam Leffler ath_hal_beaconsettimers(ah, &bt); 604210ad9a77SSam Leffler } 604310ad9a77SSam Leffler 604410ad9a77SSam Leffler /* 604510ad9a77SSam Leffler * Calculate the beacon interval. This is periodic in the 604610ad9a77SSam Leffler * superframe for the bss. We assume each station is configured 604710ad9a77SSam Leffler * identically wrt transmit rate so the guard time we calculate 604810ad9a77SSam Leffler * above will be the same on all stations. Note we need to 604910ad9a77SSam Leffler * factor in the xmit time because the hardware will schedule 605010ad9a77SSam Leffler * a frame for transmit if the start of the frame is within 605110ad9a77SSam Leffler * the burst time. When we get hardware that properly kills 605210ad9a77SSam Leffler * frames in the PCU we can reduce/eliminate the guard time. 605310ad9a77SSam Leffler * 605410ad9a77SSam Leffler * Roundup to 1024 is so we have 1 TU buffer in the guard time 605510ad9a77SSam Leffler * to deal with the granularity of the nexttbtt timer. 11n MAC's 605610ad9a77SSam Leffler * with 1us timer granularity should allow us to reduce/eliminate 605710ad9a77SSam Leffler * this. 605810ad9a77SSam Leffler */ 605910ad9a77SSam Leffler static void 606010ad9a77SSam Leffler ath_tdma_bintvalsetup(struct ath_softc *sc, 606110ad9a77SSam Leffler const struct ieee80211_tdma_state *tdma) 606210ad9a77SSam Leffler { 606310ad9a77SSam Leffler /* copy from vap state (XXX check all vaps have same value?) */ 606410ad9a77SSam Leffler sc->sc_tdmaslotlen = tdma->tdma_slotlen; 606510ad9a77SSam Leffler 606610ad9a77SSam Leffler sc->sc_tdmabintval = roundup((sc->sc_tdmaslotlen+sc->sc_tdmaguard) * 606710ad9a77SSam Leffler tdma->tdma_slotcnt, 1024); 606810ad9a77SSam Leffler sc->sc_tdmabintval >>= 10; /* TSF -> TU */ 606910ad9a77SSam Leffler if (sc->sc_tdmabintval & 1) 607010ad9a77SSam Leffler sc->sc_tdmabintval++; 607110ad9a77SSam Leffler 607210ad9a77SSam Leffler if (tdma->tdma_slot == 0) { 607310ad9a77SSam Leffler /* 607410ad9a77SSam Leffler * Only slot 0 beacons; other slots respond. 607510ad9a77SSam Leffler */ 607610ad9a77SSam Leffler sc->sc_imask |= HAL_INT_SWBA; 607710ad9a77SSam Leffler sc->sc_tdmaswba = 0; /* beacon immediately */ 607810ad9a77SSam Leffler } else { 607910ad9a77SSam Leffler /* XXX all vaps must be slot 0 or slot !0 */ 608010ad9a77SSam Leffler sc->sc_imask &= ~HAL_INT_SWBA; 608110ad9a77SSam Leffler } 608210ad9a77SSam Leffler } 608310ad9a77SSam Leffler 608410ad9a77SSam Leffler /* 608510ad9a77SSam Leffler * Max 802.11 overhead. This assumes no 4-address frames and 608610ad9a77SSam Leffler * the encapsulation done by ieee80211_encap (llc). We also 608710ad9a77SSam Leffler * include potential crypto overhead. 608810ad9a77SSam Leffler */ 608910ad9a77SSam Leffler #define IEEE80211_MAXOVERHEAD \ 609010ad9a77SSam Leffler (sizeof(struct ieee80211_qosframe) \ 609110ad9a77SSam Leffler + sizeof(struct llc) \ 609210ad9a77SSam Leffler + IEEE80211_ADDR_LEN \ 609310ad9a77SSam Leffler + IEEE80211_WEP_IVLEN \ 609410ad9a77SSam Leffler + IEEE80211_WEP_KIDLEN \ 609510ad9a77SSam Leffler + IEEE80211_WEP_CRCLEN \ 609610ad9a77SSam Leffler + IEEE80211_WEP_MICLEN \ 609710ad9a77SSam Leffler + IEEE80211_CRC_LEN) 609810ad9a77SSam Leffler 609910ad9a77SSam Leffler /* 610010ad9a77SSam Leffler * Setup initially for tdma operation. Start the beacon 610110ad9a77SSam Leffler * timers and enable SWBA if we are slot 0. Otherwise 610210ad9a77SSam Leffler * we wait for slot 0 to arrive so we can sync up before 610310ad9a77SSam Leffler * starting to transmit. 610410ad9a77SSam Leffler */ 610510ad9a77SSam Leffler static void 610610ad9a77SSam Leffler ath_tdma_config(struct ath_softc *sc, struct ieee80211vap *vap) 610710ad9a77SSam Leffler { 610810ad9a77SSam Leffler struct ath_hal *ah = sc->sc_ah; 610910ad9a77SSam Leffler struct ifnet *ifp = sc->sc_ifp; 611010ad9a77SSam Leffler struct ieee80211com *ic = ifp->if_l2com; 611110ad9a77SSam Leffler const struct ieee80211_txparam *tp; 611210ad9a77SSam Leffler const struct ieee80211_tdma_state *tdma = NULL; 611310ad9a77SSam Leffler int rix; 611410ad9a77SSam Leffler 611510ad9a77SSam Leffler if (vap == NULL) { 611610ad9a77SSam Leffler vap = TAILQ_FIRST(&ic->ic_vaps); /* XXX */ 611710ad9a77SSam Leffler if (vap == NULL) { 611810ad9a77SSam Leffler if_printf(ifp, "%s: no vaps?\n", __func__); 611910ad9a77SSam Leffler return; 612010ad9a77SSam Leffler } 612110ad9a77SSam Leffler } 612210ad9a77SSam Leffler tp = vap->iv_bss->ni_txparms; 612310ad9a77SSam Leffler /* 612410ad9a77SSam Leffler * Calculate the guard time for each slot. This is the 612510ad9a77SSam Leffler * time to send a maximal-size frame according to the 612610ad9a77SSam Leffler * fixed/lowest transmit rate. Note that the interface 612710ad9a77SSam Leffler * mtu does not include the 802.11 overhead so we must 612810ad9a77SSam Leffler * tack that on (ath_hal_computetxtime includes the 612910ad9a77SSam Leffler * preamble and plcp in it's calculation). 613010ad9a77SSam Leffler */ 613110ad9a77SSam Leffler tdma = vap->iv_tdma; 613210ad9a77SSam Leffler if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) 6133ab06fdf2SSam Leffler rix = ath_tx_findrix(sc, tp->ucastrate); 613410ad9a77SSam Leffler else 6135ab06fdf2SSam Leffler rix = ath_tx_findrix(sc, tp->mcastrate); 613610ad9a77SSam Leffler /* XXX short preamble assumed */ 613710ad9a77SSam Leffler sc->sc_tdmaguard = ath_hal_computetxtime(ah, sc->sc_currates, 613810ad9a77SSam Leffler ifp->if_mtu + IEEE80211_MAXOVERHEAD, rix, AH_TRUE); 613910ad9a77SSam Leffler 614010ad9a77SSam Leffler ath_hal_intrset(ah, 0); 614110ad9a77SSam Leffler 614210ad9a77SSam Leffler ath_beaconq_config(sc); /* setup h/w beacon q */ 61439c859a04SSam Leffler if (sc->sc_setcca) 614410ad9a77SSam Leffler ath_hal_setcca(ah, AH_FALSE); /* disable CCA */ 614510ad9a77SSam Leffler ath_tdma_bintvalsetup(sc, tdma); /* calculate beacon interval */ 614610ad9a77SSam Leffler ath_tdma_settimers(sc, sc->sc_tdmabintval, 614710ad9a77SSam Leffler sc->sc_tdmabintval | HAL_BEACON_RESET_TSF); 614810ad9a77SSam Leffler sc->sc_syncbeacon = 0; 614910ad9a77SSam Leffler 615010ad9a77SSam Leffler sc->sc_avgtsfdeltap = TDMA_DUMMY_MARKER; 615110ad9a77SSam Leffler sc->sc_avgtsfdeltam = TDMA_DUMMY_MARKER; 615210ad9a77SSam Leffler 615310ad9a77SSam Leffler ath_hal_intrset(ah, sc->sc_imask); 615410ad9a77SSam Leffler 615510ad9a77SSam Leffler DPRINTF(sc, ATH_DEBUG_TDMA, "%s: slot %u len %uus cnt %u " 615610ad9a77SSam Leffler "bsched %u guard %uus bintval %u TU dba prep %u\n", __func__, 615710ad9a77SSam Leffler tdma->tdma_slot, tdma->tdma_slotlen, tdma->tdma_slotcnt, 615810ad9a77SSam Leffler tdma->tdma_bintval, sc->sc_tdmaguard, sc->sc_tdmabintval, 615910ad9a77SSam Leffler sc->sc_tdmadbaprep); 616010ad9a77SSam Leffler } 616110ad9a77SSam Leffler 616210ad9a77SSam Leffler /* 616310ad9a77SSam Leffler * Update tdma operation. Called from the 802.11 layer 616410ad9a77SSam Leffler * when a beacon is received from the TDMA station operating 616510ad9a77SSam Leffler * in the slot immediately preceding us in the bss. Use 616610ad9a77SSam Leffler * the rx timestamp for the beacon frame to update our 616710ad9a77SSam Leffler * beacon timers so we follow their schedule. Note that 616810ad9a77SSam Leffler * by using the rx timestamp we implicitly include the 616910ad9a77SSam Leffler * propagation delay in our schedule. 617010ad9a77SSam Leffler */ 617110ad9a77SSam Leffler static void 617210ad9a77SSam Leffler ath_tdma_update(struct ieee80211_node *ni, 61732bc3ce77SSam Leffler const struct ieee80211_tdma_param *tdma, int changed) 617410ad9a77SSam Leffler { 617510ad9a77SSam Leffler #define TSF_TO_TU(_h,_l) \ 617610ad9a77SSam Leffler ((((u_int32_t)(_h)) << 22) | (((u_int32_t)(_l)) >> 10)) 617710ad9a77SSam Leffler #define TU_TO_TSF(_tu) (((u_int64_t)(_tu)) << 10) 617810ad9a77SSam Leffler struct ieee80211vap *vap = ni->ni_vap; 617910ad9a77SSam Leffler struct ieee80211com *ic = ni->ni_ic; 618010ad9a77SSam Leffler struct ath_softc *sc = ic->ic_ifp->if_softc; 618110ad9a77SSam Leffler struct ath_hal *ah = sc->sc_ah; 618210ad9a77SSam Leffler const HAL_RATE_TABLE *rt = sc->sc_currates; 618310ad9a77SSam Leffler u_int64_t tsf, rstamp, nextslot; 618410ad9a77SSam Leffler u_int32_t txtime, nextslottu, timer0; 618510ad9a77SSam Leffler int32_t tudelta, tsfdelta; 618610ad9a77SSam Leffler const struct ath_rx_status *rs; 618710ad9a77SSam Leffler int rix; 618810ad9a77SSam Leffler 618910ad9a77SSam Leffler sc->sc_stats.ast_tdma_update++; 619010ad9a77SSam Leffler 619110ad9a77SSam Leffler /* 619210ad9a77SSam Leffler * Check for and adopt configuration changes. 619310ad9a77SSam Leffler */ 61942bc3ce77SSam Leffler if (changed != 0) { 619510ad9a77SSam Leffler const struct ieee80211_tdma_state *ts = vap->iv_tdma; 619610ad9a77SSam Leffler 619710ad9a77SSam Leffler ath_tdma_bintvalsetup(sc, ts); 6198040972a1SSam Leffler if (changed & TDMA_UPDATE_SLOTLEN) 6199040972a1SSam Leffler ath_wme_update(ic); 620010ad9a77SSam Leffler 620110ad9a77SSam Leffler DPRINTF(sc, ATH_DEBUG_TDMA, 620210ad9a77SSam Leffler "%s: adopt slot %u slotcnt %u slotlen %u us " 620310ad9a77SSam Leffler "bintval %u TU\n", __func__, 620410ad9a77SSam Leffler ts->tdma_slot, ts->tdma_slotcnt, ts->tdma_slotlen, 620510ad9a77SSam Leffler sc->sc_tdmabintval); 620610ad9a77SSam Leffler 620710ad9a77SSam Leffler /* XXX right? */ 620810ad9a77SSam Leffler ath_hal_intrset(ah, sc->sc_imask); 620910ad9a77SSam Leffler /* NB: beacon timers programmed below */ 621010ad9a77SSam Leffler } 621110ad9a77SSam Leffler 621210ad9a77SSam Leffler /* extend rx timestamp to 64 bits */ 62135463c4a4SSam Leffler rs = sc->sc_lastrs; 621410ad9a77SSam Leffler tsf = ath_hal_gettsf64(ah); 62155463c4a4SSam Leffler rstamp = ath_extend_tsf(rs->rs_tstamp, tsf); 621610ad9a77SSam Leffler /* 621710ad9a77SSam Leffler * The rx timestamp is set by the hardware on completing 621810ad9a77SSam Leffler * reception (at the point where the rx descriptor is DMA'd 621910ad9a77SSam Leffler * to the host). To find the start of our next slot we 622010ad9a77SSam Leffler * must adjust this time by the time required to send 622110ad9a77SSam Leffler * the packet just received. 622210ad9a77SSam Leffler */ 622310ad9a77SSam Leffler rix = rt->rateCodeToIndex[rs->rs_rate]; 622410ad9a77SSam Leffler txtime = ath_hal_computetxtime(ah, rt, rs->rs_datalen, rix, 622510ad9a77SSam Leffler rt->info[rix].shortPreamble); 622610ad9a77SSam Leffler /* NB: << 9 is to cvt to TU and /2 */ 622710ad9a77SSam Leffler nextslot = (rstamp - txtime) + (sc->sc_tdmabintval << 9); 622810ad9a77SSam Leffler nextslottu = TSF_TO_TU(nextslot>>32, nextslot) & HAL_BEACON_PERIOD; 622910ad9a77SSam Leffler 623010ad9a77SSam Leffler /* 623110ad9a77SSam Leffler * TIMER0 is the h/w's idea of NextTBTT (in TU's). Convert 623210ad9a77SSam Leffler * to usecs and calculate the difference between what the 623310ad9a77SSam Leffler * other station thinks and what we have programmed. This 623410ad9a77SSam Leffler * lets us figure how to adjust our timers to match. The 623510ad9a77SSam Leffler * adjustments are done by pulling the TSF forward and possibly 623610ad9a77SSam Leffler * rewriting the beacon timers. 623710ad9a77SSam Leffler */ 623810ad9a77SSam Leffler timer0 = ath_hal_getnexttbtt(ah); 623910ad9a77SSam Leffler tsfdelta = (int32_t)((nextslot % TU_TO_TSF(HAL_BEACON_PERIOD+1)) - TU_TO_TSF(timer0)); 624010ad9a77SSam Leffler 624110ad9a77SSam Leffler DPRINTF(sc, ATH_DEBUG_TDMA_TIMER, 624210ad9a77SSam Leffler "tsfdelta %d avg +%d/-%d\n", tsfdelta, 624310ad9a77SSam Leffler TDMA_AVG(sc->sc_avgtsfdeltap), TDMA_AVG(sc->sc_avgtsfdeltam)); 624410ad9a77SSam Leffler 624510ad9a77SSam Leffler if (tsfdelta < 0) { 624610ad9a77SSam Leffler TDMA_SAMPLE(sc->sc_avgtsfdeltap, 0); 624710ad9a77SSam Leffler TDMA_SAMPLE(sc->sc_avgtsfdeltam, -tsfdelta); 624810ad9a77SSam Leffler tsfdelta = -tsfdelta % 1024; 624910ad9a77SSam Leffler nextslottu++; 625010ad9a77SSam Leffler } else if (tsfdelta > 0) { 625110ad9a77SSam Leffler TDMA_SAMPLE(sc->sc_avgtsfdeltap, tsfdelta); 625210ad9a77SSam Leffler TDMA_SAMPLE(sc->sc_avgtsfdeltam, 0); 625310ad9a77SSam Leffler tsfdelta = 1024 - (tsfdelta % 1024); 625410ad9a77SSam Leffler nextslottu++; 625510ad9a77SSam Leffler } else { 625610ad9a77SSam Leffler TDMA_SAMPLE(sc->sc_avgtsfdeltap, 0); 625710ad9a77SSam Leffler TDMA_SAMPLE(sc->sc_avgtsfdeltam, 0); 625810ad9a77SSam Leffler } 625910ad9a77SSam Leffler tudelta = nextslottu - timer0; 626010ad9a77SSam Leffler 626110ad9a77SSam Leffler /* 626210ad9a77SSam Leffler * Copy sender's timetstamp into tdma ie so they can 626310ad9a77SSam Leffler * calculate roundtrip time. We submit a beacon frame 626410ad9a77SSam Leffler * below after any timer adjustment. The frame goes out 626510ad9a77SSam Leffler * at the next TBTT so the sender can calculate the 626610ad9a77SSam Leffler * roundtrip by inspecting the tdma ie in our beacon frame. 626710ad9a77SSam Leffler * 626810ad9a77SSam Leffler * NB: This tstamp is subtlely preserved when 626910ad9a77SSam Leffler * IEEE80211_BEACON_TDMA is marked (e.g. when the 627010ad9a77SSam Leffler * slot position changes) because ieee80211_add_tdma 627110ad9a77SSam Leffler * skips over the data. 627210ad9a77SSam Leffler */ 627310ad9a77SSam Leffler memcpy(ATH_VAP(vap)->av_boff.bo_tdma + 627410ad9a77SSam Leffler __offsetof(struct ieee80211_tdma_param, tdma_tstamp), 627510ad9a77SSam Leffler &ni->ni_tstamp.data, 8); 627610ad9a77SSam Leffler #if 0 627710ad9a77SSam Leffler DPRINTF(sc, ATH_DEBUG_TDMA_TIMER, 627810ad9a77SSam Leffler "tsf %llu nextslot %llu (%d, %d) nextslottu %u timer0 %u (%d)\n", 627910ad9a77SSam Leffler (unsigned long long) tsf, (unsigned long long) nextslot, 628010ad9a77SSam Leffler (int)(nextslot - tsf), tsfdelta, 628110ad9a77SSam Leffler nextslottu, timer0, tudelta); 628210ad9a77SSam Leffler #endif 628310ad9a77SSam Leffler /* 628410ad9a77SSam Leffler * Adjust the beacon timers only when pulling them forward 628510ad9a77SSam Leffler * or when going back by less than the beacon interval. 628610ad9a77SSam Leffler * Negative jumps larger than the beacon interval seem to 628710ad9a77SSam Leffler * cause the timers to stop and generally cause instability. 628810ad9a77SSam Leffler * This basically filters out jumps due to missed beacons. 628910ad9a77SSam Leffler */ 629010ad9a77SSam Leffler if (tudelta != 0 && (tudelta > 0 || -tudelta < sc->sc_tdmabintval)) { 629110ad9a77SSam Leffler ath_tdma_settimers(sc, nextslottu, sc->sc_tdmabintval); 629210ad9a77SSam Leffler sc->sc_stats.ast_tdma_timers++; 629310ad9a77SSam Leffler } 629410ad9a77SSam Leffler if (tsfdelta > 0) { 629510ad9a77SSam Leffler ath_hal_adjusttsf(ah, tsfdelta); 629610ad9a77SSam Leffler sc->sc_stats.ast_tdma_tsf++; 629710ad9a77SSam Leffler } 629810ad9a77SSam Leffler ath_tdma_beacon_send(sc, vap); /* prepare response */ 629910ad9a77SSam Leffler #undef TU_TO_TSF 630010ad9a77SSam Leffler #undef TSF_TO_TU 630110ad9a77SSam Leffler } 630210ad9a77SSam Leffler 630310ad9a77SSam Leffler /* 630410ad9a77SSam Leffler * Transmit a beacon frame at SWBA. Dynamic updates 630510ad9a77SSam Leffler * to the frame contents are done as needed. 630610ad9a77SSam Leffler */ 630710ad9a77SSam Leffler static void 630810ad9a77SSam Leffler ath_tdma_beacon_send(struct ath_softc *sc, struct ieee80211vap *vap) 630910ad9a77SSam Leffler { 631010ad9a77SSam Leffler struct ath_hal *ah = sc->sc_ah; 631110ad9a77SSam Leffler struct ath_buf *bf; 631210ad9a77SSam Leffler int otherant; 631310ad9a77SSam Leffler 631410ad9a77SSam Leffler /* 631510ad9a77SSam Leffler * Check if the previous beacon has gone out. If 631610ad9a77SSam Leffler * not don't try to post another, skip this period 631710ad9a77SSam Leffler * and wait for the next. Missed beacons indicate 631810ad9a77SSam Leffler * a problem and should not occur. If we miss too 631910ad9a77SSam Leffler * many consecutive beacons reset the device. 632010ad9a77SSam Leffler */ 632110ad9a77SSam Leffler if (ath_hal_numtxpending(ah, sc->sc_bhalq) != 0) { 632210ad9a77SSam Leffler sc->sc_bmisscount++; 632310ad9a77SSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON, 632410ad9a77SSam Leffler "%s: missed %u consecutive beacons\n", 632510ad9a77SSam Leffler __func__, sc->sc_bmisscount); 6326a32ac9d3SSam Leffler if (sc->sc_bmisscount >= ath_bstuck_threshold) 632710ad9a77SSam Leffler taskqueue_enqueue(sc->sc_tq, &sc->sc_bstucktask); 632810ad9a77SSam Leffler return; 632910ad9a77SSam Leffler } 633010ad9a77SSam Leffler if (sc->sc_bmisscount != 0) { 633110ad9a77SSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON, 633210ad9a77SSam Leffler "%s: resume beacon xmit after %u misses\n", 633310ad9a77SSam Leffler __func__, sc->sc_bmisscount); 633410ad9a77SSam Leffler sc->sc_bmisscount = 0; 633510ad9a77SSam Leffler } 633610ad9a77SSam Leffler 633710ad9a77SSam Leffler /* 633810ad9a77SSam Leffler * Check recent per-antenna transmit statistics and flip 633910ad9a77SSam Leffler * the default antenna if noticeably more frames went out 634010ad9a77SSam Leffler * on the non-default antenna. 634110ad9a77SSam Leffler * XXX assumes 2 anntenae 634210ad9a77SSam Leffler */ 634310ad9a77SSam Leffler if (!sc->sc_diversity) { 634410ad9a77SSam Leffler otherant = sc->sc_defant & 1 ? 2 : 1; 634510ad9a77SSam Leffler if (sc->sc_ant_tx[otherant] > sc->sc_ant_tx[sc->sc_defant] + 2) 634610ad9a77SSam Leffler ath_setdefantenna(sc, otherant); 634710ad9a77SSam Leffler sc->sc_ant_tx[1] = sc->sc_ant_tx[2] = 0; 634810ad9a77SSam Leffler } 634910ad9a77SSam Leffler 635010ad9a77SSam Leffler bf = ath_beacon_generate(sc, vap); 635110ad9a77SSam Leffler if (bf != NULL) { 635210ad9a77SSam Leffler /* 635310ad9a77SSam Leffler * Stop any current dma and put the new frame on the queue. 635410ad9a77SSam Leffler * This should never fail since we check above that no frames 635510ad9a77SSam Leffler * are still pending on the queue. 635610ad9a77SSam Leffler */ 635710ad9a77SSam Leffler if (!ath_hal_stoptxdma(ah, sc->sc_bhalq)) { 635810ad9a77SSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, 635910ad9a77SSam Leffler "%s: beacon queue %u did not stop?\n", 636010ad9a77SSam Leffler __func__, sc->sc_bhalq); 636110ad9a77SSam Leffler /* NB: the HAL still stops DMA, so proceed */ 636210ad9a77SSam Leffler } 636310ad9a77SSam Leffler ath_hal_puttxbuf(ah, sc->sc_bhalq, bf->bf_daddr); 636410ad9a77SSam Leffler ath_hal_txstart(ah, sc->sc_bhalq); 636510ad9a77SSam Leffler 636610ad9a77SSam Leffler sc->sc_stats.ast_be_xmit++; /* XXX per-vap? */ 636710ad9a77SSam Leffler 636810ad9a77SSam Leffler /* 636910ad9a77SSam Leffler * Record local TSF for our last send for use 637010ad9a77SSam Leffler * in arbitrating slot collisions. 637110ad9a77SSam Leffler */ 637210ad9a77SSam Leffler vap->iv_bss->ni_tstamp.tsf = ath_hal_gettsf64(ah); 637310ad9a77SSam Leffler } 637410ad9a77SSam Leffler } 6375584f7327SSam Leffler #endif /* IEEE80211_SUPPORT_TDMA */ 6376e8dabfbeSAdrian Chadd 6377e8dabfbeSAdrian Chadd static int 6378e8dabfbeSAdrian Chadd ath_sysctl_clearstats(SYSCTL_HANDLER_ARGS) 6379e8dabfbeSAdrian Chadd { 6380e8dabfbeSAdrian Chadd struct ath_softc *sc = arg1; 6381e8dabfbeSAdrian Chadd int val = 0; 6382e8dabfbeSAdrian Chadd int error; 6383e8dabfbeSAdrian Chadd 6384e8dabfbeSAdrian Chadd error = sysctl_handle_int(oidp, &val, 0, req); 6385e8dabfbeSAdrian Chadd if (error || !req->newptr) 6386e8dabfbeSAdrian Chadd return error; 6387e8dabfbeSAdrian Chadd if (val == 0) 6388e8dabfbeSAdrian Chadd return 0; /* Not clearing the stats is still valid */ 6389e8dabfbeSAdrian Chadd memset(&sc->sc_stats, 0, sizeof(sc->sc_stats)); 6390e8dabfbeSAdrian Chadd val = 0; 6391e8dabfbeSAdrian Chadd return 0; 6392e8dabfbeSAdrian Chadd } 6393e8dabfbeSAdrian Chadd 6394e8dabfbeSAdrian Chadd static void 6395ec165c61SAdrian Chadd ath_sysctl_stats_attach_rxphyerr(struct ath_softc *sc, struct sysctl_oid_list *parent) 6396ec165c61SAdrian Chadd { 6397ec165c61SAdrian Chadd struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); 6398ec165c61SAdrian Chadd struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); 6399ec165c61SAdrian Chadd struct sysctl_oid_list *child = SYSCTL_CHILDREN(tree); 6400ec165c61SAdrian Chadd int i; 6401ec165c61SAdrian Chadd char sn[8]; 6402ec165c61SAdrian Chadd 6403ec165c61SAdrian Chadd tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "rx_phy_err", CTLFLAG_RD, NULL, "Per-code RX PHY Errors"); 6404ec165c61SAdrian Chadd child = SYSCTL_CHILDREN(tree); 6405ec165c61SAdrian Chadd for (i = 0; i < 32; i++) { 6406ec165c61SAdrian Chadd snprintf(sn, sizeof(sn), "%d", i); 6407ec165c61SAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, sn, CTLFLAG_RD, &sc->sc_stats.ast_rx_phy[i], 0, ""); 6408ec165c61SAdrian Chadd } 6409ec165c61SAdrian Chadd } 6410ec165c61SAdrian Chadd 6411ec165c61SAdrian Chadd static void 6412e8dabfbeSAdrian Chadd ath_sysctl_stats_attach(struct ath_softc *sc) 6413e8dabfbeSAdrian Chadd { 6414e8dabfbeSAdrian Chadd struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); 6415e8dabfbeSAdrian Chadd struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); 6416e8dabfbeSAdrian Chadd struct sysctl_oid_list *child = SYSCTL_CHILDREN(tree); 6417e8dabfbeSAdrian Chadd 6418e8dabfbeSAdrian Chadd /* Create "clear" node */ 6419e8dabfbeSAdrian Chadd SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6420e8dabfbeSAdrian Chadd "clear_stats", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 6421e8dabfbeSAdrian Chadd ath_sysctl_clearstats, "I", "clear stats"); 6422e8dabfbeSAdrian Chadd 6423e8dabfbeSAdrian Chadd /* Create stats node */ 6424e8dabfbeSAdrian Chadd tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", CTLFLAG_RD, 6425e8dabfbeSAdrian Chadd NULL, "Statistics"); 6426e8dabfbeSAdrian Chadd child = SYSCTL_CHILDREN(tree); 6427e8dabfbeSAdrian Chadd 6428e8dabfbeSAdrian Chadd /* This was generated from if_athioctl.h */ 6429e8dabfbeSAdrian Chadd 6430e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_watchdog", CTLFLAG_RD, 6431e8dabfbeSAdrian Chadd &sc->sc_stats.ast_watchdog, 0, "device reset by watchdog"); 6432e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_hardware", CTLFLAG_RD, 6433e8dabfbeSAdrian Chadd &sc->sc_stats.ast_hardware, 0, "fatal hardware error interrupts"); 6434e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_bmiss", CTLFLAG_RD, 6435e8dabfbeSAdrian Chadd &sc->sc_stats.ast_bmiss, 0, "beacon miss interrupts"); 6436e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_bmiss_phantom", CTLFLAG_RD, 6437e8dabfbeSAdrian Chadd &sc->sc_stats.ast_bmiss_phantom, 0, "beacon miss interrupts"); 6438e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_bstuck", CTLFLAG_RD, 6439e8dabfbeSAdrian Chadd &sc->sc_stats.ast_bstuck, 0, "beacon stuck interrupts"); 6440e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rxorn", CTLFLAG_RD, 6441e8dabfbeSAdrian Chadd &sc->sc_stats.ast_rxorn, 0, "rx overrun interrupts"); 6442e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rxeol", CTLFLAG_RD, 6443e8dabfbeSAdrian Chadd &sc->sc_stats.ast_rxeol, 0, "rx eol interrupts"); 6444e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_txurn", CTLFLAG_RD, 6445e8dabfbeSAdrian Chadd &sc->sc_stats.ast_txurn, 0, "tx underrun interrupts"); 6446e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_mib", CTLFLAG_RD, 6447e8dabfbeSAdrian Chadd &sc->sc_stats.ast_mib, 0, "mib interrupts"); 6448e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_intrcoal", CTLFLAG_RD, 6449e8dabfbeSAdrian Chadd &sc->sc_stats.ast_intrcoal, 0, "interrupts coalesced"); 6450e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_packets", CTLFLAG_RD, 6451e8dabfbeSAdrian Chadd &sc->sc_stats.ast_tx_packets, 0, "packet sent on the interface"); 6452e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_mgmt", CTLFLAG_RD, 6453e8dabfbeSAdrian Chadd &sc->sc_stats.ast_tx_mgmt, 0, "management frames transmitted"); 6454e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_discard", CTLFLAG_RD, 6455e8dabfbeSAdrian Chadd &sc->sc_stats.ast_tx_discard, 0, "frames discarded prior to assoc"); 6456e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_qstop", CTLFLAG_RD, 6457e8dabfbeSAdrian Chadd &sc->sc_stats.ast_tx_qstop, 0, "output stopped 'cuz no buffer"); 6458e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_encap", CTLFLAG_RD, 6459e8dabfbeSAdrian Chadd &sc->sc_stats.ast_tx_encap, 0, "tx encapsulation failed"); 6460e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_nonode", CTLFLAG_RD, 6461e8dabfbeSAdrian Chadd &sc->sc_stats.ast_tx_nonode, 0, "tx failed 'cuz no node"); 6462e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_nombuf", CTLFLAG_RD, 6463e8dabfbeSAdrian Chadd &sc->sc_stats.ast_tx_nombuf, 0, "tx failed 'cuz no mbuf"); 6464e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_nomcl", CTLFLAG_RD, 6465e8dabfbeSAdrian Chadd &sc->sc_stats.ast_tx_nomcl, 0, "tx failed 'cuz no cluster"); 6466e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_linear", CTLFLAG_RD, 6467e8dabfbeSAdrian Chadd &sc->sc_stats.ast_tx_linear, 0, "tx linearized to cluster"); 6468e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_nodata", CTLFLAG_RD, 6469e8dabfbeSAdrian Chadd &sc->sc_stats.ast_tx_nodata, 0, "tx discarded empty frame"); 6470e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_busdma", CTLFLAG_RD, 6471e8dabfbeSAdrian Chadd &sc->sc_stats.ast_tx_busdma, 0, "tx failed for dma resrcs"); 6472e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_xretries", CTLFLAG_RD, 6473e8dabfbeSAdrian Chadd &sc->sc_stats.ast_tx_xretries, 0, "tx failed 'cuz too many retries"); 6474e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_fifoerr", CTLFLAG_RD, 6475e8dabfbeSAdrian Chadd &sc->sc_stats.ast_tx_fifoerr, 0, "tx failed 'cuz FIFO underrun"); 6476e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_filtered", CTLFLAG_RD, 6477e8dabfbeSAdrian Chadd &sc->sc_stats.ast_tx_filtered, 0, "tx failed 'cuz xmit filtered"); 6478e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_shortretry", CTLFLAG_RD, 6479e8dabfbeSAdrian Chadd &sc->sc_stats.ast_tx_shortretry, 0, "tx on-chip retries (short)"); 6480e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_longretry", CTLFLAG_RD, 6481e8dabfbeSAdrian Chadd &sc->sc_stats.ast_tx_longretry, 0, "tx on-chip retries (long)"); 6482e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_badrate", CTLFLAG_RD, 6483e8dabfbeSAdrian Chadd &sc->sc_stats.ast_tx_badrate, 0, "tx failed 'cuz bogus xmit rate"); 6484e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_noack", CTLFLAG_RD, 6485e8dabfbeSAdrian Chadd &sc->sc_stats.ast_tx_noack, 0, "tx frames with no ack marked"); 6486e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_rts", CTLFLAG_RD, 6487e8dabfbeSAdrian Chadd &sc->sc_stats.ast_tx_rts, 0, "tx frames with rts enabled"); 6488e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_cts", CTLFLAG_RD, 6489e8dabfbeSAdrian Chadd &sc->sc_stats.ast_tx_cts, 0, "tx frames with cts enabled"); 6490e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_shortpre", CTLFLAG_RD, 6491e8dabfbeSAdrian Chadd &sc->sc_stats.ast_tx_shortpre, 0, "tx frames with short preamble"); 6492e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_altrate", CTLFLAG_RD, 6493e8dabfbeSAdrian Chadd &sc->sc_stats.ast_tx_altrate, 0, "tx frames with alternate rate"); 6494e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_protect", CTLFLAG_RD, 6495e8dabfbeSAdrian Chadd &sc->sc_stats.ast_tx_protect, 0, "tx frames with protection"); 6496e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_ctsburst", CTLFLAG_RD, 6497e8dabfbeSAdrian Chadd &sc->sc_stats.ast_tx_ctsburst, 0, "tx frames with cts and bursting"); 6498e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_ctsext", CTLFLAG_RD, 6499e8dabfbeSAdrian Chadd &sc->sc_stats.ast_tx_ctsext, 0, "tx frames with cts extension"); 6500e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_nombuf", CTLFLAG_RD, 6501e8dabfbeSAdrian Chadd &sc->sc_stats.ast_rx_nombuf, 0, "rx setup failed 'cuz no mbuf"); 6502e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_busdma", CTLFLAG_RD, 6503e8dabfbeSAdrian Chadd &sc->sc_stats.ast_rx_busdma, 0, "rx setup failed for dma resrcs"); 6504e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_orn", CTLFLAG_RD, 6505e8dabfbeSAdrian Chadd &sc->sc_stats.ast_rx_orn, 0, "rx failed 'cuz of desc overrun"); 6506e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_crcerr", CTLFLAG_RD, 6507e8dabfbeSAdrian Chadd &sc->sc_stats.ast_rx_crcerr, 0, "rx failed 'cuz of bad CRC"); 6508e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_fifoerr", CTLFLAG_RD, 6509e8dabfbeSAdrian Chadd &sc->sc_stats.ast_rx_fifoerr, 0, "rx failed 'cuz of FIFO overrun"); 6510e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_badcrypt", CTLFLAG_RD, 6511e8dabfbeSAdrian Chadd &sc->sc_stats.ast_rx_badcrypt, 0, "rx failed 'cuz decryption"); 6512e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_badmic", CTLFLAG_RD, 6513e8dabfbeSAdrian Chadd &sc->sc_stats.ast_rx_badmic, 0, "rx failed 'cuz MIC failure"); 6514e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_phyerr", CTLFLAG_RD, 6515e8dabfbeSAdrian Chadd &sc->sc_stats.ast_rx_phyerr, 0, "rx failed 'cuz of PHY err"); 6516e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_tooshort", CTLFLAG_RD, 6517e8dabfbeSAdrian Chadd &sc->sc_stats.ast_rx_tooshort, 0, "rx discarded 'cuz frame too short"); 6518e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_toobig", CTLFLAG_RD, 6519e8dabfbeSAdrian Chadd &sc->sc_stats.ast_rx_toobig, 0, "rx discarded 'cuz frame too large"); 6520e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_packets", CTLFLAG_RD, 6521e8dabfbeSAdrian Chadd &sc->sc_stats.ast_rx_packets, 0, "packet recv on the interface"); 6522e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_mgt", CTLFLAG_RD, 6523e8dabfbeSAdrian Chadd &sc->sc_stats.ast_rx_mgt, 0, "management frames received"); 6524e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_ctl", CTLFLAG_RD, 6525e8dabfbeSAdrian Chadd &sc->sc_stats.ast_rx_ctl, 0, "rx discarded 'cuz ctl frame"); 6526e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_be_xmit", CTLFLAG_RD, 6527e8dabfbeSAdrian Chadd &sc->sc_stats.ast_be_xmit, 0, "beacons transmitted"); 6528e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_be_nombuf", CTLFLAG_RD, 6529e8dabfbeSAdrian Chadd &sc->sc_stats.ast_be_nombuf, 0, "beacon setup failed 'cuz no mbuf"); 6530e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_per_cal", CTLFLAG_RD, 6531e8dabfbeSAdrian Chadd &sc->sc_stats.ast_per_cal, 0, "periodic calibration calls"); 6532e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_per_calfail", CTLFLAG_RD, 6533e8dabfbeSAdrian Chadd &sc->sc_stats.ast_per_calfail, 0, "periodic calibration failed"); 6534e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_per_rfgain", CTLFLAG_RD, 6535e8dabfbeSAdrian Chadd &sc->sc_stats.ast_per_rfgain, 0, "periodic calibration rfgain reset"); 6536e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rate_calls", CTLFLAG_RD, 6537e8dabfbeSAdrian Chadd &sc->sc_stats.ast_rate_calls, 0, "rate control checks"); 6538e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rate_raise", CTLFLAG_RD, 6539e8dabfbeSAdrian Chadd &sc->sc_stats.ast_rate_raise, 0, "rate control raised xmit rate"); 6540e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rate_drop", CTLFLAG_RD, 6541e8dabfbeSAdrian Chadd &sc->sc_stats.ast_rate_drop, 0, "rate control dropped xmit rate"); 6542e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_ant_defswitch", CTLFLAG_RD, 6543e8dabfbeSAdrian Chadd &sc->sc_stats.ast_ant_defswitch, 0, "rx/default antenna switches"); 6544e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_ant_txswitch", CTLFLAG_RD, 6545e8dabfbeSAdrian Chadd &sc->sc_stats.ast_ant_txswitch, 0, "tx antenna switches"); 6546e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_cabq_xmit", CTLFLAG_RD, 6547e8dabfbeSAdrian Chadd &sc->sc_stats.ast_cabq_xmit, 0, "cabq frames transmitted"); 6548e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_cabq_busy", CTLFLAG_RD, 6549e8dabfbeSAdrian Chadd &sc->sc_stats.ast_cabq_busy, 0, "cabq found busy"); 6550e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_raw", CTLFLAG_RD, 6551e8dabfbeSAdrian Chadd &sc->sc_stats.ast_tx_raw, 0, "tx frames through raw api"); 6552e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_ff_txok", CTLFLAG_RD, 6553e8dabfbeSAdrian Chadd &sc->sc_stats.ast_ff_txok, 0, "fast frames tx'd successfully"); 6554e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_ff_txerr", CTLFLAG_RD, 6555e8dabfbeSAdrian Chadd &sc->sc_stats.ast_ff_txerr, 0, "fast frames tx'd w/ error"); 6556e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_ff_rx", CTLFLAG_RD, 6557e8dabfbeSAdrian Chadd &sc->sc_stats.ast_ff_rx, 0, "fast frames rx'd"); 6558e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_ff_flush", CTLFLAG_RD, 6559e8dabfbeSAdrian Chadd &sc->sc_stats.ast_ff_flush, 0, "fast frames flushed from staging q"); 6560e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_qfull", CTLFLAG_RD, 6561e8dabfbeSAdrian Chadd &sc->sc_stats.ast_tx_qfull, 0, "tx dropped 'cuz of queue limit"); 6562e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_nobuf", CTLFLAG_RD, 6563e8dabfbeSAdrian Chadd &sc->sc_stats.ast_tx_nobuf, 0, "tx dropped 'cuz no ath buffer"); 6564e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tdma_update", CTLFLAG_RD, 6565e8dabfbeSAdrian Chadd &sc->sc_stats.ast_tdma_update, 0, "TDMA slot timing updates"); 6566e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tdma_timers", CTLFLAG_RD, 6567e8dabfbeSAdrian Chadd &sc->sc_stats.ast_tdma_timers, 0, "TDMA slot update set beacon timers"); 6568e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tdma_tsf", CTLFLAG_RD, 6569e8dabfbeSAdrian Chadd &sc->sc_stats.ast_tdma_tsf, 0, "TDMA slot update set TSF"); 6570e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tdma_ack", CTLFLAG_RD, 6571e8dabfbeSAdrian Chadd &sc->sc_stats.ast_tdma_ack, 0, "TDMA tx failed 'cuz ACK required"); 6572e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_raw_fail", CTLFLAG_RD, 6573e8dabfbeSAdrian Chadd &sc->sc_stats.ast_tx_raw_fail, 0, "raw tx failed 'cuz h/w down"); 6574e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_nofrag", CTLFLAG_RD, 6575e8dabfbeSAdrian Chadd &sc->sc_stats.ast_tx_nofrag, 0, "tx dropped 'cuz no ath frag buffer"); 6576e8dabfbeSAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_be_missed", CTLFLAG_RD, 6577e8dabfbeSAdrian Chadd &sc->sc_stats.ast_be_missed, 0, "number of -missed- beacons"); 6578a108ab63SAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_ani_cal", CTLFLAG_RD, 6579a108ab63SAdrian Chadd &sc->sc_stats.ast_ani_cal, 0, "number of ANI polls"); 6580f673a810SAdrian Chadd SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_agg", CTLFLAG_RD, 6581f673a810SAdrian Chadd &sc->sc_stats.ast_rx_agg, 0, "number of aggregate frames received"); 6582ec165c61SAdrian Chadd 6583ec165c61SAdrian Chadd /* Attach the RX phy error array */ 6584ec165c61SAdrian Chadd ath_sysctl_stats_attach_rxphyerr(sc, child); 6585e8dabfbeSAdrian Chadd } 6586