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 */ 127 static int 128 ath_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 157 bad: 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 */ 168 static int 169 ath_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 208 ATH_LOCK(sc); 209 if (id != HAL_DIAG_REGS) 210 ath_power_set_power_state(sc, HAL_PM_AWAKE); 211 ATH_UNLOCK(sc); 212 213 if (ath_hal_getdiagstate(ah, id, indata, insize, &outdata, &outsize)) { 214 if (outsize < ad->ad_out_size) 215 ad->ad_out_size = outsize; 216 if (outdata != NULL) 217 error = copyout(outdata, ad->ad_out_data, 218 ad->ad_out_size); 219 } else { 220 error = EINVAL; 221 } 222 223 ATH_LOCK(sc); 224 if (id != HAL_DIAG_REGS) 225 ath_power_restore_power_state(sc); 226 ATH_UNLOCK(sc); 227 228 bad: 229 if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL) 230 free(indata, M_TEMP); 231 if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL) 232 free(outdata, M_TEMP); 233 return error; 234 } 235 #endif /* ATH_DIAGAPI */ 236 237 int 238 ath_ioctl(struct ieee80211com *ic, u_long cmd, void *data) 239 { 240 struct ifreq *ifr = data; 241 struct ath_softc *sc = ic->ic_softc; 242 243 switch (cmd) { 244 case SIOCGATHSTATS: { 245 struct ieee80211vap *vap; 246 struct ifnet *ifp; 247 const HAL_RATE_TABLE *rt; 248 249 /* NB: embed these numbers to get a consistent view */ 250 sc->sc_stats.ast_tx_packets = 0; 251 sc->sc_stats.ast_rx_packets = 0; 252 TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 253 ifp = vap->iv_ifp; 254 sc->sc_stats.ast_tx_packets += ifp->if_get_counter(ifp, 255 IFCOUNTER_OPACKETS); 256 sc->sc_stats.ast_rx_packets += ifp->if_get_counter(ifp, 257 IFCOUNTER_IPACKETS); 258 } 259 sc->sc_stats.ast_tx_rssi = ATH_RSSI(sc->sc_halstats.ns_avgtxrssi); 260 sc->sc_stats.ast_rx_rssi = ATH_RSSI(sc->sc_halstats.ns_avgrssi); 261 #ifdef IEEE80211_SUPPORT_TDMA 262 sc->sc_stats.ast_tdma_tsfadjp = TDMA_AVG(sc->sc_avgtsfdeltap); 263 sc->sc_stats.ast_tdma_tsfadjm = TDMA_AVG(sc->sc_avgtsfdeltam); 264 #endif 265 rt = sc->sc_currates; 266 sc->sc_stats.ast_tx_rate = 267 rt->info[sc->sc_txrix].dot11Rate &~ IEEE80211_RATE_BASIC; 268 if (rt->info[sc->sc_txrix].phy & IEEE80211_T_HT) 269 sc->sc_stats.ast_tx_rate |= IEEE80211_RATE_MCS; 270 return copyout(&sc->sc_stats, ifr_data_get_ptr(ifr), 271 sizeof (sc->sc_stats)); 272 } 273 case SIOCGATHAGSTATS: 274 return copyout(&sc->sc_aggr_stats, ifr_data_get_ptr(ifr), 275 sizeof (sc->sc_aggr_stats)); 276 case SIOCZATHSTATS: { 277 int error; 278 279 error = priv_check(curthread, PRIV_DRIVER); 280 if (error == 0) { 281 memset(&sc->sc_stats, 0, sizeof(sc->sc_stats)); 282 memset(&sc->sc_aggr_stats, 0, 283 sizeof(sc->sc_aggr_stats)); 284 memset(&sc->sc_intr_stats, 0, 285 sizeof(sc->sc_intr_stats)); 286 } 287 return (error); 288 } 289 #ifdef ATH_DIAGAPI 290 case SIOCGATHDIAG: 291 return (ath_ioctl_diag(sc, data)); 292 case SIOCGATHPHYERR: 293 return (ath_ioctl_phyerr(sc, data)); 294 #endif 295 case SIOCGATHSPECTRAL: 296 return (ath_ioctl_spectral(sc, data)); 297 case SIOCGATHNODERATESTATS: 298 return (ath_ioctl_ratestats(sc, data)); 299 case SIOCGATHBTCOEX: 300 return (ath_btcoex_ioctl(sc, data)); 301 default: 302 /* 303 * This signals the net80211 layer that we didn't handle this 304 * ioctl. 305 */ 306 return (ENOTTY); 307 } 308 } 309 310