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