1b45de1ebSAdrian Chadd /*- 2b45de1ebSAdrian Chadd * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting 3b45de1ebSAdrian Chadd * All rights reserved. 4b45de1ebSAdrian Chadd * 5b45de1ebSAdrian Chadd * Redistribution and use in source and binary forms, with or without 6b45de1ebSAdrian Chadd * modification, are permitted provided that the following conditions 7b45de1ebSAdrian Chadd * are met: 8b45de1ebSAdrian Chadd * 1. Redistributions of source code must retain the above copyright 9b45de1ebSAdrian Chadd * notice, this list of conditions and the following disclaimer, 10b45de1ebSAdrian Chadd * without modification. 11b45de1ebSAdrian Chadd * 2. Redistributions in binary form must reproduce at minimum a disclaimer 12b45de1ebSAdrian Chadd * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 13b45de1ebSAdrian Chadd * redistribution must be conditioned upon including a substantially 14b45de1ebSAdrian Chadd * similar Disclaimer requirement for further binary redistribution. 15b45de1ebSAdrian Chadd * 16b45de1ebSAdrian Chadd * NO WARRANTY 17b45de1ebSAdrian Chadd * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18b45de1ebSAdrian Chadd * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19b45de1ebSAdrian Chadd * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 20b45de1ebSAdrian Chadd * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 21b45de1ebSAdrian Chadd * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 22b45de1ebSAdrian Chadd * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23b45de1ebSAdrian Chadd * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24b45de1ebSAdrian Chadd * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 25b45de1ebSAdrian Chadd * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26b45de1ebSAdrian Chadd * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27b45de1ebSAdrian Chadd * THE POSSIBILITY OF SUCH DAMAGES. 28b45de1ebSAdrian Chadd */ 29b45de1ebSAdrian Chadd 30b45de1ebSAdrian Chadd #include <sys/cdefs.h> 31b45de1ebSAdrian Chadd __FBSDID("$FreeBSD$"); 32b45de1ebSAdrian Chadd 33b45de1ebSAdrian Chadd /* 34b45de1ebSAdrian Chadd * Driver for the Atheros Wireless LAN controller. 35b45de1ebSAdrian Chadd * 36b45de1ebSAdrian Chadd * This software is derived from work of Atsushi Onoe; his contribution 37b45de1ebSAdrian Chadd * is greatly appreciated. 38b45de1ebSAdrian Chadd */ 39b45de1ebSAdrian Chadd 40b45de1ebSAdrian Chadd #include "opt_inet.h" 41b45de1ebSAdrian Chadd #include "opt_ath.h" 42b45de1ebSAdrian Chadd /* 43b45de1ebSAdrian Chadd * This is needed for register operations which are performed 44b45de1ebSAdrian Chadd * by the driver - eg, calls to ath_hal_gettsf32(). 45b45de1ebSAdrian Chadd * 46b45de1ebSAdrian Chadd * It's also required for any AH_DEBUG checks in here, eg the 47b45de1ebSAdrian Chadd * module dependencies. 48b45de1ebSAdrian Chadd */ 49b45de1ebSAdrian Chadd #include "opt_ah.h" 50b45de1ebSAdrian Chadd #include "opt_wlan.h" 51b45de1ebSAdrian Chadd 52b45de1ebSAdrian Chadd #include <sys/param.h> 53b45de1ebSAdrian Chadd #include <sys/systm.h> 54b45de1ebSAdrian Chadd #include <sys/sysctl.h> 55b45de1ebSAdrian Chadd #include <sys/mbuf.h> 56b45de1ebSAdrian Chadd #include <sys/malloc.h> 57b45de1ebSAdrian Chadd #include <sys/lock.h> 58b45de1ebSAdrian Chadd #include <sys/mutex.h> 59b45de1ebSAdrian Chadd #include <sys/kernel.h> 60b45de1ebSAdrian Chadd #include <sys/socket.h> 61b45de1ebSAdrian Chadd #include <sys/sockio.h> 62b45de1ebSAdrian Chadd #include <sys/errno.h> 63b45de1ebSAdrian Chadd #include <sys/callout.h> 64b45de1ebSAdrian Chadd #include <sys/bus.h> 65b45de1ebSAdrian Chadd #include <sys/endian.h> 66b45de1ebSAdrian Chadd #include <sys/kthread.h> 67b45de1ebSAdrian Chadd #include <sys/taskqueue.h> 68b45de1ebSAdrian Chadd #include <sys/priv.h> 69b45de1ebSAdrian Chadd #include <sys/module.h> 70b45de1ebSAdrian Chadd #include <sys/ktr.h> 71b45de1ebSAdrian Chadd #include <sys/smp.h> /* for mp_ncpus */ 72b45de1ebSAdrian Chadd 73b45de1ebSAdrian Chadd #include <machine/bus.h> 74b45de1ebSAdrian Chadd 75b45de1ebSAdrian Chadd #include <net/if.h> 76b45de1ebSAdrian Chadd #include <net/if_var.h> 77b45de1ebSAdrian Chadd #include <net/if_dl.h> 78b45de1ebSAdrian Chadd #include <net/if_media.h> 79b45de1ebSAdrian Chadd #include <net/if_types.h> 80b45de1ebSAdrian Chadd #include <net/if_arp.h> 81b45de1ebSAdrian Chadd #include <net/ethernet.h> 82b45de1ebSAdrian Chadd #include <net/if_llc.h> 83b45de1ebSAdrian Chadd 84b45de1ebSAdrian Chadd #include <net80211/ieee80211_var.h> 85b45de1ebSAdrian Chadd #include <net80211/ieee80211_regdomain.h> 86b45de1ebSAdrian Chadd #ifdef IEEE80211_SUPPORT_SUPERG 87b45de1ebSAdrian Chadd #include <net80211/ieee80211_superg.h> 88b45de1ebSAdrian Chadd #endif 89b45de1ebSAdrian Chadd #ifdef IEEE80211_SUPPORT_TDMA 90b45de1ebSAdrian Chadd #include <net80211/ieee80211_tdma.h> 91b45de1ebSAdrian Chadd #endif 92b45de1ebSAdrian Chadd 93b45de1ebSAdrian Chadd #include <net/bpf.h> 94b45de1ebSAdrian Chadd 95b45de1ebSAdrian Chadd #ifdef INET 96b45de1ebSAdrian Chadd #include <netinet/in.h> 97b45de1ebSAdrian Chadd #include <netinet/if_ether.h> 98b45de1ebSAdrian Chadd #endif 99b45de1ebSAdrian Chadd 100b45de1ebSAdrian Chadd #include <dev/ath/if_athvar.h> 101b45de1ebSAdrian Chadd #include <dev/ath/ath_hal/ah_devid.h> /* XXX for softled */ 102b45de1ebSAdrian Chadd #include <dev/ath/ath_hal/ah_diagcodes.h> 103b45de1ebSAdrian Chadd 104b45de1ebSAdrian Chadd #include <dev/ath/if_ath_debug.h> 105b45de1ebSAdrian Chadd #include <dev/ath/if_ath_misc.h> 106b45de1ebSAdrian Chadd #include <dev/ath/if_ath_btcoex.h> 107b45de1ebSAdrian Chadd #include <dev/ath/if_ath_spectral.h> 108b45de1ebSAdrian Chadd #include <dev/ath/if_ath_lna_div.h> 109b45de1ebSAdrian Chadd #include <dev/ath/if_athdfs.h> 110b45de1ebSAdrian Chadd 111b45de1ebSAdrian Chadd #ifdef IEEE80211_SUPPORT_TDMA 112b45de1ebSAdrian Chadd #include <dev/ath/if_ath_tdma.h> 113b45de1ebSAdrian Chadd #endif 114b45de1ebSAdrian Chadd 115b45de1ebSAdrian Chadd #include <dev/ath/if_ath_ioctl.h> 116b45de1ebSAdrian Chadd 117b45de1ebSAdrian Chadd /* 118b45de1ebSAdrian Chadd * ioctl() related pieces. 119b45de1ebSAdrian Chadd * 120b45de1ebSAdrian Chadd * Some subsystems (eg spectral, dfs) have their own ioctl method which 121b45de1ebSAdrian Chadd * we call. 122b45de1ebSAdrian Chadd */ 123b45de1ebSAdrian Chadd 124b45de1ebSAdrian Chadd /* 125b45de1ebSAdrian Chadd * Fetch the rate control statistics for the given node. 126b45de1ebSAdrian Chadd */ 127b45de1ebSAdrian Chadd static int 128b45de1ebSAdrian Chadd ath_ioctl_ratestats(struct ath_softc *sc, struct ath_rateioctl *rs) 129b45de1ebSAdrian Chadd { 130b45de1ebSAdrian Chadd struct ath_node *an; 131b45de1ebSAdrian Chadd struct ieee80211com *ic = &sc->sc_ic; 132b45de1ebSAdrian Chadd struct ieee80211_node *ni; 133b45de1ebSAdrian Chadd int error = 0; 134b45de1ebSAdrian Chadd 135b45de1ebSAdrian Chadd /* Perform a lookup on the given node */ 136b45de1ebSAdrian Chadd ni = ieee80211_find_node(&ic->ic_sta, rs->is_u.macaddr); 137b45de1ebSAdrian Chadd if (ni == NULL) { 138b45de1ebSAdrian Chadd error = EINVAL; 139b45de1ebSAdrian Chadd goto bad; 140b45de1ebSAdrian Chadd } 141b45de1ebSAdrian Chadd 142b45de1ebSAdrian Chadd /* Lock the ath_node */ 143b45de1ebSAdrian Chadd an = ATH_NODE(ni); 144b45de1ebSAdrian Chadd ATH_NODE_LOCK(an); 145b45de1ebSAdrian Chadd 146b45de1ebSAdrian Chadd /* Fetch the rate control stats for this node */ 147b45de1ebSAdrian Chadd error = ath_rate_fetch_node_stats(sc, an, rs); 148b45de1ebSAdrian Chadd 149b45de1ebSAdrian Chadd /* No matter what happens here, just drop through */ 150b45de1ebSAdrian Chadd 151b45de1ebSAdrian Chadd /* Unlock the ath_node */ 152b45de1ebSAdrian Chadd ATH_NODE_UNLOCK(an); 153b45de1ebSAdrian Chadd 154b45de1ebSAdrian Chadd /* Unref the node */ 155b45de1ebSAdrian Chadd ieee80211_node_decref(ni); 156b45de1ebSAdrian Chadd 157b45de1ebSAdrian Chadd bad: 158b45de1ebSAdrian Chadd return (error); 159b45de1ebSAdrian Chadd } 160b45de1ebSAdrian Chadd 161b45de1ebSAdrian Chadd #ifdef ATH_DIAGAPI 162b45de1ebSAdrian Chadd /* 163b45de1ebSAdrian Chadd * Diagnostic interface to the HAL. This is used by various 164b45de1ebSAdrian Chadd * tools to do things like retrieve register contents for 165b45de1ebSAdrian Chadd * debugging. The mechanism is intentionally opaque so that 166f6b6084bSPedro F. Giffuni * it can change frequently w/o concern for compatibility. 167b45de1ebSAdrian Chadd */ 168b45de1ebSAdrian Chadd static int 169b45de1ebSAdrian Chadd ath_ioctl_diag(struct ath_softc *sc, struct ath_diag *ad) 170b45de1ebSAdrian Chadd { 171b45de1ebSAdrian Chadd struct ath_hal *ah = sc->sc_ah; 172b45de1ebSAdrian Chadd u_int id = ad->ad_id & ATH_DIAG_ID; 173b45de1ebSAdrian Chadd void *indata = NULL; 174b45de1ebSAdrian Chadd void *outdata = NULL; 175b45de1ebSAdrian Chadd u_int32_t insize = ad->ad_in_size; 176b45de1ebSAdrian Chadd u_int32_t outsize = ad->ad_out_size; 177b45de1ebSAdrian Chadd int error = 0; 178b45de1ebSAdrian Chadd 179b45de1ebSAdrian Chadd if (ad->ad_id & ATH_DIAG_IN) { 180b45de1ebSAdrian Chadd /* 181b45de1ebSAdrian Chadd * Copy in data. 182b45de1ebSAdrian Chadd */ 183b45de1ebSAdrian Chadd indata = malloc(insize, M_TEMP, M_NOWAIT); 184b45de1ebSAdrian Chadd if (indata == NULL) { 185b45de1ebSAdrian Chadd error = ENOMEM; 186b45de1ebSAdrian Chadd goto bad; 187b45de1ebSAdrian Chadd } 188b45de1ebSAdrian Chadd error = copyin(ad->ad_in_data, indata, insize); 189b45de1ebSAdrian Chadd if (error) 190b45de1ebSAdrian Chadd goto bad; 191b45de1ebSAdrian Chadd } 192b45de1ebSAdrian Chadd if (ad->ad_id & ATH_DIAG_DYN) { 193b45de1ebSAdrian Chadd /* 194b45de1ebSAdrian Chadd * Allocate a buffer for the results (otherwise the HAL 195b45de1ebSAdrian Chadd * returns a pointer to a buffer where we can read the 196b45de1ebSAdrian Chadd * results). Note that we depend on the HAL leaving this 197b45de1ebSAdrian Chadd * pointer for us to use below in reclaiming the buffer; 198b45de1ebSAdrian Chadd * may want to be more defensive. 199b45de1ebSAdrian Chadd */ 200b081d0e4SEd Maste outdata = malloc(outsize, M_TEMP, M_NOWAIT | M_ZERO); 201b45de1ebSAdrian Chadd if (outdata == NULL) { 202b45de1ebSAdrian Chadd error = ENOMEM; 203b45de1ebSAdrian Chadd goto bad; 204b45de1ebSAdrian Chadd } 205b45de1ebSAdrian Chadd } 206b45de1ebSAdrian Chadd 207b45de1ebSAdrian Chadd ATH_LOCK(sc); 208b45de1ebSAdrian Chadd if (id != HAL_DIAG_REGS) 209b45de1ebSAdrian Chadd ath_power_set_power_state(sc, HAL_PM_AWAKE); 210b45de1ebSAdrian Chadd ATH_UNLOCK(sc); 211b45de1ebSAdrian Chadd 212b45de1ebSAdrian Chadd if (ath_hal_getdiagstate(ah, id, indata, insize, &outdata, &outsize)) { 213b45de1ebSAdrian Chadd if (outsize < ad->ad_out_size) 214b45de1ebSAdrian Chadd ad->ad_out_size = outsize; 215b45de1ebSAdrian Chadd if (outdata != NULL) 216b45de1ebSAdrian Chadd error = copyout(outdata, ad->ad_out_data, 217b45de1ebSAdrian Chadd ad->ad_out_size); 218b45de1ebSAdrian Chadd } else { 219b45de1ebSAdrian Chadd error = EINVAL; 220b45de1ebSAdrian Chadd } 221b45de1ebSAdrian Chadd 222b45de1ebSAdrian Chadd ATH_LOCK(sc); 223b45de1ebSAdrian Chadd if (id != HAL_DIAG_REGS) 224b45de1ebSAdrian Chadd ath_power_restore_power_state(sc); 225b45de1ebSAdrian Chadd ATH_UNLOCK(sc); 226b45de1ebSAdrian Chadd 227b45de1ebSAdrian Chadd bad: 228b45de1ebSAdrian Chadd if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL) 229b45de1ebSAdrian Chadd free(indata, M_TEMP); 230b45de1ebSAdrian Chadd if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL) 231b45de1ebSAdrian Chadd free(outdata, M_TEMP); 232b45de1ebSAdrian Chadd return error; 233b45de1ebSAdrian Chadd } 234b45de1ebSAdrian Chadd #endif /* ATH_DIAGAPI */ 235b45de1ebSAdrian Chadd 236b45de1ebSAdrian Chadd int 237b45de1ebSAdrian Chadd ath_ioctl(struct ieee80211com *ic, u_long cmd, void *data) 238b45de1ebSAdrian Chadd { 239b45de1ebSAdrian Chadd struct ifreq *ifr = data; 240b45de1ebSAdrian Chadd struct ath_softc *sc = ic->ic_softc; 241b45de1ebSAdrian Chadd 242b45de1ebSAdrian Chadd switch (cmd) { 243b45de1ebSAdrian Chadd case SIOCGATHSTATS: { 244b45de1ebSAdrian Chadd struct ieee80211vap *vap; 245ec22a3a2SJustin Hibbits if_t ifp; 246b45de1ebSAdrian Chadd const HAL_RATE_TABLE *rt; 247b45de1ebSAdrian Chadd 248b45de1ebSAdrian Chadd /* NB: embed these numbers to get a consistent view */ 249b45de1ebSAdrian Chadd sc->sc_stats.ast_tx_packets = 0; 250b45de1ebSAdrian Chadd sc->sc_stats.ast_rx_packets = 0; 251b45de1ebSAdrian Chadd TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 252b45de1ebSAdrian Chadd ifp = vap->iv_ifp; 253*a6c0f09aSJustin Hibbits sc->sc_stats.ast_tx_packets += if_getcounter(ifp, 254b45de1ebSAdrian Chadd IFCOUNTER_OPACKETS); 255*a6c0f09aSJustin Hibbits sc->sc_stats.ast_rx_packets += if_getcounter(ifp, 256b45de1ebSAdrian Chadd IFCOUNTER_IPACKETS); 257b45de1ebSAdrian Chadd } 258b45de1ebSAdrian Chadd sc->sc_stats.ast_tx_rssi = ATH_RSSI(sc->sc_halstats.ns_avgtxrssi); 259b45de1ebSAdrian Chadd sc->sc_stats.ast_rx_rssi = ATH_RSSI(sc->sc_halstats.ns_avgrssi); 260b45de1ebSAdrian Chadd #ifdef IEEE80211_SUPPORT_TDMA 261b45de1ebSAdrian Chadd sc->sc_stats.ast_tdma_tsfadjp = TDMA_AVG(sc->sc_avgtsfdeltap); 262b45de1ebSAdrian Chadd sc->sc_stats.ast_tdma_tsfadjm = TDMA_AVG(sc->sc_avgtsfdeltam); 263b45de1ebSAdrian Chadd #endif 264b45de1ebSAdrian Chadd rt = sc->sc_currates; 265b45de1ebSAdrian Chadd sc->sc_stats.ast_tx_rate = 266b45de1ebSAdrian Chadd rt->info[sc->sc_txrix].dot11Rate &~ IEEE80211_RATE_BASIC; 267b45de1ebSAdrian Chadd if (rt->info[sc->sc_txrix].phy & IEEE80211_T_HT) 268b45de1ebSAdrian Chadd sc->sc_stats.ast_tx_rate |= IEEE80211_RATE_MCS; 269541d96aaSBrooks Davis return copyout(&sc->sc_stats, ifr_data_get_ptr(ifr), 270541d96aaSBrooks Davis sizeof (sc->sc_stats)); 271b45de1ebSAdrian Chadd } 272b45de1ebSAdrian Chadd case SIOCGATHAGSTATS: 273541d96aaSBrooks Davis return copyout(&sc->sc_aggr_stats, ifr_data_get_ptr(ifr), 274541d96aaSBrooks Davis sizeof (sc->sc_aggr_stats)); 275b45de1ebSAdrian Chadd case SIOCZATHSTATS: { 276b45de1ebSAdrian Chadd int error; 277b45de1ebSAdrian Chadd 278b45de1ebSAdrian Chadd error = priv_check(curthread, PRIV_DRIVER); 279b45de1ebSAdrian Chadd if (error == 0) { 280b45de1ebSAdrian Chadd memset(&sc->sc_stats, 0, sizeof(sc->sc_stats)); 281b45de1ebSAdrian Chadd memset(&sc->sc_aggr_stats, 0, 282b45de1ebSAdrian Chadd sizeof(sc->sc_aggr_stats)); 283b45de1ebSAdrian Chadd memset(&sc->sc_intr_stats, 0, 284b45de1ebSAdrian Chadd sizeof(sc->sc_intr_stats)); 285b45de1ebSAdrian Chadd } 286b45de1ebSAdrian Chadd return (error); 287b45de1ebSAdrian Chadd } 288b45de1ebSAdrian Chadd #ifdef ATH_DIAGAPI 289b45de1ebSAdrian Chadd case SIOCGATHDIAG: 290b45de1ebSAdrian Chadd return (ath_ioctl_diag(sc, data)); 291b45de1ebSAdrian Chadd case SIOCGATHPHYERR: 292b45de1ebSAdrian Chadd return (ath_ioctl_phyerr(sc, data)); 293b45de1ebSAdrian Chadd #endif 294b45de1ebSAdrian Chadd case SIOCGATHSPECTRAL: 295b45de1ebSAdrian Chadd return (ath_ioctl_spectral(sc, data)); 296b45de1ebSAdrian Chadd case SIOCGATHNODERATESTATS: 297b45de1ebSAdrian Chadd return (ath_ioctl_ratestats(sc, data)); 2985566fb10SAdrian Chadd case SIOCGATHBTCOEX: 2995566fb10SAdrian Chadd return (ath_btcoex_ioctl(sc, data)); 300b45de1ebSAdrian Chadd default: 301b45de1ebSAdrian Chadd /* 302b45de1ebSAdrian Chadd * This signals the net80211 layer that we didn't handle this 303b45de1ebSAdrian Chadd * ioctl. 304b45de1ebSAdrian Chadd */ 305b45de1ebSAdrian Chadd return (ENOTTY); 306b45de1ebSAdrian Chadd } 307b45de1ebSAdrian Chadd } 308