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" 423f3087fdSAdrian Chadd /* 433f3087fdSAdrian Chadd * This is needed for register operations which are performed 443f3087fdSAdrian Chadd * by the driver - eg, calls to ath_hal_gettsf32(). 453f3087fdSAdrian Chadd */ 463f3087fdSAdrian Chadd #include "opt_ah.h" 47584f7327SSam Leffler #include "opt_wlan.h" 485591b213SSam Leffler 495591b213SSam Leffler #include <sys/param.h> 505591b213SSam Leffler #include <sys/systm.h> 515591b213SSam Leffler #include <sys/sysctl.h> 525591b213SSam Leffler #include <sys/mbuf.h> 535591b213SSam Leffler #include <sys/malloc.h> 545591b213SSam Leffler #include <sys/lock.h> 555591b213SSam Leffler #include <sys/mutex.h> 565591b213SSam Leffler #include <sys/kernel.h> 575591b213SSam Leffler #include <sys/socket.h> 585591b213SSam Leffler #include <sys/sockio.h> 595591b213SSam Leffler #include <sys/errno.h> 605591b213SSam Leffler #include <sys/callout.h> 615591b213SSam Leffler #include <sys/bus.h> 625591b213SSam Leffler #include <sys/endian.h> 630bbf5441SSam Leffler #include <sys/kthread.h> 640bbf5441SSam Leffler #include <sys/taskqueue.h> 653fc21fedSSam Leffler #include <sys/priv.h> 66dba9c859SAdrian Chadd #include <sys/module.h> 67f52d3452SAdrian Chadd #include <sys/ktr.h> 685591b213SSam Leffler 695591b213SSam Leffler #include <machine/bus.h> 705591b213SSam Leffler 715591b213SSam Leffler #include <net/if.h> 725591b213SSam Leffler #include <net/if_dl.h> 735591b213SSam Leffler #include <net/if_media.h> 74fc74a9f9SBrooks Davis #include <net/if_types.h> 755591b213SSam Leffler #include <net/if_arp.h> 765591b213SSam Leffler #include <net/ethernet.h> 775591b213SSam Leffler #include <net/if_llc.h> 785591b213SSam Leffler 795591b213SSam Leffler #include <net80211/ieee80211_var.h> 8059efa8b5SSam Leffler #include <net80211/ieee80211_regdomain.h> 81339ccfb3SSam Leffler #ifdef IEEE80211_SUPPORT_SUPERG 82339ccfb3SSam Leffler #include <net80211/ieee80211_superg.h> 83339ccfb3SSam Leffler #endif 84584f7327SSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 8510ad9a77SSam Leffler #include <net80211/ieee80211_tdma.h> 8610ad9a77SSam Leffler #endif 875591b213SSam Leffler 885591b213SSam Leffler #include <net/bpf.h> 895591b213SSam Leffler 905591b213SSam Leffler #ifdef INET 915591b213SSam Leffler #include <netinet/in.h> 925591b213SSam Leffler #include <netinet/if_ether.h> 935591b213SSam Leffler #endif 945591b213SSam Leffler 955591b213SSam Leffler #include <dev/ath/if_athvar.h> 9633644623SSam Leffler #include <dev/ath/ath_hal/ah_devid.h> /* XXX for softled */ 970dbe9289SAdrian Chadd #include <dev/ath/ath_hal/ah_diagcodes.h> 985591b213SSam Leffler 995bc8125aSAdrian Chadd #include <dev/ath/if_ath_debug.h> 100b8e788a5SAdrian Chadd #include <dev/ath/if_ath_misc.h> 101b8e788a5SAdrian Chadd #include <dev/ath/if_ath_tx.h> 1026079fdbeSAdrian Chadd #include <dev/ath/if_ath_sysctl.h> 103d2d7a00aSAdrian Chadd #include <dev/ath/if_ath_keycache.h> 10448237774SAdrian Chadd #include <dev/ath/if_athdfs.h> 1055bc8125aSAdrian Chadd 10686e07743SSam Leffler #ifdef ATH_TX99_DIAG 10786e07743SSam Leffler #include <dev/ath/ath_tx99/ath_tx99.h> 10886e07743SSam Leffler #endif 10986e07743SSam Leffler 110f52d3452SAdrian Chadd #define ATH_KTR_INTR KTR_SPARE4 111f52d3452SAdrian Chadd #define ATH_KTR_ERR KTR_SPARE3 11248237774SAdrian Chadd 113b032f27cSSam Leffler /* 114b032f27cSSam Leffler * ATH_BCBUF determines the number of vap's that can transmit 115b032f27cSSam Leffler * beacons and also (currently) the number of vap's that can 116b032f27cSSam Leffler * have unique mac addresses/bssid. When staggering beacons 117b032f27cSSam Leffler * 4 is probably a good max as otherwise the beacons become 118b032f27cSSam Leffler * very closely spaced and there is limited time for cab q traffic 119b032f27cSSam Leffler * to go out. You can burst beacons instead but that is not good 120b032f27cSSam Leffler * for stations in power save and at some point you really want 121b032f27cSSam Leffler * another radio (and channel). 122b032f27cSSam Leffler * 123b032f27cSSam Leffler * The limit on the number of mac addresses is tied to our use of 124b032f27cSSam Leffler * the U/L bit and tracking addresses in a byte; it would be 125b032f27cSSam Leffler * worthwhile to allow more for applications like proxy sta. 126b032f27cSSam Leffler */ 127b032f27cSSam Leffler CTASSERT(ATH_BCBUF <= 8); 128b032f27cSSam Leffler 129b032f27cSSam Leffler static struct ieee80211vap *ath_vap_create(struct ieee80211com *, 130b032f27cSSam Leffler const char name[IFNAMSIZ], int unit, int opmode, 131b032f27cSSam Leffler int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], 132b032f27cSSam Leffler const uint8_t mac[IEEE80211_ADDR_LEN]); 133b032f27cSSam Leffler static void ath_vap_delete(struct ieee80211vap *); 1345591b213SSam Leffler static void ath_init(void *); 135c42a7b7eSSam Leffler static void ath_stop_locked(struct ifnet *); 1365591b213SSam Leffler static void ath_stop(struct ifnet *); 1375591b213SSam Leffler static void ath_start(struct ifnet *); 138b032f27cSSam Leffler static int ath_reset_vap(struct ieee80211vap *, u_long); 1395591b213SSam Leffler static int ath_media_change(struct ifnet *); 1402e986da5SSam Leffler static void ath_watchdog(void *); 1415591b213SSam Leffler static int ath_ioctl(struct ifnet *, u_long, caddr_t); 1425591b213SSam Leffler static void ath_fatal_proc(void *, int); 143b032f27cSSam Leffler static void ath_bmiss_vap(struct ieee80211vap *); 1445591b213SSam Leffler static void ath_bmiss_proc(void *, int); 145b032f27cSSam Leffler static void ath_key_update_begin(struct ieee80211vap *); 146b032f27cSSam Leffler static void ath_key_update_end(struct ieee80211vap *); 147b032f27cSSam Leffler static void ath_update_mcast(struct ifnet *); 148b032f27cSSam Leffler static void ath_update_promisc(struct ifnet *); 1495591b213SSam Leffler static void ath_mode_init(struct ath_softc *); 150c42a7b7eSSam Leffler static void ath_setslottime(struct ath_softc *); 151c42a7b7eSSam Leffler static void ath_updateslot(struct ifnet *); 15280d2765fSSam Leffler static int ath_beaconq_setup(struct ath_hal *); 1535591b213SSam Leffler static int ath_beacon_alloc(struct ath_softc *, struct ieee80211_node *); 154b032f27cSSam Leffler static void ath_beacon_update(struct ieee80211vap *, int item); 155c42a7b7eSSam Leffler static void ath_beacon_setup(struct ath_softc *, struct ath_buf *); 1565591b213SSam Leffler static void ath_beacon_proc(void *, int); 157b032f27cSSam Leffler static struct ath_buf *ath_beacon_generate(struct ath_softc *, 158b032f27cSSam Leffler struct ieee80211vap *); 159c42a7b7eSSam Leffler static void ath_bstuck_proc(void *, int); 160b032f27cSSam Leffler static void ath_beacon_return(struct ath_softc *, struct ath_buf *); 1615591b213SSam Leffler static void ath_beacon_free(struct ath_softc *); 162b032f27cSSam Leffler static void ath_beacon_config(struct ath_softc *, struct ieee80211vap *); 163c42a7b7eSSam Leffler static void ath_descdma_cleanup(struct ath_softc *sc, 164c42a7b7eSSam Leffler struct ath_descdma *, ath_bufhead *); 1655591b213SSam Leffler static int ath_desc_alloc(struct ath_softc *); 1665591b213SSam Leffler static void ath_desc_free(struct ath_softc *); 16738c208f8SSam Leffler static struct ieee80211_node *ath_node_alloc(struct ieee80211vap *, 16838c208f8SSam Leffler const uint8_t [IEEE80211_ADDR_LEN]); 1694afa805eSAdrian Chadd static void ath_node_cleanup(struct ieee80211_node *); 170c42a7b7eSSam Leffler static void ath_node_free(struct ieee80211_node *); 17168e8e04eSSam Leffler static void ath_node_getsignal(const struct ieee80211_node *, 17268e8e04eSSam Leffler int8_t *, int8_t *); 1735591b213SSam Leffler static int ath_rxbuf_init(struct ath_softc *, struct ath_buf *); 174b032f27cSSam Leffler static void ath_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, 1755463c4a4SSam Leffler int subtype, int rssi, int nf); 176c42a7b7eSSam Leffler static void ath_setdefantenna(struct ath_softc *, u_int); 17796ff485dSAdrian Chadd static void ath_rx_proc(struct ath_softc *sc, int); 17896ff485dSAdrian Chadd static void ath_rx_tasklet(void *, int); 179622b3fd2SSam Leffler static void ath_txq_init(struct ath_softc *sc, struct ath_txq *, int); 180c42a7b7eSSam Leffler static struct ath_txq *ath_txq_setup(struct ath_softc*, int qtype, int subtype); 181c42a7b7eSSam Leffler static int ath_tx_setup(struct ath_softc *, int, int); 182c42a7b7eSSam Leffler static int ath_wme_update(struct ieee80211com *); 183c42a7b7eSSam Leffler static void ath_tx_cleanupq(struct ath_softc *, struct ath_txq *); 184c42a7b7eSSam Leffler static void ath_tx_cleanup(struct ath_softc *); 185c42a7b7eSSam Leffler static void ath_tx_proc_q0(void *, int); 186c42a7b7eSSam Leffler static void ath_tx_proc_q0123(void *, int); 1875591b213SSam Leffler static void ath_tx_proc(void *, int); 188b032f27cSSam Leffler static void ath_tx_draintxq(struct ath_softc *, struct ath_txq *); 1895591b213SSam Leffler static int ath_chan_set(struct ath_softc *, struct ieee80211_channel *); 190517526efSAdrian Chadd static void ath_draintxq(struct ath_softc *, ATH_RESET_TYPE reset_type); 1915591b213SSam Leffler static void ath_stoprecv(struct ath_softc *); 1925591b213SSam Leffler static int ath_startrecv(struct ath_softc *); 193c42a7b7eSSam Leffler static void ath_chan_change(struct ath_softc *, struct ieee80211_channel *); 19468e8e04eSSam Leffler static void ath_scan_start(struct ieee80211com *); 19568e8e04eSSam Leffler static void ath_scan_end(struct ieee80211com *); 19668e8e04eSSam Leffler static void ath_set_channel(struct ieee80211com *); 1975591b213SSam Leffler static void ath_calibrate(void *); 198b032f27cSSam Leffler static int ath_newstate(struct ieee80211vap *, enum ieee80211_state, int); 199e8fd88a3SSam Leffler static void ath_setup_stationkey(struct ieee80211_node *); 200e9962332SSam Leffler static void ath_newassoc(struct ieee80211_node *, int); 201b032f27cSSam Leffler static int ath_setregdomain(struct ieee80211com *, 202b032f27cSSam Leffler struct ieee80211_regdomain *, int, 203b032f27cSSam Leffler struct ieee80211_channel []); 2045fe9f044SSam Leffler static void ath_getradiocaps(struct ieee80211com *, int, int *, 205b032f27cSSam Leffler struct ieee80211_channel []); 206b032f27cSSam Leffler static int ath_getchannels(struct ath_softc *); 2073e50ec2cSSam Leffler static void ath_led_event(struct ath_softc *, int); 2085591b213SSam Leffler 209c42a7b7eSSam Leffler static int ath_rate_setup(struct ath_softc *, u_int mode); 2105591b213SSam Leffler static void ath_setcurmode(struct ath_softc *, enum ieee80211_phymode); 211c42a7b7eSSam Leffler 212c42a7b7eSSam Leffler static void ath_announce(struct ath_softc *); 2135591b213SSam Leffler 21448237774SAdrian Chadd static void ath_dfs_tasklet(void *, int); 21548237774SAdrian Chadd 216584f7327SSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 21710ad9a77SSam Leffler static void ath_tdma_settimers(struct ath_softc *sc, u_int32_t nexttbtt, 21810ad9a77SSam Leffler u_int32_t bintval); 21910ad9a77SSam Leffler static void ath_tdma_bintvalsetup(struct ath_softc *sc, 22010ad9a77SSam Leffler const struct ieee80211_tdma_state *tdma); 22110ad9a77SSam Leffler static void ath_tdma_config(struct ath_softc *sc, struct ieee80211vap *vap); 22210ad9a77SSam Leffler static void ath_tdma_update(struct ieee80211_node *ni, 2232bc3ce77SSam Leffler const struct ieee80211_tdma_param *tdma, int); 22410ad9a77SSam Leffler static void ath_tdma_beacon_send(struct ath_softc *sc, 22510ad9a77SSam Leffler struct ieee80211vap *vap); 22610ad9a77SSam Leffler 22710ad9a77SSam Leffler #define TDMA_EP_MULTIPLIER (1<<10) /* pow2 to optimize out * and / */ 22810ad9a77SSam Leffler #define TDMA_LPF_LEN 6 22910ad9a77SSam Leffler #define TDMA_DUMMY_MARKER 0x127 23010ad9a77SSam Leffler #define TDMA_EP_MUL(x, mul) ((x) * (mul)) 23110ad9a77SSam Leffler #define TDMA_IN(x) (TDMA_EP_MUL((x), TDMA_EP_MULTIPLIER)) 23210ad9a77SSam Leffler #define TDMA_LPF(x, y, len) \ 23310ad9a77SSam Leffler ((x != TDMA_DUMMY_MARKER) ? (((x) * ((len)-1) + (y)) / (len)) : (y)) 23410ad9a77SSam Leffler #define TDMA_SAMPLE(x, y) do { \ 23510ad9a77SSam Leffler x = TDMA_LPF((x), TDMA_IN(y), TDMA_LPF_LEN); \ 23610ad9a77SSam Leffler } while (0) 23710ad9a77SSam Leffler #define TDMA_EP_RND(x,mul) \ 23810ad9a77SSam Leffler ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul)) 23910ad9a77SSam Leffler #define TDMA_AVG(x) TDMA_EP_RND(x, TDMA_EP_MULTIPLIER) 240584f7327SSam Leffler #endif /* IEEE80211_SUPPORT_TDMA */ 24110ad9a77SSam Leffler 2425591b213SSam Leffler SYSCTL_DECL(_hw_ath); 2435591b213SSam Leffler 2445591b213SSam Leffler /* XXX validate sysctl values */ 2452dc7fcc4SSam Leffler static int ath_longcalinterval = 30; /* long cals every 30 secs */ 2462dc7fcc4SSam Leffler SYSCTL_INT(_hw_ath, OID_AUTO, longcal, CTLFLAG_RW, &ath_longcalinterval, 2472dc7fcc4SSam Leffler 0, "long chip calibration interval (secs)"); 2482dc7fcc4SSam Leffler static int ath_shortcalinterval = 100; /* short cals every 100 ms */ 2492dc7fcc4SSam Leffler SYSCTL_INT(_hw_ath, OID_AUTO, shortcal, CTLFLAG_RW, &ath_shortcalinterval, 2502dc7fcc4SSam Leffler 0, "short chip calibration interval (msecs)"); 2512dc7fcc4SSam Leffler static int ath_resetcalinterval = 20*60; /* reset cal state 20 mins */ 2522dc7fcc4SSam Leffler SYSCTL_INT(_hw_ath, OID_AUTO, resetcal, CTLFLAG_RW, &ath_resetcalinterval, 2532dc7fcc4SSam Leffler 0, "reset chip calibration results (secs)"); 254a108ab63SAdrian Chadd static int ath_anicalinterval = 100; /* ANI calibration - 100 msec */ 255a108ab63SAdrian Chadd SYSCTL_INT(_hw_ath, OID_AUTO, anical, CTLFLAG_RW, &ath_anicalinterval, 256a108ab63SAdrian Chadd 0, "ANI calibration (msecs)"); 2575591b213SSam Leffler 258e2d787faSSam Leffler static int ath_rxbuf = ATH_RXBUF; /* # rx buffers to allocate */ 259aaa70f2fSSam Leffler SYSCTL_INT(_hw_ath, OID_AUTO, rxbuf, CTLFLAG_RW, &ath_rxbuf, 260e2d787faSSam Leffler 0, "rx buffers allocated"); 261e2d787faSSam Leffler TUNABLE_INT("hw.ath.rxbuf", &ath_rxbuf); 262e2d787faSSam Leffler static int ath_txbuf = ATH_TXBUF; /* # tx buffers to allocate */ 263aaa70f2fSSam Leffler SYSCTL_INT(_hw_ath, OID_AUTO, txbuf, CTLFLAG_RW, &ath_txbuf, 264e2d787faSSam Leffler 0, "tx buffers allocated"); 265e2d787faSSam Leffler TUNABLE_INT("hw.ath.txbuf", &ath_txbuf); 266e2d787faSSam Leffler 267a32ac9d3SSam Leffler static int ath_bstuck_threshold = 4; /* max missed beacons */ 268a32ac9d3SSam Leffler SYSCTL_INT(_hw_ath, OID_AUTO, bstuck, CTLFLAG_RW, &ath_bstuck_threshold, 269a32ac9d3SSam Leffler 0, "max missed beacon xmits before chip reset"); 270a32ac9d3SSam Leffler 2716b349e5aSAdrian Chadd MALLOC_DEFINE(M_ATHDEV, "athdev", "ath driver dma buffers"); 272c42a7b7eSSam Leffler 27367397d39SAdrian Chadd #define HAL_MODE_HT20 (HAL_MODE_11NG_HT20 | HAL_MODE_11NA_HT20) 27467397d39SAdrian Chadd #define HAL_MODE_HT40 \ 27567397d39SAdrian Chadd (HAL_MODE_11NG_HT40PLUS | HAL_MODE_11NG_HT40MINUS | \ 27667397d39SAdrian Chadd HAL_MODE_11NA_HT40PLUS | HAL_MODE_11NA_HT40MINUS) 2775591b213SSam Leffler int 2785591b213SSam Leffler ath_attach(u_int16_t devid, struct ath_softc *sc) 2795591b213SSam Leffler { 280fc74a9f9SBrooks Davis struct ifnet *ifp; 281b032f27cSSam Leffler struct ieee80211com *ic; 282fc74a9f9SBrooks Davis struct ath_hal *ah = NULL; 2835591b213SSam Leffler HAL_STATUS status; 284c42a7b7eSSam Leffler int error = 0, i; 285411373ebSSam Leffler u_int wmodes; 28629aca940SSam Leffler uint8_t macaddr[IEEE80211_ADDR_LEN]; 2875591b213SSam Leffler 288c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: devid 0x%x\n", __func__, devid); 2895591b213SSam Leffler 290b032f27cSSam Leffler ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); 291fc74a9f9SBrooks Davis if (ifp == NULL) { 292fc74a9f9SBrooks Davis device_printf(sc->sc_dev, "can not if_alloc()\n"); 293fc74a9f9SBrooks Davis error = ENOSPC; 294fc74a9f9SBrooks Davis goto bad; 295fc74a9f9SBrooks Davis } 296b032f27cSSam Leffler ic = ifp->if_l2com; 297fc74a9f9SBrooks Davis 2985591b213SSam Leffler /* set these up early for if_printf use */ 2999bf40edeSBrooks Davis if_initname(ifp, device_get_name(sc->sc_dev), 3009bf40edeSBrooks Davis device_get_unit(sc->sc_dev)); 3015591b213SSam Leffler 30288117a53SAdrian Chadd ah = ath_hal_attach(devid, sc, sc->sc_st, sc->sc_sh, sc->sc_eepromdata, &status); 3035591b213SSam Leffler if (ah == NULL) { 3045591b213SSam Leffler if_printf(ifp, "unable to attach hardware; HAL status %u\n", 3055591b213SSam Leffler status); 3065591b213SSam Leffler error = ENXIO; 3075591b213SSam Leffler goto bad; 3085591b213SSam Leffler } 3095591b213SSam Leffler sc->sc_ah = ah; 310b58b3803SSam Leffler sc->sc_invalid = 0; /* ready to go, enable interrupt handling */ 3113297be13SSam Leffler #ifdef ATH_DEBUG 3123297be13SSam Leffler sc->sc_debug = ath_debug; 3133297be13SSam Leffler #endif 3145591b213SSam Leffler 3155591b213SSam Leffler /* 316c42a7b7eSSam Leffler * Check if the MAC has multi-rate retry support. 317c42a7b7eSSam Leffler * We do this by trying to setup a fake extended 318c42a7b7eSSam Leffler * descriptor. MAC's that don't have support will 319c42a7b7eSSam Leffler * return false w/o doing anything. MAC's that do 320c42a7b7eSSam Leffler * support it will return true w/o doing anything. 321c42a7b7eSSam Leffler */ 322c42a7b7eSSam Leffler sc->sc_mrretry = ath_hal_setupxtxdesc(ah, NULL, 0,0, 0,0, 0,0); 323c42a7b7eSSam Leffler 324c42a7b7eSSam Leffler /* 325c42a7b7eSSam Leffler * Check if the device has hardware counters for PHY 326c42a7b7eSSam Leffler * errors. If so we need to enable the MIB interrupt 327c42a7b7eSSam Leffler * so we can act on stat triggers. 328c42a7b7eSSam Leffler */ 329c42a7b7eSSam Leffler if (ath_hal_hwphycounters(ah)) 330c42a7b7eSSam Leffler sc->sc_needmib = 1; 331c42a7b7eSSam Leffler 332c42a7b7eSSam Leffler /* 333c42a7b7eSSam Leffler * Get the hardware key cache size. 334c42a7b7eSSam Leffler */ 335c42a7b7eSSam Leffler sc->sc_keymax = ath_hal_keycachesize(ah); 336e8fd88a3SSam Leffler if (sc->sc_keymax > ATH_KEYMAX) { 337e8fd88a3SSam Leffler if_printf(ifp, "Warning, using only %u of %u key cache slots\n", 338e8fd88a3SSam Leffler ATH_KEYMAX, sc->sc_keymax); 339e8fd88a3SSam Leffler sc->sc_keymax = ATH_KEYMAX; 340c42a7b7eSSam Leffler } 341c42a7b7eSSam Leffler /* 342c42a7b7eSSam Leffler * Reset the key cache since some parts do not 343c42a7b7eSSam Leffler * reset the contents on initial power up. 344c42a7b7eSSam Leffler */ 345c42a7b7eSSam Leffler for (i = 0; i < sc->sc_keymax; i++) 346c42a7b7eSSam Leffler ath_hal_keyreset(ah, i); 347c42a7b7eSSam Leffler 348c42a7b7eSSam Leffler /* 349b032f27cSSam Leffler * Collect the default channel list. 3505591b213SSam Leffler */ 351b032f27cSSam Leffler error = ath_getchannels(sc); 3525591b213SSam Leffler if (error != 0) 3535591b213SSam Leffler goto bad; 3545591b213SSam Leffler 3555591b213SSam Leffler /* 3565591b213SSam Leffler * Setup rate tables for all potential media types. 3575591b213SSam Leffler */ 3585591b213SSam Leffler ath_rate_setup(sc, IEEE80211_MODE_11A); 3595591b213SSam Leffler ath_rate_setup(sc, IEEE80211_MODE_11B); 3605591b213SSam Leffler ath_rate_setup(sc, IEEE80211_MODE_11G); 361c42a7b7eSSam Leffler ath_rate_setup(sc, IEEE80211_MODE_TURBO_A); 362c42a7b7eSSam Leffler ath_rate_setup(sc, IEEE80211_MODE_TURBO_G); 36368e8e04eSSam Leffler ath_rate_setup(sc, IEEE80211_MODE_STURBO_A); 36468e8e04eSSam Leffler ath_rate_setup(sc, IEEE80211_MODE_11NA); 36568e8e04eSSam Leffler ath_rate_setup(sc, IEEE80211_MODE_11NG); 366724c193aSSam Leffler ath_rate_setup(sc, IEEE80211_MODE_HALF); 367724c193aSSam Leffler ath_rate_setup(sc, IEEE80211_MODE_QUARTER); 368aaa70f2fSSam Leffler 369c42a7b7eSSam Leffler /* NB: setup here so ath_rate_update is happy */ 370c42a7b7eSSam Leffler ath_setcurmode(sc, IEEE80211_MODE_11A); 3715591b213SSam Leffler 372c42a7b7eSSam Leffler /* 373c42a7b7eSSam Leffler * Allocate tx+rx descriptors and populate the lists. 374c42a7b7eSSam Leffler */ 3755591b213SSam Leffler error = ath_desc_alloc(sc); 3765591b213SSam Leffler if (error != 0) { 3775591b213SSam Leffler if_printf(ifp, "failed to allocate descriptors: %d\n", error); 3785591b213SSam Leffler goto bad; 3795591b213SSam Leffler } 3802e986da5SSam Leffler callout_init_mtx(&sc->sc_cal_ch, &sc->sc_mtx, 0); 3812e986da5SSam Leffler callout_init_mtx(&sc->sc_wd_ch, &sc->sc_mtx, 0); 3825591b213SSam Leffler 383f0b2a0beSSam Leffler ATH_TXBUF_LOCK_INIT(sc); 3845591b213SSam Leffler 3850bbf5441SSam Leffler sc->sc_tq = taskqueue_create("ath_taskq", M_NOWAIT, 3860bbf5441SSam Leffler taskqueue_thread_enqueue, &sc->sc_tq); 3870bbf5441SSam Leffler taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, 3880bbf5441SSam Leffler "%s taskq", ifp->if_xname); 3890bbf5441SSam Leffler 39096ff485dSAdrian Chadd TASK_INIT(&sc->sc_rxtask, 0, ath_rx_tasklet, sc); 3915591b213SSam Leffler TASK_INIT(&sc->sc_bmisstask, 0, ath_bmiss_proc, sc); 392c42a7b7eSSam Leffler TASK_INIT(&sc->sc_bstucktask,0, ath_bstuck_proc, sc); 3935591b213SSam Leffler 3945591b213SSam Leffler /* 395c42a7b7eSSam Leffler * Allocate hardware transmit queues: one queue for 396c42a7b7eSSam Leffler * beacon frames and one data queue for each QoS 3974fa8d4efSDaniel Eischen * priority. Note that the hal handles resetting 398c42a7b7eSSam Leffler * these queues at the needed time. 399c42a7b7eSSam Leffler * 400c42a7b7eSSam Leffler * XXX PS-Poll 4015591b213SSam Leffler */ 40280d2765fSSam Leffler sc->sc_bhalq = ath_beaconq_setup(ah); 4035591b213SSam Leffler if (sc->sc_bhalq == (u_int) -1) { 4045591b213SSam Leffler if_printf(ifp, "unable to setup a beacon xmit queue!\n"); 405c42a7b7eSSam Leffler error = EIO; 406b28b4653SSam Leffler goto bad2; 4075591b213SSam Leffler } 408c42a7b7eSSam Leffler sc->sc_cabq = ath_txq_setup(sc, HAL_TX_QUEUE_CAB, 0); 409c42a7b7eSSam Leffler if (sc->sc_cabq == NULL) { 410c42a7b7eSSam Leffler if_printf(ifp, "unable to setup CAB xmit queue!\n"); 411c42a7b7eSSam Leffler error = EIO; 412c42a7b7eSSam Leffler goto bad2; 413c42a7b7eSSam Leffler } 414c42a7b7eSSam Leffler /* NB: insure BK queue is the lowest priority h/w queue */ 415c42a7b7eSSam Leffler if (!ath_tx_setup(sc, WME_AC_BK, HAL_WME_AC_BK)) { 416c42a7b7eSSam Leffler if_printf(ifp, "unable to setup xmit queue for %s traffic!\n", 417c42a7b7eSSam Leffler ieee80211_wme_acnames[WME_AC_BK]); 418c42a7b7eSSam Leffler error = EIO; 419c42a7b7eSSam Leffler goto bad2; 420c42a7b7eSSam Leffler } 421c42a7b7eSSam Leffler if (!ath_tx_setup(sc, WME_AC_BE, HAL_WME_AC_BE) || 422c42a7b7eSSam Leffler !ath_tx_setup(sc, WME_AC_VI, HAL_WME_AC_VI) || 423c42a7b7eSSam Leffler !ath_tx_setup(sc, WME_AC_VO, HAL_WME_AC_VO)) { 424c42a7b7eSSam Leffler /* 425c42a7b7eSSam Leffler * Not enough hardware tx queues to properly do WME; 426c42a7b7eSSam Leffler * just punt and assign them all to the same h/w queue. 427c42a7b7eSSam Leffler * We could do a better job of this if, for example, 428c42a7b7eSSam Leffler * we allocate queues when we switch from station to 429c42a7b7eSSam Leffler * AP mode. 430c42a7b7eSSam Leffler */ 431c42a7b7eSSam Leffler if (sc->sc_ac2q[WME_AC_VI] != NULL) 432c42a7b7eSSam Leffler ath_tx_cleanupq(sc, sc->sc_ac2q[WME_AC_VI]); 433c42a7b7eSSam Leffler if (sc->sc_ac2q[WME_AC_BE] != NULL) 434c42a7b7eSSam Leffler ath_tx_cleanupq(sc, sc->sc_ac2q[WME_AC_BE]); 435c42a7b7eSSam Leffler sc->sc_ac2q[WME_AC_BE] = sc->sc_ac2q[WME_AC_BK]; 436c42a7b7eSSam Leffler sc->sc_ac2q[WME_AC_VI] = sc->sc_ac2q[WME_AC_BK]; 437c42a7b7eSSam Leffler sc->sc_ac2q[WME_AC_VO] = sc->sc_ac2q[WME_AC_BK]; 438c42a7b7eSSam Leffler } 439c42a7b7eSSam Leffler 440c42a7b7eSSam Leffler /* 441c42a7b7eSSam Leffler * Special case certain configurations. Note the 442c42a7b7eSSam Leffler * CAB queue is handled by these specially so don't 443c42a7b7eSSam Leffler * include them when checking the txq setup mask. 444c42a7b7eSSam Leffler */ 445c42a7b7eSSam Leffler switch (sc->sc_txqsetup &~ (1<<sc->sc_cabq->axq_qnum)) { 446c42a7b7eSSam Leffler case 0x01: 447c42a7b7eSSam Leffler TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc_q0, sc); 448c42a7b7eSSam Leffler break; 449c42a7b7eSSam Leffler case 0x0f: 450c42a7b7eSSam Leffler TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc_q0123, sc); 451c42a7b7eSSam Leffler break; 452c42a7b7eSSam Leffler default: 453c42a7b7eSSam Leffler TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc, sc); 454c42a7b7eSSam Leffler break; 455c42a7b7eSSam Leffler } 456c42a7b7eSSam Leffler 457c42a7b7eSSam Leffler /* 458c42a7b7eSSam Leffler * Setup rate control. Some rate control modules 459c42a7b7eSSam Leffler * call back to change the anntena state so expose 460c42a7b7eSSam Leffler * the necessary entry points. 461c42a7b7eSSam Leffler * XXX maybe belongs in struct ath_ratectrl? 462c42a7b7eSSam Leffler */ 463c42a7b7eSSam Leffler sc->sc_setdefantenna = ath_setdefantenna; 464c42a7b7eSSam Leffler sc->sc_rc = ath_rate_attach(sc); 465c42a7b7eSSam Leffler if (sc->sc_rc == NULL) { 466c42a7b7eSSam Leffler error = EIO; 467c42a7b7eSSam Leffler goto bad2; 468c42a7b7eSSam Leffler } 469c42a7b7eSSam Leffler 47048237774SAdrian Chadd /* Attach DFS module */ 47148237774SAdrian Chadd if (! ath_dfs_attach(sc)) { 47248237774SAdrian Chadd device_printf(sc->sc_dev, "%s: unable to attach DFS\n", __func__); 47348237774SAdrian Chadd error = EIO; 47448237774SAdrian Chadd goto bad2; 47548237774SAdrian Chadd } 47648237774SAdrian Chadd 47748237774SAdrian Chadd /* Start DFS processing tasklet */ 47848237774SAdrian Chadd TASK_INIT(&sc->sc_dfstask, 0, ath_dfs_tasklet, sc); 47948237774SAdrian Chadd 4803e50ec2cSSam Leffler sc->sc_blinking = 0; 481c42a7b7eSSam Leffler sc->sc_ledstate = 1; 4823e50ec2cSSam Leffler sc->sc_ledon = 0; /* low true */ 4833e50ec2cSSam Leffler sc->sc_ledidle = (2700*hz)/1000; /* 2.7sec */ 4843e50ec2cSSam Leffler callout_init(&sc->sc_ledtimer, CALLOUT_MPSAFE); 485c42a7b7eSSam Leffler /* 486c42a7b7eSSam Leffler * Auto-enable soft led processing for IBM cards and for 487c42a7b7eSSam Leffler * 5211 minipci cards. Users can also manually enable/disable 488c42a7b7eSSam Leffler * support with a sysctl. 489c42a7b7eSSam Leffler */ 490c42a7b7eSSam Leffler sc->sc_softled = (devid == AR5212_DEVID_IBM || devid == AR5211_DEVID); 491c42a7b7eSSam Leffler if (sc->sc_softled) { 492869ff02eSSam Leffler ath_hal_gpioCfgOutput(ah, sc->sc_ledpin, 493869ff02eSSam Leffler HAL_GPIO_MUX_MAC_NETWORK_LED); 4943e50ec2cSSam Leffler ath_hal_gpioset(ah, sc->sc_ledpin, !sc->sc_ledon); 495c42a7b7eSSam Leffler } 4965591b213SSam Leffler 4975591b213SSam Leffler ifp->if_softc = sc; 4985591b213SSam Leffler ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; 4995591b213SSam Leffler ifp->if_start = ath_start; 5005591b213SSam Leffler ifp->if_ioctl = ath_ioctl; 5015591b213SSam Leffler ifp->if_init = ath_init; 502e50d35e6SMaxim Sobolev IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); 503e50d35e6SMaxim Sobolev ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; 504154b8df2SMax Laier IFQ_SET_READY(&ifp->if_snd); 5055591b213SSam Leffler 506c42a7b7eSSam Leffler ic->ic_ifp = ifp; 5075591b213SSam Leffler /* XXX not right but it's not used anywhere important */ 5085591b213SSam Leffler ic->ic_phytype = IEEE80211_T_OFDM; 5095591b213SSam Leffler ic->ic_opmode = IEEE80211_M_STA; 510c42a7b7eSSam Leffler ic->ic_caps = 511c43feedeSSam Leffler IEEE80211_C_STA /* station mode */ 512c43feedeSSam Leffler | IEEE80211_C_IBSS /* ibss, nee adhoc, mode */ 513fe32c3efSSam Leffler | IEEE80211_C_HOSTAP /* hostap mode */ 514fe32c3efSSam Leffler | IEEE80211_C_MONITOR /* monitor mode */ 5157a04dc27SSam Leffler | IEEE80211_C_AHDEMO /* adhoc demo mode */ 516b032f27cSSam Leffler | IEEE80211_C_WDS /* 4-address traffic works */ 51759aa14a9SRui Paulo | IEEE80211_C_MBSS /* mesh point link mode */ 518fe32c3efSSam Leffler | IEEE80211_C_SHPREAMBLE /* short preamble supported */ 519c42a7b7eSSam Leffler | IEEE80211_C_SHSLOT /* short slot time supported */ 520c42a7b7eSSam Leffler | IEEE80211_C_WPA /* capable of WPA1+WPA2 */ 52168e8e04eSSam Leffler | IEEE80211_C_BGSCAN /* capable of bg scanning */ 52268e8e04eSSam Leffler | IEEE80211_C_TXFRAG /* handle tx frags */ 52310dc8de4SAdrian Chadd #ifdef ATH_ENABLE_DFS 52410dc8de4SAdrian Chadd | IEEE80211_C_DFS /* Enable DFS radar detection */ 52510dc8de4SAdrian Chadd #endif 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); 6078a2a6beeSAdrian Chadd sc->sc_rxslink = ath_hal_self_linked_final_rxdesc(ah); 608fc4de9b7SAdrian Chadd sc->sc_rxtsf32 = ath_hal_has_long_rxdesc_tsf(ah); 60968e8e04eSSam Leffler if (ath_hal_hasfastframes(ah)) 61068e8e04eSSam Leffler ic->ic_caps |= IEEE80211_C_FF; 61159efa8b5SSam Leffler wmodes = ath_hal_getwirelessmodes(ah); 612411373ebSSam Leffler if (wmodes & (HAL_MODE_108G|HAL_MODE_TURBO)) 61368e8e04eSSam Leffler ic->ic_caps |= IEEE80211_C_TURBOP; 614584f7327SSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 61510ad9a77SSam Leffler if (ath_hal_macversion(ah) > 0x78) { 61610ad9a77SSam Leffler ic->ic_caps |= IEEE80211_C_TDMA; /* capable of TDMA */ 61710ad9a77SSam Leffler ic->ic_tdma_update = ath_tdma_update; 61810ad9a77SSam Leffler } 61910ad9a77SSam Leffler #endif 62067397d39SAdrian Chadd 62167397d39SAdrian Chadd /* 62267397d39SAdrian Chadd * The if_ath 11n support is completely not ready for normal use. 62367397d39SAdrian Chadd * Enabling this option will likely break everything and everything. 62467397d39SAdrian Chadd * Don't think of doing that unless you know what you're doing. 62567397d39SAdrian Chadd */ 62667397d39SAdrian Chadd 6278fd67f92SAdrian Chadd #ifdef ATH_ENABLE_11N 62867397d39SAdrian Chadd /* 62967397d39SAdrian Chadd * Query HT capabilities 63067397d39SAdrian Chadd */ 63167397d39SAdrian Chadd if (ath_hal_getcapability(ah, HAL_CAP_HT, 0, NULL) == HAL_OK && 63267397d39SAdrian Chadd (wmodes & (HAL_MODE_HT20 | HAL_MODE_HT40))) { 63367397d39SAdrian Chadd int rxs, txs; 63467397d39SAdrian Chadd 63567397d39SAdrian Chadd device_printf(sc->sc_dev, "[HT] enabling HT modes\n"); 63667397d39SAdrian Chadd ic->ic_htcaps = IEEE80211_HTC_HT /* HT operation */ 63767397d39SAdrian Chadd | IEEE80211_HTC_AMPDU /* A-MPDU tx/rx */ 63867397d39SAdrian Chadd | IEEE80211_HTC_AMSDU /* A-MSDU tx/rx */ 63967397d39SAdrian Chadd | IEEE80211_HTCAP_MAXAMSDU_3839 /* max A-MSDU length */ 64067397d39SAdrian Chadd | IEEE80211_HTCAP_SMPS_OFF; /* SM power save off */ 64167397d39SAdrian Chadd ; 64267397d39SAdrian Chadd 64376355edbSAdrian Chadd /* 64476355edbSAdrian Chadd * Enable short-GI for HT20 only if the hardware 64576355edbSAdrian Chadd * advertises support. 64676355edbSAdrian Chadd * Notably, anything earlier than the AR9287 doesn't. 64776355edbSAdrian Chadd */ 64876355edbSAdrian Chadd if ((ath_hal_getcapability(ah, 64976355edbSAdrian Chadd HAL_CAP_HT20_SGI, 0, NULL) == HAL_OK) && 65076355edbSAdrian Chadd (wmodes & HAL_MODE_HT20)) { 65176355edbSAdrian Chadd device_printf(sc->sc_dev, 65276355edbSAdrian Chadd "[HT] enabling short-GI in 20MHz mode\n"); 65376355edbSAdrian Chadd ic->ic_htcaps |= IEEE80211_HTCAP_SHORTGI20; 65476355edbSAdrian Chadd } 65576355edbSAdrian Chadd 65667397d39SAdrian Chadd if (wmodes & HAL_MODE_HT40) 65767397d39SAdrian Chadd ic->ic_htcaps |= IEEE80211_HTCAP_CHWIDTH40 65867397d39SAdrian Chadd | IEEE80211_HTCAP_SHORTGI40; 65967397d39SAdrian Chadd 66067397d39SAdrian Chadd /* 66167397d39SAdrian Chadd * rx/tx stream is not currently used anywhere; it needs to be taken 66267397d39SAdrian Chadd * into account when negotiating which MCS rates it'll receive and 66367397d39SAdrian Chadd * what MCS rates are available for TX. 66467397d39SAdrian Chadd */ 66567397d39SAdrian Chadd (void) ath_hal_getcapability(ah, HAL_CAP_STREAMS, 0, &rxs); 66667397d39SAdrian Chadd (void) ath_hal_getcapability(ah, HAL_CAP_STREAMS, 1, &txs); 66767397d39SAdrian Chadd 66867397d39SAdrian Chadd ath_hal_getrxchainmask(ah, &sc->sc_rxchainmask); 66967397d39SAdrian Chadd ath_hal_gettxchainmask(ah, &sc->sc_txchainmask); 67067397d39SAdrian Chadd 67167397d39SAdrian Chadd ic->ic_txstream = txs; 67267397d39SAdrian Chadd ic->ic_rxstream = rxs; 67367397d39SAdrian Chadd 67467397d39SAdrian Chadd device_printf(sc->sc_dev, "[HT] %d RX streams; %d TX streams\n", rxs, txs); 67567397d39SAdrian Chadd } 67667397d39SAdrian Chadd #endif 67767397d39SAdrian Chadd 678c42a7b7eSSam Leffler /* 679c42a7b7eSSam Leffler * Indicate we need the 802.11 header padded to a 680c42a7b7eSSam Leffler * 32-bit boundary for 4-address and QoS frames. 681c42a7b7eSSam Leffler */ 682c42a7b7eSSam Leffler ic->ic_flags |= IEEE80211_F_DATAPAD; 683c42a7b7eSSam Leffler 684c42a7b7eSSam Leffler /* 685c42a7b7eSSam Leffler * Query the hal about antenna support. 686c42a7b7eSSam Leffler */ 687c42a7b7eSSam Leffler sc->sc_defant = ath_hal_getdefantenna(ah); 688c42a7b7eSSam Leffler 689c42a7b7eSSam Leffler /* 690c42a7b7eSSam Leffler * Not all chips have the VEOL support we want to 691c42a7b7eSSam Leffler * use with IBSS beacons; check here for it. 692c42a7b7eSSam Leffler */ 693c42a7b7eSSam Leffler sc->sc_hasveol = ath_hal_hasveol(ah); 6945591b213SSam Leffler 6955591b213SSam Leffler /* get mac address from hardware */ 69629aca940SSam Leffler ath_hal_getmac(ah, macaddr); 697b032f27cSSam Leffler if (sc->sc_hasbmask) 698b032f27cSSam Leffler ath_hal_getbssidmask(ah, sc->sc_hwbssidmask); 6995591b213SSam Leffler 700b032f27cSSam Leffler /* NB: used to size node table key mapping array */ 701b032f27cSSam Leffler ic->ic_max_keyix = sc->sc_keymax; 7025591b213SSam Leffler /* call MI attach routine. */ 70329aca940SSam Leffler ieee80211_ifattach(ic, macaddr); 704b032f27cSSam Leffler ic->ic_setregdomain = ath_setregdomain; 705b032f27cSSam Leffler ic->ic_getradiocaps = ath_getradiocaps; 706b032f27cSSam Leffler sc->sc_opmode = HAL_M_STA; 707b032f27cSSam Leffler 7085591b213SSam Leffler /* override default methods */ 709b032f27cSSam Leffler ic->ic_newassoc = ath_newassoc; 710b032f27cSSam Leffler ic->ic_updateslot = ath_updateslot; 711b032f27cSSam Leffler ic->ic_wme.wme_update = ath_wme_update; 712b032f27cSSam Leffler ic->ic_vap_create = ath_vap_create; 713b032f27cSSam Leffler ic->ic_vap_delete = ath_vap_delete; 714b032f27cSSam Leffler ic->ic_raw_xmit = ath_raw_xmit; 715b032f27cSSam Leffler ic->ic_update_mcast = ath_update_mcast; 716b032f27cSSam Leffler ic->ic_update_promisc = ath_update_promisc; 7175591b213SSam Leffler ic->ic_node_alloc = ath_node_alloc; 7181e774079SSam Leffler sc->sc_node_free = ic->ic_node_free; 7195591b213SSam Leffler ic->ic_node_free = ath_node_free; 7204afa805eSAdrian Chadd sc->sc_node_cleanup = ic->ic_node_cleanup; 7214afa805eSAdrian Chadd ic->ic_node_cleanup = ath_node_cleanup; 72268e8e04eSSam Leffler ic->ic_node_getsignal = ath_node_getsignal; 72368e8e04eSSam Leffler ic->ic_scan_start = ath_scan_start; 72468e8e04eSSam Leffler ic->ic_scan_end = ath_scan_end; 72568e8e04eSSam Leffler ic->ic_set_channel = ath_set_channel; 7265591b213SSam Leffler 7275463c4a4SSam Leffler ieee80211_radiotap_attach(ic, 7285463c4a4SSam Leffler &sc->sc_tx_th.wt_ihdr, sizeof(sc->sc_tx_th), 7295463c4a4SSam Leffler ATH_TX_RADIOTAP_PRESENT, 7305463c4a4SSam Leffler &sc->sc_rx_th.wr_ihdr, sizeof(sc->sc_rx_th), 7315463c4a4SSam Leffler ATH_RX_RADIOTAP_PRESENT); 7325463c4a4SSam Leffler 7334866e6c2SSam Leffler /* 7344866e6c2SSam Leffler * Setup dynamic sysctl's now that country code and 7354866e6c2SSam Leffler * regdomain are available from the hal. 7364866e6c2SSam Leffler */ 7374866e6c2SSam Leffler ath_sysctlattach(sc); 738e8dabfbeSAdrian Chadd ath_sysctl_stats_attach(sc); 73937931a35SAdrian Chadd ath_sysctl_hal_attach(sc); 74073454c73SSam Leffler 741c42a7b7eSSam Leffler if (bootverbose) 742c42a7b7eSSam Leffler ieee80211_announce(ic); 743c42a7b7eSSam Leffler ath_announce(sc); 7445591b213SSam Leffler return 0; 745b28b4653SSam Leffler bad2: 746c42a7b7eSSam Leffler ath_tx_cleanup(sc); 747b28b4653SSam Leffler ath_desc_free(sc); 7485591b213SSam Leffler bad: 7495591b213SSam Leffler if (ah) 7505591b213SSam Leffler ath_hal_detach(ah); 751fc74a9f9SBrooks Davis if (ifp != NULL) 752fc74a9f9SBrooks Davis if_free(ifp); 7535591b213SSam Leffler sc->sc_invalid = 1; 7545591b213SSam Leffler return error; 7555591b213SSam Leffler } 7565591b213SSam Leffler 7575591b213SSam Leffler int 7585591b213SSam Leffler ath_detach(struct ath_softc *sc) 7595591b213SSam Leffler { 760fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 7615591b213SSam Leffler 762c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n", 763c42a7b7eSSam Leffler __func__, ifp->if_flags); 7645591b213SSam Leffler 765c42a7b7eSSam Leffler /* 766c42a7b7eSSam Leffler * NB: the order of these is important: 76771b85077SSam Leffler * o stop the chip so no more interrupts will fire 768c42a7b7eSSam Leffler * o call the 802.11 layer before detaching the hal to 769c42a7b7eSSam Leffler * insure callbacks into the driver to delete global 770c42a7b7eSSam Leffler * key cache entries can be handled 77171b85077SSam Leffler * o free the taskqueue which drains any pending tasks 772c42a7b7eSSam Leffler * o reclaim the tx queue data structures after calling 773c42a7b7eSSam Leffler * the 802.11 layer as we'll get called back to reclaim 774c42a7b7eSSam Leffler * node state and potentially want to use them 775c42a7b7eSSam Leffler * o to cleanup the tx queues the hal is called, so detach 776c42a7b7eSSam Leffler * it last 777c42a7b7eSSam Leffler * Other than that, it's straightforward... 778c42a7b7eSSam Leffler */ 77971b85077SSam Leffler ath_stop(ifp); 780b032f27cSSam Leffler ieee80211_ifdetach(ifp->if_l2com); 78171b85077SSam Leffler taskqueue_free(sc->sc_tq); 78286e07743SSam Leffler #ifdef ATH_TX99_DIAG 78386e07743SSam Leffler if (sc->sc_tx99 != NULL) 78486e07743SSam Leffler sc->sc_tx99->detach(sc->sc_tx99); 78586e07743SSam Leffler #endif 786c42a7b7eSSam Leffler ath_rate_detach(sc->sc_rc); 78748237774SAdrian Chadd 78848237774SAdrian Chadd ath_dfs_detach(sc); 7895591b213SSam Leffler ath_desc_free(sc); 790c42a7b7eSSam Leffler ath_tx_cleanup(sc); 79171b85077SSam Leffler ath_hal_detach(sc->sc_ah); /* NB: sets chip in full sleep */ 792c4c6f08fSRuslan Ermilov if_free(ifp); 793f0b2a0beSSam Leffler 7945591b213SSam Leffler return 0; 7955591b213SSam Leffler } 7965591b213SSam Leffler 797b032f27cSSam Leffler /* 798b032f27cSSam Leffler * MAC address handling for multiple BSS on the same radio. 799b032f27cSSam Leffler * The first vap uses the MAC address from the EEPROM. For 800b032f27cSSam Leffler * subsequent vap's we set the U/L bit (bit 1) in the MAC 801b032f27cSSam Leffler * address and use the next six bits as an index. 802b032f27cSSam Leffler */ 803b032f27cSSam Leffler static void 804b032f27cSSam Leffler assign_address(struct ath_softc *sc, uint8_t mac[IEEE80211_ADDR_LEN], int clone) 805b032f27cSSam Leffler { 806b032f27cSSam Leffler int i; 807b032f27cSSam Leffler 808b032f27cSSam Leffler if (clone && sc->sc_hasbmask) { 809b032f27cSSam Leffler /* NB: we only do this if h/w supports multiple bssid */ 810b032f27cSSam Leffler for (i = 0; i < 8; i++) 811b032f27cSSam Leffler if ((sc->sc_bssidmask & (1<<i)) == 0) 812b032f27cSSam Leffler break; 813b032f27cSSam Leffler if (i != 0) 814b032f27cSSam Leffler mac[0] |= (i << 2)|0x2; 815b032f27cSSam Leffler } else 816b032f27cSSam Leffler i = 0; 817b032f27cSSam Leffler sc->sc_bssidmask |= 1<<i; 818b032f27cSSam Leffler sc->sc_hwbssidmask[0] &= ~mac[0]; 819b032f27cSSam Leffler if (i == 0) 820b032f27cSSam Leffler sc->sc_nbssid0++; 821b032f27cSSam Leffler } 822b032f27cSSam Leffler 823b032f27cSSam Leffler static void 824b032f27cSSam Leffler reclaim_address(struct ath_softc *sc, const uint8_t mac[IEEE80211_ADDR_LEN]) 825b032f27cSSam Leffler { 826b032f27cSSam Leffler int i = mac[0] >> 2; 827b032f27cSSam Leffler uint8_t mask; 828b032f27cSSam Leffler 829b032f27cSSam Leffler if (i != 0 || --sc->sc_nbssid0 == 0) { 830b032f27cSSam Leffler sc->sc_bssidmask &= ~(1<<i); 831b032f27cSSam Leffler /* recalculate bssid mask from remaining addresses */ 832b032f27cSSam Leffler mask = 0xff; 833b032f27cSSam Leffler for (i = 1; i < 8; i++) 834b032f27cSSam Leffler if (sc->sc_bssidmask & (1<<i)) 835b032f27cSSam Leffler mask &= ~((i<<2)|0x2); 836b032f27cSSam Leffler sc->sc_hwbssidmask[0] |= mask; 837b032f27cSSam Leffler } 838b032f27cSSam Leffler } 839b032f27cSSam Leffler 840b032f27cSSam Leffler /* 841b032f27cSSam Leffler * Assign a beacon xmit slot. We try to space out 842b032f27cSSam Leffler * assignments so when beacons are staggered the 843b032f27cSSam Leffler * traffic coming out of the cab q has maximal time 844b032f27cSSam Leffler * to go out before the next beacon is scheduled. 845b032f27cSSam Leffler */ 846b032f27cSSam Leffler static int 847b032f27cSSam Leffler assign_bslot(struct ath_softc *sc) 848b032f27cSSam Leffler { 849b032f27cSSam Leffler u_int slot, free; 850b032f27cSSam Leffler 851b032f27cSSam Leffler free = 0; 852b032f27cSSam Leffler for (slot = 0; slot < ATH_BCBUF; slot++) 853b032f27cSSam Leffler if (sc->sc_bslot[slot] == NULL) { 854b032f27cSSam Leffler if (sc->sc_bslot[(slot+1)%ATH_BCBUF] == NULL && 855b032f27cSSam Leffler sc->sc_bslot[(slot-1)%ATH_BCBUF] == NULL) 856b032f27cSSam Leffler return slot; 857b032f27cSSam Leffler free = slot; 858b032f27cSSam Leffler /* NB: keep looking for a double slot */ 859b032f27cSSam Leffler } 860b032f27cSSam Leffler return free; 861b032f27cSSam Leffler } 862b032f27cSSam Leffler 863b032f27cSSam Leffler static struct ieee80211vap * 864b032f27cSSam Leffler ath_vap_create(struct ieee80211com *ic, 865b032f27cSSam Leffler const char name[IFNAMSIZ], int unit, int opmode, int flags, 866b032f27cSSam Leffler const uint8_t bssid[IEEE80211_ADDR_LEN], 867b032f27cSSam Leffler const uint8_t mac0[IEEE80211_ADDR_LEN]) 868b032f27cSSam Leffler { 869b032f27cSSam Leffler struct ath_softc *sc = ic->ic_ifp->if_softc; 870b032f27cSSam Leffler struct ath_vap *avp; 871b032f27cSSam Leffler struct ieee80211vap *vap; 872b032f27cSSam Leffler uint8_t mac[IEEE80211_ADDR_LEN]; 873b032f27cSSam Leffler int ic_opmode, needbeacon, error; 874b032f27cSSam Leffler 875b032f27cSSam Leffler avp = (struct ath_vap *) malloc(sizeof(struct ath_vap), 876b032f27cSSam Leffler M_80211_VAP, M_WAITOK | M_ZERO); 877b032f27cSSam Leffler needbeacon = 0; 878b032f27cSSam Leffler IEEE80211_ADDR_COPY(mac, mac0); 879b032f27cSSam Leffler 880b032f27cSSam Leffler ATH_LOCK(sc); 881a8962181SSam Leffler ic_opmode = opmode; /* default to opmode of new vap */ 882b032f27cSSam Leffler switch (opmode) { 883b032f27cSSam Leffler case IEEE80211_M_STA: 884a8962181SSam Leffler if (sc->sc_nstavaps != 0) { /* XXX only 1 for now */ 885b032f27cSSam Leffler device_printf(sc->sc_dev, "only 1 sta vap supported\n"); 886b032f27cSSam Leffler goto bad; 887b032f27cSSam Leffler } 888b032f27cSSam Leffler if (sc->sc_nvaps) { 889b032f27cSSam Leffler /* 890a8962181SSam Leffler * With multiple vaps we must fall back 891a8962181SSam Leffler * to s/w beacon miss handling. 892b032f27cSSam Leffler */ 893b032f27cSSam Leffler flags |= IEEE80211_CLONE_NOBEACONS; 894b032f27cSSam Leffler } 895a8962181SSam Leffler if (flags & IEEE80211_CLONE_NOBEACONS) { 896a8962181SSam Leffler /* 897a8962181SSam Leffler * Station mode w/o beacons are implemented w/ AP mode. 898a8962181SSam Leffler */ 899b032f27cSSam Leffler ic_opmode = IEEE80211_M_HOSTAP; 900a8962181SSam Leffler } 901b032f27cSSam Leffler break; 902b032f27cSSam Leffler case IEEE80211_M_IBSS: 903b032f27cSSam Leffler if (sc->sc_nvaps != 0) { /* XXX only 1 for now */ 904b032f27cSSam Leffler device_printf(sc->sc_dev, 905b032f27cSSam Leffler "only 1 ibss vap supported\n"); 906b032f27cSSam Leffler goto bad; 907b032f27cSSam Leffler } 908b032f27cSSam Leffler needbeacon = 1; 909b032f27cSSam Leffler break; 910b032f27cSSam Leffler case IEEE80211_M_AHDEMO: 911584f7327SSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 91210ad9a77SSam Leffler if (flags & IEEE80211_CLONE_TDMA) { 913a8962181SSam Leffler if (sc->sc_nvaps != 0) { 914a8962181SSam Leffler device_printf(sc->sc_dev, 915a8962181SSam Leffler "only 1 tdma vap supported\n"); 916a8962181SSam Leffler goto bad; 917a8962181SSam Leffler } 91810ad9a77SSam Leffler needbeacon = 1; 91910ad9a77SSam Leffler flags |= IEEE80211_CLONE_NOBEACONS; 92010ad9a77SSam Leffler } 921b032f27cSSam Leffler /* fall thru... */ 92210ad9a77SSam Leffler #endif 923b032f27cSSam Leffler case IEEE80211_M_MONITOR: 924b032f27cSSam Leffler if (sc->sc_nvaps != 0 && ic->ic_opmode != opmode) { 925a8962181SSam Leffler /* 926a8962181SSam Leffler * Adopt existing mode. Adding a monitor or ahdemo 927a8962181SSam Leffler * vap to an existing configuration is of dubious 928a8962181SSam Leffler * value but should be ok. 929a8962181SSam Leffler */ 930b032f27cSSam Leffler /* XXX not right for monitor mode */ 931b032f27cSSam Leffler ic_opmode = ic->ic_opmode; 932a8962181SSam Leffler } 933b032f27cSSam Leffler break; 934b032f27cSSam Leffler case IEEE80211_M_HOSTAP: 93559aa14a9SRui Paulo case IEEE80211_M_MBSS: 936b032f27cSSam Leffler needbeacon = 1; 937a8962181SSam Leffler break; 938b032f27cSSam Leffler case IEEE80211_M_WDS: 939a8962181SSam Leffler if (sc->sc_nvaps != 0 && ic->ic_opmode == IEEE80211_M_STA) { 940b032f27cSSam Leffler device_printf(sc->sc_dev, 941b032f27cSSam Leffler "wds not supported in sta mode\n"); 942b032f27cSSam Leffler goto bad; 943b032f27cSSam Leffler } 944b032f27cSSam Leffler /* 945b032f27cSSam Leffler * Silently remove any request for a unique 946b032f27cSSam Leffler * bssid; WDS vap's always share the local 947b032f27cSSam Leffler * mac address. 948b032f27cSSam Leffler */ 949b032f27cSSam Leffler flags &= ~IEEE80211_CLONE_BSSID; 950a8962181SSam Leffler if (sc->sc_nvaps == 0) 951b032f27cSSam Leffler ic_opmode = IEEE80211_M_HOSTAP; 952a8962181SSam Leffler else 953a8962181SSam Leffler ic_opmode = ic->ic_opmode; 9547d261891SRui Paulo break; 955b032f27cSSam Leffler default: 956b032f27cSSam Leffler device_printf(sc->sc_dev, "unknown opmode %d\n", opmode); 957b032f27cSSam Leffler goto bad; 958b032f27cSSam Leffler } 959b032f27cSSam Leffler /* 960b032f27cSSam Leffler * Check that a beacon buffer is available; the code below assumes it. 961b032f27cSSam Leffler */ 9626b349e5aSAdrian Chadd if (needbeacon & TAILQ_EMPTY(&sc->sc_bbuf)) { 963b032f27cSSam Leffler device_printf(sc->sc_dev, "no beacon buffer available\n"); 964b032f27cSSam Leffler goto bad; 965b032f27cSSam Leffler } 966b032f27cSSam Leffler 967b032f27cSSam Leffler /* STA, AHDEMO? */ 96859aa14a9SRui Paulo if (opmode == IEEE80211_M_HOSTAP || opmode == IEEE80211_M_MBSS) { 969b032f27cSSam Leffler assign_address(sc, mac, flags & IEEE80211_CLONE_BSSID); 970b032f27cSSam Leffler ath_hal_setbssidmask(sc->sc_ah, sc->sc_hwbssidmask); 971b032f27cSSam Leffler } 972b032f27cSSam Leffler 973b032f27cSSam Leffler vap = &avp->av_vap; 974b032f27cSSam Leffler /* XXX can't hold mutex across if_alloc */ 975b032f27cSSam Leffler ATH_UNLOCK(sc); 976b032f27cSSam Leffler error = ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, 977b032f27cSSam Leffler bssid, mac); 978b032f27cSSam Leffler ATH_LOCK(sc); 979b032f27cSSam Leffler if (error != 0) { 980b032f27cSSam Leffler device_printf(sc->sc_dev, "%s: error %d creating vap\n", 981b032f27cSSam Leffler __func__, error); 982b032f27cSSam Leffler goto bad2; 983b032f27cSSam Leffler } 984b032f27cSSam Leffler 985b032f27cSSam Leffler /* h/w crypto support */ 986b032f27cSSam Leffler vap->iv_key_alloc = ath_key_alloc; 987b032f27cSSam Leffler vap->iv_key_delete = ath_key_delete; 988b032f27cSSam Leffler vap->iv_key_set = ath_key_set; 989b032f27cSSam Leffler vap->iv_key_update_begin = ath_key_update_begin; 990b032f27cSSam Leffler vap->iv_key_update_end = ath_key_update_end; 991b032f27cSSam Leffler 992b032f27cSSam Leffler /* override various methods */ 993b032f27cSSam Leffler avp->av_recv_mgmt = vap->iv_recv_mgmt; 994b032f27cSSam Leffler vap->iv_recv_mgmt = ath_recv_mgmt; 995b032f27cSSam Leffler vap->iv_reset = ath_reset_vap; 996b032f27cSSam Leffler vap->iv_update_beacon = ath_beacon_update; 997b032f27cSSam Leffler avp->av_newstate = vap->iv_newstate; 998b032f27cSSam Leffler vap->iv_newstate = ath_newstate; 999b032f27cSSam Leffler avp->av_bmiss = vap->iv_bmiss; 1000b032f27cSSam Leffler vap->iv_bmiss = ath_bmiss_vap; 1001b032f27cSSam Leffler 10029be25f4aSAdrian Chadd /* Set default parameters */ 10039be25f4aSAdrian Chadd 10049be25f4aSAdrian Chadd /* 10059be25f4aSAdrian Chadd * Anything earlier than some AR9300 series MACs don't 10069be25f4aSAdrian Chadd * support a smaller MPDU density. 10079be25f4aSAdrian Chadd */ 10089be25f4aSAdrian Chadd vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_8; 10099be25f4aSAdrian Chadd /* 10109be25f4aSAdrian Chadd * All NICs can handle the maximum size, however 10119be25f4aSAdrian Chadd * AR5416 based MACs can only TX aggregates w/ RTS 10129be25f4aSAdrian Chadd * protection when the total aggregate size is <= 8k. 10139be25f4aSAdrian Chadd * However, for now that's enforced by the TX path. 10149be25f4aSAdrian Chadd */ 10159be25f4aSAdrian Chadd vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_64K; 10169be25f4aSAdrian Chadd 1017b032f27cSSam Leffler avp->av_bslot = -1; 1018b032f27cSSam Leffler if (needbeacon) { 1019b032f27cSSam Leffler /* 1020b032f27cSSam Leffler * Allocate beacon state and setup the q for buffered 1021b032f27cSSam Leffler * multicast frames. We know a beacon buffer is 1022b032f27cSSam Leffler * available because we checked above. 1023b032f27cSSam Leffler */ 10246b349e5aSAdrian Chadd avp->av_bcbuf = TAILQ_FIRST(&sc->sc_bbuf); 10256b349e5aSAdrian Chadd TAILQ_REMOVE(&sc->sc_bbuf, avp->av_bcbuf, bf_list); 1026b032f27cSSam Leffler if (opmode != IEEE80211_M_IBSS || !sc->sc_hasveol) { 1027b032f27cSSam Leffler /* 1028b032f27cSSam Leffler * Assign the vap to a beacon xmit slot. As above 1029b032f27cSSam Leffler * this cannot fail to find a free one. 1030b032f27cSSam Leffler */ 1031b032f27cSSam Leffler avp->av_bslot = assign_bslot(sc); 1032b032f27cSSam Leffler KASSERT(sc->sc_bslot[avp->av_bslot] == NULL, 1033b032f27cSSam Leffler ("beacon slot %u not empty", avp->av_bslot)); 1034b032f27cSSam Leffler sc->sc_bslot[avp->av_bslot] = vap; 1035b032f27cSSam Leffler sc->sc_nbcnvaps++; 1036b032f27cSSam Leffler } 1037b032f27cSSam Leffler if (sc->sc_hastsfadd && sc->sc_nbcnvaps > 0) { 1038b032f27cSSam Leffler /* 1039b032f27cSSam Leffler * Multple vaps are to transmit beacons and we 1040b032f27cSSam Leffler * have h/w support for TSF adjusting; enable 1041b032f27cSSam Leffler * use of staggered beacons. 1042b032f27cSSam Leffler */ 1043b032f27cSSam Leffler sc->sc_stagbeacons = 1; 1044b032f27cSSam Leffler } 1045b032f27cSSam Leffler ath_txq_init(sc, &avp->av_mcastq, ATH_TXQ_SWQ); 1046b032f27cSSam Leffler } 1047b032f27cSSam Leffler 1048b032f27cSSam Leffler ic->ic_opmode = ic_opmode; 1049b032f27cSSam Leffler if (opmode != IEEE80211_M_WDS) { 1050b032f27cSSam Leffler sc->sc_nvaps++; 1051b032f27cSSam Leffler if (opmode == IEEE80211_M_STA) 1052b032f27cSSam Leffler sc->sc_nstavaps++; 1053fe0dd789SSam Leffler if (opmode == IEEE80211_M_MBSS) 1054fe0dd789SSam Leffler sc->sc_nmeshvaps++; 1055b032f27cSSam Leffler } 1056b032f27cSSam Leffler switch (ic_opmode) { 1057b032f27cSSam Leffler case IEEE80211_M_IBSS: 1058b032f27cSSam Leffler sc->sc_opmode = HAL_M_IBSS; 1059b032f27cSSam Leffler break; 1060b032f27cSSam Leffler case IEEE80211_M_STA: 1061b032f27cSSam Leffler sc->sc_opmode = HAL_M_STA; 1062b032f27cSSam Leffler break; 1063b032f27cSSam Leffler case IEEE80211_M_AHDEMO: 1064584f7327SSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 106510ad9a77SSam Leffler if (vap->iv_caps & IEEE80211_C_TDMA) { 106610ad9a77SSam Leffler sc->sc_tdma = 1; 106710ad9a77SSam Leffler /* NB: disable tsf adjust */ 106810ad9a77SSam Leffler sc->sc_stagbeacons = 0; 106910ad9a77SSam Leffler } 107010ad9a77SSam Leffler /* 107110ad9a77SSam Leffler * NB: adhoc demo mode is a pseudo mode; to the hal it's 107210ad9a77SSam Leffler * just ap mode. 107310ad9a77SSam Leffler */ 107410ad9a77SSam Leffler /* fall thru... */ 107510ad9a77SSam Leffler #endif 1076b032f27cSSam Leffler case IEEE80211_M_HOSTAP: 107759aa14a9SRui Paulo case IEEE80211_M_MBSS: 1078b032f27cSSam Leffler sc->sc_opmode = HAL_M_HOSTAP; 1079b032f27cSSam Leffler break; 1080b032f27cSSam Leffler case IEEE80211_M_MONITOR: 1081b032f27cSSam Leffler sc->sc_opmode = HAL_M_MONITOR; 1082b032f27cSSam Leffler break; 1083b032f27cSSam Leffler default: 1084b032f27cSSam Leffler /* XXX should not happen */ 1085b032f27cSSam Leffler break; 1086b032f27cSSam Leffler } 1087b032f27cSSam Leffler if (sc->sc_hastsfadd) { 1088b032f27cSSam Leffler /* 1089b032f27cSSam Leffler * Configure whether or not TSF adjust should be done. 1090b032f27cSSam Leffler */ 1091b032f27cSSam Leffler ath_hal_settsfadjust(sc->sc_ah, sc->sc_stagbeacons); 1092b032f27cSSam Leffler } 109310ad9a77SSam Leffler if (flags & IEEE80211_CLONE_NOBEACONS) { 109410ad9a77SSam Leffler /* 109510ad9a77SSam Leffler * Enable s/w beacon miss handling. 109610ad9a77SSam Leffler */ 109710ad9a77SSam Leffler sc->sc_swbmiss = 1; 109810ad9a77SSam Leffler } 1099b032f27cSSam Leffler ATH_UNLOCK(sc); 1100b032f27cSSam Leffler 1101b032f27cSSam Leffler /* complete setup */ 1102b032f27cSSam Leffler ieee80211_vap_attach(vap, ath_media_change, ieee80211_media_status); 1103b032f27cSSam Leffler return vap; 1104b032f27cSSam Leffler bad2: 1105b032f27cSSam Leffler reclaim_address(sc, mac); 1106b032f27cSSam Leffler ath_hal_setbssidmask(sc->sc_ah, sc->sc_hwbssidmask); 1107b032f27cSSam Leffler bad: 1108b032f27cSSam Leffler free(avp, M_80211_VAP); 1109b032f27cSSam Leffler ATH_UNLOCK(sc); 1110b032f27cSSam Leffler return NULL; 1111b032f27cSSam Leffler } 1112b032f27cSSam Leffler 1113b032f27cSSam Leffler static void 1114b032f27cSSam Leffler ath_vap_delete(struct ieee80211vap *vap) 1115b032f27cSSam Leffler { 1116b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 1117b032f27cSSam Leffler struct ifnet *ifp = ic->ic_ifp; 1118b032f27cSSam Leffler struct ath_softc *sc = ifp->if_softc; 1119b032f27cSSam Leffler struct ath_hal *ah = sc->sc_ah; 1120b032f27cSSam Leffler struct ath_vap *avp = ATH_VAP(vap); 1121b032f27cSSam Leffler 1122f52d3452SAdrian Chadd DPRINTF(sc, ATH_DEBUG_RESET, "%s: called\n", __func__); 1123b032f27cSSam Leffler if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 1124b032f27cSSam Leffler /* 1125b032f27cSSam Leffler * Quiesce the hardware while we remove the vap. In 1126b032f27cSSam Leffler * particular we need to reclaim all references to 1127b032f27cSSam Leffler * the vap state by any frames pending on the tx queues. 1128b032f27cSSam Leffler */ 1129b032f27cSSam Leffler ath_hal_intrset(ah, 0); /* disable interrupts */ 1130517526efSAdrian Chadd ath_draintxq(sc, ATH_RESET_DEFAULT); /* stop hw xmit side */ 1131517526efSAdrian Chadd /* XXX Do all frames from all vaps/nodes need draining here? */ 1132b032f27cSSam Leffler ath_stoprecv(sc); /* stop recv side */ 1133b032f27cSSam Leffler } 1134b032f27cSSam Leffler 1135b032f27cSSam Leffler ieee80211_vap_detach(vap); 1136*16d4de92SAdrian Chadd 1137*16d4de92SAdrian Chadd /* 1138*16d4de92SAdrian Chadd * XXX Danger Will Robinson! Danger! 1139*16d4de92SAdrian Chadd * 1140*16d4de92SAdrian Chadd * Because ieee80211_vap_detach() can queue a frame (the station 1141*16d4de92SAdrian Chadd * diassociate message?) after we've drained the TXQ and 1142*16d4de92SAdrian Chadd * flushed the software TXQ, we will end up with a frame queued 1143*16d4de92SAdrian Chadd * to a node whose vap is about to be freed. 1144*16d4de92SAdrian Chadd * 1145*16d4de92SAdrian Chadd * To work around this, flush the hardware/software again. 1146*16d4de92SAdrian Chadd * This may be racy - the ath task may be running and the packet 1147*16d4de92SAdrian Chadd * may be being scheduled between sw->hw txq. Tsk. 1148*16d4de92SAdrian Chadd * 1149*16d4de92SAdrian Chadd * TODO: figure out why a new node gets allocated somewhere around 1150*16d4de92SAdrian Chadd * here (after the ath_tx_swq() call; and after an ath_stop_locked() 1151*16d4de92SAdrian Chadd * call!) 1152*16d4de92SAdrian Chadd */ 1153*16d4de92SAdrian Chadd 1154*16d4de92SAdrian Chadd ath_draintxq(sc, ATH_RESET_DEFAULT); 1155*16d4de92SAdrian Chadd 1156b032f27cSSam Leffler ATH_LOCK(sc); 1157b032f27cSSam Leffler /* 1158b032f27cSSam Leffler * Reclaim beacon state. Note this must be done before 1159b032f27cSSam Leffler * the vap instance is reclaimed as we may have a reference 1160b032f27cSSam Leffler * to it in the buffer for the beacon frame. 1161b032f27cSSam Leffler */ 1162b032f27cSSam Leffler if (avp->av_bcbuf != NULL) { 1163b032f27cSSam Leffler if (avp->av_bslot != -1) { 1164b032f27cSSam Leffler sc->sc_bslot[avp->av_bslot] = NULL; 1165b032f27cSSam Leffler sc->sc_nbcnvaps--; 1166b032f27cSSam Leffler } 1167b032f27cSSam Leffler ath_beacon_return(sc, avp->av_bcbuf); 1168b032f27cSSam Leffler avp->av_bcbuf = NULL; 1169b032f27cSSam Leffler if (sc->sc_nbcnvaps == 0) { 1170b032f27cSSam Leffler sc->sc_stagbeacons = 0; 1171b032f27cSSam Leffler if (sc->sc_hastsfadd) 1172b032f27cSSam Leffler ath_hal_settsfadjust(sc->sc_ah, 0); 1173b032f27cSSam Leffler } 1174b032f27cSSam Leffler /* 1175b032f27cSSam Leffler * Reclaim any pending mcast frames for the vap. 1176b032f27cSSam Leffler */ 1177b032f27cSSam Leffler ath_tx_draintxq(sc, &avp->av_mcastq); 1178b032f27cSSam Leffler ATH_TXQ_LOCK_DESTROY(&avp->av_mcastq); 1179b032f27cSSam Leffler } 1180b032f27cSSam Leffler /* 1181b032f27cSSam Leffler * Update bookkeeping. 1182b032f27cSSam Leffler */ 1183b032f27cSSam Leffler if (vap->iv_opmode == IEEE80211_M_STA) { 1184b032f27cSSam Leffler sc->sc_nstavaps--; 1185b032f27cSSam Leffler if (sc->sc_nstavaps == 0 && sc->sc_swbmiss) 1186b032f27cSSam Leffler sc->sc_swbmiss = 0; 118759aa14a9SRui Paulo } else if (vap->iv_opmode == IEEE80211_M_HOSTAP || 118859aa14a9SRui Paulo vap->iv_opmode == IEEE80211_M_MBSS) { 1189b032f27cSSam Leffler reclaim_address(sc, vap->iv_myaddr); 1190b032f27cSSam Leffler ath_hal_setbssidmask(ah, sc->sc_hwbssidmask); 1191fe0dd789SSam Leffler if (vap->iv_opmode == IEEE80211_M_MBSS) 1192fe0dd789SSam Leffler sc->sc_nmeshvaps--; 1193b032f27cSSam Leffler } 1194b032f27cSSam Leffler if (vap->iv_opmode != IEEE80211_M_WDS) 1195b032f27cSSam Leffler sc->sc_nvaps--; 1196584f7327SSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 119710ad9a77SSam Leffler /* TDMA operation ceases when the last vap is destroyed */ 119810ad9a77SSam Leffler if (sc->sc_tdma && sc->sc_nvaps == 0) { 119910ad9a77SSam Leffler sc->sc_tdma = 0; 120010ad9a77SSam Leffler sc->sc_swbmiss = 0; 120110ad9a77SSam Leffler } 120210ad9a77SSam Leffler #endif 1203b032f27cSSam Leffler free(avp, M_80211_VAP); 1204b032f27cSSam Leffler 1205b032f27cSSam Leffler if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 1206b032f27cSSam Leffler /* 1207b032f27cSSam Leffler * Restart rx+tx machines if still running (RUNNING will 1208b032f27cSSam Leffler * be reset if we just destroyed the last vap). 1209b032f27cSSam Leffler */ 1210b032f27cSSam Leffler if (ath_startrecv(sc) != 0) 1211b032f27cSSam Leffler if_printf(ifp, "%s: unable to restart recv logic\n", 1212b032f27cSSam Leffler __func__); 1213c89b957aSSam Leffler if (sc->sc_beacons) { /* restart beacons */ 1214c89b957aSSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 1215c89b957aSSam Leffler if (sc->sc_tdma) 1216c89b957aSSam Leffler ath_tdma_config(sc, NULL); 1217c89b957aSSam Leffler else 1218c89b957aSSam Leffler #endif 1219b032f27cSSam Leffler ath_beacon_config(sc, NULL); 1220c89b957aSSam Leffler } 1221b032f27cSSam Leffler ath_hal_intrset(ah, sc->sc_imask); 1222b032f27cSSam Leffler } 1223*16d4de92SAdrian Chadd ATH_UNLOCK(sc); 1224b032f27cSSam Leffler } 1225b032f27cSSam Leffler 12265591b213SSam Leffler void 12275591b213SSam Leffler ath_suspend(struct ath_softc *sc) 12285591b213SSam Leffler { 1229fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 1230d3ac945bSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 12315591b213SSam Leffler 1232c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n", 1233c42a7b7eSSam Leffler __func__, ifp->if_flags); 12345591b213SSam Leffler 1235d3ac945bSSam Leffler sc->sc_resume_up = (ifp->if_flags & IFF_UP) != 0; 1236d3ac945bSSam Leffler if (ic->ic_opmode == IEEE80211_M_STA) 12375591b213SSam Leffler ath_stop(ifp); 1238d3ac945bSSam Leffler else 1239d3ac945bSSam Leffler ieee80211_suspend_all(ic); 1240d3ac945bSSam Leffler /* 1241d3ac945bSSam Leffler * NB: don't worry about putting the chip in low power 1242d3ac945bSSam Leffler * mode; pci will power off our socket on suspend and 1243f29b8b7fSWarner Losh * CardBus detaches the device. 1244d3ac945bSSam Leffler */ 1245d3ac945bSSam Leffler } 1246d3ac945bSSam Leffler 1247d3ac945bSSam Leffler /* 1248d3ac945bSSam Leffler * Reset the key cache since some parts do not reset the 1249d3ac945bSSam Leffler * contents on resume. First we clear all entries, then 1250d3ac945bSSam Leffler * re-load keys that the 802.11 layer assumes are setup 1251d3ac945bSSam Leffler * in h/w. 1252d3ac945bSSam Leffler */ 1253d3ac945bSSam Leffler static void 1254d3ac945bSSam Leffler ath_reset_keycache(struct ath_softc *sc) 1255d3ac945bSSam Leffler { 1256d3ac945bSSam Leffler struct ifnet *ifp = sc->sc_ifp; 1257d3ac945bSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 1258d3ac945bSSam Leffler struct ath_hal *ah = sc->sc_ah; 1259d3ac945bSSam Leffler int i; 1260d3ac945bSSam Leffler 1261d3ac945bSSam Leffler for (i = 0; i < sc->sc_keymax; i++) 1262d3ac945bSSam Leffler ath_hal_keyreset(ah, i); 1263d3ac945bSSam Leffler ieee80211_crypto_reload_keys(ic); 12645591b213SSam Leffler } 12655591b213SSam Leffler 12665591b213SSam Leffler void 12675591b213SSam Leffler ath_resume(struct ath_softc *sc) 12685591b213SSam Leffler { 1269fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 1270d3ac945bSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 1271d3ac945bSSam Leffler struct ath_hal *ah = sc->sc_ah; 1272d3ac945bSSam Leffler HAL_STATUS status; 12735591b213SSam Leffler 1274c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n", 1275c42a7b7eSSam Leffler __func__, ifp->if_flags); 12765591b213SSam Leffler 1277d3ac945bSSam Leffler /* 1278d3ac945bSSam Leffler * Must reset the chip before we reload the 1279d3ac945bSSam Leffler * keycache as we were powered down on suspend. 1280d3ac945bSSam Leffler */ 1281054d7b69SSam Leffler ath_hal_reset(ah, sc->sc_opmode, 1282054d7b69SSam Leffler sc->sc_curchan != NULL ? sc->sc_curchan : ic->ic_curchan, 1283054d7b69SSam Leffler AH_FALSE, &status); 1284d3ac945bSSam Leffler ath_reset_keycache(sc); 12857e5eb44dSAdrian Chadd 12867e5eb44dSAdrian Chadd /* Let DFS at it in case it's a DFS channel */ 12877e5eb44dSAdrian Chadd ath_dfs_radar_enable(sc, ic->ic_curchan); 12887e5eb44dSAdrian Chadd 1289d3ac945bSSam Leffler if (sc->sc_resume_up) { 1290d3ac945bSSam Leffler if (ic->ic_opmode == IEEE80211_M_STA) { 1291fc74a9f9SBrooks Davis ath_init(sc); 1292394f34a5SSam Leffler /* 1293394f34a5SSam Leffler * Program the beacon registers using the last rx'd 1294394f34a5SSam Leffler * beacon frame and enable sync on the next beacon 1295394f34a5SSam Leffler * we see. This should handle the case where we 1296394f34a5SSam Leffler * wakeup and find the same AP and also the case where 1297394f34a5SSam Leffler * we wakeup and need to roam. For the latter we 1298394f34a5SSam Leffler * should get bmiss events that trigger a roam. 1299394f34a5SSam Leffler */ 1300394f34a5SSam Leffler ath_beacon_config(sc, NULL); 1301394f34a5SSam Leffler sc->sc_syncbeacon = 1; 1302d3ac945bSSam Leffler } else 1303d3ac945bSSam Leffler ieee80211_resume_all(ic); 13045591b213SSam Leffler } 1305b50c8bdeSSam Leffler if (sc->sc_softled) { 1306869ff02eSSam Leffler ath_hal_gpioCfgOutput(ah, sc->sc_ledpin, 1307869ff02eSSam Leffler HAL_GPIO_MUX_MAC_NETWORK_LED); 1308d3ac945bSSam Leffler ath_hal_gpioset(ah, sc->sc_ledpin, !sc->sc_ledon); 1309b50c8bdeSSam Leffler } 13102fd9aabbSAdrian Chadd 13112fd9aabbSAdrian Chadd /* XXX beacons ? */ 13126b59f5e3SSam Leffler } 13135591b213SSam Leffler 13145591b213SSam Leffler void 13155591b213SSam Leffler ath_shutdown(struct ath_softc *sc) 13165591b213SSam Leffler { 1317fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 13185591b213SSam Leffler 1319c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n", 1320c42a7b7eSSam Leffler __func__, ifp->if_flags); 13215591b213SSam Leffler 13225591b213SSam Leffler ath_stop(ifp); 1323d3ac945bSSam Leffler /* NB: no point powering down chip as we're about to reboot */ 13245591b213SSam Leffler } 13255591b213SSam Leffler 1326c42a7b7eSSam Leffler /* 1327c42a7b7eSSam Leffler * Interrupt handler. Most of the actual processing is deferred. 1328c42a7b7eSSam Leffler */ 13295591b213SSam Leffler void 13305591b213SSam Leffler ath_intr(void *arg) 13315591b213SSam Leffler { 13325591b213SSam Leffler struct ath_softc *sc = arg; 1333fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 13345591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 13356f5fe81eSAdrian Chadd HAL_INT status = 0; 13368f939e79SAdrian Chadd uint32_t txqs; 13375591b213SSam Leffler 13385591b213SSam Leffler if (sc->sc_invalid) { 13395591b213SSam Leffler /* 1340b58b3803SSam Leffler * The hardware is not ready/present, don't touch anything. 1341b58b3803SSam Leffler * Note this can happen early on if the IRQ is shared. 13425591b213SSam Leffler */ 1343c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid; ignored\n", __func__); 13445591b213SSam Leffler return; 13455591b213SSam Leffler } 1346fdd758d4SSam Leffler if (!ath_hal_intrpend(ah)) /* shared irq, not for us */ 1347fdd758d4SSam Leffler return; 134868e8e04eSSam Leffler if ((ifp->if_flags & IFF_UP) == 0 || 134968e8e04eSSam Leffler (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { 135068e8e04eSSam Leffler HAL_INT status; 135168e8e04eSSam Leffler 1352c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags 0x%x\n", 1353c42a7b7eSSam Leffler __func__, ifp->if_flags); 13545591b213SSam Leffler ath_hal_getisr(ah, &status); /* clear ISR */ 13555591b213SSam Leffler ath_hal_intrset(ah, 0); /* disable further intr's */ 13565591b213SSam Leffler return; 13575591b213SSam Leffler } 1358c42a7b7eSSam Leffler /* 1359c42a7b7eSSam Leffler * Figure out the reason(s) for the interrupt. Note 1360c42a7b7eSSam Leffler * that the hal returns a pseudo-ISR that may include 1361c42a7b7eSSam Leffler * bits we haven't explicitly enabled so we mask the 1362c42a7b7eSSam Leffler * value to insure we only process bits we requested. 1363c42a7b7eSSam Leffler */ 13645591b213SSam Leffler ath_hal_getisr(ah, &status); /* NB: clears ISR too */ 1365c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_INTR, "%s: status 0x%x\n", __func__, status); 1366f52d3452SAdrian Chadd CTR1(ATH_KTR_INTR, "ath_intr: mask=0x%.8x", status); 1367f52d3452SAdrian Chadd CTR5(ATH_KTR_INTR, 1368f52d3452SAdrian Chadd "ath_intr: ISR=0x%.8x, ISR_S0=0x%.8x, ISR_S1=0x%.8x, ISR_S2=0x%.8x, ISR_S5=0x%.8x", 1369f52d3452SAdrian Chadd ah->ah_intrstate[0], 1370f52d3452SAdrian Chadd ah->ah_intrstate[1], 1371f52d3452SAdrian Chadd ah->ah_intrstate[2], 1372f52d3452SAdrian Chadd ah->ah_intrstate[3], 1373f52d3452SAdrian Chadd ah->ah_intrstate[6]); 1374ecddff40SSam Leffler status &= sc->sc_imask; /* discard unasked for bits */ 13756f5fe81eSAdrian Chadd 13766f5fe81eSAdrian Chadd /* Short-circuit un-handled interrupts */ 13776f5fe81eSAdrian Chadd if (status == 0x0) 13786f5fe81eSAdrian Chadd return; 13796f5fe81eSAdrian Chadd 13805591b213SSam Leffler if (status & HAL_INT_FATAL) { 13815591b213SSam Leffler sc->sc_stats.ast_hardware++; 13825591b213SSam Leffler ath_hal_intrset(ah, 0); /* disable intr's until reset */ 138316c8acaaSSam Leffler ath_fatal_proc(sc, 0); 13845591b213SSam Leffler } else { 1385c42a7b7eSSam Leffler if (status & HAL_INT_SWBA) { 1386c42a7b7eSSam Leffler /* 1387c42a7b7eSSam Leffler * Software beacon alert--time to send a beacon. 1388c42a7b7eSSam Leffler * Handle beacon transmission directly; deferring 1389c42a7b7eSSam Leffler * this is too slow to meet timing constraints 1390c42a7b7eSSam Leffler * under load. 1391c42a7b7eSSam Leffler */ 1392584f7327SSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 139310ad9a77SSam Leffler if (sc->sc_tdma) { 139410ad9a77SSam Leffler if (sc->sc_tdmaswba == 0) { 139510ad9a77SSam Leffler struct ieee80211com *ic = ifp->if_l2com; 139610ad9a77SSam Leffler struct ieee80211vap *vap = 139710ad9a77SSam Leffler TAILQ_FIRST(&ic->ic_vaps); 139810ad9a77SSam Leffler ath_tdma_beacon_send(sc, vap); 139910ad9a77SSam Leffler sc->sc_tdmaswba = 140010ad9a77SSam Leffler vap->iv_tdma->tdma_bintval; 140110ad9a77SSam Leffler } else 140210ad9a77SSam Leffler sc->sc_tdmaswba--; 140310ad9a77SSam Leffler } else 140410ad9a77SSam Leffler #endif 1405339ccfb3SSam Leffler { 1406c42a7b7eSSam Leffler ath_beacon_proc(sc, 0); 1407339ccfb3SSam Leffler #ifdef IEEE80211_SUPPORT_SUPERG 1408339ccfb3SSam Leffler /* 1409339ccfb3SSam Leffler * Schedule the rx taskq in case there's no 1410339ccfb3SSam Leffler * traffic so any frames held on the staging 1411339ccfb3SSam Leffler * queue are aged and potentially flushed. 1412339ccfb3SSam Leffler */ 1413339ccfb3SSam Leffler taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask); 1414339ccfb3SSam Leffler #endif 1415339ccfb3SSam Leffler } 1416c42a7b7eSSam Leffler } 14175591b213SSam Leffler if (status & HAL_INT_RXEOL) { 14188f939e79SAdrian Chadd int imask; 1419f52d3452SAdrian Chadd CTR0(ATH_KTR_ERR, "ath_intr: RXEOL"); 14205591b213SSam Leffler /* 14215591b213SSam Leffler * NB: the hardware should re-read the link when 14225591b213SSam Leffler * RXE bit is written, but it doesn't work at 14235591b213SSam Leffler * least on older hardware revs. 14245591b213SSam Leffler */ 14255591b213SSam Leffler sc->sc_stats.ast_rxeol++; 142673f895fcSAdrian Chadd /* 142773f895fcSAdrian Chadd * Disable RXEOL/RXORN - prevent an interrupt 142873f895fcSAdrian Chadd * storm until the PCU logic can be reset. 14291fdadc0fSAdrian Chadd * In case the interface is reset some other 14301fdadc0fSAdrian Chadd * way before "sc_kickpcu" is called, don't 14311fdadc0fSAdrian Chadd * modify sc_imask - that way if it is reset 14321fdadc0fSAdrian Chadd * by a call to ath_reset() somehow, the 14331fdadc0fSAdrian Chadd * interrupt mask will be correctly reprogrammed. 143473f895fcSAdrian Chadd */ 14358f939e79SAdrian Chadd ATH_LOCK(sc); 14368f939e79SAdrian Chadd imask = sc->sc_imask; 14371fdadc0fSAdrian Chadd imask &= ~(HAL_INT_RXEOL | HAL_INT_RXORN); 14381fdadc0fSAdrian Chadd ath_hal_intrset(ah, imask); 14391fdadc0fSAdrian Chadd /* 14408f939e79SAdrian Chadd * Only blank sc_rxlink if we've not yet kicked 14418f939e79SAdrian Chadd * the PCU. 14428f939e79SAdrian Chadd * 14438f939e79SAdrian Chadd * This isn't entirely correct - the correct solution 14448f939e79SAdrian Chadd * would be to have a PCU lock and engage that for 14458f939e79SAdrian Chadd * the duration of the PCU fiddling; which would include 14468f939e79SAdrian Chadd * running the RX process. Otherwise we could end up 14478f939e79SAdrian Chadd * messing up the RX descriptor chain and making the 14488f939e79SAdrian Chadd * RX desc list much shorter. 14498f939e79SAdrian Chadd */ 14508f939e79SAdrian Chadd if (! sc->sc_kickpcu) 14518f939e79SAdrian Chadd sc->sc_rxlink = NULL; 14528f939e79SAdrian Chadd sc->sc_kickpcu = 1; 14538f939e79SAdrian Chadd ATH_UNLOCK(sc); 14548f939e79SAdrian Chadd /* 14551fdadc0fSAdrian Chadd * Enqueue an RX proc, to handled whatever 14561fdadc0fSAdrian Chadd * is in the RX queue. 14571fdadc0fSAdrian Chadd * This will then kick the PCU. 14581fdadc0fSAdrian Chadd */ 14591fdadc0fSAdrian Chadd taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask); 14605591b213SSam Leffler } 14615591b213SSam Leffler if (status & HAL_INT_TXURN) { 14625591b213SSam Leffler sc->sc_stats.ast_txurn++; 14635591b213SSam Leffler /* bump tx trigger level */ 14645591b213SSam Leffler ath_hal_updatetxtriglevel(ah, AH_TRUE); 14655591b213SSam Leffler } 14668f939e79SAdrian Chadd if (status & HAL_INT_RX) { 14678f939e79SAdrian Chadd sc->sc_stats.ast_rx_intr++; 14680bbf5441SSam Leffler taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask); 14698f939e79SAdrian Chadd } 14708f939e79SAdrian Chadd if (status & HAL_INT_TX) { 14718f939e79SAdrian Chadd sc->sc_stats.ast_tx_intr++; 14728f939e79SAdrian Chadd /* 14738f939e79SAdrian Chadd * Grab all the currently set bits in the HAL txq bitmap 14748f939e79SAdrian Chadd * and blank them. This is the only place we should be 14758f939e79SAdrian Chadd * doing this. 14768f939e79SAdrian Chadd */ 14778f939e79SAdrian Chadd ATH_LOCK(sc); 14788f939e79SAdrian Chadd txqs = 0xffffffff; 14798f939e79SAdrian Chadd ath_hal_gettxintrtxqs(sc->sc_ah, &txqs); 14808f939e79SAdrian Chadd sc->sc_txq_active |= txqs; 14818f939e79SAdrian Chadd ATH_UNLOCK(sc); 14820bbf5441SSam Leffler taskqueue_enqueue(sc->sc_tq, &sc->sc_txtask); 14838f939e79SAdrian Chadd } 14845591b213SSam Leffler if (status & HAL_INT_BMISS) { 14855591b213SSam Leffler sc->sc_stats.ast_bmiss++; 14860bbf5441SSam Leffler taskqueue_enqueue(sc->sc_tq, &sc->sc_bmisstask); 14875591b213SSam Leffler } 14886ad02dbaSAdrian Chadd if (status & HAL_INT_GTT) 14896ad02dbaSAdrian Chadd sc->sc_stats.ast_tx_timeout++; 14905594f5c0SAdrian Chadd if (status & HAL_INT_CST) 14915594f5c0SAdrian Chadd sc->sc_stats.ast_tx_cst++; 1492c42a7b7eSSam Leffler if (status & HAL_INT_MIB) { 1493c42a7b7eSSam Leffler sc->sc_stats.ast_mib++; 1494c42a7b7eSSam Leffler /* 1495c42a7b7eSSam Leffler * Disable interrupts until we service the MIB 1496c42a7b7eSSam Leffler * interrupt; otherwise it will continue to fire. 1497c42a7b7eSSam Leffler */ 1498c42a7b7eSSam Leffler ath_hal_intrset(ah, 0); 1499c42a7b7eSSam Leffler /* 1500c42a7b7eSSam Leffler * Let the hal handle the event. We assume it will 1501c42a7b7eSSam Leffler * clear whatever condition caused the interrupt. 1502c42a7b7eSSam Leffler */ 1503ffa2cab6SSam Leffler ath_hal_mibevent(ah, &sc->sc_halstats); 15048f939e79SAdrian Chadd /* 15058f939e79SAdrian Chadd * Don't reset the interrupt if we've just 15068f939e79SAdrian Chadd * kicked the PCU, or we may get a nested 15078f939e79SAdrian Chadd * RXEOL before the rxproc has had a chance 15088f939e79SAdrian Chadd * to run. 15098f939e79SAdrian Chadd */ 15108f939e79SAdrian Chadd ATH_LOCK(sc); 15118f939e79SAdrian Chadd if (sc->sc_kickpcu == 0) 1512c42a7b7eSSam Leffler ath_hal_intrset(ah, sc->sc_imask); 15138f939e79SAdrian Chadd ATH_UNLOCK(sc); 1514c42a7b7eSSam Leffler } 15159c4fc1e8SSam Leffler if (status & HAL_INT_RXORN) { 15169c4fc1e8SSam Leffler /* NB: hal marks HAL_INT_FATAL when RXORN is fatal */ 1517f52d3452SAdrian Chadd CTR0(ATH_KTR_ERR, "ath_intr: RXORN"); 15189c4fc1e8SSam Leffler sc->sc_stats.ast_rxorn++; 15199c4fc1e8SSam Leffler } 15205591b213SSam Leffler } 15215591b213SSam Leffler } 15225591b213SSam Leffler 15235591b213SSam Leffler static void 15245591b213SSam Leffler ath_fatal_proc(void *arg, int pending) 15255591b213SSam Leffler { 15265591b213SSam Leffler struct ath_softc *sc = arg; 1527fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 152816c8acaaSSam Leffler u_int32_t *state; 152916c8acaaSSam Leffler u_int32_t len; 153068e8e04eSSam Leffler void *sp; 15315591b213SSam Leffler 1532c42a7b7eSSam Leffler if_printf(ifp, "hardware error; resetting\n"); 153316c8acaaSSam Leffler /* 153416c8acaaSSam Leffler * Fatal errors are unrecoverable. Typically these 153516c8acaaSSam Leffler * are caused by DMA errors. Collect h/w state from 153616c8acaaSSam Leffler * the hal so we can diagnose what's going on. 153716c8acaaSSam Leffler */ 153868e8e04eSSam Leffler if (ath_hal_getfatalstate(sc->sc_ah, &sp, &len)) { 153916c8acaaSSam Leffler KASSERT(len >= 6*sizeof(u_int32_t), ("len %u bytes", len)); 154068e8e04eSSam Leffler state = sp; 154116c8acaaSSam Leffler if_printf(ifp, "0x%08x 0x%08x 0x%08x, 0x%08x 0x%08x 0x%08x\n", 154216c8acaaSSam Leffler state[0], state[1] , state[2], state[3], 154316c8acaaSSam Leffler state[4], state[5]); 154416c8acaaSSam Leffler } 1545517526efSAdrian Chadd ath_reset(ifp, ATH_RESET_NOLOSS); 15465591b213SSam Leffler } 15475591b213SSam Leffler 15485591b213SSam Leffler static void 1549b032f27cSSam Leffler ath_bmiss_vap(struct ieee80211vap *vap) 15505591b213SSam Leffler { 155159fbb257SSam Leffler /* 155259fbb257SSam Leffler * Workaround phantom bmiss interrupts by sanity-checking 155359fbb257SSam Leffler * the time of our last rx'd frame. If it is within the 155459fbb257SSam Leffler * beacon miss interval then ignore the interrupt. If it's 155559fbb257SSam Leffler * truly a bmiss we'll get another interrupt soon and that'll 155659fbb257SSam Leffler * be dispatched up for processing. Note this applies only 155759fbb257SSam Leffler * for h/w beacon miss events. 155859fbb257SSam Leffler */ 155959fbb257SSam Leffler if ((vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) == 0) { 1560a7ace843SSam Leffler struct ifnet *ifp = vap->iv_ic->ic_ifp; 1561a7ace843SSam Leffler struct ath_softc *sc = ifp->if_softc; 1562d7736e13SSam Leffler u_int64_t lastrx = sc->sc_lastrx; 1563d7736e13SSam Leffler u_int64_t tsf = ath_hal_gettsf64(sc->sc_ah); 1564d7736e13SSam Leffler u_int bmisstimeout = 1565b032f27cSSam Leffler vap->iv_bmissthreshold * vap->iv_bss->ni_intval * 1024; 1566d7736e13SSam Leffler 1567d7736e13SSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON, 1568d7736e13SSam Leffler "%s: tsf %llu lastrx %lld (%llu) bmiss %u\n", 1569d7736e13SSam Leffler __func__, (unsigned long long) tsf, 1570d7736e13SSam Leffler (unsigned long long)(tsf - lastrx), 1571d7736e13SSam Leffler (unsigned long long) lastrx, bmisstimeout); 157259fbb257SSam Leffler 157359fbb257SSam Leffler if (tsf - lastrx <= bmisstimeout) { 1574d7736e13SSam Leffler sc->sc_stats.ast_bmiss_phantom++; 157559fbb257SSam Leffler return; 157659fbb257SSam Leffler } 157759fbb257SSam Leffler } 157859fbb257SSam Leffler ATH_VAP(vap)->av_bmiss(vap); 1579e585d188SSam Leffler } 1580b032f27cSSam Leffler 1581459bc4f0SSam Leffler static int 1582459bc4f0SSam Leffler ath_hal_gethangstate(struct ath_hal *ah, uint32_t mask, uint32_t *hangs) 1583459bc4f0SSam Leffler { 1584459bc4f0SSam Leffler uint32_t rsize; 1585459bc4f0SSam Leffler void *sp; 1586459bc4f0SSam Leffler 158725c96056SAdrian Chadd if (!ath_hal_getdiagstate(ah, HAL_DIAG_CHECK_HANGS, &mask, sizeof(mask), &sp, &rsize)) 1588459bc4f0SSam Leffler return 0; 1589459bc4f0SSam Leffler KASSERT(rsize == sizeof(uint32_t), ("resultsize %u", rsize)); 1590459bc4f0SSam Leffler *hangs = *(uint32_t *)sp; 1591459bc4f0SSam Leffler return 1; 1592459bc4f0SSam Leffler } 1593459bc4f0SSam Leffler 1594b032f27cSSam Leffler static void 1595b032f27cSSam Leffler ath_bmiss_proc(void *arg, int pending) 1596b032f27cSSam Leffler { 1597b032f27cSSam Leffler struct ath_softc *sc = arg; 1598b032f27cSSam Leffler struct ifnet *ifp = sc->sc_ifp; 1599459bc4f0SSam Leffler uint32_t hangs; 1600b032f27cSSam Leffler 1601b032f27cSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: pending %u\n", __func__, pending); 1602459bc4f0SSam Leffler 1603459bc4f0SSam Leffler if (ath_hal_gethangstate(sc->sc_ah, 0xff, &hangs) && hangs != 0) { 16044fa8d4efSDaniel Eischen if_printf(ifp, "bb hang detected (0x%x), resetting\n", hangs); 1605517526efSAdrian Chadd ath_reset(ifp, ATH_RESET_NOLOSS); 1606459bc4f0SSam Leffler } else 1607b032f27cSSam Leffler ieee80211_beacon_miss(ifp->if_l2com); 16085591b213SSam Leffler } 16095591b213SSam Leffler 1610724c193aSSam Leffler /* 1611b032f27cSSam Leffler * Handle TKIP MIC setup to deal hardware that doesn't do MIC 1612b032f27cSSam Leffler * calcs together with WME. If necessary disable the crypto 1613b032f27cSSam Leffler * hardware and mark the 802.11 state so keys will be setup 1614b032f27cSSam Leffler * with the MIC work done in software. 1615b032f27cSSam Leffler */ 1616b032f27cSSam Leffler static void 1617b032f27cSSam Leffler ath_settkipmic(struct ath_softc *sc) 1618b032f27cSSam Leffler { 1619b032f27cSSam Leffler struct ifnet *ifp = sc->sc_ifp; 1620b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 1621b032f27cSSam Leffler 1622b032f27cSSam Leffler if ((ic->ic_cryptocaps & IEEE80211_CRYPTO_TKIP) && !sc->sc_wmetkipmic) { 1623b032f27cSSam Leffler if (ic->ic_flags & IEEE80211_F_WME) { 1624b032f27cSSam Leffler ath_hal_settkipmic(sc->sc_ah, AH_FALSE); 1625b032f27cSSam Leffler ic->ic_cryptocaps &= ~IEEE80211_CRYPTO_TKIPMIC; 1626b032f27cSSam Leffler } else { 1627b032f27cSSam Leffler ath_hal_settkipmic(sc->sc_ah, AH_TRUE); 1628b032f27cSSam Leffler ic->ic_cryptocaps |= IEEE80211_CRYPTO_TKIPMIC; 1629b032f27cSSam Leffler } 1630b032f27cSSam Leffler } 1631b032f27cSSam Leffler } 1632b032f27cSSam Leffler 16335591b213SSam Leffler static void 16345591b213SSam Leffler ath_init(void *arg) 16355591b213SSam Leffler { 16365591b213SSam Leffler struct ath_softc *sc = (struct ath_softc *) arg; 1637fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 1638b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 16395591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 16405591b213SSam Leffler HAL_STATUS status; 16415591b213SSam Leffler 1642c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags 0x%x\n", 1643c42a7b7eSSam Leffler __func__, ifp->if_flags); 16445591b213SSam Leffler 1645f0b2a0beSSam Leffler ATH_LOCK(sc); 16465591b213SSam Leffler /* 16475591b213SSam Leffler * Stop anything previously setup. This is safe 16485591b213SSam Leffler * whether this is the first time through or not. 16495591b213SSam Leffler */ 1650c42a7b7eSSam Leffler ath_stop_locked(ifp); 16515591b213SSam Leffler 16525591b213SSam Leffler /* 16535591b213SSam Leffler * The basic interface to setting the hardware in a good 16545591b213SSam Leffler * state is ``reset''. On return the hardware is known to 16555591b213SSam Leffler * be powered up and with interrupts disabled. This must 16565591b213SSam Leffler * be followed by initialization of the appropriate bits 16575591b213SSam Leffler * and then setup of the interrupt mask. 16585591b213SSam Leffler */ 1659b032f27cSSam Leffler ath_settkipmic(sc); 166059efa8b5SSam Leffler if (!ath_hal_reset(ah, sc->sc_opmode, ic->ic_curchan, AH_FALSE, &status)) { 16615591b213SSam Leffler if_printf(ifp, "unable to reset hardware; hal status %u\n", 16625591b213SSam Leffler status); 1663b032f27cSSam Leffler ATH_UNLOCK(sc); 1664b032f27cSSam Leffler return; 16655591b213SSam Leffler } 1666b032f27cSSam Leffler ath_chan_change(sc, ic->ic_curchan); 16675591b213SSam Leffler 166848237774SAdrian Chadd /* Let DFS at it in case it's a DFS channel */ 166948237774SAdrian Chadd ath_dfs_radar_enable(sc, ic->ic_curchan); 167048237774SAdrian Chadd 16715591b213SSam Leffler /* 1672c59005e9SSam Leffler * Likewise this is set during reset so update 1673c59005e9SSam Leffler * state cached in the driver. 1674c59005e9SSam Leffler */ 1675c59005e9SSam Leffler sc->sc_diversity = ath_hal_getdiversity(ah); 16762dc7fcc4SSam Leffler sc->sc_lastlongcal = 0; 16772dc7fcc4SSam Leffler sc->sc_resetcal = 1; 16782dc7fcc4SSam Leffler sc->sc_lastcalreset = 0; 1679a108ab63SAdrian Chadd sc->sc_lastani = 0; 1680a108ab63SAdrian Chadd sc->sc_lastshortcal = 0; 1681a108ab63SAdrian Chadd sc->sc_doresetcal = AH_FALSE; 16822fd9aabbSAdrian Chadd /* 16832fd9aabbSAdrian Chadd * Beacon timers were cleared here; give ath_newstate() 16842fd9aabbSAdrian Chadd * a hint that the beacon timers should be poked when 16852fd9aabbSAdrian Chadd * things transition to the RUN state. 16862fd9aabbSAdrian Chadd */ 16872fd9aabbSAdrian Chadd sc->sc_beacons = 0; 1688c42a7b7eSSam Leffler 1689c42a7b7eSSam Leffler /* 16908f939e79SAdrian Chadd * Initial aggregation settings. 16918f939e79SAdrian Chadd */ 16928f939e79SAdrian Chadd sc->sc_hwq_limit = ATH_AGGR_MIN_QDEPTH; 16938f939e79SAdrian Chadd sc->sc_tid_hwq_lo = ATH_AGGR_SCHED_LOW; 16948f939e79SAdrian Chadd sc->sc_tid_hwq_hi = ATH_AGGR_SCHED_HIGH; 16958f939e79SAdrian Chadd 16968f939e79SAdrian Chadd /* 16975591b213SSam Leffler * Setup the hardware after reset: the key cache 16985591b213SSam Leffler * is filled as needed and the receive engine is 16995591b213SSam Leffler * set going. Frame transmit is handled entirely 17005591b213SSam Leffler * in the frame output path; there's nothing to do 17015591b213SSam Leffler * here except setup the interrupt mask. 17025591b213SSam Leffler */ 17035591b213SSam Leffler if (ath_startrecv(sc) != 0) { 17045591b213SSam Leffler if_printf(ifp, "unable to start recv logic\n"); 1705b032f27cSSam Leffler ATH_UNLOCK(sc); 1706b032f27cSSam Leffler return; 17075591b213SSam Leffler } 17085591b213SSam Leffler 17095591b213SSam Leffler /* 17105591b213SSam Leffler * Enable interrupts. 17115591b213SSam Leffler */ 17125591b213SSam Leffler sc->sc_imask = HAL_INT_RX | HAL_INT_TX 17135591b213SSam Leffler | HAL_INT_RXEOL | HAL_INT_RXORN 17145591b213SSam Leffler | HAL_INT_FATAL | HAL_INT_GLOBAL; 1715c42a7b7eSSam Leffler /* 1716c42a7b7eSSam Leffler * Enable MIB interrupts when there are hardware phy counters. 1717c42a7b7eSSam Leffler * Note we only do this (at the moment) for station mode. 1718c42a7b7eSSam Leffler */ 1719c42a7b7eSSam Leffler if (sc->sc_needmib && ic->ic_opmode == IEEE80211_M_STA) 1720c42a7b7eSSam Leffler sc->sc_imask |= HAL_INT_MIB; 17215591b213SSam Leffler 17225594f5c0SAdrian Chadd /* Enable global TX timeout and carrier sense timeout if available */ 17236ad02dbaSAdrian Chadd if (ath_hal_gtxto_supported(ah)) 17243788ebedSAdrian Chadd sc->sc_imask |= HAL_INT_GTT; 1725d0a0ebc6SAdrian Chadd 1726d0a0ebc6SAdrian Chadd DPRINTF(sc, ATH_DEBUG_RESET, "%s: imask=0x%x\n", 1727d0a0ebc6SAdrian Chadd __func__, sc->sc_imask); 17286ad02dbaSAdrian Chadd 172913f4c340SRobert Watson ifp->if_drv_flags |= IFF_DRV_RUNNING; 17302e986da5SSam Leffler callout_reset(&sc->sc_wd_ch, hz, ath_watchdog, sc); 1731b032f27cSSam Leffler ath_hal_intrset(ah, sc->sc_imask); 17325591b213SSam Leffler 1733b032f27cSSam Leffler ATH_UNLOCK(sc); 1734b032f27cSSam Leffler 173586e07743SSam Leffler #ifdef ATH_TX99_DIAG 173686e07743SSam Leffler if (sc->sc_tx99 != NULL) 173786e07743SSam Leffler sc->sc_tx99->start(sc->sc_tx99); 173886e07743SSam Leffler else 173986e07743SSam Leffler #endif 1740b032f27cSSam Leffler ieee80211_start_all(ic); /* start all vap's */ 17415591b213SSam Leffler } 17425591b213SSam Leffler 17435591b213SSam Leffler static void 1744c42a7b7eSSam Leffler ath_stop_locked(struct ifnet *ifp) 17455591b213SSam Leffler { 17465591b213SSam Leffler struct ath_softc *sc = ifp->if_softc; 17475591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 17485591b213SSam Leffler 1749c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid %u if_flags 0x%x\n", 1750c42a7b7eSSam Leffler __func__, sc->sc_invalid, ifp->if_flags); 17515591b213SSam Leffler 1752c42a7b7eSSam Leffler ATH_LOCK_ASSERT(sc); 175313f4c340SRobert Watson if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 17545591b213SSam Leffler /* 17555591b213SSam Leffler * Shutdown the hardware and driver: 1756c42a7b7eSSam Leffler * reset 802.11 state machine 17575591b213SSam Leffler * turn off timers 1758c42a7b7eSSam Leffler * disable interrupts 1759c42a7b7eSSam Leffler * turn off the radio 17605591b213SSam Leffler * clear transmit machinery 17615591b213SSam Leffler * clear receive machinery 17625591b213SSam Leffler * drain and release tx queues 17635591b213SSam Leffler * reclaim beacon resources 17645591b213SSam Leffler * power down hardware 17655591b213SSam Leffler * 17665591b213SSam Leffler * Note that some of this work is not possible if the 17675591b213SSam Leffler * hardware is gone (invalid). 17685591b213SSam Leffler */ 176986e07743SSam Leffler #ifdef ATH_TX99_DIAG 177086e07743SSam Leffler if (sc->sc_tx99 != NULL) 177186e07743SSam Leffler sc->sc_tx99->stop(sc->sc_tx99); 177286e07743SSam Leffler #endif 17732e986da5SSam Leffler callout_stop(&sc->sc_wd_ch); 17742e986da5SSam Leffler sc->sc_wd_timer = 0; 177513f4c340SRobert Watson ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 1776c42a7b7eSSam Leffler if (!sc->sc_invalid) { 17773e50ec2cSSam Leffler if (sc->sc_softled) { 17783e50ec2cSSam Leffler callout_stop(&sc->sc_ledtimer); 17793e50ec2cSSam Leffler ath_hal_gpioset(ah, sc->sc_ledpin, 17803e50ec2cSSam Leffler !sc->sc_ledon); 17813e50ec2cSSam Leffler sc->sc_blinking = 0; 17823e50ec2cSSam Leffler } 17835591b213SSam Leffler ath_hal_intrset(ah, 0); 1784c42a7b7eSSam Leffler } 1785517526efSAdrian Chadd ath_draintxq(sc, ATH_RESET_DEFAULT); 1786c42a7b7eSSam Leffler if (!sc->sc_invalid) { 17875591b213SSam Leffler ath_stoprecv(sc); 1788c42a7b7eSSam Leffler ath_hal_phydisable(ah); 1789c42a7b7eSSam Leffler } else 17905591b213SSam Leffler sc->sc_rxlink = NULL; 1791b032f27cSSam Leffler ath_beacon_free(sc); /* XXX not needed */ 1792c42a7b7eSSam Leffler } 1793c42a7b7eSSam Leffler } 1794c42a7b7eSSam Leffler 1795c42a7b7eSSam Leffler static void 1796c42a7b7eSSam Leffler ath_stop(struct ifnet *ifp) 1797c42a7b7eSSam Leffler { 1798c42a7b7eSSam Leffler struct ath_softc *sc = ifp->if_softc; 1799c42a7b7eSSam Leffler 1800c42a7b7eSSam Leffler ATH_LOCK(sc); 1801c42a7b7eSSam Leffler ath_stop_locked(ifp); 1802f0b2a0beSSam Leffler ATH_UNLOCK(sc); 18035591b213SSam Leffler } 18045591b213SSam Leffler 18055591b213SSam Leffler /* 18065591b213SSam Leffler * Reset the hardware w/o losing operational state. This is 18075591b213SSam Leffler * basically a more efficient way of doing ath_stop, ath_init, 18085591b213SSam Leffler * followed by state transitions to the current 802.11 1809c42a7b7eSSam Leffler * operational state. Used to recover from various errors and 1810c42a7b7eSSam Leffler * to reset or reload hardware state. 18115591b213SSam Leffler */ 18126079fdbeSAdrian Chadd int 1813517526efSAdrian Chadd ath_reset(struct ifnet *ifp, ATH_RESET_TYPE reset_type) 18145591b213SSam Leffler { 1815c42a7b7eSSam Leffler struct ath_softc *sc = ifp->if_softc; 1816b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 18175591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 18185591b213SSam Leffler HAL_STATUS status; 18195591b213SSam Leffler 1820f52d3452SAdrian Chadd DPRINTF(sc, ATH_DEBUG_RESET, "%s: called\n", __func__); 1821*16d4de92SAdrian Chadd 18225591b213SSam Leffler ath_hal_intrset(ah, 0); /* disable interrupts */ 1823517526efSAdrian Chadd ath_draintxq(sc, reset_type); /* stop xmit side */ 1824f52d3452SAdrian Chadd /* 1825f52d3452SAdrian Chadd * XXX Don't flush if ATH_RESET_NOLOSS;but we have to first 1826f52d3452SAdrian Chadd * XXX need to ensure this doesn't race with an outstanding 1827f52d3452SAdrian Chadd * XXX taskqueue call. 1828f52d3452SAdrian Chadd */ 18295591b213SSam Leffler ath_stoprecv(sc); /* stop recv side */ 1830b032f27cSSam Leffler ath_settkipmic(sc); /* configure TKIP MIC handling */ 18315591b213SSam Leffler /* NB: indicate channel change so we do a full reset */ 183259efa8b5SSam Leffler if (!ath_hal_reset(ah, sc->sc_opmode, ic->ic_curchan, AH_TRUE, &status)) 18335591b213SSam Leffler if_printf(ifp, "%s: unable to reset hardware; hal status %u\n", 18345591b213SSam Leffler __func__, status); 1835c59005e9SSam Leffler sc->sc_diversity = ath_hal_getdiversity(ah); 183648237774SAdrian Chadd 183748237774SAdrian Chadd /* Let DFS at it in case it's a DFS channel */ 183848237774SAdrian Chadd ath_dfs_radar_enable(sc, ic->ic_curchan); 183948237774SAdrian Chadd 184068e8e04eSSam Leffler if (ath_startrecv(sc) != 0) /* restart recv */ 184168e8e04eSSam Leffler if_printf(ifp, "%s: unable to start recv logic\n", __func__); 1842c42a7b7eSSam Leffler /* 1843c42a7b7eSSam Leffler * We may be doing a reset in response to an ioctl 1844c42a7b7eSSam Leffler * that changes the channel so update any state that 1845c42a7b7eSSam Leffler * might change as a result. 1846c42a7b7eSSam Leffler */ 1847724c193aSSam Leffler ath_chan_change(sc, ic->ic_curchan); 1848c89b957aSSam Leffler if (sc->sc_beacons) { /* restart beacons */ 1849584f7327SSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 185010ad9a77SSam Leffler if (sc->sc_tdma) 185110ad9a77SSam Leffler ath_tdma_config(sc, NULL); 185210ad9a77SSam Leffler else 185310ad9a77SSam Leffler #endif 1854c89b957aSSam Leffler ath_beacon_config(sc, NULL); 185510ad9a77SSam Leffler } 1856c42a7b7eSSam Leffler ath_hal_intrset(ah, sc->sc_imask); 1857c42a7b7eSSam Leffler 1858c42a7b7eSSam Leffler ath_start(ifp); /* restart xmit */ 1859c42a7b7eSSam Leffler return 0; 18605591b213SSam Leffler } 18615591b213SSam Leffler 186268e8e04eSSam Leffler static int 1863b032f27cSSam Leffler ath_reset_vap(struct ieee80211vap *vap, u_long cmd) 1864b032f27cSSam Leffler { 18654b54a231SSam Leffler struct ieee80211com *ic = vap->iv_ic; 18664b54a231SSam Leffler struct ifnet *ifp = ic->ic_ifp; 18674b54a231SSam Leffler struct ath_softc *sc = ifp->if_softc; 18684b54a231SSam Leffler struct ath_hal *ah = sc->sc_ah; 18694b54a231SSam Leffler 18704b54a231SSam Leffler switch (cmd) { 18714b54a231SSam Leffler case IEEE80211_IOC_TXPOWER: 18724b54a231SSam Leffler /* 18734b54a231SSam Leffler * If per-packet TPC is enabled, then we have nothing 18744b54a231SSam Leffler * to do; otherwise we need to force the global limit. 18754b54a231SSam Leffler * All this can happen directly; no need to reset. 18764b54a231SSam Leffler */ 18774b54a231SSam Leffler if (!ath_hal_gettpc(ah)) 18784b54a231SSam Leffler ath_hal_settxpowlimit(ah, ic->ic_txpowlimit); 18794b54a231SSam Leffler return 0; 18804b54a231SSam Leffler } 1881517526efSAdrian Chadd /* XXX? Full or NOLOSS? */ 1882517526efSAdrian Chadd return ath_reset(ifp, ATH_RESET_FULL); 1883b032f27cSSam Leffler } 1884b032f27cSSam Leffler 1885b8e788a5SAdrian Chadd struct ath_buf * 188610ad9a77SSam Leffler _ath_getbuf_locked(struct ath_softc *sc) 188710ad9a77SSam Leffler { 188810ad9a77SSam Leffler struct ath_buf *bf; 188910ad9a77SSam Leffler 189010ad9a77SSam Leffler ATH_TXBUF_LOCK_ASSERT(sc); 189110ad9a77SSam Leffler 18926b349e5aSAdrian Chadd bf = TAILQ_FIRST(&sc->sc_txbuf); 189310ad9a77SSam Leffler if (bf != NULL && (bf->bf_flags & ATH_BUF_BUSY) == 0) 18946b349e5aSAdrian Chadd TAILQ_REMOVE(&sc->sc_txbuf, bf, bf_list); 189510ad9a77SSam Leffler else 189610ad9a77SSam Leffler bf = NULL; 189710ad9a77SSam Leffler if (bf == NULL) { 189810ad9a77SSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT, "%s: %s\n", __func__, 18996b349e5aSAdrian Chadd TAILQ_FIRST(&sc->sc_txbuf) == NULL ? 190010ad9a77SSam Leffler "out of xmit buffers" : "xmit buffer busy"); 190110ad9a77SSam Leffler } 190210ad9a77SSam Leffler return bf; 190310ad9a77SSam Leffler } 190410ad9a77SSam Leffler 1905b8e788a5SAdrian Chadd struct ath_buf * 190610ad9a77SSam Leffler ath_getbuf(struct ath_softc *sc) 190710ad9a77SSam Leffler { 190810ad9a77SSam Leffler struct ath_buf *bf; 190910ad9a77SSam Leffler 191010ad9a77SSam Leffler ATH_TXBUF_LOCK(sc); 191110ad9a77SSam Leffler bf = _ath_getbuf_locked(sc); 191210ad9a77SSam Leffler if (bf == NULL) { 191310ad9a77SSam Leffler struct ifnet *ifp = sc->sc_ifp; 191410ad9a77SSam Leffler 191510ad9a77SSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT, "%s: stop queue\n", __func__); 191610ad9a77SSam Leffler sc->sc_stats.ast_tx_qstop++; 191710ad9a77SSam Leffler ifp->if_drv_flags |= IFF_DRV_OACTIVE; 191810ad9a77SSam Leffler } 191910ad9a77SSam Leffler ATH_TXBUF_UNLOCK(sc); 192010ad9a77SSam Leffler return bf; 192110ad9a77SSam Leffler } 192210ad9a77SSam Leffler 19235591b213SSam Leffler static void 19245591b213SSam Leffler ath_start(struct ifnet *ifp) 19255591b213SSam Leffler { 19265591b213SSam Leffler struct ath_softc *sc = ifp->if_softc; 19275591b213SSam Leffler struct ieee80211_node *ni; 19285591b213SSam Leffler struct ath_buf *bf; 192968e8e04eSSam Leffler struct mbuf *m, *next; 193068e8e04eSSam Leffler ath_bufhead frags; 19315591b213SSam Leffler 193213f4c340SRobert Watson if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || sc->sc_invalid) 19335591b213SSam Leffler return; 19345591b213SSam Leffler for (;;) { 19355591b213SSam Leffler /* 19365591b213SSam Leffler * Grab a TX buffer and associated resources. 19375591b213SSam Leffler */ 193810ad9a77SSam Leffler bf = ath_getbuf(sc); 193910ad9a77SSam Leffler if (bf == NULL) 19405591b213SSam Leffler break; 19412b9411e2SSam Leffler 1942b032f27cSSam Leffler IFQ_DEQUEUE(&ifp->if_snd, m); 1943b032f27cSSam Leffler if (m == NULL) { 1944b032f27cSSam Leffler ATH_TXBUF_LOCK(sc); 19456b349e5aSAdrian Chadd TAILQ_INSERT_HEAD(&sc->sc_txbuf, bf, bf_list); 1946b032f27cSSam Leffler ATH_TXBUF_UNLOCK(sc); 1947b032f27cSSam Leffler break; 1948b032f27cSSam Leffler } 1949b032f27cSSam Leffler ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; 195068e8e04eSSam Leffler /* 195168e8e04eSSam Leffler * Check for fragmentation. If this frame 195268e8e04eSSam Leffler * has been broken up verify we have enough 195368e8e04eSSam Leffler * buffers to send all the fragments so all 195468e8e04eSSam Leffler * go out or none... 195568e8e04eSSam Leffler */ 19566b349e5aSAdrian Chadd TAILQ_INIT(&frags); 195768e8e04eSSam Leffler if ((m->m_flags & M_FRAG) && 195868e8e04eSSam Leffler !ath_txfrag_setup(sc, &frags, m, ni)) { 195968e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT, 196068e8e04eSSam Leffler "%s: out of txfrag buffers\n", __func__); 196136c6be9aSSam Leffler sc->sc_stats.ast_tx_nofrag++; 19629cb93076SSam Leffler ifp->if_oerrors++; 196368e8e04eSSam Leffler ath_freetx(m); 196468e8e04eSSam Leffler goto bad; 196568e8e04eSSam Leffler } 1966339ccfb3SSam Leffler ifp->if_opackets++; 196768e8e04eSSam Leffler nextfrag: 196868e8e04eSSam Leffler /* 196968e8e04eSSam Leffler * Pass the frame to the h/w for transmission. 197068e8e04eSSam Leffler * Fragmented frames have each frag chained together 197168e8e04eSSam Leffler * with m_nextpkt. We know there are sufficient ath_buf's 197268e8e04eSSam Leffler * to send all the frags because of work done by 197368e8e04eSSam Leffler * ath_txfrag_setup. We leave m_nextpkt set while 197468e8e04eSSam Leffler * calling ath_tx_start so it can use it to extend the 197568e8e04eSSam Leffler * the tx duration to cover the subsequent frag and 197668e8e04eSSam Leffler * so it can reclaim all the mbufs in case of an error; 197768e8e04eSSam Leffler * ath_tx_start clears m_nextpkt once it commits to 197868e8e04eSSam Leffler * handing the frame to the hardware. 197968e8e04eSSam Leffler */ 198068e8e04eSSam Leffler next = m->m_nextpkt; 19815591b213SSam Leffler if (ath_tx_start(sc, ni, bf, m)) { 19825591b213SSam Leffler bad: 19835591b213SSam Leffler ifp->if_oerrors++; 1984c42a7b7eSSam Leffler reclaim: 198568e8e04eSSam Leffler bf->bf_m = NULL; 198668e8e04eSSam Leffler bf->bf_node = NULL; 1987c42a7b7eSSam Leffler ATH_TXBUF_LOCK(sc); 19886b349e5aSAdrian Chadd TAILQ_INSERT_HEAD(&sc->sc_txbuf, bf, bf_list); 198968e8e04eSSam Leffler ath_txfrag_cleanup(sc, &frags, ni); 1990c42a7b7eSSam Leffler ATH_TXBUF_UNLOCK(sc); 1991c42a7b7eSSam Leffler if (ni != NULL) 1992c42a7b7eSSam Leffler ieee80211_free_node(ni); 19935591b213SSam Leffler continue; 19945591b213SSam Leffler } 199568e8e04eSSam Leffler if (next != NULL) { 199668e8e04eSSam Leffler /* 199768e8e04eSSam Leffler * Beware of state changing between frags. 199868e8e04eSSam Leffler * XXX check sta power-save state? 199968e8e04eSSam Leffler */ 2000b032f27cSSam Leffler if (ni->ni_vap->iv_state != IEEE80211_S_RUN) { 200168e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT, 200268e8e04eSSam Leffler "%s: flush fragmented packet, state %s\n", 200368e8e04eSSam Leffler __func__, 2004b032f27cSSam Leffler ieee80211_state_name[ni->ni_vap->iv_state]); 200568e8e04eSSam Leffler ath_freetx(next); 200668e8e04eSSam Leffler goto reclaim; 200768e8e04eSSam Leffler } 200868e8e04eSSam Leffler m = next; 20096b349e5aSAdrian Chadd bf = TAILQ_FIRST(&frags); 201068e8e04eSSam Leffler KASSERT(bf != NULL, ("no buf for txfrag")); 20116b349e5aSAdrian Chadd TAILQ_REMOVE(&frags, bf, bf_list); 201268e8e04eSSam Leffler goto nextfrag; 201368e8e04eSSam Leffler } 20145591b213SSam Leffler 20152e986da5SSam Leffler sc->sc_wd_timer = 5; 20165591b213SSam Leffler } 20175591b213SSam Leffler } 20185591b213SSam Leffler 20195591b213SSam Leffler static int 20205591b213SSam Leffler ath_media_change(struct ifnet *ifp) 20215591b213SSam Leffler { 2022b032f27cSSam Leffler int error = ieee80211_media_change(ifp); 2023b032f27cSSam Leffler /* NB: only the fixed rate can change and that doesn't need a reset */ 2024b032f27cSSam Leffler return (error == ENETRESET ? 0 : error); 20255591b213SSam Leffler } 20265591b213SSam Leffler 2027c42a7b7eSSam Leffler /* 2028c42a7b7eSSam Leffler * Block/unblock tx+rx processing while a key change is done. 2029c42a7b7eSSam Leffler * We assume the caller serializes key management operations 2030c42a7b7eSSam Leffler * so we only need to worry about synchronization with other 2031c42a7b7eSSam Leffler * uses that originate in the driver. 2032c42a7b7eSSam Leffler */ 2033c42a7b7eSSam Leffler static void 2034b032f27cSSam Leffler ath_key_update_begin(struct ieee80211vap *vap) 2035c42a7b7eSSam Leffler { 2036b032f27cSSam Leffler struct ifnet *ifp = vap->iv_ic->ic_ifp; 2037c42a7b7eSSam Leffler struct ath_softc *sc = ifp->if_softc; 2038c42a7b7eSSam Leffler 2039c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__); 2040b032f27cSSam Leffler taskqueue_block(sc->sc_tq); 2041c42a7b7eSSam Leffler IF_LOCK(&ifp->if_snd); /* NB: doesn't block mgmt frames */ 2042c42a7b7eSSam Leffler } 2043c42a7b7eSSam Leffler 2044c42a7b7eSSam Leffler static void 2045b032f27cSSam Leffler ath_key_update_end(struct ieee80211vap *vap) 2046c42a7b7eSSam Leffler { 2047b032f27cSSam Leffler struct ifnet *ifp = vap->iv_ic->ic_ifp; 2048c42a7b7eSSam Leffler struct ath_softc *sc = ifp->if_softc; 2049c42a7b7eSSam Leffler 2050c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__); 2051c42a7b7eSSam Leffler IF_UNLOCK(&ifp->if_snd); 2052b032f27cSSam Leffler taskqueue_unblock(sc->sc_tq); 2053c42a7b7eSSam Leffler } 20545591b213SSam Leffler 20554bc0e754SSam Leffler /* 20564bc0e754SSam Leffler * Calculate the receive filter according to the 20574bc0e754SSam Leffler * operating mode and state: 20584bc0e754SSam Leffler * 20594bc0e754SSam Leffler * o always accept unicast, broadcast, and multicast traffic 2060b032f27cSSam Leffler * o accept PHY error frames when hardware doesn't have MIB support 2061411373ebSSam Leffler * to count and we need them for ANI (sta mode only until recently) 2062b032f27cSSam Leffler * and we are not scanning (ANI is disabled) 2063411373ebSSam Leffler * NB: older hal's add rx filter bits out of sight and we need to 2064411373ebSSam Leffler * blindly preserve them 20654bc0e754SSam Leffler * o probe request frames are accepted only when operating in 206659aa14a9SRui Paulo * hostap, adhoc, mesh, or monitor modes 2067b032f27cSSam Leffler * o enable promiscuous mode 2068b032f27cSSam Leffler * - when in monitor mode 2069b032f27cSSam Leffler * - if interface marked PROMISC (assumes bridge setting is filtered) 20704bc0e754SSam Leffler * o accept beacons: 20714bc0e754SSam Leffler * - when operating in station mode for collecting rssi data when 20724bc0e754SSam Leffler * the station is otherwise quiet, or 2073b032f27cSSam Leffler * - when operating in adhoc mode so the 802.11 layer creates 2074b032f27cSSam Leffler * node table entries for peers, 20754bc0e754SSam Leffler * - when scanning 2076b032f27cSSam Leffler * - when doing s/w beacon miss (e.g. for ap+sta) 2077b032f27cSSam Leffler * - when operating in ap mode in 11g to detect overlapping bss that 2078b032f27cSSam Leffler * require protection 207959aa14a9SRui Paulo * - when operating in mesh mode to detect neighbors 20806f48c956SSam Leffler * o accept control frames: 20816f48c956SSam Leffler * - when in monitor mode 2082b032f27cSSam Leffler * XXX HT protection for 11n 20834bc0e754SSam Leffler */ 20844bc0e754SSam Leffler static u_int32_t 208568e8e04eSSam Leffler ath_calcrxfilter(struct ath_softc *sc) 20864bc0e754SSam Leffler { 2087fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 2088b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 20894bc0e754SSam Leffler u_int32_t rfilt; 20904bc0e754SSam Leffler 2091b032f27cSSam Leffler rfilt = HAL_RX_FILTER_UCAST | HAL_RX_FILTER_BCAST | HAL_RX_FILTER_MCAST; 2092411373ebSSam Leffler if (!sc->sc_needmib && !sc->sc_scanning) 2093411373ebSSam Leffler rfilt |= HAL_RX_FILTER_PHYERR; 20944bc0e754SSam Leffler if (ic->ic_opmode != IEEE80211_M_STA) 20954bc0e754SSam Leffler rfilt |= HAL_RX_FILTER_PROBEREQ; 20965463c4a4SSam Leffler /* XXX ic->ic_monvaps != 0? */ 2097b032f27cSSam Leffler if (ic->ic_opmode == IEEE80211_M_MONITOR || (ifp->if_flags & IFF_PROMISC)) 20984bc0e754SSam Leffler rfilt |= HAL_RX_FILTER_PROM; 20994bc0e754SSam Leffler if (ic->ic_opmode == IEEE80211_M_STA || 210047db982fSSam Leffler ic->ic_opmode == IEEE80211_M_IBSS || 2101b032f27cSSam Leffler sc->sc_swbmiss || sc->sc_scanning) 2102b032f27cSSam Leffler rfilt |= HAL_RX_FILTER_BEACON; 2103b032f27cSSam Leffler /* 2104b032f27cSSam Leffler * NB: We don't recalculate the rx filter when 2105b032f27cSSam Leffler * ic_protmode changes; otherwise we could do 2106b032f27cSSam Leffler * this only when ic_protmode != NONE. 2107b032f27cSSam Leffler */ 2108b032f27cSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP && 2109b032f27cSSam Leffler IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) 21104bc0e754SSam Leffler rfilt |= HAL_RX_FILTER_BEACON; 2111f378d4c8SAdrian Chadd 2112f378d4c8SAdrian Chadd /* 21134aa18e9dSAdrian Chadd * Enable hardware PS-POLL RX only for hostap mode; 2114f378d4c8SAdrian Chadd * STA mode sends PS-POLL frames but never 21154aa18e9dSAdrian Chadd * receives them. 2116f378d4c8SAdrian Chadd */ 2117dce0bccaSAdrian Chadd if (ath_hal_getcapability(sc->sc_ah, HAL_CAP_PSPOLL, 2118f378d4c8SAdrian Chadd 0, NULL) == HAL_OK && 2119f378d4c8SAdrian Chadd ic->ic_opmode == IEEE80211_M_HOSTAP) 2120f378d4c8SAdrian Chadd rfilt |= HAL_RX_FILTER_PSPOLL; 2121f378d4c8SAdrian Chadd 2122fe0dd789SSam Leffler if (sc->sc_nmeshvaps) { 212359aa14a9SRui Paulo rfilt |= HAL_RX_FILTER_BEACON; 212459aa14a9SRui Paulo if (sc->sc_hasbmatch) 212559aa14a9SRui Paulo rfilt |= HAL_RX_FILTER_BSSID; 212659aa14a9SRui Paulo else 212759aa14a9SRui Paulo rfilt |= HAL_RX_FILTER_PROM; 212859aa14a9SRui Paulo } 21296f48c956SSam Leffler if (ic->ic_opmode == IEEE80211_M_MONITOR) 21306f48c956SSam Leffler rfilt |= HAL_RX_FILTER_CONTROL; 2131f378d4c8SAdrian Chadd 2132f378d4c8SAdrian Chadd /* 2133f378d4c8SAdrian Chadd * Enable RX of compressed BAR frames only when doing 2134f378d4c8SAdrian Chadd * 802.11n. Required for A-MPDU. 2135f378d4c8SAdrian Chadd */ 2136a83df4d3SAdrian Chadd if (IEEE80211_IS_CHAN_HT(ic->ic_curchan)) 2137a83df4d3SAdrian Chadd rfilt |= HAL_RX_FILTER_COMPBAR; 2138f378d4c8SAdrian Chadd 2139b032f27cSSam Leffler DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x, %s if_flags 0x%x\n", 2140b032f27cSSam Leffler __func__, rfilt, ieee80211_opmode_name[ic->ic_opmode], ifp->if_flags); 21414bc0e754SSam Leffler return rfilt; 2142b032f27cSSam Leffler } 2143b032f27cSSam Leffler 2144b032f27cSSam Leffler static void 2145b032f27cSSam Leffler ath_update_promisc(struct ifnet *ifp) 2146b032f27cSSam Leffler { 2147b032f27cSSam Leffler struct ath_softc *sc = ifp->if_softc; 2148b032f27cSSam Leffler u_int32_t rfilt; 2149b032f27cSSam Leffler 2150b032f27cSSam Leffler /* configure rx filter */ 2151b032f27cSSam Leffler rfilt = ath_calcrxfilter(sc); 2152b032f27cSSam Leffler ath_hal_setrxfilter(sc->sc_ah, rfilt); 2153b032f27cSSam Leffler 2154b032f27cSSam Leffler DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x\n", __func__, rfilt); 2155b032f27cSSam Leffler } 2156b032f27cSSam Leffler 2157b032f27cSSam Leffler static void 2158b032f27cSSam Leffler ath_update_mcast(struct ifnet *ifp) 2159b032f27cSSam Leffler { 2160b032f27cSSam Leffler struct ath_softc *sc = ifp->if_softc; 2161b032f27cSSam Leffler u_int32_t mfilt[2]; 2162b032f27cSSam Leffler 2163b032f27cSSam Leffler /* calculate and install multicast filter */ 2164b032f27cSSam Leffler if ((ifp->if_flags & IFF_ALLMULTI) == 0) { 2165b032f27cSSam Leffler struct ifmultiaddr *ifma; 2166b032f27cSSam Leffler /* 2167b032f27cSSam Leffler * Merge multicast addresses to form the hardware filter. 2168b032f27cSSam Leffler */ 2169b032f27cSSam Leffler mfilt[0] = mfilt[1] = 0; 2170eb956cd0SRobert Watson if_maddr_rlock(ifp); /* XXX need some fiddling to remove? */ 2171b032f27cSSam Leffler TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 2172b032f27cSSam Leffler caddr_t dl; 2173b032f27cSSam Leffler u_int32_t val; 2174b032f27cSSam Leffler u_int8_t pos; 2175b032f27cSSam Leffler 2176b032f27cSSam Leffler /* calculate XOR of eight 6bit values */ 2177b032f27cSSam Leffler dl = LLADDR((struct sockaddr_dl *) ifma->ifma_addr); 2178b032f27cSSam Leffler val = LE_READ_4(dl + 0); 2179b032f27cSSam Leffler pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; 2180b032f27cSSam Leffler val = LE_READ_4(dl + 3); 2181b032f27cSSam Leffler pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; 2182b032f27cSSam Leffler pos &= 0x3f; 2183b032f27cSSam Leffler mfilt[pos / 32] |= (1 << (pos % 32)); 2184b032f27cSSam Leffler } 2185eb956cd0SRobert Watson if_maddr_runlock(ifp); 2186b032f27cSSam Leffler } else 2187b032f27cSSam Leffler mfilt[0] = mfilt[1] = ~0; 2188b032f27cSSam Leffler ath_hal_setmcastfilter(sc->sc_ah, mfilt[0], mfilt[1]); 2189b032f27cSSam Leffler DPRINTF(sc, ATH_DEBUG_MODE, "%s: MC filter %08x:%08x\n", 2190b032f27cSSam Leffler __func__, mfilt[0], mfilt[1]); 21914bc0e754SSam Leffler } 21924bc0e754SSam Leffler 21935591b213SSam Leffler static void 21945591b213SSam Leffler ath_mode_init(struct ath_softc *sc) 21955591b213SSam Leffler { 2196fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 2197b032f27cSSam Leffler struct ath_hal *ah = sc->sc_ah; 2198b032f27cSSam Leffler u_int32_t rfilt; 21995591b213SSam Leffler 22004bc0e754SSam Leffler /* configure rx filter */ 220168e8e04eSSam Leffler rfilt = ath_calcrxfilter(sc); 22024bc0e754SSam Leffler ath_hal_setrxfilter(ah, rfilt); 22034bc0e754SSam Leffler 22045591b213SSam Leffler /* configure operational mode */ 2205c42a7b7eSSam Leffler ath_hal_setopmode(ah); 2206c42a7b7eSSam Leffler 220729aca940SSam Leffler /* handle any link-level address change */ 220829aca940SSam Leffler ath_hal_setmac(ah, IF_LLADDR(ifp)); 22095591b213SSam Leffler 22105591b213SSam Leffler /* calculate and install multicast filter */ 2211b032f27cSSam Leffler ath_update_mcast(ifp); 22125591b213SSam Leffler } 22135591b213SSam Leffler 2214c42a7b7eSSam Leffler /* 2215c42a7b7eSSam Leffler * Set the slot time based on the current setting. 2216c42a7b7eSSam Leffler */ 2217c42a7b7eSSam Leffler static void 2218c42a7b7eSSam Leffler ath_setslottime(struct ath_softc *sc) 2219c42a7b7eSSam Leffler { 2220b032f27cSSam Leffler struct ieee80211com *ic = sc->sc_ifp->if_l2com; 2221c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 2222aaa70f2fSSam Leffler u_int usec; 2223c42a7b7eSSam Leffler 2224aaa70f2fSSam Leffler if (IEEE80211_IS_CHAN_HALF(ic->ic_curchan)) 2225aaa70f2fSSam Leffler usec = 13; 2226aaa70f2fSSam Leffler else if (IEEE80211_IS_CHAN_QUARTER(ic->ic_curchan)) 2227aaa70f2fSSam Leffler usec = 21; 2228724c193aSSam Leffler else if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) { 2229724c193aSSam Leffler /* honor short/long slot time only in 11g */ 2230724c193aSSam Leffler /* XXX shouldn't honor on pure g or turbo g channel */ 2231724c193aSSam Leffler if (ic->ic_flags & IEEE80211_F_SHSLOT) 2232aaa70f2fSSam Leffler usec = HAL_SLOT_TIME_9; 2233aaa70f2fSSam Leffler else 2234aaa70f2fSSam Leffler usec = HAL_SLOT_TIME_20; 2235724c193aSSam Leffler } else 2236724c193aSSam Leffler usec = HAL_SLOT_TIME_9; 2237aaa70f2fSSam Leffler 2238aaa70f2fSSam Leffler DPRINTF(sc, ATH_DEBUG_RESET, 2239aaa70f2fSSam Leffler "%s: chan %u MHz flags 0x%x %s slot, %u usec\n", 2240aaa70f2fSSam Leffler __func__, ic->ic_curchan->ic_freq, ic->ic_curchan->ic_flags, 2241aaa70f2fSSam Leffler ic->ic_flags & IEEE80211_F_SHSLOT ? "short" : "long", usec); 2242aaa70f2fSSam Leffler 2243aaa70f2fSSam Leffler ath_hal_setslottime(ah, usec); 2244c42a7b7eSSam Leffler sc->sc_updateslot = OK; 2245c42a7b7eSSam Leffler } 2246c42a7b7eSSam Leffler 2247c42a7b7eSSam Leffler /* 2248c42a7b7eSSam Leffler * Callback from the 802.11 layer to update the 2249c42a7b7eSSam Leffler * slot time based on the current setting. 2250c42a7b7eSSam Leffler */ 2251c42a7b7eSSam Leffler static void 2252c42a7b7eSSam Leffler ath_updateslot(struct ifnet *ifp) 2253c42a7b7eSSam Leffler { 2254c42a7b7eSSam Leffler struct ath_softc *sc = ifp->if_softc; 2255b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 2256c42a7b7eSSam Leffler 2257c42a7b7eSSam Leffler /* 2258c42a7b7eSSam Leffler * When not coordinating the BSS, change the hardware 2259c42a7b7eSSam Leffler * immediately. For other operation we defer the change 2260c42a7b7eSSam Leffler * until beacon updates have propagated to the stations. 2261c42a7b7eSSam Leffler */ 226259aa14a9SRui Paulo if (ic->ic_opmode == IEEE80211_M_HOSTAP || 226359aa14a9SRui Paulo ic->ic_opmode == IEEE80211_M_MBSS) 2264c42a7b7eSSam Leffler sc->sc_updateslot = UPDATE; 2265c42a7b7eSSam Leffler else 2266c42a7b7eSSam Leffler ath_setslottime(sc); 2267c42a7b7eSSam Leffler } 2268c42a7b7eSSam Leffler 2269c42a7b7eSSam Leffler /* 227080d2765fSSam Leffler * Setup a h/w transmit queue for beacons. 227180d2765fSSam Leffler */ 227280d2765fSSam Leffler static int 227380d2765fSSam Leffler ath_beaconq_setup(struct ath_hal *ah) 227480d2765fSSam Leffler { 227580d2765fSSam Leffler HAL_TXQ_INFO qi; 227680d2765fSSam Leffler 227780d2765fSSam Leffler memset(&qi, 0, sizeof(qi)); 227880d2765fSSam Leffler qi.tqi_aifs = HAL_TXQ_USEDEFAULT; 227980d2765fSSam Leffler qi.tqi_cwmin = HAL_TXQ_USEDEFAULT; 228080d2765fSSam Leffler qi.tqi_cwmax = HAL_TXQ_USEDEFAULT; 22810f2e86fbSSam Leffler /* NB: for dynamic turbo, don't enable any other interrupts */ 2282bd5a9920SSam Leffler qi.tqi_qflags = HAL_TXQ_TXDESCINT_ENABLE; 228380d2765fSSam Leffler return ath_hal_setuptxqueue(ah, HAL_TX_QUEUE_BEACON, &qi); 228480d2765fSSam Leffler } 228580d2765fSSam Leffler 228680d2765fSSam Leffler /* 22870f2e86fbSSam Leffler * Setup the transmit queue parameters for the beacon queue. 22880f2e86fbSSam Leffler */ 22890f2e86fbSSam Leffler static int 22900f2e86fbSSam Leffler ath_beaconq_config(struct ath_softc *sc) 22910f2e86fbSSam Leffler { 22920f2e86fbSSam Leffler #define ATH_EXPONENT_TO_VALUE(v) ((1<<(v))-1) 2293b032f27cSSam Leffler struct ieee80211com *ic = sc->sc_ifp->if_l2com; 22940f2e86fbSSam Leffler struct ath_hal *ah = sc->sc_ah; 22950f2e86fbSSam Leffler HAL_TXQ_INFO qi; 22960f2e86fbSSam Leffler 22970f2e86fbSSam Leffler ath_hal_gettxqueueprops(ah, sc->sc_bhalq, &qi); 229859aa14a9SRui Paulo if (ic->ic_opmode == IEEE80211_M_HOSTAP || 229959aa14a9SRui Paulo ic->ic_opmode == IEEE80211_M_MBSS) { 23000f2e86fbSSam Leffler /* 23010f2e86fbSSam Leffler * Always burst out beacon and CAB traffic. 23020f2e86fbSSam Leffler */ 23030f2e86fbSSam Leffler qi.tqi_aifs = ATH_BEACON_AIFS_DEFAULT; 23040f2e86fbSSam Leffler qi.tqi_cwmin = ATH_BEACON_CWMIN_DEFAULT; 23050f2e86fbSSam Leffler qi.tqi_cwmax = ATH_BEACON_CWMAX_DEFAULT; 23060f2e86fbSSam Leffler } else { 23070f2e86fbSSam Leffler struct wmeParams *wmep = 23080f2e86fbSSam Leffler &ic->ic_wme.wme_chanParams.cap_wmeParams[WME_AC_BE]; 23090f2e86fbSSam Leffler /* 23100f2e86fbSSam Leffler * Adhoc mode; important thing is to use 2x cwmin. 23110f2e86fbSSam Leffler */ 23120f2e86fbSSam Leffler qi.tqi_aifs = wmep->wmep_aifsn; 23130f2e86fbSSam Leffler qi.tqi_cwmin = 2*ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmin); 23140f2e86fbSSam Leffler qi.tqi_cwmax = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmax); 23150f2e86fbSSam Leffler } 23160f2e86fbSSam Leffler 23170f2e86fbSSam Leffler if (!ath_hal_settxqueueprops(ah, sc->sc_bhalq, &qi)) { 23180f2e86fbSSam Leffler device_printf(sc->sc_dev, "unable to update parameters for " 23190f2e86fbSSam Leffler "beacon hardware queue!\n"); 23200f2e86fbSSam Leffler return 0; 23210f2e86fbSSam Leffler } else { 23220f2e86fbSSam Leffler ath_hal_resettxqueue(ah, sc->sc_bhalq); /* push to h/w */ 23230f2e86fbSSam Leffler return 1; 23240f2e86fbSSam Leffler } 23250f2e86fbSSam Leffler #undef ATH_EXPONENT_TO_VALUE 23260f2e86fbSSam Leffler } 23270f2e86fbSSam Leffler 23280f2e86fbSSam Leffler /* 2329c42a7b7eSSam Leffler * Allocate and setup an initial beacon frame. 2330c42a7b7eSSam Leffler */ 23315591b213SSam Leffler static int 23325591b213SSam Leffler ath_beacon_alloc(struct ath_softc *sc, struct ieee80211_node *ni) 23335591b213SSam Leffler { 2334b032f27cSSam Leffler struct ieee80211vap *vap = ni->ni_vap; 2335b032f27cSSam Leffler struct ath_vap *avp = ATH_VAP(vap); 23365591b213SSam Leffler struct ath_buf *bf; 23375591b213SSam Leffler struct mbuf *m; 2338c42a7b7eSSam Leffler int error; 23395591b213SSam Leffler 2340b032f27cSSam Leffler bf = avp->av_bcbuf; 2341b032f27cSSam Leffler if (bf->bf_m != NULL) { 2342b032f27cSSam Leffler bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 2343b032f27cSSam Leffler m_freem(bf->bf_m); 2344b032f27cSSam Leffler bf->bf_m = NULL; 2345c42a7b7eSSam Leffler } 2346b032f27cSSam Leffler if (bf->bf_node != NULL) { 2347b032f27cSSam Leffler ieee80211_free_node(bf->bf_node); 2348b032f27cSSam Leffler bf->bf_node = NULL; 2349b032f27cSSam Leffler } 2350b032f27cSSam Leffler 23515591b213SSam Leffler /* 23525591b213SSam Leffler * NB: the beacon data buffer must be 32-bit aligned; 23535591b213SSam Leffler * we assume the mbuf routines will return us something 23545591b213SSam Leffler * with this alignment (perhaps should assert). 23555591b213SSam Leffler */ 2356b032f27cSSam Leffler m = ieee80211_beacon_alloc(ni, &avp->av_boff); 23575591b213SSam Leffler if (m == NULL) { 2358b032f27cSSam Leffler device_printf(sc->sc_dev, "%s: cannot get mbuf\n", __func__); 23595591b213SSam Leffler sc->sc_stats.ast_be_nombuf++; 23605591b213SSam Leffler return ENOMEM; 23615591b213SSam Leffler } 2362f9e6219bSSam Leffler error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m, 2363f9e6219bSSam Leffler bf->bf_segs, &bf->bf_nseg, 23645591b213SSam Leffler BUS_DMA_NOWAIT); 2365b032f27cSSam Leffler if (error != 0) { 2366b032f27cSSam Leffler device_printf(sc->sc_dev, 2367b032f27cSSam Leffler "%s: cannot map mbuf, bus_dmamap_load_mbuf_sg returns %d\n", 2368b032f27cSSam Leffler __func__, error); 2369b032f27cSSam Leffler m_freem(m); 2370b032f27cSSam Leffler return error; 2371b032f27cSSam Leffler } 2372b032f27cSSam Leffler 2373b032f27cSSam Leffler /* 2374b032f27cSSam Leffler * Calculate a TSF adjustment factor required for staggered 2375b032f27cSSam Leffler * beacons. Note that we assume the format of the beacon 2376b032f27cSSam Leffler * frame leaves the tstamp field immediately following the 2377b032f27cSSam Leffler * header. 2378b032f27cSSam Leffler */ 2379b032f27cSSam Leffler if (sc->sc_stagbeacons && avp->av_bslot > 0) { 2380b032f27cSSam Leffler uint64_t tsfadjust; 2381b032f27cSSam Leffler struct ieee80211_frame *wh; 2382b032f27cSSam Leffler 2383b032f27cSSam Leffler /* 2384b032f27cSSam Leffler * The beacon interval is in TU's; the TSF is in usecs. 2385b032f27cSSam Leffler * We figure out how many TU's to add to align the timestamp 2386b032f27cSSam Leffler * then convert to TSF units and handle byte swapping before 2387b032f27cSSam Leffler * inserting it in the frame. The hardware will then add this 2388b032f27cSSam Leffler * each time a beacon frame is sent. Note that we align vap's 2389b032f27cSSam Leffler * 1..N and leave vap 0 untouched. This means vap 0 has a 2390b032f27cSSam Leffler * timestamp in one beacon interval while the others get a 2391b032f27cSSam Leffler * timstamp aligned to the next interval. 2392b032f27cSSam Leffler */ 2393b032f27cSSam Leffler tsfadjust = ni->ni_intval * 2394b032f27cSSam Leffler (ATH_BCBUF - avp->av_bslot) / ATH_BCBUF; 2395b032f27cSSam Leffler tsfadjust = htole64(tsfadjust << 10); /* TU -> TSF */ 2396b032f27cSSam Leffler 2397b032f27cSSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON, 2398b032f27cSSam Leffler "%s: %s beacons bslot %d intval %u tsfadjust %llu\n", 2399b032f27cSSam Leffler __func__, sc->sc_stagbeacons ? "stagger" : "burst", 24003627e321SSam Leffler avp->av_bslot, ni->ni_intval, 24013627e321SSam Leffler (long long unsigned) le64toh(tsfadjust)); 2402b032f27cSSam Leffler 2403b032f27cSSam Leffler wh = mtod(m, struct ieee80211_frame *); 2404b032f27cSSam Leffler memcpy(&wh[1], &tsfadjust, sizeof(tsfadjust)); 2405b032f27cSSam Leffler } 2406c42a7b7eSSam Leffler bf->bf_m = m; 2407f818612bSSam Leffler bf->bf_node = ieee80211_ref_node(ni); 2408b032f27cSSam Leffler 2409b032f27cSSam Leffler return 0; 24105591b213SSam Leffler } 2411c42a7b7eSSam Leffler 2412c42a7b7eSSam Leffler /* 2413c42a7b7eSSam Leffler * Setup the beacon frame for transmit. 2414c42a7b7eSSam Leffler */ 2415c42a7b7eSSam Leffler static void 2416c42a7b7eSSam Leffler ath_beacon_setup(struct ath_softc *sc, struct ath_buf *bf) 2417c42a7b7eSSam Leffler { 2418c42a7b7eSSam Leffler #define USE_SHPREAMBLE(_ic) \ 2419c42a7b7eSSam Leffler (((_ic)->ic_flags & (IEEE80211_F_SHPREAMBLE | IEEE80211_F_USEBARKER))\ 2420c42a7b7eSSam Leffler == IEEE80211_F_SHPREAMBLE) 2421c42a7b7eSSam Leffler struct ieee80211_node *ni = bf->bf_node; 2422c42a7b7eSSam Leffler struct ieee80211com *ic = ni->ni_ic; 2423c42a7b7eSSam Leffler struct mbuf *m = bf->bf_m; 2424c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 2425c42a7b7eSSam Leffler struct ath_desc *ds; 2426c42a7b7eSSam Leffler int flags, antenna; 242755f63772SSam Leffler const HAL_RATE_TABLE *rt; 242855f63772SSam Leffler u_int8_t rix, rate; 2429c42a7b7eSSam Leffler 24304a3ac3fcSSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s: m %p len %u\n", 2431c42a7b7eSSam Leffler __func__, m, m->m_len); 24325591b213SSam Leffler 24335591b213SSam Leffler /* setup descriptors */ 24345591b213SSam Leffler ds = bf->bf_desc; 24355591b213SSam Leffler 2436c42a7b7eSSam Leffler flags = HAL_TXDESC_NOACK; 2437c42a7b7eSSam Leffler if (ic->ic_opmode == IEEE80211_M_IBSS && sc->sc_hasveol) { 2438c42a7b7eSSam Leffler ds->ds_link = bf->bf_daddr; /* self-linked */ 2439c42a7b7eSSam Leffler flags |= HAL_TXDESC_VEOL; 2440c42a7b7eSSam Leffler /* 2441c42a7b7eSSam Leffler * Let hardware handle antenna switching. 2442c42a7b7eSSam Leffler */ 24434866e6c2SSam Leffler antenna = sc->sc_txantenna; 2444c42a7b7eSSam Leffler } else { 24455591b213SSam Leffler ds->ds_link = 0; 2446c42a7b7eSSam Leffler /* 2447c42a7b7eSSam Leffler * Switch antenna every 4 beacons. 2448c42a7b7eSSam Leffler * XXX assumes two antenna 2449c42a7b7eSSam Leffler */ 2450b032f27cSSam Leffler if (sc->sc_txantenna != 0) 2451b032f27cSSam Leffler antenna = sc->sc_txantenna; 2452b032f27cSSam Leffler else if (sc->sc_stagbeacons && sc->sc_nbcnvaps != 0) 2453b032f27cSSam Leffler antenna = ((sc->sc_stats.ast_be_xmit / sc->sc_nbcnvaps) & 4 ? 2 : 1); 2454b032f27cSSam Leffler else 2455b032f27cSSam Leffler antenna = (sc->sc_stats.ast_be_xmit & 4 ? 2 : 1); 2456c42a7b7eSSam Leffler } 2457c42a7b7eSSam Leffler 2458c42a7b7eSSam Leffler KASSERT(bf->bf_nseg == 1, 2459c42a7b7eSSam Leffler ("multi-segment beacon frame; nseg %u", bf->bf_nseg)); 24605591b213SSam Leffler ds->ds_data = bf->bf_segs[0].ds_addr; 24615591b213SSam Leffler /* 24625591b213SSam Leffler * Calculate rate code. 24635591b213SSam Leffler * XXX everything at min xmit rate 24645591b213SSam Leffler */ 2465b032f27cSSam Leffler rix = 0; 246655f63772SSam Leffler rt = sc->sc_currates; 246755f63772SSam Leffler rate = rt->info[rix].rateCode; 2468c42a7b7eSSam Leffler if (USE_SHPREAMBLE(ic)) 246955f63772SSam Leffler rate |= rt->info[rix].shortPreamble; 24705591b213SSam Leffler ath_hal_setuptxdesc(ah, ds 2471c42a7b7eSSam Leffler , m->m_len + IEEE80211_CRC_LEN /* frame length */ 24725591b213SSam Leffler , sizeof(struct ieee80211_frame)/* header length */ 24735591b213SSam Leffler , HAL_PKT_TYPE_BEACON /* Atheros packet type */ 2474c42a7b7eSSam Leffler , ni->ni_txpower /* txpower XXX */ 24755591b213SSam Leffler , rate, 1 /* series 0 rate/tries */ 24765591b213SSam Leffler , HAL_TXKEYIX_INVALID /* no encryption */ 2477c42a7b7eSSam Leffler , antenna /* antenna mode */ 2478c42a7b7eSSam Leffler , flags /* no ack, veol for beacons */ 24795591b213SSam Leffler , 0 /* rts/cts rate */ 24805591b213SSam Leffler , 0 /* rts/cts duration */ 24815591b213SSam Leffler ); 24825591b213SSam Leffler /* NB: beacon's BufLen must be a multiple of 4 bytes */ 24835591b213SSam Leffler ath_hal_filltxdesc(ah, ds 2484c42a7b7eSSam Leffler , roundup(m->m_len, 4) /* buffer length */ 24855591b213SSam Leffler , AH_TRUE /* first segment */ 24865591b213SSam Leffler , AH_TRUE /* last segment */ 2487c42a7b7eSSam Leffler , ds /* first descriptor */ 24885591b213SSam Leffler ); 2489b032f27cSSam Leffler #if 0 2490b032f27cSSam Leffler ath_desc_swap(ds); 2491b032f27cSSam Leffler #endif 2492c42a7b7eSSam Leffler #undef USE_SHPREAMBLE 24935591b213SSam Leffler } 24945591b213SSam Leffler 2495b105a069SSam Leffler static void 2496b032f27cSSam Leffler ath_beacon_update(struct ieee80211vap *vap, int item) 2497b105a069SSam Leffler { 2498b032f27cSSam Leffler struct ieee80211_beacon_offsets *bo = &ATH_VAP(vap)->av_boff; 2499b105a069SSam Leffler 2500b105a069SSam Leffler setbit(bo->bo_flags, item); 2501b105a069SSam Leffler } 2502b105a069SSam Leffler 2503c42a7b7eSSam Leffler /* 2504622b3fd2SSam Leffler * Append the contents of src to dst; both queues 2505622b3fd2SSam Leffler * are assumed to be locked. 2506622b3fd2SSam Leffler */ 2507622b3fd2SSam Leffler static void 2508622b3fd2SSam Leffler ath_txqmove(struct ath_txq *dst, struct ath_txq *src) 2509622b3fd2SSam Leffler { 25106b349e5aSAdrian Chadd TAILQ_CONCAT(&dst->axq_q, &src->axq_q, bf_list); 2511622b3fd2SSam Leffler dst->axq_link = src->axq_link; 2512622b3fd2SSam Leffler src->axq_link = NULL; 2513622b3fd2SSam Leffler dst->axq_depth += src->axq_depth; 2514622b3fd2SSam Leffler src->axq_depth = 0; 2515622b3fd2SSam Leffler } 2516622b3fd2SSam Leffler 2517622b3fd2SSam Leffler /* 2518c42a7b7eSSam Leffler * Transmit a beacon frame at SWBA. Dynamic updates to the 2519c42a7b7eSSam Leffler * frame contents are done as needed and the slot time is 2520c42a7b7eSSam Leffler * also adjusted based on current state. 2521c42a7b7eSSam Leffler */ 25225591b213SSam Leffler static void 25235591b213SSam Leffler ath_beacon_proc(void *arg, int pending) 25245591b213SSam Leffler { 25255591b213SSam Leffler struct ath_softc *sc = arg; 25265591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 2527b032f27cSSam Leffler struct ieee80211vap *vap; 2528b032f27cSSam Leffler struct ath_buf *bf; 2529b032f27cSSam Leffler int slot, otherant; 2530b032f27cSSam Leffler uint32_t bfaddr; 25315591b213SSam Leffler 2532c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s: pending %u\n", 2533c42a7b7eSSam Leffler __func__, pending); 2534c42a7b7eSSam Leffler /* 2535c42a7b7eSSam Leffler * Check if the previous beacon has gone out. If 2536c66c48cbSSam Leffler * not don't try to post another, skip this period 2537c66c48cbSSam Leffler * and wait for the next. Missed beacons indicate 2538c66c48cbSSam Leffler * a problem and should not occur. If we miss too 2539c66c48cbSSam Leffler * many consecutive beacons reset the device. 2540c42a7b7eSSam Leffler */ 2541c42a7b7eSSam Leffler if (ath_hal_numtxpending(ah, sc->sc_bhalq) != 0) { 2542c42a7b7eSSam Leffler sc->sc_bmisscount++; 25437ec4e6b8SAdrian Chadd sc->sc_stats.ast_be_missed++; 25444a3ac3fcSSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON, 2545c42a7b7eSSam Leffler "%s: missed %u consecutive beacons\n", 2546c42a7b7eSSam Leffler __func__, sc->sc_bmisscount); 2547a32ac9d3SSam Leffler if (sc->sc_bmisscount >= ath_bstuck_threshold) 25480bbf5441SSam Leffler taskqueue_enqueue(sc->sc_tq, &sc->sc_bstucktask); 2549c42a7b7eSSam Leffler return; 2550c42a7b7eSSam Leffler } 2551c42a7b7eSSam Leffler if (sc->sc_bmisscount != 0) { 2552c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON, 2553c42a7b7eSSam Leffler "%s: resume beacon xmit after %u misses\n", 2554c42a7b7eSSam Leffler __func__, sc->sc_bmisscount); 2555c42a7b7eSSam Leffler sc->sc_bmisscount = 0; 2556c42a7b7eSSam Leffler } 2557c42a7b7eSSam Leffler 2558b032f27cSSam Leffler if (sc->sc_stagbeacons) { /* staggered beacons */ 2559b032f27cSSam Leffler struct ieee80211com *ic = sc->sc_ifp->if_l2com; 2560b032f27cSSam Leffler uint32_t tsftu; 2561b032f27cSSam Leffler 2562b032f27cSSam Leffler tsftu = ath_hal_gettsf32(ah) >> 10; 2563b032f27cSSam Leffler /* XXX lintval */ 2564b032f27cSSam Leffler slot = ((tsftu % ic->ic_lintval) * ATH_BCBUF) / ic->ic_lintval; 2565b032f27cSSam Leffler vap = sc->sc_bslot[(slot+1) % ATH_BCBUF]; 2566b032f27cSSam Leffler bfaddr = 0; 2567309a3e45SSam Leffler if (vap != NULL && vap->iv_state >= IEEE80211_S_RUN) { 2568b032f27cSSam Leffler bf = ath_beacon_generate(sc, vap); 2569b032f27cSSam Leffler if (bf != NULL) 2570b032f27cSSam Leffler bfaddr = bf->bf_daddr; 2571b032f27cSSam Leffler } 2572b032f27cSSam Leffler } else { /* burst'd beacons */ 2573b032f27cSSam Leffler uint32_t *bflink = &bfaddr; 2574b032f27cSSam Leffler 2575b032f27cSSam Leffler for (slot = 0; slot < ATH_BCBUF; slot++) { 2576b032f27cSSam Leffler vap = sc->sc_bslot[slot]; 2577309a3e45SSam Leffler if (vap != NULL && vap->iv_state >= IEEE80211_S_RUN) { 2578b032f27cSSam Leffler bf = ath_beacon_generate(sc, vap); 2579b032f27cSSam Leffler if (bf != NULL) { 2580b032f27cSSam Leffler *bflink = bf->bf_daddr; 2581b032f27cSSam Leffler bflink = &bf->bf_desc->ds_link; 2582c42a7b7eSSam Leffler } 2583c42a7b7eSSam Leffler } 2584b032f27cSSam Leffler } 2585b032f27cSSam Leffler *bflink = 0; /* terminate list */ 2586622b3fd2SSam Leffler } 2587c42a7b7eSSam Leffler 2588c42a7b7eSSam Leffler /* 2589c42a7b7eSSam Leffler * Handle slot time change when a non-ERP station joins/leaves 2590c42a7b7eSSam Leffler * an 11g network. The 802.11 layer notifies us via callback, 2591c42a7b7eSSam Leffler * we mark updateslot, then wait one beacon before effecting 2592c42a7b7eSSam Leffler * the change. This gives associated stations at least one 2593c42a7b7eSSam Leffler * beacon interval to note the state change. 2594c42a7b7eSSam Leffler */ 2595c42a7b7eSSam Leffler /* XXX locking */ 2596b032f27cSSam Leffler if (sc->sc_updateslot == UPDATE) { 2597c42a7b7eSSam Leffler sc->sc_updateslot = COMMIT; /* commit next beacon */ 2598b032f27cSSam Leffler sc->sc_slotupdate = slot; 2599b032f27cSSam Leffler } else if (sc->sc_updateslot == COMMIT && sc->sc_slotupdate == slot) 2600c42a7b7eSSam Leffler ath_setslottime(sc); /* commit change to h/w */ 2601c42a7b7eSSam Leffler 2602c42a7b7eSSam Leffler /* 2603c42a7b7eSSam Leffler * Check recent per-antenna transmit statistics and flip 2604c42a7b7eSSam Leffler * the default antenna if noticeably more frames went out 2605c42a7b7eSSam Leffler * on the non-default antenna. 2606c42a7b7eSSam Leffler * XXX assumes 2 anntenae 2607c42a7b7eSSam Leffler */ 2608b032f27cSSam Leffler if (!sc->sc_diversity && (!sc->sc_stagbeacons || slot == 0)) { 2609c42a7b7eSSam Leffler otherant = sc->sc_defant & 1 ? 2 : 1; 2610c42a7b7eSSam Leffler if (sc->sc_ant_tx[otherant] > sc->sc_ant_tx[sc->sc_defant] + 2) 2611c42a7b7eSSam Leffler ath_setdefantenna(sc, otherant); 2612c42a7b7eSSam Leffler sc->sc_ant_tx[1] = sc->sc_ant_tx[2] = 0; 2613b032f27cSSam Leffler } 2614c42a7b7eSSam Leffler 2615b032f27cSSam Leffler if (bfaddr != 0) { 2616c42a7b7eSSam Leffler /* 2617c42a7b7eSSam Leffler * Stop any current dma and put the new frame on the queue. 2618c42a7b7eSSam Leffler * This should never fail since we check above that no frames 2619c42a7b7eSSam Leffler * are still pending on the queue. 2620c42a7b7eSSam Leffler */ 26215591b213SSam Leffler if (!ath_hal_stoptxdma(ah, sc->sc_bhalq)) { 2622c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, 2623c42a7b7eSSam Leffler "%s: beacon queue %u did not stop?\n", 2624c42a7b7eSSam Leffler __func__, sc->sc_bhalq); 26255591b213SSam Leffler } 2626b032f27cSSam Leffler /* NB: cabq traffic should already be queued and primed */ 2627b032f27cSSam Leffler ath_hal_puttxbuf(ah, sc->sc_bhalq, bfaddr); 2628b032f27cSSam Leffler ath_hal_txstart(ah, sc->sc_bhalq); 2629b032f27cSSam Leffler 2630b032f27cSSam Leffler sc->sc_stats.ast_be_xmit++; 2631b032f27cSSam Leffler } 2632b032f27cSSam Leffler } 2633b032f27cSSam Leffler 2634b032f27cSSam Leffler static struct ath_buf * 2635b032f27cSSam Leffler ath_beacon_generate(struct ath_softc *sc, struct ieee80211vap *vap) 2636b032f27cSSam Leffler { 2637b032f27cSSam Leffler struct ath_vap *avp = ATH_VAP(vap); 2638b032f27cSSam Leffler struct ath_txq *cabq = sc->sc_cabq; 2639b032f27cSSam Leffler struct ath_buf *bf; 2640b032f27cSSam Leffler struct mbuf *m; 2641b032f27cSSam Leffler int nmcastq, error; 2642b032f27cSSam Leffler 2643309a3e45SSam Leffler KASSERT(vap->iv_state >= IEEE80211_S_RUN, 2644b032f27cSSam Leffler ("not running, state %d", vap->iv_state)); 2645b032f27cSSam Leffler KASSERT(avp->av_bcbuf != NULL, ("no beacon buffer")); 2646b032f27cSSam Leffler 2647b032f27cSSam Leffler /* 2648b032f27cSSam Leffler * Update dynamic beacon contents. If this returns 2649b032f27cSSam Leffler * non-zero then we need to remap the memory because 2650b032f27cSSam Leffler * the beacon frame changed size (probably because 2651b032f27cSSam Leffler * of the TIM bitmap). 2652b032f27cSSam Leffler */ 2653b032f27cSSam Leffler bf = avp->av_bcbuf; 2654b032f27cSSam Leffler m = bf->bf_m; 2655b032f27cSSam Leffler nmcastq = avp->av_mcastq.axq_depth; 2656b032f27cSSam Leffler if (ieee80211_beacon_update(bf->bf_node, &avp->av_boff, m, nmcastq)) { 2657b032f27cSSam Leffler /* XXX too conservative? */ 2658b032f27cSSam Leffler bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 2659b032f27cSSam Leffler error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m, 2660b032f27cSSam Leffler bf->bf_segs, &bf->bf_nseg, 2661b032f27cSSam Leffler BUS_DMA_NOWAIT); 2662b032f27cSSam Leffler if (error != 0) { 2663b032f27cSSam Leffler if_printf(vap->iv_ifp, 2664b032f27cSSam Leffler "%s: bus_dmamap_load_mbuf_sg failed, error %u\n", 2665b032f27cSSam Leffler __func__, error); 2666b032f27cSSam Leffler return NULL; 2667b032f27cSSam Leffler } 2668b032f27cSSam Leffler } 2669b032f27cSSam Leffler if ((avp->av_boff.bo_tim[4] & 1) && cabq->axq_depth) { 2670b032f27cSSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON, 2671b032f27cSSam Leffler "%s: cabq did not drain, mcastq %u cabq %u\n", 2672b032f27cSSam Leffler __func__, nmcastq, cabq->axq_depth); 2673b032f27cSSam Leffler sc->sc_stats.ast_cabq_busy++; 2674b032f27cSSam Leffler if (sc->sc_nvaps > 1 && sc->sc_stagbeacons) { 2675b032f27cSSam Leffler /* 2676b032f27cSSam Leffler * CABQ traffic from a previous vap is still pending. 2677b032f27cSSam Leffler * We must drain the q before this beacon frame goes 2678b032f27cSSam Leffler * out as otherwise this vap's stations will get cab 2679b032f27cSSam Leffler * frames from a different vap. 2680b032f27cSSam Leffler * XXX could be slow causing us to miss DBA 2681b032f27cSSam Leffler */ 2682b032f27cSSam Leffler ath_tx_draintxq(sc, cabq); 2683b032f27cSSam Leffler } 2684b032f27cSSam Leffler } 2685b032f27cSSam Leffler ath_beacon_setup(sc, bf); 26865591b213SSam Leffler bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE); 26875591b213SSam Leffler 2688c42a7b7eSSam Leffler /* 2689c42a7b7eSSam Leffler * Enable the CAB queue before the beacon queue to 2690c42a7b7eSSam Leffler * insure cab frames are triggered by this beacon. 2691c42a7b7eSSam Leffler */ 2692b032f27cSSam Leffler if (avp->av_boff.bo_tim[4] & 1) { 2693b032f27cSSam Leffler struct ath_hal *ah = sc->sc_ah; 2694b032f27cSSam Leffler 2695f3af83f7SSam Leffler /* NB: only at DTIM */ 2696622b3fd2SSam Leffler ATH_TXQ_LOCK(cabq); 2697b032f27cSSam Leffler ATH_TXQ_LOCK(&avp->av_mcastq); 2698622b3fd2SSam Leffler if (nmcastq) { 2699622b3fd2SSam Leffler struct ath_buf *bfm; 2700622b3fd2SSam Leffler 2701622b3fd2SSam Leffler /* 2702622b3fd2SSam Leffler * Move frames from the s/w mcast q to the h/w cab q. 2703b032f27cSSam Leffler * XXX MORE_DATA bit 2704622b3fd2SSam Leffler */ 27056b349e5aSAdrian Chadd bfm = TAILQ_FIRST(&avp->av_mcastq.axq_q); 2706622b3fd2SSam Leffler if (cabq->axq_link != NULL) { 2707622b3fd2SSam Leffler *cabq->axq_link = bfm->bf_daddr; 2708622b3fd2SSam Leffler } else 2709622b3fd2SSam Leffler ath_hal_puttxbuf(ah, cabq->axq_qnum, 2710622b3fd2SSam Leffler bfm->bf_daddr); 2711b032f27cSSam Leffler ath_txqmove(cabq, &avp->av_mcastq); 2712622b3fd2SSam Leffler 2713622b3fd2SSam Leffler sc->sc_stats.ast_cabq_xmit += nmcastq; 2714622b3fd2SSam Leffler } 2715622b3fd2SSam Leffler /* NB: gated by beacon so safe to start here */ 27166b349e5aSAdrian Chadd if (! TAILQ_EMPTY(&(cabq->axq_q))) 2717622b3fd2SSam Leffler ath_hal_txstart(ah, cabq->axq_qnum); 2718b032f27cSSam Leffler ATH_TXQ_UNLOCK(&avp->av_mcastq); 27197b15790aSAdrian Chadd ATH_TXQ_UNLOCK(cabq); 2720622b3fd2SSam Leffler } 2721b032f27cSSam Leffler return bf; 2722b032f27cSSam Leffler } 2723b032f27cSSam Leffler 2724b032f27cSSam Leffler static void 2725b032f27cSSam Leffler ath_beacon_start_adhoc(struct ath_softc *sc, struct ieee80211vap *vap) 2726b032f27cSSam Leffler { 2727b032f27cSSam Leffler struct ath_vap *avp = ATH_VAP(vap); 2728b032f27cSSam Leffler struct ath_hal *ah = sc->sc_ah; 2729b032f27cSSam Leffler struct ath_buf *bf; 2730b032f27cSSam Leffler struct mbuf *m; 2731b032f27cSSam Leffler int error; 2732b032f27cSSam Leffler 2733b032f27cSSam Leffler KASSERT(avp->av_bcbuf != NULL, ("no beacon buffer")); 2734b032f27cSSam Leffler 2735b032f27cSSam Leffler /* 2736b032f27cSSam Leffler * Update dynamic beacon contents. If this returns 2737b032f27cSSam Leffler * non-zero then we need to remap the memory because 2738b032f27cSSam Leffler * the beacon frame changed size (probably because 2739b032f27cSSam Leffler * of the TIM bitmap). 2740b032f27cSSam Leffler */ 2741b032f27cSSam Leffler bf = avp->av_bcbuf; 2742b032f27cSSam Leffler m = bf->bf_m; 2743b032f27cSSam Leffler if (ieee80211_beacon_update(bf->bf_node, &avp->av_boff, m, 0)) { 2744b032f27cSSam Leffler /* XXX too conservative? */ 2745b032f27cSSam Leffler bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 2746b032f27cSSam Leffler error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m, 2747b032f27cSSam Leffler bf->bf_segs, &bf->bf_nseg, 2748b032f27cSSam Leffler BUS_DMA_NOWAIT); 2749b032f27cSSam Leffler if (error != 0) { 2750b032f27cSSam Leffler if_printf(vap->iv_ifp, 2751b032f27cSSam Leffler "%s: bus_dmamap_load_mbuf_sg failed, error %u\n", 2752b032f27cSSam Leffler __func__, error); 2753b032f27cSSam Leffler return; 2754b032f27cSSam Leffler } 2755b032f27cSSam Leffler } 2756b032f27cSSam Leffler ath_beacon_setup(sc, bf); 2757b032f27cSSam Leffler bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE); 2758b032f27cSSam Leffler 2759b032f27cSSam Leffler /* NB: caller is known to have already stopped tx dma */ 27605591b213SSam Leffler ath_hal_puttxbuf(ah, sc->sc_bhalq, bf->bf_daddr); 27615591b213SSam Leffler ath_hal_txstart(ah, sc->sc_bhalq); 27625591b213SSam Leffler } 27635591b213SSam Leffler 2764c42a7b7eSSam Leffler /* 2765c42a7b7eSSam Leffler * Reset the hardware after detecting beacons have stopped. 2766c42a7b7eSSam Leffler */ 2767c42a7b7eSSam Leffler static void 2768c42a7b7eSSam Leffler ath_bstuck_proc(void *arg, int pending) 2769c42a7b7eSSam Leffler { 2770c42a7b7eSSam Leffler struct ath_softc *sc = arg; 2771fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 2772*16d4de92SAdrian Chadd uint32_t hangs = 0; 2773*16d4de92SAdrian Chadd 2774*16d4de92SAdrian Chadd if (ath_hal_gethangstate(sc->sc_ah, 0xff, &hangs) && hangs != 0) 2775*16d4de92SAdrian Chadd if_printf(ifp, "bb hang detected (0x%x)\n", hangs); 2776c42a7b7eSSam Leffler 2777c42a7b7eSSam Leffler if_printf(ifp, "stuck beacon; resetting (bmiss count %u)\n", 2778c42a7b7eSSam Leffler sc->sc_bmisscount); 2779c2e34459SSam Leffler sc->sc_stats.ast_bstuck++; 2780*16d4de92SAdrian Chadd /* 2781*16d4de92SAdrian Chadd * This assumes that there's no simultaneous channel mode change 2782*16d4de92SAdrian Chadd * occuring. 2783*16d4de92SAdrian Chadd */ 2784517526efSAdrian Chadd ath_reset(ifp, ATH_RESET_NOLOSS); 2785c42a7b7eSSam Leffler } 2786c42a7b7eSSam Leffler 2787c42a7b7eSSam Leffler /* 2788b032f27cSSam Leffler * Reclaim beacon resources and return buffer to the pool. 2789b032f27cSSam Leffler */ 2790b032f27cSSam Leffler static void 2791b032f27cSSam Leffler ath_beacon_return(struct ath_softc *sc, struct ath_buf *bf) 2792b032f27cSSam Leffler { 2793b032f27cSSam Leffler 2794b032f27cSSam Leffler if (bf->bf_m != NULL) { 2795b032f27cSSam Leffler bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 2796b032f27cSSam Leffler m_freem(bf->bf_m); 2797b032f27cSSam Leffler bf->bf_m = NULL; 2798b032f27cSSam Leffler } 2799b032f27cSSam Leffler if (bf->bf_node != NULL) { 2800b032f27cSSam Leffler ieee80211_free_node(bf->bf_node); 2801b032f27cSSam Leffler bf->bf_node = NULL; 2802b032f27cSSam Leffler } 28036b349e5aSAdrian Chadd TAILQ_INSERT_TAIL(&sc->sc_bbuf, bf, bf_list); 2804b032f27cSSam Leffler } 2805b032f27cSSam Leffler 2806b032f27cSSam Leffler /* 2807c42a7b7eSSam Leffler * Reclaim beacon resources. 2808c42a7b7eSSam Leffler */ 28095591b213SSam Leffler static void 28105591b213SSam Leffler ath_beacon_free(struct ath_softc *sc) 28115591b213SSam Leffler { 2812c42a7b7eSSam Leffler struct ath_buf *bf; 28135591b213SSam Leffler 28146b349e5aSAdrian Chadd TAILQ_FOREACH(bf, &sc->sc_bbuf, bf_list) { 28155591b213SSam Leffler if (bf->bf_m != NULL) { 28165591b213SSam Leffler bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 28175591b213SSam Leffler m_freem(bf->bf_m); 28185591b213SSam Leffler bf->bf_m = NULL; 2819f818612bSSam Leffler } 2820f818612bSSam Leffler if (bf->bf_node != NULL) { 2821f818612bSSam Leffler ieee80211_free_node(bf->bf_node); 28225591b213SSam Leffler bf->bf_node = NULL; 28235591b213SSam Leffler } 28245591b213SSam Leffler } 2825f818612bSSam Leffler } 28265591b213SSam Leffler 28275591b213SSam Leffler /* 28285591b213SSam Leffler * Configure the beacon and sleep timers. 28295591b213SSam Leffler * 28305591b213SSam Leffler * When operating as an AP this resets the TSF and sets 28315591b213SSam Leffler * up the hardware to notify us when we need to issue beacons. 28325591b213SSam Leffler * 28335591b213SSam Leffler * When operating in station mode this sets up the beacon 28345591b213SSam Leffler * timers according to the timestamp of the last received 28355591b213SSam Leffler * beacon and the current TSF, configures PCF and DTIM 28365591b213SSam Leffler * handling, programs the sleep registers so the hardware 28375591b213SSam Leffler * will wakeup in time to receive beacons, and configures 28385591b213SSam Leffler * the beacon miss handling so we'll receive a BMISS 28395591b213SSam Leffler * interrupt when we stop seeing beacons from the AP 28405591b213SSam Leffler * we've associated with. 28415591b213SSam Leffler */ 28425591b213SSam Leffler static void 2843b032f27cSSam Leffler ath_beacon_config(struct ath_softc *sc, struct ieee80211vap *vap) 28445591b213SSam Leffler { 284580d939bfSSam Leffler #define TSF_TO_TU(_h,_l) \ 284680d939bfSSam Leffler ((((u_int32_t)(_h)) << 22) | (((u_int32_t)(_l)) >> 10)) 284780d939bfSSam Leffler #define FUDGE 2 28485591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 2849b032f27cSSam Leffler struct ieee80211com *ic = sc->sc_ifp->if_l2com; 2850b032f27cSSam Leffler struct ieee80211_node *ni; 285180d939bfSSam Leffler u_int32_t nexttbtt, intval, tsftu; 285280d939bfSSam Leffler u_int64_t tsf; 28535591b213SSam Leffler 2854b032f27cSSam Leffler if (vap == NULL) 2855b032f27cSSam Leffler vap = TAILQ_FIRST(&ic->ic_vaps); /* XXX */ 2856b032f27cSSam Leffler ni = vap->iv_bss; 2857b032f27cSSam Leffler 28588371372bSSam Leffler /* extract tstamp from last beacon and convert to TU */ 28598371372bSSam Leffler nexttbtt = TSF_TO_TU(LE_READ_4(ni->ni_tstamp.data + 4), 28608371372bSSam Leffler LE_READ_4(ni->ni_tstamp.data)); 286159aa14a9SRui Paulo if (ic->ic_opmode == IEEE80211_M_HOSTAP || 286259aa14a9SRui Paulo ic->ic_opmode == IEEE80211_M_MBSS) { 2863b032f27cSSam Leffler /* 286459aa14a9SRui Paulo * For multi-bss ap/mesh support beacons are either staggered 2865b032f27cSSam Leffler * evenly over N slots or burst together. For the former 2866b032f27cSSam Leffler * arrange for the SWBA to be delivered for each slot. 2867b032f27cSSam Leffler * Slots that are not occupied will generate nothing. 2868b032f27cSSam Leffler */ 28698371372bSSam Leffler /* NB: the beacon interval is kept internally in TU's */ 28704bacf7c1SSam Leffler intval = ni->ni_intval & HAL_BEACON_PERIOD; 2871b032f27cSSam Leffler if (sc->sc_stagbeacons) 2872b032f27cSSam Leffler intval /= ATH_BCBUF; 2873b032f27cSSam Leffler } else { 2874b032f27cSSam Leffler /* NB: the beacon interval is kept internally in TU's */ 2875b032f27cSSam Leffler intval = ni->ni_intval & HAL_BEACON_PERIOD; 2876b032f27cSSam Leffler } 2877a6c992f4SSam Leffler if (nexttbtt == 0) /* e.g. for ap mode */ 2878a6c992f4SSam Leffler nexttbtt = intval; 2879a6c992f4SSam Leffler else if (intval) /* NB: can be 0 for monitor mode */ 2880a6c992f4SSam Leffler nexttbtt = roundup(nexttbtt, intval); 2881a6c992f4SSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON, "%s: nexttbtt %u intval %u (%u)\n", 2882a6c992f4SSam Leffler __func__, nexttbtt, intval, ni->ni_intval); 2883b032f27cSSam Leffler if (ic->ic_opmode == IEEE80211_M_STA && !sc->sc_swbmiss) { 28845591b213SSam Leffler HAL_BEACON_STATE bs; 28858371372bSSam Leffler int dtimperiod, dtimcount; 28868371372bSSam Leffler int cfpperiod, cfpcount; 28875591b213SSam Leffler 28888371372bSSam Leffler /* 28898371372bSSam Leffler * Setup dtim and cfp parameters according to 28908371372bSSam Leffler * last beacon we received (which may be none). 28918371372bSSam Leffler */ 28928371372bSSam Leffler dtimperiod = ni->ni_dtim_period; 28938371372bSSam Leffler if (dtimperiod <= 0) /* NB: 0 if not known */ 28948371372bSSam Leffler dtimperiod = 1; 28958371372bSSam Leffler dtimcount = ni->ni_dtim_count; 28968371372bSSam Leffler if (dtimcount >= dtimperiod) /* NB: sanity check */ 28978371372bSSam Leffler dtimcount = 0; /* XXX? */ 28988371372bSSam Leffler cfpperiod = 1; /* NB: no PCF support yet */ 28998371372bSSam Leffler cfpcount = 0; 29008371372bSSam Leffler /* 29018371372bSSam Leffler * Pull nexttbtt forward to reflect the current 29028371372bSSam Leffler * TSF and calculate dtim+cfp state for the result. 29038371372bSSam Leffler */ 29048371372bSSam Leffler tsf = ath_hal_gettsf64(ah); 290580d939bfSSam Leffler tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; 29068371372bSSam Leffler do { 29078371372bSSam Leffler nexttbtt += intval; 29088371372bSSam Leffler if (--dtimcount < 0) { 29098371372bSSam Leffler dtimcount = dtimperiod - 1; 29108371372bSSam Leffler if (--cfpcount < 0) 29118371372bSSam Leffler cfpcount = cfpperiod - 1; 29128371372bSSam Leffler } 29138371372bSSam Leffler } while (nexttbtt < tsftu); 29145591b213SSam Leffler memset(&bs, 0, sizeof(bs)); 2915a6c992f4SSam Leffler bs.bs_intval = intval; 29165591b213SSam Leffler bs.bs_nexttbtt = nexttbtt; 29178371372bSSam Leffler bs.bs_dtimperiod = dtimperiod*intval; 29188371372bSSam Leffler bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval; 29198371372bSSam Leffler bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod; 29208371372bSSam Leffler bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod; 29218371372bSSam Leffler bs.bs_cfpmaxduration = 0; 29228371372bSSam Leffler #if 0 29235591b213SSam Leffler /* 2924c42a7b7eSSam Leffler * The 802.11 layer records the offset to the DTIM 2925c42a7b7eSSam Leffler * bitmap while receiving beacons; use it here to 2926c42a7b7eSSam Leffler * enable h/w detection of our AID being marked in 2927c42a7b7eSSam Leffler * the bitmap vector (to indicate frames for us are 2928c42a7b7eSSam Leffler * pending at the AP). 29298371372bSSam Leffler * XXX do DTIM handling in s/w to WAR old h/w bugs 29308371372bSSam Leffler * XXX enable based on h/w rev for newer chips 2931c42a7b7eSSam Leffler */ 2932c42a7b7eSSam Leffler bs.bs_timoffset = ni->ni_timoff; 29338371372bSSam Leffler #endif 2934c42a7b7eSSam Leffler /* 29355591b213SSam Leffler * Calculate the number of consecutive beacons to miss 293668e8e04eSSam Leffler * before taking a BMISS interrupt. 29375591b213SSam Leffler * Note that we clamp the result to at most 10 beacons. 29385591b213SSam Leffler */ 2939b032f27cSSam Leffler bs.bs_bmissthreshold = vap->iv_bmissthreshold; 29405591b213SSam Leffler if (bs.bs_bmissthreshold > 10) 29415591b213SSam Leffler bs.bs_bmissthreshold = 10; 29425591b213SSam Leffler else if (bs.bs_bmissthreshold <= 0) 29435591b213SSam Leffler bs.bs_bmissthreshold = 1; 29445591b213SSam Leffler 29455591b213SSam Leffler /* 29465591b213SSam Leffler * Calculate sleep duration. The configuration is 29475591b213SSam Leffler * given in ms. We insure a multiple of the beacon 29485591b213SSam Leffler * period is used. Also, if the sleep duration is 29495591b213SSam Leffler * greater than the DTIM period then it makes senses 29505591b213SSam Leffler * to make it a multiple of that. 29515591b213SSam Leffler * 29525591b213SSam Leffler * XXX fixed at 100ms 29535591b213SSam Leffler */ 29544bacf7c1SSam Leffler bs.bs_sleepduration = 29554bacf7c1SSam Leffler roundup(IEEE80211_MS_TO_TU(100), bs.bs_intval); 29565591b213SSam Leffler if (bs.bs_sleepduration > bs.bs_dtimperiod) 29575591b213SSam Leffler bs.bs_sleepduration = roundup(bs.bs_sleepduration, bs.bs_dtimperiod); 29585591b213SSam Leffler 2959c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON, 29608371372bSSam 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" 29615591b213SSam Leffler , __func__ 29628371372bSSam Leffler , tsf, tsftu 29635591b213SSam Leffler , bs.bs_intval 29645591b213SSam Leffler , bs.bs_nexttbtt 29655591b213SSam Leffler , bs.bs_dtimperiod 29665591b213SSam Leffler , bs.bs_nextdtim 29675591b213SSam Leffler , bs.bs_bmissthreshold 29685591b213SSam Leffler , bs.bs_sleepduration 2969c42a7b7eSSam Leffler , bs.bs_cfpperiod 2970c42a7b7eSSam Leffler , bs.bs_cfpmaxduration 2971c42a7b7eSSam Leffler , bs.bs_cfpnext 2972c42a7b7eSSam Leffler , bs.bs_timoffset 2973c42a7b7eSSam Leffler ); 29745591b213SSam Leffler ath_hal_intrset(ah, 0); 2975c42a7b7eSSam Leffler ath_hal_beacontimers(ah, &bs); 29765591b213SSam Leffler sc->sc_imask |= HAL_INT_BMISS; 29775591b213SSam Leffler ath_hal_intrset(ah, sc->sc_imask); 29785591b213SSam Leffler } else { 29795591b213SSam Leffler ath_hal_intrset(ah, 0); 2980a6c992f4SSam Leffler if (nexttbtt == intval) 2981c42a7b7eSSam Leffler intval |= HAL_BEACON_RESET_TSF; 2982c42a7b7eSSam Leffler if (ic->ic_opmode == IEEE80211_M_IBSS) { 2983c42a7b7eSSam Leffler /* 2984c42a7b7eSSam Leffler * In IBSS mode enable the beacon timers but only 2985c42a7b7eSSam Leffler * enable SWBA interrupts if we need to manually 2986c42a7b7eSSam Leffler * prepare beacon frames. Otherwise we use a 2987c42a7b7eSSam Leffler * self-linked tx descriptor and let the hardware 2988c42a7b7eSSam Leffler * deal with things. 2989c42a7b7eSSam Leffler */ 2990c42a7b7eSSam Leffler intval |= HAL_BEACON_ENA; 2991c42a7b7eSSam Leffler if (!sc->sc_hasveol) 2992c42a7b7eSSam Leffler sc->sc_imask |= HAL_INT_SWBA; 299380d939bfSSam Leffler if ((intval & HAL_BEACON_RESET_TSF) == 0) { 299480d939bfSSam Leffler /* 299580d939bfSSam Leffler * Pull nexttbtt forward to reflect 299680d939bfSSam Leffler * the current TSF. 299780d939bfSSam Leffler */ 299880d939bfSSam Leffler tsf = ath_hal_gettsf64(ah); 299980d939bfSSam Leffler tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; 300080d939bfSSam Leffler do { 300180d939bfSSam Leffler nexttbtt += intval; 300280d939bfSSam Leffler } while (nexttbtt < tsftu); 300380d939bfSSam Leffler } 30040f2e86fbSSam Leffler ath_beaconq_config(sc); 300559aa14a9SRui Paulo } else if (ic->ic_opmode == IEEE80211_M_HOSTAP || 300659aa14a9SRui Paulo ic->ic_opmode == IEEE80211_M_MBSS) { 3007c42a7b7eSSam Leffler /* 300859aa14a9SRui Paulo * In AP/mesh mode we enable the beacon timers 300959aa14a9SRui Paulo * and SWBA interrupts to prepare beacon frames. 3010c42a7b7eSSam Leffler */ 3011c42a7b7eSSam Leffler intval |= HAL_BEACON_ENA; 30125591b213SSam Leffler sc->sc_imask |= HAL_INT_SWBA; /* beacon prepare */ 30130f2e86fbSSam Leffler ath_beaconq_config(sc); 3014c42a7b7eSSam Leffler } 3015c42a7b7eSSam Leffler ath_hal_beaconinit(ah, nexttbtt, intval); 3016c42a7b7eSSam Leffler sc->sc_bmisscount = 0; 30175591b213SSam Leffler ath_hal_intrset(ah, sc->sc_imask); 3018c42a7b7eSSam Leffler /* 3019c42a7b7eSSam Leffler * When using a self-linked beacon descriptor in 3020c42a7b7eSSam Leffler * ibss mode load it once here. 3021c42a7b7eSSam Leffler */ 3022c42a7b7eSSam Leffler if (ic->ic_opmode == IEEE80211_M_IBSS && sc->sc_hasveol) 3023b032f27cSSam Leffler ath_beacon_start_adhoc(sc, vap); 30245591b213SSam Leffler } 302580d939bfSSam Leffler sc->sc_syncbeacon = 0; 302680d939bfSSam Leffler #undef FUDGE 30278371372bSSam Leffler #undef TSF_TO_TU 30285591b213SSam Leffler } 30295591b213SSam Leffler 30305591b213SSam Leffler static void 30315591b213SSam Leffler ath_load_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 30325591b213SSam Leffler { 30335591b213SSam Leffler bus_addr_t *paddr = (bus_addr_t*) arg; 3034d77367bfSSam Leffler KASSERT(error == 0, ("error %u on bus_dma callback", error)); 30355591b213SSam Leffler *paddr = segs->ds_addr; 30365591b213SSam Leffler } 30375591b213SSam Leffler 30385591b213SSam Leffler static int 3039c42a7b7eSSam Leffler ath_descdma_setup(struct ath_softc *sc, 3040c42a7b7eSSam Leffler struct ath_descdma *dd, ath_bufhead *head, 3041c42a7b7eSSam Leffler const char *name, int nbuf, int ndesc) 3042c42a7b7eSSam Leffler { 3043c42a7b7eSSam Leffler #define DS2PHYS(_dd, _ds) \ 3044c42a7b7eSSam Leffler ((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc)) 304545abcd6cSAdrian Chadd #define ATH_DESC_4KB_BOUND_CHECK(_daddr, _len) \ 304645abcd6cSAdrian Chadd ((((u_int32_t)(_daddr) & 0xFFF) > (0x1000 - (_len))) ? 1 : 0) 3047fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 304845abcd6cSAdrian Chadd uint8_t *ds; 3049c42a7b7eSSam Leffler struct ath_buf *bf; 3050c42a7b7eSSam Leffler int i, bsize, error; 305145abcd6cSAdrian Chadd int desc_len; 305245abcd6cSAdrian Chadd 305345abcd6cSAdrian Chadd desc_len = sizeof(struct ath_desc); 3054c42a7b7eSSam Leffler 3055c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_RESET, "%s: %s DMA: %u buffers %u desc/buf\n", 3056c42a7b7eSSam Leffler __func__, name, nbuf, ndesc); 3057c42a7b7eSSam Leffler 3058c42a7b7eSSam Leffler dd->dd_name = name; 305945abcd6cSAdrian Chadd dd->dd_desc_len = desc_len * nbuf * ndesc; 306045abcd6cSAdrian Chadd 306145abcd6cSAdrian Chadd /* 306245abcd6cSAdrian Chadd * Merlin work-around: 306345abcd6cSAdrian Chadd * Descriptors that cross the 4KB boundary can't be used. 306445abcd6cSAdrian Chadd * Assume one skipped descriptor per 4KB page. 306545abcd6cSAdrian Chadd */ 306645abcd6cSAdrian Chadd if (! ath_hal_split4ktrans(sc->sc_ah)) { 306745abcd6cSAdrian Chadd int numdescpage = 4096 / (desc_len * ndesc); 306845abcd6cSAdrian Chadd dd->dd_desc_len = (nbuf / numdescpage + 1) * 4096; 306945abcd6cSAdrian Chadd } 3070c42a7b7eSSam Leffler 3071c42a7b7eSSam Leffler /* 3072c42a7b7eSSam Leffler * Setup DMA descriptor area. 3073c42a7b7eSSam Leffler */ 3074c2175ff5SMarius Strobl error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), /* parent */ 3075c42a7b7eSSam Leffler PAGE_SIZE, 0, /* alignment, bounds */ 3076c42a7b7eSSam Leffler BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 3077c42a7b7eSSam Leffler BUS_SPACE_MAXADDR, /* highaddr */ 3078c42a7b7eSSam Leffler NULL, NULL, /* filter, filterarg */ 3079c42a7b7eSSam Leffler dd->dd_desc_len, /* maxsize */ 3080c42a7b7eSSam Leffler 1, /* nsegments */ 30816ccb8ea7SSam Leffler dd->dd_desc_len, /* maxsegsize */ 3082c42a7b7eSSam Leffler BUS_DMA_ALLOCNOW, /* flags */ 3083c42a7b7eSSam Leffler NULL, /* lockfunc */ 3084c42a7b7eSSam Leffler NULL, /* lockarg */ 3085c42a7b7eSSam Leffler &dd->dd_dmat); 3086c42a7b7eSSam Leffler if (error != 0) { 3087c42a7b7eSSam Leffler if_printf(ifp, "cannot allocate %s DMA tag\n", dd->dd_name); 3088c42a7b7eSSam Leffler return error; 3089c42a7b7eSSam Leffler } 3090c42a7b7eSSam Leffler 3091c42a7b7eSSam Leffler /* allocate descriptors */ 3092c42a7b7eSSam Leffler error = bus_dmamap_create(dd->dd_dmat, BUS_DMA_NOWAIT, &dd->dd_dmamap); 3093c42a7b7eSSam Leffler if (error != 0) { 3094c42a7b7eSSam Leffler if_printf(ifp, "unable to create dmamap for %s descriptors, " 3095c42a7b7eSSam Leffler "error %u\n", dd->dd_name, error); 3096c42a7b7eSSam Leffler goto fail0; 3097c42a7b7eSSam Leffler } 3098c42a7b7eSSam Leffler 3099c42a7b7eSSam Leffler error = bus_dmamem_alloc(dd->dd_dmat, (void**) &dd->dd_desc, 31000553a01fSSam Leffler BUS_DMA_NOWAIT | BUS_DMA_COHERENT, 31010553a01fSSam Leffler &dd->dd_dmamap); 3102c42a7b7eSSam Leffler if (error != 0) { 3103c42a7b7eSSam Leffler if_printf(ifp, "unable to alloc memory for %u %s descriptors, " 3104c42a7b7eSSam Leffler "error %u\n", nbuf * ndesc, dd->dd_name, error); 3105c42a7b7eSSam Leffler goto fail1; 3106c42a7b7eSSam Leffler } 3107c42a7b7eSSam Leffler 3108c42a7b7eSSam Leffler error = bus_dmamap_load(dd->dd_dmat, dd->dd_dmamap, 3109c42a7b7eSSam Leffler dd->dd_desc, dd->dd_desc_len, 3110c42a7b7eSSam Leffler ath_load_cb, &dd->dd_desc_paddr, 3111c42a7b7eSSam Leffler BUS_DMA_NOWAIT); 3112c42a7b7eSSam Leffler if (error != 0) { 3113c42a7b7eSSam Leffler if_printf(ifp, "unable to map %s descriptors, error %u\n", 3114c42a7b7eSSam Leffler dd->dd_name, error); 3115c42a7b7eSSam Leffler goto fail2; 3116c42a7b7eSSam Leffler } 3117c42a7b7eSSam Leffler 311845abcd6cSAdrian Chadd ds = (uint8_t *) dd->dd_desc; 3119c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_RESET, "%s: %s DMA map: %p (%lu) -> %p (%lu)\n", 3120c42a7b7eSSam Leffler __func__, dd->dd_name, ds, (u_long) dd->dd_desc_len, 3121c42a7b7eSSam Leffler (caddr_t) dd->dd_desc_paddr, /*XXX*/ (u_long) dd->dd_desc_len); 3122c42a7b7eSSam Leffler 3123ebecf802SSam Leffler /* allocate rx buffers */ 3124c42a7b7eSSam Leffler bsize = sizeof(struct ath_buf) * nbuf; 3125c42a7b7eSSam Leffler bf = malloc(bsize, M_ATHDEV, M_NOWAIT | M_ZERO); 3126c42a7b7eSSam Leffler if (bf == NULL) { 3127c42a7b7eSSam Leffler if_printf(ifp, "malloc of %s buffers failed, size %u\n", 3128c42a7b7eSSam Leffler dd->dd_name, bsize); 3129c42a7b7eSSam Leffler goto fail3; 3130c42a7b7eSSam Leffler } 3131c42a7b7eSSam Leffler dd->dd_bufptr = bf; 3132c42a7b7eSSam Leffler 31336b349e5aSAdrian Chadd TAILQ_INIT(head); 313445abcd6cSAdrian Chadd for (i = 0; i < nbuf; i++, bf++, ds += (ndesc * desc_len)) { 313545abcd6cSAdrian Chadd bf->bf_desc = (struct ath_desc *) ds; 3136c42a7b7eSSam Leffler bf->bf_daddr = DS2PHYS(dd, ds); 313745abcd6cSAdrian Chadd if (! ath_hal_split4ktrans(sc->sc_ah)) { 313845abcd6cSAdrian Chadd /* 313945abcd6cSAdrian Chadd * Merlin WAR: Skip descriptor addresses which 314045abcd6cSAdrian Chadd * cause 4KB boundary crossing along any point 314145abcd6cSAdrian Chadd * in the descriptor. 314245abcd6cSAdrian Chadd */ 314345abcd6cSAdrian Chadd if (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr, 314445abcd6cSAdrian Chadd desc_len * ndesc)) { 314545abcd6cSAdrian Chadd /* Start at the next page */ 314645abcd6cSAdrian Chadd ds += 0x1000 - (bf->bf_daddr & 0xFFF); 314745abcd6cSAdrian Chadd bf->bf_desc = (struct ath_desc *) ds; 314845abcd6cSAdrian Chadd bf->bf_daddr = DS2PHYS(dd, ds); 314945abcd6cSAdrian Chadd } 315045abcd6cSAdrian Chadd } 3151c42a7b7eSSam Leffler error = bus_dmamap_create(sc->sc_dmat, BUS_DMA_NOWAIT, 3152c42a7b7eSSam Leffler &bf->bf_dmamap); 3153c42a7b7eSSam Leffler if (error != 0) { 3154c42a7b7eSSam Leffler if_printf(ifp, "unable to create dmamap for %s " 3155c42a7b7eSSam Leffler "buffer %u, error %u\n", dd->dd_name, i, error); 3156c42a7b7eSSam Leffler ath_descdma_cleanup(sc, dd, head); 3157c42a7b7eSSam Leffler return error; 3158c42a7b7eSSam Leffler } 31596b349e5aSAdrian Chadd TAILQ_INSERT_TAIL(head, bf, bf_list); 3160c42a7b7eSSam Leffler } 3161c42a7b7eSSam Leffler return 0; 3162c42a7b7eSSam Leffler fail3: 3163c42a7b7eSSam Leffler bus_dmamap_unload(dd->dd_dmat, dd->dd_dmamap); 3164c42a7b7eSSam Leffler fail2: 3165c42a7b7eSSam Leffler bus_dmamem_free(dd->dd_dmat, dd->dd_desc, dd->dd_dmamap); 3166c42a7b7eSSam Leffler fail1: 3167c42a7b7eSSam Leffler bus_dmamap_destroy(dd->dd_dmat, dd->dd_dmamap); 3168c42a7b7eSSam Leffler fail0: 3169c42a7b7eSSam Leffler bus_dma_tag_destroy(dd->dd_dmat); 3170c42a7b7eSSam Leffler memset(dd, 0, sizeof(*dd)); 3171c42a7b7eSSam Leffler return error; 3172c42a7b7eSSam Leffler #undef DS2PHYS 317345abcd6cSAdrian Chadd #undef ATH_DESC_4KB_BOUND_CHECK 3174c42a7b7eSSam Leffler } 3175c42a7b7eSSam Leffler 3176c42a7b7eSSam Leffler static void 3177c42a7b7eSSam Leffler ath_descdma_cleanup(struct ath_softc *sc, 3178c42a7b7eSSam Leffler struct ath_descdma *dd, ath_bufhead *head) 3179c42a7b7eSSam Leffler { 3180c42a7b7eSSam Leffler struct ath_buf *bf; 3181c42a7b7eSSam Leffler struct ieee80211_node *ni; 3182c42a7b7eSSam Leffler 3183c42a7b7eSSam Leffler bus_dmamap_unload(dd->dd_dmat, dd->dd_dmamap); 3184c42a7b7eSSam Leffler bus_dmamem_free(dd->dd_dmat, dd->dd_desc, dd->dd_dmamap); 3185c42a7b7eSSam Leffler bus_dmamap_destroy(dd->dd_dmat, dd->dd_dmamap); 3186c42a7b7eSSam Leffler bus_dma_tag_destroy(dd->dd_dmat); 3187c42a7b7eSSam Leffler 31886b349e5aSAdrian Chadd TAILQ_FOREACH(bf, head, bf_list) { 3189c42a7b7eSSam Leffler if (bf->bf_m) { 3190c42a7b7eSSam Leffler m_freem(bf->bf_m); 3191c42a7b7eSSam Leffler bf->bf_m = NULL; 3192c42a7b7eSSam Leffler } 3193c42a7b7eSSam Leffler if (bf->bf_dmamap != NULL) { 3194c42a7b7eSSam Leffler bus_dmamap_destroy(sc->sc_dmat, bf->bf_dmamap); 3195c42a7b7eSSam Leffler bf->bf_dmamap = NULL; 3196c42a7b7eSSam Leffler } 3197c42a7b7eSSam Leffler ni = bf->bf_node; 3198c42a7b7eSSam Leffler bf->bf_node = NULL; 3199c42a7b7eSSam Leffler if (ni != NULL) { 3200c42a7b7eSSam Leffler /* 3201c42a7b7eSSam Leffler * Reclaim node reference. 3202c42a7b7eSSam Leffler */ 3203c42a7b7eSSam Leffler ieee80211_free_node(ni); 3204c42a7b7eSSam Leffler } 3205c42a7b7eSSam Leffler } 3206c42a7b7eSSam Leffler 32076b349e5aSAdrian Chadd TAILQ_INIT(head); 3208c42a7b7eSSam Leffler free(dd->dd_bufptr, M_ATHDEV); 3209c42a7b7eSSam Leffler memset(dd, 0, sizeof(*dd)); 3210c42a7b7eSSam Leffler } 3211c42a7b7eSSam Leffler 3212c42a7b7eSSam Leffler static int 32135591b213SSam Leffler ath_desc_alloc(struct ath_softc *sc) 32145591b213SSam Leffler { 3215c42a7b7eSSam Leffler int error; 32165591b213SSam Leffler 3217c42a7b7eSSam Leffler error = ath_descdma_setup(sc, &sc->sc_rxdma, &sc->sc_rxbuf, 3218e2d787faSSam Leffler "rx", ath_rxbuf, 1); 32195591b213SSam Leffler if (error != 0) 32205591b213SSam Leffler return error; 32215591b213SSam Leffler 3222c42a7b7eSSam Leffler error = ath_descdma_setup(sc, &sc->sc_txdma, &sc->sc_txbuf, 3223e2d787faSSam Leffler "tx", ath_txbuf, ATH_TXDESC); 3224c42a7b7eSSam Leffler if (error != 0) { 3225c42a7b7eSSam Leffler ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf); 32265591b213SSam Leffler return error; 3227c42a7b7eSSam Leffler } 3228c42a7b7eSSam Leffler 3229c42a7b7eSSam Leffler error = ath_descdma_setup(sc, &sc->sc_bdma, &sc->sc_bbuf, 3230b032f27cSSam Leffler "beacon", ATH_BCBUF, 1); 3231c42a7b7eSSam Leffler if (error != 0) { 3232c42a7b7eSSam Leffler ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf); 3233c42a7b7eSSam Leffler ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf); 3234c42a7b7eSSam Leffler return error; 3235c42a7b7eSSam Leffler } 32365591b213SSam Leffler return 0; 32375591b213SSam Leffler } 32385591b213SSam Leffler 32395591b213SSam Leffler static void 32405591b213SSam Leffler ath_desc_free(struct ath_softc *sc) 32415591b213SSam Leffler { 32425591b213SSam Leffler 3243c42a7b7eSSam Leffler if (sc->sc_bdma.dd_desc_len != 0) 3244c42a7b7eSSam Leffler ath_descdma_cleanup(sc, &sc->sc_bdma, &sc->sc_bbuf); 3245c42a7b7eSSam Leffler if (sc->sc_txdma.dd_desc_len != 0) 3246c42a7b7eSSam Leffler ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf); 3247c42a7b7eSSam Leffler if (sc->sc_rxdma.dd_desc_len != 0) 3248c42a7b7eSSam Leffler ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf); 32495591b213SSam Leffler } 32505591b213SSam Leffler 32515591b213SSam Leffler static struct ieee80211_node * 325238c208f8SSam Leffler ath_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) 32535591b213SSam Leffler { 325438c208f8SSam Leffler struct ieee80211com *ic = vap->iv_ic; 3255c42a7b7eSSam Leffler struct ath_softc *sc = ic->ic_ifp->if_softc; 3256c42a7b7eSSam Leffler const size_t space = sizeof(struct ath_node) + sc->sc_rc->arc_space; 3257c42a7b7eSSam Leffler struct ath_node *an; 3258c42a7b7eSSam Leffler 3259c42a7b7eSSam Leffler an = malloc(space, M_80211_NODE, M_NOWAIT|M_ZERO); 3260c42a7b7eSSam Leffler if (an == NULL) { 3261c42a7b7eSSam Leffler /* XXX stat+msg */ 3262de5af704SSam Leffler return NULL; 32635591b213SSam Leffler } 3264c42a7b7eSSam Leffler ath_rate_node_init(sc, an); 32655591b213SSam Leffler 32663dd85b26SAdrian Chadd /* Setup the mutex - there's no associd yet so set the name to NULL */ 32673dd85b26SAdrian Chadd snprintf(an->an_name, sizeof(an->an_name), "%s: node %p", 32683dd85b26SAdrian Chadd device_get_nameunit(sc->sc_dev), an); 32693dd85b26SAdrian Chadd mtx_init(&an->an_mtx, an->an_name, NULL, MTX_DEF); 32703dd85b26SAdrian Chadd 3271c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_NODE, "%s: an %p\n", __func__, an); 3272c42a7b7eSSam Leffler return &an->an_node; 3273c42a7b7eSSam Leffler } 3274c42a7b7eSSam Leffler 32755591b213SSam Leffler static void 32764afa805eSAdrian Chadd ath_node_cleanup(struct ieee80211_node *ni) 32774afa805eSAdrian Chadd { 32784afa805eSAdrian Chadd struct ieee80211com *ic = ni->ni_ic; 32794afa805eSAdrian Chadd struct ath_softc *sc = ic->ic_ifp->if_softc; 32804afa805eSAdrian Chadd 32814afa805eSAdrian Chadd /* Cleanup ath_tid, free unused bufs, unlink bufs in TXQ */ 32824afa805eSAdrian Chadd ath_rate_node_cleanup(sc, ATH_NODE(ni)); 32834afa805eSAdrian Chadd sc->sc_node_cleanup(ni); 32844afa805eSAdrian Chadd } 32854afa805eSAdrian Chadd 32864afa805eSAdrian Chadd static void 3287c42a7b7eSSam Leffler ath_node_free(struct ieee80211_node *ni) 32885591b213SSam Leffler { 3289c42a7b7eSSam Leffler struct ieee80211com *ic = ni->ni_ic; 3290c42a7b7eSSam Leffler struct ath_softc *sc = ic->ic_ifp->if_softc; 32911e774079SSam Leffler 3292c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_NODE, "%s: ni %p\n", __func__, ni); 32933dd85b26SAdrian Chadd mtx_destroy(&ATH_NODE(ni)->an_mtx); 3294c42a7b7eSSam Leffler sc->sc_node_free(ni); 32955591b213SSam Leffler } 32965591b213SSam Leffler 329768e8e04eSSam Leffler static void 329868e8e04eSSam Leffler ath_node_getsignal(const struct ieee80211_node *ni, int8_t *rssi, int8_t *noise) 329968e8e04eSSam Leffler { 330068e8e04eSSam Leffler struct ieee80211com *ic = ni->ni_ic; 330168e8e04eSSam Leffler struct ath_softc *sc = ic->ic_ifp->if_softc; 330268e8e04eSSam Leffler struct ath_hal *ah = sc->sc_ah; 330368e8e04eSSam Leffler 3304b032f27cSSam Leffler *rssi = ic->ic_node_getrssi(ni); 330559efa8b5SSam Leffler if (ni->ni_chan != IEEE80211_CHAN_ANYC) 330659efa8b5SSam Leffler *noise = ath_hal_getchannoise(ah, ni->ni_chan); 330759efa8b5SSam Leffler else 330868e8e04eSSam Leffler *noise = -95; /* nominally correct */ 330968e8e04eSSam Leffler } 331068e8e04eSSam Leffler 33115591b213SSam Leffler static int 33125591b213SSam Leffler ath_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf) 33135591b213SSam Leffler { 33145591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 33155591b213SSam Leffler int error; 33165591b213SSam Leffler struct mbuf *m; 33175591b213SSam Leffler struct ath_desc *ds; 33185591b213SSam Leffler 33195591b213SSam Leffler m = bf->bf_m; 33205591b213SSam Leffler if (m == NULL) { 33215591b213SSam Leffler /* 33225591b213SSam Leffler * NB: by assigning a page to the rx dma buffer we 33235591b213SSam Leffler * implicitly satisfy the Atheros requirement that 33245591b213SSam Leffler * this buffer be cache-line-aligned and sized to be 33255591b213SSam Leffler * multiple of the cache line size. Not doing this 33265591b213SSam Leffler * causes weird stuff to happen (for the 5210 at least). 33275591b213SSam Leffler */ 33285591b213SSam Leffler m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); 33295591b213SSam Leffler if (m == NULL) { 3330c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, 3331c42a7b7eSSam Leffler "%s: no mbuf/cluster\n", __func__); 33325591b213SSam Leffler sc->sc_stats.ast_rx_nombuf++; 33335591b213SSam Leffler return ENOMEM; 33345591b213SSam Leffler } 33355591b213SSam Leffler m->m_pkthdr.len = m->m_len = m->m_ext.ext_size; 33365591b213SSam Leffler 3337f9e6219bSSam Leffler error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, 3338c42a7b7eSSam Leffler bf->bf_dmamap, m, 3339f9e6219bSSam Leffler bf->bf_segs, &bf->bf_nseg, 33405591b213SSam Leffler BUS_DMA_NOWAIT); 33415591b213SSam Leffler if (error != 0) { 3342c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, 3343f9e6219bSSam Leffler "%s: bus_dmamap_load_mbuf_sg failed; error %d\n", 3344c42a7b7eSSam Leffler __func__, error); 33455591b213SSam Leffler sc->sc_stats.ast_rx_busdma++; 3346b2792ff6SSam Leffler m_freem(m); 33475591b213SSam Leffler return error; 33485591b213SSam Leffler } 3349d77367bfSSam Leffler KASSERT(bf->bf_nseg == 1, 3350d77367bfSSam Leffler ("multi-segment packet; nseg %u", bf->bf_nseg)); 3351b2792ff6SSam Leffler bf->bf_m = m; 33525591b213SSam Leffler } 33535591b213SSam Leffler bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREREAD); 33545591b213SSam Leffler 335504e22a02SSam Leffler /* 335604e22a02SSam Leffler * Setup descriptors. For receive we always terminate 335704e22a02SSam Leffler * the descriptor list with a self-linked entry so we'll 335804e22a02SSam Leffler * not get overrun under high load (as can happen with a 3359c42a7b7eSSam Leffler * 5212 when ANI processing enables PHY error frames). 336004e22a02SSam Leffler * 336104e22a02SSam Leffler * To insure the last descriptor is self-linked we create 336204e22a02SSam Leffler * each descriptor as self-linked and add it to the end. As 336304e22a02SSam Leffler * each additional descriptor is added the previous self-linked 336404e22a02SSam Leffler * entry is ``fixed'' naturally. This should be safe even 336504e22a02SSam Leffler * if DMA is happening. When processing RX interrupts we 336604e22a02SSam Leffler * never remove/process the last, self-linked, entry on the 336704e22a02SSam Leffler * descriptor list. This insures the hardware always has 336804e22a02SSam Leffler * someplace to write a new frame. 336904e22a02SSam Leffler */ 33708a2a6beeSAdrian Chadd /* 33718a2a6beeSAdrian Chadd * 11N: we can no longer afford to self link the last descriptor. 33728a2a6beeSAdrian Chadd * MAC acknowledges BA status as long as it copies frames to host 33738a2a6beeSAdrian Chadd * buffer (or rx fifo). This can incorrectly acknowledge packets 33748a2a6beeSAdrian Chadd * to a sender if last desc is self-linked. 33758a2a6beeSAdrian Chadd */ 33765591b213SSam Leffler ds = bf->bf_desc; 33778a2a6beeSAdrian Chadd if (sc->sc_rxslink) 337804e22a02SSam Leffler ds->ds_link = bf->bf_daddr; /* link to self */ 33798a2a6beeSAdrian Chadd else 33808a2a6beeSAdrian Chadd ds->ds_link = 0; /* terminate the list */ 33815591b213SSam Leffler ds->ds_data = bf->bf_segs[0].ds_addr; 33825591b213SSam Leffler ath_hal_setuprxdesc(ah, ds 33835591b213SSam Leffler , m->m_len /* buffer size */ 33845591b213SSam Leffler , 0 33855591b213SSam Leffler ); 33865591b213SSam Leffler 33875591b213SSam Leffler if (sc->sc_rxlink != NULL) 33885591b213SSam Leffler *sc->sc_rxlink = bf->bf_daddr; 33895591b213SSam Leffler sc->sc_rxlink = &ds->ds_link; 33905591b213SSam Leffler return 0; 33915591b213SSam Leffler } 33925591b213SSam Leffler 3393c42a7b7eSSam Leffler /* 339403ed599aSSam Leffler * Extend 15-bit time stamp from rx descriptor to 33957b0c77ecSSam Leffler * a full 64-bit TSF using the specified TSF. 339603ed599aSSam Leffler */ 339703ed599aSSam Leffler static __inline u_int64_t 3398fc4de9b7SAdrian Chadd ath_extend_tsf15(u_int32_t rstamp, u_int64_t tsf) 339903ed599aSSam Leffler { 340003ed599aSSam Leffler if ((tsf & 0x7fff) < rstamp) 340103ed599aSSam Leffler tsf -= 0x8000; 3402fc4de9b7SAdrian Chadd 340303ed599aSSam Leffler return ((tsf &~ 0x7fff) | rstamp); 340403ed599aSSam Leffler } 340503ed599aSSam Leffler 340603ed599aSSam Leffler /* 3407fc4de9b7SAdrian Chadd * Extend 32-bit time stamp from rx descriptor to 3408fc4de9b7SAdrian Chadd * a full 64-bit TSF using the specified TSF. 3409fc4de9b7SAdrian Chadd */ 3410fc4de9b7SAdrian Chadd static __inline u_int64_t 3411fc4de9b7SAdrian Chadd ath_extend_tsf32(u_int32_t rstamp, u_int64_t tsf) 3412fc4de9b7SAdrian Chadd { 3413fc4de9b7SAdrian Chadd u_int32_t tsf_low = tsf & 0xffffffff; 3414fc4de9b7SAdrian Chadd u_int64_t tsf64 = (tsf & ~0xffffffffULL) | rstamp; 3415fc4de9b7SAdrian Chadd 3416fc4de9b7SAdrian Chadd if (rstamp > tsf_low && (rstamp - tsf_low > 0x10000000)) 3417fc4de9b7SAdrian Chadd tsf64 -= 0x100000000ULL; 3418fc4de9b7SAdrian Chadd 3419fc4de9b7SAdrian Chadd if (rstamp < tsf_low && (tsf_low - rstamp > 0x10000000)) 3420fc4de9b7SAdrian Chadd tsf64 += 0x100000000ULL; 3421fc4de9b7SAdrian Chadd 3422fc4de9b7SAdrian Chadd return tsf64; 3423fc4de9b7SAdrian Chadd } 3424fc4de9b7SAdrian Chadd 3425fc4de9b7SAdrian Chadd /* 3426fc4de9b7SAdrian Chadd * Extend the TSF from the RX descriptor to a full 64 bit TSF. 3427fc4de9b7SAdrian Chadd * Earlier hardware versions only wrote the low 15 bits of the 3428fc4de9b7SAdrian Chadd * TSF into the RX descriptor; later versions (AR5416 and up) 3429fc4de9b7SAdrian Chadd * include the 32 bit TSF value. 3430fc4de9b7SAdrian Chadd */ 3431fc4de9b7SAdrian Chadd static __inline u_int64_t 3432fc4de9b7SAdrian Chadd ath_extend_tsf(struct ath_softc *sc, u_int32_t rstamp, u_int64_t tsf) 3433fc4de9b7SAdrian Chadd { 3434fc4de9b7SAdrian Chadd if (sc->sc_rxtsf32) 3435fc4de9b7SAdrian Chadd return ath_extend_tsf32(rstamp, tsf); 3436fc4de9b7SAdrian Chadd else 3437fc4de9b7SAdrian Chadd return ath_extend_tsf15(rstamp, tsf); 3438fc4de9b7SAdrian Chadd } 3439fc4de9b7SAdrian Chadd 3440fc4de9b7SAdrian Chadd /* 3441c42a7b7eSSam Leffler * Intercept management frames to collect beacon rssi data 3442c42a7b7eSSam Leffler * and to do ibss merges. 3443c42a7b7eSSam Leffler */ 3444c42a7b7eSSam Leffler static void 3445b032f27cSSam Leffler ath_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, 34465463c4a4SSam Leffler int subtype, int rssi, int nf) 3447c42a7b7eSSam Leffler { 3448b032f27cSSam Leffler struct ieee80211vap *vap = ni->ni_vap; 3449b032f27cSSam Leffler struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; 3450c42a7b7eSSam Leffler 3451c42a7b7eSSam Leffler /* 3452c42a7b7eSSam Leffler * Call up first so subsequent work can use information 3453c42a7b7eSSam Leffler * potentially stored in the node (e.g. for ibss merge). 3454c42a7b7eSSam Leffler */ 34555463c4a4SSam Leffler ATH_VAP(vap)->av_recv_mgmt(ni, m, subtype, rssi, nf); 3456c42a7b7eSSam Leffler switch (subtype) { 3457c42a7b7eSSam Leffler case IEEE80211_FC0_SUBTYPE_BEACON: 3458c42a7b7eSSam Leffler /* update rssi statistics for use by the hal */ 3459ffa2cab6SSam Leffler ATH_RSSI_LPF(sc->sc_halstats.ns_avgbrssi, rssi); 346080d939bfSSam Leffler if (sc->sc_syncbeacon && 3461b032f27cSSam Leffler ni == vap->iv_bss && vap->iv_state == IEEE80211_S_RUN) { 346280d939bfSSam Leffler /* 346380d939bfSSam Leffler * Resync beacon timers using the tsf of the beacon 346480d939bfSSam Leffler * frame we just received. 346580d939bfSSam Leffler */ 3466b032f27cSSam Leffler ath_beacon_config(sc, vap); 346780d939bfSSam Leffler } 3468c42a7b7eSSam Leffler /* fall thru... */ 3469c42a7b7eSSam Leffler case IEEE80211_FC0_SUBTYPE_PROBE_RESP: 3470b032f27cSSam Leffler if (vap->iv_opmode == IEEE80211_M_IBSS && 3471b032f27cSSam Leffler vap->iv_state == IEEE80211_S_RUN) { 34727041d50cSBernhard Schmidt uint32_t rstamp = sc->sc_lastrs->rs_tstamp; 3473fc4de9b7SAdrian Chadd uint64_t tsf = ath_extend_tsf(sc, rstamp, 34747b0c77ecSSam Leffler ath_hal_gettsf64(sc->sc_ah)); 3475c42a7b7eSSam Leffler /* 3476c42a7b7eSSam Leffler * Handle ibss merge as needed; check the tsf on the 3477c42a7b7eSSam Leffler * frame before attempting the merge. The 802.11 spec 3478c42a7b7eSSam Leffler * says the station should change it's bssid to match 3479c42a7b7eSSam Leffler * the oldest station with the same ssid, where oldest 3480f818612bSSam Leffler * is determined by the tsf. Note that hardware 3481f818612bSSam Leffler * reconfiguration happens through callback to 348203ed599aSSam Leffler * ath_newstate as the state machine will go from 348303ed599aSSam Leffler * RUN -> RUN when this happens. 3484c42a7b7eSSam Leffler */ 348503ed599aSSam Leffler if (le64toh(ni->ni_tstamp.tsf) >= tsf) { 348603ed599aSSam Leffler DPRINTF(sc, ATH_DEBUG_STATE, 348733d7d80cSTai-hwa Liang "ibss merge, rstamp %u tsf %ju " 348833d7d80cSTai-hwa Liang "tstamp %ju\n", rstamp, (uintmax_t)tsf, 348933d7d80cSTai-hwa Liang (uintmax_t)ni->ni_tstamp.tsf); 3490641b4d0bSSam Leffler (void) ieee80211_ibss_merge(ni); 3491c42a7b7eSSam Leffler } 349203ed599aSSam Leffler } 3493c42a7b7eSSam Leffler break; 3494c42a7b7eSSam Leffler } 3495c42a7b7eSSam Leffler } 3496c42a7b7eSSam Leffler 3497c42a7b7eSSam Leffler /* 3498c42a7b7eSSam Leffler * Set the default antenna. 3499c42a7b7eSSam Leffler */ 3500c42a7b7eSSam Leffler static void 3501c42a7b7eSSam Leffler ath_setdefantenna(struct ath_softc *sc, u_int antenna) 3502c42a7b7eSSam Leffler { 3503c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 3504c42a7b7eSSam Leffler 3505c42a7b7eSSam Leffler /* XXX block beacon interrupts */ 3506c42a7b7eSSam Leffler ath_hal_setdefantenna(ah, antenna); 3507c42a7b7eSSam Leffler if (sc->sc_defant != antenna) 3508c42a7b7eSSam Leffler sc->sc_stats.ast_ant_defswitch++; 3509c42a7b7eSSam Leffler sc->sc_defant = antenna; 3510c42a7b7eSSam Leffler sc->sc_rxotherant = 0; 3511c42a7b7eSSam Leffler } 3512c42a7b7eSSam Leffler 35135463c4a4SSam Leffler static void 3514b032f27cSSam Leffler ath_rx_tap(struct ifnet *ifp, struct mbuf *m, 351565f9edeeSSam Leffler const struct ath_rx_status *rs, u_int64_t tsf, int16_t nf) 35167b0c77ecSSam Leffler { 3517e387d629SSam Leffler #define CHAN_HT20 htole32(IEEE80211_CHAN_HT20) 3518e387d629SSam Leffler #define CHAN_HT40U htole32(IEEE80211_CHAN_HT40U) 3519e387d629SSam Leffler #define CHAN_HT40D htole32(IEEE80211_CHAN_HT40D) 352046d4d74cSSam Leffler #define CHAN_HT (CHAN_HT20|CHAN_HT40U|CHAN_HT40D) 3521b032f27cSSam Leffler struct ath_softc *sc = ifp->if_softc; 352246d4d74cSSam Leffler const HAL_RATE_TABLE *rt; 352346d4d74cSSam Leffler uint8_t rix; 35247b0c77ecSSam Leffler 352546d4d74cSSam Leffler rt = sc->sc_currates; 352646d4d74cSSam Leffler KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode)); 352746d4d74cSSam Leffler rix = rt->rateCodeToIndex[rs->rs_rate]; 352868e8e04eSSam Leffler sc->sc_rx_th.wr_rate = sc->sc_hwmap[rix].ieeerate; 35297b0c77ecSSam Leffler sc->sc_rx_th.wr_flags = sc->sc_hwmap[rix].rxflags; 353046d4d74cSSam Leffler #ifdef AH_SUPPORT_AR5416 3531e387d629SSam Leffler sc->sc_rx_th.wr_chan_flags &= ~CHAN_HT; 353246d4d74cSSam Leffler if (sc->sc_rx_th.wr_rate & IEEE80211_RATE_MCS) { /* HT rate */ 353359efa8b5SSam Leffler struct ieee80211com *ic = ifp->if_l2com; 353459efa8b5SSam Leffler 3535e387d629SSam Leffler if ((rs->rs_flags & HAL_RX_2040) == 0) 3536e387d629SSam Leffler sc->sc_rx_th.wr_chan_flags |= CHAN_HT20; 353759efa8b5SSam Leffler else if (IEEE80211_IS_CHAN_HT40U(ic->ic_curchan)) 3538e387d629SSam Leffler sc->sc_rx_th.wr_chan_flags |= CHAN_HT40U; 3539e387d629SSam Leffler else 3540e387d629SSam Leffler sc->sc_rx_th.wr_chan_flags |= CHAN_HT40D; 354168e8e04eSSam Leffler if ((rs->rs_flags & HAL_RX_GI) == 0) 3542e387d629SSam Leffler sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_SHORTGI; 354368e8e04eSSam Leffler } 354468e8e04eSSam Leffler #endif 3545fc4de9b7SAdrian Chadd sc->sc_rx_th.wr_tsf = htole64(ath_extend_tsf(sc, rs->rs_tstamp, tsf)); 354665f9edeeSSam Leffler if (rs->rs_status & HAL_RXERR_CRC) 35477b0c77ecSSam Leffler sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_BADFCS; 35487b0c77ecSSam Leffler /* XXX propagate other error flags from descriptor */ 35497b0c77ecSSam Leffler sc->sc_rx_th.wr_antnoise = nf; 35505463c4a4SSam Leffler sc->sc_rx_th.wr_antsignal = nf + rs->rs_rssi; 355165f9edeeSSam Leffler sc->sc_rx_th.wr_antenna = rs->rs_antenna; 355246d4d74cSSam Leffler #undef CHAN_HT 3553e387d629SSam Leffler #undef CHAN_HT20 3554e387d629SSam Leffler #undef CHAN_HT40U 3555e387d629SSam Leffler #undef CHAN_HT40D 35567b0c77ecSSam Leffler } 35577b0c77ecSSam Leffler 35585591b213SSam Leffler static void 3559b032f27cSSam Leffler ath_handle_micerror(struct ieee80211com *ic, 3560b032f27cSSam Leffler struct ieee80211_frame *wh, int keyix) 3561b032f27cSSam Leffler { 3562b032f27cSSam Leffler struct ieee80211_node *ni; 3563b032f27cSSam Leffler 3564b032f27cSSam Leffler /* XXX recheck MIC to deal w/ chips that lie */ 3565b032f27cSSam Leffler /* XXX discard MIC errors on !data frames */ 3566b032f27cSSam Leffler ni = ieee80211_find_rxnode(ic, (const struct ieee80211_frame_min *) wh); 3567b032f27cSSam Leffler if (ni != NULL) { 3568b032f27cSSam Leffler ieee80211_notify_michael_failure(ni->ni_vap, wh, keyix); 3569b032f27cSSam Leffler ieee80211_free_node(ni); 3570b032f27cSSam Leffler } 3571b032f27cSSam Leffler } 3572b032f27cSSam Leffler 357396ff485dSAdrian Chadd /* 357496ff485dSAdrian Chadd * Only run the RX proc if it's not already running. 357596ff485dSAdrian Chadd * Since this may get run as part of the reset/flush path, 357696ff485dSAdrian Chadd * the task can't clash with an existing, running tasklet. 357796ff485dSAdrian Chadd */ 3578b032f27cSSam Leffler static void 357996ff485dSAdrian Chadd ath_rx_tasklet(void *arg, int npending) 358096ff485dSAdrian Chadd { 358196ff485dSAdrian Chadd struct ath_softc *sc = arg; 358296ff485dSAdrian Chadd 358396ff485dSAdrian Chadd CTR1(ATH_KTR_INTR, "ath_rx_proc: pending=%d", npending); 358496ff485dSAdrian Chadd DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s: pending %u\n", __func__, npending); 358596ff485dSAdrian Chadd ath_rx_proc(sc, 1); 358696ff485dSAdrian Chadd } 358796ff485dSAdrian Chadd 358896ff485dSAdrian Chadd static void 358996ff485dSAdrian Chadd ath_rx_proc(struct ath_softc *sc, int resched) 35905591b213SSam Leffler { 35918cec0ab9SSam Leffler #define PA2DESC(_sc, _pa) \ 3592c42a7b7eSSam Leffler ((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \ 3593c42a7b7eSSam Leffler ((_pa) - (_sc)->sc_rxdma.dd_desc_paddr))) 35945591b213SSam Leffler struct ath_buf *bf; 3595fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 3596b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 35975591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 35985591b213SSam Leffler struct ath_desc *ds; 359965f9edeeSSam Leffler struct ath_rx_status *rs; 36005591b213SSam Leffler struct mbuf *m; 36010a915fadSSam Leffler struct ieee80211_node *ni; 3602d7736e13SSam Leffler int len, type, ngood; 36035591b213SSam Leffler HAL_STATUS status; 36047b0c77ecSSam Leffler int16_t nf; 36057b0c77ecSSam Leffler u_int64_t tsf; 36068f939e79SAdrian Chadd int npkts = 0; 36075591b213SSam Leffler 360896ff485dSAdrian Chadd DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s: called\n", __func__); 3609d7736e13SSam Leffler ngood = 0; 361059efa8b5SSam Leffler nf = ath_hal_getchannoise(ah, sc->sc_curchan); 361184784be1SSam Leffler sc->sc_stats.ast_rx_noise = nf; 36127b0c77ecSSam Leffler tsf = ath_hal_gettsf64(ah); 36135591b213SSam Leffler do { 36146b349e5aSAdrian Chadd bf = TAILQ_FIRST(&sc->sc_rxbuf); 36158a2a6beeSAdrian Chadd if (sc->sc_rxslink && bf == NULL) { /* NB: shouldn't happen */ 3616c42a7b7eSSam Leffler if_printf(ifp, "%s: no buffer!\n", __func__); 36175591b213SSam Leffler break; 36188a2a6beeSAdrian Chadd } else if (bf == NULL) { 36198a2a6beeSAdrian Chadd /* 36208a2a6beeSAdrian Chadd * End of List: 36218a2a6beeSAdrian Chadd * this can happen for non-self-linked RX chains 36228a2a6beeSAdrian Chadd */ 36238a2a6beeSAdrian Chadd sc->sc_stats.ast_rx_hitqueueend++; 36248a2a6beeSAdrian Chadd break; 36255591b213SSam Leffler } 3626b2792ff6SSam Leffler m = bf->bf_m; 3627b2792ff6SSam Leffler if (m == NULL) { /* NB: shouldn't happen */ 3628b2792ff6SSam Leffler /* 3629b2792ff6SSam Leffler * If mbuf allocation failed previously there 3630b2792ff6SSam Leffler * will be no mbuf; try again to re-populate it. 3631b2792ff6SSam Leffler */ 3632b2792ff6SSam Leffler /* XXX make debug msg */ 3633b2792ff6SSam Leffler if_printf(ifp, "%s: no mbuf!\n", __func__); 36346b349e5aSAdrian Chadd TAILQ_REMOVE(&sc->sc_rxbuf, bf, bf_list); 3635b2792ff6SSam Leffler goto rx_next; 3636b2792ff6SSam Leffler } 363704e22a02SSam Leffler ds = bf->bf_desc; 363804e22a02SSam Leffler if (ds->ds_link == bf->bf_daddr) { 363904e22a02SSam Leffler /* NB: never process the self-linked entry at the end */ 3640f77057dbSAdrian Chadd sc->sc_stats.ast_rx_hitqueueend++; 364104e22a02SSam Leffler break; 364204e22a02SSam Leffler } 36438cec0ab9SSam Leffler /* XXX sync descriptor memory */ 36448cec0ab9SSam Leffler /* 36458cec0ab9SSam Leffler * Must provide the virtual address of the current 36468cec0ab9SSam Leffler * descriptor, the physical address, and the virtual 36478cec0ab9SSam Leffler * address of the next descriptor in the h/w chain. 36488cec0ab9SSam Leffler * This allows the HAL to look ahead to see if the 36498cec0ab9SSam Leffler * hardware is done with a descriptor by checking the 36508cec0ab9SSam Leffler * done bit in the following descriptor and the address 36518cec0ab9SSam Leffler * of the current descriptor the DMA engine is working 36528cec0ab9SSam Leffler * on. All this is necessary because of our use of 36538cec0ab9SSam Leffler * a self-linked list to avoid rx overruns. 36548cec0ab9SSam Leffler */ 365565f9edeeSSam Leffler rs = &bf->bf_status.ds_rxstat; 36568cec0ab9SSam Leffler status = ath_hal_rxprocdesc(ah, ds, 365765f9edeeSSam Leffler bf->bf_daddr, PA2DESC(sc, ds->ds_link), rs); 3658a585a9a1SSam Leffler #ifdef ATH_DEBUG 3659c42a7b7eSSam Leffler if (sc->sc_debug & ATH_DEBUG_RECV_DESC) 36606902009eSSam Leffler ath_printrxbuf(sc, bf, 0, status == HAL_OK); 36615591b213SSam Leffler #endif 36625591b213SSam Leffler if (status == HAL_EINPROGRESS) 36635591b213SSam Leffler break; 36646b349e5aSAdrian Chadd 36656b349e5aSAdrian Chadd TAILQ_REMOVE(&sc->sc_rxbuf, bf, bf_list); 36668f939e79SAdrian Chadd npkts++; 3667f9aa1d90SAdrian Chadd 3668f9aa1d90SAdrian Chadd /* These aren't specifically errors */ 3669f9aa1d90SAdrian Chadd if (rs->rs_flags & HAL_RX_GI) 3670f9aa1d90SAdrian Chadd sc->sc_stats.ast_rx_halfgi++; 3671f9aa1d90SAdrian Chadd if (rs->rs_flags & HAL_RX_2040) 3672f9aa1d90SAdrian Chadd sc->sc_stats.ast_rx_2040++; 3673f9aa1d90SAdrian Chadd if (rs->rs_flags & HAL_RX_DELIM_CRC_PRE) 3674f9aa1d90SAdrian Chadd sc->sc_stats.ast_rx_pre_crc_err++; 3675f9aa1d90SAdrian Chadd if (rs->rs_flags & HAL_RX_DELIM_CRC_POST) 3676f9aa1d90SAdrian Chadd sc->sc_stats.ast_rx_post_crc_err++; 3677f9aa1d90SAdrian Chadd if (rs->rs_flags & HAL_RX_DECRYPT_BUSY) 3678f9aa1d90SAdrian Chadd sc->sc_stats.ast_rx_decrypt_busy_err++; 3679f9aa1d90SAdrian Chadd if (rs->rs_flags & HAL_RX_HI_RX_CHAIN) 3680f9aa1d90SAdrian Chadd sc->sc_stats.ast_rx_hi_rx_chain++; 3681f9aa1d90SAdrian Chadd 368268e8e04eSSam Leffler if (rs->rs_status != 0) { 368365f9edeeSSam Leffler if (rs->rs_status & HAL_RXERR_CRC) 36845591b213SSam Leffler sc->sc_stats.ast_rx_crcerr++; 368565f9edeeSSam Leffler if (rs->rs_status & HAL_RXERR_FIFO) 36865591b213SSam Leffler sc->sc_stats.ast_rx_fifoerr++; 368765f9edeeSSam Leffler if (rs->rs_status & HAL_RXERR_PHY) { 36885591b213SSam Leffler sc->sc_stats.ast_rx_phyerr++; 368948237774SAdrian Chadd /* Process DFS radar events */ 3690373815efSAdrian Chadd if ((rs->rs_phyerr == HAL_PHYERR_RADAR) || 3691373815efSAdrian Chadd (rs->rs_phyerr == HAL_PHYERR_FALSE_RADAR_EXT)) { 3692373815efSAdrian Chadd /* Since we're touching the frame data, sync it */ 3693373815efSAdrian Chadd bus_dmamap_sync(sc->sc_dmat, 3694373815efSAdrian Chadd bf->bf_dmamap, 3695373815efSAdrian Chadd BUS_DMASYNC_POSTREAD); 3696373815efSAdrian Chadd /* Now pass it to the radar processing code */ 36977e5eb44dSAdrian Chadd ath_dfs_process_phy_err(sc, mtod(m, char *), tsf, rs); 3698373815efSAdrian Chadd } 369948237774SAdrian Chadd 3700f9aa1d90SAdrian Chadd /* Be suitably paranoid about receiving phy errors out of the stats array bounds */ 3701f9aa1d90SAdrian Chadd if (rs->rs_phyerr < 64) 3702f9aa1d90SAdrian Chadd sc->sc_stats.ast_rx_phy[rs->rs_phyerr]++; 370368e8e04eSSam Leffler goto rx_error; /* NB: don't count in ierrors */ 3704c42a7b7eSSam Leffler } 370565f9edeeSSam Leffler if (rs->rs_status & HAL_RXERR_DECRYPT) { 370685643802SSam Leffler /* 3707c42a7b7eSSam Leffler * Decrypt error. If the error occurred 3708c42a7b7eSSam Leffler * because there was no hardware key, then 3709c42a7b7eSSam Leffler * let the frame through so the upper layers 3710c42a7b7eSSam Leffler * can process it. This is necessary for 5210 3711c42a7b7eSSam Leffler * parts which have no way to setup a ``clear'' 3712c42a7b7eSSam Leffler * key cache entry. 3713c42a7b7eSSam Leffler * 3714c42a7b7eSSam Leffler * XXX do key cache faulting 371585643802SSam Leffler */ 371665f9edeeSSam Leffler if (rs->rs_keyix == HAL_RXKEYIX_INVALID) 3717c42a7b7eSSam Leffler goto rx_accept; 3718c42a7b7eSSam Leffler sc->sc_stats.ast_rx_badcrypt++; 37195591b213SSam Leffler } 372065f9edeeSSam Leffler if (rs->rs_status & HAL_RXERR_MIC) { 3721c42a7b7eSSam Leffler sc->sc_stats.ast_rx_badmic++; 3722c42a7b7eSSam Leffler /* 3723c42a7b7eSSam Leffler * Do minimal work required to hand off 37245463c4a4SSam Leffler * the 802.11 header for notification. 3725c42a7b7eSSam Leffler */ 3726c42a7b7eSSam Leffler /* XXX frag's and qos frames */ 372765f9edeeSSam Leffler len = rs->rs_datalen; 3728c42a7b7eSSam Leffler if (len >= sizeof (struct ieee80211_frame)) { 3729c42a7b7eSSam Leffler bus_dmamap_sync(sc->sc_dmat, 3730c42a7b7eSSam Leffler bf->bf_dmamap, 3731c42a7b7eSSam Leffler BUS_DMASYNC_POSTREAD); 3732b032f27cSSam Leffler ath_handle_micerror(ic, 3733c42a7b7eSSam Leffler mtod(m, struct ieee80211_frame *), 37340ab4040aSSam Leffler sc->sc_splitmic ? 3735b032f27cSSam Leffler rs->rs_keyix-32 : rs->rs_keyix); 3736c42a7b7eSSam Leffler } 3737c42a7b7eSSam Leffler } 3738c42a7b7eSSam Leffler ifp->if_ierrors++; 373968e8e04eSSam Leffler rx_error: 374068e8e04eSSam Leffler /* 374168e8e04eSSam Leffler * Cleanup any pending partial frame. 374268e8e04eSSam Leffler */ 374368e8e04eSSam Leffler if (sc->sc_rxpending != NULL) { 374468e8e04eSSam Leffler m_freem(sc->sc_rxpending); 374568e8e04eSSam Leffler sc->sc_rxpending = NULL; 374668e8e04eSSam Leffler } 3747c42a7b7eSSam Leffler /* 37487b0c77ecSSam Leffler * When a tap is present pass error frames 37497b0c77ecSSam Leffler * that have been requested. By default we 37507b0c77ecSSam Leffler * pass decrypt+mic errors but others may be 37517b0c77ecSSam Leffler * interesting (e.g. crc). 3752c42a7b7eSSam Leffler */ 37535463c4a4SSam Leffler if (ieee80211_radiotap_active(ic) && 375465f9edeeSSam Leffler (rs->rs_status & sc->sc_monpass)) { 37557b0c77ecSSam Leffler bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, 37567b0c77ecSSam Leffler BUS_DMASYNC_POSTREAD); 37577b0c77ecSSam Leffler /* NB: bpf needs the mbuf length setup */ 375865f9edeeSSam Leffler len = rs->rs_datalen; 37597b0c77ecSSam Leffler m->m_pkthdr.len = m->m_len = len; 3760dcfd99a7SAdrian Chadd bf->bf_m = NULL; 37615463c4a4SSam Leffler ath_rx_tap(ifp, m, rs, tsf, nf); 37625463c4a4SSam Leffler ieee80211_radiotap_rx_all(ic, m); 3763dcfd99a7SAdrian Chadd m_freem(m); 37647b0c77ecSSam Leffler } 37657b0c77ecSSam Leffler /* XXX pass MIC errors up for s/w reclaculation */ 37665591b213SSam Leffler goto rx_next; 37675591b213SSam Leffler } 3768c42a7b7eSSam Leffler rx_accept: 3769c42a7b7eSSam Leffler /* 3770c42a7b7eSSam Leffler * Sync and unmap the frame. At this point we're 3771c42a7b7eSSam Leffler * committed to passing the mbuf somewhere so clear 3772c66c48cbSSam Leffler * bf_m; this means a new mbuf must be allocated 3773c42a7b7eSSam Leffler * when the rx descriptor is setup again to receive 3774c42a7b7eSSam Leffler * another frame. 3775c42a7b7eSSam Leffler */ 37765591b213SSam Leffler bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, 37775591b213SSam Leffler BUS_DMASYNC_POSTREAD); 37785591b213SSam Leffler bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 37795591b213SSam Leffler bf->bf_m = NULL; 3780c42a7b7eSSam Leffler 378165f9edeeSSam Leffler len = rs->rs_datalen; 378268e8e04eSSam Leffler m->m_len = len; 378368e8e04eSSam Leffler 378468e8e04eSSam Leffler if (rs->rs_more) { 378568e8e04eSSam Leffler /* 378668e8e04eSSam Leffler * Frame spans multiple descriptors; save 378768e8e04eSSam Leffler * it for the next completed descriptor, it 378868e8e04eSSam Leffler * will be used to construct a jumbogram. 378968e8e04eSSam Leffler */ 379068e8e04eSSam Leffler if (sc->sc_rxpending != NULL) { 379168e8e04eSSam Leffler /* NB: max frame size is currently 2 clusters */ 379268e8e04eSSam Leffler sc->sc_stats.ast_rx_toobig++; 379368e8e04eSSam Leffler m_freem(sc->sc_rxpending); 379468e8e04eSSam Leffler } 379568e8e04eSSam Leffler m->m_pkthdr.rcvif = ifp; 379668e8e04eSSam Leffler m->m_pkthdr.len = len; 379768e8e04eSSam Leffler sc->sc_rxpending = m; 379868e8e04eSSam Leffler goto rx_next; 379968e8e04eSSam Leffler } else if (sc->sc_rxpending != NULL) { 380068e8e04eSSam Leffler /* 380168e8e04eSSam Leffler * This is the second part of a jumbogram, 380268e8e04eSSam Leffler * chain it to the first mbuf, adjust the 380368e8e04eSSam Leffler * frame length, and clear the rxpending state. 380468e8e04eSSam Leffler */ 380568e8e04eSSam Leffler sc->sc_rxpending->m_next = m; 380668e8e04eSSam Leffler sc->sc_rxpending->m_pkthdr.len += len; 380768e8e04eSSam Leffler m = sc->sc_rxpending; 380868e8e04eSSam Leffler sc->sc_rxpending = NULL; 380968e8e04eSSam Leffler } else { 381068e8e04eSSam Leffler /* 381168e8e04eSSam Leffler * Normal single-descriptor receive; setup 381268e8e04eSSam Leffler * the rcvif and packet length. 381368e8e04eSSam Leffler */ 381468e8e04eSSam Leffler m->m_pkthdr.rcvif = ifp; 381568e8e04eSSam Leffler m->m_pkthdr.len = len; 381668e8e04eSSam Leffler } 381773454c73SSam Leffler 3818b032f27cSSam Leffler ifp->if_ipackets++; 381965f9edeeSSam Leffler sc->sc_stats.ast_ant_rx[rs->rs_antenna]++; 3820c42a7b7eSSam Leffler 38215463c4a4SSam Leffler /* 38225463c4a4SSam Leffler * Populate the rx status block. When there are bpf 38235463c4a4SSam Leffler * listeners we do the additional work to provide 38245463c4a4SSam Leffler * complete status. Otherwise we fill in only the 38255463c4a4SSam Leffler * material required by ieee80211_input. Note that 38265463c4a4SSam Leffler * noise setting is filled in above. 38275463c4a4SSam Leffler */ 38285463c4a4SSam Leffler if (ieee80211_radiotap_active(ic)) 38295463c4a4SSam Leffler ath_rx_tap(ifp, m, rs, tsf, nf); 38300a915fadSSam Leffler 38315591b213SSam Leffler /* 3832c42a7b7eSSam Leffler * From this point on we assume the frame is at least 3833c42a7b7eSSam Leffler * as large as ieee80211_frame_min; verify that. 38345591b213SSam Leffler */ 3835c42a7b7eSSam Leffler if (len < IEEE80211_MIN_LEN) { 38365463c4a4SSam Leffler if (!ieee80211_radiotap_active(ic)) { 38375463c4a4SSam Leffler DPRINTF(sc, ATH_DEBUG_RECV, 38385463c4a4SSam Leffler "%s: short packet %d\n", __func__, len); 3839c42a7b7eSSam Leffler sc->sc_stats.ast_rx_tooshort++; 38405463c4a4SSam Leffler } else { 38415463c4a4SSam Leffler /* NB: in particular this captures ack's */ 38425463c4a4SSam Leffler ieee80211_radiotap_rx_all(ic, m); 38435463c4a4SSam Leffler } 3844c42a7b7eSSam Leffler m_freem(m); 3845c42a7b7eSSam Leffler goto rx_next; 38465591b213SSam Leffler } 38470a915fadSSam Leffler 3848c42a7b7eSSam Leffler if (IFF_DUMPPKTS(sc, ATH_DEBUG_RECV)) { 384946d4d74cSSam Leffler const HAL_RATE_TABLE *rt = sc->sc_currates; 385046d4d74cSSam Leffler uint8_t rix = rt->rateCodeToIndex[rs->rs_rate]; 385146d4d74cSSam Leffler 385268e8e04eSSam Leffler ieee80211_dump_pkt(ic, mtod(m, caddr_t), len, 385346d4d74cSSam Leffler sc->sc_hwmap[rix].ieeerate, rs->rs_rssi); 3854c42a7b7eSSam Leffler } 3855c42a7b7eSSam Leffler 3856c42a7b7eSSam Leffler m_adj(m, -IEEE80211_CRC_LEN); 3857de5af704SSam Leffler 3858de5af704SSam Leffler /* 3859c42a7b7eSSam Leffler * Locate the node for sender, track state, and then 3860c42a7b7eSSam Leffler * pass the (referenced) node up to the 802.11 layer 3861c42a7b7eSSam Leffler * for its use. 3862c42a7b7eSSam Leffler */ 3863c1225b52SSam Leffler ni = ieee80211_find_rxnode_withkey(ic, 3864c1225b52SSam Leffler mtod(m, const struct ieee80211_frame_min *), 386565f9edeeSSam Leffler rs->rs_keyix == HAL_RXKEYIX_INVALID ? 386665f9edeeSSam Leffler IEEE80211_KEYIX_NONE : rs->rs_keyix); 38677041d50cSBernhard Schmidt sc->sc_lastrs = rs; 3868a07e9ddbSAdrian Chadd 3869a07e9ddbSAdrian Chadd if (rs->rs_isaggr) 3870a07e9ddbSAdrian Chadd sc->sc_stats.ast_rx_agg++; 3871a07e9ddbSAdrian Chadd 3872a07e9ddbSAdrian Chadd if (ni != NULL) { 3873b032f27cSSam Leffler /* 3874e57539afSAdrian Chadd * Only punt packets for ampdu reorder processing for 3875e57539afSAdrian Chadd * 11n nodes; net80211 enforces that M_AMPDU is only 3876e57539afSAdrian Chadd * set for 11n nodes. 387700fc8705SAdrian Chadd */ 387800fc8705SAdrian Chadd if (ni->ni_flags & IEEE80211_NODE_HT) 387900fc8705SAdrian Chadd m->m_flags |= M_AMPDU; 388000fc8705SAdrian Chadd 388100fc8705SAdrian Chadd /* 3882b032f27cSSam Leffler * Sending station is known, dispatch directly. 3883b032f27cSSam Leffler */ 38845463c4a4SSam Leffler type = ieee80211_input(ni, m, rs->rs_rssi, nf); 3885b032f27cSSam Leffler ieee80211_free_node(ni); 3886b032f27cSSam Leffler /* 3887b032f27cSSam Leffler * Arrange to update the last rx timestamp only for 3888b032f27cSSam Leffler * frames from our ap when operating in station mode. 3889b032f27cSSam Leffler * This assumes the rx key is always setup when 3890b032f27cSSam Leffler * associated. 3891b032f27cSSam Leffler */ 3892b032f27cSSam Leffler if (ic->ic_opmode == IEEE80211_M_STA && 3893b032f27cSSam Leffler rs->rs_keyix != HAL_RXKEYIX_INVALID) 3894b032f27cSSam Leffler ngood++; 3895b032f27cSSam Leffler } else { 38965463c4a4SSam Leffler type = ieee80211_input_all(ic, m, rs->rs_rssi, nf); 3897b032f27cSSam Leffler } 3898c42a7b7eSSam Leffler /* 3899c42a7b7eSSam Leffler * Track rx rssi and do any rx antenna management. 3900de5af704SSam Leffler */ 390165f9edeeSSam Leffler ATH_RSSI_LPF(sc->sc_halstats.ns_avgrssi, rs->rs_rssi); 3902c42a7b7eSSam Leffler if (sc->sc_diversity) { 3903c42a7b7eSSam Leffler /* 3904c42a7b7eSSam Leffler * When using fast diversity, change the default rx 3905c42a7b7eSSam Leffler * antenna if diversity chooses the other antenna 3 3906c42a7b7eSSam Leffler * times in a row. 3907c42a7b7eSSam Leffler */ 390865f9edeeSSam Leffler if (sc->sc_defant != rs->rs_antenna) { 3909c42a7b7eSSam Leffler if (++sc->sc_rxotherant >= 3) 391065f9edeeSSam Leffler ath_setdefantenna(sc, rs->rs_antenna); 3911c42a7b7eSSam Leffler } else 3912c42a7b7eSSam Leffler sc->sc_rxotherant = 0; 3913c42a7b7eSSam Leffler } 3914235ab70eSAdrian Chadd 3915235ab70eSAdrian Chadd /* Newer school diversity - kite specific for now */ 3916235ab70eSAdrian Chadd /* XXX perhaps migrate the normal diversity code to this? */ 3917235ab70eSAdrian Chadd if ((ah)->ah_rxAntCombDiversity) 3918235ab70eSAdrian Chadd (*(ah)->ah_rxAntCombDiversity)(ah, rs, ticks, hz); 3919235ab70eSAdrian Chadd 39203e50ec2cSSam Leffler if (sc->sc_softled) { 39213e50ec2cSSam Leffler /* 39223e50ec2cSSam Leffler * Blink for any data frame. Otherwise do a 39233e50ec2cSSam Leffler * heartbeat-style blink when idle. The latter 39243e50ec2cSSam Leffler * is mainly for station mode where we depend on 39253e50ec2cSSam Leffler * periodic beacon frames to trigger the poll event. 39263e50ec2cSSam Leffler */ 392731640eb7SSam Leffler if (type == IEEE80211_FC0_TYPE_DATA) { 392846d4d74cSSam Leffler const HAL_RATE_TABLE *rt = sc->sc_currates; 392946d4d74cSSam Leffler ath_led_event(sc, 393046d4d74cSSam Leffler rt->rateCodeToIndex[rs->rs_rate]); 39313e50ec2cSSam Leffler } else if (ticks - sc->sc_ledevent >= sc->sc_ledidle) 393246d4d74cSSam Leffler ath_led_event(sc, 0); 39333e50ec2cSSam Leffler } 39345591b213SSam Leffler rx_next: 39356b349e5aSAdrian Chadd TAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list); 39365591b213SSam Leffler } while (ath_rxbuf_init(sc, bf) == 0); 39375591b213SSam Leffler 3938c42a7b7eSSam Leffler /* rx signal state monitoring */ 393959efa8b5SSam Leffler ath_hal_rxmonitor(ah, &sc->sc_halstats, sc->sc_curchan); 3940d7736e13SSam Leffler if (ngood) 3941d7736e13SSam Leffler sc->sc_lastrx = tsf; 3942b5f4adb3SSam Leffler 3943f52d3452SAdrian Chadd CTR2(ATH_KTR_INTR, "ath_rx_proc: npkts=%d, ngood=%d", npkts, ngood); 394448237774SAdrian Chadd /* Queue DFS tasklet if needed */ 394596ff485dSAdrian Chadd if (resched && ath_dfs_tasklet_needed(sc, sc->sc_curchan)) 394648237774SAdrian Chadd taskqueue_enqueue(sc->sc_tq, &sc->sc_dfstask); 394748237774SAdrian Chadd 39481fdadc0fSAdrian Chadd /* 39491fdadc0fSAdrian Chadd * Now that all the RX frames were handled that 39501fdadc0fSAdrian Chadd * need to be handled, kick the PCU if there's 39511fdadc0fSAdrian Chadd * been an RXEOL condition. 39521fdadc0fSAdrian Chadd */ 395396ff485dSAdrian Chadd if (resched && sc->sc_kickpcu) { 3954f52d3452SAdrian Chadd CTR0(ATH_KTR_ERR, "ath_rx_proc: kickpcu"); 39558f939e79SAdrian Chadd device_printf(sc->sc_dev, "%s: kickpcu; handled %d packets\n", 39568f939e79SAdrian Chadd __func__, npkts); 39578f939e79SAdrian Chadd 39588f939e79SAdrian Chadd /* XXX rxslink? */ 39598f939e79SAdrian Chadd bf = TAILQ_FIRST(&sc->sc_rxbuf); 39608f939e79SAdrian Chadd ath_hal_putrxbuf(ah, bf->bf_daddr); 39618f939e79SAdrian Chadd ath_hal_rxena(ah); /* enable recv descriptors */ 39628f939e79SAdrian Chadd ath_mode_init(sc); /* set filters, etc. */ 39638f939e79SAdrian Chadd ath_hal_startpcurecv(ah); /* re-enable PCU/DMA engine */ 39648f939e79SAdrian Chadd 39658f939e79SAdrian Chadd ATH_LOCK(sc); 39661fdadc0fSAdrian Chadd ath_hal_intrset(ah, sc->sc_imask); 39678f939e79SAdrian Chadd sc->sc_kickpcu = 0; 39688f939e79SAdrian Chadd ATH_UNLOCK(sc); 39691fdadc0fSAdrian Chadd } 39701fdadc0fSAdrian Chadd 397196ff485dSAdrian Chadd if (resched && (ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) { 3972339ccfb3SSam Leffler #ifdef IEEE80211_SUPPORT_SUPERG 397304f19fd6SSam Leffler ieee80211_ff_age_all(ic, 100); 3974339ccfb3SSam Leffler #endif 3975339ccfb3SSam Leffler if (!IFQ_IS_EMPTY(&ifp->if_snd)) 3976cd196bb2SSam Leffler ath_start(ifp); 3977339ccfb3SSam Leffler } 39788cec0ab9SSam Leffler #undef PA2DESC 39795591b213SSam Leffler } 39805591b213SSam Leffler 3981622b3fd2SSam Leffler static void 3982622b3fd2SSam Leffler ath_txq_init(struct ath_softc *sc, struct ath_txq *txq, int qnum) 3983622b3fd2SSam Leffler { 3984622b3fd2SSam Leffler txq->axq_qnum = qnum; 3985339ccfb3SSam Leffler txq->axq_ac = 0; 3986622b3fd2SSam Leffler txq->axq_depth = 0; 3987*16d4de92SAdrian Chadd txq->axq_aggr_depth = 0; 3988622b3fd2SSam Leffler txq->axq_intrcnt = 0; 3989622b3fd2SSam Leffler txq->axq_link = NULL; 39906b349e5aSAdrian Chadd txq->axq_softc = sc; 39916b349e5aSAdrian Chadd TAILQ_INIT(&txq->axq_q); 39926b349e5aSAdrian Chadd TAILQ_INIT(&txq->axq_tidq); 3993622b3fd2SSam Leffler ATH_TXQ_LOCK_INIT(sc, txq); 3994622b3fd2SSam Leffler } 3995622b3fd2SSam Leffler 39965591b213SSam Leffler /* 3997c42a7b7eSSam Leffler * Setup a h/w transmit queue. 39985591b213SSam Leffler */ 3999c42a7b7eSSam Leffler static struct ath_txq * 4000c42a7b7eSSam Leffler ath_txq_setup(struct ath_softc *sc, int qtype, int subtype) 4001c42a7b7eSSam Leffler { 4002c42a7b7eSSam Leffler #define N(a) (sizeof(a)/sizeof(a[0])) 4003c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 4004c42a7b7eSSam Leffler HAL_TXQ_INFO qi; 4005c42a7b7eSSam Leffler int qnum; 4006c42a7b7eSSam Leffler 4007c42a7b7eSSam Leffler memset(&qi, 0, sizeof(qi)); 4008c42a7b7eSSam Leffler qi.tqi_subtype = subtype; 4009c42a7b7eSSam Leffler qi.tqi_aifs = HAL_TXQ_USEDEFAULT; 4010c42a7b7eSSam Leffler qi.tqi_cwmin = HAL_TXQ_USEDEFAULT; 4011c42a7b7eSSam Leffler qi.tqi_cwmax = HAL_TXQ_USEDEFAULT; 4012c42a7b7eSSam Leffler /* 4013c42a7b7eSSam Leffler * Enable interrupts only for EOL and DESC conditions. 4014c42a7b7eSSam Leffler * We mark tx descriptors to receive a DESC interrupt 4015c42a7b7eSSam Leffler * when a tx queue gets deep; otherwise waiting for the 4016c42a7b7eSSam Leffler * EOL to reap descriptors. Note that this is done to 4017c42a7b7eSSam Leffler * reduce interrupt load and this only defers reaping 4018c42a7b7eSSam Leffler * descriptors, never transmitting frames. Aside from 4019c42a7b7eSSam Leffler * reducing interrupts this also permits more concurrency. 4020c42a7b7eSSam Leffler * The only potential downside is if the tx queue backs 4021c42a7b7eSSam Leffler * up in which case the top half of the kernel may backup 4022c42a7b7eSSam Leffler * due to a lack of tx descriptors. 4023c42a7b7eSSam Leffler */ 4024bd5a9920SSam Leffler qi.tqi_qflags = HAL_TXQ_TXEOLINT_ENABLE | HAL_TXQ_TXDESCINT_ENABLE; 4025c42a7b7eSSam Leffler qnum = ath_hal_setuptxqueue(ah, qtype, &qi); 4026c42a7b7eSSam Leffler if (qnum == -1) { 4027c42a7b7eSSam Leffler /* 4028c42a7b7eSSam Leffler * NB: don't print a message, this happens 4029a614e076SSam Leffler * normally on parts with too few tx queues 4030c42a7b7eSSam Leffler */ 4031c42a7b7eSSam Leffler return NULL; 4032c42a7b7eSSam Leffler } 4033c42a7b7eSSam Leffler if (qnum >= N(sc->sc_txq)) { 40346891c875SPeter Wemm device_printf(sc->sc_dev, 40356891c875SPeter Wemm "hal qnum %u out of range, max %zu!\n", 4036c42a7b7eSSam Leffler qnum, N(sc->sc_txq)); 4037c42a7b7eSSam Leffler ath_hal_releasetxqueue(ah, qnum); 4038c42a7b7eSSam Leffler return NULL; 4039c42a7b7eSSam Leffler } 4040c42a7b7eSSam Leffler if (!ATH_TXQ_SETUP(sc, qnum)) { 4041622b3fd2SSam Leffler ath_txq_init(sc, &sc->sc_txq[qnum], qnum); 4042c42a7b7eSSam Leffler sc->sc_txqsetup |= 1<<qnum; 4043c42a7b7eSSam Leffler } 4044c42a7b7eSSam Leffler return &sc->sc_txq[qnum]; 4045c42a7b7eSSam Leffler #undef N 4046c42a7b7eSSam Leffler } 4047c42a7b7eSSam Leffler 4048c42a7b7eSSam Leffler /* 4049c42a7b7eSSam Leffler * Setup a hardware data transmit queue for the specified 4050c42a7b7eSSam Leffler * access control. The hal may not support all requested 4051c42a7b7eSSam Leffler * queues in which case it will return a reference to a 4052c42a7b7eSSam Leffler * previously setup queue. We record the mapping from ac's 4053c42a7b7eSSam Leffler * to h/w queues for use by ath_tx_start and also track 4054c42a7b7eSSam Leffler * the set of h/w queues being used to optimize work in the 4055c42a7b7eSSam Leffler * transmit interrupt handler and related routines. 4056c42a7b7eSSam Leffler */ 4057c42a7b7eSSam Leffler static int 4058c42a7b7eSSam Leffler ath_tx_setup(struct ath_softc *sc, int ac, int haltype) 4059c42a7b7eSSam Leffler { 4060c42a7b7eSSam Leffler #define N(a) (sizeof(a)/sizeof(a[0])) 4061c42a7b7eSSam Leffler struct ath_txq *txq; 4062c42a7b7eSSam Leffler 4063c42a7b7eSSam Leffler if (ac >= N(sc->sc_ac2q)) { 40646891c875SPeter Wemm device_printf(sc->sc_dev, "AC %u out of range, max %zu!\n", 4065c42a7b7eSSam Leffler ac, N(sc->sc_ac2q)); 4066c42a7b7eSSam Leffler return 0; 4067c42a7b7eSSam Leffler } 4068c42a7b7eSSam Leffler txq = ath_txq_setup(sc, HAL_TX_QUEUE_DATA, haltype); 4069c42a7b7eSSam Leffler if (txq != NULL) { 4070339ccfb3SSam Leffler txq->axq_ac = ac; 4071c42a7b7eSSam Leffler sc->sc_ac2q[ac] = txq; 4072c42a7b7eSSam Leffler return 1; 4073c42a7b7eSSam Leffler } else 4074c42a7b7eSSam Leffler return 0; 4075c42a7b7eSSam Leffler #undef N 4076c42a7b7eSSam Leffler } 4077c42a7b7eSSam Leffler 4078c42a7b7eSSam Leffler /* 4079c42a7b7eSSam Leffler * Update WME parameters for a transmit queue. 4080c42a7b7eSSam Leffler */ 4081c42a7b7eSSam Leffler static int 4082c42a7b7eSSam Leffler ath_txq_update(struct ath_softc *sc, int ac) 4083c42a7b7eSSam Leffler { 4084c42a7b7eSSam Leffler #define ATH_EXPONENT_TO_VALUE(v) ((1<<v)-1) 4085c42a7b7eSSam Leffler #define ATH_TXOP_TO_US(v) (v<<5) 4086b032f27cSSam Leffler struct ifnet *ifp = sc->sc_ifp; 4087b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 4088c42a7b7eSSam Leffler struct ath_txq *txq = sc->sc_ac2q[ac]; 4089c42a7b7eSSam Leffler struct wmeParams *wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[ac]; 4090c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 4091c42a7b7eSSam Leffler HAL_TXQ_INFO qi; 4092c42a7b7eSSam Leffler 4093c42a7b7eSSam Leffler ath_hal_gettxqueueprops(ah, txq->axq_qnum, &qi); 4094584f7327SSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 409510ad9a77SSam Leffler if (sc->sc_tdma) { 409610ad9a77SSam Leffler /* 409710ad9a77SSam Leffler * AIFS is zero so there's no pre-transmit wait. The 409810ad9a77SSam Leffler * burst time defines the slot duration and is configured 409909be6601SSam Leffler * through net80211. The QCU is setup to not do post-xmit 410010ad9a77SSam Leffler * back off, lockout all lower-priority QCU's, and fire 410110ad9a77SSam Leffler * off the DMA beacon alert timer which is setup based 410210ad9a77SSam Leffler * on the slot configuration. 410310ad9a77SSam Leffler */ 410410ad9a77SSam Leffler qi.tqi_qflags = HAL_TXQ_TXOKINT_ENABLE 410510ad9a77SSam Leffler | HAL_TXQ_TXERRINT_ENABLE 410610ad9a77SSam Leffler | HAL_TXQ_TXURNINT_ENABLE 410710ad9a77SSam Leffler | HAL_TXQ_TXEOLINT_ENABLE 410810ad9a77SSam Leffler | HAL_TXQ_DBA_GATED 410910ad9a77SSam Leffler | HAL_TXQ_BACKOFF_DISABLE 411010ad9a77SSam Leffler | HAL_TXQ_ARB_LOCKOUT_GLOBAL 411110ad9a77SSam Leffler ; 411210ad9a77SSam Leffler qi.tqi_aifs = 0; 411310ad9a77SSam Leffler /* XXX +dbaprep? */ 411410ad9a77SSam Leffler qi.tqi_readyTime = sc->sc_tdmaslotlen; 411510ad9a77SSam Leffler qi.tqi_burstTime = qi.tqi_readyTime; 411610ad9a77SSam Leffler } else { 411710ad9a77SSam Leffler #endif 4118*16d4de92SAdrian Chadd /* 4119*16d4de92SAdrian Chadd * XXX shouldn't this just use the default flags 4120*16d4de92SAdrian Chadd * used in the previous queue setup? 4121*16d4de92SAdrian Chadd */ 412210ad9a77SSam Leffler qi.tqi_qflags = HAL_TXQ_TXOKINT_ENABLE 412310ad9a77SSam Leffler | HAL_TXQ_TXERRINT_ENABLE 412410ad9a77SSam Leffler | HAL_TXQ_TXDESCINT_ENABLE 412510ad9a77SSam Leffler | HAL_TXQ_TXURNINT_ENABLE 412610ad9a77SSam Leffler ; 4127c42a7b7eSSam Leffler qi.tqi_aifs = wmep->wmep_aifsn; 4128c42a7b7eSSam Leffler qi.tqi_cwmin = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmin); 4129c42a7b7eSSam Leffler qi.tqi_cwmax = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmax); 413010ad9a77SSam Leffler qi.tqi_readyTime = 0; 4131c42a7b7eSSam Leffler qi.tqi_burstTime = ATH_TXOP_TO_US(wmep->wmep_txopLimit); 4132584f7327SSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 413310ad9a77SSam Leffler } 413410ad9a77SSam Leffler #endif 413510ad9a77SSam Leffler 413610ad9a77SSam Leffler DPRINTF(sc, ATH_DEBUG_RESET, 413710ad9a77SSam Leffler "%s: Q%u qflags 0x%x aifs %u cwmin %u cwmax %u burstTime %u\n", 413810ad9a77SSam Leffler __func__, txq->axq_qnum, qi.tqi_qflags, 413910ad9a77SSam Leffler qi.tqi_aifs, qi.tqi_cwmin, qi.tqi_cwmax, qi.tqi_burstTime); 4140c42a7b7eSSam Leffler 4141c42a7b7eSSam Leffler if (!ath_hal_settxqueueprops(ah, txq->axq_qnum, &qi)) { 4142b032f27cSSam Leffler if_printf(ifp, "unable to update hardware queue " 4143c42a7b7eSSam Leffler "parameters for %s traffic!\n", 4144c42a7b7eSSam Leffler ieee80211_wme_acnames[ac]); 4145c42a7b7eSSam Leffler return 0; 4146c42a7b7eSSam Leffler } else { 4147c42a7b7eSSam Leffler ath_hal_resettxqueue(ah, txq->axq_qnum); /* push to h/w */ 4148c42a7b7eSSam Leffler return 1; 4149c42a7b7eSSam Leffler } 4150c42a7b7eSSam Leffler #undef ATH_TXOP_TO_US 4151c42a7b7eSSam Leffler #undef ATH_EXPONENT_TO_VALUE 4152c42a7b7eSSam Leffler } 4153c42a7b7eSSam Leffler 4154c42a7b7eSSam Leffler /* 4155c42a7b7eSSam Leffler * Callback from the 802.11 layer to update WME parameters. 4156c42a7b7eSSam Leffler */ 4157c42a7b7eSSam Leffler static int 4158c42a7b7eSSam Leffler ath_wme_update(struct ieee80211com *ic) 4159c42a7b7eSSam Leffler { 4160c42a7b7eSSam Leffler struct ath_softc *sc = ic->ic_ifp->if_softc; 4161c42a7b7eSSam Leffler 4162c42a7b7eSSam Leffler return !ath_txq_update(sc, WME_AC_BE) || 4163c42a7b7eSSam Leffler !ath_txq_update(sc, WME_AC_BK) || 4164c42a7b7eSSam Leffler !ath_txq_update(sc, WME_AC_VI) || 4165c42a7b7eSSam Leffler !ath_txq_update(sc, WME_AC_VO) ? EIO : 0; 4166c42a7b7eSSam Leffler } 4167c42a7b7eSSam Leffler 4168c42a7b7eSSam Leffler /* 4169c42a7b7eSSam Leffler * Reclaim resources for a setup queue. 4170c42a7b7eSSam Leffler */ 4171c42a7b7eSSam Leffler static void 4172c42a7b7eSSam Leffler ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq) 4173c42a7b7eSSam Leffler { 4174c42a7b7eSSam Leffler 4175c42a7b7eSSam Leffler ath_hal_releasetxqueue(sc->sc_ah, txq->axq_qnum); 4176c42a7b7eSSam Leffler ATH_TXQ_LOCK_DESTROY(txq); 4177c42a7b7eSSam Leffler sc->sc_txqsetup &= ~(1<<txq->axq_qnum); 4178c42a7b7eSSam Leffler } 4179c42a7b7eSSam Leffler 4180c42a7b7eSSam Leffler /* 4181c42a7b7eSSam Leffler * Reclaim all tx queue resources. 4182c42a7b7eSSam Leffler */ 4183c42a7b7eSSam Leffler static void 4184c42a7b7eSSam Leffler ath_tx_cleanup(struct ath_softc *sc) 4185c42a7b7eSSam Leffler { 4186c42a7b7eSSam Leffler int i; 4187c42a7b7eSSam Leffler 4188c42a7b7eSSam Leffler ATH_TXBUF_LOCK_DESTROY(sc); 4189c42a7b7eSSam Leffler for (i = 0; i < HAL_NUM_TX_QUEUES; i++) 4190c42a7b7eSSam Leffler if (ATH_TXQ_SETUP(sc, i)) 4191c42a7b7eSSam Leffler ath_tx_cleanupq(sc, &sc->sc_txq[i]); 4192c42a7b7eSSam Leffler } 41935591b213SSam Leffler 419499d258fdSSam Leffler /* 4195ab06fdf2SSam Leffler * Return h/w rate index for an IEEE rate (w/o basic rate bit) 4196ab06fdf2SSam Leffler * using the current rates in sc_rixmap. 41978b5341deSSam Leffler */ 4198b8e788a5SAdrian Chadd int 4199ab06fdf2SSam Leffler ath_tx_findrix(const struct ath_softc *sc, uint8_t rate) 42008b5341deSSam Leffler { 4201ab06fdf2SSam Leffler int rix = sc->sc_rixmap[rate]; 4202ab06fdf2SSam Leffler /* NB: return lowest rix for invalid rate */ 4203ab06fdf2SSam Leffler return (rix == 0xff ? 0 : rix); 42048b5341deSSam Leffler } 42058b5341deSSam Leffler 420668e8e04eSSam Leffler /* 4207c42a7b7eSSam Leffler * Process completed xmit descriptors from the specified queue. 4208c42a7b7eSSam Leffler */ 4209d7736e13SSam Leffler static int 421096ff485dSAdrian Chadd ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq, int dosched) 42115591b213SSam Leffler { 42125591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 4213b032f27cSSam Leffler struct ifnet *ifp = sc->sc_ifp; 4214b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 421510ad9a77SSam Leffler struct ath_buf *bf, *last; 4216c4c3cb46SSam Leffler struct ath_desc *ds, *ds0; 421765f9edeeSSam Leffler struct ath_tx_status *ts; 42185591b213SSam Leffler struct ieee80211_node *ni; 42195591b213SSam Leffler struct ath_node *an; 4220d7736e13SSam Leffler int sr, lr, pri, nacked; 42215591b213SSam Leffler HAL_STATUS status; 42225591b213SSam Leffler 4223c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: tx queue %u head %p link %p\n", 4224c42a7b7eSSam Leffler __func__, txq->axq_qnum, 4225c42a7b7eSSam Leffler (caddr_t)(uintptr_t) ath_hal_gettxbuf(sc->sc_ah, txq->axq_qnum), 4226c42a7b7eSSam Leffler txq->axq_link); 4227d7736e13SSam Leffler nacked = 0; 42285591b213SSam Leffler for (;;) { 4229c42a7b7eSSam Leffler ATH_TXQ_LOCK(txq); 4230c42a7b7eSSam Leffler txq->axq_intrcnt = 0; /* reset periodic desc intr count */ 42316b349e5aSAdrian Chadd bf = TAILQ_FIRST(&txq->axq_q); 42325591b213SSam Leffler if (bf == NULL) { 4233c42a7b7eSSam Leffler ATH_TXQ_UNLOCK(txq); 42345591b213SSam Leffler break; 42355591b213SSam Leffler } 4236c4c3cb46SSam Leffler ds0 = &bf->bf_desc[0]; 42375591b213SSam Leffler ds = &bf->bf_desc[bf->bf_nseg - 1]; 423865f9edeeSSam Leffler ts = &bf->bf_status.ds_txstat; 423965f9edeeSSam Leffler status = ath_hal_txprocdesc(ah, ds, ts); 4240a585a9a1SSam Leffler #ifdef ATH_DEBUG 4241c42a7b7eSSam Leffler if (sc->sc_debug & ATH_DEBUG_XMIT_DESC) 42426902009eSSam Leffler ath_printtxbuf(sc, bf, txq->axq_qnum, 0, 42436902009eSSam Leffler status == HAL_OK); 42445591b213SSam Leffler #endif 42455591b213SSam Leffler if (status == HAL_EINPROGRESS) { 4246c42a7b7eSSam Leffler ATH_TXQ_UNLOCK(txq); 42475591b213SSam Leffler break; 42485591b213SSam Leffler } 42496b349e5aSAdrian Chadd ATH_TXQ_REMOVE(txq, bf, bf_list); 4250584f7327SSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 425110ad9a77SSam Leffler if (txq->axq_depth > 0) { 425210ad9a77SSam Leffler /* 425310ad9a77SSam Leffler * More frames follow. Mark the buffer busy 425410ad9a77SSam Leffler * so it's not re-used while the hardware may 425510ad9a77SSam Leffler * still re-read the link field in the descriptor. 425610ad9a77SSam Leffler */ 425710ad9a77SSam Leffler bf->bf_flags |= ATH_BUF_BUSY; 425810ad9a77SSam Leffler } else 425910ad9a77SSam Leffler #else 4260ebecf802SSam Leffler if (txq->axq_depth == 0) 426110ad9a77SSam Leffler #endif 42621539af1eSSam Leffler txq->axq_link = NULL; 4263c42a7b7eSSam Leffler ATH_TXQ_UNLOCK(txq); 42645591b213SSam Leffler 42655591b213SSam Leffler ni = bf->bf_node; 42665591b213SSam Leffler if (ni != NULL) { 4267c42a7b7eSSam Leffler an = ATH_NODE(ni); 426865f9edeeSSam Leffler if (ts->ts_status == 0) { 426965f9edeeSSam Leffler u_int8_t txant = ts->ts_antenna; 4270c42a7b7eSSam Leffler sc->sc_stats.ast_ant_tx[txant]++; 4271c42a7b7eSSam Leffler sc->sc_ant_tx[txant]++; 4272f6cbf16aSSam Leffler if (ts->ts_finaltsi != 0) 4273c42a7b7eSSam Leffler sc->sc_stats.ast_tx_altrate++; 4274c42a7b7eSSam Leffler pri = M_WME_GETAC(bf->bf_m); 4275c42a7b7eSSam Leffler if (pri >= WME_AC_VO) 4276c42a7b7eSSam Leffler ic->ic_wme.wme_hipri_traffic++; 4277ad80c0aaSSam Leffler if ((bf->bf_txflags & HAL_TXDESC_NOACK) == 0) 4278c42a7b7eSSam Leffler ni->ni_inact = ni->ni_inact_reload; 42795591b213SSam Leffler } else { 428065f9edeeSSam Leffler if (ts->ts_status & HAL_TXERR_XRETRY) 42815591b213SSam Leffler sc->sc_stats.ast_tx_xretries++; 428265f9edeeSSam Leffler if (ts->ts_status & HAL_TXERR_FIFO) 42835591b213SSam Leffler sc->sc_stats.ast_tx_fifoerr++; 428465f9edeeSSam Leffler if (ts->ts_status & HAL_TXERR_FILT) 42855591b213SSam Leffler sc->sc_stats.ast_tx_filtered++; 4286e9d1191fSAdrian Chadd if (ts->ts_status & HAL_TXERR_XTXOP) 4287e9d1191fSAdrian Chadd sc->sc_stats.ast_tx_xtxop++; 4288e9d1191fSAdrian Chadd if (ts->ts_status & HAL_TXERR_TIMER_EXPIRED) 4289e9d1191fSAdrian Chadd sc->sc_stats.ast_tx_timerexpired++; 4290e9d1191fSAdrian Chadd 4291e9d1191fSAdrian Chadd /* XXX HAL_TX_DATA_UNDERRUN */ 4292e9d1191fSAdrian Chadd /* XXX HAL_TX_DELIM_UNDERRUN */ 4293e9d1191fSAdrian Chadd 429468e8e04eSSam Leffler if (bf->bf_m->m_flags & M_FF) 429568e8e04eSSam Leffler sc->sc_stats.ast_ff_txerr++; 42965591b213SSam Leffler } 4297e9d1191fSAdrian Chadd /* XXX when is this valid? */ 4298e9d1191fSAdrian Chadd if (ts->ts_status & HAL_TX_DESC_CFG_ERR) 4299e9d1191fSAdrian Chadd sc->sc_stats.ast_tx_desccfgerr++; 4300e9d1191fSAdrian Chadd 430165f9edeeSSam Leffler sr = ts->ts_shortretry; 430265f9edeeSSam Leffler lr = ts->ts_longretry; 43035591b213SSam Leffler sc->sc_stats.ast_tx_shortretry += sr; 43045591b213SSam Leffler sc->sc_stats.ast_tx_longretry += lr; 4305c42a7b7eSSam Leffler /* 4306c42a7b7eSSam Leffler * Hand the descriptor to the rate control algorithm. 4307c42a7b7eSSam Leffler */ 430865f9edeeSSam Leffler if ((ts->ts_status & HAL_TXERR_FILT) == 0 && 430980c07f23SSam Leffler (bf->bf_txflags & HAL_TXDESC_NOACK) == 0) { 4310d7736e13SSam Leffler /* 431184784be1SSam Leffler * If frame was ack'd update statistics, 431284784be1SSam Leffler * including the last rx time used to 431384784be1SSam Leffler * workaround phantom bmiss interrupts. 4314d7736e13SSam Leffler */ 431584784be1SSam Leffler if (ts->ts_status == 0) { 4316d7736e13SSam Leffler nacked++; 431784784be1SSam Leffler sc->sc_stats.ast_tx_rssi = ts->ts_rssi; 431884784be1SSam Leffler ATH_RSSI_LPF(sc->sc_halstats.ns_avgtxrssi, 431984784be1SSam Leffler ts->ts_rssi); 432084784be1SSam Leffler } 432165f9edeeSSam Leffler ath_rate_tx_complete(sc, an, bf); 4322d7736e13SSam Leffler } 43230a915fadSSam Leffler /* 432468e8e04eSSam Leffler * Do any tx complete callback. Note this must 432568e8e04eSSam Leffler * be done before releasing the node reference. 432668e8e04eSSam Leffler */ 432768e8e04eSSam Leffler if (bf->bf_m->m_flags & M_TXCB) 432868e8e04eSSam Leffler ieee80211_process_callback(ni, bf->bf_m, 432974eca0c2SSam Leffler (bf->bf_txflags & HAL_TXDESC_NOACK) == 0 ? 433074eca0c2SSam Leffler ts->ts_status : HAL_TXERR_XRETRY); 4331c42a7b7eSSam Leffler ieee80211_free_node(ni); 43325591b213SSam Leffler } 43335591b213SSam Leffler bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, 43345591b213SSam Leffler BUS_DMASYNC_POSTWRITE); 43355591b213SSam Leffler bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 433668e8e04eSSam Leffler 43375591b213SSam Leffler m_freem(bf->bf_m); 43385591b213SSam Leffler bf->bf_m = NULL; 43395591b213SSam Leffler bf->bf_node = NULL; 43405591b213SSam Leffler 4341f0b2a0beSSam Leffler ATH_TXBUF_LOCK(sc); 43426b349e5aSAdrian Chadd last = TAILQ_LAST(&sc->sc_txbuf, ath_bufhead_s); 434310ad9a77SSam Leffler if (last != NULL) 434410ad9a77SSam Leffler last->bf_flags &= ~ATH_BUF_BUSY; 43456b349e5aSAdrian Chadd TAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); 4346f0b2a0beSSam Leffler ATH_TXBUF_UNLOCK(sc); 43475591b213SSam Leffler } 4348339ccfb3SSam Leffler #ifdef IEEE80211_SUPPORT_SUPERG 434968e8e04eSSam Leffler /* 435068e8e04eSSam Leffler * Flush fast-frame staging queue when traffic slows. 435168e8e04eSSam Leffler */ 435268e8e04eSSam Leffler if (txq->axq_depth <= 1) 435304f19fd6SSam Leffler ieee80211_ff_flush(ic, txq->axq_ac); 4354339ccfb3SSam Leffler #endif 4355d7736e13SSam Leffler return nacked; 4356d7736e13SSam Leffler } 4357d7736e13SSam Leffler 43588f939e79SAdrian Chadd #define TXQACTIVE(t, q) ( (t) & (1 << (q))) 4359c42a7b7eSSam Leffler 4360c42a7b7eSSam Leffler /* 4361c42a7b7eSSam Leffler * Deferred processing of transmit interrupt; special-cased 4362c42a7b7eSSam Leffler * for a single hardware transmit queue (e.g. 5210 and 5211). 4363c42a7b7eSSam Leffler */ 4364c42a7b7eSSam Leffler static void 4365c42a7b7eSSam Leffler ath_tx_proc_q0(void *arg, int npending) 4366c42a7b7eSSam Leffler { 4367c42a7b7eSSam Leffler struct ath_softc *sc = arg; 4368fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 43698f939e79SAdrian Chadd uint32_t txqs; 4370c42a7b7eSSam Leffler 43718f939e79SAdrian Chadd ATH_LOCK(sc); 43728f939e79SAdrian Chadd txqs = sc->sc_txq_active; 43738f939e79SAdrian Chadd sc->sc_txq_active &= ~txqs; 43748f939e79SAdrian Chadd ATH_UNLOCK(sc); 43758f939e79SAdrian Chadd 437696ff485dSAdrian Chadd if (TXQACTIVE(txqs, 0) && ath_tx_processq(sc, &sc->sc_txq[0], 1)) 43778f939e79SAdrian Chadd /* XXX why is lastrx updated in tx code? */ 4378d7736e13SSam Leffler sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah); 43798f939e79SAdrian Chadd if (TXQACTIVE(txqs, sc->sc_cabq->axq_qnum)) 438096ff485dSAdrian Chadd ath_tx_processq(sc, sc->sc_cabq, 1); 438113f4c340SRobert Watson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 43822e986da5SSam Leffler sc->sc_wd_timer = 0; 43835591b213SSam Leffler 43843e50ec2cSSam Leffler if (sc->sc_softled) 438546d4d74cSSam Leffler ath_led_event(sc, sc->sc_txrix); 43863e50ec2cSSam Leffler 43875591b213SSam Leffler ath_start(ifp); 43885591b213SSam Leffler } 43895591b213SSam Leffler 43905591b213SSam Leffler /* 4391c42a7b7eSSam Leffler * Deferred processing of transmit interrupt; special-cased 4392c42a7b7eSSam Leffler * for four hardware queues, 0-3 (e.g. 5212 w/ WME support). 43935591b213SSam Leffler */ 43945591b213SSam Leffler static void 4395c42a7b7eSSam Leffler ath_tx_proc_q0123(void *arg, int npending) 4396c42a7b7eSSam Leffler { 4397c42a7b7eSSam Leffler struct ath_softc *sc = arg; 4398fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 4399d7736e13SSam Leffler int nacked; 44008f939e79SAdrian Chadd uint32_t txqs; 44018f939e79SAdrian Chadd 44028f939e79SAdrian Chadd ATH_LOCK(sc); 44038f939e79SAdrian Chadd txqs = sc->sc_txq_active; 44048f939e79SAdrian Chadd sc->sc_txq_active &= ~txqs; 44058f939e79SAdrian Chadd ATH_UNLOCK(sc); 4406c42a7b7eSSam Leffler 4407c42a7b7eSSam Leffler /* 4408c42a7b7eSSam Leffler * Process each active queue. 4409c42a7b7eSSam Leffler */ 4410d7736e13SSam Leffler nacked = 0; 44118f939e79SAdrian Chadd if (TXQACTIVE(txqs, 0)) 441296ff485dSAdrian Chadd nacked += ath_tx_processq(sc, &sc->sc_txq[0], 1); 44138f939e79SAdrian Chadd if (TXQACTIVE(txqs, 1)) 441496ff485dSAdrian Chadd nacked += ath_tx_processq(sc, &sc->sc_txq[1], 1); 44158f939e79SAdrian Chadd if (TXQACTIVE(txqs, 2)) 441696ff485dSAdrian Chadd nacked += ath_tx_processq(sc, &sc->sc_txq[2], 1); 44178f939e79SAdrian Chadd if (TXQACTIVE(txqs, 3)) 441896ff485dSAdrian Chadd nacked += ath_tx_processq(sc, &sc->sc_txq[3], 1); 44198f939e79SAdrian Chadd if (TXQACTIVE(txqs, sc->sc_cabq->axq_qnum)) 442096ff485dSAdrian Chadd ath_tx_processq(sc, sc->sc_cabq, 1); 4421d7736e13SSam Leffler if (nacked) 4422d7736e13SSam Leffler sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah); 4423c42a7b7eSSam Leffler 442413f4c340SRobert Watson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 44252e986da5SSam Leffler sc->sc_wd_timer = 0; 4426c42a7b7eSSam Leffler 44273e50ec2cSSam Leffler if (sc->sc_softled) 442846d4d74cSSam Leffler ath_led_event(sc, sc->sc_txrix); 44293e50ec2cSSam Leffler 4430c42a7b7eSSam Leffler ath_start(ifp); 4431c42a7b7eSSam Leffler } 4432c42a7b7eSSam Leffler 4433c42a7b7eSSam Leffler /* 4434c42a7b7eSSam Leffler * Deferred processing of transmit interrupt. 4435c42a7b7eSSam Leffler */ 4436c42a7b7eSSam Leffler static void 4437c42a7b7eSSam Leffler ath_tx_proc(void *arg, int npending) 4438c42a7b7eSSam Leffler { 4439c42a7b7eSSam Leffler struct ath_softc *sc = arg; 4440fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 4441d7736e13SSam Leffler int i, nacked; 44428f939e79SAdrian Chadd uint32_t txqs; 44438f939e79SAdrian Chadd 44448f939e79SAdrian Chadd ATH_LOCK(sc); 44458f939e79SAdrian Chadd txqs = sc->sc_txq_active; 44468f939e79SAdrian Chadd sc->sc_txq_active &= ~txqs; 44478f939e79SAdrian Chadd ATH_UNLOCK(sc); 4448c42a7b7eSSam Leffler 4449c42a7b7eSSam Leffler /* 4450c42a7b7eSSam Leffler * Process each active queue. 4451c42a7b7eSSam Leffler */ 4452d7736e13SSam Leffler nacked = 0; 4453c42a7b7eSSam Leffler for (i = 0; i < HAL_NUM_TX_QUEUES; i++) 44548f939e79SAdrian Chadd if (ATH_TXQ_SETUP(sc, i) && TXQACTIVE(txqs, i)) 445596ff485dSAdrian Chadd nacked += ath_tx_processq(sc, &sc->sc_txq[i], 1); 4456d7736e13SSam Leffler if (nacked) 4457d7736e13SSam Leffler sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah); 4458c42a7b7eSSam Leffler 445913f4c340SRobert Watson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 44602e986da5SSam Leffler sc->sc_wd_timer = 0; 4461c42a7b7eSSam Leffler 44623e50ec2cSSam Leffler if (sc->sc_softled) 446346d4d74cSSam Leffler ath_led_event(sc, sc->sc_txrix); 44643e50ec2cSSam Leffler 4465c42a7b7eSSam Leffler ath_start(ifp); 4466c42a7b7eSSam Leffler } 4467*16d4de92SAdrian Chadd #undef TXQACTIVE 4468c42a7b7eSSam Leffler 4469c42a7b7eSSam Leffler static void 4470c42a7b7eSSam Leffler ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq) 44715591b213SSam Leffler { 4472a585a9a1SSam Leffler #ifdef ATH_DEBUG 44735591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 4474d2f6ed15SSam Leffler #endif 447523428eafSSam Leffler struct ieee80211_node *ni; 44765591b213SSam Leffler struct ath_buf *bf; 44777a4c5ed9SSam Leffler u_int ix; 44785591b213SSam Leffler 4479c42a7b7eSSam Leffler /* 4480c42a7b7eSSam Leffler * NB: this assumes output has been stopped and 44815d61b5e8SSam Leffler * we do not need to block ath_tx_proc 4482c42a7b7eSSam Leffler */ 448310ad9a77SSam Leffler ATH_TXBUF_LOCK(sc); 44846b349e5aSAdrian Chadd bf = TAILQ_LAST(&sc->sc_txbuf, ath_bufhead_s); 448510ad9a77SSam Leffler if (bf != NULL) 448610ad9a77SSam Leffler bf->bf_flags &= ~ATH_BUF_BUSY; 448710ad9a77SSam Leffler ATH_TXBUF_UNLOCK(sc); 44887a4c5ed9SSam Leffler for (ix = 0;; ix++) { 4489c42a7b7eSSam Leffler ATH_TXQ_LOCK(txq); 44906b349e5aSAdrian Chadd bf = TAILQ_FIRST(&txq->axq_q); 44915591b213SSam Leffler if (bf == NULL) { 4492ebecf802SSam Leffler txq->axq_link = NULL; 4493c42a7b7eSSam Leffler ATH_TXQ_UNLOCK(txq); 44945591b213SSam Leffler break; 44955591b213SSam Leffler } 44966b349e5aSAdrian Chadd ATH_TXQ_REMOVE(txq, bf, bf_list); 4497c42a7b7eSSam Leffler ATH_TXQ_UNLOCK(txq); 4498a585a9a1SSam Leffler #ifdef ATH_DEBUG 44994a3ac3fcSSam Leffler if (sc->sc_debug & ATH_DEBUG_RESET) { 4500b032f27cSSam Leffler struct ieee80211com *ic = sc->sc_ifp->if_l2com; 4501b032f27cSSam Leffler 45026902009eSSam Leffler ath_printtxbuf(sc, bf, txq->axq_qnum, ix, 450365f9edeeSSam Leffler ath_hal_txprocdesc(ah, bf->bf_desc, 450465f9edeeSSam Leffler &bf->bf_status.ds_txstat) == HAL_OK); 4505e40b6ab1SSam Leffler ieee80211_dump_pkt(ic, mtod(bf->bf_m, const uint8_t *), 45064a3ac3fcSSam Leffler bf->bf_m->m_len, 0, -1); 45074a3ac3fcSSam Leffler } 4508a585a9a1SSam Leffler #endif /* ATH_DEBUG */ 45095591b213SSam Leffler bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 451023428eafSSam Leffler ni = bf->bf_node; 45115591b213SSam Leffler bf->bf_node = NULL; 4512c42a7b7eSSam Leffler if (ni != NULL) { 451323428eafSSam Leffler /* 4514d50ea6acSSam Leffler * Do any callback and reclaim the node reference. 451523428eafSSam Leffler */ 4516d50ea6acSSam Leffler if (bf->bf_m->m_flags & M_TXCB) 4517d50ea6acSSam Leffler ieee80211_process_callback(ni, bf->bf_m, -1); 4518c42a7b7eSSam Leffler ieee80211_free_node(ni); 451923428eafSSam Leffler } 452068e8e04eSSam Leffler m_freem(bf->bf_m); 452168e8e04eSSam Leffler bf->bf_m = NULL; 452210ad9a77SSam Leffler bf->bf_flags &= ~ATH_BUF_BUSY; 452368e8e04eSSam Leffler 4524f0b2a0beSSam Leffler ATH_TXBUF_LOCK(sc); 45256b349e5aSAdrian Chadd TAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); 4526f0b2a0beSSam Leffler ATH_TXBUF_UNLOCK(sc); 45275591b213SSam Leffler } 4528c42a7b7eSSam Leffler } 4529c42a7b7eSSam Leffler 4530c42a7b7eSSam Leffler static void 4531c42a7b7eSSam Leffler ath_tx_stopdma(struct ath_softc *sc, struct ath_txq *txq) 4532c42a7b7eSSam Leffler { 4533c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 4534c42a7b7eSSam Leffler 4535c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_RESET, "%s: tx queue [%u] %p, link %p\n", 4536c42a7b7eSSam Leffler __func__, txq->axq_qnum, 45376891c875SPeter Wemm (caddr_t)(uintptr_t) ath_hal_gettxbuf(ah, txq->axq_qnum), 45386891c875SPeter Wemm txq->axq_link); 45394a3ac3fcSSam Leffler (void) ath_hal_stoptxdma(ah, txq->axq_qnum); 4540c42a7b7eSSam Leffler } 4541c42a7b7eSSam Leffler 4542c42a7b7eSSam Leffler /* 4543c42a7b7eSSam Leffler * Drain the transmit queues and reclaim resources. 4544c42a7b7eSSam Leffler */ 4545c42a7b7eSSam Leffler static void 4546517526efSAdrian Chadd ath_draintxq(struct ath_softc *sc, ATH_RESET_TYPE reset_type) 4547c42a7b7eSSam Leffler { 4548c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 4549fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 4550c42a7b7eSSam Leffler int i; 4551c42a7b7eSSam Leffler 4552c42a7b7eSSam Leffler /* XXX return value */ 4553c42a7b7eSSam Leffler if (!sc->sc_invalid) { 4554c42a7b7eSSam Leffler /* don't touch the hardware if marked invalid */ 45554a3ac3fcSSam Leffler DPRINTF(sc, ATH_DEBUG_RESET, "%s: tx queue [%u] %p, link %p\n", 45564a3ac3fcSSam Leffler __func__, sc->sc_bhalq, 45574a3ac3fcSSam Leffler (caddr_t)(uintptr_t) ath_hal_gettxbuf(ah, sc->sc_bhalq), 45584a3ac3fcSSam Leffler NULL); 4559c42a7b7eSSam Leffler (void) ath_hal_stoptxdma(ah, sc->sc_bhalq); 4560c42a7b7eSSam Leffler for (i = 0; i < HAL_NUM_TX_QUEUES; i++) 4561c42a7b7eSSam Leffler if (ATH_TXQ_SETUP(sc, i)) 4562c42a7b7eSSam Leffler ath_tx_stopdma(sc, &sc->sc_txq[i]); 4563c42a7b7eSSam Leffler } 4564c42a7b7eSSam Leffler for (i = 0; i < HAL_NUM_TX_QUEUES; i++) 4565c42a7b7eSSam Leffler if (ATH_TXQ_SETUP(sc, i)) 4566c42a7b7eSSam Leffler ath_tx_draintxq(sc, &sc->sc_txq[i]); 45674a3ac3fcSSam Leffler #ifdef ATH_DEBUG 45684a3ac3fcSSam Leffler if (sc->sc_debug & ATH_DEBUG_RESET) { 45696b349e5aSAdrian Chadd struct ath_buf *bf = TAILQ_FIRST(&sc->sc_bbuf); 45704a3ac3fcSSam Leffler if (bf != NULL && bf->bf_m != NULL) { 45716902009eSSam Leffler ath_printtxbuf(sc, bf, sc->sc_bhalq, 0, 457265f9edeeSSam Leffler ath_hal_txprocdesc(ah, bf->bf_desc, 457365f9edeeSSam Leffler &bf->bf_status.ds_txstat) == HAL_OK); 4574e40b6ab1SSam Leffler ieee80211_dump_pkt(ifp->if_l2com, 4575e40b6ab1SSam Leffler mtod(bf->bf_m, const uint8_t *), bf->bf_m->m_len, 4576e40b6ab1SSam Leffler 0, -1); 45774a3ac3fcSSam Leffler } 45784a3ac3fcSSam Leffler } 45794a3ac3fcSSam Leffler #endif /* ATH_DEBUG */ 458013f4c340SRobert Watson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 45812e986da5SSam Leffler sc->sc_wd_timer = 0; 45825591b213SSam Leffler } 45835591b213SSam Leffler 45845591b213SSam Leffler /* 45855591b213SSam Leffler * Disable the receive h/w in preparation for a reset. 45865591b213SSam Leffler */ 45875591b213SSam Leffler static void 45885591b213SSam Leffler ath_stoprecv(struct ath_softc *sc) 45895591b213SSam Leffler { 45908cec0ab9SSam Leffler #define PA2DESC(_sc, _pa) \ 4591c42a7b7eSSam Leffler ((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \ 4592c42a7b7eSSam Leffler ((_pa) - (_sc)->sc_rxdma.dd_desc_paddr))) 45935591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 45945591b213SSam Leffler 45955591b213SSam Leffler ath_hal_stoppcurecv(ah); /* disable PCU */ 45965591b213SSam Leffler ath_hal_setrxfilter(ah, 0); /* clear recv filter */ 45975591b213SSam Leffler ath_hal_stopdmarecv(ah); /* disable DMA engine */ 4598c42a7b7eSSam Leffler DELAY(3000); /* 3ms is long enough for 1 frame */ 4599a585a9a1SSam Leffler #ifdef ATH_DEBUG 4600c42a7b7eSSam Leffler if (sc->sc_debug & (ATH_DEBUG_RESET | ATH_DEBUG_FATAL)) { 46015591b213SSam Leffler struct ath_buf *bf; 46027a4c5ed9SSam Leffler u_int ix; 46035591b213SSam Leffler 4604e325e530SSam Leffler printf("%s: rx queue %p, link %p\n", __func__, 460530310634SPeter Wemm (caddr_t)(uintptr_t) ath_hal_getrxbuf(ah), sc->sc_rxlink); 46067a4c5ed9SSam Leffler ix = 0; 46076b349e5aSAdrian Chadd TAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) { 46088cec0ab9SSam Leffler struct ath_desc *ds = bf->bf_desc; 460965f9edeeSSam Leffler struct ath_rx_status *rs = &bf->bf_status.ds_rxstat; 4610c42a7b7eSSam Leffler HAL_STATUS status = ath_hal_rxprocdesc(ah, ds, 461165f9edeeSSam Leffler bf->bf_daddr, PA2DESC(sc, ds->ds_link), rs); 4612c42a7b7eSSam Leffler if (status == HAL_OK || (sc->sc_debug & ATH_DEBUG_FATAL)) 46136902009eSSam Leffler ath_printrxbuf(sc, bf, ix, status == HAL_OK); 46147a4c5ed9SSam Leffler ix++; 46155591b213SSam Leffler } 46165591b213SSam Leffler } 46175591b213SSam Leffler #endif 461868e8e04eSSam Leffler if (sc->sc_rxpending != NULL) { 461968e8e04eSSam Leffler m_freem(sc->sc_rxpending); 462068e8e04eSSam Leffler sc->sc_rxpending = NULL; 462168e8e04eSSam Leffler } 46225591b213SSam Leffler sc->sc_rxlink = NULL; /* just in case */ 46238cec0ab9SSam Leffler #undef PA2DESC 46245591b213SSam Leffler } 46255591b213SSam Leffler 46265591b213SSam Leffler /* 46275591b213SSam Leffler * Enable the receive h/w following a reset. 46285591b213SSam Leffler */ 46295591b213SSam Leffler static int 46305591b213SSam Leffler ath_startrecv(struct ath_softc *sc) 46315591b213SSam Leffler { 46325591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 46335591b213SSam Leffler struct ath_buf *bf; 46345591b213SSam Leffler 46355591b213SSam Leffler sc->sc_rxlink = NULL; 463668e8e04eSSam Leffler sc->sc_rxpending = NULL; 46376b349e5aSAdrian Chadd TAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) { 46385591b213SSam Leffler int error = ath_rxbuf_init(sc, bf); 46395591b213SSam Leffler if (error != 0) { 4640c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_RECV, 4641c42a7b7eSSam Leffler "%s: ath_rxbuf_init failed %d\n", 4642c42a7b7eSSam Leffler __func__, error); 46435591b213SSam Leffler return error; 46445591b213SSam Leffler } 46455591b213SSam Leffler } 46465591b213SSam Leffler 46476b349e5aSAdrian Chadd bf = TAILQ_FIRST(&sc->sc_rxbuf); 46485591b213SSam Leffler ath_hal_putrxbuf(ah, bf->bf_daddr); 46495591b213SSam Leffler ath_hal_rxena(ah); /* enable recv descriptors */ 46505591b213SSam Leffler ath_mode_init(sc); /* set filters, etc. */ 46515591b213SSam Leffler ath_hal_startpcurecv(ah); /* re-enable PCU/DMA engine */ 46525591b213SSam Leffler return 0; 46535591b213SSam Leffler } 46545591b213SSam Leffler 46555591b213SSam Leffler /* 4656c42a7b7eSSam Leffler * Update internal state after a channel change. 4657c42a7b7eSSam Leffler */ 4658c42a7b7eSSam Leffler static void 4659c42a7b7eSSam Leffler ath_chan_change(struct ath_softc *sc, struct ieee80211_channel *chan) 4660c42a7b7eSSam Leffler { 4661c42a7b7eSSam Leffler enum ieee80211_phymode mode; 4662c42a7b7eSSam Leffler 4663c42a7b7eSSam Leffler /* 4664c42a7b7eSSam Leffler * Change channels and update the h/w rate map 4665c42a7b7eSSam Leffler * if we're switching; e.g. 11a to 11b/g. 4666c42a7b7eSSam Leffler */ 466768e8e04eSSam Leffler mode = ieee80211_chan2mode(chan); 4668c42a7b7eSSam Leffler if (mode != sc->sc_curmode) 4669c42a7b7eSSam Leffler ath_setcurmode(sc, mode); 467059efa8b5SSam Leffler sc->sc_curchan = chan; 4671c42a7b7eSSam Leffler } 4672c42a7b7eSSam Leffler 4673c42a7b7eSSam Leffler /* 46745591b213SSam Leffler * Set/change channels. If the channel is really being changed, 46754fa8d4efSDaniel Eischen * it's done by resetting the chip. To accomplish this we must 46765591b213SSam Leffler * first cleanup any pending DMA, then restart stuff after a la 46775591b213SSam Leffler * ath_init. 46785591b213SSam Leffler */ 46795591b213SSam Leffler static int 46805591b213SSam Leffler ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan) 46815591b213SSam Leffler { 4682b032f27cSSam Leffler struct ifnet *ifp = sc->sc_ifp; 4683b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 46845591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 4685c42a7b7eSSam Leffler 468659efa8b5SSam Leffler DPRINTF(sc, ATH_DEBUG_RESET, "%s: %u (%u MHz, flags 0x%x)\n", 468759efa8b5SSam Leffler __func__, ieee80211_chan2ieee(ic, chan), 468859efa8b5SSam Leffler chan->ic_freq, chan->ic_flags); 468959efa8b5SSam Leffler if (chan != sc->sc_curchan) { 4690c42a7b7eSSam Leffler HAL_STATUS status; 46915591b213SSam Leffler /* 46925591b213SSam Leffler * To switch channels clear any pending DMA operations; 46935591b213SSam Leffler * wait long enough for the RX fifo to drain, reset the 46945591b213SSam Leffler * hardware at the new frequency, and then re-enable 46955591b213SSam Leffler * the relevant bits of the h/w. 46965591b213SSam Leffler */ 46975591b213SSam Leffler ath_hal_intrset(ah, 0); /* disable interrupts */ 4698517526efSAdrian Chadd ath_draintxq(sc, ATH_RESET_FULL); /* clear pending tx frames */ 46995591b213SSam Leffler ath_stoprecv(sc); /* turn off frame recv */ 470059efa8b5SSam Leffler if (!ath_hal_reset(ah, sc->sc_opmode, chan, AH_TRUE, &status)) { 4701b032f27cSSam Leffler if_printf(ifp, "%s: unable to reset " 470279649302SGavin Atkinson "channel %u (%u MHz, flags 0x%x), hal status %u\n", 470359efa8b5SSam Leffler __func__, ieee80211_chan2ieee(ic, chan), 470459efa8b5SSam Leffler chan->ic_freq, chan->ic_flags, status); 47055591b213SSam Leffler return EIO; 47065591b213SSam Leffler } 4707c59005e9SSam Leffler sc->sc_diversity = ath_hal_getdiversity(ah); 4708c42a7b7eSSam Leffler 470948237774SAdrian Chadd /* Let DFS at it in case it's a DFS channel */ 471048237774SAdrian Chadd ath_dfs_radar_enable(sc, ic->ic_curchan); 471148237774SAdrian Chadd 47125591b213SSam Leffler /* 47135591b213SSam Leffler * Re-enable rx framework. 47145591b213SSam Leffler */ 47155591b213SSam Leffler if (ath_startrecv(sc) != 0) { 4716b032f27cSSam Leffler if_printf(ifp, "%s: unable to restart recv logic\n", 4717b032f27cSSam Leffler __func__); 47185591b213SSam Leffler return EIO; 47195591b213SSam Leffler } 47205591b213SSam Leffler 47215591b213SSam Leffler /* 47225591b213SSam Leffler * Change channels and update the h/w rate map 47235591b213SSam Leffler * if we're switching; e.g. 11a to 11b/g. 47245591b213SSam Leffler */ 4725c42a7b7eSSam Leffler ath_chan_change(sc, chan); 47260a915fadSSam Leffler 47270a915fadSSam Leffler /* 47282fd9aabbSAdrian Chadd * Reset clears the beacon timers; reset them 47292fd9aabbSAdrian Chadd * here if needed. 47302fd9aabbSAdrian Chadd */ 47312fd9aabbSAdrian Chadd if (sc->sc_beacons) { /* restart beacons */ 47322fd9aabbSAdrian Chadd #ifdef IEEE80211_SUPPORT_TDMA 47332fd9aabbSAdrian Chadd if (sc->sc_tdma) 47342fd9aabbSAdrian Chadd ath_tdma_config(sc, NULL); 47352fd9aabbSAdrian Chadd else 47362fd9aabbSAdrian Chadd #endif 47372fd9aabbSAdrian Chadd ath_beacon_config(sc, NULL); 47382fd9aabbSAdrian Chadd } 47392fd9aabbSAdrian Chadd 47402fd9aabbSAdrian Chadd /* 47410a915fadSSam Leffler * Re-enable interrupts. 47420a915fadSSam Leffler */ 47430a915fadSSam Leffler ath_hal_intrset(ah, sc->sc_imask); 47445591b213SSam Leffler } 47455591b213SSam Leffler return 0; 47465591b213SSam Leffler } 47475591b213SSam Leffler 47485591b213SSam Leffler /* 47495591b213SSam Leffler * Periodically recalibrate the PHY to account 47505591b213SSam Leffler * for temperature/environment changes. 47515591b213SSam Leffler */ 47525591b213SSam Leffler static void 47535591b213SSam Leffler ath_calibrate(void *arg) 47545591b213SSam Leffler { 47555591b213SSam Leffler struct ath_softc *sc = arg; 47565591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 47572dc7fcc4SSam Leffler struct ifnet *ifp = sc->sc_ifp; 47588d91de92SSam Leffler struct ieee80211com *ic = ifp->if_l2com; 47592dc7fcc4SSam Leffler HAL_BOOL longCal, isCalDone; 4760a108ab63SAdrian Chadd HAL_BOOL aniCal, shortCal = AH_FALSE; 47612dc7fcc4SSam Leffler int nextcal; 47625591b213SSam Leffler 47638d91de92SSam Leffler if (ic->ic_flags & IEEE80211_F_SCAN) /* defer, off channel */ 47648d91de92SSam Leffler goto restart; 47652dc7fcc4SSam Leffler longCal = (ticks - sc->sc_lastlongcal >= ath_longcalinterval*hz); 4766a108ab63SAdrian Chadd aniCal = (ticks - sc->sc_lastani >= ath_anicalinterval*hz/1000); 4767a108ab63SAdrian Chadd if (sc->sc_doresetcal) 4768a108ab63SAdrian Chadd shortCal = (ticks - sc->sc_lastshortcal >= ath_shortcalinterval*hz/1000); 4769a108ab63SAdrian Chadd 4770a108ab63SAdrian Chadd DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: shortCal=%d; longCal=%d; aniCal=%d\n", __func__, shortCal, longCal, aniCal); 4771a108ab63SAdrian Chadd if (aniCal) { 4772a108ab63SAdrian Chadd sc->sc_stats.ast_ani_cal++; 4773a108ab63SAdrian Chadd sc->sc_lastani = ticks; 4774a108ab63SAdrian Chadd ath_hal_ani_poll(ah, sc->sc_curchan); 4775a108ab63SAdrian Chadd } 4776a108ab63SAdrian Chadd 47772dc7fcc4SSam Leffler if (longCal) { 47785591b213SSam Leffler sc->sc_stats.ast_per_cal++; 47798197f57eSAdrian Chadd sc->sc_lastlongcal = ticks; 47805591b213SSam Leffler if (ath_hal_getrfgain(ah) == HAL_RFGAIN_NEED_CHANGE) { 47815591b213SSam Leffler /* 47825591b213SSam Leffler * Rfgain is out of bounds, reset the chip 47835591b213SSam Leffler * to load new gain values. 47845591b213SSam Leffler */ 4785370572d9SSam Leffler DPRINTF(sc, ATH_DEBUG_CALIBRATE, 4786370572d9SSam Leffler "%s: rfgain change\n", __func__); 47875591b213SSam Leffler sc->sc_stats.ast_per_rfgain++; 4788517526efSAdrian Chadd ath_reset(ifp, ATH_RESET_NOLOSS); 47895591b213SSam Leffler } 47902dc7fcc4SSam Leffler /* 47912dc7fcc4SSam Leffler * If this long cal is after an idle period, then 47922dc7fcc4SSam Leffler * reset the data collection state so we start fresh. 47932dc7fcc4SSam Leffler */ 47942dc7fcc4SSam Leffler if (sc->sc_resetcal) { 479559efa8b5SSam Leffler (void) ath_hal_calreset(ah, sc->sc_curchan); 47962dc7fcc4SSam Leffler sc->sc_lastcalreset = ticks; 4797a108ab63SAdrian Chadd sc->sc_lastshortcal = ticks; 47982dc7fcc4SSam Leffler sc->sc_resetcal = 0; 4799a108ab63SAdrian Chadd sc->sc_doresetcal = AH_TRUE; 48002dc7fcc4SSam Leffler } 48012dc7fcc4SSam Leffler } 4802a108ab63SAdrian Chadd 4803a108ab63SAdrian Chadd /* Only call if we're doing a short/long cal, not for ANI calibration */ 4804a108ab63SAdrian Chadd if (shortCal || longCal) { 480559efa8b5SSam Leffler if (ath_hal_calibrateN(ah, sc->sc_curchan, longCal, &isCalDone)) { 48062dc7fcc4SSam Leffler if (longCal) { 48072dc7fcc4SSam Leffler /* 48082dc7fcc4SSam Leffler * Calibrate noise floor data again in case of change. 48092dc7fcc4SSam Leffler */ 48102dc7fcc4SSam Leffler ath_hal_process_noisefloor(ah); 48112dc7fcc4SSam Leffler } 48122dc7fcc4SSam Leffler } else { 4813c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, 4814c42a7b7eSSam Leffler "%s: calibration of channel %u failed\n", 481559efa8b5SSam Leffler __func__, sc->sc_curchan->ic_freq); 48165591b213SSam Leffler sc->sc_stats.ast_per_calfail++; 48175591b213SSam Leffler } 4818a108ab63SAdrian Chadd if (shortCal) 4819a108ab63SAdrian Chadd sc->sc_lastshortcal = ticks; 4820a108ab63SAdrian Chadd } 48212dc7fcc4SSam Leffler if (!isCalDone) { 48228d91de92SSam Leffler restart: 48237b0c77ecSSam Leffler /* 48242dc7fcc4SSam Leffler * Use a shorter interval to potentially collect multiple 48252dc7fcc4SSam Leffler * data samples required to complete calibration. Once 48262dc7fcc4SSam Leffler * we're told the work is done we drop back to a longer 48272dc7fcc4SSam Leffler * interval between requests. We're more aggressive doing 48282dc7fcc4SSam Leffler * work when operating as an AP to improve operation right 48292dc7fcc4SSam Leffler * after startup. 48307b0c77ecSSam Leffler */ 4831a108ab63SAdrian Chadd sc->sc_lastshortcal = ticks; 4832a108ab63SAdrian Chadd nextcal = ath_shortcalinterval*hz/1000; 48332dc7fcc4SSam Leffler if (sc->sc_opmode != HAL_M_HOSTAP) 48342dc7fcc4SSam Leffler nextcal *= 10; 4835a108ab63SAdrian Chadd sc->sc_doresetcal = AH_TRUE; 48362dc7fcc4SSam Leffler } else { 4837a108ab63SAdrian Chadd /* nextcal should be the shortest time for next event */ 48382dc7fcc4SSam Leffler nextcal = ath_longcalinterval*hz; 48392dc7fcc4SSam Leffler if (sc->sc_lastcalreset == 0) 48402dc7fcc4SSam Leffler sc->sc_lastcalreset = sc->sc_lastlongcal; 48412dc7fcc4SSam Leffler else if (ticks - sc->sc_lastcalreset >= ath_resetcalinterval*hz) 48422dc7fcc4SSam Leffler sc->sc_resetcal = 1; /* setup reset next trip */ 4843a108ab63SAdrian Chadd sc->sc_doresetcal = AH_FALSE; 4844bd5a9920SSam Leffler } 4845a108ab63SAdrian Chadd /* ANI calibration may occur more often than short/long/resetcal */ 4846a108ab63SAdrian Chadd if (ath_anicalinterval > 0) 4847a108ab63SAdrian Chadd nextcal = MIN(nextcal, ath_anicalinterval*hz/1000); 4848bd5a9920SSam Leffler 48492dc7fcc4SSam Leffler if (nextcal != 0) { 48502dc7fcc4SSam Leffler DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: next +%u (%sisCalDone)\n", 48512dc7fcc4SSam Leffler __func__, nextcal, isCalDone ? "" : "!"); 48522dc7fcc4SSam Leffler callout_reset(&sc->sc_cal_ch, nextcal, ath_calibrate, sc); 48532dc7fcc4SSam Leffler } else { 48542dc7fcc4SSam Leffler DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: calibration disabled\n", 48552dc7fcc4SSam Leffler __func__); 48562dc7fcc4SSam Leffler /* NB: don't rearm timer */ 48572dc7fcc4SSam Leffler } 48585591b213SSam Leffler } 48595591b213SSam Leffler 486068e8e04eSSam Leffler static void 486168e8e04eSSam Leffler ath_scan_start(struct ieee80211com *ic) 486268e8e04eSSam Leffler { 486368e8e04eSSam Leffler struct ifnet *ifp = ic->ic_ifp; 486468e8e04eSSam Leffler struct ath_softc *sc = ifp->if_softc; 486568e8e04eSSam Leffler struct ath_hal *ah = sc->sc_ah; 486668e8e04eSSam Leffler u_int32_t rfilt; 486768e8e04eSSam Leffler 486868e8e04eSSam Leffler /* XXX calibration timer? */ 486968e8e04eSSam Leffler 487068e8e04eSSam Leffler sc->sc_scanning = 1; 487168e8e04eSSam Leffler sc->sc_syncbeacon = 0; 487268e8e04eSSam Leffler rfilt = ath_calcrxfilter(sc); 487368e8e04eSSam Leffler ath_hal_setrxfilter(ah, rfilt); 487468e8e04eSSam Leffler ath_hal_setassocid(ah, ifp->if_broadcastaddr, 0); 487568e8e04eSSam Leffler 487668e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s aid 0\n", 487768e8e04eSSam Leffler __func__, rfilt, ether_sprintf(ifp->if_broadcastaddr)); 487868e8e04eSSam Leffler } 487968e8e04eSSam Leffler 488068e8e04eSSam Leffler static void 488168e8e04eSSam Leffler ath_scan_end(struct ieee80211com *ic) 488268e8e04eSSam Leffler { 488368e8e04eSSam Leffler struct ifnet *ifp = ic->ic_ifp; 488468e8e04eSSam Leffler struct ath_softc *sc = ifp->if_softc; 488568e8e04eSSam Leffler struct ath_hal *ah = sc->sc_ah; 488668e8e04eSSam Leffler u_int32_t rfilt; 488768e8e04eSSam Leffler 488868e8e04eSSam Leffler sc->sc_scanning = 0; 488968e8e04eSSam Leffler rfilt = ath_calcrxfilter(sc); 489068e8e04eSSam Leffler ath_hal_setrxfilter(ah, rfilt); 489168e8e04eSSam Leffler ath_hal_setassocid(ah, sc->sc_curbssid, sc->sc_curaid); 489268e8e04eSSam Leffler 489368e8e04eSSam Leffler ath_hal_process_noisefloor(ah); 489468e8e04eSSam Leffler 489568e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s aid 0x%x\n", 489668e8e04eSSam Leffler __func__, rfilt, ether_sprintf(sc->sc_curbssid), 489768e8e04eSSam Leffler sc->sc_curaid); 489868e8e04eSSam Leffler } 489968e8e04eSSam Leffler 490068e8e04eSSam Leffler static void 490168e8e04eSSam Leffler ath_set_channel(struct ieee80211com *ic) 490268e8e04eSSam Leffler { 490368e8e04eSSam Leffler struct ifnet *ifp = ic->ic_ifp; 490468e8e04eSSam Leffler struct ath_softc *sc = ifp->if_softc; 490568e8e04eSSam Leffler 490668e8e04eSSam Leffler (void) ath_chan_set(sc, ic->ic_curchan); 490768e8e04eSSam Leffler /* 490868e8e04eSSam Leffler * If we are returning to our bss channel then mark state 490968e8e04eSSam Leffler * so the next recv'd beacon's tsf will be used to sync the 491068e8e04eSSam Leffler * beacon timers. Note that since we only hear beacons in 491168e8e04eSSam Leffler * sta/ibss mode this has no effect in other operating modes. 491268e8e04eSSam Leffler */ 491368e8e04eSSam Leffler if (!sc->sc_scanning && ic->ic_curchan == ic->ic_bsschan) 491468e8e04eSSam Leffler sc->sc_syncbeacon = 1; 491568e8e04eSSam Leffler } 491668e8e04eSSam Leffler 4917b032f27cSSam Leffler /* 4918b032f27cSSam Leffler * Walk the vap list and check if there any vap's in RUN state. 4919b032f27cSSam Leffler */ 49205591b213SSam Leffler static int 4921b032f27cSSam Leffler ath_isanyrunningvaps(struct ieee80211vap *this) 49225591b213SSam Leffler { 4923b032f27cSSam Leffler struct ieee80211com *ic = this->iv_ic; 4924b032f27cSSam Leffler struct ieee80211vap *vap; 4925b032f27cSSam Leffler 4926b032f27cSSam Leffler IEEE80211_LOCK_ASSERT(ic); 4927b032f27cSSam Leffler 4928b032f27cSSam Leffler TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 4929309a3e45SSam Leffler if (vap != this && vap->iv_state >= IEEE80211_S_RUN) 4930b032f27cSSam Leffler return 1; 4931b032f27cSSam Leffler } 4932b032f27cSSam Leffler return 0; 4933b032f27cSSam Leffler } 4934b032f27cSSam Leffler 4935b032f27cSSam Leffler static int 4936b032f27cSSam Leffler ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 4937b032f27cSSam Leffler { 4938b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 4939b032f27cSSam Leffler struct ath_softc *sc = ic->ic_ifp->if_softc; 4940b032f27cSSam Leffler struct ath_vap *avp = ATH_VAP(vap); 494145bbf62fSSam Leffler struct ath_hal *ah = sc->sc_ah; 4942b032f27cSSam Leffler struct ieee80211_node *ni = NULL; 494368e8e04eSSam Leffler int i, error, stamode; 49445591b213SSam Leffler u_int32_t rfilt; 4945f52efb6dSAdrian Chadd int csa_run_transition = 0; 49465591b213SSam Leffler static const HAL_LED_STATE leds[] = { 49475591b213SSam Leffler HAL_LED_INIT, /* IEEE80211_S_INIT */ 49485591b213SSam Leffler HAL_LED_SCAN, /* IEEE80211_S_SCAN */ 49495591b213SSam Leffler HAL_LED_AUTH, /* IEEE80211_S_AUTH */ 49505591b213SSam Leffler HAL_LED_ASSOC, /* IEEE80211_S_ASSOC */ 495177d5e068SSam Leffler HAL_LED_RUN, /* IEEE80211_S_CAC */ 49525591b213SSam Leffler HAL_LED_RUN, /* IEEE80211_S_RUN */ 495377d5e068SSam Leffler HAL_LED_RUN, /* IEEE80211_S_CSA */ 495477d5e068SSam Leffler HAL_LED_RUN, /* IEEE80211_S_SLEEP */ 49555591b213SSam Leffler }; 49565591b213SSam Leffler 4957c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_STATE, "%s: %s -> %s\n", __func__, 4958b032f27cSSam Leffler ieee80211_state_name[vap->iv_state], 4959c42a7b7eSSam Leffler ieee80211_state_name[nstate]); 49605591b213SSam Leffler 4961f52efb6dSAdrian Chadd if (vap->iv_state == IEEE80211_S_CSA && nstate == IEEE80211_S_RUN) 4962f52efb6dSAdrian Chadd csa_run_transition = 1; 4963f52efb6dSAdrian Chadd 49642e986da5SSam Leffler callout_drain(&sc->sc_cal_ch); 49655591b213SSam Leffler ath_hal_setledstate(ah, leds[nstate]); /* set LED */ 49665591b213SSam Leffler 4967b032f27cSSam Leffler if (nstate == IEEE80211_S_SCAN) { 496858769f58SSam Leffler /* 4969b032f27cSSam Leffler * Scanning: turn off beacon miss and don't beacon. 4970b032f27cSSam Leffler * Mark beacon state so when we reach RUN state we'll 4971b032f27cSSam Leffler * [re]setup beacons. Unblock the task q thread so 4972b032f27cSSam Leffler * deferred interrupt processing is done. 497358769f58SSam Leffler */ 4974b032f27cSSam Leffler ath_hal_intrset(ah, 4975b032f27cSSam Leffler sc->sc_imask &~ (HAL_INT_SWBA | HAL_INT_BMISS)); 49765591b213SSam Leffler sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS); 4977b032f27cSSam Leffler sc->sc_beacons = 0; 4978b032f27cSSam Leffler taskqueue_unblock(sc->sc_tq); 49795591b213SSam Leffler } 49805591b213SSam Leffler 4981b032f27cSSam Leffler ni = vap->iv_bss; 498268e8e04eSSam Leffler rfilt = ath_calcrxfilter(sc); 4983b032f27cSSam Leffler stamode = (vap->iv_opmode == IEEE80211_M_STA || 49847b916f89SSam Leffler vap->iv_opmode == IEEE80211_M_AHDEMO || 4985b032f27cSSam Leffler vap->iv_opmode == IEEE80211_M_IBSS); 498668e8e04eSSam Leffler if (stamode && nstate == IEEE80211_S_RUN) { 498768e8e04eSSam Leffler sc->sc_curaid = ni->ni_associd; 498868e8e04eSSam Leffler IEEE80211_ADDR_COPY(sc->sc_curbssid, ni->ni_bssid); 4989b032f27cSSam Leffler ath_hal_setassocid(ah, sc->sc_curbssid, sc->sc_curaid); 4990b032f27cSSam Leffler } 499168e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s aid 0x%x\n", 4992b032f27cSSam Leffler __func__, rfilt, ether_sprintf(sc->sc_curbssid), sc->sc_curaid); 499368e8e04eSSam Leffler ath_hal_setrxfilter(ah, rfilt); 499468e8e04eSSam Leffler 4995b032f27cSSam Leffler /* XXX is this to restore keycache on resume? */ 4996b032f27cSSam Leffler if (vap->iv_opmode != IEEE80211_M_STA && 4997b032f27cSSam Leffler (vap->iv_flags & IEEE80211_F_PRIVACY)) { 49985591b213SSam Leffler for (i = 0; i < IEEE80211_WEP_NKID; i++) 49995591b213SSam Leffler if (ath_hal_keyisvalid(ah, i)) 500068e8e04eSSam Leffler ath_hal_keysetmac(ah, i, ni->ni_bssid); 50015591b213SSam Leffler } 5002b032f27cSSam Leffler 5003b032f27cSSam Leffler /* 5004b032f27cSSam Leffler * Invoke the parent method to do net80211 work. 5005b032f27cSSam Leffler */ 5006b032f27cSSam Leffler error = avp->av_newstate(vap, nstate, arg); 5007b032f27cSSam Leffler if (error != 0) 5008b032f27cSSam Leffler goto bad; 5009c42a7b7eSSam Leffler 501068e8e04eSSam Leffler if (nstate == IEEE80211_S_RUN) { 5011b032f27cSSam Leffler /* NB: collect bss node again, it may have changed */ 5012b032f27cSSam Leffler ni = vap->iv_bss; 50135591b213SSam Leffler 5014b032f27cSSam Leffler DPRINTF(sc, ATH_DEBUG_STATE, 5015b032f27cSSam Leffler "%s(RUN): iv_flags 0x%08x bintvl %d bssid %s " 5016b032f27cSSam Leffler "capinfo 0x%04x chan %d\n", __func__, 5017b032f27cSSam Leffler vap->iv_flags, ni->ni_intval, ether_sprintf(ni->ni_bssid), 5018b032f27cSSam Leffler ni->ni_capinfo, ieee80211_chan2ieee(ic, ic->ic_curchan)); 5019b032f27cSSam Leffler 5020b032f27cSSam Leffler switch (vap->iv_opmode) { 5021584f7327SSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 502210ad9a77SSam Leffler case IEEE80211_M_AHDEMO: 502310ad9a77SSam Leffler if ((vap->iv_caps & IEEE80211_C_TDMA) == 0) 502410ad9a77SSam Leffler break; 502510ad9a77SSam Leffler /* fall thru... */ 502610ad9a77SSam Leffler #endif 5027e8fd88a3SSam Leffler case IEEE80211_M_HOSTAP: 5028e8fd88a3SSam Leffler case IEEE80211_M_IBSS: 502959aa14a9SRui Paulo case IEEE80211_M_MBSS: 50305591b213SSam Leffler /* 5031e8fd88a3SSam Leffler * Allocate and setup the beacon frame. 5032e8fd88a3SSam Leffler * 5033f818612bSSam Leffler * Stop any previous beacon DMA. This may be 5034f818612bSSam Leffler * necessary, for example, when an ibss merge 5035f818612bSSam Leffler * causes reconfiguration; there will be a state 5036f818612bSSam Leffler * transition from RUN->RUN that means we may 5037f818612bSSam Leffler * be called with beacon transmission active. 5038f818612bSSam Leffler */ 5039f818612bSSam Leffler ath_hal_stoptxdma(ah, sc->sc_bhalq); 5040b032f27cSSam Leffler 50415591b213SSam Leffler error = ath_beacon_alloc(sc, ni); 50425591b213SSam Leffler if (error != 0) 50435591b213SSam Leffler goto bad; 50447a04dc27SSam Leffler /* 504580d939bfSSam Leffler * If joining an adhoc network defer beacon timer 504680d939bfSSam Leffler * configuration to the next beacon frame so we 504780d939bfSSam Leffler * have a current TSF to use. Otherwise we're 5048b032f27cSSam Leffler * starting an ibss/bss so there's no need to delay; 5049b032f27cSSam Leffler * if this is the first vap moving to RUN state, then 5050b032f27cSSam Leffler * beacon state needs to be [re]configured. 50517a04dc27SSam Leffler */ 5052b032f27cSSam Leffler if (vap->iv_opmode == IEEE80211_M_IBSS && 5053b032f27cSSam Leffler ni->ni_tstamp.tsf != 0) { 505480d939bfSSam Leffler sc->sc_syncbeacon = 1; 5055b032f27cSSam Leffler } else if (!sc->sc_beacons) { 5056584f7327SSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 505710ad9a77SSam Leffler if (vap->iv_caps & IEEE80211_C_TDMA) 505810ad9a77SSam Leffler ath_tdma_config(sc, vap); 505910ad9a77SSam Leffler else 506010ad9a77SSam Leffler #endif 5061b032f27cSSam Leffler ath_beacon_config(sc, vap); 5062b032f27cSSam Leffler sc->sc_beacons = 1; 5063b032f27cSSam Leffler } 5064e8fd88a3SSam Leffler break; 5065e8fd88a3SSam Leffler case IEEE80211_M_STA: 5066e8fd88a3SSam Leffler /* 506780d939bfSSam Leffler * Defer beacon timer configuration to the next 506880d939bfSSam Leffler * beacon frame so we have a current TSF to use 506980d939bfSSam Leffler * (any TSF collected when scanning is likely old). 5070f52efb6dSAdrian Chadd * However if it's due to a CSA -> RUN transition, 5071f52efb6dSAdrian Chadd * force a beacon update so we pick up a lack of 5072f52efb6dSAdrian Chadd * beacons from an AP in CAC and thus force a 5073f52efb6dSAdrian Chadd * scan. 50747a04dc27SSam Leffler */ 507580d939bfSSam Leffler sc->sc_syncbeacon = 1; 5076f52efb6dSAdrian Chadd if (csa_run_transition) 5077f52efb6dSAdrian Chadd ath_beacon_config(sc, vap); 5078e8fd88a3SSam Leffler break; 5079b032f27cSSam Leffler case IEEE80211_M_MONITOR: 5080b032f27cSSam Leffler /* 5081b032f27cSSam Leffler * Monitor mode vaps have only INIT->RUN and RUN->RUN 5082b032f27cSSam Leffler * transitions so we must re-enable interrupts here to 5083b032f27cSSam Leffler * handle the case of a single monitor mode vap. 5084b032f27cSSam Leffler */ 5085b032f27cSSam Leffler ath_hal_intrset(ah, sc->sc_imask); 5086b032f27cSSam Leffler break; 5087b032f27cSSam Leffler case IEEE80211_M_WDS: 5088b032f27cSSam Leffler break; 5089e8fd88a3SSam Leffler default: 5090e8fd88a3SSam Leffler break; 50915591b213SSam Leffler } 50925591b213SSam Leffler /* 50937b0c77ecSSam Leffler * Let the hal process statistics collected during a 50947b0c77ecSSam Leffler * scan so it can provide calibrated noise floor data. 50957b0c77ecSSam Leffler */ 50967b0c77ecSSam Leffler ath_hal_process_noisefloor(ah); 50977b0c77ecSSam Leffler /* 5098ffa2cab6SSam Leffler * Reset rssi stats; maybe not the best place... 5099ffa2cab6SSam Leffler */ 5100ffa2cab6SSam Leffler sc->sc_halstats.ns_avgbrssi = ATH_RSSI_DUMMY_MARKER; 5101ffa2cab6SSam Leffler sc->sc_halstats.ns_avgrssi = ATH_RSSI_DUMMY_MARKER; 5102ffa2cab6SSam Leffler sc->sc_halstats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER; 510345bbf62fSSam Leffler /* 5104b032f27cSSam Leffler * Finally, start any timers and the task q thread 5105b032f27cSSam Leffler * (in case we didn't go through SCAN state). 510645bbf62fSSam Leffler */ 51072dc7fcc4SSam Leffler if (ath_longcalinterval != 0) { 5108c42a7b7eSSam Leffler /* start periodic recalibration timer */ 51092dc7fcc4SSam Leffler callout_reset(&sc->sc_cal_ch, 1, ath_calibrate, sc); 51102dc7fcc4SSam Leffler } else { 51112dc7fcc4SSam Leffler DPRINTF(sc, ATH_DEBUG_CALIBRATE, 51122dc7fcc4SSam Leffler "%s: calibration disabled\n", __func__); 5113c42a7b7eSSam Leffler } 5114b032f27cSSam Leffler taskqueue_unblock(sc->sc_tq); 5115b032f27cSSam Leffler } else if (nstate == IEEE80211_S_INIT) { 5116b032f27cSSam Leffler /* 5117b032f27cSSam Leffler * If there are no vaps left in RUN state then 5118b032f27cSSam Leffler * shutdown host/driver operation: 5119b032f27cSSam Leffler * o disable interrupts 5120b032f27cSSam Leffler * o disable the task queue thread 5121b032f27cSSam Leffler * o mark beacon processing as stopped 5122b032f27cSSam Leffler */ 5123b032f27cSSam Leffler if (!ath_isanyrunningvaps(vap)) { 5124b032f27cSSam Leffler sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS); 5125b032f27cSSam Leffler /* disable interrupts */ 5126b032f27cSSam Leffler ath_hal_intrset(ah, sc->sc_imask &~ HAL_INT_GLOBAL); 5127b032f27cSSam Leffler taskqueue_block(sc->sc_tq); 5128b032f27cSSam Leffler sc->sc_beacons = 0; 5129b032f27cSSam Leffler } 5130584f7327SSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 513110ad9a77SSam Leffler ath_hal_setcca(ah, AH_TRUE); 513210ad9a77SSam Leffler #endif 5133b032f27cSSam Leffler } 51345591b213SSam Leffler bad: 51355591b213SSam Leffler return error; 51365591b213SSam Leffler } 51375591b213SSam Leffler 51385591b213SSam Leffler /* 5139e8fd88a3SSam Leffler * Allocate a key cache slot to the station so we can 5140e8fd88a3SSam Leffler * setup a mapping from key index to node. The key cache 5141e8fd88a3SSam Leffler * slot is needed for managing antenna state and for 5142e8fd88a3SSam Leffler * compression when stations do not use crypto. We do 5143e8fd88a3SSam Leffler * it uniliaterally here; if crypto is employed this slot 5144e8fd88a3SSam Leffler * will be reassigned. 5145e8fd88a3SSam Leffler */ 5146e8fd88a3SSam Leffler static void 5147e8fd88a3SSam Leffler ath_setup_stationkey(struct ieee80211_node *ni) 5148e8fd88a3SSam Leffler { 5149b032f27cSSam Leffler struct ieee80211vap *vap = ni->ni_vap; 5150b032f27cSSam Leffler struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; 5151c1225b52SSam Leffler ieee80211_keyix keyix, rxkeyix; 5152e8fd88a3SSam Leffler 5153b032f27cSSam Leffler if (!ath_key_alloc(vap, &ni->ni_ucastkey, &keyix, &rxkeyix)) { 5154e8fd88a3SSam Leffler /* 5155e8fd88a3SSam Leffler * Key cache is full; we'll fall back to doing 5156e8fd88a3SSam Leffler * the more expensive lookup in software. Note 5157e8fd88a3SSam Leffler * this also means no h/w compression. 5158e8fd88a3SSam Leffler */ 5159e8fd88a3SSam Leffler /* XXX msg+statistic */ 5160e8fd88a3SSam Leffler } else { 5161c1225b52SSam Leffler /* XXX locking? */ 5162e8fd88a3SSam Leffler ni->ni_ucastkey.wk_keyix = keyix; 5163c1225b52SSam Leffler ni->ni_ucastkey.wk_rxkeyix = rxkeyix; 516433052833SSam Leffler /* NB: must mark device key to get called back on delete */ 516533052833SSam Leffler ni->ni_ucastkey.wk_flags |= IEEE80211_KEY_DEVKEY; 5166d3ac945bSSam Leffler IEEE80211_ADDR_COPY(ni->ni_ucastkey.wk_macaddr, ni->ni_macaddr); 5167e8fd88a3SSam Leffler /* NB: this will create a pass-thru key entry */ 5168d3ac945bSSam Leffler ath_keyset(sc, &ni->ni_ucastkey, vap->iv_bss); 5169e8fd88a3SSam Leffler } 5170e8fd88a3SSam Leffler } 5171e8fd88a3SSam Leffler 5172e8fd88a3SSam Leffler /* 51735591b213SSam Leffler * Setup driver-specific state for a newly associated node. 51745591b213SSam Leffler * Note that we're called also on a re-associate, the isnew 51755591b213SSam Leffler * param tells us if this is the first time or not. 51765591b213SSam Leffler */ 51775591b213SSam Leffler static void 5178e9962332SSam Leffler ath_newassoc(struct ieee80211_node *ni, int isnew) 51795591b213SSam Leffler { 5180b032f27cSSam Leffler struct ath_node *an = ATH_NODE(ni); 5181b032f27cSSam Leffler struct ieee80211vap *vap = ni->ni_vap; 5182b032f27cSSam Leffler struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; 5183c62362cbSSam Leffler const struct ieee80211_txparam *tp = ni->ni_txparms; 51845591b213SSam Leffler 5185ab06fdf2SSam Leffler an->an_mcastrix = ath_tx_findrix(sc, tp->mcastrate); 5186ab06fdf2SSam Leffler an->an_mgmtrix = ath_tx_findrix(sc, tp->mgmtrate); 5187b032f27cSSam Leffler 5188b032f27cSSam Leffler ath_rate_newassoc(sc, an, isnew); 5189e8fd88a3SSam Leffler if (isnew && 5190b032f27cSSam Leffler (vap->iv_flags & IEEE80211_F_PRIVACY) == 0 && sc->sc_hasclrkey && 5191b032f27cSSam Leffler ni->ni_ucastkey.wk_keyix == IEEE80211_KEYIX_NONE) 5192e8fd88a3SSam Leffler ath_setup_stationkey(ni); 5193e8fd88a3SSam Leffler } 51945591b213SSam Leffler 51955591b213SSam Leffler static int 519659efa8b5SSam Leffler ath_setregdomain(struct ieee80211com *ic, struct ieee80211_regdomain *reg, 5197b032f27cSSam Leffler int nchans, struct ieee80211_channel chans[]) 5198b032f27cSSam Leffler { 5199b032f27cSSam Leffler struct ath_softc *sc = ic->ic_ifp->if_softc; 5200b032f27cSSam Leffler struct ath_hal *ah = sc->sc_ah; 520159efa8b5SSam Leffler HAL_STATUS status; 5202b032f27cSSam Leffler 5203033022a9SSam Leffler DPRINTF(sc, ATH_DEBUG_REGDOMAIN, 520459efa8b5SSam Leffler "%s: rd %u cc %u location %c%s\n", 520559efa8b5SSam Leffler __func__, reg->regdomain, reg->country, reg->location, 520659efa8b5SSam Leffler reg->ecm ? " ecm" : ""); 5207033022a9SSam Leffler 520859efa8b5SSam Leffler status = ath_hal_set_channels(ah, chans, nchans, 520959efa8b5SSam Leffler reg->country, reg->regdomain); 521059efa8b5SSam Leffler if (status != HAL_OK) { 521159efa8b5SSam Leffler DPRINTF(sc, ATH_DEBUG_REGDOMAIN, "%s: failed, status %u\n", 521259efa8b5SSam Leffler __func__, status); 521359efa8b5SSam Leffler return EINVAL; /* XXX */ 5214b032f27cSSam Leffler } 52158db87e40SAdrian Chadd 5216b032f27cSSam Leffler return 0; 5217b032f27cSSam Leffler } 5218b032f27cSSam Leffler 5219b032f27cSSam Leffler static void 5220b032f27cSSam Leffler ath_getradiocaps(struct ieee80211com *ic, 52215fe9f044SSam Leffler int maxchans, int *nchans, struct ieee80211_channel chans[]) 5222b032f27cSSam Leffler { 5223b032f27cSSam Leffler struct ath_softc *sc = ic->ic_ifp->if_softc; 5224b032f27cSSam Leffler struct ath_hal *ah = sc->sc_ah; 5225b032f27cSSam Leffler 522659efa8b5SSam Leffler DPRINTF(sc, ATH_DEBUG_REGDOMAIN, "%s: use rd %u cc %d\n", 522759efa8b5SSam Leffler __func__, SKU_DEBUG, CTRY_DEFAULT); 5228033022a9SSam Leffler 522959efa8b5SSam Leffler /* XXX check return */ 523059efa8b5SSam Leffler (void) ath_hal_getchannels(ah, chans, maxchans, nchans, 523159efa8b5SSam Leffler HAL_MODE_ALL, CTRY_DEFAULT, SKU_DEBUG, AH_TRUE); 5232033022a9SSam Leffler 5233b032f27cSSam Leffler } 5234b032f27cSSam Leffler 5235b032f27cSSam Leffler static int 5236b032f27cSSam Leffler ath_getchannels(struct ath_softc *sc) 5237b032f27cSSam Leffler { 5238b032f27cSSam Leffler struct ifnet *ifp = sc->sc_ifp; 5239b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 5240b032f27cSSam Leffler struct ath_hal *ah = sc->sc_ah; 524159efa8b5SSam Leffler HAL_STATUS status; 5242b032f27cSSam Leffler 5243b032f27cSSam Leffler /* 524459efa8b5SSam Leffler * Collect channel set based on EEPROM contents. 5245b032f27cSSam Leffler */ 524659efa8b5SSam Leffler status = ath_hal_init_channels(ah, ic->ic_channels, IEEE80211_CHAN_MAX, 524759efa8b5SSam Leffler &ic->ic_nchans, HAL_MODE_ALL, CTRY_DEFAULT, SKU_NONE, AH_TRUE); 524859efa8b5SSam Leffler if (status != HAL_OK) { 524959efa8b5SSam Leffler if_printf(ifp, "%s: unable to collect channel list from hal, " 525059efa8b5SSam Leffler "status %d\n", __func__, status); 525159efa8b5SSam Leffler return EINVAL; 525259efa8b5SSam Leffler } 5253ca876918SSam Leffler (void) ath_hal_getregdomain(ah, &sc->sc_eerd); 5254ca876918SSam Leffler ath_hal_getcountrycode(ah, &sc->sc_eecc); /* NB: cannot fail */ 525559efa8b5SSam Leffler /* XXX map Atheros sku's to net80211 SKU's */ 525659efa8b5SSam Leffler /* XXX net80211 types too small */ 525759efa8b5SSam Leffler ic->ic_regdomain.regdomain = (uint16_t) sc->sc_eerd; 525859efa8b5SSam Leffler ic->ic_regdomain.country = (uint16_t) sc->sc_eecc; 525959efa8b5SSam Leffler ic->ic_regdomain.isocc[0] = ' '; /* XXX don't know */ 526059efa8b5SSam Leffler ic->ic_regdomain.isocc[1] = ' '; 526159efa8b5SSam Leffler 5262b032f27cSSam Leffler ic->ic_regdomain.ecm = 1; 5263b032f27cSSam Leffler ic->ic_regdomain.location = 'I'; 5264033022a9SSam Leffler 5265033022a9SSam Leffler DPRINTF(sc, ATH_DEBUG_REGDOMAIN, 526659efa8b5SSam Leffler "%s: eeprom rd %u cc %u (mapped rd %u cc %u) location %c%s\n", 5267033022a9SSam Leffler __func__, sc->sc_eerd, sc->sc_eecc, 5268033022a9SSam Leffler ic->ic_regdomain.regdomain, ic->ic_regdomain.country, 526959efa8b5SSam Leffler ic->ic_regdomain.location, ic->ic_regdomain.ecm ? " ecm" : ""); 52705591b213SSam Leffler return 0; 52715591b213SSam Leffler } 52725591b213SSam Leffler 5273c42a7b7eSSam Leffler static void 52743e50ec2cSSam Leffler ath_led_done(void *arg) 5275c42a7b7eSSam Leffler { 52763e50ec2cSSam Leffler struct ath_softc *sc = arg; 52773e50ec2cSSam Leffler 52783e50ec2cSSam Leffler sc->sc_blinking = 0; 52793e50ec2cSSam Leffler } 5280c42a7b7eSSam Leffler 5281c42a7b7eSSam Leffler /* 52823e50ec2cSSam Leffler * Turn the LED off: flip the pin and then set a timer so no 52833e50ec2cSSam Leffler * update will happen for the specified duration. 5284c42a7b7eSSam Leffler */ 52853e50ec2cSSam Leffler static void 52863e50ec2cSSam Leffler ath_led_off(void *arg) 52873e50ec2cSSam Leffler { 52883e50ec2cSSam Leffler struct ath_softc *sc = arg; 52893e50ec2cSSam Leffler 52903e50ec2cSSam Leffler ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, !sc->sc_ledon); 52913e50ec2cSSam Leffler callout_reset(&sc->sc_ledtimer, sc->sc_ledoff, ath_led_done, sc); 5292c42a7b7eSSam Leffler } 52933e50ec2cSSam Leffler 52943e50ec2cSSam Leffler /* 52953e50ec2cSSam Leffler * Blink the LED according to the specified on/off times. 52963e50ec2cSSam Leffler */ 52973e50ec2cSSam Leffler static void 52983e50ec2cSSam Leffler ath_led_blink(struct ath_softc *sc, int on, int off) 52993e50ec2cSSam Leffler { 53003e50ec2cSSam Leffler DPRINTF(sc, ATH_DEBUG_LED, "%s: on %u off %u\n", __func__, on, off); 53013e50ec2cSSam Leffler ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, sc->sc_ledon); 53023e50ec2cSSam Leffler sc->sc_blinking = 1; 53033e50ec2cSSam Leffler sc->sc_ledoff = off; 53043e50ec2cSSam Leffler callout_reset(&sc->sc_ledtimer, on, ath_led_off, sc); 53053e50ec2cSSam Leffler } 53063e50ec2cSSam Leffler 53073e50ec2cSSam Leffler static void 530846d4d74cSSam Leffler ath_led_event(struct ath_softc *sc, int rix) 53093e50ec2cSSam Leffler { 53103e50ec2cSSam Leffler sc->sc_ledevent = ticks; /* time of last event */ 53113e50ec2cSSam Leffler if (sc->sc_blinking) /* don't interrupt active blink */ 53123e50ec2cSSam Leffler return; 531346d4d74cSSam Leffler ath_led_blink(sc, sc->sc_hwmap[rix].ledon, sc->sc_hwmap[rix].ledoff); 5314c42a7b7eSSam Leffler } 5315c42a7b7eSSam Leffler 53166c4612b9SSam Leffler static int 53176c4612b9SSam Leffler ath_rate_setup(struct ath_softc *sc, u_int mode) 53186c4612b9SSam Leffler { 53196c4612b9SSam Leffler struct ath_hal *ah = sc->sc_ah; 53206c4612b9SSam Leffler const HAL_RATE_TABLE *rt; 53216c4612b9SSam Leffler 53226c4612b9SSam Leffler switch (mode) { 53236c4612b9SSam Leffler case IEEE80211_MODE_11A: 53246c4612b9SSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_11A); 53256c4612b9SSam Leffler break; 5326724c193aSSam Leffler case IEEE80211_MODE_HALF: 5327aaa70f2fSSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_11A_HALF_RATE); 5328aaa70f2fSSam Leffler break; 5329724c193aSSam Leffler case IEEE80211_MODE_QUARTER: 5330aaa70f2fSSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_11A_QUARTER_RATE); 5331aaa70f2fSSam Leffler break; 53326c4612b9SSam Leffler case IEEE80211_MODE_11B: 53336c4612b9SSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_11B); 53346c4612b9SSam Leffler break; 53356c4612b9SSam Leffler case IEEE80211_MODE_11G: 53366c4612b9SSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_11G); 53376c4612b9SSam Leffler break; 53386c4612b9SSam Leffler case IEEE80211_MODE_TURBO_A: 533968e8e04eSSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_108A); 53406c4612b9SSam Leffler break; 53416c4612b9SSam Leffler case IEEE80211_MODE_TURBO_G: 53426c4612b9SSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_108G); 53436c4612b9SSam Leffler break; 534468e8e04eSSam Leffler case IEEE80211_MODE_STURBO_A: 534568e8e04eSSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_TURBO); 534668e8e04eSSam Leffler break; 534768e8e04eSSam Leffler case IEEE80211_MODE_11NA: 534868e8e04eSSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_11NA_HT20); 534968e8e04eSSam Leffler break; 535068e8e04eSSam Leffler case IEEE80211_MODE_11NG: 535168e8e04eSSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_11NG_HT20); 535268e8e04eSSam Leffler break; 53536c4612b9SSam Leffler default: 53546c4612b9SSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid mode %u\n", 53556c4612b9SSam Leffler __func__, mode); 53566c4612b9SSam Leffler return 0; 53576c4612b9SSam Leffler } 53586c4612b9SSam Leffler sc->sc_rates[mode] = rt; 5359aaa70f2fSSam Leffler return (rt != NULL); 53605591b213SSam Leffler } 53615591b213SSam Leffler 53625591b213SSam Leffler static void 53635591b213SSam Leffler ath_setcurmode(struct ath_softc *sc, enum ieee80211_phymode mode) 53645591b213SSam Leffler { 53653e50ec2cSSam Leffler #define N(a) (sizeof(a)/sizeof(a[0])) 53663e50ec2cSSam Leffler /* NB: on/off times from the Atheros NDIS driver, w/ permission */ 53673e50ec2cSSam Leffler static const struct { 53683e50ec2cSSam Leffler u_int rate; /* tx/rx 802.11 rate */ 53693e50ec2cSSam Leffler u_int16_t timeOn; /* LED on time (ms) */ 53703e50ec2cSSam Leffler u_int16_t timeOff; /* LED off time (ms) */ 53713e50ec2cSSam Leffler } blinkrates[] = { 53723e50ec2cSSam Leffler { 108, 40, 10 }, 53733e50ec2cSSam Leffler { 96, 44, 11 }, 53743e50ec2cSSam Leffler { 72, 50, 13 }, 53753e50ec2cSSam Leffler { 48, 57, 14 }, 53763e50ec2cSSam Leffler { 36, 67, 16 }, 53773e50ec2cSSam Leffler { 24, 80, 20 }, 53783e50ec2cSSam Leffler { 22, 100, 25 }, 53793e50ec2cSSam Leffler { 18, 133, 34 }, 53803e50ec2cSSam Leffler { 12, 160, 40 }, 53813e50ec2cSSam Leffler { 10, 200, 50 }, 53823e50ec2cSSam Leffler { 6, 240, 58 }, 53833e50ec2cSSam Leffler { 4, 267, 66 }, 53843e50ec2cSSam Leffler { 2, 400, 100 }, 53853e50ec2cSSam Leffler { 0, 500, 130 }, 5386724c193aSSam Leffler /* XXX half/quarter rates */ 53873e50ec2cSSam Leffler }; 53885591b213SSam Leffler const HAL_RATE_TABLE *rt; 53893e50ec2cSSam Leffler int i, j; 53905591b213SSam Leffler 53915591b213SSam Leffler memset(sc->sc_rixmap, 0xff, sizeof(sc->sc_rixmap)); 53925591b213SSam Leffler rt = sc->sc_rates[mode]; 53935591b213SSam Leffler KASSERT(rt != NULL, ("no h/w rate set for phy mode %u", mode)); 5394180f268dSSam Leffler for (i = 0; i < rt->rateCount; i++) { 5395180f268dSSam Leffler uint8_t ieeerate = rt->info[i].dot11Rate & IEEE80211_RATE_VAL; 5396180f268dSSam Leffler if (rt->info[i].phy != IEEE80211_T_HT) 5397180f268dSSam Leffler sc->sc_rixmap[ieeerate] = i; 5398180f268dSSam Leffler else 5399180f268dSSam Leffler sc->sc_rixmap[ieeerate | IEEE80211_RATE_MCS] = i; 5400180f268dSSam Leffler } 54011b1a8e41SSam Leffler memset(sc->sc_hwmap, 0, sizeof(sc->sc_hwmap)); 540246d4d74cSSam Leffler for (i = 0; i < N(sc->sc_hwmap); i++) { 540346d4d74cSSam Leffler if (i >= rt->rateCount) { 54043e50ec2cSSam Leffler sc->sc_hwmap[i].ledon = (500 * hz) / 1000; 54053e50ec2cSSam Leffler sc->sc_hwmap[i].ledoff = (130 * hz) / 1000; 540616b4851aSSam Leffler continue; 54073e50ec2cSSam Leffler } 54083e50ec2cSSam Leffler sc->sc_hwmap[i].ieeerate = 540946d4d74cSSam Leffler rt->info[i].dot11Rate & IEEE80211_RATE_VAL; 541046d4d74cSSam Leffler if (rt->info[i].phy == IEEE80211_T_HT) 541126041a14SSam Leffler sc->sc_hwmap[i].ieeerate |= IEEE80211_RATE_MCS; 5412d3be6f5bSSam Leffler sc->sc_hwmap[i].txflags = IEEE80211_RADIOTAP_F_DATAPAD; 541346d4d74cSSam Leffler if (rt->info[i].shortPreamble || 541446d4d74cSSam Leffler rt->info[i].phy == IEEE80211_T_OFDM) 5415d3be6f5bSSam Leffler sc->sc_hwmap[i].txflags |= IEEE80211_RADIOTAP_F_SHORTPRE; 54165463c4a4SSam Leffler sc->sc_hwmap[i].rxflags = sc->sc_hwmap[i].txflags; 54173e50ec2cSSam Leffler for (j = 0; j < N(blinkrates)-1; j++) 54183e50ec2cSSam Leffler if (blinkrates[j].rate == sc->sc_hwmap[i].ieeerate) 54193e50ec2cSSam Leffler break; 54203e50ec2cSSam Leffler /* NB: this uses the last entry if the rate isn't found */ 54213e50ec2cSSam Leffler /* XXX beware of overlow */ 54223e50ec2cSSam Leffler sc->sc_hwmap[i].ledon = (blinkrates[j].timeOn * hz) / 1000; 54233e50ec2cSSam Leffler sc->sc_hwmap[i].ledoff = (blinkrates[j].timeOff * hz) / 1000; 5424c42a7b7eSSam Leffler } 54255591b213SSam Leffler sc->sc_currates = rt; 54265591b213SSam Leffler sc->sc_curmode = mode; 54275591b213SSam Leffler /* 5428c42a7b7eSSam Leffler * All protection frames are transmited at 2Mb/s for 5429c42a7b7eSSam Leffler * 11g, otherwise at 1Mb/s. 54305591b213SSam Leffler */ 5431913a1ba1SSam Leffler if (mode == IEEE80211_MODE_11G) 5432ab06fdf2SSam Leffler sc->sc_protrix = ath_tx_findrix(sc, 2*2); 5433913a1ba1SSam Leffler else 5434ab06fdf2SSam Leffler sc->sc_protrix = ath_tx_findrix(sc, 2*1); 54354fa8d4efSDaniel Eischen /* NB: caller is responsible for resetting rate control state */ 54363e50ec2cSSam Leffler #undef N 54375591b213SSam Leffler } 54385591b213SSam Leffler 5439c42a7b7eSSam Leffler static void 54402e986da5SSam Leffler ath_watchdog(void *arg) 5441c42a7b7eSSam Leffler { 54422e986da5SSam Leffler struct ath_softc *sc = arg; 5443c42a7b7eSSam Leffler 54442e986da5SSam Leffler if (sc->sc_wd_timer != 0 && --sc->sc_wd_timer == 0) { 54452e986da5SSam Leffler struct ifnet *ifp = sc->sc_ifp; 5446459bc4f0SSam Leffler uint32_t hangs; 5447459bc4f0SSam Leffler 5448459bc4f0SSam Leffler if (ath_hal_gethangstate(sc->sc_ah, 0xffff, &hangs) && 5449459bc4f0SSam Leffler hangs != 0) { 5450459bc4f0SSam Leffler if_printf(ifp, "%s hang detected (0x%x)\n", 5451459bc4f0SSam Leffler hangs & 0xff ? "bb" : "mac", hangs); 5452459bc4f0SSam Leffler } else 5453c42a7b7eSSam Leffler if_printf(ifp, "device timeout\n"); 5454517526efSAdrian Chadd ath_reset(ifp, ATH_RESET_NOLOSS); 5455c42a7b7eSSam Leffler ifp->if_oerrors++; 5456c42a7b7eSSam Leffler sc->sc_stats.ast_watchdog++; 5457c42a7b7eSSam Leffler } 54582e986da5SSam Leffler callout_schedule(&sc->sc_wd_ch, hz); 5459c42a7b7eSSam Leffler } 5460c42a7b7eSSam Leffler 5461a585a9a1SSam Leffler #ifdef ATH_DIAGAPI 5462c42a7b7eSSam Leffler /* 5463c42a7b7eSSam Leffler * Diagnostic interface to the HAL. This is used by various 5464c42a7b7eSSam Leffler * tools to do things like retrieve register contents for 5465c42a7b7eSSam Leffler * debugging. The mechanism is intentionally opaque so that 5466c42a7b7eSSam Leffler * it can change frequently w/o concern for compatiblity. 5467c42a7b7eSSam Leffler */ 5468c42a7b7eSSam Leffler static int 5469c42a7b7eSSam Leffler ath_ioctl_diag(struct ath_softc *sc, struct ath_diag *ad) 5470c42a7b7eSSam Leffler { 5471c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 5472c42a7b7eSSam Leffler u_int id = ad->ad_id & ATH_DIAG_ID; 5473c42a7b7eSSam Leffler void *indata = NULL; 5474c42a7b7eSSam Leffler void *outdata = NULL; 5475c42a7b7eSSam Leffler u_int32_t insize = ad->ad_in_size; 5476c42a7b7eSSam Leffler u_int32_t outsize = ad->ad_out_size; 5477c42a7b7eSSam Leffler int error = 0; 5478c42a7b7eSSam Leffler 5479c42a7b7eSSam Leffler if (ad->ad_id & ATH_DIAG_IN) { 5480c42a7b7eSSam Leffler /* 5481c42a7b7eSSam Leffler * Copy in data. 5482c42a7b7eSSam Leffler */ 5483c42a7b7eSSam Leffler indata = malloc(insize, M_TEMP, M_NOWAIT); 5484c42a7b7eSSam Leffler if (indata == NULL) { 5485c42a7b7eSSam Leffler error = ENOMEM; 5486c42a7b7eSSam Leffler goto bad; 5487c42a7b7eSSam Leffler } 5488c42a7b7eSSam Leffler error = copyin(ad->ad_in_data, indata, insize); 5489c42a7b7eSSam Leffler if (error) 5490c42a7b7eSSam Leffler goto bad; 5491c42a7b7eSSam Leffler } 5492c42a7b7eSSam Leffler if (ad->ad_id & ATH_DIAG_DYN) { 5493c42a7b7eSSam Leffler /* 5494c42a7b7eSSam Leffler * Allocate a buffer for the results (otherwise the HAL 5495c42a7b7eSSam Leffler * returns a pointer to a buffer where we can read the 5496c42a7b7eSSam Leffler * results). Note that we depend on the HAL leaving this 5497c42a7b7eSSam Leffler * pointer for us to use below in reclaiming the buffer; 5498c42a7b7eSSam Leffler * may want to be more defensive. 5499c42a7b7eSSam Leffler */ 5500c42a7b7eSSam Leffler outdata = malloc(outsize, M_TEMP, M_NOWAIT); 5501c42a7b7eSSam Leffler if (outdata == NULL) { 5502c42a7b7eSSam Leffler error = ENOMEM; 5503c42a7b7eSSam Leffler goto bad; 5504c42a7b7eSSam Leffler } 5505c42a7b7eSSam Leffler } 5506c42a7b7eSSam Leffler if (ath_hal_getdiagstate(ah, id, indata, insize, &outdata, &outsize)) { 5507c42a7b7eSSam Leffler if (outsize < ad->ad_out_size) 5508c42a7b7eSSam Leffler ad->ad_out_size = outsize; 5509c42a7b7eSSam Leffler if (outdata != NULL) 5510c42a7b7eSSam Leffler error = copyout(outdata, ad->ad_out_data, 5511c42a7b7eSSam Leffler ad->ad_out_size); 5512c42a7b7eSSam Leffler } else { 5513c42a7b7eSSam Leffler error = EINVAL; 5514c42a7b7eSSam Leffler } 5515c42a7b7eSSam Leffler bad: 5516c42a7b7eSSam Leffler if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL) 5517c42a7b7eSSam Leffler free(indata, M_TEMP); 5518c42a7b7eSSam Leffler if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL) 5519c42a7b7eSSam Leffler free(outdata, M_TEMP); 5520c42a7b7eSSam Leffler return error; 5521c42a7b7eSSam Leffler } 5522a585a9a1SSam Leffler #endif /* ATH_DIAGAPI */ 5523c42a7b7eSSam Leffler 5524c42a7b7eSSam Leffler static int 5525c42a7b7eSSam Leffler ath_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 5526c42a7b7eSSam Leffler { 5527c42a7b7eSSam Leffler #define IS_RUNNING(ifp) \ 552813f4c340SRobert Watson ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING)) 5529c42a7b7eSSam Leffler struct ath_softc *sc = ifp->if_softc; 5530b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 5531c42a7b7eSSam Leffler struct ifreq *ifr = (struct ifreq *)data; 553284784be1SSam Leffler const HAL_RATE_TABLE *rt; 5533c42a7b7eSSam Leffler int error = 0; 5534c42a7b7eSSam Leffler 5535c42a7b7eSSam Leffler switch (cmd) { 5536c42a7b7eSSam Leffler case SIOCSIFFLAGS: 553731a8c1edSAndrew Thompson ATH_LOCK(sc); 5538c42a7b7eSSam Leffler if (IS_RUNNING(ifp)) { 5539c42a7b7eSSam Leffler /* 5540c42a7b7eSSam Leffler * To avoid rescanning another access point, 5541c42a7b7eSSam Leffler * do not call ath_init() here. Instead, 5542c42a7b7eSSam Leffler * only reflect promisc mode settings. 5543c42a7b7eSSam Leffler */ 5544c42a7b7eSSam Leffler ath_mode_init(sc); 5545c42a7b7eSSam Leffler } else if (ifp->if_flags & IFF_UP) { 5546c42a7b7eSSam Leffler /* 5547c42a7b7eSSam Leffler * Beware of being called during attach/detach 5548c42a7b7eSSam Leffler * to reset promiscuous mode. In that case we 5549c42a7b7eSSam Leffler * will still be marked UP but not RUNNING. 5550c42a7b7eSSam Leffler * However trying to re-init the interface 5551c42a7b7eSSam Leffler * is the wrong thing to do as we've already 5552c42a7b7eSSam Leffler * torn down much of our state. There's 5553c42a7b7eSSam Leffler * probably a better way to deal with this. 5554c42a7b7eSSam Leffler */ 5555b032f27cSSam Leffler if (!sc->sc_invalid) 5556fc74a9f9SBrooks Davis ath_init(sc); /* XXX lose error */ 5557d3ac945bSSam Leffler } else { 5558c42a7b7eSSam Leffler ath_stop_locked(ifp); 5559d3ac945bSSam Leffler #ifdef notyet 5560d3ac945bSSam Leffler /* XXX must wakeup in places like ath_vap_delete */ 5561d3ac945bSSam Leffler if (!sc->sc_invalid) 5562d3ac945bSSam Leffler ath_hal_setpower(sc->sc_ah, HAL_PM_FULL_SLEEP); 5563d3ac945bSSam Leffler #endif 5564d3ac945bSSam Leffler } 556531a8c1edSAndrew Thompson ATH_UNLOCK(sc); 5566c42a7b7eSSam Leffler break; 5567b032f27cSSam Leffler case SIOCGIFMEDIA: 5568b032f27cSSam Leffler case SIOCSIFMEDIA: 5569b032f27cSSam Leffler error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); 5570b032f27cSSam Leffler break; 5571c42a7b7eSSam Leffler case SIOCGATHSTATS: 5572c42a7b7eSSam Leffler /* NB: embed these numbers to get a consistent view */ 5573c42a7b7eSSam Leffler sc->sc_stats.ast_tx_packets = ifp->if_opackets; 5574c42a7b7eSSam Leffler sc->sc_stats.ast_rx_packets = ifp->if_ipackets; 557584784be1SSam Leffler sc->sc_stats.ast_tx_rssi = ATH_RSSI(sc->sc_halstats.ns_avgtxrssi); 557684784be1SSam Leffler sc->sc_stats.ast_rx_rssi = ATH_RSSI(sc->sc_halstats.ns_avgrssi); 5577584f7327SSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 557810ad9a77SSam Leffler sc->sc_stats.ast_tdma_tsfadjp = TDMA_AVG(sc->sc_avgtsfdeltap); 557910ad9a77SSam Leffler sc->sc_stats.ast_tdma_tsfadjm = TDMA_AVG(sc->sc_avgtsfdeltam); 558010ad9a77SSam Leffler #endif 558184784be1SSam Leffler rt = sc->sc_currates; 558246d4d74cSSam Leffler sc->sc_stats.ast_tx_rate = 558346d4d74cSSam Leffler rt->info[sc->sc_txrix].dot11Rate &~ IEEE80211_RATE_BASIC; 55846aa113fdSAdrian Chadd if (rt->info[sc->sc_txrix].phy & IEEE80211_T_HT) 55856aa113fdSAdrian Chadd sc->sc_stats.ast_tx_rate |= IEEE80211_RATE_MCS; 5586c42a7b7eSSam Leffler return copyout(&sc->sc_stats, 5587c42a7b7eSSam Leffler ifr->ifr_data, sizeof (sc->sc_stats)); 55883fc21fedSSam Leffler case SIOCZATHSTATS: 55893fc21fedSSam Leffler error = priv_check(curthread, PRIV_DRIVER); 55903fc21fedSSam Leffler if (error == 0) 55913fc21fedSSam Leffler memset(&sc->sc_stats, 0, sizeof(sc->sc_stats)); 55923fc21fedSSam Leffler break; 5593a585a9a1SSam Leffler #ifdef ATH_DIAGAPI 5594c42a7b7eSSam Leffler case SIOCGATHDIAG: 5595c42a7b7eSSam Leffler error = ath_ioctl_diag(sc, (struct ath_diag *) ifr); 5596c42a7b7eSSam Leffler break; 5597f51c84eaSAdrian Chadd case SIOCGATHPHYERR: 5598f51c84eaSAdrian Chadd error = ath_ioctl_phyerr(sc,(struct ath_diag*) ifr); 5599f51c84eaSAdrian Chadd break; 5600a585a9a1SSam Leffler #endif 560131a8c1edSAndrew Thompson case SIOCGIFADDR: 5602b032f27cSSam Leffler error = ether_ioctl(ifp, cmd, data); 5603c42a7b7eSSam Leffler break; 560431a8c1edSAndrew Thompson default: 560531a8c1edSAndrew Thompson error = EINVAL; 560631a8c1edSAndrew Thompson break; 5607c42a7b7eSSam Leffler } 5608c42a7b7eSSam Leffler return error; 5609a614e076SSam Leffler #undef IS_RUNNING 5610c42a7b7eSSam Leffler } 5611c42a7b7eSSam Leffler 5612c42a7b7eSSam Leffler /* 5613c42a7b7eSSam Leffler * Announce various information on device/driver attach. 5614c42a7b7eSSam Leffler */ 5615c42a7b7eSSam Leffler static void 5616c42a7b7eSSam Leffler ath_announce(struct ath_softc *sc) 5617c42a7b7eSSam Leffler { 5618fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 5619c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 5620c42a7b7eSSam Leffler 5621498657cfSSam Leffler if_printf(ifp, "AR%s mac %d.%d RF%s phy %d.%d\n", 5622498657cfSSam Leffler ath_hal_mac_name(ah), ah->ah_macVersion, ah->ah_macRev, 5623498657cfSSam Leffler ath_hal_rf_name(ah), ah->ah_phyRev >> 4, ah->ah_phyRev & 0xf); 5624c42a7b7eSSam Leffler if (bootverbose) { 5625c42a7b7eSSam Leffler int i; 5626c42a7b7eSSam Leffler for (i = 0; i <= WME_AC_VO; i++) { 5627c42a7b7eSSam Leffler struct ath_txq *txq = sc->sc_ac2q[i]; 5628c42a7b7eSSam Leffler if_printf(ifp, "Use hw queue %u for %s traffic\n", 5629c42a7b7eSSam Leffler txq->axq_qnum, ieee80211_wme_acnames[i]); 5630c42a7b7eSSam Leffler } 5631c42a7b7eSSam Leffler if_printf(ifp, "Use hw queue %u for CAB traffic\n", 5632c42a7b7eSSam Leffler sc->sc_cabq->axq_qnum); 5633c42a7b7eSSam Leffler if_printf(ifp, "Use hw queue %u for beacons\n", sc->sc_bhalq); 5634c42a7b7eSSam Leffler } 5635e2d787faSSam Leffler if (ath_rxbuf != ATH_RXBUF) 5636e2d787faSSam Leffler if_printf(ifp, "using %u rx buffers\n", ath_rxbuf); 5637e2d787faSSam Leffler if (ath_txbuf != ATH_TXBUF) 5638e2d787faSSam Leffler if_printf(ifp, "using %u tx buffers\n", ath_txbuf); 56399ac01d39SRui Paulo if (sc->sc_mcastkey && bootverbose) 56409ac01d39SRui Paulo if_printf(ifp, "using multicast key search\n"); 5641c42a7b7eSSam Leffler } 564210ad9a77SSam Leffler 5643584f7327SSam Leffler #ifdef IEEE80211_SUPPORT_TDMA 564410ad9a77SSam Leffler static void 564510ad9a77SSam Leffler ath_tdma_settimers(struct ath_softc *sc, u_int32_t nexttbtt, u_int32_t bintval) 564610ad9a77SSam Leffler { 564710ad9a77SSam Leffler struct ath_hal *ah = sc->sc_ah; 564810ad9a77SSam Leffler HAL_BEACON_TIMERS bt; 564910ad9a77SSam Leffler 565010ad9a77SSam Leffler bt.bt_intval = bintval | HAL_BEACON_ENA; 565110ad9a77SSam Leffler bt.bt_nexttbtt = nexttbtt; 565210ad9a77SSam Leffler bt.bt_nextdba = (nexttbtt<<3) - sc->sc_tdmadbaprep; 565310ad9a77SSam Leffler bt.bt_nextswba = (nexttbtt<<3) - sc->sc_tdmaswbaprep; 565410ad9a77SSam Leffler bt.bt_nextatim = nexttbtt+1; 5655f3fb1687SAdrian Chadd /* Enables TBTT, DBA, SWBA timers by default */ 5656f3fb1687SAdrian Chadd bt.bt_flags = 0; 565710ad9a77SSam Leffler ath_hal_beaconsettimers(ah, &bt); 565810ad9a77SSam Leffler } 565910ad9a77SSam Leffler 566010ad9a77SSam Leffler /* 566110ad9a77SSam Leffler * Calculate the beacon interval. This is periodic in the 566210ad9a77SSam Leffler * superframe for the bss. We assume each station is configured 566310ad9a77SSam Leffler * identically wrt transmit rate so the guard time we calculate 566410ad9a77SSam Leffler * above will be the same on all stations. Note we need to 566510ad9a77SSam Leffler * factor in the xmit time because the hardware will schedule 566610ad9a77SSam Leffler * a frame for transmit if the start of the frame is within 566710ad9a77SSam Leffler * the burst time. When we get hardware that properly kills 566810ad9a77SSam Leffler * frames in the PCU we can reduce/eliminate the guard time. 566910ad9a77SSam Leffler * 567010ad9a77SSam Leffler * Roundup to 1024 is so we have 1 TU buffer in the guard time 567110ad9a77SSam Leffler * to deal with the granularity of the nexttbtt timer. 11n MAC's 567210ad9a77SSam Leffler * with 1us timer granularity should allow us to reduce/eliminate 567310ad9a77SSam Leffler * this. 567410ad9a77SSam Leffler */ 567510ad9a77SSam Leffler static void 567610ad9a77SSam Leffler ath_tdma_bintvalsetup(struct ath_softc *sc, 567710ad9a77SSam Leffler const struct ieee80211_tdma_state *tdma) 567810ad9a77SSam Leffler { 567910ad9a77SSam Leffler /* copy from vap state (XXX check all vaps have same value?) */ 568010ad9a77SSam Leffler sc->sc_tdmaslotlen = tdma->tdma_slotlen; 568110ad9a77SSam Leffler 568210ad9a77SSam Leffler sc->sc_tdmabintval = roundup((sc->sc_tdmaslotlen+sc->sc_tdmaguard) * 568310ad9a77SSam Leffler tdma->tdma_slotcnt, 1024); 568410ad9a77SSam Leffler sc->sc_tdmabintval >>= 10; /* TSF -> TU */ 568510ad9a77SSam Leffler if (sc->sc_tdmabintval & 1) 568610ad9a77SSam Leffler sc->sc_tdmabintval++; 568710ad9a77SSam Leffler 568810ad9a77SSam Leffler if (tdma->tdma_slot == 0) { 568910ad9a77SSam Leffler /* 569010ad9a77SSam Leffler * Only slot 0 beacons; other slots respond. 569110ad9a77SSam Leffler */ 569210ad9a77SSam Leffler sc->sc_imask |= HAL_INT_SWBA; 569310ad9a77SSam Leffler sc->sc_tdmaswba = 0; /* beacon immediately */ 569410ad9a77SSam Leffler } else { 569510ad9a77SSam Leffler /* XXX all vaps must be slot 0 or slot !0 */ 569610ad9a77SSam Leffler sc->sc_imask &= ~HAL_INT_SWBA; 569710ad9a77SSam Leffler } 569810ad9a77SSam Leffler } 569910ad9a77SSam Leffler 570010ad9a77SSam Leffler /* 570110ad9a77SSam Leffler * Max 802.11 overhead. This assumes no 4-address frames and 570210ad9a77SSam Leffler * the encapsulation done by ieee80211_encap (llc). We also 570310ad9a77SSam Leffler * include potential crypto overhead. 570410ad9a77SSam Leffler */ 570510ad9a77SSam Leffler #define IEEE80211_MAXOVERHEAD \ 570610ad9a77SSam Leffler (sizeof(struct ieee80211_qosframe) \ 570710ad9a77SSam Leffler + sizeof(struct llc) \ 570810ad9a77SSam Leffler + IEEE80211_ADDR_LEN \ 570910ad9a77SSam Leffler + IEEE80211_WEP_IVLEN \ 571010ad9a77SSam Leffler + IEEE80211_WEP_KIDLEN \ 571110ad9a77SSam Leffler + IEEE80211_WEP_CRCLEN \ 571210ad9a77SSam Leffler + IEEE80211_WEP_MICLEN \ 571310ad9a77SSam Leffler + IEEE80211_CRC_LEN) 571410ad9a77SSam Leffler 571510ad9a77SSam Leffler /* 571610ad9a77SSam Leffler * Setup initially for tdma operation. Start the beacon 571710ad9a77SSam Leffler * timers and enable SWBA if we are slot 0. Otherwise 571810ad9a77SSam Leffler * we wait for slot 0 to arrive so we can sync up before 571910ad9a77SSam Leffler * starting to transmit. 572010ad9a77SSam Leffler */ 572110ad9a77SSam Leffler static void 572210ad9a77SSam Leffler ath_tdma_config(struct ath_softc *sc, struct ieee80211vap *vap) 572310ad9a77SSam Leffler { 572410ad9a77SSam Leffler struct ath_hal *ah = sc->sc_ah; 572510ad9a77SSam Leffler struct ifnet *ifp = sc->sc_ifp; 572610ad9a77SSam Leffler struct ieee80211com *ic = ifp->if_l2com; 572710ad9a77SSam Leffler const struct ieee80211_txparam *tp; 572810ad9a77SSam Leffler const struct ieee80211_tdma_state *tdma = NULL; 572910ad9a77SSam Leffler int rix; 573010ad9a77SSam Leffler 573110ad9a77SSam Leffler if (vap == NULL) { 573210ad9a77SSam Leffler vap = TAILQ_FIRST(&ic->ic_vaps); /* XXX */ 573310ad9a77SSam Leffler if (vap == NULL) { 573410ad9a77SSam Leffler if_printf(ifp, "%s: no vaps?\n", __func__); 573510ad9a77SSam Leffler return; 573610ad9a77SSam Leffler } 573710ad9a77SSam Leffler } 573810ad9a77SSam Leffler tp = vap->iv_bss->ni_txparms; 573910ad9a77SSam Leffler /* 574010ad9a77SSam Leffler * Calculate the guard time for each slot. This is the 574110ad9a77SSam Leffler * time to send a maximal-size frame according to the 574210ad9a77SSam Leffler * fixed/lowest transmit rate. Note that the interface 574310ad9a77SSam Leffler * mtu does not include the 802.11 overhead so we must 574410ad9a77SSam Leffler * tack that on (ath_hal_computetxtime includes the 574510ad9a77SSam Leffler * preamble and plcp in it's calculation). 574610ad9a77SSam Leffler */ 574710ad9a77SSam Leffler tdma = vap->iv_tdma; 574810ad9a77SSam Leffler if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) 5749ab06fdf2SSam Leffler rix = ath_tx_findrix(sc, tp->ucastrate); 575010ad9a77SSam Leffler else 5751ab06fdf2SSam Leffler rix = ath_tx_findrix(sc, tp->mcastrate); 575210ad9a77SSam Leffler /* XXX short preamble assumed */ 575310ad9a77SSam Leffler sc->sc_tdmaguard = ath_hal_computetxtime(ah, sc->sc_currates, 575410ad9a77SSam Leffler ifp->if_mtu + IEEE80211_MAXOVERHEAD, rix, AH_TRUE); 575510ad9a77SSam Leffler 575610ad9a77SSam Leffler ath_hal_intrset(ah, 0); 575710ad9a77SSam Leffler 575810ad9a77SSam Leffler ath_beaconq_config(sc); /* setup h/w beacon q */ 57599c859a04SSam Leffler if (sc->sc_setcca) 576010ad9a77SSam Leffler ath_hal_setcca(ah, AH_FALSE); /* disable CCA */ 576110ad9a77SSam Leffler ath_tdma_bintvalsetup(sc, tdma); /* calculate beacon interval */ 576210ad9a77SSam Leffler ath_tdma_settimers(sc, sc->sc_tdmabintval, 576310ad9a77SSam Leffler sc->sc_tdmabintval | HAL_BEACON_RESET_TSF); 576410ad9a77SSam Leffler sc->sc_syncbeacon = 0; 576510ad9a77SSam Leffler 576610ad9a77SSam Leffler sc->sc_avgtsfdeltap = TDMA_DUMMY_MARKER; 576710ad9a77SSam Leffler sc->sc_avgtsfdeltam = TDMA_DUMMY_MARKER; 576810ad9a77SSam Leffler 576910ad9a77SSam Leffler ath_hal_intrset(ah, sc->sc_imask); 577010ad9a77SSam Leffler 577110ad9a77SSam Leffler DPRINTF(sc, ATH_DEBUG_TDMA, "%s: slot %u len %uus cnt %u " 577210ad9a77SSam Leffler "bsched %u guard %uus bintval %u TU dba prep %u\n", __func__, 577310ad9a77SSam Leffler tdma->tdma_slot, tdma->tdma_slotlen, tdma->tdma_slotcnt, 577410ad9a77SSam Leffler tdma->tdma_bintval, sc->sc_tdmaguard, sc->sc_tdmabintval, 577510ad9a77SSam Leffler sc->sc_tdmadbaprep); 577610ad9a77SSam Leffler } 577710ad9a77SSam Leffler 577810ad9a77SSam Leffler /* 577910ad9a77SSam Leffler * Update tdma operation. Called from the 802.11 layer 578010ad9a77SSam Leffler * when a beacon is received from the TDMA station operating 578110ad9a77SSam Leffler * in the slot immediately preceding us in the bss. Use 578210ad9a77SSam Leffler * the rx timestamp for the beacon frame to update our 578310ad9a77SSam Leffler * beacon timers so we follow their schedule. Note that 578410ad9a77SSam Leffler * by using the rx timestamp we implicitly include the 578510ad9a77SSam Leffler * propagation delay in our schedule. 578610ad9a77SSam Leffler */ 578710ad9a77SSam Leffler static void 578810ad9a77SSam Leffler ath_tdma_update(struct ieee80211_node *ni, 57892bc3ce77SSam Leffler const struct ieee80211_tdma_param *tdma, int changed) 579010ad9a77SSam Leffler { 579110ad9a77SSam Leffler #define TSF_TO_TU(_h,_l) \ 579210ad9a77SSam Leffler ((((u_int32_t)(_h)) << 22) | (((u_int32_t)(_l)) >> 10)) 579310ad9a77SSam Leffler #define TU_TO_TSF(_tu) (((u_int64_t)(_tu)) << 10) 579410ad9a77SSam Leffler struct ieee80211vap *vap = ni->ni_vap; 579510ad9a77SSam Leffler struct ieee80211com *ic = ni->ni_ic; 579610ad9a77SSam Leffler struct ath_softc *sc = ic->ic_ifp->if_softc; 579710ad9a77SSam Leffler struct ath_hal *ah = sc->sc_ah; 579810ad9a77SSam Leffler const HAL_RATE_TABLE *rt = sc->sc_currates; 5799fc4de9b7SAdrian Chadd u_int64_t tsf, rstamp, nextslot, nexttbtt; 5800fc4de9b7SAdrian Chadd u_int32_t txtime, nextslottu; 580110ad9a77SSam Leffler int32_t tudelta, tsfdelta; 580210ad9a77SSam Leffler const struct ath_rx_status *rs; 580310ad9a77SSam Leffler int rix; 580410ad9a77SSam Leffler 580510ad9a77SSam Leffler sc->sc_stats.ast_tdma_update++; 580610ad9a77SSam Leffler 580710ad9a77SSam Leffler /* 580810ad9a77SSam Leffler * Check for and adopt configuration changes. 580910ad9a77SSam Leffler */ 58102bc3ce77SSam Leffler if (changed != 0) { 581110ad9a77SSam Leffler const struct ieee80211_tdma_state *ts = vap->iv_tdma; 581210ad9a77SSam Leffler 581310ad9a77SSam Leffler ath_tdma_bintvalsetup(sc, ts); 5814040972a1SSam Leffler if (changed & TDMA_UPDATE_SLOTLEN) 5815040972a1SSam Leffler ath_wme_update(ic); 581610ad9a77SSam Leffler 581710ad9a77SSam Leffler DPRINTF(sc, ATH_DEBUG_TDMA, 581810ad9a77SSam Leffler "%s: adopt slot %u slotcnt %u slotlen %u us " 581910ad9a77SSam Leffler "bintval %u TU\n", __func__, 582010ad9a77SSam Leffler ts->tdma_slot, ts->tdma_slotcnt, ts->tdma_slotlen, 582110ad9a77SSam Leffler sc->sc_tdmabintval); 582210ad9a77SSam Leffler 582310ad9a77SSam Leffler /* XXX right? */ 582410ad9a77SSam Leffler ath_hal_intrset(ah, sc->sc_imask); 582510ad9a77SSam Leffler /* NB: beacon timers programmed below */ 582610ad9a77SSam Leffler } 582710ad9a77SSam Leffler 582810ad9a77SSam Leffler /* extend rx timestamp to 64 bits */ 58295463c4a4SSam Leffler rs = sc->sc_lastrs; 583010ad9a77SSam Leffler tsf = ath_hal_gettsf64(ah); 5831fc4de9b7SAdrian Chadd rstamp = ath_extend_tsf(sc, rs->rs_tstamp, tsf); 583210ad9a77SSam Leffler /* 583310ad9a77SSam Leffler * The rx timestamp is set by the hardware on completing 583410ad9a77SSam Leffler * reception (at the point where the rx descriptor is DMA'd 583510ad9a77SSam Leffler * to the host). To find the start of our next slot we 583610ad9a77SSam Leffler * must adjust this time by the time required to send 583710ad9a77SSam Leffler * the packet just received. 583810ad9a77SSam Leffler */ 583910ad9a77SSam Leffler rix = rt->rateCodeToIndex[rs->rs_rate]; 584010ad9a77SSam Leffler txtime = ath_hal_computetxtime(ah, rt, rs->rs_datalen, rix, 584110ad9a77SSam Leffler rt->info[rix].shortPreamble); 584210ad9a77SSam Leffler /* NB: << 9 is to cvt to TU and /2 */ 584310ad9a77SSam Leffler nextslot = (rstamp - txtime) + (sc->sc_tdmabintval << 9); 584410ad9a77SSam Leffler nextslottu = TSF_TO_TU(nextslot>>32, nextslot) & HAL_BEACON_PERIOD; 584510ad9a77SSam Leffler 584610ad9a77SSam Leffler /* 5847fc4de9b7SAdrian Chadd * Retrieve the hardware NextTBTT in usecs 5848fc4de9b7SAdrian Chadd * and calculate the difference between what the 584910ad9a77SSam Leffler * other station thinks and what we have programmed. This 585010ad9a77SSam Leffler * lets us figure how to adjust our timers to match. The 585110ad9a77SSam Leffler * adjustments are done by pulling the TSF forward and possibly 585210ad9a77SSam Leffler * rewriting the beacon timers. 585310ad9a77SSam Leffler */ 5854fc4de9b7SAdrian Chadd nexttbtt = ath_hal_getnexttbtt(ah); 5855fc4de9b7SAdrian Chadd tsfdelta = (int32_t)((nextslot % TU_TO_TSF(HAL_BEACON_PERIOD + 1)) - nexttbtt); 585610ad9a77SSam Leffler 585710ad9a77SSam Leffler DPRINTF(sc, ATH_DEBUG_TDMA_TIMER, 585810ad9a77SSam Leffler "tsfdelta %d avg +%d/-%d\n", tsfdelta, 585910ad9a77SSam Leffler TDMA_AVG(sc->sc_avgtsfdeltap), TDMA_AVG(sc->sc_avgtsfdeltam)); 586010ad9a77SSam Leffler 586110ad9a77SSam Leffler if (tsfdelta < 0) { 586210ad9a77SSam Leffler TDMA_SAMPLE(sc->sc_avgtsfdeltap, 0); 586310ad9a77SSam Leffler TDMA_SAMPLE(sc->sc_avgtsfdeltam, -tsfdelta); 586410ad9a77SSam Leffler tsfdelta = -tsfdelta % 1024; 586510ad9a77SSam Leffler nextslottu++; 586610ad9a77SSam Leffler } else if (tsfdelta > 0) { 586710ad9a77SSam Leffler TDMA_SAMPLE(sc->sc_avgtsfdeltap, tsfdelta); 586810ad9a77SSam Leffler TDMA_SAMPLE(sc->sc_avgtsfdeltam, 0); 586910ad9a77SSam Leffler tsfdelta = 1024 - (tsfdelta % 1024); 587010ad9a77SSam Leffler nextslottu++; 587110ad9a77SSam Leffler } else { 587210ad9a77SSam Leffler TDMA_SAMPLE(sc->sc_avgtsfdeltap, 0); 587310ad9a77SSam Leffler TDMA_SAMPLE(sc->sc_avgtsfdeltam, 0); 587410ad9a77SSam Leffler } 5875fc4de9b7SAdrian Chadd tudelta = nextslottu - TSF_TO_TU(nexttbtt >> 32, nexttbtt); 587610ad9a77SSam Leffler 587710ad9a77SSam Leffler /* 587810ad9a77SSam Leffler * Copy sender's timetstamp into tdma ie so they can 587910ad9a77SSam Leffler * calculate roundtrip time. We submit a beacon frame 588010ad9a77SSam Leffler * below after any timer adjustment. The frame goes out 588110ad9a77SSam Leffler * at the next TBTT so the sender can calculate the 588210ad9a77SSam Leffler * roundtrip by inspecting the tdma ie in our beacon frame. 588310ad9a77SSam Leffler * 588410ad9a77SSam Leffler * NB: This tstamp is subtlely preserved when 588510ad9a77SSam Leffler * IEEE80211_BEACON_TDMA is marked (e.g. when the 588610ad9a77SSam Leffler * slot position changes) because ieee80211_add_tdma 588710ad9a77SSam Leffler * skips over the data. 588810ad9a77SSam Leffler */ 588910ad9a77SSam Leffler memcpy(ATH_VAP(vap)->av_boff.bo_tdma + 589010ad9a77SSam Leffler __offsetof(struct ieee80211_tdma_param, tdma_tstamp), 589110ad9a77SSam Leffler &ni->ni_tstamp.data, 8); 589210ad9a77SSam Leffler #if 0 589310ad9a77SSam Leffler DPRINTF(sc, ATH_DEBUG_TDMA_TIMER, 5894fc4de9b7SAdrian Chadd "tsf %llu nextslot %llu (%d, %d) nextslottu %u nexttbtt %llu (%d)\n", 589510ad9a77SSam Leffler (unsigned long long) tsf, (unsigned long long) nextslot, 5896fc4de9b7SAdrian Chadd (int)(nextslot - tsf), tsfdelta, nextslottu, nexttbtt, tudelta); 589710ad9a77SSam Leffler #endif 589810ad9a77SSam Leffler /* 589910ad9a77SSam Leffler * Adjust the beacon timers only when pulling them forward 590010ad9a77SSam Leffler * or when going back by less than the beacon interval. 590110ad9a77SSam Leffler * Negative jumps larger than the beacon interval seem to 590210ad9a77SSam Leffler * cause the timers to stop and generally cause instability. 590310ad9a77SSam Leffler * This basically filters out jumps due to missed beacons. 590410ad9a77SSam Leffler */ 590510ad9a77SSam Leffler if (tudelta != 0 && (tudelta > 0 || -tudelta < sc->sc_tdmabintval)) { 590610ad9a77SSam Leffler ath_tdma_settimers(sc, nextslottu, sc->sc_tdmabintval); 590710ad9a77SSam Leffler sc->sc_stats.ast_tdma_timers++; 590810ad9a77SSam Leffler } 590910ad9a77SSam Leffler if (tsfdelta > 0) { 591010ad9a77SSam Leffler ath_hal_adjusttsf(ah, tsfdelta); 591110ad9a77SSam Leffler sc->sc_stats.ast_tdma_tsf++; 591210ad9a77SSam Leffler } 591310ad9a77SSam Leffler ath_tdma_beacon_send(sc, vap); /* prepare response */ 591410ad9a77SSam Leffler #undef TU_TO_TSF 591510ad9a77SSam Leffler #undef TSF_TO_TU 591610ad9a77SSam Leffler } 591710ad9a77SSam Leffler 591810ad9a77SSam Leffler /* 591910ad9a77SSam Leffler * Transmit a beacon frame at SWBA. Dynamic updates 592010ad9a77SSam Leffler * to the frame contents are done as needed. 592110ad9a77SSam Leffler */ 592210ad9a77SSam Leffler static void 592310ad9a77SSam Leffler ath_tdma_beacon_send(struct ath_softc *sc, struct ieee80211vap *vap) 592410ad9a77SSam Leffler { 592510ad9a77SSam Leffler struct ath_hal *ah = sc->sc_ah; 592610ad9a77SSam Leffler struct ath_buf *bf; 592710ad9a77SSam Leffler int otherant; 592810ad9a77SSam Leffler 592910ad9a77SSam Leffler /* 593010ad9a77SSam Leffler * Check if the previous beacon has gone out. If 593110ad9a77SSam Leffler * not don't try to post another, skip this period 593210ad9a77SSam Leffler * and wait for the next. Missed beacons indicate 593310ad9a77SSam Leffler * a problem and should not occur. If we miss too 593410ad9a77SSam Leffler * many consecutive beacons reset the device. 593510ad9a77SSam Leffler */ 593610ad9a77SSam Leffler if (ath_hal_numtxpending(ah, sc->sc_bhalq) != 0) { 593710ad9a77SSam Leffler sc->sc_bmisscount++; 593810ad9a77SSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON, 593910ad9a77SSam Leffler "%s: missed %u consecutive beacons\n", 594010ad9a77SSam Leffler __func__, sc->sc_bmisscount); 5941a32ac9d3SSam Leffler if (sc->sc_bmisscount >= ath_bstuck_threshold) 594210ad9a77SSam Leffler taskqueue_enqueue(sc->sc_tq, &sc->sc_bstucktask); 594310ad9a77SSam Leffler return; 594410ad9a77SSam Leffler } 594510ad9a77SSam Leffler if (sc->sc_bmisscount != 0) { 594610ad9a77SSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON, 594710ad9a77SSam Leffler "%s: resume beacon xmit after %u misses\n", 594810ad9a77SSam Leffler __func__, sc->sc_bmisscount); 594910ad9a77SSam Leffler sc->sc_bmisscount = 0; 595010ad9a77SSam Leffler } 595110ad9a77SSam Leffler 595210ad9a77SSam Leffler /* 595310ad9a77SSam Leffler * Check recent per-antenna transmit statistics and flip 595410ad9a77SSam Leffler * the default antenna if noticeably more frames went out 595510ad9a77SSam Leffler * on the non-default antenna. 595610ad9a77SSam Leffler * XXX assumes 2 anntenae 595710ad9a77SSam Leffler */ 595810ad9a77SSam Leffler if (!sc->sc_diversity) { 595910ad9a77SSam Leffler otherant = sc->sc_defant & 1 ? 2 : 1; 596010ad9a77SSam Leffler if (sc->sc_ant_tx[otherant] > sc->sc_ant_tx[sc->sc_defant] + 2) 596110ad9a77SSam Leffler ath_setdefantenna(sc, otherant); 596210ad9a77SSam Leffler sc->sc_ant_tx[1] = sc->sc_ant_tx[2] = 0; 596310ad9a77SSam Leffler } 596410ad9a77SSam Leffler 596510ad9a77SSam Leffler bf = ath_beacon_generate(sc, vap); 596610ad9a77SSam Leffler if (bf != NULL) { 596710ad9a77SSam Leffler /* 596810ad9a77SSam Leffler * Stop any current dma and put the new frame on the queue. 596910ad9a77SSam Leffler * This should never fail since we check above that no frames 597010ad9a77SSam Leffler * are still pending on the queue. 597110ad9a77SSam Leffler */ 597210ad9a77SSam Leffler if (!ath_hal_stoptxdma(ah, sc->sc_bhalq)) { 597310ad9a77SSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, 597410ad9a77SSam Leffler "%s: beacon queue %u did not stop?\n", 597510ad9a77SSam Leffler __func__, sc->sc_bhalq); 597610ad9a77SSam Leffler /* NB: the HAL still stops DMA, so proceed */ 597710ad9a77SSam Leffler } 597810ad9a77SSam Leffler ath_hal_puttxbuf(ah, sc->sc_bhalq, bf->bf_daddr); 597910ad9a77SSam Leffler ath_hal_txstart(ah, sc->sc_bhalq); 598010ad9a77SSam Leffler 598110ad9a77SSam Leffler sc->sc_stats.ast_be_xmit++; /* XXX per-vap? */ 598210ad9a77SSam Leffler 598310ad9a77SSam Leffler /* 598410ad9a77SSam Leffler * Record local TSF for our last send for use 598510ad9a77SSam Leffler * in arbitrating slot collisions. 598610ad9a77SSam Leffler */ 598710ad9a77SSam Leffler vap->iv_bss->ni_tstamp.tsf = ath_hal_gettsf64(ah); 598810ad9a77SSam Leffler } 598910ad9a77SSam Leffler } 5990584f7327SSam Leffler #endif /* IEEE80211_SUPPORT_TDMA */ 5991e8dabfbeSAdrian Chadd 599248237774SAdrian Chadd static void 599348237774SAdrian Chadd ath_dfs_tasklet(void *p, int npending) 599448237774SAdrian Chadd { 599548237774SAdrian Chadd struct ath_softc *sc = (struct ath_softc *) p; 599648237774SAdrian Chadd struct ifnet *ifp = sc->sc_ifp; 599748237774SAdrian Chadd struct ieee80211com *ic = ifp->if_l2com; 599848237774SAdrian Chadd 599948237774SAdrian Chadd /* 600048237774SAdrian Chadd * If previous processing has found a radar event, 600148237774SAdrian Chadd * signal this to the net80211 layer to begin DFS 600248237774SAdrian Chadd * processing. 600348237774SAdrian Chadd */ 600448237774SAdrian Chadd if (ath_dfs_process_radar_event(sc, sc->sc_curchan)) { 600548237774SAdrian Chadd /* DFS event found, initiate channel change */ 600648237774SAdrian Chadd ieee80211_dfs_notify_radar(ic, sc->sc_curchan); 600748237774SAdrian Chadd } 600848237774SAdrian Chadd } 600948237774SAdrian Chadd 6010dba9c859SAdrian Chadd MODULE_VERSION(if_ath, 1); 6011dba9c859SAdrian Chadd MODULE_DEPEND(if_ath, wlan, 1, 1, 1); /* 802.11 media layer */ 6012