Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/ath/if_ath.c
Show First 20 Lines • Show All 145 Lines • ▼ Show 20 Lines | |||||
*/ | */ | ||||
CTASSERT(ATH_BCBUF <= 8); | CTASSERT(ATH_BCBUF <= 8); | ||||
static struct ieee80211vap *ath_vap_create(struct ieee80211com *, | static struct ieee80211vap *ath_vap_create(struct ieee80211com *, | ||||
const char [IFNAMSIZ], int, enum ieee80211_opmode, int, | const char [IFNAMSIZ], int, enum ieee80211_opmode, int, | ||||
const uint8_t [IEEE80211_ADDR_LEN], | const uint8_t [IEEE80211_ADDR_LEN], | ||||
const uint8_t [IEEE80211_ADDR_LEN]); | const uint8_t [IEEE80211_ADDR_LEN]); | ||||
static void ath_vap_delete(struct ieee80211vap *); | static void ath_vap_delete(struct ieee80211vap *); | ||||
static void ath_init(void *); | static void ath_init(struct ath_softc *); | ||||
static void ath_stop_locked(struct ifnet *); | static void ath_stop_locked(struct ath_softc *); | ||||
static void ath_stop(struct ifnet *); | static void ath_stop(struct ath_softc *); | ||||
static int ath_reset_vap(struct ieee80211vap *, u_long); | static int ath_reset_vap(struct ieee80211vap *, u_long); | ||||
static int ath_transmit(struct ifnet *ifp, struct mbuf *m); | static int ath_transmit(struct ieee80211com *, struct mbuf *); | ||||
static void ath_qflush(struct ifnet *ifp); | |||||
static int ath_media_change(struct ifnet *); | static int ath_media_change(struct ifnet *); | ||||
static void ath_watchdog(void *); | static void ath_watchdog(void *); | ||||
static int ath_ioctl(struct ifnet *, u_long, caddr_t); | static int ath_ioctl(struct ieee80211com *, u_long, void *); | ||||
static void ath_parent(struct ieee80211com *); | |||||
static void ath_fatal_proc(void *, int); | static void ath_fatal_proc(void *, int); | ||||
static void ath_bmiss_vap(struct ieee80211vap *); | static void ath_bmiss_vap(struct ieee80211vap *); | ||||
static void ath_bmiss_proc(void *, int); | static void ath_bmiss_proc(void *, int); | ||||
static void ath_key_update_begin(struct ieee80211vap *); | static void ath_key_update_begin(struct ieee80211vap *); | ||||
static void ath_key_update_end(struct ieee80211vap *); | static void ath_key_update_end(struct ieee80211vap *); | ||||
static void ath_update_mcast_hw(struct ath_softc *); | static void ath_update_mcast_hw(struct ath_softc *); | ||||
static void ath_update_mcast(struct ieee80211com *); | static void ath_update_mcast(struct ieee80211com *); | ||||
static void ath_update_promisc(struct ieee80211com *); | static void ath_update_promisc(struct ieee80211com *); | ||||
▲ Show 20 Lines • Show All 395 Lines • ▼ Show 20 Lines | |||||
#define HAL_MODE_HT20 (HAL_MODE_11NG_HT20 | HAL_MODE_11NA_HT20) | #define HAL_MODE_HT20 (HAL_MODE_11NG_HT20 | HAL_MODE_11NA_HT20) | ||||
#define HAL_MODE_HT40 \ | #define HAL_MODE_HT40 \ | ||||
(HAL_MODE_11NG_HT40PLUS | HAL_MODE_11NG_HT40MINUS | \ | (HAL_MODE_11NG_HT40PLUS | HAL_MODE_11NG_HT40MINUS | \ | ||||
HAL_MODE_11NA_HT40PLUS | HAL_MODE_11NA_HT40MINUS) | HAL_MODE_11NA_HT40PLUS | HAL_MODE_11NA_HT40MINUS) | ||||
int | int | ||||
ath_attach(u_int16_t devid, struct ath_softc *sc) | ath_attach(u_int16_t devid, struct ath_softc *sc) | ||||
{ | { | ||||
struct ifnet *ifp; | struct ieee80211com *ic = &sc->sc_ic; | ||||
struct ieee80211com *ic; | |||||
struct ath_hal *ah = NULL; | struct ath_hal *ah = NULL; | ||||
HAL_STATUS status; | HAL_STATUS status; | ||||
int error = 0, i; | int error = 0, i; | ||||
u_int wmodes; | u_int wmodes; | ||||
uint8_t macaddr[IEEE80211_ADDR_LEN]; | |||||
int rx_chainmask, tx_chainmask; | int rx_chainmask, tx_chainmask; | ||||
HAL_OPS_CONFIG ah_config; | HAL_OPS_CONFIG ah_config; | ||||
DPRINTF(sc, ATH_DEBUG_ANY, "%s: devid 0x%x\n", __func__, devid); | DPRINTF(sc, ATH_DEBUG_ANY, "%s: devid 0x%x\n", __func__, devid); | ||||
CURVNET_SET(vnet0); | |||||
ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); | |||||
if (ifp == NULL) { | |||||
device_printf(sc->sc_dev, "can not if_alloc()\n"); | |||||
error = ENOSPC; | |||||
CURVNET_RESTORE(); | |||||
goto bad; | |||||
} | |||||
ic = ifp->if_l2com; | |||||
ic->ic_softc = sc; | ic->ic_softc = sc; | ||||
ic->ic_name = device_get_nameunit(sc->sc_dev); | ic->ic_name = device_get_nameunit(sc->sc_dev); | ||||
if_initname(ifp, device_get_name(sc->sc_dev), | |||||
device_get_unit(sc->sc_dev)); | |||||
CURVNET_RESTORE(); | |||||
/* | /* | ||||
* Configure the initial configuration data. | * Configure the initial configuration data. | ||||
* | * | ||||
* This is stuff that may be needed early during attach | * This is stuff that may be needed early during attach | ||||
* rather than done via configuration calls later. | * rather than done via configuration calls later. | ||||
*/ | */ | ||||
bzero(&ah_config, sizeof(ah_config)); | bzero(&ah_config, sizeof(ah_config)); | ||||
ath_setup_hal_config(sc, &ah_config); | ath_setup_hal_config(sc, &ah_config); | ||||
▲ Show 20 Lines • Show All 117 Lines • ▼ Show 20 Lines | #endif | ||||
callout_init_mtx(&sc->sc_cal_ch, &sc->sc_mtx, 0); | callout_init_mtx(&sc->sc_cal_ch, &sc->sc_mtx, 0); | ||||
callout_init_mtx(&sc->sc_wd_ch, &sc->sc_mtx, 0); | callout_init_mtx(&sc->sc_wd_ch, &sc->sc_mtx, 0); | ||||
ATH_TXBUF_LOCK_INIT(sc); | ATH_TXBUF_LOCK_INIT(sc); | ||||
sc->sc_tq = taskqueue_create("ath_taskq", M_NOWAIT, | sc->sc_tq = taskqueue_create("ath_taskq", M_NOWAIT, | ||||
taskqueue_thread_enqueue, &sc->sc_tq); | taskqueue_thread_enqueue, &sc->sc_tq); | ||||
taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, | taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq", | ||||
"%s taskq", ifp->if_xname); | device_get_nameunit(sc->sc_dev)); | ||||
TASK_INIT(&sc->sc_rxtask, 0, sc->sc_rx.recv_tasklet, sc); | 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_bmisstask, 0, ath_bmiss_proc, sc); | ||||
TASK_INIT(&sc->sc_bstucktask,0, ath_bstuck_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_resettask,0, ath_reset_proc, sc); | ||||
TASK_INIT(&sc->sc_txqtask, 0, ath_txq_sched_tasklet, sc); | TASK_INIT(&sc->sc_txqtask, 0, ath_txq_sched_tasklet, sc); | ||||
TASK_INIT(&sc->sc_fataltask, 0, ath_fatal_proc, sc); | TASK_INIT(&sc->sc_fataltask, 0, ath_fatal_proc, sc); | ||||
▲ Show 20 Lines • Show All 126 Lines • ▼ Show 20 Lines | #endif | ||||
* Auto-enable soft led processing for IBM cards and for | * Auto-enable soft led processing for IBM cards and for | ||||
* 5211 minipci cards. Users can also manually enable/disable | * 5211 minipci cards. Users can also manually enable/disable | ||||
* support with a sysctl. | * support with a sysctl. | ||||
*/ | */ | ||||
sc->sc_softled = (devid == AR5212_DEVID_IBM || devid == AR5211_DEVID); | sc->sc_softled = (devid == AR5212_DEVID_IBM || devid == AR5211_DEVID); | ||||
ath_led_config(sc); | ath_led_config(sc); | ||||
ath_hal_setledstate(ah, HAL_LED_INIT); | ath_hal_setledstate(ah, HAL_LED_INIT); | ||||
ifp->if_softc = sc; | |||||
ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; | |||||
ifp->if_transmit = ath_transmit; | |||||
ifp->if_qflush = ath_qflush; | |||||
ifp->if_ioctl = ath_ioctl; | |||||
ifp->if_init = ath_init; | |||||
IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); | |||||
ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; | |||||
IFQ_SET_READY(&ifp->if_snd); | |||||
ic->ic_ifp = ifp; | |||||
/* XXX not right but it's not used anywhere important */ | /* XXX not right but it's not used anywhere important */ | ||||
ic->ic_phytype = IEEE80211_T_OFDM; | ic->ic_phytype = IEEE80211_T_OFDM; | ||||
ic->ic_opmode = IEEE80211_M_STA; | ic->ic_opmode = IEEE80211_M_STA; | ||||
ic->ic_caps = | ic->ic_caps = | ||||
IEEE80211_C_STA /* station mode */ | IEEE80211_C_STA /* station mode */ | ||||
| IEEE80211_C_IBSS /* ibss, nee adhoc, mode */ | | IEEE80211_C_IBSS /* ibss, nee adhoc, mode */ | ||||
| IEEE80211_C_HOSTAP /* hostap mode */ | | IEEE80211_C_HOSTAP /* hostap mode */ | ||||
| IEEE80211_C_MONITOR /* monitor mode */ | | IEEE80211_C_MONITOR /* monitor mode */ | ||||
▲ Show 20 Lines • Show All 305 Lines • ▼ Show 20 Lines | #endif | ||||
/* | /* | ||||
* Not all chips have the VEOL support we want to | * Not all chips have the VEOL support we want to | ||||
* use with IBSS beacons; check here for it. | * use with IBSS beacons; check here for it. | ||||
*/ | */ | ||||
sc->sc_hasveol = ath_hal_hasveol(ah); | sc->sc_hasveol = ath_hal_hasveol(ah); | ||||
/* get mac address from kenv first, then hardware */ | /* get mac address from kenv first, then hardware */ | ||||
if (ath_fetch_mac_kenv(sc, macaddr) == 0) { | if (ath_fetch_mac_kenv(sc, ic->ic_macaddr) == 0) { | ||||
/* Tell the HAL now about the new MAC */ | /* Tell the HAL now about the new MAC */ | ||||
ath_hal_setmac(ah, macaddr); | ath_hal_setmac(ah, ic->ic_macaddr); | ||||
} else { | } else { | ||||
ath_hal_getmac(ah, macaddr); | ath_hal_getmac(ah, ic->ic_macaddr); | ||||
} | } | ||||
if (sc->sc_hasbmask) | if (sc->sc_hasbmask) | ||||
ath_hal_getbssidmask(ah, sc->sc_hwbssidmask); | ath_hal_getbssidmask(ah, sc->sc_hwbssidmask); | ||||
/* NB: used to size node table key mapping array */ | /* NB: used to size node table key mapping array */ | ||||
ic->ic_max_keyix = sc->sc_keymax; | ic->ic_max_keyix = sc->sc_keymax; | ||||
/* call MI attach routine. */ | /* call MI attach routine. */ | ||||
ieee80211_ifattach(ic, macaddr); | ieee80211_ifattach(ic); | ||||
ic->ic_setregdomain = ath_setregdomain; | ic->ic_setregdomain = ath_setregdomain; | ||||
ic->ic_getradiocaps = ath_getradiocaps; | ic->ic_getradiocaps = ath_getradiocaps; | ||||
sc->sc_opmode = HAL_M_STA; | sc->sc_opmode = HAL_M_STA; | ||||
/* override default methods */ | /* override default methods */ | ||||
ic->ic_ioctl = ath_ioctl; | |||||
ic->ic_parent = ath_parent; | |||||
ic->ic_transmit = ath_transmit; | |||||
ic->ic_newassoc = ath_newassoc; | ic->ic_newassoc = ath_newassoc; | ||||
ic->ic_updateslot = ath_updateslot; | ic->ic_updateslot = ath_updateslot; | ||||
ic->ic_wme.wme_update = ath_wme_update; | ic->ic_wme.wme_update = ath_wme_update; | ||||
ic->ic_vap_create = ath_vap_create; | ic->ic_vap_create = ath_vap_create; | ||||
ic->ic_vap_delete = ath_vap_delete; | ic->ic_vap_delete = ath_vap_delete; | ||||
ic->ic_raw_xmit = ath_raw_xmit; | ic->ic_raw_xmit = ath_raw_xmit; | ||||
ic->ic_update_mcast = ath_update_mcast; | ic->ic_update_mcast = ath_update_mcast; | ||||
ic->ic_update_promisc = ath_update_promisc; | ic->ic_update_promisc = ath_update_promisc; | ||||
▲ Show 20 Lines • Show All 79 Lines • ▼ Show 20 Lines | |||||
bad2: | bad2: | ||||
ath_tx_cleanup(sc); | ath_tx_cleanup(sc); | ||||
ath_desc_free(sc); | ath_desc_free(sc); | ||||
ath_txdma_teardown(sc); | ath_txdma_teardown(sc); | ||||
ath_rxdma_teardown(sc); | ath_rxdma_teardown(sc); | ||||
bad: | bad: | ||||
if (ah) | if (ah) | ||||
ath_hal_detach(ah); | ath_hal_detach(ah); | ||||
/* | |||||
* To work around scoping issues with CURVNET_SET/CURVNET_RESTORE.. | |||||
*/ | |||||
if (ifp != NULL && ifp->if_vnet) { | |||||
CURVNET_SET(ifp->if_vnet); | |||||
if_free(ifp); | |||||
CURVNET_RESTORE(); | |||||
} else if (ifp != NULL) | |||||
if_free(ifp); | |||||
sc->sc_invalid = 1; | sc->sc_invalid = 1; | ||||
return error; | return error; | ||||
} | } | ||||
int | int | ||||
ath_detach(struct ath_softc *sc) | ath_detach(struct ath_softc *sc) | ||||
{ | { | ||||
struct ifnet *ifp = sc->sc_ifp; | |||||
DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n", | |||||
__func__, ifp->if_flags); | |||||
/* | /* | ||||
* NB: the order of these is important: | * NB: the order of these is important: | ||||
* o stop the chip so no more interrupts will fire | * o stop the chip so no more interrupts will fire | ||||
* o call the 802.11 layer before detaching the hal to | * o call the 802.11 layer before detaching the hal to | ||||
* insure callbacks into the driver to delete global | * insure callbacks into the driver to delete global | ||||
* key cache entries can be handled | * key cache entries can be handled | ||||
* o free the taskqueue which drains any pending tasks | * o free the taskqueue which drains any pending tasks | ||||
* o reclaim the tx queue data structures after calling | * o reclaim the tx queue data structures after calling | ||||
* the 802.11 layer as we'll get called back to reclaim | * the 802.11 layer as we'll get called back to reclaim | ||||
* node state and potentially want to use them | * node state and potentially want to use them | ||||
* o to cleanup the tx queues the hal is called, so detach | * o to cleanup the tx queues the hal is called, so detach | ||||
* it last | * it last | ||||
* Other than that, it's straightforward... | * Other than that, it's straightforward... | ||||
*/ | */ | ||||
/* | /* | ||||
* XXX Wake the hardware up first. ath_stop() will still | * XXX Wake the hardware up first. ath_stop() will still | ||||
* wake it up first, but I'd rather do it here just to | * wake it up first, but I'd rather do it here just to | ||||
* ensure it's awake. | * ensure it's awake. | ||||
*/ | */ | ||||
ATH_LOCK(sc); | ATH_LOCK(sc); | ||||
ath_power_set_power_state(sc, HAL_PM_AWAKE); | ath_power_set_power_state(sc, HAL_PM_AWAKE); | ||||
ath_power_setpower(sc, HAL_PM_AWAKE); | ath_power_setpower(sc, HAL_PM_AWAKE); | ||||
ATH_UNLOCK(sc); | |||||
/* | /* | ||||
* Stop things cleanly. | * Stop things cleanly. | ||||
*/ | */ | ||||
ath_stop(ifp); | ath_stop_locked(sc); | ||||
ATH_UNLOCK(sc); | |||||
ieee80211_ifdetach(ifp->if_l2com); | ieee80211_ifdetach(&sc->sc_ic); | ||||
taskqueue_free(sc->sc_tq); | taskqueue_free(sc->sc_tq); | ||||
#ifdef ATH_TX99_DIAG | #ifdef ATH_TX99_DIAG | ||||
if (sc->sc_tx99 != NULL) | if (sc->sc_tx99 != NULL) | ||||
sc->sc_tx99->detach(sc->sc_tx99); | sc->sc_tx99->detach(sc->sc_tx99); | ||||
#endif | #endif | ||||
ath_rate_detach(sc->sc_rc); | ath_rate_detach(sc->sc_rc); | ||||
#ifdef ATH_DEBUG_ALQ | #ifdef ATH_DEBUG_ALQ | ||||
if_ath_alq_tidyup(&sc->sc_alq); | if_ath_alq_tidyup(&sc->sc_alq); | ||||
#endif | #endif | ||||
ath_lna_div_detach(sc); | ath_lna_div_detach(sc); | ||||
ath_btcoex_detach(sc); | ath_btcoex_detach(sc); | ||||
ath_spectral_detach(sc); | ath_spectral_detach(sc); | ||||
ath_dfs_detach(sc); | ath_dfs_detach(sc); | ||||
ath_desc_free(sc); | ath_desc_free(sc); | ||||
ath_txdma_teardown(sc); | ath_txdma_teardown(sc); | ||||
ath_rxdma_teardown(sc); | ath_rxdma_teardown(sc); | ||||
ath_tx_cleanup(sc); | ath_tx_cleanup(sc); | ||||
ath_hal_detach(sc->sc_ah); /* NB: sets chip in full sleep */ | ath_hal_detach(sc->sc_ah); /* NB: sets chip in full sleep */ | ||||
CURVNET_SET(ifp->if_vnet); | |||||
if_free(ifp); | |||||
CURVNET_RESTORE(); | |||||
return 0; | return 0; | ||||
} | } | ||||
/* | /* | ||||
* MAC address handling for multiple BSS on the same radio. | * MAC address handling for multiple BSS on the same radio. | ||||
* The first vap uses the MAC address from the EEPROM. For | * The first vap uses the MAC address from the EEPROM. For | ||||
* subsequent vap's we set the U/L bit (bit 1) in the MAC | * subsequent vap's we set the U/L bit (bit 1) in the MAC | ||||
* address and use the next six bits as an index. | * address and use the next six bits as an index. | ||||
▲ Show 20 Lines • Show All 59 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
static struct ieee80211vap * | static struct ieee80211vap * | ||||
ath_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, | ath_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, | ||||
enum ieee80211_opmode opmode, int flags, | enum ieee80211_opmode opmode, int flags, | ||||
const uint8_t bssid[IEEE80211_ADDR_LEN], | const uint8_t bssid[IEEE80211_ADDR_LEN], | ||||
const uint8_t mac0[IEEE80211_ADDR_LEN]) | const uint8_t mac0[IEEE80211_ADDR_LEN]) | ||||
{ | { | ||||
struct ath_softc *sc = ic->ic_ifp->if_softc; | struct ath_softc *sc = ic->ic_softc; | ||||
struct ath_vap *avp; | struct ath_vap *avp; | ||||
struct ieee80211vap *vap; | struct ieee80211vap *vap; | ||||
uint8_t mac[IEEE80211_ADDR_LEN]; | uint8_t mac[IEEE80211_ADDR_LEN]; | ||||
int needbeacon, error; | int needbeacon, error; | ||||
enum ieee80211_opmode ic_opmode; | enum ieee80211_opmode ic_opmode; | ||||
avp = (struct ath_vap *) malloc(sizeof(struct ath_vap), | avp = (struct ath_vap *) malloc(sizeof(struct ath_vap), | ||||
M_80211_VAP, M_WAITOK | M_ZERO); | M_80211_VAP, M_WAITOK | M_ZERO); | ||||
▲ Show 20 Lines • Show All 91 Lines • ▼ Show 20 Lines | #endif | ||||
if (opmode == IEEE80211_M_HOSTAP || opmode == IEEE80211_M_MBSS) { | if (opmode == IEEE80211_M_HOSTAP || opmode == IEEE80211_M_MBSS) { | ||||
assign_address(sc, mac, flags & IEEE80211_CLONE_BSSID); | assign_address(sc, mac, flags & IEEE80211_CLONE_BSSID); | ||||
ath_hal_setbssidmask(sc->sc_ah, sc->sc_hwbssidmask); | ath_hal_setbssidmask(sc->sc_ah, sc->sc_hwbssidmask); | ||||
} | } | ||||
vap = &avp->av_vap; | vap = &avp->av_vap; | ||||
/* XXX can't hold mutex across if_alloc */ | /* XXX can't hold mutex across if_alloc */ | ||||
ATH_UNLOCK(sc); | ATH_UNLOCK(sc); | ||||
error = ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, | error = ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid); | ||||
bssid, mac); | |||||
ATH_LOCK(sc); | ATH_LOCK(sc); | ||||
if (error != 0) { | if (error != 0) { | ||||
device_printf(sc->sc_dev, "%s: error %d creating vap\n", | device_printf(sc->sc_dev, "%s: error %d creating vap\n", | ||||
__func__, error); | __func__, error); | ||||
goto bad2; | goto bad2; | ||||
} | } | ||||
/* h/w crypto support */ | /* h/w crypto support */ | ||||
▲ Show 20 Lines • Show All 117 Lines • ▼ Show 20 Lines | if (flags & IEEE80211_CLONE_NOBEACONS) { | ||||
/* | /* | ||||
* Enable s/w beacon miss handling. | * Enable s/w beacon miss handling. | ||||
*/ | */ | ||||
sc->sc_swbmiss = 1; | sc->sc_swbmiss = 1; | ||||
} | } | ||||
ATH_UNLOCK(sc); | ATH_UNLOCK(sc); | ||||
/* complete setup */ | /* complete setup */ | ||||
ieee80211_vap_attach(vap, ath_media_change, ieee80211_media_status); | ieee80211_vap_attach(vap, ath_media_change, ieee80211_media_status, | ||||
mac); | |||||
return vap; | return vap; | ||||
bad2: | bad2: | ||||
reclaim_address(sc, mac); | reclaim_address(sc, mac); | ||||
ath_hal_setbssidmask(sc->sc_ah, sc->sc_hwbssidmask); | ath_hal_setbssidmask(sc->sc_ah, sc->sc_hwbssidmask); | ||||
bad: | bad: | ||||
free(avp, M_80211_VAP); | free(avp, M_80211_VAP); | ||||
ATH_UNLOCK(sc); | ATH_UNLOCK(sc); | ||||
return NULL; | return NULL; | ||||
} | } | ||||
static void | static void | ||||
ath_vap_delete(struct ieee80211vap *vap) | ath_vap_delete(struct ieee80211vap *vap) | ||||
{ | { | ||||
struct ieee80211com *ic = vap->iv_ic; | struct ieee80211com *ic = vap->iv_ic; | ||||
struct ifnet *ifp = ic->ic_ifp; | struct ath_softc *sc = ic->ic_softc; | ||||
struct ath_softc *sc = ifp->if_softc; | |||||
struct ath_hal *ah = sc->sc_ah; | struct ath_hal *ah = sc->sc_ah; | ||||
struct ath_vap *avp = ATH_VAP(vap); | struct ath_vap *avp = ATH_VAP(vap); | ||||
ATH_LOCK(sc); | ATH_LOCK(sc); | ||||
ath_power_set_power_state(sc, HAL_PM_AWAKE); | ath_power_set_power_state(sc, HAL_PM_AWAKE); | ||||
ATH_UNLOCK(sc); | ATH_UNLOCK(sc); | ||||
DPRINTF(sc, ATH_DEBUG_RESET, "%s: called\n", __func__); | DPRINTF(sc, ATH_DEBUG_RESET, "%s: called\n", __func__); | ||||
if (ifp->if_drv_flags & IFF_DRV_RUNNING) { | if (sc->sc_running) { | ||||
/* | /* | ||||
* Quiesce the hardware while we remove the vap. In | * Quiesce the hardware while we remove the vap. In | ||||
* particular we need to reclaim all references to | * particular we need to reclaim all references to | ||||
* the vap state by any frames pending on the tx queues. | * the vap state by any frames pending on the tx queues. | ||||
*/ | */ | ||||
ath_hal_intrset(ah, 0); /* disable interrupts */ | ath_hal_intrset(ah, 0); /* disable interrupts */ | ||||
/* XXX Do all frames from all vaps/nodes need draining here? */ | /* XXX Do all frames from all vaps/nodes need draining here? */ | ||||
ath_stoprecv(sc, 1); /* stop recv side */ | ath_stoprecv(sc, 1); /* stop recv side */ | ||||
▲ Show 20 Lines • Show All 66 Lines • ▼ Show 20 Lines | #ifdef IEEE80211_SUPPORT_TDMA | ||||
/* TDMA operation ceases when the last vap is destroyed */ | /* TDMA operation ceases when the last vap is destroyed */ | ||||
if (sc->sc_tdma && sc->sc_nvaps == 0) { | if (sc->sc_tdma && sc->sc_nvaps == 0) { | ||||
sc->sc_tdma = 0; | sc->sc_tdma = 0; | ||||
sc->sc_swbmiss = 0; | sc->sc_swbmiss = 0; | ||||
} | } | ||||
#endif | #endif | ||||
free(avp, M_80211_VAP); | free(avp, M_80211_VAP); | ||||
if (ifp->if_drv_flags & IFF_DRV_RUNNING) { | if (sc->sc_running) { | ||||
/* | /* | ||||
* Restart rx+tx machines if still running (RUNNING will | * Restart rx+tx machines if still running (RUNNING will | ||||
* be reset if we just destroyed the last vap). | * be reset if we just destroyed the last vap). | ||||
*/ | */ | ||||
if (ath_startrecv(sc) != 0) | if (ath_startrecv(sc) != 0) | ||||
device_printf(sc->sc_dev, | device_printf(sc->sc_dev, | ||||
"%s: unable to restart recv logic\n", __func__); | "%s: unable to restart recv logic\n", __func__); | ||||
if (sc->sc_beacons) { /* restart beacons */ | if (sc->sc_beacons) { /* restart beacons */ | ||||
Show All 10 Lines | #endif | ||||
/* Ok, let the hardware asleep. */ | /* Ok, let the hardware asleep. */ | ||||
ath_power_restore_power_state(sc); | ath_power_restore_power_state(sc); | ||||
ATH_UNLOCK(sc); | ATH_UNLOCK(sc); | ||||
} | } | ||||
void | void | ||||
ath_suspend(struct ath_softc *sc) | ath_suspend(struct ath_softc *sc) | ||||
{ | { | ||||
struct ifnet *ifp = sc->sc_ifp; | struct ieee80211com *ic = &sc->sc_ic; | ||||
struct ieee80211com *ic = ifp->if_l2com; | |||||
DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n", | sc->sc_resume_up = ic->ic_nrunning != 0; | ||||
__func__, ifp->if_flags); | |||||
sc->sc_resume_up = (ifp->if_flags & IFF_UP) != 0; | |||||
ieee80211_suspend_all(ic); | ieee80211_suspend_all(ic); | ||||
/* | /* | ||||
* NB: don't worry about putting the chip in low power | * NB: don't worry about putting the chip in low power | ||||
* mode; pci will power off our socket on suspend and | * mode; pci will power off our socket on suspend and | ||||
* CardBus detaches the device. | * CardBus detaches the device. | ||||
* | * | ||||
* XXX TODO: well, that's great, except for non-cardbus | * XXX TODO: well, that's great, except for non-cardbus | ||||
* devices! | * devices! | ||||
Show All 23 Lines | |||||
* Reset the key cache since some parts do not reset the | * Reset the key cache since some parts do not reset the | ||||
* contents on resume. First we clear all entries, then | * contents on resume. First we clear all entries, then | ||||
* re-load keys that the 802.11 layer assumes are setup | * re-load keys that the 802.11 layer assumes are setup | ||||
* in h/w. | * in h/w. | ||||
*/ | */ | ||||
static void | static void | ||||
ath_reset_keycache(struct ath_softc *sc) | ath_reset_keycache(struct ath_softc *sc) | ||||
{ | { | ||||
struct ifnet *ifp = sc->sc_ifp; | struct ieee80211com *ic = &sc->sc_ic; | ||||
struct ieee80211com *ic = ifp->if_l2com; | |||||
struct ath_hal *ah = sc->sc_ah; | struct ath_hal *ah = sc->sc_ah; | ||||
int i; | int i; | ||||
ATH_LOCK(sc); | ATH_LOCK(sc); | ||||
ath_power_set_power_state(sc, HAL_PM_AWAKE); | ath_power_set_power_state(sc, HAL_PM_AWAKE); | ||||
for (i = 0; i < sc->sc_keymax; i++) | for (i = 0; i < sc->sc_keymax; i++) | ||||
ath_hal_keyreset(ah, i); | ath_hal_keyreset(ah, i); | ||||
ath_power_restore_power_state(sc); | ath_power_restore_power_state(sc); | ||||
Show All 25 Lines | DPRINTF(sc, ATH_DEBUG_RESET, | ||||
__func__, | __func__, | ||||
sc->sc_cur_txchainmask, | sc->sc_cur_txchainmask, | ||||
sc->sc_cur_rxchainmask); | sc->sc_cur_rxchainmask); | ||||
} | } | ||||
void | void | ||||
ath_resume(struct ath_softc *sc) | ath_resume(struct ath_softc *sc) | ||||
{ | { | ||||
struct ifnet *ifp = sc->sc_ifp; | struct ieee80211com *ic = &sc->sc_ic; | ||||
struct ieee80211com *ic = ifp->if_l2com; | |||||
struct ath_hal *ah = sc->sc_ah; | struct ath_hal *ah = sc->sc_ah; | ||||
HAL_STATUS status; | HAL_STATUS status; | ||||
DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n", | DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n", | ||||
__func__, ifp->if_flags); | __func__, ifp->if_flags); | ||||
/* Re-enable PCIe, re-enable the PCIe bus */ | /* Re-enable PCIe, re-enable the PCIe bus */ | ||||
ath_hal_enablepcie(ah, 0, 0); | ath_hal_enablepcie(ah, 0, 0); | ||||
▲ Show 20 Lines • Show All 56 Lines • ▼ Show 20 Lines | ath_resume(struct ath_softc *sc) | ||||
ATH_UNLOCK(sc); | ATH_UNLOCK(sc); | ||||
/* XXX beacons ? */ | /* XXX beacons ? */ | ||||
} | } | ||||
void | void | ||||
ath_shutdown(struct ath_softc *sc) | ath_shutdown(struct ath_softc *sc) | ||||
{ | { | ||||
struct ifnet *ifp = sc->sc_ifp; | |||||
DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n", | ath_stop(sc); | ||||
__func__, ifp->if_flags); | |||||
ath_stop(ifp); | |||||
/* NB: no point powering down chip as we're about to reboot */ | /* NB: no point powering down chip as we're about to reboot */ | ||||
} | } | ||||
/* | /* | ||||
* Interrupt handler. Most of the actual processing is deferred. | * Interrupt handler. Most of the actual processing is deferred. | ||||
*/ | */ | ||||
void | void | ||||
ath_intr(void *arg) | ath_intr(void *arg) | ||||
{ | { | ||||
struct ath_softc *sc = arg; | struct ath_softc *sc = arg; | ||||
struct ifnet *ifp = sc->sc_ifp; | |||||
struct ath_hal *ah = sc->sc_ah; | struct ath_hal *ah = sc->sc_ah; | ||||
HAL_INT status = 0; | HAL_INT status = 0; | ||||
uint32_t txqs; | uint32_t txqs; | ||||
/* | /* | ||||
* If we're inside a reset path, just print a warning and | * If we're inside a reset path, just print a warning and | ||||
* clear the ISR. The reset routine will finish it for us. | * clear the ISR. The reset routine will finish it for us. | ||||
*/ | */ | ||||
Show All 22 Lines | if (!ath_hal_intrpend(ah)) { /* shared irq, not for us */ | ||||
ATH_PCU_UNLOCK(sc); | ATH_PCU_UNLOCK(sc); | ||||
return; | return; | ||||
} | } | ||||
ATH_LOCK(sc); | ATH_LOCK(sc); | ||||
ath_power_set_power_state(sc, HAL_PM_AWAKE); | ath_power_set_power_state(sc, HAL_PM_AWAKE); | ||||
ATH_UNLOCK(sc); | ATH_UNLOCK(sc); | ||||
if ((ifp->if_flags & IFF_UP) == 0 || | if (sc->sc_ic.ic_nrunning == 0 && sc->sc_running == 0) { | ||||
(ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { | |||||
HAL_INT status; | HAL_INT status; | ||||
DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags 0x%x\n", | DPRINTF(sc, ATH_DEBUG_ANY, "%s: ic_nrunning %d sc_running %d\n", | ||||
__func__, ifp->if_flags); | __func__, sc->sc_ic.ic_nrunning, sc->sc_running); | ||||
ath_hal_getisr(ah, &status); /* clear ISR */ | ath_hal_getisr(ah, &status); /* clear ISR */ | ||||
ath_hal_intrset(ah, 0); /* disable further intr's */ | ath_hal_intrset(ah, 0); /* disable further intr's */ | ||||
ATH_PCU_UNLOCK(sc); | ATH_PCU_UNLOCK(sc); | ||||
ATH_LOCK(sc); | ATH_LOCK(sc); | ||||
ath_power_restore_power_state(sc); | ath_power_restore_power_state(sc); | ||||
ATH_UNLOCK(sc); | ATH_UNLOCK(sc); | ||||
return; | return; | ||||
▲ Show 20 Lines • Show All 221 Lines • ▼ Show 20 Lines | #endif | ||||
ath_power_restore_power_state(sc); | ath_power_restore_power_state(sc); | ||||
ATH_UNLOCK(sc); | ATH_UNLOCK(sc); | ||||
} | } | ||||
static void | static void | ||||
ath_fatal_proc(void *arg, int pending) | ath_fatal_proc(void *arg, int pending) | ||||
{ | { | ||||
struct ath_softc *sc = arg; | struct ath_softc *sc = arg; | ||||
struct ifnet *ifp = sc->sc_ifp; | |||||
u_int32_t *state; | u_int32_t *state; | ||||
u_int32_t len; | u_int32_t len; | ||||
void *sp; | void *sp; | ||||
device_printf(sc->sc_dev, "hardware error; resetting\n"); | device_printf(sc->sc_dev, "hardware error; resetting\n"); | ||||
/* | /* | ||||
* Fatal errors are unrecoverable. Typically these | * Fatal errors are unrecoverable. Typically these | ||||
* are caused by DMA errors. Collect h/w state from | * are caused by DMA errors. Collect h/w state from | ||||
* the hal so we can diagnose what's going on. | * the hal so we can diagnose what's going on. | ||||
*/ | */ | ||||
if (ath_hal_getfatalstate(sc->sc_ah, &sp, &len)) { | if (ath_hal_getfatalstate(sc->sc_ah, &sp, &len)) { | ||||
KASSERT(len >= 6*sizeof(u_int32_t), ("len %u bytes", len)); | KASSERT(len >= 6*sizeof(u_int32_t), ("len %u bytes", len)); | ||||
state = sp; | state = sp; | ||||
device_printf(sc->sc_dev, | device_printf(sc->sc_dev, | ||||
"0x%08x 0x%08x 0x%08x, 0x%08x 0x%08x 0x%08x\n", state[0], | "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]); | state[1] , state[2], state[3], state[4], state[5]); | ||||
} | } | ||||
ath_reset(ifp, ATH_RESET_NOLOSS); | ath_reset(sc, ATH_RESET_NOLOSS); | ||||
} | } | ||||
static void | static void | ||||
ath_bmiss_vap(struct ieee80211vap *vap) | ath_bmiss_vap(struct ieee80211vap *vap) | ||||
{ | { | ||||
struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; | struct ath_softc *sc = vap->iv_ic->ic_softc; | ||||
/* | /* | ||||
* Workaround phantom bmiss interrupts by sanity-checking | * Workaround phantom bmiss interrupts by sanity-checking | ||||
* the time of our last rx'd frame. If it is within the | * the time of our last rx'd frame. If it is within the | ||||
* beacon miss interval then ignore the interrupt. If it's | * beacon miss interval then ignore the interrupt. If it's | ||||
* truly a bmiss we'll get another interrupt soon and that'll | * truly a bmiss we'll get another interrupt soon and that'll | ||||
* be dispatched up for processing. Note this applies only | * be dispatched up for processing. Note this applies only | ||||
* for h/w beacon miss events. | * for h/w beacon miss events. | ||||
*/ | */ | ||||
/* | /* | ||||
* XXX TODO: Just read the TSF during the interrupt path; | * XXX TODO: Just read the TSF during the interrupt path; | ||||
* that way we don't have to wake up again just to read it | * that way we don't have to wake up again just to read it | ||||
* again. | * again. | ||||
*/ | */ | ||||
ATH_LOCK(sc); | ATH_LOCK(sc); | ||||
ath_power_set_power_state(sc, HAL_PM_AWAKE); | ath_power_set_power_state(sc, HAL_PM_AWAKE); | ||||
ATH_UNLOCK(sc); | ATH_UNLOCK(sc); | ||||
if ((vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) == 0) { | if ((vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) == 0) { | ||||
struct ifnet *ifp = vap->iv_ic->ic_ifp; | |||||
struct ath_softc *sc = ifp->if_softc; | |||||
u_int64_t lastrx = sc->sc_lastrx; | u_int64_t lastrx = sc->sc_lastrx; | ||||
u_int64_t tsf = ath_hal_gettsf64(sc->sc_ah); | u_int64_t tsf = ath_hal_gettsf64(sc->sc_ah); | ||||
/* XXX should take a locked ref to iv_bss */ | /* XXX should take a locked ref to iv_bss */ | ||||
u_int bmisstimeout = | u_int bmisstimeout = | ||||
vap->iv_bmissthreshold * vap->iv_bss->ni_intval * 1024; | vap->iv_bmissthreshold * vap->iv_bss->ni_intval * 1024; | ||||
DPRINTF(sc, ATH_DEBUG_BEACON, | DPRINTF(sc, ATH_DEBUG_BEACON, | ||||
"%s: tsf %llu lastrx %lld (%llu) bmiss %u\n", | "%s: tsf %llu lastrx %lld (%llu) bmiss %u\n", | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | ath_hal_gethangstate(struct ath_hal *ah, uint32_t mask, uint32_t *hangs) | ||||
*hangs = *(uint32_t *)sp; | *hangs = *(uint32_t *)sp; | ||||
return 1; | return 1; | ||||
} | } | ||||
static void | static void | ||||
ath_bmiss_proc(void *arg, int pending) | ath_bmiss_proc(void *arg, int pending) | ||||
{ | { | ||||
struct ath_softc *sc = arg; | struct ath_softc *sc = arg; | ||||
struct ifnet *ifp = sc->sc_ifp; | |||||
uint32_t hangs; | uint32_t hangs; | ||||
DPRINTF(sc, ATH_DEBUG_ANY, "%s: pending %u\n", __func__, pending); | DPRINTF(sc, ATH_DEBUG_ANY, "%s: pending %u\n", __func__, pending); | ||||
ATH_LOCK(sc); | ATH_LOCK(sc); | ||||
ath_power_set_power_state(sc, HAL_PM_AWAKE); | ath_power_set_power_state(sc, HAL_PM_AWAKE); | ||||
ATH_UNLOCK(sc); | ATH_UNLOCK(sc); | ||||
ath_beacon_miss(sc); | ath_beacon_miss(sc); | ||||
/* | /* | ||||
* Do a reset upon any becaon miss event. | * Do a reset upon any becaon miss event. | ||||
* | * | ||||
* It may be a non-recognised RX clear hang which needs a reset | * It may be a non-recognised RX clear hang which needs a reset | ||||
* to clear. | * to clear. | ||||
*/ | */ | ||||
if (ath_hal_gethangstate(sc->sc_ah, 0xff, &hangs) && hangs != 0) { | if (ath_hal_gethangstate(sc->sc_ah, 0xff, &hangs) && hangs != 0) { | ||||
ath_reset(ifp, ATH_RESET_NOLOSS); | ath_reset(sc, ATH_RESET_NOLOSS); | ||||
device_printf(sc->sc_dev, | device_printf(sc->sc_dev, | ||||
"bb hang detected (0x%x), resetting\n", hangs); | "bb hang detected (0x%x), resetting\n", hangs); | ||||
} else { | } else { | ||||
ath_reset(ifp, ATH_RESET_NOLOSS); | ath_reset(sc, ATH_RESET_NOLOSS); | ||||
ieee80211_beacon_miss(ifp->if_l2com); | ieee80211_beacon_miss(&sc->sc_ic); | ||||
} | } | ||||
/* Force a beacon resync, in case they've drifted */ | /* Force a beacon resync, in case they've drifted */ | ||||
sc->sc_syncbeacon = 1; | sc->sc_syncbeacon = 1; | ||||
ATH_LOCK(sc); | ATH_LOCK(sc); | ||||
ath_power_restore_power_state(sc); | ath_power_restore_power_state(sc); | ||||
ATH_UNLOCK(sc); | ATH_UNLOCK(sc); | ||||
} | } | ||||
/* | /* | ||||
* Handle TKIP MIC setup to deal hardware that doesn't do MIC | * Handle TKIP MIC setup to deal hardware that doesn't do MIC | ||||
* calcs together with WME. If necessary disable the crypto | * calcs together with WME. If necessary disable the crypto | ||||
* hardware and mark the 802.11 state so keys will be setup | * hardware and mark the 802.11 state so keys will be setup | ||||
* with the MIC work done in software. | * with the MIC work done in software. | ||||
*/ | */ | ||||
static void | static void | ||||
ath_settkipmic(struct ath_softc *sc) | ath_settkipmic(struct ath_softc *sc) | ||||
{ | { | ||||
struct ifnet *ifp = sc->sc_ifp; | struct ieee80211com *ic = &sc->sc_ic; | ||||
struct ieee80211com *ic = ifp->if_l2com; | |||||
if ((ic->ic_cryptocaps & IEEE80211_CRYPTO_TKIP) && !sc->sc_wmetkipmic) { | if ((ic->ic_cryptocaps & IEEE80211_CRYPTO_TKIP) && !sc->sc_wmetkipmic) { | ||||
if (ic->ic_flags & IEEE80211_F_WME) { | if (ic->ic_flags & IEEE80211_F_WME) { | ||||
ath_hal_settkipmic(sc->sc_ah, AH_FALSE); | ath_hal_settkipmic(sc->sc_ah, AH_FALSE); | ||||
ic->ic_cryptocaps &= ~IEEE80211_CRYPTO_TKIPMIC; | ic->ic_cryptocaps &= ~IEEE80211_CRYPTO_TKIPMIC; | ||||
} else { | } else { | ||||
ath_hal_settkipmic(sc->sc_ah, AH_TRUE); | ath_hal_settkipmic(sc->sc_ah, AH_TRUE); | ||||
ic->ic_cryptocaps |= IEEE80211_CRYPTO_TKIPMIC; | ic->ic_cryptocaps |= IEEE80211_CRYPTO_TKIPMIC; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
ath_init(void *arg) | ath_init(struct ath_softc *sc) | ||||
{ | { | ||||
struct ath_softc *sc = (struct ath_softc *) arg; | struct ieee80211com *ic = &sc->sc_ic; | ||||
struct ifnet *ifp = sc->sc_ifp; | |||||
struct ieee80211com *ic = ifp->if_l2com; | |||||
struct ath_hal *ah = sc->sc_ah; | struct ath_hal *ah = sc->sc_ah; | ||||
HAL_STATUS status; | HAL_STATUS status; | ||||
DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags 0x%x\n", | DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags 0x%x\n", | ||||
__func__, ifp->if_flags); | __func__, ifp->if_flags); | ||||
ATH_LOCK(sc); | ATH_LOCK(sc); | ||||
/* | /* | ||||
* Force the sleep state awake. | * Force the sleep state awake. | ||||
*/ | */ | ||||
ath_power_setselfgen(sc, HAL_PM_AWAKE); | ath_power_setselfgen(sc, HAL_PM_AWAKE); | ||||
ath_power_set_power_state(sc, HAL_PM_AWAKE); | ath_power_set_power_state(sc, HAL_PM_AWAKE); | ||||
ath_power_setpower(sc, HAL_PM_AWAKE); | ath_power_setpower(sc, HAL_PM_AWAKE); | ||||
/* | /* | ||||
* Stop anything previously setup. This is safe | * Stop anything previously setup. This is safe | ||||
* whether this is the first time through or not. | * whether this is the first time through or not. | ||||
*/ | */ | ||||
ath_stop_locked(ifp); | ath_stop_locked(sc); | ||||
/* | /* | ||||
* The basic interface to setting the hardware in a good | * The basic interface to setting the hardware in a good | ||||
* state is ``reset''. On return the hardware is known to | * state is ``reset''. On return the hardware is known to | ||||
* be powered up and with interrupts disabled. This must | * be powered up and with interrupts disabled. This must | ||||
* be followed by initialization of the appropriate bits | * be followed by initialization of the appropriate bits | ||||
* and then setup of the interrupt mask. | * and then setup of the interrupt mask. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 109 Lines • ▼ Show 20 Lines | ath_init(struct ath_softc *sc) | ||||
/* Enable global TX timeout and carrier sense timeout if available */ | /* Enable global TX timeout and carrier sense timeout if available */ | ||||
if (ath_hal_gtxto_supported(ah)) | if (ath_hal_gtxto_supported(ah)) | ||||
sc->sc_imask |= HAL_INT_GTT; | sc->sc_imask |= HAL_INT_GTT; | ||||
DPRINTF(sc, ATH_DEBUG_RESET, "%s: imask=0x%x\n", | DPRINTF(sc, ATH_DEBUG_RESET, "%s: imask=0x%x\n", | ||||
__func__, sc->sc_imask); | __func__, sc->sc_imask); | ||||
ifp->if_drv_flags |= IFF_DRV_RUNNING; | sc->sc_running = 1; | ||||
callout_reset(&sc->sc_wd_ch, hz, ath_watchdog, sc); | callout_reset(&sc->sc_wd_ch, hz, ath_watchdog, sc); | ||||
ath_hal_intrset(ah, sc->sc_imask); | ath_hal_intrset(ah, sc->sc_imask); | ||||
ath_power_restore_power_state(sc); | ath_power_restore_power_state(sc); | ||||
ATH_UNLOCK(sc); | ATH_UNLOCK(sc); | ||||
#ifdef ATH_TX99_DIAG | #ifdef ATH_TX99_DIAG | ||||
if (sc->sc_tx99 != NULL) | if (sc->sc_tx99 != NULL) | ||||
sc->sc_tx99->start(sc->sc_tx99); | sc->sc_tx99->start(sc->sc_tx99); | ||||
else | else | ||||
#endif | #endif | ||||
ieee80211_start_all(ic); /* start all vap's */ | ieee80211_start_all(ic); /* start all vap's */ | ||||
} | } | ||||
static void | static void | ||||
ath_stop_locked(struct ifnet *ifp) | ath_stop_locked(struct ath_softc *sc) | ||||
{ | { | ||||
struct ath_softc *sc = ifp->if_softc; | |||||
struct ath_hal *ah = sc->sc_ah; | struct ath_hal *ah = sc->sc_ah; | ||||
DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid %u if_flags 0x%x\n", | |||||
__func__, sc->sc_invalid, ifp->if_flags); | |||||
ATH_LOCK_ASSERT(sc); | ATH_LOCK_ASSERT(sc); | ||||
/* | /* | ||||
* Wake the hardware up before fiddling with it. | * Wake the hardware up before fiddling with it. | ||||
*/ | */ | ||||
ath_power_set_power_state(sc, HAL_PM_AWAKE); | ath_power_set_power_state(sc, HAL_PM_AWAKE); | ||||
if (ifp->if_drv_flags & IFF_DRV_RUNNING) { | if (sc->sc_running) { | ||||
/* | /* | ||||
* Shutdown the hardware and driver: | * Shutdown the hardware and driver: | ||||
* reset 802.11 state machine | * reset 802.11 state machine | ||||
* turn off timers | * turn off timers | ||||
* disable interrupts | * disable interrupts | ||||
* turn off the radio | * turn off the radio | ||||
* clear transmit machinery | * clear transmit machinery | ||||
* clear receive machinery | * clear receive machinery | ||||
* drain and release tx queues | * drain and release tx queues | ||||
* reclaim beacon resources | * reclaim beacon resources | ||||
* power down hardware | * power down hardware | ||||
* | * | ||||
* Note that some of this work is not possible if the | * Note that some of this work is not possible if the | ||||
* hardware is gone (invalid). | * hardware is gone (invalid). | ||||
*/ | */ | ||||
#ifdef ATH_TX99_DIAG | #ifdef ATH_TX99_DIAG | ||||
if (sc->sc_tx99 != NULL) | if (sc->sc_tx99 != NULL) | ||||
sc->sc_tx99->stop(sc->sc_tx99); | sc->sc_tx99->stop(sc->sc_tx99); | ||||
#endif | #endif | ||||
callout_stop(&sc->sc_wd_ch); | callout_stop(&sc->sc_wd_ch); | ||||
sc->sc_wd_timer = 0; | sc->sc_wd_timer = 0; | ||||
ifp->if_drv_flags &= ~IFF_DRV_RUNNING; | sc->sc_running = 0; | ||||
if (!sc->sc_invalid) { | if (!sc->sc_invalid) { | ||||
if (sc->sc_softled) { | if (sc->sc_softled) { | ||||
callout_stop(&sc->sc_ledtimer); | callout_stop(&sc->sc_ledtimer); | ||||
ath_hal_gpioset(ah, sc->sc_ledpin, | ath_hal_gpioset(ah, sc->sc_ledpin, | ||||
!sc->sc_ledon); | !sc->sc_ledon); | ||||
sc->sc_blinking = 0; | sc->sc_blinking = 0; | ||||
} | } | ||||
ath_hal_intrset(ah, 0); | ath_hal_intrset(ah, 0); | ||||
▲ Show 20 Lines • Show All 135 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
#undef MAX_RESET_ITERATIONS | #undef MAX_RESET_ITERATIONS | ||||
/* | /* | ||||
* XXX TODO: write ath_reset_releaselock | * XXX TODO: write ath_reset_releaselock | ||||
*/ | */ | ||||
static void | static void | ||||
ath_stop(struct ifnet *ifp) | ath_stop(struct ath_softc *sc) | ||||
{ | { | ||||
struct ath_softc *sc = ifp->if_softc; | |||||
ATH_LOCK(sc); | ATH_LOCK(sc); | ||||
ath_stop_locked(ifp); | ath_stop_locked(sc); | ||||
ATH_UNLOCK(sc); | ATH_UNLOCK(sc); | ||||
} | } | ||||
/* | /* | ||||
* Reset the hardware w/o losing operational state. This is | * Reset the hardware w/o losing operational state. This is | ||||
* basically a more efficient way of doing ath_stop, ath_init, | * basically a more efficient way of doing ath_stop, ath_init, | ||||
* followed by state transitions to the current 802.11 | * followed by state transitions to the current 802.11 | ||||
* operational state. Used to recover from various errors and | * operational state. Used to recover from various errors and | ||||
* to reset or reload hardware state. | * to reset or reload hardware state. | ||||
*/ | */ | ||||
int | int | ||||
ath_reset(struct ifnet *ifp, ATH_RESET_TYPE reset_type) | ath_reset(struct ath_softc *sc, ATH_RESET_TYPE reset_type) | ||||
{ | { | ||||
struct ath_softc *sc = ifp->if_softc; | struct ieee80211com *ic = &sc->sc_ic; | ||||
struct ieee80211com *ic = ifp->if_l2com; | |||||
struct ath_hal *ah = sc->sc_ah; | struct ath_hal *ah = sc->sc_ah; | ||||
HAL_STATUS status; | HAL_STATUS status; | ||||
int i; | int i; | ||||
DPRINTF(sc, ATH_DEBUG_RESET, "%s: called\n", __func__); | DPRINTF(sc, ATH_DEBUG_RESET, "%s: called\n", __func__); | ||||
/* Ensure ATH_LOCK isn't held; ath_rx_proc can't be locked */ | /* Ensure ATH_LOCK isn't held; ath_rx_proc can't be locked */ | ||||
ATH_PCU_UNLOCK_ASSERT(sc); | ATH_PCU_UNLOCK_ASSERT(sc); | ||||
▲ Show 20 Lines • Show All 145 Lines • ▼ Show 20 Lines | for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { | ||||
ATH_TX_LOCK(sc); | ATH_TX_LOCK(sc); | ||||
ath_txq_sched(sc, &sc->sc_txq[i]); | ath_txq_sched(sc, &sc->sc_txq[i]); | ||||
ATH_TX_UNLOCK(sc); | ATH_TX_UNLOCK(sc); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
/* | |||||
* This may have been set during an ath_start() call which | |||||
* set this once it detected a concurrent TX was going on. | |||||
* So, clear it. | |||||
*/ | |||||
IF_LOCK(&ifp->if_snd); | |||||
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; | |||||
IF_UNLOCK(&ifp->if_snd); | |||||
ATH_LOCK(sc); | ATH_LOCK(sc); | ||||
ath_power_restore_power_state(sc); | ath_power_restore_power_state(sc); | ||||
ATH_UNLOCK(sc); | ATH_UNLOCK(sc); | ||||
ATH_PCU_LOCK(sc); | ATH_PCU_LOCK(sc); | ||||
sc->sc_txstart_cnt--; | sc->sc_txstart_cnt--; | ||||
ATH_PCU_UNLOCK(sc); | ATH_PCU_UNLOCK(sc); | ||||
/* Handle any frames in the TX queue */ | /* Handle any frames in the TX queue */ | ||||
/* | /* | ||||
* XXX should this be done by the caller, rather than | * XXX should this be done by the caller, rather than | ||||
* ath_reset() ? | * ath_reset() ? | ||||
*/ | */ | ||||
ath_tx_kick(sc); /* restart xmit */ | ath_tx_kick(sc); /* restart xmit */ | ||||
return 0; | return 0; | ||||
} | } | ||||
static int | static int | ||||
ath_reset_vap(struct ieee80211vap *vap, u_long cmd) | ath_reset_vap(struct ieee80211vap *vap, u_long cmd) | ||||
{ | { | ||||
struct ieee80211com *ic = vap->iv_ic; | struct ieee80211com *ic = vap->iv_ic; | ||||
struct ifnet *ifp = ic->ic_ifp; | struct ath_softc *sc = ic->ic_softc; | ||||
struct ath_softc *sc = ifp->if_softc; | |||||
struct ath_hal *ah = sc->sc_ah; | struct ath_hal *ah = sc->sc_ah; | ||||
switch (cmd) { | switch (cmd) { | ||||
case IEEE80211_IOC_TXPOWER: | case IEEE80211_IOC_TXPOWER: | ||||
/* | /* | ||||
* If per-packet TPC is enabled, then we have nothing | * If per-packet TPC is enabled, then we have nothing | ||||
* to do; otherwise we need to force the global limit. | * to do; otherwise we need to force the global limit. | ||||
* All this can happen directly; no need to reset. | * All this can happen directly; no need to reset. | ||||
*/ | */ | ||||
if (!ath_hal_gettpc(ah)) | if (!ath_hal_gettpc(ah)) | ||||
ath_hal_settxpowlimit(ah, ic->ic_txpowlimit); | ath_hal_settxpowlimit(ah, ic->ic_txpowlimit); | ||||
return 0; | return 0; | ||||
} | } | ||||
/* XXX? Full or NOLOSS? */ | /* XXX? Full or NOLOSS? */ | ||||
return ath_reset(ifp, ATH_RESET_FULL); | return ath_reset(sc, ATH_RESET_FULL); | ||||
} | } | ||||
struct ath_buf * | struct ath_buf * | ||||
_ath_getbuf_locked(struct ath_softc *sc, ath_buf_type_t btype) | _ath_getbuf_locked(struct ath_softc *sc, ath_buf_type_t btype) | ||||
{ | { | ||||
struct ath_buf *bf; | struct ath_buf *bf; | ||||
ATH_TXBUF_LOCK_ASSERT(sc); | ATH_TXBUF_LOCK_ASSERT(sc); | ||||
▲ Show 20 Lines • Show All 143 Lines • ▼ Show 20 Lines | ath_getbuf(struct ath_softc *sc, ath_buf_type_t btype) | ||||
/* | /* | ||||
* If a mgmt buffer was requested but we're out of those, | * If a mgmt buffer was requested but we're out of those, | ||||
* try requesting a normal one. | * try requesting a normal one. | ||||
*/ | */ | ||||
if (bf == NULL && btype == ATH_BUFTYPE_MGMT) | if (bf == NULL && btype == ATH_BUFTYPE_MGMT) | ||||
bf = _ath_getbuf_locked(sc, ATH_BUFTYPE_NORMAL); | bf = _ath_getbuf_locked(sc, ATH_BUFTYPE_NORMAL); | ||||
ATH_TXBUF_UNLOCK(sc); | ATH_TXBUF_UNLOCK(sc); | ||||
if (bf == NULL) { | if (bf == NULL) { | ||||
struct ifnet *ifp = sc->sc_ifp; | |||||
DPRINTF(sc, ATH_DEBUG_XMIT, "%s: stop queue\n", __func__); | DPRINTF(sc, ATH_DEBUG_XMIT, "%s: stop queue\n", __func__); | ||||
sc->sc_stats.ast_tx_qstop++; | sc->sc_stats.ast_tx_qstop++; | ||||
IF_LOCK(&ifp->if_snd); | |||||
ifp->if_drv_flags |= IFF_DRV_OACTIVE; | |||||
IF_UNLOCK(&ifp->if_snd); | |||||
} | } | ||||
return bf; | return bf; | ||||
} | } | ||||
static void | |||||
ath_qflush(struct ifnet *ifp) | |||||
{ | |||||
/* XXX TODO */ | |||||
} | |||||
/* | /* | ||||
* Transmit a single frame. | * Transmit a single frame. | ||||
* | * | ||||
* net80211 will free the node reference if the transmit | * net80211 will free the node reference if the transmit | ||||
* fails, so don't free the node reference here. | * fails, so don't free the node reference here. | ||||
*/ | */ | ||||
static int | static int | ||||
ath_transmit(struct ifnet *ifp, struct mbuf *m) | ath_transmit(struct ieee80211com *ic, struct mbuf *m) | ||||
{ | { | ||||
struct ieee80211com *ic = ifp->if_l2com; | struct ath_softc *sc = ic->ic_softc; | ||||
struct ath_softc *sc = ic->ic_ifp->if_softc; | |||||
struct ieee80211_node *ni; | struct ieee80211_node *ni; | ||||
struct mbuf *next; | struct mbuf *next; | ||||
struct ath_buf *bf; | struct ath_buf *bf; | ||||
ath_bufhead frags; | ath_bufhead frags; | ||||
int retval = 0; | int retval = 0; | ||||
/* | /* | ||||
* Tell the reset path that we're currently transmitting. | * Tell the reset path that we're currently transmitting. | ||||
*/ | */ | ||||
ATH_PCU_LOCK(sc); | ATH_PCU_LOCK(sc); | ||||
if (sc->sc_inreset_cnt > 0) { | if (sc->sc_inreset_cnt > 0) { | ||||
DPRINTF(sc, ATH_DEBUG_XMIT, | DPRINTF(sc, ATH_DEBUG_XMIT, | ||||
"%s: sc_inreset_cnt > 0; bailing\n", __func__); | "%s: sc_inreset_cnt > 0; bailing\n", __func__); | ||||
ATH_PCU_UNLOCK(sc); | ATH_PCU_UNLOCK(sc); | ||||
IF_LOCK(&ifp->if_snd); | |||||
sc->sc_stats.ast_tx_qstop++; | sc->sc_stats.ast_tx_qstop++; | ||||
ifp->if_drv_flags |= IFF_DRV_OACTIVE; | |||||
IF_UNLOCK(&ifp->if_snd); | |||||
ATH_KTR(sc, ATH_KTR_TX, 0, "ath_start_task: OACTIVE, finish"); | ATH_KTR(sc, ATH_KTR_TX, 0, "ath_start_task: OACTIVE, finish"); | ||||
return (ENOBUFS); /* XXX should be EINVAL or? */ | return (ENOBUFS); /* XXX should be EINVAL or? */ | ||||
} | } | ||||
sc->sc_txstart_cnt++; | sc->sc_txstart_cnt++; | ||||
ATH_PCU_UNLOCK(sc); | ATH_PCU_UNLOCK(sc); | ||||
/* Wake the hardware up already */ | /* Wake the hardware up already */ | ||||
ATH_LOCK(sc); | ATH_LOCK(sc); | ||||
Show All 23 Lines | ath_transmit(struct ieee80211com *ic, struct mbuf *m) | ||||
* XXX we should also track the per-node hardware queue | * XXX we should also track the per-node hardware queue | ||||
* depth so it is easy to limit the _SUM_ of the swq and | * depth so it is easy to limit the _SUM_ of the swq and | ||||
* hwq frames. Since we only schedule two HWQ frames | * hwq frames. Since we only schedule two HWQ frames | ||||
* at a time, this should be OK for now. | * at a time, this should be OK for now. | ||||
*/ | */ | ||||
if ((!(m->m_flags & M_EAPOL)) && | if ((!(m->m_flags & M_EAPOL)) && | ||||
(ATH_NODE(ni)->an_swq_depth > sc->sc_txq_node_maxdepth)) { | (ATH_NODE(ni)->an_swq_depth > sc->sc_txq_node_maxdepth)) { | ||||
sc->sc_stats.ast_tx_nodeq_overflow++; | sc->sc_stats.ast_tx_nodeq_overflow++; | ||||
m_freem(m); | |||||
m = NULL; | |||||
retval = ENOBUFS; | retval = ENOBUFS; | ||||
goto finish; | goto finish; | ||||
} | } | ||||
/* | /* | ||||
* Check how many TX buffers are available. | * Check how many TX buffers are available. | ||||
* | * | ||||
* If this is for non-EAPOL traffic, just leave some | * If this is for non-EAPOL traffic, just leave some | ||||
* space free in order for buffer cloning and raw | * space free in order for buffer cloning and raw | ||||
* frame transmission to occur. | * frame transmission to occur. | ||||
* | * | ||||
* If it's for EAPOL traffic, ignore this for now. | * If it's for EAPOL traffic, ignore this for now. | ||||
* Management traffic will be sent via the raw transmit | * Management traffic will be sent via the raw transmit | ||||
* method which bypasses this check. | * method which bypasses this check. | ||||
* | * | ||||
* This is needed to ensure that EAPOL frames during | * This is needed to ensure that EAPOL frames during | ||||
* (re) keying have a chance to go out. | * (re) keying have a chance to go out. | ||||
* | * | ||||
* See kern/138379 for more information. | * See kern/138379 for more information. | ||||
*/ | */ | ||||
if ((!(m->m_flags & M_EAPOL)) && | if ((!(m->m_flags & M_EAPOL)) && | ||||
(sc->sc_txbuf_cnt <= sc->sc_txq_data_minfree)) { | (sc->sc_txbuf_cnt <= sc->sc_txq_data_minfree)) { | ||||
sc->sc_stats.ast_tx_nobuf++; | sc->sc_stats.ast_tx_nobuf++; | ||||
m_freem(m); | |||||
m = NULL; | |||||
retval = ENOBUFS; | retval = ENOBUFS; | ||||
goto finish; | goto finish; | ||||
} | } | ||||
/* | /* | ||||
* Grab a TX buffer and associated resources. | * Grab a TX buffer and associated resources. | ||||
* | * | ||||
* If it's an EAPOL frame, allocate a MGMT ath_buf. | * If it's an EAPOL frame, allocate a MGMT ath_buf. | ||||
Show All 11 Lines | ath_transmit(struct ieee80211com *ic, struct mbuf *m) | ||||
if (bf == NULL) { | if (bf == NULL) { | ||||
/* | /* | ||||
* If we failed to allocate a buffer, fail. | * If we failed to allocate a buffer, fail. | ||||
* | * | ||||
* We shouldn't fail normally, due to the check | * We shouldn't fail normally, due to the check | ||||
* above. | * above. | ||||
*/ | */ | ||||
sc->sc_stats.ast_tx_nobuf++; | sc->sc_stats.ast_tx_nobuf++; | ||||
IF_LOCK(&ifp->if_snd); | |||||
ifp->if_drv_flags |= IFF_DRV_OACTIVE; | |||||
IF_UNLOCK(&ifp->if_snd); | |||||
m_freem(m); | |||||
m = NULL; | |||||
retval = ENOBUFS; | retval = ENOBUFS; | ||||
goto finish; | goto finish; | ||||
} | } | ||||
/* | /* | ||||
* At this point we have a buffer; so we need to free it | * At this point we have a buffer; so we need to free it | ||||
* if we hit any error conditions. | * if we hit any error conditions. | ||||
*/ | */ | ||||
/* | /* | ||||
* Check for fragmentation. If this frame | * Check for fragmentation. If this frame | ||||
* has been broken up verify we have enough | * has been broken up verify we have enough | ||||
* buffers to send all the fragments so all | * buffers to send all the fragments so all | ||||
* go out or none... | * go out or none... | ||||
*/ | */ | ||||
TAILQ_INIT(&frags); | TAILQ_INIT(&frags); | ||||
if ((m->m_flags & M_FRAG) && | if ((m->m_flags & M_FRAG) && | ||||
!ath_txfrag_setup(sc, &frags, m, ni)) { | !ath_txfrag_setup(sc, &frags, m, ni)) { | ||||
DPRINTF(sc, ATH_DEBUG_XMIT, | DPRINTF(sc, ATH_DEBUG_XMIT, | ||||
"%s: out of txfrag buffers\n", __func__); | "%s: out of txfrag buffers\n", __func__); | ||||
sc->sc_stats.ast_tx_nofrag++; | sc->sc_stats.ast_tx_nofrag++; | ||||
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); | if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); | ||||
/* | |||||
* XXXGL: is mbuf valid after ath_txfrag_setup? If yes, | |||||
* we shouldn't free it but return back. | |||||
*/ | |||||
ath_freetx(m); | ath_freetx(m); | ||||
m = NULL; | |||||
goto bad; | goto bad; | ||||
} | } | ||||
/* | /* | ||||
* At this point if we have any TX fragments, then we will | * At this point if we have any TX fragments, then we will | ||||
* have bumped the node reference once for each of those. | * have bumped the node reference once for each of those. | ||||
*/ | */ | ||||
Show All 25 Lines | if (m->m_flags & M_FRAG) { | ||||
*/ | */ | ||||
TAILQ_FOREACH(n_fbf, &frags, bf_list) { | TAILQ_FOREACH(n_fbf, &frags, bf_list) { | ||||
fbf->bf_nextfraglen = fm->m_pkthdr.len; | fbf->bf_nextfraglen = fm->m_pkthdr.len; | ||||
fbf = n_fbf; | fbf = n_fbf; | ||||
fm = fm->m_nextpkt; | fm = fm->m_nextpkt; | ||||
} | } | ||||
} | } | ||||
/* | |||||
* Bump the ifp output counter. | |||||
* | |||||
* XXX should use atomics? | |||||
*/ | |||||
if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); | |||||
nextfrag: | nextfrag: | ||||
/* | /* | ||||
* Pass the frame to the h/w for transmission. | * Pass the frame to the h/w for transmission. | ||||
* Fragmented frames have each frag chained together | * Fragmented frames have each frag chained together | ||||
* with m_nextpkt. We know there are sufficient ath_buf's | * with m_nextpkt. We know there are sufficient ath_buf's | ||||
* to send all the frags because of work done by | * to send all the frags because of work done by | ||||
* ath_txfrag_setup. We leave m_nextpkt set while | * ath_txfrag_setup. We leave m_nextpkt set while | ||||
* calling ath_tx_start so it can use it to extend the | * calling ath_tx_start so it can use it to extend the | ||||
* the tx duration to cover the subsequent frag and | * the tx duration to cover the subsequent frag and | ||||
* so it can reclaim all the mbufs in case of an error; | * so it can reclaim all the mbufs in case of an error; | ||||
* ath_tx_start clears m_nextpkt once it commits to | * ath_tx_start clears m_nextpkt once it commits to | ||||
* handing the frame to the hardware. | * handing the frame to the hardware. | ||||
* | * | ||||
* Note: if this fails, then the mbufs are freed but | * Note: if this fails, then the mbufs are freed but | ||||
* not the node reference. | * not the node reference. | ||||
*/ | */ | ||||
next = m->m_nextpkt; | next = m->m_nextpkt; | ||||
if (ath_tx_start(sc, ni, bf, m)) { | if (ath_tx_start(sc, ni, bf, m)) { | ||||
bad: | bad: | ||||
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); | if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); | ||||
reclaim: | reclaim: | ||||
bf->bf_m = NULL; | bf->bf_m = NULL; | ||||
bf->bf_node = NULL; | bf->bf_node = NULL; | ||||
ATH_TXBUF_LOCK(sc); | ATH_TXBUF_LOCK(sc); | ||||
ath_returnbuf_head(sc, bf); | ath_returnbuf_head(sc, bf); | ||||
/* | /* | ||||
* Free the rest of the node references and | * Free the rest of the node references and | ||||
* buffers for the fragment list. | * buffers for the fragment list. | ||||
▲ Show 20 Lines • Show All 67 Lines • ▼ Show 20 Lines | |||||
* Block/unblock tx+rx processing while a key change is done. | * Block/unblock tx+rx processing while a key change is done. | ||||
* We assume the caller serializes key management operations | * We assume the caller serializes key management operations | ||||
* so we only need to worry about synchronization with other | * so we only need to worry about synchronization with other | ||||
* uses that originate in the driver. | * uses that originate in the driver. | ||||
*/ | */ | ||||
static void | static void | ||||
ath_key_update_begin(struct ieee80211vap *vap) | ath_key_update_begin(struct ieee80211vap *vap) | ||||
{ | { | ||||
struct ifnet *ifp = vap->iv_ic->ic_ifp; | struct ath_softc *sc = vap->iv_ic->ic_softc; | ||||
struct ath_softc *sc = ifp->if_softc; | |||||
DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__); | DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__); | ||||
taskqueue_block(sc->sc_tq); | taskqueue_block(sc->sc_tq); | ||||
} | } | ||||
static void | static void | ||||
ath_key_update_end(struct ieee80211vap *vap) | ath_key_update_end(struct ieee80211vap *vap) | ||||
{ | { | ||||
struct ifnet *ifp = vap->iv_ic->ic_ifp; | struct ath_softc *sc = vap->iv_ic->ic_softc; | ||||
struct ath_softc *sc = ifp->if_softc; | |||||
DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__); | DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__); | ||||
taskqueue_unblock(sc->sc_tq); | taskqueue_unblock(sc->sc_tq); | ||||
} | } | ||||
static void | static void | ||||
ath_update_promisc(struct ieee80211com *ic) | ath_update_promisc(struct ieee80211com *ic) | ||||
{ | { | ||||
Show All 14 Lines | |||||
/* | /* | ||||
* Driver-internal mcast update call. | * Driver-internal mcast update call. | ||||
* | * | ||||
* Assumes the hardware is already awake. | * Assumes the hardware is already awake. | ||||
*/ | */ | ||||
static void | static void | ||||
ath_update_mcast_hw(struct ath_softc *sc) | ath_update_mcast_hw(struct ath_softc *sc) | ||||
{ | { | ||||
struct ifnet *ifp = sc->sc_ifp; | struct ieee80211com *ic = &sc->sc_ic; | ||||
u_int32_t mfilt[2]; | u_int32_t mfilt[2]; | ||||
/* calculate and install multicast filter */ | /* calculate and install multicast filter */ | ||||
if ((ifp->if_flags & IFF_ALLMULTI) == 0) { | if (ic->ic_allmulti == 0) { | ||||
struct ieee80211vap *vap; | |||||
struct ifnet *ifp; | |||||
struct ifmultiaddr *ifma; | struct ifmultiaddr *ifma; | ||||
/* | /* | ||||
* Merge multicast addresses to form the hardware filter. | * Merge multicast addresses to form the hardware filter. | ||||
*/ | */ | ||||
mfilt[0] = mfilt[1] = 0; | mfilt[0] = mfilt[1] = 0; | ||||
if_maddr_rlock(ifp); /* XXX need some fiddling to remove? */ | TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { | ||||
ifp = vap->iv_ifp; | |||||
if_maddr_rlock(ifp); | |||||
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { | TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { | ||||
caddr_t dl; | caddr_t dl; | ||||
u_int32_t val; | uint32_t val; | ||||
u_int8_t pos; | uint8_t pos; | ||||
/* calculate XOR of eight 6bit values */ | /* calculate XOR of eight 6bit values */ | ||||
dl = LLADDR((struct sockaddr_dl *) ifma->ifma_addr); | dl = LLADDR((struct sockaddr_dl *) | ||||
ifma->ifma_addr); | |||||
val = LE_READ_4(dl + 0); | val = LE_READ_4(dl + 0); | ||||
pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; | pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ | ||||
val; | |||||
val = LE_READ_4(dl + 3); | val = LE_READ_4(dl + 3); | ||||
pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; | pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ | ||||
val; | |||||
pos &= 0x3f; | pos &= 0x3f; | ||||
mfilt[pos / 32] |= (1 << (pos % 32)); | mfilt[pos / 32] |= (1 << (pos % 32)); | ||||
} | } | ||||
if_maddr_runlock(ifp); | if_maddr_runlock(ifp); | ||||
} | |||||
} else | } else | ||||
mfilt[0] = mfilt[1] = ~0; | mfilt[0] = mfilt[1] = ~0; | ||||
ath_hal_setmcastfilter(sc->sc_ah, mfilt[0], mfilt[1]); | ath_hal_setmcastfilter(sc->sc_ah, mfilt[0], mfilt[1]); | ||||
DPRINTF(sc, ATH_DEBUG_MODE, "%s: MC filter %08x:%08x\n", | DPRINTF(sc, ATH_DEBUG_MODE, "%s: MC filter %08x:%08x\n", | ||||
__func__, mfilt[0], mfilt[1]); | __func__, mfilt[0], mfilt[1]); | ||||
} | } | ||||
Show All 16 Lines | ath_update_mcast(struct ieee80211com *ic) | ||||
ATH_LOCK(sc); | ATH_LOCK(sc); | ||||
ath_power_restore_power_state(sc); | ath_power_restore_power_state(sc); | ||||
ATH_UNLOCK(sc); | ATH_UNLOCK(sc); | ||||
} | } | ||||
void | void | ||||
ath_mode_init(struct ath_softc *sc) | ath_mode_init(struct ath_softc *sc) | ||||
{ | { | ||||
struct ifnet *ifp = sc->sc_ifp; | struct ieee80211com *ic = &sc->sc_ic; | ||||
struct ath_hal *ah = sc->sc_ah; | struct ath_hal *ah = sc->sc_ah; | ||||
u_int32_t rfilt; | u_int32_t rfilt; | ||||
/* configure rx filter */ | /* configure rx filter */ | ||||
rfilt = ath_calcrxfilter(sc); | rfilt = ath_calcrxfilter(sc); | ||||
ath_hal_setrxfilter(ah, rfilt); | ath_hal_setrxfilter(ah, rfilt); | ||||
/* configure operational mode */ | /* configure operational mode */ | ||||
ath_hal_setopmode(ah); | ath_hal_setopmode(ah); | ||||
DPRINTF(sc, ATH_DEBUG_STATE | ATH_DEBUG_MODE, | |||||
"%s: ah=%p, ifp=%p, if_addr=%p\n", | |||||
__func__, | |||||
ah, | |||||
ifp, | |||||
(ifp == NULL) ? NULL : ifp->if_addr); | |||||
/* handle any link-level address change */ | /* handle any link-level address change */ | ||||
ath_hal_setmac(ah, IF_LLADDR(ifp)); | ath_hal_setmac(ah, ic->ic_macaddr); | ||||
/* calculate and install multicast filter */ | /* calculate and install multicast filter */ | ||||
ath_update_mcast_hw(sc); | ath_update_mcast_hw(sc); | ||||
} | } | ||||
/* | /* | ||||
* Set the slot time based on the current setting. | * Set the slot time based on the current setting. | ||||
*/ | */ | ||||
void | void | ||||
ath_setslottime(struct ath_softc *sc) | ath_setslottime(struct ath_softc *sc) | ||||
{ | { | ||||
struct ieee80211com *ic = sc->sc_ifp->if_l2com; | struct ieee80211com *ic = &sc->sc_ic; | ||||
struct ath_hal *ah = sc->sc_ah; | struct ath_hal *ah = sc->sc_ah; | ||||
u_int usec; | u_int usec; | ||||
if (IEEE80211_IS_CHAN_HALF(ic->ic_curchan)) | if (IEEE80211_IS_CHAN_HALF(ic->ic_curchan)) | ||||
usec = 13; | usec = 13; | ||||
else if (IEEE80211_IS_CHAN_QUARTER(ic->ic_curchan)) | else if (IEEE80211_IS_CHAN_QUARTER(ic->ic_curchan)) | ||||
usec = 21; | usec = 21; | ||||
else if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) { | else if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) { | ||||
▲ Show 20 Lines • Show All 67 Lines • ▼ Show 20 Lines | |||||
* Reset the hardware, with no loss. | * Reset the hardware, with no loss. | ||||
* | * | ||||
* This can't be used for a general case reset. | * This can't be used for a general case reset. | ||||
*/ | */ | ||||
static void | static void | ||||
ath_reset_proc(void *arg, int pending) | ath_reset_proc(void *arg, int pending) | ||||
{ | { | ||||
struct ath_softc *sc = arg; | struct ath_softc *sc = arg; | ||||
struct ifnet *ifp = sc->sc_ifp; | |||||
#if 0 | #if 0 | ||||
device_printf(sc->sc_dev, "%s: resetting\n", __func__); | device_printf(sc->sc_dev, "%s: resetting\n", __func__); | ||||
#endif | #endif | ||||
ath_reset(ifp, ATH_RESET_NOLOSS); | ath_reset(sc, ATH_RESET_NOLOSS); | ||||
} | } | ||||
/* | /* | ||||
* Reset the hardware after detecting beacons have stopped. | * Reset the hardware after detecting beacons have stopped. | ||||
*/ | */ | ||||
static void | static void | ||||
ath_bstuck_proc(void *arg, int pending) | ath_bstuck_proc(void *arg, int pending) | ||||
{ | { | ||||
struct ath_softc *sc = arg; | struct ath_softc *sc = arg; | ||||
struct ifnet *ifp = sc->sc_ifp; | |||||
uint32_t hangs = 0; | uint32_t hangs = 0; | ||||
if (ath_hal_gethangstate(sc->sc_ah, 0xff, &hangs) && 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); | device_printf(sc->sc_dev, "bb hang detected (0x%x)\n", hangs); | ||||
#ifdef ATH_DEBUG_ALQ | #ifdef ATH_DEBUG_ALQ | ||||
if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_STUCK_BEACON)) | 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); | if_ath_alq_post(&sc->sc_alq, ATH_ALQ_STUCK_BEACON, 0, NULL); | ||||
#endif | #endif | ||||
device_printf(sc->sc_dev, "stuck beacon; resetting (bmiss count %u)\n", | device_printf(sc->sc_dev, "stuck beacon; resetting (bmiss count %u)\n", | ||||
sc->sc_bmisscount); | sc->sc_bmisscount); | ||||
sc->sc_stats.ast_bstuck++; | sc->sc_stats.ast_bstuck++; | ||||
/* | /* | ||||
* This assumes that there's no simultaneous channel mode change | * This assumes that there's no simultaneous channel mode change | ||||
* occuring. | * occuring. | ||||
*/ | */ | ||||
ath_reset(ifp, ATH_RESET_NOLOSS); | ath_reset(sc, ATH_RESET_NOLOSS); | ||||
} | } | ||||
static void | static void | ||||
ath_load_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) | ath_load_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) | ||||
{ | { | ||||
bus_addr_t *paddr = (bus_addr_t*) arg; | bus_addr_t *paddr = (bus_addr_t*) arg; | ||||
KASSERT(error == 0, ("error %u on bus_dma callback", error)); | KASSERT(error == 0, ("error %u on bus_dma callback", error)); | ||||
*paddr = segs->ds_addr; | *paddr = segs->ds_addr; | ||||
▲ Show 20 Lines • Show All 353 Lines • ▼ Show 20 Lines | if (sc->sc_txdma_mgmt.dd_desc_len != 0) | ||||
ath_descdma_cleanup(sc, &sc->sc_txdma_mgmt, | ath_descdma_cleanup(sc, &sc->sc_txdma_mgmt, | ||||
&sc->sc_txbuf_mgmt); | &sc->sc_txbuf_mgmt); | ||||
} | } | ||||
static struct ieee80211_node * | static struct ieee80211_node * | ||||
ath_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) | ath_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) | ||||
{ | { | ||||
struct ieee80211com *ic = vap->iv_ic; | struct ieee80211com *ic = vap->iv_ic; | ||||
struct ath_softc *sc = ic->ic_ifp->if_softc; | struct ath_softc *sc = ic->ic_softc; | ||||
const size_t space = sizeof(struct ath_node) + sc->sc_rc->arc_space; | const size_t space = sizeof(struct ath_node) + sc->sc_rc->arc_space; | ||||
struct ath_node *an; | struct ath_node *an; | ||||
an = malloc(space, M_80211_NODE, M_NOWAIT|M_ZERO); | an = malloc(space, M_80211_NODE, M_NOWAIT|M_ZERO); | ||||
if (an == NULL) { | if (an == NULL) { | ||||
/* XXX stat+msg */ | /* XXX stat+msg */ | ||||
return NULL; | return NULL; | ||||
} | } | ||||
Show All 10 Lines | ath_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) | ||||
DPRINTF(sc, ATH_DEBUG_NODE, "%s: %6D: an %p\n", __func__, mac, ":", an); | DPRINTF(sc, ATH_DEBUG_NODE, "%s: %6D: an %p\n", __func__, mac, ":", an); | ||||
return &an->an_node; | return &an->an_node; | ||||
} | } | ||||
static void | static void | ||||
ath_node_cleanup(struct ieee80211_node *ni) | ath_node_cleanup(struct ieee80211_node *ni) | ||||
{ | { | ||||
struct ieee80211com *ic = ni->ni_ic; | struct ieee80211com *ic = ni->ni_ic; | ||||
struct ath_softc *sc = ic->ic_ifp->if_softc; | struct ath_softc *sc = ic->ic_softc; | ||||
DPRINTF(sc, ATH_DEBUG_NODE, "%s: %6D: an %p\n", __func__, | DPRINTF(sc, ATH_DEBUG_NODE, "%s: %6D: an %p\n", __func__, | ||||
ni->ni_macaddr, ":", ATH_NODE(ni)); | ni->ni_macaddr, ":", ATH_NODE(ni)); | ||||
/* Cleanup ath_tid, free unused bufs, unlink bufs in TXQ */ | /* Cleanup ath_tid, free unused bufs, unlink bufs in TXQ */ | ||||
ath_tx_node_flush(sc, ATH_NODE(ni)); | ath_tx_node_flush(sc, ATH_NODE(ni)); | ||||
ath_rate_node_cleanup(sc, ATH_NODE(ni)); | ath_rate_node_cleanup(sc, ATH_NODE(ni)); | ||||
sc->sc_node_cleanup(ni); | sc->sc_node_cleanup(ni); | ||||
} | } | ||||
static void | static void | ||||
ath_node_free(struct ieee80211_node *ni) | ath_node_free(struct ieee80211_node *ni) | ||||
{ | { | ||||
struct ieee80211com *ic = ni->ni_ic; | struct ieee80211com *ic = ni->ni_ic; | ||||
struct ath_softc *sc = ic->ic_ifp->if_softc; | struct ath_softc *sc = ic->ic_softc; | ||||
DPRINTF(sc, ATH_DEBUG_NODE, "%s: %6D: an %p\n", __func__, | DPRINTF(sc, ATH_DEBUG_NODE, "%s: %6D: an %p\n", __func__, | ||||
ni->ni_macaddr, ":", ATH_NODE(ni)); | ni->ni_macaddr, ":", ATH_NODE(ni)); | ||||
mtx_destroy(&ATH_NODE(ni)->an_mtx); | mtx_destroy(&ATH_NODE(ni)->an_mtx); | ||||
sc->sc_node_free(ni); | sc->sc_node_free(ni); | ||||
} | } | ||||
static void | static void | ||||
ath_node_getsignal(const struct ieee80211_node *ni, int8_t *rssi, int8_t *noise) | ath_node_getsignal(const struct ieee80211_node *ni, int8_t *rssi, int8_t *noise) | ||||
{ | { | ||||
struct ieee80211com *ic = ni->ni_ic; | struct ieee80211com *ic = ni->ni_ic; | ||||
struct ath_softc *sc = ic->ic_ifp->if_softc; | struct ath_softc *sc = ic->ic_softc; | ||||
struct ath_hal *ah = sc->sc_ah; | struct ath_hal *ah = sc->sc_ah; | ||||
*rssi = ic->ic_node_getrssi(ni); | *rssi = ic->ic_node_getrssi(ni); | ||||
if (ni->ni_chan != IEEE80211_CHAN_ANYC) | if (ni->ni_chan != IEEE80211_CHAN_ANYC) | ||||
*noise = ath_hal_getchannoise(ah, ni->ni_chan); | *noise = ath_hal_getchannoise(ah, ni->ni_chan); | ||||
else | else | ||||
*noise = -95; /* nominally correct */ | *noise = -95; /* nominally correct */ | ||||
} | } | ||||
▲ Show 20 Lines • Show All 121 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* Update WME parameters for a transmit queue. | * Update WME parameters for a transmit queue. | ||||
*/ | */ | ||||
static int | static int | ||||
ath_txq_update(struct ath_softc *sc, int ac) | ath_txq_update(struct ath_softc *sc, int ac) | ||||
{ | { | ||||
#define ATH_EXPONENT_TO_VALUE(v) ((1<<v)-1) | #define ATH_EXPONENT_TO_VALUE(v) ((1<<v)-1) | ||||
#define ATH_TXOP_TO_US(v) (v<<5) | #define ATH_TXOP_TO_US(v) (v<<5) | ||||
struct ifnet *ifp = sc->sc_ifp; | struct ieee80211com *ic = &sc->sc_ic; | ||||
struct ieee80211com *ic = ifp->if_l2com; | |||||
struct ath_txq *txq = sc->sc_ac2q[ac]; | struct ath_txq *txq = sc->sc_ac2q[ac]; | ||||
struct wmeParams *wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[ac]; | struct wmeParams *wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[ac]; | ||||
struct ath_hal *ah = sc->sc_ah; | struct ath_hal *ah = sc->sc_ah; | ||||
HAL_TXQ_INFO qi; | HAL_TXQ_INFO qi; | ||||
ath_hal_gettxqueueprops(ah, txq->axq_qnum, &qi); | ath_hal_gettxqueueprops(ah, txq->axq_qnum, &qi); | ||||
#ifdef IEEE80211_SUPPORT_TDMA | #ifdef IEEE80211_SUPPORT_TDMA | ||||
if (sc->sc_tdma) { | if (sc->sc_tdma) { | ||||
▲ Show 20 Lines • Show All 56 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
/* | /* | ||||
* Callback from the 802.11 layer to update WME parameters. | * Callback from the 802.11 layer to update WME parameters. | ||||
*/ | */ | ||||
int | int | ||||
ath_wme_update(struct ieee80211com *ic) | ath_wme_update(struct ieee80211com *ic) | ||||
{ | { | ||||
struct ath_softc *sc = ic->ic_ifp->if_softc; | struct ath_softc *sc = ic->ic_softc; | ||||
return !ath_txq_update(sc, WME_AC_BE) || | return !ath_txq_update(sc, WME_AC_BE) || | ||||
!ath_txq_update(sc, WME_AC_BK) || | !ath_txq_update(sc, WME_AC_BK) || | ||||
!ath_txq_update(sc, WME_AC_VI) || | !ath_txq_update(sc, WME_AC_VI) || | ||||
!ath_txq_update(sc, WME_AC_VO) ? EIO : 0; | !ath_txq_update(sc, WME_AC_VO) ? EIO : 0; | ||||
} | } | ||||
/* | /* | ||||
Show All 34 Lines | ath_tx_findrix(const struct ath_softc *sc, uint8_t rate) | ||||
return (rix == 0xff ? 0 : rix); | return (rix == 0xff ? 0 : rix); | ||||
} | } | ||||
static void | static void | ||||
ath_tx_update_stats(struct ath_softc *sc, struct ath_tx_status *ts, | ath_tx_update_stats(struct ath_softc *sc, struct ath_tx_status *ts, | ||||
struct ath_buf *bf) | struct ath_buf *bf) | ||||
{ | { | ||||
struct ieee80211_node *ni = bf->bf_node; | struct ieee80211_node *ni = bf->bf_node; | ||||
struct ifnet *ifp = sc->sc_ifp; | struct ieee80211com *ic = &sc->sc_ic; | ||||
struct ieee80211com *ic = ifp->if_l2com; | |||||
int sr, lr, pri; | int sr, lr, pri; | ||||
if (ts->ts_status == 0) { | if (ts->ts_status == 0) { | ||||
u_int8_t txant = ts->ts_antenna; | u_int8_t txant = ts->ts_antenna; | ||||
sc->sc_stats.ast_ant_tx[txant]++; | sc->sc_stats.ast_ant_tx[txant]++; | ||||
sc->sc_ant_tx[txant]++; | sc->sc_ant_tx[txant]++; | ||||
if (ts->ts_finaltsi != 0) | if (ts->ts_finaltsi != 0) | ||||
sc->sc_stats.ast_tx_altrate++; | sc->sc_stats.ast_tx_altrate++; | ||||
▲ Show 20 Lines • Show All 338 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* Deferred processing of transmit interrupt; special-cased | * Deferred processing of transmit interrupt; special-cased | ||||
* for a single hardware transmit queue (e.g. 5210 and 5211). | * for a single hardware transmit queue (e.g. 5210 and 5211). | ||||
*/ | */ | ||||
static void | static void | ||||
ath_tx_proc_q0(void *arg, int npending) | ath_tx_proc_q0(void *arg, int npending) | ||||
{ | { | ||||
struct ath_softc *sc = arg; | struct ath_softc *sc = arg; | ||||
struct ifnet *ifp = sc->sc_ifp; | |||||
uint32_t txqs; | uint32_t txqs; | ||||
ATH_PCU_LOCK(sc); | ATH_PCU_LOCK(sc); | ||||
sc->sc_txproc_cnt++; | sc->sc_txproc_cnt++; | ||||
txqs = sc->sc_txq_active; | txqs = sc->sc_txq_active; | ||||
sc->sc_txq_active &= ~txqs; | sc->sc_txq_active &= ~txqs; | ||||
ATH_PCU_UNLOCK(sc); | ATH_PCU_UNLOCK(sc); | ||||
ATH_LOCK(sc); | ATH_LOCK(sc); | ||||
ath_power_set_power_state(sc, HAL_PM_AWAKE); | ath_power_set_power_state(sc, HAL_PM_AWAKE); | ||||
ATH_UNLOCK(sc); | ATH_UNLOCK(sc); | ||||
ATH_KTR(sc, ATH_KTR_TXCOMP, 1, | ATH_KTR(sc, ATH_KTR_TXCOMP, 1, | ||||
"ath_tx_proc_q0: txqs=0x%08x", txqs); | "ath_tx_proc_q0: txqs=0x%08x", txqs); | ||||
if (TXQACTIVE(txqs, 0) && ath_tx_processq(sc, &sc->sc_txq[0], 1)) | if (TXQACTIVE(txqs, 0) && ath_tx_processq(sc, &sc->sc_txq[0], 1)) | ||||
/* XXX why is lastrx updated in tx code? */ | /* XXX why is lastrx updated in tx code? */ | ||||
sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah); | sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah); | ||||
if (TXQACTIVE(txqs, sc->sc_cabq->axq_qnum)) | if (TXQACTIVE(txqs, sc->sc_cabq->axq_qnum)) | ||||
ath_tx_processq(sc, sc->sc_cabq, 1); | ath_tx_processq(sc, sc->sc_cabq, 1); | ||||
IF_LOCK(&ifp->if_snd); | |||||
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; | |||||
IF_UNLOCK(&ifp->if_snd); | |||||
sc->sc_wd_timer = 0; | sc->sc_wd_timer = 0; | ||||
if (sc->sc_softled) | if (sc->sc_softled) | ||||
ath_led_event(sc, sc->sc_txrix); | ath_led_event(sc, sc->sc_txrix); | ||||
ATH_PCU_LOCK(sc); | ATH_PCU_LOCK(sc); | ||||
sc->sc_txproc_cnt--; | sc->sc_txproc_cnt--; | ||||
ATH_PCU_UNLOCK(sc); | ATH_PCU_UNLOCK(sc); | ||||
ATH_LOCK(sc); | ATH_LOCK(sc); | ||||
ath_power_restore_power_state(sc); | ath_power_restore_power_state(sc); | ||||
ATH_UNLOCK(sc); | ATH_UNLOCK(sc); | ||||
ath_tx_kick(sc); | ath_tx_kick(sc); | ||||
} | } | ||||
/* | /* | ||||
* Deferred processing of transmit interrupt; special-cased | * Deferred processing of transmit interrupt; special-cased | ||||
* for four hardware queues, 0-3 (e.g. 5212 w/ WME support). | * for four hardware queues, 0-3 (e.g. 5212 w/ WME support). | ||||
*/ | */ | ||||
static void | static void | ||||
ath_tx_proc_q0123(void *arg, int npending) | ath_tx_proc_q0123(void *arg, int npending) | ||||
{ | { | ||||
struct ath_softc *sc = arg; | struct ath_softc *sc = arg; | ||||
struct ifnet *ifp = sc->sc_ifp; | |||||
int nacked; | int nacked; | ||||
uint32_t txqs; | uint32_t txqs; | ||||
ATH_PCU_LOCK(sc); | ATH_PCU_LOCK(sc); | ||||
sc->sc_txproc_cnt++; | sc->sc_txproc_cnt++; | ||||
txqs = sc->sc_txq_active; | txqs = sc->sc_txq_active; | ||||
sc->sc_txq_active &= ~txqs; | sc->sc_txq_active &= ~txqs; | ||||
ATH_PCU_UNLOCK(sc); | ATH_PCU_UNLOCK(sc); | ||||
Show All 17 Lines | if (TXQACTIVE(txqs, 2)) | ||||
nacked += ath_tx_processq(sc, &sc->sc_txq[2], 1); | nacked += ath_tx_processq(sc, &sc->sc_txq[2], 1); | ||||
if (TXQACTIVE(txqs, 3)) | if (TXQACTIVE(txqs, 3)) | ||||
nacked += ath_tx_processq(sc, &sc->sc_txq[3], 1); | nacked += ath_tx_processq(sc, &sc->sc_txq[3], 1); | ||||
if (TXQACTIVE(txqs, sc->sc_cabq->axq_qnum)) | if (TXQACTIVE(txqs, sc->sc_cabq->axq_qnum)) | ||||
ath_tx_processq(sc, sc->sc_cabq, 1); | ath_tx_processq(sc, sc->sc_cabq, 1); | ||||
if (nacked) | if (nacked) | ||||
sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah); | sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah); | ||||
IF_LOCK(&ifp->if_snd); | |||||
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; | |||||
IF_UNLOCK(&ifp->if_snd); | |||||
sc->sc_wd_timer = 0; | sc->sc_wd_timer = 0; | ||||
if (sc->sc_softled) | if (sc->sc_softled) | ||||
ath_led_event(sc, sc->sc_txrix); | ath_led_event(sc, sc->sc_txrix); | ||||
ATH_PCU_LOCK(sc); | ATH_PCU_LOCK(sc); | ||||
sc->sc_txproc_cnt--; | sc->sc_txproc_cnt--; | ||||
ATH_PCU_UNLOCK(sc); | ATH_PCU_UNLOCK(sc); | ||||
ATH_LOCK(sc); | ATH_LOCK(sc); | ||||
ath_power_restore_power_state(sc); | ath_power_restore_power_state(sc); | ||||
ATH_UNLOCK(sc); | ATH_UNLOCK(sc); | ||||
ath_tx_kick(sc); | ath_tx_kick(sc); | ||||
} | } | ||||
/* | /* | ||||
* Deferred processing of transmit interrupt. | * Deferred processing of transmit interrupt. | ||||
*/ | */ | ||||
static void | static void | ||||
ath_tx_proc(void *arg, int npending) | ath_tx_proc(void *arg, int npending) | ||||
{ | { | ||||
struct ath_softc *sc = arg; | struct ath_softc *sc = arg; | ||||
struct ifnet *ifp = sc->sc_ifp; | |||||
int i, nacked; | int i, nacked; | ||||
uint32_t txqs; | uint32_t txqs; | ||||
ATH_PCU_LOCK(sc); | ATH_PCU_LOCK(sc); | ||||
sc->sc_txproc_cnt++; | sc->sc_txproc_cnt++; | ||||
txqs = sc->sc_txq_active; | txqs = sc->sc_txq_active; | ||||
sc->sc_txq_active &= ~txqs; | sc->sc_txq_active &= ~txqs; | ||||
ATH_PCU_UNLOCK(sc); | ATH_PCU_UNLOCK(sc); | ||||
Show All 9 Lines | ath_tx_proc(void *arg, int npending) | ||||
*/ | */ | ||||
nacked = 0; | nacked = 0; | ||||
for (i = 0; i < HAL_NUM_TX_QUEUES; i++) | for (i = 0; i < HAL_NUM_TX_QUEUES; i++) | ||||
if (ATH_TXQ_SETUP(sc, i) && TXQACTIVE(txqs, i)) | if (ATH_TXQ_SETUP(sc, i) && TXQACTIVE(txqs, i)) | ||||
nacked += ath_tx_processq(sc, &sc->sc_txq[i], 1); | nacked += ath_tx_processq(sc, &sc->sc_txq[i], 1); | ||||
if (nacked) | if (nacked) | ||||
sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah); | sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah); | ||||
/* XXX check this inside of IF_LOCK? */ | |||||
IF_LOCK(&ifp->if_snd); | |||||
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; | |||||
IF_UNLOCK(&ifp->if_snd); | |||||
sc->sc_wd_timer = 0; | sc->sc_wd_timer = 0; | ||||
if (sc->sc_softled) | if (sc->sc_softled) | ||||
ath_led_event(sc, sc->sc_txrix); | ath_led_event(sc, sc->sc_txrix); | ||||
ATH_PCU_LOCK(sc); | ATH_PCU_LOCK(sc); | ||||
sc->sc_txproc_cnt--; | sc->sc_txproc_cnt--; | ||||
ATH_PCU_UNLOCK(sc); | ATH_PCU_UNLOCK(sc); | ||||
▲ Show 20 Lines • Show All 294 Lines • ▼ Show 20 Lines | for (ix = 0;; ix++) { | ||||
if (bf == NULL) { | if (bf == NULL) { | ||||
ATH_TXQ_UNLOCK(txq); | ATH_TXQ_UNLOCK(txq); | ||||
break; | break; | ||||
} | } | ||||
if (bf->bf_state.bfs_aggr) | if (bf->bf_state.bfs_aggr) | ||||
txq->axq_aggr_depth--; | txq->axq_aggr_depth--; | ||||
#ifdef ATH_DEBUG | #ifdef ATH_DEBUG | ||||
if (sc->sc_debug & ATH_DEBUG_RESET) { | if (sc->sc_debug & ATH_DEBUG_RESET) { | ||||
struct ieee80211com *ic = sc->sc_ifp->if_l2com; | struct ieee80211com *ic = &sc->sc_ic; | ||||
int status = 0; | int status = 0; | ||||
/* | /* | ||||
* EDMA operation has a TX completion FIFO | * EDMA operation has a TX completion FIFO | ||||
* separate from the TX descriptor, so this | * separate from the TX descriptor, so this | ||||
* method of checking the "completion" status | * method of checking the "completion" status | ||||
* is wrong. | * is wrong. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 128 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* Drain the transmit queues and reclaim resources. | * Drain the transmit queues and reclaim resources. | ||||
*/ | */ | ||||
void | void | ||||
ath_legacy_tx_drain(struct ath_softc *sc, ATH_RESET_TYPE reset_type) | ath_legacy_tx_drain(struct ath_softc *sc, ATH_RESET_TYPE reset_type) | ||||
{ | { | ||||
struct ath_hal *ah = sc->sc_ah; | struct ath_hal *ah = sc->sc_ah; | ||||
struct ifnet *ifp = sc->sc_ifp; | |||||
int i; | |||||
struct ath_buf *bf_last; | struct ath_buf *bf_last; | ||||
int i; | |||||
(void) ath_stoptxdma(sc); | (void) ath_stoptxdma(sc); | ||||
/* | /* | ||||
* Dump the queue contents | * Dump the queue contents | ||||
*/ | */ | ||||
for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { | for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { | ||||
/* | /* | ||||
Show All 35 Lines | #endif /* ATH_DEBUG */ | ||||
} | } | ||||
#ifdef ATH_DEBUG | #ifdef ATH_DEBUG | ||||
if (sc->sc_debug & ATH_DEBUG_RESET) { | if (sc->sc_debug & ATH_DEBUG_RESET) { | ||||
struct ath_buf *bf = TAILQ_FIRST(&sc->sc_bbuf); | struct ath_buf *bf = TAILQ_FIRST(&sc->sc_bbuf); | ||||
if (bf != NULL && bf->bf_m != NULL) { | if (bf != NULL && bf->bf_m != NULL) { | ||||
ath_printtxbuf(sc, bf, sc->sc_bhalq, 0, | ath_printtxbuf(sc, bf, sc->sc_bhalq, 0, | ||||
ath_hal_txprocdesc(ah, bf->bf_lastds, | ath_hal_txprocdesc(ah, bf->bf_lastds, | ||||
&bf->bf_status.ds_txstat) == HAL_OK); | &bf->bf_status.ds_txstat) == HAL_OK); | ||||
ieee80211_dump_pkt(ifp->if_l2com, | ieee80211_dump_pkt(&sc->sc_ic, | ||||
mtod(bf->bf_m, const uint8_t *), bf->bf_m->m_len, | mtod(bf->bf_m, const uint8_t *), bf->bf_m->m_len, | ||||
0, -1); | 0, -1); | ||||
} | } | ||||
} | } | ||||
#endif /* ATH_DEBUG */ | #endif /* ATH_DEBUG */ | ||||
IF_LOCK(&ifp->if_snd); | |||||
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; | |||||
IF_UNLOCK(&ifp->if_snd); | |||||
sc->sc_wd_timer = 0; | sc->sc_wd_timer = 0; | ||||
} | } | ||||
/* | /* | ||||
* Update internal state after a channel change. | * Update internal state after a channel change. | ||||
*/ | */ | ||||
static void | static void | ||||
ath_chan_change(struct ath_softc *sc, struct ieee80211_channel *chan) | ath_chan_change(struct ath_softc *sc, struct ieee80211_channel *chan) | ||||
Show All 14 Lines | |||||
* Set/change channels. If the channel is really being changed, | * Set/change channels. If the channel is really being changed, | ||||
* it's done by resetting the chip. To accomplish this we must | * it's done by resetting the chip. To accomplish this we must | ||||
* first cleanup any pending DMA, then restart stuff after a la | * first cleanup any pending DMA, then restart stuff after a la | ||||
* ath_init. | * ath_init. | ||||
*/ | */ | ||||
static int | static int | ||||
ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan) | ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan) | ||||
{ | { | ||||
struct ifnet *ifp = sc->sc_ifp; | struct ieee80211com *ic = &sc->sc_ic; | ||||
struct ieee80211com *ic = ifp->if_l2com; | |||||
struct ath_hal *ah = sc->sc_ah; | struct ath_hal *ah = sc->sc_ah; | ||||
int ret = 0; | int ret = 0; | ||||
/* Treat this as an interface reset */ | /* Treat this as an interface reset */ | ||||
ATH_PCU_UNLOCK_ASSERT(sc); | ATH_PCU_UNLOCK_ASSERT(sc); | ||||
ATH_UNLOCK_ASSERT(sc); | ATH_UNLOCK_ASSERT(sc); | ||||
/* (Try to) stop TX/RX from occuring */ | /* (Try to) stop TX/RX from occuring */ | ||||
▲ Show 20 Lines • Show All 118 Lines • ▼ Show 20 Lines | |||||
finish: | finish: | ||||
ATH_PCU_LOCK(sc); | ATH_PCU_LOCK(sc); | ||||
sc->sc_inreset_cnt--; | sc->sc_inreset_cnt--; | ||||
/* XXX only do this if sc_inreset_cnt == 0? */ | /* XXX only do this if sc_inreset_cnt == 0? */ | ||||
ath_hal_intrset(ah, sc->sc_imask); | ath_hal_intrset(ah, sc->sc_imask); | ||||
ATH_PCU_UNLOCK(sc); | ATH_PCU_UNLOCK(sc); | ||||
IF_LOCK(&ifp->if_snd); | |||||
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; | |||||
IF_UNLOCK(&ifp->if_snd); | |||||
ath_txrx_start(sc); | ath_txrx_start(sc); | ||||
/* XXX ath_start? */ | /* XXX ath_start? */ | ||||
return ret; | return ret; | ||||
} | } | ||||
/* | /* | ||||
* Periodically recalibrate the PHY to account | * Periodically recalibrate the PHY to account | ||||
* for temperature/environment changes. | * for temperature/environment changes. | ||||
*/ | */ | ||||
static void | static void | ||||
ath_calibrate(void *arg) | ath_calibrate(void *arg) | ||||
{ | { | ||||
struct ath_softc *sc = arg; | struct ath_softc *sc = arg; | ||||
struct ath_hal *ah = sc->sc_ah; | struct ath_hal *ah = sc->sc_ah; | ||||
struct ifnet *ifp = sc->sc_ifp; | struct ieee80211com *ic = &sc->sc_ic; | ||||
struct ieee80211com *ic = ifp->if_l2com; | |||||
HAL_BOOL longCal, isCalDone = AH_TRUE; | HAL_BOOL longCal, isCalDone = AH_TRUE; | ||||
HAL_BOOL aniCal, shortCal = AH_FALSE; | HAL_BOOL aniCal, shortCal = AH_FALSE; | ||||
int nextcal; | int nextcal; | ||||
ATH_LOCK_ASSERT(sc); | ATH_LOCK_ASSERT(sc); | ||||
/* | /* | ||||
* Force the hardware awake for ANI work. | * Force the hardware awake for ANI work. | ||||
▲ Show 20 Lines • Show All 109 Lines • ▼ Show 20 Lines | restart: | ||||
* Restore power state now that we're done. | * Restore power state now that we're done. | ||||
*/ | */ | ||||
ath_power_restore_power_state(sc); | ath_power_restore_power_state(sc); | ||||
} | } | ||||
static void | static void | ||||
ath_scan_start(struct ieee80211com *ic) | ath_scan_start(struct ieee80211com *ic) | ||||
{ | { | ||||
struct ifnet *ifp = ic->ic_ifp; | struct ath_softc *sc = ic->ic_softc; | ||||
struct ath_softc *sc = ifp->if_softc; | |||||
struct ath_hal *ah = sc->sc_ah; | struct ath_hal *ah = sc->sc_ah; | ||||
u_int32_t rfilt; | u_int32_t rfilt; | ||||
/* XXX calibration timer? */ | /* XXX calibration timer? */ | ||||
/* XXXGL: is constant ieee80211broadcastaddr a correct choice? */ | |||||
ATH_LOCK(sc); | ATH_LOCK(sc); | ||||
sc->sc_scanning = 1; | sc->sc_scanning = 1; | ||||
sc->sc_syncbeacon = 0; | sc->sc_syncbeacon = 0; | ||||
rfilt = ath_calcrxfilter(sc); | rfilt = ath_calcrxfilter(sc); | ||||
ATH_UNLOCK(sc); | ATH_UNLOCK(sc); | ||||
ATH_PCU_LOCK(sc); | ATH_PCU_LOCK(sc); | ||||
ath_hal_setrxfilter(ah, rfilt); | ath_hal_setrxfilter(ah, rfilt); | ||||
ath_hal_setassocid(ah, ifp->if_broadcastaddr, 0); | ath_hal_setassocid(ah, ieee80211broadcastaddr, 0); | ||||
ATH_PCU_UNLOCK(sc); | ATH_PCU_UNLOCK(sc); | ||||
DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s aid 0\n", | DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s aid 0\n", | ||||
__func__, rfilt, ether_sprintf(ifp->if_broadcastaddr)); | __func__, rfilt, ether_sprintf(ieee80211broadcastaddr)); | ||||
} | } | ||||
static void | static void | ||||
ath_scan_end(struct ieee80211com *ic) | ath_scan_end(struct ieee80211com *ic) | ||||
{ | { | ||||
struct ifnet *ifp = ic->ic_ifp; | struct ath_softc *sc = ic->ic_softc; | ||||
struct ath_softc *sc = ifp->if_softc; | |||||
struct ath_hal *ah = sc->sc_ah; | struct ath_hal *ah = sc->sc_ah; | ||||
u_int32_t rfilt; | u_int32_t rfilt; | ||||
ATH_LOCK(sc); | ATH_LOCK(sc); | ||||
sc->sc_scanning = 0; | sc->sc_scanning = 0; | ||||
rfilt = ath_calcrxfilter(sc); | rfilt = ath_calcrxfilter(sc); | ||||
ATH_UNLOCK(sc); | ATH_UNLOCK(sc); | ||||
Show All 23 Lines | |||||
* mode. (Eg, 40MHz frames in 20MHz mode.) Since TX and RX can and | * mode. (Eg, 40MHz frames in 20MHz mode.) Since TX and RX can and | ||||
* does occur in parallel, we need to make certain we've blocked | * does occur in parallel, we need to make certain we've blocked | ||||
* any further ongoing TX (and RX, that can cause raw TX) | * any further ongoing TX (and RX, that can cause raw TX) | ||||
* before we do this. | * before we do this. | ||||
*/ | */ | ||||
static void | static void | ||||
ath_update_chw(struct ieee80211com *ic) | ath_update_chw(struct ieee80211com *ic) | ||||
{ | { | ||||
struct ifnet *ifp = ic->ic_ifp; | struct ath_softc *sc = ic->ic_softc; | ||||
struct ath_softc *sc = ifp->if_softc; | |||||
DPRINTF(sc, ATH_DEBUG_STATE, "%s: called\n", __func__); | DPRINTF(sc, ATH_DEBUG_STATE, "%s: called\n", __func__); | ||||
ath_set_channel(ic); | ath_set_channel(ic); | ||||
} | } | ||||
#endif /* ATH_ENABLE_11N */ | #endif /* ATH_ENABLE_11N */ | ||||
static void | static void | ||||
ath_set_channel(struct ieee80211com *ic) | ath_set_channel(struct ieee80211com *ic) | ||||
{ | { | ||||
struct ifnet *ifp = ic->ic_ifp; | struct ath_softc *sc = ic->ic_softc; | ||||
struct ath_softc *sc = ifp->if_softc; | |||||
ATH_LOCK(sc); | ATH_LOCK(sc); | ||||
ath_power_set_power_state(sc, HAL_PM_AWAKE); | ath_power_set_power_state(sc, HAL_PM_AWAKE); | ||||
ATH_UNLOCK(sc); | ATH_UNLOCK(sc); | ||||
(void) ath_chan_set(sc, ic->ic_curchan); | (void) ath_chan_set(sc, ic->ic_curchan); | ||||
/* | /* | ||||
* If we are returning to our bss channel then mark state | * If we are returning to our bss channel then mark state | ||||
Show All 25 Lines | ath_isanyrunningvaps(struct ieee80211vap *this) | ||||
} | } | ||||
return 0; | return 0; | ||||
} | } | ||||
static int | static int | ||||
ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) | ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) | ||||
{ | { | ||||
struct ieee80211com *ic = vap->iv_ic; | struct ieee80211com *ic = vap->iv_ic; | ||||
struct ath_softc *sc = ic->ic_ifp->if_softc; | struct ath_softc *sc = ic->ic_softc; | ||||
struct ath_vap *avp = ATH_VAP(vap); | struct ath_vap *avp = ATH_VAP(vap); | ||||
struct ath_hal *ah = sc->sc_ah; | struct ath_hal *ah = sc->sc_ah; | ||||
struct ieee80211_node *ni = NULL; | struct ieee80211_node *ni = NULL; | ||||
int i, error, stamode; | int i, error, stamode; | ||||
u_int32_t rfilt; | u_int32_t rfilt; | ||||
int csa_run_transition = 0; | int csa_run_transition = 0; | ||||
enum ieee80211_state ostate = vap->iv_state; | enum ieee80211_state ostate = vap->iv_state; | ||||
▲ Show 20 Lines • Show All 319 Lines • ▼ Show 20 Lines | |||||
* compression when stations do not use crypto. We do | * compression when stations do not use crypto. We do | ||||
* it uniliaterally here; if crypto is employed this slot | * it uniliaterally here; if crypto is employed this slot | ||||
* will be reassigned. | * will be reassigned. | ||||
*/ | */ | ||||
static void | static void | ||||
ath_setup_stationkey(struct ieee80211_node *ni) | ath_setup_stationkey(struct ieee80211_node *ni) | ||||
{ | { | ||||
struct ieee80211vap *vap = ni->ni_vap; | struct ieee80211vap *vap = ni->ni_vap; | ||||
struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; | struct ath_softc *sc = vap->iv_ic->ic_softc; | ||||
ieee80211_keyix keyix, rxkeyix; | ieee80211_keyix keyix, rxkeyix; | ||||
/* XXX should take a locked ref to vap->iv_bss */ | /* XXX should take a locked ref to vap->iv_bss */ | ||||
if (!ath_key_alloc(vap, &ni->ni_ucastkey, &keyix, &rxkeyix)) { | if (!ath_key_alloc(vap, &ni->ni_ucastkey, &keyix, &rxkeyix)) { | ||||
/* | /* | ||||
* Key cache is full; we'll fall back to doing | * Key cache is full; we'll fall back to doing | ||||
* the more expensive lookup in software. Note | * the more expensive lookup in software. Note | ||||
* this also means no h/w compression. | * this also means no h/w compression. | ||||
Show All 16 Lines | |||||
* Note that we're called also on a re-associate, the isnew | * Note that we're called also on a re-associate, the isnew | ||||
* param tells us if this is the first time or not. | * param tells us if this is the first time or not. | ||||
*/ | */ | ||||
static void | static void | ||||
ath_newassoc(struct ieee80211_node *ni, int isnew) | ath_newassoc(struct ieee80211_node *ni, int isnew) | ||||
{ | { | ||||
struct ath_node *an = ATH_NODE(ni); | struct ath_node *an = ATH_NODE(ni); | ||||
struct ieee80211vap *vap = ni->ni_vap; | struct ieee80211vap *vap = ni->ni_vap; | ||||
struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; | struct ath_softc *sc = vap->iv_ic->ic_softc; | ||||
const struct ieee80211_txparam *tp = ni->ni_txparms; | const struct ieee80211_txparam *tp = ni->ni_txparms; | ||||
an->an_mcastrix = ath_tx_findrix(sc, tp->mcastrate); | an->an_mcastrix = ath_tx_findrix(sc, tp->mcastrate); | ||||
an->an_mgmtrix = ath_tx_findrix(sc, tp->mgmtrate); | an->an_mgmtrix = ath_tx_findrix(sc, tp->mgmtrate); | ||||
DPRINTF(sc, ATH_DEBUG_NODE, "%s: %6D: reassoc; isnew=%d, is_powersave=%d\n", | DPRINTF(sc, ATH_DEBUG_NODE, "%s: %6D: reassoc; isnew=%d, is_powersave=%d\n", | ||||
__func__, | __func__, | ||||
ni->ni_macaddr, | ni->ni_macaddr, | ||||
Show All 35 Lines | if (an->an_is_powersave) | ||||
ath_tx_node_wakeup(sc, an); | ath_tx_node_wakeup(sc, an); | ||||
} | } | ||||
} | } | ||||
static int | static int | ||||
ath_setregdomain(struct ieee80211com *ic, struct ieee80211_regdomain *reg, | ath_setregdomain(struct ieee80211com *ic, struct ieee80211_regdomain *reg, | ||||
int nchans, struct ieee80211_channel chans[]) | int nchans, struct ieee80211_channel chans[]) | ||||
{ | { | ||||
struct ath_softc *sc = ic->ic_ifp->if_softc; | struct ath_softc *sc = ic->ic_softc; | ||||
struct ath_hal *ah = sc->sc_ah; | struct ath_hal *ah = sc->sc_ah; | ||||
HAL_STATUS status; | HAL_STATUS status; | ||||
DPRINTF(sc, ATH_DEBUG_REGDOMAIN, | DPRINTF(sc, ATH_DEBUG_REGDOMAIN, | ||||
"%s: rd %u cc %u location %c%s\n", | "%s: rd %u cc %u location %c%s\n", | ||||
__func__, reg->regdomain, reg->country, reg->location, | __func__, reg->regdomain, reg->country, reg->location, | ||||
reg->ecm ? " ecm" : ""); | reg->ecm ? " ecm" : ""); | ||||
status = ath_hal_set_channels(ah, chans, nchans, | status = ath_hal_set_channels(ah, chans, nchans, | ||||
reg->country, reg->regdomain); | reg->country, reg->regdomain); | ||||
if (status != HAL_OK) { | if (status != HAL_OK) { | ||||
DPRINTF(sc, ATH_DEBUG_REGDOMAIN, "%s: failed, status %u\n", | DPRINTF(sc, ATH_DEBUG_REGDOMAIN, "%s: failed, status %u\n", | ||||
__func__, status); | __func__, status); | ||||
return EINVAL; /* XXX */ | return EINVAL; /* XXX */ | ||||
} | } | ||||
return 0; | return 0; | ||||
} | } | ||||
static void | static void | ||||
ath_getradiocaps(struct ieee80211com *ic, | ath_getradiocaps(struct ieee80211com *ic, | ||||
int maxchans, int *nchans, struct ieee80211_channel chans[]) | int maxchans, int *nchans, struct ieee80211_channel chans[]) | ||||
{ | { | ||||
struct ath_softc *sc = ic->ic_ifp->if_softc; | struct ath_softc *sc = ic->ic_softc; | ||||
struct ath_hal *ah = sc->sc_ah; | struct ath_hal *ah = sc->sc_ah; | ||||
DPRINTF(sc, ATH_DEBUG_REGDOMAIN, "%s: use rd %u cc %d\n", | DPRINTF(sc, ATH_DEBUG_REGDOMAIN, "%s: use rd %u cc %d\n", | ||||
__func__, SKU_DEBUG, CTRY_DEFAULT); | __func__, SKU_DEBUG, CTRY_DEFAULT); | ||||
/* XXX check return */ | /* XXX check return */ | ||||
(void) ath_hal_getchannels(ah, chans, maxchans, nchans, | (void) ath_hal_getchannels(ah, chans, maxchans, nchans, | ||||
HAL_MODE_ALL, CTRY_DEFAULT, SKU_DEBUG, AH_TRUE); | HAL_MODE_ALL, CTRY_DEFAULT, SKU_DEBUG, AH_TRUE); | ||||
} | } | ||||
static int | static int | ||||
ath_getchannels(struct ath_softc *sc) | ath_getchannels(struct ath_softc *sc) | ||||
{ | { | ||||
struct ifnet *ifp = sc->sc_ifp; | struct ieee80211com *ic = &sc->sc_ic; | ||||
struct ieee80211com *ic = ifp->if_l2com; | |||||
struct ath_hal *ah = sc->sc_ah; | struct ath_hal *ah = sc->sc_ah; | ||||
HAL_STATUS status; | HAL_STATUS status; | ||||
/* | /* | ||||
* Collect channel set based on EEPROM contents. | * Collect channel set based on EEPROM contents. | ||||
*/ | */ | ||||
status = ath_hal_init_channels(ah, ic->ic_channels, IEEE80211_CHAN_MAX, | status = ath_hal_init_channels(ah, ic->ic_channels, IEEE80211_CHAN_MAX, | ||||
&ic->ic_nchans, HAL_MODE_ALL, CTRY_DEFAULT, SKU_NONE, AH_TRUE); | &ic->ic_nchans, HAL_MODE_ALL, CTRY_DEFAULT, SKU_NONE, AH_TRUE); | ||||
▲ Show 20 Lines • Show All 145 Lines • ▼ Show 20 Lines | #define N(a) (sizeof(a)/sizeof(a[0])) | ||||
/* NB: caller is responsible for resetting rate control state */ | /* NB: caller is responsible for resetting rate control state */ | ||||
#undef N | #undef N | ||||
} | } | ||||
static void | static void | ||||
ath_watchdog(void *arg) | ath_watchdog(void *arg) | ||||
{ | { | ||||
struct ath_softc *sc = arg; | struct ath_softc *sc = arg; | ||||
struct ieee80211com *ic = &sc->sc_ic; | |||||
int do_reset = 0; | int do_reset = 0; | ||||
ATH_LOCK_ASSERT(sc); | ATH_LOCK_ASSERT(sc); | ||||
if (sc->sc_wd_timer != 0 && --sc->sc_wd_timer == 0) { | if (sc->sc_wd_timer != 0 && --sc->sc_wd_timer == 0) { | ||||
struct ifnet *ifp = sc->sc_ifp; | |||||
uint32_t hangs; | uint32_t hangs; | ||||
ath_power_set_power_state(sc, HAL_PM_AWAKE); | ath_power_set_power_state(sc, HAL_PM_AWAKE); | ||||
if (ath_hal_gethangstate(sc->sc_ah, 0xffff, &hangs) && | if (ath_hal_gethangstate(sc->sc_ah, 0xffff, &hangs) && | ||||
hangs != 0) { | hangs != 0) { | ||||
device_printf(sc->sc_dev, "%s hang detected (0x%x)\n", | device_printf(sc->sc_dev, "%s hang detected (0x%x)\n", | ||||
hangs & 0xff ? "bb" : "mac", hangs); | hangs & 0xff ? "bb" : "mac", hangs); | ||||
} else | } else | ||||
device_printf(sc->sc_dev, "device timeout\n"); | device_printf(sc->sc_dev, "device timeout\n"); | ||||
do_reset = 1; | do_reset = 1; | ||||
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); | counter_u64_add(ic->ic_oerrors, 1); | ||||
sc->sc_stats.ast_watchdog++; | sc->sc_stats.ast_watchdog++; | ||||
ath_power_restore_power_state(sc); | ath_power_restore_power_state(sc); | ||||
} | } | ||||
/* | /* | ||||
* We can't hold the lock across the ath_reset() call. | * We can't hold the lock across the ath_reset() call. | ||||
* | * | ||||
Show All 9 Lines | |||||
/* | /* | ||||
* Fetch the rate control statistics for the given node. | * Fetch the rate control statistics for the given node. | ||||
*/ | */ | ||||
static int | static int | ||||
ath_ioctl_ratestats(struct ath_softc *sc, struct ath_rateioctl *rs) | ath_ioctl_ratestats(struct ath_softc *sc, struct ath_rateioctl *rs) | ||||
{ | { | ||||
struct ath_node *an; | struct ath_node *an; | ||||
struct ieee80211com *ic = sc->sc_ifp->if_l2com; | struct ieee80211com *ic = &sc->sc_ic; | ||||
struct ieee80211_node *ni; | struct ieee80211_node *ni; | ||||
int error = 0; | int error = 0; | ||||
/* Perform a lookup on the given node */ | /* Perform a lookup on the given node */ | ||||
ni = ieee80211_find_node(&ic->ic_sta, rs->is_u.macaddr); | ni = ieee80211_find_node(&ic->ic_sta, rs->is_u.macaddr); | ||||
if (ni == NULL) { | if (ni == NULL) { | ||||
error = EINVAL; | error = EINVAL; | ||||
goto bad; | goto bad; | ||||
▲ Show 20 Lines • Show All 89 Lines • ▼ Show 20 Lines | bad: | ||||
if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL) | if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL) | ||||
free(indata, M_TEMP); | free(indata, M_TEMP); | ||||
if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL) | if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL) | ||||
free(outdata, M_TEMP); | free(outdata, M_TEMP); | ||||
return error; | return error; | ||||
} | } | ||||
#endif /* ATH_DIAGAPI */ | #endif /* ATH_DIAGAPI */ | ||||
static int | static void | ||||
ath_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) | ath_parent(struct ieee80211com *ic) | ||||
{ | { | ||||
#define IS_RUNNING(ifp) \ | struct ath_softc *sc = ic->ic_softc; | ||||
((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING)) | |||||
struct ath_softc *sc = ifp->if_softc; | |||||
struct ieee80211com *ic = ifp->if_l2com; | |||||
struct ifreq *ifr = (struct ifreq *)data; | |||||
const HAL_RATE_TABLE *rt; | |||||
int error = 0; | |||||
switch (cmd) { | ATH_LOCK(sc); | ||||
case SIOCSIFFLAGS: | if (ic->ic_nrunning > 0) { | ||||
if (IS_RUNNING(ifp)) { | |||||
/* | /* | ||||
* To avoid rescanning another access point, | * To avoid rescanning another access point, | ||||
* do not call ath_init() here. Instead, | * do not call ath_init() here. Instead, | ||||
* only reflect promisc mode settings. | * only reflect promisc mode settings. | ||||
*/ | */ | ||||
ATH_LOCK(sc); | if (sc->sc_running) { | ||||
ath_power_set_power_state(sc, HAL_PM_AWAKE); | ath_power_set_power_state(sc, HAL_PM_AWAKE); | ||||
ath_mode_init(sc); | ath_mode_init(sc); | ||||
ath_power_restore_power_state(sc); | ath_power_restore_power_state(sc); | ||||
ATH_UNLOCK(sc); | } else if (!sc->sc_invalid) { | ||||
} else if (ifp->if_flags & IFF_UP) { | |||||
/* | /* | ||||
* Beware of being called during attach/detach | * Beware of being called during attach/detach | ||||
* to reset promiscuous mode. In that case we | * to reset promiscuous mode. In that case we | ||||
* will still be marked UP but not RUNNING. | * will still be marked UP but not RUNNING. | ||||
* However trying to re-init the interface | * However trying to re-init the interface | ||||
* is the wrong thing to do as we've already | * is the wrong thing to do as we've already | ||||
* torn down much of our state. There's | * torn down much of our state. There's | ||||
* probably a better way to deal with this. | * probably a better way to deal with this. | ||||
*/ | */ | ||||
if (!sc->sc_invalid) | ATH_UNLOCK(sc); | ||||
ath_init(sc); /* XXX lose error */ | ath_init(sc); /* XXX lose error */ | ||||
return; | |||||
} | |||||
} else { | } else { | ||||
ATH_LOCK(sc); | ath_stop_locked(sc); | ||||
ath_stop_locked(ifp); | |||||
if (!sc->sc_invalid) | if (!sc->sc_invalid) | ||||
ath_power_setpower(sc, HAL_PM_FULL_SLEEP); | ath_power_setpower(sc, HAL_PM_FULL_SLEEP); | ||||
} | |||||
ATH_UNLOCK(sc); | ATH_UNLOCK(sc); | ||||
} | } | ||||
break; | |||||
case SIOCGIFMEDIA: | static int | ||||
case SIOCSIFMEDIA: | ath_ioctl(struct ieee80211com *ic, u_long cmd, void *data) | ||||
error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); | { | ||||
break; | struct ifreq *ifr = data; | ||||
case SIOCGATHSTATS: | struct ath_softc *sc = ic->ic_softc; | ||||
switch (cmd) { | |||||
case SIOCGATHSTATS: { | |||||
struct ieee80211vap *vap; | |||||
struct ifnet *ifp; | |||||
const HAL_RATE_TABLE *rt; | |||||
/* NB: embed these numbers to get a consistent view */ | /* NB: embed these numbers to get a consistent view */ | ||||
sc->sc_stats.ast_tx_packets = ifp->if_get_counter(ifp, | sc->sc_stats.ast_tx_packets = 0; | ||||
sc->sc_stats.ast_rx_packets = 0; | |||||
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { | |||||
ifp = vap->iv_ifp; | |||||
sc->sc_stats.ast_tx_packets += ifp->if_get_counter(ifp, | |||||
IFCOUNTER_OPACKETS); | IFCOUNTER_OPACKETS); | ||||
sc->sc_stats.ast_rx_packets = ifp->if_get_counter(ifp, | sc->sc_stats.ast_rx_packets += ifp->if_get_counter(ifp, | ||||
IFCOUNTER_IPACKETS); | IFCOUNTER_IPACKETS); | ||||
} | |||||
sc->sc_stats.ast_tx_rssi = ATH_RSSI(sc->sc_halstats.ns_avgtxrssi); | sc->sc_stats.ast_tx_rssi = ATH_RSSI(sc->sc_halstats.ns_avgtxrssi); | ||||
sc->sc_stats.ast_rx_rssi = ATH_RSSI(sc->sc_halstats.ns_avgrssi); | sc->sc_stats.ast_rx_rssi = ATH_RSSI(sc->sc_halstats.ns_avgrssi); | ||||
#ifdef IEEE80211_SUPPORT_TDMA | #ifdef IEEE80211_SUPPORT_TDMA | ||||
sc->sc_stats.ast_tdma_tsfadjp = TDMA_AVG(sc->sc_avgtsfdeltap); | sc->sc_stats.ast_tdma_tsfadjp = TDMA_AVG(sc->sc_avgtsfdeltap); | ||||
sc->sc_stats.ast_tdma_tsfadjm = TDMA_AVG(sc->sc_avgtsfdeltam); | sc->sc_stats.ast_tdma_tsfadjm = TDMA_AVG(sc->sc_avgtsfdeltam); | ||||
#endif | #endif | ||||
rt = sc->sc_currates; | rt = sc->sc_currates; | ||||
sc->sc_stats.ast_tx_rate = | sc->sc_stats.ast_tx_rate = | ||||
rt->info[sc->sc_txrix].dot11Rate &~ IEEE80211_RATE_BASIC; | rt->info[sc->sc_txrix].dot11Rate &~ IEEE80211_RATE_BASIC; | ||||
if (rt->info[sc->sc_txrix].phy & IEEE80211_T_HT) | if (rt->info[sc->sc_txrix].phy & IEEE80211_T_HT) | ||||
sc->sc_stats.ast_tx_rate |= IEEE80211_RATE_MCS; | sc->sc_stats.ast_tx_rate |= IEEE80211_RATE_MCS; | ||||
return copyout(&sc->sc_stats, | return copyout(&sc->sc_stats, | ||||
ifr->ifr_data, sizeof (sc->sc_stats)); | ifr->ifr_data, sizeof (sc->sc_stats)); | ||||
} | |||||
case SIOCGATHAGSTATS: | case SIOCGATHAGSTATS: | ||||
return copyout(&sc->sc_aggr_stats, | return copyout(&sc->sc_aggr_stats, | ||||
ifr->ifr_data, sizeof (sc->sc_aggr_stats)); | ifr->ifr_data, sizeof (sc->sc_aggr_stats)); | ||||
case SIOCZATHSTATS: | case SIOCZATHSTATS: { | ||||
int error; | |||||
error = priv_check(curthread, PRIV_DRIVER); | error = priv_check(curthread, PRIV_DRIVER); | ||||
if (error == 0) { | if (error == 0) { | ||||
memset(&sc->sc_stats, 0, sizeof(sc->sc_stats)); | memset(&sc->sc_stats, 0, sizeof(sc->sc_stats)); | ||||
memset(&sc->sc_aggr_stats, 0, | memset(&sc->sc_aggr_stats, 0, | ||||
sizeof(sc->sc_aggr_stats)); | sizeof(sc->sc_aggr_stats)); | ||||
memset(&sc->sc_intr_stats, 0, | memset(&sc->sc_intr_stats, 0, | ||||
sizeof(sc->sc_intr_stats)); | sizeof(sc->sc_intr_stats)); | ||||
} | } | ||||
break; | return (error); | ||||
} | |||||
#ifdef ATH_DIAGAPI | #ifdef ATH_DIAGAPI | ||||
case SIOCGATHDIAG: | case SIOCGATHDIAG: | ||||
error = ath_ioctl_diag(sc, (struct ath_diag *) ifr); | return (ath_ioctl_diag(sc, data)); | ||||
break; | |||||
case SIOCGATHPHYERR: | case SIOCGATHPHYERR: | ||||
error = ath_ioctl_phyerr(sc,(struct ath_diag*) ifr); | return (ath_ioctl_phyerr(sc, data)); | ||||
break; | |||||
#endif | #endif | ||||
case SIOCGATHSPECTRAL: | case SIOCGATHSPECTRAL: | ||||
error = ath_ioctl_spectral(sc,(struct ath_diag*) ifr); | return (ath_ioctl_spectral(sc, data)); | ||||
break; | |||||
case SIOCGATHNODERATESTATS: | case SIOCGATHNODERATESTATS: | ||||
error = ath_ioctl_ratestats(sc, (struct ath_rateioctl *) ifr); | return (ath_ioctl_ratestats(sc, data)); | ||||
break; | |||||
case SIOCGIFADDR: | |||||
error = ether_ioctl(ifp, cmd, data); | |||||
break; | |||||
default: | default: | ||||
error = EINVAL; | return (ENOTTY); | ||||
break; | |||||
} | } | ||||
return error; | |||||
#undef IS_RUNNING | |||||
} | } | ||||
/* | /* | ||||
* Announce various information on device/driver attach. | * Announce various information on device/driver attach. | ||||
*/ | */ | ||||
static void | static void | ||||
ath_announce(struct ath_softc *sc) | ath_announce(struct ath_softc *sc) | ||||
{ | { | ||||
Show All 24 Lines | ath_announce(struct ath_softc *sc) | ||||
if (sc->sc_mcastkey && bootverbose) | if (sc->sc_mcastkey && bootverbose) | ||||
device_printf(sc->sc_dev, "using multicast key search\n"); | device_printf(sc->sc_dev, "using multicast key search\n"); | ||||
} | } | ||||
static void | static void | ||||
ath_dfs_tasklet(void *p, int npending) | ath_dfs_tasklet(void *p, int npending) | ||||
{ | { | ||||
struct ath_softc *sc = (struct ath_softc *) p; | struct ath_softc *sc = (struct ath_softc *) p; | ||||
struct ifnet *ifp = sc->sc_ifp; | struct ieee80211com *ic = &sc->sc_ic; | ||||
struct ieee80211com *ic = ifp->if_l2com; | |||||
/* | /* | ||||
* If previous processing has found a radar event, | * If previous processing has found a radar event, | ||||
* signal this to the net80211 layer to begin DFS | * signal this to the net80211 layer to begin DFS | ||||
* processing. | * processing. | ||||
*/ | */ | ||||
if (ath_dfs_process_radar_event(sc, sc->sc_curchan)) { | if (ath_dfs_process_radar_event(sc, sc->sc_curchan)) { | ||||
/* DFS event found, initiate channel change */ | /* DFS event found, initiate channel change */ | ||||
Show All 15 Lines | |||||
* TX driver locks.) | * TX driver locks.) | ||||
*/ | */ | ||||
static void | static void | ||||
ath_node_powersave(struct ieee80211_node *ni, int enable) | ath_node_powersave(struct ieee80211_node *ni, int enable) | ||||
{ | { | ||||
#ifdef ATH_SW_PSQ | #ifdef ATH_SW_PSQ | ||||
struct ath_node *an = ATH_NODE(ni); | struct ath_node *an = ATH_NODE(ni); | ||||
struct ieee80211com *ic = ni->ni_ic; | struct ieee80211com *ic = ni->ni_ic; | ||||
struct ath_softc *sc = ic->ic_ifp->if_softc; | struct ath_softc *sc = ic->ic_softc; | ||||
struct ath_vap *avp = ATH_VAP(ni->ni_vap); | struct ath_vap *avp = ATH_VAP(ni->ni_vap); | ||||
/* XXX and no TXQ locks should be held here */ | /* XXX and no TXQ locks should be held here */ | ||||
DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, "%s: %6D: enable=%d\n", | DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, "%s: %6D: enable=%d\n", | ||||
__func__, | __func__, | ||||
ni->ni_macaddr, | ni->ni_macaddr, | ||||
":", | ":", | ||||
▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | |||||
* a packet entering the PSQ and a ps-poll being handled will | * a packet entering the PSQ and a ps-poll being handled will | ||||
* race, causing the TIM to be cleared and not re-set. | * race, causing the TIM to be cleared and not re-set. | ||||
*/ | */ | ||||
static int | static int | ||||
ath_node_set_tim(struct ieee80211_node *ni, int enable) | ath_node_set_tim(struct ieee80211_node *ni, int enable) | ||||
{ | { | ||||
#ifdef ATH_SW_PSQ | #ifdef ATH_SW_PSQ | ||||
struct ieee80211com *ic = ni->ni_ic; | struct ieee80211com *ic = ni->ni_ic; | ||||
struct ath_softc *sc = ic->ic_ifp->if_softc; | struct ath_softc *sc = ic->ic_softc; | ||||
struct ath_node *an = ATH_NODE(ni); | struct ath_node *an = ATH_NODE(ni); | ||||
struct ath_vap *avp = ATH_VAP(ni->ni_vap); | struct ath_vap *avp = ATH_VAP(ni->ni_vap); | ||||
int changed = 0; | int changed = 0; | ||||
ATH_TX_LOCK(sc); | ATH_TX_LOCK(sc); | ||||
an->an_stack_psq = enable; | an->an_stack_psq = enable; | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 188 Lines • ▼ Show 20 Lines | |||||
*/ | */ | ||||
static void | static void | ||||
ath_node_recv_pspoll(struct ieee80211_node *ni, struct mbuf *m) | ath_node_recv_pspoll(struct ieee80211_node *ni, struct mbuf *m) | ||||
{ | { | ||||
#ifdef ATH_SW_PSQ | #ifdef ATH_SW_PSQ | ||||
struct ath_node *an; | struct ath_node *an; | ||||
struct ath_vap *avp; | struct ath_vap *avp; | ||||
struct ieee80211com *ic = ni->ni_ic; | struct ieee80211com *ic = ni->ni_ic; | ||||
struct ath_softc *sc = ic->ic_ifp->if_softc; | struct ath_softc *sc = ic->ic_softc; | ||||
int tid; | int tid; | ||||
/* Just paranoia */ | /* Just paranoia */ | ||||
if (ni == NULL) | if (ni == NULL) | ||||
return; | return; | ||||
/* | /* | ||||
* Unassociated (temporary node) station. | * Unassociated (temporary node) station. | ||||
▲ Show 20 Lines • Show All 127 Lines • Show Last 20 Lines |