1181834Sroberto/*- 2280849Scy * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting 3181834Sroberto * All rights reserved. 4280849Scy * 5285169Scy * Redistribution and use in source and binary forms, with or without 6181834Sroberto * modification, are permitted provided that the following conditions 7181834Sroberto * are met: 8181834Sroberto * 1. Redistributions of source code must retain the above copyright 9181834Sroberto * notice, this list of conditions and the following disclaimer, 10181834Sroberto * without modification. 11181834Sroberto * 2. Redistributions in binary form must reproduce at minimum a disclaimer 12285169Scy * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 13181834Sroberto * redistribution must be conditioned upon including a substantially 14280849Scy * similar Disclaimer requirement for further binary redistribution. 15280849Scy * 16285169Scy * NO WARRANTY 17280849Scy * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18280849Scy * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19280849Scy * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 20280849Scy * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 21280849Scy * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 22280849Scy * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23280849Scy * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24280849Scy * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 25280849Scy * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26280849Scy * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27280849Scy * THE POSSIBILITY OF SUCH DAMAGES. 28280849Scy */ 29280849Scy 30280849Scy#include <sys/cdefs.h> 31280849Scy__FBSDID("$FreeBSD: releng/11.0/sys/dev/ath/if_ath_ioctl.c 298939 2016-05-02 19:56:48Z pfg $"); 32280849Scy 33181834Sroberto/* 34181834Sroberto * Driver for the Atheros Wireless LAN controller. 35280849Scy * 36280849Scy * This software is derived from work of Atsushi Onoe; his contribution 37280849Scy * is greatly appreciated. 38280849Scy */ 39280849Scy 40280849Scy#include "opt_inet.h" 41181834Sroberto#include "opt_ath.h" 42280849Scy/* 43181834Sroberto * This is needed for register operations which are performed 44280849Scy * by the driver - eg, calls to ath_hal_gettsf32(). 45280849Scy * 46280849Scy * It's also required for any AH_DEBUG checks in here, eg the 47280849Scy * module dependencies. 48280849Scy */ 49280849Scy#include "opt_ah.h" 50280849Scy#include "opt_wlan.h" 51280849Scy 52280849Scy#include <sys/param.h> 53280849Scy#include <sys/systm.h> 54181834Sroberto#include <sys/sysctl.h> 55280849Scy#include <sys/mbuf.h> 56280849Scy#include <sys/malloc.h> 57280849Scy#include <sys/lock.h> 58280849Scy#include <sys/mutex.h> 59280849Scy#include <sys/kernel.h> 60181834Sroberto#include <sys/socket.h> 61280849Scy#include <sys/sockio.h> 62280849Scy#include <sys/errno.h> 63280849Scy#include <sys/callout.h> 64181834Sroberto#include <sys/bus.h> 65280849Scy#include <sys/endian.h> 66280849Scy#include <sys/kthread.h> 67280849Scy#include <sys/taskqueue.h> 68280849Scy#include <sys/priv.h> 69280849Scy#include <sys/module.h> 70181834Sroberto#include <sys/ktr.h> 71280849Scy#include <sys/smp.h> /* for mp_ncpus */ 72280849Scy 73280849Scy#include <machine/bus.h> 74280849Scy 75280849Scy#include <net/if.h> 76280849Scy#include <net/if_var.h> 77280849Scy#include <net/if_dl.h> 78280849Scy#include <net/if_media.h> 79280849Scy#include <net/if_types.h> 80280849Scy#include <net/if_arp.h> 81280849Scy#include <net/ethernet.h> 82280849Scy#include <net/if_llc.h> 83280849Scy 84280849Scy#include <net80211/ieee80211_var.h> 85285169Scy#include <net80211/ieee80211_regdomain.h> 86280849Scy#ifdef IEEE80211_SUPPORT_SUPERG 87285169Scy#include <net80211/ieee80211_superg.h> 88285169Scy#endif 89285169Scy#ifdef IEEE80211_SUPPORT_TDMA 90285169Scy#include <net80211/ieee80211_tdma.h> 91285169Scy#endif 92285169Scy 93285169Scy#include <net/bpf.h> 94181834Sroberto 95181834Sroberto#ifdef INET 96181834Sroberto#include <netinet/in.h> 97181834Sroberto#include <netinet/if_ether.h> 98181834Sroberto#endif 99181834Sroberto 100181834Sroberto#include <dev/ath/if_athvar.h> 101285169Scy#include <dev/ath/ath_hal/ah_devid.h> /* XXX for softled */ 102181834Sroberto#include <dev/ath/ath_hal/ah_diagcodes.h> 103280849Scy 104280849Scy#include <dev/ath/if_ath_debug.h> 105181834Sroberto#include <dev/ath/if_ath_misc.h> 106181834Sroberto#include <dev/ath/if_ath_btcoex.h> 107181834Sroberto#include <dev/ath/if_ath_spectral.h> 108280849Scy#include <dev/ath/if_ath_lna_div.h> 109280849Scy#include <dev/ath/if_athdfs.h> 110181834Sroberto 111280849Scy#ifdef IEEE80211_SUPPORT_TDMA 112280849Scy#include <dev/ath/if_ath_tdma.h> 113280849Scy#endif 114280849Scy 115280849Scy#include <dev/ath/if_ath_ioctl.h> 116280849Scy 117280849Scy/* 118280849Scy * ioctl() related pieces. 119280849Scy * 120280849Scy * Some subsystems (eg spectral, dfs) have their own ioctl method which 121280849Scy * we call. 122280849Scy */ 123280849Scy 124181834Sroberto/* 125280849Scy * Fetch the rate control statistics for the given node. 126280849Scy */ 127280849Scystatic int 128280849Scyath_ioctl_ratestats(struct ath_softc *sc, struct ath_rateioctl *rs) 129280849Scy{ 130181834Sroberto struct ath_node *an; 131280849Scy struct ieee80211com *ic = &sc->sc_ic; 132280849Scy struct ieee80211_node *ni; 133280849Scy int error = 0; 134280849Scy 135280849Scy /* Perform a lookup on the given node */ 136280849Scy ni = ieee80211_find_node(&ic->ic_sta, rs->is_u.macaddr); 137280849Scy if (ni == NULL) { 138280849Scy error = EINVAL; 139280849Scy goto bad; 140280849Scy } 141280849Scy 142280849Scy /* Lock the ath_node */ 143280849Scy an = ATH_NODE(ni); 144181834Sroberto ATH_NODE_LOCK(an); 145181834Sroberto 146280849Scy /* Fetch the rate control stats for this node */ 147280849Scy error = ath_rate_fetch_node_stats(sc, an, rs); 148280849Scy 149181834Sroberto /* No matter what happens here, just drop through */ 150280849Scy 151280849Scy /* Unlock the ath_node */ 152181834Sroberto ATH_NODE_UNLOCK(an); 153280849Scy 154280849Scy /* Unref the node */ 155280849Scy ieee80211_node_decref(ni); 156280849Scy 157280849Scybad: 158285169Scy return (error); 159181834Sroberto} 160181834Sroberto 161181834Sroberto#ifdef ATH_DIAGAPI 162280849Scy/* 163280849Scy * Diagnostic interface to the HAL. This is used by various 164181834Sroberto * tools to do things like retrieve register contents for 165181834Sroberto * debugging. The mechanism is intentionally opaque so that 166280849Scy * it can change frequently w/o concern for compatibility. 167280849Scy */ 168280849Scystatic int 169280849Scyath_ioctl_diag(struct ath_softc *sc, struct ath_diag *ad) 170280849Scy{ 171280849Scy struct ath_hal *ah = sc->sc_ah; 172280849Scy u_int id = ad->ad_id & ATH_DIAG_ID; 173280849Scy void *indata = NULL; 174280849Scy void *outdata = NULL; 175181834Sroberto u_int32_t insize = ad->ad_in_size; 176280849Scy u_int32_t outsize = ad->ad_out_size; 177280849Scy int error = 0; 178280849Scy 179280849Scy if (ad->ad_id & ATH_DIAG_IN) { 180280849Scy /* 181280849Scy * Copy in data. 182280849Scy */ 183181834Sroberto indata = malloc(insize, M_TEMP, M_NOWAIT); 184280849Scy if (indata == NULL) { 185280849Scy error = ENOMEM; 186280849Scy goto bad; 187280849Scy } 188280849Scy error = copyin(ad->ad_in_data, indata, insize); 189280849Scy if (error) 190280849Scy goto bad; 191280849Scy } 192280849Scy if (ad->ad_id & ATH_DIAG_DYN) { 193280849Scy /* 194280849Scy * Allocate a buffer for the results (otherwise the HAL 195181834Sroberto * returns a pointer to a buffer where we can read the 196280849Scy * results). Note that we depend on the HAL leaving this 197280849Scy * pointer for us to use below in reclaiming the buffer; 198280849Scy * may want to be more defensive. 199280849Scy */ 200280849Scy outdata = malloc(outsize, M_TEMP, M_NOWAIT); 201181834Sroberto if (outdata == NULL) { 202280849Scy error = ENOMEM; 203280849Scy goto bad; 204280849Scy } 205280849Scy } 206181834Sroberto 207280849Scy 208280849Scy ATH_LOCK(sc); 209181834Sroberto if (id != HAL_DIAG_REGS) 210280849Scy ath_power_set_power_state(sc, HAL_PM_AWAKE); 211280849Scy ATH_UNLOCK(sc); 212280849Scy 213280849Scy if (ath_hal_getdiagstate(ah, id, indata, insize, &outdata, &outsize)) { 214280849Scy if (outsize < ad->ad_out_size) 215280849Scy ad->ad_out_size = outsize; 216280849Scy if (outdata != NULL) 217280849Scy error = copyout(outdata, ad->ad_out_data, 218280849Scy ad->ad_out_size); 219280849Scy } else { 220280849Scy error = EINVAL; 221280849Scy } 222280849Scy 223280849Scy ATH_LOCK(sc); 224280849Scy if (id != HAL_DIAG_REGS) 225280849Scy ath_power_restore_power_state(sc); 226280849Scy ATH_UNLOCK(sc); 227280849Scy 228280849Scybad: 229280849Scy if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL) 230280849Scy free(indata, M_TEMP); 231280849Scy if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL) 232280849Scy free(outdata, M_TEMP); 233280849Scy return error; 234280849Scy} 235280849Scy#endif /* ATH_DIAGAPI */ 236280849Scy 237280849Scyint 238280849Scyath_ioctl(struct ieee80211com *ic, u_long cmd, void *data) 239280849Scy{ 240280849Scy struct ifreq *ifr = data; 241280849Scy struct ath_softc *sc = ic->ic_softc; 242280849Scy 243280849Scy switch (cmd) { 244280849Scy case SIOCGATHSTATS: { 245280849Scy struct ieee80211vap *vap; 246280849Scy struct ifnet *ifp; 247280849Scy const HAL_RATE_TABLE *rt; 248280849Scy 249280849Scy /* NB: embed these numbers to get a consistent view */ 250280849Scy sc->sc_stats.ast_tx_packets = 0; 251280849Scy sc->sc_stats.ast_rx_packets = 0; 252280849Scy TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 253280849Scy ifp = vap->iv_ifp; 254280849Scy sc->sc_stats.ast_tx_packets += ifp->if_get_counter(ifp, 255280849Scy IFCOUNTER_OPACKETS); 256280849Scy sc->sc_stats.ast_rx_packets += ifp->if_get_counter(ifp, 257280849Scy IFCOUNTER_IPACKETS); 258280849Scy } 259280849Scy sc->sc_stats.ast_tx_rssi = ATH_RSSI(sc->sc_halstats.ns_avgtxrssi); 260280849Scy sc->sc_stats.ast_rx_rssi = ATH_RSSI(sc->sc_halstats.ns_avgrssi); 261280849Scy#ifdef IEEE80211_SUPPORT_TDMA 262280849Scy sc->sc_stats.ast_tdma_tsfadjp = TDMA_AVG(sc->sc_avgtsfdeltap); 263280849Scy sc->sc_stats.ast_tdma_tsfadjm = TDMA_AVG(sc->sc_avgtsfdeltam); 264280849Scy#endif 265280849Scy rt = sc->sc_currates; 266280849Scy sc->sc_stats.ast_tx_rate = 267280849Scy rt->info[sc->sc_txrix].dot11Rate &~ IEEE80211_RATE_BASIC; 268280849Scy if (rt->info[sc->sc_txrix].phy & IEEE80211_T_HT) 269280849Scy sc->sc_stats.ast_tx_rate |= IEEE80211_RATE_MCS; 270280849Scy return copyout(&sc->sc_stats, 271280849Scy ifr->ifr_data, sizeof (sc->sc_stats)); 272280849Scy } 273280849Scy case SIOCGATHAGSTATS: 274280849Scy return copyout(&sc->sc_aggr_stats, 275280849Scy ifr->ifr_data, sizeof (sc->sc_aggr_stats)); 276280849Scy case SIOCZATHSTATS: { 277280849Scy int error; 278280849Scy 279280849Scy error = priv_check(curthread, PRIV_DRIVER); 280280849Scy if (error == 0) { 281280849Scy memset(&sc->sc_stats, 0, sizeof(sc->sc_stats)); 282280849Scy memset(&sc->sc_aggr_stats, 0, 283280849Scy sizeof(sc->sc_aggr_stats)); 284280849Scy memset(&sc->sc_intr_stats, 0, 285280849Scy sizeof(sc->sc_intr_stats)); 286280849Scy } 287280849Scy return (error); 288280849Scy } 289280849Scy#ifdef ATH_DIAGAPI 290280849Scy case SIOCGATHDIAG: 291280849Scy return (ath_ioctl_diag(sc, data)); 292280849Scy case SIOCGATHPHYERR: 293280849Scy return (ath_ioctl_phyerr(sc, data)); 294280849Scy#endif 295280849Scy case SIOCGATHSPECTRAL: 296280849Scy return (ath_ioctl_spectral(sc, data)); 297280849Scy case SIOCGATHNODERATESTATS: 298280849Scy return (ath_ioctl_ratestats(sc, data)); 299280849Scy default: 300280849Scy /* 301280849Scy * This signals the net80211 layer that we didn't handle this 302280849Scy * ioctl. 303280849Scy */ 304280849Scy return (ENOTTY); 305280849Scy } 306280849Scy} 307280849Scy 308280849Scy