Index: head/sys/dev/ath/ath_dfs/null/dfs_null.c =================================================================== --- head/sys/dev/ath/ath_dfs/null/dfs_null.c (revision 298938) +++ head/sys/dev/ath/ath_dfs/null/dfs_null.c (revision 298939) @@ -1,295 +1,295 @@ /*- * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * * $FreeBSD$ */ #include __FBSDID("$FreeBSD$"); /* * This implements an empty DFS module. */ #include "opt_ath.h" #include "opt_inet.h" #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* XXX for ether_sprintf */ #include #include #ifdef INET #include #include #endif #include #include #include /* * Methods which are required */ /* * Attach DFS to the given interface */ int ath_dfs_attach(struct ath_softc *sc) { return (1); } /* * Detach DFS from the given interface */ int ath_dfs_detach(struct ath_softc *sc) { return (1); } /* * Enable radar check. Return 1 if the driver should * enable radar PHY errors, or 0 if not. */ int ath_dfs_radar_enable(struct ath_softc *sc, struct ieee80211_channel *chan) { #if 0 HAL_PHYERR_PARAM pe; /* Check if the hardware supports radar reporting */ /* XXX TODO: migrate HAL_CAP_RADAR/HAL_CAP_AR to somewhere public! */ if (ath_hal_getcapability(sc->sc_ah, HAL_CAP_PHYDIAG, 0, NULL) != HAL_OK) return (0); /* Check if the current channel is radar-enabled */ if (! IEEE80211_IS_CHAN_DFS(chan)) return (0); /* Fetch the default parameters */ memset(&pe, '\0', sizeof(pe)); if (! ath_hal_getdfsdefaultthresh(sc->sc_ah, &pe)) return (0); /* Enable radar PHY error reporting */ sc->sc_dodfs = 1; /* Tell the hardware to enable radar reporting */ pe.pe_enabled = 1; /* Flip on extension channel events only if doing HT40 */ if (IEEE80211_IS_CHAN_HT40(chan)) pe.pe_extchannel = 1; else pe.pe_extchannel = 0; ath_hal_enabledfs(sc->sc_ah, &pe); /* * Disable strong signal fast diversity - needed for * AR5212 and similar PHYs for reliable short pulse * duration. */ (void) ath_hal_setcapability(sc->sc_ah, HAL_CAP_DIVERSITY, 2, 0, NULL); return (1); #else return (0); #endif } /* * Explicity disable radar reporting. * * Return 0 if it was disabled, < 0 on error. */ int ath_dfs_radar_disable(struct ath_softc *sc) { #if 0 HAL_PHYERR_PARAM pe; (void) ath_hal_getdfsthresh(sc->sc_ah, &pe); pe.pe_enabled = 0; (void) ath_hal_enabledfs(sc->sc_ah, &pe); return (0); #else return (0); #endif } /* * Process DFS related PHY errors * * The mbuf is not "ours" and if we want a copy, we have * to take a copy. It'll be freed after this function returns. */ void ath_dfs_process_phy_err(struct ath_softc *sc, struct mbuf *m, uint64_t tsf, struct ath_rx_status *rxstat) { } /* - * Process the radar events and determine whether a DFS event has occured. + * Process the radar events and determine whether a DFS event has occurred. * * This is designed to run outside of the RX processing path. * The RX path will call ath_dfs_tasklet_needed() to see whether * the task/callback running this routine needs to be called. */ int ath_dfs_process_radar_event(struct ath_softc *sc, struct ieee80211_channel *chan) { return (0); } /* * Determine whether the DFS check task needs to be queued. * * This is called in the RX task when the current batch of packets * have been received. It will return whether there are any radar * events for ath_dfs_process_radar_event() to handle. */ int ath_dfs_tasklet_needed(struct ath_softc *sc, struct ieee80211_channel *chan) { return (0); } /* * Handle ioctl requests from the diagnostic interface. * * The initial part of this code resembles ath_ioctl_diag(); * it's likely a good idea to reduce duplication between * these two routines. */ int ath_ioctl_phyerr(struct ath_softc *sc, struct ath_diag *ad) { unsigned int id = ad->ad_id & ATH_DIAG_ID; void *indata = NULL; void *outdata = NULL; u_int32_t insize = ad->ad_in_size; u_int32_t outsize = ad->ad_out_size; int error = 0; HAL_PHYERR_PARAM peout; HAL_PHYERR_PARAM *pe; if (ad->ad_id & ATH_DIAG_IN) { /* * Copy in data. */ indata = malloc(insize, M_TEMP, M_NOWAIT); if (indata == NULL) { error = ENOMEM; goto bad; } error = copyin(ad->ad_in_data, indata, insize); if (error) goto bad; } if (ad->ad_id & ATH_DIAG_DYN) { /* * Allocate a buffer for the results (otherwise the HAL * returns a pointer to a buffer where we can read the * results). Note that we depend on the HAL leaving this * pointer for us to use below in reclaiming the buffer; * may want to be more defensive. */ outdata = malloc(outsize, M_TEMP, M_NOWAIT); if (outdata == NULL) { error = ENOMEM; goto bad; } } switch (id) { case DFS_SET_THRESH: if (insize < sizeof(HAL_PHYERR_PARAM)) { error = EINVAL; break; } pe = (HAL_PHYERR_PARAM *) indata; ath_hal_enabledfs(sc->sc_ah, pe); break; case DFS_GET_THRESH: memset(&peout, 0, sizeof(peout)); outsize = sizeof(HAL_PHYERR_PARAM); ath_hal_getdfsthresh(sc->sc_ah, &peout); pe = (HAL_PHYERR_PARAM *) outdata; memcpy(pe, &peout, sizeof(*pe)); break; default: error = EINVAL; } if (outsize < ad->ad_out_size) ad->ad_out_size = outsize; if (outdata && copyout(outdata, ad->ad_out_data, ad->ad_out_size)) error = EFAULT; bad: if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL) free(indata, M_TEMP); if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL) free(outdata, M_TEMP); return (error); } /* * Get the current DFS thresholds from the HAL */ int ath_dfs_get_thresholds(struct ath_softc *sc, HAL_PHYERR_PARAM *param) { ath_hal_getdfsthresh(sc->sc_ah, param); return (1); } Index: head/sys/dev/ath/ath_hal/ah.c =================================================================== --- head/sys/dev/ath/ath_hal/ah.c (revision 298938) +++ head/sys/dev/ath/ath_hal/ah.c (revision 298939) @@ -1,1469 +1,1469 @@ /* * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * Copyright (c) 2002-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ah_devid.h" #include "ah_eeprom.h" /* for 5ghz fast clock flag */ #include "ar5416/ar5416reg.h" /* NB: includes ar5212reg.h */ #include "ar9003/ar9300_devid.h" /* linker set of registered chips */ OS_SET_DECLARE(ah_chips, struct ath_hal_chip); /* * Check the set of registered chips to see if any recognize * the device as one they can support. */ const char* ath_hal_probe(uint16_t vendorid, uint16_t devid) { struct ath_hal_chip * const *pchip; OS_SET_FOREACH(pchip, ah_chips) { const char *name = (*pchip)->probe(vendorid, devid); if (name != AH_NULL) return name; } return AH_NULL; } /* * Attach detects device chip revisions, initializes the hwLayer * function list, reads EEPROM information, * selects reset vectors, and performs a short self test. * Any failures will return an error that should cause a hardware * disable. */ struct ath_hal* ath_hal_attach(uint16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, HAL_OPS_CONFIG *ah_config, HAL_STATUS *error) { struct ath_hal_chip * const *pchip; OS_SET_FOREACH(pchip, ah_chips) { struct ath_hal_chip *chip = *pchip; struct ath_hal *ah; /* XXX don't have vendorid, assume atheros one works */ if (chip->probe(ATHEROS_VENDOR_ID, devid) == AH_NULL) continue; ah = chip->attach(devid, sc, st, sh, eepromdata, ah_config, error); if (ah != AH_NULL) { /* copy back private state to public area */ ah->ah_devid = AH_PRIVATE(ah)->ah_devid; ah->ah_subvendorid = AH_PRIVATE(ah)->ah_subvendorid; ah->ah_macVersion = AH_PRIVATE(ah)->ah_macVersion; ah->ah_macRev = AH_PRIVATE(ah)->ah_macRev; ah->ah_phyRev = AH_PRIVATE(ah)->ah_phyRev; ah->ah_analog5GhzRev = AH_PRIVATE(ah)->ah_analog5GhzRev; ah->ah_analog2GhzRev = AH_PRIVATE(ah)->ah_analog2GhzRev; return ah; } } return AH_NULL; } const char * ath_hal_mac_name(struct ath_hal *ah) { switch (ah->ah_macVersion) { case AR_SREV_VERSION_CRETE: case AR_SREV_VERSION_MAUI_1: return "AR5210"; case AR_SREV_VERSION_MAUI_2: case AR_SREV_VERSION_OAHU: return "AR5211"; case AR_SREV_VERSION_VENICE: return "AR5212"; case AR_SREV_VERSION_GRIFFIN: return "AR2413"; case AR_SREV_VERSION_CONDOR: return "AR5424"; case AR_SREV_VERSION_EAGLE: return "AR5413"; case AR_SREV_VERSION_COBRA: return "AR2415"; case AR_SREV_2425: /* Swan */ return "AR2425"; case AR_SREV_2417: /* Nala */ return "AR2417"; case AR_XSREV_VERSION_OWL_PCI: return "AR5416"; case AR_XSREV_VERSION_OWL_PCIE: return "AR5418"; case AR_XSREV_VERSION_HOWL: return "AR9130"; case AR_XSREV_VERSION_SOWL: return "AR9160"; case AR_XSREV_VERSION_MERLIN: if (AH_PRIVATE(ah)->ah_ispcie) return "AR9280"; return "AR9220"; case AR_XSREV_VERSION_KITE: return "AR9285"; case AR_XSREV_VERSION_KIWI: if (AH_PRIVATE(ah)->ah_ispcie) return "AR9287"; return "AR9227"; case AR_SREV_VERSION_AR9380: if (ah->ah_macRev >= AR_SREV_REVISION_AR9580_10) return "AR9580"; return "AR9380"; case AR_SREV_VERSION_AR9460: return "AR9460"; case AR_SREV_VERSION_AR9330: return "AR9330"; case AR_SREV_VERSION_AR9340: return "AR9340"; case AR_SREV_VERSION_QCA9550: return "QCA9550"; case AR_SREV_VERSION_AR9485: return "AR9485"; case AR_SREV_VERSION_QCA9565: return "QCA9565"; case AR_SREV_VERSION_QCA9530: return "QCA9530"; } return "????"; } /* * Return the mask of available modes based on the hardware capabilities. */ u_int ath_hal_getwirelessmodes(struct ath_hal*ah) { return ath_hal_getWirelessModes(ah); } /* linker set of registered RF backends */ OS_SET_DECLARE(ah_rfs, struct ath_hal_rf); /* * Check the set of registered RF backends to see if * any recognize the device as one they can support. */ struct ath_hal_rf * ath_hal_rfprobe(struct ath_hal *ah, HAL_STATUS *ecode) { struct ath_hal_rf * const *prf; OS_SET_FOREACH(prf, ah_rfs) { struct ath_hal_rf *rf = *prf; if (rf->probe(ah)) return rf; } *ecode = HAL_ENOTSUPP; return AH_NULL; } const char * ath_hal_rf_name(struct ath_hal *ah) { switch (ah->ah_analog5GhzRev & AR_RADIO_SREV_MAJOR) { case 0: /* 5210 */ return "5110"; /* NB: made up */ case AR_RAD5111_SREV_MAJOR: case AR_RAD5111_SREV_PROD: return "5111"; case AR_RAD2111_SREV_MAJOR: return "2111"; case AR_RAD5112_SREV_MAJOR: case AR_RAD5112_SREV_2_0: case AR_RAD5112_SREV_2_1: return "5112"; case AR_RAD2112_SREV_MAJOR: case AR_RAD2112_SREV_2_0: case AR_RAD2112_SREV_2_1: return "2112"; case AR_RAD2413_SREV_MAJOR: return "2413"; case AR_RAD5413_SREV_MAJOR: return "5413"; case AR_RAD2316_SREV_MAJOR: return "2316"; case AR_RAD2317_SREV_MAJOR: return "2317"; case AR_RAD5424_SREV_MAJOR: return "5424"; case AR_RAD5133_SREV_MAJOR: return "5133"; case AR_RAD2133_SREV_MAJOR: return "2133"; case AR_RAD5122_SREV_MAJOR: return "5122"; case AR_RAD2122_SREV_MAJOR: return "2122"; } return "????"; } /* * Poll the register looking for a specific value. */ HAL_BOOL ath_hal_wait(struct ath_hal *ah, u_int reg, uint32_t mask, uint32_t val) { #define AH_TIMEOUT 1000 return ath_hal_waitfor(ah, reg, mask, val, AH_TIMEOUT); #undef AH_TIMEOUT } HAL_BOOL ath_hal_waitfor(struct ath_hal *ah, u_int reg, uint32_t mask, uint32_t val, uint32_t timeout) { int i; for (i = 0; i < timeout; i++) { if ((OS_REG_READ(ah, reg) & mask) == val) return AH_TRUE; OS_DELAY(10); } HALDEBUG(ah, HAL_DEBUG_REGIO | HAL_DEBUG_PHYIO, "%s: timeout on reg 0x%x: 0x%08x & 0x%08x != 0x%08x\n", __func__, reg, OS_REG_READ(ah, reg), mask, val); return AH_FALSE; } /* * Reverse the bits starting at the low bit for a value of * bit_count in size */ uint32_t ath_hal_reverseBits(uint32_t val, uint32_t n) { uint32_t retval; int i; for (i = 0, retval = 0; i < n; i++) { retval = (retval << 1) | (val & 1); val >>= 1; } return retval; } /* 802.11n related timing definitions */ #define OFDM_PLCP_BITS 22 #define HT_L_STF 8 #define HT_L_LTF 8 #define HT_L_SIG 4 #define HT_SIG 8 #define HT_STF 4 #define HT_LTF(n) ((n) * 4) #define HT_RC_2_MCS(_rc) ((_rc) & 0xf) #define HT_RC_2_STREAMS(_rc) ((((_rc) & 0x78) >> 3) + 1) #define IS_HT_RATE(_rc) ( (_rc) & IEEE80211_RATE_MCS) /* * Calculate the duration of a packet whether it is 11n or legacy. */ uint32_t ath_hal_pkt_txtime(struct ath_hal *ah, const HAL_RATE_TABLE *rates, uint32_t frameLen, uint16_t rateix, HAL_BOOL isht40, HAL_BOOL shortPreamble) { uint8_t rc; int numStreams; rc = rates->info[rateix].rateCode; /* Legacy rate? Return the old way */ if (! IS_HT_RATE(rc)) return ath_hal_computetxtime(ah, rates, frameLen, rateix, shortPreamble); /* 11n frame - extract out the number of spatial streams */ numStreams = HT_RC_2_STREAMS(rc); KASSERT(numStreams > 0 && numStreams <= 4, ("number of spatial streams needs to be 1..3: MCS rate 0x%x!", rateix)); return ath_computedur_ht(frameLen, rc, numStreams, isht40, shortPreamble); } static const uint16_t ht20_bps[32] = { 26, 52, 78, 104, 156, 208, 234, 260, 52, 104, 156, 208, 312, 416, 468, 520, 78, 156, 234, 312, 468, 624, 702, 780, 104, 208, 312, 416, 624, 832, 936, 1040 }; static const uint16_t ht40_bps[32] = { 54, 108, 162, 216, 324, 432, 486, 540, 108, 216, 324, 432, 648, 864, 972, 1080, 162, 324, 486, 648, 972, 1296, 1458, 1620, 216, 432, 648, 864, 1296, 1728, 1944, 2160 }; /* * Calculate the transmit duration of an 11n frame. */ uint32_t ath_computedur_ht(uint32_t frameLen, uint16_t rate, int streams, HAL_BOOL isht40, HAL_BOOL isShortGI) { uint32_t bitsPerSymbol, numBits, numSymbols, txTime; KASSERT(rate & IEEE80211_RATE_MCS, ("not mcs %d", rate)); KASSERT((rate &~ IEEE80211_RATE_MCS) < 31, ("bad mcs 0x%x", rate)); if (isht40) bitsPerSymbol = ht40_bps[rate & 0x1f]; else bitsPerSymbol = ht20_bps[rate & 0x1f]; numBits = OFDM_PLCP_BITS + (frameLen << 3); numSymbols = howmany(numBits, bitsPerSymbol); if (isShortGI) txTime = ((numSymbols * 18) + 4) / 5; /* 3.6us */ else txTime = numSymbols * 4; /* 4us */ return txTime + HT_L_STF + HT_L_LTF + HT_L_SIG + HT_SIG + HT_STF + HT_LTF(streams); } /* * Compute the time to transmit a frame of length frameLen bytes * using the specified rate, phy, and short preamble setting. */ uint16_t ath_hal_computetxtime(struct ath_hal *ah, const HAL_RATE_TABLE *rates, uint32_t frameLen, uint16_t rateix, HAL_BOOL shortPreamble) { uint32_t bitsPerSymbol, numBits, numSymbols, phyTime, txTime; uint32_t kbps; /* Warn if this function is called for 11n rates; it should not be! */ if (IS_HT_RATE(rates->info[rateix].rateCode)) ath_hal_printf(ah, "%s: MCS rate? (index %d; hwrate 0x%x)\n", __func__, rateix, rates->info[rateix].rateCode); kbps = rates->info[rateix].rateKbps; /* - * index can be invalid duting dynamic Turbo transitions. + * index can be invalid during dynamic Turbo transitions. * XXX */ if (kbps == 0) return 0; switch (rates->info[rateix].phy) { case IEEE80211_T_CCK: phyTime = CCK_PREAMBLE_BITS + CCK_PLCP_BITS; if (shortPreamble && rates->info[rateix].shortPreamble) phyTime >>= 1; numBits = frameLen << 3; txTime = CCK_SIFS_TIME + phyTime + ((numBits * 1000)/kbps); break; case IEEE80211_T_OFDM: bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME) / 1000; HALASSERT(bitsPerSymbol != 0); numBits = OFDM_PLCP_BITS + (frameLen << 3); numSymbols = howmany(numBits, bitsPerSymbol); txTime = OFDM_SIFS_TIME + OFDM_PREAMBLE_TIME + (numSymbols * OFDM_SYMBOL_TIME); break; case IEEE80211_T_OFDM_HALF: bitsPerSymbol = (kbps * OFDM_HALF_SYMBOL_TIME) / 1000; HALASSERT(bitsPerSymbol != 0); numBits = OFDM_HALF_PLCP_BITS + (frameLen << 3); numSymbols = howmany(numBits, bitsPerSymbol); txTime = OFDM_HALF_SIFS_TIME + OFDM_HALF_PREAMBLE_TIME + (numSymbols * OFDM_HALF_SYMBOL_TIME); break; case IEEE80211_T_OFDM_QUARTER: bitsPerSymbol = (kbps * OFDM_QUARTER_SYMBOL_TIME) / 1000; HALASSERT(bitsPerSymbol != 0); numBits = OFDM_QUARTER_PLCP_BITS + (frameLen << 3); numSymbols = howmany(numBits, bitsPerSymbol); txTime = OFDM_QUARTER_SIFS_TIME + OFDM_QUARTER_PREAMBLE_TIME + (numSymbols * OFDM_QUARTER_SYMBOL_TIME); break; case IEEE80211_T_TURBO: bitsPerSymbol = (kbps * TURBO_SYMBOL_TIME) / 1000; HALASSERT(bitsPerSymbol != 0); numBits = TURBO_PLCP_BITS + (frameLen << 3); numSymbols = howmany(numBits, bitsPerSymbol); txTime = TURBO_SIFS_TIME + TURBO_PREAMBLE_TIME + (numSymbols * TURBO_SYMBOL_TIME); break; default: HALDEBUG(ah, HAL_DEBUG_PHYIO, "%s: unknown phy %u (rate ix %u)\n", __func__, rates->info[rateix].phy, rateix); txTime = 0; break; } return txTime; } int ath_hal_get_curmode(struct ath_hal *ah, const struct ieee80211_channel *chan) { /* * Pick a default mode at bootup. A channel change is inevitable. */ if (!chan) return HAL_MODE_11NG_HT20; if (IEEE80211_IS_CHAN_TURBO(chan)) return HAL_MODE_TURBO; /* check for NA_HT before plain A, since IS_CHAN_A includes NA_HT */ if (IEEE80211_IS_CHAN_5GHZ(chan) && IEEE80211_IS_CHAN_HT20(chan)) return HAL_MODE_11NA_HT20; if (IEEE80211_IS_CHAN_5GHZ(chan) && IEEE80211_IS_CHAN_HT40U(chan)) return HAL_MODE_11NA_HT40PLUS; if (IEEE80211_IS_CHAN_5GHZ(chan) && IEEE80211_IS_CHAN_HT40D(chan)) return HAL_MODE_11NA_HT40MINUS; if (IEEE80211_IS_CHAN_A(chan)) return HAL_MODE_11A; /* check for NG_HT before plain G, since IS_CHAN_G includes NG_HT */ if (IEEE80211_IS_CHAN_2GHZ(chan) && IEEE80211_IS_CHAN_HT20(chan)) return HAL_MODE_11NG_HT20; if (IEEE80211_IS_CHAN_2GHZ(chan) && IEEE80211_IS_CHAN_HT40U(chan)) return HAL_MODE_11NG_HT40PLUS; if (IEEE80211_IS_CHAN_2GHZ(chan) && IEEE80211_IS_CHAN_HT40D(chan)) return HAL_MODE_11NG_HT40MINUS; /* * XXX For FreeBSD, will this work correctly given the DYN * chan mode (OFDM+CCK dynamic) ? We have pure-G versions DYN-BG.. */ if (IEEE80211_IS_CHAN_G(chan)) return HAL_MODE_11G; if (IEEE80211_IS_CHAN_B(chan)) return HAL_MODE_11B; HALASSERT(0); return HAL_MODE_11NG_HT20; } typedef enum { WIRELESS_MODE_11a = 0, WIRELESS_MODE_TURBO = 1, WIRELESS_MODE_11b = 2, WIRELESS_MODE_11g = 3, WIRELESS_MODE_108g = 4, WIRELESS_MODE_MAX } WIRELESS_MODE; static WIRELESS_MODE ath_hal_chan2wmode(struct ath_hal *ah, const struct ieee80211_channel *chan) { if (IEEE80211_IS_CHAN_B(chan)) return WIRELESS_MODE_11b; if (IEEE80211_IS_CHAN_G(chan)) return WIRELESS_MODE_11g; if (IEEE80211_IS_CHAN_108G(chan)) return WIRELESS_MODE_108g; if (IEEE80211_IS_CHAN_TURBO(chan)) return WIRELESS_MODE_TURBO; return WIRELESS_MODE_11a; } /* * Convert between microseconds and core system clocks. */ /* 11a Turbo 11b 11g 108g */ static const uint8_t CLOCK_RATE[] = { 40, 80, 22, 44, 88 }; #define CLOCK_FAST_RATE_5GHZ_OFDM 44 u_int ath_hal_mac_clks(struct ath_hal *ah, u_int usecs) { const struct ieee80211_channel *c = AH_PRIVATE(ah)->ah_curchan; u_int clks; /* NB: ah_curchan may be null when called attach time */ /* XXX merlin and later specific workaround - 5ghz fast clock is 44 */ if (c != AH_NULL && IS_5GHZ_FAST_CLOCK_EN(ah, c)) { clks = usecs * CLOCK_FAST_RATE_5GHZ_OFDM; if (IEEE80211_IS_CHAN_HT40(c)) clks <<= 1; } else if (c != AH_NULL) { clks = usecs * CLOCK_RATE[ath_hal_chan2wmode(ah, c)]; if (IEEE80211_IS_CHAN_HT40(c)) clks <<= 1; } else clks = usecs * CLOCK_RATE[WIRELESS_MODE_11b]; /* Compensate for half/quarter rate */ if (c != AH_NULL && IEEE80211_IS_CHAN_HALF(c)) clks = clks / 2; else if (c != AH_NULL && IEEE80211_IS_CHAN_QUARTER(c)) clks = clks / 4; return clks; } u_int ath_hal_mac_usec(struct ath_hal *ah, u_int clks) { const struct ieee80211_channel *c = AH_PRIVATE(ah)->ah_curchan; u_int usec; /* NB: ah_curchan may be null when called attach time */ /* XXX merlin and later specific workaround - 5ghz fast clock is 44 */ if (c != AH_NULL && IS_5GHZ_FAST_CLOCK_EN(ah, c)) { usec = clks / CLOCK_FAST_RATE_5GHZ_OFDM; if (IEEE80211_IS_CHAN_HT40(c)) usec >>= 1; } else if (c != AH_NULL) { usec = clks / CLOCK_RATE[ath_hal_chan2wmode(ah, c)]; if (IEEE80211_IS_CHAN_HT40(c)) usec >>= 1; } else usec = clks / CLOCK_RATE[WIRELESS_MODE_11b]; return usec; } /* * Setup a h/w rate table's reverse lookup table and * fill in ack durations. This routine is called for * each rate table returned through the ah_getRateTable * method. The reverse lookup tables are assumed to be * initialized to zero (or at least the first entry). * We use this as a key that indicates whether or not * we've previously setup the reverse lookup table. * * XXX not reentrant, but shouldn't matter */ void ath_hal_setupratetable(struct ath_hal *ah, HAL_RATE_TABLE *rt) { #define N(a) (sizeof(a)/sizeof(a[0])) int i; if (rt->rateCodeToIndex[0] != 0) /* already setup */ return; for (i = 0; i < N(rt->rateCodeToIndex); i++) rt->rateCodeToIndex[i] = (uint8_t) -1; for (i = 0; i < rt->rateCount; i++) { uint8_t code = rt->info[i].rateCode; uint8_t cix = rt->info[i].controlRate; HALASSERT(code < N(rt->rateCodeToIndex)); rt->rateCodeToIndex[code] = i; HALASSERT((code | rt->info[i].shortPreamble) < N(rt->rateCodeToIndex)); rt->rateCodeToIndex[code | rt->info[i].shortPreamble] = i; /* * XXX for 11g the control rate to use for 5.5 and 11 Mb/s * depends on whether they are marked as basic rates; * the static tables are setup with an 11b-compatible * 2Mb/s rate which will work but is suboptimal */ rt->info[i].lpAckDuration = ath_hal_computetxtime(ah, rt, WLAN_CTRL_FRAME_SIZE, cix, AH_FALSE); rt->info[i].spAckDuration = ath_hal_computetxtime(ah, rt, WLAN_CTRL_FRAME_SIZE, cix, AH_TRUE); } #undef N } HAL_STATUS ath_hal_getcapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type, uint32_t capability, uint32_t *result) { const HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps; switch (type) { case HAL_CAP_REG_DMN: /* regulatory domain */ *result = AH_PRIVATE(ah)->ah_currentRD; return HAL_OK; case HAL_CAP_DFS_DMN: /* DFS Domain */ *result = AH_PRIVATE(ah)->ah_dfsDomain; return HAL_OK; case HAL_CAP_CIPHER: /* cipher handled in hardware */ case HAL_CAP_TKIP_MIC: /* handle TKIP MIC in hardware */ return HAL_ENOTSUPP; case HAL_CAP_TKIP_SPLIT: /* hardware TKIP uses split keys */ return HAL_ENOTSUPP; case HAL_CAP_PHYCOUNTERS: /* hardware PHY error counters */ return pCap->halHwPhyCounterSupport ? HAL_OK : HAL_ENXIO; case HAL_CAP_WME_TKIPMIC: /* hardware can do TKIP MIC when WMM is turned on */ return HAL_ENOTSUPP; case HAL_CAP_DIVERSITY: /* hardware supports fast diversity */ return HAL_ENOTSUPP; case HAL_CAP_KEYCACHE_SIZE: /* hardware key cache size */ *result = pCap->halKeyCacheSize; return HAL_OK; case HAL_CAP_NUM_TXQUEUES: /* number of hardware tx queues */ *result = pCap->halTotalQueues; return HAL_OK; case HAL_CAP_VEOL: /* hardware supports virtual EOL */ return pCap->halVEOLSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_PSPOLL: /* hardware PS-Poll support works */ return pCap->halPSPollBroken ? HAL_ENOTSUPP : HAL_OK; case HAL_CAP_COMPRESSION: return pCap->halCompressSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_BURST: return pCap->halBurstSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_FASTFRAME: return pCap->halFastFramesSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_DIAG: /* hardware diagnostic support */ *result = AH_PRIVATE(ah)->ah_diagreg; return HAL_OK; case HAL_CAP_TXPOW: /* global tx power limit */ switch (capability) { case 0: /* facility is supported */ return HAL_OK; case 1: /* current limit */ *result = AH_PRIVATE(ah)->ah_powerLimit; return HAL_OK; case 2: /* current max tx power */ *result = AH_PRIVATE(ah)->ah_maxPowerLevel; return HAL_OK; case 3: /* scale factor */ *result = AH_PRIVATE(ah)->ah_tpScale; return HAL_OK; } return HAL_ENOTSUPP; case HAL_CAP_BSSIDMASK: /* hardware supports bssid mask */ return pCap->halBssIdMaskSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_MCAST_KEYSRCH: /* multicast frame keycache search */ return pCap->halMcastKeySrchSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_TSF_ADJUST: /* hardware has beacon tsf adjust */ return HAL_ENOTSUPP; case HAL_CAP_RFSILENT: /* rfsilent support */ switch (capability) { case 0: /* facility is supported */ return pCap->halRfSilentSupport ? HAL_OK : HAL_ENOTSUPP; case 1: /* current setting */ return AH_PRIVATE(ah)->ah_rfkillEnabled ? HAL_OK : HAL_ENOTSUPP; case 2: /* rfsilent config */ *result = AH_PRIVATE(ah)->ah_rfsilent; return HAL_OK; } return HAL_ENOTSUPP; case HAL_CAP_11D: return HAL_OK; case HAL_CAP_HT: return pCap->halHTSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_GTXTO: return pCap->halGTTSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_FAST_CC: return pCap->halFastCCSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_TX_CHAINMASK: /* mask of TX chains supported */ *result = pCap->halTxChainMask; return HAL_OK; case HAL_CAP_RX_CHAINMASK: /* mask of RX chains supported */ *result = pCap->halRxChainMask; return HAL_OK; case HAL_CAP_NUM_GPIO_PINS: *result = pCap->halNumGpioPins; return HAL_OK; case HAL_CAP_CST: return pCap->halCSTSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_RTS_AGGR_LIMIT: *result = pCap->halRtsAggrLimit; return HAL_OK; case HAL_CAP_4ADDR_AGGR: return pCap->hal4AddrAggrSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_EXT_CHAN_DFS: return pCap->halExtChanDfsSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_RX_STBC: return pCap->halRxStbcSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_TX_STBC: return pCap->halTxStbcSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_COMBINED_RADAR_RSSI: return pCap->halUseCombinedRadarRssi ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_AUTO_SLEEP: return pCap->halAutoSleepSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_MBSSID_AGGR_SUPPORT: return pCap->halMbssidAggrSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_SPLIT_4KB_TRANS: /* hardware handles descriptors straddling 4k page boundary */ return pCap->hal4kbSplitTransSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_REG_FLAG: *result = AH_PRIVATE(ah)->ah_currentRDext; return HAL_OK; case HAL_CAP_ENHANCED_DMA_SUPPORT: return pCap->halEnhancedDmaSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_NUM_TXMAPS: *result = pCap->halNumTxMaps; return HAL_OK; case HAL_CAP_TXDESCLEN: *result = pCap->halTxDescLen; return HAL_OK; case HAL_CAP_TXSTATUSLEN: *result = pCap->halTxStatusLen; return HAL_OK; case HAL_CAP_RXSTATUSLEN: *result = pCap->halRxStatusLen; return HAL_OK; case HAL_CAP_RXFIFODEPTH: switch (capability) { case HAL_RX_QUEUE_HP: *result = pCap->halRxHpFifoDepth; return HAL_OK; case HAL_RX_QUEUE_LP: *result = pCap->halRxLpFifoDepth; return HAL_OK; default: return HAL_ENOTSUPP; } case HAL_CAP_RXBUFSIZE: case HAL_CAP_NUM_MR_RETRIES: *result = pCap->halNumMRRetries; return HAL_OK; case HAL_CAP_BT_COEX: return pCap->halBtCoexSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_SPECTRAL_SCAN: return pCap->halSpectralScanSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_HT20_SGI: return pCap->halHTSGI20Support ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_RXTSTAMP_PREC: /* rx desc tstamp precision (bits) */ *result = pCap->halTstampPrecision; return HAL_OK; case HAL_CAP_ANT_DIV_COMB: /* AR9285/AR9485 LNA diversity */ return pCap->halAntDivCombSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_ENHANCED_DFS_SUPPORT: return pCap->halEnhancedDfsSupport ? HAL_OK : HAL_ENOTSUPP; /* FreeBSD-specific entries for now */ case HAL_CAP_RXORN_FATAL: /* HAL_INT_RXORN treated as fatal */ return AH_PRIVATE(ah)->ah_rxornIsFatal ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_INTRMASK: /* mask of supported interrupts */ *result = pCap->halIntrMask; return HAL_OK; case HAL_CAP_BSSIDMATCH: /* hardware has disable bssid match */ return pCap->halBssidMatchSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_STREAMS: /* number of 11n spatial streams */ switch (capability) { case 0: /* TX */ *result = pCap->halTxStreams; return HAL_OK; case 1: /* RX */ *result = pCap->halRxStreams; return HAL_OK; default: return HAL_ENOTSUPP; } case HAL_CAP_RXDESC_SELFLINK: /* hardware supports self-linked final RX descriptors correctly */ return pCap->halHasRxSelfLinkedTail ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_LONG_RXDESC_TSF: /* 32 bit TSF in RX descriptor? */ return pCap->halHasLongRxDescTsf ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_BB_READ_WAR: /* Baseband read WAR */ return pCap->halHasBBReadWar? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_SERIALISE_WAR: /* PCI register serialisation */ return pCap->halSerialiseRegWar ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_MFP: /* Management frame protection setting */ *result = pCap->halMfpSupport; return HAL_OK; case HAL_CAP_RX_LNA_MIXING: /* Hardware uses an RX LNA mixer to map 2 antennas to a 1 stream receiver */ return pCap->halRxUsingLnaMixing ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_DO_MYBEACON: /* Hardware supports filtering my-beacons */ return pCap->halRxDoMyBeacon ? HAL_OK : HAL_ENOTSUPP; default: return HAL_EINVAL; } } HAL_BOOL ath_hal_setcapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type, uint32_t capability, uint32_t setting, HAL_STATUS *status) { switch (type) { case HAL_CAP_TXPOW: switch (capability) { case 3: if (setting <= HAL_TP_SCALE_MIN) { AH_PRIVATE(ah)->ah_tpScale = setting; return AH_TRUE; } break; } break; case HAL_CAP_RFSILENT: /* rfsilent support */ /* * NB: allow even if halRfSilentSupport is false * in case the EEPROM is misprogrammed. */ switch (capability) { case 1: /* current setting */ AH_PRIVATE(ah)->ah_rfkillEnabled = (setting != 0); return AH_TRUE; case 2: /* rfsilent config */ /* XXX better done per-chip for validation? */ AH_PRIVATE(ah)->ah_rfsilent = setting; return AH_TRUE; } break; case HAL_CAP_REG_DMN: /* regulatory domain */ AH_PRIVATE(ah)->ah_currentRD = setting; return AH_TRUE; case HAL_CAP_RXORN_FATAL: /* HAL_INT_RXORN treated as fatal */ AH_PRIVATE(ah)->ah_rxornIsFatal = setting; return AH_TRUE; default: break; } if (status) *status = HAL_EINVAL; return AH_FALSE; } /* * Common support for getDiagState method. */ static u_int ath_hal_getregdump(struct ath_hal *ah, const HAL_REGRANGE *regs, void *dstbuf, int space) { uint32_t *dp = dstbuf; int i; for (i = 0; space >= 2*sizeof(uint32_t); i++) { uint32_t r = regs[i].start; uint32_t e = regs[i].end; *dp++ = r; *dp++ = e; space -= 2*sizeof(uint32_t); do { *dp++ = OS_REG_READ(ah, r); r += sizeof(uint32_t); space -= sizeof(uint32_t); } while (r <= e && space >= sizeof(uint32_t)); } return (char *) dp - (char *) dstbuf; } static void ath_hal_setregs(struct ath_hal *ah, const HAL_REGWRITE *regs, int space) { while (space >= sizeof(HAL_REGWRITE)) { OS_REG_WRITE(ah, regs->addr, regs->value); regs++, space -= sizeof(HAL_REGWRITE); } } HAL_BOOL ath_hal_getdiagstate(struct ath_hal *ah, int request, const void *args, uint32_t argsize, void **result, uint32_t *resultsize) { switch (request) { case HAL_DIAG_REVS: *result = &AH_PRIVATE(ah)->ah_devid; *resultsize = sizeof(HAL_REVS); return AH_TRUE; case HAL_DIAG_REGS: *resultsize = ath_hal_getregdump(ah, args, *result,*resultsize); return AH_TRUE; case HAL_DIAG_SETREGS: ath_hal_setregs(ah, args, argsize); *resultsize = 0; return AH_TRUE; case HAL_DIAG_FATALERR: *result = &AH_PRIVATE(ah)->ah_fatalState[0]; *resultsize = sizeof(AH_PRIVATE(ah)->ah_fatalState); return AH_TRUE; case HAL_DIAG_EEREAD: if (argsize != sizeof(uint16_t)) return AH_FALSE; if (!ath_hal_eepromRead(ah, *(const uint16_t *)args, *result)) return AH_FALSE; *resultsize = sizeof(uint16_t); return AH_TRUE; #ifdef AH_PRIVATE_DIAG case HAL_DIAG_SETKEY: { const HAL_DIAG_KEYVAL *dk; if (argsize != sizeof(HAL_DIAG_KEYVAL)) return AH_FALSE; dk = (const HAL_DIAG_KEYVAL *)args; return ah->ah_setKeyCacheEntry(ah, dk->dk_keyix, &dk->dk_keyval, dk->dk_mac, dk->dk_xor); } case HAL_DIAG_RESETKEY: if (argsize != sizeof(uint16_t)) return AH_FALSE; return ah->ah_resetKeyCacheEntry(ah, *(const uint16_t *)args); #ifdef AH_SUPPORT_WRITE_EEPROM case HAL_DIAG_EEWRITE: { const HAL_DIAG_EEVAL *ee; if (argsize != sizeof(HAL_DIAG_EEVAL)) return AH_FALSE; ee = (const HAL_DIAG_EEVAL *)args; return ath_hal_eepromWrite(ah, ee->ee_off, ee->ee_data); } #endif /* AH_SUPPORT_WRITE_EEPROM */ #endif /* AH_PRIVATE_DIAG */ case HAL_DIAG_11NCOMPAT: if (argsize == 0) { *resultsize = sizeof(uint32_t); *((uint32_t *)(*result)) = AH_PRIVATE(ah)->ah_11nCompat; } else if (argsize == sizeof(uint32_t)) { AH_PRIVATE(ah)->ah_11nCompat = *(const uint32_t *)args; } else return AH_FALSE; return AH_TRUE; case HAL_DIAG_CHANSURVEY: *result = &AH_PRIVATE(ah)->ah_chansurvey; *resultsize = sizeof(HAL_CHANNEL_SURVEY); return AH_TRUE; } return AH_FALSE; } /* * Set the properties of the tx queue with the parameters * from qInfo. */ HAL_BOOL ath_hal_setTxQProps(struct ath_hal *ah, HAL_TX_QUEUE_INFO *qi, const HAL_TXQ_INFO *qInfo) { uint32_t cw; if (qi->tqi_type == HAL_TX_QUEUE_INACTIVE) { HALDEBUG(ah, HAL_DEBUG_TXQUEUE, "%s: inactive queue\n", __func__); return AH_FALSE; } /* XXX validate parameters */ qi->tqi_ver = qInfo->tqi_ver; qi->tqi_subtype = qInfo->tqi_subtype; qi->tqi_qflags = qInfo->tqi_qflags; qi->tqi_priority = qInfo->tqi_priority; if (qInfo->tqi_aifs != HAL_TXQ_USEDEFAULT) qi->tqi_aifs = AH_MIN(qInfo->tqi_aifs, 255); else qi->tqi_aifs = INIT_AIFS; if (qInfo->tqi_cwmin != HAL_TXQ_USEDEFAULT) { cw = AH_MIN(qInfo->tqi_cwmin, 1024); /* make sure that the CWmin is of the form (2^n - 1) */ qi->tqi_cwmin = 1; while (qi->tqi_cwmin < cw) qi->tqi_cwmin = (qi->tqi_cwmin << 1) | 1; } else qi->tqi_cwmin = qInfo->tqi_cwmin; if (qInfo->tqi_cwmax != HAL_TXQ_USEDEFAULT) { cw = AH_MIN(qInfo->tqi_cwmax, 1024); /* make sure that the CWmax is of the form (2^n - 1) */ qi->tqi_cwmax = 1; while (qi->tqi_cwmax < cw) qi->tqi_cwmax = (qi->tqi_cwmax << 1) | 1; } else qi->tqi_cwmax = INIT_CWMAX; /* Set retry limit values */ if (qInfo->tqi_shretry != 0) qi->tqi_shretry = AH_MIN(qInfo->tqi_shretry, 15); else qi->tqi_shretry = INIT_SH_RETRY; if (qInfo->tqi_lgretry != 0) qi->tqi_lgretry = AH_MIN(qInfo->tqi_lgretry, 15); else qi->tqi_lgretry = INIT_LG_RETRY; qi->tqi_cbrPeriod = qInfo->tqi_cbrPeriod; qi->tqi_cbrOverflowLimit = qInfo->tqi_cbrOverflowLimit; qi->tqi_burstTime = qInfo->tqi_burstTime; qi->tqi_readyTime = qInfo->tqi_readyTime; switch (qInfo->tqi_subtype) { case HAL_WME_UPSD: if (qi->tqi_type == HAL_TX_QUEUE_DATA) qi->tqi_intFlags = HAL_TXQ_USE_LOCKOUT_BKOFF_DIS; break; default: break; /* NB: silence compiler */ } return AH_TRUE; } HAL_BOOL ath_hal_getTxQProps(struct ath_hal *ah, HAL_TXQ_INFO *qInfo, const HAL_TX_QUEUE_INFO *qi) { if (qi->tqi_type == HAL_TX_QUEUE_INACTIVE) { HALDEBUG(ah, HAL_DEBUG_TXQUEUE, "%s: inactive queue\n", __func__); return AH_FALSE; } qInfo->tqi_qflags = qi->tqi_qflags; qInfo->tqi_ver = qi->tqi_ver; qInfo->tqi_subtype = qi->tqi_subtype; qInfo->tqi_qflags = qi->tqi_qflags; qInfo->tqi_priority = qi->tqi_priority; qInfo->tqi_aifs = qi->tqi_aifs; qInfo->tqi_cwmin = qi->tqi_cwmin; qInfo->tqi_cwmax = qi->tqi_cwmax; qInfo->tqi_shretry = qi->tqi_shretry; qInfo->tqi_lgretry = qi->tqi_lgretry; qInfo->tqi_cbrPeriod = qi->tqi_cbrPeriod; qInfo->tqi_cbrOverflowLimit = qi->tqi_cbrOverflowLimit; qInfo->tqi_burstTime = qi->tqi_burstTime; qInfo->tqi_readyTime = qi->tqi_readyTime; return AH_TRUE; } /* 11a Turbo 11b 11g 108g */ static const int16_t NOISE_FLOOR[] = { -96, -93, -98, -96, -93 }; /* * Read the current channel noise floor and return. * If nf cal hasn't finished, channel noise floor should be 0 * and we return a nominal value based on band and frequency. * * NB: This is a private routine used by per-chip code to * implement the ah_getChanNoise method. */ int16_t ath_hal_getChanNoise(struct ath_hal *ah, const struct ieee80211_channel *chan) { HAL_CHANNEL_INTERNAL *ichan; ichan = ath_hal_checkchannel(ah, chan); if (ichan == AH_NULL) { HALDEBUG(ah, HAL_DEBUG_NFCAL, "%s: invalid channel %u/0x%x; no mapping\n", __func__, chan->ic_freq, chan->ic_flags); return 0; } if (ichan->rawNoiseFloor == 0) { WIRELESS_MODE mode = ath_hal_chan2wmode(ah, chan); HALASSERT(mode < WIRELESS_MODE_MAX); return NOISE_FLOOR[mode] + ath_hal_getNfAdjust(ah, ichan); } else return ichan->rawNoiseFloor + ichan->noiseFloorAdjust; } /* * Fetch the current setup of ctl/ext noise floor values. * * If the CHANNEL_MIMO_NF_VALID flag isn't set, the array is simply * populated with values from NOISE_FLOOR[] + ath_hal_getNfAdjust(). * * The caller must supply ctl/ext NF arrays which are at least * AH_MAX_CHAINS entries long. */ int ath_hal_get_mimo_chan_noise(struct ath_hal *ah, const struct ieee80211_channel *chan, int16_t *nf_ctl, int16_t *nf_ext) { #ifdef AH_SUPPORT_AR5416 HAL_CHANNEL_INTERNAL *ichan; int i; ichan = ath_hal_checkchannel(ah, chan); if (ichan == AH_NULL) { HALDEBUG(ah, HAL_DEBUG_NFCAL, "%s: invalid channel %u/0x%x; no mapping\n", __func__, chan->ic_freq, chan->ic_flags); for (i = 0; i < AH_MAX_CHAINS; i++) { nf_ctl[i] = nf_ext[i] = 0; } return 0; } /* Return 0 if there's no valid MIMO values (yet) */ if (! (ichan->privFlags & CHANNEL_MIMO_NF_VALID)) { for (i = 0; i < AH_MAX_CHAINS; i++) { nf_ctl[i] = nf_ext[i] = 0; } return 0; } if (ichan->rawNoiseFloor == 0) { WIRELESS_MODE mode = ath_hal_chan2wmode(ah, chan); HALASSERT(mode < WIRELESS_MODE_MAX); /* * See the comment below - this could cause issues for * stations which have a very low RSSI, below the * 'normalised' NF values in NOISE_FLOOR[]. */ for (i = 0; i < AH_MAX_CHAINS; i++) { nf_ctl[i] = nf_ext[i] = NOISE_FLOOR[mode] + ath_hal_getNfAdjust(ah, ichan); } return 1; } else { /* * The value returned here from a MIMO radio is presumed to be * "good enough" as a NF calculation. As RSSI values are calculated * against this, an adjusted NF may be higher than the RSSI value * returned from a vary weak station, resulting in an obscenely * high signal strength calculation being returned. * * This should be re-evaluated at a later date, along with any * signal strength calculations which are made. Quite likely the * RSSI values will need to be adjusted to ensure the calculations * don't "wrap" when RSSI is less than the "adjusted" NF value. * ("Adjust" here is via ichan->noiseFloorAdjust.) */ for (i = 0; i < AH_MAX_CHAINS; i++) { nf_ctl[i] = ichan->noiseFloorCtl[i] + ath_hal_getNfAdjust(ah, ichan); nf_ext[i] = ichan->noiseFloorExt[i] + ath_hal_getNfAdjust(ah, ichan); } return 1; } #else return 0; #endif /* AH_SUPPORT_AR5416 */ } /* * Process all valid raw noise floors into the dBm noise floor values. * Though our device has no reference for a dBm noise floor, we perform * a relative minimization of NF's based on the lowest NF found across a * channel scan. */ void ath_hal_process_noisefloor(struct ath_hal *ah) { HAL_CHANNEL_INTERNAL *c; int16_t correct2, correct5; int16_t lowest2, lowest5; int i; /* * Find the lowest 2GHz and 5GHz noise floor values after adjusting * for statistically recorded NF/channel deviation. */ correct2 = lowest2 = 0; correct5 = lowest5 = 0; for (i = 0; i < AH_PRIVATE(ah)->ah_nchan; i++) { WIRELESS_MODE mode; int16_t nf; c = &AH_PRIVATE(ah)->ah_channels[i]; if (c->rawNoiseFloor >= 0) continue; /* XXX can't identify proper mode */ mode = IS_CHAN_5GHZ(c) ? WIRELESS_MODE_11a : WIRELESS_MODE_11g; nf = c->rawNoiseFloor + NOISE_FLOOR[mode] + ath_hal_getNfAdjust(ah, c); if (IS_CHAN_5GHZ(c)) { if (nf < lowest5) { lowest5 = nf; correct5 = NOISE_FLOOR[mode] - (c->rawNoiseFloor + ath_hal_getNfAdjust(ah, c)); } } else { if (nf < lowest2) { lowest2 = nf; correct2 = NOISE_FLOOR[mode] - (c->rawNoiseFloor + ath_hal_getNfAdjust(ah, c)); } } } /* Correct the channels to reach the expected NF value */ for (i = 0; i < AH_PRIVATE(ah)->ah_nchan; i++) { c = &AH_PRIVATE(ah)->ah_channels[i]; if (c->rawNoiseFloor >= 0) continue; /* Apply correction factor */ c->noiseFloorAdjust = ath_hal_getNfAdjust(ah, c) + (IS_CHAN_5GHZ(c) ? correct5 : correct2); HALDEBUG(ah, HAL_DEBUG_NFCAL, "%u raw nf %d adjust %d\n", c->channel, c->rawNoiseFloor, c->noiseFloorAdjust); } } /* * INI support routines. */ int ath_hal_ini_write(struct ath_hal *ah, const HAL_INI_ARRAY *ia, int col, int regWr) { int r; HALASSERT(col < ia->cols); for (r = 0; r < ia->rows; r++) { OS_REG_WRITE(ah, HAL_INI_VAL(ia, r, 0), HAL_INI_VAL(ia, r, col)); /* Analog shift register delay seems needed for Merlin - PR kern/154220 */ if (HAL_INI_VAL(ia, r, 0) >= 0x7800 && HAL_INI_VAL(ia, r, 0) < 0x7900) OS_DELAY(100); DMA_YIELD(regWr); } return regWr; } void ath_hal_ini_bank_setup(uint32_t data[], const HAL_INI_ARRAY *ia, int col) { int r; HALASSERT(col < ia->cols); for (r = 0; r < ia->rows; r++) data[r] = HAL_INI_VAL(ia, r, col); } int ath_hal_ini_bank_write(struct ath_hal *ah, const HAL_INI_ARRAY *ia, const uint32_t data[], int regWr) { int r; for (r = 0; r < ia->rows; r++) { OS_REG_WRITE(ah, HAL_INI_VAL(ia, r, 0), data[r]); DMA_YIELD(regWr); } return regWr; } /* * These are EEPROM board related routines which should likely live in * a helper library of some sort. */ /************************************************************** * ath_ee_getLowerUppderIndex * * Return indices surrounding the value in sorted integer lists. * Requirement: the input list must be monotonically increasing * and populated up to the list size * Returns: match is set if an index in the array matches exactly * or a the target is before or after the range of the array. */ HAL_BOOL ath_ee_getLowerUpperIndex(uint8_t target, uint8_t *pList, uint16_t listSize, uint16_t *indexL, uint16_t *indexR) { uint16_t i; /* * Check first and last elements for beyond ordered array cases. */ if (target <= pList[0]) { *indexL = *indexR = 0; return AH_TRUE; } if (target >= pList[listSize-1]) { *indexL = *indexR = (uint16_t)(listSize - 1); return AH_TRUE; } /* look for value being near or between 2 values in list */ for (i = 0; i < listSize - 1; i++) { /* * If value is close to the current value of the list * then target is not between values, it is one of the values */ if (pList[i] == target) { *indexL = *indexR = i; return AH_TRUE; } /* * Look for value being between current value and next value * if so return these 2 values */ if (target < pList[i + 1]) { *indexL = i; *indexR = (uint16_t)(i + 1); return AH_FALSE; } } HALASSERT(0); *indexL = *indexR = 0; return AH_FALSE; } /************************************************************** * ath_ee_FillVpdTable * * Fill the Vpdlist for indices Pmax-Pmin * Note: pwrMin, pwrMax and Vpdlist are all in dBm * 4 */ HAL_BOOL ath_ee_FillVpdTable(uint8_t pwrMin, uint8_t pwrMax, uint8_t *pPwrList, uint8_t *pVpdList, uint16_t numIntercepts, uint8_t *pRetVpdList) { uint16_t i, k; uint8_t currPwr = pwrMin; uint16_t idxL, idxR; HALASSERT(pwrMax > pwrMin); for (i = 0; i <= (pwrMax - pwrMin) / 2; i++) { ath_ee_getLowerUpperIndex(currPwr, pPwrList, numIntercepts, &(idxL), &(idxR)); if (idxR < 1) idxR = 1; /* extrapolate below */ if (idxL == numIntercepts - 1) idxL = (uint16_t)(numIntercepts - 2); /* extrapolate above */ if (pPwrList[idxL] == pPwrList[idxR]) k = pVpdList[idxL]; else k = (uint16_t)( ((currPwr - pPwrList[idxL]) * pVpdList[idxR] + (pPwrList[idxR] - currPwr) * pVpdList[idxL]) / (pPwrList[idxR] - pPwrList[idxL]) ); HALASSERT(k < 256); pRetVpdList[i] = (uint8_t)k; currPwr += 2; /* half dB steps */ } return AH_TRUE; } /************************************************************************** * ath_ee_interpolate * * Returns signed interpolated or the scaled up interpolated value */ int16_t ath_ee_interpolate(uint16_t target, uint16_t srcLeft, uint16_t srcRight, int16_t targetLeft, int16_t targetRight) { int16_t rv; if (srcRight == srcLeft) { rv = targetLeft; } else { rv = (int16_t)( ((target - srcLeft) * targetRight + (srcRight - target) * targetLeft) / (srcRight - srcLeft) ); } return rv; } /* * Adjust the TSF. */ void ath_hal_adjusttsf(struct ath_hal *ah, int32_t tsfdelta) { /* XXX handle wrap/overflow */ OS_REG_WRITE(ah, AR_TSF_L32, OS_REG_READ(ah, AR_TSF_L32) + tsfdelta); } /* * Enable or disable CCA. */ void ath_hal_setcca(struct ath_hal *ah, int ena) { /* * NB: fill me in; this is not provided by default because disabling * CCA in most locales violates regulatory. */ } /* * Get CCA setting. */ int ath_hal_getcca(struct ath_hal *ah) { u_int32_t diag; if (ath_hal_getcapability(ah, HAL_CAP_DIAG, 0, &diag) != HAL_OK) return 1; return ((diag & 0x500000) == 0); } /* * This routine is only needed when supporting EEPROM-in-RAM setups * (eg embedded SoCs and on-board PCI/PCIe devices.) */ /* NB: This is in 16 bit words; not bytes */ /* XXX This doesn't belong here! */ #define ATH_DATA_EEPROM_SIZE 2048 HAL_BOOL ath_hal_EepromDataRead(struct ath_hal *ah, u_int off, uint16_t *data) { if (ah->ah_eepromdata == AH_NULL) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: no eeprom data!\n", __func__); return AH_FALSE; } if (off > ATH_DATA_EEPROM_SIZE) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: offset %x > %x\n", __func__, off, ATH_DATA_EEPROM_SIZE); return AH_FALSE; } (*data) = ah->ah_eepromdata[off]; return AH_TRUE; } /* * Do a 2GHz specific MHz->IEEE based on the hardware * frequency. * * This is the unmapped frequency which is programmed into the hardware. */ int ath_hal_mhz2ieee_2ghz(struct ath_hal *ah, int freq) { if (freq == 2484) return 14; if (freq < 2484) return ((int) freq - 2407) / 5; else return 15 + ((freq - 2512) / 20); } /* * Clear the current survey data. * * This should be done during a channel change. */ void ath_hal_survey_clear(struct ath_hal *ah) { OS_MEMZERO(&AH_PRIVATE(ah)->ah_chansurvey, sizeof(AH_PRIVATE(ah)->ah_chansurvey)); } /* * Add a sample to the channel survey. */ void ath_hal_survey_add_sample(struct ath_hal *ah, HAL_SURVEY_SAMPLE *hs) { HAL_CHANNEL_SURVEY *cs; cs = &AH_PRIVATE(ah)->ah_chansurvey; OS_MEMCPY(&cs->samples[cs->cur_sample], hs, sizeof(*hs)); cs->samples[cs->cur_sample].seq_num = cs->cur_seq; cs->cur_sample = (cs->cur_sample + 1) % CHANNEL_SURVEY_SAMPLE_COUNT; cs->cur_seq++; } Index: head/sys/dev/ath/ath_hal/ah.h =================================================================== --- head/sys/dev/ath/ath_hal/ah.h (revision 298938) +++ head/sys/dev/ath/ath_hal/ah.h (revision 298939) @@ -1,1804 +1,1804 @@ /* * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * Copyright (c) 2002-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #ifndef _ATH_AH_H_ #define _ATH_AH_H_ /* * Atheros Hardware Access Layer * * Clients of the HAL call ath_hal_attach to obtain a reference to an ath_hal * structure for use with the device. Hardware-related operations that * follow must call back into the HAL through interface, supplying the * reference as the first parameter. */ #include "ah_osdep.h" /* * The maximum number of TX/RX chains supported. * This is intended to be used by various statistics gathering operations * (NF, RSSI, EVM). */ #define AH_MAX_CHAINS 3 #define AH_MIMO_MAX_EVM_PILOTS 6 /* * __ahdecl is analogous to _cdecl; it defines the calling * convention used within the HAL. For most systems this * can just default to be empty and the compiler will (should) * use _cdecl. For systems where _cdecl is not compatible this * must be defined. See linux/ah_osdep.h for an example. */ #ifndef __ahdecl #define __ahdecl #endif /* * Status codes that may be returned by the HAL. Note that * interfaces that return a status code set it only when an * error occurs--i.e. you cannot check it for success. */ typedef enum { HAL_OK = 0, /* No error */ HAL_ENXIO = 1, /* No hardware present */ HAL_ENOMEM = 2, /* Memory allocation failed */ HAL_EIO = 3, /* Hardware didn't respond as expected */ HAL_EEMAGIC = 4, /* EEPROM magic number invalid */ HAL_EEVERSION = 5, /* EEPROM version invalid */ HAL_EELOCKED = 6, /* EEPROM unreadable */ HAL_EEBADSUM = 7, /* EEPROM checksum invalid */ HAL_EEREAD = 8, /* EEPROM read problem */ HAL_EEBADMAC = 9, /* EEPROM mac address invalid */ HAL_EESIZE = 10, /* EEPROM size not supported */ HAL_EEWRITE = 11, /* Attempt to change write-locked EEPROM */ HAL_EINVAL = 12, /* Invalid parameter to function */ HAL_ENOTSUPP = 13, /* Hardware revision not supported */ HAL_ESELFTEST = 14, /* Hardware self-test failed */ HAL_EINPROGRESS = 15, /* Operation incomplete */ HAL_EEBADREG = 16, /* EEPROM invalid regulatory contents */ HAL_EEBADCC = 17, /* EEPROM invalid country code */ HAL_INV_PMODE = 18, /* Couldn't bring out of sleep state */ } HAL_STATUS; typedef enum { AH_FALSE = 0, /* NB: lots of code assumes false is zero */ AH_TRUE = 1, } HAL_BOOL; typedef enum { HAL_CAP_REG_DMN = 0, /* current regulatory domain */ HAL_CAP_CIPHER = 1, /* hardware supports cipher */ HAL_CAP_TKIP_MIC = 2, /* handle TKIP MIC in hardware */ HAL_CAP_TKIP_SPLIT = 3, /* hardware TKIP uses split keys */ HAL_CAP_PHYCOUNTERS = 4, /* hardware PHY error counters */ HAL_CAP_DIVERSITY = 5, /* hardware supports fast diversity */ HAL_CAP_KEYCACHE_SIZE = 6, /* number of entries in key cache */ HAL_CAP_NUM_TXQUEUES = 7, /* number of hardware xmit queues */ HAL_CAP_VEOL = 9, /* hardware supports virtual EOL */ HAL_CAP_PSPOLL = 10, /* hardware has working PS-Poll support */ HAL_CAP_DIAG = 11, /* hardware diagnostic support */ HAL_CAP_COMPRESSION = 12, /* hardware supports compression */ HAL_CAP_BURST = 13, /* hardware supports packet bursting */ HAL_CAP_FASTFRAME = 14, /* hardware supoprts fast frames */ HAL_CAP_TXPOW = 15, /* global tx power limit */ HAL_CAP_TPC = 16, /* per-packet tx power control */ HAL_CAP_PHYDIAG = 17, /* hardware phy error diagnostic */ HAL_CAP_BSSIDMASK = 18, /* hardware supports bssid mask */ HAL_CAP_MCAST_KEYSRCH = 19, /* hardware has multicast key search */ HAL_CAP_TSF_ADJUST = 20, /* hardware has beacon tsf adjust */ /* 21 was HAL_CAP_XR */ HAL_CAP_WME_TKIPMIC = 22, /* hardware can support TKIP MIC when WMM is turned on */ /* 23 was HAL_CAP_CHAN_HALFRATE */ /* 24 was HAL_CAP_CHAN_QUARTERRATE */ HAL_CAP_RFSILENT = 25, /* hardware has rfsilent support */ HAL_CAP_TPC_ACK = 26, /* ack txpower with per-packet tpc */ HAL_CAP_TPC_CTS = 27, /* cts txpower with per-packet tpc */ HAL_CAP_11D = 28, /* 11d beacon support for changing cc */ HAL_CAP_PCIE_PS = 29, HAL_CAP_HT = 30, /* hardware can support HT */ HAL_CAP_GTXTO = 31, /* hardware supports global tx timeout */ HAL_CAP_FAST_CC = 32, /* hardware supports fast channel change */ HAL_CAP_TX_CHAINMASK = 33, /* mask of TX chains supported */ HAL_CAP_RX_CHAINMASK = 34, /* mask of RX chains supported */ HAL_CAP_NUM_GPIO_PINS = 36, /* number of GPIO pins */ HAL_CAP_CST = 38, /* hardware supports carrier sense timeout */ HAL_CAP_RIFS_RX = 39, HAL_CAP_RIFS_TX = 40, HAL_CAP_FORCE_PPM = 41, HAL_CAP_RTS_AGGR_LIMIT = 42, /* aggregation limit with RTS */ HAL_CAP_4ADDR_AGGR = 43, /* hardware is capable of 4addr aggregation */ HAL_CAP_DFS_DMN = 44, /* current DFS domain */ HAL_CAP_EXT_CHAN_DFS = 45, /* DFS support for extension channel */ HAL_CAP_COMBINED_RADAR_RSSI = 46, /* Is combined RSSI for radar accurate */ HAL_CAP_AUTO_SLEEP = 48, /* hardware can go to network sleep automatically after waking up to receive TIM */ HAL_CAP_MBSSID_AGGR_SUPPORT = 49, /* Support for mBSSID Aggregation */ HAL_CAP_SPLIT_4KB_TRANS = 50, /* hardware supports descriptors straddling a 4k page boundary */ HAL_CAP_REG_FLAG = 51, /* Regulatory domain flags */ HAL_CAP_BB_RIFS_HANG = 52, HAL_CAP_RIFS_RX_ENABLED = 53, HAL_CAP_BB_DFS_HANG = 54, HAL_CAP_RX_STBC = 58, HAL_CAP_TX_STBC = 59, HAL_CAP_BT_COEX = 60, /* hardware is capable of bluetooth coexistence */ HAL_CAP_DYNAMIC_SMPS = 61, /* Dynamic MIMO Power Save hardware support */ HAL_CAP_DS = 67, /* 2 stream */ HAL_CAP_BB_RX_CLEAR_STUCK_HANG = 68, HAL_CAP_MAC_HANG = 69, /* can MAC hang */ HAL_CAP_MFP = 70, /* Management Frame Protection in hardware */ HAL_CAP_TS = 72, /* 3 stream */ HAL_CAP_ENHANCED_DMA_SUPPORT = 75, /* DMA FIFO support */ HAL_CAP_NUM_TXMAPS = 76, /* Number of buffers in a transmit descriptor */ HAL_CAP_TXDESCLEN = 77, /* Length of transmit descriptor */ HAL_CAP_TXSTATUSLEN = 78, /* Length of transmit status descriptor */ HAL_CAP_RXSTATUSLEN = 79, /* Length of transmit status descriptor */ HAL_CAP_RXFIFODEPTH = 80, /* Receive hardware FIFO depth */ HAL_CAP_RXBUFSIZE = 81, /* Receive Buffer Length */ HAL_CAP_NUM_MR_RETRIES = 82, /* limit on multirate retries */ HAL_CAP_OL_PWRCTRL = 84, /* Open loop TX power control */ HAL_CAP_SPECTRAL_SCAN = 90, /* Hardware supports spectral scan */ HAL_CAP_BB_PANIC_WATCHDOG = 92, HAL_CAP_HT20_SGI = 96, /* hardware supports HT20 short GI */ HAL_CAP_LDPC = 99, HAL_CAP_RXTSTAMP_PREC = 100, /* rx desc tstamp precision (bits) */ HAL_CAP_ANT_DIV_COMB = 105, /* Enable antenna diversity/combining */ HAL_CAP_PHYRESTART_CLR_WAR = 106, /* in some cases, clear phy restart to fix bb hang */ HAL_CAP_ENTERPRISE_MODE = 107, /* Enterprise mode features */ HAL_CAP_LDPCWAR = 108, HAL_CAP_CHANNEL_SWITCH_TIME_USEC = 109, /* Channel change time, usec */ HAL_CAP_ENABLE_APM = 110, /* APM enabled */ HAL_CAP_PCIE_LCR_EXTSYNC_EN = 111, HAL_CAP_PCIE_LCR_OFFSET = 112, HAL_CAP_ENHANCED_DFS_SUPPORT = 117, /* hardware supports enhanced DFS */ HAL_CAP_MCI = 118, HAL_CAP_SMARTANTENNA = 119, HAL_CAP_TRAFFIC_FAST_RECOVER = 120, HAL_CAP_TX_DIVERSITY = 121, HAL_CAP_CRDC = 122, /* The following are private to the FreeBSD HAL (224 onward) */ HAL_CAP_INTMIT = 229, /* interference mitigation */ HAL_CAP_RXORN_FATAL = 230, /* HAL_INT_RXORN treated as fatal */ HAL_CAP_BB_HANG = 235, /* can baseband hang */ HAL_CAP_INTRMASK = 237, /* bitmask of supported interrupts */ HAL_CAP_BSSIDMATCH = 238, /* hardware has disable bssid match */ HAL_CAP_STREAMS = 239, /* how many 802.11n spatial streams are available */ HAL_CAP_RXDESC_SELFLINK = 242, /* support a self-linked tail RX descriptor */ HAL_CAP_LONG_RXDESC_TSF = 243, /* hardware supports 32bit TSF in RX descriptor */ HAL_CAP_BB_READ_WAR = 244, /* baseband read WAR */ HAL_CAP_SERIALISE_WAR = 245, /* serialise register access on PCI */ HAL_CAP_ENFORCE_TXOP = 246, /* Enforce TXOP if supported */ HAL_CAP_RX_LNA_MIXING = 247, /* RX hardware uses LNA mixing */ HAL_CAP_DO_MYBEACON = 248, /* Supports HAL_RX_FILTER_MYBEACON */ } HAL_CAPABILITY_TYPE; /* * "States" for setting the LED. These correspond to * the possible 802.11 operational states and there may * be a many-to-one mapping between these states and the * actual hardware state for the LED's (i.e. the hardware * may have fewer states). */ typedef enum { HAL_LED_INIT = 0, HAL_LED_SCAN = 1, HAL_LED_AUTH = 2, HAL_LED_ASSOC = 3, HAL_LED_RUN = 4 } HAL_LED_STATE; /* * Transmit queue types/numbers. These are used to tag * each transmit queue in the hardware and to identify a set * of transmit queues for operations such as start/stop dma. */ typedef enum { HAL_TX_QUEUE_INACTIVE = 0, /* queue is inactive/unused */ HAL_TX_QUEUE_DATA = 1, /* data xmit q's */ HAL_TX_QUEUE_BEACON = 2, /* beacon xmit q */ HAL_TX_QUEUE_CAB = 3, /* "crap after beacon" xmit q */ HAL_TX_QUEUE_UAPSD = 4, /* u-apsd power save xmit q */ HAL_TX_QUEUE_PSPOLL = 5, /* power save poll xmit q */ HAL_TX_QUEUE_CFEND = 6, HAL_TX_QUEUE_PAPRD = 7, } HAL_TX_QUEUE; #define HAL_NUM_TX_QUEUES 10 /* max possible # of queues */ /* * Receive queue types. These are used to tag * each transmit queue in the hardware and to identify a set * of transmit queues for operations such as start/stop dma. */ typedef enum { HAL_RX_QUEUE_HP = 0, /* high priority recv queue */ HAL_RX_QUEUE_LP = 1, /* low priority recv queue */ } HAL_RX_QUEUE; #define HAL_NUM_RX_QUEUES 2 /* max possible # of queues */ #define HAL_TXFIFO_DEPTH 8 /* transmit fifo depth */ /* * Transmit queue subtype. These map directly to * WME Access Categories (except for UPSD). Refer * to Table 5 of the WME spec. */ typedef enum { HAL_WME_AC_BK = 0, /* background access category */ HAL_WME_AC_BE = 1, /* best effort access category*/ HAL_WME_AC_VI = 2, /* video access category */ HAL_WME_AC_VO = 3, /* voice access category */ HAL_WME_UPSD = 4, /* uplink power save */ } HAL_TX_QUEUE_SUBTYPE; /* * Transmit queue flags that control various * operational parameters. */ typedef enum { /* * Per queue interrupt enables. When set the associated * interrupt may be delivered for packets sent through * the queue. Without these enabled no interrupts will * be delivered for transmits through the queue. */ HAL_TXQ_TXOKINT_ENABLE = 0x0001, /* enable TXOK interrupt */ HAL_TXQ_TXERRINT_ENABLE = 0x0001, /* enable TXERR interrupt */ HAL_TXQ_TXDESCINT_ENABLE = 0x0002, /* enable TXDESC interrupt */ HAL_TXQ_TXEOLINT_ENABLE = 0x0004, /* enable TXEOL interrupt */ HAL_TXQ_TXURNINT_ENABLE = 0x0008, /* enable TXURN interrupt */ /* * Enable hardware compression for packets sent through * the queue. The compression buffer must be setup and * packets must have a key entry marked in the tx descriptor. */ HAL_TXQ_COMPRESSION_ENABLE = 0x0010, /* enable h/w compression */ /* * Disable queue when veol is hit or ready time expires. * By default the queue is disabled only on reaching the * physical end of queue (i.e. a null link ptr in the * descriptor chain). */ HAL_TXQ_RDYTIME_EXP_POLICY_ENABLE = 0x0020, /* * Schedule frames on delivery of a DBA (DMA Beacon Alert) * event. Frames will be transmitted only when this timer * fires, e.g to transmit a beacon in ap or adhoc modes. */ HAL_TXQ_DBA_GATED = 0x0040, /* schedule based on DBA */ /* * Each transmit queue has a counter that is incremented * each time the queue is enabled and decremented when * the list of frames to transmit is traversed (or when * the ready time for the queue expires). This counter * must be non-zero for frames to be scheduled for * transmission. The following controls disable bumping * this counter under certain conditions. Typically this * is used to gate frames based on the contents of another * queue (e.g. CAB traffic may only follow a beacon frame). * These are meaningful only when frames are scheduled * with a non-ASAP policy (e.g. DBA-gated). */ HAL_TXQ_CBR_DIS_QEMPTY = 0x0080, /* disable on this q empty */ HAL_TXQ_CBR_DIS_BEMPTY = 0x0100, /* disable on beacon q empty */ /* * Fragment burst backoff policy. Normally the no backoff * is done after a successful transmission, the next fragment * is sent at SIFS. If this flag is set backoff is done * after each fragment, regardless whether it was ack'd or * not, after the backoff count reaches zero a normal channel * access procedure is done before the next transmit (i.e. * wait AIFS instead of SIFS). */ HAL_TXQ_FRAG_BURST_BACKOFF_ENABLE = 0x00800000, /* * Disable post-tx backoff following each frame. */ HAL_TXQ_BACKOFF_DISABLE = 0x00010000, /* disable post backoff */ /* * DCU arbiter lockout control. This controls how * lower priority tx queues are handled with respect to * to a specific queue when multiple queues have frames * to send. No lockout means lower priority queues arbitrate * concurrently with this queue. Intra-frame lockout * means lower priority queues are locked out until the * current frame transmits (e.g. including backoffs and bursting). * Global lockout means nothing lower can arbitrary so * long as there is traffic activity on this queue (frames, * backoff, etc). */ HAL_TXQ_ARB_LOCKOUT_INTRA = 0x00020000, /* intra-frame lockout */ HAL_TXQ_ARB_LOCKOUT_GLOBAL = 0x00040000, /* full lockout s */ HAL_TXQ_IGNORE_VIRTCOL = 0x00080000, /* ignore virt collisions */ HAL_TXQ_SEQNUM_INC_DIS = 0x00100000, /* disable seqnum increment */ } HAL_TX_QUEUE_FLAGS; typedef struct { uint32_t tqi_ver; /* hal TXQ version */ HAL_TX_QUEUE_SUBTYPE tqi_subtype; /* subtype if applicable */ HAL_TX_QUEUE_FLAGS tqi_qflags; /* flags (see above) */ uint32_t tqi_priority; /* (not used) */ uint32_t tqi_aifs; /* aifs */ uint32_t tqi_cwmin; /* cwMin */ uint32_t tqi_cwmax; /* cwMax */ uint16_t tqi_shretry; /* rts retry limit */ uint16_t tqi_lgretry; /* long retry limit (not used)*/ uint32_t tqi_cbrPeriod; /* CBR period (us) */ uint32_t tqi_cbrOverflowLimit; /* threshold for CBROVF int */ uint32_t tqi_burstTime; /* max burst duration (us) */ uint32_t tqi_readyTime; /* frame schedule time (us) */ uint32_t tqi_compBuf; /* comp buffer phys addr */ } HAL_TXQ_INFO; #define HAL_TQI_NONVAL 0xffff /* token to use for aifs, cwmin, cwmax */ #define HAL_TXQ_USEDEFAULT ((uint32_t) -1) /* compression definitions */ #define HAL_COMP_BUF_MAX_SIZE 9216 /* 9K */ #define HAL_COMP_BUF_ALIGN_SIZE 512 /* * Transmit packet types. This belongs in ah_desc.h, but * is here so we can give a proper type to various parameters * (and not require everyone include the file). * * NB: These values are intentionally assigned for * direct use when setting up h/w descriptors. */ typedef enum { HAL_PKT_TYPE_NORMAL = 0, HAL_PKT_TYPE_ATIM = 1, HAL_PKT_TYPE_PSPOLL = 2, HAL_PKT_TYPE_BEACON = 3, HAL_PKT_TYPE_PROBE_RESP = 4, HAL_PKT_TYPE_CHIRP = 5, HAL_PKT_TYPE_GRP_POLL = 6, HAL_PKT_TYPE_AMPDU = 7, } HAL_PKT_TYPE; /* Rx Filter Frame Types */ typedef enum { /* * These bits correspond to AR_RX_FILTER for all chips. * Not all bits are supported by all chips. */ HAL_RX_FILTER_UCAST = 0x00000001, /* Allow unicast frames */ HAL_RX_FILTER_MCAST = 0x00000002, /* Allow multicast frames */ HAL_RX_FILTER_BCAST = 0x00000004, /* Allow broadcast frames */ HAL_RX_FILTER_CONTROL = 0x00000008, /* Allow control frames */ HAL_RX_FILTER_BEACON = 0x00000010, /* Allow beacon frames */ HAL_RX_FILTER_PROM = 0x00000020, /* Promiscuous mode */ HAL_RX_FILTER_PROBEREQ = 0x00000080, /* Allow probe request frames */ HAL_RX_FILTER_PHYERR = 0x00000100, /* Allow phy errors */ HAL_RX_FILTER_MYBEACON = 0x00000200, /* Filter beacons other than mine */ HAL_RX_FILTER_COMPBAR = 0x00000400, /* Allow compressed BAR */ HAL_RX_FILTER_COMP_BA = 0x00000800, /* Allow compressed blockack */ HAL_RX_FILTER_PHYRADAR = 0x00002000, /* Allow phy radar errors */ HAL_RX_FILTER_PSPOLL = 0x00004000, /* Allow PS-POLL frames */ HAL_RX_FILTER_MCAST_BCAST_ALL = 0x00008000, /* Allow all mcast/bcast frames */ /* - * Magic RX filter flags that aren't targetting hardware bits + * Magic RX filter flags that aren't targeting hardware bits * but instead the HAL sets individual bits - eg PHYERR will result * in OFDM/CCK timing error frames being received. */ HAL_RX_FILTER_BSSID = 0x40000000, /* Disable BSSID match */ } HAL_RX_FILTER; typedef enum { HAL_PM_AWAKE = 0, HAL_PM_FULL_SLEEP = 1, HAL_PM_NETWORK_SLEEP = 2, HAL_PM_UNDEFINED = 3 } HAL_POWER_MODE; /* * Enterprise mode flags */ #define AH_ENT_DUAL_BAND_DISABLE 0x00000001 #define AH_ENT_CHAIN2_DISABLE 0x00000002 #define AH_ENT_5MHZ_DISABLE 0x00000004 #define AH_ENT_10MHZ_DISABLE 0x00000008 #define AH_ENT_49GHZ_DISABLE 0x00000010 #define AH_ENT_LOOPBACK_DISABLE 0x00000020 #define AH_ENT_TPC_PERF_DISABLE 0x00000040 #define AH_ENT_MIN_PKT_SIZE_DISABLE 0x00000080 #define AH_ENT_SPECTRAL_PRECISION 0x00000300 #define AH_ENT_SPECTRAL_PRECISION_S 8 #define AH_ENT_RTSCTS_DELIM_WAR 0x00010000 #define AH_FIRST_DESC_NDELIMS 60 /* * NOTE WELL: * These are mapped to take advantage of the common locations for many of * the bits on all of the currently supported MAC chips. This is to make * the ISR as efficient as possible, while still abstracting HW differences. * When new hardware breaks this commonality this enumerated type, as well * as the HAL functions using it, must be modified. All values are directly * mapped unless commented otherwise. */ typedef enum { HAL_INT_RX = 0x00000001, /* Non-common mapping */ HAL_INT_RXDESC = 0x00000002, /* Legacy mapping */ HAL_INT_RXERR = 0x00000004, HAL_INT_RXHP = 0x00000001, /* EDMA */ HAL_INT_RXLP = 0x00000002, /* EDMA */ HAL_INT_RXNOFRM = 0x00000008, HAL_INT_RXEOL = 0x00000010, HAL_INT_RXORN = 0x00000020, HAL_INT_TX = 0x00000040, /* Non-common mapping */ HAL_INT_TXDESC = 0x00000080, HAL_INT_TIM_TIMER= 0x00000100, HAL_INT_MCI = 0x00000200, HAL_INT_BBPANIC = 0x00000400, HAL_INT_TXURN = 0x00000800, HAL_INT_MIB = 0x00001000, HAL_INT_RXPHY = 0x00004000, HAL_INT_RXKCM = 0x00008000, HAL_INT_SWBA = 0x00010000, HAL_INT_BRSSI = 0x00020000, HAL_INT_BMISS = 0x00040000, HAL_INT_BNR = 0x00100000, HAL_INT_TIM = 0x00200000, /* Non-common mapping */ HAL_INT_DTIM = 0x00400000, /* Non-common mapping */ HAL_INT_DTIMSYNC= 0x00800000, /* Non-common mapping */ HAL_INT_GPIO = 0x01000000, HAL_INT_CABEND = 0x02000000, /* Non-common mapping */ HAL_INT_TSFOOR = 0x04000000, /* Non-common mapping */ HAL_INT_TBTT = 0x08000000, /* Non-common mapping */ /* Atheros ref driver has a generic timer interrupt now..*/ HAL_INT_GENTIMER = 0x08000000, /* Non-common mapping */ HAL_INT_CST = 0x10000000, /* Non-common mapping */ HAL_INT_GTT = 0x20000000, /* Non-common mapping */ HAL_INT_FATAL = 0x40000000, /* Non-common mapping */ #define HAL_INT_GLOBAL 0x80000000 /* Set/clear IER */ HAL_INT_BMISC = HAL_INT_TIM | HAL_INT_DTIM | HAL_INT_DTIMSYNC | HAL_INT_CABEND | HAL_INT_TBTT, /* Interrupt bits that map directly to ISR/IMR bits */ HAL_INT_COMMON = HAL_INT_RXNOFRM | HAL_INT_RXDESC | HAL_INT_RXEOL | HAL_INT_RXORN | HAL_INT_TXDESC | HAL_INT_TXURN | HAL_INT_MIB | HAL_INT_RXPHY | HAL_INT_RXKCM | HAL_INT_SWBA | HAL_INT_BMISS | HAL_INT_BRSSI | HAL_INT_BNR | HAL_INT_GPIO, } HAL_INT; /* * MSI vector assignments */ typedef enum { HAL_MSIVEC_MISC = 0, HAL_MSIVEC_TX = 1, HAL_MSIVEC_RXLP = 2, HAL_MSIVEC_RXHP = 3, } HAL_MSIVEC; typedef enum { HAL_INT_LINE = 0, HAL_INT_MSI = 1, } HAL_INT_TYPE; /* For interrupt mitigation registers */ typedef enum { HAL_INT_RX_FIRSTPKT=0, HAL_INT_RX_LASTPKT, HAL_INT_TX_FIRSTPKT, HAL_INT_TX_LASTPKT, HAL_INT_THRESHOLD } HAL_INT_MITIGATION; /* XXX this is duplicate information! */ typedef struct { u_int32_t cyclecnt_diff; /* delta cycle count */ u_int32_t rxclr_cnt; /* rx clear count */ u_int32_t extrxclr_cnt; /* ext chan rx clear count */ u_int32_t txframecnt_diff; /* delta tx frame count */ u_int32_t rxframecnt_diff; /* delta rx frame count */ u_int32_t listen_time; /* listen time in msec - time for which ch is free */ u_int32_t ofdmphyerr_cnt; /* OFDM err count since last reset */ u_int32_t cckphyerr_cnt; /* CCK err count since last reset */ u_int32_t ofdmphyerrcnt_diff; /* delta OFDM Phy Error Count */ HAL_BOOL valid; /* if the stats are valid*/ } HAL_ANISTATS; typedef struct { u_int8_t txctl_offset; u_int8_t txctl_numwords; u_int8_t txstatus_offset; u_int8_t txstatus_numwords; u_int8_t rxctl_offset; u_int8_t rxctl_numwords; u_int8_t rxstatus_offset; u_int8_t rxstatus_numwords; u_int8_t macRevision; } HAL_DESC_INFO; typedef enum { HAL_GPIO_OUTPUT_MUX_AS_OUTPUT = 0, HAL_GPIO_OUTPUT_MUX_PCIE_ATTENTION_LED = 1, HAL_GPIO_OUTPUT_MUX_PCIE_POWER_LED = 2, HAL_GPIO_OUTPUT_MUX_MAC_NETWORK_LED = 3, HAL_GPIO_OUTPUT_MUX_MAC_POWER_LED = 4, HAL_GPIO_OUTPUT_MUX_AS_WLAN_ACTIVE = 5, HAL_GPIO_OUTPUT_MUX_AS_TX_FRAME = 6, HAL_GPIO_OUTPUT_MUX_AS_MCI_WLAN_DATA, HAL_GPIO_OUTPUT_MUX_AS_MCI_WLAN_CLK, HAL_GPIO_OUTPUT_MUX_AS_MCI_BT_DATA, HAL_GPIO_OUTPUT_MUX_AS_MCI_BT_CLK, HAL_GPIO_OUTPUT_MUX_AS_WL_IN_TX, HAL_GPIO_OUTPUT_MUX_AS_WL_IN_RX, HAL_GPIO_OUTPUT_MUX_AS_BT_IN_TX, HAL_GPIO_OUTPUT_MUX_AS_BT_IN_RX, HAL_GPIO_OUTPUT_MUX_AS_RUCKUS_STROBE, HAL_GPIO_OUTPUT_MUX_AS_RUCKUS_DATA, HAL_GPIO_OUTPUT_MUX_AS_SMARTANT_CTRL0, HAL_GPIO_OUTPUT_MUX_AS_SMARTANT_CTRL1, HAL_GPIO_OUTPUT_MUX_AS_SMARTANT_CTRL2, HAL_GPIO_OUTPUT_MUX_NUM_ENTRIES } HAL_GPIO_MUX_TYPE; typedef enum { HAL_GPIO_INTR_LOW = 0, HAL_GPIO_INTR_HIGH = 1, HAL_GPIO_INTR_DISABLE = 2 } HAL_GPIO_INTR_TYPE; typedef struct halCounters { u_int32_t tx_frame_count; u_int32_t rx_frame_count; u_int32_t rx_clear_count; u_int32_t cycle_count; u_int8_t is_rx_active; // true (1) or false (0) u_int8_t is_tx_active; // true (1) or false (0) } HAL_COUNTERS; typedef enum { HAL_RFGAIN_INACTIVE = 0, HAL_RFGAIN_READ_REQUESTED = 1, HAL_RFGAIN_NEED_CHANGE = 2 } HAL_RFGAIN; typedef uint16_t HAL_CTRY_CODE; /* country code */ typedef uint16_t HAL_REG_DOMAIN; /* regulatory domain code */ #define HAL_ANTENNA_MIN_MODE 0 #define HAL_ANTENNA_FIXED_A 1 #define HAL_ANTENNA_FIXED_B 2 #define HAL_ANTENNA_MAX_MODE 3 typedef struct { uint32_t ackrcv_bad; uint32_t rts_bad; uint32_t rts_good; uint32_t fcs_bad; uint32_t beacons; } HAL_MIB_STATS; /* * These bits represent what's in ah_currentRDext. */ typedef enum { REG_EXT_FCC_MIDBAND = 0, REG_EXT_JAPAN_MIDBAND = 1, REG_EXT_FCC_DFS_HT40 = 2, REG_EXT_JAPAN_NONDFS_HT40 = 3, REG_EXT_JAPAN_DFS_HT40 = 4 } REG_EXT_BITMAP; enum { HAL_MODE_11A = 0x001, /* 11a channels */ HAL_MODE_TURBO = 0x002, /* 11a turbo-only channels */ HAL_MODE_11B = 0x004, /* 11b channels */ HAL_MODE_PUREG = 0x008, /* 11g channels (OFDM only) */ #ifdef notdef HAL_MODE_11G = 0x010, /* 11g channels (OFDM/CCK) */ #else HAL_MODE_11G = 0x008, /* XXX historical */ #endif HAL_MODE_108G = 0x020, /* 11g+Turbo channels */ HAL_MODE_108A = 0x040, /* 11a+Turbo channels */ HAL_MODE_11A_HALF_RATE = 0x200, /* 11a half width channels */ HAL_MODE_11A_QUARTER_RATE = 0x400, /* 11a quarter width channels */ HAL_MODE_11G_HALF_RATE = 0x800, /* 11g half width channels */ HAL_MODE_11G_QUARTER_RATE = 0x1000, /* 11g quarter width channels */ HAL_MODE_11NG_HT20 = 0x008000, HAL_MODE_11NA_HT20 = 0x010000, HAL_MODE_11NG_HT40PLUS = 0x020000, HAL_MODE_11NG_HT40MINUS = 0x040000, HAL_MODE_11NA_HT40PLUS = 0x080000, HAL_MODE_11NA_HT40MINUS = 0x100000, HAL_MODE_ALL = 0xffffff }; typedef struct { int rateCount; /* NB: for proper padding */ uint8_t rateCodeToIndex[256]; /* back mapping */ struct { uint8_t valid; /* valid for rate control use */ uint8_t phy; /* CCK/OFDM/XR */ uint32_t rateKbps; /* transfer rate in kbs */ uint8_t rateCode; /* rate for h/w descriptors */ uint8_t shortPreamble; /* mask for enabling short * preamble in CCK rate code */ uint8_t dot11Rate; /* value for supported rates * info element of MLME */ uint8_t controlRate; /* index of next lower basic * rate; used for dur. calcs */ uint16_t lpAckDuration; /* long preamble ACK duration */ uint16_t spAckDuration; /* short preamble ACK duration*/ } info[64]; } HAL_RATE_TABLE; typedef struct { u_int rs_count; /* number of valid entries */ uint8_t rs_rates[64]; /* rates */ } HAL_RATE_SET; /* * 802.11n specific structures and enums */ typedef enum { HAL_CHAINTYPE_TX = 1, /* Tx chain type */ HAL_CHAINTYPE_RX = 2, /* RX chain type */ } HAL_CHAIN_TYPE; typedef struct { u_int Tries; u_int Rate; /* hardware rate code */ u_int RateIndex; /* rate series table index */ u_int PktDuration; u_int ChSel; u_int RateFlags; #define HAL_RATESERIES_RTS_CTS 0x0001 /* use rts/cts w/this series */ #define HAL_RATESERIES_2040 0x0002 /* use ext channel for series */ #define HAL_RATESERIES_HALFGI 0x0004 /* use half-gi for series */ #define HAL_RATESERIES_STBC 0x0008 /* use STBC for series */ u_int tx_power_cap; /* in 1/2 dBm units XXX TODO */ } HAL_11N_RATE_SERIES; typedef enum { HAL_HT_MACMODE_20 = 0, /* 20 MHz operation */ HAL_HT_MACMODE_2040 = 1, /* 20/40 MHz operation */ } HAL_HT_MACMODE; typedef enum { HAL_HT_PHYMODE_20 = 0, /* 20 MHz operation */ HAL_HT_PHYMODE_2040 = 1, /* 20/40 MHz operation */ } HAL_HT_PHYMODE; typedef enum { HAL_HT_EXTPROTSPACING_20 = 0, /* 20 MHz spacing */ HAL_HT_EXTPROTSPACING_25 = 1, /* 25 MHz spacing */ } HAL_HT_EXTPROTSPACING; typedef enum { HAL_RX_CLEAR_CTL_LOW = 0x1, /* force control channel to appear busy */ HAL_RX_CLEAR_EXT_LOW = 0x2, /* force extension channel to appear busy */ } HAL_HT_RXCLEAR; typedef enum { HAL_FREQ_BAND_5GHZ = 0, HAL_FREQ_BAND_2GHZ = 1, } HAL_FREQ_BAND; /* * Antenna switch control. By default antenna selection * enables multiple (2) antenna use. To force use of the * A or B antenna only specify a fixed setting. Fixing * the antenna will also disable any diversity support. */ typedef enum { HAL_ANT_VARIABLE = 0, /* variable by programming */ HAL_ANT_FIXED_A = 1, /* fixed antenna A */ HAL_ANT_FIXED_B = 2, /* fixed antenna B */ } HAL_ANT_SETTING; typedef enum { HAL_M_STA = 1, /* infrastructure station */ HAL_M_IBSS = 0, /* IBSS (adhoc) station */ HAL_M_HOSTAP = 6, /* Software Access Point */ HAL_M_MONITOR = 8 /* Monitor mode */ } HAL_OPMODE; typedef enum { HAL_RESET_NORMAL = 0, /* Do normal reset */ HAL_RESET_BBPANIC = 1, /* Reset because of BB panic */ HAL_RESET_FORCE_COLD = 2, /* Force full reset */ } HAL_RESET_TYPE; typedef struct { uint8_t kv_type; /* one of HAL_CIPHER */ uint8_t kv_apsd; /* Mask for APSD enabled ACs */ uint16_t kv_len; /* length in bits */ uint8_t kv_val[16]; /* enough for 128-bit keys */ uint8_t kv_mic[8]; /* TKIP MIC key */ uint8_t kv_txmic[8]; /* TKIP TX MIC key (optional) */ } HAL_KEYVAL; /* * This is the TX descriptor field which marks the key padding requirement. * The naming is unfortunately unclear. */ #define AH_KEYTYPE_MASK 0x0F typedef enum { HAL_KEY_TYPE_CLEAR, HAL_KEY_TYPE_WEP, HAL_KEY_TYPE_AES, HAL_KEY_TYPE_TKIP, } HAL_KEY_TYPE; typedef enum { HAL_CIPHER_WEP = 0, HAL_CIPHER_AES_OCB = 1, HAL_CIPHER_AES_CCM = 2, HAL_CIPHER_CKIP = 3, HAL_CIPHER_TKIP = 4, HAL_CIPHER_CLR = 5, /* no encryption */ HAL_CIPHER_MIC = 127 /* TKIP-MIC, not a cipher */ } HAL_CIPHER; enum { HAL_SLOT_TIME_6 = 6, /* NB: for turbo mode */ HAL_SLOT_TIME_9 = 9, HAL_SLOT_TIME_20 = 20, }; /* * Per-station beacon timer state. Note that the specified * beacon interval (given in TU's) can also include flags * to force a TSF reset and to enable the beacon xmit logic. * If bs_cfpmaxduration is non-zero the hardware is setup to * coexist with a PCF-capable AP. */ typedef struct { uint32_t bs_nexttbtt; /* next beacon in TU */ uint32_t bs_nextdtim; /* next DTIM in TU */ uint32_t bs_intval; /* beacon interval+flags */ /* * HAL_BEACON_PERIOD, HAL_BEACON_ENA and HAL_BEACON_RESET_TSF * are all 1:1 correspondances with the pre-11n chip AR_BEACON * register. */ #define HAL_BEACON_PERIOD 0x0000ffff /* beacon interval period */ #define HAL_BEACON_PERIOD_TU8 0x0007ffff /* beacon interval, tu/8 */ #define HAL_BEACON_ENA 0x00800000 /* beacon xmit enable */ #define HAL_BEACON_RESET_TSF 0x01000000 /* clear TSF */ #define HAL_TSFOOR_THRESHOLD 0x00004240 /* TSF OOR thresh (16k uS) */ uint32_t bs_dtimperiod; uint16_t bs_cfpperiod; /* CFP period in TU */ uint16_t bs_cfpmaxduration; /* max CFP duration in TU */ uint32_t bs_cfpnext; /* next CFP in TU */ uint16_t bs_timoffset; /* byte offset to TIM bitmap */ uint16_t bs_bmissthreshold; /* beacon miss threshold */ uint32_t bs_sleepduration; /* max sleep duration */ uint32_t bs_tsfoor_threshold; /* TSF out of range threshold */ } HAL_BEACON_STATE; /* * Like HAL_BEACON_STATE but for non-station mode setup. * NB: see above flag definitions for bt_intval. */ typedef struct { uint32_t bt_intval; /* beacon interval+flags */ uint32_t bt_nexttbtt; /* next beacon in TU */ uint32_t bt_nextatim; /* next ATIM in TU */ uint32_t bt_nextdba; /* next DBA in 1/8th TU */ uint32_t bt_nextswba; /* next SWBA in 1/8th TU */ uint32_t bt_flags; /* timer enables */ #define HAL_BEACON_TBTT_EN 0x00000001 #define HAL_BEACON_DBA_EN 0x00000002 #define HAL_BEACON_SWBA_EN 0x00000004 } HAL_BEACON_TIMERS; /* * Per-node statistics maintained by the driver for use in * optimizing signal quality and other operational aspects. */ typedef struct { uint32_t ns_avgbrssi; /* average beacon rssi */ uint32_t ns_avgrssi; /* average data rssi */ uint32_t ns_avgtxrssi; /* average tx rssi */ } HAL_NODE_STATS; #define HAL_RSSI_EP_MULTIPLIER (1<<7) /* pow2 to optimize out * and / */ /* * This is the ANI state and MIB stats. * * It's used by the HAL modules to keep state /and/ by the debug ioctl * to fetch ANI information. */ typedef struct { uint32_t ast_ani_niup; /* ANI increased noise immunity */ uint32_t ast_ani_nidown; /* ANI decreased noise immunity */ uint32_t ast_ani_spurup; /* ANI increased spur immunity */ uint32_t ast_ani_spurdown;/* ANI descreased spur immunity */ uint32_t ast_ani_ofdmon; /* ANI OFDM weak signal detect on */ uint32_t ast_ani_ofdmoff;/* ANI OFDM weak signal detect off */ uint32_t ast_ani_cckhigh;/* ANI CCK weak signal threshold high */ uint32_t ast_ani_ccklow; /* ANI CCK weak signal threshold low */ uint32_t ast_ani_stepup; /* ANI increased first step level */ uint32_t ast_ani_stepdown;/* ANI decreased first step level */ uint32_t ast_ani_ofdmerrs;/* ANI cumulative ofdm phy err count */ uint32_t ast_ani_cckerrs;/* ANI cumulative cck phy err count */ uint32_t ast_ani_reset; /* ANI parameters zero'd for non-STA */ uint32_t ast_ani_lzero; /* ANI listen time forced to zero */ uint32_t ast_ani_lneg; /* ANI listen time calculated < 0 */ HAL_MIB_STATS ast_mibstats; /* MIB counter stats */ HAL_NODE_STATS ast_nodestats; /* Latest rssi stats from driver */ } HAL_ANI_STATS; typedef struct { uint8_t noiseImmunityLevel; uint8_t spurImmunityLevel; uint8_t firstepLevel; uint8_t ofdmWeakSigDetectOff; uint8_t cckWeakSigThreshold; uint32_t listenTime; /* NB: intentionally ordered so data exported to user space is first */ uint32_t txFrameCount; /* Last txFrameCount */ uint32_t rxFrameCount; /* Last rx Frame count */ uint32_t cycleCount; /* Last cycleCount (to detect wrap-around) */ uint32_t ofdmPhyErrCount;/* OFDM err count since last reset */ uint32_t cckPhyErrCount; /* CCK err count since last reset */ } HAL_ANI_STATE; struct ath_desc; struct ath_tx_status; struct ath_rx_status; struct ieee80211_channel; /* * This is a channel survey sample entry. * * The AR5212 ANI routines fill these samples. The ANI code then uses it * when calculating listen time; it is also exported via a diagnostic * API. */ typedef struct { uint32_t seq_num; uint32_t tx_busy; uint32_t rx_busy; uint32_t chan_busy; uint32_t ext_chan_busy; uint32_t cycle_count; /* XXX TODO */ uint32_t ofdm_phyerr_count; uint32_t cck_phyerr_count; } HAL_SURVEY_SAMPLE; /* * This provides 3.2 seconds of sample space given an * ANI time of 1/10th of a second. This may not be enough! */ #define CHANNEL_SURVEY_SAMPLE_COUNT 32 typedef struct { HAL_SURVEY_SAMPLE samples[CHANNEL_SURVEY_SAMPLE_COUNT]; uint32_t cur_sample; /* current sample in sequence */ uint32_t cur_seq; /* current sequence number */ } HAL_CHANNEL_SURVEY; /* * ANI commands. * * These are used both internally and externally via the diagnostic * API. * * Note that this is NOT the ANI commands being used via the INTMIT * capability - that has a different mapping for some reason. */ typedef enum { HAL_ANI_PRESENT = 0, /* is ANI support present */ HAL_ANI_NOISE_IMMUNITY_LEVEL = 1, /* set level */ HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION = 2, /* enable/disable */ HAL_ANI_CCK_WEAK_SIGNAL_THR = 3, /* enable/disable */ HAL_ANI_FIRSTEP_LEVEL = 4, /* set level */ HAL_ANI_SPUR_IMMUNITY_LEVEL = 5, /* set level */ HAL_ANI_MODE = 6, /* 0 => manual, 1 => auto (XXX do not change) */ HAL_ANI_PHYERR_RESET = 7, /* reset phy error stats */ HAL_ANI_MRC_CCK = 8, } HAL_ANI_CMD; #define HAL_ANI_ALL 0xffffffff /* * This is the layout of the ANI INTMIT capability. * * Notice that the command values differ to HAL_ANI_CMD. */ typedef enum { HAL_CAP_INTMIT_PRESENT = 0, HAL_CAP_INTMIT_ENABLE = 1, HAL_CAP_INTMIT_NOISE_IMMUNITY_LEVEL = 2, HAL_CAP_INTMIT_OFDM_WEAK_SIGNAL_LEVEL = 3, HAL_CAP_INTMIT_CCK_WEAK_SIGNAL_THR = 4, HAL_CAP_INTMIT_FIRSTEP_LEVEL = 5, HAL_CAP_INTMIT_SPUR_IMMUNITY_LEVEL = 6 } HAL_CAP_INTMIT_CMD; typedef struct { int32_t pe_firpwr; /* FIR pwr out threshold */ int32_t pe_rrssi; /* Radar rssi thresh */ int32_t pe_height; /* Pulse height thresh */ int32_t pe_prssi; /* Pulse rssi thresh */ int32_t pe_inband; /* Inband thresh */ /* The following params are only for AR5413 and later */ u_int32_t pe_relpwr; /* Relative power threshold in 0.5dB steps */ u_int32_t pe_relstep; /* Pulse Relative step threshold in 0.5dB steps */ u_int32_t pe_maxlen; /* Max length of radar sign in 0.8us units */ int32_t pe_usefir128; /* Use the average in-band power measured over 128 cycles */ int32_t pe_blockradar; /* * Enable to block radar check if pkt detect is done via OFDM * weak signal detect or pkt is detected immediately after tx * to rx transition */ int32_t pe_enmaxrssi; /* * Enable to use the max rssi instead of the last rssi during * fine gain changes for radar detection */ int32_t pe_extchannel; /* Enable DFS on ext channel */ int32_t pe_enabled; /* Whether radar detection is enabled */ int32_t pe_enrelpwr; int32_t pe_en_relstep_check; } HAL_PHYERR_PARAM; #define HAL_PHYERR_PARAM_NOVAL 65535 typedef struct { u_int16_t ss_fft_period; /* Skip interval for FFT reports */ u_int16_t ss_period; /* Spectral scan period */ u_int16_t ss_count; /* # of reports to return from ss_active */ u_int16_t ss_short_report;/* Set to report ony 1 set of FFT results */ u_int8_t radar_bin_thresh_sel; /* strong signal radar FFT threshold configuration */ u_int16_t ss_spectral_pri; /* are we doing a noise power cal ? */ int8_t ss_nf_cal[AH_MAX_CHAINS*2]; /* nf calibrated values for ctl+ext from eeprom */ int8_t ss_nf_pwr[AH_MAX_CHAINS*2]; /* nf pwr values for ctl+ext from eeprom */ int32_t ss_nf_temp_data; /* temperature data taken during nf scan */ int ss_enabled; int ss_active; } HAL_SPECTRAL_PARAM; #define HAL_SPECTRAL_PARAM_NOVAL 0xFFFF #define HAL_SPECTRAL_PARAM_ENABLE 0x8000 /* Enable/Disable if applicable */ /* * DFS operating mode flags. */ typedef enum { HAL_DFS_UNINIT_DOMAIN = 0, /* Uninitialized dfs domain */ HAL_DFS_FCC_DOMAIN = 1, /* FCC3 dfs domain */ HAL_DFS_ETSI_DOMAIN = 2, /* ETSI dfs domain */ HAL_DFS_MKK4_DOMAIN = 3, /* Japan dfs domain */ } HAL_DFS_DOMAIN; /* * MFP decryption options for initializing the MAC. */ typedef enum { HAL_MFP_QOSDATA = 0, /* Decrypt MFP frames like QoS data frames. All chips before Merlin. */ HAL_MFP_PASSTHRU, /* Don't decrypt MFP frames at all. Passthrough */ HAL_MFP_HW_CRYPTO /* hardware decryption enabled. Merlin can do it. */ } HAL_MFP_OPT_T; /* LNA config supported */ typedef enum { HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2 = 0, HAL_ANT_DIV_COMB_LNA2 = 1, HAL_ANT_DIV_COMB_LNA1 = 2, HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2 = 3, } HAL_ANT_DIV_COMB_LNA_CONF; typedef struct { u_int8_t main_lna_conf; u_int8_t alt_lna_conf; u_int8_t fast_div_bias; u_int8_t main_gaintb; u_int8_t alt_gaintb; u_int8_t antdiv_configgroup; int8_t lna1_lna2_delta; } HAL_ANT_COMB_CONFIG; #define DEFAULT_ANTDIV_CONFIG_GROUP 0x00 #define HAL_ANTDIV_CONFIG_GROUP_1 0x01 #define HAL_ANTDIV_CONFIG_GROUP_2 0x02 #define HAL_ANTDIV_CONFIG_GROUP_3 0x03 /* * Flag for setting QUIET period */ typedef enum { HAL_QUIET_DISABLE = 0x0, HAL_QUIET_ENABLE = 0x1, HAL_QUIET_ADD_CURRENT_TSF = 0x2, /* add current TSF to next_start offset */ HAL_QUIET_ADD_SWBA_RESP_TIME = 0x4, /* add beacon response time to next_start offset */ } HAL_QUIET_FLAG; #define HAL_DFS_EVENT_PRICH 0x0000001 #define HAL_DFS_EVENT_EXTCH 0x0000002 #define HAL_DFS_EVENT_EXTEARLY 0x0000004 #define HAL_DFS_EVENT_ISDC 0x0000008 struct hal_dfs_event { uint64_t re_full_ts; /* 64-bit full timestamp from interrupt time */ uint32_t re_ts; /* Original 15 bit recv timestamp */ uint8_t re_rssi; /* rssi of radar event */ uint8_t re_dur; /* duration of radar pulse */ uint32_t re_flags; /* Flags (see above) */ }; typedef struct hal_dfs_event HAL_DFS_EVENT; /* * Generic Timer domain */ typedef enum { HAL_GEN_TIMER_TSF = 0, HAL_GEN_TIMER_TSF2, HAL_GEN_TIMER_TSF_ANY } HAL_GEN_TIMER_DOMAIN; /* * BT Co-existence definitions */ typedef enum { HAL_BT_MODULE_CSR_BC4 = 0, /* CSR BlueCore v4 */ HAL_BT_MODULE_JANUS = 1, /* Kite + Valkyrie combo */ HAL_BT_MODULE_HELIUS = 2, /* Kiwi + Valkyrie combo */ HAL_MAX_BT_MODULES } HAL_BT_MODULE; typedef struct { HAL_BT_MODULE bt_module; u_int8_t bt_coex_config; u_int8_t bt_gpio_bt_active; u_int8_t bt_gpio_bt_priority; u_int8_t bt_gpio_wlan_active; u_int8_t bt_active_polarity; HAL_BOOL bt_single_ant; u_int8_t bt_dutyCycle; u_int8_t bt_isolation; u_int8_t bt_period; } HAL_BT_COEX_INFO; typedef enum { HAL_BT_COEX_MODE_LEGACY = 0, /* legacy rx_clear mode */ HAL_BT_COEX_MODE_UNSLOTTED = 1, /* untimed/unslotted mode */ HAL_BT_COEX_MODE_SLOTTED = 2, /* slotted mode */ HAL_BT_COEX_MODE_DISALBED = 3, /* coexistence disabled */ } HAL_BT_COEX_MODE; typedef enum { HAL_BT_COEX_CFG_NONE, /* No bt coex enabled */ HAL_BT_COEX_CFG_2WIRE_2CH, /* 2-wire with 2 chains */ HAL_BT_COEX_CFG_2WIRE_CH1, /* 2-wire with ch1 */ HAL_BT_COEX_CFG_2WIRE_CH0, /* 2-wire with ch0 */ HAL_BT_COEX_CFG_3WIRE, /* 3-wire */ HAL_BT_COEX_CFG_MCI /* MCI */ } HAL_BT_COEX_CFG; typedef enum { HAL_BT_COEX_SET_ACK_PWR = 0, /* Change ACK power setting */ HAL_BT_COEX_LOWER_TX_PWR, /* Change transmit power */ HAL_BT_COEX_ANTENNA_DIVERSITY, /* Enable RX diversity for Kite */ HAL_BT_COEX_MCI_MAX_TX_PWR, /* Set max tx power for concurrent tx */ HAL_BT_COEX_MCI_FTP_STOMP_RX, /* Use a different weight for stomp low */ } HAL_BT_COEX_SET_PARAMETER; #define HAL_BT_COEX_FLAG_LOW_ACK_PWR 0x00000001 #define HAL_BT_COEX_FLAG_LOWER_TX_PWR 0x00000002 /* Check Rx Diversity is allowed */ #define HAL_BT_COEX_FLAG_ANT_DIV_ALLOW 0x00000004 /* Check Diversity is on or off */ #define HAL_BT_COEX_FLAG_ANT_DIV_ENABLE 0x00000008 #define HAL_BT_COEX_ANTDIV_CONTROL1_ENABLE 0x0b /* main: LNA1, alt: LNA2 */ #define HAL_BT_COEX_ANTDIV_CONTROL2_ENABLE 0x09 #define HAL_BT_COEX_ANTDIV_CONTROL1_FIXED_A 0x04 #define HAL_BT_COEX_ANTDIV_CONTROL2_FIXED_A 0x09 #define HAL_BT_COEX_ANTDIV_CONTROL1_FIXED_B 0x02 #define HAL_BT_COEX_ANTDIV_CONTROL2_FIXED_B 0x06 #define HAL_BT_COEX_ISOLATION_FOR_NO_COEX 30 #define HAL_BT_COEX_ANT_DIV_SWITCH_COM 0x66666666 #define HAL_BT_COEX_HELIUS_CHAINMASK 0x02 #define HAL_BT_COEX_LOW_ACK_POWER 0x0 #define HAL_BT_COEX_HIGH_ACK_POWER 0x3f3f3f typedef enum { HAL_BT_COEX_NO_STOMP = 0, HAL_BT_COEX_STOMP_ALL, HAL_BT_COEX_STOMP_LOW, HAL_BT_COEX_STOMP_NONE, HAL_BT_COEX_STOMP_ALL_FORCE, HAL_BT_COEX_STOMP_LOW_FORCE, } HAL_BT_COEX_STOMP_TYPE; typedef struct { /* extend rx_clear after tx/rx to protect the burst (in usec). */ u_int8_t bt_time_extend; /* * extend rx_clear as long as txsm is * transmitting or waiting for ack. */ HAL_BOOL bt_txstate_extend; /* * extend rx_clear so that when tx_frame * is asserted, rx_clear will drop. */ HAL_BOOL bt_txframe_extend; /* * coexistence mode */ HAL_BT_COEX_MODE bt_mode; /* * treat BT high priority traffic as * a quiet collision */ HAL_BOOL bt_quiet_collision; /* * invert rx_clear as WLAN_ACTIVE */ HAL_BOOL bt_rxclear_polarity; /* * slotted mode only. indicate the time in usec * from the rising edge of BT_ACTIVE to the time * BT_PRIORITY can be sampled to indicate priority. */ u_int8_t bt_priority_time; /* * slotted mode only. indicate the time in usec * from the rising edge of BT_ACTIVE to the time * BT_PRIORITY can be sampled to indicate tx/rx and * BT_FREQ is sampled. */ u_int8_t bt_first_slot_time; /* * slotted mode only. rx_clear and bt_ant decision * will be held the entire time that BT_ACTIVE is asserted, - * otherwise the decision is made before every slot boundry. + * otherwise the decision is made before every slot boundary. */ HAL_BOOL bt_hold_rxclear; } HAL_BT_COEX_CONFIG; struct hal_bb_panic_info { u_int32_t status; u_int32_t tsf; u_int32_t phy_panic_wd_ctl1; u_int32_t phy_panic_wd_ctl2; u_int32_t phy_gen_ctrl; u_int32_t rxc_pcnt; u_int32_t rxf_pcnt; u_int32_t txf_pcnt; u_int32_t cycles; u_int32_t wd; u_int32_t det; u_int32_t rdar; u_int32_t r_odfm; u_int32_t r_cck; u_int32_t t_odfm; u_int32_t t_cck; u_int32_t agc; u_int32_t src; }; /* Serialize Register Access Mode */ typedef enum { SER_REG_MODE_OFF = 0, SER_REG_MODE_ON = 1, SER_REG_MODE_AUTO = 2, } SER_REG_MODE; typedef struct { int ah_debug; /* only used if AH_DEBUG is defined */ int ah_ar5416_biasadj; /* enable AR2133 radio specific bias fiddling */ /* NB: these are deprecated; they exist for now for compatibility */ int ah_dma_beacon_response_time;/* in TU's */ int ah_sw_beacon_response_time; /* in TU's */ int ah_additional_swba_backoff; /* in TU's */ int ah_force_full_reset; /* force full chip reset rather then warm reset */ int ah_serialise_reg_war; /* force serialisation of register IO */ /* XXX these don't belong here, they're just for the ar9300 HAL port effort */ int ath_hal_desc_tpc; /* Per-packet TPC */ int ath_hal_sta_update_tx_pwr_enable; /* GreenTX */ int ath_hal_sta_update_tx_pwr_enable_S1; /* GreenTX */ int ath_hal_sta_update_tx_pwr_enable_S2; /* GreenTX */ int ath_hal_sta_update_tx_pwr_enable_S3; /* GreenTX */ /* I'm not sure what the default values for these should be */ int ath_hal_pll_pwr_save; int ath_hal_pcie_power_save_enable; int ath_hal_intr_mitigation_rx; int ath_hal_intr_mitigation_tx; int ath_hal_pcie_clock_req; #define AR_PCIE_PLL_PWRSAVE_CONTROL (1<<0) #define AR_PCIE_PLL_PWRSAVE_ON_D3 (1<<1) #define AR_PCIE_PLL_PWRSAVE_ON_D0 (1<<2) int ath_hal_pcie_waen; int ath_hal_pcie_ser_des_write; /* these are important for correct AR9300 behaviour */ int ath_hal_ht_enable; /* needs to be enabled for AR9300 HT */ int ath_hal_diversity_control; int ath_hal_antenna_switch_swap; int ath_hal_ext_lna_ctl_gpio; int ath_hal_spur_mode; int ath_hal_6mb_ack; /* should set this to 1 for 11a/11na? */ int ath_hal_enable_msi; /* enable MSI interrupts (needed?) */ int ath_hal_beacon_filter_interval; /* ok to be 0 for now? */ /* For now, set this to 0 - net80211 needs to know about hardware MFP support */ int ath_hal_mfp_support; int ath_hal_enable_ani; /* should set this.. */ int ath_hal_cwm_ignore_ext_cca; int ath_hal_show_bb_panic; int ath_hal_ant_ctrl_comm2g_switch_enable; int ath_hal_ext_atten_margin_cfg; int ath_hal_min_gainidx; int ath_hal_war70c; uint32_t ath_hal_mci_config; } HAL_OPS_CONFIG; /* * Hardware Access Layer (HAL) API. * * Clients of the HAL call ath_hal_attach to obtain a reference to an * ath_hal structure for use with the device. Hardware-related operations * that follow must call back into the HAL through interface, supplying * the reference as the first parameter. Note that before using the * reference returned by ath_hal_attach the caller should verify the * ABI version number. */ struct ath_hal { uint32_t ah_magic; /* consistency check magic number */ uint16_t ah_devid; /* PCI device ID */ uint16_t ah_subvendorid; /* PCI subvendor ID */ HAL_SOFTC ah_sc; /* back pointer to driver/os state */ HAL_BUS_TAG ah_st; /* params for register r+w */ HAL_BUS_HANDLE ah_sh; HAL_CTRY_CODE ah_countryCode; uint32_t ah_macVersion; /* MAC version id */ uint16_t ah_macRev; /* MAC revision */ uint16_t ah_phyRev; /* PHY revision */ /* NB: when only one radio is present the rev is in 5Ghz */ uint16_t ah_analog5GhzRev;/* 5GHz radio revision */ uint16_t ah_analog2GhzRev;/* 2GHz radio revision */ uint16_t *ah_eepromdata; /* eeprom buffer, if needed */ uint32_t ah_intrstate[8]; /* last int state */ uint32_t ah_syncstate; /* last sync intr state */ /* Current powerstate from HAL calls */ HAL_POWER_MODE ah_powerMode; HAL_OPS_CONFIG ah_config; const HAL_RATE_TABLE *__ahdecl(*ah_getRateTable)(struct ath_hal *, u_int mode); void __ahdecl(*ah_detach)(struct ath_hal*); /* Reset functions */ HAL_BOOL __ahdecl(*ah_reset)(struct ath_hal *, HAL_OPMODE, struct ieee80211_channel *, HAL_BOOL bChannelChange, HAL_RESET_TYPE resetType, HAL_STATUS *status); HAL_BOOL __ahdecl(*ah_phyDisable)(struct ath_hal *); HAL_BOOL __ahdecl(*ah_disable)(struct ath_hal *); void __ahdecl(*ah_configPCIE)(struct ath_hal *, HAL_BOOL restore, HAL_BOOL power_off); void __ahdecl(*ah_disablePCIE)(struct ath_hal *); void __ahdecl(*ah_setPCUConfig)(struct ath_hal *); HAL_BOOL __ahdecl(*ah_perCalibration)(struct ath_hal*, struct ieee80211_channel *, HAL_BOOL *); HAL_BOOL __ahdecl(*ah_perCalibrationN)(struct ath_hal *, struct ieee80211_channel *, u_int chainMask, HAL_BOOL longCal, HAL_BOOL *isCalDone); HAL_BOOL __ahdecl(*ah_resetCalValid)(struct ath_hal *, const struct ieee80211_channel *); HAL_BOOL __ahdecl(*ah_setTxPower)(struct ath_hal *, const struct ieee80211_channel *, uint16_t *); HAL_BOOL __ahdecl(*ah_setTxPowerLimit)(struct ath_hal *, uint32_t); HAL_BOOL __ahdecl(*ah_setBoardValues)(struct ath_hal *, const struct ieee80211_channel *); /* Transmit functions */ HAL_BOOL __ahdecl(*ah_updateTxTrigLevel)(struct ath_hal*, HAL_BOOL incTrigLevel); int __ahdecl(*ah_setupTxQueue)(struct ath_hal *, HAL_TX_QUEUE, const HAL_TXQ_INFO *qInfo); HAL_BOOL __ahdecl(*ah_setTxQueueProps)(struct ath_hal *, int q, const HAL_TXQ_INFO *qInfo); HAL_BOOL __ahdecl(*ah_getTxQueueProps)(struct ath_hal *, int q, HAL_TXQ_INFO *qInfo); HAL_BOOL __ahdecl(*ah_releaseTxQueue)(struct ath_hal *ah, u_int q); HAL_BOOL __ahdecl(*ah_resetTxQueue)(struct ath_hal *ah, u_int q); uint32_t __ahdecl(*ah_getTxDP)(struct ath_hal*, u_int); HAL_BOOL __ahdecl(*ah_setTxDP)(struct ath_hal*, u_int, uint32_t txdp); uint32_t __ahdecl(*ah_numTxPending)(struct ath_hal *, u_int q); HAL_BOOL __ahdecl(*ah_startTxDma)(struct ath_hal*, u_int); HAL_BOOL __ahdecl(*ah_stopTxDma)(struct ath_hal*, u_int); HAL_BOOL __ahdecl(*ah_setupTxDesc)(struct ath_hal *, struct ath_desc *, u_int pktLen, u_int hdrLen, HAL_PKT_TYPE type, u_int txPower, u_int txRate0, u_int txTries0, u_int keyIx, u_int antMode, u_int flags, u_int rtsctsRate, u_int rtsctsDuration, u_int compicvLen, u_int compivLen, u_int comp); HAL_BOOL __ahdecl(*ah_setupXTxDesc)(struct ath_hal *, struct ath_desc*, u_int txRate1, u_int txTries1, u_int txRate2, u_int txTries2, u_int txRate3, u_int txTries3); HAL_BOOL __ahdecl(*ah_fillTxDesc)(struct ath_hal *, struct ath_desc *, HAL_DMA_ADDR *bufAddrList, uint32_t *segLenList, u_int descId, u_int qcuId, HAL_BOOL firstSeg, HAL_BOOL lastSeg, const struct ath_desc *); HAL_STATUS __ahdecl(*ah_procTxDesc)(struct ath_hal *, struct ath_desc *, struct ath_tx_status *); void __ahdecl(*ah_getTxIntrQueue)(struct ath_hal *, uint32_t *); void __ahdecl(*ah_reqTxIntrDesc)(struct ath_hal *, struct ath_desc*); HAL_BOOL __ahdecl(*ah_getTxCompletionRates)(struct ath_hal *, const struct ath_desc *ds, int *rates, int *tries); void __ahdecl(*ah_setTxDescLink)(struct ath_hal *ah, void *ds, uint32_t link); void __ahdecl(*ah_getTxDescLink)(struct ath_hal *ah, void *ds, uint32_t *link); void __ahdecl(*ah_getTxDescLinkPtr)(struct ath_hal *ah, void *ds, uint32_t **linkptr); void __ahdecl(*ah_setupTxStatusRing)(struct ath_hal *, void *ts_start, uint32_t ts_paddr_start, uint16_t size); void __ahdecl(*ah_getTxRawTxDesc)(struct ath_hal *, u_int32_t *); /* Receive Functions */ uint32_t __ahdecl(*ah_getRxDP)(struct ath_hal*, HAL_RX_QUEUE); void __ahdecl(*ah_setRxDP)(struct ath_hal*, uint32_t rxdp, HAL_RX_QUEUE); void __ahdecl(*ah_enableReceive)(struct ath_hal*); HAL_BOOL __ahdecl(*ah_stopDmaReceive)(struct ath_hal*); void __ahdecl(*ah_startPcuReceive)(struct ath_hal*); void __ahdecl(*ah_stopPcuReceive)(struct ath_hal*); void __ahdecl(*ah_setMulticastFilter)(struct ath_hal*, uint32_t filter0, uint32_t filter1); HAL_BOOL __ahdecl(*ah_setMulticastFilterIndex)(struct ath_hal*, uint32_t index); HAL_BOOL __ahdecl(*ah_clrMulticastFilterIndex)(struct ath_hal*, uint32_t index); uint32_t __ahdecl(*ah_getRxFilter)(struct ath_hal*); void __ahdecl(*ah_setRxFilter)(struct ath_hal*, uint32_t); HAL_BOOL __ahdecl(*ah_setupRxDesc)(struct ath_hal *, struct ath_desc *, uint32_t size, u_int flags); HAL_STATUS __ahdecl(*ah_procRxDesc)(struct ath_hal *, struct ath_desc *, uint32_t phyAddr, struct ath_desc *next, uint64_t tsf, struct ath_rx_status *); void __ahdecl(*ah_rxMonitor)(struct ath_hal *, const HAL_NODE_STATS *, const struct ieee80211_channel *); void __ahdecl(*ah_aniPoll)(struct ath_hal *, const struct ieee80211_channel *); void __ahdecl(*ah_procMibEvent)(struct ath_hal *, const HAL_NODE_STATS *); /* Misc Functions */ HAL_STATUS __ahdecl(*ah_getCapability)(struct ath_hal *, HAL_CAPABILITY_TYPE, uint32_t capability, uint32_t *result); HAL_BOOL __ahdecl(*ah_setCapability)(struct ath_hal *, HAL_CAPABILITY_TYPE, uint32_t capability, uint32_t setting, HAL_STATUS *); HAL_BOOL __ahdecl(*ah_getDiagState)(struct ath_hal *, int request, const void *args, uint32_t argsize, void **result, uint32_t *resultsize); void __ahdecl(*ah_getMacAddress)(struct ath_hal *, uint8_t *); HAL_BOOL __ahdecl(*ah_setMacAddress)(struct ath_hal *, const uint8_t*); void __ahdecl(*ah_getBssIdMask)(struct ath_hal *, uint8_t *); HAL_BOOL __ahdecl(*ah_setBssIdMask)(struct ath_hal *, const uint8_t*); HAL_BOOL __ahdecl(*ah_setRegulatoryDomain)(struct ath_hal*, uint16_t, HAL_STATUS *); void __ahdecl(*ah_setLedState)(struct ath_hal*, HAL_LED_STATE); void __ahdecl(*ah_writeAssocid)(struct ath_hal*, const uint8_t *bssid, uint16_t assocId); HAL_BOOL __ahdecl(*ah_gpioCfgOutput)(struct ath_hal *, uint32_t gpio, HAL_GPIO_MUX_TYPE); HAL_BOOL __ahdecl(*ah_gpioCfgInput)(struct ath_hal *, uint32_t gpio); uint32_t __ahdecl(*ah_gpioGet)(struct ath_hal *, uint32_t gpio); HAL_BOOL __ahdecl(*ah_gpioSet)(struct ath_hal *, uint32_t gpio, uint32_t val); void __ahdecl(*ah_gpioSetIntr)(struct ath_hal*, u_int, uint32_t); uint32_t __ahdecl(*ah_getTsf32)(struct ath_hal*); uint64_t __ahdecl(*ah_getTsf64)(struct ath_hal*); void __ahdecl(*ah_setTsf64)(struct ath_hal *, uint64_t); void __ahdecl(*ah_resetTsf)(struct ath_hal*); HAL_BOOL __ahdecl(*ah_detectCardPresent)(struct ath_hal*); void __ahdecl(*ah_updateMibCounters)(struct ath_hal*, HAL_MIB_STATS*); HAL_RFGAIN __ahdecl(*ah_getRfGain)(struct ath_hal*); u_int __ahdecl(*ah_getDefAntenna)(struct ath_hal*); void __ahdecl(*ah_setDefAntenna)(struct ath_hal*, u_int); HAL_ANT_SETTING __ahdecl(*ah_getAntennaSwitch)(struct ath_hal*); HAL_BOOL __ahdecl(*ah_setAntennaSwitch)(struct ath_hal*, HAL_ANT_SETTING); HAL_BOOL __ahdecl(*ah_setSifsTime)(struct ath_hal*, u_int); u_int __ahdecl(*ah_getSifsTime)(struct ath_hal*); HAL_BOOL __ahdecl(*ah_setSlotTime)(struct ath_hal*, u_int); u_int __ahdecl(*ah_getSlotTime)(struct ath_hal*); HAL_BOOL __ahdecl(*ah_setAckTimeout)(struct ath_hal*, u_int); u_int __ahdecl(*ah_getAckTimeout)(struct ath_hal*); HAL_BOOL __ahdecl(*ah_setAckCTSRate)(struct ath_hal*, u_int); u_int __ahdecl(*ah_getAckCTSRate)(struct ath_hal*); HAL_BOOL __ahdecl(*ah_setCTSTimeout)(struct ath_hal*, u_int); u_int __ahdecl(*ah_getCTSTimeout)(struct ath_hal*); HAL_BOOL __ahdecl(*ah_setDecompMask)(struct ath_hal*, uint16_t, int); void __ahdecl(*ah_setCoverageClass)(struct ath_hal*, uint8_t, int); HAL_STATUS __ahdecl(*ah_setQuiet)(struct ath_hal *ah, uint32_t period, uint32_t duration, uint32_t nextStart, HAL_QUIET_FLAG flag); void __ahdecl(*ah_setChainMasks)(struct ath_hal *, uint32_t, uint32_t); /* DFS functions */ void __ahdecl(*ah_enableDfs)(struct ath_hal *ah, HAL_PHYERR_PARAM *pe); void __ahdecl(*ah_getDfsThresh)(struct ath_hal *ah, HAL_PHYERR_PARAM *pe); HAL_BOOL __ahdecl(*ah_getDfsDefaultThresh)(struct ath_hal *ah, HAL_PHYERR_PARAM *pe); HAL_BOOL __ahdecl(*ah_procRadarEvent)(struct ath_hal *ah, struct ath_rx_status *rxs, uint64_t fulltsf, const char *buf, HAL_DFS_EVENT *event); HAL_BOOL __ahdecl(*ah_isFastClockEnabled)(struct ath_hal *ah); /* Spectral Scan functions */ void __ahdecl(*ah_spectralConfigure)(struct ath_hal *ah, HAL_SPECTRAL_PARAM *sp); void __ahdecl(*ah_spectralGetConfig)(struct ath_hal *ah, HAL_SPECTRAL_PARAM *sp); void __ahdecl(*ah_spectralStart)(struct ath_hal *); void __ahdecl(*ah_spectralStop)(struct ath_hal *); HAL_BOOL __ahdecl(*ah_spectralIsEnabled)(struct ath_hal *); HAL_BOOL __ahdecl(*ah_spectralIsActive)(struct ath_hal *); /* XXX getNfPri() and getNfExt() */ /* Key Cache Functions */ uint32_t __ahdecl(*ah_getKeyCacheSize)(struct ath_hal*); HAL_BOOL __ahdecl(*ah_resetKeyCacheEntry)(struct ath_hal*, uint16_t); HAL_BOOL __ahdecl(*ah_isKeyCacheEntryValid)(struct ath_hal *, uint16_t); HAL_BOOL __ahdecl(*ah_setKeyCacheEntry)(struct ath_hal*, uint16_t, const HAL_KEYVAL *, const uint8_t *, int); HAL_BOOL __ahdecl(*ah_setKeyCacheEntryMac)(struct ath_hal*, uint16_t, const uint8_t *); /* Power Management Functions */ HAL_BOOL __ahdecl(*ah_setPowerMode)(struct ath_hal*, HAL_POWER_MODE mode, int setChip); HAL_POWER_MODE __ahdecl(*ah_getPowerMode)(struct ath_hal*); int16_t __ahdecl(*ah_getChanNoise)(struct ath_hal *, const struct ieee80211_channel *); /* Beacon Management Functions */ void __ahdecl(*ah_setBeaconTimers)(struct ath_hal*, const HAL_BEACON_TIMERS *); /* NB: deprecated, use ah_setBeaconTimers instead */ void __ahdecl(*ah_beaconInit)(struct ath_hal *, uint32_t nexttbtt, uint32_t intval); void __ahdecl(*ah_setStationBeaconTimers)(struct ath_hal*, const HAL_BEACON_STATE *); void __ahdecl(*ah_resetStationBeaconTimers)(struct ath_hal*); uint64_t __ahdecl(*ah_getNextTBTT)(struct ath_hal *); /* 802.11n Functions */ HAL_BOOL __ahdecl(*ah_chainTxDesc)(struct ath_hal *, struct ath_desc *, HAL_DMA_ADDR *bufAddrList, uint32_t *segLenList, u_int, u_int, HAL_PKT_TYPE, u_int, HAL_CIPHER, uint8_t, HAL_BOOL, HAL_BOOL, HAL_BOOL); HAL_BOOL __ahdecl(*ah_setupFirstTxDesc)(struct ath_hal *, struct ath_desc *, u_int, u_int, u_int, u_int, u_int, u_int, u_int, u_int); HAL_BOOL __ahdecl(*ah_setupLastTxDesc)(struct ath_hal *, struct ath_desc *, const struct ath_desc *); void __ahdecl(*ah_set11nRateScenario)(struct ath_hal *, struct ath_desc *, u_int, u_int, HAL_11N_RATE_SERIES [], u_int, u_int); /* * The next 4 (set11ntxdesc -> set11naggrlast) are specific * to the EDMA HAL. Descriptors are chained together by * using filltxdesc (not ChainTxDesc) and then setting the * aggregate flags appropriately using first/middle/last. */ void __ahdecl(*ah_set11nTxDesc)(struct ath_hal *, void *, u_int, HAL_PKT_TYPE, u_int, u_int, u_int); void __ahdecl(*ah_set11nAggrFirst)(struct ath_hal *, struct ath_desc *, u_int, u_int); void __ahdecl(*ah_set11nAggrMiddle)(struct ath_hal *, struct ath_desc *, u_int); void __ahdecl(*ah_set11nAggrLast)(struct ath_hal *, struct ath_desc *); void __ahdecl(*ah_clr11nAggr)(struct ath_hal *, struct ath_desc *); void __ahdecl(*ah_set11nBurstDuration)(struct ath_hal *, struct ath_desc *, u_int); void __ahdecl(*ah_set11nVirtMoreFrag)(struct ath_hal *, struct ath_desc *, u_int); HAL_BOOL __ahdecl(*ah_getMibCycleCounts) (struct ath_hal *, HAL_SURVEY_SAMPLE *); uint32_t __ahdecl(*ah_get11nExtBusy)(struct ath_hal *); void __ahdecl(*ah_set11nMac2040)(struct ath_hal *, HAL_HT_MACMODE); HAL_HT_RXCLEAR __ahdecl(*ah_get11nRxClear)(struct ath_hal *ah); void __ahdecl(*ah_set11nRxClear)(struct ath_hal *, HAL_HT_RXCLEAR); /* Interrupt functions */ HAL_BOOL __ahdecl(*ah_isInterruptPending)(struct ath_hal*); HAL_BOOL __ahdecl(*ah_getPendingInterrupts)(struct ath_hal*, HAL_INT*); HAL_INT __ahdecl(*ah_getInterrupts)(struct ath_hal*); HAL_INT __ahdecl(*ah_setInterrupts)(struct ath_hal*, HAL_INT); /* Bluetooth Coexistence functions */ void __ahdecl(*ah_btCoexSetInfo)(struct ath_hal *, HAL_BT_COEX_INFO *); void __ahdecl(*ah_btCoexSetConfig)(struct ath_hal *, HAL_BT_COEX_CONFIG *); void __ahdecl(*ah_btCoexSetQcuThresh)(struct ath_hal *, int); void __ahdecl(*ah_btCoexSetWeights)(struct ath_hal *, uint32_t); void __ahdecl(*ah_btCoexSetBmissThresh)(struct ath_hal *, uint32_t); void __ahdecl(*ah_btCoexSetParameter)(struct ath_hal *, uint32_t, uint32_t); void __ahdecl(*ah_btCoexDisable)(struct ath_hal *); int __ahdecl(*ah_btCoexEnable)(struct ath_hal *); /* Bluetooth MCI methods */ void __ahdecl(*ah_btMciSetup)(struct ath_hal *, uint32_t, void *, uint16_t, uint32_t); HAL_BOOL __ahdecl(*ah_btMciSendMessage)(struct ath_hal *, uint8_t, uint32_t, uint32_t *, uint8_t, HAL_BOOL, HAL_BOOL); uint32_t __ahdecl(*ah_btMciGetInterrupt)(struct ath_hal *, uint32_t *, uint32_t *); uint32_t __ahdecl(*ah_btMciGetState)(struct ath_hal *, uint32_t, uint32_t *); void __ahdecl(*ah_btMciDetach)(struct ath_hal *); /* LNA diversity configuration */ void __ahdecl(*ah_divLnaConfGet)(struct ath_hal *, HAL_ANT_COMB_CONFIG *); void __ahdecl(*ah_divLnaConfSet)(struct ath_hal *, HAL_ANT_COMB_CONFIG *); }; /* * Check the PCI vendor ID and device ID against Atheros' values * and return a printable description for any Atheros hardware. * AH_NULL is returned if the ID's do not describe Atheros hardware. */ extern const char *__ahdecl ath_hal_probe(uint16_t vendorid, uint16_t devid); /* * Attach the HAL for use with the specified device. The device is * defined by the PCI device ID. The caller provides an opaque pointer * to an upper-layer data structure (HAL_SOFTC) that is stored in the * HAL state block for later use. Hardware register accesses are done * using the specified bus tag and handle. On successful return a * reference to a state block is returned that must be supplied in all * subsequent HAL calls. Storage associated with this reference is * dynamically allocated and must be freed by calling the ah_detach * method when the client is done. If the attach operation fails a * null (AH_NULL) reference will be returned and a status code will * be returned if the status parameter is non-zero. */ extern struct ath_hal * __ahdecl ath_hal_attach(uint16_t devid, HAL_SOFTC, HAL_BUS_TAG, HAL_BUS_HANDLE, uint16_t *eepromdata, HAL_OPS_CONFIG *ah_config, HAL_STATUS* status); extern const char *ath_hal_mac_name(struct ath_hal *); extern const char *ath_hal_rf_name(struct ath_hal *); /* * Regulatory interfaces. Drivers should use ath_hal_init_channels to * request a set of channels for a particular country code and/or * regulatory domain. If CTRY_DEFAULT and SKU_NONE are specified then * this list is constructed according to the contents of the EEPROM. * ath_hal_getchannels acts similarly but does not alter the operating * state; this can be used to collect information for a particular * regulatory configuration. Finally ath_hal_set_channels installs a * channel list constructed outside the driver. The HAL will adopt the * channel list and setup internal state according to the specified * regulatory configuration (e.g. conformance test limits). * * For all interfaces the channel list is returned in the supplied array. * maxchans defines the maximum size of this array. nchans contains the * actual number of channels returned. If a problem occurred then a * status code != HAL_OK is returned. */ struct ieee80211_channel; /* * Return a list of channels according to the specified regulatory. */ extern HAL_STATUS __ahdecl ath_hal_getchannels(struct ath_hal *, struct ieee80211_channel *chans, u_int maxchans, int *nchans, u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn, HAL_BOOL enableExtendedChannels); /* * Return a list of channels and install it as the current operating * regulatory list. */ extern HAL_STATUS __ahdecl ath_hal_init_channels(struct ath_hal *, struct ieee80211_channel *chans, u_int maxchans, int *nchans, u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN rd, HAL_BOOL enableExtendedChannels); /* * Install the list of channels as the current operating regulatory * and setup related state according to the country code and sku. */ extern HAL_STATUS __ahdecl ath_hal_set_channels(struct ath_hal *, struct ieee80211_channel *chans, int nchans, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn); /* * Fetch the ctl/ext noise floor values reported by a MIMO * radio. Returns 1 for valid results, 0 for invalid channel. */ extern int __ahdecl ath_hal_get_mimo_chan_noise(struct ath_hal *ah, const struct ieee80211_channel *chan, int16_t *nf_ctl, int16_t *nf_ext); /* * Calibrate noise floor data following a channel scan or similar. * This must be called prior retrieving noise floor data. */ extern void __ahdecl ath_hal_process_noisefloor(struct ath_hal *ah); /* * Return bit mask of wireless modes supported by the hardware. */ extern u_int __ahdecl ath_hal_getwirelessmodes(struct ath_hal*); /* * Get the HAL wireless mode for the given channel. */ extern int ath_hal_get_curmode(struct ath_hal *ah, const struct ieee80211_channel *chan); /* * Calculate the packet TX time for a legacy or 11n frame */ extern uint32_t __ahdecl ath_hal_pkt_txtime(struct ath_hal *ah, const HAL_RATE_TABLE *rates, uint32_t frameLen, uint16_t rateix, HAL_BOOL isht40, HAL_BOOL shortPreamble); /* * Calculate the duration of an 11n frame. */ extern uint32_t __ahdecl ath_computedur_ht(uint32_t frameLen, uint16_t rate, int streams, HAL_BOOL isht40, HAL_BOOL isShortGI); /* * Calculate the transmit duration of a legacy frame. */ extern uint16_t __ahdecl ath_hal_computetxtime(struct ath_hal *, const HAL_RATE_TABLE *rates, uint32_t frameLen, uint16_t rateix, HAL_BOOL shortPreamble); /* * Adjust the TSF. */ extern void __ahdecl ath_hal_adjusttsf(struct ath_hal *ah, int32_t tsfdelta); /* * Enable or disable CCA. */ void __ahdecl ath_hal_setcca(struct ath_hal *ah, int ena); /* * Get CCA setting. */ int __ahdecl ath_hal_getcca(struct ath_hal *ah); /* * Read EEPROM data from ah_eepromdata */ HAL_BOOL __ahdecl ath_hal_EepromDataRead(struct ath_hal *ah, u_int off, uint16_t *data); /* * For now, simply pass through MFP frames. */ static inline u_int32_t ath_hal_get_mfp_qos(struct ath_hal *ah) { //return AH_PRIVATE(ah)->ah_mfp_qos; return HAL_MFP_QOSDATA; } #endif /* _ATH_AH_H_ */ Index: head/sys/dev/ath/ath_hal/ah_regdomain.c =================================================================== --- head/sys/dev/ath/ath_hal/ah_regdomain.c (revision 298938) +++ head/sys/dev/ath/ath_hal/ah_regdomain.c (revision 298939) @@ -1,875 +1,875 @@ /* * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * Copyright (c) 2005-2006 Atheros Communications, Inc. * All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include "opt_ah.h" #include "ah.h" #include #include #include "ah_internal.h" #include "ah_eeprom.h" #include "ah_devid.h" #include "ah_regdomain.h" /* * XXX this code needs a audit+review */ /* used throughout this file... */ #define N(a) (sizeof (a) / sizeof (a[0])) #define HAL_MODE_11A_TURBO HAL_MODE_108A #define HAL_MODE_11G_TURBO HAL_MODE_108G /* * Mask to check whether a domain is a multidomain or a single domain */ #define MULTI_DOMAIN_MASK 0xFF00 /* * Enumerated Regulatory Domain Information 8 bit values indicate that * the regdomain is really a pair of unitary regdomains. 12 bit values * are the real unitary regdomains and are the only ones which have the * frequency bitmasks and flags set. */ #include "ah_regdomain/ah_rd_regenum.h" #define WORLD_SKU_MASK 0x00F0 #define WORLD_SKU_PREFIX 0x0060 /* * THE following table is the mapping of regdomain pairs specified by * an 8 bit regdomain value to the individual unitary reg domains */ #include "ah_regdomain/ah_rd_regmap.h" /* * The following tables are the master list for all different freqeuncy * bands with the complete matrix of all possible flags and settings * for each band if it is used in ANY reg domain. */ #define COUNTRY_ERD_FLAG 0x8000 #define WORLDWIDE_ROAMING_FLAG 0x4000 /* * This table maps country ISO codes from net80211 into regulatory * domains which the ath regulatory domain code understands. */ #include "ah_regdomain/ah_rd_ctry.h" /* * The frequency band collections are a set of frequency ranges * with shared properties - max tx power, max antenna gain, channel width, * channel spacing, DFS requirements and passive scanning requirements. * * These are represented as entries in a frequency band bitmask. * Each regulatory domain entry in ah_regdomain_domains.h uses one * or more frequency band entries for each of the channel modes * supported (11bg, 11a, half, quarter, turbo, etc.) * */ #include "ah_regdomain/ah_rd_freqbands.h" /* * This is the main regulatory database. It defines the supported * set of features and requirements for each of the defined regulatory * zones. It uses combinations of frequency ranges - represented in * a bitmask - to determine the requirements and limitations needed. */ #include "ah_regdomain/ah_rd_domains.h" static const struct cmode modes[] = { { HAL_MODE_TURBO, IEEE80211_CHAN_ST }, { HAL_MODE_11A, IEEE80211_CHAN_A }, { HAL_MODE_11B, IEEE80211_CHAN_B }, { HAL_MODE_11G, IEEE80211_CHAN_G }, { HAL_MODE_11G_TURBO, IEEE80211_CHAN_108G }, { HAL_MODE_11A_TURBO, IEEE80211_CHAN_108A }, { HAL_MODE_11A_QUARTER_RATE, IEEE80211_CHAN_A | IEEE80211_CHAN_QUARTER }, { HAL_MODE_11A_HALF_RATE, IEEE80211_CHAN_A | IEEE80211_CHAN_HALF }, { HAL_MODE_11G_QUARTER_RATE, IEEE80211_CHAN_G | IEEE80211_CHAN_QUARTER }, { HAL_MODE_11G_HALF_RATE, IEEE80211_CHAN_G | IEEE80211_CHAN_HALF }, { HAL_MODE_11NG_HT20, IEEE80211_CHAN_G | IEEE80211_CHAN_HT20 }, { HAL_MODE_11NG_HT40PLUS, IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U }, { HAL_MODE_11NG_HT40MINUS, IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D }, { HAL_MODE_11NA_HT20, IEEE80211_CHAN_A | IEEE80211_CHAN_HT20 }, { HAL_MODE_11NA_HT40PLUS, IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U }, { HAL_MODE_11NA_HT40MINUS, IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D }, }; static void ath_hal_update_dfsdomain(struct ath_hal *ah); static OS_INLINE uint16_t getEepromRD(struct ath_hal *ah) { return AH_PRIVATE(ah)->ah_currentRD &~ WORLDWIDE_ROAMING_FLAG; } /* * Test to see if the bitmask array is all zeros */ static HAL_BOOL isChanBitMaskZero(const uint64_t *bitmask) { #if BMLEN > 2 #error "add more cases" #endif #if BMLEN > 1 if (bitmask[1] != 0) return AH_FALSE; #endif return (bitmask[0] == 0); } /* * Return whether or not the regulatory domain/country in EEPROM * is acceptable. */ static HAL_BOOL isEepromValid(struct ath_hal *ah) { uint16_t rd = getEepromRD(ah); int i; if (rd & COUNTRY_ERD_FLAG) { uint16_t cc = rd &~ COUNTRY_ERD_FLAG; for (i = 0; i < N(allCountries); i++) if (allCountries[i].countryCode == cc) return AH_TRUE; } else { for (i = 0; i < N(regDomainPairs); i++) if (regDomainPairs[i].regDmnEnum == rd) return AH_TRUE; } if (rd == FCC_UBNT) { return AH_TRUE; } HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: invalid regulatory domain/country code 0x%x\n", __func__, rd); return AH_FALSE; } /* * Find the pointer to the country element in the country table * corresponding to the country code */ static COUNTRY_CODE_TO_ENUM_RD* findCountry(HAL_CTRY_CODE countryCode) { int i; for (i = 0; i < N(allCountries); i++) { if (allCountries[i].countryCode == countryCode) return &allCountries[i]; } return AH_NULL; } static REG_DOMAIN * findRegDmn(int regDmn) { int i; for (i = 0; i < N(regDomains); i++) { if (regDomains[i].regDmnEnum == regDmn) return ®Domains[i]; } return AH_NULL; } static REG_DMN_PAIR_MAPPING * findRegDmnPair(int regDmnPair) { int i; if (regDmnPair != NO_ENUMRD) { for (i = 0; i < N(regDomainPairs); i++) { if (regDomainPairs[i].regDmnEnum == regDmnPair) return ®DomainPairs[i]; } } return AH_NULL; } /* * Calculate a default country based on the EEPROM setting. */ static HAL_CTRY_CODE getDefaultCountry(struct ath_hal *ah) { REG_DMN_PAIR_MAPPING *regpair; uint16_t rd; rd = getEepromRD(ah); if (rd & COUNTRY_ERD_FLAG) { COUNTRY_CODE_TO_ENUM_RD *country; uint16_t cc = rd & ~COUNTRY_ERD_FLAG; country = findCountry(cc); if (country != AH_NULL) return cc; } /* * Check reg domains that have only one country */ regpair = findRegDmnPair(rd); return (regpair != AH_NULL) ? regpair->singleCC : CTRY_DEFAULT; } static HAL_BOOL IS_BIT_SET(int bit, const uint64_t bitmask[]) { int byteOffset, bitnum; uint64_t val; byteOffset = bit/64; bitnum = bit - byteOffset*64; val = ((uint64_t) 1) << bitnum; return (bitmask[byteOffset] & val) != 0; } static HAL_STATUS getregstate(struct ath_hal *ah, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn, COUNTRY_CODE_TO_ENUM_RD **pcountry, REG_DOMAIN **prd2GHz, REG_DOMAIN **prd5GHz) { COUNTRY_CODE_TO_ENUM_RD *country; REG_DOMAIN *rd5GHz, *rd2GHz; if (cc == CTRY_DEFAULT && regDmn == SKU_NONE) { /* * Validate the EEPROM setting and setup defaults */ if (!isEepromValid(ah)) { /* * Don't return any channels if the EEPROM has an * invalid regulatory domain/country code setting. */ HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: invalid EEPROM contents\n",__func__); return HAL_EEBADREG; } cc = getDefaultCountry(ah); country = findCountry(cc); if (country == AH_NULL) { HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "NULL Country!, cc %d\n", cc); return HAL_EEBADCC; } regDmn = country->regDmnEnum; HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: EEPROM cc %u rd 0x%x\n", __func__, cc, regDmn); if (country->countryCode == CTRY_DEFAULT) { /* * Check EEPROM; SKU may be for a country, single * domain, or multiple domains (WWR). */ uint16_t rdnum = getEepromRD(ah); if ((rdnum & COUNTRY_ERD_FLAG) == 0 && (findRegDmn(rdnum) != AH_NULL || findRegDmnPair(rdnum) != AH_NULL)) { regDmn = rdnum; HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: EEPROM rd 0x%x\n", __func__, rdnum); } } } else { country = findCountry(cc); if (country == AH_NULL) { HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "unknown country, cc %d\n", cc); return HAL_EINVAL; } if (regDmn == SKU_NONE) regDmn = country->regDmnEnum; HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u rd 0x%x\n", __func__, cc, regDmn); } /* * Setup per-band state. */ if ((regDmn & MULTI_DOMAIN_MASK) == 0) { REG_DMN_PAIR_MAPPING *regpair = findRegDmnPair(regDmn); if (regpair == AH_NULL) { HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: no reg domain pair %u for country %u\n", __func__, regDmn, country->countryCode); return HAL_EINVAL; } rd5GHz = findRegDmn(regpair->regDmn5GHz); if (rd5GHz == AH_NULL) { HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: no 5GHz reg domain %u for country %u\n", __func__, regpair->regDmn5GHz, country->countryCode); return HAL_EINVAL; } rd2GHz = findRegDmn(regpair->regDmn2GHz); if (rd2GHz == AH_NULL) { HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: no 2GHz reg domain %u for country %u\n", __func__, regpair->regDmn2GHz, country->countryCode); return HAL_EINVAL; } } else { rd5GHz = rd2GHz = findRegDmn(regDmn); if (rd2GHz == AH_NULL) { HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: no unitary reg domain %u for country %u\n", __func__, regDmn, country->countryCode); return HAL_EINVAL; } } if (pcountry != AH_NULL) *pcountry = country; *prd2GHz = rd2GHz; *prd5GHz = rd5GHz; return HAL_OK; } /* * Construct the channel list for the specified regulatory config. */ static HAL_STATUS getchannels(struct ath_hal *ah, struct ieee80211_channel chans[], u_int maxchans, int *nchans, u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn, HAL_BOOL enableExtendedChannels, COUNTRY_CODE_TO_ENUM_RD **pcountry, REG_DOMAIN **prd2GHz, REG_DOMAIN **prd5GHz) { #define CHANNEL_HALF_BW 10 #define CHANNEL_QUARTER_BW 5 #define HAL_MODE_11A_ALL \ (HAL_MODE_11A | HAL_MODE_11A_TURBO | HAL_MODE_TURBO | \ HAL_MODE_11A_QUARTER_RATE | HAL_MODE_11A_HALF_RATE) REG_DOMAIN *rd5GHz, *rd2GHz; u_int modesAvail; const struct cmode *cm; struct ieee80211_channel *ic; int next, b; HAL_STATUS status; HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u regDmn 0x%x mode 0x%x%s\n", __func__, cc, regDmn, modeSelect, enableExtendedChannels ? " ecm" : ""); status = getregstate(ah, cc, regDmn, pcountry, &rd2GHz, &rd5GHz); if (status != HAL_OK) return status; /* get modes that HW is capable of */ modesAvail = ath_hal_getWirelessModes(ah); /* optimize work below if no 11a channels */ if (isChanBitMaskZero(rd5GHz->chan11a) && (modesAvail & HAL_MODE_11A_ALL)) { HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: disallow all 11a\n", __func__); modesAvail &= ~HAL_MODE_11A_ALL; } next = 0; ic = &chans[0]; for (cm = modes; cm < &modes[N(modes)]; cm++) { uint16_t c, c_hi, c_lo; uint64_t *channelBM = AH_NULL; REG_DMN_FREQ_BAND *fband = AH_NULL,*freqs; int low_adj, hi_adj, channelSep, lastc; uint32_t rdflags; uint64_t dfsMask; uint64_t pscan; if ((cm->mode & modeSelect) == 0) { HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: skip mode 0x%x flags 0x%x\n", __func__, cm->mode, cm->flags); continue; } if ((cm->mode & modesAvail) == 0) { HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: !avail mode 0x%x (0x%x) flags 0x%x\n", __func__, modesAvail, cm->mode, cm->flags); continue; } if (!ath_hal_getChannelEdges(ah, cm->flags, &c_lo, &c_hi)) { /* channel not supported by hardware, skip it */ HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: channels 0x%x not supported by hardware\n", __func__,cm->flags); continue; } switch (cm->mode) { case HAL_MODE_TURBO: case HAL_MODE_11A_TURBO: rdflags = rd5GHz->flags; dfsMask = rd5GHz->dfsMask; pscan = rd5GHz->pscan; if (cm->mode == HAL_MODE_TURBO) channelBM = rd5GHz->chan11a_turbo; else channelBM = rd5GHz->chan11a_dyn_turbo; freqs = ®Dmn5GhzTurboFreq[0]; break; case HAL_MODE_11G_TURBO: rdflags = rd2GHz->flags; dfsMask = rd2GHz->dfsMask; pscan = rd2GHz->pscan; channelBM = rd2GHz->chan11g_turbo; freqs = ®Dmn2Ghz11gTurboFreq[0]; break; case HAL_MODE_11A: case HAL_MODE_11A_HALF_RATE: case HAL_MODE_11A_QUARTER_RATE: case HAL_MODE_11NA_HT20: case HAL_MODE_11NA_HT40PLUS: case HAL_MODE_11NA_HT40MINUS: rdflags = rd5GHz->flags; dfsMask = rd5GHz->dfsMask; pscan = rd5GHz->pscan; if (cm->mode == HAL_MODE_11A_HALF_RATE) channelBM = rd5GHz->chan11a_half; else if (cm->mode == HAL_MODE_11A_QUARTER_RATE) channelBM = rd5GHz->chan11a_quarter; else channelBM = rd5GHz->chan11a; freqs = ®Dmn5GhzFreq[0]; break; case HAL_MODE_11B: case HAL_MODE_11G: case HAL_MODE_11G_HALF_RATE: case HAL_MODE_11G_QUARTER_RATE: case HAL_MODE_11NG_HT20: case HAL_MODE_11NG_HT40PLUS: case HAL_MODE_11NG_HT40MINUS: rdflags = rd2GHz->flags; dfsMask = rd2GHz->dfsMask; pscan = rd2GHz->pscan; if (cm->mode == HAL_MODE_11G_HALF_RATE) channelBM = rd2GHz->chan11g_half; else if (cm->mode == HAL_MODE_11G_QUARTER_RATE) channelBM = rd2GHz->chan11g_quarter; else if (cm->mode == HAL_MODE_11B) channelBM = rd2GHz->chan11b; else channelBM = rd2GHz->chan11g; if (cm->mode == HAL_MODE_11B) freqs = ®Dmn2GhzFreq[0]; else freqs = ®Dmn2Ghz11gFreq[0]; break; default: HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, - "%s: Unkonwn HAL mode 0x%x\n", __func__, cm->mode); + "%s: Unknown HAL mode 0x%x\n", __func__, cm->mode); continue; } if (isChanBitMaskZero(channelBM)) continue; /* * Setup special handling for HT40 channels; e.g. * 5G HT40 channels require 40Mhz channel separation. */ hi_adj = (cm->mode == HAL_MODE_11NA_HT40PLUS || cm->mode == HAL_MODE_11NG_HT40PLUS) ? -20 : 0; low_adj = (cm->mode == HAL_MODE_11NA_HT40MINUS || cm->mode == HAL_MODE_11NG_HT40MINUS) ? 20 : 0; channelSep = (cm->mode == HAL_MODE_11NA_HT40PLUS || cm->mode == HAL_MODE_11NA_HT40MINUS) ? 40 : 0; for (b = 0; b < 64*BMLEN; b++) { if (!IS_BIT_SET(b, channelBM)) continue; fband = &freqs[b]; lastc = 0; for (c = fband->lowChannel + low_adj; c <= fband->highChannel + hi_adj; c += fband->channelSep) { if (!(c_lo <= c && c <= c_hi)) { HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: c %u out of range [%u..%u]\n", __func__, c, c_lo, c_hi); continue; } if (next >= maxchans){ HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: too many channels for channel table\n", __func__); goto done; } if ((fband->usePassScan & IS_ECM_CHAN) && !enableExtendedChannels) { HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "skip ecm channel\n"); continue; } #if 0 if ((fband->useDfs & dfsMask) && (cm->flags & IEEE80211_CHAN_HT40)) { /* NB: DFS and HT40 don't mix */ HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "skip HT40 chan, DFS required\n"); continue; } #endif /* * Make sure that channel separation * meets the requirement. */ if (lastc && channelSep && (c-lastc) < channelSep) continue; lastc = c; OS_MEMZERO(ic, sizeof(*ic)); ic->ic_freq = c; ic->ic_flags = cm->flags; ic->ic_maxregpower = fband->powerDfs; ath_hal_getpowerlimits(ah, ic); ic->ic_maxantgain = fband->antennaMax; if (fband->usePassScan & pscan) ic->ic_flags |= IEEE80211_CHAN_PASSIVE; if (fband->useDfs & dfsMask) ic->ic_flags |= IEEE80211_CHAN_DFS; if (IEEE80211_IS_CHAN_5GHZ(ic) && (rdflags & DISALLOW_ADHOC_11A)) ic->ic_flags |= IEEE80211_CHAN_NOADHOC; if (IEEE80211_IS_CHAN_TURBO(ic) && (rdflags & DISALLOW_ADHOC_11A_TURB)) ic->ic_flags |= IEEE80211_CHAN_NOADHOC; if (rdflags & NO_HOSTAP) ic->ic_flags |= IEEE80211_CHAN_NOHOSTAP; if (rdflags & LIMIT_FRAME_4MS) ic->ic_flags |= IEEE80211_CHAN_4MSXMIT; if (rdflags & NEED_NFC) ic->ic_flags |= CHANNEL_NFCREQUIRED; ic++, next++; } } } done: *nchans = next; /* NB: pcountry set above by getregstate */ if (prd2GHz != AH_NULL) *prd2GHz = rd2GHz; if (prd5GHz != AH_NULL) *prd5GHz = rd5GHz; return HAL_OK; #undef HAL_MODE_11A_ALL #undef CHANNEL_HALF_BW #undef CHANNEL_QUARTER_BW } /* * Retrieve a channel list without affecting runtime state. */ HAL_STATUS ath_hal_getchannels(struct ath_hal *ah, struct ieee80211_channel chans[], u_int maxchans, int *nchans, u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn, HAL_BOOL enableExtendedChannels) { return getchannels(ah, chans, maxchans, nchans, modeSelect, cc, regDmn, enableExtendedChannels, AH_NULL, AH_NULL, AH_NULL); } /* * Handle frequency mapping from 900Mhz range to 2.4GHz range * for GSM radios. This is done when we need the h/w frequency * and the channel is marked IEEE80211_CHAN_GSM. */ static int ath_hal_mapgsm(int sku, int freq) { if (sku == SKU_XR9) return 1520 + freq; if (sku == SKU_GZ901) return 1544 + freq; if (sku == SKU_SR9) return 3344 - freq; if (sku == SKU_XC900M) return 1517 + freq; HALDEBUG(AH_NULL, HAL_DEBUG_ANY, "%s: cannot map freq %u unknown gsm sku %u\n", __func__, freq, sku); return freq; } /* * Setup the internal/private channel state given a table of * net80211 channels. We collapse entries for the same frequency * and record the frequency for doing noise floor processing * where we don't have net80211 channel context. */ static HAL_BOOL assignPrivateChannels(struct ath_hal *ah, struct ieee80211_channel chans[], int nchans, int sku) { HAL_CHANNEL_INTERNAL *ic; int i, j, next, freq; next = 0; for (i = 0; i < nchans; i++) { struct ieee80211_channel *c = &chans[i]; for (j = i-1; j >= 0; j--) if (chans[j].ic_freq == c->ic_freq) { c->ic_devdata = chans[j].ic_devdata; break; } if (j < 0) { /* new entry, assign a private channel entry */ if (next >= N(AH_PRIVATE(ah)->ah_channels)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: too many channels, max %zu\n", __func__, N(AH_PRIVATE(ah)->ah_channels)); return AH_FALSE; } /* * Handle frequency mapping for 900MHz devices. * The hardware uses 2.4GHz frequencies that are * down-converted. The 802.11 layer uses the * true frequencies. */ freq = IEEE80211_IS_CHAN_GSM(c) ? ath_hal_mapgsm(sku, c->ic_freq) : c->ic_freq; HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: private[%3u] %u/0x%x -> channel %u\n", __func__, next, c->ic_freq, c->ic_flags, freq); ic = &AH_PRIVATE(ah)->ah_channels[next]; /* * NB: This clears privFlags which means ancillary * code like ANI and IQ calibration will be * restarted and re-setup any per-channel state. */ OS_MEMZERO(ic, sizeof(*ic)); ic->channel = freq; c->ic_devdata = next; next++; } } AH_PRIVATE(ah)->ah_nchan = next; HALDEBUG(ah, HAL_DEBUG_ANY, "%s: %u public, %u private channels\n", __func__, nchans, next); return AH_TRUE; } /* * Setup the channel list based on the information in the EEPROM. */ HAL_STATUS ath_hal_init_channels(struct ath_hal *ah, struct ieee80211_channel chans[], u_int maxchans, int *nchans, u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn, HAL_BOOL enableExtendedChannels) { COUNTRY_CODE_TO_ENUM_RD *country; REG_DOMAIN *rd5GHz, *rd2GHz; HAL_STATUS status; status = getchannels(ah, chans, maxchans, nchans, modeSelect, cc, regDmn, enableExtendedChannels, &country, &rd2GHz, &rd5GHz); if (status == HAL_OK && assignPrivateChannels(ah, chans, *nchans, AH_PRIVATE(ah)->ah_currentRD)) { AH_PRIVATE(ah)->ah_rd2GHz = rd2GHz; AH_PRIVATE(ah)->ah_rd5GHz = rd5GHz; ah->ah_countryCode = country->countryCode; HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u\n", __func__, ah->ah_countryCode); /* Update current DFS domain */ ath_hal_update_dfsdomain(ah); } else status = HAL_EINVAL; return status; } /* * Set the channel list. */ HAL_STATUS ath_hal_set_channels(struct ath_hal *ah, struct ieee80211_channel chans[], int nchans, HAL_CTRY_CODE cc, HAL_REG_DOMAIN rd) { COUNTRY_CODE_TO_ENUM_RD *country; REG_DOMAIN *rd5GHz, *rd2GHz; HAL_STATUS status; switch (rd) { case SKU_SR9: case SKU_XR9: case SKU_GZ901: case SKU_XC900M: /* * Map 900MHz sku's. The frequencies will be mapped * according to the sku to compensate for the down-converter. * We use the FCC for these sku's as the mapped channel * list is known compatible (will need to change if/when * vendors do different mapping in different locales). */ status = getregstate(ah, CTRY_DEFAULT, SKU_FCC, &country, &rd2GHz, &rd5GHz); break; default: status = getregstate(ah, cc, rd, &country, &rd2GHz, &rd5GHz); rd = AH_PRIVATE(ah)->ah_currentRD; break; } if (status == HAL_OK && assignPrivateChannels(ah, chans, nchans, rd)) { AH_PRIVATE(ah)->ah_rd2GHz = rd2GHz; AH_PRIVATE(ah)->ah_rd5GHz = rd5GHz; ah->ah_countryCode = country->countryCode; HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u\n", __func__, ah->ah_countryCode); } else status = HAL_EINVAL; if (status == HAL_OK) { /* Update current DFS domain */ (void) ath_hal_update_dfsdomain(ah); } return status; } #ifdef AH_DEBUG /* * Return the internal channel corresponding to a public channel. * NB: normally this routine is inline'd (see ah_internal.h) */ HAL_CHANNEL_INTERNAL * ath_hal_checkchannel(struct ath_hal *ah, const struct ieee80211_channel *c) { HAL_CHANNEL_INTERNAL *cc = &AH_PRIVATE(ah)->ah_channels[c->ic_devdata]; if (c->ic_devdata < AH_PRIVATE(ah)->ah_nchan && (c->ic_freq == cc->channel || IEEE80211_IS_CHAN_GSM(c))) return cc; if (c->ic_devdata >= AH_PRIVATE(ah)->ah_nchan) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: bad mapping, devdata %u nchans %u\n", __func__, c->ic_devdata, AH_PRIVATE(ah)->ah_nchan); HALASSERT(c->ic_devdata < AH_PRIVATE(ah)->ah_nchan); } else { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: no match for %u/0x%x devdata %u channel %u\n", __func__, c->ic_freq, c->ic_flags, c->ic_devdata, cc->channel); HALASSERT(c->ic_freq == cc->channel || IEEE80211_IS_CHAN_GSM(c)); } return AH_NULL; } #endif /* AH_DEBUG */ #define isWwrSKU(_ah) \ ((getEepromRD((_ah)) & WORLD_SKU_MASK) == WORLD_SKU_PREFIX || \ getEepromRD(_ah) == WORLD) /* * Return the test group for the specific channel based on * the current regulatory setup. */ u_int ath_hal_getctl(struct ath_hal *ah, const struct ieee80211_channel *c) { u_int ctl; if (AH_PRIVATE(ah)->ah_rd2GHz == AH_PRIVATE(ah)->ah_rd5GHz || (ah->ah_countryCode == CTRY_DEFAULT && isWwrSKU(ah))) ctl = SD_NO_CTL; else if (IEEE80211_IS_CHAN_2GHZ(c)) ctl = AH_PRIVATE(ah)->ah_rd2GHz->conformanceTestLimit; else ctl = AH_PRIVATE(ah)->ah_rd5GHz->conformanceTestLimit; if (IEEE80211_IS_CHAN_B(c)) return ctl | CTL_11B; if (IEEE80211_IS_CHAN_G(c)) return ctl | CTL_11G; if (IEEE80211_IS_CHAN_108G(c)) return ctl | CTL_108G; if (IEEE80211_IS_CHAN_TURBO(c)) return ctl | CTL_TURBO; if (IEEE80211_IS_CHAN_A(c)) return ctl | CTL_11A; return ctl; } /* * Update the current dfsDomain setting based on the given * country code. * * Since FreeBSD/net80211 allows the channel set to change * after the card has been setup (via ath_hal_init_channels()) * this function method is needed to update ah_dfsDomain. */ void ath_hal_update_dfsdomain(struct ath_hal *ah) { const REG_DOMAIN *rd5GHz = AH_PRIVATE(ah)->ah_rd5GHz; HAL_DFS_DOMAIN dfsDomain = HAL_DFS_UNINIT_DOMAIN; if (rd5GHz->dfsMask & DFS_FCC3) dfsDomain = HAL_DFS_FCC_DOMAIN; if (rd5GHz->dfsMask & DFS_ETSI) dfsDomain = HAL_DFS_ETSI_DOMAIN; if (rd5GHz->dfsMask & DFS_MKK4) dfsDomain = HAL_DFS_MKK4_DOMAIN; AH_PRIVATE(ah)->ah_dfsDomain = dfsDomain; HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s ah_dfsDomain: %d\n", __func__, AH_PRIVATE(ah)->ah_dfsDomain); } /* * Return the max allowed antenna gain and apply any regulatory * domain specific changes. * * NOTE: a negative reduction is possible in RD's that only * measure radiated power (e.g., ETSI) which would increase * that actual conducted output power (though never beyond * the calibrated target power). */ u_int ath_hal_getantennareduction(struct ath_hal *ah, const struct ieee80211_channel *chan, u_int twiceGain) { int8_t antennaMax = twiceGain - chan->ic_maxantgain*2; return (antennaMax < 0) ? 0 : antennaMax; } Index: head/sys/dev/ath/ath_hal/ar5211/ar5211_reset.c =================================================================== --- head/sys/dev/ath/ath_hal/ar5211/ar5211_reset.c (revision 298938) +++ head/sys/dev/ath/ath_hal/ar5211/ar5211_reset.c (revision 298939) @@ -1,2120 +1,2120 @@ /* * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * Copyright (c) 2002-2006 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include "opt_ah.h" /* * Chips specific device attachment and device info collection * Connects Init Reg Vectors, EEPROM Data, and device Functions. */ #include "ah.h" #include "ah_internal.h" #include "ah_devid.h" #include "ar5211/ar5211.h" #include "ar5211/ar5211reg.h" #include "ar5211/ar5211phy.h" #include "ah_eeprom_v3.h" /* Add static register initialization vectors */ #include "ar5211/boss.ini" /* * Structure to hold 11b tuning information for Beanie/Sombrero * 16 MHz mode, divider ratio = 198 = NP+S. N=16, S=4 or 6, P=12 */ typedef struct { uint32_t refClkSel; /* reference clock, 1 for 16 MHz */ uint32_t channelSelect; /* P[7:4]S[3:0] bits */ uint16_t channel5111; /* 11a channel for 5111 */ } CHAN_INFO_2GHZ; #define CI_2GHZ_INDEX_CORRECTION 19 static const CHAN_INFO_2GHZ chan2GHzData[] = { { 1, 0x46, 96 }, /* 2312 -19 */ { 1, 0x46, 97 }, /* 2317 -18 */ { 1, 0x46, 98 }, /* 2322 -17 */ { 1, 0x46, 99 }, /* 2327 -16 */ { 1, 0x46, 100 }, /* 2332 -15 */ { 1, 0x46, 101 }, /* 2337 -14 */ { 1, 0x46, 102 }, /* 2342 -13 */ { 1, 0x46, 103 }, /* 2347 -12 */ { 1, 0x46, 104 }, /* 2352 -11 */ { 1, 0x46, 105 }, /* 2357 -10 */ { 1, 0x46, 106 }, /* 2362 -9 */ { 1, 0x46, 107 }, /* 2367 -8 */ { 1, 0x46, 108 }, /* 2372 -7 */ /* index -6 to 0 are pad to make this a nolookup table */ { 1, 0x46, 116 }, /* -6 */ { 1, 0x46, 116 }, /* -5 */ { 1, 0x46, 116 }, /* -4 */ { 1, 0x46, 116 }, /* -3 */ { 1, 0x46, 116 }, /* -2 */ { 1, 0x46, 116 }, /* -1 */ { 1, 0x46, 116 }, /* 0 */ { 1, 0x46, 116 }, /* 2412 1 */ { 1, 0x46, 117 }, /* 2417 2 */ { 1, 0x46, 118 }, /* 2422 3 */ { 1, 0x46, 119 }, /* 2427 4 */ { 1, 0x46, 120 }, /* 2432 5 */ { 1, 0x46, 121 }, /* 2437 6 */ { 1, 0x46, 122 }, /* 2442 7 */ { 1, 0x46, 123 }, /* 2447 8 */ { 1, 0x46, 124 }, /* 2452 9 */ { 1, 0x46, 125 }, /* 2457 10 */ { 1, 0x46, 126 }, /* 2462 11 */ { 1, 0x46, 127 }, /* 2467 12 */ { 1, 0x46, 128 }, /* 2472 13 */ { 1, 0x44, 124 }, /* 2484 14 */ { 1, 0x46, 136 }, /* 2512 15 */ { 1, 0x46, 140 }, /* 2532 16 */ { 1, 0x46, 144 }, /* 2552 17 */ { 1, 0x46, 148 }, /* 2572 18 */ { 1, 0x46, 152 }, /* 2592 19 */ { 1, 0x46, 156 }, /* 2612 20 */ { 1, 0x46, 160 }, /* 2632 21 */ { 1, 0x46, 164 }, /* 2652 22 */ { 1, 0x46, 168 }, /* 2672 23 */ { 1, 0x46, 172 }, /* 2692 24 */ { 1, 0x46, 176 }, /* 2712 25 */ { 1, 0x46, 180 } /* 2732 26 */ }; /* Power timeouts in usec to wait for chip to wake-up. */ #define POWER_UP_TIME 2000 #define DELAY_PLL_SETTLE 300 /* 300 us */ #define DELAY_BASE_ACTIVATE 100 /* 100 us */ #define NUM_RATES 8 static HAL_BOOL ar5211SetResetReg(struct ath_hal *ah, uint32_t resetMask); static HAL_BOOL ar5211SetChannel(struct ath_hal *, const struct ieee80211_channel *); static int16_t ar5211RunNoiseFloor(struct ath_hal *, uint8_t runTime, int16_t startingNF); static HAL_BOOL ar5211IsNfGood(struct ath_hal *, struct ieee80211_channel *chan); static HAL_BOOL ar5211SetRf6and7(struct ath_hal *, const struct ieee80211_channel *chan); static HAL_BOOL ar5211SetBoardValues(struct ath_hal *, const struct ieee80211_channel *chan); static void ar5211SetPowerTable(struct ath_hal *, PCDACS_EEPROM *pSrcStruct, uint16_t channel); static HAL_BOOL ar5211SetTransmitPower(struct ath_hal *, const struct ieee80211_channel *); static void ar5211SetRateTable(struct ath_hal *, RD_EDGES_POWER *pRdEdgesPower, TRGT_POWER_INFO *pPowerInfo, uint16_t numChannels, const struct ieee80211_channel *chan); static uint16_t ar5211GetScaledPower(uint16_t channel, uint16_t pcdacValue, const PCDACS_EEPROM *pSrcStruct); static HAL_BOOL ar5211FindValueInList(uint16_t channel, uint16_t pcdacValue, const PCDACS_EEPROM *pSrcStruct, uint16_t *powerValue); static uint16_t ar5211GetInterpolatedValue(uint16_t target, uint16_t srcLeft, uint16_t srcRight, uint16_t targetLeft, uint16_t targetRight, HAL_BOOL scaleUp); static void ar5211GetLowerUpperValues(uint16_t value, const uint16_t *pList, uint16_t listSize, uint16_t *pLowerValue, uint16_t *pUpperValue); static void ar5211GetLowerUpperPcdacs(uint16_t pcdac, uint16_t channel, const PCDACS_EEPROM *pSrcStruct, uint16_t *pLowerPcdac, uint16_t *pUpperPcdac); static void ar5211SetRfgain(struct ath_hal *, const GAIN_VALUES *); static void ar5211RequestRfgain(struct ath_hal *); static HAL_BOOL ar5211InvalidGainReadback(struct ath_hal *, GAIN_VALUES *); static HAL_BOOL ar5211IsGainAdjustNeeded(struct ath_hal *, const GAIN_VALUES *); static int32_t ar5211AdjustGain(struct ath_hal *, GAIN_VALUES *); static void ar5211SetOperatingMode(struct ath_hal *, int opmode); /* * Places the device in and out of reset and then places sane * values in the registers based on EEPROM config, initialization * vectors (as determined by the mode), and station configuration * * bChannelChange is used to preserve DMA/PCU registers across * a HW Reset during channel change. */ HAL_BOOL ar5211Reset(struct ath_hal *ah, HAL_OPMODE opmode, struct ieee80211_channel *chan, HAL_BOOL bChannelChange, HAL_RESET_TYPE resetType, HAL_STATUS *status) { uint32_t softLedCfg, softLedState; #define N(a) (sizeof (a) /sizeof (a[0])) #define FAIL(_code) do { ecode = _code; goto bad; } while (0) struct ath_hal_5211 *ahp = AH5211(ah); HAL_CHANNEL_INTERNAL *ichan; uint32_t i, ledstate; HAL_STATUS ecode; int q; uint32_t data, synthDelay; uint32_t macStaId1; uint16_t modesIndex = 0, freqIndex = 0; uint32_t saveFrameSeqCount[AR_NUM_DCU]; uint32_t saveTsfLow = 0, saveTsfHigh = 0; uint32_t saveDefAntenna; HALDEBUG(ah, HAL_DEBUG_RESET, "%s: opmode %u channel %u/0x%x %s channel\n", __func__, opmode, chan->ic_freq, chan->ic_flags, bChannelChange ? "change" : "same"); OS_MARK(ah, AH_MARK_RESET, bChannelChange); /* * Map public channel to private. */ ichan = ath_hal_checkchannel(ah, chan); if (ichan == AH_NULL) FAIL(HAL_EINVAL); switch (opmode) { case HAL_M_STA: case HAL_M_IBSS: case HAL_M_HOSTAP: case HAL_M_MONITOR: break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid operating mode %u\n", __func__, opmode); FAIL(HAL_EINVAL); break; } HALASSERT(AH_PRIVATE(ah)->ah_eeversion >= AR_EEPROM_VER3); /* Preserve certain DMA hardware registers on a channel change */ if (bChannelChange) { /* * Need to save/restore the TSF because of an issue * that accelerates the TSF during a chip reset. * * We could use system timer routines to more * accurately restore the TSF, but * 1. Timer routines on certain platforms are * not accurate enough (e.g. 1 ms resolution). * 2. It would still not be accurate. * * The most important aspect of this workaround, * is that, after reset, the TSF is behind * other STAs TSFs. This will allow the STA to * properly resynchronize its TSF in adhoc mode. */ saveTsfLow = OS_REG_READ(ah, AR_TSF_L32); saveTsfHigh = OS_REG_READ(ah, AR_TSF_U32); /* Read frame sequence count */ if (AH_PRIVATE(ah)->ah_macVersion >= AR_SREV_VERSION_OAHU) { saveFrameSeqCount[0] = OS_REG_READ(ah, AR_D0_SEQNUM); } else { for (i = 0; i < AR_NUM_DCU; i++) saveFrameSeqCount[i] = OS_REG_READ(ah, AR_DSEQNUM(i)); } if (!IEEE80211_IS_CHAN_DFS(chan)) chan->ic_state &= ~IEEE80211_CHANSTATE_CWINT; } /* * Preserve the antenna on a channel change */ saveDefAntenna = OS_REG_READ(ah, AR_DEF_ANTENNA); if (saveDefAntenna == 0) saveDefAntenna = 1; /* Save hardware flag before chip reset clears the register */ macStaId1 = OS_REG_READ(ah, AR_STA_ID1) & AR_STA_ID1_BASE_RATE_11B; /* Save led state from pci config register */ ledstate = OS_REG_READ(ah, AR_PCICFG) & (AR_PCICFG_LEDCTL | AR_PCICFG_LEDMODE | AR_PCICFG_LEDBLINK | AR_PCICFG_LEDSLOW); softLedCfg = OS_REG_READ(ah, AR_GPIOCR); softLedState = OS_REG_READ(ah, AR_GPIODO); if (!ar5211ChipReset(ah, chan)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: chip reset failed\n", __func__); FAIL(HAL_EIO); } /* Setup the indices for the next set of register array writes */ if (IEEE80211_IS_CHAN_5GHZ(chan)) { freqIndex = 1; if (IEEE80211_IS_CHAN_TURBO(chan)) modesIndex = 2; else if (IEEE80211_IS_CHAN_A(chan)) modesIndex = 1; else { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel %u/0x%x\n", __func__, chan->ic_freq, chan->ic_flags); FAIL(HAL_EINVAL); } } else { freqIndex = 2; if (IEEE80211_IS_CHAN_B(chan)) modesIndex = 3; else if (IEEE80211_IS_CHAN_PUREG(chan)) modesIndex = 4; else { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel %u/0x%x\n", __func__, chan->ic_freq, chan->ic_flags); FAIL(HAL_EINVAL); } } /* Set correct Baseband to analog shift setting to access analog chips. */ if (AH_PRIVATE(ah)->ah_macVersion >= AR_SREV_VERSION_OAHU) { OS_REG_WRITE(ah, AR_PHY_BASE, 0x00000007); } else { OS_REG_WRITE(ah, AR_PHY_BASE, 0x00000047); } /* Write parameters specific to AR5211 */ if (AH_PRIVATE(ah)->ah_macVersion >= AR_SREV_VERSION_OAHU) { if (IEEE80211_IS_CHAN_2GHZ(chan) && AH_PRIVATE(ah)->ah_eeversion >= AR_EEPROM_VER3_1) { HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; uint32_t ob2GHz, db2GHz; if (IEEE80211_IS_CHAN_CCK(chan)) { ob2GHz = ee->ee_ob2GHz[0]; db2GHz = ee->ee_db2GHz[0]; } else { ob2GHz = ee->ee_ob2GHz[1]; db2GHz = ee->ee_db2GHz[1]; } ob2GHz = ath_hal_reverseBits(ob2GHz, 3); db2GHz = ath_hal_reverseBits(db2GHz, 3); ar5211Mode2_4[25][freqIndex] = (ar5211Mode2_4[25][freqIndex] & ~0xC0) | ((ob2GHz << 6) & 0xC0); ar5211Mode2_4[26][freqIndex] = (ar5211Mode2_4[26][freqIndex] & ~0x0F) | (((ob2GHz >> 2) & 0x1) | ((db2GHz << 1) & 0x0E)); } for (i = 0; i < N(ar5211Mode2_4); i++) OS_REG_WRITE(ah, ar5211Mode2_4[i][0], ar5211Mode2_4[i][freqIndex]); } /* Write the analog registers 6 and 7 before other config */ ar5211SetRf6and7(ah, chan); /* Write registers that vary across all modes */ for (i = 0; i < N(ar5211Modes); i++) OS_REG_WRITE(ah, ar5211Modes[i][0], ar5211Modes[i][modesIndex]); /* Write RFGain Parameters that differ between 2.4 and 5 GHz */ for (i = 0; i < N(ar5211BB_RfGain); i++) OS_REG_WRITE(ah, ar5211BB_RfGain[i][0], ar5211BB_RfGain[i][freqIndex]); /* Write Common Array Parameters */ for (i = 0; i < N(ar5211Common); i++) { uint32_t reg = ar5211Common[i][0]; /* On channel change, don't reset the PCU registers */ if (!(bChannelChange && (0x8000 <= reg && reg < 0x9000))) OS_REG_WRITE(ah, reg, ar5211Common[i][1]); } /* Fix pre-AR5211 register values, this includes AR5311s. */ if (AH_PRIVATE(ah)->ah_macVersion < AR_SREV_VERSION_OAHU) { /* * The TX and RX latency values have changed locations * within the USEC register in AR5211. Since they're * set via the .ini, for both AR5211 and AR5311, they * are written properly here for AR5311. */ data = OS_REG_READ(ah, AR_USEC); /* Must be 0 for proper write in AR5311 */ HALASSERT((data & 0x00700000) == 0); OS_REG_WRITE(ah, AR_USEC, (data & (AR_USEC_M | AR_USEC_32_M | AR5311_USEC_TX_LAT_M)) | ((29 << AR5311_USEC_RX_LAT_S) & AR5311_USEC_RX_LAT_M)); /* The following registers exist only on AR5311. */ OS_REG_WRITE(ah, AR5311_QDCLKGATE, 0); /* Set proper ADC & DAC delays for AR5311. */ OS_REG_WRITE(ah, 0x00009878, 0x00000008); /* Enable the PCU FIFO corruption ECO on AR5311. */ OS_REG_WRITE(ah, AR_DIAG_SW, OS_REG_READ(ah, AR_DIAG_SW) | AR5311_DIAG_SW_USE_ECO); } /* Restore certain DMA hardware registers on a channel change */ if (bChannelChange) { /* Restore TSF */ OS_REG_WRITE(ah, AR_TSF_L32, saveTsfLow); OS_REG_WRITE(ah, AR_TSF_U32, saveTsfHigh); if (AH_PRIVATE(ah)->ah_macVersion >= AR_SREV_VERSION_OAHU) { OS_REG_WRITE(ah, AR_D0_SEQNUM, saveFrameSeqCount[0]); } else { for (i = 0; i < AR_NUM_DCU; i++) OS_REG_WRITE(ah, AR_DSEQNUM(i), saveFrameSeqCount[i]); } } OS_REG_WRITE(ah, AR_STA_ID0, LE_READ_4(ahp->ah_macaddr)); OS_REG_WRITE(ah, AR_STA_ID1, LE_READ_2(ahp->ah_macaddr + 4) | macStaId1 ); ar5211SetOperatingMode(ah, opmode); /* Restore previous led state */ OS_REG_WRITE(ah, AR_PCICFG, OS_REG_READ(ah, AR_PCICFG) | ledstate); OS_REG_WRITE(ah, AR_GPIOCR, softLedCfg); OS_REG_WRITE(ah, AR_GPIODO, softLedState); /* Restore previous antenna */ OS_REG_WRITE(ah, AR_DEF_ANTENNA, saveDefAntenna); OS_REG_WRITE(ah, AR_BSS_ID0, LE_READ_4(ahp->ah_bssid)); OS_REG_WRITE(ah, AR_BSS_ID1, LE_READ_2(ahp->ah_bssid + 4)); /* Restore bmiss rssi & count thresholds */ OS_REG_WRITE(ah, AR_RSSI_THR, ahp->ah_rssiThr); OS_REG_WRITE(ah, AR_ISR, ~0); /* cleared on write */ /* * for pre-Production Oahu only. * Disable clock gating in all DMA blocks. Helps when using * 11B and AES but results in higher power consumption. */ if (AH_PRIVATE(ah)->ah_macVersion == AR_SREV_VERSION_OAHU && AH_PRIVATE(ah)->ah_macRev < AR_SREV_OAHU_PROD) { OS_REG_WRITE(ah, AR_CFG, OS_REG_READ(ah, AR_CFG) | AR_CFG_CLK_GATE_DIS); } /* Setup the transmit power values. */ if (!ar5211SetTransmitPower(ah, chan)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: error init'ing transmit power\n", __func__); FAIL(HAL_EIO); } /* * Configurable OFDM spoofing for 11n compatibility; used * only when operating in station mode. */ if (opmode != HAL_M_HOSTAP && (AH_PRIVATE(ah)->ah_11nCompat & HAL_DIAG_11N_SERVICES) != 0) { /* NB: override the .ini setting */ OS_REG_RMW_FIELD(ah, AR_PHY_FRAME_CTL, AR_PHY_FRAME_CTL_ERR_SERV, MS(AH_PRIVATE(ah)->ah_11nCompat, HAL_DIAG_11N_SERVICES)&1); } /* Setup board specific options for EEPROM version 3 */ ar5211SetBoardValues(ah, chan); if (!ar5211SetChannel(ah, chan)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unable to set channel\n", __func__); FAIL(HAL_EIO); } /* Activate the PHY */ if (AH_PRIVATE(ah)->ah_devid == AR5211_FPGA11B && IEEE80211_IS_CHAN_2GHZ(chan)) OS_REG_WRITE(ah, 0xd808, 0x502); /* required for FPGA */ OS_REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); /* * Wait for the frequency synth to settle (synth goes on * via AR_PHY_ACTIVE_EN). Read the phy active delay register. * Value is in 100ns increments. */ data = OS_REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_M; if (IEEE80211_IS_CHAN_CCK(chan)) { synthDelay = (4 * data) / 22; } else { synthDelay = data / 10; } /* * There is an issue if the AP starts the calibration before * the baseband timeout completes. This could result in the * rxclear false triggering. Add an extra delay to ensure this * this does not happen. */ OS_DELAY(synthDelay + DELAY_BASE_ACTIVATE); /* Calibrate the AGC and wait for completion. */ OS_REG_WRITE(ah, AR_PHY_AGC_CONTROL, OS_REG_READ(ah, AR_PHY_AGC_CONTROL) | AR_PHY_AGC_CONTROL_CAL); (void) ath_hal_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0); /* Perform noise floor and set status */ if (!ar5211CalNoiseFloor(ah, chan)) { if (!IEEE80211_IS_CHAN_CCK(chan)) chan->ic_state |= IEEE80211_CHANSTATE_CWINT; HALDEBUG(ah, HAL_DEBUG_ANY, "%s: noise floor calibration failed\n", __func__); FAIL(HAL_EIO); } /* Start IQ calibration w/ 2^(INIT_IQCAL_LOG_COUNT_MAX+1) samples */ if (ahp->ah_calibrationTime != 0) { OS_REG_WRITE(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_DO_IQCAL | (INIT_IQCAL_LOG_COUNT_MAX << AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX_S)); ahp->ah_bIQCalibration = AH_TRUE; } /* set 1:1 QCU to DCU mapping for all queues */ for (q = 0; q < AR_NUM_DCU; q++) OS_REG_WRITE(ah, AR_DQCUMASK(q), 1<ah_maskReg = INIT_INTERRUPT_MASK; /* Enable bus error interrupts */ OS_REG_WRITE(ah, AR_IMR_S2, OS_REG_READ(ah, AR_IMR_S2) | AR_IMR_S2_MCABT | AR_IMR_S2_SSERR | AR_IMR_S2_DPERR); /* Enable interrupts specific to AP */ if (opmode == HAL_M_HOSTAP) { OS_REG_WRITE(ah, AR_IMR, OS_REG_READ(ah, AR_IMR) | AR_IMR_MIB); ahp->ah_maskReg |= AR_IMR_MIB; } if (AH_PRIVATE(ah)->ah_rfkillEnabled) ar5211EnableRfKill(ah); /* * Writing to AR_BEACON will start timers. Hence it should * be the last register to be written. Do not reset tsf, do * not enable beacons at this point, but preserve other values * like beaconInterval. */ OS_REG_WRITE(ah, AR_BEACON, (OS_REG_READ(ah, AR_BEACON) &~ (AR_BEACON_EN | AR_BEACON_RESET_TSF))); /* Restore user-specified slot time and timeouts */ if (ahp->ah_sifstime != (u_int) -1) ar5211SetSifsTime(ah, ahp->ah_sifstime); if (ahp->ah_slottime != (u_int) -1) ar5211SetSlotTime(ah, ahp->ah_slottime); if (ahp->ah_acktimeout != (u_int) -1) ar5211SetAckTimeout(ah, ahp->ah_acktimeout); if (ahp->ah_ctstimeout != (u_int) -1) ar5211SetCTSTimeout(ah, ahp->ah_ctstimeout); if (AH_PRIVATE(ah)->ah_diagreg != 0) OS_REG_WRITE(ah, AR_DIAG_SW, AH_PRIVATE(ah)->ah_diagreg); AH_PRIVATE(ah)->ah_opmode = opmode; /* record operating mode */ HALDEBUG(ah, HAL_DEBUG_RESET, "%s: done\n", __func__); return AH_TRUE; bad: if (status != AH_NULL) *status = ecode; return AH_FALSE; #undef FAIL #undef N } /* * Places the PHY and Radio chips into reset. A full reset * must be called to leave this state. The PCI/MAC/PCU are * not placed into reset as we must receive interrupt to * re-enable the hardware. */ HAL_BOOL ar5211PhyDisable(struct ath_hal *ah) { return ar5211SetResetReg(ah, AR_RC_BB); } /* * Places all of hardware into reset */ HAL_BOOL ar5211Disable(struct ath_hal *ah) { if (!ar5211SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) return AH_FALSE; /* * Reset the HW - PCI must be reset after the rest of the * device has been reset. */ if (!ar5211SetResetReg(ah, AR_RC_MAC | AR_RC_BB | AR_RC_PCI)) return AH_FALSE; OS_DELAY(2100); /* 8245 @ 96Mhz hangs with 2000us. */ return AH_TRUE; } /* * Places the hardware into reset and then pulls it out of reset * * Only write the PLL if we're changing to or from CCK mode * * Attach calls with channelFlags = 0, as the coldreset should have * us in the correct mode and we cannot check the hwchannel flags. */ HAL_BOOL ar5211ChipReset(struct ath_hal *ah, const struct ieee80211_channel *chan) { if (!ar5211SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) return AH_FALSE; /* NB: called from attach with chan null */ if (chan != AH_NULL) { /* Set CCK and Turbo modes correctly */ OS_REG_WRITE(ah, AR_PHY_TURBO, IEEE80211_IS_CHAN_TURBO(chan) ? AR_PHY_FC_TURBO_MODE | AR_PHY_FC_TURBO_SHORT : 0); if (IEEE80211_IS_CHAN_B(chan)) { OS_REG_WRITE(ah, AR5211_PHY_MODE, AR5211_PHY_MODE_CCK | AR5211_PHY_MODE_RF2GHZ); OS_REG_WRITE(ah, AR_PHY_PLL_CTL, AR_PHY_PLL_CTL_44); /* Wait for the PLL to settle */ OS_DELAY(DELAY_PLL_SETTLE); } else if (AH_PRIVATE(ah)->ah_devid == AR5211_DEVID) { OS_REG_WRITE(ah, AR_PHY_PLL_CTL, AR_PHY_PLL_CTL_40); OS_DELAY(DELAY_PLL_SETTLE); OS_REG_WRITE(ah, AR5211_PHY_MODE, AR5211_PHY_MODE_OFDM | (IEEE80211_IS_CHAN_2GHZ(chan) ? AR5211_PHY_MODE_RF2GHZ : AR5211_PHY_MODE_RF5GHZ)); } } /* * Reset the HW - PCI must be reset after the rest of the * device has been reset */ if (!ar5211SetResetReg(ah, AR_RC_MAC | AR_RC_BB | AR_RC_PCI)) return AH_FALSE; OS_DELAY(2100); /* 8245 @ 96Mhz hangs with 2000us. */ /* Bring out of sleep mode (AGAIN) */ if (!ar5211SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) return AH_FALSE; /* Clear warm reset register */ return ar5211SetResetReg(ah, 0); } /* * Recalibrate the lower PHY chips to account for temperature/environment * changes. */ HAL_BOOL ar5211PerCalibrationN(struct ath_hal *ah, struct ieee80211_channel *chan, u_int chainMask, HAL_BOOL longCal, HAL_BOOL *isCalDone) { struct ath_hal_5211 *ahp = AH5211(ah); HAL_CHANNEL_INTERNAL *ichan; int32_t qCoff, qCoffDenom; uint32_t data; int32_t iqCorrMeas; int32_t iCoff, iCoffDenom; uint32_t powerMeasQ, powerMeasI; ichan = ath_hal_checkchannel(ah, chan); if (ichan == AH_NULL) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel %u/0x%x; no mapping\n", __func__, chan->ic_freq, chan->ic_flags); return AH_FALSE; } /* IQ calibration in progress. Check to see if it has finished. */ if (ahp->ah_bIQCalibration && !(OS_REG_READ(ah, AR_PHY_TIMING_CTRL4) & AR_PHY_TIMING_CTRL4_DO_IQCAL)) { /* IQ Calibration has finished. */ ahp->ah_bIQCalibration = AH_FALSE; /* Read calibration results. */ powerMeasI = OS_REG_READ(ah, AR_PHY_IQCAL_RES_PWR_MEAS_I); powerMeasQ = OS_REG_READ(ah, AR_PHY_IQCAL_RES_PWR_MEAS_Q); iqCorrMeas = OS_REG_READ(ah, AR_PHY_IQCAL_RES_IQ_CORR_MEAS); /* * Prescale these values to remove 64-bit operation requirement at the loss * of a little precision. */ iCoffDenom = (powerMeasI / 2 + powerMeasQ / 2) / 128; qCoffDenom = powerMeasQ / 64; /* Protect against divide-by-0. */ if (iCoffDenom != 0 && qCoffDenom != 0) { iCoff = (-iqCorrMeas) / iCoffDenom; /* IQCORR_Q_I_COFF is a signed 6 bit number */ iCoff = iCoff & 0x3f; qCoff = ((int32_t)powerMeasI / qCoffDenom) - 64; /* IQCORR_Q_Q_COFF is a signed 5 bit number */ qCoff = qCoff & 0x1f; HALDEBUG(ah, HAL_DEBUG_PERCAL, "powerMeasI = 0x%08x\n", powerMeasI); HALDEBUG(ah, HAL_DEBUG_PERCAL, "powerMeasQ = 0x%08x\n", powerMeasQ); HALDEBUG(ah, HAL_DEBUG_PERCAL, "iqCorrMeas = 0x%08x\n", iqCorrMeas); HALDEBUG(ah, HAL_DEBUG_PERCAL, "iCoff = %d\n", iCoff); HALDEBUG(ah, HAL_DEBUG_PERCAL, "qCoff = %d\n", qCoff); /* Write IQ */ data = OS_REG_READ(ah, AR_PHY_TIMING_CTRL4) | AR_PHY_TIMING_CTRL4_IQCORR_ENABLE | (((uint32_t)iCoff) << AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF_S) | ((uint32_t)qCoff); OS_REG_WRITE(ah, AR_PHY_TIMING_CTRL4, data); } } *isCalDone = !ahp->ah_bIQCalibration; if (longCal) { /* Perform noise floor and set status */ if (!ar5211IsNfGood(ah, chan)) { /* report up and clear internal state */ chan->ic_state |= IEEE80211_CHANSTATE_CWINT; return AH_FALSE; } if (!ar5211CalNoiseFloor(ah, chan)) { /* * Delay 5ms before retrying the noise floor * just to make sure, as we are in an error * condition here. */ OS_DELAY(5000); if (!ar5211CalNoiseFloor(ah, chan)) { if (!IEEE80211_IS_CHAN_CCK(chan)) chan->ic_state |= IEEE80211_CHANSTATE_CWINT; return AH_FALSE; } } ar5211RequestRfgain(ah); } return AH_TRUE; } HAL_BOOL ar5211PerCalibration(struct ath_hal *ah, struct ieee80211_channel *chan, HAL_BOOL *isIQdone) { return ar5211PerCalibrationN(ah, chan, 0x1, AH_TRUE, isIQdone); } HAL_BOOL ar5211ResetCalValid(struct ath_hal *ah, const struct ieee80211_channel *chan) { /* XXX */ return AH_TRUE; } /* * Writes the given reset bit mask into the reset register */ static HAL_BOOL ar5211SetResetReg(struct ath_hal *ah, uint32_t resetMask) { uint32_t mask = resetMask ? resetMask : ~0; HAL_BOOL rt; (void) OS_REG_READ(ah, AR_RXDP);/* flush any pending MMR writes */ OS_REG_WRITE(ah, AR_RC, resetMask); /* need to wait at least 128 clocks when reseting PCI before read */ OS_DELAY(15); resetMask &= AR_RC_MAC | AR_RC_BB; mask &= AR_RC_MAC | AR_RC_BB; rt = ath_hal_wait(ah, AR_RC, mask, resetMask); if ((resetMask & AR_RC_MAC) == 0) { if (isBigEndian()) { /* * Set CFG, little-endian for descriptor accesses. */ mask = INIT_CONFIG_STATUS | AR_CFG_SWTD | AR_CFG_SWRD; OS_REG_WRITE(ah, AR_CFG, mask); } else OS_REG_WRITE(ah, AR_CFG, INIT_CONFIG_STATUS); } return rt; } /* * Takes the MHz channel value and sets the Channel value * * ASSUMES: Writes enabled to analog bus before AGC is active * or by disabling the AGC. */ static HAL_BOOL ar5211SetChannel(struct ath_hal *ah, const struct ieee80211_channel *chan) { uint32_t refClk, reg32, data2111; int16_t chan5111, chanIEEE; chanIEEE = chan->ic_ieee; if (IEEE80211_IS_CHAN_2GHZ(chan)) { const CHAN_INFO_2GHZ* ci = &chan2GHzData[chanIEEE + CI_2GHZ_INDEX_CORRECTION]; data2111 = ((ath_hal_reverseBits(ci->channelSelect, 8) & 0xff) << 5) | (ci->refClkSel << 4); chan5111 = ci->channel5111; } else { data2111 = 0; chan5111 = chanIEEE; } /* Rest of the code is common for 5 GHz and 2.4 GHz. */ if (chan5111 >= 145 || (chan5111 & 0x1)) { reg32 = ath_hal_reverseBits(chan5111 - 24, 8) & 0xFF; refClk = 1; } else { reg32 = ath_hal_reverseBits(((chan5111 - 24) / 2), 8) & 0xFF; refClk = 0; } reg32 = (reg32 << 2) | (refClk << 1) | (1 << 10) | 0x1; OS_REG_WRITE(ah, AR_PHY(0x27), ((data2111 & 0xff) << 8) | (reg32 & 0xff)); reg32 >>= 8; OS_REG_WRITE(ah, AR_PHY(0x34), (data2111 & 0xff00) | (reg32 & 0xff)); AH_PRIVATE(ah)->ah_curchan = chan; return AH_TRUE; } static int16_t ar5211GetNoiseFloor(struct ath_hal *ah) { int16_t nf; nf = (OS_REG_READ(ah, AR_PHY(25)) >> 19) & 0x1ff; if (nf & 0x100) nf = 0 - ((nf ^ 0x1ff) + 1); return nf; } /* * Peform the noisefloor calibration for the length of time set * in runTime (valid values 1 to 7) * * Returns: The NF value at the end of the given time (or 0 for failure) */ int16_t ar5211RunNoiseFloor(struct ath_hal *ah, uint8_t runTime, int16_t startingNF) { int i, searchTime; HALASSERT(runTime <= 7); /* Setup noise floor run time and starting value */ OS_REG_WRITE(ah, AR_PHY(25), (OS_REG_READ(ah, AR_PHY(25)) & ~0xFFF) | ((runTime << 9) & 0xE00) | (startingNF & 0x1FF)); /* Calibrate the noise floor */ OS_REG_WRITE(ah, AR_PHY_AGC_CONTROL, OS_REG_READ(ah, AR_PHY_AGC_CONTROL) | AR_PHY_AGC_CONTROL_NF); /* Compute the required amount of searchTime needed to finish NF */ if (runTime == 0) { /* 8 search windows * 6.4us each */ searchTime = 8 * 7; } else { /* 512 * runtime search windows * 6.4us each */ searchTime = (runTime * 512) * 7; } /* * Do not read noise floor until it has been updated * * As a guesstimate - we may only get 1/60th the time on * the air to see search windows in a heavily congested * network (40 us every 2400 us of time) */ for (i = 0; i < 60; i++) { if ((OS_REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) == 0) break; OS_DELAY(searchTime); } if (i >= 60) { HALDEBUG(ah, HAL_DEBUG_NFCAL, "NF with runTime %d failed to end on channel %d\n", runTime, AH_PRIVATE(ah)->ah_curchan->ic_freq); HALDEBUG(ah, HAL_DEBUG_NFCAL, " PHY NF Reg state: 0x%x\n", OS_REG_READ(ah, AR_PHY_AGC_CONTROL)); HALDEBUG(ah, HAL_DEBUG_NFCAL, " PHY Active Reg state: 0x%x\n", OS_REG_READ(ah, AR_PHY_ACTIVE)); return 0; } return ar5211GetNoiseFloor(ah); } static HAL_BOOL getNoiseFloorThresh(struct ath_hal *ah, const struct ieee80211_channel *chan, int16_t *nft) { HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; switch (chan->ic_flags & IEEE80211_CHAN_ALLFULL) { case IEEE80211_CHAN_A: *nft = ee->ee_noiseFloorThresh[0]; break; case IEEE80211_CHAN_B: *nft = ee->ee_noiseFloorThresh[1]; break; case IEEE80211_CHAN_PUREG: *nft = ee->ee_noiseFloorThresh[2]; break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel flags 0x%x\n", __func__, chan->ic_flags); return AH_FALSE; } return AH_TRUE; } /* - * Read the NF and check it against the noise floor threshhold + * Read the NF and check it against the noise floor threshold * * Returns: TRUE if the NF is good */ static HAL_BOOL ar5211IsNfGood(struct ath_hal *ah, struct ieee80211_channel *chan) { HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); int16_t nf, nfThresh; if (!getNoiseFloorThresh(ah, chan, &nfThresh)) return AH_FALSE; if (OS_REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: NF did not complete in calibration window\n", __func__); } nf = ar5211GetNoiseFloor(ah); if (nf > nfThresh) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: noise floor failed; detected %u, threshold %u\n", __func__, nf, nfThresh); /* * NB: Don't discriminate 2.4 vs 5Ghz, if this * happens it indicates a problem regardless * of the band. */ chan->ic_state |= IEEE80211_CHANSTATE_CWINT; } ichan->rawNoiseFloor = nf; return (nf <= nfThresh); } /* * Peform the noisefloor calibration and check for any constant channel * interference. * * NOTE: preAR5211 have a lengthy carrier wave detection process - hence * it is if'ed for MKK regulatory domain only. * * Returns: TRUE for a successful noise floor calibration; else FALSE */ HAL_BOOL ar5211CalNoiseFloor(struct ath_hal *ah, const struct ieee80211_channel *chan) { #define N(a) (sizeof (a) / sizeof (a[0])) /* Check for Carrier Wave interference in MKK regulatory zone */ if (AH_PRIVATE(ah)->ah_macVersion < AR_SREV_VERSION_OAHU && (chan->ic_flags & CHANNEL_NFCREQUIRED)) { static const uint8_t runtime[3] = { 0, 2, 7 }; HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); int16_t nf, nfThresh; int i; if (!getNoiseFloorThresh(ah, chan, &nfThresh)) return AH_FALSE; /* * Run a quick noise floor that will hopefully * complete (decrease delay time). */ for (i = 0; i < N(runtime); i++) { nf = ar5211RunNoiseFloor(ah, runtime[i], 0); if (nf > nfThresh) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: run failed with %u > threshold %u " "(runtime %u)\n", __func__, nf, nfThresh, runtime[i]); ichan->rawNoiseFloor = 0; } else ichan->rawNoiseFloor = nf; } return (i <= N(runtime)); } else { /* Calibrate the noise floor */ OS_REG_WRITE(ah, AR_PHY_AGC_CONTROL, OS_REG_READ(ah, AR_PHY_AGC_CONTROL) | AR_PHY_AGC_CONTROL_NF); } return AH_TRUE; #undef N } /* * Adjust NF based on statistical values for 5GHz frequencies. */ int16_t ar5211GetNfAdjust(struct ath_hal *ah, const HAL_CHANNEL_INTERNAL *c) { static const struct { uint16_t freqLow; int16_t adjust; } adjust5111[] = { { 5790, 11 }, /* NB: ordered high -> low */ { 5730, 10 }, { 5690, 9 }, { 5660, 8 }, { 5610, 7 }, { 5530, 5 }, { 5450, 4 }, { 5379, 2 }, { 5209, 0 }, /* XXX? bogus but doesn't matter */ { 0, 1 }, }; int i; for (i = 0; c->channel <= adjust5111[i].freqLow; i++) ; /* NB: placeholder for 5111's less severe requirement */ return adjust5111[i].adjust / 3; } /* * Reads EEPROM header info from device structure and programs * analog registers 6 and 7 * * REQUIRES: Access to the analog device */ static HAL_BOOL ar5211SetRf6and7(struct ath_hal *ah, const struct ieee80211_channel *chan) { #define N(a) (sizeof (a) / sizeof (a[0])) uint16_t freq = ath_hal_gethwchannel(ah, chan); HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; struct ath_hal_5211 *ahp = AH5211(ah); uint16_t rfXpdGain, rfPloSel, rfPwdXpd; uint16_t tempOB, tempDB; uint16_t freqIndex; int i; freqIndex = IEEE80211_IS_CHAN_2GHZ(chan) ? 2 : 1; /* * TODO: This array mode correspondes with the index used * during the read. * For readability, this should be changed to an enum or #define */ switch (chan->ic_flags & IEEE80211_CHAN_ALLFULL) { case IEEE80211_CHAN_A: if (freq > 4000 && freq < 5260) { tempOB = ee->ee_ob1; tempDB = ee->ee_db1; } else if (freq >= 5260 && freq < 5500) { tempOB = ee->ee_ob2; tempDB = ee->ee_db2; } else if (freq >= 5500 && freq < 5725) { tempOB = ee->ee_ob3; tempDB = ee->ee_db3; } else if (freq >= 5725) { tempOB = ee->ee_ob4; tempDB = ee->ee_db4; } else { /* XXX panic?? */ tempOB = tempDB = 0; } rfXpdGain = ee->ee_xgain[0]; rfPloSel = ee->ee_xpd[0]; rfPwdXpd = !ee->ee_xpd[0]; ar5211Rf6n7[5][freqIndex] = (ar5211Rf6n7[5][freqIndex] & ~0x10000000) | (ee->ee_cornerCal.pd84<< 28); ar5211Rf6n7[6][freqIndex] = (ar5211Rf6n7[6][freqIndex] & ~0x04000000) | (ee->ee_cornerCal.pd90 << 26); ar5211Rf6n7[21][freqIndex] = (ar5211Rf6n7[21][freqIndex] & ~0x08) | (ee->ee_cornerCal.gSel << 3); break; case IEEE80211_CHAN_B: tempOB = ee->ee_obFor24; tempDB = ee->ee_dbFor24; rfXpdGain = ee->ee_xgain[1]; rfPloSel = ee->ee_xpd[1]; rfPwdXpd = !ee->ee_xpd[1]; break; case IEEE80211_CHAN_PUREG: tempOB = ee->ee_obFor24g; tempDB = ee->ee_dbFor24g; rfXpdGain = ee->ee_xgain[2]; rfPloSel = ee->ee_xpd[2]; rfPwdXpd = !ee->ee_xpd[2]; break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel flags 0x%x\n", __func__, chan->ic_flags); return AH_FALSE; } HALASSERT(1 <= tempOB && tempOB <= 5); HALASSERT(1 <= tempDB && tempDB <= 5); /* Set rfXpdGain and rfPwdXpd */ ar5211Rf6n7[11][freqIndex] = (ar5211Rf6n7[11][freqIndex] & ~0xC0) | (((ath_hal_reverseBits(rfXpdGain, 4) << 7) | (rfPwdXpd << 6)) & 0xC0); ar5211Rf6n7[12][freqIndex] = (ar5211Rf6n7[12][freqIndex] & ~0x07) | ((ath_hal_reverseBits(rfXpdGain, 4) >> 1) & 0x07); /* Set OB */ ar5211Rf6n7[12][freqIndex] = (ar5211Rf6n7[12][freqIndex] & ~0x80) | ((ath_hal_reverseBits(tempOB, 3) << 7) & 0x80); ar5211Rf6n7[13][freqIndex] = (ar5211Rf6n7[13][freqIndex] & ~0x03) | ((ath_hal_reverseBits(tempOB, 3) >> 1) & 0x03); /* Set DB */ ar5211Rf6n7[13][freqIndex] = (ar5211Rf6n7[13][freqIndex] & ~0x1C) | ((ath_hal_reverseBits(tempDB, 3) << 2) & 0x1C); /* Set rfPloSel */ ar5211Rf6n7[17][freqIndex] = (ar5211Rf6n7[17][freqIndex] & ~0x08) | ((rfPloSel << 3) & 0x08); /* Write the Rf registers 6 & 7 */ for (i = 0; i < N(ar5211Rf6n7); i++) OS_REG_WRITE(ah, ar5211Rf6n7[i][0], ar5211Rf6n7[i][freqIndex]); /* Now that we have reprogrammed rfgain value, clear the flag. */ ahp->ah_rfgainState = RFGAIN_INACTIVE; return AH_TRUE; #undef N } HAL_BOOL ar5211SetAntennaSwitchInternal(struct ath_hal *ah, HAL_ANT_SETTING settings, const struct ieee80211_channel *chan) { #define ANT_SWITCH_TABLE1 0x9960 #define ANT_SWITCH_TABLE2 0x9964 HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; struct ath_hal_5211 *ahp = AH5211(ah); uint32_t antSwitchA, antSwitchB; int ix; switch (chan->ic_flags & IEEE80211_CHAN_ALLFULL) { case IEEE80211_CHAN_A: ix = 0; break; case IEEE80211_CHAN_B: ix = 1; break; case IEEE80211_CHAN_PUREG: ix = 2; break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel flags 0x%x\n", __func__, chan->ic_flags); return AH_FALSE; } antSwitchA = ee->ee_antennaControl[1][ix] | (ee->ee_antennaControl[2][ix] << 6) | (ee->ee_antennaControl[3][ix] << 12) | (ee->ee_antennaControl[4][ix] << 18) | (ee->ee_antennaControl[5][ix] << 24) ; antSwitchB = ee->ee_antennaControl[6][ix] | (ee->ee_antennaControl[7][ix] << 6) | (ee->ee_antennaControl[8][ix] << 12) | (ee->ee_antennaControl[9][ix] << 18) | (ee->ee_antennaControl[10][ix] << 24) ; /* * For fixed antenna, give the same setting for both switch banks */ switch (settings) { case HAL_ANT_FIXED_A: antSwitchB = antSwitchA; break; case HAL_ANT_FIXED_B: antSwitchA = antSwitchB; break; case HAL_ANT_VARIABLE: break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: bad antenna setting %u\n", __func__, settings); return AH_FALSE; } ahp->ah_diversityControl = settings; OS_REG_WRITE(ah, ANT_SWITCH_TABLE1, antSwitchA); OS_REG_WRITE(ah, ANT_SWITCH_TABLE2, antSwitchB); return AH_TRUE; #undef ANT_SWITCH_TABLE1 #undef ANT_SWITCH_TABLE2 } /* * Reads EEPROM header info and programs the device for correct operation * given the channel value */ static HAL_BOOL ar5211SetBoardValues(struct ath_hal *ah, const struct ieee80211_channel *chan) { HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; struct ath_hal_5211 *ahp = AH5211(ah); int arrayMode, falseDectectBackoff; switch (chan->ic_flags & IEEE80211_CHAN_ALLFULL) { case IEEE80211_CHAN_A: arrayMode = 0; OS_REG_RMW_FIELD(ah, AR_PHY_FRAME_CTL, AR_PHY_FRAME_CTL_TX_CLIP, ee->ee_cornerCal.clip); break; case IEEE80211_CHAN_B: arrayMode = 1; break; case IEEE80211_CHAN_PUREG: arrayMode = 2; break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel flags 0x%x\n", __func__, chan->ic_flags); return AH_FALSE; } /* Set the antenna register(s) correctly for the chip revision */ if (AH_PRIVATE(ah)->ah_macVersion < AR_SREV_VERSION_OAHU) { OS_REG_WRITE(ah, AR_PHY(68), (OS_REG_READ(ah, AR_PHY(68)) & 0xFFFFFFFC) | 0x3); } else { OS_REG_WRITE(ah, AR_PHY(68), (OS_REG_READ(ah, AR_PHY(68)) & 0xFFFFFC06) | (ee->ee_antennaControl[0][arrayMode] << 4) | 0x1); ar5211SetAntennaSwitchInternal(ah, ahp->ah_diversityControl, chan); /* Set the Noise Floor Thresh on ar5211 devices */ OS_REG_WRITE(ah, AR_PHY_BASE + (90 << 2), (ee->ee_noiseFloorThresh[arrayMode] & 0x1FF) | (1<<9)); } OS_REG_WRITE(ah, AR_PHY_BASE + (17 << 2), (OS_REG_READ(ah, AR_PHY_BASE + (17 << 2)) & 0xFFFFC07F) | ((ee->ee_switchSettling[arrayMode] << 7) & 0x3F80)); OS_REG_WRITE(ah, AR_PHY_BASE + (18 << 2), (OS_REG_READ(ah, AR_PHY_BASE + (18 << 2)) & 0xFFFC0FFF) | ((ee->ee_txrxAtten[arrayMode] << 12) & 0x3F000)); OS_REG_WRITE(ah, AR_PHY_BASE + (20 << 2), (OS_REG_READ(ah, AR_PHY_BASE + (20 << 2)) & 0xFFFF0000) | ((ee->ee_pgaDesiredSize[arrayMode] << 8) & 0xFF00) | (ee->ee_adcDesiredSize[arrayMode] & 0x00FF)); OS_REG_WRITE(ah, AR_PHY_BASE + (13 << 2), (ee->ee_txEndToXPAOff[arrayMode] << 24) | (ee->ee_txEndToXPAOff[arrayMode] << 16) | (ee->ee_txFrameToXPAOn[arrayMode] << 8) | ee->ee_txFrameToXPAOn[arrayMode]); OS_REG_WRITE(ah, AR_PHY_BASE + (10 << 2), (OS_REG_READ(ah, AR_PHY_BASE + (10 << 2)) & 0xFFFF00FF) | (ee->ee_txEndToXLNAOn[arrayMode] << 8)); OS_REG_WRITE(ah, AR_PHY_BASE + (25 << 2), (OS_REG_READ(ah, AR_PHY_BASE + (25 << 2)) & 0xFFF80FFF) | ((ee->ee_thresh62[arrayMode] << 12) & 0x7F000)); #define NO_FALSE_DETECT_BACKOFF 2 #define CB22_FALSE_DETECT_BACKOFF 6 /* * False detect backoff - suspected 32 MHz spur causes * false detects in OFDM, causing Tx Hangs. Decrease * weak signal sensitivity for this card. */ falseDectectBackoff = NO_FALSE_DETECT_BACKOFF; if (AH_PRIVATE(ah)->ah_eeversion < AR_EEPROM_VER3_3) { if (AH_PRIVATE(ah)->ah_subvendorid == 0x1022 && IEEE80211_IS_CHAN_OFDM(chan)) falseDectectBackoff += CB22_FALSE_DETECT_BACKOFF; } else { uint16_t freq = ath_hal_gethwchannel(ah, chan); uint32_t remainder = freq % 32; if (remainder && (remainder < 10 || remainder > 22)) falseDectectBackoff += ee->ee_falseDetectBackoff[arrayMode]; } OS_REG_WRITE(ah, 0x9924, (OS_REG_READ(ah, 0x9924) & 0xFFFFFF01) | ((falseDectectBackoff << 1) & 0xF7)); return AH_TRUE; #undef NO_FALSE_DETECT_BACKOFF #undef CB22_FALSE_DETECT_BACKOFF } /* * Set the limit on the overall output power. Used for dynamic * transmit power control and the like. * * NOTE: The power is passed in is in units of 0.5 dBm. */ HAL_BOOL ar5211SetTxPowerLimit(struct ath_hal *ah, uint32_t limit) { AH_PRIVATE(ah)->ah_powerLimit = AH_MIN(limit, MAX_RATE_POWER); OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE_MAX, limit); return AH_TRUE; } /* * Sets the transmit power in the baseband for the given * operating channel and mode. */ static HAL_BOOL ar5211SetTransmitPower(struct ath_hal *ah, const struct ieee80211_channel *chan) { uint16_t freq = ath_hal_gethwchannel(ah, chan); HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; TRGT_POWER_INFO *pi; RD_EDGES_POWER *rep; PCDACS_EEPROM eepromPcdacs; u_int nchan, cfgCtl; int i; /* setup the pcdac struct to point to the correct info, based on mode */ switch (chan->ic_flags & IEEE80211_CHAN_ALLFULL) { case IEEE80211_CHAN_A: eepromPcdacs.numChannels = ee->ee_numChannels11a; eepromPcdacs.pChannelList= ee->ee_channels11a; eepromPcdacs.pDataPerChannel = ee->ee_dataPerChannel11a; nchan = ee->ee_numTargetPwr_11a; pi = ee->ee_trgtPwr_11a; break; case IEEE80211_CHAN_PUREG: eepromPcdacs.numChannels = ee->ee_numChannels2_4; eepromPcdacs.pChannelList= ee->ee_channels11g; eepromPcdacs.pDataPerChannel = ee->ee_dataPerChannel11g; nchan = ee->ee_numTargetPwr_11g; pi = ee->ee_trgtPwr_11g; break; case IEEE80211_CHAN_B: eepromPcdacs.numChannels = ee->ee_numChannels2_4; eepromPcdacs.pChannelList= ee->ee_channels11b; eepromPcdacs.pDataPerChannel = ee->ee_dataPerChannel11b; nchan = ee->ee_numTargetPwr_11b; pi = ee->ee_trgtPwr_11b; break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel flags 0x%x\n", __func__, chan->ic_flags); return AH_FALSE; } ar5211SetPowerTable(ah, &eepromPcdacs, freq); rep = AH_NULL; /* Match CTL to EEPROM value */ cfgCtl = ath_hal_getctl(ah, chan); for (i = 0; i < ee->ee_numCtls; i++) if (ee->ee_ctl[i] != 0 && ee->ee_ctl[i] == cfgCtl) { rep = &ee->ee_rdEdgesPower[i * NUM_EDGES]; break; } ar5211SetRateTable(ah, rep, pi, nchan, chan); return AH_TRUE; } /* * Read the transmit power levels from the structures taken * from EEPROM. Interpolate read transmit power values for * this channel. Organize the transmit power values into a * table for writing into the hardware. */ void ar5211SetPowerTable(struct ath_hal *ah, PCDACS_EEPROM *pSrcStruct, uint16_t channel) { static FULL_PCDAC_STRUCT pcdacStruct; static uint16_t pcdacTable[PWR_TABLE_SIZE]; uint16_t i, j; uint16_t *pPcdacValues; int16_t *pScaledUpDbm; int16_t minScaledPwr; int16_t maxScaledPwr; int16_t pwr; uint16_t pcdacMin = 0; uint16_t pcdacMax = 63; uint16_t pcdacTableIndex; uint16_t scaledPcdac; uint32_t addr; uint32_t temp32; OS_MEMZERO(&pcdacStruct, sizeof(FULL_PCDAC_STRUCT)); OS_MEMZERO(pcdacTable, sizeof(uint16_t) * PWR_TABLE_SIZE); pPcdacValues = pcdacStruct.PcdacValues; pScaledUpDbm = pcdacStruct.PwrValues; /* Initialize the pcdacs to dBM structs pcdacs to be 1 to 63 */ for (i = PCDAC_START, j = 0; i <= PCDAC_STOP; i+= PCDAC_STEP, j++) pPcdacValues[j] = i; pcdacStruct.numPcdacValues = j; pcdacStruct.pcdacMin = PCDAC_START; pcdacStruct.pcdacMax = PCDAC_STOP; /* Fill out the power values for this channel */ for (j = 0; j < pcdacStruct.numPcdacValues; j++ ) pScaledUpDbm[j] = ar5211GetScaledPower(channel, pPcdacValues[j], pSrcStruct); /* Now scale the pcdac values to fit in the 64 entry power table */ minScaledPwr = pScaledUpDbm[0]; maxScaledPwr = pScaledUpDbm[pcdacStruct.numPcdacValues - 1]; /* find minimum and make monotonic */ for (j = 0; j < pcdacStruct.numPcdacValues; j++) { if (minScaledPwr >= pScaledUpDbm[j]) { minScaledPwr = pScaledUpDbm[j]; pcdacMin = j; } /* * Make the full_hsh monotonically increasing otherwise * interpolation algorithm will get fooled gotta start * working from the top, hence i = 63 - j. */ i = (uint16_t)(pcdacStruct.numPcdacValues - 1 - j); if (i == 0) break; if (pScaledUpDbm[i-1] > pScaledUpDbm[i]) { /* * It could be a glitch, so make the power for * this pcdac the same as the power from the * next highest pcdac. */ pScaledUpDbm[i - 1] = pScaledUpDbm[i]; } } for (j = 0; j < pcdacStruct.numPcdacValues; j++) if (maxScaledPwr < pScaledUpDbm[j]) { maxScaledPwr = pScaledUpDbm[j]; pcdacMax = j; } /* Find the first power level with a pcdac */ pwr = (uint16_t)(PWR_STEP * ((minScaledPwr - PWR_MIN + PWR_STEP / 2) / PWR_STEP) + PWR_MIN); /* Write all the first pcdac entries based off the pcdacMin */ pcdacTableIndex = 0; for (i = 0; i < (2 * (pwr - PWR_MIN) / EEP_SCALE + 1); i++) pcdacTable[pcdacTableIndex++] = pcdacMin; i = 0; while (pwr < pScaledUpDbm[pcdacStruct.numPcdacValues - 1]) { pwr += PWR_STEP; /* stop if dbM > max_power_possible */ while (pwr < pScaledUpDbm[pcdacStruct.numPcdacValues - 1] && (pwr - pScaledUpDbm[i])*(pwr - pScaledUpDbm[i+1]) > 0) i++; /* scale by 2 and add 1 to enable round up or down as needed */ scaledPcdac = (uint16_t)(ar5211GetInterpolatedValue(pwr, pScaledUpDbm[i], pScaledUpDbm[i+1], (uint16_t)(pPcdacValues[i] * 2), (uint16_t)(pPcdacValues[i+1] * 2), 0) + 1); pcdacTable[pcdacTableIndex] = scaledPcdac / 2; if (pcdacTable[pcdacTableIndex] > pcdacMax) pcdacTable[pcdacTableIndex] = pcdacMax; pcdacTableIndex++; } /* Write all the last pcdac entries based off the last valid pcdac */ while (pcdacTableIndex < PWR_TABLE_SIZE) { pcdacTable[pcdacTableIndex] = pcdacTable[pcdacTableIndex - 1]; pcdacTableIndex++; } /* Finally, write the power values into the baseband power table */ addr = AR_PHY_BASE + (608 << 2); for (i = 0; i < 32; i++) { temp32 = 0xffff & ((pcdacTable[2 * i + 1] << 8) | 0xff); temp32 = (temp32 << 16) | (0xffff & ((pcdacTable[2 * i] << 8) | 0xff)); OS_REG_WRITE(ah, addr, temp32); addr += 4; } } /* * Set the transmit power in the baseband for the given * operating channel and mode. */ static void ar5211SetRateTable(struct ath_hal *ah, RD_EDGES_POWER *pRdEdgesPower, TRGT_POWER_INFO *pPowerInfo, uint16_t numChannels, const struct ieee80211_channel *chan) { uint16_t freq = ath_hal_gethwchannel(ah, chan); HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; struct ath_hal_5211 *ahp = AH5211(ah); static uint16_t ratesArray[NUM_RATES]; static const uint16_t tpcScaleReductionTable[5] = { 0, 3, 6, 9, MAX_RATE_POWER }; uint16_t *pRatesPower; uint16_t lowerChannel, lowerIndex=0, lowerPower=0; uint16_t upperChannel, upperIndex=0, upperPower=0; uint16_t twiceMaxEdgePower=63; uint16_t twicePower = 0; uint16_t i, numEdges; uint16_t tempChannelList[NUM_EDGES]; /* temp array for holding edge channels */ uint16_t twiceMaxRDPower; int16_t scaledPower = 0; /* for gcc -O2 */ uint16_t mask = 0x3f; HAL_BOOL paPreDEnable = 0; int8_t twiceAntennaGain, twiceAntennaReduction = 0; pRatesPower = ratesArray; twiceMaxRDPower = chan->ic_maxregpower * 2; if (IEEE80211_IS_CHAN_5GHZ(chan)) { twiceAntennaGain = ee->ee_antennaGainMax[0]; } else { twiceAntennaGain = ee->ee_antennaGainMax[1]; } twiceAntennaReduction = ath_hal_getantennareduction(ah, chan, twiceAntennaGain); if (pRdEdgesPower) { /* Get the edge power */ for (i = 0; i < NUM_EDGES; i++) { if (pRdEdgesPower[i].rdEdge == 0) break; tempChannelList[i] = pRdEdgesPower[i].rdEdge; } numEdges = i; ar5211GetLowerUpperValues(freq, tempChannelList, numEdges, &lowerChannel, &upperChannel); /* Get the index for this channel */ for (i = 0; i < numEdges; i++) if (lowerChannel == tempChannelList[i]) break; HALASSERT(i != numEdges); if ((lowerChannel == upperChannel && lowerChannel == freq) || pRdEdgesPower[i].flag) { twiceMaxEdgePower = pRdEdgesPower[i].twice_rdEdgePower; HALASSERT(twiceMaxEdgePower > 0); } } /* extrapolate the power values for the test Groups */ for (i = 0; i < numChannels; i++) tempChannelList[i] = pPowerInfo[i].testChannel; ar5211GetLowerUpperValues(freq, tempChannelList, numChannels, &lowerChannel, &upperChannel); /* get the index for the channel */ for (i = 0; i < numChannels; i++) { if (lowerChannel == tempChannelList[i]) lowerIndex = i; if (upperChannel == tempChannelList[i]) { upperIndex = i; break; } } for (i = 0; i < NUM_RATES; i++) { if (IEEE80211_IS_CHAN_OFDM(chan)) { /* power for rates 6,9,12,18,24 is all the same */ if (i < 5) { lowerPower = pPowerInfo[lowerIndex].twicePwr6_24; upperPower = pPowerInfo[upperIndex].twicePwr6_24; } else if (i == 5) { lowerPower = pPowerInfo[lowerIndex].twicePwr36; upperPower = pPowerInfo[upperIndex].twicePwr36; } else if (i == 6) { lowerPower = pPowerInfo[lowerIndex].twicePwr48; upperPower = pPowerInfo[upperIndex].twicePwr48; } else if (i == 7) { lowerPower = pPowerInfo[lowerIndex].twicePwr54; upperPower = pPowerInfo[upperIndex].twicePwr54; } } else { switch (i) { case 0: case 1: lowerPower = pPowerInfo[lowerIndex].twicePwr6_24; upperPower = pPowerInfo[upperIndex].twicePwr6_24; break; case 2: case 3: lowerPower = pPowerInfo[lowerIndex].twicePwr36; upperPower = pPowerInfo[upperIndex].twicePwr36; break; case 4: case 5: lowerPower = pPowerInfo[lowerIndex].twicePwr48; upperPower = pPowerInfo[upperIndex].twicePwr48; break; case 6: case 7: lowerPower = pPowerInfo[lowerIndex].twicePwr54; upperPower = pPowerInfo[upperIndex].twicePwr54; break; } } twicePower = ar5211GetInterpolatedValue(freq, lowerChannel, upperChannel, lowerPower, upperPower, 0); /* Reduce power by band edge restrictions */ twicePower = AH_MIN(twicePower, twiceMaxEdgePower); /* * If turbo is set, reduce power to keep power * consumption under 2 Watts. Note that we always do * this unless specially configured. Then we limit * power only for non-AP operation. */ if (IEEE80211_IS_CHAN_TURBO(chan) && AH_PRIVATE(ah)->ah_eeversion >= AR_EEPROM_VER3_1 #ifdef AH_ENABLE_AP_SUPPORT && AH_PRIVATE(ah)->ah_opmode != HAL_M_HOSTAP #endif ) { twicePower = AH_MIN(twicePower, ee->ee_turbo2WMaxPower5); } /* Reduce power by max regulatory domain allowed restrictions */ pRatesPower[i] = AH_MIN(twicePower, twiceMaxRDPower - twiceAntennaReduction); /* Use 6 Mb power level for transmit power scaling reduction */ /* We don't want to reduce higher rates if its not needed */ if (i == 0) { scaledPower = pRatesPower[0] - (tpcScaleReductionTable[AH_PRIVATE(ah)->ah_tpScale] * 2); if (scaledPower < 1) scaledPower = 1; } pRatesPower[i] = AH_MIN(pRatesPower[i], scaledPower); } /* Record txPower at Rate 6 for info gathering */ ahp->ah_tx6PowerInHalfDbm = pRatesPower[0]; #ifdef AH_DEBUG HALDEBUG(ah, HAL_DEBUG_RESET, "%s: final output power setting %d MHz:\n", __func__, chan->ic_freq); HALDEBUG(ah, HAL_DEBUG_RESET, "6 Mb %d dBm, MaxRD: %d dBm, MaxEdge %d dBm\n", scaledPower / 2, twiceMaxRDPower / 2, twiceMaxEdgePower / 2); HALDEBUG(ah, HAL_DEBUG_RESET, "TPC Scale %d dBm - Ant Red %d dBm\n", tpcScaleReductionTable[AH_PRIVATE(ah)->ah_tpScale] * 2, twiceAntennaReduction / 2); if (IEEE80211_IS_CHAN_TURBO(chan) && AH_PRIVATE(ah)->ah_eeversion >= AR_EEPROM_VER3_1) HALDEBUG(ah, HAL_DEBUG_RESET, "Max Turbo %d dBm\n", ee->ee_turbo2WMaxPower5); HALDEBUG(ah, HAL_DEBUG_RESET, " %2d | %2d | %2d | %2d | %2d | %2d | %2d | %2d dBm\n", pRatesPower[0] / 2, pRatesPower[1] / 2, pRatesPower[2] / 2, pRatesPower[3] / 2, pRatesPower[4] / 2, pRatesPower[5] / 2, pRatesPower[6] / 2, pRatesPower[7] / 2); #endif /* AH_DEBUG */ /* Write the power table into the hardware */ OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE1, ((paPreDEnable & 1)<< 30) | ((pRatesPower[3] & mask) << 24) | ((paPreDEnable & 1)<< 22) | ((pRatesPower[2] & mask) << 16) | ((paPreDEnable & 1)<< 14) | ((pRatesPower[1] & mask) << 8) | ((paPreDEnable & 1)<< 6 ) | (pRatesPower[0] & mask)); OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE2, ((paPreDEnable & 1)<< 30) | ((pRatesPower[7] & mask) << 24) | ((paPreDEnable & 1)<< 22) | ((pRatesPower[6] & mask) << 16) | ((paPreDEnable & 1)<< 14) | ((pRatesPower[5] & mask) << 8) | ((paPreDEnable & 1)<< 6 ) | (pRatesPower[4] & mask)); /* set max power to the power value at rate 6 */ ar5211SetTxPowerLimit(ah, pRatesPower[0]); AH_PRIVATE(ah)->ah_maxPowerLevel = pRatesPower[0]; } /* * Get or interpolate the pcdac value from the calibrated data */ uint16_t ar5211GetScaledPower(uint16_t channel, uint16_t pcdacValue, const PCDACS_EEPROM *pSrcStruct) { uint16_t powerValue; uint16_t lFreq, rFreq; /* left and right frequency values */ uint16_t llPcdac, ulPcdac; /* lower and upper left pcdac values */ uint16_t lrPcdac, urPcdac; /* lower and upper right pcdac values */ uint16_t lPwr, uPwr; /* lower and upper temp pwr values */ uint16_t lScaledPwr, rScaledPwr; /* left and right scaled power */ if (ar5211FindValueInList(channel, pcdacValue, pSrcStruct, &powerValue)) /* value was copied from srcStruct */ return powerValue; ar5211GetLowerUpperValues(channel, pSrcStruct->pChannelList, pSrcStruct->numChannels, &lFreq, &rFreq); ar5211GetLowerUpperPcdacs(pcdacValue, lFreq, pSrcStruct, &llPcdac, &ulPcdac); ar5211GetLowerUpperPcdacs(pcdacValue, rFreq, pSrcStruct, &lrPcdac, &urPcdac); /* get the power index for the pcdac value */ ar5211FindValueInList(lFreq, llPcdac, pSrcStruct, &lPwr); ar5211FindValueInList(lFreq, ulPcdac, pSrcStruct, &uPwr); lScaledPwr = ar5211GetInterpolatedValue(pcdacValue, llPcdac, ulPcdac, lPwr, uPwr, 0); ar5211FindValueInList(rFreq, lrPcdac, pSrcStruct, &lPwr); ar5211FindValueInList(rFreq, urPcdac, pSrcStruct, &uPwr); rScaledPwr = ar5211GetInterpolatedValue(pcdacValue, lrPcdac, urPcdac, lPwr, uPwr, 0); return ar5211GetInterpolatedValue(channel, lFreq, rFreq, lScaledPwr, rScaledPwr, 0); } /* * Find the value from the calibrated source data struct */ HAL_BOOL ar5211FindValueInList(uint16_t channel, uint16_t pcdacValue, const PCDACS_EEPROM *pSrcStruct, uint16_t *powerValue) { const DATA_PER_CHANNEL *pChannelData; const uint16_t *pPcdac; uint16_t i, j; pChannelData = pSrcStruct->pDataPerChannel; for (i = 0; i < pSrcStruct->numChannels; i++ ) { if (pChannelData->channelValue == channel) { pPcdac = pChannelData->PcdacValues; for (j = 0; j < pChannelData->numPcdacValues; j++ ) { if (*pPcdac == pcdacValue) { *powerValue = pChannelData->PwrValues[j]; return AH_TRUE; } pPcdac++; } } pChannelData++; } return AH_FALSE; } /* * Returns interpolated or the scaled up interpolated value */ uint16_t ar5211GetInterpolatedValue(uint16_t target, uint16_t srcLeft, uint16_t srcRight, uint16_t targetLeft, uint16_t targetRight, HAL_BOOL scaleUp) { uint16_t rv; int16_t lRatio; uint16_t scaleValue = EEP_SCALE; /* to get an accurate ratio, always scale, if want to scale, then don't scale back down */ if ((targetLeft * targetRight) == 0) return 0; if (scaleUp) scaleValue = 1; if (srcRight != srcLeft) { /* * Note the ratio always need to be scaled, * since it will be a fraction. */ lRatio = (target - srcLeft) * EEP_SCALE / (srcRight - srcLeft); if (lRatio < 0) { /* Return as Left target if value would be negative */ rv = targetLeft * (scaleUp ? EEP_SCALE : 1); } else if (lRatio > EEP_SCALE) { /* Return as Right target if Ratio is greater than 100% (SCALE) */ rv = targetRight * (scaleUp ? EEP_SCALE : 1); } else { rv = (lRatio * targetRight + (EEP_SCALE - lRatio) * targetLeft) / scaleValue; } } else { rv = targetLeft; if (scaleUp) rv *= EEP_SCALE; } return rv; } /* * Look for value being within 0.1 of the search values * however, NDIS can't do float calculations, so multiply everything * up by EEP_SCALE so can do integer arithmatic * * INPUT value -value to search for * INPUT pList -ptr to the list to search * INPUT listSize -number of entries in list * OUTPUT pLowerValue -return the lower value * OUTPUT pUpperValue -return the upper value */ void ar5211GetLowerUpperValues(uint16_t value, const uint16_t *pList, uint16_t listSize, uint16_t *pLowerValue, uint16_t *pUpperValue) { const uint16_t listEndValue = *(pList + listSize - 1); uint32_t target = value * EEP_SCALE; int i; /* * See if value is lower than the first value in the list * if so return first value */ if (target < (uint32_t)(*pList * EEP_SCALE - EEP_DELTA)) { *pLowerValue = *pList; *pUpperValue = *pList; return; } /* * See if value is greater than last value in list * if so return last value */ if (target > (uint32_t)(listEndValue * EEP_SCALE + EEP_DELTA)) { *pLowerValue = listEndValue; *pUpperValue = listEndValue; return; } /* look for value being near or between 2 values in list */ for (i = 0; i < listSize; i++) { /* * If value is close to the current value of the list * then target is not between values, it is one of the values */ if (abs(pList[i] * EEP_SCALE - (int32_t) target) < EEP_DELTA) { *pLowerValue = pList[i]; *pUpperValue = pList[i]; return; } /* * Look for value being between current value and next value * if so return these 2 values */ if (target < (uint32_t)(pList[i + 1] * EEP_SCALE - EEP_DELTA)) { *pLowerValue = pList[i]; *pUpperValue = pList[i + 1]; return; } } } /* * Get the upper and lower pcdac given the channel and the pcdac * used in the search */ void ar5211GetLowerUpperPcdacs(uint16_t pcdac, uint16_t channel, const PCDACS_EEPROM *pSrcStruct, uint16_t *pLowerPcdac, uint16_t *pUpperPcdac) { const DATA_PER_CHANNEL *pChannelData; int i; /* Find the channel information */ pChannelData = pSrcStruct->pDataPerChannel; for (i = 0; i < pSrcStruct->numChannels; i++) { if (pChannelData->channelValue == channel) break; pChannelData++; } ar5211GetLowerUpperValues(pcdac, pChannelData->PcdacValues, pChannelData->numPcdacValues, pLowerPcdac, pUpperPcdac); } #define DYN_ADJ_UP_MARGIN 15 #define DYN_ADJ_LO_MARGIN 20 static const GAIN_OPTIMIZATION_LADDER gainLadder = { 9, /* numStepsInLadder */ 4, /* defaultStepNum */ { { {4, 1, 1, 1}, 6, "FG8"}, { {4, 0, 1, 1}, 4, "FG7"}, { {3, 1, 1, 1}, 3, "FG6"}, { {4, 0, 0, 1}, 1, "FG5"}, { {4, 1, 1, 0}, 0, "FG4"}, /* noJack */ { {4, 0, 1, 0}, -2, "FG3"}, /* halfJack */ { {3, 1, 1, 0}, -3, "FG2"}, /* clip3 */ { {4, 0, 0, 0}, -4, "FG1"}, /* noJack */ { {2, 1, 1, 0}, -6, "FG0"} /* clip2 */ } }; /* * Initialize the gain structure to good values */ void ar5211InitializeGainValues(struct ath_hal *ah) { struct ath_hal_5211 *ahp = AH5211(ah); GAIN_VALUES *gv = &ahp->ah_gainValues; /* initialize gain optimization values */ gv->currStepNum = gainLadder.defaultStepNum; gv->currStep = &gainLadder.optStep[gainLadder.defaultStepNum]; gv->active = AH_TRUE; gv->loTrig = 20; gv->hiTrig = 35; } static HAL_BOOL ar5211InvalidGainReadback(struct ath_hal *ah, GAIN_VALUES *gv) { const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan; uint32_t gStep, g; uint32_t L1, L2, L3, L4; if (IEEE80211_IS_CHAN_CCK(chan)) { gStep = 0x18; L1 = 0; L2 = gStep + 4; L3 = 0x40; L4 = L3 + 50; gv->loTrig = L1; gv->hiTrig = L4+5; } else { gStep = 0x3f; L1 = 0; L2 = 50; L3 = L1; L4 = L3 + 50; gv->loTrig = L1 + DYN_ADJ_LO_MARGIN; gv->hiTrig = L4 - DYN_ADJ_UP_MARGIN; } g = gv->currGain; return !((g >= L1 && g<= L2) || (g >= L3 && g <= L4)); } /* * Enable the probe gain check on the next packet */ static void ar5211RequestRfgain(struct ath_hal *ah) { struct ath_hal_5211 *ahp = AH5211(ah); /* Enable the gain readback probe */ OS_REG_WRITE(ah, AR_PHY_PAPD_PROBE, SM(ahp->ah_tx6PowerInHalfDbm, AR_PHY_PAPD_PROBE_POWERTX) | AR_PHY_PAPD_PROBE_NEXT_TX); ahp->ah_rfgainState = HAL_RFGAIN_READ_REQUESTED; } /* * Exported call to check for a recent gain reading and return * the current state of the thermal calibration gain engine. */ HAL_RFGAIN ar5211GetRfgain(struct ath_hal *ah) { struct ath_hal_5211 *ahp = AH5211(ah); GAIN_VALUES *gv = &ahp->ah_gainValues; uint32_t rddata; if (!gv->active) return HAL_RFGAIN_INACTIVE; if (ahp->ah_rfgainState == HAL_RFGAIN_READ_REQUESTED) { /* Caller had asked to setup a new reading. Check it. */ rddata = OS_REG_READ(ah, AR_PHY_PAPD_PROBE); if ((rddata & AR_PHY_PAPD_PROBE_NEXT_TX) == 0) { /* bit got cleared, we have a new reading. */ gv->currGain = rddata >> AR_PHY_PAPD_PROBE_GAINF_S; /* inactive by default */ ahp->ah_rfgainState = HAL_RFGAIN_INACTIVE; if (!ar5211InvalidGainReadback(ah, gv) && ar5211IsGainAdjustNeeded(ah, gv) && ar5211AdjustGain(ah, gv) > 0) { /* * Change needed. Copy ladder info * into eeprom info. */ ar5211SetRfgain(ah, gv); ahp->ah_rfgainState = HAL_RFGAIN_NEED_CHANGE; } } } return ahp->ah_rfgainState; } /* * Check to see if our readback gain level sits within the linear * region of our current variable attenuation window */ static HAL_BOOL ar5211IsGainAdjustNeeded(struct ath_hal *ah, const GAIN_VALUES *gv) { return (gv->currGain <= gv->loTrig || gv->currGain >= gv->hiTrig); } /* * Move the rabbit ears in the correct direction. */ static int32_t ar5211AdjustGain(struct ath_hal *ah, GAIN_VALUES *gv) { /* return > 0 for valid adjustments. */ if (!gv->active) return -1; gv->currStep = &gainLadder.optStep[gv->currStepNum]; if (gv->currGain >= gv->hiTrig) { if (gv->currStepNum == 0) { HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: Max gain limit.\n", __func__); return -1; } HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: Adding gain: currG=%d [%s] --> ", __func__, gv->currGain, gv->currStep->stepName); gv->targetGain = gv->currGain; while (gv->targetGain >= gv->hiTrig && gv->currStepNum > 0) { gv->targetGain -= 2 * (gainLadder.optStep[--(gv->currStepNum)].stepGain - gv->currStep->stepGain); gv->currStep = &gainLadder.optStep[gv->currStepNum]; } HALDEBUG(ah, HAL_DEBUG_RFPARAM, "targG=%d [%s]\n", gv->targetGain, gv->currStep->stepName); return 1; } if (gv->currGain <= gv->loTrig) { if (gv->currStepNum == gainLadder.numStepsInLadder-1) { HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: Min gain limit.\n", __func__); return -2; } HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: Deducting gain: currG=%d [%s] --> ", __func__, gv->currGain, gv->currStep->stepName); gv->targetGain = gv->currGain; while (gv->targetGain <= gv->loTrig && gv->currStepNum < (gainLadder.numStepsInLadder - 1)) { gv->targetGain -= 2 * (gainLadder.optStep[++(gv->currStepNum)].stepGain - gv->currStep->stepGain); gv->currStep = &gainLadder.optStep[gv->currStepNum]; } HALDEBUG(ah, HAL_DEBUG_RFPARAM, "targG=%d [%s]\n", gv->targetGain, gv->currStep->stepName); return 2; } return 0; /* caller didn't call needAdjGain first */ } /* * Adjust the 5GHz EEPROM information with the desired calibration values. */ static void ar5211SetRfgain(struct ath_hal *ah, const GAIN_VALUES *gv) { HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; if (!gv->active) return; ee->ee_cornerCal.clip = gv->currStep->paramVal[0]; /* bb_tx_clip */ ee->ee_cornerCal.pd90 = gv->currStep->paramVal[1]; /* rf_pwd_90 */ ee->ee_cornerCal.pd84 = gv->currStep->paramVal[2]; /* rf_pwd_84 */ ee->ee_cornerCal.gSel = gv->currStep->paramVal[3]; /* rf_rfgainsel */ } static void ar5211SetOperatingMode(struct ath_hal *ah, int opmode) { struct ath_hal_5211 *ahp = AH5211(ah); uint32_t val; val = OS_REG_READ(ah, AR_STA_ID1) & 0xffff; switch (opmode) { case HAL_M_HOSTAP: OS_REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_STA_AP | AR_STA_ID1_RTS_USE_DEF | ahp->ah_staId1Defaults); break; case HAL_M_IBSS: OS_REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_ADHOC | AR_STA_ID1_DESC_ANTENNA | ahp->ah_staId1Defaults); break; case HAL_M_STA: case HAL_M_MONITOR: OS_REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_DEFAULT_ANTENNA | ahp->ah_staId1Defaults); break; } } void ar5211SetPCUConfig(struct ath_hal *ah) { ar5211SetOperatingMode(ah, AH_PRIVATE(ah)->ah_opmode); } Index: head/sys/dev/ath/ath_hal/ar5212/ar2316.c =================================================================== --- head/sys/dev/ath/ath_hal/ar5212/ar2316.c (revision 298938) +++ head/sys/dev/ath/ath_hal/ar5212/ar2316.c (revision 298939) @@ -1,763 +1,763 @@ /* * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * Copyright (c) 2002-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ar5212/ar5212.h" #include "ar5212/ar5212reg.h" #include "ar5212/ar5212phy.h" #include "ah_eeprom_v3.h" #define AH_5212_2316 #include "ar5212/ar5212.ini" #define N(a) (sizeof(a)/sizeof(a[0])) typedef RAW_DATA_STRUCT_2413 RAW_DATA_STRUCT_2316; typedef RAW_DATA_PER_CHANNEL_2413 RAW_DATA_PER_CHANNEL_2316; #define PWR_TABLE_SIZE_2316 PWR_TABLE_SIZE_2413 struct ar2316State { RF_HAL_FUNCS base; /* public state, must be first */ uint16_t pcdacTable[PWR_TABLE_SIZE_2316]; uint32_t Bank1Data[N(ar5212Bank1_2316)]; uint32_t Bank2Data[N(ar5212Bank2_2316)]; uint32_t Bank3Data[N(ar5212Bank3_2316)]; uint32_t Bank6Data[N(ar5212Bank6_2316)]; uint32_t Bank7Data[N(ar5212Bank7_2316)]; /* * Private state for reduced stack usage. */ /* filled out Vpd table for all pdGains (chanL) */ uint16_t vpdTable_L[MAX_NUM_PDGAINS_PER_CHANNEL] [MAX_PWR_RANGE_IN_HALF_DB]; /* filled out Vpd table for all pdGains (chanR) */ uint16_t vpdTable_R[MAX_NUM_PDGAINS_PER_CHANNEL] [MAX_PWR_RANGE_IN_HALF_DB]; /* filled out Vpd table for all pdGains (interpolated) */ uint16_t vpdTable_I[MAX_NUM_PDGAINS_PER_CHANNEL] [MAX_PWR_RANGE_IN_HALF_DB]; }; #define AR2316(ah) ((struct ar2316State *) AH5212(ah)->ah_rfHal) extern void ar5212ModifyRfBuffer(uint32_t *rfBuf, uint32_t reg32, uint32_t numBits, uint32_t firstBit, uint32_t column); static void ar2316WriteRegs(struct ath_hal *ah, u_int modesIndex, u_int freqIndex, int regWrites) { struct ath_hal_5212 *ahp = AH5212(ah); HAL_INI_WRITE_ARRAY(ah, ar5212Modes_2316, modesIndex, regWrites); HAL_INI_WRITE_ARRAY(ah, ar5212Common_2316, 1, regWrites); HAL_INI_WRITE_ARRAY(ah, ar5212BB_RfGain_2316, freqIndex, regWrites); /* For AP51 */ if (!ahp->ah_cwCalRequire) { OS_REG_WRITE(ah, 0xa358, (OS_REG_READ(ah, 0xa358) & ~0x2)); } else { ahp->ah_cwCalRequire = AH_FALSE; } } /* * Take the MHz channel value and set the Channel value * * ASSUMES: Writes enabled to analog bus */ static HAL_BOOL ar2316SetChannel(struct ath_hal *ah, struct ieee80211_channel *chan) { uint16_t freq = ath_hal_gethwchannel(ah, chan); uint32_t channelSel = 0; uint32_t bModeSynth = 0; uint32_t aModeRefSel = 0; uint32_t reg32 = 0; OS_MARK(ah, AH_MARK_SETCHANNEL, freq); if (freq < 4800) { uint32_t txctl; if (((freq - 2192) % 5) == 0) { channelSel = ((freq - 672) * 2 - 3040)/10; bModeSynth = 0; } else if (((freq - 2224) % 5) == 0) { channelSel = ((freq - 704) * 2 - 3040) / 10; bModeSynth = 1; } else { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel %u MHz\n", __func__, freq); return AH_FALSE; } channelSel = (channelSel << 2) & 0xff; channelSel = ath_hal_reverseBits(channelSel, 8); txctl = OS_REG_READ(ah, AR_PHY_CCK_TX_CTRL); if (freq == 2484) { /* Enable channel spreading for channel 14 */ OS_REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, txctl | AR_PHY_CCK_TX_CTRL_JAPAN); } else { OS_REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, txctl &~ AR_PHY_CCK_TX_CTRL_JAPAN); } } else if ((freq % 20) == 0 && freq >= 5120) { channelSel = ath_hal_reverseBits( ((freq - 4800) / 20 << 2), 8); aModeRefSel = ath_hal_reverseBits(3, 2); } else if ((freq % 10) == 0) { channelSel = ath_hal_reverseBits( ((freq - 4800) / 10 << 1), 8); aModeRefSel = ath_hal_reverseBits(2, 2); } else if ((freq % 5) == 0) { channelSel = ath_hal_reverseBits( (freq - 4800) / 5, 8); aModeRefSel = ath_hal_reverseBits(1, 2); } else { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel %u MHz\n", __func__, freq); return AH_FALSE; } reg32 = (channelSel << 4) | (aModeRefSel << 2) | (bModeSynth << 1) | (1 << 12) | 0x1; OS_REG_WRITE(ah, AR_PHY(0x27), reg32 & 0xff); reg32 >>= 8; OS_REG_WRITE(ah, AR_PHY(0x36), reg32 & 0x7f); AH_PRIVATE(ah)->ah_curchan = chan; return AH_TRUE; } /* * Reads EEPROM header info from device structure and programs * all rf registers * * REQUIRES: Access to the analog rf device */ static HAL_BOOL ar2316SetRfRegs(struct ath_hal *ah, const struct ieee80211_channel *chan, uint16_t modesIndex, uint16_t *rfXpdGain) { #define RF_BANK_SETUP(_priv, _ix, _col) do { \ int i; \ for (i = 0; i < N(ar5212Bank##_ix##_2316); i++) \ (_priv)->Bank##_ix##Data[i] = ar5212Bank##_ix##_2316[i][_col];\ } while (0) struct ath_hal_5212 *ahp = AH5212(ah); const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; uint16_t ob2GHz = 0, db2GHz = 0; struct ar2316State *priv = AR2316(ah); int regWrites = 0; HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: chan %u/0x%x modesIndex %u\n", __func__, chan->ic_freq, chan->ic_flags, modesIndex); HALASSERT(priv != AH_NULL); /* Setup rf parameters */ if (IEEE80211_IS_CHAN_B(chan)) { ob2GHz = ee->ee_obFor24; db2GHz = ee->ee_dbFor24; } else { ob2GHz = ee->ee_obFor24g; db2GHz = ee->ee_dbFor24g; } /* Bank 1 Write */ RF_BANK_SETUP(priv, 1, 1); /* Bank 2 Write */ RF_BANK_SETUP(priv, 2, modesIndex); /* Bank 3 Write */ RF_BANK_SETUP(priv, 3, modesIndex); /* Bank 6 Write */ RF_BANK_SETUP(priv, 6, modesIndex); ar5212ModifyRfBuffer(priv->Bank6Data, ob2GHz, 3, 178, 0); ar5212ModifyRfBuffer(priv->Bank6Data, db2GHz, 3, 175, 0); /* Bank 7 Setup */ RF_BANK_SETUP(priv, 7, modesIndex); /* Write Analog registers */ HAL_INI_WRITE_BANK(ah, ar5212Bank1_2316, priv->Bank1Data, regWrites); HAL_INI_WRITE_BANK(ah, ar5212Bank2_2316, priv->Bank2Data, regWrites); HAL_INI_WRITE_BANK(ah, ar5212Bank3_2316, priv->Bank3Data, regWrites); HAL_INI_WRITE_BANK(ah, ar5212Bank6_2316, priv->Bank6Data, regWrites); HAL_INI_WRITE_BANK(ah, ar5212Bank7_2316, priv->Bank7Data, regWrites); /* Now that we have reprogrammed rfgain value, clear the flag. */ ahp->ah_rfgainState = HAL_RFGAIN_INACTIVE; return AH_TRUE; #undef RF_BANK_SETUP } /* * Return a reference to the requested RF Bank. */ static uint32_t * ar2316GetRfBank(struct ath_hal *ah, int bank) { struct ar2316State *priv = AR2316(ah); HALASSERT(priv != AH_NULL); switch (bank) { case 1: return priv->Bank1Data; case 2: return priv->Bank2Data; case 3: return priv->Bank3Data; case 6: return priv->Bank6Data; case 7: return priv->Bank7Data; } HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unknown RF Bank %d requested\n", __func__, bank); return AH_NULL; } /* * Return indices surrounding the value in sorted integer lists. * * NB: the input list is assumed to be sorted in ascending order */ static void GetLowerUpperIndex(int16_t v, const uint16_t *lp, uint16_t listSize, uint32_t *vlo, uint32_t *vhi) { int16_t target = v; const int16_t *ep = lp+listSize; const int16_t *tp; /* * Check first and last elements for out-of-bounds conditions. */ if (target < lp[0]) { *vlo = *vhi = 0; return; } if (target >= ep[-1]) { *vlo = *vhi = listSize - 1; return; } /* look for value being near or between 2 values in list */ for (tp = lp; tp < ep; tp++) { /* * If value is close to the current value of the list * then target is not between values, it is one of the values */ if (*tp == target) { *vlo = *vhi = tp - (const int16_t *) lp; return; } /* * Look for value being between current value and next value * if so return these 2 values */ if (target < tp[1]) { *vlo = tp - (const int16_t *) lp; *vhi = *vlo + 1; return; } } } /* * Fill the Vpdlist for indices Pmax-Pmin */ static HAL_BOOL ar2316FillVpdTable(uint32_t pdGainIdx, int16_t Pmin, int16_t Pmax, const int16_t *pwrList, const int16_t *VpdList, uint16_t numIntercepts, uint16_t retVpdList[][64]) { uint16_t ii, jj, kk; int16_t currPwr = (int16_t)(2*Pmin); /* since Pmin is pwr*2 and pwrList is 4*pwr */ uint32_t idxL, idxR; ii = 0; jj = 0; if (numIntercepts < 2) return AH_FALSE; while (ii <= (uint16_t)(Pmax - Pmin)) { GetLowerUpperIndex(currPwr, pwrList, numIntercepts, &(idxL), &(idxR)); if (idxR < 1) idxR = 1; /* extrapolate below */ if (idxL == (uint32_t)(numIntercepts - 1)) idxL = numIntercepts - 2; /* extrapolate above */ if (pwrList[idxL] == pwrList[idxR]) kk = VpdList[idxL]; else kk = (uint16_t) (((currPwr - pwrList[idxL])*VpdList[idxR]+ (pwrList[idxR] - currPwr)*VpdList[idxL])/ (pwrList[idxR] - pwrList[idxL])); retVpdList[pdGainIdx][ii] = kk; ii++; currPwr += 2; /* half dB steps */ } return AH_TRUE; } /* * Returns interpolated or the scaled up interpolated value */ static int16_t interpolate_signed(uint16_t target, uint16_t srcLeft, uint16_t srcRight, int16_t targetLeft, int16_t targetRight) { int16_t rv; if (srcRight != srcLeft) { rv = ((target - srcLeft)*targetRight + (srcRight - target)*targetLeft) / (srcRight - srcLeft); } else { rv = targetLeft; } return rv; } /* * Uses the data points read from EEPROM to reconstruct the pdadc power table * Called by ar2316SetPowerTable() */ static int ar2316getGainBoundariesAndPdadcsForPowers(struct ath_hal *ah, uint16_t channel, const RAW_DATA_STRUCT_2316 *pRawDataset, uint16_t pdGainOverlap_t2, int16_t *pMinCalPower, uint16_t pPdGainBoundaries[], uint16_t pPdGainValues[], uint16_t pPDADCValues[]) { struct ar2316State *priv = AR2316(ah); #define VpdTable_L priv->vpdTable_L #define VpdTable_R priv->vpdTable_R #define VpdTable_I priv->vpdTable_I uint32_t ii, jj, kk; int32_t ss;/* potentially -ve index for taking care of pdGainOverlap */ uint32_t idxL, idxR; uint32_t numPdGainsUsed = 0; /* * If desired to support -ve power levels in future, just * change pwr_I_0 to signed 5-bits. */ int16_t Pmin_t2[MAX_NUM_PDGAINS_PER_CHANNEL]; - /* to accomodate -ve power levels later on. */ + /* to accommodate -ve power levels later on. */ int16_t Pmax_t2[MAX_NUM_PDGAINS_PER_CHANNEL]; - /* to accomodate -ve power levels later on */ + /* to accommodate -ve power levels later on */ uint16_t numVpd = 0; uint16_t Vpd_step; int16_t tmpVal ; uint32_t sizeCurrVpdTable, maxIndex, tgtIndex; /* Get upper lower index */ GetLowerUpperIndex(channel, pRawDataset->pChannels, pRawDataset->numChannels, &(idxL), &(idxR)); for (ii = 0; ii < MAX_NUM_PDGAINS_PER_CHANNEL; ii++) { jj = MAX_NUM_PDGAINS_PER_CHANNEL - ii - 1; /* work backwards 'cause highest pdGain for lowest power */ numVpd = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].numVpd; if (numVpd > 0) { pPdGainValues[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pd_gain; Pmin_t2[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pwr_t4[0]; if (Pmin_t2[numPdGainsUsed] >pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[0]) { Pmin_t2[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[0]; } Pmin_t2[numPdGainsUsed] = (int16_t) (Pmin_t2[numPdGainsUsed] / 2); Pmax_t2[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pwr_t4[numVpd-1]; if (Pmax_t2[numPdGainsUsed] > pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[numVpd-1]) Pmax_t2[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[numVpd-1]; Pmax_t2[numPdGainsUsed] = (int16_t)(Pmax_t2[numPdGainsUsed] / 2); ar2316FillVpdTable( numPdGainsUsed, Pmin_t2[numPdGainsUsed], Pmax_t2[numPdGainsUsed], &(pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pwr_t4[0]), &(pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].Vpd[0]), numVpd, VpdTable_L ); ar2316FillVpdTable( numPdGainsUsed, Pmin_t2[numPdGainsUsed], Pmax_t2[numPdGainsUsed], &(pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[0]), &(pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].Vpd[0]), numVpd, VpdTable_R ); for (kk = 0; kk < (uint16_t)(Pmax_t2[numPdGainsUsed] - Pmin_t2[numPdGainsUsed]); kk++) { VpdTable_I[numPdGainsUsed][kk] = interpolate_signed( channel, pRawDataset->pChannels[idxL], pRawDataset->pChannels[idxR], (int16_t)VpdTable_L[numPdGainsUsed][kk], (int16_t)VpdTable_R[numPdGainsUsed][kk]); } /* fill VpdTable_I for this pdGain */ numPdGainsUsed++; } /* if this pdGain is used */ } *pMinCalPower = Pmin_t2[0]; kk = 0; /* index for the final table */ for (ii = 0; ii < numPdGainsUsed; ii++) { if (ii == (numPdGainsUsed - 1)) pPdGainBoundaries[ii] = Pmax_t2[ii] + PD_GAIN_BOUNDARY_STRETCH_IN_HALF_DB; else pPdGainBoundaries[ii] = (uint16_t) ((Pmax_t2[ii] + Pmin_t2[ii+1]) / 2 ); if (pPdGainBoundaries[ii] > 63) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: clamp pPdGainBoundaries[%d] %d\n", __func__, ii, pPdGainBoundaries[ii]);/*XXX*/ pPdGainBoundaries[ii] = 63; } /* Find starting index for this pdGain */ if (ii == 0) ss = 0; /* for the first pdGain, start from index 0 */ else ss = (pPdGainBoundaries[ii-1] - Pmin_t2[ii]) - pdGainOverlap_t2; Vpd_step = (uint16_t)(VpdTable_I[ii][1] - VpdTable_I[ii][0]); Vpd_step = (uint16_t)((Vpd_step < 1) ? 1 : Vpd_step); /* *-ve ss indicates need to extrapolate data below for this pdGain */ while (ss < 0) { tmpVal = (int16_t)(VpdTable_I[ii][0] + ss*Vpd_step); pPDADCValues[kk++] = (uint16_t)((tmpVal < 0) ? 0 : tmpVal); ss++; } sizeCurrVpdTable = Pmax_t2[ii] - Pmin_t2[ii]; tgtIndex = pPdGainBoundaries[ii] + pdGainOverlap_t2 - Pmin_t2[ii]; maxIndex = (tgtIndex < sizeCurrVpdTable) ? tgtIndex : sizeCurrVpdTable; while (ss < (int16_t)maxIndex) pPDADCValues[kk++] = VpdTable_I[ii][ss++]; Vpd_step = (uint16_t)(VpdTable_I[ii][sizeCurrVpdTable-1] - VpdTable_I[ii][sizeCurrVpdTable-2]); Vpd_step = (uint16_t)((Vpd_step < 1) ? 1 : Vpd_step); /* * for last gain, pdGainBoundary == Pmax_t2, so will * have to extrapolate */ if (tgtIndex > maxIndex) { /* need to extrapolate above */ while(ss < (int16_t)tgtIndex) { tmpVal = (uint16_t) (VpdTable_I[ii][sizeCurrVpdTable-1] + (ss-maxIndex)*Vpd_step); pPDADCValues[kk++] = (tmpVal > 127) ? 127 : tmpVal; ss++; } } /* extrapolated above */ } /* for all pdGainUsed */ while (ii < MAX_NUM_PDGAINS_PER_CHANNEL) { pPdGainBoundaries[ii] = pPdGainBoundaries[ii-1]; ii++; } while (kk < 128) { pPDADCValues[kk] = pPDADCValues[kk-1]; kk++; } return numPdGainsUsed; #undef VpdTable_L #undef VpdTable_R #undef VpdTable_I } static HAL_BOOL ar2316SetPowerTable(struct ath_hal *ah, int16_t *minPower, int16_t *maxPower, const struct ieee80211_channel *chan, uint16_t *rfXpdGain) { struct ath_hal_5212 *ahp = AH5212(ah); const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; const RAW_DATA_STRUCT_2316 *pRawDataset = AH_NULL; uint16_t pdGainOverlap_t2; int16_t minCalPower2316_t2; uint16_t *pdadcValues = ahp->ah_pcdacTable; uint16_t gainBoundaries[4]; uint32_t reg32, regoffset; int i, numPdGainsUsed; #ifndef AH_USE_INIPDGAIN uint32_t tpcrg1; #endif HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: chan 0x%x flag 0x%x\n", __func__, chan->ic_freq, chan->ic_flags); if (IEEE80211_IS_CHAN_G(chan) || IEEE80211_IS_CHAN_108G(chan)) pRawDataset = &ee->ee_rawDataset2413[headerInfo11G]; else if (IEEE80211_IS_CHAN_B(chan)) pRawDataset = &ee->ee_rawDataset2413[headerInfo11B]; else { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: illegal mode\n", __func__); return AH_FALSE; } pdGainOverlap_t2 = (uint16_t) SM(OS_REG_READ(ah, AR_PHY_TPCRG5), AR_PHY_TPCRG5_PD_GAIN_OVERLAP); numPdGainsUsed = ar2316getGainBoundariesAndPdadcsForPowers(ah, chan->channel, pRawDataset, pdGainOverlap_t2, &minCalPower2316_t2,gainBoundaries, rfXpdGain, pdadcValues); HALASSERT(1 <= numPdGainsUsed && numPdGainsUsed <= 3); #ifdef AH_USE_INIPDGAIN /* * Use pd_gains curve from eeprom; Atheros always uses * the default curve from the ini file but some vendors * (e.g. Zcomax) want to override this curve and not * honoring their settings results in tx power 5dBm low. */ OS_REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_NUM_PD_GAIN, (pRawDataset->pDataPerChannel[0].numPdGains - 1)); #else tpcrg1 = OS_REG_READ(ah, AR_PHY_TPCRG1); tpcrg1 = (tpcrg1 &~ AR_PHY_TPCRG1_NUM_PD_GAIN) | SM(numPdGainsUsed-1, AR_PHY_TPCRG1_NUM_PD_GAIN); switch (numPdGainsUsed) { case 3: tpcrg1 &= ~AR_PHY_TPCRG1_PDGAIN_SETTING3; tpcrg1 |= SM(rfXpdGain[2], AR_PHY_TPCRG1_PDGAIN_SETTING3); /* fall thru... */ case 2: tpcrg1 &= ~AR_PHY_TPCRG1_PDGAIN_SETTING2; tpcrg1 |= SM(rfXpdGain[1], AR_PHY_TPCRG1_PDGAIN_SETTING2); /* fall thru... */ case 1: tpcrg1 &= ~AR_PHY_TPCRG1_PDGAIN_SETTING1; tpcrg1 |= SM(rfXpdGain[0], AR_PHY_TPCRG1_PDGAIN_SETTING1); break; } #ifdef AH_DEBUG if (tpcrg1 != OS_REG_READ(ah, AR_PHY_TPCRG1)) HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: using non-default " "pd_gains (default 0x%x, calculated 0x%x)\n", __func__, OS_REG_READ(ah, AR_PHY_TPCRG1), tpcrg1); #endif OS_REG_WRITE(ah, AR_PHY_TPCRG1, tpcrg1); #endif /* * Note the pdadc table may not start at 0 dBm power, could be * negative or greater than 0. Need to offset the power * values by the amount of minPower for griffin */ if (minCalPower2316_t2 != 0) ahp->ah_txPowerIndexOffset = (int16_t)(0 - minCalPower2316_t2); else ahp->ah_txPowerIndexOffset = 0; /* Finally, write the power values into the baseband power table */ regoffset = 0x9800 + (672 <<2); /* beginning of pdadc table in griffin */ for (i = 0; i < 32; i++) { reg32 = ((pdadcValues[4*i + 0] & 0xFF) << 0) | ((pdadcValues[4*i + 1] & 0xFF) << 8) | ((pdadcValues[4*i + 2] & 0xFF) << 16) | ((pdadcValues[4*i + 3] & 0xFF) << 24) ; OS_REG_WRITE(ah, regoffset, reg32); regoffset += 4; } OS_REG_WRITE(ah, AR_PHY_TPCRG5, SM(pdGainOverlap_t2, AR_PHY_TPCRG5_PD_GAIN_OVERLAP) | SM(gainBoundaries[0], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1) | SM(gainBoundaries[1], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2) | SM(gainBoundaries[2], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3) | SM(gainBoundaries[3], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4)); return AH_TRUE; } static int16_t ar2316GetMinPower(struct ath_hal *ah, const RAW_DATA_PER_CHANNEL_2316 *data) { uint32_t ii,jj; uint16_t Pmin=0,numVpd; for (ii = 0; ii < MAX_NUM_PDGAINS_PER_CHANNEL; ii++) { jj = MAX_NUM_PDGAINS_PER_CHANNEL - ii - 1; /* work backwards 'cause highest pdGain for lowest power */ numVpd = data->pDataPerPDGain[jj].numVpd; if (numVpd > 0) { Pmin = data->pDataPerPDGain[jj].pwr_t4[0]; return(Pmin); } } return(Pmin); } static int16_t ar2316GetMaxPower(struct ath_hal *ah, const RAW_DATA_PER_CHANNEL_2316 *data) { uint32_t ii; uint16_t Pmax=0,numVpd; for (ii=0; ii< MAX_NUM_PDGAINS_PER_CHANNEL; ii++) { /* work forwards cuase lowest pdGain for highest power */ numVpd = data->pDataPerPDGain[ii].numVpd; if (numVpd > 0) { Pmax = data->pDataPerPDGain[ii].pwr_t4[numVpd-1]; return(Pmax); } } return(Pmax); } static HAL_BOOL ar2316GetChannelMaxMinPower(struct ath_hal *ah, const struct ieee80211_channel *chan, int16_t *maxPow, int16_t *minPow) { uint16_t freq = chan->ic_freq; /* NB: never mapped */ const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; const RAW_DATA_STRUCT_2316 *pRawDataset = AH_NULL; const RAW_DATA_PER_CHANNEL_2316 *data=AH_NULL; uint16_t numChannels; int totalD,totalF, totalMin,last, i; *maxPow = 0; if (IEEE80211_IS_CHAN_G(chan) || IEEE80211_IS_CHAN_108G(chan)) pRawDataset = &ee->ee_rawDataset2413[headerInfo11G]; else if (IEEE80211_IS_CHAN_B(chan)) pRawDataset = &ee->ee_rawDataset2413[headerInfo11B]; else return(AH_FALSE); numChannels = pRawDataset->numChannels; data = pRawDataset->pDataPerChannel; /* Make sure the channel is in the range of the TP values * (freq piers) */ if (numChannels < 1) return(AH_FALSE); if ((freq < data[0].channelValue) || (freq > data[numChannels-1].channelValue)) { if (freq < data[0].channelValue) { *maxPow = ar2316GetMaxPower(ah, &data[0]); *minPow = ar2316GetMinPower(ah, &data[0]); return(AH_TRUE); } else { *maxPow = ar2316GetMaxPower(ah, &data[numChannels - 1]); *minPow = ar2316GetMinPower(ah, &data[numChannels - 1]); return(AH_TRUE); } } /* Linearly interpolate the power value now */ for (last=0,i=0; (i data[i].channelValue); last = i++); totalD = data[i].channelValue - data[last].channelValue; if (totalD > 0) { totalF = ar2316GetMaxPower(ah, &data[i]) - ar2316GetMaxPower(ah, &data[last]); *maxPow = (int8_t) ((totalF*(freq-data[last].channelValue) + ar2316GetMaxPower(ah, &data[last])*totalD)/totalD); totalMin = ar2316GetMinPower(ah, &data[i]) - ar2316GetMinPower(ah, &data[last]); *minPow = (int8_t) ((totalMin*(freq-data[last].channelValue) + ar2316GetMinPower(ah, &data[last])*totalD)/totalD); return(AH_TRUE); } else { if (freq == data[i].channelValue) { *maxPow = ar2316GetMaxPower(ah, &data[i]); *minPow = ar2316GetMinPower(ah, &data[i]); return(AH_TRUE); } else return(AH_FALSE); } } /* * Free memory for analog bank scratch buffers */ static void ar2316RfDetach(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); HALASSERT(ahp->ah_rfHal != AH_NULL); ath_hal_free(ahp->ah_rfHal); ahp->ah_rfHal = AH_NULL; } /* * Allocate memory for private state. * Scratch Buffer will be reinitialized every reset so no need to zero now */ static HAL_BOOL ar2316RfAttach(struct ath_hal *ah, HAL_STATUS *status) { struct ath_hal_5212 *ahp = AH5212(ah); struct ar2316State *priv; HALASSERT(ah->ah_magic == AR5212_MAGIC); HALASSERT(ahp->ah_rfHal == AH_NULL); priv = ath_hal_malloc(sizeof(struct ar2316State)); if (priv == AH_NULL) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: cannot allocate private state\n", __func__); *status = HAL_ENOMEM; /* XXX */ return AH_FALSE; } priv->base.rfDetach = ar2316RfDetach; priv->base.writeRegs = ar2316WriteRegs; priv->base.getRfBank = ar2316GetRfBank; priv->base.setChannel = ar2316SetChannel; priv->base.setRfRegs = ar2316SetRfRegs; priv->base.setPowerTable = ar2316SetPowerTable; priv->base.getChannelMaxMinPower = ar2316GetChannelMaxMinPower; priv->base.getNfAdjust = ar5212GetNfAdjust; ahp->ah_pcdacTable = priv->pcdacTable; ahp->ah_pcdacTableSize = sizeof(priv->pcdacTable); ahp->ah_rfHal = &priv->base; ahp->ah_cwCalRequire = AH_TRUE; /* force initial cal */ return AH_TRUE; } static HAL_BOOL ar2316Probe(struct ath_hal *ah) { return IS_2316(ah); } AH_RF(RF2316, ar2316Probe, ar2316RfAttach); Index: head/sys/dev/ath/ath_hal/ar5212/ar2317.c =================================================================== --- head/sys/dev/ath/ath_hal/ar5212/ar2317.c (revision 298938) +++ head/sys/dev/ath/ath_hal/ar5212/ar2317.c (revision 298939) @@ -1,741 +1,741 @@ /* * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * Copyright (c) 2002-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ar5212/ar5212.h" #include "ar5212/ar5212reg.h" #include "ar5212/ar5212phy.h" #include "ah_eeprom_v3.h" #define AH_5212_2317 #include "ar5212/ar5212.ini" #define N(a) (sizeof(a)/sizeof(a[0])) typedef RAW_DATA_STRUCT_2413 RAW_DATA_STRUCT_2317; typedef RAW_DATA_PER_CHANNEL_2413 RAW_DATA_PER_CHANNEL_2317; #define PWR_TABLE_SIZE_2317 PWR_TABLE_SIZE_2413 struct ar2317State { RF_HAL_FUNCS base; /* public state, must be first */ uint16_t pcdacTable[PWR_TABLE_SIZE_2317]; uint32_t Bank1Data[N(ar5212Bank1_2317)]; uint32_t Bank2Data[N(ar5212Bank2_2317)]; uint32_t Bank3Data[N(ar5212Bank3_2317)]; uint32_t Bank6Data[N(ar5212Bank6_2317)]; uint32_t Bank7Data[N(ar5212Bank7_2317)]; /* * Private state for reduced stack usage. */ /* filled out Vpd table for all pdGains (chanL) */ uint16_t vpdTable_L[MAX_NUM_PDGAINS_PER_CHANNEL] [MAX_PWR_RANGE_IN_HALF_DB]; /* filled out Vpd table for all pdGains (chanR) */ uint16_t vpdTable_R[MAX_NUM_PDGAINS_PER_CHANNEL] [MAX_PWR_RANGE_IN_HALF_DB]; /* filled out Vpd table for all pdGains (interpolated) */ uint16_t vpdTable_I[MAX_NUM_PDGAINS_PER_CHANNEL] [MAX_PWR_RANGE_IN_HALF_DB]; }; #define AR2317(ah) ((struct ar2317State *) AH5212(ah)->ah_rfHal) extern void ar5212ModifyRfBuffer(uint32_t *rfBuf, uint32_t reg32, uint32_t numBits, uint32_t firstBit, uint32_t column); static void ar2317WriteRegs(struct ath_hal *ah, u_int modesIndex, u_int freqIndex, int writes) { HAL_INI_WRITE_ARRAY(ah, ar5212Modes_2317, modesIndex, writes); HAL_INI_WRITE_ARRAY(ah, ar5212Common_2317, 1, writes); HAL_INI_WRITE_ARRAY(ah, ar5212BB_RfGain_2317, freqIndex, writes); } /* * Take the MHz channel value and set the Channel value * * ASSUMES: Writes enabled to analog bus */ static HAL_BOOL ar2317SetChannel(struct ath_hal *ah, const struct ieee80211_channel *chan) { uint16_t freq = ath_hal_gethwchannel(ah, chan); uint32_t channelSel = 0; uint32_t bModeSynth = 0; uint32_t aModeRefSel = 0; uint32_t reg32 = 0; OS_MARK(ah, AH_MARK_SETCHANNEL, freq); if (freq < 4800) { uint32_t txctl; channelSel = freq - 2272 ; channelSel = ath_hal_reverseBits(channelSel, 8); txctl = OS_REG_READ(ah, AR_PHY_CCK_TX_CTRL); if (freq == 2484) { /* Enable channel spreading for channel 14 */ OS_REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, txctl | AR_PHY_CCK_TX_CTRL_JAPAN); } else { OS_REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, txctl &~ AR_PHY_CCK_TX_CTRL_JAPAN); } } else if ((freq % 20) == 0 && freq >= 5120) { channelSel = ath_hal_reverseBits( ((freq - 4800) / 20 << 2), 8); aModeRefSel = ath_hal_reverseBits(3, 2); } else if ((freq % 10) == 0) { channelSel = ath_hal_reverseBits( ((freq - 4800) / 10 << 1), 8); aModeRefSel = ath_hal_reverseBits(2, 2); } else if ((freq % 5) == 0) { channelSel = ath_hal_reverseBits( (freq - 4800) / 5, 8); aModeRefSel = ath_hal_reverseBits(1, 2); } else { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel %u MHz\n", __func__, freq); return AH_FALSE; } reg32 = (channelSel << 4) | (aModeRefSel << 2) | (bModeSynth << 1) | (1 << 12) | 0x1; OS_REG_WRITE(ah, AR_PHY(0x27), reg32 & 0xff); reg32 >>= 8; OS_REG_WRITE(ah, AR_PHY(0x36), reg32 & 0x7f); AH_PRIVATE(ah)->ah_curchan = chan; return AH_TRUE; } /* * Reads EEPROM header info from device structure and programs * all rf registers * * REQUIRES: Access to the analog rf device */ static HAL_BOOL ar2317SetRfRegs(struct ath_hal *ah, const struct ieee80211_channel *chan, uint16_t modesIndex, uint16_t *rfXpdGain) { #define RF_BANK_SETUP(_priv, _ix, _col) do { \ int i; \ for (i = 0; i < N(ar5212Bank##_ix##_2317); i++) \ (_priv)->Bank##_ix##Data[i] = ar5212Bank##_ix##_2317[i][_col];\ } while (0) struct ath_hal_5212 *ahp = AH5212(ah); const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; uint16_t ob2GHz = 0, db2GHz = 0; struct ar2317State *priv = AR2317(ah); int regWrites = 0; HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: chan %u/0x%x modesIndex %u\n", __func__, chan->ic_freq, chan->ic_flags, modesIndex); HALASSERT(priv); /* Setup rf parameters */ if (IEEE80211_IS_CHAN_B(chan)) { ob2GHz = ee->ee_obFor24; db2GHz = ee->ee_dbFor24; } else { ob2GHz = ee->ee_obFor24g; db2GHz = ee->ee_dbFor24g; } /* Bank 1 Write */ RF_BANK_SETUP(priv, 1, 1); /* Bank 2 Write */ RF_BANK_SETUP(priv, 2, modesIndex); /* Bank 3 Write */ RF_BANK_SETUP(priv, 3, modesIndex); /* Bank 6 Write */ RF_BANK_SETUP(priv, 6, modesIndex); ar5212ModifyRfBuffer(priv->Bank6Data, ob2GHz, 3, 193, 0); ar5212ModifyRfBuffer(priv->Bank6Data, db2GHz, 3, 190, 0); /* Bank 7 Setup */ RF_BANK_SETUP(priv, 7, modesIndex); /* Write Analog registers */ HAL_INI_WRITE_BANK(ah, ar5212Bank1_2317, priv->Bank1Data, regWrites); HAL_INI_WRITE_BANK(ah, ar5212Bank2_2317, priv->Bank2Data, regWrites); HAL_INI_WRITE_BANK(ah, ar5212Bank3_2317, priv->Bank3Data, regWrites); HAL_INI_WRITE_BANK(ah, ar5212Bank6_2317, priv->Bank6Data, regWrites); HAL_INI_WRITE_BANK(ah, ar5212Bank7_2317, priv->Bank7Data, regWrites); /* Now that we have reprogrammed rfgain value, clear the flag. */ ahp->ah_rfgainState = HAL_RFGAIN_INACTIVE; return AH_TRUE; #undef RF_BANK_SETUP } /* * Return a reference to the requested RF Bank. */ static uint32_t * ar2317GetRfBank(struct ath_hal *ah, int bank) { struct ar2317State *priv = AR2317(ah); HALASSERT(priv != AH_NULL); switch (bank) { case 1: return priv->Bank1Data; case 2: return priv->Bank2Data; case 3: return priv->Bank3Data; case 6: return priv->Bank6Data; case 7: return priv->Bank7Data; } HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unknown RF Bank %d requested\n", __func__, bank); return AH_NULL; } /* * Return indices surrounding the value in sorted integer lists. * * NB: the input list is assumed to be sorted in ascending order */ static void GetLowerUpperIndex(int16_t v, const uint16_t *lp, uint16_t listSize, uint32_t *vlo, uint32_t *vhi) { int16_t target = v; const int16_t *ep = lp+listSize; const int16_t *tp; /* * Check first and last elements for out-of-bounds conditions. */ if (target < lp[0]) { *vlo = *vhi = 0; return; } if (target >= ep[-1]) { *vlo = *vhi = listSize - 1; return; } /* look for value being near or between 2 values in list */ for (tp = lp; tp < ep; tp++) { /* * If value is close to the current value of the list * then target is not between values, it is one of the values */ if (*tp == target) { *vlo = *vhi = tp - (const int16_t *) lp; return; } /* * Look for value being between current value and next value * if so return these 2 values */ if (target < tp[1]) { *vlo = tp - (const int16_t *) lp; *vhi = *vlo + 1; return; } } } /* * Fill the Vpdlist for indices Pmax-Pmin */ static HAL_BOOL ar2317FillVpdTable(uint32_t pdGainIdx, int16_t Pmin, int16_t Pmax, const int16_t *pwrList, const int16_t *VpdList, uint16_t numIntercepts, uint16_t retVpdList[][64]) { uint16_t ii, jj, kk; int16_t currPwr = (int16_t)(2*Pmin); /* since Pmin is pwr*2 and pwrList is 4*pwr */ uint32_t idxL, idxR; ii = 0; jj = 0; if (numIntercepts < 2) return AH_FALSE; while (ii <= (uint16_t)(Pmax - Pmin)) { GetLowerUpperIndex(currPwr, pwrList, numIntercepts, &(idxL), &(idxR)); if (idxR < 1) idxR = 1; /* extrapolate below */ if (idxL == (uint32_t)(numIntercepts - 1)) idxL = numIntercepts - 2; /* extrapolate above */ if (pwrList[idxL] == pwrList[idxR]) kk = VpdList[idxL]; else kk = (uint16_t) (((currPwr - pwrList[idxL])*VpdList[idxR]+ (pwrList[idxR] - currPwr)*VpdList[idxL])/ (pwrList[idxR] - pwrList[idxL])); retVpdList[pdGainIdx][ii] = kk; ii++; currPwr += 2; /* half dB steps */ } return AH_TRUE; } /* * Returns interpolated or the scaled up interpolated value */ static int16_t interpolate_signed(uint16_t target, uint16_t srcLeft, uint16_t srcRight, int16_t targetLeft, int16_t targetRight) { int16_t rv; if (srcRight != srcLeft) { rv = ((target - srcLeft)*targetRight + (srcRight - target)*targetLeft) / (srcRight - srcLeft); } else { rv = targetLeft; } return rv; } /* * Uses the data points read from EEPROM to reconstruct the pdadc power table * Called by ar2317SetPowerTable() */ static int ar2317getGainBoundariesAndPdadcsForPowers(struct ath_hal *ah, uint16_t channel, const RAW_DATA_STRUCT_2317 *pRawDataset, uint16_t pdGainOverlap_t2, int16_t *pMinCalPower, uint16_t pPdGainBoundaries[], uint16_t pPdGainValues[], uint16_t pPDADCValues[]) { struct ar2317State *priv = AR2317(ah); #define VpdTable_L priv->vpdTable_L #define VpdTable_R priv->vpdTable_R #define VpdTable_I priv->vpdTable_I /* XXX excessive stack usage? */ uint32_t ii, jj, kk; int32_t ss;/* potentially -ve index for taking care of pdGainOverlap */ uint32_t idxL, idxR; uint32_t numPdGainsUsed = 0; /* * If desired to support -ve power levels in future, just * change pwr_I_0 to signed 5-bits. */ int16_t Pmin_t2[MAX_NUM_PDGAINS_PER_CHANNEL]; - /* to accomodate -ve power levels later on. */ + /* to accommodate -ve power levels later on. */ int16_t Pmax_t2[MAX_NUM_PDGAINS_PER_CHANNEL]; - /* to accomodate -ve power levels later on */ + /* to accommodate -ve power levels later on */ uint16_t numVpd = 0; uint16_t Vpd_step; int16_t tmpVal ; uint32_t sizeCurrVpdTable, maxIndex, tgtIndex; /* Get upper lower index */ GetLowerUpperIndex(channel, pRawDataset->pChannels, pRawDataset->numChannels, &(idxL), &(idxR)); for (ii = 0; ii < MAX_NUM_PDGAINS_PER_CHANNEL; ii++) { jj = MAX_NUM_PDGAINS_PER_CHANNEL - ii - 1; /* work backwards 'cause highest pdGain for lowest power */ numVpd = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].numVpd; if (numVpd > 0) { pPdGainValues[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pd_gain; Pmin_t2[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pwr_t4[0]; if (Pmin_t2[numPdGainsUsed] >pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[0]) { Pmin_t2[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[0]; } Pmin_t2[numPdGainsUsed] = (int16_t) (Pmin_t2[numPdGainsUsed] / 2); Pmax_t2[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pwr_t4[numVpd-1]; if (Pmax_t2[numPdGainsUsed] > pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[numVpd-1]) Pmax_t2[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[numVpd-1]; Pmax_t2[numPdGainsUsed] = (int16_t)(Pmax_t2[numPdGainsUsed] / 2); ar2317FillVpdTable( numPdGainsUsed, Pmin_t2[numPdGainsUsed], Pmax_t2[numPdGainsUsed], &(pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pwr_t4[0]), &(pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].Vpd[0]), numVpd, VpdTable_L ); ar2317FillVpdTable( numPdGainsUsed, Pmin_t2[numPdGainsUsed], Pmax_t2[numPdGainsUsed], &(pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[0]), &(pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].Vpd[0]), numVpd, VpdTable_R ); for (kk = 0; kk < (uint16_t)(Pmax_t2[numPdGainsUsed] - Pmin_t2[numPdGainsUsed]); kk++) { VpdTable_I[numPdGainsUsed][kk] = interpolate_signed( channel, pRawDataset->pChannels[idxL], pRawDataset->pChannels[idxR], (int16_t)VpdTable_L[numPdGainsUsed][kk], (int16_t)VpdTable_R[numPdGainsUsed][kk]); } /* fill VpdTable_I for this pdGain */ numPdGainsUsed++; } /* if this pdGain is used */ } *pMinCalPower = Pmin_t2[0]; kk = 0; /* index for the final table */ for (ii = 0; ii < numPdGainsUsed; ii++) { if (ii == (numPdGainsUsed - 1)) pPdGainBoundaries[ii] = Pmax_t2[ii] + PD_GAIN_BOUNDARY_STRETCH_IN_HALF_DB; else pPdGainBoundaries[ii] = (uint16_t) ((Pmax_t2[ii] + Pmin_t2[ii+1]) / 2 ); if (pPdGainBoundaries[ii] > 63) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: clamp pPdGainBoundaries[%d] %d\n", __func__, ii, pPdGainBoundaries[ii]);/*XXX*/ pPdGainBoundaries[ii] = 63; } /* Find starting index for this pdGain */ if (ii == 0) ss = 0; /* for the first pdGain, start from index 0 */ else ss = (pPdGainBoundaries[ii-1] - Pmin_t2[ii]) - pdGainOverlap_t2; Vpd_step = (uint16_t)(VpdTable_I[ii][1] - VpdTable_I[ii][0]); Vpd_step = (uint16_t)((Vpd_step < 1) ? 1 : Vpd_step); /* *-ve ss indicates need to extrapolate data below for this pdGain */ while (ss < 0) { tmpVal = (int16_t)(VpdTable_I[ii][0] + ss*Vpd_step); pPDADCValues[kk++] = (uint16_t)((tmpVal < 0) ? 0 : tmpVal); ss++; } sizeCurrVpdTable = Pmax_t2[ii] - Pmin_t2[ii]; tgtIndex = pPdGainBoundaries[ii] + pdGainOverlap_t2 - Pmin_t2[ii]; maxIndex = (tgtIndex < sizeCurrVpdTable) ? tgtIndex : sizeCurrVpdTable; while (ss < (int16_t)maxIndex) pPDADCValues[kk++] = VpdTable_I[ii][ss++]; Vpd_step = (uint16_t)(VpdTable_I[ii][sizeCurrVpdTable-1] - VpdTable_I[ii][sizeCurrVpdTable-2]); Vpd_step = (uint16_t)((Vpd_step < 1) ? 1 : Vpd_step); /* * for last gain, pdGainBoundary == Pmax_t2, so will * have to extrapolate */ if (tgtIndex > maxIndex) { /* need to extrapolate above */ while(ss < (int16_t)tgtIndex) { tmpVal = (uint16_t) (VpdTable_I[ii][sizeCurrVpdTable-1] + (ss-maxIndex)*Vpd_step); pPDADCValues[kk++] = (tmpVal > 127) ? 127 : tmpVal; ss++; } } /* extrapolated above */ } /* for all pdGainUsed */ while (ii < MAX_NUM_PDGAINS_PER_CHANNEL) { pPdGainBoundaries[ii] = pPdGainBoundaries[ii-1]; ii++; } while (kk < 128) { pPDADCValues[kk] = pPDADCValues[kk-1]; kk++; } return numPdGainsUsed; #undef VpdTable_L #undef VpdTable_R #undef VpdTable_I } static HAL_BOOL ar2317SetPowerTable(struct ath_hal *ah, int16_t *minPower, int16_t *maxPower, const struct ieee80211_channel *chan, uint16_t *rfXpdGain) { struct ath_hal_5212 *ahp = AH5212(ah); const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; const RAW_DATA_STRUCT_2317 *pRawDataset = AH_NULL; uint16_t pdGainOverlap_t2; int16_t minCalPower2317_t2; uint16_t *pdadcValues = ahp->ah_pcdacTable; uint16_t gainBoundaries[4]; uint32_t reg32, regoffset; int i, numPdGainsUsed; #ifndef AH_USE_INIPDGAIN uint32_t tpcrg1; #endif HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: chan 0x%x flag 0x%x\n", __func__, chan->ic_freq, chan->ic_flags); if (IEEE80211_IS_CHAN_G(chan) || IEEE80211_IS_CHAN_108G(chan)) pRawDataset = &ee->ee_rawDataset2413[headerInfo11G]; else if (IEEE80211_IS_CHAN_B(chan)) pRawDataset = &ee->ee_rawDataset2413[headerInfo11B]; else { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: illegal mode\n", __func__); return AH_FALSE; } pdGainOverlap_t2 = (uint16_t) SM(OS_REG_READ(ah, AR_PHY_TPCRG5), AR_PHY_TPCRG5_PD_GAIN_OVERLAP); numPdGainsUsed = ar2317getGainBoundariesAndPdadcsForPowers(ah, chan->channel, pRawDataset, pdGainOverlap_t2, &minCalPower2317_t2,gainBoundaries, rfXpdGain, pdadcValues); HALASSERT(1 <= numPdGainsUsed && numPdGainsUsed <= 3); #ifdef AH_USE_INIPDGAIN /* * Use pd_gains curve from eeprom; Atheros always uses * the default curve from the ini file but some vendors * (e.g. Zcomax) want to override this curve and not * honoring their settings results in tx power 5dBm low. */ OS_REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_NUM_PD_GAIN, (pRawDataset->pDataPerChannel[0].numPdGains - 1)); #else tpcrg1 = OS_REG_READ(ah, AR_PHY_TPCRG1); tpcrg1 = (tpcrg1 &~ AR_PHY_TPCRG1_NUM_PD_GAIN) | SM(numPdGainsUsed-1, AR_PHY_TPCRG1_NUM_PD_GAIN); switch (numPdGainsUsed) { case 3: tpcrg1 &= ~AR_PHY_TPCRG1_PDGAIN_SETTING3; tpcrg1 |= SM(rfXpdGain[2], AR_PHY_TPCRG1_PDGAIN_SETTING3); /* fall thru... */ case 2: tpcrg1 &= ~AR_PHY_TPCRG1_PDGAIN_SETTING2; tpcrg1 |= SM(rfXpdGain[1], AR_PHY_TPCRG1_PDGAIN_SETTING2); /* fall thru... */ case 1: tpcrg1 &= ~AR_PHY_TPCRG1_PDGAIN_SETTING1; tpcrg1 |= SM(rfXpdGain[0], AR_PHY_TPCRG1_PDGAIN_SETTING1); break; } #ifdef AH_DEBUG if (tpcrg1 != OS_REG_READ(ah, AR_PHY_TPCRG1)) HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: using non-default " "pd_gains (default 0x%x, calculated 0x%x)\n", __func__, OS_REG_READ(ah, AR_PHY_TPCRG1), tpcrg1); #endif OS_REG_WRITE(ah, AR_PHY_TPCRG1, tpcrg1); #endif /* * Note the pdadc table may not start at 0 dBm power, could be * negative or greater than 0. Need to offset the power * values by the amount of minPower for griffin */ if (minCalPower2317_t2 != 0) ahp->ah_txPowerIndexOffset = (int16_t)(0 - minCalPower2317_t2); else ahp->ah_txPowerIndexOffset = 0; /* Finally, write the power values into the baseband power table */ regoffset = 0x9800 + (672 <<2); /* beginning of pdadc table in griffin */ for (i = 0; i < 32; i++) { reg32 = ((pdadcValues[4*i + 0] & 0xFF) << 0) | ((pdadcValues[4*i + 1] & 0xFF) << 8) | ((pdadcValues[4*i + 2] & 0xFF) << 16) | ((pdadcValues[4*i + 3] & 0xFF) << 24) ; OS_REG_WRITE(ah, regoffset, reg32); regoffset += 4; } OS_REG_WRITE(ah, AR_PHY_TPCRG5, SM(pdGainOverlap_t2, AR_PHY_TPCRG5_PD_GAIN_OVERLAP) | SM(gainBoundaries[0], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1) | SM(gainBoundaries[1], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2) | SM(gainBoundaries[2], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3) | SM(gainBoundaries[3], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4)); return AH_TRUE; } static int16_t ar2317GetMinPower(struct ath_hal *ah, const RAW_DATA_PER_CHANNEL_2317 *data) { uint32_t ii,jj; uint16_t Pmin=0,numVpd; for (ii = 0; ii < MAX_NUM_PDGAINS_PER_CHANNEL; ii++) { jj = MAX_NUM_PDGAINS_PER_CHANNEL - ii - 1; /* work backwards 'cause highest pdGain for lowest power */ numVpd = data->pDataPerPDGain[jj].numVpd; if (numVpd > 0) { Pmin = data->pDataPerPDGain[jj].pwr_t4[0]; return(Pmin); } } return(Pmin); } static int16_t ar2317GetMaxPower(struct ath_hal *ah, const RAW_DATA_PER_CHANNEL_2317 *data) { uint32_t ii; uint16_t Pmax=0,numVpd; uint16_t vpdmax; for (ii=0; ii< MAX_NUM_PDGAINS_PER_CHANNEL; ii++) { /* work forwards cuase lowest pdGain for highest power */ numVpd = data->pDataPerPDGain[ii].numVpd; if (numVpd > 0) { Pmax = data->pDataPerPDGain[ii].pwr_t4[numVpd-1]; vpdmax = data->pDataPerPDGain[ii].Vpd[numVpd-1]; return(Pmax); } } return(Pmax); } static HAL_BOOL ar2317GetChannelMaxMinPower(struct ath_hal *ah, const struct ieee80211_channel *chan, int16_t *maxPow, int16_t *minPow) { uint16_t freq = chan->ic_freq; /* NB: never mapped */ const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; const RAW_DATA_STRUCT_2317 *pRawDataset = AH_NULL; const RAW_DATA_PER_CHANNEL_2317 *data=AH_NULL; uint16_t numChannels; int totalD,totalF, totalMin,last, i; *maxPow = 0; if (IEEE80211_IS_CHAN_G(chan) || IEEE80211_IS_CHAN_108G(chan)) pRawDataset = &ee->ee_rawDataset2413[headerInfo11G]; else if (IEEE80211_IS_CHAN_B(chan)) pRawDataset = &ee->ee_rawDataset2413[headerInfo11B]; else return(AH_FALSE); numChannels = pRawDataset->numChannels; data = pRawDataset->pDataPerChannel; /* Make sure the channel is in the range of the TP values * (freq piers) */ if (numChannels < 1) return(AH_FALSE); if ((freq < data[0].channelValue) || (freq > data[numChannels-1].channelValue)) { if (freq < data[0].channelValue) { *maxPow = ar2317GetMaxPower(ah, &data[0]); *minPow = ar2317GetMinPower(ah, &data[0]); return(AH_TRUE); } else { *maxPow = ar2317GetMaxPower(ah, &data[numChannels - 1]); *minPow = ar2317GetMinPower(ah, &data[numChannels - 1]); return(AH_TRUE); } } /* Linearly interpolate the power value now */ for (last=0,i=0; (i data[i].channelValue); last = i++); totalD = data[i].channelValue - data[last].channelValue; if (totalD > 0) { totalF = ar2317GetMaxPower(ah, &data[i]) - ar2317GetMaxPower(ah, &data[last]); *maxPow = (int8_t) ((totalF*(freq-data[last].channelValue) + ar2317GetMaxPower(ah, &data[last])*totalD)/totalD); totalMin = ar2317GetMinPower(ah, &data[i]) - ar2317GetMinPower(ah, &data[last]); *minPow = (int8_t) ((totalMin*(freq-data[last].channelValue) + ar2317GetMinPower(ah, &data[last])*totalD)/totalD); return(AH_TRUE); } else { if (freq == data[i].channelValue) { *maxPow = ar2317GetMaxPower(ah, &data[i]); *minPow = ar2317GetMinPower(ah, &data[i]); return(AH_TRUE); } else return(AH_FALSE); } } /* * Free memory for analog bank scratch buffers */ static void ar2317RfDetach(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); HALASSERT(ahp->ah_rfHal != AH_NULL); ath_hal_free(ahp->ah_rfHal); ahp->ah_rfHal = AH_NULL; } /* * Allocate memory for analog bank scratch buffers * Scratch Buffer will be reinitialized every reset so no need to zero now */ static HAL_BOOL ar2317RfAttach(struct ath_hal *ah, HAL_STATUS *status) { struct ath_hal_5212 *ahp = AH5212(ah); struct ar2317State *priv; HALASSERT(ah->ah_magic == AR5212_MAGIC); HALASSERT(ahp->ah_rfHal == AH_NULL); priv = ath_hal_malloc(sizeof(struct ar2317State)); if (priv == AH_NULL) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: cannot allocate private state\n", __func__); *status = HAL_ENOMEM; /* XXX */ return AH_FALSE; } priv->base.rfDetach = ar2317RfDetach; priv->base.writeRegs = ar2317WriteRegs; priv->base.getRfBank = ar2317GetRfBank; priv->base.setChannel = ar2317SetChannel; priv->base.setRfRegs = ar2317SetRfRegs; priv->base.setPowerTable = ar2317SetPowerTable; priv->base.getChannelMaxMinPower = ar2317GetChannelMaxMinPower; priv->base.getNfAdjust = ar5212GetNfAdjust; ahp->ah_pcdacTable = priv->pcdacTable; ahp->ah_pcdacTableSize = sizeof(priv->pcdacTable); ahp->ah_rfHal = &priv->base; return AH_TRUE; } static HAL_BOOL ar2317Probe(struct ath_hal *ah) { return IS_2317(ah); } AH_RF(RF2317, ar2317Probe, ar2317RfAttach); Index: head/sys/dev/ath/ath_hal/ar5212/ar2413.c =================================================================== --- head/sys/dev/ath/ath_hal/ar5212/ar2413.c (revision 298938) +++ head/sys/dev/ath/ath_hal/ar5212/ar2413.c (revision 298939) @@ -1,756 +1,756 @@ /* * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * Copyright (c) 2002-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ar5212/ar5212.h" #include "ar5212/ar5212reg.h" #include "ar5212/ar5212phy.h" #include "ah_eeprom_v3.h" #define AH_5212_2413 #include "ar5212/ar5212.ini" #define N(a) (sizeof(a)/sizeof(a[0])) struct ar2413State { RF_HAL_FUNCS base; /* public state, must be first */ uint16_t pcdacTable[PWR_TABLE_SIZE_2413]; uint32_t Bank1Data[N(ar5212Bank1_2413)]; uint32_t Bank2Data[N(ar5212Bank2_2413)]; uint32_t Bank3Data[N(ar5212Bank3_2413)]; uint32_t Bank6Data[N(ar5212Bank6_2413)]; uint32_t Bank7Data[N(ar5212Bank7_2413)]; /* * Private state for reduced stack usage. */ /* filled out Vpd table for all pdGains (chanL) */ uint16_t vpdTable_L[MAX_NUM_PDGAINS_PER_CHANNEL] [MAX_PWR_RANGE_IN_HALF_DB]; /* filled out Vpd table for all pdGains (chanR) */ uint16_t vpdTable_R[MAX_NUM_PDGAINS_PER_CHANNEL] [MAX_PWR_RANGE_IN_HALF_DB]; /* filled out Vpd table for all pdGains (interpolated) */ uint16_t vpdTable_I[MAX_NUM_PDGAINS_PER_CHANNEL] [MAX_PWR_RANGE_IN_HALF_DB]; }; #define AR2413(ah) ((struct ar2413State *) AH5212(ah)->ah_rfHal) extern void ar5212ModifyRfBuffer(uint32_t *rfBuf, uint32_t reg32, uint32_t numBits, uint32_t firstBit, uint32_t column); static void ar2413WriteRegs(struct ath_hal *ah, u_int modesIndex, u_int freqIndex, int writes) { HAL_INI_WRITE_ARRAY(ah, ar5212Modes_2413, modesIndex, writes); HAL_INI_WRITE_ARRAY(ah, ar5212Common_2413, 1, writes); HAL_INI_WRITE_ARRAY(ah, ar5212BB_RfGain_2413, freqIndex, writes); } /* * Take the MHz channel value and set the Channel value * * ASSUMES: Writes enabled to analog bus */ static HAL_BOOL ar2413SetChannel(struct ath_hal *ah, const struct ieee80211_channel *chan) { uint16_t freq = ath_hal_gethwchannel(ah, chan); uint32_t channelSel = 0; uint32_t bModeSynth = 0; uint32_t aModeRefSel = 0; uint32_t reg32 = 0; OS_MARK(ah, AH_MARK_SETCHANNEL, freq); if (freq < 4800) { uint32_t txctl; if (((freq - 2192) % 5) == 0) { channelSel = ((freq - 672) * 2 - 3040)/10; bModeSynth = 0; } else if (((freq - 2224) % 5) == 0) { channelSel = ((freq - 704) * 2 - 3040) / 10; bModeSynth = 1; } else { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel %u MHz\n", __func__, freq); return AH_FALSE; } channelSel = (channelSel << 2) & 0xff; channelSel = ath_hal_reverseBits(channelSel, 8); txctl = OS_REG_READ(ah, AR_PHY_CCK_TX_CTRL); if (freq == 2484) { /* Enable channel spreading for channel 14 */ OS_REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, txctl | AR_PHY_CCK_TX_CTRL_JAPAN); } else { OS_REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, txctl &~ AR_PHY_CCK_TX_CTRL_JAPAN); } } else if (((freq % 5) == 2) && (freq <= 5435)) { freq = freq - 2; /* Align to even 5MHz raster */ channelSel = ath_hal_reverseBits( (uint32_t)(((freq - 4800)*10)/25 + 1), 8); aModeRefSel = ath_hal_reverseBits(0, 2); } else if ((freq % 20) == 0 && freq >= 5120) { channelSel = ath_hal_reverseBits( ((freq - 4800) / 20 << 2), 8); aModeRefSel = ath_hal_reverseBits(3, 2); } else if ((freq % 10) == 0) { channelSel = ath_hal_reverseBits( ((freq - 4800) / 10 << 1), 8); aModeRefSel = ath_hal_reverseBits(2, 2); } else if ((freq % 5) == 0) { channelSel = ath_hal_reverseBits( (freq - 4800) / 5, 8); aModeRefSel = ath_hal_reverseBits(1, 2); } else { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel %u MHz\n", __func__, freq); return AH_FALSE; } reg32 = (channelSel << 4) | (aModeRefSel << 2) | (bModeSynth << 1) | (1 << 12) | 0x1; OS_REG_WRITE(ah, AR_PHY(0x27), reg32 & 0xff); reg32 >>= 8; OS_REG_WRITE(ah, AR_PHY(0x36), reg32 & 0x7f); AH_PRIVATE(ah)->ah_curchan = chan; return AH_TRUE; } /* * Reads EEPROM header info from device structure and programs * all rf registers * * REQUIRES: Access to the analog rf device */ static HAL_BOOL ar2413SetRfRegs(struct ath_hal *ah, const struct ieee80211_channel *chan, uint16_t modesIndex, uint16_t *rfXpdGain) { #define RF_BANK_SETUP(_priv, _ix, _col) do { \ int i; \ for (i = 0; i < N(ar5212Bank##_ix##_2413); i++) \ (_priv)->Bank##_ix##Data[i] = ar5212Bank##_ix##_2413[i][_col];\ } while (0) struct ath_hal_5212 *ahp = AH5212(ah); const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; uint16_t ob2GHz = 0, db2GHz = 0; struct ar2413State *priv = AR2413(ah); int regWrites = 0; HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: chan %u/0x%x modesIndex %u\n", __func__, chan->ic_freq, chan->ic_flags, modesIndex); HALASSERT(priv); /* Setup rf parameters */ if (IEEE80211_IS_CHAN_B(chan)) { ob2GHz = ee->ee_obFor24; db2GHz = ee->ee_dbFor24; } else { ob2GHz = ee->ee_obFor24g; db2GHz = ee->ee_dbFor24g; } /* Bank 1 Write */ RF_BANK_SETUP(priv, 1, 1); /* Bank 2 Write */ RF_BANK_SETUP(priv, 2, modesIndex); /* Bank 3 Write */ RF_BANK_SETUP(priv, 3, modesIndex); /* Bank 6 Write */ RF_BANK_SETUP(priv, 6, modesIndex); ar5212ModifyRfBuffer(priv->Bank6Data, ob2GHz, 3, 168, 0); ar5212ModifyRfBuffer(priv->Bank6Data, db2GHz, 3, 165, 0); /* Bank 7 Setup */ RF_BANK_SETUP(priv, 7, modesIndex); /* Write Analog registers */ HAL_INI_WRITE_BANK(ah, ar5212Bank1_2413, priv->Bank1Data, regWrites); HAL_INI_WRITE_BANK(ah, ar5212Bank2_2413, priv->Bank2Data, regWrites); HAL_INI_WRITE_BANK(ah, ar5212Bank3_2413, priv->Bank3Data, regWrites); HAL_INI_WRITE_BANK(ah, ar5212Bank6_2413, priv->Bank6Data, regWrites); HAL_INI_WRITE_BANK(ah, ar5212Bank7_2413, priv->Bank7Data, regWrites); /* Now that we have reprogrammed rfgain value, clear the flag. */ ahp->ah_rfgainState = HAL_RFGAIN_INACTIVE; return AH_TRUE; #undef RF_BANK_SETUP } /* * Return a reference to the requested RF Bank. */ static uint32_t * ar2413GetRfBank(struct ath_hal *ah, int bank) { struct ar2413State *priv = AR2413(ah); HALASSERT(priv != AH_NULL); switch (bank) { case 1: return priv->Bank1Data; case 2: return priv->Bank2Data; case 3: return priv->Bank3Data; case 6: return priv->Bank6Data; case 7: return priv->Bank7Data; } HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unknown RF Bank %d requested\n", __func__, bank); return AH_NULL; } /* * Return indices surrounding the value in sorted integer lists. * * NB: the input list is assumed to be sorted in ascending order */ static void GetLowerUpperIndex(int16_t v, const uint16_t *lp, uint16_t listSize, uint32_t *vlo, uint32_t *vhi) { int16_t target = v; const uint16_t *ep = lp+listSize; const uint16_t *tp; /* * Check first and last elements for out-of-bounds conditions. */ if (target < lp[0]) { *vlo = *vhi = 0; return; } if (target >= ep[-1]) { *vlo = *vhi = listSize - 1; return; } /* look for value being near or between 2 values in list */ for (tp = lp; tp < ep; tp++) { /* * If value is close to the current value of the list * then target is not between values, it is one of the values */ if (*tp == target) { *vlo = *vhi = tp - (const uint16_t *) lp; return; } /* * Look for value being between current value and next value * if so return these 2 values */ if (target < tp[1]) { *vlo = tp - (const uint16_t *) lp; *vhi = *vlo + 1; return; } } } /* * Fill the Vpdlist for indices Pmax-Pmin */ static HAL_BOOL ar2413FillVpdTable(uint32_t pdGainIdx, int16_t Pmin, int16_t Pmax, const int16_t *pwrList, const uint16_t *VpdList, uint16_t numIntercepts, uint16_t retVpdList[][64]) { uint16_t ii, jj, kk; int16_t currPwr = (int16_t)(2*Pmin); /* since Pmin is pwr*2 and pwrList is 4*pwr */ uint32_t idxL, idxR; ii = 0; jj = 0; if (numIntercepts < 2) return AH_FALSE; while (ii <= (uint16_t)(Pmax - Pmin)) { GetLowerUpperIndex(currPwr, (const uint16_t *) pwrList, numIntercepts, &(idxL), &(idxR)); if (idxR < 1) idxR = 1; /* extrapolate below */ if (idxL == (uint32_t)(numIntercepts - 1)) idxL = numIntercepts - 2; /* extrapolate above */ if (pwrList[idxL] == pwrList[idxR]) kk = VpdList[idxL]; else kk = (uint16_t) (((currPwr - pwrList[idxL])*VpdList[idxR]+ (pwrList[idxR] - currPwr)*VpdList[idxL])/ (pwrList[idxR] - pwrList[idxL])); retVpdList[pdGainIdx][ii] = kk; ii++; currPwr += 2; /* half dB steps */ } return AH_TRUE; } /* * Returns interpolated or the scaled up interpolated value */ static int16_t interpolate_signed(uint16_t target, uint16_t srcLeft, uint16_t srcRight, int16_t targetLeft, int16_t targetRight) { int16_t rv; if (srcRight != srcLeft) { rv = ((target - srcLeft)*targetRight + (srcRight - target)*targetLeft) / (srcRight - srcLeft); } else { rv = targetLeft; } return rv; } /* * Uses the data points read from EEPROM to reconstruct the pdadc power table * Called by ar2413SetPowerTable() */ static int ar2413getGainBoundariesAndPdadcsForPowers(struct ath_hal *ah, uint16_t channel, const RAW_DATA_STRUCT_2413 *pRawDataset, uint16_t pdGainOverlap_t2, int16_t *pMinCalPower, uint16_t pPdGainBoundaries[], uint16_t pPdGainValues[], uint16_t pPDADCValues[]) { struct ar2413State *priv = AR2413(ah); #define VpdTable_L priv->vpdTable_L #define VpdTable_R priv->vpdTable_R #define VpdTable_I priv->vpdTable_I uint32_t ii, jj, kk; int32_t ss;/* potentially -ve index for taking care of pdGainOverlap */ uint32_t idxL, idxR; uint32_t numPdGainsUsed = 0; /* * If desired to support -ve power levels in future, just * change pwr_I_0 to signed 5-bits. */ int16_t Pmin_t2[MAX_NUM_PDGAINS_PER_CHANNEL]; - /* to accomodate -ve power levels later on. */ + /* to accommodate -ve power levels later on. */ int16_t Pmax_t2[MAX_NUM_PDGAINS_PER_CHANNEL]; - /* to accomodate -ve power levels later on */ + /* to accommodate -ve power levels later on */ uint16_t numVpd = 0; uint16_t Vpd_step; int16_t tmpVal ; uint32_t sizeCurrVpdTable, maxIndex, tgtIndex; /* Get upper lower index */ GetLowerUpperIndex(channel, pRawDataset->pChannels, pRawDataset->numChannels, &(idxL), &(idxR)); for (ii = 0; ii < MAX_NUM_PDGAINS_PER_CHANNEL; ii++) { jj = MAX_NUM_PDGAINS_PER_CHANNEL - ii - 1; /* work backwards 'cause highest pdGain for lowest power */ numVpd = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].numVpd; if (numVpd > 0) { pPdGainValues[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pd_gain; Pmin_t2[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pwr_t4[0]; if (Pmin_t2[numPdGainsUsed] >pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[0]) { Pmin_t2[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[0]; } Pmin_t2[numPdGainsUsed] = (int16_t) (Pmin_t2[numPdGainsUsed] / 2); Pmax_t2[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pwr_t4[numVpd-1]; if (Pmax_t2[numPdGainsUsed] > pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[numVpd-1]) Pmax_t2[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[numVpd-1]; Pmax_t2[numPdGainsUsed] = (int16_t)(Pmax_t2[numPdGainsUsed] / 2); ar2413FillVpdTable( numPdGainsUsed, Pmin_t2[numPdGainsUsed], Pmax_t2[numPdGainsUsed], &(pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pwr_t4[0]), &(pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].Vpd[0]), numVpd, VpdTable_L ); ar2413FillVpdTable( numPdGainsUsed, Pmin_t2[numPdGainsUsed], Pmax_t2[numPdGainsUsed], &(pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[0]), &(pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].Vpd[0]), numVpd, VpdTable_R ); for (kk = 0; kk < (uint16_t)(Pmax_t2[numPdGainsUsed] - Pmin_t2[numPdGainsUsed]); kk++) { VpdTable_I[numPdGainsUsed][kk] = interpolate_signed( channel, pRawDataset->pChannels[idxL], pRawDataset->pChannels[idxR], (int16_t)VpdTable_L[numPdGainsUsed][kk], (int16_t)VpdTable_R[numPdGainsUsed][kk]); } /* fill VpdTable_I for this pdGain */ numPdGainsUsed++; } /* if this pdGain is used */ } *pMinCalPower = Pmin_t2[0]; kk = 0; /* index for the final table */ for (ii = 0; ii < numPdGainsUsed; ii++) { if (ii == (numPdGainsUsed - 1)) pPdGainBoundaries[ii] = Pmax_t2[ii] + PD_GAIN_BOUNDARY_STRETCH_IN_HALF_DB; else pPdGainBoundaries[ii] = (uint16_t) ((Pmax_t2[ii] + Pmin_t2[ii+1]) / 2 ); if (pPdGainBoundaries[ii] > 63) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: clamp pPdGainBoundaries[%d] %d\n", __func__, ii, pPdGainBoundaries[ii]);/*XXX*/ pPdGainBoundaries[ii] = 63; } /* Find starting index for this pdGain */ if (ii == 0) ss = 0; /* for the first pdGain, start from index 0 */ else ss = (pPdGainBoundaries[ii-1] - Pmin_t2[ii]) - pdGainOverlap_t2; Vpd_step = (uint16_t)(VpdTable_I[ii][1] - VpdTable_I[ii][0]); Vpd_step = (uint16_t)((Vpd_step < 1) ? 1 : Vpd_step); /* *-ve ss indicates need to extrapolate data below for this pdGain */ while (ss < 0) { tmpVal = (int16_t)(VpdTable_I[ii][0] + ss*Vpd_step); pPDADCValues[kk++] = (uint16_t)((tmpVal < 0) ? 0 : tmpVal); ss++; } sizeCurrVpdTable = Pmax_t2[ii] - Pmin_t2[ii]; tgtIndex = pPdGainBoundaries[ii] + pdGainOverlap_t2 - Pmin_t2[ii]; maxIndex = (tgtIndex < sizeCurrVpdTable) ? tgtIndex : sizeCurrVpdTable; while (ss < (int16_t)maxIndex) pPDADCValues[kk++] = VpdTable_I[ii][ss++]; Vpd_step = (uint16_t)(VpdTable_I[ii][sizeCurrVpdTable-1] - VpdTable_I[ii][sizeCurrVpdTable-2]); Vpd_step = (uint16_t)((Vpd_step < 1) ? 1 : Vpd_step); /* * for last gain, pdGainBoundary == Pmax_t2, so will * have to extrapolate */ if (tgtIndex > maxIndex) { /* need to extrapolate above */ while(ss < (int16_t)tgtIndex) { tmpVal = (uint16_t) (VpdTable_I[ii][sizeCurrVpdTable-1] + (ss-maxIndex)*Vpd_step); pPDADCValues[kk++] = (tmpVal > 127) ? 127 : tmpVal; ss++; } } /* extrapolated above */ } /* for all pdGainUsed */ while (ii < MAX_NUM_PDGAINS_PER_CHANNEL) { pPdGainBoundaries[ii] = pPdGainBoundaries[ii-1]; ii++; } while (kk < 128) { pPDADCValues[kk] = pPDADCValues[kk-1]; kk++; } return numPdGainsUsed; #undef VpdTable_L #undef VpdTable_R #undef VpdTable_I } static HAL_BOOL ar2413SetPowerTable(struct ath_hal *ah, int16_t *minPower, int16_t *maxPower, const struct ieee80211_channel *chan, uint16_t *rfXpdGain) { uint16_t freq = ath_hal_gethwchannel(ah, chan); struct ath_hal_5212 *ahp = AH5212(ah); const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; const RAW_DATA_STRUCT_2413 *pRawDataset = AH_NULL; uint16_t pdGainOverlap_t2; int16_t minCalPower2413_t2; uint16_t *pdadcValues = ahp->ah_pcdacTable; uint16_t gainBoundaries[4]; uint32_t reg32, regoffset; int i, numPdGainsUsed; #ifndef AH_USE_INIPDGAIN uint32_t tpcrg1; #endif HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: chan 0x%x flag 0x%x\n", __func__, freq, chan->ic_flags); if (IEEE80211_IS_CHAN_G(chan) || IEEE80211_IS_CHAN_108G(chan)) pRawDataset = &ee->ee_rawDataset2413[headerInfo11G]; else if (IEEE80211_IS_CHAN_B(chan)) pRawDataset = &ee->ee_rawDataset2413[headerInfo11B]; else { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: illegal mode\n", __func__); return AH_FALSE; } pdGainOverlap_t2 = (uint16_t) SM(OS_REG_READ(ah, AR_PHY_TPCRG5), AR_PHY_TPCRG5_PD_GAIN_OVERLAP); numPdGainsUsed = ar2413getGainBoundariesAndPdadcsForPowers(ah, freq, pRawDataset, pdGainOverlap_t2, &minCalPower2413_t2,gainBoundaries, rfXpdGain, pdadcValues); HALASSERT(1 <= numPdGainsUsed && numPdGainsUsed <= 3); #ifdef AH_USE_INIPDGAIN /* * Use pd_gains curve from eeprom; Atheros always uses * the default curve from the ini file but some vendors * (e.g. Zcomax) want to override this curve and not * honoring their settings results in tx power 5dBm low. */ OS_REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_NUM_PD_GAIN, (pRawDataset->pDataPerChannel[0].numPdGains - 1)); #else tpcrg1 = OS_REG_READ(ah, AR_PHY_TPCRG1); tpcrg1 = (tpcrg1 &~ AR_PHY_TPCRG1_NUM_PD_GAIN) | SM(numPdGainsUsed-1, AR_PHY_TPCRG1_NUM_PD_GAIN); switch (numPdGainsUsed) { case 3: tpcrg1 &= ~AR_PHY_TPCRG1_PDGAIN_SETTING3; tpcrg1 |= SM(rfXpdGain[2], AR_PHY_TPCRG1_PDGAIN_SETTING3); /* fall thru... */ case 2: tpcrg1 &= ~AR_PHY_TPCRG1_PDGAIN_SETTING2; tpcrg1 |= SM(rfXpdGain[1], AR_PHY_TPCRG1_PDGAIN_SETTING2); /* fall thru... */ case 1: tpcrg1 &= ~AR_PHY_TPCRG1_PDGAIN_SETTING1; tpcrg1 |= SM(rfXpdGain[0], AR_PHY_TPCRG1_PDGAIN_SETTING1); break; } #ifdef AH_DEBUG if (tpcrg1 != OS_REG_READ(ah, AR_PHY_TPCRG1)) HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: using non-default " "pd_gains (default 0x%x, calculated 0x%x)\n", __func__, OS_REG_READ(ah, AR_PHY_TPCRG1), tpcrg1); #endif OS_REG_WRITE(ah, AR_PHY_TPCRG1, tpcrg1); #endif /* * Note the pdadc table may not start at 0 dBm power, could be * negative or greater than 0. Need to offset the power * values by the amount of minPower for griffin */ if (minCalPower2413_t2 != 0) ahp->ah_txPowerIndexOffset = (int16_t)(0 - minCalPower2413_t2); else ahp->ah_txPowerIndexOffset = 0; /* Finally, write the power values into the baseband power table */ regoffset = 0x9800 + (672 <<2); /* beginning of pdadc table in griffin */ for (i = 0; i < 32; i++) { reg32 = ((pdadcValues[4*i + 0] & 0xFF) << 0) | ((pdadcValues[4*i + 1] & 0xFF) << 8) | ((pdadcValues[4*i + 2] & 0xFF) << 16) | ((pdadcValues[4*i + 3] & 0xFF) << 24) ; OS_REG_WRITE(ah, regoffset, reg32); regoffset += 4; } OS_REG_WRITE(ah, AR_PHY_TPCRG5, SM(pdGainOverlap_t2, AR_PHY_TPCRG5_PD_GAIN_OVERLAP) | SM(gainBoundaries[0], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1) | SM(gainBoundaries[1], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2) | SM(gainBoundaries[2], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3) | SM(gainBoundaries[3], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4)); return AH_TRUE; } static int16_t ar2413GetMinPower(struct ath_hal *ah, const RAW_DATA_PER_CHANNEL_2413 *data) { uint32_t ii,jj; uint16_t Pmin=0,numVpd; for (ii = 0; ii < MAX_NUM_PDGAINS_PER_CHANNEL; ii++) { jj = MAX_NUM_PDGAINS_PER_CHANNEL - ii - 1; /* work backwards 'cause highest pdGain for lowest power */ numVpd = data->pDataPerPDGain[jj].numVpd; if (numVpd > 0) { Pmin = data->pDataPerPDGain[jj].pwr_t4[0]; return(Pmin); } } return(Pmin); } static int16_t ar2413GetMaxPower(struct ath_hal *ah, const RAW_DATA_PER_CHANNEL_2413 *data) { uint32_t ii; uint16_t Pmax=0,numVpd; for (ii=0; ii< MAX_NUM_PDGAINS_PER_CHANNEL; ii++) { /* work forwards cuase lowest pdGain for highest power */ numVpd = data->pDataPerPDGain[ii].numVpd; if (numVpd > 0) { Pmax = data->pDataPerPDGain[ii].pwr_t4[numVpd-1]; return(Pmax); } } return(Pmax); } static HAL_BOOL ar2413GetChannelMaxMinPower(struct ath_hal *ah, const struct ieee80211_channel *chan, int16_t *maxPow, int16_t *minPow) { uint16_t freq = chan->ic_freq; /* NB: never mapped */ const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; const RAW_DATA_STRUCT_2413 *pRawDataset = AH_NULL; const RAW_DATA_PER_CHANNEL_2413 *data = AH_NULL; uint16_t numChannels; int totalD,totalF, totalMin,last, i; *maxPow = 0; if (IEEE80211_IS_CHAN_G(chan) || IEEE80211_IS_CHAN_108G(chan)) pRawDataset = &ee->ee_rawDataset2413[headerInfo11G]; else if (IEEE80211_IS_CHAN_B(chan)) pRawDataset = &ee->ee_rawDataset2413[headerInfo11B]; else return(AH_FALSE); numChannels = pRawDataset->numChannels; data = pRawDataset->pDataPerChannel; /* Make sure the channel is in the range of the TP values * (freq piers) */ if (numChannels < 1) return(AH_FALSE); if ((freq < data[0].channelValue) || (freq > data[numChannels-1].channelValue)) { if (freq < data[0].channelValue) { *maxPow = ar2413GetMaxPower(ah, &data[0]); *minPow = ar2413GetMinPower(ah, &data[0]); return(AH_TRUE); } else { *maxPow = ar2413GetMaxPower(ah, &data[numChannels - 1]); *minPow = ar2413GetMinPower(ah, &data[numChannels - 1]); return(AH_TRUE); } } /* Linearly interpolate the power value now */ for (last=0,i=0; (i data[i].channelValue); last = i++); totalD = data[i].channelValue - data[last].channelValue; if (totalD > 0) { totalF = ar2413GetMaxPower(ah, &data[i]) - ar2413GetMaxPower(ah, &data[last]); *maxPow = (int8_t) ((totalF*(freq-data[last].channelValue) + ar2413GetMaxPower(ah, &data[last])*totalD)/totalD); totalMin = ar2413GetMinPower(ah, &data[i]) - ar2413GetMinPower(ah, &data[last]); *minPow = (int8_t) ((totalMin*(freq-data[last].channelValue) + ar2413GetMinPower(ah, &data[last])*totalD)/totalD); return(AH_TRUE); } else { if (freq == data[i].channelValue) { *maxPow = ar2413GetMaxPower(ah, &data[i]); *minPow = ar2413GetMinPower(ah, &data[i]); return(AH_TRUE); } else return(AH_FALSE); } } /* * Free memory for analog bank scratch buffers */ static void ar2413RfDetach(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); HALASSERT(ahp->ah_rfHal != AH_NULL); ath_hal_free(ahp->ah_rfHal); ahp->ah_rfHal = AH_NULL; } /* * Allocate memory for analog bank scratch buffers * Scratch Buffer will be reinitialized every reset so no need to zero now */ static HAL_BOOL ar2413RfAttach(struct ath_hal *ah, HAL_STATUS *status) { struct ath_hal_5212 *ahp = AH5212(ah); struct ar2413State *priv; HALASSERT(ah->ah_magic == AR5212_MAGIC); HALASSERT(ahp->ah_rfHal == AH_NULL); priv = ath_hal_malloc(sizeof(struct ar2413State)); if (priv == AH_NULL) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: cannot allocate private state\n", __func__); *status = HAL_ENOMEM; /* XXX */ return AH_FALSE; } priv->base.rfDetach = ar2413RfDetach; priv->base.writeRegs = ar2413WriteRegs; priv->base.getRfBank = ar2413GetRfBank; priv->base.setChannel = ar2413SetChannel; priv->base.setRfRegs = ar2413SetRfRegs; priv->base.setPowerTable = ar2413SetPowerTable; priv->base.getChannelMaxMinPower = ar2413GetChannelMaxMinPower; priv->base.getNfAdjust = ar5212GetNfAdjust; ahp->ah_pcdacTable = priv->pcdacTable; ahp->ah_pcdacTableSize = sizeof(priv->pcdacTable); ahp->ah_rfHal = &priv->base; return AH_TRUE; } static HAL_BOOL ar2413Probe(struct ath_hal *ah) { return IS_2413(ah); } AH_RF(RF2413, ar2413Probe, ar2413RfAttach); Index: head/sys/dev/ath/ath_hal/ar5212/ar2425.c =================================================================== --- head/sys/dev/ath/ath_hal/ar5212/ar2425.c (revision 298938) +++ head/sys/dev/ath/ath_hal/ar5212/ar2425.c (revision 298939) @@ -1,719 +1,719 @@ /* * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * Copyright (c) 2002-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ar5212/ar5212.h" #include "ar5212/ar5212reg.h" #include "ar5212/ar5212phy.h" #include "ah_eeprom_v3.h" #define AH_5212_2425 #define AH_5212_2417 #include "ar5212/ar5212.ini" #define N(a) (sizeof(a)/sizeof(a[0])) struct ar2425State { RF_HAL_FUNCS base; /* public state, must be first */ uint16_t pcdacTable[PWR_TABLE_SIZE_2413]; uint32_t Bank1Data[N(ar5212Bank1_2425)]; uint32_t Bank2Data[N(ar5212Bank2_2425)]; uint32_t Bank3Data[N(ar5212Bank3_2425)]; uint32_t Bank6Data[N(ar5212Bank6_2425)]; /* 2417 is same size */ uint32_t Bank7Data[N(ar5212Bank7_2425)]; }; #define AR2425(ah) ((struct ar2425State *) AH5212(ah)->ah_rfHal) extern void ar5212ModifyRfBuffer(uint32_t *rfBuf, uint32_t reg32, uint32_t numBits, uint32_t firstBit, uint32_t column); static void ar2425WriteRegs(struct ath_hal *ah, u_int modesIndex, u_int freqIndex, int writes) { HAL_INI_WRITE_ARRAY(ah, ar5212Modes_2425, modesIndex, writes); HAL_INI_WRITE_ARRAY(ah, ar5212Common_2425, 1, writes); HAL_INI_WRITE_ARRAY(ah, ar5212BB_RfGain_2425, freqIndex, writes); #if 0 /* * for SWAN similar to Condor * Bit 0 enables link to go to L1 when MAC goes to sleep. * Bit 3 enables the loop back the link down to reset. */ if (AH_PRIVATE(ah)->ah_ispcie && && ath_hal_pcieL1SKPEnable) { OS_REG_WRITE(ah, AR_PCIE_PMC, AR_PCIE_PMC_ENA_L1 | AR_PCIE_PMC_ENA_RESET); } /* * for Standby issue in Swan/Condor. * Bit 9 (MAC_WOW_PWR_STATE_MASK_D2)to be set to avoid skips * before last Training Sequence 2 (TS2) * Bit 8 (MAC_WOW_PWR_STATE_MASK_D1)to be unset to assert * Power Reset along with PCI Reset */ OS_REG_SET_BIT(ah, AR_PCIE_PMC, MAC_WOW_PWR_STATE_MASK_D2); #endif } /* * Take the MHz channel value and set the Channel value * * ASSUMES: Writes enabled to analog bus */ static HAL_BOOL ar2425SetChannel(struct ath_hal *ah, const struct ieee80211_channel *chan) { uint16_t freq = ath_hal_gethwchannel(ah, chan); uint32_t channelSel = 0; uint32_t bModeSynth = 0; uint32_t aModeRefSel = 0; uint32_t reg32 = 0; OS_MARK(ah, AH_MARK_SETCHANNEL, freq); if (freq < 4800) { uint32_t txctl; channelSel = freq - 2272; channelSel = ath_hal_reverseBits(channelSel, 8); txctl = OS_REG_READ(ah, AR_PHY_CCK_TX_CTRL); if (freq == 2484) { // Enable channel spreading for channel 14 OS_REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, txctl | AR_PHY_CCK_TX_CTRL_JAPAN); } else { OS_REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, txctl &~ AR_PHY_CCK_TX_CTRL_JAPAN); } } else if (((freq % 5) == 2) && (freq <= 5435)) { freq = freq - 2; /* Align to even 5MHz raster */ channelSel = ath_hal_reverseBits( (uint32_t)(((freq - 4800)*10)/25 + 1), 8); aModeRefSel = ath_hal_reverseBits(0, 2); } else if ((freq % 20) == 0 && freq >= 5120) { channelSel = ath_hal_reverseBits( ((freq - 4800) / 20 << 2), 8); aModeRefSel = ath_hal_reverseBits(1, 2); } else if ((freq % 10) == 0) { channelSel = ath_hal_reverseBits( ((freq - 4800) / 10 << 1), 8); aModeRefSel = ath_hal_reverseBits(1, 2); } else if ((freq % 5) == 0) { channelSel = ath_hal_reverseBits( (freq - 4800) / 5, 8); aModeRefSel = ath_hal_reverseBits(1, 2); } else { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel %u MHz\n", __func__, freq); return AH_FALSE; } reg32 = (channelSel << 4) | (aModeRefSel << 2) | (bModeSynth << 1) | (1 << 12) | 0x1; OS_REG_WRITE(ah, AR_PHY(0x27), reg32 & 0xff); reg32 >>= 8; OS_REG_WRITE(ah, AR_PHY(0x36), reg32 & 0x7f); AH_PRIVATE(ah)->ah_curchan = chan; return AH_TRUE; } /* * Reads EEPROM header info from device structure and programs * all rf registers * * REQUIRES: Access to the analog rf device */ static HAL_BOOL ar2425SetRfRegs(struct ath_hal *ah, const struct ieee80211_channel *chan, uint16_t modesIndex, uint16_t *rfXpdGain) { #define RF_BANK_SETUP(_priv, _ix, _col) do { \ int i; \ for (i = 0; i < N(ar5212Bank##_ix##_2425); i++) \ (_priv)->Bank##_ix##Data[i] = ar5212Bank##_ix##_2425[i][_col];\ } while (0) struct ath_hal_5212 *ahp = AH5212(ah); const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; struct ar2425State *priv = AR2425(ah); uint16_t ob2GHz = 0, db2GHz = 0; int regWrites = 0; HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: chan %u/0x%x modesIndex %u\n", __func__, chan->ic_freq, chan->ic_flags, modesIndex); HALASSERT(priv); /* Setup rf parameters */ if (IEEE80211_IS_CHAN_B(chan)) { ob2GHz = ee->ee_obFor24; db2GHz = ee->ee_dbFor24; } else { ob2GHz = ee->ee_obFor24g; db2GHz = ee->ee_dbFor24g; } /* Bank 1 Write */ RF_BANK_SETUP(priv, 1, 1); /* Bank 2 Write */ RF_BANK_SETUP(priv, 2, modesIndex); /* Bank 3 Write */ RF_BANK_SETUP(priv, 3, modesIndex); /* Bank 6 Write */ RF_BANK_SETUP(priv, 6, modesIndex); ar5212ModifyRfBuffer(priv->Bank6Data, ob2GHz, 3, 193, 0); ar5212ModifyRfBuffer(priv->Bank6Data, db2GHz, 3, 190, 0); /* Bank 7 Setup */ RF_BANK_SETUP(priv, 7, modesIndex); /* Write Analog registers */ HAL_INI_WRITE_BANK(ah, ar5212Bank1_2425, priv->Bank1Data, regWrites); HAL_INI_WRITE_BANK(ah, ar5212Bank2_2425, priv->Bank2Data, regWrites); HAL_INI_WRITE_BANK(ah, ar5212Bank3_2425, priv->Bank3Data, regWrites); if (IS_2417(ah)) { HALASSERT(N(ar5212Bank6_2425) == N(ar5212Bank6_2417)); HAL_INI_WRITE_BANK(ah, ar5212Bank6_2417, priv->Bank6Data, regWrites); } else HAL_INI_WRITE_BANK(ah, ar5212Bank6_2425, priv->Bank6Data, regWrites); HAL_INI_WRITE_BANK(ah, ar5212Bank7_2425, priv->Bank7Data, regWrites); /* Now that we have reprogrammed rfgain value, clear the flag. */ ahp->ah_rfgainState = HAL_RFGAIN_INACTIVE; HALDEBUG(ah, HAL_DEBUG_RFPARAM, "<==%s\n", __func__); return AH_TRUE; #undef RF_BANK_SETUP } /* * Return a reference to the requested RF Bank. */ static uint32_t * ar2425GetRfBank(struct ath_hal *ah, int bank) { struct ar2425State *priv = AR2425(ah); HALASSERT(priv != AH_NULL); switch (bank) { case 1: return priv->Bank1Data; case 2: return priv->Bank2Data; case 3: return priv->Bank3Data; case 6: return priv->Bank6Data; case 7: return priv->Bank7Data; } HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unknown RF Bank %d requested\n", __func__, bank); return AH_NULL; } /* * Return indices surrounding the value in sorted integer lists. * * NB: the input list is assumed to be sorted in ascending order */ static void GetLowerUpperIndex(int16_t v, const uint16_t *lp, uint16_t listSize, uint32_t *vlo, uint32_t *vhi) { int16_t target = v; const uint16_t *ep = lp+listSize; const uint16_t *tp; /* * Check first and last elements for out-of-bounds conditions. */ if (target < lp[0]) { *vlo = *vhi = 0; return; } if (target >= ep[-1]) { *vlo = *vhi = listSize - 1; return; } /* look for value being near or between 2 values in list */ for (tp = lp; tp < ep; tp++) { /* * If value is close to the current value of the list * then target is not between values, it is one of the values */ if (*tp == target) { *vlo = *vhi = tp - (const uint16_t *) lp; return; } /* * Look for value being between current value and next value * if so return these 2 values */ if (target < tp[1]) { *vlo = tp - (const uint16_t *) lp; *vhi = *vlo + 1; return; } } } /* * Fill the Vpdlist for indices Pmax-Pmin */ static HAL_BOOL ar2425FillVpdTable(uint32_t pdGainIdx, int16_t Pmin, int16_t Pmax, const int16_t *pwrList, const uint16_t *VpdList, uint16_t numIntercepts, uint16_t retVpdList[][64]) { uint16_t ii, jj, kk; int16_t currPwr = (int16_t)(2*Pmin); /* since Pmin is pwr*2 and pwrList is 4*pwr */ uint32_t idxL, idxR; ii = 0; jj = 0; if (numIntercepts < 2) return AH_FALSE; while (ii <= (uint16_t)(Pmax - Pmin)) { GetLowerUpperIndex(currPwr, (const uint16_t *) pwrList, numIntercepts, &(idxL), &(idxR)); if (idxR < 1) idxR = 1; /* extrapolate below */ if (idxL == (uint32_t)(numIntercepts - 1)) idxL = numIntercepts - 2; /* extrapolate above */ if (pwrList[idxL] == pwrList[idxR]) kk = VpdList[idxL]; else kk = (uint16_t) (((currPwr - pwrList[idxL])*VpdList[idxR]+ (pwrList[idxR] - currPwr)*VpdList[idxL])/ (pwrList[idxR] - pwrList[idxL])); retVpdList[pdGainIdx][ii] = kk; ii++; currPwr += 2; /* half dB steps */ } return AH_TRUE; } /* * Returns interpolated or the scaled up interpolated value */ static int16_t interpolate_signed(uint16_t target, uint16_t srcLeft, uint16_t srcRight, int16_t targetLeft, int16_t targetRight) { int16_t rv; if (srcRight != srcLeft) { rv = ((target - srcLeft)*targetRight + (srcRight - target)*targetLeft) / (srcRight - srcLeft); } else { rv = targetLeft; } return rv; } /* * Uses the data points read from EEPROM to reconstruct the pdadc power table * Called by ar2425SetPowerTable() */ static void ar2425getGainBoundariesAndPdadcsForPowers(struct ath_hal *ah, uint16_t channel, const RAW_DATA_STRUCT_2413 *pRawDataset, uint16_t pdGainOverlap_t2, int16_t *pMinCalPower, uint16_t pPdGainBoundaries[], uint16_t pPdGainValues[], uint16_t pPDADCValues[]) { /* Note the items statically allocated below are to reduce stack usage */ uint32_t ii, jj, kk; int32_t ss;/* potentially -ve index for taking care of pdGainOverlap */ uint32_t idxL, idxR; uint32_t numPdGainsUsed = 0; static uint16_t VpdTable_L[MAX_NUM_PDGAINS_PER_CHANNEL][MAX_PWR_RANGE_IN_HALF_DB]; /* filled out Vpd table for all pdGains (chanL) */ static uint16_t VpdTable_R[MAX_NUM_PDGAINS_PER_CHANNEL][MAX_PWR_RANGE_IN_HALF_DB]; /* filled out Vpd table for all pdGains (chanR) */ static uint16_t VpdTable_I[MAX_NUM_PDGAINS_PER_CHANNEL][MAX_PWR_RANGE_IN_HALF_DB]; /* filled out Vpd table for all pdGains (interpolated) */ /* * If desired to support -ve power levels in future, just * change pwr_I_0 to signed 5-bits. */ static int16_t Pmin_t2[MAX_NUM_PDGAINS_PER_CHANNEL]; - /* to accomodate -ve power levels later on. */ + /* to accommodate -ve power levels later on. */ static int16_t Pmax_t2[MAX_NUM_PDGAINS_PER_CHANNEL]; - /* to accomodate -ve power levels later on */ + /* to accommodate -ve power levels later on */ uint16_t numVpd = 0; uint16_t Vpd_step; int16_t tmpVal ; uint32_t sizeCurrVpdTable, maxIndex, tgtIndex; HALDEBUG(ah, HAL_DEBUG_RFPARAM, "==>%s:\n", __func__); /* Get upper lower index */ GetLowerUpperIndex(channel, pRawDataset->pChannels, pRawDataset->numChannels, &(idxL), &(idxR)); for (ii = 0; ii < MAX_NUM_PDGAINS_PER_CHANNEL; ii++) { jj = MAX_NUM_PDGAINS_PER_CHANNEL - ii - 1; /* work backwards 'cause highest pdGain for lowest power */ numVpd = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].numVpd; if (numVpd > 0) { pPdGainValues[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pd_gain; Pmin_t2[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pwr_t4[0]; if (Pmin_t2[numPdGainsUsed] >pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[0]) { Pmin_t2[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[0]; } Pmin_t2[numPdGainsUsed] = (int16_t) (Pmin_t2[numPdGainsUsed] / 2); Pmax_t2[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pwr_t4[numVpd-1]; if (Pmax_t2[numPdGainsUsed] > pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[numVpd-1]) Pmax_t2[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[numVpd-1]; Pmax_t2[numPdGainsUsed] = (int16_t)(Pmax_t2[numPdGainsUsed] / 2); ar2425FillVpdTable( numPdGainsUsed, Pmin_t2[numPdGainsUsed], Pmax_t2[numPdGainsUsed], &(pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pwr_t4[0]), &(pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].Vpd[0]), numVpd, VpdTable_L ); ar2425FillVpdTable( numPdGainsUsed, Pmin_t2[numPdGainsUsed], Pmax_t2[numPdGainsUsed], &(pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[0]), &(pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].Vpd[0]), numVpd, VpdTable_R ); for (kk = 0; kk < (uint16_t)(Pmax_t2[numPdGainsUsed] - Pmin_t2[numPdGainsUsed]); kk++) { VpdTable_I[numPdGainsUsed][kk] = interpolate_signed( channel, pRawDataset->pChannels[idxL], pRawDataset->pChannels[idxR], (int16_t)VpdTable_L[numPdGainsUsed][kk], (int16_t)VpdTable_R[numPdGainsUsed][kk]); } /* fill VpdTable_I for this pdGain */ numPdGainsUsed++; } /* if this pdGain is used */ } *pMinCalPower = Pmin_t2[0]; kk = 0; /* index for the final table */ for (ii = 0; ii < numPdGainsUsed; ii++) { if (ii == (numPdGainsUsed - 1)) pPdGainBoundaries[ii] = Pmax_t2[ii] + PD_GAIN_BOUNDARY_STRETCH_IN_HALF_DB; else pPdGainBoundaries[ii] = (uint16_t) ((Pmax_t2[ii] + Pmin_t2[ii+1]) / 2 ); /* Find starting index for this pdGain */ if (ii == 0) ss = 0; /* for the first pdGain, start from index 0 */ else ss = (pPdGainBoundaries[ii-1] - Pmin_t2[ii]) - pdGainOverlap_t2; Vpd_step = (uint16_t)(VpdTable_I[ii][1] - VpdTable_I[ii][0]); Vpd_step = (uint16_t)((Vpd_step < 1) ? 1 : Vpd_step); /* *-ve ss indicates need to extrapolate data below for this pdGain */ while (ss < 0) { tmpVal = (int16_t)(VpdTable_I[ii][0] + ss*Vpd_step); pPDADCValues[kk++] = (uint16_t)((tmpVal < 0) ? 0 : tmpVal); ss++; } sizeCurrVpdTable = Pmax_t2[ii] - Pmin_t2[ii]; tgtIndex = pPdGainBoundaries[ii] + pdGainOverlap_t2 - Pmin_t2[ii]; maxIndex = (tgtIndex < sizeCurrVpdTable) ? tgtIndex : sizeCurrVpdTable; while (ss < (int16_t)maxIndex) pPDADCValues[kk++] = VpdTable_I[ii][ss++]; Vpd_step = (uint16_t)(VpdTable_I[ii][sizeCurrVpdTable-1] - VpdTable_I[ii][sizeCurrVpdTable-2]); Vpd_step = (uint16_t)((Vpd_step < 1) ? 1 : Vpd_step); /* * for last gain, pdGainBoundary == Pmax_t2, so will * have to extrapolate */ if (tgtIndex > maxIndex) { /* need to extrapolate above */ while(ss < (int16_t)tgtIndex) { tmpVal = (uint16_t) (VpdTable_I[ii][sizeCurrVpdTable-1] + (ss-maxIndex)*Vpd_step); pPDADCValues[kk++] = (tmpVal > 127) ? 127 : tmpVal; ss++; } } /* extrapolated above */ } /* for all pdGainUsed */ while (ii < MAX_NUM_PDGAINS_PER_CHANNEL) { pPdGainBoundaries[ii] = pPdGainBoundaries[ii-1]; ii++; } while (kk < 128) { pPDADCValues[kk] = pPDADCValues[kk-1]; kk++; } HALDEBUG(ah, HAL_DEBUG_RFPARAM, "<==%s\n", __func__); } /* Same as 2413 set power table */ static HAL_BOOL ar2425SetPowerTable(struct ath_hal *ah, int16_t *minPower, int16_t *maxPower, const struct ieee80211_channel *chan, uint16_t *rfXpdGain) { uint16_t freq = ath_hal_gethwchannel(ah, chan); struct ath_hal_5212 *ahp = AH5212(ah); const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; const RAW_DATA_STRUCT_2413 *pRawDataset = AH_NULL; uint16_t pdGainOverlap_t2; int16_t minCalPower2413_t2; uint16_t *pdadcValues = ahp->ah_pcdacTable; uint16_t gainBoundaries[4]; uint32_t i, reg32, regoffset; HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s:chan 0x%x flag 0x%x\n", __func__, freq, chan->ic_flags); if (IEEE80211_IS_CHAN_G(chan) || IEEE80211_IS_CHAN_108G(chan)) pRawDataset = &ee->ee_rawDataset2413[headerInfo11G]; else if (IEEE80211_IS_CHAN_B(chan)) pRawDataset = &ee->ee_rawDataset2413[headerInfo11B]; else { HALDEBUG(ah, HAL_DEBUG_ANY, "%s:illegal mode\n", __func__); return AH_FALSE; } pdGainOverlap_t2 = (uint16_t) SM(OS_REG_READ(ah, AR_PHY_TPCRG5), AR_PHY_TPCRG5_PD_GAIN_OVERLAP); ar2425getGainBoundariesAndPdadcsForPowers(ah, freq, pRawDataset, pdGainOverlap_t2,&minCalPower2413_t2,gainBoundaries, rfXpdGain, pdadcValues); OS_REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_NUM_PD_GAIN, (pRawDataset->pDataPerChannel[0].numPdGains - 1)); /* * Note the pdadc table may not start at 0 dBm power, could be * negative or greater than 0. Need to offset the power * values by the amount of minPower for griffin */ if (minCalPower2413_t2 != 0) ahp->ah_txPowerIndexOffset = (int16_t)(0 - minCalPower2413_t2); else ahp->ah_txPowerIndexOffset = 0; /* Finally, write the power values into the baseband power table */ regoffset = 0x9800 + (672 <<2); /* beginning of pdadc table in griffin */ for (i = 0; i < 32; i++) { reg32 = ((pdadcValues[4*i + 0] & 0xFF) << 0) | ((pdadcValues[4*i + 1] & 0xFF) << 8) | ((pdadcValues[4*i + 2] & 0xFF) << 16) | ((pdadcValues[4*i + 3] & 0xFF) << 24) ; OS_REG_WRITE(ah, regoffset, reg32); regoffset += 4; } OS_REG_WRITE(ah, AR_PHY_TPCRG5, SM(pdGainOverlap_t2, AR_PHY_TPCRG5_PD_GAIN_OVERLAP) | SM(gainBoundaries[0], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1) | SM(gainBoundaries[1], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2) | SM(gainBoundaries[2], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3) | SM(gainBoundaries[3], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4)); return AH_TRUE; } static int16_t ar2425GetMinPower(struct ath_hal *ah, const RAW_DATA_PER_CHANNEL_2413 *data) { uint32_t ii,jj; uint16_t Pmin=0,numVpd; for (ii = 0; ii < MAX_NUM_PDGAINS_PER_CHANNEL; ii++) { jj = MAX_NUM_PDGAINS_PER_CHANNEL - ii - 1; /* work backwards 'cause highest pdGain for lowest power */ numVpd = data->pDataPerPDGain[jj].numVpd; if (numVpd > 0) { Pmin = data->pDataPerPDGain[jj].pwr_t4[0]; return(Pmin); } } return(Pmin); } static int16_t ar2425GetMaxPower(struct ath_hal *ah, const RAW_DATA_PER_CHANNEL_2413 *data) { uint32_t ii; uint16_t Pmax=0,numVpd; for (ii=0; ii< MAX_NUM_PDGAINS_PER_CHANNEL; ii++) { /* work forwards cuase lowest pdGain for highest power */ numVpd = data->pDataPerPDGain[ii].numVpd; if (numVpd > 0) { Pmax = data->pDataPerPDGain[ii].pwr_t4[numVpd-1]; return(Pmax); } } return(Pmax); } static HAL_BOOL ar2425GetChannelMaxMinPower(struct ath_hal *ah, const struct ieee80211_channel *chan, int16_t *maxPow, int16_t *minPow) { uint16_t freq = chan->ic_freq; /* NB: never mapped */ const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; const RAW_DATA_STRUCT_2413 *pRawDataset = AH_NULL; const RAW_DATA_PER_CHANNEL_2413 *data = AH_NULL; uint16_t numChannels; int totalD,totalF, totalMin,last, i; *maxPow = 0; if (IEEE80211_IS_CHAN_G(chan) || IEEE80211_IS_CHAN_108G(chan)) pRawDataset = &ee->ee_rawDataset2413[headerInfo11G]; else if (IEEE80211_IS_CHAN_B(chan)) pRawDataset = &ee->ee_rawDataset2413[headerInfo11B]; else return(AH_FALSE); numChannels = pRawDataset->numChannels; data = pRawDataset->pDataPerChannel; /* Make sure the channel is in the range of the TP values * (freq piers) */ if (numChannels < 1) return(AH_FALSE); if ((freq < data[0].channelValue) || (freq > data[numChannels-1].channelValue)) { if (freq < data[0].channelValue) { *maxPow = ar2425GetMaxPower(ah, &data[0]); *minPow = ar2425GetMinPower(ah, &data[0]); return(AH_TRUE); } else { *maxPow = ar2425GetMaxPower(ah, &data[numChannels - 1]); *minPow = ar2425GetMinPower(ah, &data[numChannels - 1]); return(AH_TRUE); } } /* Linearly interpolate the power value now */ for (last=0,i=0; (i data[i].channelValue); last = i++); totalD = data[i].channelValue - data[last].channelValue; if (totalD > 0) { totalF = ar2425GetMaxPower(ah, &data[i]) - ar2425GetMaxPower(ah, &data[last]); *maxPow = (int8_t) ((totalF*(freq-data[last].channelValue) + ar2425GetMaxPower(ah, &data[last])*totalD)/totalD); totalMin = ar2425GetMinPower(ah, &data[i]) - ar2425GetMinPower(ah, &data[last]); *minPow = (int8_t) ((totalMin*(freq-data[last].channelValue) + ar2425GetMinPower(ah, &data[last])*totalD)/totalD); return(AH_TRUE); } else { if (freq == data[i].channelValue) { *maxPow = ar2425GetMaxPower(ah, &data[i]); *minPow = ar2425GetMinPower(ah, &data[i]); return(AH_TRUE); } else return(AH_FALSE); } } /* * Free memory for analog bank scratch buffers */ static void ar2425RfDetach(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); HALASSERT(ahp->ah_rfHal != AH_NULL); ath_hal_free(ahp->ah_rfHal); ahp->ah_rfHal = AH_NULL; } /* * Allocate memory for analog bank scratch buffers * Scratch Buffer will be reinitialized every reset so no need to zero now */ static HAL_BOOL ar2425RfAttach(struct ath_hal *ah, HAL_STATUS *status) { struct ath_hal_5212 *ahp = AH5212(ah); struct ar2425State *priv; HALASSERT(ah->ah_magic == AR5212_MAGIC); HALASSERT(ahp->ah_rfHal == AH_NULL); priv = ath_hal_malloc(sizeof(struct ar2425State)); if (priv == AH_NULL) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: cannot allocate private state\n", __func__); *status = HAL_ENOMEM; /* XXX */ return AH_FALSE; } priv->base.rfDetach = ar2425RfDetach; priv->base.writeRegs = ar2425WriteRegs; priv->base.getRfBank = ar2425GetRfBank; priv->base.setChannel = ar2425SetChannel; priv->base.setRfRegs = ar2425SetRfRegs; priv->base.setPowerTable = ar2425SetPowerTable; priv->base.getChannelMaxMinPower = ar2425GetChannelMaxMinPower; priv->base.getNfAdjust = ar5212GetNfAdjust; ahp->ah_pcdacTable = priv->pcdacTable; ahp->ah_pcdacTableSize = sizeof(priv->pcdacTable); ahp->ah_rfHal = &priv->base; return AH_TRUE; } static HAL_BOOL ar2425Probe(struct ath_hal *ah) { return IS_2425(ah) || IS_2417(ah); } AH_RF(RF2425, ar2425Probe, ar2425RfAttach); Index: head/sys/dev/ath/ath_hal/ar5212/ar5212_attach.c =================================================================== --- head/sys/dev/ath/ath_hal/ar5212/ar5212_attach.c (revision 298938) +++ head/sys/dev/ath/ath_hal/ar5212/ar5212_attach.c (revision 298939) @@ -1,947 +1,947 @@ /* * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * Copyright (c) 2002-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ah_devid.h" #include "ar5212/ar5212.h" #include "ar5212/ar5212reg.h" #include "ar5212/ar5212phy.h" #define AH_5212_COMMON #include "ar5212/ar5212.ini" static void ar5212ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore, HAL_BOOL power_off); static void ar5212DisablePCIE(struct ath_hal *ah); static const struct ath_hal_private ar5212hal = {{ .ah_magic = AR5212_MAGIC, .ah_getRateTable = ar5212GetRateTable, .ah_detach = ar5212Detach, /* Reset Functions */ .ah_reset = ar5212Reset, .ah_phyDisable = ar5212PhyDisable, .ah_disable = ar5212Disable, .ah_configPCIE = ar5212ConfigPCIE, .ah_disablePCIE = ar5212DisablePCIE, .ah_setPCUConfig = ar5212SetPCUConfig, .ah_perCalibration = ar5212PerCalibration, .ah_perCalibrationN = ar5212PerCalibrationN, .ah_resetCalValid = ar5212ResetCalValid, .ah_setTxPowerLimit = ar5212SetTxPowerLimit, .ah_getChanNoise = ath_hal_getChanNoise, /* Transmit functions */ .ah_updateTxTrigLevel = ar5212UpdateTxTrigLevel, .ah_setupTxQueue = ar5212SetupTxQueue, .ah_setTxQueueProps = ar5212SetTxQueueProps, .ah_getTxQueueProps = ar5212GetTxQueueProps, .ah_releaseTxQueue = ar5212ReleaseTxQueue, .ah_resetTxQueue = ar5212ResetTxQueue, .ah_getTxDP = ar5212GetTxDP, .ah_setTxDP = ar5212SetTxDP, .ah_numTxPending = ar5212NumTxPending, .ah_startTxDma = ar5212StartTxDma, .ah_stopTxDma = ar5212StopTxDma, .ah_setupTxDesc = ar5212SetupTxDesc, .ah_setupXTxDesc = ar5212SetupXTxDesc, .ah_fillTxDesc = ar5212FillTxDesc, .ah_procTxDesc = ar5212ProcTxDesc, .ah_getTxIntrQueue = ar5212GetTxIntrQueue, .ah_reqTxIntrDesc = ar5212IntrReqTxDesc, .ah_getTxCompletionRates = ar5212GetTxCompletionRates, .ah_setTxDescLink = ar5212SetTxDescLink, .ah_getTxDescLink = ar5212GetTxDescLink, .ah_getTxDescLinkPtr = ar5212GetTxDescLinkPtr, /* RX Functions */ .ah_getRxDP = ar5212GetRxDP, .ah_setRxDP = ar5212SetRxDP, .ah_enableReceive = ar5212EnableReceive, .ah_stopDmaReceive = ar5212StopDmaReceive, .ah_startPcuReceive = ar5212StartPcuReceive, .ah_stopPcuReceive = ar5212StopPcuReceive, .ah_setMulticastFilter = ar5212SetMulticastFilter, .ah_setMulticastFilterIndex = ar5212SetMulticastFilterIndex, .ah_clrMulticastFilterIndex = ar5212ClrMulticastFilterIndex, .ah_getRxFilter = ar5212GetRxFilter, .ah_setRxFilter = ar5212SetRxFilter, .ah_setupRxDesc = ar5212SetupRxDesc, .ah_procRxDesc = ar5212ProcRxDesc, .ah_rxMonitor = ar5212RxMonitor, .ah_aniPoll = ar5212AniPoll, .ah_procMibEvent = ar5212ProcessMibIntr, /* Misc Functions */ .ah_getCapability = ar5212GetCapability, .ah_setCapability = ar5212SetCapability, .ah_getDiagState = ar5212GetDiagState, .ah_getMacAddress = ar5212GetMacAddress, .ah_setMacAddress = ar5212SetMacAddress, .ah_getBssIdMask = ar5212GetBssIdMask, .ah_setBssIdMask = ar5212SetBssIdMask, .ah_setRegulatoryDomain = ar5212SetRegulatoryDomain, .ah_setLedState = ar5212SetLedState, .ah_writeAssocid = ar5212WriteAssocid, .ah_gpioCfgInput = ar5212GpioCfgInput, .ah_gpioCfgOutput = ar5212GpioCfgOutput, .ah_gpioGet = ar5212GpioGet, .ah_gpioSet = ar5212GpioSet, .ah_gpioSetIntr = ar5212GpioSetIntr, .ah_getTsf32 = ar5212GetTsf32, .ah_getTsf64 = ar5212GetTsf64, .ah_setTsf64 = ar5212SetTsf64, .ah_resetTsf = ar5212ResetTsf, .ah_detectCardPresent = ar5212DetectCardPresent, .ah_updateMibCounters = ar5212UpdateMibCounters, .ah_getRfGain = ar5212GetRfgain, .ah_getDefAntenna = ar5212GetDefAntenna, .ah_setDefAntenna = ar5212SetDefAntenna, .ah_getAntennaSwitch = ar5212GetAntennaSwitch, .ah_setAntennaSwitch = ar5212SetAntennaSwitch, .ah_setSifsTime = ar5212SetSifsTime, .ah_getSifsTime = ar5212GetSifsTime, .ah_setSlotTime = ar5212SetSlotTime, .ah_getSlotTime = ar5212GetSlotTime, .ah_setAckTimeout = ar5212SetAckTimeout, .ah_getAckTimeout = ar5212GetAckTimeout, .ah_setAckCTSRate = ar5212SetAckCTSRate, .ah_getAckCTSRate = ar5212GetAckCTSRate, .ah_setCTSTimeout = ar5212SetCTSTimeout, .ah_getCTSTimeout = ar5212GetCTSTimeout, .ah_setDecompMask = ar5212SetDecompMask, .ah_setCoverageClass = ar5212SetCoverageClass, .ah_setQuiet = ar5212SetQuiet, .ah_getMibCycleCounts = ar5212GetMibCycleCounts, .ah_setChainMasks = ar5212SetChainMasks, /* DFS Functions */ .ah_enableDfs = ar5212EnableDfs, .ah_getDfsThresh = ar5212GetDfsThresh, .ah_getDfsDefaultThresh = ar5212GetDfsDefaultThresh, .ah_procRadarEvent = ar5212ProcessRadarEvent, .ah_isFastClockEnabled = ar5212IsFastClockEnabled, .ah_get11nExtBusy = ar5212Get11nExtBusy, /* Key Cache Functions */ .ah_getKeyCacheSize = ar5212GetKeyCacheSize, .ah_resetKeyCacheEntry = ar5212ResetKeyCacheEntry, .ah_isKeyCacheEntryValid = ar5212IsKeyCacheEntryValid, .ah_setKeyCacheEntry = ar5212SetKeyCacheEntry, .ah_setKeyCacheEntryMac = ar5212SetKeyCacheEntryMac, /* Power Management Functions */ .ah_setPowerMode = ar5212SetPowerMode, .ah_getPowerMode = ar5212GetPowerMode, /* Beacon Functions */ .ah_setBeaconTimers = ar5212SetBeaconTimers, .ah_beaconInit = ar5212BeaconInit, .ah_setStationBeaconTimers = ar5212SetStaBeaconTimers, .ah_resetStationBeaconTimers = ar5212ResetStaBeaconTimers, .ah_getNextTBTT = ar5212GetNextTBTT, /* Interrupt Functions */ .ah_isInterruptPending = ar5212IsInterruptPending, .ah_getPendingInterrupts = ar5212GetPendingInterrupts, .ah_getInterrupts = ar5212GetInterrupts, .ah_setInterrupts = ar5212SetInterrupts }, .ah_getChannelEdges = ar5212GetChannelEdges, .ah_getWirelessModes = ar5212GetWirelessModes, .ah_eepromRead = ar5212EepromRead, #ifdef AH_SUPPORT_WRITE_EEPROM .ah_eepromWrite = ar5212EepromWrite, #endif .ah_getChipPowerLimits = ar5212GetChipPowerLimits, }; uint32_t ar5212GetRadioRev(struct ath_hal *ah) { uint32_t val; int i; /* Read Radio Chip Rev Extract */ OS_REG_WRITE(ah, AR_PHY(0x34), 0x00001c16); for (i = 0; i < 8; i++) OS_REG_WRITE(ah, AR_PHY(0x20), 0x00010000); val = (OS_REG_READ(ah, AR_PHY(256)) >> 24) & 0xff; val = ((val & 0xf0) >> 4) | ((val & 0x0f) << 4); return ath_hal_reverseBits(val, 8); } static void ar5212AniSetup(struct ath_hal *ah) { static const struct ar5212AniParams aniparams = { .maxNoiseImmunityLevel = 4, /* levels 0..4 */ .totalSizeDesired = { -55, -55, -55, -55, -62 }, .coarseHigh = { -14, -14, -14, -14, -12 }, .coarseLow = { -64, -64, -64, -64, -70 }, .firpwr = { -78, -78, -78, -78, -80 }, .maxSpurImmunityLevel = 2, /* NB: depends on chip rev */ .cycPwrThr1 = { 2, 4, 6, 8, 10, 12, 14, 16 }, .maxFirstepLevel = 2, /* levels 0..2 */ .firstep = { 0, 4, 8 }, .ofdmTrigHigh = 500, .ofdmTrigLow = 200, .cckTrigHigh = 200, .cckTrigLow = 100, .rssiThrHigh = 40, .rssiThrLow = 7, .period = 100, }; if (AH_PRIVATE(ah)->ah_macVersion < AR_SREV_VERSION_GRIFFIN) { struct ar5212AniParams tmp; OS_MEMCPY(&tmp, &aniparams, sizeof(struct ar5212AniParams)); tmp.maxSpurImmunityLevel = 7; /* Venice and earlier */ ar5212AniAttach(ah, &tmp, &tmp, AH_TRUE); } else ar5212AniAttach(ah, &aniparams, &aniparams, AH_TRUE); /* Set overridable ANI methods */ AH5212(ah)->ah_aniControl = ar5212AniControl; } /* * Attach for an AR5212 part. */ void ar5212InitState(struct ath_hal_5212 *ahp, uint16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, HAL_STATUS *status) { #define N(a) (sizeof(a)/sizeof(a[0])) static const uint8_t defbssidmask[IEEE80211_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; struct ath_hal *ah; ah = &ahp->ah_priv.h; /* set initial values */ OS_MEMCPY(&ahp->ah_priv, &ar5212hal, sizeof(struct ath_hal_private)); ah->ah_sc = sc; ah->ah_st = st; ah->ah_sh = sh; ah->ah_devid = devid; /* NB: for alq */ AH_PRIVATE(ah)->ah_devid = devid; AH_PRIVATE(ah)->ah_subvendorid = 0; /* XXX */ AH_PRIVATE(ah)->ah_powerLimit = MAX_RATE_POWER; AH_PRIVATE(ah)->ah_tpScale = HAL_TP_SCALE_MAX; /* no scaling */ ahp->ah_antControl = HAL_ANT_VARIABLE; ahp->ah_diversity = AH_TRUE; ahp->ah_bIQCalibration = AH_FALSE; /* * Enable MIC handling. */ ahp->ah_staId1Defaults = AR_STA_ID1_CRPT_MIC_ENABLE; ahp->ah_rssiThr = INIT_RSSI_THR; ahp->ah_tpcEnabled = AH_FALSE; /* disabled by default */ ahp->ah_phyPowerOn = AH_FALSE; ahp->ah_macTPC = SM(MAX_RATE_POWER, AR_TPC_ACK) | SM(MAX_RATE_POWER, AR_TPC_CTS) | SM(MAX_RATE_POWER, AR_TPC_CHIRP); ahp->ah_beaconInterval = 100; /* XXX [20..1000] */ ahp->ah_enable32kHzClock = DONT_USE_32KHZ;/* XXX */ ahp->ah_slottime = (u_int) -1; ahp->ah_acktimeout = (u_int) -1; ahp->ah_ctstimeout = (u_int) -1; ahp->ah_sifstime = (u_int) -1; ahp->ah_txTrigLev = INIT_TX_FIFO_THRESHOLD, ahp->ah_maxTxTrigLev = MAX_TX_FIFO_THRESHOLD, OS_MEMCPY(&ahp->ah_bssidmask, defbssidmask, IEEE80211_ADDR_LEN); #undef N } /* * Validate MAC version and revision. */ static HAL_BOOL ar5212IsMacSupported(uint8_t macVersion, uint8_t macRev) { #define N(a) (sizeof(a)/sizeof(a[0])) static const struct { uint8_t version; uint8_t revMin, revMax; } macs[] = { { AR_SREV_VERSION_VENICE, AR_SREV_D2PLUS, AR_SREV_REVISION_MAX }, { AR_SREV_VERSION_GRIFFIN, AR_SREV_D2PLUS, AR_SREV_REVISION_MAX }, { AR_SREV_5413, AR_SREV_REVISION_MIN, AR_SREV_REVISION_MAX }, { AR_SREV_5424, AR_SREV_REVISION_MIN, AR_SREV_REVISION_MAX }, { AR_SREV_2425, AR_SREV_REVISION_MIN, AR_SREV_REVISION_MAX }, { AR_SREV_2417, AR_SREV_REVISION_MIN, AR_SREV_REVISION_MAX }, }; int i; for (i = 0; i < N(macs); i++) if (macs[i].version == macVersion && macs[i].revMin <= macRev && macRev <= macs[i].revMax) return AH_TRUE; return AH_FALSE; #undef N } /* * Attach for an AR5212 part. */ static struct ath_hal * ar5212Attach(uint16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, HAL_OPS_CONFIG *ah_config, HAL_STATUS *status) { #define AH_EEPROM_PROTECT(ah) \ (AH_PRIVATE(ah)->ah_ispcie)? AR_EEPROM_PROTECT_PCIE : AR_EEPROM_PROTECT) struct ath_hal_5212 *ahp; struct ath_hal *ah; struct ath_hal_rf *rf; uint32_t val; uint16_t eeval; HAL_STATUS ecode; HALDEBUG(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n", __func__, sc, (void*) st, (void*) sh); /* NB: memory is returned zero'd */ ahp = ath_hal_malloc(sizeof (struct ath_hal_5212)); if (ahp == AH_NULL) { HALDEBUG(AH_NULL, HAL_DEBUG_ANY, "%s: cannot allocate memory for state block\n", __func__); *status = HAL_ENOMEM; return AH_NULL; } ar5212InitState(ahp, devid, sc, st, sh, status); ah = &ahp->ah_priv.h; if (!ar5212SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: couldn't wakeup chip\n", __func__); ecode = HAL_EIO; goto bad; } /* Read Revisions from Chips before taking out of reset */ val = OS_REG_READ(ah, AR_SREV) & AR_SREV_ID; AH_PRIVATE(ah)->ah_macVersion = val >> AR_SREV_ID_S; AH_PRIVATE(ah)->ah_macRev = val & AR_SREV_REVISION; AH_PRIVATE(ah)->ah_ispcie = IS_5424(ah) || IS_2425(ah); if (!ar5212IsMacSupported(AH_PRIVATE(ah)->ah_macVersion, AH_PRIVATE(ah)->ah_macRev)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: Mac Chip Rev 0x%02x.%x not supported\n" , __func__, AH_PRIVATE(ah)->ah_macVersion, AH_PRIVATE(ah)->ah_macRev); ecode = HAL_ENOTSUPP; goto bad; } /* setup common ini data; rf backends handle remainder */ HAL_INI_INIT(&ahp->ah_ini_modes, ar5212Modes, 6); HAL_INI_INIT(&ahp->ah_ini_common, ar5212Common, 2); if (!ar5212ChipReset(ah, AH_NULL)) { /* reset chip */ HALDEBUG(ah, HAL_DEBUG_ANY, "%s: chip reset failed\n", __func__); ecode = HAL_EIO; goto bad; } AH_PRIVATE(ah)->ah_phyRev = OS_REG_READ(ah, AR_PHY_CHIP_ID); if (AH_PRIVATE(ah)->ah_ispcie) { /* XXX: build flag to disable this? */ ath_hal_configPCIE(ah, AH_FALSE, AH_FALSE); } if (!ar5212ChipTest(ah)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: hardware self-test failed\n", __func__); ecode = HAL_ESELFTEST; goto bad; } /* Enable PCI core retry fix in software for Hainan and up */ if (AH_PRIVATE(ah)->ah_macVersion >= AR_SREV_VERSION_VENICE) OS_REG_SET_BIT(ah, AR_PCICFG, AR_PCICFG_RETRYFIXEN); /* * Set correct Baseband to analog shift * setting to access analog chips. */ OS_REG_WRITE(ah, AR_PHY(0), 0x00000007); /* Read Radio Chip Rev Extract */ AH_PRIVATE(ah)->ah_analog5GhzRev = ar5212GetRadioRev(ah); rf = ath_hal_rfprobe(ah, &ecode); if (rf == AH_NULL) goto bad; /* NB: silently accept anything in release code per Atheros */ switch (AH_PRIVATE(ah)->ah_analog5GhzRev & AR_RADIO_SREV_MAJOR) { case AR_RAD5111_SREV_MAJOR: case AR_RAD5112_SREV_MAJOR: case AR_RAD2112_SREV_MAJOR: case AR_RAD2111_SREV_MAJOR: case AR_RAD2413_SREV_MAJOR: case AR_RAD5413_SREV_MAJOR: case AR_RAD5424_SREV_MAJOR: break; default: if (AH_PRIVATE(ah)->ah_analog5GhzRev == 0) { /* * When RF_Silent is used, the * analog chip is reset. So when the system boots * up with the radio switch off we cannot determine * the RF chip rev. To workaround this check the * mac+phy revs and if Hainan, set the radio rev * to Derby. */ if (AH_PRIVATE(ah)->ah_macVersion == AR_SREV_VERSION_VENICE && AH_PRIVATE(ah)->ah_macRev == AR_SREV_HAINAN && AH_PRIVATE(ah)->ah_phyRev == AR_PHYREV_HAINAN) { AH_PRIVATE(ah)->ah_analog5GhzRev = AR_ANALOG5REV_HAINAN; break; } if (IS_2413(ah)) { /* Griffin */ AH_PRIVATE(ah)->ah_analog5GhzRev = AR_RAD2413_SREV_MAJOR | 0x1; break; } if (IS_5413(ah)) { /* Eagle */ AH_PRIVATE(ah)->ah_analog5GhzRev = AR_RAD5413_SREV_MAJOR | 0x2; break; } if (IS_2425(ah) || IS_2417(ah)) {/* Swan or Nala */ AH_PRIVATE(ah)->ah_analog5GhzRev = AR_RAD5424_SREV_MAJOR | 0x2; break; } } #ifdef AH_DEBUG HALDEBUG(ah, HAL_DEBUG_ANY, "%s: 5G Radio Chip Rev 0x%02X is not supported by " "this driver\n", __func__, AH_PRIVATE(ah)->ah_analog5GhzRev); ecode = HAL_ENOTSUPP; goto bad; #endif } if (IS_RAD5112_REV1(ah)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: 5112 Rev 1 is not supported by this " "driver (analog5GhzRev 0x%x)\n", __func__, AH_PRIVATE(ah)->ah_analog5GhzRev); ecode = HAL_ENOTSUPP; goto bad; } val = OS_REG_READ(ah, AR_PCICFG); val = MS(val, AR_PCICFG_EEPROM_SIZE); if (val == 0) { if (!AH_PRIVATE(ah)->ah_ispcie) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unsupported EEPROM size %u (0x%x) found\n", __func__, val, val); ecode = HAL_EESIZE; goto bad; } /* XXX AH_PRIVATE(ah)->ah_isPciExpress = AH_TRUE; */ } else if (val != AR_PCICFG_EEPROM_SIZE_16K) { if (AR_PCICFG_EEPROM_SIZE_FAILED == val) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unsupported EEPROM size %u (0x%x) found\n", __func__, val, val); ecode = HAL_EESIZE; goto bad; } HALDEBUG(ah, HAL_DEBUG_ANY, "%s: EEPROM size = %d. Must be %d (16k).\n", __func__, val, AR_PCICFG_EEPROM_SIZE_16K); ecode = HAL_EESIZE; goto bad; } ecode = ath_hal_legacyEepromAttach(ah); if (ecode != HAL_OK) { goto bad; } ahp->ah_isHb63 = IS_2425(ah) && ath_hal_eepromGetFlag(ah, AR_EEP_ISTALON); /* * If Bmode and AR5212, verify 2.4 analog exists */ if (ath_hal_eepromGetFlag(ah, AR_EEP_BMODE) && (AH_PRIVATE(ah)->ah_analog5GhzRev & 0xF0) == AR_RAD5111_SREV_MAJOR) { /* * Set correct Baseband to analog shift * setting to access analog chips. */ OS_REG_WRITE(ah, AR_PHY(0), 0x00004007); OS_DELAY(2000); AH_PRIVATE(ah)->ah_analog2GhzRev = ar5212GetRadioRev(ah); /* Set baseband for 5GHz chip */ OS_REG_WRITE(ah, AR_PHY(0), 0x00000007); OS_DELAY(2000); if ((AH_PRIVATE(ah)->ah_analog2GhzRev & 0xF0) != AR_RAD2111_SREV_MAJOR) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: 2G Radio Chip Rev 0x%02X is not " "supported by this driver\n", __func__, AH_PRIVATE(ah)->ah_analog2GhzRev); ecode = HAL_ENOTSUPP; goto bad; } } ecode = ath_hal_eepromGet(ah, AR_EEP_REGDMN_0, &eeval); if (ecode != HAL_OK) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: cannot read regulatory domain from EEPROM\n", __func__); goto bad; } AH_PRIVATE(ah)->ah_currentRD = eeval; /* XXX record serial number */ /* * Got everything we need now to setup the capabilities. */ if (!ar5212FillCapabilityInfo(ah)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: failed ar5212FillCapabilityInfo\n", __func__); ecode = HAL_EEREAD; goto bad; } if (!rf->attach(ah, &ecode)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: RF setup failed, status %u\n", __func__, ecode); goto bad; } /* * Set noise floor adjust method; we arrange a * direct call instead of thunking. */ AH_PRIVATE(ah)->ah_getNfAdjust = ahp->ah_rfHal->getNfAdjust; /* Initialize gain ladder thermal calibration structure */ ar5212InitializeGainValues(ah); ecode = ath_hal_eepromGet(ah, AR_EEP_MACADDR, ahp->ah_macaddr); if (ecode != HAL_OK) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: error getting mac address from EEPROM\n", __func__); goto bad; } ar5212AniSetup(ah); /* Setup of Radar/AR structures happens in ath_hal_initchannels*/ ar5212InitNfCalHistBuffer(ah); /* XXX EAR stuff goes here */ HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s: return\n", __func__); return ah; bad: if (ahp) ar5212Detach((struct ath_hal *) ahp); if (status) *status = ecode; return AH_NULL; #undef AH_EEPROM_PROTECT } void ar5212Detach(struct ath_hal *ah) { HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s:\n", __func__); HALASSERT(ah != AH_NULL); HALASSERT(ah->ah_magic == AR5212_MAGIC); ar5212AniDetach(ah); ar5212RfDetach(ah); ar5212Disable(ah); ar5212SetPowerMode(ah, HAL_PM_FULL_SLEEP, AH_TRUE); ath_hal_eepromDetach(ah); ath_hal_free(ah); } HAL_BOOL ar5212ChipTest(struct ath_hal *ah) { uint32_t regAddr[2] = { AR_STA_ID0, AR_PHY_BASE+(8 << 2) }; uint32_t regHold[2]; uint32_t patternData[4] = { 0x55555555, 0xaaaaaaaa, 0x66666666, 0x99999999 }; int i, j; /* Test PHY & MAC registers */ for (i = 0; i < 2; i++) { uint32_t addr = regAddr[i]; uint32_t wrData, rdData; regHold[i] = OS_REG_READ(ah, addr); for (j = 0; j < 0x100; j++) { wrData = (j << 16) | j; OS_REG_WRITE(ah, addr, wrData); rdData = OS_REG_READ(ah, addr); if (rdData != wrData) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: address test failed addr: 0x%08x - wr:0x%08x != rd:0x%08x\n", __func__, addr, wrData, rdData); return AH_FALSE; } } for (j = 0; j < 4; j++) { wrData = patternData[j]; OS_REG_WRITE(ah, addr, wrData); rdData = OS_REG_READ(ah, addr); if (wrData != rdData) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: address test failed addr: 0x%08x - wr:0x%08x != rd:0x%08x\n", __func__, addr, wrData, rdData); return AH_FALSE; } } OS_REG_WRITE(ah, regAddr[i], regHold[i]); } OS_DELAY(100); return AH_TRUE; } /* * Store the channel edges for the requested operational mode */ HAL_BOOL ar5212GetChannelEdges(struct ath_hal *ah, uint16_t flags, uint16_t *low, uint16_t *high) { if (flags & IEEE80211_CHAN_5GHZ) { *low = 4915; *high = 6100; return AH_TRUE; } if ((flags & IEEE80211_CHAN_2GHZ) && (ath_hal_eepromGetFlag(ah, AR_EEP_BMODE) || ath_hal_eepromGetFlag(ah, AR_EEP_GMODE))) { *low = 2312; *high = 2732; return AH_TRUE; } return AH_FALSE; } /* * Disable PLL when in L0s as well as receiver clock when in L1. * This power saving option must be enabled through the Serdes. * * Programming the Serdes must go through the same 288 bit serial shift * register as the other analog registers. Hence the 9 writes. * * XXX Clean up the magic numbers. */ static void ar5212ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore, HAL_BOOL power_off) { OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fc00); OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924); /* RX shut off when elecidle is asserted */ OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x28000039); OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x53160824); OS_REG_WRITE(ah, AR_PCIE_SERDES, 0xe5980579); /* Shut off PLL and CLKREQ active in L1 */ OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x001defff); OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x1aaabe40); OS_REG_WRITE(ah, AR_PCIE_SERDES, 0xbe105554); OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x000e3007); /* Load the new settings */ OS_REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000); } static void ar5212DisablePCIE(struct ath_hal *ah) { /* NB: fill in for 9100 */ } /* * Fill all software cached or static hardware state information. * Return failure if capabilities are to come from EEPROM and * cannot be read. */ HAL_BOOL ar5212FillCapabilityInfo(struct ath_hal *ah) { #define AR_KEYTABLE_SIZE 128 #define IS_GRIFFIN_LITE(ah) \ (AH_PRIVATE(ah)->ah_macVersion == AR_SREV_VERSION_GRIFFIN && \ AH_PRIVATE(ah)->ah_macRev == AR_SREV_GRIFFIN_LITE) #define IS_COBRA(ah) \ (AH_PRIVATE(ah)->ah_macVersion == AR_SREV_VERSION_COBRA) #define IS_2112(ah) \ ((AH_PRIVATE(ah)->ah_analog5GhzRev & 0xF0) == AR_RAD2112_SREV_MAJOR) struct ath_hal_private *ahpriv = AH_PRIVATE(ah); HAL_CAPABILITIES *pCap = &ahpriv->ah_caps; uint16_t capField, val; /* Read the capability EEPROM location */ if (ath_hal_eepromGet(ah, AR_EEP_OPCAP, &capField) != HAL_OK) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unable to read caps from eeprom\n", __func__); return AH_FALSE; } if (IS_2112(ah)) ath_hal_eepromSet(ah, AR_EEP_AMODE, AH_FALSE); if (capField == 0 && IS_GRIFFIN_LITE(ah)) { /* * For griffin-lite cards with unprogrammed capabilities. */ ath_hal_eepromSet(ah, AR_EEP_COMPRESS, AH_FALSE); ath_hal_eepromSet(ah, AR_EEP_FASTFRAME, AH_FALSE); ath_hal_eepromSet(ah, AR_EEP_TURBO5DISABLE, AH_TRUE); ath_hal_eepromSet(ah, AR_EEP_TURBO2DISABLE, AH_TRUE); HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s: override caps for griffin-lite, now 0x%x (+!turbo)\n", __func__, capField); } /* Modify reg domain on newer cards that need to work with older sw */ if (ahpriv->ah_opmode != HAL_M_HOSTAP && ahpriv->ah_subvendorid == AR_SUBVENDOR_ID_NEW_A) { if (ahpriv->ah_currentRD == 0x64 || ahpriv->ah_currentRD == 0x65) ahpriv->ah_currentRD += 5; else if (ahpriv->ah_currentRD == 0x41) ahpriv->ah_currentRD = 0x43; HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s: regdomain mapped to 0x%x\n", __func__, ahpriv->ah_currentRD); } if (AH_PRIVATE(ah)->ah_macVersion == AR_SREV_2417 || AH_PRIVATE(ah)->ah_macVersion == AR_SREV_2425) { HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s: enable Bmode and disable turbo for Swan/Nala\n", __func__); ath_hal_eepromSet(ah, AR_EEP_BMODE, AH_TRUE); ath_hal_eepromSet(ah, AR_EEP_COMPRESS, AH_FALSE); ath_hal_eepromSet(ah, AR_EEP_FASTFRAME, AH_FALSE); ath_hal_eepromSet(ah, AR_EEP_TURBO5DISABLE, AH_TRUE); ath_hal_eepromSet(ah, AR_EEP_TURBO2DISABLE, AH_TRUE); } /* Construct wireless mode from EEPROM */ pCap->halWirelessModes = 0; if (ath_hal_eepromGetFlag(ah, AR_EEP_AMODE)) { pCap->halWirelessModes |= HAL_MODE_11A; if (!ath_hal_eepromGetFlag(ah, AR_EEP_TURBO5DISABLE)) pCap->halWirelessModes |= HAL_MODE_TURBO; } if (ath_hal_eepromGetFlag(ah, AR_EEP_BMODE)) pCap->halWirelessModes |= HAL_MODE_11B; if (ath_hal_eepromGetFlag(ah, AR_EEP_GMODE) && ahpriv->ah_subvendorid != AR_SUBVENDOR_ID_NOG) { pCap->halWirelessModes |= HAL_MODE_11G; if (!ath_hal_eepromGetFlag(ah, AR_EEP_TURBO2DISABLE)) pCap->halWirelessModes |= HAL_MODE_108G; } pCap->halLow2GhzChan = 2312; /* XXX 2417 too? */ if (IS_RAD5112_ANY(ah) || IS_5413(ah) || IS_2425(ah) || IS_2417(ah)) pCap->halHigh2GhzChan = 2500; else pCap->halHigh2GhzChan = 2732; /* * For AR5111 version < 4, the lowest centre frequency supported is * 5130MHz. For AR5111 version 4, the 4.9GHz channels are supported * but only in 10MHz increments. * * In addition, the programming method is wrong - it uses the IEEE * channel number to calculate the frequency, rather than the * channel centre. Since half/quarter rates re-use some of the * 5GHz channel IEEE numbers, this will result in a badly programmed * synth. * * Until the relevant support is written, just limit lower frequency * support for AR5111 so things aren't incorrectly programmed. * * XXX It's also possible this code doesn't correctly limit the * centre frequencies of potential channels; this is very important * for half/quarter rate! */ if (AH_RADIO_MAJOR(ah) == AR_RAD5111_SREV_MAJOR) { pCap->halLow5GhzChan = 5120; /* XXX lowest centre = 5130MHz */ } else { pCap->halLow5GhzChan = 4915; } pCap->halHigh5GhzChan = 6100; pCap->halCipherCkipSupport = AH_FALSE; pCap->halCipherTkipSupport = AH_TRUE; pCap->halCipherAesCcmSupport = (ath_hal_eepromGetFlag(ah, AR_EEP_AES) && ((AH_PRIVATE(ah)->ah_macVersion > AR_SREV_VERSION_VENICE) || ((AH_PRIVATE(ah)->ah_macVersion == AR_SREV_VERSION_VENICE) && (AH_PRIVATE(ah)->ah_macRev >= AR_SREV_VERSION_OAHU)))); pCap->halMicCkipSupport = AH_FALSE; pCap->halMicTkipSupport = AH_TRUE; pCap->halMicAesCcmSupport = ath_hal_eepromGetFlag(ah, AR_EEP_AES); /* * Starting with Griffin TX+RX mic keys can be combined * in one key cache slot. */ if (AH_PRIVATE(ah)->ah_macVersion >= AR_SREV_VERSION_GRIFFIN) pCap->halTkipMicTxRxKeySupport = AH_TRUE; else pCap->halTkipMicTxRxKeySupport = AH_FALSE; pCap->halChanSpreadSupport = AH_TRUE; pCap->halSleepAfterBeaconBroken = AH_TRUE; if (ahpriv->ah_macRev > 1 || IS_COBRA(ah)) { pCap->halCompressSupport = ath_hal_eepromGetFlag(ah, AR_EEP_COMPRESS) && (pCap->halWirelessModes & (HAL_MODE_11A|HAL_MODE_11G)) != 0; pCap->halBurstSupport = ath_hal_eepromGetFlag(ah, AR_EEP_BURST); pCap->halFastFramesSupport = ath_hal_eepromGetFlag(ah, AR_EEP_FASTFRAME) && (pCap->halWirelessModes & (HAL_MODE_11A|HAL_MODE_11G)) != 0; pCap->halChapTuningSupport = AH_TRUE; pCap->halTurboPrimeSupport = AH_TRUE; } pCap->halTurboGSupport = pCap->halWirelessModes & HAL_MODE_108G; pCap->halPSPollBroken = AH_TRUE; /* XXX fixed in later revs? */ pCap->halNumMRRetries = 4; /* Hardware supports 4 MRR */ pCap->halNumTxMaps = 1; /* Single TX ptr per descr */ pCap->halVEOLSupport = AH_TRUE; pCap->halBssIdMaskSupport = AH_TRUE; pCap->halMcastKeySrchSupport = AH_TRUE; if ((ahpriv->ah_macVersion == AR_SREV_VERSION_VENICE && ahpriv->ah_macRev == 8) || ahpriv->ah_macVersion > AR_SREV_VERSION_VENICE) pCap->halTsfAddSupport = AH_TRUE; if (ath_hal_eepromGet(ah, AR_EEP_MAXQCU, &val) == HAL_OK) pCap->halTotalQueues = val; else pCap->halTotalQueues = HAL_NUM_TX_QUEUES; if (ath_hal_eepromGet(ah, AR_EEP_KCENTRIES, &val) == HAL_OK) pCap->halKeyCacheSize = val; else pCap->halKeyCacheSize = AR_KEYTABLE_SIZE; pCap->halChanHalfRate = AH_TRUE; pCap->halChanQuarterRate = AH_TRUE; /* * RSSI uses the combined field; some 11n NICs may use * the control chain RSSI. */ pCap->halUseCombinedRadarRssi = AH_TRUE; if (ath_hal_eepromGetFlag(ah, AR_EEP_RFKILL) && ath_hal_eepromGet(ah, AR_EEP_RFSILENT, &ahpriv->ah_rfsilent) == HAL_OK) { /* NB: enabled by default */ ahpriv->ah_rfkillEnabled = AH_TRUE; pCap->halRfSilentSupport = AH_TRUE; } - /* NB: this is a guess, noone seems to know the answer */ + /* NB: this is a guess, no one seems to know the answer */ ahpriv->ah_rxornIsFatal = (AH_PRIVATE(ah)->ah_macVersion < AR_SREV_VERSION_VENICE); /* enable features that first appeared in Hainan */ if ((AH_PRIVATE(ah)->ah_macVersion == AR_SREV_VERSION_VENICE && AH_PRIVATE(ah)->ah_macRev == AR_SREV_HAINAN) || AH_PRIVATE(ah)->ah_macVersion > AR_SREV_VERSION_VENICE) { /* h/w phy counters */ pCap->halHwPhyCounterSupport = AH_TRUE; /* bssid match disable */ pCap->halBssidMatchSupport = AH_TRUE; } pCap->halTstampPrecision = 15; pCap->halIntrMask = HAL_INT_COMMON | HAL_INT_RX | HAL_INT_TX | HAL_INT_FATAL | HAL_INT_BNR | HAL_INT_BMISC ; if (AH_PRIVATE(ah)->ah_macVersion < AR_SREV_VERSION_GRIFFIN) pCap->halIntrMask &= ~HAL_INT_TBTT; pCap->hal4kbSplitTransSupport = AH_TRUE; pCap->halHasRxSelfLinkedTail = AH_TRUE; return AH_TRUE; #undef IS_COBRA #undef IS_GRIFFIN_LITE #undef AR_KEYTABLE_SIZE } static const char* ar5212Probe(uint16_t vendorid, uint16_t devid) { if (vendorid == ATHEROS_VENDOR_ID || vendorid == ATHEROS_3COM_VENDOR_ID || vendorid == ATHEROS_3COM2_VENDOR_ID) { switch (devid) { case AR5212_FPGA: return "Atheros 5212 (FPGA)"; case AR5212_DEVID: case AR5212_DEVID_IBM: case AR5212_DEFAULT: return "Atheros 5212"; case AR5212_AR2413: return "Atheros 2413"; case AR5212_AR2417: return "Atheros 2417"; case AR5212_AR5413: return "Atheros 5413"; case AR5212_AR5424: return "Atheros 5424/2424"; } } return AH_NULL; } AH_CHIP(AR5212, ar5212Probe, ar5212Attach); Index: head/sys/dev/ath/ath_hal/ar5212/ar5212_power.c =================================================================== --- head/sys/dev/ath/ath_hal/ar5212/ar5212_power.c (revision 298938) +++ head/sys/dev/ath/ath_hal/ar5212/ar5212_power.c (revision 298939) @@ -1,179 +1,179 @@ /* * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * Copyright (c) 2002-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ar5212/ar5212.h" #include "ar5212/ar5212reg.h" #include "ar5212/ar5212desc.h" /* * Notify Power Mgt is enabled in self-generated frames. * If requested, force chip awake. * * Returns A_OK if chip is awake or successfully forced awake. * * WARNING WARNING WARNING * There is a problem with the chip where sometimes it will not wake up. */ static HAL_BOOL ar5212SetPowerModeAwake(struct ath_hal *ah, int setChip) { #define AR_SCR_MASK \ (AR_SCR_SLDUR|AR_SCR_SLE|AR_SCR_SLDTP|AR_SCR_SLDWP|\ AR_SCR_SLEPOL|AR_SCR_MIBIE|AR_SCR_UNKNOWN) #define POWER_UP_TIME 2000 uint32_t scr, val; int i; if (setChip) { /* * Be careful setting the AWAKE mode. When we are called * with the chip powered down the read returns 0xffffffff * which when blindly written back with OS_REG_RMW_FIELD * enables the MIB interrupt for the sleep performance * counters. This can result in an interrupt storm when - * ANI is in operation as noone knows to turn off the MIB + * ANI is in operation as no one knows to turn off the MIB * interrupt cause. */ scr = OS_REG_READ(ah, AR_SCR); if (scr & ~AR_SCR_MASK) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: bogus SCR 0x%x, PCICFG 0x%x\n", __func__, scr, OS_REG_READ(ah, AR_PCICFG)); scr = 0; } scr = (scr &~ AR_SCR_SLE) | AR_SCR_SLE_WAKE; OS_REG_WRITE(ah, AR_SCR, scr); OS_DELAY(10); /* Give chip the chance to awake */ for (i = POWER_UP_TIME / 50; i != 0; i--) { val = OS_REG_READ(ah, AR_PCICFG); if ((val & AR_PCICFG_SPWR_DN) == 0) break; OS_DELAY(50); OS_REG_WRITE(ah, AR_SCR, scr); } if (i == 0) { #ifdef AH_DEBUG ath_hal_printf(ah, "%s: Failed to wakeup in %ums\n", __func__, POWER_UP_TIME/50); #endif return AH_FALSE; } } OS_REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV); return AH_TRUE; #undef POWER_UP_TIME #undef AR_SCR_MASK } /* * Notify Power Mgt is disabled in self-generated frames. * If requested, force chip to sleep. */ static void ar5212SetPowerModeSleep(struct ath_hal *ah, int setChip) { OS_REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV); if (setChip) OS_REG_RMW_FIELD(ah, AR_SCR, AR_SCR_SLE, AR_SCR_SLE_SLP); } /* * Notify Power Management is enabled in self-generating * fames. If request, set power mode of chip to * auto/normal. Duration in units of 128us (1/8 TU). */ static void ar5212SetPowerModeNetworkSleep(struct ath_hal *ah, int setChip) { OS_REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV); if (setChip) OS_REG_RMW_FIELD(ah, AR_SCR, AR_SCR_SLE, AR_SCR_SLE_NORM); } /* * Set power mgt to the requested mode, and conditionally set * the chip as well */ HAL_BOOL ar5212SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip) { #ifdef AH_DEBUG static const char* modes[] = { "AWAKE", "FULL-SLEEP", "NETWORK SLEEP", "UNDEFINED" }; #endif int status = AH_TRUE; HALDEBUG(ah, HAL_DEBUG_POWER, "%s: %s -> %s (%s)\n", __func__, modes[ah->ah_powerMode], modes[mode], setChip ? "set chip " : ""); switch (mode) { case HAL_PM_AWAKE: if (setChip) ah->ah_powerMode = mode; status = ar5212SetPowerModeAwake(ah, setChip); break; case HAL_PM_FULL_SLEEP: ar5212SetPowerModeSleep(ah, setChip); if (setChip) ah->ah_powerMode = mode; break; case HAL_PM_NETWORK_SLEEP: ar5212SetPowerModeNetworkSleep(ah, setChip); if (setChip) ah->ah_powerMode = mode; break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unknown power mode %u\n", __func__, mode); return AH_FALSE; } return status; } /* * Return the current sleep mode of the chip */ HAL_POWER_MODE ar5212GetPowerMode(struct ath_hal *ah) { /* Just so happens the h/w maps directly to the abstracted value */ return MS(OS_REG_READ(ah, AR_SCR), AR_SCR_SLE); } #if 0 /* * Return the current sleep state of the chip * TRUE = sleeping */ HAL_BOOL ar5212GetPowerStatus(struct ath_hal *ah) { return (OS_REG_READ(ah, AR_PCICFG) & AR_PCICFG_SPWR_DN) != 0; } #endif Index: head/sys/dev/ath/ath_hal/ar5212/ar5212_reset.c =================================================================== --- head/sys/dev/ath/ath_hal/ar5212/ar5212_reset.c (revision 298938) +++ head/sys/dev/ath/ath_hal/ar5212/ar5212_reset.c (revision 298939) @@ -1,2799 +1,2799 @@ /* * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * Copyright (c) 2002-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ah_devid.h" #include "ar5212/ar5212.h" #include "ar5212/ar5212reg.h" #include "ar5212/ar5212phy.h" #include "ah_eeprom_v3.h" /* Additional Time delay to wait after activiting the Base band */ #define BASE_ACTIVATE_DELAY 100 /* 100 usec */ #define PLL_SETTLE_DELAY 300 /* 300 usec */ static HAL_BOOL ar5212SetResetReg(struct ath_hal *, uint32_t resetMask); /* NB: public for 5312 use */ HAL_BOOL ar5212IsSpurChannel(struct ath_hal *, const struct ieee80211_channel *); HAL_BOOL ar5212ChannelChange(struct ath_hal *, const struct ieee80211_channel *); int16_t ar5212GetNf(struct ath_hal *, struct ieee80211_channel *); HAL_BOOL ar5212SetBoardValues(struct ath_hal *, const struct ieee80211_channel *); void ar5212SetDeltaSlope(struct ath_hal *, const struct ieee80211_channel *); HAL_BOOL ar5212SetTransmitPower(struct ath_hal *ah, const struct ieee80211_channel *chan, uint16_t *rfXpdGain); static HAL_BOOL ar5212SetRateTable(struct ath_hal *, const struct ieee80211_channel *, int16_t tpcScaleReduction, int16_t powerLimit, HAL_BOOL commit, int16_t *minPower, int16_t *maxPower); static void ar5212CorrectGainDelta(struct ath_hal *, int twiceOfdmCckDelta); static void ar5212GetTargetPowers(struct ath_hal *, const struct ieee80211_channel *, const TRGT_POWER_INFO *pPowerInfo, uint16_t numChannels, TRGT_POWER_INFO *pNewPower); static uint16_t ar5212GetMaxEdgePower(uint16_t channel, const RD_EDGES_POWER *pRdEdgesPower); void ar5212SetRateDurationTable(struct ath_hal *, const struct ieee80211_channel *); void ar5212SetIFSTiming(struct ath_hal *, const struct ieee80211_channel *); /* NB: public for RF backend use */ void ar5212GetLowerUpperValues(uint16_t value, uint16_t *pList, uint16_t listSize, uint16_t *pLowerValue, uint16_t *pUpperValue); void ar5212ModifyRfBuffer(uint32_t *rfBuf, uint32_t reg32, uint32_t numBits, uint32_t firstBit, uint32_t column); static int write_common(struct ath_hal *ah, const HAL_INI_ARRAY *ia, HAL_BOOL bChannelChange, int writes) { #define IS_NO_RESET_TIMER_ADDR(x) \ ( (((x) >= AR_BEACON) && ((x) <= AR_CFP_DUR)) || \ (((x) >= AR_SLEEP1) && ((x) <= AR_SLEEP3))) #define V(r, c) (ia)->data[((r)*(ia)->cols) + (c)] int r; /* Write Common Array Parameters */ for (r = 0; r < ia->rows; r++) { uint32_t reg = V(r, 0); /* XXX timer/beacon setup registers? */ /* On channel change, don't reset the PCU registers */ if (!(bChannelChange && IS_NO_RESET_TIMER_ADDR(reg))) { OS_REG_WRITE(ah, reg, V(r, 1)); DMA_YIELD(writes); } } return writes; #undef IS_NO_RESET_TIMER_ADDR #undef V } #define IS_DISABLE_FAST_ADC_CHAN(x) (((x) == 2462) || ((x) == 2467)) /* * XXX NDIS 5.x code had MAX_RESET_WAIT set to 2000 for AP code * and 10 for Client code */ #define MAX_RESET_WAIT 10 #define TX_QUEUEPEND_CHECK 1 #define TX_ENABLE_CHECK 2 #define RX_ENABLE_CHECK 4 /* * Places the device in and out of reset and then places sane * values in the registers based on EEPROM config, initialization * vectors (as determined by the mode), and station configuration * * bChannelChange is used to preserve DMA/PCU registers across * a HW Reset during channel change. */ HAL_BOOL ar5212Reset(struct ath_hal *ah, HAL_OPMODE opmode, struct ieee80211_channel *chan, HAL_BOOL bChannelChange, HAL_RESET_TYPE resetType, HAL_STATUS *status) { #define N(a) (sizeof (a) / sizeof (a[0])) #define FAIL(_code) do { ecode = _code; goto bad; } while (0) struct ath_hal_5212 *ahp = AH5212(ah); HAL_CHANNEL_INTERNAL *ichan = AH_NULL; const HAL_EEPROM *ee; uint32_t softLedCfg, softLedState; uint32_t saveFrameSeqCount, saveDefAntenna, saveLedState; uint32_t macStaId1, synthDelay, txFrm2TxDStart; uint16_t rfXpdGain[MAX_NUM_PDGAINS_PER_CHANNEL]; int16_t cckOfdmPwrDelta = 0; u_int modesIndex, freqIndex; HAL_STATUS ecode; int i, regWrites; uint32_t testReg, powerVal; int8_t twiceAntennaGain, twiceAntennaReduction; uint32_t ackTpcPow, ctsTpcPow, chirpTpcPow; HAL_BOOL isBmode = AH_FALSE; HALASSERT(ah->ah_magic == AR5212_MAGIC); ee = AH_PRIVATE(ah)->ah_eeprom; OS_MARK(ah, AH_MARK_RESET, bChannelChange); /* Bring out of sleep mode */ if (!ar5212SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: chip did not wakeup\n", __func__); FAIL(HAL_EIO); } /* * Map public channel to private. */ ichan = ath_hal_checkchannel(ah, chan); if (ichan == AH_NULL) FAIL(HAL_EINVAL); switch (opmode) { case HAL_M_STA: case HAL_M_IBSS: case HAL_M_HOSTAP: case HAL_M_MONITOR: break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid operating mode %u\n", __func__, opmode); FAIL(HAL_EINVAL); break; } HALASSERT(AH_PRIVATE(ah)->ah_eeversion >= AR_EEPROM_VER3); SAVE_CCK(ah, chan, isBmode); /* Preserve certain DMA hardware registers on a channel change */ if (bChannelChange) { /* * On Venice, the TSF is almost preserved across a reset; * it requires doubling writes to the RESET_TSF * bit in the AR_BEACON register; it also has the quirk * of the TSF going back in time on the station (station * latches onto the last beacon's tsf during a reset 50% * of the times); the latter is not a problem for adhoc * stations since as long as the TSF is behind, it will * get resynchronized on receiving the next beacon; the * TSF going backwards in time could be a problem for the * sleep operation (supported on infrastructure stations * only) - the best and most general fix for this situation * is to resynchronize the various sleep/beacon timers on * the receipt of the next beacon i.e. when the TSF itself * gets resynchronized to the AP's TSF - power save is * needed to be temporarily disabled until that time * * Need to save the sequence number to restore it after * the reset! */ saveFrameSeqCount = OS_REG_READ(ah, AR_D_SEQNUM); } else saveFrameSeqCount = 0; /* NB: silence compiler */ /* Blank the channel survey statistics */ ath_hal_survey_clear(ah); #if 0 /* * XXX disable for now; this appears to sometimes cause OFDM * XXX timing error floods when ani is enabled and bg scanning * XXX kicks in */ /* If the channel change is across the same mode - perform a fast channel change */ if (IS_2413(ah) || IS_5413(ah)) { /* * Fast channel change can only be used when: * -channel change requested - so it's not the initial reset. * -it's not a change to the current channel - * often called when switching modes on a channel * -the modes of the previous and requested channel are the * same * XXX opmode shouldn't change either? */ if (bChannelChange && (AH_PRIVATE(ah)->ah_curchan != AH_NULL) && (chan->ic_freq != AH_PRIVATE(ah)->ah_curchan->ic_freq) && ((chan->ic_flags & IEEE80211_CHAN_ALLTURBO) == (AH_PRIVATE(ah)->ah_curchan->ic_flags & IEEE80211_CHAN_ALLTURBO))) { if (ar5212ChannelChange(ah, chan)) { /* If ChannelChange completed - skip the rest of reset */ /* XXX ani? */ goto done; } } } #endif /* * Preserve the antenna on a channel change */ saveDefAntenna = OS_REG_READ(ah, AR_DEF_ANTENNA); if (saveDefAntenna == 0) /* XXX magic constants */ saveDefAntenna = 1; /* Save hardware flag before chip reset clears the register */ macStaId1 = OS_REG_READ(ah, AR_STA_ID1) & (AR_STA_ID1_BASE_RATE_11B | AR_STA_ID1_USE_DEFANT); /* Save led state from pci config register */ saveLedState = OS_REG_READ(ah, AR_PCICFG) & (AR_PCICFG_LEDCTL | AR_PCICFG_LEDMODE | AR_PCICFG_LEDBLINK | AR_PCICFG_LEDSLOW); softLedCfg = OS_REG_READ(ah, AR_GPIOCR); softLedState = OS_REG_READ(ah, AR_GPIODO); ar5212RestoreClock(ah, opmode); /* move to refclk operation */ /* * Adjust gain parameters before reset if * there's an outstanding gain updated. */ (void) ar5212GetRfgain(ah); if (!ar5212ChipReset(ah, chan)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: chip reset failed\n", __func__); FAIL(HAL_EIO); } /* Setup the indices for the next set of register array writes */ if (IEEE80211_IS_CHAN_2GHZ(chan)) { freqIndex = 2; if (IEEE80211_IS_CHAN_108G(chan)) modesIndex = 5; else if (IEEE80211_IS_CHAN_G(chan)) modesIndex = 4; else if (IEEE80211_IS_CHAN_B(chan)) modesIndex = 3; else { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel %u/0x%x\n", __func__, chan->ic_freq, chan->ic_flags); FAIL(HAL_EINVAL); } } else { freqIndex = 1; if (IEEE80211_IS_CHAN_TURBO(chan)) modesIndex = 2; else if (IEEE80211_IS_CHAN_A(chan)) modesIndex = 1; else { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel %u/0x%x\n", __func__, chan->ic_freq, chan->ic_flags); FAIL(HAL_EINVAL); } } OS_MARK(ah, AH_MARK_RESET_LINE, __LINE__); /* Set correct Baseband to analog shift setting to access analog chips. */ OS_REG_WRITE(ah, AR_PHY(0), 0x00000007); regWrites = ath_hal_ini_write(ah, &ahp->ah_ini_modes, modesIndex, 0); regWrites = write_common(ah, &ahp->ah_ini_common, bChannelChange, regWrites); #ifdef AH_RXCFG_SDMAMW_4BYTES /* * Nala doesn't work with 128 byte bursts on pb42(hydra) (ar71xx), * use 4 instead. Enabling it on all platforms would hurt performance, * so we only enable it on the ones that are affected by it. */ OS_REG_WRITE(ah, AR_RXCFG, 0); #endif ahp->ah_rfHal->writeRegs(ah, modesIndex, freqIndex, regWrites); OS_MARK(ah, AH_MARK_RESET_LINE, __LINE__); if (IEEE80211_IS_CHAN_HALF(chan) || IEEE80211_IS_CHAN_QUARTER(chan)) { ar5212SetIFSTiming(ah, chan); if (IS_5413(ah)) { /* * Force window_length for 1/2 and 1/4 rate channels, * the ini file sets this to zero otherwise. */ OS_REG_RMW_FIELD(ah, AR_PHY_FRAME_CTL, AR_PHY_FRAME_CTL_WINLEN, 3); } } /* Overwrite INI values for revised chipsets */ if (AH_PRIVATE(ah)->ah_phyRev >= AR_PHY_CHIP_ID_REV_2) { /* ADC_CTL */ OS_REG_WRITE(ah, AR_PHY_ADC_CTL, SM(2, AR_PHY_ADC_CTL_OFF_INBUFGAIN) | SM(2, AR_PHY_ADC_CTL_ON_INBUFGAIN) | AR_PHY_ADC_CTL_OFF_PWDDAC | AR_PHY_ADC_CTL_OFF_PWDADC); /* TX_PWR_ADJ */ if (ichan->channel == 2484) { cckOfdmPwrDelta = SCALE_OC_DELTA( ee->ee_cckOfdmPwrDelta - ee->ee_scaledCh14FilterCckDelta); } else { cckOfdmPwrDelta = SCALE_OC_DELTA( ee->ee_cckOfdmPwrDelta); } if (IEEE80211_IS_CHAN_G(chan)) { OS_REG_WRITE(ah, AR_PHY_TXPWRADJ, SM((ee->ee_cckOfdmPwrDelta*-1), AR_PHY_TXPWRADJ_CCK_GAIN_DELTA) | SM((cckOfdmPwrDelta*-1), AR_PHY_TXPWRADJ_CCK_PCDAC_INDEX)); } else { OS_REG_WRITE(ah, AR_PHY_TXPWRADJ, 0); } /* Add barker RSSI thresh enable as disabled */ OS_REG_CLR_BIT(ah, AR_PHY_DAG_CTRLCCK, AR_PHY_DAG_CTRLCCK_EN_RSSI_THR); OS_REG_RMW_FIELD(ah, AR_PHY_DAG_CTRLCCK, AR_PHY_DAG_CTRLCCK_RSSI_THR, 2); /* Set the mute mask to the correct default */ OS_REG_WRITE(ah, AR_SEQ_MASK, 0x0000000F); } if (AH_PRIVATE(ah)->ah_phyRev >= AR_PHY_CHIP_ID_REV_3) { /* Clear reg to alllow RX_CLEAR line debug */ OS_REG_WRITE(ah, AR_PHY_BLUETOOTH, 0); } if (AH_PRIVATE(ah)->ah_phyRev >= AR_PHY_CHIP_ID_REV_4) { #ifdef notyet /* Enable burst prefetch for the data queues */ OS_REG_RMW_FIELD(ah, AR_D_FPCTL, ... ); /* Enable double-buffering */ OS_REG_CLR_BIT(ah, AR_TXCFG, AR_TXCFG_DBL_BUF_DIS); #endif } /* Set ADC/DAC select values */ OS_REG_WRITE(ah, AR_PHY_SLEEP_SCAL, 0x0e); if (IS_5413(ah) || IS_2417(ah)) { uint32_t newReg = 1; if (IS_DISABLE_FAST_ADC_CHAN(ichan->channel)) newReg = 0; /* As it's a clock changing register, only write when the value needs to be changed */ if (OS_REG_READ(ah, AR_PHY_FAST_ADC) != newReg) OS_REG_WRITE(ah, AR_PHY_FAST_ADC, newReg); } /* Setup the transmit power values. */ if (!ar5212SetTransmitPower(ah, chan, rfXpdGain)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: error init'ing transmit power\n", __func__); FAIL(HAL_EIO); } /* Write the analog registers */ if (!ahp->ah_rfHal->setRfRegs(ah, chan, modesIndex, rfXpdGain)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: ar5212SetRfRegs failed\n", __func__); FAIL(HAL_EIO); } /* Write delta slope for OFDM enabled modes (A, G, Turbo) */ if (IEEE80211_IS_CHAN_OFDM(chan)) { if (IS_5413(ah) || AH_PRIVATE(ah)->ah_eeversion >= AR_EEPROM_VER5_3) ar5212SetSpurMitigation(ah, chan); ar5212SetDeltaSlope(ah, chan); } /* Setup board specific options for EEPROM version 3 */ if (!ar5212SetBoardValues(ah, chan)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: error setting board options\n", __func__); FAIL(HAL_EIO); } /* Restore certain DMA hardware registers on a channel change */ if (bChannelChange) OS_REG_WRITE(ah, AR_D_SEQNUM, saveFrameSeqCount); OS_MARK(ah, AH_MARK_RESET_LINE, __LINE__); OS_REG_WRITE(ah, AR_STA_ID0, LE_READ_4(ahp->ah_macaddr)); OS_REG_WRITE(ah, AR_STA_ID1, LE_READ_2(ahp->ah_macaddr + 4) | macStaId1 | AR_STA_ID1_RTS_USE_DEF | ahp->ah_staId1Defaults ); ar5212SetOperatingMode(ah, opmode); /* Set Venice BSSID mask according to current state */ OS_REG_WRITE(ah, AR_BSSMSKL, LE_READ_4(ahp->ah_bssidmask)); OS_REG_WRITE(ah, AR_BSSMSKU, LE_READ_2(ahp->ah_bssidmask + 4)); /* Restore previous led state */ OS_REG_WRITE(ah, AR_PCICFG, OS_REG_READ(ah, AR_PCICFG) | saveLedState); /* Restore soft Led state to GPIO */ OS_REG_WRITE(ah, AR_GPIOCR, softLedCfg); OS_REG_WRITE(ah, AR_GPIODO, softLedState); /* Restore previous antenna */ OS_REG_WRITE(ah, AR_DEF_ANTENNA, saveDefAntenna); /* then our BSSID and associate id */ OS_REG_WRITE(ah, AR_BSS_ID0, LE_READ_4(ahp->ah_bssid)); OS_REG_WRITE(ah, AR_BSS_ID1, LE_READ_2(ahp->ah_bssid + 4) | (ahp->ah_assocId & 0x3fff) << AR_BSS_ID1_AID_S); /* Restore bmiss rssi & count thresholds */ OS_REG_WRITE(ah, AR_RSSI_THR, ahp->ah_rssiThr); OS_REG_WRITE(ah, AR_ISR, ~0); /* cleared on write */ if (!ar5212SetChannel(ah, chan)) FAIL(HAL_EIO); OS_MARK(ah, AH_MARK_RESET_LINE, __LINE__); ar5212SetCoverageClass(ah, AH_PRIVATE(ah)->ah_coverageClass, 1); ar5212SetRateDurationTable(ah, chan); /* Set Tx frame start to tx data start delay */ if (IS_RAD5112_ANY(ah) && (IEEE80211_IS_CHAN_HALF(chan) || IEEE80211_IS_CHAN_QUARTER(chan))) { txFrm2TxDStart = IEEE80211_IS_CHAN_HALF(chan) ? TX_FRAME_D_START_HALF_RATE: TX_FRAME_D_START_QUARTER_RATE; OS_REG_RMW_FIELD(ah, AR_PHY_TX_CTL, AR_PHY_TX_FRAME_TO_TX_DATA_START, txFrm2TxDStart); } /* * Setup fast diversity. * Fast diversity can be enabled or disabled via regadd.txt. * Default is enabled. * For reference, * Disable: reg val * 0x00009860 0x00009d18 (if 11a / 11g, else no change) * 0x00009970 0x192bb514 * 0x0000a208 0xd03e4648 * * Enable: 0x00009860 0x00009d10 (if 11a / 11g, else no change) * 0x00009970 0x192fb514 * 0x0000a208 0xd03e6788 */ /* XXX Setup pre PHY ENABLE EAR additions */ /* * Wait for the frequency synth to settle (synth goes on * via AR_PHY_ACTIVE_EN). Read the phy active delay register. * Value is in 100ns increments. */ synthDelay = OS_REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY; if (IEEE80211_IS_CHAN_B(chan)) { synthDelay = (4 * synthDelay) / 22; } else { synthDelay /= 10; } /* Activate the PHY (includes baseband activate and synthesizer on) */ OS_REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); /* * There is an issue if the AP starts the calibration before * the base band timeout completes. This could result in the * rx_clear false triggering. As a workaround we add delay an * extra BASE_ACTIVATE_DELAY usecs to ensure this condition * does not happen. */ if (IEEE80211_IS_CHAN_HALF(chan)) { OS_DELAY((synthDelay << 1) + BASE_ACTIVATE_DELAY); } else if (IEEE80211_IS_CHAN_QUARTER(chan)) { OS_DELAY((synthDelay << 2) + BASE_ACTIVATE_DELAY); } else { OS_DELAY(synthDelay + BASE_ACTIVATE_DELAY); } /* * The udelay method is not reliable with notebooks. * Need to check to see if the baseband is ready */ testReg = OS_REG_READ(ah, AR_PHY_TESTCTRL); /* Selects the Tx hold */ OS_REG_WRITE(ah, AR_PHY_TESTCTRL, AR_PHY_TESTCTRL_TXHOLD); i = 0; while ((i++ < 20) && (OS_REG_READ(ah, 0x9c24) & 0x10)) /* test if baseband not ready */ OS_DELAY(200); OS_REG_WRITE(ah, AR_PHY_TESTCTRL, testReg); /* Calibrate the AGC and start a NF calculation */ OS_REG_WRITE(ah, AR_PHY_AGC_CONTROL, OS_REG_READ(ah, AR_PHY_AGC_CONTROL) | AR_PHY_AGC_CONTROL_CAL | AR_PHY_AGC_CONTROL_NF); if (!IEEE80211_IS_CHAN_B(chan) && ahp->ah_bIQCalibration != IQ_CAL_DONE) { /* Start IQ calibration w/ 2^(INIT_IQCAL_LOG_COUNT_MAX+1) samples */ OS_REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX, INIT_IQCAL_LOG_COUNT_MAX); OS_REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_DO_IQCAL); ahp->ah_bIQCalibration = IQ_CAL_RUNNING; } else ahp->ah_bIQCalibration = IQ_CAL_INACTIVE; /* Setup compression registers */ ar5212SetCompRegs(ah); /* Set 1:1 QCU to DCU mapping for all queues */ for (i = 0; i < AR_NUM_DCU; i++) OS_REG_WRITE(ah, AR_DQCUMASK(i), 1 << i); ahp->ah_intrTxqs = 0; for (i = 0; i < AH_PRIVATE(ah)->ah_caps.halTotalQueues; i++) ar5212ResetTxQueue(ah, i); /* * Setup interrupt handling. Note that ar5212ResetTxQueue * manipulates the secondary IMR's as queues are enabled * and disabled. This is done with RMW ops to insure the * settings we make here are preserved. */ ahp->ah_maskReg = AR_IMR_TXOK | AR_IMR_TXERR | AR_IMR_TXURN | AR_IMR_RXOK | AR_IMR_RXERR | AR_IMR_RXORN | AR_IMR_HIUERR ; if (opmode == HAL_M_HOSTAP) ahp->ah_maskReg |= AR_IMR_MIB; OS_REG_WRITE(ah, AR_IMR, ahp->ah_maskReg); /* Enable bus errors that are OR'd to set the HIUERR bit */ OS_REG_WRITE(ah, AR_IMR_S2, OS_REG_READ(ah, AR_IMR_S2) | AR_IMR_S2_MCABT | AR_IMR_S2_SSERR | AR_IMR_S2_DPERR); if (AH_PRIVATE(ah)->ah_rfkillEnabled) ar5212EnableRfKill(ah); if (!ath_hal_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: offset calibration failed to complete in 1ms;" " noisy environment?\n", __func__); } /* * Set clocks back to 32kHz if they had been using refClk, then * use an external 32kHz crystal when sleeping, if one exists. */ ar5212SetupClock(ah, opmode); /* * Writing to AR_BEACON will start timers. Hence it should * be the last register to be written. Do not reset tsf, do * not enable beacons at this point, but preserve other values * like beaconInterval. */ OS_REG_WRITE(ah, AR_BEACON, (OS_REG_READ(ah, AR_BEACON) &~ (AR_BEACON_EN | AR_BEACON_RESET_TSF))); /* XXX Setup post reset EAR additions */ /* QoS support */ if (AH_PRIVATE(ah)->ah_macVersion > AR_SREV_VERSION_VENICE || (AH_PRIVATE(ah)->ah_macVersion == AR_SREV_VERSION_VENICE && AH_PRIVATE(ah)->ah_macRev >= AR_SREV_GRIFFIN_LITE)) { OS_REG_WRITE(ah, AR_QOS_CONTROL, 0x100aa); /* XXX magic */ OS_REG_WRITE(ah, AR_QOS_SELECT, 0x3210); /* XXX magic */ } /* Turn on NOACK Support for QoS packets */ OS_REG_WRITE(ah, AR_NOACK, SM(2, AR_NOACK_2BIT_VALUE) | SM(5, AR_NOACK_BIT_OFFSET) | SM(0, AR_NOACK_BYTE_OFFSET)); /* Get Antenna Gain reduction */ if (IEEE80211_IS_CHAN_5GHZ(chan)) { ath_hal_eepromGet(ah, AR_EEP_ANTGAINMAX_5, &twiceAntennaGain); } else { ath_hal_eepromGet(ah, AR_EEP_ANTGAINMAX_2, &twiceAntennaGain); } twiceAntennaReduction = ath_hal_getantennareduction(ah, chan, twiceAntennaGain); /* TPC for self-generated frames */ ackTpcPow = MS(ahp->ah_macTPC, AR_TPC_ACK); if ((ackTpcPow-ahp->ah_txPowerIndexOffset) > chan->ic_maxpower) ackTpcPow = chan->ic_maxpower+ahp->ah_txPowerIndexOffset; if (ackTpcPow > (2*chan->ic_maxregpower - twiceAntennaReduction)) ackTpcPow = (2*chan->ic_maxregpower - twiceAntennaReduction) + ahp->ah_txPowerIndexOffset; ctsTpcPow = MS(ahp->ah_macTPC, AR_TPC_CTS); if ((ctsTpcPow-ahp->ah_txPowerIndexOffset) > chan->ic_maxpower) ctsTpcPow = chan->ic_maxpower+ahp->ah_txPowerIndexOffset; if (ctsTpcPow > (2*chan->ic_maxregpower - twiceAntennaReduction)) ctsTpcPow = (2*chan->ic_maxregpower - twiceAntennaReduction) + ahp->ah_txPowerIndexOffset; chirpTpcPow = MS(ahp->ah_macTPC, AR_TPC_CHIRP); if ((chirpTpcPow-ahp->ah_txPowerIndexOffset) > chan->ic_maxpower) chirpTpcPow = chan->ic_maxpower+ahp->ah_txPowerIndexOffset; if (chirpTpcPow > (2*chan->ic_maxregpower - twiceAntennaReduction)) chirpTpcPow = (2*chan->ic_maxregpower - twiceAntennaReduction) + ahp->ah_txPowerIndexOffset; if (ackTpcPow > 63) ackTpcPow = 63; if (ctsTpcPow > 63) ctsTpcPow = 63; if (chirpTpcPow > 63) chirpTpcPow = 63; powerVal = SM(ackTpcPow, AR_TPC_ACK) | SM(ctsTpcPow, AR_TPC_CTS) | SM(chirpTpcPow, AR_TPC_CHIRP); OS_REG_WRITE(ah, AR_TPC, powerVal); /* Restore user-specified settings */ if (ahp->ah_miscMode != 0) OS_REG_WRITE(ah, AR_MISC_MODE, ahp->ah_miscMode); if (ahp->ah_sifstime != (u_int) -1) ar5212SetSifsTime(ah, ahp->ah_sifstime); if (ahp->ah_slottime != (u_int) -1) ar5212SetSlotTime(ah, ahp->ah_slottime); if (ahp->ah_acktimeout != (u_int) -1) ar5212SetAckTimeout(ah, ahp->ah_acktimeout); if (ahp->ah_ctstimeout != (u_int) -1) ar5212SetCTSTimeout(ah, ahp->ah_ctstimeout); if (AH_PRIVATE(ah)->ah_diagreg != 0) OS_REG_WRITE(ah, AR_DIAG_SW, AH_PRIVATE(ah)->ah_diagreg); AH_PRIVATE(ah)->ah_opmode = opmode; /* record operating mode */ #if 0 done: #endif if (bChannelChange && !IEEE80211_IS_CHAN_DFS(chan)) chan->ic_state &= ~IEEE80211_CHANSTATE_CWINT; HALDEBUG(ah, HAL_DEBUG_RESET, "%s: done\n", __func__); RESTORE_CCK(ah, chan, isBmode); OS_MARK(ah, AH_MARK_RESET_DONE, 0); return AH_TRUE; bad: RESTORE_CCK(ah, chan, isBmode); OS_MARK(ah, AH_MARK_RESET_DONE, ecode); if (status != AH_NULL) *status = ecode; return AH_FALSE; #undef FAIL #undef N } /* * Call the rf backend to change the channel. */ HAL_BOOL ar5212SetChannel(struct ath_hal *ah, const struct ieee80211_channel *chan) { struct ath_hal_5212 *ahp = AH5212(ah); /* Change the synth */ if (!ahp->ah_rfHal->setChannel(ah, chan)) return AH_FALSE; return AH_TRUE; } /* * This channel change evaluates whether the selected hardware can * perform a synthesizer-only channel change (no reset). If the * TX is not stopped, or the RFBus cannot be granted in the given * time, the function returns false as a reset is necessary */ HAL_BOOL ar5212ChannelChange(struct ath_hal *ah, const struct ieee80211_channel *chan) { uint32_t ulCount; uint32_t data, synthDelay, qnum; uint16_t rfXpdGain[MAX_NUM_PDGAINS_PER_CHANNEL]; HAL_BOOL txStopped = AH_TRUE; HAL_CHANNEL_INTERNAL *ichan; /* * Map public channel to private. */ ichan = ath_hal_checkchannel(ah, chan); /* TX must be stopped or RF Bus grant will not work */ for (qnum = 0; qnum < AH_PRIVATE(ah)->ah_caps.halTotalQueues; qnum++) { if (ar5212NumTxPending(ah, qnum)) { txStopped = AH_FALSE; break; } } if (!txStopped) return AH_FALSE; /* Kill last Baseband Rx Frame */ OS_REG_WRITE(ah, AR_PHY_RFBUS_REQ, AR_PHY_RFBUS_REQ_REQUEST); /* Request analog bus grant */ for (ulCount = 0; ulCount < 100; ulCount++) { if (OS_REG_READ(ah, AR_PHY_RFBUS_GNT)) break; OS_DELAY(5); } if (ulCount >= 100) return AH_FALSE; /* Change the synth */ if (!ar5212SetChannel(ah, chan)) return AH_FALSE; /* * Wait for the frequency synth to settle (synth goes on via PHY_ACTIVE_EN). * Read the phy active delay register. Value is in 100ns increments. */ data = OS_REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY; if (IEEE80211_IS_CHAN_B(chan)) { synthDelay = (4 * data) / 22; } else { synthDelay = data / 10; } OS_DELAY(synthDelay + BASE_ACTIVATE_DELAY); /* Setup the transmit power values. */ if (!ar5212SetTransmitPower(ah, chan, rfXpdGain)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: error init'ing transmit power\n", __func__); return AH_FALSE; } /* Write delta slope for OFDM enabled modes (A, G, Turbo) */ if (IEEE80211_IS_CHAN_OFDM(chan)) { if (IS_5413(ah) || AH_PRIVATE(ah)->ah_eeversion >= AR_EEPROM_VER5_3) ar5212SetSpurMitigation(ah, chan); ar5212SetDeltaSlope(ah, chan); } /* Release the RFBus Grant */ OS_REG_WRITE(ah, AR_PHY_RFBUS_REQ, 0); /* Start Noise Floor Cal */ OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); return AH_TRUE; } void ar5212SetOperatingMode(struct ath_hal *ah, int opmode) { uint32_t val; val = OS_REG_READ(ah, AR_STA_ID1); val &= ~(AR_STA_ID1_STA_AP | AR_STA_ID1_ADHOC); switch (opmode) { case HAL_M_HOSTAP: OS_REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_STA_AP | AR_STA_ID1_KSRCH_MODE); OS_REG_CLR_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION); break; case HAL_M_IBSS: OS_REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_ADHOC | AR_STA_ID1_KSRCH_MODE); OS_REG_SET_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION); break; case HAL_M_STA: case HAL_M_MONITOR: OS_REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_KSRCH_MODE); break; } } /* * Places the PHY and Radio chips into reset. A full reset * must be called to leave this state. The PCI/MAC/PCU are * not placed into reset as we must receive interrupt to * re-enable the hardware. */ HAL_BOOL ar5212PhyDisable(struct ath_hal *ah) { return ar5212SetResetReg(ah, AR_RC_BB); } /* * Places all of hardware into reset */ HAL_BOOL ar5212Disable(struct ath_hal *ah) { if (!ar5212SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) return AH_FALSE; /* * Reset the HW - PCI must be reset after the rest of the * device has been reset. */ return ar5212SetResetReg(ah, AR_RC_MAC | AR_RC_BB | AR_RC_PCI); } /* * Places the hardware into reset and then pulls it out of reset * * TODO: Only write the PLL if we're changing to or from CCK mode * * WARNING: The order of the PLL and mode registers must be correct. */ HAL_BOOL ar5212ChipReset(struct ath_hal *ah, const struct ieee80211_channel *chan) { OS_MARK(ah, AH_MARK_CHIPRESET, chan ? chan->ic_freq : 0); /* * Reset the HW - PCI must be reset after the rest of the * device has been reset */ if (!ar5212SetResetReg(ah, AR_RC_MAC | AR_RC_BB | AR_RC_PCI)) return AH_FALSE; /* Bring out of sleep mode (AGAIN) */ if (!ar5212SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) return AH_FALSE; /* Clear warm reset register */ if (!ar5212SetResetReg(ah, 0)) return AH_FALSE; /* * Perform warm reset before the mode/PLL/turbo registers * are changed in order to deactivate the radio. Mode changes * with an active radio can result in corrupted shifts to the * radio device. */ /* * Set CCK and Turbo modes correctly. */ if (chan != AH_NULL) { /* NB: can be null during attach */ uint32_t rfMode, phyPLL = 0, curPhyPLL, turbo; if (IS_5413(ah)) { /* NB: =>'s 5424 also */ rfMode = AR_PHY_MODE_AR5112; if (IEEE80211_IS_CHAN_HALF(chan)) rfMode |= AR_PHY_MODE_HALF; else if (IEEE80211_IS_CHAN_QUARTER(chan)) rfMode |= AR_PHY_MODE_QUARTER; if (IEEE80211_IS_CHAN_CCK(chan)) phyPLL = AR_PHY_PLL_CTL_44_5112; else phyPLL = AR_PHY_PLL_CTL_40_5413; } else if (IS_RAD5111(ah)) { rfMode = AR_PHY_MODE_AR5111; if (IEEE80211_IS_CHAN_CCK(chan)) phyPLL = AR_PHY_PLL_CTL_44; else phyPLL = AR_PHY_PLL_CTL_40; if (IEEE80211_IS_CHAN_HALF(chan)) phyPLL = AR_PHY_PLL_CTL_HALF; else if (IEEE80211_IS_CHAN_QUARTER(chan)) phyPLL = AR_PHY_PLL_CTL_QUARTER; } else { /* 5112, 2413, 2316, 2317 */ rfMode = AR_PHY_MODE_AR5112; if (IEEE80211_IS_CHAN_CCK(chan)) phyPLL = AR_PHY_PLL_CTL_44_5112; else phyPLL = AR_PHY_PLL_CTL_40_5112; if (IEEE80211_IS_CHAN_HALF(chan)) phyPLL |= AR_PHY_PLL_CTL_HALF; else if (IEEE80211_IS_CHAN_QUARTER(chan)) phyPLL |= AR_PHY_PLL_CTL_QUARTER; } if (IEEE80211_IS_CHAN_G(chan)) rfMode |= AR_PHY_MODE_DYNAMIC; else if (IEEE80211_IS_CHAN_OFDM(chan)) rfMode |= AR_PHY_MODE_OFDM; else rfMode |= AR_PHY_MODE_CCK; if (IEEE80211_IS_CHAN_5GHZ(chan)) rfMode |= AR_PHY_MODE_RF5GHZ; else rfMode |= AR_PHY_MODE_RF2GHZ; turbo = IEEE80211_IS_CHAN_TURBO(chan) ? (AR_PHY_FC_TURBO_MODE | AR_PHY_FC_TURBO_SHORT) : 0; curPhyPLL = OS_REG_READ(ah, AR_PHY_PLL_CTL); /* * PLL, Mode, and Turbo values must be written in the correct * order to ensure: * - The PLL cannot be set to 44 unless the CCK or DYNAMIC * mode bit is set * - Turbo cannot be set at the same time as CCK or DYNAMIC */ if (IEEE80211_IS_CHAN_CCK(chan)) { OS_REG_WRITE(ah, AR_PHY_TURBO, turbo); OS_REG_WRITE(ah, AR_PHY_MODE, rfMode); if (curPhyPLL != phyPLL) { OS_REG_WRITE(ah, AR_PHY_PLL_CTL, phyPLL); /* Wait for the PLL to settle */ OS_DELAY(PLL_SETTLE_DELAY); } } else { if (curPhyPLL != phyPLL) { OS_REG_WRITE(ah, AR_PHY_PLL_CTL, phyPLL); /* Wait for the PLL to settle */ OS_DELAY(PLL_SETTLE_DELAY); } OS_REG_WRITE(ah, AR_PHY_TURBO, turbo); OS_REG_WRITE(ah, AR_PHY_MODE, rfMode); } } return AH_TRUE; } /* * Recalibrate the lower PHY chips to account for temperature/environment * changes. */ HAL_BOOL ar5212PerCalibrationN(struct ath_hal *ah, struct ieee80211_channel *chan, u_int chainMask, HAL_BOOL longCal, HAL_BOOL *isCalDone) { #define IQ_CAL_TRIES 10 struct ath_hal_5212 *ahp = AH5212(ah); HAL_CHANNEL_INTERNAL *ichan; int32_t qCoff, qCoffDenom; int32_t iqCorrMeas, iCoff, iCoffDenom; uint32_t powerMeasQ, powerMeasI; HAL_BOOL isBmode = AH_FALSE; OS_MARK(ah, AH_MARK_PERCAL, chan->ic_freq); *isCalDone = AH_FALSE; ichan = ath_hal_checkchannel(ah, chan); if (ichan == AH_NULL) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel %u/0x%x; no mapping\n", __func__, chan->ic_freq, chan->ic_flags); return AH_FALSE; } SAVE_CCK(ah, chan, isBmode); if (ahp->ah_bIQCalibration == IQ_CAL_DONE || ahp->ah_bIQCalibration == IQ_CAL_INACTIVE) *isCalDone = AH_TRUE; /* IQ calibration in progress. Check to see if it has finished. */ if (ahp->ah_bIQCalibration == IQ_CAL_RUNNING && !(OS_REG_READ(ah, AR_PHY_TIMING_CTRL4) & AR_PHY_TIMING_CTRL4_DO_IQCAL)) { int i; /* IQ Calibration has finished. */ ahp->ah_bIQCalibration = IQ_CAL_INACTIVE; *isCalDone = AH_TRUE; /* workaround for misgated IQ Cal results */ i = 0; do { /* Read calibration results. */ powerMeasI = OS_REG_READ(ah, AR_PHY_IQCAL_RES_PWR_MEAS_I); powerMeasQ = OS_REG_READ(ah, AR_PHY_IQCAL_RES_PWR_MEAS_Q); iqCorrMeas = OS_REG_READ(ah, AR_PHY_IQCAL_RES_IQ_CORR_MEAS); if (powerMeasI && powerMeasQ) break; /* Do we really need this??? */ OS_REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_DO_IQCAL); } while (++i < IQ_CAL_TRIES); HALDEBUG(ah, HAL_DEBUG_PERCAL, "%s: IQ cal finished: %d tries\n", __func__, i); HALDEBUG(ah, HAL_DEBUG_PERCAL, "%s: powerMeasI %u powerMeasQ %u iqCorrMeas %d\n", __func__, powerMeasI, powerMeasQ, iqCorrMeas); /* * Prescale these values to remove 64-bit operation * requirement at the loss of a little precision. */ iCoffDenom = (powerMeasI / 2 + powerMeasQ / 2) / 128; qCoffDenom = powerMeasQ / 128; /* Protect against divide-by-0 and loss of sign bits. */ if (iCoffDenom != 0 && qCoffDenom >= 2) { iCoff = (int8_t)(-iqCorrMeas) / iCoffDenom; /* IQCORR_Q_I_COFF is a signed 6 bit number */ if (iCoff < -32) { iCoff = -32; } else if (iCoff > 31) { iCoff = 31; } /* IQCORR_Q_Q_COFF is a signed 5 bit number */ qCoff = (powerMeasI / qCoffDenom) - 128; if (qCoff < -16) { qCoff = -16; } else if (qCoff > 15) { qCoff = 15; } HALDEBUG(ah, HAL_DEBUG_PERCAL, "%s: iCoff %d qCoff %d\n", __func__, iCoff, qCoff); /* Write values and enable correction */ OS_REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF, iCoff); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF, qCoff); OS_REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_IQCORR_ENABLE); ahp->ah_bIQCalibration = IQ_CAL_DONE; ichan->privFlags |= CHANNEL_IQVALID; ichan->iCoff = iCoff; ichan->qCoff = qCoff; } } else if (!IEEE80211_IS_CHAN_B(chan) && ahp->ah_bIQCalibration == IQ_CAL_DONE && (ichan->privFlags & CHANNEL_IQVALID) == 0) { /* * Start IQ calibration if configured channel has changed. * Use a magic number of 15 based on default value. */ OS_REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX, INIT_IQCAL_LOG_COUNT_MAX); OS_REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_DO_IQCAL); ahp->ah_bIQCalibration = IQ_CAL_RUNNING; } /* XXX EAR */ if (longCal) { /* Check noise floor results */ ar5212GetNf(ah, chan); if (!IEEE80211_IS_CHAN_CWINT(chan)) { /* Perform cal for 5Ghz channels and any OFDM on 5112 */ if (IEEE80211_IS_CHAN_5GHZ(chan) || (IS_RAD5112(ah) && IEEE80211_IS_CHAN_OFDM(chan))) ar5212RequestRfgain(ah); } } RESTORE_CCK(ah, chan, isBmode); return AH_TRUE; #undef IQ_CAL_TRIES } HAL_BOOL ar5212PerCalibration(struct ath_hal *ah, struct ieee80211_channel *chan, HAL_BOOL *isIQdone) { return ar5212PerCalibrationN(ah, chan, 0x1, AH_TRUE, isIQdone); } HAL_BOOL ar5212ResetCalValid(struct ath_hal *ah, const struct ieee80211_channel *chan) { HAL_CHANNEL_INTERNAL *ichan; ichan = ath_hal_checkchannel(ah, chan); if (ichan == AH_NULL) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel %u/0x%x; no mapping\n", __func__, chan->ic_freq, chan->ic_flags); return AH_FALSE; } ichan->privFlags &= ~CHANNEL_IQVALID; return AH_TRUE; } /************************************************************** * ar5212MacStop * * Disables all active QCUs and ensure that the mac is in a * quiessence state. */ static HAL_BOOL ar5212MacStop(struct ath_hal *ah) { HAL_BOOL status; uint32_t count; uint32_t pendFrameCount; uint32_t macStateFlag; uint32_t queue; status = AH_FALSE; /* Disable Rx Operation ***********************************/ OS_REG_SET_BIT(ah, AR_CR, AR_CR_RXD); /* Disable TX Operation ***********************************/ #ifdef NOT_YET ar5212SetTxdpInvalid(ah); #endif OS_REG_SET_BIT(ah, AR_Q_TXD, AR_Q_TXD_M); /* Polling operation for completion of disable ************/ macStateFlag = TX_ENABLE_CHECK | RX_ENABLE_CHECK; for (count = 0; count < MAX_RESET_WAIT; count++) { if (macStateFlag & RX_ENABLE_CHECK) { if (!OS_REG_IS_BIT_SET(ah, AR_CR, AR_CR_RXE)) { macStateFlag &= ~RX_ENABLE_CHECK; } } if (macStateFlag & TX_ENABLE_CHECK) { if (!OS_REG_IS_BIT_SET(ah, AR_Q_TXE, AR_Q_TXE_M)) { macStateFlag &= ~TX_ENABLE_CHECK; macStateFlag |= TX_QUEUEPEND_CHECK; } } if (macStateFlag & TX_QUEUEPEND_CHECK) { pendFrameCount = 0; for (queue = 0; queue < AR_NUM_DCU; queue++) { pendFrameCount += OS_REG_READ(ah, AR_Q0_STS + (queue * 4)) & AR_Q_STS_PEND_FR_CNT; } if (pendFrameCount == 0) { macStateFlag &= ~TX_QUEUEPEND_CHECK; } } if (macStateFlag == 0) { status = AH_TRUE; break; } OS_DELAY(50); } if (status != AH_TRUE) { HALDEBUG(ah, HAL_DEBUG_RESET, "%s:Failed to stop the MAC state 0x%x\n", __func__, macStateFlag); } return status; } /* * Write the given reset bit mask into the reset register */ static HAL_BOOL ar5212SetResetReg(struct ath_hal *ah, uint32_t resetMask) { uint32_t mask = resetMask ? resetMask : ~0; HAL_BOOL rt; /* Never reset the PCIE core */ if (AH_PRIVATE(ah)->ah_ispcie) { resetMask &= ~AR_RC_PCI; } if (resetMask & (AR_RC_MAC | AR_RC_PCI)) { /* * To ensure that the driver can reset the * MAC, wake up the chip */ rt = ar5212SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE); if (rt != AH_TRUE) { return rt; } /* * Disable interrupts */ OS_REG_WRITE(ah, AR_IER, AR_IER_DISABLE); OS_REG_READ(ah, AR_IER); if (ar5212MacStop(ah) != AH_TRUE) { /* * Failed to stop the MAC gracefully; let's be more forceful then */ /* need some delay before flush any pending MMR writes */ OS_DELAY(15); OS_REG_READ(ah, AR_RXDP); resetMask |= AR_RC_MAC | AR_RC_BB; /* _Never_ reset PCI Express core */ if (! AH_PRIVATE(ah)->ah_ispcie) { resetMask |= AR_RC_PCI; } #if 0 /* * Flush the park address of the PCI controller */ /* Read PCI slot information less than Hainan revision */ if (AH_PRIVATE(ah)->ah_bustype == HAL_BUS_TYPE_PCI) { if (!IS_5112_REV5_UP(ah)) { #define PCI_COMMON_CONFIG_STATUS 0x06 u_int32_t i; u_int16_t reg16; for (i = 0; i < 32; i++) { ath_hal_read_pci_config_space(ah, PCI_COMMON_CONFIG_STATUS, ®16, sizeof(reg16)); } } #undef PCI_COMMON_CONFIG_STATUS } #endif } else { /* * MAC stopped gracefully; no need to warm-reset the PCI bus */ resetMask &= ~AR_RC_PCI; /* need some delay before flush any pending MMR writes */ OS_DELAY(15); OS_REG_READ(ah, AR_RXDP); } } (void) OS_REG_READ(ah, AR_RXDP);/* flush any pending MMR writes */ OS_REG_WRITE(ah, AR_RC, resetMask); OS_DELAY(15); /* need to wait at least 128 clocks when reseting PCI before read */ mask &= (AR_RC_MAC | AR_RC_BB); resetMask &= (AR_RC_MAC | AR_RC_BB); rt = ath_hal_wait(ah, AR_RC, mask, resetMask); if ((resetMask & AR_RC_MAC) == 0) { if (isBigEndian()) { /* * Set CFG, little-endian for descriptor accesses. */ mask = INIT_CONFIG_STATUS | AR_CFG_SWRD; #ifndef AH_NEED_DESC_SWAP mask |= AR_CFG_SWTD; #endif OS_REG_WRITE(ah, AR_CFG, mask); } else OS_REG_WRITE(ah, AR_CFG, INIT_CONFIG_STATUS); if (ar5212SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) (void) OS_REG_READ(ah, AR_ISR_RAC); } /* track PHY power state so we don't try to r/w BB registers */ AH5212(ah)->ah_phyPowerOn = ((resetMask & AR_RC_BB) == 0); return rt; } int16_t ar5212GetNoiseFloor(struct ath_hal *ah) { int16_t nf = (OS_REG_READ(ah, AR_PHY(25)) >> 19) & 0x1ff; if (nf & 0x100) nf = 0 - ((nf ^ 0x1ff) + 1); return nf; } static HAL_BOOL getNoiseFloorThresh(struct ath_hal *ah, const struct ieee80211_channel *chan, int16_t *nft) { const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; HALASSERT(ah->ah_magic == AR5212_MAGIC); switch (chan->ic_flags & IEEE80211_CHAN_ALLFULL) { case IEEE80211_CHAN_A: *nft = ee->ee_noiseFloorThresh[headerInfo11A]; break; case IEEE80211_CHAN_B: *nft = ee->ee_noiseFloorThresh[headerInfo11B]; break; case IEEE80211_CHAN_G: case IEEE80211_CHAN_PUREG: /* NB: really 108G */ *nft = ee->ee_noiseFloorThresh[headerInfo11G]; break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel flags %u/0x%x\n", __func__, chan->ic_freq, chan->ic_flags); return AH_FALSE; } return AH_TRUE; } /* * Setup the noise floor cal history buffer. */ void ar5212InitNfCalHistBuffer(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); int i; ahp->ah_nfCalHist.first_run = 1; ahp->ah_nfCalHist.currIndex = 0; ahp->ah_nfCalHist.privNF = AR5212_CCA_MAX_GOOD_VALUE; ahp->ah_nfCalHist.invalidNFcount = AR512_NF_CAL_HIST_MAX; for (i = 0; i < AR512_NF_CAL_HIST_MAX; i ++) ahp->ah_nfCalHist.nfCalBuffer[i] = AR5212_CCA_MAX_GOOD_VALUE; } /* * Add a noise floor value to the ring buffer. */ static __inline void updateNFHistBuff(struct ar5212NfCalHist *h, int16_t nf) { h->nfCalBuffer[h->currIndex] = nf; if (++h->currIndex >= AR512_NF_CAL_HIST_MAX) h->currIndex = 0; } /* * Return the median noise floor value in the ring buffer. */ int16_t ar5212GetNfHistMid(const int16_t calData[AR512_NF_CAL_HIST_MAX]) { int16_t sort[AR512_NF_CAL_HIST_MAX]; int i, j; OS_MEMCPY(sort, calData, AR512_NF_CAL_HIST_MAX*sizeof(int16_t)); for (i = 0; i < AR512_NF_CAL_HIST_MAX-1; i ++) { for (j = 1; j < AR512_NF_CAL_HIST_MAX-i; j ++) { if (sort[j] > sort[j-1]) { int16_t nf = sort[j]; sort[j] = sort[j-1]; sort[j-1] = nf; } } } return sort[(AR512_NF_CAL_HIST_MAX-1)>>1]; } /* - * Read the NF and check it against the noise floor threshhold + * Read the NF and check it against the noise floor threshold */ int16_t ar5212GetNf(struct ath_hal *ah, struct ieee80211_channel *chan) { struct ath_hal_5212 *ahp = AH5212(ah); struct ar5212NfCalHist *h = &ahp->ah_nfCalHist; HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); int16_t nf, nfThresh; int32_t val; if (OS_REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: NF did not complete in calibration window\n", __func__); ichan->rawNoiseFloor = h->privNF; /* most recent value */ return ichan->rawNoiseFloor; } /* * Finished NF cal, check against threshold. */ nf = ar5212GetNoiseFloor(ah); if (getNoiseFloorThresh(ah, chan, &nfThresh)) { if (nf > nfThresh) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: noise floor failed detected; detected %u, " "threshold %u\n", __func__, nf, nfThresh); /* * NB: Don't discriminate 2.4 vs 5Ghz, if this * happens it indicates a problem regardless * of the band. */ chan->ic_state |= IEEE80211_CHANSTATE_CWINT; nf = 0; } } else nf = 0; /* * Pass through histogram and write median value as * calculated from the accrued window. We require a * full window of in-range values to be seen before we * start using the history. */ updateNFHistBuff(h, nf); if (h->first_run) { if (nf < AR5212_CCA_MIN_BAD_VALUE || nf > AR5212_CCA_MAX_HIGH_VALUE) { nf = AR5212_CCA_MAX_GOOD_VALUE; h->invalidNFcount = AR512_NF_CAL_HIST_MAX; } else if (--(h->invalidNFcount) == 0) { h->first_run = 0; h->privNF = nf = ar5212GetNfHistMid(h->nfCalBuffer); } else { nf = AR5212_CCA_MAX_GOOD_VALUE; } } else { h->privNF = nf = ar5212GetNfHistMid(h->nfCalBuffer); } val = OS_REG_READ(ah, AR_PHY(25)); val &= 0xFFFFFE00; val |= (((uint32_t)nf << 1) & 0x1FF); OS_REG_WRITE(ah, AR_PHY(25), val); OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF); OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NO_UPDATE_NF); OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); if (!ath_hal_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF, 0)) { #ifdef AH_DEBUG ath_hal_printf(ah, "%s: AGC not ready AGC_CONTROL 0x%x\n", __func__, OS_REG_READ(ah, AR_PHY_AGC_CONTROL)); #endif } /* * Now load a high maxCCAPower value again so that we're * not capped by the median we just loaded */ val &= 0xFFFFFE00; val |= (((uint32_t)(-50) << 1) & 0x1FF); OS_REG_WRITE(ah, AR_PHY(25), val); OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF); OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NO_UPDATE_NF); OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); return (ichan->rawNoiseFloor = nf); } /* * Set up compression configuration registers */ void ar5212SetCompRegs(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); int i; /* Check if h/w supports compression */ if (!AH_PRIVATE(ah)->ah_caps.halCompressSupport) return; OS_REG_WRITE(ah, AR_DCCFG, 1); OS_REG_WRITE(ah, AR_CCFG, (AR_COMPRESSION_WINDOW_SIZE >> 8) & AR_CCFG_WIN_M); OS_REG_WRITE(ah, AR_CCFG, OS_REG_READ(ah, AR_CCFG) | AR_CCFG_MIB_INT_EN); OS_REG_WRITE(ah, AR_CCUCFG, AR_CCUCFG_RESET_VAL | AR_CCUCFG_CATCHUP_EN); OS_REG_WRITE(ah, AR_CPCOVF, 0); /* reset decompression mask */ for (i = 0; i < HAL_DECOMP_MASK_SIZE; i++) { OS_REG_WRITE(ah, AR_DCM_A, i); OS_REG_WRITE(ah, AR_DCM_D, ahp->ah_decompMask[i]); } } HAL_BOOL ar5212SetAntennaSwitchInternal(struct ath_hal *ah, HAL_ANT_SETTING settings, const struct ieee80211_channel *chan) { #define ANT_SWITCH_TABLE1 AR_PHY(88) #define ANT_SWITCH_TABLE2 AR_PHY(89) struct ath_hal_5212 *ahp = AH5212(ah); const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; uint32_t antSwitchA, antSwitchB; int ix; HALASSERT(ah->ah_magic == AR5212_MAGIC); HALASSERT(ahp->ah_phyPowerOn); switch (chan->ic_flags & IEEE80211_CHAN_ALLFULL) { case IEEE80211_CHAN_A: ix = 0; break; case IEEE80211_CHAN_G: case IEEE80211_CHAN_PUREG: /* NB: 108G */ ix = 2; break; case IEEE80211_CHAN_B: if (IS_2425(ah) || IS_2417(ah)) { /* NB: Nala/Swan: 11b is handled using 11g */ ix = 2; } else ix = 1; break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel flags 0x%x\n", __func__, chan->ic_flags); return AH_FALSE; } antSwitchA = ee->ee_antennaControl[1][ix] | (ee->ee_antennaControl[2][ix] << 6) | (ee->ee_antennaControl[3][ix] << 12) | (ee->ee_antennaControl[4][ix] << 18) | (ee->ee_antennaControl[5][ix] << 24) ; antSwitchB = ee->ee_antennaControl[6][ix] | (ee->ee_antennaControl[7][ix] << 6) | (ee->ee_antennaControl[8][ix] << 12) | (ee->ee_antennaControl[9][ix] << 18) | (ee->ee_antennaControl[10][ix] << 24) ; /* * For fixed antenna, give the same setting for both switch banks */ switch (settings) { case HAL_ANT_FIXED_A: antSwitchB = antSwitchA; break; case HAL_ANT_FIXED_B: antSwitchA = antSwitchB; break; case HAL_ANT_VARIABLE: break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: bad antenna setting %u\n", __func__, settings); return AH_FALSE; } if (antSwitchB == antSwitchA) { HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: Setting fast diversity off.\n", __func__); OS_REG_CLR_BIT(ah,AR_PHY_CCK_DETECT, AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV); ahp->ah_diversity = AH_FALSE; } else { HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: Setting fast diversity on.\n", __func__); OS_REG_SET_BIT(ah,AR_PHY_CCK_DETECT, AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV); ahp->ah_diversity = AH_TRUE; } ahp->ah_antControl = settings; OS_REG_WRITE(ah, ANT_SWITCH_TABLE1, antSwitchA); OS_REG_WRITE(ah, ANT_SWITCH_TABLE2, antSwitchB); return AH_TRUE; #undef ANT_SWITCH_TABLE2 #undef ANT_SWITCH_TABLE1 } HAL_BOOL ar5212IsSpurChannel(struct ath_hal *ah, const struct ieee80211_channel *chan) { uint16_t freq = ath_hal_gethwchannel(ah, chan); uint32_t clockFreq = ((IS_5413(ah) || IS_RAD5112_ANY(ah) || IS_2417(ah)) ? 40 : 32); return ( ((freq % clockFreq) != 0) && (((freq % clockFreq) < 10) || (((freq) % clockFreq) > 22)) ); } /* * Read EEPROM header info and program the device for correct operation * given the channel value. */ HAL_BOOL ar5212SetBoardValues(struct ath_hal *ah, const struct ieee80211_channel *chan) { #define NO_FALSE_DETECT_BACKOFF 2 #define CB22_FALSE_DETECT_BACKOFF 6 #define AR_PHY_BIS(_ah, _reg, _mask, _val) \ OS_REG_WRITE(_ah, AR_PHY(_reg), \ (OS_REG_READ(_ah, AR_PHY(_reg)) & _mask) | (_val)); struct ath_hal_5212 *ahp = AH5212(ah); const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; int arrayMode, falseDectectBackoff; int is2GHz = IEEE80211_IS_CHAN_2GHZ(chan); HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); int8_t adcDesiredSize, pgaDesiredSize; uint16_t switchSettling, txrxAtten, rxtxMargin; int iCoff, qCoff; HALASSERT(ah->ah_magic == AR5212_MAGIC); switch (chan->ic_flags & IEEE80211_CHAN_ALLTURBOFULL) { case IEEE80211_CHAN_A: case IEEE80211_CHAN_ST: arrayMode = headerInfo11A; if (!IS_RAD5112_ANY(ah) && !IS_2413(ah) && !IS_5413(ah)) OS_REG_RMW_FIELD(ah, AR_PHY_FRAME_CTL, AR_PHY_FRAME_CTL_TX_CLIP, ahp->ah_gainValues.currStep->paramVal[GP_TXCLIP]); break; case IEEE80211_CHAN_B: arrayMode = headerInfo11B; break; case IEEE80211_CHAN_G: case IEEE80211_CHAN_108G: arrayMode = headerInfo11G; break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel flags 0x%x\n", __func__, chan->ic_flags); return AH_FALSE; } /* Set the antenna register(s) correctly for the chip revision */ AR_PHY_BIS(ah, 68, 0xFFFFFC06, (ee->ee_antennaControl[0][arrayMode] << 4) | 0x1); ar5212SetAntennaSwitchInternal(ah, ahp->ah_antControl, chan); /* Set the Noise Floor Thresh on ar5211 devices */ OS_REG_WRITE(ah, AR_PHY(90), (ee->ee_noiseFloorThresh[arrayMode] & 0x1FF) | (1 << 9)); if (ee->ee_version >= AR_EEPROM_VER5_0 && IEEE80211_IS_CHAN_TURBO(chan)) { switchSettling = ee->ee_switchSettlingTurbo[is2GHz]; adcDesiredSize = ee->ee_adcDesiredSizeTurbo[is2GHz]; pgaDesiredSize = ee->ee_pgaDesiredSizeTurbo[is2GHz]; txrxAtten = ee->ee_txrxAttenTurbo[is2GHz]; rxtxMargin = ee->ee_rxtxMarginTurbo[is2GHz]; } else { switchSettling = ee->ee_switchSettling[arrayMode]; adcDesiredSize = ee->ee_adcDesiredSize[arrayMode]; pgaDesiredSize = ee->ee_pgaDesiredSize[is2GHz]; txrxAtten = ee->ee_txrxAtten[is2GHz]; rxtxMargin = ee->ee_rxtxMargin[is2GHz]; } OS_REG_RMW_FIELD(ah, AR_PHY_SETTLING, AR_PHY_SETTLING_SWITCH, switchSettling); OS_REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, AR_PHY_DESIRED_SZ_ADC, adcDesiredSize); OS_REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, AR_PHY_DESIRED_SZ_PGA, pgaDesiredSize); OS_REG_RMW_FIELD(ah, AR_PHY_RXGAIN, AR_PHY_RXGAIN_TXRX_ATTEN, txrxAtten); OS_REG_WRITE(ah, AR_PHY(13), (ee->ee_txEndToXPAOff[arrayMode] << 24) | (ee->ee_txEndToXPAOff[arrayMode] << 16) | (ee->ee_txFrameToXPAOn[arrayMode] << 8) | ee->ee_txFrameToXPAOn[arrayMode]); AR_PHY_BIS(ah, 10, 0xFFFF00FF, ee->ee_txEndToXLNAOn[arrayMode] << 8); AR_PHY_BIS(ah, 25, 0xFFF80FFF, (ee->ee_thresh62[arrayMode] << 12) & 0x7F000); /* * False detect backoff - suspected 32 MHz spur causes false * detects in OFDM, causing Tx Hangs. Decrease weak signal * sensitivity for this card. */ falseDectectBackoff = NO_FALSE_DETECT_BACKOFF; if (ee->ee_version < AR_EEPROM_VER3_3) { /* XXX magic number */ if (AH_PRIVATE(ah)->ah_subvendorid == 0x1022 && IEEE80211_IS_CHAN_OFDM(chan)) falseDectectBackoff += CB22_FALSE_DETECT_BACKOFF; } else { if (ar5212IsSpurChannel(ah, chan)) falseDectectBackoff += ee->ee_falseDetectBackoff[arrayMode]; } AR_PHY_BIS(ah, 73, 0xFFFFFF01, (falseDectectBackoff << 1) & 0xFE); if (ichan->privFlags & CHANNEL_IQVALID) { iCoff = ichan->iCoff; qCoff = ichan->qCoff; } else { iCoff = ee->ee_iqCalI[is2GHz]; qCoff = ee->ee_iqCalQ[is2GHz]; } /* write previous IQ results */ OS_REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF, iCoff); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF, qCoff); OS_REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_IQCORR_ENABLE); if (ee->ee_version >= AR_EEPROM_VER4_1) { if (!IEEE80211_IS_CHAN_108G(chan) || ee->ee_version >= AR_EEPROM_VER5_0) OS_REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ, AR_PHY_GAIN_2GHZ_RXTX_MARGIN, rxtxMargin); } if (ee->ee_version >= AR_EEPROM_VER5_1) { /* for now always disabled */ OS_REG_WRITE(ah, AR_PHY_HEAVY_CLIP_ENABLE, 0); } return AH_TRUE; #undef AR_PHY_BIS #undef NO_FALSE_DETECT_BACKOFF #undef CB22_FALSE_DETECT_BACKOFF } /* * Apply Spur Immunity to Boards that require it. * Applies only to OFDM RX operation. */ void ar5212SetSpurMitigation(struct ath_hal *ah, const struct ieee80211_channel *chan) { uint32_t pilotMask[2] = {0, 0}, binMagMask[4] = {0, 0, 0 , 0}; uint16_t i, finalSpur, curChanAsSpur, binWidth = 0, spurDetectWidth, spurChan; int32_t spurDeltaPhase = 0, spurFreqSd = 0, spurOffset, binOffsetNumT16, curBinOffset; int16_t numBinOffsets; static const uint16_t magMapFor4[4] = {1, 2, 2, 1}; static const uint16_t magMapFor3[3] = {1, 2, 1}; const uint16_t *pMagMap; HAL_BOOL is2GHz = IEEE80211_IS_CHAN_2GHZ(chan); HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); uint32_t val; #define CHAN_TO_SPUR(_f, _freq) ( ((_freq) - ((_f) ? 2300 : 4900)) * 10 ) if (IS_2417(ah)) { HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: no spur mitigation\n", __func__); return; } curChanAsSpur = CHAN_TO_SPUR(is2GHz, ichan->channel); if (ichan->mainSpur) { /* Pull out the saved spur value */ finalSpur = ichan->mainSpur; } else { /* * Check if spur immunity should be performed for this channel * Should only be performed once per channel and then saved */ finalSpur = AR_NO_SPUR; spurDetectWidth = HAL_SPUR_CHAN_WIDTH; if (IEEE80211_IS_CHAN_TURBO(chan)) spurDetectWidth *= 2; /* Decide if any spur affects the current channel */ for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { spurChan = ath_hal_getSpurChan(ah, i, is2GHz); if (spurChan == AR_NO_SPUR) { break; } if ((curChanAsSpur - spurDetectWidth <= (spurChan & HAL_SPUR_VAL_MASK)) && (curChanAsSpur + spurDetectWidth >= (spurChan & HAL_SPUR_VAL_MASK))) { finalSpur = spurChan & HAL_SPUR_VAL_MASK; break; } } /* Save detected spur (or no spur) for this channel */ ichan->mainSpur = finalSpur; } /* Write spur immunity data */ if (finalSpur == AR_NO_SPUR) { /* Disable Spur Immunity Regs if they appear set */ if (OS_REG_READ(ah, AR_PHY_TIMING_CTRL4) & AR_PHY_TIMING_CTRL4_ENABLE_SPUR_FILTER) { /* Clear Spur Delta Phase, Spur Freq, and enable bits */ OS_REG_RMW_FIELD(ah, AR_PHY_MASK_CTL, AR_PHY_MASK_CTL_RATE, 0); val = OS_REG_READ(ah, AR_PHY_TIMING_CTRL4); val &= ~(AR_PHY_TIMING_CTRL4_ENABLE_SPUR_FILTER | AR_PHY_TIMING_CTRL4_ENABLE_CHAN_MASK | AR_PHY_TIMING_CTRL4_ENABLE_PILOT_MASK); OS_REG_WRITE(ah, AR_PHY_MASK_CTL, val); OS_REG_WRITE(ah, AR_PHY_TIMING11, 0); /* Clear pilot masks */ OS_REG_WRITE(ah, AR_PHY_TIMING7, 0); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING8, AR_PHY_TIMING8_PILOT_MASK_2, 0); OS_REG_WRITE(ah, AR_PHY_TIMING9, 0); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING10, AR_PHY_TIMING10_PILOT_MASK_2, 0); /* Clear magnitude masks */ OS_REG_WRITE(ah, AR_PHY_BIN_MASK_1, 0); OS_REG_WRITE(ah, AR_PHY_BIN_MASK_2, 0); OS_REG_WRITE(ah, AR_PHY_BIN_MASK_3, 0); OS_REG_RMW_FIELD(ah, AR_PHY_MASK_CTL, AR_PHY_MASK_CTL_MASK_4, 0); OS_REG_WRITE(ah, AR_PHY_BIN_MASK2_1, 0); OS_REG_WRITE(ah, AR_PHY_BIN_MASK2_2, 0); OS_REG_WRITE(ah, AR_PHY_BIN_MASK2_3, 0); OS_REG_RMW_FIELD(ah, AR_PHY_BIN_MASK2_4, AR_PHY_BIN_MASK2_4_MASK_4, 0); } } else { spurOffset = finalSpur - curChanAsSpur; /* * Spur calculations: * spurDeltaPhase is (spurOffsetIn100KHz / chipFrequencyIn100KHz) << 21 * spurFreqSd is (spurOffsetIn100KHz / sampleFrequencyIn100KHz) << 11 */ if (IEEE80211_IS_CHAN_TURBO(chan)) { /* Chip Frequency & sampleFrequency are 80 MHz */ spurDeltaPhase = (spurOffset << 16) / 25; spurFreqSd = spurDeltaPhase >> 10; binWidth = HAL_BIN_WIDTH_TURBO_100HZ; } else if (IEEE80211_IS_CHAN_G(chan)) { /* Chip Frequency is 44MHz, sampleFrequency is 40 MHz */ spurFreqSd = (spurOffset << 8) / 55; spurDeltaPhase = (spurOffset << 17) / 25; binWidth = HAL_BIN_WIDTH_BASE_100HZ; } else { HALASSERT(!IEEE80211_IS_CHAN_B(chan)); /* Chip Frequency & sampleFrequency are 40 MHz */ spurDeltaPhase = (spurOffset << 17) / 25; spurFreqSd = spurDeltaPhase >> 10; binWidth = HAL_BIN_WIDTH_BASE_100HZ; } /* Compute Pilot Mask */ binOffsetNumT16 = ((spurOffset * 1000) << 4) / binWidth; /* The spur is on a bin if it's remainder at times 16 is 0 */ if (binOffsetNumT16 & 0xF) { numBinOffsets = 4; pMagMap = magMapFor4; } else { numBinOffsets = 3; pMagMap = magMapFor3; } for (i = 0; i < numBinOffsets; i++) { if ((binOffsetNumT16 >> 4) > HAL_MAX_BINS_ALLOWED) { HALDEBUG(ah, HAL_DEBUG_ANY, "Too man bins in spur mitigation\n"); return; } /* Get Pilot Mask values */ curBinOffset = (binOffsetNumT16 >> 4) + i + 25; if ((curBinOffset >= 0) && (curBinOffset <= 32)) { if (curBinOffset <= 25) pilotMask[0] |= 1 << curBinOffset; else if (curBinOffset >= 27) pilotMask[0] |= 1 << (curBinOffset - 1); } else if ((curBinOffset >= 33) && (curBinOffset <= 52)) pilotMask[1] |= 1 << (curBinOffset - 33); /* Get viterbi values */ if ((curBinOffset >= -1) && (curBinOffset <= 14)) binMagMask[0] |= pMagMap[i] << (curBinOffset + 1) * 2; else if ((curBinOffset >= 15) && (curBinOffset <= 30)) binMagMask[1] |= pMagMap[i] << (curBinOffset - 15) * 2; else if ((curBinOffset >= 31) && (curBinOffset <= 46)) binMagMask[2] |= pMagMap[i] << (curBinOffset -31) * 2; else if((curBinOffset >= 47) && (curBinOffset <= 53)) binMagMask[3] |= pMagMap[i] << (curBinOffset -47) * 2; } /* Write Spur Delta Phase, Spur Freq, and enable bits */ OS_REG_RMW_FIELD(ah, AR_PHY_MASK_CTL, AR_PHY_MASK_CTL_RATE, 0xFF); val = OS_REG_READ(ah, AR_PHY_TIMING_CTRL4); val |= (AR_PHY_TIMING_CTRL4_ENABLE_SPUR_FILTER | AR_PHY_TIMING_CTRL4_ENABLE_CHAN_MASK | AR_PHY_TIMING_CTRL4_ENABLE_PILOT_MASK); OS_REG_WRITE(ah, AR_PHY_TIMING_CTRL4, val); OS_REG_WRITE(ah, AR_PHY_TIMING11, AR_PHY_TIMING11_USE_SPUR_IN_AGC | SM(spurFreqSd, AR_PHY_TIMING11_SPUR_FREQ_SD) | SM(spurDeltaPhase, AR_PHY_TIMING11_SPUR_DELTA_PHASE)); /* Write pilot masks */ OS_REG_WRITE(ah, AR_PHY_TIMING7, pilotMask[0]); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING8, AR_PHY_TIMING8_PILOT_MASK_2, pilotMask[1]); OS_REG_WRITE(ah, AR_PHY_TIMING9, pilotMask[0]); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING10, AR_PHY_TIMING10_PILOT_MASK_2, pilotMask[1]); /* Write magnitude masks */ OS_REG_WRITE(ah, AR_PHY_BIN_MASK_1, binMagMask[0]); OS_REG_WRITE(ah, AR_PHY_BIN_MASK_2, binMagMask[1]); OS_REG_WRITE(ah, AR_PHY_BIN_MASK_3, binMagMask[2]); OS_REG_RMW_FIELD(ah, AR_PHY_MASK_CTL, AR_PHY_MASK_CTL_MASK_4, binMagMask[3]); OS_REG_WRITE(ah, AR_PHY_BIN_MASK2_1, binMagMask[0]); OS_REG_WRITE(ah, AR_PHY_BIN_MASK2_2, binMagMask[1]); OS_REG_WRITE(ah, AR_PHY_BIN_MASK2_3, binMagMask[2]); OS_REG_RMW_FIELD(ah, AR_PHY_BIN_MASK2_4, AR_PHY_BIN_MASK2_4_MASK_4, binMagMask[3]); } #undef CHAN_TO_SPUR } /* * Delta slope coefficient computation. * Required for OFDM operation. */ void ar5212SetDeltaSlope(struct ath_hal *ah, const struct ieee80211_channel *chan) { #define COEF_SCALE_S 24 #define INIT_CLOCKMHZSCALED 0x64000000 uint16_t freq = ath_hal_gethwchannel(ah, chan); unsigned long coef_scaled, coef_exp, coef_man, ds_coef_exp, ds_coef_man; unsigned long clockMhzScaled = INIT_CLOCKMHZSCALED; if (IEEE80211_IS_CHAN_TURBO(chan)) clockMhzScaled *= 2; /* half and quarter rate can divide the scaled clock by 2 or 4 respectively */ /* scale for selected channel bandwidth */ if (IEEE80211_IS_CHAN_HALF(chan)) { clockMhzScaled = clockMhzScaled >> 1; } else if (IEEE80211_IS_CHAN_QUARTER(chan)) { clockMhzScaled = clockMhzScaled >> 2; } /* * ALGO -> coef = 1e8/fcarrier*fclock/40; * scaled coef to provide precision for this floating calculation */ coef_scaled = clockMhzScaled / freq; /* * ALGO -> coef_exp = 14-floor(log2(coef)); * floor(log2(x)) is the highest set bit position */ for (coef_exp = 31; coef_exp > 0; coef_exp--) if ((coef_scaled >> coef_exp) & 0x1) break; /* A coef_exp of 0 is a legal bit position but an unexpected coef_exp */ HALASSERT(coef_exp); coef_exp = 14 - (coef_exp - COEF_SCALE_S); /* * ALGO -> coef_man = floor(coef* 2^coef_exp+0.5); * The coefficient is already shifted up for scaling */ coef_man = coef_scaled + (1 << (COEF_SCALE_S - coef_exp - 1)); ds_coef_man = coef_man >> (COEF_SCALE_S - coef_exp); ds_coef_exp = coef_exp - 16; OS_REG_RMW_FIELD(ah, AR_PHY_TIMING3, AR_PHY_TIMING3_DSC_MAN, ds_coef_man); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING3, AR_PHY_TIMING3_DSC_EXP, ds_coef_exp); #undef INIT_CLOCKMHZSCALED #undef COEF_SCALE_S } /* * Set a limit on the overall output power. Used for dynamic * transmit power control and the like. * * NB: limit is in units of 0.5 dbM. */ HAL_BOOL ar5212SetTxPowerLimit(struct ath_hal *ah, uint32_t limit) { /* XXX blech, construct local writable copy */ struct ieee80211_channel dummy = *AH_PRIVATE(ah)->ah_curchan; uint16_t dummyXpdGains[2]; HAL_BOOL isBmode; SAVE_CCK(ah, &dummy, isBmode); AH_PRIVATE(ah)->ah_powerLimit = AH_MIN(limit, MAX_RATE_POWER); return ar5212SetTransmitPower(ah, &dummy, dummyXpdGains); } /* * Set the transmit power in the baseband for the given * operating channel and mode. */ HAL_BOOL ar5212SetTransmitPower(struct ath_hal *ah, const struct ieee80211_channel *chan, uint16_t *rfXpdGain) { #define POW_OFDM(_r, _s) (((0 & 1)<< ((_s)+6)) | (((_r) & 0x3f) << (_s))) #define POW_CCK(_r, _s) (((_r) & 0x3f) << (_s)) #define N(a) (sizeof (a) / sizeof (a[0])) static const uint16_t tpcScaleReductionTable[5] = { 0, 3, 6, 9, MAX_RATE_POWER }; struct ath_hal_5212 *ahp = AH5212(ah); uint16_t freq = ath_hal_gethwchannel(ah, chan); const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; int16_t minPower, maxPower, tpcInDb, powerLimit; int i; HALASSERT(ah->ah_magic == AR5212_MAGIC); OS_MEMZERO(ahp->ah_pcdacTable, ahp->ah_pcdacTableSize); OS_MEMZERO(ahp->ah_ratesArray, sizeof(ahp->ah_ratesArray)); powerLimit = AH_MIN(MAX_RATE_POWER, AH_PRIVATE(ah)->ah_powerLimit); if (powerLimit >= MAX_RATE_POWER || powerLimit == 0) tpcInDb = tpcScaleReductionTable[AH_PRIVATE(ah)->ah_tpScale]; else tpcInDb = 0; if (!ar5212SetRateTable(ah, chan, tpcInDb, powerLimit, AH_TRUE, &minPower, &maxPower)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unable to set rate table\n", __func__); return AH_FALSE; } if (!ahp->ah_rfHal->setPowerTable(ah, &minPower, &maxPower, chan, rfXpdGain)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unable to set power table\n", __func__); return AH_FALSE; } /* * Adjust XR power/rate up by 2 dB to account for greater peak * to avg ratio - except in newer avg power designs */ if (!IS_2413(ah) && !IS_5413(ah)) ahp->ah_ratesArray[15] += 4; /* * txPowerIndexOffset is set by the SetPowerTable() call - * adjust the rate table */ for (i = 0; i < N(ahp->ah_ratesArray); i++) { ahp->ah_ratesArray[i] += ahp->ah_txPowerIndexOffset; if (ahp->ah_ratesArray[i] > 63) ahp->ah_ratesArray[i] = 63; } if (ee->ee_eepMap < 2) { /* * Correct gain deltas for 5212 G operation - * Removed with revised chipset */ if (AH_PRIVATE(ah)->ah_phyRev < AR_PHY_CHIP_ID_REV_2 && IEEE80211_IS_CHAN_G(chan)) { uint16_t cckOfdmPwrDelta; if (freq == 2484) cckOfdmPwrDelta = SCALE_OC_DELTA( ee->ee_cckOfdmPwrDelta - ee->ee_scaledCh14FilterCckDelta); else cckOfdmPwrDelta = SCALE_OC_DELTA( ee->ee_cckOfdmPwrDelta); ar5212CorrectGainDelta(ah, cckOfdmPwrDelta); } /* * Finally, write the power values into the * baseband power table */ for (i = 0; i < (PWR_TABLE_SIZE/2); i++) { OS_REG_WRITE(ah, AR_PHY_PCDAC_TX_POWER(i), ((((ahp->ah_pcdacTable[2*i + 1] << 8) | 0xff) & 0xffff) << 16) | (((ahp->ah_pcdacTable[2*i] << 8) | 0xff) & 0xffff) ); } } /* Write the OFDM power per rate set */ OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE1, POW_OFDM(ahp->ah_ratesArray[3], 24) | POW_OFDM(ahp->ah_ratesArray[2], 16) | POW_OFDM(ahp->ah_ratesArray[1], 8) | POW_OFDM(ahp->ah_ratesArray[0], 0) ); OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE2, POW_OFDM(ahp->ah_ratesArray[7], 24) | POW_OFDM(ahp->ah_ratesArray[6], 16) | POW_OFDM(ahp->ah_ratesArray[5], 8) | POW_OFDM(ahp->ah_ratesArray[4], 0) ); /* Write the CCK power per rate set */ OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE3, POW_CCK(ahp->ah_ratesArray[10], 24) | POW_CCK(ahp->ah_ratesArray[9], 16) | POW_CCK(ahp->ah_ratesArray[15], 8) /* XR target power */ | POW_CCK(ahp->ah_ratesArray[8], 0) ); OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE4, POW_CCK(ahp->ah_ratesArray[14], 24) | POW_CCK(ahp->ah_ratesArray[13], 16) | POW_CCK(ahp->ah_ratesArray[12], 8) | POW_CCK(ahp->ah_ratesArray[11], 0) ); /* * Set max power to 30 dBm and, optionally, * enable TPC in tx descriptors. */ OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE_MAX, MAX_RATE_POWER | (ahp->ah_tpcEnabled ? AR_PHY_POWER_TX_RATE_MAX_TPC_ENABLE : 0)); return AH_TRUE; #undef N #undef POW_CCK #undef POW_OFDM } /* * Sets the transmit power in the baseband for the given * operating channel and mode. */ static HAL_BOOL ar5212SetRateTable(struct ath_hal *ah, const struct ieee80211_channel *chan, int16_t tpcScaleReduction, int16_t powerLimit, HAL_BOOL commit, int16_t *pMinPower, int16_t *pMaxPower) { struct ath_hal_5212 *ahp = AH5212(ah); uint16_t freq = ath_hal_gethwchannel(ah, chan); const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; uint16_t *rpow = ahp->ah_ratesArray; uint16_t twiceMaxEdgePower = MAX_RATE_POWER; uint16_t twiceMaxEdgePowerCck = MAX_RATE_POWER; uint16_t twiceMaxRDPower = MAX_RATE_POWER; int i; uint8_t cfgCtl; int8_t twiceAntennaGain, twiceAntennaReduction; const RD_EDGES_POWER *rep; TRGT_POWER_INFO targetPowerOfdm, targetPowerCck; int16_t scaledPower, maxAvailPower = 0; int16_t r13, r9, r7, r0; HALASSERT(ah->ah_magic == AR5212_MAGIC); twiceMaxRDPower = chan->ic_maxregpower * 2; *pMaxPower = -MAX_RATE_POWER; *pMinPower = MAX_RATE_POWER; /* Get conformance test limit maximum for this channel */ cfgCtl = ath_hal_getctl(ah, chan); for (i = 0; i < ee->ee_numCtls; i++) { uint16_t twiceMinEdgePower; if (ee->ee_ctl[i] == 0) continue; if (ee->ee_ctl[i] == cfgCtl || cfgCtl == ((ee->ee_ctl[i] & CTL_MODE_M) | SD_NO_CTL)) { rep = &ee->ee_rdEdgesPower[i * NUM_EDGES]; twiceMinEdgePower = ar5212GetMaxEdgePower(freq, rep); if ((cfgCtl & ~CTL_MODE_M) == SD_NO_CTL) { /* Find the minimum of all CTL edge powers that apply to this channel */ twiceMaxEdgePower = AH_MIN(twiceMaxEdgePower, twiceMinEdgePower); } else { twiceMaxEdgePower = twiceMinEdgePower; break; } } } if (IEEE80211_IS_CHAN_G(chan)) { /* Check for a CCK CTL for 11G CCK powers */ cfgCtl = (cfgCtl & ~CTL_MODE_M) | CTL_11B; for (i = 0; i < ee->ee_numCtls; i++) { uint16_t twiceMinEdgePowerCck; if (ee->ee_ctl[i] == 0) continue; if (ee->ee_ctl[i] == cfgCtl || cfgCtl == ((ee->ee_ctl[i] & CTL_MODE_M) | SD_NO_CTL)) { rep = &ee->ee_rdEdgesPower[i * NUM_EDGES]; twiceMinEdgePowerCck = ar5212GetMaxEdgePower(freq, rep); if ((cfgCtl & ~CTL_MODE_M) == SD_NO_CTL) { /* Find the minimum of all CTL edge powers that apply to this channel */ twiceMaxEdgePowerCck = AH_MIN(twiceMaxEdgePowerCck, twiceMinEdgePowerCck); } else { twiceMaxEdgePowerCck = twiceMinEdgePowerCck; break; } } } } else { /* Set the 11B cck edge power to the one found before */ twiceMaxEdgePowerCck = twiceMaxEdgePower; } /* Get Antenna Gain reduction */ if (IEEE80211_IS_CHAN_5GHZ(chan)) { ath_hal_eepromGet(ah, AR_EEP_ANTGAINMAX_5, &twiceAntennaGain); } else { ath_hal_eepromGet(ah, AR_EEP_ANTGAINMAX_2, &twiceAntennaGain); } twiceAntennaReduction = ath_hal_getantennareduction(ah, chan, twiceAntennaGain); if (IEEE80211_IS_CHAN_OFDM(chan)) { /* Get final OFDM target powers */ if (IEEE80211_IS_CHAN_2GHZ(chan)) { ar5212GetTargetPowers(ah, chan, ee->ee_trgtPwr_11g, ee->ee_numTargetPwr_11g, &targetPowerOfdm); } else { ar5212GetTargetPowers(ah, chan, ee->ee_trgtPwr_11a, ee->ee_numTargetPwr_11a, &targetPowerOfdm); } /* Get Maximum OFDM power */ /* Minimum of target and edge powers */ scaledPower = AH_MIN(twiceMaxEdgePower, twiceMaxRDPower - twiceAntennaReduction); /* * If turbo is set, reduce power to keep power * consumption under 2 Watts. Note that we always do * this unless specially configured. Then we limit * power only for non-AP operation. */ if (IEEE80211_IS_CHAN_TURBO(chan) #ifdef AH_ENABLE_AP_SUPPORT && AH_PRIVATE(ah)->ah_opmode != HAL_M_HOSTAP #endif ) { /* * If turbo is set, reduce power to keep power * consumption under 2 Watts */ if (ee->ee_version >= AR_EEPROM_VER3_1) scaledPower = AH_MIN(scaledPower, ee->ee_turbo2WMaxPower5); /* * EEPROM version 4.0 added an additional * constraint on 2.4GHz channels. */ if (ee->ee_version >= AR_EEPROM_VER4_0 && IEEE80211_IS_CHAN_2GHZ(chan)) scaledPower = AH_MIN(scaledPower, ee->ee_turbo2WMaxPower2); } maxAvailPower = AH_MIN(scaledPower, targetPowerOfdm.twicePwr6_24); /* Reduce power by max regulatory domain allowed restrictions */ scaledPower = maxAvailPower - (tpcScaleReduction * 2); scaledPower = (scaledPower < 0) ? 0 : scaledPower; scaledPower = AH_MIN(scaledPower, powerLimit); if (commit) { /* Set OFDM rates 9, 12, 18, 24 */ r0 = rpow[0] = rpow[1] = rpow[2] = rpow[3] = rpow[4] = scaledPower; /* Set OFDM rates 36, 48, 54, XR */ rpow[5] = AH_MIN(rpow[0], targetPowerOfdm.twicePwr36); rpow[6] = AH_MIN(rpow[0], targetPowerOfdm.twicePwr48); r7 = rpow[7] = AH_MIN(rpow[0], targetPowerOfdm.twicePwr54); if (ee->ee_version >= AR_EEPROM_VER4_0) { /* Setup XR target power from EEPROM */ rpow[15] = AH_MIN(scaledPower, IEEE80211_IS_CHAN_2GHZ(chan) ? ee->ee_xrTargetPower2 : ee->ee_xrTargetPower5); } else { /* XR uses 6mb power */ rpow[15] = rpow[0]; } ahp->ah_ofdmTxPower = *pMaxPower; } else { r0 = scaledPower; r7 = AH_MIN(r0, targetPowerOfdm.twicePwr54); } *pMinPower = r7; *pMaxPower = r0; HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: MaxRD: %d TurboMax: %d MaxCTL: %d " "TPC_Reduction %d chan=%d (0x%x) maxAvailPower=%d pwr6_24=%d, maxPower=%d\n", __func__, twiceMaxRDPower, ee->ee_turbo2WMaxPower5, twiceMaxEdgePower, tpcScaleReduction * 2, chan->ic_freq, chan->ic_flags, maxAvailPower, targetPowerOfdm.twicePwr6_24, *pMaxPower); } if (IEEE80211_IS_CHAN_CCK(chan)) { /* Get final CCK target powers */ ar5212GetTargetPowers(ah, chan, ee->ee_trgtPwr_11b, ee->ee_numTargetPwr_11b, &targetPowerCck); /* Reduce power by max regulatory domain allowed restrictions */ scaledPower = AH_MIN(twiceMaxEdgePowerCck, twiceMaxRDPower - twiceAntennaReduction); if (maxAvailPower < AH_MIN(scaledPower, targetPowerCck.twicePwr6_24)) maxAvailPower = AH_MIN(scaledPower, targetPowerCck.twicePwr6_24); /* Reduce power by user selection */ scaledPower = AH_MIN(scaledPower, targetPowerCck.twicePwr6_24) - (tpcScaleReduction * 2); scaledPower = (scaledPower < 0) ? 0 : scaledPower; scaledPower = AH_MIN(scaledPower, powerLimit); if (commit) { /* Set CCK rates 2L, 2S, 5.5L, 5.5S, 11L, 11S */ rpow[8] = AH_MIN(scaledPower, targetPowerCck.twicePwr6_24); r9 = rpow[9] = AH_MIN(scaledPower, targetPowerCck.twicePwr36); rpow[10] = rpow[9]; rpow[11] = AH_MIN(scaledPower, targetPowerCck.twicePwr48); rpow[12] = rpow[11]; r13 = rpow[13] = AH_MIN(scaledPower, targetPowerCck.twicePwr54); rpow[14] = rpow[13]; } else { r9 = AH_MIN(scaledPower, targetPowerCck.twicePwr36); r13 = AH_MIN(scaledPower, targetPowerCck.twicePwr54); } /* Set min/max power based off OFDM values or initialization */ if (r13 < *pMinPower) *pMinPower = r13; if (r9 > *pMaxPower) *pMaxPower = r9; HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: cck: MaxRD: %d MaxCTL: %d " "TPC_Reduction %d chan=%d (0x%x) maxAvailPower=%d pwr6_24=%d, maxPower=%d\n", __func__, twiceMaxRDPower, twiceMaxEdgePowerCck, tpcScaleReduction * 2, chan->ic_freq, chan->ic_flags, maxAvailPower, targetPowerCck.twicePwr6_24, *pMaxPower); } if (commit) { ahp->ah_tx6PowerInHalfDbm = *pMaxPower; AH_PRIVATE(ah)->ah_maxPowerLevel = ahp->ah_tx6PowerInHalfDbm; } return AH_TRUE; } HAL_BOOL ar5212GetChipPowerLimits(struct ath_hal *ah, struct ieee80211_channel *chan) { struct ath_hal_5212 *ahp = AH5212(ah); #if 0 static const uint16_t tpcScaleReductionTable[5] = { 0, 3, 6, 9, MAX_RATE_POWER }; int16_t tpcInDb, powerLimit; #endif int16_t minPower, maxPower; /* * Get Pier table max and min powers. */ if (ahp->ah_rfHal->getChannelMaxMinPower(ah, chan, &maxPower, &minPower)) { /* NB: rf code returns 1/4 dBm units, convert */ chan->ic_maxpower = maxPower / 2; chan->ic_minpower = minPower / 2; } else { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: no min/max power for %u/0x%x\n", __func__, chan->ic_freq, chan->ic_flags); chan->ic_maxpower = MAX_RATE_POWER; chan->ic_minpower = 0; } #if 0 /* * Now adjust to reflect any global scale and/or CTL's. * (XXX is that correct?) */ powerLimit = AH_MIN(MAX_RATE_POWER, AH_PRIVATE(ah)->ah_powerLimit); if (powerLimit >= MAX_RATE_POWER || powerLimit == 0) tpcInDb = tpcScaleReductionTable[AH_PRIVATE(ah)->ah_tpScale]; else tpcInDb = 0; if (!ar5212SetRateTable(ah, chan, tpcInDb, powerLimit, AH_FALSE, &minPower, &maxPower)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unable to find max/min power\n",__func__); return AH_FALSE; } if (maxPower < chan->ic_maxpower) chan->ic_maxpower = maxPower; if (minPower < chan->ic_minpower) chan->ic_minpower = minPower; HALDEBUG(ah, HAL_DEBUG_RESET, "Chan %d: MaxPow = %d MinPow = %d\n", chan->ic_freq, chan->ic_maxpower, chans->ic_minpower); #endif return AH_TRUE; } /* * Correct for the gain-delta between ofdm and cck mode target * powers. Write the results to the rate table and the power table. * * Conventions : * 1. rpow[ii] is the integer value of 2*(desired power * for the rate ii in dBm) to provide 0.5dB resolution. rate * mapping is as following : * [0..7] --> ofdm 6, 9, .. 48, 54 * [8..14] --> cck 1L, 2L, 2S, .. 11L, 11S * [15] --> XR (all rates get the same power) * 2. powv[ii] is the pcdac corresponding to ii/2 dBm. */ static void ar5212CorrectGainDelta(struct ath_hal *ah, int twiceOfdmCckDelta) { #define N(_a) (sizeof(_a) / sizeof(_a[0])) struct ath_hal_5212 *ahp = AH5212(ah); const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; int16_t ratesIndex[N(ahp->ah_ratesArray)]; uint16_t ii, jj, iter; int32_t cckIndex; int16_t gainDeltaAdjust; HALASSERT(ah->ah_magic == AR5212_MAGIC); gainDeltaAdjust = ee->ee_cckOfdmGainDelta; /* make a local copy of desired powers as initial indices */ OS_MEMCPY(ratesIndex, ahp->ah_ratesArray, sizeof(ratesIndex)); /* fix only the CCK indices */ for (ii = 8; ii < 15; ii++) { /* apply a gain_delta correction of -15 for CCK */ ratesIndex[ii] -= gainDeltaAdjust; /* Now check for contention with all ofdm target powers */ jj = 0; iter = 0; /* indicates not all ofdm rates checked forcontention yet */ while (jj < 16) { if (ratesIndex[ii] < 0) ratesIndex[ii] = 0; if (jj == 8) { /* skip CCK rates */ jj = 15; continue; } if (ratesIndex[ii] == ahp->ah_ratesArray[jj]) { if (ahp->ah_ratesArray[jj] == 0) ratesIndex[ii]++; else if (iter > 50) { /* * To avoid pathological case of of * dm target powers 0 and 0.5dBm */ ratesIndex[ii]++; } else ratesIndex[ii]--; /* check with all rates again */ jj = 0; iter++; } else jj++; } if (ratesIndex[ii] >= PWR_TABLE_SIZE) ratesIndex[ii] = PWR_TABLE_SIZE -1; cckIndex = ahp->ah_ratesArray[ii] - twiceOfdmCckDelta; if (cckIndex < 0) cckIndex = 0; /* * Validate that the indexes for the powv are not * out of bounds. */ HALASSERT(cckIndex < PWR_TABLE_SIZE); HALASSERT(ratesIndex[ii] < PWR_TABLE_SIZE); ahp->ah_pcdacTable[ratesIndex[ii]] = ahp->ah_pcdacTable[cckIndex]; } /* Override rate per power table with new values */ for (ii = 8; ii < 15; ii++) ahp->ah_ratesArray[ii] = ratesIndex[ii]; #undef N } /* * Find the maximum conformance test limit for the given channel and CTL info */ static uint16_t ar5212GetMaxEdgePower(uint16_t channel, const RD_EDGES_POWER *pRdEdgesPower) { /* temp array for holding edge channels */ uint16_t tempChannelList[NUM_EDGES]; uint16_t clo, chi, twiceMaxEdgePower; int i, numEdges; /* Get the edge power */ for (i = 0; i < NUM_EDGES; i++) { if (pRdEdgesPower[i].rdEdge == 0) break; tempChannelList[i] = pRdEdgesPower[i].rdEdge; } numEdges = i; ar5212GetLowerUpperValues(channel, tempChannelList, numEdges, &clo, &chi); /* Get the index for the lower channel */ for (i = 0; i < numEdges && clo != tempChannelList[i]; i++) ; /* Is lower channel ever outside the rdEdge? */ HALASSERT(i != numEdges); if ((clo == chi && clo == channel) || (pRdEdgesPower[i].flag)) { /* * If there's an exact channel match or an inband flag set * on the lower channel use the given rdEdgePower */ twiceMaxEdgePower = pRdEdgesPower[i].twice_rdEdgePower; HALASSERT(twiceMaxEdgePower > 0); } else twiceMaxEdgePower = MAX_RATE_POWER; return twiceMaxEdgePower; } /* * Returns interpolated or the scaled up interpolated value */ static uint16_t interpolate(uint16_t target, uint16_t srcLeft, uint16_t srcRight, uint16_t targetLeft, uint16_t targetRight) { uint16_t rv; int16_t lRatio; /* to get an accurate ratio, always scale, if want to scale, then don't scale back down */ if ((targetLeft * targetRight) == 0) return 0; if (srcRight != srcLeft) { /* * Note the ratio always need to be scaled, * since it will be a fraction. */ lRatio = (target - srcLeft) * EEP_SCALE / (srcRight - srcLeft); if (lRatio < 0) { /* Return as Left target if value would be negative */ rv = targetLeft; } else if (lRatio > EEP_SCALE) { /* Return as Right target if Ratio is greater than 100% (SCALE) */ rv = targetRight; } else { rv = (lRatio * targetRight + (EEP_SCALE - lRatio) * targetLeft) / EEP_SCALE; } } else { rv = targetLeft; } return rv; } /* * Return the four rates of target power for the given target power table * channel, and number of channels */ static void ar5212GetTargetPowers(struct ath_hal *ah, const struct ieee80211_channel *chan, const TRGT_POWER_INFO *powInfo, uint16_t numChannels, TRGT_POWER_INFO *pNewPower) { uint16_t freq = ath_hal_gethwchannel(ah, chan); /* temp array for holding target power channels */ uint16_t tempChannelList[NUM_TEST_FREQUENCIES]; uint16_t clo, chi, ixlo, ixhi; int i; /* Copy the target powers into the temp channel list */ for (i = 0; i < numChannels; i++) tempChannelList[i] = powInfo[i].testChannel; ar5212GetLowerUpperValues(freq, tempChannelList, numChannels, &clo, &chi); /* Get the indices for the channel */ ixlo = ixhi = 0; for (i = 0; i < numChannels; i++) { if (clo == tempChannelList[i]) { ixlo = i; } if (chi == tempChannelList[i]) { ixhi = i; break; } } /* * Get the lower and upper channels, target powers, * and interpolate between them. */ pNewPower->twicePwr6_24 = interpolate(freq, clo, chi, powInfo[ixlo].twicePwr6_24, powInfo[ixhi].twicePwr6_24); pNewPower->twicePwr36 = interpolate(freq, clo, chi, powInfo[ixlo].twicePwr36, powInfo[ixhi].twicePwr36); pNewPower->twicePwr48 = interpolate(freq, clo, chi, powInfo[ixlo].twicePwr48, powInfo[ixhi].twicePwr48); pNewPower->twicePwr54 = interpolate(freq, clo, chi, powInfo[ixlo].twicePwr54, powInfo[ixhi].twicePwr54); } static uint32_t udiff(uint32_t u, uint32_t v) { return (u >= v ? u - v : v - u); } /* * Search a list for a specified value v that is within * EEP_DELTA of the search values. Return the closest * values in the list above and below the desired value. * EEP_DELTA is a factional value; everything is scaled * so only integer arithmetic is used. * * NB: the input list is assumed to be sorted in ascending order */ void ar5212GetLowerUpperValues(uint16_t v, uint16_t *lp, uint16_t listSize, uint16_t *vlo, uint16_t *vhi) { uint32_t target = v * EEP_SCALE; uint16_t *ep = lp+listSize; /* * Check first and last elements for out-of-bounds conditions. */ if (target < (uint32_t)(lp[0] * EEP_SCALE - EEP_DELTA)) { *vlo = *vhi = lp[0]; return; } if (target > (uint32_t)(ep[-1] * EEP_SCALE + EEP_DELTA)) { *vlo = *vhi = ep[-1]; return; } /* look for value being near or between 2 values in list */ for (; lp < ep; lp++) { /* * If value is close to the current value of the list * then target is not between values, it is one of the values */ if (udiff(lp[0] * EEP_SCALE, target) < EEP_DELTA) { *vlo = *vhi = lp[0]; return; } /* * Look for value being between current value and next value * if so return these 2 values */ if (target < (uint32_t)(lp[1] * EEP_SCALE - EEP_DELTA)) { *vlo = lp[0]; *vhi = lp[1]; return; } } HALASSERT(AH_FALSE); /* should not reach here */ } /* * Perform analog "swizzling" of parameters into their location * * NB: used by RF backends */ void ar5212ModifyRfBuffer(uint32_t *rfBuf, uint32_t reg32, uint32_t numBits, uint32_t firstBit, uint32_t column) { #define MAX_ANALOG_START 319 /* XXX */ uint32_t tmp32, mask, arrayEntry, lastBit; int32_t bitPosition, bitsLeft; HALASSERT(column <= 3); HALASSERT(numBits <= 32); HALASSERT(firstBit + numBits <= MAX_ANALOG_START); tmp32 = ath_hal_reverseBits(reg32, numBits); arrayEntry = (firstBit - 1) / 8; bitPosition = (firstBit - 1) % 8; bitsLeft = numBits; while (bitsLeft > 0) { lastBit = (bitPosition + bitsLeft > 8) ? 8 : bitPosition + bitsLeft; mask = (((1 << lastBit) - 1) ^ ((1 << bitPosition) - 1)) << (column * 8); rfBuf[arrayEntry] &= ~mask; rfBuf[arrayEntry] |= ((tmp32 << bitPosition) << (column * 8)) & mask; bitsLeft -= 8 - bitPosition; tmp32 = tmp32 >> (8 - bitPosition); bitPosition = 0; arrayEntry++; } #undef MAX_ANALOG_START } /* * Sets the rate to duration values in MAC - used for multi- * rate retry. * The rate duration table needs to cover all valid rate codes; * the 11g table covers all ofdm rates, while the 11b table * covers all cck rates => all valid rates get covered between * these two mode's ratetables! * But if we're turbo, the ofdm phy is replaced by the turbo phy * and cck is not valid with turbo => all rates get covered * by the turbo ratetable only */ void ar5212SetRateDurationTable(struct ath_hal *ah, const struct ieee80211_channel *chan) { const HAL_RATE_TABLE *rt; int i; /* NB: band doesn't matter for 1/2 and 1/4 rate */ if (IEEE80211_IS_CHAN_HALF(chan)) { rt = ar5212GetRateTable(ah, HAL_MODE_11A_HALF_RATE); } else if (IEEE80211_IS_CHAN_QUARTER(chan)) { rt = ar5212GetRateTable(ah, HAL_MODE_11A_QUARTER_RATE); } else { rt = ar5212GetRateTable(ah, IEEE80211_IS_CHAN_TURBO(chan) ? HAL_MODE_TURBO : HAL_MODE_11G); } for (i = 0; i < rt->rateCount; ++i) OS_REG_WRITE(ah, AR_RATE_DURATION(rt->info[i].rateCode), ath_hal_computetxtime(ah, rt, WLAN_CTRL_FRAME_SIZE, rt->info[i].controlRate, AH_FALSE)); if (!IEEE80211_IS_CHAN_TURBO(chan)) { /* 11g Table is used to cover the CCK rates. */ rt = ar5212GetRateTable(ah, HAL_MODE_11G); for (i = 0; i < rt->rateCount; ++i) { uint32_t reg = AR_RATE_DURATION(rt->info[i].rateCode); if (rt->info[i].phy != IEEE80211_T_CCK) continue; OS_REG_WRITE(ah, reg, ath_hal_computetxtime(ah, rt, WLAN_CTRL_FRAME_SIZE, rt->info[i].controlRate, AH_FALSE)); /* cck rates have short preamble option also */ if (rt->info[i].shortPreamble) { reg += rt->info[i].shortPreamble << 2; OS_REG_WRITE(ah, reg, ath_hal_computetxtime(ah, rt, WLAN_CTRL_FRAME_SIZE, rt->info[i].controlRate, AH_TRUE)); } } } } /* Adjust various register settings based on half/quarter rate clock setting. * This includes: +USEC, TX/RX latency, * + IFS params: slot, eifs, misc etc. */ void ar5212SetIFSTiming(struct ath_hal *ah, const struct ieee80211_channel *chan) { uint32_t txLat, rxLat, usec, slot, refClock, eifs, init_usec; HALASSERT(IEEE80211_IS_CHAN_HALF(chan) || IEEE80211_IS_CHAN_QUARTER(chan)); refClock = OS_REG_READ(ah, AR_USEC) & AR_USEC_USEC32; if (IEEE80211_IS_CHAN_HALF(chan)) { slot = IFS_SLOT_HALF_RATE; rxLat = RX_NON_FULL_RATE_LATENCY << AR5212_USEC_RX_LAT_S; txLat = TX_HALF_RATE_LATENCY << AR5212_USEC_TX_LAT_S; usec = HALF_RATE_USEC; eifs = IFS_EIFS_HALF_RATE; init_usec = INIT_USEC >> 1; } else { /* quarter rate */ slot = IFS_SLOT_QUARTER_RATE; rxLat = RX_NON_FULL_RATE_LATENCY << AR5212_USEC_RX_LAT_S; txLat = TX_QUARTER_RATE_LATENCY << AR5212_USEC_TX_LAT_S; usec = QUARTER_RATE_USEC; eifs = IFS_EIFS_QUARTER_RATE; init_usec = INIT_USEC >> 2; } OS_REG_WRITE(ah, AR_USEC, (usec | refClock | txLat | rxLat)); OS_REG_WRITE(ah, AR_D_GBL_IFS_SLOT, slot); OS_REG_WRITE(ah, AR_D_GBL_IFS_EIFS, eifs); OS_REG_RMW_FIELD(ah, AR_D_GBL_IFS_MISC, AR_D_GBL_IFS_MISC_USEC_DURATION, init_usec); } Index: head/sys/dev/ath/ath_hal/ar5212/ar5413.c =================================================================== --- head/sys/dev/ath/ath_hal/ar5212/ar5413.c (revision 298938) +++ head/sys/dev/ath/ath_hal/ar5212/ar5413.c (revision 298939) @@ -1,802 +1,802 @@ /* * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * Copyright (c) 2002-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ah_eeprom_v3.h" #include "ar5212/ar5212.h" #include "ar5212/ar5212reg.h" #include "ar5212/ar5212phy.h" #define AH_5212_5413 #include "ar5212/ar5212.ini" #define N(a) (sizeof(a)/sizeof(a[0])) struct ar5413State { RF_HAL_FUNCS base; /* public state, must be first */ uint16_t pcdacTable[PWR_TABLE_SIZE_2413]; uint32_t Bank1Data[N(ar5212Bank1_5413)]; uint32_t Bank2Data[N(ar5212Bank2_5413)]; uint32_t Bank3Data[N(ar5212Bank3_5413)]; uint32_t Bank6Data[N(ar5212Bank6_5413)]; uint32_t Bank7Data[N(ar5212Bank7_5413)]; /* * Private state for reduced stack usage. */ /* filled out Vpd table for all pdGains (chanL) */ uint16_t vpdTable_L[MAX_NUM_PDGAINS_PER_CHANNEL] [MAX_PWR_RANGE_IN_HALF_DB]; /* filled out Vpd table for all pdGains (chanR) */ uint16_t vpdTable_R[MAX_NUM_PDGAINS_PER_CHANNEL] [MAX_PWR_RANGE_IN_HALF_DB]; /* filled out Vpd table for all pdGains (interpolated) */ uint16_t vpdTable_I[MAX_NUM_PDGAINS_PER_CHANNEL] [MAX_PWR_RANGE_IN_HALF_DB]; }; #define AR5413(ah) ((struct ar5413State *) AH5212(ah)->ah_rfHal) extern void ar5212ModifyRfBuffer(uint32_t *rfBuf, uint32_t reg32, uint32_t numBits, uint32_t firstBit, uint32_t column); static void ar5413WriteRegs(struct ath_hal *ah, u_int modesIndex, u_int freqIndex, int writes) { HAL_INI_WRITE_ARRAY(ah, ar5212Modes_5413, modesIndex, writes); HAL_INI_WRITE_ARRAY(ah, ar5212Common_5413, 1, writes); HAL_INI_WRITE_ARRAY(ah, ar5212BB_RfGain_5413, freqIndex, writes); } /* * Take the MHz channel value and set the Channel value * * ASSUMES: Writes enabled to analog bus */ static HAL_BOOL ar5413SetChannel(struct ath_hal *ah, const struct ieee80211_channel *chan) { uint16_t freq = ath_hal_gethwchannel(ah, chan); uint32_t channelSel = 0; uint32_t bModeSynth = 0; uint32_t aModeRefSel = 0; uint32_t reg32 = 0; OS_MARK(ah, AH_MARK_SETCHANNEL, freq); if (freq < 4800) { uint32_t txctl; if (((freq - 2192) % 5) == 0) { channelSel = ((freq - 672) * 2 - 3040)/10; bModeSynth = 0; } else if (((freq - 2224) % 5) == 0) { channelSel = ((freq - 704) * 2 - 3040) / 10; bModeSynth = 1; } else { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel %u MHz\n", __func__, freq); return AH_FALSE; } channelSel = (channelSel << 2) & 0xff; channelSel = ath_hal_reverseBits(channelSel, 8); txctl = OS_REG_READ(ah, AR_PHY_CCK_TX_CTRL); if (freq == 2484) { /* Enable channel spreading for channel 14 */ OS_REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, txctl | AR_PHY_CCK_TX_CTRL_JAPAN); } else { OS_REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, txctl &~ AR_PHY_CCK_TX_CTRL_JAPAN); } } else if (((freq % 5) == 2) && (freq <= 5435)) { freq = freq - 2; /* Align to even 5MHz raster */ channelSel = ath_hal_reverseBits( (uint32_t)(((freq - 4800)*10)/25 + 1), 8); aModeRefSel = ath_hal_reverseBits(0, 2); } else if ((freq % 20) == 0 && freq >= 5120) { channelSel = ath_hal_reverseBits( ((freq - 4800) / 20 << 2), 8); aModeRefSel = ath_hal_reverseBits(1, 2); } else if ((freq % 10) == 0) { channelSel = ath_hal_reverseBits( ((freq - 4800) / 10 << 1), 8); aModeRefSel = ath_hal_reverseBits(1, 2); } else if ((freq % 5) == 0) { channelSel = ath_hal_reverseBits( (freq - 4800) / 5, 8); aModeRefSel = ath_hal_reverseBits(1, 2); } else { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel %u MHz\n", __func__, freq); return AH_FALSE; } reg32 = (channelSel << 4) | (aModeRefSel << 2) | (bModeSynth << 1) | (1 << 12) | 0x1; OS_REG_WRITE(ah, AR_PHY(0x27), reg32 & 0xff); reg32 >>= 8; OS_REG_WRITE(ah, AR_PHY(0x36), reg32 & 0x7f); AH_PRIVATE(ah)->ah_curchan = chan; return AH_TRUE; } /* * Reads EEPROM header info from device structure and programs * all rf registers * * REQUIRES: Access to the analog rf device */ static HAL_BOOL ar5413SetRfRegs(struct ath_hal *ah, const struct ieee80211_channel *chan, uint16_t modesIndex, uint16_t *rfXpdGain) { #define RF_BANK_SETUP(_priv, _ix, _col) do { \ int i; \ for (i = 0; i < N(ar5212Bank##_ix##_5413); i++) \ (_priv)->Bank##_ix##Data[i] = ar5212Bank##_ix##_5413[i][_col];\ } while (0) struct ath_hal_5212 *ahp = AH5212(ah); uint16_t freq = ath_hal_gethwchannel(ah, chan); const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; uint16_t ob5GHz = 0, db5GHz = 0; uint16_t ob2GHz = 0, db2GHz = 0; struct ar5413State *priv = AR5413(ah); int regWrites = 0; HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: chan %u/0x%x modesIndex %u\n", __func__, chan->ic_freq, chan->ic_flags, modesIndex); HALASSERT(priv != AH_NULL); /* Setup rf parameters */ switch (chan->ic_flags & IEEE80211_CHAN_ALLFULL) { case IEEE80211_CHAN_A: if (freq > 4000 && freq < 5260) { ob5GHz = ee->ee_ob1; db5GHz = ee->ee_db1; } else if (freq >= 5260 && freq < 5500) { ob5GHz = ee->ee_ob2; db5GHz = ee->ee_db2; } else if (freq >= 5500 && freq < 5725) { ob5GHz = ee->ee_ob3; db5GHz = ee->ee_db3; } else if (freq >= 5725) { ob5GHz = ee->ee_ob4; db5GHz = ee->ee_db4; } else { /* XXX else */ } break; case IEEE80211_CHAN_B: ob2GHz = ee->ee_obFor24; db2GHz = ee->ee_dbFor24; break; case IEEE80211_CHAN_G: case IEEE80211_CHAN_PUREG: /* NB: really 108G */ ob2GHz = ee->ee_obFor24g; db2GHz = ee->ee_dbFor24g; break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel flags 0x%x\n", __func__, chan->ic_flags); return AH_FALSE; } /* Bank 1 Write */ RF_BANK_SETUP(priv, 1, 1); /* Bank 2 Write */ RF_BANK_SETUP(priv, 2, modesIndex); /* Bank 3 Write */ RF_BANK_SETUP(priv, 3, modesIndex); /* Bank 6 Write */ RF_BANK_SETUP(priv, 6, modesIndex); /* Only the 5 or 2 GHz OB/DB need to be set for a mode */ if (IEEE80211_IS_CHAN_2GHZ(chan)) { ar5212ModifyRfBuffer(priv->Bank6Data, ob2GHz, 3, 241, 0); ar5212ModifyRfBuffer(priv->Bank6Data, db2GHz, 3, 238, 0); /* TODO - only for Eagle 1.0 2GHz - remove for production */ /* XXX: but without this bit G doesn't work. */ ar5212ModifyRfBuffer(priv->Bank6Data, 1 , 1, 291, 2); /* Optimum value for rf_pwd_iclobuf2G for PCIe chips only */ if (AH_PRIVATE(ah)->ah_ispcie) { ar5212ModifyRfBuffer(priv->Bank6Data, ath_hal_reverseBits(6, 3), 3, 131, 3); } } else { ar5212ModifyRfBuffer(priv->Bank6Data, ob5GHz, 3, 247, 0); ar5212ModifyRfBuffer(priv->Bank6Data, db5GHz, 3, 244, 0); } /* Bank 7 Setup */ RF_BANK_SETUP(priv, 7, modesIndex); /* Write Analog registers */ HAL_INI_WRITE_BANK(ah, ar5212Bank1_5413, priv->Bank1Data, regWrites); HAL_INI_WRITE_BANK(ah, ar5212Bank2_5413, priv->Bank2Data, regWrites); HAL_INI_WRITE_BANK(ah, ar5212Bank3_5413, priv->Bank3Data, regWrites); HAL_INI_WRITE_BANK(ah, ar5212Bank6_5413, priv->Bank6Data, regWrites); HAL_INI_WRITE_BANK(ah, ar5212Bank7_5413, priv->Bank7Data, regWrites); /* Now that we have reprogrammed rfgain value, clear the flag. */ ahp->ah_rfgainState = HAL_RFGAIN_INACTIVE; return AH_TRUE; #undef RF_BANK_SETUP } /* * Return a reference to the requested RF Bank. */ static uint32_t * ar5413GetRfBank(struct ath_hal *ah, int bank) { struct ar5413State *priv = AR5413(ah); HALASSERT(priv != AH_NULL); switch (bank) { case 1: return priv->Bank1Data; case 2: return priv->Bank2Data; case 3: return priv->Bank3Data; case 6: return priv->Bank6Data; case 7: return priv->Bank7Data; } HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unknown RF Bank %d requested\n", __func__, bank); return AH_NULL; } /* * Return indices surrounding the value in sorted integer lists. * * NB: the input list is assumed to be sorted in ascending order */ static void GetLowerUpperIndex(int16_t v, const uint16_t *lp, uint16_t listSize, uint32_t *vlo, uint32_t *vhi) { int16_t target = v; const uint16_t *ep = lp+listSize; const uint16_t *tp; /* * Check first and last elements for out-of-bounds conditions. */ if (target < lp[0]) { *vlo = *vhi = 0; return; } if (target >= ep[-1]) { *vlo = *vhi = listSize - 1; return; } /* look for value being near or between 2 values in list */ for (tp = lp; tp < ep; tp++) { /* * If value is close to the current value of the list * then target is not between values, it is one of the values */ if (*tp == target) { *vlo = *vhi = tp - (const uint16_t *) lp; return; } /* * Look for value being between current value and next value * if so return these 2 values */ if (target < tp[1]) { *vlo = tp - (const uint16_t *) lp; *vhi = *vlo + 1; return; } } } /* * Fill the Vpdlist for indices Pmax-Pmin */ static HAL_BOOL ar5413FillVpdTable(uint32_t pdGainIdx, int16_t Pmin, int16_t Pmax, const int16_t *pwrList, const uint16_t *VpdList, uint16_t numIntercepts, uint16_t retVpdList[][64]) { uint16_t ii, jj, kk; int16_t currPwr = (int16_t)(2*Pmin); /* since Pmin is pwr*2 and pwrList is 4*pwr */ uint32_t idxL, idxR; ii = 0; jj = 0; if (numIntercepts < 2) return AH_FALSE; while (ii <= (uint16_t)(Pmax - Pmin)) { GetLowerUpperIndex(currPwr, (const uint16_t *) pwrList, numIntercepts, &(idxL), &(idxR)); if (idxR < 1) idxR = 1; /* extrapolate below */ if (idxL == (uint32_t)(numIntercepts - 1)) idxL = numIntercepts - 2; /* extrapolate above */ if (pwrList[idxL] == pwrList[idxR]) kk = VpdList[idxL]; else kk = (uint16_t) (((currPwr - pwrList[idxL])*VpdList[idxR]+ (pwrList[idxR] - currPwr)*VpdList[idxL])/ (pwrList[idxR] - pwrList[idxL])); retVpdList[pdGainIdx][ii] = kk; ii++; currPwr += 2; /* half dB steps */ } return AH_TRUE; } /* * Returns interpolated or the scaled up interpolated value */ static int16_t interpolate_signed(uint16_t target, uint16_t srcLeft, uint16_t srcRight, int16_t targetLeft, int16_t targetRight) { int16_t rv; if (srcRight != srcLeft) { rv = ((target - srcLeft)*targetRight + (srcRight - target)*targetLeft) / (srcRight - srcLeft); } else { rv = targetLeft; } return rv; } /* * Uses the data points read from EEPROM to reconstruct the pdadc power table * Called by ar5413SetPowerTable() */ static int ar5413getGainBoundariesAndPdadcsForPowers(struct ath_hal *ah, uint16_t channel, const RAW_DATA_STRUCT_2413 *pRawDataset, uint16_t pdGainOverlap_t2, int16_t *pMinCalPower, uint16_t pPdGainBoundaries[], uint16_t pPdGainValues[], uint16_t pPDADCValues[]) { struct ar5413State *priv = AR5413(ah); #define VpdTable_L priv->vpdTable_L #define VpdTable_R priv->vpdTable_R #define VpdTable_I priv->vpdTable_I uint32_t ii, jj, kk; int32_t ss;/* potentially -ve index for taking care of pdGainOverlap */ uint32_t idxL, idxR; uint32_t numPdGainsUsed = 0; /* * If desired to support -ve power levels in future, just * change pwr_I_0 to signed 5-bits. */ int16_t Pmin_t2[MAX_NUM_PDGAINS_PER_CHANNEL]; - /* to accomodate -ve power levels later on. */ + /* to accommodate -ve power levels later on. */ int16_t Pmax_t2[MAX_NUM_PDGAINS_PER_CHANNEL]; - /* to accomodate -ve power levels later on */ + /* to accommodate -ve power levels later on */ uint16_t numVpd = 0; uint16_t Vpd_step; int16_t tmpVal ; uint32_t sizeCurrVpdTable, maxIndex, tgtIndex; /* Get upper lower index */ GetLowerUpperIndex(channel, pRawDataset->pChannels, pRawDataset->numChannels, &(idxL), &(idxR)); for (ii = 0; ii < MAX_NUM_PDGAINS_PER_CHANNEL; ii++) { jj = MAX_NUM_PDGAINS_PER_CHANNEL - ii - 1; /* work backwards 'cause highest pdGain for lowest power */ numVpd = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].numVpd; if (numVpd > 0) { pPdGainValues[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pd_gain; Pmin_t2[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pwr_t4[0]; if (Pmin_t2[numPdGainsUsed] >pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[0]) { Pmin_t2[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[0]; } Pmin_t2[numPdGainsUsed] = (int16_t) (Pmin_t2[numPdGainsUsed] / 2); Pmax_t2[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pwr_t4[numVpd-1]; if (Pmax_t2[numPdGainsUsed] > pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[numVpd-1]) Pmax_t2[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[numVpd-1]; Pmax_t2[numPdGainsUsed] = (int16_t)(Pmax_t2[numPdGainsUsed] / 2); ar5413FillVpdTable( numPdGainsUsed, Pmin_t2[numPdGainsUsed], Pmax_t2[numPdGainsUsed], &(pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pwr_t4[0]), &(pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].Vpd[0]), numVpd, VpdTable_L ); ar5413FillVpdTable( numPdGainsUsed, Pmin_t2[numPdGainsUsed], Pmax_t2[numPdGainsUsed], &(pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[0]), &(pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].Vpd[0]), numVpd, VpdTable_R ); for (kk = 0; kk < (uint16_t)(Pmax_t2[numPdGainsUsed] - Pmin_t2[numPdGainsUsed]); kk++) { VpdTable_I[numPdGainsUsed][kk] = interpolate_signed( channel, pRawDataset->pChannels[idxL], pRawDataset->pChannels[idxR], (int16_t)VpdTable_L[numPdGainsUsed][kk], (int16_t)VpdTable_R[numPdGainsUsed][kk]); } /* fill VpdTable_I for this pdGain */ numPdGainsUsed++; } /* if this pdGain is used */ } *pMinCalPower = Pmin_t2[0]; kk = 0; /* index for the final table */ for (ii = 0; ii < numPdGainsUsed; ii++) { if (ii == (numPdGainsUsed - 1)) pPdGainBoundaries[ii] = Pmax_t2[ii] + PD_GAIN_BOUNDARY_STRETCH_IN_HALF_DB; else pPdGainBoundaries[ii] = (uint16_t) ((Pmax_t2[ii] + Pmin_t2[ii+1]) / 2 ); if (pPdGainBoundaries[ii] > 63) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: clamp pPdGainBoundaries[%d] %d\n", __func__, ii, pPdGainBoundaries[ii]);/*XXX*/ pPdGainBoundaries[ii] = 63; } /* Find starting index for this pdGain */ if (ii == 0) ss = 0; /* for the first pdGain, start from index 0 */ else ss = (pPdGainBoundaries[ii-1] - Pmin_t2[ii]) - pdGainOverlap_t2; Vpd_step = (uint16_t)(VpdTable_I[ii][1] - VpdTable_I[ii][0]); Vpd_step = (uint16_t)((Vpd_step < 1) ? 1 : Vpd_step); /* *-ve ss indicates need to extrapolate data below for this pdGain */ while (ss < 0) { tmpVal = (int16_t)(VpdTable_I[ii][0] + ss*Vpd_step); pPDADCValues[kk++] = (uint16_t)((tmpVal < 0) ? 0 : tmpVal); ss++; } sizeCurrVpdTable = Pmax_t2[ii] - Pmin_t2[ii]; tgtIndex = pPdGainBoundaries[ii] + pdGainOverlap_t2 - Pmin_t2[ii]; maxIndex = (tgtIndex < sizeCurrVpdTable) ? tgtIndex : sizeCurrVpdTable; while (ss < (int16_t)maxIndex) pPDADCValues[kk++] = VpdTable_I[ii][ss++]; Vpd_step = (uint16_t)(VpdTable_I[ii][sizeCurrVpdTable-1] - VpdTable_I[ii][sizeCurrVpdTable-2]); Vpd_step = (uint16_t)((Vpd_step < 1) ? 1 : Vpd_step); /* * for last gain, pdGainBoundary == Pmax_t2, so will * have to extrapolate */ if (tgtIndex > maxIndex) { /* need to extrapolate above */ while(ss < (int16_t)tgtIndex) { tmpVal = (uint16_t) (VpdTable_I[ii][sizeCurrVpdTable-1] + (ss-maxIndex)*Vpd_step); pPDADCValues[kk++] = (tmpVal > 127) ? 127 : tmpVal; ss++; } } /* extrapolated above */ } /* for all pdGainUsed */ while (ii < MAX_NUM_PDGAINS_PER_CHANNEL) { pPdGainBoundaries[ii] = pPdGainBoundaries[ii-1]; ii++; } while (kk < 128) { pPDADCValues[kk] = pPDADCValues[kk-1]; kk++; } return numPdGainsUsed; #undef VpdTable_L #undef VpdTable_R #undef VpdTable_I } static HAL_BOOL ar5413SetPowerTable(struct ath_hal *ah, int16_t *minPower, int16_t *maxPower, const struct ieee80211_channel *chan, uint16_t *rfXpdGain) { struct ath_hal_5212 *ahp = AH5212(ah); uint16_t freq = ath_hal_gethwchannel(ah, chan); const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; const RAW_DATA_STRUCT_2413 *pRawDataset = AH_NULL; uint16_t pdGainOverlap_t2; int16_t minCalPower5413_t2; uint16_t *pdadcValues = ahp->ah_pcdacTable; uint16_t gainBoundaries[4]; uint32_t reg32, regoffset; int i, numPdGainsUsed; #ifndef AH_USE_INIPDGAIN uint32_t tpcrg1; #endif HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: chan 0x%x flag 0x%x\n", __func__, chan->ic_freq, chan->ic_flags); if (IEEE80211_IS_CHAN_G(chan) || IEEE80211_IS_CHAN_108G(chan)) pRawDataset = &ee->ee_rawDataset2413[headerInfo11G]; else if (IEEE80211_IS_CHAN_B(chan)) pRawDataset = &ee->ee_rawDataset2413[headerInfo11B]; else { HALASSERT(IEEE80211_IS_CHAN_5GHZ(chan)); pRawDataset = &ee->ee_rawDataset2413[headerInfo11A]; } pdGainOverlap_t2 = (uint16_t) SM(OS_REG_READ(ah, AR_PHY_TPCRG5), AR_PHY_TPCRG5_PD_GAIN_OVERLAP); numPdGainsUsed = ar5413getGainBoundariesAndPdadcsForPowers(ah, freq, pRawDataset, pdGainOverlap_t2, &minCalPower5413_t2,gainBoundaries, rfXpdGain, pdadcValues); HALASSERT(1 <= numPdGainsUsed && numPdGainsUsed <= 3); #ifdef AH_USE_INIPDGAIN /* * Use pd_gains curve from eeprom; Atheros always uses * the default curve from the ini file but some vendors * (e.g. Zcomax) want to override this curve and not * honoring their settings results in tx power 5dBm low. */ OS_REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_NUM_PD_GAIN, (pRawDataset->pDataPerChannel[0].numPdGains - 1)); #else tpcrg1 = OS_REG_READ(ah, AR_PHY_TPCRG1); tpcrg1 = (tpcrg1 &~ AR_PHY_TPCRG1_NUM_PD_GAIN) | SM(numPdGainsUsed-1, AR_PHY_TPCRG1_NUM_PD_GAIN); switch (numPdGainsUsed) { case 3: tpcrg1 &= ~AR_PHY_TPCRG1_PDGAIN_SETTING3; tpcrg1 |= SM(rfXpdGain[2], AR_PHY_TPCRG1_PDGAIN_SETTING3); /* fall thru... */ case 2: tpcrg1 &= ~AR_PHY_TPCRG1_PDGAIN_SETTING2; tpcrg1 |= SM(rfXpdGain[1], AR_PHY_TPCRG1_PDGAIN_SETTING2); /* fall thru... */ case 1: tpcrg1 &= ~AR_PHY_TPCRG1_PDGAIN_SETTING1; tpcrg1 |= SM(rfXpdGain[0], AR_PHY_TPCRG1_PDGAIN_SETTING1); break; } #ifdef AH_DEBUG if (tpcrg1 != OS_REG_READ(ah, AR_PHY_TPCRG1)) HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: using non-default " "pd_gains (default 0x%x, calculated 0x%x)\n", __func__, OS_REG_READ(ah, AR_PHY_TPCRG1), tpcrg1); #endif OS_REG_WRITE(ah, AR_PHY_TPCRG1, tpcrg1); #endif /* * Note the pdadc table may not start at 0 dBm power, could be * negative or greater than 0. Need to offset the power * values by the amount of minPower for griffin */ if (minCalPower5413_t2 != 0) ahp->ah_txPowerIndexOffset = (int16_t)(0 - minCalPower5413_t2); else ahp->ah_txPowerIndexOffset = 0; /* Finally, write the power values into the baseband power table */ regoffset = 0x9800 + (672 <<2); /* beginning of pdadc table in griffin */ for (i = 0; i < 32; i++) { reg32 = ((pdadcValues[4*i + 0] & 0xFF) << 0) | ((pdadcValues[4*i + 1] & 0xFF) << 8) | ((pdadcValues[4*i + 2] & 0xFF) << 16) | ((pdadcValues[4*i + 3] & 0xFF) << 24) ; OS_REG_WRITE(ah, regoffset, reg32); regoffset += 4; } OS_REG_WRITE(ah, AR_PHY_TPCRG5, SM(pdGainOverlap_t2, AR_PHY_TPCRG5_PD_GAIN_OVERLAP) | SM(gainBoundaries[0], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1) | SM(gainBoundaries[1], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2) | SM(gainBoundaries[2], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3) | SM(gainBoundaries[3], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4)); return AH_TRUE; } static int16_t ar5413GetMinPower(struct ath_hal *ah, const RAW_DATA_PER_CHANNEL_2413 *data) { uint32_t ii,jj; uint16_t Pmin=0,numVpd; for (ii = 0; ii < MAX_NUM_PDGAINS_PER_CHANNEL; ii++) { jj = MAX_NUM_PDGAINS_PER_CHANNEL - ii - 1; /* work backwards 'cause highest pdGain for lowest power */ numVpd = data->pDataPerPDGain[jj].numVpd; if (numVpd > 0) { Pmin = data->pDataPerPDGain[jj].pwr_t4[0]; return(Pmin); } } return(Pmin); } static int16_t ar5413GetMaxPower(struct ath_hal *ah, const RAW_DATA_PER_CHANNEL_2413 *data) { uint32_t ii; uint16_t Pmax=0,numVpd; for (ii=0; ii< MAX_NUM_PDGAINS_PER_CHANNEL; ii++) { /* work forwards cuase lowest pdGain for highest power */ numVpd = data->pDataPerPDGain[ii].numVpd; if (numVpd > 0) { Pmax = data->pDataPerPDGain[ii].pwr_t4[numVpd-1]; return(Pmax); } } return(Pmax); } static HAL_BOOL ar5413GetChannelMaxMinPower(struct ath_hal *ah, const struct ieee80211_channel *chan, int16_t *maxPow, int16_t *minPow) { uint16_t freq = chan->ic_freq; /* NB: never mapped */ const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; const RAW_DATA_STRUCT_2413 *pRawDataset = AH_NULL; const RAW_DATA_PER_CHANNEL_2413 *data=AH_NULL; uint16_t numChannels; int totalD,totalF, totalMin,last, i; *maxPow = 0; if (IEEE80211_IS_CHAN_G(chan) || IEEE80211_IS_CHAN_108G(chan)) pRawDataset = &ee->ee_rawDataset2413[headerInfo11G]; else if (IEEE80211_IS_CHAN_B(chan)) pRawDataset = &ee->ee_rawDataset2413[headerInfo11B]; else { HALASSERT(IEEE80211_IS_CHAN_5GHZ(chan)); pRawDataset = &ee->ee_rawDataset2413[headerInfo11A]; } numChannels = pRawDataset->numChannels; data = pRawDataset->pDataPerChannel; /* Make sure the channel is in the range of the TP values * (freq piers) */ if (numChannels < 1) return(AH_FALSE); if ((freq < data[0].channelValue) || (freq > data[numChannels-1].channelValue)) { if (freq < data[0].channelValue) { *maxPow = ar5413GetMaxPower(ah, &data[0]); *minPow = ar5413GetMinPower(ah, &data[0]); return(AH_TRUE); } else { *maxPow = ar5413GetMaxPower(ah, &data[numChannels - 1]); *minPow = ar5413GetMinPower(ah, &data[numChannels - 1]); return(AH_TRUE); } } /* Linearly interpolate the power value now */ for (last=0,i=0; (i data[i].channelValue); last = i++); totalD = data[i].channelValue - data[last].channelValue; if (totalD > 0) { totalF = ar5413GetMaxPower(ah, &data[i]) - ar5413GetMaxPower(ah, &data[last]); *maxPow = (int8_t) ((totalF*(freq-data[last].channelValue) + ar5413GetMaxPower(ah, &data[last])*totalD)/totalD); totalMin = ar5413GetMinPower(ah, &data[i]) - ar5413GetMinPower(ah, &data[last]); *minPow = (int8_t) ((totalMin*(freq-data[last].channelValue) + ar5413GetMinPower(ah, &data[last])*totalD)/totalD); return(AH_TRUE); } else { if (freq == data[i].channelValue) { *maxPow = ar5413GetMaxPower(ah, &data[i]); *minPow = ar5413GetMinPower(ah, &data[i]); return(AH_TRUE); } else return(AH_FALSE); } } /* * Free memory for analog bank scratch buffers */ static void ar5413RfDetach(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); HALASSERT(ahp->ah_rfHal != AH_NULL); ath_hal_free(ahp->ah_rfHal); ahp->ah_rfHal = AH_NULL; } /* * Allocate memory for analog bank scratch buffers * Scratch Buffer will be reinitialized every reset so no need to zero now */ static HAL_BOOL ar5413RfAttach(struct ath_hal *ah, HAL_STATUS *status) { struct ath_hal_5212 *ahp = AH5212(ah); struct ar5413State *priv; HALASSERT(ah->ah_magic == AR5212_MAGIC); HALASSERT(ahp->ah_rfHal == AH_NULL); priv = ath_hal_malloc(sizeof(struct ar5413State)); if (priv == AH_NULL) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: cannot allocate private state\n", __func__); *status = HAL_ENOMEM; /* XXX */ return AH_FALSE; } priv->base.rfDetach = ar5413RfDetach; priv->base.writeRegs = ar5413WriteRegs; priv->base.getRfBank = ar5413GetRfBank; priv->base.setChannel = ar5413SetChannel; priv->base.setRfRegs = ar5413SetRfRegs; priv->base.setPowerTable = ar5413SetPowerTable; priv->base.getChannelMaxMinPower = ar5413GetChannelMaxMinPower; priv->base.getNfAdjust = ar5212GetNfAdjust; ahp->ah_pcdacTable = priv->pcdacTable; ahp->ah_pcdacTableSize = sizeof(priv->pcdacTable); ahp->ah_rfHal = &priv->base; return AH_TRUE; } static HAL_BOOL ar5413Probe(struct ath_hal *ah) { return IS_5413(ah); } AH_RF(RF5413, ar5413Probe, ar5413RfAttach); Index: head/sys/dev/ath/ath_hal/ar5416/ar5416_btcoex.c =================================================================== --- head/sys/dev/ath/ath_hal/ar5416/ar5416_btcoex.c (revision 298938) +++ head/sys/dev/ath/ath_hal/ar5416/ar5416_btcoex.c (revision 298939) @@ -1,394 +1,394 @@ /* * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting * Copyright (c) 2002-2005 Atheros Communications, Inc. * Copyright (c) 2008-2010, Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ah_devid.h" #ifdef AH_DEBUG #include "ah_desc.h" /* NB: for HAL_PHYERR* */ #endif #include "ar5416/ar5416.h" #include "ar5416/ar5416reg.h" #include "ar5416/ar5416phy.h" #include "ar5416/ar5416desc.h" /* AR5416_CONTTXMODE */ #include "ar5416/ar5416_btcoex.h" void ar5416SetBTCoexInfo(struct ath_hal *ah, HAL_BT_COEX_INFO *btinfo) { struct ath_hal_5416 *ahp = AH5416(ah); ahp->ah_btModule = btinfo->bt_module; ahp->ah_btCoexConfigType = btinfo->bt_coex_config; ahp->ah_btActiveGpioSelect = btinfo->bt_gpio_bt_active; ahp->ah_btPriorityGpioSelect = btinfo->bt_gpio_bt_priority; ahp->ah_wlanActiveGpioSelect = btinfo->bt_gpio_wlan_active; ahp->ah_btActivePolarity = btinfo->bt_active_polarity; ahp->ah_btCoexSingleAnt = btinfo->bt_single_ant; ahp->ah_btWlanIsolation = btinfo->bt_isolation; } void ar5416BTCoexConfig(struct ath_hal *ah, HAL_BT_COEX_CONFIG *btconf) { struct ath_hal_5416 *ahp = AH5416(ah); HAL_BOOL rxClearPolarity = btconf->bt_rxclear_polarity; /* * For Kiwi and Osprey, the polarity of rx_clear is active high. * The bt_rxclear_polarity flag from ath(4) needs to be inverted. */ if (AR_SREV_KIWI(ah)) { rxClearPolarity = !btconf->bt_rxclear_polarity; } ahp->ah_btCoexMode = (ahp->ah_btCoexMode & AR_BT_QCU_THRESH) | SM(btconf->bt_time_extend, AR_BT_TIME_EXTEND) | SM(btconf->bt_txstate_extend, AR_BT_TXSTATE_EXTEND) | SM(btconf->bt_txframe_extend, AR_BT_TX_FRAME_EXTEND) | SM(btconf->bt_mode, AR_BT_MODE) | SM(btconf->bt_quiet_collision, AR_BT_QUIET) | SM(rxClearPolarity, AR_BT_RX_CLEAR_POLARITY) | SM(btconf->bt_priority_time, AR_BT_PRIORITY_TIME) | SM(btconf->bt_first_slot_time, AR_BT_FIRST_SLOT_TIME); ahp->ah_btCoexMode2 |= SM(btconf->bt_hold_rxclear, AR_BT_HOLD_RX_CLEAR); if (ahp->ah_btCoexSingleAnt == AH_FALSE) { /* Enable ACK to go out even though BT has higher priority. */ ahp->ah_btCoexMode2 |= AR_BT_DISABLE_BT_ANT; } } void ar5416BTCoexSetQcuThresh(struct ath_hal *ah, int qnum) { struct ath_hal_5416 *ahp = AH5416(ah); ahp->ah_btCoexMode |= SM(qnum, AR_BT_QCU_THRESH); } void ar5416BTCoexSetWeights(struct ath_hal *ah, u_int32_t stompType) { struct ath_hal_5416 *ahp = AH5416(ah); if (AR_SREV_KIWI_10_OR_LATER(ah)) { - /* TODO: TX RX seperate is not enabled. */ + /* TODO: TX RX separate is not enabled. */ switch (stompType) { case HAL_BT_COEX_STOMP_ALL: ahp->ah_btCoexBTWeight = AR5416_BT_WGHT; ahp->ah_btCoexWLANWeight = AR5416_STOMP_ALL_WLAN_WGHT; break; case HAL_BT_COEX_STOMP_LOW: ahp->ah_btCoexBTWeight = AR5416_BT_WGHT; ahp->ah_btCoexWLANWeight = AR5416_STOMP_LOW_WLAN_WGHT; break; case HAL_BT_COEX_STOMP_ALL_FORCE: ahp->ah_btCoexBTWeight = AR5416_BT_WGHT; ahp->ah_btCoexWLANWeight = AR5416_STOMP_ALL_FORCE_WLAN_WGHT; break; case HAL_BT_COEX_STOMP_LOW_FORCE: ahp->ah_btCoexBTWeight = AR5416_BT_WGHT; ahp->ah_btCoexWLANWeight = AR5416_STOMP_LOW_FORCE_WLAN_WGHT; break; case HAL_BT_COEX_STOMP_NONE: case HAL_BT_COEX_NO_STOMP: ahp->ah_btCoexBTWeight = AR5416_BT_WGHT; ahp->ah_btCoexWLANWeight = AR5416_STOMP_NONE_WLAN_WGHT; break; default: /* There is a forceWeight from registry */ ahp->ah_btCoexBTWeight = stompType & 0xffff; ahp->ah_btCoexWLANWeight = stompType >> 16; break; } } else { switch (stompType) { case HAL_BT_COEX_STOMP_ALL: ahp->ah_btCoexBTWeight = AR5416_BT_WGHT; ahp->ah_btCoexWLANWeight = AR5416_STOMP_ALL_WLAN_WGHT; break; case HAL_BT_COEX_STOMP_LOW: ahp->ah_btCoexBTWeight = AR5416_BT_WGHT; ahp->ah_btCoexWLANWeight = AR5416_STOMP_LOW_WLAN_WGHT; break; case HAL_BT_COEX_STOMP_ALL_FORCE: ahp->ah_btCoexBTWeight = AR5416_BT_WGHT; ahp->ah_btCoexWLANWeight = AR5416_STOMP_ALL_FORCE_WLAN_WGHT; break; case HAL_BT_COEX_STOMP_LOW_FORCE: ahp->ah_btCoexBTWeight = AR5416_BT_WGHT; ahp->ah_btCoexWLANWeight = AR5416_STOMP_LOW_FORCE_WLAN_WGHT; break; case HAL_BT_COEX_STOMP_NONE: case HAL_BT_COEX_NO_STOMP: ahp->ah_btCoexBTWeight = AR5416_BT_WGHT; ahp->ah_btCoexWLANWeight = AR5416_STOMP_NONE_WLAN_WGHT; break; default: /* There is a forceWeight from registry */ ahp->ah_btCoexBTWeight = stompType & 0xffff; ahp->ah_btCoexWLANWeight = stompType >> 16; break; } } } void ar5416BTCoexSetupBmissThresh(struct ath_hal *ah, u_int32_t thresh) { struct ath_hal_5416 *ahp = AH5416(ah); ahp->ah_btCoexMode2 |= SM(thresh, AR_BT_BCN_MISS_THRESH); } /* * There is no antenna diversity for Owl, Kiwi, etc. * * Kite will override this particular method. */ void ar5416BTCoexAntennaDiversity(struct ath_hal *ah) { } void ar5416BTCoexSetParameter(struct ath_hal *ah, u_int32_t type, u_int32_t value) { struct ath_hal_5416 *ahp = AH5416(ah); switch (type) { case HAL_BT_COEX_SET_ACK_PWR: if (value) { ahp->ah_btCoexFlag |= HAL_BT_COEX_FLAG_LOW_ACK_PWR; OS_REG_WRITE(ah, AR_TPC, HAL_BT_COEX_LOW_ACK_POWER); } else { ahp->ah_btCoexFlag &= ~HAL_BT_COEX_FLAG_LOW_ACK_PWR; OS_REG_WRITE(ah, AR_TPC, HAL_BT_COEX_HIGH_ACK_POWER); } break; case HAL_BT_COEX_ANTENNA_DIVERSITY: /* This is overridden for Kite */ break; #if 0 case HAL_BT_COEX_LOWER_TX_PWR: if (value) { if ((ahp->ah_btCoexFlag & HAL_BT_COEX_FLAG_LOWER_TX_PWR) == 0) { ahp->ah_btCoexFlag |= HAL_BT_COEX_FLAG_LOWER_TX_PWR; AH_PRIVATE(ah)->ah_config.ath_hal_desc_tpc = 1; ar5416SetTxPowerLimit(ah, AH_PRIVATE(ah)->ah_power_limit, AH_PRIVATE(ah)->ah_extra_txpow, 0); } } else { if (ahp->ah_btCoexFlag & HAL_BT_COEX_FLAG_LOWER_TX_PWR) { ahp->ah_btCoexFlag &= ~HAL_BT_COEX_FLAG_LOWER_TX_PWR; AH_PRIVATE(ah)->ah_config.ath_hal_desc_tpc = 0; ar5416SetTxPowerLimit(ah, AH_PRIVATE(ah)->ah_power_limit, AH_PRIVATE(ah)->ah_extra_txpow, 0); } } break; #endif default: break; } } void ar5416BTCoexDisable(struct ath_hal *ah) { struct ath_hal_5416 *ahp = AH5416(ah); /* Always drive rx_clear_external output as 0 */ ar5416GpioSet(ah, ahp->ah_wlanActiveGpioSelect, 0); ar5416GpioCfgOutput(ah, ahp->ah_wlanActiveGpioSelect, HAL_GPIO_OUTPUT_MUX_AS_OUTPUT); if (AR_SREV_9271(ah)) { /* * Set wlanActiveGpio to input when disabling BT-COEX to * reduce power consumption */ ar5416GpioCfgInput(ah, ahp->ah_wlanActiveGpioSelect); } if (ahp->ah_btCoexSingleAnt == AH_TRUE) { OS_REG_RMW_FIELD(ah, AR_QUIET1, AR_QUIET1_QUIET_ACK_CTS_ENABLE, 1); OS_REG_RMW_FIELD(ah, AR_MISC_MODE, AR_PCU_BT_ANT_PREVENT_RX, 0); } OS_REG_WRITE(ah, AR_BT_COEX_MODE, AR_BT_QUIET | AR_BT_MODE); OS_REG_WRITE(ah, AR_BT_COEX_WEIGHT, 0); if (AR_SREV_KIWI_10_OR_LATER(ah)) OS_REG_WRITE(ah, AR_BT_COEX_WEIGHT2, 0); OS_REG_WRITE(ah, AR_BT_COEX_MODE2, 0); ahp->ah_btCoexEnabled = AH_FALSE; } int ar5416BTCoexEnable(struct ath_hal *ah) { struct ath_hal_5416 *ahp = AH5416(ah); /* Program coex mode and weight registers to actually enable coex */ OS_REG_WRITE(ah, AR_BT_COEX_MODE, ahp->ah_btCoexMode); OS_REG_WRITE(ah, AR_BT_COEX_WEIGHT, SM(ahp->ah_btCoexWLANWeight & 0xFFFF, AR_BT_WL_WGHT) | SM(ahp->ah_btCoexBTWeight & 0xFFFF, AR_BT_BT_WGHT)); if (AR_SREV_KIWI_10_OR_LATER(ah)) { OS_REG_WRITE(ah, AR_BT_COEX_WEIGHT2, SM(ahp->ah_btCoexWLANWeight >> 16, AR_BT_WL_WGHT)); } OS_REG_WRITE(ah, AR_BT_COEX_MODE2, ahp->ah_btCoexMode2); /* Added Select GPIO5~8 instaed SPI */ if (AR_SREV_9271(ah)) { uint32_t val; val = OS_REG_READ(ah, AR9271_CLOCK_CONTROL); val &= 0xFFFFFEFF; OS_REG_WRITE(ah, AR9271_CLOCK_CONTROL, val); } if (ahp->ah_btCoexFlag & HAL_BT_COEX_FLAG_LOW_ACK_PWR) OS_REG_WRITE(ah, AR_TPC, HAL_BT_COEX_LOW_ACK_POWER); else OS_REG_WRITE(ah, AR_TPC, HAL_BT_COEX_HIGH_ACK_POWER); if (ahp->ah_btCoexSingleAnt == AH_TRUE) { OS_REG_RMW_FIELD(ah, AR_QUIET1, AR_QUIET1_QUIET_ACK_CTS_ENABLE, 1); /* XXX should update miscMode? */ OS_REG_RMW_FIELD(ah, AR_MISC_MODE, AR_PCU_BT_ANT_PREVENT_RX, 1); } else { OS_REG_RMW_FIELD(ah, AR_QUIET1, AR_QUIET1_QUIET_ACK_CTS_ENABLE, 1); /* XXX should update miscMode? */ OS_REG_RMW_FIELD(ah, AR_MISC_MODE, AR_PCU_BT_ANT_PREVENT_RX, 0); } if (ahp->ah_btCoexConfigType == HAL_BT_COEX_CFG_3WIRE) { /* For 3-wire, configure the desired GPIO port for rx_clear */ ar5416GpioCfgOutput(ah, ahp->ah_wlanActiveGpioSelect, HAL_GPIO_OUTPUT_MUX_AS_WLAN_ACTIVE); } else { /* * For 2-wire, configure the desired GPIO port * for TX_FRAME output */ ar5416GpioCfgOutput(ah, ahp->ah_wlanActiveGpioSelect, HAL_GPIO_OUTPUT_MUX_AS_TX_FRAME); } /* * Enable a weak pull down on BT_ACTIVE. * When BT device is disabled, BT_ACTIVE might be floating. */ OS_REG_RMW(ah, AR_GPIO_PDPU, (0x2 << (ahp->ah_btActiveGpioSelect * 2)), (0x3 << (ahp->ah_btActiveGpioSelect * 2))); ahp->ah_btCoexEnabled = AH_TRUE; return (0); } void ar5416InitBTCoex(struct ath_hal *ah) { struct ath_hal_5416 *ahp = AH5416(ah); HALDEBUG(ah, HAL_DEBUG_BT_COEX, "%s: called; configType=%d\n", __func__, ahp->ah_btCoexConfigType); if (ahp->ah_btCoexConfigType == HAL_BT_COEX_CFG_3WIRE) { OS_REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, (AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_BB | AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB)); /* * Set input mux for bt_prority_async and * bt_active_async to GPIO pins */ OS_REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1, AR_GPIO_INPUT_MUX1_BT_ACTIVE, ahp->ah_btActiveGpioSelect); OS_REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1, AR_GPIO_INPUT_MUX1_BT_PRIORITY, ahp->ah_btPriorityGpioSelect); /* * Configure the desired GPIO ports for input */ ar5416GpioCfgInput(ah, ahp->ah_btActiveGpioSelect); ar5416GpioCfgInput(ah, ahp->ah_btPriorityGpioSelect); /* * Configure the antenna diversity setup. * It's a no-op for AR9287; AR9285 overrides this * as required. */ AH5416(ah)->ah_btCoexSetDiversity(ah); if (ahp->ah_btCoexEnabled) ar5416BTCoexEnable(ah); else ar5416BTCoexDisable(ah); } else if (ahp->ah_btCoexConfigType != HAL_BT_COEX_CFG_NONE) { /* 2-wire */ if (ahp->ah_btCoexEnabled) { /* Connect bt_active_async to baseband */ OS_REG_CLR_BIT(ah, AR_GPIO_INPUT_EN_VAL, (AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_DEF | AR_GPIO_INPUT_EN_VAL_BT_FREQUENCY_DEF)); OS_REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB); /* * Set input mux for bt_prority_async and * bt_active_async to GPIO pins */ OS_REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1, AR_GPIO_INPUT_MUX1_BT_ACTIVE, ahp->ah_btActiveGpioSelect); /* Configure the desired GPIO ports for input */ ar5416GpioCfgInput(ah, ahp->ah_btActiveGpioSelect); /* Enable coexistence on initialization */ ar5416BTCoexEnable(ah); } } } Index: head/sys/dev/ath/ath_hal/ar5416/ar5416_cal.c =================================================================== --- head/sys/dev/ath/ath_hal/ar5416/ar5416_cal.c (revision 298938) +++ head/sys/dev/ath/ath_hal/ar5416/ar5416_cal.c (revision 298939) @@ -1,832 +1,832 @@ /* * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * Copyright (c) 2002-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ah_devid.h" #include "ah_eeprom_v14.h" #include "ar5212/ar5212.h" /* for NF cal related declarations */ #include "ar5416/ar5416.h" #include "ar5416/ar5416reg.h" #include "ar5416/ar5416phy.h" /* Owl specific stuff */ #define NUM_NOISEFLOOR_READINGS 6 /* 3 chains * (ctl + ext) */ static void ar5416StartNFCal(struct ath_hal *ah); static void ar5416LoadNF(struct ath_hal *ah, const struct ieee80211_channel *); static int16_t ar5416GetNf(struct ath_hal *, struct ieee80211_channel *); static uint16_t ar5416GetDefaultNF(struct ath_hal *ah, const struct ieee80211_channel *chan); static void ar5416SanitizeNF(struct ath_hal *ah, int16_t *nf); /* * Determine if calibration is supported by device and channel flags */ /* * ADC GAIN/DC offset calibration is for calibrating two ADCs that * are acting as one by interleaving incoming symbols. This isn't * relevant for 2.4GHz 20MHz wide modes because, as far as I can tell, * the secondary ADC is never enabled. It is enabled however for * 5GHz modes. * * It hasn't been confirmed whether doing this calibration is needed * at all in the above modes and/or whether it's actually harmful. * So for now, let's leave it enabled and just remember to get * confirmation that it needs to be clarified. * * See US Patent No: US 7,541,952 B1: * " Method and Apparatus for Offset and Gain Compensation for * Analog-to-Digital Converters." */ static OS_INLINE HAL_BOOL ar5416IsCalSupp(struct ath_hal *ah, const struct ieee80211_channel *chan, HAL_CAL_TYPE calType) { struct ar5416PerCal *cal = &AH5416(ah)->ah_cal; switch (calType & cal->suppCals) { case IQ_MISMATCH_CAL: /* Run IQ Mismatch for non-CCK only */ return !IEEE80211_IS_CHAN_B(chan); case ADC_GAIN_CAL: case ADC_DC_CAL: /* * Run ADC Gain Cal for either 5ghz any or 2ghz HT40. * * Don't run ADC calibrations for 5ghz fast clock mode * in HT20 - only one ADC is used. */ if (IEEE80211_IS_CHAN_HT20(chan) && (IS_5GHZ_FAST_CLOCK_EN(ah, chan))) return AH_FALSE; if (IEEE80211_IS_CHAN_5GHZ(chan)) return AH_TRUE; if (IEEE80211_IS_CHAN_HT40(chan)) return AH_TRUE; return AH_FALSE; } return AH_FALSE; } /* * Setup HW to collect samples used for current cal */ static void ar5416SetupMeasurement(struct ath_hal *ah, HAL_CAL_LIST *currCal) { /* Start calibration w/ 2^(INIT_IQCAL_LOG_COUNT_MAX+1) samples */ OS_REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX, currCal->calData->calCountMax); /* Select calibration to run */ switch (currCal->calData->calType) { case IQ_MISMATCH_CAL: OS_REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_IQ); HALDEBUG(ah, HAL_DEBUG_PERCAL, "%s: start IQ Mismatch calibration\n", __func__); break; case ADC_GAIN_CAL: OS_REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_GAIN); HALDEBUG(ah, HAL_DEBUG_PERCAL, "%s: start ADC Gain calibration\n", __func__); break; case ADC_DC_CAL: OS_REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_DC_PER); HALDEBUG(ah, HAL_DEBUG_PERCAL, "%s: start ADC DC calibration\n", __func__); break; case ADC_DC_INIT_CAL: OS_REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_DC_INIT); HALDEBUG(ah, HAL_DEBUG_PERCAL, "%s: start Init ADC DC calibration\n", __func__); break; } /* Kick-off cal */ OS_REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_DO_CAL); } /* * Initialize shared data structures and prepare a cal to be run. */ static void ar5416ResetMeasurement(struct ath_hal *ah, HAL_CAL_LIST *currCal) { struct ar5416PerCal *cal = &AH5416(ah)->ah_cal; /* Reset data structures shared between different calibrations */ OS_MEMZERO(cal->caldata, sizeof(cal->caldata)); cal->calSamples = 0; /* Setup HW for new calibration */ ar5416SetupMeasurement(ah, currCal); /* Change SW state to RUNNING for this calibration */ currCal->calState = CAL_RUNNING; } #if 0 /* * Run non-periodic calibrations. */ static HAL_BOOL ar5416RunInitCals(struct ath_hal *ah, int init_cal_count) { struct ath_hal_5416 *ahp = AH5416(ah); struct ar5416PerCal *cal = &AH5416(ah)->ah_cal; HAL_CHANNEL_INTERNAL ichan; /* XXX bogus */ HAL_CAL_LIST *curCal = ahp->ah_cal_curr; HAL_BOOL isCalDone; int i; if (curCal == AH_NULL) return AH_FALSE; ichan.calValid = 0; for (i = 0; i < init_cal_count; i++) { /* Reset this Cal */ ar5416ResetMeasurement(ah, curCal); /* Poll for offset calibration complete */ if (!ath_hal_wait(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_DO_CAL, 0)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: Cal %d failed to finish in 100ms.\n", __func__, curCal->calData->calType); /* Re-initialize list pointers for periodic cals */ cal->cal_list = cal->cal_last = cal->cal_curr = AH_NULL; return AH_FALSE; } /* Run this cal */ ar5416DoCalibration(ah, &ichan, ahp->ah_rxchainmask, curCal, &isCalDone); if (!isCalDone) HALDEBUG(ah, HAL_DEBUG_ANY, "%s: init cal %d did not complete.\n", __func__, curCal->calData->calType); if (curCal->calNext != AH_NULL) curCal = curCal->calNext; } /* Re-initialize list pointers for periodic cals */ cal->cal_list = cal->cal_last = cal->cal_curr = AH_NULL; return AH_TRUE; } #endif /* * AGC calibration for the AR5416, AR9130, AR9160, AR9280. */ HAL_BOOL ar5416InitCalHardware(struct ath_hal *ah, const struct ieee80211_channel *chan) { if (AR_SREV_MERLIN_10_OR_LATER(ah)) { /* Disable ADC */ OS_REG_CLR_BIT(ah, AR_PHY_ADC_CTL, AR_PHY_ADC_CTL_OFF_PWDADC); /* Enable Rx Filter Cal */ OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_FLTR_CAL); } /* Calibrate the AGC */ OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL); /* Poll for offset calibration complete */ if (!ath_hal_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: offset calibration did not complete in 1ms; " "noisy environment?\n", __func__); return AH_FALSE; } if (AR_SREV_MERLIN_10_OR_LATER(ah)) { /* Enable ADC */ OS_REG_SET_BIT(ah, AR_PHY_ADC_CTL, AR_PHY_ADC_CTL_OFF_PWDADC); /* Disable Rx Filter Cal */ OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_FLTR_CAL); } return AH_TRUE; } /* * Initialize Calibration infrastructure. */ #define MAX_CAL_CHECK 32 HAL_BOOL ar5416InitCal(struct ath_hal *ah, const struct ieee80211_channel *chan) { struct ar5416PerCal *cal = &AH5416(ah)->ah_cal; HAL_CHANNEL_INTERNAL *ichan; ichan = ath_hal_checkchannel(ah, chan); HALASSERT(ichan != AH_NULL); /* Do initial chipset-specific calibration */ if (! AH5416(ah)->ah_cal_initcal(ah, chan)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: initial chipset calibration did " "not complete in time; noisy environment?\n", __func__); return AH_FALSE; } /* If there's PA Cal, do it */ if (AH5416(ah)->ah_cal_pacal) AH5416(ah)->ah_cal_pacal(ah, AH_TRUE); /* * Do NF calibration after DC offset and other CALs. * Per system engineers, noise floor value can sometimes be 20 dB * higher than normal value if DC offset and noise floor cal are * triggered at the same time. */ OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); /* * This may take a while to run; make sure subsequent * calibration routines check that this has completed * before reading the value and triggering a subsequent * calibration. */ /* Initialize list pointers */ cal->cal_list = cal->cal_last = cal->cal_curr = AH_NULL; /* * Enable IQ, ADC Gain, ADC DC Offset Cals */ if (AR_SREV_HOWL(ah) || AR_SREV_SOWL_10_OR_LATER(ah)) { /* Setup all non-periodic, init time only calibrations */ /* XXX: Init DC Offset not working yet */ #if 0 if (ar5416IsCalSupp(ah, chan, ADC_DC_INIT_CAL)) { INIT_CAL(&cal->adcDcCalInitData); INSERT_CAL(cal, &cal->adcDcCalInitData); } /* Initialize current pointer to first element in list */ cal->cal_curr = cal->cal_list; if (cal->ah_cal_curr != AH_NULL && !ar5416RunInitCals(ah, 0)) return AH_FALSE; #endif } /* If Cals are supported, add them to list via INIT/INSERT_CAL */ if (ar5416IsCalSupp(ah, chan, ADC_GAIN_CAL)) { INIT_CAL(&cal->adcGainCalData); INSERT_CAL(cal, &cal->adcGainCalData); HALDEBUG(ah, HAL_DEBUG_PERCAL, "%s: enable ADC Gain Calibration.\n", __func__); } if (ar5416IsCalSupp(ah, chan, ADC_DC_CAL)) { INIT_CAL(&cal->adcDcCalData); INSERT_CAL(cal, &cal->adcDcCalData); HALDEBUG(ah, HAL_DEBUG_PERCAL, "%s: enable ADC DC Calibration.\n", __func__); } if (ar5416IsCalSupp(ah, chan, IQ_MISMATCH_CAL)) { INIT_CAL(&cal->iqCalData); INSERT_CAL(cal, &cal->iqCalData); HALDEBUG(ah, HAL_DEBUG_PERCAL, "%s: enable IQ Calibration.\n", __func__); } /* Initialize current pointer to first element in list */ cal->cal_curr = cal->cal_list; /* Kick off measurements for the first cal */ if (cal->cal_curr != AH_NULL) ar5416ResetMeasurement(ah, cal->cal_curr); /* Mark all calibrations on this channel as being invalid */ ichan->calValid = 0; return AH_TRUE; #undef MAX_CAL_CHECK } /* * Entry point for upper layers to restart current cal. * Reset the calibration valid bit in channel. */ HAL_BOOL ar5416ResetCalValid(struct ath_hal *ah, const struct ieee80211_channel *chan) { struct ar5416PerCal *cal = &AH5416(ah)->ah_cal; HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); HAL_CAL_LIST *currCal = cal->cal_curr; if (!AR_SREV_SOWL_10_OR_LATER(ah)) return AH_FALSE; if (currCal == AH_NULL) return AH_FALSE; if (ichan == AH_NULL) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel %u/0x%x; no mapping\n", __func__, chan->ic_freq, chan->ic_flags); return AH_FALSE; } /* * Expected that this calibration has run before, post-reset. * Current state should be done */ if (currCal->calState != CAL_DONE) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: Calibration state incorrect, %d\n", __func__, currCal->calState); return AH_FALSE; } /* Verify Cal is supported on this channel */ if (!ar5416IsCalSupp(ah, chan, currCal->calData->calType)) return AH_FALSE; HALDEBUG(ah, HAL_DEBUG_PERCAL, "%s: Resetting Cal %d state for channel %u/0x%x\n", __func__, currCal->calData->calType, chan->ic_freq, chan->ic_flags); /* Disable cal validity in channel */ ichan->calValid &= ~currCal->calData->calType; currCal->calState = CAL_WAITING; return AH_TRUE; } /* * Recalibrate the lower PHY chips to account for temperature/environment * changes. */ static void ar5416DoCalibration(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *ichan, uint8_t rxchainmask, HAL_CAL_LIST *currCal, HAL_BOOL *isCalDone) { struct ar5416PerCal *cal = &AH5416(ah)->ah_cal; /* Cal is assumed not done until explicitly set below */ *isCalDone = AH_FALSE; HALDEBUG(ah, HAL_DEBUG_PERCAL, "%s: %s Calibration, state %d, calValid 0x%x\n", __func__, currCal->calData->calName, currCal->calState, ichan->calValid); /* Calibration in progress. */ if (currCal->calState == CAL_RUNNING) { /* Check to see if it has finished. */ if (!(OS_REG_READ(ah, AR_PHY_TIMING_CTRL4) & AR_PHY_TIMING_CTRL4_DO_CAL)) { HALDEBUG(ah, HAL_DEBUG_PERCAL, "%s: sample %d of %d finished\n", __func__, cal->calSamples, currCal->calData->calNumSamples); /* * Collect measurements for active chains. */ currCal->calData->calCollect(ah); if (++cal->calSamples >= currCal->calData->calNumSamples) { int i, numChains = 0; for (i = 0; i < AR5416_MAX_CHAINS; i++) { if (rxchainmask & (1 << i)) numChains++; } /* * Process accumulated data */ currCal->calData->calPostProc(ah, numChains); /* Calibration has finished. */ ichan->calValid |= currCal->calData->calType; currCal->calState = CAL_DONE; *isCalDone = AH_TRUE; } else { /* * Set-up to collect of another sub-sample. */ ar5416SetupMeasurement(ah, currCal); } } } else if (!(ichan->calValid & currCal->calData->calType)) { /* If current cal is marked invalid in channel, kick it off */ ar5416ResetMeasurement(ah, currCal); } } /* * Internal interface to schedule periodic calibration work. */ HAL_BOOL ar5416PerCalibrationN(struct ath_hal *ah, struct ieee80211_channel *chan, u_int rxchainmask, HAL_BOOL longcal, HAL_BOOL *isCalDone) { struct ar5416PerCal *cal = &AH5416(ah)->ah_cal; HAL_CAL_LIST *currCal = cal->cal_curr; HAL_CHANNEL_INTERNAL *ichan; int r; OS_MARK(ah, AH_MARK_PERCAL, chan->ic_freq); *isCalDone = AH_TRUE; /* * Since ath_hal calls the PerCal method with rxchainmask=0x1; * override it with the current chainmask. The upper levels currently * doesn't know about the chainmask. */ rxchainmask = AH5416(ah)->ah_rx_chainmask; /* Invalid channel check */ ichan = ath_hal_checkchannel(ah, chan); if (ichan == AH_NULL) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel %u/0x%x; no mapping\n", __func__, chan->ic_freq, chan->ic_flags); return AH_FALSE; } /* * For given calibration: * 1. Call generic cal routine * 2. When this cal is done (isCalDone) if we have more cals waiting * (eg after reset), mask this to upper layers by not propagating * isCalDone if it is set to TRUE. * Instead, change isCalDone to FALSE and setup the waiting cal(s) * to be run. */ if (currCal != AH_NULL && (currCal->calState == CAL_RUNNING || currCal->calState == CAL_WAITING)) { ar5416DoCalibration(ah, ichan, rxchainmask, currCal, isCalDone); if (*isCalDone == AH_TRUE) { cal->cal_curr = currCal = currCal->calNext; if (currCal->calState == CAL_WAITING) { *isCalDone = AH_FALSE; ar5416ResetMeasurement(ah, currCal); } } } /* Do NF cal only at longer intervals */ if (longcal) { /* Do PA calibration if the chipset supports */ if (AH5416(ah)->ah_cal_pacal) AH5416(ah)->ah_cal_pacal(ah, AH_FALSE); /* Do open-loop temperature compensation if the chipset needs it */ if (ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) AH5416(ah)->ah_olcTempCompensation(ah); /* * Get the value from the previous NF cal * and update the history buffer. */ r = ar5416GetNf(ah, chan); if (r == 0 || r == -1) { /* NF calibration result isn't valid */ HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s: NF calibration" " didn't finish; delaying CCA\n", __func__); } else { /* * NF calibration result is valid. * * Load the NF from history buffer of the current channel. * NF is slow time-variant, so it is OK to use a * historical value. */ ar5416LoadNF(ah, AH_PRIVATE(ah)->ah_curchan); /* start NF calibration, without updating BB NF register*/ ar5416StartNFCal(ah); } } return AH_TRUE; } /* * Recalibrate the lower PHY chips to account for temperature/environment * changes. */ HAL_BOOL ar5416PerCalibration(struct ath_hal *ah, struct ieee80211_channel *chan, HAL_BOOL *isIQdone) { struct ath_hal_5416 *ahp = AH5416(ah); struct ar5416PerCal *cal = &AH5416(ah)->ah_cal; HAL_CAL_LIST *curCal = cal->cal_curr; if (curCal != AH_NULL && curCal->calData->calType == IQ_MISMATCH_CAL) { return ar5416PerCalibrationN(ah, chan, ahp->ah_rx_chainmask, AH_TRUE, isIQdone); } else { HAL_BOOL isCalDone; *isIQdone = AH_FALSE; return ar5416PerCalibrationN(ah, chan, ahp->ah_rx_chainmask, AH_TRUE, &isCalDone); } } static HAL_BOOL ar5416GetEepromNoiseFloorThresh(struct ath_hal *ah, const struct ieee80211_channel *chan, int16_t *nft) { if (IEEE80211_IS_CHAN_5GHZ(chan)) { ath_hal_eepromGet(ah, AR_EEP_NFTHRESH_5, nft); return AH_TRUE; } if (IEEE80211_IS_CHAN_2GHZ(chan)) { ath_hal_eepromGet(ah, AR_EEP_NFTHRESH_2, nft); return AH_TRUE; } HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel flags 0x%x\n", __func__, chan->ic_flags); return AH_FALSE; } static void ar5416StartNFCal(struct ath_hal *ah) { OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF); OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NO_UPDATE_NF); OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); } static void ar5416LoadNF(struct ath_hal *ah, const struct ieee80211_channel *chan) { static const uint32_t ar5416_cca_regs[] = { AR_PHY_CCA, AR_PHY_CH1_CCA, AR_PHY_CH2_CCA, AR_PHY_EXT_CCA, AR_PHY_CH1_EXT_CCA, AR_PHY_CH2_EXT_CCA }; struct ar5212NfCalHist *h; int i; int32_t val; uint8_t chainmask; int16_t default_nf = ar5416GetDefaultNF(ah, chan); /* * Force NF calibration for all chains. */ if (AR_SREV_KITE(ah)) { /* Kite has only one chain */ chainmask = 0x9; } else if (AR_SREV_MERLIN(ah) || AR_SREV_KIWI(ah)) { /* Merlin/Kiwi has only two chains */ chainmask = 0x1B; } else { chainmask = 0x3F; } /* * Write filtered NF values into maxCCApwr register parameter * so we can load below. */ h = AH5416(ah)->ah_cal.nfCalHist; HALDEBUG(ah, HAL_DEBUG_NFCAL, "CCA: "); for (i = 0; i < AR5416_NUM_NF_READINGS; i ++) { /* Don't write to EXT radio CCA registers unless in HT/40 mode */ /* XXX this check should really be cleaner! */ if (i > 2 && !IEEE80211_IS_CHAN_HT40(chan)) continue; if (chainmask & (1 << i)) { int16_t nf_val; if (h) nf_val = h[i].privNF; else nf_val = default_nf; val = OS_REG_READ(ah, ar5416_cca_regs[i]); val &= 0xFFFFFE00; val |= (((uint32_t) nf_val << 1) & 0x1ff); HALDEBUG(ah, HAL_DEBUG_NFCAL, "[%d: %d]", i, nf_val); OS_REG_WRITE(ah, ar5416_cca_regs[i], val); } } HALDEBUG(ah, HAL_DEBUG_NFCAL, "\n"); /* Load software filtered NF value into baseband internal minCCApwr variable. */ OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF); OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NO_UPDATE_NF); OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); /* Wait for load to complete, should be fast, a few 10s of us. */ if (! ar5212WaitNFCalComplete(ah, 1000)) { /* * We timed out waiting for the noisefloor to load, probably due to an * in-progress rx. Simply return here and allow the load plenty of time * to complete before the next calibration interval. We need to avoid * trying to load -50 (which happens below) while the previous load is * still in progress as this can cause rx deafness. Instead by returning * here, the baseband nf cal will just be capped by our present * noisefloor until the next calibration timer. */ HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "Timeout while waiting for " "nf to load: AR_PHY_AGC_CONTROL=0x%x\n", OS_REG_READ(ah, AR_PHY_AGC_CONTROL)); return; } /* * Restore maxCCAPower register parameter again so that we're not capped * by the median we just loaded. This will be initial (and max) value * of next noise floor calibration the baseband does. */ for (i = 0; i < AR5416_NUM_NF_READINGS; i ++) { /* Don't write to EXT radio CCA registers unless in HT/40 mode */ /* XXX this check should really be cleaner! */ if (i > 2 && !IEEE80211_IS_CHAN_HT40(chan)) continue; if (chainmask & (1 << i)) { val = OS_REG_READ(ah, ar5416_cca_regs[i]); val &= 0xFFFFFE00; val |= (((uint32_t)(-50) << 1) & 0x1ff); OS_REG_WRITE(ah, ar5416_cca_regs[i], val); } } } /* * This just initialises the "good" values for AR5416 which * may not be right; it'lll be overridden by ar5416SanitizeNF() * to nominal values. */ void ar5416InitNfHistBuff(struct ar5212NfCalHist *h) { int i, j; for (i = 0; i < AR5416_NUM_NF_READINGS; i ++) { h[i].currIndex = 0; h[i].privNF = AR5416_CCA_MAX_GOOD_VALUE; h[i].invalidNFcount = AR512_NF_CAL_HIST_MAX; for (j = 0; j < AR512_NF_CAL_HIST_MAX; j ++) h[i].nfCalBuffer[j] = AR5416_CCA_MAX_GOOD_VALUE; } } /* * Update the noise floor buffer as a ring buffer */ static void ar5416UpdateNFHistBuff(struct ath_hal *ah, struct ar5212NfCalHist *h, int16_t *nfarray) { int i; /* XXX TODO: don't record nfarray[] entries for inactive chains */ for (i = 0; i < AR5416_NUM_NF_READINGS; i ++) { h[i].nfCalBuffer[h[i].currIndex] = nfarray[i]; if (++h[i].currIndex >= AR512_NF_CAL_HIST_MAX) h[i].currIndex = 0; if (h[i].invalidNFcount > 0) { if (nfarray[i] < AR5416_CCA_MIN_BAD_VALUE || nfarray[i] > AR5416_CCA_MAX_HIGH_VALUE) { h[i].invalidNFcount = AR512_NF_CAL_HIST_MAX; } else { h[i].invalidNFcount--; h[i].privNF = nfarray[i]; } } else { h[i].privNF = ar5212GetNfHistMid(h[i].nfCalBuffer); } } } static uint16_t ar5416GetDefaultNF(struct ath_hal *ah, const struct ieee80211_channel *chan) { struct ar5416NfLimits *limit; if (!chan || IEEE80211_IS_CHAN_2GHZ(chan)) limit = &AH5416(ah)->nf_2g; else limit = &AH5416(ah)->nf_5g; return limit->nominal; } static void ar5416SanitizeNF(struct ath_hal *ah, int16_t *nf) { struct ar5416NfLimits *limit; int i; if (IEEE80211_IS_CHAN_2GHZ(AH_PRIVATE(ah)->ah_curchan)) limit = &AH5416(ah)->nf_2g; else limit = &AH5416(ah)->nf_5g; for (i = 0; i < AR5416_NUM_NF_READINGS; i++) { if (!nf[i]) continue; if (nf[i] > limit->max) { HALDEBUG(ah, HAL_DEBUG_NFCAL, "NF[%d] (%d) > MAX (%d), correcting to MAX\n", i, nf[i], limit->max); nf[i] = limit->max; } else if (nf[i] < limit->min) { HALDEBUG(ah, HAL_DEBUG_NFCAL, "NF[%d] (%d) < MIN (%d), correcting to NOM\n", i, nf[i], limit->min); nf[i] = limit->nominal; } } } /* - * Read the NF and check it against the noise floor threshhold + * Read the NF and check it against the noise floor threshold * * Return 0 if the NF calibration hadn't finished, 0 if it was * invalid, or > 0 for a valid NF reading. */ static int16_t ar5416GetNf(struct ath_hal *ah, struct ieee80211_channel *chan) { int16_t nf, nfThresh; int i; int retval = 0; if (ar5212IsNFCalInProgress(ah)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: NF didn't complete in calibration window\n", __func__); nf = 0; retval = -1; /* NF didn't finish */ } else { /* Finished NF cal, check against threshold */ int16_t nfarray[NUM_NOISEFLOOR_READINGS] = { 0 }; HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); /* TODO - enhance for multiple chains and ext ch */ ath_hal_getNoiseFloor(ah, nfarray); nf = nfarray[0]; ar5416SanitizeNF(ah, nfarray); if (ar5416GetEepromNoiseFloorThresh(ah, chan, &nfThresh)) { if (nf > nfThresh) { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s: noise floor failed detected; " "detected %d, threshold %d\n", __func__, nf, nfThresh); /* * NB: Don't discriminate 2.4 vs 5Ghz, if this * happens it indicates a problem regardless * of the band. */ chan->ic_state |= IEEE80211_CHANSTATE_CWINT; nf = 0; retval = 0; } } else { nf = 0; retval = 0; } /* Update MIMO channel statistics, regardless of validity or not (for now) */ for (i = 0; i < 3; i++) { ichan->noiseFloorCtl[i] = nfarray[i]; ichan->noiseFloorExt[i] = nfarray[i + 3]; } ichan->privFlags |= CHANNEL_MIMO_NF_VALID; ar5416UpdateNFHistBuff(ah, AH5416(ah)->ah_cal.nfCalHist, nfarray); ichan->rawNoiseFloor = nf; retval = nf; } return retval; } Index: head/sys/dev/ath/ath_hal/ar5416/ar5416_spectral.c =================================================================== --- head/sys/dev/ath/ath_hal/ar5416/ar5416_spectral.c (revision 298938) +++ head/sys/dev/ath/ath_hal/ar5416/ar5416_spectral.c (revision 298939) @@ -1,244 +1,244 @@ /* * Copyright (c) 2012 Qualcomm Atheros, All Rights Reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ah_devid.h" #include "ah_desc.h" /* NB: for HAL_PHYERR* */ #include "ar5416/ar5416.h" #include "ar5416/ar5416reg.h" #include "ar5416/ar5416phy.h" /* * Default AR9280 spectral scan parameters */ #define AR5416_SPECTRAL_SCAN_ENA 0 #define AR5416_SPECTRAL_SCAN_ACTIVE 0 #define AR5416_SPECTRAL_SCAN_FFT_PERIOD 8 #define AR5416_SPECTRAL_SCAN_PERIOD 1 #define AR5416_SPECTRAL_SCAN_COUNT 16 //used to be 128 #define AR5416_SPECTRAL_SCAN_SHORT_REPEAT 1 /* constants */ #define MAX_RADAR_RSSI_THRESH 0x3f #define MAX_RADAR_HEIGHT 0x3f #define ENABLE_ALL_PHYERR 0xffffffff static void ar5416DisableRadar(struct ath_hal *ah); static void ar5416PrepSpectralScan(struct ath_hal *ah); static void ar5416DisableRadar(struct ath_hal *ah) { uint32_t val; // Enable radar FFT val = OS_REG_READ(ah, AR_PHY_RADAR_0); val |= AR_PHY_RADAR_0_FFT_ENA; // set radar detect thresholds to max to effectively disable radar val &= ~AR_PHY_RADAR_0_RRSSI; val |= SM(MAX_RADAR_RSSI_THRESH, AR_PHY_RADAR_0_RRSSI); val &= ~AR_PHY_RADAR_0_HEIGHT; val |= SM(MAX_RADAR_HEIGHT, AR_PHY_RADAR_0_HEIGHT); val &= ~(AR_PHY_RADAR_0_ENA); OS_REG_WRITE(ah, AR_PHY_RADAR_0, val); // disable extension radar detect val = OS_REG_READ(ah, AR_PHY_RADAR_EXT); OS_REG_WRITE(ah, AR_PHY_RADAR_EXT, val & ~AR_PHY_RADAR_EXT_ENA); val = OS_REG_READ(ah, AR_RX_FILTER); val |= (1<<13); OS_REG_WRITE(ah, AR_RX_FILTER, val); } static void ar5416PrepSpectralScan(struct ath_hal *ah) { ar5416DisableRadar(ah); OS_REG_WRITE(ah, AR_PHY_ERR, ENABLE_ALL_PHYERR); } void ar5416ConfigureSpectralScan(struct ath_hal *ah, HAL_SPECTRAL_PARAM *ss) { uint32_t val; ar5416PrepSpectralScan(ah); val = OS_REG_READ(ah, AR_PHY_SPECTRAL_SCAN); if (ss->ss_fft_period != HAL_SPECTRAL_PARAM_NOVAL) { val &= ~AR_PHY_SPECTRAL_SCAN_FFT_PERIOD; val |= SM(ss->ss_fft_period, AR_PHY_SPECTRAL_SCAN_FFT_PERIOD); } if (ss->ss_period != HAL_SPECTRAL_PARAM_NOVAL) { val &= ~AR_PHY_SPECTRAL_SCAN_PERIOD; val |= SM(ss->ss_period, AR_PHY_SPECTRAL_SCAN_PERIOD); } if (ss->ss_period != HAL_SPECTRAL_PARAM_NOVAL) { val &= ~AR_PHY_SPECTRAL_SCAN_PERIOD; val |= SM(ss->ss_period, AR_PHY_SPECTRAL_SCAN_PERIOD); } /* This section is different for Kiwi and Merlin */ if (AR_SREV_MERLIN(ah) ) { if (ss->ss_count != HAL_SPECTRAL_PARAM_NOVAL) { val &= ~AR_PHY_SPECTRAL_SCAN_COUNT; val |= SM(ss->ss_count, AR_PHY_SPECTRAL_SCAN_COUNT); } if (ss->ss_short_report == AH_TRUE) { val |= AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT; } else if (ss->ss_short_report != HAL_SPECTRAL_PARAM_NOVAL) { val &= ~AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT; } } else { if (ss->ss_count != HAL_SPECTRAL_PARAM_NOVAL) { /* - * In Merlin, for continous scan, scan_count = 128. + * In Merlin, for continuous scan, scan_count = 128. * In case of Kiwi, this value should be 0 */ if (ss->ss_count == 128) ss->ss_count = 0; val &= ~AR_PHY_SPECTRAL_SCAN_COUNT_KIWI; val |= SM(ss->ss_count, AR_PHY_SPECTRAL_SCAN_COUNT_KIWI); } if (ss->ss_short_report == AH_TRUE) { val |= AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT_KIWI; } else if (ss->ss_short_report != HAL_SPECTRAL_PARAM_NOVAL) { val &= ~AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT_KIWI; } //Select the mask to be same as before val |= AR_PHY_SPECTRAL_SCAN_PHYERR_MASK_SELECT_KIWI; } // Enable spectral scan OS_REG_WRITE(ah, AR_PHY_SPECTRAL_SCAN, val | AR_PHY_SPECTRAL_SCAN_ENA); ar5416GetSpectralParams(ah, ss); } /* * Get the spectral parameter values and return them in the pe * structure */ void ar5416GetSpectralParams(struct ath_hal *ah, HAL_SPECTRAL_PARAM *ss) { uint32_t val; val = OS_REG_READ(ah, AR_PHY_SPECTRAL_SCAN); ss->ss_fft_period = MS(val, AR_PHY_SPECTRAL_SCAN_FFT_PERIOD); ss->ss_period = MS(val, AR_PHY_SPECTRAL_SCAN_PERIOD); if (AR_SREV_MERLIN(ah) ) { ss->ss_count = MS(val, AR_PHY_SPECTRAL_SCAN_COUNT); ss->ss_short_report = MS(val, AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT); } else { ss->ss_count = MS(val, AR_PHY_SPECTRAL_SCAN_COUNT_KIWI); ss->ss_short_report = MS(val, AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT_KIWI); } val = OS_REG_READ(ah, AR_PHY_RADAR_1); ss->radar_bin_thresh_sel = MS(val, AR_PHY_RADAR_1_BIN_THRESH_SELECT); } HAL_BOOL ar5416IsSpectralActive(struct ath_hal *ah) { uint32_t val; val = OS_REG_READ(ah, AR_PHY_SPECTRAL_SCAN); return MS(val, AR_PHY_SPECTRAL_SCAN_ACTIVE); } HAL_BOOL ar5416IsSpectralEnabled(struct ath_hal *ah) { uint32_t val; val = OS_REG_READ(ah, AR_PHY_SPECTRAL_SCAN); return MS(val,AR_PHY_SPECTRAL_SCAN_ENA); } void ar5416StartSpectralScan(struct ath_hal *ah) { uint32_t val; ar5416PrepSpectralScan(ah); // Activate spectral scan val = OS_REG_READ(ah, AR_PHY_SPECTRAL_SCAN); val |= AR_PHY_SPECTRAL_SCAN_ENA; val |= AR_PHY_SPECTRAL_SCAN_ACTIVE; OS_REG_WRITE(ah, AR_PHY_SPECTRAL_SCAN, val); val = OS_REG_READ(ah, AR_PHY_SPECTRAL_SCAN); val = OS_REG_READ(ah, AR_PHY_ERR_MASK_REG); OS_REG_WRITE(ah, AR_PHY_ERR_MASK_REG, val | AR_PHY_ERR_RADAR); } void ar5416StopSpectralScan(struct ath_hal *ah) { uint32_t val; val = OS_REG_READ(ah, AR_PHY_SPECTRAL_SCAN); // Deactivate spectral scan val &= ~AR_PHY_SPECTRAL_SCAN_ENA; val &= ~AR_PHY_SPECTRAL_SCAN_ACTIVE; OS_REG_WRITE(ah, AR_PHY_SPECTRAL_SCAN, val); val = OS_REG_READ(ah, AR_PHY_SPECTRAL_SCAN); val = OS_REG_READ(ah, AR_PHY_ERR_MASK_REG) & (~AR_PHY_ERR_RADAR); OS_REG_WRITE(ah, AR_PHY_ERR_MASK_REG, val); } uint32_t ar5416GetSpectralConfig(struct ath_hal *ah) { uint32_t val; val = OS_REG_READ(ah, AR_PHY_SPECTRAL_SCAN); return val; } void ar5416RestoreSpectralConfig(struct ath_hal *ah, uint32_t restoreval) { uint32_t curval; ar5416PrepSpectralScan(ah); curval = OS_REG_READ(ah, AR_PHY_SPECTRAL_SCAN); if (restoreval != curval) { restoreval |= AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT; OS_REG_WRITE(ah, AR_PHY_SPECTRAL_SCAN, restoreval); } return; } Index: head/sys/dev/ath/ath_hal/ar5416/ar5416_xmit.c =================================================================== --- head/sys/dev/ath/ath_hal/ar5416/ar5416_xmit.c (revision 298938) +++ head/sys/dev/ath/ath_hal/ar5416/ar5416_xmit.c (revision 298939) @@ -1,1494 +1,1494 @@ /* * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * Copyright (c) 2002-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include "opt_ah.h" #include "ah.h" #include "ah_desc.h" #include "ah_internal.h" #include "ar5416/ar5416.h" #include "ar5416/ar5416reg.h" #include "ar5416/ar5416phy.h" #include "ar5416/ar5416desc.h" /* * Stop transmit on the specified queue */ HAL_BOOL ar5416StopTxDma(struct ath_hal *ah, u_int q) { #define STOP_DMA_TIMEOUT 4000 /* us */ #define STOP_DMA_ITER 100 /* us */ u_int i; HALASSERT(q < AH_PRIVATE(ah)->ah_caps.halTotalQueues); HALASSERT(AH5212(ah)->ah_txq[q].tqi_type != HAL_TX_QUEUE_INACTIVE); OS_REG_WRITE(ah, AR_Q_TXD, 1 << q); for (i = STOP_DMA_TIMEOUT/STOP_DMA_ITER; i != 0; i--) { if (ar5212NumTxPending(ah, q) == 0) break; OS_DELAY(STOP_DMA_ITER); } #ifdef AH_DEBUG if (i == 0) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: queue %u DMA did not stop in 400 msec\n", __func__, q); HALDEBUG(ah, HAL_DEBUG_ANY, "%s: QSTS 0x%x Q_TXE 0x%x Q_TXD 0x%x Q_CBR 0x%x\n", __func__, OS_REG_READ(ah, AR_QSTS(q)), OS_REG_READ(ah, AR_Q_TXE), OS_REG_READ(ah, AR_Q_TXD), OS_REG_READ(ah, AR_QCBRCFG(q))); HALDEBUG(ah, HAL_DEBUG_ANY, "%s: Q_MISC 0x%x Q_RDYTIMECFG 0x%x Q_RDYTIMESHDN 0x%x\n", __func__, OS_REG_READ(ah, AR_QMISC(q)), OS_REG_READ(ah, AR_QRDYTIMECFG(q)), OS_REG_READ(ah, AR_Q_RDYTIMESHDN)); } #endif /* AH_DEBUG */ /* ar5416 and up can kill packets at the PCU level */ if (ar5212NumTxPending(ah, q)) { uint32_t j; HALDEBUG(ah, HAL_DEBUG_TXQUEUE, "%s: Num of pending TX Frames %d on Q %d\n", __func__, ar5212NumTxPending(ah, q), q); /* Kill last PCU Tx Frame */ /* TODO - save off and restore current values of Q1/Q2? */ for (j = 0; j < 2; j++) { uint32_t tsfLow = OS_REG_READ(ah, AR_TSF_L32); OS_REG_WRITE(ah, AR_QUIET2, SM(10, AR_QUIET2_QUIET_DUR)); OS_REG_WRITE(ah, AR_QUIET_PERIOD, 100); OS_REG_WRITE(ah, AR_NEXT_QUIET, tsfLow >> 10); OS_REG_SET_BIT(ah, AR_TIMER_MODE, AR_TIMER_MODE_QUIET); if ((OS_REG_READ(ah, AR_TSF_L32)>>10) == (tsfLow>>10)) break; HALDEBUG(ah, HAL_DEBUG_ANY, "%s: TSF moved while trying to set quiet time " "TSF: 0x%08x\n", __func__, tsfLow); HALASSERT(j < 1); /* TSF shouldn't count twice or reg access is taking forever */ } OS_REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_CHAN_IDLE); /* Allow the quiet mechanism to do its work */ OS_DELAY(200); OS_REG_CLR_BIT(ah, AR_TIMER_MODE, AR_TIMER_MODE_QUIET); /* Verify the transmit q is empty */ for (i = STOP_DMA_TIMEOUT/STOP_DMA_ITER; i != 0; i--) { if (ar5212NumTxPending(ah, q) == 0) break; OS_DELAY(STOP_DMA_ITER); } if (i == 0) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: Failed to stop Tx DMA in %d msec after killing" " last frame\n", __func__, STOP_DMA_TIMEOUT / 1000); } OS_REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_CHAN_IDLE); } OS_REG_WRITE(ah, AR_Q_TXD, 0); return (i != 0); #undef STOP_DMA_ITER #undef STOP_DMA_TIMEOUT } #define VALID_KEY_TYPES \ ((1 << HAL_KEY_TYPE_CLEAR) | (1 << HAL_KEY_TYPE_WEP)|\ (1 << HAL_KEY_TYPE_AES) | (1 << HAL_KEY_TYPE_TKIP)) #define isValidKeyType(_t) ((1 << (_t)) & VALID_KEY_TYPES) #define set11nTries(_series, _index) \ (SM((_series)[_index].Tries, AR_XmitDataTries##_index)) #define set11nRate(_series, _index) \ (SM((_series)[_index].Rate, AR_XmitRate##_index)) #define set11nPktDurRTSCTS(_series, _index) \ (SM((_series)[_index].PktDuration, AR_PacketDur##_index) |\ ((_series)[_index].RateFlags & HAL_RATESERIES_RTS_CTS ?\ AR_RTSCTSQual##_index : 0)) #define set11nRateFlags(_series, _index) \ ((_series)[_index].RateFlags & HAL_RATESERIES_2040 ? AR_2040_##_index : 0) \ |((_series)[_index].RateFlags & HAL_RATESERIES_HALFGI ? AR_GI##_index : 0) \ |((_series)[_index].RateFlags & HAL_RATESERIES_STBC ? AR_STBC##_index : 0) \ |SM((_series)[_index].ChSel, AR_ChainSel##_index) /* * Descriptor Access Functions */ #define VALID_PKT_TYPES \ ((1<ah_ratesArray[rate1l]); case /* 2 Mb */ 0x1a: return (AH5416(ah)->ah_ratesArray[rate2l]); case /* 2 MbS*/ 0x1a | 0x4: return (AH5416(ah)->ah_ratesArray[rate2s]); case /* 5.5 Mb */ 0x19: return (AH5416(ah)->ah_ratesArray[rate5_5l]); case /* 5.5 MbS*/ 0x19 | 0x4: return (AH5416(ah)->ah_ratesArray[rate5_5s]); case /* 11 Mb */ 0x18: return (AH5416(ah)->ah_ratesArray[rate11l]); case /* 11 MbS*/ 0x18 | 0x4: return (AH5416(ah)->ah_ratesArray[rate11s]); } /* OFDM rates */ switch (rate) { case /* 6 Mb */ 0x0b: return (AH5416(ah)->ah_ratesArray[rate6mb]); case /* 9 Mb */ 0x0f: return (AH5416(ah)->ah_ratesArray[rate9mb]); case /* 12 Mb */ 0x0a: return (AH5416(ah)->ah_ratesArray[rate12mb]); case /* 18 Mb */ 0x0e: return (AH5416(ah)->ah_ratesArray[rate18mb]); case /* 24 Mb */ 0x09: return (AH5416(ah)->ah_ratesArray[rate24mb]); case /* 36 Mb */ 0x0d: return (AH5416(ah)->ah_ratesArray[rate36mb]); case /* 48 Mb */ 0x08: return (AH5416(ah)->ah_ratesArray[rate48mb]); case /* 54 Mb */ 0x0c: return (AH5416(ah)->ah_ratesArray[rate54mb]); } /* * Handle HT20/HT40 - we only have to do MCS0-7; * there's no stream differences. */ if ((rate & 0x80) && is_ht40) { return (AH5416(ah)->ah_ratesArray[rateHt40_0 + (rate & 0x7)]); } else if (rate & 0x80) { return (AH5416(ah)->ah_ratesArray[rateHt20_0 + (rate & 0x7)]); } /* XXX default (eg XR, bad bad person!) */ return (AH5416(ah)->ah_ratesArray[rate6mb]); } /* * Return the TX power to be used for the given rate/chains/TX power. * * There are a bunch of tweaks to make to a given TX power based on * the current configuration, so... */ static uint16_t ar5416GetTxRatePower(struct ath_hal *ah, uint8_t rate, uint8_t tx_chainmask, uint16_t txPower, HAL_BOOL is_ht40) { int n_txpower, max_txpower; const int cck_ofdm_delta = 2; #define EEP_MINOR(_ah) \ (AH_PRIVATE(_ah)->ah_eeversion & AR5416_EEP_VER_MINOR_MASK) #define IS_EEP_MINOR_V2(_ah) (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_2) /* Take a copy ; we may underflow and thus need to clamp things */ n_txpower = txPower; /* HT40? Need to adjust the TX power by this */ if (is_ht40) n_txpower += AH5416(ah)->ah_ht40PowerIncForPdadc; /* * Merlin? Offset the target TX power offset - it defaults to * starting at -5.0dBm, but that can change! * * Kiwi/Kite? Always -5.0dBm offset. */ if (AR_SREV_KIWI_10_OR_LATER(ah)) { n_txpower -= (AR5416_PWR_TABLE_OFFSET_DB * 2); } else if (AR_SREV_MERLIN_20_OR_LATER(ah)) { int8_t pwr_table_offset = 0; /* This is in dBm, convert to 1/2 dBm */ (void) ath_hal_eepromGet(ah, AR_EEP_PWR_TABLE_OFFSET, &pwr_table_offset); n_txpower -= (pwr_table_offset * 2); } /* * If Open-loop TX power control is used, the CCK rates need * to be offset by that. * * Rates: 2S, 2L, 1S, 1L, 5.5S, 5.5L * * XXX Odd, we don't have a PHY table entry for long preamble * 1mbit CCK? */ if (AR_SREV_MERLIN_20_OR_LATER(ah) && ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) { if (rate == 0x19 || rate == 0x1a || rate == 0x1b || rate == (0x19 | 0x04) || rate == (0x1a | 0x04) || rate == (0x1b | 0x04)) { n_txpower -= cck_ofdm_delta; } } /* * We're now offset by the same amount that the static maximum * PHY power tables are. So, clamp the value based on that rate. */ max_txpower = ar5416RateToRateTable(ah, rate, is_ht40); #if 0 ath_hal_printf(ah, "%s: n_txpower = %d, max_txpower = %d, " "rate = 0x%x , is_ht40 = %d\n", __func__, n_txpower, max_txpower, rate, is_ht40); #endif n_txpower = MIN(max_txpower, n_txpower); /* * We don't have to offset the TX power for two or three * chain operation here - it's done by the AR_PHY_POWER_TX_SUB * register setting via the EEPROM. * * So for vendors that programmed the maximum target power assuming * that 2/3 chains are always on, things will just plain work. * (They won't reach that target power if only one chain is on, but * that's a different problem.) */ /* Over/underflow? Adjust */ if (n_txpower < 0) n_txpower = 0; else if (n_txpower > 63) n_txpower = 63; /* * For some odd reason the AR9160 with txpower=0 results in a * much higher (max?) TX power. So, if it's a chipset before * AR9220/AR9280, just clamp the minimum value at 1. */ if ((! AR_SREV_MERLIN_10_OR_LATER(ah)) && (n_txpower == 0)) n_txpower = 1; return (n_txpower); #undef EEP_MINOR #undef IS_EEP_MINOR_V2 } HAL_BOOL ar5416SetupTxDesc(struct ath_hal *ah, struct ath_desc *ds, u_int pktLen, u_int hdrLen, HAL_PKT_TYPE type, u_int txPower, u_int txRate0, u_int txTries0, u_int keyIx, u_int antMode, u_int flags, u_int rtsctsRate, u_int rtsctsDuration, u_int compicvLen, u_int compivLen, u_int comp) { #define RTSCTS (HAL_TXDESC_RTSENA|HAL_TXDESC_CTSENA) struct ar5416_desc *ads = AR5416DESC(ds); struct ath_hal_5416 *ahp = AH5416(ah); (void) hdrLen; HALASSERT(txTries0 != 0); HALASSERT(isValidPktType(type)); HALASSERT(isValidTxRate(txRate0)); HALASSERT((flags & RTSCTS) != RTSCTS); /* XXX validate antMode */ txPower = (txPower + AH5212(ah)->ah_txPowerIndexOffset); if (txPower > 63) txPower = 63; /* * XXX For now, just assume that this isn't a HT40 frame. */ if (AH5212(ah)->ah_tpcEnabled) { txPower = ar5416GetTxRatePower(ah, txRate0, ahp->ah_tx_chainmask, txPower, AH_FALSE); } ads->ds_ctl0 = (pktLen & AR_FrameLen) | (txPower << AR_XmitPower_S) | (flags & HAL_TXDESC_VEOL ? AR_VEOL : 0) | (flags & HAL_TXDESC_CLRDMASK ? AR_ClrDestMask : 0) | (flags & HAL_TXDESC_INTREQ ? AR_TxIntrReq : 0) ; ads->ds_ctl1 = (type << AR_FrameType_S) | (flags & HAL_TXDESC_NOACK ? AR_NoAck : 0) ; ads->ds_ctl2 = SM(txTries0, AR_XmitDataTries0) | (flags & HAL_TXDESC_DURENA ? AR_DurUpdateEn : 0) ; ads->ds_ctl3 = (txRate0 << AR_XmitRate0_S) ; ads->ds_ctl4 = 0; ads->ds_ctl5 = 0; ads->ds_ctl6 = 0; ads->ds_ctl7 = SM(ahp->ah_tx_chainmask, AR_ChainSel0) | SM(ahp->ah_tx_chainmask, AR_ChainSel1) | SM(ahp->ah_tx_chainmask, AR_ChainSel2) | SM(ahp->ah_tx_chainmask, AR_ChainSel3) ; ads->ds_ctl8 = SM(0, AR_AntCtl0); ads->ds_ctl9 = SM(0, AR_AntCtl1) | SM(txPower, AR_XmitPower1); ads->ds_ctl10 = SM(0, AR_AntCtl2) | SM(txPower, AR_XmitPower2); ads->ds_ctl11 = SM(0, AR_AntCtl3) | SM(txPower, AR_XmitPower3); if (keyIx != HAL_TXKEYIX_INVALID) { /* XXX validate key index */ ads->ds_ctl1 |= SM(keyIx, AR_DestIdx); ads->ds_ctl0 |= AR_DestIdxValid; ads->ds_ctl6 |= SM(ahp->ah_keytype[keyIx], AR_EncrType); } if (flags & RTSCTS) { if (!isValidTxRate(rtsctsRate)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid rts/cts rate 0x%x\n", __func__, rtsctsRate); return AH_FALSE; } /* XXX validate rtsctsDuration */ ads->ds_ctl0 |= (flags & HAL_TXDESC_CTSENA ? AR_CTSEnable : 0) | (flags & HAL_TXDESC_RTSENA ? AR_RTSEnable : 0) ; ads->ds_ctl7 |= (rtsctsRate << AR_RTSCTSRate_S); } /* * Set the TX antenna to 0 for Kite * To preserve existing behaviour, also set the TPC bits to 0; * when TPC is enabled these should be filled in appropriately. * * XXX TODO: when doing TPC, set the TX power up appropriately? */ if (AR_SREV_KITE(ah)) { ads->ds_ctl8 = SM(0, AR_AntCtl0); ads->ds_ctl9 = SM(0, AR_AntCtl1) | SM(0, AR_XmitPower1); ads->ds_ctl10 = SM(0, AR_AntCtl2) | SM(0, AR_XmitPower2); ads->ds_ctl11 = SM(0, AR_AntCtl3) | SM(0, AR_XmitPower3); } return AH_TRUE; #undef RTSCTS } HAL_BOOL ar5416SetupXTxDesc(struct ath_hal *ah, struct ath_desc *ds, u_int txRate1, u_int txTries1, u_int txRate2, u_int txTries2, u_int txRate3, u_int txTries3) { struct ar5416_desc *ads = AR5416DESC(ds); if (txTries1) { HALASSERT(isValidTxRate(txRate1)); ads->ds_ctl2 |= SM(txTries1, AR_XmitDataTries1); ads->ds_ctl3 |= (txRate1 << AR_XmitRate1_S); } if (txTries2) { HALASSERT(isValidTxRate(txRate2)); ads->ds_ctl2 |= SM(txTries2, AR_XmitDataTries2); ads->ds_ctl3 |= (txRate2 << AR_XmitRate2_S); } if (txTries3) { HALASSERT(isValidTxRate(txRate3)); ads->ds_ctl2 |= SM(txTries3, AR_XmitDataTries3); ads->ds_ctl3 |= (txRate3 << AR_XmitRate3_S); } return AH_TRUE; } HAL_BOOL ar5416FillTxDesc(struct ath_hal *ah, struct ath_desc *ds, HAL_DMA_ADDR *bufAddrList, uint32_t *segLenList, u_int descId, u_int qcuId, HAL_BOOL firstSeg, HAL_BOOL lastSeg, const struct ath_desc *ds0) { struct ar5416_desc *ads = AR5416DESC(ds); uint32_t segLen = segLenList[0]; HALASSERT((segLen &~ AR_BufLen) == 0); ds->ds_data = bufAddrList[0]; if (firstSeg) { /* * First descriptor, don't clobber xmit control data * setup by ar5212SetupTxDesc. */ ads->ds_ctl1 |= segLen | (lastSeg ? 0 : AR_TxMore); } else if (lastSeg) { /* !firstSeg && lastSeg */ /* * Last descriptor in a multi-descriptor frame, * copy the multi-rate transmit parameters from * the first frame for processing on completion. */ ads->ds_ctl1 = segLen; #ifdef AH_NEED_DESC_SWAP ads->ds_ctl0 = __bswap32(AR5416DESC_CONST(ds0)->ds_ctl0) & AR_TxIntrReq; ads->ds_ctl2 = __bswap32(AR5416DESC_CONST(ds0)->ds_ctl2); ads->ds_ctl3 = __bswap32(AR5416DESC_CONST(ds0)->ds_ctl3); /* ctl6 - we only need encrtype; the rest are blank */ ads->ds_ctl6 = __bswap32(AR5416DESC_CONST(ds0)->ds_ctl6 & AR_EncrType); #else ads->ds_ctl0 = AR5416DESC_CONST(ds0)->ds_ctl0 & AR_TxIntrReq; ads->ds_ctl2 = AR5416DESC_CONST(ds0)->ds_ctl2; ads->ds_ctl3 = AR5416DESC_CONST(ds0)->ds_ctl3; /* ctl6 - we only need encrtype; the rest are blank */ ads->ds_ctl6 = AR5416DESC_CONST(ds0)->ds_ctl6 & AR_EncrType; #endif } else { /* !firstSeg && !lastSeg */ /* * Intermediate descriptor in a multi-descriptor frame. */ #ifdef AH_NEED_DESC_SWAP ads->ds_ctl0 = __bswap32(AR5416DESC_CONST(ds0)->ds_ctl0) & AR_TxIntrReq; ads->ds_ctl6 = __bswap32(AR5416DESC_CONST(ds0)->ds_ctl6 & AR_EncrType); #else ads->ds_ctl0 = AR5416DESC_CONST(ds0)->ds_ctl0 & AR_TxIntrReq; ads->ds_ctl6 = AR5416DESC_CONST(ds0)->ds_ctl6 & AR_EncrType; #endif ads->ds_ctl1 = segLen | AR_TxMore; ads->ds_ctl2 = 0; ads->ds_ctl3 = 0; } /* XXX only on last descriptor? */ OS_MEMZERO(ads->u.tx.status, sizeof(ads->u.tx.status)); return AH_TRUE; } /* * NB: cipher is no longer used, it's calculated. */ HAL_BOOL ar5416ChainTxDesc(struct ath_hal *ah, struct ath_desc *ds, HAL_DMA_ADDR *bufAddrList, uint32_t *segLenList, u_int pktLen, u_int hdrLen, HAL_PKT_TYPE type, u_int keyIx, HAL_CIPHER cipher, uint8_t delims, HAL_BOOL firstSeg, HAL_BOOL lastSeg, HAL_BOOL lastAggr) { struct ar5416_desc *ads = AR5416DESC(ds); uint32_t *ds_txstatus = AR5416_DS_TXSTATUS(ah,ads); struct ath_hal_5416 *ahp = AH5416(ah); u_int segLen = segLenList[0]; int isaggr = 0; uint32_t last_aggr = 0; (void) hdrLen; (void) ah; HALASSERT((segLen &~ AR_BufLen) == 0); ds->ds_data = bufAddrList[0]; HALASSERT(isValidPktType(type)); if (type == HAL_PKT_TYPE_AMPDU) { type = HAL_PKT_TYPE_NORMAL; isaggr = 1; if (lastAggr == AH_FALSE) last_aggr = AR_MoreAggr; } /* * Since this function is called before any of the other * descriptor setup functions (at least in this particular * 802.11n aggregation implementation), always bzero() the * descriptor. Previously this would be done for all but * the first segment. * XXX TODO: figure out why; perhaps I'm using this slightly * XXX incorrectly. */ OS_MEMZERO(ds->ds_hw, AR5416_DESC_TX_CTL_SZ); /* * Note: VEOL should only be for the last descriptor in the chain. */ ads->ds_ctl0 = (pktLen & AR_FrameLen); /* * For aggregates: * + IsAggr must be set for all descriptors of all subframes of * the aggregate * + MoreAggr must be set for all descriptors of all subframes * of the aggregate EXCEPT the last subframe; * + MoreAggr must be _CLEAR_ for all descrpitors of the last * subframe of the aggregate. */ ads->ds_ctl1 = (type << AR_FrameType_S) | (isaggr ? (AR_IsAggr | last_aggr) : 0); ads->ds_ctl2 = 0; ads->ds_ctl3 = 0; if (keyIx != HAL_TXKEYIX_INVALID) { /* XXX validate key index */ ads->ds_ctl1 |= SM(keyIx, AR_DestIdx); ads->ds_ctl0 |= AR_DestIdxValid; } ads->ds_ctl6 |= SM(ahp->ah_keytype[keyIx], AR_EncrType); if (isaggr) { ads->ds_ctl6 |= SM(delims, AR_PadDelim); } if (firstSeg) { ads->ds_ctl1 |= segLen | (lastSeg ? 0 : AR_TxMore); } else if (lastSeg) { /* !firstSeg && lastSeg */ ads->ds_ctl0 = 0; ads->ds_ctl1 |= segLen; } else { /* !firstSeg && !lastSeg */ /* * Intermediate descriptor in a multi-descriptor frame. */ ads->ds_ctl0 = 0; ads->ds_ctl1 |= segLen | AR_TxMore; } ds_txstatus[0] = ds_txstatus[1] = 0; ds_txstatus[9] &= ~AR_TxDone; return AH_TRUE; } HAL_BOOL ar5416SetupFirstTxDesc(struct ath_hal *ah, struct ath_desc *ds, u_int aggrLen, u_int flags, u_int txPower, u_int txRate0, u_int txTries0, u_int antMode, u_int rtsctsRate, u_int rtsctsDuration) { #define RTSCTS (HAL_TXDESC_RTSENA|HAL_TXDESC_CTSENA) struct ar5416_desc *ads = AR5416DESC(ds); struct ath_hal_5212 *ahp = AH5212(ah); HALASSERT(txTries0 != 0); HALASSERT(isValidTxRate(txRate0)); HALASSERT((flags & RTSCTS) != RTSCTS); /* XXX validate antMode */ txPower = (txPower + ahp->ah_txPowerIndexOffset ); if(txPower > 63) txPower=63; ads->ds_ctl0 |= (txPower << AR_XmitPower_S) | (flags & HAL_TXDESC_VEOL ? AR_VEOL : 0) | (flags & HAL_TXDESC_CLRDMASK ? AR_ClrDestMask : 0) | (flags & HAL_TXDESC_INTREQ ? AR_TxIntrReq : 0); ads->ds_ctl1 |= (flags & HAL_TXDESC_NOACK ? AR_NoAck : 0); ads->ds_ctl2 |= SM(txTries0, AR_XmitDataTries0); ads->ds_ctl3 |= (txRate0 << AR_XmitRate0_S); ads->ds_ctl7 = SM(AH5416(ah)->ah_tx_chainmask, AR_ChainSel0) | SM(AH5416(ah)->ah_tx_chainmask, AR_ChainSel1) | SM(AH5416(ah)->ah_tx_chainmask, AR_ChainSel2) | SM(AH5416(ah)->ah_tx_chainmask, AR_ChainSel3); /* NB: no V1 WAR */ ads->ds_ctl8 = SM(0, AR_AntCtl0); ads->ds_ctl9 = SM(0, AR_AntCtl1) | SM(txPower, AR_XmitPower1); ads->ds_ctl10 = SM(0, AR_AntCtl2) | SM(txPower, AR_XmitPower2); ads->ds_ctl11 = SM(0, AR_AntCtl3) | SM(txPower, AR_XmitPower3); ads->ds_ctl6 &= ~(0xffff); ads->ds_ctl6 |= SM(aggrLen, AR_AggrLen); if (flags & RTSCTS) { /* XXX validate rtsctsDuration */ ads->ds_ctl0 |= (flags & HAL_TXDESC_CTSENA ? AR_CTSEnable : 0) | (flags & HAL_TXDESC_RTSENA ? AR_RTSEnable : 0); } /* * Set the TX antenna to 0 for Kite * To preserve existing behaviour, also set the TPC bits to 0; * when TPC is enabled these should be filled in appropriately. */ if (AR_SREV_KITE(ah)) { ads->ds_ctl8 = SM(0, AR_AntCtl0); ads->ds_ctl9 = SM(0, AR_AntCtl1) | SM(0, AR_XmitPower1); ads->ds_ctl10 = SM(0, AR_AntCtl2) | SM(0, AR_XmitPower2); ads->ds_ctl11 = SM(0, AR_AntCtl3) | SM(0, AR_XmitPower3); } return AH_TRUE; #undef RTSCTS } HAL_BOOL ar5416SetupLastTxDesc(struct ath_hal *ah, struct ath_desc *ds, const struct ath_desc *ds0) { struct ar5416_desc *ads = AR5416DESC(ds); ads->ds_ctl1 &= ~AR_MoreAggr; ads->ds_ctl6 &= ~AR_PadDelim; /* hack to copy rate info to last desc for later processing */ #ifdef AH_NEED_DESC_SWAP ads->ds_ctl2 = __bswap32(AR5416DESC_CONST(ds0)->ds_ctl2); ads->ds_ctl3 = __bswap32(AR5416DESC_CONST(ds0)->ds_ctl3); #else ads->ds_ctl2 = AR5416DESC_CONST(ds0)->ds_ctl2; ads->ds_ctl3 = AR5416DESC_CONST(ds0)->ds_ctl3; #endif return AH_TRUE; } #ifdef AH_NEED_DESC_SWAP /* Swap transmit descriptor */ static __inline void ar5416SwapTxDesc(struct ath_desc *ds) { ds->ds_data = __bswap32(ds->ds_data); ds->ds_ctl0 = __bswap32(ds->ds_ctl0); ds->ds_ctl1 = __bswap32(ds->ds_ctl1); ds->ds_hw[0] = __bswap32(ds->ds_hw[0]); ds->ds_hw[1] = __bswap32(ds->ds_hw[1]); ds->ds_hw[2] = __bswap32(ds->ds_hw[2]); ds->ds_hw[3] = __bswap32(ds->ds_hw[3]); } #endif /* * Processing of HW TX descriptor. */ HAL_STATUS ar5416ProcTxDesc(struct ath_hal *ah, struct ath_desc *ds, struct ath_tx_status *ts) { struct ar5416_desc *ads = AR5416DESC(ds); uint32_t *ds_txstatus = AR5416_DS_TXSTATUS(ah,ads); #ifdef AH_NEED_DESC_SWAP if ((ds_txstatus[9] & __bswap32(AR_TxDone)) == 0) return HAL_EINPROGRESS; ar5416SwapTxDesc(ds); #else if ((ds_txstatus[9] & AR_TxDone) == 0) return HAL_EINPROGRESS; #endif /* Update software copies of the HW status */ ts->ts_seqnum = MS(ds_txstatus[9], AR_SeqNum); ts->ts_tstamp = AR_SendTimestamp(ds_txstatus); ts->ts_tid = MS(ds_txstatus[9], AR_TxTid); ts->ts_status = 0; if (ds_txstatus[1] & AR_ExcessiveRetries) ts->ts_status |= HAL_TXERR_XRETRY; if (ds_txstatus[1] & AR_Filtered) ts->ts_status |= HAL_TXERR_FILT; if (ds_txstatus[1] & AR_FIFOUnderrun) ts->ts_status |= HAL_TXERR_FIFO; if (ds_txstatus[9] & AR_TxOpExceeded) ts->ts_status |= HAL_TXERR_XTXOP; if (ds_txstatus[1] & AR_TxTimerExpired) ts->ts_status |= HAL_TXERR_TIMER_EXPIRED; ts->ts_flags = 0; if (ds_txstatus[0] & AR_TxBaStatus) { ts->ts_flags |= HAL_TX_BA; ts->ts_ba_low = AR_BaBitmapLow(ds_txstatus); ts->ts_ba_high = AR_BaBitmapHigh(ds_txstatus); } if (ds->ds_ctl1 & AR_IsAggr) ts->ts_flags |= HAL_TX_AGGR; if (ds_txstatus[1] & AR_DescCfgErr) ts->ts_flags |= HAL_TX_DESC_CFG_ERR; if (ds_txstatus[1] & AR_TxDataUnderrun) ts->ts_flags |= HAL_TX_DATA_UNDERRUN; if (ds_txstatus[1] & AR_TxDelimUnderrun) ts->ts_flags |= HAL_TX_DELIM_UNDERRUN; /* * Extract the transmit rate used and mark the rate as * ``alternate'' if it wasn't the series 0 rate. */ ts->ts_finaltsi = MS(ds_txstatus[9], AR_FinalTxIdx); switch (ts->ts_finaltsi) { case 0: ts->ts_rate = MS(ads->ds_ctl3, AR_XmitRate0); break; case 1: ts->ts_rate = MS(ads->ds_ctl3, AR_XmitRate1); break; case 2: ts->ts_rate = MS(ads->ds_ctl3, AR_XmitRate2); break; case 3: ts->ts_rate = MS(ads->ds_ctl3, AR_XmitRate3); break; } ts->ts_rssi = MS(ds_txstatus[5], AR_TxRSSICombined); ts->ts_rssi_ctl[0] = MS(ds_txstatus[0], AR_TxRSSIAnt00); ts->ts_rssi_ctl[1] = MS(ds_txstatus[0], AR_TxRSSIAnt01); ts->ts_rssi_ctl[2] = MS(ds_txstatus[0], AR_TxRSSIAnt02); ts->ts_rssi_ext[0] = MS(ds_txstatus[5], AR_TxRSSIAnt10); ts->ts_rssi_ext[1] = MS(ds_txstatus[5], AR_TxRSSIAnt11); ts->ts_rssi_ext[2] = MS(ds_txstatus[5], AR_TxRSSIAnt12); ts->ts_evm0 = AR_TxEVM0(ds_txstatus); ts->ts_evm1 = AR_TxEVM1(ds_txstatus); ts->ts_evm2 = AR_TxEVM2(ds_txstatus); ts->ts_shortretry = MS(ds_txstatus[1], AR_RTSFailCnt); ts->ts_longretry = MS(ds_txstatus[1], AR_DataFailCnt); /* * The retry count has the number of un-acked tries for the * final series used. When doing multi-rate retry we must * fixup the retry count by adding in the try counts for * each series that was fully-processed. Beware that this * takes values from the try counts in the final descriptor. * These are not required by the hardware. We assume they * are placed there by the driver as otherwise we have no * access and the driver can't do the calculation because it * doesn't know the descriptor format. */ switch (ts->ts_finaltsi) { case 3: ts->ts_longretry += MS(ads->ds_ctl2, AR_XmitDataTries2); case 2: ts->ts_longretry += MS(ads->ds_ctl2, AR_XmitDataTries1); case 1: ts->ts_longretry += MS(ads->ds_ctl2, AR_XmitDataTries0); } /* - * These fields are not used. Zero these to preserve compatability + * These fields are not used. Zero these to preserve compatibility * with existing drivers. */ ts->ts_virtcol = MS(ads->ds_ctl1, AR_VirtRetryCnt); ts->ts_antenna = 0; /* We don't switch antennas on Owl*/ /* handle tx trigger level changes internally */ if ((ts->ts_status & HAL_TXERR_FIFO) || (ts->ts_flags & (HAL_TX_DATA_UNDERRUN | HAL_TX_DELIM_UNDERRUN))) ar5212UpdateTxTrigLevel(ah, AH_TRUE); return HAL_OK; } HAL_BOOL ar5416SetGlobalTxTimeout(struct ath_hal *ah, u_int tu) { struct ath_hal_5416 *ahp = AH5416(ah); if (tu > 0xFFFF) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: bad global tx timeout %u\n", __func__, tu); /* restore default handling */ ahp->ah_globaltxtimeout = (u_int) -1; return AH_FALSE; } OS_REG_RMW_FIELD(ah, AR_GTXTO, AR_GTXTO_TIMEOUT_LIMIT, tu); ahp->ah_globaltxtimeout = tu; return AH_TRUE; } u_int ar5416GetGlobalTxTimeout(struct ath_hal *ah) { return MS(OS_REG_READ(ah, AR_GTXTO), AR_GTXTO_TIMEOUT_LIMIT); } #define HT_RC_2_MCS(_rc) ((_rc) & 0x0f) static const u_int8_t baDurationDelta[] = { 24, // 0: BPSK 12, // 1: QPSK 1/2 12, // 2: QPSK 3/4 4, // 3: 16-QAM 1/2 4, // 4: 16-QAM 3/4 4, // 5: 64-QAM 2/3 4, // 6: 64-QAM 3/4 4, // 7: 64-QAM 5/6 24, // 8: BPSK 12, // 9: QPSK 1/2 12, // 10: QPSK 3/4 4, // 11: 16-QAM 1/2 4, // 12: 16-QAM 3/4 4, // 13: 64-QAM 2/3 4, // 14: 64-QAM 3/4 4, // 15: 64-QAM 5/6 }; void ar5416Set11nRateScenario(struct ath_hal *ah, struct ath_desc *ds, u_int durUpdateEn, u_int rtsctsRate, HAL_11N_RATE_SERIES series[], u_int nseries, u_int flags) { struct ar5416_desc *ads = AR5416DESC(ds); uint32_t ds_ctl0; HALASSERT(nseries == 4); (void)nseries; /* * Only one of RTS and CTS enable must be set. * If a frame has both set, just do RTS protection - * that's enough to satisfy legacy protection. */ if (flags & (HAL_TXDESC_RTSENA | HAL_TXDESC_CTSENA)) { ds_ctl0 = ads->ds_ctl0; if (flags & HAL_TXDESC_RTSENA) { ds_ctl0 &= ~AR_CTSEnable; ds_ctl0 |= AR_RTSEnable; } else { ds_ctl0 &= ~AR_RTSEnable; ds_ctl0 |= AR_CTSEnable; } ads->ds_ctl0 = ds_ctl0; } else { ads->ds_ctl0 = (ads->ds_ctl0 & ~(AR_RTSEnable | AR_CTSEnable)); } ads->ds_ctl2 = set11nTries(series, 0) | set11nTries(series, 1) | set11nTries(series, 2) | set11nTries(series, 3) | (durUpdateEn ? AR_DurUpdateEn : 0); ads->ds_ctl3 = set11nRate(series, 0) | set11nRate(series, 1) | set11nRate(series, 2) | set11nRate(series, 3); ads->ds_ctl4 = set11nPktDurRTSCTS(series, 0) | set11nPktDurRTSCTS(series, 1); ads->ds_ctl5 = set11nPktDurRTSCTS(series, 2) | set11nPktDurRTSCTS(series, 3); ads->ds_ctl7 = set11nRateFlags(series, 0) | set11nRateFlags(series, 1) | set11nRateFlags(series, 2) | set11nRateFlags(series, 3) | SM(rtsctsRate, AR_RTSCTSRate); /* * Doing per-packet TPC - update the TX power for the first * field; program in the other series. */ if (AH5212(ah)->ah_tpcEnabled) { uint32_t ds_ctl0; uint16_t txPower; /* Modify the tx power field for rate 0 */ txPower = ar5416GetTxRatePower(ah, series[0].Rate, series[0].ChSel, series[0].tx_power_cap, !! (series[0].RateFlags & HAL_RATESERIES_2040)); ds_ctl0 = ads->ds_ctl0 & ~AR_XmitPower; ds_ctl0 |= (txPower << AR_XmitPower_S); ads->ds_ctl0 = ds_ctl0; /* * Override the whole descriptor field for each TX power. * * This will need changing if we ever support antenna control * programming. */ txPower = ar5416GetTxRatePower(ah, series[1].Rate, series[1].ChSel, series[1].tx_power_cap, !! (series[1].RateFlags & HAL_RATESERIES_2040)); ads->ds_ctl9 = SM(0, AR_AntCtl1) | SM(txPower, AR_XmitPower1); txPower = ar5416GetTxRatePower(ah, series[2].Rate, series[2].ChSel, series[2].tx_power_cap, !! (series[2].RateFlags & HAL_RATESERIES_2040)); ads->ds_ctl10 = SM(0, AR_AntCtl2) | SM(txPower, AR_XmitPower2); txPower = ar5416GetTxRatePower(ah, series[3].Rate, series[3].ChSel, series[3].tx_power_cap, !! (series[3].RateFlags & HAL_RATESERIES_2040)); ads->ds_ctl11 = SM(0, AR_AntCtl3) | SM(txPower, AR_XmitPower3); } } /* * Note: this should be called before calling ar5416SetBurstDuration() * (if it is indeed called) in order to ensure that the burst duration * is correctly updated with the BA delta workaround. */ void ar5416Set11nAggrFirst(struct ath_hal *ah, struct ath_desc *ds, u_int aggrLen, u_int numDelims) { struct ar5416_desc *ads = AR5416DESC(ds); uint32_t flags; uint32_t burstDur; uint8_t rate; ads->ds_ctl1 |= (AR_IsAggr | AR_MoreAggr); ads->ds_ctl6 &= ~(AR_AggrLen | AR_PadDelim); ads->ds_ctl6 |= SM(aggrLen, AR_AggrLen); ads->ds_ctl6 |= SM(numDelims, AR_PadDelim); if (! AR_SREV_MERLIN_10_OR_LATER(ah)) { /* * XXX It'd be nice if I were passed in the rate scenario * at this point.. */ rate = MS(ads->ds_ctl3, AR_XmitRate0); flags = ads->ds_ctl0 & (AR_CTSEnable | AR_RTSEnable); /* * WAR - MAC assumes normal ACK time instead of * block ACK while computing packet duration. * Add this delta to the burst duration in the descriptor. */ if (flags && (ads->ds_ctl1 & AR_IsAggr)) { burstDur = baDurationDelta[HT_RC_2_MCS(rate)]; ads->ds_ctl2 &= ~(AR_BurstDur); ads->ds_ctl2 |= SM(burstDur, AR_BurstDur); } } } void ar5416Set11nAggrMiddle(struct ath_hal *ah, struct ath_desc *ds, u_int numDelims) { struct ar5416_desc *ads = AR5416DESC(ds); uint32_t *ds_txstatus = AR5416_DS_TXSTATUS(ah,ads); ads->ds_ctl1 |= (AR_IsAggr | AR_MoreAggr); ads->ds_ctl6 &= ~AR_PadDelim; ads->ds_ctl6 |= SM(numDelims, AR_PadDelim); ads->ds_ctl6 &= ~AR_AggrLen; /* * Clear the TxDone status here, may need to change * func name to reflect this */ ds_txstatus[9] &= ~AR_TxDone; } void ar5416Set11nAggrLast(struct ath_hal *ah, struct ath_desc *ds) { struct ar5416_desc *ads = AR5416DESC(ds); ads->ds_ctl1 |= AR_IsAggr; ads->ds_ctl1 &= ~AR_MoreAggr; ads->ds_ctl6 &= ~AR_PadDelim; } void ar5416Clr11nAggr(struct ath_hal *ah, struct ath_desc *ds) { struct ar5416_desc *ads = AR5416DESC(ds); ads->ds_ctl1 &= (~AR_IsAggr & ~AR_MoreAggr); ads->ds_ctl6 &= ~AR_PadDelim; ads->ds_ctl6 &= ~AR_AggrLen; } void ar5416Set11nVirtualMoreFrag(struct ath_hal *ah, struct ath_desc *ds, u_int vmf) { struct ar5416_desc *ads = AR5416DESC(ds); if (vmf) ads->ds_ctl0 |= AR_VirtMoreFrag; else ads->ds_ctl0 &= ~AR_VirtMoreFrag; } /* * Program the burst duration, with the included BA delta if it's * applicable. */ void ar5416Set11nBurstDuration(struct ath_hal *ah, struct ath_desc *ds, u_int burstDuration) { struct ar5416_desc *ads = AR5416DESC(ds); uint32_t burstDur = 0; uint8_t rate; if (! AR_SREV_MERLIN_10_OR_LATER(ah)) { /* * XXX It'd be nice if I were passed in the rate scenario * at this point.. */ rate = MS(ads->ds_ctl3, AR_XmitDataTries0); /* * WAR - MAC assumes normal ACK time instead of * block ACK while computing packet duration. * Add this delta to the burst duration in the descriptor. */ if (ads->ds_ctl1 & AR_IsAggr) { burstDur = baDurationDelta[HT_RC_2_MCS(rate)]; } } ads->ds_ctl2 &= ~AR_BurstDur; ads->ds_ctl2 |= SM(burstDur + burstDuration, AR_BurstDur); } /* * Retrieve the rate table from the given TX completion descriptor */ HAL_BOOL ar5416GetTxCompletionRates(struct ath_hal *ah, const struct ath_desc *ds0, int *rates, int *tries) { const struct ar5416_desc *ads = AR5416DESC_CONST(ds0); rates[0] = MS(ads->ds_ctl3, AR_XmitRate0); rates[1] = MS(ads->ds_ctl3, AR_XmitRate1); rates[2] = MS(ads->ds_ctl3, AR_XmitRate2); rates[3] = MS(ads->ds_ctl3, AR_XmitRate3); tries[0] = MS(ads->ds_ctl2, AR_XmitDataTries0); tries[1] = MS(ads->ds_ctl2, AR_XmitDataTries1); tries[2] = MS(ads->ds_ctl2, AR_XmitDataTries2); tries[3] = MS(ads->ds_ctl2, AR_XmitDataTries3); return AH_TRUE; } /* * TX queue management routines - AR5416 and later chipsets */ /* * Allocate and initialize a tx DCU/QCU combination. */ int ar5416SetupTxQueue(struct ath_hal *ah, HAL_TX_QUEUE type, const HAL_TXQ_INFO *qInfo) { struct ath_hal_5212 *ahp = AH5212(ah); HAL_TX_QUEUE_INFO *qi; HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps; int q, defqflags; /* by default enable OK+ERR+DESC+URN interrupts */ defqflags = HAL_TXQ_TXOKINT_ENABLE | HAL_TXQ_TXERRINT_ENABLE | HAL_TXQ_TXDESCINT_ENABLE | HAL_TXQ_TXURNINT_ENABLE; /* XXX move queue assignment to driver */ switch (type) { case HAL_TX_QUEUE_BEACON: q = pCap->halTotalQueues-1; /* highest priority */ defqflags |= HAL_TXQ_DBA_GATED | HAL_TXQ_CBR_DIS_QEMPTY | HAL_TXQ_ARB_LOCKOUT_GLOBAL | HAL_TXQ_BACKOFF_DISABLE; break; case HAL_TX_QUEUE_CAB: q = pCap->halTotalQueues-2; /* next highest priority */ defqflags |= HAL_TXQ_DBA_GATED | HAL_TXQ_CBR_DIS_QEMPTY | HAL_TXQ_CBR_DIS_BEMPTY | HAL_TXQ_ARB_LOCKOUT_GLOBAL | HAL_TXQ_BACKOFF_DISABLE; break; case HAL_TX_QUEUE_PSPOLL: q = 1; /* lowest priority */ defqflags |= HAL_TXQ_DBA_GATED | HAL_TXQ_CBR_DIS_QEMPTY | HAL_TXQ_CBR_DIS_BEMPTY | HAL_TXQ_ARB_LOCKOUT_GLOBAL | HAL_TXQ_BACKOFF_DISABLE; break; case HAL_TX_QUEUE_UAPSD: q = pCap->halTotalQueues-3; /* nextest highest priority */ if (ahp->ah_txq[q].tqi_type != HAL_TX_QUEUE_INACTIVE) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: no available UAPSD tx queue\n", __func__); return -1; } break; case HAL_TX_QUEUE_DATA: for (q = 0; q < pCap->halTotalQueues; q++) if (ahp->ah_txq[q].tqi_type == HAL_TX_QUEUE_INACTIVE) break; if (q == pCap->halTotalQueues) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: no available tx queue\n", __func__); return -1; } break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: bad tx queue type %u\n", __func__, type); return -1; } HALDEBUG(ah, HAL_DEBUG_TXQUEUE, "%s: queue %u\n", __func__, q); qi = &ahp->ah_txq[q]; if (qi->tqi_type != HAL_TX_QUEUE_INACTIVE) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: tx queue %u already active\n", __func__, q); return -1; } OS_MEMZERO(qi, sizeof(HAL_TX_QUEUE_INFO)); qi->tqi_type = type; if (qInfo == AH_NULL) { qi->tqi_qflags = defqflags; qi->tqi_aifs = INIT_AIFS; qi->tqi_cwmin = HAL_TXQ_USEDEFAULT; /* NB: do at reset */ qi->tqi_cwmax = INIT_CWMAX; qi->tqi_shretry = INIT_SH_RETRY; qi->tqi_lgretry = INIT_LG_RETRY; qi->tqi_physCompBuf = 0; } else { qi->tqi_physCompBuf = qInfo->tqi_compBuf; (void) ar5212SetTxQueueProps(ah, q, qInfo); } /* NB: must be followed by ar5212ResetTxQueue */ return q; } /* * Update the h/w interrupt registers to reflect a tx q's configuration. */ static void setTxQInterrupts(struct ath_hal *ah, HAL_TX_QUEUE_INFO *qi) { struct ath_hal_5212 *ahp = AH5212(ah); HALDEBUG(ah, HAL_DEBUG_TXQUEUE, "%s: tx ok 0x%x err 0x%x desc 0x%x eol 0x%x urn 0x%x\n", __func__, ahp->ah_txOkInterruptMask, ahp->ah_txErrInterruptMask, ahp->ah_txDescInterruptMask, ahp->ah_txEolInterruptMask, ahp->ah_txUrnInterruptMask); OS_REG_WRITE(ah, AR_IMR_S0, SM(ahp->ah_txOkInterruptMask, AR_IMR_S0_QCU_TXOK) | SM(ahp->ah_txDescInterruptMask, AR_IMR_S0_QCU_TXDESC) ); OS_REG_WRITE(ah, AR_IMR_S1, SM(ahp->ah_txErrInterruptMask, AR_IMR_S1_QCU_TXERR) | SM(ahp->ah_txEolInterruptMask, AR_IMR_S1_QCU_TXEOL) ); OS_REG_RMW_FIELD(ah, AR_IMR_S2, AR_IMR_S2_QCU_TXURN, ahp->ah_txUrnInterruptMask); } /* * Set the retry, aifs, cwmin/max, readyTime regs for specified queue * Assumes: * phwChannel has been set to point to the current channel */ #define TU_TO_USEC(_tu) ((_tu) << 10) HAL_BOOL ar5416ResetTxQueue(struct ath_hal *ah, u_int q) { struct ath_hal_5212 *ahp = AH5212(ah); HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps; const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan; HAL_TX_QUEUE_INFO *qi; uint32_t cwMin, chanCwMin, qmisc, dmisc; if (q >= pCap->halTotalQueues) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid queue num %u\n", __func__, q); return AH_FALSE; } qi = &ahp->ah_txq[q]; if (qi->tqi_type == HAL_TX_QUEUE_INACTIVE) { HALDEBUG(ah, HAL_DEBUG_TXQUEUE, "%s: inactive queue %u\n", __func__, q); return AH_TRUE; /* XXX??? */ } HALDEBUG(ah, HAL_DEBUG_TXQUEUE, "%s: reset queue %u\n", __func__, q); if (qi->tqi_cwmin == HAL_TXQ_USEDEFAULT) { /* * Select cwmin according to channel type. * NB: chan can be NULL during attach */ if (chan && IEEE80211_IS_CHAN_B(chan)) chanCwMin = INIT_CWMIN_11B; else chanCwMin = INIT_CWMIN; /* make sure that the CWmin is of the form (2^n - 1) */ for (cwMin = 1; cwMin < chanCwMin; cwMin = (cwMin << 1) | 1) ; } else cwMin = qi->tqi_cwmin; /* set cwMin/Max and AIFS values */ OS_REG_WRITE(ah, AR_DLCL_IFS(q), SM(cwMin, AR_D_LCL_IFS_CWMIN) | SM(qi->tqi_cwmax, AR_D_LCL_IFS_CWMAX) | SM(qi->tqi_aifs, AR_D_LCL_IFS_AIFS)); /* Set retry limit values */ OS_REG_WRITE(ah, AR_DRETRY_LIMIT(q), SM(INIT_SSH_RETRY, AR_D_RETRY_LIMIT_STA_SH) | SM(INIT_SLG_RETRY, AR_D_RETRY_LIMIT_STA_LG) | SM(qi->tqi_lgretry, AR_D_RETRY_LIMIT_FR_LG) | SM(qi->tqi_shretry, AR_D_RETRY_LIMIT_FR_SH) ); /* NB: always enable early termination on the QCU */ qmisc = AR_Q_MISC_DCU_EARLY_TERM_REQ | SM(AR_Q_MISC_FSP_ASAP, AR_Q_MISC_FSP); /* NB: always enable DCU to wait for next fragment from QCU */ dmisc = AR_D_MISC_FRAG_WAIT_EN; /* Enable exponential backoff window */ dmisc |= AR_D_MISC_BKOFF_PERSISTENCE; /* * The chip reset default is to use a DCU backoff threshold of 0x2. * Restore this when programming the DCU MISC register. */ dmisc |= 0x2; /* multiqueue support */ if (qi->tqi_cbrPeriod) { OS_REG_WRITE(ah, AR_QCBRCFG(q), SM(qi->tqi_cbrPeriod,AR_Q_CBRCFG_CBR_INTERVAL) | SM(qi->tqi_cbrOverflowLimit, AR_Q_CBRCFG_CBR_OVF_THRESH)); qmisc = (qmisc &~ AR_Q_MISC_FSP) | AR_Q_MISC_FSP_CBR; if (qi->tqi_cbrOverflowLimit) qmisc |= AR_Q_MISC_CBR_EXP_CNTR_LIMIT; } if (qi->tqi_readyTime && (qi->tqi_type != HAL_TX_QUEUE_CAB)) { OS_REG_WRITE(ah, AR_QRDYTIMECFG(q), SM(qi->tqi_readyTime, AR_Q_RDYTIMECFG_INT) | AR_Q_RDYTIMECFG_ENA); } OS_REG_WRITE(ah, AR_DCHNTIME(q), SM(qi->tqi_burstTime, AR_D_CHNTIME_DUR) | (qi->tqi_burstTime ? AR_D_CHNTIME_EN : 0)); if (qi->tqi_readyTime && (qi->tqi_qflags & HAL_TXQ_RDYTIME_EXP_POLICY_ENABLE)) qmisc |= AR_Q_MISC_RDYTIME_EXP_POLICY; if (qi->tqi_qflags & HAL_TXQ_DBA_GATED) qmisc = (qmisc &~ AR_Q_MISC_FSP) | AR_Q_MISC_FSP_DBA_GATED; if (MS(qmisc, AR_Q_MISC_FSP) != AR_Q_MISC_FSP_ASAP) { /* * These are meangingful only when not scheduled asap. */ if (qi->tqi_qflags & HAL_TXQ_CBR_DIS_BEMPTY) qmisc |= AR_Q_MISC_CBR_INCR_DIS0; else qmisc &= ~AR_Q_MISC_CBR_INCR_DIS0; if (qi->tqi_qflags & HAL_TXQ_CBR_DIS_QEMPTY) qmisc |= AR_Q_MISC_CBR_INCR_DIS1; else qmisc &= ~AR_Q_MISC_CBR_INCR_DIS1; } if (qi->tqi_qflags & HAL_TXQ_BACKOFF_DISABLE) dmisc |= AR_D_MISC_POST_FR_BKOFF_DIS; if (qi->tqi_qflags & HAL_TXQ_FRAG_BURST_BACKOFF_ENABLE) dmisc |= AR_D_MISC_FRAG_BKOFF_EN; if (qi->tqi_qflags & HAL_TXQ_ARB_LOCKOUT_GLOBAL) dmisc |= SM(AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL, AR_D_MISC_ARB_LOCKOUT_CNTRL); else if (qi->tqi_qflags & HAL_TXQ_ARB_LOCKOUT_INTRA) dmisc |= SM(AR_D_MISC_ARB_LOCKOUT_CNTRL_INTRA_FR, AR_D_MISC_ARB_LOCKOUT_CNTRL); if (qi->tqi_qflags & HAL_TXQ_IGNORE_VIRTCOL) dmisc |= SM(AR_D_MISC_VIR_COL_HANDLING_IGNORE, AR_D_MISC_VIR_COL_HANDLING); if (qi->tqi_qflags & HAL_TXQ_SEQNUM_INC_DIS) dmisc |= AR_D_MISC_SEQ_NUM_INCR_DIS; /* * Fillin type-dependent bits. Most of this can be * removed by specifying the queue parameters in the * driver; it's here for backwards compatibility. */ switch (qi->tqi_type) { case HAL_TX_QUEUE_BEACON: /* beacon frames */ qmisc |= AR_Q_MISC_FSP_DBA_GATED | AR_Q_MISC_BEACON_USE | AR_Q_MISC_CBR_INCR_DIS1; dmisc |= SM(AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL, AR_D_MISC_ARB_LOCKOUT_CNTRL) | AR_D_MISC_BEACON_USE | AR_D_MISC_POST_FR_BKOFF_DIS; break; case HAL_TX_QUEUE_CAB: /* CAB frames */ /* * No longer Enable AR_Q_MISC_RDYTIME_EXP_POLICY, * There is an issue with the CAB Queue * not properly refreshing the Tx descriptor if * the TXE clear setting is used. */ qmisc |= AR_Q_MISC_FSP_DBA_GATED | AR_Q_MISC_CBR_INCR_DIS1 | AR_Q_MISC_CBR_INCR_DIS0; HALDEBUG(ah, HAL_DEBUG_TXQUEUE, "%s: CAB: tqi_readyTime = %d\n", __func__, qi->tqi_readyTime); if (qi->tqi_readyTime) { HALDEBUG(ah, HAL_DEBUG_TXQUEUE, "%s: using tqi_readyTime\n", __func__); OS_REG_WRITE(ah, AR_QRDYTIMECFG(q), SM(qi->tqi_readyTime, AR_Q_RDYTIMECFG_INT) | AR_Q_RDYTIMECFG_ENA); } else { int value; /* * NB: don't set default ready time if driver * has explicitly specified something. This is * here solely for backwards compatibility. */ /* * XXX for now, hard-code a CAB interval of 70% * XXX of the total beacon interval. * * XXX This keeps Merlin and later based MACs * XXX quite a bit happier (stops stuck beacons, * XXX which I gather is because of such a long * XXX cabq time.) */ value = (ahp->ah_beaconInterval * 50 / 100) - ah->ah_config.ah_additional_swba_backoff - ah->ah_config.ah_sw_beacon_response_time + ah->ah_config.ah_dma_beacon_response_time; /* * XXX Ensure it isn't too low - nothing lower * XXX than 10 TU */ if (value < 10) value = 10; HALDEBUG(ah, HAL_DEBUG_TXQUEUE, "%s: defaulting to rdytime = %d uS\n", __func__, value); OS_REG_WRITE(ah, AR_QRDYTIMECFG(q), SM(TU_TO_USEC(value), AR_Q_RDYTIMECFG_INT) | AR_Q_RDYTIMECFG_ENA); } dmisc |= SM(AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL, AR_D_MISC_ARB_LOCKOUT_CNTRL); break; case HAL_TX_QUEUE_PSPOLL: qmisc |= AR_Q_MISC_CBR_INCR_DIS1; break; case HAL_TX_QUEUE_UAPSD: dmisc |= AR_D_MISC_POST_FR_BKOFF_DIS; break; default: /* NB: silence compiler */ break; } OS_REG_WRITE(ah, AR_QMISC(q), qmisc); OS_REG_WRITE(ah, AR_DMISC(q), dmisc); /* Setup compression scratchpad buffer */ /* * XXX: calling this asynchronously to queue operation can * cause unexpected behavior!!! */ if (qi->tqi_physCompBuf) { HALASSERT(qi->tqi_type == HAL_TX_QUEUE_DATA || qi->tqi_type == HAL_TX_QUEUE_UAPSD); OS_REG_WRITE(ah, AR_Q_CBBS, (80 + 2*q)); OS_REG_WRITE(ah, AR_Q_CBBA, qi->tqi_physCompBuf); OS_REG_WRITE(ah, AR_Q_CBC, HAL_COMP_BUF_MAX_SIZE/1024); OS_REG_WRITE(ah, AR_Q0_MISC + 4*q, OS_REG_READ(ah, AR_Q0_MISC + 4*q) | AR_Q_MISC_QCU_COMP_EN); } /* * Always update the secondary interrupt mask registers - this * could be a new queue getting enabled in a running system or * hw getting re-initialized during a reset! * * Since we don't differentiate between tx interrupts corresponding * to individual queues - secondary tx mask regs are always unmasked; * tx interrupts are enabled/disabled for all queues collectively * using the primary mask reg */ if (qi->tqi_qflags & HAL_TXQ_TXOKINT_ENABLE) ahp->ah_txOkInterruptMask |= 1 << q; else ahp->ah_txOkInterruptMask &= ~(1 << q); if (qi->tqi_qflags & HAL_TXQ_TXERRINT_ENABLE) ahp->ah_txErrInterruptMask |= 1 << q; else ahp->ah_txErrInterruptMask &= ~(1 << q); if (qi->tqi_qflags & HAL_TXQ_TXDESCINT_ENABLE) ahp->ah_txDescInterruptMask |= 1 << q; else ahp->ah_txDescInterruptMask &= ~(1 << q); if (qi->tqi_qflags & HAL_TXQ_TXEOLINT_ENABLE) ahp->ah_txEolInterruptMask |= 1 << q; else ahp->ah_txEolInterruptMask &= ~(1 << q); if (qi->tqi_qflags & HAL_TXQ_TXURNINT_ENABLE) ahp->ah_txUrnInterruptMask |= 1 << q; else ahp->ah_txUrnInterruptMask &= ~(1 << q); setTxQInterrupts(ah, qi); return AH_TRUE; } #undef TU_TO_USEC Index: head/sys/dev/ath/ath_hal/ar5416/ar5416reg.h =================================================================== --- head/sys/dev/ath/ath_hal/ar5416/ar5416reg.h (revision 298938) +++ head/sys/dev/ath/ath_hal/ar5416/ar5416reg.h (revision 298939) @@ -1,813 +1,813 @@ /* * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * Copyright (c) 2002-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #ifndef _DEV_ATH_AR5416REG_H #define _DEV_ATH_AR5416REG_H #include /* * Register added starting with the AR5416 */ #define AR_MIRT 0x0020 /* interrupt rate threshold */ #define AR_TIMT 0x0028 /* Tx Interrupt mitigation threshold */ #define AR_RIMT 0x002C /* Rx Interrupt mitigation threshold */ #define AR_GTXTO 0x0064 /* global transmit timeout */ #define AR_GTTM 0x0068 /* global transmit timeout mode */ #define AR_CST 0x006C /* carrier sense timeout */ #define AR_MAC_LED 0x1f04 /* LED control */ #define AR_WA 0x4004 /* PCIE work-arounds */ #define AR_PCIE_PM_CTRL 0x4014 #define AR_AHB_MODE 0x4024 /* AHB mode for dma */ #define AR_INTR_SYNC_CAUSE_CLR 0x4028 /* clear interrupt */ #define AR_INTR_SYNC_CAUSE 0x4028 /* check pending interrupts */ #define AR_INTR_SYNC_ENABLE 0x402c /* enable interrupts */ #define AR_INTR_ASYNC_MASK 0x4030 /* asynchronous interrupt mask */ #define AR_INTR_SYNC_MASK 0x4034 /* synchronous interrupt mask */ #define AR_INTR_ASYNC_CAUSE 0x4038 /* check pending interrupts */ #define AR_INTR_ASYNC_CAUSE_CLR 0x4038 /* clear pending interrupts */ #define AR_INTR_ASYNC_ENABLE 0x403c /* enable interrupts */ #define AR5416_PCIE_SERDES 0x4040 #define AR5416_PCIE_SERDES2 0x4044 #define AR_GPIO_IN_OUT 0x4048 /* GPIO input/output register */ #define AR_GPIO_OE_OUT 0x404c /* GPIO output enable register */ #define AR_GPIO_INTR_POL 0x4050 /* GPIO interrupt polarity */ #define AR_GPIO_INPUT_EN_VAL 0x4054 /* GPIO input enable and value */ #define AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_DEF 0x00000004 #define AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_S 2 #define AR_GPIO_INPUT_EN_VAL_BT_FREQUENCY_DEF 0x00000008 #define AR_GPIO_INPUT_EN_VAL_BT_FREQUENCY_S 3 #define AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_DEF 0x00000010 #define AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_S 4 #define AR_GPIO_INPUT_EN_VAL_RFSILENT_DEF 0x00000080 #define AR_GPIO_INPUT_EN_VAL_RFSILENT_DEF_S 7 #define AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_BB 0x00000400 #define AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_BB_S 10 #define AR_GPIO_INPUT_EN_VAL_BT_FREQUENCY_BB 0x00000800 #define AR_GPIO_INPUT_EN_VAL_BT_FREQUENCY_BB_S 11 #define AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB 0x00001000 #define AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB_S 12 #define AR_GPIO_INPUT_EN_VAL_RFSILENT_BB 0x00008000 #define AR_GPIO_INPUT_EN_VAL_RFSILENT_BB_S 15 #define AR_GPIO_RTC_RESET_OVERRIDE_ENABLE 0x00010000 #define AR_GPIO_JTAG_DISABLE 0x00020000 #define AR_GPIO_INPUT_MUX1 0x4058 #define AR_GPIO_INPUT_MUX1_BT_PRIORITY 0x00000f00 #define AR_GPIO_INPUT_MUX1_BT_PRIORITY_S 8 #define AR_GPIO_INPUT_MUX1_BT_FREQUENCY 0x0000f000 #define AR_GPIO_INPUT_MUX1_BT_FREQUENCY_S 12 #define AR_GPIO_INPUT_MUX1_BT_ACTIVE 0x000f0000 #define AR_GPIO_INPUT_MUX1_BT_ACTIVE_S 16 #define AR_GPIO_INPUT_MUX2 0x405c #define AR_GPIO_INPUT_MUX2_CLK25 0x0000000f #define AR_GPIO_INPUT_MUX2_CLK25_S 0 #define AR_GPIO_INPUT_MUX2_RFSILENT 0x000000f0 #define AR_GPIO_INPUT_MUX2_RFSILENT_S 4 #define AR_GPIO_INPUT_MUX2_RTC_RESET 0x00000f00 #define AR_GPIO_INPUT_MUX2_RTC_RESET_S 8 #define AR_GPIO_OUTPUT_MUX1 0x4060 #define AR_GPIO_OUTPUT_MUX2 0x4064 #define AR_GPIO_OUTPUT_MUX3 0x4068 #define AR_GPIO_OUTPUT_MUX_AS_OUTPUT 0 #define AR_GPIO_OUTPUT_MUX_AS_PCIE_ATTENTION_LED 1 #define AR_GPIO_OUTPUT_MUX_AS_PCIE_POWER_LED 2 #define AR_GPIO_OUTPUT_MUX_AS_TX_FRAME 3 #define AR_GPIO_OUTPUT_MUX_AS_RX_CLEAR_EXTERNAL 4 #define AR_GPIO_OUTPUT_MUX_AS_MAC_NETWORK_LED 5 #define AR_GPIO_OUTPUT_MUX_AS_MAC_POWER_LED 6 #define AR_EEPROM_STATUS_DATA 0x407c #define AR_OBS 0x4080 #define AR_GPIO_PDPU 0x4088 #ifdef AH_SUPPORT_AR9130 #define AR_RTC_BASE 0x20000 #else #define AR_RTC_BASE 0x7000 #endif /* AH_SUPPORT_AR9130 */ #define AR_RTC_RC AR_RTC_BASE + 0x00 /* reset control */ #define AR_RTC_PLL_CONTROL AR_RTC_BASE + 0x14 #define AR_RTC_RESET AR_RTC_BASE + 0x40 /* RTC reset register */ #define AR_RTC_STATUS AR_RTC_BASE + 0x44 /* system sleep status */ #define AR_RTC_SLEEP_CLK AR_RTC_BASE + 0x48 #define AR_RTC_FORCE_WAKE AR_RTC_BASE + 0x4c /* control MAC force wake */ #define AR_RTC_INTR_CAUSE AR_RTC_BASE + 0x50 /* RTC interrupt cause/clear */ #define AR_RTC_INTR_ENABLE AR_RTC_BASE + 0x54 /* RTC interrupt enable */ #define AR_RTC_INTR_MASK AR_RTC_BASE + 0x58 /* RTC interrupt mask */ #ifdef AH_SUPPORT_AR9130 /* RTC_DERIVED_* - only for AR9130 */ #define AR_RTC_DERIVED_CLK (AR_RTC_BASE + 0x0038) #define AR_RTC_DERIVED_CLK_PERIOD 0x0000fffe #define AR_RTC_DERIVED_CLK_PERIOD_S 1 #endif /* AH_SUPPORT_AR9130 */ /* AR_USEC: 0x801c */ #define AR5416_USEC_TX_LAT 0x007FC000 /* tx latency to start of SIGNAL (usec) */ #define AR5416_USEC_TX_LAT_S 14 /* tx latency to start of SIGNAL (usec) */ #define AR5416_USEC_RX_LAT 0x1F800000 /* rx latency to start of SIGNAL (usec) */ #define AR5416_USEC_RX_LAT_S 23 /* rx latency to start of SIGNAL (usec) */ #define AR_RESET_TSF 0x8020 /* * AR_SLEEP1 / AR_SLEEP2 are in the same place as in * AR5212, however the fields have changed. */ #define AR5416_SLEEP1 0x80d4 #define AR5416_SLEEP2 0x80d8 #define AR_RXFIFO_CFG 0x8114 #define AR_PHY_ERR_1 0x812c #define AR_PHY_ERR_MASK_1 0x8130 /* mask for AR_PHY_ERR_1 */ #define AR_PHY_ERR_2 0x8134 #define AR_PHY_ERR_MASK_2 0x8138 /* mask for AR_PHY_ERR_2 */ #define AR_TSFOOR_THRESHOLD 0x813c #define AR_PHY_ERR_3 0x8168 #define AR_PHY_ERR_MASK_3 0x816c /* mask for AR_PHY_ERR_3 */ #define AR_BT_COEX_WEIGHT2 0x81c4 #define AR_TXOP_X 0x81ec /* txop for legacy non-qos */ #define AR_TXOP_0_3 0x81f0 /* txop for various tid's */ #define AR_TXOP_4_7 0x81f4 #define AR_TXOP_8_11 0x81f8 #define AR_TXOP_12_15 0x81fc /* generic timers based on tsf - all uS */ #define AR_NEXT_TBTT 0x8200 #define AR_NEXT_DBA 0x8204 #define AR_NEXT_SWBA 0x8208 #define AR_NEXT_CFP 0x8208 #define AR_NEXT_HCF 0x820C #define AR_NEXT_TIM 0x8210 #define AR_NEXT_DTIM 0x8214 #define AR_NEXT_QUIET 0x8218 #define AR_NEXT_NDP 0x821C #define AR5416_BEACON_PERIOD 0x8220 #define AR_DBA_PERIOD 0x8224 #define AR_SWBA_PERIOD 0x8228 #define AR_HCF_PERIOD 0x822C #define AR_TIM_PERIOD 0x8230 #define AR_DTIM_PERIOD 0x8234 #define AR_QUIET_PERIOD 0x8238 #define AR_NDP_PERIOD 0x823C #define AR_TIMER_MODE 0x8240 #define AR_SLP32_MODE 0x8244 #define AR_SLP32_WAKE 0x8248 #define AR_SLP32_INC 0x824c #define AR_SLP_CNT 0x8250 /* 32kHz cycles with mac asleep */ #define AR_SLP_CYCLE_CNT 0x8254 /* absolute number of 32kHz cycles */ #define AR_SLP_MIB_CTRL 0x8258 #define AR_2040_MODE 0x8318 #define AR_EXTRCCNT 0x8328 /* extension channel rx clear count */ #define AR_SELFGEN_MASK 0x832c /* rx and cal chain masks */ #define AR_PHY_ERR_MASK_REG 0x8338 #define AR_PCU_TXBUF_CTRL 0x8340 #define AR_PCU_MISC_MODE2 0x8344 /* DMA & PCI Registers in PCI space (usable during sleep)*/ #define AR_RC_AHB 0x00000001 /* AHB reset */ #define AR_RC_APB 0x00000002 /* APB reset */ #define AR_RC_HOSTIF 0x00000100 /* host interface reset */ #define AR_MIRT_VAL 0x0000ffff /* in uS */ #define AR_MIRT_VAL_S 16 #define AR_TIMT_LAST 0x0000ffff /* Last packet threshold */ #define AR_TIMT_LAST_S 0 #define AR_TIMT_FIRST 0xffff0000 /* First packet threshold */ #define AR_TIMT_FIRST_S 16 #define AR_RIMT_LAST 0x0000ffff /* Last packet threshold */ #define AR_RIMT_LAST_S 0 #define AR_RIMT_FIRST 0xffff0000 /* First packet threshold */ #define AR_RIMT_FIRST_S 16 #define AR_GTXTO_TIMEOUT_COUNTER 0x0000FFFF // Mask for timeout counter (in TUs) #define AR_GTXTO_TIMEOUT_LIMIT 0xFFFF0000 // Mask for timeout limit (in TUs) #define AR_GTXTO_TIMEOUT_LIMIT_S 16 // Shift for timeout limit #define AR_GTTM_USEC 0x00000001 // usec strobe #define AR_GTTM_IGNORE_IDLE 0x00000002 // ignore channel idle #define AR_GTTM_RESET_IDLE 0x00000004 // reset counter on channel idle low #define AR_GTTM_CST_USEC 0x00000008 // CST usec strobe #define AR_CST_TIMEOUT_COUNTER 0x0000FFFF // Mask for timeout counter (in TUs) #define AR_CST_TIMEOUT_LIMIT 0xFFFF0000 // Mask for timeout limit (in TUs) #define AR_CST_TIMEOUT_LIMIT_S 16 // Shift for timeout limit /* MAC tx DMA size config */ #define AR_TXCFG_DMASZ_MASK 0x00000003 #define AR_TXCFG_DMASZ_4B 0 #define AR_TXCFG_DMASZ_8B 1 #define AR_TXCFG_DMASZ_16B 2 #define AR_TXCFG_DMASZ_32B 3 #define AR_TXCFG_DMASZ_64B 4 #define AR_TXCFG_DMASZ_128B 5 #define AR_TXCFG_DMASZ_256B 6 #define AR_TXCFG_DMASZ_512B 7 #define AR_TXCFG_ATIM_TXPOLICY 0x00000800 /* MAC rx DMA size config */ #define AR_RXCFG_DMASZ_MASK 0x00000007 #define AR_RXCFG_DMASZ_4B 0 #define AR_RXCFG_DMASZ_8B 1 #define AR_RXCFG_DMASZ_16B 2 #define AR_RXCFG_DMASZ_32B 3 #define AR_RXCFG_DMASZ_64B 4 #define AR_RXCFG_DMASZ_128B 5 #define AR_RXCFG_DMASZ_256B 6 #define AR_RXCFG_DMASZ_512B 7 /* MAC Led registers */ #define AR_CFG_SCLK_RATE_IND 0x00000003 /* sleep clock indication */ #define AR_CFG_SCLK_RATE_IND_S 0 #define AR_CFG_SCLK_32MHZ 0x00000000 /* Sleep clock rate */ #define AR_CFG_SCLK_4MHZ 0x00000001 /* Sleep clock rate */ #define AR_CFG_SCLK_1MHZ 0x00000002 /* Sleep clock rate */ #define AR_CFG_SCLK_32KHZ 0x00000003 /* Sleep clock rate */ #define AR_MAC_LED_BLINK_SLOW 0x00000008 /* LED slowest blink rate mode */ #define AR_MAC_LED_BLINK_THRESH_SEL 0x00000070 /* LED blink threshold select */ #define AR_MAC_LED_MODE 0x00000380 /* LED mode select */ #define AR_MAC_LED_MODE_S 7 #define AR_MAC_LED_MODE_PROP 0 /* Blink prop to filtered tx/rx */ #define AR_MAC_LED_MODE_RPROP 1 /* Blink prop to unfiltered tx/rx */ #define AR_MAC_LED_MODE_SPLIT 2 /* Blink power for tx/net for rx */ #define AR_MAC_LED_MODE_RAND 3 /* Blink randomly */ #define AR_MAC_LED_MODE_POWON 5 /* Power LED on (s/w control) */ #define AR_MAC_LED_MODE_NETON 6 /* Network LED on (s/w control) */ #define AR_MAC_LED_ASSOC 0x00000c00 #define AR_MAC_LED_ASSOC_NONE 0x0 /* STA is not associated or trying */ #define AR_MAC_LED_ASSOC_ACTIVE 0x1 /* STA is associated */ #define AR_MAC_LED_ASSOC_PEND 0x2 /* STA is trying to associate */ #define AR_MAC_LED_ASSOC_S 10 #define AR_WA_BIT6 0x00000040 #define AR_WA_BIT7 0x00000080 #define AR_WA_D3_L1_DISABLE 0x00004000 /* */ #define AR_WA_UNTIE_RESET_EN 0x00008000 /* ena PCI reset to POR */ #define AR_WA_RESET_EN 0x00040000 /* ena AR_WA_UNTIE_RESET_EN */ #define AR_WA_ANALOG_SHIFT 0x00100000 #define AR_WA_POR_SHORT 0x00200000 /* PCIE phy reset control */ #define AR_WA_BIT22 0x00400000 #define AR_WA_BIT23 0x00800000 #define AR_WA_DEFAULT 0x0000073f #define AR9280_WA_DEFAULT 0x0040073b /* disable bit 2, see commit */ #define AR9285_WA_DEFAULT 0x004a05cb #define AR_PCIE_PM_CTRL_ENA 0x00080000 #define AR_AHB_EXACT_WR_EN 0x00000000 /* write exact bytes */ -#define AR_AHB_BUF_WR_EN 0x00000001 /* buffer write upto cacheline*/ +#define AR_AHB_BUF_WR_EN 0x00000001 /* buffer write up to cacheline*/ #define AR_AHB_EXACT_RD_EN 0x00000000 /* read exact bytes */ -#define AR_AHB_CACHELINE_RD_EN 0x00000002 /* read upto end of cacheline */ -#define AR_AHB_PREFETCH_RD_EN 0x00000004 /* prefetch upto page boundary*/ +#define AR_AHB_CACHELINE_RD_EN 0x00000002 /* read up to end of cacheline */ +#define AR_AHB_PREFETCH_RD_EN 0x00000004 /* prefetch up to page boundary*/ #define AR_AHB_PAGE_SIZE_1K 0x00000000 /* set page-size as 1k */ #define AR_AHB_PAGE_SIZE_2K 0x00000008 /* set page-size as 2k */ #define AR_AHB_PAGE_SIZE_4K 0x00000010 /* set page-size as 4k */ /* Kiwi */ #define AR_AHB_CUSTOM_BURST_EN 0x000000C0 /* set Custom Burst Mode */ #define AR_AHB_CUSTOM_BURST_EN_S 6 /* set Custom Burst Mode */ #define AR_AHB_CUSTOM_BURST_ASYNC_FIFO_VAL 3 /* set both bits in Async FIFO mode */ /* MAC PCU Registers */ #define AR_STA_ID1_PRESERVE_SEQNUM 0x20000000 /* Don't replace seq num */ /* Extended PCU DIAG_SW control fields */ #define AR_DIAG_DUAL_CHAIN_INFO 0x01000000 /* dual chain channel info */ #define AR_DIAG_RX_ABORT 0x02000000 /* abort rx */ #define AR_DIAG_SATURATE_CCNT 0x04000000 /* sat. cycle cnts (no shift) */ #define AR_DIAG_OBS_PT_SEL2 0x08000000 /* observation point sel */ #define AR_DIAG_RXCLEAR_CTL_LOW 0x10000000 /* force rx_clear(ctl) low/busy */ #define AR_DIAG_RXCLEAR_EXT_LOW 0x20000000 /* force rx_clear(ext) low/busy */ #define AR_TXOP_X_VAL 0x000000FF #define AR_RESET_TSF_ONCE 0x01000000 /* reset tsf once; self-clears*/ /* Interrupts */ #define AR_ISR_TXMINTR 0x00080000 /* Maximum interrupt tx rate */ #define AR_ISR_RXMINTR 0x01000000 /* Maximum interrupt rx rate */ #define AR_ISR_GENTMR 0x10000000 /* OR of generic timer bits in S5 */ #define AR_ISR_TXINTM 0x40000000 /* Tx int after mitigation */ #define AR_ISR_RXINTM 0x80000000 /* Rx int after mitigation */ #define AR_ISR_S2_CST 0x00400000 /* Carrier sense timeout */ #define AR_ISR_S2_GTT 0x00800000 /* Global transmit timeout */ #define AR_ISR_S2_TSFOOR 0x40000000 /* RX TSF out of range */ #define AR_ISR_S5 0x0098 #define AR_ISR_S5_S 0x00d8 #define AR_ISR_S5_GENTIMER7 0x00000080 // Mask for timer 7 trigger #define AR_ISR_S5_TIM_TIMER 0x00000010 // TIM Timer ISR #define AR_ISR_S5_DTIM_TIMER 0x00000020 // DTIM Timer ISR #define AR_ISR_S5_GENTIMER_TRIG 0x0000FF80 // ISR for generic timer trigger 7-15 #define AR_ISR_S5_GENTIMER_TRIG_S 0 #define AR_ISR_S5_GENTIMER_THRESH 0xFF800000 // ISR for generic timer threshold 7-15 #define AR_ISR_S5_GENTIMER_THRESH_S 16 #define AR_INTR_SPURIOUS 0xffffffff #define AR_INTR_RTC_IRQ 0x00000001 /* rtc in shutdown state */ #define AR_INTR_MAC_IRQ 0x00000002 /* pending mac interrupt */ #define AR_INTR_EEP_PROT_ACCESS 0x00000004 /* eeprom protected access */ #define AR_INTR_MAC_AWAKE 0x00020000 /* mac is awake */ #define AR_INTR_MAC_ASLEEP 0x00040000 /* mac is asleep */ /* Interrupt Mask Registers */ #define AR_IMR_TXMINTR 0x00080000 /* Maximum interrupt tx rate */ #define AR_IMR_RXMINTR 0x01000000 /* Maximum interrupt rx rate */ #define AR_IMR_TXINTM 0x40000000 /* Tx int after mitigation */ #define AR_IMR_RXINTM 0x80000000 /* Rx int after mitigation */ #define AR_IMR_S2_CST 0x00400000 /* Carrier sense timeout */ #define AR_IMR_S2_GTT 0x00800000 /* Global transmit timeout */ /* synchronous interrupt signals */ #define AR_INTR_SYNC_RTC_IRQ 0x00000001 #define AR_INTR_SYNC_MAC_IRQ 0x00000002 #define AR_INTR_SYNC_EEPROM_ILLEGAL_ACCESS 0x00000004 #define AR_INTR_SYNC_APB_TIMEOUT 0x00000008 #define AR_INTR_SYNC_PCI_MODE_CONFLICT 0x00000010 #define AR_INTR_SYNC_HOST1_FATAL 0x00000020 #define AR_INTR_SYNC_HOST1_PERR 0x00000040 #define AR_INTR_SYNC_TRCV_FIFO_PERR 0x00000080 #define AR_INTR_SYNC_RADM_CPL_EP 0x00000100 #define AR_INTR_SYNC_RADM_CPL_DLLP_ABORT 0x00000200 #define AR_INTR_SYNC_RADM_CPL_TLP_ABORT 0x00000400 #define AR_INTR_SYNC_RADM_CPL_ECRC_ERR 0x00000800 #define AR_INTR_SYNC_RADM_CPL_TIMEOUT 0x00001000 #define AR_INTR_SYNC_LOCAL_TIMEOUT 0x00002000 #define AR_INTR_SYNC_PM_ACCESS 0x00004000 #define AR_INTR_SYNC_MAC_AWAKE 0x00008000 #define AR_INTR_SYNC_MAC_ASLEEP 0x00010000 #define AR_INTR_SYNC_MAC_SLEEP_ACCESS 0x00020000 #define AR_INTR_SYNC_ALL 0x0003FFFF /* default synchronous interrupt signals enabled */ #define AR_INTR_SYNC_DEFAULT \ (AR_INTR_SYNC_HOST1_FATAL | AR_INTR_SYNC_HOST1_PERR | \ AR_INTR_SYNC_RADM_CPL_EP | AR_INTR_SYNC_RADM_CPL_DLLP_ABORT | \ AR_INTR_SYNC_RADM_CPL_TLP_ABORT | AR_INTR_SYNC_RADM_CPL_ECRC_ERR | \ AR_INTR_SYNC_RADM_CPL_TIMEOUT | AR_INTR_SYNC_LOCAL_TIMEOUT | \ AR_INTR_SYNC_MAC_SLEEP_ACCESS) #define AR_INTR_SYNC_MASK_GPIO 0xFFFC0000 #define AR_INTR_SYNC_MASK_GPIO_S 18 #define AR_INTR_SYNC_ENABLE_GPIO 0xFFFC0000 #define AR_INTR_SYNC_ENABLE_GPIO_S 18 #define AR_INTR_ASYNC_MASK_GPIO 0xFFFC0000 /* async int mask */ #define AR_INTR_ASYNC_MASK_GPIO_S 18 #define AR_INTR_ASYNC_CAUSE_GPIO 0xFFFC0000 /* GPIO interrupts */ #define AR_INTR_ASYNC_USED (AR_INTR_MAC_IRQ | AR_INTR_ASYNC_CAUSE_GPIO) #define AR_INTR_ASYNC_ENABLE_GPIO 0xFFFC0000 /* enable interrupts */ #define AR_INTR_ASYNC_ENABLE_GPIO_S 18 /* RTC registers */ #define AR_RTC_RC_M 0x00000003 #define AR_RTC_RC_MAC_WARM 0x00000001 #define AR_RTC_RC_MAC_COLD 0x00000002 #ifdef AH_SUPPORT_AR9130 #define AR_RTC_RC_COLD_RESET 0x00000004 #define AR_RTC_RC_WARM_RESET 0x00000008 #endif /* AH_SUPPORT_AR9130 */ #define AR_RTC_PLL_DIV 0x0000001f #define AR_RTC_PLL_DIV_S 0 #define AR_RTC_PLL_DIV2 0x00000020 #define AR_RTC_PLL_REFDIV_5 0x000000c0 #define AR_RTC_SOWL_PLL_DIV 0x000003ff #define AR_RTC_SOWL_PLL_DIV_S 0 #define AR_RTC_SOWL_PLL_REFDIV 0x00003C00 #define AR_RTC_SOWL_PLL_REFDIV_S 10 #define AR_RTC_SOWL_PLL_CLKSEL 0x0000C000 #define AR_RTC_SOWL_PLL_CLKSEL_S 14 #define AR_RTC_RESET_EN 0x00000001 /* Reset RTC bit */ #define AR_RTC_PM_STATUS_M 0x0000000f /* Pwr Mgmt Status */ #ifdef AH_SUPPORT_AR9130 #define AR_RTC_STATUS_M 0x0000000f /* RTC Status */ #else #define AR_RTC_STATUS_M 0x0000003f /* RTC Status */ #endif /* AH_SUPPORT_AR9130 */ #define AR_RTC_STATUS_SHUTDOWN 0x00000001 #define AR_RTC_STATUS_ON 0x00000002 #define AR_RTC_STATUS_SLEEP 0x00000004 #define AR_RTC_STATUS_WAKEUP 0x00000008 #define AR_RTC_STATUS_COLDRESET 0x00000010 /* Not currently used */ #define AR_RTC_STATUS_PLLCHANGE 0x00000020 /* Not currently used */ #define AR_RTC_SLEEP_DERIVED_CLK 0x2 #define AR_RTC_FORCE_WAKE_EN 0x00000001 /* enable force wake */ #define AR_RTC_FORCE_WAKE_ON_INT 0x00000002 /* auto-wake on MAC interrupt */ #define AR_RTC_PLL_CLKSEL 0x00000300 #define AR_RTC_PLL_CLKSEL_S 8 /* AR9280: rf long shift registers */ #define AR_AN_RF2G1_CH0 0x7810 #define AR_AN_RF5G1_CH0 0x7818 #define AR_AN_RF2G1_CH1 0x7834 #define AR_AN_RF5G1_CH1 0x783C #define AR_AN_TOP2 0x7894 #define AR_AN_SYNTH9 0x7868 #define AR_AN_RF2G1_CH0_OB 0x03800000 #define AR_AN_RF2G1_CH0_OB_S 23 #define AR_AN_RF2G1_CH0_DB 0x1C000000 #define AR_AN_RF2G1_CH0_DB_S 26 #define AR_AN_RF5G1_CH0_OB5 0x00070000 #define AR_AN_RF5G1_CH0_OB5_S 16 #define AR_AN_RF5G1_CH0_DB5 0x00380000 #define AR_AN_RF5G1_CH0_DB5_S 19 #define AR_AN_RF2G1_CH1_OB 0x03800000 #define AR_AN_RF2G1_CH1_OB_S 23 #define AR_AN_RF2G1_CH1_DB 0x1C000000 #define AR_AN_RF2G1_CH1_DB_S 26 #define AR_AN_RF5G1_CH1_OB5 0x00070000 #define AR_AN_RF5G1_CH1_OB5_S 16 #define AR_AN_RF5G1_CH1_DB5 0x00380000 #define AR_AN_RF5G1_CH1_DB5_S 19 #define AR_AN_TOP1 0x7890 #define AR_AN_TOP1_DACIPMODE 0x00040000 #define AR_AN_TOP1_DACIPMODE_S 18 #define AR_AN_TOP2_XPABIAS_LVL 0xC0000000 #define AR_AN_TOP2_XPABIAS_LVL_S 30 #define AR_AN_TOP2_LOCALBIAS 0x00200000 #define AR_AN_TOP2_LOCALBIAS_S 21 #define AR_AN_TOP2_PWDCLKIND 0x00400000 #define AR_AN_TOP2_PWDCLKIND_S 22 #define AR_AN_SYNTH9_REFDIVA 0xf8000000 #define AR_AN_SYNTH9_REFDIVA_S 27 #define AR9271_AN_RF2G6_OFFS 0x07f00000 #define AR9271_AN_RF2G6_OFFS_S 20 /* Sleep control */ #define AR5416_SLEEP1_ASSUME_DTIM 0x00080000 #define AR5416_SLEEP1_CAB_TIMEOUT 0xFFE00000 /* Cab timeout (TU) */ #define AR5416_SLEEP1_CAB_TIMEOUT_S 21 #define AR5416_SLEEP2_BEACON_TIMEOUT 0xFFE00000 /* Beacon timeout (TU)*/ #define AR5416_SLEEP2_BEACON_TIMEOUT_S 21 /* Sleep Registers */ #define AR_SLP32_HALFCLK_LATENCY 0x000FFFFF /* rising <-> falling edge */ #define AR_SLP32_ENA 0x00100000 #define AR_SLP32_TSF_WRITE_STATUS 0x00200000 /* tsf update in progress */ #define AR_SLP32_WAKE_XTL_TIME 0x0000FFFF /* time to wake crystal */ #define AR_SLP32_TST_INC 0x000FFFFF #define AR_SLP_MIB_CLEAR 0x00000001 /* clear pending */ #define AR_SLP_MIB_PENDING 0x00000002 /* clear counters */ #define AR_TIMER_MODE_TBTT 0x00000001 #define AR_TIMER_MODE_DBA 0x00000002 #define AR_TIMER_MODE_SWBA 0x00000004 #define AR_TIMER_MODE_HCF 0x00000008 #define AR_TIMER_MODE_TIM 0x00000010 #define AR_TIMER_MODE_DTIM 0x00000020 #define AR_TIMER_MODE_QUIET 0x00000040 #define AR_TIMER_MODE_NDP 0x00000080 #define AR_TIMER_MODE_OVERFLOW_INDEX 0x00000700 #define AR_TIMER_MODE_OVERFLOW_INDEX_S 8 #define AR_TIMER_MODE_THRESH 0xFFFFF000 #define AR_TIMER_MODE_THRESH_S 12 /* PCU Misc modes */ #define AR_PCU_FORCE_BSSID_MATCH 0x00000001 /* force bssid to match */ #define AR_PCU_MIC_NEW_LOC_ENA 0x00000004 /* tx/rx mic keys together */ #define AR_PCU_TX_ADD_TSF 0x00000008 /* add tx_tsf + int_tsf */ #define AR_PCU_CCK_SIFS_MODE 0x00000010 /* assume 11b sifs */ #define AR_PCU_RX_ANT_UPDT 0x00000800 /* KC_RX_ANT_UPDATE */ #define AR_PCU_TXOP_TBTT_LIMIT_ENA 0x00001000 /* enforce txop / tbtt */ #define AR_PCU_MISS_BCN_IN_SLEEP 0x00004000 /* count bmiss's when sleeping */ #define AR_PCU_BUG_12306_FIX_ENA 0x00020000 /* use rx_clear to count sifs */ #define AR_PCU_FORCE_QUIET_COLL 0x00040000 /* kill xmit for channel change */ #define AR_PCU_BT_ANT_PREVENT_RX 0x00100000 #define AR_PCU_BT_ANT_PREVENT_RX_S 20 -#define AR_PCU_TBTT_PROTECT 0x00200000 /* no xmit upto tbtt+20 uS */ +#define AR_PCU_TBTT_PROTECT 0x00200000 /* no xmit up to tbtt+20 uS */ #define AR_PCU_CLEAR_VMF 0x01000000 /* clear vmf mode (fast cc)*/ #define AR_PCU_CLEAR_BA_VALID 0x04000000 /* clear ba state */ #define AR_PCU_SEL_EVM 0x08000000 /* select EVM data or PLCP header */ #define AR_PCU_MISC_MODE2_MGMT_CRYPTO_ENABLE 0x00000002 #define AR_PCU_MISC_MODE2_NO_CRYPTO_FOR_NON_DATA_PKT 0x00000004 /* * This bit enables the Multicast search based on both MAC Address and Key ID. * If bit is 0, then Multicast search is based on MAC address only. * For Merlin and above only. */ #define AR_PCU_MISC_MODE2_ADHOC_MCAST_KEYID_ENABLE 0x00000040 #define AR_PCU_MISC_MODE2_ENABLE_AGGWEP 0x00020000 /* Kiwi or later? */ #define AR_PCU_MISC_MODE2_HWWAR1 0x00100000 #define AR_PCU_MISC_MODE2_HWWAR2 0x02000000 /* For Kiwi */ #define AR_MAC_PCU_ASYNC_FIFO_REG3 0x8358 #define AR_MAC_PCU_ASYNC_FIFO_REG3_DATAPATH_SEL 0x00000400 #define AR_MAC_PCU_ASYNC_FIFO_REG3_SOFT_RESET 0x80000000 /* TSF2. For Kiwi only */ #define AR_TSF2_L32 0x8390 #define AR_TSF2_U32 0x8394 /* MAC Direct Connect Control. For Kiwi only */ #define AR_DIRECT_CONNECT 0x83A0 #define AR_DC_AP_STA_EN 0x00000001 /* GPIO Interrupt */ #define AR_INTR_GPIO 0x3FF00000 /* gpio interrupted */ #define AR_INTR_GPIO_S 20 #define AR_GPIO_OUT_CTRL 0x000003FF /* 0 = out, 1 = in */ #define AR_GPIO_OUT_VAL 0x000FFC00 #define AR_GPIO_OUT_VAL_S 10 #define AR_GPIO_INTR_CTRL 0x3FF00000 #define AR_GPIO_INTR_CTRL_S 20 #define AR_GPIO_IN_VAL 0x0FFFC000 /* pre-9280 */ #define AR_GPIO_IN_VAL_S 14 #define AR928X_GPIO_IN_VAL 0x000FFC00 #define AR928X_GPIO_IN_VAL_S 10 #define AR9285_GPIO_IN_VAL 0x00FFF000 #define AR9285_GPIO_IN_VAL_S 12 #define AR9287_GPIO_IN_VAL 0x003FF800 #define AR9287_GPIO_IN_VAL_S 11 #define AR_GPIO_OE_OUT_DRV 0x3 /* 2 bit mask shifted by 2*bitpos */ #define AR_GPIO_OE_OUT_DRV_NO 0x0 /* tristate */ #define AR_GPIO_OE_OUT_DRV_LOW 0x1 /* drive if low */ #define AR_GPIO_OE_OUT_DRV_HI 0x2 /* drive if high */ #define AR_GPIO_OE_OUT_DRV_ALL 0x3 /* drive always */ #define AR_GPIO_INTR_POL_VAL 0x1FFF #define AR_GPIO_INTR_POL_VAL_S 0 #define AR_GPIO_JTAG_DISABLE 0x00020000 #define AR_2040_JOINED_RX_CLEAR 0x00000001 /* use ctl + ext rx_clear for cca */ #define AR_PCU_TXBUF_CTRL_SIZE_MASK 0x7FF #define AR_PCU_TXBUF_CTRL_USABLE_SIZE 0x700 #define AR_9285_PCU_TXBUF_CTRL_USABLE_SIZE 0x380 /* IFS, SIFS, slot, etc for Async FIFO mode (Kiwi) */ #define AR_D_GBL_IFS_SIFS_ASYNC_FIFO_DUR 0x000003AB #define AR_TIME_OUT_ACK_CTS_ASYNC_FIFO_DUR 0x16001D56 #define AR_USEC_ASYNC_FIFO_DUR 0x12e00074 #define AR_D_GBL_IFS_SLOT_ASYNC_FIFO_DUR 0x00000420 #define AR_D_GBL_IFS_EIFS_ASYNC_FIFO_DUR 0x0000A5EB /* Used by Kiwi Async FIFO */ #define AR_MAC_PCU_LOGIC_ANALYZER 0x8264 #define AR_MAC_PCU_LOGIC_ANALYZER_DISBUG20768 0x20000000 /* Eeprom defines */ #define AR_EEPROM_STATUS_DATA_VAL 0x0000ffff #define AR_EEPROM_STATUS_DATA_VAL_S 0 #define AR_EEPROM_STATUS_DATA_BUSY 0x00010000 #define AR_EEPROM_STATUS_DATA_BUSY_ACCESS 0x00020000 #define AR_EEPROM_STATUS_DATA_PROT_ACCESS 0x00040000 #define AR_EEPROM_STATUS_DATA_ABSENT_ACCESS 0x00080000 /* K2 (9271) */ #define AR9271_CLOCK_CONTROL 0x50040 #define AR9271_CLOCK_SELECTION_22 0x0 #define AR9271_CLOCK_SELECTION_88 0x1 #define AR9271_CLOCK_SELECTION_44 0x2 #define AR9271_CLOCK_SELECTION_117 0x4 #define AR9271_CLOCK_SELECTION_OSC_40 0x6 #define AR9271_CLOCK_SELECTION_RTC 0x7 #define AR9271_SPI_SEL 0x100 #define AR9271_UART_SEL 0x200 #define AR9271_RESET_POWER_DOWN_CONTROL 0x50044 #define AR9271_RADIO_RF_RST 0x20 #define AR9271_GATE_MAC_CTL 0x4000 #define AR9271_MAIN_PLL_PWD_CTL 0x40000 #define AR9271_CLKMISC 0x4090 #define AR9271_OSC_to_10M_EN 0x00000001 /* * AR5212 defines the MAC revision mask as 0xF, but both ath9k and * the Atheros HAL define it as 0x7. * * What this means however is AR5416 silicon revisions have * changed. The below macros are for what is contained in the * lower four bits; if the lower three bits are taken into account * the revisions become 1.0 => 0x0, 2.0 => 0x1, 2.2 => 0x2. */ /* These are the legacy revisions, with a four bit AR_SREV_REVISION mask */ #define AR_SREV_REVISION_OWL_10 0x08 #define AR_SREV_REVISION_OWL_20 0x09 #define AR_SREV_REVISION_OWL_22 0x0a #define AR_RAD5133_SREV_MAJOR 0xc0 /* Fowl: 2+5G/3x3 */ #define AR_RAD2133_SREV_MAJOR 0xd0 /* Fowl: 2G/3x3 */ #define AR_RAD5122_SREV_MAJOR 0xe0 /* Fowl: 5G/2x2 */ #define AR_RAD2122_SREV_MAJOR 0xf0 /* Fowl: 2+5G/2x2 */ /* Test macro for owl 1.0 */ #define IS_5416V1(_ah) (AR_SREV_OWL((_ah)) && AH_PRIVATE((_ah))->ah_macRev == AR_SREV_REVISION_OWL_10) #define IS_5416V2(_ah) (AR_SREV_OWL((_ah)) && AH_PRIVATE((_ah))->ah_macRev >= AR_SREV_REVISION_OWL_20) #define IS_5416V2_2(_ah) (AR_SREV_OWL((_ah)) && AH_PRIVATE((_ah))->ah_macRev == AR_SREV_REVISION_OWL_22) /* Misc; compatibility with Atheros HAL */ #define AR_SREV_5416_V20_OR_LATER(_ah) (AR_SREV_HOWL((_ah)) || AR_SREV_OWL_20_OR_LATER(_ah)) #define AR_SREV_5416_V22_OR_LATER(_ah) (AR_SREV_HOWL((_ah)) || AR_SREV_OWL_22_OR_LATER(_ah)) /* Expanded Mac Silicon Rev (16 bits starting with Sowl) */ #define AR_XSREV_ID 0xFFFFFFFF /* Chip ID */ #define AR_XSREV_ID_S 0 #define AR_XSREV_VERSION 0xFFFC0000 /* Chip version */ #define AR_XSREV_VERSION_S 18 #define AR_XSREV_TYPE 0x0003F000 /* Chip type */ #define AR_XSREV_TYPE_S 12 #define AR_XSREV_TYPE_CHAIN 0x00001000 /* Chain Mode (1:3 chains, * 0:2 chains) */ #define AR_XSREV_TYPE_HOST_MODE 0x00002000 /* Host Mode (1:PCI, 0:PCIe) */ #define AR_XSREV_REVISION 0x00000F00 #define AR_XSREV_REVISION_S 8 #define AR_XSREV_VERSION_OWL_PCI 0x0D #define AR_XSREV_VERSION_OWL_PCIE 0x0C /* * These are from ath9k/Atheros and assume an AR_SREV version mask * of 0x07, rather than 0x0F which is being used in the FreeBSD HAL. * Thus, don't use these values as they're incorrect here; use * AR_SREV_REVISION_OWL_{10,20,22}. */ #if 0 #define AR_XSREV_REVISION_OWL_10 0 /* Owl 1.0 */ #define AR_XSREV_REVISION_OWL_20 1 /* Owl 2.0/2.1 */ #define AR_XSREV_REVISION_OWL_22 2 /* Owl 2.2 */ #endif #define AR_XSREV_VERSION_HOWL 0x14 /* Howl (AR9130) */ #define AR_XSREV_VERSION_SOWL 0x40 /* Sowl (AR9160) */ #define AR_XSREV_REVISION_SOWL_10 0 /* Sowl 1.0 */ #define AR_XSREV_REVISION_SOWL_11 1 /* Sowl 1.1 */ #define AR_XSREV_VERSION_MERLIN 0x80 /* Merlin Version */ #define AR_XSREV_REVISION_MERLIN_10 0 /* Merlin 1.0 */ #define AR_XSREV_REVISION_MERLIN_20 1 /* Merlin 2.0 */ #define AR_XSREV_REVISION_MERLIN_21 2 /* Merlin 2.1 */ #define AR_XSREV_VERSION_KITE 0xC0 /* Kite Version */ #define AR_XSREV_REVISION_KITE_10 0 /* Kite 1.0 */ #define AR_XSREV_REVISION_KITE_11 1 /* Kite 1.1 */ #define AR_XSREV_REVISION_KITE_12 2 /* Kite 1.2 */ #define AR_XSREV_VERSION_KIWI 0x180 /* Kiwi (AR9287) */ #define AR_XSREV_REVISION_KIWI_10 0 /* Kiwi 1.0 */ #define AR_XSREV_REVISION_KIWI_11 1 /* Kiwi 1.1 */ #define AR_XSREV_REVISION_KIWI_12 2 /* Kiwi 1.2 */ #define AR_XSREV_REVISION_KIWI_13 3 /* Kiwi 1.3 */ /* Owl (AR5416) */ #define AR_SREV_OWL(_ah) \ ((AH_PRIVATE((_ah))->ah_macVersion == AR_XSREV_VERSION_OWL_PCI) || \ (AH_PRIVATE((_ah))->ah_macVersion == AR_XSREV_VERSION_OWL_PCIE)) #define AR_SREV_OWL_20_OR_LATER(_ah) \ ((AR_SREV_OWL(_ah) && \ AH_PRIVATE((_ah))->ah_macRev >= AR_SREV_REVISION_OWL_20) || \ AH_PRIVATE((_ah))->ah_macVersion >= AR_XSREV_VERSION_HOWL) #define AR_SREV_OWL_22_OR_LATER(_ah) \ ((AR_SREV_OWL(_ah) && \ AH_PRIVATE((_ah))->ah_macRev >= AR_SREV_REVISION_OWL_22) || \ AH_PRIVATE((_ah))->ah_macVersion >= AR_XSREV_VERSION_HOWL) /* Howl (AR9130) */ #define AR_SREV_HOWL(_ah) \ (AH_PRIVATE((_ah))->ah_macVersion == AR_XSREV_VERSION_HOWL) #define AR_SREV_9100(_ah) AR_SREV_HOWL(_ah) /* Sowl (AR9160) */ #define AR_SREV_SOWL(_ah) \ (AH_PRIVATE((_ah))->ah_macVersion == AR_XSREV_VERSION_SOWL) #define AR_SREV_SOWL_10_OR_LATER(_ah) \ (AH_PRIVATE((_ah))->ah_macVersion >= AR_XSREV_VERSION_SOWL) #define AR_SREV_SOWL_11(_ah) \ (AR_SREV_SOWL(_ah) && \ AH_PRIVATE((_ah))->ah_macRev == AR_XSREV_REVISION_SOWL_11) /* Merlin (AR9280) */ #define AR_SREV_MERLIN(_ah) \ (AH_PRIVATE((_ah))->ah_macVersion == AR_XSREV_VERSION_MERLIN) #define AR_SREV_MERLIN_10_OR_LATER(_ah) \ (AH_PRIVATE((_ah))->ah_macVersion >= AR_XSREV_VERSION_MERLIN) #define AR_SREV_MERLIN_20(_ah) \ (AR_SREV_MERLIN(_ah) && \ AH_PRIVATE((_ah))->ah_macRev >= AR_XSREV_REVISION_MERLIN_20) #define AR_SREV_MERLIN_20_OR_LATER(_ah) \ ((AH_PRIVATE((_ah))->ah_macVersion > AR_XSREV_VERSION_MERLIN) || \ (AR_SREV_MERLIN((_ah)) && \ AH_PRIVATE((_ah))->ah_macRev >= AR_XSREV_REVISION_MERLIN_20)) /* Kite (AR9285) */ #define AR_SREV_KITE(_ah) \ (AH_PRIVATE((_ah))->ah_macVersion == AR_XSREV_VERSION_KITE) #define AR_SREV_KITE_10_OR_LATER(_ah) \ (AH_PRIVATE((_ah))->ah_macVersion >= AR_XSREV_VERSION_KITE) #define AR_SREV_KITE_11(_ah) \ (AR_SREV_KITE(ah) && \ AH_PRIVATE((_ah))->ah_macRev == AR_XSREV_REVISION_KITE_11) #define AR_SREV_KITE_11_OR_LATER(_ah) \ ((AH_PRIVATE((_ah))->ah_macVersion > AR_XSREV_VERSION_KITE) || \ (AR_SREV_KITE((_ah)) && \ AH_PRIVATE((_ah))->ah_macRev >= AR_XSREV_REVISION_KITE_11)) #define AR_SREV_KITE_12(_ah) \ (AR_SREV_KITE(ah) && \ AH_PRIVATE((_ah))->ah_macRev == AR_XSREV_REVISION_KITE_12) #define AR_SREV_KITE_12_OR_LATER(_ah) \ ((AH_PRIVATE((_ah))->ah_macVersion > AR_XSREV_VERSION_KITE) || \ (AR_SREV_KITE((_ah)) && \ AH_PRIVATE((_ah))->ah_macRev >= AR_XSREV_REVISION_KITE_12)) #define AR_SREV_9285E_20(_ah) \ (AR_SREV_KITE_12_OR_LATER(_ah) && \ ((OS_REG_READ(_ah, AR_AN_SYNTH9) & 0x7) == 0x1)) #define AR_SREV_KIWI(_ah) \ (AH_PRIVATE((_ah))->ah_macVersion == AR_XSREV_VERSION_KIWI) #define AR_SREV_KIWI_10_OR_LATER(_ah) \ (AH_PRIVATE((_ah))->ah_macVersion >= AR_XSREV_VERSION_KIWI) /* XXX TODO: make these handle macVersion > Kiwi */ #define AR_SREV_KIWI_11_OR_LATER(_ah) \ (AR_SREV_KIWI(_ah) && \ AH_PRIVATE((_ah))->ah_macRev >= AR_XSREV_REVISION_KIWI_11) #define AR_SREV_KIWI_11(_ah) \ (AR_SREV_KIWI(_ah) && \ AH_PRIVATE((_ah))->ah_macRev == AR_XSREV_REVISION_KIWI_11) #define AR_SREV_KIWI_12(_ah) \ (AR_SREV_KIWI(_ah) && \ AH_PRIVATE((_ah))->ah_macRev == AR_XSREV_REVISION_KIWI_12) #define AR_SREV_KIWI_12_OR_LATER(_ah) \ (AR_SREV_KIWI(_ah) && \ AH_PRIVATE((_ah))->ah_macRev >= AR_XSREV_REVISION_KIWI_12) #define AR_SREV_KIWI_13_OR_LATER(_ah) \ (AR_SREV_KIWI(_ah) && \ AH_PRIVATE((_ah))->ah_macRev >= AR_XSREV_REVISION_KIWI_13) /* Not yet implemented chips */ #define AR_SREV_9271(_ah) 0 #endif /* _DEV_ATH_AR5416REG_H */ Index: head/sys/dev/ath/ath_hal/ar9002/ar9280_olc.c =================================================================== --- head/sys/dev/ath/ath_hal/ar9002/ar9280_olc.c (revision 298938) +++ head/sys/dev/ath/ath_hal/ar9002/ar9280_olc.c (revision 298939) @@ -1,408 +1,408 @@ /* * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ah_eeprom_v14.h" #include "ar9002/ar9280.h" #include "ar5416/ar5416reg.h" #include "ar5416/ar5416phy.h" #include "ar9002/ar9002phy.h" #include "ar9002/ar9280_olc.h" void ar9280olcInit(struct ath_hal *ah) { uint32_t i; /* Only do OLC if it's enabled for this chipset */ if (! ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) return; HALDEBUG(ah, HAL_DEBUG_RESET, "%s: Setting up TX gain tables.\n", __func__); for (i = 0; i < AR9280_TX_GAIN_TABLE_SIZE; i++) AH9280(ah)->originalGain[i] = MS(OS_REG_READ(ah, AR_PHY_TX_GAIN_TBL1 + i * 4), AR_PHY_TX_GAIN); AH9280(ah)->PDADCdelta = 0; } void ar9280olcGetTxGainIndex(struct ath_hal *ah, const struct ieee80211_channel *chan, struct calDataPerFreqOpLoop *rawDatasetOpLoop, uint8_t *calChans, uint16_t availPiers, uint8_t *pwr, uint8_t *pcdacIdx) { uint8_t pcdac, i = 0; uint16_t idxL = 0, idxR = 0, numPiers; HAL_BOOL match; CHAN_CENTERS centers; ar5416GetChannelCenters(ah, chan, ¢ers); for (numPiers = 0; numPiers < availPiers; numPiers++) if (calChans[numPiers] == AR5416_BCHAN_UNUSED) break; match = ath_ee_getLowerUpperIndex((uint8_t)FREQ2FBIN(centers.synth_center, IEEE80211_IS_CHAN_2GHZ(chan)), calChans, numPiers, &idxL, &idxR); if (match) { pcdac = rawDatasetOpLoop[idxL].pcdac[0][0]; *pwr = rawDatasetOpLoop[idxL].pwrPdg[0][0]; } else { pcdac = rawDatasetOpLoop[idxR].pcdac[0][0]; *pwr = (rawDatasetOpLoop[idxL].pwrPdg[0][0] + rawDatasetOpLoop[idxR].pwrPdg[0][0])/2; } while (pcdac > AH9280(ah)->originalGain[i] && i < (AR9280_TX_GAIN_TABLE_SIZE - 1)) i++; *pcdacIdx = i; } /* * XXX txPower here is likely not the target txPower in the traditional * XXX sense, but is set by a call to ar9280olcGetTxGainIndex(). * XXX Thus, be careful if you're trying to use this routine yourself. */ void ar9280olcGetPDADCs(struct ath_hal *ah, uint32_t initTxGain, int txPower, uint8_t *pPDADCValues) { uint32_t i; uint32_t offset; OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL6_0, AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3); OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL6_1, AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3); OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL7, AR_PHY_TX_PWRCTRL_INIT_TX_GAIN, initTxGain); offset = txPower; for (i = 0; i < AR5416_NUM_PDADC_VALUES; i++) if (i < offset) pPDADCValues[i] = 0x0; else pPDADCValues[i] = 0xFF; } /* * Run temperature compensation calibration. * * The TX gain table is adjusted depending upon the difference * between the initial PDADC value and the currently read * average TX power sample value. This value is only valid if * frames have been transmitted, so currPDADC will be 0 if * no frames have yet been transmitted. */ void ar9280olcTemperatureCompensation(struct ath_hal *ah) { uint32_t rddata, i; int delta, currPDADC, regval; uint8_t hpwr_5g = 0; if (! ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) return; rddata = OS_REG_READ(ah, AR_PHY_TX_PWRCTRL4); currPDADC = MS(rddata, AR_PHY_TX_PWRCTRL_PD_AVG_OUT); HALDEBUG(ah, HAL_DEBUG_PERCAL, "%s: called: initPDADC=%d, currPDADC=%d\n", __func__, AH5416(ah)->initPDADC, currPDADC); if (AH5416(ah)->initPDADC == 0 || currPDADC == 0) return; (void) (ath_hal_eepromGet(ah, AR_EEP_DAC_HPWR_5G, &hpwr_5g)); if (hpwr_5g) delta = (currPDADC - AH5416(ah)->initPDADC + 4) / 8; else delta = (currPDADC - AH5416(ah)->initPDADC + 5) / 10; HALDEBUG(ah, HAL_DEBUG_PERCAL, "%s: delta=%d, PDADCdelta=%d\n", __func__, delta, AH9280(ah)->PDADCdelta); if (delta != AH9280(ah)->PDADCdelta) { AH9280(ah)->PDADCdelta = delta; for (i = 1; i < AR9280_TX_GAIN_TABLE_SIZE; i++) { regval = AH9280(ah)->originalGain[i] - delta; if (regval < 0) regval = 0; OS_REG_RMW_FIELD(ah, AR_PHY_TX_GAIN_TBL1 + i * 4, AR_PHY_TX_GAIN, regval); } } } static int16_t ar9280ChangeGainBoundarySettings(struct ath_hal *ah, uint16_t *gb, uint16_t numXpdGain, uint16_t pdGainOverlap_t2, int8_t pwr_table_offset, int16_t *diff) { uint16_t k; /* Prior to writing the boundaries or the pdadc vs. power table * into the chip registers the default starting point on the pdadc * vs. power table needs to be checked and the curve boundaries * adjusted accordingly */ if (AR_SREV_MERLIN_20_OR_LATER(ah)) { uint16_t gb_limit; if (AR5416_PWR_TABLE_OFFSET_DB != pwr_table_offset) { /* get the difference in dB */ *diff = (uint16_t)(pwr_table_offset - AR5416_PWR_TABLE_OFFSET_DB); /* get the number of half dB steps */ *diff *= 2; /* change the original gain boundary settings * by the number of half dB steps */ for (k = 0; k < numXpdGain; k++) gb[k] = (uint16_t)(gb[k] - *diff); } /* Because of a hardware limitation, ensure the gain boundary * is not larger than (63 - overlap) */ gb_limit = (uint16_t)(AR5416_MAX_RATE_POWER - pdGainOverlap_t2); for (k = 0; k < numXpdGain; k++) gb[k] = (uint16_t)min(gb_limit, gb[k]); } return *diff; } static void ar9280AdjustPDADCValues(struct ath_hal *ah, int8_t pwr_table_offset, int16_t diff, uint8_t *pdadcValues) { #define NUM_PDADC(diff) (AR5416_NUM_PDADC_VALUES - diff) uint16_t k; /* If this is a board that has a pwrTableOffset that differs from * the default AR5416_PWR_TABLE_OFFSET_DB then the start of the * pdadc vs pwr table needs to be adjusted prior to writing to the * chip. */ if (AR_SREV_MERLIN_20_OR_LATER(ah)) { if (AR5416_PWR_TABLE_OFFSET_DB != pwr_table_offset) { /* shift the table to start at the new offset */ for (k = 0; k < (uint16_t)NUM_PDADC(diff); k++ ) { pdadcValues[k] = pdadcValues[k + diff]; } /* fill the back of the table */ for (k = (uint16_t)NUM_PDADC(diff); k < NUM_PDADC(0); k++) { pdadcValues[k] = pdadcValues[NUM_PDADC(diff)]; } } } #undef NUM_PDADC } /* * This effectively disables the gain boundaries leaving it * to the open-loop TX power control. */ static void ar9280SetGainBoundariesOpenLoop(struct ath_hal *ah, int i, uint16_t pdGainOverlap_t2, uint16_t gainBoundaries[]) { int regChainOffset; regChainOffset = ar5416GetRegChainOffset(ah, i); /* These are unused for OLC */ (void) pdGainOverlap_t2; (void) gainBoundaries; HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: chain %d: writing closed loop values\n", __func__, i); OS_REG_WRITE(ah, AR_PHY_TPCRG5 + regChainOffset, SM(0x6, AR_PHY_TPCRG5_PD_GAIN_OVERLAP) | SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1) | SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2) | SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3) | SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4)); } /* Eeprom versioning macros. Returns true if the version is equal or newer than the ver specified */ /* XXX shouldn't be here! */ #define EEP_MINOR(_ah) \ (AH_PRIVATE(_ah)->ah_eeversion & AR5416_EEP_VER_MINOR_MASK) #define IS_EEP_MINOR_V2(_ah) (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_2) #define IS_EEP_MINOR_V3(_ah) (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_3) /************************************************************** * ar9280SetPowerCalTable * * Pull the PDADC piers from cal data and interpolate them across the given * points as well as from the nearest pier(s) to get a power detector * linear voltage to power level table. * * Handle OLC for Merlin where required. */ HAL_BOOL ar9280SetPowerCalTable(struct ath_hal *ah, struct ar5416eeprom *pEepData, const struct ieee80211_channel *chan, int16_t *pTxPowerIndexOffset) { CAL_DATA_PER_FREQ *pRawDataset; uint8_t *pCalBChans = AH_NULL; uint16_t pdGainOverlap_t2; static uint8_t pdadcValues[AR5416_NUM_PDADC_VALUES]; uint16_t gainBoundaries[AR5416_PD_GAINS_IN_MASK]; uint16_t numPiers, i; int16_t tMinCalPower; uint16_t numXpdGain, xpdMask; uint16_t xpdGainValues[AR5416_NUM_PD_GAINS]; uint32_t regChainOffset; int8_t pwr_table_offset; OS_MEMZERO(xpdGainValues, sizeof(xpdGainValues)); xpdMask = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].xpdGain; (void) ath_hal_eepromGet(ah, AR_EEP_PWR_TABLE_OFFSET, &pwr_table_offset); if (IS_EEP_MINOR_V2(ah)) { pdGainOverlap_t2 = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].pdGainOverlap; } else { pdGainOverlap_t2 = (uint16_t)(MS(OS_REG_READ(ah, AR_PHY_TPCRG5), AR_PHY_TPCRG5_PD_GAIN_OVERLAP)); } if (IEEE80211_IS_CHAN_2GHZ(chan)) { pCalBChans = pEepData->calFreqPier2G; numPiers = AR5416_NUM_2G_CAL_PIERS; } else { pCalBChans = pEepData->calFreqPier5G; numPiers = AR5416_NUM_5G_CAL_PIERS; } /* If OLC is being done, set the init PDADC value appropriately */ if (IEEE80211_IS_CHAN_2GHZ(chan) && AR_SREV_MERLIN_20_OR_LATER(ah) && ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) { struct calDataPerFreq *pRawDataset = pEepData->calPierData2G[0]; AH5416(ah)->initPDADC = ((struct calDataPerFreqOpLoop *) pRawDataset)->vpdPdg[0][0]; } else { /* * XXX ath9k doesn't clear this for 5ghz mode if * it were set in 2ghz mode before! * The Merlin OLC temperature compensation code * uses this to calculate the PDADC delta during * calibration ; 0 here effectively stops the * temperature compensation calibration from - * occuring. + * occurring. */ AH5416(ah)->initPDADC = 0; } /* Calculate the value of xpdgains from the xpdGain Mask */ numXpdGain = ar5416GetXpdGainValues(ah, xpdMask, xpdGainValues); /* Write the detector gain biases and their number */ ar5416WriteDetectorGainBiases(ah, numXpdGain, xpdGainValues); for (i = 0; i < AR5416_MAX_CHAINS; i++) { regChainOffset = ar5416GetRegChainOffset(ah, i); if (pEepData->baseEepHeader.txMask & (1 << i)) { uint16_t diff; if (IEEE80211_IS_CHAN_2GHZ(chan)) { pRawDataset = pEepData->calPierData2G[i]; } else { pRawDataset = pEepData->calPierData5G[i]; } /* Fetch the gain boundaries and the PDADC values */ if (AR_SREV_MERLIN_20_OR_LATER(ah) && ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) { uint8_t pcdacIdx; uint8_t txPower; ar9280olcGetTxGainIndex(ah, chan, (struct calDataPerFreqOpLoop *) pRawDataset, pCalBChans, numPiers, &txPower, &pcdacIdx); ar9280olcGetPDADCs(ah, pcdacIdx, txPower / 2, pdadcValues); } else { ar5416GetGainBoundariesAndPdadcs(ah, chan, pRawDataset, pCalBChans, numPiers, pdGainOverlap_t2, &tMinCalPower, gainBoundaries, pdadcValues, numXpdGain); } /* * Prior to writing the boundaries or the pdadc vs. power table * into the chip registers the default starting point on the pdadc * vs. power table needs to be checked and the curve boundaries * adjusted accordingly */ diff = ar9280ChangeGainBoundarySettings(ah, gainBoundaries, numXpdGain, pdGainOverlap_t2, pwr_table_offset, &diff); if ((i == 0) || AR_SREV_5416_V20_OR_LATER(ah)) { /* Set gain boundaries for either open- or closed-loop TPC */ if (AR_SREV_MERLIN_20_OR_LATER(ah) && ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) ar9280SetGainBoundariesOpenLoop(ah, i, pdGainOverlap_t2, gainBoundaries); else ar5416SetGainBoundariesClosedLoop(ah, i, pdGainOverlap_t2, gainBoundaries); } /* * If this is a board that has a pwrTableOffset that differs from * the default AR5416_PWR_TABLE_OFFSET_DB then the start of the * pdadc vs pwr table needs to be adjusted prior to writing to the * chip. */ ar9280AdjustPDADCValues(ah, pwr_table_offset, diff, pdadcValues); /* Write the power values into the baseband power table */ ar5416WritePdadcValues(ah, i, pdadcValues); } } *pTxPowerIndexOffset = 0; return AH_TRUE; } Index: head/sys/dev/ath/if_ath.c =================================================================== --- head/sys/dev/ath/if_ath.c (revision 298938) +++ head/sys/dev/ath/if_ath.c (revision 298939) @@ -1,6644 +1,6644 @@ /*- * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include __FBSDID("$FreeBSD$"); /* * Driver for the Atheros Wireless LAN controller. * * This software is derived from work of Atsushi Onoe; his contribution * is greatly appreciated. */ #include "opt_inet.h" #include "opt_ath.h" /* * This is needed for register operations which are performed * by the driver - eg, calls to ath_hal_gettsf32(). * * It's also required for any AH_DEBUG checks in here, eg the * module dependencies. */ #include "opt_ah.h" #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for mp_ncpus */ #include #include #include #include #include #include #include #include #include #include #include #ifdef IEEE80211_SUPPORT_SUPERG #include #endif #ifdef IEEE80211_SUPPORT_TDMA #include #endif #include #ifdef INET #include #include #endif #include #include /* XXX for softled */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef ATH_TX99_DIAG #include #endif #ifdef ATH_DEBUG_ALQ #include #endif /* * Only enable this if you're working on PS-POLL support. */ #define ATH_SW_PSQ /* * ATH_BCBUF determines the number of vap's that can transmit * beacons and also (currently) the number of vap's that can * have unique mac addresses/bssid. When staggering beacons * 4 is probably a good max as otherwise the beacons become * very closely spaced and there is limited time for cab q traffic * to go out. You can burst beacons instead but that is not good * for stations in power save and at some point you really want * another radio (and channel). * * The limit on the number of mac addresses is tied to our use of * the U/L bit and tracking addresses in a byte; it would be * worthwhile to allow more for applications like proxy sta. */ CTASSERT(ATH_BCBUF <= 8); static struct ieee80211vap *ath_vap_create(struct ieee80211com *, const char [IFNAMSIZ], int, enum ieee80211_opmode, int, const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN]); static void ath_vap_delete(struct ieee80211vap *); static int ath_init(struct ath_softc *); static void ath_stop(struct ath_softc *); static int ath_reset_vap(struct ieee80211vap *, u_long); static int ath_transmit(struct ieee80211com *, struct mbuf *); static int ath_media_change(struct ifnet *); static void ath_watchdog(void *); static void ath_parent(struct ieee80211com *); static void ath_fatal_proc(void *, int); static void ath_bmiss_vap(struct ieee80211vap *); static void ath_bmiss_proc(void *, int); static void ath_key_update_begin(struct ieee80211vap *); static void ath_key_update_end(struct ieee80211vap *); static void ath_update_mcast_hw(struct ath_softc *); static void ath_update_mcast(struct ieee80211com *); static void ath_update_promisc(struct ieee80211com *); static void ath_updateslot(struct ieee80211com *); static void ath_bstuck_proc(void *, int); static void ath_reset_proc(void *, int); static int ath_desc_alloc(struct ath_softc *); static void ath_desc_free(struct ath_softc *); static struct ieee80211_node *ath_node_alloc(struct ieee80211vap *, const uint8_t [IEEE80211_ADDR_LEN]); static void ath_node_cleanup(struct ieee80211_node *); static void ath_node_free(struct ieee80211_node *); static void ath_node_getsignal(const struct ieee80211_node *, int8_t *, int8_t *); static void ath_txq_init(struct ath_softc *sc, struct ath_txq *, int); static struct ath_txq *ath_txq_setup(struct ath_softc*, int qtype, int subtype); static int ath_tx_setup(struct ath_softc *, int, int); static void ath_tx_cleanupq(struct ath_softc *, struct ath_txq *); static void ath_tx_cleanup(struct ath_softc *); static int ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq, int dosched); static void ath_tx_proc_q0(void *, int); static void ath_tx_proc_q0123(void *, int); static void ath_tx_proc(void *, int); static void ath_txq_sched_tasklet(void *, int); static int ath_chan_set(struct ath_softc *, struct ieee80211_channel *); static void ath_chan_change(struct ath_softc *, struct ieee80211_channel *); static void ath_scan_start(struct ieee80211com *); static void ath_scan_end(struct ieee80211com *); static void ath_set_channel(struct ieee80211com *); #ifdef ATH_ENABLE_11N static void ath_update_chw(struct ieee80211com *); #endif /* ATH_ENABLE_11N */ static void ath_calibrate(void *); static int ath_newstate(struct ieee80211vap *, enum ieee80211_state, int); static void ath_setup_stationkey(struct ieee80211_node *); static void ath_newassoc(struct ieee80211_node *, int); static int ath_setregdomain(struct ieee80211com *, struct ieee80211_regdomain *, int, struct ieee80211_channel []); static void ath_getradiocaps(struct ieee80211com *, int, int *, struct ieee80211_channel []); static int ath_getchannels(struct ath_softc *); static int ath_rate_setup(struct ath_softc *, u_int mode); static void ath_setcurmode(struct ath_softc *, enum ieee80211_phymode); static void ath_announce(struct ath_softc *); static void ath_dfs_tasklet(void *, int); static void ath_node_powersave(struct ieee80211_node *, int); static int ath_node_set_tim(struct ieee80211_node *, int); static void ath_node_recv_pspoll(struct ieee80211_node *, struct mbuf *); #ifdef IEEE80211_SUPPORT_TDMA #include #endif SYSCTL_DECL(_hw_ath); /* XXX validate sysctl values */ static int ath_longcalinterval = 30; /* long cals every 30 secs */ SYSCTL_INT(_hw_ath, OID_AUTO, longcal, CTLFLAG_RW, &ath_longcalinterval, 0, "long chip calibration interval (secs)"); static int ath_shortcalinterval = 100; /* short cals every 100 ms */ SYSCTL_INT(_hw_ath, OID_AUTO, shortcal, CTLFLAG_RW, &ath_shortcalinterval, 0, "short chip calibration interval (msecs)"); static int ath_resetcalinterval = 20*60; /* reset cal state 20 mins */ SYSCTL_INT(_hw_ath, OID_AUTO, resetcal, CTLFLAG_RW, &ath_resetcalinterval, 0, "reset chip calibration results (secs)"); static int ath_anicalinterval = 100; /* ANI calibration - 100 msec */ SYSCTL_INT(_hw_ath, OID_AUTO, anical, CTLFLAG_RW, &ath_anicalinterval, 0, "ANI calibration (msecs)"); int ath_rxbuf = ATH_RXBUF; /* # rx buffers to allocate */ SYSCTL_INT(_hw_ath, OID_AUTO, rxbuf, CTLFLAG_RWTUN, &ath_rxbuf, 0, "rx buffers allocated"); int ath_txbuf = ATH_TXBUF; /* # tx buffers to allocate */ SYSCTL_INT(_hw_ath, OID_AUTO, txbuf, CTLFLAG_RWTUN, &ath_txbuf, 0, "tx buffers allocated"); int ath_txbuf_mgmt = ATH_MGMT_TXBUF; /* # mgmt tx buffers to allocate */ SYSCTL_INT(_hw_ath, OID_AUTO, txbuf_mgmt, CTLFLAG_RWTUN, &ath_txbuf_mgmt, 0, "tx (mgmt) buffers allocated"); int ath_bstuck_threshold = 4; /* max missed beacons */ SYSCTL_INT(_hw_ath, OID_AUTO, bstuck, CTLFLAG_RW, &ath_bstuck_threshold, 0, "max missed beacon xmits before chip reset"); MALLOC_DEFINE(M_ATHDEV, "athdev", "ath driver dma buffers"); void ath_legacy_attach_comp_func(struct ath_softc *sc) { /* * Special case certain configurations. Note the * CAB queue is handled by these specially so don't * include them when checking the txq setup mask. */ switch (sc->sc_txqsetup &~ (1<sc_cabq->axq_qnum)) { case 0x01: TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc_q0, sc); break; case 0x0f: TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc_q0123, sc); break; default: TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc, sc); break; } } /* * Set the target power mode. * * If this is called during a point in time where * the hardware is being programmed elsewhere, it will * simply store it away and update it when all current * uses of the hardware are completed. */ void _ath_power_setpower(struct ath_softc *sc, int power_state, const char *file, int line) { ATH_LOCK_ASSERT(sc); sc->sc_target_powerstate = power_state; DPRINTF(sc, ATH_DEBUG_PWRSAVE, "%s: (%s:%d) state=%d, refcnt=%d\n", __func__, file, line, power_state, sc->sc_powersave_refcnt); if (sc->sc_powersave_refcnt == 0 && power_state != sc->sc_cur_powerstate) { sc->sc_cur_powerstate = power_state; ath_hal_setpower(sc->sc_ah, power_state); /* * If the NIC is force-awake, then set the * self-gen frame state appropriately. * * If the nic is in network sleep or full-sleep, * we let the above call leave the self-gen * state as "sleep". */ if (sc->sc_cur_powerstate == HAL_PM_AWAKE && sc->sc_target_selfgen_state != HAL_PM_AWAKE) { ath_hal_setselfgenpower(sc->sc_ah, sc->sc_target_selfgen_state); } } } /* * Set the current self-generated frames state. * * This is separate from the target power mode. The chip may be * awake but the desired state is "sleep", so frames sent to the * destination has PWRMGT=1 in the 802.11 header. The NIC also * needs to know to set PWRMGT=1 in self-generated frames. */ void _ath_power_set_selfgen(struct ath_softc *sc, int power_state, const char *file, int line) { ATH_LOCK_ASSERT(sc); DPRINTF(sc, ATH_DEBUG_PWRSAVE, "%s: (%s:%d) state=%d, refcnt=%d\n", __func__, file, line, power_state, sc->sc_target_selfgen_state); sc->sc_target_selfgen_state = power_state; /* * If the NIC is force-awake, then set the power state. * Network-state and full-sleep will already transition it to * mark self-gen frames as sleeping - and we can't * guarantee the NIC is awake to program the self-gen frame * setting anyway. */ if (sc->sc_cur_powerstate == HAL_PM_AWAKE) { ath_hal_setselfgenpower(sc->sc_ah, power_state); } } /* * Set the hardware power mode and take a reference. * * This doesn't update the target power mode in the driver; * it just updates the hardware power state. * * XXX it should only ever force the hardware awake; it should * never be called to set it asleep. */ void _ath_power_set_power_state(struct ath_softc *sc, int power_state, const char *file, int line) { ATH_LOCK_ASSERT(sc); DPRINTF(sc, ATH_DEBUG_PWRSAVE, "%s: (%s:%d) state=%d, refcnt=%d\n", __func__, file, line, power_state, sc->sc_powersave_refcnt); sc->sc_powersave_refcnt++; if (power_state != sc->sc_cur_powerstate) { ath_hal_setpower(sc->sc_ah, power_state); sc->sc_cur_powerstate = power_state; /* * Adjust the self-gen powerstate if appropriate. */ if (sc->sc_cur_powerstate == HAL_PM_AWAKE && sc->sc_target_selfgen_state != HAL_PM_AWAKE) { ath_hal_setselfgenpower(sc->sc_ah, sc->sc_target_selfgen_state); } } } /* * Restore the power save mode to what it once was. * * This will decrement the reference counter and once it hits * zero, it'll restore the powersave state. */ void _ath_power_restore_power_state(struct ath_softc *sc, const char *file, int line) { ATH_LOCK_ASSERT(sc); DPRINTF(sc, ATH_DEBUG_PWRSAVE, "%s: (%s:%d) refcnt=%d, target state=%d\n", __func__, file, line, sc->sc_powersave_refcnt, sc->sc_target_powerstate); if (sc->sc_powersave_refcnt == 0) device_printf(sc->sc_dev, "%s: refcnt=0?\n", __func__); else sc->sc_powersave_refcnt--; if (sc->sc_powersave_refcnt == 0 && sc->sc_target_powerstate != sc->sc_cur_powerstate) { sc->sc_cur_powerstate = sc->sc_target_powerstate; ath_hal_setpower(sc->sc_ah, sc->sc_target_powerstate); } /* * Adjust the self-gen powerstate if appropriate. */ if (sc->sc_cur_powerstate == HAL_PM_AWAKE && sc->sc_target_selfgen_state != HAL_PM_AWAKE) { ath_hal_setselfgenpower(sc->sc_ah, sc->sc_target_selfgen_state); } } /* * Configure the initial HAL configuration values based on bus * specific parameters. * * Some PCI IDs and other information may need tweaking. * * XXX TODO: ath9k and the Atheros HAL only program comm2g_switch_enable * if BT antenna diversity isn't enabled. * * So, let's also figure out how to enable BT diversity for AR9485. */ static void ath_setup_hal_config(struct ath_softc *sc, HAL_OPS_CONFIG *ah_config) { /* XXX TODO: only for PCI devices? */ if (sc->sc_pci_devinfo & (ATH_PCI_CUS198 | ATH_PCI_CUS230)) { ah_config->ath_hal_ext_lna_ctl_gpio = 0x200; /* bit 9 */ ah_config->ath_hal_ext_atten_margin_cfg = AH_TRUE; ah_config->ath_hal_min_gainidx = AH_TRUE; ah_config->ath_hal_ant_ctrl_comm2g_switch_enable = 0x000bbb88; /* XXX low_rssi_thresh */ /* XXX fast_div_bias */ device_printf(sc->sc_dev, "configuring for %s\n", (sc->sc_pci_devinfo & ATH_PCI_CUS198) ? "CUS198" : "CUS230"); } if (sc->sc_pci_devinfo & ATH_PCI_CUS217) device_printf(sc->sc_dev, "CUS217 card detected\n"); if (sc->sc_pci_devinfo & ATH_PCI_CUS252) device_printf(sc->sc_dev, "CUS252 card detected\n"); if (sc->sc_pci_devinfo & ATH_PCI_AR9565_1ANT) device_printf(sc->sc_dev, "WB335 1-ANT card detected\n"); if (sc->sc_pci_devinfo & ATH_PCI_AR9565_2ANT) device_printf(sc->sc_dev, "WB335 2-ANT card detected\n"); if (sc->sc_pci_devinfo & ATH_PCI_KILLER) device_printf(sc->sc_dev, "Killer Wireless card detected\n"); #if 0 /* * Some WB335 cards do not support antenna diversity. Since * we use a hardcoded value for AR9565 instead of using the * EEPROM/OTP data, remove the combining feature from * the HW capabilities bitmap. */ if (sc->sc_pci_devinfo & (ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_AR9565_2ANT)) { if (!(sc->sc_pci_devinfo & ATH9K_PCI_BT_ANT_DIV)) pCap->hw_caps &= ~ATH9K_HW_CAP_ANT_DIV_COMB; } if (sc->sc_pci_devinfo & ATH9K_PCI_BT_ANT_DIV) { pCap->hw_caps |= ATH9K_HW_CAP_BT_ANT_DIV; device_printf(sc->sc_dev, "Set BT/WLAN RX diversity capability\n"); } #endif if (sc->sc_pci_devinfo & ATH_PCI_D3_L1_WAR) { ah_config->ath_hal_pcie_waen = 0x0040473b; device_printf(sc->sc_dev, "Enable WAR for ASPM D3/L1\n"); } #if 0 if (sc->sc_pci_devinfo & ATH9K_PCI_NO_PLL_PWRSAVE) { ah->config.no_pll_pwrsave = true; device_printf(sc->sc_dev, "Disable PLL PowerSave\n"); } #endif } /* * Attempt to fetch the MAC address from the kernel environment. * * Returns 0, macaddr in macaddr if successful; -1 otherwise. */ static int ath_fetch_mac_kenv(struct ath_softc *sc, uint8_t *macaddr) { char devid_str[32]; int local_mac = 0; char *local_macstr; /* * Fetch from the kenv rather than using hints. * * Hints would be nice but the transition to dynamic * hints/kenv doesn't happen early enough for this * to work reliably (eg on anything embedded.) */ snprintf(devid_str, 32, "hint.%s.%d.macaddr", device_get_name(sc->sc_dev), device_get_unit(sc->sc_dev)); if ((local_macstr = kern_getenv(devid_str)) != NULL) { uint32_t tmpmac[ETHER_ADDR_LEN]; int count; int i; /* Have a MAC address; should use it */ device_printf(sc->sc_dev, "Overriding MAC address from environment: '%s'\n", local_macstr); /* Extract out the MAC address */ count = sscanf(local_macstr, "%x%*c%x%*c%x%*c%x%*c%x%*c%x", &tmpmac[0], &tmpmac[1], &tmpmac[2], &tmpmac[3], &tmpmac[4], &tmpmac[5]); if (count == 6) { /* Valid! */ local_mac = 1; for (i = 0; i < ETHER_ADDR_LEN; i++) macaddr[i] = tmpmac[i]; } /* Done! */ freeenv(local_macstr); local_macstr = NULL; } if (local_mac) return (0); return (-1); } #define HAL_MODE_HT20 (HAL_MODE_11NG_HT20 | HAL_MODE_11NA_HT20) #define HAL_MODE_HT40 \ (HAL_MODE_11NG_HT40PLUS | HAL_MODE_11NG_HT40MINUS | \ HAL_MODE_11NA_HT40PLUS | HAL_MODE_11NA_HT40MINUS) int ath_attach(u_int16_t devid, struct ath_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = NULL; HAL_STATUS status; int error = 0, i; u_int wmodes; int rx_chainmask, tx_chainmask; HAL_OPS_CONFIG ah_config; DPRINTF(sc, ATH_DEBUG_ANY, "%s: devid 0x%x\n", __func__, devid); ic->ic_softc = sc; ic->ic_name = device_get_nameunit(sc->sc_dev); /* * Configure the initial configuration data. * * This is stuff that may be needed early during attach * rather than done via configuration calls later. */ bzero(&ah_config, sizeof(ah_config)); ath_setup_hal_config(sc, &ah_config); ah = ath_hal_attach(devid, sc, sc->sc_st, sc->sc_sh, sc->sc_eepromdata, &ah_config, &status); if (ah == NULL) { device_printf(sc->sc_dev, "unable to attach hardware; HAL status %u\n", status); error = ENXIO; goto bad; } sc->sc_ah = ah; sc->sc_invalid = 0; /* ready to go, enable interrupt handling */ #ifdef ATH_DEBUG sc->sc_debug = ath_debug; #endif /* * Setup the DMA/EDMA functions based on the current * hardware support. * * This is required before the descriptors are allocated. */ if (ath_hal_hasedma(sc->sc_ah)) { sc->sc_isedma = 1; ath_recv_setup_edma(sc); ath_xmit_setup_edma(sc); } else { ath_recv_setup_legacy(sc); ath_xmit_setup_legacy(sc); } if (ath_hal_hasmybeacon(sc->sc_ah)) { sc->sc_do_mybeacon = 1; } /* * Check if the MAC has multi-rate retry support. * We do this by trying to setup a fake extended * descriptor. MAC's that don't have support will * return false w/o doing anything. MAC's that do * support it will return true w/o doing anything. */ sc->sc_mrretry = ath_hal_setupxtxdesc(ah, NULL, 0,0, 0,0, 0,0); /* * Check if the device has hardware counters for PHY * errors. If so we need to enable the MIB interrupt * so we can act on stat triggers. */ if (ath_hal_hwphycounters(ah)) sc->sc_needmib = 1; /* * Get the hardware key cache size. */ sc->sc_keymax = ath_hal_keycachesize(ah); if (sc->sc_keymax > ATH_KEYMAX) { device_printf(sc->sc_dev, "Warning, using only %u of %u key cache slots\n", ATH_KEYMAX, sc->sc_keymax); sc->sc_keymax = ATH_KEYMAX; } /* * Reset the key cache since some parts do not * reset the contents on initial power up. */ for (i = 0; i < sc->sc_keymax; i++) ath_hal_keyreset(ah, i); /* * Collect the default channel list. */ error = ath_getchannels(sc); if (error != 0) goto bad; /* * Setup rate tables for all potential media types. */ ath_rate_setup(sc, IEEE80211_MODE_11A); ath_rate_setup(sc, IEEE80211_MODE_11B); ath_rate_setup(sc, IEEE80211_MODE_11G); ath_rate_setup(sc, IEEE80211_MODE_TURBO_A); ath_rate_setup(sc, IEEE80211_MODE_TURBO_G); ath_rate_setup(sc, IEEE80211_MODE_STURBO_A); ath_rate_setup(sc, IEEE80211_MODE_11NA); ath_rate_setup(sc, IEEE80211_MODE_11NG); ath_rate_setup(sc, IEEE80211_MODE_HALF); ath_rate_setup(sc, IEEE80211_MODE_QUARTER); /* NB: setup here so ath_rate_update is happy */ ath_setcurmode(sc, IEEE80211_MODE_11A); /* * Allocate TX descriptors and populate the lists. */ error = ath_desc_alloc(sc); if (error != 0) { device_printf(sc->sc_dev, "failed to allocate TX descriptors: %d\n", error); goto bad; } error = ath_txdma_setup(sc); if (error != 0) { device_printf(sc->sc_dev, "failed to allocate TX descriptors: %d\n", error); goto bad; } /* * Allocate RX descriptors and populate the lists. */ error = ath_rxdma_setup(sc); if (error != 0) { device_printf(sc->sc_dev, "failed to allocate RX descriptors: %d\n", error); goto bad; } callout_init_mtx(&sc->sc_cal_ch, &sc->sc_mtx, 0); callout_init_mtx(&sc->sc_wd_ch, &sc->sc_mtx, 0); ATH_TXBUF_LOCK_INIT(sc); sc->sc_tq = taskqueue_create("ath_taskq", M_NOWAIT, taskqueue_thread_enqueue, &sc->sc_tq); taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq", device_get_nameunit(sc->sc_dev)); TASK_INIT(&sc->sc_rxtask, 0, sc->sc_rx.recv_tasklet, sc); TASK_INIT(&sc->sc_bmisstask, 0, ath_bmiss_proc, sc); TASK_INIT(&sc->sc_bstucktask,0, ath_bstuck_proc, sc); TASK_INIT(&sc->sc_resettask,0, ath_reset_proc, sc); TASK_INIT(&sc->sc_txqtask, 0, ath_txq_sched_tasklet, sc); TASK_INIT(&sc->sc_fataltask, 0, ath_fatal_proc, sc); /* * Allocate hardware transmit queues: one queue for * beacon frames and one data queue for each QoS * priority. Note that the hal handles resetting * these queues at the needed time. * * XXX PS-Poll */ sc->sc_bhalq = ath_beaconq_setup(sc); if (sc->sc_bhalq == (u_int) -1) { device_printf(sc->sc_dev, "unable to setup a beacon xmit queue!\n"); error = EIO; goto bad2; } sc->sc_cabq = ath_txq_setup(sc, HAL_TX_QUEUE_CAB, 0); if (sc->sc_cabq == NULL) { device_printf(sc->sc_dev, "unable to setup CAB xmit queue!\n"); error = EIO; goto bad2; } /* NB: insure BK queue is the lowest priority h/w queue */ if (!ath_tx_setup(sc, WME_AC_BK, HAL_WME_AC_BK)) { device_printf(sc->sc_dev, "unable to setup xmit queue for %s traffic!\n", ieee80211_wme_acnames[WME_AC_BK]); error = EIO; goto bad2; } if (!ath_tx_setup(sc, WME_AC_BE, HAL_WME_AC_BE) || !ath_tx_setup(sc, WME_AC_VI, HAL_WME_AC_VI) || !ath_tx_setup(sc, WME_AC_VO, HAL_WME_AC_VO)) { /* * Not enough hardware tx queues to properly do WME; * just punt and assign them all to the same h/w queue. * We could do a better job of this if, for example, * we allocate queues when we switch from station to * AP mode. */ if (sc->sc_ac2q[WME_AC_VI] != NULL) ath_tx_cleanupq(sc, sc->sc_ac2q[WME_AC_VI]); if (sc->sc_ac2q[WME_AC_BE] != NULL) ath_tx_cleanupq(sc, sc->sc_ac2q[WME_AC_BE]); sc->sc_ac2q[WME_AC_BE] = sc->sc_ac2q[WME_AC_BK]; sc->sc_ac2q[WME_AC_VI] = sc->sc_ac2q[WME_AC_BK]; sc->sc_ac2q[WME_AC_VO] = sc->sc_ac2q[WME_AC_BK]; } /* * Attach the TX completion function. * * The non-EDMA chips may have some special case optimisations; * this method gives everyone a chance to attach cleanly. */ sc->sc_tx.xmit_attach_comp_func(sc); /* * Setup rate control. Some rate control modules * call back to change the anntena state so expose * the necessary entry points. * XXX maybe belongs in struct ath_ratectrl? */ sc->sc_setdefantenna = ath_setdefantenna; sc->sc_rc = ath_rate_attach(sc); if (sc->sc_rc == NULL) { error = EIO; goto bad2; } /* Attach DFS module */ if (! ath_dfs_attach(sc)) { device_printf(sc->sc_dev, "%s: unable to attach DFS\n", __func__); error = EIO; goto bad2; } /* Attach spectral module */ if (ath_spectral_attach(sc) < 0) { device_printf(sc->sc_dev, "%s: unable to attach spectral\n", __func__); error = EIO; goto bad2; } /* Attach bluetooth coexistence module */ if (ath_btcoex_attach(sc) < 0) { device_printf(sc->sc_dev, "%s: unable to attach bluetooth coexistence\n", __func__); error = EIO; goto bad2; } /* Attach LNA diversity module */ if (ath_lna_div_attach(sc) < 0) { device_printf(sc->sc_dev, "%s: unable to attach LNA diversity\n", __func__); error = EIO; goto bad2; } /* Start DFS processing tasklet */ TASK_INIT(&sc->sc_dfstask, 0, ath_dfs_tasklet, sc); /* Configure LED state */ sc->sc_blinking = 0; sc->sc_ledstate = 1; sc->sc_ledon = 0; /* low true */ sc->sc_ledidle = (2700*hz)/1000; /* 2.7sec */ callout_init(&sc->sc_ledtimer, 1); /* * Don't setup hardware-based blinking. * * Although some NICs may have this configured in the * default reset register values, the user may wish * to alter which pins have which function. * * The reference driver attaches the MAC network LED to GPIO1 and * the MAC power LED to GPIO2. However, the DWA-552 cardbus * NIC has these reversed. */ sc->sc_hardled = (1 == 0); sc->sc_led_net_pin = -1; sc->sc_led_pwr_pin = -1; /* * Auto-enable soft led processing for IBM cards and for * 5211 minipci cards. Users can also manually enable/disable * support with a sysctl. */ sc->sc_softled = (devid == AR5212_DEVID_IBM || devid == AR5211_DEVID); ath_led_config(sc); ath_hal_setledstate(ah, HAL_LED_INIT); /* XXX not right but it's not used anywhere important */ ic->ic_phytype = IEEE80211_T_OFDM; ic->ic_opmode = IEEE80211_M_STA; ic->ic_caps = IEEE80211_C_STA /* station mode */ | IEEE80211_C_IBSS /* ibss, nee adhoc, mode */ | IEEE80211_C_HOSTAP /* hostap mode */ | IEEE80211_C_MONITOR /* monitor mode */ | IEEE80211_C_AHDEMO /* adhoc demo mode */ | IEEE80211_C_WDS /* 4-address traffic works */ | IEEE80211_C_MBSS /* mesh point link mode */ | IEEE80211_C_SHPREAMBLE /* short preamble supported */ | IEEE80211_C_SHSLOT /* short slot time supported */ | IEEE80211_C_WPA /* capable of WPA1+WPA2 */ #ifndef ATH_ENABLE_11N | IEEE80211_C_BGSCAN /* capable of bg scanning */ #endif | IEEE80211_C_TXFRAG /* handle tx frags */ #ifdef ATH_ENABLE_DFS | IEEE80211_C_DFS /* Enable radar detection */ #endif | IEEE80211_C_PMGT /* Station side power mgmt */ | IEEE80211_C_SWSLEEP ; /* * Query the hal to figure out h/w crypto support. */ if (ath_hal_ciphersupported(ah, HAL_CIPHER_WEP)) ic->ic_cryptocaps |= IEEE80211_CRYPTO_WEP; if (ath_hal_ciphersupported(ah, HAL_CIPHER_AES_OCB)) ic->ic_cryptocaps |= IEEE80211_CRYPTO_AES_OCB; if (ath_hal_ciphersupported(ah, HAL_CIPHER_AES_CCM)) ic->ic_cryptocaps |= IEEE80211_CRYPTO_AES_CCM; if (ath_hal_ciphersupported(ah, HAL_CIPHER_CKIP)) ic->ic_cryptocaps |= IEEE80211_CRYPTO_CKIP; if (ath_hal_ciphersupported(ah, HAL_CIPHER_TKIP)) { ic->ic_cryptocaps |= IEEE80211_CRYPTO_TKIP; /* * Check if h/w does the MIC and/or whether the * separate key cache entries are required to * handle both tx+rx MIC keys. */ if (ath_hal_ciphersupported(ah, HAL_CIPHER_MIC)) ic->ic_cryptocaps |= IEEE80211_CRYPTO_TKIPMIC; /* * If the h/w supports storing tx+rx MIC keys * in one cache slot automatically enable use. */ if (ath_hal_hastkipsplit(ah) || !ath_hal_settkipsplit(ah, AH_FALSE)) sc->sc_splitmic = 1; /* * If the h/w can do TKIP MIC together with WME then * we use it; otherwise we force the MIC to be done * in software by the net80211 layer. */ if (ath_hal_haswmetkipmic(ah)) sc->sc_wmetkipmic = 1; } sc->sc_hasclrkey = ath_hal_ciphersupported(ah, HAL_CIPHER_CLR); /* * Check for multicast key search support. */ if (ath_hal_hasmcastkeysearch(sc->sc_ah) && !ath_hal_getmcastkeysearch(sc->sc_ah)) { ath_hal_setmcastkeysearch(sc->sc_ah, 1); } sc->sc_mcastkey = ath_hal_getmcastkeysearch(ah); /* * Mark key cache slots associated with global keys * as in use. If we knew TKIP was not to be used we * could leave the +32, +64, and +32+64 slots free. */ for (i = 0; i < IEEE80211_WEP_NKID; i++) { setbit(sc->sc_keymap, i); setbit(sc->sc_keymap, i+64); if (sc->sc_splitmic) { setbit(sc->sc_keymap, i+32); setbit(sc->sc_keymap, i+32+64); } } /* * TPC support can be done either with a global cap or * per-packet support. The latter is not available on * all parts. We're a bit pedantic here as all parts * support a global cap. */ if (ath_hal_hastpc(ah) || ath_hal_hastxpowlimit(ah)) ic->ic_caps |= IEEE80211_C_TXPMGT; /* * Mark WME capability only if we have sufficient * hardware queues to do proper priority scheduling. */ if (sc->sc_ac2q[WME_AC_BE] != sc->sc_ac2q[WME_AC_BK]) ic->ic_caps |= IEEE80211_C_WME; /* * Check for misc other capabilities. */ if (ath_hal_hasbursting(ah)) ic->ic_caps |= IEEE80211_C_BURST; sc->sc_hasbmask = ath_hal_hasbssidmask(ah); sc->sc_hasbmatch = ath_hal_hasbssidmatch(ah); sc->sc_hastsfadd = ath_hal_hastsfadjust(ah); sc->sc_rxslink = ath_hal_self_linked_final_rxdesc(ah); sc->sc_rxtsf32 = ath_hal_has_long_rxdesc_tsf(ah); sc->sc_hasenforcetxop = ath_hal_hasenforcetxop(ah); sc->sc_rx_lnamixer = ath_hal_hasrxlnamixer(ah); sc->sc_hasdivcomb = ath_hal_hasdivantcomb(ah); if (ath_hal_hasfastframes(ah)) ic->ic_caps |= IEEE80211_C_FF; wmodes = ath_hal_getwirelessmodes(ah); if (wmodes & (HAL_MODE_108G|HAL_MODE_TURBO)) ic->ic_caps |= IEEE80211_C_TURBOP; #ifdef IEEE80211_SUPPORT_TDMA if (ath_hal_macversion(ah) > 0x78) { ic->ic_caps |= IEEE80211_C_TDMA; /* capable of TDMA */ ic->ic_tdma_update = ath_tdma_update; } #endif /* * TODO: enforce that at least this many frames are available * in the txbuf list before allowing data frames (raw or * otherwise) to be transmitted. */ sc->sc_txq_data_minfree = 10; /* * Leave this as default to maintain legacy behaviour. * Shortening the cabq/mcastq may end up causing some * undesirable behaviour. */ sc->sc_txq_mcastq_maxdepth = ath_txbuf; /* * How deep can the node software TX queue get whilst it's asleep. */ sc->sc_txq_node_psq_maxdepth = 16; /* * Default the maximum queue depth for a given node * to 1/4'th the TX buffers, or 64, whichever * is larger. */ sc->sc_txq_node_maxdepth = MAX(64, ath_txbuf / 4); /* Enable CABQ by default */ sc->sc_cabq_enable = 1; /* * Allow the TX and RX chainmasks to be overridden by * environment variables and/or device.hints. * * This must be done early - before the hardware is * calibrated or before the 802.11n stream calculation * is done. */ if (resource_int_value(device_get_name(sc->sc_dev), device_get_unit(sc->sc_dev), "rx_chainmask", &rx_chainmask) == 0) { device_printf(sc->sc_dev, "Setting RX chainmask to 0x%x\n", rx_chainmask); (void) ath_hal_setrxchainmask(sc->sc_ah, rx_chainmask); } if (resource_int_value(device_get_name(sc->sc_dev), device_get_unit(sc->sc_dev), "tx_chainmask", &tx_chainmask) == 0) { device_printf(sc->sc_dev, "Setting TX chainmask to 0x%x\n", tx_chainmask); (void) ath_hal_settxchainmask(sc->sc_ah, tx_chainmask); } /* * Query the TX/RX chainmask configuration. * * This is only relevant for 11n devices. */ ath_hal_getrxchainmask(ah, &sc->sc_rxchainmask); ath_hal_gettxchainmask(ah, &sc->sc_txchainmask); /* * Disable MRR with protected frames by default. * Only 802.11n series NICs can handle this. */ sc->sc_mrrprot = 0; /* XXX should be a capability */ /* * Query the enterprise mode information the HAL. */ if (ath_hal_getcapability(ah, HAL_CAP_ENTERPRISE_MODE, 0, &sc->sc_ent_cfg) == HAL_OK) sc->sc_use_ent = 1; #ifdef ATH_ENABLE_11N /* * Query HT capabilities */ if (ath_hal_getcapability(ah, HAL_CAP_HT, 0, NULL) == HAL_OK && (wmodes & (HAL_MODE_HT20 | HAL_MODE_HT40))) { uint32_t rxs, txs; uint32_t ldpc; device_printf(sc->sc_dev, "[HT] enabling HT modes\n"); sc->sc_mrrprot = 1; /* XXX should be a capability */ ic->ic_htcaps = IEEE80211_HTC_HT /* HT operation */ | IEEE80211_HTC_AMPDU /* A-MPDU tx/rx */ | IEEE80211_HTC_AMSDU /* A-MSDU tx/rx */ | IEEE80211_HTCAP_MAXAMSDU_3839 /* max A-MSDU length */ | IEEE80211_HTCAP_SMPS_OFF; /* SM power save off */ /* * Enable short-GI for HT20 only if the hardware * advertises support. * Notably, anything earlier than the AR9287 doesn't. */ if ((ath_hal_getcapability(ah, HAL_CAP_HT20_SGI, 0, NULL) == HAL_OK) && (wmodes & HAL_MODE_HT20)) { device_printf(sc->sc_dev, "[HT] enabling short-GI in 20MHz mode\n"); ic->ic_htcaps |= IEEE80211_HTCAP_SHORTGI20; } if (wmodes & HAL_MODE_HT40) ic->ic_htcaps |= IEEE80211_HTCAP_CHWIDTH40 | IEEE80211_HTCAP_SHORTGI40; /* * TX/RX streams need to be taken into account when * negotiating which MCS rates it'll receive and * what MCS rates are available for TX. */ (void) ath_hal_getcapability(ah, HAL_CAP_STREAMS, 0, &txs); (void) ath_hal_getcapability(ah, HAL_CAP_STREAMS, 1, &rxs); ic->ic_txstream = txs; ic->ic_rxstream = rxs; /* * Setup TX and RX STBC based on what the HAL allows and * the currently configured chainmask set. * Ie - don't enable STBC TX if only one chain is enabled. * STBC RX is fine on a single RX chain; it just won't * provide any real benefit. */ if (ath_hal_getcapability(ah, HAL_CAP_RX_STBC, 0, NULL) == HAL_OK) { sc->sc_rx_stbc = 1; device_printf(sc->sc_dev, "[HT] 1 stream STBC receive enabled\n"); ic->ic_htcaps |= IEEE80211_HTCAP_RXSTBC_1STREAM; } if (txs > 1 && ath_hal_getcapability(ah, HAL_CAP_TX_STBC, 0, NULL) == HAL_OK) { sc->sc_tx_stbc = 1; device_printf(sc->sc_dev, "[HT] 1 stream STBC transmit enabled\n"); ic->ic_htcaps |= IEEE80211_HTCAP_TXSTBC; } (void) ath_hal_getcapability(ah, HAL_CAP_RTS_AGGR_LIMIT, 1, &sc->sc_rts_aggr_limit); if (sc->sc_rts_aggr_limit != (64 * 1024)) device_printf(sc->sc_dev, "[HT] RTS aggregates limited to %d KiB\n", sc->sc_rts_aggr_limit / 1024); /* * LDPC */ if ((ath_hal_getcapability(ah, HAL_CAP_LDPC, 0, &ldpc)) == HAL_OK && (ldpc == 1)) { sc->sc_has_ldpc = 1; device_printf(sc->sc_dev, "[HT] LDPC transmit/receive enabled\n"); ic->ic_htcaps |= IEEE80211_HTCAP_LDPC; } device_printf(sc->sc_dev, "[HT] %d RX streams; %d TX streams\n", rxs, txs); } #endif /* * Initial aggregation settings. */ sc->sc_hwq_limit_aggr = ATH_AGGR_MIN_QDEPTH; sc->sc_hwq_limit_nonaggr = ATH_NONAGGR_MIN_QDEPTH; sc->sc_tid_hwq_lo = ATH_AGGR_SCHED_LOW; sc->sc_tid_hwq_hi = ATH_AGGR_SCHED_HIGH; sc->sc_aggr_limit = ATH_AGGR_MAXSIZE; sc->sc_delim_min_pad = 0; /* * Check if the hardware requires PCI register serialisation. * Some of the Owl based MACs require this. */ if (mp_ncpus > 1 && ath_hal_getcapability(ah, HAL_CAP_SERIALISE_WAR, 0, NULL) == HAL_OK) { sc->sc_ah->ah_config.ah_serialise_reg_war = 1; device_printf(sc->sc_dev, "Enabling register serialisation\n"); } /* * Initialise the deferred completed RX buffer list. */ TAILQ_INIT(&sc->sc_rx_rxlist[HAL_RX_QUEUE_HP]); TAILQ_INIT(&sc->sc_rx_rxlist[HAL_RX_QUEUE_LP]); /* * Indicate we need the 802.11 header padded to a * 32-bit boundary for 4-address and QoS frames. */ ic->ic_flags |= IEEE80211_F_DATAPAD; /* * Query the hal about antenna support. */ sc->sc_defant = ath_hal_getdefantenna(ah); /* * Not all chips have the VEOL support we want to * use with IBSS beacons; check here for it. */ sc->sc_hasveol = ath_hal_hasveol(ah); /* get mac address from kenv first, then hardware */ if (ath_fetch_mac_kenv(sc, ic->ic_macaddr) == 0) { /* Tell the HAL now about the new MAC */ ath_hal_setmac(ah, ic->ic_macaddr); } else { ath_hal_getmac(ah, ic->ic_macaddr); } if (sc->sc_hasbmask) ath_hal_getbssidmask(ah, sc->sc_hwbssidmask); /* NB: used to size node table key mapping array */ ic->ic_max_keyix = sc->sc_keymax; /* call MI attach routine. */ ieee80211_ifattach(ic); ic->ic_setregdomain = ath_setregdomain; ic->ic_getradiocaps = ath_getradiocaps; sc->sc_opmode = HAL_M_STA; /* override default methods */ ic->ic_ioctl = ath_ioctl; ic->ic_parent = ath_parent; ic->ic_transmit = ath_transmit; ic->ic_newassoc = ath_newassoc; ic->ic_updateslot = ath_updateslot; ic->ic_wme.wme_update = ath_wme_update; ic->ic_vap_create = ath_vap_create; ic->ic_vap_delete = ath_vap_delete; ic->ic_raw_xmit = ath_raw_xmit; ic->ic_update_mcast = ath_update_mcast; ic->ic_update_promisc = ath_update_promisc; ic->ic_node_alloc = ath_node_alloc; sc->sc_node_free = ic->ic_node_free; ic->ic_node_free = ath_node_free; sc->sc_node_cleanup = ic->ic_node_cleanup; ic->ic_node_cleanup = ath_node_cleanup; ic->ic_node_getsignal = ath_node_getsignal; ic->ic_scan_start = ath_scan_start; ic->ic_scan_end = ath_scan_end; ic->ic_set_channel = ath_set_channel; #ifdef ATH_ENABLE_11N /* 802.11n specific - but just override anyway */ sc->sc_addba_request = ic->ic_addba_request; sc->sc_addba_response = ic->ic_addba_response; sc->sc_addba_stop = ic->ic_addba_stop; sc->sc_bar_response = ic->ic_bar_response; sc->sc_addba_response_timeout = ic->ic_addba_response_timeout; ic->ic_addba_request = ath_addba_request; ic->ic_addba_response = ath_addba_response; ic->ic_addba_response_timeout = ath_addba_response_timeout; ic->ic_addba_stop = ath_addba_stop; ic->ic_bar_response = ath_bar_response; ic->ic_update_chw = ath_update_chw; #endif /* ATH_ENABLE_11N */ #ifdef ATH_ENABLE_RADIOTAP_VENDOR_EXT /* * There's one vendor bitmap entry in the RX radiotap * header; make sure that's taken into account. */ ieee80211_radiotap_attachv(ic, &sc->sc_tx_th.wt_ihdr, sizeof(sc->sc_tx_th), 0, ATH_TX_RADIOTAP_PRESENT, &sc->sc_rx_th.wr_ihdr, sizeof(sc->sc_rx_th), 1, ATH_RX_RADIOTAP_PRESENT); #else /* * No vendor bitmap/extensions are present. */ ieee80211_radiotap_attach(ic, &sc->sc_tx_th.wt_ihdr, sizeof(sc->sc_tx_th), ATH_TX_RADIOTAP_PRESENT, &sc->sc_rx_th.wr_ihdr, sizeof(sc->sc_rx_th), ATH_RX_RADIOTAP_PRESENT); #endif /* ATH_ENABLE_RADIOTAP_VENDOR_EXT */ /* * Setup the ALQ logging if required */ #ifdef ATH_DEBUG_ALQ if_ath_alq_init(&sc->sc_alq, device_get_nameunit(sc->sc_dev)); if_ath_alq_setcfg(&sc->sc_alq, sc->sc_ah->ah_macVersion, sc->sc_ah->ah_macRev, sc->sc_ah->ah_phyRev, sc->sc_ah->ah_magic); #endif /* * Setup dynamic sysctl's now that country code and * regdomain are available from the hal. */ ath_sysctlattach(sc); ath_sysctl_stats_attach(sc); ath_sysctl_hal_attach(sc); if (bootverbose) ieee80211_announce(ic); ath_announce(sc); /* * Put it to sleep for now. */ ATH_LOCK(sc); ath_power_setpower(sc, HAL_PM_FULL_SLEEP); ATH_UNLOCK(sc); return 0; bad2: ath_tx_cleanup(sc); ath_desc_free(sc); ath_txdma_teardown(sc); ath_rxdma_teardown(sc); bad: if (ah) ath_hal_detach(ah); sc->sc_invalid = 1; return error; } int ath_detach(struct ath_softc *sc) { /* * NB: the order of these is important: * o stop the chip so no more interrupts will fire * o call the 802.11 layer before detaching the hal to * insure callbacks into the driver to delete global * key cache entries can be handled * o free the taskqueue which drains any pending tasks * o reclaim the tx queue data structures after calling * the 802.11 layer as we'll get called back to reclaim * node state and potentially want to use them * o to cleanup the tx queues the hal is called, so detach * it last * Other than that, it's straightforward... */ /* * XXX Wake the hardware up first. ath_stop() will still * wake it up first, but I'd rather do it here just to * ensure it's awake. */ ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ath_power_setpower(sc, HAL_PM_AWAKE); /* * Stop things cleanly. */ ath_stop(sc); ATH_UNLOCK(sc); ieee80211_ifdetach(&sc->sc_ic); taskqueue_free(sc->sc_tq); #ifdef ATH_TX99_DIAG if (sc->sc_tx99 != NULL) sc->sc_tx99->detach(sc->sc_tx99); #endif ath_rate_detach(sc->sc_rc); #ifdef ATH_DEBUG_ALQ if_ath_alq_tidyup(&sc->sc_alq); #endif ath_lna_div_detach(sc); ath_btcoex_detach(sc); ath_spectral_detach(sc); ath_dfs_detach(sc); ath_desc_free(sc); ath_txdma_teardown(sc); ath_rxdma_teardown(sc); ath_tx_cleanup(sc); ath_hal_detach(sc->sc_ah); /* NB: sets chip in full sleep */ return 0; } /* * MAC address handling for multiple BSS on the same radio. * The first vap uses the MAC address from the EEPROM. For * subsequent vap's we set the U/L bit (bit 1) in the MAC * address and use the next six bits as an index. */ static void assign_address(struct ath_softc *sc, uint8_t mac[IEEE80211_ADDR_LEN], int clone) { int i; if (clone && sc->sc_hasbmask) { /* NB: we only do this if h/w supports multiple bssid */ for (i = 0; i < 8; i++) if ((sc->sc_bssidmask & (1<sc_bssidmask |= 1<sc_hwbssidmask[0] &= ~mac[0]; if (i == 0) sc->sc_nbssid0++; } static void reclaim_address(struct ath_softc *sc, const uint8_t mac[IEEE80211_ADDR_LEN]) { int i = mac[0] >> 2; uint8_t mask; if (i != 0 || --sc->sc_nbssid0 == 0) { sc->sc_bssidmask &= ~(1<sc_bssidmask & (1<sc_hwbssidmask[0] |= mask; } } /* * Assign a beacon xmit slot. We try to space out * assignments so when beacons are staggered the * traffic coming out of the cab q has maximal time * to go out before the next beacon is scheduled. */ static int assign_bslot(struct ath_softc *sc) { u_int slot, free; free = 0; for (slot = 0; slot < ATH_BCBUF; slot++) if (sc->sc_bslot[slot] == NULL) { if (sc->sc_bslot[(slot+1)%ATH_BCBUF] == NULL && sc->sc_bslot[(slot-1)%ATH_BCBUF] == NULL) return slot; free = slot; /* NB: keep looking for a double slot */ } return free; } static struct ieee80211vap * ath_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, enum ieee80211_opmode opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac0[IEEE80211_ADDR_LEN]) { struct ath_softc *sc = ic->ic_softc; struct ath_vap *avp; struct ieee80211vap *vap; uint8_t mac[IEEE80211_ADDR_LEN]; int needbeacon, error; enum ieee80211_opmode ic_opmode; avp = malloc(sizeof(struct ath_vap), M_80211_VAP, M_WAITOK | M_ZERO); needbeacon = 0; IEEE80211_ADDR_COPY(mac, mac0); ATH_LOCK(sc); ic_opmode = opmode; /* default to opmode of new vap */ switch (opmode) { case IEEE80211_M_STA: if (sc->sc_nstavaps != 0) { /* XXX only 1 for now */ device_printf(sc->sc_dev, "only 1 sta vap supported\n"); goto bad; } if (sc->sc_nvaps) { /* * With multiple vaps we must fall back * to s/w beacon miss handling. */ flags |= IEEE80211_CLONE_NOBEACONS; } if (flags & IEEE80211_CLONE_NOBEACONS) { /* * Station mode w/o beacons are implemented w/ AP mode. */ ic_opmode = IEEE80211_M_HOSTAP; } break; case IEEE80211_M_IBSS: if (sc->sc_nvaps != 0) { /* XXX only 1 for now */ device_printf(sc->sc_dev, "only 1 ibss vap supported\n"); goto bad; } needbeacon = 1; break; case IEEE80211_M_AHDEMO: #ifdef IEEE80211_SUPPORT_TDMA if (flags & IEEE80211_CLONE_TDMA) { if (sc->sc_nvaps != 0) { device_printf(sc->sc_dev, "only 1 tdma vap supported\n"); goto bad; } needbeacon = 1; flags |= IEEE80211_CLONE_NOBEACONS; } /* fall thru... */ #endif case IEEE80211_M_MONITOR: if (sc->sc_nvaps != 0 && ic->ic_opmode != opmode) { /* * Adopt existing mode. Adding a monitor or ahdemo * vap to an existing configuration is of dubious * value but should be ok. */ /* XXX not right for monitor mode */ ic_opmode = ic->ic_opmode; } break; case IEEE80211_M_HOSTAP: case IEEE80211_M_MBSS: needbeacon = 1; break; case IEEE80211_M_WDS: if (sc->sc_nvaps != 0 && ic->ic_opmode == IEEE80211_M_STA) { device_printf(sc->sc_dev, "wds not supported in sta mode\n"); goto bad; } /* * Silently remove any request for a unique * bssid; WDS vap's always share the local * mac address. */ flags &= ~IEEE80211_CLONE_BSSID; if (sc->sc_nvaps == 0) ic_opmode = IEEE80211_M_HOSTAP; else ic_opmode = ic->ic_opmode; break; default: device_printf(sc->sc_dev, "unknown opmode %d\n", opmode); goto bad; } /* * Check that a beacon buffer is available; the code below assumes it. */ if (needbeacon & TAILQ_EMPTY(&sc->sc_bbuf)) { device_printf(sc->sc_dev, "no beacon buffer available\n"); goto bad; } /* STA, AHDEMO? */ if (opmode == IEEE80211_M_HOSTAP || opmode == IEEE80211_M_MBSS) { assign_address(sc, mac, flags & IEEE80211_CLONE_BSSID); ath_hal_setbssidmask(sc->sc_ah, sc->sc_hwbssidmask); } vap = &avp->av_vap; /* XXX can't hold mutex across if_alloc */ ATH_UNLOCK(sc); error = ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid); ATH_LOCK(sc); if (error != 0) { device_printf(sc->sc_dev, "%s: error %d creating vap\n", __func__, error); goto bad2; } /* h/w crypto support */ vap->iv_key_alloc = ath_key_alloc; vap->iv_key_delete = ath_key_delete; vap->iv_key_set = ath_key_set; vap->iv_key_update_begin = ath_key_update_begin; vap->iv_key_update_end = ath_key_update_end; /* override various methods */ avp->av_recv_mgmt = vap->iv_recv_mgmt; vap->iv_recv_mgmt = ath_recv_mgmt; vap->iv_reset = ath_reset_vap; vap->iv_update_beacon = ath_beacon_update; avp->av_newstate = vap->iv_newstate; vap->iv_newstate = ath_newstate; avp->av_bmiss = vap->iv_bmiss; vap->iv_bmiss = ath_bmiss_vap; avp->av_node_ps = vap->iv_node_ps; vap->iv_node_ps = ath_node_powersave; avp->av_set_tim = vap->iv_set_tim; vap->iv_set_tim = ath_node_set_tim; avp->av_recv_pspoll = vap->iv_recv_pspoll; vap->iv_recv_pspoll = ath_node_recv_pspoll; /* Set default parameters */ /* * Anything earlier than some AR9300 series MACs don't * support a smaller MPDU density. */ vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_8; /* * All NICs can handle the maximum size, however * AR5416 based MACs can only TX aggregates w/ RTS * protection when the total aggregate size is <= 8k. * However, for now that's enforced by the TX path. */ vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_64K; avp->av_bslot = -1; if (needbeacon) { /* * Allocate beacon state and setup the q for buffered * multicast frames. We know a beacon buffer is * available because we checked above. */ avp->av_bcbuf = TAILQ_FIRST(&sc->sc_bbuf); TAILQ_REMOVE(&sc->sc_bbuf, avp->av_bcbuf, bf_list); if (opmode != IEEE80211_M_IBSS || !sc->sc_hasveol) { /* * Assign the vap to a beacon xmit slot. As above * this cannot fail to find a free one. */ avp->av_bslot = assign_bslot(sc); KASSERT(sc->sc_bslot[avp->av_bslot] == NULL, ("beacon slot %u not empty", avp->av_bslot)); sc->sc_bslot[avp->av_bslot] = vap; sc->sc_nbcnvaps++; } if (sc->sc_hastsfadd && sc->sc_nbcnvaps > 0) { /* * Multple vaps are to transmit beacons and we * have h/w support for TSF adjusting; enable * use of staggered beacons. */ sc->sc_stagbeacons = 1; } ath_txq_init(sc, &avp->av_mcastq, ATH_TXQ_SWQ); } ic->ic_opmode = ic_opmode; if (opmode != IEEE80211_M_WDS) { sc->sc_nvaps++; if (opmode == IEEE80211_M_STA) sc->sc_nstavaps++; if (opmode == IEEE80211_M_MBSS) sc->sc_nmeshvaps++; } switch (ic_opmode) { case IEEE80211_M_IBSS: sc->sc_opmode = HAL_M_IBSS; break; case IEEE80211_M_STA: sc->sc_opmode = HAL_M_STA; break; case IEEE80211_M_AHDEMO: #ifdef IEEE80211_SUPPORT_TDMA if (vap->iv_caps & IEEE80211_C_TDMA) { sc->sc_tdma = 1; /* NB: disable tsf adjust */ sc->sc_stagbeacons = 0; } /* * NB: adhoc demo mode is a pseudo mode; to the hal it's * just ap mode. */ /* fall thru... */ #endif case IEEE80211_M_HOSTAP: case IEEE80211_M_MBSS: sc->sc_opmode = HAL_M_HOSTAP; break; case IEEE80211_M_MONITOR: sc->sc_opmode = HAL_M_MONITOR; break; default: /* XXX should not happen */ break; } if (sc->sc_hastsfadd) { /* * Configure whether or not TSF adjust should be done. */ ath_hal_settsfadjust(sc->sc_ah, sc->sc_stagbeacons); } if (flags & IEEE80211_CLONE_NOBEACONS) { /* * Enable s/w beacon miss handling. */ sc->sc_swbmiss = 1; } ATH_UNLOCK(sc); /* complete setup */ ieee80211_vap_attach(vap, ath_media_change, ieee80211_media_status, mac); return vap; bad2: reclaim_address(sc, mac); ath_hal_setbssidmask(sc->sc_ah, sc->sc_hwbssidmask); bad: free(avp, M_80211_VAP); ATH_UNLOCK(sc); return NULL; } static void ath_vap_delete(struct ieee80211vap *vap) { struct ieee80211com *ic = vap->iv_ic; struct ath_softc *sc = ic->ic_softc; struct ath_hal *ah = sc->sc_ah; struct ath_vap *avp = ATH_VAP(vap); ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); DPRINTF(sc, ATH_DEBUG_RESET, "%s: called\n", __func__); if (sc->sc_running) { /* * Quiesce the hardware while we remove the vap. In * particular we need to reclaim all references to * the vap state by any frames pending on the tx queues. */ ath_hal_intrset(ah, 0); /* disable interrupts */ /* XXX Do all frames from all vaps/nodes need draining here? */ ath_stoprecv(sc, 1); /* stop recv side */ ath_draintxq(sc, ATH_RESET_DEFAULT); /* stop hw xmit side */ } /* .. leave the hardware awake for now. */ ieee80211_vap_detach(vap); /* * XXX Danger Will Robinson! Danger! * * Because ieee80211_vap_detach() can queue a frame (the station * diassociate message?) after we've drained the TXQ and * flushed the software TXQ, we will end up with a frame queued * to a node whose vap is about to be freed. * * To work around this, flush the hardware/software again. * This may be racy - the ath task may be running and the packet * may be being scheduled between sw->hw txq. Tsk. * * TODO: figure out why a new node gets allocated somewhere around * here (after the ath_tx_swq() call; and after an ath_stop() * call!) */ ath_draintxq(sc, ATH_RESET_DEFAULT); ATH_LOCK(sc); /* * Reclaim beacon state. Note this must be done before * the vap instance is reclaimed as we may have a reference * to it in the buffer for the beacon frame. */ if (avp->av_bcbuf != NULL) { if (avp->av_bslot != -1) { sc->sc_bslot[avp->av_bslot] = NULL; sc->sc_nbcnvaps--; } ath_beacon_return(sc, avp->av_bcbuf); avp->av_bcbuf = NULL; if (sc->sc_nbcnvaps == 0) { sc->sc_stagbeacons = 0; if (sc->sc_hastsfadd) ath_hal_settsfadjust(sc->sc_ah, 0); } /* * Reclaim any pending mcast frames for the vap. */ ath_tx_draintxq(sc, &avp->av_mcastq); } /* * Update bookkeeping. */ if (vap->iv_opmode == IEEE80211_M_STA) { sc->sc_nstavaps--; if (sc->sc_nstavaps == 0 && sc->sc_swbmiss) sc->sc_swbmiss = 0; } else if (vap->iv_opmode == IEEE80211_M_HOSTAP || vap->iv_opmode == IEEE80211_M_MBSS) { reclaim_address(sc, vap->iv_myaddr); ath_hal_setbssidmask(ah, sc->sc_hwbssidmask); if (vap->iv_opmode == IEEE80211_M_MBSS) sc->sc_nmeshvaps--; } if (vap->iv_opmode != IEEE80211_M_WDS) sc->sc_nvaps--; #ifdef IEEE80211_SUPPORT_TDMA /* TDMA operation ceases when the last vap is destroyed */ if (sc->sc_tdma && sc->sc_nvaps == 0) { sc->sc_tdma = 0; sc->sc_swbmiss = 0; } #endif free(avp, M_80211_VAP); if (sc->sc_running) { /* * Restart rx+tx machines if still running (RUNNING will * be reset if we just destroyed the last vap). */ if (ath_startrecv(sc) != 0) device_printf(sc->sc_dev, "%s: unable to restart recv logic\n", __func__); if (sc->sc_beacons) { /* restart beacons */ #ifdef IEEE80211_SUPPORT_TDMA if (sc->sc_tdma) ath_tdma_config(sc, NULL); else #endif ath_beacon_config(sc, NULL); } ath_hal_intrset(ah, sc->sc_imask); } /* Ok, let the hardware asleep. */ ath_power_restore_power_state(sc); ATH_UNLOCK(sc); } void ath_suspend(struct ath_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; sc->sc_resume_up = ic->ic_nrunning != 0; ieee80211_suspend_all(ic); /* * NB: don't worry about putting the chip in low power * mode; pci will power off our socket on suspend and * CardBus detaches the device. * * XXX TODO: well, that's great, except for non-cardbus * devices! */ /* * XXX This doesn't wait until all pending taskqueue * items and parallel transmit/receive/other threads * are running! */ ath_hal_intrset(sc->sc_ah, 0); taskqueue_block(sc->sc_tq); ATH_LOCK(sc); callout_stop(&sc->sc_cal_ch); ATH_UNLOCK(sc); /* * XXX ensure sc_invalid is 1 */ /* Disable the PCIe PHY, complete with workarounds */ ath_hal_enablepcie(sc->sc_ah, 1, 1); } /* * Reset the key cache since some parts do not reset the * contents on resume. First we clear all entries, then * re-load keys that the 802.11 layer assumes are setup * in h/w. */ static void ath_reset_keycache(struct ath_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = sc->sc_ah; int i; ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); for (i = 0; i < sc->sc_keymax; i++) ath_hal_keyreset(ah, i); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); ieee80211_crypto_reload_keys(ic); } /* * Fetch the current chainmask configuration based on the current * operating channel and options. */ static void ath_update_chainmasks(struct ath_softc *sc, struct ieee80211_channel *chan) { /* * Set TX chainmask to the currently configured chainmask; * the TX chainmask depends upon the current operating mode. */ sc->sc_cur_rxchainmask = sc->sc_rxchainmask; if (IEEE80211_IS_CHAN_HT(chan)) { sc->sc_cur_txchainmask = sc->sc_txchainmask; } else { sc->sc_cur_txchainmask = 1; } DPRINTF(sc, ATH_DEBUG_RESET, "%s: TX chainmask is now 0x%x, RX is now 0x%x\n", __func__, sc->sc_cur_txchainmask, sc->sc_cur_rxchainmask); } void ath_resume(struct ath_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = sc->sc_ah; HAL_STATUS status; ath_hal_enablepcie(ah, 0, 0); /* * Must reset the chip before we reload the * keycache as we were powered down on suspend. */ ath_update_chainmasks(sc, sc->sc_curchan != NULL ? sc->sc_curchan : ic->ic_curchan); ath_hal_setchainmasks(sc->sc_ah, sc->sc_cur_txchainmask, sc->sc_cur_rxchainmask); /* Ensure we set the current power state to on */ ATH_LOCK(sc); ath_power_setselfgen(sc, HAL_PM_AWAKE); ath_power_set_power_state(sc, HAL_PM_AWAKE); ath_power_setpower(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); ath_hal_reset(ah, sc->sc_opmode, sc->sc_curchan != NULL ? sc->sc_curchan : ic->ic_curchan, AH_FALSE, HAL_RESET_NORMAL, &status); ath_reset_keycache(sc); ATH_RX_LOCK(sc); sc->sc_rx_stopped = 1; sc->sc_rx_resetted = 1; ATH_RX_UNLOCK(sc); /* Let DFS at it in case it's a DFS channel */ ath_dfs_radar_enable(sc, ic->ic_curchan); /* Let spectral at in case spectral is enabled */ ath_spectral_enable(sc, ic->ic_curchan); /* * Let bluetooth coexistence at in case it's needed for this channel */ ath_btcoex_enable(sc, ic->ic_curchan); /* * If we're doing TDMA, enforce the TXOP limitation for chips that * support it. */ if (sc->sc_hasenforcetxop && sc->sc_tdma) ath_hal_setenforcetxop(sc->sc_ah, 1); else ath_hal_setenforcetxop(sc->sc_ah, 0); /* Restore the LED configuration */ ath_led_config(sc); ath_hal_setledstate(ah, HAL_LED_INIT); if (sc->sc_resume_up) ieee80211_resume_all(ic); ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); /* XXX beacons ? */ } void ath_shutdown(struct ath_softc *sc) { ATH_LOCK(sc); ath_stop(sc); ATH_UNLOCK(sc); /* NB: no point powering down chip as we're about to reboot */ } /* * Interrupt handler. Most of the actual processing is deferred. */ void ath_intr(void *arg) { struct ath_softc *sc = arg; struct ath_hal *ah = sc->sc_ah; HAL_INT status = 0; uint32_t txqs; /* * If we're inside a reset path, just print a warning and * clear the ISR. The reset routine will finish it for us. */ ATH_PCU_LOCK(sc); if (sc->sc_inreset_cnt) { HAL_INT status; ath_hal_getisr(ah, &status); /* clear ISR */ ath_hal_intrset(ah, 0); /* disable further intr's */ DPRINTF(sc, ATH_DEBUG_ANY, "%s: in reset, ignoring: status=0x%x\n", __func__, status); ATH_PCU_UNLOCK(sc); return; } if (sc->sc_invalid) { /* * The hardware is not ready/present, don't touch anything. * Note this can happen early on if the IRQ is shared. */ DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid; ignored\n", __func__); ATH_PCU_UNLOCK(sc); return; } if (!ath_hal_intrpend(ah)) { /* shared irq, not for us */ ATH_PCU_UNLOCK(sc); return; } ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); if (sc->sc_ic.ic_nrunning == 0 && sc->sc_running == 0) { HAL_INT status; DPRINTF(sc, ATH_DEBUG_ANY, "%s: ic_nrunning %d sc_running %d\n", __func__, sc->sc_ic.ic_nrunning, sc->sc_running); ath_hal_getisr(ah, &status); /* clear ISR */ ath_hal_intrset(ah, 0); /* disable further intr's */ ATH_PCU_UNLOCK(sc); ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); return; } /* * Figure out the reason(s) for the interrupt. Note * that the hal returns a pseudo-ISR that may include * bits we haven't explicitly enabled so we mask the * value to insure we only process bits we requested. */ ath_hal_getisr(ah, &status); /* NB: clears ISR too */ DPRINTF(sc, ATH_DEBUG_INTR, "%s: status 0x%x\n", __func__, status); ATH_KTR(sc, ATH_KTR_INTERRUPTS, 1, "ath_intr: mask=0x%.8x", status); #ifdef ATH_DEBUG_ALQ if_ath_alq_post_intr(&sc->sc_alq, status, ah->ah_intrstate, ah->ah_syncstate); #endif /* ATH_DEBUG_ALQ */ #ifdef ATH_KTR_INTR_DEBUG ATH_KTR(sc, ATH_KTR_INTERRUPTS, 5, "ath_intr: ISR=0x%.8x, ISR_S0=0x%.8x, ISR_S1=0x%.8x, ISR_S2=0x%.8x, ISR_S5=0x%.8x", ah->ah_intrstate[0], ah->ah_intrstate[1], ah->ah_intrstate[2], ah->ah_intrstate[3], ah->ah_intrstate[6]); #endif /* Squirrel away SYNC interrupt debugging */ if (ah->ah_syncstate != 0) { int i; for (i = 0; i < 32; i++) if (ah->ah_syncstate & (i << i)) sc->sc_intr_stats.sync_intr[i]++; } status &= sc->sc_imask; /* discard unasked for bits */ /* Short-circuit un-handled interrupts */ if (status == 0x0) { ATH_PCU_UNLOCK(sc); ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); return; } /* * Take a note that we're inside the interrupt handler, so * the reset routines know to wait. */ sc->sc_intr_cnt++; ATH_PCU_UNLOCK(sc); /* * Handle the interrupt. We won't run concurrent with the reset * or channel change routines as they'll wait for sc_intr_cnt * to be 0 before continuing. */ if (status & HAL_INT_FATAL) { sc->sc_stats.ast_hardware++; ath_hal_intrset(ah, 0); /* disable intr's until reset */ taskqueue_enqueue(sc->sc_tq, &sc->sc_fataltask); } else { if (status & HAL_INT_SWBA) { /* * Software beacon alert--time to send a beacon. * Handle beacon transmission directly; deferring * this is too slow to meet timing constraints * under load. */ #ifdef IEEE80211_SUPPORT_TDMA if (sc->sc_tdma) { if (sc->sc_tdmaswba == 0) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); ath_tdma_beacon_send(sc, vap); sc->sc_tdmaswba = vap->iv_tdma->tdma_bintval; } else sc->sc_tdmaswba--; } else #endif { ath_beacon_proc(sc, 0); #ifdef IEEE80211_SUPPORT_SUPERG /* * Schedule the rx taskq in case there's no * traffic so any frames held on the staging * queue are aged and potentially flushed. */ sc->sc_rx.recv_sched(sc, 1); #endif } } if (status & HAL_INT_RXEOL) { int imask; ATH_KTR(sc, ATH_KTR_ERROR, 0, "ath_intr: RXEOL"); if (! sc->sc_isedma) { ATH_PCU_LOCK(sc); /* * NB: the hardware should re-read the link when * RXE bit is written, but it doesn't work at * least on older hardware revs. */ sc->sc_stats.ast_rxeol++; /* * Disable RXEOL/RXORN - prevent an interrupt * storm until the PCU logic can be reset. * In case the interface is reset some other * way before "sc_kickpcu" is called, don't * modify sc_imask - that way if it is reset * by a call to ath_reset() somehow, the * interrupt mask will be correctly reprogrammed. */ imask = sc->sc_imask; imask &= ~(HAL_INT_RXEOL | HAL_INT_RXORN); ath_hal_intrset(ah, imask); /* * Only blank sc_rxlink if we've not yet kicked * the PCU. * * This isn't entirely correct - the correct solution * would be to have a PCU lock and engage that for * the duration of the PCU fiddling; which would include * running the RX process. Otherwise we could end up * messing up the RX descriptor chain and making the * RX desc list much shorter. */ if (! sc->sc_kickpcu) sc->sc_rxlink = NULL; sc->sc_kickpcu = 1; ATH_PCU_UNLOCK(sc); } /* * Enqueue an RX proc to handle whatever * is in the RX queue. * This will then kick the PCU if required. */ sc->sc_rx.recv_sched(sc, 1); } if (status & HAL_INT_TXURN) { sc->sc_stats.ast_txurn++; /* bump tx trigger level */ ath_hal_updatetxtriglevel(ah, AH_TRUE); } /* * Handle both the legacy and RX EDMA interrupt bits. * Note that HAL_INT_RXLP is also HAL_INT_RXDESC. */ if (status & (HAL_INT_RX | HAL_INT_RXHP | HAL_INT_RXLP)) { sc->sc_stats.ast_rx_intr++; sc->sc_rx.recv_sched(sc, 1); } if (status & HAL_INT_TX) { sc->sc_stats.ast_tx_intr++; /* * Grab all the currently set bits in the HAL txq bitmap * and blank them. This is the only place we should be * doing this. */ if (! sc->sc_isedma) { ATH_PCU_LOCK(sc); txqs = 0xffffffff; ath_hal_gettxintrtxqs(sc->sc_ah, &txqs); ATH_KTR(sc, ATH_KTR_INTERRUPTS, 3, "ath_intr: TX; txqs=0x%08x, txq_active was 0x%08x, now 0x%08x", txqs, sc->sc_txq_active, sc->sc_txq_active | txqs); sc->sc_txq_active |= txqs; ATH_PCU_UNLOCK(sc); } taskqueue_enqueue(sc->sc_tq, &sc->sc_txtask); } if (status & HAL_INT_BMISS) { sc->sc_stats.ast_bmiss++; taskqueue_enqueue(sc->sc_tq, &sc->sc_bmisstask); } if (status & HAL_INT_GTT) sc->sc_stats.ast_tx_timeout++; if (status & HAL_INT_CST) sc->sc_stats.ast_tx_cst++; if (status & HAL_INT_MIB) { sc->sc_stats.ast_mib++; ATH_PCU_LOCK(sc); /* * Disable interrupts until we service the MIB * interrupt; otherwise it will continue to fire. */ ath_hal_intrset(ah, 0); /* * Let the hal handle the event. We assume it will * clear whatever condition caused the interrupt. */ ath_hal_mibevent(ah, &sc->sc_halstats); /* * Don't reset the interrupt if we've just * kicked the PCU, or we may get a nested * RXEOL before the rxproc has had a chance * to run. */ if (sc->sc_kickpcu == 0) ath_hal_intrset(ah, sc->sc_imask); ATH_PCU_UNLOCK(sc); } if (status & HAL_INT_RXORN) { /* NB: hal marks HAL_INT_FATAL when RXORN is fatal */ ATH_KTR(sc, ATH_KTR_ERROR, 0, "ath_intr: RXORN"); sc->sc_stats.ast_rxorn++; } if (status & HAL_INT_TSFOOR) { device_printf(sc->sc_dev, "%s: TSFOOR\n", __func__); sc->sc_syncbeacon = 1; } } ATH_PCU_LOCK(sc); sc->sc_intr_cnt--; ATH_PCU_UNLOCK(sc); ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); } static void ath_fatal_proc(void *arg, int pending) { struct ath_softc *sc = arg; u_int32_t *state; u_int32_t len; void *sp; if (sc->sc_invalid) return; device_printf(sc->sc_dev, "hardware error; resetting\n"); /* * Fatal errors are unrecoverable. Typically these * are caused by DMA errors. Collect h/w state from * the hal so we can diagnose what's going on. */ if (ath_hal_getfatalstate(sc->sc_ah, &sp, &len)) { KASSERT(len >= 6*sizeof(u_int32_t), ("len %u bytes", len)); state = sp; device_printf(sc->sc_dev, "0x%08x 0x%08x 0x%08x, 0x%08x 0x%08x 0x%08x\n", state[0], state[1] , state[2], state[3], state[4], state[5]); } ath_reset(sc, ATH_RESET_NOLOSS); } static void ath_bmiss_vap(struct ieee80211vap *vap) { struct ath_softc *sc = vap->iv_ic->ic_softc; /* * Workaround phantom bmiss interrupts by sanity-checking * the time of our last rx'd frame. If it is within the * beacon miss interval then ignore the interrupt. If it's * truly a bmiss we'll get another interrupt soon and that'll * be dispatched up for processing. Note this applies only * for h/w beacon miss events. */ /* * XXX TODO: Just read the TSF during the interrupt path; * that way we don't have to wake up again just to read it * again. */ ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); if ((vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) == 0) { u_int64_t lastrx = sc->sc_lastrx; u_int64_t tsf = ath_hal_gettsf64(sc->sc_ah); /* XXX should take a locked ref to iv_bss */ u_int bmisstimeout = vap->iv_bmissthreshold * vap->iv_bss->ni_intval * 1024; DPRINTF(sc, ATH_DEBUG_BEACON, "%s: tsf %llu lastrx %lld (%llu) bmiss %u\n", __func__, (unsigned long long) tsf, (unsigned long long)(tsf - lastrx), (unsigned long long) lastrx, bmisstimeout); if (tsf - lastrx <= bmisstimeout) { sc->sc_stats.ast_bmiss_phantom++; ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); return; } } /* * There's no need to keep the hardware awake during the call * to av_bmiss(). */ ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); /* * Attempt to force a beacon resync. */ sc->sc_syncbeacon = 1; ATH_VAP(vap)->av_bmiss(vap); } /* XXX this needs a force wakeup! */ int ath_hal_gethangstate(struct ath_hal *ah, uint32_t mask, uint32_t *hangs) { uint32_t rsize; void *sp; if (!ath_hal_getdiagstate(ah, HAL_DIAG_CHECK_HANGS, &mask, sizeof(mask), &sp, &rsize)) return 0; KASSERT(rsize == sizeof(uint32_t), ("resultsize %u", rsize)); *hangs = *(uint32_t *)sp; return 1; } static void ath_bmiss_proc(void *arg, int pending) { struct ath_softc *sc = arg; uint32_t hangs; DPRINTF(sc, ATH_DEBUG_ANY, "%s: pending %u\n", __func__, pending); ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); ath_beacon_miss(sc); /* * Do a reset upon any becaon miss event. * * It may be a non-recognised RX clear hang which needs a reset * to clear. */ if (ath_hal_gethangstate(sc->sc_ah, 0xff, &hangs) && hangs != 0) { ath_reset(sc, ATH_RESET_NOLOSS); device_printf(sc->sc_dev, "bb hang detected (0x%x), resetting\n", hangs); } else { ath_reset(sc, ATH_RESET_NOLOSS); ieee80211_beacon_miss(&sc->sc_ic); } /* Force a beacon resync, in case they've drifted */ sc->sc_syncbeacon = 1; ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); } /* * Handle TKIP MIC setup to deal hardware that doesn't do MIC * calcs together with WME. If necessary disable the crypto * hardware and mark the 802.11 state so keys will be setup * with the MIC work done in software. */ static void ath_settkipmic(struct ath_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; if ((ic->ic_cryptocaps & IEEE80211_CRYPTO_TKIP) && !sc->sc_wmetkipmic) { if (ic->ic_flags & IEEE80211_F_WME) { ath_hal_settkipmic(sc->sc_ah, AH_FALSE); ic->ic_cryptocaps &= ~IEEE80211_CRYPTO_TKIPMIC; } else { ath_hal_settkipmic(sc->sc_ah, AH_TRUE); ic->ic_cryptocaps |= IEEE80211_CRYPTO_TKIPMIC; } } } static int ath_init(struct ath_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = sc->sc_ah; HAL_STATUS status; ATH_LOCK_ASSERT(sc); /* * Force the sleep state awake. */ ath_power_setselfgen(sc, HAL_PM_AWAKE); ath_power_set_power_state(sc, HAL_PM_AWAKE); ath_power_setpower(sc, HAL_PM_AWAKE); /* * Stop anything previously setup. This is safe * whether this is the first time through or not. */ ath_stop(sc); /* * The basic interface to setting the hardware in a good * state is ``reset''. On return the hardware is known to * be powered up and with interrupts disabled. This must * be followed by initialization of the appropriate bits * and then setup of the interrupt mask. */ ath_settkipmic(sc); ath_update_chainmasks(sc, ic->ic_curchan); ath_hal_setchainmasks(sc->sc_ah, sc->sc_cur_txchainmask, sc->sc_cur_rxchainmask); if (!ath_hal_reset(ah, sc->sc_opmode, ic->ic_curchan, AH_FALSE, HAL_RESET_NORMAL, &status)) { device_printf(sc->sc_dev, "unable to reset hardware; hal status %u\n", status); return (ENODEV); } ATH_RX_LOCK(sc); sc->sc_rx_stopped = 1; sc->sc_rx_resetted = 1; ATH_RX_UNLOCK(sc); ath_chan_change(sc, ic->ic_curchan); /* Let DFS at it in case it's a DFS channel */ ath_dfs_radar_enable(sc, ic->ic_curchan); /* Let spectral at in case spectral is enabled */ ath_spectral_enable(sc, ic->ic_curchan); /* * Let bluetooth coexistence at in case it's needed for this channel */ ath_btcoex_enable(sc, ic->ic_curchan); /* * If we're doing TDMA, enforce the TXOP limitation for chips that * support it. */ if (sc->sc_hasenforcetxop && sc->sc_tdma) ath_hal_setenforcetxop(sc->sc_ah, 1); else ath_hal_setenforcetxop(sc->sc_ah, 0); /* * Likewise this is set during reset so update * state cached in the driver. */ sc->sc_diversity = ath_hal_getdiversity(ah); sc->sc_lastlongcal = ticks; sc->sc_resetcal = 1; sc->sc_lastcalreset = 0; sc->sc_lastani = ticks; sc->sc_lastshortcal = ticks; sc->sc_doresetcal = AH_FALSE; /* * Beacon timers were cleared here; give ath_newstate() * a hint that the beacon timers should be poked when * things transition to the RUN state. */ sc->sc_beacons = 0; /* * Setup the hardware after reset: the key cache * is filled as needed and the receive engine is * set going. Frame transmit is handled entirely * in the frame output path; there's nothing to do * here except setup the interrupt mask. */ if (ath_startrecv(sc) != 0) { device_printf(sc->sc_dev, "unable to start recv logic\n"); ath_power_restore_power_state(sc); return (ENODEV); } /* * Enable interrupts. */ sc->sc_imask = HAL_INT_RX | HAL_INT_TX | HAL_INT_RXORN | HAL_INT_TXURN | HAL_INT_FATAL | HAL_INT_GLOBAL; /* * Enable RX EDMA bits. Note these overlap with * HAL_INT_RX and HAL_INT_RXDESC respectively. */ if (sc->sc_isedma) sc->sc_imask |= (HAL_INT_RXHP | HAL_INT_RXLP); /* * If we're an EDMA NIC, we don't care about RXEOL. * Writing a new descriptor in will simply restart * RX DMA. */ if (! sc->sc_isedma) sc->sc_imask |= HAL_INT_RXEOL; /* * Enable MIB interrupts when there are hardware phy counters. * Note we only do this (at the moment) for station mode. */ if (sc->sc_needmib && ic->ic_opmode == IEEE80211_M_STA) sc->sc_imask |= HAL_INT_MIB; /* * XXX add capability for this. * * If we're in STA mode (and maybe IBSS?) then register for * TSFOOR interrupts. */ if (ic->ic_opmode == IEEE80211_M_STA) sc->sc_imask |= HAL_INT_TSFOOR; /* Enable global TX timeout and carrier sense timeout if available */ if (ath_hal_gtxto_supported(ah)) sc->sc_imask |= HAL_INT_GTT; DPRINTF(sc, ATH_DEBUG_RESET, "%s: imask=0x%x\n", __func__, sc->sc_imask); sc->sc_running = 1; callout_reset(&sc->sc_wd_ch, hz, ath_watchdog, sc); ath_hal_intrset(ah, sc->sc_imask); ath_power_restore_power_state(sc); return (0); } static void ath_stop(struct ath_softc *sc) { struct ath_hal *ah = sc->sc_ah; ATH_LOCK_ASSERT(sc); /* * Wake the hardware up before fiddling with it. */ ath_power_set_power_state(sc, HAL_PM_AWAKE); if (sc->sc_running) { /* * Shutdown the hardware and driver: * reset 802.11 state machine * turn off timers * disable interrupts * turn off the radio * clear transmit machinery * clear receive machinery * drain and release tx queues * reclaim beacon resources * power down hardware * * Note that some of this work is not possible if the * hardware is gone (invalid). */ #ifdef ATH_TX99_DIAG if (sc->sc_tx99 != NULL) sc->sc_tx99->stop(sc->sc_tx99); #endif callout_stop(&sc->sc_wd_ch); sc->sc_wd_timer = 0; sc->sc_running = 0; if (!sc->sc_invalid) { if (sc->sc_softled) { callout_stop(&sc->sc_ledtimer); ath_hal_gpioset(ah, sc->sc_ledpin, !sc->sc_ledon); sc->sc_blinking = 0; } ath_hal_intrset(ah, 0); } /* XXX we should stop RX regardless of whether it's valid */ if (!sc->sc_invalid) { ath_stoprecv(sc, 1); ath_hal_phydisable(ah); } else sc->sc_rxlink = NULL; ath_draintxq(sc, ATH_RESET_DEFAULT); ath_beacon_free(sc); /* XXX not needed */ } /* And now, restore the current power state */ ath_power_restore_power_state(sc); } /* * Wait until all pending TX/RX has completed. * * This waits until all existing transmit, receive and interrupts * have completed. It's assumed that the caller has first * grabbed the reset lock so it doesn't try to do overlapping * chip resets. */ #define MAX_TXRX_ITERATIONS 100 static void ath_txrx_stop_locked(struct ath_softc *sc) { int i = MAX_TXRX_ITERATIONS; ATH_UNLOCK_ASSERT(sc); ATH_PCU_LOCK_ASSERT(sc); /* * Sleep until all the pending operations have completed. * * The caller must ensure that reset has been incremented * or the pending operations may continue being queued. */ while (sc->sc_rxproc_cnt || sc->sc_txproc_cnt || sc->sc_txstart_cnt || sc->sc_intr_cnt) { if (i <= 0) break; msleep(sc, &sc->sc_pcu_mtx, 0, "ath_txrx_stop", msecs_to_ticks(10)); i--; } if (i <= 0) device_printf(sc->sc_dev, "%s: didn't finish after %d iterations\n", __func__, MAX_TXRX_ITERATIONS); } #undef MAX_TXRX_ITERATIONS #if 0 static void ath_txrx_stop(struct ath_softc *sc) { ATH_UNLOCK_ASSERT(sc); ATH_PCU_UNLOCK_ASSERT(sc); ATH_PCU_LOCK(sc); ath_txrx_stop_locked(sc); ATH_PCU_UNLOCK(sc); } #endif static void ath_txrx_start(struct ath_softc *sc) { taskqueue_unblock(sc->sc_tq); } /* - * Grab the reset lock, and wait around until noone else + * Grab the reset lock, and wait around until no one else * is trying to do anything with it. * * This is totally horrible but we can't hold this lock for * long enough to do TX/RX or we end up with net80211/ip stack * LORs and eventual deadlock. * * "dowait" signals whether to spin, waiting for the reset * lock count to reach 0. This should (for now) only be used * during the reset path, as the rest of the code may not * be locking-reentrant enough to behave correctly. * * Another, cleaner way should be found to serialise all of * these operations. */ #define MAX_RESET_ITERATIONS 25 static int ath_reset_grablock(struct ath_softc *sc, int dowait) { int w = 0; int i = MAX_RESET_ITERATIONS; ATH_PCU_LOCK_ASSERT(sc); do { if (sc->sc_inreset_cnt == 0) { w = 1; break; } if (dowait == 0) { w = 0; break; } ATH_PCU_UNLOCK(sc); /* * 1 tick is likely not enough time for long calibrations * to complete. So we should wait quite a while. */ pause("ath_reset_grablock", msecs_to_ticks(100)); i--; ATH_PCU_LOCK(sc); } while (i > 0); /* * We always increment the refcounter, regardless * of whether we succeeded to get it in an exclusive * way. */ sc->sc_inreset_cnt++; if (i <= 0) device_printf(sc->sc_dev, "%s: didn't finish after %d iterations\n", __func__, MAX_RESET_ITERATIONS); if (w == 0) device_printf(sc->sc_dev, "%s: warning, recursive reset path!\n", __func__); return w; } #undef MAX_RESET_ITERATIONS /* * Reset the hardware w/o losing operational state. This is * basically a more efficient way of doing ath_stop, ath_init, * followed by state transitions to the current 802.11 * operational state. Used to recover from various errors and * to reset or reload hardware state. */ int ath_reset(struct ath_softc *sc, ATH_RESET_TYPE reset_type) { struct ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = sc->sc_ah; HAL_STATUS status; int i; DPRINTF(sc, ATH_DEBUG_RESET, "%s: called\n", __func__); /* Ensure ATH_LOCK isn't held; ath_rx_proc can't be locked */ ATH_PCU_UNLOCK_ASSERT(sc); ATH_UNLOCK_ASSERT(sc); - /* Try to (stop any further TX/RX from occuring */ + /* Try to (stop any further TX/RX from occurring */ taskqueue_block(sc->sc_tq); /* * Wake the hardware up. */ ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); ATH_PCU_LOCK(sc); /* * Grab the reset lock before TX/RX is stopped. * * This is needed to ensure that when the TX/RX actually does finish, * no further TX/RX/reset runs in parallel with this. */ if (ath_reset_grablock(sc, 1) == 0) { device_printf(sc->sc_dev, "%s: concurrent reset! Danger!\n", __func__); } /* disable interrupts */ ath_hal_intrset(ah, 0); /* * Now, ensure that any in progress TX/RX completes before we * continue. */ ath_txrx_stop_locked(sc); ATH_PCU_UNLOCK(sc); /* * Regardless of whether we're doing a no-loss flush or * not, stop the PCU and handle what's in the RX queue. * That way frames aren't dropped which shouldn't be. */ ath_stoprecv(sc, (reset_type != ATH_RESET_NOLOSS)); ath_rx_flush(sc); /* * Should now wait for pending TX/RX to complete - * and block future ones from occuring. This needs to be + * and block future ones from occurring. This needs to be * done before the TX queue is drained. */ ath_draintxq(sc, reset_type); /* stop xmit side */ ath_settkipmic(sc); /* configure TKIP MIC handling */ /* NB: indicate channel change so we do a full reset */ ath_update_chainmasks(sc, ic->ic_curchan); ath_hal_setchainmasks(sc->sc_ah, sc->sc_cur_txchainmask, sc->sc_cur_rxchainmask); if (!ath_hal_reset(ah, sc->sc_opmode, ic->ic_curchan, AH_TRUE, HAL_RESET_NORMAL, &status)) device_printf(sc->sc_dev, "%s: unable to reset hardware; hal status %u\n", __func__, status); sc->sc_diversity = ath_hal_getdiversity(ah); ATH_RX_LOCK(sc); sc->sc_rx_stopped = 1; sc->sc_rx_resetted = 1; ATH_RX_UNLOCK(sc); /* Let DFS at it in case it's a DFS channel */ ath_dfs_radar_enable(sc, ic->ic_curchan); /* Let spectral at in case spectral is enabled */ ath_spectral_enable(sc, ic->ic_curchan); /* * Let bluetooth coexistence at in case it's needed for this channel */ ath_btcoex_enable(sc, ic->ic_curchan); /* * If we're doing TDMA, enforce the TXOP limitation for chips that * support it. */ if (sc->sc_hasenforcetxop && sc->sc_tdma) ath_hal_setenforcetxop(sc->sc_ah, 1); else ath_hal_setenforcetxop(sc->sc_ah, 0); if (ath_startrecv(sc) != 0) /* restart recv */ device_printf(sc->sc_dev, "%s: unable to start recv logic\n", __func__); /* * We may be doing a reset in response to an ioctl * that changes the channel so update any state that * might change as a result. */ ath_chan_change(sc, ic->ic_curchan); if (sc->sc_beacons) { /* restart beacons */ #ifdef IEEE80211_SUPPORT_TDMA if (sc->sc_tdma) ath_tdma_config(sc, NULL); else #endif ath_beacon_config(sc, NULL); } /* * Release the reset lock and re-enable interrupts here. * If an interrupt was being processed in ath_intr(), * it would disable interrupts at this point. So we have * to atomically enable interrupts and decrement the * reset counter - this way ath_intr() doesn't end up * disabling interrupts without a corresponding enable * in the rest or channel change path. * * Grab the TX reference in case we need to transmit. * That way a parallel transmit doesn't. */ ATH_PCU_LOCK(sc); sc->sc_inreset_cnt--; sc->sc_txstart_cnt++; /* XXX only do this if sc_inreset_cnt == 0? */ ath_hal_intrset(ah, sc->sc_imask); ATH_PCU_UNLOCK(sc); /* * TX and RX can be started here. If it were started with * sc_inreset_cnt > 0, the TX and RX path would abort. * Thus if this is a nested call through the reset or * channel change code, TX completion will occur but * RX completion and ath_start / ath_tx_start will not * run. */ /* Restart TX/RX as needed */ ath_txrx_start(sc); /* XXX TODO: we need to hold the tx refcount here! */ /* Restart TX completion and pending TX */ if (reset_type == ATH_RESET_NOLOSS) { for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { if (ATH_TXQ_SETUP(sc, i)) { ATH_TXQ_LOCK(&sc->sc_txq[i]); ath_txq_restart_dma(sc, &sc->sc_txq[i]); ATH_TXQ_UNLOCK(&sc->sc_txq[i]); ATH_TX_LOCK(sc); ath_txq_sched(sc, &sc->sc_txq[i]); ATH_TX_UNLOCK(sc); } } } ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); ATH_PCU_LOCK(sc); sc->sc_txstart_cnt--; ATH_PCU_UNLOCK(sc); /* Handle any frames in the TX queue */ /* * XXX should this be done by the caller, rather than * ath_reset() ? */ ath_tx_kick(sc); /* restart xmit */ return 0; } static int ath_reset_vap(struct ieee80211vap *vap, u_long cmd) { struct ieee80211com *ic = vap->iv_ic; struct ath_softc *sc = ic->ic_softc; struct ath_hal *ah = sc->sc_ah; switch (cmd) { case IEEE80211_IOC_TXPOWER: /* * If per-packet TPC is enabled, then we have nothing * to do; otherwise we need to force the global limit. * All this can happen directly; no need to reset. */ if (!ath_hal_gettpc(ah)) ath_hal_settxpowlimit(ah, ic->ic_txpowlimit); return 0; } /* XXX? Full or NOLOSS? */ return ath_reset(sc, ATH_RESET_FULL); } struct ath_buf * _ath_getbuf_locked(struct ath_softc *sc, ath_buf_type_t btype) { struct ath_buf *bf; ATH_TXBUF_LOCK_ASSERT(sc); if (btype == ATH_BUFTYPE_MGMT) bf = TAILQ_FIRST(&sc->sc_txbuf_mgmt); else bf = TAILQ_FIRST(&sc->sc_txbuf); if (bf == NULL) { sc->sc_stats.ast_tx_getnobuf++; } else { if (bf->bf_flags & ATH_BUF_BUSY) { sc->sc_stats.ast_tx_getbusybuf++; bf = NULL; } } if (bf != NULL && (bf->bf_flags & ATH_BUF_BUSY) == 0) { if (btype == ATH_BUFTYPE_MGMT) TAILQ_REMOVE(&sc->sc_txbuf_mgmt, bf, bf_list); else { TAILQ_REMOVE(&sc->sc_txbuf, bf, bf_list); sc->sc_txbuf_cnt--; /* * This shuldn't happen; however just to be * safe print a warning and fudge the txbuf * count. */ if (sc->sc_txbuf_cnt < 0) { device_printf(sc->sc_dev, "%s: sc_txbuf_cnt < 0?\n", __func__); sc->sc_txbuf_cnt = 0; } } } else bf = NULL; if (bf == NULL) { /* XXX should check which list, mgmt or otherwise */ DPRINTF(sc, ATH_DEBUG_XMIT, "%s: %s\n", __func__, TAILQ_FIRST(&sc->sc_txbuf) == NULL ? "out of xmit buffers" : "xmit buffer busy"); return NULL; } /* XXX TODO: should do this at buffer list initialisation */ /* XXX (then, ensure the buffer has the right flag set) */ bf->bf_flags = 0; if (btype == ATH_BUFTYPE_MGMT) bf->bf_flags |= ATH_BUF_MGMT; else bf->bf_flags &= (~ATH_BUF_MGMT); /* Valid bf here; clear some basic fields */ bf->bf_next = NULL; /* XXX just to be sure */ bf->bf_last = NULL; /* XXX again, just to be sure */ bf->bf_comp = NULL; /* XXX again, just to be sure */ bzero(&bf->bf_state, sizeof(bf->bf_state)); /* * Track the descriptor ID only if doing EDMA */ if (sc->sc_isedma) { bf->bf_descid = sc->sc_txbuf_descid; sc->sc_txbuf_descid++; } return bf; } /* * When retrying a software frame, buffers marked ATH_BUF_BUSY * can't be thrown back on the queue as they could still be * in use by the hardware. * * This duplicates the buffer, or returns NULL. * * The descriptor is also copied but the link pointers and * the DMA segments aren't copied; this frame should thus * be again passed through the descriptor setup/chain routines * so the link is correct. * * The caller must free the buffer using ath_freebuf(). */ struct ath_buf * ath_buf_clone(struct ath_softc *sc, struct ath_buf *bf) { struct ath_buf *tbf; tbf = ath_getbuf(sc, (bf->bf_flags & ATH_BUF_MGMT) ? ATH_BUFTYPE_MGMT : ATH_BUFTYPE_NORMAL); if (tbf == NULL) return NULL; /* XXX failure? Why? */ /* Copy basics */ tbf->bf_next = NULL; tbf->bf_nseg = bf->bf_nseg; tbf->bf_flags = bf->bf_flags & ATH_BUF_FLAGS_CLONE; tbf->bf_status = bf->bf_status; tbf->bf_m = bf->bf_m; tbf->bf_node = bf->bf_node; KASSERT((bf->bf_node != NULL), ("%s: bf_node=NULL!", __func__)); /* will be setup by the chain/setup function */ tbf->bf_lastds = NULL; /* for now, last == self */ tbf->bf_last = tbf; tbf->bf_comp = bf->bf_comp; /* NOTE: DMA segments will be setup by the setup/chain functions */ /* The caller has to re-init the descriptor + links */ /* * Free the DMA mapping here, before we NULL the mbuf. * We must only call bus_dmamap_unload() once per mbuf chain * or behaviour is undefined. */ if (bf->bf_m != NULL) { /* * XXX is this POSTWRITE call required? */ bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); } bf->bf_m = NULL; bf->bf_node = NULL; /* Copy state */ memcpy(&tbf->bf_state, &bf->bf_state, sizeof(bf->bf_state)); return tbf; } struct ath_buf * ath_getbuf(struct ath_softc *sc, ath_buf_type_t btype) { struct ath_buf *bf; ATH_TXBUF_LOCK(sc); bf = _ath_getbuf_locked(sc, btype); /* * If a mgmt buffer was requested but we're out of those, * try requesting a normal one. */ if (bf == NULL && btype == ATH_BUFTYPE_MGMT) bf = _ath_getbuf_locked(sc, ATH_BUFTYPE_NORMAL); ATH_TXBUF_UNLOCK(sc); if (bf == NULL) { DPRINTF(sc, ATH_DEBUG_XMIT, "%s: stop queue\n", __func__); sc->sc_stats.ast_tx_qstop++; } return bf; } /* * Transmit a single frame. * * net80211 will free the node reference if the transmit * fails, so don't free the node reference here. */ static int ath_transmit(struct ieee80211com *ic, struct mbuf *m) { struct ath_softc *sc = ic->ic_softc; struct ieee80211_node *ni; struct mbuf *next; struct ath_buf *bf; ath_bufhead frags; int retval = 0; /* * Tell the reset path that we're currently transmitting. */ ATH_PCU_LOCK(sc); if (sc->sc_inreset_cnt > 0) { DPRINTF(sc, ATH_DEBUG_XMIT, "%s: sc_inreset_cnt > 0; bailing\n", __func__); ATH_PCU_UNLOCK(sc); sc->sc_stats.ast_tx_qstop++; ATH_KTR(sc, ATH_KTR_TX, 0, "ath_start_task: OACTIVE, finish"); return (ENOBUFS); /* XXX should be EINVAL or? */ } sc->sc_txstart_cnt++; ATH_PCU_UNLOCK(sc); /* Wake the hardware up already */ ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); ATH_KTR(sc, ATH_KTR_TX, 0, "ath_transmit: start"); /* * Grab the TX lock - it's ok to do this here; we haven't * yet started transmitting. */ ATH_TX_LOCK(sc); /* * Node reference, if there's one. */ ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; /* * Enforce how deep a node queue can get. * * XXX it would be nicer if we kept an mbuf queue per * node and only whacked them into ath_bufs when we * are ready to schedule some traffic from them. * .. that may come later. * * XXX we should also track the per-node hardware queue * depth so it is easy to limit the _SUM_ of the swq and * hwq frames. Since we only schedule two HWQ frames * at a time, this should be OK for now. */ if ((!(m->m_flags & M_EAPOL)) && (ATH_NODE(ni)->an_swq_depth > sc->sc_txq_node_maxdepth)) { sc->sc_stats.ast_tx_nodeq_overflow++; retval = ENOBUFS; goto finish; } /* * Check how many TX buffers are available. * * If this is for non-EAPOL traffic, just leave some * space free in order for buffer cloning and raw * frame transmission to occur. * * If it's for EAPOL traffic, ignore this for now. * Management traffic will be sent via the raw transmit * method which bypasses this check. * * This is needed to ensure that EAPOL frames during * (re) keying have a chance to go out. * * See kern/138379 for more information. */ if ((!(m->m_flags & M_EAPOL)) && (sc->sc_txbuf_cnt <= sc->sc_txq_data_minfree)) { sc->sc_stats.ast_tx_nobuf++; retval = ENOBUFS; goto finish; } /* * Grab a TX buffer and associated resources. * * If it's an EAPOL frame, allocate a MGMT ath_buf. * That way even with temporary buffer exhaustion due to * the data path doesn't leave us without the ability * to transmit management frames. * * Otherwise allocate a normal buffer. */ if (m->m_flags & M_EAPOL) bf = ath_getbuf(sc, ATH_BUFTYPE_MGMT); else bf = ath_getbuf(sc, ATH_BUFTYPE_NORMAL); if (bf == NULL) { /* * If we failed to allocate a buffer, fail. * * We shouldn't fail normally, due to the check * above. */ sc->sc_stats.ast_tx_nobuf++; retval = ENOBUFS; goto finish; } /* * At this point we have a buffer; so we need to free it * if we hit any error conditions. */ /* * Check for fragmentation. If this frame * has been broken up verify we have enough * buffers to send all the fragments so all * go out or none... */ TAILQ_INIT(&frags); if ((m->m_flags & M_FRAG) && !ath_txfrag_setup(sc, &frags, m, ni)) { DPRINTF(sc, ATH_DEBUG_XMIT, "%s: out of txfrag buffers\n", __func__); sc->sc_stats.ast_tx_nofrag++; if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); /* * XXXGL: is mbuf valid after ath_txfrag_setup? If yes, * we shouldn't free it but return back. */ ieee80211_free_mbuf(m); m = NULL; goto bad; } /* * At this point if we have any TX fragments, then we will * have bumped the node reference once for each of those. */ /* * XXX Is there anything actually _enforcing_ that the * fragments are being transmitted in one hit, rather than * being interleaved with other transmissions on that * hardware queue? * * The ATH TX output lock is the only thing serialising this * right now. */ /* * Calculate the "next fragment" length field in ath_buf * in order to let the transmit path know enough about * what to next write to the hardware. */ if (m->m_flags & M_FRAG) { struct ath_buf *fbf = bf; struct ath_buf *n_fbf = NULL; struct mbuf *fm = m->m_nextpkt; /* * We need to walk the list of fragments and set * the next size to the following buffer. * However, the first buffer isn't in the frag * list, so we have to do some gymnastics here. */ TAILQ_FOREACH(n_fbf, &frags, bf_list) { fbf->bf_nextfraglen = fm->m_pkthdr.len; fbf = n_fbf; fm = fm->m_nextpkt; } } nextfrag: /* * Pass the frame to the h/w for transmission. * Fragmented frames have each frag chained together * with m_nextpkt. We know there are sufficient ath_buf's * to send all the frags because of work done by * ath_txfrag_setup. We leave m_nextpkt set while * calling ath_tx_start so it can use it to extend the * the tx duration to cover the subsequent frag and * so it can reclaim all the mbufs in case of an error; * ath_tx_start clears m_nextpkt once it commits to * handing the frame to the hardware. * * Note: if this fails, then the mbufs are freed but * not the node reference. * * So, we now have to free the node reference ourselves here * and return OK up to the stack. */ next = m->m_nextpkt; if (ath_tx_start(sc, ni, bf, m)) { bad: if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); reclaim: bf->bf_m = NULL; bf->bf_node = NULL; ATH_TXBUF_LOCK(sc); ath_returnbuf_head(sc, bf); /* * Free the rest of the node references and * buffers for the fragment list. */ ath_txfrag_cleanup(sc, &frags, ni); ATH_TXBUF_UNLOCK(sc); /* * XXX: And free the node/return OK; ath_tx_start() may have * modified the buffer. We currently have no way to * signify that the mbuf was freed but there was an error. */ ieee80211_free_node(ni); retval = 0; goto finish; } /* * Check here if the node is in power save state. */ ath_tx_update_tim(sc, ni, 1); if (next != NULL) { /* * Beware of state changing between frags. * XXX check sta power-save state? */ if (ni->ni_vap->iv_state != IEEE80211_S_RUN) { DPRINTF(sc, ATH_DEBUG_XMIT, "%s: flush fragmented packet, state %s\n", __func__, ieee80211_state_name[ni->ni_vap->iv_state]); /* XXX dmamap */ ieee80211_free_mbuf(next); goto reclaim; } m = next; bf = TAILQ_FIRST(&frags); KASSERT(bf != NULL, ("no buf for txfrag")); TAILQ_REMOVE(&frags, bf, bf_list); goto nextfrag; } /* * Bump watchdog timer. */ sc->sc_wd_timer = 5; finish: ATH_TX_UNLOCK(sc); /* * Finished transmitting! */ ATH_PCU_LOCK(sc); sc->sc_txstart_cnt--; ATH_PCU_UNLOCK(sc); /* Sleep the hardware if required */ ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); ATH_KTR(sc, ATH_KTR_TX, 0, "ath_transmit: finished"); return (retval); } static int ath_media_change(struct ifnet *ifp) { int error = ieee80211_media_change(ifp); /* NB: only the fixed rate can change and that doesn't need a reset */ return (error == ENETRESET ? 0 : error); } /* * Block/unblock tx+rx processing while a key change is done. * We assume the caller serializes key management operations * so we only need to worry about synchronization with other * uses that originate in the driver. */ static void ath_key_update_begin(struct ieee80211vap *vap) { struct ath_softc *sc = vap->iv_ic->ic_softc; DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__); taskqueue_block(sc->sc_tq); } static void ath_key_update_end(struct ieee80211vap *vap) { struct ath_softc *sc = vap->iv_ic->ic_softc; DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__); taskqueue_unblock(sc->sc_tq); } static void ath_update_promisc(struct ieee80211com *ic) { struct ath_softc *sc = ic->ic_softc; u_int32_t rfilt; /* configure rx filter */ ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); rfilt = ath_calcrxfilter(sc); ath_hal_setrxfilter(sc->sc_ah, rfilt); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x\n", __func__, rfilt); } /* * Driver-internal mcast update call. * * Assumes the hardware is already awake. */ static void ath_update_mcast_hw(struct ath_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; u_int32_t mfilt[2]; /* calculate and install multicast filter */ if (ic->ic_allmulti == 0) { struct ieee80211vap *vap; struct ifnet *ifp; struct ifmultiaddr *ifma; /* * Merge multicast addresses to form the hardware filter. */ mfilt[0] = mfilt[1] = 0; TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { ifp = vap->iv_ifp; if_maddr_rlock(ifp); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { caddr_t dl; uint32_t val; uint8_t pos; /* calculate XOR of eight 6bit values */ dl = LLADDR((struct sockaddr_dl *) ifma->ifma_addr); val = le32dec(dl + 0); pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; val = le32dec(dl + 3); pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; pos &= 0x3f; mfilt[pos / 32] |= (1 << (pos % 32)); } if_maddr_runlock(ifp); } } else mfilt[0] = mfilt[1] = ~0; ath_hal_setmcastfilter(sc->sc_ah, mfilt[0], mfilt[1]); DPRINTF(sc, ATH_DEBUG_MODE, "%s: MC filter %08x:%08x\n", __func__, mfilt[0], mfilt[1]); } /* * Called from the net80211 layer - force the hardware * awake before operating. */ static void ath_update_mcast(struct ieee80211com *ic) { struct ath_softc *sc = ic->ic_softc; ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); ath_update_mcast_hw(sc); ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); } void ath_mode_init(struct ath_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = sc->sc_ah; u_int32_t rfilt; /* configure rx filter */ rfilt = ath_calcrxfilter(sc); ath_hal_setrxfilter(ah, rfilt); /* configure operational mode */ ath_hal_setopmode(ah); /* handle any link-level address change */ ath_hal_setmac(ah, ic->ic_macaddr); /* calculate and install multicast filter */ ath_update_mcast_hw(sc); } /* * Set the slot time based on the current setting. */ void ath_setslottime(struct ath_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = sc->sc_ah; u_int usec; if (IEEE80211_IS_CHAN_HALF(ic->ic_curchan)) usec = 13; else if (IEEE80211_IS_CHAN_QUARTER(ic->ic_curchan)) usec = 21; else if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) { /* honor short/long slot time only in 11g */ /* XXX shouldn't honor on pure g or turbo g channel */ if (ic->ic_flags & IEEE80211_F_SHSLOT) usec = HAL_SLOT_TIME_9; else usec = HAL_SLOT_TIME_20; } else usec = HAL_SLOT_TIME_9; DPRINTF(sc, ATH_DEBUG_RESET, "%s: chan %u MHz flags 0x%x %s slot, %u usec\n", __func__, ic->ic_curchan->ic_freq, ic->ic_curchan->ic_flags, ic->ic_flags & IEEE80211_F_SHSLOT ? "short" : "long", usec); /* Wake up the hardware first before updating the slot time */ ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ath_hal_setslottime(ah, usec); ath_power_restore_power_state(sc); sc->sc_updateslot = OK; ATH_UNLOCK(sc); } /* * Callback from the 802.11 layer to update the * slot time based on the current setting. */ static void ath_updateslot(struct ieee80211com *ic) { struct ath_softc *sc = ic->ic_softc; /* * When not coordinating the BSS, change the hardware * immediately. For other operation we defer the change * until beacon updates have propagated to the stations. * * XXX sc_updateslot isn't changed behind a lock? */ if (ic->ic_opmode == IEEE80211_M_HOSTAP || ic->ic_opmode == IEEE80211_M_MBSS) sc->sc_updateslot = UPDATE; else ath_setslottime(sc); } /* * Append the contents of src to dst; both queues * are assumed to be locked. */ void ath_txqmove(struct ath_txq *dst, struct ath_txq *src) { ATH_TXQ_LOCK_ASSERT(src); ATH_TXQ_LOCK_ASSERT(dst); TAILQ_CONCAT(&dst->axq_q, &src->axq_q, bf_list); dst->axq_link = src->axq_link; src->axq_link = NULL; dst->axq_depth += src->axq_depth; dst->axq_aggr_depth += src->axq_aggr_depth; src->axq_depth = 0; src->axq_aggr_depth = 0; } /* * Reset the hardware, with no loss. * * This can't be used for a general case reset. */ static void ath_reset_proc(void *arg, int pending) { struct ath_softc *sc = arg; #if 0 device_printf(sc->sc_dev, "%s: resetting\n", __func__); #endif ath_reset(sc, ATH_RESET_NOLOSS); } /* * Reset the hardware after detecting beacons have stopped. */ static void ath_bstuck_proc(void *arg, int pending) { struct ath_softc *sc = arg; uint32_t hangs = 0; if (ath_hal_gethangstate(sc->sc_ah, 0xff, &hangs) && hangs != 0) device_printf(sc->sc_dev, "bb hang detected (0x%x)\n", hangs); #ifdef ATH_DEBUG_ALQ if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_STUCK_BEACON)) if_ath_alq_post(&sc->sc_alq, ATH_ALQ_STUCK_BEACON, 0, NULL); #endif device_printf(sc->sc_dev, "stuck beacon; resetting (bmiss count %u)\n", sc->sc_bmisscount); sc->sc_stats.ast_bstuck++; /* * This assumes that there's no simultaneous channel mode change - * occuring. + * occurring. */ ath_reset(sc, ATH_RESET_NOLOSS); } static int ath_desc_alloc(struct ath_softc *sc) { int error; error = ath_descdma_setup(sc, &sc->sc_txdma, &sc->sc_txbuf, "tx", sc->sc_tx_desclen, ath_txbuf, ATH_MAX_SCATTER); if (error != 0) { return error; } sc->sc_txbuf_cnt = ath_txbuf; error = ath_descdma_setup(sc, &sc->sc_txdma_mgmt, &sc->sc_txbuf_mgmt, "tx_mgmt", sc->sc_tx_desclen, ath_txbuf_mgmt, ATH_TXDESC); if (error != 0) { ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf); return error; } /* * XXX mark txbuf_mgmt frames with ATH_BUF_MGMT, so the * flag doesn't have to be set in ath_getbuf_locked(). */ error = ath_descdma_setup(sc, &sc->sc_bdma, &sc->sc_bbuf, "beacon", sc->sc_tx_desclen, ATH_BCBUF, 1); if (error != 0) { ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf); ath_descdma_cleanup(sc, &sc->sc_txdma_mgmt, &sc->sc_txbuf_mgmt); return error; } return 0; } static void ath_desc_free(struct ath_softc *sc) { if (sc->sc_bdma.dd_desc_len != 0) ath_descdma_cleanup(sc, &sc->sc_bdma, &sc->sc_bbuf); if (sc->sc_txdma.dd_desc_len != 0) ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf); if (sc->sc_txdma_mgmt.dd_desc_len != 0) ath_descdma_cleanup(sc, &sc->sc_txdma_mgmt, &sc->sc_txbuf_mgmt); } static struct ieee80211_node * ath_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) { struct ieee80211com *ic = vap->iv_ic; struct ath_softc *sc = ic->ic_softc; const size_t space = sizeof(struct ath_node) + sc->sc_rc->arc_space; struct ath_node *an; an = malloc(space, M_80211_NODE, M_NOWAIT|M_ZERO); if (an == NULL) { /* XXX stat+msg */ return NULL; } ath_rate_node_init(sc, an); /* Setup the mutex - there's no associd yet so set the name to NULL */ snprintf(an->an_name, sizeof(an->an_name), "%s: node %p", device_get_nameunit(sc->sc_dev), an); mtx_init(&an->an_mtx, an->an_name, NULL, MTX_DEF); /* XXX setup ath_tid */ ath_tx_tid_init(sc, an); DPRINTF(sc, ATH_DEBUG_NODE, "%s: %6D: an %p\n", __func__, mac, ":", an); return &an->an_node; } static void ath_node_cleanup(struct ieee80211_node *ni) { struct ieee80211com *ic = ni->ni_ic; struct ath_softc *sc = ic->ic_softc; DPRINTF(sc, ATH_DEBUG_NODE, "%s: %6D: an %p\n", __func__, ni->ni_macaddr, ":", ATH_NODE(ni)); /* Cleanup ath_tid, free unused bufs, unlink bufs in TXQ */ ath_tx_node_flush(sc, ATH_NODE(ni)); ath_rate_node_cleanup(sc, ATH_NODE(ni)); sc->sc_node_cleanup(ni); } static void ath_node_free(struct ieee80211_node *ni) { struct ieee80211com *ic = ni->ni_ic; struct ath_softc *sc = ic->ic_softc; DPRINTF(sc, ATH_DEBUG_NODE, "%s: %6D: an %p\n", __func__, ni->ni_macaddr, ":", ATH_NODE(ni)); mtx_destroy(&ATH_NODE(ni)->an_mtx); sc->sc_node_free(ni); } static void ath_node_getsignal(const struct ieee80211_node *ni, int8_t *rssi, int8_t *noise) { struct ieee80211com *ic = ni->ni_ic; struct ath_softc *sc = ic->ic_softc; struct ath_hal *ah = sc->sc_ah; *rssi = ic->ic_node_getrssi(ni); if (ni->ni_chan != IEEE80211_CHAN_ANYC) *noise = ath_hal_getchannoise(ah, ni->ni_chan); else *noise = -95; /* nominally correct */ } /* * Set the default antenna. */ void ath_setdefantenna(struct ath_softc *sc, u_int antenna) { struct ath_hal *ah = sc->sc_ah; /* XXX block beacon interrupts */ ath_hal_setdefantenna(ah, antenna); if (sc->sc_defant != antenna) sc->sc_stats.ast_ant_defswitch++; sc->sc_defant = antenna; sc->sc_rxotherant = 0; } static void ath_txq_init(struct ath_softc *sc, struct ath_txq *txq, int qnum) { txq->axq_qnum = qnum; txq->axq_ac = 0; txq->axq_depth = 0; txq->axq_aggr_depth = 0; txq->axq_intrcnt = 0; txq->axq_link = NULL; txq->axq_softc = sc; TAILQ_INIT(&txq->axq_q); TAILQ_INIT(&txq->axq_tidq); TAILQ_INIT(&txq->fifo.axq_q); ATH_TXQ_LOCK_INIT(sc, txq); } /* * Setup a h/w transmit queue. */ static struct ath_txq * ath_txq_setup(struct ath_softc *sc, int qtype, int subtype) { struct ath_hal *ah = sc->sc_ah; HAL_TXQ_INFO qi; int qnum; memset(&qi, 0, sizeof(qi)); qi.tqi_subtype = subtype; qi.tqi_aifs = HAL_TXQ_USEDEFAULT; qi.tqi_cwmin = HAL_TXQ_USEDEFAULT; qi.tqi_cwmax = HAL_TXQ_USEDEFAULT; /* * Enable interrupts only for EOL and DESC conditions. * We mark tx descriptors to receive a DESC interrupt * when a tx queue gets deep; otherwise waiting for the * EOL to reap descriptors. Note that this is done to * reduce interrupt load and this only defers reaping * descriptors, never transmitting frames. Aside from * reducing interrupts this also permits more concurrency. * The only potential downside is if the tx queue backs * up in which case the top half of the kernel may backup * due to a lack of tx descriptors. */ if (sc->sc_isedma) qi.tqi_qflags = HAL_TXQ_TXEOLINT_ENABLE | HAL_TXQ_TXOKINT_ENABLE; else qi.tqi_qflags = HAL_TXQ_TXEOLINT_ENABLE | HAL_TXQ_TXDESCINT_ENABLE; qnum = ath_hal_setuptxqueue(ah, qtype, &qi); if (qnum == -1) { /* * NB: don't print a message, this happens * normally on parts with too few tx queues */ return NULL; } if (qnum >= nitems(sc->sc_txq)) { device_printf(sc->sc_dev, "hal qnum %u out of range, max %zu!\n", qnum, nitems(sc->sc_txq)); ath_hal_releasetxqueue(ah, qnum); return NULL; } if (!ATH_TXQ_SETUP(sc, qnum)) { ath_txq_init(sc, &sc->sc_txq[qnum], qnum); sc->sc_txqsetup |= 1<sc_txq[qnum]; } /* * Setup a hardware data transmit queue for the specified * access control. The hal may not support all requested * queues in which case it will return a reference to a * previously setup queue. We record the mapping from ac's * to h/w queues for use by ath_tx_start and also track * the set of h/w queues being used to optimize work in the * transmit interrupt handler and related routines. */ static int ath_tx_setup(struct ath_softc *sc, int ac, int haltype) { struct ath_txq *txq; if (ac >= nitems(sc->sc_ac2q)) { device_printf(sc->sc_dev, "AC %u out of range, max %zu!\n", ac, nitems(sc->sc_ac2q)); return 0; } txq = ath_txq_setup(sc, HAL_TX_QUEUE_DATA, haltype); if (txq != NULL) { txq->axq_ac = ac; sc->sc_ac2q[ac] = txq; return 1; } else return 0; } /* * Update WME parameters for a transmit queue. */ static int ath_txq_update(struct ath_softc *sc, int ac) { #define ATH_EXPONENT_TO_VALUE(v) ((1<sc_ic; struct ath_txq *txq = sc->sc_ac2q[ac]; struct wmeParams *wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[ac]; struct ath_hal *ah = sc->sc_ah; HAL_TXQ_INFO qi; ath_hal_gettxqueueprops(ah, txq->axq_qnum, &qi); #ifdef IEEE80211_SUPPORT_TDMA if (sc->sc_tdma) { /* * AIFS is zero so there's no pre-transmit wait. The * burst time defines the slot duration and is configured * through net80211. The QCU is setup to not do post-xmit * back off, lockout all lower-priority QCU's, and fire * off the DMA beacon alert timer which is setup based * on the slot configuration. */ qi.tqi_qflags = HAL_TXQ_TXOKINT_ENABLE | HAL_TXQ_TXERRINT_ENABLE | HAL_TXQ_TXURNINT_ENABLE | HAL_TXQ_TXEOLINT_ENABLE | HAL_TXQ_DBA_GATED | HAL_TXQ_BACKOFF_DISABLE | HAL_TXQ_ARB_LOCKOUT_GLOBAL ; qi.tqi_aifs = 0; /* XXX +dbaprep? */ qi.tqi_readyTime = sc->sc_tdmaslotlen; qi.tqi_burstTime = qi.tqi_readyTime; } else { #endif /* * XXX shouldn't this just use the default flags * used in the previous queue setup? */ qi.tqi_qflags = HAL_TXQ_TXOKINT_ENABLE | HAL_TXQ_TXERRINT_ENABLE | HAL_TXQ_TXDESCINT_ENABLE | HAL_TXQ_TXURNINT_ENABLE | HAL_TXQ_TXEOLINT_ENABLE ; qi.tqi_aifs = wmep->wmep_aifsn; qi.tqi_cwmin = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmin); qi.tqi_cwmax = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmax); qi.tqi_readyTime = 0; qi.tqi_burstTime = IEEE80211_TXOP_TO_US(wmep->wmep_txopLimit); #ifdef IEEE80211_SUPPORT_TDMA } #endif DPRINTF(sc, ATH_DEBUG_RESET, "%s: Q%u qflags 0x%x aifs %u cwmin %u cwmax %u burstTime %u\n", __func__, txq->axq_qnum, qi.tqi_qflags, qi.tqi_aifs, qi.tqi_cwmin, qi.tqi_cwmax, qi.tqi_burstTime); if (!ath_hal_settxqueueprops(ah, txq->axq_qnum, &qi)) { device_printf(sc->sc_dev, "unable to update hardware queue " "parameters for %s traffic!\n", ieee80211_wme_acnames[ac]); return 0; } else { ath_hal_resettxqueue(ah, txq->axq_qnum); /* push to h/w */ return 1; } #undef ATH_EXPONENT_TO_VALUE } /* * Callback from the 802.11 layer to update WME parameters. */ int ath_wme_update(struct ieee80211com *ic) { struct ath_softc *sc = ic->ic_softc; return !ath_txq_update(sc, WME_AC_BE) || !ath_txq_update(sc, WME_AC_BK) || !ath_txq_update(sc, WME_AC_VI) || !ath_txq_update(sc, WME_AC_VO) ? EIO : 0; } /* * Reclaim resources for a setup queue. */ static void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq) { ath_hal_releasetxqueue(sc->sc_ah, txq->axq_qnum); sc->sc_txqsetup &= ~(1<axq_qnum); ATH_TXQ_LOCK_DESTROY(txq); } /* * Reclaim all tx queue resources. */ static void ath_tx_cleanup(struct ath_softc *sc) { int i; ATH_TXBUF_LOCK_DESTROY(sc); for (i = 0; i < HAL_NUM_TX_QUEUES; i++) if (ATH_TXQ_SETUP(sc, i)) ath_tx_cleanupq(sc, &sc->sc_txq[i]); } /* * Return h/w rate index for an IEEE rate (w/o basic rate bit) * using the current rates in sc_rixmap. */ int ath_tx_findrix(const struct ath_softc *sc, uint8_t rate) { int rix = sc->sc_rixmap[rate]; /* NB: return lowest rix for invalid rate */ return (rix == 0xff ? 0 : rix); } static void ath_tx_update_stats(struct ath_softc *sc, struct ath_tx_status *ts, struct ath_buf *bf) { struct ieee80211_node *ni = bf->bf_node; struct ieee80211com *ic = &sc->sc_ic; int sr, lr, pri; if (ts->ts_status == 0) { u_int8_t txant = ts->ts_antenna; sc->sc_stats.ast_ant_tx[txant]++; sc->sc_ant_tx[txant]++; if (ts->ts_finaltsi != 0) sc->sc_stats.ast_tx_altrate++; pri = M_WME_GETAC(bf->bf_m); if (pri >= WME_AC_VO) ic->ic_wme.wme_hipri_traffic++; if ((bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 0) ni->ni_inact = ni->ni_inact_reload; } else { if (ts->ts_status & HAL_TXERR_XRETRY) sc->sc_stats.ast_tx_xretries++; if (ts->ts_status & HAL_TXERR_FIFO) sc->sc_stats.ast_tx_fifoerr++; if (ts->ts_status & HAL_TXERR_FILT) sc->sc_stats.ast_tx_filtered++; if (ts->ts_status & HAL_TXERR_XTXOP) sc->sc_stats.ast_tx_xtxop++; if (ts->ts_status & HAL_TXERR_TIMER_EXPIRED) sc->sc_stats.ast_tx_timerexpired++; if (bf->bf_m->m_flags & M_FF) sc->sc_stats.ast_ff_txerr++; } /* XXX when is this valid? */ if (ts->ts_flags & HAL_TX_DESC_CFG_ERR) sc->sc_stats.ast_tx_desccfgerr++; /* * This can be valid for successful frame transmission! * If there's a TX FIFO underrun during aggregate transmission, * the MAC will pad the rest of the aggregate with delimiters. * If a BA is returned, the frame is marked as "OK" and it's up * to the TX completion code to notice which frames weren't * successfully transmitted. */ if (ts->ts_flags & HAL_TX_DATA_UNDERRUN) sc->sc_stats.ast_tx_data_underrun++; if (ts->ts_flags & HAL_TX_DELIM_UNDERRUN) sc->sc_stats.ast_tx_delim_underrun++; sr = ts->ts_shortretry; lr = ts->ts_longretry; sc->sc_stats.ast_tx_shortretry += sr; sc->sc_stats.ast_tx_longretry += lr; } /* * The default completion. If fail is 1, this means * "please don't retry the frame, and just return -1 status * to the net80211 stack. */ void ath_tx_default_comp(struct ath_softc *sc, struct ath_buf *bf, int fail) { struct ath_tx_status *ts = &bf->bf_status.ds_txstat; int st; if (fail == 1) st = -1; else st = ((bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 0) ? ts->ts_status : HAL_TXERR_XRETRY; #if 0 if (bf->bf_state.bfs_dobaw) device_printf(sc->sc_dev, "%s: bf %p: seqno %d: dobaw should've been cleared!\n", __func__, bf, SEQNO(bf->bf_state.bfs_seqno)); #endif if (bf->bf_next != NULL) device_printf(sc->sc_dev, "%s: bf %p: seqno %d: bf_next not NULL!\n", __func__, bf, SEQNO(bf->bf_state.bfs_seqno)); /* * Check if the node software queue is empty; if so * then clear the TIM. * * This needs to be done before the buffer is freed as * otherwise the node reference will have been released * and the node may not actually exist any longer. * * XXX I don't like this belonging here, but it's cleaner * to do it here right now then all the other places * where ath_tx_default_comp() is called. * * XXX TODO: during drain, ensure that the callback is * being called so we get a chance to update the TIM. */ if (bf->bf_node) { ATH_TX_LOCK(sc); ath_tx_update_tim(sc, bf->bf_node, 0); ATH_TX_UNLOCK(sc); } /* * Do any tx complete callback. Note this must * be done before releasing the node reference. * This will free the mbuf, release the net80211 * node and recycle the ath_buf. */ ath_tx_freebuf(sc, bf, st); } /* * Update rate control with the given completion status. */ void ath_tx_update_ratectrl(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_rc_series *rc, struct ath_tx_status *ts, int frmlen, int nframes, int nbad) { struct ath_node *an; /* Only for unicast frames */ if (ni == NULL) return; an = ATH_NODE(ni); ATH_NODE_UNLOCK_ASSERT(an); if ((ts->ts_status & HAL_TXERR_FILT) == 0) { ATH_NODE_LOCK(an); ath_rate_tx_complete(sc, an, rc, ts, frmlen, nframes, nbad); ATH_NODE_UNLOCK(an); } } /* * Process the completion of the given buffer. * * This calls the rate control update and then the buffer completion. * This will either free the buffer or requeue it. In any case, the * bf pointer should be treated as invalid after this function is called. */ void ath_tx_process_buf_completion(struct ath_softc *sc, struct ath_txq *txq, struct ath_tx_status *ts, struct ath_buf *bf) { struct ieee80211_node *ni = bf->bf_node; ATH_TX_UNLOCK_ASSERT(sc); ATH_TXQ_UNLOCK_ASSERT(txq); /* If unicast frame, update general statistics */ if (ni != NULL) { /* update statistics */ ath_tx_update_stats(sc, ts, bf); } /* * Call the completion handler. * The completion handler is responsible for * calling the rate control code. * * Frames with no completion handler get the * rate control code called here. */ if (bf->bf_comp == NULL) { if ((ts->ts_status & HAL_TXERR_FILT) == 0 && (bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 0) { /* * XXX assume this isn't an aggregate * frame. */ ath_tx_update_ratectrl(sc, ni, bf->bf_state.bfs_rc, ts, bf->bf_state.bfs_pktlen, 1, (ts->ts_status == 0 ? 0 : 1)); } ath_tx_default_comp(sc, bf, 0); } else bf->bf_comp(sc, bf, 0); } /* * Process completed xmit descriptors from the specified queue. * Kick the packet scheduler if needed. This can occur from this * particular task. */ static int ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq, int dosched) { struct ath_hal *ah = sc->sc_ah; struct ath_buf *bf; struct ath_desc *ds; struct ath_tx_status *ts; struct ieee80211_node *ni; #ifdef IEEE80211_SUPPORT_SUPERG struct ieee80211com *ic = &sc->sc_ic; #endif /* IEEE80211_SUPPORT_SUPERG */ int nacked; HAL_STATUS status; DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: tx queue %u head %p link %p\n", __func__, txq->axq_qnum, (caddr_t)(uintptr_t) ath_hal_gettxbuf(sc->sc_ah, txq->axq_qnum), txq->axq_link); ATH_KTR(sc, ATH_KTR_TXCOMP, 4, "ath_tx_processq: txq=%u head %p link %p depth %p", txq->axq_qnum, (caddr_t)(uintptr_t) ath_hal_gettxbuf(sc->sc_ah, txq->axq_qnum), txq->axq_link, txq->axq_depth); nacked = 0; for (;;) { ATH_TXQ_LOCK(txq); txq->axq_intrcnt = 0; /* reset periodic desc intr count */ bf = TAILQ_FIRST(&txq->axq_q); if (bf == NULL) { ATH_TXQ_UNLOCK(txq); break; } ds = bf->bf_lastds; /* XXX must be setup correctly! */ ts = &bf->bf_status.ds_txstat; status = ath_hal_txprocdesc(ah, ds, ts); #ifdef ATH_DEBUG if (sc->sc_debug & ATH_DEBUG_XMIT_DESC) ath_printtxbuf(sc, bf, txq->axq_qnum, 0, status == HAL_OK); else if ((sc->sc_debug & ATH_DEBUG_RESET) && (dosched == 0)) ath_printtxbuf(sc, bf, txq->axq_qnum, 0, status == HAL_OK); #endif #ifdef ATH_DEBUG_ALQ if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_EDMA_TXSTATUS)) { if_ath_alq_post(&sc->sc_alq, ATH_ALQ_EDMA_TXSTATUS, sc->sc_tx_statuslen, (char *) ds); } #endif if (status == HAL_EINPROGRESS) { ATH_KTR(sc, ATH_KTR_TXCOMP, 3, "ath_tx_processq: txq=%u, bf=%p ds=%p, HAL_EINPROGRESS", txq->axq_qnum, bf, ds); ATH_TXQ_UNLOCK(txq); break; } ATH_TXQ_REMOVE(txq, bf, bf_list); /* * Sanity check. */ if (txq->axq_qnum != bf->bf_state.bfs_tx_queue) { device_printf(sc->sc_dev, "%s: TXQ=%d: bf=%p, bfs_tx_queue=%d\n", __func__, txq->axq_qnum, bf, bf->bf_state.bfs_tx_queue); } if (txq->axq_qnum != bf->bf_last->bf_state.bfs_tx_queue) { device_printf(sc->sc_dev, "%s: TXQ=%d: bf_last=%p, bfs_tx_queue=%d\n", __func__, txq->axq_qnum, bf->bf_last, bf->bf_last->bf_state.bfs_tx_queue); } #if 0 if (txq->axq_depth > 0) { /* * More frames follow. Mark the buffer busy * so it's not re-used while the hardware may * still re-read the link field in the descriptor. * * Use the last buffer in an aggregate as that * is where the hardware may be - intermediate * descriptors won't be "busy". */ bf->bf_last->bf_flags |= ATH_BUF_BUSY; } else txq->axq_link = NULL; #else bf->bf_last->bf_flags |= ATH_BUF_BUSY; #endif if (bf->bf_state.bfs_aggr) txq->axq_aggr_depth--; ni = bf->bf_node; ATH_KTR(sc, ATH_KTR_TXCOMP, 5, "ath_tx_processq: txq=%u, bf=%p, ds=%p, ni=%p, ts_status=0x%08x", txq->axq_qnum, bf, ds, ni, ts->ts_status); /* * If unicast frame was ack'd update RSSI, * including the last rx time used to * workaround phantom bmiss interrupts. */ if (ni != NULL && ts->ts_status == 0 && ((bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 0)) { nacked++; sc->sc_stats.ast_tx_rssi = ts->ts_rssi; ATH_RSSI_LPF(sc->sc_halstats.ns_avgtxrssi, ts->ts_rssi); } ATH_TXQ_UNLOCK(txq); /* * Update statistics and call completion */ ath_tx_process_buf_completion(sc, txq, ts, bf); /* XXX at this point, bf and ni may be totally invalid */ } #ifdef IEEE80211_SUPPORT_SUPERG /* * Flush fast-frame staging queue when traffic slows. */ if (txq->axq_depth <= 1) ieee80211_ff_flush(ic, txq->axq_ac); #endif /* Kick the software TXQ scheduler */ if (dosched) { ATH_TX_LOCK(sc); ath_txq_sched(sc, txq); ATH_TX_UNLOCK(sc); } ATH_KTR(sc, ATH_KTR_TXCOMP, 1, "ath_tx_processq: txq=%u: done", txq->axq_qnum); return nacked; } #define TXQACTIVE(t, q) ( (t) & (1 << (q))) /* * Deferred processing of transmit interrupt; special-cased * for a single hardware transmit queue (e.g. 5210 and 5211). */ static void ath_tx_proc_q0(void *arg, int npending) { struct ath_softc *sc = arg; uint32_t txqs; ATH_PCU_LOCK(sc); sc->sc_txproc_cnt++; txqs = sc->sc_txq_active; sc->sc_txq_active &= ~txqs; ATH_PCU_UNLOCK(sc); ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); ATH_KTR(sc, ATH_KTR_TXCOMP, 1, "ath_tx_proc_q0: txqs=0x%08x", txqs); if (TXQACTIVE(txqs, 0) && ath_tx_processq(sc, &sc->sc_txq[0], 1)) /* XXX why is lastrx updated in tx code? */ sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah); if (TXQACTIVE(txqs, sc->sc_cabq->axq_qnum)) ath_tx_processq(sc, sc->sc_cabq, 1); sc->sc_wd_timer = 0; if (sc->sc_softled) ath_led_event(sc, sc->sc_txrix); ATH_PCU_LOCK(sc); sc->sc_txproc_cnt--; ATH_PCU_UNLOCK(sc); ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); ath_tx_kick(sc); } /* * Deferred processing of transmit interrupt; special-cased * for four hardware queues, 0-3 (e.g. 5212 w/ WME support). */ static void ath_tx_proc_q0123(void *arg, int npending) { struct ath_softc *sc = arg; int nacked; uint32_t txqs; ATH_PCU_LOCK(sc); sc->sc_txproc_cnt++; txqs = sc->sc_txq_active; sc->sc_txq_active &= ~txqs; ATH_PCU_UNLOCK(sc); ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); ATH_KTR(sc, ATH_KTR_TXCOMP, 1, "ath_tx_proc_q0123: txqs=0x%08x", txqs); /* * Process each active queue. */ nacked = 0; if (TXQACTIVE(txqs, 0)) nacked += ath_tx_processq(sc, &sc->sc_txq[0], 1); if (TXQACTIVE(txqs, 1)) nacked += ath_tx_processq(sc, &sc->sc_txq[1], 1); if (TXQACTIVE(txqs, 2)) nacked += ath_tx_processq(sc, &sc->sc_txq[2], 1); if (TXQACTIVE(txqs, 3)) nacked += ath_tx_processq(sc, &sc->sc_txq[3], 1); if (TXQACTIVE(txqs, sc->sc_cabq->axq_qnum)) ath_tx_processq(sc, sc->sc_cabq, 1); if (nacked) sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah); sc->sc_wd_timer = 0; if (sc->sc_softled) ath_led_event(sc, sc->sc_txrix); ATH_PCU_LOCK(sc); sc->sc_txproc_cnt--; ATH_PCU_UNLOCK(sc); ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); ath_tx_kick(sc); } /* * Deferred processing of transmit interrupt. */ static void ath_tx_proc(void *arg, int npending) { struct ath_softc *sc = arg; int i, nacked; uint32_t txqs; ATH_PCU_LOCK(sc); sc->sc_txproc_cnt++; txqs = sc->sc_txq_active; sc->sc_txq_active &= ~txqs; ATH_PCU_UNLOCK(sc); ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); ATH_KTR(sc, ATH_KTR_TXCOMP, 1, "ath_tx_proc: txqs=0x%08x", txqs); /* * Process each active queue. */ nacked = 0; for (i = 0; i < HAL_NUM_TX_QUEUES; i++) if (ATH_TXQ_SETUP(sc, i) && TXQACTIVE(txqs, i)) nacked += ath_tx_processq(sc, &sc->sc_txq[i], 1); if (nacked) sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah); sc->sc_wd_timer = 0; if (sc->sc_softled) ath_led_event(sc, sc->sc_txrix); ATH_PCU_LOCK(sc); sc->sc_txproc_cnt--; ATH_PCU_UNLOCK(sc); ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); ath_tx_kick(sc); } #undef TXQACTIVE /* * Deferred processing of TXQ rescheduling. */ static void ath_txq_sched_tasklet(void *arg, int npending) { struct ath_softc *sc = arg; int i; /* XXX is skipping ok? */ ATH_PCU_LOCK(sc); #if 0 if (sc->sc_inreset_cnt > 0) { device_printf(sc->sc_dev, "%s: sc_inreset_cnt > 0; skipping\n", __func__); ATH_PCU_UNLOCK(sc); return; } #endif sc->sc_txproc_cnt++; ATH_PCU_UNLOCK(sc); ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); ATH_TX_LOCK(sc); for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { if (ATH_TXQ_SETUP(sc, i)) { ath_txq_sched(sc, &sc->sc_txq[i]); } } ATH_TX_UNLOCK(sc); ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); ATH_PCU_LOCK(sc); sc->sc_txproc_cnt--; ATH_PCU_UNLOCK(sc); } void ath_returnbuf_tail(struct ath_softc *sc, struct ath_buf *bf) { ATH_TXBUF_LOCK_ASSERT(sc); if (bf->bf_flags & ATH_BUF_MGMT) TAILQ_INSERT_TAIL(&sc->sc_txbuf_mgmt, bf, bf_list); else { TAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); sc->sc_txbuf_cnt++; if (sc->sc_txbuf_cnt > ath_txbuf) { device_printf(sc->sc_dev, "%s: sc_txbuf_cnt > %d?\n", __func__, ath_txbuf); sc->sc_txbuf_cnt = ath_txbuf; } } } void ath_returnbuf_head(struct ath_softc *sc, struct ath_buf *bf) { ATH_TXBUF_LOCK_ASSERT(sc); if (bf->bf_flags & ATH_BUF_MGMT) TAILQ_INSERT_HEAD(&sc->sc_txbuf_mgmt, bf, bf_list); else { TAILQ_INSERT_HEAD(&sc->sc_txbuf, bf, bf_list); sc->sc_txbuf_cnt++; if (sc->sc_txbuf_cnt > ATH_TXBUF) { device_printf(sc->sc_dev, "%s: sc_txbuf_cnt > %d?\n", __func__, ATH_TXBUF); sc->sc_txbuf_cnt = ATH_TXBUF; } } } /* * Free the holding buffer if it exists */ void ath_txq_freeholdingbuf(struct ath_softc *sc, struct ath_txq *txq) { ATH_TXBUF_UNLOCK_ASSERT(sc); ATH_TXQ_LOCK_ASSERT(txq); if (txq->axq_holdingbf == NULL) return; txq->axq_holdingbf->bf_flags &= ~ATH_BUF_BUSY; ATH_TXBUF_LOCK(sc); ath_returnbuf_tail(sc, txq->axq_holdingbf); ATH_TXBUF_UNLOCK(sc); txq->axq_holdingbf = NULL; } /* * Add this buffer to the holding queue, freeing the previous * one if it exists. */ static void ath_txq_addholdingbuf(struct ath_softc *sc, struct ath_buf *bf) { struct ath_txq *txq; txq = &sc->sc_txq[bf->bf_state.bfs_tx_queue]; ATH_TXBUF_UNLOCK_ASSERT(sc); ATH_TXQ_LOCK_ASSERT(txq); /* XXX assert ATH_BUF_BUSY is set */ /* XXX assert the tx queue is under the max number */ if (bf->bf_state.bfs_tx_queue > HAL_NUM_TX_QUEUES) { device_printf(sc->sc_dev, "%s: bf=%p: invalid tx queue (%d)\n", __func__, bf, bf->bf_state.bfs_tx_queue); bf->bf_flags &= ~ATH_BUF_BUSY; ath_returnbuf_tail(sc, bf); return; } ath_txq_freeholdingbuf(sc, txq); txq->axq_holdingbf = bf; } /* * Return a buffer to the pool and update the 'busy' flag on the * previous 'tail' entry. * * This _must_ only be called when the buffer is involved in a completed * TX. The logic is that if it was part of an active TX, the previous * buffer on the list is now not involved in a halted TX DMA queue, waiting * for restart (eg for TDMA.) * * The caller must free the mbuf and recycle the node reference. * * XXX This method of handling busy / holding buffers is insanely stupid. * It requires bf_state.bfs_tx_queue to be correctly assigned. It would * be much nicer if buffers in the processq() methods would instead be * always completed there (pushed onto a txq or ath_bufhead) so we knew * exactly what hardware queue they came from in the first place. */ void ath_freebuf(struct ath_softc *sc, struct ath_buf *bf) { struct ath_txq *txq; txq = &sc->sc_txq[bf->bf_state.bfs_tx_queue]; KASSERT((bf->bf_node == NULL), ("%s: bf->bf_node != NULL\n", __func__)); KASSERT((bf->bf_m == NULL), ("%s: bf->bf_m != NULL\n", __func__)); /* * If this buffer is busy, push it onto the holding queue. */ if (bf->bf_flags & ATH_BUF_BUSY) { ATH_TXQ_LOCK(txq); ath_txq_addholdingbuf(sc, bf); ATH_TXQ_UNLOCK(txq); return; } /* * Not a busy buffer, so free normally */ ATH_TXBUF_LOCK(sc); ath_returnbuf_tail(sc, bf); ATH_TXBUF_UNLOCK(sc); } /* * This is currently used by ath_tx_draintxq() and * ath_tx_tid_free_pkts(). * * It recycles a single ath_buf. */ void ath_tx_freebuf(struct ath_softc *sc, struct ath_buf *bf, int status) { struct ieee80211_node *ni = bf->bf_node; struct mbuf *m0 = bf->bf_m; /* * Make sure that we only sync/unload if there's an mbuf. * If not (eg we cloned a buffer), the unload will have already - * occured. + * occurred. */ if (bf->bf_m != NULL) { bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); } bf->bf_node = NULL; bf->bf_m = NULL; /* Free the buffer, it's not needed any longer */ ath_freebuf(sc, bf); /* Pass the buffer back to net80211 - completing it */ ieee80211_tx_complete(ni, m0, status); } static struct ath_buf * ath_tx_draintxq_get_one(struct ath_softc *sc, struct ath_txq *txq) { struct ath_buf *bf; ATH_TXQ_LOCK_ASSERT(txq); /* * Drain the FIFO queue first, then if it's * empty, move to the normal frame queue. */ bf = TAILQ_FIRST(&txq->fifo.axq_q); if (bf != NULL) { /* * Is it the last buffer in this set? * Decrement the FIFO counter. */ if (bf->bf_flags & ATH_BUF_FIFOEND) { if (txq->axq_fifo_depth == 0) { device_printf(sc->sc_dev, "%s: Q%d: fifo_depth=0, fifo.axq_depth=%d?\n", __func__, txq->axq_qnum, txq->fifo.axq_depth); } else txq->axq_fifo_depth--; } ATH_TXQ_REMOVE(&txq->fifo, bf, bf_list); return (bf); } /* * Debugging! */ if (txq->axq_fifo_depth != 0 || txq->fifo.axq_depth != 0) { device_printf(sc->sc_dev, "%s: Q%d: fifo_depth=%d, fifo.axq_depth=%d\n", __func__, txq->axq_qnum, txq->axq_fifo_depth, txq->fifo.axq_depth); } /* * Now drain the pending queue. */ bf = TAILQ_FIRST(&txq->axq_q); if (bf == NULL) { txq->axq_link = NULL; return (NULL); } ATH_TXQ_REMOVE(txq, bf, bf_list); return (bf); } void ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq) { #ifdef ATH_DEBUG struct ath_hal *ah = sc->sc_ah; #endif struct ath_buf *bf; u_int ix; /* * NB: this assumes output has been stopped and * we do not need to block ath_tx_proc */ for (ix = 0;; ix++) { ATH_TXQ_LOCK(txq); bf = ath_tx_draintxq_get_one(sc, txq); if (bf == NULL) { ATH_TXQ_UNLOCK(txq); break; } if (bf->bf_state.bfs_aggr) txq->axq_aggr_depth--; #ifdef ATH_DEBUG if (sc->sc_debug & ATH_DEBUG_RESET) { struct ieee80211com *ic = &sc->sc_ic; int status = 0; /* * EDMA operation has a TX completion FIFO * separate from the TX descriptor, so this * method of checking the "completion" status * is wrong. */ if (! sc->sc_isedma) { status = (ath_hal_txprocdesc(ah, bf->bf_lastds, &bf->bf_status.ds_txstat) == HAL_OK); } ath_printtxbuf(sc, bf, txq->axq_qnum, ix, status); ieee80211_dump_pkt(ic, mtod(bf->bf_m, const uint8_t *), bf->bf_m->m_len, 0, -1); } #endif /* ATH_DEBUG */ /* * Since we're now doing magic in the completion * functions, we -must- call it for aggregation * destinations or BAW tracking will get upset. */ /* * Clear ATH_BUF_BUSY; the completion handler * will free the buffer. */ ATH_TXQ_UNLOCK(txq); bf->bf_flags &= ~ATH_BUF_BUSY; if (bf->bf_comp) bf->bf_comp(sc, bf, 1); else ath_tx_default_comp(sc, bf, 1); } /* * Free the holding buffer if it exists */ ATH_TXQ_LOCK(txq); ath_txq_freeholdingbuf(sc, txq); ATH_TXQ_UNLOCK(txq); /* * Drain software queued frames which are on * active TIDs. */ ath_tx_txq_drain(sc, txq); } static void ath_tx_stopdma(struct ath_softc *sc, struct ath_txq *txq) { struct ath_hal *ah = sc->sc_ah; ATH_TXQ_LOCK_ASSERT(txq); DPRINTF(sc, ATH_DEBUG_RESET, "%s: tx queue [%u] %p, active=%d, hwpending=%d, flags 0x%08x, " "link %p, holdingbf=%p\n", __func__, txq->axq_qnum, (caddr_t)(uintptr_t) ath_hal_gettxbuf(ah, txq->axq_qnum), (int) (!! ath_hal_txqenabled(ah, txq->axq_qnum)), (int) ath_hal_numtxpending(ah, txq->axq_qnum), txq->axq_flags, txq->axq_link, txq->axq_holdingbf); (void) ath_hal_stoptxdma(ah, txq->axq_qnum); /* We've stopped TX DMA, so mark this as stopped. */ txq->axq_flags &= ~ATH_TXQ_PUTRUNNING; #ifdef ATH_DEBUG if ((sc->sc_debug & ATH_DEBUG_RESET) && (txq->axq_holdingbf != NULL)) { ath_printtxbuf(sc, txq->axq_holdingbf, txq->axq_qnum, 0, 0); } #endif } int ath_stoptxdma(struct ath_softc *sc) { struct ath_hal *ah = sc->sc_ah; int i; /* XXX return value */ if (sc->sc_invalid) return 0; if (!sc->sc_invalid) { /* don't touch the hardware if marked invalid */ DPRINTF(sc, ATH_DEBUG_RESET, "%s: tx queue [%u] %p, link %p\n", __func__, sc->sc_bhalq, (caddr_t)(uintptr_t) ath_hal_gettxbuf(ah, sc->sc_bhalq), NULL); /* stop the beacon queue */ (void) ath_hal_stoptxdma(ah, sc->sc_bhalq); /* Stop the data queues */ for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { if (ATH_TXQ_SETUP(sc, i)) { ATH_TXQ_LOCK(&sc->sc_txq[i]); ath_tx_stopdma(sc, &sc->sc_txq[i]); ATH_TXQ_UNLOCK(&sc->sc_txq[i]); } } } return 1; } #ifdef ATH_DEBUG void ath_tx_dump(struct ath_softc *sc, struct ath_txq *txq) { struct ath_hal *ah = sc->sc_ah; struct ath_buf *bf; int i = 0; if (! (sc->sc_debug & ATH_DEBUG_RESET)) return; device_printf(sc->sc_dev, "%s: Q%d: begin\n", __func__, txq->axq_qnum); TAILQ_FOREACH(bf, &txq->axq_q, bf_list) { ath_printtxbuf(sc, bf, txq->axq_qnum, i, ath_hal_txprocdesc(ah, bf->bf_lastds, &bf->bf_status.ds_txstat) == HAL_OK); i++; } device_printf(sc->sc_dev, "%s: Q%d: end\n", __func__, txq->axq_qnum); } #endif /* ATH_DEBUG */ /* * Drain the transmit queues and reclaim resources. */ void ath_legacy_tx_drain(struct ath_softc *sc, ATH_RESET_TYPE reset_type) { struct ath_hal *ah = sc->sc_ah; struct ath_buf *bf_last; int i; (void) ath_stoptxdma(sc); /* * Dump the queue contents */ for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { /* * XXX TODO: should we just handle the completed TX frames * here, whether or not the reset is a full one or not? */ if (ATH_TXQ_SETUP(sc, i)) { #ifdef ATH_DEBUG if (sc->sc_debug & ATH_DEBUG_RESET) ath_tx_dump(sc, &sc->sc_txq[i]); #endif /* ATH_DEBUG */ if (reset_type == ATH_RESET_NOLOSS) { ath_tx_processq(sc, &sc->sc_txq[i], 0); ATH_TXQ_LOCK(&sc->sc_txq[i]); /* * Free the holding buffer; DMA is now * stopped. */ ath_txq_freeholdingbuf(sc, &sc->sc_txq[i]); /* * Setup the link pointer to be the * _last_ buffer/descriptor in the list. * If there's nothing in the list, set it * to NULL. */ bf_last = ATH_TXQ_LAST(&sc->sc_txq[i], axq_q_s); if (bf_last != NULL) { ath_hal_gettxdesclinkptr(ah, bf_last->bf_lastds, &sc->sc_txq[i].axq_link); } else { sc->sc_txq[i].axq_link = NULL; } ATH_TXQ_UNLOCK(&sc->sc_txq[i]); } else ath_tx_draintxq(sc, &sc->sc_txq[i]); } } #ifdef ATH_DEBUG if (sc->sc_debug & ATH_DEBUG_RESET) { struct ath_buf *bf = TAILQ_FIRST(&sc->sc_bbuf); if (bf != NULL && bf->bf_m != NULL) { ath_printtxbuf(sc, bf, sc->sc_bhalq, 0, ath_hal_txprocdesc(ah, bf->bf_lastds, &bf->bf_status.ds_txstat) == HAL_OK); ieee80211_dump_pkt(&sc->sc_ic, mtod(bf->bf_m, const uint8_t *), bf->bf_m->m_len, 0, -1); } } #endif /* ATH_DEBUG */ sc->sc_wd_timer = 0; } /* * Update internal state after a channel change. */ static void ath_chan_change(struct ath_softc *sc, struct ieee80211_channel *chan) { enum ieee80211_phymode mode; /* * Change channels and update the h/w rate map * if we're switching; e.g. 11a to 11b/g. */ mode = ieee80211_chan2mode(chan); if (mode != sc->sc_curmode) ath_setcurmode(sc, mode); sc->sc_curchan = chan; } /* * Set/change channels. If the channel is really being changed, * it's done by resetting the chip. To accomplish this we must * first cleanup any pending DMA, then restart stuff after a la * ath_init. */ static int ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan) { struct ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = sc->sc_ah; int ret = 0; /* Treat this as an interface reset */ ATH_PCU_UNLOCK_ASSERT(sc); ATH_UNLOCK_ASSERT(sc); - /* (Try to) stop TX/RX from occuring */ + /* (Try to) stop TX/RX from occurring */ taskqueue_block(sc->sc_tq); ATH_PCU_LOCK(sc); /* Disable interrupts */ ath_hal_intrset(ah, 0); /* Stop new RX/TX/interrupt completion */ if (ath_reset_grablock(sc, 1) == 0) { device_printf(sc->sc_dev, "%s: concurrent reset! Danger!\n", __func__); } /* Stop pending RX/TX completion */ ath_txrx_stop_locked(sc); ATH_PCU_UNLOCK(sc); DPRINTF(sc, ATH_DEBUG_RESET, "%s: %u (%u MHz, flags 0x%x)\n", __func__, ieee80211_chan2ieee(ic, chan), chan->ic_freq, chan->ic_flags); if (chan != sc->sc_curchan) { HAL_STATUS status; /* * To switch channels clear any pending DMA operations; * wait long enough for the RX fifo to drain, reset the * hardware at the new frequency, and then re-enable * the relevant bits of the h/w. */ #if 0 ath_hal_intrset(ah, 0); /* disable interrupts */ #endif ath_stoprecv(sc, 1); /* turn off frame recv */ /* * First, handle completed TX/RX frames. */ ath_rx_flush(sc); ath_draintxq(sc, ATH_RESET_NOLOSS); /* * Next, flush the non-scheduled frames. */ ath_draintxq(sc, ATH_RESET_FULL); /* clear pending tx frames */ ath_update_chainmasks(sc, chan); ath_hal_setchainmasks(sc->sc_ah, sc->sc_cur_txchainmask, sc->sc_cur_rxchainmask); if (!ath_hal_reset(ah, sc->sc_opmode, chan, AH_TRUE, HAL_RESET_NORMAL, &status)) { device_printf(sc->sc_dev, "%s: unable to reset " "channel %u (%u MHz, flags 0x%x), hal status %u\n", __func__, ieee80211_chan2ieee(ic, chan), chan->ic_freq, chan->ic_flags, status); ret = EIO; goto finish; } sc->sc_diversity = ath_hal_getdiversity(ah); ATH_RX_LOCK(sc); sc->sc_rx_stopped = 1; sc->sc_rx_resetted = 1; ATH_RX_UNLOCK(sc); /* Let DFS at it in case it's a DFS channel */ ath_dfs_radar_enable(sc, chan); /* Let spectral at in case spectral is enabled */ ath_spectral_enable(sc, chan); /* * Let bluetooth coexistence at in case it's needed for this * channel */ ath_btcoex_enable(sc, ic->ic_curchan); /* * If we're doing TDMA, enforce the TXOP limitation for chips * that support it. */ if (sc->sc_hasenforcetxop && sc->sc_tdma) ath_hal_setenforcetxop(sc->sc_ah, 1); else ath_hal_setenforcetxop(sc->sc_ah, 0); /* * Re-enable rx framework. */ if (ath_startrecv(sc) != 0) { device_printf(sc->sc_dev, "%s: unable to restart recv logic\n", __func__); ret = EIO; goto finish; } /* * Change channels and update the h/w rate map * if we're switching; e.g. 11a to 11b/g. */ ath_chan_change(sc, chan); /* * Reset clears the beacon timers; reset them * here if needed. */ if (sc->sc_beacons) { /* restart beacons */ #ifdef IEEE80211_SUPPORT_TDMA if (sc->sc_tdma) ath_tdma_config(sc, NULL); else #endif ath_beacon_config(sc, NULL); } /* * Re-enable interrupts. */ #if 0 ath_hal_intrset(ah, sc->sc_imask); #endif } finish: ATH_PCU_LOCK(sc); sc->sc_inreset_cnt--; /* XXX only do this if sc_inreset_cnt == 0? */ ath_hal_intrset(ah, sc->sc_imask); ATH_PCU_UNLOCK(sc); ath_txrx_start(sc); /* XXX ath_start? */ return ret; } /* * Periodically recalibrate the PHY to account * for temperature/environment changes. */ static void ath_calibrate(void *arg) { struct ath_softc *sc = arg; struct ath_hal *ah = sc->sc_ah; struct ieee80211com *ic = &sc->sc_ic; HAL_BOOL longCal, isCalDone = AH_TRUE; HAL_BOOL aniCal, shortCal = AH_FALSE; int nextcal; ATH_LOCK_ASSERT(sc); /* * Force the hardware awake for ANI work. */ ath_power_set_power_state(sc, HAL_PM_AWAKE); /* Skip trying to do this if we're in reset */ if (sc->sc_inreset_cnt) goto restart; if (ic->ic_flags & IEEE80211_F_SCAN) /* defer, off channel */ goto restart; longCal = (ticks - sc->sc_lastlongcal >= ath_longcalinterval*hz); aniCal = (ticks - sc->sc_lastani >= ath_anicalinterval*hz/1000); if (sc->sc_doresetcal) shortCal = (ticks - sc->sc_lastshortcal >= ath_shortcalinterval*hz/1000); DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: shortCal=%d; longCal=%d; aniCal=%d\n", __func__, shortCal, longCal, aniCal); if (aniCal) { sc->sc_stats.ast_ani_cal++; sc->sc_lastani = ticks; ath_hal_ani_poll(ah, sc->sc_curchan); } if (longCal) { sc->sc_stats.ast_per_cal++; sc->sc_lastlongcal = ticks; if (ath_hal_getrfgain(ah) == HAL_RFGAIN_NEED_CHANGE) { /* * Rfgain is out of bounds, reset the chip * to load new gain values. */ DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: rfgain change\n", __func__); sc->sc_stats.ast_per_rfgain++; sc->sc_resetcal = 0; sc->sc_doresetcal = AH_TRUE; taskqueue_enqueue(sc->sc_tq, &sc->sc_resettask); callout_reset(&sc->sc_cal_ch, 1, ath_calibrate, sc); ath_power_restore_power_state(sc); return; } /* * If this long cal is after an idle period, then * reset the data collection state so we start fresh. */ if (sc->sc_resetcal) { (void) ath_hal_calreset(ah, sc->sc_curchan); sc->sc_lastcalreset = ticks; sc->sc_lastshortcal = ticks; sc->sc_resetcal = 0; sc->sc_doresetcal = AH_TRUE; } } /* Only call if we're doing a short/long cal, not for ANI calibration */ if (shortCal || longCal) { isCalDone = AH_FALSE; if (ath_hal_calibrateN(ah, sc->sc_curchan, longCal, &isCalDone)) { if (longCal) { /* * Calibrate noise floor data again in case of change. */ ath_hal_process_noisefloor(ah); } } else { DPRINTF(sc, ATH_DEBUG_ANY, "%s: calibration of channel %u failed\n", __func__, sc->sc_curchan->ic_freq); sc->sc_stats.ast_per_calfail++; } if (shortCal) sc->sc_lastshortcal = ticks; } if (!isCalDone) { restart: /* * Use a shorter interval to potentially collect multiple * data samples required to complete calibration. Once * we're told the work is done we drop back to a longer * interval between requests. We're more aggressive doing * work when operating as an AP to improve operation right * after startup. */ sc->sc_lastshortcal = ticks; nextcal = ath_shortcalinterval*hz/1000; if (sc->sc_opmode != HAL_M_HOSTAP) nextcal *= 10; sc->sc_doresetcal = AH_TRUE; } else { /* nextcal should be the shortest time for next event */ nextcal = ath_longcalinterval*hz; if (sc->sc_lastcalreset == 0) sc->sc_lastcalreset = sc->sc_lastlongcal; else if (ticks - sc->sc_lastcalreset >= ath_resetcalinterval*hz) sc->sc_resetcal = 1; /* setup reset next trip */ sc->sc_doresetcal = AH_FALSE; } /* ANI calibration may occur more often than short/long/resetcal */ if (ath_anicalinterval > 0) nextcal = MIN(nextcal, ath_anicalinterval*hz/1000); if (nextcal != 0) { DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: next +%u (%sisCalDone)\n", __func__, nextcal, isCalDone ? "" : "!"); callout_reset(&sc->sc_cal_ch, nextcal, ath_calibrate, sc); } else { DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: calibration disabled\n", __func__); /* NB: don't rearm timer */ } /* * Restore power state now that we're done. */ ath_power_restore_power_state(sc); } static void ath_scan_start(struct ieee80211com *ic) { struct ath_softc *sc = ic->ic_softc; struct ath_hal *ah = sc->sc_ah; u_int32_t rfilt; /* XXX calibration timer? */ /* XXXGL: is constant ieee80211broadcastaddr a correct choice? */ ATH_LOCK(sc); sc->sc_scanning = 1; sc->sc_syncbeacon = 0; rfilt = ath_calcrxfilter(sc); ATH_UNLOCK(sc); ATH_PCU_LOCK(sc); ath_hal_setrxfilter(ah, rfilt); ath_hal_setassocid(ah, ieee80211broadcastaddr, 0); ATH_PCU_UNLOCK(sc); DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s aid 0\n", __func__, rfilt, ether_sprintf(ieee80211broadcastaddr)); } static void ath_scan_end(struct ieee80211com *ic) { struct ath_softc *sc = ic->ic_softc; struct ath_hal *ah = sc->sc_ah; u_int32_t rfilt; ATH_LOCK(sc); sc->sc_scanning = 0; rfilt = ath_calcrxfilter(sc); ATH_UNLOCK(sc); ATH_PCU_LOCK(sc); ath_hal_setrxfilter(ah, rfilt); ath_hal_setassocid(ah, sc->sc_curbssid, sc->sc_curaid); ath_hal_process_noisefloor(ah); ATH_PCU_UNLOCK(sc); DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s aid 0x%x\n", __func__, rfilt, ether_sprintf(sc->sc_curbssid), sc->sc_curaid); } #ifdef ATH_ENABLE_11N /* * For now, just do a channel change. * * Later, we'll go through the hard slog of suspending tx/rx, changing rate * control state and resetting the hardware without dropping frames out * of the queue. * * The unfortunate trouble here is making absolutely sure that the * channel width change has propagated enough so the hardware * absolutely isn't handed bogus frames for it's current operating * mode. (Eg, 40MHz frames in 20MHz mode.) Since TX and RX can and * does occur in parallel, we need to make certain we've blocked * any further ongoing TX (and RX, that can cause raw TX) * before we do this. */ static void ath_update_chw(struct ieee80211com *ic) { struct ath_softc *sc = ic->ic_softc; DPRINTF(sc, ATH_DEBUG_STATE, "%s: called\n", __func__); ath_set_channel(ic); } #endif /* ATH_ENABLE_11N */ static void ath_set_channel(struct ieee80211com *ic) { struct ath_softc *sc = ic->ic_softc; ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); (void) ath_chan_set(sc, ic->ic_curchan); /* * If we are returning to our bss channel then mark state * so the next recv'd beacon's tsf will be used to sync the * beacon timers. Note that since we only hear beacons in * sta/ibss mode this has no effect in other operating modes. */ ATH_LOCK(sc); if (!sc->sc_scanning && ic->ic_curchan == ic->ic_bsschan) sc->sc_syncbeacon = 1; ath_power_restore_power_state(sc); ATH_UNLOCK(sc); } /* * Walk the vap list and check if there any vap's in RUN state. */ static int ath_isanyrunningvaps(struct ieee80211vap *this) { struct ieee80211com *ic = this->iv_ic; struct ieee80211vap *vap; IEEE80211_LOCK_ASSERT(ic); TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { if (vap != this && vap->iv_state >= IEEE80211_S_RUN) return 1; } return 0; } static int ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct ieee80211com *ic = vap->iv_ic; struct ath_softc *sc = ic->ic_softc; struct ath_vap *avp = ATH_VAP(vap); struct ath_hal *ah = sc->sc_ah; struct ieee80211_node *ni = NULL; int i, error, stamode; u_int32_t rfilt; int csa_run_transition = 0; enum ieee80211_state ostate = vap->iv_state; static const HAL_LED_STATE leds[] = { HAL_LED_INIT, /* IEEE80211_S_INIT */ HAL_LED_SCAN, /* IEEE80211_S_SCAN */ HAL_LED_AUTH, /* IEEE80211_S_AUTH */ HAL_LED_ASSOC, /* IEEE80211_S_ASSOC */ HAL_LED_RUN, /* IEEE80211_S_CAC */ HAL_LED_RUN, /* IEEE80211_S_RUN */ HAL_LED_RUN, /* IEEE80211_S_CSA */ HAL_LED_RUN, /* IEEE80211_S_SLEEP */ }; DPRINTF(sc, ATH_DEBUG_STATE, "%s: %s -> %s\n", __func__, ieee80211_state_name[ostate], ieee80211_state_name[nstate]); /* * net80211 _should_ have the comlock asserted at this point. * There are some comments around the calls to vap->iv_newstate * which indicate that it (newstate) may end up dropping the * lock. This and the subsequent lock assert check after newstate * are an attempt to catch these and figure out how/why. */ IEEE80211_LOCK_ASSERT(ic); /* Before we touch the hardware - wake it up */ ATH_LOCK(sc); /* * If the NIC is in anything other than SLEEP state, * we need to ensure that self-generated frames are * set for PWRMGT=0. Otherwise we may end up with * strange situations. * * XXX TODO: is this actually the case? :-) */ if (nstate != IEEE80211_S_SLEEP) ath_power_setselfgen(sc, HAL_PM_AWAKE); /* * Now, wake the thing up. */ ath_power_set_power_state(sc, HAL_PM_AWAKE); /* * And stop the calibration callout whilst we have * ATH_LOCK held. */ callout_stop(&sc->sc_cal_ch); ATH_UNLOCK(sc); if (ostate == IEEE80211_S_CSA && nstate == IEEE80211_S_RUN) csa_run_transition = 1; ath_hal_setledstate(ah, leds[nstate]); /* set LED */ if (nstate == IEEE80211_S_SCAN) { /* * Scanning: turn off beacon miss and don't beacon. * Mark beacon state so when we reach RUN state we'll * [re]setup beacons. Unblock the task q thread so * deferred interrupt processing is done. */ /* Ensure we stay awake during scan */ ATH_LOCK(sc); ath_power_setselfgen(sc, HAL_PM_AWAKE); ath_power_setpower(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); ath_hal_intrset(ah, sc->sc_imask &~ (HAL_INT_SWBA | HAL_INT_BMISS)); sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS); sc->sc_beacons = 0; taskqueue_unblock(sc->sc_tq); } ni = ieee80211_ref_node(vap->iv_bss); rfilt = ath_calcrxfilter(sc); stamode = (vap->iv_opmode == IEEE80211_M_STA || vap->iv_opmode == IEEE80211_M_AHDEMO || vap->iv_opmode == IEEE80211_M_IBSS); /* * XXX Dont need to do this (and others) if we've transitioned * from SLEEP->RUN. */ if (stamode && nstate == IEEE80211_S_RUN) { sc->sc_curaid = ni->ni_associd; IEEE80211_ADDR_COPY(sc->sc_curbssid, ni->ni_bssid); ath_hal_setassocid(ah, sc->sc_curbssid, sc->sc_curaid); } DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s aid 0x%x\n", __func__, rfilt, ether_sprintf(sc->sc_curbssid), sc->sc_curaid); ath_hal_setrxfilter(ah, rfilt); /* XXX is this to restore keycache on resume? */ if (vap->iv_opmode != IEEE80211_M_STA && (vap->iv_flags & IEEE80211_F_PRIVACY)) { for (i = 0; i < IEEE80211_WEP_NKID; i++) if (ath_hal_keyisvalid(ah, i)) ath_hal_keysetmac(ah, i, ni->ni_bssid); } /* * Invoke the parent method to do net80211 work. */ error = avp->av_newstate(vap, nstate, arg); if (error != 0) goto bad; /* * See above: ensure av_newstate() doesn't drop the lock * on us. */ IEEE80211_LOCK_ASSERT(ic); if (nstate == IEEE80211_S_RUN) { /* NB: collect bss node again, it may have changed */ ieee80211_free_node(ni); ni = ieee80211_ref_node(vap->iv_bss); DPRINTF(sc, ATH_DEBUG_STATE, "%s(RUN): iv_flags 0x%08x bintvl %d bssid %s " "capinfo 0x%04x chan %d\n", __func__, vap->iv_flags, ni->ni_intval, ether_sprintf(ni->ni_bssid), ni->ni_capinfo, ieee80211_chan2ieee(ic, ic->ic_curchan)); switch (vap->iv_opmode) { #ifdef IEEE80211_SUPPORT_TDMA case IEEE80211_M_AHDEMO: if ((vap->iv_caps & IEEE80211_C_TDMA) == 0) break; /* fall thru... */ #endif case IEEE80211_M_HOSTAP: case IEEE80211_M_IBSS: case IEEE80211_M_MBSS: /* * Allocate and setup the beacon frame. * * Stop any previous beacon DMA. This may be * necessary, for example, when an ibss merge * causes reconfiguration; there will be a state * transition from RUN->RUN that means we may * be called with beacon transmission active. */ ath_hal_stoptxdma(ah, sc->sc_bhalq); error = ath_beacon_alloc(sc, ni); if (error != 0) goto bad; /* * If joining an adhoc network defer beacon timer * configuration to the next beacon frame so we * have a current TSF to use. Otherwise we're * starting an ibss/bss so there's no need to delay; * if this is the first vap moving to RUN state, then * beacon state needs to be [re]configured. */ if (vap->iv_opmode == IEEE80211_M_IBSS && ni->ni_tstamp.tsf != 0) { sc->sc_syncbeacon = 1; } else if (!sc->sc_beacons) { #ifdef IEEE80211_SUPPORT_TDMA if (vap->iv_caps & IEEE80211_C_TDMA) ath_tdma_config(sc, vap); else #endif ath_beacon_config(sc, vap); sc->sc_beacons = 1; } break; case IEEE80211_M_STA: /* * Defer beacon timer configuration to the next * beacon frame so we have a current TSF to use * (any TSF collected when scanning is likely old). * However if it's due to a CSA -> RUN transition, * force a beacon update so we pick up a lack of * beacons from an AP in CAC and thus force a * scan. * * And, there's also corner cases here where * after a scan, the AP may have disappeared. * In that case, we may not receive an actual * beacon to update the beacon timer and thus we * won't get notified of the missing beacons. */ if (ostate != IEEE80211_S_RUN && ostate != IEEE80211_S_SLEEP) { DPRINTF(sc, ATH_DEBUG_BEACON, "%s: STA; syncbeacon=1\n", __func__); sc->sc_syncbeacon = 1; if (csa_run_transition) ath_beacon_config(sc, vap); /* * PR: kern/175227 * * Reconfigure beacons during reset; as otherwise * we won't get the beacon timers reprogrammed * after a reset and thus we won't pick up a * beacon miss interrupt. * * Hopefully we'll see a beacon before the BMISS * timer fires (too often), leading to a STA * disassociation. */ sc->sc_beacons = 1; } break; case IEEE80211_M_MONITOR: /* * Monitor mode vaps have only INIT->RUN and RUN->RUN * transitions so we must re-enable interrupts here to * handle the case of a single monitor mode vap. */ ath_hal_intrset(ah, sc->sc_imask); break; case IEEE80211_M_WDS: break; default: break; } /* * Let the hal process statistics collected during a * scan so it can provide calibrated noise floor data. */ ath_hal_process_noisefloor(ah); /* * Reset rssi stats; maybe not the best place... */ sc->sc_halstats.ns_avgbrssi = ATH_RSSI_DUMMY_MARKER; sc->sc_halstats.ns_avgrssi = ATH_RSSI_DUMMY_MARKER; sc->sc_halstats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER; /* * Force awake for RUN mode. */ ATH_LOCK(sc); ath_power_setselfgen(sc, HAL_PM_AWAKE); ath_power_setpower(sc, HAL_PM_AWAKE); /* * Finally, start any timers and the task q thread * (in case we didn't go through SCAN state). */ if (ath_longcalinterval != 0) { /* start periodic recalibration timer */ callout_reset(&sc->sc_cal_ch, 1, ath_calibrate, sc); } else { DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: calibration disabled\n", __func__); } ATH_UNLOCK(sc); taskqueue_unblock(sc->sc_tq); } else if (nstate == IEEE80211_S_INIT) { /* * If there are no vaps left in RUN state then * shutdown host/driver operation: * o disable interrupts * o disable the task queue thread * o mark beacon processing as stopped */ if (!ath_isanyrunningvaps(vap)) { sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS); /* disable interrupts */ ath_hal_intrset(ah, sc->sc_imask &~ HAL_INT_GLOBAL); taskqueue_block(sc->sc_tq); sc->sc_beacons = 0; } #ifdef IEEE80211_SUPPORT_TDMA ath_hal_setcca(ah, AH_TRUE); #endif } else if (nstate == IEEE80211_S_SLEEP) { /* We're going to sleep, so transition appropriately */ /* For now, only do this if we're a single STA vap */ if (sc->sc_nvaps == 1 && vap->iv_opmode == IEEE80211_M_STA) { DPRINTF(sc, ATH_DEBUG_BEACON, "%s: syncbeacon=%d\n", __func__, sc->sc_syncbeacon); ATH_LOCK(sc); /* * Always at least set the self-generated * frame config to set PWRMGT=1. */ ath_power_setselfgen(sc, HAL_PM_NETWORK_SLEEP); /* * If we're not syncing beacons, transition * to NETWORK_SLEEP. * * We stay awake if syncbeacon > 0 in case * we need to listen for some beacons otherwise * our beacon timer config may be wrong. */ if (sc->sc_syncbeacon == 0) { ath_power_setpower(sc, HAL_PM_NETWORK_SLEEP); } ATH_UNLOCK(sc); } } bad: ieee80211_free_node(ni); /* * Restore the power state - either to what it was, or * to network_sleep if it's alright. */ ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); return error; } /* * Allocate a key cache slot to the station so we can * setup a mapping from key index to node. The key cache * slot is needed for managing antenna state and for * compression when stations do not use crypto. We do * it uniliaterally here; if crypto is employed this slot * will be reassigned. */ static void ath_setup_stationkey(struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ath_softc *sc = vap->iv_ic->ic_softc; ieee80211_keyix keyix, rxkeyix; /* XXX should take a locked ref to vap->iv_bss */ if (!ath_key_alloc(vap, &ni->ni_ucastkey, &keyix, &rxkeyix)) { /* * Key cache is full; we'll fall back to doing * the more expensive lookup in software. Note * this also means no h/w compression. */ /* XXX msg+statistic */ } else { /* XXX locking? */ ni->ni_ucastkey.wk_keyix = keyix; ni->ni_ucastkey.wk_rxkeyix = rxkeyix; /* NB: must mark device key to get called back on delete */ ni->ni_ucastkey.wk_flags |= IEEE80211_KEY_DEVKEY; IEEE80211_ADDR_COPY(ni->ni_ucastkey.wk_macaddr, ni->ni_macaddr); /* NB: this will create a pass-thru key entry */ ath_keyset(sc, vap, &ni->ni_ucastkey, vap->iv_bss); } } /* * Setup driver-specific state for a newly associated node. * Note that we're called also on a re-associate, the isnew * param tells us if this is the first time or not. */ static void ath_newassoc(struct ieee80211_node *ni, int isnew) { struct ath_node *an = ATH_NODE(ni); struct ieee80211vap *vap = ni->ni_vap; struct ath_softc *sc = vap->iv_ic->ic_softc; const struct ieee80211_txparam *tp = ni->ni_txparms; an->an_mcastrix = ath_tx_findrix(sc, tp->mcastrate); an->an_mgmtrix = ath_tx_findrix(sc, tp->mgmtrate); DPRINTF(sc, ATH_DEBUG_NODE, "%s: %6D: reassoc; isnew=%d, is_powersave=%d\n", __func__, ni->ni_macaddr, ":", isnew, an->an_is_powersave); ATH_NODE_LOCK(an); ath_rate_newassoc(sc, an, isnew); ATH_NODE_UNLOCK(an); if (isnew && (vap->iv_flags & IEEE80211_F_PRIVACY) == 0 && sc->sc_hasclrkey && ni->ni_ucastkey.wk_keyix == IEEE80211_KEYIX_NONE) ath_setup_stationkey(ni); /* * If we're reassociating, make sure that any paused queues * get unpaused. * - * Now, we may hvae frames in the hardware queue for this node. + * Now, we may have frames in the hardware queue for this node. * So if we are reassociating and there are frames in the queue, * we need to go through the cleanup path to ensure that they're * marked as non-aggregate. */ if (! isnew) { DPRINTF(sc, ATH_DEBUG_NODE, "%s: %6D: reassoc; is_powersave=%d\n", __func__, ni->ni_macaddr, ":", an->an_is_powersave); /* XXX for now, we can't hold the lock across assoc */ ath_tx_node_reassoc(sc, an); /* XXX for now, we can't hold the lock across wakeup */ if (an->an_is_powersave) ath_tx_node_wakeup(sc, an); } } static int ath_setregdomain(struct ieee80211com *ic, struct ieee80211_regdomain *reg, int nchans, struct ieee80211_channel chans[]) { struct ath_softc *sc = ic->ic_softc; struct ath_hal *ah = sc->sc_ah; HAL_STATUS status; DPRINTF(sc, ATH_DEBUG_REGDOMAIN, "%s: rd %u cc %u location %c%s\n", __func__, reg->regdomain, reg->country, reg->location, reg->ecm ? " ecm" : ""); status = ath_hal_set_channels(ah, chans, nchans, reg->country, reg->regdomain); if (status != HAL_OK) { DPRINTF(sc, ATH_DEBUG_REGDOMAIN, "%s: failed, status %u\n", __func__, status); return EINVAL; /* XXX */ } return 0; } static void ath_getradiocaps(struct ieee80211com *ic, int maxchans, int *nchans, struct ieee80211_channel chans[]) { struct ath_softc *sc = ic->ic_softc; struct ath_hal *ah = sc->sc_ah; DPRINTF(sc, ATH_DEBUG_REGDOMAIN, "%s: use rd %u cc %d\n", __func__, SKU_DEBUG, CTRY_DEFAULT); /* XXX check return */ (void) ath_hal_getchannels(ah, chans, maxchans, nchans, HAL_MODE_ALL, CTRY_DEFAULT, SKU_DEBUG, AH_TRUE); } static int ath_getchannels(struct ath_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = sc->sc_ah; HAL_STATUS status; /* * Collect channel set based on EEPROM contents. */ status = ath_hal_init_channels(ah, ic->ic_channels, IEEE80211_CHAN_MAX, &ic->ic_nchans, HAL_MODE_ALL, CTRY_DEFAULT, SKU_NONE, AH_TRUE); if (status != HAL_OK) { device_printf(sc->sc_dev, "%s: unable to collect channel list from hal, status %d\n", __func__, status); return EINVAL; } (void) ath_hal_getregdomain(ah, &sc->sc_eerd); ath_hal_getcountrycode(ah, &sc->sc_eecc); /* NB: cannot fail */ /* XXX map Atheros sku's to net80211 SKU's */ /* XXX net80211 types too small */ ic->ic_regdomain.regdomain = (uint16_t) sc->sc_eerd; ic->ic_regdomain.country = (uint16_t) sc->sc_eecc; ic->ic_regdomain.isocc[0] = ' '; /* XXX don't know */ ic->ic_regdomain.isocc[1] = ' '; ic->ic_regdomain.ecm = 1; ic->ic_regdomain.location = 'I'; DPRINTF(sc, ATH_DEBUG_REGDOMAIN, "%s: eeprom rd %u cc %u (mapped rd %u cc %u) location %c%s\n", __func__, sc->sc_eerd, sc->sc_eecc, ic->ic_regdomain.regdomain, ic->ic_regdomain.country, ic->ic_regdomain.location, ic->ic_regdomain.ecm ? " ecm" : ""); return 0; } static int ath_rate_setup(struct ath_softc *sc, u_int mode) { struct ath_hal *ah = sc->sc_ah; const HAL_RATE_TABLE *rt; switch (mode) { case IEEE80211_MODE_11A: rt = ath_hal_getratetable(ah, HAL_MODE_11A); break; case IEEE80211_MODE_HALF: rt = ath_hal_getratetable(ah, HAL_MODE_11A_HALF_RATE); break; case IEEE80211_MODE_QUARTER: rt = ath_hal_getratetable(ah, HAL_MODE_11A_QUARTER_RATE); break; case IEEE80211_MODE_11B: rt = ath_hal_getratetable(ah, HAL_MODE_11B); break; case IEEE80211_MODE_11G: rt = ath_hal_getratetable(ah, HAL_MODE_11G); break; case IEEE80211_MODE_TURBO_A: rt = ath_hal_getratetable(ah, HAL_MODE_108A); break; case IEEE80211_MODE_TURBO_G: rt = ath_hal_getratetable(ah, HAL_MODE_108G); break; case IEEE80211_MODE_STURBO_A: rt = ath_hal_getratetable(ah, HAL_MODE_TURBO); break; case IEEE80211_MODE_11NA: rt = ath_hal_getratetable(ah, HAL_MODE_11NA_HT20); break; case IEEE80211_MODE_11NG: rt = ath_hal_getratetable(ah, HAL_MODE_11NG_HT20); break; default: DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid mode %u\n", __func__, mode); return 0; } sc->sc_rates[mode] = rt; return (rt != NULL); } static void ath_setcurmode(struct ath_softc *sc, enum ieee80211_phymode mode) { /* NB: on/off times from the Atheros NDIS driver, w/ permission */ static const struct { u_int rate; /* tx/rx 802.11 rate */ u_int16_t timeOn; /* LED on time (ms) */ u_int16_t timeOff; /* LED off time (ms) */ } blinkrates[] = { { 108, 40, 10 }, { 96, 44, 11 }, { 72, 50, 13 }, { 48, 57, 14 }, { 36, 67, 16 }, { 24, 80, 20 }, { 22, 100, 25 }, { 18, 133, 34 }, { 12, 160, 40 }, { 10, 200, 50 }, { 6, 240, 58 }, { 4, 267, 66 }, { 2, 400, 100 }, { 0, 500, 130 }, /* XXX half/quarter rates */ }; const HAL_RATE_TABLE *rt; int i, j; memset(sc->sc_rixmap, 0xff, sizeof(sc->sc_rixmap)); rt = sc->sc_rates[mode]; KASSERT(rt != NULL, ("no h/w rate set for phy mode %u", mode)); for (i = 0; i < rt->rateCount; i++) { uint8_t ieeerate = rt->info[i].dot11Rate & IEEE80211_RATE_VAL; if (rt->info[i].phy != IEEE80211_T_HT) sc->sc_rixmap[ieeerate] = i; else sc->sc_rixmap[ieeerate | IEEE80211_RATE_MCS] = i; } memset(sc->sc_hwmap, 0, sizeof(sc->sc_hwmap)); for (i = 0; i < nitems(sc->sc_hwmap); i++) { if (i >= rt->rateCount) { sc->sc_hwmap[i].ledon = (500 * hz) / 1000; sc->sc_hwmap[i].ledoff = (130 * hz) / 1000; continue; } sc->sc_hwmap[i].ieeerate = rt->info[i].dot11Rate & IEEE80211_RATE_VAL; if (rt->info[i].phy == IEEE80211_T_HT) sc->sc_hwmap[i].ieeerate |= IEEE80211_RATE_MCS; sc->sc_hwmap[i].txflags = IEEE80211_RADIOTAP_F_DATAPAD; if (rt->info[i].shortPreamble || rt->info[i].phy == IEEE80211_T_OFDM) sc->sc_hwmap[i].txflags |= IEEE80211_RADIOTAP_F_SHORTPRE; sc->sc_hwmap[i].rxflags = sc->sc_hwmap[i].txflags; for (j = 0; j < nitems(blinkrates)-1; j++) if (blinkrates[j].rate == sc->sc_hwmap[i].ieeerate) break; /* NB: this uses the last entry if the rate isn't found */ /* XXX beware of overlow */ sc->sc_hwmap[i].ledon = (blinkrates[j].timeOn * hz) / 1000; sc->sc_hwmap[i].ledoff = (blinkrates[j].timeOff * hz) / 1000; } sc->sc_currates = rt; sc->sc_curmode = mode; /* - * All protection frames are transmited at 2Mb/s for + * All protection frames are transmitted at 2Mb/s for * 11g, otherwise at 1Mb/s. */ if (mode == IEEE80211_MODE_11G) sc->sc_protrix = ath_tx_findrix(sc, 2*2); else sc->sc_protrix = ath_tx_findrix(sc, 2*1); /* NB: caller is responsible for resetting rate control state */ } static void ath_watchdog(void *arg) { struct ath_softc *sc = arg; struct ieee80211com *ic = &sc->sc_ic; int do_reset = 0; ATH_LOCK_ASSERT(sc); if (sc->sc_wd_timer != 0 && --sc->sc_wd_timer == 0) { uint32_t hangs; ath_power_set_power_state(sc, HAL_PM_AWAKE); if (ath_hal_gethangstate(sc->sc_ah, 0xffff, &hangs) && hangs != 0) { device_printf(sc->sc_dev, "%s hang detected (0x%x)\n", hangs & 0xff ? "bb" : "mac", hangs); } else device_printf(sc->sc_dev, "device timeout\n"); do_reset = 1; counter_u64_add(ic->ic_oerrors, 1); sc->sc_stats.ast_watchdog++; ath_power_restore_power_state(sc); } /* * We can't hold the lock across the ath_reset() call. * * And since this routine can't hold a lock and sleep, * do the reset deferred. */ if (do_reset) { taskqueue_enqueue(sc->sc_tq, &sc->sc_resettask); } callout_schedule(&sc->sc_wd_ch, hz); } static void ath_parent(struct ieee80211com *ic) { struct ath_softc *sc = ic->ic_softc; int error = EDOOFUS; ATH_LOCK(sc); if (ic->ic_nrunning > 0) { /* * To avoid rescanning another access point, * do not call ath_init() here. Instead, * only reflect promisc mode settings. */ if (sc->sc_running) { ath_power_set_power_state(sc, HAL_PM_AWAKE); ath_mode_init(sc); ath_power_restore_power_state(sc); } else if (!sc->sc_invalid) { /* * Beware of being called during attach/detach * to reset promiscuous mode. In that case we * will still be marked UP but not RUNNING. * However trying to re-init the interface * is the wrong thing to do as we've already * torn down much of our state. There's * probably a better way to deal with this. */ error = ath_init(sc); } } else { ath_stop(sc); if (!sc->sc_invalid) ath_power_setpower(sc, HAL_PM_FULL_SLEEP); } ATH_UNLOCK(sc); if (error == 0) { #ifdef ATH_TX99_DIAG if (sc->sc_tx99 != NULL) sc->sc_tx99->start(sc->sc_tx99); else #endif ieee80211_start_all(ic); } } /* * Announce various information on device/driver attach. */ static void ath_announce(struct ath_softc *sc) { struct ath_hal *ah = sc->sc_ah; device_printf(sc->sc_dev, "%s mac %d.%d RF%s phy %d.%d\n", ath_hal_mac_name(ah), ah->ah_macVersion, ah->ah_macRev, ath_hal_rf_name(ah), ah->ah_phyRev >> 4, ah->ah_phyRev & 0xf); device_printf(sc->sc_dev, "2GHz radio: 0x%.4x; 5GHz radio: 0x%.4x\n", ah->ah_analog2GhzRev, ah->ah_analog5GhzRev); if (bootverbose) { int i; for (i = 0; i <= WME_AC_VO; i++) { struct ath_txq *txq = sc->sc_ac2q[i]; device_printf(sc->sc_dev, "Use hw queue %u for %s traffic\n", txq->axq_qnum, ieee80211_wme_acnames[i]); } device_printf(sc->sc_dev, "Use hw queue %u for CAB traffic\n", sc->sc_cabq->axq_qnum); device_printf(sc->sc_dev, "Use hw queue %u for beacons\n", sc->sc_bhalq); } if (ath_rxbuf != ATH_RXBUF) device_printf(sc->sc_dev, "using %u rx buffers\n", ath_rxbuf); if (ath_txbuf != ATH_TXBUF) device_printf(sc->sc_dev, "using %u tx buffers\n", ath_txbuf); if (sc->sc_mcastkey && bootverbose) device_printf(sc->sc_dev, "using multicast key search\n"); } static void ath_dfs_tasklet(void *p, int npending) { struct ath_softc *sc = (struct ath_softc *) p; struct ieee80211com *ic = &sc->sc_ic; /* * If previous processing has found a radar event, * signal this to the net80211 layer to begin DFS * processing. */ if (ath_dfs_process_radar_event(sc, sc->sc_curchan)) { /* DFS event found, initiate channel change */ /* * XXX doesn't currently tell us whether the event * XXX was found in the primary or extension * XXX channel! */ IEEE80211_LOCK(ic); ieee80211_dfs_notify_radar(ic, sc->sc_curchan); IEEE80211_UNLOCK(ic); } } /* * Enable/disable power save. This must be called with * no TX driver locks currently held, so it should only * be called from the RX path (which doesn't hold any * TX driver locks.) */ static void ath_node_powersave(struct ieee80211_node *ni, int enable) { #ifdef ATH_SW_PSQ struct ath_node *an = ATH_NODE(ni); struct ieee80211com *ic = ni->ni_ic; struct ath_softc *sc = ic->ic_softc; struct ath_vap *avp = ATH_VAP(ni->ni_vap); /* XXX and no TXQ locks should be held here */ DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, "%s: %6D: enable=%d\n", __func__, ni->ni_macaddr, ":", !! enable); /* Suspend or resume software queue handling */ if (enable) ath_tx_node_sleep(sc, an); else ath_tx_node_wakeup(sc, an); /* Update net80211 state */ avp->av_node_ps(ni, enable); #else struct ath_vap *avp = ATH_VAP(ni->ni_vap); /* Update net80211 state */ avp->av_node_ps(ni, enable); #endif/* ATH_SW_PSQ */ } /* * Notification from net80211 that the powersave queue state has * changed. * * Since the software queue also may have some frames: * * + if the node software queue has frames and the TID state * is 0, we set the TIM; * + if the node and the stack are both empty, we clear the TIM bit. * + If the stack tries to set the bit, always set it. * + If the stack tries to clear the bit, only clear it if the * software queue in question is also cleared. * * TODO: this is called during node teardown; so let's ensure this * is all correctly handled and that the TIM bit is cleared. * It may be that the node flush is called _AFTER_ the net80211 * stack clears the TIM. * * Here is the racy part. Since it's possible >1 concurrent, * overlapping TXes will appear complete with a TX completion in * another thread, it's possible that the concurrent TIM calls will * clash. We can't hold the node lock here because setting the * TIM grabs the net80211 comlock and this may cause a LOR. * The solution is either to totally serialise _everything_ at * this point (ie, all TX, completion and any reset/flush go into * one taskqueue) or a new "ath TIM lock" needs to be created that * just wraps the driver state change and this call to avp->av_set_tim(). * * The same race exists in the net80211 power save queue handling * as well. Since multiple transmitting threads may queue frames * into the driver, as well as ps-poll and the driver transmitting * frames (and thus clearing the psq), it's quite possible that * a packet entering the PSQ and a ps-poll being handled will * race, causing the TIM to be cleared and not re-set. */ static int ath_node_set_tim(struct ieee80211_node *ni, int enable) { #ifdef ATH_SW_PSQ struct ieee80211com *ic = ni->ni_ic; struct ath_softc *sc = ic->ic_softc; struct ath_node *an = ATH_NODE(ni); struct ath_vap *avp = ATH_VAP(ni->ni_vap); int changed = 0; ATH_TX_LOCK(sc); an->an_stack_psq = enable; /* * This will get called for all operating modes, * even if avp->av_set_tim is unset. * It's currently set for hostap/ibss modes; but * the same infrastructure is used for both STA * and AP/IBSS node power save. */ if (avp->av_set_tim == NULL) { ATH_TX_UNLOCK(sc); return (0); } /* * If setting the bit, always set it here. * If clearing the bit, only clear it if the * software queue is also empty. * * If the node has left power save, just clear the TIM * bit regardless of the state of the power save queue. * * XXX TODO: although atomics are used, it's quite possible * that a race will occur between this and setting/clearing * in another thread. TX completion will occur always in * one thread, however setting/clearing the TIM bit can come * from a variety of different process contexts! */ if (enable && an->an_tim_set == 1) { DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, "%s: %6D: enable=%d, tim_set=1, ignoring\n", __func__, ni->ni_macaddr, ":", enable); ATH_TX_UNLOCK(sc); } else if (enable) { DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, "%s: %6D: enable=%d, enabling TIM\n", __func__, ni->ni_macaddr, ":", enable); an->an_tim_set = 1; ATH_TX_UNLOCK(sc); changed = avp->av_set_tim(ni, enable); } else if (an->an_swq_depth == 0) { /* disable */ DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, "%s: %6D: enable=%d, an_swq_depth == 0, disabling\n", __func__, ni->ni_macaddr, ":", enable); an->an_tim_set = 0; ATH_TX_UNLOCK(sc); changed = avp->av_set_tim(ni, enable); } else if (! an->an_is_powersave) { /* * disable regardless; the node isn't in powersave now */ DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, "%s: %6D: enable=%d, an_pwrsave=0, disabling\n", __func__, ni->ni_macaddr, ":", enable); an->an_tim_set = 0; ATH_TX_UNLOCK(sc); changed = avp->av_set_tim(ni, enable); } else { /* * psq disable, node is currently in powersave, node * software queue isn't empty, so don't clear the TIM bit * for now. */ ATH_TX_UNLOCK(sc); DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, "%s: %6D: enable=%d, an_swq_depth > 0, ignoring\n", __func__, ni->ni_macaddr, ":", enable); changed = 0; } return (changed); #else struct ath_vap *avp = ATH_VAP(ni->ni_vap); /* * Some operating modes don't set av_set_tim(), so don't * update it here. */ if (avp->av_set_tim == NULL) return (0); return (avp->av_set_tim(ni, enable)); #endif /* ATH_SW_PSQ */ } /* * Set or update the TIM from the software queue. * * Check the software queue depth before attempting to do lock * anything; that avoids trying to obtain the lock. Then, * re-check afterwards to ensure nothing has changed in the * meantime. * * set: This is designed to be called from the TX path, after * a frame has been queued; to see if the swq > 0. * * clear: This is designed to be called from the buffer completion point * (right now it's ath_tx_default_comp()) where the state of * a software queue has changed. * * It makes sense to place it at buffer free / completion rather * than after each software queue operation, as there's no real * point in churning the TIM bit as the last frames in the software * queue are transmitted. If they fail and we retry them, we'd * just be setting the TIM bit again anyway. */ void ath_tx_update_tim(struct ath_softc *sc, struct ieee80211_node *ni, int enable) { #ifdef ATH_SW_PSQ struct ath_node *an; struct ath_vap *avp; /* Don't do this for broadcast/etc frames */ if (ni == NULL) return; an = ATH_NODE(ni); avp = ATH_VAP(ni->ni_vap); /* * And for operating modes without the TIM handler set, let's * just skip those. */ if (avp->av_set_tim == NULL) return; ATH_TX_LOCK_ASSERT(sc); if (enable) { if (an->an_is_powersave && an->an_tim_set == 0 && an->an_swq_depth != 0) { DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, "%s: %6D: swq_depth>0, tim_set=0, set!\n", __func__, ni->ni_macaddr, ":"); an->an_tim_set = 1; (void) avp->av_set_tim(ni, 1); } } else { /* * Don't bother grabbing the lock unless the queue is empty. */ if (an->an_swq_depth != 0) return; if (an->an_is_powersave && an->an_stack_psq == 0 && an->an_tim_set == 1 && an->an_swq_depth == 0) { DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, "%s: %6D: swq_depth=0, tim_set=1, psq_set=0," " clear!\n", __func__, ni->ni_macaddr, ":"); an->an_tim_set = 0; (void) avp->av_set_tim(ni, 0); } } #else return; #endif /* ATH_SW_PSQ */ } /* * Received a ps-poll frame from net80211. * * Here we get a chance to serve out a software-queued frame ourselves * before we punt it to net80211 to transmit us one itself - either * because there's traffic in the net80211 psq, or a NULL frame to * indicate there's nothing else. */ static void ath_node_recv_pspoll(struct ieee80211_node *ni, struct mbuf *m) { #ifdef ATH_SW_PSQ struct ath_node *an; struct ath_vap *avp; struct ieee80211com *ic = ni->ni_ic; struct ath_softc *sc = ic->ic_softc; int tid; /* Just paranoia */ if (ni == NULL) return; /* * Unassociated (temporary node) station. */ if (ni->ni_associd == 0) return; /* * We do have an active node, so let's begin looking into it. */ an = ATH_NODE(ni); avp = ATH_VAP(ni->ni_vap); /* * For now, we just call the original ps-poll method. * Once we're ready to flip this on: * * + Set leak to 1, as no matter what we're going to have * to send a frame; * + Check the software queue and if there's something in it, * schedule the highest TID thas has traffic from this node. * Then make sure we schedule the software scheduler to * run so it picks up said frame. * * That way whatever happens, we'll at least send _a_ frame * to the given node. * * Again, yes, it's crappy QoS if the node has multiple * TIDs worth of traffic - but let's get it working first * before we optimise it. * * Also yes, there's definitely latency here - we're not * direct dispatching to the hardware in this path (and * we're likely being called from the packet receive path, * so going back into TX may be a little hairy!) but again * I'd like to get this working first before optimising * turn-around time. */ ATH_TX_LOCK(sc); /* * Legacy - we're called and the node isn't asleep. * Immediately punt. */ if (! an->an_is_powersave) { DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, "%s: %6D: not in powersave?\n", __func__, ni->ni_macaddr, ":"); ATH_TX_UNLOCK(sc); avp->av_recv_pspoll(ni, m); return; } /* * We're in powersave. * * Leak a frame. */ an->an_leak_count = 1; /* * Now, if there's no frames in the node, just punt to * recv_pspoll. * * Don't bother checking if the TIM bit is set, we really * only care if there are any frames here! */ if (an->an_swq_depth == 0) { ATH_TX_UNLOCK(sc); DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, "%s: %6D: SWQ empty; punting to net80211\n", __func__, ni->ni_macaddr, ":"); avp->av_recv_pspoll(ni, m); return; } /* * Ok, let's schedule the highest TID that has traffic * and then schedule something. */ for (tid = IEEE80211_TID_SIZE - 1; tid >= 0; tid--) { struct ath_tid *atid = &an->an_tid[tid]; /* * No frames? Skip. */ if (atid->axq_depth == 0) continue; ath_tx_tid_sched(sc, atid); /* * XXX we could do a direct call to the TXQ * scheduler code here to optimise latency * at the expense of a REALLY deep callstack. */ ATH_TX_UNLOCK(sc); taskqueue_enqueue(sc->sc_tq, &sc->sc_txqtask); DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, "%s: %6D: leaking frame to TID %d\n", __func__, ni->ni_macaddr, ":", tid); return; } ATH_TX_UNLOCK(sc); /* * XXX nothing in the TIDs at this point? Eek. */ DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, "%s: %6D: TIDs empty, but ath_node showed traffic?!\n", __func__, ni->ni_macaddr, ":"); avp->av_recv_pspoll(ni, m); #else avp->av_recv_pspoll(ni, m); #endif /* ATH_SW_PSQ */ } MODULE_VERSION(if_ath, 1); MODULE_DEPEND(if_ath, wlan, 1, 1, 1); /* 802.11 media layer */ #if defined(IEEE80211_ALQ) || defined(AH_DEBUG_ALQ) || defined(ATH_DEBUG_ALQ) MODULE_DEPEND(if_ath, alq, 1, 1, 1); #endif Index: head/sys/dev/ath/if_ath_ioctl.c =================================================================== --- head/sys/dev/ath/if_ath_ioctl.c (revision 298938) +++ head/sys/dev/ath/if_ath_ioctl.c (revision 298939) @@ -1,307 +1,307 @@ /*- * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include __FBSDID("$FreeBSD$"); /* * Driver for the Atheros Wireless LAN controller. * * This software is derived from work of Atsushi Onoe; his contribution * is greatly appreciated. */ #include "opt_inet.h" #include "opt_ath.h" /* * This is needed for register operations which are performed * by the driver - eg, calls to ath_hal_gettsf32(). * * It's also required for any AH_DEBUG checks in here, eg the * module dependencies. */ #include "opt_ah.h" #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for mp_ncpus */ #include #include #include #include #include #include #include #include #include #include #include #ifdef IEEE80211_SUPPORT_SUPERG #include #endif #ifdef IEEE80211_SUPPORT_TDMA #include #endif #include #ifdef INET #include #include #endif #include #include /* XXX for softled */ #include #include #include #include #include #include #include #ifdef IEEE80211_SUPPORT_TDMA #include #endif #include /* * ioctl() related pieces. * * Some subsystems (eg spectral, dfs) have their own ioctl method which * we call. */ /* * Fetch the rate control statistics for the given node. */ static int ath_ioctl_ratestats(struct ath_softc *sc, struct ath_rateioctl *rs) { struct ath_node *an; struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_node *ni; int error = 0; /* Perform a lookup on the given node */ ni = ieee80211_find_node(&ic->ic_sta, rs->is_u.macaddr); if (ni == NULL) { error = EINVAL; goto bad; } /* Lock the ath_node */ an = ATH_NODE(ni); ATH_NODE_LOCK(an); /* Fetch the rate control stats for this node */ error = ath_rate_fetch_node_stats(sc, an, rs); /* No matter what happens here, just drop through */ /* Unlock the ath_node */ ATH_NODE_UNLOCK(an); /* Unref the node */ ieee80211_node_decref(ni); bad: return (error); } #ifdef ATH_DIAGAPI /* * Diagnostic interface to the HAL. This is used by various * tools to do things like retrieve register contents for * debugging. The mechanism is intentionally opaque so that - * it can change frequently w/o concern for compatiblity. + * it can change frequently w/o concern for compatibility. */ static int ath_ioctl_diag(struct ath_softc *sc, struct ath_diag *ad) { struct ath_hal *ah = sc->sc_ah; u_int id = ad->ad_id & ATH_DIAG_ID; void *indata = NULL; void *outdata = NULL; u_int32_t insize = ad->ad_in_size; u_int32_t outsize = ad->ad_out_size; int error = 0; if (ad->ad_id & ATH_DIAG_IN) { /* * Copy in data. */ indata = malloc(insize, M_TEMP, M_NOWAIT); if (indata == NULL) { error = ENOMEM; goto bad; } error = copyin(ad->ad_in_data, indata, insize); if (error) goto bad; } if (ad->ad_id & ATH_DIAG_DYN) { /* * Allocate a buffer for the results (otherwise the HAL * returns a pointer to a buffer where we can read the * results). Note that we depend on the HAL leaving this * pointer for us to use below in reclaiming the buffer; * may want to be more defensive. */ outdata = malloc(outsize, M_TEMP, M_NOWAIT); if (outdata == NULL) { error = ENOMEM; goto bad; } } ATH_LOCK(sc); if (id != HAL_DIAG_REGS) ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); if (ath_hal_getdiagstate(ah, id, indata, insize, &outdata, &outsize)) { if (outsize < ad->ad_out_size) ad->ad_out_size = outsize; if (outdata != NULL) error = copyout(outdata, ad->ad_out_data, ad->ad_out_size); } else { error = EINVAL; } ATH_LOCK(sc); if (id != HAL_DIAG_REGS) ath_power_restore_power_state(sc); ATH_UNLOCK(sc); bad: if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL) free(indata, M_TEMP); if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL) free(outdata, M_TEMP); return error; } #endif /* ATH_DIAGAPI */ int ath_ioctl(struct ieee80211com *ic, u_long cmd, void *data) { struct ifreq *ifr = data; struct ath_softc *sc = ic->ic_softc; switch (cmd) { case SIOCGATHSTATS: { struct ieee80211vap *vap; struct ifnet *ifp; const HAL_RATE_TABLE *rt; /* NB: embed these numbers to get a consistent view */ sc->sc_stats.ast_tx_packets = 0; sc->sc_stats.ast_rx_packets = 0; TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { ifp = vap->iv_ifp; sc->sc_stats.ast_tx_packets += ifp->if_get_counter(ifp, IFCOUNTER_OPACKETS); sc->sc_stats.ast_rx_packets += ifp->if_get_counter(ifp, IFCOUNTER_IPACKETS); } sc->sc_stats.ast_tx_rssi = ATH_RSSI(sc->sc_halstats.ns_avgtxrssi); sc->sc_stats.ast_rx_rssi = ATH_RSSI(sc->sc_halstats.ns_avgrssi); #ifdef IEEE80211_SUPPORT_TDMA sc->sc_stats.ast_tdma_tsfadjp = TDMA_AVG(sc->sc_avgtsfdeltap); sc->sc_stats.ast_tdma_tsfadjm = TDMA_AVG(sc->sc_avgtsfdeltam); #endif rt = sc->sc_currates; sc->sc_stats.ast_tx_rate = rt->info[sc->sc_txrix].dot11Rate &~ IEEE80211_RATE_BASIC; if (rt->info[sc->sc_txrix].phy & IEEE80211_T_HT) sc->sc_stats.ast_tx_rate |= IEEE80211_RATE_MCS; return copyout(&sc->sc_stats, ifr->ifr_data, sizeof (sc->sc_stats)); } case SIOCGATHAGSTATS: return copyout(&sc->sc_aggr_stats, ifr->ifr_data, sizeof (sc->sc_aggr_stats)); case SIOCZATHSTATS: { int error; error = priv_check(curthread, PRIV_DRIVER); if (error == 0) { memset(&sc->sc_stats, 0, sizeof(sc->sc_stats)); memset(&sc->sc_aggr_stats, 0, sizeof(sc->sc_aggr_stats)); memset(&sc->sc_intr_stats, 0, sizeof(sc->sc_intr_stats)); } return (error); } #ifdef ATH_DIAGAPI case SIOCGATHDIAG: return (ath_ioctl_diag(sc, data)); case SIOCGATHPHYERR: return (ath_ioctl_phyerr(sc, data)); #endif case SIOCGATHSPECTRAL: return (ath_ioctl_spectral(sc, data)); case SIOCGATHNODERATESTATS: return (ath_ioctl_ratestats(sc, data)); default: /* * This signals the net80211 layer that we didn't handle this * ioctl. */ return (ENOTTY); } } Index: head/sys/dev/ath/if_ath_lna_div.c =================================================================== --- head/sys/dev/ath/if_ath_lna_div.c (revision 298938) +++ head/sys/dev/ath/if_ath_lna_div.c (revision 298939) @@ -1,1018 +1,1018 @@ /*- * Copyright (c) 2013 Adrian Chadd * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * * $FreeBSD$ */ #include __FBSDID("$FreeBSD$"); /* * This module handles LNA diversity for those chips which implement LNA * mixing (AR9285/AR9485.) */ #include "opt_ath.h" #include "opt_inet.h" #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* XXX for ether_sprintf */ #include #include #ifdef INET #include #include #endif #include #include #include -/* Linux compability macros */ +/* Linux compatibility macros */ /* * XXX these don't handle rounding, underflow, overflow, wrapping! */ #define msecs_to_jiffies(a) ( (a) * hz / 1000 ) /* * Methods which are required */ /* * Attach the LNA diversity to the given interface */ int ath_lna_div_attach(struct ath_softc *sc) { struct if_ath_ant_comb_state *ss; HAL_ANT_COMB_CONFIG div_ant_conf; /* Only do this if diversity is enabled */ if (! ath_hal_hasdivantcomb(sc->sc_ah)) return (0); ss = malloc(sizeof(struct if_ath_ant_comb_state), M_TEMP, M_WAITOK | M_ZERO); if (ss == NULL) { device_printf(sc->sc_dev, "%s: failed to allocate\n", __func__); /* Don't fail at this point */ return (0); } /* Fetch the hardware configuration */ OS_MEMZERO(&div_ant_conf, sizeof(div_ant_conf)); ath_hal_div_comb_conf_get(sc->sc_ah, &div_ant_conf); /* Figure out what the hardware specific bits should be */ if ((div_ant_conf.antdiv_configgroup == HAL_ANTDIV_CONFIG_GROUP_1) || (div_ant_conf.antdiv_configgroup == HAL_ANTDIV_CONFIG_GROUP_2)) { ss->lna1_lna2_delta = -9; } else { ss->lna1_lna2_delta = -3; } /* Let's flip this on */ sc->sc_lna_div = ss; sc->sc_dolnadiv = 1; return (0); } /* * Detach the LNA diversity state from the given interface */ int ath_lna_div_detach(struct ath_softc *sc) { if (sc->sc_lna_div != NULL) { free(sc->sc_lna_div, M_TEMP); sc->sc_lna_div = NULL; } sc->sc_dolnadiv = 0; return (0); } /* * Enable LNA diversity on the current channel if it's required. */ int ath_lna_div_enable(struct ath_softc *sc, const struct ieee80211_channel *chan) { return (0); } /* * Handle ioctl requests from the diagnostic interface. * * The initial part of this code resembles ath_ioctl_diag(); * it's likely a good idea to reduce duplication between * these two routines. */ int ath_lna_div_ioctl(struct ath_softc *sc, struct ath_diag *ad) { unsigned int id = ad->ad_id & ATH_DIAG_ID; void *indata = NULL; void *outdata = NULL; u_int32_t insize = ad->ad_in_size; u_int32_t outsize = ad->ad_out_size; int error = 0; // int val; if (ad->ad_id & ATH_DIAG_IN) { /* * Copy in data. */ indata = malloc(insize, M_TEMP, M_NOWAIT); if (indata == NULL) { error = ENOMEM; goto bad; } error = copyin(ad->ad_in_data, indata, insize); if (error) goto bad; } if (ad->ad_id & ATH_DIAG_DYN) { /* * Allocate a buffer for the results (otherwise the HAL * returns a pointer to a buffer where we can read the * results). Note that we depend on the HAL leaving this * pointer for us to use below in reclaiming the buffer; * may want to be more defensive. */ outdata = malloc(outsize, M_TEMP, M_NOWAIT); if (outdata == NULL) { error = ENOMEM; goto bad; } } switch (id) { default: error = EINVAL; } if (outsize < ad->ad_out_size) ad->ad_out_size = outsize; if (outdata && copyout(outdata, ad->ad_out_data, ad->ad_out_size)) error = EFAULT; bad: if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL) free(indata, M_TEMP); if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL) free(outdata, M_TEMP); return (error); } /* * XXX need to low_rssi_thresh config from ath9k, to support CUS198 * antenna diversity correctly. */ static HAL_BOOL ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta, int mindelta, int main_rssi_avg, int alt_rssi_avg, int pkt_count) { return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) && (alt_rssi_avg > main_rssi_avg + maxdelta)) || (alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50); } static void ath_lnaconf_alt_good_scan(struct if_ath_ant_comb_state *antcomb, HAL_ANT_COMB_CONFIG *ant_conf, int main_rssi_avg) { antcomb->quick_scan_cnt = 0; if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA2) antcomb->rssi_lna2 = main_rssi_avg; else if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA1) antcomb->rssi_lna1 = main_rssi_avg; switch ((ant_conf->main_lna_conf << 4) | ant_conf->alt_lna_conf) { case (0x10): /* LNA2 A-B */ antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2; antcomb->first_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2; antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1; break; case (0x20): /* LNA1 A-B */ antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2; antcomb->first_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2; antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2; break; case (0x21): /* LNA1 LNA2 */ antcomb->main_conf = HAL_ANT_DIV_COMB_LNA2; antcomb->first_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2; antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2; break; case (0x12): /* LNA2 LNA1 */ antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1; antcomb->first_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2; antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2; break; case (0x13): /* LNA2 A+B */ antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2; antcomb->first_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2; antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1; break; case (0x23): /* LNA1 A+B */ antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2; antcomb->first_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2; antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2; break; default: break; } } static void ath_select_ant_div_from_quick_scan(struct if_ath_ant_comb_state *antcomb, HAL_ANT_COMB_CONFIG *div_ant_conf, int main_rssi_avg, int alt_rssi_avg, int alt_ratio) { /* alt_good */ switch (antcomb->quick_scan_cnt) { case 0: /* set alt to main, and alt to first conf */ div_ant_conf->main_lna_conf = antcomb->main_conf; div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf; break; case 1: /* set alt to main, and alt to first conf */ div_ant_conf->main_lna_conf = antcomb->main_conf; div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf; antcomb->rssi_first = main_rssi_avg; antcomb->rssi_second = alt_rssi_avg; if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) { /* main is LNA1 */ if (ath_is_alt_ant_ratio_better(alt_ratio, ATH_ANT_DIV_COMB_LNA1_DELTA_HI, ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, main_rssi_avg, alt_rssi_avg, antcomb->total_pkt_count)) antcomb->first_ratio = AH_TRUE; else antcomb->first_ratio = AH_FALSE; } else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) { if (ath_is_alt_ant_ratio_better(alt_ratio, ATH_ANT_DIV_COMB_LNA1_DELTA_MID, ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, main_rssi_avg, alt_rssi_avg, antcomb->total_pkt_count)) antcomb->first_ratio = AH_TRUE; else antcomb->first_ratio = AH_FALSE; } else { if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) && (alt_rssi_avg > main_rssi_avg + ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) || (alt_rssi_avg > main_rssi_avg)) && (antcomb->total_pkt_count > 50)) antcomb->first_ratio = AH_TRUE; else antcomb->first_ratio = AH_FALSE; } break; case 2: antcomb->alt_good = AH_FALSE; antcomb->scan_not_start = AH_FALSE; antcomb->scan = AH_FALSE; antcomb->rssi_first = main_rssi_avg; antcomb->rssi_third = alt_rssi_avg; if (antcomb->second_quick_scan_conf == HAL_ANT_DIV_COMB_LNA1) antcomb->rssi_lna1 = alt_rssi_avg; else if (antcomb->second_quick_scan_conf == HAL_ANT_DIV_COMB_LNA2) antcomb->rssi_lna2 = alt_rssi_avg; else if (antcomb->second_quick_scan_conf == HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2) { if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) antcomb->rssi_lna2 = main_rssi_avg; else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) antcomb->rssi_lna1 = main_rssi_avg; } if (antcomb->rssi_lna2 > antcomb->rssi_lna1 + ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA) div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA2; else div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA1; if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) { if (ath_is_alt_ant_ratio_better(alt_ratio, ATH_ANT_DIV_COMB_LNA1_DELTA_HI, ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, main_rssi_avg, alt_rssi_avg, antcomb->total_pkt_count)) antcomb->second_ratio = AH_TRUE; else antcomb->second_ratio = AH_FALSE; } else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) { if (ath_is_alt_ant_ratio_better(alt_ratio, ATH_ANT_DIV_COMB_LNA1_DELTA_MID, ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, main_rssi_avg, alt_rssi_avg, antcomb->total_pkt_count)) antcomb->second_ratio = AH_TRUE; else antcomb->second_ratio = AH_FALSE; } else { if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) && (alt_rssi_avg > main_rssi_avg + ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) || (alt_rssi_avg > main_rssi_avg)) && (antcomb->total_pkt_count > 50)) antcomb->second_ratio = AH_TRUE; else antcomb->second_ratio = AH_FALSE; } /* set alt to the conf with maximun ratio */ if (antcomb->first_ratio && antcomb->second_ratio) { if (antcomb->rssi_second > antcomb->rssi_third) { /* first alt*/ if ((antcomb->first_quick_scan_conf == HAL_ANT_DIV_COMB_LNA1) || (antcomb->first_quick_scan_conf == HAL_ANT_DIV_COMB_LNA2)) /* Set alt LNA1 or LNA2*/ if (div_ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA2) div_ant_conf->alt_lna_conf = HAL_ANT_DIV_COMB_LNA1; else div_ant_conf->alt_lna_conf = HAL_ANT_DIV_COMB_LNA2; else /* Set alt to A+B or A-B */ div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf; } else if ((antcomb->second_quick_scan_conf == HAL_ANT_DIV_COMB_LNA1) || (antcomb->second_quick_scan_conf == HAL_ANT_DIV_COMB_LNA2)) { /* Set alt LNA1 or LNA2 */ if (div_ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA2) div_ant_conf->alt_lna_conf = HAL_ANT_DIV_COMB_LNA1; else div_ant_conf->alt_lna_conf = HAL_ANT_DIV_COMB_LNA2; } else { /* Set alt to A+B or A-B */ div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf; } } else if (antcomb->first_ratio) { /* first alt */ if ((antcomb->first_quick_scan_conf == HAL_ANT_DIV_COMB_LNA1) || (antcomb->first_quick_scan_conf == HAL_ANT_DIV_COMB_LNA2)) /* Set alt LNA1 or LNA2 */ if (div_ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA2) div_ant_conf->alt_lna_conf = HAL_ANT_DIV_COMB_LNA1; else div_ant_conf->alt_lna_conf = HAL_ANT_DIV_COMB_LNA2; else /* Set alt to A+B or A-B */ div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf; } else if (antcomb->second_ratio) { /* second alt */ if ((antcomb->second_quick_scan_conf == HAL_ANT_DIV_COMB_LNA1) || (antcomb->second_quick_scan_conf == HAL_ANT_DIV_COMB_LNA2)) /* Set alt LNA1 or LNA2 */ if (div_ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA2) div_ant_conf->alt_lna_conf = HAL_ANT_DIV_COMB_LNA1; else div_ant_conf->alt_lna_conf = HAL_ANT_DIV_COMB_LNA2; else /* Set alt to A+B or A-B */ div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf; } else { /* main is largest */ if ((antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) || (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2)) /* Set alt LNA1 or LNA2 */ if (div_ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA2) div_ant_conf->alt_lna_conf = HAL_ANT_DIV_COMB_LNA1; else div_ant_conf->alt_lna_conf = HAL_ANT_DIV_COMB_LNA2; else /* Set alt to A+B or A-B */ div_ant_conf->alt_lna_conf = antcomb->main_conf; } break; default: break; } } static void ath_ant_adjust_fast_divbias(struct if_ath_ant_comb_state *antcomb, int alt_ratio, int alt_ant_ratio_th, u_int config_group, HAL_ANT_COMB_CONFIG *pdiv_ant_conf) { if (config_group == HAL_ANTDIV_CONFIG_GROUP_1) { switch ((pdiv_ant_conf->main_lna_conf << 4) | pdiv_ant_conf->alt_lna_conf) { case (0x01): //A-B LNA2 pdiv_ant_conf->fast_div_bias = 0x1; pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x02): //A-B LNA1 pdiv_ant_conf->fast_div_bias = 0x1; pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x03): //A-B A+B pdiv_ant_conf->fast_div_bias = 0x1; pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x10): //LNA2 A-B if ((antcomb->scan == 0) && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) { pdiv_ant_conf->fast_div_bias = 0x3f; } else { pdiv_ant_conf->fast_div_bias = 0x1; } pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x12): //LNA2 LNA1 pdiv_ant_conf->fast_div_bias = 0x1; pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x13): //LNA2 A+B if ((antcomb->scan == 0) && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) { pdiv_ant_conf->fast_div_bias = 0x3f; } else { pdiv_ant_conf->fast_div_bias = 0x1; } pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x20): //LNA1 A-B if ((antcomb->scan == 0) && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) { pdiv_ant_conf->fast_div_bias = 0x3f; } else { pdiv_ant_conf->fast_div_bias = 0x1; } pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x21): //LNA1 LNA2 pdiv_ant_conf->fast_div_bias = 0x1; pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x23): //LNA1 A+B if ((antcomb->scan == 0) && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) { pdiv_ant_conf->fast_div_bias = 0x3f; } else { pdiv_ant_conf->fast_div_bias = 0x1; } pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x30): //A+B A-B pdiv_ant_conf->fast_div_bias = 0x1; pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x31): //A+B LNA2 pdiv_ant_conf->fast_div_bias = 0x1; pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x32): //A+B LNA1 pdiv_ant_conf->fast_div_bias = 0x1; pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; default: break; } } else if (config_group == HAL_ANTDIV_CONFIG_GROUP_2) { switch ((pdiv_ant_conf->main_lna_conf << 4) | pdiv_ant_conf->alt_lna_conf) { case (0x01): //A-B LNA2 pdiv_ant_conf->fast_div_bias = 0x1; pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x02): //A-B LNA1 pdiv_ant_conf->fast_div_bias = 0x1; pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x03): //A-B A+B pdiv_ant_conf->fast_div_bias = 0x1; pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x10): //LNA2 A-B if ((antcomb->scan == 0) && (alt_ratio > alt_ant_ratio_th)) { pdiv_ant_conf->fast_div_bias = 0x1; } else { pdiv_ant_conf->fast_div_bias = 0x2; } pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x12): //LNA2 LNA1 pdiv_ant_conf->fast_div_bias = 0x1; pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x13): //LNA2 A+B if ((antcomb->scan == 0) && (alt_ratio > alt_ant_ratio_th)) { pdiv_ant_conf->fast_div_bias = 0x1; } else { pdiv_ant_conf->fast_div_bias = 0x2; } pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x20): //LNA1 A-B if ((antcomb->scan == 0) && (alt_ratio > alt_ant_ratio_th)) { pdiv_ant_conf->fast_div_bias = 0x1; } else { pdiv_ant_conf->fast_div_bias = 0x2; } pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x21): //LNA1 LNA2 pdiv_ant_conf->fast_div_bias = 0x1; pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x23): //LNA1 A+B if ((antcomb->scan == 0) && (alt_ratio > alt_ant_ratio_th)) { pdiv_ant_conf->fast_div_bias = 0x1; } else { pdiv_ant_conf->fast_div_bias = 0x2; } pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x30): //A+B A-B pdiv_ant_conf->fast_div_bias = 0x1; pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x31): //A+B LNA2 pdiv_ant_conf->fast_div_bias = 0x1; pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x32): //A+B LNA1 pdiv_ant_conf->fast_div_bias = 0x1; pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; default: break; } } else { /* DEFAULT_ANTDIV_CONFIG_GROUP */ switch ((pdiv_ant_conf->main_lna_conf << 4) | pdiv_ant_conf->alt_lna_conf) { case (0x01): //A-B LNA2 pdiv_ant_conf->fast_div_bias = 0x3b; break; case (0x02): //A-B LNA1 pdiv_ant_conf->fast_div_bias = 0x3d; break; case (0x03): //A-B A+B pdiv_ant_conf->fast_div_bias = 0x1; break; case (0x10): //LNA2 A-B pdiv_ant_conf->fast_div_bias = 0x7; break; case (0x12): //LNA2 LNA1 pdiv_ant_conf->fast_div_bias = 0x2; break; case (0x13): //LNA2 A+B pdiv_ant_conf->fast_div_bias = 0x7; break; case (0x20): //LNA1 A-B pdiv_ant_conf->fast_div_bias = 0x6; break; case (0x21): //LNA1 LNA2 pdiv_ant_conf->fast_div_bias = 0x0; break; case (0x23): //LNA1 A+B pdiv_ant_conf->fast_div_bias = 0x6; break; case (0x30): //A+B A-B pdiv_ant_conf->fast_div_bias = 0x1; break; case (0x31): //A+B LNA2 pdiv_ant_conf->fast_div_bias = 0x3b; break; case (0x32): //A+B LNA1 pdiv_ant_conf->fast_div_bias = 0x3d; break; default: break; } } } /* * AR9485/AR933x TODO: * + Select a ratio based on whether RSSI is low or not; but I need * to figure out what "low_rssi_th" is sourced from. * + What's ath_ant_div_comb_alt_check() in the reference driver do? * + .. and there's likely a bunch of other things to include in this. */ /* Antenna diversity and combining */ void ath_lna_rx_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs, unsigned long ticks, int hz) { HAL_ANT_COMB_CONFIG div_ant_conf; struct if_ath_ant_comb_state *antcomb = sc->sc_lna_div; int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set; int curr_main_set, curr_bias; int main_rssi = rs->rs_rssi_ctl[0]; int alt_rssi = rs->rs_rssi_ctl[1]; int rx_ant_conf, main_ant_conf, alt_ant_conf; HAL_BOOL short_scan = AH_FALSE; rx_ant_conf = (rs->rs_rssi_ctl[2] >> 4) & ATH_ANT_RX_MASK; main_ant_conf = (rs->rs_rssi_ctl[2] >> 2) & ATH_ANT_RX_MASK; alt_ant_conf = (rs->rs_rssi_ctl[2] >> 0) & ATH_ANT_RX_MASK; #if 0 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: RSSI %d/%d, conf %x/%x, rxconf %x, LNA: %d; ANT: %d; " "FastDiv: %d\n", __func__, main_rssi, alt_rssi, main_ant_conf, alt_ant_conf, rx_ant_conf, !!(rs->rs_rssi_ctl[2] & 0x80), !!(rs->rs_rssi_ctl[2] & 0x40), !!(rs->rs_rssi_ext[2] & 0x40)); #endif /* * If LNA diversity combining isn't enabled, don't run this. */ if (! sc->sc_dolnadiv) return; /* * XXX this is ugly, but the HAL code attaches the * LNA diversity to the TX antenna settings. * I don't know why. */ if (sc->sc_txantenna != HAL_ANT_VARIABLE) return; /* Record packet only when alt_rssi is positive */ if (main_rssi > 0 && alt_rssi > 0) { antcomb->total_pkt_count++; antcomb->main_total_rssi += main_rssi; antcomb->alt_total_rssi += alt_rssi; if (main_ant_conf == rx_ant_conf) antcomb->main_recv_cnt++; else antcomb->alt_recv_cnt++; } /* Short scan check */ if (antcomb->scan && antcomb->alt_good) { if (ieee80211_time_after(ticks, antcomb->scan_start_time + msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR))) short_scan = AH_TRUE; else if (antcomb->total_pkt_count == ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) { alt_ratio = ((antcomb->alt_recv_cnt * 100) / antcomb->total_pkt_count); if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO) short_scan = AH_TRUE; } } #if 0 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: total pkt=%d, aggr=%d, short_scan=%d\n", __func__, antcomb->total_pkt_count, !! (rs->rs_moreaggr), !! (short_scan)); #endif if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) || rs->rs_moreaggr) && !short_scan) return; if (antcomb->total_pkt_count) { alt_ratio = ((antcomb->alt_recv_cnt * 100) / antcomb->total_pkt_count); main_rssi_avg = (antcomb->main_total_rssi / antcomb->total_pkt_count); alt_rssi_avg = (antcomb->alt_total_rssi / antcomb->total_pkt_count); } OS_MEMZERO(&div_ant_conf, sizeof(div_ant_conf)); ath_hal_div_comb_conf_get(sc->sc_ah, &div_ant_conf); curr_alt_set = div_ant_conf.alt_lna_conf; curr_main_set = div_ant_conf.main_lna_conf; curr_bias = div_ant_conf.fast_div_bias; antcomb->count++; if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) { if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) { ath_lnaconf_alt_good_scan(antcomb, &div_ant_conf, main_rssi_avg); antcomb->alt_good = AH_TRUE; } else { antcomb->alt_good = AH_FALSE; } antcomb->count = 0; antcomb->scan = AH_TRUE; antcomb->scan_not_start = AH_TRUE; } if (!antcomb->scan) { if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) { if (curr_alt_set == HAL_ANT_DIV_COMB_LNA2) { /* Switch main and alt LNA */ div_ant_conf.main_lna_conf = HAL_ANT_DIV_COMB_LNA2; div_ant_conf.alt_lna_conf = HAL_ANT_DIV_COMB_LNA1; } else if (curr_alt_set == HAL_ANT_DIV_COMB_LNA1) { div_ant_conf.main_lna_conf = HAL_ANT_DIV_COMB_LNA1; div_ant_conf.alt_lna_conf = HAL_ANT_DIV_COMB_LNA2; } goto div_comb_done; } else if ((curr_alt_set != HAL_ANT_DIV_COMB_LNA1) && (curr_alt_set != HAL_ANT_DIV_COMB_LNA2)) { /* Set alt to another LNA */ if (curr_main_set == HAL_ANT_DIV_COMB_LNA2) div_ant_conf.alt_lna_conf = HAL_ANT_DIV_COMB_LNA1; else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1) div_ant_conf.alt_lna_conf = HAL_ANT_DIV_COMB_LNA2; goto div_comb_done; } if ((alt_rssi_avg < (main_rssi_avg + antcomb->lna1_lna2_delta))) goto div_comb_done; } if (!antcomb->scan_not_start) { switch (curr_alt_set) { case HAL_ANT_DIV_COMB_LNA2: antcomb->rssi_lna2 = alt_rssi_avg; antcomb->rssi_lna1 = main_rssi_avg; antcomb->scan = AH_TRUE; /* set to A+B */ div_ant_conf.main_lna_conf = HAL_ANT_DIV_COMB_LNA1; div_ant_conf.alt_lna_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2; break; case HAL_ANT_DIV_COMB_LNA1: antcomb->rssi_lna1 = alt_rssi_avg; antcomb->rssi_lna2 = main_rssi_avg; antcomb->scan = AH_TRUE; /* set to A+B */ div_ant_conf.main_lna_conf = HAL_ANT_DIV_COMB_LNA2; div_ant_conf.alt_lna_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2; break; case HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2: antcomb->rssi_add = alt_rssi_avg; antcomb->scan = AH_TRUE; /* set to A-B */ div_ant_conf.alt_lna_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2; break; case HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2: antcomb->rssi_sub = alt_rssi_avg; antcomb->scan = AH_FALSE; if (antcomb->rssi_lna2 > (antcomb->rssi_lna1 + ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) { /* use LNA2 as main LNA */ if ((antcomb->rssi_add > antcomb->rssi_lna1) && (antcomb->rssi_add > antcomb->rssi_sub)) { /* set to A+B */ div_ant_conf.main_lna_conf = HAL_ANT_DIV_COMB_LNA2; div_ant_conf.alt_lna_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2; } else if (antcomb->rssi_sub > antcomb->rssi_lna1) { /* set to A-B */ div_ant_conf.main_lna_conf = HAL_ANT_DIV_COMB_LNA2; div_ant_conf.alt_lna_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2; } else { /* set to LNA1 */ div_ant_conf.main_lna_conf = HAL_ANT_DIV_COMB_LNA2; div_ant_conf.alt_lna_conf = HAL_ANT_DIV_COMB_LNA1; } } else { /* use LNA1 as main LNA */ if ((antcomb->rssi_add > antcomb->rssi_lna2) && (antcomb->rssi_add > antcomb->rssi_sub)) { /* set to A+B */ div_ant_conf.main_lna_conf = HAL_ANT_DIV_COMB_LNA1; div_ant_conf.alt_lna_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2; } else if (antcomb->rssi_sub > antcomb->rssi_lna1) { /* set to A-B */ div_ant_conf.main_lna_conf = HAL_ANT_DIV_COMB_LNA1; div_ant_conf.alt_lna_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2; } else { /* set to LNA2 */ div_ant_conf.main_lna_conf = HAL_ANT_DIV_COMB_LNA1; div_ant_conf.alt_lna_conf = HAL_ANT_DIV_COMB_LNA2; } } break; default: break; } } else { if (!antcomb->alt_good) { antcomb->scan_not_start = AH_FALSE; /* Set alt to another LNA */ if (curr_main_set == HAL_ANT_DIV_COMB_LNA2) { div_ant_conf.main_lna_conf = HAL_ANT_DIV_COMB_LNA2; div_ant_conf.alt_lna_conf = HAL_ANT_DIV_COMB_LNA1; } else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1) { div_ant_conf.main_lna_conf = HAL_ANT_DIV_COMB_LNA1; div_ant_conf.alt_lna_conf = HAL_ANT_DIV_COMB_LNA2; } goto div_comb_done; } } ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf, main_rssi_avg, alt_rssi_avg, alt_ratio); antcomb->quick_scan_cnt++; div_comb_done: #if 0 ath_ant_div_conf_fast_divbias(&div_ant_conf); #endif ath_ant_adjust_fast_divbias(antcomb, alt_ratio, ATH_ANT_DIV_COMB_ALT_ANT_RATIO, div_ant_conf.antdiv_configgroup, &div_ant_conf); ath_hal_div_comb_conf_set(sc->sc_ah, &div_ant_conf); DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: total_pkt_count=%d\n", __func__, antcomb->total_pkt_count); DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_total_rssi=%d\n", __func__, antcomb->main_total_rssi); DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_total_rssi=%d\n", __func__, antcomb->alt_total_rssi); DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_rssi_avg=%d\n", __func__, main_rssi_avg); DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_alt_rssi_avg=%d\n", __func__, alt_rssi_avg); DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_recv_cnt=%d\n", __func__, antcomb->main_recv_cnt); DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_recv_cnt=%d\n", __func__, antcomb->alt_recv_cnt); // if (curr_alt_set != div_ant_conf.alt_lna_conf) DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: lna_conf: %x -> %x\n", __func__, curr_alt_set, div_ant_conf.alt_lna_conf); // if (curr_main_set != div_ant_conf.main_lna_conf) DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_lna_conf: %x -> %x\n", __func__, curr_main_set, div_ant_conf.main_lna_conf); // if (curr_bias != div_ant_conf.fast_div_bias) DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: fast_div_bias: %x -> %x\n", __func__, curr_bias, div_ant_conf.fast_div_bias); antcomb->scan_start_time = ticks; antcomb->total_pkt_count = 0; antcomb->main_total_rssi = 0; antcomb->alt_total_rssi = 0; antcomb->main_recv_cnt = 0; antcomb->alt_recv_cnt = 0; } Index: head/sys/dev/ath/if_ath_pci.c =================================================================== --- head/sys/dev/ath/if_ath_pci.c (revision 298938) +++ head/sys/dev/ath/if_ath_pci.c (revision 298939) @@ -1,469 +1,469 @@ /*- * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include __FBSDID("$FreeBSD$"); /* * PCI/Cardbus front-end for the Atheros Wireless LAN controller driver. */ #include "opt_ath.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* For EEPROM firmware */ #ifdef ATH_EEPROM_FIRMWARE #include #include #endif /* ATH_EEPROM_FIRMWARE */ /* * PCI glue. */ struct ath_pci_softc { struct ath_softc sc_sc; struct resource *sc_sr; /* memory resource */ struct resource *sc_irq; /* irq resource */ void *sc_ih; /* interrupt handler */ }; /* * XXX eventually this should be some system level definition - * so modules will hvae probe/attach information like USB. + * so modules will have probe/attach information like USB. * But for now.. */ struct pci_device_id { int vendor_id; int device_id; int sub_vendor_id; int sub_device_id; int driver_data; int match_populated:1; int match_vendor_id:1; int match_device_id:1; int match_sub_vendor_id:1; int match_sub_device_id:1; }; #define PCI_VDEVICE(v, s) \ .vendor_id = (v), \ .device_id = (s), \ .match_populated = 1, \ .match_vendor_id = 1, \ .match_device_id = 1 #define PCI_DEVICE_SUB(v, d, dv, ds) \ .match_populated = 1, \ .vendor_id = (v), .match_vendor_id = 1, \ .device_id = (d), .match_device_id = 1, \ .sub_vendor_id = (dv), .match_sub_vendor_id = 1, \ .sub_device_id = (ds), .match_sub_device_id = 1 #define PCI_VENDOR_ID_ATHEROS 0x168c #define PCI_VENDOR_ID_SAMSUNG 0x144d #define PCI_VENDOR_ID_AZWAVE 0x1a3b #define PCI_VENDOR_ID_FOXCONN 0x105b #define PCI_VENDOR_ID_ATTANSIC 0x1969 #define PCI_VENDOR_ID_ASUSTEK 0x1043 #define PCI_VENDOR_ID_DELL 0x1028 #define PCI_VENDOR_ID_QMI 0x1a32 #define PCI_VENDOR_ID_LENOVO 0x17aa #define PCI_VENDOR_ID_HP 0x103c #include "if_ath_pci_devlist.h" /* * Attempt to find a match for the given device in * the given device table. * * Returns the device structure or NULL if no matching * PCI device is found. */ static const struct pci_device_id * ath_pci_probe_device(device_t dev, const struct pci_device_id *dev_table, int nentries) { int i; int vendor_id, device_id; int sub_vendor_id, sub_device_id; vendor_id = pci_get_vendor(dev); device_id = pci_get_device(dev); sub_vendor_id = pci_get_subvendor(dev); sub_device_id = pci_get_subdevice(dev); for (i = 0; i < nentries; i++) { /* Don't match on non-populated (eg empty) entries */ if (! dev_table[i].match_populated) continue; if (dev_table[i].match_vendor_id && (dev_table[i].vendor_id != vendor_id)) continue; if (dev_table[i].match_device_id && (dev_table[i].device_id != device_id)) continue; if (dev_table[i].match_sub_vendor_id && (dev_table[i].sub_vendor_id != sub_vendor_id)) continue; if (dev_table[i].match_sub_device_id && (dev_table[i].sub_device_id != sub_device_id)) continue; /* Match */ return (&dev_table[i]); } return (NULL); } #define BS_BAR 0x10 #define PCIR_RETRY_TIMEOUT 0x41 #define PCIR_CFG_PMCSR 0x48 #define DEFAULT_CACHESIZE 32 static void ath_pci_setup(device_t dev) { uint8_t cz; /* XXX TODO: need to override the _system_ saved copies of this */ /* * If the cache line size is 0, force it to a reasonable * value. */ cz = pci_read_config(dev, PCIR_CACHELNSZ, 1); if (cz == 0) { pci_write_config(dev, PCIR_CACHELNSZ, DEFAULT_CACHESIZE / 4, 1); } /* Override the system latency timer */ pci_write_config(dev, PCIR_LATTIMER, 0xa8, 1); /* If a PCI NIC, force wakeup */ #ifdef ATH_PCI_WAKEUP_WAR /* XXX TODO: don't do this for non-PCI (ie, PCIe, Cardbus!) */ if (1) { uint16_t pmcsr; pmcsr = pci_read_config(dev, PCIR_CFG_PMCSR, 2); pmcsr |= 3; pci_write_config(dev, PCIR_CFG_PMCSR, pmcsr, 2); pmcsr &= ~3; pci_write_config(dev, PCIR_CFG_PMCSR, pmcsr, 2); } #endif /* * Disable retry timeout to keep PCI Tx retries from * interfering with C3 CPU state. */ pci_write_config(dev, PCIR_RETRY_TIMEOUT, 0, 1); } static int ath_pci_probe(device_t dev) { const char* devname; devname = ath_hal_probe(pci_get_vendor(dev), pci_get_device(dev)); if (devname != NULL) { device_set_desc(dev, devname); return BUS_PROBE_DEFAULT; } return ENXIO; } static int ath_pci_attach(device_t dev) { struct ath_pci_softc *psc = device_get_softc(dev); struct ath_softc *sc = &psc->sc_sc; int error = ENXIO; int rid; #ifdef ATH_EEPROM_FIRMWARE const struct firmware *fw = NULL; const char *buf; #endif const struct pci_device_id *pd; sc->sc_dev = dev; /* Do this lookup anyway; figure out what to do with it later */ pd = ath_pci_probe_device(dev, ath_pci_id_table, nitems(ath_pci_id_table)); if (pd) sc->sc_pci_devinfo = pd->driver_data; /* * Enable bus mastering. */ pci_enable_busmaster(dev); /* * Setup other PCI bus configuration parameters. */ ath_pci_setup(dev); /* * Setup memory-mapping of PCI registers. */ rid = BS_BAR; psc->sc_sr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (psc->sc_sr == NULL) { device_printf(dev, "cannot map register space\n"); goto bad; } sc->sc_st = (HAL_BUS_TAG) rman_get_bustag(psc->sc_sr); sc->sc_sh = (HAL_BUS_HANDLE) rman_get_bushandle(psc->sc_sr); /* * Mark device invalid so any interrupts (shared or otherwise) * that arrive before the HAL is setup are discarded. */ sc->sc_invalid = 1; ATH_LOCK_INIT(sc); ATH_PCU_LOCK_INIT(sc); ATH_RX_LOCK_INIT(sc); ATH_TX_LOCK_INIT(sc); ATH_TXSTATUS_LOCK_INIT(sc); /* * Arrange interrupt line. */ rid = 0; psc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE|RF_ACTIVE); if (psc->sc_irq == NULL) { device_printf(dev, "could not map interrupt\n"); goto bad1; } if (bus_setup_intr(dev, psc->sc_irq, INTR_TYPE_NET | INTR_MPSAFE, NULL, ath_intr, sc, &psc->sc_ih)) { device_printf(dev, "could not establish interrupt\n"); goto bad2; } /* * Setup DMA descriptor area. */ if (bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */ 1, 0, /* alignment, bounds */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ 0x3ffff, /* maxsize XXX */ ATH_MAX_SCATTER, /* nsegments */ 0x3ffff, /* maxsegsize XXX */ BUS_DMA_ALLOCNOW, /* flags */ NULL, /* lockfunc */ NULL, /* lockarg */ &sc->sc_dmat)) { device_printf(dev, "cannot allocate DMA tag\n"); goto bad3; } #ifdef ATH_EEPROM_FIRMWARE /* * If there's an EEPROM firmware image, load that in. */ if (resource_string_value(device_get_name(dev), device_get_unit(dev), "eeprom_firmware", &buf) == 0) { if (bootverbose) device_printf(dev, "%s: looking up firmware @ '%s'\n", __func__, buf); fw = firmware_get(buf); if (fw == NULL) { device_printf(dev, "%s: couldn't find firmware\n", __func__); goto bad4; } device_printf(dev, "%s: EEPROM firmware @ %p\n", __func__, fw->data); sc->sc_eepromdata = malloc(fw->datasize, M_TEMP, M_WAITOK | M_ZERO); if (! sc->sc_eepromdata) { device_printf(dev, "%s: can't malloc eepromdata\n", __func__); goto bad4; } memcpy(sc->sc_eepromdata, fw->data, fw->datasize); firmware_put(fw, 0); } #endif /* ATH_EEPROM_FIRMWARE */ error = ath_attach(pci_get_device(dev), sc); if (error == 0) /* success */ return 0; #ifdef ATH_EEPROM_FIRMWARE bad4: #endif bus_dma_tag_destroy(sc->sc_dmat); bad3: bus_teardown_intr(dev, psc->sc_irq, psc->sc_ih); bad2: bus_release_resource(dev, SYS_RES_IRQ, 0, psc->sc_irq); bad1: bus_release_resource(dev, SYS_RES_MEMORY, BS_BAR, psc->sc_sr); ATH_TXSTATUS_LOCK_DESTROY(sc); ATH_PCU_LOCK_DESTROY(sc); ATH_RX_LOCK_DESTROY(sc); ATH_TX_LOCK_DESTROY(sc); ATH_LOCK_DESTROY(sc); bad: return (error); } static int ath_pci_detach(device_t dev) { struct ath_pci_softc *psc = device_get_softc(dev); struct ath_softc *sc = &psc->sc_sc; /* check if device was removed */ sc->sc_invalid = !bus_child_present(dev); /* * Do a config read to clear pre-existing pci error status. */ (void) pci_read_config(dev, PCIR_COMMAND, 4); ath_detach(sc); bus_generic_detach(dev); bus_teardown_intr(dev, psc->sc_irq, psc->sc_ih); bus_release_resource(dev, SYS_RES_IRQ, 0, psc->sc_irq); bus_dma_tag_destroy(sc->sc_dmat); bus_release_resource(dev, SYS_RES_MEMORY, BS_BAR, psc->sc_sr); if (sc->sc_eepromdata) free(sc->sc_eepromdata, M_TEMP); ATH_TXSTATUS_LOCK_DESTROY(sc); ATH_PCU_LOCK_DESTROY(sc); ATH_RX_LOCK_DESTROY(sc); ATH_TX_LOCK_DESTROY(sc); ATH_LOCK_DESTROY(sc); return (0); } static int ath_pci_shutdown(device_t dev) { struct ath_pci_softc *psc = device_get_softc(dev); ath_shutdown(&psc->sc_sc); return (0); } static int ath_pci_suspend(device_t dev) { struct ath_pci_softc *psc = device_get_softc(dev); ath_suspend(&psc->sc_sc); return (0); } static int ath_pci_resume(device_t dev) { struct ath_pci_softc *psc = device_get_softc(dev); /* * Suspend/resume resets the PCI configuration space. */ ath_pci_setup(dev); ath_resume(&psc->sc_sc); return (0); } static device_method_t ath_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ath_pci_probe), DEVMETHOD(device_attach, ath_pci_attach), DEVMETHOD(device_detach, ath_pci_detach), DEVMETHOD(device_shutdown, ath_pci_shutdown), DEVMETHOD(device_suspend, ath_pci_suspend), DEVMETHOD(device_resume, ath_pci_resume), { 0,0 } }; static driver_t ath_pci_driver = { "ath", ath_pci_methods, sizeof (struct ath_pci_softc) }; static devclass_t ath_devclass; DRIVER_MODULE(ath_pci, pci, ath_pci_driver, ath_devclass, 0, 0); MODULE_VERSION(ath_pci, 1); MODULE_DEPEND(ath_pci, wlan, 1, 1, 1); /* 802.11 media layer */ MODULE_DEPEND(ath_pci, if_ath, 1, 1, 1); /* if_ath driver */ Index: head/sys/dev/ath/if_ath_rx.c =================================================================== --- head/sys/dev/ath/if_ath_rx.c (revision 298938) +++ head/sys/dev/ath/if_ath_rx.c (revision 298939) @@ -1,1477 +1,1477 @@ /*- * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include __FBSDID("$FreeBSD$"); /* * Driver for the Atheros Wireless LAN controller. * * This software is derived from work of Atsushi Onoe; his contribution * is greatly appreciated. */ #include "opt_inet.h" #include "opt_ath.h" /* * This is needed for register operations which are performed * by the driver - eg, calls to ath_hal_gettsf32(). * * It's also required for any AH_DEBUG checks in here, eg the * module dependencies. */ #include "opt_ah.h" #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for mp_ncpus */ #include #include #include #include #include #include #include #include #include #include #include #ifdef IEEE80211_SUPPORT_SUPERG #include #endif #ifdef IEEE80211_SUPPORT_TDMA #include #endif #include #ifdef INET #include #include #endif #include #include /* XXX for softled */ #include #include #include #include #include #include #include #include #include #include #include #include #ifdef ATH_TX99_DIAG #include #endif #ifdef ATH_DEBUG_ALQ #include #endif #include /* * Calculate the receive filter according to the * operating mode and state: * * o always accept unicast, broadcast, and multicast traffic * o accept PHY error frames when hardware doesn't have MIB support * to count and we need them for ANI (sta mode only until recently) * and we are not scanning (ANI is disabled) * NB: older hal's add rx filter bits out of sight and we need to * blindly preserve them * o probe request frames are accepted only when operating in * hostap, adhoc, mesh, or monitor modes * o enable promiscuous mode * - when in monitor mode * - if interface marked PROMISC (assumes bridge setting is filtered) * o accept beacons: * - when operating in station mode for collecting rssi data when * the station is otherwise quiet, or * - when operating in adhoc mode so the 802.11 layer creates * node table entries for peers, * - when scanning * - when doing s/w beacon miss (e.g. for ap+sta) * - when operating in ap mode in 11g to detect overlapping bss that * require protection * - when operating in mesh mode to detect neighbors * o accept control frames: * - when in monitor mode * XXX HT protection for 11n */ u_int32_t ath_calcrxfilter(struct ath_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; u_int32_t rfilt; rfilt = HAL_RX_FILTER_UCAST | HAL_RX_FILTER_BCAST | HAL_RX_FILTER_MCAST; if (!sc->sc_needmib && !sc->sc_scanning) rfilt |= HAL_RX_FILTER_PHYERR; if (ic->ic_opmode != IEEE80211_M_STA) rfilt |= HAL_RX_FILTER_PROBEREQ; /* XXX ic->ic_monvaps != 0? */ if (ic->ic_opmode == IEEE80211_M_MONITOR || ic->ic_promisc > 0) rfilt |= HAL_RX_FILTER_PROM; /* * Only listen to all beacons if we're scanning. * * Otherwise we only really need to hear beacons from * our own BSSID. * * IBSS? software beacon miss? Just receive all beacons. * We need to hear beacons/probe requests from everyone so * we can merge ibss. */ if (ic->ic_opmode == IEEE80211_M_IBSS || sc->sc_swbmiss) { rfilt |= HAL_RX_FILTER_BEACON; } else if (ic->ic_opmode == IEEE80211_M_STA) { if (sc->sc_do_mybeacon && ! sc->sc_scanning) { rfilt |= HAL_RX_FILTER_MYBEACON; } else { /* scanning, non-mybeacon chips */ rfilt |= HAL_RX_FILTER_BEACON; } } /* * NB: We don't recalculate the rx filter when * ic_protmode changes; otherwise we could do * this only when ic_protmode != NONE. */ if (ic->ic_opmode == IEEE80211_M_HOSTAP && IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) rfilt |= HAL_RX_FILTER_BEACON; /* * Enable hardware PS-POLL RX only for hostap mode; * STA mode sends PS-POLL frames but never * receives them. */ if (ath_hal_getcapability(sc->sc_ah, HAL_CAP_PSPOLL, 0, NULL) == HAL_OK && ic->ic_opmode == IEEE80211_M_HOSTAP) rfilt |= HAL_RX_FILTER_PSPOLL; if (sc->sc_nmeshvaps) { rfilt |= HAL_RX_FILTER_BEACON; if (sc->sc_hasbmatch) rfilt |= HAL_RX_FILTER_BSSID; else rfilt |= HAL_RX_FILTER_PROM; } if (ic->ic_opmode == IEEE80211_M_MONITOR) rfilt |= HAL_RX_FILTER_CONTROL; /* * Enable RX of compressed BAR frames only when doing * 802.11n. Required for A-MPDU. */ if (IEEE80211_IS_CHAN_HT(ic->ic_curchan)) rfilt |= HAL_RX_FILTER_COMPBAR; /* * Enable radar PHY errors if requested by the * DFS module. */ if (sc->sc_dodfs) rfilt |= HAL_RX_FILTER_PHYRADAR; /* * Enable spectral PHY errors if requested by the * spectral module. */ if (sc->sc_dospectral) rfilt |= HAL_RX_FILTER_PHYRADAR; DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x, %s\n", __func__, rfilt, ieee80211_opmode_name[ic->ic_opmode]); return rfilt; } static int ath_legacy_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf) { struct ath_hal *ah = sc->sc_ah; int error; struct mbuf *m; struct ath_desc *ds; /* XXX TODO: ATH_RX_LOCK_ASSERT(sc); */ m = bf->bf_m; if (m == NULL) { /* * NB: by assigning a page to the rx dma buffer we * implicitly satisfy the Atheros requirement that * this buffer be cache-line-aligned and sized to be * multiple of the cache line size. Not doing this * causes weird stuff to happen (for the 5210 at least). */ m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (m == NULL) { DPRINTF(sc, ATH_DEBUG_ANY, "%s: no mbuf/cluster\n", __func__); sc->sc_stats.ast_rx_nombuf++; return ENOMEM; } m->m_pkthdr.len = m->m_len = m->m_ext.ext_size; error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m, bf->bf_segs, &bf->bf_nseg, BUS_DMA_NOWAIT); if (error != 0) { DPRINTF(sc, ATH_DEBUG_ANY, "%s: bus_dmamap_load_mbuf_sg failed; error %d\n", __func__, error); sc->sc_stats.ast_rx_busdma++; m_freem(m); return error; } KASSERT(bf->bf_nseg == 1, ("multi-segment packet; nseg %u", bf->bf_nseg)); bf->bf_m = m; } bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREREAD); /* * Setup descriptors. For receive we always terminate * the descriptor list with a self-linked entry so we'll * not get overrun under high load (as can happen with a * 5212 when ANI processing enables PHY error frames). * * To insure the last descriptor is self-linked we create * each descriptor as self-linked and add it to the end. As * each additional descriptor is added the previous self-linked * entry is ``fixed'' naturally. This should be safe even * if DMA is happening. When processing RX interrupts we * never remove/process the last, self-linked, entry on the * descriptor list. This insures the hardware always has * someplace to write a new frame. */ /* * 11N: we can no longer afford to self link the last descriptor. * MAC acknowledges BA status as long as it copies frames to host * buffer (or rx fifo). This can incorrectly acknowledge packets * to a sender if last desc is self-linked. */ ds = bf->bf_desc; if (sc->sc_rxslink) ds->ds_link = bf->bf_daddr; /* link to self */ else ds->ds_link = 0; /* terminate the list */ ds->ds_data = bf->bf_segs[0].ds_addr; ath_hal_setuprxdesc(ah, ds , m->m_len /* buffer size */ , 0 ); if (sc->sc_rxlink != NULL) *sc->sc_rxlink = bf->bf_daddr; sc->sc_rxlink = &ds->ds_link; return 0; } /* * Intercept management frames to collect beacon rssi data * and to do ibss merges. */ void ath_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, int subtype, const struct ieee80211_rx_stats *rxs, int rssi, int nf) { struct ieee80211vap *vap = ni->ni_vap; struct ath_softc *sc = vap->iv_ic->ic_softc; uint64_t tsf_beacon_old, tsf_beacon; uint64_t nexttbtt; int64_t tsf_delta; int32_t tsf_delta_bmiss; int32_t tsf_remainder; uint64_t tsf_beacon_target; int tsf_intval; tsf_beacon_old = ((uint64_t) le32dec(ni->ni_tstamp.data + 4)) << 32; tsf_beacon_old |= le32dec(ni->ni_tstamp.data); #define TU_TO_TSF(_tu) (((u_int64_t)(_tu)) << 10) tsf_intval = 1; if (ni->ni_intval > 0) { tsf_intval = TU_TO_TSF(ni->ni_intval); } #undef TU_TO_TSF /* * Call up first so subsequent work can use information * potentially stored in the node (e.g. for ibss merge). */ ATH_VAP(vap)->av_recv_mgmt(ni, m, subtype, rxs, rssi, nf); switch (subtype) { case IEEE80211_FC0_SUBTYPE_BEACON: /* * Only do the following processing if it's for * the current BSS. * * In scan and IBSS mode we receive all beacons, * which means we need to filter out stuff * that isn't for us or we'll end up constantly * trying to sync / merge to BSSes that aren't * actually us. */ if (IEEE80211_ADDR_EQ(ni->ni_bssid, vap->iv_bss->ni_bssid)) { /* update rssi statistics for use by the hal */ /* XXX unlocked check against vap->iv_bss? */ ATH_RSSI_LPF(sc->sc_halstats.ns_avgbrssi, rssi); tsf_beacon = ((uint64_t) le32dec(ni->ni_tstamp.data + 4)) << 32; tsf_beacon |= le32dec(ni->ni_tstamp.data); nexttbtt = ath_hal_getnexttbtt(sc->sc_ah); /* * Let's calculate the delta and remainder, so we can see * if the beacon timer from the AP is varying by more than * a few TU. (Which would be a huge, huge problem.) */ tsf_delta = (long long) tsf_beacon - (long long) tsf_beacon_old; tsf_delta_bmiss = tsf_delta / tsf_intval; /* * If our delta is greater than half the beacon interval, * let's round the bmiss value up to the next beacon * interval. Ie, we're running really, really early * on the next beacon. */ if (tsf_delta % tsf_intval > (tsf_intval / 2)) tsf_delta_bmiss ++; tsf_beacon_target = tsf_beacon_old + (((unsigned long long) tsf_delta_bmiss) * (long long) tsf_intval); /* * The remainder using '%' is between 0 .. intval-1. * If we're actually running too fast, then the remainder * will be some large number just under intval-1. * So we need to look at whether we're running * before or after the target beacon interval * and if we are, modify how we do the remainder * calculation. */ if (tsf_beacon < tsf_beacon_target) { tsf_remainder = -(tsf_intval - ((tsf_beacon - tsf_beacon_old) % tsf_intval)); } else { tsf_remainder = (tsf_beacon - tsf_beacon_old) % tsf_intval; } DPRINTF(sc, ATH_DEBUG_BEACON, "%s: old_tsf=%llu, new_tsf=%llu, target_tsf=%llu, delta=%lld, bmiss=%d, remainder=%d\n", __func__, (unsigned long long) tsf_beacon_old, (unsigned long long) tsf_beacon, (unsigned long long) tsf_beacon_target, (long long) tsf_delta, tsf_delta_bmiss, tsf_remainder); DPRINTF(sc, ATH_DEBUG_BEACON, "%s: tsf=%llu, nexttbtt=%llu, delta=%d\n", __func__, (unsigned long long) tsf_beacon, (unsigned long long) nexttbtt, (int32_t) tsf_beacon - (int32_t) nexttbtt + tsf_intval); /* We only do syncbeacon on STA VAPs; not on IBSS */ if (vap->iv_opmode == IEEE80211_M_STA && sc->sc_syncbeacon && ni == vap->iv_bss && (vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP)) { DPRINTF(sc, ATH_DEBUG_BEACON, "%s: syncbeacon=1; syncing\n", __func__); /* * Resync beacon timers using the tsf of the beacon * frame we just received. */ ath_beacon_config(sc, vap); sc->sc_syncbeacon = 0; } } /* fall thru... */ case IEEE80211_FC0_SUBTYPE_PROBE_RESP: if (vap->iv_opmode == IEEE80211_M_IBSS && vap->iv_state == IEEE80211_S_RUN && ieee80211_ibss_merge_check(ni)) { uint32_t rstamp = sc->sc_lastrs->rs_tstamp; uint64_t tsf = ath_extend_tsf(sc, rstamp, ath_hal_gettsf64(sc->sc_ah)); /* * Handle ibss merge as needed; check the tsf on the * frame before attempting the merge. The 802.11 spec * says the station should change it's bssid to match * the oldest station with the same ssid, where oldest * is determined by the tsf. Note that hardware * reconfiguration happens through callback to * ath_newstate as the state machine will go from * RUN -> RUN when this happens. */ if (le64toh(ni->ni_tstamp.tsf) >= tsf) { DPRINTF(sc, ATH_DEBUG_STATE, "ibss merge, rstamp %u tsf %ju " "tstamp %ju\n", rstamp, (uintmax_t)tsf, (uintmax_t)ni->ni_tstamp.tsf); (void) ieee80211_ibss_merge(ni); } } break; } } #ifdef ATH_ENABLE_RADIOTAP_VENDOR_EXT static void ath_rx_tap_vendor(struct ath_softc *sc, struct mbuf *m, const struct ath_rx_status *rs, u_int64_t tsf, int16_t nf) { /* Fill in the extension bitmap */ sc->sc_rx_th.wr_ext_bitmap = htole32(1 << ATH_RADIOTAP_VENDOR_HEADER); /* Fill in the vendor header */ sc->sc_rx_th.wr_vh.vh_oui[0] = 0x7f; sc->sc_rx_th.wr_vh.vh_oui[1] = 0x03; sc->sc_rx_th.wr_vh.vh_oui[2] = 0x00; /* XXX what should this be? */ sc->sc_rx_th.wr_vh.vh_sub_ns = 0; sc->sc_rx_th.wr_vh.vh_skip_len = htole16(sizeof(struct ath_radiotap_vendor_hdr)); /* General version info */ sc->sc_rx_th.wr_v.vh_version = 1; sc->sc_rx_th.wr_v.vh_rx_chainmask = sc->sc_rxchainmask; /* rssi */ sc->sc_rx_th.wr_v.rssi_ctl[0] = rs->rs_rssi_ctl[0]; sc->sc_rx_th.wr_v.rssi_ctl[1] = rs->rs_rssi_ctl[1]; sc->sc_rx_th.wr_v.rssi_ctl[2] = rs->rs_rssi_ctl[2]; sc->sc_rx_th.wr_v.rssi_ext[0] = rs->rs_rssi_ext[0]; sc->sc_rx_th.wr_v.rssi_ext[1] = rs->rs_rssi_ext[1]; sc->sc_rx_th.wr_v.rssi_ext[2] = rs->rs_rssi_ext[2]; /* evm */ sc->sc_rx_th.wr_v.evm[0] = rs->rs_evm0; sc->sc_rx_th.wr_v.evm[1] = rs->rs_evm1; sc->sc_rx_th.wr_v.evm[2] = rs->rs_evm2; /* These are only populated from the AR9300 or later */ sc->sc_rx_th.wr_v.evm[3] = rs->rs_evm3; sc->sc_rx_th.wr_v.evm[4] = rs->rs_evm4; /* direction */ sc->sc_rx_th.wr_v.vh_flags = ATH_VENDOR_PKT_RX; /* RX rate */ sc->sc_rx_th.wr_v.vh_rx_hwrate = rs->rs_rate; /* RX flags */ sc->sc_rx_th.wr_v.vh_rs_flags = rs->rs_flags; if (rs->rs_isaggr) sc->sc_rx_th.wr_v.vh_flags |= ATH_VENDOR_PKT_ISAGGR; if (rs->rs_moreaggr) sc->sc_rx_th.wr_v.vh_flags |= ATH_VENDOR_PKT_MOREAGGR; /* phyerr info */ if (rs->rs_status & HAL_RXERR_PHY) { sc->sc_rx_th.wr_v.vh_phyerr_code = rs->rs_phyerr; sc->sc_rx_th.wr_v.vh_flags |= ATH_VENDOR_PKT_RXPHYERR; } else { sc->sc_rx_th.wr_v.vh_phyerr_code = 0xff; } sc->sc_rx_th.wr_v.vh_rs_status = rs->rs_status; sc->sc_rx_th.wr_v.vh_rssi = rs->rs_rssi; } #endif /* ATH_ENABLE_RADIOTAP_VENDOR_EXT */ static void ath_rx_tap(struct ath_softc *sc, struct mbuf *m, const struct ath_rx_status *rs, u_int64_t tsf, int16_t nf) { #define CHAN_HT20 htole32(IEEE80211_CHAN_HT20) #define CHAN_HT40U htole32(IEEE80211_CHAN_HT40U) #define CHAN_HT40D htole32(IEEE80211_CHAN_HT40D) #define CHAN_HT (CHAN_HT20|CHAN_HT40U|CHAN_HT40D) const HAL_RATE_TABLE *rt; uint8_t rix; rt = sc->sc_currates; KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode)); rix = rt->rateCodeToIndex[rs->rs_rate]; sc->sc_rx_th.wr_rate = sc->sc_hwmap[rix].ieeerate; sc->sc_rx_th.wr_flags = sc->sc_hwmap[rix].rxflags; #ifdef AH_SUPPORT_AR5416 sc->sc_rx_th.wr_chan_flags &= ~CHAN_HT; if (rs->rs_status & HAL_RXERR_PHY) { /* * PHY error - make sure the channel flags * reflect the actual channel configuration, * not the received frame. */ if (IEEE80211_IS_CHAN_HT40U(sc->sc_curchan)) sc->sc_rx_th.wr_chan_flags |= CHAN_HT40U; else if (IEEE80211_IS_CHAN_HT40D(sc->sc_curchan)) sc->sc_rx_th.wr_chan_flags |= CHAN_HT40D; else if (IEEE80211_IS_CHAN_HT20(sc->sc_curchan)) sc->sc_rx_th.wr_chan_flags |= CHAN_HT20; } else if (sc->sc_rx_th.wr_rate & IEEE80211_RATE_MCS) { /* HT rate */ struct ieee80211com *ic = &sc->sc_ic; if ((rs->rs_flags & HAL_RX_2040) == 0) sc->sc_rx_th.wr_chan_flags |= CHAN_HT20; else if (IEEE80211_IS_CHAN_HT40U(ic->ic_curchan)) sc->sc_rx_th.wr_chan_flags |= CHAN_HT40U; else sc->sc_rx_th.wr_chan_flags |= CHAN_HT40D; if ((rs->rs_flags & HAL_RX_GI) == 0) sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_SHORTGI; } #endif sc->sc_rx_th.wr_tsf = htole64(ath_extend_tsf(sc, rs->rs_tstamp, tsf)); if (rs->rs_status & HAL_RXERR_CRC) sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_BADFCS; /* XXX propagate other error flags from descriptor */ sc->sc_rx_th.wr_antnoise = nf; sc->sc_rx_th.wr_antsignal = nf + rs->rs_rssi; sc->sc_rx_th.wr_antenna = rs->rs_antenna; #undef CHAN_HT #undef CHAN_HT20 #undef CHAN_HT40U #undef CHAN_HT40D } static void ath_handle_micerror(struct ieee80211com *ic, struct ieee80211_frame *wh, int keyix) { struct ieee80211_node *ni; /* XXX recheck MIC to deal w/ chips that lie */ /* XXX discard MIC errors on !data frames */ ni = ieee80211_find_rxnode(ic, (const struct ieee80211_frame_min *) wh); if (ni != NULL) { ieee80211_notify_michael_failure(ni->ni_vap, wh, keyix); ieee80211_free_node(ni); } } /* * Process a single packet. * * The mbuf must already be synced, unmapped and removed from bf->bf_m * by this stage. * * The mbuf must be consumed by this routine - either passed up the * net80211 stack, put on the holding queue, or freed. */ int ath_rx_pkt(struct ath_softc *sc, struct ath_rx_status *rs, HAL_STATUS status, uint64_t tsf, int nf, HAL_RX_QUEUE qtype, struct ath_buf *bf, struct mbuf *m) { uint64_t rstamp; int len, type; struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_node *ni; int is_good = 0; struct ath_rx_edma *re = &sc->sc_rxedma[qtype]; /* * Calculate the correct 64 bit TSF given * the TSF64 register value and rs_tstamp. */ rstamp = ath_extend_tsf(sc, rs->rs_tstamp, tsf); /* These aren't specifically errors */ #ifdef AH_SUPPORT_AR5416 if (rs->rs_flags & HAL_RX_GI) sc->sc_stats.ast_rx_halfgi++; if (rs->rs_flags & HAL_RX_2040) sc->sc_stats.ast_rx_2040++; if (rs->rs_flags & HAL_RX_DELIM_CRC_PRE) sc->sc_stats.ast_rx_pre_crc_err++; if (rs->rs_flags & HAL_RX_DELIM_CRC_POST) sc->sc_stats.ast_rx_post_crc_err++; if (rs->rs_flags & HAL_RX_DECRYPT_BUSY) sc->sc_stats.ast_rx_decrypt_busy_err++; if (rs->rs_flags & HAL_RX_HI_RX_CHAIN) sc->sc_stats.ast_rx_hi_rx_chain++; if (rs->rs_flags & HAL_RX_STBC) sc->sc_stats.ast_rx_stbc++; #endif /* AH_SUPPORT_AR5416 */ if (rs->rs_status != 0) { if (rs->rs_status & HAL_RXERR_CRC) sc->sc_stats.ast_rx_crcerr++; if (rs->rs_status & HAL_RXERR_FIFO) sc->sc_stats.ast_rx_fifoerr++; if (rs->rs_status & HAL_RXERR_PHY) { sc->sc_stats.ast_rx_phyerr++; /* Process DFS radar events */ if ((rs->rs_phyerr == HAL_PHYERR_RADAR) || (rs->rs_phyerr == HAL_PHYERR_FALSE_RADAR_EXT)) { /* Now pass it to the radar processing code */ ath_dfs_process_phy_err(sc, m, rstamp, rs); } /* Be suitably paranoid about receiving phy errors out of the stats array bounds */ if (rs->rs_phyerr < 64) sc->sc_stats.ast_rx_phy[rs->rs_phyerr]++; goto rx_error; /* NB: don't count in ierrors */ } if (rs->rs_status & HAL_RXERR_DECRYPT) { /* * Decrypt error. If the error occurred * because there was no hardware key, then * let the frame through so the upper layers * can process it. This is necessary for 5210 * parts which have no way to setup a ``clear'' * key cache entry. * * XXX do key cache faulting */ if (rs->rs_keyix == HAL_RXKEYIX_INVALID) goto rx_accept; sc->sc_stats.ast_rx_badcrypt++; } /* * Similar as above - if the failure was a keymiss * just punt it up to the upper layers for now. */ if (rs->rs_status & HAL_RXERR_KEYMISS) { sc->sc_stats.ast_rx_keymiss++; goto rx_accept; } if (rs->rs_status & HAL_RXERR_MIC) { sc->sc_stats.ast_rx_badmic++; /* * Do minimal work required to hand off * the 802.11 header for notification. */ /* XXX frag's and qos frames */ len = rs->rs_datalen; if (len >= sizeof (struct ieee80211_frame)) { ath_handle_micerror(ic, mtod(m, struct ieee80211_frame *), sc->sc_splitmic ? rs->rs_keyix-32 : rs->rs_keyix); } } counter_u64_add(ic->ic_ierrors, 1); rx_error: /* * Cleanup any pending partial frame. */ if (re->m_rxpending != NULL) { m_freem(re->m_rxpending); re->m_rxpending = NULL; } /* * When a tap is present pass error frames * that have been requested. By default we * pass decrypt+mic errors but others may be * interesting (e.g. crc). */ if (ieee80211_radiotap_active(ic) && (rs->rs_status & sc->sc_monpass)) { /* NB: bpf needs the mbuf length setup */ len = rs->rs_datalen; m->m_pkthdr.len = m->m_len = len; ath_rx_tap(sc, m, rs, rstamp, nf); #ifdef ATH_ENABLE_RADIOTAP_VENDOR_EXT ath_rx_tap_vendor(sc, m, rs, rstamp, nf); #endif /* ATH_ENABLE_RADIOTAP_VENDOR_EXT */ ieee80211_radiotap_rx_all(ic, m); } /* XXX pass MIC errors up for s/w reclaculation */ m_freem(m); m = NULL; goto rx_next; } rx_accept: len = rs->rs_datalen; m->m_len = len; if (rs->rs_more) { /* * Frame spans multiple descriptors; save * it for the next completed descriptor, it * will be used to construct a jumbogram. */ if (re->m_rxpending != NULL) { /* NB: max frame size is currently 2 clusters */ sc->sc_stats.ast_rx_toobig++; m_freem(re->m_rxpending); } m->m_pkthdr.len = len; re->m_rxpending = m; m = NULL; goto rx_next; } else if (re->m_rxpending != NULL) { /* * This is the second part of a jumbogram, * chain it to the first mbuf, adjust the * frame length, and clear the rxpending state. */ re->m_rxpending->m_next = m; re->m_rxpending->m_pkthdr.len += len; m = re->m_rxpending; re->m_rxpending = NULL; } else { /* * Normal single-descriptor receive; setup packet length. */ m->m_pkthdr.len = len; } /* * Validate rs->rs_antenna. * * Some users w/ AR9285 NICs have reported crashes * here because rs_antenna field is bogusly large. * Let's enforce the maximum antenna limit of 8 * (and it shouldn't be hard coded, but that's a * separate problem) and if there's an issue, print * out an error and adjust rs_antenna to something * sensible. * * This code should be removed once the actual * root cause of the issue has been identified. * For example, it may be that the rs_antenna - * field is only valid for the lsat frame of + * field is only valid for the last frame of * an aggregate and it just happens that it is * "mostly" right. (This is a general statement - * the majority of the statistics are only valid * for the last frame in an aggregate. */ if (rs->rs_antenna > 7) { device_printf(sc->sc_dev, "%s: rs_antenna > 7 (%d)\n", __func__, rs->rs_antenna); #ifdef ATH_DEBUG ath_printrxbuf(sc, bf, 0, status == HAL_OK); #endif /* ATH_DEBUG */ rs->rs_antenna = 0; /* XXX better than nothing */ } /* * If this is an AR9285/AR9485, then the receive and LNA * configuration is stored in RSSI[2] / EXTRSSI[2]. * We can extract this out to build a much better * receive antenna profile. * * Yes, this just blurts over the above RX antenna field * for now. It's fine, the AR9285 doesn't really use * that. * * Later on we should store away the fine grained LNA * information and keep separate counters just for * that. It'll help when debugging the AR9285/AR9485 * combined diversity code. */ if (sc->sc_rx_lnamixer) { rs->rs_antenna = 0; /* Bits 0:1 - the LNA configuration used */ rs->rs_antenna |= ((rs->rs_rssi_ctl[2] & HAL_RX_LNA_CFG_USED) >> HAL_RX_LNA_CFG_USED_S); /* Bit 2 - the external RX antenna switch */ if (rs->rs_rssi_ctl[2] & HAL_RX_LNA_EXTCFG) rs->rs_antenna |= 0x4; } sc->sc_stats.ast_ant_rx[rs->rs_antenna]++; /* * Populate the rx status block. When there are bpf * listeners we do the additional work to provide * complete status. Otherwise we fill in only the * material required by ieee80211_input. Note that * noise setting is filled in above. */ if (ieee80211_radiotap_active(ic)) { ath_rx_tap(sc, m, rs, rstamp, nf); #ifdef ATH_ENABLE_RADIOTAP_VENDOR_EXT ath_rx_tap_vendor(sc, m, rs, rstamp, nf); #endif /* ATH_ENABLE_RADIOTAP_VENDOR_EXT */ } /* * From this point on we assume the frame is at least * as large as ieee80211_frame_min; verify that. */ if (len < IEEE80211_MIN_LEN) { if (!ieee80211_radiotap_active(ic)) { DPRINTF(sc, ATH_DEBUG_RECV, "%s: short packet %d\n", __func__, len); sc->sc_stats.ast_rx_tooshort++; } else { /* NB: in particular this captures ack's */ ieee80211_radiotap_rx_all(ic, m); } m_freem(m); m = NULL; goto rx_next; } if (IFF_DUMPPKTS(sc, ATH_DEBUG_RECV)) { const HAL_RATE_TABLE *rt = sc->sc_currates; uint8_t rix = rt->rateCodeToIndex[rs->rs_rate]; ieee80211_dump_pkt(ic, mtod(m, caddr_t), len, sc->sc_hwmap[rix].ieeerate, rs->rs_rssi); } m_adj(m, -IEEE80211_CRC_LEN); /* * Locate the node for sender, track state, and then * pass the (referenced) node up to the 802.11 layer * for its use. */ ni = ieee80211_find_rxnode_withkey(ic, mtod(m, const struct ieee80211_frame_min *), rs->rs_keyix == HAL_RXKEYIX_INVALID ? IEEE80211_KEYIX_NONE : rs->rs_keyix); sc->sc_lastrs = rs; #ifdef AH_SUPPORT_AR5416 if (rs->rs_isaggr) sc->sc_stats.ast_rx_agg++; #endif /* AH_SUPPORT_AR5416 */ if (ni != NULL) { /* * Only punt packets for ampdu reorder processing for * 11n nodes; net80211 enforces that M_AMPDU is only * set for 11n nodes. */ if (ni->ni_flags & IEEE80211_NODE_HT) m->m_flags |= M_AMPDU; /* * Sending station is known, dispatch directly. */ type = ieee80211_input(ni, m, rs->rs_rssi, nf); ieee80211_free_node(ni); m = NULL; /* * Arrange to update the last rx timestamp only for * frames from our ap when operating in station mode. * This assumes the rx key is always setup when * associated. */ if (ic->ic_opmode == IEEE80211_M_STA && rs->rs_keyix != HAL_RXKEYIX_INVALID) is_good = 1; } else { type = ieee80211_input_all(ic, m, rs->rs_rssi, nf); m = NULL; } /* * At this point we have passed the frame up the stack; thus * the mbuf is no longer ours. */ /* * Track rx rssi and do any rx antenna management. */ ATH_RSSI_LPF(sc->sc_halstats.ns_avgrssi, rs->rs_rssi); if (sc->sc_diversity) { /* * When using fast diversity, change the default rx * antenna if diversity chooses the other antenna 3 * times in a row. */ if (sc->sc_defant != rs->rs_antenna) { if (++sc->sc_rxotherant >= 3) ath_setdefantenna(sc, rs->rs_antenna); } else sc->sc_rxotherant = 0; } /* Handle slow diversity if enabled */ if (sc->sc_dolnadiv) { ath_lna_rx_comb_scan(sc, rs, ticks, hz); } if (sc->sc_softled) { /* * Blink for any data frame. Otherwise do a * heartbeat-style blink when idle. The latter * is mainly for station mode where we depend on * periodic beacon frames to trigger the poll event. */ if (type == IEEE80211_FC0_TYPE_DATA) { const HAL_RATE_TABLE *rt = sc->sc_currates; ath_led_event(sc, rt->rateCodeToIndex[rs->rs_rate]); } else if (ticks - sc->sc_ledevent >= sc->sc_ledidle) ath_led_event(sc, 0); } rx_next: /* * Debugging - complain if we didn't NULL the mbuf pointer * here. */ if (m != NULL) { device_printf(sc->sc_dev, "%s: mbuf %p should've been freed!\n", __func__, m); } return (is_good); } #define ATH_RX_MAX 128 /* * XXX TODO: break out the "get buffers" from "call ath_rx_pkt()" like * the EDMA code does. * * XXX TODO: then, do all of the RX list management stuff inside * ATH_RX_LOCK() so we don't end up potentially racing. The EDMA * code is doing it right. */ static void ath_rx_proc(struct ath_softc *sc, int resched) { #define PA2DESC(_sc, _pa) \ ((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \ ((_pa) - (_sc)->sc_rxdma.dd_desc_paddr))) struct ath_buf *bf; struct ath_hal *ah = sc->sc_ah; #ifdef IEEE80211_SUPPORT_SUPERG struct ieee80211com *ic = &sc->sc_ic; #endif struct ath_desc *ds; struct ath_rx_status *rs; struct mbuf *m; int ngood; HAL_STATUS status; int16_t nf; u_int64_t tsf; int npkts = 0; int kickpcu = 0; int ret; /* XXX we must not hold the ATH_LOCK here */ ATH_UNLOCK_ASSERT(sc); ATH_PCU_UNLOCK_ASSERT(sc); ATH_PCU_LOCK(sc); sc->sc_rxproc_cnt++; kickpcu = sc->sc_kickpcu; ATH_PCU_UNLOCK(sc); ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s: called\n", __func__); ngood = 0; nf = ath_hal_getchannoise(ah, sc->sc_curchan); sc->sc_stats.ast_rx_noise = nf; tsf = ath_hal_gettsf64(ah); do { /* * Don't process too many packets at a time; give the * TX thread time to also run - otherwise the TX * latency can jump by quite a bit, causing throughput * degredation. */ if (!kickpcu && npkts >= ATH_RX_MAX) break; bf = TAILQ_FIRST(&sc->sc_rxbuf); if (sc->sc_rxslink && bf == NULL) { /* NB: shouldn't happen */ device_printf(sc->sc_dev, "%s: no buffer!\n", __func__); break; } else if (bf == NULL) { /* * End of List: * this can happen for non-self-linked RX chains */ sc->sc_stats.ast_rx_hitqueueend++; break; } m = bf->bf_m; if (m == NULL) { /* NB: shouldn't happen */ /* * If mbuf allocation failed previously there * will be no mbuf; try again to re-populate it. */ /* XXX make debug msg */ device_printf(sc->sc_dev, "%s: no mbuf!\n", __func__); TAILQ_REMOVE(&sc->sc_rxbuf, bf, bf_list); goto rx_proc_next; } ds = bf->bf_desc; if (ds->ds_link == bf->bf_daddr) { /* NB: never process the self-linked entry at the end */ sc->sc_stats.ast_rx_hitqueueend++; break; } /* XXX sync descriptor memory */ /* * Must provide the virtual address of the current * descriptor, the physical address, and the virtual * address of the next descriptor in the h/w chain. * This allows the HAL to look ahead to see if the * hardware is done with a descriptor by checking the * done bit in the following descriptor and the address * of the current descriptor the DMA engine is working * on. All this is necessary because of our use of * a self-linked list to avoid rx overruns. */ rs = &bf->bf_status.ds_rxstat; status = ath_hal_rxprocdesc(ah, ds, bf->bf_daddr, PA2DESC(sc, ds->ds_link), rs); #ifdef ATH_DEBUG if (sc->sc_debug & ATH_DEBUG_RECV_DESC) ath_printrxbuf(sc, bf, 0, status == HAL_OK); #endif #ifdef ATH_DEBUG_ALQ if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_EDMA_RXSTATUS)) if_ath_alq_post(&sc->sc_alq, ATH_ALQ_EDMA_RXSTATUS, sc->sc_rx_statuslen, (char *) ds); #endif /* ATH_DEBUG_ALQ */ if (status == HAL_EINPROGRESS) break; TAILQ_REMOVE(&sc->sc_rxbuf, bf, bf_list); npkts++; /* * Process a single frame. */ bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); bf->bf_m = NULL; if (ath_rx_pkt(sc, rs, status, tsf, nf, HAL_RX_QUEUE_HP, bf, m)) ngood++; rx_proc_next: /* * If there's a holding buffer, insert that onto * the RX list; the hardware is now definitely not pointing * to it now. */ ret = 0; if (sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf != NULL) { TAILQ_INSERT_TAIL(&sc->sc_rxbuf, sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf, bf_list); ret = ath_rxbuf_init(sc, sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf); } /* * Next, throw our buffer into the holding entry. The hardware * may use the descriptor to read the link pointer before * DMAing the next descriptor in to write out a packet. */ sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf = bf; } while (ret == 0); /* rx signal state monitoring */ ath_hal_rxmonitor(ah, &sc->sc_halstats, sc->sc_curchan); if (ngood) sc->sc_lastrx = tsf; ATH_KTR(sc, ATH_KTR_RXPROC, 2, "ath_rx_proc: npkts=%d, ngood=%d", npkts, ngood); /* Queue DFS tasklet if needed */ if (resched && ath_dfs_tasklet_needed(sc, sc->sc_curchan)) taskqueue_enqueue(sc->sc_tq, &sc->sc_dfstask); /* * Now that all the RX frames were handled that * need to be handled, kick the PCU if there's * been an RXEOL condition. */ if (resched && kickpcu) { ATH_PCU_LOCK(sc); ATH_KTR(sc, ATH_KTR_ERROR, 0, "ath_rx_proc: kickpcu"); device_printf(sc->sc_dev, "%s: kickpcu; handled %d packets\n", __func__, npkts); /* * Go through the process of fully tearing down * the RX buffers and reinitialising them. * * There's a hardware bug that causes the RX FIFO * to get confused under certain conditions and * constantly write over the same frame, leading * the RX driver code here to get heavily confused. */ /* * XXX Has RX DMA stopped enough here to just call * ath_startrecv()? * XXX Do we need to use the holding buffer to restart * RX DMA by appending entries to the final * descriptor? Quite likely. */ #if 1 ath_startrecv(sc); #else /* * Disabled for now - it'd be nice to be able to do * this in order to limit the amount of CPU time spent * reinitialising the RX side (and thus minimise RX * drops) however there's a hardware issue that * causes things to get too far out of whack. */ /* * XXX can we hold the PCU lock here? * Are there any net80211 buffer calls involved? */ bf = TAILQ_FIRST(&sc->sc_rxbuf); ath_hal_putrxbuf(ah, bf->bf_daddr, HAL_RX_QUEUE_HP); ath_hal_rxena(ah); /* enable recv descriptors */ ath_mode_init(sc); /* set filters, etc. */ ath_hal_startpcurecv(ah); /* re-enable PCU/DMA engine */ #endif ath_hal_intrset(ah, sc->sc_imask); sc->sc_kickpcu = 0; ATH_PCU_UNLOCK(sc); } #ifdef IEEE80211_SUPPORT_SUPERG if (resched) ieee80211_ff_age_all(ic, 100); #endif /* * Put the hardware to sleep again if we're done with it. */ ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); /* * If we hit the maximum number of frames in this round, * reschedule for another immediate pass. This gives * the TX and TX completion routines time to run, which * will reduce latency. */ if (npkts >= ATH_RX_MAX) sc->sc_rx.recv_sched(sc, resched); ATH_PCU_LOCK(sc); sc->sc_rxproc_cnt--; ATH_PCU_UNLOCK(sc); } #undef PA2DESC #undef ATH_RX_MAX /* * Only run the RX proc if it's not already running. * Since this may get run as part of the reset/flush path, * the task can't clash with an existing, running tasklet. */ static void ath_legacy_rx_tasklet(void *arg, int npending) { struct ath_softc *sc = arg; ATH_KTR(sc, ATH_KTR_RXPROC, 1, "ath_rx_proc: pending=%d", npending); DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s: pending %u\n", __func__, npending); ATH_PCU_LOCK(sc); if (sc->sc_inreset_cnt > 0) { device_printf(sc->sc_dev, "%s: sc_inreset_cnt > 0; skipping\n", __func__); ATH_PCU_UNLOCK(sc); return; } ATH_PCU_UNLOCK(sc); ath_rx_proc(sc, 1); } static void ath_legacy_flushrecv(struct ath_softc *sc) { ath_rx_proc(sc, 0); } static void ath_legacy_flush_rxpending(struct ath_softc *sc) { /* XXX ATH_RX_LOCK_ASSERT(sc); */ if (sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending != NULL) { m_freem(sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending); sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending = NULL; } if (sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending != NULL) { m_freem(sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending); sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending = NULL; } } static int ath_legacy_flush_rxholdbf(struct ath_softc *sc) { struct ath_buf *bf; /* XXX ATH_RX_LOCK_ASSERT(sc); */ /* * If there are RX holding buffers, free them here and return * them to the list. * * XXX should just verify that bf->bf_m is NULL, as it must * be at this point! */ bf = sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf; if (bf != NULL) { if (bf->bf_m != NULL) m_freem(bf->bf_m); bf->bf_m = NULL; TAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list); (void) ath_rxbuf_init(sc, bf); } sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf = NULL; bf = sc->sc_rxedma[HAL_RX_QUEUE_LP].m_holdbf; if (bf != NULL) { if (bf->bf_m != NULL) m_freem(bf->bf_m); bf->bf_m = NULL; TAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list); (void) ath_rxbuf_init(sc, bf); } sc->sc_rxedma[HAL_RX_QUEUE_LP].m_holdbf = NULL; return (0); } /* * Disable the receive h/w in preparation for a reset. */ static void ath_legacy_stoprecv(struct ath_softc *sc, int dodelay) { #define PA2DESC(_sc, _pa) \ ((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \ ((_pa) - (_sc)->sc_rxdma.dd_desc_paddr))) struct ath_hal *ah = sc->sc_ah; ATH_RX_LOCK(sc); ath_hal_stoppcurecv(ah); /* disable PCU */ ath_hal_setrxfilter(ah, 0); /* clear recv filter */ ath_hal_stopdmarecv(ah); /* disable DMA engine */ /* * TODO: see if this particular DELAY() is required; it may be * masking some missing FIFO flush or DMA sync. */ #if 0 if (dodelay) #endif DELAY(3000); /* 3ms is long enough for 1 frame */ #ifdef ATH_DEBUG if (sc->sc_debug & (ATH_DEBUG_RESET | ATH_DEBUG_FATAL)) { struct ath_buf *bf; u_int ix; device_printf(sc->sc_dev, "%s: rx queue %p, link %p\n", __func__, (caddr_t)(uintptr_t) ath_hal_getrxbuf(ah, HAL_RX_QUEUE_HP), sc->sc_rxlink); ix = 0; TAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) { struct ath_desc *ds = bf->bf_desc; struct ath_rx_status *rs = &bf->bf_status.ds_rxstat; HAL_STATUS status = ath_hal_rxprocdesc(ah, ds, bf->bf_daddr, PA2DESC(sc, ds->ds_link), rs); if (status == HAL_OK || (sc->sc_debug & ATH_DEBUG_FATAL)) ath_printrxbuf(sc, bf, ix, status == HAL_OK); ix++; } } #endif (void) ath_legacy_flush_rxpending(sc); (void) ath_legacy_flush_rxholdbf(sc); sc->sc_rxlink = NULL; /* just in case */ ATH_RX_UNLOCK(sc); #undef PA2DESC } /* * XXX TODO: something was calling startrecv without calling * stoprecv. Let's figure out what/why. It was showing up * as a mbuf leak (rxpending) and ath_buf leak (holdbf.) */ /* * Enable the receive h/w following a reset. */ static int ath_legacy_startrecv(struct ath_softc *sc) { struct ath_hal *ah = sc->sc_ah; struct ath_buf *bf; ATH_RX_LOCK(sc); /* * XXX should verify these are already all NULL! */ sc->sc_rxlink = NULL; (void) ath_legacy_flush_rxpending(sc); (void) ath_legacy_flush_rxholdbf(sc); /* * Re-chain all of the buffers in the RX buffer list. */ TAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) { int error = ath_rxbuf_init(sc, bf); if (error != 0) { DPRINTF(sc, ATH_DEBUG_RECV, "%s: ath_rxbuf_init failed %d\n", __func__, error); return error; } } bf = TAILQ_FIRST(&sc->sc_rxbuf); ath_hal_putrxbuf(ah, bf->bf_daddr, HAL_RX_QUEUE_HP); ath_hal_rxena(ah); /* enable recv descriptors */ ath_mode_init(sc); /* set filters, etc. */ ath_hal_startpcurecv(ah); /* re-enable PCU/DMA engine */ ATH_RX_UNLOCK(sc); return 0; } static int ath_legacy_dma_rxsetup(struct ath_softc *sc) { int error; error = ath_descdma_setup(sc, &sc->sc_rxdma, &sc->sc_rxbuf, "rx", sizeof(struct ath_desc), ath_rxbuf, 1); if (error != 0) return (error); return (0); } static int ath_legacy_dma_rxteardown(struct ath_softc *sc) { if (sc->sc_rxdma.dd_desc_len != 0) ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf); return (0); } static void ath_legacy_recv_sched(struct ath_softc *sc, int dosched) { taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask); } static void ath_legacy_recv_sched_queue(struct ath_softc *sc, HAL_RX_QUEUE q, int dosched) { taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask); } void ath_recv_setup_legacy(struct ath_softc *sc) { /* Sensible legacy defaults */ /* * XXX this should be changed to properly support the * exact RX descriptor size for each HAL. */ sc->sc_rx_statuslen = sizeof(struct ath_desc); sc->sc_rx.recv_start = ath_legacy_startrecv; sc->sc_rx.recv_stop = ath_legacy_stoprecv; sc->sc_rx.recv_flush = ath_legacy_flushrecv; sc->sc_rx.recv_tasklet = ath_legacy_rx_tasklet; sc->sc_rx.recv_rxbuf_init = ath_legacy_rxbuf_init; sc->sc_rx.recv_setup = ath_legacy_dma_rxsetup; sc->sc_rx.recv_teardown = ath_legacy_dma_rxteardown; sc->sc_rx.recv_sched = ath_legacy_recv_sched; sc->sc_rx.recv_sched_queue = ath_legacy_recv_sched_queue; } Index: head/sys/dev/ath/if_ath_tx.c =================================================================== --- head/sys/dev/ath/if_ath_tx.c (revision 298938) +++ head/sys/dev/ath/if_ath_tx.c (revision 298939) @@ -1,6206 +1,6206 @@ /*- * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * Copyright (c) 2010-2012 Adrian Chadd, Xenion Pty Ltd * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include __FBSDID("$FreeBSD$"); /* * Driver for the Atheros Wireless LAN controller. * * This software is derived from work of Atsushi Onoe; his contribution * is greatly appreciated. */ #include "opt_inet.h" #include "opt_ath.h" #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef IEEE80211_SUPPORT_SUPERG #include #endif #ifdef IEEE80211_SUPPORT_TDMA #include #endif #include #include #ifdef INET #include #include #endif #include #include /* XXX for softled */ #include #include #ifdef ATH_TX99_DIAG #include #endif #include #include #include #ifdef ATH_DEBUG_ALQ #include #endif /* * How many retries to perform in software */ #define SWMAX_RETRIES 10 /* * What queue to throw the non-QoS TID traffic into */ #define ATH_NONQOS_TID_AC WME_AC_VO #if 0 static int ath_tx_node_is_asleep(struct ath_softc *sc, struct ath_node *an); #endif static int ath_tx_ampdu_pending(struct ath_softc *sc, struct ath_node *an, int tid); static int ath_tx_ampdu_running(struct ath_softc *sc, struct ath_node *an, int tid); static ieee80211_seq ath_tx_tid_seqno_assign(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf, struct mbuf *m0); static int ath_tx_action_frame_override_queue(struct ath_softc *sc, struct ieee80211_node *ni, struct mbuf *m0, int *tid); static struct ath_buf * ath_tx_retry_clone(struct ath_softc *sc, struct ath_node *an, struct ath_tid *tid, struct ath_buf *bf); #ifdef ATH_DEBUG_ALQ void ath_tx_alq_post(struct ath_softc *sc, struct ath_buf *bf_first) { struct ath_buf *bf; int i, n; const char *ds; /* XXX we should skip out early if debugging isn't enabled! */ bf = bf_first; while (bf != NULL) { /* XXX should ensure bf_nseg > 0! */ if (bf->bf_nseg == 0) break; n = ((bf->bf_nseg - 1) / sc->sc_tx_nmaps) + 1; for (i = 0, ds = (const char *) bf->bf_desc; i < n; i++, ds += sc->sc_tx_desclen) { if_ath_alq_post(&sc->sc_alq, ATH_ALQ_EDMA_TXDESC, sc->sc_tx_desclen, ds); } bf = bf->bf_next; } } #endif /* ATH_DEBUG_ALQ */ /* * Whether to use the 11n rate scenario functions or not */ static inline int ath_tx_is_11n(struct ath_softc *sc) { return ((sc->sc_ah->ah_magic == 0x20065416) || (sc->sc_ah->ah_magic == 0x19741014)); } /* * Obtain the current TID from the given frame. * * Non-QoS frames need to go into TID 16 (IEEE80211_NONQOS_TID.) * This has implications for which AC/priority the packet is placed * in. */ static int ath_tx_gettid(struct ath_softc *sc, const struct mbuf *m0) { const struct ieee80211_frame *wh; int pri = M_WME_GETAC(m0); wh = mtod(m0, const struct ieee80211_frame *); if (! IEEE80211_QOS_HAS_SEQ(wh)) return IEEE80211_NONQOS_TID; else return WME_AC_TO_TID(pri); } static void ath_tx_set_retry(struct ath_softc *sc, struct ath_buf *bf) { struct ieee80211_frame *wh; wh = mtod(bf->bf_m, struct ieee80211_frame *); /* Only update/resync if needed */ if (bf->bf_state.bfs_isretried == 0) { wh->i_fc[1] |= IEEE80211_FC1_RETRY; bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE); } bf->bf_state.bfs_isretried = 1; bf->bf_state.bfs_retries ++; } /* * Determine what the correct AC queue for the given frame * should be. * * This code assumes that the TIDs map consistently to * the underlying hardware (or software) ath_txq. * Since the sender may try to set an AC which is * arbitrary, non-QoS TIDs may end up being put on * completely different ACs. There's no way to put a * TID into multiple ath_txq's for scheduling, so * for now we override the AC/TXQ selection and set * non-QOS TID frames into the BE queue. * * This may be completely incorrect - specifically, * some management frames may end up out of order * compared to the QoS traffic they're controlling. * I'll look into this later. */ static int ath_tx_getac(struct ath_softc *sc, const struct mbuf *m0) { const struct ieee80211_frame *wh; int pri = M_WME_GETAC(m0); wh = mtod(m0, const struct ieee80211_frame *); if (IEEE80211_QOS_HAS_SEQ(wh)) return pri; return ATH_NONQOS_TID_AC; } void ath_txfrag_cleanup(struct ath_softc *sc, ath_bufhead *frags, struct ieee80211_node *ni) { struct ath_buf *bf, *next; ATH_TXBUF_LOCK_ASSERT(sc); TAILQ_FOREACH_SAFE(bf, frags, bf_list, next) { /* NB: bf assumed clean */ TAILQ_REMOVE(frags, bf, bf_list); ath_returnbuf_head(sc, bf); ieee80211_node_decref(ni); } } /* * Setup xmit of a fragmented frame. Allocate a buffer * for each frag and bump the node reference count to * reflect the held reference to be setup by ath_tx_start. */ int ath_txfrag_setup(struct ath_softc *sc, ath_bufhead *frags, struct mbuf *m0, struct ieee80211_node *ni) { struct mbuf *m; struct ath_buf *bf; ATH_TXBUF_LOCK(sc); for (m = m0->m_nextpkt; m != NULL; m = m->m_nextpkt) { /* XXX non-management? */ bf = _ath_getbuf_locked(sc, ATH_BUFTYPE_NORMAL); if (bf == NULL) { /* out of buffers, cleanup */ DPRINTF(sc, ATH_DEBUG_XMIT, "%s: no buffer?\n", __func__); ath_txfrag_cleanup(sc, frags, ni); break; } ieee80211_node_incref(ni); TAILQ_INSERT_TAIL(frags, bf, bf_list); } ATH_TXBUF_UNLOCK(sc); return !TAILQ_EMPTY(frags); } static int ath_tx_dmasetup(struct ath_softc *sc, struct ath_buf *bf, struct mbuf *m0) { struct mbuf *m; int error; /* * Load the DMA map so any coalescing is done. This * also calculates the number of descriptors we need. */ error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m0, bf->bf_segs, &bf->bf_nseg, BUS_DMA_NOWAIT); if (error == EFBIG) { /* XXX packet requires too many descriptors */ bf->bf_nseg = ATH_MAX_SCATTER + 1; } else if (error != 0) { sc->sc_stats.ast_tx_busdma++; ieee80211_free_mbuf(m0); return error; } /* * Discard null packets and check for packets that * require too many TX descriptors. We try to convert * the latter to a cluster. */ if (bf->bf_nseg > ATH_MAX_SCATTER) { /* too many desc's, linearize */ sc->sc_stats.ast_tx_linear++; m = m_collapse(m0, M_NOWAIT, ATH_MAX_SCATTER); if (m == NULL) { ieee80211_free_mbuf(m0); sc->sc_stats.ast_tx_nombuf++; return ENOMEM; } m0 = m; error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m0, bf->bf_segs, &bf->bf_nseg, BUS_DMA_NOWAIT); if (error != 0) { sc->sc_stats.ast_tx_busdma++; ieee80211_free_mbuf(m0); return error; } KASSERT(bf->bf_nseg <= ATH_MAX_SCATTER, ("too many segments after defrag; nseg %u", bf->bf_nseg)); } else if (bf->bf_nseg == 0) { /* null packet, discard */ sc->sc_stats.ast_tx_nodata++; ieee80211_free_mbuf(m0); return EIO; } DPRINTF(sc, ATH_DEBUG_XMIT, "%s: m %p len %u\n", __func__, m0, m0->m_pkthdr.len); bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE); bf->bf_m = m0; return 0; } /* * Chain together segments+descriptors for a frame - 11n or otherwise. * * For aggregates, this is called on each frame in the aggregate. */ static void ath_tx_chaindesclist(struct ath_softc *sc, struct ath_desc *ds0, struct ath_buf *bf, int is_aggr, int is_first_subframe, int is_last_subframe) { struct ath_hal *ah = sc->sc_ah; char *ds; int i, bp, dsp; HAL_DMA_ADDR bufAddrList[4]; uint32_t segLenList[4]; int numTxMaps = 1; int isFirstDesc = 1; /* * XXX There's txdma and txdma_mgmt; the descriptor * sizes must match. */ struct ath_descdma *dd = &sc->sc_txdma; /* * Fillin the remainder of the descriptor info. */ /* * We need the number of TX data pointers in each descriptor. * EDMA and later chips support 4 TX buffers per descriptor; * previous chips just support one. */ numTxMaps = sc->sc_tx_nmaps; /* * For EDMA and later chips ensure the TX map is fully populated * before advancing to the next descriptor. */ ds = (char *) bf->bf_desc; bp = dsp = 0; bzero(bufAddrList, sizeof(bufAddrList)); bzero(segLenList, sizeof(segLenList)); for (i = 0; i < bf->bf_nseg; i++) { bufAddrList[bp] = bf->bf_segs[i].ds_addr; segLenList[bp] = bf->bf_segs[i].ds_len; bp++; /* * Go to the next segment if this isn't the last segment * and there's space in the current TX map. */ if ((i != bf->bf_nseg - 1) && (bp < numTxMaps)) continue; /* * Last segment or we're out of buffer pointers. */ bp = 0; if (i == bf->bf_nseg - 1) ath_hal_settxdesclink(ah, (struct ath_desc *) ds, 0); else ath_hal_settxdesclink(ah, (struct ath_desc *) ds, bf->bf_daddr + dd->dd_descsize * (dsp + 1)); /* * XXX This assumes that bfs_txq is the actual destination * hardware queue at this point. It may not have been * assigned, it may actually be pointing to the multicast * software TXQ id. These must be fixed! */ ath_hal_filltxdesc(ah, (struct ath_desc *) ds , bufAddrList , segLenList , bf->bf_descid /* XXX desc id */ , bf->bf_state.bfs_tx_queue , isFirstDesc /* first segment */ , i == bf->bf_nseg - 1 /* last segment */ , (struct ath_desc *) ds0 /* first descriptor */ ); /* * Make sure the 11n aggregate fields are cleared. * * XXX TODO: this doesn't need to be called for * aggregate frames; as it'll be called on all * sub-frames. Since the descriptors are in * non-cacheable memory, this leads to some * rather slow writes on MIPS/ARM platforms. */ if (ath_tx_is_11n(sc)) ath_hal_clr11n_aggr(sc->sc_ah, (struct ath_desc *) ds); /* * If 11n is enabled, set it up as if it's an aggregate * frame. */ if (is_last_subframe) { ath_hal_set11n_aggr_last(sc->sc_ah, (struct ath_desc *) ds); } else if (is_aggr) { /* * This clears the aggrlen field; so * the caller needs to call set_aggr_first()! * * XXX TODO: don't call this for the first * descriptor in the first frame in an * aggregate! */ ath_hal_set11n_aggr_middle(sc->sc_ah, (struct ath_desc *) ds, bf->bf_state.bfs_ndelim); } isFirstDesc = 0; bf->bf_lastds = (struct ath_desc *) ds; /* * Don't forget to skip to the next descriptor. */ ds += sc->sc_tx_desclen; dsp++; /* * .. and don't forget to blank these out! */ bzero(bufAddrList, sizeof(bufAddrList)); bzero(segLenList, sizeof(segLenList)); } bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE); } /* * Set the rate control fields in the given descriptor based on * the bf_state fields and node state. * * The bfs fields should already be set with the relevant rate * control information, including whether MRR is to be enabled. * * Since the FreeBSD HAL currently sets up the first TX rate * in ath_hal_setuptxdesc(), this will setup the MRR * conditionally for the pre-11n chips, and call ath_buf_set_rate * unconditionally for 11n chips. These require the 11n rate * scenario to be set if MCS rates are enabled, so it's easier * to just always call it. The caller can then only set rates 2, 3 * and 4 if multi-rate retry is needed. */ static void ath_tx_set_ratectrl(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf) { struct ath_rc_series *rc = bf->bf_state.bfs_rc; /* If mrr is disabled, blank tries 1, 2, 3 */ if (! bf->bf_state.bfs_ismrr) rc[1].tries = rc[2].tries = rc[3].tries = 0; #if 0 /* * If NOACK is set, just set ntries=1. */ else if (bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) { rc[1].tries = rc[2].tries = rc[3].tries = 0; rc[0].tries = 1; } #endif /* * Always call - that way a retried descriptor will * have the MRR fields overwritten. * * XXX TODO: see if this is really needed - setting up * the first descriptor should set the MRR fields to 0 * for us anyway. */ if (ath_tx_is_11n(sc)) { ath_buf_set_rate(sc, ni, bf); } else { ath_hal_setupxtxdesc(sc->sc_ah, bf->bf_desc , rc[1].ratecode, rc[1].tries , rc[2].ratecode, rc[2].tries , rc[3].ratecode, rc[3].tries ); } } /* * Setup segments+descriptors for an 11n aggregate. * bf_first is the first buffer in the aggregate. * The descriptor list must already been linked together using * bf->bf_next. */ static void ath_tx_setds_11n(struct ath_softc *sc, struct ath_buf *bf_first) { struct ath_buf *bf, *bf_prev = NULL; struct ath_desc *ds0 = bf_first->bf_desc; DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: nframes=%d, al=%d\n", __func__, bf_first->bf_state.bfs_nframes, bf_first->bf_state.bfs_al); bf = bf_first; if (bf->bf_state.bfs_txrate0 == 0) DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: bf=%p, txrate0=%d\n", __func__, bf, 0); if (bf->bf_state.bfs_rc[0].ratecode == 0) DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: bf=%p, rix0=%d\n", __func__, bf, 0); /* * Setup all descriptors of all subframes - this will * call ath_hal_set11naggrmiddle() on every frame. */ while (bf != NULL) { DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: bf=%p, nseg=%d, pktlen=%d, seqno=%d\n", __func__, bf, bf->bf_nseg, bf->bf_state.bfs_pktlen, SEQNO(bf->bf_state.bfs_seqno)); /* * Setup the initial fields for the first descriptor - all * the non-11n specific stuff. */ ath_hal_setuptxdesc(sc->sc_ah, bf->bf_desc , bf->bf_state.bfs_pktlen /* packet length */ , bf->bf_state.bfs_hdrlen /* header length */ , bf->bf_state.bfs_atype /* Atheros packet type */ , bf->bf_state.bfs_txpower /* txpower */ , bf->bf_state.bfs_txrate0 , bf->bf_state.bfs_try0 /* series 0 rate/tries */ , bf->bf_state.bfs_keyix /* key cache index */ , bf->bf_state.bfs_txantenna /* antenna mode */ , bf->bf_state.bfs_txflags | HAL_TXDESC_INTREQ /* flags */ , bf->bf_state.bfs_ctsrate /* rts/cts rate */ , bf->bf_state.bfs_ctsduration /* rts/cts duration */ ); /* * First descriptor? Setup the rate control and initial * aggregate header information. */ if (bf == bf_first) { /* * setup first desc with rate and aggr info */ ath_tx_set_ratectrl(sc, bf->bf_node, bf); } /* * Setup the descriptors for a multi-descriptor frame. * This is both aggregate and non-aggregate aware. */ ath_tx_chaindesclist(sc, ds0, bf, 1, /* is_aggr */ !! (bf == bf_first), /* is_first_subframe */ !! (bf->bf_next == NULL) /* is_last_subframe */ ); if (bf == bf_first) { /* * Initialise the first 11n aggregate with the * aggregate length and aggregate enable bits. */ ath_hal_set11n_aggr_first(sc->sc_ah, ds0, bf->bf_state.bfs_al, bf->bf_state.bfs_ndelim); } /* * Link the last descriptor of the previous frame * to the beginning descriptor of this frame. */ if (bf_prev != NULL) ath_hal_settxdesclink(sc->sc_ah, bf_prev->bf_lastds, bf->bf_daddr); /* Save a copy so we can link the next descriptor in */ bf_prev = bf; bf = bf->bf_next; } /* * Set the first descriptor bf_lastds field to point to * the last descriptor in the last subframe, that's where * the status update will occur. */ bf_first->bf_lastds = bf_prev->bf_lastds; /* * And bf_last in the first descriptor points to the end of * the aggregate list. */ bf_first->bf_last = bf_prev; /* * For non-AR9300 NICs, which require the rate control * in the final descriptor - let's set that up now. * * This is because the filltxdesc() HAL call doesn't * populate the last segment with rate control information * if firstSeg is also true. For non-aggregate frames * that is fine, as the first frame already has rate control * info. But if the last frame in an aggregate has one * descriptor, both firstseg and lastseg will be true and * the rate info isn't copied. * * This is inefficient on MIPS/ARM platforms that have * non-cachable memory for TX descriptors, but we'll just * make do for now. * * As to why the rate table is stashed in the last descriptor * rather than the first descriptor? Because proctxdesc() * is called on the final descriptor in an MPDU or A-MPDU - * ie, the one that gets updated by the hardware upon * completion. That way proctxdesc() doesn't need to know * about the first _and_ last TX descriptor. */ ath_hal_setuplasttxdesc(sc->sc_ah, bf_prev->bf_lastds, ds0); DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: end\n", __func__); } /* * Hand-off a frame to the multicast TX queue. * * This is a software TXQ which will be appended to the CAB queue * during the beacon setup code. * * XXX TODO: since the AR9300 EDMA TX queue support wants the QCU ID * as part of the TX descriptor, bf_state.bfs_tx_queue must be updated * with the actual hardware txq, or all of this will fall apart. * * XXX It may not be a bad idea to just stuff the QCU ID into bf_state * and retire bfs_tx_queue; then make sure the CABQ QCU ID is populated * correctly. */ static void ath_tx_handoff_mcast(struct ath_softc *sc, struct ath_txq *txq, struct ath_buf *bf) { ATH_TX_LOCK_ASSERT(sc); KASSERT((bf->bf_flags & ATH_BUF_BUSY) == 0, ("%s: busy status 0x%x", __func__, bf->bf_flags)); /* * Ensure that the tx queue is the cabq, so things get * mapped correctly. */ if (bf->bf_state.bfs_tx_queue != sc->sc_cabq->axq_qnum) { DPRINTF(sc, ATH_DEBUG_XMIT, "%s: bf=%p, bfs_tx_queue=%d, axq_qnum=%d\n", __func__, bf, bf->bf_state.bfs_tx_queue, txq->axq_qnum); } ATH_TXQ_LOCK(txq); if (ATH_TXQ_LAST(txq, axq_q_s) != NULL) { struct ath_buf *bf_last = ATH_TXQ_LAST(txq, axq_q_s); struct ieee80211_frame *wh; /* mark previous frame */ wh = mtod(bf_last->bf_m, struct ieee80211_frame *); wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA; bus_dmamap_sync(sc->sc_dmat, bf_last->bf_dmamap, BUS_DMASYNC_PREWRITE); /* link descriptor */ ath_hal_settxdesclink(sc->sc_ah, bf_last->bf_lastds, bf->bf_daddr); } ATH_TXQ_INSERT_TAIL(txq, bf, bf_list); ATH_TXQ_UNLOCK(txq); } /* * Hand-off packet to a hardware queue. */ static void ath_tx_handoff_hw(struct ath_softc *sc, struct ath_txq *txq, struct ath_buf *bf) { struct ath_hal *ah = sc->sc_ah; struct ath_buf *bf_first; /* * Insert the frame on the outbound list and pass it on * to the hardware. Multicast frames buffered for power * save stations and transmit from the CAB queue are stored * on a s/w only queue and loaded on to the CAB queue in * the SWBA handler since frames only go out on DTIM and * to avoid possible races. */ ATH_TX_LOCK_ASSERT(sc); KASSERT((bf->bf_flags & ATH_BUF_BUSY) == 0, ("%s: busy status 0x%x", __func__, bf->bf_flags)); KASSERT(txq->axq_qnum != ATH_TXQ_SWQ, ("ath_tx_handoff_hw called for mcast queue")); /* * XXX We should instead just verify that sc_txstart_cnt * or ath_txproc_cnt > 0. That would mean that * the reset is going to be waiting for us to complete. */ if (sc->sc_txproc_cnt == 0 && sc->sc_txstart_cnt == 0) { device_printf(sc->sc_dev, "%s: TX dispatch without holding txcount/txstart refcnt!\n", __func__); } /* * XXX .. this is going to cause the hardware to get upset; * so we really should find some way to drop or queue * things. */ ATH_TXQ_LOCK(txq); /* * XXX TODO: if there's a holdingbf, then * ATH_TXQ_PUTRUNNING should be clear. * * If there is a holdingbf and the list is empty, * then axq_link should be pointing to the holdingbf. * * Otherwise it should point to the last descriptor * in the last ath_buf. * * In any case, we should really ensure that we * update the previous descriptor link pointer to * this descriptor, regardless of all of the above state. * * For now this is captured by having axq_link point * to either the holdingbf (if the TXQ list is empty) * or the end of the list (if the TXQ list isn't empty.) * I'd rather just kill axq_link here and do it as above. */ /* * Append the frame to the TX queue. */ ATH_TXQ_INSERT_TAIL(txq, bf, bf_list); ATH_KTR(sc, ATH_KTR_TX, 3, "ath_tx_handoff: non-tdma: txq=%u, add bf=%p " "depth=%d", txq->axq_qnum, bf, txq->axq_depth); /* * If there's a link pointer, update it. * * XXX we should replace this with the above logic, just * to kill axq_link with fire. */ if (txq->axq_link != NULL) { *txq->axq_link = bf->bf_daddr; DPRINTF(sc, ATH_DEBUG_XMIT, "%s: link[%u](%p)=%p (%p) depth %d\n", __func__, txq->axq_qnum, txq->axq_link, (caddr_t)bf->bf_daddr, bf->bf_desc, txq->axq_depth); ATH_KTR(sc, ATH_KTR_TX, 5, "ath_tx_handoff: non-tdma: link[%u](%p)=%p (%p) " "lastds=%d", txq->axq_qnum, txq->axq_link, (caddr_t)bf->bf_daddr, bf->bf_desc, bf->bf_lastds); } /* * If we've not pushed anything into the hardware yet, * push the head of the queue into the TxDP. * * Once we've started DMA, there's no guarantee that * updating the TxDP with a new value will actually work. * So we just don't do that - if we hit the end of the list, * we keep that buffer around (the "holding buffer") and * re-start DMA by updating the link pointer of _that_ * descriptor and then restart DMA. */ if (! (txq->axq_flags & ATH_TXQ_PUTRUNNING)) { bf_first = TAILQ_FIRST(&txq->axq_q); txq->axq_flags |= ATH_TXQ_PUTRUNNING; ath_hal_puttxbuf(ah, txq->axq_qnum, bf_first->bf_daddr); DPRINTF(sc, ATH_DEBUG_XMIT, "%s: TXDP[%u] = %p (%p) depth %d\n", __func__, txq->axq_qnum, (caddr_t)bf_first->bf_daddr, bf_first->bf_desc, txq->axq_depth); ATH_KTR(sc, ATH_KTR_TX, 5, "ath_tx_handoff: TXDP[%u] = %p (%p) " "lastds=%p depth %d", txq->axq_qnum, (caddr_t)bf_first->bf_daddr, bf_first->bf_desc, bf_first->bf_lastds, txq->axq_depth); } /* * Ensure that the bf TXQ matches this TXQ, so later * checking and holding buffer manipulation is sane. */ if (bf->bf_state.bfs_tx_queue != txq->axq_qnum) { DPRINTF(sc, ATH_DEBUG_XMIT, "%s: bf=%p, bfs_tx_queue=%d, axq_qnum=%d\n", __func__, bf, bf->bf_state.bfs_tx_queue, txq->axq_qnum); } /* * Track aggregate queue depth. */ if (bf->bf_state.bfs_aggr) txq->axq_aggr_depth++; /* * Update the link pointer. */ ath_hal_gettxdesclinkptr(ah, bf->bf_lastds, &txq->axq_link); /* * Start DMA. * * If we wrote a TxDP above, DMA will start from here. * * If DMA is running, it'll do nothing. * * If the DMA engine hit the end of the QCU list (ie LINK=NULL, * or VEOL) then it stops at the last transmitted write. * We then append a new frame by updating the link pointer * in that descriptor and then kick TxE here; it will re-read * that last descriptor and find the new descriptor to transmit. * * This is why we keep the holding descriptor around. */ ath_hal_txstart(ah, txq->axq_qnum); ATH_TXQ_UNLOCK(txq); ATH_KTR(sc, ATH_KTR_TX, 1, "ath_tx_handoff: txq=%u, txstart", txq->axq_qnum); } /* * Restart TX DMA for the given TXQ. * * This must be called whether the queue is empty or not. */ static void ath_legacy_tx_dma_restart(struct ath_softc *sc, struct ath_txq *txq) { struct ath_buf *bf, *bf_last; ATH_TXQ_LOCK_ASSERT(txq); /* XXX make this ATH_TXQ_FIRST */ bf = TAILQ_FIRST(&txq->axq_q); bf_last = ATH_TXQ_LAST(txq, axq_q_s); if (bf == NULL) return; DPRINTF(sc, ATH_DEBUG_RESET, "%s: Q%d: bf=%p, bf_last=%p, daddr=0x%08x\n", __func__, txq->axq_qnum, bf, bf_last, (uint32_t) bf->bf_daddr); #ifdef ATH_DEBUG if (sc->sc_debug & ATH_DEBUG_RESET) ath_tx_dump(sc, txq); #endif /* * This is called from a restart, so DMA is known to be * completely stopped. */ KASSERT((!(txq->axq_flags & ATH_TXQ_PUTRUNNING)), ("%s: Q%d: called with PUTRUNNING=1\n", __func__, txq->axq_qnum)); ath_hal_puttxbuf(sc->sc_ah, txq->axq_qnum, bf->bf_daddr); txq->axq_flags |= ATH_TXQ_PUTRUNNING; ath_hal_gettxdesclinkptr(sc->sc_ah, bf_last->bf_lastds, &txq->axq_link); ath_hal_txstart(sc->sc_ah, txq->axq_qnum); } /* * Hand off a packet to the hardware (or mcast queue.) * * The relevant hardware txq should be locked. */ static void ath_legacy_xmit_handoff(struct ath_softc *sc, struct ath_txq *txq, struct ath_buf *bf) { ATH_TX_LOCK_ASSERT(sc); #ifdef ATH_DEBUG_ALQ if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_EDMA_TXDESC)) ath_tx_alq_post(sc, bf); #endif if (txq->axq_qnum == ATH_TXQ_SWQ) ath_tx_handoff_mcast(sc, txq, bf); else ath_tx_handoff_hw(sc, txq, bf); } static int ath_tx_tag_crypto(struct ath_softc *sc, struct ieee80211_node *ni, struct mbuf *m0, int iswep, int isfrag, int *hdrlen, int *pktlen, int *keyix) { DPRINTF(sc, ATH_DEBUG_XMIT, "%s: hdrlen=%d, pktlen=%d, isfrag=%d, iswep=%d, m0=%p\n", __func__, *hdrlen, *pktlen, isfrag, iswep, m0); if (iswep) { const struct ieee80211_cipher *cip; struct ieee80211_key *k; /* * Construct the 802.11 header+trailer for an encrypted * frame. The only reason this can fail is because of an * unknown or unsupported cipher/key type. */ k = ieee80211_crypto_encap(ni, m0); if (k == NULL) { /* * This can happen when the key is yanked after the * frame was queued. Just discard the frame; the * 802.11 layer counts failures and provides * debugging/diagnostics. */ return (0); } /* * Adjust the packet + header lengths for the crypto * additions and calculate the h/w key index. When * a s/w mic is done the frame will have had any mic * added to it prior to entry so m0->m_pkthdr.len will * account for it. Otherwise we need to add it to the * packet length. */ cip = k->wk_cipher; (*hdrlen) += cip->ic_header; (*pktlen) += cip->ic_header + cip->ic_trailer; /* NB: frags always have any TKIP MIC done in s/w */ if ((k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && !isfrag) (*pktlen) += cip->ic_miclen; (*keyix) = k->wk_keyix; } else if (ni->ni_ucastkey.wk_cipher == &ieee80211_cipher_none) { /* * Use station key cache slot, if assigned. */ (*keyix) = ni->ni_ucastkey.wk_keyix; if ((*keyix) == IEEE80211_KEYIX_NONE) (*keyix) = HAL_TXKEYIX_INVALID; } else (*keyix) = HAL_TXKEYIX_INVALID; return (1); } /* * Calculate whether interoperability protection is required for * this frame. * * This requires the rate control information be filled in, * as the protection requirement depends upon the current * operating mode / PHY. */ static void ath_tx_calc_protection(struct ath_softc *sc, struct ath_buf *bf) { struct ieee80211_frame *wh; uint8_t rix; uint16_t flags; int shortPreamble; const HAL_RATE_TABLE *rt = sc->sc_currates; struct ieee80211com *ic = &sc->sc_ic; flags = bf->bf_state.bfs_txflags; rix = bf->bf_state.bfs_rc[0].rix; shortPreamble = bf->bf_state.bfs_shpream; wh = mtod(bf->bf_m, struct ieee80211_frame *); /* * If 802.11g protection is enabled, determine whether * to use RTS/CTS or just CTS. Note that this is only * done for OFDM unicast frames. */ if ((ic->ic_flags & IEEE80211_F_USEPROT) && rt->info[rix].phy == IEEE80211_T_OFDM && (flags & HAL_TXDESC_NOACK) == 0) { bf->bf_state.bfs_doprot = 1; /* XXX fragments must use CCK rates w/ protection */ if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) { flags |= HAL_TXDESC_RTSENA; } else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) { flags |= HAL_TXDESC_CTSENA; } /* * For frags it would be desirable to use the * highest CCK rate for RTS/CTS. But stations * farther away may detect it at a lower CCK rate * so use the configured protection rate instead * (for now). */ sc->sc_stats.ast_tx_protect++; } /* * If 11n protection is enabled and it's a HT frame, * enable RTS. * * XXX ic_htprotmode or ic_curhtprotmode? * XXX should it_htprotmode only matter if ic_curhtprotmode * XXX indicates it's not a HT pure environment? */ if ((ic->ic_htprotmode == IEEE80211_PROT_RTSCTS) && rt->info[rix].phy == IEEE80211_T_HT && (flags & HAL_TXDESC_NOACK) == 0) { flags |= HAL_TXDESC_RTSENA; sc->sc_stats.ast_tx_htprotect++; } bf->bf_state.bfs_txflags = flags; } /* * Update the frame duration given the currently selected rate. * * This also updates the frame duration value, so it will require * a DMA flush. */ static void ath_tx_calc_duration(struct ath_softc *sc, struct ath_buf *bf) { struct ieee80211_frame *wh; uint8_t rix; uint16_t flags; int shortPreamble; struct ath_hal *ah = sc->sc_ah; const HAL_RATE_TABLE *rt = sc->sc_currates; int isfrag = bf->bf_m->m_flags & M_FRAG; flags = bf->bf_state.bfs_txflags; rix = bf->bf_state.bfs_rc[0].rix; shortPreamble = bf->bf_state.bfs_shpream; wh = mtod(bf->bf_m, struct ieee80211_frame *); /* * Calculate duration. This logically belongs in the 802.11 * layer but it lacks sufficient information to calculate it. */ if ((flags & HAL_TXDESC_NOACK) == 0 && (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_CTL) { u_int16_t dur; if (shortPreamble) dur = rt->info[rix].spAckDuration; else dur = rt->info[rix].lpAckDuration; if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) { dur += dur; /* additional SIFS+ACK */ /* * Include the size of next fragment so NAV is * updated properly. The last fragment uses only * the ACK duration * * XXX TODO: ensure that the rate lookup for each * fragment is the same as the rate used by the * first fragment! */ dur += ath_hal_computetxtime(ah, rt, bf->bf_nextfraglen, rix, shortPreamble); } if (isfrag) { /* * Force hardware to use computed duration for next * fragment by disabling multi-rate retry which updates * duration based on the multi-rate duration table. */ bf->bf_state.bfs_ismrr = 0; bf->bf_state.bfs_try0 = ATH_TXMGTTRY; /* XXX update bfs_rc[0].try? */ } /* Update the duration field itself */ *(u_int16_t *)wh->i_dur = htole16(dur); } } static uint8_t ath_tx_get_rtscts_rate(struct ath_hal *ah, const HAL_RATE_TABLE *rt, int cix, int shortPreamble) { uint8_t ctsrate; /* * CTS transmit rate is derived from the transmit rate * by looking in the h/w rate table. We must also factor * in whether or not a short preamble is to be used. */ /* NB: cix is set above where RTS/CTS is enabled */ KASSERT(cix != 0xff, ("cix not setup")); ctsrate = rt->info[cix].rateCode; /* XXX this should only matter for legacy rates */ if (shortPreamble) ctsrate |= rt->info[cix].shortPreamble; return (ctsrate); } /* * Calculate the RTS/CTS duration for legacy frames. */ static int ath_tx_calc_ctsduration(struct ath_hal *ah, int rix, int cix, int shortPreamble, int pktlen, const HAL_RATE_TABLE *rt, int flags) { int ctsduration = 0; /* This mustn't be called for HT modes */ if (rt->info[cix].phy == IEEE80211_T_HT) { printf("%s: HT rate where it shouldn't be (0x%x)\n", __func__, rt->info[cix].rateCode); return (-1); } /* * Compute the transmit duration based on the frame * size and the size of an ACK frame. We call into the * HAL to do the computation since it depends on the * characteristics of the actual PHY being used. * * NB: CTS is assumed the same size as an ACK so we can * use the precalculated ACK durations. */ if (shortPreamble) { if (flags & HAL_TXDESC_RTSENA) /* SIFS + CTS */ ctsduration += rt->info[cix].spAckDuration; ctsduration += ath_hal_computetxtime(ah, rt, pktlen, rix, AH_TRUE); if ((flags & HAL_TXDESC_NOACK) == 0) /* SIFS + ACK */ ctsduration += rt->info[rix].spAckDuration; } else { if (flags & HAL_TXDESC_RTSENA) /* SIFS + CTS */ ctsduration += rt->info[cix].lpAckDuration; ctsduration += ath_hal_computetxtime(ah, rt, pktlen, rix, AH_FALSE); if ((flags & HAL_TXDESC_NOACK) == 0) /* SIFS + ACK */ ctsduration += rt->info[rix].lpAckDuration; } return (ctsduration); } /* * Update the given ath_buf with updated rts/cts setup and duration * values. * * To support rate lookups for each software retry, the rts/cts rate * and cts duration must be re-calculated. * * This function assumes the RTS/CTS flags have been set as needed; * mrr has been disabled; and the rate control lookup has been done. * * XXX TODO: MRR need only be disabled for the pre-11n NICs. * XXX The 11n NICs support per-rate RTS/CTS configuration. */ static void ath_tx_set_rtscts(struct ath_softc *sc, struct ath_buf *bf) { uint16_t ctsduration = 0; uint8_t ctsrate = 0; uint8_t rix = bf->bf_state.bfs_rc[0].rix; uint8_t cix = 0; const HAL_RATE_TABLE *rt = sc->sc_currates; /* * No RTS/CTS enabled? Don't bother. */ if ((bf->bf_state.bfs_txflags & (HAL_TXDESC_RTSENA | HAL_TXDESC_CTSENA)) == 0) { /* XXX is this really needed? */ bf->bf_state.bfs_ctsrate = 0; bf->bf_state.bfs_ctsduration = 0; return; } /* * If protection is enabled, use the protection rix control * rate. Otherwise use the rate0 control rate. */ if (bf->bf_state.bfs_doprot) rix = sc->sc_protrix; else rix = bf->bf_state.bfs_rc[0].rix; /* * If the raw path has hard-coded ctsrate0 to something, * use it. */ if (bf->bf_state.bfs_ctsrate0 != 0) cix = ath_tx_findrix(sc, bf->bf_state.bfs_ctsrate0); else /* Control rate from above */ cix = rt->info[rix].controlRate; /* Calculate the rtscts rate for the given cix */ ctsrate = ath_tx_get_rtscts_rate(sc->sc_ah, rt, cix, bf->bf_state.bfs_shpream); /* The 11n chipsets do ctsduration calculations for you */ if (! ath_tx_is_11n(sc)) ctsduration = ath_tx_calc_ctsduration(sc->sc_ah, rix, cix, bf->bf_state.bfs_shpream, bf->bf_state.bfs_pktlen, rt, bf->bf_state.bfs_txflags); /* Squirrel away in ath_buf */ bf->bf_state.bfs_ctsrate = ctsrate; bf->bf_state.bfs_ctsduration = ctsduration; /* * Must disable multi-rate retry when using RTS/CTS. */ if (!sc->sc_mrrprot) { bf->bf_state.bfs_ismrr = 0; bf->bf_state.bfs_try0 = bf->bf_state.bfs_rc[0].tries = ATH_TXMGTTRY; /* XXX ew */ } } /* * Setup the descriptor chain for a normal or fast-frame * frame. * * XXX TODO: extend to include the destination hardware QCU ID. * Make sure that is correct. Make sure that when being added * to the mcastq, the CABQ QCUID is set or things will get a bit * odd. */ static void ath_tx_setds(struct ath_softc *sc, struct ath_buf *bf) { struct ath_desc *ds = bf->bf_desc; struct ath_hal *ah = sc->sc_ah; if (bf->bf_state.bfs_txrate0 == 0) DPRINTF(sc, ATH_DEBUG_XMIT, "%s: bf=%p, txrate0=%d\n", __func__, bf, 0); ath_hal_setuptxdesc(ah, ds , bf->bf_state.bfs_pktlen /* packet length */ , bf->bf_state.bfs_hdrlen /* header length */ , bf->bf_state.bfs_atype /* Atheros packet type */ , bf->bf_state.bfs_txpower /* txpower */ , bf->bf_state.bfs_txrate0 , bf->bf_state.bfs_try0 /* series 0 rate/tries */ , bf->bf_state.bfs_keyix /* key cache index */ , bf->bf_state.bfs_txantenna /* antenna mode */ , bf->bf_state.bfs_txflags /* flags */ , bf->bf_state.bfs_ctsrate /* rts/cts rate */ , bf->bf_state.bfs_ctsduration /* rts/cts duration */ ); /* * This will be overriden when the descriptor chain is written. */ bf->bf_lastds = ds; bf->bf_last = bf; /* Set rate control and descriptor chain for this frame */ ath_tx_set_ratectrl(sc, bf->bf_node, bf); ath_tx_chaindesclist(sc, ds, bf, 0, 0, 0); } /* * Do a rate lookup. * * This performs a rate lookup for the given ath_buf only if it's required. * Non-data frames and raw frames don't require it. * * This populates the primary and MRR entries; MRR values are * then disabled later on if something requires it (eg RTS/CTS on * pre-11n chipsets. * * This needs to be done before the RTS/CTS fields are calculated * as they may depend upon the rate chosen. */ static void ath_tx_do_ratelookup(struct ath_softc *sc, struct ath_buf *bf) { uint8_t rate, rix; int try0; if (! bf->bf_state.bfs_doratelookup) return; /* Get rid of any previous state */ bzero(bf->bf_state.bfs_rc, sizeof(bf->bf_state.bfs_rc)); ATH_NODE_LOCK(ATH_NODE(bf->bf_node)); ath_rate_findrate(sc, ATH_NODE(bf->bf_node), bf->bf_state.bfs_shpream, bf->bf_state.bfs_pktlen, &rix, &try0, &rate); /* In case MRR is disabled, make sure rc[0] is setup correctly */ bf->bf_state.bfs_rc[0].rix = rix; bf->bf_state.bfs_rc[0].ratecode = rate; bf->bf_state.bfs_rc[0].tries = try0; if (bf->bf_state.bfs_ismrr && try0 != ATH_TXMAXTRY) ath_rate_getxtxrates(sc, ATH_NODE(bf->bf_node), rix, bf->bf_state.bfs_rc); ATH_NODE_UNLOCK(ATH_NODE(bf->bf_node)); sc->sc_txrix = rix; /* for LED blinking */ sc->sc_lastdatarix = rix; /* for fast frames */ bf->bf_state.bfs_try0 = try0; bf->bf_state.bfs_txrate0 = rate; } /* * Update the CLRDMASK bit in the ath_buf if it needs to be set. */ static void ath_tx_update_clrdmask(struct ath_softc *sc, struct ath_tid *tid, struct ath_buf *bf) { struct ath_node *an = ATH_NODE(bf->bf_node); ATH_TX_LOCK_ASSERT(sc); if (an->clrdmask == 1) { bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK; an->clrdmask = 0; } } /* * Return whether this frame should be software queued or * direct dispatched. * * When doing powersave, BAR frames should be queued but other management * frames should be directly sent. * * When not doing powersave, stick BAR frames into the hardware queue * so it goes out even though the queue is paused. * * For now, management frames are also software queued by default. */ static int ath_tx_should_swq_frame(struct ath_softc *sc, struct ath_node *an, struct mbuf *m0, int *queue_to_head) { struct ieee80211_node *ni = &an->an_node; struct ieee80211_frame *wh; uint8_t type, subtype; wh = mtod(m0, struct ieee80211_frame *); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; (*queue_to_head) = 0; /* If it's not in powersave - direct-dispatch BAR */ if ((ATH_NODE(ni)->an_is_powersave == 0) && type == IEEE80211_FC0_TYPE_CTL && subtype == IEEE80211_FC0_SUBTYPE_BAR) { DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: BAR: TX'ing direct\n", __func__); return (0); } else if ((ATH_NODE(ni)->an_is_powersave == 1) && type == IEEE80211_FC0_TYPE_CTL && subtype == IEEE80211_FC0_SUBTYPE_BAR) { /* BAR TX whilst asleep; queue */ DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: swq: TX'ing\n", __func__); (*queue_to_head) = 1; return (1); } else if ((ATH_NODE(ni)->an_is_powersave == 1) && (type == IEEE80211_FC0_TYPE_MGT || type == IEEE80211_FC0_TYPE_CTL)) { /* * Other control/mgmt frame; bypass software queuing * for now! */ DPRINTF(sc, ATH_DEBUG_XMIT, "%s: %6D: Node is asleep; sending mgmt " "(type=%d, subtype=%d)\n", __func__, ni->ni_macaddr, ":", type, subtype); return (0); } else { return (1); } } /* * Transmit the given frame to the hardware. * * The frame must already be setup; rate control must already have * been done. * * XXX since the TXQ lock is being held here (and I dislike holding * it for this long when not doing software aggregation), later on * break this function into "setup_normal" and "xmit_normal". The * lock only needs to be held for the ath_tx_handoff call. * * XXX we don't update the leak count here - if we're doing * direct frame dispatch, we need to be able to do it without * decrementing the leak count (eg multicast queue frames.) */ static void ath_tx_xmit_normal(struct ath_softc *sc, struct ath_txq *txq, struct ath_buf *bf) { struct ath_node *an = ATH_NODE(bf->bf_node); struct ath_tid *tid = &an->an_tid[bf->bf_state.bfs_tid]; ATH_TX_LOCK_ASSERT(sc); /* * For now, just enable CLRDMASK. ath_tx_xmit_normal() does * set a completion handler however it doesn't (yet) properly * handle the strict ordering requirements needed for normal, * non-aggregate session frames. * * Once this is implemented, only set CLRDMASK like this for * frames that must go out - eg management/raw frames. */ bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK; /* Setup the descriptor before handoff */ ath_tx_do_ratelookup(sc, bf); ath_tx_calc_duration(sc, bf); ath_tx_calc_protection(sc, bf); ath_tx_set_rtscts(sc, bf); ath_tx_rate_fill_rcflags(sc, bf); ath_tx_setds(sc, bf); /* Track per-TID hardware queue depth correctly */ tid->hwq_depth++; /* Assign the completion handler */ bf->bf_comp = ath_tx_normal_comp; /* Hand off to hardware */ ath_tx_handoff(sc, txq, bf); } /* * Do the basic frame setup stuff that's required before the frame * is added to a software queue. * * All frames get mostly the same treatment and it's done once. * Retransmits fiddle with things like the rate control setup, * setting the retransmit bit in the packet; doing relevant DMA/bus * syncing and relinking it (back) into the hardware TX queue. * * Note that this may cause the mbuf to be reallocated, so * m0 may not be valid. */ static int ath_tx_normal_setup(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf, struct mbuf *m0, struct ath_txq *txq) { struct ieee80211vap *vap = ni->ni_vap; struct ath_hal *ah = sc->sc_ah; struct ieee80211com *ic = &sc->sc_ic; const struct chanAccParams *cap = &ic->ic_wme.wme_chanParams; int error, iswep, ismcast, isfrag, ismrr; int keyix, hdrlen, pktlen, try0 = 0; u_int8_t rix = 0, txrate = 0; struct ath_desc *ds; struct ieee80211_frame *wh; u_int subtype, flags; HAL_PKT_TYPE atype; const HAL_RATE_TABLE *rt; HAL_BOOL shortPreamble; struct ath_node *an; u_int pri; /* * To ensure that both sequence numbers and the CCMP PN handling * is "correct", make sure that the relevant TID queue is locked. * Otherwise the CCMP PN and seqno may appear out of order, causing * re-ordered frames to have out of order CCMP PN's, resulting * in many, many frame drops. */ ATH_TX_LOCK_ASSERT(sc); wh = mtod(m0, struct ieee80211_frame *); iswep = wh->i_fc[1] & IEEE80211_FC1_PROTECTED; ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); isfrag = m0->m_flags & M_FRAG; hdrlen = ieee80211_anyhdrsize(wh); /* * Packet length must not include any * pad bytes; deduct them here. */ pktlen = m0->m_pkthdr.len - (hdrlen & 3); /* Handle encryption twiddling if needed */ if (! ath_tx_tag_crypto(sc, ni, m0, iswep, isfrag, &hdrlen, &pktlen, &keyix)) { ieee80211_free_mbuf(m0); return EIO; } /* packet header may have moved, reset our local pointer */ wh = mtod(m0, struct ieee80211_frame *); pktlen += IEEE80211_CRC_LEN; /* * Load the DMA map so any coalescing is done. This * also calculates the number of descriptors we need. */ error = ath_tx_dmasetup(sc, bf, m0); if (error != 0) return error; KASSERT((ni != NULL), ("%s: ni=NULL!", __func__)); bf->bf_node = ni; /* NB: held reference */ m0 = bf->bf_m; /* NB: may have changed */ wh = mtod(m0, struct ieee80211_frame *); /* setup descriptors */ ds = bf->bf_desc; rt = sc->sc_currates; KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode)); /* * NB: the 802.11 layer marks whether or not we should * use short preamble based on the current mode and * negotiated parameters. */ if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) { shortPreamble = AH_TRUE; sc->sc_stats.ast_tx_shortpre++; } else { shortPreamble = AH_FALSE; } an = ATH_NODE(ni); //flags = HAL_TXDESC_CLRDMASK; /* XXX needed for crypto errs */ flags = 0; ismrr = 0; /* default no multi-rate retry*/ pri = M_WME_GETAC(m0); /* honor classification */ /* XXX use txparams instead of fixed values */ /* * Calculate Atheros packet type from IEEE80211 packet header, * setup for rate calculations, and select h/w transmit queue. */ switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { case IEEE80211_FC0_TYPE_MGT: subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) atype = HAL_PKT_TYPE_BEACON; else if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) atype = HAL_PKT_TYPE_PROBE_RESP; else if (subtype == IEEE80211_FC0_SUBTYPE_ATIM) atype = HAL_PKT_TYPE_ATIM; else atype = HAL_PKT_TYPE_NORMAL; /* XXX */ rix = an->an_mgmtrix; txrate = rt->info[rix].rateCode; if (shortPreamble) txrate |= rt->info[rix].shortPreamble; try0 = ATH_TXMGTTRY; flags |= HAL_TXDESC_INTREQ; /* force interrupt */ break; case IEEE80211_FC0_TYPE_CTL: atype = HAL_PKT_TYPE_PSPOLL; /* stop setting of duration */ rix = an->an_mgmtrix; txrate = rt->info[rix].rateCode; if (shortPreamble) txrate |= rt->info[rix].shortPreamble; try0 = ATH_TXMGTTRY; flags |= HAL_TXDESC_INTREQ; /* force interrupt */ break; case IEEE80211_FC0_TYPE_DATA: atype = HAL_PKT_TYPE_NORMAL; /* default */ /* * Data frames: multicast frames go out at a fixed rate, * EAPOL frames use the mgmt frame rate; otherwise consult * the rate control module for the rate to use. */ if (ismcast) { rix = an->an_mcastrix; txrate = rt->info[rix].rateCode; if (shortPreamble) txrate |= rt->info[rix].shortPreamble; try0 = 1; } else if (m0->m_flags & M_EAPOL) { /* XXX? maybe always use long preamble? */ rix = an->an_mgmtrix; txrate = rt->info[rix].rateCode; if (shortPreamble) txrate |= rt->info[rix].shortPreamble; try0 = ATH_TXMAXTRY; /* XXX?too many? */ } else { /* * Do rate lookup on each TX, rather than using * the hard-coded TX information decided here. */ ismrr = 1; bf->bf_state.bfs_doratelookup = 1; } if (cap->cap_wmeParams[pri].wmep_noackPolicy) flags |= HAL_TXDESC_NOACK; break; default: device_printf(sc->sc_dev, "bogus frame type 0x%x (%s)\n", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK, __func__); /* XXX statistic */ /* XXX free tx dmamap */ ieee80211_free_mbuf(m0); return EIO; } /* * There are two known scenarios where the frame AC doesn't match * what the destination TXQ is. * * + non-QoS frames (eg management?) that the net80211 stack has * assigned a higher AC to, but since it's a non-QoS TID, it's * being thrown into TID 16. TID 16 gets the AC_BE queue. * It's quite possible that management frames should just be * direct dispatched to hardware rather than go via the software * queue; that should be investigated in the future. There are * some specific scenarios where this doesn't make sense, mostly * surrounding ADDBA request/response - hence why that is special * cased. * * + Multicast frames going into the VAP mcast queue. That shows up * as "TXQ 11". * * This driver should eventually support separate TID and TXQ locking, * allowing for arbitrary AC frames to appear on arbitrary software * queues, being queued to the "correct" hardware queue when needed. */ #if 0 if (txq != sc->sc_ac2q[pri]) { DPRINTF(sc, ATH_DEBUG_XMIT, "%s: txq=%p (%d), pri=%d, pri txq=%p (%d)\n", __func__, txq, txq->axq_qnum, pri, sc->sc_ac2q[pri], sc->sc_ac2q[pri]->axq_qnum); } #endif /* * Calculate miscellaneous flags. */ if (ismcast) { flags |= HAL_TXDESC_NOACK; /* no ack on broad/multicast */ } else if (pktlen > vap->iv_rtsthreshold && (ni->ni_ath_flags & IEEE80211_NODE_FF) == 0) { flags |= HAL_TXDESC_RTSENA; /* RTS based on frame length */ sc->sc_stats.ast_tx_rts++; } if (flags & HAL_TXDESC_NOACK) /* NB: avoid double counting */ sc->sc_stats.ast_tx_noack++; #ifdef IEEE80211_SUPPORT_TDMA if (sc->sc_tdma && (flags & HAL_TXDESC_NOACK) == 0) { DPRINTF(sc, ATH_DEBUG_TDMA, "%s: discard frame, ACK required w/ TDMA\n", __func__); sc->sc_stats.ast_tdma_ack++; /* XXX free tx dmamap */ ieee80211_free_mbuf(m0); return EIO; } #endif /* * Determine if a tx interrupt should be generated for * this descriptor. We take a tx interrupt to reap * descriptors when the h/w hits an EOL condition or * when the descriptor is specifically marked to generate * an interrupt. We periodically mark descriptors in this * way to insure timely replenishing of the supply needed * for sending frames. Defering interrupts reduces system * load and potentially allows more concurrent work to be * done but if done to aggressively can cause senders to * backup. * * NB: use >= to deal with sc_txintrperiod changing * dynamically through sysctl. */ if (flags & HAL_TXDESC_INTREQ) { txq->axq_intrcnt = 0; } else if (++txq->axq_intrcnt >= sc->sc_txintrperiod) { flags |= HAL_TXDESC_INTREQ; txq->axq_intrcnt = 0; } /* This point forward is actual TX bits */ /* * At this point we are committed to sending the frame * and we don't need to look at m_nextpkt; clear it in * case this frame is part of frag chain. */ m0->m_nextpkt = NULL; if (IFF_DUMPPKTS(sc, ATH_DEBUG_XMIT)) ieee80211_dump_pkt(ic, mtod(m0, const uint8_t *), m0->m_len, sc->sc_hwmap[rix].ieeerate, -1); if (ieee80211_radiotap_active_vap(vap)) { u_int64_t tsf = ath_hal_gettsf64(ah); sc->sc_tx_th.wt_tsf = htole64(tsf); sc->sc_tx_th.wt_flags = sc->sc_hwmap[rix].txflags; if (iswep) sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_WEP; if (isfrag) sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_FRAG; sc->sc_tx_th.wt_rate = sc->sc_hwmap[rix].ieeerate; sc->sc_tx_th.wt_txpower = ieee80211_get_node_txpower(ni); sc->sc_tx_th.wt_antenna = sc->sc_txantenna; ieee80211_radiotap_tx(vap, m0); } /* Blank the legacy rate array */ bzero(&bf->bf_state.bfs_rc, sizeof(bf->bf_state.bfs_rc)); /* * ath_buf_set_rate needs at least one rate/try to setup * the rate scenario. */ bf->bf_state.bfs_rc[0].rix = rix; bf->bf_state.bfs_rc[0].tries = try0; bf->bf_state.bfs_rc[0].ratecode = txrate; /* Store the decided rate index values away */ bf->bf_state.bfs_pktlen = pktlen; bf->bf_state.bfs_hdrlen = hdrlen; bf->bf_state.bfs_atype = atype; bf->bf_state.bfs_txpower = ieee80211_get_node_txpower(ni); bf->bf_state.bfs_txrate0 = txrate; bf->bf_state.bfs_try0 = try0; bf->bf_state.bfs_keyix = keyix; bf->bf_state.bfs_txantenna = sc->sc_txantenna; bf->bf_state.bfs_txflags = flags; bf->bf_state.bfs_shpream = shortPreamble; /* XXX this should be done in ath_tx_setrate() */ bf->bf_state.bfs_ctsrate0 = 0; /* ie, no hard-coded ctsrate */ bf->bf_state.bfs_ctsrate = 0; /* calculated later */ bf->bf_state.bfs_ctsduration = 0; bf->bf_state.bfs_ismrr = ismrr; return 0; } /* * Queue a frame to the hardware or software queue. * * This can be called by the net80211 code. * * XXX what about locking? Or, push the seqno assign into the * XXX aggregate scheduler so its serialised? * * XXX When sending management frames via ath_raw_xmit(), * should CLRDMASK be set unconditionally? */ int ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf, struct mbuf *m0) { struct ieee80211vap *vap = ni->ni_vap; struct ath_vap *avp = ATH_VAP(vap); int r = 0; u_int pri; int tid; struct ath_txq *txq; int ismcast; const struct ieee80211_frame *wh; int is_ampdu, is_ampdu_tx, is_ampdu_pending; ieee80211_seq seqno; uint8_t type, subtype; int queue_to_head; ATH_TX_LOCK_ASSERT(sc); /* * Determine the target hardware queue. * * For multicast frames, the txq gets overridden appropriately * depending upon the state of PS. * * For any other frame, we do a TID/QoS lookup inside the frame * to see what the TID should be. If it's a non-QoS frame, the * AC and TID are overridden. The TID/TXQ code assumes the * TID is on a predictable hardware TXQ, so we don't support * having a node TID queued to multiple hardware TXQs. * This may change in the future but would require some locking * fudgery. */ pri = ath_tx_getac(sc, m0); tid = ath_tx_gettid(sc, m0); txq = sc->sc_ac2q[pri]; wh = mtod(m0, struct ieee80211_frame *); ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; /* * Enforce how deep the multicast queue can grow. * * XXX duplicated in ath_raw_xmit(). */ if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { if (sc->sc_cabq->axq_depth + sc->sc_cabq->fifo.axq_depth > sc->sc_txq_mcastq_maxdepth) { sc->sc_stats.ast_tx_mcastq_overflow++; m_freem(m0); return (ENOBUFS); } } /* * Enforce how deep the unicast queue can grow. * * If the node is in power save then we don't want * the software queue to grow too deep, or a node may * end up consuming all of the ath_buf entries. * * For now, only do this for DATA frames. * * We will want to cap how many management/control * frames get punted to the software queue so it doesn't * fill up. But the correct solution isn't yet obvious. * In any case, this check should at least let frames pass * that we are direct-dispatching. * * XXX TODO: duplicate this to the raw xmit path! */ if (type == IEEE80211_FC0_TYPE_DATA && ATH_NODE(ni)->an_is_powersave && ATH_NODE(ni)->an_swq_depth > sc->sc_txq_node_psq_maxdepth) { sc->sc_stats.ast_tx_node_psq_overflow++; m_freem(m0); return (ENOBUFS); } /* A-MPDU TX */ is_ampdu_tx = ath_tx_ampdu_running(sc, ATH_NODE(ni), tid); is_ampdu_pending = ath_tx_ampdu_pending(sc, ATH_NODE(ni), tid); is_ampdu = is_ampdu_tx | is_ampdu_pending; DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: tid=%d, ac=%d, is_ampdu=%d\n", __func__, tid, pri, is_ampdu); /* Set local packet state, used to queue packets to hardware */ bf->bf_state.bfs_tid = tid; bf->bf_state.bfs_tx_queue = txq->axq_qnum; bf->bf_state.bfs_pri = pri; #if 1 /* * When servicing one or more stations in power-save mode * (or) if there is some mcast data waiting on the mcast * queue (to prevent out of order delivery) multicast frames * must be bufferd until after the beacon. * * TODO: we should lock the mcastq before we check the length. */ if (sc->sc_cabq_enable && ismcast && (vap->iv_ps_sta || avp->av_mcastq.axq_depth)) { txq = &avp->av_mcastq; /* * Mark the frame as eventually belonging on the CAB * queue, so the descriptor setup functions will * correctly initialise the descriptor 'qcuId' field. */ bf->bf_state.bfs_tx_queue = sc->sc_cabq->axq_qnum; } #endif /* Do the generic frame setup */ /* XXX should just bzero the bf_state? */ bf->bf_state.bfs_dobaw = 0; /* A-MPDU TX? Manually set sequence number */ /* * Don't do it whilst pending; the net80211 layer still * assigns them. */ if (is_ampdu_tx) { /* * Always call; this function will * handle making sure that null data frames * don't get a sequence number from the current * TID and thus mess with the BAW. */ seqno = ath_tx_tid_seqno_assign(sc, ni, bf, m0); /* * Don't add QoS NULL frames to the BAW. */ if (IEEE80211_QOS_HAS_SEQ(wh) && subtype != IEEE80211_FC0_SUBTYPE_QOS_NULL) { bf->bf_state.bfs_dobaw = 1; } } /* * If needed, the sequence number has been assigned. * Squirrel it away somewhere easy to get to. */ bf->bf_state.bfs_seqno = M_SEQNO_GET(m0) << IEEE80211_SEQ_SEQ_SHIFT; /* Is ampdu pending? fetch the seqno and print it out */ if (is_ampdu_pending) DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: tid %d: ampdu pending, seqno %d\n", __func__, tid, M_SEQNO_GET(m0)); /* This also sets up the DMA map */ r = ath_tx_normal_setup(sc, ni, bf, m0, txq); if (r != 0) goto done; /* At this point m0 could have changed! */ m0 = bf->bf_m; #if 1 /* * If it's a multicast frame, do a direct-dispatch to the * destination hardware queue. Don't bother software * queuing it. */ /* * If it's a BAR frame, do a direct dispatch to the * destination hardware queue. Don't bother software * queuing it, as the TID will now be paused. * Sending a BAR frame can occur from the net80211 txa timer * (ie, retries) or from the ath txtask (completion call.) * It queues directly to hardware because the TID is paused * at this point (and won't be unpaused until the BAR has * either been TXed successfully or max retries has been * reached.) */ /* * Until things are better debugged - if this node is asleep * and we're sending it a non-BAR frame, direct dispatch it. * Why? Because we need to figure out what's actually being * sent - eg, during reassociation/reauthentication after * the node (last) disappeared whilst asleep, the driver should * have unpaused/unsleep'ed the node. So until that is * sorted out, use this workaround. */ if (txq == &avp->av_mcastq) { DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: bf=%p: mcastq: TX'ing\n", __func__, bf); bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK; ath_tx_xmit_normal(sc, txq, bf); } else if (ath_tx_should_swq_frame(sc, ATH_NODE(ni), m0, &queue_to_head)) { ath_tx_swq(sc, ni, txq, queue_to_head, bf); } else { bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK; ath_tx_xmit_normal(sc, txq, bf); } #else /* * For now, since there's no software queue, * direct-dispatch to the hardware. */ bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK; /* * Update the current leak count if * we're leaking frames; and set the * MORE flag as appropriate. */ ath_tx_leak_count_update(sc, tid, bf); ath_tx_xmit_normal(sc, txq, bf); #endif done: return 0; } static int ath_tx_raw_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf, struct mbuf *m0, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = sc->sc_ah; struct ieee80211vap *vap = ni->ni_vap; int error, ismcast, ismrr; int keyix, hdrlen, pktlen, try0, txantenna; u_int8_t rix, txrate; struct ieee80211_frame *wh; u_int flags; HAL_PKT_TYPE atype; const HAL_RATE_TABLE *rt; struct ath_desc *ds; u_int pri; int o_tid = -1; int do_override; uint8_t type, subtype; int queue_to_head; struct ath_node *an = ATH_NODE(ni); ATH_TX_LOCK_ASSERT(sc); wh = mtod(m0, struct ieee80211_frame *); ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); hdrlen = ieee80211_anyhdrsize(wh); /* * Packet length must not include any * pad bytes; deduct them here. */ /* XXX honor IEEE80211_BPF_DATAPAD */ pktlen = m0->m_pkthdr.len - (hdrlen & 3) + IEEE80211_CRC_LEN; type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; ATH_KTR(sc, ATH_KTR_TX, 2, "ath_tx_raw_start: ni=%p, bf=%p, raw", ni, bf); DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: ismcast=%d\n", __func__, ismcast); pri = params->ibp_pri & 3; /* Override pri if the frame isn't a QoS one */ if (! IEEE80211_QOS_HAS_SEQ(wh)) pri = ath_tx_getac(sc, m0); /* XXX If it's an ADDBA, override the correct queue */ do_override = ath_tx_action_frame_override_queue(sc, ni, m0, &o_tid); /* Map ADDBA to the correct priority */ if (do_override) { #if 0 DPRINTF(sc, ATH_DEBUG_XMIT, "%s: overriding tid %d pri %d -> %d\n", __func__, o_tid, pri, TID_TO_WME_AC(o_tid)); #endif pri = TID_TO_WME_AC(o_tid); } /* Handle encryption twiddling if needed */ if (! ath_tx_tag_crypto(sc, ni, m0, params->ibp_flags & IEEE80211_BPF_CRYPTO, 0, &hdrlen, &pktlen, &keyix)) { ieee80211_free_mbuf(m0); return EIO; } /* packet header may have moved, reset our local pointer */ wh = mtod(m0, struct ieee80211_frame *); /* Do the generic frame setup */ /* XXX should just bzero the bf_state? */ bf->bf_state.bfs_dobaw = 0; error = ath_tx_dmasetup(sc, bf, m0); if (error != 0) return error; m0 = bf->bf_m; /* NB: may have changed */ wh = mtod(m0, struct ieee80211_frame *); KASSERT((ni != NULL), ("%s: ni=NULL!", __func__)); bf->bf_node = ni; /* NB: held reference */ /* Always enable CLRDMASK for raw frames for now.. */ flags = HAL_TXDESC_CLRDMASK; /* XXX needed for crypto errs */ flags |= HAL_TXDESC_INTREQ; /* force interrupt */ if (params->ibp_flags & IEEE80211_BPF_RTS) flags |= HAL_TXDESC_RTSENA; else if (params->ibp_flags & IEEE80211_BPF_CTS) { /* XXX assume 11g/11n protection? */ bf->bf_state.bfs_doprot = 1; flags |= HAL_TXDESC_CTSENA; } /* XXX leave ismcast to injector? */ if ((params->ibp_flags & IEEE80211_BPF_NOACK) || ismcast) flags |= HAL_TXDESC_NOACK; rt = sc->sc_currates; KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode)); /* Fetch first rate information */ rix = ath_tx_findrix(sc, params->ibp_rate0); try0 = params->ibp_try0; /* * Override EAPOL rate as appropriate. */ if (m0->m_flags & M_EAPOL) { /* XXX? maybe always use long preamble? */ rix = an->an_mgmtrix; try0 = ATH_TXMAXTRY; /* XXX?too many? */ } txrate = rt->info[rix].rateCode; if (params->ibp_flags & IEEE80211_BPF_SHORTPRE) txrate |= rt->info[rix].shortPreamble; sc->sc_txrix = rix; ismrr = (params->ibp_try1 != 0); txantenna = params->ibp_pri >> 2; if (txantenna == 0) /* XXX? */ txantenna = sc->sc_txantenna; /* * Since ctsrate is fixed, store it away for later * use when the descriptor fields are being set. */ if (flags & (HAL_TXDESC_RTSENA|HAL_TXDESC_CTSENA)) bf->bf_state.bfs_ctsrate0 = params->ibp_ctsrate; /* * NB: we mark all packets as type PSPOLL so the h/w won't * set the sequence number, duration, etc. */ atype = HAL_PKT_TYPE_PSPOLL; if (IFF_DUMPPKTS(sc, ATH_DEBUG_XMIT)) ieee80211_dump_pkt(ic, mtod(m0, caddr_t), m0->m_len, sc->sc_hwmap[rix].ieeerate, -1); if (ieee80211_radiotap_active_vap(vap)) { u_int64_t tsf = ath_hal_gettsf64(ah); sc->sc_tx_th.wt_tsf = htole64(tsf); sc->sc_tx_th.wt_flags = sc->sc_hwmap[rix].txflags; if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_WEP; if (m0->m_flags & M_FRAG) sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_FRAG; sc->sc_tx_th.wt_rate = sc->sc_hwmap[rix].ieeerate; sc->sc_tx_th.wt_txpower = MIN(params->ibp_power, ieee80211_get_node_txpower(ni)); sc->sc_tx_th.wt_antenna = sc->sc_txantenna; ieee80211_radiotap_tx(vap, m0); } /* * Formulate first tx descriptor with tx controls. */ ds = bf->bf_desc; /* XXX check return value? */ /* Store the decided rate index values away */ bf->bf_state.bfs_pktlen = pktlen; bf->bf_state.bfs_hdrlen = hdrlen; bf->bf_state.bfs_atype = atype; bf->bf_state.bfs_txpower = MIN(params->ibp_power, ieee80211_get_node_txpower(ni)); bf->bf_state.bfs_txrate0 = txrate; bf->bf_state.bfs_try0 = try0; bf->bf_state.bfs_keyix = keyix; bf->bf_state.bfs_txantenna = txantenna; bf->bf_state.bfs_txflags = flags; bf->bf_state.bfs_shpream = !! (params->ibp_flags & IEEE80211_BPF_SHORTPRE); /* Set local packet state, used to queue packets to hardware */ bf->bf_state.bfs_tid = WME_AC_TO_TID(pri); bf->bf_state.bfs_tx_queue = sc->sc_ac2q[pri]->axq_qnum; bf->bf_state.bfs_pri = pri; /* XXX this should be done in ath_tx_setrate() */ bf->bf_state.bfs_ctsrate = 0; bf->bf_state.bfs_ctsduration = 0; bf->bf_state.bfs_ismrr = ismrr; /* Blank the legacy rate array */ bzero(&bf->bf_state.bfs_rc, sizeof(bf->bf_state.bfs_rc)); bf->bf_state.bfs_rc[0].rix = rix; bf->bf_state.bfs_rc[0].tries = try0; bf->bf_state.bfs_rc[0].ratecode = txrate; if (ismrr) { int rix; rix = ath_tx_findrix(sc, params->ibp_rate1); bf->bf_state.bfs_rc[1].rix = rix; bf->bf_state.bfs_rc[1].tries = params->ibp_try1; rix = ath_tx_findrix(sc, params->ibp_rate2); bf->bf_state.bfs_rc[2].rix = rix; bf->bf_state.bfs_rc[2].tries = params->ibp_try2; rix = ath_tx_findrix(sc, params->ibp_rate3); bf->bf_state.bfs_rc[3].rix = rix; bf->bf_state.bfs_rc[3].tries = params->ibp_try3; } /* * All the required rate control decisions have been made; * fill in the rc flags. */ ath_tx_rate_fill_rcflags(sc, bf); /* NB: no buffered multicast in power save support */ /* * If we're overiding the ADDBA destination, dump directly * into the hardware queue, right after any pending * frames to that node are. */ DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: dooverride=%d\n", __func__, do_override); #if 1 /* * Put addba frames in the right place in the right TID/HWQ. */ if (do_override) { bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK; /* * XXX if it's addba frames, should we be leaking * them out via the frame leak method? * XXX for now let's not risk it; but we may wish * to investigate this later. */ ath_tx_xmit_normal(sc, sc->sc_ac2q[pri], bf); } else if (ath_tx_should_swq_frame(sc, ATH_NODE(ni), m0, &queue_to_head)) { /* Queue to software queue */ ath_tx_swq(sc, ni, sc->sc_ac2q[pri], queue_to_head, bf); } else { bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK; ath_tx_xmit_normal(sc, sc->sc_ac2q[pri], bf); } #else /* Direct-dispatch to the hardware */ bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK; /* * Update the current leak count if * we're leaking frames; and set the * MORE flag as appropriate. */ ath_tx_leak_count_update(sc, tid, bf); ath_tx_xmit_normal(sc, sc->sc_ac2q[pri], bf); #endif return 0; } /* * Send a raw frame. * * This can be called by net80211. */ int ath_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct ath_softc *sc = ic->ic_softc; struct ath_buf *bf; struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); int error = 0; ATH_PCU_LOCK(sc); if (sc->sc_inreset_cnt > 0) { DPRINTF(sc, ATH_DEBUG_XMIT, "%s: sc_inreset_cnt > 0; bailing\n", __func__); error = EIO; ATH_PCU_UNLOCK(sc); goto badbad; } sc->sc_txstart_cnt++; ATH_PCU_UNLOCK(sc); /* Wake the hardware up already */ ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); ATH_TX_LOCK(sc); if (!sc->sc_running || sc->sc_invalid) { DPRINTF(sc, ATH_DEBUG_XMIT, "%s: discard frame, r/i: %d/%d", __func__, sc->sc_running, sc->sc_invalid); m_freem(m); error = ENETDOWN; goto bad; } /* * Enforce how deep the multicast queue can grow. * * XXX duplicated in ath_tx_start(). */ if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { if (sc->sc_cabq->axq_depth + sc->sc_cabq->fifo.axq_depth > sc->sc_txq_mcastq_maxdepth) { sc->sc_stats.ast_tx_mcastq_overflow++; error = ENOBUFS; } if (error != 0) { m_freem(m); goto bad; } } /* * Grab a TX buffer and associated resources. */ bf = ath_getbuf(sc, ATH_BUFTYPE_MGMT); if (bf == NULL) { sc->sc_stats.ast_tx_nobuf++; m_freem(m); error = ENOBUFS; goto bad; } ATH_KTR(sc, ATH_KTR_TX, 3, "ath_raw_xmit: m=%p, params=%p, bf=%p\n", m, params, bf); if (params == NULL) { /* * Legacy path; interpret frame contents to decide * precisely how to send the frame. */ if (ath_tx_start(sc, ni, bf, m)) { error = EIO; /* XXX */ goto bad2; } } else { /* * Caller supplied explicit parameters to use in * sending the frame. */ if (ath_tx_raw_start(sc, ni, bf, m, params)) { error = EIO; /* XXX */ goto bad2; } } sc->sc_wd_timer = 5; sc->sc_stats.ast_tx_raw++; /* * Update the TIM - if there's anything queued to the * software queue and power save is enabled, we should * set the TIM. */ ath_tx_update_tim(sc, ni, 1); ATH_TX_UNLOCK(sc); ATH_PCU_LOCK(sc); sc->sc_txstart_cnt--; ATH_PCU_UNLOCK(sc); /* Put the hardware back to sleep if required */ ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); return 0; bad2: ATH_KTR(sc, ATH_KTR_TX, 3, "ath_raw_xmit: bad2: m=%p, params=%p, " "bf=%p", m, params, bf); ATH_TXBUF_LOCK(sc); ath_returnbuf_head(sc, bf); ATH_TXBUF_UNLOCK(sc); bad: ATH_TX_UNLOCK(sc); ATH_PCU_LOCK(sc); sc->sc_txstart_cnt--; ATH_PCU_UNLOCK(sc); /* Put the hardware back to sleep if required */ ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); badbad: ATH_KTR(sc, ATH_KTR_TX, 2, "ath_raw_xmit: bad0: m=%p, params=%p", m, params); sc->sc_stats.ast_tx_raw_fail++; return error; } /* Some helper functions */ /* * ADDBA (and potentially others) need to be placed in the same * hardware queue as the TID/node it's relating to. This is so * it goes out after any pending non-aggregate frames to the * same node/TID. * * If this isn't done, the ADDBA can go out before the frames * queued in hardware. Even though these frames have a sequence * number -earlier- than the ADDBA can be transmitted (but * no frames whose sequence numbers are after the ADDBA should * be!) they'll arrive after the ADDBA - and the receiving end * will simply drop them as being out of the BAW. * * The frames can't be appended to the TID software queue - it'll * never be sent out. So these frames have to be directly * dispatched to the hardware, rather than queued in software. * So if this function returns true, the TXQ has to be * overridden and it has to be directly dispatched. * * It's a dirty hack, but someone's gotta do it. */ /* * XXX doesn't belong here! */ static int ieee80211_is_action(struct ieee80211_frame *wh) { /* Type: Management frame? */ if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT) return 0; /* Subtype: Action frame? */ if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) != IEEE80211_FC0_SUBTYPE_ACTION) return 0; return 1; } #define MS(_v, _f) (((_v) & _f) >> _f##_S) /* * Return an alternate TID for ADDBA request frames. * * Yes, this likely should be done in the net80211 layer. */ static int ath_tx_action_frame_override_queue(struct ath_softc *sc, struct ieee80211_node *ni, struct mbuf *m0, int *tid) { struct ieee80211_frame *wh = mtod(m0, struct ieee80211_frame *); struct ieee80211_action_ba_addbarequest *ia; uint8_t *frm; uint16_t baparamset; /* Not action frame? Bail */ if (! ieee80211_is_action(wh)) return 0; /* XXX Not needed for frames we send? */ #if 0 /* Correct length? */ if (! ieee80211_parse_action(ni, m)) return 0; #endif /* Extract out action frame */ frm = (u_int8_t *)&wh[1]; ia = (struct ieee80211_action_ba_addbarequest *) frm; /* Not ADDBA? Bail */ if (ia->rq_header.ia_category != IEEE80211_ACTION_CAT_BA) return 0; if (ia->rq_header.ia_action != IEEE80211_ACTION_BA_ADDBA_REQUEST) return 0; /* Extract TID, return it */ baparamset = le16toh(ia->rq_baparamset); *tid = (int) MS(baparamset, IEEE80211_BAPS_TID); return 1; } #undef MS /* Per-node software queue operations */ /* * Add the current packet to the given BAW. * It is assumed that the current packet * * + fits inside the BAW; * + already has had a sequence number allocated. * * Since the BAW status may be modified by both the ath task and * the net80211/ifnet contexts, the TID must be locked. */ void ath_tx_addto_baw(struct ath_softc *sc, struct ath_node *an, struct ath_tid *tid, struct ath_buf *bf) { int index, cindex; struct ieee80211_tx_ampdu *tap; ATH_TX_LOCK_ASSERT(sc); if (bf->bf_state.bfs_isretried) return; tap = ath_tx_get_tx_tid(an, tid->tid); if (! bf->bf_state.bfs_dobaw) { DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, "%s: dobaw=0, seqno=%d, window %d:%d\n", __func__, SEQNO(bf->bf_state.bfs_seqno), tap->txa_start, tap->txa_wnd); } if (bf->bf_state.bfs_addedbaw) DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, "%s: re-added? tid=%d, seqno %d; window %d:%d; " "baw head=%d tail=%d\n", __func__, tid->tid, SEQNO(bf->bf_state.bfs_seqno), tap->txa_start, tap->txa_wnd, tid->baw_head, tid->baw_tail); /* * Verify that the given sequence number is not outside of the * BAW. Complain loudly if that's the case. */ if (! BAW_WITHIN(tap->txa_start, tap->txa_wnd, SEQNO(bf->bf_state.bfs_seqno))) { DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, "%s: bf=%p: outside of BAW?? tid=%d, seqno %d; window %d:%d; " "baw head=%d tail=%d\n", __func__, bf, tid->tid, SEQNO(bf->bf_state.bfs_seqno), tap->txa_start, tap->txa_wnd, tid->baw_head, tid->baw_tail); } /* * ni->ni_txseqs[] is the currently allocated seqno. * the txa state contains the current baw start. */ index = ATH_BA_INDEX(tap->txa_start, SEQNO(bf->bf_state.bfs_seqno)); cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1); DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, "%s: tid=%d, seqno %d; window %d:%d; index=%d cindex=%d " "baw head=%d tail=%d\n", __func__, tid->tid, SEQNO(bf->bf_state.bfs_seqno), tap->txa_start, tap->txa_wnd, index, cindex, tid->baw_head, tid->baw_tail); #if 0 assert(tid->tx_buf[cindex] == NULL); #endif if (tid->tx_buf[cindex] != NULL) { DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, "%s: ba packet dup (index=%d, cindex=%d, " "head=%d, tail=%d)\n", __func__, index, cindex, tid->baw_head, tid->baw_tail); DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, "%s: BA bf: %p; seqno=%d ; new bf: %p; seqno=%d\n", __func__, tid->tx_buf[cindex], SEQNO(tid->tx_buf[cindex]->bf_state.bfs_seqno), bf, SEQNO(bf->bf_state.bfs_seqno) ); } tid->tx_buf[cindex] = bf; if (index >= ((tid->baw_tail - tid->baw_head) & (ATH_TID_MAX_BUFS - 1))) { tid->baw_tail = cindex; INCR(tid->baw_tail, ATH_TID_MAX_BUFS); } } /* * Flip the BAW buffer entry over from the existing one to the new one. * * When software retransmitting a (sub-)frame, it is entirely possible that * the frame ath_buf is marked as BUSY and can't be immediately reused. * In that instance the buffer is cloned and the new buffer is used for * retransmit. We thus need to update the ath_buf slot in the BAW buf * tracking array to maintain consistency. */ static void ath_tx_switch_baw_buf(struct ath_softc *sc, struct ath_node *an, struct ath_tid *tid, struct ath_buf *old_bf, struct ath_buf *new_bf) { int index, cindex; struct ieee80211_tx_ampdu *tap; int seqno = SEQNO(old_bf->bf_state.bfs_seqno); ATH_TX_LOCK_ASSERT(sc); tap = ath_tx_get_tx_tid(an, tid->tid); index = ATH_BA_INDEX(tap->txa_start, seqno); cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1); /* * Just warn for now; if it happens then we should find out * about it. It's highly likely the aggregation session will * soon hang. */ if (old_bf->bf_state.bfs_seqno != new_bf->bf_state.bfs_seqno) { DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, "%s: retransmitted buffer" " has mismatching seqno's, BA session may hang.\n", __func__); DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, "%s: old seqno=%d, new_seqno=%d\n", __func__, old_bf->bf_state.bfs_seqno, new_bf->bf_state.bfs_seqno); } if (tid->tx_buf[cindex] != old_bf) { DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, "%s: ath_buf pointer incorrect; " " has m BA session may hang.\n", __func__); DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, "%s: old bf=%p, new bf=%p\n", __func__, old_bf, new_bf); } tid->tx_buf[cindex] = new_bf; } /* * seq_start - left edge of BAW * seq_next - current/next sequence number to allocate * * Since the BAW status may be modified by both the ath task and * the net80211/ifnet contexts, the TID must be locked. */ static void ath_tx_update_baw(struct ath_softc *sc, struct ath_node *an, struct ath_tid *tid, const struct ath_buf *bf) { int index, cindex; struct ieee80211_tx_ampdu *tap; int seqno = SEQNO(bf->bf_state.bfs_seqno); ATH_TX_LOCK_ASSERT(sc); tap = ath_tx_get_tx_tid(an, tid->tid); index = ATH_BA_INDEX(tap->txa_start, seqno); cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1); DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, "%s: tid=%d, baw=%d:%d, seqno=%d, index=%d, cindex=%d, " "baw head=%d, tail=%d\n", __func__, tid->tid, tap->txa_start, tap->txa_wnd, seqno, index, cindex, tid->baw_head, tid->baw_tail); /* * If this occurs then we have a big problem - something else * has slid tap->txa_start along without updating the BAW * tracking start/end pointers. Thus the TX BAW state is now * completely busted. * * But for now, since I haven't yet fixed TDMA and buffer cloning, * it's quite possible that a cloned buffer is making its way * here and causing it to fire off. Disable TDMA for now. */ if (tid->tx_buf[cindex] != bf) { DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, "%s: comp bf=%p, seq=%d; slot bf=%p, seqno=%d\n", __func__, bf, SEQNO(bf->bf_state.bfs_seqno), tid->tx_buf[cindex], (tid->tx_buf[cindex] != NULL) ? SEQNO(tid->tx_buf[cindex]->bf_state.bfs_seqno) : -1); } tid->tx_buf[cindex] = NULL; while (tid->baw_head != tid->baw_tail && !tid->tx_buf[tid->baw_head]) { INCR(tap->txa_start, IEEE80211_SEQ_RANGE); INCR(tid->baw_head, ATH_TID_MAX_BUFS); } DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, "%s: tid=%d: baw is now %d:%d, baw head=%d\n", __func__, tid->tid, tap->txa_start, tap->txa_wnd, tid->baw_head); } static void ath_tx_leak_count_update(struct ath_softc *sc, struct ath_tid *tid, struct ath_buf *bf) { struct ieee80211_frame *wh; ATH_TX_LOCK_ASSERT(sc); if (tid->an->an_leak_count > 0) { wh = mtod(bf->bf_m, struct ieee80211_frame *); /* * Update MORE based on the software/net80211 queue states. */ if ((tid->an->an_stack_psq > 0) || (tid->an->an_swq_depth > 0)) wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA; else wh->i_fc[1] &= ~IEEE80211_FC1_MORE_DATA; DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, "%s: %6D: leak count = %d, psq=%d, swq=%d, MORE=%d\n", __func__, tid->an->an_node.ni_macaddr, ":", tid->an->an_leak_count, tid->an->an_stack_psq, tid->an->an_swq_depth, !! (wh->i_fc[1] & IEEE80211_FC1_MORE_DATA)); /* * Re-sync the underlying buffer. */ bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE); tid->an->an_leak_count --; } } static int ath_tx_tid_can_tx_or_sched(struct ath_softc *sc, struct ath_tid *tid) { ATH_TX_LOCK_ASSERT(sc); if (tid->an->an_leak_count > 0) { return (1); } if (tid->paused) return (0); return (1); } /* * Mark the current node/TID as ready to TX. * * This is done to make it easy for the software scheduler to * find which nodes have data to send. * * The TXQ lock must be held. */ void ath_tx_tid_sched(struct ath_softc *sc, struct ath_tid *tid) { struct ath_txq *txq = sc->sc_ac2q[tid->ac]; ATH_TX_LOCK_ASSERT(sc); /* * If we are leaking out a frame to this destination * for PS-POLL, ensure that we allow scheduling to * occur. */ if (! ath_tx_tid_can_tx_or_sched(sc, tid)) return; /* paused, can't schedule yet */ if (tid->sched) return; /* already scheduled */ tid->sched = 1; #if 0 /* * If this is a sleeping node we're leaking to, given * it a higher priority. This is so bad for QoS it hurts. */ if (tid->an->an_leak_count) { TAILQ_INSERT_HEAD(&txq->axq_tidq, tid, axq_qelem); } else { TAILQ_INSERT_TAIL(&txq->axq_tidq, tid, axq_qelem); } #endif /* * We can't do the above - it'll confuse the TXQ software * scheduler which will keep checking the _head_ TID * in the list to see if it has traffic. If we queue * a TID to the head of the list and it doesn't transmit, * we'll check it again. * * So, get the rest of this leaking frames support working * and reliable first and _then_ optimise it so they're * pushed out in front of any other pending software * queued nodes. */ TAILQ_INSERT_TAIL(&txq->axq_tidq, tid, axq_qelem); } /* * Mark the current node as no longer needing to be polled for * TX packets. * * The TXQ lock must be held. */ static void ath_tx_tid_unsched(struct ath_softc *sc, struct ath_tid *tid) { struct ath_txq *txq = sc->sc_ac2q[tid->ac]; ATH_TX_LOCK_ASSERT(sc); if (tid->sched == 0) return; tid->sched = 0; TAILQ_REMOVE(&txq->axq_tidq, tid, axq_qelem); } /* * Assign a sequence number manually to the given frame. * * This should only be called for A-MPDU TX frames. */ static ieee80211_seq ath_tx_tid_seqno_assign(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf, struct mbuf *m0) { struct ieee80211_frame *wh; int tid, pri; ieee80211_seq seqno; uint8_t subtype; /* TID lookup */ wh = mtod(m0, struct ieee80211_frame *); pri = M_WME_GETAC(m0); /* honor classification */ tid = WME_AC_TO_TID(pri); DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: pri=%d, tid=%d, qos has seq=%d\n", __func__, pri, tid, IEEE80211_QOS_HAS_SEQ(wh)); /* XXX Is it a control frame? Ignore */ /* Does the packet require a sequence number? */ if (! IEEE80211_QOS_HAS_SEQ(wh)) return -1; ATH_TX_LOCK_ASSERT(sc); /* * Is it a QOS NULL Data frame? Give it a sequence number from * the default TID (IEEE80211_NONQOS_TID.) * * The RX path of everything I've looked at doesn't include the NULL * data frame sequence number in the aggregation state updates, so * assigning it a sequence number there will cause a BAW hole on the * RX side. */ subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; if (subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL) { /* XXX no locking for this TID? This is a bit of a problem. */ seqno = ni->ni_txseqs[IEEE80211_NONQOS_TID]; INCR(ni->ni_txseqs[IEEE80211_NONQOS_TID], IEEE80211_SEQ_RANGE); } else { /* Manually assign sequence number */ seqno = ni->ni_txseqs[tid]; INCR(ni->ni_txseqs[tid], IEEE80211_SEQ_RANGE); } *(uint16_t *)&wh->i_seq[0] = htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); M_SEQNO_SET(m0, seqno); /* Return so caller can do something with it if needed */ DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: -> seqno=%d\n", __func__, seqno); return seqno; } /* * Attempt to direct dispatch an aggregate frame to hardware. * If the frame is out of BAW, queue. * Otherwise, schedule it as a single frame. */ static void ath_tx_xmit_aggr(struct ath_softc *sc, struct ath_node *an, struct ath_txq *txq, struct ath_buf *bf) { struct ath_tid *tid = &an->an_tid[bf->bf_state.bfs_tid]; struct ieee80211_tx_ampdu *tap; ATH_TX_LOCK_ASSERT(sc); tap = ath_tx_get_tx_tid(an, tid->tid); /* paused? queue */ if (! ath_tx_tid_can_tx_or_sched(sc, tid)) { ATH_TID_INSERT_HEAD(tid, bf, bf_list); /* XXX don't sched - we're paused! */ return; } /* outside baw? queue */ if (bf->bf_state.bfs_dobaw && (! BAW_WITHIN(tap->txa_start, tap->txa_wnd, SEQNO(bf->bf_state.bfs_seqno)))) { ATH_TID_INSERT_HEAD(tid, bf, bf_list); ath_tx_tid_sched(sc, tid); return; } /* * This is a temporary check and should be removed once * all the relevant code paths have been fixed. * * During aggregate retries, it's possible that the head * frame will fail (which has the bfs_aggr and bfs_nframes * fields set for said aggregate) and will be retried as * a single frame. In this instance, the values should * be reset or the completion code will get upset with you. */ if (bf->bf_state.bfs_aggr != 0 || bf->bf_state.bfs_nframes > 1) { DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: bfs_aggr=%d, bfs_nframes=%d\n", __func__, bf->bf_state.bfs_aggr, bf->bf_state.bfs_nframes); bf->bf_state.bfs_aggr = 0; bf->bf_state.bfs_nframes = 1; } /* Update CLRDMASK just before this frame is queued */ ath_tx_update_clrdmask(sc, tid, bf); /* Direct dispatch to hardware */ ath_tx_do_ratelookup(sc, bf); ath_tx_calc_duration(sc, bf); ath_tx_calc_protection(sc, bf); ath_tx_set_rtscts(sc, bf); ath_tx_rate_fill_rcflags(sc, bf); ath_tx_setds(sc, bf); /* Statistics */ sc->sc_aggr_stats.aggr_low_hwq_single_pkt++; /* Track per-TID hardware queue depth correctly */ tid->hwq_depth++; /* Add to BAW */ if (bf->bf_state.bfs_dobaw) { ath_tx_addto_baw(sc, an, tid, bf); bf->bf_state.bfs_addedbaw = 1; } /* Set completion handler, multi-frame aggregate or not */ bf->bf_comp = ath_tx_aggr_comp; /* * Update the current leak count if * we're leaking frames; and set the * MORE flag as appropriate. */ ath_tx_leak_count_update(sc, tid, bf); /* Hand off to hardware */ ath_tx_handoff(sc, txq, bf); } /* * Attempt to send the packet. * If the queue isn't busy, direct-dispatch. * If the queue is busy enough, queue the given packet on the * relevant software queue. */ void ath_tx_swq(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_txq *txq, int queue_to_head, struct ath_buf *bf) { struct ath_node *an = ATH_NODE(ni); struct ieee80211_frame *wh; struct ath_tid *atid; int pri, tid; struct mbuf *m0 = bf->bf_m; ATH_TX_LOCK_ASSERT(sc); /* Fetch the TID - non-QoS frames get assigned to TID 16 */ wh = mtod(m0, struct ieee80211_frame *); pri = ath_tx_getac(sc, m0); tid = ath_tx_gettid(sc, m0); atid = &an->an_tid[tid]; DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: bf=%p, pri=%d, tid=%d, qos=%d\n", __func__, bf, pri, tid, IEEE80211_QOS_HAS_SEQ(wh)); /* Set local packet state, used to queue packets to hardware */ /* XXX potentially duplicate info, re-check */ bf->bf_state.bfs_tid = tid; bf->bf_state.bfs_tx_queue = txq->axq_qnum; bf->bf_state.bfs_pri = pri; /* * If the hardware queue isn't busy, queue it directly. * If the hardware queue is busy, queue it. * If the TID is paused or the traffic it outside BAW, software * queue it. * * If the node is in power-save and we're leaking a frame, * leak a single frame. */ if (! ath_tx_tid_can_tx_or_sched(sc, atid)) { /* TID is paused, queue */ DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: paused\n", __func__); /* * If the caller requested that it be sent at a high * priority, queue it at the head of the list. */ if (queue_to_head) ATH_TID_INSERT_HEAD(atid, bf, bf_list); else ATH_TID_INSERT_TAIL(atid, bf, bf_list); } else if (ath_tx_ampdu_pending(sc, an, tid)) { /* AMPDU pending; queue */ DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: pending\n", __func__); ATH_TID_INSERT_TAIL(atid, bf, bf_list); /* XXX sched? */ } else if (ath_tx_ampdu_running(sc, an, tid)) { /* AMPDU running, attempt direct dispatch if possible */ /* * Always queue the frame to the tail of the list. */ ATH_TID_INSERT_TAIL(atid, bf, bf_list); /* * If the hardware queue isn't busy, direct dispatch * the head frame in the list. Don't schedule the * TID - let it build some more frames first? * * When running A-MPDU, always just check the hardware * queue depth against the aggregate frame limit. * We don't want to burst a large number of single frames * out to the hardware; we want to aggressively hold back. * * Otherwise, schedule the TID. */ /* XXX TXQ locking */ if (txq->axq_depth + txq->fifo.axq_depth < sc->sc_hwq_limit_aggr) { bf = ATH_TID_FIRST(atid); ATH_TID_REMOVE(atid, bf, bf_list); /* * Ensure it's definitely treated as a non-AMPDU * frame - this information may have been left * over from a previous attempt. */ bf->bf_state.bfs_aggr = 0; bf->bf_state.bfs_nframes = 1; /* Queue to the hardware */ ath_tx_xmit_aggr(sc, an, txq, bf); DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: xmit_aggr\n", __func__); } else { DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: ampdu; swq'ing\n", __func__); ath_tx_tid_sched(sc, atid); } /* * If we're not doing A-MPDU, be prepared to direct dispatch * up to both limits if possible. This particular corner * case may end up with packet starvation between aggregate - * traffic and non-aggregate traffic: we wnat to ensure + * traffic and non-aggregate traffic: we want to ensure * that non-aggregate stations get a few frames queued to the * hardware before the aggregate station(s) get their chance. * * So if you only ever see a couple of frames direct dispatched * to the hardware from a non-AMPDU client, check both here * and in the software queue dispatcher to ensure that those * non-AMPDU stations get a fair chance to transmit. */ /* XXX TXQ locking */ } else if ((txq->axq_depth + txq->fifo.axq_depth < sc->sc_hwq_limit_nonaggr) && (txq->axq_aggr_depth < sc->sc_hwq_limit_aggr)) { /* AMPDU not running, attempt direct dispatch */ DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: xmit_normal\n", __func__); /* See if clrdmask needs to be set */ ath_tx_update_clrdmask(sc, atid, bf); /* * Update the current leak count if * we're leaking frames; and set the * MORE flag as appropriate. */ ath_tx_leak_count_update(sc, atid, bf); /* * Dispatch the frame. */ ath_tx_xmit_normal(sc, txq, bf); } else { /* Busy; queue */ DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: swq'ing\n", __func__); ATH_TID_INSERT_TAIL(atid, bf, bf_list); ath_tx_tid_sched(sc, atid); } } /* * Only set the clrdmask bit if none of the nodes are currently * filtered. * * XXX TODO: go through all the callers and check to see * which are being called in the context of looping over all * TIDs (eg, if all tids are being paused, resumed, etc.) * That'll avoid O(n^2) complexity here. */ static void ath_tx_set_clrdmask(struct ath_softc *sc, struct ath_node *an) { int i; ATH_TX_LOCK_ASSERT(sc); for (i = 0; i < IEEE80211_TID_SIZE; i++) { if (an->an_tid[i].isfiltered == 1) return; } an->clrdmask = 1; } /* * Configure the per-TID node state. * * This likely belongs in if_ath_node.c but I can't think of anywhere * else to put it just yet. * * This sets up the SLISTs and the mutex as appropriate. */ void ath_tx_tid_init(struct ath_softc *sc, struct ath_node *an) { int i, j; struct ath_tid *atid; for (i = 0; i < IEEE80211_TID_SIZE; i++) { atid = &an->an_tid[i]; /* XXX now with this bzer(), is the field 0'ing needed? */ bzero(atid, sizeof(*atid)); TAILQ_INIT(&atid->tid_q); TAILQ_INIT(&atid->filtq.tid_q); atid->tid = i; atid->an = an; for (j = 0; j < ATH_TID_MAX_BUFS; j++) atid->tx_buf[j] = NULL; atid->baw_head = atid->baw_tail = 0; atid->paused = 0; atid->sched = 0; atid->hwq_depth = 0; atid->cleanup_inprogress = 0; if (i == IEEE80211_NONQOS_TID) atid->ac = ATH_NONQOS_TID_AC; else atid->ac = TID_TO_WME_AC(i); } an->clrdmask = 1; /* Always start by setting this bit */ } /* * Pause the current TID. This stops packets from being transmitted * on it. * * Since this is also called from upper layers as well as the driver, * it will get the TID lock. */ static void ath_tx_tid_pause(struct ath_softc *sc, struct ath_tid *tid) { ATH_TX_LOCK_ASSERT(sc); tid->paused++; DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: [%6D]: tid=%d, paused = %d\n", __func__, tid->an->an_node.ni_macaddr, ":", tid->tid, tid->paused); } /* * Unpause the current TID, and schedule it if needed. */ static void ath_tx_tid_resume(struct ath_softc *sc, struct ath_tid *tid) { ATH_TX_LOCK_ASSERT(sc); /* * There's some odd places where ath_tx_tid_resume() is called * when it shouldn't be; this works around that particular issue * until it's actually resolved. */ if (tid->paused == 0) { device_printf(sc->sc_dev, "%s: [%6D]: tid=%d, paused=0?\n", __func__, tid->an->an_node.ni_macaddr, ":", tid->tid); } else { tid->paused--; } DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: [%6D]: tid=%d, unpaused = %d\n", __func__, tid->an->an_node.ni_macaddr, ":", tid->tid, tid->paused); if (tid->paused) return; /* * Override the clrdmask configuration for the next frame * from this TID, just to get the ball rolling. */ ath_tx_set_clrdmask(sc, tid->an); if (tid->axq_depth == 0) return; /* XXX isfiltered shouldn't ever be 0 at this point */ if (tid->isfiltered == 1) { DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: filtered?!\n", __func__); return; } ath_tx_tid_sched(sc, tid); /* * Queue the software TX scheduler. */ ath_tx_swq_kick(sc); } /* * Add the given ath_buf to the TID filtered frame list. * This requires the TID be filtered. */ static void ath_tx_tid_filt_addbuf(struct ath_softc *sc, struct ath_tid *tid, struct ath_buf *bf) { ATH_TX_LOCK_ASSERT(sc); if (!tid->isfiltered) DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, "%s: not filtered?!\n", __func__); DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, "%s: bf=%p\n", __func__, bf); /* Set the retry bit and bump the retry counter */ ath_tx_set_retry(sc, bf); sc->sc_stats.ast_tx_swfiltered++; ATH_TID_FILT_INSERT_TAIL(tid, bf, bf_list); } /* * Handle a completed filtered frame from the given TID. * This just enables/pauses the filtered frame state if required * and appends the filtered frame to the filtered queue. */ static void ath_tx_tid_filt_comp_buf(struct ath_softc *sc, struct ath_tid *tid, struct ath_buf *bf) { ATH_TX_LOCK_ASSERT(sc); if (! tid->isfiltered) { DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, "%s: tid=%d; filter transition\n", __func__, tid->tid); tid->isfiltered = 1; ath_tx_tid_pause(sc, tid); } /* Add the frame to the filter queue */ ath_tx_tid_filt_addbuf(sc, tid, bf); } /* * Complete the filtered frame TX completion. * * If there are no more frames in the hardware queue, unpause/unfilter * the TID if applicable. Otherwise we will wait for a node PS transition * to unfilter. */ static void ath_tx_tid_filt_comp_complete(struct ath_softc *sc, struct ath_tid *tid) { struct ath_buf *bf; int do_resume = 0; ATH_TX_LOCK_ASSERT(sc); if (tid->hwq_depth != 0) return; DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, "%s: tid=%d, hwq=0, transition back\n", __func__, tid->tid); if (tid->isfiltered == 1) { tid->isfiltered = 0; do_resume = 1; } /* XXX ath_tx_tid_resume() also calls ath_tx_set_clrdmask()! */ ath_tx_set_clrdmask(sc, tid->an); /* XXX this is really quite inefficient */ while ((bf = ATH_TID_FILT_LAST(tid, ath_bufhead_s)) != NULL) { ATH_TID_FILT_REMOVE(tid, bf, bf_list); ATH_TID_INSERT_HEAD(tid, bf, bf_list); } /* And only resume if we had paused before */ if (do_resume) ath_tx_tid_resume(sc, tid); } /* * Called when a single (aggregate or otherwise) frame is completed. * * Returns 0 if the buffer could be added to the filtered list * (cloned or otherwise), 1 if the buffer couldn't be added to the * filtered list (failed clone; expired retry) and the caller should * free it and handle it like a failure (eg by sending a BAR.) * * since the buffer may be cloned, bf must be not touched after this * if the return value is 0. */ static int ath_tx_tid_filt_comp_single(struct ath_softc *sc, struct ath_tid *tid, struct ath_buf *bf) { struct ath_buf *nbf; int retval; ATH_TX_LOCK_ASSERT(sc); /* * Don't allow a filtered frame to live forever. */ if (bf->bf_state.bfs_retries > SWMAX_RETRIES) { sc->sc_stats.ast_tx_swretrymax++; DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, "%s: bf=%p, seqno=%d, exceeded retries\n", __func__, bf, SEQNO(bf->bf_state.bfs_seqno)); retval = 1; /* error */ goto finish; } /* * A busy buffer can't be added to the retry list. * It needs to be cloned. */ if (bf->bf_flags & ATH_BUF_BUSY) { nbf = ath_tx_retry_clone(sc, tid->an, tid, bf); DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, "%s: busy buffer clone: %p -> %p\n", __func__, bf, nbf); } else { nbf = bf; } if (nbf == NULL) { DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, "%s: busy buffer couldn't be cloned (%p)!\n", __func__, bf); retval = 1; /* error */ } else { ath_tx_tid_filt_comp_buf(sc, tid, nbf); retval = 0; /* ok */ } finish: ath_tx_tid_filt_comp_complete(sc, tid); return (retval); } static void ath_tx_tid_filt_comp_aggr(struct ath_softc *sc, struct ath_tid *tid, struct ath_buf *bf_first, ath_bufhead *bf_q) { struct ath_buf *bf, *bf_next, *nbf; ATH_TX_LOCK_ASSERT(sc); bf = bf_first; while (bf) { bf_next = bf->bf_next; bf->bf_next = NULL; /* Remove it from the aggr list */ /* * Don't allow a filtered frame to live forever. */ if (bf->bf_state.bfs_retries > SWMAX_RETRIES) { sc->sc_stats.ast_tx_swretrymax++; DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, "%s: tid=%d, bf=%p, seqno=%d, exceeded retries\n", __func__, tid->tid, bf, SEQNO(bf->bf_state.bfs_seqno)); TAILQ_INSERT_TAIL(bf_q, bf, bf_list); goto next; } if (bf->bf_flags & ATH_BUF_BUSY) { nbf = ath_tx_retry_clone(sc, tid->an, tid, bf); DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, "%s: tid=%d, busy buffer cloned: %p -> %p, seqno=%d\n", __func__, tid->tid, bf, nbf, SEQNO(bf->bf_state.bfs_seqno)); } else { nbf = bf; } /* * If the buffer couldn't be cloned, add it to bf_q; * the caller will free the buffer(s) as required. */ if (nbf == NULL) { DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, "%s: tid=%d, buffer couldn't be cloned! (%p) seqno=%d\n", __func__, tid->tid, bf, SEQNO(bf->bf_state.bfs_seqno)); TAILQ_INSERT_TAIL(bf_q, bf, bf_list); } else { ath_tx_tid_filt_comp_buf(sc, tid, nbf); } next: bf = bf_next; } ath_tx_tid_filt_comp_complete(sc, tid); } /* * Suspend the queue because we need to TX a BAR. */ static void ath_tx_tid_bar_suspend(struct ath_softc *sc, struct ath_tid *tid) { ATH_TX_LOCK_ASSERT(sc); DPRINTF(sc, ATH_DEBUG_SW_TX_BAR, "%s: tid=%d, bar_wait=%d, bar_tx=%d, called\n", __func__, tid->tid, tid->bar_wait, tid->bar_tx); /* We shouldn't be called when bar_tx is 1 */ if (tid->bar_tx) { DPRINTF(sc, ATH_DEBUG_SW_TX_BAR, "%s: bar_tx is 1?!\n", __func__); } /* If we've already been called, just be patient. */ if (tid->bar_wait) return; /* Wait! */ tid->bar_wait = 1; /* Only one pause, no matter how many frames fail */ ath_tx_tid_pause(sc, tid); } /* * We've finished with BAR handling - either we succeeded or * failed. Either way, unsuspend TX. */ static void ath_tx_tid_bar_unsuspend(struct ath_softc *sc, struct ath_tid *tid) { ATH_TX_LOCK_ASSERT(sc); DPRINTF(sc, ATH_DEBUG_SW_TX_BAR, "%s: %6D: TID=%d, called\n", __func__, tid->an->an_node.ni_macaddr, ":", tid->tid); if (tid->bar_tx == 0 || tid->bar_wait == 0) { DPRINTF(sc, ATH_DEBUG_SW_TX_BAR, "%s: %6D: TID=%d, bar_tx=%d, bar_wait=%d: ?\n", __func__, tid->an->an_node.ni_macaddr, ":", tid->tid, tid->bar_tx, tid->bar_wait); } tid->bar_tx = tid->bar_wait = 0; ath_tx_tid_resume(sc, tid); } /* * Return whether we're ready to TX a BAR frame. * * Requires the TID lock be held. */ static int ath_tx_tid_bar_tx_ready(struct ath_softc *sc, struct ath_tid *tid) { ATH_TX_LOCK_ASSERT(sc); if (tid->bar_wait == 0 || tid->hwq_depth > 0) return (0); DPRINTF(sc, ATH_DEBUG_SW_TX_BAR, "%s: %6D: TID=%d, bar ready\n", __func__, tid->an->an_node.ni_macaddr, ":", tid->tid); return (1); } /* * Check whether the current TID is ready to have a BAR * TXed and if so, do the TX. * * Since the TID/TXQ lock can't be held during a call to * ieee80211_send_bar(), we have to do the dirty thing of unlocking it, * sending the BAR and locking it again. * * Eventually, the code to send the BAR should be broken out * from this routine so the lock doesn't have to be reacquired * just to be immediately dropped by the caller. */ static void ath_tx_tid_bar_tx(struct ath_softc *sc, struct ath_tid *tid) { struct ieee80211_tx_ampdu *tap; ATH_TX_LOCK_ASSERT(sc); DPRINTF(sc, ATH_DEBUG_SW_TX_BAR, "%s: %6D: TID=%d, called\n", __func__, tid->an->an_node.ni_macaddr, ":", tid->tid); tap = ath_tx_get_tx_tid(tid->an, tid->tid); /* * This is an error condition! */ if (tid->bar_wait == 0 || tid->bar_tx == 1) { DPRINTF(sc, ATH_DEBUG_SW_TX_BAR, "%s: %6D: TID=%d, bar_tx=%d, bar_wait=%d: ?\n", __func__, tid->an->an_node.ni_macaddr, ":", tid->tid, tid->bar_tx, tid->bar_wait); return; } /* Don't do anything if we still have pending frames */ if (tid->hwq_depth > 0) { DPRINTF(sc, ATH_DEBUG_SW_TX_BAR, "%s: %6D: TID=%d, hwq_depth=%d, waiting\n", __func__, tid->an->an_node.ni_macaddr, ":", tid->tid, tid->hwq_depth); return; } /* We're now about to TX */ tid->bar_tx = 1; /* * Override the clrdmask configuration for the next frame, * just to get the ball rolling. */ ath_tx_set_clrdmask(sc, tid->an); /* * Calculate new BAW left edge, now that all frames have either * succeeded or failed. * * XXX verify this is _actually_ the valid value to begin at! */ DPRINTF(sc, ATH_DEBUG_SW_TX_BAR, "%s: %6D: TID=%d, new BAW left edge=%d\n", __func__, tid->an->an_node.ni_macaddr, ":", tid->tid, tap->txa_start); /* Try sending the BAR frame */ /* We can't hold the lock here! */ ATH_TX_UNLOCK(sc); if (ieee80211_send_bar(&tid->an->an_node, tap, tap->txa_start) == 0) { /* Success? Now we wait for notification that it's done */ ATH_TX_LOCK(sc); return; } /* Failure? For now, warn loudly and continue */ ATH_TX_LOCK(sc); DPRINTF(sc, ATH_DEBUG_SW_TX_BAR, "%s: %6D: TID=%d, failed to TX BAR, continue!\n", __func__, tid->an->an_node.ni_macaddr, ":", tid->tid); ath_tx_tid_bar_unsuspend(sc, tid); } static void ath_tx_tid_drain_pkt(struct ath_softc *sc, struct ath_node *an, struct ath_tid *tid, ath_bufhead *bf_cq, struct ath_buf *bf) { ATH_TX_LOCK_ASSERT(sc); /* * If the current TID is running AMPDU, update * the BAW. */ if (ath_tx_ampdu_running(sc, an, tid->tid) && bf->bf_state.bfs_dobaw) { /* * Only remove the frame from the BAW if it's * been transmitted at least once; this means * the frame was in the BAW to begin with. */ if (bf->bf_state.bfs_retries > 0) { ath_tx_update_baw(sc, an, tid, bf); bf->bf_state.bfs_dobaw = 0; } #if 0 /* * This has become a non-fatal error now */ if (! bf->bf_state.bfs_addedbaw) DPRINTF(sc, ATH_DEBUG_SW_TX_BAW "%s: wasn't added: seqno %d\n", __func__, SEQNO(bf->bf_state.bfs_seqno)); #endif } /* Strip it out of an aggregate list if it was in one */ bf->bf_next = NULL; /* Insert on the free queue to be freed by the caller */ TAILQ_INSERT_TAIL(bf_cq, bf, bf_list); } static void ath_tx_tid_drain_print(struct ath_softc *sc, struct ath_node *an, const char *pfx, struct ath_tid *tid, struct ath_buf *bf) { struct ieee80211_node *ni = &an->an_node; struct ath_txq *txq; struct ieee80211_tx_ampdu *tap; txq = sc->sc_ac2q[tid->ac]; tap = ath_tx_get_tx_tid(an, tid->tid); DPRINTF(sc, ATH_DEBUG_SW_TX | ATH_DEBUG_RESET, "%s: %s: %6D: bf=%p: addbaw=%d, dobaw=%d, " "seqno=%d, retry=%d\n", __func__, pfx, ni->ni_macaddr, ":", bf, bf->bf_state.bfs_addedbaw, bf->bf_state.bfs_dobaw, SEQNO(bf->bf_state.bfs_seqno), bf->bf_state.bfs_retries); DPRINTF(sc, ATH_DEBUG_SW_TX | ATH_DEBUG_RESET, "%s: %s: %6D: bf=%p: txq[%d] axq_depth=%d, axq_aggr_depth=%d\n", __func__, pfx, ni->ni_macaddr, ":", bf, txq->axq_qnum, txq->axq_depth, txq->axq_aggr_depth); DPRINTF(sc, ATH_DEBUG_SW_TX | ATH_DEBUG_RESET, "%s: %s: %6D: bf=%p: tid txq_depth=%d hwq_depth=%d, bar_wait=%d, " "isfiltered=%d\n", __func__, pfx, ni->ni_macaddr, ":", bf, tid->axq_depth, tid->hwq_depth, tid->bar_wait, tid->isfiltered); DPRINTF(sc, ATH_DEBUG_SW_TX | ATH_DEBUG_RESET, "%s: %s: %6D: tid %d: " "sched=%d, paused=%d, " "incomp=%d, baw_head=%d, " "baw_tail=%d txa_start=%d, ni_txseqs=%d\n", __func__, pfx, ni->ni_macaddr, ":", tid->tid, tid->sched, tid->paused, tid->incomp, tid->baw_head, tid->baw_tail, tap == NULL ? -1 : tap->txa_start, ni->ni_txseqs[tid->tid]); /* XXX Dump the frame, see what it is? */ if (IFF_DUMPPKTS(sc, ATH_DEBUG_XMIT)) ieee80211_dump_pkt(ni->ni_ic, mtod(bf->bf_m, const uint8_t *), bf->bf_m->m_len, 0, -1); } /* * Free any packets currently pending in the software TX queue. * * This will be called when a node is being deleted. * * It can also be called on an active node during an interface * reset or state transition. * * (From Linux/reference): * * TODO: For frame(s) that are in the retry state, we will reuse the * sequence number(s) without setting the retry bit. The * alternative is to give up on these and BAR the receiver's window * forward. */ static void ath_tx_tid_drain(struct ath_softc *sc, struct ath_node *an, struct ath_tid *tid, ath_bufhead *bf_cq) { struct ath_buf *bf; struct ieee80211_tx_ampdu *tap; struct ieee80211_node *ni = &an->an_node; int t; tap = ath_tx_get_tx_tid(an, tid->tid); ATH_TX_LOCK_ASSERT(sc); /* Walk the queue, free frames */ t = 0; for (;;) { bf = ATH_TID_FIRST(tid); if (bf == NULL) { break; } if (t == 0) { ath_tx_tid_drain_print(sc, an, "norm", tid, bf); // t = 1; } ATH_TID_REMOVE(tid, bf, bf_list); ath_tx_tid_drain_pkt(sc, an, tid, bf_cq, bf); } /* And now, drain the filtered frame queue */ t = 0; for (;;) { bf = ATH_TID_FILT_FIRST(tid); if (bf == NULL) break; if (t == 0) { ath_tx_tid_drain_print(sc, an, "filt", tid, bf); // t = 1; } ATH_TID_FILT_REMOVE(tid, bf, bf_list); ath_tx_tid_drain_pkt(sc, an, tid, bf_cq, bf); } /* * Override the clrdmask configuration for the next frame * in case there is some future transmission, just to get * the ball rolling. * * This won't hurt things if the TID is about to be freed. */ ath_tx_set_clrdmask(sc, tid->an); /* * Now that it's completed, grab the TID lock and update * the sequence number and BAW window. * Because sequence numbers have been assigned to frames * that haven't been sent yet, it's entirely possible * we'll be called with some pending frames that have not * been transmitted. * * The cleaner solution is to do the sequence number allocation * when the packet is first transmitted - and thus the "retries" * check above would be enough to update the BAW/seqno. */ /* But don't do it for non-QoS TIDs */ if (tap) { #if 1 DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: %6D: node %p: TID %d: sliding BAW left edge to %d\n", __func__, ni->ni_macaddr, ":", an, tid->tid, tap->txa_start); #endif ni->ni_txseqs[tid->tid] = tap->txa_start; tid->baw_tail = tid->baw_head; } } /* * Reset the TID state. This must be only called once the node has * had its frames flushed from this TID, to ensure that no other * pause / unpause logic can kick in. */ static void ath_tx_tid_reset(struct ath_softc *sc, struct ath_tid *tid) { #if 0 tid->bar_wait = tid->bar_tx = tid->isfiltered = 0; tid->paused = tid->sched = tid->addba_tx_pending = 0; tid->incomp = tid->cleanup_inprogress = 0; #endif /* * If we have a bar_wait set, we need to unpause the TID * here. Otherwise once cleanup has finished, the TID won't * have the right paused counter. * * XXX I'm not going through resume here - I don't want the * node to be rescheuled just yet. This however should be * methodized! */ if (tid->bar_wait) { if (tid->paused > 0) { tid->paused --; } } /* * XXX same with a currently filtered TID. * * Since this is being called during a flush, we assume that * the filtered frame list is actually empty. * * XXX TODO: add in a check to ensure that the filtered queue * depth is actually 0! */ if (tid->isfiltered) { if (tid->paused > 0) { tid->paused --; } } /* * Clear BAR, filtered frames, scheduled and ADDBA pending. * The TID may be going through cleanup from the last association * where things in the BAW are still in the hardware queue. */ tid->bar_wait = 0; tid->bar_tx = 0; tid->isfiltered = 0; tid->sched = 0; tid->addba_tx_pending = 0; /* * XXX TODO: it may just be enough to walk the HWQs and mark * frames for that node as non-aggregate; or mark the ath_node * with something that indicates that aggregation is no longer - * occuring. Then we can just toss the BAW complaints and + * occurring. Then we can just toss the BAW complaints and * do a complete hard reset of state here - no pause, no * complete counter, etc. */ } /* * Flush all software queued packets for the given node. * * This occurs when a completion handler frees the last buffer * for a node, and the node is thus freed. This causes the node * to be cleaned up, which ends up calling ath_tx_node_flush. */ void ath_tx_node_flush(struct ath_softc *sc, struct ath_node *an) { int tid; ath_bufhead bf_cq; struct ath_buf *bf; TAILQ_INIT(&bf_cq); ATH_KTR(sc, ATH_KTR_NODE, 1, "ath_tx_node_flush: flush node; ni=%p", &an->an_node); ATH_TX_LOCK(sc); DPRINTF(sc, ATH_DEBUG_NODE, "%s: %6D: flush; is_powersave=%d, stack_psq=%d, tim=%d, " "swq_depth=%d, clrdmask=%d, leak_count=%d\n", __func__, an->an_node.ni_macaddr, ":", an->an_is_powersave, an->an_stack_psq, an->an_tim_set, an->an_swq_depth, an->clrdmask, an->an_leak_count); for (tid = 0; tid < IEEE80211_TID_SIZE; tid++) { struct ath_tid *atid = &an->an_tid[tid]; /* Free packets */ ath_tx_tid_drain(sc, an, atid, &bf_cq); /* Remove this tid from the list of active tids */ ath_tx_tid_unsched(sc, atid); /* Reset the per-TID pause, BAR, etc state */ ath_tx_tid_reset(sc, atid); } /* * Clear global leak count */ an->an_leak_count = 0; ATH_TX_UNLOCK(sc); /* Handle completed frames */ while ((bf = TAILQ_FIRST(&bf_cq)) != NULL) { TAILQ_REMOVE(&bf_cq, bf, bf_list); ath_tx_default_comp(sc, bf, 0); } } /* * Drain all the software TXQs currently with traffic queued. */ void ath_tx_txq_drain(struct ath_softc *sc, struct ath_txq *txq) { struct ath_tid *tid; ath_bufhead bf_cq; struct ath_buf *bf; TAILQ_INIT(&bf_cq); ATH_TX_LOCK(sc); /* * Iterate over all active tids for the given txq, * flushing and unsched'ing them */ while (! TAILQ_EMPTY(&txq->axq_tidq)) { tid = TAILQ_FIRST(&txq->axq_tidq); ath_tx_tid_drain(sc, tid->an, tid, &bf_cq); ath_tx_tid_unsched(sc, tid); } ATH_TX_UNLOCK(sc); while ((bf = TAILQ_FIRST(&bf_cq)) != NULL) { TAILQ_REMOVE(&bf_cq, bf, bf_list); ath_tx_default_comp(sc, bf, 0); } } /* * Handle completion of non-aggregate session frames. * * This (currently) doesn't implement software retransmission of * non-aggregate frames! * * Software retransmission of non-aggregate frames needs to obey * the strict sequence number ordering, and drop any frames that * will fail this. * * For now, filtered frames and frame transmission will cause * all kinds of issues. So we don't support them. * * So anyone queuing frames via ath_tx_normal_xmit() or * ath_tx_hw_queue_norm() must override and set CLRDMASK. */ void ath_tx_normal_comp(struct ath_softc *sc, struct ath_buf *bf, int fail) { struct ieee80211_node *ni = bf->bf_node; struct ath_node *an = ATH_NODE(ni); int tid = bf->bf_state.bfs_tid; struct ath_tid *atid = &an->an_tid[tid]; struct ath_tx_status *ts = &bf->bf_status.ds_txstat; /* The TID state is protected behind the TXQ lock */ ATH_TX_LOCK(sc); DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: bf=%p: fail=%d, hwq_depth now %d\n", __func__, bf, fail, atid->hwq_depth - 1); atid->hwq_depth--; #if 0 /* * If the frame was filtered, stick it on the filter frame * queue and complain about it. It shouldn't happen! */ if ((ts->ts_status & HAL_TXERR_FILT) || (ts->ts_status != 0 && atid->isfiltered)) { DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: isfiltered=%d, ts_status=%d: huh?\n", __func__, atid->isfiltered, ts->ts_status); ath_tx_tid_filt_comp_buf(sc, atid, bf); } #endif if (atid->isfiltered) DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: filtered?!\n", __func__); if (atid->hwq_depth < 0) DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: hwq_depth < 0: %d\n", __func__, atid->hwq_depth); /* If the TID is being cleaned up, track things */ /* XXX refactor! */ if (atid->cleanup_inprogress) { atid->incomp--; if (atid->incomp == 0) { DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: TID %d: cleaned up! resume!\n", __func__, tid); atid->cleanup_inprogress = 0; ath_tx_tid_resume(sc, atid); } } /* * If the queue is filtered, potentially mark it as complete * and reschedule it as needed. * * This is required as there may be a subsequent TX descriptor * for this end-node that has CLRDMASK set, so it's quite possible * that a filtered frame will be followed by a non-filtered * (complete or otherwise) frame. * * XXX should we do this before we complete the frame? */ if (atid->isfiltered) ath_tx_tid_filt_comp_complete(sc, atid); ATH_TX_UNLOCK(sc); /* * punt to rate control if we're not being cleaned up * during a hw queue drain and the frame wanted an ACK. */ if (fail == 0 && ((bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 0)) ath_tx_update_ratectrl(sc, ni, bf->bf_state.bfs_rc, ts, bf->bf_state.bfs_pktlen, 1, (ts->ts_status == 0) ? 0 : 1); ath_tx_default_comp(sc, bf, fail); } /* * Handle cleanup of aggregate session packets that aren't * an A-MPDU. * * There's no need to update the BAW here - the session is being * torn down. */ static void ath_tx_comp_cleanup_unaggr(struct ath_softc *sc, struct ath_buf *bf) { struct ieee80211_node *ni = bf->bf_node; struct ath_node *an = ATH_NODE(ni); int tid = bf->bf_state.bfs_tid; struct ath_tid *atid = &an->an_tid[tid]; DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: TID %d: incomp=%d\n", __func__, tid, atid->incomp); ATH_TX_LOCK(sc); atid->incomp--; /* XXX refactor! */ if (bf->bf_state.bfs_dobaw) { ath_tx_update_baw(sc, an, atid, bf); if (!bf->bf_state.bfs_addedbaw) DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: wasn't added: seqno %d\n", __func__, SEQNO(bf->bf_state.bfs_seqno)); } if (atid->incomp == 0) { DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: TID %d: cleaned up! resume!\n", __func__, tid); atid->cleanup_inprogress = 0; ath_tx_tid_resume(sc, atid); } ATH_TX_UNLOCK(sc); ath_tx_default_comp(sc, bf, 0); } /* * This as it currently stands is a bit dumb. Ideally we'd just * fail the frame the normal way and have it permanently fail * via the normal aggregate completion path. */ static void ath_tx_tid_cleanup_frame(struct ath_softc *sc, struct ath_node *an, int tid, struct ath_buf *bf_head, ath_bufhead *bf_cq) { struct ath_tid *atid = &an->an_tid[tid]; struct ath_buf *bf, *bf_next; ATH_TX_LOCK_ASSERT(sc); /* * Remove this frame from the queue. */ ATH_TID_REMOVE(atid, bf_head, bf_list); /* * Loop over all the frames in the aggregate. */ bf = bf_head; while (bf != NULL) { bf_next = bf->bf_next; /* next aggregate frame, or NULL */ /* * If it's been added to the BAW we need to kick * it out of the BAW before we continue. * * XXX if it's an aggregate, assert that it's in the * BAW - we shouldn't have it be in an aggregate * otherwise! */ if (bf->bf_state.bfs_addedbaw) { ath_tx_update_baw(sc, an, atid, bf); bf->bf_state.bfs_dobaw = 0; } /* * Give it the default completion handler. */ bf->bf_comp = ath_tx_normal_comp; bf->bf_next = NULL; /* * Add it to the list to free. */ TAILQ_INSERT_TAIL(bf_cq, bf, bf_list); /* * Now advance to the next frame in the aggregate. */ bf = bf_next; } } /* * Performs transmit side cleanup when TID changes from aggregated to * unaggregated and during reassociation. * * For now, this just tosses everything from the TID software queue * whether or not it has been retried and marks the TID as * pending completion if there's anything for this TID queued to * the hardware. * * The caller is responsible for pausing the TID and unpausing the * TID if no cleanup was required. Otherwise the cleanup path will * unpause the TID once the last hardware queued frame is completed. */ static void ath_tx_tid_cleanup(struct ath_softc *sc, struct ath_node *an, int tid, ath_bufhead *bf_cq) { struct ath_tid *atid = &an->an_tid[tid]; struct ath_buf *bf, *bf_next; ATH_TX_LOCK_ASSERT(sc); DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, "%s: TID %d: called; inprogress=%d\n", __func__, tid, atid->cleanup_inprogress); /* * Move the filtered frames to the TX queue, before * we run off and discard/process things. */ /* XXX this is really quite inefficient */ while ((bf = ATH_TID_FILT_LAST(atid, ath_bufhead_s)) != NULL) { ATH_TID_FILT_REMOVE(atid, bf, bf_list); ATH_TID_INSERT_HEAD(atid, bf, bf_list); } /* * Update the frames in the software TX queue: * * + Discard retry frames in the queue * + Fix the completion function to be non-aggregate */ bf = ATH_TID_FIRST(atid); while (bf) { /* * Grab the next frame in the list, we may * be fiddling with the list. */ bf_next = TAILQ_NEXT(bf, bf_list); /* * Free the frame and all subframes. */ ath_tx_tid_cleanup_frame(sc, an, tid, bf, bf_cq); /* * Next frame! */ bf = bf_next; } /* * If there's anything in the hardware queue we wait * for the TID HWQ to empty. */ if (atid->hwq_depth > 0) { /* * XXX how about we kill atid->incomp, and instead * replace it with a macro that checks that atid->hwq_depth * is 0? */ atid->incomp = atid->hwq_depth; atid->cleanup_inprogress = 1; } if (atid->cleanup_inprogress) DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: TID %d: cleanup needed: %d packets\n", __func__, tid, atid->incomp); /* Owner now must free completed frames */ } static struct ath_buf * ath_tx_retry_clone(struct ath_softc *sc, struct ath_node *an, struct ath_tid *tid, struct ath_buf *bf) { struct ath_buf *nbf; int error; /* * Clone the buffer. This will handle the dma unmap and * copy the node reference to the new buffer. If this * works out, 'bf' will have no DMA mapping, no mbuf * pointer and no node reference. */ nbf = ath_buf_clone(sc, bf); #if 0 DPRINTF(sc, ATH_DEBUG_XMIT, "%s: ATH_BUF_BUSY; cloning\n", __func__); #endif if (nbf == NULL) { /* Failed to clone */ DPRINTF(sc, ATH_DEBUG_XMIT, "%s: failed to clone a busy buffer\n", __func__); return NULL; } /* Setup the dma for the new buffer */ error = ath_tx_dmasetup(sc, nbf, nbf->bf_m); if (error != 0) { DPRINTF(sc, ATH_DEBUG_XMIT, "%s: failed to setup dma for clone\n", __func__); /* * Put this at the head of the list, not tail; * that way it doesn't interfere with the * busy buffer logic (which uses the tail of * the list.) */ ATH_TXBUF_LOCK(sc); ath_returnbuf_head(sc, nbf); ATH_TXBUF_UNLOCK(sc); return NULL; } /* Update BAW if required, before we free the original buf */ if (bf->bf_state.bfs_dobaw) ath_tx_switch_baw_buf(sc, an, tid, bf, nbf); /* Free original buffer; return new buffer */ ath_freebuf(sc, bf); return nbf; } /* * Handle retrying an unaggregate frame in an aggregate * session. * * If too many retries occur, pause the TID, wait for * any further retransmits (as there's no reason why * non-aggregate frames in an aggregate session are * transmitted in-order; they just have to be in-BAW) * and then queue a BAR. */ static void ath_tx_aggr_retry_unaggr(struct ath_softc *sc, struct ath_buf *bf) { struct ieee80211_node *ni = bf->bf_node; struct ath_node *an = ATH_NODE(ni); int tid = bf->bf_state.bfs_tid; struct ath_tid *atid = &an->an_tid[tid]; struct ieee80211_tx_ampdu *tap; ATH_TX_LOCK(sc); tap = ath_tx_get_tx_tid(an, tid); /* * If the buffer is marked as busy, we can't directly * reuse it. Instead, try to clone the buffer. * If the clone is successful, recycle the old buffer. * If the clone is unsuccessful, set bfs_retries to max * to force the next bit of code to free the buffer * for us. */ if ((bf->bf_state.bfs_retries < SWMAX_RETRIES) && (bf->bf_flags & ATH_BUF_BUSY)) { struct ath_buf *nbf; nbf = ath_tx_retry_clone(sc, an, atid, bf); if (nbf) /* bf has been freed at this point */ bf = nbf; else bf->bf_state.bfs_retries = SWMAX_RETRIES + 1; } if (bf->bf_state.bfs_retries >= SWMAX_RETRIES) { DPRINTF(sc, ATH_DEBUG_SW_TX_RETRIES, "%s: exceeded retries; seqno %d\n", __func__, SEQNO(bf->bf_state.bfs_seqno)); sc->sc_stats.ast_tx_swretrymax++; /* Update BAW anyway */ if (bf->bf_state.bfs_dobaw) { ath_tx_update_baw(sc, an, atid, bf); if (! bf->bf_state.bfs_addedbaw) DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, "%s: wasn't added: seqno %d\n", __func__, SEQNO(bf->bf_state.bfs_seqno)); } bf->bf_state.bfs_dobaw = 0; /* Suspend the TX queue and get ready to send the BAR */ ath_tx_tid_bar_suspend(sc, atid); /* Send the BAR if there are no other frames waiting */ if (ath_tx_tid_bar_tx_ready(sc, atid)) ath_tx_tid_bar_tx(sc, atid); ATH_TX_UNLOCK(sc); /* Free buffer, bf is free after this call */ ath_tx_default_comp(sc, bf, 0); return; } /* * This increments the retry counter as well as * sets the retry flag in the ath_buf and packet * body. */ ath_tx_set_retry(sc, bf); sc->sc_stats.ast_tx_swretries++; /* * Insert this at the head of the queue, so it's * retried before any current/subsequent frames. */ ATH_TID_INSERT_HEAD(atid, bf, bf_list); ath_tx_tid_sched(sc, atid); /* Send the BAR if there are no other frames waiting */ if (ath_tx_tid_bar_tx_ready(sc, atid)) ath_tx_tid_bar_tx(sc, atid); ATH_TX_UNLOCK(sc); } /* * Common code for aggregate excessive retry/subframe retry. * If retrying, queues buffers to bf_q. If not, frees the * buffers. * * XXX should unify this with ath_tx_aggr_retry_unaggr() */ static int ath_tx_retry_subframe(struct ath_softc *sc, struct ath_buf *bf, ath_bufhead *bf_q) { struct ieee80211_node *ni = bf->bf_node; struct ath_node *an = ATH_NODE(ni); int tid = bf->bf_state.bfs_tid; struct ath_tid *atid = &an->an_tid[tid]; ATH_TX_LOCK_ASSERT(sc); /* XXX clr11naggr should be done for all subframes */ ath_hal_clr11n_aggr(sc->sc_ah, bf->bf_desc); ath_hal_set11nburstduration(sc->sc_ah, bf->bf_desc, 0); /* ath_hal_set11n_virtualmorefrag(sc->sc_ah, bf->bf_desc, 0); */ /* * If the buffer is marked as busy, we can't directly * reuse it. Instead, try to clone the buffer. * If the clone is successful, recycle the old buffer. * If the clone is unsuccessful, set bfs_retries to max * to force the next bit of code to free the buffer * for us. */ if ((bf->bf_state.bfs_retries < SWMAX_RETRIES) && (bf->bf_flags & ATH_BUF_BUSY)) { struct ath_buf *nbf; nbf = ath_tx_retry_clone(sc, an, atid, bf); if (nbf) /* bf has been freed at this point */ bf = nbf; else bf->bf_state.bfs_retries = SWMAX_RETRIES + 1; } if (bf->bf_state.bfs_retries >= SWMAX_RETRIES) { sc->sc_stats.ast_tx_swretrymax++; DPRINTF(sc, ATH_DEBUG_SW_TX_RETRIES, "%s: max retries: seqno %d\n", __func__, SEQNO(bf->bf_state.bfs_seqno)); ath_tx_update_baw(sc, an, atid, bf); if (!bf->bf_state.bfs_addedbaw) DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, "%s: wasn't added: seqno %d\n", __func__, SEQNO(bf->bf_state.bfs_seqno)); bf->bf_state.bfs_dobaw = 0; return 1; } ath_tx_set_retry(sc, bf); sc->sc_stats.ast_tx_swretries++; bf->bf_next = NULL; /* Just to make sure */ /* Clear the aggregate state */ bf->bf_state.bfs_aggr = 0; bf->bf_state.bfs_ndelim = 0; /* ??? needed? */ bf->bf_state.bfs_nframes = 1; TAILQ_INSERT_TAIL(bf_q, bf, bf_list); return 0; } /* * error pkt completion for an aggregate destination */ static void ath_tx_comp_aggr_error(struct ath_softc *sc, struct ath_buf *bf_first, struct ath_tid *tid) { struct ieee80211_node *ni = bf_first->bf_node; struct ath_node *an = ATH_NODE(ni); struct ath_buf *bf_next, *bf; ath_bufhead bf_q; int drops = 0; struct ieee80211_tx_ampdu *tap; ath_bufhead bf_cq; TAILQ_INIT(&bf_q); TAILQ_INIT(&bf_cq); /* * Update rate control - all frames have failed. * * XXX use the length in the first frame in the series; * XXX just so things are consistent for now. */ ath_tx_update_ratectrl(sc, ni, bf_first->bf_state.bfs_rc, &bf_first->bf_status.ds_txstat, bf_first->bf_state.bfs_pktlen, bf_first->bf_state.bfs_nframes, bf_first->bf_state.bfs_nframes); ATH_TX_LOCK(sc); tap = ath_tx_get_tx_tid(an, tid->tid); sc->sc_stats.ast_tx_aggr_failall++; /* Retry all subframes */ bf = bf_first; while (bf) { bf_next = bf->bf_next; bf->bf_next = NULL; /* Remove it from the aggr list */ sc->sc_stats.ast_tx_aggr_fail++; if (ath_tx_retry_subframe(sc, bf, &bf_q)) { drops++; bf->bf_next = NULL; TAILQ_INSERT_TAIL(&bf_cq, bf, bf_list); } bf = bf_next; } /* Prepend all frames to the beginning of the queue */ while ((bf = TAILQ_LAST(&bf_q, ath_bufhead_s)) != NULL) { TAILQ_REMOVE(&bf_q, bf, bf_list); ATH_TID_INSERT_HEAD(tid, bf, bf_list); } /* * Schedule the TID to be re-tried. */ ath_tx_tid_sched(sc, tid); /* * send bar if we dropped any frames * * Keep the txq lock held for now, as we need to ensure * that ni_txseqs[] is consistent (as it's being updated * in the ifnet TX context or raw TX context.) */ if (drops) { /* Suspend the TX queue and get ready to send the BAR */ ath_tx_tid_bar_suspend(sc, tid); } /* * Send BAR if required */ if (ath_tx_tid_bar_tx_ready(sc, tid)) ath_tx_tid_bar_tx(sc, tid); ATH_TX_UNLOCK(sc); /* Complete frames which errored out */ while ((bf = TAILQ_FIRST(&bf_cq)) != NULL) { TAILQ_REMOVE(&bf_cq, bf, bf_list); ath_tx_default_comp(sc, bf, 0); } } /* * Handle clean-up of packets from an aggregate list. * * There's no need to update the BAW here - the session is being * torn down. */ static void ath_tx_comp_cleanup_aggr(struct ath_softc *sc, struct ath_buf *bf_first) { struct ath_buf *bf, *bf_next; struct ieee80211_node *ni = bf_first->bf_node; struct ath_node *an = ATH_NODE(ni); int tid = bf_first->bf_state.bfs_tid; struct ath_tid *atid = &an->an_tid[tid]; ATH_TX_LOCK(sc); /* update incomp */ atid->incomp--; /* Update the BAW */ bf = bf_first; while (bf) { /* XXX refactor! */ if (bf->bf_state.bfs_dobaw) { ath_tx_update_baw(sc, an, atid, bf); if (!bf->bf_state.bfs_addedbaw) DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: wasn't added: seqno %d\n", __func__, SEQNO(bf->bf_state.bfs_seqno)); } bf = bf->bf_next; } if (atid->incomp == 0) { DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: TID %d: cleaned up! resume!\n", __func__, tid); atid->cleanup_inprogress = 0; ath_tx_tid_resume(sc, atid); } /* Send BAR if required */ /* XXX why would we send a BAR when transitioning to non-aggregation? */ /* * XXX TODO: we should likely just tear down the BAR state here, * rather than sending a BAR. */ if (ath_tx_tid_bar_tx_ready(sc, atid)) ath_tx_tid_bar_tx(sc, atid); ATH_TX_UNLOCK(sc); /* Handle frame completion as individual frames */ bf = bf_first; while (bf) { bf_next = bf->bf_next; bf->bf_next = NULL; ath_tx_default_comp(sc, bf, 1); bf = bf_next; } } /* * Handle completion of an set of aggregate frames. * * Note: the completion handler is the last descriptor in the aggregate, * not the last descriptor in the first frame. */ static void ath_tx_aggr_comp_aggr(struct ath_softc *sc, struct ath_buf *bf_first, int fail) { //struct ath_desc *ds = bf->bf_lastds; struct ieee80211_node *ni = bf_first->bf_node; struct ath_node *an = ATH_NODE(ni); int tid = bf_first->bf_state.bfs_tid; struct ath_tid *atid = &an->an_tid[tid]; struct ath_tx_status ts; struct ieee80211_tx_ampdu *tap; ath_bufhead bf_q; ath_bufhead bf_cq; int seq_st, tx_ok; int hasba, isaggr; uint32_t ba[2]; struct ath_buf *bf, *bf_next; int ba_index; int drops = 0; int nframes = 0, nbad = 0, nf; int pktlen; /* XXX there's too much on the stack? */ struct ath_rc_series rc[ATH_RC_NUM]; int txseq; DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: called; hwq_depth=%d\n", __func__, atid->hwq_depth); /* * Take a copy; this may be needed -after- bf_first * has been completed and freed. */ ts = bf_first->bf_status.ds_txstat; TAILQ_INIT(&bf_q); TAILQ_INIT(&bf_cq); /* The TID state is kept behind the TXQ lock */ ATH_TX_LOCK(sc); atid->hwq_depth--; if (atid->hwq_depth < 0) DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: hwq_depth < 0: %d\n", __func__, atid->hwq_depth); /* * If the TID is filtered, handle completing the filter * transition before potentially kicking it to the cleanup * function. * * XXX this is duplicate work, ew. */ if (atid->isfiltered) ath_tx_tid_filt_comp_complete(sc, atid); /* * Punt cleanup to the relevant function, not our problem now */ if (atid->cleanup_inprogress) { if (atid->isfiltered) DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: isfiltered=1, normal_comp?\n", __func__); ATH_TX_UNLOCK(sc); ath_tx_comp_cleanup_aggr(sc, bf_first); return; } /* * If the frame is filtered, transition to filtered frame * mode and add this to the filtered frame list. * * XXX TODO: figure out how this interoperates with * BAR, pause and cleanup states. */ if ((ts.ts_status & HAL_TXERR_FILT) || (ts.ts_status != 0 && atid->isfiltered)) { if (fail != 0) DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: isfiltered=1, fail=%d\n", __func__, fail); ath_tx_tid_filt_comp_aggr(sc, atid, bf_first, &bf_cq); /* Remove from BAW */ TAILQ_FOREACH_SAFE(bf, &bf_cq, bf_list, bf_next) { if (bf->bf_state.bfs_addedbaw) drops++; if (bf->bf_state.bfs_dobaw) { ath_tx_update_baw(sc, an, atid, bf); if (!bf->bf_state.bfs_addedbaw) DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: wasn't added: seqno %d\n", __func__, SEQNO(bf->bf_state.bfs_seqno)); } bf->bf_state.bfs_dobaw = 0; } /* * If any intermediate frames in the BAW were dropped when * handling filtering things, send a BAR. */ if (drops) ath_tx_tid_bar_suspend(sc, atid); /* * Finish up by sending a BAR if required and freeing * the frames outside of the TX lock. */ goto finish_send_bar; } /* * XXX for now, use the first frame in the aggregate for * XXX rate control completion; it's at least consistent. */ pktlen = bf_first->bf_state.bfs_pktlen; /* * Handle errors first! * * Here, handle _any_ error as a "exceeded retries" error. * Later on (when filtered frames are to be specially handled) * it'll have to be expanded. */ #if 0 if (ts.ts_status & HAL_TXERR_XRETRY) { #endif if (ts.ts_status != 0) { ATH_TX_UNLOCK(sc); ath_tx_comp_aggr_error(sc, bf_first, atid); return; } tap = ath_tx_get_tx_tid(an, tid); /* * extract starting sequence and block-ack bitmap */ /* XXX endian-ness of seq_st, ba? */ seq_st = ts.ts_seqnum; hasba = !! (ts.ts_flags & HAL_TX_BA); tx_ok = (ts.ts_status == 0); isaggr = bf_first->bf_state.bfs_aggr; ba[0] = ts.ts_ba_low; ba[1] = ts.ts_ba_high; /* * Copy the TX completion status and the rate control * series from the first descriptor, as it may be freed * before the rate control code can get its grubby fingers * into things. */ memcpy(rc, bf_first->bf_state.bfs_rc, sizeof(rc)); DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: txa_start=%d, tx_ok=%d, status=%.8x, flags=%.8x, " "isaggr=%d, seq_st=%d, hasba=%d, ba=%.8x, %.8x\n", __func__, tap->txa_start, tx_ok, ts.ts_status, ts.ts_flags, isaggr, seq_st, hasba, ba[0], ba[1]); /* * The reference driver doesn't do this; it simply ignores * this check in its entirety. * * I've seen this occur when using iperf to send traffic * out tid 1 - the aggregate frames are all marked as TID 1, * but the TXSTATUS has TID=0. So, let's just ignore this * check. */ #if 0 /* Occasionally, the MAC sends a tx status for the wrong TID. */ if (tid != ts.ts_tid) { DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: tid %d != hw tid %d\n", __func__, tid, ts.ts_tid); tx_ok = 0; } #endif /* AR5416 BA bug; this requires an interface reset */ if (isaggr && tx_ok && (! hasba)) { device_printf(sc->sc_dev, "%s: AR5416 bug: hasba=%d; txok=%d, isaggr=%d, " "seq_st=%d\n", __func__, hasba, tx_ok, isaggr, seq_st); /* XXX TODO: schedule an interface reset */ #ifdef ATH_DEBUG ath_printtxbuf(sc, bf_first, sc->sc_ac2q[atid->ac]->axq_qnum, 0, 0); #endif } /* * Walk the list of frames, figure out which ones were correctly * sent and which weren't. */ bf = bf_first; nf = bf_first->bf_state.bfs_nframes; /* bf_first is going to be invalid once this list is walked */ bf_first = NULL; /* * Walk the list of completed frames and determine * which need to be completed and which need to be * retransmitted. * * For completed frames, the completion functions need * to be called at the end of this function as the last * node reference may free the node. * * Finally, since the TXQ lock can't be held during the * completion callback (to avoid lock recursion), * the completion calls have to be done outside of the * lock. */ while (bf) { nframes++; ba_index = ATH_BA_INDEX(seq_st, SEQNO(bf->bf_state.bfs_seqno)); bf_next = bf->bf_next; bf->bf_next = NULL; /* Remove it from the aggr list */ DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: checking bf=%p seqno=%d; ack=%d\n", __func__, bf, SEQNO(bf->bf_state.bfs_seqno), ATH_BA_ISSET(ba, ba_index)); if (tx_ok && ATH_BA_ISSET(ba, ba_index)) { sc->sc_stats.ast_tx_aggr_ok++; ath_tx_update_baw(sc, an, atid, bf); bf->bf_state.bfs_dobaw = 0; if (!bf->bf_state.bfs_addedbaw) DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: wasn't added: seqno %d\n", __func__, SEQNO(bf->bf_state.bfs_seqno)); bf->bf_next = NULL; TAILQ_INSERT_TAIL(&bf_cq, bf, bf_list); } else { sc->sc_stats.ast_tx_aggr_fail++; if (ath_tx_retry_subframe(sc, bf, &bf_q)) { drops++; bf->bf_next = NULL; TAILQ_INSERT_TAIL(&bf_cq, bf, bf_list); } nbad++; } bf = bf_next; } /* * Now that the BAW updates have been done, unlock * * txseq is grabbed before the lock is released so we * have a consistent view of what -was- in the BAW. * Anything after this point will not yet have been * TXed. */ txseq = tap->txa_start; ATH_TX_UNLOCK(sc); if (nframes != nf) DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: num frames seen=%d; bf nframes=%d\n", __func__, nframes, nf); /* * Now we know how many frames were bad, call the rate * control code. */ if (fail == 0) ath_tx_update_ratectrl(sc, ni, rc, &ts, pktlen, nframes, nbad); /* * send bar if we dropped any frames */ if (drops) { /* Suspend the TX queue and get ready to send the BAR */ ATH_TX_LOCK(sc); ath_tx_tid_bar_suspend(sc, atid); ATH_TX_UNLOCK(sc); } DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: txa_start now %d\n", __func__, tap->txa_start); ATH_TX_LOCK(sc); /* Prepend all frames to the beginning of the queue */ while ((bf = TAILQ_LAST(&bf_q, ath_bufhead_s)) != NULL) { TAILQ_REMOVE(&bf_q, bf, bf_list); ATH_TID_INSERT_HEAD(atid, bf, bf_list); } /* * Reschedule to grab some further frames. */ ath_tx_tid_sched(sc, atid); /* * If the queue is filtered, re-schedule as required. * * This is required as there may be a subsequent TX descriptor * for this end-node that has CLRDMASK set, so it's quite possible * that a filtered frame will be followed by a non-filtered * (complete or otherwise) frame. * * XXX should we do this before we complete the frame? */ if (atid->isfiltered) ath_tx_tid_filt_comp_complete(sc, atid); finish_send_bar: /* * Send BAR if required */ if (ath_tx_tid_bar_tx_ready(sc, atid)) ath_tx_tid_bar_tx(sc, atid); ATH_TX_UNLOCK(sc); /* Do deferred completion */ while ((bf = TAILQ_FIRST(&bf_cq)) != NULL) { TAILQ_REMOVE(&bf_cq, bf, bf_list); ath_tx_default_comp(sc, bf, 0); } } /* * Handle completion of unaggregated frames in an ADDBA * session. * * Fail is set to 1 if the entry is being freed via a call to * ath_tx_draintxq(). */ static void ath_tx_aggr_comp_unaggr(struct ath_softc *sc, struct ath_buf *bf, int fail) { struct ieee80211_node *ni = bf->bf_node; struct ath_node *an = ATH_NODE(ni); int tid = bf->bf_state.bfs_tid; struct ath_tid *atid = &an->an_tid[tid]; struct ath_tx_status ts; int drops = 0; /* * Take a copy of this; filtering/cloning the frame may free the * bf pointer. */ ts = bf->bf_status.ds_txstat; /* * Update rate control status here, before we possibly * punt to retry or cleanup. * * Do it outside of the TXQ lock. */ if (fail == 0 && ((bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 0)) ath_tx_update_ratectrl(sc, ni, bf->bf_state.bfs_rc, &bf->bf_status.ds_txstat, bf->bf_state.bfs_pktlen, 1, (ts.ts_status == 0) ? 0 : 1); /* * This is called early so atid->hwq_depth can be tracked. * This unfortunately means that it's released and regrabbed * during retry and cleanup. That's rather inefficient. */ ATH_TX_LOCK(sc); if (tid == IEEE80211_NONQOS_TID) DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: TID=16!\n", __func__); DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: bf=%p: tid=%d, hwq_depth=%d, seqno=%d\n", __func__, bf, bf->bf_state.bfs_tid, atid->hwq_depth, SEQNO(bf->bf_state.bfs_seqno)); atid->hwq_depth--; if (atid->hwq_depth < 0) DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: hwq_depth < 0: %d\n", __func__, atid->hwq_depth); /* * If the TID is filtered, handle completing the filter * transition before potentially kicking it to the cleanup * function. */ if (atid->isfiltered) ath_tx_tid_filt_comp_complete(sc, atid); /* * If a cleanup is in progress, punt to comp_cleanup; * rather than handling it here. It's thus their * responsibility to clean up, call the completion * function in net80211, etc. */ if (atid->cleanup_inprogress) { if (atid->isfiltered) DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: isfiltered=1, normal_comp?\n", __func__); ATH_TX_UNLOCK(sc); DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: cleanup_unaggr\n", __func__); ath_tx_comp_cleanup_unaggr(sc, bf); return; } /* * XXX TODO: how does cleanup, BAR and filtered frame handling * overlap? * * If the frame is filtered OR if it's any failure but * the TID is filtered, the frame must be added to the * filtered frame list. * * However - a busy buffer can't be added to the filtered * list as it will end up being recycled without having * been made available for the hardware. */ if ((ts.ts_status & HAL_TXERR_FILT) || (ts.ts_status != 0 && atid->isfiltered)) { int freeframe; if (fail != 0) DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: isfiltered=1, fail=%d\n", __func__, fail); freeframe = ath_tx_tid_filt_comp_single(sc, atid, bf); /* * If freeframe=0 then bf is no longer ours; don't * touch it. */ if (freeframe) { /* Remove from BAW */ if (bf->bf_state.bfs_addedbaw) drops++; if (bf->bf_state.bfs_dobaw) { ath_tx_update_baw(sc, an, atid, bf); if (!bf->bf_state.bfs_addedbaw) DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: wasn't added: seqno %d\n", __func__, SEQNO(bf->bf_state.bfs_seqno)); } bf->bf_state.bfs_dobaw = 0; } /* * If the frame couldn't be filtered, treat it as a drop and * prepare to send a BAR. */ if (freeframe && drops) ath_tx_tid_bar_suspend(sc, atid); /* * Send BAR if required */ if (ath_tx_tid_bar_tx_ready(sc, atid)) ath_tx_tid_bar_tx(sc, atid); ATH_TX_UNLOCK(sc); /* * If freeframe is set, then the frame couldn't be * cloned and bf is still valid. Just complete/free it. */ if (freeframe) ath_tx_default_comp(sc, bf, fail); return; } /* * Don't bother with the retry check if all frames * are being failed (eg during queue deletion.) */ #if 0 if (fail == 0 && ts->ts_status & HAL_TXERR_XRETRY) { #endif if (fail == 0 && ts.ts_status != 0) { ATH_TX_UNLOCK(sc); DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: retry_unaggr\n", __func__); ath_tx_aggr_retry_unaggr(sc, bf); return; } /* Success? Complete */ DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: TID=%d, seqno %d\n", __func__, tid, SEQNO(bf->bf_state.bfs_seqno)); if (bf->bf_state.bfs_dobaw) { ath_tx_update_baw(sc, an, atid, bf); bf->bf_state.bfs_dobaw = 0; if (!bf->bf_state.bfs_addedbaw) DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: wasn't added: seqno %d\n", __func__, SEQNO(bf->bf_state.bfs_seqno)); } /* * If the queue is filtered, re-schedule as required. * * This is required as there may be a subsequent TX descriptor * for this end-node that has CLRDMASK set, so it's quite possible * that a filtered frame will be followed by a non-filtered * (complete or otherwise) frame. * * XXX should we do this before we complete the frame? */ if (atid->isfiltered) ath_tx_tid_filt_comp_complete(sc, atid); /* * Send BAR if required */ if (ath_tx_tid_bar_tx_ready(sc, atid)) ath_tx_tid_bar_tx(sc, atid); ATH_TX_UNLOCK(sc); ath_tx_default_comp(sc, bf, fail); /* bf is freed at this point */ } void ath_tx_aggr_comp(struct ath_softc *sc, struct ath_buf *bf, int fail) { if (bf->bf_state.bfs_aggr) ath_tx_aggr_comp_aggr(sc, bf, fail); else ath_tx_aggr_comp_unaggr(sc, bf, fail); } /* * Schedule some packets from the given node/TID to the hardware. * * This is the aggregate version. */ void ath_tx_tid_hw_queue_aggr(struct ath_softc *sc, struct ath_node *an, struct ath_tid *tid) { struct ath_buf *bf; struct ath_txq *txq = sc->sc_ac2q[tid->ac]; struct ieee80211_tx_ampdu *tap; ATH_AGGR_STATUS status; ath_bufhead bf_q; DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: tid=%d\n", __func__, tid->tid); ATH_TX_LOCK_ASSERT(sc); /* * XXX TODO: If we're called for a queue that we're leaking frames to, * ensure we only leak one. */ tap = ath_tx_get_tx_tid(an, tid->tid); if (tid->tid == IEEE80211_NONQOS_TID) DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: called for TID=NONQOS_TID?\n", __func__); for (;;) { status = ATH_AGGR_DONE; /* * If the upper layer has paused the TID, don't * queue any further packets. * * This can also occur from the completion task because * of packet loss; but as its serialised with this code, * it won't "appear" half way through queuing packets. */ if (! ath_tx_tid_can_tx_or_sched(sc, tid)) break; bf = ATH_TID_FIRST(tid); if (bf == NULL) { break; } /* * If the packet doesn't fall within the BAW (eg a NULL * data frame), schedule it directly; continue. */ if (! bf->bf_state.bfs_dobaw) { DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: non-baw packet\n", __func__); ATH_TID_REMOVE(tid, bf, bf_list); if (bf->bf_state.bfs_nframes > 1) DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: aggr=%d, nframes=%d\n", __func__, bf->bf_state.bfs_aggr, bf->bf_state.bfs_nframes); /* * This shouldn't happen - such frames shouldn't * ever have been queued as an aggregate in the * first place. However, make sure the fields * are correctly setup just to be totally sure. */ bf->bf_state.bfs_aggr = 0; bf->bf_state.bfs_nframes = 1; /* Update CLRDMASK just before this frame is queued */ ath_tx_update_clrdmask(sc, tid, bf); ath_tx_do_ratelookup(sc, bf); ath_tx_calc_duration(sc, bf); ath_tx_calc_protection(sc, bf); ath_tx_set_rtscts(sc, bf); ath_tx_rate_fill_rcflags(sc, bf); ath_tx_setds(sc, bf); ath_hal_clr11n_aggr(sc->sc_ah, bf->bf_desc); sc->sc_aggr_stats.aggr_nonbaw_pkt++; /* Queue the packet; continue */ goto queuepkt; } TAILQ_INIT(&bf_q); /* * Do a rate control lookup on the first frame in the * list. The rate control code needs that to occur * before it can determine whether to TX. * It's inaccurate because the rate control code doesn't * really "do" aggregate lookups, so it only considers * the size of the first frame. */ ath_tx_do_ratelookup(sc, bf); bf->bf_state.bfs_rc[3].rix = 0; bf->bf_state.bfs_rc[3].tries = 0; ath_tx_calc_duration(sc, bf); ath_tx_calc_protection(sc, bf); ath_tx_set_rtscts(sc, bf); ath_tx_rate_fill_rcflags(sc, bf); status = ath_tx_form_aggr(sc, an, tid, &bf_q); DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: ath_tx_form_aggr() status=%d\n", __func__, status); /* * No frames to be picked up - out of BAW */ if (TAILQ_EMPTY(&bf_q)) break; /* * This assumes that the descriptor list in the ath_bufhead * are already linked together via bf_next pointers. */ bf = TAILQ_FIRST(&bf_q); if (status == ATH_AGGR_8K_LIMITED) sc->sc_aggr_stats.aggr_rts_aggr_limited++; /* * If it's the only frame send as non-aggregate * assume that ath_tx_form_aggr() has checked * whether it's in the BAW and added it appropriately. */ if (bf->bf_state.bfs_nframes == 1) { DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: single-frame aggregate\n", __func__); /* Update CLRDMASK just before this frame is queued */ ath_tx_update_clrdmask(sc, tid, bf); bf->bf_state.bfs_aggr = 0; bf->bf_state.bfs_ndelim = 0; ath_tx_setds(sc, bf); ath_hal_clr11n_aggr(sc->sc_ah, bf->bf_desc); if (status == ATH_AGGR_BAW_CLOSED) sc->sc_aggr_stats.aggr_baw_closed_single_pkt++; else sc->sc_aggr_stats.aggr_single_pkt++; } else { DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: multi-frame aggregate: %d frames, " "length %d\n", __func__, bf->bf_state.bfs_nframes, bf->bf_state.bfs_al); bf->bf_state.bfs_aggr = 1; sc->sc_aggr_stats.aggr_pkts[bf->bf_state.bfs_nframes]++; sc->sc_aggr_stats.aggr_aggr_pkt++; /* Update CLRDMASK just before this frame is queued */ ath_tx_update_clrdmask(sc, tid, bf); /* * Calculate the duration/protection as required. */ ath_tx_calc_duration(sc, bf); ath_tx_calc_protection(sc, bf); /* * Update the rate and rtscts information based on the * rate decision made by the rate control code; * the first frame in the aggregate needs it. */ ath_tx_set_rtscts(sc, bf); /* * Setup the relevant descriptor fields * for aggregation. The first descriptor * already points to the rest in the chain. */ ath_tx_setds_11n(sc, bf); } queuepkt: /* Set completion handler, multi-frame aggregate or not */ bf->bf_comp = ath_tx_aggr_comp; if (bf->bf_state.bfs_tid == IEEE80211_NONQOS_TID) DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: TID=16?\n", __func__); /* * Update leak count and frame config if were leaking frames. * * XXX TODO: it should update all frames in an aggregate * correctly! */ ath_tx_leak_count_update(sc, tid, bf); /* Punt to txq */ ath_tx_handoff(sc, txq, bf); /* Track outstanding buffer count to hardware */ /* aggregates are "one" buffer */ tid->hwq_depth++; /* * Break out if ath_tx_form_aggr() indicated * there can't be any further progress (eg BAW is full.) * Checking for an empty txq is done above. * * XXX locking on txq here? */ /* XXX TXQ locking */ if (txq->axq_aggr_depth >= sc->sc_hwq_limit_aggr || (status == ATH_AGGR_BAW_CLOSED || status == ATH_AGGR_LEAK_CLOSED)) break; } } /* * Schedule some packets from the given node/TID to the hardware. * * XXX TODO: this routine doesn't enforce the maximum TXQ depth. * It just dumps frames into the TXQ. We should limit how deep * the transmit queue can grow for frames dispatched to the given * TXQ. * * To avoid locking issues, either we need to own the TXQ lock * at this point, or we need to pass in the maximum frame count * from the caller. */ void ath_tx_tid_hw_queue_norm(struct ath_softc *sc, struct ath_node *an, struct ath_tid *tid) { struct ath_buf *bf; struct ath_txq *txq = sc->sc_ac2q[tid->ac]; DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: node %p: TID %d: called\n", __func__, an, tid->tid); ATH_TX_LOCK_ASSERT(sc); /* Check - is AMPDU pending or running? then print out something */ if (ath_tx_ampdu_pending(sc, an, tid->tid)) DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: tid=%d, ampdu pending?\n", __func__, tid->tid); if (ath_tx_ampdu_running(sc, an, tid->tid)) DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: tid=%d, ampdu running?\n", __func__, tid->tid); for (;;) { /* * If the upper layers have paused the TID, don't * queue any further packets. * * XXX if we are leaking frames, make sure we decrement * that counter _and_ we continue here. */ if (! ath_tx_tid_can_tx_or_sched(sc, tid)) break; bf = ATH_TID_FIRST(tid); if (bf == NULL) { break; } ATH_TID_REMOVE(tid, bf, bf_list); /* Sanity check! */ if (tid->tid != bf->bf_state.bfs_tid) { DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: bfs_tid %d !=" " tid %d\n", __func__, bf->bf_state.bfs_tid, tid->tid); } /* Normal completion handler */ bf->bf_comp = ath_tx_normal_comp; /* * Override this for now, until the non-aggregate * completion handler correctly handles software retransmits. */ bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK; /* Update CLRDMASK just before this frame is queued */ ath_tx_update_clrdmask(sc, tid, bf); /* Program descriptors + rate control */ ath_tx_do_ratelookup(sc, bf); ath_tx_calc_duration(sc, bf); ath_tx_calc_protection(sc, bf); ath_tx_set_rtscts(sc, bf); ath_tx_rate_fill_rcflags(sc, bf); ath_tx_setds(sc, bf); /* * Update the current leak count if * we're leaking frames; and set the * MORE flag as appropriate. */ ath_tx_leak_count_update(sc, tid, bf); /* Track outstanding buffer count to hardware */ /* aggregates are "one" buffer */ tid->hwq_depth++; /* Punt to hardware or software txq */ ath_tx_handoff(sc, txq, bf); } } /* * Schedule some packets to the given hardware queue. * * This function walks the list of TIDs (ie, ath_node TIDs * with queued traffic) and attempts to schedule traffic * from them. * * TID scheduling is implemented as a FIFO, with TIDs being * added to the end of the queue after some frames have been * scheduled. */ void ath_txq_sched(struct ath_softc *sc, struct ath_txq *txq) { struct ath_tid *tid, *next, *last; ATH_TX_LOCK_ASSERT(sc); /* * Don't schedule if the hardware queue is busy. * This (hopefully) gives some more time to aggregate * some packets in the aggregation queue. * * XXX It doesn't stop a parallel sender from sneaking * in transmitting a frame! */ /* XXX TXQ locking */ if (txq->axq_aggr_depth + txq->fifo.axq_depth >= sc->sc_hwq_limit_aggr) { sc->sc_aggr_stats.aggr_sched_nopkt++; return; } if (txq->axq_depth >= sc->sc_hwq_limit_nonaggr) { sc->sc_aggr_stats.aggr_sched_nopkt++; return; } last = TAILQ_LAST(&txq->axq_tidq, axq_t_s); TAILQ_FOREACH_SAFE(tid, &txq->axq_tidq, axq_qelem, next) { /* * Suspend paused queues here; they'll be resumed * once the addba completes or times out. */ DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: tid=%d, paused=%d\n", __func__, tid->tid, tid->paused); ath_tx_tid_unsched(sc, tid); /* * This node may be in power-save and we're leaking * a frame; be careful. */ if (! ath_tx_tid_can_tx_or_sched(sc, tid)) { goto loop_done; } if (ath_tx_ampdu_running(sc, tid->an, tid->tid)) ath_tx_tid_hw_queue_aggr(sc, tid->an, tid); else ath_tx_tid_hw_queue_norm(sc, tid->an, tid); /* Not empty? Re-schedule */ if (tid->axq_depth != 0) ath_tx_tid_sched(sc, tid); /* * Give the software queue time to aggregate more * packets. If we aren't running aggregation then * we should still limit the hardware queue depth. */ /* XXX TXQ locking */ if (txq->axq_aggr_depth + txq->fifo.axq_depth >= sc->sc_hwq_limit_aggr) { break; } if (txq->axq_depth >= sc->sc_hwq_limit_nonaggr) { break; } loop_done: /* * If this was the last entry on the original list, stop. * Otherwise nodes that have been rescheduled onto the end * of the TID FIFO list will just keep being rescheduled. * * XXX What should we do about nodes that were paused * but are pending a leaking frame in response to a ps-poll? * They'll be put at the front of the list; so they'll * prematurely trigger this condition! Ew. */ if (tid == last) break; } } /* * TX addba handling */ /* * Return net80211 TID struct pointer, or NULL for none */ struct ieee80211_tx_ampdu * ath_tx_get_tx_tid(struct ath_node *an, int tid) { struct ieee80211_node *ni = &an->an_node; struct ieee80211_tx_ampdu *tap; if (tid == IEEE80211_NONQOS_TID) return NULL; tap = &ni->ni_tx_ampdu[tid]; return tap; } /* * Is AMPDU-TX running? */ static int ath_tx_ampdu_running(struct ath_softc *sc, struct ath_node *an, int tid) { struct ieee80211_tx_ampdu *tap; if (tid == IEEE80211_NONQOS_TID) return 0; tap = ath_tx_get_tx_tid(an, tid); if (tap == NULL) return 0; /* Not valid; default to not running */ return !! (tap->txa_flags & IEEE80211_AGGR_RUNNING); } /* * Is AMPDU-TX negotiation pending? */ static int ath_tx_ampdu_pending(struct ath_softc *sc, struct ath_node *an, int tid) { struct ieee80211_tx_ampdu *tap; if (tid == IEEE80211_NONQOS_TID) return 0; tap = ath_tx_get_tx_tid(an, tid); if (tap == NULL) return 0; /* Not valid; default to not pending */ return !! (tap->txa_flags & IEEE80211_AGGR_XCHGPEND); } /* * Is AMPDU-TX pending for the given TID? */ /* * Method to handle sending an ADDBA request. * * We tap this so the relevant flags can be set to pause the TID * whilst waiting for the response. * * XXX there's no timeout handler we can override? */ int ath_addba_request(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, int dialogtoken, int baparamset, int batimeout) { struct ath_softc *sc = ni->ni_ic->ic_softc; int tid = tap->txa_tid; struct ath_node *an = ATH_NODE(ni); struct ath_tid *atid = &an->an_tid[tid]; /* * XXX danger Will Robinson! * * Although the taskqueue may be running and scheduling some more * packets, these should all be _before_ the addba sequence number. * However, net80211 will keep self-assigning sequence numbers * until addba has been negotiated. * * In the past, these packets would be "paused" (which still works * fine, as they're being scheduled to the driver in the same * serialised method which is calling the addba request routine) * and when the aggregation session begins, they'll be dequeued * as aggregate packets and added to the BAW. However, now there's * a "bf->bf_state.bfs_dobaw" flag, and this isn't set for these * packets. Thus they never get included in the BAW tracking and * this can cause the initial burst of packets after the addba * negotiation to "hang", as they quickly fall outside the BAW. * * The "eventual" solution should be to tag these packets with * dobaw. Although net80211 has given us a sequence number, * it'll be "after" the left edge of the BAW and thus it'll * fall within it. */ ATH_TX_LOCK(sc); /* * This is a bit annoying. Until net80211 HT code inherits some * (any) locking, we may have this called in parallel BUT only * one response/timeout will be called. Grr. */ if (atid->addba_tx_pending == 0) { ath_tx_tid_pause(sc, atid); atid->addba_tx_pending = 1; } ATH_TX_UNLOCK(sc); DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: %6D: called; dialogtoken=%d, baparamset=%d, batimeout=%d\n", __func__, ni->ni_macaddr, ":", dialogtoken, baparamset, batimeout); DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: txa_start=%d, ni_txseqs=%d\n", __func__, tap->txa_start, ni->ni_txseqs[tid]); return sc->sc_addba_request(ni, tap, dialogtoken, baparamset, batimeout); } /* * Handle an ADDBA response. * * We unpause the queue so TX'ing can resume. * * Any packets TX'ed from this point should be "aggregate" (whether * aggregate or not) so the BAW is updated. * * Note! net80211 keeps self-assigning sequence numbers until * ampdu is negotiated. This means the initially-negotiated BAW left * edge won't match the ni->ni_txseq. * * So, being very dirty, the BAW left edge is "slid" here to match * ni->ni_txseq. * * What likely SHOULD happen is that all packets subsequent to the * addba request should be tagged as aggregate and queued as non-aggregate * frames; thus updating the BAW. For now though, I'll just slide the * window. */ int ath_addba_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, int status, int code, int batimeout) { struct ath_softc *sc = ni->ni_ic->ic_softc; int tid = tap->txa_tid; struct ath_node *an = ATH_NODE(ni); struct ath_tid *atid = &an->an_tid[tid]; int r; DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: %6D: called; status=%d, code=%d, batimeout=%d\n", __func__, ni->ni_macaddr, ":", status, code, batimeout); DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: txa_start=%d, ni_txseqs=%d\n", __func__, tap->txa_start, ni->ni_txseqs[tid]); /* * Call this first, so the interface flags get updated * before the TID is unpaused. Otherwise a race condition * exists where the unpaused TID still doesn't yet have * IEEE80211_AGGR_RUNNING set. */ r = sc->sc_addba_response(ni, tap, status, code, batimeout); ATH_TX_LOCK(sc); atid->addba_tx_pending = 0; /* * XXX dirty! * Slide the BAW left edge to wherever net80211 left it for us. * Read above for more information. */ tap->txa_start = ni->ni_txseqs[tid]; ath_tx_tid_resume(sc, atid); ATH_TX_UNLOCK(sc); return r; } /* * Stop ADDBA on a queue. * * This can be called whilst BAR TX is currently active on the queue, * so make sure this is unblocked before continuing. */ void ath_addba_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) { struct ath_softc *sc = ni->ni_ic->ic_softc; int tid = tap->txa_tid; struct ath_node *an = ATH_NODE(ni); struct ath_tid *atid = &an->an_tid[tid]; ath_bufhead bf_cq; struct ath_buf *bf; DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: %6D: called\n", __func__, ni->ni_macaddr, ":"); /* * Pause TID traffic early, so there aren't any races * Unblock the pending BAR held traffic, if it's currently paused. */ ATH_TX_LOCK(sc); ath_tx_tid_pause(sc, atid); if (atid->bar_wait) { /* * bar_unsuspend() expects bar_tx == 1, as it should be * called from the TX completion path. This quietens * the warning. It's cleared for us anyway. */ atid->bar_tx = 1; ath_tx_tid_bar_unsuspend(sc, atid); } ATH_TX_UNLOCK(sc); /* There's no need to hold the TXQ lock here */ sc->sc_addba_stop(ni, tap); /* * ath_tx_tid_cleanup will resume the TID if possible, otherwise * it'll set the cleanup flag, and it'll be unpaused once * things have been cleaned up. */ TAILQ_INIT(&bf_cq); ATH_TX_LOCK(sc); /* * In case there's a followup call to this, only call it * if we don't have a cleanup in progress. * * Since we've paused the queue above, we need to make * sure we unpause if there's already a cleanup in * progress - it means something else is also doing * this stuff, so we don't need to also keep it paused. */ if (atid->cleanup_inprogress) { ath_tx_tid_resume(sc, atid); } else { ath_tx_tid_cleanup(sc, an, tid, &bf_cq); /* * Unpause the TID if no cleanup is required. */ if (! atid->cleanup_inprogress) ath_tx_tid_resume(sc, atid); } ATH_TX_UNLOCK(sc); /* Handle completing frames and fail them */ while ((bf = TAILQ_FIRST(&bf_cq)) != NULL) { TAILQ_REMOVE(&bf_cq, bf, bf_list); ath_tx_default_comp(sc, bf, 1); } } /* * Handle a node reassociation. * * We may have a bunch of frames queued to the hardware; those need * to be marked as cleanup. */ void ath_tx_node_reassoc(struct ath_softc *sc, struct ath_node *an) { struct ath_tid *tid; int i; ath_bufhead bf_cq; struct ath_buf *bf; TAILQ_INIT(&bf_cq); ATH_TX_UNLOCK_ASSERT(sc); ATH_TX_LOCK(sc); for (i = 0; i < IEEE80211_TID_SIZE; i++) { tid = &an->an_tid[i]; if (tid->hwq_depth == 0) continue; DPRINTF(sc, ATH_DEBUG_NODE, "%s: %6D: TID %d: cleaning up TID\n", __func__, an->an_node.ni_macaddr, ":", i); /* * In case there's a followup call to this, only call it * if we don't have a cleanup in progress. */ if (! tid->cleanup_inprogress) { ath_tx_tid_pause(sc, tid); ath_tx_tid_cleanup(sc, an, i, &bf_cq); /* * Unpause the TID if no cleanup is required. */ if (! tid->cleanup_inprogress) ath_tx_tid_resume(sc, tid); } } ATH_TX_UNLOCK(sc); /* Handle completing frames and fail them */ while ((bf = TAILQ_FIRST(&bf_cq)) != NULL) { TAILQ_REMOVE(&bf_cq, bf, bf_list); ath_tx_default_comp(sc, bf, 1); } } /* * Note: net80211 bar_timeout() doesn't call this function on BAR failure; * it simply tears down the aggregation session. Ew. * * It however will call ieee80211_ampdu_stop() which will call * ic->ic_addba_stop(). * * XXX This uses a hard-coded max BAR count value; the whole * XXX BAR TX success or failure should be better handled! */ void ath_bar_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, int status) { struct ath_softc *sc = ni->ni_ic->ic_softc; int tid = tap->txa_tid; struct ath_node *an = ATH_NODE(ni); struct ath_tid *atid = &an->an_tid[tid]; int attempts = tap->txa_attempts; int old_txa_start; DPRINTF(sc, ATH_DEBUG_SW_TX_BAR, "%s: %6D: called; txa_tid=%d, atid->tid=%d, status=%d, attempts=%d, txa_start=%d, txa_seqpending=%d\n", __func__, ni->ni_macaddr, ":", tap->txa_tid, atid->tid, status, attempts, tap->txa_start, tap->txa_seqpending); /* Note: This may update the BAW details */ /* * XXX What if this does slide the BAW along? We need to somehow * XXX either fix things when it does happen, or prevent the * XXX seqpending value to be anything other than exactly what * XXX the hell we want! * * XXX So for now, how I do this inside the TX lock for now * XXX and just correct it afterwards? The below condition should * XXX never happen and if it does I need to fix all kinds of things. */ ATH_TX_LOCK(sc); old_txa_start = tap->txa_start; sc->sc_bar_response(ni, tap, status); if (tap->txa_start != old_txa_start) { device_printf(sc->sc_dev, "%s: tid=%d; txa_start=%d, old=%d, adjusting\n", __func__, tid, tap->txa_start, old_txa_start); } tap->txa_start = old_txa_start; ATH_TX_UNLOCK(sc); /* Unpause the TID */ /* * XXX if this is attempt=50, the TID will be downgraded * XXX to a non-aggregate session. So we must unpause the * XXX TID here or it'll never be done. * * Also, don't call it if bar_tx/bar_wait are 0; something * has beaten us to the punch? (XXX figure out what?) */ if (status == 0 || attempts == 50) { ATH_TX_LOCK(sc); if (atid->bar_tx == 0 || atid->bar_wait == 0) DPRINTF(sc, ATH_DEBUG_SW_TX_BAR, "%s: huh? bar_tx=%d, bar_wait=%d\n", __func__, atid->bar_tx, atid->bar_wait); else ath_tx_tid_bar_unsuspend(sc, atid); ATH_TX_UNLOCK(sc); } } /* * This is called whenever the pending ADDBA request times out. * Unpause and reschedule the TID. */ void ath_addba_response_timeout(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) { struct ath_softc *sc = ni->ni_ic->ic_softc; int tid = tap->txa_tid; struct ath_node *an = ATH_NODE(ni); struct ath_tid *atid = &an->an_tid[tid]; DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: %6D: TID=%d, called; resuming\n", __func__, ni->ni_macaddr, ":", tid); ATH_TX_LOCK(sc); atid->addba_tx_pending = 0; ATH_TX_UNLOCK(sc); /* Note: This updates the aggregate state to (again) pending */ sc->sc_addba_response_timeout(ni, tap); /* Unpause the TID; which reschedules it */ ATH_TX_LOCK(sc); ath_tx_tid_resume(sc, atid); ATH_TX_UNLOCK(sc); } /* * Check if a node is asleep or not. */ int ath_tx_node_is_asleep(struct ath_softc *sc, struct ath_node *an) { ATH_TX_LOCK_ASSERT(sc); return (an->an_is_powersave); } /* * Mark a node as currently "in powersaving." * This suspends all traffic on the node. * * This must be called with the node/tx locks free. * * XXX TODO: the locking silliness below is due to how the node * locking currently works. Right now, the node lock is grabbed * to do rate control lookups and these are done with the TX * queue lock held. This means the node lock can't be grabbed * first here or a LOR will occur. * * Eventually (hopefully!) the TX path code will only grab * the TXQ lock when transmitting and the ath_node lock when * doing node/TID operations. There are other complications - * the sched/unsched operations involve walking the per-txq * 'active tid' list and this requires both locks to be held. */ void ath_tx_node_sleep(struct ath_softc *sc, struct ath_node *an) { struct ath_tid *atid; struct ath_txq *txq; int tid; ATH_TX_UNLOCK_ASSERT(sc); /* Suspend all traffic on the node */ ATH_TX_LOCK(sc); if (an->an_is_powersave) { DPRINTF(sc, ATH_DEBUG_XMIT, "%s: %6D: node was already asleep!\n", __func__, an->an_node.ni_macaddr, ":"); ATH_TX_UNLOCK(sc); return; } for (tid = 0; tid < IEEE80211_TID_SIZE; tid++) { atid = &an->an_tid[tid]; txq = sc->sc_ac2q[atid->ac]; ath_tx_tid_pause(sc, atid); } /* Mark node as in powersaving */ an->an_is_powersave = 1; ATH_TX_UNLOCK(sc); } /* * Mark a node as currently "awake." * This resumes all traffic to the node. */ void ath_tx_node_wakeup(struct ath_softc *sc, struct ath_node *an) { struct ath_tid *atid; struct ath_txq *txq; int tid; ATH_TX_UNLOCK_ASSERT(sc); ATH_TX_LOCK(sc); /* !? */ if (an->an_is_powersave == 0) { ATH_TX_UNLOCK(sc); DPRINTF(sc, ATH_DEBUG_XMIT, "%s: an=%p: node was already awake\n", __func__, an); return; } /* Mark node as awake */ an->an_is_powersave = 0; /* * Clear any pending leaked frame requests */ an->an_leak_count = 0; for (tid = 0; tid < IEEE80211_TID_SIZE; tid++) { atid = &an->an_tid[tid]; txq = sc->sc_ac2q[atid->ac]; ath_tx_tid_resume(sc, atid); } ATH_TX_UNLOCK(sc); } static int ath_legacy_dma_txsetup(struct ath_softc *sc) { /* nothing new needed */ return (0); } static int ath_legacy_dma_txteardown(struct ath_softc *sc) { /* nothing new needed */ return (0); } void ath_xmit_setup_legacy(struct ath_softc *sc) { /* * For now, just set the descriptor length to sizeof(ath_desc); * worry about extracting the real length out of the HAL later. */ sc->sc_tx_desclen = sizeof(struct ath_desc); sc->sc_tx_statuslen = sizeof(struct ath_desc); sc->sc_tx_nmaps = 1; /* only one buffer per TX desc */ sc->sc_tx.xmit_setup = ath_legacy_dma_txsetup; sc->sc_tx.xmit_teardown = ath_legacy_dma_txteardown; sc->sc_tx.xmit_attach_comp_func = ath_legacy_attach_comp_func; sc->sc_tx.xmit_dma_restart = ath_legacy_tx_dma_restart; sc->sc_tx.xmit_handoff = ath_legacy_xmit_handoff; sc->sc_tx.xmit_drain = ath_legacy_tx_drain; } Index: head/sys/dev/ath/if_athvar.h =================================================================== --- head/sys/dev/ath/if_athvar.h (revision 298938) +++ head/sys/dev/ath/if_athvar.h (revision 298939) @@ -1,1507 +1,1507 @@ /*- * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * * $FreeBSD$ */ /* * Defintions for the Atheros Wireless LAN controller driver. */ #ifndef _DEV_ATH_ATHVAR_H #define _DEV_ATH_ATHVAR_H #include #include #include #include #include #include #ifdef ATH_DEBUG_ALQ #include #endif #define ATH_TIMEOUT 1000 /* * There is a separate TX ath_buf pool for management frames. * This ensures that management frames such as probe responses * and BAR frames can be transmitted during periods of high * TX activity. */ #define ATH_MGMT_TXBUF 32 /* * 802.11n requires more TX and RX buffers to do AMPDU. */ #ifdef ATH_ENABLE_11N #define ATH_TXBUF 512 #define ATH_RXBUF 512 #endif #ifndef ATH_RXBUF #define ATH_RXBUF 40 /* number of RX buffers */ #endif #ifndef ATH_TXBUF #define ATH_TXBUF 200 /* number of TX buffers */ #endif #define ATH_BCBUF 4 /* number of beacon buffers */ #define ATH_TXDESC 10 /* number of descriptors per buffer */ #define ATH_TXMAXTRY 11 /* max number of transmit attempts */ #define ATH_TXMGTTRY 4 /* xmit attempts for mgt/ctl frames */ #define ATH_TXINTR_PERIOD 5 /* max number of batched tx descriptors */ #define ATH_BEACON_AIFS_DEFAULT 1 /* default aifs for ap beacon q */ #define ATH_BEACON_CWMIN_DEFAULT 0 /* default cwmin for ap beacon q */ #define ATH_BEACON_CWMAX_DEFAULT 0 /* default cwmax for ap beacon q */ /* * The following bits can be set during the PCI (and perhaps non-PCI * later) device probe path. * * It controls some of the driver and HAL behaviour. */ #define ATH_PCI_CUS198 0x0001 #define ATH_PCI_CUS230 0x0002 #define ATH_PCI_CUS217 0x0004 #define ATH_PCI_CUS252 0x0008 #define ATH_PCI_WOW 0x0010 #define ATH_PCI_BT_ANT_DIV 0x0020 #define ATH_PCI_D3_L1_WAR 0x0040 #define ATH_PCI_AR9565_1ANT 0x0080 #define ATH_PCI_AR9565_2ANT 0x0100 #define ATH_PCI_NO_PLL_PWRSAVE 0x0200 #define ATH_PCI_KILLER 0x0400 /* * The key cache is used for h/w cipher state and also for * tracking station state such as the current tx antenna. * We also setup a mapping table between key cache slot indices * and station state to short-circuit node lookups on rx. * Different parts have different size key caches. We handle * up to ATH_KEYMAX entries (could dynamically allocate state). */ #define ATH_KEYMAX 128 /* max key cache size we handle */ #define ATH_KEYBYTES (ATH_KEYMAX/NBBY) /* storage space in bytes */ struct taskqueue; struct kthread; struct ath_buf; #define ATH_TID_MAX_BUFS (2 * IEEE80211_AGGR_BAWMAX) /* * Per-TID state * * Note that TID 16 (WME_NUM_TID+1) is for handling non-QoS frames. */ struct ath_tid { TAILQ_HEAD(,ath_buf) tid_q; /* pending buffers */ struct ath_node *an; /* pointer to parent */ int tid; /* tid */ - int ac; /* which AC gets this trafic */ + int ac; /* which AC gets this traffic */ int hwq_depth; /* how many buffers are on HW */ u_int axq_depth; /* SW queue depth */ struct { TAILQ_HEAD(,ath_buf) tid_q; /* filtered queue */ u_int axq_depth; /* SW queue depth */ } filtq; /* * Entry on the ath_txq; when there's traffic * to send */ TAILQ_ENTRY(ath_tid) axq_qelem; int sched; int paused; /* >0 if the TID has been paused */ /* * These are flags - perhaps later collapse * down to a single uint32_t ? */ int addba_tx_pending; /* TX ADDBA pending */ int bar_wait; /* waiting for BAR */ int bar_tx; /* BAR TXed */ int isfiltered; /* is this node currently filtered */ /* * Is the TID being cleaned up after a transition * from aggregation to non-aggregation? * When this is set to 1, this TID will be paused * and no further traffic will be queued until all * the hardware packets pending for this TID have been * TXed/completed; at which point (non-aggregation) * traffic will resume being TXed. */ int cleanup_inprogress; /* * How many hardware-queued packets are * waiting to be cleaned up. * This is only valid if cleanup_inprogress is 1. */ int incomp; /* * The following implements a ring representing * the frames in the current BAW. * To avoid copying the array content each time * the BAW is moved, the baw_head/baw_tail point * to the current BAW begin/end; when the BAW is * shifted the head/tail of the array are also * appropriately shifted. */ /* active tx buffers, beginning at current BAW */ struct ath_buf *tx_buf[ATH_TID_MAX_BUFS]; /* where the baw head is in the array */ int baw_head; /* where the BAW tail is in the array */ int baw_tail; }; /* driver-specific node state */ struct ath_node { struct ieee80211_node an_node; /* base class */ u_int8_t an_mgmtrix; /* min h/w rate index */ u_int8_t an_mcastrix; /* mcast h/w rate index */ uint32_t an_is_powersave; /* node is sleeping */ uint32_t an_stack_psq; /* net80211 psq isn't empty */ uint32_t an_tim_set; /* TIM has been set */ struct ath_buf *an_ff_buf[WME_NUM_AC]; /* ff staging area */ struct ath_tid an_tid[IEEE80211_TID_SIZE]; /* per-TID state */ char an_name[32]; /* eg "wlan0_a1" */ struct mtx an_mtx; /* protecting the rate control state */ uint32_t an_swq_depth; /* how many SWQ packets for this node */ int clrdmask; /* has clrdmask been set */ uint32_t an_leak_count; /* How many frames to leak during pause */ /* variable-length rate control state follows */ }; #define ATH_NODE(ni) ((struct ath_node *)(ni)) #define ATH_NODE_CONST(ni) ((const struct ath_node *)(ni)) #define ATH_RSSI_LPF_LEN 10 #define ATH_RSSI_DUMMY_MARKER 0x127 #define ATH_EP_MUL(x, mul) ((x) * (mul)) #define ATH_RSSI_IN(x) (ATH_EP_MUL((x), HAL_RSSI_EP_MULTIPLIER)) #define ATH_LPF_RSSI(x, y, len) \ ((x != ATH_RSSI_DUMMY_MARKER) ? (((x) * ((len) - 1) + (y)) / (len)) : (y)) #define ATH_RSSI_LPF(x, y) do { \ if ((y) >= -20) \ x = ATH_LPF_RSSI((x), ATH_RSSI_IN((y)), ATH_RSSI_LPF_LEN); \ } while (0) #define ATH_EP_RND(x,mul) \ ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul)) #define ATH_RSSI(x) ATH_EP_RND(x, HAL_RSSI_EP_MULTIPLIER) typedef enum { ATH_BUFTYPE_NORMAL = 0, ATH_BUFTYPE_MGMT = 1, } ath_buf_type_t; struct ath_buf { TAILQ_ENTRY(ath_buf) bf_list; struct ath_buf * bf_next; /* next buffer in the aggregate */ int bf_nseg; HAL_STATUS bf_rxstatus; uint16_t bf_flags; /* status flags (below) */ uint16_t bf_descid; /* 16 bit descriptor ID */ struct ath_desc *bf_desc; /* virtual addr of desc */ struct ath_desc_status bf_status; /* tx/rx status */ bus_addr_t bf_daddr; /* physical addr of desc */ bus_dmamap_t bf_dmamap; /* DMA map for mbuf chain */ struct mbuf *bf_m; /* mbuf for buf */ struct ieee80211_node *bf_node; /* pointer to the node */ struct ath_desc *bf_lastds; /* last descriptor for comp status */ struct ath_buf *bf_last; /* last buffer in aggregate, or self for non-aggregate */ bus_size_t bf_mapsize; #define ATH_MAX_SCATTER ATH_TXDESC /* max(tx,rx,beacon) desc's */ bus_dma_segment_t bf_segs[ATH_MAX_SCATTER]; uint32_t bf_nextfraglen; /* length of next fragment */ /* Completion function to call on TX complete (fail or not) */ /* * "fail" here is set to 1 if the queue entries were removed * through a call to ath_tx_draintxq(). */ void(* bf_comp) (struct ath_softc *sc, struct ath_buf *bf, int fail); /* This state is kept to support software retries and aggregation */ struct { uint16_t bfs_seqno; /* sequence number of this packet */ uint16_t bfs_ndelim; /* number of delims for padding */ uint8_t bfs_retries; /* retry count */ uint8_t bfs_tid; /* packet TID (or TID_MAX for no QoS) */ uint8_t bfs_nframes; /* number of frames in aggregate */ uint8_t bfs_pri; /* packet AC priority */ uint8_t bfs_tx_queue; /* destination hardware TX queue */ u_int32_t bfs_aggr:1, /* part of aggregate? */ bfs_aggrburst:1, /* part of aggregate burst? */ bfs_isretried:1, /* retried frame? */ bfs_dobaw:1, /* actually check against BAW? */ bfs_addedbaw:1, /* has been added to the BAW */ bfs_shpream:1, /* use short preamble */ bfs_istxfrag:1, /* is fragmented */ bfs_ismrr:1, /* do multi-rate TX retry */ bfs_doprot:1, /* do RTS/CTS based protection */ bfs_doratelookup:1; /* do rate lookup before each TX */ /* * These fields are passed into the * descriptor setup functions. */ /* Make this an 8 bit value? */ HAL_PKT_TYPE bfs_atype; /* packet type */ uint32_t bfs_pktlen; /* length of this packet */ uint16_t bfs_hdrlen; /* length of this packet header */ uint16_t bfs_al; /* length of aggregate */ uint16_t bfs_txflags; /* HAL (tx) descriptor flags */ uint8_t bfs_txrate0; /* first TX rate */ uint8_t bfs_try0; /* first try count */ uint16_t bfs_txpower; /* tx power */ uint8_t bfs_ctsrate0; /* Non-zero - use this as ctsrate */ uint8_t bfs_ctsrate; /* CTS rate */ /* 16 bit? */ int32_t bfs_keyix; /* crypto key index */ int32_t bfs_txantenna; /* TX antenna config */ /* Make this an 8 bit value? */ enum ieee80211_protmode bfs_protmode; /* 16 bit? */ uint32_t bfs_ctsduration; /* CTS duration (pre-11n NICs) */ struct ath_rc_series bfs_rc[ATH_RC_NUM]; /* non-11n TX series */ } bf_state; }; typedef TAILQ_HEAD(ath_bufhead_s, ath_buf) ath_bufhead; #define ATH_BUF_MGMT 0x00000001 /* (tx) desc is a mgmt desc */ #define ATH_BUF_BUSY 0x00000002 /* (tx) desc owned by h/w */ #define ATH_BUF_FIFOEND 0x00000004 #define ATH_BUF_FIFOPTR 0x00000008 #define ATH_BUF_FLAGS_CLONE (ATH_BUF_MGMT) /* * DMA state for tx/rx descriptors. */ struct ath_descdma { const char* dd_name; struct ath_desc *dd_desc; /* descriptors */ int dd_descsize; /* size of single descriptor */ bus_addr_t dd_desc_paddr; /* physical addr of dd_desc */ bus_size_t dd_desc_len; /* size of dd_desc */ bus_dma_segment_t dd_dseg; bus_dma_tag_t dd_dmat; /* bus DMA tag */ bus_dmamap_t dd_dmamap; /* DMA map for descriptors */ struct ath_buf *dd_bufptr; /* associated buffers */ }; /* * Data transmit queue state. One of these exists for each * hardware transmit queue. Packets sent to us from above * are assigned to queues based on their priority. Not all * devices support a complete set of hardware transmit queues. * For those devices the array sc_ac2q will map multiple * priorities to fewer hardware queues (typically all to one * hardware queue). */ struct ath_txq { struct ath_softc *axq_softc; /* Needed for scheduling */ u_int axq_qnum; /* hardware q number */ #define ATH_TXQ_SWQ (HAL_NUM_TX_QUEUES+1) /* qnum for s/w only queue */ u_int axq_ac; /* WME AC */ u_int axq_flags; //#define ATH_TXQ_PUTPENDING 0x0001 /* ath_hal_puttxbuf pending */ #define ATH_TXQ_PUTRUNNING 0x0002 /* ath_hal_puttxbuf has been called */ u_int axq_depth; /* queue depth (stat only) */ u_int axq_aggr_depth; /* how many aggregates are queued */ u_int axq_intrcnt; /* interrupt count */ u_int32_t *axq_link; /* link ptr in last TX desc */ TAILQ_HEAD(axq_q_s, ath_buf) axq_q; /* transmit queue */ struct mtx axq_lock; /* lock on q and link */ /* * This is the FIFO staging buffer when doing EDMA. * * For legacy chips, we just push the head pointer to * the hardware and we ignore this list. * * For EDMA, the staging buffer is treated as normal; * when it's time to push a list of frames to the hardware * we move that list here and we stamp buffers with * flags to identify the beginning/end of that particular * FIFO entry. */ struct { TAILQ_HEAD(axq_q_f_s, ath_buf) axq_q; u_int axq_depth; } fifo; u_int axq_fifo_depth; /* depth of FIFO frames */ /* * XXX the holdingbf field is protected by the TXBUF lock * for now, NOT the TXQ lock. * * Architecturally, it would likely be better to move * the holdingbf field to a separate array in ath_softc * just to highlight that it's not protected by the normal * TX path lock. */ struct ath_buf *axq_holdingbf; /* holding TX buffer */ char axq_name[12]; /* e.g. "ath0_txq4" */ /* Per-TID traffic queue for software -> hardware TX */ /* * This is protected by the general TX path lock, not (for now) * by the TXQ lock. */ TAILQ_HEAD(axq_t_s,ath_tid) axq_tidq; }; #define ATH_TXQ_LOCK_INIT(_sc, _tq) do { \ snprintf((_tq)->axq_name, sizeof((_tq)->axq_name), "%s_txq%u", \ device_get_nameunit((_sc)->sc_dev), (_tq)->axq_qnum); \ mtx_init(&(_tq)->axq_lock, (_tq)->axq_name, NULL, MTX_DEF); \ } while (0) #define ATH_TXQ_LOCK_DESTROY(_tq) mtx_destroy(&(_tq)->axq_lock) #define ATH_TXQ_LOCK(_tq) mtx_lock(&(_tq)->axq_lock) #define ATH_TXQ_UNLOCK(_tq) mtx_unlock(&(_tq)->axq_lock) #define ATH_TXQ_LOCK_ASSERT(_tq) mtx_assert(&(_tq)->axq_lock, MA_OWNED) #define ATH_TXQ_UNLOCK_ASSERT(_tq) mtx_assert(&(_tq)->axq_lock, \ MA_NOTOWNED) #define ATH_NODE_LOCK(_an) mtx_lock(&(_an)->an_mtx) #define ATH_NODE_UNLOCK(_an) mtx_unlock(&(_an)->an_mtx) #define ATH_NODE_LOCK_ASSERT(_an) mtx_assert(&(_an)->an_mtx, MA_OWNED) #define ATH_NODE_UNLOCK_ASSERT(_an) mtx_assert(&(_an)->an_mtx, \ MA_NOTOWNED) /* * These are for the hardware queue. */ #define ATH_TXQ_INSERT_HEAD(_tq, _elm, _field) do { \ TAILQ_INSERT_HEAD(&(_tq)->axq_q, (_elm), _field); \ (_tq)->axq_depth++; \ } while (0) #define ATH_TXQ_INSERT_TAIL(_tq, _elm, _field) do { \ TAILQ_INSERT_TAIL(&(_tq)->axq_q, (_elm), _field); \ (_tq)->axq_depth++; \ } while (0) #define ATH_TXQ_REMOVE(_tq, _elm, _field) do { \ TAILQ_REMOVE(&(_tq)->axq_q, _elm, _field); \ (_tq)->axq_depth--; \ } while (0) #define ATH_TXQ_FIRST(_tq) TAILQ_FIRST(&(_tq)->axq_q) #define ATH_TXQ_LAST(_tq, _field) TAILQ_LAST(&(_tq)->axq_q, _field) /* * These are for the TID software queue. */ #define ATH_TID_INSERT_HEAD(_tq, _elm, _field) do { \ TAILQ_INSERT_HEAD(&(_tq)->tid_q, (_elm), _field); \ (_tq)->axq_depth++; \ (_tq)->an->an_swq_depth++; \ } while (0) #define ATH_TID_INSERT_TAIL(_tq, _elm, _field) do { \ TAILQ_INSERT_TAIL(&(_tq)->tid_q, (_elm), _field); \ (_tq)->axq_depth++; \ (_tq)->an->an_swq_depth++; \ } while (0) #define ATH_TID_REMOVE(_tq, _elm, _field) do { \ TAILQ_REMOVE(&(_tq)->tid_q, _elm, _field); \ (_tq)->axq_depth--; \ (_tq)->an->an_swq_depth--; \ } while (0) #define ATH_TID_FIRST(_tq) TAILQ_FIRST(&(_tq)->tid_q) #define ATH_TID_LAST(_tq, _field) TAILQ_LAST(&(_tq)->tid_q, _field) /* * These are for the TID filtered frame queue */ #define ATH_TID_FILT_INSERT_HEAD(_tq, _elm, _field) do { \ TAILQ_INSERT_HEAD(&(_tq)->filtq.tid_q, (_elm), _field); \ (_tq)->axq_depth++; \ (_tq)->an->an_swq_depth++; \ } while (0) #define ATH_TID_FILT_INSERT_TAIL(_tq, _elm, _field) do { \ TAILQ_INSERT_TAIL(&(_tq)->filtq.tid_q, (_elm), _field); \ (_tq)->axq_depth++; \ (_tq)->an->an_swq_depth++; \ } while (0) #define ATH_TID_FILT_REMOVE(_tq, _elm, _field) do { \ TAILQ_REMOVE(&(_tq)->filtq.tid_q, _elm, _field); \ (_tq)->axq_depth--; \ (_tq)->an->an_swq_depth--; \ } while (0) #define ATH_TID_FILT_FIRST(_tq) TAILQ_FIRST(&(_tq)->filtq.tid_q) #define ATH_TID_FILT_LAST(_tq, _field) TAILQ_LAST(&(_tq)->filtq.tid_q,_field) struct ath_vap { struct ieee80211vap av_vap; /* base class */ int av_bslot; /* beacon slot index */ struct ath_buf *av_bcbuf; /* beacon buffer */ struct ath_txq av_mcastq; /* buffered mcast s/w queue */ void (*av_recv_mgmt)(struct ieee80211_node *, struct mbuf *, int, const struct ieee80211_rx_stats *, int, int); int (*av_newstate)(struct ieee80211vap *, enum ieee80211_state, int); void (*av_bmiss)(struct ieee80211vap *); void (*av_node_ps)(struct ieee80211_node *, int); int (*av_set_tim)(struct ieee80211_node *, int); void (*av_recv_pspoll)(struct ieee80211_node *, struct mbuf *); }; #define ATH_VAP(vap) ((struct ath_vap *)(vap)) struct taskqueue; struct ath_tx99; /* * Whether to reset the TX/RX queue with or without * a queue flush. */ typedef enum { ATH_RESET_DEFAULT = 0, ATH_RESET_NOLOSS = 1, ATH_RESET_FULL = 2, } ATH_RESET_TYPE; struct ath_rx_methods { void (*recv_sched_queue)(struct ath_softc *sc, HAL_RX_QUEUE q, int dosched); void (*recv_sched)(struct ath_softc *sc, int dosched); void (*recv_stop)(struct ath_softc *sc, int dodelay); int (*recv_start)(struct ath_softc *sc); void (*recv_flush)(struct ath_softc *sc); void (*recv_tasklet)(void *arg, int npending); int (*recv_rxbuf_init)(struct ath_softc *sc, struct ath_buf *bf); int (*recv_setup)(struct ath_softc *sc); int (*recv_teardown)(struct ath_softc *sc); }; /* * Represent the current state of the RX FIFO. */ struct ath_rx_edma { struct ath_buf **m_fifo; int m_fifolen; int m_fifo_head; int m_fifo_tail; int m_fifo_depth; struct mbuf *m_rxpending; struct ath_buf *m_holdbf; }; struct ath_tx_edma_fifo { struct ath_buf **m_fifo; int m_fifolen; int m_fifo_head; int m_fifo_tail; int m_fifo_depth; }; struct ath_tx_methods { int (*xmit_setup)(struct ath_softc *sc); int (*xmit_teardown)(struct ath_softc *sc); void (*xmit_attach_comp_func)(struct ath_softc *sc); void (*xmit_dma_restart)(struct ath_softc *sc, struct ath_txq *txq); void (*xmit_handoff)(struct ath_softc *sc, struct ath_txq *txq, struct ath_buf *bf); void (*xmit_drain)(struct ath_softc *sc, ATH_RESET_TYPE reset_type); }; struct ath_softc { struct ieee80211com sc_ic; struct ath_stats sc_stats; /* device statistics */ struct ath_tx_aggr_stats sc_aggr_stats; struct ath_intr_stats sc_intr_stats; uint64_t sc_debug; uint64_t sc_ktrdebug; int sc_nvaps; /* # vaps */ int sc_nstavaps; /* # station vaps */ int sc_nmeshvaps; /* # mbss vaps */ u_int8_t sc_hwbssidmask[IEEE80211_ADDR_LEN]; u_int8_t sc_nbssid0; /* # vap's using base mac */ uint32_t sc_bssidmask; /* bssid mask */ struct ath_rx_methods sc_rx; struct ath_rx_edma sc_rxedma[HAL_NUM_RX_QUEUES]; /* HP/LP queues */ ath_bufhead sc_rx_rxlist[HAL_NUM_RX_QUEUES]; /* deferred RX completion */ struct ath_tx_methods sc_tx; struct ath_tx_edma_fifo sc_txedma[HAL_NUM_TX_QUEUES]; /* * This is (currently) protected by the TX queue lock; * it should migrate to a separate lock later * so as to minimise contention. */ ath_bufhead sc_txbuf_list; int sc_rx_statuslen; int sc_tx_desclen; int sc_tx_statuslen; int sc_tx_nmaps; /* Number of TX maps */ int sc_edma_bufsize; int sc_rx_stopped; /* XXX only for EDMA */ int sc_rx_resetted; /* XXX only for EDMA */ void (*sc_node_cleanup)(struct ieee80211_node *); void (*sc_node_free)(struct ieee80211_node *); device_t sc_dev; HAL_BUS_TAG sc_st; /* bus space tag */ HAL_BUS_HANDLE sc_sh; /* bus space handle */ bus_dma_tag_t sc_dmat; /* bus DMA tag */ struct mtx sc_mtx; /* master lock (recursive) */ struct mtx sc_pcu_mtx; /* PCU access mutex */ char sc_pcu_mtx_name[32]; struct mtx sc_rx_mtx; /* RX access mutex */ char sc_rx_mtx_name[32]; struct mtx sc_tx_mtx; /* TX handling/comp mutex */ char sc_tx_mtx_name[32]; struct mtx sc_tx_ic_mtx; /* TX queue mutex */ char sc_tx_ic_mtx_name[32]; struct taskqueue *sc_tq; /* private task queue */ struct ath_hal *sc_ah; /* Atheros HAL */ struct ath_ratectrl *sc_rc; /* tx rate control support */ struct ath_tx99 *sc_tx99; /* tx99 adjunct state */ void (*sc_setdefantenna)(struct ath_softc *, u_int); /* * First set of flags. */ uint32_t sc_invalid : 1,/* disable hardware accesses */ sc_mrretry : 1,/* multi-rate retry support */ sc_mrrprot : 1,/* MRR + protection support */ sc_softled : 1,/* enable LED gpio status */ sc_hardled : 1,/* enable MAC LED status */ sc_splitmic : 1,/* split TKIP MIC keys */ sc_needmib : 1,/* enable MIB stats intr */ sc_diversity: 1,/* enable rx diversity */ sc_hasveol : 1,/* tx VEOL support */ sc_ledstate : 1,/* LED on/off state */ sc_blinking : 1,/* LED blink operation active */ sc_mcastkey : 1,/* mcast key cache search */ sc_scanning : 1,/* scanning active */ sc_syncbeacon:1,/* sync/resync beacon timers */ sc_hasclrkey: 1,/* CLR key supported */ sc_xchanmode: 1,/* extended channel mode */ sc_outdoor : 1,/* outdoor operation */ sc_dturbo : 1,/* dynamic turbo in use */ sc_hasbmask : 1,/* bssid mask support */ sc_hasbmatch: 1,/* bssid match disable support*/ sc_hastsfadd: 1,/* tsf adjust support */ sc_beacons : 1,/* beacons running */ sc_swbmiss : 1,/* sta mode using sw bmiss */ sc_stagbeacons:1,/* use staggered beacons */ sc_wmetkipmic:1,/* can do WME+TKIP MIC */ sc_resume_up: 1,/* on resume, start all vaps */ sc_tdma : 1,/* TDMA in use */ sc_setcca : 1,/* set/clr CCA with TDMA */ sc_resetcal : 1,/* reset cal state next trip */ sc_rxslink : 1,/* do self-linked final descriptor */ sc_rxtsf32 : 1,/* RX dec TSF is 32 bits */ sc_isedma : 1,/* supports EDMA */ sc_do_mybeacon : 1; /* supports mybeacon */ /* * Second set of flags. */ u_int32_t sc_running : 1, /* initialized */ sc_use_ent : 1, sc_rx_stbc : 1, sc_tx_stbc : 1, sc_has_ldpc : 1, sc_hasenforcetxop : 1, /* support enforce TxOP */ sc_hasdivcomb : 1, /* RX diversity combining */ sc_rx_lnamixer : 1; /* RX using LNA mixing */ int sc_cabq_enable; /* Enable cabq transmission */ /* * Enterprise mode configuration for AR9380 and later chipsets. */ uint32_t sc_ent_cfg; uint32_t sc_eerd; /* regdomain from EEPROM */ uint32_t sc_eecc; /* country code from EEPROM */ /* rate tables */ const HAL_RATE_TABLE *sc_rates[IEEE80211_MODE_MAX]; const HAL_RATE_TABLE *sc_currates; /* current rate table */ enum ieee80211_phymode sc_curmode; /* current phy mode */ HAL_OPMODE sc_opmode; /* current operating mode */ u_int16_t sc_curtxpow; /* current tx power limit */ u_int16_t sc_curaid; /* current association id */ struct ieee80211_channel *sc_curchan; /* current installed channel */ u_int8_t sc_curbssid[IEEE80211_ADDR_LEN]; u_int8_t sc_rixmap[256]; /* IEEE to h/w rate table ix */ struct { u_int8_t ieeerate; /* IEEE rate */ u_int8_t rxflags; /* radiotap rx flags */ u_int8_t txflags; /* radiotap tx flags */ u_int16_t ledon; /* softled on time */ u_int16_t ledoff; /* softled off time */ } sc_hwmap[32]; /* h/w rate ix mappings */ u_int8_t sc_protrix; /* protection rate index */ u_int8_t sc_lastdatarix; /* last data frame rate index */ u_int sc_mcastrate; /* ieee rate for mcastrateix */ u_int sc_fftxqmin; /* min frames before staging */ u_int sc_fftxqmax; /* max frames before drop */ u_int sc_txantenna; /* tx antenna (fixed or auto) */ HAL_INT sc_imask; /* interrupt mask copy */ /* * These are modified in the interrupt handler as well as * the task queues and other contexts. Thus these must be * protected by a mutex, or they could clash. * * For now, access to these is behind the ATH_LOCK, * just to save time. */ uint32_t sc_txq_active; /* bitmap of active TXQs */ uint32_t sc_kickpcu; /* whether to kick the PCU */ uint32_t sc_rxproc_cnt; /* In RX processing */ uint32_t sc_txproc_cnt; /* In TX processing */ uint32_t sc_txstart_cnt; /* In TX output (raw/start) */ uint32_t sc_inreset_cnt; /* In active reset/chanchange */ uint32_t sc_txrx_cnt; /* refcount on stop/start'ing TX */ uint32_t sc_intr_cnt; /* refcount on interrupt handling */ u_int sc_keymax; /* size of key cache */ u_int8_t sc_keymap[ATH_KEYBYTES];/* key use bit map */ /* * Software based LED blinking */ u_int sc_ledpin; /* GPIO pin for driving LED */ u_int sc_ledon; /* pin setting for LED on */ u_int sc_ledidle; /* idle polling interval */ int sc_ledevent; /* time of last LED event */ u_int8_t sc_txrix; /* current tx rate for LED */ u_int16_t sc_ledoff; /* off time for current blink */ struct callout sc_ledtimer; /* led off timer */ /* * Hardware based LED blinking */ int sc_led_pwr_pin; /* MAC power LED GPIO pin */ int sc_led_net_pin; /* MAC network LED GPIO pin */ u_int sc_rfsilentpin; /* GPIO pin for rfkill int */ u_int sc_rfsilentpol; /* pin setting for rfkill on */ struct ath_descdma sc_rxdma; /* RX descriptors */ ath_bufhead sc_rxbuf; /* receive buffer */ u_int32_t *sc_rxlink; /* link ptr in last RX desc */ struct task sc_rxtask; /* rx int processing */ u_int8_t sc_defant; /* current default antenna */ u_int8_t sc_rxotherant; /* rx's on non-default antenna*/ u_int64_t sc_lastrx; /* tsf at last rx'd frame */ struct ath_rx_status *sc_lastrs; /* h/w status of last rx */ struct ath_rx_radiotap_header sc_rx_th; int sc_rx_th_len; u_int sc_monpass; /* frames to pass in mon.mode */ struct ath_descdma sc_txdma; /* TX descriptors */ uint16_t sc_txbuf_descid; ath_bufhead sc_txbuf; /* transmit buffer */ int sc_txbuf_cnt; /* how many buffers avail */ struct ath_descdma sc_txdma_mgmt; /* mgmt TX descriptors */ ath_bufhead sc_txbuf_mgmt; /* mgmt transmit buffer */ struct ath_descdma sc_txsdma; /* EDMA TX status desc's */ struct mtx sc_txbuflock; /* txbuf lock */ char sc_txname[12]; /* e.g. "ath0_buf" */ u_int sc_txqsetup; /* h/w queues setup */ u_int sc_txintrperiod;/* tx interrupt batching */ struct ath_txq sc_txq[HAL_NUM_TX_QUEUES]; struct ath_txq *sc_ac2q[5]; /* WME AC -> h/w q map */ struct task sc_txtask; /* tx int processing */ struct task sc_txqtask; /* tx proc processing */ struct ath_descdma sc_txcompdma; /* TX EDMA completion */ struct mtx sc_txcomplock; /* TX EDMA completion lock */ char sc_txcompname[12]; /* eg ath0_txcomp */ int sc_wd_timer; /* count down for wd timer */ struct callout sc_wd_ch; /* tx watchdog timer */ struct ath_tx_radiotap_header sc_tx_th; int sc_tx_th_len; struct ath_descdma sc_bdma; /* beacon descriptors */ ath_bufhead sc_bbuf; /* beacon buffers */ u_int sc_bhalq; /* HAL q for outgoing beacons */ u_int sc_bmisscount; /* missed beacon transmits */ u_int32_t sc_ant_tx[8]; /* recent tx frames/antenna */ struct ath_txq *sc_cabq; /* tx q for cab frames */ struct task sc_bmisstask; /* bmiss int processing */ struct task sc_bstucktask; /* stuck beacon processing */ struct task sc_resettask; /* interface reset task */ struct task sc_fataltask; /* fatal task */ enum { OK, /* no change needed */ UPDATE, /* update pending */ COMMIT /* beacon sent, commit change */ } sc_updateslot; /* slot time update fsm */ int sc_slotupdate; /* slot to advance fsm */ struct ieee80211vap *sc_bslot[ATH_BCBUF]; int sc_nbcnvaps; /* # vaps with beacons */ struct callout sc_cal_ch; /* callout handle for cals */ int sc_lastlongcal; /* last long cal completed */ int sc_lastcalreset;/* last cal reset done */ int sc_lastani; /* last ANI poll */ int sc_lastshortcal; /* last short calibration */ HAL_BOOL sc_doresetcal; /* Yes, we're doing a reset cal atm */ HAL_NODE_STATS sc_halstats; /* station-mode rssi stats */ u_int sc_tdmadbaprep; /* TDMA DBA prep time */ u_int sc_tdmaswbaprep;/* TDMA SWBA prep time */ u_int sc_tdmaswba; /* TDMA SWBA counter */ u_int32_t sc_tdmabintval; /* TDMA beacon interval (TU) */ u_int32_t sc_tdmaguard; /* TDMA guard time (usec) */ u_int sc_tdmaslotlen; /* TDMA slot length (usec) */ u_int32_t sc_avgtsfdeltap;/* TDMA slot adjust (+) */ u_int32_t sc_avgtsfdeltam;/* TDMA slot adjust (-) */ uint16_t *sc_eepromdata; /* Local eeprom data, if AR9100 */ uint32_t sc_txchainmask; /* hardware TX chainmask */ uint32_t sc_rxchainmask; /* hardware RX chainmask */ uint32_t sc_cur_txchainmask; /* currently configured TX chainmask */ uint32_t sc_cur_rxchainmask; /* currently configured RX chainmask */ uint32_t sc_rts_aggr_limit; /* TX limit on RTS aggregates */ int sc_aggr_limit; /* TX limit on all aggregates */ int sc_delim_min_pad; /* Minimum delimiter count */ /* Queue limits */ /* * To avoid queue starvation in congested conditions, * these parameters tune the maximum number of frames * queued to the data/mcastq before they're dropped. * * This is to prevent: * + a single destination overwhelming everything, including * management/multicast frames; * + multicast frames overwhelming everything (when the * air is sufficiently busy that cabq can't drain.) * + A node in powersave shouldn't be allowed to exhaust * all available mbufs; * * These implement: * + data_minfree is the maximum number of free buffers * overall to successfully allow a data frame. * * + mcastq_maxdepth is the maximum depth allowed of the cabq. */ int sc_txq_node_maxdepth; int sc_txq_data_minfree; int sc_txq_mcastq_maxdepth; int sc_txq_node_psq_maxdepth; /* * Software queue twiddles * * hwq_limit_nonaggr: * when to begin limiting non-aggregate frames to the * hardware queue, regardless of the TID. * hwq_limit_aggr: * when to begin limiting A-MPDU frames to the * hardware queue, regardless of the TID. * tid_hwq_lo: how low the per-TID hwq count has to be before the * TID will be scheduled again * tid_hwq_hi: how many frames to queue to the HWQ before the TID * stops being scheduled. */ int sc_hwq_limit_nonaggr; int sc_hwq_limit_aggr; int sc_tid_hwq_lo; int sc_tid_hwq_hi; /* DFS related state */ void *sc_dfs; /* Used by an optional DFS module */ int sc_dodfs; /* Whether to enable DFS rx filter bits */ struct task sc_dfstask; /* DFS processing task */ /* Spectral related state */ void *sc_spectral; int sc_dospectral; /* LNA diversity related state */ void *sc_lna_div; int sc_dolnadiv; /* ALQ */ #ifdef ATH_DEBUG_ALQ struct if_ath_alq sc_alq; #endif /* TX AMPDU handling */ int (*sc_addba_request)(struct ieee80211_node *, struct ieee80211_tx_ampdu *, int, int, int); int (*sc_addba_response)(struct ieee80211_node *, struct ieee80211_tx_ampdu *, int, int, int); void (*sc_addba_stop)(struct ieee80211_node *, struct ieee80211_tx_ampdu *); void (*sc_addba_response_timeout) (struct ieee80211_node *, struct ieee80211_tx_ampdu *); void (*sc_bar_response)(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, int status); /* * Powersave state tracking. * * target/cur powerstate is the chip power state. * target selfgen state is the self-generated frames * state. The chip can be awake but transmitted frames * can have the PWRMGT bit set to 1 so the destination * thinks the node is asleep. */ HAL_POWER_MODE sc_target_powerstate; HAL_POWER_MODE sc_target_selfgen_state; HAL_POWER_MODE sc_cur_powerstate; int sc_powersave_refcnt; /* ATH_PCI_* flags */ uint32_t sc_pci_devinfo; }; #define ATH_LOCK_INIT(_sc) \ mtx_init(&(_sc)->sc_mtx, device_get_nameunit((_sc)->sc_dev), \ NULL, MTX_DEF | MTX_RECURSE) #define ATH_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx) #define ATH_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define ATH_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define ATH_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) #define ATH_UNLOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_NOTOWNED) /* * The TX lock is non-reentrant and serialises the TX frame send * and completion operations. */ #define ATH_TX_LOCK_INIT(_sc) do {\ snprintf((_sc)->sc_tx_mtx_name, \ sizeof((_sc)->sc_tx_mtx_name), \ "%s TX lock", \ device_get_nameunit((_sc)->sc_dev)); \ mtx_init(&(_sc)->sc_tx_mtx, (_sc)->sc_tx_mtx_name, \ NULL, MTX_DEF); \ } while (0) #define ATH_TX_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_tx_mtx) #define ATH_TX_LOCK(_sc) mtx_lock(&(_sc)->sc_tx_mtx) #define ATH_TX_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_tx_mtx) #define ATH_TX_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_tx_mtx, \ MA_OWNED) #define ATH_TX_UNLOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_tx_mtx, \ MA_NOTOWNED) #define ATH_TX_TRYLOCK(_sc) (mtx_owned(&(_sc)->sc_tx_mtx) != 0 && \ mtx_trylock(&(_sc)->sc_tx_mtx)) /* * The PCU lock is non-recursive and should be treated as a spinlock. * Although currently the interrupt code is run in netisr context and * doesn't require this, this may change in the future. * Please keep this in mind when protecting certain code paths * with the PCU lock. * * The PCU lock is used to serialise access to the PCU so things such * as TX, RX, state change (eg channel change), channel reset and updates * from interrupt context (eg kickpcu, txqactive bits) do not clash. * * Although the current single-thread taskqueue mechanism protects the * majority of these situations by simply serialising them, there are * a few others which occur at the same time. These include the TX path * (which only acquires ATH_LOCK when recycling buffers to the free list), * ath_set_channel, the channel scanning API and perhaps quite a bit more. */ #define ATH_PCU_LOCK_INIT(_sc) do {\ snprintf((_sc)->sc_pcu_mtx_name, \ sizeof((_sc)->sc_pcu_mtx_name), \ "%s PCU lock", \ device_get_nameunit((_sc)->sc_dev)); \ mtx_init(&(_sc)->sc_pcu_mtx, (_sc)->sc_pcu_mtx_name, \ NULL, MTX_DEF); \ } while (0) #define ATH_PCU_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_pcu_mtx) #define ATH_PCU_LOCK(_sc) mtx_lock(&(_sc)->sc_pcu_mtx) #define ATH_PCU_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_pcu_mtx) #define ATH_PCU_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_pcu_mtx, \ MA_OWNED) #define ATH_PCU_UNLOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_pcu_mtx, \ MA_NOTOWNED) /* * The RX lock is primarily a(nother) workaround to ensure that the * RX FIFO/list isn't modified by various execution paths. * Even though RX occurs in a single context (the ath taskqueue), the * RX path can be executed via various reset/channel change paths. */ #define ATH_RX_LOCK_INIT(_sc) do {\ snprintf((_sc)->sc_rx_mtx_name, \ sizeof((_sc)->sc_rx_mtx_name), \ "%s RX lock", \ device_get_nameunit((_sc)->sc_dev)); \ mtx_init(&(_sc)->sc_rx_mtx, (_sc)->sc_rx_mtx_name, \ NULL, MTX_DEF); \ } while (0) #define ATH_RX_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_rx_mtx) #define ATH_RX_LOCK(_sc) mtx_lock(&(_sc)->sc_rx_mtx) #define ATH_RX_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_rx_mtx) #define ATH_RX_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_rx_mtx, \ MA_OWNED) #define ATH_RX_UNLOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_rx_mtx, \ MA_NOTOWNED) #define ATH_TXQ_SETUP(sc, i) ((sc)->sc_txqsetup & (1<sc_txname, sizeof((_sc)->sc_txname), "%s_buf", \ device_get_nameunit((_sc)->sc_dev)); \ mtx_init(&(_sc)->sc_txbuflock, (_sc)->sc_txname, NULL, MTX_DEF); \ } while (0) #define ATH_TXBUF_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_txbuflock) #define ATH_TXBUF_LOCK(_sc) mtx_lock(&(_sc)->sc_txbuflock) #define ATH_TXBUF_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_txbuflock) #define ATH_TXBUF_LOCK_ASSERT(_sc) \ mtx_assert(&(_sc)->sc_txbuflock, MA_OWNED) #define ATH_TXBUF_UNLOCK_ASSERT(_sc) \ mtx_assert(&(_sc)->sc_txbuflock, MA_NOTOWNED) #define ATH_TXSTATUS_LOCK_INIT(_sc) do { \ snprintf((_sc)->sc_txcompname, sizeof((_sc)->sc_txcompname), \ "%s_buf", \ device_get_nameunit((_sc)->sc_dev)); \ mtx_init(&(_sc)->sc_txcomplock, (_sc)->sc_txcompname, NULL, \ MTX_DEF); \ } while (0) #define ATH_TXSTATUS_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_txcomplock) #define ATH_TXSTATUS_LOCK(_sc) mtx_lock(&(_sc)->sc_txcomplock) #define ATH_TXSTATUS_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_txcomplock) #define ATH_TXSTATUS_LOCK_ASSERT(_sc) \ mtx_assert(&(_sc)->sc_txcomplock, MA_OWNED) int ath_attach(u_int16_t, struct ath_softc *); int ath_detach(struct ath_softc *); void ath_resume(struct ath_softc *); void ath_suspend(struct ath_softc *); void ath_shutdown(struct ath_softc *); void ath_intr(void *); /* * HAL definitions to comply with local coding convention. */ #define ath_hal_detach(_ah) \ ((*(_ah)->ah_detach)((_ah))) #define ath_hal_reset(_ah, _opmode, _chan, _fullreset, _resettype, _pstatus) \ ((*(_ah)->ah_reset)((_ah), (_opmode), (_chan), (_fullreset), \ (_resettype), (_pstatus))) #define ath_hal_macversion(_ah) \ (((_ah)->ah_macVersion << 4) | ((_ah)->ah_macRev)) #define ath_hal_getratetable(_ah, _mode) \ ((*(_ah)->ah_getRateTable)((_ah), (_mode))) #define ath_hal_getmac(_ah, _mac) \ ((*(_ah)->ah_getMacAddress)((_ah), (_mac))) #define ath_hal_setmac(_ah, _mac) \ ((*(_ah)->ah_setMacAddress)((_ah), (_mac))) #define ath_hal_getbssidmask(_ah, _mask) \ ((*(_ah)->ah_getBssIdMask)((_ah), (_mask))) #define ath_hal_setbssidmask(_ah, _mask) \ ((*(_ah)->ah_setBssIdMask)((_ah), (_mask))) #define ath_hal_intrset(_ah, _mask) \ ((*(_ah)->ah_setInterrupts)((_ah), (_mask))) #define ath_hal_intrget(_ah) \ ((*(_ah)->ah_getInterrupts)((_ah))) #define ath_hal_intrpend(_ah) \ ((*(_ah)->ah_isInterruptPending)((_ah))) #define ath_hal_getisr(_ah, _pmask) \ ((*(_ah)->ah_getPendingInterrupts)((_ah), (_pmask))) #define ath_hal_updatetxtriglevel(_ah, _inc) \ ((*(_ah)->ah_updateTxTrigLevel)((_ah), (_inc))) #define ath_hal_setpower(_ah, _mode) \ ((*(_ah)->ah_setPowerMode)((_ah), (_mode), AH_TRUE)) #define ath_hal_setselfgenpower(_ah, _mode) \ ((*(_ah)->ah_setPowerMode)((_ah), (_mode), AH_FALSE)) #define ath_hal_keycachesize(_ah) \ ((*(_ah)->ah_getKeyCacheSize)((_ah))) #define ath_hal_keyreset(_ah, _ix) \ ((*(_ah)->ah_resetKeyCacheEntry)((_ah), (_ix))) #define ath_hal_keyset(_ah, _ix, _pk, _mac) \ ((*(_ah)->ah_setKeyCacheEntry)((_ah), (_ix), (_pk), (_mac), AH_FALSE)) #define ath_hal_keyisvalid(_ah, _ix) \ (((*(_ah)->ah_isKeyCacheEntryValid)((_ah), (_ix)))) #define ath_hal_keysetmac(_ah, _ix, _mac) \ ((*(_ah)->ah_setKeyCacheEntryMac)((_ah), (_ix), (_mac))) #define ath_hal_getrxfilter(_ah) \ ((*(_ah)->ah_getRxFilter)((_ah))) #define ath_hal_setrxfilter(_ah, _filter) \ ((*(_ah)->ah_setRxFilter)((_ah), (_filter))) #define ath_hal_setmcastfilter(_ah, _mfilt0, _mfilt1) \ ((*(_ah)->ah_setMulticastFilter)((_ah), (_mfilt0), (_mfilt1))) #define ath_hal_waitforbeacon(_ah, _bf) \ ((*(_ah)->ah_waitForBeaconDone)((_ah), (_bf)->bf_daddr)) #define ath_hal_putrxbuf(_ah, _bufaddr, _rxq) \ ((*(_ah)->ah_setRxDP)((_ah), (_bufaddr), (_rxq))) /* NB: common across all chips */ #define AR_TSF_L32 0x804c /* MAC local clock lower 32 bits */ #define ath_hal_gettsf32(_ah) \ OS_REG_READ(_ah, AR_TSF_L32) #define ath_hal_gettsf64(_ah) \ ((*(_ah)->ah_getTsf64)((_ah))) #define ath_hal_settsf64(_ah, _val) \ ((*(_ah)->ah_setTsf64)((_ah), (_val))) #define ath_hal_resettsf(_ah) \ ((*(_ah)->ah_resetTsf)((_ah))) #define ath_hal_rxena(_ah) \ ((*(_ah)->ah_enableReceive)((_ah))) #define ath_hal_puttxbuf(_ah, _q, _bufaddr) \ ((*(_ah)->ah_setTxDP)((_ah), (_q), (_bufaddr))) #define ath_hal_gettxbuf(_ah, _q) \ ((*(_ah)->ah_getTxDP)((_ah), (_q))) #define ath_hal_numtxpending(_ah, _q) \ ((*(_ah)->ah_numTxPending)((_ah), (_q))) #define ath_hal_getrxbuf(_ah, _rxq) \ ((*(_ah)->ah_getRxDP)((_ah), (_rxq))) #define ath_hal_txstart(_ah, _q) \ ((*(_ah)->ah_startTxDma)((_ah), (_q))) #define ath_hal_setchannel(_ah, _chan) \ ((*(_ah)->ah_setChannel)((_ah), (_chan))) #define ath_hal_calibrate(_ah, _chan, _iqcal) \ ((*(_ah)->ah_perCalibration)((_ah), (_chan), (_iqcal))) #define ath_hal_calibrateN(_ah, _chan, _lcal, _isdone) \ ((*(_ah)->ah_perCalibrationN)((_ah), (_chan), 0x1, (_lcal), (_isdone))) #define ath_hal_calreset(_ah, _chan) \ ((*(_ah)->ah_resetCalValid)((_ah), (_chan))) #define ath_hal_setledstate(_ah, _state) \ ((*(_ah)->ah_setLedState)((_ah), (_state))) #define ath_hal_beaconinit(_ah, _nextb, _bperiod) \ ((*(_ah)->ah_beaconInit)((_ah), (_nextb), (_bperiod))) #define ath_hal_beaconreset(_ah) \ ((*(_ah)->ah_resetStationBeaconTimers)((_ah))) #define ath_hal_beaconsettimers(_ah, _bt) \ ((*(_ah)->ah_setBeaconTimers)((_ah), (_bt))) #define ath_hal_beacontimers(_ah, _bs) \ ((*(_ah)->ah_setStationBeaconTimers)((_ah), (_bs))) #define ath_hal_getnexttbtt(_ah) \ ((*(_ah)->ah_getNextTBTT)((_ah))) #define ath_hal_setassocid(_ah, _bss, _associd) \ ((*(_ah)->ah_writeAssocid)((_ah), (_bss), (_associd))) #define ath_hal_phydisable(_ah) \ ((*(_ah)->ah_phyDisable)((_ah))) #define ath_hal_setopmode(_ah) \ ((*(_ah)->ah_setPCUConfig)((_ah))) #define ath_hal_stoptxdma(_ah, _qnum) \ ((*(_ah)->ah_stopTxDma)((_ah), (_qnum))) #define ath_hal_stoppcurecv(_ah) \ ((*(_ah)->ah_stopPcuReceive)((_ah))) #define ath_hal_startpcurecv(_ah) \ ((*(_ah)->ah_startPcuReceive)((_ah))) #define ath_hal_stopdmarecv(_ah) \ ((*(_ah)->ah_stopDmaReceive)((_ah))) #define ath_hal_getdiagstate(_ah, _id, _indata, _insize, _outdata, _outsize) \ ((*(_ah)->ah_getDiagState)((_ah), (_id), \ (_indata), (_insize), (_outdata), (_outsize))) #define ath_hal_getfatalstate(_ah, _outdata, _outsize) \ ath_hal_getdiagstate(_ah, 29, NULL, 0, (_outdata), _outsize) #define ath_hal_setuptxqueue(_ah, _type, _irq) \ ((*(_ah)->ah_setupTxQueue)((_ah), (_type), (_irq))) #define ath_hal_resettxqueue(_ah, _q) \ ((*(_ah)->ah_resetTxQueue)((_ah), (_q))) #define ath_hal_releasetxqueue(_ah, _q) \ ((*(_ah)->ah_releaseTxQueue)((_ah), (_q))) #define ath_hal_gettxqueueprops(_ah, _q, _qi) \ ((*(_ah)->ah_getTxQueueProps)((_ah), (_q), (_qi))) #define ath_hal_settxqueueprops(_ah, _q, _qi) \ ((*(_ah)->ah_setTxQueueProps)((_ah), (_q), (_qi))) /* NB: common across all chips */ #define AR_Q_TXE 0x0840 /* MAC Transmit Queue enable */ #define ath_hal_txqenabled(_ah, _qnum) \ (OS_REG_READ(_ah, AR_Q_TXE) & (1<<(_qnum))) #define ath_hal_getrfgain(_ah) \ ((*(_ah)->ah_getRfGain)((_ah))) #define ath_hal_getdefantenna(_ah) \ ((*(_ah)->ah_getDefAntenna)((_ah))) #define ath_hal_setdefantenna(_ah, _ant) \ ((*(_ah)->ah_setDefAntenna)((_ah), (_ant))) #define ath_hal_rxmonitor(_ah, _arg, _chan) \ ((*(_ah)->ah_rxMonitor)((_ah), (_arg), (_chan))) #define ath_hal_ani_poll(_ah, _chan) \ ((*(_ah)->ah_aniPoll)((_ah), (_chan))) #define ath_hal_mibevent(_ah, _stats) \ ((*(_ah)->ah_procMibEvent)((_ah), (_stats))) #define ath_hal_setslottime(_ah, _us) \ ((*(_ah)->ah_setSlotTime)((_ah), (_us))) #define ath_hal_getslottime(_ah) \ ((*(_ah)->ah_getSlotTime)((_ah))) #define ath_hal_setacktimeout(_ah, _us) \ ((*(_ah)->ah_setAckTimeout)((_ah), (_us))) #define ath_hal_getacktimeout(_ah) \ ((*(_ah)->ah_getAckTimeout)((_ah))) #define ath_hal_setctstimeout(_ah, _us) \ ((*(_ah)->ah_setCTSTimeout)((_ah), (_us))) #define ath_hal_getctstimeout(_ah) \ ((*(_ah)->ah_getCTSTimeout)((_ah))) #define ath_hal_getcapability(_ah, _cap, _param, _result) \ ((*(_ah)->ah_getCapability)((_ah), (_cap), (_param), (_result))) #define ath_hal_setcapability(_ah, _cap, _param, _v, _status) \ ((*(_ah)->ah_setCapability)((_ah), (_cap), (_param), (_v), (_status))) #define ath_hal_ciphersupported(_ah, _cipher) \ (ath_hal_getcapability(_ah, HAL_CAP_CIPHER, _cipher, NULL) == HAL_OK) #define ath_hal_getregdomain(_ah, _prd) \ (ath_hal_getcapability(_ah, HAL_CAP_REG_DMN, 0, (_prd)) == HAL_OK) #define ath_hal_setregdomain(_ah, _rd) \ ath_hal_setcapability(_ah, HAL_CAP_REG_DMN, 0, _rd, NULL) #define ath_hal_getcountrycode(_ah, _pcc) \ (*(_pcc) = (_ah)->ah_countryCode) #define ath_hal_gettkipmic(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_TKIP_MIC, 1, NULL) == HAL_OK) #define ath_hal_settkipmic(_ah, _v) \ ath_hal_setcapability(_ah, HAL_CAP_TKIP_MIC, 1, _v, NULL) #define ath_hal_hastkipsplit(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_TKIP_SPLIT, 0, NULL) == HAL_OK) #define ath_hal_gettkipsplit(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_TKIP_SPLIT, 1, NULL) == HAL_OK) #define ath_hal_settkipsplit(_ah, _v) \ ath_hal_setcapability(_ah, HAL_CAP_TKIP_SPLIT, 1, _v, NULL) #define ath_hal_haswmetkipmic(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_WME_TKIPMIC, 0, NULL) == HAL_OK) #define ath_hal_hwphycounters(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_PHYCOUNTERS, 0, NULL) == HAL_OK) #define ath_hal_hasdiversity(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_DIVERSITY, 0, NULL) == HAL_OK) #define ath_hal_getdiversity(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_DIVERSITY, 1, NULL) == HAL_OK) #define ath_hal_setdiversity(_ah, _v) \ ath_hal_setcapability(_ah, HAL_CAP_DIVERSITY, 1, _v, NULL) #define ath_hal_getantennaswitch(_ah) \ ((*(_ah)->ah_getAntennaSwitch)((_ah))) #define ath_hal_setantennaswitch(_ah, _v) \ ((*(_ah)->ah_setAntennaSwitch)((_ah), (_v))) #define ath_hal_getdiag(_ah, _pv) \ (ath_hal_getcapability(_ah, HAL_CAP_DIAG, 0, _pv) == HAL_OK) #define ath_hal_setdiag(_ah, _v) \ ath_hal_setcapability(_ah, HAL_CAP_DIAG, 0, _v, NULL) #define ath_hal_getnumtxqueues(_ah, _pv) \ (ath_hal_getcapability(_ah, HAL_CAP_NUM_TXQUEUES, 0, _pv) == HAL_OK) #define ath_hal_hasveol(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_VEOL, 0, NULL) == HAL_OK) #define ath_hal_hastxpowlimit(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_TXPOW, 0, NULL) == HAL_OK) #define ath_hal_settxpowlimit(_ah, _pow) \ ((*(_ah)->ah_setTxPowerLimit)((_ah), (_pow))) #define ath_hal_gettxpowlimit(_ah, _ppow) \ (ath_hal_getcapability(_ah, HAL_CAP_TXPOW, 1, _ppow) == HAL_OK) #define ath_hal_getmaxtxpow(_ah, _ppow) \ (ath_hal_getcapability(_ah, HAL_CAP_TXPOW, 2, _ppow) == HAL_OK) #define ath_hal_gettpscale(_ah, _scale) \ (ath_hal_getcapability(_ah, HAL_CAP_TXPOW, 3, _scale) == HAL_OK) #define ath_hal_settpscale(_ah, _v) \ ath_hal_setcapability(_ah, HAL_CAP_TXPOW, 3, _v, NULL) #define ath_hal_hastpc(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_TPC, 0, NULL) == HAL_OK) #define ath_hal_gettpc(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_TPC, 1, NULL) == HAL_OK) #define ath_hal_settpc(_ah, _v) \ ath_hal_setcapability(_ah, HAL_CAP_TPC, 1, _v, NULL) #define ath_hal_hasbursting(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_BURST, 0, NULL) == HAL_OK) #define ath_hal_setmcastkeysearch(_ah, _v) \ ath_hal_setcapability(_ah, HAL_CAP_MCAST_KEYSRCH, 0, _v, NULL) #define ath_hal_hasmcastkeysearch(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_MCAST_KEYSRCH, 0, NULL) == HAL_OK) #define ath_hal_getmcastkeysearch(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_MCAST_KEYSRCH, 1, NULL) == HAL_OK) #define ath_hal_hasfastframes(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_FASTFRAME, 0, NULL) == HAL_OK) #define ath_hal_hasbssidmask(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_BSSIDMASK, 0, NULL) == HAL_OK) #define ath_hal_hasbssidmatch(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_BSSIDMATCH, 0, NULL) == HAL_OK) #define ath_hal_hastsfadjust(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_TSF_ADJUST, 0, NULL) == HAL_OK) #define ath_hal_gettsfadjust(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_TSF_ADJUST, 1, NULL) == HAL_OK) #define ath_hal_settsfadjust(_ah, _onoff) \ ath_hal_setcapability(_ah, HAL_CAP_TSF_ADJUST, 1, _onoff, NULL) #define ath_hal_hasrfsilent(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_RFSILENT, 0, NULL) == HAL_OK) #define ath_hal_getrfkill(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_RFSILENT, 1, NULL) == HAL_OK) #define ath_hal_setrfkill(_ah, _onoff) \ ath_hal_setcapability(_ah, HAL_CAP_RFSILENT, 1, _onoff, NULL) #define ath_hal_getrfsilent(_ah, _prfsilent) \ (ath_hal_getcapability(_ah, HAL_CAP_RFSILENT, 2, _prfsilent) == HAL_OK) #define ath_hal_setrfsilent(_ah, _rfsilent) \ ath_hal_setcapability(_ah, HAL_CAP_RFSILENT, 2, _rfsilent, NULL) #define ath_hal_gettpack(_ah, _ptpack) \ (ath_hal_getcapability(_ah, HAL_CAP_TPC_ACK, 0, _ptpack) == HAL_OK) #define ath_hal_settpack(_ah, _tpack) \ ath_hal_setcapability(_ah, HAL_CAP_TPC_ACK, 0, _tpack, NULL) #define ath_hal_gettpcts(_ah, _ptpcts) \ (ath_hal_getcapability(_ah, HAL_CAP_TPC_CTS, 0, _ptpcts) == HAL_OK) #define ath_hal_settpcts(_ah, _tpcts) \ ath_hal_setcapability(_ah, HAL_CAP_TPC_CTS, 0, _tpcts, NULL) #define ath_hal_hasintmit(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_INTMIT, \ HAL_CAP_INTMIT_PRESENT, NULL) == HAL_OK) #define ath_hal_getintmit(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_INTMIT, \ HAL_CAP_INTMIT_ENABLE, NULL) == HAL_OK) #define ath_hal_setintmit(_ah, _v) \ ath_hal_setcapability(_ah, HAL_CAP_INTMIT, \ HAL_CAP_INTMIT_ENABLE, _v, NULL) #define ath_hal_hasmybeacon(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_DO_MYBEACON, 1, NULL) == HAL_OK) #define ath_hal_hasenforcetxop(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_ENFORCE_TXOP, 0, NULL) == HAL_OK) #define ath_hal_getenforcetxop(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_ENFORCE_TXOP, 1, NULL) == HAL_OK) #define ath_hal_setenforcetxop(_ah, _v) \ ath_hal_setcapability(_ah, HAL_CAP_ENFORCE_TXOP, 1, _v, NULL) #define ath_hal_hasrxlnamixer(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_RX_LNA_MIXING, 0, NULL) == HAL_OK) #define ath_hal_hasdivantcomb(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_ANT_DIV_COMB, 0, NULL) == HAL_OK) #define ath_hal_hasldpc(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_LDPC, 0, NULL) == HAL_OK) #define ath_hal_hasldpcwar(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_LDPCWAR, 0, NULL) == HAL_OK) /* EDMA definitions */ #define ath_hal_hasedma(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_ENHANCED_DMA_SUPPORT, \ 0, NULL) == HAL_OK) #define ath_hal_getrxfifodepth(_ah, _qtype, _req) \ (ath_hal_getcapability(_ah, HAL_CAP_RXFIFODEPTH, _qtype, _req) \ == HAL_OK) #define ath_hal_getntxmaps(_ah, _req) \ (ath_hal_getcapability(_ah, HAL_CAP_NUM_TXMAPS, 0, _req) \ == HAL_OK) #define ath_hal_gettxdesclen(_ah, _req) \ (ath_hal_getcapability(_ah, HAL_CAP_TXDESCLEN, 0, _req) \ == HAL_OK) #define ath_hal_gettxstatuslen(_ah, _req) \ (ath_hal_getcapability(_ah, HAL_CAP_TXSTATUSLEN, 0, _req) \ == HAL_OK) #define ath_hal_getrxstatuslen(_ah, _req) \ (ath_hal_getcapability(_ah, HAL_CAP_RXSTATUSLEN, 0, _req) \ == HAL_OK) #define ath_hal_setrxbufsize(_ah, _req) \ (ath_hal_setcapability(_ah, HAL_CAP_RXBUFSIZE, 0, _req, NULL) \ == HAL_OK) #define ath_hal_getchannoise(_ah, _c) \ ((*(_ah)->ah_getChanNoise)((_ah), (_c))) /* 802.11n HAL methods */ #define ath_hal_getrxchainmask(_ah, _prxchainmask) \ (ath_hal_getcapability(_ah, HAL_CAP_RX_CHAINMASK, 0, _prxchainmask)) #define ath_hal_gettxchainmask(_ah, _ptxchainmask) \ (ath_hal_getcapability(_ah, HAL_CAP_TX_CHAINMASK, 0, _ptxchainmask)) #define ath_hal_setrxchainmask(_ah, _rx) \ (ath_hal_setcapability(_ah, HAL_CAP_RX_CHAINMASK, 1, _rx, NULL)) #define ath_hal_settxchainmask(_ah, _tx) \ (ath_hal_setcapability(_ah, HAL_CAP_TX_CHAINMASK, 1, _tx, NULL)) #define ath_hal_split4ktrans(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_SPLIT_4KB_TRANS, \ 0, NULL) == HAL_OK) #define ath_hal_self_linked_final_rxdesc(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_RXDESC_SELFLINK, \ 0, NULL) == HAL_OK) #define ath_hal_gtxto_supported(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_GTXTO, 0, NULL) == HAL_OK) #define ath_hal_has_long_rxdesc_tsf(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_LONG_RXDESC_TSF, \ 0, NULL) == HAL_OK) #define ath_hal_setuprxdesc(_ah, _ds, _size, _intreq) \ ((*(_ah)->ah_setupRxDesc)((_ah), (_ds), (_size), (_intreq))) #define ath_hal_rxprocdesc(_ah, _ds, _dspa, _dsnext, _rs) \ ((*(_ah)->ah_procRxDesc)((_ah), (_ds), (_dspa), (_dsnext), 0, (_rs))) #define ath_hal_setuptxdesc(_ah, _ds, _plen, _hlen, _atype, _txpow, \ _txr0, _txtr0, _keyix, _ant, _flags, \ _rtsrate, _rtsdura) \ ((*(_ah)->ah_setupTxDesc)((_ah), (_ds), (_plen), (_hlen), (_atype), \ (_txpow), (_txr0), (_txtr0), (_keyix), (_ant), \ (_flags), (_rtsrate), (_rtsdura), 0, 0, 0)) #define ath_hal_setupxtxdesc(_ah, _ds, \ _txr1, _txtr1, _txr2, _txtr2, _txr3, _txtr3) \ ((*(_ah)->ah_setupXTxDesc)((_ah), (_ds), \ (_txr1), (_txtr1), (_txr2), (_txtr2), (_txr3), (_txtr3))) #define ath_hal_filltxdesc(_ah, _ds, _b, _l, _did, _qid, _first, _last, _ds0) \ ((*(_ah)->ah_fillTxDesc)((_ah), (_ds), (_b), (_l), (_did), (_qid), \ (_first), (_last), (_ds0))) #define ath_hal_txprocdesc(_ah, _ds, _ts) \ ((*(_ah)->ah_procTxDesc)((_ah), (_ds), (_ts))) #define ath_hal_gettxintrtxqs(_ah, _txqs) \ ((*(_ah)->ah_getTxIntrQueue)((_ah), (_txqs))) #define ath_hal_gettxcompletionrates(_ah, _ds, _rates, _tries) \ ((*(_ah)->ah_getTxCompletionRates)((_ah), (_ds), (_rates), (_tries))) #define ath_hal_settxdesclink(_ah, _ds, _link) \ ((*(_ah)->ah_setTxDescLink)((_ah), (_ds), (_link))) #define ath_hal_gettxdesclink(_ah, _ds, _link) \ ((*(_ah)->ah_getTxDescLink)((_ah), (_ds), (_link))) #define ath_hal_gettxdesclinkptr(_ah, _ds, _linkptr) \ ((*(_ah)->ah_getTxDescLinkPtr)((_ah), (_ds), (_linkptr))) #define ath_hal_setuptxstatusring(_ah, _tsstart, _tspstart, _size) \ ((*(_ah)->ah_setupTxStatusRing)((_ah), (_tsstart), (_tspstart), \ (_size))) #define ath_hal_gettxrawtxdesc(_ah, _txstatus) \ ((*(_ah)->ah_getTxRawTxDesc)((_ah), (_txstatus))) #define ath_hal_setupfirsttxdesc(_ah, _ds, _aggrlen, _flags, _txpower, \ _txr0, _txtr0, _antm, _rcr, _rcd) \ ((*(_ah)->ah_setupFirstTxDesc)((_ah), (_ds), (_aggrlen), (_flags), \ (_txpower), (_txr0), (_txtr0), (_antm), (_rcr), (_rcd))) #define ath_hal_chaintxdesc(_ah, _ds, _bl, _sl, _pktlen, _hdrlen, _type, \ _keyix, _cipher, _delims, _first, _last, _lastaggr) \ ((*(_ah)->ah_chainTxDesc)((_ah), (_ds), (_bl), (_sl), \ (_pktlen), (_hdrlen), (_type), (_keyix), (_cipher), (_delims), \ (_first), (_last), (_lastaggr))) #define ath_hal_setuplasttxdesc(_ah, _ds, _ds0) \ ((*(_ah)->ah_setupLastTxDesc)((_ah), (_ds), (_ds0))) #define ath_hal_set11nratescenario(_ah, _ds, _dur, _rt, _series, _ns, _flags) \ ((*(_ah)->ah_set11nRateScenario)((_ah), (_ds), (_dur), (_rt), \ (_series), (_ns), (_flags))) #define ath_hal_set11n_aggr_first(_ah, _ds, _len, _num) \ ((*(_ah)->ah_set11nAggrFirst)((_ah), (_ds), (_len), (_num))) #define ath_hal_set11n_aggr_middle(_ah, _ds, _num) \ ((*(_ah)->ah_set11nAggrMiddle)((_ah), (_ds), (_num))) #define ath_hal_set11n_aggr_last(_ah, _ds) \ ((*(_ah)->ah_set11nAggrLast)((_ah), (_ds))) #define ath_hal_set11nburstduration(_ah, _ds, _dur) \ ((*(_ah)->ah_set11nBurstDuration)((_ah), (_ds), (_dur))) #define ath_hal_clr11n_aggr(_ah, _ds) \ ((*(_ah)->ah_clr11nAggr)((_ah), (_ds))) #define ath_hal_set11n_virtmorefrag(_ah, _ds, _v) \ ((*(_ah)->ah_set11nVirtMoreFrag)((_ah), (_ds), (_v))) #define ath_hal_gpioCfgOutput(_ah, _gpio, _type) \ ((*(_ah)->ah_gpioCfgOutput)((_ah), (_gpio), (_type))) #define ath_hal_gpioset(_ah, _gpio, _b) \ ((*(_ah)->ah_gpioSet)((_ah), (_gpio), (_b))) #define ath_hal_gpioget(_ah, _gpio) \ ((*(_ah)->ah_gpioGet)((_ah), (_gpio))) #define ath_hal_gpiosetintr(_ah, _gpio, _b) \ ((*(_ah)->ah_gpioSetIntr)((_ah), (_gpio), (_b))) /* * PCIe suspend/resume/poweron/poweroff related macros */ #define ath_hal_enablepcie(_ah, _restore, _poweroff) \ ((*(_ah)->ah_configPCIE)((_ah), (_restore), (_poweroff))) #define ath_hal_disablepcie(_ah) \ ((*(_ah)->ah_disablePCIE)((_ah))) /* * This is badly-named; you need to set the correct parameters * to begin to receive useful radar events; and even then * it doesn't "enable" DFS. See the ath_dfs/null/ module for * more information. */ #define ath_hal_enabledfs(_ah, _param) \ ((*(_ah)->ah_enableDfs)((_ah), (_param))) #define ath_hal_getdfsthresh(_ah, _param) \ ((*(_ah)->ah_getDfsThresh)((_ah), (_param))) #define ath_hal_getdfsdefaultthresh(_ah, _param) \ ((*(_ah)->ah_getDfsDefaultThresh)((_ah), (_param))) #define ath_hal_procradarevent(_ah, _rxs, _fulltsf, _buf, _event) \ ((*(_ah)->ah_procRadarEvent)((_ah), (_rxs), (_fulltsf), \ (_buf), (_event))) #define ath_hal_is_fast_clock_enabled(_ah) \ ((*(_ah)->ah_isFastClockEnabled)((_ah))) #define ath_hal_radar_wait(_ah, _chan) \ ((*(_ah)->ah_radarWait)((_ah), (_chan))) #define ath_hal_get_mib_cycle_counts(_ah, _sample) \ ((*(_ah)->ah_getMibCycleCounts)((_ah), (_sample))) #define ath_hal_get_chan_ext_busy(_ah) \ ((*(_ah)->ah_get11nExtBusy)((_ah))) #define ath_hal_setchainmasks(_ah, _txchainmask, _rxchainmask) \ ((*(_ah)->ah_setChainMasks)((_ah), (_txchainmask), (_rxchainmask))) #define ath_hal_spectral_supported(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_SPECTRAL_SCAN, 0, NULL) == HAL_OK) #define ath_hal_spectral_get_config(_ah, _p) \ ((*(_ah)->ah_spectralGetConfig)((_ah), (_p))) #define ath_hal_spectral_configure(_ah, _p) \ ((*(_ah)->ah_spectralConfigure)((_ah), (_p))) #define ath_hal_spectral_start(_ah) \ ((*(_ah)->ah_spectralStart)((_ah))) #define ath_hal_spectral_stop(_ah) \ ((*(_ah)->ah_spectralStop)((_ah))) #define ath_hal_btcoex_supported(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_BT_COEX, 0, NULL) == HAL_OK) #define ath_hal_btcoex_set_info(_ah, _info) \ ((*(_ah)->ah_btCoexSetInfo)((_ah), (_info))) #define ath_hal_btcoex_set_config(_ah, _cfg) \ ((*(_ah)->ah_btCoexSetConfig)((_ah), (_cfg))) #define ath_hal_btcoex_set_qcu_thresh(_ah, _qcuid) \ ((*(_ah)->ah_btCoexSetQcuThresh)((_ah), (_qcuid))) #define ath_hal_btcoex_set_weights(_ah, _weight) \ ((*(_ah)->ah_btCoexSetWeights)((_ah), (_weight))) #define ath_hal_btcoex_set_weights(_ah, _weight) \ ((*(_ah)->ah_btCoexSetWeights)((_ah), (_weight))) #define ath_hal_btcoex_set_bmiss_thresh(_ah, _thr) \ ((*(_ah)->ah_btCoexSetBmissThresh)((_ah), (_thr))) #define ath_hal_btcoex_set_parameter(_ah, _attrib, _val) \ ((*(_ah)->ah_btCoexSetParameter)((_ah), (_attrib), (_val))) #define ath_hal_btcoex_enable(_ah) \ ((*(_ah)->ah_btCoexEnable)((_ah))) #define ath_hal_btcoex_disable(_ah) \ ((*(_ah)->ah_btCoexDisable)((_ah))) #define ath_hal_div_comb_conf_get(_ah, _conf) \ ((*(_ah)->ah_divLnaConfGet)((_ah), (_conf))) #define ath_hal_div_comb_conf_set(_ah, _conf) \ ((*(_ah)->ah_divLnaConfSet)((_ah), (_conf))) #endif /* _DEV_ATH_ATHVAR_H */