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" 425591b213SSam Leffler 435591b213SSam Leffler #include <sys/param.h> 445591b213SSam Leffler #include <sys/systm.h> 455591b213SSam Leffler #include <sys/sysctl.h> 465591b213SSam Leffler #include <sys/mbuf.h> 475591b213SSam Leffler #include <sys/malloc.h> 485591b213SSam Leffler #include <sys/lock.h> 495591b213SSam Leffler #include <sys/mutex.h> 505591b213SSam Leffler #include <sys/kernel.h> 515591b213SSam Leffler #include <sys/socket.h> 525591b213SSam Leffler #include <sys/sockio.h> 535591b213SSam Leffler #include <sys/errno.h> 545591b213SSam Leffler #include <sys/callout.h> 555591b213SSam Leffler #include <sys/bus.h> 565591b213SSam Leffler #include <sys/endian.h> 570bbf5441SSam Leffler #include <sys/kthread.h> 580bbf5441SSam Leffler #include <sys/taskqueue.h> 595591b213SSam Leffler 605591b213SSam Leffler #include <machine/bus.h> 615591b213SSam Leffler 625591b213SSam Leffler #include <net/if.h> 635591b213SSam Leffler #include <net/if_dl.h> 645591b213SSam Leffler #include <net/if_media.h> 65fc74a9f9SBrooks Davis #include <net/if_types.h> 665591b213SSam Leffler #include <net/if_arp.h> 675591b213SSam Leffler #include <net/ethernet.h> 685591b213SSam Leffler #include <net/if_llc.h> 695591b213SSam Leffler 705591b213SSam Leffler #include <net80211/ieee80211_var.h> 7159efa8b5SSam Leffler #include <net80211/ieee80211_regdomain.h> 7210ad9a77SSam Leffler #ifdef ATH_SUPPORT_TDMA 7310ad9a77SSam Leffler #include <net80211/ieee80211_tdma.h> 7410ad9a77SSam Leffler #endif 755591b213SSam Leffler 765591b213SSam Leffler #include <net/bpf.h> 775591b213SSam Leffler 785591b213SSam Leffler #ifdef INET 795591b213SSam Leffler #include <netinet/in.h> 805591b213SSam Leffler #include <netinet/if_ether.h> 815591b213SSam Leffler #endif 825591b213SSam Leffler 835591b213SSam Leffler #include <dev/ath/if_athvar.h> 8433644623SSam Leffler #include <dev/ath/ath_hal/ah_devid.h> /* XXX for softled */ 855591b213SSam Leffler 8686e07743SSam Leffler #ifdef ATH_TX99_DIAG 8786e07743SSam Leffler #include <dev/ath/ath_tx99/ath_tx99.h> 8886e07743SSam Leffler #endif 8986e07743SSam Leffler 90b032f27cSSam Leffler /* 9172d9df0aSSam Leffler * We require a HAL w/ the changes for split tx/rx MIC. 9272d9df0aSSam Leffler */ 9372d9df0aSSam Leffler CTASSERT(HAL_ABI_VERSION > 0x06052200); 9472d9df0aSSam Leffler 9572d9df0aSSam Leffler /* 96b032f27cSSam Leffler * ATH_BCBUF determines the number of vap's that can transmit 97b032f27cSSam Leffler * beacons and also (currently) the number of vap's that can 98b032f27cSSam Leffler * have unique mac addresses/bssid. When staggering beacons 99b032f27cSSam Leffler * 4 is probably a good max as otherwise the beacons become 100b032f27cSSam Leffler * very closely spaced and there is limited time for cab q traffic 101b032f27cSSam Leffler * to go out. You can burst beacons instead but that is not good 102b032f27cSSam Leffler * for stations in power save and at some point you really want 103b032f27cSSam Leffler * another radio (and channel). 104b032f27cSSam Leffler * 105b032f27cSSam Leffler * The limit on the number of mac addresses is tied to our use of 106b032f27cSSam Leffler * the U/L bit and tracking addresses in a byte; it would be 107b032f27cSSam Leffler * worthwhile to allow more for applications like proxy sta. 108b032f27cSSam Leffler */ 109b032f27cSSam Leffler CTASSERT(ATH_BCBUF <= 8); 110b032f27cSSam Leffler 111e8fd88a3SSam Leffler /* unaligned little endian access */ 1125591b213SSam Leffler #define LE_READ_2(p) \ 1135591b213SSam Leffler ((u_int16_t) \ 1145591b213SSam Leffler ((((u_int8_t *)(p))[0] ) | (((u_int8_t *)(p))[1] << 8))) 1155591b213SSam Leffler #define LE_READ_4(p) \ 1165591b213SSam Leffler ((u_int32_t) \ 1175591b213SSam Leffler ((((u_int8_t *)(p))[0] ) | (((u_int8_t *)(p))[1] << 8) | \ 1185591b213SSam Leffler (((u_int8_t *)(p))[2] << 16) | (((u_int8_t *)(p))[3] << 24))) 1195591b213SSam Leffler 120b032f27cSSam Leffler static struct ieee80211vap *ath_vap_create(struct ieee80211com *, 121b032f27cSSam Leffler const char name[IFNAMSIZ], int unit, int opmode, 122b032f27cSSam Leffler int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], 123b032f27cSSam Leffler const uint8_t mac[IEEE80211_ADDR_LEN]); 124b032f27cSSam Leffler static void ath_vap_delete(struct ieee80211vap *); 1255591b213SSam Leffler static void ath_init(void *); 126c42a7b7eSSam Leffler static void ath_stop_locked(struct ifnet *); 1275591b213SSam Leffler static void ath_stop(struct ifnet *); 1285591b213SSam Leffler static void ath_start(struct ifnet *); 129c42a7b7eSSam Leffler static int ath_reset(struct ifnet *); 130b032f27cSSam Leffler static int ath_reset_vap(struct ieee80211vap *, u_long); 1315591b213SSam Leffler static int ath_media_change(struct ifnet *); 1325591b213SSam Leffler static void ath_watchdog(struct ifnet *); 1335591b213SSam Leffler static int ath_ioctl(struct ifnet *, u_long, caddr_t); 1345591b213SSam Leffler static void ath_fatal_proc(void *, int); 135b032f27cSSam Leffler static void ath_bmiss_vap(struct ieee80211vap *); 1365591b213SSam Leffler static void ath_bmiss_proc(void *, int); 137d3ac945bSSam Leffler static int ath_keyset(struct ath_softc *, const struct ieee80211_key *, 138d3ac945bSSam Leffler struct ieee80211_node *); 139b032f27cSSam Leffler static int ath_key_alloc(struct ieee80211vap *, 140e6e547d5SSam Leffler struct ieee80211_key *, 141c1225b52SSam Leffler ieee80211_keyix *, ieee80211_keyix *); 142b032f27cSSam Leffler static int ath_key_delete(struct ieee80211vap *, 143c42a7b7eSSam Leffler const struct ieee80211_key *); 144b032f27cSSam Leffler static int ath_key_set(struct ieee80211vap *, const struct ieee80211_key *, 145c42a7b7eSSam Leffler const u_int8_t mac[IEEE80211_ADDR_LEN]); 146b032f27cSSam Leffler static void ath_key_update_begin(struct ieee80211vap *); 147b032f27cSSam Leffler static void ath_key_update_end(struct ieee80211vap *); 148b032f27cSSam Leffler static void ath_update_mcast(struct ifnet *); 149b032f27cSSam Leffler static void ath_update_promisc(struct ifnet *); 1505591b213SSam Leffler static void ath_mode_init(struct ath_softc *); 151c42a7b7eSSam Leffler static void ath_setslottime(struct ath_softc *); 152c42a7b7eSSam Leffler static void ath_updateslot(struct ifnet *); 15380d2765fSSam Leffler static int ath_beaconq_setup(struct ath_hal *); 1545591b213SSam Leffler static int ath_beacon_alloc(struct ath_softc *, struct ieee80211_node *); 155b032f27cSSam Leffler static void ath_beacon_update(struct ieee80211vap *, int item); 156c42a7b7eSSam Leffler static void ath_beacon_setup(struct ath_softc *, struct ath_buf *); 1575591b213SSam Leffler static void ath_beacon_proc(void *, int); 158b032f27cSSam Leffler static struct ath_buf *ath_beacon_generate(struct ath_softc *, 159b032f27cSSam Leffler struct ieee80211vap *); 160c42a7b7eSSam Leffler static void ath_bstuck_proc(void *, int); 161b032f27cSSam Leffler static void ath_beacon_return(struct ath_softc *, struct ath_buf *); 1625591b213SSam Leffler static void ath_beacon_free(struct ath_softc *); 163b032f27cSSam Leffler static void ath_beacon_config(struct ath_softc *, struct ieee80211vap *); 164c42a7b7eSSam Leffler static void ath_descdma_cleanup(struct ath_softc *sc, 165c42a7b7eSSam Leffler struct ath_descdma *, ath_bufhead *); 1665591b213SSam Leffler static int ath_desc_alloc(struct ath_softc *); 1675591b213SSam Leffler static void ath_desc_free(struct ath_softc *); 16838c208f8SSam Leffler static struct ieee80211_node *ath_node_alloc(struct ieee80211vap *, 16938c208f8SSam Leffler const uint8_t [IEEE80211_ADDR_LEN]); 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, 17568e8e04eSSam Leffler int subtype, int rssi, int noise, u_int32_t rstamp); 176c42a7b7eSSam Leffler static void ath_setdefantenna(struct ath_softc *, u_int); 1775591b213SSam Leffler static void ath_rx_proc(void *, int); 178622b3fd2SSam Leffler static void ath_txq_init(struct ath_softc *sc, struct ath_txq *, int); 179c42a7b7eSSam Leffler static struct ath_txq *ath_txq_setup(struct ath_softc*, int qtype, int subtype); 180c42a7b7eSSam Leffler static int ath_tx_setup(struct ath_softc *, int, int); 181c42a7b7eSSam Leffler static int ath_wme_update(struct ieee80211com *); 182c42a7b7eSSam Leffler static void ath_tx_cleanupq(struct ath_softc *, struct ath_txq *); 183c42a7b7eSSam Leffler static void ath_tx_cleanup(struct ath_softc *); 18468e8e04eSSam Leffler static void ath_freetx(struct mbuf *); 1855591b213SSam Leffler static int ath_tx_start(struct ath_softc *, struct ieee80211_node *, 1865591b213SSam Leffler struct ath_buf *, struct mbuf *); 187c42a7b7eSSam Leffler static void ath_tx_proc_q0(void *, int); 188c42a7b7eSSam Leffler static void ath_tx_proc_q0123(void *, int); 1895591b213SSam Leffler static void ath_tx_proc(void *, int); 190b032f27cSSam Leffler static void ath_tx_draintxq(struct ath_softc *, struct ath_txq *); 1915591b213SSam Leffler static int ath_chan_set(struct ath_softc *, struct ieee80211_channel *); 1925591b213SSam Leffler static void ath_draintxq(struct ath_softc *); 1935591b213SSam Leffler static void ath_stoprecv(struct ath_softc *); 1945591b213SSam Leffler static int ath_startrecv(struct ath_softc *); 195c42a7b7eSSam Leffler static void ath_chan_change(struct ath_softc *, struct ieee80211_channel *); 19668e8e04eSSam Leffler static void ath_scan_start(struct ieee80211com *); 19768e8e04eSSam Leffler static void ath_scan_end(struct ieee80211com *); 19868e8e04eSSam Leffler static void ath_set_channel(struct ieee80211com *); 1995591b213SSam Leffler static void ath_calibrate(void *); 200b032f27cSSam Leffler static int ath_newstate(struct ieee80211vap *, enum ieee80211_state, int); 201e8fd88a3SSam Leffler static void ath_setup_stationkey(struct ieee80211_node *); 202e9962332SSam Leffler static void ath_newassoc(struct ieee80211_node *, int); 203b032f27cSSam Leffler static int ath_setregdomain(struct ieee80211com *, 204b032f27cSSam Leffler struct ieee80211_regdomain *, int, 205b032f27cSSam Leffler struct ieee80211_channel []); 2065fe9f044SSam Leffler static void ath_getradiocaps(struct ieee80211com *, int, int *, 207b032f27cSSam Leffler struct ieee80211_channel []); 208b032f27cSSam Leffler static int ath_getchannels(struct ath_softc *); 2093e50ec2cSSam Leffler static void ath_led_event(struct ath_softc *, int); 2105591b213SSam Leffler 211c42a7b7eSSam Leffler static int ath_rate_setup(struct ath_softc *, u_int mode); 2125591b213SSam Leffler static void ath_setcurmode(struct ath_softc *, enum ieee80211_phymode); 213c42a7b7eSSam Leffler 214c42a7b7eSSam Leffler static void ath_sysctlattach(struct ath_softc *); 215664443d0SSam Leffler static int ath_raw_xmit(struct ieee80211_node *, 216664443d0SSam Leffler struct mbuf *, const struct ieee80211_bpf_params *); 217c42a7b7eSSam Leffler static void ath_bpfattach(struct ath_softc *); 218c42a7b7eSSam Leffler static void ath_announce(struct ath_softc *); 2195591b213SSam Leffler 22010ad9a77SSam Leffler #ifdef ATH_SUPPORT_TDMA 22110ad9a77SSam Leffler static void ath_tdma_settimers(struct ath_softc *sc, u_int32_t nexttbtt, 22210ad9a77SSam Leffler u_int32_t bintval); 22310ad9a77SSam Leffler static void ath_tdma_bintvalsetup(struct ath_softc *sc, 22410ad9a77SSam Leffler const struct ieee80211_tdma_state *tdma); 22510ad9a77SSam Leffler static void ath_tdma_config(struct ath_softc *sc, struct ieee80211vap *vap); 22610ad9a77SSam Leffler static void ath_tdma_update(struct ieee80211_node *ni, 22710ad9a77SSam Leffler const struct ieee80211_tdma_param *tdma); 22810ad9a77SSam Leffler static void ath_tdma_beacon_send(struct ath_softc *sc, 22910ad9a77SSam Leffler struct ieee80211vap *vap); 23010ad9a77SSam Leffler 23110ad9a77SSam Leffler static __inline void 23210ad9a77SSam Leffler ath_hal_setcca(struct ath_hal *ah, int ena) 23310ad9a77SSam Leffler { 23410ad9a77SSam Leffler /* 23510ad9a77SSam Leffler * NB: fill me in; this is not provided by default because disabling 23610ad9a77SSam Leffler * CCA in most locales violates regulatory. 23710ad9a77SSam Leffler */ 23810ad9a77SSam Leffler } 23910ad9a77SSam Leffler 24010ad9a77SSam Leffler static __inline int 24110ad9a77SSam Leffler ath_hal_getcca(struct ath_hal *ah) 24210ad9a77SSam Leffler { 24310ad9a77SSam Leffler u_int32_t diag; 24410ad9a77SSam Leffler if (ath_hal_getcapability(ah, HAL_CAP_DIAG, 0, &diag) != HAL_OK) 24510ad9a77SSam Leffler return 1; 24610ad9a77SSam Leffler return ((diag & 0x500000) == 0); 24710ad9a77SSam Leffler } 24810ad9a77SSam Leffler 24910ad9a77SSam Leffler #define TDMA_EP_MULTIPLIER (1<<10) /* pow2 to optimize out * and / */ 25010ad9a77SSam Leffler #define TDMA_LPF_LEN 6 25110ad9a77SSam Leffler #define TDMA_DUMMY_MARKER 0x127 25210ad9a77SSam Leffler #define TDMA_EP_MUL(x, mul) ((x) * (mul)) 25310ad9a77SSam Leffler #define TDMA_IN(x) (TDMA_EP_MUL((x), TDMA_EP_MULTIPLIER)) 25410ad9a77SSam Leffler #define TDMA_LPF(x, y, len) \ 25510ad9a77SSam Leffler ((x != TDMA_DUMMY_MARKER) ? (((x) * ((len)-1) + (y)) / (len)) : (y)) 25610ad9a77SSam Leffler #define TDMA_SAMPLE(x, y) do { \ 25710ad9a77SSam Leffler x = TDMA_LPF((x), TDMA_IN(y), TDMA_LPF_LEN); \ 25810ad9a77SSam Leffler } while (0) 25910ad9a77SSam Leffler #define TDMA_EP_RND(x,mul) \ 26010ad9a77SSam Leffler ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul)) 26110ad9a77SSam Leffler #define TDMA_AVG(x) TDMA_EP_RND(x, TDMA_EP_MULTIPLIER) 26210ad9a77SSam Leffler #endif /* ATH_SUPPORT_TDMA */ 26310ad9a77SSam Leffler 2645591b213SSam Leffler SYSCTL_DECL(_hw_ath); 2655591b213SSam Leffler 2665591b213SSam Leffler /* XXX validate sysctl values */ 2672dc7fcc4SSam Leffler static int ath_longcalinterval = 30; /* long cals every 30 secs */ 2682dc7fcc4SSam Leffler SYSCTL_INT(_hw_ath, OID_AUTO, longcal, CTLFLAG_RW, &ath_longcalinterval, 2692dc7fcc4SSam Leffler 0, "long chip calibration interval (secs)"); 2702dc7fcc4SSam Leffler static int ath_shortcalinterval = 100; /* short cals every 100 ms */ 2712dc7fcc4SSam Leffler SYSCTL_INT(_hw_ath, OID_AUTO, shortcal, CTLFLAG_RW, &ath_shortcalinterval, 2722dc7fcc4SSam Leffler 0, "short chip calibration interval (msecs)"); 2732dc7fcc4SSam Leffler static int ath_resetcalinterval = 20*60; /* reset cal state 20 mins */ 2742dc7fcc4SSam Leffler SYSCTL_INT(_hw_ath, OID_AUTO, resetcal, CTLFLAG_RW, &ath_resetcalinterval, 2752dc7fcc4SSam Leffler 0, "reset chip calibration results (secs)"); 2765591b213SSam Leffler 277e2d787faSSam Leffler static int ath_rxbuf = ATH_RXBUF; /* # rx buffers to allocate */ 278aaa70f2fSSam Leffler SYSCTL_INT(_hw_ath, OID_AUTO, rxbuf, CTLFLAG_RW, &ath_rxbuf, 279e2d787faSSam Leffler 0, "rx buffers allocated"); 280e2d787faSSam Leffler TUNABLE_INT("hw.ath.rxbuf", &ath_rxbuf); 281e2d787faSSam Leffler static int ath_txbuf = ATH_TXBUF; /* # tx buffers to allocate */ 282aaa70f2fSSam Leffler SYSCTL_INT(_hw_ath, OID_AUTO, txbuf, CTLFLAG_RW, &ath_txbuf, 283e2d787faSSam Leffler 0, "tx buffers allocated"); 284e2d787faSSam Leffler TUNABLE_INT("hw.ath.txbuf", &ath_txbuf); 285e2d787faSSam Leffler 286a32ac9d3SSam Leffler static int ath_bstuck_threshold = 4; /* max missed beacons */ 287a32ac9d3SSam Leffler SYSCTL_INT(_hw_ath, OID_AUTO, bstuck, CTLFLAG_RW, &ath_bstuck_threshold, 288a32ac9d3SSam Leffler 0, "max missed beacon xmits before chip reset"); 289a32ac9d3SSam Leffler 290a585a9a1SSam Leffler #ifdef ATH_DEBUG 291e325e530SSam Leffler enum { 292e325e530SSam Leffler ATH_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ 293e325e530SSam Leffler ATH_DEBUG_XMIT_DESC = 0x00000002, /* xmit descriptors */ 294e325e530SSam Leffler ATH_DEBUG_RECV = 0x00000004, /* basic recv operation */ 295e325e530SSam Leffler ATH_DEBUG_RECV_DESC = 0x00000008, /* recv descriptors */ 296e325e530SSam Leffler ATH_DEBUG_RATE = 0x00000010, /* rate control */ 297e325e530SSam Leffler ATH_DEBUG_RESET = 0x00000020, /* reset processing */ 298e325e530SSam Leffler ATH_DEBUG_MODE = 0x00000040, /* mode init/setup */ 299e325e530SSam Leffler ATH_DEBUG_BEACON = 0x00000080, /* beacon handling */ 300e325e530SSam Leffler ATH_DEBUG_WATCHDOG = 0x00000100, /* watchdog timeout */ 301e325e530SSam Leffler ATH_DEBUG_INTR = 0x00001000, /* ISR */ 302e325e530SSam Leffler ATH_DEBUG_TX_PROC = 0x00002000, /* tx ISR proc */ 303e325e530SSam Leffler ATH_DEBUG_RX_PROC = 0x00004000, /* rx ISR proc */ 304e325e530SSam Leffler ATH_DEBUG_BEACON_PROC = 0x00008000, /* beacon ISR proc */ 305e325e530SSam Leffler ATH_DEBUG_CALIBRATE = 0x00010000, /* periodic calibration */ 306c42a7b7eSSam Leffler ATH_DEBUG_KEYCACHE = 0x00020000, /* key cache management */ 307c42a7b7eSSam Leffler ATH_DEBUG_STATE = 0x00040000, /* 802.11 state transitions */ 308c42a7b7eSSam Leffler ATH_DEBUG_NODE = 0x00080000, /* node management */ 3093e50ec2cSSam Leffler ATH_DEBUG_LED = 0x00100000, /* led management */ 310bd5a9920SSam Leffler ATH_DEBUG_FF = 0x00200000, /* fast frames */ 311bd5a9920SSam Leffler ATH_DEBUG_DFS = 0x00400000, /* DFS processing */ 31210ad9a77SSam Leffler ATH_DEBUG_TDMA = 0x00800000, /* TDMA processing */ 31310ad9a77SSam Leffler ATH_DEBUG_TDMA_TIMER = 0x01000000, /* TDMA timer processing */ 314033022a9SSam Leffler ATH_DEBUG_REGDOMAIN = 0x02000000, /* regulatory processing */ 315c42a7b7eSSam Leffler ATH_DEBUG_FATAL = 0x80000000, /* fatal errors */ 316e325e530SSam Leffler ATH_DEBUG_ANY = 0xffffffff 317e325e530SSam Leffler }; 3183297be13SSam Leffler static int ath_debug = 0; 3193297be13SSam Leffler SYSCTL_INT(_hw_ath, OID_AUTO, debug, CTLFLAG_RW, &ath_debug, 3203297be13SSam Leffler 0, "control debugging printfs"); 3213297be13SSam Leffler TUNABLE_INT("hw.ath.debug", &ath_debug); 3223297be13SSam Leffler 323c42a7b7eSSam Leffler #define IFF_DUMPPKTS(sc, m) \ 3240a1b94c4SSam Leffler ((sc->sc_debug & (m)) || \ 325fc74a9f9SBrooks Davis (sc->sc_ifp->if_flags & (IFF_DEBUG|IFF_LINK2)) == (IFF_DEBUG|IFF_LINK2)) 326c42a7b7eSSam Leffler #define DPRINTF(sc, m, fmt, ...) do { \ 3270a1b94c4SSam Leffler if (sc->sc_debug & (m)) \ 328c42a7b7eSSam Leffler printf(fmt, __VA_ARGS__); \ 329c42a7b7eSSam Leffler } while (0) 330c42a7b7eSSam Leffler #define KEYPRINTF(sc, ix, hk, mac) do { \ 331c42a7b7eSSam Leffler if (sc->sc_debug & ATH_DEBUG_KEYCACHE) \ 3325901d2d3SSam Leffler ath_keyprint(sc, __func__, ix, hk, mac); \ 333c42a7b7eSSam Leffler } while (0) 3346902009eSSam Leffler static void ath_printrxbuf(struct ath_softc *, const struct ath_buf *bf, 3356902009eSSam Leffler u_int ix, int); 3366902009eSSam Leffler static void ath_printtxbuf(struct ath_softc *, const struct ath_buf *bf, 3376902009eSSam Leffler u_int qnum, u_int ix, int done); 3385591b213SSam Leffler #else 339c42a7b7eSSam Leffler #define IFF_DUMPPKTS(sc, m) \ 340fc74a9f9SBrooks Davis ((sc->sc_ifp->if_flags & (IFF_DEBUG|IFF_LINK2)) == (IFF_DEBUG|IFF_LINK2)) 341d2f6ed15SSam Leffler #define DPRINTF(sc, m, fmt, ...) do { \ 342d2f6ed15SSam Leffler (void) sc; \ 343d2f6ed15SSam Leffler } while (0) 344d2f6ed15SSam Leffler #define KEYPRINTF(sc, k, ix, mac) do { \ 345d2f6ed15SSam Leffler (void) sc; \ 346d2f6ed15SSam Leffler } while (0) 3475591b213SSam Leffler #endif 3485591b213SSam Leffler 349c42a7b7eSSam Leffler MALLOC_DEFINE(M_ATHDEV, "athdev", "ath driver dma buffers"); 350c42a7b7eSSam Leffler 3515591b213SSam Leffler int 3525591b213SSam Leffler ath_attach(u_int16_t devid, struct ath_softc *sc) 3535591b213SSam Leffler { 354fc74a9f9SBrooks Davis struct ifnet *ifp; 355b032f27cSSam Leffler struct ieee80211com *ic; 356fc74a9f9SBrooks Davis struct ath_hal *ah = NULL; 3575591b213SSam Leffler HAL_STATUS status; 358c42a7b7eSSam Leffler int error = 0, i; 359411373ebSSam Leffler u_int wmodes; 3605591b213SSam Leffler 361c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: devid 0x%x\n", __func__, devid); 3625591b213SSam Leffler 363b032f27cSSam Leffler ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); 364fc74a9f9SBrooks Davis if (ifp == NULL) { 365fc74a9f9SBrooks Davis device_printf(sc->sc_dev, "can not if_alloc()\n"); 366fc74a9f9SBrooks Davis error = ENOSPC; 367fc74a9f9SBrooks Davis goto bad; 368fc74a9f9SBrooks Davis } 369b032f27cSSam Leffler ic = ifp->if_l2com; 370fc74a9f9SBrooks Davis 3715591b213SSam Leffler /* set these up early for if_printf use */ 3729bf40edeSBrooks Davis if_initname(ifp, device_get_name(sc->sc_dev), 3739bf40edeSBrooks Davis device_get_unit(sc->sc_dev)); 3745591b213SSam Leffler 375f9fc583fSSam Leffler ah = ath_hal_attach(devid, sc, sc->sc_st, sc->sc_sh, &status); 3765591b213SSam Leffler if (ah == NULL) { 3775591b213SSam Leffler if_printf(ifp, "unable to attach hardware; HAL status %u\n", 3785591b213SSam Leffler status); 3795591b213SSam Leffler error = ENXIO; 3805591b213SSam Leffler goto bad; 3815591b213SSam Leffler } 38285bdc65aSSam Leffler if (ah->ah_abi != HAL_ABI_VERSION) { 383c42a7b7eSSam Leffler if_printf(ifp, "HAL ABI mismatch detected " 384c42a7b7eSSam Leffler "(HAL:0x%x != driver:0x%x)\n", 38585bdc65aSSam Leffler ah->ah_abi, HAL_ABI_VERSION); 38685bdc65aSSam Leffler error = ENXIO; 38785bdc65aSSam Leffler goto bad; 38885bdc65aSSam Leffler } 3895591b213SSam Leffler sc->sc_ah = ah; 390b58b3803SSam Leffler sc->sc_invalid = 0; /* ready to go, enable interrupt handling */ 3913297be13SSam Leffler #ifdef ATH_DEBUG 3923297be13SSam Leffler sc->sc_debug = ath_debug; 3933297be13SSam Leffler #endif 3945591b213SSam Leffler 3955591b213SSam Leffler /* 396c42a7b7eSSam Leffler * Check if the MAC has multi-rate retry support. 397c42a7b7eSSam Leffler * We do this by trying to setup a fake extended 398c42a7b7eSSam Leffler * descriptor. MAC's that don't have support will 399c42a7b7eSSam Leffler * return false w/o doing anything. MAC's that do 400c42a7b7eSSam Leffler * support it will return true w/o doing anything. 401c42a7b7eSSam Leffler */ 402c42a7b7eSSam Leffler sc->sc_mrretry = ath_hal_setupxtxdesc(ah, NULL, 0,0, 0,0, 0,0); 403c42a7b7eSSam Leffler 404c42a7b7eSSam Leffler /* 405c42a7b7eSSam Leffler * Check if the device has hardware counters for PHY 406c42a7b7eSSam Leffler * errors. If so we need to enable the MIB interrupt 407c42a7b7eSSam Leffler * so we can act on stat triggers. 408c42a7b7eSSam Leffler */ 409c42a7b7eSSam Leffler if (ath_hal_hwphycounters(ah)) 410c42a7b7eSSam Leffler sc->sc_needmib = 1; 411c42a7b7eSSam Leffler 412c42a7b7eSSam Leffler /* 413c42a7b7eSSam Leffler * Get the hardware key cache size. 414c42a7b7eSSam Leffler */ 415c42a7b7eSSam Leffler sc->sc_keymax = ath_hal_keycachesize(ah); 416e8fd88a3SSam Leffler if (sc->sc_keymax > ATH_KEYMAX) { 417e8fd88a3SSam Leffler if_printf(ifp, "Warning, using only %u of %u key cache slots\n", 418e8fd88a3SSam Leffler ATH_KEYMAX, sc->sc_keymax); 419e8fd88a3SSam Leffler sc->sc_keymax = ATH_KEYMAX; 420c42a7b7eSSam Leffler } 421c42a7b7eSSam Leffler /* 422c42a7b7eSSam Leffler * Reset the key cache since some parts do not 423c42a7b7eSSam Leffler * reset the contents on initial power up. 424c42a7b7eSSam Leffler */ 425c42a7b7eSSam Leffler for (i = 0; i < sc->sc_keymax; i++) 426c42a7b7eSSam Leffler ath_hal_keyreset(ah, i); 427c42a7b7eSSam Leffler 428c42a7b7eSSam Leffler /* 429b032f27cSSam Leffler * Collect the default channel list. 4305591b213SSam Leffler */ 431b032f27cSSam Leffler error = ath_getchannels(sc); 4325591b213SSam Leffler if (error != 0) 4335591b213SSam Leffler goto bad; 4345591b213SSam Leffler 4355591b213SSam Leffler /* 4365591b213SSam Leffler * Setup rate tables for all potential media types. 4375591b213SSam Leffler */ 4385591b213SSam Leffler ath_rate_setup(sc, IEEE80211_MODE_11A); 4395591b213SSam Leffler ath_rate_setup(sc, IEEE80211_MODE_11B); 4405591b213SSam Leffler ath_rate_setup(sc, IEEE80211_MODE_11G); 441c42a7b7eSSam Leffler ath_rate_setup(sc, IEEE80211_MODE_TURBO_A); 442c42a7b7eSSam Leffler ath_rate_setup(sc, IEEE80211_MODE_TURBO_G); 44368e8e04eSSam Leffler ath_rate_setup(sc, IEEE80211_MODE_STURBO_A); 44468e8e04eSSam Leffler ath_rate_setup(sc, IEEE80211_MODE_11NA); 44568e8e04eSSam Leffler ath_rate_setup(sc, IEEE80211_MODE_11NG); 446724c193aSSam Leffler ath_rate_setup(sc, IEEE80211_MODE_HALF); 447724c193aSSam Leffler ath_rate_setup(sc, IEEE80211_MODE_QUARTER); 448aaa70f2fSSam Leffler 449c42a7b7eSSam Leffler /* NB: setup here so ath_rate_update is happy */ 450c42a7b7eSSam Leffler ath_setcurmode(sc, IEEE80211_MODE_11A); 4515591b213SSam Leffler 452c42a7b7eSSam Leffler /* 453c42a7b7eSSam Leffler * Allocate tx+rx descriptors and populate the lists. 454c42a7b7eSSam Leffler */ 4555591b213SSam Leffler error = ath_desc_alloc(sc); 4565591b213SSam Leffler if (error != 0) { 4575591b213SSam Leffler if_printf(ifp, "failed to allocate descriptors: %d\n", error); 4585591b213SSam Leffler goto bad; 4595591b213SSam Leffler } 4602274d8c8SSam Leffler callout_init(&sc->sc_cal_ch, CALLOUT_MPSAFE); 4615591b213SSam Leffler 462f0b2a0beSSam Leffler ATH_TXBUF_LOCK_INIT(sc); 4635591b213SSam Leffler 4640bbf5441SSam Leffler sc->sc_tq = taskqueue_create("ath_taskq", M_NOWAIT, 4650bbf5441SSam Leffler taskqueue_thread_enqueue, &sc->sc_tq); 4660bbf5441SSam Leffler taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, 4670bbf5441SSam Leffler "%s taskq", ifp->if_xname); 4680bbf5441SSam Leffler 4695591b213SSam Leffler TASK_INIT(&sc->sc_rxtask, 0, ath_rx_proc, sc); 4705591b213SSam Leffler TASK_INIT(&sc->sc_bmisstask, 0, ath_bmiss_proc, sc); 471c42a7b7eSSam Leffler TASK_INIT(&sc->sc_bstucktask,0, ath_bstuck_proc, sc); 4725591b213SSam Leffler 4735591b213SSam Leffler /* 474c42a7b7eSSam Leffler * Allocate hardware transmit queues: one queue for 475c42a7b7eSSam Leffler * beacon frames and one data queue for each QoS 476c42a7b7eSSam Leffler * priority. Note that the hal handles reseting 477c42a7b7eSSam Leffler * these queues at the needed time. 478c42a7b7eSSam Leffler * 479c42a7b7eSSam Leffler * XXX PS-Poll 4805591b213SSam Leffler */ 48180d2765fSSam Leffler sc->sc_bhalq = ath_beaconq_setup(ah); 4825591b213SSam Leffler if (sc->sc_bhalq == (u_int) -1) { 4835591b213SSam Leffler if_printf(ifp, "unable to setup a beacon xmit queue!\n"); 484c42a7b7eSSam Leffler error = EIO; 485b28b4653SSam Leffler goto bad2; 4865591b213SSam Leffler } 487c42a7b7eSSam Leffler sc->sc_cabq = ath_txq_setup(sc, HAL_TX_QUEUE_CAB, 0); 488c42a7b7eSSam Leffler if (sc->sc_cabq == NULL) { 489c42a7b7eSSam Leffler if_printf(ifp, "unable to setup CAB xmit queue!\n"); 490c42a7b7eSSam Leffler error = EIO; 491c42a7b7eSSam Leffler goto bad2; 492c42a7b7eSSam Leffler } 493c42a7b7eSSam Leffler /* NB: insure BK queue is the lowest priority h/w queue */ 494c42a7b7eSSam Leffler if (!ath_tx_setup(sc, WME_AC_BK, HAL_WME_AC_BK)) { 495c42a7b7eSSam Leffler if_printf(ifp, "unable to setup xmit queue for %s traffic!\n", 496c42a7b7eSSam Leffler ieee80211_wme_acnames[WME_AC_BK]); 497c42a7b7eSSam Leffler error = EIO; 498c42a7b7eSSam Leffler goto bad2; 499c42a7b7eSSam Leffler } 500c42a7b7eSSam Leffler if (!ath_tx_setup(sc, WME_AC_BE, HAL_WME_AC_BE) || 501c42a7b7eSSam Leffler !ath_tx_setup(sc, WME_AC_VI, HAL_WME_AC_VI) || 502c42a7b7eSSam Leffler !ath_tx_setup(sc, WME_AC_VO, HAL_WME_AC_VO)) { 503c42a7b7eSSam Leffler /* 504c42a7b7eSSam Leffler * Not enough hardware tx queues to properly do WME; 505c42a7b7eSSam Leffler * just punt and assign them all to the same h/w queue. 506c42a7b7eSSam Leffler * We could do a better job of this if, for example, 507c42a7b7eSSam Leffler * we allocate queues when we switch from station to 508c42a7b7eSSam Leffler * AP mode. 509c42a7b7eSSam Leffler */ 510c42a7b7eSSam Leffler if (sc->sc_ac2q[WME_AC_VI] != NULL) 511c42a7b7eSSam Leffler ath_tx_cleanupq(sc, sc->sc_ac2q[WME_AC_VI]); 512c42a7b7eSSam Leffler if (sc->sc_ac2q[WME_AC_BE] != NULL) 513c42a7b7eSSam Leffler ath_tx_cleanupq(sc, sc->sc_ac2q[WME_AC_BE]); 514c42a7b7eSSam Leffler sc->sc_ac2q[WME_AC_BE] = sc->sc_ac2q[WME_AC_BK]; 515c42a7b7eSSam Leffler sc->sc_ac2q[WME_AC_VI] = sc->sc_ac2q[WME_AC_BK]; 516c42a7b7eSSam Leffler sc->sc_ac2q[WME_AC_VO] = sc->sc_ac2q[WME_AC_BK]; 517c42a7b7eSSam Leffler } 518c42a7b7eSSam Leffler 519c42a7b7eSSam Leffler /* 520c42a7b7eSSam Leffler * Special case certain configurations. Note the 521c42a7b7eSSam Leffler * CAB queue is handled by these specially so don't 522c42a7b7eSSam Leffler * include them when checking the txq setup mask. 523c42a7b7eSSam Leffler */ 524c42a7b7eSSam Leffler switch (sc->sc_txqsetup &~ (1<<sc->sc_cabq->axq_qnum)) { 525c42a7b7eSSam Leffler case 0x01: 526c42a7b7eSSam Leffler TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc_q0, sc); 527c42a7b7eSSam Leffler break; 528c42a7b7eSSam Leffler case 0x0f: 529c42a7b7eSSam Leffler TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc_q0123, sc); 530c42a7b7eSSam Leffler break; 531c42a7b7eSSam Leffler default: 532c42a7b7eSSam Leffler TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc, sc); 533c42a7b7eSSam Leffler break; 534c42a7b7eSSam Leffler } 535c42a7b7eSSam Leffler 536c42a7b7eSSam Leffler /* 537c42a7b7eSSam Leffler * Setup rate control. Some rate control modules 538c42a7b7eSSam Leffler * call back to change the anntena state so expose 539c42a7b7eSSam Leffler * the necessary entry points. 540c42a7b7eSSam Leffler * XXX maybe belongs in struct ath_ratectrl? 541c42a7b7eSSam Leffler */ 542c42a7b7eSSam Leffler sc->sc_setdefantenna = ath_setdefantenna; 543c42a7b7eSSam Leffler sc->sc_rc = ath_rate_attach(sc); 544c42a7b7eSSam Leffler if (sc->sc_rc == NULL) { 545c42a7b7eSSam Leffler error = EIO; 546c42a7b7eSSam Leffler goto bad2; 547c42a7b7eSSam Leffler } 548c42a7b7eSSam Leffler 5493e50ec2cSSam Leffler sc->sc_blinking = 0; 550c42a7b7eSSam Leffler sc->sc_ledstate = 1; 5513e50ec2cSSam Leffler sc->sc_ledon = 0; /* low true */ 5523e50ec2cSSam Leffler sc->sc_ledidle = (2700*hz)/1000; /* 2.7sec */ 5533e50ec2cSSam Leffler callout_init(&sc->sc_ledtimer, CALLOUT_MPSAFE); 554c42a7b7eSSam Leffler /* 555c42a7b7eSSam Leffler * Auto-enable soft led processing for IBM cards and for 556c42a7b7eSSam Leffler * 5211 minipci cards. Users can also manually enable/disable 557c42a7b7eSSam Leffler * support with a sysctl. 558c42a7b7eSSam Leffler */ 559c42a7b7eSSam Leffler sc->sc_softled = (devid == AR5212_DEVID_IBM || devid == AR5211_DEVID); 560c42a7b7eSSam Leffler if (sc->sc_softled) { 561c42a7b7eSSam Leffler ath_hal_gpioCfgOutput(ah, sc->sc_ledpin); 5623e50ec2cSSam Leffler ath_hal_gpioset(ah, sc->sc_ledpin, !sc->sc_ledon); 563c42a7b7eSSam Leffler } 5645591b213SSam Leffler 5655591b213SSam Leffler ifp->if_softc = sc; 5665591b213SSam Leffler ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; 5675591b213SSam Leffler ifp->if_start = ath_start; 5685591b213SSam Leffler ifp->if_watchdog = ath_watchdog; 5695591b213SSam Leffler ifp->if_ioctl = ath_ioctl; 5705591b213SSam Leffler ifp->if_init = ath_init; 571154b8df2SMax Laier IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); 572154b8df2SMax Laier ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; 573154b8df2SMax Laier IFQ_SET_READY(&ifp->if_snd); 5745591b213SSam Leffler 575c42a7b7eSSam Leffler ic->ic_ifp = ifp; 5765591b213SSam Leffler /* XXX not right but it's not used anywhere important */ 5775591b213SSam Leffler ic->ic_phytype = IEEE80211_T_OFDM; 5785591b213SSam Leffler ic->ic_opmode = IEEE80211_M_STA; 579c42a7b7eSSam Leffler ic->ic_caps = 580c43feedeSSam Leffler IEEE80211_C_STA /* station mode */ 581c43feedeSSam Leffler | IEEE80211_C_IBSS /* ibss, nee adhoc, mode */ 582fe32c3efSSam Leffler | IEEE80211_C_HOSTAP /* hostap mode */ 583fe32c3efSSam Leffler | IEEE80211_C_MONITOR /* monitor mode */ 5847a04dc27SSam Leffler | IEEE80211_C_AHDEMO /* adhoc demo mode */ 585b032f27cSSam Leffler | IEEE80211_C_WDS /* 4-address traffic works */ 586fe32c3efSSam Leffler | IEEE80211_C_SHPREAMBLE /* short preamble supported */ 587c42a7b7eSSam Leffler | IEEE80211_C_SHSLOT /* short slot time supported */ 588c42a7b7eSSam Leffler | IEEE80211_C_WPA /* capable of WPA1+WPA2 */ 58968e8e04eSSam Leffler | IEEE80211_C_BGSCAN /* capable of bg scanning */ 59068e8e04eSSam Leffler | IEEE80211_C_TXFRAG /* handle tx frags */ 59101e7e035SSam Leffler ; 592c42a7b7eSSam Leffler /* 593c42a7b7eSSam Leffler * Query the hal to figure out h/w crypto support. 594c42a7b7eSSam Leffler */ 595c42a7b7eSSam Leffler if (ath_hal_ciphersupported(ah, HAL_CIPHER_WEP)) 596b032f27cSSam Leffler ic->ic_cryptocaps |= IEEE80211_CRYPTO_WEP; 597c42a7b7eSSam Leffler if (ath_hal_ciphersupported(ah, HAL_CIPHER_AES_OCB)) 598b032f27cSSam Leffler ic->ic_cryptocaps |= IEEE80211_CRYPTO_AES_OCB; 599c42a7b7eSSam Leffler if (ath_hal_ciphersupported(ah, HAL_CIPHER_AES_CCM)) 600b032f27cSSam Leffler ic->ic_cryptocaps |= IEEE80211_CRYPTO_AES_CCM; 601c42a7b7eSSam Leffler if (ath_hal_ciphersupported(ah, HAL_CIPHER_CKIP)) 602b032f27cSSam Leffler ic->ic_cryptocaps |= IEEE80211_CRYPTO_CKIP; 603c42a7b7eSSam Leffler if (ath_hal_ciphersupported(ah, HAL_CIPHER_TKIP)) { 604b032f27cSSam Leffler ic->ic_cryptocaps |= IEEE80211_CRYPTO_TKIP; 605c42a7b7eSSam Leffler /* 606c42a7b7eSSam Leffler * Check if h/w does the MIC and/or whether the 607c42a7b7eSSam Leffler * separate key cache entries are required to 608c42a7b7eSSam Leffler * handle both tx+rx MIC keys. 609c42a7b7eSSam Leffler */ 610c42a7b7eSSam Leffler if (ath_hal_ciphersupported(ah, HAL_CIPHER_MIC)) 611b032f27cSSam Leffler ic->ic_cryptocaps |= IEEE80211_CRYPTO_TKIPMIC; 6125901d2d3SSam Leffler /* 6135901d2d3SSam Leffler * If the h/w supports storing tx+rx MIC keys 6145901d2d3SSam Leffler * in one cache slot automatically enable use. 6155901d2d3SSam Leffler */ 6165901d2d3SSam Leffler if (ath_hal_hastkipsplit(ah) || 6175901d2d3SSam Leffler !ath_hal_settkipsplit(ah, AH_FALSE)) 618c42a7b7eSSam Leffler sc->sc_splitmic = 1; 619b032f27cSSam Leffler /* 620b032f27cSSam Leffler * If the h/w can do TKIP MIC together with WME then 621b032f27cSSam Leffler * we use it; otherwise we force the MIC to be done 622b032f27cSSam Leffler * in software by the net80211 layer. 623b032f27cSSam Leffler */ 624b032f27cSSam Leffler if (ath_hal_haswmetkipmic(ah)) 625b032f27cSSam Leffler sc->sc_wmetkipmic = 1; 626c42a7b7eSSam Leffler } 627e8fd88a3SSam Leffler sc->sc_hasclrkey = ath_hal_ciphersupported(ah, HAL_CIPHER_CLR); 628e8fd88a3SSam Leffler sc->sc_mcastkey = ath_hal_getmcastkeysearch(ah); 629c42a7b7eSSam Leffler /* 6305901d2d3SSam Leffler * Mark key cache slots associated with global keys 6315901d2d3SSam Leffler * as in use. If we knew TKIP was not to be used we 6325901d2d3SSam Leffler * could leave the +32, +64, and +32+64 slots free. 6335901d2d3SSam Leffler */ 6345901d2d3SSam Leffler for (i = 0; i < IEEE80211_WEP_NKID; i++) { 6355901d2d3SSam Leffler setbit(sc->sc_keymap, i); 6365901d2d3SSam Leffler setbit(sc->sc_keymap, i+64); 6375901d2d3SSam Leffler if (sc->sc_splitmic) { 6385901d2d3SSam Leffler setbit(sc->sc_keymap, i+32); 6395901d2d3SSam Leffler setbit(sc->sc_keymap, i+32+64); 6405901d2d3SSam Leffler } 6415901d2d3SSam Leffler } 6425901d2d3SSam Leffler /* 643c42a7b7eSSam Leffler * TPC support can be done either with a global cap or 644c42a7b7eSSam Leffler * per-packet support. The latter is not available on 645c42a7b7eSSam Leffler * all parts. We're a bit pedantic here as all parts 646c42a7b7eSSam Leffler * support a global cap. 647c42a7b7eSSam Leffler */ 648c59005e9SSam Leffler if (ath_hal_hastpc(ah) || ath_hal_hastxpowlimit(ah)) 649c42a7b7eSSam Leffler ic->ic_caps |= IEEE80211_C_TXPMGT; 650c42a7b7eSSam Leffler 651c42a7b7eSSam Leffler /* 652c42a7b7eSSam Leffler * Mark WME capability only if we have sufficient 653c42a7b7eSSam Leffler * hardware queues to do proper priority scheduling. 654c42a7b7eSSam Leffler */ 655c42a7b7eSSam Leffler if (sc->sc_ac2q[WME_AC_BE] != sc->sc_ac2q[WME_AC_BK]) 656c42a7b7eSSam Leffler ic->ic_caps |= IEEE80211_C_WME; 657c42a7b7eSSam Leffler /* 658e8fd88a3SSam Leffler * Check for misc other capabilities. 659c42a7b7eSSam Leffler */ 660c42a7b7eSSam Leffler if (ath_hal_hasbursting(ah)) 661c42a7b7eSSam Leffler ic->ic_caps |= IEEE80211_C_BURST; 662b032f27cSSam Leffler sc->sc_hasbmask = ath_hal_hasbssidmask(ah); 663b032f27cSSam Leffler sc->sc_hastsfadd = ath_hal_hastsfadjust(ah); 66468e8e04eSSam Leffler if (ath_hal_hasfastframes(ah)) 66568e8e04eSSam Leffler ic->ic_caps |= IEEE80211_C_FF; 66659efa8b5SSam Leffler wmodes = ath_hal_getwirelessmodes(ah); 667411373ebSSam Leffler if (wmodes & (HAL_MODE_108G|HAL_MODE_TURBO)) 66868e8e04eSSam Leffler ic->ic_caps |= IEEE80211_C_TURBOP; 66910ad9a77SSam Leffler #ifdef ATH_SUPPORT_TDMA 67010ad9a77SSam Leffler if (ath_hal_macversion(ah) > 0x78) { 67110ad9a77SSam Leffler ic->ic_caps |= IEEE80211_C_TDMA; /* capable of TDMA */ 67210ad9a77SSam Leffler ic->ic_tdma_update = ath_tdma_update; 67310ad9a77SSam Leffler } 67410ad9a77SSam Leffler #endif 675c42a7b7eSSam Leffler /* 676c42a7b7eSSam Leffler * Indicate we need the 802.11 header padded to a 677c42a7b7eSSam Leffler * 32-bit boundary for 4-address and QoS frames. 678c42a7b7eSSam Leffler */ 679c42a7b7eSSam Leffler ic->ic_flags |= IEEE80211_F_DATAPAD; 680c42a7b7eSSam Leffler 681c42a7b7eSSam Leffler /* 682c42a7b7eSSam Leffler * Query the hal about antenna support. 683c42a7b7eSSam Leffler */ 684c42a7b7eSSam Leffler sc->sc_defant = ath_hal_getdefantenna(ah); 685c42a7b7eSSam Leffler 686c42a7b7eSSam Leffler /* 687c42a7b7eSSam Leffler * Not all chips have the VEOL support we want to 688c42a7b7eSSam Leffler * use with IBSS beacons; check here for it. 689c42a7b7eSSam Leffler */ 690c42a7b7eSSam Leffler sc->sc_hasveol = ath_hal_hasveol(ah); 6915591b213SSam Leffler 6925591b213SSam Leffler /* get mac address from hardware */ 6935591b213SSam Leffler ath_hal_getmac(ah, ic->ic_myaddr); 694b032f27cSSam Leffler if (sc->sc_hasbmask) 695b032f27cSSam Leffler ath_hal_getbssidmask(ah, sc->sc_hwbssidmask); 6965591b213SSam Leffler 697b032f27cSSam Leffler /* NB: used to size node table key mapping array */ 698b032f27cSSam Leffler ic->ic_max_keyix = sc->sc_keymax; 6995591b213SSam Leffler /* call MI attach routine. */ 700c42a7b7eSSam Leffler ieee80211_ifattach(ic); 701b032f27cSSam Leffler ic->ic_setregdomain = ath_setregdomain; 702b032f27cSSam Leffler ic->ic_getradiocaps = ath_getradiocaps; 703b032f27cSSam Leffler sc->sc_opmode = HAL_M_STA; 704b032f27cSSam Leffler 7055591b213SSam Leffler /* override default methods */ 706b032f27cSSam Leffler ic->ic_newassoc = ath_newassoc; 707b032f27cSSam Leffler ic->ic_updateslot = ath_updateslot; 708b032f27cSSam Leffler ic->ic_wme.wme_update = ath_wme_update; 709b032f27cSSam Leffler ic->ic_vap_create = ath_vap_create; 710b032f27cSSam Leffler ic->ic_vap_delete = ath_vap_delete; 711b032f27cSSam Leffler ic->ic_raw_xmit = ath_raw_xmit; 712b032f27cSSam Leffler ic->ic_update_mcast = ath_update_mcast; 713b032f27cSSam Leffler ic->ic_update_promisc = ath_update_promisc; 7145591b213SSam Leffler ic->ic_node_alloc = ath_node_alloc; 7151e774079SSam Leffler sc->sc_node_free = ic->ic_node_free; 7165591b213SSam Leffler ic->ic_node_free = ath_node_free; 71768e8e04eSSam Leffler ic->ic_node_getsignal = ath_node_getsignal; 71868e8e04eSSam Leffler ic->ic_scan_start = ath_scan_start; 71968e8e04eSSam Leffler ic->ic_scan_end = ath_scan_end; 72068e8e04eSSam Leffler ic->ic_set_channel = ath_set_channel; 7215591b213SSam Leffler 722c42a7b7eSSam Leffler ath_bpfattach(sc); 7234866e6c2SSam Leffler /* 7244866e6c2SSam Leffler * Setup dynamic sysctl's now that country code and 7254866e6c2SSam Leffler * regdomain are available from the hal. 7264866e6c2SSam Leffler */ 7274866e6c2SSam Leffler ath_sysctlattach(sc); 72873454c73SSam Leffler 729c42a7b7eSSam Leffler if (bootverbose) 730c42a7b7eSSam Leffler ieee80211_announce(ic); 731c42a7b7eSSam Leffler ath_announce(sc); 7325591b213SSam Leffler return 0; 733b28b4653SSam Leffler bad2: 734c42a7b7eSSam Leffler ath_tx_cleanup(sc); 735b28b4653SSam Leffler ath_desc_free(sc); 7365591b213SSam Leffler bad: 7375591b213SSam Leffler if (ah) 7385591b213SSam Leffler ath_hal_detach(ah); 739fc74a9f9SBrooks Davis if (ifp != NULL) 740fc74a9f9SBrooks Davis if_free(ifp); 7415591b213SSam Leffler sc->sc_invalid = 1; 7425591b213SSam Leffler return error; 7435591b213SSam Leffler } 7445591b213SSam Leffler 7455591b213SSam Leffler int 7465591b213SSam Leffler ath_detach(struct ath_softc *sc) 7475591b213SSam Leffler { 748fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 7495591b213SSam Leffler 750c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n", 751c42a7b7eSSam Leffler __func__, ifp->if_flags); 7525591b213SSam Leffler 753c42a7b7eSSam Leffler /* 754c42a7b7eSSam Leffler * NB: the order of these is important: 75571b85077SSam Leffler * o stop the chip so no more interrupts will fire 756c42a7b7eSSam Leffler * o call the 802.11 layer before detaching the hal to 757c42a7b7eSSam Leffler * insure callbacks into the driver to delete global 758c42a7b7eSSam Leffler * key cache entries can be handled 75971b85077SSam Leffler * o free the taskqueue which drains any pending tasks 76071b85077SSam Leffler * o reclaim the bpf tap now that we know nothing will use 76171b85077SSam Leffler * it (e.g. rx processing from the task q thread) 762c42a7b7eSSam Leffler * o reclaim the tx queue data structures after calling 763c42a7b7eSSam Leffler * the 802.11 layer as we'll get called back to reclaim 764c42a7b7eSSam Leffler * node state and potentially want to use them 765c42a7b7eSSam Leffler * o to cleanup the tx queues the hal is called, so detach 766c42a7b7eSSam Leffler * it last 767c42a7b7eSSam Leffler * Other than that, it's straightforward... 768c42a7b7eSSam Leffler */ 76971b85077SSam Leffler ath_stop(ifp); 770b032f27cSSam Leffler ieee80211_ifdetach(ifp->if_l2com); 77171b85077SSam Leffler taskqueue_free(sc->sc_tq); 77271b85077SSam Leffler bpfdetach(ifp); 77386e07743SSam Leffler #ifdef ATH_TX99_DIAG 77486e07743SSam Leffler if (sc->sc_tx99 != NULL) 77586e07743SSam Leffler sc->sc_tx99->detach(sc->sc_tx99); 77686e07743SSam Leffler #endif 777c42a7b7eSSam Leffler ath_rate_detach(sc->sc_rc); 7785591b213SSam Leffler ath_desc_free(sc); 779c42a7b7eSSam Leffler ath_tx_cleanup(sc); 78071b85077SSam Leffler ath_hal_detach(sc->sc_ah); /* NB: sets chip in full sleep */ 781c4c6f08fSRuslan Ermilov if_free(ifp); 782f0b2a0beSSam Leffler 7835591b213SSam Leffler return 0; 7845591b213SSam Leffler } 7855591b213SSam Leffler 786b032f27cSSam Leffler /* 787b032f27cSSam Leffler * MAC address handling for multiple BSS on the same radio. 788b032f27cSSam Leffler * The first vap uses the MAC address from the EEPROM. For 789b032f27cSSam Leffler * subsequent vap's we set the U/L bit (bit 1) in the MAC 790b032f27cSSam Leffler * address and use the next six bits as an index. 791b032f27cSSam Leffler */ 792b032f27cSSam Leffler static void 793b032f27cSSam Leffler assign_address(struct ath_softc *sc, uint8_t mac[IEEE80211_ADDR_LEN], int clone) 794b032f27cSSam Leffler { 795b032f27cSSam Leffler int i; 796b032f27cSSam Leffler 797b032f27cSSam Leffler if (clone && sc->sc_hasbmask) { 798b032f27cSSam Leffler /* NB: we only do this if h/w supports multiple bssid */ 799b032f27cSSam Leffler for (i = 0; i < 8; i++) 800b032f27cSSam Leffler if ((sc->sc_bssidmask & (1<<i)) == 0) 801b032f27cSSam Leffler break; 802b032f27cSSam Leffler if (i != 0) 803b032f27cSSam Leffler mac[0] |= (i << 2)|0x2; 804b032f27cSSam Leffler } else 805b032f27cSSam Leffler i = 0; 806b032f27cSSam Leffler sc->sc_bssidmask |= 1<<i; 807b032f27cSSam Leffler sc->sc_hwbssidmask[0] &= ~mac[0]; 808b032f27cSSam Leffler if (i == 0) 809b032f27cSSam Leffler sc->sc_nbssid0++; 810b032f27cSSam Leffler } 811b032f27cSSam Leffler 812b032f27cSSam Leffler static void 813b032f27cSSam Leffler reclaim_address(struct ath_softc *sc, const uint8_t mac[IEEE80211_ADDR_LEN]) 814b032f27cSSam Leffler { 815b032f27cSSam Leffler int i = mac[0] >> 2; 816b032f27cSSam Leffler uint8_t mask; 817b032f27cSSam Leffler 818b032f27cSSam Leffler if (i != 0 || --sc->sc_nbssid0 == 0) { 819b032f27cSSam Leffler sc->sc_bssidmask &= ~(1<<i); 820b032f27cSSam Leffler /* recalculate bssid mask from remaining addresses */ 821b032f27cSSam Leffler mask = 0xff; 822b032f27cSSam Leffler for (i = 1; i < 8; i++) 823b032f27cSSam Leffler if (sc->sc_bssidmask & (1<<i)) 824b032f27cSSam Leffler mask &= ~((i<<2)|0x2); 825b032f27cSSam Leffler sc->sc_hwbssidmask[0] |= mask; 826b032f27cSSam Leffler } 827b032f27cSSam Leffler } 828b032f27cSSam Leffler 829b032f27cSSam Leffler /* 830b032f27cSSam Leffler * Assign a beacon xmit slot. We try to space out 831b032f27cSSam Leffler * assignments so when beacons are staggered the 832b032f27cSSam Leffler * traffic coming out of the cab q has maximal time 833b032f27cSSam Leffler * to go out before the next beacon is scheduled. 834b032f27cSSam Leffler */ 835b032f27cSSam Leffler static int 836b032f27cSSam Leffler assign_bslot(struct ath_softc *sc) 837b032f27cSSam Leffler { 838b032f27cSSam Leffler u_int slot, free; 839b032f27cSSam Leffler 840b032f27cSSam Leffler free = 0; 841b032f27cSSam Leffler for (slot = 0; slot < ATH_BCBUF; slot++) 842b032f27cSSam Leffler if (sc->sc_bslot[slot] == NULL) { 843b032f27cSSam Leffler if (sc->sc_bslot[(slot+1)%ATH_BCBUF] == NULL && 844b032f27cSSam Leffler sc->sc_bslot[(slot-1)%ATH_BCBUF] == NULL) 845b032f27cSSam Leffler return slot; 846b032f27cSSam Leffler free = slot; 847b032f27cSSam Leffler /* NB: keep looking for a double slot */ 848b032f27cSSam Leffler } 849b032f27cSSam Leffler return free; 850b032f27cSSam Leffler } 851b032f27cSSam Leffler 852b032f27cSSam Leffler static struct ieee80211vap * 853b032f27cSSam Leffler ath_vap_create(struct ieee80211com *ic, 854b032f27cSSam Leffler const char name[IFNAMSIZ], int unit, int opmode, int flags, 855b032f27cSSam Leffler const uint8_t bssid[IEEE80211_ADDR_LEN], 856b032f27cSSam Leffler const uint8_t mac0[IEEE80211_ADDR_LEN]) 857b032f27cSSam Leffler { 858b032f27cSSam Leffler struct ath_softc *sc = ic->ic_ifp->if_softc; 859b032f27cSSam Leffler struct ath_vap *avp; 860b032f27cSSam Leffler struct ieee80211vap *vap; 861b032f27cSSam Leffler uint8_t mac[IEEE80211_ADDR_LEN]; 862b032f27cSSam Leffler int ic_opmode, needbeacon, error; 863b032f27cSSam Leffler 864b032f27cSSam Leffler avp = (struct ath_vap *) malloc(sizeof(struct ath_vap), 865b032f27cSSam Leffler M_80211_VAP, M_WAITOK | M_ZERO); 866b032f27cSSam Leffler needbeacon = 0; 867b032f27cSSam Leffler IEEE80211_ADDR_COPY(mac, mac0); 868b032f27cSSam Leffler 869b032f27cSSam Leffler ATH_LOCK(sc); 870b032f27cSSam Leffler switch (opmode) { 871b032f27cSSam Leffler case IEEE80211_M_STA: 872b032f27cSSam Leffler if (sc->sc_nstavaps != 0) { /* XXX only 1 sta for now */ 873b032f27cSSam Leffler device_printf(sc->sc_dev, "only 1 sta vap supported\n"); 874b032f27cSSam Leffler goto bad; 875b032f27cSSam Leffler } 876b032f27cSSam Leffler if (sc->sc_nvaps) { 877b032f27cSSam Leffler /* 878b032f27cSSam Leffler * When there are multiple vaps we must fall 879b032f27cSSam Leffler * back to s/w beacon miss handling. 880b032f27cSSam Leffler */ 881b032f27cSSam Leffler flags |= IEEE80211_CLONE_NOBEACONS; 882b032f27cSSam Leffler } 88310ad9a77SSam Leffler if (flags & IEEE80211_CLONE_NOBEACONS) 884b032f27cSSam Leffler ic_opmode = IEEE80211_M_HOSTAP; 88510ad9a77SSam Leffler else 886b032f27cSSam Leffler ic_opmode = opmode; 887b032f27cSSam Leffler break; 888b032f27cSSam Leffler case IEEE80211_M_IBSS: 889b032f27cSSam Leffler if (sc->sc_nvaps != 0) { /* XXX only 1 for now */ 890b032f27cSSam Leffler device_printf(sc->sc_dev, 891b032f27cSSam Leffler "only 1 ibss vap supported\n"); 892b032f27cSSam Leffler goto bad; 893b032f27cSSam Leffler } 894b032f27cSSam Leffler ic_opmode = opmode; 895b032f27cSSam Leffler needbeacon = 1; 896b032f27cSSam Leffler break; 897b032f27cSSam Leffler case IEEE80211_M_AHDEMO: 89810ad9a77SSam Leffler #ifdef ATH_SUPPORT_TDMA 89910ad9a77SSam Leffler if (flags & IEEE80211_CLONE_TDMA) { 90010ad9a77SSam Leffler needbeacon = 1; 90110ad9a77SSam Leffler flags |= IEEE80211_CLONE_NOBEACONS; 90210ad9a77SSam Leffler } 903b032f27cSSam Leffler /* fall thru... */ 90410ad9a77SSam Leffler #endif 905b032f27cSSam Leffler case IEEE80211_M_MONITOR: 906b032f27cSSam Leffler if (sc->sc_nvaps != 0 && ic->ic_opmode != opmode) { 907b032f27cSSam Leffler /* XXX not right for monitor mode */ 908b032f27cSSam Leffler ic_opmode = ic->ic_opmode; 909b032f27cSSam Leffler } else 910b032f27cSSam Leffler ic_opmode = opmode; 911b032f27cSSam Leffler break; 912b032f27cSSam Leffler case IEEE80211_M_HOSTAP: 913b032f27cSSam Leffler needbeacon = 1; 914b032f27cSSam Leffler /* fall thru... */ 915b032f27cSSam Leffler case IEEE80211_M_WDS: 916b032f27cSSam Leffler if (sc->sc_nvaps && ic->ic_opmode == IEEE80211_M_STA) { 917b032f27cSSam Leffler device_printf(sc->sc_dev, 918b032f27cSSam Leffler "wds not supported in sta mode\n"); 919b032f27cSSam Leffler goto bad; 920b032f27cSSam Leffler } 921b032f27cSSam Leffler if (opmode == IEEE80211_M_WDS) { 922b032f27cSSam Leffler /* 923b032f27cSSam Leffler * Silently remove any request for a unique 924b032f27cSSam Leffler * bssid; WDS vap's always share the local 925b032f27cSSam Leffler * mac address. 926b032f27cSSam Leffler */ 927b032f27cSSam Leffler flags &= ~IEEE80211_CLONE_BSSID; 928b032f27cSSam Leffler } 929b032f27cSSam Leffler ic_opmode = IEEE80211_M_HOSTAP; 930b032f27cSSam Leffler break; 931b032f27cSSam Leffler default: 932b032f27cSSam Leffler device_printf(sc->sc_dev, "unknown opmode %d\n", opmode); 933b032f27cSSam Leffler goto bad; 934b032f27cSSam Leffler } 935b032f27cSSam Leffler /* 936b032f27cSSam Leffler * Check that a beacon buffer is available; the code below assumes it. 937b032f27cSSam Leffler */ 938b032f27cSSam Leffler if (needbeacon & STAILQ_EMPTY(&sc->sc_bbuf)) { 939b032f27cSSam Leffler device_printf(sc->sc_dev, "no beacon buffer available\n"); 940b032f27cSSam Leffler goto bad; 941b032f27cSSam Leffler } 942b032f27cSSam Leffler 943b032f27cSSam Leffler /* STA, AHDEMO? */ 944b032f27cSSam Leffler if (opmode == IEEE80211_M_HOSTAP) { 945b032f27cSSam Leffler assign_address(sc, mac, flags & IEEE80211_CLONE_BSSID); 946b032f27cSSam Leffler ath_hal_setbssidmask(sc->sc_ah, sc->sc_hwbssidmask); 947b032f27cSSam Leffler } 948b032f27cSSam Leffler 949b032f27cSSam Leffler vap = &avp->av_vap; 950b032f27cSSam Leffler /* XXX can't hold mutex across if_alloc */ 951b032f27cSSam Leffler ATH_UNLOCK(sc); 952b032f27cSSam Leffler error = ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, 953b032f27cSSam Leffler bssid, mac); 954b032f27cSSam Leffler ATH_LOCK(sc); 955b032f27cSSam Leffler if (error != 0) { 956b032f27cSSam Leffler device_printf(sc->sc_dev, "%s: error %d creating vap\n", 957b032f27cSSam Leffler __func__, error); 958b032f27cSSam Leffler goto bad2; 959b032f27cSSam Leffler } 960b032f27cSSam Leffler 961b032f27cSSam Leffler /* h/w crypto support */ 962b032f27cSSam Leffler vap->iv_key_alloc = ath_key_alloc; 963b032f27cSSam Leffler vap->iv_key_delete = ath_key_delete; 964b032f27cSSam Leffler vap->iv_key_set = ath_key_set; 965b032f27cSSam Leffler vap->iv_key_update_begin = ath_key_update_begin; 966b032f27cSSam Leffler vap->iv_key_update_end = ath_key_update_end; 967b032f27cSSam Leffler 968b032f27cSSam Leffler /* override various methods */ 969b032f27cSSam Leffler avp->av_recv_mgmt = vap->iv_recv_mgmt; 970b032f27cSSam Leffler vap->iv_recv_mgmt = ath_recv_mgmt; 971b032f27cSSam Leffler vap->iv_reset = ath_reset_vap; 972b032f27cSSam Leffler vap->iv_update_beacon = ath_beacon_update; 973b032f27cSSam Leffler avp->av_newstate = vap->iv_newstate; 974b032f27cSSam Leffler vap->iv_newstate = ath_newstate; 975b032f27cSSam Leffler avp->av_bmiss = vap->iv_bmiss; 976b032f27cSSam Leffler vap->iv_bmiss = ath_bmiss_vap; 977b032f27cSSam Leffler 978b032f27cSSam Leffler avp->av_bslot = -1; 979b032f27cSSam Leffler if (needbeacon) { 980b032f27cSSam Leffler /* 981b032f27cSSam Leffler * Allocate beacon state and setup the q for buffered 982b032f27cSSam Leffler * multicast frames. We know a beacon buffer is 983b032f27cSSam Leffler * available because we checked above. 984b032f27cSSam Leffler */ 985b032f27cSSam Leffler avp->av_bcbuf = STAILQ_FIRST(&sc->sc_bbuf); 986b032f27cSSam Leffler STAILQ_REMOVE_HEAD(&sc->sc_bbuf, bf_list); 987b032f27cSSam Leffler if (opmode != IEEE80211_M_IBSS || !sc->sc_hasveol) { 988b032f27cSSam Leffler /* 989b032f27cSSam Leffler * Assign the vap to a beacon xmit slot. As above 990b032f27cSSam Leffler * this cannot fail to find a free one. 991b032f27cSSam Leffler */ 992b032f27cSSam Leffler avp->av_bslot = assign_bslot(sc); 993b032f27cSSam Leffler KASSERT(sc->sc_bslot[avp->av_bslot] == NULL, 994b032f27cSSam Leffler ("beacon slot %u not empty", avp->av_bslot)); 995b032f27cSSam Leffler sc->sc_bslot[avp->av_bslot] = vap; 996b032f27cSSam Leffler sc->sc_nbcnvaps++; 997b032f27cSSam Leffler } 998b032f27cSSam Leffler if (sc->sc_hastsfadd && sc->sc_nbcnvaps > 0) { 999b032f27cSSam Leffler /* 1000b032f27cSSam Leffler * Multple vaps are to transmit beacons and we 1001b032f27cSSam Leffler * have h/w support for TSF adjusting; enable 1002b032f27cSSam Leffler * use of staggered beacons. 1003b032f27cSSam Leffler */ 1004b032f27cSSam Leffler sc->sc_stagbeacons = 1; 1005b032f27cSSam Leffler } 1006b032f27cSSam Leffler ath_txq_init(sc, &avp->av_mcastq, ATH_TXQ_SWQ); 1007b032f27cSSam Leffler } 1008b032f27cSSam Leffler 1009b032f27cSSam Leffler ic->ic_opmode = ic_opmode; 1010b032f27cSSam Leffler if (opmode != IEEE80211_M_WDS) { 1011b032f27cSSam Leffler sc->sc_nvaps++; 1012b032f27cSSam Leffler if (opmode == IEEE80211_M_STA) 1013b032f27cSSam Leffler sc->sc_nstavaps++; 1014b032f27cSSam Leffler } 1015b032f27cSSam Leffler switch (ic_opmode) { 1016b032f27cSSam Leffler case IEEE80211_M_IBSS: 1017b032f27cSSam Leffler sc->sc_opmode = HAL_M_IBSS; 1018b032f27cSSam Leffler break; 1019b032f27cSSam Leffler case IEEE80211_M_STA: 1020b032f27cSSam Leffler sc->sc_opmode = HAL_M_STA; 1021b032f27cSSam Leffler break; 1022b032f27cSSam Leffler case IEEE80211_M_AHDEMO: 102310ad9a77SSam Leffler #ifdef ATH_SUPPORT_TDMA 102410ad9a77SSam Leffler if (vap->iv_caps & IEEE80211_C_TDMA) { 102510ad9a77SSam Leffler sc->sc_tdma = 1; 102610ad9a77SSam Leffler /* NB: disable tsf adjust */ 102710ad9a77SSam Leffler sc->sc_stagbeacons = 0; 102810ad9a77SSam Leffler } 102910ad9a77SSam Leffler /* 103010ad9a77SSam Leffler * NB: adhoc demo mode is a pseudo mode; to the hal it's 103110ad9a77SSam Leffler * just ap mode. 103210ad9a77SSam Leffler */ 103310ad9a77SSam Leffler /* fall thru... */ 103410ad9a77SSam Leffler #endif 1035b032f27cSSam Leffler case IEEE80211_M_HOSTAP: 1036b032f27cSSam Leffler sc->sc_opmode = HAL_M_HOSTAP; 1037b032f27cSSam Leffler break; 1038b032f27cSSam Leffler case IEEE80211_M_MONITOR: 1039b032f27cSSam Leffler sc->sc_opmode = HAL_M_MONITOR; 1040b032f27cSSam Leffler break; 1041b032f27cSSam Leffler default: 1042b032f27cSSam Leffler /* XXX should not happen */ 1043b032f27cSSam Leffler break; 1044b032f27cSSam Leffler } 1045b032f27cSSam Leffler if (sc->sc_hastsfadd) { 1046b032f27cSSam Leffler /* 1047b032f27cSSam Leffler * Configure whether or not TSF adjust should be done. 1048b032f27cSSam Leffler */ 1049b032f27cSSam Leffler ath_hal_settsfadjust(sc->sc_ah, sc->sc_stagbeacons); 1050b032f27cSSam Leffler } 105110ad9a77SSam Leffler if (flags & IEEE80211_CLONE_NOBEACONS) { 105210ad9a77SSam Leffler /* 105310ad9a77SSam Leffler * Enable s/w beacon miss handling. 105410ad9a77SSam Leffler */ 105510ad9a77SSam Leffler sc->sc_swbmiss = 1; 105610ad9a77SSam Leffler } 1057b032f27cSSam Leffler ATH_UNLOCK(sc); 1058b032f27cSSam Leffler 1059b032f27cSSam Leffler /* complete setup */ 1060b032f27cSSam Leffler ieee80211_vap_attach(vap, ath_media_change, ieee80211_media_status); 1061b032f27cSSam Leffler return vap; 1062b032f27cSSam Leffler bad2: 1063b032f27cSSam Leffler reclaim_address(sc, mac); 1064b032f27cSSam Leffler ath_hal_setbssidmask(sc->sc_ah, sc->sc_hwbssidmask); 1065b032f27cSSam Leffler bad: 1066b032f27cSSam Leffler free(avp, M_80211_VAP); 1067b032f27cSSam Leffler ATH_UNLOCK(sc); 1068b032f27cSSam Leffler return NULL; 1069b032f27cSSam Leffler } 1070b032f27cSSam Leffler 1071b032f27cSSam Leffler static void 1072b032f27cSSam Leffler ath_vap_delete(struct ieee80211vap *vap) 1073b032f27cSSam Leffler { 1074b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 1075b032f27cSSam Leffler struct ifnet *ifp = ic->ic_ifp; 1076b032f27cSSam Leffler struct ath_softc *sc = ifp->if_softc; 1077b032f27cSSam Leffler struct ath_hal *ah = sc->sc_ah; 1078b032f27cSSam Leffler struct ath_vap *avp = ATH_VAP(vap); 1079b032f27cSSam Leffler 1080b032f27cSSam Leffler if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 1081b032f27cSSam Leffler /* 1082b032f27cSSam Leffler * Quiesce the hardware while we remove the vap. In 1083b032f27cSSam Leffler * particular we need to reclaim all references to 1084b032f27cSSam Leffler * the vap state by any frames pending on the tx queues. 1085b032f27cSSam Leffler */ 1086b032f27cSSam Leffler ath_hal_intrset(ah, 0); /* disable interrupts */ 1087b032f27cSSam Leffler ath_draintxq(sc); /* stop xmit side */ 1088b032f27cSSam Leffler ath_stoprecv(sc); /* stop recv side */ 1089b032f27cSSam Leffler } 1090b032f27cSSam Leffler 1091b032f27cSSam Leffler ieee80211_vap_detach(vap); 1092b032f27cSSam Leffler ATH_LOCK(sc); 1093b032f27cSSam Leffler /* 1094b032f27cSSam Leffler * Reclaim beacon state. Note this must be done before 1095b032f27cSSam Leffler * the vap instance is reclaimed as we may have a reference 1096b032f27cSSam Leffler * to it in the buffer for the beacon frame. 1097b032f27cSSam Leffler */ 1098b032f27cSSam Leffler if (avp->av_bcbuf != NULL) { 1099b032f27cSSam Leffler if (avp->av_bslot != -1) { 1100b032f27cSSam Leffler sc->sc_bslot[avp->av_bslot] = NULL; 1101b032f27cSSam Leffler sc->sc_nbcnvaps--; 1102b032f27cSSam Leffler } 1103b032f27cSSam Leffler ath_beacon_return(sc, avp->av_bcbuf); 1104b032f27cSSam Leffler avp->av_bcbuf = NULL; 1105b032f27cSSam Leffler if (sc->sc_nbcnvaps == 0) { 1106b032f27cSSam Leffler sc->sc_stagbeacons = 0; 1107b032f27cSSam Leffler if (sc->sc_hastsfadd) 1108b032f27cSSam Leffler ath_hal_settsfadjust(sc->sc_ah, 0); 1109b032f27cSSam Leffler } 1110b032f27cSSam Leffler /* 1111b032f27cSSam Leffler * Reclaim any pending mcast frames for the vap. 1112b032f27cSSam Leffler */ 1113b032f27cSSam Leffler ath_tx_draintxq(sc, &avp->av_mcastq); 1114b032f27cSSam Leffler ATH_TXQ_LOCK_DESTROY(&avp->av_mcastq); 1115b032f27cSSam Leffler } 1116b032f27cSSam Leffler /* 1117b032f27cSSam Leffler * Update bookkeeping. 1118b032f27cSSam Leffler */ 1119b032f27cSSam Leffler if (vap->iv_opmode == IEEE80211_M_STA) { 1120b032f27cSSam Leffler sc->sc_nstavaps--; 1121b032f27cSSam Leffler if (sc->sc_nstavaps == 0 && sc->sc_swbmiss) 1122b032f27cSSam Leffler sc->sc_swbmiss = 0; 1123b032f27cSSam Leffler } else if (vap->iv_opmode == IEEE80211_M_HOSTAP) { 1124b032f27cSSam Leffler reclaim_address(sc, vap->iv_myaddr); 1125b032f27cSSam Leffler ath_hal_setbssidmask(ah, sc->sc_hwbssidmask); 1126b032f27cSSam Leffler } 1127b032f27cSSam Leffler if (vap->iv_opmode != IEEE80211_M_WDS) 1128b032f27cSSam Leffler sc->sc_nvaps--; 112910ad9a77SSam Leffler #ifdef ATH_SUPPORT_TDMA 113010ad9a77SSam Leffler /* TDMA operation ceases when the last vap is destroyed */ 113110ad9a77SSam Leffler if (sc->sc_tdma && sc->sc_nvaps == 0) { 113210ad9a77SSam Leffler sc->sc_tdma = 0; 113310ad9a77SSam Leffler sc->sc_swbmiss = 0; 113410ad9a77SSam Leffler } 113510ad9a77SSam Leffler #endif 1136b032f27cSSam Leffler ATH_UNLOCK(sc); 1137b032f27cSSam Leffler free(avp, M_80211_VAP); 1138b032f27cSSam Leffler 1139b032f27cSSam Leffler if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 1140b032f27cSSam Leffler /* 1141b032f27cSSam Leffler * Restart rx+tx machines if still running (RUNNING will 1142b032f27cSSam Leffler * be reset if we just destroyed the last vap). 1143b032f27cSSam Leffler */ 1144b032f27cSSam Leffler if (ath_startrecv(sc) != 0) 1145b032f27cSSam Leffler if_printf(ifp, "%s: unable to restart recv logic\n", 1146b032f27cSSam Leffler __func__); 1147b032f27cSSam Leffler if (sc->sc_beacons) 1148b032f27cSSam Leffler ath_beacon_config(sc, NULL); 1149b032f27cSSam Leffler ath_hal_intrset(ah, sc->sc_imask); 1150b032f27cSSam Leffler } 1151b032f27cSSam Leffler } 1152b032f27cSSam Leffler 11535591b213SSam Leffler void 11545591b213SSam Leffler ath_suspend(struct ath_softc *sc) 11555591b213SSam Leffler { 1156fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 1157d3ac945bSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 11585591b213SSam Leffler 1159c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n", 1160c42a7b7eSSam Leffler __func__, ifp->if_flags); 11615591b213SSam Leffler 1162d3ac945bSSam Leffler sc->sc_resume_up = (ifp->if_flags & IFF_UP) != 0; 1163d3ac945bSSam Leffler if (ic->ic_opmode == IEEE80211_M_STA) 11645591b213SSam Leffler ath_stop(ifp); 1165d3ac945bSSam Leffler else 1166d3ac945bSSam Leffler ieee80211_suspend_all(ic); 1167d3ac945bSSam Leffler /* 1168d3ac945bSSam Leffler * NB: don't worry about putting the chip in low power 1169d3ac945bSSam Leffler * mode; pci will power off our socket on suspend and 1170d3ac945bSSam Leffler * cardbus detaches the device. 1171d3ac945bSSam Leffler */ 1172d3ac945bSSam Leffler } 1173d3ac945bSSam Leffler 1174d3ac945bSSam Leffler /* 1175d3ac945bSSam Leffler * Reset the key cache since some parts do not reset the 1176d3ac945bSSam Leffler * contents on resume. First we clear all entries, then 1177d3ac945bSSam Leffler * re-load keys that the 802.11 layer assumes are setup 1178d3ac945bSSam Leffler * in h/w. 1179d3ac945bSSam Leffler */ 1180d3ac945bSSam Leffler static void 1181d3ac945bSSam Leffler ath_reset_keycache(struct ath_softc *sc) 1182d3ac945bSSam Leffler { 1183d3ac945bSSam Leffler struct ifnet *ifp = sc->sc_ifp; 1184d3ac945bSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 1185d3ac945bSSam Leffler struct ath_hal *ah = sc->sc_ah; 1186d3ac945bSSam Leffler int i; 1187d3ac945bSSam Leffler 1188d3ac945bSSam Leffler for (i = 0; i < sc->sc_keymax; i++) 1189d3ac945bSSam Leffler ath_hal_keyreset(ah, i); 1190d3ac945bSSam Leffler ieee80211_crypto_reload_keys(ic); 11915591b213SSam Leffler } 11925591b213SSam Leffler 11935591b213SSam Leffler void 11945591b213SSam Leffler ath_resume(struct ath_softc *sc) 11955591b213SSam Leffler { 1196fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 1197d3ac945bSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 1198d3ac945bSSam Leffler struct ath_hal *ah = sc->sc_ah; 1199d3ac945bSSam Leffler HAL_STATUS status; 12005591b213SSam Leffler 1201c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n", 1202c42a7b7eSSam Leffler __func__, ifp->if_flags); 12035591b213SSam Leffler 1204d3ac945bSSam Leffler /* 1205d3ac945bSSam Leffler * Must reset the chip before we reload the 1206d3ac945bSSam Leffler * keycache as we were powered down on suspend. 1207d3ac945bSSam Leffler */ 1208054d7b69SSam Leffler ath_hal_reset(ah, sc->sc_opmode, 1209054d7b69SSam Leffler sc->sc_curchan != NULL ? sc->sc_curchan : ic->ic_curchan, 1210054d7b69SSam Leffler AH_FALSE, &status); 1211d3ac945bSSam Leffler ath_reset_keycache(sc); 1212d3ac945bSSam Leffler if (sc->sc_resume_up) { 1213d3ac945bSSam Leffler if (ic->ic_opmode == IEEE80211_M_STA) { 1214fc74a9f9SBrooks Davis ath_init(sc); 1215d3ac945bSSam Leffler ieee80211_beacon_miss(ic); 1216d3ac945bSSam Leffler } else 1217d3ac945bSSam Leffler ieee80211_resume_all(ic); 12185591b213SSam Leffler } 1219b50c8bdeSSam Leffler if (sc->sc_softled) { 1220d3ac945bSSam Leffler ath_hal_gpioCfgOutput(ah, sc->sc_ledpin); 1221d3ac945bSSam Leffler ath_hal_gpioset(ah, sc->sc_ledpin, !sc->sc_ledon); 1222b50c8bdeSSam Leffler } 12236b59f5e3SSam Leffler } 12245591b213SSam Leffler 12255591b213SSam Leffler void 12265591b213SSam Leffler ath_shutdown(struct ath_softc *sc) 12275591b213SSam Leffler { 1228fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 12295591b213SSam Leffler 1230c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n", 1231c42a7b7eSSam Leffler __func__, ifp->if_flags); 12325591b213SSam Leffler 12335591b213SSam Leffler ath_stop(ifp); 1234d3ac945bSSam Leffler /* NB: no point powering down chip as we're about to reboot */ 12355591b213SSam Leffler } 12365591b213SSam Leffler 1237c42a7b7eSSam Leffler /* 1238c42a7b7eSSam Leffler * Interrupt handler. Most of the actual processing is deferred. 1239c42a7b7eSSam Leffler */ 12405591b213SSam Leffler void 12415591b213SSam Leffler ath_intr(void *arg) 12425591b213SSam Leffler { 12435591b213SSam Leffler struct ath_softc *sc = arg; 1244fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 12455591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 12465591b213SSam Leffler HAL_INT status; 12475591b213SSam Leffler 12485591b213SSam Leffler if (sc->sc_invalid) { 12495591b213SSam Leffler /* 1250b58b3803SSam Leffler * The hardware is not ready/present, don't touch anything. 1251b58b3803SSam Leffler * Note this can happen early on if the IRQ is shared. 12525591b213SSam Leffler */ 1253c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid; ignored\n", __func__); 12545591b213SSam Leffler return; 12555591b213SSam Leffler } 1256fdd758d4SSam Leffler if (!ath_hal_intrpend(ah)) /* shared irq, not for us */ 1257fdd758d4SSam Leffler return; 125868e8e04eSSam Leffler if ((ifp->if_flags & IFF_UP) == 0 || 125968e8e04eSSam Leffler (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { 126068e8e04eSSam Leffler HAL_INT status; 126168e8e04eSSam Leffler 1262c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags 0x%x\n", 1263c42a7b7eSSam Leffler __func__, ifp->if_flags); 12645591b213SSam Leffler ath_hal_getisr(ah, &status); /* clear ISR */ 12655591b213SSam Leffler ath_hal_intrset(ah, 0); /* disable further intr's */ 12665591b213SSam Leffler return; 12675591b213SSam Leffler } 1268c42a7b7eSSam Leffler /* 1269c42a7b7eSSam Leffler * Figure out the reason(s) for the interrupt. Note 1270c42a7b7eSSam Leffler * that the hal returns a pseudo-ISR that may include 1271c42a7b7eSSam Leffler * bits we haven't explicitly enabled so we mask the 1272c42a7b7eSSam Leffler * value to insure we only process bits we requested. 1273c42a7b7eSSam Leffler */ 12745591b213SSam Leffler ath_hal_getisr(ah, &status); /* NB: clears ISR too */ 1275c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_INTR, "%s: status 0x%x\n", __func__, status); 1276ecddff40SSam Leffler status &= sc->sc_imask; /* discard unasked for bits */ 12775591b213SSam Leffler if (status & HAL_INT_FATAL) { 12785591b213SSam Leffler sc->sc_stats.ast_hardware++; 12795591b213SSam Leffler ath_hal_intrset(ah, 0); /* disable intr's until reset */ 128016c8acaaSSam Leffler ath_fatal_proc(sc, 0); 12815591b213SSam Leffler } else { 1282c42a7b7eSSam Leffler if (status & HAL_INT_SWBA) { 1283c42a7b7eSSam Leffler /* 1284c42a7b7eSSam Leffler * Software beacon alert--time to send a beacon. 1285c42a7b7eSSam Leffler * Handle beacon transmission directly; deferring 1286c42a7b7eSSam Leffler * this is too slow to meet timing constraints 1287c42a7b7eSSam Leffler * under load. 1288c42a7b7eSSam Leffler */ 128910ad9a77SSam Leffler #ifdef ATH_SUPPORT_TDMA 129010ad9a77SSam Leffler if (sc->sc_tdma) { 129110ad9a77SSam Leffler if (sc->sc_tdmaswba == 0) { 129210ad9a77SSam Leffler struct ieee80211com *ic = ifp->if_l2com; 129310ad9a77SSam Leffler struct ieee80211vap *vap = 129410ad9a77SSam Leffler TAILQ_FIRST(&ic->ic_vaps); 129510ad9a77SSam Leffler ath_tdma_beacon_send(sc, vap); 129610ad9a77SSam Leffler sc->sc_tdmaswba = 129710ad9a77SSam Leffler vap->iv_tdma->tdma_bintval; 129810ad9a77SSam Leffler } else 129910ad9a77SSam Leffler sc->sc_tdmaswba--; 130010ad9a77SSam Leffler } else 130110ad9a77SSam Leffler #endif 1302c42a7b7eSSam Leffler ath_beacon_proc(sc, 0); 1303c42a7b7eSSam Leffler } 13045591b213SSam Leffler if (status & HAL_INT_RXEOL) { 13055591b213SSam Leffler /* 13065591b213SSam Leffler * NB: the hardware should re-read the link when 13075591b213SSam Leffler * RXE bit is written, but it doesn't work at 13085591b213SSam Leffler * least on older hardware revs. 13095591b213SSam Leffler */ 13105591b213SSam Leffler sc->sc_stats.ast_rxeol++; 13115591b213SSam Leffler sc->sc_rxlink = NULL; 13125591b213SSam Leffler } 13135591b213SSam Leffler if (status & HAL_INT_TXURN) { 13145591b213SSam Leffler sc->sc_stats.ast_txurn++; 13155591b213SSam Leffler /* bump tx trigger level */ 13165591b213SSam Leffler ath_hal_updatetxtriglevel(ah, AH_TRUE); 13175591b213SSam Leffler } 13185591b213SSam Leffler if (status & HAL_INT_RX) 13190bbf5441SSam Leffler taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask); 13205591b213SSam Leffler if (status & HAL_INT_TX) 13210bbf5441SSam Leffler taskqueue_enqueue(sc->sc_tq, &sc->sc_txtask); 13225591b213SSam Leffler if (status & HAL_INT_BMISS) { 13235591b213SSam Leffler sc->sc_stats.ast_bmiss++; 13240bbf5441SSam Leffler taskqueue_enqueue(sc->sc_tq, &sc->sc_bmisstask); 13255591b213SSam Leffler } 1326c42a7b7eSSam Leffler if (status & HAL_INT_MIB) { 1327c42a7b7eSSam Leffler sc->sc_stats.ast_mib++; 1328c42a7b7eSSam Leffler /* 1329c42a7b7eSSam Leffler * Disable interrupts until we service the MIB 1330c42a7b7eSSam Leffler * interrupt; otherwise it will continue to fire. 1331c42a7b7eSSam Leffler */ 1332c42a7b7eSSam Leffler ath_hal_intrset(ah, 0); 1333c42a7b7eSSam Leffler /* 1334c42a7b7eSSam Leffler * Let the hal handle the event. We assume it will 1335c42a7b7eSSam Leffler * clear whatever condition caused the interrupt. 1336c42a7b7eSSam Leffler */ 1337ffa2cab6SSam Leffler ath_hal_mibevent(ah, &sc->sc_halstats); 1338c42a7b7eSSam Leffler ath_hal_intrset(ah, sc->sc_imask); 1339c42a7b7eSSam Leffler } 13409c4fc1e8SSam Leffler if (status & HAL_INT_RXORN) { 13419c4fc1e8SSam Leffler /* NB: hal marks HAL_INT_FATAL when RXORN is fatal */ 13429c4fc1e8SSam Leffler sc->sc_stats.ast_rxorn++; 13439c4fc1e8SSam Leffler } 13445591b213SSam Leffler } 13455591b213SSam Leffler } 13465591b213SSam Leffler 13475591b213SSam Leffler static void 13485591b213SSam Leffler ath_fatal_proc(void *arg, int pending) 13495591b213SSam Leffler { 13505591b213SSam Leffler struct ath_softc *sc = arg; 1351fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 135216c8acaaSSam Leffler u_int32_t *state; 135316c8acaaSSam Leffler u_int32_t len; 135468e8e04eSSam Leffler void *sp; 13555591b213SSam Leffler 1356c42a7b7eSSam Leffler if_printf(ifp, "hardware error; resetting\n"); 135716c8acaaSSam Leffler /* 135816c8acaaSSam Leffler * Fatal errors are unrecoverable. Typically these 135916c8acaaSSam Leffler * are caused by DMA errors. Collect h/w state from 136016c8acaaSSam Leffler * the hal so we can diagnose what's going on. 136116c8acaaSSam Leffler */ 136268e8e04eSSam Leffler if (ath_hal_getfatalstate(sc->sc_ah, &sp, &len)) { 136316c8acaaSSam Leffler KASSERT(len >= 6*sizeof(u_int32_t), ("len %u bytes", len)); 136468e8e04eSSam Leffler state = sp; 136516c8acaaSSam Leffler if_printf(ifp, "0x%08x 0x%08x 0x%08x, 0x%08x 0x%08x 0x%08x\n", 136616c8acaaSSam Leffler state[0], state[1] , state[2], state[3], 136716c8acaaSSam Leffler state[4], state[5]); 136816c8acaaSSam Leffler } 1369c42a7b7eSSam Leffler ath_reset(ifp); 13705591b213SSam Leffler } 13715591b213SSam Leffler 13725591b213SSam Leffler static void 1373b032f27cSSam Leffler ath_bmiss_vap(struct ieee80211vap *vap) 13745591b213SSam Leffler { 1375a7ace843SSam Leffler struct ifnet *ifp = vap->iv_ic->ic_ifp; 1376a7ace843SSam Leffler struct ath_softc *sc = ifp->if_softc; 1377d7736e13SSam Leffler u_int64_t lastrx = sc->sc_lastrx; 1378d7736e13SSam Leffler u_int64_t tsf = ath_hal_gettsf64(sc->sc_ah); 1379d7736e13SSam Leffler u_int bmisstimeout = 1380b032f27cSSam Leffler vap->iv_bmissthreshold * vap->iv_bss->ni_intval * 1024; 1381d7736e13SSam Leffler 1382d7736e13SSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON, 1383d7736e13SSam Leffler "%s: tsf %llu lastrx %lld (%llu) bmiss %u\n", 1384d7736e13SSam Leffler __func__, (unsigned long long) tsf, 1385d7736e13SSam Leffler (unsigned long long)(tsf - lastrx), 1386d7736e13SSam Leffler (unsigned long long) lastrx, bmisstimeout); 1387e585d188SSam Leffler /* 1388d7736e13SSam Leffler * Workaround phantom bmiss interrupts by sanity-checking 1389d7736e13SSam Leffler * the time of our last rx'd frame. If it is within the 1390d7736e13SSam Leffler * beacon miss interval then ignore the interrupt. If it's 1391d7736e13SSam Leffler * truly a bmiss we'll get another interrupt soon and that'll 1392d7736e13SSam Leffler * be dispatched up for processing. 1393e585d188SSam Leffler */ 13940bf686c1SRobert Watson if (tsf - lastrx > bmisstimeout) 1395b032f27cSSam Leffler ATH_VAP(vap)->av_bmiss(vap); 13960bf686c1SRobert Watson else 1397d7736e13SSam Leffler sc->sc_stats.ast_bmiss_phantom++; 1398e585d188SSam Leffler } 1399b032f27cSSam Leffler 1400459bc4f0SSam Leffler static int 1401459bc4f0SSam Leffler ath_hal_gethangstate(struct ath_hal *ah, uint32_t mask, uint32_t *hangs) 1402459bc4f0SSam Leffler { 1403459bc4f0SSam Leffler uint32_t rsize; 1404459bc4f0SSam Leffler void *sp; 1405459bc4f0SSam Leffler 1406459bc4f0SSam Leffler if (!ath_hal_getdiagstate(ah, 32, &mask, sizeof(&mask), &sp, &rsize)) 1407459bc4f0SSam Leffler return 0; 1408459bc4f0SSam Leffler KASSERT(rsize == sizeof(uint32_t), ("resultsize %u", rsize)); 1409459bc4f0SSam Leffler *hangs = *(uint32_t *)sp; 1410459bc4f0SSam Leffler return 1; 1411459bc4f0SSam Leffler } 1412459bc4f0SSam Leffler 1413b032f27cSSam Leffler static void 1414b032f27cSSam Leffler ath_bmiss_proc(void *arg, int pending) 1415b032f27cSSam Leffler { 1416b032f27cSSam Leffler struct ath_softc *sc = arg; 1417b032f27cSSam Leffler struct ifnet *ifp = sc->sc_ifp; 1418459bc4f0SSam Leffler uint32_t hangs; 1419b032f27cSSam Leffler 1420b032f27cSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: pending %u\n", __func__, pending); 1421459bc4f0SSam Leffler 1422459bc4f0SSam Leffler if (ath_hal_gethangstate(sc->sc_ah, 0xff, &hangs) && hangs != 0) { 1423459bc4f0SSam Leffler if_printf(ifp, "bb hang detected (0x%x), reseting\n", hangs); 1424459bc4f0SSam Leffler ath_reset(ifp); 1425459bc4f0SSam Leffler } else 1426b032f27cSSam Leffler ieee80211_beacon_miss(ifp->if_l2com); 14275591b213SSam Leffler } 14285591b213SSam Leffler 1429724c193aSSam Leffler /* 1430b032f27cSSam Leffler * Handle TKIP MIC setup to deal hardware that doesn't do MIC 1431b032f27cSSam Leffler * calcs together with WME. If necessary disable the crypto 1432b032f27cSSam Leffler * hardware and mark the 802.11 state so keys will be setup 1433b032f27cSSam Leffler * with the MIC work done in software. 1434b032f27cSSam Leffler */ 1435b032f27cSSam Leffler static void 1436b032f27cSSam Leffler ath_settkipmic(struct ath_softc *sc) 1437b032f27cSSam Leffler { 1438b032f27cSSam Leffler struct ifnet *ifp = sc->sc_ifp; 1439b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 1440b032f27cSSam Leffler 1441b032f27cSSam Leffler if ((ic->ic_cryptocaps & IEEE80211_CRYPTO_TKIP) && !sc->sc_wmetkipmic) { 1442b032f27cSSam Leffler if (ic->ic_flags & IEEE80211_F_WME) { 1443b032f27cSSam Leffler ath_hal_settkipmic(sc->sc_ah, AH_FALSE); 1444b032f27cSSam Leffler ic->ic_cryptocaps &= ~IEEE80211_CRYPTO_TKIPMIC; 1445b032f27cSSam Leffler } else { 1446b032f27cSSam Leffler ath_hal_settkipmic(sc->sc_ah, AH_TRUE); 1447b032f27cSSam Leffler ic->ic_cryptocaps |= IEEE80211_CRYPTO_TKIPMIC; 1448b032f27cSSam Leffler } 1449b032f27cSSam Leffler } 1450b032f27cSSam Leffler } 1451b032f27cSSam Leffler 14525591b213SSam Leffler static void 14535591b213SSam Leffler ath_init(void *arg) 14545591b213SSam Leffler { 14555591b213SSam Leffler struct ath_softc *sc = (struct ath_softc *) arg; 1456fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 1457b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 14585591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 14595591b213SSam Leffler HAL_STATUS status; 14605591b213SSam Leffler 1461c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags 0x%x\n", 1462c42a7b7eSSam Leffler __func__, ifp->if_flags); 14635591b213SSam Leffler 1464f0b2a0beSSam Leffler ATH_LOCK(sc); 14655591b213SSam Leffler /* 14665591b213SSam Leffler * Stop anything previously setup. This is safe 14675591b213SSam Leffler * whether this is the first time through or not. 14685591b213SSam Leffler */ 1469c42a7b7eSSam Leffler ath_stop_locked(ifp); 14705591b213SSam Leffler 14715591b213SSam Leffler /* 14725591b213SSam Leffler * The basic interface to setting the hardware in a good 14735591b213SSam Leffler * state is ``reset''. On return the hardware is known to 14745591b213SSam Leffler * be powered up and with interrupts disabled. This must 14755591b213SSam Leffler * be followed by initialization of the appropriate bits 14765591b213SSam Leffler * and then setup of the interrupt mask. 14775591b213SSam Leffler */ 1478b032f27cSSam Leffler ath_settkipmic(sc); 147959efa8b5SSam Leffler if (!ath_hal_reset(ah, sc->sc_opmode, ic->ic_curchan, AH_FALSE, &status)) { 14805591b213SSam Leffler if_printf(ifp, "unable to reset hardware; hal status %u\n", 14815591b213SSam Leffler status); 1482b032f27cSSam Leffler ATH_UNLOCK(sc); 1483b032f27cSSam Leffler return; 14845591b213SSam Leffler } 1485b032f27cSSam Leffler ath_chan_change(sc, ic->ic_curchan); 14865591b213SSam Leffler 14875591b213SSam Leffler /* 1488c59005e9SSam Leffler * Likewise this is set during reset so update 1489c59005e9SSam Leffler * state cached in the driver. 1490c59005e9SSam Leffler */ 1491c59005e9SSam Leffler sc->sc_diversity = ath_hal_getdiversity(ah); 14922dc7fcc4SSam Leffler sc->sc_lastlongcal = 0; 14932dc7fcc4SSam Leffler sc->sc_resetcal = 1; 14942dc7fcc4SSam Leffler sc->sc_lastcalreset = 0; 1495c42a7b7eSSam Leffler 1496c42a7b7eSSam Leffler /* 14975591b213SSam Leffler * Setup the hardware after reset: the key cache 14985591b213SSam Leffler * is filled as needed and the receive engine is 14995591b213SSam Leffler * set going. Frame transmit is handled entirely 15005591b213SSam Leffler * in the frame output path; there's nothing to do 15015591b213SSam Leffler * here except setup the interrupt mask. 15025591b213SSam Leffler */ 15035591b213SSam Leffler if (ath_startrecv(sc) != 0) { 15045591b213SSam Leffler if_printf(ifp, "unable to start recv logic\n"); 1505b032f27cSSam Leffler ATH_UNLOCK(sc); 1506b032f27cSSam Leffler return; 15075591b213SSam Leffler } 15085591b213SSam Leffler 15095591b213SSam Leffler /* 15105591b213SSam Leffler * Enable interrupts. 15115591b213SSam Leffler */ 15125591b213SSam Leffler sc->sc_imask = HAL_INT_RX | HAL_INT_TX 15135591b213SSam Leffler | HAL_INT_RXEOL | HAL_INT_RXORN 15145591b213SSam Leffler | HAL_INT_FATAL | HAL_INT_GLOBAL; 1515c42a7b7eSSam Leffler /* 1516c42a7b7eSSam Leffler * Enable MIB interrupts when there are hardware phy counters. 1517c42a7b7eSSam Leffler * Note we only do this (at the moment) for station mode. 1518c42a7b7eSSam Leffler */ 1519c42a7b7eSSam Leffler if (sc->sc_needmib && ic->ic_opmode == IEEE80211_M_STA) 1520c42a7b7eSSam Leffler sc->sc_imask |= HAL_INT_MIB; 15215591b213SSam Leffler 152213f4c340SRobert Watson ifp->if_drv_flags |= IFF_DRV_RUNNING; 1523b032f27cSSam Leffler ath_hal_intrset(ah, sc->sc_imask); 15245591b213SSam Leffler 1525b032f27cSSam Leffler ATH_UNLOCK(sc); 1526b032f27cSSam Leffler 152786e07743SSam Leffler #ifdef ATH_TX99_DIAG 152886e07743SSam Leffler if (sc->sc_tx99 != NULL) 152986e07743SSam Leffler sc->sc_tx99->start(sc->sc_tx99); 153086e07743SSam Leffler else 153186e07743SSam Leffler #endif 1532b032f27cSSam Leffler ieee80211_start_all(ic); /* start all vap's */ 15335591b213SSam Leffler } 15345591b213SSam Leffler 15355591b213SSam Leffler static void 1536c42a7b7eSSam Leffler ath_stop_locked(struct ifnet *ifp) 15375591b213SSam Leffler { 15385591b213SSam Leffler struct ath_softc *sc = ifp->if_softc; 15395591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 15405591b213SSam Leffler 1541c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid %u if_flags 0x%x\n", 1542c42a7b7eSSam Leffler __func__, sc->sc_invalid, ifp->if_flags); 15435591b213SSam Leffler 1544c42a7b7eSSam Leffler ATH_LOCK_ASSERT(sc); 154513f4c340SRobert Watson if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 15465591b213SSam Leffler /* 15475591b213SSam Leffler * Shutdown the hardware and driver: 1548c42a7b7eSSam Leffler * reset 802.11 state machine 15495591b213SSam Leffler * turn off timers 1550c42a7b7eSSam Leffler * disable interrupts 1551c42a7b7eSSam Leffler * turn off the radio 15525591b213SSam Leffler * clear transmit machinery 15535591b213SSam Leffler * clear receive machinery 15545591b213SSam Leffler * drain and release tx queues 15555591b213SSam Leffler * reclaim beacon resources 15565591b213SSam Leffler * power down hardware 15575591b213SSam Leffler * 15585591b213SSam Leffler * Note that some of this work is not possible if the 15595591b213SSam Leffler * hardware is gone (invalid). 15605591b213SSam Leffler */ 156186e07743SSam Leffler #ifdef ATH_TX99_DIAG 156286e07743SSam Leffler if (sc->sc_tx99 != NULL) 156386e07743SSam Leffler sc->sc_tx99->stop(sc->sc_tx99); 156486e07743SSam Leffler #endif 156513f4c340SRobert Watson ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 15665591b213SSam Leffler ifp->if_timer = 0; 1567c42a7b7eSSam Leffler if (!sc->sc_invalid) { 15683e50ec2cSSam Leffler if (sc->sc_softled) { 15693e50ec2cSSam Leffler callout_stop(&sc->sc_ledtimer); 15703e50ec2cSSam Leffler ath_hal_gpioset(ah, sc->sc_ledpin, 15713e50ec2cSSam Leffler !sc->sc_ledon); 15723e50ec2cSSam Leffler sc->sc_blinking = 0; 15733e50ec2cSSam Leffler } 15745591b213SSam Leffler ath_hal_intrset(ah, 0); 1575c42a7b7eSSam Leffler } 15765591b213SSam Leffler ath_draintxq(sc); 1577c42a7b7eSSam Leffler if (!sc->sc_invalid) { 15785591b213SSam Leffler ath_stoprecv(sc); 1579c42a7b7eSSam Leffler ath_hal_phydisable(ah); 1580c42a7b7eSSam Leffler } else 15815591b213SSam Leffler sc->sc_rxlink = NULL; 1582b032f27cSSam Leffler ath_beacon_free(sc); /* XXX not needed */ 1583c42a7b7eSSam Leffler } 1584c42a7b7eSSam Leffler } 1585c42a7b7eSSam Leffler 1586c42a7b7eSSam Leffler static void 1587c42a7b7eSSam Leffler ath_stop(struct ifnet *ifp) 1588c42a7b7eSSam Leffler { 1589c42a7b7eSSam Leffler struct ath_softc *sc = ifp->if_softc; 1590c42a7b7eSSam Leffler 1591c42a7b7eSSam Leffler ATH_LOCK(sc); 1592c42a7b7eSSam Leffler ath_stop_locked(ifp); 1593f0b2a0beSSam Leffler ATH_UNLOCK(sc); 15945591b213SSam Leffler } 15955591b213SSam Leffler 15965591b213SSam Leffler /* 15975591b213SSam Leffler * Reset the hardware w/o losing operational state. This is 15985591b213SSam Leffler * basically a more efficient way of doing ath_stop, ath_init, 15995591b213SSam Leffler * followed by state transitions to the current 802.11 1600c42a7b7eSSam Leffler * operational state. Used to recover from various errors and 1601c42a7b7eSSam Leffler * to reset or reload hardware state. 16025591b213SSam Leffler */ 1603c42a7b7eSSam Leffler static int 1604c42a7b7eSSam Leffler ath_reset(struct ifnet *ifp) 16055591b213SSam Leffler { 1606c42a7b7eSSam Leffler struct ath_softc *sc = ifp->if_softc; 1607b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 16085591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 16095591b213SSam Leffler HAL_STATUS status; 16105591b213SSam Leffler 16115591b213SSam Leffler ath_hal_intrset(ah, 0); /* disable interrupts */ 16125591b213SSam Leffler ath_draintxq(sc); /* stop xmit side */ 16135591b213SSam Leffler ath_stoprecv(sc); /* stop recv side */ 1614b032f27cSSam Leffler ath_settkipmic(sc); /* configure TKIP MIC handling */ 16155591b213SSam Leffler /* NB: indicate channel change so we do a full reset */ 161659efa8b5SSam Leffler if (!ath_hal_reset(ah, sc->sc_opmode, ic->ic_curchan, AH_TRUE, &status)) 16175591b213SSam Leffler if_printf(ifp, "%s: unable to reset hardware; hal status %u\n", 16185591b213SSam Leffler __func__, status); 1619c59005e9SSam Leffler sc->sc_diversity = ath_hal_getdiversity(ah); 162068e8e04eSSam Leffler if (ath_startrecv(sc) != 0) /* restart recv */ 162168e8e04eSSam Leffler if_printf(ifp, "%s: unable to start recv logic\n", __func__); 1622c42a7b7eSSam Leffler /* 1623c42a7b7eSSam Leffler * We may be doing a reset in response to an ioctl 1624c42a7b7eSSam Leffler * that changes the channel so update any state that 1625c42a7b7eSSam Leffler * might change as a result. 1626c42a7b7eSSam Leffler */ 1627724c193aSSam Leffler ath_chan_change(sc, ic->ic_curchan); 162810ad9a77SSam Leffler if (sc->sc_beacons) { 162910ad9a77SSam Leffler #ifdef ATH_SUPPORT_TDMA 163010ad9a77SSam Leffler if (sc->sc_tdma) 163110ad9a77SSam Leffler ath_tdma_config(sc, NULL); 163210ad9a77SSam Leffler else 163310ad9a77SSam Leffler #endif 1634b032f27cSSam Leffler ath_beacon_config(sc, NULL); /* restart beacons */ 163510ad9a77SSam Leffler } 1636c42a7b7eSSam Leffler ath_hal_intrset(ah, sc->sc_imask); 1637c42a7b7eSSam Leffler 1638c42a7b7eSSam Leffler ath_start(ifp); /* restart xmit */ 1639c42a7b7eSSam Leffler return 0; 16405591b213SSam Leffler } 16415591b213SSam Leffler 164268e8e04eSSam Leffler static int 1643b032f27cSSam Leffler ath_reset_vap(struct ieee80211vap *vap, u_long cmd) 1644b032f27cSSam Leffler { 16454b54a231SSam Leffler struct ieee80211com *ic = vap->iv_ic; 16464b54a231SSam Leffler struct ifnet *ifp = ic->ic_ifp; 16474b54a231SSam Leffler struct ath_softc *sc = ifp->if_softc; 16484b54a231SSam Leffler struct ath_hal *ah = sc->sc_ah; 16494b54a231SSam Leffler 16504b54a231SSam Leffler switch (cmd) { 16514b54a231SSam Leffler case IEEE80211_IOC_TXPOWER: 16524b54a231SSam Leffler /* 16534b54a231SSam Leffler * If per-packet TPC is enabled, then we have nothing 16544b54a231SSam Leffler * to do; otherwise we need to force the global limit. 16554b54a231SSam Leffler * All this can happen directly; no need to reset. 16564b54a231SSam Leffler */ 16574b54a231SSam Leffler if (!ath_hal_gettpc(ah)) 16584b54a231SSam Leffler ath_hal_settxpowlimit(ah, ic->ic_txpowlimit); 16594b54a231SSam Leffler return 0; 16604b54a231SSam Leffler } 16614b54a231SSam Leffler return ath_reset(ifp); 1662b032f27cSSam Leffler } 1663b032f27cSSam Leffler 1664b032f27cSSam Leffler static int 166568e8e04eSSam Leffler ath_ff_always(struct ath_txq *txq, struct ath_buf *bf) 166668e8e04eSSam Leffler { 166768e8e04eSSam Leffler return 0; 166868e8e04eSSam Leffler } 166968e8e04eSSam Leffler 167068e8e04eSSam Leffler #if 0 167168e8e04eSSam Leffler static int 167268e8e04eSSam Leffler ath_ff_ageflushtestdone(struct ath_txq *txq, struct ath_buf *bf) 167368e8e04eSSam Leffler { 167468e8e04eSSam Leffler return (txq->axq_curage - bf->bf_age) < ATH_FF_STAGEMAX; 167568e8e04eSSam Leffler } 167668e8e04eSSam Leffler #endif 167768e8e04eSSam Leffler 167868e8e04eSSam Leffler /* 167968e8e04eSSam Leffler * Flush FF staging queue. 168068e8e04eSSam Leffler */ 168168e8e04eSSam Leffler static void 168268e8e04eSSam Leffler ath_ff_stageq_flush(struct ath_softc *sc, struct ath_txq *txq, 168368e8e04eSSam Leffler int (*ath_ff_flushdonetest)(struct ath_txq *txq, struct ath_buf *bf)) 168468e8e04eSSam Leffler { 168568e8e04eSSam Leffler struct ath_buf *bf; 168668e8e04eSSam Leffler struct ieee80211_node *ni; 168768e8e04eSSam Leffler int pktlen, pri; 168868e8e04eSSam Leffler 168968e8e04eSSam Leffler for (;;) { 169068e8e04eSSam Leffler ATH_TXQ_LOCK(txq); 169168e8e04eSSam Leffler /* 169268e8e04eSSam Leffler * Go from the back (oldest) to front so we can 169368e8e04eSSam Leffler * stop early based on the age of the entry. 169468e8e04eSSam Leffler */ 169568e8e04eSSam Leffler bf = TAILQ_LAST(&txq->axq_stageq, axq_headtype); 169668e8e04eSSam Leffler if (bf == NULL || ath_ff_flushdonetest(txq, bf)) { 169768e8e04eSSam Leffler ATH_TXQ_UNLOCK(txq); 169868e8e04eSSam Leffler break; 169968e8e04eSSam Leffler } 170068e8e04eSSam Leffler 170168e8e04eSSam Leffler ni = bf->bf_node; 170268e8e04eSSam Leffler pri = M_WME_GETAC(bf->bf_m); 170368e8e04eSSam Leffler KASSERT(ATH_NODE(ni)->an_ff_buf[pri], 170468e8e04eSSam Leffler ("no bf on staging queue %p", bf)); 170568e8e04eSSam Leffler ATH_NODE(ni)->an_ff_buf[pri] = NULL; 170668e8e04eSSam Leffler TAILQ_REMOVE(&txq->axq_stageq, bf, bf_stagelist); 170768e8e04eSSam Leffler 170868e8e04eSSam Leffler ATH_TXQ_UNLOCK(txq); 170968e8e04eSSam Leffler 171068e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_FF, "%s: flush frame, age %u\n", 171168e8e04eSSam Leffler __func__, bf->bf_age); 171268e8e04eSSam Leffler 171368e8e04eSSam Leffler sc->sc_stats.ast_ff_flush++; 171468e8e04eSSam Leffler 171568e8e04eSSam Leffler /* encap and xmit */ 1716b032f27cSSam Leffler bf->bf_m = ieee80211_encap(ni, bf->bf_m); 171768e8e04eSSam Leffler if (bf->bf_m == NULL) { 171868e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT | ATH_DEBUG_FF, 171968e8e04eSSam Leffler "%s: discard, encapsulation failure\n", 172068e8e04eSSam Leffler __func__); 172168e8e04eSSam Leffler sc->sc_stats.ast_tx_encap++; 172268e8e04eSSam Leffler goto bad; 172368e8e04eSSam Leffler } 172468e8e04eSSam Leffler pktlen = bf->bf_m->m_pkthdr.len; /* NB: don't reference below */ 172568e8e04eSSam Leffler if (ath_tx_start(sc, ni, bf, bf->bf_m) == 0) { 172668e8e04eSSam Leffler #if 0 /*XXX*/ 172768e8e04eSSam Leffler ifp->if_opackets++; 172868e8e04eSSam Leffler #endif 172968e8e04eSSam Leffler continue; 173068e8e04eSSam Leffler } 173168e8e04eSSam Leffler bad: 173268e8e04eSSam Leffler if (ni != NULL) 173368e8e04eSSam Leffler ieee80211_free_node(ni); 173468e8e04eSSam Leffler bf->bf_node = NULL; 173568e8e04eSSam Leffler if (bf->bf_m != NULL) { 173668e8e04eSSam Leffler m_freem(bf->bf_m); 173768e8e04eSSam Leffler bf->bf_m = NULL; 173868e8e04eSSam Leffler } 173968e8e04eSSam Leffler 174068e8e04eSSam Leffler ATH_TXBUF_LOCK(sc); 174110ad9a77SSam Leffler STAILQ_INSERT_HEAD(&sc->sc_txbuf, bf, bf_list); 174268e8e04eSSam Leffler ATH_TXBUF_UNLOCK(sc); 174368e8e04eSSam Leffler } 174468e8e04eSSam Leffler } 174568e8e04eSSam Leffler 174668e8e04eSSam Leffler static __inline u_int32_t 174768e8e04eSSam Leffler ath_ff_approx_txtime(struct ath_softc *sc, struct ath_node *an, struct mbuf *m) 174868e8e04eSSam Leffler { 1749b032f27cSSam Leffler struct ieee80211com *ic = sc->sc_ifp->if_l2com; 175068e8e04eSSam Leffler u_int32_t framelen; 175168e8e04eSSam Leffler struct ath_buf *bf; 175268e8e04eSSam Leffler 175368e8e04eSSam Leffler /* 175468e8e04eSSam Leffler * Approximate the frame length to be transmitted. A swag to add 175568e8e04eSSam Leffler * the following maximal values to the skb payload: 175668e8e04eSSam Leffler * - 32: 802.11 encap + CRC 175768e8e04eSSam Leffler * - 24: encryption overhead (if wep bit) 175868e8e04eSSam Leffler * - 4 + 6: fast-frame header and padding 175968e8e04eSSam Leffler * - 16: 2 LLC FF tunnel headers 176068e8e04eSSam Leffler * - 14: 1 802.3 FF tunnel header (skb already accounts for 2nd) 176168e8e04eSSam Leffler */ 176268e8e04eSSam Leffler framelen = m->m_pkthdr.len + 32 + 4 + 6 + 16 + 14; 1763b032f27cSSam Leffler if (ic->ic_flags & IEEE80211_F_PRIVACY) 176468e8e04eSSam Leffler framelen += 24; 176568e8e04eSSam Leffler bf = an->an_ff_buf[M_WME_GETAC(m)]; 176668e8e04eSSam Leffler if (bf != NULL) 176768e8e04eSSam Leffler framelen += bf->bf_m->m_pkthdr.len; 176868e8e04eSSam Leffler return ath_hal_computetxtime(sc->sc_ah, sc->sc_currates, framelen, 176968e8e04eSSam Leffler sc->sc_lastdatarix, AH_FALSE); 177068e8e04eSSam Leffler } 177168e8e04eSSam Leffler 177268e8e04eSSam Leffler /* 177368e8e04eSSam Leffler * Determine if a data frame may be aggregated via ff tunnelling. 177468e8e04eSSam Leffler * Note the caller is responsible for checking if the destination 177568e8e04eSSam Leffler * supports fast frames. 177668e8e04eSSam Leffler * 177768e8e04eSSam Leffler * NB: allowing EAPOL frames to be aggregated with other unicast traffic. 177868e8e04eSSam Leffler * Do 802.1x EAPOL frames proceed in the clear? Then they couldn't 177968e8e04eSSam Leffler * be aggregated with other types of frames when encryption is on? 178068e8e04eSSam Leffler * 178168e8e04eSSam Leffler * NB: assumes lock on an_ff_buf effectively held by txq lock mechanism. 178268e8e04eSSam Leffler */ 178368e8e04eSSam Leffler static __inline int 178468e8e04eSSam Leffler ath_ff_can_aggregate(struct ath_softc *sc, 178568e8e04eSSam Leffler struct ath_node *an, struct mbuf *m, int *flushq) 178668e8e04eSSam Leffler { 1787b032f27cSSam Leffler struct ieee80211com *ic = sc->sc_ifp->if_l2com; 178868e8e04eSSam Leffler struct ath_txq *txq; 178968e8e04eSSam Leffler u_int32_t txoplimit; 179068e8e04eSSam Leffler u_int pri; 179168e8e04eSSam Leffler 179268e8e04eSSam Leffler *flushq = 0; 179368e8e04eSSam Leffler 179468e8e04eSSam Leffler /* 179568e8e04eSSam Leffler * If there is no frame to combine with and the txq has 179668e8e04eSSam Leffler * fewer frames than the minimum required; then do not 179768e8e04eSSam Leffler * attempt to aggregate this frame. 179868e8e04eSSam Leffler */ 179968e8e04eSSam Leffler pri = M_WME_GETAC(m); 180068e8e04eSSam Leffler txq = sc->sc_ac2q[pri]; 180168e8e04eSSam Leffler if (an->an_ff_buf[pri] == NULL && txq->axq_depth < sc->sc_fftxqmin) 180268e8e04eSSam Leffler return 0; 180368e8e04eSSam Leffler /* 180468e8e04eSSam Leffler * When not in station mode never aggregate a multicast 180568e8e04eSSam Leffler * frame; this insures, for example, that a combined frame 180668e8e04eSSam Leffler * does not require multiple encryption keys when using 180768e8e04eSSam Leffler * 802.1x/WPA. 180868e8e04eSSam Leffler */ 180968e8e04eSSam Leffler if (ic->ic_opmode != IEEE80211_M_STA && 181068e8e04eSSam Leffler ETHER_IS_MULTICAST(mtod(m, struct ether_header *)->ether_dhost)) 181168e8e04eSSam Leffler return 0; 181268e8e04eSSam Leffler /* 181368e8e04eSSam Leffler * Consult the max bursting interval to insure a combined 181468e8e04eSSam Leffler * frame fits within the TxOp window. 181568e8e04eSSam Leffler */ 181668e8e04eSSam Leffler txoplimit = IEEE80211_TXOP_TO_US( 181768e8e04eSSam Leffler ic->ic_wme.wme_chanParams.cap_wmeParams[pri].wmep_txopLimit); 181868e8e04eSSam Leffler if (txoplimit != 0 && ath_ff_approx_txtime(sc, an, m) > txoplimit) { 181968e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT | ATH_DEBUG_FF, 182068e8e04eSSam Leffler "%s: FF TxOp violation\n", __func__); 182168e8e04eSSam Leffler if (an->an_ff_buf[pri] != NULL) 182268e8e04eSSam Leffler *flushq = 1; 182368e8e04eSSam Leffler return 0; 182468e8e04eSSam Leffler } 182568e8e04eSSam Leffler return 1; /* try to aggregate */ 182668e8e04eSSam Leffler } 182768e8e04eSSam Leffler 182868e8e04eSSam Leffler /* 182968e8e04eSSam Leffler * Check if the supplied frame can be partnered with an existing 183068e8e04eSSam Leffler * or pending frame. Return a reference to any frame that should be 183168e8e04eSSam Leffler * sent on return; otherwise return NULL. 183268e8e04eSSam Leffler */ 183368e8e04eSSam Leffler static struct mbuf * 183468e8e04eSSam Leffler ath_ff_check(struct ath_softc *sc, struct ath_txq *txq, 183568e8e04eSSam Leffler struct ath_buf *bf, struct mbuf *m, struct ieee80211_node *ni) 183668e8e04eSSam Leffler { 183768e8e04eSSam Leffler struct ath_node *an = ATH_NODE(ni); 183868e8e04eSSam Leffler struct ath_buf *bfstaged; 183968e8e04eSSam Leffler int ff_flush, pri; 184068e8e04eSSam Leffler 184168e8e04eSSam Leffler /* 184268e8e04eSSam Leffler * Check if the supplied frame can be aggregated. 184368e8e04eSSam Leffler * 184468e8e04eSSam Leffler * NB: we use the txq lock to protect references to 184568e8e04eSSam Leffler * an->an_ff_txbuf in ath_ff_can_aggregate(). 184668e8e04eSSam Leffler */ 184768e8e04eSSam Leffler ATH_TXQ_LOCK(txq); 184868e8e04eSSam Leffler pri = M_WME_GETAC(m); 184968e8e04eSSam Leffler if (ath_ff_can_aggregate(sc, an, m, &ff_flush)) { 185068e8e04eSSam Leffler struct ath_buf *bfstaged = an->an_ff_buf[pri]; 185168e8e04eSSam Leffler if (bfstaged != NULL) { 185268e8e04eSSam Leffler /* 185368e8e04eSSam Leffler * A frame is available for partnering; remove 185468e8e04eSSam Leffler * it, chain it to this one, and encapsulate. 185568e8e04eSSam Leffler */ 185668e8e04eSSam Leffler an->an_ff_buf[pri] = NULL; 185768e8e04eSSam Leffler TAILQ_REMOVE(&txq->axq_stageq, bfstaged, bf_stagelist); 185868e8e04eSSam Leffler ATH_TXQ_UNLOCK(txq); 185968e8e04eSSam Leffler 186068e8e04eSSam Leffler /* 186168e8e04eSSam Leffler * Chain mbufs and add FF magic. 186268e8e04eSSam Leffler */ 186368e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_FF, 186468e8e04eSSam Leffler "[%s] aggregate fast-frame, age %u\n", 186568e8e04eSSam Leffler ether_sprintf(ni->ni_macaddr), txq->axq_curage); 186668e8e04eSSam Leffler m->m_nextpkt = NULL; 186768e8e04eSSam Leffler bfstaged->bf_m->m_nextpkt = m; 186868e8e04eSSam Leffler m = bfstaged->bf_m; 186968e8e04eSSam Leffler bfstaged->bf_m = NULL; 187068e8e04eSSam Leffler m->m_flags |= M_FF; 187168e8e04eSSam Leffler /* 187268e8e04eSSam Leffler * Release the node reference held while 187368e8e04eSSam Leffler * the packet sat on an_ff_buf[] 187468e8e04eSSam Leffler */ 187568e8e04eSSam Leffler bfstaged->bf_node = NULL; 187668e8e04eSSam Leffler ieee80211_free_node(ni); 187768e8e04eSSam Leffler 187868e8e04eSSam Leffler /* 187968e8e04eSSam Leffler * Return bfstaged to the free list. 188068e8e04eSSam Leffler */ 188168e8e04eSSam Leffler ATH_TXBUF_LOCK(sc); 188210ad9a77SSam Leffler STAILQ_INSERT_HEAD(&sc->sc_txbuf, bfstaged, bf_list); 188368e8e04eSSam Leffler ATH_TXBUF_UNLOCK(sc); 188468e8e04eSSam Leffler 188568e8e04eSSam Leffler return m; /* ready to go */ 188668e8e04eSSam Leffler } else { 188768e8e04eSSam Leffler /* 188868e8e04eSSam Leffler * No frame available, queue this frame to wait 188968e8e04eSSam Leffler * for a partner. Note that we hold the buffer 189068e8e04eSSam Leffler * and a reference to the node; we need the 189168e8e04eSSam Leffler * buffer in particular so we're certain we 189268e8e04eSSam Leffler * can flush the frame at a later time. 189368e8e04eSSam Leffler */ 189468e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_FF, 189568e8e04eSSam Leffler "[%s] stage fast-frame, age %u\n", 189668e8e04eSSam Leffler ether_sprintf(ni->ni_macaddr), txq->axq_curage); 189768e8e04eSSam Leffler 189868e8e04eSSam Leffler bf->bf_m = m; 189968e8e04eSSam Leffler bf->bf_node = ni; /* NB: held reference */ 190068e8e04eSSam Leffler bf->bf_age = txq->axq_curage; 190168e8e04eSSam Leffler an->an_ff_buf[pri] = bf; 190268e8e04eSSam Leffler TAILQ_INSERT_HEAD(&txq->axq_stageq, bf, bf_stagelist); 190368e8e04eSSam Leffler ATH_TXQ_UNLOCK(txq); 190468e8e04eSSam Leffler 190568e8e04eSSam Leffler return NULL; /* consumed */ 190668e8e04eSSam Leffler } 190768e8e04eSSam Leffler } 190868e8e04eSSam Leffler /* 190968e8e04eSSam Leffler * Frame could not be aggregated, it needs to be returned 191068e8e04eSSam Leffler * to the caller for immediate transmission. In addition 191168e8e04eSSam Leffler * we check if we should first flush a frame from the 191268e8e04eSSam Leffler * staging queue before sending this one. 191368e8e04eSSam Leffler * 191468e8e04eSSam Leffler * NB: ath_ff_can_aggregate only marks ff_flush if a frame 191568e8e04eSSam Leffler * is present to flush. 191668e8e04eSSam Leffler */ 191768e8e04eSSam Leffler if (ff_flush) { 191868e8e04eSSam Leffler int pktlen; 191968e8e04eSSam Leffler 192068e8e04eSSam Leffler bfstaged = an->an_ff_buf[pri]; 192168e8e04eSSam Leffler an->an_ff_buf[pri] = NULL; 192268e8e04eSSam Leffler TAILQ_REMOVE(&txq->axq_stageq, bfstaged, bf_stagelist); 192368e8e04eSSam Leffler ATH_TXQ_UNLOCK(txq); 192468e8e04eSSam Leffler 192568e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_FF, "[%s] flush staged frame\n", 192668e8e04eSSam Leffler ether_sprintf(an->an_node.ni_macaddr)); 192768e8e04eSSam Leffler 192868e8e04eSSam Leffler /* encap and xmit */ 1929b032f27cSSam Leffler bfstaged->bf_m = ieee80211_encap(ni, bfstaged->bf_m); 193068e8e04eSSam Leffler if (bfstaged->bf_m == NULL) { 193168e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT | ATH_DEBUG_FF, 193268e8e04eSSam Leffler "%s: discard, encap failure\n", __func__); 193368e8e04eSSam Leffler sc->sc_stats.ast_tx_encap++; 193468e8e04eSSam Leffler goto ff_flushbad; 193568e8e04eSSam Leffler } 193668e8e04eSSam Leffler pktlen = bfstaged->bf_m->m_pkthdr.len; 193768e8e04eSSam Leffler if (ath_tx_start(sc, ni, bfstaged, bfstaged->bf_m)) { 193868e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT, 193968e8e04eSSam Leffler "%s: discard, xmit failure\n", __func__); 194068e8e04eSSam Leffler ff_flushbad: 194168e8e04eSSam Leffler /* 194268e8e04eSSam Leffler * Unable to transmit frame that was on the staging 194368e8e04eSSam Leffler * queue. Reclaim the node reference and other 194468e8e04eSSam Leffler * resources. 194568e8e04eSSam Leffler */ 194668e8e04eSSam Leffler if (ni != NULL) 194768e8e04eSSam Leffler ieee80211_free_node(ni); 194868e8e04eSSam Leffler bfstaged->bf_node = NULL; 194968e8e04eSSam Leffler if (bfstaged->bf_m != NULL) { 195068e8e04eSSam Leffler m_freem(bfstaged->bf_m); 195168e8e04eSSam Leffler bfstaged->bf_m = NULL; 195268e8e04eSSam Leffler } 195368e8e04eSSam Leffler 195468e8e04eSSam Leffler ATH_TXBUF_LOCK(sc); 195510ad9a77SSam Leffler STAILQ_INSERT_HEAD(&sc->sc_txbuf, bfstaged, bf_list); 195668e8e04eSSam Leffler ATH_TXBUF_UNLOCK(sc); 195768e8e04eSSam Leffler } else { 195868e8e04eSSam Leffler #if 0 195968e8e04eSSam Leffler ifp->if_opackets++; 196068e8e04eSSam Leffler #endif 196168e8e04eSSam Leffler } 196268e8e04eSSam Leffler } else { 196368e8e04eSSam Leffler if (an->an_ff_buf[pri] != NULL) { 196468e8e04eSSam Leffler /* 196568e8e04eSSam Leffler * XXX: out-of-order condition only occurs for AP 196668e8e04eSSam Leffler * mode and multicast. There may be no valid way 196768e8e04eSSam Leffler * to get this condition. 196868e8e04eSSam Leffler */ 196968e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_FF, "[%s] out-of-order frame\n", 197068e8e04eSSam Leffler ether_sprintf(an->an_node.ni_macaddr)); 197168e8e04eSSam Leffler /* XXX stat */ 197268e8e04eSSam Leffler } 197368e8e04eSSam Leffler ATH_TXQ_UNLOCK(txq); 197468e8e04eSSam Leffler } 197568e8e04eSSam Leffler return m; 197668e8e04eSSam Leffler } 197768e8e04eSSam Leffler 197810ad9a77SSam Leffler static struct ath_buf * 197910ad9a77SSam Leffler _ath_getbuf_locked(struct ath_softc *sc) 198010ad9a77SSam Leffler { 198110ad9a77SSam Leffler struct ath_buf *bf; 198210ad9a77SSam Leffler 198310ad9a77SSam Leffler ATH_TXBUF_LOCK_ASSERT(sc); 198410ad9a77SSam Leffler 198510ad9a77SSam Leffler bf = STAILQ_FIRST(&sc->sc_txbuf); 198610ad9a77SSam Leffler if (bf != NULL && (bf->bf_flags & ATH_BUF_BUSY) == 0) 198710ad9a77SSam Leffler STAILQ_REMOVE_HEAD(&sc->sc_txbuf, bf_list); 198810ad9a77SSam Leffler else 198910ad9a77SSam Leffler bf = NULL; 199010ad9a77SSam Leffler if (bf == NULL) { 199110ad9a77SSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT, "%s: %s\n", __func__, 199210ad9a77SSam Leffler STAILQ_FIRST(&sc->sc_txbuf) == NULL ? 199310ad9a77SSam Leffler "out of xmit buffers" : "xmit buffer busy"); 199410ad9a77SSam Leffler sc->sc_stats.ast_tx_nobuf++; 199510ad9a77SSam Leffler } 199610ad9a77SSam Leffler return bf; 199710ad9a77SSam Leffler } 199810ad9a77SSam Leffler 199910ad9a77SSam Leffler static struct ath_buf * 200010ad9a77SSam Leffler ath_getbuf(struct ath_softc *sc) 200110ad9a77SSam Leffler { 200210ad9a77SSam Leffler struct ath_buf *bf; 200310ad9a77SSam Leffler 200410ad9a77SSam Leffler ATH_TXBUF_LOCK(sc); 200510ad9a77SSam Leffler bf = _ath_getbuf_locked(sc); 200610ad9a77SSam Leffler if (bf == NULL) { 200710ad9a77SSam Leffler struct ifnet *ifp = sc->sc_ifp; 200810ad9a77SSam Leffler 200910ad9a77SSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT, "%s: stop queue\n", __func__); 201010ad9a77SSam Leffler sc->sc_stats.ast_tx_qstop++; 201110ad9a77SSam Leffler ifp->if_drv_flags |= IFF_DRV_OACTIVE; 201210ad9a77SSam Leffler } 201310ad9a77SSam Leffler ATH_TXBUF_UNLOCK(sc); 201410ad9a77SSam Leffler return bf; 201510ad9a77SSam Leffler } 201610ad9a77SSam Leffler 201768e8e04eSSam Leffler /* 201868e8e04eSSam Leffler * Cleanup driver resources when we run out of buffers 201968e8e04eSSam Leffler * while processing fragments; return the tx buffers 202068e8e04eSSam Leffler * allocated and drop node references. 202168e8e04eSSam Leffler */ 202268e8e04eSSam Leffler static void 202368e8e04eSSam Leffler ath_txfrag_cleanup(struct ath_softc *sc, 202468e8e04eSSam Leffler ath_bufhead *frags, struct ieee80211_node *ni) 202568e8e04eSSam Leffler { 202668e8e04eSSam Leffler struct ath_buf *bf, *next; 202768e8e04eSSam Leffler 202868e8e04eSSam Leffler ATH_TXBUF_LOCK_ASSERT(sc); 202968e8e04eSSam Leffler 203068e8e04eSSam Leffler STAILQ_FOREACH_SAFE(bf, frags, bf_list, next) { 203168e8e04eSSam Leffler /* NB: bf assumed clean */ 203268e8e04eSSam Leffler STAILQ_REMOVE_HEAD(frags, bf_list); 203310ad9a77SSam Leffler STAILQ_INSERT_HEAD(&sc->sc_txbuf, bf, bf_list); 203468e8e04eSSam Leffler ieee80211_node_decref(ni); 203568e8e04eSSam Leffler } 203668e8e04eSSam Leffler } 203768e8e04eSSam Leffler 203868e8e04eSSam Leffler /* 203968e8e04eSSam Leffler * Setup xmit of a fragmented frame. Allocate a buffer 204068e8e04eSSam Leffler * for each frag and bump the node reference count to 204168e8e04eSSam Leffler * reflect the held reference to be setup by ath_tx_start. 204268e8e04eSSam Leffler */ 204368e8e04eSSam Leffler static int 204468e8e04eSSam Leffler ath_txfrag_setup(struct ath_softc *sc, ath_bufhead *frags, 204568e8e04eSSam Leffler struct mbuf *m0, struct ieee80211_node *ni) 204668e8e04eSSam Leffler { 204768e8e04eSSam Leffler struct mbuf *m; 204868e8e04eSSam Leffler struct ath_buf *bf; 204968e8e04eSSam Leffler 205068e8e04eSSam Leffler ATH_TXBUF_LOCK(sc); 205168e8e04eSSam Leffler for (m = m0->m_nextpkt; m != NULL; m = m->m_nextpkt) { 205210ad9a77SSam Leffler bf = _ath_getbuf_locked(sc); 205368e8e04eSSam Leffler if (bf == NULL) { /* out of buffers, cleanup */ 205468e8e04eSSam Leffler ath_txfrag_cleanup(sc, frags, ni); 205568e8e04eSSam Leffler break; 205668e8e04eSSam Leffler } 205768e8e04eSSam Leffler ieee80211_node_incref(ni); 205868e8e04eSSam Leffler STAILQ_INSERT_TAIL(frags, bf, bf_list); 205968e8e04eSSam Leffler } 206068e8e04eSSam Leffler ATH_TXBUF_UNLOCK(sc); 206168e8e04eSSam Leffler 206268e8e04eSSam Leffler return !STAILQ_EMPTY(frags); 206368e8e04eSSam Leffler } 206468e8e04eSSam Leffler 20655591b213SSam Leffler static void 20665591b213SSam Leffler ath_start(struct ifnet *ifp) 20675591b213SSam Leffler { 20685591b213SSam Leffler struct ath_softc *sc = ifp->if_softc; 2069b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 20705591b213SSam Leffler struct ieee80211_node *ni; 20715591b213SSam Leffler struct ath_buf *bf; 207268e8e04eSSam Leffler struct mbuf *m, *next; 207368e8e04eSSam Leffler struct ath_txq *txq; 207468e8e04eSSam Leffler ath_bufhead frags; 207568e8e04eSSam Leffler int pri; 20765591b213SSam Leffler 207713f4c340SRobert Watson if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || sc->sc_invalid) 20785591b213SSam Leffler return; 20795591b213SSam Leffler for (;;) { 20805591b213SSam Leffler /* 20815591b213SSam Leffler * Grab a TX buffer and associated resources. 20825591b213SSam Leffler */ 208310ad9a77SSam Leffler bf = ath_getbuf(sc); 208410ad9a77SSam Leffler if (bf == NULL) 20855591b213SSam Leffler break; 20862b9411e2SSam Leffler 2087b032f27cSSam Leffler IFQ_DEQUEUE(&ifp->if_snd, m); 2088b032f27cSSam Leffler if (m == NULL) { 2089b032f27cSSam Leffler ATH_TXBUF_LOCK(sc); 209010ad9a77SSam Leffler STAILQ_INSERT_HEAD(&sc->sc_txbuf, bf, bf_list); 2091b032f27cSSam Leffler ATH_TXBUF_UNLOCK(sc); 2092b032f27cSSam Leffler break; 2093b032f27cSSam Leffler } 209468e8e04eSSam Leffler STAILQ_INIT(&frags); 2095b032f27cSSam Leffler ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; 209668e8e04eSSam Leffler pri = M_WME_GETAC(m); 209768e8e04eSSam Leffler txq = sc->sc_ac2q[pri]; 2098d6f57961SSam Leffler if (IEEE80211_ATH_CAP(ni->ni_vap, ni, IEEE80211_NODE_FF)) { 209968e8e04eSSam Leffler /* 210068e8e04eSSam Leffler * Check queue length; if too deep drop this 210168e8e04eSSam Leffler * frame (tail drop considered good). 210268e8e04eSSam Leffler */ 210368e8e04eSSam Leffler if (txq->axq_depth >= sc->sc_fftxqmax) { 210468e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_FF, 210568e8e04eSSam Leffler "[%s] tail drop on q %u depth %u\n", 210668e8e04eSSam Leffler ether_sprintf(ni->ni_macaddr), 210768e8e04eSSam Leffler txq->axq_qnum, txq->axq_depth); 210868e8e04eSSam Leffler sc->sc_stats.ast_tx_qfull++; 210968e8e04eSSam Leffler m_freem(m); 211068e8e04eSSam Leffler goto reclaim; 211168e8e04eSSam Leffler } 211268e8e04eSSam Leffler m = ath_ff_check(sc, txq, bf, m, ni); 211368e8e04eSSam Leffler if (m == NULL) { 211468e8e04eSSam Leffler /* NB: ni ref & bf held on stageq */ 211568e8e04eSSam Leffler continue; 211668e8e04eSSam Leffler } 211768e8e04eSSam Leffler } 21185591b213SSam Leffler ifp->if_opackets++; 21195591b213SSam Leffler /* 21205591b213SSam Leffler * Encapsulate the packet in prep for transmission. 21215591b213SSam Leffler */ 2122b032f27cSSam Leffler m = ieee80211_encap(ni, m); 21235591b213SSam Leffler if (m == NULL) { 2124370572d9SSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT, 2125b032f27cSSam Leffler "%s: encapsulation failure\n", __func__); 21265591b213SSam Leffler sc->sc_stats.ast_tx_encap++; 21275591b213SSam Leffler goto bad; 21285591b213SSam Leffler } 212968e8e04eSSam Leffler /* 213068e8e04eSSam Leffler * Check for fragmentation. If this frame 213168e8e04eSSam Leffler * has been broken up verify we have enough 213268e8e04eSSam Leffler * buffers to send all the fragments so all 213368e8e04eSSam Leffler * go out or none... 213468e8e04eSSam Leffler */ 213568e8e04eSSam Leffler if ((m->m_flags & M_FRAG) && 213668e8e04eSSam Leffler !ath_txfrag_setup(sc, &frags, m, ni)) { 213768e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT, 213868e8e04eSSam Leffler "%s: out of txfrag buffers\n", __func__); 213968e8e04eSSam Leffler ic->ic_stats.is_tx_nobuf++; /* XXX */ 214068e8e04eSSam Leffler ath_freetx(m); 214168e8e04eSSam Leffler goto bad; 214268e8e04eSSam Leffler } 214368e8e04eSSam Leffler nextfrag: 214468e8e04eSSam Leffler /* 214568e8e04eSSam Leffler * Pass the frame to the h/w for transmission. 214668e8e04eSSam Leffler * Fragmented frames have each frag chained together 214768e8e04eSSam Leffler * with m_nextpkt. We know there are sufficient ath_buf's 214868e8e04eSSam Leffler * to send all the frags because of work done by 214968e8e04eSSam Leffler * ath_txfrag_setup. We leave m_nextpkt set while 215068e8e04eSSam Leffler * calling ath_tx_start so it can use it to extend the 215168e8e04eSSam Leffler * the tx duration to cover the subsequent frag and 215268e8e04eSSam Leffler * so it can reclaim all the mbufs in case of an error; 215368e8e04eSSam Leffler * ath_tx_start clears m_nextpkt once it commits to 215468e8e04eSSam Leffler * handing the frame to the hardware. 215568e8e04eSSam Leffler */ 215668e8e04eSSam Leffler next = m->m_nextpkt; 21575591b213SSam Leffler if (ath_tx_start(sc, ni, bf, m)) { 21585591b213SSam Leffler bad: 21595591b213SSam Leffler ifp->if_oerrors++; 2160c42a7b7eSSam Leffler reclaim: 216168e8e04eSSam Leffler bf->bf_m = NULL; 216268e8e04eSSam Leffler bf->bf_node = NULL; 2163c42a7b7eSSam Leffler ATH_TXBUF_LOCK(sc); 216410ad9a77SSam Leffler STAILQ_INSERT_HEAD(&sc->sc_txbuf, bf, bf_list); 216568e8e04eSSam Leffler ath_txfrag_cleanup(sc, &frags, ni); 2166c42a7b7eSSam Leffler ATH_TXBUF_UNLOCK(sc); 2167c42a7b7eSSam Leffler if (ni != NULL) 2168c42a7b7eSSam Leffler ieee80211_free_node(ni); 21695591b213SSam Leffler continue; 21705591b213SSam Leffler } 217168e8e04eSSam Leffler if (next != NULL) { 217268e8e04eSSam Leffler /* 217368e8e04eSSam Leffler * Beware of state changing between frags. 217468e8e04eSSam Leffler * XXX check sta power-save state? 217568e8e04eSSam Leffler */ 2176b032f27cSSam Leffler if (ni->ni_vap->iv_state != IEEE80211_S_RUN) { 217768e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT, 217868e8e04eSSam Leffler "%s: flush fragmented packet, state %s\n", 217968e8e04eSSam Leffler __func__, 2180b032f27cSSam Leffler ieee80211_state_name[ni->ni_vap->iv_state]); 218168e8e04eSSam Leffler ath_freetx(next); 218268e8e04eSSam Leffler goto reclaim; 218368e8e04eSSam Leffler } 218468e8e04eSSam Leffler m = next; 218568e8e04eSSam Leffler bf = STAILQ_FIRST(&frags); 218668e8e04eSSam Leffler KASSERT(bf != NULL, ("no buf for txfrag")); 218768e8e04eSSam Leffler STAILQ_REMOVE_HEAD(&frags, bf_list); 218868e8e04eSSam Leffler goto nextfrag; 218968e8e04eSSam Leffler } 21905591b213SSam Leffler 219168e8e04eSSam Leffler ifp->if_timer = 5; 219268e8e04eSSam Leffler #if 0 219368e8e04eSSam Leffler /* 219468e8e04eSSam Leffler * Flush stale frames from the fast-frame staging queue. 219568e8e04eSSam Leffler */ 219668e8e04eSSam Leffler if (ic->ic_opmode != IEEE80211_M_STA) 219768e8e04eSSam Leffler ath_ff_stageq_flush(sc, txq, ath_ff_ageflushtestdone); 219868e8e04eSSam Leffler #endif 21995591b213SSam Leffler } 22005591b213SSam Leffler } 22015591b213SSam Leffler 22025591b213SSam Leffler static int 22035591b213SSam Leffler ath_media_change(struct ifnet *ifp) 22045591b213SSam Leffler { 2205b032f27cSSam Leffler int error = ieee80211_media_change(ifp); 2206b032f27cSSam Leffler /* NB: only the fixed rate can change and that doesn't need a reset */ 2207b032f27cSSam Leffler return (error == ENETRESET ? 0 : error); 22085591b213SSam Leffler } 22095591b213SSam Leffler 2210a585a9a1SSam Leffler #ifdef ATH_DEBUG 2211c42a7b7eSSam Leffler static void 22125901d2d3SSam Leffler ath_keyprint(struct ath_softc *sc, const char *tag, u_int ix, 2213c42a7b7eSSam Leffler const HAL_KEYVAL *hk, const u_int8_t mac[IEEE80211_ADDR_LEN]) 22145591b213SSam Leffler { 2215c42a7b7eSSam Leffler static const char *ciphers[] = { 2216c42a7b7eSSam Leffler "WEP", 2217c42a7b7eSSam Leffler "AES-OCB", 2218c42a7b7eSSam Leffler "AES-CCM", 2219c42a7b7eSSam Leffler "CKIP", 2220c42a7b7eSSam Leffler "TKIP", 2221c42a7b7eSSam Leffler "CLR", 2222c42a7b7eSSam Leffler }; 2223c42a7b7eSSam Leffler int i, n; 22245591b213SSam Leffler 2225c42a7b7eSSam Leffler printf("%s: [%02u] %-7s ", tag, ix, ciphers[hk->kv_type]); 2226c42a7b7eSSam Leffler for (i = 0, n = hk->kv_len; i < n; i++) 2227c42a7b7eSSam Leffler printf("%02x", hk->kv_val[i]); 2228c42a7b7eSSam Leffler printf(" mac %s", ether_sprintf(mac)); 2229c42a7b7eSSam Leffler if (hk->kv_type == HAL_CIPHER_TKIP) { 22305901d2d3SSam Leffler printf(" %s ", sc->sc_splitmic ? "mic" : "rxmic"); 2231c42a7b7eSSam Leffler for (i = 0; i < sizeof(hk->kv_mic); i++) 2232c42a7b7eSSam Leffler printf("%02x", hk->kv_mic[i]); 22335901d2d3SSam Leffler if (!sc->sc_splitmic) { 22345901d2d3SSam Leffler printf(" txmic "); 22355901d2d3SSam Leffler for (i = 0; i < sizeof(hk->kv_txmic); i++) 22365901d2d3SSam Leffler printf("%02x", hk->kv_txmic[i]); 22375901d2d3SSam Leffler } 22382075afbaSSam Leffler } 2239c42a7b7eSSam Leffler printf("\n"); 2240c42a7b7eSSam Leffler } 2241c42a7b7eSSam Leffler #endif 2242c42a7b7eSSam Leffler 22435591b213SSam Leffler /* 2244c42a7b7eSSam Leffler * Set a TKIP key into the hardware. This handles the 2245c42a7b7eSSam Leffler * potential distribution of key state to multiple key 2246c42a7b7eSSam Leffler * cache slots for TKIP. 22475591b213SSam Leffler */ 2248c42a7b7eSSam Leffler static int 2249c42a7b7eSSam Leffler ath_keyset_tkip(struct ath_softc *sc, const struct ieee80211_key *k, 2250c42a7b7eSSam Leffler HAL_KEYVAL *hk, const u_int8_t mac[IEEE80211_ADDR_LEN]) 2251c42a7b7eSSam Leffler { 2252c42a7b7eSSam Leffler #define IEEE80211_KEY_XR (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV) 2253c42a7b7eSSam Leffler static const u_int8_t zerobssid[IEEE80211_ADDR_LEN]; 22548cec0ab9SSam Leffler struct ath_hal *ah = sc->sc_ah; 22558cec0ab9SSam Leffler 2256c42a7b7eSSam Leffler KASSERT(k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP, 2257c42a7b7eSSam Leffler ("got a non-TKIP key, cipher %u", k->wk_cipher->ic_cipher)); 2258c42a7b7eSSam Leffler if ((k->wk_flags & IEEE80211_KEY_XR) == IEEE80211_KEY_XR) { 22595901d2d3SSam Leffler if (sc->sc_splitmic) { 2260c42a7b7eSSam Leffler /* 2261c1225b52SSam Leffler * TX key goes at first index, RX key at the rx index. 2262c42a7b7eSSam Leffler * The hal handles the MIC keys at index+64. 2263c42a7b7eSSam Leffler */ 2264c42a7b7eSSam Leffler memcpy(hk->kv_mic, k->wk_txmic, sizeof(hk->kv_mic)); 2265c42a7b7eSSam Leffler KEYPRINTF(sc, k->wk_keyix, hk, zerobssid); 2266c42a7b7eSSam Leffler if (!ath_hal_keyset(ah, k->wk_keyix, hk, zerobssid)) 2267c42a7b7eSSam Leffler return 0; 2268c42a7b7eSSam Leffler 2269c42a7b7eSSam Leffler memcpy(hk->kv_mic, k->wk_rxmic, sizeof(hk->kv_mic)); 2270c42a7b7eSSam Leffler KEYPRINTF(sc, k->wk_keyix+32, hk, mac); 2271c42a7b7eSSam Leffler /* XXX delete tx key on failure? */ 2272c42a7b7eSSam Leffler return ath_hal_keyset(ah, k->wk_keyix+32, hk, mac); 22735901d2d3SSam Leffler } else { 22745901d2d3SSam Leffler /* 22755901d2d3SSam Leffler * Room for both TX+RX MIC keys in one key cache 22765901d2d3SSam Leffler * slot, just set key at the first index; the hal 2277b032f27cSSam Leffler * will handle the rest. 22785901d2d3SSam Leffler */ 22795901d2d3SSam Leffler memcpy(hk->kv_mic, k->wk_rxmic, sizeof(hk->kv_mic)); 22805901d2d3SSam Leffler memcpy(hk->kv_txmic, k->wk_txmic, sizeof(hk->kv_txmic)); 22815901d2d3SSam Leffler KEYPRINTF(sc, k->wk_keyix, hk, mac); 22825901d2d3SSam Leffler return ath_hal_keyset(ah, k->wk_keyix, hk, mac); 22835901d2d3SSam Leffler } 2284b032f27cSSam Leffler } else if (k->wk_flags & IEEE80211_KEY_XMIT) { 228572d9df0aSSam Leffler if (sc->sc_splitmic) { 228672d9df0aSSam Leffler /* 228772d9df0aSSam Leffler * NB: must pass MIC key in expected location when 228872d9df0aSSam Leffler * the keycache only holds one MIC key per entry. 228972d9df0aSSam Leffler */ 229072d9df0aSSam Leffler memcpy(hk->kv_mic, k->wk_txmic, sizeof(hk->kv_txmic)); 229172d9df0aSSam Leffler } else 2292b032f27cSSam Leffler memcpy(hk->kv_txmic, k->wk_txmic, sizeof(hk->kv_txmic)); 2293b032f27cSSam Leffler KEYPRINTF(sc, k->wk_keyix, hk, mac); 2294b032f27cSSam Leffler return ath_hal_keyset(ah, k->wk_keyix, hk, mac); 2295b032f27cSSam Leffler } else if (k->wk_flags & IEEE80211_KEY_RECV) { 2296b032f27cSSam Leffler memcpy(hk->kv_mic, k->wk_rxmic, sizeof(hk->kv_mic)); 2297e8fd88a3SSam Leffler KEYPRINTF(sc, k->wk_keyix, hk, mac); 2298e8fd88a3SSam Leffler return ath_hal_keyset(ah, k->wk_keyix, hk, mac); 2299c42a7b7eSSam Leffler } 2300c42a7b7eSSam Leffler return 0; 2301c42a7b7eSSam Leffler #undef IEEE80211_KEY_XR 2302c42a7b7eSSam Leffler } 2303c42a7b7eSSam Leffler 2304c42a7b7eSSam Leffler /* 2305c42a7b7eSSam Leffler * Set a net80211 key into the hardware. This handles the 2306c42a7b7eSSam Leffler * potential distribution of key state to multiple key 2307c42a7b7eSSam Leffler * cache slots for TKIP with hardware MIC support. 2308c42a7b7eSSam Leffler */ 2309c42a7b7eSSam Leffler static int 2310c42a7b7eSSam Leffler ath_keyset(struct ath_softc *sc, const struct ieee80211_key *k, 2311e8fd88a3SSam Leffler struct ieee80211_node *bss) 2312c42a7b7eSSam Leffler { 2313c42a7b7eSSam Leffler #define N(a) (sizeof(a)/sizeof(a[0])) 2314c42a7b7eSSam Leffler static const u_int8_t ciphermap[] = { 2315c42a7b7eSSam Leffler HAL_CIPHER_WEP, /* IEEE80211_CIPHER_WEP */ 2316c42a7b7eSSam Leffler HAL_CIPHER_TKIP, /* IEEE80211_CIPHER_TKIP */ 2317c42a7b7eSSam Leffler HAL_CIPHER_AES_OCB, /* IEEE80211_CIPHER_AES_OCB */ 2318c42a7b7eSSam Leffler HAL_CIPHER_AES_CCM, /* IEEE80211_CIPHER_AES_CCM */ 2319c42a7b7eSSam Leffler (u_int8_t) -1, /* 4 is not allocated */ 2320c42a7b7eSSam Leffler HAL_CIPHER_CKIP, /* IEEE80211_CIPHER_CKIP */ 2321c42a7b7eSSam Leffler HAL_CIPHER_CLR, /* IEEE80211_CIPHER_NONE */ 2322c42a7b7eSSam Leffler }; 2323c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 2324c42a7b7eSSam Leffler const struct ieee80211_cipher *cip = k->wk_cipher; 2325e8fd88a3SSam Leffler u_int8_t gmac[IEEE80211_ADDR_LEN]; 2326e8fd88a3SSam Leffler const u_int8_t *mac; 2327c42a7b7eSSam Leffler HAL_KEYVAL hk; 2328c42a7b7eSSam Leffler 2329c42a7b7eSSam Leffler memset(&hk, 0, sizeof(hk)); 2330c42a7b7eSSam Leffler /* 2331c42a7b7eSSam Leffler * Software crypto uses a "clear key" so non-crypto 2332c42a7b7eSSam Leffler * state kept in the key cache are maintained and 2333c42a7b7eSSam Leffler * so that rx frames have an entry to match. 2334c42a7b7eSSam Leffler */ 2335c42a7b7eSSam Leffler if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) == 0) { 2336c42a7b7eSSam Leffler KASSERT(cip->ic_cipher < N(ciphermap), 2337c42a7b7eSSam Leffler ("invalid cipher type %u", cip->ic_cipher)); 2338c42a7b7eSSam Leffler hk.kv_type = ciphermap[cip->ic_cipher]; 2339c42a7b7eSSam Leffler hk.kv_len = k->wk_keylen; 2340c42a7b7eSSam Leffler memcpy(hk.kv_val, k->wk_key, k->wk_keylen); 23418cec0ab9SSam Leffler } else 2342c42a7b7eSSam Leffler hk.kv_type = HAL_CIPHER_CLR; 2343c42a7b7eSSam Leffler 2344e8fd88a3SSam Leffler if ((k->wk_flags & IEEE80211_KEY_GROUP) && sc->sc_mcastkey) { 2345e8fd88a3SSam Leffler /* 2346e8fd88a3SSam Leffler * Group keys on hardware that supports multicast frame 2347e8fd88a3SSam Leffler * key search use a mac that is the sender's address with 2348e8fd88a3SSam Leffler * the high bit set instead of the app-specified address. 2349e8fd88a3SSam Leffler */ 2350e8fd88a3SSam Leffler IEEE80211_ADDR_COPY(gmac, bss->ni_macaddr); 2351e8fd88a3SSam Leffler gmac[0] |= 0x80; 2352e8fd88a3SSam Leffler mac = gmac; 2353e8fd88a3SSam Leffler } else 2354d3ac945bSSam Leffler mac = k->wk_macaddr; 2355e8fd88a3SSam Leffler 2356c42a7b7eSSam Leffler if (hk.kv_type == HAL_CIPHER_TKIP && 23575901d2d3SSam Leffler (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) { 2358c42a7b7eSSam Leffler return ath_keyset_tkip(sc, k, &hk, mac); 2359c42a7b7eSSam Leffler } else { 2360c42a7b7eSSam Leffler KEYPRINTF(sc, k->wk_keyix, &hk, mac); 2361c42a7b7eSSam Leffler return ath_hal_keyset(ah, k->wk_keyix, &hk, mac); 23628cec0ab9SSam Leffler } 2363c42a7b7eSSam Leffler #undef N 23645591b213SSam Leffler } 23655591b213SSam Leffler 23665591b213SSam Leffler /* 2367c42a7b7eSSam Leffler * Allocate tx/rx key slots for TKIP. We allocate two slots for 2368c42a7b7eSSam Leffler * each key, one for decrypt/encrypt and the other for the MIC. 2369c42a7b7eSSam Leffler */ 2370c42a7b7eSSam Leffler static u_int16_t 2371c1225b52SSam Leffler key_alloc_2pair(struct ath_softc *sc, 2372c1225b52SSam Leffler ieee80211_keyix *txkeyix, ieee80211_keyix *rxkeyix) 2373c42a7b7eSSam Leffler { 2374c42a7b7eSSam Leffler #define N(a) (sizeof(a)/sizeof(a[0])) 2375c42a7b7eSSam Leffler u_int i, keyix; 2376c42a7b7eSSam Leffler 2377c42a7b7eSSam Leffler KASSERT(sc->sc_splitmic, ("key cache !split")); 2378c42a7b7eSSam Leffler /* XXX could optimize */ 2379c42a7b7eSSam Leffler for (i = 0; i < N(sc->sc_keymap)/4; i++) { 2380c42a7b7eSSam Leffler u_int8_t b = sc->sc_keymap[i]; 2381c42a7b7eSSam Leffler if (b != 0xff) { 2382c42a7b7eSSam Leffler /* 2383c42a7b7eSSam Leffler * One or more slots in this byte are free. 2384c42a7b7eSSam Leffler */ 2385c42a7b7eSSam Leffler keyix = i*NBBY; 2386c42a7b7eSSam Leffler while (b & 1) { 2387c42a7b7eSSam Leffler again: 2388c42a7b7eSSam Leffler keyix++; 2389c42a7b7eSSam Leffler b >>= 1; 2390c42a7b7eSSam Leffler } 2391c42a7b7eSSam Leffler /* XXX IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV */ 2392c42a7b7eSSam Leffler if (isset(sc->sc_keymap, keyix+32) || 2393c42a7b7eSSam Leffler isset(sc->sc_keymap, keyix+64) || 2394c42a7b7eSSam Leffler isset(sc->sc_keymap, keyix+32+64)) { 2395c42a7b7eSSam Leffler /* full pair unavailable */ 2396c42a7b7eSSam Leffler /* XXX statistic */ 2397c42a7b7eSSam Leffler if (keyix == (i+1)*NBBY) { 2398c42a7b7eSSam Leffler /* no slots were appropriate, advance */ 2399c42a7b7eSSam Leffler continue; 2400c42a7b7eSSam Leffler } 2401c42a7b7eSSam Leffler goto again; 2402c42a7b7eSSam Leffler } 2403c42a7b7eSSam Leffler setbit(sc->sc_keymap, keyix); 2404c42a7b7eSSam Leffler setbit(sc->sc_keymap, keyix+64); 2405c42a7b7eSSam Leffler setbit(sc->sc_keymap, keyix+32); 2406c42a7b7eSSam Leffler setbit(sc->sc_keymap, keyix+32+64); 2407c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_KEYCACHE, 2408c42a7b7eSSam Leffler "%s: key pair %u,%u %u,%u\n", 2409c42a7b7eSSam Leffler __func__, keyix, keyix+64, 2410c42a7b7eSSam Leffler keyix+32, keyix+32+64); 2411c1225b52SSam Leffler *txkeyix = keyix; 2412c1225b52SSam Leffler *rxkeyix = keyix+32; 2413c1225b52SSam Leffler return 1; 2414c42a7b7eSSam Leffler } 2415c42a7b7eSSam Leffler } 2416c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: out of pair space\n", __func__); 2417c1225b52SSam Leffler return 0; 2418c42a7b7eSSam Leffler #undef N 2419c42a7b7eSSam Leffler } 2420c42a7b7eSSam Leffler 2421c42a7b7eSSam Leffler /* 24225901d2d3SSam Leffler * Allocate tx/rx key slots for TKIP. We allocate two slots for 24235901d2d3SSam Leffler * each key, one for decrypt/encrypt and the other for the MIC. 24245901d2d3SSam Leffler */ 24255901d2d3SSam Leffler static u_int16_t 24265901d2d3SSam Leffler key_alloc_pair(struct ath_softc *sc, 24275901d2d3SSam Leffler ieee80211_keyix *txkeyix, ieee80211_keyix *rxkeyix) 24285901d2d3SSam Leffler { 24295901d2d3SSam Leffler #define N(a) (sizeof(a)/sizeof(a[0])) 24305901d2d3SSam Leffler u_int i, keyix; 24315901d2d3SSam Leffler 24325901d2d3SSam Leffler KASSERT(!sc->sc_splitmic, ("key cache split")); 24335901d2d3SSam Leffler /* XXX could optimize */ 24345901d2d3SSam Leffler for (i = 0; i < N(sc->sc_keymap)/4; i++) { 24355901d2d3SSam Leffler u_int8_t b = sc->sc_keymap[i]; 24365901d2d3SSam Leffler if (b != 0xff) { 24375901d2d3SSam Leffler /* 24385901d2d3SSam Leffler * One or more slots in this byte are free. 24395901d2d3SSam Leffler */ 24405901d2d3SSam Leffler keyix = i*NBBY; 24415901d2d3SSam Leffler while (b & 1) { 24425901d2d3SSam Leffler again: 24435901d2d3SSam Leffler keyix++; 24445901d2d3SSam Leffler b >>= 1; 24455901d2d3SSam Leffler } 24465901d2d3SSam Leffler if (isset(sc->sc_keymap, keyix+64)) { 24475901d2d3SSam Leffler /* full pair unavailable */ 24485901d2d3SSam Leffler /* XXX statistic */ 24495901d2d3SSam Leffler if (keyix == (i+1)*NBBY) { 24505901d2d3SSam Leffler /* no slots were appropriate, advance */ 24515901d2d3SSam Leffler continue; 24525901d2d3SSam Leffler } 24535901d2d3SSam Leffler goto again; 24545901d2d3SSam Leffler } 24555901d2d3SSam Leffler setbit(sc->sc_keymap, keyix); 24565901d2d3SSam Leffler setbit(sc->sc_keymap, keyix+64); 24575901d2d3SSam Leffler DPRINTF(sc, ATH_DEBUG_KEYCACHE, 24585901d2d3SSam Leffler "%s: key pair %u,%u\n", 24595901d2d3SSam Leffler __func__, keyix, keyix+64); 24605901d2d3SSam Leffler *txkeyix = *rxkeyix = keyix; 24615901d2d3SSam Leffler return 1; 24625901d2d3SSam Leffler } 24635901d2d3SSam Leffler } 24645901d2d3SSam Leffler DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: out of pair space\n", __func__); 24655901d2d3SSam Leffler return 0; 24665901d2d3SSam Leffler #undef N 24675901d2d3SSam Leffler } 24685901d2d3SSam Leffler 24695901d2d3SSam Leffler /* 2470c42a7b7eSSam Leffler * Allocate a single key cache slot. 2471c42a7b7eSSam Leffler */ 2472c1225b52SSam Leffler static int 2473c1225b52SSam Leffler key_alloc_single(struct ath_softc *sc, 2474c1225b52SSam Leffler ieee80211_keyix *txkeyix, ieee80211_keyix *rxkeyix) 2475c42a7b7eSSam Leffler { 2476c42a7b7eSSam Leffler #define N(a) (sizeof(a)/sizeof(a[0])) 2477c42a7b7eSSam Leffler u_int i, keyix; 2478c42a7b7eSSam Leffler 2479c42a7b7eSSam Leffler /* XXX try i,i+32,i+64,i+32+64 to minimize key pair conflicts */ 2480c42a7b7eSSam Leffler for (i = 0; i < N(sc->sc_keymap); i++) { 2481c42a7b7eSSam Leffler u_int8_t b = sc->sc_keymap[i]; 2482c42a7b7eSSam Leffler if (b != 0xff) { 2483c42a7b7eSSam Leffler /* 2484c42a7b7eSSam Leffler * One or more slots are free. 2485c42a7b7eSSam Leffler */ 2486c42a7b7eSSam Leffler keyix = i*NBBY; 2487c42a7b7eSSam Leffler while (b & 1) 2488c42a7b7eSSam Leffler keyix++, b >>= 1; 2489c42a7b7eSSam Leffler setbit(sc->sc_keymap, keyix); 2490c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: key %u\n", 2491c42a7b7eSSam Leffler __func__, keyix); 2492c1225b52SSam Leffler *txkeyix = *rxkeyix = keyix; 2493c1225b52SSam Leffler return 1; 2494c42a7b7eSSam Leffler } 2495c42a7b7eSSam Leffler } 2496c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: out of space\n", __func__); 2497c1225b52SSam Leffler return 0; 2498c42a7b7eSSam Leffler #undef N 2499c42a7b7eSSam Leffler } 2500c42a7b7eSSam Leffler 2501c42a7b7eSSam Leffler /* 2502c42a7b7eSSam Leffler * Allocate one or more key cache slots for a uniacst key. The 2503c42a7b7eSSam Leffler * key itself is needed only to identify the cipher. For hardware 2504c42a7b7eSSam Leffler * TKIP with split cipher+MIC keys we allocate two key cache slot 2505c42a7b7eSSam Leffler * pairs so that we can setup separate TX and RX MIC keys. Note 2506c42a7b7eSSam Leffler * that the MIC key for a TKIP key at slot i is assumed by the 2507c42a7b7eSSam Leffler * hardware to be at slot i+64. This limits TKIP keys to the first 2508c42a7b7eSSam Leffler * 64 entries. 2509c42a7b7eSSam Leffler */ 2510c42a7b7eSSam Leffler static int 2511e6e547d5SSam Leffler ath_key_alloc(struct ieee80211vap *vap, struct ieee80211_key *k, 2512c1225b52SSam Leffler ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix) 2513c42a7b7eSSam Leffler { 2514b032f27cSSam Leffler struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; 2515c42a7b7eSSam Leffler 2516c42a7b7eSSam Leffler /* 25178ca623d7SSam Leffler * Group key allocation must be handled specially for 25188ca623d7SSam Leffler * parts that do not support multicast key cache search 25198ca623d7SSam Leffler * functionality. For those parts the key id must match 25208ca623d7SSam Leffler * the h/w key index so lookups find the right key. On 25218ca623d7SSam Leffler * parts w/ the key search facility we install the sender's 25228ca623d7SSam Leffler * mac address (with the high bit set) and let the hardware 25238ca623d7SSam Leffler * find the key w/o using the key id. This is preferred as 25248ca623d7SSam Leffler * it permits us to support multiple users for adhoc and/or 25258ca623d7SSam Leffler * multi-station operation. 25268ca623d7SSam Leffler */ 25271e63cecbSSam Leffler if (k->wk_keyix != IEEE80211_KEYIX_NONE || /* global key */ 25281e63cecbSSam Leffler ((k->wk_flags & IEEE80211_KEY_GROUP) && !sc->sc_mcastkey)) { 2529b032f27cSSam Leffler if (!(&vap->iv_nw_keys[0] <= k && 2530b032f27cSSam Leffler k < &vap->iv_nw_keys[IEEE80211_WEP_NKID])) { 25318ca623d7SSam Leffler /* should not happen */ 25328ca623d7SSam Leffler DPRINTF(sc, ATH_DEBUG_KEYCACHE, 25338ca623d7SSam Leffler "%s: bogus group key\n", __func__); 2534c1225b52SSam Leffler return 0; 25358ca623d7SSam Leffler } 25368ca623d7SSam Leffler /* 25378ca623d7SSam Leffler * XXX we pre-allocate the global keys so 25388ca623d7SSam Leffler * have no way to check if they've already been allocated. 25398ca623d7SSam Leffler */ 2540b032f27cSSam Leffler *keyix = *rxkeyix = k - vap->iv_nw_keys; 2541c1225b52SSam Leffler return 1; 25428ca623d7SSam Leffler } 25438ca623d7SSam Leffler 25448ca623d7SSam Leffler /* 2545c42a7b7eSSam Leffler * We allocate two pair for TKIP when using the h/w to do 2546c42a7b7eSSam Leffler * the MIC. For everything else, including software crypto, 2547c42a7b7eSSam Leffler * we allocate a single entry. Note that s/w crypto requires 2548c42a7b7eSSam Leffler * a pass-through slot on the 5211 and 5212. The 5210 does 2549c42a7b7eSSam Leffler * not support pass-through cache entries and we map all 2550c42a7b7eSSam Leffler * those requests to slot 0. 2551c42a7b7eSSam Leffler */ 2552c42a7b7eSSam Leffler if (k->wk_flags & IEEE80211_KEY_SWCRYPT) { 2553c1225b52SSam Leffler return key_alloc_single(sc, keyix, rxkeyix); 2554c42a7b7eSSam Leffler } else if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP && 25555901d2d3SSam Leffler (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) { 25565901d2d3SSam Leffler if (sc->sc_splitmic) 2557c1225b52SSam Leffler return key_alloc_2pair(sc, keyix, rxkeyix); 25585901d2d3SSam Leffler else 25595901d2d3SSam Leffler return key_alloc_pair(sc, keyix, rxkeyix); 2560c42a7b7eSSam Leffler } else { 2561c1225b52SSam Leffler return key_alloc_single(sc, keyix, rxkeyix); 2562c42a7b7eSSam Leffler } 2563c42a7b7eSSam Leffler } 2564c42a7b7eSSam Leffler 2565c42a7b7eSSam Leffler /* 2566c42a7b7eSSam Leffler * Delete an entry in the key cache allocated by ath_key_alloc. 2567c42a7b7eSSam Leffler */ 2568c42a7b7eSSam Leffler static int 2569b032f27cSSam Leffler ath_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k) 2570c42a7b7eSSam Leffler { 2571b032f27cSSam Leffler struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; 2572c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 2573c42a7b7eSSam Leffler const struct ieee80211_cipher *cip = k->wk_cipher; 2574c42a7b7eSSam Leffler u_int keyix = k->wk_keyix; 2575c42a7b7eSSam Leffler 2576c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: delete key %u\n", __func__, keyix); 2577c42a7b7eSSam Leffler 2578c42a7b7eSSam Leffler ath_hal_keyreset(ah, keyix); 2579c42a7b7eSSam Leffler /* 2580c42a7b7eSSam Leffler * Handle split tx/rx keying required for TKIP with h/w MIC. 2581c42a7b7eSSam Leffler */ 2582c42a7b7eSSam Leffler if (cip->ic_cipher == IEEE80211_CIPHER_TKIP && 2583c1225b52SSam Leffler (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && sc->sc_splitmic) 2584c42a7b7eSSam Leffler ath_hal_keyreset(ah, keyix+32); /* RX key */ 2585c42a7b7eSSam Leffler if (keyix >= IEEE80211_WEP_NKID) { 2586c42a7b7eSSam Leffler /* 2587c42a7b7eSSam Leffler * Don't touch keymap entries for global keys so 2588c42a7b7eSSam Leffler * they are never considered for dynamic allocation. 2589c42a7b7eSSam Leffler */ 2590c42a7b7eSSam Leffler clrbit(sc->sc_keymap, keyix); 2591c42a7b7eSSam Leffler if (cip->ic_cipher == IEEE80211_CIPHER_TKIP && 25925901d2d3SSam Leffler (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) { 2593c42a7b7eSSam Leffler clrbit(sc->sc_keymap, keyix+64); /* TX key MIC */ 25945901d2d3SSam Leffler if (sc->sc_splitmic) { 25955901d2d3SSam Leffler /* +32 for RX key, +32+64 for RX key MIC */ 25965901d2d3SSam Leffler clrbit(sc->sc_keymap, keyix+32); 25975901d2d3SSam Leffler clrbit(sc->sc_keymap, keyix+32+64); 25985901d2d3SSam Leffler } 2599c42a7b7eSSam Leffler } 2600c42a7b7eSSam Leffler } 2601c42a7b7eSSam Leffler return 1; 2602c42a7b7eSSam Leffler } 2603c42a7b7eSSam Leffler 2604c42a7b7eSSam Leffler /* 2605c42a7b7eSSam Leffler * Set the key cache contents for the specified key. Key cache 2606c42a7b7eSSam Leffler * slot(s) must already have been allocated by ath_key_alloc. 2607c42a7b7eSSam Leffler */ 2608c42a7b7eSSam Leffler static int 2609b032f27cSSam Leffler ath_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k, 2610c42a7b7eSSam Leffler const u_int8_t mac[IEEE80211_ADDR_LEN]) 2611c42a7b7eSSam Leffler { 2612b032f27cSSam Leffler struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; 2613c42a7b7eSSam Leffler 2614d3ac945bSSam Leffler return ath_keyset(sc, k, vap->iv_bss); 2615c42a7b7eSSam Leffler } 2616c42a7b7eSSam Leffler 2617c42a7b7eSSam Leffler /* 2618c42a7b7eSSam Leffler * Block/unblock tx+rx processing while a key change is done. 2619c42a7b7eSSam Leffler * We assume the caller serializes key management operations 2620c42a7b7eSSam Leffler * so we only need to worry about synchronization with other 2621c42a7b7eSSam Leffler * uses that originate in the driver. 2622c42a7b7eSSam Leffler */ 2623c42a7b7eSSam Leffler static void 2624b032f27cSSam Leffler ath_key_update_begin(struct ieee80211vap *vap) 2625c42a7b7eSSam Leffler { 2626b032f27cSSam Leffler struct ifnet *ifp = vap->iv_ic->ic_ifp; 2627c42a7b7eSSam Leffler struct ath_softc *sc = ifp->if_softc; 2628c42a7b7eSSam Leffler 2629c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__); 2630b032f27cSSam Leffler taskqueue_block(sc->sc_tq); 2631c42a7b7eSSam Leffler IF_LOCK(&ifp->if_snd); /* NB: doesn't block mgmt frames */ 2632c42a7b7eSSam Leffler } 2633c42a7b7eSSam Leffler 2634c42a7b7eSSam Leffler static void 2635b032f27cSSam Leffler ath_key_update_end(struct ieee80211vap *vap) 2636c42a7b7eSSam Leffler { 2637b032f27cSSam Leffler struct ifnet *ifp = vap->iv_ic->ic_ifp; 2638c42a7b7eSSam Leffler struct ath_softc *sc = ifp->if_softc; 2639c42a7b7eSSam Leffler 2640c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__); 2641c42a7b7eSSam Leffler IF_UNLOCK(&ifp->if_snd); 2642b032f27cSSam Leffler taskqueue_unblock(sc->sc_tq); 2643c42a7b7eSSam Leffler } 26445591b213SSam Leffler 26454bc0e754SSam Leffler /* 26464bc0e754SSam Leffler * Calculate the receive filter according to the 26474bc0e754SSam Leffler * operating mode and state: 26484bc0e754SSam Leffler * 26494bc0e754SSam Leffler * o always accept unicast, broadcast, and multicast traffic 2650b032f27cSSam Leffler * o accept PHY error frames when hardware doesn't have MIB support 2651411373ebSSam Leffler * to count and we need them for ANI (sta mode only until recently) 2652b032f27cSSam Leffler * and we are not scanning (ANI is disabled) 2653411373ebSSam Leffler * NB: older hal's add rx filter bits out of sight and we need to 2654411373ebSSam Leffler * blindly preserve them 26554bc0e754SSam Leffler * o probe request frames are accepted only when operating in 26564bc0e754SSam Leffler * hostap, adhoc, or monitor modes 2657b032f27cSSam Leffler * o enable promiscuous mode 2658b032f27cSSam Leffler * - when in monitor mode 2659b032f27cSSam Leffler * - if interface marked PROMISC (assumes bridge setting is filtered) 26604bc0e754SSam Leffler * o accept beacons: 26614bc0e754SSam Leffler * - when operating in station mode for collecting rssi data when 26624bc0e754SSam Leffler * the station is otherwise quiet, or 2663b032f27cSSam Leffler * - when operating in adhoc mode so the 802.11 layer creates 2664b032f27cSSam Leffler * node table entries for peers, 26654bc0e754SSam Leffler * - when scanning 2666b032f27cSSam Leffler * - when doing s/w beacon miss (e.g. for ap+sta) 2667b032f27cSSam Leffler * - when operating in ap mode in 11g to detect overlapping bss that 2668b032f27cSSam Leffler * require protection 26696f48c956SSam Leffler * o accept control frames: 26706f48c956SSam Leffler * - when in monitor mode 2671b032f27cSSam Leffler * XXX BAR frames for 11n 2672b032f27cSSam Leffler * XXX HT protection for 11n 26734bc0e754SSam Leffler */ 26744bc0e754SSam Leffler static u_int32_t 267568e8e04eSSam Leffler ath_calcrxfilter(struct ath_softc *sc) 26764bc0e754SSam Leffler { 2677fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 2678b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 26794bc0e754SSam Leffler u_int32_t rfilt; 26804bc0e754SSam Leffler 2681b032f27cSSam Leffler rfilt = HAL_RX_FILTER_UCAST | HAL_RX_FILTER_BCAST | HAL_RX_FILTER_MCAST; 2682411373ebSSam Leffler #if HAL_ABI_VERSION < 0x08011600 2683411373ebSSam Leffler rfilt |= (ath_hal_getrxfilter(sc->sc_ah) & 2684411373ebSSam Leffler (HAL_RX_FILTER_PHYRADAR | HAL_RX_FILTER_PHYERR)); 2685411373ebSSam Leffler #elif HAL_ABI_VERSION < 0x08060100 2686b032f27cSSam Leffler if (ic->ic_opmode == IEEE80211_M_STA && 2687b032f27cSSam Leffler !sc->sc_needmib && !sc->sc_scanning) 2688b032f27cSSam Leffler rfilt |= HAL_RX_FILTER_PHYERR; 2689411373ebSSam Leffler #else 2690411373ebSSam Leffler if (!sc->sc_needmib && !sc->sc_scanning) 2691411373ebSSam Leffler rfilt |= HAL_RX_FILTER_PHYERR; 2692b032f27cSSam Leffler #endif 26934bc0e754SSam Leffler if (ic->ic_opmode != IEEE80211_M_STA) 26944bc0e754SSam Leffler rfilt |= HAL_RX_FILTER_PROBEREQ; 2695b032f27cSSam Leffler if (ic->ic_opmode == IEEE80211_M_MONITOR || (ifp->if_flags & IFF_PROMISC)) 26964bc0e754SSam Leffler rfilt |= HAL_RX_FILTER_PROM; 26974bc0e754SSam Leffler if (ic->ic_opmode == IEEE80211_M_STA || 269847db982fSSam Leffler ic->ic_opmode == IEEE80211_M_IBSS || 2699b032f27cSSam Leffler sc->sc_swbmiss || sc->sc_scanning) 2700b032f27cSSam Leffler rfilt |= HAL_RX_FILTER_BEACON; 2701b032f27cSSam Leffler /* 2702b032f27cSSam Leffler * NB: We don't recalculate the rx filter when 2703b032f27cSSam Leffler * ic_protmode changes; otherwise we could do 2704b032f27cSSam Leffler * this only when ic_protmode != NONE. 2705b032f27cSSam Leffler */ 2706b032f27cSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP && 2707b032f27cSSam Leffler IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) 27084bc0e754SSam Leffler rfilt |= HAL_RX_FILTER_BEACON; 27096f48c956SSam Leffler if (ic->ic_opmode == IEEE80211_M_MONITOR) 27106f48c956SSam Leffler rfilt |= HAL_RX_FILTER_CONTROL; 2711b032f27cSSam Leffler DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x, %s if_flags 0x%x\n", 2712b032f27cSSam Leffler __func__, rfilt, ieee80211_opmode_name[ic->ic_opmode], ifp->if_flags); 27134bc0e754SSam Leffler return rfilt; 2714b032f27cSSam Leffler } 2715b032f27cSSam Leffler 2716b032f27cSSam Leffler static void 2717b032f27cSSam Leffler ath_update_promisc(struct ifnet *ifp) 2718b032f27cSSam Leffler { 2719b032f27cSSam Leffler struct ath_softc *sc = ifp->if_softc; 2720b032f27cSSam Leffler u_int32_t rfilt; 2721b032f27cSSam Leffler 2722b032f27cSSam Leffler /* configure rx filter */ 2723b032f27cSSam Leffler rfilt = ath_calcrxfilter(sc); 2724b032f27cSSam Leffler ath_hal_setrxfilter(sc->sc_ah, rfilt); 2725b032f27cSSam Leffler 2726b032f27cSSam Leffler DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x\n", __func__, rfilt); 2727b032f27cSSam Leffler } 2728b032f27cSSam Leffler 2729b032f27cSSam Leffler static void 2730b032f27cSSam Leffler ath_update_mcast(struct ifnet *ifp) 2731b032f27cSSam Leffler { 2732b032f27cSSam Leffler struct ath_softc *sc = ifp->if_softc; 2733b032f27cSSam Leffler u_int32_t mfilt[2]; 2734b032f27cSSam Leffler 2735b032f27cSSam Leffler /* calculate and install multicast filter */ 2736b032f27cSSam Leffler if ((ifp->if_flags & IFF_ALLMULTI) == 0) { 2737b032f27cSSam Leffler struct ifmultiaddr *ifma; 2738b032f27cSSam Leffler /* 2739b032f27cSSam Leffler * Merge multicast addresses to form the hardware filter. 2740b032f27cSSam Leffler */ 2741b032f27cSSam Leffler mfilt[0] = mfilt[1] = 0; 2742b032f27cSSam Leffler IF_ADDR_LOCK(ifp); /* XXX need some fiddling to remove? */ 2743b032f27cSSam Leffler TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 2744b032f27cSSam Leffler caddr_t dl; 2745b032f27cSSam Leffler u_int32_t val; 2746b032f27cSSam Leffler u_int8_t pos; 2747b032f27cSSam Leffler 2748b032f27cSSam Leffler /* calculate XOR of eight 6bit values */ 2749b032f27cSSam Leffler dl = LLADDR((struct sockaddr_dl *) ifma->ifma_addr); 2750b032f27cSSam Leffler val = LE_READ_4(dl + 0); 2751b032f27cSSam Leffler pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; 2752b032f27cSSam Leffler val = LE_READ_4(dl + 3); 2753b032f27cSSam Leffler pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; 2754b032f27cSSam Leffler pos &= 0x3f; 2755b032f27cSSam Leffler mfilt[pos / 32] |= (1 << (pos % 32)); 2756b032f27cSSam Leffler } 2757b032f27cSSam Leffler IF_ADDR_UNLOCK(ifp); 2758b032f27cSSam Leffler } else 2759b032f27cSSam Leffler mfilt[0] = mfilt[1] = ~0; 2760b032f27cSSam Leffler ath_hal_setmcastfilter(sc->sc_ah, mfilt[0], mfilt[1]); 2761b032f27cSSam Leffler DPRINTF(sc, ATH_DEBUG_MODE, "%s: MC filter %08x:%08x\n", 2762b032f27cSSam Leffler __func__, mfilt[0], mfilt[1]); 27634bc0e754SSam Leffler } 27644bc0e754SSam Leffler 27655591b213SSam Leffler static void 27665591b213SSam Leffler ath_mode_init(struct ath_softc *sc) 27675591b213SSam Leffler { 2768fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 2769b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 2770b032f27cSSam Leffler struct ath_hal *ah = sc->sc_ah; 2771b032f27cSSam Leffler u_int32_t rfilt; 27725591b213SSam Leffler 27734bc0e754SSam Leffler /* configure rx filter */ 277468e8e04eSSam Leffler rfilt = ath_calcrxfilter(sc); 27754bc0e754SSam Leffler ath_hal_setrxfilter(ah, rfilt); 27764bc0e754SSam Leffler 27775591b213SSam Leffler /* configure operational mode */ 2778c42a7b7eSSam Leffler ath_hal_setopmode(ah); 2779c42a7b7eSSam Leffler 2780c42a7b7eSSam Leffler /* 2781c42a7b7eSSam Leffler * Handle any link-level address change. Note that we only 2782c42a7b7eSSam Leffler * need to force ic_myaddr; any other addresses are handled 2783c42a7b7eSSam Leffler * as a byproduct of the ifnet code marking the interface 2784c42a7b7eSSam Leffler * down then up. 2785c42a7b7eSSam Leffler * 2786c42a7b7eSSam Leffler * XXX should get from lladdr instead of arpcom but that's more work 2787c42a7b7eSSam Leffler */ 27884a0d6638SRuslan Ermilov IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp)); 2789c42a7b7eSSam Leffler ath_hal_setmac(ah, ic->ic_myaddr); 27905591b213SSam Leffler 27915591b213SSam Leffler /* calculate and install multicast filter */ 2792b032f27cSSam Leffler ath_update_mcast(ifp); 27935591b213SSam Leffler } 27945591b213SSam Leffler 2795c42a7b7eSSam Leffler /* 2796c42a7b7eSSam Leffler * Set the slot time based on the current setting. 2797c42a7b7eSSam Leffler */ 2798c42a7b7eSSam Leffler static void 2799c42a7b7eSSam Leffler ath_setslottime(struct ath_softc *sc) 2800c42a7b7eSSam Leffler { 2801b032f27cSSam Leffler struct ieee80211com *ic = sc->sc_ifp->if_l2com; 2802c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 2803aaa70f2fSSam Leffler u_int usec; 2804c42a7b7eSSam Leffler 2805aaa70f2fSSam Leffler if (IEEE80211_IS_CHAN_HALF(ic->ic_curchan)) 2806aaa70f2fSSam Leffler usec = 13; 2807aaa70f2fSSam Leffler else if (IEEE80211_IS_CHAN_QUARTER(ic->ic_curchan)) 2808aaa70f2fSSam Leffler usec = 21; 2809724c193aSSam Leffler else if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) { 2810724c193aSSam Leffler /* honor short/long slot time only in 11g */ 2811724c193aSSam Leffler /* XXX shouldn't honor on pure g or turbo g channel */ 2812724c193aSSam Leffler if (ic->ic_flags & IEEE80211_F_SHSLOT) 2813aaa70f2fSSam Leffler usec = HAL_SLOT_TIME_9; 2814aaa70f2fSSam Leffler else 2815aaa70f2fSSam Leffler usec = HAL_SLOT_TIME_20; 2816724c193aSSam Leffler } else 2817724c193aSSam Leffler usec = HAL_SLOT_TIME_9; 2818aaa70f2fSSam Leffler 2819aaa70f2fSSam Leffler DPRINTF(sc, ATH_DEBUG_RESET, 2820aaa70f2fSSam Leffler "%s: chan %u MHz flags 0x%x %s slot, %u usec\n", 2821aaa70f2fSSam Leffler __func__, ic->ic_curchan->ic_freq, ic->ic_curchan->ic_flags, 2822aaa70f2fSSam Leffler ic->ic_flags & IEEE80211_F_SHSLOT ? "short" : "long", usec); 2823aaa70f2fSSam Leffler 2824aaa70f2fSSam Leffler ath_hal_setslottime(ah, usec); 2825c42a7b7eSSam Leffler sc->sc_updateslot = OK; 2826c42a7b7eSSam Leffler } 2827c42a7b7eSSam Leffler 2828c42a7b7eSSam Leffler /* 2829c42a7b7eSSam Leffler * Callback from the 802.11 layer to update the 2830c42a7b7eSSam Leffler * slot time based on the current setting. 2831c42a7b7eSSam Leffler */ 2832c42a7b7eSSam Leffler static void 2833c42a7b7eSSam Leffler ath_updateslot(struct ifnet *ifp) 2834c42a7b7eSSam Leffler { 2835c42a7b7eSSam Leffler struct ath_softc *sc = ifp->if_softc; 2836b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 2837c42a7b7eSSam Leffler 2838c42a7b7eSSam Leffler /* 2839c42a7b7eSSam Leffler * When not coordinating the BSS, change the hardware 2840c42a7b7eSSam Leffler * immediately. For other operation we defer the change 2841c42a7b7eSSam Leffler * until beacon updates have propagated to the stations. 2842c42a7b7eSSam Leffler */ 2843c42a7b7eSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) 2844c42a7b7eSSam Leffler sc->sc_updateslot = UPDATE; 2845c42a7b7eSSam Leffler else 2846c42a7b7eSSam Leffler ath_setslottime(sc); 2847c42a7b7eSSam Leffler } 2848c42a7b7eSSam Leffler 2849c42a7b7eSSam Leffler /* 285080d2765fSSam Leffler * Setup a h/w transmit queue for beacons. 285180d2765fSSam Leffler */ 285280d2765fSSam Leffler static int 285380d2765fSSam Leffler ath_beaconq_setup(struct ath_hal *ah) 285480d2765fSSam Leffler { 285580d2765fSSam Leffler HAL_TXQ_INFO qi; 285680d2765fSSam Leffler 285780d2765fSSam Leffler memset(&qi, 0, sizeof(qi)); 285880d2765fSSam Leffler qi.tqi_aifs = HAL_TXQ_USEDEFAULT; 285980d2765fSSam Leffler qi.tqi_cwmin = HAL_TXQ_USEDEFAULT; 286080d2765fSSam Leffler qi.tqi_cwmax = HAL_TXQ_USEDEFAULT; 28610f2e86fbSSam Leffler /* NB: for dynamic turbo, don't enable any other interrupts */ 2862bd5a9920SSam Leffler qi.tqi_qflags = HAL_TXQ_TXDESCINT_ENABLE; 286380d2765fSSam Leffler return ath_hal_setuptxqueue(ah, HAL_TX_QUEUE_BEACON, &qi); 286480d2765fSSam Leffler } 286580d2765fSSam Leffler 286680d2765fSSam Leffler /* 28670f2e86fbSSam Leffler * Setup the transmit queue parameters for the beacon queue. 28680f2e86fbSSam Leffler */ 28690f2e86fbSSam Leffler static int 28700f2e86fbSSam Leffler ath_beaconq_config(struct ath_softc *sc) 28710f2e86fbSSam Leffler { 28720f2e86fbSSam Leffler #define ATH_EXPONENT_TO_VALUE(v) ((1<<(v))-1) 2873b032f27cSSam Leffler struct ieee80211com *ic = sc->sc_ifp->if_l2com; 28740f2e86fbSSam Leffler struct ath_hal *ah = sc->sc_ah; 28750f2e86fbSSam Leffler HAL_TXQ_INFO qi; 28760f2e86fbSSam Leffler 28770f2e86fbSSam Leffler ath_hal_gettxqueueprops(ah, sc->sc_bhalq, &qi); 28780f2e86fbSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 28790f2e86fbSSam Leffler /* 28800f2e86fbSSam Leffler * Always burst out beacon and CAB traffic. 28810f2e86fbSSam Leffler */ 28820f2e86fbSSam Leffler qi.tqi_aifs = ATH_BEACON_AIFS_DEFAULT; 28830f2e86fbSSam Leffler qi.tqi_cwmin = ATH_BEACON_CWMIN_DEFAULT; 28840f2e86fbSSam Leffler qi.tqi_cwmax = ATH_BEACON_CWMAX_DEFAULT; 28850f2e86fbSSam Leffler } else { 28860f2e86fbSSam Leffler struct wmeParams *wmep = 28870f2e86fbSSam Leffler &ic->ic_wme.wme_chanParams.cap_wmeParams[WME_AC_BE]; 28880f2e86fbSSam Leffler /* 28890f2e86fbSSam Leffler * Adhoc mode; important thing is to use 2x cwmin. 28900f2e86fbSSam Leffler */ 28910f2e86fbSSam Leffler qi.tqi_aifs = wmep->wmep_aifsn; 28920f2e86fbSSam Leffler qi.tqi_cwmin = 2*ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmin); 28930f2e86fbSSam Leffler qi.tqi_cwmax = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmax); 28940f2e86fbSSam Leffler } 28950f2e86fbSSam Leffler 28960f2e86fbSSam Leffler if (!ath_hal_settxqueueprops(ah, sc->sc_bhalq, &qi)) { 28970f2e86fbSSam Leffler device_printf(sc->sc_dev, "unable to update parameters for " 28980f2e86fbSSam Leffler "beacon hardware queue!\n"); 28990f2e86fbSSam Leffler return 0; 29000f2e86fbSSam Leffler } else { 29010f2e86fbSSam Leffler ath_hal_resettxqueue(ah, sc->sc_bhalq); /* push to h/w */ 29020f2e86fbSSam Leffler return 1; 29030f2e86fbSSam Leffler } 29040f2e86fbSSam Leffler #undef ATH_EXPONENT_TO_VALUE 29050f2e86fbSSam Leffler } 29060f2e86fbSSam Leffler 29070f2e86fbSSam Leffler /* 2908c42a7b7eSSam Leffler * Allocate and setup an initial beacon frame. 2909c42a7b7eSSam Leffler */ 29105591b213SSam Leffler static int 29115591b213SSam Leffler ath_beacon_alloc(struct ath_softc *sc, struct ieee80211_node *ni) 29125591b213SSam Leffler { 2913b032f27cSSam Leffler struct ieee80211vap *vap = ni->ni_vap; 2914b032f27cSSam Leffler struct ath_vap *avp = ATH_VAP(vap); 29155591b213SSam Leffler struct ath_buf *bf; 29165591b213SSam Leffler struct mbuf *m; 2917c42a7b7eSSam Leffler int error; 29185591b213SSam Leffler 2919b032f27cSSam Leffler bf = avp->av_bcbuf; 2920b032f27cSSam Leffler if (bf->bf_m != NULL) { 2921b032f27cSSam Leffler bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 2922b032f27cSSam Leffler m_freem(bf->bf_m); 2923b032f27cSSam Leffler bf->bf_m = NULL; 2924c42a7b7eSSam Leffler } 2925b032f27cSSam Leffler if (bf->bf_node != NULL) { 2926b032f27cSSam Leffler ieee80211_free_node(bf->bf_node); 2927b032f27cSSam Leffler bf->bf_node = NULL; 2928b032f27cSSam Leffler } 2929b032f27cSSam Leffler 29305591b213SSam Leffler /* 29315591b213SSam Leffler * NB: the beacon data buffer must be 32-bit aligned; 29325591b213SSam Leffler * we assume the mbuf routines will return us something 29335591b213SSam Leffler * with this alignment (perhaps should assert). 29345591b213SSam Leffler */ 2935b032f27cSSam Leffler m = ieee80211_beacon_alloc(ni, &avp->av_boff); 29365591b213SSam Leffler if (m == NULL) { 2937b032f27cSSam Leffler device_printf(sc->sc_dev, "%s: cannot get mbuf\n", __func__); 29385591b213SSam Leffler sc->sc_stats.ast_be_nombuf++; 29395591b213SSam Leffler return ENOMEM; 29405591b213SSam Leffler } 2941f9e6219bSSam Leffler error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m, 2942f9e6219bSSam Leffler bf->bf_segs, &bf->bf_nseg, 29435591b213SSam Leffler BUS_DMA_NOWAIT); 2944b032f27cSSam Leffler if (error != 0) { 2945b032f27cSSam Leffler device_printf(sc->sc_dev, 2946b032f27cSSam Leffler "%s: cannot map mbuf, bus_dmamap_load_mbuf_sg returns %d\n", 2947b032f27cSSam Leffler __func__, error); 2948b032f27cSSam Leffler m_freem(m); 2949b032f27cSSam Leffler return error; 2950b032f27cSSam Leffler } 2951b032f27cSSam Leffler 2952b032f27cSSam Leffler /* 2953b032f27cSSam Leffler * Calculate a TSF adjustment factor required for staggered 2954b032f27cSSam Leffler * beacons. Note that we assume the format of the beacon 2955b032f27cSSam Leffler * frame leaves the tstamp field immediately following the 2956b032f27cSSam Leffler * header. 2957b032f27cSSam Leffler */ 2958b032f27cSSam Leffler if (sc->sc_stagbeacons && avp->av_bslot > 0) { 2959b032f27cSSam Leffler uint64_t tsfadjust; 2960b032f27cSSam Leffler struct ieee80211_frame *wh; 2961b032f27cSSam Leffler 2962b032f27cSSam Leffler /* 2963b032f27cSSam Leffler * The beacon interval is in TU's; the TSF is in usecs. 2964b032f27cSSam Leffler * We figure out how many TU's to add to align the timestamp 2965b032f27cSSam Leffler * then convert to TSF units and handle byte swapping before 2966b032f27cSSam Leffler * inserting it in the frame. The hardware will then add this 2967b032f27cSSam Leffler * each time a beacon frame is sent. Note that we align vap's 2968b032f27cSSam Leffler * 1..N and leave vap 0 untouched. This means vap 0 has a 2969b032f27cSSam Leffler * timestamp in one beacon interval while the others get a 2970b032f27cSSam Leffler * timstamp aligned to the next interval. 2971b032f27cSSam Leffler */ 2972b032f27cSSam Leffler tsfadjust = ni->ni_intval * 2973b032f27cSSam Leffler (ATH_BCBUF - avp->av_bslot) / ATH_BCBUF; 2974b032f27cSSam Leffler tsfadjust = htole64(tsfadjust << 10); /* TU -> TSF */ 2975b032f27cSSam Leffler 2976b032f27cSSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON, 2977b032f27cSSam Leffler "%s: %s beacons bslot %d intval %u tsfadjust %llu\n", 2978b032f27cSSam Leffler __func__, sc->sc_stagbeacons ? "stagger" : "burst", 29793627e321SSam Leffler avp->av_bslot, ni->ni_intval, 29803627e321SSam Leffler (long long unsigned) le64toh(tsfadjust)); 2981b032f27cSSam Leffler 2982b032f27cSSam Leffler wh = mtod(m, struct ieee80211_frame *); 2983b032f27cSSam Leffler memcpy(&wh[1], &tsfadjust, sizeof(tsfadjust)); 2984b032f27cSSam Leffler } 2985c42a7b7eSSam Leffler bf->bf_m = m; 2986f818612bSSam Leffler bf->bf_node = ieee80211_ref_node(ni); 2987b032f27cSSam Leffler 2988b032f27cSSam Leffler return 0; 29895591b213SSam Leffler } 2990c42a7b7eSSam Leffler 2991c42a7b7eSSam Leffler /* 2992c42a7b7eSSam Leffler * Setup the beacon frame for transmit. 2993c42a7b7eSSam Leffler */ 2994c42a7b7eSSam Leffler static void 2995c42a7b7eSSam Leffler ath_beacon_setup(struct ath_softc *sc, struct ath_buf *bf) 2996c42a7b7eSSam Leffler { 2997c42a7b7eSSam Leffler #define USE_SHPREAMBLE(_ic) \ 2998c42a7b7eSSam Leffler (((_ic)->ic_flags & (IEEE80211_F_SHPREAMBLE | IEEE80211_F_USEBARKER))\ 2999c42a7b7eSSam Leffler == IEEE80211_F_SHPREAMBLE) 3000c42a7b7eSSam Leffler struct ieee80211_node *ni = bf->bf_node; 3001c42a7b7eSSam Leffler struct ieee80211com *ic = ni->ni_ic; 3002c42a7b7eSSam Leffler struct mbuf *m = bf->bf_m; 3003c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 3004c42a7b7eSSam Leffler struct ath_desc *ds; 3005c42a7b7eSSam Leffler int flags, antenna; 300655f63772SSam Leffler const HAL_RATE_TABLE *rt; 300755f63772SSam Leffler u_int8_t rix, rate; 3008c42a7b7eSSam Leffler 30094a3ac3fcSSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s: m %p len %u\n", 3010c42a7b7eSSam Leffler __func__, m, m->m_len); 30115591b213SSam Leffler 30125591b213SSam Leffler /* setup descriptors */ 30135591b213SSam Leffler ds = bf->bf_desc; 30145591b213SSam Leffler 3015c42a7b7eSSam Leffler flags = HAL_TXDESC_NOACK; 3016c42a7b7eSSam Leffler if (ic->ic_opmode == IEEE80211_M_IBSS && sc->sc_hasveol) { 3017c42a7b7eSSam Leffler ds->ds_link = bf->bf_daddr; /* self-linked */ 3018c42a7b7eSSam Leffler flags |= HAL_TXDESC_VEOL; 3019c42a7b7eSSam Leffler /* 3020c42a7b7eSSam Leffler * Let hardware handle antenna switching. 3021c42a7b7eSSam Leffler */ 30224866e6c2SSam Leffler antenna = sc->sc_txantenna; 3023c42a7b7eSSam Leffler } else { 30245591b213SSam Leffler ds->ds_link = 0; 3025c42a7b7eSSam Leffler /* 3026c42a7b7eSSam Leffler * Switch antenna every 4 beacons. 3027c42a7b7eSSam Leffler * XXX assumes two antenna 3028c42a7b7eSSam Leffler */ 3029b032f27cSSam Leffler if (sc->sc_txantenna != 0) 3030b032f27cSSam Leffler antenna = sc->sc_txantenna; 3031b032f27cSSam Leffler else if (sc->sc_stagbeacons && sc->sc_nbcnvaps != 0) 3032b032f27cSSam Leffler antenna = ((sc->sc_stats.ast_be_xmit / sc->sc_nbcnvaps) & 4 ? 2 : 1); 3033b032f27cSSam Leffler else 3034b032f27cSSam Leffler antenna = (sc->sc_stats.ast_be_xmit & 4 ? 2 : 1); 3035c42a7b7eSSam Leffler } 3036c42a7b7eSSam Leffler 3037c42a7b7eSSam Leffler KASSERT(bf->bf_nseg == 1, 3038c42a7b7eSSam Leffler ("multi-segment beacon frame; nseg %u", bf->bf_nseg)); 30395591b213SSam Leffler ds->ds_data = bf->bf_segs[0].ds_addr; 30405591b213SSam Leffler /* 30415591b213SSam Leffler * Calculate rate code. 30425591b213SSam Leffler * XXX everything at min xmit rate 30435591b213SSam Leffler */ 3044b032f27cSSam Leffler rix = 0; 304555f63772SSam Leffler rt = sc->sc_currates; 304655f63772SSam Leffler rate = rt->info[rix].rateCode; 3047c42a7b7eSSam Leffler if (USE_SHPREAMBLE(ic)) 304855f63772SSam Leffler rate |= rt->info[rix].shortPreamble; 30495591b213SSam Leffler ath_hal_setuptxdesc(ah, ds 3050c42a7b7eSSam Leffler , m->m_len + IEEE80211_CRC_LEN /* frame length */ 30515591b213SSam Leffler , sizeof(struct ieee80211_frame)/* header length */ 30525591b213SSam Leffler , HAL_PKT_TYPE_BEACON /* Atheros packet type */ 3053c42a7b7eSSam Leffler , ni->ni_txpower /* txpower XXX */ 30545591b213SSam Leffler , rate, 1 /* series 0 rate/tries */ 30555591b213SSam Leffler , HAL_TXKEYIX_INVALID /* no encryption */ 3056c42a7b7eSSam Leffler , antenna /* antenna mode */ 3057c42a7b7eSSam Leffler , flags /* no ack, veol for beacons */ 30585591b213SSam Leffler , 0 /* rts/cts rate */ 30595591b213SSam Leffler , 0 /* rts/cts duration */ 30605591b213SSam Leffler ); 30615591b213SSam Leffler /* NB: beacon's BufLen must be a multiple of 4 bytes */ 30625591b213SSam Leffler ath_hal_filltxdesc(ah, ds 3063c42a7b7eSSam Leffler , roundup(m->m_len, 4) /* buffer length */ 30645591b213SSam Leffler , AH_TRUE /* first segment */ 30655591b213SSam Leffler , AH_TRUE /* last segment */ 3066c42a7b7eSSam Leffler , ds /* first descriptor */ 30675591b213SSam Leffler ); 3068b032f27cSSam Leffler #if 0 3069b032f27cSSam Leffler ath_desc_swap(ds); 3070b032f27cSSam Leffler #endif 3071c42a7b7eSSam Leffler #undef USE_SHPREAMBLE 30725591b213SSam Leffler } 30735591b213SSam Leffler 3074b105a069SSam Leffler static void 3075b032f27cSSam Leffler ath_beacon_update(struct ieee80211vap *vap, int item) 3076b105a069SSam Leffler { 3077b032f27cSSam Leffler struct ieee80211_beacon_offsets *bo = &ATH_VAP(vap)->av_boff; 3078b105a069SSam Leffler 3079b105a069SSam Leffler setbit(bo->bo_flags, item); 3080b105a069SSam Leffler } 3081b105a069SSam Leffler 3082c42a7b7eSSam Leffler /* 3083622b3fd2SSam Leffler * Append the contents of src to dst; both queues 3084622b3fd2SSam Leffler * are assumed to be locked. 3085622b3fd2SSam Leffler */ 3086622b3fd2SSam Leffler static void 3087622b3fd2SSam Leffler ath_txqmove(struct ath_txq *dst, struct ath_txq *src) 3088622b3fd2SSam Leffler { 3089622b3fd2SSam Leffler STAILQ_CONCAT(&dst->axq_q, &src->axq_q); 3090622b3fd2SSam Leffler dst->axq_link = src->axq_link; 3091622b3fd2SSam Leffler src->axq_link = NULL; 3092622b3fd2SSam Leffler dst->axq_depth += src->axq_depth; 3093622b3fd2SSam Leffler src->axq_depth = 0; 3094622b3fd2SSam Leffler } 3095622b3fd2SSam Leffler 3096622b3fd2SSam Leffler /* 3097c42a7b7eSSam Leffler * Transmit a beacon frame at SWBA. Dynamic updates to the 3098c42a7b7eSSam Leffler * frame contents are done as needed and the slot time is 3099c42a7b7eSSam Leffler * also adjusted based on current state. 3100c42a7b7eSSam Leffler */ 31015591b213SSam Leffler static void 31025591b213SSam Leffler ath_beacon_proc(void *arg, int pending) 31035591b213SSam Leffler { 31045591b213SSam Leffler struct ath_softc *sc = arg; 31055591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 3106b032f27cSSam Leffler struct ieee80211vap *vap; 3107b032f27cSSam Leffler struct ath_buf *bf; 3108b032f27cSSam Leffler int slot, otherant; 3109b032f27cSSam Leffler uint32_t bfaddr; 31105591b213SSam Leffler 3111c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s: pending %u\n", 3112c42a7b7eSSam Leffler __func__, pending); 3113c42a7b7eSSam Leffler /* 3114c42a7b7eSSam Leffler * Check if the previous beacon has gone out. If 3115c66c48cbSSam Leffler * not don't try to post another, skip this period 3116c66c48cbSSam Leffler * and wait for the next. Missed beacons indicate 3117c66c48cbSSam Leffler * a problem and should not occur. If we miss too 3118c66c48cbSSam Leffler * many consecutive beacons reset the device. 3119c42a7b7eSSam Leffler */ 3120c42a7b7eSSam Leffler if (ath_hal_numtxpending(ah, sc->sc_bhalq) != 0) { 3121c42a7b7eSSam Leffler sc->sc_bmisscount++; 31224a3ac3fcSSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON, 3123c42a7b7eSSam Leffler "%s: missed %u consecutive beacons\n", 3124c42a7b7eSSam Leffler __func__, sc->sc_bmisscount); 3125a32ac9d3SSam Leffler if (sc->sc_bmisscount >= ath_bstuck_threshold) 31260bbf5441SSam Leffler taskqueue_enqueue(sc->sc_tq, &sc->sc_bstucktask); 3127c42a7b7eSSam Leffler return; 3128c42a7b7eSSam Leffler } 3129c42a7b7eSSam Leffler if (sc->sc_bmisscount != 0) { 3130c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON, 3131c42a7b7eSSam Leffler "%s: resume beacon xmit after %u misses\n", 3132c42a7b7eSSam Leffler __func__, sc->sc_bmisscount); 3133c42a7b7eSSam Leffler sc->sc_bmisscount = 0; 3134c42a7b7eSSam Leffler } 3135c42a7b7eSSam Leffler 3136b032f27cSSam Leffler if (sc->sc_stagbeacons) { /* staggered beacons */ 3137b032f27cSSam Leffler struct ieee80211com *ic = sc->sc_ifp->if_l2com; 3138b032f27cSSam Leffler uint32_t tsftu; 3139b032f27cSSam Leffler 3140b032f27cSSam Leffler tsftu = ath_hal_gettsf32(ah) >> 10; 3141b032f27cSSam Leffler /* XXX lintval */ 3142b032f27cSSam Leffler slot = ((tsftu % ic->ic_lintval) * ATH_BCBUF) / ic->ic_lintval; 3143b032f27cSSam Leffler vap = sc->sc_bslot[(slot+1) % ATH_BCBUF]; 3144b032f27cSSam Leffler bfaddr = 0; 3145b032f27cSSam Leffler if (vap != NULL && vap->iv_state == IEEE80211_S_RUN) { 3146b032f27cSSam Leffler bf = ath_beacon_generate(sc, vap); 3147b032f27cSSam Leffler if (bf != NULL) 3148b032f27cSSam Leffler bfaddr = bf->bf_daddr; 3149b032f27cSSam Leffler } 3150b032f27cSSam Leffler } else { /* burst'd beacons */ 3151b032f27cSSam Leffler uint32_t *bflink = &bfaddr; 3152b032f27cSSam Leffler 3153b032f27cSSam Leffler for (slot = 0; slot < ATH_BCBUF; slot++) { 3154b032f27cSSam Leffler vap = sc->sc_bslot[slot]; 3155b032f27cSSam Leffler if (vap != NULL && vap->iv_state == IEEE80211_S_RUN) { 3156b032f27cSSam Leffler bf = ath_beacon_generate(sc, vap); 3157b032f27cSSam Leffler if (bf != NULL) { 3158b032f27cSSam Leffler *bflink = bf->bf_daddr; 3159b032f27cSSam Leffler bflink = &bf->bf_desc->ds_link; 3160c42a7b7eSSam Leffler } 3161c42a7b7eSSam Leffler } 3162b032f27cSSam Leffler } 3163b032f27cSSam Leffler *bflink = 0; /* terminate list */ 3164622b3fd2SSam Leffler } 3165c42a7b7eSSam Leffler 3166c42a7b7eSSam Leffler /* 3167c42a7b7eSSam Leffler * Handle slot time change when a non-ERP station joins/leaves 3168c42a7b7eSSam Leffler * an 11g network. The 802.11 layer notifies us via callback, 3169c42a7b7eSSam Leffler * we mark updateslot, then wait one beacon before effecting 3170c42a7b7eSSam Leffler * the change. This gives associated stations at least one 3171c42a7b7eSSam Leffler * beacon interval to note the state change. 3172c42a7b7eSSam Leffler */ 3173c42a7b7eSSam Leffler /* XXX locking */ 3174b032f27cSSam Leffler if (sc->sc_updateslot == UPDATE) { 3175c42a7b7eSSam Leffler sc->sc_updateslot = COMMIT; /* commit next beacon */ 3176b032f27cSSam Leffler sc->sc_slotupdate = slot; 3177b032f27cSSam Leffler } else if (sc->sc_updateslot == COMMIT && sc->sc_slotupdate == slot) 3178c42a7b7eSSam Leffler ath_setslottime(sc); /* commit change to h/w */ 3179c42a7b7eSSam Leffler 3180c42a7b7eSSam Leffler /* 3181c42a7b7eSSam Leffler * Check recent per-antenna transmit statistics and flip 3182c42a7b7eSSam Leffler * the default antenna if noticeably more frames went out 3183c42a7b7eSSam Leffler * on the non-default antenna. 3184c42a7b7eSSam Leffler * XXX assumes 2 anntenae 3185c42a7b7eSSam Leffler */ 3186b032f27cSSam Leffler if (!sc->sc_diversity && (!sc->sc_stagbeacons || slot == 0)) { 3187c42a7b7eSSam Leffler otherant = sc->sc_defant & 1 ? 2 : 1; 3188c42a7b7eSSam Leffler if (sc->sc_ant_tx[otherant] > sc->sc_ant_tx[sc->sc_defant] + 2) 3189c42a7b7eSSam Leffler ath_setdefantenna(sc, otherant); 3190c42a7b7eSSam Leffler sc->sc_ant_tx[1] = sc->sc_ant_tx[2] = 0; 3191b032f27cSSam Leffler } 3192c42a7b7eSSam Leffler 3193b032f27cSSam Leffler if (bfaddr != 0) { 3194c42a7b7eSSam Leffler /* 3195c42a7b7eSSam Leffler * Stop any current dma and put the new frame on the queue. 3196c42a7b7eSSam Leffler * This should never fail since we check above that no frames 3197c42a7b7eSSam Leffler * are still pending on the queue. 3198c42a7b7eSSam Leffler */ 31995591b213SSam Leffler if (!ath_hal_stoptxdma(ah, sc->sc_bhalq)) { 3200c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, 3201c42a7b7eSSam Leffler "%s: beacon queue %u did not stop?\n", 3202c42a7b7eSSam Leffler __func__, sc->sc_bhalq); 32035591b213SSam Leffler } 3204b032f27cSSam Leffler /* NB: cabq traffic should already be queued and primed */ 3205b032f27cSSam Leffler ath_hal_puttxbuf(ah, sc->sc_bhalq, bfaddr); 3206b032f27cSSam Leffler ath_hal_txstart(ah, sc->sc_bhalq); 3207b032f27cSSam Leffler 3208b032f27cSSam Leffler sc->sc_stats.ast_be_xmit++; 3209b032f27cSSam Leffler } 3210b032f27cSSam Leffler } 3211b032f27cSSam Leffler 3212b032f27cSSam Leffler static struct ath_buf * 3213b032f27cSSam Leffler ath_beacon_generate(struct ath_softc *sc, struct ieee80211vap *vap) 3214b032f27cSSam Leffler { 3215b032f27cSSam Leffler struct ath_vap *avp = ATH_VAP(vap); 3216b032f27cSSam Leffler struct ath_txq *cabq = sc->sc_cabq; 3217b032f27cSSam Leffler struct ath_buf *bf; 3218b032f27cSSam Leffler struct mbuf *m; 3219b032f27cSSam Leffler int nmcastq, error; 3220b032f27cSSam Leffler 3221b032f27cSSam Leffler KASSERT(vap->iv_state == IEEE80211_S_RUN, 3222b032f27cSSam Leffler ("not running, state %d", vap->iv_state)); 3223b032f27cSSam Leffler KASSERT(avp->av_bcbuf != NULL, ("no beacon buffer")); 3224b032f27cSSam Leffler 3225b032f27cSSam Leffler /* 3226b032f27cSSam Leffler * Update dynamic beacon contents. If this returns 3227b032f27cSSam Leffler * non-zero then we need to remap the memory because 3228b032f27cSSam Leffler * the beacon frame changed size (probably because 3229b032f27cSSam Leffler * of the TIM bitmap). 3230b032f27cSSam Leffler */ 3231b032f27cSSam Leffler bf = avp->av_bcbuf; 3232b032f27cSSam Leffler m = bf->bf_m; 3233b032f27cSSam Leffler nmcastq = avp->av_mcastq.axq_depth; 3234b032f27cSSam Leffler if (ieee80211_beacon_update(bf->bf_node, &avp->av_boff, m, nmcastq)) { 3235b032f27cSSam Leffler /* XXX too conservative? */ 3236b032f27cSSam Leffler bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 3237b032f27cSSam Leffler error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m, 3238b032f27cSSam Leffler bf->bf_segs, &bf->bf_nseg, 3239b032f27cSSam Leffler BUS_DMA_NOWAIT); 3240b032f27cSSam Leffler if (error != 0) { 3241b032f27cSSam Leffler if_printf(vap->iv_ifp, 3242b032f27cSSam Leffler "%s: bus_dmamap_load_mbuf_sg failed, error %u\n", 3243b032f27cSSam Leffler __func__, error); 3244b032f27cSSam Leffler return NULL; 3245b032f27cSSam Leffler } 3246b032f27cSSam Leffler } 3247b032f27cSSam Leffler if ((avp->av_boff.bo_tim[4] & 1) && cabq->axq_depth) { 3248b032f27cSSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON, 3249b032f27cSSam Leffler "%s: cabq did not drain, mcastq %u cabq %u\n", 3250b032f27cSSam Leffler __func__, nmcastq, cabq->axq_depth); 3251b032f27cSSam Leffler sc->sc_stats.ast_cabq_busy++; 3252b032f27cSSam Leffler if (sc->sc_nvaps > 1 && sc->sc_stagbeacons) { 3253b032f27cSSam Leffler /* 3254b032f27cSSam Leffler * CABQ traffic from a previous vap is still pending. 3255b032f27cSSam Leffler * We must drain the q before this beacon frame goes 3256b032f27cSSam Leffler * out as otherwise this vap's stations will get cab 3257b032f27cSSam Leffler * frames from a different vap. 3258b032f27cSSam Leffler * XXX could be slow causing us to miss DBA 3259b032f27cSSam Leffler */ 3260b032f27cSSam Leffler ath_tx_draintxq(sc, cabq); 3261b032f27cSSam Leffler } 3262b032f27cSSam Leffler } 3263b032f27cSSam Leffler ath_beacon_setup(sc, bf); 32645591b213SSam Leffler bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE); 32655591b213SSam Leffler 3266c42a7b7eSSam Leffler /* 3267c42a7b7eSSam Leffler * Enable the CAB queue before the beacon queue to 3268c42a7b7eSSam Leffler * insure cab frames are triggered by this beacon. 3269c42a7b7eSSam Leffler */ 3270b032f27cSSam Leffler if (avp->av_boff.bo_tim[4] & 1) { 3271b032f27cSSam Leffler struct ath_hal *ah = sc->sc_ah; 3272b032f27cSSam Leffler 3273f3af83f7SSam Leffler /* NB: only at DTIM */ 3274622b3fd2SSam Leffler ATH_TXQ_LOCK(cabq); 3275b032f27cSSam Leffler ATH_TXQ_LOCK(&avp->av_mcastq); 3276622b3fd2SSam Leffler if (nmcastq) { 3277622b3fd2SSam Leffler struct ath_buf *bfm; 3278622b3fd2SSam Leffler 3279622b3fd2SSam Leffler /* 3280622b3fd2SSam Leffler * Move frames from the s/w mcast q to the h/w cab q. 3281b032f27cSSam Leffler * XXX MORE_DATA bit 3282622b3fd2SSam Leffler */ 3283b032f27cSSam Leffler bfm = STAILQ_FIRST(&avp->av_mcastq.axq_q); 3284622b3fd2SSam Leffler if (cabq->axq_link != NULL) { 3285622b3fd2SSam Leffler *cabq->axq_link = bfm->bf_daddr; 3286622b3fd2SSam Leffler } else 3287622b3fd2SSam Leffler ath_hal_puttxbuf(ah, cabq->axq_qnum, 3288622b3fd2SSam Leffler bfm->bf_daddr); 3289b032f27cSSam Leffler ath_txqmove(cabq, &avp->av_mcastq); 3290622b3fd2SSam Leffler 3291622b3fd2SSam Leffler sc->sc_stats.ast_cabq_xmit += nmcastq; 3292622b3fd2SSam Leffler } 3293622b3fd2SSam Leffler /* NB: gated by beacon so safe to start here */ 3294622b3fd2SSam Leffler ath_hal_txstart(ah, cabq->axq_qnum); 3295622b3fd2SSam Leffler ATH_TXQ_UNLOCK(cabq); 3296b032f27cSSam Leffler ATH_TXQ_UNLOCK(&avp->av_mcastq); 3297622b3fd2SSam Leffler } 3298b032f27cSSam Leffler return bf; 3299b032f27cSSam Leffler } 3300b032f27cSSam Leffler 3301b032f27cSSam Leffler static void 3302b032f27cSSam Leffler ath_beacon_start_adhoc(struct ath_softc *sc, struct ieee80211vap *vap) 3303b032f27cSSam Leffler { 3304b032f27cSSam Leffler struct ath_vap *avp = ATH_VAP(vap); 3305b032f27cSSam Leffler struct ath_hal *ah = sc->sc_ah; 3306b032f27cSSam Leffler struct ath_buf *bf; 3307b032f27cSSam Leffler struct mbuf *m; 3308b032f27cSSam Leffler int error; 3309b032f27cSSam Leffler 3310b032f27cSSam Leffler KASSERT(avp->av_bcbuf != NULL, ("no beacon buffer")); 3311b032f27cSSam Leffler 3312b032f27cSSam Leffler /* 3313b032f27cSSam Leffler * Update dynamic beacon contents. If this returns 3314b032f27cSSam Leffler * non-zero then we need to remap the memory because 3315b032f27cSSam Leffler * the beacon frame changed size (probably because 3316b032f27cSSam Leffler * of the TIM bitmap). 3317b032f27cSSam Leffler */ 3318b032f27cSSam Leffler bf = avp->av_bcbuf; 3319b032f27cSSam Leffler m = bf->bf_m; 3320b032f27cSSam Leffler if (ieee80211_beacon_update(bf->bf_node, &avp->av_boff, m, 0)) { 3321b032f27cSSam Leffler /* XXX too conservative? */ 3322b032f27cSSam Leffler bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 3323b032f27cSSam Leffler error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m, 3324b032f27cSSam Leffler bf->bf_segs, &bf->bf_nseg, 3325b032f27cSSam Leffler BUS_DMA_NOWAIT); 3326b032f27cSSam Leffler if (error != 0) { 3327b032f27cSSam Leffler if_printf(vap->iv_ifp, 3328b032f27cSSam Leffler "%s: bus_dmamap_load_mbuf_sg failed, error %u\n", 3329b032f27cSSam Leffler __func__, error); 3330b032f27cSSam Leffler return; 3331b032f27cSSam Leffler } 3332b032f27cSSam Leffler } 3333b032f27cSSam Leffler ath_beacon_setup(sc, bf); 3334b032f27cSSam Leffler bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE); 3335b032f27cSSam Leffler 3336b032f27cSSam Leffler /* NB: caller is known to have already stopped tx dma */ 33375591b213SSam Leffler ath_hal_puttxbuf(ah, sc->sc_bhalq, bf->bf_daddr); 33385591b213SSam Leffler ath_hal_txstart(ah, sc->sc_bhalq); 33395591b213SSam Leffler } 33405591b213SSam Leffler 3341c42a7b7eSSam Leffler /* 3342c42a7b7eSSam Leffler * Reset the hardware after detecting beacons have stopped. 3343c42a7b7eSSam Leffler */ 3344c42a7b7eSSam Leffler static void 3345c42a7b7eSSam Leffler ath_bstuck_proc(void *arg, int pending) 3346c42a7b7eSSam Leffler { 3347c42a7b7eSSam Leffler struct ath_softc *sc = arg; 3348fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 3349c42a7b7eSSam Leffler 3350c42a7b7eSSam Leffler if_printf(ifp, "stuck beacon; resetting (bmiss count %u)\n", 3351c42a7b7eSSam Leffler sc->sc_bmisscount); 3352c2e34459SSam Leffler sc->sc_stats.ast_bstuck++; 3353c42a7b7eSSam Leffler ath_reset(ifp); 3354c42a7b7eSSam Leffler } 3355c42a7b7eSSam Leffler 3356c42a7b7eSSam Leffler /* 3357b032f27cSSam Leffler * Reclaim beacon resources and return buffer to the pool. 3358b032f27cSSam Leffler */ 3359b032f27cSSam Leffler static void 3360b032f27cSSam Leffler ath_beacon_return(struct ath_softc *sc, struct ath_buf *bf) 3361b032f27cSSam Leffler { 3362b032f27cSSam Leffler 3363b032f27cSSam Leffler if (bf->bf_m != NULL) { 3364b032f27cSSam Leffler bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 3365b032f27cSSam Leffler m_freem(bf->bf_m); 3366b032f27cSSam Leffler bf->bf_m = NULL; 3367b032f27cSSam Leffler } 3368b032f27cSSam Leffler if (bf->bf_node != NULL) { 3369b032f27cSSam Leffler ieee80211_free_node(bf->bf_node); 3370b032f27cSSam Leffler bf->bf_node = NULL; 3371b032f27cSSam Leffler } 3372b032f27cSSam Leffler STAILQ_INSERT_TAIL(&sc->sc_bbuf, bf, bf_list); 3373b032f27cSSam Leffler } 3374b032f27cSSam Leffler 3375b032f27cSSam Leffler /* 3376c42a7b7eSSam Leffler * Reclaim beacon resources. 3377c42a7b7eSSam Leffler */ 33785591b213SSam Leffler static void 33795591b213SSam Leffler ath_beacon_free(struct ath_softc *sc) 33805591b213SSam Leffler { 3381c42a7b7eSSam Leffler struct ath_buf *bf; 33825591b213SSam Leffler 3383f818612bSSam Leffler STAILQ_FOREACH(bf, &sc->sc_bbuf, bf_list) { 33845591b213SSam Leffler if (bf->bf_m != NULL) { 33855591b213SSam Leffler bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 33865591b213SSam Leffler m_freem(bf->bf_m); 33875591b213SSam Leffler bf->bf_m = NULL; 3388f818612bSSam Leffler } 3389f818612bSSam Leffler if (bf->bf_node != NULL) { 3390f818612bSSam Leffler ieee80211_free_node(bf->bf_node); 33915591b213SSam Leffler bf->bf_node = NULL; 33925591b213SSam Leffler } 33935591b213SSam Leffler } 3394f818612bSSam Leffler } 33955591b213SSam Leffler 33965591b213SSam Leffler /* 33975591b213SSam Leffler * Configure the beacon and sleep timers. 33985591b213SSam Leffler * 33995591b213SSam Leffler * When operating as an AP this resets the TSF and sets 34005591b213SSam Leffler * up the hardware to notify us when we need to issue beacons. 34015591b213SSam Leffler * 34025591b213SSam Leffler * When operating in station mode this sets up the beacon 34035591b213SSam Leffler * timers according to the timestamp of the last received 34045591b213SSam Leffler * beacon and the current TSF, configures PCF and DTIM 34055591b213SSam Leffler * handling, programs the sleep registers so the hardware 34065591b213SSam Leffler * will wakeup in time to receive beacons, and configures 34075591b213SSam Leffler * the beacon miss handling so we'll receive a BMISS 34085591b213SSam Leffler * interrupt when we stop seeing beacons from the AP 34095591b213SSam Leffler * we've associated with. 34105591b213SSam Leffler */ 34115591b213SSam Leffler static void 3412b032f27cSSam Leffler ath_beacon_config(struct ath_softc *sc, struct ieee80211vap *vap) 34135591b213SSam Leffler { 341480d939bfSSam Leffler #define TSF_TO_TU(_h,_l) \ 341580d939bfSSam Leffler ((((u_int32_t)(_h)) << 22) | (((u_int32_t)(_l)) >> 10)) 341680d939bfSSam Leffler #define FUDGE 2 34175591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 3418b032f27cSSam Leffler struct ieee80211com *ic = sc->sc_ifp->if_l2com; 3419b032f27cSSam Leffler struct ieee80211_node *ni; 342080d939bfSSam Leffler u_int32_t nexttbtt, intval, tsftu; 342180d939bfSSam Leffler u_int64_t tsf; 34225591b213SSam Leffler 3423b032f27cSSam Leffler if (vap == NULL) 3424b032f27cSSam Leffler vap = TAILQ_FIRST(&ic->ic_vaps); /* XXX */ 3425b032f27cSSam Leffler ni = vap->iv_bss; 3426b032f27cSSam Leffler 34278371372bSSam Leffler /* extract tstamp from last beacon and convert to TU */ 34288371372bSSam Leffler nexttbtt = TSF_TO_TU(LE_READ_4(ni->ni_tstamp.data + 4), 34298371372bSSam Leffler LE_READ_4(ni->ni_tstamp.data)); 3430b032f27cSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 3431b032f27cSSam Leffler /* 3432b032f27cSSam Leffler * For multi-bss ap support beacons are either staggered 3433b032f27cSSam Leffler * evenly over N slots or burst together. For the former 3434b032f27cSSam Leffler * arrange for the SWBA to be delivered for each slot. 3435b032f27cSSam Leffler * Slots that are not occupied will generate nothing. 3436b032f27cSSam Leffler */ 34378371372bSSam Leffler /* NB: the beacon interval is kept internally in TU's */ 34384bacf7c1SSam Leffler intval = ni->ni_intval & HAL_BEACON_PERIOD; 3439b032f27cSSam Leffler if (sc->sc_stagbeacons) 3440b032f27cSSam Leffler intval /= ATH_BCBUF; 3441b032f27cSSam Leffler } else { 3442b032f27cSSam Leffler /* NB: the beacon interval is kept internally in TU's */ 3443b032f27cSSam Leffler intval = ni->ni_intval & HAL_BEACON_PERIOD; 3444b032f27cSSam Leffler } 3445a6c992f4SSam Leffler if (nexttbtt == 0) /* e.g. for ap mode */ 3446a6c992f4SSam Leffler nexttbtt = intval; 3447a6c992f4SSam Leffler else if (intval) /* NB: can be 0 for monitor mode */ 3448a6c992f4SSam Leffler nexttbtt = roundup(nexttbtt, intval); 3449a6c992f4SSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON, "%s: nexttbtt %u intval %u (%u)\n", 3450a6c992f4SSam Leffler __func__, nexttbtt, intval, ni->ni_intval); 3451b032f27cSSam Leffler if (ic->ic_opmode == IEEE80211_M_STA && !sc->sc_swbmiss) { 34525591b213SSam Leffler HAL_BEACON_STATE bs; 34538371372bSSam Leffler int dtimperiod, dtimcount; 34548371372bSSam Leffler int cfpperiod, cfpcount; 34555591b213SSam Leffler 34568371372bSSam Leffler /* 34578371372bSSam Leffler * Setup dtim and cfp parameters according to 34588371372bSSam Leffler * last beacon we received (which may be none). 34598371372bSSam Leffler */ 34608371372bSSam Leffler dtimperiod = ni->ni_dtim_period; 34618371372bSSam Leffler if (dtimperiod <= 0) /* NB: 0 if not known */ 34628371372bSSam Leffler dtimperiod = 1; 34638371372bSSam Leffler dtimcount = ni->ni_dtim_count; 34648371372bSSam Leffler if (dtimcount >= dtimperiod) /* NB: sanity check */ 34658371372bSSam Leffler dtimcount = 0; /* XXX? */ 34668371372bSSam Leffler cfpperiod = 1; /* NB: no PCF support yet */ 34678371372bSSam Leffler cfpcount = 0; 34688371372bSSam Leffler /* 34698371372bSSam Leffler * Pull nexttbtt forward to reflect the current 34708371372bSSam Leffler * TSF and calculate dtim+cfp state for the result. 34718371372bSSam Leffler */ 34728371372bSSam Leffler tsf = ath_hal_gettsf64(ah); 347380d939bfSSam Leffler tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; 34748371372bSSam Leffler do { 34758371372bSSam Leffler nexttbtt += intval; 34768371372bSSam Leffler if (--dtimcount < 0) { 34778371372bSSam Leffler dtimcount = dtimperiod - 1; 34788371372bSSam Leffler if (--cfpcount < 0) 34798371372bSSam Leffler cfpcount = cfpperiod - 1; 34808371372bSSam Leffler } 34818371372bSSam Leffler } while (nexttbtt < tsftu); 34825591b213SSam Leffler memset(&bs, 0, sizeof(bs)); 3483a6c992f4SSam Leffler bs.bs_intval = intval; 34845591b213SSam Leffler bs.bs_nexttbtt = nexttbtt; 34858371372bSSam Leffler bs.bs_dtimperiod = dtimperiod*intval; 34868371372bSSam Leffler bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval; 34878371372bSSam Leffler bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod; 34888371372bSSam Leffler bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod; 34898371372bSSam Leffler bs.bs_cfpmaxduration = 0; 34908371372bSSam Leffler #if 0 34915591b213SSam Leffler /* 3492c42a7b7eSSam Leffler * The 802.11 layer records the offset to the DTIM 3493c42a7b7eSSam Leffler * bitmap while receiving beacons; use it here to 3494c42a7b7eSSam Leffler * enable h/w detection of our AID being marked in 3495c42a7b7eSSam Leffler * the bitmap vector (to indicate frames for us are 3496c42a7b7eSSam Leffler * pending at the AP). 34978371372bSSam Leffler * XXX do DTIM handling in s/w to WAR old h/w bugs 34988371372bSSam Leffler * XXX enable based on h/w rev for newer chips 3499c42a7b7eSSam Leffler */ 3500c42a7b7eSSam Leffler bs.bs_timoffset = ni->ni_timoff; 35018371372bSSam Leffler #endif 3502c42a7b7eSSam Leffler /* 35035591b213SSam Leffler * Calculate the number of consecutive beacons to miss 350468e8e04eSSam Leffler * before taking a BMISS interrupt. 35055591b213SSam Leffler * Note that we clamp the result to at most 10 beacons. 35065591b213SSam Leffler */ 3507b032f27cSSam Leffler bs.bs_bmissthreshold = vap->iv_bmissthreshold; 35085591b213SSam Leffler if (bs.bs_bmissthreshold > 10) 35095591b213SSam Leffler bs.bs_bmissthreshold = 10; 35105591b213SSam Leffler else if (bs.bs_bmissthreshold <= 0) 35115591b213SSam Leffler bs.bs_bmissthreshold = 1; 35125591b213SSam Leffler 35135591b213SSam Leffler /* 35145591b213SSam Leffler * Calculate sleep duration. The configuration is 35155591b213SSam Leffler * given in ms. We insure a multiple of the beacon 35165591b213SSam Leffler * period is used. Also, if the sleep duration is 35175591b213SSam Leffler * greater than the DTIM period then it makes senses 35185591b213SSam Leffler * to make it a multiple of that. 35195591b213SSam Leffler * 35205591b213SSam Leffler * XXX fixed at 100ms 35215591b213SSam Leffler */ 35224bacf7c1SSam Leffler bs.bs_sleepduration = 35234bacf7c1SSam Leffler roundup(IEEE80211_MS_TO_TU(100), bs.bs_intval); 35245591b213SSam Leffler if (bs.bs_sleepduration > bs.bs_dtimperiod) 35255591b213SSam Leffler bs.bs_sleepduration = roundup(bs.bs_sleepduration, bs.bs_dtimperiod); 35265591b213SSam Leffler 3527c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON, 35288371372bSSam 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" 35295591b213SSam Leffler , __func__ 35308371372bSSam Leffler , tsf, tsftu 35315591b213SSam Leffler , bs.bs_intval 35325591b213SSam Leffler , bs.bs_nexttbtt 35335591b213SSam Leffler , bs.bs_dtimperiod 35345591b213SSam Leffler , bs.bs_nextdtim 35355591b213SSam Leffler , bs.bs_bmissthreshold 35365591b213SSam Leffler , bs.bs_sleepduration 3537c42a7b7eSSam Leffler , bs.bs_cfpperiod 3538c42a7b7eSSam Leffler , bs.bs_cfpmaxduration 3539c42a7b7eSSam Leffler , bs.bs_cfpnext 3540c42a7b7eSSam Leffler , bs.bs_timoffset 3541c42a7b7eSSam Leffler ); 35425591b213SSam Leffler ath_hal_intrset(ah, 0); 3543c42a7b7eSSam Leffler ath_hal_beacontimers(ah, &bs); 35445591b213SSam Leffler sc->sc_imask |= HAL_INT_BMISS; 35455591b213SSam Leffler ath_hal_intrset(ah, sc->sc_imask); 35465591b213SSam Leffler } else { 35475591b213SSam Leffler ath_hal_intrset(ah, 0); 3548a6c992f4SSam Leffler if (nexttbtt == intval) 3549c42a7b7eSSam Leffler intval |= HAL_BEACON_RESET_TSF; 3550c42a7b7eSSam Leffler if (ic->ic_opmode == IEEE80211_M_IBSS) { 3551c42a7b7eSSam Leffler /* 3552c42a7b7eSSam Leffler * In IBSS mode enable the beacon timers but only 3553c42a7b7eSSam Leffler * enable SWBA interrupts if we need to manually 3554c42a7b7eSSam Leffler * prepare beacon frames. Otherwise we use a 3555c42a7b7eSSam Leffler * self-linked tx descriptor and let the hardware 3556c42a7b7eSSam Leffler * deal with things. 3557c42a7b7eSSam Leffler */ 3558c42a7b7eSSam Leffler intval |= HAL_BEACON_ENA; 3559c42a7b7eSSam Leffler if (!sc->sc_hasveol) 3560c42a7b7eSSam Leffler sc->sc_imask |= HAL_INT_SWBA; 356180d939bfSSam Leffler if ((intval & HAL_BEACON_RESET_TSF) == 0) { 356280d939bfSSam Leffler /* 356380d939bfSSam Leffler * Pull nexttbtt forward to reflect 356480d939bfSSam Leffler * the current TSF. 356580d939bfSSam Leffler */ 356680d939bfSSam Leffler tsf = ath_hal_gettsf64(ah); 356780d939bfSSam Leffler tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; 356880d939bfSSam Leffler do { 356980d939bfSSam Leffler nexttbtt += intval; 357080d939bfSSam Leffler } while (nexttbtt < tsftu); 357180d939bfSSam Leffler } 35720f2e86fbSSam Leffler ath_beaconq_config(sc); 3573c42a7b7eSSam Leffler } else if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 3574c42a7b7eSSam Leffler /* 3575c42a7b7eSSam Leffler * In AP mode we enable the beacon timers and 3576c42a7b7eSSam Leffler * SWBA interrupts to prepare beacon frames. 3577c42a7b7eSSam Leffler */ 3578c42a7b7eSSam Leffler intval |= HAL_BEACON_ENA; 35795591b213SSam Leffler sc->sc_imask |= HAL_INT_SWBA; /* beacon prepare */ 35800f2e86fbSSam Leffler ath_beaconq_config(sc); 3581c42a7b7eSSam Leffler } 3582c42a7b7eSSam Leffler ath_hal_beaconinit(ah, nexttbtt, intval); 3583c42a7b7eSSam Leffler sc->sc_bmisscount = 0; 35845591b213SSam Leffler ath_hal_intrset(ah, sc->sc_imask); 3585c42a7b7eSSam Leffler /* 3586c42a7b7eSSam Leffler * When using a self-linked beacon descriptor in 3587c42a7b7eSSam Leffler * ibss mode load it once here. 3588c42a7b7eSSam Leffler */ 3589c42a7b7eSSam Leffler if (ic->ic_opmode == IEEE80211_M_IBSS && sc->sc_hasveol) 3590b032f27cSSam Leffler ath_beacon_start_adhoc(sc, vap); 35915591b213SSam Leffler } 359280d939bfSSam Leffler sc->sc_syncbeacon = 0; 359380d939bfSSam Leffler #undef FUDGE 35948371372bSSam Leffler #undef TSF_TO_TU 35955591b213SSam Leffler } 35965591b213SSam Leffler 35975591b213SSam Leffler static void 35985591b213SSam Leffler ath_load_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 35995591b213SSam Leffler { 36005591b213SSam Leffler bus_addr_t *paddr = (bus_addr_t*) arg; 3601d77367bfSSam Leffler KASSERT(error == 0, ("error %u on bus_dma callback", error)); 36025591b213SSam Leffler *paddr = segs->ds_addr; 36035591b213SSam Leffler } 36045591b213SSam Leffler 36055591b213SSam Leffler static int 3606c42a7b7eSSam Leffler ath_descdma_setup(struct ath_softc *sc, 3607c42a7b7eSSam Leffler struct ath_descdma *dd, ath_bufhead *head, 3608c42a7b7eSSam Leffler const char *name, int nbuf, int ndesc) 3609c42a7b7eSSam Leffler { 3610c42a7b7eSSam Leffler #define DS2PHYS(_dd, _ds) \ 3611c42a7b7eSSam Leffler ((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc)) 3612fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 3613c42a7b7eSSam Leffler struct ath_desc *ds; 3614c42a7b7eSSam Leffler struct ath_buf *bf; 3615c42a7b7eSSam Leffler int i, bsize, error; 3616c42a7b7eSSam Leffler 3617c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_RESET, "%s: %s DMA: %u buffers %u desc/buf\n", 3618c42a7b7eSSam Leffler __func__, name, nbuf, ndesc); 3619c42a7b7eSSam Leffler 3620c42a7b7eSSam Leffler dd->dd_name = name; 3621c42a7b7eSSam Leffler dd->dd_desc_len = sizeof(struct ath_desc) * nbuf * ndesc; 3622c42a7b7eSSam Leffler 3623c42a7b7eSSam Leffler /* 3624c42a7b7eSSam Leffler * Setup DMA descriptor area. 3625c42a7b7eSSam Leffler */ 3626c2175ff5SMarius Strobl error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), /* parent */ 3627c42a7b7eSSam Leffler PAGE_SIZE, 0, /* alignment, bounds */ 3628c42a7b7eSSam Leffler BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 3629c42a7b7eSSam Leffler BUS_SPACE_MAXADDR, /* highaddr */ 3630c42a7b7eSSam Leffler NULL, NULL, /* filter, filterarg */ 3631c42a7b7eSSam Leffler dd->dd_desc_len, /* maxsize */ 3632c42a7b7eSSam Leffler 1, /* nsegments */ 36336ccb8ea7SSam Leffler dd->dd_desc_len, /* maxsegsize */ 3634c42a7b7eSSam Leffler BUS_DMA_ALLOCNOW, /* flags */ 3635c42a7b7eSSam Leffler NULL, /* lockfunc */ 3636c42a7b7eSSam Leffler NULL, /* lockarg */ 3637c42a7b7eSSam Leffler &dd->dd_dmat); 3638c42a7b7eSSam Leffler if (error != 0) { 3639c42a7b7eSSam Leffler if_printf(ifp, "cannot allocate %s DMA tag\n", dd->dd_name); 3640c42a7b7eSSam Leffler return error; 3641c42a7b7eSSam Leffler } 3642c42a7b7eSSam Leffler 3643c42a7b7eSSam Leffler /* allocate descriptors */ 3644c42a7b7eSSam Leffler error = bus_dmamap_create(dd->dd_dmat, BUS_DMA_NOWAIT, &dd->dd_dmamap); 3645c42a7b7eSSam Leffler if (error != 0) { 3646c42a7b7eSSam Leffler if_printf(ifp, "unable to create dmamap for %s descriptors, " 3647c42a7b7eSSam Leffler "error %u\n", dd->dd_name, error); 3648c42a7b7eSSam Leffler goto fail0; 3649c42a7b7eSSam Leffler } 3650c42a7b7eSSam Leffler 3651c42a7b7eSSam Leffler error = bus_dmamem_alloc(dd->dd_dmat, (void**) &dd->dd_desc, 36520553a01fSSam Leffler BUS_DMA_NOWAIT | BUS_DMA_COHERENT, 36530553a01fSSam Leffler &dd->dd_dmamap); 3654c42a7b7eSSam Leffler if (error != 0) { 3655c42a7b7eSSam Leffler if_printf(ifp, "unable to alloc memory for %u %s descriptors, " 3656c42a7b7eSSam Leffler "error %u\n", nbuf * ndesc, dd->dd_name, error); 3657c42a7b7eSSam Leffler goto fail1; 3658c42a7b7eSSam Leffler } 3659c42a7b7eSSam Leffler 3660c42a7b7eSSam Leffler error = bus_dmamap_load(dd->dd_dmat, dd->dd_dmamap, 3661c42a7b7eSSam Leffler dd->dd_desc, dd->dd_desc_len, 3662c42a7b7eSSam Leffler ath_load_cb, &dd->dd_desc_paddr, 3663c42a7b7eSSam Leffler BUS_DMA_NOWAIT); 3664c42a7b7eSSam Leffler if (error != 0) { 3665c42a7b7eSSam Leffler if_printf(ifp, "unable to map %s descriptors, error %u\n", 3666c42a7b7eSSam Leffler dd->dd_name, error); 3667c42a7b7eSSam Leffler goto fail2; 3668c42a7b7eSSam Leffler } 3669c42a7b7eSSam Leffler 3670c42a7b7eSSam Leffler ds = dd->dd_desc; 3671c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_RESET, "%s: %s DMA map: %p (%lu) -> %p (%lu)\n", 3672c42a7b7eSSam Leffler __func__, dd->dd_name, ds, (u_long) dd->dd_desc_len, 3673c42a7b7eSSam Leffler (caddr_t) dd->dd_desc_paddr, /*XXX*/ (u_long) dd->dd_desc_len); 3674c42a7b7eSSam Leffler 3675ebecf802SSam Leffler /* allocate rx buffers */ 3676c42a7b7eSSam Leffler bsize = sizeof(struct ath_buf) * nbuf; 3677c42a7b7eSSam Leffler bf = malloc(bsize, M_ATHDEV, M_NOWAIT | M_ZERO); 3678c42a7b7eSSam Leffler if (bf == NULL) { 3679c42a7b7eSSam Leffler if_printf(ifp, "malloc of %s buffers failed, size %u\n", 3680c42a7b7eSSam Leffler dd->dd_name, bsize); 3681c42a7b7eSSam Leffler goto fail3; 3682c42a7b7eSSam Leffler } 3683c42a7b7eSSam Leffler dd->dd_bufptr = bf; 3684c42a7b7eSSam Leffler 3685c42a7b7eSSam Leffler STAILQ_INIT(head); 3686c42a7b7eSSam Leffler for (i = 0; i < nbuf; i++, bf++, ds += ndesc) { 3687c42a7b7eSSam Leffler bf->bf_desc = ds; 3688c42a7b7eSSam Leffler bf->bf_daddr = DS2PHYS(dd, ds); 3689c42a7b7eSSam Leffler error = bus_dmamap_create(sc->sc_dmat, BUS_DMA_NOWAIT, 3690c42a7b7eSSam Leffler &bf->bf_dmamap); 3691c42a7b7eSSam Leffler if (error != 0) { 3692c42a7b7eSSam Leffler if_printf(ifp, "unable to create dmamap for %s " 3693c42a7b7eSSam Leffler "buffer %u, error %u\n", dd->dd_name, i, error); 3694c42a7b7eSSam Leffler ath_descdma_cleanup(sc, dd, head); 3695c42a7b7eSSam Leffler return error; 3696c42a7b7eSSam Leffler } 3697c42a7b7eSSam Leffler STAILQ_INSERT_TAIL(head, bf, bf_list); 3698c42a7b7eSSam Leffler } 3699c42a7b7eSSam Leffler return 0; 3700c42a7b7eSSam Leffler fail3: 3701c42a7b7eSSam Leffler bus_dmamap_unload(dd->dd_dmat, dd->dd_dmamap); 3702c42a7b7eSSam Leffler fail2: 3703c42a7b7eSSam Leffler bus_dmamem_free(dd->dd_dmat, dd->dd_desc, dd->dd_dmamap); 3704c42a7b7eSSam Leffler fail1: 3705c42a7b7eSSam Leffler bus_dmamap_destroy(dd->dd_dmat, dd->dd_dmamap); 3706c42a7b7eSSam Leffler fail0: 3707c42a7b7eSSam Leffler bus_dma_tag_destroy(dd->dd_dmat); 3708c42a7b7eSSam Leffler memset(dd, 0, sizeof(*dd)); 3709c42a7b7eSSam Leffler return error; 3710c42a7b7eSSam Leffler #undef DS2PHYS 3711c42a7b7eSSam Leffler } 3712c42a7b7eSSam Leffler 3713c42a7b7eSSam Leffler static void 3714c42a7b7eSSam Leffler ath_descdma_cleanup(struct ath_softc *sc, 3715c42a7b7eSSam Leffler struct ath_descdma *dd, ath_bufhead *head) 3716c42a7b7eSSam Leffler { 3717c42a7b7eSSam Leffler struct ath_buf *bf; 3718c42a7b7eSSam Leffler struct ieee80211_node *ni; 3719c42a7b7eSSam Leffler 3720c42a7b7eSSam Leffler bus_dmamap_unload(dd->dd_dmat, dd->dd_dmamap); 3721c42a7b7eSSam Leffler bus_dmamem_free(dd->dd_dmat, dd->dd_desc, dd->dd_dmamap); 3722c42a7b7eSSam Leffler bus_dmamap_destroy(dd->dd_dmat, dd->dd_dmamap); 3723c42a7b7eSSam Leffler bus_dma_tag_destroy(dd->dd_dmat); 3724c42a7b7eSSam Leffler 3725c42a7b7eSSam Leffler STAILQ_FOREACH(bf, head, bf_list) { 3726c42a7b7eSSam Leffler if (bf->bf_m) { 3727c42a7b7eSSam Leffler m_freem(bf->bf_m); 3728c42a7b7eSSam Leffler bf->bf_m = NULL; 3729c42a7b7eSSam Leffler } 3730c42a7b7eSSam Leffler if (bf->bf_dmamap != NULL) { 3731c42a7b7eSSam Leffler bus_dmamap_destroy(sc->sc_dmat, bf->bf_dmamap); 3732c42a7b7eSSam Leffler bf->bf_dmamap = NULL; 3733c42a7b7eSSam Leffler } 3734c42a7b7eSSam Leffler ni = bf->bf_node; 3735c42a7b7eSSam Leffler bf->bf_node = NULL; 3736c42a7b7eSSam Leffler if (ni != NULL) { 3737c42a7b7eSSam Leffler /* 3738c42a7b7eSSam Leffler * Reclaim node reference. 3739c42a7b7eSSam Leffler */ 3740c42a7b7eSSam Leffler ieee80211_free_node(ni); 3741c42a7b7eSSam Leffler } 3742c42a7b7eSSam Leffler } 3743c42a7b7eSSam Leffler 3744c42a7b7eSSam Leffler STAILQ_INIT(head); 3745c42a7b7eSSam Leffler free(dd->dd_bufptr, M_ATHDEV); 3746c42a7b7eSSam Leffler memset(dd, 0, sizeof(*dd)); 3747c42a7b7eSSam Leffler } 3748c42a7b7eSSam Leffler 3749c42a7b7eSSam Leffler static int 37505591b213SSam Leffler ath_desc_alloc(struct ath_softc *sc) 37515591b213SSam Leffler { 3752c42a7b7eSSam Leffler int error; 37535591b213SSam Leffler 3754c42a7b7eSSam Leffler error = ath_descdma_setup(sc, &sc->sc_rxdma, &sc->sc_rxbuf, 3755e2d787faSSam Leffler "rx", ath_rxbuf, 1); 37565591b213SSam Leffler if (error != 0) 37575591b213SSam Leffler return error; 37585591b213SSam Leffler 3759c42a7b7eSSam Leffler error = ath_descdma_setup(sc, &sc->sc_txdma, &sc->sc_txbuf, 3760e2d787faSSam Leffler "tx", ath_txbuf, ATH_TXDESC); 3761c42a7b7eSSam Leffler if (error != 0) { 3762c42a7b7eSSam Leffler ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf); 37635591b213SSam Leffler return error; 3764c42a7b7eSSam Leffler } 3765c42a7b7eSSam Leffler 3766c42a7b7eSSam Leffler error = ath_descdma_setup(sc, &sc->sc_bdma, &sc->sc_bbuf, 3767b032f27cSSam Leffler "beacon", ATH_BCBUF, 1); 3768c42a7b7eSSam Leffler if (error != 0) { 3769c42a7b7eSSam Leffler ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf); 3770c42a7b7eSSam Leffler ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf); 3771c42a7b7eSSam Leffler return error; 3772c42a7b7eSSam Leffler } 37735591b213SSam Leffler return 0; 37745591b213SSam Leffler } 37755591b213SSam Leffler 37765591b213SSam Leffler static void 37775591b213SSam Leffler ath_desc_free(struct ath_softc *sc) 37785591b213SSam Leffler { 37795591b213SSam Leffler 3780c42a7b7eSSam Leffler if (sc->sc_bdma.dd_desc_len != 0) 3781c42a7b7eSSam Leffler ath_descdma_cleanup(sc, &sc->sc_bdma, &sc->sc_bbuf); 3782c42a7b7eSSam Leffler if (sc->sc_txdma.dd_desc_len != 0) 3783c42a7b7eSSam Leffler ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf); 3784c42a7b7eSSam Leffler if (sc->sc_rxdma.dd_desc_len != 0) 3785c42a7b7eSSam Leffler ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf); 37865591b213SSam Leffler } 37875591b213SSam Leffler 37885591b213SSam Leffler static struct ieee80211_node * 378938c208f8SSam Leffler ath_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) 37905591b213SSam Leffler { 379138c208f8SSam Leffler struct ieee80211com *ic = vap->iv_ic; 3792c42a7b7eSSam Leffler struct ath_softc *sc = ic->ic_ifp->if_softc; 3793c42a7b7eSSam Leffler const size_t space = sizeof(struct ath_node) + sc->sc_rc->arc_space; 3794c42a7b7eSSam Leffler struct ath_node *an; 3795c42a7b7eSSam Leffler 3796c42a7b7eSSam Leffler an = malloc(space, M_80211_NODE, M_NOWAIT|M_ZERO); 3797c42a7b7eSSam Leffler if (an == NULL) { 3798c42a7b7eSSam Leffler /* XXX stat+msg */ 3799de5af704SSam Leffler return NULL; 38005591b213SSam Leffler } 3801c42a7b7eSSam Leffler ath_rate_node_init(sc, an); 38025591b213SSam Leffler 3803c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_NODE, "%s: an %p\n", __func__, an); 3804c42a7b7eSSam Leffler return &an->an_node; 3805c42a7b7eSSam Leffler } 3806c42a7b7eSSam Leffler 38075591b213SSam Leffler static void 3808c42a7b7eSSam Leffler ath_node_free(struct ieee80211_node *ni) 38095591b213SSam Leffler { 3810c42a7b7eSSam Leffler struct ieee80211com *ic = ni->ni_ic; 3811c42a7b7eSSam Leffler struct ath_softc *sc = ic->ic_ifp->if_softc; 38121e774079SSam Leffler 3813c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_NODE, "%s: ni %p\n", __func__, ni); 3814c42a7b7eSSam Leffler 3815c42a7b7eSSam Leffler ath_rate_node_cleanup(sc, ATH_NODE(ni)); 3816c42a7b7eSSam Leffler sc->sc_node_free(ni); 38175591b213SSam Leffler } 38185591b213SSam Leffler 381968e8e04eSSam Leffler static void 382068e8e04eSSam Leffler ath_node_getsignal(const struct ieee80211_node *ni, int8_t *rssi, int8_t *noise) 382168e8e04eSSam Leffler { 382268e8e04eSSam Leffler struct ieee80211com *ic = ni->ni_ic; 382368e8e04eSSam Leffler struct ath_softc *sc = ic->ic_ifp->if_softc; 382468e8e04eSSam Leffler struct ath_hal *ah = sc->sc_ah; 382568e8e04eSSam Leffler 3826b032f27cSSam Leffler *rssi = ic->ic_node_getrssi(ni); 382759efa8b5SSam Leffler if (ni->ni_chan != IEEE80211_CHAN_ANYC) 382859efa8b5SSam Leffler *noise = ath_hal_getchannoise(ah, ni->ni_chan); 382959efa8b5SSam Leffler else 383068e8e04eSSam Leffler *noise = -95; /* nominally correct */ 383168e8e04eSSam Leffler } 383268e8e04eSSam Leffler 38335591b213SSam Leffler static int 38345591b213SSam Leffler ath_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf) 38355591b213SSam Leffler { 38365591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 38375591b213SSam Leffler int error; 38385591b213SSam Leffler struct mbuf *m; 38395591b213SSam Leffler struct ath_desc *ds; 38405591b213SSam Leffler 38415591b213SSam Leffler m = bf->bf_m; 38425591b213SSam Leffler if (m == NULL) { 38435591b213SSam Leffler /* 38445591b213SSam Leffler * NB: by assigning a page to the rx dma buffer we 38455591b213SSam Leffler * implicitly satisfy the Atheros requirement that 38465591b213SSam Leffler * this buffer be cache-line-aligned and sized to be 38475591b213SSam Leffler * multiple of the cache line size. Not doing this 38485591b213SSam Leffler * causes weird stuff to happen (for the 5210 at least). 38495591b213SSam Leffler */ 38505591b213SSam Leffler m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); 38515591b213SSam Leffler if (m == NULL) { 3852c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, 3853c42a7b7eSSam Leffler "%s: no mbuf/cluster\n", __func__); 38545591b213SSam Leffler sc->sc_stats.ast_rx_nombuf++; 38555591b213SSam Leffler return ENOMEM; 38565591b213SSam Leffler } 38575591b213SSam Leffler m->m_pkthdr.len = m->m_len = m->m_ext.ext_size; 38585591b213SSam Leffler 3859f9e6219bSSam Leffler error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, 3860c42a7b7eSSam Leffler bf->bf_dmamap, m, 3861f9e6219bSSam Leffler bf->bf_segs, &bf->bf_nseg, 38625591b213SSam Leffler BUS_DMA_NOWAIT); 38635591b213SSam Leffler if (error != 0) { 3864c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, 3865f9e6219bSSam Leffler "%s: bus_dmamap_load_mbuf_sg failed; error %d\n", 3866c42a7b7eSSam Leffler __func__, error); 38675591b213SSam Leffler sc->sc_stats.ast_rx_busdma++; 3868b2792ff6SSam Leffler m_freem(m); 38695591b213SSam Leffler return error; 38705591b213SSam Leffler } 3871d77367bfSSam Leffler KASSERT(bf->bf_nseg == 1, 3872d77367bfSSam Leffler ("multi-segment packet; nseg %u", bf->bf_nseg)); 3873b2792ff6SSam Leffler bf->bf_m = m; 38745591b213SSam Leffler } 38755591b213SSam Leffler bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREREAD); 38765591b213SSam Leffler 387704e22a02SSam Leffler /* 387804e22a02SSam Leffler * Setup descriptors. For receive we always terminate 387904e22a02SSam Leffler * the descriptor list with a self-linked entry so we'll 388004e22a02SSam Leffler * not get overrun under high load (as can happen with a 3881c42a7b7eSSam Leffler * 5212 when ANI processing enables PHY error frames). 388204e22a02SSam Leffler * 388304e22a02SSam Leffler * To insure the last descriptor is self-linked we create 388404e22a02SSam Leffler * each descriptor as self-linked and add it to the end. As 388504e22a02SSam Leffler * each additional descriptor is added the previous self-linked 388604e22a02SSam Leffler * entry is ``fixed'' naturally. This should be safe even 388704e22a02SSam Leffler * if DMA is happening. When processing RX interrupts we 388804e22a02SSam Leffler * never remove/process the last, self-linked, entry on the 388904e22a02SSam Leffler * descriptor list. This insures the hardware always has 389004e22a02SSam Leffler * someplace to write a new frame. 389104e22a02SSam Leffler */ 38925591b213SSam Leffler ds = bf->bf_desc; 389304e22a02SSam Leffler ds->ds_link = bf->bf_daddr; /* link to self */ 38945591b213SSam Leffler ds->ds_data = bf->bf_segs[0].ds_addr; 38955591b213SSam Leffler ath_hal_setuprxdesc(ah, ds 38965591b213SSam Leffler , m->m_len /* buffer size */ 38975591b213SSam Leffler , 0 38985591b213SSam Leffler ); 38995591b213SSam Leffler 39005591b213SSam Leffler if (sc->sc_rxlink != NULL) 39015591b213SSam Leffler *sc->sc_rxlink = bf->bf_daddr; 39025591b213SSam Leffler sc->sc_rxlink = &ds->ds_link; 39035591b213SSam Leffler return 0; 39045591b213SSam Leffler } 39055591b213SSam Leffler 3906c42a7b7eSSam Leffler /* 390703ed599aSSam Leffler * Extend 15-bit time stamp from rx descriptor to 39087b0c77ecSSam Leffler * a full 64-bit TSF using the specified TSF. 390903ed599aSSam Leffler */ 391003ed599aSSam Leffler static __inline u_int64_t 39117b0c77ecSSam Leffler ath_extend_tsf(u_int32_t rstamp, u_int64_t tsf) 391203ed599aSSam Leffler { 391303ed599aSSam Leffler if ((tsf & 0x7fff) < rstamp) 391403ed599aSSam Leffler tsf -= 0x8000; 391503ed599aSSam Leffler return ((tsf &~ 0x7fff) | rstamp); 391603ed599aSSam Leffler } 391703ed599aSSam Leffler 391803ed599aSSam Leffler /* 3919c42a7b7eSSam Leffler * Intercept management frames to collect beacon rssi data 3920c42a7b7eSSam Leffler * and to do ibss merges. 3921c42a7b7eSSam Leffler */ 3922c42a7b7eSSam Leffler static void 3923b032f27cSSam Leffler ath_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, 392468e8e04eSSam Leffler int subtype, int rssi, int noise, u_int32_t rstamp) 3925c42a7b7eSSam Leffler { 3926b032f27cSSam Leffler struct ieee80211vap *vap = ni->ni_vap; 3927b032f27cSSam Leffler struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; 3928c42a7b7eSSam Leffler 3929c42a7b7eSSam Leffler /* 3930c42a7b7eSSam Leffler * Call up first so subsequent work can use information 3931c42a7b7eSSam Leffler * potentially stored in the node (e.g. for ibss merge). 3932c42a7b7eSSam Leffler */ 3933b032f27cSSam Leffler ATH_VAP(vap)->av_recv_mgmt(ni, m, subtype, rssi, noise, rstamp); 3934c42a7b7eSSam Leffler switch (subtype) { 3935c42a7b7eSSam Leffler case IEEE80211_FC0_SUBTYPE_BEACON: 3936c42a7b7eSSam Leffler /* update rssi statistics for use by the hal */ 3937ffa2cab6SSam Leffler ATH_RSSI_LPF(sc->sc_halstats.ns_avgbrssi, rssi); 393880d939bfSSam Leffler if (sc->sc_syncbeacon && 3939b032f27cSSam Leffler ni == vap->iv_bss && vap->iv_state == IEEE80211_S_RUN) { 394080d939bfSSam Leffler /* 394180d939bfSSam Leffler * Resync beacon timers using the tsf of the beacon 394280d939bfSSam Leffler * frame we just received. 394380d939bfSSam Leffler */ 3944b032f27cSSam Leffler ath_beacon_config(sc, vap); 394580d939bfSSam Leffler } 3946c42a7b7eSSam Leffler /* fall thru... */ 3947c42a7b7eSSam Leffler case IEEE80211_FC0_SUBTYPE_PROBE_RESP: 3948b032f27cSSam Leffler if (vap->iv_opmode == IEEE80211_M_IBSS && 3949b032f27cSSam Leffler vap->iv_state == IEEE80211_S_RUN) { 39507b0c77ecSSam Leffler u_int64_t tsf = ath_extend_tsf(rstamp, 39517b0c77ecSSam Leffler ath_hal_gettsf64(sc->sc_ah)); 3952c42a7b7eSSam Leffler /* 3953c42a7b7eSSam Leffler * Handle ibss merge as needed; check the tsf on the 3954c42a7b7eSSam Leffler * frame before attempting the merge. The 802.11 spec 3955c42a7b7eSSam Leffler * says the station should change it's bssid to match 3956c42a7b7eSSam Leffler * the oldest station with the same ssid, where oldest 3957f818612bSSam Leffler * is determined by the tsf. Note that hardware 3958f818612bSSam Leffler * reconfiguration happens through callback to 395903ed599aSSam Leffler * ath_newstate as the state machine will go from 396003ed599aSSam Leffler * RUN -> RUN when this happens. 3961c42a7b7eSSam Leffler */ 396203ed599aSSam Leffler if (le64toh(ni->ni_tstamp.tsf) >= tsf) { 396303ed599aSSam Leffler DPRINTF(sc, ATH_DEBUG_STATE, 396433d7d80cSTai-hwa Liang "ibss merge, rstamp %u tsf %ju " 396533d7d80cSTai-hwa Liang "tstamp %ju\n", rstamp, (uintmax_t)tsf, 396633d7d80cSTai-hwa Liang (uintmax_t)ni->ni_tstamp.tsf); 3967641b4d0bSSam Leffler (void) ieee80211_ibss_merge(ni); 3968c42a7b7eSSam Leffler } 396903ed599aSSam Leffler } 3970c42a7b7eSSam Leffler break; 3971c42a7b7eSSam Leffler } 3972c42a7b7eSSam Leffler } 3973c42a7b7eSSam Leffler 3974c42a7b7eSSam Leffler /* 3975c42a7b7eSSam Leffler * Set the default antenna. 3976c42a7b7eSSam Leffler */ 3977c42a7b7eSSam Leffler static void 3978c42a7b7eSSam Leffler ath_setdefantenna(struct ath_softc *sc, u_int antenna) 3979c42a7b7eSSam Leffler { 3980c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 3981c42a7b7eSSam Leffler 3982c42a7b7eSSam Leffler /* XXX block beacon interrupts */ 3983c42a7b7eSSam Leffler ath_hal_setdefantenna(ah, antenna); 3984c42a7b7eSSam Leffler if (sc->sc_defant != antenna) 3985c42a7b7eSSam Leffler sc->sc_stats.ast_ant_defswitch++; 3986c42a7b7eSSam Leffler sc->sc_defant = antenna; 3987c42a7b7eSSam Leffler sc->sc_rxotherant = 0; 3988c42a7b7eSSam Leffler } 3989c42a7b7eSSam Leffler 39907b0c77ecSSam Leffler static int 3991b032f27cSSam Leffler ath_rx_tap(struct ifnet *ifp, struct mbuf *m, 399265f9edeeSSam Leffler const struct ath_rx_status *rs, u_int64_t tsf, int16_t nf) 39937b0c77ecSSam Leffler { 3994e387d629SSam Leffler #define CHAN_HT20 htole32(IEEE80211_CHAN_HT20) 3995e387d629SSam Leffler #define CHAN_HT40U htole32(IEEE80211_CHAN_HT40U) 3996e387d629SSam Leffler #define CHAN_HT40D htole32(IEEE80211_CHAN_HT40D) 399746d4d74cSSam Leffler #define CHAN_HT (CHAN_HT20|CHAN_HT40U|CHAN_HT40D) 3998b032f27cSSam Leffler struct ath_softc *sc = ifp->if_softc; 399946d4d74cSSam Leffler const HAL_RATE_TABLE *rt; 400046d4d74cSSam Leffler uint8_t rix; 40017b0c77ecSSam Leffler 40027b0c77ecSSam Leffler /* 40037b0c77ecSSam Leffler * Discard anything shorter than an ack or cts. 40047b0c77ecSSam Leffler */ 40057b0c77ecSSam Leffler if (m->m_pkthdr.len < IEEE80211_ACK_LEN) { 40067b0c77ecSSam Leffler DPRINTF(sc, ATH_DEBUG_RECV, "%s: runt packet %d\n", 40077b0c77ecSSam Leffler __func__, m->m_pkthdr.len); 40087b0c77ecSSam Leffler sc->sc_stats.ast_rx_tooshort++; 40097b0c77ecSSam Leffler return 0; 40107b0c77ecSSam Leffler } 401146d4d74cSSam Leffler rt = sc->sc_currates; 401246d4d74cSSam Leffler KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode)); 401346d4d74cSSam Leffler rix = rt->rateCodeToIndex[rs->rs_rate]; 401468e8e04eSSam Leffler sc->sc_rx_th.wr_rate = sc->sc_hwmap[rix].ieeerate; 40157b0c77ecSSam Leffler sc->sc_rx_th.wr_flags = sc->sc_hwmap[rix].rxflags; 401646d4d74cSSam Leffler #ifdef AH_SUPPORT_AR5416 4017e387d629SSam Leffler sc->sc_rx_th.wr_chan_flags &= ~CHAN_HT; 401846d4d74cSSam Leffler if (sc->sc_rx_th.wr_rate & IEEE80211_RATE_MCS) { /* HT rate */ 401959efa8b5SSam Leffler struct ieee80211com *ic = ifp->if_l2com; 402059efa8b5SSam Leffler 4021e387d629SSam Leffler if ((rs->rs_flags & HAL_RX_2040) == 0) 4022e387d629SSam Leffler sc->sc_rx_th.wr_chan_flags |= CHAN_HT20; 402359efa8b5SSam Leffler else if (IEEE80211_IS_CHAN_HT40U(ic->ic_curchan)) 4024e387d629SSam Leffler sc->sc_rx_th.wr_chan_flags |= CHAN_HT40U; 4025e387d629SSam Leffler else 4026e387d629SSam Leffler sc->sc_rx_th.wr_chan_flags |= CHAN_HT40D; 402768e8e04eSSam Leffler if ((rs->rs_flags & HAL_RX_GI) == 0) 4028e387d629SSam Leffler sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_SHORTGI; 402968e8e04eSSam Leffler } 403068e8e04eSSam Leffler #endif 403168e8e04eSSam Leffler sc->sc_rx_th.wr_tsf = htole64(ath_extend_tsf(rs->rs_tstamp, tsf)); 403265f9edeeSSam Leffler if (rs->rs_status & HAL_RXERR_CRC) 40337b0c77ecSSam Leffler sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_BADFCS; 40347b0c77ecSSam Leffler /* XXX propagate other error flags from descriptor */ 403565f9edeeSSam Leffler sc->sc_rx_th.wr_antsignal = rs->rs_rssi + nf; 40367b0c77ecSSam Leffler sc->sc_rx_th.wr_antnoise = nf; 403765f9edeeSSam Leffler sc->sc_rx_th.wr_antenna = rs->rs_antenna; 40387b0c77ecSSam Leffler 4039b032f27cSSam Leffler bpf_mtap2(ifp->if_bpf, &sc->sc_rx_th, sc->sc_rx_th_len, m); 40407b0c77ecSSam Leffler 40417b0c77ecSSam Leffler return 1; 404246d4d74cSSam Leffler #undef CHAN_HT 4043e387d629SSam Leffler #undef CHAN_HT20 4044e387d629SSam Leffler #undef CHAN_HT40U 4045e387d629SSam Leffler #undef CHAN_HT40D 40467b0c77ecSSam Leffler } 40477b0c77ecSSam Leffler 40485591b213SSam Leffler static void 4049b032f27cSSam Leffler ath_handle_micerror(struct ieee80211com *ic, 4050b032f27cSSam Leffler struct ieee80211_frame *wh, int keyix) 4051b032f27cSSam Leffler { 4052b032f27cSSam Leffler struct ieee80211_node *ni; 4053b032f27cSSam Leffler 4054b032f27cSSam Leffler /* XXX recheck MIC to deal w/ chips that lie */ 4055b032f27cSSam Leffler /* XXX discard MIC errors on !data frames */ 4056b032f27cSSam Leffler ni = ieee80211_find_rxnode(ic, (const struct ieee80211_frame_min *) wh); 4057b032f27cSSam Leffler if (ni != NULL) { 4058b032f27cSSam Leffler ieee80211_notify_michael_failure(ni->ni_vap, wh, keyix); 4059b032f27cSSam Leffler ieee80211_free_node(ni); 4060b032f27cSSam Leffler } 4061b032f27cSSam Leffler } 4062b032f27cSSam Leffler 4063b032f27cSSam Leffler static void 40645591b213SSam Leffler ath_rx_proc(void *arg, int npending) 40655591b213SSam Leffler { 40668cec0ab9SSam Leffler #define PA2DESC(_sc, _pa) \ 4067c42a7b7eSSam Leffler ((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \ 4068c42a7b7eSSam Leffler ((_pa) - (_sc)->sc_rxdma.dd_desc_paddr))) 40695591b213SSam Leffler struct ath_softc *sc = arg; 40705591b213SSam Leffler struct ath_buf *bf; 4071fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 4072b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 40735591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 40745591b213SSam Leffler struct ath_desc *ds; 407565f9edeeSSam Leffler struct ath_rx_status *rs; 40765591b213SSam Leffler struct mbuf *m; 40770a915fadSSam Leffler struct ieee80211_node *ni; 4078d7736e13SSam Leffler int len, type, ngood; 40795591b213SSam Leffler u_int phyerr; 40805591b213SSam Leffler HAL_STATUS status; 40817b0c77ecSSam Leffler int16_t nf; 40827b0c77ecSSam Leffler u_int64_t tsf; 40835591b213SSam Leffler 4084c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s: pending %u\n", __func__, npending); 4085d7736e13SSam Leffler ngood = 0; 408659efa8b5SSam Leffler nf = ath_hal_getchannoise(ah, sc->sc_curchan); 408784784be1SSam Leffler sc->sc_stats.ast_rx_noise = nf; 40887b0c77ecSSam Leffler tsf = ath_hal_gettsf64(ah); 40895591b213SSam Leffler do { 4090c42a7b7eSSam Leffler bf = STAILQ_FIRST(&sc->sc_rxbuf); 40915591b213SSam Leffler if (bf == NULL) { /* NB: shouldn't happen */ 4092c42a7b7eSSam Leffler if_printf(ifp, "%s: no buffer!\n", __func__); 40935591b213SSam Leffler break; 40945591b213SSam Leffler } 4095b2792ff6SSam Leffler m = bf->bf_m; 4096b2792ff6SSam Leffler if (m == NULL) { /* NB: shouldn't happen */ 4097b2792ff6SSam Leffler /* 4098b2792ff6SSam Leffler * If mbuf allocation failed previously there 4099b2792ff6SSam Leffler * will be no mbuf; try again to re-populate it. 4100b2792ff6SSam Leffler */ 4101b2792ff6SSam Leffler /* XXX make debug msg */ 4102b2792ff6SSam Leffler if_printf(ifp, "%s: no mbuf!\n", __func__); 4103b2792ff6SSam Leffler STAILQ_REMOVE_HEAD(&sc->sc_rxbuf, bf_list); 4104b2792ff6SSam Leffler goto rx_next; 4105b2792ff6SSam Leffler } 410604e22a02SSam Leffler ds = bf->bf_desc; 410704e22a02SSam Leffler if (ds->ds_link == bf->bf_daddr) { 410804e22a02SSam Leffler /* NB: never process the self-linked entry at the end */ 410904e22a02SSam Leffler break; 411004e22a02SSam Leffler } 41118cec0ab9SSam Leffler /* XXX sync descriptor memory */ 41128cec0ab9SSam Leffler /* 41138cec0ab9SSam Leffler * Must provide the virtual address of the current 41148cec0ab9SSam Leffler * descriptor, the physical address, and the virtual 41158cec0ab9SSam Leffler * address of the next descriptor in the h/w chain. 41168cec0ab9SSam Leffler * This allows the HAL to look ahead to see if the 41178cec0ab9SSam Leffler * hardware is done with a descriptor by checking the 41188cec0ab9SSam Leffler * done bit in the following descriptor and the address 41198cec0ab9SSam Leffler * of the current descriptor the DMA engine is working 41208cec0ab9SSam Leffler * on. All this is necessary because of our use of 41218cec0ab9SSam Leffler * a self-linked list to avoid rx overruns. 41228cec0ab9SSam Leffler */ 412365f9edeeSSam Leffler rs = &bf->bf_status.ds_rxstat; 41248cec0ab9SSam Leffler status = ath_hal_rxprocdesc(ah, ds, 412565f9edeeSSam Leffler bf->bf_daddr, PA2DESC(sc, ds->ds_link), rs); 4126a585a9a1SSam Leffler #ifdef ATH_DEBUG 4127c42a7b7eSSam Leffler if (sc->sc_debug & ATH_DEBUG_RECV_DESC) 41286902009eSSam Leffler ath_printrxbuf(sc, bf, 0, status == HAL_OK); 41295591b213SSam Leffler #endif 41305591b213SSam Leffler if (status == HAL_EINPROGRESS) 41315591b213SSam Leffler break; 4132c42a7b7eSSam Leffler STAILQ_REMOVE_HEAD(&sc->sc_rxbuf, bf_list); 413368e8e04eSSam Leffler if (rs->rs_status != 0) { 413465f9edeeSSam Leffler if (rs->rs_status & HAL_RXERR_CRC) 41355591b213SSam Leffler sc->sc_stats.ast_rx_crcerr++; 413665f9edeeSSam Leffler if (rs->rs_status & HAL_RXERR_FIFO) 41375591b213SSam Leffler sc->sc_stats.ast_rx_fifoerr++; 413865f9edeeSSam Leffler if (rs->rs_status & HAL_RXERR_PHY) { 41395591b213SSam Leffler sc->sc_stats.ast_rx_phyerr++; 414065f9edeeSSam Leffler phyerr = rs->rs_phyerr & 0x1f; 41415591b213SSam Leffler sc->sc_stats.ast_rx_phy[phyerr]++; 414268e8e04eSSam Leffler goto rx_error; /* NB: don't count in ierrors */ 4143c42a7b7eSSam Leffler } 414465f9edeeSSam Leffler if (rs->rs_status & HAL_RXERR_DECRYPT) { 414585643802SSam Leffler /* 4146c42a7b7eSSam Leffler * Decrypt error. If the error occurred 4147c42a7b7eSSam Leffler * because there was no hardware key, then 4148c42a7b7eSSam Leffler * let the frame through so the upper layers 4149c42a7b7eSSam Leffler * can process it. This is necessary for 5210 4150c42a7b7eSSam Leffler * parts which have no way to setup a ``clear'' 4151c42a7b7eSSam Leffler * key cache entry. 4152c42a7b7eSSam Leffler * 4153c42a7b7eSSam Leffler * XXX do key cache faulting 415485643802SSam Leffler */ 415565f9edeeSSam Leffler if (rs->rs_keyix == HAL_RXKEYIX_INVALID) 4156c42a7b7eSSam Leffler goto rx_accept; 4157c42a7b7eSSam Leffler sc->sc_stats.ast_rx_badcrypt++; 41585591b213SSam Leffler } 415965f9edeeSSam Leffler if (rs->rs_status & HAL_RXERR_MIC) { 4160c42a7b7eSSam Leffler sc->sc_stats.ast_rx_badmic++; 4161c42a7b7eSSam Leffler /* 4162c42a7b7eSSam Leffler * Do minimal work required to hand off 4163c42a7b7eSSam Leffler * the 802.11 header for notifcation. 4164c42a7b7eSSam Leffler */ 4165c42a7b7eSSam Leffler /* XXX frag's and qos frames */ 416665f9edeeSSam Leffler len = rs->rs_datalen; 4167c42a7b7eSSam Leffler if (len >= sizeof (struct ieee80211_frame)) { 4168c42a7b7eSSam Leffler bus_dmamap_sync(sc->sc_dmat, 4169c42a7b7eSSam Leffler bf->bf_dmamap, 4170c42a7b7eSSam Leffler BUS_DMASYNC_POSTREAD); 4171b032f27cSSam Leffler ath_handle_micerror(ic, 4172c42a7b7eSSam Leffler mtod(m, struct ieee80211_frame *), 41730ab4040aSSam Leffler sc->sc_splitmic ? 4174b032f27cSSam Leffler rs->rs_keyix-32 : rs->rs_keyix); 4175c42a7b7eSSam Leffler } 4176c42a7b7eSSam Leffler } 4177c42a7b7eSSam Leffler ifp->if_ierrors++; 417868e8e04eSSam Leffler rx_error: 417968e8e04eSSam Leffler /* 418068e8e04eSSam Leffler * Cleanup any pending partial frame. 418168e8e04eSSam Leffler */ 418268e8e04eSSam Leffler if (sc->sc_rxpending != NULL) { 418368e8e04eSSam Leffler m_freem(sc->sc_rxpending); 418468e8e04eSSam Leffler sc->sc_rxpending = NULL; 418568e8e04eSSam Leffler } 4186c42a7b7eSSam Leffler /* 41877b0c77ecSSam Leffler * When a tap is present pass error frames 41887b0c77ecSSam Leffler * that have been requested. By default we 41897b0c77ecSSam Leffler * pass decrypt+mic errors but others may be 41907b0c77ecSSam Leffler * interesting (e.g. crc). 4191c42a7b7eSSam Leffler */ 4192b032f27cSSam Leffler if (bpf_peers_present(ifp->if_bpf) && 419365f9edeeSSam Leffler (rs->rs_status & sc->sc_monpass)) { 41947b0c77ecSSam Leffler bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, 41957b0c77ecSSam Leffler BUS_DMASYNC_POSTREAD); 41967b0c77ecSSam Leffler /* NB: bpf needs the mbuf length setup */ 419765f9edeeSSam Leffler len = rs->rs_datalen; 41987b0c77ecSSam Leffler m->m_pkthdr.len = m->m_len = len; 4199b032f27cSSam Leffler (void) ath_rx_tap(ifp, m, rs, tsf, nf); 42007b0c77ecSSam Leffler } 42017b0c77ecSSam Leffler /* XXX pass MIC errors up for s/w reclaculation */ 42025591b213SSam Leffler goto rx_next; 42035591b213SSam Leffler } 4204c42a7b7eSSam Leffler rx_accept: 4205c42a7b7eSSam Leffler /* 4206c42a7b7eSSam Leffler * Sync and unmap the frame. At this point we're 4207c42a7b7eSSam Leffler * committed to passing the mbuf somewhere so clear 4208c66c48cbSSam Leffler * bf_m; this means a new mbuf must be allocated 4209c42a7b7eSSam Leffler * when the rx descriptor is setup again to receive 4210c42a7b7eSSam Leffler * another frame. 4211c42a7b7eSSam Leffler */ 42125591b213SSam Leffler bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, 42135591b213SSam Leffler BUS_DMASYNC_POSTREAD); 42145591b213SSam Leffler bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 42155591b213SSam Leffler bf->bf_m = NULL; 4216c42a7b7eSSam Leffler 421765f9edeeSSam Leffler len = rs->rs_datalen; 421868e8e04eSSam Leffler m->m_len = len; 421968e8e04eSSam Leffler 422068e8e04eSSam Leffler if (rs->rs_more) { 422168e8e04eSSam Leffler /* 422268e8e04eSSam Leffler * Frame spans multiple descriptors; save 422368e8e04eSSam Leffler * it for the next completed descriptor, it 422468e8e04eSSam Leffler * will be used to construct a jumbogram. 422568e8e04eSSam Leffler */ 422668e8e04eSSam Leffler if (sc->sc_rxpending != NULL) { 422768e8e04eSSam Leffler /* NB: max frame size is currently 2 clusters */ 422868e8e04eSSam Leffler sc->sc_stats.ast_rx_toobig++; 422968e8e04eSSam Leffler m_freem(sc->sc_rxpending); 423068e8e04eSSam Leffler } 423168e8e04eSSam Leffler m->m_pkthdr.rcvif = ifp; 423268e8e04eSSam Leffler m->m_pkthdr.len = len; 423368e8e04eSSam Leffler sc->sc_rxpending = m; 423468e8e04eSSam Leffler goto rx_next; 423568e8e04eSSam Leffler } else if (sc->sc_rxpending != NULL) { 423668e8e04eSSam Leffler /* 423768e8e04eSSam Leffler * This is the second part of a jumbogram, 423868e8e04eSSam Leffler * chain it to the first mbuf, adjust the 423968e8e04eSSam Leffler * frame length, and clear the rxpending state. 424068e8e04eSSam Leffler */ 424168e8e04eSSam Leffler sc->sc_rxpending->m_next = m; 424268e8e04eSSam Leffler sc->sc_rxpending->m_pkthdr.len += len; 424368e8e04eSSam Leffler m = sc->sc_rxpending; 424468e8e04eSSam Leffler sc->sc_rxpending = NULL; 424568e8e04eSSam Leffler } else { 424668e8e04eSSam Leffler /* 424768e8e04eSSam Leffler * Normal single-descriptor receive; setup 424868e8e04eSSam Leffler * the rcvif and packet length. 424968e8e04eSSam Leffler */ 425068e8e04eSSam Leffler m->m_pkthdr.rcvif = ifp; 425168e8e04eSSam Leffler m->m_pkthdr.len = len; 425268e8e04eSSam Leffler } 425373454c73SSam Leffler 4254b032f27cSSam Leffler ifp->if_ipackets++; 425565f9edeeSSam Leffler sc->sc_stats.ast_ant_rx[rs->rs_antenna]++; 4256c42a7b7eSSam Leffler 4257b032f27cSSam Leffler if (bpf_peers_present(ifp->if_bpf) && 4258b032f27cSSam Leffler !ath_rx_tap(ifp, m, rs, tsf, nf)) { 42597b0c77ecSSam Leffler m_freem(m); /* XXX reclaim */ 4260c42a7b7eSSam Leffler goto rx_next; 4261c42a7b7eSSam Leffler } 42620a915fadSSam Leffler 42635591b213SSam Leffler /* 4264c42a7b7eSSam Leffler * From this point on we assume the frame is at least 4265c42a7b7eSSam Leffler * as large as ieee80211_frame_min; verify that. 42665591b213SSam Leffler */ 4267c42a7b7eSSam Leffler if (len < IEEE80211_MIN_LEN) { 4268c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_RECV, "%s: short packet %d\n", 4269c42a7b7eSSam Leffler __func__, len); 4270c42a7b7eSSam Leffler sc->sc_stats.ast_rx_tooshort++; 4271c42a7b7eSSam Leffler m_freem(m); 4272c42a7b7eSSam Leffler goto rx_next; 42735591b213SSam Leffler } 42740a915fadSSam Leffler 4275c42a7b7eSSam Leffler if (IFF_DUMPPKTS(sc, ATH_DEBUG_RECV)) { 427646d4d74cSSam Leffler const HAL_RATE_TABLE *rt = sc->sc_currates; 427746d4d74cSSam Leffler uint8_t rix = rt->rateCodeToIndex[rs->rs_rate]; 427846d4d74cSSam Leffler 427968e8e04eSSam Leffler ieee80211_dump_pkt(ic, mtod(m, caddr_t), len, 428046d4d74cSSam Leffler sc->sc_hwmap[rix].ieeerate, rs->rs_rssi); 4281c42a7b7eSSam Leffler } 4282c42a7b7eSSam Leffler 4283c42a7b7eSSam Leffler m_adj(m, -IEEE80211_CRC_LEN); 4284de5af704SSam Leffler 4285de5af704SSam Leffler /* 4286c42a7b7eSSam Leffler * Locate the node for sender, track state, and then 4287c42a7b7eSSam Leffler * pass the (referenced) node up to the 802.11 layer 4288c42a7b7eSSam Leffler * for its use. 4289c42a7b7eSSam Leffler */ 4290c1225b52SSam Leffler ni = ieee80211_find_rxnode_withkey(ic, 4291c1225b52SSam Leffler mtod(m, const struct ieee80211_frame_min *), 429265f9edeeSSam Leffler rs->rs_keyix == HAL_RXKEYIX_INVALID ? 429365f9edeeSSam Leffler IEEE80211_KEYIX_NONE : rs->rs_keyix); 4294b032f27cSSam Leffler if (ni != NULL) { 4295b032f27cSSam Leffler /* 4296b032f27cSSam Leffler * Sending station is known, dispatch directly. 4297b032f27cSSam Leffler */ 429810ad9a77SSam Leffler #ifdef ATH_SUPPORT_TDMA 429910ad9a77SSam Leffler sc->sc_tdmars = rs; 430010ad9a77SSam Leffler #endif 4301b032f27cSSam Leffler type = ieee80211_input(ni, m, 4302b032f27cSSam Leffler rs->rs_rssi, nf, rs->rs_tstamp); 4303b032f27cSSam Leffler ieee80211_free_node(ni); 4304b032f27cSSam Leffler /* 4305b032f27cSSam Leffler * Arrange to update the last rx timestamp only for 4306b032f27cSSam Leffler * frames from our ap when operating in station mode. 4307b032f27cSSam Leffler * This assumes the rx key is always setup when 4308b032f27cSSam Leffler * associated. 4309b032f27cSSam Leffler */ 4310b032f27cSSam Leffler if (ic->ic_opmode == IEEE80211_M_STA && 4311b032f27cSSam Leffler rs->rs_keyix != HAL_RXKEYIX_INVALID) 4312b032f27cSSam Leffler ngood++; 4313b032f27cSSam Leffler } else { 4314b032f27cSSam Leffler type = ieee80211_input_all(ic, m, 4315b032f27cSSam Leffler rs->rs_rssi, nf, rs->rs_tstamp); 4316b032f27cSSam Leffler } 4317c42a7b7eSSam Leffler /* 4318c42a7b7eSSam Leffler * Track rx rssi and do any rx antenna management. 4319de5af704SSam Leffler */ 432065f9edeeSSam Leffler ATH_RSSI_LPF(sc->sc_halstats.ns_avgrssi, rs->rs_rssi); 4321c42a7b7eSSam Leffler if (sc->sc_diversity) { 4322c42a7b7eSSam Leffler /* 4323c42a7b7eSSam Leffler * When using fast diversity, change the default rx 4324c42a7b7eSSam Leffler * antenna if diversity chooses the other antenna 3 4325c42a7b7eSSam Leffler * times in a row. 4326c42a7b7eSSam Leffler */ 432765f9edeeSSam Leffler if (sc->sc_defant != rs->rs_antenna) { 4328c42a7b7eSSam Leffler if (++sc->sc_rxotherant >= 3) 432965f9edeeSSam Leffler ath_setdefantenna(sc, rs->rs_antenna); 4330c42a7b7eSSam Leffler } else 4331c42a7b7eSSam Leffler sc->sc_rxotherant = 0; 4332c42a7b7eSSam Leffler } 43333e50ec2cSSam Leffler if (sc->sc_softled) { 43343e50ec2cSSam Leffler /* 43353e50ec2cSSam Leffler * Blink for any data frame. Otherwise do a 43363e50ec2cSSam Leffler * heartbeat-style blink when idle. The latter 43373e50ec2cSSam Leffler * is mainly for station mode where we depend on 43383e50ec2cSSam Leffler * periodic beacon frames to trigger the poll event. 43393e50ec2cSSam Leffler */ 434031640eb7SSam Leffler if (type == IEEE80211_FC0_TYPE_DATA) { 434146d4d74cSSam Leffler const HAL_RATE_TABLE *rt = sc->sc_currates; 434246d4d74cSSam Leffler ath_led_event(sc, 434346d4d74cSSam Leffler rt->rateCodeToIndex[rs->rs_rate]); 43443e50ec2cSSam Leffler } else if (ticks - sc->sc_ledevent >= sc->sc_ledidle) 434546d4d74cSSam Leffler ath_led_event(sc, 0); 43463e50ec2cSSam Leffler } 43475591b213SSam Leffler rx_next: 4348c42a7b7eSSam Leffler STAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list); 43495591b213SSam Leffler } while (ath_rxbuf_init(sc, bf) == 0); 43505591b213SSam Leffler 4351c42a7b7eSSam Leffler /* rx signal state monitoring */ 435259efa8b5SSam Leffler ath_hal_rxmonitor(ah, &sc->sc_halstats, sc->sc_curchan); 4353d7736e13SSam Leffler if (ngood) 4354d7736e13SSam Leffler sc->sc_lastrx = tsf; 4355b5f4adb3SSam Leffler 4356cd196bb2SSam Leffler if ((ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0 && 4357cd196bb2SSam Leffler !IFQ_IS_EMPTY(&ifp->if_snd)) 4358cd196bb2SSam Leffler ath_start(ifp); 4359cd196bb2SSam Leffler 43608cec0ab9SSam Leffler #undef PA2DESC 43615591b213SSam Leffler } 43625591b213SSam Leffler 4363622b3fd2SSam Leffler static void 4364622b3fd2SSam Leffler ath_txq_init(struct ath_softc *sc, struct ath_txq *txq, int qnum) 4365622b3fd2SSam Leffler { 4366622b3fd2SSam Leffler txq->axq_qnum = qnum; 4367622b3fd2SSam Leffler txq->axq_depth = 0; 4368622b3fd2SSam Leffler txq->axq_intrcnt = 0; 4369622b3fd2SSam Leffler txq->axq_link = NULL; 4370622b3fd2SSam Leffler STAILQ_INIT(&txq->axq_q); 4371622b3fd2SSam Leffler ATH_TXQ_LOCK_INIT(sc, txq); 437268e8e04eSSam Leffler TAILQ_INIT(&txq->axq_stageq); 437368e8e04eSSam Leffler txq->axq_curage = 0; 4374622b3fd2SSam Leffler } 4375622b3fd2SSam Leffler 43765591b213SSam Leffler /* 4377c42a7b7eSSam Leffler * Setup a h/w transmit queue. 43785591b213SSam Leffler */ 4379c42a7b7eSSam Leffler static struct ath_txq * 4380c42a7b7eSSam Leffler ath_txq_setup(struct ath_softc *sc, int qtype, int subtype) 4381c42a7b7eSSam Leffler { 4382c42a7b7eSSam Leffler #define N(a) (sizeof(a)/sizeof(a[0])) 4383c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 4384c42a7b7eSSam Leffler HAL_TXQ_INFO qi; 4385c42a7b7eSSam Leffler int qnum; 4386c42a7b7eSSam Leffler 4387c42a7b7eSSam Leffler memset(&qi, 0, sizeof(qi)); 4388c42a7b7eSSam Leffler qi.tqi_subtype = subtype; 4389c42a7b7eSSam Leffler qi.tqi_aifs = HAL_TXQ_USEDEFAULT; 4390c42a7b7eSSam Leffler qi.tqi_cwmin = HAL_TXQ_USEDEFAULT; 4391c42a7b7eSSam Leffler qi.tqi_cwmax = HAL_TXQ_USEDEFAULT; 4392c42a7b7eSSam Leffler /* 4393c42a7b7eSSam Leffler * Enable interrupts only for EOL and DESC conditions. 4394c42a7b7eSSam Leffler * We mark tx descriptors to receive a DESC interrupt 4395c42a7b7eSSam Leffler * when a tx queue gets deep; otherwise waiting for the 4396c42a7b7eSSam Leffler * EOL to reap descriptors. Note that this is done to 4397c42a7b7eSSam Leffler * reduce interrupt load and this only defers reaping 4398c42a7b7eSSam Leffler * descriptors, never transmitting frames. Aside from 4399c42a7b7eSSam Leffler * reducing interrupts this also permits more concurrency. 4400c42a7b7eSSam Leffler * The only potential downside is if the tx queue backs 4401c42a7b7eSSam Leffler * up in which case the top half of the kernel may backup 4402c42a7b7eSSam Leffler * due to a lack of tx descriptors. 4403c42a7b7eSSam Leffler */ 4404bd5a9920SSam Leffler qi.tqi_qflags = HAL_TXQ_TXEOLINT_ENABLE | HAL_TXQ_TXDESCINT_ENABLE; 4405c42a7b7eSSam Leffler qnum = ath_hal_setuptxqueue(ah, qtype, &qi); 4406c42a7b7eSSam Leffler if (qnum == -1) { 4407c42a7b7eSSam Leffler /* 4408c42a7b7eSSam Leffler * NB: don't print a message, this happens 4409a614e076SSam Leffler * normally on parts with too few tx queues 4410c42a7b7eSSam Leffler */ 4411c42a7b7eSSam Leffler return NULL; 4412c42a7b7eSSam Leffler } 4413c42a7b7eSSam Leffler if (qnum >= N(sc->sc_txq)) { 44146891c875SPeter Wemm device_printf(sc->sc_dev, 44156891c875SPeter Wemm "hal qnum %u out of range, max %zu!\n", 4416c42a7b7eSSam Leffler qnum, N(sc->sc_txq)); 4417c42a7b7eSSam Leffler ath_hal_releasetxqueue(ah, qnum); 4418c42a7b7eSSam Leffler return NULL; 4419c42a7b7eSSam Leffler } 4420c42a7b7eSSam Leffler if (!ATH_TXQ_SETUP(sc, qnum)) { 4421622b3fd2SSam Leffler ath_txq_init(sc, &sc->sc_txq[qnum], qnum); 4422c42a7b7eSSam Leffler sc->sc_txqsetup |= 1<<qnum; 4423c42a7b7eSSam Leffler } 4424c42a7b7eSSam Leffler return &sc->sc_txq[qnum]; 4425c42a7b7eSSam Leffler #undef N 4426c42a7b7eSSam Leffler } 4427c42a7b7eSSam Leffler 4428c42a7b7eSSam Leffler /* 4429c42a7b7eSSam Leffler * Setup a hardware data transmit queue for the specified 4430c42a7b7eSSam Leffler * access control. The hal may not support all requested 4431c42a7b7eSSam Leffler * queues in which case it will return a reference to a 4432c42a7b7eSSam Leffler * previously setup queue. We record the mapping from ac's 4433c42a7b7eSSam Leffler * to h/w queues for use by ath_tx_start and also track 4434c42a7b7eSSam Leffler * the set of h/w queues being used to optimize work in the 4435c42a7b7eSSam Leffler * transmit interrupt handler and related routines. 4436c42a7b7eSSam Leffler */ 4437c42a7b7eSSam Leffler static int 4438c42a7b7eSSam Leffler ath_tx_setup(struct ath_softc *sc, int ac, int haltype) 4439c42a7b7eSSam Leffler { 4440c42a7b7eSSam Leffler #define N(a) (sizeof(a)/sizeof(a[0])) 4441c42a7b7eSSam Leffler struct ath_txq *txq; 4442c42a7b7eSSam Leffler 4443c42a7b7eSSam Leffler if (ac >= N(sc->sc_ac2q)) { 44446891c875SPeter Wemm device_printf(sc->sc_dev, "AC %u out of range, max %zu!\n", 4445c42a7b7eSSam Leffler ac, N(sc->sc_ac2q)); 4446c42a7b7eSSam Leffler return 0; 4447c42a7b7eSSam Leffler } 4448c42a7b7eSSam Leffler txq = ath_txq_setup(sc, HAL_TX_QUEUE_DATA, haltype); 4449c42a7b7eSSam Leffler if (txq != NULL) { 4450c42a7b7eSSam Leffler sc->sc_ac2q[ac] = txq; 4451c42a7b7eSSam Leffler return 1; 4452c42a7b7eSSam Leffler } else 4453c42a7b7eSSam Leffler return 0; 4454c42a7b7eSSam Leffler #undef N 4455c42a7b7eSSam Leffler } 4456c42a7b7eSSam Leffler 4457c42a7b7eSSam Leffler /* 4458c42a7b7eSSam Leffler * Update WME parameters for a transmit queue. 4459c42a7b7eSSam Leffler */ 4460c42a7b7eSSam Leffler static int 4461c42a7b7eSSam Leffler ath_txq_update(struct ath_softc *sc, int ac) 4462c42a7b7eSSam Leffler { 4463c42a7b7eSSam Leffler #define ATH_EXPONENT_TO_VALUE(v) ((1<<v)-1) 4464c42a7b7eSSam Leffler #define ATH_TXOP_TO_US(v) (v<<5) 4465b032f27cSSam Leffler struct ifnet *ifp = sc->sc_ifp; 4466b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 4467c42a7b7eSSam Leffler struct ath_txq *txq = sc->sc_ac2q[ac]; 4468c42a7b7eSSam Leffler struct wmeParams *wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[ac]; 4469c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 4470c42a7b7eSSam Leffler HAL_TXQ_INFO qi; 4471c42a7b7eSSam Leffler 4472c42a7b7eSSam Leffler ath_hal_gettxqueueprops(ah, txq->axq_qnum, &qi); 447310ad9a77SSam Leffler #ifdef ATH_SUPPORT_TDMA 447410ad9a77SSam Leffler if (sc->sc_tdma) { 447510ad9a77SSam Leffler /* 447610ad9a77SSam Leffler * AIFS is zero so there's no pre-transmit wait. The 447710ad9a77SSam Leffler * burst time defines the slot duration and is configured 447810ad9a77SSam Leffler * via sysctl. The QCU is setup to not do post-xmit 447910ad9a77SSam Leffler * back off, lockout all lower-priority QCU's, and fire 448010ad9a77SSam Leffler * off the DMA beacon alert timer which is setup based 448110ad9a77SSam Leffler * on the slot configuration. 448210ad9a77SSam Leffler */ 448310ad9a77SSam Leffler qi.tqi_qflags = HAL_TXQ_TXOKINT_ENABLE 448410ad9a77SSam Leffler | HAL_TXQ_TXERRINT_ENABLE 448510ad9a77SSam Leffler | HAL_TXQ_TXURNINT_ENABLE 448610ad9a77SSam Leffler | HAL_TXQ_TXEOLINT_ENABLE 448710ad9a77SSam Leffler | HAL_TXQ_DBA_GATED 448810ad9a77SSam Leffler | HAL_TXQ_BACKOFF_DISABLE 448910ad9a77SSam Leffler | HAL_TXQ_ARB_LOCKOUT_GLOBAL 449010ad9a77SSam Leffler ; 449110ad9a77SSam Leffler qi.tqi_aifs = 0; 449210ad9a77SSam Leffler /* XXX +dbaprep? */ 449310ad9a77SSam Leffler qi.tqi_readyTime = sc->sc_tdmaslotlen; 449410ad9a77SSam Leffler qi.tqi_burstTime = qi.tqi_readyTime; 449510ad9a77SSam Leffler } else { 449610ad9a77SSam Leffler #endif 449710ad9a77SSam Leffler qi.tqi_qflags = HAL_TXQ_TXOKINT_ENABLE 449810ad9a77SSam Leffler | HAL_TXQ_TXERRINT_ENABLE 449910ad9a77SSam Leffler | HAL_TXQ_TXDESCINT_ENABLE 450010ad9a77SSam Leffler | HAL_TXQ_TXURNINT_ENABLE 450110ad9a77SSam Leffler ; 4502c42a7b7eSSam Leffler qi.tqi_aifs = wmep->wmep_aifsn; 4503c42a7b7eSSam Leffler qi.tqi_cwmin = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmin); 4504c42a7b7eSSam Leffler qi.tqi_cwmax = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmax); 450510ad9a77SSam Leffler qi.tqi_readyTime = 0; 4506c42a7b7eSSam Leffler qi.tqi_burstTime = ATH_TXOP_TO_US(wmep->wmep_txopLimit); 450710ad9a77SSam Leffler #ifdef ATH_SUPPORT_TDMA 450810ad9a77SSam Leffler } 450910ad9a77SSam Leffler #endif 451010ad9a77SSam Leffler 451110ad9a77SSam Leffler DPRINTF(sc, ATH_DEBUG_RESET, 451210ad9a77SSam Leffler "%s: Q%u qflags 0x%x aifs %u cwmin %u cwmax %u burstTime %u\n", 451310ad9a77SSam Leffler __func__, txq->axq_qnum, qi.tqi_qflags, 451410ad9a77SSam Leffler qi.tqi_aifs, qi.tqi_cwmin, qi.tqi_cwmax, qi.tqi_burstTime); 4515c42a7b7eSSam Leffler 4516c42a7b7eSSam Leffler if (!ath_hal_settxqueueprops(ah, txq->axq_qnum, &qi)) { 4517b032f27cSSam Leffler if_printf(ifp, "unable to update hardware queue " 4518c42a7b7eSSam Leffler "parameters for %s traffic!\n", 4519c42a7b7eSSam Leffler ieee80211_wme_acnames[ac]); 4520c42a7b7eSSam Leffler return 0; 4521c42a7b7eSSam Leffler } else { 4522c42a7b7eSSam Leffler ath_hal_resettxqueue(ah, txq->axq_qnum); /* push to h/w */ 4523c42a7b7eSSam Leffler return 1; 4524c42a7b7eSSam Leffler } 4525c42a7b7eSSam Leffler #undef ATH_TXOP_TO_US 4526c42a7b7eSSam Leffler #undef ATH_EXPONENT_TO_VALUE 4527c42a7b7eSSam Leffler } 4528c42a7b7eSSam Leffler 4529c42a7b7eSSam Leffler /* 4530c42a7b7eSSam Leffler * Callback from the 802.11 layer to update WME parameters. 4531c42a7b7eSSam Leffler */ 4532c42a7b7eSSam Leffler static int 4533c42a7b7eSSam Leffler ath_wme_update(struct ieee80211com *ic) 4534c42a7b7eSSam Leffler { 4535c42a7b7eSSam Leffler struct ath_softc *sc = ic->ic_ifp->if_softc; 4536c42a7b7eSSam Leffler 4537c42a7b7eSSam Leffler return !ath_txq_update(sc, WME_AC_BE) || 4538c42a7b7eSSam Leffler !ath_txq_update(sc, WME_AC_BK) || 4539c42a7b7eSSam Leffler !ath_txq_update(sc, WME_AC_VI) || 4540c42a7b7eSSam Leffler !ath_txq_update(sc, WME_AC_VO) ? EIO : 0; 4541c42a7b7eSSam Leffler } 4542c42a7b7eSSam Leffler 4543c42a7b7eSSam Leffler /* 4544c42a7b7eSSam Leffler * Reclaim resources for a setup queue. 4545c42a7b7eSSam Leffler */ 4546c42a7b7eSSam Leffler static void 4547c42a7b7eSSam Leffler ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq) 4548c42a7b7eSSam Leffler { 4549c42a7b7eSSam Leffler 4550c42a7b7eSSam Leffler ath_hal_releasetxqueue(sc->sc_ah, txq->axq_qnum); 4551c42a7b7eSSam Leffler ATH_TXQ_LOCK_DESTROY(txq); 4552c42a7b7eSSam Leffler sc->sc_txqsetup &= ~(1<<txq->axq_qnum); 4553c42a7b7eSSam Leffler } 4554c42a7b7eSSam Leffler 4555c42a7b7eSSam Leffler /* 4556c42a7b7eSSam Leffler * Reclaim all tx queue resources. 4557c42a7b7eSSam Leffler */ 4558c42a7b7eSSam Leffler static void 4559c42a7b7eSSam Leffler ath_tx_cleanup(struct ath_softc *sc) 4560c42a7b7eSSam Leffler { 4561c42a7b7eSSam Leffler int i; 4562c42a7b7eSSam Leffler 4563c42a7b7eSSam Leffler ATH_TXBUF_LOCK_DESTROY(sc); 4564c42a7b7eSSam Leffler for (i = 0; i < HAL_NUM_TX_QUEUES; i++) 4565c42a7b7eSSam Leffler if (ATH_TXQ_SETUP(sc, i)) 4566c42a7b7eSSam Leffler ath_tx_cleanupq(sc, &sc->sc_txq[i]); 4567c42a7b7eSSam Leffler } 45685591b213SSam Leffler 456999d258fdSSam Leffler /* 45708b5341deSSam Leffler * Return h/w rate index for an IEEE rate (w/o basic rate bit). 45718b5341deSSam Leffler */ 45728b5341deSSam Leffler static int 45738b5341deSSam Leffler ath_tx_findrix(const HAL_RATE_TABLE *rt, int rate) 45748b5341deSSam Leffler { 45758b5341deSSam Leffler int i; 45768b5341deSSam Leffler 45778b5341deSSam Leffler for (i = 0; i < rt->rateCount; i++) 45788b5341deSSam Leffler if ((rt->info[i].dot11Rate & IEEE80211_RATE_VAL) == rate) 45798b5341deSSam Leffler return i; 45808b5341deSSam Leffler return 0; /* NB: lowest rate */ 45818b5341deSSam Leffler } 45828b5341deSSam Leffler 458368e8e04eSSam Leffler /* 458468e8e04eSSam Leffler * Reclaim mbuf resources. For fragmented frames we 458568e8e04eSSam Leffler * need to claim each frag chained with m_nextpkt. 458668e8e04eSSam Leffler */ 458768e8e04eSSam Leffler static void 458868e8e04eSSam Leffler ath_freetx(struct mbuf *m) 458968e8e04eSSam Leffler { 459068e8e04eSSam Leffler struct mbuf *next; 459168e8e04eSSam Leffler 459268e8e04eSSam Leffler do { 459368e8e04eSSam Leffler next = m->m_nextpkt; 459468e8e04eSSam Leffler m->m_nextpkt = NULL; 459568e8e04eSSam Leffler m_freem(m); 459668e8e04eSSam Leffler } while ((m = next) != NULL); 459768e8e04eSSam Leffler } 459868e8e04eSSam Leffler 45995591b213SSam Leffler static int 4600664443d0SSam Leffler ath_tx_dmasetup(struct ath_softc *sc, struct ath_buf *bf, struct mbuf *m0) 4601664443d0SSam Leffler { 4602664443d0SSam Leffler struct mbuf *m; 4603664443d0SSam Leffler int error; 4604664443d0SSam Leffler 4605664443d0SSam Leffler /* 4606664443d0SSam Leffler * Load the DMA map so any coalescing is done. This 4607664443d0SSam Leffler * also calculates the number of descriptors we need. 4608664443d0SSam Leffler */ 4609664443d0SSam Leffler error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m0, 4610664443d0SSam Leffler bf->bf_segs, &bf->bf_nseg, 4611664443d0SSam Leffler BUS_DMA_NOWAIT); 4612664443d0SSam Leffler if (error == EFBIG) { 4613664443d0SSam Leffler /* XXX packet requires too many descriptors */ 4614664443d0SSam Leffler bf->bf_nseg = ATH_TXDESC+1; 4615664443d0SSam Leffler } else if (error != 0) { 4616664443d0SSam Leffler sc->sc_stats.ast_tx_busdma++; 461768e8e04eSSam Leffler ath_freetx(m0); 4618664443d0SSam Leffler return error; 4619664443d0SSam Leffler } 4620664443d0SSam Leffler /* 4621664443d0SSam Leffler * Discard null packets and check for packets that 4622664443d0SSam Leffler * require too many TX descriptors. We try to convert 4623664443d0SSam Leffler * the latter to a cluster. 4624664443d0SSam Leffler */ 4625664443d0SSam Leffler if (bf->bf_nseg > ATH_TXDESC) { /* too many desc's, linearize */ 4626664443d0SSam Leffler sc->sc_stats.ast_tx_linear++; 4627eeb76a18SSam Leffler m = m_collapse(m0, M_DONTWAIT, ATH_TXDESC); 4628664443d0SSam Leffler if (m == NULL) { 462968e8e04eSSam Leffler ath_freetx(m0); 4630664443d0SSam Leffler sc->sc_stats.ast_tx_nombuf++; 4631664443d0SSam Leffler return ENOMEM; 4632664443d0SSam Leffler } 4633664443d0SSam Leffler m0 = m; 4634664443d0SSam Leffler error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m0, 4635664443d0SSam Leffler bf->bf_segs, &bf->bf_nseg, 4636664443d0SSam Leffler BUS_DMA_NOWAIT); 4637664443d0SSam Leffler if (error != 0) { 4638664443d0SSam Leffler sc->sc_stats.ast_tx_busdma++; 463968e8e04eSSam Leffler ath_freetx(m0); 4640664443d0SSam Leffler return error; 4641664443d0SSam Leffler } 4642664443d0SSam Leffler KASSERT(bf->bf_nseg <= ATH_TXDESC, 4643664443d0SSam Leffler ("too many segments after defrag; nseg %u", bf->bf_nseg)); 4644664443d0SSam Leffler } else if (bf->bf_nseg == 0) { /* null packet, discard */ 4645664443d0SSam Leffler sc->sc_stats.ast_tx_nodata++; 464668e8e04eSSam Leffler ath_freetx(m0); 4647664443d0SSam Leffler return EIO; 4648664443d0SSam Leffler } 4649664443d0SSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT, "%s: m %p len %u\n", 4650664443d0SSam Leffler __func__, m0, m0->m_pkthdr.len); 4651664443d0SSam Leffler bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE); 4652664443d0SSam Leffler bf->bf_m = m0; 4653664443d0SSam Leffler 4654664443d0SSam Leffler return 0; 4655664443d0SSam Leffler } 4656664443d0SSam Leffler 4657664443d0SSam Leffler static void 4658664443d0SSam Leffler ath_tx_handoff(struct ath_softc *sc, struct ath_txq *txq, struct ath_buf *bf) 4659664443d0SSam Leffler { 4660664443d0SSam Leffler struct ath_hal *ah = sc->sc_ah; 4661664443d0SSam Leffler struct ath_desc *ds, *ds0; 4662664443d0SSam Leffler int i; 4663664443d0SSam Leffler 4664664443d0SSam Leffler /* 4665664443d0SSam Leffler * Fillin the remainder of the descriptor info. 4666664443d0SSam Leffler */ 4667664443d0SSam Leffler ds0 = ds = bf->bf_desc; 4668664443d0SSam Leffler for (i = 0; i < bf->bf_nseg; i++, ds++) { 4669664443d0SSam Leffler ds->ds_data = bf->bf_segs[i].ds_addr; 4670664443d0SSam Leffler if (i == bf->bf_nseg - 1) 4671664443d0SSam Leffler ds->ds_link = 0; 4672664443d0SSam Leffler else 4673664443d0SSam Leffler ds->ds_link = bf->bf_daddr + sizeof(*ds) * (i + 1); 4674664443d0SSam Leffler ath_hal_filltxdesc(ah, ds 4675664443d0SSam Leffler , bf->bf_segs[i].ds_len /* segment length */ 4676664443d0SSam Leffler , i == 0 /* first segment */ 4677664443d0SSam Leffler , i == bf->bf_nseg - 1 /* last segment */ 4678664443d0SSam Leffler , ds0 /* first descriptor */ 4679664443d0SSam Leffler ); 4680664443d0SSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT, 4681664443d0SSam Leffler "%s: %d: %08x %08x %08x %08x %08x %08x\n", 4682664443d0SSam Leffler __func__, i, ds->ds_link, ds->ds_data, 4683664443d0SSam Leffler ds->ds_ctl0, ds->ds_ctl1, ds->ds_hw[0], ds->ds_hw[1]); 4684664443d0SSam Leffler } 4685664443d0SSam Leffler /* 4686664443d0SSam Leffler * Insert the frame on the outbound list and pass it on 4687664443d0SSam Leffler * to the hardware. Multicast frames buffered for power 4688664443d0SSam Leffler * save stations and transmit from the CAB queue are stored 4689664443d0SSam Leffler * on a s/w only queue and loaded on to the CAB queue in 4690664443d0SSam Leffler * the SWBA handler since frames only go out on DTIM and 4691664443d0SSam Leffler * to avoid possible races. 4692664443d0SSam Leffler */ 4693664443d0SSam Leffler ATH_TXQ_LOCK(txq); 469410ad9a77SSam Leffler KASSERT((bf->bf_flags & ATH_BUF_BUSY) == 0, 469510ad9a77SSam Leffler ("busy status 0x%x", bf->bf_flags)); 4696b032f27cSSam Leffler if (txq->axq_qnum != ATH_TXQ_SWQ) { 469710ad9a77SSam Leffler #ifdef ATH_SUPPORT_TDMA 469810ad9a77SSam Leffler int qbusy; 469910ad9a77SSam Leffler 470010ad9a77SSam Leffler ATH_TXQ_INSERT_TAIL(txq, bf, bf_list); 470110ad9a77SSam Leffler qbusy = ath_hal_txqenabled(ah, txq->axq_qnum); 470210ad9a77SSam Leffler if (txq->axq_link == NULL) { 470310ad9a77SSam Leffler /* 470410ad9a77SSam Leffler * Be careful writing the address to TXDP. If 470510ad9a77SSam Leffler * the tx q is enabled then this write will be 470610ad9a77SSam Leffler * ignored. Normally this is not an issue but 470710ad9a77SSam Leffler * when tdma is in use and the q is beacon gated 470810ad9a77SSam Leffler * this race can occur. If the q is busy then 470910ad9a77SSam Leffler * defer the work to later--either when another 471010ad9a77SSam Leffler * packet comes along or when we prepare a beacon 471110ad9a77SSam Leffler * frame at SWBA. 471210ad9a77SSam Leffler */ 471310ad9a77SSam Leffler if (!qbusy) { 471410ad9a77SSam Leffler ath_hal_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr); 471510ad9a77SSam Leffler txq->axq_flags &= ~ATH_TXQ_PUTPENDING; 471610ad9a77SSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT, 471710ad9a77SSam Leffler "%s: TXDP[%u] = %p (%p) depth %d\n", 471810ad9a77SSam Leffler __func__, txq->axq_qnum, 471910ad9a77SSam Leffler (caddr_t)bf->bf_daddr, bf->bf_desc, 472010ad9a77SSam Leffler txq->axq_depth); 472110ad9a77SSam Leffler } else { 472210ad9a77SSam Leffler txq->axq_flags |= ATH_TXQ_PUTPENDING; 472310ad9a77SSam Leffler DPRINTF(sc, ATH_DEBUG_TDMA | ATH_DEBUG_XMIT, 472410ad9a77SSam Leffler "%s: Q%u busy, defer enable\n", __func__, 472510ad9a77SSam Leffler txq->axq_qnum); 472610ad9a77SSam Leffler } 472710ad9a77SSam Leffler } else { 472810ad9a77SSam Leffler *txq->axq_link = bf->bf_daddr; 472910ad9a77SSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT, 473010ad9a77SSam Leffler "%s: link[%u](%p)=%p (%p) depth %d\n", __func__, 473110ad9a77SSam Leffler txq->axq_qnum, txq->axq_link, 473210ad9a77SSam Leffler (caddr_t)bf->bf_daddr, bf->bf_desc, txq->axq_depth); 473310ad9a77SSam Leffler if ((txq->axq_flags & ATH_TXQ_PUTPENDING) && !qbusy) { 473410ad9a77SSam Leffler /* 473510ad9a77SSam Leffler * The q was busy when we previously tried 473610ad9a77SSam Leffler * to write the address of the first buffer 473710ad9a77SSam Leffler * in the chain. Since it's not busy now 473810ad9a77SSam Leffler * handle this chore. We are certain the 473910ad9a77SSam Leffler * buffer at the front is the right one since 474010ad9a77SSam Leffler * axq_link is NULL only when the buffer list 474110ad9a77SSam Leffler * is/was empty. 474210ad9a77SSam Leffler */ 474310ad9a77SSam Leffler ath_hal_puttxbuf(ah, txq->axq_qnum, 474410ad9a77SSam Leffler STAILQ_FIRST(&txq->axq_q)->bf_daddr); 474510ad9a77SSam Leffler txq->axq_flags &= ~ATH_TXQ_PUTPENDING; 474610ad9a77SSam Leffler DPRINTF(sc, ATH_DEBUG_TDMA | ATH_DEBUG_XMIT, 474710ad9a77SSam Leffler "%s: Q%u restarted\n", __func__, 474810ad9a77SSam Leffler txq->axq_qnum); 474910ad9a77SSam Leffler } 475010ad9a77SSam Leffler } 475110ad9a77SSam Leffler #else 4752664443d0SSam Leffler ATH_TXQ_INSERT_TAIL(txq, bf, bf_list); 4753664443d0SSam Leffler if (txq->axq_link == NULL) { 4754664443d0SSam Leffler ath_hal_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr); 4755664443d0SSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT, 475610ad9a77SSam Leffler "%s: TXDP[%u] = %p (%p) depth %d\n", 475710ad9a77SSam Leffler __func__, txq->axq_qnum, 475810ad9a77SSam Leffler (caddr_t)bf->bf_daddr, bf->bf_desc, 4759664443d0SSam Leffler txq->axq_depth); 4760664443d0SSam Leffler } else { 4761664443d0SSam Leffler *txq->axq_link = bf->bf_daddr; 4762664443d0SSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT, 4763664443d0SSam Leffler "%s: link[%u](%p)=%p (%p) depth %d\n", __func__, 4764664443d0SSam Leffler txq->axq_qnum, txq->axq_link, 4765664443d0SSam Leffler (caddr_t)bf->bf_daddr, bf->bf_desc, txq->axq_depth); 4766664443d0SSam Leffler } 476710ad9a77SSam Leffler #endif /* ATH_SUPPORT_TDMA */ 4768664443d0SSam Leffler txq->axq_link = &bf->bf_desc[bf->bf_nseg - 1].ds_link; 4769664443d0SSam Leffler ath_hal_txstart(ah, txq->axq_qnum); 4770664443d0SSam Leffler } else { 4771b032f27cSSam Leffler if (txq->axq_link != NULL) { 4772b032f27cSSam Leffler struct ath_buf *last = ATH_TXQ_LAST(txq); 4773b032f27cSSam Leffler struct ieee80211_frame *wh; 4774b032f27cSSam Leffler 4775b032f27cSSam Leffler /* mark previous frame */ 4776b032f27cSSam Leffler wh = mtod(last->bf_m, struct ieee80211_frame *); 4777b032f27cSSam Leffler wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA; 4778b032f27cSSam Leffler bus_dmamap_sync(sc->sc_dmat, last->bf_dmamap, 4779b032f27cSSam Leffler BUS_DMASYNC_PREWRITE); 4780b032f27cSSam Leffler 4781b032f27cSSam Leffler /* link descriptor */ 4782664443d0SSam Leffler *txq->axq_link = bf->bf_daddr; 4783b032f27cSSam Leffler } 4784b032f27cSSam Leffler ATH_TXQ_INSERT_TAIL(txq, bf, bf_list); 4785664443d0SSam Leffler txq->axq_link = &bf->bf_desc[bf->bf_nseg - 1].ds_link; 4786664443d0SSam Leffler } 4787664443d0SSam Leffler ATH_TXQ_UNLOCK(txq); 4788664443d0SSam Leffler } 4789664443d0SSam Leffler 4790664443d0SSam Leffler static int 47915591b213SSam Leffler ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf, 47925591b213SSam Leffler struct mbuf *m0) 47935591b213SSam Leffler { 4794b032f27cSSam Leffler struct ieee80211vap *vap = ni->ni_vap; 4795b032f27cSSam Leffler struct ath_vap *avp = ATH_VAP(vap); 47965591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 4797fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 4798b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 4799c4c3cb46SSam Leffler const struct chanAccParams *cap = &ic->ic_wme.wme_chanParams; 480068e8e04eSSam Leffler int error, iswep, ismcast, isfrag, ismrr; 4801be613480SSam Leffler int keyix, hdrlen, pktlen, try0; 4802c42a7b7eSSam Leffler u_int8_t rix, txrate, ctsrate; 4803c42a7b7eSSam Leffler u_int8_t cix = 0xff; /* NB: silence compiler */ 4804664443d0SSam Leffler struct ath_desc *ds; 4805c42a7b7eSSam Leffler struct ath_txq *txq; 48065591b213SSam Leffler struct ieee80211_frame *wh; 4807c42a7b7eSSam Leffler u_int subtype, flags, ctsduration; 48085591b213SSam Leffler HAL_PKT_TYPE atype; 48095591b213SSam Leffler const HAL_RATE_TABLE *rt; 48105591b213SSam Leffler HAL_BOOL shortPreamble; 48115591b213SSam Leffler struct ath_node *an; 4812c4c3cb46SSam Leffler u_int pri; 48135591b213SSam Leffler 48145591b213SSam Leffler wh = mtod(m0, struct ieee80211_frame *); 48155591b213SSam Leffler iswep = wh->i_fc[1] & IEEE80211_FC1_WEP; 4816c42a7b7eSSam Leffler ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); 481768e8e04eSSam Leffler isfrag = m0->m_flags & M_FRAG; 4818c42a7b7eSSam Leffler hdrlen = ieee80211_anyhdrsize(wh); 4819c42a7b7eSSam Leffler /* 4820a614e076SSam Leffler * Packet length must not include any 4821a614e076SSam Leffler * pad bytes; deduct them here. 4822c42a7b7eSSam Leffler */ 4823c42a7b7eSSam Leffler pktlen = m0->m_pkthdr.len - (hdrlen & 3); 48245591b213SSam Leffler 48255591b213SSam Leffler if (iswep) { 4826c42a7b7eSSam Leffler const struct ieee80211_cipher *cip; 4827c42a7b7eSSam Leffler struct ieee80211_key *k; 4828c42a7b7eSSam Leffler 4829c42a7b7eSSam Leffler /* 4830c42a7b7eSSam Leffler * Construct the 802.11 header+trailer for an encrypted 4831c42a7b7eSSam Leffler * frame. The only reason this can fail is because of an 4832c42a7b7eSSam Leffler * unknown or unsupported cipher/key type. 4833c42a7b7eSSam Leffler */ 4834b032f27cSSam Leffler k = ieee80211_crypto_encap(ni, m0); 4835c42a7b7eSSam Leffler if (k == NULL) { 4836c42a7b7eSSam Leffler /* 4837c42a7b7eSSam Leffler * This can happen when the key is yanked after the 4838c42a7b7eSSam Leffler * frame was queued. Just discard the frame; the 4839c42a7b7eSSam Leffler * 802.11 layer counts failures and provides 4840c42a7b7eSSam Leffler * debugging/diagnostics. 4841c42a7b7eSSam Leffler */ 484268e8e04eSSam Leffler ath_freetx(m0); 4843c42a7b7eSSam Leffler return EIO; 48445591b213SSam Leffler } 4845c42a7b7eSSam Leffler /* 4846c42a7b7eSSam Leffler * Adjust the packet + header lengths for the crypto 4847c42a7b7eSSam Leffler * additions and calculate the h/w key index. When 4848c42a7b7eSSam Leffler * a s/w mic is done the frame will have had any mic 484968e8e04eSSam Leffler * added to it prior to entry so m0->m_pkthdr.len will 4850c42a7b7eSSam Leffler * account for it. Otherwise we need to add it to the 4851c42a7b7eSSam Leffler * packet length. 4852c42a7b7eSSam Leffler */ 4853c42a7b7eSSam Leffler cip = k->wk_cipher; 4854c42a7b7eSSam Leffler hdrlen += cip->ic_header; 4855c42a7b7eSSam Leffler pktlen += cip->ic_header + cip->ic_trailer; 485668e8e04eSSam Leffler /* NB: frags always have any TKIP MIC done in s/w */ 485768e8e04eSSam Leffler if ((k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && !isfrag) 4858c42a7b7eSSam Leffler pktlen += cip->ic_miclen; 4859c42a7b7eSSam Leffler keyix = k->wk_keyix; 4860c42a7b7eSSam Leffler 4861c42a7b7eSSam Leffler /* packet header may have moved, reset our local pointer */ 4862167ecdcaSSam Leffler wh = mtod(m0, struct ieee80211_frame *); 4863e8fd88a3SSam Leffler } else if (ni->ni_ucastkey.wk_cipher == &ieee80211_cipher_none) { 4864e8fd88a3SSam Leffler /* 4865e8fd88a3SSam Leffler * Use station key cache slot, if assigned. 4866e8fd88a3SSam Leffler */ 4867e8fd88a3SSam Leffler keyix = ni->ni_ucastkey.wk_keyix; 4868e8fd88a3SSam Leffler if (keyix == IEEE80211_KEYIX_NONE) 4869e8fd88a3SSam Leffler keyix = HAL_TXKEYIX_INVALID; 4870c42a7b7eSSam Leffler } else 4871c42a7b7eSSam Leffler keyix = HAL_TXKEYIX_INVALID; 4872c42a7b7eSSam Leffler 48735591b213SSam Leffler pktlen += IEEE80211_CRC_LEN; 48745591b213SSam Leffler 48755591b213SSam Leffler /* 48765591b213SSam Leffler * Load the DMA map so any coalescing is done. This 48775591b213SSam Leffler * also calculates the number of descriptors we need. 48785591b213SSam Leffler */ 4879664443d0SSam Leffler error = ath_tx_dmasetup(sc, bf, m0); 488005680ab6SSam Leffler if (error != 0) 488105680ab6SSam Leffler return error; 48820a915fadSSam Leffler bf->bf_node = ni; /* NB: held reference */ 4883664443d0SSam Leffler m0 = bf->bf_m; /* NB: may have changed */ 4884664443d0SSam Leffler wh = mtod(m0, struct ieee80211_frame *); 48855591b213SSam Leffler 48865591b213SSam Leffler /* setup descriptors */ 48875591b213SSam Leffler ds = bf->bf_desc; 48885591b213SSam Leffler rt = sc->sc_currates; 48895591b213SSam Leffler KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode)); 48905591b213SSam Leffler 48915591b213SSam Leffler /* 4892c42a7b7eSSam Leffler * NB: the 802.11 layer marks whether or not we should 4893c42a7b7eSSam Leffler * use short preamble based on the current mode and 4894c42a7b7eSSam Leffler * negotiated parameters. 48955591b213SSam Leffler */ 4896c42a7b7eSSam Leffler if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && 4897c42a7b7eSSam Leffler (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) { 4898c42a7b7eSSam Leffler shortPreamble = AH_TRUE; 4899c42a7b7eSSam Leffler sc->sc_stats.ast_tx_shortpre++; 4900c42a7b7eSSam Leffler } else { 4901c42a7b7eSSam Leffler shortPreamble = AH_FALSE; 4902c42a7b7eSSam Leffler } 4903c42a7b7eSSam Leffler 4904c42a7b7eSSam Leffler an = ATH_NODE(ni); 4905c42a7b7eSSam Leffler flags = HAL_TXDESC_CLRDMASK; /* XXX needed for crypto errs */ 4906be613480SSam Leffler ismrr = 0; /* default no multi-rate retry*/ 4907b032f27cSSam Leffler pri = M_WME_GETAC(m0); /* honor classification */ 49084df62779SSam Leffler /* XXX use txparams instead of fixed values */ 4909c42a7b7eSSam Leffler /* 4910c42a7b7eSSam Leffler * Calculate Atheros packet type from IEEE80211 packet header, 4911c42a7b7eSSam Leffler * setup for rate calculations, and select h/w transmit queue. 4912c42a7b7eSSam Leffler */ 49135591b213SSam Leffler switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { 49145591b213SSam Leffler case IEEE80211_FC0_TYPE_MGT: 49155591b213SSam Leffler subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; 49165591b213SSam Leffler if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) 49175591b213SSam Leffler atype = HAL_PKT_TYPE_BEACON; 49185591b213SSam Leffler else if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) 49195591b213SSam Leffler atype = HAL_PKT_TYPE_PROBE_RESP; 49205591b213SSam Leffler else if (subtype == IEEE80211_FC0_SUBTYPE_ATIM) 49215591b213SSam Leffler atype = HAL_PKT_TYPE_ATIM; 4922c42a7b7eSSam Leffler else 4923c42a7b7eSSam Leffler atype = HAL_PKT_TYPE_NORMAL; /* XXX */ 4924b032f27cSSam Leffler rix = an->an_mgmtrix; 492555f63772SSam Leffler txrate = rt->info[rix].rateCode; 4926c42a7b7eSSam Leffler if (shortPreamble) 492755f63772SSam Leffler txrate |= rt->info[rix].shortPreamble; 4928be613480SSam Leffler try0 = ATH_TXMGTTRY; 4929c42a7b7eSSam Leffler flags |= HAL_TXDESC_INTREQ; /* force interrupt */ 49305591b213SSam Leffler break; 49315591b213SSam Leffler case IEEE80211_FC0_TYPE_CTL: 4932c42a7b7eSSam Leffler atype = HAL_PKT_TYPE_PSPOLL; /* stop setting of duration */ 4933b032f27cSSam Leffler rix = an->an_mgmtrix; 493455f63772SSam Leffler txrate = rt->info[rix].rateCode; 4935c42a7b7eSSam Leffler if (shortPreamble) 493655f63772SSam Leffler txrate |= rt->info[rix].shortPreamble; 4937be613480SSam Leffler try0 = ATH_TXMGTTRY; 4938c42a7b7eSSam Leffler flags |= HAL_TXDESC_INTREQ; /* force interrupt */ 4939c42a7b7eSSam Leffler break; 4940c42a7b7eSSam Leffler case IEEE80211_FC0_TYPE_DATA: 4941c42a7b7eSSam Leffler atype = HAL_PKT_TYPE_NORMAL; /* default */ 4942c42a7b7eSSam Leffler /* 49438b5341deSSam Leffler * Data frames: multicast frames go out at a fixed rate, 49444df62779SSam Leffler * EAPOL frames use the mgmt frame rate; otherwise consult 49454df62779SSam Leffler * the rate control module for the rate to use. 4946c42a7b7eSSam Leffler */ 49478b5341deSSam Leffler if (ismcast) { 4948b032f27cSSam Leffler rix = an->an_mcastrix; 49498b5341deSSam Leffler txrate = rt->info[rix].rateCode; 49508b5341deSSam Leffler if (shortPreamble) 49518b5341deSSam Leffler txrate |= rt->info[rix].shortPreamble; 49528b5341deSSam Leffler try0 = 1; 49534df62779SSam Leffler } else if (m0->m_flags & M_EAPOL) { 49544df62779SSam Leffler /* XXX? maybe always use long preamble? */ 49554df62779SSam Leffler rix = an->an_mgmtrix; 49564df62779SSam Leffler txrate = rt->info[rix].rateCode; 49574df62779SSam Leffler if (shortPreamble) 49584df62779SSam Leffler txrate |= rt->info[rix].shortPreamble; 49594df62779SSam Leffler try0 = ATH_TXMAXTRY; /* XXX?too many? */ 49608b5341deSSam Leffler } else { 4961c42a7b7eSSam Leffler ath_rate_findrate(sc, an, shortPreamble, pktlen, 4962c42a7b7eSSam Leffler &rix, &try0, &txrate); 496346d4d74cSSam Leffler sc->sc_txrix = rix; /* for LED blinking */ 496468e8e04eSSam Leffler sc->sc_lastdatarix = rix; /* for fast frames */ 4965be613480SSam Leffler if (try0 != ATH_TXMAXTRY) 4966be613480SSam Leffler ismrr = 1; 49678b5341deSSam Leffler } 4968f9748b9dSSam Leffler if (cap->cap_wmeParams[pri].wmep_noackPolicy) 4969c42a7b7eSSam Leffler flags |= HAL_TXDESC_NOACK; 49705591b213SSam Leffler break; 49715591b213SSam Leffler default: 4972c42a7b7eSSam Leffler if_printf(ifp, "bogus frame type 0x%x (%s)\n", 4973c42a7b7eSSam Leffler wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK, __func__); 4974c42a7b7eSSam Leffler /* XXX statistic */ 497568e8e04eSSam Leffler ath_freetx(m0); 49765591b213SSam Leffler return EIO; 49775591b213SSam Leffler } 4978c4c3cb46SSam Leffler txq = sc->sc_ac2q[pri]; 4979c42a7b7eSSam Leffler 49805591b213SSam Leffler /* 4981c42a7b7eSSam Leffler * When servicing one or more stations in power-save mode 4982622b3fd2SSam Leffler * (or) if there is some mcast data waiting on the mcast 4983622b3fd2SSam Leffler * queue (to prevent out of order delivery) multicast 4984622b3fd2SSam Leffler * frames must be buffered until after the beacon. 49855591b213SSam Leffler */ 4986b032f27cSSam Leffler if (ismcast && (vap->iv_ps_sta || avp->av_mcastq.axq_depth)) 4987b032f27cSSam Leffler txq = &avp->av_mcastq; 49885591b213SSam Leffler 49895591b213SSam Leffler /* 49905591b213SSam Leffler * Calculate miscellaneous flags. 49915591b213SSam Leffler */ 4992c42a7b7eSSam Leffler if (ismcast) { 49935591b213SSam Leffler flags |= HAL_TXDESC_NOACK; /* no ack on broad/multicast */ 4994b032f27cSSam Leffler } else if (pktlen > vap->iv_rtsthreshold && 499568e8e04eSSam Leffler (ni->ni_ath_flags & IEEE80211_NODE_FF) == 0) { 49965591b213SSam Leffler flags |= HAL_TXDESC_RTSENA; /* RTS based on frame length */ 4997c42a7b7eSSam Leffler cix = rt->info[rix].controlRate; 49985591b213SSam Leffler sc->sc_stats.ast_tx_rts++; 49995591b213SSam Leffler } 5000f9748b9dSSam Leffler if (flags & HAL_TXDESC_NOACK) /* NB: avoid double counting */ 5001f9748b9dSSam Leffler sc->sc_stats.ast_tx_noack++; 500210ad9a77SSam Leffler #ifdef ATH_SUPPORT_TDMA 500310ad9a77SSam Leffler if (sc->sc_tdma && (flags & HAL_TXDESC_NOACK) == 0) { 50043267a60cSSam Leffler DPRINTF(sc, ATH_DEBUG_TDMA, 50053267a60cSSam Leffler "%s: discard frame, ACK required w/ TDMA\n", __func__); 50063267a60cSSam Leffler sc->sc_stats.ast_tdma_ack++; 500710ad9a77SSam Leffler ath_freetx(m0); 500810ad9a77SSam Leffler return EIO; 500910ad9a77SSam Leffler } 501010ad9a77SSam Leffler #endif 50115591b213SSam Leffler 50125591b213SSam Leffler /* 5013c42a7b7eSSam Leffler * If 802.11g protection is enabled, determine whether 5014c42a7b7eSSam Leffler * to use RTS/CTS or just CTS. Note that this is only 5015c42a7b7eSSam Leffler * done for OFDM unicast frames. 5016c42a7b7eSSam Leffler */ 5017c42a7b7eSSam Leffler if ((ic->ic_flags & IEEE80211_F_USEPROT) && 5018c42a7b7eSSam Leffler rt->info[rix].phy == IEEE80211_T_OFDM && 5019c42a7b7eSSam Leffler (flags & HAL_TXDESC_NOACK) == 0) { 5020c42a7b7eSSam Leffler /* XXX fragments must use CCK rates w/ protection */ 5021c42a7b7eSSam Leffler if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) 5022c42a7b7eSSam Leffler flags |= HAL_TXDESC_RTSENA; 5023c42a7b7eSSam Leffler else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) 5024c42a7b7eSSam Leffler flags |= HAL_TXDESC_CTSENA; 502568e8e04eSSam Leffler if (isfrag) { 502668e8e04eSSam Leffler /* 502768e8e04eSSam Leffler * For frags it would be desirable to use the 502868e8e04eSSam Leffler * highest CCK rate for RTS/CTS. But stations 502968e8e04eSSam Leffler * farther away may detect it at a lower CCK rate 503068e8e04eSSam Leffler * so use the configured protection rate instead 503168e8e04eSSam Leffler * (for now). 503268e8e04eSSam Leffler */ 503368e8e04eSSam Leffler cix = rt->info[sc->sc_protrix].controlRate; 503468e8e04eSSam Leffler } else 5035c42a7b7eSSam Leffler cix = rt->info[sc->sc_protrix].controlRate; 5036c42a7b7eSSam Leffler sc->sc_stats.ast_tx_protect++; 5037c42a7b7eSSam Leffler } 5038c42a7b7eSSam Leffler 5039c42a7b7eSSam Leffler /* 5040f6aa038bSSam Leffler * Calculate duration. This logically belongs in the 802.11 5041f6aa038bSSam Leffler * layer but it lacks sufficient information to calculate it. 5042f6aa038bSSam Leffler */ 5043f6aa038bSSam Leffler if ((flags & HAL_TXDESC_NOACK) == 0 && 5044f6aa038bSSam Leffler (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_CTL) { 5045f6aa038bSSam Leffler u_int16_t dur; 5046c42a7b7eSSam Leffler if (shortPreamble) 5047c42a7b7eSSam Leffler dur = rt->info[rix].spAckDuration; 5048c42a7b7eSSam Leffler else 5049c42a7b7eSSam Leffler dur = rt->info[rix].lpAckDuration; 505068e8e04eSSam Leffler if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) { 505168e8e04eSSam Leffler dur += dur; /* additional SIFS+ACK */ 505268e8e04eSSam Leffler KASSERT(m0->m_nextpkt != NULL, ("no fragment")); 505368e8e04eSSam Leffler /* 505468e8e04eSSam Leffler * Include the size of next fragment so NAV is 505568e8e04eSSam Leffler * updated properly. The last fragment uses only 505668e8e04eSSam Leffler * the ACK duration 505768e8e04eSSam Leffler */ 505868e8e04eSSam Leffler dur += ath_hal_computetxtime(ah, rt, 505968e8e04eSSam Leffler m0->m_nextpkt->m_pkthdr.len, 506068e8e04eSSam Leffler rix, shortPreamble); 506168e8e04eSSam Leffler } 506268e8e04eSSam Leffler if (isfrag) { 506368e8e04eSSam Leffler /* 506468e8e04eSSam Leffler * Force hardware to use computed duration for next 506568e8e04eSSam Leffler * fragment by disabling multi-rate retry which updates 506668e8e04eSSam Leffler * duration based on the multi-rate duration table. 506768e8e04eSSam Leffler */ 506868e8e04eSSam Leffler ismrr = 0; 506968e8e04eSSam Leffler try0 = ATH_TXMGTTRY; /* XXX? */ 507068e8e04eSSam Leffler } 5071c42a7b7eSSam Leffler *(u_int16_t *)wh->i_dur = htole16(dur); 5072f6aa038bSSam Leffler } 5073f6aa038bSSam Leffler 5074f6aa038bSSam Leffler /* 50755591b213SSam Leffler * Calculate RTS/CTS rate and duration if needed. 50765591b213SSam Leffler */ 50775591b213SSam Leffler ctsduration = 0; 50785591b213SSam Leffler if (flags & (HAL_TXDESC_RTSENA|HAL_TXDESC_CTSENA)) { 50795591b213SSam Leffler /* 50805591b213SSam Leffler * CTS transmit rate is derived from the transmit rate 50815591b213SSam Leffler * by looking in the h/w rate table. We must also factor 50825591b213SSam Leffler * in whether or not a short preamble is to be used. 50835591b213SSam Leffler */ 5084c42a7b7eSSam Leffler /* NB: cix is set above where RTS/CTS is enabled */ 5085c42a7b7eSSam Leffler KASSERT(cix != 0xff, ("cix not setup")); 50865591b213SSam Leffler ctsrate = rt->info[cix].rateCode; 50875591b213SSam Leffler /* 5088c42a7b7eSSam Leffler * Compute the transmit duration based on the frame 5089c42a7b7eSSam Leffler * size and the size of an ACK frame. We call into the 5090c42a7b7eSSam Leffler * HAL to do the computation since it depends on the 5091c42a7b7eSSam Leffler * characteristics of the actual PHY being used. 5092c42a7b7eSSam Leffler * 5093c42a7b7eSSam Leffler * NB: CTS is assumed the same size as an ACK so we can 5094c42a7b7eSSam Leffler * use the precalculated ACK durations. 50955591b213SSam Leffler */ 5096c42a7b7eSSam Leffler if (shortPreamble) { 5097c42a7b7eSSam Leffler ctsrate |= rt->info[cix].shortPreamble; 5098c42a7b7eSSam Leffler if (flags & HAL_TXDESC_RTSENA) /* SIFS + CTS */ 5099c42a7b7eSSam Leffler ctsduration += rt->info[cix].spAckDuration; 51005591b213SSam Leffler ctsduration += ath_hal_computetxtime(ah, 5101c42a7b7eSSam Leffler rt, pktlen, rix, AH_TRUE); 5102c42a7b7eSSam Leffler if ((flags & HAL_TXDESC_NOACK) == 0) /* SIFS + ACK */ 51036ee571b2SSam Leffler ctsduration += rt->info[rix].spAckDuration; 5104c42a7b7eSSam Leffler } else { 5105c42a7b7eSSam Leffler if (flags & HAL_TXDESC_RTSENA) /* SIFS + CTS */ 5106c42a7b7eSSam Leffler ctsduration += rt->info[cix].lpAckDuration; 5107c42a7b7eSSam Leffler ctsduration += ath_hal_computetxtime(ah, 5108c42a7b7eSSam Leffler rt, pktlen, rix, AH_FALSE); 5109c42a7b7eSSam Leffler if ((flags & HAL_TXDESC_NOACK) == 0) /* SIFS + ACK */ 51106ee571b2SSam Leffler ctsduration += rt->info[rix].lpAckDuration; 51115591b213SSam Leffler } 5112c42a7b7eSSam Leffler /* 5113c42a7b7eSSam Leffler * Must disable multi-rate retry when using RTS/CTS. 5114c42a7b7eSSam Leffler */ 5115be613480SSam Leffler ismrr = 0; 5116be613480SSam Leffler try0 = ATH_TXMGTTRY; /* XXX */ 51175591b213SSam Leffler } else 51185591b213SSam Leffler ctsrate = 0; 51195591b213SSam Leffler 512068e8e04eSSam Leffler /* 512168e8e04eSSam Leffler * At this point we are committed to sending the frame 512268e8e04eSSam Leffler * and we don't need to look at m_nextpkt; clear it in 512368e8e04eSSam Leffler * case this frame is part of frag chain. 512468e8e04eSSam Leffler */ 512568e8e04eSSam Leffler m0->m_nextpkt = NULL; 512668e8e04eSSam Leffler 5127c42a7b7eSSam Leffler if (IFF_DUMPPKTS(sc, ATH_DEBUG_XMIT)) 512868e8e04eSSam Leffler ieee80211_dump_pkt(ic, mtod(m0, caddr_t), m0->m_len, 512946d4d74cSSam Leffler sc->sc_hwmap[rix].ieeerate, -1); 51305591b213SSam Leffler 5131b032f27cSSam Leffler if (bpf_peers_present(ifp->if_bpf)) { 51327b0c77ecSSam Leffler u_int64_t tsf = ath_hal_gettsf64(ah); 51337b0c77ecSSam Leffler 51347b0c77ecSSam Leffler sc->sc_tx_th.wt_tsf = htole64(tsf); 513546d4d74cSSam Leffler sc->sc_tx_th.wt_flags = sc->sc_hwmap[rix].txflags; 5136eb2cdcb1SSam Leffler if (iswep) 5137eb2cdcb1SSam Leffler sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_WEP; 513868e8e04eSSam Leffler if (isfrag) 513968e8e04eSSam Leffler sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_FRAG; 514046d4d74cSSam Leffler sc->sc_tx_th.wt_rate = sc->sc_hwmap[rix].ieeerate; 5141c42a7b7eSSam Leffler sc->sc_tx_th.wt_txpower = ni->ni_txpower; 5142c42a7b7eSSam Leffler sc->sc_tx_th.wt_antenna = sc->sc_txantenna; 5143eb2cdcb1SSam Leffler 5144b032f27cSSam Leffler bpf_mtap2(ifp->if_bpf, &sc->sc_tx_th, sc->sc_tx_th_len, m0); 5145eb2cdcb1SSam Leffler } 5146eb2cdcb1SSam Leffler 51475591b213SSam Leffler /* 5148c42a7b7eSSam Leffler * Determine if a tx interrupt should be generated for 5149c42a7b7eSSam Leffler * this descriptor. We take a tx interrupt to reap 5150c42a7b7eSSam Leffler * descriptors when the h/w hits an EOL condition or 5151c42a7b7eSSam Leffler * when the descriptor is specifically marked to generate 5152c42a7b7eSSam Leffler * an interrupt. We periodically mark descriptors in this 5153c42a7b7eSSam Leffler * way to insure timely replenishing of the supply needed 5154c42a7b7eSSam Leffler * for sending frames. Defering interrupts reduces system 5155c42a7b7eSSam Leffler * load and potentially allows more concurrent work to be 5156c42a7b7eSSam Leffler * done but if done to aggressively can cause senders to 5157c42a7b7eSSam Leffler * backup. 5158c42a7b7eSSam Leffler * 5159c42a7b7eSSam Leffler * NB: use >= to deal with sc_txintrperiod changing 5160c42a7b7eSSam Leffler * dynamically through sysctl. 5161c42a7b7eSSam Leffler */ 5162c42a7b7eSSam Leffler if (flags & HAL_TXDESC_INTREQ) { 5163c42a7b7eSSam Leffler txq->axq_intrcnt = 0; 5164c42a7b7eSSam Leffler } else if (++txq->axq_intrcnt >= sc->sc_txintrperiod) { 5165c42a7b7eSSam Leffler flags |= HAL_TXDESC_INTREQ; 5166c42a7b7eSSam Leffler txq->axq_intrcnt = 0; 5167c42a7b7eSSam Leffler } 5168c42a7b7eSSam Leffler 5169c42a7b7eSSam Leffler /* 51705591b213SSam Leffler * Formulate first tx descriptor with tx controls. 51715591b213SSam Leffler */ 51725591b213SSam Leffler /* XXX check return value? */ 51735591b213SSam Leffler ath_hal_setuptxdesc(ah, ds 51745591b213SSam Leffler , pktlen /* packet length */ 51755591b213SSam Leffler , hdrlen /* header length */ 51765591b213SSam Leffler , atype /* Atheros packet type */ 5177c42a7b7eSSam Leffler , ni->ni_txpower /* txpower */ 5178c42a7b7eSSam Leffler , txrate, try0 /* series 0 rate/tries */ 5179c42a7b7eSSam Leffler , keyix /* key cache index */ 5180c42a7b7eSSam Leffler , sc->sc_txantenna /* antenna mode */ 51815591b213SSam Leffler , flags /* flags */ 51825591b213SSam Leffler , ctsrate /* rts/cts rate */ 51835591b213SSam Leffler , ctsduration /* rts/cts duration */ 51845591b213SSam Leffler ); 518580c07f23SSam Leffler bf->bf_txflags = flags; 5186c42a7b7eSSam Leffler /* 5187c42a7b7eSSam Leffler * Setup the multi-rate retry state only when we're 5188c42a7b7eSSam Leffler * going to use it. This assumes ath_hal_setuptxdesc 5189c42a7b7eSSam Leffler * initializes the descriptors (so we don't have to) 5190c42a7b7eSSam Leffler * when the hardware supports multi-rate retry and 5191c42a7b7eSSam Leffler * we don't use it. 5192c42a7b7eSSam Leffler */ 5193be613480SSam Leffler if (ismrr) 5194c42a7b7eSSam Leffler ath_rate_setupxtxdesc(sc, an, ds, shortPreamble, rix); 5195c42a7b7eSSam Leffler 5196664443d0SSam Leffler ath_tx_handoff(sc, txq, bf); 51975591b213SSam Leffler return 0; 51985591b213SSam Leffler } 51995591b213SSam Leffler 5200c42a7b7eSSam Leffler /* 5201c42a7b7eSSam Leffler * Process completed xmit descriptors from the specified queue. 5202c42a7b7eSSam Leffler */ 5203d7736e13SSam Leffler static int 5204c42a7b7eSSam Leffler ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) 52055591b213SSam Leffler { 52065591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 5207b032f27cSSam Leffler struct ifnet *ifp = sc->sc_ifp; 5208b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 520910ad9a77SSam Leffler struct ath_buf *bf, *last; 5210c4c3cb46SSam Leffler struct ath_desc *ds, *ds0; 521165f9edeeSSam Leffler struct ath_tx_status *ts; 52125591b213SSam Leffler struct ieee80211_node *ni; 52135591b213SSam Leffler struct ath_node *an; 5214d7736e13SSam Leffler int sr, lr, pri, nacked; 52155591b213SSam Leffler HAL_STATUS status; 52165591b213SSam Leffler 5217c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: tx queue %u head %p link %p\n", 5218c42a7b7eSSam Leffler __func__, txq->axq_qnum, 5219c42a7b7eSSam Leffler (caddr_t)(uintptr_t) ath_hal_gettxbuf(sc->sc_ah, txq->axq_qnum), 5220c42a7b7eSSam Leffler txq->axq_link); 5221d7736e13SSam Leffler nacked = 0; 52225591b213SSam Leffler for (;;) { 5223c42a7b7eSSam Leffler ATH_TXQ_LOCK(txq); 5224c42a7b7eSSam Leffler txq->axq_intrcnt = 0; /* reset periodic desc intr count */ 5225c42a7b7eSSam Leffler bf = STAILQ_FIRST(&txq->axq_q); 52265591b213SSam Leffler if (bf == NULL) { 5227c42a7b7eSSam Leffler ATH_TXQ_UNLOCK(txq); 52285591b213SSam Leffler break; 52295591b213SSam Leffler } 5230c4c3cb46SSam Leffler ds0 = &bf->bf_desc[0]; 52315591b213SSam Leffler ds = &bf->bf_desc[bf->bf_nseg - 1]; 523265f9edeeSSam Leffler ts = &bf->bf_status.ds_txstat; 523365f9edeeSSam Leffler status = ath_hal_txprocdesc(ah, ds, ts); 5234a585a9a1SSam Leffler #ifdef ATH_DEBUG 5235c42a7b7eSSam Leffler if (sc->sc_debug & ATH_DEBUG_XMIT_DESC) 52366902009eSSam Leffler ath_printtxbuf(sc, bf, txq->axq_qnum, 0, 52376902009eSSam Leffler status == HAL_OK); 52385591b213SSam Leffler #endif 52395591b213SSam Leffler if (status == HAL_EINPROGRESS) { 5240c42a7b7eSSam Leffler ATH_TXQ_UNLOCK(txq); 52415591b213SSam Leffler break; 52425591b213SSam Leffler } 5243c42a7b7eSSam Leffler ATH_TXQ_REMOVE_HEAD(txq, bf_list); 524410ad9a77SSam Leffler #ifdef ATH_SUPPORT_TDMA 524510ad9a77SSam Leffler if (txq->axq_depth > 0) { 524610ad9a77SSam Leffler /* 524710ad9a77SSam Leffler * More frames follow. Mark the buffer busy 524810ad9a77SSam Leffler * so it's not re-used while the hardware may 524910ad9a77SSam Leffler * still re-read the link field in the descriptor. 525010ad9a77SSam Leffler */ 525110ad9a77SSam Leffler bf->bf_flags |= ATH_BUF_BUSY; 525210ad9a77SSam Leffler } else 525310ad9a77SSam Leffler #else 5254ebecf802SSam Leffler if (txq->axq_depth == 0) 525510ad9a77SSam Leffler #endif 52561539af1eSSam Leffler txq->axq_link = NULL; 5257c42a7b7eSSam Leffler ATH_TXQ_UNLOCK(txq); 52585591b213SSam Leffler 52595591b213SSam Leffler ni = bf->bf_node; 52605591b213SSam Leffler if (ni != NULL) { 5261c42a7b7eSSam Leffler an = ATH_NODE(ni); 526265f9edeeSSam Leffler if (ts->ts_status == 0) { 526365f9edeeSSam Leffler u_int8_t txant = ts->ts_antenna; 5264c42a7b7eSSam Leffler sc->sc_stats.ast_ant_tx[txant]++; 5265c42a7b7eSSam Leffler sc->sc_ant_tx[txant]++; 526665f9edeeSSam Leffler if (ts->ts_rate & HAL_TXSTAT_ALTRATE) 5267c42a7b7eSSam Leffler sc->sc_stats.ast_tx_altrate++; 5268c42a7b7eSSam Leffler pri = M_WME_GETAC(bf->bf_m); 5269c42a7b7eSSam Leffler if (pri >= WME_AC_VO) 5270c42a7b7eSSam Leffler ic->ic_wme.wme_hipri_traffic++; 5271ad80c0aaSSam Leffler if ((bf->bf_txflags & HAL_TXDESC_NOACK) == 0) 5272c42a7b7eSSam Leffler ni->ni_inact = ni->ni_inact_reload; 52735591b213SSam Leffler } else { 527465f9edeeSSam Leffler if (ts->ts_status & HAL_TXERR_XRETRY) 52755591b213SSam Leffler sc->sc_stats.ast_tx_xretries++; 527665f9edeeSSam Leffler if (ts->ts_status & HAL_TXERR_FIFO) 52775591b213SSam Leffler sc->sc_stats.ast_tx_fifoerr++; 527865f9edeeSSam Leffler if (ts->ts_status & HAL_TXERR_FILT) 52795591b213SSam Leffler sc->sc_stats.ast_tx_filtered++; 528068e8e04eSSam Leffler if (bf->bf_m->m_flags & M_FF) 528168e8e04eSSam Leffler sc->sc_stats.ast_ff_txerr++; 52825591b213SSam Leffler } 528365f9edeeSSam Leffler sr = ts->ts_shortretry; 528465f9edeeSSam Leffler lr = ts->ts_longretry; 52855591b213SSam Leffler sc->sc_stats.ast_tx_shortretry += sr; 52865591b213SSam Leffler sc->sc_stats.ast_tx_longretry += lr; 5287c42a7b7eSSam Leffler /* 5288c42a7b7eSSam Leffler * Hand the descriptor to the rate control algorithm. 5289c42a7b7eSSam Leffler */ 529065f9edeeSSam Leffler if ((ts->ts_status & HAL_TXERR_FILT) == 0 && 529180c07f23SSam Leffler (bf->bf_txflags & HAL_TXDESC_NOACK) == 0) { 5292d7736e13SSam Leffler /* 529384784be1SSam Leffler * If frame was ack'd update statistics, 529484784be1SSam Leffler * including the last rx time used to 529584784be1SSam Leffler * workaround phantom bmiss interrupts. 5296d7736e13SSam Leffler */ 529784784be1SSam Leffler if (ts->ts_status == 0) { 5298d7736e13SSam Leffler nacked++; 529984784be1SSam Leffler sc->sc_stats.ast_tx_rssi = ts->ts_rssi; 530084784be1SSam Leffler ATH_RSSI_LPF(sc->sc_halstats.ns_avgtxrssi, 530184784be1SSam Leffler ts->ts_rssi); 530284784be1SSam Leffler } 530365f9edeeSSam Leffler ath_rate_tx_complete(sc, an, bf); 5304d7736e13SSam Leffler } 53050a915fadSSam Leffler /* 530668e8e04eSSam Leffler * Do any tx complete callback. Note this must 530768e8e04eSSam Leffler * be done before releasing the node reference. 530868e8e04eSSam Leffler */ 530968e8e04eSSam Leffler if (bf->bf_m->m_flags & M_TXCB) 531068e8e04eSSam Leffler ieee80211_process_callback(ni, bf->bf_m, 531174eca0c2SSam Leffler (bf->bf_txflags & HAL_TXDESC_NOACK) == 0 ? 531274eca0c2SSam Leffler ts->ts_status : HAL_TXERR_XRETRY); 531368e8e04eSSam Leffler /* 53140a915fadSSam Leffler * Reclaim reference to node. 53150a915fadSSam Leffler * 53160a915fadSSam Leffler * NB: the node may be reclaimed here if, for example 53170a915fadSSam Leffler * this is a DEAUTH message that was sent and the 53180a915fadSSam Leffler * node was timed out due to inactivity. 53190a915fadSSam Leffler */ 5320c42a7b7eSSam Leffler ieee80211_free_node(ni); 53215591b213SSam Leffler } 53225591b213SSam Leffler bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, 53235591b213SSam Leffler BUS_DMASYNC_POSTWRITE); 53245591b213SSam Leffler bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 532568e8e04eSSam Leffler 53265591b213SSam Leffler m_freem(bf->bf_m); 53275591b213SSam Leffler bf->bf_m = NULL; 53285591b213SSam Leffler bf->bf_node = NULL; 53295591b213SSam Leffler 5330f0b2a0beSSam Leffler ATH_TXBUF_LOCK(sc); 533110ad9a77SSam Leffler last = STAILQ_LAST(&sc->sc_txbuf, ath_buf, bf_list); 533210ad9a77SSam Leffler if (last != NULL) 533310ad9a77SSam Leffler last->bf_flags &= ~ATH_BUF_BUSY; 5334c42a7b7eSSam Leffler STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); 5335f0b2a0beSSam Leffler ATH_TXBUF_UNLOCK(sc); 53365591b213SSam Leffler } 533768e8e04eSSam Leffler /* 533868e8e04eSSam Leffler * Flush fast-frame staging queue when traffic slows. 533968e8e04eSSam Leffler */ 534068e8e04eSSam Leffler if (txq->axq_depth <= 1) 534168e8e04eSSam Leffler ath_ff_stageq_flush(sc, txq, ath_ff_always); 5342d7736e13SSam Leffler return nacked; 5343d7736e13SSam Leffler } 5344d7736e13SSam Leffler 5345d7736e13SSam Leffler static __inline int 5346d7736e13SSam Leffler txqactive(struct ath_hal *ah, int qnum) 5347d7736e13SSam Leffler { 5348e2815d69SSam Leffler u_int32_t txqs = 1<<qnum; 5349e2815d69SSam Leffler ath_hal_gettxintrtxqs(ah, &txqs); 53509760f8aeSSam Leffler return (txqs & (1<<qnum)); 5351c42a7b7eSSam Leffler } 5352c42a7b7eSSam Leffler 5353c42a7b7eSSam Leffler /* 5354c42a7b7eSSam Leffler * Deferred processing of transmit interrupt; special-cased 5355c42a7b7eSSam Leffler * for a single hardware transmit queue (e.g. 5210 and 5211). 5356c42a7b7eSSam Leffler */ 5357c42a7b7eSSam Leffler static void 5358c42a7b7eSSam Leffler ath_tx_proc_q0(void *arg, int npending) 5359c42a7b7eSSam Leffler { 5360c42a7b7eSSam Leffler struct ath_softc *sc = arg; 5361fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 5362c42a7b7eSSam Leffler 5363d7736e13SSam Leffler if (txqactive(sc->sc_ah, 0) && ath_tx_processq(sc, &sc->sc_txq[0])) 5364d7736e13SSam Leffler sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah); 5365d7736e13SSam Leffler if (txqactive(sc->sc_ah, sc->sc_cabq->axq_qnum)) 5366d7736e13SSam Leffler ath_tx_processq(sc, sc->sc_cabq); 536713f4c340SRobert Watson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 536868e8e04eSSam Leffler ifp->if_timer = 0; 53695591b213SSam Leffler 53703e50ec2cSSam Leffler if (sc->sc_softled) 537146d4d74cSSam Leffler ath_led_event(sc, sc->sc_txrix); 53723e50ec2cSSam Leffler 53735591b213SSam Leffler ath_start(ifp); 53745591b213SSam Leffler } 53755591b213SSam Leffler 53765591b213SSam Leffler /* 5377c42a7b7eSSam Leffler * Deferred processing of transmit interrupt; special-cased 5378c42a7b7eSSam Leffler * for four hardware queues, 0-3 (e.g. 5212 w/ WME support). 53795591b213SSam Leffler */ 53805591b213SSam Leffler static void 5381c42a7b7eSSam Leffler ath_tx_proc_q0123(void *arg, int npending) 5382c42a7b7eSSam Leffler { 5383c42a7b7eSSam Leffler struct ath_softc *sc = arg; 5384fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 5385d7736e13SSam Leffler int nacked; 5386c42a7b7eSSam Leffler 5387c42a7b7eSSam Leffler /* 5388c42a7b7eSSam Leffler * Process each active queue. 5389c42a7b7eSSam Leffler */ 5390d7736e13SSam Leffler nacked = 0; 5391d7736e13SSam Leffler if (txqactive(sc->sc_ah, 0)) 5392d7736e13SSam Leffler nacked += ath_tx_processq(sc, &sc->sc_txq[0]); 5393d7736e13SSam Leffler if (txqactive(sc->sc_ah, 1)) 5394d7736e13SSam Leffler nacked += ath_tx_processq(sc, &sc->sc_txq[1]); 5395d7736e13SSam Leffler if (txqactive(sc->sc_ah, 2)) 5396d7736e13SSam Leffler nacked += ath_tx_processq(sc, &sc->sc_txq[2]); 5397d7736e13SSam Leffler if (txqactive(sc->sc_ah, 3)) 5398d7736e13SSam Leffler nacked += ath_tx_processq(sc, &sc->sc_txq[3]); 5399d7736e13SSam Leffler if (txqactive(sc->sc_ah, sc->sc_cabq->axq_qnum)) 5400c42a7b7eSSam Leffler ath_tx_processq(sc, sc->sc_cabq); 5401d7736e13SSam Leffler if (nacked) 5402d7736e13SSam Leffler sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah); 5403c42a7b7eSSam Leffler 540413f4c340SRobert Watson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 540568e8e04eSSam Leffler ifp->if_timer = 0; 5406c42a7b7eSSam Leffler 54073e50ec2cSSam Leffler if (sc->sc_softled) 540846d4d74cSSam Leffler ath_led_event(sc, sc->sc_txrix); 54093e50ec2cSSam Leffler 5410c42a7b7eSSam Leffler ath_start(ifp); 5411c42a7b7eSSam Leffler } 5412c42a7b7eSSam Leffler 5413c42a7b7eSSam Leffler /* 5414c42a7b7eSSam Leffler * Deferred processing of transmit interrupt. 5415c42a7b7eSSam Leffler */ 5416c42a7b7eSSam Leffler static void 5417c42a7b7eSSam Leffler ath_tx_proc(void *arg, int npending) 5418c42a7b7eSSam Leffler { 5419c42a7b7eSSam Leffler struct ath_softc *sc = arg; 5420fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 5421d7736e13SSam Leffler int i, nacked; 5422c42a7b7eSSam Leffler 5423c42a7b7eSSam Leffler /* 5424c42a7b7eSSam Leffler * Process each active queue. 5425c42a7b7eSSam Leffler */ 5426d7736e13SSam Leffler nacked = 0; 5427c42a7b7eSSam Leffler for (i = 0; i < HAL_NUM_TX_QUEUES; i++) 5428d7736e13SSam Leffler if (ATH_TXQ_SETUP(sc, i) && txqactive(sc->sc_ah, i)) 5429d7736e13SSam Leffler nacked += ath_tx_processq(sc, &sc->sc_txq[i]); 5430d7736e13SSam Leffler if (nacked) 5431d7736e13SSam Leffler sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah); 5432c42a7b7eSSam Leffler 543313f4c340SRobert Watson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 543468e8e04eSSam Leffler ifp->if_timer = 0; 5435c42a7b7eSSam Leffler 54363e50ec2cSSam Leffler if (sc->sc_softled) 543746d4d74cSSam Leffler ath_led_event(sc, sc->sc_txrix); 54383e50ec2cSSam Leffler 5439c42a7b7eSSam Leffler ath_start(ifp); 5440c42a7b7eSSam Leffler } 5441c42a7b7eSSam Leffler 5442c42a7b7eSSam Leffler static void 5443c42a7b7eSSam Leffler ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq) 54445591b213SSam Leffler { 5445a585a9a1SSam Leffler #ifdef ATH_DEBUG 54465591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 5447d2f6ed15SSam Leffler #endif 544823428eafSSam Leffler struct ieee80211_node *ni; 54495591b213SSam Leffler struct ath_buf *bf; 54507a4c5ed9SSam Leffler u_int ix; 54515591b213SSam Leffler 5452c42a7b7eSSam Leffler /* 5453c42a7b7eSSam Leffler * NB: this assumes output has been stopped and 54545d61b5e8SSam Leffler * we do not need to block ath_tx_proc 5455c42a7b7eSSam Leffler */ 545610ad9a77SSam Leffler ATH_TXBUF_LOCK(sc); 545710ad9a77SSam Leffler bf = STAILQ_LAST(&sc->sc_txbuf, ath_buf, bf_list); 545810ad9a77SSam Leffler if (bf != NULL) 545910ad9a77SSam Leffler bf->bf_flags &= ~ATH_BUF_BUSY; 546010ad9a77SSam Leffler ATH_TXBUF_UNLOCK(sc); 54617a4c5ed9SSam Leffler for (ix = 0;; ix++) { 5462c42a7b7eSSam Leffler ATH_TXQ_LOCK(txq); 5463c42a7b7eSSam Leffler bf = STAILQ_FIRST(&txq->axq_q); 54645591b213SSam Leffler if (bf == NULL) { 5465ebecf802SSam Leffler txq->axq_link = NULL; 5466c42a7b7eSSam Leffler ATH_TXQ_UNLOCK(txq); 54675591b213SSam Leffler break; 54685591b213SSam Leffler } 5469c42a7b7eSSam Leffler ATH_TXQ_REMOVE_HEAD(txq, bf_list); 5470c42a7b7eSSam Leffler ATH_TXQ_UNLOCK(txq); 5471a585a9a1SSam Leffler #ifdef ATH_DEBUG 54724a3ac3fcSSam Leffler if (sc->sc_debug & ATH_DEBUG_RESET) { 5473b032f27cSSam Leffler struct ieee80211com *ic = sc->sc_ifp->if_l2com; 5474b032f27cSSam Leffler 54756902009eSSam Leffler ath_printtxbuf(sc, bf, txq->axq_qnum, ix, 547665f9edeeSSam Leffler ath_hal_txprocdesc(ah, bf->bf_desc, 547765f9edeeSSam Leffler &bf->bf_status.ds_txstat) == HAL_OK); 5478b032f27cSSam Leffler ieee80211_dump_pkt(ic, mtod(bf->bf_m, caddr_t), 54794a3ac3fcSSam Leffler bf->bf_m->m_len, 0, -1); 54804a3ac3fcSSam Leffler } 5481a585a9a1SSam Leffler #endif /* ATH_DEBUG */ 54825591b213SSam Leffler bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 548323428eafSSam Leffler ni = bf->bf_node; 54845591b213SSam Leffler bf->bf_node = NULL; 5485c42a7b7eSSam Leffler if (ni != NULL) { 548623428eafSSam Leffler /* 5487d50ea6acSSam Leffler * Do any callback and reclaim the node reference. 548823428eafSSam Leffler */ 5489d50ea6acSSam Leffler if (bf->bf_m->m_flags & M_TXCB) 5490d50ea6acSSam Leffler ieee80211_process_callback(ni, bf->bf_m, -1); 5491c42a7b7eSSam Leffler ieee80211_free_node(ni); 549223428eafSSam Leffler } 549368e8e04eSSam Leffler m_freem(bf->bf_m); 549468e8e04eSSam Leffler bf->bf_m = NULL; 549510ad9a77SSam Leffler bf->bf_flags &= ~ATH_BUF_BUSY; 549668e8e04eSSam Leffler 5497f0b2a0beSSam Leffler ATH_TXBUF_LOCK(sc); 5498c42a7b7eSSam Leffler STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); 5499f0b2a0beSSam Leffler ATH_TXBUF_UNLOCK(sc); 55005591b213SSam Leffler } 5501c42a7b7eSSam Leffler } 5502c42a7b7eSSam Leffler 5503c42a7b7eSSam Leffler static void 5504c42a7b7eSSam Leffler ath_tx_stopdma(struct ath_softc *sc, struct ath_txq *txq) 5505c42a7b7eSSam Leffler { 5506c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 5507c42a7b7eSSam Leffler 5508c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_RESET, "%s: tx queue [%u] %p, link %p\n", 5509c42a7b7eSSam Leffler __func__, txq->axq_qnum, 55106891c875SPeter Wemm (caddr_t)(uintptr_t) ath_hal_gettxbuf(ah, txq->axq_qnum), 55116891c875SPeter Wemm txq->axq_link); 55124a3ac3fcSSam Leffler (void) ath_hal_stoptxdma(ah, txq->axq_qnum); 5513c42a7b7eSSam Leffler } 5514c42a7b7eSSam Leffler 5515c42a7b7eSSam Leffler /* 5516c42a7b7eSSam Leffler * Drain the transmit queues and reclaim resources. 5517c42a7b7eSSam Leffler */ 5518c42a7b7eSSam Leffler static void 5519c42a7b7eSSam Leffler ath_draintxq(struct ath_softc *sc) 5520c42a7b7eSSam Leffler { 5521c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 5522fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 5523c42a7b7eSSam Leffler int i; 5524c42a7b7eSSam Leffler 5525c42a7b7eSSam Leffler /* XXX return value */ 5526c42a7b7eSSam Leffler if (!sc->sc_invalid) { 5527c42a7b7eSSam Leffler /* don't touch the hardware if marked invalid */ 55284a3ac3fcSSam Leffler DPRINTF(sc, ATH_DEBUG_RESET, "%s: tx queue [%u] %p, link %p\n", 55294a3ac3fcSSam Leffler __func__, sc->sc_bhalq, 55304a3ac3fcSSam Leffler (caddr_t)(uintptr_t) ath_hal_gettxbuf(ah, sc->sc_bhalq), 55314a3ac3fcSSam Leffler NULL); 5532c42a7b7eSSam Leffler (void) ath_hal_stoptxdma(ah, sc->sc_bhalq); 5533c42a7b7eSSam Leffler for (i = 0; i < HAL_NUM_TX_QUEUES; i++) 5534c42a7b7eSSam Leffler if (ATH_TXQ_SETUP(sc, i)) 5535c42a7b7eSSam Leffler ath_tx_stopdma(sc, &sc->sc_txq[i]); 5536c42a7b7eSSam Leffler } 5537c42a7b7eSSam Leffler for (i = 0; i < HAL_NUM_TX_QUEUES; i++) 5538c42a7b7eSSam Leffler if (ATH_TXQ_SETUP(sc, i)) 5539c42a7b7eSSam Leffler ath_tx_draintxq(sc, &sc->sc_txq[i]); 55404a3ac3fcSSam Leffler #ifdef ATH_DEBUG 55414a3ac3fcSSam Leffler if (sc->sc_debug & ATH_DEBUG_RESET) { 55424a3ac3fcSSam Leffler struct ath_buf *bf = STAILQ_FIRST(&sc->sc_bbuf); 55434a3ac3fcSSam Leffler if (bf != NULL && bf->bf_m != NULL) { 55446902009eSSam Leffler ath_printtxbuf(sc, bf, sc->sc_bhalq, 0, 554565f9edeeSSam Leffler ath_hal_txprocdesc(ah, bf->bf_desc, 554665f9edeeSSam Leffler &bf->bf_status.ds_txstat) == HAL_OK); 5547b032f27cSSam Leffler ieee80211_dump_pkt(ifp->if_l2com, mtod(bf->bf_m, caddr_t), 55484a3ac3fcSSam Leffler bf->bf_m->m_len, 0, -1); 55494a3ac3fcSSam Leffler } 55504a3ac3fcSSam Leffler } 55514a3ac3fcSSam Leffler #endif /* ATH_DEBUG */ 555213f4c340SRobert Watson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 555368e8e04eSSam Leffler ifp->if_timer = 0; 55545591b213SSam Leffler } 55555591b213SSam Leffler 55565591b213SSam Leffler /* 55575591b213SSam Leffler * Disable the receive h/w in preparation for a reset. 55585591b213SSam Leffler */ 55595591b213SSam Leffler static void 55605591b213SSam Leffler ath_stoprecv(struct ath_softc *sc) 55615591b213SSam Leffler { 55628cec0ab9SSam Leffler #define PA2DESC(_sc, _pa) \ 5563c42a7b7eSSam Leffler ((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \ 5564c42a7b7eSSam Leffler ((_pa) - (_sc)->sc_rxdma.dd_desc_paddr))) 55655591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 55665591b213SSam Leffler 55675591b213SSam Leffler ath_hal_stoppcurecv(ah); /* disable PCU */ 55685591b213SSam Leffler ath_hal_setrxfilter(ah, 0); /* clear recv filter */ 55695591b213SSam Leffler ath_hal_stopdmarecv(ah); /* disable DMA engine */ 5570c42a7b7eSSam Leffler DELAY(3000); /* 3ms is long enough for 1 frame */ 5571a585a9a1SSam Leffler #ifdef ATH_DEBUG 5572c42a7b7eSSam Leffler if (sc->sc_debug & (ATH_DEBUG_RESET | ATH_DEBUG_FATAL)) { 55735591b213SSam Leffler struct ath_buf *bf; 55747a4c5ed9SSam Leffler u_int ix; 55755591b213SSam Leffler 5576e325e530SSam Leffler printf("%s: rx queue %p, link %p\n", __func__, 557730310634SPeter Wemm (caddr_t)(uintptr_t) ath_hal_getrxbuf(ah), sc->sc_rxlink); 55787a4c5ed9SSam Leffler ix = 0; 5579c42a7b7eSSam Leffler STAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) { 55808cec0ab9SSam Leffler struct ath_desc *ds = bf->bf_desc; 558165f9edeeSSam Leffler struct ath_rx_status *rs = &bf->bf_status.ds_rxstat; 5582c42a7b7eSSam Leffler HAL_STATUS status = ath_hal_rxprocdesc(ah, ds, 558365f9edeeSSam Leffler bf->bf_daddr, PA2DESC(sc, ds->ds_link), rs); 5584c42a7b7eSSam Leffler if (status == HAL_OK || (sc->sc_debug & ATH_DEBUG_FATAL)) 55856902009eSSam Leffler ath_printrxbuf(sc, bf, ix, status == HAL_OK); 55867a4c5ed9SSam Leffler ix++; 55875591b213SSam Leffler } 55885591b213SSam Leffler } 55895591b213SSam Leffler #endif 559068e8e04eSSam Leffler if (sc->sc_rxpending != NULL) { 559168e8e04eSSam Leffler m_freem(sc->sc_rxpending); 559268e8e04eSSam Leffler sc->sc_rxpending = NULL; 559368e8e04eSSam Leffler } 55945591b213SSam Leffler sc->sc_rxlink = NULL; /* just in case */ 55958cec0ab9SSam Leffler #undef PA2DESC 55965591b213SSam Leffler } 55975591b213SSam Leffler 55985591b213SSam Leffler /* 55995591b213SSam Leffler * Enable the receive h/w following a reset. 56005591b213SSam Leffler */ 56015591b213SSam Leffler static int 56025591b213SSam Leffler ath_startrecv(struct ath_softc *sc) 56035591b213SSam Leffler { 56045591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 56055591b213SSam Leffler struct ath_buf *bf; 56065591b213SSam Leffler 56075591b213SSam Leffler sc->sc_rxlink = NULL; 560868e8e04eSSam Leffler sc->sc_rxpending = NULL; 5609c42a7b7eSSam Leffler STAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) { 56105591b213SSam Leffler int error = ath_rxbuf_init(sc, bf); 56115591b213SSam Leffler if (error != 0) { 5612c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_RECV, 5613c42a7b7eSSam Leffler "%s: ath_rxbuf_init failed %d\n", 5614c42a7b7eSSam Leffler __func__, error); 56155591b213SSam Leffler return error; 56165591b213SSam Leffler } 56175591b213SSam Leffler } 56185591b213SSam Leffler 5619c42a7b7eSSam Leffler bf = STAILQ_FIRST(&sc->sc_rxbuf); 56205591b213SSam Leffler ath_hal_putrxbuf(ah, bf->bf_daddr); 56215591b213SSam Leffler ath_hal_rxena(ah); /* enable recv descriptors */ 56225591b213SSam Leffler ath_mode_init(sc); /* set filters, etc. */ 56235591b213SSam Leffler ath_hal_startpcurecv(ah); /* re-enable PCU/DMA engine */ 56245591b213SSam Leffler return 0; 56255591b213SSam Leffler } 56265591b213SSam Leffler 56275591b213SSam Leffler /* 5628c42a7b7eSSam Leffler * Update internal state after a channel change. 5629c42a7b7eSSam Leffler */ 5630c42a7b7eSSam Leffler static void 5631c42a7b7eSSam Leffler ath_chan_change(struct ath_softc *sc, struct ieee80211_channel *chan) 5632c42a7b7eSSam Leffler { 5633c42a7b7eSSam Leffler enum ieee80211_phymode mode; 5634c42a7b7eSSam Leffler 5635c42a7b7eSSam Leffler /* 5636c42a7b7eSSam Leffler * Change channels and update the h/w rate map 5637c42a7b7eSSam Leffler * if we're switching; e.g. 11a to 11b/g. 5638c42a7b7eSSam Leffler */ 5639aaa70f2fSSam Leffler if (IEEE80211_IS_CHAN_HALF(chan)) 5640724c193aSSam Leffler mode = IEEE80211_MODE_HALF; 5641aaa70f2fSSam Leffler else if (IEEE80211_IS_CHAN_QUARTER(chan)) 5642724c193aSSam Leffler mode = IEEE80211_MODE_QUARTER; 5643724c193aSSam Leffler else 564468e8e04eSSam Leffler mode = ieee80211_chan2mode(chan); 5645c42a7b7eSSam Leffler if (mode != sc->sc_curmode) 5646c42a7b7eSSam Leffler ath_setcurmode(sc, mode); 564759efa8b5SSam Leffler sc->sc_curchan = chan; 564868e8e04eSSam Leffler 564968e8e04eSSam Leffler sc->sc_rx_th.wr_chan_flags = htole32(chan->ic_flags); 565068e8e04eSSam Leffler sc->sc_tx_th.wt_chan_flags = sc->sc_rx_th.wr_chan_flags; 565168e8e04eSSam Leffler sc->sc_rx_th.wr_chan_freq = htole16(chan->ic_freq); 565268e8e04eSSam Leffler sc->sc_tx_th.wt_chan_freq = sc->sc_rx_th.wr_chan_freq; 565368e8e04eSSam Leffler sc->sc_rx_th.wr_chan_ieee = chan->ic_ieee; 565468e8e04eSSam Leffler sc->sc_tx_th.wt_chan_ieee = sc->sc_rx_th.wr_chan_ieee; 565568e8e04eSSam Leffler sc->sc_rx_th.wr_chan_maxpow = chan->ic_maxregpower; 565668e8e04eSSam Leffler sc->sc_tx_th.wt_chan_maxpow = sc->sc_rx_th.wr_chan_maxpow; 5657c42a7b7eSSam Leffler } 5658c42a7b7eSSam Leffler 5659c42a7b7eSSam Leffler /* 56605591b213SSam Leffler * Set/change channels. If the channel is really being changed, 5661c42a7b7eSSam Leffler * it's done by reseting the chip. To accomplish this we must 56625591b213SSam Leffler * first cleanup any pending DMA, then restart stuff after a la 56635591b213SSam Leffler * ath_init. 56645591b213SSam Leffler */ 56655591b213SSam Leffler static int 56665591b213SSam Leffler ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan) 56675591b213SSam Leffler { 5668b032f27cSSam Leffler struct ifnet *ifp = sc->sc_ifp; 5669b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 56705591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 5671c42a7b7eSSam Leffler 567259efa8b5SSam Leffler DPRINTF(sc, ATH_DEBUG_RESET, "%s: %u (%u MHz, flags 0x%x)\n", 567359efa8b5SSam Leffler __func__, ieee80211_chan2ieee(ic, chan), 567459efa8b5SSam Leffler chan->ic_freq, chan->ic_flags); 567559efa8b5SSam Leffler if (chan != sc->sc_curchan) { 5676c42a7b7eSSam Leffler HAL_STATUS status; 56775591b213SSam Leffler /* 56785591b213SSam Leffler * To switch channels clear any pending DMA operations; 56795591b213SSam Leffler * wait long enough for the RX fifo to drain, reset the 56805591b213SSam Leffler * hardware at the new frequency, and then re-enable 56815591b213SSam Leffler * the relevant bits of the h/w. 56825591b213SSam Leffler */ 56835591b213SSam Leffler ath_hal_intrset(ah, 0); /* disable interrupts */ 56845591b213SSam Leffler ath_draintxq(sc); /* clear pending tx frames */ 56855591b213SSam Leffler ath_stoprecv(sc); /* turn off frame recv */ 568659efa8b5SSam Leffler if (!ath_hal_reset(ah, sc->sc_opmode, chan, AH_TRUE, &status)) { 5687b032f27cSSam Leffler if_printf(ifp, "%s: unable to reset " 568859efa8b5SSam Leffler "channel %u (%u Mhz, flags 0x%x), hal status %u\n", 568959efa8b5SSam Leffler __func__, ieee80211_chan2ieee(ic, chan), 569059efa8b5SSam Leffler chan->ic_freq, chan->ic_flags, status); 56915591b213SSam Leffler return EIO; 56925591b213SSam Leffler } 5693c59005e9SSam Leffler sc->sc_diversity = ath_hal_getdiversity(ah); 5694c42a7b7eSSam Leffler 56955591b213SSam Leffler /* 56965591b213SSam Leffler * Re-enable rx framework. 56975591b213SSam Leffler */ 56985591b213SSam Leffler if (ath_startrecv(sc) != 0) { 5699b032f27cSSam Leffler if_printf(ifp, "%s: unable to restart recv logic\n", 5700b032f27cSSam Leffler __func__); 57015591b213SSam Leffler return EIO; 57025591b213SSam Leffler } 57035591b213SSam Leffler 57045591b213SSam Leffler /* 57055591b213SSam Leffler * Change channels and update the h/w rate map 57065591b213SSam Leffler * if we're switching; e.g. 11a to 11b/g. 57075591b213SSam Leffler */ 5708c42a7b7eSSam Leffler ath_chan_change(sc, chan); 57090a915fadSSam Leffler 57100a915fadSSam Leffler /* 57110a915fadSSam Leffler * Re-enable interrupts. 57120a915fadSSam Leffler */ 57130a915fadSSam Leffler ath_hal_intrset(ah, sc->sc_imask); 57145591b213SSam Leffler } 57155591b213SSam Leffler return 0; 57165591b213SSam Leffler } 57175591b213SSam Leffler 57185591b213SSam Leffler /* 57195591b213SSam Leffler * Periodically recalibrate the PHY to account 57205591b213SSam Leffler * for temperature/environment changes. 57215591b213SSam Leffler */ 57225591b213SSam Leffler static void 57235591b213SSam Leffler ath_calibrate(void *arg) 57245591b213SSam Leffler { 57255591b213SSam Leffler struct ath_softc *sc = arg; 57265591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 57272dc7fcc4SSam Leffler struct ifnet *ifp = sc->sc_ifp; 57288d91de92SSam Leffler struct ieee80211com *ic = ifp->if_l2com; 57292dc7fcc4SSam Leffler HAL_BOOL longCal, isCalDone; 57302dc7fcc4SSam Leffler int nextcal; 57315591b213SSam Leffler 57328d91de92SSam Leffler if (ic->ic_flags & IEEE80211_F_SCAN) /* defer, off channel */ 57338d91de92SSam Leffler goto restart; 57342dc7fcc4SSam Leffler longCal = (ticks - sc->sc_lastlongcal >= ath_longcalinterval*hz); 57352dc7fcc4SSam Leffler if (longCal) { 57365591b213SSam Leffler sc->sc_stats.ast_per_cal++; 57375591b213SSam Leffler if (ath_hal_getrfgain(ah) == HAL_RFGAIN_NEED_CHANGE) { 57385591b213SSam Leffler /* 57395591b213SSam Leffler * Rfgain is out of bounds, reset the chip 57405591b213SSam Leffler * to load new gain values. 57415591b213SSam Leffler */ 5742370572d9SSam Leffler DPRINTF(sc, ATH_DEBUG_CALIBRATE, 5743370572d9SSam Leffler "%s: rfgain change\n", __func__); 57445591b213SSam Leffler sc->sc_stats.ast_per_rfgain++; 57452dc7fcc4SSam Leffler ath_reset(ifp); 57465591b213SSam Leffler } 57472dc7fcc4SSam Leffler /* 57482dc7fcc4SSam Leffler * If this long cal is after an idle period, then 57492dc7fcc4SSam Leffler * reset the data collection state so we start fresh. 57502dc7fcc4SSam Leffler */ 57512dc7fcc4SSam Leffler if (sc->sc_resetcal) { 575259efa8b5SSam Leffler (void) ath_hal_calreset(ah, sc->sc_curchan); 57532dc7fcc4SSam Leffler sc->sc_lastcalreset = ticks; 57542dc7fcc4SSam Leffler sc->sc_resetcal = 0; 57552dc7fcc4SSam Leffler } 57562dc7fcc4SSam Leffler } 575759efa8b5SSam Leffler if (ath_hal_calibrateN(ah, sc->sc_curchan, longCal, &isCalDone)) { 57582dc7fcc4SSam Leffler if (longCal) { 57592dc7fcc4SSam Leffler /* 57602dc7fcc4SSam Leffler * Calibrate noise floor data again in case of change. 57612dc7fcc4SSam Leffler */ 57622dc7fcc4SSam Leffler ath_hal_process_noisefloor(ah); 57632dc7fcc4SSam Leffler } 57642dc7fcc4SSam Leffler } else { 5765c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, 5766c42a7b7eSSam Leffler "%s: calibration of channel %u failed\n", 576759efa8b5SSam Leffler __func__, sc->sc_curchan->ic_freq); 57685591b213SSam Leffler sc->sc_stats.ast_per_calfail++; 57695591b213SSam Leffler } 57702dc7fcc4SSam Leffler if (!isCalDone) { 57718d91de92SSam Leffler restart: 57727b0c77ecSSam Leffler /* 57732dc7fcc4SSam Leffler * Use a shorter interval to potentially collect multiple 57742dc7fcc4SSam Leffler * data samples required to complete calibration. Once 57752dc7fcc4SSam Leffler * we're told the work is done we drop back to a longer 57762dc7fcc4SSam Leffler * interval between requests. We're more aggressive doing 57772dc7fcc4SSam Leffler * work when operating as an AP to improve operation right 57782dc7fcc4SSam Leffler * after startup. 57797b0c77ecSSam Leffler */ 57802dc7fcc4SSam Leffler nextcal = (1000*ath_shortcalinterval)/hz; 57812dc7fcc4SSam Leffler if (sc->sc_opmode != HAL_M_HOSTAP) 57822dc7fcc4SSam Leffler nextcal *= 10; 57832dc7fcc4SSam Leffler } else { 57842dc7fcc4SSam Leffler nextcal = ath_longcalinterval*hz; 57852dc7fcc4SSam Leffler sc->sc_lastlongcal = ticks; 57862dc7fcc4SSam Leffler if (sc->sc_lastcalreset == 0) 57872dc7fcc4SSam Leffler sc->sc_lastcalreset = sc->sc_lastlongcal; 57882dc7fcc4SSam Leffler else if (ticks - sc->sc_lastcalreset >= ath_resetcalinterval*hz) 57892dc7fcc4SSam Leffler sc->sc_resetcal = 1; /* setup reset next trip */ 5790bd5a9920SSam Leffler } 5791bd5a9920SSam Leffler 57922dc7fcc4SSam Leffler if (nextcal != 0) { 57932dc7fcc4SSam Leffler DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: next +%u (%sisCalDone)\n", 57942dc7fcc4SSam Leffler __func__, nextcal, isCalDone ? "" : "!"); 57952dc7fcc4SSam Leffler callout_reset(&sc->sc_cal_ch, nextcal, ath_calibrate, sc); 57962dc7fcc4SSam Leffler } else { 57972dc7fcc4SSam Leffler DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: calibration disabled\n", 57982dc7fcc4SSam Leffler __func__); 57992dc7fcc4SSam Leffler /* NB: don't rearm timer */ 58002dc7fcc4SSam Leffler } 58015591b213SSam Leffler } 58025591b213SSam Leffler 580368e8e04eSSam Leffler static void 580468e8e04eSSam Leffler ath_scan_start(struct ieee80211com *ic) 580568e8e04eSSam Leffler { 580668e8e04eSSam Leffler struct ifnet *ifp = ic->ic_ifp; 580768e8e04eSSam Leffler struct ath_softc *sc = ifp->if_softc; 580868e8e04eSSam Leffler struct ath_hal *ah = sc->sc_ah; 580968e8e04eSSam Leffler u_int32_t rfilt; 581068e8e04eSSam Leffler 581168e8e04eSSam Leffler /* XXX calibration timer? */ 581268e8e04eSSam Leffler 581368e8e04eSSam Leffler sc->sc_scanning = 1; 581468e8e04eSSam Leffler sc->sc_syncbeacon = 0; 581568e8e04eSSam Leffler rfilt = ath_calcrxfilter(sc); 581668e8e04eSSam Leffler ath_hal_setrxfilter(ah, rfilt); 581768e8e04eSSam Leffler ath_hal_setassocid(ah, ifp->if_broadcastaddr, 0); 581868e8e04eSSam Leffler 581968e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s aid 0\n", 582068e8e04eSSam Leffler __func__, rfilt, ether_sprintf(ifp->if_broadcastaddr)); 582168e8e04eSSam Leffler } 582268e8e04eSSam Leffler 582368e8e04eSSam Leffler static void 582468e8e04eSSam Leffler ath_scan_end(struct ieee80211com *ic) 582568e8e04eSSam Leffler { 582668e8e04eSSam Leffler struct ifnet *ifp = ic->ic_ifp; 582768e8e04eSSam Leffler struct ath_softc *sc = ifp->if_softc; 582868e8e04eSSam Leffler struct ath_hal *ah = sc->sc_ah; 582968e8e04eSSam Leffler u_int32_t rfilt; 583068e8e04eSSam Leffler 583168e8e04eSSam Leffler sc->sc_scanning = 0; 583268e8e04eSSam Leffler rfilt = ath_calcrxfilter(sc); 583368e8e04eSSam Leffler ath_hal_setrxfilter(ah, rfilt); 583468e8e04eSSam Leffler ath_hal_setassocid(ah, sc->sc_curbssid, sc->sc_curaid); 583568e8e04eSSam Leffler 583668e8e04eSSam Leffler ath_hal_process_noisefloor(ah); 583768e8e04eSSam Leffler 583868e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s aid 0x%x\n", 583968e8e04eSSam Leffler __func__, rfilt, ether_sprintf(sc->sc_curbssid), 584068e8e04eSSam Leffler sc->sc_curaid); 584168e8e04eSSam Leffler } 584268e8e04eSSam Leffler 584368e8e04eSSam Leffler static void 584468e8e04eSSam Leffler ath_set_channel(struct ieee80211com *ic) 584568e8e04eSSam Leffler { 584668e8e04eSSam Leffler struct ifnet *ifp = ic->ic_ifp; 584768e8e04eSSam Leffler struct ath_softc *sc = ifp->if_softc; 584868e8e04eSSam Leffler 584968e8e04eSSam Leffler (void) ath_chan_set(sc, ic->ic_curchan); 585068e8e04eSSam Leffler /* 585168e8e04eSSam Leffler * If we are returning to our bss channel then mark state 585268e8e04eSSam Leffler * so the next recv'd beacon's tsf will be used to sync the 585368e8e04eSSam Leffler * beacon timers. Note that since we only hear beacons in 585468e8e04eSSam Leffler * sta/ibss mode this has no effect in other operating modes. 585568e8e04eSSam Leffler */ 585668e8e04eSSam Leffler if (!sc->sc_scanning && ic->ic_curchan == ic->ic_bsschan) 585768e8e04eSSam Leffler sc->sc_syncbeacon = 1; 585868e8e04eSSam Leffler } 585968e8e04eSSam Leffler 5860b032f27cSSam Leffler /* 5861b032f27cSSam Leffler * Walk the vap list and check if there any vap's in RUN state. 5862b032f27cSSam Leffler */ 58635591b213SSam Leffler static int 5864b032f27cSSam Leffler ath_isanyrunningvaps(struct ieee80211vap *this) 58655591b213SSam Leffler { 5866b032f27cSSam Leffler struct ieee80211com *ic = this->iv_ic; 5867b032f27cSSam Leffler struct ieee80211vap *vap; 5868b032f27cSSam Leffler 5869b032f27cSSam Leffler IEEE80211_LOCK_ASSERT(ic); 5870b032f27cSSam Leffler 5871b032f27cSSam Leffler TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 5872b032f27cSSam Leffler if (vap != this && vap->iv_state == IEEE80211_S_RUN) 5873b032f27cSSam Leffler return 1; 5874b032f27cSSam Leffler } 5875b032f27cSSam Leffler return 0; 5876b032f27cSSam Leffler } 5877b032f27cSSam Leffler 5878b032f27cSSam Leffler static int 5879b032f27cSSam Leffler ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 5880b032f27cSSam Leffler { 5881b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 5882b032f27cSSam Leffler struct ath_softc *sc = ic->ic_ifp->if_softc; 5883b032f27cSSam Leffler struct ath_vap *avp = ATH_VAP(vap); 588445bbf62fSSam Leffler struct ath_hal *ah = sc->sc_ah; 5885b032f27cSSam Leffler struct ieee80211_node *ni = NULL; 588668e8e04eSSam Leffler int i, error, stamode; 58875591b213SSam Leffler u_int32_t rfilt; 58885591b213SSam Leffler static const HAL_LED_STATE leds[] = { 58895591b213SSam Leffler HAL_LED_INIT, /* IEEE80211_S_INIT */ 58905591b213SSam Leffler HAL_LED_SCAN, /* IEEE80211_S_SCAN */ 58915591b213SSam Leffler HAL_LED_AUTH, /* IEEE80211_S_AUTH */ 58925591b213SSam Leffler HAL_LED_ASSOC, /* IEEE80211_S_ASSOC */ 589377d5e068SSam Leffler HAL_LED_RUN, /* IEEE80211_S_CAC */ 58945591b213SSam Leffler HAL_LED_RUN, /* IEEE80211_S_RUN */ 589577d5e068SSam Leffler HAL_LED_RUN, /* IEEE80211_S_CSA */ 589677d5e068SSam Leffler HAL_LED_RUN, /* IEEE80211_S_SLEEP */ 58975591b213SSam Leffler }; 58985591b213SSam Leffler 5899c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_STATE, "%s: %s -> %s\n", __func__, 5900b032f27cSSam Leffler ieee80211_state_name[vap->iv_state], 5901c42a7b7eSSam Leffler ieee80211_state_name[nstate]); 59025591b213SSam Leffler 5903c42a7b7eSSam Leffler callout_stop(&sc->sc_cal_ch); 59045591b213SSam Leffler ath_hal_setledstate(ah, leds[nstate]); /* set LED */ 59055591b213SSam Leffler 5906b032f27cSSam Leffler if (nstate == IEEE80211_S_SCAN) { 590758769f58SSam Leffler /* 5908b032f27cSSam Leffler * Scanning: turn off beacon miss and don't beacon. 5909b032f27cSSam Leffler * Mark beacon state so when we reach RUN state we'll 5910b032f27cSSam Leffler * [re]setup beacons. Unblock the task q thread so 5911b032f27cSSam Leffler * deferred interrupt processing is done. 591258769f58SSam Leffler */ 5913b032f27cSSam Leffler ath_hal_intrset(ah, 5914b032f27cSSam Leffler sc->sc_imask &~ (HAL_INT_SWBA | HAL_INT_BMISS)); 59155591b213SSam Leffler sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS); 5916b032f27cSSam Leffler sc->sc_beacons = 0; 5917b032f27cSSam Leffler taskqueue_unblock(sc->sc_tq); 59185591b213SSam Leffler } 59195591b213SSam Leffler 5920b032f27cSSam Leffler ni = vap->iv_bss; 592168e8e04eSSam Leffler rfilt = ath_calcrxfilter(sc); 5922b032f27cSSam Leffler stamode = (vap->iv_opmode == IEEE80211_M_STA || 59237b916f89SSam Leffler vap->iv_opmode == IEEE80211_M_AHDEMO || 5924b032f27cSSam Leffler vap->iv_opmode == IEEE80211_M_IBSS); 592568e8e04eSSam Leffler if (stamode && nstate == IEEE80211_S_RUN) { 592668e8e04eSSam Leffler sc->sc_curaid = ni->ni_associd; 592768e8e04eSSam Leffler IEEE80211_ADDR_COPY(sc->sc_curbssid, ni->ni_bssid); 5928b032f27cSSam Leffler ath_hal_setassocid(ah, sc->sc_curbssid, sc->sc_curaid); 5929b032f27cSSam Leffler } 593068e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s aid 0x%x\n", 5931b032f27cSSam Leffler __func__, rfilt, ether_sprintf(sc->sc_curbssid), sc->sc_curaid); 593268e8e04eSSam Leffler ath_hal_setrxfilter(ah, rfilt); 593368e8e04eSSam Leffler 5934b032f27cSSam Leffler /* XXX is this to restore keycache on resume? */ 5935b032f27cSSam Leffler if (vap->iv_opmode != IEEE80211_M_STA && 5936b032f27cSSam Leffler (vap->iv_flags & IEEE80211_F_PRIVACY)) { 59375591b213SSam Leffler for (i = 0; i < IEEE80211_WEP_NKID; i++) 59385591b213SSam Leffler if (ath_hal_keyisvalid(ah, i)) 593968e8e04eSSam Leffler ath_hal_keysetmac(ah, i, ni->ni_bssid); 59405591b213SSam Leffler } 5941b032f27cSSam Leffler 5942b032f27cSSam Leffler /* 5943b032f27cSSam Leffler * Invoke the parent method to do net80211 work. 5944b032f27cSSam Leffler */ 5945b032f27cSSam Leffler error = avp->av_newstate(vap, nstate, arg); 5946b032f27cSSam Leffler if (error != 0) 5947b032f27cSSam Leffler goto bad; 5948c42a7b7eSSam Leffler 594968e8e04eSSam Leffler if (nstate == IEEE80211_S_RUN) { 5950b032f27cSSam Leffler /* NB: collect bss node again, it may have changed */ 5951b032f27cSSam Leffler ni = vap->iv_bss; 59525591b213SSam Leffler 5953b032f27cSSam Leffler DPRINTF(sc, ATH_DEBUG_STATE, 5954b032f27cSSam Leffler "%s(RUN): iv_flags 0x%08x bintvl %d bssid %s " 5955b032f27cSSam Leffler "capinfo 0x%04x chan %d\n", __func__, 5956b032f27cSSam Leffler vap->iv_flags, ni->ni_intval, ether_sprintf(ni->ni_bssid), 5957b032f27cSSam Leffler ni->ni_capinfo, ieee80211_chan2ieee(ic, ic->ic_curchan)); 5958b032f27cSSam Leffler 5959b032f27cSSam Leffler switch (vap->iv_opmode) { 596010ad9a77SSam Leffler #ifdef ATH_SUPPORT_TDMA 596110ad9a77SSam Leffler case IEEE80211_M_AHDEMO: 596210ad9a77SSam Leffler if ((vap->iv_caps & IEEE80211_C_TDMA) == 0) 596310ad9a77SSam Leffler break; 596410ad9a77SSam Leffler /* fall thru... */ 596510ad9a77SSam Leffler #endif 5966e8fd88a3SSam Leffler case IEEE80211_M_HOSTAP: 5967e8fd88a3SSam Leffler case IEEE80211_M_IBSS: 59685591b213SSam Leffler /* 5969e8fd88a3SSam Leffler * Allocate and setup the beacon frame. 5970e8fd88a3SSam Leffler * 5971f818612bSSam Leffler * Stop any previous beacon DMA. This may be 5972f818612bSSam Leffler * necessary, for example, when an ibss merge 5973f818612bSSam Leffler * causes reconfiguration; there will be a state 5974f818612bSSam Leffler * transition from RUN->RUN that means we may 5975f818612bSSam Leffler * be called with beacon transmission active. 5976f818612bSSam Leffler */ 5977f818612bSSam Leffler ath_hal_stoptxdma(ah, sc->sc_bhalq); 5978b032f27cSSam Leffler 59795591b213SSam Leffler error = ath_beacon_alloc(sc, ni); 59805591b213SSam Leffler if (error != 0) 59815591b213SSam Leffler goto bad; 59827a04dc27SSam Leffler /* 598380d939bfSSam Leffler * If joining an adhoc network defer beacon timer 598480d939bfSSam Leffler * configuration to the next beacon frame so we 598580d939bfSSam Leffler * have a current TSF to use. Otherwise we're 5986b032f27cSSam Leffler * starting an ibss/bss so there's no need to delay; 5987b032f27cSSam Leffler * if this is the first vap moving to RUN state, then 5988b032f27cSSam Leffler * beacon state needs to be [re]configured. 59897a04dc27SSam Leffler */ 5990b032f27cSSam Leffler if (vap->iv_opmode == IEEE80211_M_IBSS && 5991b032f27cSSam Leffler ni->ni_tstamp.tsf != 0) { 599280d939bfSSam Leffler sc->sc_syncbeacon = 1; 5993b032f27cSSam Leffler } else if (!sc->sc_beacons) { 599410ad9a77SSam Leffler #ifdef ATH_SUPPORT_TDMA 599510ad9a77SSam Leffler if (vap->iv_caps & IEEE80211_C_TDMA) 599610ad9a77SSam Leffler ath_tdma_config(sc, vap); 599710ad9a77SSam Leffler else 599810ad9a77SSam Leffler #endif 5999b032f27cSSam Leffler ath_beacon_config(sc, vap); 6000b032f27cSSam Leffler sc->sc_beacons = 1; 6001b032f27cSSam Leffler } 6002e8fd88a3SSam Leffler break; 6003e8fd88a3SSam Leffler case IEEE80211_M_STA: 6004e8fd88a3SSam Leffler /* 600580d939bfSSam Leffler * Defer beacon timer configuration to the next 600680d939bfSSam Leffler * beacon frame so we have a current TSF to use 600780d939bfSSam Leffler * (any TSF collected when scanning is likely old). 60087a04dc27SSam Leffler */ 600980d939bfSSam Leffler sc->sc_syncbeacon = 1; 6010e8fd88a3SSam Leffler break; 6011b032f27cSSam Leffler case IEEE80211_M_MONITOR: 6012b032f27cSSam Leffler /* 6013b032f27cSSam Leffler * Monitor mode vaps have only INIT->RUN and RUN->RUN 6014b032f27cSSam Leffler * transitions so we must re-enable interrupts here to 6015b032f27cSSam Leffler * handle the case of a single monitor mode vap. 6016b032f27cSSam Leffler */ 6017b032f27cSSam Leffler ath_hal_intrset(ah, sc->sc_imask); 6018b032f27cSSam Leffler break; 6019b032f27cSSam Leffler case IEEE80211_M_WDS: 6020b032f27cSSam Leffler break; 6021e8fd88a3SSam Leffler default: 6022e8fd88a3SSam Leffler break; 60235591b213SSam Leffler } 60245591b213SSam Leffler /* 60257b0c77ecSSam Leffler * Let the hal process statistics collected during a 60267b0c77ecSSam Leffler * scan so it can provide calibrated noise floor data. 60277b0c77ecSSam Leffler */ 60287b0c77ecSSam Leffler ath_hal_process_noisefloor(ah); 60297b0c77ecSSam Leffler /* 6030ffa2cab6SSam Leffler * Reset rssi stats; maybe not the best place... 6031ffa2cab6SSam Leffler */ 6032ffa2cab6SSam Leffler sc->sc_halstats.ns_avgbrssi = ATH_RSSI_DUMMY_MARKER; 6033ffa2cab6SSam Leffler sc->sc_halstats.ns_avgrssi = ATH_RSSI_DUMMY_MARKER; 6034ffa2cab6SSam Leffler sc->sc_halstats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER; 603545bbf62fSSam Leffler /* 6036b032f27cSSam Leffler * Finally, start any timers and the task q thread 6037b032f27cSSam Leffler * (in case we didn't go through SCAN state). 603845bbf62fSSam Leffler */ 60392dc7fcc4SSam Leffler if (ath_longcalinterval != 0) { 6040c42a7b7eSSam Leffler /* start periodic recalibration timer */ 60412dc7fcc4SSam Leffler callout_reset(&sc->sc_cal_ch, 1, ath_calibrate, sc); 60422dc7fcc4SSam Leffler } else { 60432dc7fcc4SSam Leffler DPRINTF(sc, ATH_DEBUG_CALIBRATE, 60442dc7fcc4SSam Leffler "%s: calibration disabled\n", __func__); 6045c42a7b7eSSam Leffler } 6046b032f27cSSam Leffler taskqueue_unblock(sc->sc_tq); 6047b032f27cSSam Leffler } else if (nstate == IEEE80211_S_INIT) { 6048b032f27cSSam Leffler /* 6049b032f27cSSam Leffler * If there are no vaps left in RUN state then 6050b032f27cSSam Leffler * shutdown host/driver operation: 6051b032f27cSSam Leffler * o disable interrupts 6052b032f27cSSam Leffler * o disable the task queue thread 6053b032f27cSSam Leffler * o mark beacon processing as stopped 6054b032f27cSSam Leffler */ 6055b032f27cSSam Leffler if (!ath_isanyrunningvaps(vap)) { 6056b032f27cSSam Leffler sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS); 6057b032f27cSSam Leffler /* disable interrupts */ 6058b032f27cSSam Leffler ath_hal_intrset(ah, sc->sc_imask &~ HAL_INT_GLOBAL); 6059b032f27cSSam Leffler taskqueue_block(sc->sc_tq); 6060b032f27cSSam Leffler sc->sc_beacons = 0; 6061b032f27cSSam Leffler } 606210ad9a77SSam Leffler #ifdef ATH_SUPPORT_TDMA 606310ad9a77SSam Leffler ath_hal_setcca(ah, AH_TRUE); 606410ad9a77SSam Leffler #endif 6065b032f27cSSam Leffler } 60665591b213SSam Leffler bad: 60675591b213SSam Leffler return error; 60685591b213SSam Leffler } 60695591b213SSam Leffler 60705591b213SSam Leffler /* 6071e8fd88a3SSam Leffler * Allocate a key cache slot to the station so we can 6072e8fd88a3SSam Leffler * setup a mapping from key index to node. The key cache 6073e8fd88a3SSam Leffler * slot is needed for managing antenna state and for 6074e8fd88a3SSam Leffler * compression when stations do not use crypto. We do 6075e8fd88a3SSam Leffler * it uniliaterally here; if crypto is employed this slot 6076e8fd88a3SSam Leffler * will be reassigned. 6077e8fd88a3SSam Leffler */ 6078e8fd88a3SSam Leffler static void 6079e8fd88a3SSam Leffler ath_setup_stationkey(struct ieee80211_node *ni) 6080e8fd88a3SSam Leffler { 6081b032f27cSSam Leffler struct ieee80211vap *vap = ni->ni_vap; 6082b032f27cSSam Leffler struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; 6083c1225b52SSam Leffler ieee80211_keyix keyix, rxkeyix; 6084e8fd88a3SSam Leffler 6085b032f27cSSam Leffler if (!ath_key_alloc(vap, &ni->ni_ucastkey, &keyix, &rxkeyix)) { 6086e8fd88a3SSam Leffler /* 6087e8fd88a3SSam Leffler * Key cache is full; we'll fall back to doing 6088e8fd88a3SSam Leffler * the more expensive lookup in software. Note 6089e8fd88a3SSam Leffler * this also means no h/w compression. 6090e8fd88a3SSam Leffler */ 6091e8fd88a3SSam Leffler /* XXX msg+statistic */ 6092e8fd88a3SSam Leffler } else { 6093c1225b52SSam Leffler /* XXX locking? */ 6094e8fd88a3SSam Leffler ni->ni_ucastkey.wk_keyix = keyix; 6095c1225b52SSam Leffler ni->ni_ucastkey.wk_rxkeyix = rxkeyix; 6096d3ac945bSSam Leffler IEEE80211_ADDR_COPY(ni->ni_ucastkey.wk_macaddr, ni->ni_macaddr); 6097e8fd88a3SSam Leffler /* NB: this will create a pass-thru key entry */ 6098d3ac945bSSam Leffler ath_keyset(sc, &ni->ni_ucastkey, vap->iv_bss); 6099e8fd88a3SSam Leffler } 6100e8fd88a3SSam Leffler } 6101e8fd88a3SSam Leffler 6102e8fd88a3SSam Leffler /* 61035591b213SSam Leffler * Setup driver-specific state for a newly associated node. 61045591b213SSam Leffler * Note that we're called also on a re-associate, the isnew 61055591b213SSam Leffler * param tells us if this is the first time or not. 61065591b213SSam Leffler */ 61075591b213SSam Leffler static void 6108e9962332SSam Leffler ath_newassoc(struct ieee80211_node *ni, int isnew) 61095591b213SSam Leffler { 6110b032f27cSSam Leffler struct ath_node *an = ATH_NODE(ni); 6111b032f27cSSam Leffler struct ieee80211vap *vap = ni->ni_vap; 6112b032f27cSSam Leffler struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; 6113c62362cbSSam Leffler const struct ieee80211_txparam *tp = ni->ni_txparms; 61145591b213SSam Leffler 6115c62362cbSSam Leffler an->an_mcastrix = ath_tx_findrix(sc->sc_currates, tp->mcastrate); 6116c62362cbSSam Leffler an->an_mgmtrix = ath_tx_findrix(sc->sc_currates, tp->mgmtrate); 6117b032f27cSSam Leffler 6118b032f27cSSam Leffler ath_rate_newassoc(sc, an, isnew); 6119e8fd88a3SSam Leffler if (isnew && 6120b032f27cSSam Leffler (vap->iv_flags & IEEE80211_F_PRIVACY) == 0 && sc->sc_hasclrkey && 6121b032f27cSSam Leffler ni->ni_ucastkey.wk_keyix == IEEE80211_KEYIX_NONE) 6122e8fd88a3SSam Leffler ath_setup_stationkey(ni); 6123e8fd88a3SSam Leffler } 61245591b213SSam Leffler 61255591b213SSam Leffler static int 612659efa8b5SSam Leffler ath_setregdomain(struct ieee80211com *ic, struct ieee80211_regdomain *reg, 6127b032f27cSSam Leffler int nchans, struct ieee80211_channel chans[]) 6128b032f27cSSam Leffler { 6129b032f27cSSam Leffler struct ath_softc *sc = ic->ic_ifp->if_softc; 6130b032f27cSSam Leffler struct ath_hal *ah = sc->sc_ah; 613159efa8b5SSam Leffler HAL_STATUS status; 6132b032f27cSSam Leffler 6133033022a9SSam Leffler DPRINTF(sc, ATH_DEBUG_REGDOMAIN, 613459efa8b5SSam Leffler "%s: rd %u cc %u location %c%s\n", 613559efa8b5SSam Leffler __func__, reg->regdomain, reg->country, reg->location, 613659efa8b5SSam Leffler reg->ecm ? " ecm" : ""); 6137033022a9SSam Leffler 613859efa8b5SSam Leffler status = ath_hal_set_channels(ah, chans, nchans, 613959efa8b5SSam Leffler reg->country, reg->regdomain); 614059efa8b5SSam Leffler if (status != HAL_OK) { 614159efa8b5SSam Leffler DPRINTF(sc, ATH_DEBUG_REGDOMAIN, "%s: failed, status %u\n", 614259efa8b5SSam Leffler __func__, status); 614359efa8b5SSam Leffler return EINVAL; /* XXX */ 6144b032f27cSSam Leffler } 6145b032f27cSSam Leffler return 0; 6146b032f27cSSam Leffler } 6147b032f27cSSam Leffler 6148b032f27cSSam Leffler static void 6149b032f27cSSam Leffler ath_getradiocaps(struct ieee80211com *ic, 61505fe9f044SSam Leffler int maxchans, int *nchans, struct ieee80211_channel chans[]) 6151b032f27cSSam Leffler { 6152b032f27cSSam Leffler struct ath_softc *sc = ic->ic_ifp->if_softc; 6153b032f27cSSam Leffler struct ath_hal *ah = sc->sc_ah; 6154b032f27cSSam Leffler 615559efa8b5SSam Leffler DPRINTF(sc, ATH_DEBUG_REGDOMAIN, "%s: use rd %u cc %d\n", 615659efa8b5SSam Leffler __func__, SKU_DEBUG, CTRY_DEFAULT); 6157033022a9SSam Leffler 615859efa8b5SSam Leffler /* XXX check return */ 615959efa8b5SSam Leffler (void) ath_hal_getchannels(ah, chans, maxchans, nchans, 616059efa8b5SSam Leffler HAL_MODE_ALL, CTRY_DEFAULT, SKU_DEBUG, AH_TRUE); 6161033022a9SSam Leffler 6162b032f27cSSam Leffler } 6163b032f27cSSam Leffler 6164b032f27cSSam Leffler static int 6165b032f27cSSam Leffler ath_getchannels(struct ath_softc *sc) 6166b032f27cSSam Leffler { 6167b032f27cSSam Leffler struct ifnet *ifp = sc->sc_ifp; 6168b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 6169b032f27cSSam Leffler struct ath_hal *ah = sc->sc_ah; 617059efa8b5SSam Leffler HAL_STATUS status; 6171b032f27cSSam Leffler 6172b032f27cSSam Leffler /* 617359efa8b5SSam Leffler * Collect channel set based on EEPROM contents. 6174b032f27cSSam Leffler */ 617559efa8b5SSam Leffler status = ath_hal_init_channels(ah, ic->ic_channels, IEEE80211_CHAN_MAX, 617659efa8b5SSam Leffler &ic->ic_nchans, HAL_MODE_ALL, CTRY_DEFAULT, SKU_NONE, AH_TRUE); 617759efa8b5SSam Leffler if (status != HAL_OK) { 617859efa8b5SSam Leffler if_printf(ifp, "%s: unable to collect channel list from hal, " 617959efa8b5SSam Leffler "status %d\n", __func__, status); 618059efa8b5SSam Leffler return EINVAL; 618159efa8b5SSam Leffler } 6182ca876918SSam Leffler (void) ath_hal_getregdomain(ah, &sc->sc_eerd); 6183ca876918SSam Leffler ath_hal_getcountrycode(ah, &sc->sc_eecc); /* NB: cannot fail */ 618459efa8b5SSam Leffler /* XXX map Atheros sku's to net80211 SKU's */ 618559efa8b5SSam Leffler /* XXX net80211 types too small */ 618659efa8b5SSam Leffler ic->ic_regdomain.regdomain = (uint16_t) sc->sc_eerd; 618759efa8b5SSam Leffler ic->ic_regdomain.country = (uint16_t) sc->sc_eecc; 618859efa8b5SSam Leffler ic->ic_regdomain.isocc[0] = ' '; /* XXX don't know */ 618959efa8b5SSam Leffler ic->ic_regdomain.isocc[1] = ' '; 619059efa8b5SSam Leffler 6191b032f27cSSam Leffler ic->ic_regdomain.ecm = 1; 6192b032f27cSSam Leffler ic->ic_regdomain.location = 'I'; 6193033022a9SSam Leffler 6194033022a9SSam Leffler DPRINTF(sc, ATH_DEBUG_REGDOMAIN, 619559efa8b5SSam Leffler "%s: eeprom rd %u cc %u (mapped rd %u cc %u) location %c%s\n", 6196033022a9SSam Leffler __func__, sc->sc_eerd, sc->sc_eecc, 6197033022a9SSam Leffler ic->ic_regdomain.regdomain, ic->ic_regdomain.country, 619859efa8b5SSam Leffler ic->ic_regdomain.location, ic->ic_regdomain.ecm ? " ecm" : ""); 61995591b213SSam Leffler return 0; 62005591b213SSam Leffler } 62015591b213SSam Leffler 6202c42a7b7eSSam Leffler static void 62033e50ec2cSSam Leffler ath_led_done(void *arg) 6204c42a7b7eSSam Leffler { 62053e50ec2cSSam Leffler struct ath_softc *sc = arg; 62063e50ec2cSSam Leffler 62073e50ec2cSSam Leffler sc->sc_blinking = 0; 62083e50ec2cSSam Leffler } 6209c42a7b7eSSam Leffler 6210c42a7b7eSSam Leffler /* 62113e50ec2cSSam Leffler * Turn the LED off: flip the pin and then set a timer so no 62123e50ec2cSSam Leffler * update will happen for the specified duration. 6213c42a7b7eSSam Leffler */ 62143e50ec2cSSam Leffler static void 62153e50ec2cSSam Leffler ath_led_off(void *arg) 62163e50ec2cSSam Leffler { 62173e50ec2cSSam Leffler struct ath_softc *sc = arg; 62183e50ec2cSSam Leffler 62193e50ec2cSSam Leffler ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, !sc->sc_ledon); 62203e50ec2cSSam Leffler callout_reset(&sc->sc_ledtimer, sc->sc_ledoff, ath_led_done, sc); 6221c42a7b7eSSam Leffler } 62223e50ec2cSSam Leffler 62233e50ec2cSSam Leffler /* 62243e50ec2cSSam Leffler * Blink the LED according to the specified on/off times. 62253e50ec2cSSam Leffler */ 62263e50ec2cSSam Leffler static void 62273e50ec2cSSam Leffler ath_led_blink(struct ath_softc *sc, int on, int off) 62283e50ec2cSSam Leffler { 62293e50ec2cSSam Leffler DPRINTF(sc, ATH_DEBUG_LED, "%s: on %u off %u\n", __func__, on, off); 62303e50ec2cSSam Leffler ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, sc->sc_ledon); 62313e50ec2cSSam Leffler sc->sc_blinking = 1; 62323e50ec2cSSam Leffler sc->sc_ledoff = off; 62333e50ec2cSSam Leffler callout_reset(&sc->sc_ledtimer, on, ath_led_off, sc); 62343e50ec2cSSam Leffler } 62353e50ec2cSSam Leffler 62363e50ec2cSSam Leffler static void 623746d4d74cSSam Leffler ath_led_event(struct ath_softc *sc, int rix) 62383e50ec2cSSam Leffler { 62393e50ec2cSSam Leffler sc->sc_ledevent = ticks; /* time of last event */ 62403e50ec2cSSam Leffler if (sc->sc_blinking) /* don't interrupt active blink */ 62413e50ec2cSSam Leffler return; 624246d4d74cSSam Leffler ath_led_blink(sc, sc->sc_hwmap[rix].ledon, sc->sc_hwmap[rix].ledoff); 6243c42a7b7eSSam Leffler } 6244c42a7b7eSSam Leffler 62456c4612b9SSam Leffler static int 62466c4612b9SSam Leffler ath_rate_setup(struct ath_softc *sc, u_int mode) 62476c4612b9SSam Leffler { 62486c4612b9SSam Leffler struct ath_hal *ah = sc->sc_ah; 62496c4612b9SSam Leffler const HAL_RATE_TABLE *rt; 62506c4612b9SSam Leffler 62516c4612b9SSam Leffler switch (mode) { 62526c4612b9SSam Leffler case IEEE80211_MODE_11A: 62536c4612b9SSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_11A); 62546c4612b9SSam Leffler break; 6255724c193aSSam Leffler case IEEE80211_MODE_HALF: 6256aaa70f2fSSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_11A_HALF_RATE); 6257aaa70f2fSSam Leffler break; 6258724c193aSSam Leffler case IEEE80211_MODE_QUARTER: 6259aaa70f2fSSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_11A_QUARTER_RATE); 6260aaa70f2fSSam Leffler break; 62616c4612b9SSam Leffler case IEEE80211_MODE_11B: 62626c4612b9SSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_11B); 62636c4612b9SSam Leffler break; 62646c4612b9SSam Leffler case IEEE80211_MODE_11G: 62656c4612b9SSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_11G); 62666c4612b9SSam Leffler break; 62676c4612b9SSam Leffler case IEEE80211_MODE_TURBO_A: 626868e8e04eSSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_108A); 62690e8c5adfSSam Leffler #if HAL_ABI_VERSION < 0x07013100 62700e8c5adfSSam Leffler if (rt == NULL) /* XXX bandaid for old hal's */ 62710e8c5adfSSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_TURBO); 62720e8c5adfSSam Leffler #endif 62736c4612b9SSam Leffler break; 62746c4612b9SSam Leffler case IEEE80211_MODE_TURBO_G: 62756c4612b9SSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_108G); 62766c4612b9SSam Leffler break; 627768e8e04eSSam Leffler case IEEE80211_MODE_STURBO_A: 627868e8e04eSSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_TURBO); 627968e8e04eSSam Leffler break; 628068e8e04eSSam Leffler case IEEE80211_MODE_11NA: 628168e8e04eSSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_11NA_HT20); 628268e8e04eSSam Leffler break; 628368e8e04eSSam Leffler case IEEE80211_MODE_11NG: 628468e8e04eSSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_11NG_HT20); 628568e8e04eSSam Leffler break; 62866c4612b9SSam Leffler default: 62876c4612b9SSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid mode %u\n", 62886c4612b9SSam Leffler __func__, mode); 62896c4612b9SSam Leffler return 0; 62906c4612b9SSam Leffler } 62916c4612b9SSam Leffler sc->sc_rates[mode] = rt; 6292aaa70f2fSSam Leffler return (rt != NULL); 62935591b213SSam Leffler } 62945591b213SSam Leffler 62955591b213SSam Leffler static void 62965591b213SSam Leffler ath_setcurmode(struct ath_softc *sc, enum ieee80211_phymode mode) 62975591b213SSam Leffler { 62983e50ec2cSSam Leffler #define N(a) (sizeof(a)/sizeof(a[0])) 62993e50ec2cSSam Leffler /* NB: on/off times from the Atheros NDIS driver, w/ permission */ 63003e50ec2cSSam Leffler static const struct { 63013e50ec2cSSam Leffler u_int rate; /* tx/rx 802.11 rate */ 63023e50ec2cSSam Leffler u_int16_t timeOn; /* LED on time (ms) */ 63033e50ec2cSSam Leffler u_int16_t timeOff; /* LED off time (ms) */ 63043e50ec2cSSam Leffler } blinkrates[] = { 63053e50ec2cSSam Leffler { 108, 40, 10 }, 63063e50ec2cSSam Leffler { 96, 44, 11 }, 63073e50ec2cSSam Leffler { 72, 50, 13 }, 63083e50ec2cSSam Leffler { 48, 57, 14 }, 63093e50ec2cSSam Leffler { 36, 67, 16 }, 63103e50ec2cSSam Leffler { 24, 80, 20 }, 63113e50ec2cSSam Leffler { 22, 100, 25 }, 63123e50ec2cSSam Leffler { 18, 133, 34 }, 63133e50ec2cSSam Leffler { 12, 160, 40 }, 63143e50ec2cSSam Leffler { 10, 200, 50 }, 63153e50ec2cSSam Leffler { 6, 240, 58 }, 63163e50ec2cSSam Leffler { 4, 267, 66 }, 63173e50ec2cSSam Leffler { 2, 400, 100 }, 63183e50ec2cSSam Leffler { 0, 500, 130 }, 6319724c193aSSam Leffler /* XXX half/quarter rates */ 63203e50ec2cSSam Leffler }; 63215591b213SSam Leffler const HAL_RATE_TABLE *rt; 63223e50ec2cSSam Leffler int i, j; 63235591b213SSam Leffler 63245591b213SSam Leffler memset(sc->sc_rixmap, 0xff, sizeof(sc->sc_rixmap)); 63255591b213SSam Leffler rt = sc->sc_rates[mode]; 63265591b213SSam Leffler KASSERT(rt != NULL, ("no h/w rate set for phy mode %u", mode)); 6327180f268dSSam Leffler for (i = 0; i < rt->rateCount; i++) { 6328180f268dSSam Leffler uint8_t ieeerate = rt->info[i].dot11Rate & IEEE80211_RATE_VAL; 6329180f268dSSam Leffler if (rt->info[i].phy != IEEE80211_T_HT) 6330180f268dSSam Leffler sc->sc_rixmap[ieeerate] = i; 6331180f268dSSam Leffler else 6332180f268dSSam Leffler sc->sc_rixmap[ieeerate | IEEE80211_RATE_MCS] = i; 6333180f268dSSam Leffler } 63341b1a8e41SSam Leffler memset(sc->sc_hwmap, 0, sizeof(sc->sc_hwmap)); 633546d4d74cSSam Leffler for (i = 0; i < N(sc->sc_hwmap); i++) { 633646d4d74cSSam Leffler if (i >= rt->rateCount) { 63373e50ec2cSSam Leffler sc->sc_hwmap[i].ledon = (500 * hz) / 1000; 63383e50ec2cSSam Leffler sc->sc_hwmap[i].ledoff = (130 * hz) / 1000; 633916b4851aSSam Leffler continue; 63403e50ec2cSSam Leffler } 63413e50ec2cSSam Leffler sc->sc_hwmap[i].ieeerate = 634246d4d74cSSam Leffler rt->info[i].dot11Rate & IEEE80211_RATE_VAL; 634346d4d74cSSam Leffler if (rt->info[i].phy == IEEE80211_T_HT) 634426041a14SSam Leffler sc->sc_hwmap[i].ieeerate |= IEEE80211_RATE_MCS; 6345d3be6f5bSSam Leffler sc->sc_hwmap[i].txflags = IEEE80211_RADIOTAP_F_DATAPAD; 634646d4d74cSSam Leffler if (rt->info[i].shortPreamble || 634746d4d74cSSam Leffler rt->info[i].phy == IEEE80211_T_OFDM) 6348d3be6f5bSSam Leffler sc->sc_hwmap[i].txflags |= IEEE80211_RADIOTAP_F_SHORTPRE; 6349d3be6f5bSSam Leffler /* NB: receive frames include FCS */ 6350d3be6f5bSSam Leffler sc->sc_hwmap[i].rxflags = sc->sc_hwmap[i].txflags | 6351d3be6f5bSSam Leffler IEEE80211_RADIOTAP_F_FCS; 63523e50ec2cSSam Leffler /* setup blink rate table to avoid per-packet lookup */ 63533e50ec2cSSam Leffler for (j = 0; j < N(blinkrates)-1; j++) 63543e50ec2cSSam Leffler if (blinkrates[j].rate == sc->sc_hwmap[i].ieeerate) 63553e50ec2cSSam Leffler break; 63563e50ec2cSSam Leffler /* NB: this uses the last entry if the rate isn't found */ 63573e50ec2cSSam Leffler /* XXX beware of overlow */ 63583e50ec2cSSam Leffler sc->sc_hwmap[i].ledon = (blinkrates[j].timeOn * hz) / 1000; 63593e50ec2cSSam Leffler sc->sc_hwmap[i].ledoff = (blinkrates[j].timeOff * hz) / 1000; 6360c42a7b7eSSam Leffler } 63615591b213SSam Leffler sc->sc_currates = rt; 63625591b213SSam Leffler sc->sc_curmode = mode; 63635591b213SSam Leffler /* 6364c42a7b7eSSam Leffler * All protection frames are transmited at 2Mb/s for 6365c42a7b7eSSam Leffler * 11g, otherwise at 1Mb/s. 63665591b213SSam Leffler */ 6367913a1ba1SSam Leffler if (mode == IEEE80211_MODE_11G) 6368913a1ba1SSam Leffler sc->sc_protrix = ath_tx_findrix(rt, 2*2); 6369913a1ba1SSam Leffler else 6370913a1ba1SSam Leffler sc->sc_protrix = ath_tx_findrix(rt, 2*1); 6371c42a7b7eSSam Leffler /* NB: caller is responsible for reseting rate control state */ 63723e50ec2cSSam Leffler #undef N 63735591b213SSam Leffler } 63745591b213SSam Leffler 6375a585a9a1SSam Leffler #ifdef ATH_DEBUG 63765591b213SSam Leffler static void 63776902009eSSam Leffler ath_printrxbuf(struct ath_softc *sc, const struct ath_buf *bf, 63786902009eSSam Leffler u_int ix, int done) 63795591b213SSam Leffler { 638065f9edeeSSam Leffler const struct ath_rx_status *rs = &bf->bf_status.ds_rxstat; 63816902009eSSam Leffler struct ath_hal *ah = sc->sc_ah; 638265f9edeeSSam Leffler const struct ath_desc *ds; 63835591b213SSam Leffler int i; 63845591b213SSam Leffler 63855591b213SSam Leffler for (i = 0, ds = bf->bf_desc; i < bf->bf_nseg; i++, ds++) { 63867a4c5ed9SSam Leffler printf("R[%2u] (DS.V:%p DS.P:%p) L:%08x D:%08x%s\n" 63877a4c5ed9SSam Leffler " %08x %08x %08x %08x\n", 638865f9edeeSSam Leffler ix, ds, (const struct ath_desc *)bf->bf_daddr + i, 63895591b213SSam Leffler ds->ds_link, ds->ds_data, 639065f9edeeSSam Leffler !done ? "" : (rs->rs_status == 0) ? " *" : " !", 63915591b213SSam Leffler ds->ds_ctl0, ds->ds_ctl1, 63927a4c5ed9SSam Leffler ds->ds_hw[0], ds->ds_hw[1]); 63936902009eSSam Leffler if (ah->ah_magic == 0x20065416) { 63946902009eSSam Leffler printf(" %08x %08x %08x %08x %08x %08x %08x\n", 63956902009eSSam Leffler ds->ds_hw[2], ds->ds_hw[3], ds->ds_hw[4], 63966902009eSSam Leffler ds->ds_hw[5], ds->ds_hw[6], ds->ds_hw[7], 63976902009eSSam Leffler ds->ds_hw[8]); 63986902009eSSam Leffler } 63995591b213SSam Leffler } 64005591b213SSam Leffler } 64015591b213SSam Leffler 64025591b213SSam Leffler static void 64036902009eSSam Leffler ath_printtxbuf(struct ath_softc *sc, const struct ath_buf *bf, 64046902009eSSam Leffler u_int qnum, u_int ix, int done) 64055591b213SSam Leffler { 640665f9edeeSSam Leffler const struct ath_tx_status *ts = &bf->bf_status.ds_txstat; 64076902009eSSam Leffler struct ath_hal *ah = sc->sc_ah; 640865f9edeeSSam Leffler const struct ath_desc *ds; 64095591b213SSam Leffler int i; 64105591b213SSam Leffler 64117a4c5ed9SSam Leffler printf("Q%u[%3u]", qnum, ix); 64125591b213SSam Leffler for (i = 0, ds = bf->bf_desc; i < bf->bf_nseg; i++, ds++) { 6413ebecf802SSam Leffler printf(" (DS.V:%p DS.P:%p) L:%08x D:%08x F:04%x%s\n" 64147a4c5ed9SSam Leffler " %08x %08x %08x %08x %08x %08x\n", 641565f9edeeSSam Leffler ds, (const struct ath_desc *)bf->bf_daddr + i, 641680c07f23SSam Leffler ds->ds_link, ds->ds_data, bf->bf_txflags, 641765f9edeeSSam Leffler !done ? "" : (ts->ts_status == 0) ? " *" : " !", 64185591b213SSam Leffler ds->ds_ctl0, ds->ds_ctl1, 64197a4c5ed9SSam Leffler ds->ds_hw[0], ds->ds_hw[1], ds->ds_hw[2], ds->ds_hw[3]); 64206902009eSSam Leffler if (ah->ah_magic == 0x20065416) { 64216902009eSSam Leffler printf(" %08x %08x %08x %08x %08x %08x %08x %08x\n", 64226902009eSSam Leffler ds->ds_hw[4], ds->ds_hw[5], ds->ds_hw[6], 64236902009eSSam Leffler ds->ds_hw[7], ds->ds_hw[8], ds->ds_hw[9], 64246902009eSSam Leffler ds->ds_hw[10],ds->ds_hw[11]); 64256902009eSSam Leffler printf(" %08x %08x %08x %08x %08x %08x %08x %08x\n", 64266902009eSSam Leffler ds->ds_hw[12],ds->ds_hw[13],ds->ds_hw[14], 64276902009eSSam Leffler ds->ds_hw[15],ds->ds_hw[16],ds->ds_hw[17], 64286902009eSSam Leffler ds->ds_hw[18], ds->ds_hw[19]); 64296902009eSSam Leffler } 64305591b213SSam Leffler } 64315591b213SSam Leffler } 6432a585a9a1SSam Leffler #endif /* ATH_DEBUG */ 6433c42a7b7eSSam Leffler 6434c42a7b7eSSam Leffler static void 6435c42a7b7eSSam Leffler ath_watchdog(struct ifnet *ifp) 6436c42a7b7eSSam Leffler { 6437c42a7b7eSSam Leffler struct ath_softc *sc = ifp->if_softc; 6438c42a7b7eSSam Leffler 643968e8e04eSSam Leffler if ((ifp->if_drv_flags & IFF_DRV_RUNNING) && !sc->sc_invalid) { 6440459bc4f0SSam Leffler uint32_t hangs; 6441459bc4f0SSam Leffler 6442459bc4f0SSam Leffler if (ath_hal_gethangstate(sc->sc_ah, 0xffff, &hangs) && 6443459bc4f0SSam Leffler hangs != 0) { 6444459bc4f0SSam Leffler if_printf(ifp, "%s hang detected (0x%x)\n", 6445459bc4f0SSam Leffler hangs & 0xff ? "bb" : "mac", hangs); 6446459bc4f0SSam Leffler } else 6447c42a7b7eSSam Leffler if_printf(ifp, "device timeout\n"); 6448c42a7b7eSSam Leffler ath_reset(ifp); 6449c42a7b7eSSam Leffler ifp->if_oerrors++; 6450c42a7b7eSSam Leffler sc->sc_stats.ast_watchdog++; 6451c42a7b7eSSam Leffler } 6452c42a7b7eSSam Leffler } 6453c42a7b7eSSam Leffler 6454a585a9a1SSam Leffler #ifdef ATH_DIAGAPI 6455c42a7b7eSSam Leffler /* 6456c42a7b7eSSam Leffler * Diagnostic interface to the HAL. This is used by various 6457c42a7b7eSSam Leffler * tools to do things like retrieve register contents for 6458c42a7b7eSSam Leffler * debugging. The mechanism is intentionally opaque so that 6459c42a7b7eSSam Leffler * it can change frequently w/o concern for compatiblity. 6460c42a7b7eSSam Leffler */ 6461c42a7b7eSSam Leffler static int 6462c42a7b7eSSam Leffler ath_ioctl_diag(struct ath_softc *sc, struct ath_diag *ad) 6463c42a7b7eSSam Leffler { 6464c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 6465c42a7b7eSSam Leffler u_int id = ad->ad_id & ATH_DIAG_ID; 6466c42a7b7eSSam Leffler void *indata = NULL; 6467c42a7b7eSSam Leffler void *outdata = NULL; 6468c42a7b7eSSam Leffler u_int32_t insize = ad->ad_in_size; 6469c42a7b7eSSam Leffler u_int32_t outsize = ad->ad_out_size; 6470c42a7b7eSSam Leffler int error = 0; 6471c42a7b7eSSam Leffler 6472c42a7b7eSSam Leffler if (ad->ad_id & ATH_DIAG_IN) { 6473c42a7b7eSSam Leffler /* 6474c42a7b7eSSam Leffler * Copy in data. 6475c42a7b7eSSam Leffler */ 6476c42a7b7eSSam Leffler indata = malloc(insize, M_TEMP, M_NOWAIT); 6477c42a7b7eSSam Leffler if (indata == NULL) { 6478c42a7b7eSSam Leffler error = ENOMEM; 6479c42a7b7eSSam Leffler goto bad; 6480c42a7b7eSSam Leffler } 6481c42a7b7eSSam Leffler error = copyin(ad->ad_in_data, indata, insize); 6482c42a7b7eSSam Leffler if (error) 6483c42a7b7eSSam Leffler goto bad; 6484c42a7b7eSSam Leffler } 6485c42a7b7eSSam Leffler if (ad->ad_id & ATH_DIAG_DYN) { 6486c42a7b7eSSam Leffler /* 6487c42a7b7eSSam Leffler * Allocate a buffer for the results (otherwise the HAL 6488c42a7b7eSSam Leffler * returns a pointer to a buffer where we can read the 6489c42a7b7eSSam Leffler * results). Note that we depend on the HAL leaving this 6490c42a7b7eSSam Leffler * pointer for us to use below in reclaiming the buffer; 6491c42a7b7eSSam Leffler * may want to be more defensive. 6492c42a7b7eSSam Leffler */ 6493c42a7b7eSSam Leffler outdata = malloc(outsize, M_TEMP, M_NOWAIT); 6494c42a7b7eSSam Leffler if (outdata == NULL) { 6495c42a7b7eSSam Leffler error = ENOMEM; 6496c42a7b7eSSam Leffler goto bad; 6497c42a7b7eSSam Leffler } 6498c42a7b7eSSam Leffler } 6499c42a7b7eSSam Leffler if (ath_hal_getdiagstate(ah, id, indata, insize, &outdata, &outsize)) { 6500c42a7b7eSSam Leffler if (outsize < ad->ad_out_size) 6501c42a7b7eSSam Leffler ad->ad_out_size = outsize; 6502c42a7b7eSSam Leffler if (outdata != NULL) 6503c42a7b7eSSam Leffler error = copyout(outdata, ad->ad_out_data, 6504c42a7b7eSSam Leffler ad->ad_out_size); 6505c42a7b7eSSam Leffler } else { 6506c42a7b7eSSam Leffler error = EINVAL; 6507c42a7b7eSSam Leffler } 6508c42a7b7eSSam Leffler bad: 6509c42a7b7eSSam Leffler if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL) 6510c42a7b7eSSam Leffler free(indata, M_TEMP); 6511c42a7b7eSSam Leffler if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL) 6512c42a7b7eSSam Leffler free(outdata, M_TEMP); 6513c42a7b7eSSam Leffler return error; 6514c42a7b7eSSam Leffler } 6515a585a9a1SSam Leffler #endif /* ATH_DIAGAPI */ 6516c42a7b7eSSam Leffler 6517c42a7b7eSSam Leffler static int 6518c42a7b7eSSam Leffler ath_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 6519c42a7b7eSSam Leffler { 6520c42a7b7eSSam Leffler #define IS_RUNNING(ifp) \ 652113f4c340SRobert Watson ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING)) 6522c42a7b7eSSam Leffler struct ath_softc *sc = ifp->if_softc; 6523b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 6524c42a7b7eSSam Leffler struct ifreq *ifr = (struct ifreq *)data; 652584784be1SSam Leffler const HAL_RATE_TABLE *rt; 6526c42a7b7eSSam Leffler int error = 0; 6527c42a7b7eSSam Leffler 6528c42a7b7eSSam Leffler switch (cmd) { 6529c42a7b7eSSam Leffler case SIOCSIFFLAGS: 653031a8c1edSAndrew Thompson ATH_LOCK(sc); 6531c42a7b7eSSam Leffler if (IS_RUNNING(ifp)) { 6532c42a7b7eSSam Leffler /* 6533c42a7b7eSSam Leffler * To avoid rescanning another access point, 6534c42a7b7eSSam Leffler * do not call ath_init() here. Instead, 6535c42a7b7eSSam Leffler * only reflect promisc mode settings. 6536c42a7b7eSSam Leffler */ 6537c42a7b7eSSam Leffler ath_mode_init(sc); 6538c42a7b7eSSam Leffler } else if (ifp->if_flags & IFF_UP) { 6539c42a7b7eSSam Leffler /* 6540c42a7b7eSSam Leffler * Beware of being called during attach/detach 6541c42a7b7eSSam Leffler * to reset promiscuous mode. In that case we 6542c42a7b7eSSam Leffler * will still be marked UP but not RUNNING. 6543c42a7b7eSSam Leffler * However trying to re-init the interface 6544c42a7b7eSSam Leffler * is the wrong thing to do as we've already 6545c42a7b7eSSam Leffler * torn down much of our state. There's 6546c42a7b7eSSam Leffler * probably a better way to deal with this. 6547c42a7b7eSSam Leffler */ 6548b032f27cSSam Leffler if (!sc->sc_invalid) 6549fc74a9f9SBrooks Davis ath_init(sc); /* XXX lose error */ 6550d3ac945bSSam Leffler } else { 6551c42a7b7eSSam Leffler ath_stop_locked(ifp); 6552d3ac945bSSam Leffler #ifdef notyet 6553d3ac945bSSam Leffler /* XXX must wakeup in places like ath_vap_delete */ 6554d3ac945bSSam Leffler if (!sc->sc_invalid) 6555d3ac945bSSam Leffler ath_hal_setpower(sc->sc_ah, HAL_PM_FULL_SLEEP); 6556d3ac945bSSam Leffler #endif 6557d3ac945bSSam Leffler } 655831a8c1edSAndrew Thompson ATH_UNLOCK(sc); 6559c42a7b7eSSam Leffler break; 6560b032f27cSSam Leffler case SIOCGIFMEDIA: 6561b032f27cSSam Leffler case SIOCSIFMEDIA: 6562b032f27cSSam Leffler error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); 6563b032f27cSSam Leffler break; 6564c42a7b7eSSam Leffler case SIOCGATHSTATS: 6565c42a7b7eSSam Leffler /* NB: embed these numbers to get a consistent view */ 6566c42a7b7eSSam Leffler sc->sc_stats.ast_tx_packets = ifp->if_opackets; 6567c42a7b7eSSam Leffler sc->sc_stats.ast_rx_packets = ifp->if_ipackets; 656884784be1SSam Leffler sc->sc_stats.ast_tx_rssi = ATH_RSSI(sc->sc_halstats.ns_avgtxrssi); 656984784be1SSam Leffler sc->sc_stats.ast_rx_rssi = ATH_RSSI(sc->sc_halstats.ns_avgrssi); 657010ad9a77SSam Leffler #ifdef ATH_SUPPORT_TDMA 657110ad9a77SSam Leffler sc->sc_stats.ast_tdma_tsfadjp = TDMA_AVG(sc->sc_avgtsfdeltap); 657210ad9a77SSam Leffler sc->sc_stats.ast_tdma_tsfadjm = TDMA_AVG(sc->sc_avgtsfdeltam); 657310ad9a77SSam Leffler #endif 657484784be1SSam Leffler rt = sc->sc_currates; 657546d4d74cSSam Leffler /* XXX HT rates */ 657646d4d74cSSam Leffler sc->sc_stats.ast_tx_rate = 657746d4d74cSSam Leffler rt->info[sc->sc_txrix].dot11Rate &~ IEEE80211_RATE_BASIC; 6578c42a7b7eSSam Leffler return copyout(&sc->sc_stats, 6579c42a7b7eSSam Leffler ifr->ifr_data, sizeof (sc->sc_stats)); 6580a585a9a1SSam Leffler #ifdef ATH_DIAGAPI 6581c42a7b7eSSam Leffler case SIOCGATHDIAG: 6582c42a7b7eSSam Leffler error = ath_ioctl_diag(sc, (struct ath_diag *) ifr); 6583c42a7b7eSSam Leffler break; 6584a585a9a1SSam Leffler #endif 658531a8c1edSAndrew Thompson case SIOCGIFADDR: 6586b032f27cSSam Leffler error = ether_ioctl(ifp, cmd, data); 6587c42a7b7eSSam Leffler break; 658831a8c1edSAndrew Thompson default: 658931a8c1edSAndrew Thompson error = EINVAL; 659031a8c1edSAndrew Thompson break; 6591c42a7b7eSSam Leffler } 6592c42a7b7eSSam Leffler return error; 6593a614e076SSam Leffler #undef IS_RUNNING 6594c42a7b7eSSam Leffler } 6595c42a7b7eSSam Leffler 6596c42a7b7eSSam Leffler static int 6597c42a7b7eSSam Leffler ath_sysctl_slottime(SYSCTL_HANDLER_ARGS) 6598c42a7b7eSSam Leffler { 6599c42a7b7eSSam Leffler struct ath_softc *sc = arg1; 6600c42a7b7eSSam Leffler u_int slottime = ath_hal_getslottime(sc->sc_ah); 6601c42a7b7eSSam Leffler int error; 6602c42a7b7eSSam Leffler 6603c42a7b7eSSam Leffler error = sysctl_handle_int(oidp, &slottime, 0, req); 6604c42a7b7eSSam Leffler if (error || !req->newptr) 6605c42a7b7eSSam Leffler return error; 6606c42a7b7eSSam Leffler return !ath_hal_setslottime(sc->sc_ah, slottime) ? EINVAL : 0; 6607c42a7b7eSSam Leffler } 6608c42a7b7eSSam Leffler 6609c42a7b7eSSam Leffler static int 6610c42a7b7eSSam Leffler ath_sysctl_acktimeout(SYSCTL_HANDLER_ARGS) 6611c42a7b7eSSam Leffler { 6612c42a7b7eSSam Leffler struct ath_softc *sc = arg1; 6613c42a7b7eSSam Leffler u_int acktimeout = ath_hal_getacktimeout(sc->sc_ah); 6614c42a7b7eSSam Leffler int error; 6615c42a7b7eSSam Leffler 6616c42a7b7eSSam Leffler error = sysctl_handle_int(oidp, &acktimeout, 0, req); 6617c42a7b7eSSam Leffler if (error || !req->newptr) 6618c42a7b7eSSam Leffler return error; 6619c42a7b7eSSam Leffler return !ath_hal_setacktimeout(sc->sc_ah, acktimeout) ? EINVAL : 0; 6620c42a7b7eSSam Leffler } 6621c42a7b7eSSam Leffler 6622c42a7b7eSSam Leffler static int 6623c42a7b7eSSam Leffler ath_sysctl_ctstimeout(SYSCTL_HANDLER_ARGS) 6624c42a7b7eSSam Leffler { 6625c42a7b7eSSam Leffler struct ath_softc *sc = arg1; 6626c42a7b7eSSam Leffler u_int ctstimeout = ath_hal_getctstimeout(sc->sc_ah); 6627c42a7b7eSSam Leffler int error; 6628c42a7b7eSSam Leffler 6629c42a7b7eSSam Leffler error = sysctl_handle_int(oidp, &ctstimeout, 0, req); 6630c42a7b7eSSam Leffler if (error || !req->newptr) 6631c42a7b7eSSam Leffler return error; 6632c42a7b7eSSam Leffler return !ath_hal_setctstimeout(sc->sc_ah, ctstimeout) ? EINVAL : 0; 6633c42a7b7eSSam Leffler } 6634c42a7b7eSSam Leffler 6635c42a7b7eSSam Leffler static int 6636c42a7b7eSSam Leffler ath_sysctl_softled(SYSCTL_HANDLER_ARGS) 6637c42a7b7eSSam Leffler { 6638c42a7b7eSSam Leffler struct ath_softc *sc = arg1; 6639c42a7b7eSSam Leffler int softled = sc->sc_softled; 6640c42a7b7eSSam Leffler int error; 6641c42a7b7eSSam Leffler 6642c42a7b7eSSam Leffler error = sysctl_handle_int(oidp, &softled, 0, req); 6643c42a7b7eSSam Leffler if (error || !req->newptr) 6644c42a7b7eSSam Leffler return error; 66453e50ec2cSSam Leffler softled = (softled != 0); 6646c42a7b7eSSam Leffler if (softled != sc->sc_softled) { 66473e50ec2cSSam Leffler if (softled) { 66483e50ec2cSSam Leffler /* NB: handle any sc_ledpin change */ 6649c42a7b7eSSam Leffler ath_hal_gpioCfgOutput(sc->sc_ah, sc->sc_ledpin); 66503e50ec2cSSam Leffler ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, 66513e50ec2cSSam Leffler !sc->sc_ledon); 66523e50ec2cSSam Leffler } 6653c42a7b7eSSam Leffler sc->sc_softled = softled; 6654c42a7b7eSSam Leffler } 6655c42a7b7eSSam Leffler return 0; 6656c42a7b7eSSam Leffler } 6657c42a7b7eSSam Leffler 6658c42a7b7eSSam Leffler static int 6659b298baf2SSam Leffler ath_sysctl_ledpin(SYSCTL_HANDLER_ARGS) 6660b298baf2SSam Leffler { 6661b298baf2SSam Leffler struct ath_softc *sc = arg1; 6662b298baf2SSam Leffler int ledpin = sc->sc_ledpin; 6663b298baf2SSam Leffler int error; 6664b298baf2SSam Leffler 6665b298baf2SSam Leffler error = sysctl_handle_int(oidp, &ledpin, 0, req); 6666b298baf2SSam Leffler if (error || !req->newptr) 6667b298baf2SSam Leffler return error; 6668b298baf2SSam Leffler if (ledpin != sc->sc_ledpin) { 6669b298baf2SSam Leffler sc->sc_ledpin = ledpin; 6670b298baf2SSam Leffler if (sc->sc_softled) { 6671b298baf2SSam Leffler ath_hal_gpioCfgOutput(sc->sc_ah, sc->sc_ledpin); 6672b298baf2SSam Leffler ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, 6673b298baf2SSam Leffler !sc->sc_ledon); 6674b298baf2SSam Leffler } 6675b298baf2SSam Leffler } 6676b298baf2SSam Leffler return 0; 6677b298baf2SSam Leffler } 6678b298baf2SSam Leffler 6679b298baf2SSam Leffler static int 66808debcae4SSam Leffler ath_sysctl_txantenna(SYSCTL_HANDLER_ARGS) 66818debcae4SSam Leffler { 66828debcae4SSam Leffler struct ath_softc *sc = arg1; 66838debcae4SSam Leffler u_int txantenna = ath_hal_getantennaswitch(sc->sc_ah); 66848debcae4SSam Leffler int error; 66858debcae4SSam Leffler 66868debcae4SSam Leffler error = sysctl_handle_int(oidp, &txantenna, 0, req); 66878debcae4SSam Leffler if (!error && req->newptr) { 66888debcae4SSam Leffler /* XXX assumes 2 antenna ports */ 66898debcae4SSam Leffler if (txantenna < HAL_ANT_VARIABLE || txantenna > HAL_ANT_FIXED_B) 66908debcae4SSam Leffler return EINVAL; 66918debcae4SSam Leffler ath_hal_setantennaswitch(sc->sc_ah, txantenna); 66928debcae4SSam Leffler /* 66938debcae4SSam Leffler * NB: with the switch locked this isn't meaningful, 66948debcae4SSam Leffler * but set it anyway so things like radiotap get 66958debcae4SSam Leffler * consistent info in their data. 66968debcae4SSam Leffler */ 66978debcae4SSam Leffler sc->sc_txantenna = txantenna; 66988debcae4SSam Leffler } 66998debcae4SSam Leffler return error; 67008debcae4SSam Leffler } 67018debcae4SSam Leffler 67028debcae4SSam Leffler static int 6703c42a7b7eSSam Leffler ath_sysctl_rxantenna(SYSCTL_HANDLER_ARGS) 6704c42a7b7eSSam Leffler { 6705c42a7b7eSSam Leffler struct ath_softc *sc = arg1; 6706c42a7b7eSSam Leffler u_int defantenna = ath_hal_getdefantenna(sc->sc_ah); 6707c42a7b7eSSam Leffler int error; 6708c42a7b7eSSam Leffler 6709c42a7b7eSSam Leffler error = sysctl_handle_int(oidp, &defantenna, 0, req); 6710c42a7b7eSSam Leffler if (!error && req->newptr) 6711c42a7b7eSSam Leffler ath_hal_setdefantenna(sc->sc_ah, defantenna); 6712c42a7b7eSSam Leffler return error; 6713c42a7b7eSSam Leffler } 6714c42a7b7eSSam Leffler 6715c42a7b7eSSam Leffler static int 6716c42a7b7eSSam Leffler ath_sysctl_diversity(SYSCTL_HANDLER_ARGS) 6717c42a7b7eSSam Leffler { 6718c42a7b7eSSam Leffler struct ath_softc *sc = arg1; 6719c59005e9SSam Leffler u_int diversity = ath_hal_getdiversity(sc->sc_ah); 6720c42a7b7eSSam Leffler int error; 6721c42a7b7eSSam Leffler 6722c42a7b7eSSam Leffler error = sysctl_handle_int(oidp, &diversity, 0, req); 6723c42a7b7eSSam Leffler if (error || !req->newptr) 6724c42a7b7eSSam Leffler return error; 6725c59005e9SSam Leffler if (!ath_hal_setdiversity(sc->sc_ah, diversity)) 6726c59005e9SSam Leffler return EINVAL; 6727c42a7b7eSSam Leffler sc->sc_diversity = diversity; 6728c59005e9SSam Leffler return 0; 6729c42a7b7eSSam Leffler } 6730c42a7b7eSSam Leffler 6731c42a7b7eSSam Leffler static int 6732c42a7b7eSSam Leffler ath_sysctl_diag(SYSCTL_HANDLER_ARGS) 6733c42a7b7eSSam Leffler { 6734c42a7b7eSSam Leffler struct ath_softc *sc = arg1; 6735c42a7b7eSSam Leffler u_int32_t diag; 6736c42a7b7eSSam Leffler int error; 6737c42a7b7eSSam Leffler 6738c42a7b7eSSam Leffler if (!ath_hal_getdiag(sc->sc_ah, &diag)) 6739c42a7b7eSSam Leffler return EINVAL; 6740c42a7b7eSSam Leffler error = sysctl_handle_int(oidp, &diag, 0, req); 6741c42a7b7eSSam Leffler if (error || !req->newptr) 6742c42a7b7eSSam Leffler return error; 6743c42a7b7eSSam Leffler return !ath_hal_setdiag(sc->sc_ah, diag) ? EINVAL : 0; 6744c42a7b7eSSam Leffler } 6745c42a7b7eSSam Leffler 6746c42a7b7eSSam Leffler static int 6747c42a7b7eSSam Leffler ath_sysctl_tpscale(SYSCTL_HANDLER_ARGS) 6748c42a7b7eSSam Leffler { 6749c42a7b7eSSam Leffler struct ath_softc *sc = arg1; 6750fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 6751c42a7b7eSSam Leffler u_int32_t scale; 6752c42a7b7eSSam Leffler int error; 6753c42a7b7eSSam Leffler 6754ee7d6840SSam Leffler (void) ath_hal_gettpscale(sc->sc_ah, &scale); 6755c42a7b7eSSam Leffler error = sysctl_handle_int(oidp, &scale, 0, req); 6756c42a7b7eSSam Leffler if (error || !req->newptr) 6757c42a7b7eSSam Leffler return error; 675816d84e01SSam Leffler return !ath_hal_settpscale(sc->sc_ah, scale) ? EINVAL : 675916d84e01SSam Leffler (ifp->if_drv_flags & IFF_DRV_RUNNING) ? ath_reset(ifp) : 0; 6760c42a7b7eSSam Leffler } 6761c42a7b7eSSam Leffler 6762c42a7b7eSSam Leffler static int 6763c42a7b7eSSam Leffler ath_sysctl_tpc(SYSCTL_HANDLER_ARGS) 6764c42a7b7eSSam Leffler { 6765c42a7b7eSSam Leffler struct ath_softc *sc = arg1; 6766c42a7b7eSSam Leffler u_int tpc = ath_hal_gettpc(sc->sc_ah); 6767c42a7b7eSSam Leffler int error; 6768c42a7b7eSSam Leffler 6769c42a7b7eSSam Leffler error = sysctl_handle_int(oidp, &tpc, 0, req); 6770c42a7b7eSSam Leffler if (error || !req->newptr) 6771c42a7b7eSSam Leffler return error; 6772c42a7b7eSSam Leffler return !ath_hal_settpc(sc->sc_ah, tpc) ? EINVAL : 0; 6773c42a7b7eSSam Leffler } 6774c42a7b7eSSam Leffler 677517f3f177SSam Leffler static int 6776bd5a9920SSam Leffler ath_sysctl_rfkill(SYSCTL_HANDLER_ARGS) 6777bd5a9920SSam Leffler { 6778bd5a9920SSam Leffler struct ath_softc *sc = arg1; 677916d84e01SSam Leffler struct ifnet *ifp = sc->sc_ifp; 6780bd5a9920SSam Leffler struct ath_hal *ah = sc->sc_ah; 6781bd5a9920SSam Leffler u_int rfkill = ath_hal_getrfkill(ah); 6782bd5a9920SSam Leffler int error; 6783bd5a9920SSam Leffler 6784bd5a9920SSam Leffler error = sysctl_handle_int(oidp, &rfkill, 0, req); 6785bd5a9920SSam Leffler if (error || !req->newptr) 6786bd5a9920SSam Leffler return error; 6787bd5a9920SSam Leffler if (rfkill == ath_hal_getrfkill(ah)) /* unchanged */ 6788bd5a9920SSam Leffler return 0; 678916d84e01SSam Leffler if (!ath_hal_setrfkill(ah, rfkill)) 6790bd5a9920SSam Leffler return EINVAL; 679116d84e01SSam Leffler return (ifp->if_drv_flags & IFF_DRV_RUNNING) ? ath_reset(ifp) : 0; 6792bd5a9920SSam Leffler } 6793bd5a9920SSam Leffler 6794bd5a9920SSam Leffler static int 6795bd5a9920SSam Leffler ath_sysctl_rfsilent(SYSCTL_HANDLER_ARGS) 6796bd5a9920SSam Leffler { 6797bd5a9920SSam Leffler struct ath_softc *sc = arg1; 6798bd5a9920SSam Leffler u_int rfsilent; 6799bd5a9920SSam Leffler int error; 6800bd5a9920SSam Leffler 6801ee7d6840SSam Leffler (void) ath_hal_getrfsilent(sc->sc_ah, &rfsilent); 6802bd5a9920SSam Leffler error = sysctl_handle_int(oidp, &rfsilent, 0, req); 6803bd5a9920SSam Leffler if (error || !req->newptr) 6804bd5a9920SSam Leffler return error; 6805bd5a9920SSam Leffler if (!ath_hal_setrfsilent(sc->sc_ah, rfsilent)) 6806bd5a9920SSam Leffler return EINVAL; 6807bd5a9920SSam Leffler sc->sc_rfsilentpin = rfsilent & 0x1c; 6808bd5a9920SSam Leffler sc->sc_rfsilentpol = (rfsilent & 0x2) != 0; 6809bd5a9920SSam Leffler return 0; 6810bd5a9920SSam Leffler } 6811bd5a9920SSam Leffler 6812bd5a9920SSam Leffler static int 6813bd5a9920SSam Leffler ath_sysctl_tpack(SYSCTL_HANDLER_ARGS) 6814bd5a9920SSam Leffler { 6815bd5a9920SSam Leffler struct ath_softc *sc = arg1; 6816bd5a9920SSam Leffler u_int32_t tpack; 6817bd5a9920SSam Leffler int error; 6818bd5a9920SSam Leffler 6819ee7d6840SSam Leffler (void) ath_hal_gettpack(sc->sc_ah, &tpack); 6820bd5a9920SSam Leffler error = sysctl_handle_int(oidp, &tpack, 0, req); 6821bd5a9920SSam Leffler if (error || !req->newptr) 6822bd5a9920SSam Leffler return error; 6823bd5a9920SSam Leffler return !ath_hal_settpack(sc->sc_ah, tpack) ? EINVAL : 0; 6824bd5a9920SSam Leffler } 6825bd5a9920SSam Leffler 6826bd5a9920SSam Leffler static int 6827bd5a9920SSam Leffler ath_sysctl_tpcts(SYSCTL_HANDLER_ARGS) 6828bd5a9920SSam Leffler { 6829bd5a9920SSam Leffler struct ath_softc *sc = arg1; 6830bd5a9920SSam Leffler u_int32_t tpcts; 6831bd5a9920SSam Leffler int error; 6832bd5a9920SSam Leffler 6833ee7d6840SSam Leffler (void) ath_hal_gettpcts(sc->sc_ah, &tpcts); 6834bd5a9920SSam Leffler error = sysctl_handle_int(oidp, &tpcts, 0, req); 6835bd5a9920SSam Leffler if (error || !req->newptr) 6836bd5a9920SSam Leffler return error; 6837bd5a9920SSam Leffler return !ath_hal_settpcts(sc->sc_ah, tpcts) ? EINVAL : 0; 6838bd5a9920SSam Leffler } 6839bd5a9920SSam Leffler 6840577eaa60SSam Leffler static int 6841577eaa60SSam Leffler ath_sysctl_intmit(SYSCTL_HANDLER_ARGS) 6842577eaa60SSam Leffler { 6843577eaa60SSam Leffler struct ath_softc *sc = arg1; 6844577eaa60SSam Leffler int intmit, error; 6845577eaa60SSam Leffler 6846577eaa60SSam Leffler intmit = ath_hal_getintmit(sc->sc_ah); 6847577eaa60SSam Leffler error = sysctl_handle_int(oidp, &intmit, 0, req); 6848577eaa60SSam Leffler if (error || !req->newptr) 6849577eaa60SSam Leffler return error; 6850577eaa60SSam Leffler return !ath_hal_setintmit(sc->sc_ah, intmit) ? EINVAL : 0; 6851577eaa60SSam Leffler } 6852577eaa60SSam Leffler 6853c42a7b7eSSam Leffler static void 6854c42a7b7eSSam Leffler ath_sysctlattach(struct ath_softc *sc) 6855c42a7b7eSSam Leffler { 6856c42a7b7eSSam Leffler struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); 6857c42a7b7eSSam Leffler struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); 6858c59005e9SSam Leffler struct ath_hal *ah = sc->sc_ah; 6859c42a7b7eSSam Leffler 6860ca876918SSam Leffler SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6861ca876918SSam Leffler "countrycode", CTLFLAG_RD, &sc->sc_eecc, 0, 6862ca876918SSam Leffler "EEPROM country code"); 6863ca876918SSam Leffler SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6864ca876918SSam Leffler "regdomain", CTLFLAG_RD, &sc->sc_eerd, 0, 6865ca876918SSam Leffler "EEPROM regdomain code"); 6866a585a9a1SSam Leffler #ifdef ATH_DEBUG 6867c42a7b7eSSam Leffler SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6868c42a7b7eSSam Leffler "debug", CTLFLAG_RW, &sc->sc_debug, 0, 6869c42a7b7eSSam Leffler "control debugging printfs"); 6870d2f6ed15SSam Leffler #endif 6871c42a7b7eSSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6872c42a7b7eSSam Leffler "slottime", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 6873c42a7b7eSSam Leffler ath_sysctl_slottime, "I", "802.11 slot time (us)"); 6874c42a7b7eSSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6875c42a7b7eSSam Leffler "acktimeout", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 6876c42a7b7eSSam Leffler ath_sysctl_acktimeout, "I", "802.11 ACK timeout (us)"); 6877c42a7b7eSSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6878c42a7b7eSSam Leffler "ctstimeout", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 6879c42a7b7eSSam Leffler ath_sysctl_ctstimeout, "I", "802.11 CTS timeout (us)"); 6880c42a7b7eSSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6881c42a7b7eSSam Leffler "softled", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 6882c42a7b7eSSam Leffler ath_sysctl_softled, "I", "enable/disable software LED support"); 6883b298baf2SSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6884b298baf2SSam Leffler "ledpin", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 6885b298baf2SSam Leffler ath_sysctl_ledpin, "I", "GPIO pin connected to LED"); 6886c42a7b7eSSam Leffler SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 68873e50ec2cSSam Leffler "ledon", CTLFLAG_RW, &sc->sc_ledon, 0, 68883e50ec2cSSam Leffler "setting to turn LED on"); 68893e50ec2cSSam Leffler SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 68903e50ec2cSSam Leffler "ledidle", CTLFLAG_RW, &sc->sc_ledidle, 0, 68913e50ec2cSSam Leffler "idle time for inactivity LED (ticks)"); 68928debcae4SSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 68938debcae4SSam Leffler "txantenna", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 68948debcae4SSam Leffler ath_sysctl_txantenna, "I", "antenna switch"); 6895c42a7b7eSSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6896c42a7b7eSSam Leffler "rxantenna", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 6897c42a7b7eSSam Leffler ath_sysctl_rxantenna, "I", "default/rx antenna"); 6898c59005e9SSam Leffler if (ath_hal_hasdiversity(ah)) 6899c42a7b7eSSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6900c42a7b7eSSam Leffler "diversity", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 6901c42a7b7eSSam Leffler ath_sysctl_diversity, "I", "antenna diversity"); 6902c42a7b7eSSam Leffler sc->sc_txintrperiod = ATH_TXINTR_PERIOD; 6903c42a7b7eSSam Leffler SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6904c42a7b7eSSam Leffler "txintrperiod", CTLFLAG_RW, &sc->sc_txintrperiod, 0, 6905c42a7b7eSSam Leffler "tx descriptor batching"); 6906c42a7b7eSSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6907c42a7b7eSSam Leffler "diag", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 6908c42a7b7eSSam Leffler ath_sysctl_diag, "I", "h/w diagnostic control"); 6909c42a7b7eSSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6910c42a7b7eSSam Leffler "tpscale", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 6911c42a7b7eSSam Leffler ath_sysctl_tpscale, "I", "tx power scaling"); 6912bd5a9920SSam Leffler if (ath_hal_hastpc(ah)) { 6913c42a7b7eSSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6914c42a7b7eSSam Leffler "tpc", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 6915c42a7b7eSSam Leffler ath_sysctl_tpc, "I", "enable/disable per-packet TPC"); 6916bd5a9920SSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6917bd5a9920SSam Leffler "tpack", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 6918bd5a9920SSam Leffler ath_sysctl_tpack, "I", "tx power for ack frames"); 6919bd5a9920SSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6920bd5a9920SSam Leffler "tpcts", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 6921bd5a9920SSam Leffler ath_sysctl_tpcts, "I", "tx power for cts frames"); 6922bd5a9920SSam Leffler } 692368e8e04eSSam Leffler if (ath_hal_hasfastframes(sc->sc_ah)) { 692468e8e04eSSam Leffler sc->sc_fftxqmin = ATH_FF_TXQMIN; 692568e8e04eSSam Leffler SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 692668e8e04eSSam Leffler "fftxqmin", CTLFLAG_RW, &sc->sc_fftxqmin, 0, 692768e8e04eSSam Leffler "min frames before fast-frame staging"); 692868e8e04eSSam Leffler sc->sc_fftxqmax = ATH_FF_TXQMAX; 692968e8e04eSSam Leffler SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 693068e8e04eSSam Leffler "fftxqmax", CTLFLAG_RW, &sc->sc_fftxqmax, 0, 693168e8e04eSSam Leffler "max queued frames before tail drop"); 693268e8e04eSSam Leffler } 6933bd5a9920SSam Leffler if (ath_hal_hasrfsilent(ah)) { 6934bd5a9920SSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6935bd5a9920SSam Leffler "rfsilent", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 6936bd5a9920SSam Leffler ath_sysctl_rfsilent, "I", "h/w RF silent config"); 6937bd5a9920SSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6938bd5a9920SSam Leffler "rfkill", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 6939bd5a9920SSam Leffler ath_sysctl_rfkill, "I", "enable/disable RF kill switch"); 6940bd5a9920SSam Leffler } 6941577eaa60SSam Leffler if (ath_hal_hasintmit(ah)) { 6942577eaa60SSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6943577eaa60SSam Leffler "intmit", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 6944577eaa60SSam Leffler ath_sysctl_intmit, "I", "interference mitigation"); 6945577eaa60SSam Leffler } 69467b0c77ecSSam Leffler sc->sc_monpass = HAL_RXERR_DECRYPT | HAL_RXERR_MIC; 69477b0c77ecSSam Leffler SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 69487b0c77ecSSam Leffler "monpass", CTLFLAG_RW, &sc->sc_monpass, 0, 69497b0c77ecSSam Leffler "mask of error frames to pass when monitoring"); 695010ad9a77SSam Leffler #ifdef ATH_SUPPORT_TDMA 695110ad9a77SSam Leffler if (ath_hal_macversion(ah) > 0x78) { 695210ad9a77SSam Leffler sc->sc_tdmadbaprep = 2; 695310ad9a77SSam Leffler SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 695410ad9a77SSam Leffler "dbaprep", CTLFLAG_RW, &sc->sc_tdmadbaprep, 0, 695510ad9a77SSam Leffler "TDMA DBA preparation time"); 695610ad9a77SSam Leffler sc->sc_tdmaswbaprep = 10; 695710ad9a77SSam Leffler SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 695810ad9a77SSam Leffler "swbaprep", CTLFLAG_RW, &sc->sc_tdmaswbaprep, 0, 695910ad9a77SSam Leffler "TDMA SWBA preparation time"); 696010ad9a77SSam Leffler SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 696110ad9a77SSam Leffler "guardtime", CTLFLAG_RW, &sc->sc_tdmaguard, 0, 696210ad9a77SSam Leffler "TDMA slot guard time"); 696310ad9a77SSam Leffler SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 696410ad9a77SSam Leffler "superframe", CTLFLAG_RD, &sc->sc_tdmabintval, 0, 696510ad9a77SSam Leffler "TDMA calculated super frame"); 696610ad9a77SSam Leffler } 696710ad9a77SSam Leffler #endif 6968c42a7b7eSSam Leffler } 6969c42a7b7eSSam Leffler 6970c42a7b7eSSam Leffler static void 6971c42a7b7eSSam Leffler ath_bpfattach(struct ath_softc *sc) 6972c42a7b7eSSam Leffler { 6973fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 6974c42a7b7eSSam Leffler 6975b032f27cSSam Leffler bpfattach(ifp, DLT_IEEE802_11_RADIO, 6976b032f27cSSam Leffler sizeof(struct ieee80211_frame) + sizeof(sc->sc_tx_th)); 6977c42a7b7eSSam Leffler /* 6978c42a7b7eSSam Leffler * Initialize constant fields. 6979c42a7b7eSSam Leffler * XXX make header lengths a multiple of 32-bits so subsequent 6980c42a7b7eSSam Leffler * headers are properly aligned; this is a kludge to keep 6981c42a7b7eSSam Leffler * certain applications happy. 6982c42a7b7eSSam Leffler * 6983c42a7b7eSSam Leffler * NB: the channel is setup each time we transition to the 6984c42a7b7eSSam Leffler * RUN state to avoid filling it in for each frame. 6985c42a7b7eSSam Leffler */ 6986c42a7b7eSSam Leffler sc->sc_tx_th_len = roundup(sizeof(sc->sc_tx_th), sizeof(u_int32_t)); 6987c42a7b7eSSam Leffler sc->sc_tx_th.wt_ihdr.it_len = htole16(sc->sc_tx_th_len); 6988c42a7b7eSSam Leffler sc->sc_tx_th.wt_ihdr.it_present = htole32(ATH_TX_RADIOTAP_PRESENT); 6989c42a7b7eSSam Leffler 6990d3be6f5bSSam Leffler sc->sc_rx_th_len = roundup(sizeof(sc->sc_rx_th), sizeof(u_int32_t)); 6991d3be6f5bSSam Leffler sc->sc_rx_th.wr_ihdr.it_len = htole16(sc->sc_rx_th_len); 6992c42a7b7eSSam Leffler sc->sc_rx_th.wr_ihdr.it_present = htole32(ATH_RX_RADIOTAP_PRESENT); 6993c42a7b7eSSam Leffler } 6994c42a7b7eSSam Leffler 6995664443d0SSam Leffler static int 6996664443d0SSam Leffler ath_tx_raw_start(struct ath_softc *sc, struct ieee80211_node *ni, 6997664443d0SSam Leffler struct ath_buf *bf, struct mbuf *m0, 6998664443d0SSam Leffler const struct ieee80211_bpf_params *params) 6999664443d0SSam Leffler { 7000b032f27cSSam Leffler struct ifnet *ifp = sc->sc_ifp; 7001b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 7002664443d0SSam Leffler struct ath_hal *ah = sc->sc_ah; 7003664443d0SSam Leffler int error, ismcast, ismrr; 70049d6b29a6SSam Leffler int keyix, hdrlen, pktlen, try0, txantenna; 7005664443d0SSam Leffler u_int8_t rix, cix, txrate, ctsrate, rate1, rate2, rate3; 7006664443d0SSam Leffler struct ieee80211_frame *wh; 7007664443d0SSam Leffler u_int flags, ctsduration; 7008664443d0SSam Leffler HAL_PKT_TYPE atype; 7009664443d0SSam Leffler const HAL_RATE_TABLE *rt; 7010664443d0SSam Leffler struct ath_desc *ds; 7011664443d0SSam Leffler u_int pri; 7012664443d0SSam Leffler 7013664443d0SSam Leffler wh = mtod(m0, struct ieee80211_frame *); 7014664443d0SSam Leffler ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); 7015664443d0SSam Leffler hdrlen = ieee80211_anyhdrsize(wh); 7016664443d0SSam Leffler /* 7017664443d0SSam Leffler * Packet length must not include any 7018664443d0SSam Leffler * pad bytes; deduct them here. 7019664443d0SSam Leffler */ 7020664443d0SSam Leffler /* XXX honor IEEE80211_BPF_DATAPAD */ 7021664443d0SSam Leffler pktlen = m0->m_pkthdr.len - (hdrlen & 3) + IEEE80211_CRC_LEN; 7022664443d0SSam Leffler 70239d6b29a6SSam Leffler if (params->ibp_flags & IEEE80211_BPF_CRYPTO) { 70249d6b29a6SSam Leffler const struct ieee80211_cipher *cip; 70259d6b29a6SSam Leffler struct ieee80211_key *k; 70269d6b29a6SSam Leffler 70279d6b29a6SSam Leffler /* 70289d6b29a6SSam Leffler * Construct the 802.11 header+trailer for an encrypted 70299d6b29a6SSam Leffler * frame. The only reason this can fail is because of an 70309d6b29a6SSam Leffler * unknown or unsupported cipher/key type. 70319d6b29a6SSam Leffler */ 70329d6b29a6SSam Leffler k = ieee80211_crypto_encap(ni, m0); 70339d6b29a6SSam Leffler if (k == NULL) { 70349d6b29a6SSam Leffler /* 70359d6b29a6SSam Leffler * This can happen when the key is yanked after the 70369d6b29a6SSam Leffler * frame was queued. Just discard the frame; the 70379d6b29a6SSam Leffler * 802.11 layer counts failures and provides 70389d6b29a6SSam Leffler * debugging/diagnostics. 70399d6b29a6SSam Leffler */ 70409d6b29a6SSam Leffler ath_freetx(m0); 70419d6b29a6SSam Leffler return EIO; 70429d6b29a6SSam Leffler } 70439d6b29a6SSam Leffler /* 70449d6b29a6SSam Leffler * Adjust the packet + header lengths for the crypto 70459d6b29a6SSam Leffler * additions and calculate the h/w key index. When 70469d6b29a6SSam Leffler * a s/w mic is done the frame will have had any mic 70479d6b29a6SSam Leffler * added to it prior to entry so m0->m_pkthdr.len will 70489d6b29a6SSam Leffler * account for it. Otherwise we need to add it to the 70499d6b29a6SSam Leffler * packet length. 70509d6b29a6SSam Leffler */ 70519d6b29a6SSam Leffler cip = k->wk_cipher; 70529d6b29a6SSam Leffler hdrlen += cip->ic_header; 70539d6b29a6SSam Leffler pktlen += cip->ic_header + cip->ic_trailer; 70549d6b29a6SSam Leffler /* NB: frags always have any TKIP MIC done in s/w */ 70559d6b29a6SSam Leffler if ((k->wk_flags & IEEE80211_KEY_SWMIC) == 0) 70569d6b29a6SSam Leffler pktlen += cip->ic_miclen; 70579d6b29a6SSam Leffler keyix = k->wk_keyix; 70589d6b29a6SSam Leffler 70599d6b29a6SSam Leffler /* packet header may have moved, reset our local pointer */ 70609d6b29a6SSam Leffler wh = mtod(m0, struct ieee80211_frame *); 70619d6b29a6SSam Leffler } else if (ni->ni_ucastkey.wk_cipher == &ieee80211_cipher_none) { 70629d6b29a6SSam Leffler /* 70639d6b29a6SSam Leffler * Use station key cache slot, if assigned. 70649d6b29a6SSam Leffler */ 70659d6b29a6SSam Leffler keyix = ni->ni_ucastkey.wk_keyix; 70669d6b29a6SSam Leffler if (keyix == IEEE80211_KEYIX_NONE) 70679d6b29a6SSam Leffler keyix = HAL_TXKEYIX_INVALID; 70689d6b29a6SSam Leffler } else 70699d6b29a6SSam Leffler keyix = HAL_TXKEYIX_INVALID; 70709d6b29a6SSam Leffler 7071664443d0SSam Leffler error = ath_tx_dmasetup(sc, bf, m0); 7072664443d0SSam Leffler if (error != 0) 7073664443d0SSam Leffler return error; 7074664443d0SSam Leffler m0 = bf->bf_m; /* NB: may have changed */ 7075664443d0SSam Leffler wh = mtod(m0, struct ieee80211_frame *); 7076664443d0SSam Leffler bf->bf_node = ni; /* NB: held reference */ 7077664443d0SSam Leffler 7078664443d0SSam Leffler flags = HAL_TXDESC_CLRDMASK; /* XXX needed for crypto errs */ 7079664443d0SSam Leffler flags |= HAL_TXDESC_INTREQ; /* force interrupt */ 7080664443d0SSam Leffler if (params->ibp_flags & IEEE80211_BPF_RTS) 7081664443d0SSam Leffler flags |= HAL_TXDESC_RTSENA; 7082664443d0SSam Leffler else if (params->ibp_flags & IEEE80211_BPF_CTS) 7083664443d0SSam Leffler flags |= HAL_TXDESC_CTSENA; 7084664443d0SSam Leffler /* XXX leave ismcast to injector? */ 7085664443d0SSam Leffler if ((params->ibp_flags & IEEE80211_BPF_NOACK) || ismcast) 7086664443d0SSam Leffler flags |= HAL_TXDESC_NOACK; 7087664443d0SSam Leffler 7088664443d0SSam Leffler rt = sc->sc_currates; 7089664443d0SSam Leffler KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode)); 7090664443d0SSam Leffler rix = ath_tx_findrix(rt, params->ibp_rate0); 7091664443d0SSam Leffler txrate = rt->info[rix].rateCode; 7092664443d0SSam Leffler if (params->ibp_flags & IEEE80211_BPF_SHORTPRE) 7093664443d0SSam Leffler txrate |= rt->info[rix].shortPreamble; 709446d4d74cSSam Leffler sc->sc_txrix = rix; 7095664443d0SSam Leffler try0 = params->ibp_try0; 7096664443d0SSam Leffler ismrr = (params->ibp_try1 != 0); 7097664443d0SSam Leffler txantenna = params->ibp_pri >> 2; 7098664443d0SSam Leffler if (txantenna == 0) /* XXX? */ 7099664443d0SSam Leffler txantenna = sc->sc_txantenna; 7100664443d0SSam Leffler ctsduration = 0; 7101664443d0SSam Leffler if (flags & (HAL_TXDESC_CTSENA | HAL_TXDESC_RTSENA)) { 7102664443d0SSam Leffler cix = ath_tx_findrix(rt, params->ibp_ctsrate); 7103664443d0SSam Leffler ctsrate = rt->info[cix].rateCode; 7104664443d0SSam Leffler if (params->ibp_flags & IEEE80211_BPF_SHORTPRE) { 7105664443d0SSam Leffler ctsrate |= rt->info[cix].shortPreamble; 7106664443d0SSam Leffler if (flags & HAL_TXDESC_RTSENA) /* SIFS + CTS */ 7107664443d0SSam Leffler ctsduration += rt->info[cix].spAckDuration; 7108664443d0SSam Leffler ctsduration += ath_hal_computetxtime(ah, 7109664443d0SSam Leffler rt, pktlen, rix, AH_TRUE); 7110664443d0SSam Leffler if ((flags & HAL_TXDESC_NOACK) == 0) /* SIFS + ACK */ 7111664443d0SSam Leffler ctsduration += rt->info[rix].spAckDuration; 7112664443d0SSam Leffler } else { 7113664443d0SSam Leffler if (flags & HAL_TXDESC_RTSENA) /* SIFS + CTS */ 7114664443d0SSam Leffler ctsduration += rt->info[cix].lpAckDuration; 7115664443d0SSam Leffler ctsduration += ath_hal_computetxtime(ah, 7116664443d0SSam Leffler rt, pktlen, rix, AH_FALSE); 7117664443d0SSam Leffler if ((flags & HAL_TXDESC_NOACK) == 0) /* SIFS + ACK */ 7118664443d0SSam Leffler ctsduration += rt->info[rix].lpAckDuration; 7119664443d0SSam Leffler } 7120664443d0SSam Leffler ismrr = 0; /* XXX */ 7121664443d0SSam Leffler } else 7122664443d0SSam Leffler ctsrate = 0; 7123664443d0SSam Leffler pri = params->ibp_pri & 3; 7124664443d0SSam Leffler /* 7125664443d0SSam Leffler * NB: we mark all packets as type PSPOLL so the h/w won't 7126664443d0SSam Leffler * set the sequence number, duration, etc. 7127664443d0SSam Leffler */ 7128664443d0SSam Leffler atype = HAL_PKT_TYPE_PSPOLL; 7129664443d0SSam Leffler 7130664443d0SSam Leffler if (IFF_DUMPPKTS(sc, ATH_DEBUG_XMIT)) 713168e8e04eSSam Leffler ieee80211_dump_pkt(ic, mtod(m0, caddr_t), m0->m_len, 713246d4d74cSSam Leffler sc->sc_hwmap[rix].ieeerate, -1); 7133664443d0SSam Leffler 7134b032f27cSSam Leffler if (bpf_peers_present(ifp->if_bpf)) { 7135664443d0SSam Leffler u_int64_t tsf = ath_hal_gettsf64(ah); 7136664443d0SSam Leffler 7137664443d0SSam Leffler sc->sc_tx_th.wt_tsf = htole64(tsf); 713846d4d74cSSam Leffler sc->sc_tx_th.wt_flags = sc->sc_hwmap[rix].txflags; 7139664443d0SSam Leffler if (wh->i_fc[1] & IEEE80211_FC1_WEP) 7140664443d0SSam Leffler sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_WEP; 714146d4d74cSSam Leffler sc->sc_tx_th.wt_rate = sc->sc_hwmap[rix].ieeerate; 7142664443d0SSam Leffler sc->sc_tx_th.wt_txpower = ni->ni_txpower; 7143664443d0SSam Leffler sc->sc_tx_th.wt_antenna = sc->sc_txantenna; 7144664443d0SSam Leffler 7145b032f27cSSam Leffler bpf_mtap2(ifp->if_bpf, &sc->sc_tx_th, sc->sc_tx_th_len, m0); 7146664443d0SSam Leffler } 7147664443d0SSam Leffler 7148664443d0SSam Leffler /* 7149664443d0SSam Leffler * Formulate first tx descriptor with tx controls. 7150664443d0SSam Leffler */ 7151664443d0SSam Leffler ds = bf->bf_desc; 7152664443d0SSam Leffler /* XXX check return value? */ 7153664443d0SSam Leffler ath_hal_setuptxdesc(ah, ds 7154664443d0SSam Leffler , pktlen /* packet length */ 7155664443d0SSam Leffler , hdrlen /* header length */ 7156664443d0SSam Leffler , atype /* Atheros packet type */ 7157664443d0SSam Leffler , params->ibp_power /* txpower */ 7158664443d0SSam Leffler , txrate, try0 /* series 0 rate/tries */ 71599d6b29a6SSam Leffler , keyix /* key cache index */ 7160664443d0SSam Leffler , txantenna /* antenna mode */ 7161664443d0SSam Leffler , flags /* flags */ 7162664443d0SSam Leffler , ctsrate /* rts/cts rate */ 7163664443d0SSam Leffler , ctsduration /* rts/cts duration */ 7164664443d0SSam Leffler ); 716580c07f23SSam Leffler bf->bf_txflags = flags; 7166664443d0SSam Leffler 7167664443d0SSam Leffler if (ismrr) { 7168664443d0SSam Leffler rix = ath_tx_findrix(rt, params->ibp_rate1); 7169664443d0SSam Leffler rate1 = rt->info[rix].rateCode; 7170664443d0SSam Leffler if (params->ibp_flags & IEEE80211_BPF_SHORTPRE) 7171664443d0SSam Leffler rate1 |= rt->info[rix].shortPreamble; 7172664443d0SSam Leffler if (params->ibp_try2) { 7173664443d0SSam Leffler rix = ath_tx_findrix(rt, params->ibp_rate2); 7174664443d0SSam Leffler rate2 = rt->info[rix].rateCode; 7175664443d0SSam Leffler if (params->ibp_flags & IEEE80211_BPF_SHORTPRE) 7176664443d0SSam Leffler rate2 |= rt->info[rix].shortPreamble; 7177664443d0SSam Leffler } else 7178664443d0SSam Leffler rate2 = 0; 7179664443d0SSam Leffler if (params->ibp_try3) { 7180664443d0SSam Leffler rix = ath_tx_findrix(rt, params->ibp_rate3); 7181664443d0SSam Leffler rate3 = rt->info[rix].rateCode; 7182664443d0SSam Leffler if (params->ibp_flags & IEEE80211_BPF_SHORTPRE) 7183664443d0SSam Leffler rate3 |= rt->info[rix].shortPreamble; 7184664443d0SSam Leffler } else 7185664443d0SSam Leffler rate3 = 0; 7186664443d0SSam Leffler ath_hal_setupxtxdesc(ah, ds 7187664443d0SSam Leffler , rate1, params->ibp_try1 /* series 1 */ 7188664443d0SSam Leffler , rate2, params->ibp_try2 /* series 2 */ 7189664443d0SSam Leffler , rate3, params->ibp_try3 /* series 3 */ 7190664443d0SSam Leffler ); 7191664443d0SSam Leffler } 7192664443d0SSam Leffler 7193b032f27cSSam Leffler /* NB: no buffered multicast in power save support */ 7194b032f27cSSam Leffler ath_tx_handoff(sc, sc->sc_ac2q[pri], bf); 7195664443d0SSam Leffler return 0; 7196664443d0SSam Leffler } 7197664443d0SSam Leffler 7198664443d0SSam Leffler static int 7199664443d0SSam Leffler ath_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, 7200664443d0SSam Leffler const struct ieee80211_bpf_params *params) 7201664443d0SSam Leffler { 7202664443d0SSam Leffler struct ieee80211com *ic = ni->ni_ic; 7203664443d0SSam Leffler struct ifnet *ifp = ic->ic_ifp; 7204664443d0SSam Leffler struct ath_softc *sc = ifp->if_softc; 7205664443d0SSam Leffler struct ath_buf *bf; 7206664443d0SSam Leffler 7207664443d0SSam Leffler if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || sc->sc_invalid) { 72083267a60cSSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT, "%s: discard frame, %s", __func__, 72093267a60cSSam Leffler (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ? 72103267a60cSSam Leffler "!running" : "invalid"); 72113267a60cSSam Leffler sc->sc_stats.ast_tx_raw_fail++; 7212b03cfe23SSepherosa Ziehau ieee80211_free_node(ni); 7213664443d0SSam Leffler m_freem(m); 7214664443d0SSam Leffler return ENETDOWN; 7215664443d0SSam Leffler } 7216664443d0SSam Leffler /* 7217664443d0SSam Leffler * Grab a TX buffer and associated resources. 7218664443d0SSam Leffler */ 721910ad9a77SSam Leffler bf = ath_getbuf(sc); 7220664443d0SSam Leffler if (bf == NULL) { 72213267a60cSSam Leffler /* NB: ath_getbuf handles stat+msg */ 7222b03cfe23SSepherosa Ziehau ieee80211_free_node(ni); 7223664443d0SSam Leffler m_freem(m); 7224664443d0SSam Leffler return ENOBUFS; 7225664443d0SSam Leffler } 7226664443d0SSam Leffler 7227664443d0SSam Leffler ifp->if_opackets++; 7228664443d0SSam Leffler sc->sc_stats.ast_tx_raw++; 7229664443d0SSam Leffler 7230664443d0SSam Leffler if (params == NULL) { 7231664443d0SSam Leffler /* 7232664443d0SSam Leffler * Legacy path; interpret frame contents to decide 7233664443d0SSam Leffler * precisely how to send the frame. 7234664443d0SSam Leffler */ 7235664443d0SSam Leffler if (ath_tx_start(sc, ni, bf, m)) 7236664443d0SSam Leffler goto bad; 7237664443d0SSam Leffler } else { 7238664443d0SSam Leffler /* 7239664443d0SSam Leffler * Caller supplied explicit parameters to use in 7240664443d0SSam Leffler * sending the frame. 7241664443d0SSam Leffler */ 7242664443d0SSam Leffler if (ath_tx_raw_start(sc, ni, bf, m, params)) 7243664443d0SSam Leffler goto bad; 7244664443d0SSam Leffler } 724568e8e04eSSam Leffler ifp->if_timer = 5; 7246664443d0SSam Leffler 7247664443d0SSam Leffler return 0; 7248664443d0SSam Leffler bad: 7249664443d0SSam Leffler ifp->if_oerrors++; 7250664443d0SSam Leffler ATH_TXBUF_LOCK(sc); 725110ad9a77SSam Leffler STAILQ_INSERT_HEAD(&sc->sc_txbuf, bf, bf_list); 7252664443d0SSam Leffler ATH_TXBUF_UNLOCK(sc); 7253664443d0SSam Leffler ieee80211_free_node(ni); 7254664443d0SSam Leffler return EIO; /* XXX */ 7255664443d0SSam Leffler } 7256664443d0SSam Leffler 7257c42a7b7eSSam Leffler /* 7258c42a7b7eSSam Leffler * Announce various information on device/driver attach. 7259c42a7b7eSSam Leffler */ 7260c42a7b7eSSam Leffler static void 7261c42a7b7eSSam Leffler ath_announce(struct ath_softc *sc) 7262c42a7b7eSSam Leffler { 7263c42a7b7eSSam Leffler #define HAL_MODE_DUALBAND (HAL_MODE_11A|HAL_MODE_11B) 7264fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 7265c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 726659efa8b5SSam Leffler u_int modes; 7267c42a7b7eSSam Leffler 7268c42a7b7eSSam Leffler if_printf(ifp, "mac %d.%d phy %d.%d", 7269c42a7b7eSSam Leffler ah->ah_macVersion, ah->ah_macRev, 7270c42a7b7eSSam Leffler ah->ah_phyRev >> 4, ah->ah_phyRev & 0xf); 7271c42a7b7eSSam Leffler /* 7272c42a7b7eSSam Leffler * Print radio revision(s). We check the wireless modes 7273c42a7b7eSSam Leffler * to avoid falsely printing revs for inoperable parts. 7274c42a7b7eSSam Leffler * Dual-band radio revs are returned in the 5Ghz rev number. 7275c42a7b7eSSam Leffler */ 727659efa8b5SSam Leffler modes = ath_hal_getwirelessmodes(ah); 7277c42a7b7eSSam Leffler if ((modes & HAL_MODE_DUALBAND) == HAL_MODE_DUALBAND) { 7278c42a7b7eSSam Leffler if (ah->ah_analog5GhzRev && ah->ah_analog2GhzRev) 7279c42a7b7eSSam Leffler printf(" 5ghz radio %d.%d 2ghz radio %d.%d", 7280c42a7b7eSSam Leffler ah->ah_analog5GhzRev >> 4, 7281c42a7b7eSSam Leffler ah->ah_analog5GhzRev & 0xf, 7282c42a7b7eSSam Leffler ah->ah_analog2GhzRev >> 4, 7283c42a7b7eSSam Leffler ah->ah_analog2GhzRev & 0xf); 7284c42a7b7eSSam Leffler else 7285c42a7b7eSSam Leffler printf(" radio %d.%d", ah->ah_analog5GhzRev >> 4, 7286c42a7b7eSSam Leffler ah->ah_analog5GhzRev & 0xf); 7287c42a7b7eSSam Leffler } else 7288c42a7b7eSSam Leffler printf(" radio %d.%d", ah->ah_analog5GhzRev >> 4, 7289c42a7b7eSSam Leffler ah->ah_analog5GhzRev & 0xf); 7290c42a7b7eSSam Leffler printf("\n"); 7291c42a7b7eSSam Leffler if (bootverbose) { 7292c42a7b7eSSam Leffler int i; 7293c42a7b7eSSam Leffler for (i = 0; i <= WME_AC_VO; i++) { 7294c42a7b7eSSam Leffler struct ath_txq *txq = sc->sc_ac2q[i]; 7295c42a7b7eSSam Leffler if_printf(ifp, "Use hw queue %u for %s traffic\n", 7296c42a7b7eSSam Leffler txq->axq_qnum, ieee80211_wme_acnames[i]); 7297c42a7b7eSSam Leffler } 7298c42a7b7eSSam Leffler if_printf(ifp, "Use hw queue %u for CAB traffic\n", 7299c42a7b7eSSam Leffler sc->sc_cabq->axq_qnum); 7300c42a7b7eSSam Leffler if_printf(ifp, "Use hw queue %u for beacons\n", sc->sc_bhalq); 7301c42a7b7eSSam Leffler } 7302e2d787faSSam Leffler if (ath_rxbuf != ATH_RXBUF) 7303e2d787faSSam Leffler if_printf(ifp, "using %u rx buffers\n", ath_rxbuf); 7304e2d787faSSam Leffler if (ath_txbuf != ATH_TXBUF) 7305e2d787faSSam Leffler if_printf(ifp, "using %u tx buffers\n", ath_txbuf); 7306c42a7b7eSSam Leffler #undef HAL_MODE_DUALBAND 7307c42a7b7eSSam Leffler } 730810ad9a77SSam Leffler 730910ad9a77SSam Leffler #ifdef ATH_SUPPORT_TDMA 731010ad9a77SSam Leffler static __inline uint32_t 731110ad9a77SSam Leffler ath_hal_getnexttbtt(struct ath_hal *ah) 731210ad9a77SSam Leffler { 731310ad9a77SSam Leffler #define AR_TIMER0 0x8028 731410ad9a77SSam Leffler return OS_REG_READ(ah, AR_TIMER0); 731510ad9a77SSam Leffler } 731610ad9a77SSam Leffler 731710ad9a77SSam Leffler static __inline void 731810ad9a77SSam Leffler ath_hal_adjusttsf(struct ath_hal *ah, int32_t tsfdelta) 731910ad9a77SSam Leffler { 732010ad9a77SSam Leffler /* XXX handle wrap/overflow */ 732110ad9a77SSam Leffler OS_REG_WRITE(ah, AR_TSF_L32, OS_REG_READ(ah, AR_TSF_L32) + tsfdelta); 732210ad9a77SSam Leffler } 732310ad9a77SSam Leffler 732410ad9a77SSam Leffler static void 732510ad9a77SSam Leffler ath_tdma_settimers(struct ath_softc *sc, u_int32_t nexttbtt, u_int32_t bintval) 732610ad9a77SSam Leffler { 732710ad9a77SSam Leffler struct ath_hal *ah = sc->sc_ah; 732810ad9a77SSam Leffler HAL_BEACON_TIMERS bt; 732910ad9a77SSam Leffler 733010ad9a77SSam Leffler bt.bt_intval = bintval | HAL_BEACON_ENA; 733110ad9a77SSam Leffler bt.bt_nexttbtt = nexttbtt; 733210ad9a77SSam Leffler bt.bt_nextdba = (nexttbtt<<3) - sc->sc_tdmadbaprep; 733310ad9a77SSam Leffler bt.bt_nextswba = (nexttbtt<<3) - sc->sc_tdmaswbaprep; 733410ad9a77SSam Leffler bt.bt_nextatim = nexttbtt+1; 733510ad9a77SSam Leffler ath_hal_beaconsettimers(ah, &bt); 733610ad9a77SSam Leffler } 733710ad9a77SSam Leffler 733810ad9a77SSam Leffler /* 733910ad9a77SSam Leffler * Calculate the beacon interval. This is periodic in the 734010ad9a77SSam Leffler * superframe for the bss. We assume each station is configured 734110ad9a77SSam Leffler * identically wrt transmit rate so the guard time we calculate 734210ad9a77SSam Leffler * above will be the same on all stations. Note we need to 734310ad9a77SSam Leffler * factor in the xmit time because the hardware will schedule 734410ad9a77SSam Leffler * a frame for transmit if the start of the frame is within 734510ad9a77SSam Leffler * the burst time. When we get hardware that properly kills 734610ad9a77SSam Leffler * frames in the PCU we can reduce/eliminate the guard time. 734710ad9a77SSam Leffler * 734810ad9a77SSam Leffler * Roundup to 1024 is so we have 1 TU buffer in the guard time 734910ad9a77SSam Leffler * to deal with the granularity of the nexttbtt timer. 11n MAC's 735010ad9a77SSam Leffler * with 1us timer granularity should allow us to reduce/eliminate 735110ad9a77SSam Leffler * this. 735210ad9a77SSam Leffler */ 735310ad9a77SSam Leffler static void 735410ad9a77SSam Leffler ath_tdma_bintvalsetup(struct ath_softc *sc, 735510ad9a77SSam Leffler const struct ieee80211_tdma_state *tdma) 735610ad9a77SSam Leffler { 735710ad9a77SSam Leffler /* copy from vap state (XXX check all vaps have same value?) */ 735810ad9a77SSam Leffler sc->sc_tdmaslotlen = tdma->tdma_slotlen; 735910ad9a77SSam Leffler sc->sc_tdmabintcnt = tdma->tdma_bintval; 736010ad9a77SSam Leffler 736110ad9a77SSam Leffler sc->sc_tdmabintval = roundup((sc->sc_tdmaslotlen+sc->sc_tdmaguard) * 736210ad9a77SSam Leffler tdma->tdma_slotcnt, 1024); 736310ad9a77SSam Leffler sc->sc_tdmabintval >>= 10; /* TSF -> TU */ 736410ad9a77SSam Leffler if (sc->sc_tdmabintval & 1) 736510ad9a77SSam Leffler sc->sc_tdmabintval++; 736610ad9a77SSam Leffler 736710ad9a77SSam Leffler if (tdma->tdma_slot == 0) { 736810ad9a77SSam Leffler /* 736910ad9a77SSam Leffler * Only slot 0 beacons; other slots respond. 737010ad9a77SSam Leffler */ 737110ad9a77SSam Leffler sc->sc_imask |= HAL_INT_SWBA; 737210ad9a77SSam Leffler sc->sc_tdmaswba = 0; /* beacon immediately */ 737310ad9a77SSam Leffler } else { 737410ad9a77SSam Leffler /* XXX all vaps must be slot 0 or slot !0 */ 737510ad9a77SSam Leffler sc->sc_imask &= ~HAL_INT_SWBA; 737610ad9a77SSam Leffler } 737710ad9a77SSam Leffler } 737810ad9a77SSam Leffler 737910ad9a77SSam Leffler /* 738010ad9a77SSam Leffler * Max 802.11 overhead. This assumes no 4-address frames and 738110ad9a77SSam Leffler * the encapsulation done by ieee80211_encap (llc). We also 738210ad9a77SSam Leffler * include potential crypto overhead. 738310ad9a77SSam Leffler */ 738410ad9a77SSam Leffler #define IEEE80211_MAXOVERHEAD \ 738510ad9a77SSam Leffler (sizeof(struct ieee80211_qosframe) \ 738610ad9a77SSam Leffler + sizeof(struct llc) \ 738710ad9a77SSam Leffler + IEEE80211_ADDR_LEN \ 738810ad9a77SSam Leffler + IEEE80211_WEP_IVLEN \ 738910ad9a77SSam Leffler + IEEE80211_WEP_KIDLEN \ 739010ad9a77SSam Leffler + IEEE80211_WEP_CRCLEN \ 739110ad9a77SSam Leffler + IEEE80211_WEP_MICLEN \ 739210ad9a77SSam Leffler + IEEE80211_CRC_LEN) 739310ad9a77SSam Leffler 739410ad9a77SSam Leffler /* 739510ad9a77SSam Leffler * Setup initially for tdma operation. Start the beacon 739610ad9a77SSam Leffler * timers and enable SWBA if we are slot 0. Otherwise 739710ad9a77SSam Leffler * we wait for slot 0 to arrive so we can sync up before 739810ad9a77SSam Leffler * starting to transmit. 739910ad9a77SSam Leffler */ 740010ad9a77SSam Leffler static void 740110ad9a77SSam Leffler ath_tdma_config(struct ath_softc *sc, struct ieee80211vap *vap) 740210ad9a77SSam Leffler { 740310ad9a77SSam Leffler struct ath_hal *ah = sc->sc_ah; 740410ad9a77SSam Leffler struct ifnet *ifp = sc->sc_ifp; 740510ad9a77SSam Leffler struct ieee80211com *ic = ifp->if_l2com; 740610ad9a77SSam Leffler const struct ieee80211_txparam *tp; 740710ad9a77SSam Leffler const struct ieee80211_tdma_state *tdma = NULL; 740810ad9a77SSam Leffler int rix; 740910ad9a77SSam Leffler 741010ad9a77SSam Leffler if (vap == NULL) { 741110ad9a77SSam Leffler vap = TAILQ_FIRST(&ic->ic_vaps); /* XXX */ 741210ad9a77SSam Leffler if (vap == NULL) { 741310ad9a77SSam Leffler if_printf(ifp, "%s: no vaps?\n", __func__); 741410ad9a77SSam Leffler return; 741510ad9a77SSam Leffler } 741610ad9a77SSam Leffler } 741710ad9a77SSam Leffler tp = vap->iv_bss->ni_txparms; 741810ad9a77SSam Leffler /* 741910ad9a77SSam Leffler * Calculate the guard time for each slot. This is the 742010ad9a77SSam Leffler * time to send a maximal-size frame according to the 742110ad9a77SSam Leffler * fixed/lowest transmit rate. Note that the interface 742210ad9a77SSam Leffler * mtu does not include the 802.11 overhead so we must 742310ad9a77SSam Leffler * tack that on (ath_hal_computetxtime includes the 742410ad9a77SSam Leffler * preamble and plcp in it's calculation). 742510ad9a77SSam Leffler */ 742610ad9a77SSam Leffler tdma = vap->iv_tdma; 742710ad9a77SSam Leffler if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) 742810ad9a77SSam Leffler rix = ath_tx_findrix(sc->sc_currates, tp->ucastrate); 742910ad9a77SSam Leffler else 743010ad9a77SSam Leffler rix = ath_tx_findrix(sc->sc_currates, tp->mcastrate); 743110ad9a77SSam Leffler /* XXX short preamble assumed */ 743210ad9a77SSam Leffler sc->sc_tdmaguard = ath_hal_computetxtime(ah, sc->sc_currates, 743310ad9a77SSam Leffler ifp->if_mtu + IEEE80211_MAXOVERHEAD, rix, AH_TRUE); 743410ad9a77SSam Leffler 743510ad9a77SSam Leffler ath_hal_intrset(ah, 0); 743610ad9a77SSam Leffler 743710ad9a77SSam Leffler ath_beaconq_config(sc); /* setup h/w beacon q */ 743810ad9a77SSam Leffler ath_hal_setcca(ah, AH_FALSE); /* disable CCA */ 743910ad9a77SSam Leffler ath_tdma_bintvalsetup(sc, tdma); /* calculate beacon interval */ 744010ad9a77SSam Leffler ath_tdma_settimers(sc, sc->sc_tdmabintval, 744110ad9a77SSam Leffler sc->sc_tdmabintval | HAL_BEACON_RESET_TSF); 744210ad9a77SSam Leffler sc->sc_syncbeacon = 0; 744310ad9a77SSam Leffler 744410ad9a77SSam Leffler sc->sc_avgtsfdeltap = TDMA_DUMMY_MARKER; 744510ad9a77SSam Leffler sc->sc_avgtsfdeltam = TDMA_DUMMY_MARKER; 744610ad9a77SSam Leffler 744710ad9a77SSam Leffler ath_hal_intrset(ah, sc->sc_imask); 744810ad9a77SSam Leffler 744910ad9a77SSam Leffler DPRINTF(sc, ATH_DEBUG_TDMA, "%s: slot %u len %uus cnt %u " 745010ad9a77SSam Leffler "bsched %u guard %uus bintval %u TU dba prep %u\n", __func__, 745110ad9a77SSam Leffler tdma->tdma_slot, tdma->tdma_slotlen, tdma->tdma_slotcnt, 745210ad9a77SSam Leffler tdma->tdma_bintval, sc->sc_tdmaguard, sc->sc_tdmabintval, 745310ad9a77SSam Leffler sc->sc_tdmadbaprep); 745410ad9a77SSam Leffler } 745510ad9a77SSam Leffler 745610ad9a77SSam Leffler /* 745710ad9a77SSam Leffler * Update tdma operation. Called from the 802.11 layer 745810ad9a77SSam Leffler * when a beacon is received from the TDMA station operating 745910ad9a77SSam Leffler * in the slot immediately preceding us in the bss. Use 746010ad9a77SSam Leffler * the rx timestamp for the beacon frame to update our 746110ad9a77SSam Leffler * beacon timers so we follow their schedule. Note that 746210ad9a77SSam Leffler * by using the rx timestamp we implicitly include the 746310ad9a77SSam Leffler * propagation delay in our schedule. 746410ad9a77SSam Leffler */ 746510ad9a77SSam Leffler static void 746610ad9a77SSam Leffler ath_tdma_update(struct ieee80211_node *ni, 746710ad9a77SSam Leffler const struct ieee80211_tdma_param *tdma) 746810ad9a77SSam Leffler { 746910ad9a77SSam Leffler #define TSF_TO_TU(_h,_l) \ 747010ad9a77SSam Leffler ((((u_int32_t)(_h)) << 22) | (((u_int32_t)(_l)) >> 10)) 747110ad9a77SSam Leffler #define TU_TO_TSF(_tu) (((u_int64_t)(_tu)) << 10) 747210ad9a77SSam Leffler struct ieee80211vap *vap = ni->ni_vap; 747310ad9a77SSam Leffler struct ieee80211com *ic = ni->ni_ic; 747410ad9a77SSam Leffler struct ath_softc *sc = ic->ic_ifp->if_softc; 747510ad9a77SSam Leffler struct ath_hal *ah = sc->sc_ah; 747610ad9a77SSam Leffler const HAL_RATE_TABLE *rt = sc->sc_currates; 747710ad9a77SSam Leffler u_int64_t tsf, rstamp, nextslot; 747810ad9a77SSam Leffler u_int32_t txtime, nextslottu, timer0; 747910ad9a77SSam Leffler int32_t tudelta, tsfdelta; 748010ad9a77SSam Leffler const struct ath_rx_status *rs; 748110ad9a77SSam Leffler int rix; 748210ad9a77SSam Leffler 748310ad9a77SSam Leffler sc->sc_stats.ast_tdma_update++; 748410ad9a77SSam Leffler 748510ad9a77SSam Leffler /* 748610ad9a77SSam Leffler * Check for and adopt configuration changes. 748710ad9a77SSam Leffler */ 748810ad9a77SSam Leffler if (isset(ATH_VAP(vap)->av_boff.bo_flags, IEEE80211_BEACON_TDMA)) { 748910ad9a77SSam Leffler const struct ieee80211_tdma_state *ts = vap->iv_tdma; 749010ad9a77SSam Leffler 749110ad9a77SSam Leffler ath_tdma_bintvalsetup(sc, ts); 749210ad9a77SSam Leffler 749310ad9a77SSam Leffler DPRINTF(sc, ATH_DEBUG_TDMA, 749410ad9a77SSam Leffler "%s: adopt slot %u slotcnt %u slotlen %u us " 749510ad9a77SSam Leffler "bintval %u TU\n", __func__, 749610ad9a77SSam Leffler ts->tdma_slot, ts->tdma_slotcnt, ts->tdma_slotlen, 749710ad9a77SSam Leffler sc->sc_tdmabintval); 749810ad9a77SSam Leffler 749910ad9a77SSam Leffler ath_beaconq_config(sc); 750010ad9a77SSam Leffler /* XXX right? */ 750110ad9a77SSam Leffler ath_hal_intrset(ah, sc->sc_imask); 750210ad9a77SSam Leffler /* NB: beacon timers programmed below */ 750310ad9a77SSam Leffler } 750410ad9a77SSam Leffler 750510ad9a77SSam Leffler /* extend rx timestamp to 64 bits */ 750610ad9a77SSam Leffler tsf = ath_hal_gettsf64(ah); 750710ad9a77SSam Leffler rstamp = ath_extend_tsf(ni->ni_rstamp, tsf); 750810ad9a77SSam Leffler /* 750910ad9a77SSam Leffler * The rx timestamp is set by the hardware on completing 751010ad9a77SSam Leffler * reception (at the point where the rx descriptor is DMA'd 751110ad9a77SSam Leffler * to the host). To find the start of our next slot we 751210ad9a77SSam Leffler * must adjust this time by the time required to send 751310ad9a77SSam Leffler * the packet just received. 751410ad9a77SSam Leffler */ 751510ad9a77SSam Leffler rs = sc->sc_tdmars; 751610ad9a77SSam Leffler rix = rt->rateCodeToIndex[rs->rs_rate]; 751710ad9a77SSam Leffler txtime = ath_hal_computetxtime(ah, rt, rs->rs_datalen, rix, 751810ad9a77SSam Leffler rt->info[rix].shortPreamble); 751910ad9a77SSam Leffler /* NB: << 9 is to cvt to TU and /2 */ 752010ad9a77SSam Leffler nextslot = (rstamp - txtime) + (sc->sc_tdmabintval << 9); 752110ad9a77SSam Leffler nextslottu = TSF_TO_TU(nextslot>>32, nextslot) & HAL_BEACON_PERIOD; 752210ad9a77SSam Leffler 752310ad9a77SSam Leffler /* 752410ad9a77SSam Leffler * TIMER0 is the h/w's idea of NextTBTT (in TU's). Convert 752510ad9a77SSam Leffler * to usecs and calculate the difference between what the 752610ad9a77SSam Leffler * other station thinks and what we have programmed. This 752710ad9a77SSam Leffler * lets us figure how to adjust our timers to match. The 752810ad9a77SSam Leffler * adjustments are done by pulling the TSF forward and possibly 752910ad9a77SSam Leffler * rewriting the beacon timers. 753010ad9a77SSam Leffler */ 753110ad9a77SSam Leffler timer0 = ath_hal_getnexttbtt(ah); 753210ad9a77SSam Leffler tsfdelta = (int32_t)((nextslot % TU_TO_TSF(HAL_BEACON_PERIOD+1)) - TU_TO_TSF(timer0)); 753310ad9a77SSam Leffler 753410ad9a77SSam Leffler DPRINTF(sc, ATH_DEBUG_TDMA_TIMER, 753510ad9a77SSam Leffler "tsfdelta %d avg +%d/-%d\n", tsfdelta, 753610ad9a77SSam Leffler TDMA_AVG(sc->sc_avgtsfdeltap), TDMA_AVG(sc->sc_avgtsfdeltam)); 753710ad9a77SSam Leffler 753810ad9a77SSam Leffler if (tsfdelta < 0) { 753910ad9a77SSam Leffler TDMA_SAMPLE(sc->sc_avgtsfdeltap, 0); 754010ad9a77SSam Leffler TDMA_SAMPLE(sc->sc_avgtsfdeltam, -tsfdelta); 754110ad9a77SSam Leffler tsfdelta = -tsfdelta % 1024; 754210ad9a77SSam Leffler nextslottu++; 754310ad9a77SSam Leffler } else if (tsfdelta > 0) { 754410ad9a77SSam Leffler TDMA_SAMPLE(sc->sc_avgtsfdeltap, tsfdelta); 754510ad9a77SSam Leffler TDMA_SAMPLE(sc->sc_avgtsfdeltam, 0); 754610ad9a77SSam Leffler tsfdelta = 1024 - (tsfdelta % 1024); 754710ad9a77SSam Leffler nextslottu++; 754810ad9a77SSam Leffler } else { 754910ad9a77SSam Leffler TDMA_SAMPLE(sc->sc_avgtsfdeltap, 0); 755010ad9a77SSam Leffler TDMA_SAMPLE(sc->sc_avgtsfdeltam, 0); 755110ad9a77SSam Leffler } 755210ad9a77SSam Leffler tudelta = nextslottu - timer0; 755310ad9a77SSam Leffler 755410ad9a77SSam Leffler /* 755510ad9a77SSam Leffler * Copy sender's timetstamp into tdma ie so they can 755610ad9a77SSam Leffler * calculate roundtrip time. We submit a beacon frame 755710ad9a77SSam Leffler * below after any timer adjustment. The frame goes out 755810ad9a77SSam Leffler * at the next TBTT so the sender can calculate the 755910ad9a77SSam Leffler * roundtrip by inspecting the tdma ie in our beacon frame. 756010ad9a77SSam Leffler * 756110ad9a77SSam Leffler * NB: This tstamp is subtlely preserved when 756210ad9a77SSam Leffler * IEEE80211_BEACON_TDMA is marked (e.g. when the 756310ad9a77SSam Leffler * slot position changes) because ieee80211_add_tdma 756410ad9a77SSam Leffler * skips over the data. 756510ad9a77SSam Leffler */ 756610ad9a77SSam Leffler memcpy(ATH_VAP(vap)->av_boff.bo_tdma + 756710ad9a77SSam Leffler __offsetof(struct ieee80211_tdma_param, tdma_tstamp), 756810ad9a77SSam Leffler &ni->ni_tstamp.data, 8); 756910ad9a77SSam Leffler #if 0 757010ad9a77SSam Leffler DPRINTF(sc, ATH_DEBUG_TDMA_TIMER, 757110ad9a77SSam Leffler "tsf %llu nextslot %llu (%d, %d) nextslottu %u timer0 %u (%d)\n", 757210ad9a77SSam Leffler (unsigned long long) tsf, (unsigned long long) nextslot, 757310ad9a77SSam Leffler (int)(nextslot - tsf), tsfdelta, 757410ad9a77SSam Leffler nextslottu, timer0, tudelta); 757510ad9a77SSam Leffler #endif 757610ad9a77SSam Leffler /* 757710ad9a77SSam Leffler * Adjust the beacon timers only when pulling them forward 757810ad9a77SSam Leffler * or when going back by less than the beacon interval. 757910ad9a77SSam Leffler * Negative jumps larger than the beacon interval seem to 758010ad9a77SSam Leffler * cause the timers to stop and generally cause instability. 758110ad9a77SSam Leffler * This basically filters out jumps due to missed beacons. 758210ad9a77SSam Leffler */ 758310ad9a77SSam Leffler if (tudelta != 0 && (tudelta > 0 || -tudelta < sc->sc_tdmabintval)) { 758410ad9a77SSam Leffler ath_tdma_settimers(sc, nextslottu, sc->sc_tdmabintval); 758510ad9a77SSam Leffler sc->sc_stats.ast_tdma_timers++; 758610ad9a77SSam Leffler } 758710ad9a77SSam Leffler if (tsfdelta > 0) { 758810ad9a77SSam Leffler ath_hal_adjusttsf(ah, tsfdelta); 758910ad9a77SSam Leffler sc->sc_stats.ast_tdma_tsf++; 759010ad9a77SSam Leffler } 759110ad9a77SSam Leffler ath_tdma_beacon_send(sc, vap); /* prepare response */ 759210ad9a77SSam Leffler #undef TU_TO_TSF 759310ad9a77SSam Leffler #undef TSF_TO_TU 759410ad9a77SSam Leffler } 759510ad9a77SSam Leffler 759610ad9a77SSam Leffler /* 759710ad9a77SSam Leffler * Transmit a beacon frame at SWBA. Dynamic updates 759810ad9a77SSam Leffler * to the frame contents are done as needed. 759910ad9a77SSam Leffler */ 760010ad9a77SSam Leffler static void 760110ad9a77SSam Leffler ath_tdma_beacon_send(struct ath_softc *sc, struct ieee80211vap *vap) 760210ad9a77SSam Leffler { 760310ad9a77SSam Leffler struct ath_hal *ah = sc->sc_ah; 760410ad9a77SSam Leffler struct ath_buf *bf; 760510ad9a77SSam Leffler int otherant; 760610ad9a77SSam Leffler 760710ad9a77SSam Leffler /* 760810ad9a77SSam Leffler * Check if the previous beacon has gone out. If 760910ad9a77SSam Leffler * not don't try to post another, skip this period 761010ad9a77SSam Leffler * and wait for the next. Missed beacons indicate 761110ad9a77SSam Leffler * a problem and should not occur. If we miss too 761210ad9a77SSam Leffler * many consecutive beacons reset the device. 761310ad9a77SSam Leffler */ 761410ad9a77SSam Leffler if (ath_hal_numtxpending(ah, sc->sc_bhalq) != 0) { 761510ad9a77SSam Leffler sc->sc_bmisscount++; 761610ad9a77SSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON, 761710ad9a77SSam Leffler "%s: missed %u consecutive beacons\n", 761810ad9a77SSam Leffler __func__, sc->sc_bmisscount); 7619a32ac9d3SSam Leffler if (sc->sc_bmisscount >= ath_bstuck_threshold) 762010ad9a77SSam Leffler taskqueue_enqueue(sc->sc_tq, &sc->sc_bstucktask); 762110ad9a77SSam Leffler return; 762210ad9a77SSam Leffler } 762310ad9a77SSam Leffler if (sc->sc_bmisscount != 0) { 762410ad9a77SSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON, 762510ad9a77SSam Leffler "%s: resume beacon xmit after %u misses\n", 762610ad9a77SSam Leffler __func__, sc->sc_bmisscount); 762710ad9a77SSam Leffler sc->sc_bmisscount = 0; 762810ad9a77SSam Leffler } 762910ad9a77SSam Leffler 763010ad9a77SSam Leffler /* 763110ad9a77SSam Leffler * Check recent per-antenna transmit statistics and flip 763210ad9a77SSam Leffler * the default antenna if noticeably more frames went out 763310ad9a77SSam Leffler * on the non-default antenna. 763410ad9a77SSam Leffler * XXX assumes 2 anntenae 763510ad9a77SSam Leffler */ 763610ad9a77SSam Leffler if (!sc->sc_diversity) { 763710ad9a77SSam Leffler otherant = sc->sc_defant & 1 ? 2 : 1; 763810ad9a77SSam Leffler if (sc->sc_ant_tx[otherant] > sc->sc_ant_tx[sc->sc_defant] + 2) 763910ad9a77SSam Leffler ath_setdefantenna(sc, otherant); 764010ad9a77SSam Leffler sc->sc_ant_tx[1] = sc->sc_ant_tx[2] = 0; 764110ad9a77SSam Leffler } 764210ad9a77SSam Leffler 764310ad9a77SSam Leffler bf = ath_beacon_generate(sc, vap); 764410ad9a77SSam Leffler if (bf != NULL) { 764510ad9a77SSam Leffler /* 764610ad9a77SSam Leffler * Stop any current dma and put the new frame on the queue. 764710ad9a77SSam Leffler * This should never fail since we check above that no frames 764810ad9a77SSam Leffler * are still pending on the queue. 764910ad9a77SSam Leffler */ 765010ad9a77SSam Leffler if (!ath_hal_stoptxdma(ah, sc->sc_bhalq)) { 765110ad9a77SSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, 765210ad9a77SSam Leffler "%s: beacon queue %u did not stop?\n", 765310ad9a77SSam Leffler __func__, sc->sc_bhalq); 765410ad9a77SSam Leffler /* NB: the HAL still stops DMA, so proceed */ 765510ad9a77SSam Leffler } 765610ad9a77SSam Leffler ath_hal_puttxbuf(ah, sc->sc_bhalq, bf->bf_daddr); 765710ad9a77SSam Leffler ath_hal_txstart(ah, sc->sc_bhalq); 765810ad9a77SSam Leffler 765910ad9a77SSam Leffler sc->sc_stats.ast_be_xmit++; /* XXX per-vap? */ 766010ad9a77SSam Leffler 766110ad9a77SSam Leffler /* 766210ad9a77SSam Leffler * Record local TSF for our last send for use 766310ad9a77SSam Leffler * in arbitrating slot collisions. 766410ad9a77SSam Leffler */ 766510ad9a77SSam Leffler vap->iv_bss->ni_tstamp.tsf = ath_hal_gettsf64(ah); 766610ad9a77SSam Leffler } 766710ad9a77SSam Leffler } 766810ad9a77SSam Leffler #endif /* ATH_SUPPORT_TDMA */ 7669