15591b213SSam Leffler /*- 2b032f27cSSam Leffler * Copyright (c) 2002-2008 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> 715591b213SSam Leffler 725591b213SSam Leffler #include <net/bpf.h> 735591b213SSam Leffler 745591b213SSam Leffler #ifdef INET 755591b213SSam Leffler #include <netinet/in.h> 765591b213SSam Leffler #include <netinet/if_ether.h> 775591b213SSam Leffler #endif 785591b213SSam Leffler 795591b213SSam Leffler #include <dev/ath/if_athvar.h> 805591b213SSam Leffler #include <contrib/dev/ath/ah_desc.h> 81c42a7b7eSSam Leffler #include <contrib/dev/ath/ah_devid.h> /* XXX for softled */ 825591b213SSam Leffler 8386e07743SSam Leffler #ifdef ATH_TX99_DIAG 8486e07743SSam Leffler #include <dev/ath/ath_tx99/ath_tx99.h> 8586e07743SSam Leffler #endif 8686e07743SSam Leffler 87b032f27cSSam Leffler /* 88b032f27cSSam Leffler * ATH_BCBUF determines the number of vap's that can transmit 89b032f27cSSam Leffler * beacons and also (currently) the number of vap's that can 90b032f27cSSam Leffler * have unique mac addresses/bssid. When staggering beacons 91b032f27cSSam Leffler * 4 is probably a good max as otherwise the beacons become 92b032f27cSSam Leffler * very closely spaced and there is limited time for cab q traffic 93b032f27cSSam Leffler * to go out. You can burst beacons instead but that is not good 94b032f27cSSam Leffler * for stations in power save and at some point you really want 95b032f27cSSam Leffler * another radio (and channel). 96b032f27cSSam Leffler * 97b032f27cSSam Leffler * The limit on the number of mac addresses is tied to our use of 98b032f27cSSam Leffler * the U/L bit and tracking addresses in a byte; it would be 99b032f27cSSam Leffler * worthwhile to allow more for applications like proxy sta. 100b032f27cSSam Leffler */ 101b032f27cSSam Leffler CTASSERT(ATH_BCBUF <= 8); 102b032f27cSSam Leffler 103e8fd88a3SSam Leffler /* unaligned little endian access */ 1045591b213SSam Leffler #define LE_READ_2(p) \ 1055591b213SSam Leffler ((u_int16_t) \ 1065591b213SSam Leffler ((((u_int8_t *)(p))[0] ) | (((u_int8_t *)(p))[1] << 8))) 1075591b213SSam Leffler #define LE_READ_4(p) \ 1085591b213SSam Leffler ((u_int32_t) \ 1095591b213SSam Leffler ((((u_int8_t *)(p))[0] ) | (((u_int8_t *)(p))[1] << 8) | \ 1105591b213SSam Leffler (((u_int8_t *)(p))[2] << 16) | (((u_int8_t *)(p))[3] << 24))) 1115591b213SSam Leffler 1123e50ec2cSSam Leffler enum { 1133e50ec2cSSam Leffler ATH_LED_TX, 1143e50ec2cSSam Leffler ATH_LED_RX, 1153e50ec2cSSam Leffler ATH_LED_POLL, 1163e50ec2cSSam Leffler }; 1173e50ec2cSSam Leffler 118b032f27cSSam Leffler static struct ieee80211vap *ath_vap_create(struct ieee80211com *, 119b032f27cSSam Leffler const char name[IFNAMSIZ], int unit, int opmode, 120b032f27cSSam Leffler int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], 121b032f27cSSam Leffler const uint8_t mac[IEEE80211_ADDR_LEN]); 122b032f27cSSam Leffler static void ath_vap_delete(struct ieee80211vap *); 1235591b213SSam Leffler static void ath_init(void *); 124c42a7b7eSSam Leffler static void ath_stop_locked(struct ifnet *); 1255591b213SSam Leffler static void ath_stop(struct ifnet *); 1265591b213SSam Leffler static void ath_start(struct ifnet *); 127c42a7b7eSSam Leffler static int ath_reset(struct ifnet *); 128b032f27cSSam Leffler static int ath_reset_vap(struct ieee80211vap *, u_long); 1295591b213SSam Leffler static int ath_media_change(struct ifnet *); 1305591b213SSam Leffler static void ath_watchdog(struct ifnet *); 1315591b213SSam Leffler static int ath_ioctl(struct ifnet *, u_long, caddr_t); 1325591b213SSam Leffler static void ath_fatal_proc(void *, int); 1335591b213SSam Leffler static void ath_rxorn_proc(void *, int); 134b032f27cSSam Leffler static void ath_bmiss_vap(struct ieee80211vap *); 1355591b213SSam Leffler static void ath_bmiss_proc(void *, int); 136b032f27cSSam Leffler static int ath_key_alloc(struct ieee80211vap *, 137c1225b52SSam Leffler const struct ieee80211_key *, 138c1225b52SSam Leffler ieee80211_keyix *, ieee80211_keyix *); 139b032f27cSSam Leffler static int ath_key_delete(struct ieee80211vap *, 140c42a7b7eSSam Leffler const struct ieee80211_key *); 141b032f27cSSam Leffler static int ath_key_set(struct ieee80211vap *, const struct ieee80211_key *, 142c42a7b7eSSam Leffler const u_int8_t mac[IEEE80211_ADDR_LEN]); 143b032f27cSSam Leffler static void ath_key_update_begin(struct ieee80211vap *); 144b032f27cSSam Leffler static void ath_key_update_end(struct ieee80211vap *); 145b032f27cSSam Leffler static void ath_update_mcast(struct ifnet *); 146b032f27cSSam Leffler static void ath_update_promisc(struct ifnet *); 1475591b213SSam Leffler static void ath_mode_init(struct ath_softc *); 148c42a7b7eSSam Leffler static void ath_setslottime(struct ath_softc *); 149c42a7b7eSSam Leffler static void ath_updateslot(struct ifnet *); 15080d2765fSSam Leffler static int ath_beaconq_setup(struct ath_hal *); 1515591b213SSam Leffler static int ath_beacon_alloc(struct ath_softc *, struct ieee80211_node *); 152b032f27cSSam Leffler static void ath_beacon_update(struct ieee80211vap *, int item); 153c42a7b7eSSam Leffler static void ath_beacon_setup(struct ath_softc *, struct ath_buf *); 1545591b213SSam Leffler static void ath_beacon_proc(void *, int); 155b032f27cSSam Leffler static struct ath_buf *ath_beacon_generate(struct ath_softc *, 156b032f27cSSam Leffler struct ieee80211vap *); 157c42a7b7eSSam Leffler static void ath_bstuck_proc(void *, int); 158b032f27cSSam Leffler static void ath_beacon_return(struct ath_softc *, struct ath_buf *); 1595591b213SSam Leffler static void ath_beacon_free(struct ath_softc *); 160b032f27cSSam Leffler static void ath_beacon_config(struct ath_softc *, struct ieee80211vap *); 161c42a7b7eSSam Leffler static void ath_descdma_cleanup(struct ath_softc *sc, 162c42a7b7eSSam Leffler struct ath_descdma *, ath_bufhead *); 1635591b213SSam Leffler static int ath_desc_alloc(struct ath_softc *); 1645591b213SSam Leffler static void ath_desc_free(struct ath_softc *); 165c42a7b7eSSam Leffler static struct ieee80211_node *ath_node_alloc(struct ieee80211_node_table *); 166c42a7b7eSSam Leffler static void ath_node_free(struct ieee80211_node *); 16768e8e04eSSam Leffler static void ath_node_getsignal(const struct ieee80211_node *, 16868e8e04eSSam Leffler int8_t *, int8_t *); 1695591b213SSam Leffler static int ath_rxbuf_init(struct ath_softc *, struct ath_buf *); 170b032f27cSSam Leffler static void ath_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, 17168e8e04eSSam Leffler int subtype, int rssi, int noise, u_int32_t rstamp); 172c42a7b7eSSam Leffler static void ath_setdefantenna(struct ath_softc *, u_int); 1735591b213SSam Leffler static void ath_rx_proc(void *, int); 174622b3fd2SSam Leffler static void ath_txq_init(struct ath_softc *sc, struct ath_txq *, int); 175c42a7b7eSSam Leffler static struct ath_txq *ath_txq_setup(struct ath_softc*, int qtype, int subtype); 176c42a7b7eSSam Leffler static int ath_tx_setup(struct ath_softc *, int, int); 177c42a7b7eSSam Leffler static int ath_wme_update(struct ieee80211com *); 178c42a7b7eSSam Leffler static void ath_tx_cleanupq(struct ath_softc *, struct ath_txq *); 179c42a7b7eSSam Leffler static void ath_tx_cleanup(struct ath_softc *); 18068e8e04eSSam Leffler static void ath_freetx(struct mbuf *); 1815591b213SSam Leffler static int ath_tx_start(struct ath_softc *, struct ieee80211_node *, 1825591b213SSam Leffler struct ath_buf *, struct mbuf *); 183c42a7b7eSSam Leffler static void ath_tx_proc_q0(void *, int); 184c42a7b7eSSam Leffler static void ath_tx_proc_q0123(void *, int); 1855591b213SSam Leffler static void ath_tx_proc(void *, int); 186b032f27cSSam Leffler static void ath_tx_draintxq(struct ath_softc *, struct ath_txq *); 1875591b213SSam Leffler static int ath_chan_set(struct ath_softc *, struct ieee80211_channel *); 1885591b213SSam Leffler static void ath_draintxq(struct ath_softc *); 1895591b213SSam Leffler static void ath_stoprecv(struct ath_softc *); 1905591b213SSam Leffler static int ath_startrecv(struct ath_softc *); 191c42a7b7eSSam Leffler static void ath_chan_change(struct ath_softc *, struct ieee80211_channel *); 19268e8e04eSSam Leffler static void ath_scan_start(struct ieee80211com *); 19368e8e04eSSam Leffler static void ath_scan_end(struct ieee80211com *); 19468e8e04eSSam Leffler static void ath_set_channel(struct ieee80211com *); 1955591b213SSam Leffler static void ath_calibrate(void *); 196b032f27cSSam Leffler static int ath_newstate(struct ieee80211vap *, enum ieee80211_state, int); 197e8fd88a3SSam Leffler static void ath_setup_stationkey(struct ieee80211_node *); 198e9962332SSam Leffler static void ath_newassoc(struct ieee80211_node *, int); 199b032f27cSSam Leffler static int ath_setregdomain(struct ieee80211com *, 200b032f27cSSam Leffler struct ieee80211_regdomain *, int, 201b032f27cSSam Leffler struct ieee80211_channel []); 202b032f27cSSam Leffler static void ath_getradiocaps(struct ieee80211com *, int *, 203b032f27cSSam Leffler struct ieee80211_channel []); 204b032f27cSSam Leffler static int ath_getchannels(struct ath_softc *); 2053e50ec2cSSam Leffler static void ath_led_event(struct ath_softc *, int); 2065591b213SSam Leffler 207c42a7b7eSSam Leffler static int ath_rate_setup(struct ath_softc *, u_int mode); 2085591b213SSam Leffler static void ath_setcurmode(struct ath_softc *, enum ieee80211_phymode); 209c42a7b7eSSam Leffler 210c42a7b7eSSam Leffler static void ath_sysctlattach(struct ath_softc *); 211664443d0SSam Leffler static int ath_raw_xmit(struct ieee80211_node *, 212664443d0SSam Leffler struct mbuf *, const struct ieee80211_bpf_params *); 213c42a7b7eSSam Leffler static void ath_bpfattach(struct ath_softc *); 214c42a7b7eSSam Leffler static void ath_announce(struct ath_softc *); 2155591b213SSam Leffler 2165591b213SSam Leffler SYSCTL_DECL(_hw_ath); 2175591b213SSam Leffler 2185591b213SSam Leffler /* XXX validate sysctl values */ 2195591b213SSam Leffler static int ath_calinterval = 30; /* calibrate every 30 secs */ 2205591b213SSam Leffler SYSCTL_INT(_hw_ath, OID_AUTO, calibrate, CTLFLAG_RW, &ath_calinterval, 2215591b213SSam Leffler 0, "chip calibration interval (secs)"); 2225591b213SSam Leffler 223e2d787faSSam Leffler static int ath_rxbuf = ATH_RXBUF; /* # rx buffers to allocate */ 224aaa70f2fSSam Leffler SYSCTL_INT(_hw_ath, OID_AUTO, rxbuf, CTLFLAG_RW, &ath_rxbuf, 225e2d787faSSam Leffler 0, "rx buffers allocated"); 226e2d787faSSam Leffler TUNABLE_INT("hw.ath.rxbuf", &ath_rxbuf); 227e2d787faSSam Leffler static int ath_txbuf = ATH_TXBUF; /* # tx buffers to allocate */ 228aaa70f2fSSam Leffler SYSCTL_INT(_hw_ath, OID_AUTO, txbuf, CTLFLAG_RW, &ath_txbuf, 229e2d787faSSam Leffler 0, "tx buffers allocated"); 230e2d787faSSam Leffler TUNABLE_INT("hw.ath.txbuf", &ath_txbuf); 231e2d787faSSam Leffler 232a585a9a1SSam Leffler #ifdef ATH_DEBUG 233c42a7b7eSSam Leffler static int ath_debug = 0; 2345591b213SSam Leffler SYSCTL_INT(_hw_ath, OID_AUTO, debug, CTLFLAG_RW, &ath_debug, 2355591b213SSam Leffler 0, "control debugging printfs"); 236f3be7956SSam Leffler TUNABLE_INT("hw.ath.debug", &ath_debug); 237e325e530SSam Leffler enum { 238e325e530SSam Leffler ATH_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ 239e325e530SSam Leffler ATH_DEBUG_XMIT_DESC = 0x00000002, /* xmit descriptors */ 240e325e530SSam Leffler ATH_DEBUG_RECV = 0x00000004, /* basic recv operation */ 241e325e530SSam Leffler ATH_DEBUG_RECV_DESC = 0x00000008, /* recv descriptors */ 242e325e530SSam Leffler ATH_DEBUG_RATE = 0x00000010, /* rate control */ 243e325e530SSam Leffler ATH_DEBUG_RESET = 0x00000020, /* reset processing */ 244e325e530SSam Leffler ATH_DEBUG_MODE = 0x00000040, /* mode init/setup */ 245e325e530SSam Leffler ATH_DEBUG_BEACON = 0x00000080, /* beacon handling */ 246e325e530SSam Leffler ATH_DEBUG_WATCHDOG = 0x00000100, /* watchdog timeout */ 247e325e530SSam Leffler ATH_DEBUG_INTR = 0x00001000, /* ISR */ 248e325e530SSam Leffler ATH_DEBUG_TX_PROC = 0x00002000, /* tx ISR proc */ 249e325e530SSam Leffler ATH_DEBUG_RX_PROC = 0x00004000, /* rx ISR proc */ 250e325e530SSam Leffler ATH_DEBUG_BEACON_PROC = 0x00008000, /* beacon ISR proc */ 251e325e530SSam Leffler ATH_DEBUG_CALIBRATE = 0x00010000, /* periodic calibration */ 252c42a7b7eSSam Leffler ATH_DEBUG_KEYCACHE = 0x00020000, /* key cache management */ 253c42a7b7eSSam Leffler ATH_DEBUG_STATE = 0x00040000, /* 802.11 state transitions */ 254c42a7b7eSSam Leffler ATH_DEBUG_NODE = 0x00080000, /* node management */ 2553e50ec2cSSam Leffler ATH_DEBUG_LED = 0x00100000, /* led management */ 256bd5a9920SSam Leffler ATH_DEBUG_FF = 0x00200000, /* fast frames */ 257bd5a9920SSam Leffler ATH_DEBUG_DFS = 0x00400000, /* DFS processing */ 258c42a7b7eSSam Leffler ATH_DEBUG_FATAL = 0x80000000, /* fatal errors */ 259e325e530SSam Leffler ATH_DEBUG_ANY = 0xffffffff 260e325e530SSam Leffler }; 261c42a7b7eSSam Leffler #define IFF_DUMPPKTS(sc, m) \ 2620a1b94c4SSam Leffler ((sc->sc_debug & (m)) || \ 263fc74a9f9SBrooks Davis (sc->sc_ifp->if_flags & (IFF_DEBUG|IFF_LINK2)) == (IFF_DEBUG|IFF_LINK2)) 264c42a7b7eSSam Leffler #define DPRINTF(sc, m, fmt, ...) do { \ 2650a1b94c4SSam Leffler if (sc->sc_debug & (m)) \ 266c42a7b7eSSam Leffler printf(fmt, __VA_ARGS__); \ 267c42a7b7eSSam Leffler } while (0) 268c42a7b7eSSam Leffler #define KEYPRINTF(sc, ix, hk, mac) do { \ 269c42a7b7eSSam Leffler if (sc->sc_debug & ATH_DEBUG_KEYCACHE) \ 2705901d2d3SSam Leffler ath_keyprint(sc, __func__, ix, hk, mac); \ 271c42a7b7eSSam Leffler } while (0) 27265f9edeeSSam Leffler static void ath_printrxbuf(const struct ath_buf *bf, u_int ix, int); 27365f9edeeSSam Leffler static void ath_printtxbuf(const struct ath_buf *bf, u_int qnum, u_int ix, int done); 2745591b213SSam Leffler #else 275c42a7b7eSSam Leffler #define IFF_DUMPPKTS(sc, m) \ 276fc74a9f9SBrooks Davis ((sc->sc_ifp->if_flags & (IFF_DEBUG|IFF_LINK2)) == (IFF_DEBUG|IFF_LINK2)) 277d2f6ed15SSam Leffler #define DPRINTF(sc, m, fmt, ...) do { \ 278d2f6ed15SSam Leffler (void) sc; \ 279d2f6ed15SSam Leffler } while (0) 280d2f6ed15SSam Leffler #define KEYPRINTF(sc, k, ix, mac) do { \ 281d2f6ed15SSam Leffler (void) sc; \ 282d2f6ed15SSam Leffler } while (0) 2835591b213SSam Leffler #endif 2845591b213SSam Leffler 285c42a7b7eSSam Leffler MALLOC_DEFINE(M_ATHDEV, "athdev", "ath driver dma buffers"); 286c42a7b7eSSam Leffler 2875591b213SSam Leffler int 2885591b213SSam Leffler ath_attach(u_int16_t devid, struct ath_softc *sc) 2895591b213SSam Leffler { 290fc74a9f9SBrooks Davis struct ifnet *ifp; 291b032f27cSSam Leffler struct ieee80211com *ic; 292fc74a9f9SBrooks Davis struct ath_hal *ah = NULL; 2935591b213SSam Leffler HAL_STATUS status; 294c42a7b7eSSam Leffler int error = 0, i; 2955591b213SSam Leffler 296c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: devid 0x%x\n", __func__, devid); 2975591b213SSam Leffler 298b032f27cSSam Leffler ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); 299fc74a9f9SBrooks Davis if (ifp == NULL) { 300fc74a9f9SBrooks Davis device_printf(sc->sc_dev, "can not if_alloc()\n"); 301fc74a9f9SBrooks Davis error = ENOSPC; 302fc74a9f9SBrooks Davis goto bad; 303fc74a9f9SBrooks Davis } 304b032f27cSSam Leffler ic = ifp->if_l2com; 305fc74a9f9SBrooks Davis 3065591b213SSam Leffler /* set these up early for if_printf use */ 3079bf40edeSBrooks Davis if_initname(ifp, device_get_name(sc->sc_dev), 3089bf40edeSBrooks Davis device_get_unit(sc->sc_dev)); 3095591b213SSam Leffler 310f9fc583fSSam Leffler ah = ath_hal_attach(devid, sc, sc->sc_st, sc->sc_sh, &status); 3115591b213SSam Leffler if (ah == NULL) { 3125591b213SSam Leffler if_printf(ifp, "unable to attach hardware; HAL status %u\n", 3135591b213SSam Leffler status); 3145591b213SSam Leffler error = ENXIO; 3155591b213SSam Leffler goto bad; 3165591b213SSam Leffler } 31785bdc65aSSam Leffler if (ah->ah_abi != HAL_ABI_VERSION) { 318c42a7b7eSSam Leffler if_printf(ifp, "HAL ABI mismatch detected " 319c42a7b7eSSam Leffler "(HAL:0x%x != driver:0x%x)\n", 32085bdc65aSSam Leffler ah->ah_abi, HAL_ABI_VERSION); 32185bdc65aSSam Leffler error = ENXIO; 32285bdc65aSSam Leffler goto bad; 32385bdc65aSSam Leffler } 3245591b213SSam Leffler sc->sc_ah = ah; 325b58b3803SSam Leffler sc->sc_invalid = 0; /* ready to go, enable interrupt handling */ 3265591b213SSam Leffler 3275591b213SSam Leffler /* 328c42a7b7eSSam Leffler * Check if the MAC has multi-rate retry support. 329c42a7b7eSSam Leffler * We do this by trying to setup a fake extended 330c42a7b7eSSam Leffler * descriptor. MAC's that don't have support will 331c42a7b7eSSam Leffler * return false w/o doing anything. MAC's that do 332c42a7b7eSSam Leffler * support it will return true w/o doing anything. 333c42a7b7eSSam Leffler */ 334c42a7b7eSSam Leffler sc->sc_mrretry = ath_hal_setupxtxdesc(ah, NULL, 0,0, 0,0, 0,0); 335c42a7b7eSSam Leffler 336c42a7b7eSSam Leffler /* 337c42a7b7eSSam Leffler * Check if the device has hardware counters for PHY 338c42a7b7eSSam Leffler * errors. If so we need to enable the MIB interrupt 339c42a7b7eSSam Leffler * so we can act on stat triggers. 340c42a7b7eSSam Leffler */ 341c42a7b7eSSam Leffler if (ath_hal_hwphycounters(ah)) 342c42a7b7eSSam Leffler sc->sc_needmib = 1; 343c42a7b7eSSam Leffler 344c42a7b7eSSam Leffler /* 345c42a7b7eSSam Leffler * Get the hardware key cache size. 346c42a7b7eSSam Leffler */ 347c42a7b7eSSam Leffler sc->sc_keymax = ath_hal_keycachesize(ah); 348e8fd88a3SSam Leffler if (sc->sc_keymax > ATH_KEYMAX) { 349e8fd88a3SSam Leffler if_printf(ifp, "Warning, using only %u of %u key cache slots\n", 350e8fd88a3SSam Leffler ATH_KEYMAX, sc->sc_keymax); 351e8fd88a3SSam Leffler sc->sc_keymax = ATH_KEYMAX; 352c42a7b7eSSam Leffler } 353c42a7b7eSSam Leffler /* 354c42a7b7eSSam Leffler * Reset the key cache since some parts do not 355c42a7b7eSSam Leffler * reset the contents on initial power up. 356c42a7b7eSSam Leffler */ 357c42a7b7eSSam Leffler for (i = 0; i < sc->sc_keymax; i++) 358c42a7b7eSSam Leffler ath_hal_keyreset(ah, i); 359c42a7b7eSSam Leffler 360c42a7b7eSSam Leffler /* 361b032f27cSSam Leffler * Collect the default channel list. 3625591b213SSam Leffler */ 363b032f27cSSam Leffler error = ath_getchannels(sc); 3645591b213SSam Leffler if (error != 0) 3655591b213SSam Leffler goto bad; 3665591b213SSam Leffler 3675591b213SSam Leffler /* 3685591b213SSam Leffler * Setup rate tables for all potential media types. 3695591b213SSam Leffler */ 3705591b213SSam Leffler ath_rate_setup(sc, IEEE80211_MODE_11A); 3715591b213SSam Leffler ath_rate_setup(sc, IEEE80211_MODE_11B); 3725591b213SSam Leffler ath_rate_setup(sc, IEEE80211_MODE_11G); 373c42a7b7eSSam Leffler ath_rate_setup(sc, IEEE80211_MODE_TURBO_A); 374c42a7b7eSSam Leffler ath_rate_setup(sc, IEEE80211_MODE_TURBO_G); 37568e8e04eSSam Leffler ath_rate_setup(sc, IEEE80211_MODE_STURBO_A); 37668e8e04eSSam Leffler ath_rate_setup(sc, IEEE80211_MODE_11NA); 37768e8e04eSSam Leffler ath_rate_setup(sc, IEEE80211_MODE_11NG); 378724c193aSSam Leffler ath_rate_setup(sc, IEEE80211_MODE_HALF); 379724c193aSSam Leffler ath_rate_setup(sc, IEEE80211_MODE_QUARTER); 380aaa70f2fSSam Leffler 381c42a7b7eSSam Leffler /* NB: setup here so ath_rate_update is happy */ 382c42a7b7eSSam Leffler ath_setcurmode(sc, IEEE80211_MODE_11A); 3835591b213SSam Leffler 384c42a7b7eSSam Leffler /* 385c42a7b7eSSam Leffler * Allocate tx+rx descriptors and populate the lists. 386c42a7b7eSSam Leffler */ 3875591b213SSam Leffler error = ath_desc_alloc(sc); 3885591b213SSam Leffler if (error != 0) { 3895591b213SSam Leffler if_printf(ifp, "failed to allocate descriptors: %d\n", error); 3905591b213SSam Leffler goto bad; 3915591b213SSam Leffler } 3922274d8c8SSam Leffler callout_init(&sc->sc_cal_ch, CALLOUT_MPSAFE); 3935591b213SSam Leffler 394f0b2a0beSSam Leffler ATH_TXBUF_LOCK_INIT(sc); 3955591b213SSam Leffler 3960bbf5441SSam Leffler sc->sc_tq = taskqueue_create("ath_taskq", M_NOWAIT, 3970bbf5441SSam Leffler taskqueue_thread_enqueue, &sc->sc_tq); 3980bbf5441SSam Leffler taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, 3990bbf5441SSam Leffler "%s taskq", ifp->if_xname); 4000bbf5441SSam Leffler 4015591b213SSam Leffler TASK_INIT(&sc->sc_rxtask, 0, ath_rx_proc, sc); 4025591b213SSam Leffler TASK_INIT(&sc->sc_rxorntask, 0, ath_rxorn_proc, sc); 4035591b213SSam Leffler TASK_INIT(&sc->sc_bmisstask, 0, ath_bmiss_proc, sc); 404c42a7b7eSSam Leffler TASK_INIT(&sc->sc_bstucktask,0, ath_bstuck_proc, sc); 4055591b213SSam Leffler 4065591b213SSam Leffler /* 407c42a7b7eSSam Leffler * Allocate hardware transmit queues: one queue for 408c42a7b7eSSam Leffler * beacon frames and one data queue for each QoS 409c42a7b7eSSam Leffler * priority. Note that the hal handles reseting 410c42a7b7eSSam Leffler * these queues at the needed time. 411c42a7b7eSSam Leffler * 412c42a7b7eSSam Leffler * XXX PS-Poll 4135591b213SSam Leffler */ 41480d2765fSSam Leffler sc->sc_bhalq = ath_beaconq_setup(ah); 4155591b213SSam Leffler if (sc->sc_bhalq == (u_int) -1) { 4165591b213SSam Leffler if_printf(ifp, "unable to setup a beacon xmit queue!\n"); 417c42a7b7eSSam Leffler error = EIO; 418b28b4653SSam Leffler goto bad2; 4195591b213SSam Leffler } 420c42a7b7eSSam Leffler sc->sc_cabq = ath_txq_setup(sc, HAL_TX_QUEUE_CAB, 0); 421c42a7b7eSSam Leffler if (sc->sc_cabq == NULL) { 422c42a7b7eSSam Leffler if_printf(ifp, "unable to setup CAB xmit queue!\n"); 423c42a7b7eSSam Leffler error = EIO; 424c42a7b7eSSam Leffler goto bad2; 425c42a7b7eSSam Leffler } 426c42a7b7eSSam Leffler /* NB: insure BK queue is the lowest priority h/w queue */ 427c42a7b7eSSam Leffler if (!ath_tx_setup(sc, WME_AC_BK, HAL_WME_AC_BK)) { 428c42a7b7eSSam Leffler if_printf(ifp, "unable to setup xmit queue for %s traffic!\n", 429c42a7b7eSSam Leffler ieee80211_wme_acnames[WME_AC_BK]); 430c42a7b7eSSam Leffler error = EIO; 431c42a7b7eSSam Leffler goto bad2; 432c42a7b7eSSam Leffler } 433c42a7b7eSSam Leffler if (!ath_tx_setup(sc, WME_AC_BE, HAL_WME_AC_BE) || 434c42a7b7eSSam Leffler !ath_tx_setup(sc, WME_AC_VI, HAL_WME_AC_VI) || 435c42a7b7eSSam Leffler !ath_tx_setup(sc, WME_AC_VO, HAL_WME_AC_VO)) { 436c42a7b7eSSam Leffler /* 437c42a7b7eSSam Leffler * Not enough hardware tx queues to properly do WME; 438c42a7b7eSSam Leffler * just punt and assign them all to the same h/w queue. 439c42a7b7eSSam Leffler * We could do a better job of this if, for example, 440c42a7b7eSSam Leffler * we allocate queues when we switch from station to 441c42a7b7eSSam Leffler * AP mode. 442c42a7b7eSSam Leffler */ 443c42a7b7eSSam Leffler if (sc->sc_ac2q[WME_AC_VI] != NULL) 444c42a7b7eSSam Leffler ath_tx_cleanupq(sc, sc->sc_ac2q[WME_AC_VI]); 445c42a7b7eSSam Leffler if (sc->sc_ac2q[WME_AC_BE] != NULL) 446c42a7b7eSSam Leffler ath_tx_cleanupq(sc, sc->sc_ac2q[WME_AC_BE]); 447c42a7b7eSSam Leffler sc->sc_ac2q[WME_AC_BE] = sc->sc_ac2q[WME_AC_BK]; 448c42a7b7eSSam Leffler sc->sc_ac2q[WME_AC_VI] = sc->sc_ac2q[WME_AC_BK]; 449c42a7b7eSSam Leffler sc->sc_ac2q[WME_AC_VO] = sc->sc_ac2q[WME_AC_BK]; 450c42a7b7eSSam Leffler } 451c42a7b7eSSam Leffler 452c42a7b7eSSam Leffler /* 453c42a7b7eSSam Leffler * Special case certain configurations. Note the 454c42a7b7eSSam Leffler * CAB queue is handled by these specially so don't 455c42a7b7eSSam Leffler * include them when checking the txq setup mask. 456c42a7b7eSSam Leffler */ 457c42a7b7eSSam Leffler switch (sc->sc_txqsetup &~ (1<<sc->sc_cabq->axq_qnum)) { 458c42a7b7eSSam Leffler case 0x01: 459c42a7b7eSSam Leffler TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc_q0, sc); 460c42a7b7eSSam Leffler break; 461c42a7b7eSSam Leffler case 0x0f: 462c42a7b7eSSam Leffler TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc_q0123, sc); 463c42a7b7eSSam Leffler break; 464c42a7b7eSSam Leffler default: 465c42a7b7eSSam Leffler TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc, sc); 466c42a7b7eSSam Leffler break; 467c42a7b7eSSam Leffler } 468c42a7b7eSSam Leffler 469c42a7b7eSSam Leffler /* 470c42a7b7eSSam Leffler * Setup rate control. Some rate control modules 471c42a7b7eSSam Leffler * call back to change the anntena state so expose 472c42a7b7eSSam Leffler * the necessary entry points. 473c42a7b7eSSam Leffler * XXX maybe belongs in struct ath_ratectrl? 474c42a7b7eSSam Leffler */ 475c42a7b7eSSam Leffler sc->sc_setdefantenna = ath_setdefantenna; 476c42a7b7eSSam Leffler sc->sc_rc = ath_rate_attach(sc); 477c42a7b7eSSam Leffler if (sc->sc_rc == NULL) { 478c42a7b7eSSam Leffler error = EIO; 479c42a7b7eSSam Leffler goto bad2; 480c42a7b7eSSam Leffler } 481c42a7b7eSSam Leffler 4823e50ec2cSSam Leffler sc->sc_blinking = 0; 483c42a7b7eSSam Leffler sc->sc_ledstate = 1; 4843e50ec2cSSam Leffler sc->sc_ledon = 0; /* low true */ 4853e50ec2cSSam Leffler sc->sc_ledidle = (2700*hz)/1000; /* 2.7sec */ 4863e50ec2cSSam Leffler callout_init(&sc->sc_ledtimer, CALLOUT_MPSAFE); 487c42a7b7eSSam Leffler /* 488c42a7b7eSSam Leffler * Auto-enable soft led processing for IBM cards and for 489c42a7b7eSSam Leffler * 5211 minipci cards. Users can also manually enable/disable 490c42a7b7eSSam Leffler * support with a sysctl. 491c42a7b7eSSam Leffler */ 492c42a7b7eSSam Leffler sc->sc_softled = (devid == AR5212_DEVID_IBM || devid == AR5211_DEVID); 493c42a7b7eSSam Leffler if (sc->sc_softled) { 494c42a7b7eSSam Leffler ath_hal_gpioCfgOutput(ah, sc->sc_ledpin); 4953e50ec2cSSam Leffler ath_hal_gpioset(ah, sc->sc_ledpin, !sc->sc_ledon); 496c42a7b7eSSam Leffler } 4975591b213SSam Leffler 4985591b213SSam Leffler ifp->if_softc = sc; 4995591b213SSam Leffler ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; 5005591b213SSam Leffler ifp->if_start = ath_start; 5015591b213SSam Leffler ifp->if_watchdog = ath_watchdog; 5025591b213SSam Leffler ifp->if_ioctl = ath_ioctl; 5035591b213SSam Leffler ifp->if_init = ath_init; 504154b8df2SMax Laier IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); 505154b8df2SMax Laier ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; 506154b8df2SMax Laier IFQ_SET_READY(&ifp->if_snd); 5075591b213SSam Leffler 508c42a7b7eSSam Leffler ic->ic_ifp = ifp; 5095591b213SSam Leffler /* XXX not right but it's not used anywhere important */ 5105591b213SSam Leffler ic->ic_phytype = IEEE80211_T_OFDM; 5115591b213SSam Leffler ic->ic_opmode = IEEE80211_M_STA; 512c42a7b7eSSam Leffler ic->ic_caps = 513c42a7b7eSSam Leffler IEEE80211_C_IBSS /* ibss, nee adhoc, mode */ 514fe32c3efSSam Leffler | IEEE80211_C_HOSTAP /* hostap mode */ 515fe32c3efSSam Leffler | IEEE80211_C_MONITOR /* monitor mode */ 5167a04dc27SSam Leffler | IEEE80211_C_AHDEMO /* adhoc demo mode */ 517b032f27cSSam Leffler | IEEE80211_C_WDS /* 4-address traffic works */ 518fe32c3efSSam Leffler | IEEE80211_C_SHPREAMBLE /* short preamble supported */ 519c42a7b7eSSam Leffler | IEEE80211_C_SHSLOT /* short slot time supported */ 520c42a7b7eSSam Leffler | IEEE80211_C_WPA /* capable of WPA1+WPA2 */ 52168e8e04eSSam Leffler | IEEE80211_C_BGSCAN /* capable of bg scanning */ 52268e8e04eSSam Leffler | IEEE80211_C_TXFRAG /* handle tx frags */ 52301e7e035SSam Leffler ; 524c42a7b7eSSam Leffler /* 525c42a7b7eSSam Leffler * Query the hal to figure out h/w crypto support. 526c42a7b7eSSam Leffler */ 527c42a7b7eSSam Leffler if (ath_hal_ciphersupported(ah, HAL_CIPHER_WEP)) 528b032f27cSSam Leffler ic->ic_cryptocaps |= IEEE80211_CRYPTO_WEP; 529c42a7b7eSSam Leffler if (ath_hal_ciphersupported(ah, HAL_CIPHER_AES_OCB)) 530b032f27cSSam Leffler ic->ic_cryptocaps |= IEEE80211_CRYPTO_AES_OCB; 531c42a7b7eSSam Leffler if (ath_hal_ciphersupported(ah, HAL_CIPHER_AES_CCM)) 532b032f27cSSam Leffler ic->ic_cryptocaps |= IEEE80211_CRYPTO_AES_CCM; 533c42a7b7eSSam Leffler if (ath_hal_ciphersupported(ah, HAL_CIPHER_CKIP)) 534b032f27cSSam Leffler ic->ic_cryptocaps |= IEEE80211_CRYPTO_CKIP; 535c42a7b7eSSam Leffler if (ath_hal_ciphersupported(ah, HAL_CIPHER_TKIP)) { 536b032f27cSSam Leffler ic->ic_cryptocaps |= IEEE80211_CRYPTO_TKIP; 537c42a7b7eSSam Leffler /* 538c42a7b7eSSam Leffler * Check if h/w does the MIC and/or whether the 539c42a7b7eSSam Leffler * separate key cache entries are required to 540c42a7b7eSSam Leffler * handle both tx+rx MIC keys. 541c42a7b7eSSam Leffler */ 542c42a7b7eSSam Leffler if (ath_hal_ciphersupported(ah, HAL_CIPHER_MIC)) 543b032f27cSSam Leffler ic->ic_cryptocaps |= IEEE80211_CRYPTO_TKIPMIC; 5445901d2d3SSam Leffler /* 5455901d2d3SSam Leffler * If the h/w supports storing tx+rx MIC keys 5465901d2d3SSam Leffler * in one cache slot automatically enable use. 5475901d2d3SSam Leffler */ 5485901d2d3SSam Leffler if (ath_hal_hastkipsplit(ah) || 5495901d2d3SSam Leffler !ath_hal_settkipsplit(ah, AH_FALSE)) 550c42a7b7eSSam Leffler sc->sc_splitmic = 1; 551b032f27cSSam Leffler /* 552b032f27cSSam Leffler * If the h/w can do TKIP MIC together with WME then 553b032f27cSSam Leffler * we use it; otherwise we force the MIC to be done 554b032f27cSSam Leffler * in software by the net80211 layer. 555b032f27cSSam Leffler */ 556b032f27cSSam Leffler if (ath_hal_haswmetkipmic(ah)) 557b032f27cSSam Leffler sc->sc_wmetkipmic = 1; 558c42a7b7eSSam Leffler } 559e8fd88a3SSam Leffler sc->sc_hasclrkey = ath_hal_ciphersupported(ah, HAL_CIPHER_CLR); 560e8fd88a3SSam Leffler sc->sc_mcastkey = ath_hal_getmcastkeysearch(ah); 561c42a7b7eSSam Leffler /* 5625901d2d3SSam Leffler * Mark key cache slots associated with global keys 5635901d2d3SSam Leffler * as in use. If we knew TKIP was not to be used we 5645901d2d3SSam Leffler * could leave the +32, +64, and +32+64 slots free. 5655901d2d3SSam Leffler */ 5665901d2d3SSam Leffler for (i = 0; i < IEEE80211_WEP_NKID; i++) { 5675901d2d3SSam Leffler setbit(sc->sc_keymap, i); 5685901d2d3SSam Leffler setbit(sc->sc_keymap, i+64); 5695901d2d3SSam Leffler if (sc->sc_splitmic) { 5705901d2d3SSam Leffler setbit(sc->sc_keymap, i+32); 5715901d2d3SSam Leffler setbit(sc->sc_keymap, i+32+64); 5725901d2d3SSam Leffler } 5735901d2d3SSam Leffler } 5745901d2d3SSam Leffler /* 575c42a7b7eSSam Leffler * TPC support can be done either with a global cap or 576c42a7b7eSSam Leffler * per-packet support. The latter is not available on 577c42a7b7eSSam Leffler * all parts. We're a bit pedantic here as all parts 578c42a7b7eSSam Leffler * support a global cap. 579c42a7b7eSSam Leffler */ 580c59005e9SSam Leffler if (ath_hal_hastpc(ah) || ath_hal_hastxpowlimit(ah)) 581c42a7b7eSSam Leffler ic->ic_caps |= IEEE80211_C_TXPMGT; 582c42a7b7eSSam Leffler 583c42a7b7eSSam Leffler /* 584c42a7b7eSSam Leffler * Mark WME capability only if we have sufficient 585c42a7b7eSSam Leffler * hardware queues to do proper priority scheduling. 586c42a7b7eSSam Leffler */ 587c42a7b7eSSam Leffler if (sc->sc_ac2q[WME_AC_BE] != sc->sc_ac2q[WME_AC_BK]) 588c42a7b7eSSam Leffler ic->ic_caps |= IEEE80211_C_WME; 589c42a7b7eSSam Leffler /* 590e8fd88a3SSam Leffler * Check for misc other capabilities. 591c42a7b7eSSam Leffler */ 592c42a7b7eSSam Leffler if (ath_hal_hasbursting(ah)) 593c42a7b7eSSam Leffler ic->ic_caps |= IEEE80211_C_BURST; 594b032f27cSSam Leffler sc->sc_hasbmask = ath_hal_hasbssidmask(ah); 595b032f27cSSam Leffler sc->sc_hastsfadd = ath_hal_hastsfadjust(ah); 59668e8e04eSSam Leffler if (ath_hal_hasfastframes(ah)) 59768e8e04eSSam Leffler ic->ic_caps |= IEEE80211_C_FF; 598b032f27cSSam Leffler if (ath_hal_getwirelessmodes(ah, ic->ic_regdomain.country) & (HAL_MODE_108G|HAL_MODE_TURBO)) 59968e8e04eSSam Leffler ic->ic_caps |= IEEE80211_C_TURBOP; 600c42a7b7eSSam Leffler 601c42a7b7eSSam Leffler /* 602c42a7b7eSSam Leffler * Indicate we need the 802.11 header padded to a 603c42a7b7eSSam Leffler * 32-bit boundary for 4-address and QoS frames. 604c42a7b7eSSam Leffler */ 605c42a7b7eSSam Leffler ic->ic_flags |= IEEE80211_F_DATAPAD; 606c42a7b7eSSam Leffler 607c42a7b7eSSam Leffler /* 608c42a7b7eSSam Leffler * Query the hal about antenna support. 609c42a7b7eSSam Leffler */ 610c42a7b7eSSam Leffler sc->sc_defant = ath_hal_getdefantenna(ah); 611c42a7b7eSSam Leffler 612c42a7b7eSSam Leffler /* 613c42a7b7eSSam Leffler * Not all chips have the VEOL support we want to 614c42a7b7eSSam Leffler * use with IBSS beacons; check here for it. 615c42a7b7eSSam Leffler */ 616c42a7b7eSSam Leffler sc->sc_hasveol = ath_hal_hasveol(ah); 6175591b213SSam Leffler 6185591b213SSam Leffler /* get mac address from hardware */ 6195591b213SSam Leffler ath_hal_getmac(ah, ic->ic_myaddr); 620b032f27cSSam Leffler if (sc->sc_hasbmask) 621b032f27cSSam Leffler ath_hal_getbssidmask(ah, sc->sc_hwbssidmask); 6225591b213SSam Leffler 623b032f27cSSam Leffler /* NB: used to size node table key mapping array */ 624b032f27cSSam Leffler ic->ic_max_keyix = sc->sc_keymax; 6255591b213SSam Leffler /* call MI attach routine. */ 626c42a7b7eSSam Leffler ieee80211_ifattach(ic); 627b032f27cSSam Leffler ic->ic_setregdomain = ath_setregdomain; 628b032f27cSSam Leffler ic->ic_getradiocaps = ath_getradiocaps; 629b032f27cSSam Leffler sc->sc_opmode = HAL_M_STA; 630b032f27cSSam Leffler 6315591b213SSam Leffler /* override default methods */ 632b032f27cSSam Leffler ic->ic_newassoc = ath_newassoc; 633b032f27cSSam Leffler ic->ic_updateslot = ath_updateslot; 634b032f27cSSam Leffler ic->ic_wme.wme_update = ath_wme_update; 635b032f27cSSam Leffler ic->ic_vap_create = ath_vap_create; 636b032f27cSSam Leffler ic->ic_vap_delete = ath_vap_delete; 637b032f27cSSam Leffler ic->ic_raw_xmit = ath_raw_xmit; 638b032f27cSSam Leffler ic->ic_update_mcast = ath_update_mcast; 639b032f27cSSam Leffler ic->ic_update_promisc = ath_update_promisc; 6405591b213SSam Leffler ic->ic_node_alloc = ath_node_alloc; 6411e774079SSam Leffler sc->sc_node_free = ic->ic_node_free; 6425591b213SSam Leffler ic->ic_node_free = ath_node_free; 64368e8e04eSSam Leffler ic->ic_node_getsignal = ath_node_getsignal; 64468e8e04eSSam Leffler ic->ic_scan_start = ath_scan_start; 64568e8e04eSSam Leffler ic->ic_scan_end = ath_scan_end; 64668e8e04eSSam Leffler ic->ic_set_channel = ath_set_channel; 6475591b213SSam Leffler 648c42a7b7eSSam Leffler ath_bpfattach(sc); 6494866e6c2SSam Leffler /* 6504866e6c2SSam Leffler * Setup dynamic sysctl's now that country code and 6514866e6c2SSam Leffler * regdomain are available from the hal. 6524866e6c2SSam Leffler */ 6534866e6c2SSam Leffler ath_sysctlattach(sc); 65473454c73SSam Leffler 655c42a7b7eSSam Leffler if (bootverbose) 656c42a7b7eSSam Leffler ieee80211_announce(ic); 657c42a7b7eSSam Leffler ath_announce(sc); 6585591b213SSam Leffler return 0; 659b28b4653SSam Leffler bad2: 660c42a7b7eSSam Leffler ath_tx_cleanup(sc); 661b28b4653SSam Leffler ath_desc_free(sc); 6625591b213SSam Leffler bad: 6635591b213SSam Leffler if (ah) 6645591b213SSam Leffler ath_hal_detach(ah); 665fc74a9f9SBrooks Davis if (ifp != NULL) 666fc74a9f9SBrooks Davis if_free(ifp); 6675591b213SSam Leffler sc->sc_invalid = 1; 6685591b213SSam Leffler return error; 6695591b213SSam Leffler } 6705591b213SSam Leffler 6715591b213SSam Leffler int 6725591b213SSam Leffler ath_detach(struct ath_softc *sc) 6735591b213SSam Leffler { 674fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 6755591b213SSam Leffler 676c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n", 677c42a7b7eSSam Leffler __func__, ifp->if_flags); 6785591b213SSam Leffler 6795591b213SSam Leffler ath_stop(ifp); 68073454c73SSam Leffler bpfdetach(ifp); 681c42a7b7eSSam Leffler /* 682c42a7b7eSSam Leffler * NB: the order of these is important: 683c42a7b7eSSam Leffler * o call the 802.11 layer before detaching the hal to 684c42a7b7eSSam Leffler * insure callbacks into the driver to delete global 685c42a7b7eSSam Leffler * key cache entries can be handled 686c42a7b7eSSam Leffler * o reclaim the tx queue data structures after calling 687c42a7b7eSSam Leffler * the 802.11 layer as we'll get called back to reclaim 688c42a7b7eSSam Leffler * node state and potentially want to use them 689c42a7b7eSSam Leffler * o to cleanup the tx queues the hal is called, so detach 690c42a7b7eSSam Leffler * it last 691c42a7b7eSSam Leffler * Other than that, it's straightforward... 692c42a7b7eSSam Leffler */ 693b032f27cSSam Leffler ieee80211_ifdetach(ifp->if_l2com); 69486e07743SSam Leffler #ifdef ATH_TX99_DIAG 69586e07743SSam Leffler if (sc->sc_tx99 != NULL) 69686e07743SSam Leffler sc->sc_tx99->detach(sc->sc_tx99); 69786e07743SSam Leffler #endif 6980bbf5441SSam Leffler taskqueue_free(sc->sc_tq); 699c42a7b7eSSam Leffler ath_rate_detach(sc->sc_rc); 7005591b213SSam Leffler ath_desc_free(sc); 701c42a7b7eSSam Leffler ath_tx_cleanup(sc); 7025591b213SSam Leffler ath_hal_detach(sc->sc_ah); 703c4c6f08fSRuslan Ermilov if_free(ifp); 704f0b2a0beSSam Leffler 7055591b213SSam Leffler return 0; 7065591b213SSam Leffler } 7075591b213SSam Leffler 708b032f27cSSam Leffler /* 709b032f27cSSam Leffler * MAC address handling for multiple BSS on the same radio. 710b032f27cSSam Leffler * The first vap uses the MAC address from the EEPROM. For 711b032f27cSSam Leffler * subsequent vap's we set the U/L bit (bit 1) in the MAC 712b032f27cSSam Leffler * address and use the next six bits as an index. 713b032f27cSSam Leffler */ 714b032f27cSSam Leffler static void 715b032f27cSSam Leffler assign_address(struct ath_softc *sc, uint8_t mac[IEEE80211_ADDR_LEN], int clone) 716b032f27cSSam Leffler { 717b032f27cSSam Leffler int i; 718b032f27cSSam Leffler 719b032f27cSSam Leffler if (clone && sc->sc_hasbmask) { 720b032f27cSSam Leffler /* NB: we only do this if h/w supports multiple bssid */ 721b032f27cSSam Leffler for (i = 0; i < 8; i++) 722b032f27cSSam Leffler if ((sc->sc_bssidmask & (1<<i)) == 0) 723b032f27cSSam Leffler break; 724b032f27cSSam Leffler if (i != 0) 725b032f27cSSam Leffler mac[0] |= (i << 2)|0x2; 726b032f27cSSam Leffler } else 727b032f27cSSam Leffler i = 0; 728b032f27cSSam Leffler sc->sc_bssidmask |= 1<<i; 729b032f27cSSam Leffler sc->sc_hwbssidmask[0] &= ~mac[0]; 730b032f27cSSam Leffler if (i == 0) 731b032f27cSSam Leffler sc->sc_nbssid0++; 732b032f27cSSam Leffler } 733b032f27cSSam Leffler 734b032f27cSSam Leffler static void 735b032f27cSSam Leffler reclaim_address(struct ath_softc *sc, const uint8_t mac[IEEE80211_ADDR_LEN]) 736b032f27cSSam Leffler { 737b032f27cSSam Leffler int i = mac[0] >> 2; 738b032f27cSSam Leffler uint8_t mask; 739b032f27cSSam Leffler 740b032f27cSSam Leffler if (i != 0 || --sc->sc_nbssid0 == 0) { 741b032f27cSSam Leffler sc->sc_bssidmask &= ~(1<<i); 742b032f27cSSam Leffler /* recalculate bssid mask from remaining addresses */ 743b032f27cSSam Leffler mask = 0xff; 744b032f27cSSam Leffler for (i = 1; i < 8; i++) 745b032f27cSSam Leffler if (sc->sc_bssidmask & (1<<i)) 746b032f27cSSam Leffler mask &= ~((i<<2)|0x2); 747b032f27cSSam Leffler sc->sc_hwbssidmask[0] |= mask; 748b032f27cSSam Leffler } 749b032f27cSSam Leffler } 750b032f27cSSam Leffler 751b032f27cSSam Leffler /* 752b032f27cSSam Leffler * Assign a beacon xmit slot. We try to space out 753b032f27cSSam Leffler * assignments so when beacons are staggered the 754b032f27cSSam Leffler * traffic coming out of the cab q has maximal time 755b032f27cSSam Leffler * to go out before the next beacon is scheduled. 756b032f27cSSam Leffler */ 757b032f27cSSam Leffler static int 758b032f27cSSam Leffler assign_bslot(struct ath_softc *sc) 759b032f27cSSam Leffler { 760b032f27cSSam Leffler u_int slot, free; 761b032f27cSSam Leffler 762b032f27cSSam Leffler free = 0; 763b032f27cSSam Leffler for (slot = 0; slot < ATH_BCBUF; slot++) 764b032f27cSSam Leffler if (sc->sc_bslot[slot] == NULL) { 765b032f27cSSam Leffler if (sc->sc_bslot[(slot+1)%ATH_BCBUF] == NULL && 766b032f27cSSam Leffler sc->sc_bslot[(slot-1)%ATH_BCBUF] == NULL) 767b032f27cSSam Leffler return slot; 768b032f27cSSam Leffler free = slot; 769b032f27cSSam Leffler /* NB: keep looking for a double slot */ 770b032f27cSSam Leffler } 771b032f27cSSam Leffler return free; 772b032f27cSSam Leffler } 773b032f27cSSam Leffler 774b032f27cSSam Leffler static struct ieee80211vap * 775b032f27cSSam Leffler ath_vap_create(struct ieee80211com *ic, 776b032f27cSSam Leffler const char name[IFNAMSIZ], int unit, int opmode, int flags, 777b032f27cSSam Leffler const uint8_t bssid[IEEE80211_ADDR_LEN], 778b032f27cSSam Leffler const uint8_t mac0[IEEE80211_ADDR_LEN]) 779b032f27cSSam Leffler { 780b032f27cSSam Leffler struct ath_softc *sc = ic->ic_ifp->if_softc; 781b032f27cSSam Leffler struct ath_vap *avp; 782b032f27cSSam Leffler struct ieee80211vap *vap; 783b032f27cSSam Leffler uint8_t mac[IEEE80211_ADDR_LEN]; 784b032f27cSSam Leffler int ic_opmode, needbeacon, error; 785b032f27cSSam Leffler 786b032f27cSSam Leffler avp = (struct ath_vap *) malloc(sizeof(struct ath_vap), 787b032f27cSSam Leffler M_80211_VAP, M_WAITOK | M_ZERO); 788b032f27cSSam Leffler needbeacon = 0; 789b032f27cSSam Leffler IEEE80211_ADDR_COPY(mac, mac0); 790b032f27cSSam Leffler 791b032f27cSSam Leffler ATH_LOCK(sc); 792b032f27cSSam Leffler switch (opmode) { 793b032f27cSSam Leffler case IEEE80211_M_STA: 794b032f27cSSam Leffler if (sc->sc_nstavaps != 0) { /* XXX only 1 sta for now */ 795b032f27cSSam Leffler device_printf(sc->sc_dev, "only 1 sta vap supported\n"); 796b032f27cSSam Leffler goto bad; 797b032f27cSSam Leffler } 798b032f27cSSam Leffler if (sc->sc_nvaps) { 799b032f27cSSam Leffler /* 800b032f27cSSam Leffler * When there are multiple vaps we must fall 801b032f27cSSam Leffler * back to s/w beacon miss handling. 802b032f27cSSam Leffler */ 803b032f27cSSam Leffler flags |= IEEE80211_CLONE_NOBEACONS; 804b032f27cSSam Leffler } 805b032f27cSSam Leffler if (flags & IEEE80211_CLONE_NOBEACONS) { 806b032f27cSSam Leffler sc->sc_swbmiss = 1; 807b032f27cSSam Leffler ic_opmode = IEEE80211_M_HOSTAP; 808b032f27cSSam Leffler } else 809b032f27cSSam Leffler ic_opmode = opmode; 810b032f27cSSam Leffler break; 811b032f27cSSam Leffler case IEEE80211_M_IBSS: 812b032f27cSSam Leffler if (sc->sc_nvaps != 0) { /* XXX only 1 for now */ 813b032f27cSSam Leffler device_printf(sc->sc_dev, 814b032f27cSSam Leffler "only 1 ibss vap supported\n"); 815b032f27cSSam Leffler goto bad; 816b032f27cSSam Leffler } 817b032f27cSSam Leffler ic_opmode = opmode; 818b032f27cSSam Leffler needbeacon = 1; 819b032f27cSSam Leffler break; 820b032f27cSSam Leffler case IEEE80211_M_AHDEMO: 821b032f27cSSam Leffler /* fall thru... */ 822b032f27cSSam Leffler case IEEE80211_M_MONITOR: 823b032f27cSSam Leffler if (sc->sc_nvaps != 0 && ic->ic_opmode != opmode) { 824b032f27cSSam Leffler /* XXX not right for monitor mode */ 825b032f27cSSam Leffler ic_opmode = ic->ic_opmode; 826b032f27cSSam Leffler } else 827b032f27cSSam Leffler ic_opmode = opmode; 828b032f27cSSam Leffler break; 829b032f27cSSam Leffler case IEEE80211_M_HOSTAP: 830b032f27cSSam Leffler needbeacon = 1; 831b032f27cSSam Leffler /* fall thru... */ 832b032f27cSSam Leffler case IEEE80211_M_WDS: 833b032f27cSSam Leffler if (sc->sc_nvaps && ic->ic_opmode == IEEE80211_M_STA) { 834b032f27cSSam Leffler device_printf(sc->sc_dev, 835b032f27cSSam Leffler "wds not supported in sta mode\n"); 836b032f27cSSam Leffler goto bad; 837b032f27cSSam Leffler } 838b032f27cSSam Leffler if (opmode == IEEE80211_M_WDS) { 839b032f27cSSam Leffler /* 840b032f27cSSam Leffler * Silently remove any request for a unique 841b032f27cSSam Leffler * bssid; WDS vap's always share the local 842b032f27cSSam Leffler * mac address. 843b032f27cSSam Leffler */ 844b032f27cSSam Leffler flags &= ~IEEE80211_CLONE_BSSID; 845b032f27cSSam Leffler } 846b032f27cSSam Leffler ic_opmode = IEEE80211_M_HOSTAP; 847b032f27cSSam Leffler break; 848b032f27cSSam Leffler default: 849b032f27cSSam Leffler device_printf(sc->sc_dev, "unknown opmode %d\n", opmode); 850b032f27cSSam Leffler goto bad; 851b032f27cSSam Leffler } 852b032f27cSSam Leffler /* 853b032f27cSSam Leffler * Check that a beacon buffer is available; the code below assumes it. 854b032f27cSSam Leffler */ 855b032f27cSSam Leffler if (needbeacon & STAILQ_EMPTY(&sc->sc_bbuf)) { 856b032f27cSSam Leffler device_printf(sc->sc_dev, "no beacon buffer available\n"); 857b032f27cSSam Leffler goto bad; 858b032f27cSSam Leffler } 859b032f27cSSam Leffler 860b032f27cSSam Leffler /* STA, AHDEMO? */ 861b032f27cSSam Leffler if (opmode == IEEE80211_M_HOSTAP) { 862b032f27cSSam Leffler assign_address(sc, mac, flags & IEEE80211_CLONE_BSSID); 863b032f27cSSam Leffler ath_hal_setbssidmask(sc->sc_ah, sc->sc_hwbssidmask); 864b032f27cSSam Leffler } 865b032f27cSSam Leffler 866b032f27cSSam Leffler vap = &avp->av_vap; 867b032f27cSSam Leffler /* XXX can't hold mutex across if_alloc */ 868b032f27cSSam Leffler ATH_UNLOCK(sc); 869b032f27cSSam Leffler error = ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, 870b032f27cSSam Leffler bssid, mac); 871b032f27cSSam Leffler ATH_LOCK(sc); 872b032f27cSSam Leffler if (error != 0) { 873b032f27cSSam Leffler device_printf(sc->sc_dev, "%s: error %d creating vap\n", 874b032f27cSSam Leffler __func__, error); 875b032f27cSSam Leffler goto bad2; 876b032f27cSSam Leffler } 877b032f27cSSam Leffler 878b032f27cSSam Leffler /* h/w crypto support */ 879b032f27cSSam Leffler vap->iv_key_alloc = ath_key_alloc; 880b032f27cSSam Leffler vap->iv_key_delete = ath_key_delete; 881b032f27cSSam Leffler vap->iv_key_set = ath_key_set; 882b032f27cSSam Leffler vap->iv_key_update_begin = ath_key_update_begin; 883b032f27cSSam Leffler vap->iv_key_update_end = ath_key_update_end; 884b032f27cSSam Leffler 885b032f27cSSam Leffler /* override various methods */ 886b032f27cSSam Leffler avp->av_recv_mgmt = vap->iv_recv_mgmt; 887b032f27cSSam Leffler vap->iv_recv_mgmt = ath_recv_mgmt; 888b032f27cSSam Leffler vap->iv_reset = ath_reset_vap; 889b032f27cSSam Leffler vap->iv_update_beacon = ath_beacon_update; 890b032f27cSSam Leffler avp->av_newstate = vap->iv_newstate; 891b032f27cSSam Leffler vap->iv_newstate = ath_newstate; 892b032f27cSSam Leffler avp->av_bmiss = vap->iv_bmiss; 893b032f27cSSam Leffler vap->iv_bmiss = ath_bmiss_vap; 894b032f27cSSam Leffler 895b032f27cSSam Leffler avp->av_bslot = -1; 896b032f27cSSam Leffler if (needbeacon) { 897b032f27cSSam Leffler /* 898b032f27cSSam Leffler * Allocate beacon state and setup the q for buffered 899b032f27cSSam Leffler * multicast frames. We know a beacon buffer is 900b032f27cSSam Leffler * available because we checked above. 901b032f27cSSam Leffler */ 902b032f27cSSam Leffler avp->av_bcbuf = STAILQ_FIRST(&sc->sc_bbuf); 903b032f27cSSam Leffler STAILQ_REMOVE_HEAD(&sc->sc_bbuf, bf_list); 904b032f27cSSam Leffler if (opmode != IEEE80211_M_IBSS || !sc->sc_hasveol) { 905b032f27cSSam Leffler /* 906b032f27cSSam Leffler * Assign the vap to a beacon xmit slot. As above 907b032f27cSSam Leffler * this cannot fail to find a free one. 908b032f27cSSam Leffler */ 909b032f27cSSam Leffler avp->av_bslot = assign_bslot(sc); 910b032f27cSSam Leffler KASSERT(sc->sc_bslot[avp->av_bslot] == NULL, 911b032f27cSSam Leffler ("beacon slot %u not empty", avp->av_bslot)); 912b032f27cSSam Leffler sc->sc_bslot[avp->av_bslot] = vap; 913b032f27cSSam Leffler sc->sc_nbcnvaps++; 914b032f27cSSam Leffler } 915b032f27cSSam Leffler if (sc->sc_hastsfadd && sc->sc_nbcnvaps > 0) { 916b032f27cSSam Leffler /* 917b032f27cSSam Leffler * Multple vaps are to transmit beacons and we 918b032f27cSSam Leffler * have h/w support for TSF adjusting; enable 919b032f27cSSam Leffler * use of staggered beacons. 920b032f27cSSam Leffler */ 921b032f27cSSam Leffler sc->sc_stagbeacons = 1; 922b032f27cSSam Leffler } 923b032f27cSSam Leffler ath_txq_init(sc, &avp->av_mcastq, ATH_TXQ_SWQ); 924b032f27cSSam Leffler } 925b032f27cSSam Leffler 926b032f27cSSam Leffler ic->ic_opmode = ic_opmode; 927b032f27cSSam Leffler if (opmode != IEEE80211_M_WDS) { 928b032f27cSSam Leffler sc->sc_nvaps++; 929b032f27cSSam Leffler if (opmode == IEEE80211_M_STA) 930b032f27cSSam Leffler sc->sc_nstavaps++; 931b032f27cSSam Leffler } 932b032f27cSSam Leffler switch (ic_opmode) { 933b032f27cSSam Leffler case IEEE80211_M_IBSS: 934b032f27cSSam Leffler sc->sc_opmode = HAL_M_IBSS; 935b032f27cSSam Leffler break; 936b032f27cSSam Leffler case IEEE80211_M_STA: 937b032f27cSSam Leffler sc->sc_opmode = HAL_M_STA; 938b032f27cSSam Leffler break; 939b032f27cSSam Leffler case IEEE80211_M_AHDEMO: 940b032f27cSSam Leffler case IEEE80211_M_HOSTAP: 941b032f27cSSam Leffler sc->sc_opmode = HAL_M_HOSTAP; 942b032f27cSSam Leffler break; 943b032f27cSSam Leffler case IEEE80211_M_MONITOR: 944b032f27cSSam Leffler sc->sc_opmode = HAL_M_MONITOR; 945b032f27cSSam Leffler break; 946b032f27cSSam Leffler default: 947b032f27cSSam Leffler /* XXX should not happen */ 948b032f27cSSam Leffler break; 949b032f27cSSam Leffler } 950b032f27cSSam Leffler if (sc->sc_hastsfadd) { 951b032f27cSSam Leffler /* 952b032f27cSSam Leffler * Configure whether or not TSF adjust should be done. 953b032f27cSSam Leffler */ 954b032f27cSSam Leffler ath_hal_settsfadjust(sc->sc_ah, sc->sc_stagbeacons); 955b032f27cSSam Leffler } 956b032f27cSSam Leffler ATH_UNLOCK(sc); 957b032f27cSSam Leffler 958b032f27cSSam Leffler /* complete setup */ 959b032f27cSSam Leffler ieee80211_vap_attach(vap, ath_media_change, ieee80211_media_status); 960b032f27cSSam Leffler return vap; 961b032f27cSSam Leffler bad2: 962b032f27cSSam Leffler reclaim_address(sc, mac); 963b032f27cSSam Leffler ath_hal_setbssidmask(sc->sc_ah, sc->sc_hwbssidmask); 964b032f27cSSam Leffler bad: 965b032f27cSSam Leffler free(avp, M_80211_VAP); 966b032f27cSSam Leffler ATH_UNLOCK(sc); 967b032f27cSSam Leffler return NULL; 968b032f27cSSam Leffler } 969b032f27cSSam Leffler 970b032f27cSSam Leffler static void 971b032f27cSSam Leffler ath_vap_delete(struct ieee80211vap *vap) 972b032f27cSSam Leffler { 973b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 974b032f27cSSam Leffler struct ifnet *ifp = ic->ic_ifp; 975b032f27cSSam Leffler struct ath_softc *sc = ifp->if_softc; 976b032f27cSSam Leffler struct ath_hal *ah = sc->sc_ah; 977b032f27cSSam Leffler struct ath_vap *avp = ATH_VAP(vap); 978b032f27cSSam Leffler 979b032f27cSSam Leffler if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 980b032f27cSSam Leffler /* 981b032f27cSSam Leffler * Quiesce the hardware while we remove the vap. In 982b032f27cSSam Leffler * particular we need to reclaim all references to 983b032f27cSSam Leffler * the vap state by any frames pending on the tx queues. 984b032f27cSSam Leffler */ 985b032f27cSSam Leffler ath_hal_intrset(ah, 0); /* disable interrupts */ 986b032f27cSSam Leffler ath_draintxq(sc); /* stop xmit side */ 987b032f27cSSam Leffler ath_stoprecv(sc); /* stop recv side */ 988b032f27cSSam Leffler } 989b032f27cSSam Leffler 990b032f27cSSam Leffler ieee80211_vap_detach(vap); 991b032f27cSSam Leffler ATH_LOCK(sc); 992b032f27cSSam Leffler /* 993b032f27cSSam Leffler * Reclaim beacon state. Note this must be done before 994b032f27cSSam Leffler * the vap instance is reclaimed as we may have a reference 995b032f27cSSam Leffler * to it in the buffer for the beacon frame. 996b032f27cSSam Leffler */ 997b032f27cSSam Leffler if (avp->av_bcbuf != NULL) { 998b032f27cSSam Leffler if (avp->av_bslot != -1) { 999b032f27cSSam Leffler sc->sc_bslot[avp->av_bslot] = NULL; 1000b032f27cSSam Leffler sc->sc_nbcnvaps--; 1001b032f27cSSam Leffler } 1002b032f27cSSam Leffler ath_beacon_return(sc, avp->av_bcbuf); 1003b032f27cSSam Leffler avp->av_bcbuf = NULL; 1004b032f27cSSam Leffler if (sc->sc_nbcnvaps == 0) { 1005b032f27cSSam Leffler sc->sc_stagbeacons = 0; 1006b032f27cSSam Leffler if (sc->sc_hastsfadd) 1007b032f27cSSam Leffler ath_hal_settsfadjust(sc->sc_ah, 0); 1008b032f27cSSam Leffler } 1009b032f27cSSam Leffler /* 1010b032f27cSSam Leffler * Reclaim any pending mcast frames for the vap. 1011b032f27cSSam Leffler */ 1012b032f27cSSam Leffler ath_tx_draintxq(sc, &avp->av_mcastq); 1013b032f27cSSam Leffler ATH_TXQ_LOCK_DESTROY(&avp->av_mcastq); 1014b032f27cSSam Leffler } 1015b032f27cSSam Leffler /* 1016b032f27cSSam Leffler * Update bookkeeping. 1017b032f27cSSam Leffler */ 1018b032f27cSSam Leffler if (vap->iv_opmode == IEEE80211_M_STA) { 1019b032f27cSSam Leffler sc->sc_nstavaps--; 1020b032f27cSSam Leffler if (sc->sc_nstavaps == 0 && sc->sc_swbmiss) 1021b032f27cSSam Leffler sc->sc_swbmiss = 0; 1022b032f27cSSam Leffler } else if (vap->iv_opmode == IEEE80211_M_HOSTAP) { 1023b032f27cSSam Leffler reclaim_address(sc, vap->iv_myaddr); 1024b032f27cSSam Leffler ath_hal_setbssidmask(ah, sc->sc_hwbssidmask); 1025b032f27cSSam Leffler } 1026b032f27cSSam Leffler if (vap->iv_opmode != IEEE80211_M_WDS) 1027b032f27cSSam Leffler sc->sc_nvaps--; 1028b032f27cSSam Leffler ATH_UNLOCK(sc); 1029b032f27cSSam Leffler free(avp, M_80211_VAP); 1030b032f27cSSam Leffler 1031b032f27cSSam Leffler if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 1032b032f27cSSam Leffler /* 1033b032f27cSSam Leffler * Restart rx+tx machines if still running (RUNNING will 1034b032f27cSSam Leffler * be reset if we just destroyed the last vap). 1035b032f27cSSam Leffler */ 1036b032f27cSSam Leffler if (ath_startrecv(sc) != 0) 1037b032f27cSSam Leffler if_printf(ifp, "%s: unable to restart recv logic\n", 1038b032f27cSSam Leffler __func__); 1039b032f27cSSam Leffler if (sc->sc_beacons) 1040b032f27cSSam Leffler ath_beacon_config(sc, NULL); 1041b032f27cSSam Leffler ath_hal_intrset(ah, sc->sc_imask); 1042b032f27cSSam Leffler } 1043b032f27cSSam Leffler } 1044b032f27cSSam Leffler 10455591b213SSam Leffler void 10465591b213SSam Leffler ath_suspend(struct ath_softc *sc) 10475591b213SSam Leffler { 1048fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 10495591b213SSam Leffler 1050c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n", 1051c42a7b7eSSam Leffler __func__, ifp->if_flags); 10525591b213SSam Leffler 10535591b213SSam Leffler ath_stop(ifp); 10545591b213SSam Leffler } 10555591b213SSam Leffler 10565591b213SSam Leffler void 10575591b213SSam Leffler ath_resume(struct ath_softc *sc) 10585591b213SSam Leffler { 1059fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 10605591b213SSam Leffler 1061c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n", 1062c42a7b7eSSam Leffler __func__, ifp->if_flags); 10635591b213SSam Leffler 10646b59f5e3SSam Leffler if (ifp->if_flags & IFF_UP) { 1065fc74a9f9SBrooks Davis ath_init(sc); 106613f4c340SRobert Watson if (ifp->if_drv_flags & IFF_DRV_RUNNING) 10675591b213SSam Leffler ath_start(ifp); 10685591b213SSam Leffler } 1069b50c8bdeSSam Leffler if (sc->sc_softled) { 1070b50c8bdeSSam Leffler ath_hal_gpioCfgOutput(sc->sc_ah, sc->sc_ledpin); 1071b50c8bdeSSam Leffler ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, !sc->sc_ledon); 1072b50c8bdeSSam Leffler } 10736b59f5e3SSam Leffler } 10745591b213SSam Leffler 10755591b213SSam Leffler void 10765591b213SSam Leffler ath_shutdown(struct ath_softc *sc) 10775591b213SSam Leffler { 1078fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 10795591b213SSam Leffler 1080c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n", 1081c42a7b7eSSam Leffler __func__, ifp->if_flags); 10825591b213SSam Leffler 10835591b213SSam Leffler ath_stop(ifp); 10845591b213SSam Leffler } 10855591b213SSam Leffler 1086c42a7b7eSSam Leffler /* 1087c42a7b7eSSam Leffler * Interrupt handler. Most of the actual processing is deferred. 1088c42a7b7eSSam Leffler */ 10895591b213SSam Leffler void 10905591b213SSam Leffler ath_intr(void *arg) 10915591b213SSam Leffler { 10925591b213SSam Leffler struct ath_softc *sc = arg; 1093fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 10945591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 10955591b213SSam Leffler HAL_INT status; 10965591b213SSam Leffler 10975591b213SSam Leffler if (sc->sc_invalid) { 10985591b213SSam Leffler /* 1099b58b3803SSam Leffler * The hardware is not ready/present, don't touch anything. 1100b58b3803SSam Leffler * Note this can happen early on if the IRQ is shared. 11015591b213SSam Leffler */ 1102c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid; ignored\n", __func__); 11035591b213SSam Leffler return; 11045591b213SSam Leffler } 1105fdd758d4SSam Leffler if (!ath_hal_intrpend(ah)) /* shared irq, not for us */ 1106fdd758d4SSam Leffler return; 110768e8e04eSSam Leffler if ((ifp->if_flags & IFF_UP) == 0 || 110868e8e04eSSam Leffler (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { 110968e8e04eSSam Leffler HAL_INT status; 111068e8e04eSSam Leffler 1111c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags 0x%x\n", 1112c42a7b7eSSam Leffler __func__, ifp->if_flags); 11135591b213SSam Leffler ath_hal_getisr(ah, &status); /* clear ISR */ 11145591b213SSam Leffler ath_hal_intrset(ah, 0); /* disable further intr's */ 11155591b213SSam Leffler return; 11165591b213SSam Leffler } 1117c42a7b7eSSam Leffler /* 1118c42a7b7eSSam Leffler * Figure out the reason(s) for the interrupt. Note 1119c42a7b7eSSam Leffler * that the hal returns a pseudo-ISR that may include 1120c42a7b7eSSam Leffler * bits we haven't explicitly enabled so we mask the 1121c42a7b7eSSam Leffler * value to insure we only process bits we requested. 1122c42a7b7eSSam Leffler */ 11235591b213SSam Leffler ath_hal_getisr(ah, &status); /* NB: clears ISR too */ 1124c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_INTR, "%s: status 0x%x\n", __func__, status); 1125ecddff40SSam Leffler status &= sc->sc_imask; /* discard unasked for bits */ 11265591b213SSam Leffler if (status & HAL_INT_FATAL) { 11275591b213SSam Leffler sc->sc_stats.ast_hardware++; 11285591b213SSam Leffler ath_hal_intrset(ah, 0); /* disable intr's until reset */ 112916c8acaaSSam Leffler ath_fatal_proc(sc, 0); 11305591b213SSam Leffler } else if (status & HAL_INT_RXORN) { 11315591b213SSam Leffler sc->sc_stats.ast_rxorn++; 11325591b213SSam Leffler ath_hal_intrset(ah, 0); /* disable intr's until reset */ 11330bbf5441SSam Leffler taskqueue_enqueue(sc->sc_tq, &sc->sc_rxorntask); 11345591b213SSam Leffler } else { 1135c42a7b7eSSam Leffler if (status & HAL_INT_SWBA) { 1136c42a7b7eSSam Leffler /* 1137c42a7b7eSSam Leffler * Software beacon alert--time to send a beacon. 1138c42a7b7eSSam Leffler * Handle beacon transmission directly; deferring 1139c42a7b7eSSam Leffler * this is too slow to meet timing constraints 1140c42a7b7eSSam Leffler * under load. 1141c42a7b7eSSam Leffler */ 1142c42a7b7eSSam Leffler ath_beacon_proc(sc, 0); 1143c42a7b7eSSam Leffler } 11445591b213SSam Leffler if (status & HAL_INT_RXEOL) { 11455591b213SSam Leffler /* 11465591b213SSam Leffler * NB: the hardware should re-read the link when 11475591b213SSam Leffler * RXE bit is written, but it doesn't work at 11485591b213SSam Leffler * least on older hardware revs. 11495591b213SSam Leffler */ 11505591b213SSam Leffler sc->sc_stats.ast_rxeol++; 11515591b213SSam Leffler sc->sc_rxlink = NULL; 11525591b213SSam Leffler } 11535591b213SSam Leffler if (status & HAL_INT_TXURN) { 11545591b213SSam Leffler sc->sc_stats.ast_txurn++; 11555591b213SSam Leffler /* bump tx trigger level */ 11565591b213SSam Leffler ath_hal_updatetxtriglevel(ah, AH_TRUE); 11575591b213SSam Leffler } 11585591b213SSam Leffler if (status & HAL_INT_RX) 11590bbf5441SSam Leffler taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask); 11605591b213SSam Leffler if (status & HAL_INT_TX) 11610bbf5441SSam Leffler taskqueue_enqueue(sc->sc_tq, &sc->sc_txtask); 11625591b213SSam Leffler if (status & HAL_INT_BMISS) { 11635591b213SSam Leffler sc->sc_stats.ast_bmiss++; 11640bbf5441SSam Leffler taskqueue_enqueue(sc->sc_tq, &sc->sc_bmisstask); 11655591b213SSam Leffler } 1166c42a7b7eSSam Leffler if (status & HAL_INT_MIB) { 1167c42a7b7eSSam Leffler sc->sc_stats.ast_mib++; 1168c42a7b7eSSam Leffler /* 1169c42a7b7eSSam Leffler * Disable interrupts until we service the MIB 1170c42a7b7eSSam Leffler * interrupt; otherwise it will continue to fire. 1171c42a7b7eSSam Leffler */ 1172c42a7b7eSSam Leffler ath_hal_intrset(ah, 0); 1173c42a7b7eSSam Leffler /* 1174c42a7b7eSSam Leffler * Let the hal handle the event. We assume it will 1175c42a7b7eSSam Leffler * clear whatever condition caused the interrupt. 1176c42a7b7eSSam Leffler */ 1177ffa2cab6SSam Leffler ath_hal_mibevent(ah, &sc->sc_halstats); 1178c42a7b7eSSam Leffler ath_hal_intrset(ah, sc->sc_imask); 1179c42a7b7eSSam Leffler } 11805591b213SSam Leffler } 11815591b213SSam Leffler } 11825591b213SSam Leffler 11835591b213SSam Leffler static void 11845591b213SSam Leffler ath_fatal_proc(void *arg, int pending) 11855591b213SSam Leffler { 11865591b213SSam Leffler struct ath_softc *sc = arg; 1187fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 118816c8acaaSSam Leffler u_int32_t *state; 118916c8acaaSSam Leffler u_int32_t len; 119068e8e04eSSam Leffler void *sp; 11915591b213SSam Leffler 1192c42a7b7eSSam Leffler if_printf(ifp, "hardware error; resetting\n"); 119316c8acaaSSam Leffler /* 119416c8acaaSSam Leffler * Fatal errors are unrecoverable. Typically these 119516c8acaaSSam Leffler * are caused by DMA errors. Collect h/w state from 119616c8acaaSSam Leffler * the hal so we can diagnose what's going on. 119716c8acaaSSam Leffler */ 119868e8e04eSSam Leffler if (ath_hal_getfatalstate(sc->sc_ah, &sp, &len)) { 119916c8acaaSSam Leffler KASSERT(len >= 6*sizeof(u_int32_t), ("len %u bytes", len)); 120068e8e04eSSam Leffler state = sp; 120116c8acaaSSam Leffler if_printf(ifp, "0x%08x 0x%08x 0x%08x, 0x%08x 0x%08x 0x%08x\n", 120216c8acaaSSam Leffler state[0], state[1] , state[2], state[3], 120316c8acaaSSam Leffler state[4], state[5]); 120416c8acaaSSam Leffler } 1205c42a7b7eSSam Leffler ath_reset(ifp); 12065591b213SSam Leffler } 12075591b213SSam Leffler 12085591b213SSam Leffler static void 12095591b213SSam Leffler ath_rxorn_proc(void *arg, int pending) 12105591b213SSam Leffler { 12115591b213SSam Leffler struct ath_softc *sc = arg; 1212fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 12135591b213SSam Leffler 1214c42a7b7eSSam Leffler if_printf(ifp, "rx FIFO overrun; resetting\n"); 1215c42a7b7eSSam Leffler ath_reset(ifp); 12165591b213SSam Leffler } 12175591b213SSam Leffler 12185591b213SSam Leffler static void 1219b032f27cSSam Leffler ath_bmiss_vap(struct ieee80211vap *vap) 12205591b213SSam Leffler { 1221b032f27cSSam Leffler struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; 1222d7736e13SSam Leffler u_int64_t lastrx = sc->sc_lastrx; 1223d7736e13SSam Leffler u_int64_t tsf = ath_hal_gettsf64(sc->sc_ah); 1224d7736e13SSam Leffler u_int bmisstimeout = 1225b032f27cSSam Leffler vap->iv_bmissthreshold * vap->iv_bss->ni_intval * 1024; 1226d7736e13SSam Leffler 1227d7736e13SSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON, 1228d7736e13SSam Leffler "%s: tsf %llu lastrx %lld (%llu) bmiss %u\n", 1229d7736e13SSam Leffler __func__, (unsigned long long) tsf, 1230d7736e13SSam Leffler (unsigned long long)(tsf - lastrx), 1231d7736e13SSam Leffler (unsigned long long) lastrx, bmisstimeout); 1232e585d188SSam Leffler /* 1233d7736e13SSam Leffler * Workaround phantom bmiss interrupts by sanity-checking 1234d7736e13SSam Leffler * the time of our last rx'd frame. If it is within the 1235d7736e13SSam Leffler * beacon miss interval then ignore the interrupt. If it's 1236d7736e13SSam Leffler * truly a bmiss we'll get another interrupt soon and that'll 1237d7736e13SSam Leffler * be dispatched up for processing. 1238e585d188SSam Leffler */ 12390bf686c1SRobert Watson if (tsf - lastrx > bmisstimeout) 1240b032f27cSSam Leffler ATH_VAP(vap)->av_bmiss(vap); 12410bf686c1SRobert Watson else 1242d7736e13SSam Leffler sc->sc_stats.ast_bmiss_phantom++; 1243e585d188SSam Leffler } 1244b032f27cSSam Leffler 1245b032f27cSSam Leffler static void 1246b032f27cSSam Leffler ath_bmiss_proc(void *arg, int pending) 1247b032f27cSSam Leffler { 1248b032f27cSSam Leffler struct ath_softc *sc = arg; 1249b032f27cSSam Leffler struct ifnet *ifp = sc->sc_ifp; 1250b032f27cSSam Leffler 1251b032f27cSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: pending %u\n", __func__, pending); 1252b032f27cSSam Leffler ieee80211_beacon_miss(ifp->if_l2com); 12535591b213SSam Leffler } 12545591b213SSam Leffler 1255724c193aSSam Leffler /* 1256724c193aSSam Leffler * Convert net80211 channel to a HAL channel with the flags 1257724c193aSSam Leffler * constrained to reflect the current operating mode and 1258724c193aSSam Leffler * the frequency possibly mapped for GSM channels. 1259724c193aSSam Leffler */ 1260724c193aSSam Leffler static void 126168e8e04eSSam Leffler ath_mapchan(HAL_CHANNEL *hc, const struct ieee80211_channel *chan) 12625591b213SSam Leffler { 1263c42a7b7eSSam Leffler #define N(a) (sizeof(a) / sizeof(a[0])) 126468e8e04eSSam Leffler static const u_int modeflags[IEEE80211_MODE_MAX] = { 12655591b213SSam Leffler 0, /* IEEE80211_MODE_AUTO */ 12665591b213SSam Leffler CHANNEL_A, /* IEEE80211_MODE_11A */ 12675591b213SSam Leffler CHANNEL_B, /* IEEE80211_MODE_11B */ 12685591b213SSam Leffler CHANNEL_PUREG, /* IEEE80211_MODE_11G */ 1269c42a7b7eSSam Leffler 0, /* IEEE80211_MODE_FH */ 127068e8e04eSSam Leffler CHANNEL_108A, /* IEEE80211_MODE_TURBO_A */ 127168e8e04eSSam Leffler CHANNEL_108G, /* IEEE80211_MODE_TURBO_G */ 127268e8e04eSSam Leffler CHANNEL_ST, /* IEEE80211_MODE_STURBO_A */ 127368e8e04eSSam Leffler CHANNEL_A, /* IEEE80211_MODE_11NA */ 127468e8e04eSSam Leffler CHANNEL_PUREG, /* IEEE80211_MODE_11NG */ 12755591b213SSam Leffler }; 127668e8e04eSSam Leffler enum ieee80211_phymode mode = ieee80211_chan2mode(chan); 1277c42a7b7eSSam Leffler 1278c42a7b7eSSam Leffler KASSERT(mode < N(modeflags), ("unexpected phy mode %u", mode)); 1279c42a7b7eSSam Leffler KASSERT(modeflags[mode] != 0, ("mode %u undefined", mode)); 1280724c193aSSam Leffler hc->channelFlags = modeflags[mode]; 1281aaa70f2fSSam Leffler if (IEEE80211_IS_CHAN_HALF(chan)) 1282724c193aSSam Leffler hc->channelFlags |= CHANNEL_HALF; 1283aaa70f2fSSam Leffler if (IEEE80211_IS_CHAN_QUARTER(chan)) 1284724c193aSSam Leffler hc->channelFlags |= CHANNEL_QUARTER; 128568e8e04eSSam Leffler if (IEEE80211_IS_CHAN_HT20(chan)) 128668e8e04eSSam Leffler hc->channelFlags |= CHANNEL_HT20; 128768e8e04eSSam Leffler if (IEEE80211_IS_CHAN_HT40D(chan)) 128868e8e04eSSam Leffler hc->channelFlags |= CHANNEL_HT40MINUS; 128968e8e04eSSam Leffler if (IEEE80211_IS_CHAN_HT40U(chan)) 129068e8e04eSSam Leffler hc->channelFlags |= CHANNEL_HT40PLUS; 1291724c193aSSam Leffler 1292724c193aSSam Leffler hc->channel = IEEE80211_IS_CHAN_GSM(chan) ? 1293724c193aSSam Leffler 2422 + (922 - chan->ic_freq) : chan->ic_freq; 1294c42a7b7eSSam Leffler #undef N 12955591b213SSam Leffler } 12965591b213SSam Leffler 1297b032f27cSSam Leffler /* 1298b032f27cSSam Leffler * Handle TKIP MIC setup to deal hardware that doesn't do MIC 1299b032f27cSSam Leffler * calcs together with WME. If necessary disable the crypto 1300b032f27cSSam Leffler * hardware and mark the 802.11 state so keys will be setup 1301b032f27cSSam Leffler * with the MIC work done in software. 1302b032f27cSSam Leffler */ 1303b032f27cSSam Leffler static void 1304b032f27cSSam Leffler ath_settkipmic(struct ath_softc *sc) 1305b032f27cSSam Leffler { 1306b032f27cSSam Leffler struct ifnet *ifp = sc->sc_ifp; 1307b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 1308b032f27cSSam Leffler 1309b032f27cSSam Leffler if ((ic->ic_cryptocaps & IEEE80211_CRYPTO_TKIP) && !sc->sc_wmetkipmic) { 1310b032f27cSSam Leffler if (ic->ic_flags & IEEE80211_F_WME) { 1311b032f27cSSam Leffler ath_hal_settkipmic(sc->sc_ah, AH_FALSE); 1312b032f27cSSam Leffler ic->ic_cryptocaps &= ~IEEE80211_CRYPTO_TKIPMIC; 1313b032f27cSSam Leffler } else { 1314b032f27cSSam Leffler ath_hal_settkipmic(sc->sc_ah, AH_TRUE); 1315b032f27cSSam Leffler ic->ic_cryptocaps |= IEEE80211_CRYPTO_TKIPMIC; 1316b032f27cSSam Leffler } 1317b032f27cSSam Leffler } 1318b032f27cSSam Leffler } 1319b032f27cSSam Leffler 13205591b213SSam Leffler static void 13215591b213SSam Leffler ath_init(void *arg) 13225591b213SSam Leffler { 13235591b213SSam Leffler struct ath_softc *sc = (struct ath_softc *) arg; 1324fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 1325b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 13265591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 13275591b213SSam Leffler HAL_STATUS status; 13285591b213SSam Leffler 1329c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags 0x%x\n", 1330c42a7b7eSSam Leffler __func__, ifp->if_flags); 13315591b213SSam Leffler 1332f0b2a0beSSam Leffler ATH_LOCK(sc); 13335591b213SSam Leffler /* 13345591b213SSam Leffler * Stop anything previously setup. This is safe 13355591b213SSam Leffler * whether this is the first time through or not. 13365591b213SSam Leffler */ 1337c42a7b7eSSam Leffler ath_stop_locked(ifp); 13385591b213SSam Leffler 13395591b213SSam Leffler /* 13405591b213SSam Leffler * The basic interface to setting the hardware in a good 13415591b213SSam Leffler * state is ``reset''. On return the hardware is known to 13425591b213SSam Leffler * be powered up and with interrupts disabled. This must 13435591b213SSam Leffler * be followed by initialization of the appropriate bits 13445591b213SSam Leffler * and then setup of the interrupt mask. 13455591b213SSam Leffler */ 134668e8e04eSSam Leffler ath_mapchan(&sc->sc_curchan, ic->ic_curchan); 1347b032f27cSSam Leffler ath_settkipmic(sc); 13487a04dc27SSam Leffler if (!ath_hal_reset(ah, sc->sc_opmode, &sc->sc_curchan, AH_FALSE, &status)) { 13495591b213SSam Leffler if_printf(ifp, "unable to reset hardware; hal status %u\n", 13505591b213SSam Leffler status); 1351b032f27cSSam Leffler ATH_UNLOCK(sc); 1352b032f27cSSam Leffler return; 13535591b213SSam Leffler } 1354b032f27cSSam Leffler ath_chan_change(sc, ic->ic_curchan); 13555591b213SSam Leffler 13565591b213SSam Leffler /* 1357c59005e9SSam Leffler * Likewise this is set during reset so update 1358c59005e9SSam Leffler * state cached in the driver. 1359c59005e9SSam Leffler */ 1360c59005e9SSam Leffler sc->sc_diversity = ath_hal_getdiversity(ah); 1361bd5a9920SSam Leffler sc->sc_calinterval = 1; 1362bd5a9920SSam Leffler sc->sc_caltries = 0; 1363c42a7b7eSSam Leffler 1364c42a7b7eSSam Leffler /* 13655591b213SSam Leffler * Setup the hardware after reset: the key cache 13665591b213SSam Leffler * is filled as needed and the receive engine is 13675591b213SSam Leffler * set going. Frame transmit is handled entirely 13685591b213SSam Leffler * in the frame output path; there's nothing to do 13695591b213SSam Leffler * here except setup the interrupt mask. 13705591b213SSam Leffler */ 13715591b213SSam Leffler if (ath_startrecv(sc) != 0) { 13725591b213SSam Leffler if_printf(ifp, "unable to start recv logic\n"); 1373b032f27cSSam Leffler ATH_UNLOCK(sc); 1374b032f27cSSam Leffler return; 13755591b213SSam Leffler } 13765591b213SSam Leffler 13775591b213SSam Leffler /* 13785591b213SSam Leffler * Enable interrupts. 13795591b213SSam Leffler */ 13805591b213SSam Leffler sc->sc_imask = HAL_INT_RX | HAL_INT_TX 13815591b213SSam Leffler | HAL_INT_RXEOL | HAL_INT_RXORN 13825591b213SSam Leffler | HAL_INT_FATAL | HAL_INT_GLOBAL; 1383c42a7b7eSSam Leffler /* 1384c42a7b7eSSam Leffler * Enable MIB interrupts when there are hardware phy counters. 1385c42a7b7eSSam Leffler * Note we only do this (at the moment) for station mode. 1386c42a7b7eSSam Leffler */ 1387c42a7b7eSSam Leffler if (sc->sc_needmib && ic->ic_opmode == IEEE80211_M_STA) 1388c42a7b7eSSam Leffler sc->sc_imask |= HAL_INT_MIB; 13895591b213SSam Leffler 139013f4c340SRobert Watson ifp->if_drv_flags |= IFF_DRV_RUNNING; 1391b032f27cSSam Leffler ath_hal_intrset(ah, sc->sc_imask); 13925591b213SSam Leffler 1393b032f27cSSam Leffler ATH_UNLOCK(sc); 1394b032f27cSSam Leffler 139586e07743SSam Leffler #ifdef ATH_TX99_DIAG 139686e07743SSam Leffler if (sc->sc_tx99 != NULL) 139786e07743SSam Leffler sc->sc_tx99->start(sc->sc_tx99); 139886e07743SSam Leffler else 139986e07743SSam Leffler #endif 1400b032f27cSSam Leffler ieee80211_start_all(ic); /* start all vap's */ 14015591b213SSam Leffler } 14025591b213SSam Leffler 14035591b213SSam Leffler static void 1404c42a7b7eSSam Leffler ath_stop_locked(struct ifnet *ifp) 14055591b213SSam Leffler { 14065591b213SSam Leffler struct ath_softc *sc = ifp->if_softc; 14075591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 14085591b213SSam Leffler 1409c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid %u if_flags 0x%x\n", 1410c42a7b7eSSam Leffler __func__, sc->sc_invalid, ifp->if_flags); 14115591b213SSam Leffler 1412c42a7b7eSSam Leffler ATH_LOCK_ASSERT(sc); 141313f4c340SRobert Watson if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 14145591b213SSam Leffler /* 14155591b213SSam Leffler * Shutdown the hardware and driver: 1416c42a7b7eSSam Leffler * reset 802.11 state machine 14175591b213SSam Leffler * turn off timers 1418c42a7b7eSSam Leffler * disable interrupts 1419c42a7b7eSSam Leffler * turn off the radio 14205591b213SSam Leffler * clear transmit machinery 14215591b213SSam Leffler * clear receive machinery 14225591b213SSam Leffler * drain and release tx queues 14235591b213SSam Leffler * reclaim beacon resources 14245591b213SSam Leffler * power down hardware 14255591b213SSam Leffler * 14265591b213SSam Leffler * Note that some of this work is not possible if the 14275591b213SSam Leffler * hardware is gone (invalid). 14285591b213SSam Leffler */ 142986e07743SSam Leffler #ifdef ATH_TX99_DIAG 143086e07743SSam Leffler if (sc->sc_tx99 != NULL) 143186e07743SSam Leffler sc->sc_tx99->stop(sc->sc_tx99); 143286e07743SSam Leffler #endif 143313f4c340SRobert Watson ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 14345591b213SSam Leffler ifp->if_timer = 0; 1435c42a7b7eSSam Leffler if (!sc->sc_invalid) { 14363e50ec2cSSam Leffler if (sc->sc_softled) { 14373e50ec2cSSam Leffler callout_stop(&sc->sc_ledtimer); 14383e50ec2cSSam Leffler ath_hal_gpioset(ah, sc->sc_ledpin, 14393e50ec2cSSam Leffler !sc->sc_ledon); 14403e50ec2cSSam Leffler sc->sc_blinking = 0; 14413e50ec2cSSam Leffler } 14425591b213SSam Leffler ath_hal_intrset(ah, 0); 1443c42a7b7eSSam Leffler } 14445591b213SSam Leffler ath_draintxq(sc); 1445c42a7b7eSSam Leffler if (!sc->sc_invalid) { 14465591b213SSam Leffler ath_stoprecv(sc); 1447c42a7b7eSSam Leffler ath_hal_phydisable(ah); 1448c42a7b7eSSam Leffler } else 14495591b213SSam Leffler sc->sc_rxlink = NULL; 1450b032f27cSSam Leffler ath_beacon_free(sc); /* XXX not needed */ 1451c42a7b7eSSam Leffler } 1452c42a7b7eSSam Leffler } 1453c42a7b7eSSam Leffler 1454c42a7b7eSSam Leffler static void 1455c42a7b7eSSam Leffler ath_stop(struct ifnet *ifp) 1456c42a7b7eSSam Leffler { 1457c42a7b7eSSam Leffler struct ath_softc *sc = ifp->if_softc; 1458c42a7b7eSSam Leffler 1459c42a7b7eSSam Leffler ATH_LOCK(sc); 1460c42a7b7eSSam Leffler ath_stop_locked(ifp); 1461c42a7b7eSSam Leffler if (!sc->sc_invalid) { 1462c42a7b7eSSam Leffler /* 1463c42a7b7eSSam Leffler * Set the chip in full sleep mode. Note that we are 1464c42a7b7eSSam Leffler * careful to do this only when bringing the interface 1465c42a7b7eSSam Leffler * completely to a stop. When the chip is in this state 1466c42a7b7eSSam Leffler * it must be carefully woken up or references to 1467c42a7b7eSSam Leffler * registers in the PCI clock domain may freeze the bus 1468c42a7b7eSSam Leffler * (and system). This varies by chip and is mostly an 1469c42a7b7eSSam Leffler * issue with newer parts that go to sleep more quickly. 1470c42a7b7eSSam Leffler */ 1471bd5a9920SSam Leffler ath_hal_setpower(sc->sc_ah, HAL_PM_FULL_SLEEP); 14725591b213SSam Leffler } 1473f0b2a0beSSam Leffler ATH_UNLOCK(sc); 14745591b213SSam Leffler } 14755591b213SSam Leffler 14765591b213SSam Leffler /* 14775591b213SSam Leffler * Reset the hardware w/o losing operational state. This is 14785591b213SSam Leffler * basically a more efficient way of doing ath_stop, ath_init, 14795591b213SSam Leffler * followed by state transitions to the current 802.11 1480c42a7b7eSSam Leffler * operational state. Used to recover from various errors and 1481c42a7b7eSSam Leffler * to reset or reload hardware state. 14825591b213SSam Leffler */ 1483c42a7b7eSSam Leffler static int 1484c42a7b7eSSam Leffler ath_reset(struct ifnet *ifp) 14855591b213SSam Leffler { 1486c42a7b7eSSam Leffler struct ath_softc *sc = ifp->if_softc; 1487b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 14885591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 14895591b213SSam Leffler HAL_STATUS status; 14905591b213SSam Leffler 14915591b213SSam Leffler /* 14925591b213SSam Leffler * Convert to a HAL channel description with the flags 14935591b213SSam Leffler * constrained to reflect the current operating mode. 14945591b213SSam Leffler */ 149568e8e04eSSam Leffler ath_mapchan(&sc->sc_curchan, ic->ic_curchan); 14965591b213SSam Leffler 14975591b213SSam Leffler ath_hal_intrset(ah, 0); /* disable interrupts */ 14985591b213SSam Leffler ath_draintxq(sc); /* stop xmit side */ 14995591b213SSam Leffler ath_stoprecv(sc); /* stop recv side */ 1500b032f27cSSam Leffler ath_settkipmic(sc); /* configure TKIP MIC handling */ 15015591b213SSam Leffler /* NB: indicate channel change so we do a full reset */ 15027a04dc27SSam Leffler if (!ath_hal_reset(ah, sc->sc_opmode, &sc->sc_curchan, AH_TRUE, &status)) 15035591b213SSam Leffler if_printf(ifp, "%s: unable to reset hardware; hal status %u\n", 15045591b213SSam Leffler __func__, status); 1505c59005e9SSam Leffler sc->sc_diversity = ath_hal_getdiversity(ah); 1506bd5a9920SSam Leffler sc->sc_calinterval = 1; 1507bd5a9920SSam Leffler sc->sc_caltries = 0; 150868e8e04eSSam Leffler if (ath_startrecv(sc) != 0) /* restart recv */ 150968e8e04eSSam Leffler if_printf(ifp, "%s: unable to start recv logic\n", __func__); 1510c42a7b7eSSam Leffler /* 1511c42a7b7eSSam Leffler * We may be doing a reset in response to an ioctl 1512c42a7b7eSSam Leffler * that changes the channel so update any state that 1513c42a7b7eSSam Leffler * might change as a result. 1514c42a7b7eSSam Leffler */ 1515724c193aSSam Leffler ath_chan_change(sc, ic->ic_curchan); 1516b032f27cSSam Leffler if (sc->sc_beacons) 1517b032f27cSSam Leffler ath_beacon_config(sc, NULL); /* restart beacons */ 1518c42a7b7eSSam Leffler ath_hal_intrset(ah, sc->sc_imask); 1519c42a7b7eSSam Leffler 1520c42a7b7eSSam Leffler ath_start(ifp); /* restart xmit */ 1521c42a7b7eSSam Leffler return 0; 15225591b213SSam Leffler } 15235591b213SSam Leffler 152468e8e04eSSam Leffler static int 1525b032f27cSSam Leffler ath_reset_vap(struct ieee80211vap *vap, u_long cmd) 1526b032f27cSSam Leffler { 1527b032f27cSSam Leffler return ath_reset(vap->iv_ic->ic_ifp); 1528b032f27cSSam Leffler } 1529b032f27cSSam Leffler 1530b032f27cSSam Leffler static int 153168e8e04eSSam Leffler ath_ff_always(struct ath_txq *txq, struct ath_buf *bf) 153268e8e04eSSam Leffler { 153368e8e04eSSam Leffler return 0; 153468e8e04eSSam Leffler } 153568e8e04eSSam Leffler 153668e8e04eSSam Leffler #if 0 153768e8e04eSSam Leffler static int 153868e8e04eSSam Leffler ath_ff_ageflushtestdone(struct ath_txq *txq, struct ath_buf *bf) 153968e8e04eSSam Leffler { 154068e8e04eSSam Leffler return (txq->axq_curage - bf->bf_age) < ATH_FF_STAGEMAX; 154168e8e04eSSam Leffler } 154268e8e04eSSam Leffler #endif 154368e8e04eSSam Leffler 154468e8e04eSSam Leffler /* 154568e8e04eSSam Leffler * Flush FF staging queue. 154668e8e04eSSam Leffler */ 154768e8e04eSSam Leffler static void 154868e8e04eSSam Leffler ath_ff_stageq_flush(struct ath_softc *sc, struct ath_txq *txq, 154968e8e04eSSam Leffler int (*ath_ff_flushdonetest)(struct ath_txq *txq, struct ath_buf *bf)) 155068e8e04eSSam Leffler { 155168e8e04eSSam Leffler struct ath_buf *bf; 155268e8e04eSSam Leffler struct ieee80211_node *ni; 155368e8e04eSSam Leffler int pktlen, pri; 155468e8e04eSSam Leffler 155568e8e04eSSam Leffler for (;;) { 155668e8e04eSSam Leffler ATH_TXQ_LOCK(txq); 155768e8e04eSSam Leffler /* 155868e8e04eSSam Leffler * Go from the back (oldest) to front so we can 155968e8e04eSSam Leffler * stop early based on the age of the entry. 156068e8e04eSSam Leffler */ 156168e8e04eSSam Leffler bf = TAILQ_LAST(&txq->axq_stageq, axq_headtype); 156268e8e04eSSam Leffler if (bf == NULL || ath_ff_flushdonetest(txq, bf)) { 156368e8e04eSSam Leffler ATH_TXQ_UNLOCK(txq); 156468e8e04eSSam Leffler break; 156568e8e04eSSam Leffler } 156668e8e04eSSam Leffler 156768e8e04eSSam Leffler ni = bf->bf_node; 156868e8e04eSSam Leffler pri = M_WME_GETAC(bf->bf_m); 156968e8e04eSSam Leffler KASSERT(ATH_NODE(ni)->an_ff_buf[pri], 157068e8e04eSSam Leffler ("no bf on staging queue %p", bf)); 157168e8e04eSSam Leffler ATH_NODE(ni)->an_ff_buf[pri] = NULL; 157268e8e04eSSam Leffler TAILQ_REMOVE(&txq->axq_stageq, bf, bf_stagelist); 157368e8e04eSSam Leffler 157468e8e04eSSam Leffler ATH_TXQ_UNLOCK(txq); 157568e8e04eSSam Leffler 157668e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_FF, "%s: flush frame, age %u\n", 157768e8e04eSSam Leffler __func__, bf->bf_age); 157868e8e04eSSam Leffler 157968e8e04eSSam Leffler sc->sc_stats.ast_ff_flush++; 158068e8e04eSSam Leffler 158168e8e04eSSam Leffler /* encap and xmit */ 1582b032f27cSSam Leffler bf->bf_m = ieee80211_encap(ni, bf->bf_m); 158368e8e04eSSam Leffler if (bf->bf_m == NULL) { 158468e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT | ATH_DEBUG_FF, 158568e8e04eSSam Leffler "%s: discard, encapsulation failure\n", 158668e8e04eSSam Leffler __func__); 158768e8e04eSSam Leffler sc->sc_stats.ast_tx_encap++; 158868e8e04eSSam Leffler goto bad; 158968e8e04eSSam Leffler } 159068e8e04eSSam Leffler pktlen = bf->bf_m->m_pkthdr.len; /* NB: don't reference below */ 159168e8e04eSSam Leffler if (ath_tx_start(sc, ni, bf, bf->bf_m) == 0) { 159268e8e04eSSam Leffler #if 0 /*XXX*/ 159368e8e04eSSam Leffler ifp->if_opackets++; 159468e8e04eSSam Leffler #endif 159568e8e04eSSam Leffler continue; 159668e8e04eSSam Leffler } 159768e8e04eSSam Leffler bad: 159868e8e04eSSam Leffler if (ni != NULL) 159968e8e04eSSam Leffler ieee80211_free_node(ni); 160068e8e04eSSam Leffler bf->bf_node = NULL; 160168e8e04eSSam Leffler if (bf->bf_m != NULL) { 160268e8e04eSSam Leffler m_freem(bf->bf_m); 160368e8e04eSSam Leffler bf->bf_m = NULL; 160468e8e04eSSam Leffler } 160568e8e04eSSam Leffler 160668e8e04eSSam Leffler ATH_TXBUF_LOCK(sc); 160768e8e04eSSam Leffler STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); 160868e8e04eSSam Leffler ATH_TXBUF_UNLOCK(sc); 160968e8e04eSSam Leffler } 161068e8e04eSSam Leffler } 161168e8e04eSSam Leffler 161268e8e04eSSam Leffler static __inline u_int32_t 161368e8e04eSSam Leffler ath_ff_approx_txtime(struct ath_softc *sc, struct ath_node *an, struct mbuf *m) 161468e8e04eSSam Leffler { 1615b032f27cSSam Leffler struct ieee80211com *ic = sc->sc_ifp->if_l2com; 161668e8e04eSSam Leffler u_int32_t framelen; 161768e8e04eSSam Leffler struct ath_buf *bf; 161868e8e04eSSam Leffler 161968e8e04eSSam Leffler /* 162068e8e04eSSam Leffler * Approximate the frame length to be transmitted. A swag to add 162168e8e04eSSam Leffler * the following maximal values to the skb payload: 162268e8e04eSSam Leffler * - 32: 802.11 encap + CRC 162368e8e04eSSam Leffler * - 24: encryption overhead (if wep bit) 162468e8e04eSSam Leffler * - 4 + 6: fast-frame header and padding 162568e8e04eSSam Leffler * - 16: 2 LLC FF tunnel headers 162668e8e04eSSam Leffler * - 14: 1 802.3 FF tunnel header (skb already accounts for 2nd) 162768e8e04eSSam Leffler */ 162868e8e04eSSam Leffler framelen = m->m_pkthdr.len + 32 + 4 + 6 + 16 + 14; 1629b032f27cSSam Leffler if (ic->ic_flags & IEEE80211_F_PRIVACY) 163068e8e04eSSam Leffler framelen += 24; 163168e8e04eSSam Leffler bf = an->an_ff_buf[M_WME_GETAC(m)]; 163268e8e04eSSam Leffler if (bf != NULL) 163368e8e04eSSam Leffler framelen += bf->bf_m->m_pkthdr.len; 163468e8e04eSSam Leffler return ath_hal_computetxtime(sc->sc_ah, sc->sc_currates, framelen, 163568e8e04eSSam Leffler sc->sc_lastdatarix, AH_FALSE); 163668e8e04eSSam Leffler } 163768e8e04eSSam Leffler 163868e8e04eSSam Leffler /* 163968e8e04eSSam Leffler * Determine if a data frame may be aggregated via ff tunnelling. 164068e8e04eSSam Leffler * Note the caller is responsible for checking if the destination 164168e8e04eSSam Leffler * supports fast frames. 164268e8e04eSSam Leffler * 164368e8e04eSSam Leffler * NB: allowing EAPOL frames to be aggregated with other unicast traffic. 164468e8e04eSSam Leffler * Do 802.1x EAPOL frames proceed in the clear? Then they couldn't 164568e8e04eSSam Leffler * be aggregated with other types of frames when encryption is on? 164668e8e04eSSam Leffler * 164768e8e04eSSam Leffler * NB: assumes lock on an_ff_buf effectively held by txq lock mechanism. 164868e8e04eSSam Leffler */ 164968e8e04eSSam Leffler static __inline int 165068e8e04eSSam Leffler ath_ff_can_aggregate(struct ath_softc *sc, 165168e8e04eSSam Leffler struct ath_node *an, struct mbuf *m, int *flushq) 165268e8e04eSSam Leffler { 1653b032f27cSSam Leffler struct ieee80211com *ic = sc->sc_ifp->if_l2com; 165468e8e04eSSam Leffler struct ath_txq *txq; 165568e8e04eSSam Leffler u_int32_t txoplimit; 165668e8e04eSSam Leffler u_int pri; 165768e8e04eSSam Leffler 165868e8e04eSSam Leffler *flushq = 0; 165968e8e04eSSam Leffler 166068e8e04eSSam Leffler /* 166168e8e04eSSam Leffler * If there is no frame to combine with and the txq has 166268e8e04eSSam Leffler * fewer frames than the minimum required; then do not 166368e8e04eSSam Leffler * attempt to aggregate this frame. 166468e8e04eSSam Leffler */ 166568e8e04eSSam Leffler pri = M_WME_GETAC(m); 166668e8e04eSSam Leffler txq = sc->sc_ac2q[pri]; 166768e8e04eSSam Leffler if (an->an_ff_buf[pri] == NULL && txq->axq_depth < sc->sc_fftxqmin) 166868e8e04eSSam Leffler return 0; 166968e8e04eSSam Leffler /* 167068e8e04eSSam Leffler * When not in station mode never aggregate a multicast 167168e8e04eSSam Leffler * frame; this insures, for example, that a combined frame 167268e8e04eSSam Leffler * does not require multiple encryption keys when using 167368e8e04eSSam Leffler * 802.1x/WPA. 167468e8e04eSSam Leffler */ 167568e8e04eSSam Leffler if (ic->ic_opmode != IEEE80211_M_STA && 167668e8e04eSSam Leffler ETHER_IS_MULTICAST(mtod(m, struct ether_header *)->ether_dhost)) 167768e8e04eSSam Leffler return 0; 167868e8e04eSSam Leffler /* 167968e8e04eSSam Leffler * Consult the max bursting interval to insure a combined 168068e8e04eSSam Leffler * frame fits within the TxOp window. 168168e8e04eSSam Leffler */ 168268e8e04eSSam Leffler txoplimit = IEEE80211_TXOP_TO_US( 168368e8e04eSSam Leffler ic->ic_wme.wme_chanParams.cap_wmeParams[pri].wmep_txopLimit); 168468e8e04eSSam Leffler if (txoplimit != 0 && ath_ff_approx_txtime(sc, an, m) > txoplimit) { 168568e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT | ATH_DEBUG_FF, 168668e8e04eSSam Leffler "%s: FF TxOp violation\n", __func__); 168768e8e04eSSam Leffler if (an->an_ff_buf[pri] != NULL) 168868e8e04eSSam Leffler *flushq = 1; 168968e8e04eSSam Leffler return 0; 169068e8e04eSSam Leffler } 169168e8e04eSSam Leffler return 1; /* try to aggregate */ 169268e8e04eSSam Leffler } 169368e8e04eSSam Leffler 169468e8e04eSSam Leffler /* 169568e8e04eSSam Leffler * Check if the supplied frame can be partnered with an existing 169668e8e04eSSam Leffler * or pending frame. Return a reference to any frame that should be 169768e8e04eSSam Leffler * sent on return; otherwise return NULL. 169868e8e04eSSam Leffler */ 169968e8e04eSSam Leffler static struct mbuf * 170068e8e04eSSam Leffler ath_ff_check(struct ath_softc *sc, struct ath_txq *txq, 170168e8e04eSSam Leffler struct ath_buf *bf, struct mbuf *m, struct ieee80211_node *ni) 170268e8e04eSSam Leffler { 170368e8e04eSSam Leffler struct ath_node *an = ATH_NODE(ni); 170468e8e04eSSam Leffler struct ath_buf *bfstaged; 170568e8e04eSSam Leffler int ff_flush, pri; 170668e8e04eSSam Leffler 170768e8e04eSSam Leffler /* 170868e8e04eSSam Leffler * Check if the supplied frame can be aggregated. 170968e8e04eSSam Leffler * 171068e8e04eSSam Leffler * NB: we use the txq lock to protect references to 171168e8e04eSSam Leffler * an->an_ff_txbuf in ath_ff_can_aggregate(). 171268e8e04eSSam Leffler */ 171368e8e04eSSam Leffler ATH_TXQ_LOCK(txq); 171468e8e04eSSam Leffler pri = M_WME_GETAC(m); 171568e8e04eSSam Leffler if (ath_ff_can_aggregate(sc, an, m, &ff_flush)) { 171668e8e04eSSam Leffler struct ath_buf *bfstaged = an->an_ff_buf[pri]; 171768e8e04eSSam Leffler if (bfstaged != NULL) { 171868e8e04eSSam Leffler /* 171968e8e04eSSam Leffler * A frame is available for partnering; remove 172068e8e04eSSam Leffler * it, chain it to this one, and encapsulate. 172168e8e04eSSam Leffler */ 172268e8e04eSSam Leffler an->an_ff_buf[pri] = NULL; 172368e8e04eSSam Leffler TAILQ_REMOVE(&txq->axq_stageq, bfstaged, bf_stagelist); 172468e8e04eSSam Leffler ATH_TXQ_UNLOCK(txq); 172568e8e04eSSam Leffler 172668e8e04eSSam Leffler /* 172768e8e04eSSam Leffler * Chain mbufs and add FF magic. 172868e8e04eSSam Leffler */ 172968e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_FF, 173068e8e04eSSam Leffler "[%s] aggregate fast-frame, age %u\n", 173168e8e04eSSam Leffler ether_sprintf(ni->ni_macaddr), txq->axq_curage); 173268e8e04eSSam Leffler m->m_nextpkt = NULL; 173368e8e04eSSam Leffler bfstaged->bf_m->m_nextpkt = m; 173468e8e04eSSam Leffler m = bfstaged->bf_m; 173568e8e04eSSam Leffler bfstaged->bf_m = NULL; 173668e8e04eSSam Leffler m->m_flags |= M_FF; 173768e8e04eSSam Leffler /* 173868e8e04eSSam Leffler * Release the node reference held while 173968e8e04eSSam Leffler * the packet sat on an_ff_buf[] 174068e8e04eSSam Leffler */ 174168e8e04eSSam Leffler bfstaged->bf_node = NULL; 174268e8e04eSSam Leffler ieee80211_free_node(ni); 174368e8e04eSSam Leffler 174468e8e04eSSam Leffler /* 174568e8e04eSSam Leffler * Return bfstaged to the free list. 174668e8e04eSSam Leffler */ 174768e8e04eSSam Leffler ATH_TXBUF_LOCK(sc); 174868e8e04eSSam Leffler STAILQ_INSERT_TAIL(&sc->sc_txbuf, bfstaged, bf_list); 174968e8e04eSSam Leffler ATH_TXBUF_UNLOCK(sc); 175068e8e04eSSam Leffler 175168e8e04eSSam Leffler return m; /* ready to go */ 175268e8e04eSSam Leffler } else { 175368e8e04eSSam Leffler /* 175468e8e04eSSam Leffler * No frame available, queue this frame to wait 175568e8e04eSSam Leffler * for a partner. Note that we hold the buffer 175668e8e04eSSam Leffler * and a reference to the node; we need the 175768e8e04eSSam Leffler * buffer in particular so we're certain we 175868e8e04eSSam Leffler * can flush the frame at a later time. 175968e8e04eSSam Leffler */ 176068e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_FF, 176168e8e04eSSam Leffler "[%s] stage fast-frame, age %u\n", 176268e8e04eSSam Leffler ether_sprintf(ni->ni_macaddr), txq->axq_curage); 176368e8e04eSSam Leffler 176468e8e04eSSam Leffler bf->bf_m = m; 176568e8e04eSSam Leffler bf->bf_node = ni; /* NB: held reference */ 176668e8e04eSSam Leffler bf->bf_age = txq->axq_curage; 176768e8e04eSSam Leffler an->an_ff_buf[pri] = bf; 176868e8e04eSSam Leffler TAILQ_INSERT_HEAD(&txq->axq_stageq, bf, bf_stagelist); 176968e8e04eSSam Leffler ATH_TXQ_UNLOCK(txq); 177068e8e04eSSam Leffler 177168e8e04eSSam Leffler return NULL; /* consumed */ 177268e8e04eSSam Leffler } 177368e8e04eSSam Leffler } 177468e8e04eSSam Leffler /* 177568e8e04eSSam Leffler * Frame could not be aggregated, it needs to be returned 177668e8e04eSSam Leffler * to the caller for immediate transmission. In addition 177768e8e04eSSam Leffler * we check if we should first flush a frame from the 177868e8e04eSSam Leffler * staging queue before sending this one. 177968e8e04eSSam Leffler * 178068e8e04eSSam Leffler * NB: ath_ff_can_aggregate only marks ff_flush if a frame 178168e8e04eSSam Leffler * is present to flush. 178268e8e04eSSam Leffler */ 178368e8e04eSSam Leffler if (ff_flush) { 178468e8e04eSSam Leffler int pktlen; 178568e8e04eSSam Leffler 178668e8e04eSSam Leffler bfstaged = an->an_ff_buf[pri]; 178768e8e04eSSam Leffler an->an_ff_buf[pri] = NULL; 178868e8e04eSSam Leffler TAILQ_REMOVE(&txq->axq_stageq, bfstaged, bf_stagelist); 178968e8e04eSSam Leffler ATH_TXQ_UNLOCK(txq); 179068e8e04eSSam Leffler 179168e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_FF, "[%s] flush staged frame\n", 179268e8e04eSSam Leffler ether_sprintf(an->an_node.ni_macaddr)); 179368e8e04eSSam Leffler 179468e8e04eSSam Leffler /* encap and xmit */ 1795b032f27cSSam Leffler bfstaged->bf_m = ieee80211_encap(ni, bfstaged->bf_m); 179668e8e04eSSam Leffler if (bfstaged->bf_m == NULL) { 179768e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT | ATH_DEBUG_FF, 179868e8e04eSSam Leffler "%s: discard, encap failure\n", __func__); 179968e8e04eSSam Leffler sc->sc_stats.ast_tx_encap++; 180068e8e04eSSam Leffler goto ff_flushbad; 180168e8e04eSSam Leffler } 180268e8e04eSSam Leffler pktlen = bfstaged->bf_m->m_pkthdr.len; 180368e8e04eSSam Leffler if (ath_tx_start(sc, ni, bfstaged, bfstaged->bf_m)) { 180468e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT, 180568e8e04eSSam Leffler "%s: discard, xmit failure\n", __func__); 180668e8e04eSSam Leffler ff_flushbad: 180768e8e04eSSam Leffler /* 180868e8e04eSSam Leffler * Unable to transmit frame that was on the staging 180968e8e04eSSam Leffler * queue. Reclaim the node reference and other 181068e8e04eSSam Leffler * resources. 181168e8e04eSSam Leffler */ 181268e8e04eSSam Leffler if (ni != NULL) 181368e8e04eSSam Leffler ieee80211_free_node(ni); 181468e8e04eSSam Leffler bfstaged->bf_node = NULL; 181568e8e04eSSam Leffler if (bfstaged->bf_m != NULL) { 181668e8e04eSSam Leffler m_freem(bfstaged->bf_m); 181768e8e04eSSam Leffler bfstaged->bf_m = NULL; 181868e8e04eSSam Leffler } 181968e8e04eSSam Leffler 182068e8e04eSSam Leffler ATH_TXBUF_LOCK(sc); 182168e8e04eSSam Leffler STAILQ_INSERT_TAIL(&sc->sc_txbuf, bfstaged, bf_list); 182268e8e04eSSam Leffler ATH_TXBUF_UNLOCK(sc); 182368e8e04eSSam Leffler } else { 182468e8e04eSSam Leffler #if 0 182568e8e04eSSam Leffler ifp->if_opackets++; 182668e8e04eSSam Leffler #endif 182768e8e04eSSam Leffler } 182868e8e04eSSam Leffler } else { 182968e8e04eSSam Leffler if (an->an_ff_buf[pri] != NULL) { 183068e8e04eSSam Leffler /* 183168e8e04eSSam Leffler * XXX: out-of-order condition only occurs for AP 183268e8e04eSSam Leffler * mode and multicast. There may be no valid way 183368e8e04eSSam Leffler * to get this condition. 183468e8e04eSSam Leffler */ 183568e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_FF, "[%s] out-of-order frame\n", 183668e8e04eSSam Leffler ether_sprintf(an->an_node.ni_macaddr)); 183768e8e04eSSam Leffler /* XXX stat */ 183868e8e04eSSam Leffler } 183968e8e04eSSam Leffler ATH_TXQ_UNLOCK(txq); 184068e8e04eSSam Leffler } 184168e8e04eSSam Leffler return m; 184268e8e04eSSam Leffler } 184368e8e04eSSam Leffler 184468e8e04eSSam Leffler /* 184568e8e04eSSam Leffler * Cleanup driver resources when we run out of buffers 184668e8e04eSSam Leffler * while processing fragments; return the tx buffers 184768e8e04eSSam Leffler * allocated and drop node references. 184868e8e04eSSam Leffler */ 184968e8e04eSSam Leffler static void 185068e8e04eSSam Leffler ath_txfrag_cleanup(struct ath_softc *sc, 185168e8e04eSSam Leffler ath_bufhead *frags, struct ieee80211_node *ni) 185268e8e04eSSam Leffler { 185368e8e04eSSam Leffler struct ath_buf *bf, *next; 185468e8e04eSSam Leffler 185568e8e04eSSam Leffler ATH_TXBUF_LOCK_ASSERT(sc); 185668e8e04eSSam Leffler 185768e8e04eSSam Leffler STAILQ_FOREACH_SAFE(bf, frags, bf_list, next) { 185868e8e04eSSam Leffler /* NB: bf assumed clean */ 185968e8e04eSSam Leffler STAILQ_REMOVE_HEAD(frags, bf_list); 186068e8e04eSSam Leffler STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); 186168e8e04eSSam Leffler ieee80211_node_decref(ni); 186268e8e04eSSam Leffler } 186368e8e04eSSam Leffler } 186468e8e04eSSam Leffler 186568e8e04eSSam Leffler /* 186668e8e04eSSam Leffler * Setup xmit of a fragmented frame. Allocate a buffer 186768e8e04eSSam Leffler * for each frag and bump the node reference count to 186868e8e04eSSam Leffler * reflect the held reference to be setup by ath_tx_start. 186968e8e04eSSam Leffler */ 187068e8e04eSSam Leffler static int 187168e8e04eSSam Leffler ath_txfrag_setup(struct ath_softc *sc, ath_bufhead *frags, 187268e8e04eSSam Leffler struct mbuf *m0, struct ieee80211_node *ni) 187368e8e04eSSam Leffler { 187468e8e04eSSam Leffler struct mbuf *m; 187568e8e04eSSam Leffler struct ath_buf *bf; 187668e8e04eSSam Leffler 187768e8e04eSSam Leffler ATH_TXBUF_LOCK(sc); 187868e8e04eSSam Leffler for (m = m0->m_nextpkt; m != NULL; m = m->m_nextpkt) { 187968e8e04eSSam Leffler bf = STAILQ_FIRST(&sc->sc_txbuf); 188068e8e04eSSam Leffler if (bf == NULL) { /* out of buffers, cleanup */ 188168e8e04eSSam Leffler ath_txfrag_cleanup(sc, frags, ni); 188268e8e04eSSam Leffler break; 188368e8e04eSSam Leffler } 188468e8e04eSSam Leffler STAILQ_REMOVE_HEAD(&sc->sc_txbuf, bf_list); 188568e8e04eSSam Leffler ieee80211_node_incref(ni); 188668e8e04eSSam Leffler STAILQ_INSERT_TAIL(frags, bf, bf_list); 188768e8e04eSSam Leffler } 188868e8e04eSSam Leffler ATH_TXBUF_UNLOCK(sc); 188968e8e04eSSam Leffler 189068e8e04eSSam Leffler return !STAILQ_EMPTY(frags); 189168e8e04eSSam Leffler } 189268e8e04eSSam Leffler 18935591b213SSam Leffler static void 18945591b213SSam Leffler ath_start(struct ifnet *ifp) 18955591b213SSam Leffler { 18965591b213SSam Leffler struct ath_softc *sc = ifp->if_softc; 1897b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 18985591b213SSam Leffler struct ieee80211_node *ni; 18995591b213SSam Leffler struct ath_buf *bf; 190068e8e04eSSam Leffler struct mbuf *m, *next; 190168e8e04eSSam Leffler struct ath_txq *txq; 190268e8e04eSSam Leffler ath_bufhead frags; 190368e8e04eSSam Leffler int pri; 19045591b213SSam Leffler 190513f4c340SRobert Watson if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || sc->sc_invalid) 19065591b213SSam Leffler return; 19075591b213SSam Leffler for (;;) { 19085591b213SSam Leffler /* 19095591b213SSam Leffler * Grab a TX buffer and associated resources. 19105591b213SSam Leffler */ 1911f0b2a0beSSam Leffler ATH_TXBUF_LOCK(sc); 1912c42a7b7eSSam Leffler bf = STAILQ_FIRST(&sc->sc_txbuf); 1913ebecf802SSam Leffler if (bf != NULL) 1914c42a7b7eSSam Leffler STAILQ_REMOVE_HEAD(&sc->sc_txbuf, bf_list); 1915f0b2a0beSSam Leffler ATH_TXBUF_UNLOCK(sc); 19165591b213SSam Leffler if (bf == NULL) { 1917ebecf802SSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT, "%s: out of xmit buffers\n", 1918ebecf802SSam Leffler __func__); 19195591b213SSam Leffler sc->sc_stats.ast_tx_qstop++; 192013f4c340SRobert Watson ifp->if_drv_flags |= IFF_DRV_OACTIVE; 19215591b213SSam Leffler break; 19225591b213SSam Leffler } 19232b9411e2SSam Leffler 1924b032f27cSSam Leffler IFQ_DEQUEUE(&ifp->if_snd, m); 1925b032f27cSSam Leffler if (m == NULL) { 1926b032f27cSSam Leffler ATH_TXBUF_LOCK(sc); 1927b032f27cSSam Leffler STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); 1928b032f27cSSam Leffler ATH_TXBUF_UNLOCK(sc); 1929b032f27cSSam Leffler break; 1930b032f27cSSam Leffler } 193168e8e04eSSam Leffler STAILQ_INIT(&frags); 1932b032f27cSSam Leffler ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; 193368e8e04eSSam Leffler pri = M_WME_GETAC(m); 193468e8e04eSSam Leffler txq = sc->sc_ac2q[pri]; 193568e8e04eSSam Leffler if (ni->ni_ath_flags & IEEE80211_NODE_FF) { 193668e8e04eSSam Leffler /* 193768e8e04eSSam Leffler * Check queue length; if too deep drop this 193868e8e04eSSam Leffler * frame (tail drop considered good). 193968e8e04eSSam Leffler */ 194068e8e04eSSam Leffler if (txq->axq_depth >= sc->sc_fftxqmax) { 194168e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_FF, 194268e8e04eSSam Leffler "[%s] tail drop on q %u depth %u\n", 194368e8e04eSSam Leffler ether_sprintf(ni->ni_macaddr), 194468e8e04eSSam Leffler txq->axq_qnum, txq->axq_depth); 194568e8e04eSSam Leffler sc->sc_stats.ast_tx_qfull++; 194668e8e04eSSam Leffler m_freem(m); 194768e8e04eSSam Leffler goto reclaim; 194868e8e04eSSam Leffler } 194968e8e04eSSam Leffler m = ath_ff_check(sc, txq, bf, m, ni); 195068e8e04eSSam Leffler if (m == NULL) { 195168e8e04eSSam Leffler /* NB: ni ref & bf held on stageq */ 195268e8e04eSSam Leffler continue; 195368e8e04eSSam Leffler } 195468e8e04eSSam Leffler } 19555591b213SSam Leffler ifp->if_opackets++; 19565591b213SSam Leffler /* 19575591b213SSam Leffler * Encapsulate the packet in prep for transmission. 19585591b213SSam Leffler */ 1959b032f27cSSam Leffler m = ieee80211_encap(ni, m); 19605591b213SSam Leffler if (m == NULL) { 1961370572d9SSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT, 1962b032f27cSSam Leffler "%s: encapsulation failure\n", __func__); 19635591b213SSam Leffler sc->sc_stats.ast_tx_encap++; 19645591b213SSam Leffler goto bad; 19655591b213SSam Leffler } 196668e8e04eSSam Leffler /* 196768e8e04eSSam Leffler * Check for fragmentation. If this frame 196868e8e04eSSam Leffler * has been broken up verify we have enough 196968e8e04eSSam Leffler * buffers to send all the fragments so all 197068e8e04eSSam Leffler * go out or none... 197168e8e04eSSam Leffler */ 197268e8e04eSSam Leffler if ((m->m_flags & M_FRAG) && 197368e8e04eSSam Leffler !ath_txfrag_setup(sc, &frags, m, ni)) { 197468e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT, 197568e8e04eSSam Leffler "%s: out of txfrag buffers\n", __func__); 197668e8e04eSSam Leffler ic->ic_stats.is_tx_nobuf++; /* XXX */ 197768e8e04eSSam Leffler ath_freetx(m); 197868e8e04eSSam Leffler goto bad; 197968e8e04eSSam Leffler } 198068e8e04eSSam Leffler nextfrag: 198168e8e04eSSam Leffler /* 198268e8e04eSSam Leffler * Pass the frame to the h/w for transmission. 198368e8e04eSSam Leffler * Fragmented frames have each frag chained together 198468e8e04eSSam Leffler * with m_nextpkt. We know there are sufficient ath_buf's 198568e8e04eSSam Leffler * to send all the frags because of work done by 198668e8e04eSSam Leffler * ath_txfrag_setup. We leave m_nextpkt set while 198768e8e04eSSam Leffler * calling ath_tx_start so it can use it to extend the 198868e8e04eSSam Leffler * the tx duration to cover the subsequent frag and 198968e8e04eSSam Leffler * so it can reclaim all the mbufs in case of an error; 199068e8e04eSSam Leffler * ath_tx_start clears m_nextpkt once it commits to 199168e8e04eSSam Leffler * handing the frame to the hardware. 199268e8e04eSSam Leffler */ 199368e8e04eSSam Leffler next = m->m_nextpkt; 19945591b213SSam Leffler if (ath_tx_start(sc, ni, bf, m)) { 19955591b213SSam Leffler bad: 19965591b213SSam Leffler ifp->if_oerrors++; 1997c42a7b7eSSam Leffler reclaim: 199868e8e04eSSam Leffler bf->bf_m = NULL; 199968e8e04eSSam Leffler bf->bf_node = NULL; 2000c42a7b7eSSam Leffler ATH_TXBUF_LOCK(sc); 2001ebecf802SSam Leffler STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); 200268e8e04eSSam Leffler ath_txfrag_cleanup(sc, &frags, ni); 2003c42a7b7eSSam Leffler ATH_TXBUF_UNLOCK(sc); 2004c42a7b7eSSam Leffler if (ni != NULL) 2005c42a7b7eSSam Leffler ieee80211_free_node(ni); 20065591b213SSam Leffler continue; 20075591b213SSam Leffler } 200868e8e04eSSam Leffler if (next != NULL) { 200968e8e04eSSam Leffler /* 201068e8e04eSSam Leffler * Beware of state changing between frags. 201168e8e04eSSam Leffler * XXX check sta power-save state? 201268e8e04eSSam Leffler */ 2013b032f27cSSam Leffler if (ni->ni_vap->iv_state != IEEE80211_S_RUN) { 201468e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT, 201568e8e04eSSam Leffler "%s: flush fragmented packet, state %s\n", 201668e8e04eSSam Leffler __func__, 2017b032f27cSSam Leffler ieee80211_state_name[ni->ni_vap->iv_state]); 201868e8e04eSSam Leffler ath_freetx(next); 201968e8e04eSSam Leffler goto reclaim; 202068e8e04eSSam Leffler } 202168e8e04eSSam Leffler m = next; 202268e8e04eSSam Leffler bf = STAILQ_FIRST(&frags); 202368e8e04eSSam Leffler KASSERT(bf != NULL, ("no buf for txfrag")); 202468e8e04eSSam Leffler STAILQ_REMOVE_HEAD(&frags, bf_list); 202568e8e04eSSam Leffler goto nextfrag; 202668e8e04eSSam Leffler } 20275591b213SSam Leffler 202868e8e04eSSam Leffler ifp->if_timer = 5; 202968e8e04eSSam Leffler #if 0 203068e8e04eSSam Leffler /* 203168e8e04eSSam Leffler * Flush stale frames from the fast-frame staging queue. 203268e8e04eSSam Leffler */ 203368e8e04eSSam Leffler if (ic->ic_opmode != IEEE80211_M_STA) 203468e8e04eSSam Leffler ath_ff_stageq_flush(sc, txq, ath_ff_ageflushtestdone); 203568e8e04eSSam Leffler #endif 20365591b213SSam Leffler } 20375591b213SSam Leffler } 20385591b213SSam Leffler 20395591b213SSam Leffler static int 20405591b213SSam Leffler ath_media_change(struct ifnet *ifp) 20415591b213SSam Leffler { 2042b032f27cSSam Leffler int error = ieee80211_media_change(ifp); 2043b032f27cSSam Leffler /* NB: only the fixed rate can change and that doesn't need a reset */ 2044b032f27cSSam Leffler return (error == ENETRESET ? 0 : error); 20455591b213SSam Leffler } 20465591b213SSam Leffler 2047a585a9a1SSam Leffler #ifdef ATH_DEBUG 2048c42a7b7eSSam Leffler static void 20495901d2d3SSam Leffler ath_keyprint(struct ath_softc *sc, const char *tag, u_int ix, 2050c42a7b7eSSam Leffler const HAL_KEYVAL *hk, const u_int8_t mac[IEEE80211_ADDR_LEN]) 20515591b213SSam Leffler { 2052c42a7b7eSSam Leffler static const char *ciphers[] = { 2053c42a7b7eSSam Leffler "WEP", 2054c42a7b7eSSam Leffler "AES-OCB", 2055c42a7b7eSSam Leffler "AES-CCM", 2056c42a7b7eSSam Leffler "CKIP", 2057c42a7b7eSSam Leffler "TKIP", 2058c42a7b7eSSam Leffler "CLR", 2059c42a7b7eSSam Leffler }; 2060c42a7b7eSSam Leffler int i, n; 20615591b213SSam Leffler 2062c42a7b7eSSam Leffler printf("%s: [%02u] %-7s ", tag, ix, ciphers[hk->kv_type]); 2063c42a7b7eSSam Leffler for (i = 0, n = hk->kv_len; i < n; i++) 2064c42a7b7eSSam Leffler printf("%02x", hk->kv_val[i]); 2065c42a7b7eSSam Leffler printf(" mac %s", ether_sprintf(mac)); 2066c42a7b7eSSam Leffler if (hk->kv_type == HAL_CIPHER_TKIP) { 20675901d2d3SSam Leffler printf(" %s ", sc->sc_splitmic ? "mic" : "rxmic"); 2068c42a7b7eSSam Leffler for (i = 0; i < sizeof(hk->kv_mic); i++) 2069c42a7b7eSSam Leffler printf("%02x", hk->kv_mic[i]); 20705901d2d3SSam Leffler #if HAL_ABI_VERSION > 0x06052200 20715901d2d3SSam Leffler if (!sc->sc_splitmic) { 20725901d2d3SSam Leffler printf(" txmic "); 20735901d2d3SSam Leffler for (i = 0; i < sizeof(hk->kv_txmic); i++) 20745901d2d3SSam Leffler printf("%02x", hk->kv_txmic[i]); 20755901d2d3SSam Leffler } 20765901d2d3SSam Leffler #endif 20772075afbaSSam Leffler } 2078c42a7b7eSSam Leffler printf("\n"); 2079c42a7b7eSSam Leffler } 2080c42a7b7eSSam Leffler #endif 2081c42a7b7eSSam Leffler 20825591b213SSam Leffler /* 2083c42a7b7eSSam Leffler * Set a TKIP key into the hardware. This handles the 2084c42a7b7eSSam Leffler * potential distribution of key state to multiple key 2085c42a7b7eSSam Leffler * cache slots for TKIP. 20865591b213SSam Leffler */ 2087c42a7b7eSSam Leffler static int 2088c42a7b7eSSam Leffler ath_keyset_tkip(struct ath_softc *sc, const struct ieee80211_key *k, 2089c42a7b7eSSam Leffler HAL_KEYVAL *hk, const u_int8_t mac[IEEE80211_ADDR_LEN]) 2090c42a7b7eSSam Leffler { 2091c42a7b7eSSam Leffler #define IEEE80211_KEY_XR (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV) 2092c42a7b7eSSam Leffler static const u_int8_t zerobssid[IEEE80211_ADDR_LEN]; 20938cec0ab9SSam Leffler struct ath_hal *ah = sc->sc_ah; 20948cec0ab9SSam Leffler 2095c42a7b7eSSam Leffler KASSERT(k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP, 2096c42a7b7eSSam Leffler ("got a non-TKIP key, cipher %u", k->wk_cipher->ic_cipher)); 2097c42a7b7eSSam Leffler if ((k->wk_flags & IEEE80211_KEY_XR) == IEEE80211_KEY_XR) { 20985901d2d3SSam Leffler if (sc->sc_splitmic) { 2099c42a7b7eSSam Leffler /* 2100c1225b52SSam Leffler * TX key goes at first index, RX key at the rx index. 2101c42a7b7eSSam Leffler * The hal handles the MIC keys at index+64. 2102c42a7b7eSSam Leffler */ 2103c42a7b7eSSam Leffler memcpy(hk->kv_mic, k->wk_txmic, sizeof(hk->kv_mic)); 2104c42a7b7eSSam Leffler KEYPRINTF(sc, k->wk_keyix, hk, zerobssid); 2105c42a7b7eSSam Leffler if (!ath_hal_keyset(ah, k->wk_keyix, hk, zerobssid)) 2106c42a7b7eSSam Leffler return 0; 2107c42a7b7eSSam Leffler 2108c42a7b7eSSam Leffler memcpy(hk->kv_mic, k->wk_rxmic, sizeof(hk->kv_mic)); 2109c42a7b7eSSam Leffler KEYPRINTF(sc, k->wk_keyix+32, hk, mac); 2110c42a7b7eSSam Leffler /* XXX delete tx key on failure? */ 2111c42a7b7eSSam Leffler return ath_hal_keyset(ah, k->wk_keyix+32, hk, mac); 21125901d2d3SSam Leffler } else { 21135901d2d3SSam Leffler /* 21145901d2d3SSam Leffler * Room for both TX+RX MIC keys in one key cache 21155901d2d3SSam Leffler * slot, just set key at the first index; the hal 2116b032f27cSSam Leffler * will handle the rest. 21175901d2d3SSam Leffler */ 21185901d2d3SSam Leffler memcpy(hk->kv_mic, k->wk_rxmic, sizeof(hk->kv_mic)); 21195901d2d3SSam Leffler #if HAL_ABI_VERSION > 0x06052200 21205901d2d3SSam Leffler memcpy(hk->kv_txmic, k->wk_txmic, sizeof(hk->kv_txmic)); 21215901d2d3SSam Leffler #endif 21225901d2d3SSam Leffler KEYPRINTF(sc, k->wk_keyix, hk, mac); 21235901d2d3SSam Leffler return ath_hal_keyset(ah, k->wk_keyix, hk, mac); 21245901d2d3SSam Leffler } 2125b032f27cSSam Leffler } else if (k->wk_flags & IEEE80211_KEY_XMIT) { 2126b032f27cSSam Leffler #if HAL_ABI_VERSION > 0x06052200 2127b032f27cSSam Leffler memcpy(hk->kv_txmic, k->wk_txmic, sizeof(hk->kv_txmic)); 2128b032f27cSSam Leffler #else 2129b032f27cSSam Leffler memcpy(hk->kv_mic, k->wk_mic, sizeof(hk->kv_mic)); 2130b032f27cSSam Leffler #endif 2131b032f27cSSam Leffler KEYPRINTF(sc, k->wk_keyix, hk, mac); 2132b032f27cSSam Leffler return ath_hal_keyset(ah, k->wk_keyix, hk, mac); 2133b032f27cSSam Leffler } else if (k->wk_flags & IEEE80211_KEY_RECV) { 2134b032f27cSSam Leffler memcpy(hk->kv_mic, k->wk_rxmic, sizeof(hk->kv_mic)); 2135e8fd88a3SSam Leffler KEYPRINTF(sc, k->wk_keyix, hk, mac); 2136e8fd88a3SSam Leffler return ath_hal_keyset(ah, k->wk_keyix, hk, mac); 2137c42a7b7eSSam Leffler } 2138c42a7b7eSSam Leffler return 0; 2139c42a7b7eSSam Leffler #undef IEEE80211_KEY_XR 2140c42a7b7eSSam Leffler } 2141c42a7b7eSSam Leffler 2142c42a7b7eSSam Leffler /* 2143c42a7b7eSSam Leffler * Set a net80211 key into the hardware. This handles the 2144c42a7b7eSSam Leffler * potential distribution of key state to multiple key 2145c42a7b7eSSam Leffler * cache slots for TKIP with hardware MIC support. 2146c42a7b7eSSam Leffler */ 2147c42a7b7eSSam Leffler static int 2148c42a7b7eSSam Leffler ath_keyset(struct ath_softc *sc, const struct ieee80211_key *k, 2149e8fd88a3SSam Leffler const u_int8_t mac0[IEEE80211_ADDR_LEN], 2150e8fd88a3SSam Leffler struct ieee80211_node *bss) 2151c42a7b7eSSam Leffler { 2152c42a7b7eSSam Leffler #define N(a) (sizeof(a)/sizeof(a[0])) 2153c42a7b7eSSam Leffler static const u_int8_t ciphermap[] = { 2154c42a7b7eSSam Leffler HAL_CIPHER_WEP, /* IEEE80211_CIPHER_WEP */ 2155c42a7b7eSSam Leffler HAL_CIPHER_TKIP, /* IEEE80211_CIPHER_TKIP */ 2156c42a7b7eSSam Leffler HAL_CIPHER_AES_OCB, /* IEEE80211_CIPHER_AES_OCB */ 2157c42a7b7eSSam Leffler HAL_CIPHER_AES_CCM, /* IEEE80211_CIPHER_AES_CCM */ 2158c42a7b7eSSam Leffler (u_int8_t) -1, /* 4 is not allocated */ 2159c42a7b7eSSam Leffler HAL_CIPHER_CKIP, /* IEEE80211_CIPHER_CKIP */ 2160c42a7b7eSSam Leffler HAL_CIPHER_CLR, /* IEEE80211_CIPHER_NONE */ 2161c42a7b7eSSam Leffler }; 2162c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 2163c42a7b7eSSam Leffler const struct ieee80211_cipher *cip = k->wk_cipher; 2164e8fd88a3SSam Leffler u_int8_t gmac[IEEE80211_ADDR_LEN]; 2165e8fd88a3SSam Leffler const u_int8_t *mac; 2166c42a7b7eSSam Leffler HAL_KEYVAL hk; 2167c42a7b7eSSam Leffler 2168c42a7b7eSSam Leffler memset(&hk, 0, sizeof(hk)); 2169c42a7b7eSSam Leffler /* 2170c42a7b7eSSam Leffler * Software crypto uses a "clear key" so non-crypto 2171c42a7b7eSSam Leffler * state kept in the key cache are maintained and 2172c42a7b7eSSam Leffler * so that rx frames have an entry to match. 2173c42a7b7eSSam Leffler */ 2174c42a7b7eSSam Leffler if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) == 0) { 2175c42a7b7eSSam Leffler KASSERT(cip->ic_cipher < N(ciphermap), 2176c42a7b7eSSam Leffler ("invalid cipher type %u", cip->ic_cipher)); 2177c42a7b7eSSam Leffler hk.kv_type = ciphermap[cip->ic_cipher]; 2178c42a7b7eSSam Leffler hk.kv_len = k->wk_keylen; 2179c42a7b7eSSam Leffler memcpy(hk.kv_val, k->wk_key, k->wk_keylen); 21808cec0ab9SSam Leffler } else 2181c42a7b7eSSam Leffler hk.kv_type = HAL_CIPHER_CLR; 2182c42a7b7eSSam Leffler 2183e8fd88a3SSam Leffler if ((k->wk_flags & IEEE80211_KEY_GROUP) && sc->sc_mcastkey) { 2184e8fd88a3SSam Leffler /* 2185e8fd88a3SSam Leffler * Group keys on hardware that supports multicast frame 2186e8fd88a3SSam Leffler * key search use a mac that is the sender's address with 2187e8fd88a3SSam Leffler * the high bit set instead of the app-specified address. 2188e8fd88a3SSam Leffler */ 2189e8fd88a3SSam Leffler IEEE80211_ADDR_COPY(gmac, bss->ni_macaddr); 2190e8fd88a3SSam Leffler gmac[0] |= 0x80; 2191e8fd88a3SSam Leffler mac = gmac; 2192e8fd88a3SSam Leffler } else 2193e8fd88a3SSam Leffler mac = mac0; 2194e8fd88a3SSam Leffler 2195c42a7b7eSSam Leffler if (hk.kv_type == HAL_CIPHER_TKIP && 21965901d2d3SSam Leffler (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) { 2197c42a7b7eSSam Leffler return ath_keyset_tkip(sc, k, &hk, mac); 2198c42a7b7eSSam Leffler } else { 2199c42a7b7eSSam Leffler KEYPRINTF(sc, k->wk_keyix, &hk, mac); 2200c42a7b7eSSam Leffler return ath_hal_keyset(ah, k->wk_keyix, &hk, mac); 22018cec0ab9SSam Leffler } 2202c42a7b7eSSam Leffler #undef N 22035591b213SSam Leffler } 22045591b213SSam Leffler 22055591b213SSam Leffler /* 2206c42a7b7eSSam Leffler * Allocate tx/rx key slots for TKIP. We allocate two slots for 2207c42a7b7eSSam Leffler * each key, one for decrypt/encrypt and the other for the MIC. 2208c42a7b7eSSam Leffler */ 2209c42a7b7eSSam Leffler static u_int16_t 2210c1225b52SSam Leffler key_alloc_2pair(struct ath_softc *sc, 2211c1225b52SSam Leffler ieee80211_keyix *txkeyix, ieee80211_keyix *rxkeyix) 2212c42a7b7eSSam Leffler { 2213c42a7b7eSSam Leffler #define N(a) (sizeof(a)/sizeof(a[0])) 2214c42a7b7eSSam Leffler u_int i, keyix; 2215c42a7b7eSSam Leffler 2216c42a7b7eSSam Leffler KASSERT(sc->sc_splitmic, ("key cache !split")); 2217c42a7b7eSSam Leffler /* XXX could optimize */ 2218c42a7b7eSSam Leffler for (i = 0; i < N(sc->sc_keymap)/4; i++) { 2219c42a7b7eSSam Leffler u_int8_t b = sc->sc_keymap[i]; 2220c42a7b7eSSam Leffler if (b != 0xff) { 2221c42a7b7eSSam Leffler /* 2222c42a7b7eSSam Leffler * One or more slots in this byte are free. 2223c42a7b7eSSam Leffler */ 2224c42a7b7eSSam Leffler keyix = i*NBBY; 2225c42a7b7eSSam Leffler while (b & 1) { 2226c42a7b7eSSam Leffler again: 2227c42a7b7eSSam Leffler keyix++; 2228c42a7b7eSSam Leffler b >>= 1; 2229c42a7b7eSSam Leffler } 2230c42a7b7eSSam Leffler /* XXX IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV */ 2231c42a7b7eSSam Leffler if (isset(sc->sc_keymap, keyix+32) || 2232c42a7b7eSSam Leffler isset(sc->sc_keymap, keyix+64) || 2233c42a7b7eSSam Leffler isset(sc->sc_keymap, keyix+32+64)) { 2234c42a7b7eSSam Leffler /* full pair unavailable */ 2235c42a7b7eSSam Leffler /* XXX statistic */ 2236c42a7b7eSSam Leffler if (keyix == (i+1)*NBBY) { 2237c42a7b7eSSam Leffler /* no slots were appropriate, advance */ 2238c42a7b7eSSam Leffler continue; 2239c42a7b7eSSam Leffler } 2240c42a7b7eSSam Leffler goto again; 2241c42a7b7eSSam Leffler } 2242c42a7b7eSSam Leffler setbit(sc->sc_keymap, keyix); 2243c42a7b7eSSam Leffler setbit(sc->sc_keymap, keyix+64); 2244c42a7b7eSSam Leffler setbit(sc->sc_keymap, keyix+32); 2245c42a7b7eSSam Leffler setbit(sc->sc_keymap, keyix+32+64); 2246c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_KEYCACHE, 2247c42a7b7eSSam Leffler "%s: key pair %u,%u %u,%u\n", 2248c42a7b7eSSam Leffler __func__, keyix, keyix+64, 2249c42a7b7eSSam Leffler keyix+32, keyix+32+64); 2250c1225b52SSam Leffler *txkeyix = keyix; 2251c1225b52SSam Leffler *rxkeyix = keyix+32; 2252c1225b52SSam Leffler return 1; 2253c42a7b7eSSam Leffler } 2254c42a7b7eSSam Leffler } 2255c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: out of pair space\n", __func__); 2256c1225b52SSam Leffler return 0; 2257c42a7b7eSSam Leffler #undef N 2258c42a7b7eSSam Leffler } 2259c42a7b7eSSam Leffler 2260c42a7b7eSSam Leffler /* 22615901d2d3SSam Leffler * Allocate tx/rx key slots for TKIP. We allocate two slots for 22625901d2d3SSam Leffler * each key, one for decrypt/encrypt and the other for the MIC. 22635901d2d3SSam Leffler */ 22645901d2d3SSam Leffler static u_int16_t 22655901d2d3SSam Leffler key_alloc_pair(struct ath_softc *sc, 22665901d2d3SSam Leffler ieee80211_keyix *txkeyix, ieee80211_keyix *rxkeyix) 22675901d2d3SSam Leffler { 22685901d2d3SSam Leffler #define N(a) (sizeof(a)/sizeof(a[0])) 22695901d2d3SSam Leffler u_int i, keyix; 22705901d2d3SSam Leffler 22715901d2d3SSam Leffler KASSERT(!sc->sc_splitmic, ("key cache split")); 22725901d2d3SSam Leffler /* XXX could optimize */ 22735901d2d3SSam Leffler for (i = 0; i < N(sc->sc_keymap)/4; i++) { 22745901d2d3SSam Leffler u_int8_t b = sc->sc_keymap[i]; 22755901d2d3SSam Leffler if (b != 0xff) { 22765901d2d3SSam Leffler /* 22775901d2d3SSam Leffler * One or more slots in this byte are free. 22785901d2d3SSam Leffler */ 22795901d2d3SSam Leffler keyix = i*NBBY; 22805901d2d3SSam Leffler while (b & 1) { 22815901d2d3SSam Leffler again: 22825901d2d3SSam Leffler keyix++; 22835901d2d3SSam Leffler b >>= 1; 22845901d2d3SSam Leffler } 22855901d2d3SSam Leffler if (isset(sc->sc_keymap, keyix+64)) { 22865901d2d3SSam Leffler /* full pair unavailable */ 22875901d2d3SSam Leffler /* XXX statistic */ 22885901d2d3SSam Leffler if (keyix == (i+1)*NBBY) { 22895901d2d3SSam Leffler /* no slots were appropriate, advance */ 22905901d2d3SSam Leffler continue; 22915901d2d3SSam Leffler } 22925901d2d3SSam Leffler goto again; 22935901d2d3SSam Leffler } 22945901d2d3SSam Leffler setbit(sc->sc_keymap, keyix); 22955901d2d3SSam Leffler setbit(sc->sc_keymap, keyix+64); 22965901d2d3SSam Leffler DPRINTF(sc, ATH_DEBUG_KEYCACHE, 22975901d2d3SSam Leffler "%s: key pair %u,%u\n", 22985901d2d3SSam Leffler __func__, keyix, keyix+64); 22995901d2d3SSam Leffler *txkeyix = *rxkeyix = keyix; 23005901d2d3SSam Leffler return 1; 23015901d2d3SSam Leffler } 23025901d2d3SSam Leffler } 23035901d2d3SSam Leffler DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: out of pair space\n", __func__); 23045901d2d3SSam Leffler return 0; 23055901d2d3SSam Leffler #undef N 23065901d2d3SSam Leffler } 23075901d2d3SSam Leffler 23085901d2d3SSam Leffler /* 2309c42a7b7eSSam Leffler * Allocate a single key cache slot. 2310c42a7b7eSSam Leffler */ 2311c1225b52SSam Leffler static int 2312c1225b52SSam Leffler key_alloc_single(struct ath_softc *sc, 2313c1225b52SSam Leffler ieee80211_keyix *txkeyix, ieee80211_keyix *rxkeyix) 2314c42a7b7eSSam Leffler { 2315c42a7b7eSSam Leffler #define N(a) (sizeof(a)/sizeof(a[0])) 2316c42a7b7eSSam Leffler u_int i, keyix; 2317c42a7b7eSSam Leffler 2318c42a7b7eSSam Leffler /* XXX try i,i+32,i+64,i+32+64 to minimize key pair conflicts */ 2319c42a7b7eSSam Leffler for (i = 0; i < N(sc->sc_keymap); i++) { 2320c42a7b7eSSam Leffler u_int8_t b = sc->sc_keymap[i]; 2321c42a7b7eSSam Leffler if (b != 0xff) { 2322c42a7b7eSSam Leffler /* 2323c42a7b7eSSam Leffler * One or more slots are free. 2324c42a7b7eSSam Leffler */ 2325c42a7b7eSSam Leffler keyix = i*NBBY; 2326c42a7b7eSSam Leffler while (b & 1) 2327c42a7b7eSSam Leffler keyix++, b >>= 1; 2328c42a7b7eSSam Leffler setbit(sc->sc_keymap, keyix); 2329c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: key %u\n", 2330c42a7b7eSSam Leffler __func__, keyix); 2331c1225b52SSam Leffler *txkeyix = *rxkeyix = keyix; 2332c1225b52SSam Leffler return 1; 2333c42a7b7eSSam Leffler } 2334c42a7b7eSSam Leffler } 2335c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: out of space\n", __func__); 2336c1225b52SSam Leffler return 0; 2337c42a7b7eSSam Leffler #undef N 2338c42a7b7eSSam Leffler } 2339c42a7b7eSSam Leffler 2340c42a7b7eSSam Leffler /* 2341c42a7b7eSSam Leffler * Allocate one or more key cache slots for a uniacst key. The 2342c42a7b7eSSam Leffler * key itself is needed only to identify the cipher. For hardware 2343c42a7b7eSSam Leffler * TKIP with split cipher+MIC keys we allocate two key cache slot 2344c42a7b7eSSam Leffler * pairs so that we can setup separate TX and RX MIC keys. Note 2345c42a7b7eSSam Leffler * that the MIC key for a TKIP key at slot i is assumed by the 2346c42a7b7eSSam Leffler * hardware to be at slot i+64. This limits TKIP keys to the first 2347c42a7b7eSSam Leffler * 64 entries. 2348c42a7b7eSSam Leffler */ 2349c42a7b7eSSam Leffler static int 2350b032f27cSSam Leffler ath_key_alloc(struct ieee80211vap *vap, const struct ieee80211_key *k, 2351c1225b52SSam Leffler ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix) 2352c42a7b7eSSam Leffler { 2353b032f27cSSam Leffler struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; 2354c42a7b7eSSam Leffler 2355c42a7b7eSSam Leffler /* 23568ca623d7SSam Leffler * Group key allocation must be handled specially for 23578ca623d7SSam Leffler * parts that do not support multicast key cache search 23588ca623d7SSam Leffler * functionality. For those parts the key id must match 23598ca623d7SSam Leffler * the h/w key index so lookups find the right key. On 23608ca623d7SSam Leffler * parts w/ the key search facility we install the sender's 23618ca623d7SSam Leffler * mac address (with the high bit set) and let the hardware 23628ca623d7SSam Leffler * find the key w/o using the key id. This is preferred as 23638ca623d7SSam Leffler * it permits us to support multiple users for adhoc and/or 23648ca623d7SSam Leffler * multi-station operation. 23658ca623d7SSam Leffler */ 23668ca623d7SSam Leffler if ((k->wk_flags & IEEE80211_KEY_GROUP) && !sc->sc_mcastkey) { 2367b032f27cSSam Leffler if (!(&vap->iv_nw_keys[0] <= k && 2368b032f27cSSam Leffler k < &vap->iv_nw_keys[IEEE80211_WEP_NKID])) { 23698ca623d7SSam Leffler /* should not happen */ 23708ca623d7SSam Leffler DPRINTF(sc, ATH_DEBUG_KEYCACHE, 23718ca623d7SSam Leffler "%s: bogus group key\n", __func__); 2372c1225b52SSam Leffler return 0; 23738ca623d7SSam Leffler } 23748ca623d7SSam Leffler /* 23758ca623d7SSam Leffler * XXX we pre-allocate the global keys so 23768ca623d7SSam Leffler * have no way to check if they've already been allocated. 23778ca623d7SSam Leffler */ 2378b032f27cSSam Leffler *keyix = *rxkeyix = k - vap->iv_nw_keys; 2379c1225b52SSam Leffler return 1; 23808ca623d7SSam Leffler } 23818ca623d7SSam Leffler 23828ca623d7SSam Leffler /* 2383c42a7b7eSSam Leffler * We allocate two pair for TKIP when using the h/w to do 2384c42a7b7eSSam Leffler * the MIC. For everything else, including software crypto, 2385c42a7b7eSSam Leffler * we allocate a single entry. Note that s/w crypto requires 2386c42a7b7eSSam Leffler * a pass-through slot on the 5211 and 5212. The 5210 does 2387c42a7b7eSSam Leffler * not support pass-through cache entries and we map all 2388c42a7b7eSSam Leffler * those requests to slot 0. 2389c42a7b7eSSam Leffler */ 2390c42a7b7eSSam Leffler if (k->wk_flags & IEEE80211_KEY_SWCRYPT) { 2391c1225b52SSam Leffler return key_alloc_single(sc, keyix, rxkeyix); 2392c42a7b7eSSam Leffler } else if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP && 23935901d2d3SSam Leffler (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) { 23945901d2d3SSam Leffler if (sc->sc_splitmic) 2395c1225b52SSam Leffler return key_alloc_2pair(sc, keyix, rxkeyix); 23965901d2d3SSam Leffler else 23975901d2d3SSam Leffler return key_alloc_pair(sc, keyix, rxkeyix); 2398c42a7b7eSSam Leffler } else { 2399c1225b52SSam Leffler return key_alloc_single(sc, keyix, rxkeyix); 2400c42a7b7eSSam Leffler } 2401c42a7b7eSSam Leffler } 2402c42a7b7eSSam Leffler 2403c42a7b7eSSam Leffler /* 2404c42a7b7eSSam Leffler * Delete an entry in the key cache allocated by ath_key_alloc. 2405c42a7b7eSSam Leffler */ 2406c42a7b7eSSam Leffler static int 2407b032f27cSSam Leffler ath_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k) 2408c42a7b7eSSam Leffler { 2409b032f27cSSam Leffler struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; 2410c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 2411c42a7b7eSSam Leffler const struct ieee80211_cipher *cip = k->wk_cipher; 2412c42a7b7eSSam Leffler u_int keyix = k->wk_keyix; 2413c42a7b7eSSam Leffler 2414c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: delete key %u\n", __func__, keyix); 2415c42a7b7eSSam Leffler 2416c42a7b7eSSam Leffler ath_hal_keyreset(ah, keyix); 2417c42a7b7eSSam Leffler /* 2418c42a7b7eSSam Leffler * Handle split tx/rx keying required for TKIP with h/w MIC. 2419c42a7b7eSSam Leffler */ 2420c42a7b7eSSam Leffler if (cip->ic_cipher == IEEE80211_CIPHER_TKIP && 2421c1225b52SSam Leffler (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && sc->sc_splitmic) 2422c42a7b7eSSam Leffler ath_hal_keyreset(ah, keyix+32); /* RX key */ 2423c42a7b7eSSam Leffler if (keyix >= IEEE80211_WEP_NKID) { 2424c42a7b7eSSam Leffler /* 2425c42a7b7eSSam Leffler * Don't touch keymap entries for global keys so 2426c42a7b7eSSam Leffler * they are never considered for dynamic allocation. 2427c42a7b7eSSam Leffler */ 2428c42a7b7eSSam Leffler clrbit(sc->sc_keymap, keyix); 2429c42a7b7eSSam Leffler if (cip->ic_cipher == IEEE80211_CIPHER_TKIP && 24305901d2d3SSam Leffler (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) { 2431c42a7b7eSSam Leffler clrbit(sc->sc_keymap, keyix+64); /* TX key MIC */ 24325901d2d3SSam Leffler if (sc->sc_splitmic) { 24335901d2d3SSam Leffler /* +32 for RX key, +32+64 for RX key MIC */ 24345901d2d3SSam Leffler clrbit(sc->sc_keymap, keyix+32); 24355901d2d3SSam Leffler clrbit(sc->sc_keymap, keyix+32+64); 24365901d2d3SSam Leffler } 2437c42a7b7eSSam Leffler } 2438c42a7b7eSSam Leffler } 2439c42a7b7eSSam Leffler return 1; 2440c42a7b7eSSam Leffler } 2441c42a7b7eSSam Leffler 2442c42a7b7eSSam Leffler /* 2443c42a7b7eSSam Leffler * Set the key cache contents for the specified key. Key cache 2444c42a7b7eSSam Leffler * slot(s) must already have been allocated by ath_key_alloc. 2445c42a7b7eSSam Leffler */ 2446c42a7b7eSSam Leffler static int 2447b032f27cSSam Leffler ath_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k, 2448c42a7b7eSSam Leffler const u_int8_t mac[IEEE80211_ADDR_LEN]) 2449c42a7b7eSSam Leffler { 2450b032f27cSSam Leffler struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; 2451c42a7b7eSSam Leffler 2452b032f27cSSam Leffler return ath_keyset(sc, k, mac, vap->iv_bss); 2453c42a7b7eSSam Leffler } 2454c42a7b7eSSam Leffler 2455c42a7b7eSSam Leffler /* 2456c42a7b7eSSam Leffler * Block/unblock tx+rx processing while a key change is done. 2457c42a7b7eSSam Leffler * We assume the caller serializes key management operations 2458c42a7b7eSSam Leffler * so we only need to worry about synchronization with other 2459c42a7b7eSSam Leffler * uses that originate in the driver. 2460c42a7b7eSSam Leffler */ 2461c42a7b7eSSam Leffler static void 2462b032f27cSSam Leffler ath_key_update_begin(struct ieee80211vap *vap) 2463c42a7b7eSSam Leffler { 2464b032f27cSSam Leffler struct ifnet *ifp = vap->iv_ic->ic_ifp; 2465c42a7b7eSSam Leffler struct ath_softc *sc = ifp->if_softc; 2466c42a7b7eSSam Leffler 2467c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__); 2468b032f27cSSam Leffler taskqueue_block(sc->sc_tq); 2469c42a7b7eSSam Leffler IF_LOCK(&ifp->if_snd); /* NB: doesn't block mgmt frames */ 2470c42a7b7eSSam Leffler } 2471c42a7b7eSSam Leffler 2472c42a7b7eSSam Leffler static void 2473b032f27cSSam Leffler ath_key_update_end(struct ieee80211vap *vap) 2474c42a7b7eSSam Leffler { 2475b032f27cSSam Leffler struct ifnet *ifp = vap->iv_ic->ic_ifp; 2476c42a7b7eSSam Leffler struct ath_softc *sc = ifp->if_softc; 2477c42a7b7eSSam Leffler 2478c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__); 2479c42a7b7eSSam Leffler IF_UNLOCK(&ifp->if_snd); 2480b032f27cSSam Leffler taskqueue_unblock(sc->sc_tq); 2481c42a7b7eSSam Leffler } 24825591b213SSam Leffler 24834bc0e754SSam Leffler /* 24844bc0e754SSam Leffler * Calculate the receive filter according to the 24854bc0e754SSam Leffler * operating mode and state: 24864bc0e754SSam Leffler * 24874bc0e754SSam Leffler * o always accept unicast, broadcast, and multicast traffic 2488b032f27cSSam Leffler * o accept PHY error frames when hardware doesn't have MIB support 2489b032f27cSSam Leffler * to count and we need them for ANI (sta mode only at the moment) 2490b032f27cSSam Leffler * and we are not scanning (ANI is disabled) 2491b032f27cSSam Leffler * NB: only with recent hal's; older hal's add rx filter bits out 2492b032f27cSSam Leffler * of sight and we need to blindly preserve them 24934bc0e754SSam Leffler * o probe request frames are accepted only when operating in 24944bc0e754SSam Leffler * hostap, adhoc, or monitor modes 2495b032f27cSSam Leffler * o enable promiscuous mode 2496b032f27cSSam Leffler * - when in monitor mode 2497b032f27cSSam Leffler * - if interface marked PROMISC (assumes bridge setting is filtered) 24984bc0e754SSam Leffler * o accept beacons: 24994bc0e754SSam Leffler * - when operating in station mode for collecting rssi data when 25004bc0e754SSam Leffler * the station is otherwise quiet, or 2501b032f27cSSam Leffler * - when operating in adhoc mode so the 802.11 layer creates 2502b032f27cSSam Leffler * node table entries for peers, 25034bc0e754SSam Leffler * - when scanning 2504b032f27cSSam Leffler * - when doing s/w beacon miss (e.g. for ap+sta) 2505b032f27cSSam Leffler * - when operating in ap mode in 11g to detect overlapping bss that 2506b032f27cSSam Leffler * require protection 25076f48c956SSam Leffler * o accept control frames: 25086f48c956SSam Leffler * - when in monitor mode 2509b032f27cSSam Leffler * XXX BAR frames for 11n 2510b032f27cSSam Leffler * XXX HT protection for 11n 25114bc0e754SSam Leffler */ 25124bc0e754SSam Leffler static u_int32_t 251368e8e04eSSam Leffler ath_calcrxfilter(struct ath_softc *sc) 25144bc0e754SSam Leffler { 2515fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 2516b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 25174bc0e754SSam Leffler u_int32_t rfilt; 25184bc0e754SSam Leffler 2519b032f27cSSam Leffler #if HAL_ABI_VERSION < 0x08011600 2520b032f27cSSam Leffler rfilt = (ath_hal_getrxfilter(sc->sc_ah) & 2521b032f27cSSam Leffler (HAL_RX_FILTER_PHYRADAR | HAL_RX_FILTER_PHYERR)) 25224bc0e754SSam Leffler | HAL_RX_FILTER_UCAST | HAL_RX_FILTER_BCAST | HAL_RX_FILTER_MCAST; 2523b032f27cSSam Leffler #else 2524b032f27cSSam Leffler rfilt = HAL_RX_FILTER_UCAST | HAL_RX_FILTER_BCAST | HAL_RX_FILTER_MCAST; 2525b032f27cSSam Leffler if (ic->ic_opmode == IEEE80211_M_STA && 2526b032f27cSSam Leffler !sc->sc_needmib && !sc->sc_scanning) 2527b032f27cSSam Leffler rfilt |= HAL_RX_FILTER_PHYERR; 2528b032f27cSSam Leffler #endif 25294bc0e754SSam Leffler if (ic->ic_opmode != IEEE80211_M_STA) 25304bc0e754SSam Leffler rfilt |= HAL_RX_FILTER_PROBEREQ; 2531b032f27cSSam Leffler if (ic->ic_opmode == IEEE80211_M_MONITOR || (ifp->if_flags & IFF_PROMISC)) 25324bc0e754SSam Leffler rfilt |= HAL_RX_FILTER_PROM; 25334bc0e754SSam Leffler if (ic->ic_opmode == IEEE80211_M_STA || 2534b032f27cSSam Leffler sc->sc_opmode == HAL_M_IBSS || 2535b032f27cSSam Leffler sc->sc_swbmiss || sc->sc_scanning) 2536b032f27cSSam Leffler rfilt |= HAL_RX_FILTER_BEACON; 2537b032f27cSSam Leffler /* 2538b032f27cSSam Leffler * NB: We don't recalculate the rx filter when 2539b032f27cSSam Leffler * ic_protmode changes; otherwise we could do 2540b032f27cSSam Leffler * this only when ic_protmode != NONE. 2541b032f27cSSam Leffler */ 2542b032f27cSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP && 2543b032f27cSSam Leffler IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) 25444bc0e754SSam Leffler rfilt |= HAL_RX_FILTER_BEACON; 25456f48c956SSam Leffler if (ic->ic_opmode == IEEE80211_M_MONITOR) 25466f48c956SSam Leffler rfilt |= HAL_RX_FILTER_CONTROL; 2547b032f27cSSam Leffler DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x, %s if_flags 0x%x\n", 2548b032f27cSSam Leffler __func__, rfilt, ieee80211_opmode_name[ic->ic_opmode], ifp->if_flags); 25494bc0e754SSam Leffler return rfilt; 2550b032f27cSSam Leffler } 2551b032f27cSSam Leffler 2552b032f27cSSam Leffler static void 2553b032f27cSSam Leffler ath_update_promisc(struct ifnet *ifp) 2554b032f27cSSam Leffler { 2555b032f27cSSam Leffler struct ath_softc *sc = ifp->if_softc; 2556b032f27cSSam Leffler u_int32_t rfilt; 2557b032f27cSSam Leffler 2558b032f27cSSam Leffler /* configure rx filter */ 2559b032f27cSSam Leffler rfilt = ath_calcrxfilter(sc); 2560b032f27cSSam Leffler ath_hal_setrxfilter(sc->sc_ah, rfilt); 2561b032f27cSSam Leffler 2562b032f27cSSam Leffler DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x\n", __func__, rfilt); 2563b032f27cSSam Leffler } 2564b032f27cSSam Leffler 2565b032f27cSSam Leffler static void 2566b032f27cSSam Leffler ath_update_mcast(struct ifnet *ifp) 2567b032f27cSSam Leffler { 2568b032f27cSSam Leffler struct ath_softc *sc = ifp->if_softc; 2569b032f27cSSam Leffler u_int32_t mfilt[2]; 2570b032f27cSSam Leffler 2571b032f27cSSam Leffler /* calculate and install multicast filter */ 2572b032f27cSSam Leffler if ((ifp->if_flags & IFF_ALLMULTI) == 0) { 2573b032f27cSSam Leffler struct ifmultiaddr *ifma; 2574b032f27cSSam Leffler /* 2575b032f27cSSam Leffler * Merge multicast addresses to form the hardware filter. 2576b032f27cSSam Leffler */ 2577b032f27cSSam Leffler mfilt[0] = mfilt[1] = 0; 2578b032f27cSSam Leffler IF_ADDR_LOCK(ifp); /* XXX need some fiddling to remove? */ 2579b032f27cSSam Leffler TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 2580b032f27cSSam Leffler caddr_t dl; 2581b032f27cSSam Leffler u_int32_t val; 2582b032f27cSSam Leffler u_int8_t pos; 2583b032f27cSSam Leffler 2584b032f27cSSam Leffler /* calculate XOR of eight 6bit values */ 2585b032f27cSSam Leffler dl = LLADDR((struct sockaddr_dl *) ifma->ifma_addr); 2586b032f27cSSam Leffler val = LE_READ_4(dl + 0); 2587b032f27cSSam Leffler pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; 2588b032f27cSSam Leffler val = LE_READ_4(dl + 3); 2589b032f27cSSam Leffler pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; 2590b032f27cSSam Leffler pos &= 0x3f; 2591b032f27cSSam Leffler mfilt[pos / 32] |= (1 << (pos % 32)); 2592b032f27cSSam Leffler } 2593b032f27cSSam Leffler IF_ADDR_UNLOCK(ifp); 2594b032f27cSSam Leffler } else 2595b032f27cSSam Leffler mfilt[0] = mfilt[1] = ~0; 2596b032f27cSSam Leffler ath_hal_setmcastfilter(sc->sc_ah, mfilt[0], mfilt[1]); 2597b032f27cSSam Leffler DPRINTF(sc, ATH_DEBUG_MODE, "%s: MC filter %08x:%08x\n", 2598b032f27cSSam Leffler __func__, mfilt[0], mfilt[1]); 25994bc0e754SSam Leffler } 26004bc0e754SSam Leffler 26015591b213SSam Leffler static void 26025591b213SSam Leffler ath_mode_init(struct ath_softc *sc) 26035591b213SSam Leffler { 2604fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 2605b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 2606b032f27cSSam Leffler struct ath_hal *ah = sc->sc_ah; 2607b032f27cSSam Leffler u_int32_t rfilt; 26085591b213SSam Leffler 26094bc0e754SSam Leffler /* configure rx filter */ 261068e8e04eSSam Leffler rfilt = ath_calcrxfilter(sc); 26114bc0e754SSam Leffler ath_hal_setrxfilter(ah, rfilt); 26124bc0e754SSam Leffler 26135591b213SSam Leffler /* configure operational mode */ 2614c42a7b7eSSam Leffler ath_hal_setopmode(ah); 2615c42a7b7eSSam Leffler 2616c42a7b7eSSam Leffler /* 2617c42a7b7eSSam Leffler * Handle any link-level address change. Note that we only 2618c42a7b7eSSam Leffler * need to force ic_myaddr; any other addresses are handled 2619c42a7b7eSSam Leffler * as a byproduct of the ifnet code marking the interface 2620c42a7b7eSSam Leffler * down then up. 2621c42a7b7eSSam Leffler * 2622c42a7b7eSSam Leffler * XXX should get from lladdr instead of arpcom but that's more work 2623c42a7b7eSSam Leffler */ 26244a0d6638SRuslan Ermilov IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp)); 2625c42a7b7eSSam Leffler ath_hal_setmac(ah, ic->ic_myaddr); 26265591b213SSam Leffler 26275591b213SSam Leffler /* calculate and install multicast filter */ 2628b032f27cSSam Leffler ath_update_mcast(ifp); 26295591b213SSam Leffler } 26305591b213SSam Leffler 2631c42a7b7eSSam Leffler /* 2632c42a7b7eSSam Leffler * Set the slot time based on the current setting. 2633c42a7b7eSSam Leffler */ 2634c42a7b7eSSam Leffler static void 2635c42a7b7eSSam Leffler ath_setslottime(struct ath_softc *sc) 2636c42a7b7eSSam Leffler { 2637b032f27cSSam Leffler struct ieee80211com *ic = sc->sc_ifp->if_l2com; 2638c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 2639aaa70f2fSSam Leffler u_int usec; 2640c42a7b7eSSam Leffler 2641aaa70f2fSSam Leffler if (IEEE80211_IS_CHAN_HALF(ic->ic_curchan)) 2642aaa70f2fSSam Leffler usec = 13; 2643aaa70f2fSSam Leffler else if (IEEE80211_IS_CHAN_QUARTER(ic->ic_curchan)) 2644aaa70f2fSSam Leffler usec = 21; 2645724c193aSSam Leffler else if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) { 2646724c193aSSam Leffler /* honor short/long slot time only in 11g */ 2647724c193aSSam Leffler /* XXX shouldn't honor on pure g or turbo g channel */ 2648724c193aSSam Leffler if (ic->ic_flags & IEEE80211_F_SHSLOT) 2649aaa70f2fSSam Leffler usec = HAL_SLOT_TIME_9; 2650aaa70f2fSSam Leffler else 2651aaa70f2fSSam Leffler usec = HAL_SLOT_TIME_20; 2652724c193aSSam Leffler } else 2653724c193aSSam Leffler usec = HAL_SLOT_TIME_9; 2654aaa70f2fSSam Leffler 2655aaa70f2fSSam Leffler DPRINTF(sc, ATH_DEBUG_RESET, 2656aaa70f2fSSam Leffler "%s: chan %u MHz flags 0x%x %s slot, %u usec\n", 2657aaa70f2fSSam Leffler __func__, ic->ic_curchan->ic_freq, ic->ic_curchan->ic_flags, 2658aaa70f2fSSam Leffler ic->ic_flags & IEEE80211_F_SHSLOT ? "short" : "long", usec); 2659aaa70f2fSSam Leffler 2660aaa70f2fSSam Leffler ath_hal_setslottime(ah, usec); 2661c42a7b7eSSam Leffler sc->sc_updateslot = OK; 2662c42a7b7eSSam Leffler } 2663c42a7b7eSSam Leffler 2664c42a7b7eSSam Leffler /* 2665c42a7b7eSSam Leffler * Callback from the 802.11 layer to update the 2666c42a7b7eSSam Leffler * slot time based on the current setting. 2667c42a7b7eSSam Leffler */ 2668c42a7b7eSSam Leffler static void 2669c42a7b7eSSam Leffler ath_updateslot(struct ifnet *ifp) 2670c42a7b7eSSam Leffler { 2671c42a7b7eSSam Leffler struct ath_softc *sc = ifp->if_softc; 2672b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 2673c42a7b7eSSam Leffler 2674c42a7b7eSSam Leffler /* 2675c42a7b7eSSam Leffler * When not coordinating the BSS, change the hardware 2676c42a7b7eSSam Leffler * immediately. For other operation we defer the change 2677c42a7b7eSSam Leffler * until beacon updates have propagated to the stations. 2678c42a7b7eSSam Leffler */ 2679c42a7b7eSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) 2680c42a7b7eSSam Leffler sc->sc_updateslot = UPDATE; 2681c42a7b7eSSam Leffler else 2682c42a7b7eSSam Leffler ath_setslottime(sc); 2683c42a7b7eSSam Leffler } 2684c42a7b7eSSam Leffler 2685c42a7b7eSSam Leffler /* 268680d2765fSSam Leffler * Setup a h/w transmit queue for beacons. 268780d2765fSSam Leffler */ 268880d2765fSSam Leffler static int 268980d2765fSSam Leffler ath_beaconq_setup(struct ath_hal *ah) 269080d2765fSSam Leffler { 269180d2765fSSam Leffler HAL_TXQ_INFO qi; 269280d2765fSSam Leffler 269380d2765fSSam Leffler memset(&qi, 0, sizeof(qi)); 269480d2765fSSam Leffler qi.tqi_aifs = HAL_TXQ_USEDEFAULT; 269580d2765fSSam Leffler qi.tqi_cwmin = HAL_TXQ_USEDEFAULT; 269680d2765fSSam Leffler qi.tqi_cwmax = HAL_TXQ_USEDEFAULT; 26970f2e86fbSSam Leffler /* NB: for dynamic turbo, don't enable any other interrupts */ 2698bd5a9920SSam Leffler qi.tqi_qflags = HAL_TXQ_TXDESCINT_ENABLE; 269980d2765fSSam Leffler return ath_hal_setuptxqueue(ah, HAL_TX_QUEUE_BEACON, &qi); 270080d2765fSSam Leffler } 270180d2765fSSam Leffler 270280d2765fSSam Leffler /* 27030f2e86fbSSam Leffler * Setup the transmit queue parameters for the beacon queue. 27040f2e86fbSSam Leffler */ 27050f2e86fbSSam Leffler static int 27060f2e86fbSSam Leffler ath_beaconq_config(struct ath_softc *sc) 27070f2e86fbSSam Leffler { 27080f2e86fbSSam Leffler #define ATH_EXPONENT_TO_VALUE(v) ((1<<(v))-1) 2709b032f27cSSam Leffler struct ieee80211com *ic = sc->sc_ifp->if_l2com; 27100f2e86fbSSam Leffler struct ath_hal *ah = sc->sc_ah; 27110f2e86fbSSam Leffler HAL_TXQ_INFO qi; 27120f2e86fbSSam Leffler 27130f2e86fbSSam Leffler ath_hal_gettxqueueprops(ah, sc->sc_bhalq, &qi); 27140f2e86fbSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 27150f2e86fbSSam Leffler /* 27160f2e86fbSSam Leffler * Always burst out beacon and CAB traffic. 27170f2e86fbSSam Leffler */ 27180f2e86fbSSam Leffler qi.tqi_aifs = ATH_BEACON_AIFS_DEFAULT; 27190f2e86fbSSam Leffler qi.tqi_cwmin = ATH_BEACON_CWMIN_DEFAULT; 27200f2e86fbSSam Leffler qi.tqi_cwmax = ATH_BEACON_CWMAX_DEFAULT; 27210f2e86fbSSam Leffler } else { 27220f2e86fbSSam Leffler struct wmeParams *wmep = 27230f2e86fbSSam Leffler &ic->ic_wme.wme_chanParams.cap_wmeParams[WME_AC_BE]; 27240f2e86fbSSam Leffler /* 27250f2e86fbSSam Leffler * Adhoc mode; important thing is to use 2x cwmin. 27260f2e86fbSSam Leffler */ 27270f2e86fbSSam Leffler qi.tqi_aifs = wmep->wmep_aifsn; 27280f2e86fbSSam Leffler qi.tqi_cwmin = 2*ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmin); 27290f2e86fbSSam Leffler qi.tqi_cwmax = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmax); 27300f2e86fbSSam Leffler } 27310f2e86fbSSam Leffler 27320f2e86fbSSam Leffler if (!ath_hal_settxqueueprops(ah, sc->sc_bhalq, &qi)) { 27330f2e86fbSSam Leffler device_printf(sc->sc_dev, "unable to update parameters for " 27340f2e86fbSSam Leffler "beacon hardware queue!\n"); 27350f2e86fbSSam Leffler return 0; 27360f2e86fbSSam Leffler } else { 27370f2e86fbSSam Leffler ath_hal_resettxqueue(ah, sc->sc_bhalq); /* push to h/w */ 27380f2e86fbSSam Leffler return 1; 27390f2e86fbSSam Leffler } 27400f2e86fbSSam Leffler #undef ATH_EXPONENT_TO_VALUE 27410f2e86fbSSam Leffler } 27420f2e86fbSSam Leffler 27430f2e86fbSSam Leffler /* 2744c42a7b7eSSam Leffler * Allocate and setup an initial beacon frame. 2745c42a7b7eSSam Leffler */ 27465591b213SSam Leffler static int 27475591b213SSam Leffler ath_beacon_alloc(struct ath_softc *sc, struct ieee80211_node *ni) 27485591b213SSam Leffler { 2749b032f27cSSam Leffler struct ieee80211vap *vap = ni->ni_vap; 2750b032f27cSSam Leffler struct ath_vap *avp = ATH_VAP(vap); 27515591b213SSam Leffler struct ath_buf *bf; 27525591b213SSam Leffler struct mbuf *m; 2753c42a7b7eSSam Leffler int error; 27545591b213SSam Leffler 2755b032f27cSSam Leffler bf = avp->av_bcbuf; 2756b032f27cSSam Leffler if (bf->bf_m != NULL) { 2757b032f27cSSam Leffler bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 2758b032f27cSSam Leffler m_freem(bf->bf_m); 2759b032f27cSSam Leffler bf->bf_m = NULL; 2760c42a7b7eSSam Leffler } 2761b032f27cSSam Leffler if (bf->bf_node != NULL) { 2762b032f27cSSam Leffler ieee80211_free_node(bf->bf_node); 2763b032f27cSSam Leffler bf->bf_node = NULL; 2764b032f27cSSam Leffler } 2765b032f27cSSam Leffler 27665591b213SSam Leffler /* 27675591b213SSam Leffler * NB: the beacon data buffer must be 32-bit aligned; 27685591b213SSam Leffler * we assume the mbuf routines will return us something 27695591b213SSam Leffler * with this alignment (perhaps should assert). 27705591b213SSam Leffler */ 2771b032f27cSSam Leffler m = ieee80211_beacon_alloc(ni, &avp->av_boff); 27725591b213SSam Leffler if (m == NULL) { 2773b032f27cSSam Leffler device_printf(sc->sc_dev, "%s: cannot get mbuf\n", __func__); 27745591b213SSam Leffler sc->sc_stats.ast_be_nombuf++; 27755591b213SSam Leffler return ENOMEM; 27765591b213SSam Leffler } 2777f9e6219bSSam Leffler error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m, 2778f9e6219bSSam Leffler bf->bf_segs, &bf->bf_nseg, 27795591b213SSam Leffler BUS_DMA_NOWAIT); 2780b032f27cSSam Leffler if (error != 0) { 2781b032f27cSSam Leffler device_printf(sc->sc_dev, 2782b032f27cSSam Leffler "%s: cannot map mbuf, bus_dmamap_load_mbuf_sg returns %d\n", 2783b032f27cSSam Leffler __func__, error); 2784b032f27cSSam Leffler m_freem(m); 2785b032f27cSSam Leffler return error; 2786b032f27cSSam Leffler } 2787b032f27cSSam Leffler 2788b032f27cSSam Leffler /* 2789b032f27cSSam Leffler * Calculate a TSF adjustment factor required for staggered 2790b032f27cSSam Leffler * beacons. Note that we assume the format of the beacon 2791b032f27cSSam Leffler * frame leaves the tstamp field immediately following the 2792b032f27cSSam Leffler * header. 2793b032f27cSSam Leffler */ 2794b032f27cSSam Leffler if (sc->sc_stagbeacons && avp->av_bslot > 0) { 2795b032f27cSSam Leffler uint64_t tsfadjust; 2796b032f27cSSam Leffler struct ieee80211_frame *wh; 2797b032f27cSSam Leffler 2798b032f27cSSam Leffler /* 2799b032f27cSSam Leffler * The beacon interval is in TU's; the TSF is in usecs. 2800b032f27cSSam Leffler * We figure out how many TU's to add to align the timestamp 2801b032f27cSSam Leffler * then convert to TSF units and handle byte swapping before 2802b032f27cSSam Leffler * inserting it in the frame. The hardware will then add this 2803b032f27cSSam Leffler * each time a beacon frame is sent. Note that we align vap's 2804b032f27cSSam Leffler * 1..N and leave vap 0 untouched. This means vap 0 has a 2805b032f27cSSam Leffler * timestamp in one beacon interval while the others get a 2806b032f27cSSam Leffler * timstamp aligned to the next interval. 2807b032f27cSSam Leffler */ 2808b032f27cSSam Leffler tsfadjust = ni->ni_intval * 2809b032f27cSSam Leffler (ATH_BCBUF - avp->av_bslot) / ATH_BCBUF; 2810b032f27cSSam Leffler tsfadjust = htole64(tsfadjust << 10); /* TU -> TSF */ 2811b032f27cSSam Leffler 2812b032f27cSSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON, 2813b032f27cSSam Leffler "%s: %s beacons bslot %d intval %u tsfadjust %llu\n", 2814b032f27cSSam Leffler __func__, sc->sc_stagbeacons ? "stagger" : "burst", 2815b032f27cSSam Leffler avp->av_bslot, ni->ni_intval, le64toh(tsfadjust)); 2816b032f27cSSam Leffler 2817b032f27cSSam Leffler wh = mtod(m, struct ieee80211_frame *); 2818b032f27cSSam Leffler memcpy(&wh[1], &tsfadjust, sizeof(tsfadjust)); 2819b032f27cSSam Leffler } 2820c42a7b7eSSam Leffler bf->bf_m = m; 2821f818612bSSam Leffler bf->bf_node = ieee80211_ref_node(ni); 2822b032f27cSSam Leffler 2823b032f27cSSam Leffler return 0; 28245591b213SSam Leffler } 2825c42a7b7eSSam Leffler 2826c42a7b7eSSam Leffler /* 2827c42a7b7eSSam Leffler * Setup the beacon frame for transmit. 2828c42a7b7eSSam Leffler */ 2829c42a7b7eSSam Leffler static void 2830c42a7b7eSSam Leffler ath_beacon_setup(struct ath_softc *sc, struct ath_buf *bf) 2831c42a7b7eSSam Leffler { 2832c42a7b7eSSam Leffler #define USE_SHPREAMBLE(_ic) \ 2833c42a7b7eSSam Leffler (((_ic)->ic_flags & (IEEE80211_F_SHPREAMBLE | IEEE80211_F_USEBARKER))\ 2834c42a7b7eSSam Leffler == IEEE80211_F_SHPREAMBLE) 2835c42a7b7eSSam Leffler struct ieee80211_node *ni = bf->bf_node; 2836c42a7b7eSSam Leffler struct ieee80211com *ic = ni->ni_ic; 2837c42a7b7eSSam Leffler struct mbuf *m = bf->bf_m; 2838c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 2839c42a7b7eSSam Leffler struct ath_desc *ds; 2840c42a7b7eSSam Leffler int flags, antenna; 284155f63772SSam Leffler const HAL_RATE_TABLE *rt; 284255f63772SSam Leffler u_int8_t rix, rate; 2843c42a7b7eSSam Leffler 28444a3ac3fcSSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s: m %p len %u\n", 2845c42a7b7eSSam Leffler __func__, m, m->m_len); 28465591b213SSam Leffler 28475591b213SSam Leffler /* setup descriptors */ 28485591b213SSam Leffler ds = bf->bf_desc; 28495591b213SSam Leffler 2850c42a7b7eSSam Leffler flags = HAL_TXDESC_NOACK; 2851c42a7b7eSSam Leffler if (ic->ic_opmode == IEEE80211_M_IBSS && sc->sc_hasveol) { 2852c42a7b7eSSam Leffler ds->ds_link = bf->bf_daddr; /* self-linked */ 2853c42a7b7eSSam Leffler flags |= HAL_TXDESC_VEOL; 2854c42a7b7eSSam Leffler /* 2855c42a7b7eSSam Leffler * Let hardware handle antenna switching. 2856c42a7b7eSSam Leffler */ 28574866e6c2SSam Leffler antenna = sc->sc_txantenna; 2858c42a7b7eSSam Leffler } else { 28595591b213SSam Leffler ds->ds_link = 0; 2860c42a7b7eSSam Leffler /* 2861c42a7b7eSSam Leffler * Switch antenna every 4 beacons. 2862c42a7b7eSSam Leffler * XXX assumes two antenna 2863c42a7b7eSSam Leffler */ 2864b032f27cSSam Leffler if (sc->sc_txantenna != 0) 2865b032f27cSSam Leffler antenna = sc->sc_txantenna; 2866b032f27cSSam Leffler else if (sc->sc_stagbeacons && sc->sc_nbcnvaps != 0) 2867b032f27cSSam Leffler antenna = ((sc->sc_stats.ast_be_xmit / sc->sc_nbcnvaps) & 4 ? 2 : 1); 2868b032f27cSSam Leffler else 2869b032f27cSSam Leffler antenna = (sc->sc_stats.ast_be_xmit & 4 ? 2 : 1); 2870c42a7b7eSSam Leffler } 2871c42a7b7eSSam Leffler 2872c42a7b7eSSam Leffler KASSERT(bf->bf_nseg == 1, 2873c42a7b7eSSam Leffler ("multi-segment beacon frame; nseg %u", bf->bf_nseg)); 28745591b213SSam Leffler ds->ds_data = bf->bf_segs[0].ds_addr; 28755591b213SSam Leffler /* 28765591b213SSam Leffler * Calculate rate code. 28775591b213SSam Leffler * XXX everything at min xmit rate 28785591b213SSam Leffler */ 2879b032f27cSSam Leffler rix = 0; 288055f63772SSam Leffler rt = sc->sc_currates; 288155f63772SSam Leffler rate = rt->info[rix].rateCode; 2882c42a7b7eSSam Leffler if (USE_SHPREAMBLE(ic)) 288355f63772SSam Leffler rate |= rt->info[rix].shortPreamble; 28845591b213SSam Leffler ath_hal_setuptxdesc(ah, ds 2885c42a7b7eSSam Leffler , m->m_len + IEEE80211_CRC_LEN /* frame length */ 28865591b213SSam Leffler , sizeof(struct ieee80211_frame)/* header length */ 28875591b213SSam Leffler , HAL_PKT_TYPE_BEACON /* Atheros packet type */ 2888c42a7b7eSSam Leffler , ni->ni_txpower /* txpower XXX */ 28895591b213SSam Leffler , rate, 1 /* series 0 rate/tries */ 28905591b213SSam Leffler , HAL_TXKEYIX_INVALID /* no encryption */ 2891c42a7b7eSSam Leffler , antenna /* antenna mode */ 2892c42a7b7eSSam Leffler , flags /* no ack, veol for beacons */ 28935591b213SSam Leffler , 0 /* rts/cts rate */ 28945591b213SSam Leffler , 0 /* rts/cts duration */ 28955591b213SSam Leffler ); 28965591b213SSam Leffler /* NB: beacon's BufLen must be a multiple of 4 bytes */ 28975591b213SSam Leffler ath_hal_filltxdesc(ah, ds 2898c42a7b7eSSam Leffler , roundup(m->m_len, 4) /* buffer length */ 28995591b213SSam Leffler , AH_TRUE /* first segment */ 29005591b213SSam Leffler , AH_TRUE /* last segment */ 2901c42a7b7eSSam Leffler , ds /* first descriptor */ 29025591b213SSam Leffler ); 2903b032f27cSSam Leffler #if 0 2904b032f27cSSam Leffler ath_desc_swap(ds); 2905b032f27cSSam Leffler #endif 2906c42a7b7eSSam Leffler #undef USE_SHPREAMBLE 29075591b213SSam Leffler } 29085591b213SSam Leffler 2909b105a069SSam Leffler static void 2910b032f27cSSam Leffler ath_beacon_update(struct ieee80211vap *vap, int item) 2911b105a069SSam Leffler { 2912b032f27cSSam Leffler struct ieee80211_beacon_offsets *bo = &ATH_VAP(vap)->av_boff; 2913b105a069SSam Leffler 2914b105a069SSam Leffler setbit(bo->bo_flags, item); 2915b105a069SSam Leffler } 2916b105a069SSam Leffler 2917c42a7b7eSSam Leffler /* 2918622b3fd2SSam Leffler * Append the contents of src to dst; both queues 2919622b3fd2SSam Leffler * are assumed to be locked. 2920622b3fd2SSam Leffler */ 2921622b3fd2SSam Leffler static void 2922622b3fd2SSam Leffler ath_txqmove(struct ath_txq *dst, struct ath_txq *src) 2923622b3fd2SSam Leffler { 2924622b3fd2SSam Leffler STAILQ_CONCAT(&dst->axq_q, &src->axq_q); 2925622b3fd2SSam Leffler dst->axq_link = src->axq_link; 2926622b3fd2SSam Leffler src->axq_link = NULL; 2927622b3fd2SSam Leffler dst->axq_depth += src->axq_depth; 2928622b3fd2SSam Leffler src->axq_depth = 0; 2929622b3fd2SSam Leffler } 2930622b3fd2SSam Leffler 2931622b3fd2SSam Leffler /* 2932c42a7b7eSSam Leffler * Transmit a beacon frame at SWBA. Dynamic updates to the 2933c42a7b7eSSam Leffler * frame contents are done as needed and the slot time is 2934c42a7b7eSSam Leffler * also adjusted based on current state. 2935c42a7b7eSSam Leffler */ 29365591b213SSam Leffler static void 29375591b213SSam Leffler ath_beacon_proc(void *arg, int pending) 29385591b213SSam Leffler { 29395591b213SSam Leffler struct ath_softc *sc = arg; 29405591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 2941b032f27cSSam Leffler struct ieee80211vap *vap; 2942b032f27cSSam Leffler struct ath_buf *bf; 2943b032f27cSSam Leffler int slot, otherant; 2944b032f27cSSam Leffler uint32_t bfaddr; 29455591b213SSam Leffler 2946c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s: pending %u\n", 2947c42a7b7eSSam Leffler __func__, pending); 2948c42a7b7eSSam Leffler /* 2949c42a7b7eSSam Leffler * Check if the previous beacon has gone out. If 2950c66c48cbSSam Leffler * not don't try to post another, skip this period 2951c66c48cbSSam Leffler * and wait for the next. Missed beacons indicate 2952c66c48cbSSam Leffler * a problem and should not occur. If we miss too 2953c66c48cbSSam Leffler * many consecutive beacons reset the device. 2954c42a7b7eSSam Leffler */ 2955c42a7b7eSSam Leffler if (ath_hal_numtxpending(ah, sc->sc_bhalq) != 0) { 2956c42a7b7eSSam Leffler sc->sc_bmisscount++; 29574a3ac3fcSSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON, 2958c42a7b7eSSam Leffler "%s: missed %u consecutive beacons\n", 2959c42a7b7eSSam Leffler __func__, sc->sc_bmisscount); 2960c42a7b7eSSam Leffler if (sc->sc_bmisscount > 3) /* NB: 3 is a guess */ 29610bbf5441SSam Leffler taskqueue_enqueue(sc->sc_tq, &sc->sc_bstucktask); 2962c42a7b7eSSam Leffler return; 2963c42a7b7eSSam Leffler } 2964c42a7b7eSSam Leffler if (sc->sc_bmisscount != 0) { 2965c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON, 2966c42a7b7eSSam Leffler "%s: resume beacon xmit after %u misses\n", 2967c42a7b7eSSam Leffler __func__, sc->sc_bmisscount); 2968c42a7b7eSSam Leffler sc->sc_bmisscount = 0; 2969c42a7b7eSSam Leffler } 2970c42a7b7eSSam Leffler 2971b032f27cSSam Leffler if (sc->sc_stagbeacons) { /* staggered beacons */ 2972b032f27cSSam Leffler struct ieee80211com *ic = sc->sc_ifp->if_l2com; 2973b032f27cSSam Leffler uint32_t tsftu; 2974b032f27cSSam Leffler 2975b032f27cSSam Leffler tsftu = ath_hal_gettsf32(ah) >> 10; 2976b032f27cSSam Leffler /* XXX lintval */ 2977b032f27cSSam Leffler slot = ((tsftu % ic->ic_lintval) * ATH_BCBUF) / ic->ic_lintval; 2978b032f27cSSam Leffler vap = sc->sc_bslot[(slot+1) % ATH_BCBUF]; 2979b032f27cSSam Leffler bfaddr = 0; 2980b032f27cSSam Leffler if (vap != NULL && vap->iv_state == IEEE80211_S_RUN) { 2981b032f27cSSam Leffler bf = ath_beacon_generate(sc, vap); 2982b032f27cSSam Leffler if (bf != NULL) 2983b032f27cSSam Leffler bfaddr = bf->bf_daddr; 2984b032f27cSSam Leffler } 2985b032f27cSSam Leffler } else { /* burst'd beacons */ 2986b032f27cSSam Leffler uint32_t *bflink = &bfaddr; 2987b032f27cSSam Leffler 2988b032f27cSSam Leffler for (slot = 0; slot < ATH_BCBUF; slot++) { 2989b032f27cSSam Leffler vap = sc->sc_bslot[slot]; 2990b032f27cSSam Leffler if (vap != NULL && vap->iv_state == IEEE80211_S_RUN) { 2991b032f27cSSam Leffler bf = ath_beacon_generate(sc, vap); 2992b032f27cSSam Leffler if (bf != NULL) { 2993b032f27cSSam Leffler *bflink = bf->bf_daddr; 2994b032f27cSSam Leffler bflink = &bf->bf_desc->ds_link; 2995c42a7b7eSSam Leffler } 2996c42a7b7eSSam Leffler } 2997b032f27cSSam Leffler } 2998b032f27cSSam Leffler *bflink = 0; /* terminate list */ 2999622b3fd2SSam Leffler } 3000c42a7b7eSSam Leffler 3001c42a7b7eSSam Leffler /* 3002c42a7b7eSSam Leffler * Handle slot time change when a non-ERP station joins/leaves 3003c42a7b7eSSam Leffler * an 11g network. The 802.11 layer notifies us via callback, 3004c42a7b7eSSam Leffler * we mark updateslot, then wait one beacon before effecting 3005c42a7b7eSSam Leffler * the change. This gives associated stations at least one 3006c42a7b7eSSam Leffler * beacon interval to note the state change. 3007c42a7b7eSSam Leffler */ 3008c42a7b7eSSam Leffler /* XXX locking */ 3009b032f27cSSam Leffler if (sc->sc_updateslot == UPDATE) { 3010c42a7b7eSSam Leffler sc->sc_updateslot = COMMIT; /* commit next beacon */ 3011b032f27cSSam Leffler sc->sc_slotupdate = slot; 3012b032f27cSSam Leffler } else if (sc->sc_updateslot == COMMIT && sc->sc_slotupdate == slot) 3013c42a7b7eSSam Leffler ath_setslottime(sc); /* commit change to h/w */ 3014c42a7b7eSSam Leffler 3015c42a7b7eSSam Leffler /* 3016c42a7b7eSSam Leffler * Check recent per-antenna transmit statistics and flip 3017c42a7b7eSSam Leffler * the default antenna if noticeably more frames went out 3018c42a7b7eSSam Leffler * on the non-default antenna. 3019c42a7b7eSSam Leffler * XXX assumes 2 anntenae 3020c42a7b7eSSam Leffler */ 3021b032f27cSSam Leffler if (!sc->sc_diversity && (!sc->sc_stagbeacons || slot == 0)) { 3022c42a7b7eSSam Leffler otherant = sc->sc_defant & 1 ? 2 : 1; 3023c42a7b7eSSam Leffler if (sc->sc_ant_tx[otherant] > sc->sc_ant_tx[sc->sc_defant] + 2) 3024c42a7b7eSSam Leffler ath_setdefantenna(sc, otherant); 3025c42a7b7eSSam Leffler sc->sc_ant_tx[1] = sc->sc_ant_tx[2] = 0; 3026b032f27cSSam Leffler } 3027c42a7b7eSSam Leffler 3028b032f27cSSam Leffler if (bfaddr != 0) { 3029c42a7b7eSSam Leffler /* 3030c42a7b7eSSam Leffler * Stop any current dma and put the new frame on the queue. 3031c42a7b7eSSam Leffler * This should never fail since we check above that no frames 3032c42a7b7eSSam Leffler * are still pending on the queue. 3033c42a7b7eSSam Leffler */ 30345591b213SSam Leffler if (!ath_hal_stoptxdma(ah, sc->sc_bhalq)) { 3035c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, 3036c42a7b7eSSam Leffler "%s: beacon queue %u did not stop?\n", 3037c42a7b7eSSam Leffler __func__, sc->sc_bhalq); 30385591b213SSam Leffler } 3039b032f27cSSam Leffler /* NB: cabq traffic should already be queued and primed */ 3040b032f27cSSam Leffler ath_hal_puttxbuf(ah, sc->sc_bhalq, bfaddr); 3041b032f27cSSam Leffler ath_hal_txstart(ah, sc->sc_bhalq); 3042b032f27cSSam Leffler 3043b032f27cSSam Leffler sc->sc_stats.ast_be_xmit++; 3044b032f27cSSam Leffler } 3045b032f27cSSam Leffler } 3046b032f27cSSam Leffler 3047b032f27cSSam Leffler static struct ath_buf * 3048b032f27cSSam Leffler ath_beacon_generate(struct ath_softc *sc, struct ieee80211vap *vap) 3049b032f27cSSam Leffler { 3050b032f27cSSam Leffler struct ath_vap *avp = ATH_VAP(vap); 3051b032f27cSSam Leffler struct ath_txq *cabq = sc->sc_cabq; 3052b032f27cSSam Leffler struct ath_buf *bf; 3053b032f27cSSam Leffler struct mbuf *m; 3054b032f27cSSam Leffler int nmcastq, error; 3055b032f27cSSam Leffler 3056b032f27cSSam Leffler KASSERT(vap->iv_state == IEEE80211_S_RUN, 3057b032f27cSSam Leffler ("not running, state %d", vap->iv_state)); 3058b032f27cSSam Leffler KASSERT(avp->av_bcbuf != NULL, ("no beacon buffer")); 3059b032f27cSSam Leffler 3060b032f27cSSam Leffler /* 3061b032f27cSSam Leffler * Update dynamic beacon contents. If this returns 3062b032f27cSSam Leffler * non-zero then we need to remap the memory because 3063b032f27cSSam Leffler * the beacon frame changed size (probably because 3064b032f27cSSam Leffler * of the TIM bitmap). 3065b032f27cSSam Leffler */ 3066b032f27cSSam Leffler bf = avp->av_bcbuf; 3067b032f27cSSam Leffler m = bf->bf_m; 3068b032f27cSSam Leffler nmcastq = avp->av_mcastq.axq_depth; 3069b032f27cSSam Leffler if (ieee80211_beacon_update(bf->bf_node, &avp->av_boff, m, nmcastq)) { 3070b032f27cSSam Leffler /* XXX too conservative? */ 3071b032f27cSSam Leffler bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 3072b032f27cSSam Leffler error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m, 3073b032f27cSSam Leffler bf->bf_segs, &bf->bf_nseg, 3074b032f27cSSam Leffler BUS_DMA_NOWAIT); 3075b032f27cSSam Leffler if (error != 0) { 3076b032f27cSSam Leffler if_printf(vap->iv_ifp, 3077b032f27cSSam Leffler "%s: bus_dmamap_load_mbuf_sg failed, error %u\n", 3078b032f27cSSam Leffler __func__, error); 3079b032f27cSSam Leffler return NULL; 3080b032f27cSSam Leffler } 3081b032f27cSSam Leffler } 3082b032f27cSSam Leffler if ((avp->av_boff.bo_tim[4] & 1) && cabq->axq_depth) { 3083b032f27cSSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON, 3084b032f27cSSam Leffler "%s: cabq did not drain, mcastq %u cabq %u\n", 3085b032f27cSSam Leffler __func__, nmcastq, cabq->axq_depth); 3086b032f27cSSam Leffler sc->sc_stats.ast_cabq_busy++; 3087b032f27cSSam Leffler if (sc->sc_nvaps > 1 && sc->sc_stagbeacons) { 3088b032f27cSSam Leffler /* 3089b032f27cSSam Leffler * CABQ traffic from a previous vap is still pending. 3090b032f27cSSam Leffler * We must drain the q before this beacon frame goes 3091b032f27cSSam Leffler * out as otherwise this vap's stations will get cab 3092b032f27cSSam Leffler * frames from a different vap. 3093b032f27cSSam Leffler * XXX could be slow causing us to miss DBA 3094b032f27cSSam Leffler */ 3095b032f27cSSam Leffler ath_tx_draintxq(sc, cabq); 3096b032f27cSSam Leffler } 3097b032f27cSSam Leffler } 3098b032f27cSSam Leffler ath_beacon_setup(sc, bf); 30995591b213SSam Leffler bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE); 31005591b213SSam Leffler 3101c42a7b7eSSam Leffler /* 3102c42a7b7eSSam Leffler * Enable the CAB queue before the beacon queue to 3103c42a7b7eSSam Leffler * insure cab frames are triggered by this beacon. 3104c42a7b7eSSam Leffler */ 3105b032f27cSSam Leffler if (avp->av_boff.bo_tim[4] & 1) { 3106b032f27cSSam Leffler struct ath_hal *ah = sc->sc_ah; 3107b032f27cSSam Leffler 3108f3af83f7SSam Leffler /* NB: only at DTIM */ 3109622b3fd2SSam Leffler ATH_TXQ_LOCK(cabq); 3110b032f27cSSam Leffler ATH_TXQ_LOCK(&avp->av_mcastq); 3111622b3fd2SSam Leffler if (nmcastq) { 3112622b3fd2SSam Leffler struct ath_buf *bfm; 3113622b3fd2SSam Leffler 3114622b3fd2SSam Leffler /* 3115622b3fd2SSam Leffler * Move frames from the s/w mcast q to the h/w cab q. 3116b032f27cSSam Leffler * XXX MORE_DATA bit 3117622b3fd2SSam Leffler */ 3118b032f27cSSam Leffler bfm = STAILQ_FIRST(&avp->av_mcastq.axq_q); 3119622b3fd2SSam Leffler if (cabq->axq_link != NULL) { 3120622b3fd2SSam Leffler *cabq->axq_link = bfm->bf_daddr; 3121622b3fd2SSam Leffler } else 3122622b3fd2SSam Leffler ath_hal_puttxbuf(ah, cabq->axq_qnum, 3123622b3fd2SSam Leffler bfm->bf_daddr); 3124b032f27cSSam Leffler ath_txqmove(cabq, &avp->av_mcastq); 3125622b3fd2SSam Leffler 3126622b3fd2SSam Leffler sc->sc_stats.ast_cabq_xmit += nmcastq; 3127622b3fd2SSam Leffler } 3128622b3fd2SSam Leffler /* NB: gated by beacon so safe to start here */ 3129622b3fd2SSam Leffler ath_hal_txstart(ah, cabq->axq_qnum); 3130622b3fd2SSam Leffler ATH_TXQ_UNLOCK(cabq); 3131b032f27cSSam Leffler ATH_TXQ_UNLOCK(&avp->av_mcastq); 3132622b3fd2SSam Leffler } 3133b032f27cSSam Leffler return bf; 3134b032f27cSSam Leffler } 3135b032f27cSSam Leffler 3136b032f27cSSam Leffler static void 3137b032f27cSSam Leffler ath_beacon_start_adhoc(struct ath_softc *sc, struct ieee80211vap *vap) 3138b032f27cSSam Leffler { 3139b032f27cSSam Leffler struct ath_vap *avp = ATH_VAP(vap); 3140b032f27cSSam Leffler struct ath_hal *ah = sc->sc_ah; 3141b032f27cSSam Leffler struct ath_buf *bf; 3142b032f27cSSam Leffler struct mbuf *m; 3143b032f27cSSam Leffler int error; 3144b032f27cSSam Leffler 3145b032f27cSSam Leffler KASSERT(avp->av_bcbuf != NULL, ("no beacon buffer")); 3146b032f27cSSam Leffler 3147b032f27cSSam Leffler /* 3148b032f27cSSam Leffler * Update dynamic beacon contents. If this returns 3149b032f27cSSam Leffler * non-zero then we need to remap the memory because 3150b032f27cSSam Leffler * the beacon frame changed size (probably because 3151b032f27cSSam Leffler * of the TIM bitmap). 3152b032f27cSSam Leffler */ 3153b032f27cSSam Leffler bf = avp->av_bcbuf; 3154b032f27cSSam Leffler m = bf->bf_m; 3155b032f27cSSam Leffler if (ieee80211_beacon_update(bf->bf_node, &avp->av_boff, m, 0)) { 3156b032f27cSSam Leffler /* XXX too conservative? */ 3157b032f27cSSam Leffler bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 3158b032f27cSSam Leffler error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m, 3159b032f27cSSam Leffler bf->bf_segs, &bf->bf_nseg, 3160b032f27cSSam Leffler BUS_DMA_NOWAIT); 3161b032f27cSSam Leffler if (error != 0) { 3162b032f27cSSam Leffler if_printf(vap->iv_ifp, 3163b032f27cSSam Leffler "%s: bus_dmamap_load_mbuf_sg failed, error %u\n", 3164b032f27cSSam Leffler __func__, error); 3165b032f27cSSam Leffler return; 3166b032f27cSSam Leffler } 3167b032f27cSSam Leffler } 3168b032f27cSSam Leffler ath_beacon_setup(sc, bf); 3169b032f27cSSam Leffler bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE); 3170b032f27cSSam Leffler 3171b032f27cSSam Leffler /* NB: caller is known to have already stopped tx dma */ 31725591b213SSam Leffler ath_hal_puttxbuf(ah, sc->sc_bhalq, bf->bf_daddr); 31735591b213SSam Leffler ath_hal_txstart(ah, sc->sc_bhalq); 31745591b213SSam Leffler } 31755591b213SSam Leffler 3176c42a7b7eSSam Leffler /* 3177c42a7b7eSSam Leffler * Reset the hardware after detecting beacons have stopped. 3178c42a7b7eSSam Leffler */ 3179c42a7b7eSSam Leffler static void 3180c42a7b7eSSam Leffler ath_bstuck_proc(void *arg, int pending) 3181c42a7b7eSSam Leffler { 3182c42a7b7eSSam Leffler struct ath_softc *sc = arg; 3183fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 3184c42a7b7eSSam Leffler 3185c42a7b7eSSam Leffler if_printf(ifp, "stuck beacon; resetting (bmiss count %u)\n", 3186c42a7b7eSSam Leffler sc->sc_bmisscount); 3187c42a7b7eSSam Leffler ath_reset(ifp); 3188c42a7b7eSSam Leffler } 3189c42a7b7eSSam Leffler 3190c42a7b7eSSam Leffler /* 3191b032f27cSSam Leffler * Reclaim beacon resources and return buffer to the pool. 3192b032f27cSSam Leffler */ 3193b032f27cSSam Leffler static void 3194b032f27cSSam Leffler ath_beacon_return(struct ath_softc *sc, struct ath_buf *bf) 3195b032f27cSSam Leffler { 3196b032f27cSSam Leffler 3197b032f27cSSam Leffler if (bf->bf_m != NULL) { 3198b032f27cSSam Leffler bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 3199b032f27cSSam Leffler m_freem(bf->bf_m); 3200b032f27cSSam Leffler bf->bf_m = NULL; 3201b032f27cSSam Leffler } 3202b032f27cSSam Leffler if (bf->bf_node != NULL) { 3203b032f27cSSam Leffler ieee80211_free_node(bf->bf_node); 3204b032f27cSSam Leffler bf->bf_node = NULL; 3205b032f27cSSam Leffler } 3206b032f27cSSam Leffler STAILQ_INSERT_TAIL(&sc->sc_bbuf, bf, bf_list); 3207b032f27cSSam Leffler } 3208b032f27cSSam Leffler 3209b032f27cSSam Leffler /* 3210c42a7b7eSSam Leffler * Reclaim beacon resources. 3211c42a7b7eSSam Leffler */ 32125591b213SSam Leffler static void 32135591b213SSam Leffler ath_beacon_free(struct ath_softc *sc) 32145591b213SSam Leffler { 3215c42a7b7eSSam Leffler struct ath_buf *bf; 32165591b213SSam Leffler 3217f818612bSSam Leffler STAILQ_FOREACH(bf, &sc->sc_bbuf, bf_list) { 32185591b213SSam Leffler if (bf->bf_m != NULL) { 32195591b213SSam Leffler bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 32205591b213SSam Leffler m_freem(bf->bf_m); 32215591b213SSam Leffler bf->bf_m = NULL; 3222f818612bSSam Leffler } 3223f818612bSSam Leffler if (bf->bf_node != NULL) { 3224f818612bSSam Leffler ieee80211_free_node(bf->bf_node); 32255591b213SSam Leffler bf->bf_node = NULL; 32265591b213SSam Leffler } 32275591b213SSam Leffler } 3228f818612bSSam Leffler } 32295591b213SSam Leffler 32305591b213SSam Leffler /* 32315591b213SSam Leffler * Configure the beacon and sleep timers. 32325591b213SSam Leffler * 32335591b213SSam Leffler * When operating as an AP this resets the TSF and sets 32345591b213SSam Leffler * up the hardware to notify us when we need to issue beacons. 32355591b213SSam Leffler * 32365591b213SSam Leffler * When operating in station mode this sets up the beacon 32375591b213SSam Leffler * timers according to the timestamp of the last received 32385591b213SSam Leffler * beacon and the current TSF, configures PCF and DTIM 32395591b213SSam Leffler * handling, programs the sleep registers so the hardware 32405591b213SSam Leffler * will wakeup in time to receive beacons, and configures 32415591b213SSam Leffler * the beacon miss handling so we'll receive a BMISS 32425591b213SSam Leffler * interrupt when we stop seeing beacons from the AP 32435591b213SSam Leffler * we've associated with. 32445591b213SSam Leffler */ 32455591b213SSam Leffler static void 3246b032f27cSSam Leffler ath_beacon_config(struct ath_softc *sc, struct ieee80211vap *vap) 32475591b213SSam Leffler { 324880d939bfSSam Leffler #define TSF_TO_TU(_h,_l) \ 324980d939bfSSam Leffler ((((u_int32_t)(_h)) << 22) | (((u_int32_t)(_l)) >> 10)) 325080d939bfSSam Leffler #define FUDGE 2 32515591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 3252b032f27cSSam Leffler struct ieee80211com *ic = sc->sc_ifp->if_l2com; 3253b032f27cSSam Leffler struct ieee80211_node *ni; 325480d939bfSSam Leffler u_int32_t nexttbtt, intval, tsftu; 325580d939bfSSam Leffler u_int64_t tsf; 32565591b213SSam Leffler 3257b032f27cSSam Leffler if (vap == NULL) 3258b032f27cSSam Leffler vap = TAILQ_FIRST(&ic->ic_vaps); /* XXX */ 3259b032f27cSSam Leffler ni = vap->iv_bss; 3260b032f27cSSam Leffler 32618371372bSSam Leffler /* extract tstamp from last beacon and convert to TU */ 32628371372bSSam Leffler nexttbtt = TSF_TO_TU(LE_READ_4(ni->ni_tstamp.data + 4), 32638371372bSSam Leffler LE_READ_4(ni->ni_tstamp.data)); 3264b032f27cSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 3265b032f27cSSam Leffler /* 3266b032f27cSSam Leffler * For multi-bss ap support beacons are either staggered 3267b032f27cSSam Leffler * evenly over N slots or burst together. For the former 3268b032f27cSSam Leffler * arrange for the SWBA to be delivered for each slot. 3269b032f27cSSam Leffler * Slots that are not occupied will generate nothing. 3270b032f27cSSam Leffler */ 32718371372bSSam Leffler /* NB: the beacon interval is kept internally in TU's */ 32724bacf7c1SSam Leffler intval = ni->ni_intval & HAL_BEACON_PERIOD; 3273b032f27cSSam Leffler if (sc->sc_stagbeacons) 3274b032f27cSSam Leffler intval /= ATH_BCBUF; 3275b032f27cSSam Leffler } else { 3276b032f27cSSam Leffler /* NB: the beacon interval is kept internally in TU's */ 3277b032f27cSSam Leffler intval = ni->ni_intval & HAL_BEACON_PERIOD; 3278b032f27cSSam Leffler } 3279a6c992f4SSam Leffler if (nexttbtt == 0) /* e.g. for ap mode */ 3280a6c992f4SSam Leffler nexttbtt = intval; 3281a6c992f4SSam Leffler else if (intval) /* NB: can be 0 for monitor mode */ 3282a6c992f4SSam Leffler nexttbtt = roundup(nexttbtt, intval); 3283a6c992f4SSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON, "%s: nexttbtt %u intval %u (%u)\n", 3284a6c992f4SSam Leffler __func__, nexttbtt, intval, ni->ni_intval); 3285b032f27cSSam Leffler if (ic->ic_opmode == IEEE80211_M_STA && !sc->sc_swbmiss) { 32865591b213SSam Leffler HAL_BEACON_STATE bs; 32878371372bSSam Leffler int dtimperiod, dtimcount; 32888371372bSSam Leffler int cfpperiod, cfpcount; 32895591b213SSam Leffler 32908371372bSSam Leffler /* 32918371372bSSam Leffler * Setup dtim and cfp parameters according to 32928371372bSSam Leffler * last beacon we received (which may be none). 32938371372bSSam Leffler */ 32948371372bSSam Leffler dtimperiod = ni->ni_dtim_period; 32958371372bSSam Leffler if (dtimperiod <= 0) /* NB: 0 if not known */ 32968371372bSSam Leffler dtimperiod = 1; 32978371372bSSam Leffler dtimcount = ni->ni_dtim_count; 32988371372bSSam Leffler if (dtimcount >= dtimperiod) /* NB: sanity check */ 32998371372bSSam Leffler dtimcount = 0; /* XXX? */ 33008371372bSSam Leffler cfpperiod = 1; /* NB: no PCF support yet */ 33018371372bSSam Leffler cfpcount = 0; 33028371372bSSam Leffler /* 33038371372bSSam Leffler * Pull nexttbtt forward to reflect the current 33048371372bSSam Leffler * TSF and calculate dtim+cfp state for the result. 33058371372bSSam Leffler */ 33068371372bSSam Leffler tsf = ath_hal_gettsf64(ah); 330780d939bfSSam Leffler tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; 33088371372bSSam Leffler do { 33098371372bSSam Leffler nexttbtt += intval; 33108371372bSSam Leffler if (--dtimcount < 0) { 33118371372bSSam Leffler dtimcount = dtimperiod - 1; 33128371372bSSam Leffler if (--cfpcount < 0) 33138371372bSSam Leffler cfpcount = cfpperiod - 1; 33148371372bSSam Leffler } 33158371372bSSam Leffler } while (nexttbtt < tsftu); 33165591b213SSam Leffler memset(&bs, 0, sizeof(bs)); 3317a6c992f4SSam Leffler bs.bs_intval = intval; 33185591b213SSam Leffler bs.bs_nexttbtt = nexttbtt; 33198371372bSSam Leffler bs.bs_dtimperiod = dtimperiod*intval; 33208371372bSSam Leffler bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval; 33218371372bSSam Leffler bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod; 33228371372bSSam Leffler bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod; 33238371372bSSam Leffler bs.bs_cfpmaxduration = 0; 33248371372bSSam Leffler #if 0 33255591b213SSam Leffler /* 3326c42a7b7eSSam Leffler * The 802.11 layer records the offset to the DTIM 3327c42a7b7eSSam Leffler * bitmap while receiving beacons; use it here to 3328c42a7b7eSSam Leffler * enable h/w detection of our AID being marked in 3329c42a7b7eSSam Leffler * the bitmap vector (to indicate frames for us are 3330c42a7b7eSSam Leffler * pending at the AP). 33318371372bSSam Leffler * XXX do DTIM handling in s/w to WAR old h/w bugs 33328371372bSSam Leffler * XXX enable based on h/w rev for newer chips 3333c42a7b7eSSam Leffler */ 3334c42a7b7eSSam Leffler bs.bs_timoffset = ni->ni_timoff; 33358371372bSSam Leffler #endif 3336c42a7b7eSSam Leffler /* 33375591b213SSam Leffler * Calculate the number of consecutive beacons to miss 333868e8e04eSSam Leffler * before taking a BMISS interrupt. 33395591b213SSam Leffler * Note that we clamp the result to at most 10 beacons. 33405591b213SSam Leffler */ 3341b032f27cSSam Leffler bs.bs_bmissthreshold = vap->iv_bmissthreshold; 33425591b213SSam Leffler if (bs.bs_bmissthreshold > 10) 33435591b213SSam Leffler bs.bs_bmissthreshold = 10; 33445591b213SSam Leffler else if (bs.bs_bmissthreshold <= 0) 33455591b213SSam Leffler bs.bs_bmissthreshold = 1; 33465591b213SSam Leffler 33475591b213SSam Leffler /* 33485591b213SSam Leffler * Calculate sleep duration. The configuration is 33495591b213SSam Leffler * given in ms. We insure a multiple of the beacon 33505591b213SSam Leffler * period is used. Also, if the sleep duration is 33515591b213SSam Leffler * greater than the DTIM period then it makes senses 33525591b213SSam Leffler * to make it a multiple of that. 33535591b213SSam Leffler * 33545591b213SSam Leffler * XXX fixed at 100ms 33555591b213SSam Leffler */ 33564bacf7c1SSam Leffler bs.bs_sleepduration = 33574bacf7c1SSam Leffler roundup(IEEE80211_MS_TO_TU(100), bs.bs_intval); 33585591b213SSam Leffler if (bs.bs_sleepduration > bs.bs_dtimperiod) 33595591b213SSam Leffler bs.bs_sleepduration = roundup(bs.bs_sleepduration, bs.bs_dtimperiod); 33605591b213SSam Leffler 3361c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_BEACON, 33628371372bSSam 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" 33635591b213SSam Leffler , __func__ 33648371372bSSam Leffler , tsf, tsftu 33655591b213SSam Leffler , bs.bs_intval 33665591b213SSam Leffler , bs.bs_nexttbtt 33675591b213SSam Leffler , bs.bs_dtimperiod 33685591b213SSam Leffler , bs.bs_nextdtim 33695591b213SSam Leffler , bs.bs_bmissthreshold 33705591b213SSam Leffler , bs.bs_sleepduration 3371c42a7b7eSSam Leffler , bs.bs_cfpperiod 3372c42a7b7eSSam Leffler , bs.bs_cfpmaxduration 3373c42a7b7eSSam Leffler , bs.bs_cfpnext 3374c42a7b7eSSam Leffler , bs.bs_timoffset 3375c42a7b7eSSam Leffler ); 33765591b213SSam Leffler ath_hal_intrset(ah, 0); 3377c42a7b7eSSam Leffler ath_hal_beacontimers(ah, &bs); 33785591b213SSam Leffler sc->sc_imask |= HAL_INT_BMISS; 33795591b213SSam Leffler ath_hal_intrset(ah, sc->sc_imask); 33805591b213SSam Leffler } else { 33815591b213SSam Leffler ath_hal_intrset(ah, 0); 3382a6c992f4SSam Leffler if (nexttbtt == intval) 3383c42a7b7eSSam Leffler intval |= HAL_BEACON_RESET_TSF; 3384c42a7b7eSSam Leffler if (ic->ic_opmode == IEEE80211_M_IBSS) { 3385c42a7b7eSSam Leffler /* 3386c42a7b7eSSam Leffler * In IBSS mode enable the beacon timers but only 3387c42a7b7eSSam Leffler * enable SWBA interrupts if we need to manually 3388c42a7b7eSSam Leffler * prepare beacon frames. Otherwise we use a 3389c42a7b7eSSam Leffler * self-linked tx descriptor and let the hardware 3390c42a7b7eSSam Leffler * deal with things. 3391c42a7b7eSSam Leffler */ 3392c42a7b7eSSam Leffler intval |= HAL_BEACON_ENA; 3393c42a7b7eSSam Leffler if (!sc->sc_hasveol) 3394c42a7b7eSSam Leffler sc->sc_imask |= HAL_INT_SWBA; 339580d939bfSSam Leffler if ((intval & HAL_BEACON_RESET_TSF) == 0) { 339680d939bfSSam Leffler /* 339780d939bfSSam Leffler * Pull nexttbtt forward to reflect 339880d939bfSSam Leffler * the current TSF. 339980d939bfSSam Leffler */ 340080d939bfSSam Leffler tsf = ath_hal_gettsf64(ah); 340180d939bfSSam Leffler tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; 340280d939bfSSam Leffler do { 340380d939bfSSam Leffler nexttbtt += intval; 340480d939bfSSam Leffler } while (nexttbtt < tsftu); 340580d939bfSSam Leffler } 34060f2e86fbSSam Leffler ath_beaconq_config(sc); 3407c42a7b7eSSam Leffler } else if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 3408c42a7b7eSSam Leffler /* 3409c42a7b7eSSam Leffler * In AP mode we enable the beacon timers and 3410c42a7b7eSSam Leffler * SWBA interrupts to prepare beacon frames. 3411c42a7b7eSSam Leffler */ 3412c42a7b7eSSam Leffler intval |= HAL_BEACON_ENA; 34135591b213SSam Leffler sc->sc_imask |= HAL_INT_SWBA; /* beacon prepare */ 34140f2e86fbSSam Leffler ath_beaconq_config(sc); 3415c42a7b7eSSam Leffler } 3416c42a7b7eSSam Leffler ath_hal_beaconinit(ah, nexttbtt, intval); 3417c42a7b7eSSam Leffler sc->sc_bmisscount = 0; 34185591b213SSam Leffler ath_hal_intrset(ah, sc->sc_imask); 3419c42a7b7eSSam Leffler /* 3420c42a7b7eSSam Leffler * When using a self-linked beacon descriptor in 3421c42a7b7eSSam Leffler * ibss mode load it once here. 3422c42a7b7eSSam Leffler */ 3423c42a7b7eSSam Leffler if (ic->ic_opmode == IEEE80211_M_IBSS && sc->sc_hasveol) 3424b032f27cSSam Leffler ath_beacon_start_adhoc(sc, vap); 34255591b213SSam Leffler } 342680d939bfSSam Leffler sc->sc_syncbeacon = 0; 342780d939bfSSam Leffler #undef FUDGE 34288371372bSSam Leffler #undef TSF_TO_TU 34295591b213SSam Leffler } 34305591b213SSam Leffler 34315591b213SSam Leffler static void 34325591b213SSam Leffler ath_load_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 34335591b213SSam Leffler { 34345591b213SSam Leffler bus_addr_t *paddr = (bus_addr_t*) arg; 3435d77367bfSSam Leffler KASSERT(error == 0, ("error %u on bus_dma callback", error)); 34365591b213SSam Leffler *paddr = segs->ds_addr; 34375591b213SSam Leffler } 34385591b213SSam Leffler 34395591b213SSam Leffler static int 3440c42a7b7eSSam Leffler ath_descdma_setup(struct ath_softc *sc, 3441c42a7b7eSSam Leffler struct ath_descdma *dd, ath_bufhead *head, 3442c42a7b7eSSam Leffler const char *name, int nbuf, int ndesc) 3443c42a7b7eSSam Leffler { 3444c42a7b7eSSam Leffler #define DS2PHYS(_dd, _ds) \ 3445c42a7b7eSSam Leffler ((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc)) 3446fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 3447c42a7b7eSSam Leffler struct ath_desc *ds; 3448c42a7b7eSSam Leffler struct ath_buf *bf; 3449c42a7b7eSSam Leffler int i, bsize, error; 3450c42a7b7eSSam Leffler 3451c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_RESET, "%s: %s DMA: %u buffers %u desc/buf\n", 3452c42a7b7eSSam Leffler __func__, name, nbuf, ndesc); 3453c42a7b7eSSam Leffler 3454c42a7b7eSSam Leffler dd->dd_name = name; 3455c42a7b7eSSam Leffler dd->dd_desc_len = sizeof(struct ath_desc) * nbuf * ndesc; 3456c42a7b7eSSam Leffler 3457c42a7b7eSSam Leffler /* 3458c42a7b7eSSam Leffler * Setup DMA descriptor area. 3459c42a7b7eSSam Leffler */ 3460c2175ff5SMarius Strobl error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), /* parent */ 3461c42a7b7eSSam Leffler PAGE_SIZE, 0, /* alignment, bounds */ 3462c42a7b7eSSam Leffler BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 3463c42a7b7eSSam Leffler BUS_SPACE_MAXADDR, /* highaddr */ 3464c42a7b7eSSam Leffler NULL, NULL, /* filter, filterarg */ 3465c42a7b7eSSam Leffler dd->dd_desc_len, /* maxsize */ 3466c42a7b7eSSam Leffler 1, /* nsegments */ 34676ccb8ea7SSam Leffler dd->dd_desc_len, /* maxsegsize */ 3468c42a7b7eSSam Leffler BUS_DMA_ALLOCNOW, /* flags */ 3469c42a7b7eSSam Leffler NULL, /* lockfunc */ 3470c42a7b7eSSam Leffler NULL, /* lockarg */ 3471c42a7b7eSSam Leffler &dd->dd_dmat); 3472c42a7b7eSSam Leffler if (error != 0) { 3473c42a7b7eSSam Leffler if_printf(ifp, "cannot allocate %s DMA tag\n", dd->dd_name); 3474c42a7b7eSSam Leffler return error; 3475c42a7b7eSSam Leffler } 3476c42a7b7eSSam Leffler 3477c42a7b7eSSam Leffler /* allocate descriptors */ 3478c42a7b7eSSam Leffler error = bus_dmamap_create(dd->dd_dmat, BUS_DMA_NOWAIT, &dd->dd_dmamap); 3479c42a7b7eSSam Leffler if (error != 0) { 3480c42a7b7eSSam Leffler if_printf(ifp, "unable to create dmamap for %s descriptors, " 3481c42a7b7eSSam Leffler "error %u\n", dd->dd_name, error); 3482c42a7b7eSSam Leffler goto fail0; 3483c42a7b7eSSam Leffler } 3484c42a7b7eSSam Leffler 3485c42a7b7eSSam Leffler error = bus_dmamem_alloc(dd->dd_dmat, (void**) &dd->dd_desc, 34860553a01fSSam Leffler BUS_DMA_NOWAIT | BUS_DMA_COHERENT, 34870553a01fSSam Leffler &dd->dd_dmamap); 3488c42a7b7eSSam Leffler if (error != 0) { 3489c42a7b7eSSam Leffler if_printf(ifp, "unable to alloc memory for %u %s descriptors, " 3490c42a7b7eSSam Leffler "error %u\n", nbuf * ndesc, dd->dd_name, error); 3491c42a7b7eSSam Leffler goto fail1; 3492c42a7b7eSSam Leffler } 3493c42a7b7eSSam Leffler 3494c42a7b7eSSam Leffler error = bus_dmamap_load(dd->dd_dmat, dd->dd_dmamap, 3495c42a7b7eSSam Leffler dd->dd_desc, dd->dd_desc_len, 3496c42a7b7eSSam Leffler ath_load_cb, &dd->dd_desc_paddr, 3497c42a7b7eSSam Leffler BUS_DMA_NOWAIT); 3498c42a7b7eSSam Leffler if (error != 0) { 3499c42a7b7eSSam Leffler if_printf(ifp, "unable to map %s descriptors, error %u\n", 3500c42a7b7eSSam Leffler dd->dd_name, error); 3501c42a7b7eSSam Leffler goto fail2; 3502c42a7b7eSSam Leffler } 3503c42a7b7eSSam Leffler 3504c42a7b7eSSam Leffler ds = dd->dd_desc; 3505c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_RESET, "%s: %s DMA map: %p (%lu) -> %p (%lu)\n", 3506c42a7b7eSSam Leffler __func__, dd->dd_name, ds, (u_long) dd->dd_desc_len, 3507c42a7b7eSSam Leffler (caddr_t) dd->dd_desc_paddr, /*XXX*/ (u_long) dd->dd_desc_len); 3508c42a7b7eSSam Leffler 3509ebecf802SSam Leffler /* allocate rx buffers */ 3510c42a7b7eSSam Leffler bsize = sizeof(struct ath_buf) * nbuf; 3511c42a7b7eSSam Leffler bf = malloc(bsize, M_ATHDEV, M_NOWAIT | M_ZERO); 3512c42a7b7eSSam Leffler if (bf == NULL) { 3513c42a7b7eSSam Leffler if_printf(ifp, "malloc of %s buffers failed, size %u\n", 3514c42a7b7eSSam Leffler dd->dd_name, bsize); 3515c42a7b7eSSam Leffler goto fail3; 3516c42a7b7eSSam Leffler } 3517c42a7b7eSSam Leffler dd->dd_bufptr = bf; 3518c42a7b7eSSam Leffler 3519c42a7b7eSSam Leffler STAILQ_INIT(head); 3520c42a7b7eSSam Leffler for (i = 0; i < nbuf; i++, bf++, ds += ndesc) { 3521c42a7b7eSSam Leffler bf->bf_desc = ds; 3522c42a7b7eSSam Leffler bf->bf_daddr = DS2PHYS(dd, ds); 3523c42a7b7eSSam Leffler error = bus_dmamap_create(sc->sc_dmat, BUS_DMA_NOWAIT, 3524c42a7b7eSSam Leffler &bf->bf_dmamap); 3525c42a7b7eSSam Leffler if (error != 0) { 3526c42a7b7eSSam Leffler if_printf(ifp, "unable to create dmamap for %s " 3527c42a7b7eSSam Leffler "buffer %u, error %u\n", dd->dd_name, i, error); 3528c42a7b7eSSam Leffler ath_descdma_cleanup(sc, dd, head); 3529c42a7b7eSSam Leffler return error; 3530c42a7b7eSSam Leffler } 3531c42a7b7eSSam Leffler STAILQ_INSERT_TAIL(head, bf, bf_list); 3532c42a7b7eSSam Leffler } 3533c42a7b7eSSam Leffler return 0; 3534c42a7b7eSSam Leffler fail3: 3535c42a7b7eSSam Leffler bus_dmamap_unload(dd->dd_dmat, dd->dd_dmamap); 3536c42a7b7eSSam Leffler fail2: 3537c42a7b7eSSam Leffler bus_dmamem_free(dd->dd_dmat, dd->dd_desc, dd->dd_dmamap); 3538c42a7b7eSSam Leffler fail1: 3539c42a7b7eSSam Leffler bus_dmamap_destroy(dd->dd_dmat, dd->dd_dmamap); 3540c42a7b7eSSam Leffler fail0: 3541c42a7b7eSSam Leffler bus_dma_tag_destroy(dd->dd_dmat); 3542c42a7b7eSSam Leffler memset(dd, 0, sizeof(*dd)); 3543c42a7b7eSSam Leffler return error; 3544c42a7b7eSSam Leffler #undef DS2PHYS 3545c42a7b7eSSam Leffler } 3546c42a7b7eSSam Leffler 3547c42a7b7eSSam Leffler static void 3548c42a7b7eSSam Leffler ath_descdma_cleanup(struct ath_softc *sc, 3549c42a7b7eSSam Leffler struct ath_descdma *dd, ath_bufhead *head) 3550c42a7b7eSSam Leffler { 3551c42a7b7eSSam Leffler struct ath_buf *bf; 3552c42a7b7eSSam Leffler struct ieee80211_node *ni; 3553c42a7b7eSSam Leffler 3554c42a7b7eSSam Leffler bus_dmamap_unload(dd->dd_dmat, dd->dd_dmamap); 3555c42a7b7eSSam Leffler bus_dmamem_free(dd->dd_dmat, dd->dd_desc, dd->dd_dmamap); 3556c42a7b7eSSam Leffler bus_dmamap_destroy(dd->dd_dmat, dd->dd_dmamap); 3557c42a7b7eSSam Leffler bus_dma_tag_destroy(dd->dd_dmat); 3558c42a7b7eSSam Leffler 3559c42a7b7eSSam Leffler STAILQ_FOREACH(bf, head, bf_list) { 3560c42a7b7eSSam Leffler if (bf->bf_m) { 3561c42a7b7eSSam Leffler m_freem(bf->bf_m); 3562c42a7b7eSSam Leffler bf->bf_m = NULL; 3563c42a7b7eSSam Leffler } 3564c42a7b7eSSam Leffler if (bf->bf_dmamap != NULL) { 3565c42a7b7eSSam Leffler bus_dmamap_destroy(sc->sc_dmat, bf->bf_dmamap); 3566c42a7b7eSSam Leffler bf->bf_dmamap = NULL; 3567c42a7b7eSSam Leffler } 3568c42a7b7eSSam Leffler ni = bf->bf_node; 3569c42a7b7eSSam Leffler bf->bf_node = NULL; 3570c42a7b7eSSam Leffler if (ni != NULL) { 3571c42a7b7eSSam Leffler /* 3572c42a7b7eSSam Leffler * Reclaim node reference. 3573c42a7b7eSSam Leffler */ 3574c42a7b7eSSam Leffler ieee80211_free_node(ni); 3575c42a7b7eSSam Leffler } 3576c42a7b7eSSam Leffler } 3577c42a7b7eSSam Leffler 3578c42a7b7eSSam Leffler STAILQ_INIT(head); 3579c42a7b7eSSam Leffler free(dd->dd_bufptr, M_ATHDEV); 3580c42a7b7eSSam Leffler memset(dd, 0, sizeof(*dd)); 3581c42a7b7eSSam Leffler } 3582c42a7b7eSSam Leffler 3583c42a7b7eSSam Leffler static int 35845591b213SSam Leffler ath_desc_alloc(struct ath_softc *sc) 35855591b213SSam Leffler { 3586c42a7b7eSSam Leffler int error; 35875591b213SSam Leffler 3588c42a7b7eSSam Leffler error = ath_descdma_setup(sc, &sc->sc_rxdma, &sc->sc_rxbuf, 3589e2d787faSSam Leffler "rx", ath_rxbuf, 1); 35905591b213SSam Leffler if (error != 0) 35915591b213SSam Leffler return error; 35925591b213SSam Leffler 3593c42a7b7eSSam Leffler error = ath_descdma_setup(sc, &sc->sc_txdma, &sc->sc_txbuf, 3594e2d787faSSam Leffler "tx", ath_txbuf, ATH_TXDESC); 3595c42a7b7eSSam Leffler if (error != 0) { 3596c42a7b7eSSam Leffler ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf); 35975591b213SSam Leffler return error; 3598c42a7b7eSSam Leffler } 3599c42a7b7eSSam Leffler 3600c42a7b7eSSam Leffler error = ath_descdma_setup(sc, &sc->sc_bdma, &sc->sc_bbuf, 3601b032f27cSSam Leffler "beacon", ATH_BCBUF, 1); 3602c42a7b7eSSam Leffler if (error != 0) { 3603c42a7b7eSSam Leffler ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf); 3604c42a7b7eSSam Leffler ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf); 3605c42a7b7eSSam Leffler return error; 3606c42a7b7eSSam Leffler } 36075591b213SSam Leffler return 0; 36085591b213SSam Leffler } 36095591b213SSam Leffler 36105591b213SSam Leffler static void 36115591b213SSam Leffler ath_desc_free(struct ath_softc *sc) 36125591b213SSam Leffler { 36135591b213SSam Leffler 3614c42a7b7eSSam Leffler if (sc->sc_bdma.dd_desc_len != 0) 3615c42a7b7eSSam Leffler ath_descdma_cleanup(sc, &sc->sc_bdma, &sc->sc_bbuf); 3616c42a7b7eSSam Leffler if (sc->sc_txdma.dd_desc_len != 0) 3617c42a7b7eSSam Leffler ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf); 3618c42a7b7eSSam Leffler if (sc->sc_rxdma.dd_desc_len != 0) 3619c42a7b7eSSam Leffler ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf); 36205591b213SSam Leffler } 36215591b213SSam Leffler 36225591b213SSam Leffler static struct ieee80211_node * 3623c42a7b7eSSam Leffler ath_node_alloc(struct ieee80211_node_table *nt) 36245591b213SSam Leffler { 3625c42a7b7eSSam Leffler struct ieee80211com *ic = nt->nt_ic; 3626c42a7b7eSSam Leffler struct ath_softc *sc = ic->ic_ifp->if_softc; 3627c42a7b7eSSam Leffler const size_t space = sizeof(struct ath_node) + sc->sc_rc->arc_space; 3628c42a7b7eSSam Leffler struct ath_node *an; 3629c42a7b7eSSam Leffler 3630c42a7b7eSSam Leffler an = malloc(space, M_80211_NODE, M_NOWAIT|M_ZERO); 3631c42a7b7eSSam Leffler if (an == NULL) { 3632c42a7b7eSSam Leffler /* XXX stat+msg */ 3633de5af704SSam Leffler return NULL; 36345591b213SSam Leffler } 3635c42a7b7eSSam Leffler ath_rate_node_init(sc, an); 36365591b213SSam Leffler 3637c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_NODE, "%s: an %p\n", __func__, an); 3638c42a7b7eSSam Leffler return &an->an_node; 3639c42a7b7eSSam Leffler } 3640c42a7b7eSSam Leffler 36415591b213SSam Leffler static void 3642c42a7b7eSSam Leffler ath_node_free(struct ieee80211_node *ni) 36435591b213SSam Leffler { 3644c42a7b7eSSam Leffler struct ieee80211com *ic = ni->ni_ic; 3645c42a7b7eSSam Leffler struct ath_softc *sc = ic->ic_ifp->if_softc; 36461e774079SSam Leffler 3647c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_NODE, "%s: ni %p\n", __func__, ni); 3648c42a7b7eSSam Leffler 3649c42a7b7eSSam Leffler ath_rate_node_cleanup(sc, ATH_NODE(ni)); 3650c42a7b7eSSam Leffler sc->sc_node_free(ni); 36515591b213SSam Leffler } 36525591b213SSam Leffler 365368e8e04eSSam Leffler static void 365468e8e04eSSam Leffler ath_node_getsignal(const struct ieee80211_node *ni, int8_t *rssi, int8_t *noise) 365568e8e04eSSam Leffler { 365668e8e04eSSam Leffler struct ieee80211com *ic = ni->ni_ic; 365768e8e04eSSam Leffler struct ath_softc *sc = ic->ic_ifp->if_softc; 365868e8e04eSSam Leffler struct ath_hal *ah = sc->sc_ah; 365968e8e04eSSam Leffler HAL_CHANNEL hchan; 366068e8e04eSSam Leffler 3661b032f27cSSam Leffler *rssi = ic->ic_node_getrssi(ni); 366268e8e04eSSam Leffler if (ni->ni_chan != IEEE80211_CHAN_ANYC) { 366368e8e04eSSam Leffler ath_mapchan(&hchan, ni->ni_chan); 366468e8e04eSSam Leffler *noise = ath_hal_getchannoise(ah, &hchan); 366568e8e04eSSam Leffler } else 366668e8e04eSSam Leffler *noise = -95; /* nominally correct */ 366768e8e04eSSam Leffler } 366868e8e04eSSam Leffler 36695591b213SSam Leffler static int 36705591b213SSam Leffler ath_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf) 36715591b213SSam Leffler { 36725591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 36735591b213SSam Leffler int error; 36745591b213SSam Leffler struct mbuf *m; 36755591b213SSam Leffler struct ath_desc *ds; 36765591b213SSam Leffler 36775591b213SSam Leffler m = bf->bf_m; 36785591b213SSam Leffler if (m == NULL) { 36795591b213SSam Leffler /* 36805591b213SSam Leffler * NB: by assigning a page to the rx dma buffer we 36815591b213SSam Leffler * implicitly satisfy the Atheros requirement that 36825591b213SSam Leffler * this buffer be cache-line-aligned and sized to be 36835591b213SSam Leffler * multiple of the cache line size. Not doing this 36845591b213SSam Leffler * causes weird stuff to happen (for the 5210 at least). 36855591b213SSam Leffler */ 36865591b213SSam Leffler m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); 36875591b213SSam Leffler if (m == NULL) { 3688c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, 3689c42a7b7eSSam Leffler "%s: no mbuf/cluster\n", __func__); 36905591b213SSam Leffler sc->sc_stats.ast_rx_nombuf++; 36915591b213SSam Leffler return ENOMEM; 36925591b213SSam Leffler } 36935591b213SSam Leffler m->m_pkthdr.len = m->m_len = m->m_ext.ext_size; 36945591b213SSam Leffler 3695f9e6219bSSam Leffler error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, 3696c42a7b7eSSam Leffler bf->bf_dmamap, m, 3697f9e6219bSSam Leffler bf->bf_segs, &bf->bf_nseg, 36985591b213SSam Leffler BUS_DMA_NOWAIT); 36995591b213SSam Leffler if (error != 0) { 3700c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, 3701f9e6219bSSam Leffler "%s: bus_dmamap_load_mbuf_sg failed; error %d\n", 3702c42a7b7eSSam Leffler __func__, error); 37035591b213SSam Leffler sc->sc_stats.ast_rx_busdma++; 3704b2792ff6SSam Leffler m_freem(m); 37055591b213SSam Leffler return error; 37065591b213SSam Leffler } 3707d77367bfSSam Leffler KASSERT(bf->bf_nseg == 1, 3708d77367bfSSam Leffler ("multi-segment packet; nseg %u", bf->bf_nseg)); 3709b2792ff6SSam Leffler bf->bf_m = m; 37105591b213SSam Leffler } 37115591b213SSam Leffler bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREREAD); 37125591b213SSam Leffler 371304e22a02SSam Leffler /* 371404e22a02SSam Leffler * Setup descriptors. For receive we always terminate 371504e22a02SSam Leffler * the descriptor list with a self-linked entry so we'll 371604e22a02SSam Leffler * not get overrun under high load (as can happen with a 3717c42a7b7eSSam Leffler * 5212 when ANI processing enables PHY error frames). 371804e22a02SSam Leffler * 371904e22a02SSam Leffler * To insure the last descriptor is self-linked we create 372004e22a02SSam Leffler * each descriptor as self-linked and add it to the end. As 372104e22a02SSam Leffler * each additional descriptor is added the previous self-linked 372204e22a02SSam Leffler * entry is ``fixed'' naturally. This should be safe even 372304e22a02SSam Leffler * if DMA is happening. When processing RX interrupts we 372404e22a02SSam Leffler * never remove/process the last, self-linked, entry on the 372504e22a02SSam Leffler * descriptor list. This insures the hardware always has 372604e22a02SSam Leffler * someplace to write a new frame. 372704e22a02SSam Leffler */ 37285591b213SSam Leffler ds = bf->bf_desc; 372904e22a02SSam Leffler ds->ds_link = bf->bf_daddr; /* link to self */ 37305591b213SSam Leffler ds->ds_data = bf->bf_segs[0].ds_addr; 37315591b213SSam Leffler ath_hal_setuprxdesc(ah, ds 37325591b213SSam Leffler , m->m_len /* buffer size */ 37335591b213SSam Leffler , 0 37345591b213SSam Leffler ); 37355591b213SSam Leffler 37365591b213SSam Leffler if (sc->sc_rxlink != NULL) 37375591b213SSam Leffler *sc->sc_rxlink = bf->bf_daddr; 37385591b213SSam Leffler sc->sc_rxlink = &ds->ds_link; 37395591b213SSam Leffler return 0; 37405591b213SSam Leffler } 37415591b213SSam Leffler 3742c42a7b7eSSam Leffler /* 374303ed599aSSam Leffler * Extend 15-bit time stamp from rx descriptor to 37447b0c77ecSSam Leffler * a full 64-bit TSF using the specified TSF. 374503ed599aSSam Leffler */ 374603ed599aSSam Leffler static __inline u_int64_t 37477b0c77ecSSam Leffler ath_extend_tsf(u_int32_t rstamp, u_int64_t tsf) 374803ed599aSSam Leffler { 374903ed599aSSam Leffler if ((tsf & 0x7fff) < rstamp) 375003ed599aSSam Leffler tsf -= 0x8000; 375103ed599aSSam Leffler return ((tsf &~ 0x7fff) | rstamp); 375203ed599aSSam Leffler } 375303ed599aSSam Leffler 375403ed599aSSam Leffler /* 3755c42a7b7eSSam Leffler * Intercept management frames to collect beacon rssi data 3756c42a7b7eSSam Leffler * and to do ibss merges. 3757c42a7b7eSSam Leffler */ 3758c42a7b7eSSam Leffler static void 3759b032f27cSSam Leffler ath_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, 376068e8e04eSSam Leffler int subtype, int rssi, int noise, u_int32_t rstamp) 3761c42a7b7eSSam Leffler { 3762b032f27cSSam Leffler struct ieee80211vap *vap = ni->ni_vap; 3763b032f27cSSam Leffler struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; 3764c42a7b7eSSam Leffler 3765c42a7b7eSSam Leffler /* 3766c42a7b7eSSam Leffler * Call up first so subsequent work can use information 3767c42a7b7eSSam Leffler * potentially stored in the node (e.g. for ibss merge). 3768c42a7b7eSSam Leffler */ 3769b032f27cSSam Leffler ATH_VAP(vap)->av_recv_mgmt(ni, m, subtype, rssi, noise, rstamp); 3770c42a7b7eSSam Leffler switch (subtype) { 3771c42a7b7eSSam Leffler case IEEE80211_FC0_SUBTYPE_BEACON: 3772c42a7b7eSSam Leffler /* update rssi statistics for use by the hal */ 3773ffa2cab6SSam Leffler ATH_RSSI_LPF(sc->sc_halstats.ns_avgbrssi, rssi); 377480d939bfSSam Leffler if (sc->sc_syncbeacon && 3775b032f27cSSam Leffler ni == vap->iv_bss && vap->iv_state == IEEE80211_S_RUN) { 377680d939bfSSam Leffler /* 377780d939bfSSam Leffler * Resync beacon timers using the tsf of the beacon 377880d939bfSSam Leffler * frame we just received. 377980d939bfSSam Leffler */ 3780b032f27cSSam Leffler ath_beacon_config(sc, vap); 378180d939bfSSam Leffler } 3782c42a7b7eSSam Leffler /* fall thru... */ 3783c42a7b7eSSam Leffler case IEEE80211_FC0_SUBTYPE_PROBE_RESP: 3784b032f27cSSam Leffler if (vap->iv_opmode == IEEE80211_M_IBSS && 3785b032f27cSSam Leffler vap->iv_state == IEEE80211_S_RUN) { 37867b0c77ecSSam Leffler u_int64_t tsf = ath_extend_tsf(rstamp, 37877b0c77ecSSam Leffler ath_hal_gettsf64(sc->sc_ah)); 3788c42a7b7eSSam Leffler /* 3789c42a7b7eSSam Leffler * Handle ibss merge as needed; check the tsf on the 3790c42a7b7eSSam Leffler * frame before attempting the merge. The 802.11 spec 3791c42a7b7eSSam Leffler * says the station should change it's bssid to match 3792c42a7b7eSSam Leffler * the oldest station with the same ssid, where oldest 3793f818612bSSam Leffler * is determined by the tsf. Note that hardware 3794f818612bSSam Leffler * reconfiguration happens through callback to 379503ed599aSSam Leffler * ath_newstate as the state machine will go from 379603ed599aSSam Leffler * RUN -> RUN when this happens. 3797c42a7b7eSSam Leffler */ 379803ed599aSSam Leffler if (le64toh(ni->ni_tstamp.tsf) >= tsf) { 379903ed599aSSam Leffler DPRINTF(sc, ATH_DEBUG_STATE, 380033d7d80cSTai-hwa Liang "ibss merge, rstamp %u tsf %ju " 380133d7d80cSTai-hwa Liang "tstamp %ju\n", rstamp, (uintmax_t)tsf, 380233d7d80cSTai-hwa Liang (uintmax_t)ni->ni_tstamp.tsf); 3803641b4d0bSSam Leffler (void) ieee80211_ibss_merge(ni); 3804c42a7b7eSSam Leffler } 380503ed599aSSam Leffler } 3806c42a7b7eSSam Leffler break; 3807c42a7b7eSSam Leffler } 3808c42a7b7eSSam Leffler } 3809c42a7b7eSSam Leffler 3810c42a7b7eSSam Leffler /* 3811c42a7b7eSSam Leffler * Set the default antenna. 3812c42a7b7eSSam Leffler */ 3813c42a7b7eSSam Leffler static void 3814c42a7b7eSSam Leffler ath_setdefantenna(struct ath_softc *sc, u_int antenna) 3815c42a7b7eSSam Leffler { 3816c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 3817c42a7b7eSSam Leffler 3818c42a7b7eSSam Leffler /* XXX block beacon interrupts */ 3819c42a7b7eSSam Leffler ath_hal_setdefantenna(ah, antenna); 3820c42a7b7eSSam Leffler if (sc->sc_defant != antenna) 3821c42a7b7eSSam Leffler sc->sc_stats.ast_ant_defswitch++; 3822c42a7b7eSSam Leffler sc->sc_defant = antenna; 3823c42a7b7eSSam Leffler sc->sc_rxotherant = 0; 3824c42a7b7eSSam Leffler } 3825c42a7b7eSSam Leffler 38267b0c77ecSSam Leffler static int 3827b032f27cSSam Leffler ath_rx_tap(struct ifnet *ifp, struct mbuf *m, 382865f9edeeSSam Leffler const struct ath_rx_status *rs, u_int64_t tsf, int16_t nf) 38297b0c77ecSSam Leffler { 383068e8e04eSSam Leffler #define CHANNEL_HT (CHANNEL_HT20|CHANNEL_HT40PLUS|CHANNEL_HT40MINUS) 3831b032f27cSSam Leffler struct ath_softc *sc = ifp->if_softc; 38327b0c77ecSSam Leffler u_int8_t rix; 38337b0c77ecSSam Leffler 38347b0c77ecSSam Leffler /* 38357b0c77ecSSam Leffler * Discard anything shorter than an ack or cts. 38367b0c77ecSSam Leffler */ 38377b0c77ecSSam Leffler if (m->m_pkthdr.len < IEEE80211_ACK_LEN) { 38387b0c77ecSSam Leffler DPRINTF(sc, ATH_DEBUG_RECV, "%s: runt packet %d\n", 38397b0c77ecSSam Leffler __func__, m->m_pkthdr.len); 38407b0c77ecSSam Leffler sc->sc_stats.ast_rx_tooshort++; 38417b0c77ecSSam Leffler return 0; 38427b0c77ecSSam Leffler } 384365f9edeeSSam Leffler rix = rs->rs_rate; 384468e8e04eSSam Leffler sc->sc_rx_th.wr_rate = sc->sc_hwmap[rix].ieeerate; 38457b0c77ecSSam Leffler sc->sc_rx_th.wr_flags = sc->sc_hwmap[rix].rxflags; 384668e8e04eSSam Leffler #if HAL_ABI_VERSION >= 0x07050400 384768e8e04eSSam Leffler if (sc->sc_curchan.channelFlags & CHANNEL_HT) { 384868e8e04eSSam Leffler /* 384968e8e04eSSam Leffler * For HT operation we must specify the channel 385068e8e04eSSam Leffler * attributes for each packet since they vary. 385168e8e04eSSam Leffler * We deduce this by from HT40 bit in the rx 385268e8e04eSSam Leffler * status and the MCS/legacy rate bit. 385368e8e04eSSam Leffler */ 385468e8e04eSSam Leffler sc->sc_rx_th.wr_chan_flags &= ~IEEE80211_CHAN_HT; 385568e8e04eSSam Leffler if (sc->sc_rx_th.wr_rate & 0x80) { /* HT rate */ 385668e8e04eSSam Leffler /* XXX 40U/40D */ 385768e8e04eSSam Leffler sc->sc_rx_th.wr_chan_flags |= 385868e8e04eSSam Leffler (rs->rs_flags & HAL_RX_2040) ? 385968e8e04eSSam Leffler IEEE80211_CHAN_HT40U : IEEE80211_CHAN_HT20; 386068e8e04eSSam Leffler if ((rs->rs_flags & HAL_RX_GI) == 0) 386168e8e04eSSam Leffler sc->sc_rx_th.wr_flags |= 386268e8e04eSSam Leffler IEEE80211_RADIOTAP_F_SHORTGI; 386368e8e04eSSam Leffler } 386468e8e04eSSam Leffler } 386568e8e04eSSam Leffler #endif 386668e8e04eSSam Leffler sc->sc_rx_th.wr_tsf = htole64(ath_extend_tsf(rs->rs_tstamp, tsf)); 386765f9edeeSSam Leffler if (rs->rs_status & HAL_RXERR_CRC) 38687b0c77ecSSam Leffler sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_BADFCS; 38697b0c77ecSSam Leffler /* XXX propagate other error flags from descriptor */ 387065f9edeeSSam Leffler sc->sc_rx_th.wr_antsignal = rs->rs_rssi + nf; 38717b0c77ecSSam Leffler sc->sc_rx_th.wr_antnoise = nf; 387265f9edeeSSam Leffler sc->sc_rx_th.wr_antenna = rs->rs_antenna; 38737b0c77ecSSam Leffler 3874b032f27cSSam Leffler bpf_mtap2(ifp->if_bpf, &sc->sc_rx_th, sc->sc_rx_th_len, m); 38757b0c77ecSSam Leffler 38767b0c77ecSSam Leffler return 1; 387768e8e04eSSam Leffler #undef CHANNEL_HT 38787b0c77ecSSam Leffler } 38797b0c77ecSSam Leffler 38805591b213SSam Leffler static void 3881b032f27cSSam Leffler ath_handle_micerror(struct ieee80211com *ic, 3882b032f27cSSam Leffler struct ieee80211_frame *wh, int keyix) 3883b032f27cSSam Leffler { 3884b032f27cSSam Leffler struct ieee80211_node *ni; 3885b032f27cSSam Leffler 3886b032f27cSSam Leffler /* XXX recheck MIC to deal w/ chips that lie */ 3887b032f27cSSam Leffler /* XXX discard MIC errors on !data frames */ 3888b032f27cSSam Leffler ni = ieee80211_find_rxnode(ic, (const struct ieee80211_frame_min *) wh); 3889b032f27cSSam Leffler if (ni != NULL) { 3890b032f27cSSam Leffler ieee80211_notify_michael_failure(ni->ni_vap, wh, keyix); 3891b032f27cSSam Leffler ieee80211_free_node(ni); 3892b032f27cSSam Leffler } 3893b032f27cSSam Leffler } 3894b032f27cSSam Leffler 3895b032f27cSSam Leffler static void 38965591b213SSam Leffler ath_rx_proc(void *arg, int npending) 38975591b213SSam Leffler { 38988cec0ab9SSam Leffler #define PA2DESC(_sc, _pa) \ 3899c42a7b7eSSam Leffler ((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \ 3900c42a7b7eSSam Leffler ((_pa) - (_sc)->sc_rxdma.dd_desc_paddr))) 39015591b213SSam Leffler struct ath_softc *sc = arg; 39025591b213SSam Leffler struct ath_buf *bf; 3903fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 3904b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 39055591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 39065591b213SSam Leffler struct ath_desc *ds; 390765f9edeeSSam Leffler struct ath_rx_status *rs; 39085591b213SSam Leffler struct mbuf *m; 39090a915fadSSam Leffler struct ieee80211_node *ni; 3910d7736e13SSam Leffler int len, type, ngood; 39115591b213SSam Leffler u_int phyerr; 39125591b213SSam Leffler HAL_STATUS status; 39137b0c77ecSSam Leffler int16_t nf; 39147b0c77ecSSam Leffler u_int64_t tsf; 39155591b213SSam Leffler 3916c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s: pending %u\n", __func__, npending); 3917d7736e13SSam Leffler ngood = 0; 39187b0c77ecSSam Leffler nf = ath_hal_getchannoise(ah, &sc->sc_curchan); 39197b0c77ecSSam Leffler tsf = ath_hal_gettsf64(ah); 39205591b213SSam Leffler do { 3921c42a7b7eSSam Leffler bf = STAILQ_FIRST(&sc->sc_rxbuf); 39225591b213SSam Leffler if (bf == NULL) { /* NB: shouldn't happen */ 3923c42a7b7eSSam Leffler if_printf(ifp, "%s: no buffer!\n", __func__); 39245591b213SSam Leffler break; 39255591b213SSam Leffler } 3926b2792ff6SSam Leffler m = bf->bf_m; 3927b2792ff6SSam Leffler if (m == NULL) { /* NB: shouldn't happen */ 3928b2792ff6SSam Leffler /* 3929b2792ff6SSam Leffler * If mbuf allocation failed previously there 3930b2792ff6SSam Leffler * will be no mbuf; try again to re-populate it. 3931b2792ff6SSam Leffler */ 3932b2792ff6SSam Leffler /* XXX make debug msg */ 3933b2792ff6SSam Leffler if_printf(ifp, "%s: no mbuf!\n", __func__); 3934b2792ff6SSam Leffler STAILQ_REMOVE_HEAD(&sc->sc_rxbuf, bf_list); 3935b2792ff6SSam Leffler goto rx_next; 3936b2792ff6SSam Leffler } 393704e22a02SSam Leffler ds = bf->bf_desc; 393804e22a02SSam Leffler if (ds->ds_link == bf->bf_daddr) { 393904e22a02SSam Leffler /* NB: never process the self-linked entry at the end */ 394004e22a02SSam Leffler break; 394104e22a02SSam Leffler } 39428cec0ab9SSam Leffler /* XXX sync descriptor memory */ 39438cec0ab9SSam Leffler /* 39448cec0ab9SSam Leffler * Must provide the virtual address of the current 39458cec0ab9SSam Leffler * descriptor, the physical address, and the virtual 39468cec0ab9SSam Leffler * address of the next descriptor in the h/w chain. 39478cec0ab9SSam Leffler * This allows the HAL to look ahead to see if the 39488cec0ab9SSam Leffler * hardware is done with a descriptor by checking the 39498cec0ab9SSam Leffler * done bit in the following descriptor and the address 39508cec0ab9SSam Leffler * of the current descriptor the DMA engine is working 39518cec0ab9SSam Leffler * on. All this is necessary because of our use of 39528cec0ab9SSam Leffler * a self-linked list to avoid rx overruns. 39538cec0ab9SSam Leffler */ 395465f9edeeSSam Leffler rs = &bf->bf_status.ds_rxstat; 39558cec0ab9SSam Leffler status = ath_hal_rxprocdesc(ah, ds, 395665f9edeeSSam Leffler bf->bf_daddr, PA2DESC(sc, ds->ds_link), rs); 3957a585a9a1SSam Leffler #ifdef ATH_DEBUG 3958c42a7b7eSSam Leffler if (sc->sc_debug & ATH_DEBUG_RECV_DESC) 39597a4c5ed9SSam Leffler ath_printrxbuf(bf, 0, status == HAL_OK); 39605591b213SSam Leffler #endif 39615591b213SSam Leffler if (status == HAL_EINPROGRESS) 39625591b213SSam Leffler break; 3963c42a7b7eSSam Leffler STAILQ_REMOVE_HEAD(&sc->sc_rxbuf, bf_list); 396468e8e04eSSam Leffler if (rs->rs_status != 0) { 396565f9edeeSSam Leffler if (rs->rs_status & HAL_RXERR_CRC) 39665591b213SSam Leffler sc->sc_stats.ast_rx_crcerr++; 396765f9edeeSSam Leffler if (rs->rs_status & HAL_RXERR_FIFO) 39685591b213SSam Leffler sc->sc_stats.ast_rx_fifoerr++; 396965f9edeeSSam Leffler if (rs->rs_status & HAL_RXERR_PHY) { 39705591b213SSam Leffler sc->sc_stats.ast_rx_phyerr++; 397165f9edeeSSam Leffler phyerr = rs->rs_phyerr & 0x1f; 39725591b213SSam Leffler sc->sc_stats.ast_rx_phy[phyerr]++; 397368e8e04eSSam Leffler goto rx_error; /* NB: don't count in ierrors */ 3974c42a7b7eSSam Leffler } 397565f9edeeSSam Leffler if (rs->rs_status & HAL_RXERR_DECRYPT) { 397685643802SSam Leffler /* 3977c42a7b7eSSam Leffler * Decrypt error. If the error occurred 3978c42a7b7eSSam Leffler * because there was no hardware key, then 3979c42a7b7eSSam Leffler * let the frame through so the upper layers 3980c42a7b7eSSam Leffler * can process it. This is necessary for 5210 3981c42a7b7eSSam Leffler * parts which have no way to setup a ``clear'' 3982c42a7b7eSSam Leffler * key cache entry. 3983c42a7b7eSSam Leffler * 3984c42a7b7eSSam Leffler * XXX do key cache faulting 398585643802SSam Leffler */ 398665f9edeeSSam Leffler if (rs->rs_keyix == HAL_RXKEYIX_INVALID) 3987c42a7b7eSSam Leffler goto rx_accept; 3988c42a7b7eSSam Leffler sc->sc_stats.ast_rx_badcrypt++; 39895591b213SSam Leffler } 399065f9edeeSSam Leffler if (rs->rs_status & HAL_RXERR_MIC) { 3991c42a7b7eSSam Leffler sc->sc_stats.ast_rx_badmic++; 3992c42a7b7eSSam Leffler /* 3993c42a7b7eSSam Leffler * Do minimal work required to hand off 3994c42a7b7eSSam Leffler * the 802.11 header for notifcation. 3995c42a7b7eSSam Leffler */ 3996c42a7b7eSSam Leffler /* XXX frag's and qos frames */ 399765f9edeeSSam Leffler len = rs->rs_datalen; 3998c42a7b7eSSam Leffler if (len >= sizeof (struct ieee80211_frame)) { 3999c42a7b7eSSam Leffler bus_dmamap_sync(sc->sc_dmat, 4000c42a7b7eSSam Leffler bf->bf_dmamap, 4001c42a7b7eSSam Leffler BUS_DMASYNC_POSTREAD); 4002b032f27cSSam Leffler ath_handle_micerror(ic, 4003c42a7b7eSSam Leffler mtod(m, struct ieee80211_frame *), 40040ab4040aSSam Leffler sc->sc_splitmic ? 4005b032f27cSSam Leffler rs->rs_keyix-32 : rs->rs_keyix); 4006c42a7b7eSSam Leffler } 4007c42a7b7eSSam Leffler } 4008c42a7b7eSSam Leffler ifp->if_ierrors++; 400968e8e04eSSam Leffler rx_error: 401068e8e04eSSam Leffler /* 401168e8e04eSSam Leffler * Cleanup any pending partial frame. 401268e8e04eSSam Leffler */ 401368e8e04eSSam Leffler if (sc->sc_rxpending != NULL) { 401468e8e04eSSam Leffler m_freem(sc->sc_rxpending); 401568e8e04eSSam Leffler sc->sc_rxpending = NULL; 401668e8e04eSSam Leffler } 4017c42a7b7eSSam Leffler /* 40187b0c77ecSSam Leffler * When a tap is present pass error frames 40197b0c77ecSSam Leffler * that have been requested. By default we 40207b0c77ecSSam Leffler * pass decrypt+mic errors but others may be 40217b0c77ecSSam Leffler * interesting (e.g. crc). 4022c42a7b7eSSam Leffler */ 4023b032f27cSSam Leffler if (bpf_peers_present(ifp->if_bpf) && 402465f9edeeSSam Leffler (rs->rs_status & sc->sc_monpass)) { 40257b0c77ecSSam Leffler bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, 40267b0c77ecSSam Leffler BUS_DMASYNC_POSTREAD); 40277b0c77ecSSam Leffler /* NB: bpf needs the mbuf length setup */ 402865f9edeeSSam Leffler len = rs->rs_datalen; 40297b0c77ecSSam Leffler m->m_pkthdr.len = m->m_len = len; 4030b032f27cSSam Leffler (void) ath_rx_tap(ifp, m, rs, tsf, nf); 40317b0c77ecSSam Leffler } 40327b0c77ecSSam Leffler /* XXX pass MIC errors up for s/w reclaculation */ 40335591b213SSam Leffler goto rx_next; 40345591b213SSam Leffler } 4035c42a7b7eSSam Leffler rx_accept: 4036c42a7b7eSSam Leffler /* 4037c42a7b7eSSam Leffler * Sync and unmap the frame. At this point we're 4038c42a7b7eSSam Leffler * committed to passing the mbuf somewhere so clear 4039c66c48cbSSam Leffler * bf_m; this means a new mbuf must be allocated 4040c42a7b7eSSam Leffler * when the rx descriptor is setup again to receive 4041c42a7b7eSSam Leffler * another frame. 4042c42a7b7eSSam Leffler */ 40435591b213SSam Leffler bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, 40445591b213SSam Leffler BUS_DMASYNC_POSTREAD); 40455591b213SSam Leffler bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 40465591b213SSam Leffler bf->bf_m = NULL; 4047c42a7b7eSSam Leffler 404865f9edeeSSam Leffler len = rs->rs_datalen; 404968e8e04eSSam Leffler m->m_len = len; 405068e8e04eSSam Leffler 405168e8e04eSSam Leffler if (rs->rs_more) { 405268e8e04eSSam Leffler /* 405368e8e04eSSam Leffler * Frame spans multiple descriptors; save 405468e8e04eSSam Leffler * it for the next completed descriptor, it 405568e8e04eSSam Leffler * will be used to construct a jumbogram. 405668e8e04eSSam Leffler */ 405768e8e04eSSam Leffler if (sc->sc_rxpending != NULL) { 405868e8e04eSSam Leffler /* NB: max frame size is currently 2 clusters */ 405968e8e04eSSam Leffler sc->sc_stats.ast_rx_toobig++; 406068e8e04eSSam Leffler m_freem(sc->sc_rxpending); 406168e8e04eSSam Leffler } 406268e8e04eSSam Leffler m->m_pkthdr.rcvif = ifp; 406368e8e04eSSam Leffler m->m_pkthdr.len = len; 406468e8e04eSSam Leffler sc->sc_rxpending = m; 406568e8e04eSSam Leffler goto rx_next; 406668e8e04eSSam Leffler } else if (sc->sc_rxpending != NULL) { 406768e8e04eSSam Leffler /* 406868e8e04eSSam Leffler * This is the second part of a jumbogram, 406968e8e04eSSam Leffler * chain it to the first mbuf, adjust the 407068e8e04eSSam Leffler * frame length, and clear the rxpending state. 407168e8e04eSSam Leffler */ 407268e8e04eSSam Leffler sc->sc_rxpending->m_next = m; 407368e8e04eSSam Leffler sc->sc_rxpending->m_pkthdr.len += len; 407468e8e04eSSam Leffler m = sc->sc_rxpending; 407568e8e04eSSam Leffler sc->sc_rxpending = NULL; 407668e8e04eSSam Leffler } else { 407768e8e04eSSam Leffler /* 407868e8e04eSSam Leffler * Normal single-descriptor receive; setup 407968e8e04eSSam Leffler * the rcvif and packet length. 408068e8e04eSSam Leffler */ 408168e8e04eSSam Leffler m->m_pkthdr.rcvif = ifp; 408268e8e04eSSam Leffler m->m_pkthdr.len = len; 408368e8e04eSSam Leffler } 408473454c73SSam Leffler 4085b032f27cSSam Leffler ifp->if_ipackets++; 408665f9edeeSSam Leffler sc->sc_stats.ast_ant_rx[rs->rs_antenna]++; 4087c42a7b7eSSam Leffler 4088b032f27cSSam Leffler if (bpf_peers_present(ifp->if_bpf) && 4089b032f27cSSam Leffler !ath_rx_tap(ifp, m, rs, tsf, nf)) { 40907b0c77ecSSam Leffler m_freem(m); /* XXX reclaim */ 4091c42a7b7eSSam Leffler goto rx_next; 4092c42a7b7eSSam Leffler } 40930a915fadSSam Leffler 40945591b213SSam Leffler /* 4095c42a7b7eSSam Leffler * From this point on we assume the frame is at least 4096c42a7b7eSSam Leffler * as large as ieee80211_frame_min; verify that. 40975591b213SSam Leffler */ 4098c42a7b7eSSam Leffler if (len < IEEE80211_MIN_LEN) { 4099c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_RECV, "%s: short packet %d\n", 4100c42a7b7eSSam Leffler __func__, len); 4101c42a7b7eSSam Leffler sc->sc_stats.ast_rx_tooshort++; 4102c42a7b7eSSam Leffler m_freem(m); 4103c42a7b7eSSam Leffler goto rx_next; 41045591b213SSam Leffler } 41050a915fadSSam Leffler 4106c42a7b7eSSam Leffler if (IFF_DUMPPKTS(sc, ATH_DEBUG_RECV)) { 410768e8e04eSSam Leffler ieee80211_dump_pkt(ic, mtod(m, caddr_t), len, 410865f9edeeSSam Leffler sc->sc_hwmap[rs->rs_rate].ieeerate, 410965f9edeeSSam Leffler rs->rs_rssi); 4110c42a7b7eSSam Leffler } 4111c42a7b7eSSam Leffler 4112c42a7b7eSSam Leffler m_adj(m, -IEEE80211_CRC_LEN); 4113de5af704SSam Leffler 4114de5af704SSam Leffler /* 4115c42a7b7eSSam Leffler * Locate the node for sender, track state, and then 4116c42a7b7eSSam Leffler * pass the (referenced) node up to the 802.11 layer 4117c42a7b7eSSam Leffler * for its use. 4118c42a7b7eSSam Leffler */ 4119c1225b52SSam Leffler ni = ieee80211_find_rxnode_withkey(ic, 4120c1225b52SSam Leffler mtod(m, const struct ieee80211_frame_min *), 412165f9edeeSSam Leffler rs->rs_keyix == HAL_RXKEYIX_INVALID ? 412265f9edeeSSam Leffler IEEE80211_KEYIX_NONE : rs->rs_keyix); 4123b032f27cSSam Leffler if (ni != NULL) { 4124b032f27cSSam Leffler /* 4125b032f27cSSam Leffler * Sending station is known, dispatch directly. 4126b032f27cSSam Leffler */ 4127b032f27cSSam Leffler type = ieee80211_input(ni, m, 4128b032f27cSSam Leffler rs->rs_rssi, nf, rs->rs_tstamp); 4129b032f27cSSam Leffler ieee80211_free_node(ni); 4130b032f27cSSam Leffler /* 4131b032f27cSSam Leffler * Arrange to update the last rx timestamp only for 4132b032f27cSSam Leffler * frames from our ap when operating in station mode. 4133b032f27cSSam Leffler * This assumes the rx key is always setup when 4134b032f27cSSam Leffler * associated. 4135b032f27cSSam Leffler */ 4136b032f27cSSam Leffler if (ic->ic_opmode == IEEE80211_M_STA && 4137b032f27cSSam Leffler rs->rs_keyix != HAL_RXKEYIX_INVALID) 4138b032f27cSSam Leffler ngood++; 4139b032f27cSSam Leffler } else { 4140b032f27cSSam Leffler type = ieee80211_input_all(ic, m, 4141b032f27cSSam Leffler rs->rs_rssi, nf, rs->rs_tstamp); 4142b032f27cSSam Leffler } 4143c42a7b7eSSam Leffler /* 4144c42a7b7eSSam Leffler * Track rx rssi and do any rx antenna management. 4145de5af704SSam Leffler */ 414665f9edeeSSam Leffler ATH_RSSI_LPF(sc->sc_halstats.ns_avgrssi, rs->rs_rssi); 4147c42a7b7eSSam Leffler if (sc->sc_diversity) { 4148c42a7b7eSSam Leffler /* 4149c42a7b7eSSam Leffler * When using fast diversity, change the default rx 4150c42a7b7eSSam Leffler * antenna if diversity chooses the other antenna 3 4151c42a7b7eSSam Leffler * times in a row. 4152c42a7b7eSSam Leffler */ 415365f9edeeSSam Leffler if (sc->sc_defant != rs->rs_antenna) { 4154c42a7b7eSSam Leffler if (++sc->sc_rxotherant >= 3) 415565f9edeeSSam Leffler ath_setdefantenna(sc, rs->rs_antenna); 4156c42a7b7eSSam Leffler } else 4157c42a7b7eSSam Leffler sc->sc_rxotherant = 0; 4158c42a7b7eSSam Leffler } 41593e50ec2cSSam Leffler if (sc->sc_softled) { 41603e50ec2cSSam Leffler /* 41613e50ec2cSSam Leffler * Blink for any data frame. Otherwise do a 41623e50ec2cSSam Leffler * heartbeat-style blink when idle. The latter 41633e50ec2cSSam Leffler * is mainly for station mode where we depend on 41643e50ec2cSSam Leffler * periodic beacon frames to trigger the poll event. 41653e50ec2cSSam Leffler */ 416631640eb7SSam Leffler if (type == IEEE80211_FC0_TYPE_DATA) { 416765f9edeeSSam Leffler sc->sc_rxrate = rs->rs_rate; 41683e50ec2cSSam Leffler ath_led_event(sc, ATH_LED_RX); 41693e50ec2cSSam Leffler } else if (ticks - sc->sc_ledevent >= sc->sc_ledidle) 41703e50ec2cSSam Leffler ath_led_event(sc, ATH_LED_POLL); 41713e50ec2cSSam Leffler } 41725591b213SSam Leffler rx_next: 4173c42a7b7eSSam Leffler STAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list); 41745591b213SSam Leffler } while (ath_rxbuf_init(sc, bf) == 0); 41755591b213SSam Leffler 4176c42a7b7eSSam Leffler /* rx signal state monitoring */ 4177bd5a9920SSam Leffler ath_hal_rxmonitor(ah, &sc->sc_halstats, &sc->sc_curchan); 4178d7736e13SSam Leffler if (ngood) 4179d7736e13SSam Leffler sc->sc_lastrx = tsf; 4180b5f4adb3SSam Leffler 4181cd196bb2SSam Leffler if ((ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0 && 4182cd196bb2SSam Leffler !IFQ_IS_EMPTY(&ifp->if_snd)) 4183cd196bb2SSam Leffler ath_start(ifp); 4184cd196bb2SSam Leffler 41858cec0ab9SSam Leffler #undef PA2DESC 41865591b213SSam Leffler } 41875591b213SSam Leffler 4188622b3fd2SSam Leffler static void 4189622b3fd2SSam Leffler ath_txq_init(struct ath_softc *sc, struct ath_txq *txq, int qnum) 4190622b3fd2SSam Leffler { 4191622b3fd2SSam Leffler txq->axq_qnum = qnum; 4192622b3fd2SSam Leffler txq->axq_depth = 0; 4193622b3fd2SSam Leffler txq->axq_intrcnt = 0; 4194622b3fd2SSam Leffler txq->axq_link = NULL; 4195622b3fd2SSam Leffler STAILQ_INIT(&txq->axq_q); 4196622b3fd2SSam Leffler ATH_TXQ_LOCK_INIT(sc, txq); 419768e8e04eSSam Leffler TAILQ_INIT(&txq->axq_stageq); 419868e8e04eSSam Leffler txq->axq_curage = 0; 4199622b3fd2SSam Leffler } 4200622b3fd2SSam Leffler 42015591b213SSam Leffler /* 4202c42a7b7eSSam Leffler * Setup a h/w transmit queue. 42035591b213SSam Leffler */ 4204c42a7b7eSSam Leffler static struct ath_txq * 4205c42a7b7eSSam Leffler ath_txq_setup(struct ath_softc *sc, int qtype, int subtype) 4206c42a7b7eSSam Leffler { 4207c42a7b7eSSam Leffler #define N(a) (sizeof(a)/sizeof(a[0])) 4208c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 4209c42a7b7eSSam Leffler HAL_TXQ_INFO qi; 4210c42a7b7eSSam Leffler int qnum; 4211c42a7b7eSSam Leffler 4212c42a7b7eSSam Leffler memset(&qi, 0, sizeof(qi)); 4213c42a7b7eSSam Leffler qi.tqi_subtype = subtype; 4214c42a7b7eSSam Leffler qi.tqi_aifs = HAL_TXQ_USEDEFAULT; 4215c42a7b7eSSam Leffler qi.tqi_cwmin = HAL_TXQ_USEDEFAULT; 4216c42a7b7eSSam Leffler qi.tqi_cwmax = HAL_TXQ_USEDEFAULT; 4217c42a7b7eSSam Leffler /* 4218c42a7b7eSSam Leffler * Enable interrupts only for EOL and DESC conditions. 4219c42a7b7eSSam Leffler * We mark tx descriptors to receive a DESC interrupt 4220c42a7b7eSSam Leffler * when a tx queue gets deep; otherwise waiting for the 4221c42a7b7eSSam Leffler * EOL to reap descriptors. Note that this is done to 4222c42a7b7eSSam Leffler * reduce interrupt load and this only defers reaping 4223c42a7b7eSSam Leffler * descriptors, never transmitting frames. Aside from 4224c42a7b7eSSam Leffler * reducing interrupts this also permits more concurrency. 4225c42a7b7eSSam Leffler * The only potential downside is if the tx queue backs 4226c42a7b7eSSam Leffler * up in which case the top half of the kernel may backup 4227c42a7b7eSSam Leffler * due to a lack of tx descriptors. 4228c42a7b7eSSam Leffler */ 4229bd5a9920SSam Leffler qi.tqi_qflags = HAL_TXQ_TXEOLINT_ENABLE | HAL_TXQ_TXDESCINT_ENABLE; 4230c42a7b7eSSam Leffler qnum = ath_hal_setuptxqueue(ah, qtype, &qi); 4231c42a7b7eSSam Leffler if (qnum == -1) { 4232c42a7b7eSSam Leffler /* 4233c42a7b7eSSam Leffler * NB: don't print a message, this happens 4234a614e076SSam Leffler * normally on parts with too few tx queues 4235c42a7b7eSSam Leffler */ 4236c42a7b7eSSam Leffler return NULL; 4237c42a7b7eSSam Leffler } 4238c42a7b7eSSam Leffler if (qnum >= N(sc->sc_txq)) { 42396891c875SPeter Wemm device_printf(sc->sc_dev, 42406891c875SPeter Wemm "hal qnum %u out of range, max %zu!\n", 4241c42a7b7eSSam Leffler qnum, N(sc->sc_txq)); 4242c42a7b7eSSam Leffler ath_hal_releasetxqueue(ah, qnum); 4243c42a7b7eSSam Leffler return NULL; 4244c42a7b7eSSam Leffler } 4245c42a7b7eSSam Leffler if (!ATH_TXQ_SETUP(sc, qnum)) { 4246622b3fd2SSam Leffler ath_txq_init(sc, &sc->sc_txq[qnum], qnum); 4247c42a7b7eSSam Leffler sc->sc_txqsetup |= 1<<qnum; 4248c42a7b7eSSam Leffler } 4249c42a7b7eSSam Leffler return &sc->sc_txq[qnum]; 4250c42a7b7eSSam Leffler #undef N 4251c42a7b7eSSam Leffler } 4252c42a7b7eSSam Leffler 4253c42a7b7eSSam Leffler /* 4254c42a7b7eSSam Leffler * Setup a hardware data transmit queue for the specified 4255c42a7b7eSSam Leffler * access control. The hal may not support all requested 4256c42a7b7eSSam Leffler * queues in which case it will return a reference to a 4257c42a7b7eSSam Leffler * previously setup queue. We record the mapping from ac's 4258c42a7b7eSSam Leffler * to h/w queues for use by ath_tx_start and also track 4259c42a7b7eSSam Leffler * the set of h/w queues being used to optimize work in the 4260c42a7b7eSSam Leffler * transmit interrupt handler and related routines. 4261c42a7b7eSSam Leffler */ 4262c42a7b7eSSam Leffler static int 4263c42a7b7eSSam Leffler ath_tx_setup(struct ath_softc *sc, int ac, int haltype) 4264c42a7b7eSSam Leffler { 4265c42a7b7eSSam Leffler #define N(a) (sizeof(a)/sizeof(a[0])) 4266c42a7b7eSSam Leffler struct ath_txq *txq; 4267c42a7b7eSSam Leffler 4268c42a7b7eSSam Leffler if (ac >= N(sc->sc_ac2q)) { 42696891c875SPeter Wemm device_printf(sc->sc_dev, "AC %u out of range, max %zu!\n", 4270c42a7b7eSSam Leffler ac, N(sc->sc_ac2q)); 4271c42a7b7eSSam Leffler return 0; 4272c42a7b7eSSam Leffler } 4273c42a7b7eSSam Leffler txq = ath_txq_setup(sc, HAL_TX_QUEUE_DATA, haltype); 4274c42a7b7eSSam Leffler if (txq != NULL) { 4275c42a7b7eSSam Leffler sc->sc_ac2q[ac] = txq; 4276c42a7b7eSSam Leffler return 1; 4277c42a7b7eSSam Leffler } else 4278c42a7b7eSSam Leffler return 0; 4279c42a7b7eSSam Leffler #undef N 4280c42a7b7eSSam Leffler } 4281c42a7b7eSSam Leffler 4282c42a7b7eSSam Leffler /* 4283c42a7b7eSSam Leffler * Update WME parameters for a transmit queue. 4284c42a7b7eSSam Leffler */ 4285c42a7b7eSSam Leffler static int 4286c42a7b7eSSam Leffler ath_txq_update(struct ath_softc *sc, int ac) 4287c42a7b7eSSam Leffler { 4288c42a7b7eSSam Leffler #define ATH_EXPONENT_TO_VALUE(v) ((1<<v)-1) 4289c42a7b7eSSam Leffler #define ATH_TXOP_TO_US(v) (v<<5) 4290b032f27cSSam Leffler struct ifnet *ifp = sc->sc_ifp; 4291b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 4292c42a7b7eSSam Leffler struct ath_txq *txq = sc->sc_ac2q[ac]; 4293c42a7b7eSSam Leffler struct wmeParams *wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[ac]; 4294c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 4295c42a7b7eSSam Leffler HAL_TXQ_INFO qi; 4296c42a7b7eSSam Leffler 4297c42a7b7eSSam Leffler ath_hal_gettxqueueprops(ah, txq->axq_qnum, &qi); 4298c42a7b7eSSam Leffler qi.tqi_aifs = wmep->wmep_aifsn; 4299c42a7b7eSSam Leffler qi.tqi_cwmin = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmin); 4300c42a7b7eSSam Leffler qi.tqi_cwmax = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmax); 4301c42a7b7eSSam Leffler qi.tqi_burstTime = ATH_TXOP_TO_US(wmep->wmep_txopLimit); 4302c42a7b7eSSam Leffler 4303c42a7b7eSSam Leffler if (!ath_hal_settxqueueprops(ah, txq->axq_qnum, &qi)) { 4304b032f27cSSam Leffler if_printf(ifp, "unable to update hardware queue " 4305c42a7b7eSSam Leffler "parameters for %s traffic!\n", 4306c42a7b7eSSam Leffler ieee80211_wme_acnames[ac]); 4307c42a7b7eSSam Leffler return 0; 4308c42a7b7eSSam Leffler } else { 4309c42a7b7eSSam Leffler ath_hal_resettxqueue(ah, txq->axq_qnum); /* push to h/w */ 4310c42a7b7eSSam Leffler return 1; 4311c42a7b7eSSam Leffler } 4312c42a7b7eSSam Leffler #undef ATH_TXOP_TO_US 4313c42a7b7eSSam Leffler #undef ATH_EXPONENT_TO_VALUE 4314c42a7b7eSSam Leffler } 4315c42a7b7eSSam Leffler 4316c42a7b7eSSam Leffler /* 4317c42a7b7eSSam Leffler * Callback from the 802.11 layer to update WME parameters. 4318c42a7b7eSSam Leffler */ 4319c42a7b7eSSam Leffler static int 4320c42a7b7eSSam Leffler ath_wme_update(struct ieee80211com *ic) 4321c42a7b7eSSam Leffler { 4322c42a7b7eSSam Leffler struct ath_softc *sc = ic->ic_ifp->if_softc; 4323c42a7b7eSSam Leffler 4324c42a7b7eSSam Leffler return !ath_txq_update(sc, WME_AC_BE) || 4325c42a7b7eSSam Leffler !ath_txq_update(sc, WME_AC_BK) || 4326c42a7b7eSSam Leffler !ath_txq_update(sc, WME_AC_VI) || 4327c42a7b7eSSam Leffler !ath_txq_update(sc, WME_AC_VO) ? EIO : 0; 4328c42a7b7eSSam Leffler } 4329c42a7b7eSSam Leffler 4330c42a7b7eSSam Leffler /* 4331c42a7b7eSSam Leffler * Reclaim resources for a setup queue. 4332c42a7b7eSSam Leffler */ 4333c42a7b7eSSam Leffler static void 4334c42a7b7eSSam Leffler ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq) 4335c42a7b7eSSam Leffler { 4336c42a7b7eSSam Leffler 4337c42a7b7eSSam Leffler ath_hal_releasetxqueue(sc->sc_ah, txq->axq_qnum); 4338c42a7b7eSSam Leffler ATH_TXQ_LOCK_DESTROY(txq); 4339c42a7b7eSSam Leffler sc->sc_txqsetup &= ~(1<<txq->axq_qnum); 4340c42a7b7eSSam Leffler } 4341c42a7b7eSSam Leffler 4342c42a7b7eSSam Leffler /* 4343c42a7b7eSSam Leffler * Reclaim all tx queue resources. 4344c42a7b7eSSam Leffler */ 4345c42a7b7eSSam Leffler static void 4346c42a7b7eSSam Leffler ath_tx_cleanup(struct ath_softc *sc) 4347c42a7b7eSSam Leffler { 4348c42a7b7eSSam Leffler int i; 4349c42a7b7eSSam Leffler 4350c42a7b7eSSam Leffler ATH_TXBUF_LOCK_DESTROY(sc); 4351c42a7b7eSSam Leffler for (i = 0; i < HAL_NUM_TX_QUEUES; i++) 4352c42a7b7eSSam Leffler if (ATH_TXQ_SETUP(sc, i)) 4353c42a7b7eSSam Leffler ath_tx_cleanupq(sc, &sc->sc_txq[i]); 4354c42a7b7eSSam Leffler } 43555591b213SSam Leffler 435699d258fdSSam Leffler /* 43578b5341deSSam Leffler * Return h/w rate index for an IEEE rate (w/o basic rate bit). 43588b5341deSSam Leffler */ 43598b5341deSSam Leffler static int 43608b5341deSSam Leffler ath_tx_findrix(const HAL_RATE_TABLE *rt, int rate) 43618b5341deSSam Leffler { 43628b5341deSSam Leffler int i; 43638b5341deSSam Leffler 43648b5341deSSam Leffler for (i = 0; i < rt->rateCount; i++) 43658b5341deSSam Leffler if ((rt->info[i].dot11Rate & IEEE80211_RATE_VAL) == rate) 43668b5341deSSam Leffler return i; 43678b5341deSSam Leffler return 0; /* NB: lowest rate */ 43688b5341deSSam Leffler } 43698b5341deSSam Leffler 437068e8e04eSSam Leffler /* 437168e8e04eSSam Leffler * Reclaim mbuf resources. For fragmented frames we 437268e8e04eSSam Leffler * need to claim each frag chained with m_nextpkt. 437368e8e04eSSam Leffler */ 437468e8e04eSSam Leffler static void 437568e8e04eSSam Leffler ath_freetx(struct mbuf *m) 437668e8e04eSSam Leffler { 437768e8e04eSSam Leffler struct mbuf *next; 437868e8e04eSSam Leffler 437968e8e04eSSam Leffler do { 438068e8e04eSSam Leffler next = m->m_nextpkt; 438168e8e04eSSam Leffler m->m_nextpkt = NULL; 438268e8e04eSSam Leffler m_freem(m); 438368e8e04eSSam Leffler } while ((m = next) != NULL); 438468e8e04eSSam Leffler } 438568e8e04eSSam Leffler 43865591b213SSam Leffler static int 4387664443d0SSam Leffler ath_tx_dmasetup(struct ath_softc *sc, struct ath_buf *bf, struct mbuf *m0) 4388664443d0SSam Leffler { 4389664443d0SSam Leffler struct mbuf *m; 4390664443d0SSam Leffler int error; 4391664443d0SSam Leffler 4392664443d0SSam Leffler /* 4393664443d0SSam Leffler * Load the DMA map so any coalescing is done. This 4394664443d0SSam Leffler * also calculates the number of descriptors we need. 4395664443d0SSam Leffler */ 4396664443d0SSam Leffler error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m0, 4397664443d0SSam Leffler bf->bf_segs, &bf->bf_nseg, 4398664443d0SSam Leffler BUS_DMA_NOWAIT); 4399664443d0SSam Leffler if (error == EFBIG) { 4400664443d0SSam Leffler /* XXX packet requires too many descriptors */ 4401664443d0SSam Leffler bf->bf_nseg = ATH_TXDESC+1; 4402664443d0SSam Leffler } else if (error != 0) { 4403664443d0SSam Leffler sc->sc_stats.ast_tx_busdma++; 440468e8e04eSSam Leffler ath_freetx(m0); 4405664443d0SSam Leffler return error; 4406664443d0SSam Leffler } 4407664443d0SSam Leffler /* 4408664443d0SSam Leffler * Discard null packets and check for packets that 4409664443d0SSam Leffler * require too many TX descriptors. We try to convert 4410664443d0SSam Leffler * the latter to a cluster. 4411664443d0SSam Leffler */ 4412664443d0SSam Leffler if (bf->bf_nseg > ATH_TXDESC) { /* too many desc's, linearize */ 4413664443d0SSam Leffler sc->sc_stats.ast_tx_linear++; 4414eeb76a18SSam Leffler m = m_collapse(m0, M_DONTWAIT, ATH_TXDESC); 4415664443d0SSam Leffler if (m == NULL) { 441668e8e04eSSam Leffler ath_freetx(m0); 4417664443d0SSam Leffler sc->sc_stats.ast_tx_nombuf++; 4418664443d0SSam Leffler return ENOMEM; 4419664443d0SSam Leffler } 4420664443d0SSam Leffler m0 = m; 4421664443d0SSam Leffler error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m0, 4422664443d0SSam Leffler bf->bf_segs, &bf->bf_nseg, 4423664443d0SSam Leffler BUS_DMA_NOWAIT); 4424664443d0SSam Leffler if (error != 0) { 4425664443d0SSam Leffler sc->sc_stats.ast_tx_busdma++; 442668e8e04eSSam Leffler ath_freetx(m0); 4427664443d0SSam Leffler return error; 4428664443d0SSam Leffler } 4429664443d0SSam Leffler KASSERT(bf->bf_nseg <= ATH_TXDESC, 4430664443d0SSam Leffler ("too many segments after defrag; nseg %u", bf->bf_nseg)); 4431664443d0SSam Leffler } else if (bf->bf_nseg == 0) { /* null packet, discard */ 4432664443d0SSam Leffler sc->sc_stats.ast_tx_nodata++; 443368e8e04eSSam Leffler ath_freetx(m0); 4434664443d0SSam Leffler return EIO; 4435664443d0SSam Leffler } 4436664443d0SSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT, "%s: m %p len %u\n", 4437664443d0SSam Leffler __func__, m0, m0->m_pkthdr.len); 4438664443d0SSam Leffler bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE); 4439664443d0SSam Leffler bf->bf_m = m0; 4440664443d0SSam Leffler 4441664443d0SSam Leffler return 0; 4442664443d0SSam Leffler } 4443664443d0SSam Leffler 4444664443d0SSam Leffler static void 4445664443d0SSam Leffler ath_tx_handoff(struct ath_softc *sc, struct ath_txq *txq, struct ath_buf *bf) 4446664443d0SSam Leffler { 4447664443d0SSam Leffler struct ath_hal *ah = sc->sc_ah; 4448664443d0SSam Leffler struct ath_desc *ds, *ds0; 4449664443d0SSam Leffler int i; 4450664443d0SSam Leffler 4451664443d0SSam Leffler /* 4452664443d0SSam Leffler * Fillin the remainder of the descriptor info. 4453664443d0SSam Leffler */ 4454664443d0SSam Leffler ds0 = ds = bf->bf_desc; 4455664443d0SSam Leffler for (i = 0; i < bf->bf_nseg; i++, ds++) { 4456664443d0SSam Leffler ds->ds_data = bf->bf_segs[i].ds_addr; 4457664443d0SSam Leffler if (i == bf->bf_nseg - 1) 4458664443d0SSam Leffler ds->ds_link = 0; 4459664443d0SSam Leffler else 4460664443d0SSam Leffler ds->ds_link = bf->bf_daddr + sizeof(*ds) * (i + 1); 4461664443d0SSam Leffler ath_hal_filltxdesc(ah, ds 4462664443d0SSam Leffler , bf->bf_segs[i].ds_len /* segment length */ 4463664443d0SSam Leffler , i == 0 /* first segment */ 4464664443d0SSam Leffler , i == bf->bf_nseg - 1 /* last segment */ 4465664443d0SSam Leffler , ds0 /* first descriptor */ 4466664443d0SSam Leffler ); 4467664443d0SSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT, 4468664443d0SSam Leffler "%s: %d: %08x %08x %08x %08x %08x %08x\n", 4469664443d0SSam Leffler __func__, i, ds->ds_link, ds->ds_data, 4470664443d0SSam Leffler ds->ds_ctl0, ds->ds_ctl1, ds->ds_hw[0], ds->ds_hw[1]); 4471664443d0SSam Leffler } 4472664443d0SSam Leffler /* 4473664443d0SSam Leffler * Insert the frame on the outbound list and pass it on 4474664443d0SSam Leffler * to the hardware. Multicast frames buffered for power 4475664443d0SSam Leffler * save stations and transmit from the CAB queue are stored 4476664443d0SSam Leffler * on a s/w only queue and loaded on to the CAB queue in 4477664443d0SSam Leffler * the SWBA handler since frames only go out on DTIM and 4478664443d0SSam Leffler * to avoid possible races. 4479664443d0SSam Leffler */ 4480664443d0SSam Leffler ATH_TXQ_LOCK(txq); 4481b032f27cSSam Leffler if (txq->axq_qnum != ATH_TXQ_SWQ) { 4482664443d0SSam Leffler ATH_TXQ_INSERT_TAIL(txq, bf, bf_list); 4483664443d0SSam Leffler if (txq->axq_link == NULL) { 4484664443d0SSam Leffler ath_hal_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr); 4485664443d0SSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT, 4486664443d0SSam Leffler "%s: TXDP[%u] = %p (%p) depth %d\n", __func__, 4487664443d0SSam Leffler txq->axq_qnum, (caddr_t)bf->bf_daddr, bf->bf_desc, 4488664443d0SSam Leffler txq->axq_depth); 4489664443d0SSam Leffler } else { 4490664443d0SSam Leffler *txq->axq_link = bf->bf_daddr; 4491664443d0SSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT, 4492664443d0SSam Leffler "%s: link[%u](%p)=%p (%p) depth %d\n", __func__, 4493664443d0SSam Leffler txq->axq_qnum, txq->axq_link, 4494664443d0SSam Leffler (caddr_t)bf->bf_daddr, bf->bf_desc, txq->axq_depth); 4495664443d0SSam Leffler } 4496664443d0SSam Leffler txq->axq_link = &bf->bf_desc[bf->bf_nseg - 1].ds_link; 4497664443d0SSam Leffler ath_hal_txstart(ah, txq->axq_qnum); 4498664443d0SSam Leffler } else { 4499b032f27cSSam Leffler if (txq->axq_link != NULL) { 4500b032f27cSSam Leffler struct ath_buf *last = ATH_TXQ_LAST(txq); 4501b032f27cSSam Leffler struct ieee80211_frame *wh; 4502b032f27cSSam Leffler 4503b032f27cSSam Leffler /* mark previous frame */ 4504b032f27cSSam Leffler wh = mtod(last->bf_m, struct ieee80211_frame *); 4505b032f27cSSam Leffler wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA; 4506b032f27cSSam Leffler bus_dmamap_sync(sc->sc_dmat, last->bf_dmamap, 4507b032f27cSSam Leffler BUS_DMASYNC_PREWRITE); 4508b032f27cSSam Leffler 4509b032f27cSSam Leffler /* link descriptor */ 4510664443d0SSam Leffler *txq->axq_link = bf->bf_daddr; 4511b032f27cSSam Leffler } 4512b032f27cSSam Leffler ATH_TXQ_INSERT_TAIL(txq, bf, bf_list); 4513664443d0SSam Leffler txq->axq_link = &bf->bf_desc[bf->bf_nseg - 1].ds_link; 4514664443d0SSam Leffler } 4515664443d0SSam Leffler ATH_TXQ_UNLOCK(txq); 4516664443d0SSam Leffler } 4517664443d0SSam Leffler 4518664443d0SSam Leffler static int 45195591b213SSam Leffler ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf, 45205591b213SSam Leffler struct mbuf *m0) 45215591b213SSam Leffler { 4522b032f27cSSam Leffler struct ieee80211vap *vap = ni->ni_vap; 4523b032f27cSSam Leffler struct ath_vap *avp = ATH_VAP(vap); 45245591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 4525fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 4526b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 4527c4c3cb46SSam Leffler const struct chanAccParams *cap = &ic->ic_wme.wme_chanParams; 452868e8e04eSSam Leffler int error, iswep, ismcast, isfrag, ismrr; 4529be613480SSam Leffler int keyix, hdrlen, pktlen, try0; 4530c42a7b7eSSam Leffler u_int8_t rix, txrate, ctsrate; 4531c42a7b7eSSam Leffler u_int8_t cix = 0xff; /* NB: silence compiler */ 4532664443d0SSam Leffler struct ath_desc *ds; 4533c42a7b7eSSam Leffler struct ath_txq *txq; 45345591b213SSam Leffler struct ieee80211_frame *wh; 4535c42a7b7eSSam Leffler u_int subtype, flags, ctsduration; 45365591b213SSam Leffler HAL_PKT_TYPE atype; 45375591b213SSam Leffler const HAL_RATE_TABLE *rt; 45385591b213SSam Leffler HAL_BOOL shortPreamble; 45395591b213SSam Leffler struct ath_node *an; 4540c4c3cb46SSam Leffler u_int pri; 45415591b213SSam Leffler 45425591b213SSam Leffler wh = mtod(m0, struct ieee80211_frame *); 45435591b213SSam Leffler iswep = wh->i_fc[1] & IEEE80211_FC1_WEP; 4544c42a7b7eSSam Leffler ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); 454568e8e04eSSam Leffler isfrag = m0->m_flags & M_FRAG; 4546c42a7b7eSSam Leffler hdrlen = ieee80211_anyhdrsize(wh); 4547c42a7b7eSSam Leffler /* 4548a614e076SSam Leffler * Packet length must not include any 4549a614e076SSam Leffler * pad bytes; deduct them here. 4550c42a7b7eSSam Leffler */ 4551c42a7b7eSSam Leffler pktlen = m0->m_pkthdr.len - (hdrlen & 3); 45525591b213SSam Leffler 45535591b213SSam Leffler if (iswep) { 4554c42a7b7eSSam Leffler const struct ieee80211_cipher *cip; 4555c42a7b7eSSam Leffler struct ieee80211_key *k; 4556c42a7b7eSSam Leffler 4557c42a7b7eSSam Leffler /* 4558c42a7b7eSSam Leffler * Construct the 802.11 header+trailer for an encrypted 4559c42a7b7eSSam Leffler * frame. The only reason this can fail is because of an 4560c42a7b7eSSam Leffler * unknown or unsupported cipher/key type. 4561c42a7b7eSSam Leffler */ 4562b032f27cSSam Leffler k = ieee80211_crypto_encap(ni, m0); 4563c42a7b7eSSam Leffler if (k == NULL) { 4564c42a7b7eSSam Leffler /* 4565c42a7b7eSSam Leffler * This can happen when the key is yanked after the 4566c42a7b7eSSam Leffler * frame was queued. Just discard the frame; the 4567c42a7b7eSSam Leffler * 802.11 layer counts failures and provides 4568c42a7b7eSSam Leffler * debugging/diagnostics. 4569c42a7b7eSSam Leffler */ 457068e8e04eSSam Leffler ath_freetx(m0); 4571c42a7b7eSSam Leffler return EIO; 45725591b213SSam Leffler } 4573c42a7b7eSSam Leffler /* 4574c42a7b7eSSam Leffler * Adjust the packet + header lengths for the crypto 4575c42a7b7eSSam Leffler * additions and calculate the h/w key index. When 4576c42a7b7eSSam Leffler * a s/w mic is done the frame will have had any mic 457768e8e04eSSam Leffler * added to it prior to entry so m0->m_pkthdr.len will 4578c42a7b7eSSam Leffler * account for it. Otherwise we need to add it to the 4579c42a7b7eSSam Leffler * packet length. 4580c42a7b7eSSam Leffler */ 4581c42a7b7eSSam Leffler cip = k->wk_cipher; 4582c42a7b7eSSam Leffler hdrlen += cip->ic_header; 4583c42a7b7eSSam Leffler pktlen += cip->ic_header + cip->ic_trailer; 458468e8e04eSSam Leffler /* NB: frags always have any TKIP MIC done in s/w */ 458568e8e04eSSam Leffler if ((k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && !isfrag) 4586c42a7b7eSSam Leffler pktlen += cip->ic_miclen; 4587c42a7b7eSSam Leffler keyix = k->wk_keyix; 4588c42a7b7eSSam Leffler 4589c42a7b7eSSam Leffler /* packet header may have moved, reset our local pointer */ 4590167ecdcaSSam Leffler wh = mtod(m0, struct ieee80211_frame *); 4591e8fd88a3SSam Leffler } else if (ni->ni_ucastkey.wk_cipher == &ieee80211_cipher_none) { 4592e8fd88a3SSam Leffler /* 4593e8fd88a3SSam Leffler * Use station key cache slot, if assigned. 4594e8fd88a3SSam Leffler */ 4595e8fd88a3SSam Leffler keyix = ni->ni_ucastkey.wk_keyix; 4596e8fd88a3SSam Leffler if (keyix == IEEE80211_KEYIX_NONE) 4597e8fd88a3SSam Leffler keyix = HAL_TXKEYIX_INVALID; 4598c42a7b7eSSam Leffler } else 4599c42a7b7eSSam Leffler keyix = HAL_TXKEYIX_INVALID; 4600c42a7b7eSSam Leffler 46015591b213SSam Leffler pktlen += IEEE80211_CRC_LEN; 46025591b213SSam Leffler 46035591b213SSam Leffler /* 46045591b213SSam Leffler * Load the DMA map so any coalescing is done. This 46055591b213SSam Leffler * also calculates the number of descriptors we need. 46065591b213SSam Leffler */ 4607664443d0SSam Leffler error = ath_tx_dmasetup(sc, bf, m0); 460805680ab6SSam Leffler if (error != 0) 460905680ab6SSam Leffler return error; 46100a915fadSSam Leffler bf->bf_node = ni; /* NB: held reference */ 4611664443d0SSam Leffler m0 = bf->bf_m; /* NB: may have changed */ 4612664443d0SSam Leffler wh = mtod(m0, struct ieee80211_frame *); 46135591b213SSam Leffler 46145591b213SSam Leffler /* setup descriptors */ 46155591b213SSam Leffler ds = bf->bf_desc; 46165591b213SSam Leffler rt = sc->sc_currates; 46175591b213SSam Leffler KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode)); 46185591b213SSam Leffler 46195591b213SSam Leffler /* 4620c42a7b7eSSam Leffler * NB: the 802.11 layer marks whether or not we should 4621c42a7b7eSSam Leffler * use short preamble based on the current mode and 4622c42a7b7eSSam Leffler * negotiated parameters. 46235591b213SSam Leffler */ 4624c42a7b7eSSam Leffler if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && 4625c42a7b7eSSam Leffler (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) { 4626c42a7b7eSSam Leffler shortPreamble = AH_TRUE; 4627c42a7b7eSSam Leffler sc->sc_stats.ast_tx_shortpre++; 4628c42a7b7eSSam Leffler } else { 4629c42a7b7eSSam Leffler shortPreamble = AH_FALSE; 4630c42a7b7eSSam Leffler } 4631c42a7b7eSSam Leffler 4632c42a7b7eSSam Leffler an = ATH_NODE(ni); 4633c42a7b7eSSam Leffler flags = HAL_TXDESC_CLRDMASK; /* XXX needed for crypto errs */ 4634be613480SSam Leffler ismrr = 0; /* default no multi-rate retry*/ 4635b032f27cSSam Leffler pri = M_WME_GETAC(m0); /* honor classification */ 4636c42a7b7eSSam Leffler /* 4637c42a7b7eSSam Leffler * Calculate Atheros packet type from IEEE80211 packet header, 4638c42a7b7eSSam Leffler * setup for rate calculations, and select h/w transmit queue. 4639c42a7b7eSSam Leffler */ 46405591b213SSam Leffler switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { 46415591b213SSam Leffler case IEEE80211_FC0_TYPE_MGT: 46425591b213SSam Leffler subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; 46435591b213SSam Leffler if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) 46445591b213SSam Leffler atype = HAL_PKT_TYPE_BEACON; 46455591b213SSam Leffler else if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) 46465591b213SSam Leffler atype = HAL_PKT_TYPE_PROBE_RESP; 46475591b213SSam Leffler else if (subtype == IEEE80211_FC0_SUBTYPE_ATIM) 46485591b213SSam Leffler atype = HAL_PKT_TYPE_ATIM; 4649c42a7b7eSSam Leffler else 4650c42a7b7eSSam Leffler atype = HAL_PKT_TYPE_NORMAL; /* XXX */ 4651b032f27cSSam Leffler rix = an->an_mgmtrix; 465255f63772SSam Leffler txrate = rt->info[rix].rateCode; 4653c42a7b7eSSam Leffler if (shortPreamble) 465455f63772SSam Leffler txrate |= rt->info[rix].shortPreamble; 4655be613480SSam Leffler try0 = ATH_TXMGTTRY; 4656c42a7b7eSSam Leffler flags |= HAL_TXDESC_INTREQ; /* force interrupt */ 46575591b213SSam Leffler break; 46585591b213SSam Leffler case IEEE80211_FC0_TYPE_CTL: 4659c42a7b7eSSam Leffler atype = HAL_PKT_TYPE_PSPOLL; /* stop setting of duration */ 4660b032f27cSSam Leffler rix = an->an_mgmtrix; 466155f63772SSam Leffler txrate = rt->info[rix].rateCode; 4662c42a7b7eSSam Leffler if (shortPreamble) 466355f63772SSam Leffler txrate |= rt->info[rix].shortPreamble; 4664be613480SSam Leffler try0 = ATH_TXMGTTRY; 4665c42a7b7eSSam Leffler flags |= HAL_TXDESC_INTREQ; /* force interrupt */ 4666c42a7b7eSSam Leffler break; 4667c42a7b7eSSam Leffler case IEEE80211_FC0_TYPE_DATA: 4668c42a7b7eSSam Leffler atype = HAL_PKT_TYPE_NORMAL; /* default */ 4669c42a7b7eSSam Leffler /* 46708b5341deSSam Leffler * Data frames: multicast frames go out at a fixed rate, 46718b5341deSSam Leffler * otherwise consult the rate control module for the 46728b5341deSSam Leffler * rate to use. 4673c42a7b7eSSam Leffler */ 46748b5341deSSam Leffler if (ismcast) { 4675b032f27cSSam Leffler rix = an->an_mcastrix; 46768b5341deSSam Leffler txrate = rt->info[rix].rateCode; 46778b5341deSSam Leffler if (shortPreamble) 46788b5341deSSam Leffler txrate |= rt->info[rix].shortPreamble; 46798b5341deSSam Leffler try0 = 1; 46808b5341deSSam Leffler } else { 4681c42a7b7eSSam Leffler ath_rate_findrate(sc, an, shortPreamble, pktlen, 4682c42a7b7eSSam Leffler &rix, &try0, &txrate); 46833e50ec2cSSam Leffler sc->sc_txrate = txrate; /* for LED blinking */ 468468e8e04eSSam Leffler sc->sc_lastdatarix = rix; /* for fast frames */ 4685be613480SSam Leffler if (try0 != ATH_TXMAXTRY) 4686be613480SSam Leffler ismrr = 1; 46878b5341deSSam Leffler } 4688f9748b9dSSam Leffler if (cap->cap_wmeParams[pri].wmep_noackPolicy) 4689c42a7b7eSSam Leffler flags |= HAL_TXDESC_NOACK; 46905591b213SSam Leffler break; 46915591b213SSam Leffler default: 4692c42a7b7eSSam Leffler if_printf(ifp, "bogus frame type 0x%x (%s)\n", 4693c42a7b7eSSam Leffler wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK, __func__); 4694c42a7b7eSSam Leffler /* XXX statistic */ 469568e8e04eSSam Leffler ath_freetx(m0); 46965591b213SSam Leffler return EIO; 46975591b213SSam Leffler } 4698c4c3cb46SSam Leffler txq = sc->sc_ac2q[pri]; 4699c42a7b7eSSam Leffler 47005591b213SSam Leffler /* 4701c42a7b7eSSam Leffler * When servicing one or more stations in power-save mode 4702622b3fd2SSam Leffler * (or) if there is some mcast data waiting on the mcast 4703622b3fd2SSam Leffler * queue (to prevent out of order delivery) multicast 4704622b3fd2SSam Leffler * frames must be buffered until after the beacon. 47055591b213SSam Leffler */ 4706b032f27cSSam Leffler if (ismcast && (vap->iv_ps_sta || avp->av_mcastq.axq_depth)) 4707b032f27cSSam Leffler txq = &avp->av_mcastq; 47085591b213SSam Leffler 47095591b213SSam Leffler /* 47105591b213SSam Leffler * Calculate miscellaneous flags. 47115591b213SSam Leffler */ 4712c42a7b7eSSam Leffler if (ismcast) { 47135591b213SSam Leffler flags |= HAL_TXDESC_NOACK; /* no ack on broad/multicast */ 4714b032f27cSSam Leffler } else if (pktlen > vap->iv_rtsthreshold && 471568e8e04eSSam Leffler (ni->ni_ath_flags & IEEE80211_NODE_FF) == 0) { 47165591b213SSam Leffler flags |= HAL_TXDESC_RTSENA; /* RTS based on frame length */ 4717c42a7b7eSSam Leffler cix = rt->info[rix].controlRate; 47185591b213SSam Leffler sc->sc_stats.ast_tx_rts++; 47195591b213SSam Leffler } 4720f9748b9dSSam Leffler if (flags & HAL_TXDESC_NOACK) /* NB: avoid double counting */ 4721f9748b9dSSam Leffler sc->sc_stats.ast_tx_noack++; 47225591b213SSam Leffler 47235591b213SSam Leffler /* 4724c42a7b7eSSam Leffler * If 802.11g protection is enabled, determine whether 4725c42a7b7eSSam Leffler * to use RTS/CTS or just CTS. Note that this is only 4726c42a7b7eSSam Leffler * done for OFDM unicast frames. 4727c42a7b7eSSam Leffler */ 4728c42a7b7eSSam Leffler if ((ic->ic_flags & IEEE80211_F_USEPROT) && 4729c42a7b7eSSam Leffler rt->info[rix].phy == IEEE80211_T_OFDM && 4730c42a7b7eSSam Leffler (flags & HAL_TXDESC_NOACK) == 0) { 4731c42a7b7eSSam Leffler /* XXX fragments must use CCK rates w/ protection */ 4732c42a7b7eSSam Leffler if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) 4733c42a7b7eSSam Leffler flags |= HAL_TXDESC_RTSENA; 4734c42a7b7eSSam Leffler else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) 4735c42a7b7eSSam Leffler flags |= HAL_TXDESC_CTSENA; 473668e8e04eSSam Leffler if (isfrag) { 473768e8e04eSSam Leffler /* 473868e8e04eSSam Leffler * For frags it would be desirable to use the 473968e8e04eSSam Leffler * highest CCK rate for RTS/CTS. But stations 474068e8e04eSSam Leffler * farther away may detect it at a lower CCK rate 474168e8e04eSSam Leffler * so use the configured protection rate instead 474268e8e04eSSam Leffler * (for now). 474368e8e04eSSam Leffler */ 474468e8e04eSSam Leffler cix = rt->info[sc->sc_protrix].controlRate; 474568e8e04eSSam Leffler } else 4746c42a7b7eSSam Leffler cix = rt->info[sc->sc_protrix].controlRate; 4747c42a7b7eSSam Leffler sc->sc_stats.ast_tx_protect++; 4748c42a7b7eSSam Leffler } 4749c42a7b7eSSam Leffler 4750c42a7b7eSSam Leffler /* 4751f6aa038bSSam Leffler * Calculate duration. This logically belongs in the 802.11 4752f6aa038bSSam Leffler * layer but it lacks sufficient information to calculate it. 4753f6aa038bSSam Leffler */ 4754f6aa038bSSam Leffler if ((flags & HAL_TXDESC_NOACK) == 0 && 4755f6aa038bSSam Leffler (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_CTL) { 4756f6aa038bSSam Leffler u_int16_t dur; 4757c42a7b7eSSam Leffler if (shortPreamble) 4758c42a7b7eSSam Leffler dur = rt->info[rix].spAckDuration; 4759c42a7b7eSSam Leffler else 4760c42a7b7eSSam Leffler dur = rt->info[rix].lpAckDuration; 476168e8e04eSSam Leffler if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) { 476268e8e04eSSam Leffler dur += dur; /* additional SIFS+ACK */ 476368e8e04eSSam Leffler KASSERT(m0->m_nextpkt != NULL, ("no fragment")); 476468e8e04eSSam Leffler /* 476568e8e04eSSam Leffler * Include the size of next fragment so NAV is 476668e8e04eSSam Leffler * updated properly. The last fragment uses only 476768e8e04eSSam Leffler * the ACK duration 476868e8e04eSSam Leffler */ 476968e8e04eSSam Leffler dur += ath_hal_computetxtime(ah, rt, 477068e8e04eSSam Leffler m0->m_nextpkt->m_pkthdr.len, 477168e8e04eSSam Leffler rix, shortPreamble); 477268e8e04eSSam Leffler } 477368e8e04eSSam Leffler if (isfrag) { 477468e8e04eSSam Leffler /* 477568e8e04eSSam Leffler * Force hardware to use computed duration for next 477668e8e04eSSam Leffler * fragment by disabling multi-rate retry which updates 477768e8e04eSSam Leffler * duration based on the multi-rate duration table. 477868e8e04eSSam Leffler */ 477968e8e04eSSam Leffler ismrr = 0; 478068e8e04eSSam Leffler try0 = ATH_TXMGTTRY; /* XXX? */ 478168e8e04eSSam Leffler } 4782c42a7b7eSSam Leffler *(u_int16_t *)wh->i_dur = htole16(dur); 4783f6aa038bSSam Leffler } 4784f6aa038bSSam Leffler 4785f6aa038bSSam Leffler /* 47865591b213SSam Leffler * Calculate RTS/CTS rate and duration if needed. 47875591b213SSam Leffler */ 47885591b213SSam Leffler ctsduration = 0; 47895591b213SSam Leffler if (flags & (HAL_TXDESC_RTSENA|HAL_TXDESC_CTSENA)) { 47905591b213SSam Leffler /* 47915591b213SSam Leffler * CTS transmit rate is derived from the transmit rate 47925591b213SSam Leffler * by looking in the h/w rate table. We must also factor 47935591b213SSam Leffler * in whether or not a short preamble is to be used. 47945591b213SSam Leffler */ 4795c42a7b7eSSam Leffler /* NB: cix is set above where RTS/CTS is enabled */ 4796c42a7b7eSSam Leffler KASSERT(cix != 0xff, ("cix not setup")); 47975591b213SSam Leffler ctsrate = rt->info[cix].rateCode; 47985591b213SSam Leffler /* 4799c42a7b7eSSam Leffler * Compute the transmit duration based on the frame 4800c42a7b7eSSam Leffler * size and the size of an ACK frame. We call into the 4801c42a7b7eSSam Leffler * HAL to do the computation since it depends on the 4802c42a7b7eSSam Leffler * characteristics of the actual PHY being used. 4803c42a7b7eSSam Leffler * 4804c42a7b7eSSam Leffler * NB: CTS is assumed the same size as an ACK so we can 4805c42a7b7eSSam Leffler * use the precalculated ACK durations. 48065591b213SSam Leffler */ 4807c42a7b7eSSam Leffler if (shortPreamble) { 4808c42a7b7eSSam Leffler ctsrate |= rt->info[cix].shortPreamble; 4809c42a7b7eSSam Leffler if (flags & HAL_TXDESC_RTSENA) /* SIFS + CTS */ 4810c42a7b7eSSam Leffler ctsduration += rt->info[cix].spAckDuration; 48115591b213SSam Leffler ctsduration += ath_hal_computetxtime(ah, 4812c42a7b7eSSam Leffler rt, pktlen, rix, AH_TRUE); 4813c42a7b7eSSam Leffler if ((flags & HAL_TXDESC_NOACK) == 0) /* SIFS + ACK */ 48146ee571b2SSam Leffler ctsduration += rt->info[rix].spAckDuration; 4815c42a7b7eSSam Leffler } else { 4816c42a7b7eSSam Leffler if (flags & HAL_TXDESC_RTSENA) /* SIFS + CTS */ 4817c42a7b7eSSam Leffler ctsduration += rt->info[cix].lpAckDuration; 4818c42a7b7eSSam Leffler ctsduration += ath_hal_computetxtime(ah, 4819c42a7b7eSSam Leffler rt, pktlen, rix, AH_FALSE); 4820c42a7b7eSSam Leffler if ((flags & HAL_TXDESC_NOACK) == 0) /* SIFS + ACK */ 48216ee571b2SSam Leffler ctsduration += rt->info[rix].lpAckDuration; 48225591b213SSam Leffler } 4823c42a7b7eSSam Leffler /* 4824c42a7b7eSSam Leffler * Must disable multi-rate retry when using RTS/CTS. 4825c42a7b7eSSam Leffler */ 4826be613480SSam Leffler ismrr = 0; 4827be613480SSam Leffler try0 = ATH_TXMGTTRY; /* XXX */ 48285591b213SSam Leffler } else 48295591b213SSam Leffler ctsrate = 0; 48305591b213SSam Leffler 483168e8e04eSSam Leffler /* 483268e8e04eSSam Leffler * At this point we are committed to sending the frame 483368e8e04eSSam Leffler * and we don't need to look at m_nextpkt; clear it in 483468e8e04eSSam Leffler * case this frame is part of frag chain. 483568e8e04eSSam Leffler */ 483668e8e04eSSam Leffler m0->m_nextpkt = NULL; 483768e8e04eSSam Leffler 4838c42a7b7eSSam Leffler if (IFF_DUMPPKTS(sc, ATH_DEBUG_XMIT)) 483968e8e04eSSam Leffler ieee80211_dump_pkt(ic, mtod(m0, caddr_t), m0->m_len, 48403e50ec2cSSam Leffler sc->sc_hwmap[txrate].ieeerate, -1); 48415591b213SSam Leffler 4842b032f27cSSam Leffler if (bpf_peers_present(ifp->if_bpf)) { 48437b0c77ecSSam Leffler u_int64_t tsf = ath_hal_gettsf64(ah); 48447b0c77ecSSam Leffler 48457b0c77ecSSam Leffler sc->sc_tx_th.wt_tsf = htole64(tsf); 4846d3be6f5bSSam Leffler sc->sc_tx_th.wt_flags = sc->sc_hwmap[txrate].txflags; 4847eb2cdcb1SSam Leffler if (iswep) 4848eb2cdcb1SSam Leffler sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_WEP; 484968e8e04eSSam Leffler if (isfrag) 485068e8e04eSSam Leffler sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_FRAG; 48513e50ec2cSSam Leffler sc->sc_tx_th.wt_rate = sc->sc_hwmap[txrate].ieeerate; 4852c42a7b7eSSam Leffler sc->sc_tx_th.wt_txpower = ni->ni_txpower; 4853c42a7b7eSSam Leffler sc->sc_tx_th.wt_antenna = sc->sc_txantenna; 4854eb2cdcb1SSam Leffler 4855b032f27cSSam Leffler bpf_mtap2(ifp->if_bpf, &sc->sc_tx_th, sc->sc_tx_th_len, m0); 4856eb2cdcb1SSam Leffler } 4857eb2cdcb1SSam Leffler 48585591b213SSam Leffler /* 4859c42a7b7eSSam Leffler * Determine if a tx interrupt should be generated for 4860c42a7b7eSSam Leffler * this descriptor. We take a tx interrupt to reap 4861c42a7b7eSSam Leffler * descriptors when the h/w hits an EOL condition or 4862c42a7b7eSSam Leffler * when the descriptor is specifically marked to generate 4863c42a7b7eSSam Leffler * an interrupt. We periodically mark descriptors in this 4864c42a7b7eSSam Leffler * way to insure timely replenishing of the supply needed 4865c42a7b7eSSam Leffler * for sending frames. Defering interrupts reduces system 4866c42a7b7eSSam Leffler * load and potentially allows more concurrent work to be 4867c42a7b7eSSam Leffler * done but if done to aggressively can cause senders to 4868c42a7b7eSSam Leffler * backup. 4869c42a7b7eSSam Leffler * 4870c42a7b7eSSam Leffler * NB: use >= to deal with sc_txintrperiod changing 4871c42a7b7eSSam Leffler * dynamically through sysctl. 4872c42a7b7eSSam Leffler */ 4873c42a7b7eSSam Leffler if (flags & HAL_TXDESC_INTREQ) { 4874c42a7b7eSSam Leffler txq->axq_intrcnt = 0; 4875c42a7b7eSSam Leffler } else if (++txq->axq_intrcnt >= sc->sc_txintrperiod) { 4876c42a7b7eSSam Leffler flags |= HAL_TXDESC_INTREQ; 4877c42a7b7eSSam Leffler txq->axq_intrcnt = 0; 4878c42a7b7eSSam Leffler } 4879c42a7b7eSSam Leffler 4880c42a7b7eSSam Leffler /* 48815591b213SSam Leffler * Formulate first tx descriptor with tx controls. 48825591b213SSam Leffler */ 48835591b213SSam Leffler /* XXX check return value? */ 48845591b213SSam Leffler ath_hal_setuptxdesc(ah, ds 48855591b213SSam Leffler , pktlen /* packet length */ 48865591b213SSam Leffler , hdrlen /* header length */ 48875591b213SSam Leffler , atype /* Atheros packet type */ 4888c42a7b7eSSam Leffler , ni->ni_txpower /* txpower */ 4889c42a7b7eSSam Leffler , txrate, try0 /* series 0 rate/tries */ 4890c42a7b7eSSam Leffler , keyix /* key cache index */ 4891c42a7b7eSSam Leffler , sc->sc_txantenna /* antenna mode */ 48925591b213SSam Leffler , flags /* flags */ 48935591b213SSam Leffler , ctsrate /* rts/cts rate */ 48945591b213SSam Leffler , ctsduration /* rts/cts duration */ 48955591b213SSam Leffler ); 4896ebecf802SSam Leffler bf->bf_flags = flags; 4897c42a7b7eSSam Leffler /* 4898c42a7b7eSSam Leffler * Setup the multi-rate retry state only when we're 4899c42a7b7eSSam Leffler * going to use it. This assumes ath_hal_setuptxdesc 4900c42a7b7eSSam Leffler * initializes the descriptors (so we don't have to) 4901c42a7b7eSSam Leffler * when the hardware supports multi-rate retry and 4902c42a7b7eSSam Leffler * we don't use it. 4903c42a7b7eSSam Leffler */ 4904be613480SSam Leffler if (ismrr) 4905c42a7b7eSSam Leffler ath_rate_setupxtxdesc(sc, an, ds, shortPreamble, rix); 4906c42a7b7eSSam Leffler 4907664443d0SSam Leffler ath_tx_handoff(sc, txq, bf); 49085591b213SSam Leffler return 0; 49095591b213SSam Leffler } 49105591b213SSam Leffler 4911c42a7b7eSSam Leffler /* 4912c42a7b7eSSam Leffler * Process completed xmit descriptors from the specified queue. 4913c42a7b7eSSam Leffler */ 4914d7736e13SSam Leffler static int 4915c42a7b7eSSam Leffler ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) 49165591b213SSam Leffler { 49175591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 4918b032f27cSSam Leffler struct ifnet *ifp = sc->sc_ifp; 4919b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 4920ebecf802SSam Leffler struct ath_buf *bf; 4921c4c3cb46SSam Leffler struct ath_desc *ds, *ds0; 492265f9edeeSSam Leffler struct ath_tx_status *ts; 49235591b213SSam Leffler struct ieee80211_node *ni; 49245591b213SSam Leffler struct ath_node *an; 4925d7736e13SSam Leffler int sr, lr, pri, nacked; 49265591b213SSam Leffler HAL_STATUS status; 49275591b213SSam Leffler 4928c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: tx queue %u head %p link %p\n", 4929c42a7b7eSSam Leffler __func__, txq->axq_qnum, 4930c42a7b7eSSam Leffler (caddr_t)(uintptr_t) ath_hal_gettxbuf(sc->sc_ah, txq->axq_qnum), 4931c42a7b7eSSam Leffler txq->axq_link); 4932d7736e13SSam Leffler nacked = 0; 49335591b213SSam Leffler for (;;) { 4934c42a7b7eSSam Leffler ATH_TXQ_LOCK(txq); 4935c42a7b7eSSam Leffler txq->axq_intrcnt = 0; /* reset periodic desc intr count */ 4936c42a7b7eSSam Leffler bf = STAILQ_FIRST(&txq->axq_q); 49375591b213SSam Leffler if (bf == NULL) { 4938c42a7b7eSSam Leffler ATH_TXQ_UNLOCK(txq); 49395591b213SSam Leffler break; 49405591b213SSam Leffler } 4941c4c3cb46SSam Leffler ds0 = &bf->bf_desc[0]; 49425591b213SSam Leffler ds = &bf->bf_desc[bf->bf_nseg - 1]; 494365f9edeeSSam Leffler ts = &bf->bf_status.ds_txstat; 494465f9edeeSSam Leffler status = ath_hal_txprocdesc(ah, ds, ts); 4945a585a9a1SSam Leffler #ifdef ATH_DEBUG 4946c42a7b7eSSam Leffler if (sc->sc_debug & ATH_DEBUG_XMIT_DESC) 49477a4c5ed9SSam Leffler ath_printtxbuf(bf, txq->axq_qnum, 0, status == HAL_OK); 49485591b213SSam Leffler #endif 49495591b213SSam Leffler if (status == HAL_EINPROGRESS) { 4950c42a7b7eSSam Leffler ATH_TXQ_UNLOCK(txq); 49515591b213SSam Leffler break; 49525591b213SSam Leffler } 4953c42a7b7eSSam Leffler ATH_TXQ_REMOVE_HEAD(txq, bf_list); 4954ebecf802SSam Leffler if (txq->axq_depth == 0) 49551539af1eSSam Leffler txq->axq_link = NULL; 4956c42a7b7eSSam Leffler ATH_TXQ_UNLOCK(txq); 49575591b213SSam Leffler 49585591b213SSam Leffler ni = bf->bf_node; 49595591b213SSam Leffler if (ni != NULL) { 4960c42a7b7eSSam Leffler an = ATH_NODE(ni); 496165f9edeeSSam Leffler if (ts->ts_status == 0) { 496265f9edeeSSam Leffler u_int8_t txant = ts->ts_antenna; 4963c42a7b7eSSam Leffler sc->sc_stats.ast_ant_tx[txant]++; 4964c42a7b7eSSam Leffler sc->sc_ant_tx[txant]++; 496565f9edeeSSam Leffler if (ts->ts_rate & HAL_TXSTAT_ALTRATE) 4966c42a7b7eSSam Leffler sc->sc_stats.ast_tx_altrate++; 496765f9edeeSSam Leffler sc->sc_stats.ast_tx_rssi = ts->ts_rssi; 4968ffa2cab6SSam Leffler ATH_RSSI_LPF(sc->sc_halstats.ns_avgtxrssi, 496965f9edeeSSam Leffler ts->ts_rssi); 4970c42a7b7eSSam Leffler pri = M_WME_GETAC(bf->bf_m); 4971c42a7b7eSSam Leffler if (pri >= WME_AC_VO) 4972c42a7b7eSSam Leffler ic->ic_wme.wme_hipri_traffic++; 4973c42a7b7eSSam Leffler ni->ni_inact = ni->ni_inact_reload; 49745591b213SSam Leffler } else { 497565f9edeeSSam Leffler if (ts->ts_status & HAL_TXERR_XRETRY) 49765591b213SSam Leffler sc->sc_stats.ast_tx_xretries++; 497765f9edeeSSam Leffler if (ts->ts_status & HAL_TXERR_FIFO) 49785591b213SSam Leffler sc->sc_stats.ast_tx_fifoerr++; 497965f9edeeSSam Leffler if (ts->ts_status & HAL_TXERR_FILT) 49805591b213SSam Leffler sc->sc_stats.ast_tx_filtered++; 498168e8e04eSSam Leffler if (bf->bf_m->m_flags & M_FF) 498268e8e04eSSam Leffler sc->sc_stats.ast_ff_txerr++; 49835591b213SSam Leffler } 498465f9edeeSSam Leffler sr = ts->ts_shortretry; 498565f9edeeSSam Leffler lr = ts->ts_longretry; 49865591b213SSam Leffler sc->sc_stats.ast_tx_shortretry += sr; 49875591b213SSam Leffler sc->sc_stats.ast_tx_longretry += lr; 4988c42a7b7eSSam Leffler /* 4989c42a7b7eSSam Leffler * Hand the descriptor to the rate control algorithm. 4990c42a7b7eSSam Leffler */ 499165f9edeeSSam Leffler if ((ts->ts_status & HAL_TXERR_FILT) == 0 && 4992ebecf802SSam Leffler (bf->bf_flags & HAL_TXDESC_NOACK) == 0) { 4993d7736e13SSam Leffler /* 4994d7736e13SSam Leffler * If frame was ack'd update the last rx time 4995d7736e13SSam Leffler * used to workaround phantom bmiss interrupts. 4996d7736e13SSam Leffler */ 499765f9edeeSSam Leffler if (ts->ts_status == 0) 4998d7736e13SSam Leffler nacked++; 499965f9edeeSSam Leffler ath_rate_tx_complete(sc, an, bf); 5000d7736e13SSam Leffler } 50010a915fadSSam Leffler /* 500268e8e04eSSam Leffler * Do any tx complete callback. Note this must 500368e8e04eSSam Leffler * be done before releasing the node reference. 500468e8e04eSSam Leffler */ 500568e8e04eSSam Leffler if (bf->bf_m->m_flags & M_TXCB) 500668e8e04eSSam Leffler ieee80211_process_callback(ni, bf->bf_m, 500768e8e04eSSam Leffler ts->ts_status); 500868e8e04eSSam Leffler /* 50090a915fadSSam Leffler * Reclaim reference to node. 50100a915fadSSam Leffler * 50110a915fadSSam Leffler * NB: the node may be reclaimed here if, for example 50120a915fadSSam Leffler * this is a DEAUTH message that was sent and the 50130a915fadSSam Leffler * node was timed out due to inactivity. 50140a915fadSSam Leffler */ 5015c42a7b7eSSam Leffler ieee80211_free_node(ni); 50165591b213SSam Leffler } 50175591b213SSam Leffler bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, 50185591b213SSam Leffler BUS_DMASYNC_POSTWRITE); 50195591b213SSam Leffler bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 502068e8e04eSSam Leffler 50215591b213SSam Leffler m_freem(bf->bf_m); 50225591b213SSam Leffler bf->bf_m = NULL; 50235591b213SSam Leffler bf->bf_node = NULL; 50245591b213SSam Leffler 5025f0b2a0beSSam Leffler ATH_TXBUF_LOCK(sc); 5026c42a7b7eSSam Leffler STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); 5027f0b2a0beSSam Leffler ATH_TXBUF_UNLOCK(sc); 50285591b213SSam Leffler } 502968e8e04eSSam Leffler /* 503068e8e04eSSam Leffler * Flush fast-frame staging queue when traffic slows. 503168e8e04eSSam Leffler */ 503268e8e04eSSam Leffler if (txq->axq_depth <= 1) 503368e8e04eSSam Leffler ath_ff_stageq_flush(sc, txq, ath_ff_always); 5034d7736e13SSam Leffler return nacked; 5035d7736e13SSam Leffler } 5036d7736e13SSam Leffler 5037d7736e13SSam Leffler static __inline int 5038d7736e13SSam Leffler txqactive(struct ath_hal *ah, int qnum) 5039d7736e13SSam Leffler { 5040e2815d69SSam Leffler u_int32_t txqs = 1<<qnum; 5041e2815d69SSam Leffler ath_hal_gettxintrtxqs(ah, &txqs); 50429760f8aeSSam Leffler return (txqs & (1<<qnum)); 5043c42a7b7eSSam Leffler } 5044c42a7b7eSSam Leffler 5045c42a7b7eSSam Leffler /* 5046c42a7b7eSSam Leffler * Deferred processing of transmit interrupt; special-cased 5047c42a7b7eSSam Leffler * for a single hardware transmit queue (e.g. 5210 and 5211). 5048c42a7b7eSSam Leffler */ 5049c42a7b7eSSam Leffler static void 5050c42a7b7eSSam Leffler ath_tx_proc_q0(void *arg, int npending) 5051c42a7b7eSSam Leffler { 5052c42a7b7eSSam Leffler struct ath_softc *sc = arg; 5053fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 5054c42a7b7eSSam Leffler 5055d7736e13SSam Leffler if (txqactive(sc->sc_ah, 0) && ath_tx_processq(sc, &sc->sc_txq[0])) 5056d7736e13SSam Leffler sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah); 5057d7736e13SSam Leffler if (txqactive(sc->sc_ah, sc->sc_cabq->axq_qnum)) 5058d7736e13SSam Leffler ath_tx_processq(sc, sc->sc_cabq); 505913f4c340SRobert Watson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 506068e8e04eSSam Leffler ifp->if_timer = 0; 50615591b213SSam Leffler 50623e50ec2cSSam Leffler if (sc->sc_softled) 50633e50ec2cSSam Leffler ath_led_event(sc, ATH_LED_TX); 50643e50ec2cSSam Leffler 50655591b213SSam Leffler ath_start(ifp); 50665591b213SSam Leffler } 50675591b213SSam Leffler 50685591b213SSam Leffler /* 5069c42a7b7eSSam Leffler * Deferred processing of transmit interrupt; special-cased 5070c42a7b7eSSam Leffler * for four hardware queues, 0-3 (e.g. 5212 w/ WME support). 50715591b213SSam Leffler */ 50725591b213SSam Leffler static void 5073c42a7b7eSSam Leffler ath_tx_proc_q0123(void *arg, int npending) 5074c42a7b7eSSam Leffler { 5075c42a7b7eSSam Leffler struct ath_softc *sc = arg; 5076fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 5077d7736e13SSam Leffler int nacked; 5078c42a7b7eSSam Leffler 5079c42a7b7eSSam Leffler /* 5080c42a7b7eSSam Leffler * Process each active queue. 5081c42a7b7eSSam Leffler */ 5082d7736e13SSam Leffler nacked = 0; 5083d7736e13SSam Leffler if (txqactive(sc->sc_ah, 0)) 5084d7736e13SSam Leffler nacked += ath_tx_processq(sc, &sc->sc_txq[0]); 5085d7736e13SSam Leffler if (txqactive(sc->sc_ah, 1)) 5086d7736e13SSam Leffler nacked += ath_tx_processq(sc, &sc->sc_txq[1]); 5087d7736e13SSam Leffler if (txqactive(sc->sc_ah, 2)) 5088d7736e13SSam Leffler nacked += ath_tx_processq(sc, &sc->sc_txq[2]); 5089d7736e13SSam Leffler if (txqactive(sc->sc_ah, 3)) 5090d7736e13SSam Leffler nacked += ath_tx_processq(sc, &sc->sc_txq[3]); 5091d7736e13SSam Leffler if (txqactive(sc->sc_ah, sc->sc_cabq->axq_qnum)) 5092c42a7b7eSSam Leffler ath_tx_processq(sc, sc->sc_cabq); 5093d7736e13SSam Leffler if (nacked) 5094d7736e13SSam Leffler sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah); 5095c42a7b7eSSam Leffler 509613f4c340SRobert Watson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 509768e8e04eSSam Leffler ifp->if_timer = 0; 5098c42a7b7eSSam Leffler 50993e50ec2cSSam Leffler if (sc->sc_softled) 51003e50ec2cSSam Leffler ath_led_event(sc, ATH_LED_TX); 51013e50ec2cSSam Leffler 5102c42a7b7eSSam Leffler ath_start(ifp); 5103c42a7b7eSSam Leffler } 5104c42a7b7eSSam Leffler 5105c42a7b7eSSam Leffler /* 5106c42a7b7eSSam Leffler * Deferred processing of transmit interrupt. 5107c42a7b7eSSam Leffler */ 5108c42a7b7eSSam Leffler static void 5109c42a7b7eSSam Leffler ath_tx_proc(void *arg, int npending) 5110c42a7b7eSSam Leffler { 5111c42a7b7eSSam Leffler struct ath_softc *sc = arg; 5112fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 5113d7736e13SSam Leffler int i, nacked; 5114c42a7b7eSSam Leffler 5115c42a7b7eSSam Leffler /* 5116c42a7b7eSSam Leffler * Process each active queue. 5117c42a7b7eSSam Leffler */ 5118d7736e13SSam Leffler nacked = 0; 5119c42a7b7eSSam Leffler for (i = 0; i < HAL_NUM_TX_QUEUES; i++) 5120d7736e13SSam Leffler if (ATH_TXQ_SETUP(sc, i) && txqactive(sc->sc_ah, i)) 5121d7736e13SSam Leffler nacked += ath_tx_processq(sc, &sc->sc_txq[i]); 5122d7736e13SSam Leffler if (nacked) 5123d7736e13SSam Leffler sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah); 5124c42a7b7eSSam Leffler 512513f4c340SRobert Watson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 512668e8e04eSSam Leffler ifp->if_timer = 0; 5127c42a7b7eSSam Leffler 51283e50ec2cSSam Leffler if (sc->sc_softled) 51293e50ec2cSSam Leffler ath_led_event(sc, ATH_LED_TX); 51303e50ec2cSSam Leffler 5131c42a7b7eSSam Leffler ath_start(ifp); 5132c42a7b7eSSam Leffler } 5133c42a7b7eSSam Leffler 5134c42a7b7eSSam Leffler static void 5135c42a7b7eSSam Leffler ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq) 51365591b213SSam Leffler { 5137a585a9a1SSam Leffler #ifdef ATH_DEBUG 51385591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 5139d2f6ed15SSam Leffler #endif 514023428eafSSam Leffler struct ieee80211_node *ni; 51415591b213SSam Leffler struct ath_buf *bf; 51427a4c5ed9SSam Leffler u_int ix; 51435591b213SSam Leffler 5144c42a7b7eSSam Leffler /* 5145c42a7b7eSSam Leffler * NB: this assumes output has been stopped and 5146ebecf802SSam Leffler * we do not need to block ath_tx_tasklet 5147c42a7b7eSSam Leffler */ 51487a4c5ed9SSam Leffler for (ix = 0;; ix++) { 5149c42a7b7eSSam Leffler ATH_TXQ_LOCK(txq); 5150c42a7b7eSSam Leffler bf = STAILQ_FIRST(&txq->axq_q); 51515591b213SSam Leffler if (bf == NULL) { 5152ebecf802SSam Leffler txq->axq_link = NULL; 5153c42a7b7eSSam Leffler ATH_TXQ_UNLOCK(txq); 51545591b213SSam Leffler break; 51555591b213SSam Leffler } 5156c42a7b7eSSam Leffler ATH_TXQ_REMOVE_HEAD(txq, bf_list); 5157c42a7b7eSSam Leffler ATH_TXQ_UNLOCK(txq); 5158a585a9a1SSam Leffler #ifdef ATH_DEBUG 51594a3ac3fcSSam Leffler if (sc->sc_debug & ATH_DEBUG_RESET) { 5160b032f27cSSam Leffler struct ieee80211com *ic = sc->sc_ifp->if_l2com; 5161b032f27cSSam Leffler 51627a4c5ed9SSam Leffler ath_printtxbuf(bf, txq->axq_qnum, ix, 516365f9edeeSSam Leffler ath_hal_txprocdesc(ah, bf->bf_desc, 516465f9edeeSSam Leffler &bf->bf_status.ds_txstat) == HAL_OK); 5165b032f27cSSam Leffler ieee80211_dump_pkt(ic, mtod(bf->bf_m, caddr_t), 51664a3ac3fcSSam Leffler bf->bf_m->m_len, 0, -1); 51674a3ac3fcSSam Leffler } 5168a585a9a1SSam Leffler #endif /* ATH_DEBUG */ 51695591b213SSam Leffler bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 517023428eafSSam Leffler ni = bf->bf_node; 51715591b213SSam Leffler bf->bf_node = NULL; 5172c42a7b7eSSam Leffler if (ni != NULL) { 517323428eafSSam Leffler /* 5174d50ea6acSSam Leffler * Do any callback and reclaim the node reference. 517523428eafSSam Leffler */ 5176d50ea6acSSam Leffler if (bf->bf_m->m_flags & M_TXCB) 5177d50ea6acSSam Leffler ieee80211_process_callback(ni, bf->bf_m, -1); 5178c42a7b7eSSam Leffler ieee80211_free_node(ni); 517923428eafSSam Leffler } 518068e8e04eSSam Leffler m_freem(bf->bf_m); 518168e8e04eSSam Leffler bf->bf_m = NULL; 518268e8e04eSSam Leffler 5183f0b2a0beSSam Leffler ATH_TXBUF_LOCK(sc); 5184c42a7b7eSSam Leffler STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); 5185f0b2a0beSSam Leffler ATH_TXBUF_UNLOCK(sc); 51865591b213SSam Leffler } 5187c42a7b7eSSam Leffler } 5188c42a7b7eSSam Leffler 5189c42a7b7eSSam Leffler static void 5190c42a7b7eSSam Leffler ath_tx_stopdma(struct ath_softc *sc, struct ath_txq *txq) 5191c42a7b7eSSam Leffler { 5192c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 5193c42a7b7eSSam Leffler 5194c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_RESET, "%s: tx queue [%u] %p, link %p\n", 5195c42a7b7eSSam Leffler __func__, txq->axq_qnum, 51966891c875SPeter Wemm (caddr_t)(uintptr_t) ath_hal_gettxbuf(ah, txq->axq_qnum), 51976891c875SPeter Wemm txq->axq_link); 51984a3ac3fcSSam Leffler (void) ath_hal_stoptxdma(ah, txq->axq_qnum); 5199c42a7b7eSSam Leffler } 5200c42a7b7eSSam Leffler 5201c42a7b7eSSam Leffler /* 5202c42a7b7eSSam Leffler * Drain the transmit queues and reclaim resources. 5203c42a7b7eSSam Leffler */ 5204c42a7b7eSSam Leffler static void 5205c42a7b7eSSam Leffler ath_draintxq(struct ath_softc *sc) 5206c42a7b7eSSam Leffler { 5207c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 5208fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 5209c42a7b7eSSam Leffler int i; 5210c42a7b7eSSam Leffler 5211c42a7b7eSSam Leffler /* XXX return value */ 5212c42a7b7eSSam Leffler if (!sc->sc_invalid) { 5213c42a7b7eSSam Leffler /* don't touch the hardware if marked invalid */ 52144a3ac3fcSSam Leffler DPRINTF(sc, ATH_DEBUG_RESET, "%s: tx queue [%u] %p, link %p\n", 52154a3ac3fcSSam Leffler __func__, sc->sc_bhalq, 52164a3ac3fcSSam Leffler (caddr_t)(uintptr_t) ath_hal_gettxbuf(ah, sc->sc_bhalq), 52174a3ac3fcSSam Leffler NULL); 5218c42a7b7eSSam Leffler (void) ath_hal_stoptxdma(ah, sc->sc_bhalq); 5219c42a7b7eSSam Leffler for (i = 0; i < HAL_NUM_TX_QUEUES; i++) 5220c42a7b7eSSam Leffler if (ATH_TXQ_SETUP(sc, i)) 5221c42a7b7eSSam Leffler ath_tx_stopdma(sc, &sc->sc_txq[i]); 5222c42a7b7eSSam Leffler } 5223c42a7b7eSSam Leffler for (i = 0; i < HAL_NUM_TX_QUEUES; i++) 5224c42a7b7eSSam Leffler if (ATH_TXQ_SETUP(sc, i)) 5225c42a7b7eSSam Leffler ath_tx_draintxq(sc, &sc->sc_txq[i]); 52264a3ac3fcSSam Leffler #ifdef ATH_DEBUG 52274a3ac3fcSSam Leffler if (sc->sc_debug & ATH_DEBUG_RESET) { 52284a3ac3fcSSam Leffler struct ath_buf *bf = STAILQ_FIRST(&sc->sc_bbuf); 52294a3ac3fcSSam Leffler if (bf != NULL && bf->bf_m != NULL) { 52304a3ac3fcSSam Leffler ath_printtxbuf(bf, sc->sc_bhalq, 0, 523165f9edeeSSam Leffler ath_hal_txprocdesc(ah, bf->bf_desc, 523265f9edeeSSam Leffler &bf->bf_status.ds_txstat) == HAL_OK); 5233b032f27cSSam Leffler ieee80211_dump_pkt(ifp->if_l2com, mtod(bf->bf_m, caddr_t), 52344a3ac3fcSSam Leffler bf->bf_m->m_len, 0, -1); 52354a3ac3fcSSam Leffler } 52364a3ac3fcSSam Leffler } 52374a3ac3fcSSam Leffler #endif /* ATH_DEBUG */ 523813f4c340SRobert Watson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 523968e8e04eSSam Leffler ifp->if_timer = 0; 52405591b213SSam Leffler } 52415591b213SSam Leffler 52425591b213SSam Leffler /* 52435591b213SSam Leffler * Disable the receive h/w in preparation for a reset. 52445591b213SSam Leffler */ 52455591b213SSam Leffler static void 52465591b213SSam Leffler ath_stoprecv(struct ath_softc *sc) 52475591b213SSam Leffler { 52488cec0ab9SSam Leffler #define PA2DESC(_sc, _pa) \ 5249c42a7b7eSSam Leffler ((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \ 5250c42a7b7eSSam Leffler ((_pa) - (_sc)->sc_rxdma.dd_desc_paddr))) 52515591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 52525591b213SSam Leffler 52535591b213SSam Leffler ath_hal_stoppcurecv(ah); /* disable PCU */ 52545591b213SSam Leffler ath_hal_setrxfilter(ah, 0); /* clear recv filter */ 52555591b213SSam Leffler ath_hal_stopdmarecv(ah); /* disable DMA engine */ 5256c42a7b7eSSam Leffler DELAY(3000); /* 3ms is long enough for 1 frame */ 5257a585a9a1SSam Leffler #ifdef ATH_DEBUG 5258c42a7b7eSSam Leffler if (sc->sc_debug & (ATH_DEBUG_RESET | ATH_DEBUG_FATAL)) { 52595591b213SSam Leffler struct ath_buf *bf; 52607a4c5ed9SSam Leffler u_int ix; 52615591b213SSam Leffler 5262e325e530SSam Leffler printf("%s: rx queue %p, link %p\n", __func__, 526330310634SPeter Wemm (caddr_t)(uintptr_t) ath_hal_getrxbuf(ah), sc->sc_rxlink); 52647a4c5ed9SSam Leffler ix = 0; 5265c42a7b7eSSam Leffler STAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) { 52668cec0ab9SSam Leffler struct ath_desc *ds = bf->bf_desc; 526765f9edeeSSam Leffler struct ath_rx_status *rs = &bf->bf_status.ds_rxstat; 5268c42a7b7eSSam Leffler HAL_STATUS status = ath_hal_rxprocdesc(ah, ds, 526965f9edeeSSam Leffler bf->bf_daddr, PA2DESC(sc, ds->ds_link), rs); 5270c42a7b7eSSam Leffler if (status == HAL_OK || (sc->sc_debug & ATH_DEBUG_FATAL)) 52717a4c5ed9SSam Leffler ath_printrxbuf(bf, ix, status == HAL_OK); 52727a4c5ed9SSam Leffler ix++; 52735591b213SSam Leffler } 52745591b213SSam Leffler } 52755591b213SSam Leffler #endif 527668e8e04eSSam Leffler if (sc->sc_rxpending != NULL) { 527768e8e04eSSam Leffler m_freem(sc->sc_rxpending); 527868e8e04eSSam Leffler sc->sc_rxpending = NULL; 527968e8e04eSSam Leffler } 52805591b213SSam Leffler sc->sc_rxlink = NULL; /* just in case */ 52818cec0ab9SSam Leffler #undef PA2DESC 52825591b213SSam Leffler } 52835591b213SSam Leffler 52845591b213SSam Leffler /* 52855591b213SSam Leffler * Enable the receive h/w following a reset. 52865591b213SSam Leffler */ 52875591b213SSam Leffler static int 52885591b213SSam Leffler ath_startrecv(struct ath_softc *sc) 52895591b213SSam Leffler { 52905591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 52915591b213SSam Leffler struct ath_buf *bf; 52925591b213SSam Leffler 52935591b213SSam Leffler sc->sc_rxlink = NULL; 529468e8e04eSSam Leffler sc->sc_rxpending = NULL; 5295c42a7b7eSSam Leffler STAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) { 52965591b213SSam Leffler int error = ath_rxbuf_init(sc, bf); 52975591b213SSam Leffler if (error != 0) { 5298c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_RECV, 5299c42a7b7eSSam Leffler "%s: ath_rxbuf_init failed %d\n", 5300c42a7b7eSSam Leffler __func__, error); 53015591b213SSam Leffler return error; 53025591b213SSam Leffler } 53035591b213SSam Leffler } 53045591b213SSam Leffler 5305c42a7b7eSSam Leffler bf = STAILQ_FIRST(&sc->sc_rxbuf); 53065591b213SSam Leffler ath_hal_putrxbuf(ah, bf->bf_daddr); 53075591b213SSam Leffler ath_hal_rxena(ah); /* enable recv descriptors */ 53085591b213SSam Leffler ath_mode_init(sc); /* set filters, etc. */ 53095591b213SSam Leffler ath_hal_startpcurecv(ah); /* re-enable PCU/DMA engine */ 53105591b213SSam Leffler return 0; 53115591b213SSam Leffler } 53125591b213SSam Leffler 53135591b213SSam Leffler /* 5314c42a7b7eSSam Leffler * Update internal state after a channel change. 5315c42a7b7eSSam Leffler */ 5316c42a7b7eSSam Leffler static void 5317c42a7b7eSSam Leffler ath_chan_change(struct ath_softc *sc, struct ieee80211_channel *chan) 5318c42a7b7eSSam Leffler { 5319c42a7b7eSSam Leffler enum ieee80211_phymode mode; 5320c42a7b7eSSam Leffler 5321c42a7b7eSSam Leffler /* 5322c42a7b7eSSam Leffler * Change channels and update the h/w rate map 5323c42a7b7eSSam Leffler * if we're switching; e.g. 11a to 11b/g. 5324c42a7b7eSSam Leffler */ 5325aaa70f2fSSam Leffler if (IEEE80211_IS_CHAN_HALF(chan)) 5326724c193aSSam Leffler mode = IEEE80211_MODE_HALF; 5327aaa70f2fSSam Leffler else if (IEEE80211_IS_CHAN_QUARTER(chan)) 5328724c193aSSam Leffler mode = IEEE80211_MODE_QUARTER; 5329724c193aSSam Leffler else 533068e8e04eSSam Leffler mode = ieee80211_chan2mode(chan); 5331c42a7b7eSSam Leffler if (mode != sc->sc_curmode) 5332c42a7b7eSSam Leffler ath_setcurmode(sc, mode); 533368e8e04eSSam Leffler 533468e8e04eSSam Leffler sc->sc_rx_th.wr_chan_flags = htole32(chan->ic_flags); 533568e8e04eSSam Leffler sc->sc_tx_th.wt_chan_flags = sc->sc_rx_th.wr_chan_flags; 533668e8e04eSSam Leffler sc->sc_rx_th.wr_chan_freq = htole16(chan->ic_freq); 533768e8e04eSSam Leffler sc->sc_tx_th.wt_chan_freq = sc->sc_rx_th.wr_chan_freq; 533868e8e04eSSam Leffler sc->sc_rx_th.wr_chan_ieee = chan->ic_ieee; 533968e8e04eSSam Leffler sc->sc_tx_th.wt_chan_ieee = sc->sc_rx_th.wr_chan_ieee; 534068e8e04eSSam Leffler sc->sc_rx_th.wr_chan_maxpow = chan->ic_maxregpower; 534168e8e04eSSam Leffler sc->sc_tx_th.wt_chan_maxpow = sc->sc_rx_th.wr_chan_maxpow; 5342c42a7b7eSSam Leffler } 5343c42a7b7eSSam Leffler 5344c42a7b7eSSam Leffler /* 53455591b213SSam Leffler * Set/change channels. If the channel is really being changed, 5346c42a7b7eSSam Leffler * it's done by reseting the chip. To accomplish this we must 53475591b213SSam Leffler * first cleanup any pending DMA, then restart stuff after a la 53485591b213SSam Leffler * ath_init. 53495591b213SSam Leffler */ 53505591b213SSam Leffler static int 53515591b213SSam Leffler ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan) 53525591b213SSam Leffler { 5353b032f27cSSam Leffler struct ifnet *ifp = sc->sc_ifp; 5354b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 53555591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 53565591b213SSam Leffler HAL_CHANNEL hchan; 5357c42a7b7eSSam Leffler 5358c42a7b7eSSam Leffler /* 5359c42a7b7eSSam Leffler * Convert to a HAL channel description with 5360c42a7b7eSSam Leffler * the flags constrained to reflect the current 5361c42a7b7eSSam Leffler * operating mode. 5362c42a7b7eSSam Leffler */ 536368e8e04eSSam Leffler ath_mapchan(&hchan, chan); 5364c42a7b7eSSam Leffler 5365370572d9SSam Leffler DPRINTF(sc, ATH_DEBUG_RESET, 5366370572d9SSam Leffler "%s: %u (%u MHz, hal flags 0x%x) -> %u (%u MHz, hal flags 0x%x)\n", 5367c42a7b7eSSam Leffler __func__, 5368bd5a9920SSam Leffler ath_hal_mhz2ieee(ah, sc->sc_curchan.channel, 5369c42a7b7eSSam Leffler sc->sc_curchan.channelFlags), 5370370572d9SSam Leffler sc->sc_curchan.channel, sc->sc_curchan.channelFlags, 5371bd5a9920SSam Leffler ath_hal_mhz2ieee(ah, hchan.channel, hchan.channelFlags), 5372370572d9SSam Leffler hchan.channel, hchan.channelFlags); 5373c42a7b7eSSam Leffler if (hchan.channel != sc->sc_curchan.channel || 5374c42a7b7eSSam Leffler hchan.channelFlags != sc->sc_curchan.channelFlags) { 5375c42a7b7eSSam Leffler HAL_STATUS status; 53765591b213SSam Leffler 53775591b213SSam Leffler /* 53785591b213SSam Leffler * To switch channels clear any pending DMA operations; 53795591b213SSam Leffler * wait long enough for the RX fifo to drain, reset the 53805591b213SSam Leffler * hardware at the new frequency, and then re-enable 53815591b213SSam Leffler * the relevant bits of the h/w. 53825591b213SSam Leffler */ 53835591b213SSam Leffler ath_hal_intrset(ah, 0); /* disable interrupts */ 53845591b213SSam Leffler ath_draintxq(sc); /* clear pending tx frames */ 53855591b213SSam Leffler ath_stoprecv(sc); /* turn off frame recv */ 53867a04dc27SSam Leffler if (!ath_hal_reset(ah, sc->sc_opmode, &hchan, AH_TRUE, &status)) { 5387b032f27cSSam Leffler if_printf(ifp, "%s: unable to reset " 5388c28953b4SSam Leffler "channel %u (%u Mhz, flags 0x%x hal flags 0x%x), " 5389c28953b4SSam Leffler "hal status %u\n", __func__, 5390c28953b4SSam Leffler ieee80211_chan2ieee(ic, chan), chan->ic_freq, 5391c28953b4SSam Leffler chan->ic_flags, hchan.channelFlags, status); 53925591b213SSam Leffler return EIO; 53935591b213SSam Leffler } 5394c42a7b7eSSam Leffler sc->sc_curchan = hchan; 5395c59005e9SSam Leffler sc->sc_diversity = ath_hal_getdiversity(ah); 5396bd5a9920SSam Leffler sc->sc_calinterval = 1; 5397bd5a9920SSam Leffler sc->sc_caltries = 0; 5398c42a7b7eSSam Leffler 53995591b213SSam Leffler /* 54005591b213SSam Leffler * Re-enable rx framework. 54015591b213SSam Leffler */ 54025591b213SSam Leffler if (ath_startrecv(sc) != 0) { 5403b032f27cSSam Leffler if_printf(ifp, "%s: unable to restart recv logic\n", 5404b032f27cSSam Leffler __func__); 54055591b213SSam Leffler return EIO; 54065591b213SSam Leffler } 54075591b213SSam Leffler 54085591b213SSam Leffler /* 54095591b213SSam Leffler * Change channels and update the h/w rate map 54105591b213SSam Leffler * if we're switching; e.g. 11a to 11b/g. 54115591b213SSam Leffler */ 5412c42a7b7eSSam Leffler ath_chan_change(sc, chan); 54130a915fadSSam Leffler 54140a915fadSSam Leffler /* 54150a915fadSSam Leffler * Re-enable interrupts. 54160a915fadSSam Leffler */ 54170a915fadSSam Leffler ath_hal_intrset(ah, sc->sc_imask); 54185591b213SSam Leffler } 54195591b213SSam Leffler return 0; 54205591b213SSam Leffler } 54215591b213SSam Leffler 54225591b213SSam Leffler /* 54235591b213SSam Leffler * Periodically recalibrate the PHY to account 54245591b213SSam Leffler * for temperature/environment changes. 54255591b213SSam Leffler */ 54265591b213SSam Leffler static void 54275591b213SSam Leffler ath_calibrate(void *arg) 54285591b213SSam Leffler { 54295591b213SSam Leffler struct ath_softc *sc = arg; 54305591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 5431bd5a9920SSam Leffler HAL_BOOL iqCalDone; 54325591b213SSam Leffler 54335591b213SSam Leffler sc->sc_stats.ast_per_cal++; 54345591b213SSam Leffler 54355591b213SSam Leffler if (ath_hal_getrfgain(ah) == HAL_RFGAIN_NEED_CHANGE) { 54365591b213SSam Leffler /* 54375591b213SSam Leffler * Rfgain is out of bounds, reset the chip 54385591b213SSam Leffler * to load new gain values. 54395591b213SSam Leffler */ 5440370572d9SSam Leffler DPRINTF(sc, ATH_DEBUG_CALIBRATE, 5441370572d9SSam Leffler "%s: rfgain change\n", __func__); 54425591b213SSam Leffler sc->sc_stats.ast_per_rfgain++; 5443fc74a9f9SBrooks Davis ath_reset(sc->sc_ifp); 54445591b213SSam Leffler } 5445bd5a9920SSam Leffler if (!ath_hal_calibrate(ah, &sc->sc_curchan, &iqCalDone)) { 5446c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, 5447c42a7b7eSSam Leffler "%s: calibration of channel %u failed\n", 5448c42a7b7eSSam Leffler __func__, sc->sc_curchan.channel); 54495591b213SSam Leffler sc->sc_stats.ast_per_calfail++; 54505591b213SSam Leffler } 54517b0c77ecSSam Leffler /* 54527b0c77ecSSam Leffler * Calibrate noise floor data again in case of change. 54537b0c77ecSSam Leffler */ 54547b0c77ecSSam Leffler ath_hal_process_noisefloor(ah); 5455bd5a9920SSam Leffler /* 5456bd5a9920SSam Leffler * Poll more frequently when the IQ calibration is in 5457bd5a9920SSam Leffler * progress to speedup loading the final settings. 5458bd5a9920SSam Leffler * We temper this aggressive polling with an exponential 5459bd5a9920SSam Leffler * back off after 4 tries up to ath_calinterval. 5460bd5a9920SSam Leffler */ 5461bd5a9920SSam Leffler if (iqCalDone || sc->sc_calinterval >= ath_calinterval) { 5462bd5a9920SSam Leffler sc->sc_caltries = 0; 5463bd5a9920SSam Leffler sc->sc_calinterval = ath_calinterval; 5464bd5a9920SSam Leffler } else if (sc->sc_caltries > 4) { 5465bd5a9920SSam Leffler sc->sc_caltries = 0; 5466bd5a9920SSam Leffler sc->sc_calinterval <<= 1; 5467bd5a9920SSam Leffler if (sc->sc_calinterval > ath_calinterval) 5468bd5a9920SSam Leffler sc->sc_calinterval = ath_calinterval; 5469bd5a9920SSam Leffler } 5470bd5a9920SSam Leffler KASSERT(0 < sc->sc_calinterval && sc->sc_calinterval <= ath_calinterval, 5471bd5a9920SSam Leffler ("bad calibration interval %u", sc->sc_calinterval)); 5472bd5a9920SSam Leffler 5473bd5a9920SSam Leffler DPRINTF(sc, ATH_DEBUG_CALIBRATE, 5474bd5a9920SSam Leffler "%s: next +%u (%siqCalDone tries %u)\n", __func__, 5475bd5a9920SSam Leffler sc->sc_calinterval, iqCalDone ? "" : "!", sc->sc_caltries); 5476bd5a9920SSam Leffler sc->sc_caltries++; 5477bd5a9920SSam Leffler callout_reset(&sc->sc_cal_ch, sc->sc_calinterval * hz, 5478bd5a9920SSam Leffler ath_calibrate, sc); 54795591b213SSam Leffler } 54805591b213SSam Leffler 548168e8e04eSSam Leffler static void 548268e8e04eSSam Leffler ath_scan_start(struct ieee80211com *ic) 548368e8e04eSSam Leffler { 548468e8e04eSSam Leffler struct ifnet *ifp = ic->ic_ifp; 548568e8e04eSSam Leffler struct ath_softc *sc = ifp->if_softc; 548668e8e04eSSam Leffler struct ath_hal *ah = sc->sc_ah; 548768e8e04eSSam Leffler u_int32_t rfilt; 548868e8e04eSSam Leffler 548968e8e04eSSam Leffler /* XXX calibration timer? */ 549068e8e04eSSam Leffler 549168e8e04eSSam Leffler sc->sc_scanning = 1; 549268e8e04eSSam Leffler sc->sc_syncbeacon = 0; 549368e8e04eSSam Leffler rfilt = ath_calcrxfilter(sc); 549468e8e04eSSam Leffler ath_hal_setrxfilter(ah, rfilt); 549568e8e04eSSam Leffler ath_hal_setassocid(ah, ifp->if_broadcastaddr, 0); 549668e8e04eSSam Leffler 549768e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s aid 0\n", 549868e8e04eSSam Leffler __func__, rfilt, ether_sprintf(ifp->if_broadcastaddr)); 549968e8e04eSSam Leffler } 550068e8e04eSSam Leffler 550168e8e04eSSam Leffler static void 550268e8e04eSSam Leffler ath_scan_end(struct ieee80211com *ic) 550368e8e04eSSam Leffler { 550468e8e04eSSam Leffler struct ifnet *ifp = ic->ic_ifp; 550568e8e04eSSam Leffler struct ath_softc *sc = ifp->if_softc; 550668e8e04eSSam Leffler struct ath_hal *ah = sc->sc_ah; 550768e8e04eSSam Leffler u_int32_t rfilt; 550868e8e04eSSam Leffler 550968e8e04eSSam Leffler sc->sc_scanning = 0; 551068e8e04eSSam Leffler rfilt = ath_calcrxfilter(sc); 551168e8e04eSSam Leffler ath_hal_setrxfilter(ah, rfilt); 551268e8e04eSSam Leffler ath_hal_setassocid(ah, sc->sc_curbssid, sc->sc_curaid); 551368e8e04eSSam Leffler 551468e8e04eSSam Leffler ath_hal_process_noisefloor(ah); 551568e8e04eSSam Leffler 551668e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s aid 0x%x\n", 551768e8e04eSSam Leffler __func__, rfilt, ether_sprintf(sc->sc_curbssid), 551868e8e04eSSam Leffler sc->sc_curaid); 551968e8e04eSSam Leffler } 552068e8e04eSSam Leffler 552168e8e04eSSam Leffler static void 552268e8e04eSSam Leffler ath_set_channel(struct ieee80211com *ic) 552368e8e04eSSam Leffler { 552468e8e04eSSam Leffler struct ifnet *ifp = ic->ic_ifp; 552568e8e04eSSam Leffler struct ath_softc *sc = ifp->if_softc; 552668e8e04eSSam Leffler 552768e8e04eSSam Leffler (void) ath_chan_set(sc, ic->ic_curchan); 552868e8e04eSSam Leffler /* 552968e8e04eSSam Leffler * If we are returning to our bss channel then mark state 553068e8e04eSSam Leffler * so the next recv'd beacon's tsf will be used to sync the 553168e8e04eSSam Leffler * beacon timers. Note that since we only hear beacons in 553268e8e04eSSam Leffler * sta/ibss mode this has no effect in other operating modes. 553368e8e04eSSam Leffler */ 553468e8e04eSSam Leffler if (!sc->sc_scanning && ic->ic_curchan == ic->ic_bsschan) 553568e8e04eSSam Leffler sc->sc_syncbeacon = 1; 553668e8e04eSSam Leffler } 553768e8e04eSSam Leffler 5538b032f27cSSam Leffler /* 5539b032f27cSSam Leffler * Walk the vap list and check if there any vap's in RUN state. 5540b032f27cSSam Leffler */ 55415591b213SSam Leffler static int 5542b032f27cSSam Leffler ath_isanyrunningvaps(struct ieee80211vap *this) 55435591b213SSam Leffler { 5544b032f27cSSam Leffler struct ieee80211com *ic = this->iv_ic; 5545b032f27cSSam Leffler struct ieee80211vap *vap; 5546b032f27cSSam Leffler 5547b032f27cSSam Leffler IEEE80211_LOCK_ASSERT(ic); 5548b032f27cSSam Leffler 5549b032f27cSSam Leffler TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 5550b032f27cSSam Leffler if (vap != this && vap->iv_state == IEEE80211_S_RUN) 5551b032f27cSSam Leffler return 1; 5552b032f27cSSam Leffler } 5553b032f27cSSam Leffler return 0; 5554b032f27cSSam Leffler } 5555b032f27cSSam Leffler 5556b032f27cSSam Leffler static int 5557b032f27cSSam Leffler ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 5558b032f27cSSam Leffler { 5559b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 5560b032f27cSSam Leffler struct ath_softc *sc = ic->ic_ifp->if_softc; 5561b032f27cSSam Leffler struct ath_vap *avp = ATH_VAP(vap); 556245bbf62fSSam Leffler struct ath_hal *ah = sc->sc_ah; 5563b032f27cSSam Leffler struct ieee80211_node *ni = NULL; 556468e8e04eSSam Leffler int i, error, stamode; 55655591b213SSam Leffler u_int32_t rfilt; 55665591b213SSam Leffler static const HAL_LED_STATE leds[] = { 55675591b213SSam Leffler HAL_LED_INIT, /* IEEE80211_S_INIT */ 55685591b213SSam Leffler HAL_LED_SCAN, /* IEEE80211_S_SCAN */ 55695591b213SSam Leffler HAL_LED_AUTH, /* IEEE80211_S_AUTH */ 55705591b213SSam Leffler HAL_LED_ASSOC, /* IEEE80211_S_ASSOC */ 557177d5e068SSam Leffler HAL_LED_RUN, /* IEEE80211_S_CAC */ 55725591b213SSam Leffler HAL_LED_RUN, /* IEEE80211_S_RUN */ 557377d5e068SSam Leffler HAL_LED_RUN, /* IEEE80211_S_CSA */ 557477d5e068SSam Leffler HAL_LED_RUN, /* IEEE80211_S_SLEEP */ 55755591b213SSam Leffler }; 55765591b213SSam Leffler 5577c42a7b7eSSam Leffler DPRINTF(sc, ATH_DEBUG_STATE, "%s: %s -> %s\n", __func__, 5578b032f27cSSam Leffler ieee80211_state_name[vap->iv_state], 5579c42a7b7eSSam Leffler ieee80211_state_name[nstate]); 55805591b213SSam Leffler 5581c42a7b7eSSam Leffler callout_stop(&sc->sc_cal_ch); 55825591b213SSam Leffler ath_hal_setledstate(ah, leds[nstate]); /* set LED */ 55835591b213SSam Leffler 5584b032f27cSSam Leffler if (nstate == IEEE80211_S_SCAN) { 558558769f58SSam Leffler /* 5586b032f27cSSam Leffler * Scanning: turn off beacon miss and don't beacon. 5587b032f27cSSam Leffler * Mark beacon state so when we reach RUN state we'll 5588b032f27cSSam Leffler * [re]setup beacons. Unblock the task q thread so 5589b032f27cSSam Leffler * deferred interrupt processing is done. 559058769f58SSam Leffler */ 5591b032f27cSSam Leffler ath_hal_intrset(ah, 5592b032f27cSSam Leffler sc->sc_imask &~ (HAL_INT_SWBA | HAL_INT_BMISS)); 55935591b213SSam Leffler sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS); 5594b032f27cSSam Leffler sc->sc_beacons = 0; 5595b032f27cSSam Leffler taskqueue_unblock(sc->sc_tq); 55965591b213SSam Leffler } 55975591b213SSam Leffler 5598b032f27cSSam Leffler ni = vap->iv_bss; 559968e8e04eSSam Leffler rfilt = ath_calcrxfilter(sc); 5600b032f27cSSam Leffler stamode = (vap->iv_opmode == IEEE80211_M_STA || 5601b032f27cSSam Leffler vap->iv_opmode == IEEE80211_M_IBSS); 560268e8e04eSSam Leffler if (stamode && nstate == IEEE80211_S_RUN) { 560368e8e04eSSam Leffler sc->sc_curaid = ni->ni_associd; 560468e8e04eSSam Leffler IEEE80211_ADDR_COPY(sc->sc_curbssid, ni->ni_bssid); 5605b032f27cSSam Leffler ath_hal_setassocid(ah, sc->sc_curbssid, sc->sc_curaid); 5606b032f27cSSam Leffler } 560768e8e04eSSam Leffler DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s aid 0x%x\n", 5608b032f27cSSam Leffler __func__, rfilt, ether_sprintf(sc->sc_curbssid), sc->sc_curaid); 560968e8e04eSSam Leffler ath_hal_setrxfilter(ah, rfilt); 561068e8e04eSSam Leffler 5611b032f27cSSam Leffler /* XXX is this to restore keycache on resume? */ 5612b032f27cSSam Leffler if (vap->iv_opmode != IEEE80211_M_STA && 5613b032f27cSSam Leffler (vap->iv_flags & IEEE80211_F_PRIVACY)) { 56145591b213SSam Leffler for (i = 0; i < IEEE80211_WEP_NKID; i++) 56155591b213SSam Leffler if (ath_hal_keyisvalid(ah, i)) 561668e8e04eSSam Leffler ath_hal_keysetmac(ah, i, ni->ni_bssid); 56175591b213SSam Leffler } 5618c42a7b7eSSam Leffler /* 5619c42a7b7eSSam Leffler * Notify the rate control algorithm so rates 5620c42a7b7eSSam Leffler * are setup should ath_beacon_alloc be called. 5621c42a7b7eSSam Leffler */ 5622b032f27cSSam Leffler ath_rate_newstate(vap, nstate); 5623b032f27cSSam Leffler 5624b032f27cSSam Leffler /* 5625b032f27cSSam Leffler * Invoke the parent method to do net80211 work. 5626b032f27cSSam Leffler */ 5627b032f27cSSam Leffler error = avp->av_newstate(vap, nstate, arg); 5628b032f27cSSam Leffler if (error != 0) 5629b032f27cSSam Leffler goto bad; 5630c42a7b7eSSam Leffler 563168e8e04eSSam Leffler if (nstate == IEEE80211_S_RUN) { 5632b032f27cSSam Leffler /* NB: collect bss node again, it may have changed */ 5633b032f27cSSam Leffler ni = vap->iv_bss; 56345591b213SSam Leffler 5635b032f27cSSam Leffler DPRINTF(sc, ATH_DEBUG_STATE, 5636b032f27cSSam Leffler "%s(RUN): iv_flags 0x%08x bintvl %d bssid %s " 5637b032f27cSSam Leffler "capinfo 0x%04x chan %d\n", __func__, 5638b032f27cSSam Leffler vap->iv_flags, ni->ni_intval, ether_sprintf(ni->ni_bssid), 5639b032f27cSSam Leffler ni->ni_capinfo, ieee80211_chan2ieee(ic, ic->ic_curchan)); 5640b032f27cSSam Leffler 5641b032f27cSSam Leffler switch (vap->iv_opmode) { 5642e8fd88a3SSam Leffler case IEEE80211_M_HOSTAP: 5643e8fd88a3SSam Leffler case IEEE80211_M_IBSS: 56445591b213SSam Leffler /* 5645e8fd88a3SSam Leffler * Allocate and setup the beacon frame. 5646e8fd88a3SSam Leffler * 5647f818612bSSam Leffler * Stop any previous beacon DMA. This may be 5648f818612bSSam Leffler * necessary, for example, when an ibss merge 5649f818612bSSam Leffler * causes reconfiguration; there will be a state 5650f818612bSSam Leffler * transition from RUN->RUN that means we may 5651f818612bSSam Leffler * be called with beacon transmission active. 5652f818612bSSam Leffler */ 5653f818612bSSam Leffler ath_hal_stoptxdma(ah, sc->sc_bhalq); 5654b032f27cSSam Leffler 56555591b213SSam Leffler error = ath_beacon_alloc(sc, ni); 56565591b213SSam Leffler if (error != 0) 56575591b213SSam Leffler goto bad; 56587a04dc27SSam Leffler /* 565980d939bfSSam Leffler * If joining an adhoc network defer beacon timer 566080d939bfSSam Leffler * configuration to the next beacon frame so we 566180d939bfSSam Leffler * have a current TSF to use. Otherwise we're 5662b032f27cSSam Leffler * starting an ibss/bss so there's no need to delay; 5663b032f27cSSam Leffler * if this is the first vap moving to RUN state, then 5664b032f27cSSam Leffler * beacon state needs to be [re]configured. 56657a04dc27SSam Leffler */ 5666b032f27cSSam Leffler if (vap->iv_opmode == IEEE80211_M_IBSS && 5667b032f27cSSam Leffler ni->ni_tstamp.tsf != 0) { 566880d939bfSSam Leffler sc->sc_syncbeacon = 1; 5669b032f27cSSam Leffler } else if (!sc->sc_beacons) { 5670b032f27cSSam Leffler ath_beacon_config(sc, vap); 5671b032f27cSSam Leffler sc->sc_beacons = 1; 5672b032f27cSSam Leffler } 5673e8fd88a3SSam Leffler break; 5674e8fd88a3SSam Leffler case IEEE80211_M_STA: 5675e8fd88a3SSam Leffler /* 5676b032f27cSSam Leffler * Fakeup since we're not called by net80211. 5677e8fd88a3SSam Leffler */ 5678b032f27cSSam Leffler ath_newassoc(ni, 1); 56797a04dc27SSam Leffler /* 568080d939bfSSam Leffler * Defer beacon timer configuration to the next 568180d939bfSSam Leffler * beacon frame so we have a current TSF to use 568280d939bfSSam Leffler * (any TSF collected when scanning is likely old). 56837a04dc27SSam Leffler */ 568480d939bfSSam Leffler sc->sc_syncbeacon = 1; 5685e8fd88a3SSam Leffler break; 5686b032f27cSSam Leffler case IEEE80211_M_MONITOR: 5687b032f27cSSam Leffler /* 5688b032f27cSSam Leffler * Monitor mode vaps have only INIT->RUN and RUN->RUN 5689b032f27cSSam Leffler * transitions so we must re-enable interrupts here to 5690b032f27cSSam Leffler * handle the case of a single monitor mode vap. 5691b032f27cSSam Leffler */ 5692b032f27cSSam Leffler ath_hal_intrset(ah, sc->sc_imask); 5693b032f27cSSam Leffler break; 5694b032f27cSSam Leffler case IEEE80211_M_WDS: 5695b032f27cSSam Leffler break; 5696e8fd88a3SSam Leffler default: 5697e8fd88a3SSam Leffler break; 56985591b213SSam Leffler } 56995591b213SSam Leffler /* 57007b0c77ecSSam Leffler * Let the hal process statistics collected during a 57017b0c77ecSSam Leffler * scan so it can provide calibrated noise floor data. 57027b0c77ecSSam Leffler */ 57037b0c77ecSSam Leffler ath_hal_process_noisefloor(ah); 57047b0c77ecSSam Leffler /* 5705ffa2cab6SSam Leffler * Reset rssi stats; maybe not the best place... 5706ffa2cab6SSam Leffler */ 5707ffa2cab6SSam Leffler sc->sc_halstats.ns_avgbrssi = ATH_RSSI_DUMMY_MARKER; 5708ffa2cab6SSam Leffler sc->sc_halstats.ns_avgrssi = ATH_RSSI_DUMMY_MARKER; 5709ffa2cab6SSam Leffler sc->sc_halstats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER; 571045bbf62fSSam Leffler /* 5711b032f27cSSam Leffler * Finally, start any timers and the task q thread 5712b032f27cSSam Leffler * (in case we didn't go through SCAN state). 571345bbf62fSSam Leffler */ 5714b032f27cSSam Leffler if (sc->sc_calinterval != 0) { 5715c42a7b7eSSam Leffler /* start periodic recalibration timer */ 5716bd5a9920SSam Leffler callout_reset(&sc->sc_cal_ch, sc->sc_calinterval * hz, 5717c42a7b7eSSam Leffler ath_calibrate, sc); 5718c42a7b7eSSam Leffler } 5719b032f27cSSam Leffler taskqueue_unblock(sc->sc_tq); 5720b032f27cSSam Leffler } else if (nstate == IEEE80211_S_INIT) { 5721b032f27cSSam Leffler /* 5722b032f27cSSam Leffler * If there are no vaps left in RUN state then 5723b032f27cSSam Leffler * shutdown host/driver operation: 5724b032f27cSSam Leffler * o disable interrupts 5725b032f27cSSam Leffler * o disable the task queue thread 5726b032f27cSSam Leffler * o mark beacon processing as stopped 5727b032f27cSSam Leffler */ 5728b032f27cSSam Leffler if (!ath_isanyrunningvaps(vap)) { 5729b032f27cSSam Leffler sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS); 5730b032f27cSSam Leffler /* disable interrupts */ 5731b032f27cSSam Leffler ath_hal_intrset(ah, sc->sc_imask &~ HAL_INT_GLOBAL); 5732b032f27cSSam Leffler taskqueue_block(sc->sc_tq); 5733b032f27cSSam Leffler sc->sc_beacons = 0; 5734b032f27cSSam Leffler } 5735b032f27cSSam Leffler } 57365591b213SSam Leffler bad: 57375591b213SSam Leffler return error; 57385591b213SSam Leffler } 57395591b213SSam Leffler 57405591b213SSam Leffler /* 5741e8fd88a3SSam Leffler * Allocate a key cache slot to the station so we can 5742e8fd88a3SSam Leffler * setup a mapping from key index to node. The key cache 5743e8fd88a3SSam Leffler * slot is needed for managing antenna state and for 5744e8fd88a3SSam Leffler * compression when stations do not use crypto. We do 5745e8fd88a3SSam Leffler * it uniliaterally here; if crypto is employed this slot 5746e8fd88a3SSam Leffler * will be reassigned. 5747e8fd88a3SSam Leffler */ 5748e8fd88a3SSam Leffler static void 5749e8fd88a3SSam Leffler ath_setup_stationkey(struct ieee80211_node *ni) 5750e8fd88a3SSam Leffler { 5751b032f27cSSam Leffler struct ieee80211vap *vap = ni->ni_vap; 5752b032f27cSSam Leffler struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; 5753c1225b52SSam Leffler ieee80211_keyix keyix, rxkeyix; 5754e8fd88a3SSam Leffler 5755b032f27cSSam Leffler if (!ath_key_alloc(vap, &ni->ni_ucastkey, &keyix, &rxkeyix)) { 5756e8fd88a3SSam Leffler /* 5757e8fd88a3SSam Leffler * Key cache is full; we'll fall back to doing 5758e8fd88a3SSam Leffler * the more expensive lookup in software. Note 5759e8fd88a3SSam Leffler * this also means no h/w compression. 5760e8fd88a3SSam Leffler */ 5761e8fd88a3SSam Leffler /* XXX msg+statistic */ 5762e8fd88a3SSam Leffler } else { 5763c1225b52SSam Leffler /* XXX locking? */ 5764e8fd88a3SSam Leffler ni->ni_ucastkey.wk_keyix = keyix; 5765c1225b52SSam Leffler ni->ni_ucastkey.wk_rxkeyix = rxkeyix; 5766e8fd88a3SSam Leffler /* NB: this will create a pass-thru key entry */ 5767b032f27cSSam Leffler ath_keyset(sc, &ni->ni_ucastkey, ni->ni_macaddr, vap->iv_bss); 5768e8fd88a3SSam Leffler } 5769e8fd88a3SSam Leffler } 5770e8fd88a3SSam Leffler 5771e8fd88a3SSam Leffler /* 57725591b213SSam Leffler * Setup driver-specific state for a newly associated node. 57735591b213SSam Leffler * Note that we're called also on a re-associate, the isnew 57745591b213SSam Leffler * param tells us if this is the first time or not. 57755591b213SSam Leffler */ 57765591b213SSam Leffler static void 5777e9962332SSam Leffler ath_newassoc(struct ieee80211_node *ni, int isnew) 57785591b213SSam Leffler { 5779b032f27cSSam Leffler struct ath_node *an = ATH_NODE(ni); 5780b032f27cSSam Leffler struct ieee80211vap *vap = ni->ni_vap; 5781b032f27cSSam Leffler struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; 5782b032f27cSSam Leffler const struct ieee80211_txparam *tp; 5783b032f27cSSam Leffler enum ieee80211_phymode mode; 57845591b213SSam Leffler 5785b032f27cSSam Leffler /* 5786b032f27cSSam Leffler * Deduce netband of station to simplify setting up xmit 5787b032f27cSSam Leffler * parameters. Note this allows us to assign different 5788b032f27cSSam Leffler * parameters to each station in a mixed bss (b/g, n/[abg]). 5789b032f27cSSam Leffler */ 5790b032f27cSSam Leffler if (ni->ni_flags & IEEE80211_NODE_HT) { 5791b032f27cSSam Leffler if (IEEE80211_IS_CHAN_5GHZ(ni->ni_chan)) 5792b032f27cSSam Leffler mode = IEEE80211_MODE_11NA; 5793b032f27cSSam Leffler else 5794b032f27cSSam Leffler mode = IEEE80211_MODE_11NG; 5795b032f27cSSam Leffler } else if (IEEE80211_IS_CHAN_A(ni->ni_chan)) 5796b032f27cSSam Leffler mode = IEEE80211_MODE_11A; 5797b032f27cSSam Leffler else if (ni->ni_flags & IEEE80211_NODE_ERP) 5798b032f27cSSam Leffler mode = IEEE80211_MODE_11G; 5799b032f27cSSam Leffler else 5800b032f27cSSam Leffler mode = IEEE80211_MODE_11B; 5801b032f27cSSam Leffler tp = &vap->iv_txparms[mode]; 5802b032f27cSSam Leffler an->an_tp = tp; 5803b032f27cSSam Leffler an->an_mcastrix = ath_tx_findrix(sc->sc_rates[mode], tp->mcastrate); 5804b032f27cSSam Leffler an->an_mgmtrix = ath_tx_findrix(sc->sc_rates[mode], tp->mgmtrate); 5805b032f27cSSam Leffler 5806b032f27cSSam Leffler ath_rate_newassoc(sc, an, isnew); 5807e8fd88a3SSam Leffler if (isnew && 5808b032f27cSSam Leffler (vap->iv_flags & IEEE80211_F_PRIVACY) == 0 && sc->sc_hasclrkey && 5809b032f27cSSam Leffler ni->ni_ucastkey.wk_keyix == IEEE80211_KEYIX_NONE) 5810e8fd88a3SSam Leffler ath_setup_stationkey(ni); 5811e8fd88a3SSam Leffler } 58125591b213SSam Leffler 58135591b213SSam Leffler static int 5814b032f27cSSam Leffler getchannels(struct ath_softc *sc, int *nchans, struct ieee80211_channel chans[], 5815b032f27cSSam Leffler int cc, int ecm, int outdoor) 58165591b213SSam Leffler { 58175591b213SSam Leffler struct ath_hal *ah = sc->sc_ah; 5818b032f27cSSam Leffler HAL_CHANNEL *halchans; 5819b032f27cSSam Leffler int i, nhalchans, error; 58205591b213SSam Leffler 5821b032f27cSSam Leffler halchans = malloc(IEEE80211_CHAN_MAX * sizeof(HAL_CHANNEL), 5822b032f27cSSam Leffler M_TEMP, M_NOWAIT | M_ZERO); 5823b032f27cSSam Leffler if (halchans == NULL) { 5824b032f27cSSam Leffler device_printf(sc->sc_dev, 5825b032f27cSSam Leffler "%s: unable to allocate channel table\n", __func__); 58265591b213SSam Leffler return ENOMEM; 58275591b213SSam Leffler } 5828b032f27cSSam Leffler error = 0; 5829b032f27cSSam Leffler if (!ath_hal_init_channels(ah, halchans, IEEE80211_CHAN_MAX, &nhalchans, 5830b032f27cSSam Leffler NULL, 0, NULL, CTRY_DEFAULT, HAL_MODE_ALL, AH_FALSE, AH_TRUE)) { 5831b032f27cSSam Leffler error = EINVAL; 5832b032f27cSSam Leffler goto done; 58335591b213SSam Leffler } 5834b032f27cSSam Leffler if (nchans == NULL) /* no table requested */ 5835b032f27cSSam Leffler goto done; 58365591b213SSam Leffler 58375591b213SSam Leffler /* 583868e8e04eSSam Leffler * Convert HAL channels to ieee80211 ones. 58395591b213SSam Leffler */ 5840b032f27cSSam Leffler for (i = 0; i < nhalchans; i++) { 5841b032f27cSSam Leffler HAL_CHANNEL *c = &halchans[i]; 5842b032f27cSSam Leffler struct ieee80211_channel *ichan = &chans[i]; 5843bd5a9920SSam Leffler 584468e8e04eSSam Leffler ichan->ic_ieee = ath_hal_mhz2ieee(ah, c->channel, 584568e8e04eSSam Leffler c->channelFlags); 5846724c193aSSam Leffler if (bootverbose) 5847b032f27cSSam Leffler device_printf(sc->sc_dev, "hal channel %u/%x -> %u " 5848b032f27cSSam Leffler "maxpow %d minpow %d maxreg %d\n", 5849b032f27cSSam Leffler c->channel, c->channelFlags, ichan->ic_ieee, 5850b032f27cSSam Leffler c->maxTxPower, c->minTxPower, c->maxRegTxPower); 585168e8e04eSSam Leffler ichan->ic_freq = c->channel; 585268e8e04eSSam Leffler 585368e8e04eSSam Leffler if ((c->channelFlags & CHANNEL_PUREG) == CHANNEL_PUREG) { 5854bd5a9920SSam Leffler /* 585568e8e04eSSam Leffler * Except for AR5211, HAL's PUREG means mixed 585668e8e04eSSam Leffler * DSSS and OFDM. 5857bd5a9920SSam Leffler */ 585868e8e04eSSam Leffler ichan->ic_flags = c->channelFlags &~ CHANNEL_PUREG; 585968e8e04eSSam Leffler ichan->ic_flags |= IEEE80211_CHAN_G; 586068e8e04eSSam Leffler } else { 586168e8e04eSSam Leffler ichan->ic_flags = c->channelFlags; 586268e8e04eSSam Leffler } 586368e8e04eSSam Leffler 5864724c193aSSam Leffler if (ath_hal_isgsmsku(ah)) { 5865724c193aSSam Leffler /* remap to true frequencies */ 586668e8e04eSSam Leffler ichan->ic_freq = 922 + (2422 - ichan->ic_freq); 586768e8e04eSSam Leffler ichan->ic_flags |= IEEE80211_CHAN_GSM; 586868e8e04eSSam Leffler ichan->ic_ieee = ieee80211_mhz2ieee(ichan->ic_freq, 586968e8e04eSSam Leffler ichan->ic_flags); 5870724c193aSSam Leffler } 587168e8e04eSSam Leffler ichan->ic_maxregpower = c->maxRegTxPower; /* dBm */ 5872b032f27cSSam Leffler /* XXX: old hal's don't provide maxTxPower for some parts */ 5873b032f27cSSam Leffler ichan->ic_maxpower = (c->maxTxPower != 0) ? 5874b032f27cSSam Leffler c->maxTxPower : 2*c->maxRegTxPower; /* 1/2 dBm */ 587568e8e04eSSam Leffler ichan->ic_minpower = c->minTxPower; /* 1/2 dBm */ 58765591b213SSam Leffler } 5877b032f27cSSam Leffler *nchans = nhalchans; 5878b032f27cSSam Leffler done: 5879b032f27cSSam Leffler free(halchans, M_TEMP); 5880b032f27cSSam Leffler return error; 5881b032f27cSSam Leffler } 5882b032f27cSSam Leffler 5883b032f27cSSam Leffler static int 5884b032f27cSSam Leffler ath_setregdomain(struct ieee80211com *ic, struct ieee80211_regdomain *rd, 5885b032f27cSSam Leffler int nchans, struct ieee80211_channel chans[]) 5886b032f27cSSam Leffler { 5887b032f27cSSam Leffler struct ath_softc *sc = ic->ic_ifp->if_softc; 5888b032f27cSSam Leffler struct ath_hal *ah = sc->sc_ah; 5889b032f27cSSam Leffler u_int32_t ord; 5890b032f27cSSam Leffler int error; 5891b032f27cSSam Leffler 5892b032f27cSSam Leffler (void) ath_hal_getregdomain(ah, &ord); 5893b032f27cSSam Leffler /* XXX map sku->rd */ 5894b032f27cSSam Leffler ath_hal_setregdomain(ah, rd->regdomain); 5895b032f27cSSam Leffler error = getchannels(sc, &nchans, chans, rd->country, 5896b032f27cSSam Leffler rd->ecm ? AH_TRUE : AH_FALSE, 5897b032f27cSSam Leffler rd->location == 'O' ? AH_TRUE : AH_FALSE); 5898b032f27cSSam Leffler if (error != 0) { 5899b032f27cSSam Leffler /* 5900b032f27cSSam Leffler * Restore previous state. 5901b032f27cSSam Leffler */ 5902b032f27cSSam Leffler ath_hal_setregdomain(ah, ord); 5903b032f27cSSam Leffler (void) getchannels(sc, NULL, NULL, ic->ic_regdomain.country, 5904b032f27cSSam Leffler ic->ic_regdomain.ecm ? AH_TRUE : AH_FALSE, 5905b032f27cSSam Leffler ic->ic_regdomain.location == 'O' ? AH_TRUE : AH_FALSE); 5906b032f27cSSam Leffler return error; 5907b032f27cSSam Leffler } 5908b032f27cSSam Leffler return 0; 5909b032f27cSSam Leffler } 5910b032f27cSSam Leffler 5911b032f27cSSam Leffler static void 5912b032f27cSSam Leffler ath_getradiocaps(struct ieee80211com *ic, 5913b032f27cSSam Leffler int *nchans, struct ieee80211_channel chans[]) 5914b032f27cSSam Leffler { 5915b032f27cSSam Leffler struct ath_softc *sc = ic->ic_ifp->if_softc; 5916b032f27cSSam Leffler struct ath_hal *ah = sc->sc_ah; 5917b032f27cSSam Leffler u_int32_t ord; 5918b032f27cSSam Leffler 5919b032f27cSSam Leffler (void) ath_hal_getregdomain(ah, &ord); 5920b032f27cSSam Leffler ath_hal_setregdomain(ah, 0); 5921b032f27cSSam Leffler /* XXX not quite right but close enough for now */ 5922b032f27cSSam Leffler getchannels(sc, nchans, chans, CTRY_DEBUG, AH_TRUE, AH_FALSE); 59237de3bc26SSam Leffler 59247de3bc26SSam Leffler /* NB: restore previous state */ 5925b032f27cSSam Leffler ath_hal_setregdomain(ah, ord); 59267de3bc26SSam Leffler (void) getchannels(sc, NULL, NULL, ic->ic_regdomain.country, 59277de3bc26SSam Leffler ic->ic_regdomain.ecm ? AH_TRUE : AH_FALSE, 59287de3bc26SSam Leffler ic->ic_regdomain.location == 'O' ? AH_TRUE : AH_FALSE); 5929b032f27cSSam Leffler } 5930b032f27cSSam Leffler 5931b032f27cSSam Leffler static int 5932b032f27cSSam Leffler ath_mapregdomain(struct ath_softc *sc, u_int32_t rd) 5933b032f27cSSam Leffler { 5934b032f27cSSam Leffler /* map Atheros rd's to SKU's */ 5935b032f27cSSam Leffler return rd; 5936b032f27cSSam Leffler } 5937b032f27cSSam Leffler 5938b032f27cSSam Leffler static int 5939b032f27cSSam Leffler ath_getchannels(struct ath_softc *sc) 5940b032f27cSSam Leffler { 5941b032f27cSSam Leffler struct ifnet *ifp = sc->sc_ifp; 5942b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 5943b032f27cSSam Leffler struct ath_hal *ah = sc->sc_ah; 5944b032f27cSSam Leffler int error; 5945b032f27cSSam Leffler 5946b032f27cSSam Leffler /* 5947b032f27cSSam Leffler * Convert HAL channels to ieee80211 ones. 5948b032f27cSSam Leffler */ 5949b032f27cSSam Leffler error = getchannels(sc, &ic->ic_nchans, ic->ic_channels, 5950b032f27cSSam Leffler CTRY_DEFAULT, AH_TRUE, AH_FALSE); 5951ca876918SSam Leffler (void) ath_hal_getregdomain(ah, &sc->sc_eerd); 5952ca876918SSam Leffler ath_hal_getcountrycode(ah, &sc->sc_eecc); /* NB: cannot fail */ 5953b032f27cSSam Leffler if (error) { 5954b032f27cSSam Leffler if_printf(ifp, "%s: unable to collect channel list from hal, " 5955b032f27cSSam Leffler "error %d\n", __func__, error); 5956b032f27cSSam Leffler if (error == EINVAL) { 5957b032f27cSSam Leffler if_printf(ifp, "%s: regdomain likely %u country code %u\n", 5958ca876918SSam Leffler __func__, sc->sc_eerd, sc->sc_eecc); 5959b032f27cSSam Leffler } 5960b032f27cSSam Leffler return error; 5961b032f27cSSam Leffler } 5962ca876918SSam Leffler ic->ic_regdomain.regdomain = ath_mapregdomain(sc, sc->sc_eerd); 5963ca876918SSam Leffler ic->ic_regdomain.country = sc->sc_eecc; 5964b032f27cSSam Leffler ic->ic_regdomain.ecm = 1; 5965b032f27cSSam Leffler ic->ic_regdomain.location = 'I'; 5966b032f27cSSam Leffler ic->ic_regdomain.isocc[0] = ' '; /* XXX don't know */ 5967b032f27cSSam Leffler ic->ic_regdomain.isocc[1] = ' '; 59685591b213SSam Leffler return 0; 59695591b213SSam Leffler } 59705591b213SSam Leffler 5971c42a7b7eSSam Leffler static void 59723e50ec2cSSam Leffler ath_led_done(void *arg) 5973c42a7b7eSSam Leffler { 59743e50ec2cSSam Leffler struct ath_softc *sc = arg; 59753e50ec2cSSam Leffler 59763e50ec2cSSam Leffler sc->sc_blinking = 0; 59773e50ec2cSSam Leffler } 5978c42a7b7eSSam Leffler 5979c42a7b7eSSam Leffler /* 59803e50ec2cSSam Leffler * Turn the LED off: flip the pin and then set a timer so no 59813e50ec2cSSam Leffler * update will happen for the specified duration. 5982c42a7b7eSSam Leffler */ 59833e50ec2cSSam Leffler static void 59843e50ec2cSSam Leffler ath_led_off(void *arg) 59853e50ec2cSSam Leffler { 59863e50ec2cSSam Leffler struct ath_softc *sc = arg; 59873e50ec2cSSam Leffler 59883e50ec2cSSam Leffler ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, !sc->sc_ledon); 59893e50ec2cSSam Leffler callout_reset(&sc->sc_ledtimer, sc->sc_ledoff, ath_led_done, sc); 5990c42a7b7eSSam Leffler } 59913e50ec2cSSam Leffler 59923e50ec2cSSam Leffler /* 59933e50ec2cSSam Leffler * Blink the LED according to the specified on/off times. 59943e50ec2cSSam Leffler */ 59953e50ec2cSSam Leffler static void 59963e50ec2cSSam Leffler ath_led_blink(struct ath_softc *sc, int on, int off) 59973e50ec2cSSam Leffler { 59983e50ec2cSSam Leffler DPRINTF(sc, ATH_DEBUG_LED, "%s: on %u off %u\n", __func__, on, off); 59993e50ec2cSSam Leffler ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, sc->sc_ledon); 60003e50ec2cSSam Leffler sc->sc_blinking = 1; 60013e50ec2cSSam Leffler sc->sc_ledoff = off; 60023e50ec2cSSam Leffler callout_reset(&sc->sc_ledtimer, on, ath_led_off, sc); 60033e50ec2cSSam Leffler } 60043e50ec2cSSam Leffler 60053e50ec2cSSam Leffler static void 60063e50ec2cSSam Leffler ath_led_event(struct ath_softc *sc, int event) 60073e50ec2cSSam Leffler { 60083e50ec2cSSam Leffler 60093e50ec2cSSam Leffler sc->sc_ledevent = ticks; /* time of last event */ 60103e50ec2cSSam Leffler if (sc->sc_blinking) /* don't interrupt active blink */ 60113e50ec2cSSam Leffler return; 60123e50ec2cSSam Leffler switch (event) { 60133e50ec2cSSam Leffler case ATH_LED_POLL: 60143e50ec2cSSam Leffler ath_led_blink(sc, sc->sc_hwmap[0].ledon, 60153e50ec2cSSam Leffler sc->sc_hwmap[0].ledoff); 60163e50ec2cSSam Leffler break; 60173e50ec2cSSam Leffler case ATH_LED_TX: 60183e50ec2cSSam Leffler ath_led_blink(sc, sc->sc_hwmap[sc->sc_txrate].ledon, 60193e50ec2cSSam Leffler sc->sc_hwmap[sc->sc_txrate].ledoff); 60203e50ec2cSSam Leffler break; 60213e50ec2cSSam Leffler case ATH_LED_RX: 60223e50ec2cSSam Leffler ath_led_blink(sc, sc->sc_hwmap[sc->sc_rxrate].ledon, 60233e50ec2cSSam Leffler sc->sc_hwmap[sc->sc_rxrate].ledoff); 60243e50ec2cSSam Leffler break; 6025c42a7b7eSSam Leffler } 6026c42a7b7eSSam Leffler } 6027c42a7b7eSSam Leffler 60286c4612b9SSam Leffler static int 60296c4612b9SSam Leffler ath_rate_setup(struct ath_softc *sc, u_int mode) 60306c4612b9SSam Leffler { 60316c4612b9SSam Leffler struct ath_hal *ah = sc->sc_ah; 60326c4612b9SSam Leffler const HAL_RATE_TABLE *rt; 60336c4612b9SSam Leffler 60346c4612b9SSam Leffler switch (mode) { 60356c4612b9SSam Leffler case IEEE80211_MODE_11A: 60366c4612b9SSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_11A); 60376c4612b9SSam Leffler break; 6038724c193aSSam Leffler case IEEE80211_MODE_HALF: 6039aaa70f2fSSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_11A_HALF_RATE); 6040aaa70f2fSSam Leffler break; 6041724c193aSSam Leffler case IEEE80211_MODE_QUARTER: 6042aaa70f2fSSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_11A_QUARTER_RATE); 6043aaa70f2fSSam Leffler break; 60446c4612b9SSam Leffler case IEEE80211_MODE_11B: 60456c4612b9SSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_11B); 60466c4612b9SSam Leffler break; 60476c4612b9SSam Leffler case IEEE80211_MODE_11G: 60486c4612b9SSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_11G); 60496c4612b9SSam Leffler break; 60506c4612b9SSam Leffler case IEEE80211_MODE_TURBO_A: 605168e8e04eSSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_108A); 60520e8c5adfSSam Leffler #if HAL_ABI_VERSION < 0x07013100 60530e8c5adfSSam Leffler if (rt == NULL) /* XXX bandaid for old hal's */ 60540e8c5adfSSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_TURBO); 60550e8c5adfSSam Leffler #endif 60566c4612b9SSam Leffler break; 60576c4612b9SSam Leffler case IEEE80211_MODE_TURBO_G: 60586c4612b9SSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_108G); 60596c4612b9SSam Leffler break; 606068e8e04eSSam Leffler case IEEE80211_MODE_STURBO_A: 606168e8e04eSSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_TURBO); 606268e8e04eSSam Leffler break; 606368e8e04eSSam Leffler case IEEE80211_MODE_11NA: 606468e8e04eSSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_11NA_HT20); 606568e8e04eSSam Leffler break; 606668e8e04eSSam Leffler case IEEE80211_MODE_11NG: 606768e8e04eSSam Leffler rt = ath_hal_getratetable(ah, HAL_MODE_11NG_HT20); 606868e8e04eSSam Leffler break; 60696c4612b9SSam Leffler default: 60706c4612b9SSam Leffler DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid mode %u\n", 60716c4612b9SSam Leffler __func__, mode); 60726c4612b9SSam Leffler return 0; 60736c4612b9SSam Leffler } 60746c4612b9SSam Leffler sc->sc_rates[mode] = rt; 6075aaa70f2fSSam Leffler return (rt != NULL); 60765591b213SSam Leffler } 60775591b213SSam Leffler 60785591b213SSam Leffler static void 60795591b213SSam Leffler ath_setcurmode(struct ath_softc *sc, enum ieee80211_phymode mode) 60805591b213SSam Leffler { 60813e50ec2cSSam Leffler #define N(a) (sizeof(a)/sizeof(a[0])) 60823e50ec2cSSam Leffler /* NB: on/off times from the Atheros NDIS driver, w/ permission */ 60833e50ec2cSSam Leffler static const struct { 60843e50ec2cSSam Leffler u_int rate; /* tx/rx 802.11 rate */ 60853e50ec2cSSam Leffler u_int16_t timeOn; /* LED on time (ms) */ 60863e50ec2cSSam Leffler u_int16_t timeOff; /* LED off time (ms) */ 60873e50ec2cSSam Leffler } blinkrates[] = { 60883e50ec2cSSam Leffler { 108, 40, 10 }, 60893e50ec2cSSam Leffler { 96, 44, 11 }, 60903e50ec2cSSam Leffler { 72, 50, 13 }, 60913e50ec2cSSam Leffler { 48, 57, 14 }, 60923e50ec2cSSam Leffler { 36, 67, 16 }, 60933e50ec2cSSam Leffler { 24, 80, 20 }, 60943e50ec2cSSam Leffler { 22, 100, 25 }, 60953e50ec2cSSam Leffler { 18, 133, 34 }, 60963e50ec2cSSam Leffler { 12, 160, 40 }, 60973e50ec2cSSam Leffler { 10, 200, 50 }, 60983e50ec2cSSam Leffler { 6, 240, 58 }, 60993e50ec2cSSam Leffler { 4, 267, 66 }, 61003e50ec2cSSam Leffler { 2, 400, 100 }, 61013e50ec2cSSam Leffler { 0, 500, 130 }, 6102724c193aSSam Leffler /* XXX half/quarter rates */ 61033e50ec2cSSam Leffler }; 61045591b213SSam Leffler const HAL_RATE_TABLE *rt; 61053e50ec2cSSam Leffler int i, j; 61065591b213SSam Leffler 61075591b213SSam Leffler memset(sc->sc_rixmap, 0xff, sizeof(sc->sc_rixmap)); 61085591b213SSam Leffler rt = sc->sc_rates[mode]; 61095591b213SSam Leffler KASSERT(rt != NULL, ("no h/w rate set for phy mode %u", mode)); 61105591b213SSam Leffler for (i = 0; i < rt->rateCount; i++) 61115591b213SSam Leffler sc->sc_rixmap[rt->info[i].dot11Rate & IEEE80211_RATE_VAL] = i; 61121b1a8e41SSam Leffler memset(sc->sc_hwmap, 0, sizeof(sc->sc_hwmap)); 6113c42a7b7eSSam Leffler for (i = 0; i < 32; i++) { 6114c42a7b7eSSam Leffler u_int8_t ix = rt->rateCodeToIndex[i]; 61153e50ec2cSSam Leffler if (ix == 0xff) { 61163e50ec2cSSam Leffler sc->sc_hwmap[i].ledon = (500 * hz) / 1000; 61173e50ec2cSSam Leffler sc->sc_hwmap[i].ledoff = (130 * hz) / 1000; 611816b4851aSSam Leffler continue; 61193e50ec2cSSam Leffler } 61203e50ec2cSSam Leffler sc->sc_hwmap[i].ieeerate = 61213e50ec2cSSam Leffler rt->info[ix].dot11Rate & IEEE80211_RATE_VAL; 612268e8e04eSSam Leffler if (rt->info[ix].phy == IEEE80211_T_HT) 612368e8e04eSSam Leffler sc->sc_hwmap[i].ieeerate |= 0x80; /* MCS */ 6124d3be6f5bSSam Leffler sc->sc_hwmap[i].txflags = IEEE80211_RADIOTAP_F_DATAPAD; 612516b4851aSSam Leffler if (rt->info[ix].shortPreamble || 612616b4851aSSam Leffler rt->info[ix].phy == IEEE80211_T_OFDM) 6127d3be6f5bSSam Leffler sc->sc_hwmap[i].txflags |= IEEE80211_RADIOTAP_F_SHORTPRE; 6128d3be6f5bSSam Leffler /* NB: receive frames include FCS */ 6129d3be6f5bSSam Leffler sc->sc_hwmap[i].rxflags = sc->sc_hwmap[i].txflags | 6130d3be6f5bSSam Leffler IEEE80211_RADIOTAP_F_FCS; 61313e50ec2cSSam Leffler /* setup blink rate table to avoid per-packet lookup */ 61323e50ec2cSSam Leffler for (j = 0; j < N(blinkrates)-1; j++) 61333e50ec2cSSam Leffler if (blinkrates[j].rate == sc->sc_hwmap[i].ieeerate) 61343e50ec2cSSam Leffler break; 61353e50ec2cSSam Leffler /* NB: this uses the last entry if the rate isn't found */ 61363e50ec2cSSam Leffler /* XXX beware of overlow */ 61373e50ec2cSSam Leffler sc->sc_hwmap[i].ledon = (blinkrates[j].timeOn * hz) / 1000; 61383e50ec2cSSam Leffler sc->sc_hwmap[i].ledoff = (blinkrates[j].timeOff * hz) / 1000; 6139c42a7b7eSSam Leffler } 61405591b213SSam Leffler sc->sc_currates = rt; 61415591b213SSam Leffler sc->sc_curmode = mode; 61425591b213SSam Leffler /* 6143c42a7b7eSSam Leffler * All protection frames are transmited at 2Mb/s for 6144c42a7b7eSSam Leffler * 11g, otherwise at 1Mb/s. 61455591b213SSam Leffler */ 6146913a1ba1SSam Leffler if (mode == IEEE80211_MODE_11G) 6147913a1ba1SSam Leffler sc->sc_protrix = ath_tx_findrix(rt, 2*2); 6148913a1ba1SSam Leffler else 6149913a1ba1SSam Leffler sc->sc_protrix = ath_tx_findrix(rt, 2*1); 6150c42a7b7eSSam Leffler /* NB: caller is responsible for reseting rate control state */ 61513e50ec2cSSam Leffler #undef N 61525591b213SSam Leffler } 61535591b213SSam Leffler 6154a585a9a1SSam Leffler #ifdef ATH_DEBUG 61555591b213SSam Leffler static void 615665f9edeeSSam Leffler ath_printrxbuf(const struct ath_buf *bf, u_int ix, int done) 61575591b213SSam Leffler { 615865f9edeeSSam Leffler const struct ath_rx_status *rs = &bf->bf_status.ds_rxstat; 615965f9edeeSSam Leffler const struct ath_desc *ds; 61605591b213SSam Leffler int i; 61615591b213SSam Leffler 61625591b213SSam Leffler for (i = 0, ds = bf->bf_desc; i < bf->bf_nseg; i++, ds++) { 61637a4c5ed9SSam Leffler printf("R[%2u] (DS.V:%p DS.P:%p) L:%08x D:%08x%s\n" 61647a4c5ed9SSam Leffler " %08x %08x %08x %08x\n", 616565f9edeeSSam Leffler ix, ds, (const struct ath_desc *)bf->bf_daddr + i, 61665591b213SSam Leffler ds->ds_link, ds->ds_data, 616765f9edeeSSam Leffler !done ? "" : (rs->rs_status == 0) ? " *" : " !", 61685591b213SSam Leffler ds->ds_ctl0, ds->ds_ctl1, 61697a4c5ed9SSam Leffler ds->ds_hw[0], ds->ds_hw[1]); 61705591b213SSam Leffler } 61715591b213SSam Leffler } 61725591b213SSam Leffler 61735591b213SSam Leffler static void 617465f9edeeSSam Leffler ath_printtxbuf(const struct ath_buf *bf, u_int qnum, u_int ix, int done) 61755591b213SSam Leffler { 617665f9edeeSSam Leffler const struct ath_tx_status *ts = &bf->bf_status.ds_txstat; 617765f9edeeSSam Leffler const struct ath_desc *ds; 61785591b213SSam Leffler int i; 61795591b213SSam Leffler 61807a4c5ed9SSam Leffler printf("Q%u[%3u]", qnum, ix); 61815591b213SSam Leffler for (i = 0, ds = bf->bf_desc; i < bf->bf_nseg; i++, ds++) { 6182ebecf802SSam Leffler printf(" (DS.V:%p DS.P:%p) L:%08x D:%08x F:04%x%s\n" 61837a4c5ed9SSam Leffler " %08x %08x %08x %08x %08x %08x\n", 618465f9edeeSSam Leffler ds, (const struct ath_desc *)bf->bf_daddr + i, 6185ebecf802SSam Leffler ds->ds_link, ds->ds_data, bf->bf_flags, 618665f9edeeSSam Leffler !done ? "" : (ts->ts_status == 0) ? " *" : " !", 61875591b213SSam Leffler ds->ds_ctl0, ds->ds_ctl1, 61887a4c5ed9SSam Leffler ds->ds_hw[0], ds->ds_hw[1], ds->ds_hw[2], ds->ds_hw[3]); 61895591b213SSam Leffler } 61905591b213SSam Leffler } 6191a585a9a1SSam Leffler #endif /* ATH_DEBUG */ 6192c42a7b7eSSam Leffler 6193c42a7b7eSSam Leffler static void 6194c42a7b7eSSam Leffler ath_watchdog(struct ifnet *ifp) 6195c42a7b7eSSam Leffler { 6196c42a7b7eSSam Leffler struct ath_softc *sc = ifp->if_softc; 6197c42a7b7eSSam Leffler 619868e8e04eSSam Leffler if ((ifp->if_drv_flags & IFF_DRV_RUNNING) && !sc->sc_invalid) { 6199c42a7b7eSSam Leffler if_printf(ifp, "device timeout\n"); 6200c42a7b7eSSam Leffler ath_reset(ifp); 6201c42a7b7eSSam Leffler ifp->if_oerrors++; 6202c42a7b7eSSam Leffler sc->sc_stats.ast_watchdog++; 6203c42a7b7eSSam Leffler } 6204c42a7b7eSSam Leffler } 6205c42a7b7eSSam Leffler 6206a585a9a1SSam Leffler #ifdef ATH_DIAGAPI 6207c42a7b7eSSam Leffler /* 6208c42a7b7eSSam Leffler * Diagnostic interface to the HAL. This is used by various 6209c42a7b7eSSam Leffler * tools to do things like retrieve register contents for 6210c42a7b7eSSam Leffler * debugging. The mechanism is intentionally opaque so that 6211c42a7b7eSSam Leffler * it can change frequently w/o concern for compatiblity. 6212c42a7b7eSSam Leffler */ 6213c42a7b7eSSam Leffler static int 6214c42a7b7eSSam Leffler ath_ioctl_diag(struct ath_softc *sc, struct ath_diag *ad) 6215c42a7b7eSSam Leffler { 6216c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 6217c42a7b7eSSam Leffler u_int id = ad->ad_id & ATH_DIAG_ID; 6218c42a7b7eSSam Leffler void *indata = NULL; 6219c42a7b7eSSam Leffler void *outdata = NULL; 6220c42a7b7eSSam Leffler u_int32_t insize = ad->ad_in_size; 6221c42a7b7eSSam Leffler u_int32_t outsize = ad->ad_out_size; 6222c42a7b7eSSam Leffler int error = 0; 6223c42a7b7eSSam Leffler 6224c42a7b7eSSam Leffler if (ad->ad_id & ATH_DIAG_IN) { 6225c42a7b7eSSam Leffler /* 6226c42a7b7eSSam Leffler * Copy in data. 6227c42a7b7eSSam Leffler */ 6228c42a7b7eSSam Leffler indata = malloc(insize, M_TEMP, M_NOWAIT); 6229c42a7b7eSSam Leffler if (indata == NULL) { 6230c42a7b7eSSam Leffler error = ENOMEM; 6231c42a7b7eSSam Leffler goto bad; 6232c42a7b7eSSam Leffler } 6233c42a7b7eSSam Leffler error = copyin(ad->ad_in_data, indata, insize); 6234c42a7b7eSSam Leffler if (error) 6235c42a7b7eSSam Leffler goto bad; 6236c42a7b7eSSam Leffler } 6237c42a7b7eSSam Leffler if (ad->ad_id & ATH_DIAG_DYN) { 6238c42a7b7eSSam Leffler /* 6239c42a7b7eSSam Leffler * Allocate a buffer for the results (otherwise the HAL 6240c42a7b7eSSam Leffler * returns a pointer to a buffer where we can read the 6241c42a7b7eSSam Leffler * results). Note that we depend on the HAL leaving this 6242c42a7b7eSSam Leffler * pointer for us to use below in reclaiming the buffer; 6243c42a7b7eSSam Leffler * may want to be more defensive. 6244c42a7b7eSSam Leffler */ 6245c42a7b7eSSam Leffler outdata = malloc(outsize, M_TEMP, M_NOWAIT); 6246c42a7b7eSSam Leffler if (outdata == NULL) { 6247c42a7b7eSSam Leffler error = ENOMEM; 6248c42a7b7eSSam Leffler goto bad; 6249c42a7b7eSSam Leffler } 6250c42a7b7eSSam Leffler } 6251c42a7b7eSSam Leffler if (ath_hal_getdiagstate(ah, id, indata, insize, &outdata, &outsize)) { 6252c42a7b7eSSam Leffler if (outsize < ad->ad_out_size) 6253c42a7b7eSSam Leffler ad->ad_out_size = outsize; 6254c42a7b7eSSam Leffler if (outdata != NULL) 6255c42a7b7eSSam Leffler error = copyout(outdata, ad->ad_out_data, 6256c42a7b7eSSam Leffler ad->ad_out_size); 6257c42a7b7eSSam Leffler } else { 6258c42a7b7eSSam Leffler error = EINVAL; 6259c42a7b7eSSam Leffler } 6260c42a7b7eSSam Leffler bad: 6261c42a7b7eSSam Leffler if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL) 6262c42a7b7eSSam Leffler free(indata, M_TEMP); 6263c42a7b7eSSam Leffler if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL) 6264c42a7b7eSSam Leffler free(outdata, M_TEMP); 6265c42a7b7eSSam Leffler return error; 6266c42a7b7eSSam Leffler } 6267a585a9a1SSam Leffler #endif /* ATH_DIAGAPI */ 6268c42a7b7eSSam Leffler 6269c42a7b7eSSam Leffler static int 6270c42a7b7eSSam Leffler ath_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 6271c42a7b7eSSam Leffler { 6272c42a7b7eSSam Leffler #define IS_RUNNING(ifp) \ 627313f4c340SRobert Watson ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING)) 6274c42a7b7eSSam Leffler struct ath_softc *sc = ifp->if_softc; 6275b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 6276c42a7b7eSSam Leffler struct ifreq *ifr = (struct ifreq *)data; 6277c42a7b7eSSam Leffler int error = 0; 6278c42a7b7eSSam Leffler 6279c42a7b7eSSam Leffler switch (cmd) { 6280c42a7b7eSSam Leffler case SIOCSIFFLAGS: 628131a8c1edSAndrew Thompson ATH_LOCK(sc); 6282c42a7b7eSSam Leffler if (IS_RUNNING(ifp)) { 6283c42a7b7eSSam Leffler /* 6284c42a7b7eSSam Leffler * To avoid rescanning another access point, 6285c42a7b7eSSam Leffler * do not call ath_init() here. Instead, 6286c42a7b7eSSam Leffler * only reflect promisc mode settings. 6287c42a7b7eSSam Leffler */ 6288c42a7b7eSSam Leffler ath_mode_init(sc); 6289c42a7b7eSSam Leffler } else if (ifp->if_flags & IFF_UP) { 6290c42a7b7eSSam Leffler /* 6291c42a7b7eSSam Leffler * Beware of being called during attach/detach 6292c42a7b7eSSam Leffler * to reset promiscuous mode. In that case we 6293c42a7b7eSSam Leffler * will still be marked UP but not RUNNING. 6294c42a7b7eSSam Leffler * However trying to re-init the interface 6295c42a7b7eSSam Leffler * is the wrong thing to do as we've already 6296c42a7b7eSSam Leffler * torn down much of our state. There's 6297c42a7b7eSSam Leffler * probably a better way to deal with this. 6298c42a7b7eSSam Leffler */ 6299b032f27cSSam Leffler if (!sc->sc_invalid) 6300fc74a9f9SBrooks Davis ath_init(sc); /* XXX lose error */ 6301c42a7b7eSSam Leffler } else 6302c42a7b7eSSam Leffler ath_stop_locked(ifp); 630331a8c1edSAndrew Thompson ATH_UNLOCK(sc); 6304c42a7b7eSSam Leffler break; 6305b032f27cSSam Leffler case SIOCGIFMEDIA: 6306b032f27cSSam Leffler case SIOCSIFMEDIA: 6307b032f27cSSam Leffler error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); 6308b032f27cSSam Leffler break; 6309c42a7b7eSSam Leffler case SIOCGATHSTATS: 6310c42a7b7eSSam Leffler /* NB: embed these numbers to get a consistent view */ 6311c42a7b7eSSam Leffler sc->sc_stats.ast_tx_packets = ifp->if_opackets; 6312c42a7b7eSSam Leffler sc->sc_stats.ast_rx_packets = ifp->if_ipackets; 6313b032f27cSSam Leffler #if 0 631468e8e04eSSam Leffler ieee80211_getsignal(ic, &sc->sc_stats.ast_rx_rssi, 631568e8e04eSSam Leffler &sc->sc_stats.ast_rx_noise); 6316b032f27cSSam Leffler #endif 63176bf62dd1SSam Leffler sc->sc_stats.ast_tx_rate = sc->sc_hwmap[sc->sc_txrate].ieeerate; 6318c42a7b7eSSam Leffler return copyout(&sc->sc_stats, 6319c42a7b7eSSam Leffler ifr->ifr_data, sizeof (sc->sc_stats)); 6320a585a9a1SSam Leffler #ifdef ATH_DIAGAPI 6321c42a7b7eSSam Leffler case SIOCGATHDIAG: 6322c42a7b7eSSam Leffler error = ath_ioctl_diag(sc, (struct ath_diag *) ifr); 6323c42a7b7eSSam Leffler break; 6324a585a9a1SSam Leffler #endif 632531a8c1edSAndrew Thompson case SIOCGIFADDR: 6326b032f27cSSam Leffler error = ether_ioctl(ifp, cmd, data); 6327c42a7b7eSSam Leffler break; 632831a8c1edSAndrew Thompson default: 632931a8c1edSAndrew Thompson error = EINVAL; 633031a8c1edSAndrew Thompson break; 6331c42a7b7eSSam Leffler } 6332c42a7b7eSSam Leffler return error; 6333a614e076SSam Leffler #undef IS_RUNNING 6334c42a7b7eSSam Leffler } 6335c42a7b7eSSam Leffler 6336c42a7b7eSSam Leffler static int 6337c42a7b7eSSam Leffler ath_sysctl_slottime(SYSCTL_HANDLER_ARGS) 6338c42a7b7eSSam Leffler { 6339c42a7b7eSSam Leffler struct ath_softc *sc = arg1; 6340c42a7b7eSSam Leffler u_int slottime = ath_hal_getslottime(sc->sc_ah); 6341c42a7b7eSSam Leffler int error; 6342c42a7b7eSSam Leffler 6343c42a7b7eSSam Leffler error = sysctl_handle_int(oidp, &slottime, 0, req); 6344c42a7b7eSSam Leffler if (error || !req->newptr) 6345c42a7b7eSSam Leffler return error; 6346c42a7b7eSSam Leffler return !ath_hal_setslottime(sc->sc_ah, slottime) ? EINVAL : 0; 6347c42a7b7eSSam Leffler } 6348c42a7b7eSSam Leffler 6349c42a7b7eSSam Leffler static int 6350c42a7b7eSSam Leffler ath_sysctl_acktimeout(SYSCTL_HANDLER_ARGS) 6351c42a7b7eSSam Leffler { 6352c42a7b7eSSam Leffler struct ath_softc *sc = arg1; 6353c42a7b7eSSam Leffler u_int acktimeout = ath_hal_getacktimeout(sc->sc_ah); 6354c42a7b7eSSam Leffler int error; 6355c42a7b7eSSam Leffler 6356c42a7b7eSSam Leffler error = sysctl_handle_int(oidp, &acktimeout, 0, req); 6357c42a7b7eSSam Leffler if (error || !req->newptr) 6358c42a7b7eSSam Leffler return error; 6359c42a7b7eSSam Leffler return !ath_hal_setacktimeout(sc->sc_ah, acktimeout) ? EINVAL : 0; 6360c42a7b7eSSam Leffler } 6361c42a7b7eSSam Leffler 6362c42a7b7eSSam Leffler static int 6363c42a7b7eSSam Leffler ath_sysctl_ctstimeout(SYSCTL_HANDLER_ARGS) 6364c42a7b7eSSam Leffler { 6365c42a7b7eSSam Leffler struct ath_softc *sc = arg1; 6366c42a7b7eSSam Leffler u_int ctstimeout = ath_hal_getctstimeout(sc->sc_ah); 6367c42a7b7eSSam Leffler int error; 6368c42a7b7eSSam Leffler 6369c42a7b7eSSam Leffler error = sysctl_handle_int(oidp, &ctstimeout, 0, req); 6370c42a7b7eSSam Leffler if (error || !req->newptr) 6371c42a7b7eSSam Leffler return error; 6372c42a7b7eSSam Leffler return !ath_hal_setctstimeout(sc->sc_ah, ctstimeout) ? EINVAL : 0; 6373c42a7b7eSSam Leffler } 6374c42a7b7eSSam Leffler 6375c42a7b7eSSam Leffler static int 6376c42a7b7eSSam Leffler ath_sysctl_softled(SYSCTL_HANDLER_ARGS) 6377c42a7b7eSSam Leffler { 6378c42a7b7eSSam Leffler struct ath_softc *sc = arg1; 6379c42a7b7eSSam Leffler int softled = sc->sc_softled; 6380c42a7b7eSSam Leffler int error; 6381c42a7b7eSSam Leffler 6382c42a7b7eSSam Leffler error = sysctl_handle_int(oidp, &softled, 0, req); 6383c42a7b7eSSam Leffler if (error || !req->newptr) 6384c42a7b7eSSam Leffler return error; 63853e50ec2cSSam Leffler softled = (softled != 0); 6386c42a7b7eSSam Leffler if (softled != sc->sc_softled) { 63873e50ec2cSSam Leffler if (softled) { 63883e50ec2cSSam Leffler /* NB: handle any sc_ledpin change */ 6389c42a7b7eSSam Leffler ath_hal_gpioCfgOutput(sc->sc_ah, sc->sc_ledpin); 63903e50ec2cSSam Leffler ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, 63913e50ec2cSSam Leffler !sc->sc_ledon); 63923e50ec2cSSam Leffler } 6393c42a7b7eSSam Leffler sc->sc_softled = softled; 6394c42a7b7eSSam Leffler } 6395c42a7b7eSSam Leffler return 0; 6396c42a7b7eSSam Leffler } 6397c42a7b7eSSam Leffler 6398c42a7b7eSSam Leffler static int 6399b298baf2SSam Leffler ath_sysctl_ledpin(SYSCTL_HANDLER_ARGS) 6400b298baf2SSam Leffler { 6401b298baf2SSam Leffler struct ath_softc *sc = arg1; 6402b298baf2SSam Leffler int ledpin = sc->sc_ledpin; 6403b298baf2SSam Leffler int error; 6404b298baf2SSam Leffler 6405b298baf2SSam Leffler error = sysctl_handle_int(oidp, &ledpin, 0, req); 6406b298baf2SSam Leffler if (error || !req->newptr) 6407b298baf2SSam Leffler return error; 6408b298baf2SSam Leffler if (ledpin != sc->sc_ledpin) { 6409b298baf2SSam Leffler sc->sc_ledpin = ledpin; 6410b298baf2SSam Leffler if (sc->sc_softled) { 6411b298baf2SSam Leffler ath_hal_gpioCfgOutput(sc->sc_ah, sc->sc_ledpin); 6412b298baf2SSam Leffler ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, 6413b298baf2SSam Leffler !sc->sc_ledon); 6414b298baf2SSam Leffler } 6415b298baf2SSam Leffler } 6416b298baf2SSam Leffler return 0; 6417b298baf2SSam Leffler } 6418b298baf2SSam Leffler 6419b298baf2SSam Leffler static int 64208debcae4SSam Leffler ath_sysctl_txantenna(SYSCTL_HANDLER_ARGS) 64218debcae4SSam Leffler { 64228debcae4SSam Leffler struct ath_softc *sc = arg1; 64238debcae4SSam Leffler u_int txantenna = ath_hal_getantennaswitch(sc->sc_ah); 64248debcae4SSam Leffler int error; 64258debcae4SSam Leffler 64268debcae4SSam Leffler error = sysctl_handle_int(oidp, &txantenna, 0, req); 64278debcae4SSam Leffler if (!error && req->newptr) { 64288debcae4SSam Leffler /* XXX assumes 2 antenna ports */ 64298debcae4SSam Leffler if (txantenna < HAL_ANT_VARIABLE || txantenna > HAL_ANT_FIXED_B) 64308debcae4SSam Leffler return EINVAL; 64318debcae4SSam Leffler ath_hal_setantennaswitch(sc->sc_ah, txantenna); 64328debcae4SSam Leffler /* 64338debcae4SSam Leffler * NB: with the switch locked this isn't meaningful, 64348debcae4SSam Leffler * but set it anyway so things like radiotap get 64358debcae4SSam Leffler * consistent info in their data. 64368debcae4SSam Leffler */ 64378debcae4SSam Leffler sc->sc_txantenna = txantenna; 64388debcae4SSam Leffler } 64398debcae4SSam Leffler return error; 64408debcae4SSam Leffler } 64418debcae4SSam Leffler 64428debcae4SSam Leffler static int 6443c42a7b7eSSam Leffler ath_sysctl_rxantenna(SYSCTL_HANDLER_ARGS) 6444c42a7b7eSSam Leffler { 6445c42a7b7eSSam Leffler struct ath_softc *sc = arg1; 6446c42a7b7eSSam Leffler u_int defantenna = ath_hal_getdefantenna(sc->sc_ah); 6447c42a7b7eSSam Leffler int error; 6448c42a7b7eSSam Leffler 6449c42a7b7eSSam Leffler error = sysctl_handle_int(oidp, &defantenna, 0, req); 6450c42a7b7eSSam Leffler if (!error && req->newptr) 6451c42a7b7eSSam Leffler ath_hal_setdefantenna(sc->sc_ah, defantenna); 6452c42a7b7eSSam Leffler return error; 6453c42a7b7eSSam Leffler } 6454c42a7b7eSSam Leffler 6455c42a7b7eSSam Leffler static int 6456c42a7b7eSSam Leffler ath_sysctl_diversity(SYSCTL_HANDLER_ARGS) 6457c42a7b7eSSam Leffler { 6458c42a7b7eSSam Leffler struct ath_softc *sc = arg1; 6459c59005e9SSam Leffler u_int diversity = ath_hal_getdiversity(sc->sc_ah); 6460c42a7b7eSSam Leffler int error; 6461c42a7b7eSSam Leffler 6462c42a7b7eSSam Leffler error = sysctl_handle_int(oidp, &diversity, 0, req); 6463c42a7b7eSSam Leffler if (error || !req->newptr) 6464c42a7b7eSSam Leffler return error; 6465c59005e9SSam Leffler if (!ath_hal_setdiversity(sc->sc_ah, diversity)) 6466c59005e9SSam Leffler return EINVAL; 6467c42a7b7eSSam Leffler sc->sc_diversity = diversity; 6468c59005e9SSam Leffler return 0; 6469c42a7b7eSSam Leffler } 6470c42a7b7eSSam Leffler 6471c42a7b7eSSam Leffler static int 6472c42a7b7eSSam Leffler ath_sysctl_diag(SYSCTL_HANDLER_ARGS) 6473c42a7b7eSSam Leffler { 6474c42a7b7eSSam Leffler struct ath_softc *sc = arg1; 6475c42a7b7eSSam Leffler u_int32_t diag; 6476c42a7b7eSSam Leffler int error; 6477c42a7b7eSSam Leffler 6478c42a7b7eSSam Leffler if (!ath_hal_getdiag(sc->sc_ah, &diag)) 6479c42a7b7eSSam Leffler return EINVAL; 6480c42a7b7eSSam Leffler error = sysctl_handle_int(oidp, &diag, 0, req); 6481c42a7b7eSSam Leffler if (error || !req->newptr) 6482c42a7b7eSSam Leffler return error; 6483c42a7b7eSSam Leffler return !ath_hal_setdiag(sc->sc_ah, diag) ? EINVAL : 0; 6484c42a7b7eSSam Leffler } 6485c42a7b7eSSam Leffler 6486c42a7b7eSSam Leffler static int 6487c42a7b7eSSam Leffler ath_sysctl_tpscale(SYSCTL_HANDLER_ARGS) 6488c42a7b7eSSam Leffler { 6489c42a7b7eSSam Leffler struct ath_softc *sc = arg1; 6490fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 6491c42a7b7eSSam Leffler u_int32_t scale; 6492c42a7b7eSSam Leffler int error; 6493c42a7b7eSSam Leffler 6494ee7d6840SSam Leffler (void) ath_hal_gettpscale(sc->sc_ah, &scale); 6495c42a7b7eSSam Leffler error = sysctl_handle_int(oidp, &scale, 0, req); 6496c42a7b7eSSam Leffler if (error || !req->newptr) 6497c42a7b7eSSam Leffler return error; 649816d84e01SSam Leffler return !ath_hal_settpscale(sc->sc_ah, scale) ? EINVAL : 649916d84e01SSam Leffler (ifp->if_drv_flags & IFF_DRV_RUNNING) ? ath_reset(ifp) : 0; 6500c42a7b7eSSam Leffler } 6501c42a7b7eSSam Leffler 6502c42a7b7eSSam Leffler static int 6503c42a7b7eSSam Leffler ath_sysctl_tpc(SYSCTL_HANDLER_ARGS) 6504c42a7b7eSSam Leffler { 6505c42a7b7eSSam Leffler struct ath_softc *sc = arg1; 6506c42a7b7eSSam Leffler u_int tpc = ath_hal_gettpc(sc->sc_ah); 6507c42a7b7eSSam Leffler int error; 6508c42a7b7eSSam Leffler 6509c42a7b7eSSam Leffler error = sysctl_handle_int(oidp, &tpc, 0, req); 6510c42a7b7eSSam Leffler if (error || !req->newptr) 6511c42a7b7eSSam Leffler return error; 6512c42a7b7eSSam Leffler return !ath_hal_settpc(sc->sc_ah, tpc) ? EINVAL : 0; 6513c42a7b7eSSam Leffler } 6514c42a7b7eSSam Leffler 651517f3f177SSam Leffler static int 6516bd5a9920SSam Leffler ath_sysctl_rfkill(SYSCTL_HANDLER_ARGS) 6517bd5a9920SSam Leffler { 6518bd5a9920SSam Leffler struct ath_softc *sc = arg1; 651916d84e01SSam Leffler struct ifnet *ifp = sc->sc_ifp; 6520bd5a9920SSam Leffler struct ath_hal *ah = sc->sc_ah; 6521bd5a9920SSam Leffler u_int rfkill = ath_hal_getrfkill(ah); 6522bd5a9920SSam Leffler int error; 6523bd5a9920SSam Leffler 6524bd5a9920SSam Leffler error = sysctl_handle_int(oidp, &rfkill, 0, req); 6525bd5a9920SSam Leffler if (error || !req->newptr) 6526bd5a9920SSam Leffler return error; 6527bd5a9920SSam Leffler if (rfkill == ath_hal_getrfkill(ah)) /* unchanged */ 6528bd5a9920SSam Leffler return 0; 652916d84e01SSam Leffler if (!ath_hal_setrfkill(ah, rfkill)) 6530bd5a9920SSam Leffler return EINVAL; 653116d84e01SSam Leffler return (ifp->if_drv_flags & IFF_DRV_RUNNING) ? ath_reset(ifp) : 0; 6532bd5a9920SSam Leffler } 6533bd5a9920SSam Leffler 6534bd5a9920SSam Leffler static int 6535bd5a9920SSam Leffler ath_sysctl_rfsilent(SYSCTL_HANDLER_ARGS) 6536bd5a9920SSam Leffler { 6537bd5a9920SSam Leffler struct ath_softc *sc = arg1; 6538bd5a9920SSam Leffler u_int rfsilent; 6539bd5a9920SSam Leffler int error; 6540bd5a9920SSam Leffler 6541ee7d6840SSam Leffler (void) ath_hal_getrfsilent(sc->sc_ah, &rfsilent); 6542bd5a9920SSam Leffler error = sysctl_handle_int(oidp, &rfsilent, 0, req); 6543bd5a9920SSam Leffler if (error || !req->newptr) 6544bd5a9920SSam Leffler return error; 6545bd5a9920SSam Leffler if (!ath_hal_setrfsilent(sc->sc_ah, rfsilent)) 6546bd5a9920SSam Leffler return EINVAL; 6547bd5a9920SSam Leffler sc->sc_rfsilentpin = rfsilent & 0x1c; 6548bd5a9920SSam Leffler sc->sc_rfsilentpol = (rfsilent & 0x2) != 0; 6549bd5a9920SSam Leffler return 0; 6550bd5a9920SSam Leffler } 6551bd5a9920SSam Leffler 6552bd5a9920SSam Leffler static int 6553bd5a9920SSam Leffler ath_sysctl_tpack(SYSCTL_HANDLER_ARGS) 6554bd5a9920SSam Leffler { 6555bd5a9920SSam Leffler struct ath_softc *sc = arg1; 6556bd5a9920SSam Leffler u_int32_t tpack; 6557bd5a9920SSam Leffler int error; 6558bd5a9920SSam Leffler 6559ee7d6840SSam Leffler (void) ath_hal_gettpack(sc->sc_ah, &tpack); 6560bd5a9920SSam Leffler error = sysctl_handle_int(oidp, &tpack, 0, req); 6561bd5a9920SSam Leffler if (error || !req->newptr) 6562bd5a9920SSam Leffler return error; 6563bd5a9920SSam Leffler return !ath_hal_settpack(sc->sc_ah, tpack) ? EINVAL : 0; 6564bd5a9920SSam Leffler } 6565bd5a9920SSam Leffler 6566bd5a9920SSam Leffler static int 6567bd5a9920SSam Leffler ath_sysctl_tpcts(SYSCTL_HANDLER_ARGS) 6568bd5a9920SSam Leffler { 6569bd5a9920SSam Leffler struct ath_softc *sc = arg1; 6570bd5a9920SSam Leffler u_int32_t tpcts; 6571bd5a9920SSam Leffler int error; 6572bd5a9920SSam Leffler 6573ee7d6840SSam Leffler (void) ath_hal_gettpcts(sc->sc_ah, &tpcts); 6574bd5a9920SSam Leffler error = sysctl_handle_int(oidp, &tpcts, 0, req); 6575bd5a9920SSam Leffler if (error || !req->newptr) 6576bd5a9920SSam Leffler return error; 6577bd5a9920SSam Leffler return !ath_hal_settpcts(sc->sc_ah, tpcts) ? EINVAL : 0; 6578bd5a9920SSam Leffler } 6579bd5a9920SSam Leffler 6580c42a7b7eSSam Leffler static void 6581c42a7b7eSSam Leffler ath_sysctlattach(struct ath_softc *sc) 6582c42a7b7eSSam Leffler { 6583c42a7b7eSSam Leffler struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); 6584c42a7b7eSSam Leffler struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); 6585c59005e9SSam Leffler struct ath_hal *ah = sc->sc_ah; 6586c42a7b7eSSam Leffler 6587ca876918SSam Leffler SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6588ca876918SSam Leffler "countrycode", CTLFLAG_RD, &sc->sc_eecc, 0, 6589ca876918SSam Leffler "EEPROM country code"); 6590ca876918SSam Leffler SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6591ca876918SSam Leffler "regdomain", CTLFLAG_RD, &sc->sc_eerd, 0, 6592ca876918SSam Leffler "EEPROM regdomain code"); 6593a585a9a1SSam Leffler #ifdef ATH_DEBUG 6594c42a7b7eSSam Leffler sc->sc_debug = ath_debug; 6595c42a7b7eSSam Leffler SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6596c42a7b7eSSam Leffler "debug", CTLFLAG_RW, &sc->sc_debug, 0, 6597c42a7b7eSSam Leffler "control debugging printfs"); 6598d2f6ed15SSam Leffler #endif 6599c42a7b7eSSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6600c42a7b7eSSam Leffler "slottime", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 6601c42a7b7eSSam Leffler ath_sysctl_slottime, "I", "802.11 slot time (us)"); 6602c42a7b7eSSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6603c42a7b7eSSam Leffler "acktimeout", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 6604c42a7b7eSSam Leffler ath_sysctl_acktimeout, "I", "802.11 ACK timeout (us)"); 6605c42a7b7eSSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6606c42a7b7eSSam Leffler "ctstimeout", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 6607c42a7b7eSSam Leffler ath_sysctl_ctstimeout, "I", "802.11 CTS timeout (us)"); 6608c42a7b7eSSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6609c42a7b7eSSam Leffler "softled", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 6610c42a7b7eSSam Leffler ath_sysctl_softled, "I", "enable/disable software LED support"); 6611b298baf2SSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6612b298baf2SSam Leffler "ledpin", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 6613b298baf2SSam Leffler ath_sysctl_ledpin, "I", "GPIO pin connected to LED"); 6614c42a7b7eSSam Leffler SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 66153e50ec2cSSam Leffler "ledon", CTLFLAG_RW, &sc->sc_ledon, 0, 66163e50ec2cSSam Leffler "setting to turn LED on"); 66173e50ec2cSSam Leffler SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 66183e50ec2cSSam Leffler "ledidle", CTLFLAG_RW, &sc->sc_ledidle, 0, 66193e50ec2cSSam Leffler "idle time for inactivity LED (ticks)"); 66208debcae4SSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 66218debcae4SSam Leffler "txantenna", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 66228debcae4SSam Leffler ath_sysctl_txantenna, "I", "antenna switch"); 6623c42a7b7eSSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6624c42a7b7eSSam Leffler "rxantenna", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 6625c42a7b7eSSam Leffler ath_sysctl_rxantenna, "I", "default/rx antenna"); 6626c59005e9SSam Leffler if (ath_hal_hasdiversity(ah)) 6627c42a7b7eSSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6628c42a7b7eSSam Leffler "diversity", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 6629c42a7b7eSSam Leffler ath_sysctl_diversity, "I", "antenna diversity"); 6630c42a7b7eSSam Leffler sc->sc_txintrperiod = ATH_TXINTR_PERIOD; 6631c42a7b7eSSam Leffler SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6632c42a7b7eSSam Leffler "txintrperiod", CTLFLAG_RW, &sc->sc_txintrperiod, 0, 6633c42a7b7eSSam Leffler "tx descriptor batching"); 6634c42a7b7eSSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6635c42a7b7eSSam Leffler "diag", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 6636c42a7b7eSSam Leffler ath_sysctl_diag, "I", "h/w diagnostic control"); 6637c42a7b7eSSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6638c42a7b7eSSam Leffler "tpscale", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 6639c42a7b7eSSam Leffler ath_sysctl_tpscale, "I", "tx power scaling"); 6640bd5a9920SSam Leffler if (ath_hal_hastpc(ah)) { 6641c42a7b7eSSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6642c42a7b7eSSam Leffler "tpc", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 6643c42a7b7eSSam Leffler ath_sysctl_tpc, "I", "enable/disable per-packet TPC"); 6644bd5a9920SSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6645bd5a9920SSam Leffler "tpack", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 6646bd5a9920SSam Leffler ath_sysctl_tpack, "I", "tx power for ack frames"); 6647bd5a9920SSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6648bd5a9920SSam Leffler "tpcts", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 6649bd5a9920SSam Leffler ath_sysctl_tpcts, "I", "tx power for cts frames"); 6650bd5a9920SSam Leffler } 665168e8e04eSSam Leffler if (ath_hal_hasfastframes(sc->sc_ah)) { 665268e8e04eSSam Leffler sc->sc_fftxqmin = ATH_FF_TXQMIN; 665368e8e04eSSam Leffler SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 665468e8e04eSSam Leffler "fftxqmin", CTLFLAG_RW, &sc->sc_fftxqmin, 0, 665568e8e04eSSam Leffler "min frames before fast-frame staging"); 665668e8e04eSSam Leffler sc->sc_fftxqmax = ATH_FF_TXQMAX; 665768e8e04eSSam Leffler SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 665868e8e04eSSam Leffler "fftxqmax", CTLFLAG_RW, &sc->sc_fftxqmax, 0, 665968e8e04eSSam Leffler "max queued frames before tail drop"); 666068e8e04eSSam Leffler } 6661bd5a9920SSam Leffler if (ath_hal_hasrfsilent(ah)) { 6662bd5a9920SSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6663bd5a9920SSam Leffler "rfsilent", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 6664bd5a9920SSam Leffler ath_sysctl_rfsilent, "I", "h/w RF silent config"); 6665bd5a9920SSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 6666bd5a9920SSam Leffler "rfkill", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 6667bd5a9920SSam Leffler ath_sysctl_rfkill, "I", "enable/disable RF kill switch"); 6668bd5a9920SSam Leffler } 66697b0c77ecSSam Leffler sc->sc_monpass = HAL_RXERR_DECRYPT | HAL_RXERR_MIC; 66707b0c77ecSSam Leffler SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 66717b0c77ecSSam Leffler "monpass", CTLFLAG_RW, &sc->sc_monpass, 0, 66727b0c77ecSSam Leffler "mask of error frames to pass when monitoring"); 6673c42a7b7eSSam Leffler } 6674c42a7b7eSSam Leffler 6675c42a7b7eSSam Leffler static void 6676c42a7b7eSSam Leffler ath_bpfattach(struct ath_softc *sc) 6677c42a7b7eSSam Leffler { 6678fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 6679c42a7b7eSSam Leffler 6680b032f27cSSam Leffler bpfattach(ifp, DLT_IEEE802_11_RADIO, 6681b032f27cSSam Leffler sizeof(struct ieee80211_frame) + sizeof(sc->sc_tx_th)); 6682c42a7b7eSSam Leffler /* 6683c42a7b7eSSam Leffler * Initialize constant fields. 6684c42a7b7eSSam Leffler * XXX make header lengths a multiple of 32-bits so subsequent 6685c42a7b7eSSam Leffler * headers are properly aligned; this is a kludge to keep 6686c42a7b7eSSam Leffler * certain applications happy. 6687c42a7b7eSSam Leffler * 6688c42a7b7eSSam Leffler * NB: the channel is setup each time we transition to the 6689c42a7b7eSSam Leffler * RUN state to avoid filling it in for each frame. 6690c42a7b7eSSam Leffler */ 6691c42a7b7eSSam Leffler sc->sc_tx_th_len = roundup(sizeof(sc->sc_tx_th), sizeof(u_int32_t)); 6692c42a7b7eSSam Leffler sc->sc_tx_th.wt_ihdr.it_len = htole16(sc->sc_tx_th_len); 6693c42a7b7eSSam Leffler sc->sc_tx_th.wt_ihdr.it_present = htole32(ATH_TX_RADIOTAP_PRESENT); 6694c42a7b7eSSam Leffler 6695d3be6f5bSSam Leffler sc->sc_rx_th_len = roundup(sizeof(sc->sc_rx_th), sizeof(u_int32_t)); 6696d3be6f5bSSam Leffler sc->sc_rx_th.wr_ihdr.it_len = htole16(sc->sc_rx_th_len); 6697c42a7b7eSSam Leffler sc->sc_rx_th.wr_ihdr.it_present = htole32(ATH_RX_RADIOTAP_PRESENT); 6698c42a7b7eSSam Leffler } 6699c42a7b7eSSam Leffler 6700664443d0SSam Leffler static int 6701664443d0SSam Leffler ath_tx_raw_start(struct ath_softc *sc, struct ieee80211_node *ni, 6702664443d0SSam Leffler struct ath_buf *bf, struct mbuf *m0, 6703664443d0SSam Leffler const struct ieee80211_bpf_params *params) 6704664443d0SSam Leffler { 6705b032f27cSSam Leffler struct ifnet *ifp = sc->sc_ifp; 6706b032f27cSSam Leffler struct ieee80211com *ic = ifp->if_l2com; 6707664443d0SSam Leffler struct ath_hal *ah = sc->sc_ah; 6708664443d0SSam Leffler int error, ismcast, ismrr; 6709664443d0SSam Leffler int hdrlen, pktlen, try0, txantenna; 6710664443d0SSam Leffler u_int8_t rix, cix, txrate, ctsrate, rate1, rate2, rate3; 6711664443d0SSam Leffler struct ieee80211_frame *wh; 6712664443d0SSam Leffler u_int flags, ctsduration; 6713664443d0SSam Leffler HAL_PKT_TYPE atype; 6714664443d0SSam Leffler const HAL_RATE_TABLE *rt; 6715664443d0SSam Leffler struct ath_desc *ds; 6716664443d0SSam Leffler u_int pri; 6717664443d0SSam Leffler 6718664443d0SSam Leffler wh = mtod(m0, struct ieee80211_frame *); 6719664443d0SSam Leffler ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); 6720664443d0SSam Leffler hdrlen = ieee80211_anyhdrsize(wh); 6721664443d0SSam Leffler /* 6722664443d0SSam Leffler * Packet length must not include any 6723664443d0SSam Leffler * pad bytes; deduct them here. 6724664443d0SSam Leffler */ 6725664443d0SSam Leffler /* XXX honor IEEE80211_BPF_DATAPAD */ 6726664443d0SSam Leffler pktlen = m0->m_pkthdr.len - (hdrlen & 3) + IEEE80211_CRC_LEN; 6727664443d0SSam Leffler 6728664443d0SSam Leffler error = ath_tx_dmasetup(sc, bf, m0); 6729664443d0SSam Leffler if (error != 0) 6730664443d0SSam Leffler return error; 6731664443d0SSam Leffler m0 = bf->bf_m; /* NB: may have changed */ 6732664443d0SSam Leffler wh = mtod(m0, struct ieee80211_frame *); 6733664443d0SSam Leffler bf->bf_node = ni; /* NB: held reference */ 6734664443d0SSam Leffler 6735664443d0SSam Leffler flags = HAL_TXDESC_CLRDMASK; /* XXX needed for crypto errs */ 6736664443d0SSam Leffler flags |= HAL_TXDESC_INTREQ; /* force interrupt */ 6737664443d0SSam Leffler if (params->ibp_flags & IEEE80211_BPF_RTS) 6738664443d0SSam Leffler flags |= HAL_TXDESC_RTSENA; 6739664443d0SSam Leffler else if (params->ibp_flags & IEEE80211_BPF_CTS) 6740664443d0SSam Leffler flags |= HAL_TXDESC_CTSENA; 6741664443d0SSam Leffler /* XXX leave ismcast to injector? */ 6742664443d0SSam Leffler if ((params->ibp_flags & IEEE80211_BPF_NOACK) || ismcast) 6743664443d0SSam Leffler flags |= HAL_TXDESC_NOACK; 6744664443d0SSam Leffler 6745664443d0SSam Leffler rt = sc->sc_currates; 6746664443d0SSam Leffler KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode)); 6747664443d0SSam Leffler rix = ath_tx_findrix(rt, params->ibp_rate0); 6748664443d0SSam Leffler txrate = rt->info[rix].rateCode; 6749664443d0SSam Leffler if (params->ibp_flags & IEEE80211_BPF_SHORTPRE) 6750664443d0SSam Leffler txrate |= rt->info[rix].shortPreamble; 67516bf62dd1SSam Leffler sc->sc_txrate = txrate; 6752664443d0SSam Leffler try0 = params->ibp_try0; 6753664443d0SSam Leffler ismrr = (params->ibp_try1 != 0); 6754664443d0SSam Leffler txantenna = params->ibp_pri >> 2; 6755664443d0SSam Leffler if (txantenna == 0) /* XXX? */ 6756664443d0SSam Leffler txantenna = sc->sc_txantenna; 6757664443d0SSam Leffler ctsduration = 0; 6758664443d0SSam Leffler if (flags & (HAL_TXDESC_CTSENA | HAL_TXDESC_RTSENA)) { 6759664443d0SSam Leffler cix = ath_tx_findrix(rt, params->ibp_ctsrate); 6760664443d0SSam Leffler ctsrate = rt->info[cix].rateCode; 6761664443d0SSam Leffler if (params->ibp_flags & IEEE80211_BPF_SHORTPRE) { 6762664443d0SSam Leffler ctsrate |= rt->info[cix].shortPreamble; 6763664443d0SSam Leffler if (flags & HAL_TXDESC_RTSENA) /* SIFS + CTS */ 6764664443d0SSam Leffler ctsduration += rt->info[cix].spAckDuration; 6765664443d0SSam Leffler ctsduration += ath_hal_computetxtime(ah, 6766664443d0SSam Leffler rt, pktlen, rix, AH_TRUE); 6767664443d0SSam Leffler if ((flags & HAL_TXDESC_NOACK) == 0) /* SIFS + ACK */ 6768664443d0SSam Leffler ctsduration += rt->info[rix].spAckDuration; 6769664443d0SSam Leffler } else { 6770664443d0SSam Leffler if (flags & HAL_TXDESC_RTSENA) /* SIFS + CTS */ 6771664443d0SSam Leffler ctsduration += rt->info[cix].lpAckDuration; 6772664443d0SSam Leffler ctsduration += ath_hal_computetxtime(ah, 6773664443d0SSam Leffler rt, pktlen, rix, AH_FALSE); 6774664443d0SSam Leffler if ((flags & HAL_TXDESC_NOACK) == 0) /* SIFS + ACK */ 6775664443d0SSam Leffler ctsduration += rt->info[rix].lpAckDuration; 6776664443d0SSam Leffler } 6777664443d0SSam Leffler ismrr = 0; /* XXX */ 6778664443d0SSam Leffler } else 6779664443d0SSam Leffler ctsrate = 0; 6780664443d0SSam Leffler pri = params->ibp_pri & 3; 6781664443d0SSam Leffler /* 6782664443d0SSam Leffler * NB: we mark all packets as type PSPOLL so the h/w won't 6783664443d0SSam Leffler * set the sequence number, duration, etc. 6784664443d0SSam Leffler */ 6785664443d0SSam Leffler atype = HAL_PKT_TYPE_PSPOLL; 6786664443d0SSam Leffler 6787664443d0SSam Leffler if (IFF_DUMPPKTS(sc, ATH_DEBUG_XMIT)) 678868e8e04eSSam Leffler ieee80211_dump_pkt(ic, mtod(m0, caddr_t), m0->m_len, 6789664443d0SSam Leffler sc->sc_hwmap[txrate].ieeerate, -1); 6790664443d0SSam Leffler 6791b032f27cSSam Leffler if (bpf_peers_present(ifp->if_bpf)) { 6792664443d0SSam Leffler u_int64_t tsf = ath_hal_gettsf64(ah); 6793664443d0SSam Leffler 6794664443d0SSam Leffler sc->sc_tx_th.wt_tsf = htole64(tsf); 6795664443d0SSam Leffler sc->sc_tx_th.wt_flags = sc->sc_hwmap[txrate].txflags; 6796664443d0SSam Leffler if (wh->i_fc[1] & IEEE80211_FC1_WEP) 6797664443d0SSam Leffler sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_WEP; 6798664443d0SSam Leffler sc->sc_tx_th.wt_rate = sc->sc_hwmap[txrate].ieeerate; 6799664443d0SSam Leffler sc->sc_tx_th.wt_txpower = ni->ni_txpower; 6800664443d0SSam Leffler sc->sc_tx_th.wt_antenna = sc->sc_txantenna; 6801664443d0SSam Leffler 6802b032f27cSSam Leffler bpf_mtap2(ifp->if_bpf, &sc->sc_tx_th, sc->sc_tx_th_len, m0); 6803664443d0SSam Leffler } 6804664443d0SSam Leffler 6805664443d0SSam Leffler /* 6806664443d0SSam Leffler * Formulate first tx descriptor with tx controls. 6807664443d0SSam Leffler */ 6808664443d0SSam Leffler ds = bf->bf_desc; 6809664443d0SSam Leffler /* XXX check return value? */ 6810664443d0SSam Leffler ath_hal_setuptxdesc(ah, ds 6811664443d0SSam Leffler , pktlen /* packet length */ 6812664443d0SSam Leffler , hdrlen /* header length */ 6813664443d0SSam Leffler , atype /* Atheros packet type */ 6814664443d0SSam Leffler , params->ibp_power /* txpower */ 6815664443d0SSam Leffler , txrate, try0 /* series 0 rate/tries */ 6816664443d0SSam Leffler , HAL_TXKEYIX_INVALID /* key cache index */ 6817664443d0SSam Leffler , txantenna /* antenna mode */ 6818664443d0SSam Leffler , flags /* flags */ 6819664443d0SSam Leffler , ctsrate /* rts/cts rate */ 6820664443d0SSam Leffler , ctsduration /* rts/cts duration */ 6821664443d0SSam Leffler ); 6822664443d0SSam Leffler bf->bf_flags = flags; 6823664443d0SSam Leffler 6824664443d0SSam Leffler if (ismrr) { 6825664443d0SSam Leffler rix = ath_tx_findrix(rt, params->ibp_rate1); 6826664443d0SSam Leffler rate1 = rt->info[rix].rateCode; 6827664443d0SSam Leffler if (params->ibp_flags & IEEE80211_BPF_SHORTPRE) 6828664443d0SSam Leffler rate1 |= rt->info[rix].shortPreamble; 6829664443d0SSam Leffler if (params->ibp_try2) { 6830664443d0SSam Leffler rix = ath_tx_findrix(rt, params->ibp_rate2); 6831664443d0SSam Leffler rate2 = rt->info[rix].rateCode; 6832664443d0SSam Leffler if (params->ibp_flags & IEEE80211_BPF_SHORTPRE) 6833664443d0SSam Leffler rate2 |= rt->info[rix].shortPreamble; 6834664443d0SSam Leffler } else 6835664443d0SSam Leffler rate2 = 0; 6836664443d0SSam Leffler if (params->ibp_try3) { 6837664443d0SSam Leffler rix = ath_tx_findrix(rt, params->ibp_rate3); 6838664443d0SSam Leffler rate3 = rt->info[rix].rateCode; 6839664443d0SSam Leffler if (params->ibp_flags & IEEE80211_BPF_SHORTPRE) 6840664443d0SSam Leffler rate3 |= rt->info[rix].shortPreamble; 6841664443d0SSam Leffler } else 6842664443d0SSam Leffler rate3 = 0; 6843664443d0SSam Leffler ath_hal_setupxtxdesc(ah, ds 6844664443d0SSam Leffler , rate1, params->ibp_try1 /* series 1 */ 6845664443d0SSam Leffler , rate2, params->ibp_try2 /* series 2 */ 6846664443d0SSam Leffler , rate3, params->ibp_try3 /* series 3 */ 6847664443d0SSam Leffler ); 6848664443d0SSam Leffler } 6849664443d0SSam Leffler 6850b032f27cSSam Leffler /* NB: no buffered multicast in power save support */ 6851b032f27cSSam Leffler ath_tx_handoff(sc, sc->sc_ac2q[pri], bf); 6852664443d0SSam Leffler return 0; 6853664443d0SSam Leffler } 6854664443d0SSam Leffler 6855664443d0SSam Leffler static int 6856664443d0SSam Leffler ath_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, 6857664443d0SSam Leffler const struct ieee80211_bpf_params *params) 6858664443d0SSam Leffler { 6859664443d0SSam Leffler struct ieee80211com *ic = ni->ni_ic; 6860664443d0SSam Leffler struct ifnet *ifp = ic->ic_ifp; 6861664443d0SSam Leffler struct ath_softc *sc = ifp->if_softc; 6862664443d0SSam Leffler struct ath_buf *bf; 6863664443d0SSam Leffler 6864664443d0SSam Leffler if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || sc->sc_invalid) { 6865b03cfe23SSepherosa Ziehau ieee80211_free_node(ni); 6866664443d0SSam Leffler m_freem(m); 6867664443d0SSam Leffler return ENETDOWN; 6868664443d0SSam Leffler } 6869664443d0SSam Leffler /* 6870664443d0SSam Leffler * Grab a TX buffer and associated resources. 6871664443d0SSam Leffler */ 6872664443d0SSam Leffler ATH_TXBUF_LOCK(sc); 6873664443d0SSam Leffler bf = STAILQ_FIRST(&sc->sc_txbuf); 6874664443d0SSam Leffler if (bf != NULL) 6875664443d0SSam Leffler STAILQ_REMOVE_HEAD(&sc->sc_txbuf, bf_list); 6876664443d0SSam Leffler ATH_TXBUF_UNLOCK(sc); 6877664443d0SSam Leffler if (bf == NULL) { 6878664443d0SSam Leffler DPRINTF(sc, ATH_DEBUG_XMIT, "%s: out of xmit buffers\n", 6879664443d0SSam Leffler __func__); 6880664443d0SSam Leffler sc->sc_stats.ast_tx_qstop++; 6881664443d0SSam Leffler ifp->if_drv_flags |= IFF_DRV_OACTIVE; 6882b03cfe23SSepherosa Ziehau ieee80211_free_node(ni); 6883664443d0SSam Leffler m_freem(m); 6884664443d0SSam Leffler return ENOBUFS; 6885664443d0SSam Leffler } 6886664443d0SSam Leffler 6887664443d0SSam Leffler ifp->if_opackets++; 6888664443d0SSam Leffler sc->sc_stats.ast_tx_raw++; 6889664443d0SSam Leffler 6890664443d0SSam Leffler if (params == NULL) { 6891664443d0SSam Leffler /* 6892664443d0SSam Leffler * Legacy path; interpret frame contents to decide 6893664443d0SSam Leffler * precisely how to send the frame. 6894664443d0SSam Leffler */ 6895664443d0SSam Leffler if (ath_tx_start(sc, ni, bf, m)) 6896664443d0SSam Leffler goto bad; 6897664443d0SSam Leffler } else { 6898664443d0SSam Leffler /* 6899664443d0SSam Leffler * Caller supplied explicit parameters to use in 6900664443d0SSam Leffler * sending the frame. 6901664443d0SSam Leffler */ 6902664443d0SSam Leffler if (ath_tx_raw_start(sc, ni, bf, m, params)) 6903664443d0SSam Leffler goto bad; 6904664443d0SSam Leffler } 690568e8e04eSSam Leffler ifp->if_timer = 5; 6906664443d0SSam Leffler 6907664443d0SSam Leffler return 0; 6908664443d0SSam Leffler bad: 6909664443d0SSam Leffler ifp->if_oerrors++; 6910664443d0SSam Leffler ATH_TXBUF_LOCK(sc); 6911664443d0SSam Leffler STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); 6912664443d0SSam Leffler ATH_TXBUF_UNLOCK(sc); 6913664443d0SSam Leffler ieee80211_free_node(ni); 6914664443d0SSam Leffler return EIO; /* XXX */ 6915664443d0SSam Leffler } 6916664443d0SSam Leffler 6917c42a7b7eSSam Leffler /* 6918c42a7b7eSSam Leffler * Announce various information on device/driver attach. 6919c42a7b7eSSam Leffler */ 6920c42a7b7eSSam Leffler static void 6921c42a7b7eSSam Leffler ath_announce(struct ath_softc *sc) 6922c42a7b7eSSam Leffler { 6923c42a7b7eSSam Leffler #define HAL_MODE_DUALBAND (HAL_MODE_11A|HAL_MODE_11B) 6924fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 6925c42a7b7eSSam Leffler struct ath_hal *ah = sc->sc_ah; 6926c42a7b7eSSam Leffler u_int modes, cc; 6927c42a7b7eSSam Leffler 6928c42a7b7eSSam Leffler if_printf(ifp, "mac %d.%d phy %d.%d", 6929c42a7b7eSSam Leffler ah->ah_macVersion, ah->ah_macRev, 6930c42a7b7eSSam Leffler ah->ah_phyRev >> 4, ah->ah_phyRev & 0xf); 6931c42a7b7eSSam Leffler /* 6932c42a7b7eSSam Leffler * Print radio revision(s). We check the wireless modes 6933c42a7b7eSSam Leffler * to avoid falsely printing revs for inoperable parts. 6934c42a7b7eSSam Leffler * Dual-band radio revs are returned in the 5Ghz rev number. 6935c42a7b7eSSam Leffler */ 6936c42a7b7eSSam Leffler ath_hal_getcountrycode(ah, &cc); 6937c42a7b7eSSam Leffler modes = ath_hal_getwirelessmodes(ah, cc); 6938c42a7b7eSSam Leffler if ((modes & HAL_MODE_DUALBAND) == HAL_MODE_DUALBAND) { 6939c42a7b7eSSam Leffler if (ah->ah_analog5GhzRev && ah->ah_analog2GhzRev) 6940c42a7b7eSSam Leffler printf(" 5ghz radio %d.%d 2ghz radio %d.%d", 6941c42a7b7eSSam Leffler ah->ah_analog5GhzRev >> 4, 6942c42a7b7eSSam Leffler ah->ah_analog5GhzRev & 0xf, 6943c42a7b7eSSam Leffler ah->ah_analog2GhzRev >> 4, 6944c42a7b7eSSam Leffler ah->ah_analog2GhzRev & 0xf); 6945c42a7b7eSSam Leffler else 6946c42a7b7eSSam Leffler printf(" radio %d.%d", ah->ah_analog5GhzRev >> 4, 6947c42a7b7eSSam Leffler ah->ah_analog5GhzRev & 0xf); 6948c42a7b7eSSam Leffler } else 6949c42a7b7eSSam Leffler printf(" radio %d.%d", ah->ah_analog5GhzRev >> 4, 6950c42a7b7eSSam Leffler ah->ah_analog5GhzRev & 0xf); 6951c42a7b7eSSam Leffler printf("\n"); 6952c42a7b7eSSam Leffler if (bootverbose) { 6953c42a7b7eSSam Leffler int i; 6954c42a7b7eSSam Leffler for (i = 0; i <= WME_AC_VO; i++) { 6955c42a7b7eSSam Leffler struct ath_txq *txq = sc->sc_ac2q[i]; 6956c42a7b7eSSam Leffler if_printf(ifp, "Use hw queue %u for %s traffic\n", 6957c42a7b7eSSam Leffler txq->axq_qnum, ieee80211_wme_acnames[i]); 6958c42a7b7eSSam Leffler } 6959c42a7b7eSSam Leffler if_printf(ifp, "Use hw queue %u for CAB traffic\n", 6960c42a7b7eSSam Leffler sc->sc_cabq->axq_qnum); 6961c42a7b7eSSam Leffler if_printf(ifp, "Use hw queue %u for beacons\n", sc->sc_bhalq); 6962c42a7b7eSSam Leffler } 6963e2d787faSSam Leffler if (ath_rxbuf != ATH_RXBUF) 6964e2d787faSSam Leffler if_printf(ifp, "using %u rx buffers\n", ath_rxbuf); 6965e2d787faSSam Leffler if (ath_txbuf != ATH_TXBUF) 6966e2d787faSSam Leffler if_printf(ifp, "using %u tx buffers\n", ath_txbuf); 6967c42a7b7eSSam Leffler #undef HAL_MODE_DUALBAND 6968c42a7b7eSSam Leffler } 6969