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 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 227 bad: 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 236 int 237 ath_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