#include <sys/cdefs.h>
#include "opt_inet.h"
#include "opt_ath.h"
#include "opt_ah.h"
#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sysctl.h>
#include <sys/mbuf.h>
#include <sys/malloc.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/errno.h>
#include <sys/callout.h>
#include <sys/bus.h>
#include <sys/endian.h>
#include <sys/kthread.h>
#include <sys/taskqueue.h>
#include <sys/priv.h>
#include <sys/module.h>
#include <sys/ktr.h>
#include <sys/smp.h>
#include <machine/bus.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_types.h>
#include <net/if_arp.h>
#include <net/ethernet.h>
#include <net/if_llc.h>
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_regdomain.h>
#ifdef IEEE80211_SUPPORT_SUPERG
#include <net80211/ieee80211_superg.h>
#endif
#ifdef IEEE80211_SUPPORT_TDMA
#include <net80211/ieee80211_tdma.h>
#endif
#include <net/bpf.h>
#ifdef INET
#include <netinet/in.h>
#include <netinet/if_ether.h>
#endif
#include <dev/ath/if_athvar.h>
#include <dev/ath/ath_hal/ah_devid.h>
#include <dev/ath/ath_hal/ah_diagcodes.h>
#include <dev/ath/if_ath_debug.h>
#include <dev/ath/if_ath_misc.h>
#include <dev/ath/if_ath_tsf.h>
#include <dev/ath/if_ath_tx.h>
#include <dev/ath/if_ath_sysctl.h>
#include <dev/ath/if_ath_led.h>
#include <dev/ath/if_ath_keycache.h>
#include <dev/ath/if_ath_rx.h>
#include <dev/ath/if_ath_rx_edma.h>
#include <dev/ath/if_ath_tx_edma.h>
#include <dev/ath/if_ath_beacon.h>
#include <dev/ath/if_ath_btcoex.h>
#include <dev/ath/if_ath_btcoex_mci.h>
#include <dev/ath/if_ath_spectral.h>
#include <dev/ath/if_ath_lna_div.h>
#include <dev/ath/if_athdfs.h>
#include <dev/ath/if_ath_ioctl.h>
#include <dev/ath/if_ath_descdma.h>
#ifdef ATH_TX99_DIAG
#include <dev/ath/ath_tx99/ath_tx99.h>
#endif
#ifdef ATH_DEBUG_ALQ
#include <dev/ath/if_ath_alq.h>
#endif
#define ATH_SW_PSQ
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 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_tsfoor_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
static int ath_set_quiet_ie(struct ieee80211_node *, uint8_t *);
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 <dev/ath/if_ath_tdma.h>
#endif
SYSCTL_DECL(_hw_ath);
static int ath_longcalinterval = 30;
SYSCTL_INT(_hw_ath, OID_AUTO, longcal, CTLFLAG_RW, &ath_longcalinterval,
0, "long chip calibration interval (secs)");
static int ath_shortcalinterval = 100;
SYSCTL_INT(_hw_ath, OID_AUTO, shortcal, CTLFLAG_RW, &ath_shortcalinterval,
0, "short chip calibration interval (msecs)");
static int ath_resetcalinterval = 20*60;
SYSCTL_INT(_hw_ath, OID_AUTO, resetcal, CTLFLAG_RW, &ath_resetcalinterval,
0, "reset chip calibration results (secs)");
static int ath_anicalinterval = 100;
SYSCTL_INT(_hw_ath, OID_AUTO, anical, CTLFLAG_RW, &ath_anicalinterval,
0, "ANI calibration (msecs)");
int ath_rxbuf = ATH_RXBUF;
SYSCTL_INT(_hw_ath, OID_AUTO, rxbuf, CTLFLAG_RWTUN, &ath_rxbuf,
0, "rx buffers allocated");
int ath_txbuf = ATH_TXBUF;
SYSCTL_INT(_hw_ath, OID_AUTO, txbuf, CTLFLAG_RWTUN, &ath_txbuf,
0, "tx buffers allocated");
int ath_txbuf_mgmt = ATH_MGMT_TXBUF;
SYSCTL_INT(_hw_ath, OID_AUTO, txbuf_mgmt, CTLFLAG_RWTUN, &ath_txbuf_mgmt,
0, "tx (mgmt) buffers allocated");
int ath_bstuck_threshold = 4;
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)
{
switch (sc->sc_txqsetup &~ (1<<sc->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;
}
}
void
_ath_power_setpower(struct ath_softc *sc, int power_state, int selfgen,
const char *file, int line)
{
ATH_LOCK_ASSERT(sc);
DPRINTF(sc, ATH_DEBUG_PWRSAVE, "%s: (%s:%d) state=%d, refcnt=%d, target=%d, cur=%d\n",
__func__,
file,
line,
power_state,
sc->sc_powersave_refcnt,
sc->sc_target_powerstate,
sc->sc_cur_powerstate);
sc->sc_target_powerstate = power_state;
if ((sc->sc_powersave_refcnt == 0 || power_state == HAL_PM_AWAKE) &&
power_state != sc->sc_cur_powerstate) {
sc->sc_cur_powerstate = power_state;
ath_hal_setpower(sc->sc_ah, power_state);
if (selfgen &&
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);
}
}
}
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 (sc->sc_cur_powerstate == HAL_PM_AWAKE) {
ath_hal_setselfgenpower(sc->sc_ah, power_state);
}
}
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;
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);
}
}
}
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);
}
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);
}
}
static void
ath_setup_hal_config(struct ath_softc *sc, HAL_OPS_CONFIG *ah_config)
{
if (sc->sc_pci_devinfo & (ATH_PCI_CUS198 | ATH_PCI_CUS230)) {
ah_config->ath_hal_ext_lna_ctl_gpio = 0x200;
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;
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_BT_ANT_DIV)
device_printf(sc->sc_dev,
"Bluetooth Antenna Diversity card detected\n");
if (sc->sc_pci_devinfo & ATH_PCI_KILLER)
device_printf(sc->sc_dev, "Killer Wireless card detected\n");
#if 0
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
}
static int
ath_fetch_mac_kenv(struct ath_softc *sc, uint8_t *macaddr)
{
char devid_str[32];
int local_mac = 0;
char *local_macstr;
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;
device_printf(sc->sc_dev,
"Overriding MAC address from environment: '%s'\n",
local_macstr);
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) {
local_mac = 1;
for (i = 0; i < ETHER_ADDR_LEN; i++)
macaddr[i] = tmpmac[i];
}
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);
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;
#ifdef ATH_DEBUG
sc->sc_debug = ath_debug;
#endif
ATH_LOCK(sc);
ath_power_setpower(sc, HAL_PM_AWAKE, 1);
ATH_UNLOCK(sc);
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;
}
sc->sc_mrretry = ath_hal_setupxtxdesc(ah, NULL, 0,0, 0,0, 0,0);
if (ath_hal_hwphycounters(ah))
sc->sc_needmib = 1;
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;
}
for (i = 0; i < sc->sc_keymax; i++)
ath_hal_keyreset(ah, i);
error = ath_getchannels(sc);
if (error != 0)
goto bad;
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);
ath_setcurmode(sc, IEEE80211_MODE_11A);
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;
}
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_tsfoortask, 0, ath_tsfoor_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);
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;
}
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)) {
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];
}
sc->sc_tx.xmit_attach_comp_func(sc);
sc->sc_setdefantenna = ath_setdefantenna;
sc->sc_rc = ath_rate_attach(sc);
if (sc->sc_rc == NULL) {
error = EIO;
goto bad2;
}
if (! ath_dfs_attach(sc)) {
device_printf(sc->sc_dev,
"%s: unable to attach DFS\n", __func__);
error = EIO;
goto bad2;
}
if (ath_spectral_attach(sc) < 0) {
device_printf(sc->sc_dev,
"%s: unable to attach spectral\n", __func__);
error = EIO;
goto bad2;
}
if (ath_btcoex_attach(sc) < 0) {
device_printf(sc->sc_dev,
"%s: unable to attach bluetooth coexistence\n", __func__);
error = EIO;
goto bad2;
}
if (ath_lna_div_attach(sc) < 0) {
device_printf(sc->sc_dev,
"%s: unable to attach LNA diversity\n", __func__);
error = EIO;
goto bad2;
}
TASK_INIT(&sc->sc_dfstask, 0, ath_dfs_tasklet, sc);
sc->sc_blinking = 0;
sc->sc_ledstate = 1;
sc->sc_ledon = 0;
sc->sc_ledidle = (2700*hz)/1000;
callout_init(&sc->sc_ledtimer, 1);
sc->sc_hardled = (1 == 0);
sc->sc_led_net_pin = -1;
sc->sc_led_pwr_pin = -1;
sc->sc_softled = (devid == AR5212_DEVID_IBM || devid == AR5211_DEVID);
ath_led_config(sc);
ath_hal_setledstate(ah, HAL_LED_INIT);
ic->ic_phytype = IEEE80211_T_OFDM;
ic->ic_opmode = IEEE80211_M_STA;
ic->ic_caps =
IEEE80211_C_STA
| IEEE80211_C_IBSS
| IEEE80211_C_HOSTAP
| IEEE80211_C_MONITOR
| IEEE80211_C_AHDEMO
| IEEE80211_C_WDS
| IEEE80211_C_MBSS
| IEEE80211_C_SHPREAMBLE
| IEEE80211_C_SHSLOT
| IEEE80211_C_WPA
#ifndef ATH_ENABLE_11N
| IEEE80211_C_BGSCAN
#endif
| IEEE80211_C_TXFRAG
#ifdef ATH_ENABLE_DFS
| IEEE80211_C_DFS
#endif
| IEEE80211_C_PMGT
| IEEE80211_C_SWSLEEP
;
ic->ic_flags_ext |= IEEE80211_FEXT_SEQNO_OFFLOAD;
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;
if (ath_hal_ciphersupported(ah, HAL_CIPHER_MIC))
ic->ic_cryptocaps |= IEEE80211_CRYPTO_TKIPMIC;
if (ath_hal_hastkipsplit(ah) ||
!ath_hal_settkipsplit(ah, AH_FALSE))
sc->sc_splitmic = 1;
if (ath_hal_haswmetkipmic(ah))
sc->sc_wmetkipmic = 1;
}
sc->sc_hasclrkey = ath_hal_ciphersupported(ah, HAL_CIPHER_CLR);
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);
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);
}
}
if (ath_hal_hastpc(ah) || ath_hal_hastxpowlimit(ah))
ic->ic_caps |= IEEE80211_C_TXPMGT;
if (sc->sc_ac2q[WME_AC_BE] != sc->sc_ac2q[WME_AC_BK])
ic->ic_caps |= IEEE80211_C_WME;
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);
if (ath_hal_get_rx_tsf_prec(ah, &i)) {
if (i == 32) {
sc->sc_rxtsf32 = 1;
}
if (bootverbose)
device_printf(sc->sc_dev, "RX timestamp: %d bits\n", i);
}
if (ath_hal_get_tx_tsf_prec(ah, &i)) {
if (bootverbose)
device_printf(sc->sc_dev, "TX timestamp: %d bits\n", i);
}
sc->sc_hasenforcetxop = ath_hal_hasenforcetxop(ah);
sc->sc_rx_lnamixer = ath_hal_hasrxlnamixer(ah);
sc->sc_hasdivcomb = ath_hal_hasdivantcomb(ah);
#if 0
if (sc->sc_pci_devinfo & (ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_AR9565_2ANT)) {
device_printf(sc->sc_dev, "%s: WB335: disabling LNA mixer diversity\n",
__func__);
sc->sc_dolnadiv = 0;
}
#endif
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;
ic->ic_tdma_update = ath_tdma_update;
}
#endif
sc->sc_txq_data_minfree = 10;
sc->sc_txq_mcastq_maxdepth = MIN(64, ath_txbuf / 4);
sc->sc_txq_node_psq_maxdepth = 16;
sc->sc_txq_node_maxdepth = MIN(128, ath_txbuf / 4);
sc->sc_cabq_enable = 1;
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);
}
ath_hal_getrxchainmask(ah, &sc->sc_rxchainmask);
ath_hal_gettxchainmask(ah, &sc->sc_txchainmask);
sc->sc_mrrprot = 0;
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
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;
ic->ic_htcaps = IEEE80211_HTC_HT
| IEEE80211_HTC_AMPDU
| IEEE80211_HTC_AMSDU
| IEEE80211_HTCAP_MAXAMSDU_3839
| IEEE80211_HTCAP_SMPS_OFF;
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;
(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;
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);
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 |
IEEE80211_HTC_TXLDPC;
}
device_printf(sc->sc_dev,
"[HT] %d RX streams; %d TX streams\n", rxs, txs);
}
#endif
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;
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");
}
TAILQ_INIT(&sc->sc_rx_rxlist[HAL_RX_QUEUE_HP]);
TAILQ_INIT(&sc->sc_rx_rxlist[HAL_RX_QUEUE_LP]);
ic->ic_flags |= IEEE80211_F_DATAPAD;
sc->sc_defant = ath_hal_getdefantenna(ah);
sc->sc_hasveol = ath_hal_hasveol(ah);
if (ath_fetch_mac_kenv(sc, ic->ic_macaddr) == 0) {
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);
ic->ic_max_keyix = sc->sc_keymax;
ieee80211_ifattach(ic);
ic->ic_setregdomain = ath_setregdomain;
ic->ic_getradiocaps = ath_getradiocaps;
sc->sc_opmode = HAL_M_STA;
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
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
ic->ic_set_quiet = ath_set_quiet_ie;
#ifdef ATH_ENABLE_RADIOTAP_VENDOR_EXT
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
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
#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
ath_sysctlattach(sc);
ath_sysctl_stats_attach(sc);
ath_sysctl_hal_attach(sc);
if (bootverbose)
ieee80211_announce(ic);
ath_announce(sc);
ATH_LOCK(sc);
ath_power_setpower(sc, HAL_PM_FULL_SLEEP, 1);
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)
{
ATH_LOCK(sc);
ath_power_set_power_state(sc, HAL_PM_AWAKE);
ath_power_setpower(sc, HAL_PM_AWAKE, 1);
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);
return 0;
}
static void
assign_address(struct ath_softc *sc, uint8_t mac[IEEE80211_ADDR_LEN], int clone)
{
int i;
if (clone && sc->sc_hasbmask) {
for (i = 0; i < 8; i++)
if ((sc->sc_bssidmask & (1<<i)) == 0)
break;
if (i != 0)
mac[0] |= (i << 2)|0x2;
} else
i = 0;
sc->sc_bssidmask |= 1<<i;
sc->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<<i);
mask = 0xff;
for (i = 1; i < 8; i++)
if (sc->sc_bssidmask & (1<<i))
mask &= ~((i<<2)|0x2);
sc->sc_hwbssidmask[0] |= mask;
}
}
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;
}
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;
switch (opmode) {
case IEEE80211_M_STA:
if (sc->sc_nstavaps != 0) {
device_printf(sc->sc_dev, "only 1 sta vap supported\n");
goto bad;
}
if (sc->sc_nvaps) {
flags |= IEEE80211_CLONE_NOBEACONS;
}
if (flags & IEEE80211_CLONE_NOBEACONS) {
ic_opmode = IEEE80211_M_HOSTAP;
}
break;
case IEEE80211_M_IBSS:
if (sc->sc_nvaps != 0) {
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;
}
#endif
case IEEE80211_M_MONITOR:
if (sc->sc_nvaps != 0 && ic->ic_opmode != opmode) {
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;
}
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;
}
if (needbeacon & TAILQ_EMPTY(&sc->sc_bbuf)) {
device_printf(sc->sc_dev, "no beacon buffer available\n");
goto bad;
}
if (opmode == IEEE80211_M_HOSTAP || opmode == IEEE80211_M_MBSS || opmode == IEEE80211_M_STA) {
assign_address(sc, mac, flags & IEEE80211_CLONE_BSSID);
ath_hal_setbssidmask(sc->sc_ah, sc->sc_hwbssidmask);
}
vap = &avp->av_vap;
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;
}
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;
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;
vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_8;
vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_64K;
vap->iv_ampdu_limit = IEEE80211_HTCAP_MAXRXAMPDU_64K;
avp->av_bslot = -1;
if (needbeacon) {
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) {
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) {
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;
sc->sc_stagbeacons = 0;
}
#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:
break;
}
if (sc->sc_hastsfadd) {
ath_hal_settsfadjust(sc->sc_ah, sc->sc_stagbeacons);
}
if (flags & IEEE80211_CLONE_NOBEACONS) {
sc->sc_swbmiss = 1;
}
ATH_UNLOCK(sc);
ieee80211_vap_attach(vap, ieee80211_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) {
ath_hal_intrset(ah, 0);
ath_stoprecv(sc, 1);
ath_rx_flush(sc);
ath_draintxq(sc, ATH_RESET_DEFAULT);
}
ieee80211_vap_detach(vap);
ath_draintxq(sc, ATH_RESET_DEFAULT);
ATH_LOCK(sc);
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);
}
ath_tx_draintxq(sc, &avp->av_mcastq);
}
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_STA ||
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
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) {
if (ath_startrecv(sc) != 0)
device_printf(sc->sc_dev,
"%s: unable to restart recv logic\n", __func__);
if (sc->sc_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);
}
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);
ath_hal_intrset(sc->sc_ah, 0);
taskqueue_block(sc->sc_tq);
ATH_LOCK(sc);
callout_stop(&sc->sc_cal_ch);
ATH_UNLOCK(sc);
ath_hal_enablepcie(sc->sc_ah, 1, 1);
}
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);
}
static void
ath_update_chainmasks(struct ath_softc *sc, struct ieee80211_channel *chan)
{
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);
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);
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, 1);
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);
ath_dfs_radar_enable(sc, ic->ic_curchan);
ath_spectral_enable(sc, ic->ic_curchan);
ath_btcoex_enable(sc, ic->ic_curchan);
if (sc->sc_hasenforcetxop && sc->sc_tdma)
ath_hal_setenforcetxop(sc->sc_ah, 1);
else
ath_hal_setenforcetxop(sc->sc_ah, 0);
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);
}
void
ath_shutdown(struct ath_softc *sc)
{
ATH_LOCK(sc);
ath_stop(sc);
ATH_UNLOCK(sc);
}
void
ath_intr(void *arg)
{
struct ath_softc *sc = arg;
struct ath_hal *ah = sc->sc_ah;
HAL_INT status = 0;
uint32_t txqs;
ATH_PCU_LOCK(sc);
if (sc->sc_inreset_cnt) {
HAL_INT status;
ath_hal_getisr(ah, &status);
ath_hal_intrset(ah, 0);
DPRINTF(sc, ATH_DEBUG_ANY,
"%s: in reset, ignoring: status=0x%x\n",
__func__, status);
ATH_PCU_UNLOCK(sc);
return;
}
if (sc->sc_invalid) {
DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid; ignored\n", __func__);
ATH_PCU_UNLOCK(sc);
return;
}
if (!ath_hal_intrpend(ah)) {
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);
ath_hal_intrset(ah, 0);
ATH_PCU_UNLOCK(sc);
ATH_LOCK(sc);
ath_power_restore_power_state(sc);
ATH_UNLOCK(sc);
return;
}
ath_hal_getisr(ah, &status);
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
#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
if (ah->ah_syncstate != 0) {
int i;
for (i = 0; i < 32; i++)
if (ah->ah_syncstate & (1 << i))
sc->sc_intr_stats.sync_intr[i]++;
}
status &= sc->sc_imask;
if (status == 0x0) {
ATH_PCU_UNLOCK(sc);
ATH_LOCK(sc);
ath_power_restore_power_state(sc);
ATH_UNLOCK(sc);
return;
}
sc->sc_intr_cnt++;
ATH_PCU_UNLOCK(sc);
if (status & HAL_INT_FATAL) {
sc->sc_stats.ast_hardware++;
ath_hal_intrset(ah, 0);
taskqueue_enqueue(sc->sc_tq, &sc->sc_fataltask);
} else {
if (status & HAL_INT_SWBA) {
#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
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);
sc->sc_stats.ast_rxeol++;
imask = sc->sc_imask;
imask &= ~(HAL_INT_RXEOL | HAL_INT_RXORN);
ath_hal_intrset(ah, imask);
if (! sc->sc_kickpcu)
sc->sc_rxlink = NULL;
sc->sc_kickpcu = 1;
ATH_PCU_UNLOCK(sc);
}
sc->sc_rx.recv_sched(sc, 1);
}
if (status & HAL_INT_TXURN) {
sc->sc_stats.ast_txurn++;
ath_hal_updatetxtriglevel(ah, AH_TRUE);
}
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++;
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);
ath_hal_intrset(ah, 0);
ath_hal_mibevent(ah, &sc->sc_halstats);
if (sc->sc_kickpcu == 0)
ath_hal_intrset(ah, sc->sc_imask);
ATH_PCU_UNLOCK(sc);
}
if (status & HAL_INT_RXORN) {
ATH_KTR(sc, ATH_KTR_ERROR, 0, "ath_intr: RXORN");
sc->sc_stats.ast_rxorn++;
}
if (status & HAL_INT_TSFOOR) {
sc->sc_stats.ast_tsfoor++;
ATH_LOCK(sc);
ath_power_setpower(sc, HAL_PM_AWAKE, 0);
ATH_UNLOCK(sc);
taskqueue_enqueue(sc->sc_tq, &sc->sc_tsfoortask);
device_printf(sc->sc_dev, "%s: TSFOOR\n", __func__);
}
if (status & HAL_INT_MCI) {
ath_btcoex_mci_intr(sc);
}
}
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");
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, HAL_RESET_FORCE_COLD);
}
static void
ath_bmiss_vap(struct ieee80211vap *vap)
{
struct ath_softc *sc = vap->iv_ic->ic_softc;
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);
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;
}
}
ATH_LOCK(sc);
ath_power_setpower(sc, HAL_PM_AWAKE, 0);
ath_power_restore_power_state(sc);
ATH_UNLOCK(sc);
DPRINTF(sc, ATH_DEBUG_BEACON,
"%s: forced awake; force syncbeacon=1\n", __func__);
if ((vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) == 0) {
sc->sc_syncbeacon = 1;
}
ATH_VAP(vap)->av_bmiss(vap);
}
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);
if (ath_hal_gethangstate(sc->sc_ah, 0xff, &hangs) && hangs != 0) {
ath_reset(sc, ATH_RESET_NOLOSS, HAL_RESET_BBPANIC);
device_printf(sc->sc_dev,
"bb hang detected (0x%x), resetting\n", hangs);
} else {
ath_reset(sc, ATH_RESET_NOLOSS, HAL_RESET_FORCE_COLD);
ieee80211_beacon_miss(&sc->sc_ic);
}
sc->sc_syncbeacon = 1;
ATH_LOCK(sc);
ath_power_restore_power_state(sc);
ATH_UNLOCK(sc);
}
static void
ath_tsfoor_proc(void *arg, int pending)
{
struct ath_softc *sc = arg;
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_reset(sc, ATH_RESET_NOLOSS, HAL_RESET_FORCE_COLD);
sc->sc_syncbeacon = 1;
ATH_LOCK(sc);
ath_power_restore_power_state(sc);
ATH_UNLOCK(sc);
}
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 void
ath_vap_clear_quiet_ie(struct ath_softc *sc)
{
struct ieee80211com *ic = &sc->sc_ic;
struct ieee80211vap *vap;
struct ath_vap *avp;
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
avp = ATH_VAP(vap);
memset(&avp->quiet_ie, 0, sizeof(avp->quiet_ie));
}
}
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);
ath_power_setselfgen(sc, HAL_PM_AWAKE);
ath_power_set_power_state(sc, HAL_PM_AWAKE);
ath_power_setpower(sc, HAL_PM_AWAKE, 1);
ath_stop(sc);
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_vap_clear_quiet_ie(sc);
ath_chan_change(sc, ic->ic_curchan);
ath_dfs_radar_enable(sc, ic->ic_curchan);
ath_spectral_enable(sc, ic->ic_curchan);
ath_btcoex_enable(sc, ic->ic_curchan);
if (sc->sc_hasenforcetxop && sc->sc_tdma)
ath_hal_setenforcetxop(sc->sc_ah, 1);
else
ath_hal_setenforcetxop(sc->sc_ah, 0);
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;
sc->sc_beacons = 0;
if (ath_startrecv(sc) != 0) {
device_printf(sc->sc_dev, "unable to start recv logic\n");
ath_power_restore_power_state(sc);
return (ENODEV);
}
sc->sc_imask = HAL_INT_RX | HAL_INT_TX
| HAL_INT_RXORN | HAL_INT_TXURN
| HAL_INT_FATAL | HAL_INT_GLOBAL;
if (sc->sc_isedma)
sc->sc_imask |= (HAL_INT_RXHP | HAL_INT_RXLP);
if (! sc->sc_isedma)
sc->sc_imask |= HAL_INT_RXEOL;
if (sc->sc_btcoex_mci)
sc->sc_imask |= HAL_INT_MCI;
if (sc->sc_needmib && ic->ic_opmode == IEEE80211_M_STA)
sc->sc_imask |= HAL_INT_MIB;
if (ic->ic_opmode == IEEE80211_M_STA)
sc->sc_imask |= HAL_INT_TSFOOR;
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);
ath_power_set_power_state(sc, HAL_PM_AWAKE);
if (sc->sc_running) {
#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);
}
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);
}
ath_power_restore_power_state(sc);
}
#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);
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);
}
#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);
pause("ath_reset_grablock", msecs_to_ticks(100));
i--;
ATH_PCU_LOCK(sc);
} while (i > 0);
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
int
ath_reset(struct ath_softc *sc, ATH_RESET_TYPE reset_type,
HAL_RESET_TYPE ah_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__);
ATH_PCU_UNLOCK_ASSERT(sc);
ATH_UNLOCK_ASSERT(sc);
taskqueue_block(sc->sc_tq);
ATH_LOCK(sc);
ath_power_set_power_state(sc, HAL_PM_AWAKE);
ATH_UNLOCK(sc);
ATH_PCU_LOCK(sc);
if (ath_reset_grablock(sc, 1) == 0) {
device_printf(sc->sc_dev, "%s: concurrent reset! Danger!\n",
__func__);
}
ath_hal_intrset(ah, 0);
ath_txrx_stop_locked(sc);
ATH_PCU_UNLOCK(sc);
ath_stoprecv(sc, (reset_type != ATH_RESET_NOLOSS));
ath_rx_flush(sc);
ath_draintxq(sc, reset_type);
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_TRUE,
ah_reset_type, &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);
ath_vap_clear_quiet_ie(sc);
ath_dfs_radar_enable(sc, ic->ic_curchan);
ath_spectral_enable(sc, ic->ic_curchan);
ath_btcoex_enable(sc, ic->ic_curchan);
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)
device_printf(sc->sc_dev,
"%s: unable to start recv logic\n", __func__);
ath_chan_change(sc, ic->ic_curchan);
if (sc->sc_beacons) {
#ifdef IEEE80211_SUPPORT_TDMA
if (sc->sc_tdma)
ath_tdma_config(sc, NULL);
else
#endif
ath_beacon_config(sc, NULL);
}
ATH_PCU_LOCK(sc);
sc->sc_inreset_cnt--;
sc->sc_txstart_cnt++;
ath_hal_intrset(ah, sc->sc_imask);
ATH_PCU_UNLOCK(sc);
ath_txrx_start(sc);
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);
ath_tx_kick(sc);
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 (!ath_hal_gettpc(ah))
ath_hal_settxpowlimit(ah, ic->ic_txpowlimit);
return 0;
}
return ath_reset(sc, ATH_RESET_FULL, HAL_RESET_NORMAL);
}
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--;
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) {
DPRINTF(sc, ATH_DEBUG_XMIT, "%s: %s\n", __func__,
TAILQ_FIRST(&sc->sc_txbuf) == NULL ?
"out of xmit buffers" : "xmit buffer busy");
return NULL;
}
bf->bf_flags = 0;
if (btype == ATH_BUFTYPE_MGMT)
bf->bf_flags |= ATH_BUF_MGMT;
else
bf->bf_flags &= (~ATH_BUF_MGMT);
bf->bf_next = NULL;
bf->bf_last = NULL;
bf->bf_comp = NULL;
bzero(&bf->bf_state, sizeof(bf->bf_state));
if (sc->sc_isedma) {
bf->bf_descid = sc->sc_txbuf_descid;
sc->sc_txbuf_descid++;
}
return bf;
}
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;
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__));
tbf->bf_lastds = NULL;
tbf->bf_last = tbf;
tbf->bf_comp = bf->bf_comp;
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_m = NULL;
bf->bf_node = NULL;
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 (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;
}
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;
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);
}
sc->sc_txstart_cnt++;
ATH_PCU_UNLOCK(sc);
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");
ATH_TX_LOCK(sc);
ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
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;
}
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;
}
if (m->m_flags & M_EAPOL)
bf = ath_getbuf(sc, ATH_BUFTYPE_MGMT);
else
bf = ath_getbuf(sc, ATH_BUFTYPE_NORMAL);
if (bf == NULL) {
sc->sc_stats.ast_tx_nobuf++;
retval = ENOBUFS;
goto finish;
}
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);
ieee80211_free_mbuf(m);
m = NULL;
goto bad;
}
if (m->m_flags & M_FRAG) {
struct ath_buf *fbf = bf;
struct ath_buf *n_fbf = NULL;
struct mbuf *fm = m->m_nextpkt;
TAILQ_FOREACH(n_fbf, &frags, bf_list) {
fbf->bf_nextfraglen = fm->m_pkthdr.len;
fbf = n_fbf;
fm = fm->m_nextpkt;
}
}
nextfrag:
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);
ath_txfrag_cleanup(sc, &frags, ni);
ATH_TXBUF_UNLOCK(sc);
ieee80211_free_node(ni);
retval = 0;
goto finish;
}
ath_tx_update_tim(sc, ni, 1);
if (next != NULL) {
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]);
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;
}
sc->sc_wd_timer = 5;
finish:
ATH_TX_UNLOCK(sc);
ATH_PCU_LOCK(sc);
sc->sc_txstart_cnt--;
ATH_PCU_UNLOCK(sc);
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 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;
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);
}
static u_int
ath_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
{
uint32_t val, *mfilt = arg;
char *dl;
uint8_t pos;
dl = LLADDR(sdl);
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));
return (1);
}
static void
ath_update_mcast_hw(struct ath_softc *sc)
{
struct ieee80211com *ic = &sc->sc_ic;
u_int32_t mfilt[2];
if (ic->ic_allmulti == 0) {
struct ieee80211vap *vap;
mfilt[0] = mfilt[1] = 0;
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
if_foreach_llmaddr(vap->iv_ifp, ath_hash_maddr, &mfilt);
} 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]);
}
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;
rfilt = ath_calcrxfilter(sc);
ath_hal_setrxfilter(ah, rfilt);
ath_hal_setopmode(ah);
ath_hal_setmac(ah, ic->ic_macaddr);
ath_update_mcast_hw(sc);
}
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)) {
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);
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);
}
static void
ath_updateslot(struct ieee80211com *ic)
{
struct ath_softc *sc = ic->ic_softc;
if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
ic->ic_opmode == IEEE80211_M_MBSS)
sc->sc_updateslot = UPDATE;
else
ath_setslottime(sc);
}
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;
}
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, HAL_RESET_FORCE_COLD);
}
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++;
ath_reset(sc, ATH_RESET_NOLOSS, HAL_RESET_FORCE_COLD);
}
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;
}
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) {
return NULL;
}
ath_rate_node_init(sc, an);
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);
ath_tx_tid_init(sc, an);
an->an_node_stats.ns_avgbrssi = ATH_RSSI_DUMMY_MARKER;
an->an_node_stats.ns_avgrssi = ATH_RSSI_DUMMY_MARKER;
an->an_node_stats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER;
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));
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;
}
void
ath_setdefantenna(struct ath_softc *sc, u_int antenna)
{
struct ath_hal *ah = sc->sc_ah;
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);
}
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;
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) {
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<<qnum;
}
return &sc->sc_txq[qnum];
}
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;
}
static int
ath_txq_update(struct ath_softc *sc, int ac)
{
#define ATH_EXPONENT_TO_VALUE(v) ((1<<v)-1)
struct ieee80211com *ic = &sc->sc_ic;
struct ath_txq *txq = sc->sc_ac2q[ac];
struct chanAccParams chp;
struct wmeParams *wmep;
struct ath_hal *ah = sc->sc_ah;
HAL_TXQ_INFO qi;
ieee80211_wme_ic_getparams(ic, &chp);
wmep = &chp.cap_wmeParams[ac];
ath_hal_gettxqueueprops(ah, txq->axq_qnum, &qi);
#ifdef IEEE80211_SUPPORT_TDMA
if (sc->sc_tdma) {
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;
qi.tqi_readyTime = sc->sc_tdmaslotlen;
qi.tqi_burstTime = qi.tqi_readyTime;
} else {
#endif
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);
return 1;
}
#undef ATH_EXPONENT_TO_VALUE
}
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;
}
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<<txq->axq_qnum);
ATH_TXQ_LOCK_DESTROY(txq);
}
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]);
}
int
ath_tx_findrix(const struct ath_softc *sc, uint8_t rate)
{
int rix = sc->sc_rixmap[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;
if (txant >= ATH_IOCTL_STATS_NUM_TX_ANTENNA)
txant = 0;
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++;
}
if (ts->ts_flags & HAL_TX_DESC_CFG_ERR)
sc->sc_stats.ast_tx_desccfgerr++;
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;
}
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));
if (bf->bf_node) {
ATH_TX_LOCK(sc);
ath_tx_update_tim(sc, bf->bf_node, 0);
ATH_TX_UNLOCK(sc);
}
ath_tx_freebuf(sc, bf, st);
}
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 rc_framelen, int nframes, int nbad)
{
struct ath_node *an;
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, rc_framelen,
nframes, nbad);
ATH_NODE_UNLOCK(an);
}
}
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 (ni != NULL) {
ath_tx_update_stats(sc, ts, bf);
}
if (bf->bf_comp == NULL) {
if ((ts->ts_status & HAL_TXERR_FILT) == 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,
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);
}
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
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;
bf = TAILQ_FIRST(&txq->axq_q);
if (bf == NULL) {
ATH_TXQ_UNLOCK(txq);
break;
}
ds = bf->bf_lastds;
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);
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) {
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 (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_RSSI_LPF(ATH_NODE(ni)->an_node_stats.ns_avgtxrssi,
ts->ts_rssi);
}
ATH_TXQ_UNLOCK(txq);
ath_tx_process_buf_completion(sc, txq, ts, bf);
}
#ifdef IEEE80211_SUPPORT_SUPERG
if (txq->axq_depth <= 1)
ieee80211_ff_flush(ic, txq->axq_ac);
#endif
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)))
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))
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);
}
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);
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);
}
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);
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
static void
ath_txq_sched_tasklet(void *arg, int npending)
{
struct ath_softc *sc = arg;
int i;
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;
}
}
}
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;
}
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);
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;
}
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 (bf->bf_flags & ATH_BUF_BUSY) {
ATH_TXQ_LOCK(txq);
ath_txq_addholdingbuf(sc, bf);
ATH_TXQ_UNLOCK(txq);
return;
}
ATH_TXBUF_LOCK(sc);
ath_returnbuf_tail(sc, bf);
ATH_TXBUF_UNLOCK(sc);
}
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;
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;
ath_freebuf(sc, bf);
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);
bf = TAILQ_FIRST(&txq->fifo.axq_q);
if (bf != NULL) {
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);
}
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);
}
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;
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;
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_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);
}
ATH_TXQ_LOCK(txq);
ath_txq_freeholdingbuf(sc, txq);
ATH_TXQ_UNLOCK(txq);
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);
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;
if (sc->sc_invalid)
return 0;
if (!sc->sc_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);
(void) ath_hal_stoptxdma(ah, sc->sc_bhalq);
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
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);
for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
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
if (reset_type == ATH_RESET_NOLOSS) {
ath_tx_processq(sc, &sc->sc_txq[i], 0);
ATH_TXQ_LOCK(&sc->sc_txq[i]);
ath_txq_freeholdingbuf(sc, &sc->sc_txq[i]);
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
sc->sc_wd_timer = 0;
}
static void
ath_chan_change(struct ath_softc *sc, struct ieee80211_channel *chan)
{
enum ieee80211_phymode mode;
mode = ieee80211_chan2mode(chan);
if (mode != sc->sc_curmode)
ath_setcurmode(sc, mode);
sc->sc_curchan = chan;
}
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;
ATH_PCU_UNLOCK_ASSERT(sc);
ATH_UNLOCK_ASSERT(sc);
taskqueue_block(sc->sc_tq);
ATH_PCU_LOCK(sc);
ath_hal_intrset(ah, 0);
if (ath_reset_grablock(sc, 1) == 0) {
device_printf(sc->sc_dev, "%s: concurrent reset! Danger!\n",
__func__);
}
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;
#if 0
ath_hal_intrset(ah, 0);
#endif
ath_stoprecv(sc, 1);
ath_rx_flush(sc);
ath_draintxq(sc, ATH_RESET_NOLOSS);
ath_draintxq(sc, ATH_RESET_FULL);
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);
ath_vap_clear_quiet_ie(sc);
ath_dfs_radar_enable(sc, chan);
ath_spectral_enable(sc, chan);
ath_btcoex_enable(sc, ic->ic_curchan);
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) {
device_printf(sc->sc_dev,
"%s: unable to restart recv logic\n", __func__);
ret = EIO;
goto finish;
}
ath_chan_change(sc, chan);
if (sc->sc_beacons) {
#ifdef IEEE80211_SUPPORT_TDMA
if (sc->sc_tdma)
ath_tdma_config(sc, NULL);
else
#endif
ath_beacon_config(sc, NULL);
}
#if 0
ath_hal_intrset(ah, sc->sc_imask);
#endif
}
finish:
ATH_PCU_LOCK(sc);
sc->sc_inreset_cnt--;
ath_hal_intrset(ah, sc->sc_imask);
ATH_PCU_UNLOCK(sc);
ath_txrx_start(sc);
return ret;
}
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);
ath_power_set_power_state(sc, HAL_PM_AWAKE);
if (sc->sc_inreset_cnt)
goto restart;
if (ic->ic_flags & IEEE80211_F_SCAN)
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) {
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 (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;
}
}
if (shortCal || longCal) {
isCalDone = AH_FALSE;
if (ath_hal_calibrateN(ah, sc->sc_curchan, longCal, &isCalDone)) {
if (longCal) {
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:
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 = 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;
sc->sc_doresetcal = AH_FALSE;
}
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__);
}
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;
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
static void
ath_update_chw(struct ieee80211com *ic)
{
struct ath_softc *sc = ic->ic_softc;
device_printf(sc->sc_dev, "%s: called\n", __func__);
ath_set_channel(ic);
}
#endif
static int
ath_set_quiet_ie(struct ieee80211_node *ni, uint8_t *ie)
{
struct ieee80211_quiet_ie *q;
struct ieee80211vap *vap = ni->ni_vap;
struct ath_vap *avp = ATH_VAP(vap);
struct ieee80211com *ic = vap->iv_ic;
struct ath_softc *sc = ic->ic_softc;
if (vap->iv_opmode != IEEE80211_M_STA)
return (0);
if (ie == NULL) {
DPRINTF(sc, ATH_DEBUG_QUIETIE,
"%s: called; NULL IE, disabling\n", __func__);
ath_hal_set_quiet(sc->sc_ah, 0, 0, 0, HAL_QUIET_DISABLE);
memset(&avp->quiet_ie, 0, sizeof(avp->quiet_ie));
return (0);
}
if (ie[0] != IEEE80211_ELEMID_QUIET)
return 0;
if (ie[1] != 6)
return 0;
q = (void *) ie;
if ((q->period == avp->quiet_ie.period) &&
(le16dec(&q->duration) == le16dec(&avp->quiet_ie.duration)) &&
(le16dec(&q->offset) == le16dec(&avp->quiet_ie.offset)))
return (0);
DPRINTF(sc, ATH_DEBUG_QUIETIE,
"%s: called; tbttcount=%d, period=%d, duration=%d, offset=%d\n",
__func__,
(int) q->tbttcount,
(int) q->period,
(int) le16dec(&q->duration),
(int) le16dec(&q->offset));
if ((le16dec(&q->duration) == 0) ||
(le16dec(&q->duration) >= ni->ni_intval)) {
DPRINTF(sc, ATH_DEBUG_QUIETIE,
"%s: invalid duration (%d)\n", __func__,
le16dec(&q->duration));
return (0);
}
if (le16dec(&q->duration) + le16dec(&q->offset) >= ni->ni_intval) {
DPRINTF(sc, ATH_DEBUG_QUIETIE,
"%s: invalid duration + offset (%d+%d)\n", __func__,
le16dec(&q->duration),
le16dec(&q->offset));
return (0);
}
if (q->tbttcount == 0) {
DPRINTF(sc, ATH_DEBUG_QUIETIE,
"%s: invalid tbttcount (0)\n", __func__);
return (0);
}
if (q->period == 0) {
DPRINTF(sc, ATH_DEBUG_QUIETIE,
"%s: invalid period (0)\n", __func__);
return (0);
}
if (q->tbttcount == 1) {
DPRINTF(sc, ATH_DEBUG_QUIETIE,
"%s: programming\n", __func__);
ath_hal_set_quiet(sc->sc_ah,
q->period * ni->ni_intval,
le16dec(&q->duration),
le16dec(&q->offset) + ni->ni_intval,
HAL_QUIET_ENABLE | HAL_QUIET_ADD_CURRENT_TSF);
memcpy(&avp->quiet_ie, ie, sizeof(struct ieee80211_quiet_ie));
}
return (0);
}
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);
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);
}
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,
HAL_LED_SCAN,
HAL_LED_AUTH,
HAL_LED_ASSOC,
HAL_LED_RUN,
HAL_LED_RUN,
HAL_LED_RUN,
HAL_LED_RUN,
};
DPRINTF(sc, ATH_DEBUG_STATE, "%s: %s -> %s\n", __func__,
ieee80211_state_name[ostate],
ieee80211_state_name[nstate]);
IEEE80211_LOCK_ASSERT(ic);
ATH_LOCK(sc);
if (nstate != IEEE80211_S_SLEEP)
ath_power_setselfgen(sc, HAL_PM_AWAKE);
ath_power_set_power_state(sc, HAL_PM_AWAKE);
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]);
if (nstate == IEEE80211_S_SCAN) {
ATH_LOCK(sc);
ath_power_setselfgen(sc, HAL_PM_AWAKE);
ath_power_setpower(sc, HAL_PM_AWAKE, 1);
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);
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);
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);
}
error = avp->av_newstate(vap, nstate, arg);
if (error != 0)
goto bad;
IEEE80211_LOCK_ASSERT(ic);
if (nstate == IEEE80211_S_RUN) {
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;
#endif
case IEEE80211_M_HOSTAP:
case IEEE80211_M_IBSS:
case IEEE80211_M_MBSS:
ath_hal_stoptxdma(ah, sc->sc_bhalq);
error = ath_beacon_alloc(sc, ni);
if (error != 0)
goto bad;
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:
if (ostate != IEEE80211_S_RUN &&
ostate != IEEE80211_S_SLEEP) {
if ((vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) == 0) {
DPRINTF(sc, ATH_DEBUG_BEACON,
"%s: STA; syncbeacon=1\n", __func__);
sc->sc_syncbeacon = 1;
if (csa_run_transition)
ath_beacon_config(sc, vap);
}
memset(&avp->quiet_ie, 0, sizeof(avp->quiet_ie));
if ((vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) == 0) {
sc->sc_beacons = 1;
}
}
break;
case IEEE80211_M_MONITOR:
ath_hal_intrset(ah, sc->sc_imask);
break;
case IEEE80211_M_WDS:
break;
default:
break;
}
ath_hal_process_noisefloor(ah);
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;
ATH_LOCK(sc);
ath_power_setselfgen(sc, HAL_PM_AWAKE);
ath_power_setpower(sc, HAL_PM_AWAKE, 1);
if (ath_longcalinterval != 0) {
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) {
memset(&avp->quiet_ie, 0, sizeof(avp->quiet_ie));
if (!ath_isanyrunningvaps(vap)) {
sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS);
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) {
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);
ath_power_setselfgen(sc, HAL_PM_NETWORK_SLEEP);
if (sc->sc_syncbeacon == 0) {
ath_power_setpower(sc, HAL_PM_NETWORK_SLEEP, 1);
}
ATH_UNLOCK(sc);
}
} else if (nstate == IEEE80211_S_SCAN) {
memset(&avp->quiet_ie, 0, sizeof(avp->quiet_ie));
ATH_LOCK(sc);
if (ath_longcalinterval != 0) {
callout_reset(&sc->sc_cal_ch, 1, ath_calibrate, sc);
} else {
DPRINTF(sc, ATH_DEBUG_CALIBRATE,
"%s: calibration disabled\n", __func__);
}
ATH_UNLOCK(sc);
}
bad:
ieee80211_free_node(ni);
ATH_LOCK(sc);
ath_power_restore_power_state(sc);
ATH_UNLOCK(sc);
return error;
}
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;
if (!ath_key_alloc(vap, &ni->ni_ucastkey, &keyix, &rxkeyix)) {
} else {
ni->ni_ucastkey.wk_keyix = keyix;
ni->ni_ucastkey.wk_rxkeyix = rxkeyix;
ni->ni_ucastkey.wk_flags |= IEEE80211_KEY_DEVKEY;
IEEE80211_ADDR_COPY(ni->ni_ucastkey.wk_macaddr, ni->ni_macaddr);
ath_keyset(sc, vap, &ni->ni_ucastkey, vap->iv_bss);
}
}
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 (! isnew) {
DPRINTF(sc, ATH_DEBUG_NODE,
"%s: %6D: reassoc; is_powersave=%d\n",
__func__,
ni->ni_macaddr,
":",
an->an_is_powersave);
ath_tx_node_reassoc(sc, an);
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;
}
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);
(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;
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);
ic->ic_regdomain.regdomain = (uint16_t) sc->sc_eerd;
ic->ic_regdomain.country = (uint16_t) sc->sc_eecc;
ic->ic_regdomain.isocc[0] = ' ';
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)
{
static const struct {
u_int rate;
u_int16_t timeOn;
u_int16_t timeOff;
} 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 },
};
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;
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;
if (mode == IEEE80211_MODE_11G)
sc->sc_protrix = ath_tx_findrix(sc, 2*2);
else
sc->sc_protrix = ath_tx_findrix(sc, 2*1);
}
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);
}
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) {
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) {
error = ath_init(sc);
}
} else {
ath_stop(sc);
if (!sc->sc_invalid)
ath_power_setpower(sc, HAL_PM_FULL_SLEEP, 1);
}
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);
}
}
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 (ath_dfs_process_radar_event(sc, sc->sc_curchan)) {
IEEE80211_LOCK(ic);
ieee80211_dfs_notify_radar(ic, sc->sc_curchan);
IEEE80211_UNLOCK(ic);
}
}
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);
DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, "%s: %6D: enable=%d\n",
__func__,
ni->ni_macaddr,
":",
!! enable);
if (enable)
ath_tx_node_sleep(sc, an);
else
ath_tx_node_wakeup(sc, an);
avp->av_node_ps(ni, enable);
#else
struct ath_vap *avp = ATH_VAP(ni->ni_vap);
avp->av_node_ps(ni, enable);
#endif
}
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;
if (avp->av_set_tim == NULL) {
ATH_TX_UNLOCK(sc);
return (0);
}
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) {
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) {
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 {
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);
if (avp->av_set_tim == NULL)
return (0);
return (avp->av_set_tim(ni, enable));
#endif
}
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;
if (ni == NULL)
return;
an = ATH_NODE(ni);
avp = ATH_VAP(ni->ni_vap);
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 {
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
}
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;
if (ni == NULL)
return;
if (ni->ni_associd == 0)
return;
an = ATH_NODE(ni);
avp = ATH_VAP(ni->ni_vap);
ATH_TX_LOCK(sc);
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;
}
an->an_leak_count = 1;
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;
}
for (tid = IEEE80211_TID_SIZE - 1; tid >= 0; tid--) {
struct ath_tid *atid = &an->an_tid[tid];
if (atid->axq_depth == 0)
continue;
ath_tx_tid_sched(sc, atid);
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);
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
}
MODULE_VERSION(ath_main, 1);
MODULE_DEPEND(ath_main, wlan, 1, 1, 1);
MODULE_DEPEND(ath_main, ath_rate, 1, 1, 1);
MODULE_DEPEND(ath_main, ath_dfs, 1, 1, 1);
MODULE_DEPEND(ath_main, ath_hal, 1, 1, 1);
#if defined(IEEE80211_ALQ) || defined(AH_DEBUG_ALQ) || defined(ATH_DEBUG_ALQ)
MODULE_DEPEND(ath_main, alq, 1, 1, 1);
#endif