xref: /freebsd/sys/dev/ath/if_ath.c (revision 80d939bfba2fd5d3dd883c2ee01892f318fc2a2e)
15591b213SSam Leffler /*-
21f1d7810SSam Leffler  * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
35591b213SSam Leffler  * All rights reserved.
45591b213SSam Leffler  *
55591b213SSam Leffler  * Redistribution and use in source and binary forms, with or without
65591b213SSam Leffler  * modification, are permitted provided that the following conditions
75591b213SSam Leffler  * are met:
85591b213SSam Leffler  * 1. Redistributions of source code must retain the above copyright
95591b213SSam Leffler  *    notice, this list of conditions and the following disclaimer,
105591b213SSam Leffler  *    without modification.
115591b213SSam Leffler  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
125591b213SSam Leffler  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
135591b213SSam Leffler  *    redistribution must be conditioned upon including a substantially
145591b213SSam Leffler  *    similar Disclaimer requirement for further binary redistribution.
155591b213SSam Leffler  * 3. Neither the names of the above-listed copyright holders nor the names
165591b213SSam Leffler  *    of any contributors may be used to endorse or promote products derived
175591b213SSam Leffler  *    from this software without specific prior written permission.
185591b213SSam Leffler  *
195591b213SSam Leffler  * Alternatively, this software may be distributed under the terms of the
205591b213SSam Leffler  * GNU General Public License ("GPL") version 2 as published by the Free
215591b213SSam Leffler  * Software Foundation.
225591b213SSam Leffler  *
235591b213SSam Leffler  * NO WARRANTY
245591b213SSam Leffler  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
255591b213SSam Leffler  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
265591b213SSam Leffler  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
275591b213SSam Leffler  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
285591b213SSam Leffler  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
295591b213SSam Leffler  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
305591b213SSam Leffler  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
315591b213SSam Leffler  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
325591b213SSam Leffler  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
335591b213SSam Leffler  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
345591b213SSam Leffler  * THE POSSIBILITY OF SUCH DAMAGES.
355591b213SSam Leffler  */
365591b213SSam Leffler 
375591b213SSam Leffler #include <sys/cdefs.h>
385591b213SSam Leffler __FBSDID("$FreeBSD$");
395591b213SSam Leffler 
405591b213SSam Leffler /*
415591b213SSam Leffler  * Driver for the Atheros Wireless LAN controller.
425f3721d5SSam Leffler  *
435f3721d5SSam Leffler  * This software is derived from work of Atsushi Onoe; his contribution
445f3721d5SSam Leffler  * is greatly appreciated.
455591b213SSam Leffler  */
465591b213SSam Leffler 
475591b213SSam Leffler #include "opt_inet.h"
485591b213SSam Leffler 
495591b213SSam Leffler #include <sys/param.h>
505591b213SSam Leffler #include <sys/systm.h>
515591b213SSam Leffler #include <sys/sysctl.h>
525591b213SSam Leffler #include <sys/mbuf.h>
535591b213SSam Leffler #include <sys/malloc.h>
545591b213SSam Leffler #include <sys/lock.h>
555591b213SSam Leffler #include <sys/mutex.h>
565591b213SSam Leffler #include <sys/kernel.h>
575591b213SSam Leffler #include <sys/socket.h>
585591b213SSam Leffler #include <sys/sockio.h>
595591b213SSam Leffler #include <sys/errno.h>
605591b213SSam Leffler #include <sys/callout.h>
615591b213SSam Leffler #include <sys/bus.h>
625591b213SSam Leffler #include <sys/endian.h>
630bbf5441SSam Leffler #include <sys/kthread.h>
640bbf5441SSam Leffler #include <sys/taskqueue.h>
655591b213SSam Leffler 
665591b213SSam Leffler #include <machine/bus.h>
675591b213SSam Leffler 
685591b213SSam Leffler #include <net/if.h>
695591b213SSam Leffler #include <net/if_dl.h>
705591b213SSam Leffler #include <net/if_media.h>
71fc74a9f9SBrooks Davis #include <net/if_types.h>
725591b213SSam Leffler #include <net/if_arp.h>
735591b213SSam Leffler #include <net/ethernet.h>
745591b213SSam Leffler #include <net/if_llc.h>
755591b213SSam Leffler 
765591b213SSam Leffler #include <net80211/ieee80211_var.h>
775591b213SSam Leffler 
785591b213SSam Leffler #include <net/bpf.h>
795591b213SSam Leffler 
805591b213SSam Leffler #ifdef INET
815591b213SSam Leffler #include <netinet/in.h>
825591b213SSam Leffler #include <netinet/if_ether.h>
835591b213SSam Leffler #endif
845591b213SSam Leffler 
855591b213SSam Leffler #define	AR_DEBUG
865591b213SSam Leffler #include <dev/ath/if_athvar.h>
875591b213SSam Leffler #include <contrib/dev/ath/ah_desc.h>
88c42a7b7eSSam Leffler #include <contrib/dev/ath/ah_devid.h>		/* XXX for softled */
895591b213SSam Leffler 
9086e07743SSam Leffler #ifdef ATH_TX99_DIAG
9186e07743SSam Leffler #include <dev/ath/ath_tx99/ath_tx99.h>
9286e07743SSam Leffler #endif
9386e07743SSam Leffler 
94e8fd88a3SSam Leffler /* unaligned little endian access */
955591b213SSam Leffler #define LE_READ_2(p)							\
965591b213SSam Leffler 	((u_int16_t)							\
975591b213SSam Leffler 	 ((((u_int8_t *)(p))[0]      ) | (((u_int8_t *)(p))[1] <<  8)))
985591b213SSam Leffler #define LE_READ_4(p)							\
995591b213SSam Leffler 	((u_int32_t)							\
1005591b213SSam Leffler 	 ((((u_int8_t *)(p))[0]      ) | (((u_int8_t *)(p))[1] <<  8) |	\
1015591b213SSam Leffler 	  (((u_int8_t *)(p))[2] << 16) | (((u_int8_t *)(p))[3] << 24)))
1025591b213SSam Leffler 
1033e50ec2cSSam Leffler enum {
1043e50ec2cSSam Leffler 	ATH_LED_TX,
1053e50ec2cSSam Leffler 	ATH_LED_RX,
1063e50ec2cSSam Leffler 	ATH_LED_POLL,
1073e50ec2cSSam Leffler };
1083e50ec2cSSam Leffler 
1095591b213SSam Leffler static void	ath_init(void *);
110c42a7b7eSSam Leffler static void	ath_stop_locked(struct ifnet *);
1115591b213SSam Leffler static void	ath_stop(struct ifnet *);
1125591b213SSam Leffler static void	ath_start(struct ifnet *);
113c42a7b7eSSam Leffler static int	ath_reset(struct ifnet *);
1145591b213SSam Leffler static int	ath_media_change(struct ifnet *);
1155591b213SSam Leffler static void	ath_watchdog(struct ifnet *);
1165591b213SSam Leffler static int	ath_ioctl(struct ifnet *, u_long, caddr_t);
1175591b213SSam Leffler static void	ath_fatal_proc(void *, int);
1185591b213SSam Leffler static void	ath_rxorn_proc(void *, int);
1195591b213SSam Leffler static void	ath_bmiss_proc(void *, int);
120c42a7b7eSSam Leffler static int	ath_key_alloc(struct ieee80211com *,
121c1225b52SSam Leffler 			const struct ieee80211_key *,
122c1225b52SSam Leffler 			ieee80211_keyix *, ieee80211_keyix *);
123c42a7b7eSSam Leffler static int	ath_key_delete(struct ieee80211com *,
124c42a7b7eSSam Leffler 			const struct ieee80211_key *);
125c42a7b7eSSam Leffler static int	ath_key_set(struct ieee80211com *, const struct ieee80211_key *,
126c42a7b7eSSam Leffler 			const u_int8_t mac[IEEE80211_ADDR_LEN]);
127c42a7b7eSSam Leffler static void	ath_key_update_begin(struct ieee80211com *);
128c42a7b7eSSam Leffler static void	ath_key_update_end(struct ieee80211com *);
1295591b213SSam Leffler static void	ath_mode_init(struct ath_softc *);
130c42a7b7eSSam Leffler static void	ath_setslottime(struct ath_softc *);
131c42a7b7eSSam Leffler static void	ath_updateslot(struct ifnet *);
13280d2765fSSam Leffler static int	ath_beaconq_setup(struct ath_hal *);
1335591b213SSam Leffler static int	ath_beacon_alloc(struct ath_softc *, struct ieee80211_node *);
134c42a7b7eSSam Leffler static void	ath_beacon_setup(struct ath_softc *, struct ath_buf *);
1355591b213SSam Leffler static void	ath_beacon_proc(void *, int);
136c42a7b7eSSam Leffler static void	ath_bstuck_proc(void *, int);
1375591b213SSam Leffler static void	ath_beacon_free(struct ath_softc *);
1385591b213SSam Leffler static void	ath_beacon_config(struct ath_softc *);
139c42a7b7eSSam Leffler static void	ath_descdma_cleanup(struct ath_softc *sc,
140c42a7b7eSSam Leffler 			struct ath_descdma *, ath_bufhead *);
1415591b213SSam Leffler static int	ath_desc_alloc(struct ath_softc *);
1425591b213SSam Leffler static void	ath_desc_free(struct ath_softc *);
143c42a7b7eSSam Leffler static struct ieee80211_node *ath_node_alloc(struct ieee80211_node_table *);
144c42a7b7eSSam Leffler static void	ath_node_free(struct ieee80211_node *);
145c42a7b7eSSam Leffler static u_int8_t	ath_node_getrssi(const struct ieee80211_node *);
1465591b213SSam Leffler static int	ath_rxbuf_init(struct ath_softc *, struct ath_buf *);
147c42a7b7eSSam Leffler static void	ath_recv_mgmt(struct ieee80211com *ic, struct mbuf *m,
148c42a7b7eSSam Leffler 			struct ieee80211_node *ni,
149c42a7b7eSSam Leffler 			int subtype, int rssi, u_int32_t rstamp);
150c42a7b7eSSam Leffler static void	ath_setdefantenna(struct ath_softc *, u_int);
1515591b213SSam Leffler static void	ath_rx_proc(void *, int);
152c42a7b7eSSam Leffler static struct ath_txq *ath_txq_setup(struct ath_softc*, int qtype, int subtype);
153c42a7b7eSSam Leffler static int	ath_tx_setup(struct ath_softc *, int, int);
154c42a7b7eSSam Leffler static int	ath_wme_update(struct ieee80211com *);
155c42a7b7eSSam Leffler static void	ath_tx_cleanupq(struct ath_softc *, struct ath_txq *);
156c42a7b7eSSam Leffler static void	ath_tx_cleanup(struct ath_softc *);
1575591b213SSam Leffler static int	ath_tx_start(struct ath_softc *, struct ieee80211_node *,
1585591b213SSam Leffler 			     struct ath_buf *, struct mbuf *);
159c42a7b7eSSam Leffler static void	ath_tx_proc_q0(void *, int);
160c42a7b7eSSam Leffler static void	ath_tx_proc_q0123(void *, int);
1615591b213SSam Leffler static void	ath_tx_proc(void *, int);
1625591b213SSam Leffler static int	ath_chan_set(struct ath_softc *, struct ieee80211_channel *);
1635591b213SSam Leffler static void	ath_draintxq(struct ath_softc *);
1645591b213SSam Leffler static void	ath_stoprecv(struct ath_softc *);
1655591b213SSam Leffler static int	ath_startrecv(struct ath_softc *);
166c42a7b7eSSam Leffler static void	ath_chan_change(struct ath_softc *, struct ieee80211_channel *);
1675591b213SSam Leffler static void	ath_next_scan(void *);
1685591b213SSam Leffler static void	ath_calibrate(void *);
16945bbf62fSSam Leffler static int	ath_newstate(struct ieee80211com *, enum ieee80211_state, int);
170e8fd88a3SSam Leffler static void	ath_setup_stationkey(struct ieee80211_node *);
171e9962332SSam Leffler static void	ath_newassoc(struct ieee80211_node *, int);
172c42a7b7eSSam Leffler static int	ath_getchannels(struct ath_softc *, u_int cc,
173c42a7b7eSSam Leffler 			HAL_BOOL outdoor, HAL_BOOL xchanmode);
1743e50ec2cSSam Leffler static void	ath_led_event(struct ath_softc *, int);
175c42a7b7eSSam Leffler static void	ath_update_txpow(struct ath_softc *);
1765591b213SSam Leffler 
177c42a7b7eSSam Leffler static int	ath_rate_setup(struct ath_softc *, u_int mode);
1785591b213SSam Leffler static void	ath_setcurmode(struct ath_softc *, enum ieee80211_phymode);
179c42a7b7eSSam Leffler 
180c42a7b7eSSam Leffler static void	ath_sysctlattach(struct ath_softc *);
181c42a7b7eSSam Leffler static void	ath_bpfattach(struct ath_softc *);
182c42a7b7eSSam Leffler static void	ath_announce(struct ath_softc *);
1835591b213SSam Leffler 
1845591b213SSam Leffler SYSCTL_DECL(_hw_ath);
1855591b213SSam Leffler 
1865591b213SSam Leffler /* XXX validate sysctl values */
1875591b213SSam Leffler static	int ath_dwelltime = 200;		/* 5 channels/second */
1885591b213SSam Leffler SYSCTL_INT(_hw_ath, OID_AUTO, dwell, CTLFLAG_RW, &ath_dwelltime,
1895591b213SSam Leffler 	    0, "channel dwell time (ms) for AP/station scanning");
1905591b213SSam Leffler static	int ath_calinterval = 30;		/* calibrate every 30 secs */
1915591b213SSam Leffler SYSCTL_INT(_hw_ath, OID_AUTO, calibrate, CTLFLAG_RW, &ath_calinterval,
1925591b213SSam Leffler 	    0, "chip calibration interval (secs)");
19345cabbdcSSam Leffler static	int ath_outdoor = AH_TRUE;		/* outdoor operation */
19445cabbdcSSam Leffler SYSCTL_INT(_hw_ath, OID_AUTO, outdoor, CTLFLAG_RD, &ath_outdoor,
195c42a7b7eSSam Leffler 	    0, "outdoor operation");
1968c0370b7SSam Leffler TUNABLE_INT("hw.ath.outdoor", &ath_outdoor);
197c42a7b7eSSam Leffler static	int ath_xchanmode = AH_TRUE;		/* extended channel use */
198c42a7b7eSSam Leffler SYSCTL_INT(_hw_ath, OID_AUTO, xchanmode, CTLFLAG_RD, &ath_xchanmode,
199c42a7b7eSSam Leffler 	    0, "extended channel mode");
200c42a7b7eSSam Leffler TUNABLE_INT("hw.ath.xchanmode", &ath_xchanmode);
20145cabbdcSSam Leffler static	int ath_countrycode = CTRY_DEFAULT;	/* country code */
20245cabbdcSSam Leffler SYSCTL_INT(_hw_ath, OID_AUTO, countrycode, CTLFLAG_RD, &ath_countrycode,
20345cabbdcSSam Leffler 	    0, "country code");
2048c0370b7SSam Leffler TUNABLE_INT("hw.ath.countrycode", &ath_countrycode);
20545cabbdcSSam Leffler static	int ath_regdomain = 0;			/* regulatory domain */
20645cabbdcSSam Leffler SYSCTL_INT(_hw_ath, OID_AUTO, regdomain, CTLFLAG_RD, &ath_regdomain,
20745cabbdcSSam Leffler 	    0, "regulatory domain");
2085591b213SSam Leffler 
209e2d787faSSam Leffler static	int ath_rxbuf = ATH_RXBUF;		/* # rx buffers to allocate */
210e2d787faSSam Leffler SYSCTL_INT(_hw_ath, OID_AUTO, rxbuf, CTLFLAG_RD, &ath_rxbuf,
211e2d787faSSam Leffler 	    0, "rx buffers allocated");
212e2d787faSSam Leffler TUNABLE_INT("hw.ath.rxbuf", &ath_rxbuf);
213e2d787faSSam Leffler static	int ath_txbuf = ATH_TXBUF;		/* # tx buffers to allocate */
214e2d787faSSam Leffler SYSCTL_INT(_hw_ath, OID_AUTO, txbuf, CTLFLAG_RD, &ath_txbuf,
215e2d787faSSam Leffler 	    0, "tx buffers allocated");
216e2d787faSSam Leffler TUNABLE_INT("hw.ath.txbuf", &ath_txbuf);
217e2d787faSSam Leffler 
2185591b213SSam Leffler #ifdef AR_DEBUG
219c42a7b7eSSam Leffler static	int ath_debug = 0;
2205591b213SSam Leffler SYSCTL_INT(_hw_ath, OID_AUTO, debug, CTLFLAG_RW, &ath_debug,
2215591b213SSam Leffler 	    0, "control debugging printfs");
222f3be7956SSam Leffler TUNABLE_INT("hw.ath.debug", &ath_debug);
223e325e530SSam Leffler enum {
224e325e530SSam Leffler 	ATH_DEBUG_XMIT		= 0x00000001,	/* basic xmit operation */
225e325e530SSam Leffler 	ATH_DEBUG_XMIT_DESC	= 0x00000002,	/* xmit descriptors */
226e325e530SSam Leffler 	ATH_DEBUG_RECV		= 0x00000004,	/* basic recv operation */
227e325e530SSam Leffler 	ATH_DEBUG_RECV_DESC	= 0x00000008,	/* recv descriptors */
228e325e530SSam Leffler 	ATH_DEBUG_RATE		= 0x00000010,	/* rate control */
229e325e530SSam Leffler 	ATH_DEBUG_RESET		= 0x00000020,	/* reset processing */
230e325e530SSam Leffler 	ATH_DEBUG_MODE		= 0x00000040,	/* mode init/setup */
231e325e530SSam Leffler 	ATH_DEBUG_BEACON 	= 0x00000080,	/* beacon handling */
232e325e530SSam Leffler 	ATH_DEBUG_WATCHDOG 	= 0x00000100,	/* watchdog timeout */
233e325e530SSam Leffler 	ATH_DEBUG_INTR		= 0x00001000,	/* ISR */
234e325e530SSam Leffler 	ATH_DEBUG_TX_PROC	= 0x00002000,	/* tx ISR proc */
235e325e530SSam Leffler 	ATH_DEBUG_RX_PROC	= 0x00004000,	/* rx ISR proc */
236e325e530SSam Leffler 	ATH_DEBUG_BEACON_PROC	= 0x00008000,	/* beacon ISR proc */
237e325e530SSam Leffler 	ATH_DEBUG_CALIBRATE	= 0x00010000,	/* periodic calibration */
238c42a7b7eSSam Leffler 	ATH_DEBUG_KEYCACHE	= 0x00020000,	/* key cache management */
239c42a7b7eSSam Leffler 	ATH_DEBUG_STATE		= 0x00040000,	/* 802.11 state transitions */
240c42a7b7eSSam Leffler 	ATH_DEBUG_NODE		= 0x00080000,	/* node management */
2413e50ec2cSSam Leffler 	ATH_DEBUG_LED		= 0x00100000,	/* led management */
242c42a7b7eSSam Leffler 	ATH_DEBUG_FATAL		= 0x80000000,	/* fatal errors */
243e325e530SSam Leffler 	ATH_DEBUG_ANY		= 0xffffffff
244e325e530SSam Leffler };
245c42a7b7eSSam Leffler #define	IFF_DUMPPKTS(sc, m) \
2460a1b94c4SSam Leffler 	((sc->sc_debug & (m)) || \
247fc74a9f9SBrooks Davis 	    (sc->sc_ifp->if_flags & (IFF_DEBUG|IFF_LINK2)) == (IFF_DEBUG|IFF_LINK2))
248c42a7b7eSSam Leffler #define	DPRINTF(sc, m, fmt, ...) do {				\
2490a1b94c4SSam Leffler 	if (sc->sc_debug & (m))					\
250c42a7b7eSSam Leffler 		printf(fmt, __VA_ARGS__);			\
251c42a7b7eSSam Leffler } while (0)
252c42a7b7eSSam Leffler #define	KEYPRINTF(sc, ix, hk, mac) do {				\
253c42a7b7eSSam Leffler 	if (sc->sc_debug & ATH_DEBUG_KEYCACHE)			\
254c42a7b7eSSam Leffler 		ath_keyprint(__func__, ix, hk, mac);		\
255c42a7b7eSSam Leffler } while (0)
256c42a7b7eSSam Leffler static	void ath_printrxbuf(struct ath_buf *bf, int);
257c42a7b7eSSam Leffler static	void ath_printtxbuf(struct ath_buf *bf, int);
2585591b213SSam Leffler #else
259c42a7b7eSSam Leffler #define	IFF_DUMPPKTS(sc, m) \
260fc74a9f9SBrooks Davis 	((sc->sc_ifp->if_flags & (IFF_DEBUG|IFF_LINK2)) == (IFF_DEBUG|IFF_LINK2))
261c42a7b7eSSam Leffler #define	DPRINTF(m, fmt, ...)
262c42a7b7eSSam Leffler #define	KEYPRINTF(sc, k, ix, mac)
2635591b213SSam Leffler #endif
2645591b213SSam Leffler 
265c42a7b7eSSam Leffler MALLOC_DEFINE(M_ATHDEV, "athdev", "ath driver dma buffers");
266c42a7b7eSSam Leffler 
2675591b213SSam Leffler int
2685591b213SSam Leffler ath_attach(u_int16_t devid, struct ath_softc *sc)
2695591b213SSam Leffler {
270fc74a9f9SBrooks Davis 	struct ifnet *ifp;
2715591b213SSam Leffler 	struct ieee80211com *ic = &sc->sc_ic;
272fc74a9f9SBrooks Davis 	struct ath_hal *ah = NULL;
2735591b213SSam Leffler 	HAL_STATUS status;
274c42a7b7eSSam Leffler 	int error = 0, i;
2755591b213SSam Leffler 
276c42a7b7eSSam Leffler 	DPRINTF(sc, ATH_DEBUG_ANY, "%s: devid 0x%x\n", __func__, devid);
2775591b213SSam Leffler 
278fc74a9f9SBrooks Davis 	ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
279fc74a9f9SBrooks Davis 	if (ifp == NULL) {
280fc74a9f9SBrooks Davis 		device_printf(sc->sc_dev, "can not if_alloc()\n");
281fc74a9f9SBrooks Davis 		error = ENOSPC;
282fc74a9f9SBrooks Davis 		goto bad;
283fc74a9f9SBrooks Davis 	}
284fc74a9f9SBrooks Davis 
2855591b213SSam Leffler 	/* set these up early for if_printf use */
2869bf40edeSBrooks Davis 	if_initname(ifp, device_get_name(sc->sc_dev),
2879bf40edeSBrooks Davis 		device_get_unit(sc->sc_dev));
2885591b213SSam Leffler 
2895591b213SSam Leffler 	ah = ath_hal_attach(devid, sc, sc->sc_st, sc->sc_sh, &status);
2905591b213SSam Leffler 	if (ah == NULL) {
2915591b213SSam Leffler 		if_printf(ifp, "unable to attach hardware; HAL status %u\n",
2925591b213SSam Leffler 			status);
2935591b213SSam Leffler 		error = ENXIO;
2945591b213SSam Leffler 		goto bad;
2955591b213SSam Leffler 	}
29685bdc65aSSam Leffler 	if (ah->ah_abi != HAL_ABI_VERSION) {
297c42a7b7eSSam Leffler 		if_printf(ifp, "HAL ABI mismatch detected "
298c42a7b7eSSam Leffler 			"(HAL:0x%x != driver:0x%x)\n",
29985bdc65aSSam Leffler 			ah->ah_abi, HAL_ABI_VERSION);
30085bdc65aSSam Leffler 		error = ENXIO;
30185bdc65aSSam Leffler 		goto bad;
30285bdc65aSSam Leffler 	}
3035591b213SSam Leffler 	sc->sc_ah = ah;
304b58b3803SSam Leffler 	sc->sc_invalid = 0;	/* ready to go, enable interrupt handling */
3055591b213SSam Leffler 
3065591b213SSam Leffler 	/*
307c42a7b7eSSam Leffler 	 * Check if the MAC has multi-rate retry support.
308c42a7b7eSSam Leffler 	 * We do this by trying to setup a fake extended
309c42a7b7eSSam Leffler 	 * descriptor.  MAC's that don't have support will
310c42a7b7eSSam Leffler 	 * return false w/o doing anything.  MAC's that do
311c42a7b7eSSam Leffler 	 * support it will return true w/o doing anything.
312c42a7b7eSSam Leffler 	 */
313c42a7b7eSSam Leffler 	sc->sc_mrretry = ath_hal_setupxtxdesc(ah, NULL, 0,0, 0,0, 0,0);
314c42a7b7eSSam Leffler 
315c42a7b7eSSam Leffler 	/*
316c42a7b7eSSam Leffler 	 * Check if the device has hardware counters for PHY
317c42a7b7eSSam Leffler 	 * errors.  If so we need to enable the MIB interrupt
318c42a7b7eSSam Leffler 	 * so we can act on stat triggers.
319c42a7b7eSSam Leffler 	 */
320c42a7b7eSSam Leffler 	if (ath_hal_hwphycounters(ah))
321c42a7b7eSSam Leffler 		sc->sc_needmib = 1;
322c42a7b7eSSam Leffler 
323c42a7b7eSSam Leffler 	/*
324c42a7b7eSSam Leffler 	 * Get the hardware key cache size.
325c42a7b7eSSam Leffler 	 */
326c42a7b7eSSam Leffler 	sc->sc_keymax = ath_hal_keycachesize(ah);
327e8fd88a3SSam Leffler 	if (sc->sc_keymax > ATH_KEYMAX) {
328e8fd88a3SSam Leffler 		if_printf(ifp, "Warning, using only %u of %u key cache slots\n",
329e8fd88a3SSam Leffler 			ATH_KEYMAX, sc->sc_keymax);
330e8fd88a3SSam Leffler 		sc->sc_keymax = ATH_KEYMAX;
331c42a7b7eSSam Leffler 	}
332c42a7b7eSSam Leffler 	/*
333c42a7b7eSSam Leffler 	 * Reset the key cache since some parts do not
334c42a7b7eSSam Leffler 	 * reset the contents on initial power up.
335c42a7b7eSSam Leffler 	 */
336c42a7b7eSSam Leffler 	for (i = 0; i < sc->sc_keymax; i++)
337c42a7b7eSSam Leffler 		ath_hal_keyreset(ah, i);
338c42a7b7eSSam Leffler 	/*
339c42a7b7eSSam Leffler 	 * Mark key cache slots associated with global keys
340c42a7b7eSSam Leffler 	 * as in use.  If we knew TKIP was not to be used we
341c42a7b7eSSam Leffler 	 * could leave the +32, +64, and +32+64 slots free.
342c42a7b7eSSam Leffler 	 * XXX only for splitmic.
343c42a7b7eSSam Leffler 	 */
344c42a7b7eSSam Leffler 	for (i = 0; i < IEEE80211_WEP_NKID; i++) {
345c42a7b7eSSam Leffler 		setbit(sc->sc_keymap, i);
346c42a7b7eSSam Leffler 		setbit(sc->sc_keymap, i+32);
347c42a7b7eSSam Leffler 		setbit(sc->sc_keymap, i+64);
348c42a7b7eSSam Leffler 		setbit(sc->sc_keymap, i+32+64);
349c42a7b7eSSam Leffler 	}
350c42a7b7eSSam Leffler 
351c42a7b7eSSam Leffler 	/*
3525591b213SSam Leffler 	 * Collect the channel list using the default country
3535591b213SSam Leffler 	 * code and including outdoor channels.  The 802.11 layer
35445cabbdcSSam Leffler 	 * is resposible for filtering this list based on settings
35545cabbdcSSam Leffler 	 * like the phy mode.
3565591b213SSam Leffler 	 */
357c42a7b7eSSam Leffler 	error = ath_getchannels(sc, ath_countrycode,
358c42a7b7eSSam Leffler 			ath_outdoor, ath_xchanmode);
3595591b213SSam Leffler 	if (error != 0)
3605591b213SSam Leffler 		goto bad;
3615591b213SSam Leffler 
3625591b213SSam Leffler 	/*
3635591b213SSam Leffler 	 * Setup rate tables for all potential media types.
3645591b213SSam Leffler 	 */
3655591b213SSam Leffler 	ath_rate_setup(sc, IEEE80211_MODE_11A);
3665591b213SSam Leffler 	ath_rate_setup(sc, IEEE80211_MODE_11B);
3675591b213SSam Leffler 	ath_rate_setup(sc, IEEE80211_MODE_11G);
368c42a7b7eSSam Leffler 	ath_rate_setup(sc, IEEE80211_MODE_TURBO_A);
369c42a7b7eSSam Leffler 	ath_rate_setup(sc, IEEE80211_MODE_TURBO_G);
370c42a7b7eSSam Leffler 	/* NB: setup here so ath_rate_update is happy */
371c42a7b7eSSam Leffler 	ath_setcurmode(sc, IEEE80211_MODE_11A);
3725591b213SSam Leffler 
373c42a7b7eSSam Leffler 	/*
374c42a7b7eSSam Leffler 	 * Allocate tx+rx descriptors and populate the lists.
375c42a7b7eSSam Leffler 	 */
3765591b213SSam Leffler 	error = ath_desc_alloc(sc);
3775591b213SSam Leffler 	if (error != 0) {
3785591b213SSam Leffler 		if_printf(ifp, "failed to allocate descriptors: %d\n", error);
3795591b213SSam Leffler 		goto bad;
3805591b213SSam Leffler 	}
381e383b240SSam Leffler 	callout_init(&sc->sc_scan_ch, debug_mpsafenet ? CALLOUT_MPSAFE : 0);
3822274d8c8SSam Leffler 	callout_init(&sc->sc_cal_ch, CALLOUT_MPSAFE);
3835591b213SSam Leffler 
384f0b2a0beSSam Leffler 	ATH_TXBUF_LOCK_INIT(sc);
3855591b213SSam Leffler 
3860bbf5441SSam Leffler 	sc->sc_tq = taskqueue_create("ath_taskq", M_NOWAIT,
3870bbf5441SSam Leffler 		taskqueue_thread_enqueue, &sc->sc_tq);
3880bbf5441SSam Leffler 	taskqueue_start_threads(&sc->sc_tq, 1, PI_NET,
3890bbf5441SSam Leffler 		"%s taskq", ifp->if_xname);
3900bbf5441SSam Leffler 
3915591b213SSam Leffler 	TASK_INIT(&sc->sc_rxtask, 0, ath_rx_proc, sc);
3925591b213SSam Leffler 	TASK_INIT(&sc->sc_rxorntask, 0, ath_rxorn_proc, sc);
3935591b213SSam Leffler 	TASK_INIT(&sc->sc_fataltask, 0, ath_fatal_proc, sc);
3945591b213SSam Leffler 	TASK_INIT(&sc->sc_bmisstask, 0, ath_bmiss_proc, sc);
395c42a7b7eSSam Leffler 	TASK_INIT(&sc->sc_bstucktask, 0, ath_bstuck_proc, sc);
3965591b213SSam Leffler 
3975591b213SSam Leffler 	/*
398c42a7b7eSSam Leffler 	 * Allocate hardware transmit queues: one queue for
399c42a7b7eSSam Leffler 	 * beacon frames and one data queue for each QoS
400c42a7b7eSSam Leffler 	 * priority.  Note that the hal handles reseting
401c42a7b7eSSam Leffler 	 * these queues at the needed time.
402c42a7b7eSSam Leffler 	 *
403c42a7b7eSSam Leffler 	 * XXX PS-Poll
4045591b213SSam Leffler 	 */
40580d2765fSSam Leffler 	sc->sc_bhalq = ath_beaconq_setup(ah);
4065591b213SSam Leffler 	if (sc->sc_bhalq == (u_int) -1) {
4075591b213SSam Leffler 		if_printf(ifp, "unable to setup a beacon xmit queue!\n");
408c42a7b7eSSam Leffler 		error = EIO;
409b28b4653SSam Leffler 		goto bad2;
4105591b213SSam Leffler 	}
411c42a7b7eSSam Leffler 	sc->sc_cabq = ath_txq_setup(sc, HAL_TX_QUEUE_CAB, 0);
412c42a7b7eSSam Leffler 	if (sc->sc_cabq == NULL) {
413c42a7b7eSSam Leffler 		if_printf(ifp, "unable to setup CAB xmit queue!\n");
414c42a7b7eSSam Leffler 		error = EIO;
415c42a7b7eSSam Leffler 		goto bad2;
416c42a7b7eSSam Leffler 	}
417c42a7b7eSSam Leffler 	/* NB: insure BK queue is the lowest priority h/w queue */
418c42a7b7eSSam Leffler 	if (!ath_tx_setup(sc, WME_AC_BK, HAL_WME_AC_BK)) {
419c42a7b7eSSam Leffler 		if_printf(ifp, "unable to setup xmit queue for %s traffic!\n",
420c42a7b7eSSam Leffler 			ieee80211_wme_acnames[WME_AC_BK]);
421c42a7b7eSSam Leffler 		error = EIO;
422c42a7b7eSSam Leffler 		goto bad2;
423c42a7b7eSSam Leffler 	}
424c42a7b7eSSam Leffler 	if (!ath_tx_setup(sc, WME_AC_BE, HAL_WME_AC_BE) ||
425c42a7b7eSSam Leffler 	    !ath_tx_setup(sc, WME_AC_VI, HAL_WME_AC_VI) ||
426c42a7b7eSSam Leffler 	    !ath_tx_setup(sc, WME_AC_VO, HAL_WME_AC_VO)) {
427c42a7b7eSSam Leffler 		/*
428c42a7b7eSSam Leffler 		 * Not enough hardware tx queues to properly do WME;
429c42a7b7eSSam Leffler 		 * just punt and assign them all to the same h/w queue.
430c42a7b7eSSam Leffler 		 * We could do a better job of this if, for example,
431c42a7b7eSSam Leffler 		 * we allocate queues when we switch from station to
432c42a7b7eSSam Leffler 		 * AP mode.
433c42a7b7eSSam Leffler 		 */
434c42a7b7eSSam Leffler 		if (sc->sc_ac2q[WME_AC_VI] != NULL)
435c42a7b7eSSam Leffler 			ath_tx_cleanupq(sc, sc->sc_ac2q[WME_AC_VI]);
436c42a7b7eSSam Leffler 		if (sc->sc_ac2q[WME_AC_BE] != NULL)
437c42a7b7eSSam Leffler 			ath_tx_cleanupq(sc, sc->sc_ac2q[WME_AC_BE]);
438c42a7b7eSSam Leffler 		sc->sc_ac2q[WME_AC_BE] = sc->sc_ac2q[WME_AC_BK];
439c42a7b7eSSam Leffler 		sc->sc_ac2q[WME_AC_VI] = sc->sc_ac2q[WME_AC_BK];
440c42a7b7eSSam Leffler 		sc->sc_ac2q[WME_AC_VO] = sc->sc_ac2q[WME_AC_BK];
441c42a7b7eSSam Leffler 	}
442c42a7b7eSSam Leffler 
443c42a7b7eSSam Leffler 	/*
444c42a7b7eSSam Leffler 	 * Special case certain configurations.  Note the
445c42a7b7eSSam Leffler 	 * CAB queue is handled by these specially so don't
446c42a7b7eSSam Leffler 	 * include them when checking the txq setup mask.
447c42a7b7eSSam Leffler 	 */
448c42a7b7eSSam Leffler 	switch (sc->sc_txqsetup &~ (1<<sc->sc_cabq->axq_qnum)) {
449c42a7b7eSSam Leffler 	case 0x01:
450c42a7b7eSSam Leffler 		TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc_q0, sc);
451c42a7b7eSSam Leffler 		break;
452c42a7b7eSSam Leffler 	case 0x0f:
453c42a7b7eSSam Leffler 		TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc_q0123, sc);
454c42a7b7eSSam Leffler 		break;
455c42a7b7eSSam Leffler 	default:
456c42a7b7eSSam Leffler 		TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc, sc);
457c42a7b7eSSam Leffler 		break;
458c42a7b7eSSam Leffler 	}
459c42a7b7eSSam Leffler 
460c42a7b7eSSam Leffler 	/*
461c42a7b7eSSam Leffler 	 * Setup rate control.  Some rate control modules
462c42a7b7eSSam Leffler 	 * call back to change the anntena state so expose
463c42a7b7eSSam Leffler 	 * the necessary entry points.
464c42a7b7eSSam Leffler 	 * XXX maybe belongs in struct ath_ratectrl?
465c42a7b7eSSam Leffler 	 */
466c42a7b7eSSam Leffler 	sc->sc_setdefantenna = ath_setdefantenna;
467c42a7b7eSSam Leffler 	sc->sc_rc = ath_rate_attach(sc);
468c42a7b7eSSam Leffler 	if (sc->sc_rc == NULL) {
469c42a7b7eSSam Leffler 		error = EIO;
470c42a7b7eSSam Leffler 		goto bad2;
471c42a7b7eSSam Leffler 	}
472c42a7b7eSSam Leffler 
4733e50ec2cSSam Leffler 	sc->sc_blinking = 0;
474c42a7b7eSSam Leffler 	sc->sc_ledstate = 1;
4753e50ec2cSSam Leffler 	sc->sc_ledon = 0;			/* low true */
4763e50ec2cSSam Leffler 	sc->sc_ledidle = (2700*hz)/1000;	/* 2.7sec */
4773e50ec2cSSam Leffler 	callout_init(&sc->sc_ledtimer, CALLOUT_MPSAFE);
478c42a7b7eSSam Leffler 	/*
479c42a7b7eSSam Leffler 	 * Auto-enable soft led processing for IBM cards and for
480c42a7b7eSSam Leffler 	 * 5211 minipci cards.  Users can also manually enable/disable
481c42a7b7eSSam Leffler 	 * support with a sysctl.
482c42a7b7eSSam Leffler 	 */
483c42a7b7eSSam Leffler 	sc->sc_softled = (devid == AR5212_DEVID_IBM || devid == AR5211_DEVID);
484c42a7b7eSSam Leffler 	if (sc->sc_softled) {
485c42a7b7eSSam Leffler 		ath_hal_gpioCfgOutput(ah, sc->sc_ledpin);
4863e50ec2cSSam Leffler 		ath_hal_gpioset(ah, sc->sc_ledpin, !sc->sc_ledon);
487c42a7b7eSSam Leffler 	}
4885591b213SSam Leffler 
4895591b213SSam Leffler 	ifp->if_softc = sc;
4905591b213SSam Leffler 	ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST;
4915591b213SSam Leffler 	ifp->if_start = ath_start;
4925591b213SSam Leffler 	ifp->if_watchdog = ath_watchdog;
4935591b213SSam Leffler 	ifp->if_ioctl = ath_ioctl;
4945591b213SSam Leffler 	ifp->if_init = ath_init;
495154b8df2SMax Laier 	IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
496154b8df2SMax Laier 	ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN;
497154b8df2SMax Laier 	IFQ_SET_READY(&ifp->if_snd);
4985591b213SSam Leffler 
499c42a7b7eSSam Leffler 	ic->ic_ifp = ifp;
500c42a7b7eSSam Leffler 	ic->ic_reset = ath_reset;
5015591b213SSam Leffler 	ic->ic_newassoc = ath_newassoc;
502c42a7b7eSSam Leffler 	ic->ic_updateslot = ath_updateslot;
503c42a7b7eSSam Leffler 	ic->ic_wme.wme_update = ath_wme_update;
5045591b213SSam Leffler 	/* XXX not right but it's not used anywhere important */
5055591b213SSam Leffler 	ic->ic_phytype = IEEE80211_T_OFDM;
5065591b213SSam Leffler 	ic->ic_opmode = IEEE80211_M_STA;
507c42a7b7eSSam Leffler 	ic->ic_caps =
508c42a7b7eSSam Leffler 		  IEEE80211_C_IBSS		/* ibss, nee adhoc, mode */
509fe32c3efSSam Leffler 		| IEEE80211_C_HOSTAP		/* hostap mode */
510fe32c3efSSam Leffler 		| IEEE80211_C_MONITOR		/* monitor mode */
5117a04dc27SSam Leffler 		| IEEE80211_C_AHDEMO		/* adhoc demo mode */
512fe32c3efSSam Leffler 		| IEEE80211_C_SHPREAMBLE	/* short preamble supported */
513c42a7b7eSSam Leffler 		| IEEE80211_C_SHSLOT		/* short slot time supported */
514c42a7b7eSSam Leffler 		| IEEE80211_C_WPA		/* capable of WPA1+WPA2 */
51501e7e035SSam Leffler 		;
516c42a7b7eSSam Leffler 	/*
517c42a7b7eSSam Leffler 	 * Query the hal to figure out h/w crypto support.
518c42a7b7eSSam Leffler 	 */
519c42a7b7eSSam Leffler 	if (ath_hal_ciphersupported(ah, HAL_CIPHER_WEP))
520c42a7b7eSSam Leffler 		ic->ic_caps |= IEEE80211_C_WEP;
521c42a7b7eSSam Leffler 	if (ath_hal_ciphersupported(ah, HAL_CIPHER_AES_OCB))
522c42a7b7eSSam Leffler 		ic->ic_caps |= IEEE80211_C_AES;
523c42a7b7eSSam Leffler 	if (ath_hal_ciphersupported(ah, HAL_CIPHER_AES_CCM))
524c42a7b7eSSam Leffler 		ic->ic_caps |= IEEE80211_C_AES_CCM;
525c42a7b7eSSam Leffler 	if (ath_hal_ciphersupported(ah, HAL_CIPHER_CKIP))
526c42a7b7eSSam Leffler 		ic->ic_caps |= IEEE80211_C_CKIP;
527c42a7b7eSSam Leffler 	if (ath_hal_ciphersupported(ah, HAL_CIPHER_TKIP)) {
528c42a7b7eSSam Leffler 		ic->ic_caps |= IEEE80211_C_TKIP;
529c42a7b7eSSam Leffler 		/*
530c42a7b7eSSam Leffler 		 * Check if h/w does the MIC and/or whether the
531c42a7b7eSSam Leffler 		 * separate key cache entries are required to
532c42a7b7eSSam Leffler 		 * handle both tx+rx MIC keys.
533c42a7b7eSSam Leffler 		 */
534c42a7b7eSSam Leffler 		if (ath_hal_ciphersupported(ah, HAL_CIPHER_MIC))
535c42a7b7eSSam Leffler 			ic->ic_caps |= IEEE80211_C_TKIPMIC;
536c42a7b7eSSam Leffler 		if (ath_hal_tkipsplit(ah))
537c42a7b7eSSam Leffler 			sc->sc_splitmic = 1;
538c42a7b7eSSam Leffler 	}
539e8fd88a3SSam Leffler 	sc->sc_hasclrkey = ath_hal_ciphersupported(ah, HAL_CIPHER_CLR);
540e8fd88a3SSam Leffler 	sc->sc_mcastkey = ath_hal_getmcastkeysearch(ah);
541c42a7b7eSSam Leffler 	/*
542c42a7b7eSSam Leffler 	 * TPC support can be done either with a global cap or
543c42a7b7eSSam Leffler 	 * per-packet support.  The latter is not available on
544c42a7b7eSSam Leffler 	 * all parts.  We're a bit pedantic here as all parts
545c42a7b7eSSam Leffler 	 * support a global cap.
546c42a7b7eSSam Leffler 	 */
547c59005e9SSam Leffler 	if (ath_hal_hastpc(ah) || ath_hal_hastxpowlimit(ah))
548c42a7b7eSSam Leffler 		ic->ic_caps |= IEEE80211_C_TXPMGT;
549c42a7b7eSSam Leffler 
550c42a7b7eSSam Leffler 	/*
551c42a7b7eSSam Leffler 	 * Mark WME capability only if we have sufficient
552c42a7b7eSSam Leffler 	 * hardware queues to do proper priority scheduling.
553c42a7b7eSSam Leffler 	 */
554c42a7b7eSSam Leffler 	if (sc->sc_ac2q[WME_AC_BE] != sc->sc_ac2q[WME_AC_BK])
555c42a7b7eSSam Leffler 		ic->ic_caps |= IEEE80211_C_WME;
556c42a7b7eSSam Leffler 	/*
557e8fd88a3SSam Leffler 	 * Check for misc other capabilities.
558c42a7b7eSSam Leffler 	 */
559c42a7b7eSSam Leffler 	if (ath_hal_hasbursting(ah))
560c42a7b7eSSam Leffler 		ic->ic_caps |= IEEE80211_C_BURST;
561c42a7b7eSSam Leffler 
562c42a7b7eSSam Leffler 	/*
563c42a7b7eSSam Leffler 	 * Indicate we need the 802.11 header padded to a
564c42a7b7eSSam Leffler 	 * 32-bit boundary for 4-address and QoS frames.
565c42a7b7eSSam Leffler 	 */
566c42a7b7eSSam Leffler 	ic->ic_flags |= IEEE80211_F_DATAPAD;
567c42a7b7eSSam Leffler 
568c42a7b7eSSam Leffler 	/*
569c42a7b7eSSam Leffler 	 * Query the hal about antenna support.
570c42a7b7eSSam Leffler 	 */
571c42a7b7eSSam Leffler 	sc->sc_defant = ath_hal_getdefantenna(ah);
572c42a7b7eSSam Leffler 
573c42a7b7eSSam Leffler 	/*
574c42a7b7eSSam Leffler 	 * Not all chips have the VEOL support we want to
575c42a7b7eSSam Leffler 	 * use with IBSS beacons; check here for it.
576c42a7b7eSSam Leffler 	 */
577c42a7b7eSSam Leffler 	sc->sc_hasveol = ath_hal_hasveol(ah);
5785591b213SSam Leffler 
5795591b213SSam Leffler 	/* get mac address from hardware */
5805591b213SSam Leffler 	ath_hal_getmac(ah, ic->ic_myaddr);
5815591b213SSam Leffler 
5825591b213SSam Leffler 	/* call MI attach routine. */
583c42a7b7eSSam Leffler 	ieee80211_ifattach(ic);
5847a04dc27SSam Leffler 	sc->sc_opmode = ic->ic_opmode;
5855591b213SSam Leffler 	/* override default methods */
5865591b213SSam Leffler 	ic->ic_node_alloc = ath_node_alloc;
5871e774079SSam Leffler 	sc->sc_node_free = ic->ic_node_free;
5885591b213SSam Leffler 	ic->ic_node_free = ath_node_free;
589de5af704SSam Leffler 	ic->ic_node_getrssi = ath_node_getrssi;
590c42a7b7eSSam Leffler 	sc->sc_recv_mgmt = ic->ic_recv_mgmt;
591c42a7b7eSSam Leffler 	ic->ic_recv_mgmt = ath_recv_mgmt;
59245bbf62fSSam Leffler 	sc->sc_newstate = ic->ic_newstate;
59345bbf62fSSam Leffler 	ic->ic_newstate = ath_newstate;
594c1225b52SSam Leffler 	ic->ic_crypto.cs_max_keyix = sc->sc_keymax;
595c42a7b7eSSam Leffler 	ic->ic_crypto.cs_key_alloc = ath_key_alloc;
596c42a7b7eSSam Leffler 	ic->ic_crypto.cs_key_delete = ath_key_delete;
597c42a7b7eSSam Leffler 	ic->ic_crypto.cs_key_set = ath_key_set;
598c42a7b7eSSam Leffler 	ic->ic_crypto.cs_key_update_begin = ath_key_update_begin;
599c42a7b7eSSam Leffler 	ic->ic_crypto.cs_key_update_end = ath_key_update_end;
60045bbf62fSSam Leffler 	/* complete initialization */
601c42a7b7eSSam Leffler 	ieee80211_media_init(ic, ath_media_change, ieee80211_media_status);
6025591b213SSam Leffler 
603c42a7b7eSSam Leffler 	ath_bpfattach(sc);
6044866e6c2SSam Leffler 	/*
6054866e6c2SSam Leffler 	 * Setup dynamic sysctl's now that country code and
6064866e6c2SSam Leffler 	 * regdomain are available from the hal.
6074866e6c2SSam Leffler 	 */
6084866e6c2SSam Leffler 	ath_sysctlattach(sc);
60973454c73SSam Leffler 
610c42a7b7eSSam Leffler 	if (bootverbose)
611c42a7b7eSSam Leffler 		ieee80211_announce(ic);
612c42a7b7eSSam Leffler 	ath_announce(sc);
6135591b213SSam Leffler 	return 0;
614b28b4653SSam Leffler bad2:
615c42a7b7eSSam Leffler 	ath_tx_cleanup(sc);
616b28b4653SSam Leffler 	ath_desc_free(sc);
6175591b213SSam Leffler bad:
6185591b213SSam Leffler 	if (ah)
6195591b213SSam Leffler 		ath_hal_detach(ah);
620fc74a9f9SBrooks Davis 	if (ifp != NULL)
621fc74a9f9SBrooks Davis 		if_free(ifp);
6225591b213SSam Leffler 	sc->sc_invalid = 1;
6235591b213SSam Leffler 	return error;
6245591b213SSam Leffler }
6255591b213SSam Leffler 
6265591b213SSam Leffler int
6275591b213SSam Leffler ath_detach(struct ath_softc *sc)
6285591b213SSam Leffler {
629fc74a9f9SBrooks Davis 	struct ifnet *ifp = sc->sc_ifp;
6305591b213SSam Leffler 
631c42a7b7eSSam Leffler 	DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n",
632c42a7b7eSSam Leffler 		__func__, ifp->if_flags);
6335591b213SSam Leffler 
6345591b213SSam Leffler 	ath_stop(ifp);
63573454c73SSam Leffler 	bpfdetach(ifp);
636c42a7b7eSSam Leffler 	/*
637c42a7b7eSSam Leffler 	 * NB: the order of these is important:
638c42a7b7eSSam Leffler 	 * o call the 802.11 layer before detaching the hal to
639c42a7b7eSSam Leffler 	 *   insure callbacks into the driver to delete global
640c42a7b7eSSam Leffler 	 *   key cache entries can be handled
641c42a7b7eSSam Leffler 	 * o reclaim the tx queue data structures after calling
642c42a7b7eSSam Leffler 	 *   the 802.11 layer as we'll get called back to reclaim
643c42a7b7eSSam Leffler 	 *   node state and potentially want to use them
644c42a7b7eSSam Leffler 	 * o to cleanup the tx queues the hal is called, so detach
645c42a7b7eSSam Leffler 	 *   it last
646c42a7b7eSSam Leffler 	 * Other than that, it's straightforward...
647c42a7b7eSSam Leffler 	 */
648c42a7b7eSSam Leffler 	ieee80211_ifdetach(&sc->sc_ic);
64986e07743SSam Leffler #ifdef ATH_TX99_DIAG
65086e07743SSam Leffler 	if (sc->sc_tx99 != NULL)
65186e07743SSam Leffler 		sc->sc_tx99->detach(sc->sc_tx99);
65286e07743SSam Leffler #endif
6530bbf5441SSam Leffler 	taskqueue_free(sc->sc_tq);
654c42a7b7eSSam Leffler 	ath_rate_detach(sc->sc_rc);
6555591b213SSam Leffler 	ath_desc_free(sc);
656c42a7b7eSSam Leffler 	ath_tx_cleanup(sc);
6575591b213SSam Leffler 	ath_hal_detach(sc->sc_ah);
658c4c6f08fSRuslan Ermilov 	if_free(ifp);
659f0b2a0beSSam Leffler 
6605591b213SSam Leffler 	return 0;
6615591b213SSam Leffler }
6625591b213SSam Leffler 
6635591b213SSam Leffler void
6645591b213SSam Leffler ath_suspend(struct ath_softc *sc)
6655591b213SSam Leffler {
666fc74a9f9SBrooks Davis 	struct ifnet *ifp = sc->sc_ifp;
6675591b213SSam Leffler 
668c42a7b7eSSam Leffler 	DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n",
669c42a7b7eSSam Leffler 		__func__, ifp->if_flags);
6705591b213SSam Leffler 
6715591b213SSam Leffler 	ath_stop(ifp);
6725591b213SSam Leffler }
6735591b213SSam Leffler 
6745591b213SSam Leffler void
6755591b213SSam Leffler ath_resume(struct ath_softc *sc)
6765591b213SSam Leffler {
677fc74a9f9SBrooks Davis 	struct ifnet *ifp = sc->sc_ifp;
6785591b213SSam Leffler 
679c42a7b7eSSam Leffler 	DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n",
680c42a7b7eSSam Leffler 		__func__, ifp->if_flags);
6815591b213SSam Leffler 
6826b59f5e3SSam Leffler 	if (ifp->if_flags & IFF_UP) {
683fc74a9f9SBrooks Davis 		ath_init(sc);
68413f4c340SRobert Watson 		if (ifp->if_drv_flags & IFF_DRV_RUNNING)
6855591b213SSam Leffler 			ath_start(ifp);
6865591b213SSam Leffler 	}
687b50c8bdeSSam Leffler 	if (sc->sc_softled) {
688b50c8bdeSSam Leffler 		ath_hal_gpioCfgOutput(sc->sc_ah, sc->sc_ledpin);
689b50c8bdeSSam Leffler 		ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, !sc->sc_ledon);
690b50c8bdeSSam Leffler 	}
6916b59f5e3SSam Leffler }
6925591b213SSam Leffler 
6935591b213SSam Leffler void
6945591b213SSam Leffler ath_shutdown(struct ath_softc *sc)
6955591b213SSam Leffler {
696fc74a9f9SBrooks Davis 	struct ifnet *ifp = sc->sc_ifp;
6975591b213SSam Leffler 
698c42a7b7eSSam Leffler 	DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n",
699c42a7b7eSSam Leffler 		__func__, ifp->if_flags);
7005591b213SSam Leffler 
7015591b213SSam Leffler 	ath_stop(ifp);
7025591b213SSam Leffler }
7035591b213SSam Leffler 
704c42a7b7eSSam Leffler /*
705c42a7b7eSSam Leffler  * Interrupt handler.  Most of the actual processing is deferred.
706c42a7b7eSSam Leffler  */
7075591b213SSam Leffler void
7085591b213SSam Leffler ath_intr(void *arg)
7095591b213SSam Leffler {
7105591b213SSam Leffler 	struct ath_softc *sc = arg;
711fc74a9f9SBrooks Davis 	struct ifnet *ifp = sc->sc_ifp;
7125591b213SSam Leffler 	struct ath_hal *ah = sc->sc_ah;
7135591b213SSam Leffler 	HAL_INT status;
7145591b213SSam Leffler 
7155591b213SSam Leffler 	if (sc->sc_invalid) {
7165591b213SSam Leffler 		/*
717b58b3803SSam Leffler 		 * The hardware is not ready/present, don't touch anything.
718b58b3803SSam Leffler 		 * Note this can happen early on if the IRQ is shared.
7195591b213SSam Leffler 		 */
720c42a7b7eSSam Leffler 		DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid; ignored\n", __func__);
7215591b213SSam Leffler 		return;
7225591b213SSam Leffler 	}
723fdd758d4SSam Leffler 	if (!ath_hal_intrpend(ah))		/* shared irq, not for us */
724fdd758d4SSam Leffler 		return;
72513f4c340SRobert Watson 	if (!((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags &
72613f4c340SRobert Watson 	    IFF_DRV_RUNNING))) {
727c42a7b7eSSam Leffler 		DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags 0x%x\n",
728c42a7b7eSSam Leffler 			__func__, ifp->if_flags);
7295591b213SSam Leffler 		ath_hal_getisr(ah, &status);	/* clear ISR */
7305591b213SSam Leffler 		ath_hal_intrset(ah, 0);		/* disable further intr's */
7315591b213SSam Leffler 		return;
7325591b213SSam Leffler 	}
733c42a7b7eSSam Leffler 	/*
734c42a7b7eSSam Leffler 	 * Figure out the reason(s) for the interrupt.  Note
735c42a7b7eSSam Leffler 	 * that the hal returns a pseudo-ISR that may include
736c42a7b7eSSam Leffler 	 * bits we haven't explicitly enabled so we mask the
737c42a7b7eSSam Leffler 	 * value to insure we only process bits we requested.
738c42a7b7eSSam Leffler 	 */
7395591b213SSam Leffler 	ath_hal_getisr(ah, &status);		/* NB: clears ISR too */
740c42a7b7eSSam Leffler 	DPRINTF(sc, ATH_DEBUG_INTR, "%s: status 0x%x\n", __func__, status);
741ecddff40SSam Leffler 	status &= sc->sc_imask;			/* discard unasked for bits */
7425591b213SSam Leffler 	if (status & HAL_INT_FATAL) {
743c42a7b7eSSam Leffler 		/*
744c42a7b7eSSam Leffler 		 * Fatal errors are unrecoverable.  Typically
745c42a7b7eSSam Leffler 		 * these are caused by DMA errors.  Unfortunately
746c42a7b7eSSam Leffler 		 * the exact reason is not (presently) returned
747c42a7b7eSSam Leffler 		 * by the hal.
748c42a7b7eSSam Leffler 		 */
7495591b213SSam Leffler 		sc->sc_stats.ast_hardware++;
7505591b213SSam Leffler 		ath_hal_intrset(ah, 0);		/* disable intr's until reset */
7510bbf5441SSam Leffler 		taskqueue_enqueue(sc->sc_tq, &sc->sc_fataltask);
7525591b213SSam Leffler 	} else if (status & HAL_INT_RXORN) {
7535591b213SSam Leffler 		sc->sc_stats.ast_rxorn++;
7545591b213SSam Leffler 		ath_hal_intrset(ah, 0);		/* disable intr's until reset */
7550bbf5441SSam Leffler 		taskqueue_enqueue(sc->sc_tq, &sc->sc_rxorntask);
7565591b213SSam Leffler 	} else {
757c42a7b7eSSam Leffler 		if (status & HAL_INT_SWBA) {
758c42a7b7eSSam Leffler 			/*
759c42a7b7eSSam Leffler 			 * Software beacon alert--time to send a beacon.
760c42a7b7eSSam Leffler 			 * Handle beacon transmission directly; deferring
761c42a7b7eSSam Leffler 			 * this is too slow to meet timing constraints
762c42a7b7eSSam Leffler 			 * under load.
763c42a7b7eSSam Leffler 			 */
764c42a7b7eSSam Leffler 			ath_beacon_proc(sc, 0);
765c42a7b7eSSam Leffler 		}
7665591b213SSam Leffler 		if (status & HAL_INT_RXEOL) {
7675591b213SSam Leffler 			/*
7685591b213SSam Leffler 			 * NB: the hardware should re-read the link when
7695591b213SSam Leffler 			 *     RXE bit is written, but it doesn't work at
7705591b213SSam Leffler 			 *     least on older hardware revs.
7715591b213SSam Leffler 			 */
7725591b213SSam Leffler 			sc->sc_stats.ast_rxeol++;
7735591b213SSam Leffler 			sc->sc_rxlink = NULL;
7745591b213SSam Leffler 		}
7755591b213SSam Leffler 		if (status & HAL_INT_TXURN) {
7765591b213SSam Leffler 			sc->sc_stats.ast_txurn++;
7775591b213SSam Leffler 			/* bump tx trigger level */
7785591b213SSam Leffler 			ath_hal_updatetxtriglevel(ah, AH_TRUE);
7795591b213SSam Leffler 		}
7805591b213SSam Leffler 		if (status & HAL_INT_RX)
7810bbf5441SSam Leffler 			taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask);
7825591b213SSam Leffler 		if (status & HAL_INT_TX)
7830bbf5441SSam Leffler 			taskqueue_enqueue(sc->sc_tq, &sc->sc_txtask);
7845591b213SSam Leffler 		if (status & HAL_INT_BMISS) {
7855591b213SSam Leffler 			sc->sc_stats.ast_bmiss++;
7860bbf5441SSam Leffler 			taskqueue_enqueue(sc->sc_tq, &sc->sc_bmisstask);
7875591b213SSam Leffler 		}
788c42a7b7eSSam Leffler 		if (status & HAL_INT_MIB) {
789c42a7b7eSSam Leffler 			sc->sc_stats.ast_mib++;
790c42a7b7eSSam Leffler 			/*
791c42a7b7eSSam Leffler 			 * Disable interrupts until we service the MIB
792c42a7b7eSSam Leffler 			 * interrupt; otherwise it will continue to fire.
793c42a7b7eSSam Leffler 			 */
794c42a7b7eSSam Leffler 			ath_hal_intrset(ah, 0);
795c42a7b7eSSam Leffler 			/*
796c42a7b7eSSam Leffler 			 * Let the hal handle the event.  We assume it will
797c42a7b7eSSam Leffler 			 * clear whatever condition caused the interrupt.
798c42a7b7eSSam Leffler 			 */
799ffa2cab6SSam Leffler 			ath_hal_mibevent(ah, &sc->sc_halstats);
800c42a7b7eSSam Leffler 			ath_hal_intrset(ah, sc->sc_imask);
801c42a7b7eSSam Leffler 		}
8025591b213SSam Leffler 	}
8035591b213SSam Leffler }
8045591b213SSam Leffler 
8055591b213SSam Leffler static void
8065591b213SSam Leffler ath_fatal_proc(void *arg, int pending)
8075591b213SSam Leffler {
8085591b213SSam Leffler 	struct ath_softc *sc = arg;
809fc74a9f9SBrooks Davis 	struct ifnet *ifp = sc->sc_ifp;
8105591b213SSam Leffler 
811c42a7b7eSSam Leffler 	if_printf(ifp, "hardware error; resetting\n");
812c42a7b7eSSam Leffler 	ath_reset(ifp);
8135591b213SSam Leffler }
8145591b213SSam Leffler 
8155591b213SSam Leffler static void
8165591b213SSam Leffler ath_rxorn_proc(void *arg, int pending)
8175591b213SSam Leffler {
8185591b213SSam Leffler 	struct ath_softc *sc = arg;
819fc74a9f9SBrooks Davis 	struct ifnet *ifp = sc->sc_ifp;
8205591b213SSam Leffler 
821c42a7b7eSSam Leffler 	if_printf(ifp, "rx FIFO overrun; resetting\n");
822c42a7b7eSSam Leffler 	ath_reset(ifp);
8235591b213SSam Leffler }
8245591b213SSam Leffler 
8255591b213SSam Leffler static void
8265591b213SSam Leffler ath_bmiss_proc(void *arg, int pending)
8275591b213SSam Leffler {
8285591b213SSam Leffler 	struct ath_softc *sc = arg;
8295591b213SSam Leffler 	struct ieee80211com *ic = &sc->sc_ic;
8305591b213SSam Leffler 
831c42a7b7eSSam Leffler 	DPRINTF(sc, ATH_DEBUG_ANY, "%s: pending %u\n", __func__, pending);
8325591b213SSam Leffler 	KASSERT(ic->ic_opmode == IEEE80211_M_STA,
8335591b213SSam Leffler 		("unexpect operating mode %u", ic->ic_opmode));
834e585d188SSam Leffler 	if (ic->ic_state == IEEE80211_S_RUN) {
835d7736e13SSam Leffler 		u_int64_t lastrx = sc->sc_lastrx;
836d7736e13SSam Leffler 		u_int64_t tsf = ath_hal_gettsf64(sc->sc_ah);
837d7736e13SSam Leffler 		u_int bmisstimeout =
838d7736e13SSam Leffler 			ic->ic_bmissthreshold * ic->ic_bss->ni_intval * 1024;
839d7736e13SSam Leffler 
840d7736e13SSam Leffler 		DPRINTF(sc, ATH_DEBUG_BEACON,
841d7736e13SSam Leffler 		    "%s: tsf %llu lastrx %lld (%llu) bmiss %u\n",
842d7736e13SSam Leffler 		    __func__, (unsigned long long) tsf,
843d7736e13SSam Leffler 		    (unsigned long long)(tsf - lastrx),
844d7736e13SSam Leffler 		    (unsigned long long) lastrx, bmisstimeout);
845e585d188SSam Leffler 		/*
846d7736e13SSam Leffler 		 * Workaround phantom bmiss interrupts by sanity-checking
847d7736e13SSam Leffler 		 * the time of our last rx'd frame.  If it is within the
848d7736e13SSam Leffler 		 * beacon miss interval then ignore the interrupt.  If it's
849d7736e13SSam Leffler 		 * truly a bmiss we'll get another interrupt soon and that'll
850d7736e13SSam Leffler 		 * be dispatched up for processing.
851e585d188SSam Leffler 		 */
852d7736e13SSam Leffler 		if (tsf - lastrx > bmisstimeout) {
853b5f4adb3SSam Leffler 			NET_LOCK_GIANT();
854d7736e13SSam Leffler 			ieee80211_beacon_miss(ic);
855b5f4adb3SSam Leffler 			NET_UNLOCK_GIANT();
856d7736e13SSam Leffler 		} else
857d7736e13SSam Leffler 			sc->sc_stats.ast_bmiss_phantom++;
858e585d188SSam Leffler 	}
8595591b213SSam Leffler }
8605591b213SSam Leffler 
8615591b213SSam Leffler static u_int
8625591b213SSam Leffler ath_chan2flags(struct ieee80211com *ic, struct ieee80211_channel *chan)
8635591b213SSam Leffler {
864c42a7b7eSSam Leffler #define	N(a)	(sizeof(a) / sizeof(a[0]))
8655591b213SSam Leffler 	static const u_int modeflags[] = {
8665591b213SSam Leffler 		0,			/* IEEE80211_MODE_AUTO */
8675591b213SSam Leffler 		CHANNEL_A,		/* IEEE80211_MODE_11A */
8685591b213SSam Leffler 		CHANNEL_B,		/* IEEE80211_MODE_11B */
8695591b213SSam Leffler 		CHANNEL_PUREG,		/* IEEE80211_MODE_11G */
870c42a7b7eSSam Leffler 		0,			/* IEEE80211_MODE_FH */
871c42a7b7eSSam Leffler 		CHANNEL_T,		/* IEEE80211_MODE_TURBO_A */
872c42a7b7eSSam Leffler 		CHANNEL_108G		/* IEEE80211_MODE_TURBO_G */
8735591b213SSam Leffler 	};
874c42a7b7eSSam Leffler 	enum ieee80211_phymode mode = ieee80211_chan2mode(ic, chan);
875c42a7b7eSSam Leffler 
876c42a7b7eSSam Leffler 	KASSERT(mode < N(modeflags), ("unexpected phy mode %u", mode));
877c42a7b7eSSam Leffler 	KASSERT(modeflags[mode] != 0, ("mode %u undefined", mode));
878c42a7b7eSSam Leffler 	return modeflags[mode];
879c42a7b7eSSam Leffler #undef N
8805591b213SSam Leffler }
8815591b213SSam Leffler 
8825591b213SSam Leffler static void
8835591b213SSam Leffler ath_init(void *arg)
8845591b213SSam Leffler {
8855591b213SSam Leffler 	struct ath_softc *sc = (struct ath_softc *) arg;
8865591b213SSam Leffler 	struct ieee80211com *ic = &sc->sc_ic;
887fc74a9f9SBrooks Davis 	struct ifnet *ifp = sc->sc_ifp;
8885591b213SSam Leffler 	struct ath_hal *ah = sc->sc_ah;
8895591b213SSam Leffler 	HAL_STATUS status;
8905591b213SSam Leffler 
891c42a7b7eSSam Leffler 	DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags 0x%x\n",
892c42a7b7eSSam Leffler 		__func__, ifp->if_flags);
8935591b213SSam Leffler 
894f0b2a0beSSam Leffler 	ATH_LOCK(sc);
8955591b213SSam Leffler 	/*
8965591b213SSam Leffler 	 * Stop anything previously setup.  This is safe
8975591b213SSam Leffler 	 * whether this is the first time through or not.
8985591b213SSam Leffler 	 */
899c42a7b7eSSam Leffler 	ath_stop_locked(ifp);
9005591b213SSam Leffler 
9015591b213SSam Leffler 	/*
9025591b213SSam Leffler 	 * The basic interface to setting the hardware in a good
9035591b213SSam Leffler 	 * state is ``reset''.  On return the hardware is known to
9045591b213SSam Leffler 	 * be powered up and with interrupts disabled.  This must
9055591b213SSam Leffler 	 * be followed by initialization of the appropriate bits
9065591b213SSam Leffler 	 * and then setup of the interrupt mask.
9075591b213SSam Leffler 	 */
908b5c99415SSam Leffler 	sc->sc_curchan.channel = ic->ic_curchan->ic_freq;
909b5c99415SSam Leffler 	sc->sc_curchan.channelFlags = ath_chan2flags(ic, ic->ic_curchan);
9107a04dc27SSam Leffler 	if (!ath_hal_reset(ah, sc->sc_opmode, &sc->sc_curchan, AH_FALSE, &status)) {
9115591b213SSam Leffler 		if_printf(ifp, "unable to reset hardware; hal status %u\n",
9125591b213SSam Leffler 			status);
9135591b213SSam Leffler 		goto done;
9145591b213SSam Leffler 	}
9155591b213SSam Leffler 
9165591b213SSam Leffler 	/*
917c42a7b7eSSam Leffler 	 * This is needed only to setup initial state
918c42a7b7eSSam Leffler 	 * but it's best done after a reset.
919c42a7b7eSSam Leffler 	 */
920c42a7b7eSSam Leffler 	ath_update_txpow(sc);
921c59005e9SSam Leffler 	/*
922c59005e9SSam Leffler 	 * Likewise this is set during reset so update
923c59005e9SSam Leffler 	 * state cached in the driver.
924c59005e9SSam Leffler 	 */
925c59005e9SSam Leffler 	sc->sc_diversity = ath_hal_getdiversity(ah);
926c42a7b7eSSam Leffler 
927c42a7b7eSSam Leffler 	/*
9285591b213SSam Leffler 	 * Setup the hardware after reset: the key cache
9295591b213SSam Leffler 	 * is filled as needed and the receive engine is
9305591b213SSam Leffler 	 * set going.  Frame transmit is handled entirely
9315591b213SSam Leffler 	 * in the frame output path; there's nothing to do
9325591b213SSam Leffler 	 * here except setup the interrupt mask.
9335591b213SSam Leffler 	 */
9345591b213SSam Leffler 	if (ath_startrecv(sc) != 0) {
9355591b213SSam Leffler 		if_printf(ifp, "unable to start recv logic\n");
9365591b213SSam Leffler 		goto done;
9375591b213SSam Leffler 	}
9385591b213SSam Leffler 
9395591b213SSam Leffler 	/*
9405591b213SSam Leffler 	 * Enable interrupts.
9415591b213SSam Leffler 	 */
9425591b213SSam Leffler 	sc->sc_imask = HAL_INT_RX | HAL_INT_TX
9435591b213SSam Leffler 		  | HAL_INT_RXEOL | HAL_INT_RXORN
9445591b213SSam Leffler 		  | HAL_INT_FATAL | HAL_INT_GLOBAL;
945c42a7b7eSSam Leffler 	/*
946c42a7b7eSSam Leffler 	 * Enable MIB interrupts when there are hardware phy counters.
947c42a7b7eSSam Leffler 	 * Note we only do this (at the moment) for station mode.
948c42a7b7eSSam Leffler 	 */
949c42a7b7eSSam Leffler 	if (sc->sc_needmib && ic->ic_opmode == IEEE80211_M_STA)
950c42a7b7eSSam Leffler 		sc->sc_imask |= HAL_INT_MIB;
9515591b213SSam Leffler 	ath_hal_intrset(ah, sc->sc_imask);
9525591b213SSam Leffler 
95313f4c340SRobert Watson 	ifp->if_drv_flags |= IFF_DRV_RUNNING;
9545591b213SSam Leffler 	ic->ic_state = IEEE80211_S_INIT;
9555591b213SSam Leffler 
9565591b213SSam Leffler 	/*
9575591b213SSam Leffler 	 * The hardware should be ready to go now so it's safe
9585591b213SSam Leffler 	 * to kick the 802.11 state machine as it's likely to
9595591b213SSam Leffler 	 * immediately call back to us to send mgmt frames.
9605591b213SSam Leffler 	 */
961b5c99415SSam Leffler 	ath_chan_change(sc, ic->ic_curchan);
96286e07743SSam Leffler #ifdef ATH_TX99_DIAG
96386e07743SSam Leffler 	if (sc->sc_tx99 != NULL)
96486e07743SSam Leffler 		sc->sc_tx99->start(sc->sc_tx99);
96586e07743SSam Leffler 	else
96686e07743SSam Leffler #endif
967c42a7b7eSSam Leffler 	if (ic->ic_opmode != IEEE80211_M_MONITOR) {
968c42a7b7eSSam Leffler 		if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
96945bbf62fSSam Leffler 			ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
970c42a7b7eSSam Leffler 	} else
9716b59f5e3SSam Leffler 		ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
9725591b213SSam Leffler done:
973f0b2a0beSSam Leffler 	ATH_UNLOCK(sc);
9745591b213SSam Leffler }
9755591b213SSam Leffler 
9765591b213SSam Leffler static void
977c42a7b7eSSam Leffler ath_stop_locked(struct ifnet *ifp)
9785591b213SSam Leffler {
9795591b213SSam Leffler 	struct ath_softc *sc = ifp->if_softc;
980c42a7b7eSSam Leffler 	struct ieee80211com *ic = &sc->sc_ic;
9815591b213SSam Leffler 	struct ath_hal *ah = sc->sc_ah;
9825591b213SSam Leffler 
983c42a7b7eSSam Leffler 	DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid %u if_flags 0x%x\n",
984c42a7b7eSSam Leffler 		__func__, sc->sc_invalid, ifp->if_flags);
9855591b213SSam Leffler 
986c42a7b7eSSam Leffler 	ATH_LOCK_ASSERT(sc);
98713f4c340SRobert Watson 	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
9885591b213SSam Leffler 		/*
9895591b213SSam Leffler 		 * Shutdown the hardware and driver:
990c42a7b7eSSam Leffler 		 *    reset 802.11 state machine
9915591b213SSam Leffler 		 *    turn off timers
992c42a7b7eSSam Leffler 		 *    disable interrupts
993c42a7b7eSSam Leffler 		 *    turn off the radio
9945591b213SSam Leffler 		 *    clear transmit machinery
9955591b213SSam Leffler 		 *    clear receive machinery
9965591b213SSam Leffler 		 *    drain and release tx queues
9975591b213SSam Leffler 		 *    reclaim beacon resources
9985591b213SSam Leffler 		 *    power down hardware
9995591b213SSam Leffler 		 *
10005591b213SSam Leffler 		 * Note that some of this work is not possible if the
10015591b213SSam Leffler 		 * hardware is gone (invalid).
10025591b213SSam Leffler 		 */
100386e07743SSam Leffler #ifdef ATH_TX99_DIAG
100486e07743SSam Leffler 		if (sc->sc_tx99 != NULL)
100586e07743SSam Leffler 			sc->sc_tx99->stop(sc->sc_tx99);
100686e07743SSam Leffler #endif
1007c42a7b7eSSam Leffler 		ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
100813f4c340SRobert Watson 		ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
10095591b213SSam Leffler 		ifp->if_timer = 0;
1010c42a7b7eSSam Leffler 		if (!sc->sc_invalid) {
10113e50ec2cSSam Leffler 			if (sc->sc_softled) {
10123e50ec2cSSam Leffler 				callout_stop(&sc->sc_ledtimer);
10133e50ec2cSSam Leffler 				ath_hal_gpioset(ah, sc->sc_ledpin,
10143e50ec2cSSam Leffler 					!sc->sc_ledon);
10153e50ec2cSSam Leffler 				sc->sc_blinking = 0;
10163e50ec2cSSam Leffler 			}
10175591b213SSam Leffler 			ath_hal_intrset(ah, 0);
1018c42a7b7eSSam Leffler 		}
10195591b213SSam Leffler 		ath_draintxq(sc);
1020c42a7b7eSSam Leffler 		if (!sc->sc_invalid) {
10215591b213SSam Leffler 			ath_stoprecv(sc);
1022c42a7b7eSSam Leffler 			ath_hal_phydisable(ah);
1023c42a7b7eSSam Leffler 		} else
10245591b213SSam Leffler 			sc->sc_rxlink = NULL;
1025154b8df2SMax Laier 		IFQ_DRV_PURGE(&ifp->if_snd);
10265591b213SSam Leffler 		ath_beacon_free(sc);
1027c42a7b7eSSam Leffler 	}
1028c42a7b7eSSam Leffler }
1029c42a7b7eSSam Leffler 
1030c42a7b7eSSam Leffler static void
1031c42a7b7eSSam Leffler ath_stop(struct ifnet *ifp)
1032c42a7b7eSSam Leffler {
1033c42a7b7eSSam Leffler 	struct ath_softc *sc = ifp->if_softc;
1034c42a7b7eSSam Leffler 
1035c42a7b7eSSam Leffler 	ATH_LOCK(sc);
1036c42a7b7eSSam Leffler 	ath_stop_locked(ifp);
1037c42a7b7eSSam Leffler 	if (!sc->sc_invalid) {
1038c42a7b7eSSam Leffler 		/*
1039c42a7b7eSSam Leffler 		 * Set the chip in full sleep mode.  Note that we are
1040c42a7b7eSSam Leffler 		 * careful to do this only when bringing the interface
1041c42a7b7eSSam Leffler 		 * completely to a stop.  When the chip is in this state
1042c42a7b7eSSam Leffler 		 * it must be carefully woken up or references to
1043c42a7b7eSSam Leffler 		 * registers in the PCI clock domain may freeze the bus
1044c42a7b7eSSam Leffler 		 * (and system).  This varies by chip and is mostly an
1045c42a7b7eSSam Leffler 		 * issue with newer parts that go to sleep more quickly.
1046c42a7b7eSSam Leffler 		 */
1047c42a7b7eSSam Leffler 		ath_hal_setpower(sc->sc_ah, HAL_PM_FULL_SLEEP, 0);
10485591b213SSam Leffler 	}
1049f0b2a0beSSam Leffler 	ATH_UNLOCK(sc);
10505591b213SSam Leffler }
10515591b213SSam Leffler 
10525591b213SSam Leffler /*
10535591b213SSam Leffler  * Reset the hardware w/o losing operational state.  This is
10545591b213SSam Leffler  * basically a more efficient way of doing ath_stop, ath_init,
10555591b213SSam Leffler  * followed by state transitions to the current 802.11
1056c42a7b7eSSam Leffler  * operational state.  Used to recover from various errors and
1057c42a7b7eSSam Leffler  * to reset or reload hardware state.
10585591b213SSam Leffler  */
1059c42a7b7eSSam Leffler static int
1060c42a7b7eSSam Leffler ath_reset(struct ifnet *ifp)
10615591b213SSam Leffler {
1062c42a7b7eSSam Leffler 	struct ath_softc *sc = ifp->if_softc;
10635591b213SSam Leffler 	struct ieee80211com *ic = &sc->sc_ic;
10645591b213SSam Leffler 	struct ath_hal *ah = sc->sc_ah;
10655591b213SSam Leffler 	struct ieee80211_channel *c;
10665591b213SSam Leffler 	HAL_STATUS status;
10675591b213SSam Leffler 
10685591b213SSam Leffler 	/*
10695591b213SSam Leffler 	 * Convert to a HAL channel description with the flags
10705591b213SSam Leffler 	 * constrained to reflect the current operating mode.
10715591b213SSam Leffler 	 */
1072b5c99415SSam Leffler 	c = ic->ic_curchan;
1073c42a7b7eSSam Leffler 	sc->sc_curchan.channel = c->ic_freq;
1074c42a7b7eSSam Leffler 	sc->sc_curchan.channelFlags = ath_chan2flags(ic, c);
10755591b213SSam Leffler 
10765591b213SSam Leffler 	ath_hal_intrset(ah, 0);		/* disable interrupts */
10775591b213SSam Leffler 	ath_draintxq(sc);		/* stop xmit side */
10785591b213SSam Leffler 	ath_stoprecv(sc);		/* stop recv side */
10795591b213SSam Leffler 	/* NB: indicate channel change so we do a full reset */
10807a04dc27SSam Leffler 	if (!ath_hal_reset(ah, sc->sc_opmode, &sc->sc_curchan, AH_TRUE, &status))
10815591b213SSam Leffler 		if_printf(ifp, "%s: unable to reset hardware; hal status %u\n",
10825591b213SSam Leffler 			__func__, status);
1083c42a7b7eSSam Leffler 	ath_update_txpow(sc);		/* update tx power state */
1084c59005e9SSam Leffler 	sc->sc_diversity = ath_hal_getdiversity(ah);
10855591b213SSam Leffler 	if (ath_startrecv(sc) != 0)	/* restart recv */
10865591b213SSam Leffler 		if_printf(ifp, "%s: unable to start recv logic\n", __func__);
1087c42a7b7eSSam Leffler 	/*
1088c42a7b7eSSam Leffler 	 * We may be doing a reset in response to an ioctl
1089c42a7b7eSSam Leffler 	 * that changes the channel so update any state that
1090c42a7b7eSSam Leffler 	 * might change as a result.
1091c42a7b7eSSam Leffler 	 */
1092c42a7b7eSSam Leffler 	ath_chan_change(sc, c);
10935591b213SSam Leffler 	if (ic->ic_state == IEEE80211_S_RUN)
10945591b213SSam Leffler 		ath_beacon_config(sc);	/* restart beacons */
1095c42a7b7eSSam Leffler 	ath_hal_intrset(ah, sc->sc_imask);
1096c42a7b7eSSam Leffler 
1097c42a7b7eSSam Leffler 	ath_start(ifp);			/* restart xmit */
1098c42a7b7eSSam Leffler 	return 0;
10995591b213SSam Leffler }
11005591b213SSam Leffler 
11015591b213SSam Leffler static void
11025591b213SSam Leffler ath_start(struct ifnet *ifp)
11035591b213SSam Leffler {
11045591b213SSam Leffler 	struct ath_softc *sc = ifp->if_softc;
11055591b213SSam Leffler 	struct ath_hal *ah = sc->sc_ah;
11065591b213SSam Leffler 	struct ieee80211com *ic = &sc->sc_ic;
11075591b213SSam Leffler 	struct ieee80211_node *ni;
11085591b213SSam Leffler 	struct ath_buf *bf;
11095591b213SSam Leffler 	struct mbuf *m;
11105591b213SSam Leffler 	struct ieee80211_frame *wh;
1111c42a7b7eSSam Leffler 	struct ether_header *eh;
11125591b213SSam Leffler 
111313f4c340SRobert Watson 	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || sc->sc_invalid)
11145591b213SSam Leffler 		return;
11155591b213SSam Leffler 	for (;;) {
11165591b213SSam Leffler 		/*
11175591b213SSam Leffler 		 * Grab a TX buffer and associated resources.
11185591b213SSam Leffler 		 */
1119f0b2a0beSSam Leffler 		ATH_TXBUF_LOCK(sc);
1120c42a7b7eSSam Leffler 		bf = STAILQ_FIRST(&sc->sc_txbuf);
11215591b213SSam Leffler 		if (bf != NULL)
1122c42a7b7eSSam Leffler 			STAILQ_REMOVE_HEAD(&sc->sc_txbuf, bf_list);
1123f0b2a0beSSam Leffler 		ATH_TXBUF_UNLOCK(sc);
11245591b213SSam Leffler 		if (bf == NULL) {
1125370572d9SSam Leffler 			DPRINTF(sc, ATH_DEBUG_XMIT, "%s: out of xmit buffers\n",
1126c42a7b7eSSam Leffler 				__func__);
11275591b213SSam Leffler 			sc->sc_stats.ast_tx_qstop++;
112813f4c340SRobert Watson 			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
11295591b213SSam Leffler 			break;
11305591b213SSam Leffler 		}
11315591b213SSam Leffler 		/*
11325591b213SSam Leffler 		 * Poll the management queue for frames; they
11335591b213SSam Leffler 		 * have priority over normal data frames.
11345591b213SSam Leffler 		 */
11355591b213SSam Leffler 		IF_DEQUEUE(&ic->ic_mgtq, m);
11365591b213SSam Leffler 		if (m == NULL) {
11375591b213SSam Leffler 			/*
11385591b213SSam Leffler 			 * No data frames go out unless we're associated.
11395591b213SSam Leffler 			 */
11405591b213SSam Leffler 			if (ic->ic_state != IEEE80211_S_RUN) {
1141370572d9SSam Leffler 				DPRINTF(sc, ATH_DEBUG_XMIT,
1142370572d9SSam Leffler 				    "%s: discard data packet, state %s\n",
1143370572d9SSam Leffler 				    __func__,
1144370572d9SSam Leffler 				    ieee80211_state_name[ic->ic_state]);
11455591b213SSam Leffler 				sc->sc_stats.ast_tx_discard++;
1146f0b2a0beSSam Leffler 				ATH_TXBUF_LOCK(sc);
1147c42a7b7eSSam Leffler 				STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
1148f0b2a0beSSam Leffler 				ATH_TXBUF_UNLOCK(sc);
11495591b213SSam Leffler 				break;
11505591b213SSam Leffler 			}
1151154b8df2SMax Laier 			IFQ_DRV_DEQUEUE(&ifp->if_snd, m);	/* XXX: LOCK */
11525591b213SSam Leffler 			if (m == NULL) {
1153f0b2a0beSSam Leffler 				ATH_TXBUF_LOCK(sc);
1154c42a7b7eSSam Leffler 				STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
1155f0b2a0beSSam Leffler 				ATH_TXBUF_UNLOCK(sc);
11565591b213SSam Leffler 				break;
11575591b213SSam Leffler 			}
1158c42a7b7eSSam Leffler 			/*
1159c42a7b7eSSam Leffler 			 * Find the node for the destination so we can do
1160c42a7b7eSSam Leffler 			 * things like power save and fast frames aggregation.
1161c42a7b7eSSam Leffler 			 */
1162c42a7b7eSSam Leffler 			if (m->m_len < sizeof(struct ether_header) &&
1163c42a7b7eSSam Leffler 			   (m = m_pullup(m, sizeof(struct ether_header))) == NULL) {
1164c42a7b7eSSam Leffler 				ic->ic_stats.is_tx_nobuf++;	/* XXX */
1165c42a7b7eSSam Leffler 				ni = NULL;
1166c42a7b7eSSam Leffler 				goto bad;
1167c42a7b7eSSam Leffler 			}
1168c42a7b7eSSam Leffler 			eh = mtod(m, struct ether_header *);
1169c42a7b7eSSam Leffler 			ni = ieee80211_find_txnode(ic, eh->ether_dhost);
1170c42a7b7eSSam Leffler 			if (ni == NULL) {
1171c42a7b7eSSam Leffler 				/* NB: ieee80211_find_txnode does stat+msg */
1172fe234894SSam Leffler 				m_freem(m);
1173c42a7b7eSSam Leffler 				goto bad;
1174c42a7b7eSSam Leffler 			}
1175c42a7b7eSSam Leffler 			if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) &&
1176c42a7b7eSSam Leffler 			    (m->m_flags & M_PWR_SAV) == 0) {
1177c42a7b7eSSam Leffler 				/*
1178c42a7b7eSSam Leffler 				 * Station in power save mode; pass the frame
1179c42a7b7eSSam Leffler 				 * to the 802.11 layer and continue.  We'll get
1180c42a7b7eSSam Leffler 				 * the frame back when the time is right.
1181c42a7b7eSSam Leffler 				 */
1182c42a7b7eSSam Leffler 				ieee80211_pwrsave(ic, ni, m);
1183c42a7b7eSSam Leffler 				goto reclaim;
1184c42a7b7eSSam Leffler 			}
1185c42a7b7eSSam Leffler 			/* calculate priority so we can find the tx queue */
1186c42a7b7eSSam Leffler 			if (ieee80211_classify(ic, m, ni)) {
1187c42a7b7eSSam Leffler 				DPRINTF(sc, ATH_DEBUG_XMIT,
1188c42a7b7eSSam Leffler 					"%s: discard, classification failure\n",
1189c42a7b7eSSam Leffler 					__func__);
1190fe234894SSam Leffler 				m_freem(m);
1191c42a7b7eSSam Leffler 				goto bad;
1192c42a7b7eSSam Leffler 			}
11935591b213SSam Leffler 			ifp->if_opackets++;
11945591b213SSam Leffler 			BPF_MTAP(ifp, m);
11955591b213SSam Leffler 			/*
11965591b213SSam Leffler 			 * Encapsulate the packet in prep for transmission.
11975591b213SSam Leffler 			 */
1198c42a7b7eSSam Leffler 			m = ieee80211_encap(ic, m, ni);
11995591b213SSam Leffler 			if (m == NULL) {
1200370572d9SSam Leffler 				DPRINTF(sc, ATH_DEBUG_XMIT,
1201c42a7b7eSSam Leffler 					"%s: encapsulation failure\n",
1202c42a7b7eSSam Leffler 					__func__);
12035591b213SSam Leffler 				sc->sc_stats.ast_tx_encap++;
12045591b213SSam Leffler 				goto bad;
12055591b213SSam Leffler 			}
12065591b213SSam Leffler 		} else {
12070a915fadSSam Leffler 			/*
12080a915fadSSam Leffler 			 * Hack!  The referenced node pointer is in the
12090a915fadSSam Leffler 			 * rcvif field of the packet header.  This is
12100a915fadSSam Leffler 			 * placed there by ieee80211_mgmt_output because
12110a915fadSSam Leffler 			 * we need to hold the reference with the frame
12120a915fadSSam Leffler 			 * and there's no other way (other than packet
12130a915fadSSam Leffler 			 * tags which we consider too expensive to use)
12140a915fadSSam Leffler 			 * to pass it along.
12150a915fadSSam Leffler 			 */
12160a915fadSSam Leffler 			ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
12170a915fadSSam Leffler 			m->m_pkthdr.rcvif = NULL;
12180a915fadSSam Leffler 
12195591b213SSam Leffler 			wh = mtod(m, struct ieee80211_frame *);
12205591b213SSam Leffler 			if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
12215591b213SSam Leffler 			    IEEE80211_FC0_SUBTYPE_PROBE_RESP) {
12225591b213SSam Leffler 				/* fill time stamp */
12235591b213SSam Leffler 				u_int64_t tsf;
12245591b213SSam Leffler 				u_int32_t *tstamp;
12255591b213SSam Leffler 
12265591b213SSam Leffler 				tsf = ath_hal_gettsf64(ah);
12275591b213SSam Leffler 				/* XXX: adjust 100us delay to xmit */
12285591b213SSam Leffler 				tsf += 100;
12295591b213SSam Leffler 				tstamp = (u_int32_t *)&wh[1];
12305591b213SSam Leffler 				tstamp[0] = htole32(tsf & 0xffffffff);
12315591b213SSam Leffler 				tstamp[1] = htole32(tsf >> 32);
12325591b213SSam Leffler 			}
12335591b213SSam Leffler 			sc->sc_stats.ast_tx_mgmt++;
12345591b213SSam Leffler 		}
123573454c73SSam Leffler 
12365591b213SSam Leffler 		if (ath_tx_start(sc, ni, bf, m)) {
12375591b213SSam Leffler 	bad:
12385591b213SSam Leffler 			ifp->if_oerrors++;
1239c42a7b7eSSam Leffler 	reclaim:
1240c42a7b7eSSam Leffler 			ATH_TXBUF_LOCK(sc);
1241c42a7b7eSSam Leffler 			STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
1242c42a7b7eSSam Leffler 			ATH_TXBUF_UNLOCK(sc);
1243c42a7b7eSSam Leffler 			if (ni != NULL)
1244c42a7b7eSSam Leffler 				ieee80211_free_node(ni);
12455591b213SSam Leffler 			continue;
12465591b213SSam Leffler 		}
12475591b213SSam Leffler 
12485591b213SSam Leffler 		sc->sc_tx_timer = 5;
12495591b213SSam Leffler 		ifp->if_timer = 1;
12505591b213SSam Leffler 	}
12515591b213SSam Leffler }
12525591b213SSam Leffler 
12535591b213SSam Leffler static int
12545591b213SSam Leffler ath_media_change(struct ifnet *ifp)
12555591b213SSam Leffler {
1256c42a7b7eSSam Leffler #define	IS_UP(ifp) \
125713f4c340SRobert Watson 	((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING))
12585591b213SSam Leffler 	int error;
12595591b213SSam Leffler 
12605591b213SSam Leffler 	error = ieee80211_media_change(ifp);
12615591b213SSam Leffler 	if (error == ENETRESET) {
12627a04dc27SSam Leffler 		struct ath_softc *sc = ifp->if_softc;
12637a04dc27SSam Leffler 		struct ieee80211com *ic = &sc->sc_ic;
12647a04dc27SSam Leffler 
12657a04dc27SSam Leffler 		if (ic->ic_opmode == IEEE80211_M_AHDEMO) {
12667a04dc27SSam Leffler 			/*
12677a04dc27SSam Leffler 			 * Adhoc demo mode is just ibss mode w/o beacons
12687a04dc27SSam Leffler 			 * (mostly).  The hal knows nothing about it;
12697a04dc27SSam Leffler 			 * tell it we're operating in ibss mode.
12707a04dc27SSam Leffler 			 */
12717a04dc27SSam Leffler 			sc->sc_opmode = HAL_M_IBSS;
12727a04dc27SSam Leffler 		} else
12737a04dc27SSam Leffler 			sc->sc_opmode = ic->ic_opmode;
1274c42a7b7eSSam Leffler 		if (IS_UP(ifp))
1275fc74a9f9SBrooks Davis 			ath_init(ifp->if_softc);	/* XXX lose error */
12765591b213SSam Leffler 		error = 0;
12775591b213SSam Leffler 	}
12785591b213SSam Leffler 	return error;
1279c42a7b7eSSam Leffler #undef IS_UP
12805591b213SSam Leffler }
12815591b213SSam Leffler 
12825591b213SSam Leffler #ifdef AR_DEBUG
1283c42a7b7eSSam Leffler static void
1284c42a7b7eSSam Leffler ath_keyprint(const char *tag, u_int ix,
1285c42a7b7eSSam Leffler 	const HAL_KEYVAL *hk, const u_int8_t mac[IEEE80211_ADDR_LEN])
12865591b213SSam Leffler {
1287c42a7b7eSSam Leffler 	static const char *ciphers[] = {
1288c42a7b7eSSam Leffler 		"WEP",
1289c42a7b7eSSam Leffler 		"AES-OCB",
1290c42a7b7eSSam Leffler 		"AES-CCM",
1291c42a7b7eSSam Leffler 		"CKIP",
1292c42a7b7eSSam Leffler 		"TKIP",
1293c42a7b7eSSam Leffler 		"CLR",
1294c42a7b7eSSam Leffler 	};
1295c42a7b7eSSam Leffler 	int i, n;
12965591b213SSam Leffler 
1297c42a7b7eSSam Leffler 	printf("%s: [%02u] %-7s ", tag, ix, ciphers[hk->kv_type]);
1298c42a7b7eSSam Leffler 	for (i = 0, n = hk->kv_len; i < n; i++)
1299c42a7b7eSSam Leffler 		printf("%02x", hk->kv_val[i]);
1300c42a7b7eSSam Leffler 	printf(" mac %s", ether_sprintf(mac));
1301c42a7b7eSSam Leffler 	if (hk->kv_type == HAL_CIPHER_TKIP) {
1302c42a7b7eSSam Leffler 		printf(" mic ");
1303c42a7b7eSSam Leffler 		for (i = 0; i < sizeof(hk->kv_mic); i++)
1304c42a7b7eSSam Leffler 			printf("%02x", hk->kv_mic[i]);
13052075afbaSSam Leffler 	}
1306c42a7b7eSSam Leffler 	printf("\n");
1307c42a7b7eSSam Leffler }
1308c42a7b7eSSam Leffler #endif
1309c42a7b7eSSam Leffler 
13105591b213SSam Leffler /*
1311c42a7b7eSSam Leffler  * Set a TKIP key into the hardware.  This handles the
1312c42a7b7eSSam Leffler  * potential distribution of key state to multiple key
1313c42a7b7eSSam Leffler  * cache slots for TKIP.
13145591b213SSam Leffler  */
1315c42a7b7eSSam Leffler static int
1316c42a7b7eSSam Leffler ath_keyset_tkip(struct ath_softc *sc, const struct ieee80211_key *k,
1317c42a7b7eSSam Leffler 	HAL_KEYVAL *hk, const u_int8_t mac[IEEE80211_ADDR_LEN])
1318c42a7b7eSSam Leffler {
1319c42a7b7eSSam Leffler #define	IEEE80211_KEY_XR	(IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV)
1320c42a7b7eSSam Leffler 	static const u_int8_t zerobssid[IEEE80211_ADDR_LEN];
13218cec0ab9SSam Leffler 	struct ath_hal *ah = sc->sc_ah;
13228cec0ab9SSam Leffler 
1323c42a7b7eSSam Leffler 	KASSERT(k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP,
1324c42a7b7eSSam Leffler 		("got a non-TKIP key, cipher %u", k->wk_cipher->ic_cipher));
1325c42a7b7eSSam Leffler 	KASSERT(sc->sc_splitmic, ("key cache !split"));
1326c42a7b7eSSam Leffler 	if ((k->wk_flags & IEEE80211_KEY_XR) == IEEE80211_KEY_XR) {
1327c42a7b7eSSam Leffler 		/*
1328c1225b52SSam Leffler 		 * TX key goes at first index, RX key at the rx index.
1329c42a7b7eSSam Leffler 		 * The hal handles the MIC keys at index+64.
1330c42a7b7eSSam Leffler 		 */
1331c42a7b7eSSam Leffler 		memcpy(hk->kv_mic, k->wk_txmic, sizeof(hk->kv_mic));
1332c42a7b7eSSam Leffler 		KEYPRINTF(sc, k->wk_keyix, hk, zerobssid);
1333c42a7b7eSSam Leffler 		if (!ath_hal_keyset(ah, k->wk_keyix, hk, zerobssid))
1334c42a7b7eSSam Leffler 			return 0;
1335c42a7b7eSSam Leffler 
1336c42a7b7eSSam Leffler 		memcpy(hk->kv_mic, k->wk_rxmic, sizeof(hk->kv_mic));
1337c42a7b7eSSam Leffler 		KEYPRINTF(sc, k->wk_keyix+32, hk, mac);
1338c42a7b7eSSam Leffler 		/* XXX delete tx key on failure? */
1339c42a7b7eSSam Leffler 		return ath_hal_keyset(ah, k->wk_keyix+32, hk, mac);
1340c42a7b7eSSam Leffler 	} else if (k->wk_flags & IEEE80211_KEY_XR) {
1341c42a7b7eSSam Leffler 		/*
1342c42a7b7eSSam Leffler 		 * TX/RX key goes at first index.
1343c42a7b7eSSam Leffler 		 * The hal handles the MIC keys are index+64.
1344c42a7b7eSSam Leffler 		 */
1345c42a7b7eSSam Leffler 		memcpy(hk->kv_mic, k->wk_flags & IEEE80211_KEY_XMIT ?
1346c42a7b7eSSam Leffler 			k->wk_txmic : k->wk_rxmic, sizeof(hk->kv_mic));
1347e8fd88a3SSam Leffler 		KEYPRINTF(sc, k->wk_keyix, hk, mac);
1348e8fd88a3SSam Leffler 		return ath_hal_keyset(ah, k->wk_keyix, hk, mac);
1349c42a7b7eSSam Leffler 	}
1350c42a7b7eSSam Leffler 	return 0;
1351c42a7b7eSSam Leffler #undef IEEE80211_KEY_XR
1352c42a7b7eSSam Leffler }
1353c42a7b7eSSam Leffler 
1354c42a7b7eSSam Leffler /*
1355c42a7b7eSSam Leffler  * Set a net80211 key into the hardware.  This handles the
1356c42a7b7eSSam Leffler  * potential distribution of key state to multiple key
1357c42a7b7eSSam Leffler  * cache slots for TKIP with hardware MIC support.
1358c42a7b7eSSam Leffler  */
1359c42a7b7eSSam Leffler static int
1360c42a7b7eSSam Leffler ath_keyset(struct ath_softc *sc, const struct ieee80211_key *k,
1361e8fd88a3SSam Leffler 	const u_int8_t mac0[IEEE80211_ADDR_LEN],
1362e8fd88a3SSam Leffler 	struct ieee80211_node *bss)
1363c42a7b7eSSam Leffler {
1364c42a7b7eSSam Leffler #define	N(a)	(sizeof(a)/sizeof(a[0]))
1365c42a7b7eSSam Leffler 	static const u_int8_t ciphermap[] = {
1366c42a7b7eSSam Leffler 		HAL_CIPHER_WEP,		/* IEEE80211_CIPHER_WEP */
1367c42a7b7eSSam Leffler 		HAL_CIPHER_TKIP,	/* IEEE80211_CIPHER_TKIP */
1368c42a7b7eSSam Leffler 		HAL_CIPHER_AES_OCB,	/* IEEE80211_CIPHER_AES_OCB */
1369c42a7b7eSSam Leffler 		HAL_CIPHER_AES_CCM,	/* IEEE80211_CIPHER_AES_CCM */
1370c42a7b7eSSam Leffler 		(u_int8_t) -1,		/* 4 is not allocated */
1371c42a7b7eSSam Leffler 		HAL_CIPHER_CKIP,	/* IEEE80211_CIPHER_CKIP */
1372c42a7b7eSSam Leffler 		HAL_CIPHER_CLR,		/* IEEE80211_CIPHER_NONE */
1373c42a7b7eSSam Leffler 	};
1374c42a7b7eSSam Leffler 	struct ath_hal *ah = sc->sc_ah;
1375c42a7b7eSSam Leffler 	const struct ieee80211_cipher *cip = k->wk_cipher;
1376e8fd88a3SSam Leffler 	u_int8_t gmac[IEEE80211_ADDR_LEN];
1377e8fd88a3SSam Leffler 	const u_int8_t *mac;
1378c42a7b7eSSam Leffler 	HAL_KEYVAL hk;
1379c42a7b7eSSam Leffler 
1380c42a7b7eSSam Leffler 	memset(&hk, 0, sizeof(hk));
1381c42a7b7eSSam Leffler 	/*
1382c42a7b7eSSam Leffler 	 * Software crypto uses a "clear key" so non-crypto
1383c42a7b7eSSam Leffler 	 * state kept in the key cache are maintained and
1384c42a7b7eSSam Leffler 	 * so that rx frames have an entry to match.
1385c42a7b7eSSam Leffler 	 */
1386c42a7b7eSSam Leffler 	if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) == 0) {
1387c42a7b7eSSam Leffler 		KASSERT(cip->ic_cipher < N(ciphermap),
1388c42a7b7eSSam Leffler 			("invalid cipher type %u", cip->ic_cipher));
1389c42a7b7eSSam Leffler 		hk.kv_type = ciphermap[cip->ic_cipher];
1390c42a7b7eSSam Leffler 		hk.kv_len = k->wk_keylen;
1391c42a7b7eSSam Leffler 		memcpy(hk.kv_val, k->wk_key, k->wk_keylen);
13928cec0ab9SSam Leffler 	} else
1393c42a7b7eSSam Leffler 		hk.kv_type = HAL_CIPHER_CLR;
1394c42a7b7eSSam Leffler 
1395e8fd88a3SSam Leffler 	if ((k->wk_flags & IEEE80211_KEY_GROUP) && sc->sc_mcastkey) {
1396e8fd88a3SSam Leffler 		/*
1397e8fd88a3SSam Leffler 		 * Group keys on hardware that supports multicast frame
1398e8fd88a3SSam Leffler 		 * key search use a mac that is the sender's address with
1399e8fd88a3SSam Leffler 		 * the high bit set instead of the app-specified address.
1400e8fd88a3SSam Leffler 		 */
1401e8fd88a3SSam Leffler 		IEEE80211_ADDR_COPY(gmac, bss->ni_macaddr);
1402e8fd88a3SSam Leffler 		gmac[0] |= 0x80;
1403e8fd88a3SSam Leffler 		mac = gmac;
1404e8fd88a3SSam Leffler 	} else
1405e8fd88a3SSam Leffler 		mac = mac0;
1406e8fd88a3SSam Leffler 
1407c42a7b7eSSam Leffler 	if (hk.kv_type == HAL_CIPHER_TKIP &&
1408c42a7b7eSSam Leffler 	    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 &&
1409c42a7b7eSSam Leffler 	    sc->sc_splitmic) {
1410c42a7b7eSSam Leffler 		return ath_keyset_tkip(sc, k, &hk, mac);
1411c42a7b7eSSam Leffler 	} else {
1412c42a7b7eSSam Leffler 		KEYPRINTF(sc, k->wk_keyix, &hk, mac);
1413c42a7b7eSSam Leffler 		return ath_hal_keyset(ah, k->wk_keyix, &hk, mac);
14148cec0ab9SSam Leffler 	}
1415c42a7b7eSSam Leffler #undef N
14165591b213SSam Leffler }
14175591b213SSam Leffler 
14185591b213SSam Leffler /*
1419c42a7b7eSSam Leffler  * Allocate tx/rx key slots for TKIP.  We allocate two slots for
1420c42a7b7eSSam Leffler  * each key, one for decrypt/encrypt and the other for the MIC.
1421c42a7b7eSSam Leffler  */
1422c42a7b7eSSam Leffler static u_int16_t
1423c1225b52SSam Leffler key_alloc_2pair(struct ath_softc *sc,
1424c1225b52SSam Leffler 	ieee80211_keyix *txkeyix, ieee80211_keyix *rxkeyix)
1425c42a7b7eSSam Leffler {
1426c42a7b7eSSam Leffler #define	N(a)	(sizeof(a)/sizeof(a[0]))
1427c42a7b7eSSam Leffler 	u_int i, keyix;
1428c42a7b7eSSam Leffler 
1429c42a7b7eSSam Leffler 	KASSERT(sc->sc_splitmic, ("key cache !split"));
1430c42a7b7eSSam Leffler 	/* XXX could optimize */
1431c42a7b7eSSam Leffler 	for (i = 0; i < N(sc->sc_keymap)/4; i++) {
1432c42a7b7eSSam Leffler 		u_int8_t b = sc->sc_keymap[i];
1433c42a7b7eSSam Leffler 		if (b != 0xff) {
1434c42a7b7eSSam Leffler 			/*
1435c42a7b7eSSam Leffler 			 * One or more slots in this byte are free.
1436c42a7b7eSSam Leffler 			 */
1437c42a7b7eSSam Leffler 			keyix = i*NBBY;
1438c42a7b7eSSam Leffler 			while (b & 1) {
1439c42a7b7eSSam Leffler 		again:
1440c42a7b7eSSam Leffler 				keyix++;
1441c42a7b7eSSam Leffler 				b >>= 1;
1442c42a7b7eSSam Leffler 			}
1443c42a7b7eSSam Leffler 			/* XXX IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV */
1444c42a7b7eSSam Leffler 			if (isset(sc->sc_keymap, keyix+32) ||
1445c42a7b7eSSam Leffler 			    isset(sc->sc_keymap, keyix+64) ||
1446c42a7b7eSSam Leffler 			    isset(sc->sc_keymap, keyix+32+64)) {
1447c42a7b7eSSam Leffler 				/* full pair unavailable */
1448c42a7b7eSSam Leffler 				/* XXX statistic */
1449c42a7b7eSSam Leffler 				if (keyix == (i+1)*NBBY) {
1450c42a7b7eSSam Leffler 					/* no slots were appropriate, advance */
1451c42a7b7eSSam Leffler 					continue;
1452c42a7b7eSSam Leffler 				}
1453c42a7b7eSSam Leffler 				goto again;
1454c42a7b7eSSam Leffler 			}
1455c42a7b7eSSam Leffler 			setbit(sc->sc_keymap, keyix);
1456c42a7b7eSSam Leffler 			setbit(sc->sc_keymap, keyix+64);
1457c42a7b7eSSam Leffler 			setbit(sc->sc_keymap, keyix+32);
1458c42a7b7eSSam Leffler 			setbit(sc->sc_keymap, keyix+32+64);
1459c42a7b7eSSam Leffler 			DPRINTF(sc, ATH_DEBUG_KEYCACHE,
1460c42a7b7eSSam Leffler 				"%s: key pair %u,%u %u,%u\n",
1461c42a7b7eSSam Leffler 				__func__, keyix, keyix+64,
1462c42a7b7eSSam Leffler 				keyix+32, keyix+32+64);
1463c1225b52SSam Leffler 			*txkeyix = keyix;
1464c1225b52SSam Leffler 			*rxkeyix = keyix+32;
1465c1225b52SSam Leffler 			return 1;
1466c42a7b7eSSam Leffler 		}
1467c42a7b7eSSam Leffler 	}
1468c42a7b7eSSam Leffler 	DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: out of pair space\n", __func__);
1469c1225b52SSam Leffler 	return 0;
1470c42a7b7eSSam Leffler #undef N
1471c42a7b7eSSam Leffler }
1472c42a7b7eSSam Leffler 
1473c42a7b7eSSam Leffler /*
1474c42a7b7eSSam Leffler  * Allocate a single key cache slot.
1475c42a7b7eSSam Leffler  */
1476c1225b52SSam Leffler static int
1477c1225b52SSam Leffler key_alloc_single(struct ath_softc *sc,
1478c1225b52SSam Leffler 	ieee80211_keyix *txkeyix, ieee80211_keyix *rxkeyix)
1479c42a7b7eSSam Leffler {
1480c42a7b7eSSam Leffler #define	N(a)	(sizeof(a)/sizeof(a[0]))
1481c42a7b7eSSam Leffler 	u_int i, keyix;
1482c42a7b7eSSam Leffler 
1483c42a7b7eSSam Leffler 	/* XXX try i,i+32,i+64,i+32+64 to minimize key pair conflicts */
1484c42a7b7eSSam Leffler 	for (i = 0; i < N(sc->sc_keymap); i++) {
1485c42a7b7eSSam Leffler 		u_int8_t b = sc->sc_keymap[i];
1486c42a7b7eSSam Leffler 		if (b != 0xff) {
1487c42a7b7eSSam Leffler 			/*
1488c42a7b7eSSam Leffler 			 * One or more slots are free.
1489c42a7b7eSSam Leffler 			 */
1490c42a7b7eSSam Leffler 			keyix = i*NBBY;
1491c42a7b7eSSam Leffler 			while (b & 1)
1492c42a7b7eSSam Leffler 				keyix++, b >>= 1;
1493c42a7b7eSSam Leffler 			setbit(sc->sc_keymap, keyix);
1494c42a7b7eSSam Leffler 			DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: key %u\n",
1495c42a7b7eSSam Leffler 				__func__, keyix);
1496c1225b52SSam Leffler 			*txkeyix = *rxkeyix = keyix;
1497c1225b52SSam Leffler 			return 1;
1498c42a7b7eSSam Leffler 		}
1499c42a7b7eSSam Leffler 	}
1500c42a7b7eSSam Leffler 	DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: out of space\n", __func__);
1501c1225b52SSam Leffler 	return 0;
1502c42a7b7eSSam Leffler #undef N
1503c42a7b7eSSam Leffler }
1504c42a7b7eSSam Leffler 
1505c42a7b7eSSam Leffler /*
1506c42a7b7eSSam Leffler  * Allocate one or more key cache slots for a uniacst key.  The
1507c42a7b7eSSam Leffler  * key itself is needed only to identify the cipher.  For hardware
1508c42a7b7eSSam Leffler  * TKIP with split cipher+MIC keys we allocate two key cache slot
1509c42a7b7eSSam Leffler  * pairs so that we can setup separate TX and RX MIC keys.  Note
1510c42a7b7eSSam Leffler  * that the MIC key for a TKIP key at slot i is assumed by the
1511c42a7b7eSSam Leffler  * hardware to be at slot i+64.  This limits TKIP keys to the first
1512c42a7b7eSSam Leffler  * 64 entries.
1513c42a7b7eSSam Leffler  */
1514c42a7b7eSSam Leffler static int
1515c1225b52SSam Leffler ath_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k,
1516c1225b52SSam Leffler 	ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
1517c42a7b7eSSam Leffler {
1518c42a7b7eSSam Leffler 	struct ath_softc *sc = ic->ic_ifp->if_softc;
1519c42a7b7eSSam Leffler 
1520c42a7b7eSSam Leffler 	/*
15218ca623d7SSam Leffler 	 * Group key allocation must be handled specially for
15228ca623d7SSam Leffler 	 * parts that do not support multicast key cache search
15238ca623d7SSam Leffler 	 * functionality.  For those parts the key id must match
15248ca623d7SSam Leffler 	 * the h/w key index so lookups find the right key.  On
15258ca623d7SSam Leffler 	 * parts w/ the key search facility we install the sender's
15268ca623d7SSam Leffler 	 * mac address (with the high bit set) and let the hardware
15278ca623d7SSam Leffler 	 * find the key w/o using the key id.  This is preferred as
15288ca623d7SSam Leffler 	 * it permits us to support multiple users for adhoc and/or
15298ca623d7SSam Leffler 	 * multi-station operation.
15308ca623d7SSam Leffler 	 */
15318ca623d7SSam Leffler 	if ((k->wk_flags & IEEE80211_KEY_GROUP) && !sc->sc_mcastkey) {
15328ca623d7SSam Leffler 		if (!(&ic->ic_nw_keys[0] <= k &&
15338ca623d7SSam Leffler 		      k < &ic->ic_nw_keys[IEEE80211_WEP_NKID])) {
15348ca623d7SSam Leffler 			/* should not happen */
15358ca623d7SSam Leffler 			DPRINTF(sc, ATH_DEBUG_KEYCACHE,
15368ca623d7SSam Leffler 				"%s: bogus group key\n", __func__);
1537c1225b52SSam Leffler 			return 0;
15388ca623d7SSam Leffler 		}
15398ca623d7SSam Leffler 		/*
15408ca623d7SSam Leffler 		 * XXX we pre-allocate the global keys so
15418ca623d7SSam Leffler 		 * have no way to check if they've already been allocated.
15428ca623d7SSam Leffler 		 */
1543c1225b52SSam Leffler 		*keyix = *rxkeyix = k - ic->ic_nw_keys;
1544c1225b52SSam Leffler 		return 1;
15458ca623d7SSam Leffler 	}
15468ca623d7SSam Leffler 
15478ca623d7SSam Leffler 	/*
1548c42a7b7eSSam Leffler 	 * We allocate two pair for TKIP when using the h/w to do
1549c42a7b7eSSam Leffler 	 * the MIC.  For everything else, including software crypto,
1550c42a7b7eSSam Leffler 	 * we allocate a single entry.  Note that s/w crypto requires
1551c42a7b7eSSam Leffler 	 * a pass-through slot on the 5211 and 5212.  The 5210 does
1552c42a7b7eSSam Leffler 	 * not support pass-through cache entries and we map all
1553c42a7b7eSSam Leffler 	 * those requests to slot 0.
1554c42a7b7eSSam Leffler 	 */
1555c42a7b7eSSam Leffler 	if (k->wk_flags & IEEE80211_KEY_SWCRYPT) {
1556c1225b52SSam Leffler 		return key_alloc_single(sc, keyix, rxkeyix);
1557c42a7b7eSSam Leffler 	} else if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP &&
1558c42a7b7eSSam Leffler 	    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && sc->sc_splitmic) {
1559c1225b52SSam Leffler 		return key_alloc_2pair(sc, keyix, rxkeyix);
1560c42a7b7eSSam Leffler 	} else {
1561c1225b52SSam Leffler 		return key_alloc_single(sc, keyix, rxkeyix);
1562c42a7b7eSSam Leffler 	}
1563c42a7b7eSSam Leffler }
1564c42a7b7eSSam Leffler 
1565c42a7b7eSSam Leffler /*
1566c42a7b7eSSam Leffler  * Delete an entry in the key cache allocated by ath_key_alloc.
1567c42a7b7eSSam Leffler  */
1568c42a7b7eSSam Leffler static int
1569c42a7b7eSSam Leffler ath_key_delete(struct ieee80211com *ic, const struct ieee80211_key *k)
1570c42a7b7eSSam Leffler {
1571c42a7b7eSSam Leffler 	struct ath_softc *sc = ic->ic_ifp->if_softc;
1572c42a7b7eSSam Leffler 	struct ath_hal *ah = sc->sc_ah;
1573c42a7b7eSSam Leffler 	const struct ieee80211_cipher *cip = k->wk_cipher;
1574c42a7b7eSSam Leffler 	u_int keyix = k->wk_keyix;
1575c42a7b7eSSam Leffler 
1576c42a7b7eSSam Leffler 	DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: delete key %u\n", __func__, keyix);
1577c42a7b7eSSam Leffler 
1578c42a7b7eSSam Leffler 	ath_hal_keyreset(ah, keyix);
1579c42a7b7eSSam Leffler 	/*
1580c42a7b7eSSam Leffler 	 * Handle split tx/rx keying required for TKIP with h/w MIC.
1581c42a7b7eSSam Leffler 	 */
1582c42a7b7eSSam Leffler 	if (cip->ic_cipher == IEEE80211_CIPHER_TKIP &&
1583c1225b52SSam Leffler 	    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && sc->sc_splitmic)
1584c42a7b7eSSam Leffler 		ath_hal_keyreset(ah, keyix+32);		/* RX key */
1585c42a7b7eSSam Leffler 	if (keyix >= IEEE80211_WEP_NKID) {
1586c42a7b7eSSam Leffler 		/*
1587c42a7b7eSSam Leffler 		 * Don't touch keymap entries for global keys so
1588c42a7b7eSSam Leffler 		 * they are never considered for dynamic allocation.
1589c42a7b7eSSam Leffler 		 */
1590c42a7b7eSSam Leffler 		clrbit(sc->sc_keymap, keyix);
1591c42a7b7eSSam Leffler 		if (cip->ic_cipher == IEEE80211_CIPHER_TKIP &&
1592c42a7b7eSSam Leffler 		    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 &&
1593c42a7b7eSSam Leffler 		    sc->sc_splitmic) {
1594c42a7b7eSSam Leffler 			clrbit(sc->sc_keymap, keyix+64);	/* TX key MIC */
1595c42a7b7eSSam Leffler 			clrbit(sc->sc_keymap, keyix+32);	/* RX key */
1596c42a7b7eSSam Leffler 			clrbit(sc->sc_keymap, keyix+32+64);	/* RX key MIC */
1597c42a7b7eSSam Leffler 		}
1598c42a7b7eSSam Leffler 	}
1599c42a7b7eSSam Leffler 	return 1;
1600c42a7b7eSSam Leffler }
1601c42a7b7eSSam Leffler 
1602c42a7b7eSSam Leffler /*
1603c42a7b7eSSam Leffler  * Set the key cache contents for the specified key.  Key cache
1604c42a7b7eSSam Leffler  * slot(s) must already have been allocated by ath_key_alloc.
1605c42a7b7eSSam Leffler  */
1606c42a7b7eSSam Leffler static int
1607c42a7b7eSSam Leffler ath_key_set(struct ieee80211com *ic, const struct ieee80211_key *k,
1608c42a7b7eSSam Leffler 	const u_int8_t mac[IEEE80211_ADDR_LEN])
1609c42a7b7eSSam Leffler {
1610c42a7b7eSSam Leffler 	struct ath_softc *sc = ic->ic_ifp->if_softc;
1611c42a7b7eSSam Leffler 
1612e8fd88a3SSam Leffler 	return ath_keyset(sc, k, mac, ic->ic_bss);
1613c42a7b7eSSam Leffler }
1614c42a7b7eSSam Leffler 
1615c42a7b7eSSam Leffler /*
1616c42a7b7eSSam Leffler  * Block/unblock tx+rx processing while a key change is done.
1617c42a7b7eSSam Leffler  * We assume the caller serializes key management operations
1618c42a7b7eSSam Leffler  * so we only need to worry about synchronization with other
1619c42a7b7eSSam Leffler  * uses that originate in the driver.
1620c42a7b7eSSam Leffler  */
1621c42a7b7eSSam Leffler static void
1622c42a7b7eSSam Leffler ath_key_update_begin(struct ieee80211com *ic)
1623c42a7b7eSSam Leffler {
1624c42a7b7eSSam Leffler 	struct ifnet *ifp = ic->ic_ifp;
1625c42a7b7eSSam Leffler 	struct ath_softc *sc = ifp->if_softc;
1626c42a7b7eSSam Leffler 
1627c42a7b7eSSam Leffler 	DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__);
1628c42a7b7eSSam Leffler #if 0
1629c42a7b7eSSam Leffler 	tasklet_disable(&sc->sc_rxtq);
1630c42a7b7eSSam Leffler #endif
1631c42a7b7eSSam Leffler 	IF_LOCK(&ifp->if_snd);		/* NB: doesn't block mgmt frames */
1632c42a7b7eSSam Leffler }
1633c42a7b7eSSam Leffler 
1634c42a7b7eSSam Leffler static void
1635c42a7b7eSSam Leffler ath_key_update_end(struct ieee80211com *ic)
1636c42a7b7eSSam Leffler {
1637c42a7b7eSSam Leffler 	struct ifnet *ifp = ic->ic_ifp;
1638c42a7b7eSSam Leffler 	struct ath_softc *sc = ifp->if_softc;
1639c42a7b7eSSam Leffler 
1640c42a7b7eSSam Leffler 	DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__);
1641c42a7b7eSSam Leffler 	IF_UNLOCK(&ifp->if_snd);
1642c42a7b7eSSam Leffler #if 0
1643c42a7b7eSSam Leffler 	tasklet_enable(&sc->sc_rxtq);
1644c42a7b7eSSam Leffler #endif
1645c42a7b7eSSam Leffler }
16465591b213SSam Leffler 
16474bc0e754SSam Leffler /*
16484bc0e754SSam Leffler  * Calculate the receive filter according to the
16494bc0e754SSam Leffler  * operating mode and state:
16504bc0e754SSam Leffler  *
16514bc0e754SSam Leffler  * o always accept unicast, broadcast, and multicast traffic
1652c42a7b7eSSam Leffler  * o maintain current state of phy error reception (the hal
1653c42a7b7eSSam Leffler  *   may enable phy error frames for noise immunity work)
16544bc0e754SSam Leffler  * o probe request frames are accepted only when operating in
16554bc0e754SSam Leffler  *   hostap, adhoc, or monitor modes
16564bc0e754SSam Leffler  * o enable promiscuous mode according to the interface state
16574bc0e754SSam Leffler  * o accept beacons:
16584bc0e754SSam Leffler  *   - when operating in adhoc mode so the 802.11 layer creates
16594bc0e754SSam Leffler  *     node table entries for peers,
16604bc0e754SSam Leffler  *   - when operating in station mode for collecting rssi data when
16614bc0e754SSam Leffler  *     the station is otherwise quiet, or
16624bc0e754SSam Leffler  *   - when scanning
16634bc0e754SSam Leffler  */
16644bc0e754SSam Leffler static u_int32_t
1665c42a7b7eSSam Leffler ath_calcrxfilter(struct ath_softc *sc, enum ieee80211_state state)
16664bc0e754SSam Leffler {
16674bc0e754SSam Leffler 	struct ieee80211com *ic = &sc->sc_ic;
16684bc0e754SSam Leffler 	struct ath_hal *ah = sc->sc_ah;
1669fc74a9f9SBrooks Davis 	struct ifnet *ifp = sc->sc_ifp;
16704bc0e754SSam Leffler 	u_int32_t rfilt;
16714bc0e754SSam Leffler 
16724bc0e754SSam Leffler 	rfilt = (ath_hal_getrxfilter(ah) & HAL_RX_FILTER_PHYERR)
16734bc0e754SSam Leffler 	      | HAL_RX_FILTER_UCAST | HAL_RX_FILTER_BCAST | HAL_RX_FILTER_MCAST;
16744bc0e754SSam Leffler 	if (ic->ic_opmode != IEEE80211_M_STA)
16754bc0e754SSam Leffler 		rfilt |= HAL_RX_FILTER_PROBEREQ;
16764bc0e754SSam Leffler 	if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
16774bc0e754SSam Leffler 	    (ifp->if_flags & IFF_PROMISC))
16784bc0e754SSam Leffler 		rfilt |= HAL_RX_FILTER_PROM;
16794bc0e754SSam Leffler 	if (ic->ic_opmode == IEEE80211_M_STA ||
16804bc0e754SSam Leffler 	    ic->ic_opmode == IEEE80211_M_IBSS ||
1681c42a7b7eSSam Leffler 	    state == IEEE80211_S_SCAN)
16824bc0e754SSam Leffler 		rfilt |= HAL_RX_FILTER_BEACON;
16834bc0e754SSam Leffler 	return rfilt;
16844bc0e754SSam Leffler }
16854bc0e754SSam Leffler 
16865591b213SSam Leffler static void
16875591b213SSam Leffler ath_mode_init(struct ath_softc *sc)
16885591b213SSam Leffler {
16895591b213SSam Leffler 	struct ieee80211com *ic = &sc->sc_ic;
16905591b213SSam Leffler 	struct ath_hal *ah = sc->sc_ah;
1691fc74a9f9SBrooks Davis 	struct ifnet *ifp = sc->sc_ifp;
16925591b213SSam Leffler 	u_int32_t rfilt, mfilt[2], val;
16935591b213SSam Leffler 	u_int8_t pos;
16945591b213SSam Leffler 	struct ifmultiaddr *ifma;
16955591b213SSam Leffler 
16964bc0e754SSam Leffler 	/* configure rx filter */
1697c42a7b7eSSam Leffler 	rfilt = ath_calcrxfilter(sc, ic->ic_state);
16984bc0e754SSam Leffler 	ath_hal_setrxfilter(ah, rfilt);
16994bc0e754SSam Leffler 
17005591b213SSam Leffler 	/* configure operational mode */
1701c42a7b7eSSam Leffler 	ath_hal_setopmode(ah);
1702c42a7b7eSSam Leffler 
1703c42a7b7eSSam Leffler 	/*
1704c42a7b7eSSam Leffler 	 * Handle any link-level address change.  Note that we only
1705c42a7b7eSSam Leffler 	 * need to force ic_myaddr; any other addresses are handled
1706c42a7b7eSSam Leffler 	 * as a byproduct of the ifnet code marking the interface
1707c42a7b7eSSam Leffler 	 * down then up.
1708c42a7b7eSSam Leffler 	 *
1709c42a7b7eSSam Leffler 	 * XXX should get from lladdr instead of arpcom but that's more work
1710c42a7b7eSSam Leffler 	 */
17114a0d6638SRuslan Ermilov 	IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp));
1712c42a7b7eSSam Leffler 	ath_hal_setmac(ah, ic->ic_myaddr);
17135591b213SSam Leffler 
17145591b213SSam Leffler 	/* calculate and install multicast filter */
17155591b213SSam Leffler 	if ((ifp->if_flags & IFF_ALLMULTI) == 0) {
17165591b213SSam Leffler 		mfilt[0] = mfilt[1] = 0;
171713b203d0SRobert Watson 		IF_ADDR_LOCK(ifp);
17185591b213SSam Leffler 		TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
17195591b213SSam Leffler 			caddr_t dl;
17205591b213SSam Leffler 
17215591b213SSam Leffler 			/* calculate XOR of eight 6bit values */
17225591b213SSam Leffler 			dl = LLADDR((struct sockaddr_dl *) ifma->ifma_addr);
17235591b213SSam Leffler 			val = LE_READ_4(dl + 0);
17245591b213SSam Leffler 			pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
17255591b213SSam Leffler 			val = LE_READ_4(dl + 3);
17265591b213SSam Leffler 			pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
17275591b213SSam Leffler 			pos &= 0x3f;
17285591b213SSam Leffler 			mfilt[pos / 32] |= (1 << (pos % 32));
17295591b213SSam Leffler 		}
173013b203d0SRobert Watson 		IF_ADDR_UNLOCK(ifp);
17315591b213SSam Leffler 	} else {
17325591b213SSam Leffler 		mfilt[0] = mfilt[1] = ~0;
17335591b213SSam Leffler 	}
17345591b213SSam Leffler 	ath_hal_setmcastfilter(ah, mfilt[0], mfilt[1]);
1735c42a7b7eSSam Leffler 	DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x, MC filter %08x:%08x\n",
1736c42a7b7eSSam Leffler 		__func__, rfilt, mfilt[0], mfilt[1]);
17375591b213SSam Leffler }
17385591b213SSam Leffler 
1739c42a7b7eSSam Leffler /*
1740c42a7b7eSSam Leffler  * Set the slot time based on the current setting.
1741c42a7b7eSSam Leffler  */
1742c42a7b7eSSam Leffler static void
1743c42a7b7eSSam Leffler ath_setslottime(struct ath_softc *sc)
1744c42a7b7eSSam Leffler {
1745c42a7b7eSSam Leffler 	struct ieee80211com *ic = &sc->sc_ic;
1746c42a7b7eSSam Leffler 	struct ath_hal *ah = sc->sc_ah;
1747c42a7b7eSSam Leffler 
1748c42a7b7eSSam Leffler 	if (ic->ic_flags & IEEE80211_F_SHSLOT)
1749c42a7b7eSSam Leffler 		ath_hal_setslottime(ah, HAL_SLOT_TIME_9);
1750c42a7b7eSSam Leffler 	else
1751c42a7b7eSSam Leffler 		ath_hal_setslottime(ah, HAL_SLOT_TIME_20);
1752c42a7b7eSSam Leffler 	sc->sc_updateslot = OK;
1753c42a7b7eSSam Leffler }
1754c42a7b7eSSam Leffler 
1755c42a7b7eSSam Leffler /*
1756c42a7b7eSSam Leffler  * Callback from the 802.11 layer to update the
1757c42a7b7eSSam Leffler  * slot time based on the current setting.
1758c42a7b7eSSam Leffler  */
1759c42a7b7eSSam Leffler static void
1760c42a7b7eSSam Leffler ath_updateslot(struct ifnet *ifp)
1761c42a7b7eSSam Leffler {
1762c42a7b7eSSam Leffler 	struct ath_softc *sc = ifp->if_softc;
1763c42a7b7eSSam Leffler 	struct ieee80211com *ic = &sc->sc_ic;
1764c42a7b7eSSam Leffler 
1765c42a7b7eSSam Leffler 	/*
1766c42a7b7eSSam Leffler 	 * When not coordinating the BSS, change the hardware
1767c42a7b7eSSam Leffler 	 * immediately.  For other operation we defer the change
1768c42a7b7eSSam Leffler 	 * until beacon updates have propagated to the stations.
1769c42a7b7eSSam Leffler 	 */
1770c42a7b7eSSam Leffler 	if (ic->ic_opmode == IEEE80211_M_HOSTAP)
1771c42a7b7eSSam Leffler 		sc->sc_updateslot = UPDATE;
1772c42a7b7eSSam Leffler 	else
1773c42a7b7eSSam Leffler 		ath_setslottime(sc);
1774c42a7b7eSSam Leffler }
1775c42a7b7eSSam Leffler 
1776c42a7b7eSSam Leffler /*
177780d2765fSSam Leffler  * Setup a h/w transmit queue for beacons.
177880d2765fSSam Leffler  */
177980d2765fSSam Leffler static int
178080d2765fSSam Leffler ath_beaconq_setup(struct ath_hal *ah)
178180d2765fSSam Leffler {
178280d2765fSSam Leffler 	HAL_TXQ_INFO qi;
178380d2765fSSam Leffler 
178480d2765fSSam Leffler 	memset(&qi, 0, sizeof(qi));
178580d2765fSSam Leffler 	qi.tqi_aifs = HAL_TXQ_USEDEFAULT;
178680d2765fSSam Leffler 	qi.tqi_cwmin = HAL_TXQ_USEDEFAULT;
178780d2765fSSam Leffler 	qi.tqi_cwmax = HAL_TXQ_USEDEFAULT;
17880f2e86fbSSam Leffler 	/* NB: for dynamic turbo, don't enable any other interrupts */
17890f2e86fbSSam Leffler 	qi.tqi_qflags = TXQ_FLAG_TXDESCINT_ENABLE;
179080d2765fSSam Leffler 	return ath_hal_setuptxqueue(ah, HAL_TX_QUEUE_BEACON, &qi);
179180d2765fSSam Leffler }
179280d2765fSSam Leffler 
179380d2765fSSam Leffler /*
17940f2e86fbSSam Leffler  * Setup the transmit queue parameters for the beacon queue.
17950f2e86fbSSam Leffler  */
17960f2e86fbSSam Leffler static int
17970f2e86fbSSam Leffler ath_beaconq_config(struct ath_softc *sc)
17980f2e86fbSSam Leffler {
17990f2e86fbSSam Leffler #define	ATH_EXPONENT_TO_VALUE(v)	((1<<(v))-1)
18000f2e86fbSSam Leffler 	struct ieee80211com *ic = &sc->sc_ic;
18010f2e86fbSSam Leffler 	struct ath_hal *ah = sc->sc_ah;
18020f2e86fbSSam Leffler 	HAL_TXQ_INFO qi;
18030f2e86fbSSam Leffler 
18040f2e86fbSSam Leffler 	ath_hal_gettxqueueprops(ah, sc->sc_bhalq, &qi);
18050f2e86fbSSam Leffler 	if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
18060f2e86fbSSam Leffler 		/*
18070f2e86fbSSam Leffler 		 * Always burst out beacon and CAB traffic.
18080f2e86fbSSam Leffler 		 */
18090f2e86fbSSam Leffler 		qi.tqi_aifs = ATH_BEACON_AIFS_DEFAULT;
18100f2e86fbSSam Leffler 		qi.tqi_cwmin = ATH_BEACON_CWMIN_DEFAULT;
18110f2e86fbSSam Leffler 		qi.tqi_cwmax = ATH_BEACON_CWMAX_DEFAULT;
18120f2e86fbSSam Leffler 	} else {
18130f2e86fbSSam Leffler 		struct wmeParams *wmep =
18140f2e86fbSSam Leffler 			&ic->ic_wme.wme_chanParams.cap_wmeParams[WME_AC_BE];
18150f2e86fbSSam Leffler 		/*
18160f2e86fbSSam Leffler 		 * Adhoc mode; important thing is to use 2x cwmin.
18170f2e86fbSSam Leffler 		 */
18180f2e86fbSSam Leffler 		qi.tqi_aifs = wmep->wmep_aifsn;
18190f2e86fbSSam Leffler 		qi.tqi_cwmin = 2*ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmin);
18200f2e86fbSSam Leffler 		qi.tqi_cwmax = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmax);
18210f2e86fbSSam Leffler 	}
18220f2e86fbSSam Leffler 
18230f2e86fbSSam Leffler 	if (!ath_hal_settxqueueprops(ah, sc->sc_bhalq, &qi)) {
18240f2e86fbSSam Leffler 		device_printf(sc->sc_dev, "unable to update parameters for "
18250f2e86fbSSam Leffler 			"beacon hardware queue!\n");
18260f2e86fbSSam Leffler 		return 0;
18270f2e86fbSSam Leffler 	} else {
18280f2e86fbSSam Leffler 		ath_hal_resettxqueue(ah, sc->sc_bhalq); /* push to h/w */
18290f2e86fbSSam Leffler 		return 1;
18300f2e86fbSSam Leffler 	}
18310f2e86fbSSam Leffler #undef ATH_EXPONENT_TO_VALUE
18320f2e86fbSSam Leffler }
18330f2e86fbSSam Leffler 
18340f2e86fbSSam Leffler /*
1835c42a7b7eSSam Leffler  * Allocate and setup an initial beacon frame.
1836c42a7b7eSSam Leffler  */
18375591b213SSam Leffler static int
18385591b213SSam Leffler ath_beacon_alloc(struct ath_softc *sc, struct ieee80211_node *ni)
18395591b213SSam Leffler {
1840c42a7b7eSSam Leffler 	struct ieee80211com *ic = ni->ni_ic;
18415591b213SSam Leffler 	struct ath_buf *bf;
18425591b213SSam Leffler 	struct mbuf *m;
1843c42a7b7eSSam Leffler 	int error;
18445591b213SSam Leffler 
1845c42a7b7eSSam Leffler 	bf = STAILQ_FIRST(&sc->sc_bbuf);
1846c42a7b7eSSam Leffler 	if (bf == NULL) {
1847c42a7b7eSSam Leffler 		DPRINTF(sc, ATH_DEBUG_BEACON, "%s: no dma buffers\n", __func__);
1848c42a7b7eSSam Leffler 		sc->sc_stats.ast_be_nombuf++;	/* XXX */
1849c42a7b7eSSam Leffler 		return ENOMEM;			/* XXX */
1850c42a7b7eSSam Leffler 	}
18515591b213SSam Leffler 	/*
18525591b213SSam Leffler 	 * NB: the beacon data buffer must be 32-bit aligned;
18535591b213SSam Leffler 	 * we assume the mbuf routines will return us something
18545591b213SSam Leffler 	 * with this alignment (perhaps should assert).
18555591b213SSam Leffler 	 */
1856c42a7b7eSSam Leffler 	m = ieee80211_beacon_alloc(ic, ni, &sc->sc_boff);
18575591b213SSam Leffler 	if (m == NULL) {
1858c42a7b7eSSam Leffler 		DPRINTF(sc, ATH_DEBUG_BEACON, "%s: cannot get mbuf\n",
1859c42a7b7eSSam Leffler 			__func__);
18605591b213SSam Leffler 		sc->sc_stats.ast_be_nombuf++;
18615591b213SSam Leffler 		return ENOMEM;
18625591b213SSam Leffler 	}
1863f9e6219bSSam Leffler 	error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m,
1864f9e6219bSSam Leffler 				     bf->bf_segs, &bf->bf_nseg,
18655591b213SSam Leffler 				     BUS_DMA_NOWAIT);
1866c42a7b7eSSam Leffler 	if (error == 0) {
1867c42a7b7eSSam Leffler 		bf->bf_m = m;
1868f818612bSSam Leffler 		bf->bf_node = ieee80211_ref_node(ni);
1869c42a7b7eSSam Leffler 	} else {
18705591b213SSam Leffler 		m_freem(m);
1871c42a7b7eSSam Leffler 	}
18725591b213SSam Leffler 	return error;
18735591b213SSam Leffler }
1874c42a7b7eSSam Leffler 
1875c42a7b7eSSam Leffler /*
1876c42a7b7eSSam Leffler  * Setup the beacon frame for transmit.
1877c42a7b7eSSam Leffler  */
1878c42a7b7eSSam Leffler static void
1879c42a7b7eSSam Leffler ath_beacon_setup(struct ath_softc *sc, struct ath_buf *bf)
1880c42a7b7eSSam Leffler {
1881c42a7b7eSSam Leffler #define	USE_SHPREAMBLE(_ic) \
1882c42a7b7eSSam Leffler 	(((_ic)->ic_flags & (IEEE80211_F_SHPREAMBLE | IEEE80211_F_USEBARKER))\
1883c42a7b7eSSam Leffler 		== IEEE80211_F_SHPREAMBLE)
1884c42a7b7eSSam Leffler 	struct ieee80211_node *ni = bf->bf_node;
1885c42a7b7eSSam Leffler 	struct ieee80211com *ic = ni->ni_ic;
1886c42a7b7eSSam Leffler 	struct mbuf *m = bf->bf_m;
1887c42a7b7eSSam Leffler 	struct ath_hal *ah = sc->sc_ah;
1888c42a7b7eSSam Leffler 	struct ath_desc *ds;
1889c42a7b7eSSam Leffler 	int flags, antenna;
189055f63772SSam Leffler 	const HAL_RATE_TABLE *rt;
189155f63772SSam Leffler 	u_int8_t rix, rate;
1892c42a7b7eSSam Leffler 
1893c42a7b7eSSam Leffler 	DPRINTF(sc, ATH_DEBUG_BEACON, "%s: m %p len %u\n",
1894c42a7b7eSSam Leffler 		__func__, m, m->m_len);
18955591b213SSam Leffler 
18965591b213SSam Leffler 	/* setup descriptors */
18975591b213SSam Leffler 	ds = bf->bf_desc;
18985591b213SSam Leffler 
1899c42a7b7eSSam Leffler 	flags = HAL_TXDESC_NOACK;
1900c42a7b7eSSam Leffler 	if (ic->ic_opmode == IEEE80211_M_IBSS && sc->sc_hasveol) {
1901c42a7b7eSSam Leffler 		ds->ds_link = bf->bf_daddr;	/* self-linked */
1902c42a7b7eSSam Leffler 		flags |= HAL_TXDESC_VEOL;
1903c42a7b7eSSam Leffler 		/*
1904c42a7b7eSSam Leffler 		 * Let hardware handle antenna switching.
1905c42a7b7eSSam Leffler 		 */
19064866e6c2SSam Leffler 		antenna = sc->sc_txantenna;
1907c42a7b7eSSam Leffler 	} else {
19085591b213SSam Leffler 		ds->ds_link = 0;
1909c42a7b7eSSam Leffler 		/*
1910c42a7b7eSSam Leffler 		 * Switch antenna every 4 beacons.
1911c42a7b7eSSam Leffler 		 * XXX assumes two antenna
1912c42a7b7eSSam Leffler 		 */
1913c42a7b7eSSam Leffler 		antenna = (sc->sc_stats.ast_be_xmit & 4 ? 2 : 1);
1914c42a7b7eSSam Leffler 	}
1915c42a7b7eSSam Leffler 
1916c42a7b7eSSam Leffler 	KASSERT(bf->bf_nseg == 1,
1917c42a7b7eSSam Leffler 		("multi-segment beacon frame; nseg %u", bf->bf_nseg));
19185591b213SSam Leffler 	ds->ds_data = bf->bf_segs[0].ds_addr;
19195591b213SSam Leffler 	/*
19205591b213SSam Leffler 	 * Calculate rate code.
19215591b213SSam Leffler 	 * XXX everything at min xmit rate
19225591b213SSam Leffler 	 */
192355f63772SSam Leffler 	rix = sc->sc_minrateix;
192455f63772SSam Leffler 	rt = sc->sc_currates;
192555f63772SSam Leffler 	rate = rt->info[rix].rateCode;
1926c42a7b7eSSam Leffler 	if (USE_SHPREAMBLE(ic))
192755f63772SSam Leffler 		rate |= rt->info[rix].shortPreamble;
19285591b213SSam Leffler 	ath_hal_setuptxdesc(ah, ds
1929c42a7b7eSSam Leffler 		, m->m_len + IEEE80211_CRC_LEN	/* frame length */
19305591b213SSam Leffler 		, sizeof(struct ieee80211_frame)/* header length */
19315591b213SSam Leffler 		, HAL_PKT_TYPE_BEACON		/* Atheros packet type */
1932c42a7b7eSSam Leffler 		, ni->ni_txpower		/* txpower XXX */
19335591b213SSam Leffler 		, rate, 1			/* series 0 rate/tries */
19345591b213SSam Leffler 		, HAL_TXKEYIX_INVALID		/* no encryption */
1935c42a7b7eSSam Leffler 		, antenna			/* antenna mode */
1936c42a7b7eSSam Leffler 		, flags				/* no ack, veol for beacons */
19375591b213SSam Leffler 		, 0				/* rts/cts rate */
19385591b213SSam Leffler 		, 0				/* rts/cts duration */
19395591b213SSam Leffler 	);
19405591b213SSam Leffler 	/* NB: beacon's BufLen must be a multiple of 4 bytes */
19415591b213SSam Leffler 	ath_hal_filltxdesc(ah, ds
1942c42a7b7eSSam Leffler 		, roundup(m->m_len, 4)		/* buffer length */
19435591b213SSam Leffler 		, AH_TRUE			/* first segment */
19445591b213SSam Leffler 		, AH_TRUE			/* last segment */
1945c42a7b7eSSam Leffler 		, ds				/* first descriptor */
19465591b213SSam Leffler 	);
1947c42a7b7eSSam Leffler #undef USE_SHPREAMBLE
19485591b213SSam Leffler }
19495591b213SSam Leffler 
1950c42a7b7eSSam Leffler /*
1951c42a7b7eSSam Leffler  * Transmit a beacon frame at SWBA.  Dynamic updates to the
1952c42a7b7eSSam Leffler  * frame contents are done as needed and the slot time is
1953c42a7b7eSSam Leffler  * also adjusted based on current state.
1954c42a7b7eSSam Leffler  */
19555591b213SSam Leffler static void
19565591b213SSam Leffler ath_beacon_proc(void *arg, int pending)
19575591b213SSam Leffler {
19585591b213SSam Leffler 	struct ath_softc *sc = arg;
1959c42a7b7eSSam Leffler 	struct ath_buf *bf = STAILQ_FIRST(&sc->sc_bbuf);
1960c42a7b7eSSam Leffler 	struct ieee80211_node *ni = bf->bf_node;
1961c42a7b7eSSam Leffler 	struct ieee80211com *ic = ni->ni_ic;
19625591b213SSam Leffler 	struct ath_hal *ah = sc->sc_ah;
1963c42a7b7eSSam Leffler 	struct mbuf *m;
1964c42a7b7eSSam Leffler 	int ncabq, error, otherant;
19655591b213SSam Leffler 
1966c42a7b7eSSam Leffler 	DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s: pending %u\n",
1967c42a7b7eSSam Leffler 		__func__, pending);
1968c42a7b7eSSam Leffler 
19690a915fadSSam Leffler 	if (ic->ic_opmode == IEEE80211_M_STA ||
1970c42a7b7eSSam Leffler 	    ic->ic_opmode == IEEE80211_M_MONITOR ||
19710a915fadSSam Leffler 	    bf == NULL || bf->bf_m == NULL) {
1972c42a7b7eSSam Leffler 		DPRINTF(sc, ATH_DEBUG_ANY, "%s: ic_flags=%x bf=%p bf_m=%p\n",
1973c42a7b7eSSam Leffler 			__func__, ic->ic_flags, bf, bf ? bf->bf_m : NULL);
19745591b213SSam Leffler 		return;
19755591b213SSam Leffler 	}
1976c42a7b7eSSam Leffler 	/*
1977c42a7b7eSSam Leffler 	 * Check if the previous beacon has gone out.  If
1978c42a7b7eSSam Leffler 	 * not don't don't try to post another, skip this
1979c42a7b7eSSam Leffler 	 * period and wait for the next.  Missed beacons
1980c42a7b7eSSam Leffler 	 * indicate a problem and should not occur.  If we
1981c42a7b7eSSam Leffler 	 * miss too many consecutive beacons reset the device.
1982c42a7b7eSSam Leffler 	 */
1983c42a7b7eSSam Leffler 	if (ath_hal_numtxpending(ah, sc->sc_bhalq) != 0) {
1984c42a7b7eSSam Leffler 		sc->sc_bmisscount++;
1985c42a7b7eSSam Leffler 		DPRINTF(sc, ATH_DEBUG_BEACON_PROC,
1986c42a7b7eSSam Leffler 			"%s: missed %u consecutive beacons\n",
1987c42a7b7eSSam Leffler 			__func__, sc->sc_bmisscount);
1988c42a7b7eSSam Leffler 		if (sc->sc_bmisscount > 3)		/* NB: 3 is a guess */
19890bbf5441SSam Leffler 			taskqueue_enqueue(sc->sc_tq, &sc->sc_bstucktask);
1990c42a7b7eSSam Leffler 		return;
1991c42a7b7eSSam Leffler 	}
1992c42a7b7eSSam Leffler 	if (sc->sc_bmisscount != 0) {
1993c42a7b7eSSam Leffler 		DPRINTF(sc, ATH_DEBUG_BEACON,
1994c42a7b7eSSam Leffler 			"%s: resume beacon xmit after %u misses\n",
1995c42a7b7eSSam Leffler 			__func__, sc->sc_bmisscount);
1996c42a7b7eSSam Leffler 		sc->sc_bmisscount = 0;
1997c42a7b7eSSam Leffler 	}
1998c42a7b7eSSam Leffler 
1999c42a7b7eSSam Leffler 	/*
2000c42a7b7eSSam Leffler 	 * Update dynamic beacon contents.  If this returns
2001c42a7b7eSSam Leffler 	 * non-zero then we need to remap the memory because
2002c42a7b7eSSam Leffler 	 * the beacon frame changed size (probably because
2003c42a7b7eSSam Leffler 	 * of the TIM bitmap).
2004c42a7b7eSSam Leffler 	 */
2005c42a7b7eSSam Leffler 	m = bf->bf_m;
2006c42a7b7eSSam Leffler 	ncabq = ath_hal_numtxpending(ah, sc->sc_cabq->axq_qnum);
2007c42a7b7eSSam Leffler 	if (ieee80211_beacon_update(ic, bf->bf_node, &sc->sc_boff, m, ncabq)) {
2008c42a7b7eSSam Leffler 		/* XXX too conservative? */
2009c42a7b7eSSam Leffler 		bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
2010f9e6219bSSam Leffler 		error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m,
2011f9e6219bSSam Leffler 					     bf->bf_segs, &bf->bf_nseg,
2012c42a7b7eSSam Leffler 					     BUS_DMA_NOWAIT);
2013c42a7b7eSSam Leffler 		if (error != 0) {
2014c42a7b7eSSam Leffler 			if_printf(ic->ic_ifp,
2015f9e6219bSSam Leffler 			    "%s: bus_dmamap_load_mbuf_sg failed, error %u\n",
2016c42a7b7eSSam Leffler 			    __func__, error);
2017c42a7b7eSSam Leffler 			return;
2018c42a7b7eSSam Leffler 		}
2019c42a7b7eSSam Leffler 	}
2020c42a7b7eSSam Leffler 
2021c42a7b7eSSam Leffler 	/*
2022c42a7b7eSSam Leffler 	 * Handle slot time change when a non-ERP station joins/leaves
2023c42a7b7eSSam Leffler 	 * an 11g network.  The 802.11 layer notifies us via callback,
2024c42a7b7eSSam Leffler 	 * we mark updateslot, then wait one beacon before effecting
2025c42a7b7eSSam Leffler 	 * the change.  This gives associated stations at least one
2026c42a7b7eSSam Leffler 	 * beacon interval to note the state change.
2027c42a7b7eSSam Leffler 	 */
2028c42a7b7eSSam Leffler 	/* XXX locking */
2029c42a7b7eSSam Leffler 	if (sc->sc_updateslot == UPDATE)
2030c42a7b7eSSam Leffler 		sc->sc_updateslot = COMMIT;	/* commit next beacon */
2031c42a7b7eSSam Leffler 	else if (sc->sc_updateslot == COMMIT)
2032c42a7b7eSSam Leffler 		ath_setslottime(sc);		/* commit change to h/w */
2033c42a7b7eSSam Leffler 
2034c42a7b7eSSam Leffler 	/*
2035c42a7b7eSSam Leffler 	 * Check recent per-antenna transmit statistics and flip
2036c42a7b7eSSam Leffler 	 * the default antenna if noticeably more frames went out
2037c42a7b7eSSam Leffler 	 * on the non-default antenna.
2038c42a7b7eSSam Leffler 	 * XXX assumes 2 anntenae
2039c42a7b7eSSam Leffler 	 */
2040c42a7b7eSSam Leffler 	otherant = sc->sc_defant & 1 ? 2 : 1;
2041c42a7b7eSSam Leffler 	if (sc->sc_ant_tx[otherant] > sc->sc_ant_tx[sc->sc_defant] + 2)
2042c42a7b7eSSam Leffler 		ath_setdefantenna(sc, otherant);
2043c42a7b7eSSam Leffler 	sc->sc_ant_tx[1] = sc->sc_ant_tx[2] = 0;
2044c42a7b7eSSam Leffler 
2045c42a7b7eSSam Leffler 	/*
2046c42a7b7eSSam Leffler 	 * Construct tx descriptor.
2047c42a7b7eSSam Leffler 	 */
2048c42a7b7eSSam Leffler 	ath_beacon_setup(sc, bf);
2049c42a7b7eSSam Leffler 
2050c42a7b7eSSam Leffler 	/*
2051c42a7b7eSSam Leffler 	 * Stop any current dma and put the new frame on the queue.
2052c42a7b7eSSam Leffler 	 * This should never fail since we check above that no frames
2053c42a7b7eSSam Leffler 	 * are still pending on the queue.
2054c42a7b7eSSam Leffler 	 */
20555591b213SSam Leffler 	if (!ath_hal_stoptxdma(ah, sc->sc_bhalq)) {
2056c42a7b7eSSam Leffler 		DPRINTF(sc, ATH_DEBUG_ANY,
2057c42a7b7eSSam Leffler 			"%s: beacon queue %u did not stop?\n",
2058c42a7b7eSSam Leffler 			__func__, sc->sc_bhalq);
20595591b213SSam Leffler 	}
20605591b213SSam Leffler 	bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE);
20615591b213SSam Leffler 
2062c42a7b7eSSam Leffler 	/*
2063c42a7b7eSSam Leffler 	 * Enable the CAB queue before the beacon queue to
2064c42a7b7eSSam Leffler 	 * insure cab frames are triggered by this beacon.
2065c42a7b7eSSam Leffler 	 */
20662c27b2f6SSam Leffler 	if (ncabq != 0 && (sc->sc_boff.bo_tim[4] & 1))	/* NB: only at DTIM */
2067c42a7b7eSSam Leffler 		ath_hal_txstart(ah, sc->sc_cabq->axq_qnum);
20685591b213SSam Leffler 	ath_hal_puttxbuf(ah, sc->sc_bhalq, bf->bf_daddr);
20695591b213SSam Leffler 	ath_hal_txstart(ah, sc->sc_bhalq);
2070c42a7b7eSSam Leffler 	DPRINTF(sc, ATH_DEBUG_BEACON_PROC,
2071c42a7b7eSSam Leffler 		"%s: TXDP[%u] = %p (%p)\n", __func__,
2072c42a7b7eSSam Leffler 		sc->sc_bhalq, (caddr_t)bf->bf_daddr, bf->bf_desc);
2073c42a7b7eSSam Leffler 
2074c42a7b7eSSam Leffler 	sc->sc_stats.ast_be_xmit++;
20755591b213SSam Leffler }
20765591b213SSam Leffler 
2077c42a7b7eSSam Leffler /*
2078c42a7b7eSSam Leffler  * Reset the hardware after detecting beacons have stopped.
2079c42a7b7eSSam Leffler  */
2080c42a7b7eSSam Leffler static void
2081c42a7b7eSSam Leffler ath_bstuck_proc(void *arg, int pending)
2082c42a7b7eSSam Leffler {
2083c42a7b7eSSam Leffler 	struct ath_softc *sc = arg;
2084fc74a9f9SBrooks Davis 	struct ifnet *ifp = sc->sc_ifp;
2085c42a7b7eSSam Leffler 
2086c42a7b7eSSam Leffler 	if_printf(ifp, "stuck beacon; resetting (bmiss count %u)\n",
2087c42a7b7eSSam Leffler 		sc->sc_bmisscount);
2088c42a7b7eSSam Leffler 	ath_reset(ifp);
2089c42a7b7eSSam Leffler }
2090c42a7b7eSSam Leffler 
2091c42a7b7eSSam Leffler /*
2092c42a7b7eSSam Leffler  * Reclaim beacon resources.
2093c42a7b7eSSam Leffler  */
20945591b213SSam Leffler static void
20955591b213SSam Leffler ath_beacon_free(struct ath_softc *sc)
20965591b213SSam Leffler {
2097c42a7b7eSSam Leffler 	struct ath_buf *bf;
20985591b213SSam Leffler 
2099f818612bSSam Leffler 	STAILQ_FOREACH(bf, &sc->sc_bbuf, bf_list) {
21005591b213SSam Leffler 		if (bf->bf_m != NULL) {
21015591b213SSam Leffler 			bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
21025591b213SSam Leffler 			m_freem(bf->bf_m);
21035591b213SSam Leffler 			bf->bf_m = NULL;
2104f818612bSSam Leffler 		}
2105f818612bSSam Leffler 		if (bf->bf_node != NULL) {
2106f818612bSSam Leffler 			ieee80211_free_node(bf->bf_node);
21075591b213SSam Leffler 			bf->bf_node = NULL;
21085591b213SSam Leffler 		}
21095591b213SSam Leffler 	}
2110f818612bSSam Leffler }
21115591b213SSam Leffler 
21125591b213SSam Leffler /*
21135591b213SSam Leffler  * Configure the beacon and sleep timers.
21145591b213SSam Leffler  *
21155591b213SSam Leffler  * When operating as an AP this resets the TSF and sets
21165591b213SSam Leffler  * up the hardware to notify us when we need to issue beacons.
21175591b213SSam Leffler  *
21185591b213SSam Leffler  * When operating in station mode this sets up the beacon
21195591b213SSam Leffler  * timers according to the timestamp of the last received
21205591b213SSam Leffler  * beacon and the current TSF, configures PCF and DTIM
21215591b213SSam Leffler  * handling, programs the sleep registers so the hardware
21225591b213SSam Leffler  * will wakeup in time to receive beacons, and configures
21235591b213SSam Leffler  * the beacon miss handling so we'll receive a BMISS
21245591b213SSam Leffler  * interrupt when we stop seeing beacons from the AP
21255591b213SSam Leffler  * we've associated with.
21265591b213SSam Leffler  */
21275591b213SSam Leffler static void
21285591b213SSam Leffler ath_beacon_config(struct ath_softc *sc)
21295591b213SSam Leffler {
213080d939bfSSam Leffler #define	TSF_TO_TU(_h,_l) \
213180d939bfSSam Leffler 	((((u_int32_t)(_h)) << 22) | (((u_int32_t)(_l)) >> 10))
213280d939bfSSam Leffler #define	FUDGE	2
21335591b213SSam Leffler 	struct ath_hal *ah = sc->sc_ah;
21345591b213SSam Leffler 	struct ieee80211com *ic = &sc->sc_ic;
21355591b213SSam Leffler 	struct ieee80211_node *ni = ic->ic_bss;
213680d939bfSSam Leffler 	u_int32_t nexttbtt, intval, tsftu;
213780d939bfSSam Leffler 	u_int64_t tsf;
21385591b213SSam Leffler 
21398371372bSSam Leffler 	/* extract tstamp from last beacon and convert to TU */
21408371372bSSam Leffler 	nexttbtt = TSF_TO_TU(LE_READ_4(ni->ni_tstamp.data + 4),
21418371372bSSam Leffler 			     LE_READ_4(ni->ni_tstamp.data));
21428371372bSSam Leffler 	/* NB: the beacon interval is kept internally in TU's */
21434bacf7c1SSam Leffler 	intval = ni->ni_intval & HAL_BEACON_PERIOD;
2144a6c992f4SSam Leffler 	if (nexttbtt == 0)		/* e.g. for ap mode */
2145a6c992f4SSam Leffler 		nexttbtt = intval;
2146a6c992f4SSam Leffler 	else if (intval)		/* NB: can be 0 for monitor mode */
2147a6c992f4SSam Leffler 		nexttbtt = roundup(nexttbtt, intval);
2148a6c992f4SSam Leffler 	DPRINTF(sc, ATH_DEBUG_BEACON, "%s: nexttbtt %u intval %u (%u)\n",
2149a6c992f4SSam Leffler 		__func__, nexttbtt, intval, ni->ni_intval);
21506b59f5e3SSam Leffler 	if (ic->ic_opmode == IEEE80211_M_STA) {
21515591b213SSam Leffler 		HAL_BEACON_STATE bs;
21528371372bSSam Leffler 		int dtimperiod, dtimcount;
21538371372bSSam Leffler 		int cfpperiod, cfpcount;
21545591b213SSam Leffler 
21558371372bSSam Leffler 		/*
21568371372bSSam Leffler 		 * Setup dtim and cfp parameters according to
21578371372bSSam Leffler 		 * last beacon we received (which may be none).
21588371372bSSam Leffler 		 */
21598371372bSSam Leffler 		dtimperiod = ni->ni_dtim_period;
21608371372bSSam Leffler 		if (dtimperiod <= 0)		/* NB: 0 if not known */
21618371372bSSam Leffler 			dtimperiod = 1;
21628371372bSSam Leffler 		dtimcount = ni->ni_dtim_count;
21638371372bSSam Leffler 		if (dtimcount >= dtimperiod)	/* NB: sanity check */
21648371372bSSam Leffler 			dtimcount = 0;		/* XXX? */
21658371372bSSam Leffler 		cfpperiod = 1;			/* NB: no PCF support yet */
21668371372bSSam Leffler 		cfpcount = 0;
21678371372bSSam Leffler 		/*
21688371372bSSam Leffler 		 * Pull nexttbtt forward to reflect the current
21698371372bSSam Leffler 		 * TSF and calculate dtim+cfp state for the result.
21708371372bSSam Leffler 		 */
21718371372bSSam Leffler 		tsf = ath_hal_gettsf64(ah);
217280d939bfSSam Leffler 		tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE;
21738371372bSSam Leffler 		do {
21748371372bSSam Leffler 			nexttbtt += intval;
21758371372bSSam Leffler 			if (--dtimcount < 0) {
21768371372bSSam Leffler 				dtimcount = dtimperiod - 1;
21778371372bSSam Leffler 				if (--cfpcount < 0)
21788371372bSSam Leffler 					cfpcount = cfpperiod - 1;
21798371372bSSam Leffler 			}
21808371372bSSam Leffler 		} while (nexttbtt < tsftu);
21815591b213SSam Leffler 		memset(&bs, 0, sizeof(bs));
2182a6c992f4SSam Leffler 		bs.bs_intval = intval;
21835591b213SSam Leffler 		bs.bs_nexttbtt = nexttbtt;
21848371372bSSam Leffler 		bs.bs_dtimperiod = dtimperiod*intval;
21858371372bSSam Leffler 		bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval;
21868371372bSSam Leffler 		bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod;
21878371372bSSam Leffler 		bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod;
21888371372bSSam Leffler 		bs.bs_cfpmaxduration = 0;
21898371372bSSam Leffler #if 0
21905591b213SSam Leffler 		/*
2191c42a7b7eSSam Leffler 		 * The 802.11 layer records the offset to the DTIM
2192c42a7b7eSSam Leffler 		 * bitmap while receiving beacons; use it here to
2193c42a7b7eSSam Leffler 		 * enable h/w detection of our AID being marked in
2194c42a7b7eSSam Leffler 		 * the bitmap vector (to indicate frames for us are
2195c42a7b7eSSam Leffler 		 * pending at the AP).
21968371372bSSam Leffler 		 * XXX do DTIM handling in s/w to WAR old h/w bugs
21978371372bSSam Leffler 		 * XXX enable based on h/w rev for newer chips
2198c42a7b7eSSam Leffler 		 */
2199c42a7b7eSSam Leffler 		bs.bs_timoffset = ni->ni_timoff;
22008371372bSSam Leffler #endif
2201c42a7b7eSSam Leffler 		/*
22025591b213SSam Leffler 		 * Calculate the number of consecutive beacons to miss
22035591b213SSam Leffler 		 * before taking a BMISS interrupt.  The configuration
22045591b213SSam Leffler 		 * is specified in ms, so we need to convert that to
22055591b213SSam Leffler 		 * TU's and then calculate based on the beacon interval.
22065591b213SSam Leffler 		 * Note that we clamp the result to at most 10 beacons.
22075591b213SSam Leffler 		 */
2208b9919097SSam Leffler 		bs.bs_bmissthreshold = ic->ic_bmissthreshold;
22095591b213SSam Leffler 		if (bs.bs_bmissthreshold > 10)
22105591b213SSam Leffler 			bs.bs_bmissthreshold = 10;
22115591b213SSam Leffler 		else if (bs.bs_bmissthreshold <= 0)
22125591b213SSam Leffler 			bs.bs_bmissthreshold = 1;
22135591b213SSam Leffler 
22145591b213SSam Leffler 		/*
22155591b213SSam Leffler 		 * Calculate sleep duration.  The configuration is
22165591b213SSam Leffler 		 * given in ms.  We insure a multiple of the beacon
22175591b213SSam Leffler 		 * period is used.  Also, if the sleep duration is
22185591b213SSam Leffler 		 * greater than the DTIM period then it makes senses
22195591b213SSam Leffler 		 * to make it a multiple of that.
22205591b213SSam Leffler 		 *
22215591b213SSam Leffler 		 * XXX fixed at 100ms
22225591b213SSam Leffler 		 */
22234bacf7c1SSam Leffler 		bs.bs_sleepduration =
22244bacf7c1SSam Leffler 			roundup(IEEE80211_MS_TO_TU(100), bs.bs_intval);
22255591b213SSam Leffler 		if (bs.bs_sleepduration > bs.bs_dtimperiod)
22265591b213SSam Leffler 			bs.bs_sleepduration = roundup(bs.bs_sleepduration, bs.bs_dtimperiod);
22275591b213SSam Leffler 
2228c42a7b7eSSam Leffler 		DPRINTF(sc, ATH_DEBUG_BEACON,
22298371372bSSam Leffler 			"%s: tsf %ju tsf:tu %u intval %u nexttbtt %u dtim %u nextdtim %u bmiss %u sleep %u cfp:period %u maxdur %u next %u timoffset %u\n"
22305591b213SSam Leffler 			, __func__
22318371372bSSam Leffler 			, tsf, tsftu
22325591b213SSam Leffler 			, bs.bs_intval
22335591b213SSam Leffler 			, bs.bs_nexttbtt
22345591b213SSam Leffler 			, bs.bs_dtimperiod
22355591b213SSam Leffler 			, bs.bs_nextdtim
22365591b213SSam Leffler 			, bs.bs_bmissthreshold
22375591b213SSam Leffler 			, bs.bs_sleepduration
2238c42a7b7eSSam Leffler 			, bs.bs_cfpperiod
2239c42a7b7eSSam Leffler 			, bs.bs_cfpmaxduration
2240c42a7b7eSSam Leffler 			, bs.bs_cfpnext
2241c42a7b7eSSam Leffler 			, bs.bs_timoffset
2242c42a7b7eSSam Leffler 		);
22435591b213SSam Leffler 		ath_hal_intrset(ah, 0);
2244c42a7b7eSSam Leffler 		ath_hal_beacontimers(ah, &bs);
22455591b213SSam Leffler 		sc->sc_imask |= HAL_INT_BMISS;
22465591b213SSam Leffler 		ath_hal_intrset(ah, sc->sc_imask);
22475591b213SSam Leffler 	} else {
22485591b213SSam Leffler 		ath_hal_intrset(ah, 0);
2249a6c992f4SSam Leffler 		if (nexttbtt == intval)
2250c42a7b7eSSam Leffler 			intval |= HAL_BEACON_RESET_TSF;
2251c42a7b7eSSam Leffler 		if (ic->ic_opmode == IEEE80211_M_IBSS) {
2252c42a7b7eSSam Leffler 			/*
2253c42a7b7eSSam Leffler 			 * In IBSS mode enable the beacon timers but only
2254c42a7b7eSSam Leffler 			 * enable SWBA interrupts if we need to manually
2255c42a7b7eSSam Leffler 			 * prepare beacon frames.  Otherwise we use a
2256c42a7b7eSSam Leffler 			 * self-linked tx descriptor and let the hardware
2257c42a7b7eSSam Leffler 			 * deal with things.
2258c42a7b7eSSam Leffler 			 */
2259c42a7b7eSSam Leffler 			intval |= HAL_BEACON_ENA;
2260c42a7b7eSSam Leffler 			if (!sc->sc_hasveol)
2261c42a7b7eSSam Leffler 				sc->sc_imask |= HAL_INT_SWBA;
226280d939bfSSam Leffler 			if ((intval & HAL_BEACON_RESET_TSF) == 0) {
226380d939bfSSam Leffler 				/*
226480d939bfSSam Leffler 				 * Pull nexttbtt forward to reflect
226580d939bfSSam Leffler 				 * the current TSF.
226680d939bfSSam Leffler 				 */
226780d939bfSSam Leffler 				tsf = ath_hal_gettsf64(ah);
226880d939bfSSam Leffler 				tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE;
226980d939bfSSam Leffler 				do {
227080d939bfSSam Leffler 					nexttbtt += intval;
227180d939bfSSam Leffler 				} while (nexttbtt < tsftu);
227280d939bfSSam Leffler 			}
22730f2e86fbSSam Leffler 			ath_beaconq_config(sc);
2274c42a7b7eSSam Leffler 		} else if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
2275c42a7b7eSSam Leffler 			/*
2276c42a7b7eSSam Leffler 			 * In AP mode we enable the beacon timers and
2277c42a7b7eSSam Leffler 			 * SWBA interrupts to prepare beacon frames.
2278c42a7b7eSSam Leffler 			 */
2279c42a7b7eSSam Leffler 			intval |= HAL_BEACON_ENA;
22805591b213SSam Leffler 			sc->sc_imask |= HAL_INT_SWBA;	/* beacon prepare */
22810f2e86fbSSam Leffler 			ath_beaconq_config(sc);
2282c42a7b7eSSam Leffler 		}
2283c42a7b7eSSam Leffler 		ath_hal_beaconinit(ah, nexttbtt, intval);
2284c42a7b7eSSam Leffler 		sc->sc_bmisscount = 0;
22855591b213SSam Leffler 		ath_hal_intrset(ah, sc->sc_imask);
2286c42a7b7eSSam Leffler 		/*
2287c42a7b7eSSam Leffler 		 * When using a self-linked beacon descriptor in
2288c42a7b7eSSam Leffler 		 * ibss mode load it once here.
2289c42a7b7eSSam Leffler 		 */
2290c42a7b7eSSam Leffler 		if (ic->ic_opmode == IEEE80211_M_IBSS && sc->sc_hasveol)
2291c42a7b7eSSam Leffler 			ath_beacon_proc(sc, 0);
22925591b213SSam Leffler 	}
229380d939bfSSam Leffler 	sc->sc_syncbeacon = 0;
229480d939bfSSam Leffler #undef FUDGE
22958371372bSSam Leffler #undef TSF_TO_TU
22965591b213SSam Leffler }
22975591b213SSam Leffler 
22985591b213SSam Leffler static void
22995591b213SSam Leffler ath_load_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
23005591b213SSam Leffler {
23015591b213SSam Leffler 	bus_addr_t *paddr = (bus_addr_t*) arg;
2302d77367bfSSam Leffler 	KASSERT(error == 0, ("error %u on bus_dma callback", error));
23035591b213SSam Leffler 	*paddr = segs->ds_addr;
23045591b213SSam Leffler }
23055591b213SSam Leffler 
23065591b213SSam Leffler static int
2307c42a7b7eSSam Leffler ath_descdma_setup(struct ath_softc *sc,
2308c42a7b7eSSam Leffler 	struct ath_descdma *dd, ath_bufhead *head,
2309c42a7b7eSSam Leffler 	const char *name, int nbuf, int ndesc)
2310c42a7b7eSSam Leffler {
2311c42a7b7eSSam Leffler #define	DS2PHYS(_dd, _ds) \
2312c42a7b7eSSam Leffler 	((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc))
2313fc74a9f9SBrooks Davis 	struct ifnet *ifp = sc->sc_ifp;
2314c42a7b7eSSam Leffler 	struct ath_desc *ds;
2315c42a7b7eSSam Leffler 	struct ath_buf *bf;
2316c42a7b7eSSam Leffler 	int i, bsize, error;
2317c42a7b7eSSam Leffler 
2318c42a7b7eSSam Leffler 	DPRINTF(sc, ATH_DEBUG_RESET, "%s: %s DMA: %u buffers %u desc/buf\n",
2319c42a7b7eSSam Leffler 	    __func__, name, nbuf, ndesc);
2320c42a7b7eSSam Leffler 
2321c42a7b7eSSam Leffler 	dd->dd_name = name;
2322c42a7b7eSSam Leffler 	dd->dd_desc_len = sizeof(struct ath_desc) * nbuf * ndesc;
2323c42a7b7eSSam Leffler 
2324c42a7b7eSSam Leffler 	/*
2325c42a7b7eSSam Leffler 	 * Setup DMA descriptor area.
2326c42a7b7eSSam Leffler 	 */
2327c42a7b7eSSam Leffler 	error = bus_dma_tag_create(NULL,	/* parent */
2328c42a7b7eSSam Leffler 		       PAGE_SIZE, 0,		/* alignment, bounds */
2329c42a7b7eSSam Leffler 		       BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
2330c42a7b7eSSam Leffler 		       BUS_SPACE_MAXADDR,	/* highaddr */
2331c42a7b7eSSam Leffler 		       NULL, NULL,		/* filter, filterarg */
2332c42a7b7eSSam Leffler 		       dd->dd_desc_len,		/* maxsize */
2333c42a7b7eSSam Leffler 		       1,			/* nsegments */
2334c42a7b7eSSam Leffler 		       BUS_SPACE_MAXADDR,	/* maxsegsize */
2335c42a7b7eSSam Leffler 		       BUS_DMA_ALLOCNOW,	/* flags */
2336c42a7b7eSSam Leffler 		       NULL,			/* lockfunc */
2337c42a7b7eSSam Leffler 		       NULL,			/* lockarg */
2338c42a7b7eSSam Leffler 		       &dd->dd_dmat);
2339c42a7b7eSSam Leffler 	if (error != 0) {
2340c42a7b7eSSam Leffler 		if_printf(ifp, "cannot allocate %s DMA tag\n", dd->dd_name);
2341c42a7b7eSSam Leffler 		return error;
2342c42a7b7eSSam Leffler 	}
2343c42a7b7eSSam Leffler 
2344c42a7b7eSSam Leffler 	/* allocate descriptors */
2345c42a7b7eSSam Leffler 	error = bus_dmamap_create(dd->dd_dmat, BUS_DMA_NOWAIT, &dd->dd_dmamap);
2346c42a7b7eSSam Leffler 	if (error != 0) {
2347c42a7b7eSSam Leffler 		if_printf(ifp, "unable to create dmamap for %s descriptors, "
2348c42a7b7eSSam Leffler 			"error %u\n", dd->dd_name, error);
2349c42a7b7eSSam Leffler 		goto fail0;
2350c42a7b7eSSam Leffler 	}
2351c42a7b7eSSam Leffler 
2352c42a7b7eSSam Leffler 	error = bus_dmamem_alloc(dd->dd_dmat, (void**) &dd->dd_desc,
2353c42a7b7eSSam Leffler 				 BUS_DMA_NOWAIT, &dd->dd_dmamap);
2354c42a7b7eSSam Leffler 	if (error != 0) {
2355c42a7b7eSSam Leffler 		if_printf(ifp, "unable to alloc memory for %u %s descriptors, "
2356c42a7b7eSSam Leffler 			"error %u\n", nbuf * ndesc, dd->dd_name, error);
2357c42a7b7eSSam Leffler 		goto fail1;
2358c42a7b7eSSam Leffler 	}
2359c42a7b7eSSam Leffler 
2360c42a7b7eSSam Leffler 	error = bus_dmamap_load(dd->dd_dmat, dd->dd_dmamap,
2361c42a7b7eSSam Leffler 				dd->dd_desc, dd->dd_desc_len,
2362c42a7b7eSSam Leffler 				ath_load_cb, &dd->dd_desc_paddr,
2363c42a7b7eSSam Leffler 				BUS_DMA_NOWAIT);
2364c42a7b7eSSam Leffler 	if (error != 0) {
2365c42a7b7eSSam Leffler 		if_printf(ifp, "unable to map %s descriptors, error %u\n",
2366c42a7b7eSSam Leffler 			dd->dd_name, error);
2367c42a7b7eSSam Leffler 		goto fail2;
2368c42a7b7eSSam Leffler 	}
2369c42a7b7eSSam Leffler 
2370c42a7b7eSSam Leffler 	ds = dd->dd_desc;
2371c42a7b7eSSam Leffler 	DPRINTF(sc, ATH_DEBUG_RESET, "%s: %s DMA map: %p (%lu) -> %p (%lu)\n",
2372c42a7b7eSSam Leffler 	    __func__, dd->dd_name, ds, (u_long) dd->dd_desc_len,
2373c42a7b7eSSam Leffler 	    (caddr_t) dd->dd_desc_paddr, /*XXX*/ (u_long) dd->dd_desc_len);
2374c42a7b7eSSam Leffler 
2375c42a7b7eSSam Leffler 	/* allocate rx buffers */
2376c42a7b7eSSam Leffler 	bsize = sizeof(struct ath_buf) * nbuf;
2377c42a7b7eSSam Leffler 	bf = malloc(bsize, M_ATHDEV, M_NOWAIT | M_ZERO);
2378c42a7b7eSSam Leffler 	if (bf == NULL) {
2379c42a7b7eSSam Leffler 		if_printf(ifp, "malloc of %s buffers failed, size %u\n",
2380c42a7b7eSSam Leffler 			dd->dd_name, bsize);
2381c42a7b7eSSam Leffler 		goto fail3;
2382c42a7b7eSSam Leffler 	}
2383c42a7b7eSSam Leffler 	dd->dd_bufptr = bf;
2384c42a7b7eSSam Leffler 
2385c42a7b7eSSam Leffler 	STAILQ_INIT(head);
2386c42a7b7eSSam Leffler 	for (i = 0; i < nbuf; i++, bf++, ds += ndesc) {
2387c42a7b7eSSam Leffler 		bf->bf_desc = ds;
2388c42a7b7eSSam Leffler 		bf->bf_daddr = DS2PHYS(dd, ds);
2389c42a7b7eSSam Leffler 		error = bus_dmamap_create(sc->sc_dmat, BUS_DMA_NOWAIT,
2390c42a7b7eSSam Leffler 				&bf->bf_dmamap);
2391c42a7b7eSSam Leffler 		if (error != 0) {
2392c42a7b7eSSam Leffler 			if_printf(ifp, "unable to create dmamap for %s "
2393c42a7b7eSSam Leffler 				"buffer %u, error %u\n", dd->dd_name, i, error);
2394c42a7b7eSSam Leffler 			ath_descdma_cleanup(sc, dd, head);
2395c42a7b7eSSam Leffler 			return error;
2396c42a7b7eSSam Leffler 		}
2397c42a7b7eSSam Leffler 		STAILQ_INSERT_TAIL(head, bf, bf_list);
2398c42a7b7eSSam Leffler 	}
2399c42a7b7eSSam Leffler 	return 0;
2400c42a7b7eSSam Leffler fail3:
2401c42a7b7eSSam Leffler 	bus_dmamap_unload(dd->dd_dmat, dd->dd_dmamap);
2402c42a7b7eSSam Leffler fail2:
2403c42a7b7eSSam Leffler 	bus_dmamem_free(dd->dd_dmat, dd->dd_desc, dd->dd_dmamap);
2404c42a7b7eSSam Leffler fail1:
2405c42a7b7eSSam Leffler 	bus_dmamap_destroy(dd->dd_dmat, dd->dd_dmamap);
2406c42a7b7eSSam Leffler fail0:
2407c42a7b7eSSam Leffler 	bus_dma_tag_destroy(dd->dd_dmat);
2408c42a7b7eSSam Leffler 	memset(dd, 0, sizeof(*dd));
2409c42a7b7eSSam Leffler 	return error;
2410c42a7b7eSSam Leffler #undef DS2PHYS
2411c42a7b7eSSam Leffler }
2412c42a7b7eSSam Leffler 
2413c42a7b7eSSam Leffler static void
2414c42a7b7eSSam Leffler ath_descdma_cleanup(struct ath_softc *sc,
2415c42a7b7eSSam Leffler 	struct ath_descdma *dd, ath_bufhead *head)
2416c42a7b7eSSam Leffler {
2417c42a7b7eSSam Leffler 	struct ath_buf *bf;
2418c42a7b7eSSam Leffler 	struct ieee80211_node *ni;
2419c42a7b7eSSam Leffler 
2420c42a7b7eSSam Leffler 	bus_dmamap_unload(dd->dd_dmat, dd->dd_dmamap);
2421c42a7b7eSSam Leffler 	bus_dmamem_free(dd->dd_dmat, dd->dd_desc, dd->dd_dmamap);
2422c42a7b7eSSam Leffler 	bus_dmamap_destroy(dd->dd_dmat, dd->dd_dmamap);
2423c42a7b7eSSam Leffler 	bus_dma_tag_destroy(dd->dd_dmat);
2424c42a7b7eSSam Leffler 
2425c42a7b7eSSam Leffler 	STAILQ_FOREACH(bf, head, bf_list) {
2426c42a7b7eSSam Leffler 		if (bf->bf_m) {
2427c42a7b7eSSam Leffler 			m_freem(bf->bf_m);
2428c42a7b7eSSam Leffler 			bf->bf_m = NULL;
2429c42a7b7eSSam Leffler 		}
2430c42a7b7eSSam Leffler 		if (bf->bf_dmamap != NULL) {
2431c42a7b7eSSam Leffler 			bus_dmamap_destroy(sc->sc_dmat, bf->bf_dmamap);
2432c42a7b7eSSam Leffler 			bf->bf_dmamap = NULL;
2433c42a7b7eSSam Leffler 		}
2434c42a7b7eSSam Leffler 		ni = bf->bf_node;
2435c42a7b7eSSam Leffler 		bf->bf_node = NULL;
2436c42a7b7eSSam Leffler 		if (ni != NULL) {
2437c42a7b7eSSam Leffler 			/*
2438c42a7b7eSSam Leffler 			 * Reclaim node reference.
2439c42a7b7eSSam Leffler 			 */
2440c42a7b7eSSam Leffler 			ieee80211_free_node(ni);
2441c42a7b7eSSam Leffler 		}
2442c42a7b7eSSam Leffler 	}
2443c42a7b7eSSam Leffler 
2444c42a7b7eSSam Leffler 	STAILQ_INIT(head);
2445c42a7b7eSSam Leffler 	free(dd->dd_bufptr, M_ATHDEV);
2446c42a7b7eSSam Leffler 	memset(dd, 0, sizeof(*dd));
2447c42a7b7eSSam Leffler }
2448c42a7b7eSSam Leffler 
2449c42a7b7eSSam Leffler static int
24505591b213SSam Leffler ath_desc_alloc(struct ath_softc *sc)
24515591b213SSam Leffler {
2452c42a7b7eSSam Leffler 	int error;
24535591b213SSam Leffler 
2454c42a7b7eSSam Leffler 	error = ath_descdma_setup(sc, &sc->sc_rxdma, &sc->sc_rxbuf,
2455e2d787faSSam Leffler 			"rx", ath_rxbuf, 1);
24565591b213SSam Leffler 	if (error != 0)
24575591b213SSam Leffler 		return error;
24585591b213SSam Leffler 
2459c42a7b7eSSam Leffler 	error = ath_descdma_setup(sc, &sc->sc_txdma, &sc->sc_txbuf,
2460e2d787faSSam Leffler 			"tx", ath_txbuf, ATH_TXDESC);
2461c42a7b7eSSam Leffler 	if (error != 0) {
2462c42a7b7eSSam Leffler 		ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf);
24635591b213SSam Leffler 		return error;
2464c42a7b7eSSam Leffler 	}
2465c42a7b7eSSam Leffler 
2466c42a7b7eSSam Leffler 	error = ath_descdma_setup(sc, &sc->sc_bdma, &sc->sc_bbuf,
2467c42a7b7eSSam Leffler 			"beacon", 1, 1);
2468c42a7b7eSSam Leffler 	if (error != 0) {
2469c42a7b7eSSam Leffler 		ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf);
2470c42a7b7eSSam Leffler 		ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf);
2471c42a7b7eSSam Leffler 		return error;
2472c42a7b7eSSam Leffler 	}
24735591b213SSam Leffler 	return 0;
24745591b213SSam Leffler }
24755591b213SSam Leffler 
24765591b213SSam Leffler static void
24775591b213SSam Leffler ath_desc_free(struct ath_softc *sc)
24785591b213SSam Leffler {
24795591b213SSam Leffler 
2480c42a7b7eSSam Leffler 	if (sc->sc_bdma.dd_desc_len != 0)
2481c42a7b7eSSam Leffler 		ath_descdma_cleanup(sc, &sc->sc_bdma, &sc->sc_bbuf);
2482c42a7b7eSSam Leffler 	if (sc->sc_txdma.dd_desc_len != 0)
2483c42a7b7eSSam Leffler 		ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf);
2484c42a7b7eSSam Leffler 	if (sc->sc_rxdma.dd_desc_len != 0)
2485c42a7b7eSSam Leffler 		ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf);
24865591b213SSam Leffler }
24875591b213SSam Leffler 
24885591b213SSam Leffler static struct ieee80211_node *
2489c42a7b7eSSam Leffler ath_node_alloc(struct ieee80211_node_table *nt)
24905591b213SSam Leffler {
2491c42a7b7eSSam Leffler 	struct ieee80211com *ic = nt->nt_ic;
2492c42a7b7eSSam Leffler 	struct ath_softc *sc = ic->ic_ifp->if_softc;
2493c42a7b7eSSam Leffler 	const size_t space = sizeof(struct ath_node) + sc->sc_rc->arc_space;
2494c42a7b7eSSam Leffler 	struct ath_node *an;
2495c42a7b7eSSam Leffler 
2496c42a7b7eSSam Leffler 	an = malloc(space, M_80211_NODE, M_NOWAIT|M_ZERO);
2497c42a7b7eSSam Leffler 	if (an == NULL) {
2498c42a7b7eSSam Leffler 		/* XXX stat+msg */
2499de5af704SSam Leffler 		return NULL;
25005591b213SSam Leffler 	}
2501c42a7b7eSSam Leffler 	an->an_avgrssi = ATH_RSSI_DUMMY_MARKER;
2502c42a7b7eSSam Leffler 	ath_rate_node_init(sc, an);
25035591b213SSam Leffler 
2504c42a7b7eSSam Leffler 	DPRINTF(sc, ATH_DEBUG_NODE, "%s: an %p\n", __func__, an);
2505c42a7b7eSSam Leffler 	return &an->an_node;
2506c42a7b7eSSam Leffler }
2507c42a7b7eSSam Leffler 
25085591b213SSam Leffler static void
2509c42a7b7eSSam Leffler ath_node_free(struct ieee80211_node *ni)
25105591b213SSam Leffler {
2511c42a7b7eSSam Leffler 	struct ieee80211com *ic = ni->ni_ic;
2512c42a7b7eSSam Leffler         struct ath_softc *sc = ic->ic_ifp->if_softc;
25131e774079SSam Leffler 
2514c42a7b7eSSam Leffler 	DPRINTF(sc, ATH_DEBUG_NODE, "%s: ni %p\n", __func__, ni);
2515c42a7b7eSSam Leffler 
2516c42a7b7eSSam Leffler 	ath_rate_node_cleanup(sc, ATH_NODE(ni));
2517c42a7b7eSSam Leffler 	sc->sc_node_free(ni);
25185591b213SSam Leffler }
25195591b213SSam Leffler 
2520de5af704SSam Leffler static u_int8_t
2521c42a7b7eSSam Leffler ath_node_getrssi(const struct ieee80211_node *ni)
2522de5af704SSam Leffler {
2523c42a7b7eSSam Leffler #define	HAL_EP_RND(x, mul) \
2524c42a7b7eSSam Leffler 	((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul))
2525c42a7b7eSSam Leffler 	u_int32_t avgrssi = ATH_NODE_CONST(ni)->an_avgrssi;
2526c42a7b7eSSam Leffler 	int32_t rssi;
2527de5af704SSam Leffler 
2528de5af704SSam Leffler 	/*
2529c42a7b7eSSam Leffler 	 * When only one frame is received there will be no state in
2530c42a7b7eSSam Leffler 	 * avgrssi so fallback on the value recorded by the 802.11 layer.
2531de5af704SSam Leffler 	 */
2532c42a7b7eSSam Leffler 	if (avgrssi != ATH_RSSI_DUMMY_MARKER)
2533c42a7b7eSSam Leffler 		rssi = HAL_EP_RND(avgrssi, HAL_RSSI_EP_MULTIPLIER);
2534de5af704SSam Leffler 	else
2535c42a7b7eSSam Leffler 		rssi = ni->ni_rssi;
2536c42a7b7eSSam Leffler 	/* NB: theoretically we shouldn't need this, but be paranoid */
2537c42a7b7eSSam Leffler 	return rssi < 0 ? 0 : rssi > 127 ? 127 : rssi;
2538c42a7b7eSSam Leffler #undef HAL_EP_RND
2539de5af704SSam Leffler }
2540de5af704SSam Leffler 
25415591b213SSam Leffler static int
25425591b213SSam Leffler ath_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf)
25435591b213SSam Leffler {
25445591b213SSam Leffler 	struct ath_hal *ah = sc->sc_ah;
25455591b213SSam Leffler 	int error;
25465591b213SSam Leffler 	struct mbuf *m;
25475591b213SSam Leffler 	struct ath_desc *ds;
25485591b213SSam Leffler 
25495591b213SSam Leffler 	m = bf->bf_m;
25505591b213SSam Leffler 	if (m == NULL) {
25515591b213SSam Leffler 		/*
25525591b213SSam Leffler 		 * NB: by assigning a page to the rx dma buffer we
25535591b213SSam Leffler 		 * implicitly satisfy the Atheros requirement that
25545591b213SSam Leffler 		 * this buffer be cache-line-aligned and sized to be
25555591b213SSam Leffler 		 * multiple of the cache line size.  Not doing this
25565591b213SSam Leffler 		 * causes weird stuff to happen (for the 5210 at least).
25575591b213SSam Leffler 		 */
25585591b213SSam Leffler 		m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
25595591b213SSam Leffler 		if (m == NULL) {
2560c42a7b7eSSam Leffler 			DPRINTF(sc, ATH_DEBUG_ANY,
2561c42a7b7eSSam Leffler 				"%s: no mbuf/cluster\n", __func__);
25625591b213SSam Leffler 			sc->sc_stats.ast_rx_nombuf++;
25635591b213SSam Leffler 			return ENOMEM;
25645591b213SSam Leffler 		}
25655591b213SSam Leffler 		bf->bf_m = m;
25665591b213SSam Leffler 		m->m_pkthdr.len = m->m_len = m->m_ext.ext_size;
25675591b213SSam Leffler 
2568f9e6219bSSam Leffler 		error = bus_dmamap_load_mbuf_sg(sc->sc_dmat,
2569c42a7b7eSSam Leffler 					     bf->bf_dmamap, m,
2570f9e6219bSSam Leffler 					     bf->bf_segs, &bf->bf_nseg,
25715591b213SSam Leffler 					     BUS_DMA_NOWAIT);
25725591b213SSam Leffler 		if (error != 0) {
2573c42a7b7eSSam Leffler 			DPRINTF(sc, ATH_DEBUG_ANY,
2574f9e6219bSSam Leffler 			    "%s: bus_dmamap_load_mbuf_sg failed; error %d\n",
2575c42a7b7eSSam Leffler 			    __func__, error);
25765591b213SSam Leffler 			sc->sc_stats.ast_rx_busdma++;
25775591b213SSam Leffler 			return error;
25785591b213SSam Leffler 		}
2579d77367bfSSam Leffler 		KASSERT(bf->bf_nseg == 1,
2580d77367bfSSam Leffler 			("multi-segment packet; nseg %u", bf->bf_nseg));
25815591b213SSam Leffler 	}
25825591b213SSam Leffler 	bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREREAD);
25835591b213SSam Leffler 
258404e22a02SSam Leffler 	/*
258504e22a02SSam Leffler 	 * Setup descriptors.  For receive we always terminate
258604e22a02SSam Leffler 	 * the descriptor list with a self-linked entry so we'll
258704e22a02SSam Leffler 	 * not get overrun under high load (as can happen with a
2588c42a7b7eSSam Leffler 	 * 5212 when ANI processing enables PHY error frames).
258904e22a02SSam Leffler 	 *
259004e22a02SSam Leffler 	 * To insure the last descriptor is self-linked we create
259104e22a02SSam Leffler 	 * each descriptor as self-linked and add it to the end.  As
259204e22a02SSam Leffler 	 * each additional descriptor is added the previous self-linked
259304e22a02SSam Leffler 	 * entry is ``fixed'' naturally.  This should be safe even
259404e22a02SSam Leffler 	 * if DMA is happening.  When processing RX interrupts we
259504e22a02SSam Leffler 	 * never remove/process the last, self-linked, entry on the
259604e22a02SSam Leffler 	 * descriptor list.  This insures the hardware always has
259704e22a02SSam Leffler 	 * someplace to write a new frame.
259804e22a02SSam Leffler 	 */
25995591b213SSam Leffler 	ds = bf->bf_desc;
260004e22a02SSam Leffler 	ds->ds_link = bf->bf_daddr;	/* link to self */
26015591b213SSam Leffler 	ds->ds_data = bf->bf_segs[0].ds_addr;
26025591b213SSam Leffler 	ath_hal_setuprxdesc(ah, ds
26035591b213SSam Leffler 		, m->m_len		/* buffer size */
26045591b213SSam Leffler 		, 0
26055591b213SSam Leffler 	);
26065591b213SSam Leffler 
26075591b213SSam Leffler 	if (sc->sc_rxlink != NULL)
26085591b213SSam Leffler 		*sc->sc_rxlink = bf->bf_daddr;
26095591b213SSam Leffler 	sc->sc_rxlink = &ds->ds_link;
26105591b213SSam Leffler 	return 0;
26115591b213SSam Leffler }
26125591b213SSam Leffler 
2613c42a7b7eSSam Leffler /*
261403ed599aSSam Leffler  * Extend 15-bit time stamp from rx descriptor to
26157b0c77ecSSam Leffler  * a full 64-bit TSF using the specified TSF.
261603ed599aSSam Leffler  */
261703ed599aSSam Leffler static __inline u_int64_t
26187b0c77ecSSam Leffler ath_extend_tsf(u_int32_t rstamp, u_int64_t tsf)
261903ed599aSSam Leffler {
262003ed599aSSam Leffler 	if ((tsf & 0x7fff) < rstamp)
262103ed599aSSam Leffler 		tsf -= 0x8000;
262203ed599aSSam Leffler 	return ((tsf &~ 0x7fff) | rstamp);
262303ed599aSSam Leffler }
262403ed599aSSam Leffler 
262503ed599aSSam Leffler /*
2626c42a7b7eSSam Leffler  * Intercept management frames to collect beacon rssi data
2627c42a7b7eSSam Leffler  * and to do ibss merges.
2628c42a7b7eSSam Leffler  */
2629c42a7b7eSSam Leffler static void
2630c42a7b7eSSam Leffler ath_recv_mgmt(struct ieee80211com *ic, struct mbuf *m,
2631c42a7b7eSSam Leffler 	struct ieee80211_node *ni,
2632c42a7b7eSSam Leffler 	int subtype, int rssi, u_int32_t rstamp)
2633c42a7b7eSSam Leffler {
2634c42a7b7eSSam Leffler 	struct ath_softc *sc = ic->ic_ifp->if_softc;
2635c42a7b7eSSam Leffler 
2636c42a7b7eSSam Leffler 	/*
2637c42a7b7eSSam Leffler 	 * Call up first so subsequent work can use information
2638c42a7b7eSSam Leffler 	 * potentially stored in the node (e.g. for ibss merge).
2639c42a7b7eSSam Leffler 	 */
2640c42a7b7eSSam Leffler 	sc->sc_recv_mgmt(ic, m, ni, subtype, rssi, rstamp);
2641c42a7b7eSSam Leffler 	switch (subtype) {
2642c42a7b7eSSam Leffler 	case IEEE80211_FC0_SUBTYPE_BEACON:
2643c42a7b7eSSam Leffler 		/* update rssi statistics for use by the hal */
2644ffa2cab6SSam Leffler 		ATH_RSSI_LPF(sc->sc_halstats.ns_avgbrssi, rssi);
264580d939bfSSam Leffler 		if (sc->sc_syncbeacon &&
264680d939bfSSam Leffler 		    ni == ic->ic_bss && ic->ic_state == IEEE80211_S_RUN) {
264780d939bfSSam Leffler 			/*
264880d939bfSSam Leffler 			 * Resync beacon timers using the tsf of the beacon
264980d939bfSSam Leffler 			 * frame we just received.
265080d939bfSSam Leffler 			 */
265180d939bfSSam Leffler 			ath_beacon_config(sc);
265280d939bfSSam Leffler 		}
2653c42a7b7eSSam Leffler 		/* fall thru... */
2654c42a7b7eSSam Leffler 	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
2655c42a7b7eSSam Leffler 		if (ic->ic_opmode == IEEE80211_M_IBSS &&
2656c42a7b7eSSam Leffler 		    ic->ic_state == IEEE80211_S_RUN) {
26577b0c77ecSSam Leffler 			u_int64_t tsf = ath_extend_tsf(rstamp,
26587b0c77ecSSam Leffler 				ath_hal_gettsf64(sc->sc_ah));
2659c42a7b7eSSam Leffler 			/*
2660c42a7b7eSSam Leffler 			 * Handle ibss merge as needed; check the tsf on the
2661c42a7b7eSSam Leffler 			 * frame before attempting the merge.  The 802.11 spec
2662c42a7b7eSSam Leffler 			 * says the station should change it's bssid to match
2663c42a7b7eSSam Leffler 			 * the oldest station with the same ssid, where oldest
2664f818612bSSam Leffler 			 * is determined by the tsf.  Note that hardware
2665f818612bSSam Leffler 			 * reconfiguration happens through callback to
266603ed599aSSam Leffler 			 * ath_newstate as the state machine will go from
266703ed599aSSam Leffler 			 * RUN -> RUN when this happens.
2668c42a7b7eSSam Leffler 			 */
266903ed599aSSam Leffler 			if (le64toh(ni->ni_tstamp.tsf) >= tsf) {
267003ed599aSSam Leffler 				DPRINTF(sc, ATH_DEBUG_STATE,
267133d7d80cSTai-hwa Liang 				    "ibss merge, rstamp %u tsf %ju "
267233d7d80cSTai-hwa Liang 				    "tstamp %ju\n", rstamp, (uintmax_t)tsf,
267333d7d80cSTai-hwa Liang 				    (uintmax_t)ni->ni_tstamp.tsf);
2674641b4d0bSSam Leffler 				(void) ieee80211_ibss_merge(ni);
2675c42a7b7eSSam Leffler 			}
267603ed599aSSam Leffler 		}
2677c42a7b7eSSam Leffler 		break;
2678c42a7b7eSSam Leffler 	}
2679c42a7b7eSSam Leffler }
2680c42a7b7eSSam Leffler 
2681c42a7b7eSSam Leffler /*
2682c42a7b7eSSam Leffler  * Set the default antenna.
2683c42a7b7eSSam Leffler  */
2684c42a7b7eSSam Leffler static void
2685c42a7b7eSSam Leffler ath_setdefantenna(struct ath_softc *sc, u_int antenna)
2686c42a7b7eSSam Leffler {
2687c42a7b7eSSam Leffler 	struct ath_hal *ah = sc->sc_ah;
2688c42a7b7eSSam Leffler 
2689c42a7b7eSSam Leffler 	/* XXX block beacon interrupts */
2690c42a7b7eSSam Leffler 	ath_hal_setdefantenna(ah, antenna);
2691c42a7b7eSSam Leffler 	if (sc->sc_defant != antenna)
2692c42a7b7eSSam Leffler 		sc->sc_stats.ast_ant_defswitch++;
2693c42a7b7eSSam Leffler 	sc->sc_defant = antenna;
2694c42a7b7eSSam Leffler 	sc->sc_rxotherant = 0;
2695c42a7b7eSSam Leffler }
2696c42a7b7eSSam Leffler 
26977b0c77ecSSam Leffler static int
26987b0c77ecSSam Leffler ath_rx_tap(struct ath_softc *sc, struct mbuf *m,
26997b0c77ecSSam Leffler 	const struct ath_desc *ds, u_int64_t tsf, int16_t nf)
27007b0c77ecSSam Leffler {
27017b0c77ecSSam Leffler 	u_int8_t rix;
27027b0c77ecSSam Leffler 
27037b0c77ecSSam Leffler 	KASSERT(sc->sc_drvbpf != NULL, ("no tap"));
27047b0c77ecSSam Leffler 
27057b0c77ecSSam Leffler 	/*
27067b0c77ecSSam Leffler 	 * Discard anything shorter than an ack or cts.
27077b0c77ecSSam Leffler 	 */
27087b0c77ecSSam Leffler 	if (m->m_pkthdr.len < IEEE80211_ACK_LEN) {
27097b0c77ecSSam Leffler 		DPRINTF(sc, ATH_DEBUG_RECV, "%s: runt packet %d\n",
27107b0c77ecSSam Leffler 			__func__, m->m_pkthdr.len);
27117b0c77ecSSam Leffler 		sc->sc_stats.ast_rx_tooshort++;
27127b0c77ecSSam Leffler 		return 0;
27137b0c77ecSSam Leffler 	}
27147b0c77ecSSam Leffler 	sc->sc_rx_th.wr_tsf = htole64(
27157b0c77ecSSam Leffler 		ath_extend_tsf(ds->ds_rxstat.rs_tstamp, tsf));
27167b0c77ecSSam Leffler 	rix = ds->ds_rxstat.rs_rate;
27177b0c77ecSSam Leffler 	sc->sc_rx_th.wr_flags = sc->sc_hwmap[rix].rxflags;
27187b0c77ecSSam Leffler 	if (ds->ds_rxstat.rs_status & HAL_RXERR_CRC)
27197b0c77ecSSam Leffler 		sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_BADFCS;
27207b0c77ecSSam Leffler 	/* XXX propagate other error flags from descriptor */
27217b0c77ecSSam Leffler 	sc->sc_rx_th.wr_rate = sc->sc_hwmap[rix].ieeerate;
27227b0c77ecSSam Leffler 	sc->sc_rx_th.wr_antsignal = ds->ds_rxstat.rs_rssi + nf;
27237b0c77ecSSam Leffler 	sc->sc_rx_th.wr_antnoise = nf;
27247b0c77ecSSam Leffler 	sc->sc_rx_th.wr_antenna = ds->ds_rxstat.rs_antenna;
27257b0c77ecSSam Leffler 
27267b0c77ecSSam Leffler 	bpf_mtap2(sc->sc_drvbpf, &sc->sc_rx_th, sc->sc_rx_th_len, m);
27277b0c77ecSSam Leffler 
27287b0c77ecSSam Leffler 	return 1;
27297b0c77ecSSam Leffler }
27307b0c77ecSSam Leffler 
27315591b213SSam Leffler static void
27325591b213SSam Leffler ath_rx_proc(void *arg, int npending)
27335591b213SSam Leffler {
27348cec0ab9SSam Leffler #define	PA2DESC(_sc, _pa) \
2735c42a7b7eSSam Leffler 	((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \
2736c42a7b7eSSam Leffler 		((_pa) - (_sc)->sc_rxdma.dd_desc_paddr)))
27375591b213SSam Leffler 	struct ath_softc *sc = arg;
27385591b213SSam Leffler 	struct ath_buf *bf;
2739d1d0cf62SSam Leffler 	struct ieee80211com *ic = &sc->sc_ic;
2740fc74a9f9SBrooks Davis 	struct ifnet *ifp = sc->sc_ifp;
27415591b213SSam Leffler 	struct ath_hal *ah = sc->sc_ah;
27425591b213SSam Leffler 	struct ath_desc *ds;
27435591b213SSam Leffler 	struct mbuf *m;
27440a915fadSSam Leffler 	struct ieee80211_node *ni;
2745de5af704SSam Leffler 	struct ath_node *an;
2746d7736e13SSam Leffler 	int len, type, ngood;
27475591b213SSam Leffler 	u_int phyerr;
27485591b213SSam Leffler 	HAL_STATUS status;
27497b0c77ecSSam Leffler 	int16_t nf;
27507b0c77ecSSam Leffler 	u_int64_t tsf;
27515591b213SSam Leffler 
2752b5f4adb3SSam Leffler 	NET_LOCK_GIANT();		/* XXX */
2753b5f4adb3SSam Leffler 
2754c42a7b7eSSam Leffler 	DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s: pending %u\n", __func__, npending);
2755d7736e13SSam Leffler 	ngood = 0;
27567b0c77ecSSam Leffler 	nf = ath_hal_getchannoise(ah, &sc->sc_curchan);
27577b0c77ecSSam Leffler 	tsf = ath_hal_gettsf64(ah);
27585591b213SSam Leffler 	do {
2759c42a7b7eSSam Leffler 		bf = STAILQ_FIRST(&sc->sc_rxbuf);
27605591b213SSam Leffler 		if (bf == NULL) {		/* NB: shouldn't happen */
2761c42a7b7eSSam Leffler 			if_printf(ifp, "%s: no buffer!\n", __func__);
27625591b213SSam Leffler 			break;
27635591b213SSam Leffler 		}
276404e22a02SSam Leffler 		ds = bf->bf_desc;
276504e22a02SSam Leffler 		if (ds->ds_link == bf->bf_daddr) {
276604e22a02SSam Leffler 			/* NB: never process the self-linked entry at the end */
276704e22a02SSam Leffler 			break;
276804e22a02SSam Leffler 		}
27695591b213SSam Leffler 		m = bf->bf_m;
27705591b213SSam Leffler 		if (m == NULL) {		/* NB: shouldn't happen */
2771c42a7b7eSSam Leffler 			if_printf(ifp, "%s: no mbuf!\n", __func__);
27725591b213SSam Leffler 			continue;
27735591b213SSam Leffler 		}
27748cec0ab9SSam Leffler 		/* XXX sync descriptor memory */
27758cec0ab9SSam Leffler 		/*
27768cec0ab9SSam Leffler 		 * Must provide the virtual address of the current
27778cec0ab9SSam Leffler 		 * descriptor, the physical address, and the virtual
27788cec0ab9SSam Leffler 		 * address of the next descriptor in the h/w chain.
27798cec0ab9SSam Leffler 		 * This allows the HAL to look ahead to see if the
27808cec0ab9SSam Leffler 		 * hardware is done with a descriptor by checking the
27818cec0ab9SSam Leffler 		 * done bit in the following descriptor and the address
27828cec0ab9SSam Leffler 		 * of the current descriptor the DMA engine is working
27838cec0ab9SSam Leffler 		 * on.  All this is necessary because of our use of
27848cec0ab9SSam Leffler 		 * a self-linked list to avoid rx overruns.
27858cec0ab9SSam Leffler 		 */
27868cec0ab9SSam Leffler 		status = ath_hal_rxprocdesc(ah, ds,
27878cec0ab9SSam Leffler 				bf->bf_daddr, PA2DESC(sc, ds->ds_link));
27885591b213SSam Leffler #ifdef AR_DEBUG
2789c42a7b7eSSam Leffler 		if (sc->sc_debug & ATH_DEBUG_RECV_DESC)
27905591b213SSam Leffler 			ath_printrxbuf(bf, status == HAL_OK);
27915591b213SSam Leffler #endif
27925591b213SSam Leffler 		if (status == HAL_EINPROGRESS)
27935591b213SSam Leffler 			break;
2794c42a7b7eSSam Leffler 		STAILQ_REMOVE_HEAD(&sc->sc_rxbuf, bf_list);
2795c42a7b7eSSam Leffler 		if (ds->ds_rxstat.rs_more) {
2796c42a7b7eSSam Leffler 			/*
2797c42a7b7eSSam Leffler 			 * Frame spans multiple descriptors; this
2798c42a7b7eSSam Leffler 			 * cannot happen yet as we don't support
2799c42a7b7eSSam Leffler 			 * jumbograms.  If not in monitor mode,
2800c42a7b7eSSam Leffler 			 * discard the frame.
2801c42a7b7eSSam Leffler 			 */
2802c42a7b7eSSam Leffler 			if (ic->ic_opmode != IEEE80211_M_MONITOR) {
2803c42a7b7eSSam Leffler 				sc->sc_stats.ast_rx_toobig++;
2804c42a7b7eSSam Leffler 				goto rx_next;
2805c42a7b7eSSam Leffler 			}
2806c42a7b7eSSam Leffler 			/* fall thru for monitor mode handling... */
2807c42a7b7eSSam Leffler 		} else if (ds->ds_rxstat.rs_status != 0) {
28085591b213SSam Leffler 			if (ds->ds_rxstat.rs_status & HAL_RXERR_CRC)
28095591b213SSam Leffler 				sc->sc_stats.ast_rx_crcerr++;
28105591b213SSam Leffler 			if (ds->ds_rxstat.rs_status & HAL_RXERR_FIFO)
28115591b213SSam Leffler 				sc->sc_stats.ast_rx_fifoerr++;
28125591b213SSam Leffler 			if (ds->ds_rxstat.rs_status & HAL_RXERR_PHY) {
28135591b213SSam Leffler 				sc->sc_stats.ast_rx_phyerr++;
28145591b213SSam Leffler 				phyerr = ds->ds_rxstat.rs_phyerr & 0x1f;
28155591b213SSam Leffler 				sc->sc_stats.ast_rx_phy[phyerr]++;
2816c42a7b7eSSam Leffler 				goto rx_next;
2817c42a7b7eSSam Leffler 			}
2818c42a7b7eSSam Leffler 			if (ds->ds_rxstat.rs_status & HAL_RXERR_DECRYPT) {
281985643802SSam Leffler 				/*
2820c42a7b7eSSam Leffler 				 * Decrypt error.  If the error occurred
2821c42a7b7eSSam Leffler 				 * because there was no hardware key, then
2822c42a7b7eSSam Leffler 				 * let the frame through so the upper layers
2823c42a7b7eSSam Leffler 				 * can process it.  This is necessary for 5210
2824c42a7b7eSSam Leffler 				 * parts which have no way to setup a ``clear''
2825c42a7b7eSSam Leffler 				 * key cache entry.
2826c42a7b7eSSam Leffler 				 *
2827c42a7b7eSSam Leffler 				 * XXX do key cache faulting
282885643802SSam Leffler 				 */
2829c42a7b7eSSam Leffler 				if (ds->ds_rxstat.rs_keyix == HAL_RXKEYIX_INVALID)
2830c42a7b7eSSam Leffler 					goto rx_accept;
2831c42a7b7eSSam Leffler 				sc->sc_stats.ast_rx_badcrypt++;
28325591b213SSam Leffler 			}
2833c42a7b7eSSam Leffler 			if (ds->ds_rxstat.rs_status & HAL_RXERR_MIC) {
2834c42a7b7eSSam Leffler 				sc->sc_stats.ast_rx_badmic++;
2835c42a7b7eSSam Leffler 				/*
2836c42a7b7eSSam Leffler 				 * Do minimal work required to hand off
2837c42a7b7eSSam Leffler 				 * the 802.11 header for notifcation.
2838c42a7b7eSSam Leffler 				 */
2839c42a7b7eSSam Leffler 				/* XXX frag's and qos frames */
28405591b213SSam Leffler 				len = ds->ds_rxstat.rs_datalen;
2841c42a7b7eSSam Leffler 				if (len >= sizeof (struct ieee80211_frame)) {
2842c42a7b7eSSam Leffler 					bus_dmamap_sync(sc->sc_dmat,
2843c42a7b7eSSam Leffler 					    bf->bf_dmamap,
2844c42a7b7eSSam Leffler 					    BUS_DMASYNC_POSTREAD);
2845c42a7b7eSSam Leffler 					ieee80211_notify_michael_failure(ic,
2846c42a7b7eSSam Leffler 					    mtod(m, struct ieee80211_frame *),
28470ab4040aSSam Leffler 					    sc->sc_splitmic ?
28480ab4040aSSam Leffler 					        ds->ds_rxstat.rs_keyix-32 :
28490ab4040aSSam Leffler 					        ds->ds_rxstat.rs_keyix
28500ab4040aSSam Leffler 					);
2851c42a7b7eSSam Leffler 				}
2852c42a7b7eSSam Leffler 			}
2853c42a7b7eSSam Leffler 			ifp->if_ierrors++;
2854c42a7b7eSSam Leffler 			/*
28557b0c77ecSSam Leffler 			 * When a tap is present pass error frames
28567b0c77ecSSam Leffler 			 * that have been requested.  By default we
28577b0c77ecSSam Leffler 			 * pass decrypt+mic errors but others may be
28587b0c77ecSSam Leffler 			 * interesting (e.g. crc).
2859c42a7b7eSSam Leffler 			 */
28607b0c77ecSSam Leffler 			if (sc->sc_drvbpf != NULL &&
28617b0c77ecSSam Leffler 			    (ds->ds_rxstat.rs_status & sc->sc_monpass)) {
28627b0c77ecSSam Leffler 				bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap,
28637b0c77ecSSam Leffler 				    BUS_DMASYNC_POSTREAD);
28647b0c77ecSSam Leffler 				/* NB: bpf needs the mbuf length setup */
28657b0c77ecSSam Leffler 				len = ds->ds_rxstat.rs_datalen;
28667b0c77ecSSam Leffler 				m->m_pkthdr.len = m->m_len = len;
28677b0c77ecSSam Leffler 				(void) ath_rx_tap(sc, m, ds, tsf, nf);
28687b0c77ecSSam Leffler 			}
28697b0c77ecSSam Leffler 			/* XXX pass MIC errors up for s/w reclaculation */
28705591b213SSam Leffler 			goto rx_next;
28715591b213SSam Leffler 		}
2872c42a7b7eSSam Leffler rx_accept:
2873c42a7b7eSSam Leffler 		/*
2874c42a7b7eSSam Leffler 		 * Sync and unmap the frame.  At this point we're
2875c42a7b7eSSam Leffler 		 * committed to passing the mbuf somewhere so clear
2876c42a7b7eSSam Leffler 		 * bf_m; this means a new sk_buff must be allocated
2877c42a7b7eSSam Leffler 		 * when the rx descriptor is setup again to receive
2878c42a7b7eSSam Leffler 		 * another frame.
2879c42a7b7eSSam Leffler 		 */
28805591b213SSam Leffler 		bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap,
28815591b213SSam Leffler 		    BUS_DMASYNC_POSTREAD);
28825591b213SSam Leffler 		bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
28835591b213SSam Leffler 		bf->bf_m = NULL;
2884c42a7b7eSSam Leffler 
28855591b213SSam Leffler 		m->m_pkthdr.rcvif = ifp;
2886c42a7b7eSSam Leffler 		len = ds->ds_rxstat.rs_datalen;
28875591b213SSam Leffler 		m->m_pkthdr.len = m->m_len = len;
288873454c73SSam Leffler 
2889c42a7b7eSSam Leffler 		sc->sc_stats.ast_ant_rx[ds->ds_rxstat.rs_antenna]++;
2890c42a7b7eSSam Leffler 
28917b0c77ecSSam Leffler 		if (sc->sc_drvbpf != NULL && !ath_rx_tap(sc, m, ds, tsf, nf)) {
28927b0c77ecSSam Leffler 			m_freem(m);		/* XXX reclaim */
2893c42a7b7eSSam Leffler 			goto rx_next;
2894c42a7b7eSSam Leffler 		}
28950a915fadSSam Leffler 
28965591b213SSam Leffler 		/*
2897c42a7b7eSSam Leffler 		 * From this point on we assume the frame is at least
2898c42a7b7eSSam Leffler 		 * as large as ieee80211_frame_min; verify that.
28995591b213SSam Leffler 		 */
2900c42a7b7eSSam Leffler 		if (len < IEEE80211_MIN_LEN) {
2901c42a7b7eSSam Leffler 			DPRINTF(sc, ATH_DEBUG_RECV, "%s: short packet %d\n",
2902c42a7b7eSSam Leffler 				__func__, len);
2903c42a7b7eSSam Leffler 			sc->sc_stats.ast_rx_tooshort++;
2904c42a7b7eSSam Leffler 			m_freem(m);
2905c42a7b7eSSam Leffler 			goto rx_next;
29065591b213SSam Leffler 		}
29070a915fadSSam Leffler 
2908c42a7b7eSSam Leffler 		if (IFF_DUMPPKTS(sc, ATH_DEBUG_RECV)) {
2909c42a7b7eSSam Leffler 			ieee80211_dump_pkt(mtod(m, caddr_t), len,
29103e50ec2cSSam Leffler 				   sc->sc_hwmap[ds->ds_rxstat.rs_rate].ieeerate,
2911c42a7b7eSSam Leffler 				   ds->ds_rxstat.rs_rssi);
2912c42a7b7eSSam Leffler 		}
2913c42a7b7eSSam Leffler 
2914c42a7b7eSSam Leffler 		m_adj(m, -IEEE80211_CRC_LEN);
2915de5af704SSam Leffler 
2916de5af704SSam Leffler 		/*
2917c42a7b7eSSam Leffler 		 * Locate the node for sender, track state, and then
2918c42a7b7eSSam Leffler 		 * pass the (referenced) node up to the 802.11 layer
2919c42a7b7eSSam Leffler 		 * for its use.
2920c42a7b7eSSam Leffler 		 */
2921c1225b52SSam Leffler 		ni = ieee80211_find_rxnode_withkey(ic,
2922c1225b52SSam Leffler 			mtod(m, const struct ieee80211_frame_min *),
2923c1225b52SSam Leffler 			ds->ds_rxstat.rs_keyix == HAL_RXKEYIX_INVALID ?
2924c1225b52SSam Leffler 				IEEE80211_KEYIX_NONE : ds->ds_rxstat.rs_keyix);
2925c42a7b7eSSam Leffler 		/*
2926c42a7b7eSSam Leffler 		 * Track rx rssi and do any rx antenna management.
2927de5af704SSam Leffler 		 */
2928de5af704SSam Leffler 		an = ATH_NODE(ni);
2929c42a7b7eSSam Leffler 		ATH_RSSI_LPF(an->an_avgrssi, ds->ds_rxstat.rs_rssi);
2930ffa2cab6SSam Leffler 		ATH_RSSI_LPF(sc->sc_halstats.ns_avgrssi, ds->ds_rxstat.rs_rssi);
2931e8fd88a3SSam Leffler 		/*
2932e8fd88a3SSam Leffler 		 * Send frame up for processing.
2933e8fd88a3SSam Leffler 		 */
2934e8fd88a3SSam Leffler 		type = ieee80211_input(ic, m, ni,
2935e8fd88a3SSam Leffler 			ds->ds_rxstat.rs_rssi, ds->ds_rxstat.rs_tstamp);
2936e8fd88a3SSam Leffler 		ieee80211_free_node(ni);
2937c42a7b7eSSam Leffler 		if (sc->sc_diversity) {
2938c42a7b7eSSam Leffler 			/*
2939c42a7b7eSSam Leffler 			 * When using fast diversity, change the default rx
2940c42a7b7eSSam Leffler 			 * antenna if diversity chooses the other antenna 3
2941c42a7b7eSSam Leffler 			 * times in a row.
2942c42a7b7eSSam Leffler 			 */
2943c42a7b7eSSam Leffler 			if (sc->sc_defant != ds->ds_rxstat.rs_antenna) {
2944c42a7b7eSSam Leffler 				if (++sc->sc_rxotherant >= 3)
2945c42a7b7eSSam Leffler 					ath_setdefantenna(sc,
2946c42a7b7eSSam Leffler 						ds->ds_rxstat.rs_antenna);
2947c42a7b7eSSam Leffler 			} else
2948c42a7b7eSSam Leffler 				sc->sc_rxotherant = 0;
2949c42a7b7eSSam Leffler 		}
29503e50ec2cSSam Leffler 		if (sc->sc_softled) {
29513e50ec2cSSam Leffler 			/*
29523e50ec2cSSam Leffler 			 * Blink for any data frame.  Otherwise do a
29533e50ec2cSSam Leffler 			 * heartbeat-style blink when idle.  The latter
29543e50ec2cSSam Leffler 			 * is mainly for station mode where we depend on
29553e50ec2cSSam Leffler 			 * periodic beacon frames to trigger the poll event.
29563e50ec2cSSam Leffler 			 */
295731640eb7SSam Leffler 			if (type == IEEE80211_FC0_TYPE_DATA) {
29583e50ec2cSSam Leffler 				sc->sc_rxrate = ds->ds_rxstat.rs_rate;
29593e50ec2cSSam Leffler 				ath_led_event(sc, ATH_LED_RX);
29603e50ec2cSSam Leffler 			} else if (ticks - sc->sc_ledevent >= sc->sc_ledidle)
29613e50ec2cSSam Leffler 				ath_led_event(sc, ATH_LED_POLL);
29623e50ec2cSSam Leffler 		}
2963d7736e13SSam Leffler 		/*
2964d7736e13SSam Leffler 		 * Arrange to update the last rx timestamp only for
2965d7736e13SSam Leffler 		 * frames from our ap when operating in station mode.
2966d7736e13SSam Leffler 		 * This assumes the rx key is always setup when associated.
2967d7736e13SSam Leffler 		 */
2968d7736e13SSam Leffler 		if (ic->ic_opmode == IEEE80211_M_STA &&
2969d7736e13SSam Leffler 		    ds->ds_rxstat.rs_keyix != HAL_RXKEYIX_INVALID)
2970d7736e13SSam Leffler 			ngood++;
29715591b213SSam Leffler rx_next:
2972c42a7b7eSSam Leffler 		STAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list);
29735591b213SSam Leffler 	} while (ath_rxbuf_init(sc, bf) == 0);
29745591b213SSam Leffler 
2975c42a7b7eSSam Leffler 	/* rx signal state monitoring */
2976ffa2cab6SSam Leffler 	ath_hal_rxmonitor(ah, &sc->sc_halstats);
2977d7736e13SSam Leffler 	if (ngood)
2978d7736e13SSam Leffler 		sc->sc_lastrx = tsf;
2979b5f4adb3SSam Leffler 
2980b5f4adb3SSam Leffler 	NET_UNLOCK_GIANT();		/* XXX */
29818cec0ab9SSam Leffler #undef PA2DESC
29825591b213SSam Leffler }
29835591b213SSam Leffler 
29845591b213SSam Leffler /*
2985c42a7b7eSSam Leffler  * Setup a h/w transmit queue.
29865591b213SSam Leffler  */
2987c42a7b7eSSam Leffler static struct ath_txq *
2988c42a7b7eSSam Leffler ath_txq_setup(struct ath_softc *sc, int qtype, int subtype)
2989c42a7b7eSSam Leffler {
2990c42a7b7eSSam Leffler #define	N(a)	(sizeof(a)/sizeof(a[0]))
2991c42a7b7eSSam Leffler 	struct ath_hal *ah = sc->sc_ah;
2992c42a7b7eSSam Leffler 	HAL_TXQ_INFO qi;
2993c42a7b7eSSam Leffler 	int qnum;
2994c42a7b7eSSam Leffler 
2995c42a7b7eSSam Leffler 	memset(&qi, 0, sizeof(qi));
2996c42a7b7eSSam Leffler 	qi.tqi_subtype = subtype;
2997c42a7b7eSSam Leffler 	qi.tqi_aifs = HAL_TXQ_USEDEFAULT;
2998c42a7b7eSSam Leffler 	qi.tqi_cwmin = HAL_TXQ_USEDEFAULT;
2999c42a7b7eSSam Leffler 	qi.tqi_cwmax = HAL_TXQ_USEDEFAULT;
3000c42a7b7eSSam Leffler 	/*
3001c42a7b7eSSam Leffler 	 * Enable interrupts only for EOL and DESC conditions.
3002c42a7b7eSSam Leffler 	 * We mark tx descriptors to receive a DESC interrupt
3003c42a7b7eSSam Leffler 	 * when a tx queue gets deep; otherwise waiting for the
3004c42a7b7eSSam Leffler 	 * EOL to reap descriptors.  Note that this is done to
3005c42a7b7eSSam Leffler 	 * reduce interrupt load and this only defers reaping
3006c42a7b7eSSam Leffler 	 * descriptors, never transmitting frames.  Aside from
3007c42a7b7eSSam Leffler 	 * reducing interrupts this also permits more concurrency.
3008c42a7b7eSSam Leffler 	 * The only potential downside is if the tx queue backs
3009c42a7b7eSSam Leffler 	 * up in which case the top half of the kernel may backup
3010c42a7b7eSSam Leffler 	 * due to a lack of tx descriptors.
3011c42a7b7eSSam Leffler 	 */
3012c42a7b7eSSam Leffler 	qi.tqi_qflags = TXQ_FLAG_TXEOLINT_ENABLE | TXQ_FLAG_TXDESCINT_ENABLE;
3013c42a7b7eSSam Leffler 	qnum = ath_hal_setuptxqueue(ah, qtype, &qi);
3014c42a7b7eSSam Leffler 	if (qnum == -1) {
3015c42a7b7eSSam Leffler 		/*
3016c42a7b7eSSam Leffler 		 * NB: don't print a message, this happens
3017a614e076SSam Leffler 		 * normally on parts with too few tx queues
3018c42a7b7eSSam Leffler 		 */
3019c42a7b7eSSam Leffler 		return NULL;
3020c42a7b7eSSam Leffler 	}
3021c42a7b7eSSam Leffler 	if (qnum >= N(sc->sc_txq)) {
30226891c875SPeter Wemm 		device_printf(sc->sc_dev,
30236891c875SPeter Wemm 			"hal qnum %u out of range, max %zu!\n",
3024c42a7b7eSSam Leffler 			qnum, N(sc->sc_txq));
3025c42a7b7eSSam Leffler 		ath_hal_releasetxqueue(ah, qnum);
3026c42a7b7eSSam Leffler 		return NULL;
3027c42a7b7eSSam Leffler 	}
3028c42a7b7eSSam Leffler 	if (!ATH_TXQ_SETUP(sc, qnum)) {
3029c42a7b7eSSam Leffler 		struct ath_txq *txq = &sc->sc_txq[qnum];
3030c42a7b7eSSam Leffler 
3031c42a7b7eSSam Leffler 		txq->axq_qnum = qnum;
3032c42a7b7eSSam Leffler 		txq->axq_depth = 0;
3033c42a7b7eSSam Leffler 		txq->axq_intrcnt = 0;
3034c42a7b7eSSam Leffler 		txq->axq_link = NULL;
3035c42a7b7eSSam Leffler 		STAILQ_INIT(&txq->axq_q);
3036c42a7b7eSSam Leffler 		ATH_TXQ_LOCK_INIT(sc, txq);
3037c42a7b7eSSam Leffler 		sc->sc_txqsetup |= 1<<qnum;
3038c42a7b7eSSam Leffler 	}
3039c42a7b7eSSam Leffler 	return &sc->sc_txq[qnum];
3040c42a7b7eSSam Leffler #undef N
3041c42a7b7eSSam Leffler }
3042c42a7b7eSSam Leffler 
3043c42a7b7eSSam Leffler /*
3044c42a7b7eSSam Leffler  * Setup a hardware data transmit queue for the specified
3045c42a7b7eSSam Leffler  * access control.  The hal may not support all requested
3046c42a7b7eSSam Leffler  * queues in which case it will return a reference to a
3047c42a7b7eSSam Leffler  * previously setup queue.  We record the mapping from ac's
3048c42a7b7eSSam Leffler  * to h/w queues for use by ath_tx_start and also track
3049c42a7b7eSSam Leffler  * the set of h/w queues being used to optimize work in the
3050c42a7b7eSSam Leffler  * transmit interrupt handler and related routines.
3051c42a7b7eSSam Leffler  */
3052c42a7b7eSSam Leffler static int
3053c42a7b7eSSam Leffler ath_tx_setup(struct ath_softc *sc, int ac, int haltype)
3054c42a7b7eSSam Leffler {
3055c42a7b7eSSam Leffler #define	N(a)	(sizeof(a)/sizeof(a[0]))
3056c42a7b7eSSam Leffler 	struct ath_txq *txq;
3057c42a7b7eSSam Leffler 
3058c42a7b7eSSam Leffler 	if (ac >= N(sc->sc_ac2q)) {
30596891c875SPeter Wemm 		device_printf(sc->sc_dev, "AC %u out of range, max %zu!\n",
3060c42a7b7eSSam Leffler 			ac, N(sc->sc_ac2q));
3061c42a7b7eSSam Leffler 		return 0;
3062c42a7b7eSSam Leffler 	}
3063c42a7b7eSSam Leffler 	txq = ath_txq_setup(sc, HAL_TX_QUEUE_DATA, haltype);
3064c42a7b7eSSam Leffler 	if (txq != NULL) {
3065c42a7b7eSSam Leffler 		sc->sc_ac2q[ac] = txq;
3066c42a7b7eSSam Leffler 		return 1;
3067c42a7b7eSSam Leffler 	} else
3068c42a7b7eSSam Leffler 		return 0;
3069c42a7b7eSSam Leffler #undef N
3070c42a7b7eSSam Leffler }
3071c42a7b7eSSam Leffler 
3072c42a7b7eSSam Leffler /*
3073c42a7b7eSSam Leffler  * Update WME parameters for a transmit queue.
3074c42a7b7eSSam Leffler  */
3075c42a7b7eSSam Leffler static int
3076c42a7b7eSSam Leffler ath_txq_update(struct ath_softc *sc, int ac)
3077c42a7b7eSSam Leffler {
3078c42a7b7eSSam Leffler #define	ATH_EXPONENT_TO_VALUE(v)	((1<<v)-1)
3079c42a7b7eSSam Leffler #define	ATH_TXOP_TO_US(v)		(v<<5)
3080c42a7b7eSSam Leffler 	struct ieee80211com *ic = &sc->sc_ic;
3081c42a7b7eSSam Leffler 	struct ath_txq *txq = sc->sc_ac2q[ac];
3082c42a7b7eSSam Leffler 	struct wmeParams *wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[ac];
3083c42a7b7eSSam Leffler 	struct ath_hal *ah = sc->sc_ah;
3084c42a7b7eSSam Leffler 	HAL_TXQ_INFO qi;
3085c42a7b7eSSam Leffler 
3086c42a7b7eSSam Leffler 	ath_hal_gettxqueueprops(ah, txq->axq_qnum, &qi);
3087c42a7b7eSSam Leffler 	qi.tqi_aifs = wmep->wmep_aifsn;
3088c42a7b7eSSam Leffler 	qi.tqi_cwmin = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmin);
3089c42a7b7eSSam Leffler 	qi.tqi_cwmax = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmax);
3090c42a7b7eSSam Leffler 	qi.tqi_burstTime = ATH_TXOP_TO_US(wmep->wmep_txopLimit);
3091c42a7b7eSSam Leffler 
3092c42a7b7eSSam Leffler 	if (!ath_hal_settxqueueprops(ah, txq->axq_qnum, &qi)) {
3093c42a7b7eSSam Leffler 		device_printf(sc->sc_dev, "unable to update hardware queue "
3094c42a7b7eSSam Leffler 			"parameters for %s traffic!\n",
3095c42a7b7eSSam Leffler 			ieee80211_wme_acnames[ac]);
3096c42a7b7eSSam Leffler 		return 0;
3097c42a7b7eSSam Leffler 	} else {
3098c42a7b7eSSam Leffler 		ath_hal_resettxqueue(ah, txq->axq_qnum); /* push to h/w */
3099c42a7b7eSSam Leffler 		return 1;
3100c42a7b7eSSam Leffler 	}
3101c42a7b7eSSam Leffler #undef ATH_TXOP_TO_US
3102c42a7b7eSSam Leffler #undef ATH_EXPONENT_TO_VALUE
3103c42a7b7eSSam Leffler }
3104c42a7b7eSSam Leffler 
3105c42a7b7eSSam Leffler /*
3106c42a7b7eSSam Leffler  * Callback from the 802.11 layer to update WME parameters.
3107c42a7b7eSSam Leffler  */
3108c42a7b7eSSam Leffler static int
3109c42a7b7eSSam Leffler ath_wme_update(struct ieee80211com *ic)
3110c42a7b7eSSam Leffler {
3111c42a7b7eSSam Leffler 	struct ath_softc *sc = ic->ic_ifp->if_softc;
3112c42a7b7eSSam Leffler 
3113c42a7b7eSSam Leffler 	return !ath_txq_update(sc, WME_AC_BE) ||
3114c42a7b7eSSam Leffler 	    !ath_txq_update(sc, WME_AC_BK) ||
3115c42a7b7eSSam Leffler 	    !ath_txq_update(sc, WME_AC_VI) ||
3116c42a7b7eSSam Leffler 	    !ath_txq_update(sc, WME_AC_VO) ? EIO : 0;
3117c42a7b7eSSam Leffler }
3118c42a7b7eSSam Leffler 
3119c42a7b7eSSam Leffler /*
3120c42a7b7eSSam Leffler  * Reclaim resources for a setup queue.
3121c42a7b7eSSam Leffler  */
3122c42a7b7eSSam Leffler static void
3123c42a7b7eSSam Leffler ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq)
3124c42a7b7eSSam Leffler {
3125c42a7b7eSSam Leffler 
3126c42a7b7eSSam Leffler 	ath_hal_releasetxqueue(sc->sc_ah, txq->axq_qnum);
3127c42a7b7eSSam Leffler 	ATH_TXQ_LOCK_DESTROY(txq);
3128c42a7b7eSSam Leffler 	sc->sc_txqsetup &= ~(1<<txq->axq_qnum);
3129c42a7b7eSSam Leffler }
3130c42a7b7eSSam Leffler 
3131c42a7b7eSSam Leffler /*
3132c42a7b7eSSam Leffler  * Reclaim all tx queue resources.
3133c42a7b7eSSam Leffler  */
3134c42a7b7eSSam Leffler static void
3135c42a7b7eSSam Leffler ath_tx_cleanup(struct ath_softc *sc)
3136c42a7b7eSSam Leffler {
3137c42a7b7eSSam Leffler 	int i;
3138c42a7b7eSSam Leffler 
3139c42a7b7eSSam Leffler 	ATH_TXBUF_LOCK_DESTROY(sc);
3140c42a7b7eSSam Leffler 	for (i = 0; i < HAL_NUM_TX_QUEUES; i++)
3141c42a7b7eSSam Leffler 		if (ATH_TXQ_SETUP(sc, i))
3142c42a7b7eSSam Leffler 			ath_tx_cleanupq(sc, &sc->sc_txq[i]);
3143c42a7b7eSSam Leffler }
31445591b213SSam Leffler 
314599d258fdSSam Leffler /*
314699d258fdSSam Leffler  * Defragment an mbuf chain, returning at most maxfrags separate
314799d258fdSSam Leffler  * mbufs+clusters.  If this is not possible NULL is returned and
3148a7073e8bSSam Leffler  * the original mbuf chain is left in it's present (potentially
3149a7073e8bSSam Leffler  * modified) state.  We use two techniques: collapsing consecutive
3150a7073e8bSSam Leffler  * mbufs and replacing consecutive mbufs by a cluster.
315199d258fdSSam Leffler  */
315299d258fdSSam Leffler static struct mbuf *
315399d258fdSSam Leffler ath_defrag(struct mbuf *m0, int how, int maxfrags)
315499d258fdSSam Leffler {
315599d258fdSSam Leffler 	struct mbuf *m, *n, *n2, **prev;
315699d258fdSSam Leffler 	u_int curfrags;
315799d258fdSSam Leffler 
315899d258fdSSam Leffler 	/*
315999d258fdSSam Leffler 	 * Calculate the current number of frags.
316099d258fdSSam Leffler 	 */
316199d258fdSSam Leffler 	curfrags = 0;
316299d258fdSSam Leffler 	for (m = m0; m != NULL; m = m->m_next)
316399d258fdSSam Leffler 		curfrags++;
316499d258fdSSam Leffler 	/*
316599d258fdSSam Leffler 	 * First, try to collapse mbufs.  Note that we always collapse
316699d258fdSSam Leffler 	 * towards the front so we don't need to deal with moving the
316799d258fdSSam Leffler 	 * pkthdr.  This may be suboptimal if the first mbuf has much
316899d258fdSSam Leffler 	 * less data than the following.
316999d258fdSSam Leffler 	 */
317099d258fdSSam Leffler 	m = m0;
317199d258fdSSam Leffler again:
317299d258fdSSam Leffler 	for (;;) {
317399d258fdSSam Leffler 		n = m->m_next;
317499d258fdSSam Leffler 		if (n == NULL)
317599d258fdSSam Leffler 			break;
3176019b9669SSam Leffler 		if ((m->m_flags & M_RDONLY) == 0 &&
3177019b9669SSam Leffler 		    n->m_len < M_TRAILINGSPACE(m)) {
317899d258fdSSam Leffler 			bcopy(mtod(n, void *), mtod(m, char *) + m->m_len,
317999d258fdSSam Leffler 				n->m_len);
318099d258fdSSam Leffler 			m->m_len += n->m_len;
318199d258fdSSam Leffler 			m->m_next = n->m_next;
318299d258fdSSam Leffler 			m_free(n);
318399d258fdSSam Leffler 			if (--curfrags <= maxfrags)
318499d258fdSSam Leffler 				return m0;
318599d258fdSSam Leffler 		} else
318699d258fdSSam Leffler 			m = n;
318799d258fdSSam Leffler 	}
318899d258fdSSam Leffler 	KASSERT(maxfrags > 1,
318999d258fdSSam Leffler 		("maxfrags %u, but normal collapse failed", maxfrags));
319099d258fdSSam Leffler 	/*
319199d258fdSSam Leffler 	 * Collapse consecutive mbufs to a cluster.
319299d258fdSSam Leffler 	 */
319399d258fdSSam Leffler 	prev = &m0->m_next;		/* NB: not the first mbuf */
319499d258fdSSam Leffler 	while ((n = *prev) != NULL) {
319599d258fdSSam Leffler 		if ((n2 = n->m_next) != NULL &&
319699d258fdSSam Leffler 		    n->m_len + n2->m_len < MCLBYTES) {
319799d258fdSSam Leffler 			m = m_getcl(how, MT_DATA, 0);
319899d258fdSSam Leffler 			if (m == NULL)
319999d258fdSSam Leffler 				goto bad;
320099d258fdSSam Leffler 			bcopy(mtod(n, void *), mtod(m, void *), n->m_len);
320199d258fdSSam Leffler 			bcopy(mtod(n2, void *), mtod(m, char *) + n->m_len,
320299d258fdSSam Leffler 				n2->m_len);
320399d258fdSSam Leffler 			m->m_len = n->m_len + n2->m_len;
320499d258fdSSam Leffler 			m->m_next = n2->m_next;
320599d258fdSSam Leffler 			*prev = m;
320699d258fdSSam Leffler 			m_free(n);
320799d258fdSSam Leffler 			m_free(n2);
320899d258fdSSam Leffler 			if (--curfrags <= maxfrags)	/* +1 cl -2 mbufs */
320999d258fdSSam Leffler 				return m0;
321099d258fdSSam Leffler 			/*
321199d258fdSSam Leffler 			 * Still not there, try the normal collapse
321299d258fdSSam Leffler 			 * again before we allocate another cluster.
321399d258fdSSam Leffler 			 */
321499d258fdSSam Leffler 			goto again;
321599d258fdSSam Leffler 		}
321699d258fdSSam Leffler 		prev = &n->m_next;
321799d258fdSSam Leffler 	}
321899d258fdSSam Leffler 	/*
321999d258fdSSam Leffler 	 * No place where we can collapse to a cluster; punt.
322099d258fdSSam Leffler 	 * This can occur if, for example, you request 2 frags
322199d258fdSSam Leffler 	 * but the packet requires that both be clusters (we
322299d258fdSSam Leffler 	 * never reallocate the first mbuf to avoid moving the
322399d258fdSSam Leffler 	 * packet header).
322499d258fdSSam Leffler 	 */
322599d258fdSSam Leffler bad:
322699d258fdSSam Leffler 	return NULL;
322799d258fdSSam Leffler }
322899d258fdSSam Leffler 
32298b5341deSSam Leffler /*
32308b5341deSSam Leffler  * Return h/w rate index for an IEEE rate (w/o basic rate bit).
32318b5341deSSam Leffler  */
32328b5341deSSam Leffler static int
32338b5341deSSam Leffler ath_tx_findrix(const HAL_RATE_TABLE *rt, int rate)
32348b5341deSSam Leffler {
32358b5341deSSam Leffler 	int i;
32368b5341deSSam Leffler 
32378b5341deSSam Leffler 	for (i = 0; i < rt->rateCount; i++)
32388b5341deSSam Leffler 		if ((rt->info[i].dot11Rate & IEEE80211_RATE_VAL) == rate)
32398b5341deSSam Leffler 			return i;
32408b5341deSSam Leffler 	return 0;		/* NB: lowest rate */
32418b5341deSSam Leffler }
32428b5341deSSam Leffler 
32435591b213SSam Leffler static int
32445591b213SSam Leffler ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf,
32455591b213SSam Leffler     struct mbuf *m0)
32465591b213SSam Leffler {
32475591b213SSam Leffler 	struct ieee80211com *ic = &sc->sc_ic;
32485591b213SSam Leffler 	struct ath_hal *ah = sc->sc_ah;
3249fc74a9f9SBrooks Davis 	struct ifnet *ifp = sc->sc_ifp;
3250c4c3cb46SSam Leffler 	const struct chanAccParams *cap = &ic->ic_wme.wme_chanParams;
3251be613480SSam Leffler 	int i, error, iswep, ismcast, ismrr;
3252be613480SSam Leffler 	int keyix, hdrlen, pktlen, try0;
3253c42a7b7eSSam Leffler 	u_int8_t rix, txrate, ctsrate;
3254c42a7b7eSSam Leffler 	u_int8_t cix = 0xff;		/* NB: silence compiler */
3255c42a7b7eSSam Leffler 	struct ath_desc *ds, *ds0;
3256c42a7b7eSSam Leffler 	struct ath_txq *txq;
32575591b213SSam Leffler 	struct ieee80211_frame *wh;
3258c42a7b7eSSam Leffler 	u_int subtype, flags, ctsduration;
32595591b213SSam Leffler 	HAL_PKT_TYPE atype;
32605591b213SSam Leffler 	const HAL_RATE_TABLE *rt;
32615591b213SSam Leffler 	HAL_BOOL shortPreamble;
32625591b213SSam Leffler 	struct ath_node *an;
326399d258fdSSam Leffler 	struct mbuf *m;
3264c4c3cb46SSam Leffler 	u_int pri;
32655591b213SSam Leffler 
32665591b213SSam Leffler 	wh = mtod(m0, struct ieee80211_frame *);
32675591b213SSam Leffler 	iswep = wh->i_fc[1] & IEEE80211_FC1_WEP;
3268c42a7b7eSSam Leffler 	ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1);
3269c42a7b7eSSam Leffler 	hdrlen = ieee80211_anyhdrsize(wh);
3270c42a7b7eSSam Leffler 	/*
3271a614e076SSam Leffler 	 * Packet length must not include any
3272a614e076SSam Leffler 	 * pad bytes; deduct them here.
3273c42a7b7eSSam Leffler 	 */
3274c42a7b7eSSam Leffler 	pktlen = m0->m_pkthdr.len - (hdrlen & 3);
32755591b213SSam Leffler 
32765591b213SSam Leffler 	if (iswep) {
3277c42a7b7eSSam Leffler 		const struct ieee80211_cipher *cip;
3278c42a7b7eSSam Leffler 		struct ieee80211_key *k;
3279c42a7b7eSSam Leffler 
3280c42a7b7eSSam Leffler 		/*
3281c42a7b7eSSam Leffler 		 * Construct the 802.11 header+trailer for an encrypted
3282c42a7b7eSSam Leffler 		 * frame. The only reason this can fail is because of an
3283c42a7b7eSSam Leffler 		 * unknown or unsupported cipher/key type.
3284c42a7b7eSSam Leffler 		 */
3285c42a7b7eSSam Leffler 		k = ieee80211_crypto_encap(ic, ni, m0);
3286c42a7b7eSSam Leffler 		if (k == NULL) {
3287c42a7b7eSSam Leffler 			/*
3288c42a7b7eSSam Leffler 			 * This can happen when the key is yanked after the
3289c42a7b7eSSam Leffler 			 * frame was queued.  Just discard the frame; the
3290c42a7b7eSSam Leffler 			 * 802.11 layer counts failures and provides
3291c42a7b7eSSam Leffler 			 * debugging/diagnostics.
3292c42a7b7eSSam Leffler 			 */
32930c97ab96SSam Leffler 			m_freem(m0);
3294c42a7b7eSSam Leffler 			return EIO;
32955591b213SSam Leffler 		}
3296c42a7b7eSSam Leffler 		/*
3297c42a7b7eSSam Leffler 		 * Adjust the packet + header lengths for the crypto
3298c42a7b7eSSam Leffler 		 * additions and calculate the h/w key index.  When
3299c42a7b7eSSam Leffler 		 * a s/w mic is done the frame will have had any mic
3300c42a7b7eSSam Leffler 		 * added to it prior to entry so skb->len above will
3301c42a7b7eSSam Leffler 		 * account for it. Otherwise we need to add it to the
3302c42a7b7eSSam Leffler 		 * packet length.
3303c42a7b7eSSam Leffler 		 */
3304c42a7b7eSSam Leffler 		cip = k->wk_cipher;
3305c42a7b7eSSam Leffler 		hdrlen += cip->ic_header;
3306c42a7b7eSSam Leffler 		pktlen += cip->ic_header + cip->ic_trailer;
3307c42a7b7eSSam Leffler 		if ((k->wk_flags & IEEE80211_KEY_SWMIC) == 0)
3308c42a7b7eSSam Leffler 			pktlen += cip->ic_miclen;
3309c42a7b7eSSam Leffler 		keyix = k->wk_keyix;
3310c42a7b7eSSam Leffler 
3311c42a7b7eSSam Leffler 		/* packet header may have moved, reset our local pointer */
3312167ecdcaSSam Leffler 		wh = mtod(m0, struct ieee80211_frame *);
3313e8fd88a3SSam Leffler 	} else if (ni->ni_ucastkey.wk_cipher == &ieee80211_cipher_none) {
3314e8fd88a3SSam Leffler 		/*
3315e8fd88a3SSam Leffler 		 * Use station key cache slot, if assigned.
3316e8fd88a3SSam Leffler 		 */
3317e8fd88a3SSam Leffler 		keyix = ni->ni_ucastkey.wk_keyix;
3318e8fd88a3SSam Leffler 		if (keyix == IEEE80211_KEYIX_NONE)
3319e8fd88a3SSam Leffler 			keyix = HAL_TXKEYIX_INVALID;
3320c42a7b7eSSam Leffler 	} else
3321c42a7b7eSSam Leffler 		keyix = HAL_TXKEYIX_INVALID;
3322c42a7b7eSSam Leffler 
33235591b213SSam Leffler 	pktlen += IEEE80211_CRC_LEN;
33245591b213SSam Leffler 
33255591b213SSam Leffler 	/*
33265591b213SSam Leffler 	 * Load the DMA map so any coalescing is done.  This
33275591b213SSam Leffler 	 * also calculates the number of descriptors we need.
33285591b213SSam Leffler 	 */
3329f9e6219bSSam Leffler 	error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m0,
3330f9e6219bSSam Leffler 				     bf->bf_segs, &bf->bf_nseg,
33315591b213SSam Leffler 				     BUS_DMA_NOWAIT);
333200a12f3aSSam Leffler 	if (error == EFBIG) {
333300a12f3aSSam Leffler 		/* XXX packet requires too many descriptors */
333400a12f3aSSam Leffler 		bf->bf_nseg = ATH_TXDESC+1;
333500a12f3aSSam Leffler 	} else if (error != 0) {
33365591b213SSam Leffler 		sc->sc_stats.ast_tx_busdma++;
33375591b213SSam Leffler 		m_freem(m0);
33385591b213SSam Leffler 		return error;
33395591b213SSam Leffler 	}
33405591b213SSam Leffler 	/*
33415591b213SSam Leffler 	 * Discard null packets and check for packets that
33425591b213SSam Leffler 	 * require too many TX descriptors.  We try to convert
33435591b213SSam Leffler 	 * the latter to a cluster.
33445591b213SSam Leffler 	 */
33455591b213SSam Leffler 	if (bf->bf_nseg > ATH_TXDESC) {		/* too many desc's, linearize */
33465591b213SSam Leffler 		sc->sc_stats.ast_tx_linear++;
334799d258fdSSam Leffler 		m = ath_defrag(m0, M_DONTWAIT, ATH_TXDESC);
334899d258fdSSam Leffler 		if (m == NULL) {
33495591b213SSam Leffler 			m_freem(m0);
335099d258fdSSam Leffler 			sc->sc_stats.ast_tx_nombuf++;
33515591b213SSam Leffler 			return ENOMEM;
33525591b213SSam Leffler 		}
335399d258fdSSam Leffler 		m0 = m;
3354f9e6219bSSam Leffler 		error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m0,
3355f9e6219bSSam Leffler 					     bf->bf_segs, &bf->bf_nseg,
33565591b213SSam Leffler 					     BUS_DMA_NOWAIT);
33575591b213SSam Leffler 		if (error != 0) {
33585591b213SSam Leffler 			sc->sc_stats.ast_tx_busdma++;
33595591b213SSam Leffler 			m_freem(m0);
33605591b213SSam Leffler 			return error;
33615591b213SSam Leffler 		}
3362f6b8ec16SSam Leffler 		KASSERT(bf->bf_nseg <= ATH_TXDESC,
3363f6b8ec16SSam Leffler 		    ("too many segments after defrag; nseg %u", bf->bf_nseg));
33645591b213SSam Leffler 	} else if (bf->bf_nseg == 0) {		/* null packet, discard */
33655591b213SSam Leffler 		sc->sc_stats.ast_tx_nodata++;
33665591b213SSam Leffler 		m_freem(m0);
33675591b213SSam Leffler 		return EIO;
33685591b213SSam Leffler 	}
3369c42a7b7eSSam Leffler 	DPRINTF(sc, ATH_DEBUG_XMIT, "%s: m %p len %u\n", __func__, m0, pktlen);
33705591b213SSam Leffler 	bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE);
33715591b213SSam Leffler 	bf->bf_m = m0;
33720a915fadSSam Leffler 	bf->bf_node = ni;			/* NB: held reference */
33735591b213SSam Leffler 
33745591b213SSam Leffler 	/* setup descriptors */
33755591b213SSam Leffler 	ds = bf->bf_desc;
33765591b213SSam Leffler 	rt = sc->sc_currates;
33775591b213SSam Leffler 	KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
33785591b213SSam Leffler 
33795591b213SSam Leffler 	/*
3380c42a7b7eSSam Leffler 	 * NB: the 802.11 layer marks whether or not we should
3381c42a7b7eSSam Leffler 	 * use short preamble based on the current mode and
3382c42a7b7eSSam Leffler 	 * negotiated parameters.
33835591b213SSam Leffler 	 */
3384c42a7b7eSSam Leffler 	if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
3385c42a7b7eSSam Leffler 	    (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) {
3386c42a7b7eSSam Leffler 		shortPreamble = AH_TRUE;
3387c42a7b7eSSam Leffler 		sc->sc_stats.ast_tx_shortpre++;
3388c42a7b7eSSam Leffler 	} else {
3389c42a7b7eSSam Leffler 		shortPreamble = AH_FALSE;
3390c42a7b7eSSam Leffler 	}
3391c42a7b7eSSam Leffler 
3392c42a7b7eSSam Leffler 	an = ATH_NODE(ni);
3393c42a7b7eSSam Leffler 	flags = HAL_TXDESC_CLRDMASK;		/* XXX needed for crypto errs */
3394be613480SSam Leffler 	ismrr = 0;				/* default no multi-rate retry*/
3395c42a7b7eSSam Leffler 	/*
3396c42a7b7eSSam Leffler 	 * Calculate Atheros packet type from IEEE80211 packet header,
3397c42a7b7eSSam Leffler 	 * setup for rate calculations, and select h/w transmit queue.
3398c42a7b7eSSam Leffler 	 */
33995591b213SSam Leffler 	switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
34005591b213SSam Leffler 	case IEEE80211_FC0_TYPE_MGT:
34015591b213SSam Leffler 		subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
34025591b213SSam Leffler 		if (subtype == IEEE80211_FC0_SUBTYPE_BEACON)
34035591b213SSam Leffler 			atype = HAL_PKT_TYPE_BEACON;
34045591b213SSam Leffler 		else if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
34055591b213SSam Leffler 			atype = HAL_PKT_TYPE_PROBE_RESP;
34065591b213SSam Leffler 		else if (subtype == IEEE80211_FC0_SUBTYPE_ATIM)
34075591b213SSam Leffler 			atype = HAL_PKT_TYPE_ATIM;
3408c42a7b7eSSam Leffler 		else
3409c42a7b7eSSam Leffler 			atype = HAL_PKT_TYPE_NORMAL;	/* XXX */
341055f63772SSam Leffler 		rix = sc->sc_minrateix;
341155f63772SSam Leffler 		txrate = rt->info[rix].rateCode;
3412c42a7b7eSSam Leffler 		if (shortPreamble)
341355f63772SSam Leffler 			txrate |= rt->info[rix].shortPreamble;
3414be613480SSam Leffler 		try0 = ATH_TXMGTTRY;
3415c42a7b7eSSam Leffler 		/* NB: force all management frames to highest queue */
3416c42a7b7eSSam Leffler 		if (ni->ni_flags & IEEE80211_NODE_QOS) {
3417c42a7b7eSSam Leffler 			/* NB: force all management frames to highest queue */
3418c4c3cb46SSam Leffler 			pri = WME_AC_VO;
3419c42a7b7eSSam Leffler 		} else
3420c4c3cb46SSam Leffler 			pri = WME_AC_BE;
3421c42a7b7eSSam Leffler 		flags |= HAL_TXDESC_INTREQ;	/* force interrupt */
34225591b213SSam Leffler 		break;
34235591b213SSam Leffler 	case IEEE80211_FC0_TYPE_CTL:
3424c42a7b7eSSam Leffler 		atype = HAL_PKT_TYPE_PSPOLL;	/* stop setting of duration */
342555f63772SSam Leffler 		rix = sc->sc_minrateix;
342655f63772SSam Leffler 		txrate = rt->info[rix].rateCode;
3427c42a7b7eSSam Leffler 		if (shortPreamble)
342855f63772SSam Leffler 			txrate |= rt->info[rix].shortPreamble;
3429be613480SSam Leffler 		try0 = ATH_TXMGTTRY;
3430c42a7b7eSSam Leffler 		/* NB: force all ctl frames to highest queue */
3431c42a7b7eSSam Leffler 		if (ni->ni_flags & IEEE80211_NODE_QOS) {
3432c42a7b7eSSam Leffler 			/* NB: force all ctl frames to highest queue */
3433c4c3cb46SSam Leffler 			pri = WME_AC_VO;
3434c42a7b7eSSam Leffler 		} else
3435c4c3cb46SSam Leffler 			pri = WME_AC_BE;
3436c42a7b7eSSam Leffler 		flags |= HAL_TXDESC_INTREQ;	/* force interrupt */
3437c42a7b7eSSam Leffler 		break;
3438c42a7b7eSSam Leffler 	case IEEE80211_FC0_TYPE_DATA:
3439c42a7b7eSSam Leffler 		atype = HAL_PKT_TYPE_NORMAL;		/* default */
3440c42a7b7eSSam Leffler 		/*
34418b5341deSSam Leffler 		 * Data frames: multicast frames go out at a fixed rate,
34428b5341deSSam Leffler 		 * otherwise consult the rate control module for the
34438b5341deSSam Leffler 		 * rate to use.
3444c42a7b7eSSam Leffler 		 */
34458b5341deSSam Leffler 		if (ismcast) {
34468b5341deSSam Leffler 			/*
34478b5341deSSam Leffler 			 * Check mcast rate setting in case it's changed.
34488b5341deSSam Leffler 			 * XXX move out of fastpath
34498b5341deSSam Leffler 			 */
34508b5341deSSam Leffler 			if (ic->ic_mcast_rate != sc->sc_mcastrate) {
34518b5341deSSam Leffler 				sc->sc_mcastrix =
34528b5341deSSam Leffler 					ath_tx_findrix(rt, ic->ic_mcast_rate);
34538b5341deSSam Leffler 				sc->sc_mcastrate = ic->ic_mcast_rate;
34548b5341deSSam Leffler 			}
34558b5341deSSam Leffler 			rix = sc->sc_mcastrix;
34568b5341deSSam Leffler 			txrate = rt->info[rix].rateCode;
34578b5341deSSam Leffler 			if (shortPreamble)
34588b5341deSSam Leffler 				txrate |= rt->info[rix].shortPreamble;
34598b5341deSSam Leffler 			try0 = 1;
34608b5341deSSam Leffler 		} else {
3461c42a7b7eSSam Leffler 			ath_rate_findrate(sc, an, shortPreamble, pktlen,
3462c42a7b7eSSam Leffler 				&rix, &try0, &txrate);
34633e50ec2cSSam Leffler 			sc->sc_txrate = txrate;		/* for LED blinking */
3464be613480SSam Leffler 			if (try0 != ATH_TXMAXTRY)
3465be613480SSam Leffler 				ismrr = 1;
34668b5341deSSam Leffler 		}
3467c42a7b7eSSam Leffler 		/*
3468c42a7b7eSSam Leffler 		 * Default all non-QoS traffic to the background queue.
3469c42a7b7eSSam Leffler 		 */
3470c42a7b7eSSam Leffler 		if (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_QOS) {
3471c4c3cb46SSam Leffler 			pri = M_WME_GETAC(m0);
3472c4c3cb46SSam Leffler 			if (cap->cap_wmeParams[pri].wmep_noackPolicy) {
3473c42a7b7eSSam Leffler 				flags |= HAL_TXDESC_NOACK;
3474aab26fb4SSam Leffler 				sc->sc_stats.ast_tx_noack++;
3475aab26fb4SSam Leffler 			}
3476c42a7b7eSSam Leffler 		} else
3477c4c3cb46SSam Leffler 			pri = WME_AC_BE;
34785591b213SSam Leffler 		break;
34795591b213SSam Leffler 	default:
3480c42a7b7eSSam Leffler 		if_printf(ifp, "bogus frame type 0x%x (%s)\n",
3481c42a7b7eSSam Leffler 			wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK, __func__);
3482c42a7b7eSSam Leffler 		/* XXX statistic */
34835591b213SSam Leffler 		m_freem(m0);
34845591b213SSam Leffler 		return EIO;
34855591b213SSam Leffler 	}
3486c4c3cb46SSam Leffler 	txq = sc->sc_ac2q[pri];
3487c42a7b7eSSam Leffler 
34885591b213SSam Leffler 	/*
3489c42a7b7eSSam Leffler 	 * When servicing one or more stations in power-save mode
3490c42a7b7eSSam Leffler 	 * multicast frames must be buffered until after the beacon.
3491c42a7b7eSSam Leffler 	 * We use the CAB queue for that.
34925591b213SSam Leffler 	 */
3493c42a7b7eSSam Leffler 	if (ismcast && ic->ic_ps_sta) {
3494c42a7b7eSSam Leffler 		txq = sc->sc_cabq;
3495c42a7b7eSSam Leffler 		/* XXX? more bit in 802.11 frame header */
34965591b213SSam Leffler 	}
34975591b213SSam Leffler 
34985591b213SSam Leffler 	/*
34995591b213SSam Leffler 	 * Calculate miscellaneous flags.
35005591b213SSam Leffler 	 */
3501c42a7b7eSSam Leffler 	if (ismcast) {
35025591b213SSam Leffler 		flags |= HAL_TXDESC_NOACK;	/* no ack on broad/multicast */
35035591b213SSam Leffler 		sc->sc_stats.ast_tx_noack++;
35045591b213SSam Leffler 	} else if (pktlen > ic->ic_rtsthreshold) {
35055591b213SSam Leffler 		flags |= HAL_TXDESC_RTSENA;	/* RTS based on frame length */
3506c42a7b7eSSam Leffler 		cix = rt->info[rix].controlRate;
35075591b213SSam Leffler 		sc->sc_stats.ast_tx_rts++;
35085591b213SSam Leffler 	}
35095591b213SSam Leffler 
35105591b213SSam Leffler 	/*
3511c42a7b7eSSam Leffler 	 * If 802.11g protection is enabled, determine whether
3512c42a7b7eSSam Leffler 	 * to use RTS/CTS or just CTS.  Note that this is only
3513c42a7b7eSSam Leffler 	 * done for OFDM unicast frames.
3514c42a7b7eSSam Leffler 	 */
3515c42a7b7eSSam Leffler 	if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
3516c42a7b7eSSam Leffler 	    rt->info[rix].phy == IEEE80211_T_OFDM &&
3517c42a7b7eSSam Leffler 	    (flags & HAL_TXDESC_NOACK) == 0) {
3518c42a7b7eSSam Leffler 		/* XXX fragments must use CCK rates w/ protection */
3519c42a7b7eSSam Leffler 		if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
3520c42a7b7eSSam Leffler 			flags |= HAL_TXDESC_RTSENA;
3521c42a7b7eSSam Leffler 		else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
3522c42a7b7eSSam Leffler 			flags |= HAL_TXDESC_CTSENA;
3523c42a7b7eSSam Leffler 		cix = rt->info[sc->sc_protrix].controlRate;
3524c42a7b7eSSam Leffler 		sc->sc_stats.ast_tx_protect++;
3525c42a7b7eSSam Leffler 	}
3526c42a7b7eSSam Leffler 
3527c42a7b7eSSam Leffler 	/*
3528f6aa038bSSam Leffler 	 * Calculate duration.  This logically belongs in the 802.11
3529f6aa038bSSam Leffler 	 * layer but it lacks sufficient information to calculate it.
3530f6aa038bSSam Leffler 	 */
3531f6aa038bSSam Leffler 	if ((flags & HAL_TXDESC_NOACK) == 0 &&
3532f6aa038bSSam Leffler 	    (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_CTL) {
3533f6aa038bSSam Leffler 		u_int16_t dur;
3534f6aa038bSSam Leffler 		/*
3535f6aa038bSSam Leffler 		 * XXX not right with fragmentation.
3536f6aa038bSSam Leffler 		 */
3537c42a7b7eSSam Leffler 		if (shortPreamble)
3538c42a7b7eSSam Leffler 			dur = rt->info[rix].spAckDuration;
3539c42a7b7eSSam Leffler 		else
3540c42a7b7eSSam Leffler 			dur = rt->info[rix].lpAckDuration;
3541c42a7b7eSSam Leffler 		*(u_int16_t *)wh->i_dur = htole16(dur);
3542f6aa038bSSam Leffler 	}
3543f6aa038bSSam Leffler 
3544f6aa038bSSam Leffler 	/*
35455591b213SSam Leffler 	 * Calculate RTS/CTS rate and duration if needed.
35465591b213SSam Leffler 	 */
35475591b213SSam Leffler 	ctsduration = 0;
35485591b213SSam Leffler 	if (flags & (HAL_TXDESC_RTSENA|HAL_TXDESC_CTSENA)) {
35495591b213SSam Leffler 		/*
35505591b213SSam Leffler 		 * CTS transmit rate is derived from the transmit rate
35515591b213SSam Leffler 		 * by looking in the h/w rate table.  We must also factor
35525591b213SSam Leffler 		 * in whether or not a short preamble is to be used.
35535591b213SSam Leffler 		 */
3554c42a7b7eSSam Leffler 		/* NB: cix is set above where RTS/CTS is enabled */
3555c42a7b7eSSam Leffler 		KASSERT(cix != 0xff, ("cix not setup"));
35565591b213SSam Leffler 		ctsrate = rt->info[cix].rateCode;
35575591b213SSam Leffler 		/*
3558c42a7b7eSSam Leffler 		 * Compute the transmit duration based on the frame
3559c42a7b7eSSam Leffler 		 * size and the size of an ACK frame.  We call into the
3560c42a7b7eSSam Leffler 		 * HAL to do the computation since it depends on the
3561c42a7b7eSSam Leffler 		 * characteristics of the actual PHY being used.
3562c42a7b7eSSam Leffler 		 *
3563c42a7b7eSSam Leffler 		 * NB: CTS is assumed the same size as an ACK so we can
3564c42a7b7eSSam Leffler 		 *     use the precalculated ACK durations.
35655591b213SSam Leffler 		 */
3566c42a7b7eSSam Leffler 		if (shortPreamble) {
3567c42a7b7eSSam Leffler 			ctsrate |= rt->info[cix].shortPreamble;
3568c42a7b7eSSam Leffler 			if (flags & HAL_TXDESC_RTSENA)		/* SIFS + CTS */
3569c42a7b7eSSam Leffler 				ctsduration += rt->info[cix].spAckDuration;
35705591b213SSam Leffler 			ctsduration += ath_hal_computetxtime(ah,
3571c42a7b7eSSam Leffler 				rt, pktlen, rix, AH_TRUE);
3572c42a7b7eSSam Leffler 			if ((flags & HAL_TXDESC_NOACK) == 0)	/* SIFS + ACK */
35736ee571b2SSam Leffler 				ctsduration += rt->info[rix].spAckDuration;
3574c42a7b7eSSam Leffler 		} else {
3575c42a7b7eSSam Leffler 			if (flags & HAL_TXDESC_RTSENA)		/* SIFS + CTS */
3576c42a7b7eSSam Leffler 				ctsduration += rt->info[cix].lpAckDuration;
3577c42a7b7eSSam Leffler 			ctsduration += ath_hal_computetxtime(ah,
3578c42a7b7eSSam Leffler 				rt, pktlen, rix, AH_FALSE);
3579c42a7b7eSSam Leffler 			if ((flags & HAL_TXDESC_NOACK) == 0)	/* SIFS + ACK */
35806ee571b2SSam Leffler 				ctsduration += rt->info[rix].lpAckDuration;
35815591b213SSam Leffler 		}
3582c42a7b7eSSam Leffler 		/*
3583c42a7b7eSSam Leffler 		 * Must disable multi-rate retry when using RTS/CTS.
3584c42a7b7eSSam Leffler 		 */
3585be613480SSam Leffler 		ismrr = 0;
3586be613480SSam Leffler 		try0 = ATH_TXMGTTRY;		/* XXX */
35875591b213SSam Leffler 	} else
35885591b213SSam Leffler 		ctsrate = 0;
35895591b213SSam Leffler 
3590c42a7b7eSSam Leffler 	if (IFF_DUMPPKTS(sc, ATH_DEBUG_XMIT))
3591c42a7b7eSSam Leffler 		ieee80211_dump_pkt(mtod(m0, caddr_t), m0->m_len,
35923e50ec2cSSam Leffler 			sc->sc_hwmap[txrate].ieeerate, -1);
35935591b213SSam Leffler 
3594eb2cdcb1SSam Leffler 	if (ic->ic_rawbpf)
3595eb2cdcb1SSam Leffler 		bpf_mtap(ic->ic_rawbpf, m0);
3596eb2cdcb1SSam Leffler 	if (sc->sc_drvbpf) {
35977b0c77ecSSam Leffler 		u_int64_t tsf = ath_hal_gettsf64(ah);
35987b0c77ecSSam Leffler 
35997b0c77ecSSam Leffler 		sc->sc_tx_th.wt_tsf = htole64(tsf);
3600d3be6f5bSSam Leffler 		sc->sc_tx_th.wt_flags = sc->sc_hwmap[txrate].txflags;
3601eb2cdcb1SSam Leffler 		if (iswep)
3602eb2cdcb1SSam Leffler 			sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_WEP;
36033e50ec2cSSam Leffler 		sc->sc_tx_th.wt_rate = sc->sc_hwmap[txrate].ieeerate;
3604c42a7b7eSSam Leffler 		sc->sc_tx_th.wt_txpower = ni->ni_txpower;
3605c42a7b7eSSam Leffler 		sc->sc_tx_th.wt_antenna = sc->sc_txantenna;
3606eb2cdcb1SSam Leffler 
3607eb2cdcb1SSam Leffler 		bpf_mtap2(sc->sc_drvbpf,
36082f1ad18bSSam Leffler 			&sc->sc_tx_th, sc->sc_tx_th_len, m0);
3609eb2cdcb1SSam Leffler 	}
3610eb2cdcb1SSam Leffler 
36115591b213SSam Leffler 	/*
3612c42a7b7eSSam Leffler 	 * Determine if a tx interrupt should be generated for
3613c42a7b7eSSam Leffler 	 * this descriptor.  We take a tx interrupt to reap
3614c42a7b7eSSam Leffler 	 * descriptors when the h/w hits an EOL condition or
3615c42a7b7eSSam Leffler 	 * when the descriptor is specifically marked to generate
3616c42a7b7eSSam Leffler 	 * an interrupt.  We periodically mark descriptors in this
3617c42a7b7eSSam Leffler 	 * way to insure timely replenishing of the supply needed
3618c42a7b7eSSam Leffler 	 * for sending frames.  Defering interrupts reduces system
3619c42a7b7eSSam Leffler 	 * load and potentially allows more concurrent work to be
3620c42a7b7eSSam Leffler 	 * done but if done to aggressively can cause senders to
3621c42a7b7eSSam Leffler 	 * backup.
3622c42a7b7eSSam Leffler 	 *
3623c42a7b7eSSam Leffler 	 * NB: use >= to deal with sc_txintrperiod changing
3624c42a7b7eSSam Leffler 	 *     dynamically through sysctl.
3625c42a7b7eSSam Leffler 	 */
3626c42a7b7eSSam Leffler 	if (flags & HAL_TXDESC_INTREQ) {
3627c42a7b7eSSam Leffler 		txq->axq_intrcnt = 0;
3628c42a7b7eSSam Leffler 	} else if (++txq->axq_intrcnt >= sc->sc_txintrperiod) {
3629c42a7b7eSSam Leffler 		flags |= HAL_TXDESC_INTREQ;
3630c42a7b7eSSam Leffler 		txq->axq_intrcnt = 0;
3631c42a7b7eSSam Leffler 	}
3632c42a7b7eSSam Leffler 
3633c42a7b7eSSam Leffler 	/*
36345591b213SSam Leffler 	 * Formulate first tx descriptor with tx controls.
36355591b213SSam Leffler 	 */
36365591b213SSam Leffler 	/* XXX check return value? */
36375591b213SSam Leffler 	ath_hal_setuptxdesc(ah, ds
36385591b213SSam Leffler 		, pktlen		/* packet length */
36395591b213SSam Leffler 		, hdrlen		/* header length */
36405591b213SSam Leffler 		, atype			/* Atheros packet type */
3641c42a7b7eSSam Leffler 		, ni->ni_txpower	/* txpower */
3642c42a7b7eSSam Leffler 		, txrate, try0		/* series 0 rate/tries */
3643c42a7b7eSSam Leffler 		, keyix			/* key cache index */
3644c42a7b7eSSam Leffler 		, sc->sc_txantenna	/* antenna mode */
36455591b213SSam Leffler 		, flags			/* flags */
36465591b213SSam Leffler 		, ctsrate		/* rts/cts rate */
36475591b213SSam Leffler 		, ctsduration		/* rts/cts duration */
36485591b213SSam Leffler 	);
36498f409431SSam Leffler 	bf->bf_flags = flags;
3650c42a7b7eSSam Leffler 	/*
3651c42a7b7eSSam Leffler 	 * Setup the multi-rate retry state only when we're
3652c42a7b7eSSam Leffler 	 * going to use it.  This assumes ath_hal_setuptxdesc
3653c42a7b7eSSam Leffler 	 * initializes the descriptors (so we don't have to)
3654c42a7b7eSSam Leffler 	 * when the hardware supports multi-rate retry and
3655c42a7b7eSSam Leffler 	 * we don't use it.
3656c42a7b7eSSam Leffler 	 */
3657be613480SSam Leffler 	if (ismrr)
3658c42a7b7eSSam Leffler 		ath_rate_setupxtxdesc(sc, an, ds, shortPreamble, rix);
3659c42a7b7eSSam Leffler 
36605591b213SSam Leffler 	/*
36615591b213SSam Leffler 	 * Fillin the remainder of the descriptor info.
36625591b213SSam Leffler 	 */
3663c42a7b7eSSam Leffler 	ds0 = ds;
36645591b213SSam Leffler 	for (i = 0; i < bf->bf_nseg; i++, ds++) {
36655591b213SSam Leffler 		ds->ds_data = bf->bf_segs[i].ds_addr;
36665591b213SSam Leffler 		if (i == bf->bf_nseg - 1)
36675591b213SSam Leffler 			ds->ds_link = 0;
36685591b213SSam Leffler 		else
36695591b213SSam Leffler 			ds->ds_link = bf->bf_daddr + sizeof(*ds) * (i + 1);
36705591b213SSam Leffler 		ath_hal_filltxdesc(ah, ds
36715591b213SSam Leffler 			, bf->bf_segs[i].ds_len	/* segment length */
36725591b213SSam Leffler 			, i == 0		/* first segment */
36735591b213SSam Leffler 			, i == bf->bf_nseg - 1	/* last segment */
3674c42a7b7eSSam Leffler 			, ds0			/* first descriptor */
36755591b213SSam Leffler 		);
3676c42a7b7eSSam Leffler 		DPRINTF(sc, ATH_DEBUG_XMIT,
3677c42a7b7eSSam Leffler 			"%s: %d: %08x %08x %08x %08x %08x %08x\n",
3678e325e530SSam Leffler 			__func__, i, ds->ds_link, ds->ds_data,
3679c42a7b7eSSam Leffler 			ds->ds_ctl0, ds->ds_ctl1, ds->ds_hw[0], ds->ds_hw[1]);
36805591b213SSam Leffler 	}
36815591b213SSam Leffler 	/*
36825591b213SSam Leffler 	 * Insert the frame on the outbound list and
36835591b213SSam Leffler 	 * pass it on to the hardware.
36845591b213SSam Leffler 	 */
3685c42a7b7eSSam Leffler 	ATH_TXQ_LOCK(txq);
3686c42a7b7eSSam Leffler 	ATH_TXQ_INSERT_TAIL(txq, bf, bf_list);
3687c42a7b7eSSam Leffler 	if (txq->axq_link == NULL) {
3688c42a7b7eSSam Leffler 		ath_hal_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr);
3689c42a7b7eSSam Leffler 		DPRINTF(sc, ATH_DEBUG_XMIT,
3690c42a7b7eSSam Leffler 			"%s: TXDP[%u] = %p (%p) depth %d\n", __func__,
3691c42a7b7eSSam Leffler 			txq->axq_qnum, (caddr_t)bf->bf_daddr, bf->bf_desc,
3692c42a7b7eSSam Leffler 			txq->axq_depth);
36935591b213SSam Leffler 	} else {
3694c42a7b7eSSam Leffler 		*txq->axq_link = bf->bf_daddr;
3695c42a7b7eSSam Leffler 		DPRINTF(sc, ATH_DEBUG_XMIT,
3696c42a7b7eSSam Leffler 			"%s: link[%u](%p)=%p (%p) depth %d\n", __func__,
3697c42a7b7eSSam Leffler 			txq->axq_qnum, txq->axq_link,
3698c42a7b7eSSam Leffler 			(caddr_t)bf->bf_daddr, bf->bf_desc, txq->axq_depth);
36995591b213SSam Leffler 	}
3700c42a7b7eSSam Leffler 	txq->axq_link = &bf->bf_desc[bf->bf_nseg - 1].ds_link;
3701c42a7b7eSSam Leffler 	/*
3702c42a7b7eSSam Leffler 	 * The CAB queue is started from the SWBA handler since
3703c42a7b7eSSam Leffler 	 * frames only go out on DTIM and to avoid possible races.
3704c42a7b7eSSam Leffler 	 */
3705c42a7b7eSSam Leffler 	if (txq != sc->sc_cabq)
3706c42a7b7eSSam Leffler 		ath_hal_txstart(ah, txq->axq_qnum);
3707a8d7e0f6SSam Leffler 	ATH_TXQ_UNLOCK(txq);
3708a8d7e0f6SSam Leffler 
37095591b213SSam Leffler 	return 0;
37105591b213SSam Leffler }
37115591b213SSam Leffler 
3712c42a7b7eSSam Leffler /*
3713c42a7b7eSSam Leffler  * Process completed xmit descriptors from the specified queue.
3714c42a7b7eSSam Leffler  */
3715d7736e13SSam Leffler static int
3716c42a7b7eSSam Leffler ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
37175591b213SSam Leffler {
37185591b213SSam Leffler 	struct ath_hal *ah = sc->sc_ah;
37190a915fadSSam Leffler 	struct ieee80211com *ic = &sc->sc_ic;
3720c42a7b7eSSam Leffler 	struct ath_buf *bf;
3721c4c3cb46SSam Leffler 	struct ath_desc *ds, *ds0;
37225591b213SSam Leffler 	struct ieee80211_node *ni;
37235591b213SSam Leffler 	struct ath_node *an;
3724d7736e13SSam Leffler 	int sr, lr, pri, nacked;
37255591b213SSam Leffler 	HAL_STATUS status;
37265591b213SSam Leffler 
3727c42a7b7eSSam Leffler 	DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: tx queue %u head %p link %p\n",
3728c42a7b7eSSam Leffler 		__func__, txq->axq_qnum,
3729c42a7b7eSSam Leffler 		(caddr_t)(uintptr_t) ath_hal_gettxbuf(sc->sc_ah, txq->axq_qnum),
3730c42a7b7eSSam Leffler 		txq->axq_link);
3731d7736e13SSam Leffler 	nacked = 0;
37325591b213SSam Leffler 	for (;;) {
3733c42a7b7eSSam Leffler 		ATH_TXQ_LOCK(txq);
3734c42a7b7eSSam Leffler 		txq->axq_intrcnt = 0;	/* reset periodic desc intr count */
3735c42a7b7eSSam Leffler 		bf = STAILQ_FIRST(&txq->axq_q);
37365591b213SSam Leffler 		if (bf == NULL) {
3737c42a7b7eSSam Leffler 			txq->axq_link = NULL;
3738c42a7b7eSSam Leffler 			ATH_TXQ_UNLOCK(txq);
37395591b213SSam Leffler 			break;
37405591b213SSam Leffler 		}
3741c4c3cb46SSam Leffler 		ds0 = &bf->bf_desc[0];
37425591b213SSam Leffler 		ds = &bf->bf_desc[bf->bf_nseg - 1];
37435591b213SSam Leffler 		status = ath_hal_txprocdesc(ah, ds);
37445591b213SSam Leffler #ifdef AR_DEBUG
3745c42a7b7eSSam Leffler 		if (sc->sc_debug & ATH_DEBUG_XMIT_DESC)
37465591b213SSam Leffler 			ath_printtxbuf(bf, status == HAL_OK);
37475591b213SSam Leffler #endif
37485591b213SSam Leffler 		if (status == HAL_EINPROGRESS) {
3749c42a7b7eSSam Leffler 			ATH_TXQ_UNLOCK(txq);
37505591b213SSam Leffler 			break;
37515591b213SSam Leffler 		}
3752c42a7b7eSSam Leffler 		ATH_TXQ_REMOVE_HEAD(txq, bf_list);
3753c42a7b7eSSam Leffler 		ATH_TXQ_UNLOCK(txq);
37545591b213SSam Leffler 
37555591b213SSam Leffler 		ni = bf->bf_node;
37565591b213SSam Leffler 		if (ni != NULL) {
3757c42a7b7eSSam Leffler 			an = ATH_NODE(ni);
37585591b213SSam Leffler 			if (ds->ds_txstat.ts_status == 0) {
3759c42a7b7eSSam Leffler 				u_int8_t txant = ds->ds_txstat.ts_antenna;
3760c42a7b7eSSam Leffler 				sc->sc_stats.ast_ant_tx[txant]++;
3761c42a7b7eSSam Leffler 				sc->sc_ant_tx[txant]++;
3762c42a7b7eSSam Leffler 				if (ds->ds_txstat.ts_rate & HAL_TXSTAT_ALTRATE)
3763c42a7b7eSSam Leffler 					sc->sc_stats.ast_tx_altrate++;
3764c42a7b7eSSam Leffler 				sc->sc_stats.ast_tx_rssi =
3765c42a7b7eSSam Leffler 					ds->ds_txstat.ts_rssi;
3766ffa2cab6SSam Leffler 				ATH_RSSI_LPF(sc->sc_halstats.ns_avgtxrssi,
3767c42a7b7eSSam Leffler 					ds->ds_txstat.ts_rssi);
3768c42a7b7eSSam Leffler 				pri = M_WME_GETAC(bf->bf_m);
3769c42a7b7eSSam Leffler 				if (pri >= WME_AC_VO)
3770c42a7b7eSSam Leffler 					ic->ic_wme.wme_hipri_traffic++;
3771c42a7b7eSSam Leffler 				ni->ni_inact = ni->ni_inact_reload;
37725591b213SSam Leffler 			} else {
37735591b213SSam Leffler 				if (ds->ds_txstat.ts_status & HAL_TXERR_XRETRY)
37745591b213SSam Leffler 					sc->sc_stats.ast_tx_xretries++;
37755591b213SSam Leffler 				if (ds->ds_txstat.ts_status & HAL_TXERR_FIFO)
37765591b213SSam Leffler 					sc->sc_stats.ast_tx_fifoerr++;
37775591b213SSam Leffler 				if (ds->ds_txstat.ts_status & HAL_TXERR_FILT)
37785591b213SSam Leffler 					sc->sc_stats.ast_tx_filtered++;
37795591b213SSam Leffler 			}
37805591b213SSam Leffler 			sr = ds->ds_txstat.ts_shortretry;
37815591b213SSam Leffler 			lr = ds->ds_txstat.ts_longretry;
37825591b213SSam Leffler 			sc->sc_stats.ast_tx_shortretry += sr;
37835591b213SSam Leffler 			sc->sc_stats.ast_tx_longretry += lr;
3784c42a7b7eSSam Leffler 			/*
3785c42a7b7eSSam Leffler 			 * Hand the descriptor to the rate control algorithm.
3786c42a7b7eSSam Leffler 			 */
37878f409431SSam Leffler 			if ((ds->ds_txstat.ts_status & HAL_TXERR_FILT) == 0 &&
3788d7736e13SSam Leffler 			    (bf->bf_flags & HAL_TXDESC_NOACK) == 0) {
3789d7736e13SSam Leffler 				/*
3790d7736e13SSam Leffler 				 * If frame was ack'd update the last rx time
3791d7736e13SSam Leffler 				 * used to workaround phantom bmiss interrupts.
3792d7736e13SSam Leffler 				 */
3793d7736e13SSam Leffler 				if (ds->ds_txstat.ts_status == 0)
3794d7736e13SSam Leffler 					nacked++;
379522233301SSam Leffler 				ath_rate_tx_complete(sc, an, ds, ds0);
3796d7736e13SSam Leffler 			}
37970a915fadSSam Leffler 			/*
37980a915fadSSam Leffler 			 * Reclaim reference to node.
37990a915fadSSam Leffler 			 *
38000a915fadSSam Leffler 			 * NB: the node may be reclaimed here if, for example
38010a915fadSSam Leffler 			 *     this is a DEAUTH message that was sent and the
38020a915fadSSam Leffler 			 *     node was timed out due to inactivity.
38030a915fadSSam Leffler 			 */
3804c42a7b7eSSam Leffler 			ieee80211_free_node(ni);
38055591b213SSam Leffler 		}
38065591b213SSam Leffler 		bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap,
38075591b213SSam Leffler 		    BUS_DMASYNC_POSTWRITE);
38085591b213SSam Leffler 		bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
38095591b213SSam Leffler 		m_freem(bf->bf_m);
38105591b213SSam Leffler 		bf->bf_m = NULL;
38115591b213SSam Leffler 		bf->bf_node = NULL;
38125591b213SSam Leffler 
3813f0b2a0beSSam Leffler 		ATH_TXBUF_LOCK(sc);
3814c42a7b7eSSam Leffler 		STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
3815f0b2a0beSSam Leffler 		ATH_TXBUF_UNLOCK(sc);
38165591b213SSam Leffler 	}
3817d7736e13SSam Leffler 	return nacked;
3818d7736e13SSam Leffler }
3819d7736e13SSam Leffler 
3820d7736e13SSam Leffler static __inline int
3821d7736e13SSam Leffler txqactive(struct ath_hal *ah, int qnum)
3822d7736e13SSam Leffler {
3823d7736e13SSam Leffler 	/* XXX not yet */
3824d7736e13SSam Leffler 	return 1;
3825c42a7b7eSSam Leffler }
3826c42a7b7eSSam Leffler 
3827c42a7b7eSSam Leffler /*
3828c42a7b7eSSam Leffler  * Deferred processing of transmit interrupt; special-cased
3829c42a7b7eSSam Leffler  * for a single hardware transmit queue (e.g. 5210 and 5211).
3830c42a7b7eSSam Leffler  */
3831c42a7b7eSSam Leffler static void
3832c42a7b7eSSam Leffler ath_tx_proc_q0(void *arg, int npending)
3833c42a7b7eSSam Leffler {
3834c42a7b7eSSam Leffler 	struct ath_softc *sc = arg;
3835fc74a9f9SBrooks Davis 	struct ifnet *ifp = sc->sc_ifp;
3836c42a7b7eSSam Leffler 
3837d7736e13SSam Leffler 	if (txqactive(sc->sc_ah, 0) && ath_tx_processq(sc, &sc->sc_txq[0]))
3838d7736e13SSam Leffler 		sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah);
3839d7736e13SSam Leffler 	if (txqactive(sc->sc_ah, sc->sc_cabq->axq_qnum))
3840d7736e13SSam Leffler 		ath_tx_processq(sc, sc->sc_cabq);
3841c42a7b7eSSam Leffler 	ath_tx_processq(sc, sc->sc_cabq);
384213f4c340SRobert Watson 	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
38435591b213SSam Leffler 	sc->sc_tx_timer = 0;
38445591b213SSam Leffler 
38453e50ec2cSSam Leffler 	if (sc->sc_softled)
38463e50ec2cSSam Leffler 		ath_led_event(sc, ATH_LED_TX);
38473e50ec2cSSam Leffler 
38485591b213SSam Leffler 	ath_start(ifp);
38495591b213SSam Leffler }
38505591b213SSam Leffler 
38515591b213SSam Leffler /*
3852c42a7b7eSSam Leffler  * Deferred processing of transmit interrupt; special-cased
3853c42a7b7eSSam Leffler  * for four hardware queues, 0-3 (e.g. 5212 w/ WME support).
38545591b213SSam Leffler  */
38555591b213SSam Leffler static void
3856c42a7b7eSSam Leffler ath_tx_proc_q0123(void *arg, int npending)
3857c42a7b7eSSam Leffler {
3858c42a7b7eSSam Leffler 	struct ath_softc *sc = arg;
3859fc74a9f9SBrooks Davis 	struct ifnet *ifp = sc->sc_ifp;
3860d7736e13SSam Leffler 	int nacked;
3861c42a7b7eSSam Leffler 
3862c42a7b7eSSam Leffler 	/*
3863c42a7b7eSSam Leffler 	 * Process each active queue.
3864c42a7b7eSSam Leffler 	 */
3865d7736e13SSam Leffler 	nacked = 0;
3866d7736e13SSam Leffler 	if (txqactive(sc->sc_ah, 0))
3867d7736e13SSam Leffler 		nacked += ath_tx_processq(sc, &sc->sc_txq[0]);
3868d7736e13SSam Leffler 	if (txqactive(sc->sc_ah, 1))
3869d7736e13SSam Leffler 		nacked += ath_tx_processq(sc, &sc->sc_txq[1]);
3870d7736e13SSam Leffler 	if (txqactive(sc->sc_ah, 2))
3871d7736e13SSam Leffler 		nacked += ath_tx_processq(sc, &sc->sc_txq[2]);
3872d7736e13SSam Leffler 	if (txqactive(sc->sc_ah, 3))
3873d7736e13SSam Leffler 		nacked += ath_tx_processq(sc, &sc->sc_txq[3]);
3874d7736e13SSam Leffler 	if (txqactive(sc->sc_ah, sc->sc_cabq->axq_qnum))
3875c42a7b7eSSam Leffler 		ath_tx_processq(sc, sc->sc_cabq);
3876d7736e13SSam Leffler 	if (nacked)
3877d7736e13SSam Leffler 		sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah);
3878c42a7b7eSSam Leffler 
387913f4c340SRobert Watson 	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
3880c42a7b7eSSam Leffler 	sc->sc_tx_timer = 0;
3881c42a7b7eSSam Leffler 
38823e50ec2cSSam Leffler 	if (sc->sc_softled)
38833e50ec2cSSam Leffler 		ath_led_event(sc, ATH_LED_TX);
38843e50ec2cSSam Leffler 
3885c42a7b7eSSam Leffler 	ath_start(ifp);
3886c42a7b7eSSam Leffler }
3887c42a7b7eSSam Leffler 
3888c42a7b7eSSam Leffler /*
3889c42a7b7eSSam Leffler  * Deferred processing of transmit interrupt.
3890c42a7b7eSSam Leffler  */
3891c42a7b7eSSam Leffler static void
3892c42a7b7eSSam Leffler ath_tx_proc(void *arg, int npending)
3893c42a7b7eSSam Leffler {
3894c42a7b7eSSam Leffler 	struct ath_softc *sc = arg;
3895fc74a9f9SBrooks Davis 	struct ifnet *ifp = sc->sc_ifp;
3896d7736e13SSam Leffler 	int i, nacked;
3897c42a7b7eSSam Leffler 
3898c42a7b7eSSam Leffler 	/*
3899c42a7b7eSSam Leffler 	 * Process each active queue.
3900c42a7b7eSSam Leffler 	 */
3901d7736e13SSam Leffler 	nacked = 0;
3902c42a7b7eSSam Leffler 	for (i = 0; i < HAL_NUM_TX_QUEUES; i++)
3903d7736e13SSam Leffler 		if (ATH_TXQ_SETUP(sc, i) && txqactive(sc->sc_ah, i))
3904d7736e13SSam Leffler 			nacked += ath_tx_processq(sc, &sc->sc_txq[i]);
3905d7736e13SSam Leffler 	if (nacked)
3906d7736e13SSam Leffler 		sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah);
3907c42a7b7eSSam Leffler 
390813f4c340SRobert Watson 	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
3909c42a7b7eSSam Leffler 	sc->sc_tx_timer = 0;
3910c42a7b7eSSam Leffler 
39113e50ec2cSSam Leffler 	if (sc->sc_softled)
39123e50ec2cSSam Leffler 		ath_led_event(sc, ATH_LED_TX);
39133e50ec2cSSam Leffler 
3914c42a7b7eSSam Leffler 	ath_start(ifp);
3915c42a7b7eSSam Leffler }
3916c42a7b7eSSam Leffler 
3917c42a7b7eSSam Leffler static void
3918c42a7b7eSSam Leffler ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq)
39195591b213SSam Leffler {
39205591b213SSam Leffler 	struct ath_hal *ah = sc->sc_ah;
392123428eafSSam Leffler 	struct ieee80211_node *ni;
39225591b213SSam Leffler 	struct ath_buf *bf;
39235591b213SSam Leffler 
3924c42a7b7eSSam Leffler 	/*
3925c42a7b7eSSam Leffler 	 * NB: this assumes output has been stopped and
3926c42a7b7eSSam Leffler 	 *     we do not need to block ath_tx_tasklet
3927c42a7b7eSSam Leffler 	 */
39285591b213SSam Leffler 	for (;;) {
3929c42a7b7eSSam Leffler 		ATH_TXQ_LOCK(txq);
3930c42a7b7eSSam Leffler 		bf = STAILQ_FIRST(&txq->axq_q);
39315591b213SSam Leffler 		if (bf == NULL) {
3932c42a7b7eSSam Leffler 			txq->axq_link = NULL;
3933c42a7b7eSSam Leffler 			ATH_TXQ_UNLOCK(txq);
39345591b213SSam Leffler 			break;
39355591b213SSam Leffler 		}
3936c42a7b7eSSam Leffler 		ATH_TXQ_REMOVE_HEAD(txq, bf_list);
3937c42a7b7eSSam Leffler 		ATH_TXQ_UNLOCK(txq);
39385591b213SSam Leffler #ifdef AR_DEBUG
3939c42a7b7eSSam Leffler 		if (sc->sc_debug & ATH_DEBUG_RESET)
39405591b213SSam Leffler 			ath_printtxbuf(bf,
39415591b213SSam Leffler 				ath_hal_txprocdesc(ah, bf->bf_desc) == HAL_OK);
39425591b213SSam Leffler #endif /* AR_DEBUG */
39435591b213SSam Leffler 		bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
39445591b213SSam Leffler 		m_freem(bf->bf_m);
39455591b213SSam Leffler 		bf->bf_m = NULL;
394623428eafSSam Leffler 		ni = bf->bf_node;
39475591b213SSam Leffler 		bf->bf_node = NULL;
3948c42a7b7eSSam Leffler 		if (ni != NULL) {
394923428eafSSam Leffler 			/*
395023428eafSSam Leffler 			 * Reclaim node reference.
395123428eafSSam Leffler 			 */
3952c42a7b7eSSam Leffler 			ieee80211_free_node(ni);
395323428eafSSam Leffler 		}
3954f0b2a0beSSam Leffler 		ATH_TXBUF_LOCK(sc);
3955c42a7b7eSSam Leffler 		STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
3956f0b2a0beSSam Leffler 		ATH_TXBUF_UNLOCK(sc);
39575591b213SSam Leffler 	}
3958c42a7b7eSSam Leffler }
3959c42a7b7eSSam Leffler 
3960c42a7b7eSSam Leffler static void
3961c42a7b7eSSam Leffler ath_tx_stopdma(struct ath_softc *sc, struct ath_txq *txq)
3962c42a7b7eSSam Leffler {
3963c42a7b7eSSam Leffler 	struct ath_hal *ah = sc->sc_ah;
3964c42a7b7eSSam Leffler 
3965c42a7b7eSSam Leffler 	(void) ath_hal_stoptxdma(ah, txq->axq_qnum);
3966c42a7b7eSSam Leffler 	DPRINTF(sc, ATH_DEBUG_RESET, "%s: tx queue [%u] %p, link %p\n",
3967c42a7b7eSSam Leffler 	    __func__, txq->axq_qnum,
39686891c875SPeter Wemm 	    (caddr_t)(uintptr_t) ath_hal_gettxbuf(ah, txq->axq_qnum),
39696891c875SPeter Wemm 	    txq->axq_link);
3970c42a7b7eSSam Leffler }
3971c42a7b7eSSam Leffler 
3972c42a7b7eSSam Leffler /*
3973c42a7b7eSSam Leffler  * Drain the transmit queues and reclaim resources.
3974c42a7b7eSSam Leffler  */
3975c42a7b7eSSam Leffler static void
3976c42a7b7eSSam Leffler ath_draintxq(struct ath_softc *sc)
3977c42a7b7eSSam Leffler {
3978c42a7b7eSSam Leffler 	struct ath_hal *ah = sc->sc_ah;
3979fc74a9f9SBrooks Davis 	struct ifnet *ifp = sc->sc_ifp;
3980c42a7b7eSSam Leffler 	int i;
3981c42a7b7eSSam Leffler 
3982c42a7b7eSSam Leffler 	/* XXX return value */
3983c42a7b7eSSam Leffler 	if (!sc->sc_invalid) {
3984c42a7b7eSSam Leffler 		/* don't touch the hardware if marked invalid */
3985c42a7b7eSSam Leffler 		(void) ath_hal_stoptxdma(ah, sc->sc_bhalq);
3986c42a7b7eSSam Leffler 		DPRINTF(sc, ATH_DEBUG_RESET,
3987c42a7b7eSSam Leffler 		    "%s: beacon queue %p\n", __func__,
3988c42a7b7eSSam Leffler 		    (caddr_t)(uintptr_t) ath_hal_gettxbuf(ah, sc->sc_bhalq));
3989c42a7b7eSSam Leffler 		for (i = 0; i < HAL_NUM_TX_QUEUES; i++)
3990c42a7b7eSSam Leffler 			if (ATH_TXQ_SETUP(sc, i))
3991c42a7b7eSSam Leffler 				ath_tx_stopdma(sc, &sc->sc_txq[i]);
3992c42a7b7eSSam Leffler 	}
3993c42a7b7eSSam Leffler 	for (i = 0; i < HAL_NUM_TX_QUEUES; i++)
3994c42a7b7eSSam Leffler 		if (ATH_TXQ_SETUP(sc, i))
3995c42a7b7eSSam Leffler 			ath_tx_draintxq(sc, &sc->sc_txq[i]);
399613f4c340SRobert Watson 	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
39975591b213SSam Leffler 	sc->sc_tx_timer = 0;
39985591b213SSam Leffler }
39995591b213SSam Leffler 
40005591b213SSam Leffler /*
40015591b213SSam Leffler  * Disable the receive h/w in preparation for a reset.
40025591b213SSam Leffler  */
40035591b213SSam Leffler static void
40045591b213SSam Leffler ath_stoprecv(struct ath_softc *sc)
40055591b213SSam Leffler {
40068cec0ab9SSam Leffler #define	PA2DESC(_sc, _pa) \
4007c42a7b7eSSam Leffler 	((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \
4008c42a7b7eSSam Leffler 		((_pa) - (_sc)->sc_rxdma.dd_desc_paddr)))
40095591b213SSam Leffler 	struct ath_hal *ah = sc->sc_ah;
40105591b213SSam Leffler 
40115591b213SSam Leffler 	ath_hal_stoppcurecv(ah);	/* disable PCU */
40125591b213SSam Leffler 	ath_hal_setrxfilter(ah, 0);	/* clear recv filter */
40135591b213SSam Leffler 	ath_hal_stopdmarecv(ah);	/* disable DMA engine */
4014c42a7b7eSSam Leffler 	DELAY(3000);			/* 3ms is long enough for 1 frame */
40155591b213SSam Leffler #ifdef AR_DEBUG
4016c42a7b7eSSam Leffler 	if (sc->sc_debug & (ATH_DEBUG_RESET | ATH_DEBUG_FATAL)) {
40175591b213SSam Leffler 		struct ath_buf *bf;
40185591b213SSam Leffler 
4019e325e530SSam Leffler 		printf("%s: rx queue %p, link %p\n", __func__,
402030310634SPeter Wemm 			(caddr_t)(uintptr_t) ath_hal_getrxbuf(ah), sc->sc_rxlink);
4021c42a7b7eSSam Leffler 		STAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) {
40228cec0ab9SSam Leffler 			struct ath_desc *ds = bf->bf_desc;
4023c42a7b7eSSam Leffler 			HAL_STATUS status = ath_hal_rxprocdesc(ah, ds,
4024c42a7b7eSSam Leffler 				bf->bf_daddr, PA2DESC(sc, ds->ds_link));
4025c42a7b7eSSam Leffler 			if (status == HAL_OK || (sc->sc_debug & ATH_DEBUG_FATAL))
4026c42a7b7eSSam Leffler 				ath_printrxbuf(bf, status == HAL_OK);
40275591b213SSam Leffler 		}
40285591b213SSam Leffler 	}
40295591b213SSam Leffler #endif
40305591b213SSam Leffler 	sc->sc_rxlink = NULL;		/* just in case */
40318cec0ab9SSam Leffler #undef PA2DESC
40325591b213SSam Leffler }
40335591b213SSam Leffler 
40345591b213SSam Leffler /*
40355591b213SSam Leffler  * Enable the receive h/w following a reset.
40365591b213SSam Leffler  */
40375591b213SSam Leffler static int
40385591b213SSam Leffler ath_startrecv(struct ath_softc *sc)
40395591b213SSam Leffler {
40405591b213SSam Leffler 	struct ath_hal *ah = sc->sc_ah;
40415591b213SSam Leffler 	struct ath_buf *bf;
40425591b213SSam Leffler 
40435591b213SSam Leffler 	sc->sc_rxlink = NULL;
4044c42a7b7eSSam Leffler 	STAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) {
40455591b213SSam Leffler 		int error = ath_rxbuf_init(sc, bf);
40465591b213SSam Leffler 		if (error != 0) {
4047c42a7b7eSSam Leffler 			DPRINTF(sc, ATH_DEBUG_RECV,
4048c42a7b7eSSam Leffler 				"%s: ath_rxbuf_init failed %d\n",
4049c42a7b7eSSam Leffler 				__func__, error);
40505591b213SSam Leffler 			return error;
40515591b213SSam Leffler 		}
40525591b213SSam Leffler 	}
40535591b213SSam Leffler 
4054c42a7b7eSSam Leffler 	bf = STAILQ_FIRST(&sc->sc_rxbuf);
40555591b213SSam Leffler 	ath_hal_putrxbuf(ah, bf->bf_daddr);
40565591b213SSam Leffler 	ath_hal_rxena(ah);		/* enable recv descriptors */
40575591b213SSam Leffler 	ath_mode_init(sc);		/* set filters, etc. */
40585591b213SSam Leffler 	ath_hal_startpcurecv(ah);	/* re-enable PCU/DMA engine */
40595591b213SSam Leffler 	return 0;
40605591b213SSam Leffler }
40615591b213SSam Leffler 
40625591b213SSam Leffler /*
4063c42a7b7eSSam Leffler  * Update internal state after a channel change.
4064c42a7b7eSSam Leffler  */
4065c42a7b7eSSam Leffler static void
4066c42a7b7eSSam Leffler ath_chan_change(struct ath_softc *sc, struct ieee80211_channel *chan)
4067c42a7b7eSSam Leffler {
4068c42a7b7eSSam Leffler 	struct ieee80211com *ic = &sc->sc_ic;
4069c42a7b7eSSam Leffler 	enum ieee80211_phymode mode;
407016b4851aSSam Leffler 	u_int16_t flags;
4071c42a7b7eSSam Leffler 
4072c42a7b7eSSam Leffler 	/*
4073c42a7b7eSSam Leffler 	 * Change channels and update the h/w rate map
4074c42a7b7eSSam Leffler 	 * if we're switching; e.g. 11a to 11b/g.
4075c42a7b7eSSam Leffler 	 */
4076c42a7b7eSSam Leffler 	mode = ieee80211_chan2mode(ic, chan);
4077c42a7b7eSSam Leffler 	if (mode != sc->sc_curmode)
4078c42a7b7eSSam Leffler 		ath_setcurmode(sc, mode);
4079c42a7b7eSSam Leffler 	/*
408016b4851aSSam Leffler 	 * Update BPF state.  NB: ethereal et. al. don't handle
408116b4851aSSam Leffler 	 * merged flags well so pick a unique mode for their use.
4082c42a7b7eSSam Leffler 	 */
408316b4851aSSam Leffler 	if (IEEE80211_IS_CHAN_A(chan))
408416b4851aSSam Leffler 		flags = IEEE80211_CHAN_A;
408516b4851aSSam Leffler 	/* XXX 11g schizophrenia */
408616b4851aSSam Leffler 	else if (IEEE80211_IS_CHAN_G(chan) ||
408716b4851aSSam Leffler 	    IEEE80211_IS_CHAN_PUREG(chan))
408816b4851aSSam Leffler 		flags = IEEE80211_CHAN_G;
408916b4851aSSam Leffler 	else
409016b4851aSSam Leffler 		flags = IEEE80211_CHAN_B;
409116b4851aSSam Leffler 	if (IEEE80211_IS_CHAN_T(chan))
409216b4851aSSam Leffler 		flags |= IEEE80211_CHAN_TURBO;
4093c42a7b7eSSam Leffler 	sc->sc_tx_th.wt_chan_freq = sc->sc_rx_th.wr_chan_freq =
4094c42a7b7eSSam Leffler 		htole16(chan->ic_freq);
4095c42a7b7eSSam Leffler 	sc->sc_tx_th.wt_chan_flags = sc->sc_rx_th.wr_chan_flags =
409616b4851aSSam Leffler 		htole16(flags);
4097c42a7b7eSSam Leffler }
4098c42a7b7eSSam Leffler 
4099c42a7b7eSSam Leffler /*
41005591b213SSam Leffler  * Set/change channels.  If the channel is really being changed,
4101c42a7b7eSSam Leffler  * it's done by reseting the chip.  To accomplish this we must
41025591b213SSam Leffler  * first cleanup any pending DMA, then restart stuff after a la
41035591b213SSam Leffler  * ath_init.
41045591b213SSam Leffler  */
41055591b213SSam Leffler static int
41065591b213SSam Leffler ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan)
41075591b213SSam Leffler {
41085591b213SSam Leffler 	struct ath_hal *ah = sc->sc_ah;
41095591b213SSam Leffler 	struct ieee80211com *ic = &sc->sc_ic;
41105591b213SSam Leffler 	HAL_CHANNEL hchan;
4111c42a7b7eSSam Leffler 
4112c42a7b7eSSam Leffler 	/*
4113c42a7b7eSSam Leffler 	 * Convert to a HAL channel description with
4114c42a7b7eSSam Leffler 	 * the flags constrained to reflect the current
4115c42a7b7eSSam Leffler 	 * operating mode.
4116c42a7b7eSSam Leffler 	 */
4117c42a7b7eSSam Leffler 	hchan.channel = chan->ic_freq;
4118c42a7b7eSSam Leffler 	hchan.channelFlags = ath_chan2flags(ic, chan);
4119c42a7b7eSSam Leffler 
4120370572d9SSam Leffler 	DPRINTF(sc, ATH_DEBUG_RESET,
4121370572d9SSam Leffler 	    "%s: %u (%u MHz, hal flags 0x%x) -> %u (%u MHz, hal flags 0x%x)\n",
4122c42a7b7eSSam Leffler 	    __func__,
4123c42a7b7eSSam Leffler 	    ath_hal_mhz2ieee(sc->sc_curchan.channel,
4124c42a7b7eSSam Leffler 		sc->sc_curchan.channelFlags),
4125370572d9SSam Leffler 	    	sc->sc_curchan.channel, sc->sc_curchan.channelFlags,
4126370572d9SSam Leffler 	    ath_hal_mhz2ieee(hchan.channel, hchan.channelFlags),
4127370572d9SSam Leffler 	        hchan.channel, hchan.channelFlags);
4128c42a7b7eSSam Leffler 	if (hchan.channel != sc->sc_curchan.channel ||
4129c42a7b7eSSam Leffler 	    hchan.channelFlags != sc->sc_curchan.channelFlags) {
4130c42a7b7eSSam Leffler 		HAL_STATUS status;
41315591b213SSam Leffler 
41325591b213SSam Leffler 		/*
41335591b213SSam Leffler 		 * To switch channels clear any pending DMA operations;
41345591b213SSam Leffler 		 * wait long enough for the RX fifo to drain, reset the
41355591b213SSam Leffler 		 * hardware at the new frequency, and then re-enable
41365591b213SSam Leffler 		 * the relevant bits of the h/w.
41375591b213SSam Leffler 		 */
41385591b213SSam Leffler 		ath_hal_intrset(ah, 0);		/* disable interrupts */
41395591b213SSam Leffler 		ath_draintxq(sc);		/* clear pending tx frames */
41405591b213SSam Leffler 		ath_stoprecv(sc);		/* turn off frame recv */
41417a04dc27SSam Leffler 		if (!ath_hal_reset(ah, sc->sc_opmode, &hchan, AH_TRUE, &status)) {
4142370572d9SSam Leffler 			if_printf(ic->ic_ifp, "%s: unable to reset "
4143370572d9SSam Leffler 			    "channel %u (%u Mhz, flags 0x%x hal flags 0x%x)\n",
4144370572d9SSam Leffler 			    __func__, ieee80211_chan2ieee(ic, chan),
4145370572d9SSam Leffler 			    chan->ic_freq, chan->ic_flags, hchan.channelFlags);
41465591b213SSam Leffler 			return EIO;
41475591b213SSam Leffler 		}
4148c42a7b7eSSam Leffler 		sc->sc_curchan = hchan;
4149c42a7b7eSSam Leffler 		ath_update_txpow(sc);		/* update tx power state */
4150c59005e9SSam Leffler 		sc->sc_diversity = ath_hal_getdiversity(ah);
4151c42a7b7eSSam Leffler 
41525591b213SSam Leffler 		/*
41535591b213SSam Leffler 		 * Re-enable rx framework.
41545591b213SSam Leffler 		 */
41555591b213SSam Leffler 		if (ath_startrecv(sc) != 0) {
4156c42a7b7eSSam Leffler 			if_printf(ic->ic_ifp,
4157370572d9SSam Leffler 				"%s: unable to restart recv logic\n", __func__);
41585591b213SSam Leffler 			return EIO;
41595591b213SSam Leffler 		}
41605591b213SSam Leffler 
41615591b213SSam Leffler 		/*
41625591b213SSam Leffler 		 * Change channels and update the h/w rate map
41635591b213SSam Leffler 		 * if we're switching; e.g. 11a to 11b/g.
41645591b213SSam Leffler 		 */
41655591b213SSam Leffler 		ic->ic_ibss_chan = chan;
4166c42a7b7eSSam Leffler 		ath_chan_change(sc, chan);
41670a915fadSSam Leffler 
41680a915fadSSam Leffler 		/*
41690a915fadSSam Leffler 		 * Re-enable interrupts.
41700a915fadSSam Leffler 		 */
41710a915fadSSam Leffler 		ath_hal_intrset(ah, sc->sc_imask);
41725591b213SSam Leffler 	}
41735591b213SSam Leffler 	return 0;
41745591b213SSam Leffler }
41755591b213SSam Leffler 
41765591b213SSam Leffler static void
41775591b213SSam Leffler ath_next_scan(void *arg)
41785591b213SSam Leffler {
41795591b213SSam Leffler 	struct ath_softc *sc = arg;
41805591b213SSam Leffler 	struct ieee80211com *ic = &sc->sc_ic;
41815591b213SSam Leffler 
41825591b213SSam Leffler 	if (ic->ic_state == IEEE80211_S_SCAN)
4183c42a7b7eSSam Leffler 		ieee80211_next_scan(ic);
41845591b213SSam Leffler }
41855591b213SSam Leffler 
41865591b213SSam Leffler /*
41875591b213SSam Leffler  * Periodically recalibrate the PHY to account
41885591b213SSam Leffler  * for temperature/environment changes.
41895591b213SSam Leffler  */
41905591b213SSam Leffler static void
41915591b213SSam Leffler ath_calibrate(void *arg)
41925591b213SSam Leffler {
41935591b213SSam Leffler 	struct ath_softc *sc = arg;
41945591b213SSam Leffler 	struct ath_hal *ah = sc->sc_ah;
41955591b213SSam Leffler 
41965591b213SSam Leffler 	sc->sc_stats.ast_per_cal++;
41975591b213SSam Leffler 
41985591b213SSam Leffler 	if (ath_hal_getrfgain(ah) == HAL_RFGAIN_NEED_CHANGE) {
41995591b213SSam Leffler 		/*
42005591b213SSam Leffler 		 * Rfgain is out of bounds, reset the chip
42015591b213SSam Leffler 		 * to load new gain values.
42025591b213SSam Leffler 		 */
4203370572d9SSam Leffler 		DPRINTF(sc, ATH_DEBUG_CALIBRATE,
4204370572d9SSam Leffler 			"%s: rfgain change\n", __func__);
42055591b213SSam Leffler 		sc->sc_stats.ast_per_rfgain++;
4206fc74a9f9SBrooks Davis 		ath_reset(sc->sc_ifp);
42075591b213SSam Leffler 	}
4208c42a7b7eSSam Leffler 	if (!ath_hal_calibrate(ah, &sc->sc_curchan)) {
4209c42a7b7eSSam Leffler 		DPRINTF(sc, ATH_DEBUG_ANY,
4210c42a7b7eSSam Leffler 			"%s: calibration of channel %u failed\n",
4211c42a7b7eSSam Leffler 			__func__, sc->sc_curchan.channel);
42125591b213SSam Leffler 		sc->sc_stats.ast_per_calfail++;
42135591b213SSam Leffler 	}
42147b0c77ecSSam Leffler 	/*
42157b0c77ecSSam Leffler 	 * Calibrate noise floor data again in case of change.
42167b0c77ecSSam Leffler 	 */
42177b0c77ecSSam Leffler 	ath_hal_process_noisefloor(ah);
4218c42a7b7eSSam Leffler 	callout_reset(&sc->sc_cal_ch, ath_calinterval * hz, ath_calibrate, sc);
42195591b213SSam Leffler }
42205591b213SSam Leffler 
42215591b213SSam Leffler static int
422245bbf62fSSam Leffler ath_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
42235591b213SSam Leffler {
4224c42a7b7eSSam Leffler 	struct ifnet *ifp = ic->ic_ifp;
422545bbf62fSSam Leffler 	struct ath_softc *sc = ifp->if_softc;
422645bbf62fSSam Leffler 	struct ath_hal *ah = sc->sc_ah;
42275591b213SSam Leffler 	struct ieee80211_node *ni;
42285591b213SSam Leffler 	int i, error;
42298cec0ab9SSam Leffler 	const u_int8_t *bssid;
42305591b213SSam Leffler 	u_int32_t rfilt;
42315591b213SSam Leffler 	static const HAL_LED_STATE leds[] = {
42325591b213SSam Leffler 	    HAL_LED_INIT,	/* IEEE80211_S_INIT */
42335591b213SSam Leffler 	    HAL_LED_SCAN,	/* IEEE80211_S_SCAN */
42345591b213SSam Leffler 	    HAL_LED_AUTH,	/* IEEE80211_S_AUTH */
42355591b213SSam Leffler 	    HAL_LED_ASSOC, 	/* IEEE80211_S_ASSOC */
42365591b213SSam Leffler 	    HAL_LED_RUN, 	/* IEEE80211_S_RUN */
42375591b213SSam Leffler 	};
42385591b213SSam Leffler 
4239c42a7b7eSSam Leffler 	DPRINTF(sc, ATH_DEBUG_STATE, "%s: %s -> %s\n", __func__,
424045bbf62fSSam Leffler 		ieee80211_state_name[ic->ic_state],
4241c42a7b7eSSam Leffler 		ieee80211_state_name[nstate]);
42425591b213SSam Leffler 
4243c42a7b7eSSam Leffler 	callout_stop(&sc->sc_scan_ch);
4244c42a7b7eSSam Leffler 	callout_stop(&sc->sc_cal_ch);
42455591b213SSam Leffler 	ath_hal_setledstate(ah, leds[nstate]);	/* set LED */
42465591b213SSam Leffler 
42475591b213SSam Leffler 	if (nstate == IEEE80211_S_INIT) {
42485591b213SSam Leffler 		sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS);
42494c24deacSSam Leffler 		/*
42504c24deacSSam Leffler 		 * NB: disable interrupts so we don't rx frames.
42514c24deacSSam Leffler 		 */
4252e8fd88a3SSam Leffler 		ath_hal_intrset(ah, sc->sc_imask &~ HAL_INT_GLOBAL);
4253c42a7b7eSSam Leffler 		/*
4254c42a7b7eSSam Leffler 		 * Notify the rate control algorithm.
4255c42a7b7eSSam Leffler 		 */
4256c42a7b7eSSam Leffler 		ath_rate_newstate(sc, nstate);
4257c42a7b7eSSam Leffler 		goto done;
42585591b213SSam Leffler 	}
42595591b213SSam Leffler 	ni = ic->ic_bss;
4260b5c99415SSam Leffler 	error = ath_chan_set(sc, ic->ic_curchan);
42615591b213SSam Leffler 	if (error != 0)
42625591b213SSam Leffler 		goto bad;
4263c42a7b7eSSam Leffler 	rfilt = ath_calcrxfilter(sc, nstate);
4264c42a7b7eSSam Leffler 	if (nstate == IEEE80211_S_SCAN)
42655591b213SSam Leffler 		bssid = ifp->if_broadcastaddr;
4266c42a7b7eSSam Leffler 	else
42675591b213SSam Leffler 		bssid = ni->ni_bssid;
42685591b213SSam Leffler 	ath_hal_setrxfilter(ah, rfilt);
4269c42a7b7eSSam Leffler 	DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s\n",
4270c42a7b7eSSam Leffler 		 __func__, rfilt, ether_sprintf(bssid));
42715591b213SSam Leffler 
42725591b213SSam Leffler 	if (nstate == IEEE80211_S_RUN && ic->ic_opmode == IEEE80211_M_STA)
42735591b213SSam Leffler 		ath_hal_setassocid(ah, bssid, ni->ni_associd);
42745591b213SSam Leffler 	else
42755591b213SSam Leffler 		ath_hal_setassocid(ah, bssid, 0);
4276c42a7b7eSSam Leffler 	if (ic->ic_flags & IEEE80211_F_PRIVACY) {
42775591b213SSam Leffler 		for (i = 0; i < IEEE80211_WEP_NKID; i++)
42785591b213SSam Leffler 			if (ath_hal_keyisvalid(ah, i))
42795591b213SSam Leffler 				ath_hal_keysetmac(ah, i, bssid);
42805591b213SSam Leffler 	}
42815591b213SSam Leffler 
4282c42a7b7eSSam Leffler 	/*
4283c42a7b7eSSam Leffler 	 * Notify the rate control algorithm so rates
4284c42a7b7eSSam Leffler 	 * are setup should ath_beacon_alloc be called.
4285c42a7b7eSSam Leffler 	 */
4286c42a7b7eSSam Leffler 	ath_rate_newstate(sc, nstate);
4287c42a7b7eSSam Leffler 
4288c42a7b7eSSam Leffler 	if (ic->ic_opmode == IEEE80211_M_MONITOR) {
4289c42a7b7eSSam Leffler 		/* nothing to do */;
4290c42a7b7eSSam Leffler 	} else if (nstate == IEEE80211_S_RUN) {
4291c42a7b7eSSam Leffler 		DPRINTF(sc, ATH_DEBUG_STATE,
4292c42a7b7eSSam Leffler 			"%s(RUN): ic_flags=0x%08x iv=%d bssid=%s "
42935591b213SSam Leffler 			"capinfo=0x%04x chan=%d\n"
42945591b213SSam Leffler 			 , __func__
42955591b213SSam Leffler 			 , ic->ic_flags
42965591b213SSam Leffler 			 , ni->ni_intval
42975591b213SSam Leffler 			 , ether_sprintf(ni->ni_bssid)
42985591b213SSam Leffler 			 , ni->ni_capinfo
4299b5c99415SSam Leffler 			 , ieee80211_chan2ieee(ic, ic->ic_curchan));
43005591b213SSam Leffler 
4301e8fd88a3SSam Leffler 		switch (ic->ic_opmode) {
4302e8fd88a3SSam Leffler 		case IEEE80211_M_HOSTAP:
4303e8fd88a3SSam Leffler 		case IEEE80211_M_IBSS:
43045591b213SSam Leffler 			/*
4305e8fd88a3SSam Leffler 			 * Allocate and setup the beacon frame.
4306e8fd88a3SSam Leffler 			 *
4307f818612bSSam Leffler 			 * Stop any previous beacon DMA.  This may be
4308f818612bSSam Leffler 			 * necessary, for example, when an ibss merge
4309f818612bSSam Leffler 			 * causes reconfiguration; there will be a state
4310f818612bSSam Leffler 			 * transition from RUN->RUN that means we may
4311f818612bSSam Leffler 			 * be called with beacon transmission active.
4312f818612bSSam Leffler 			 */
4313f818612bSSam Leffler 			ath_hal_stoptxdma(ah, sc->sc_bhalq);
4314f818612bSSam Leffler 			ath_beacon_free(sc);
43155591b213SSam Leffler 			error = ath_beacon_alloc(sc, ni);
43165591b213SSam Leffler 			if (error != 0)
43175591b213SSam Leffler 				goto bad;
43187a04dc27SSam Leffler 			/*
431980d939bfSSam Leffler 			 * If joining an adhoc network defer beacon timer
432080d939bfSSam Leffler 			 * configuration to the next beacon frame so we
432180d939bfSSam Leffler 			 * have a current TSF to use.  Otherwise we're
432280d939bfSSam Leffler 			 * starting an ibss/bss so there's no need to delay.
43237a04dc27SSam Leffler 			 */
432480d939bfSSam Leffler 			if (ic->ic_opmode == IEEE80211_M_IBSS &&
432580d939bfSSam Leffler 			    ic->ic_bss->ni_tstamp.tsf != 0)
432680d939bfSSam Leffler 				sc->sc_syncbeacon = 1;
432780d939bfSSam Leffler 			else
43287a04dc27SSam Leffler 				ath_beacon_config(sc);
4329e8fd88a3SSam Leffler 			break;
4330e8fd88a3SSam Leffler 		case IEEE80211_M_STA:
4331e8fd88a3SSam Leffler 			/*
4332e8fd88a3SSam Leffler 			 * Allocate a key cache slot to the station.
4333e8fd88a3SSam Leffler 			 */
4334e8fd88a3SSam Leffler 			if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0 &&
4335e8fd88a3SSam Leffler 			    sc->sc_hasclrkey &&
4336e8fd88a3SSam Leffler 			    ni->ni_ucastkey.wk_keyix == IEEE80211_KEYIX_NONE)
4337e8fd88a3SSam Leffler 				ath_setup_stationkey(ni);
43387a04dc27SSam Leffler 			/*
433980d939bfSSam Leffler 			 * Defer beacon timer configuration to the next
434080d939bfSSam Leffler 			 * beacon frame so we have a current TSF to use
434180d939bfSSam Leffler 			 * (any TSF collected when scanning is likely old).
43427a04dc27SSam Leffler 			 */
434380d939bfSSam Leffler 			sc->sc_syncbeacon = 1;
4344e8fd88a3SSam Leffler 			break;
4345e8fd88a3SSam Leffler 		default:
4346e8fd88a3SSam Leffler 			break;
43475591b213SSam Leffler 		}
43485591b213SSam Leffler 
43495591b213SSam Leffler 		/*
43507b0c77ecSSam Leffler 		 * Let the hal process statistics collected during a
43517b0c77ecSSam Leffler 		 * scan so it can provide calibrated noise floor data.
43527b0c77ecSSam Leffler 		 */
43537b0c77ecSSam Leffler 		ath_hal_process_noisefloor(ah);
43547b0c77ecSSam Leffler 		/*
4355ffa2cab6SSam Leffler 		 * Reset rssi stats; maybe not the best place...
4356ffa2cab6SSam Leffler 		 */
4357ffa2cab6SSam Leffler 		sc->sc_halstats.ns_avgbrssi = ATH_RSSI_DUMMY_MARKER;
4358ffa2cab6SSam Leffler 		sc->sc_halstats.ns_avgrssi = ATH_RSSI_DUMMY_MARKER;
4359ffa2cab6SSam Leffler 		sc->sc_halstats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER;
43605591b213SSam Leffler 	} else {
4361c42a7b7eSSam Leffler 		ath_hal_intrset(ah,
4362c42a7b7eSSam Leffler 			sc->sc_imask &~ (HAL_INT_SWBA | HAL_INT_BMISS));
43635591b213SSam Leffler 		sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS);
43645591b213SSam Leffler 	}
4365c42a7b7eSSam Leffler done:
436645bbf62fSSam Leffler 	/*
436745bbf62fSSam Leffler 	 * Invoke the parent method to complete the work.
436845bbf62fSSam Leffler 	 */
4369c42a7b7eSSam Leffler 	error = sc->sc_newstate(ic, nstate, arg);
4370c42a7b7eSSam Leffler 	/*
4371c42a7b7eSSam Leffler 	 * Finally, start any timers.
4372c42a7b7eSSam Leffler 	 */
4373c42a7b7eSSam Leffler 	if (nstate == IEEE80211_S_RUN) {
4374c42a7b7eSSam Leffler 		/* start periodic recalibration timer */
4375c42a7b7eSSam Leffler 		callout_reset(&sc->sc_cal_ch, ath_calinterval * hz,
4376c42a7b7eSSam Leffler 			ath_calibrate, sc);
4377c42a7b7eSSam Leffler 	} else if (nstate == IEEE80211_S_SCAN) {
4378c42a7b7eSSam Leffler 		/* start ap/neighbor scan timer */
4379c42a7b7eSSam Leffler 		callout_reset(&sc->sc_scan_ch, (ath_dwelltime * hz) / 1000,
4380c42a7b7eSSam Leffler 			ath_next_scan, sc);
4381c42a7b7eSSam Leffler 	}
43825591b213SSam Leffler bad:
43835591b213SSam Leffler 	return error;
43845591b213SSam Leffler }
43855591b213SSam Leffler 
43865591b213SSam Leffler /*
4387e8fd88a3SSam Leffler  * Allocate a key cache slot to the station so we can
4388e8fd88a3SSam Leffler  * setup a mapping from key index to node. The key cache
4389e8fd88a3SSam Leffler  * slot is needed for managing antenna state and for
4390e8fd88a3SSam Leffler  * compression when stations do not use crypto.  We do
4391e8fd88a3SSam Leffler  * it uniliaterally here; if crypto is employed this slot
4392e8fd88a3SSam Leffler  * will be reassigned.
4393e8fd88a3SSam Leffler  */
4394e8fd88a3SSam Leffler static void
4395e8fd88a3SSam Leffler ath_setup_stationkey(struct ieee80211_node *ni)
4396e8fd88a3SSam Leffler {
4397e8fd88a3SSam Leffler 	struct ieee80211com *ic = ni->ni_ic;
4398e8fd88a3SSam Leffler 	struct ath_softc *sc = ic->ic_ifp->if_softc;
4399c1225b52SSam Leffler 	ieee80211_keyix keyix, rxkeyix;
4400e8fd88a3SSam Leffler 
4401c1225b52SSam Leffler 	if (!ath_key_alloc(ic, &ni->ni_ucastkey, &keyix, &rxkeyix)) {
4402e8fd88a3SSam Leffler 		/*
4403e8fd88a3SSam Leffler 		 * Key cache is full; we'll fall back to doing
4404e8fd88a3SSam Leffler 		 * the more expensive lookup in software.  Note
4405e8fd88a3SSam Leffler 		 * this also means no h/w compression.
4406e8fd88a3SSam Leffler 		 */
4407e8fd88a3SSam Leffler 		/* XXX msg+statistic */
4408e8fd88a3SSam Leffler 	} else {
4409c1225b52SSam Leffler 		/* XXX locking? */
4410e8fd88a3SSam Leffler 		ni->ni_ucastkey.wk_keyix = keyix;
4411c1225b52SSam Leffler 		ni->ni_ucastkey.wk_rxkeyix = rxkeyix;
4412e8fd88a3SSam Leffler 		/* NB: this will create a pass-thru key entry */
4413e8fd88a3SSam Leffler 		ath_keyset(sc, &ni->ni_ucastkey, ni->ni_macaddr, ic->ic_bss);
4414e8fd88a3SSam Leffler 	}
4415e8fd88a3SSam Leffler }
4416e8fd88a3SSam Leffler 
4417e8fd88a3SSam Leffler /*
44185591b213SSam Leffler  * Setup driver-specific state for a newly associated node.
44195591b213SSam Leffler  * Note that we're called also on a re-associate, the isnew
44205591b213SSam Leffler  * param tells us if this is the first time or not.
44215591b213SSam Leffler  */
44225591b213SSam Leffler static void
4423e9962332SSam Leffler ath_newassoc(struct ieee80211_node *ni, int isnew)
44245591b213SSam Leffler {
4425e9962332SSam Leffler 	struct ieee80211com *ic = ni->ni_ic;
4426c42a7b7eSSam Leffler 	struct ath_softc *sc = ic->ic_ifp->if_softc;
44275591b213SSam Leffler 
4428c42a7b7eSSam Leffler 	ath_rate_newassoc(sc, ATH_NODE(ni), isnew);
4429e8fd88a3SSam Leffler 	if (isnew &&
4430e8fd88a3SSam Leffler 	    (ic->ic_flags & IEEE80211_F_PRIVACY) == 0 && sc->sc_hasclrkey) {
4431e8fd88a3SSam Leffler 		KASSERT(ni->ni_ucastkey.wk_keyix == IEEE80211_KEYIX_NONE,
4432e8fd88a3SSam Leffler 		    ("new assoc with a unicast key already setup (keyix %u)",
4433e8fd88a3SSam Leffler 		    ni->ni_ucastkey.wk_keyix));
4434e8fd88a3SSam Leffler 		ath_setup_stationkey(ni);
4435e8fd88a3SSam Leffler 	}
44365591b213SSam Leffler }
44375591b213SSam Leffler 
44385591b213SSam Leffler static int
4439c42a7b7eSSam Leffler ath_getchannels(struct ath_softc *sc, u_int cc,
4440c42a7b7eSSam Leffler 	HAL_BOOL outdoor, HAL_BOOL xchanmode)
44415591b213SSam Leffler {
44425591b213SSam Leffler 	struct ieee80211com *ic = &sc->sc_ic;
4443fc74a9f9SBrooks Davis 	struct ifnet *ifp = sc->sc_ifp;
44445591b213SSam Leffler 	struct ath_hal *ah = sc->sc_ah;
44455591b213SSam Leffler 	HAL_CHANNEL *chans;
44465591b213SSam Leffler 	int i, ix, nchan;
44475591b213SSam Leffler 
44485591b213SSam Leffler 	chans = malloc(IEEE80211_CHAN_MAX * sizeof(HAL_CHANNEL),
44495591b213SSam Leffler 			M_TEMP, M_NOWAIT);
44505591b213SSam Leffler 	if (chans == NULL) {
44515591b213SSam Leffler 		if_printf(ifp, "unable to allocate channel table\n");
44525591b213SSam Leffler 		return ENOMEM;
44535591b213SSam Leffler 	}
44545591b213SSam Leffler 	if (!ath_hal_init_channels(ah, chans, IEEE80211_CHAN_MAX, &nchan,
4455c42a7b7eSSam Leffler 	    cc, HAL_MODE_ALL, outdoor, xchanmode)) {
4456c42a7b7eSSam Leffler 		u_int32_t rd;
4457c42a7b7eSSam Leffler 
4458c42a7b7eSSam Leffler 		ath_hal_getregdomain(ah, &rd);
4459c42a7b7eSSam Leffler 		if_printf(ifp, "unable to collect channel list from hal; "
4460c42a7b7eSSam Leffler 			"regdomain likely %u country code %u\n", rd, cc);
44615591b213SSam Leffler 		free(chans, M_TEMP);
44625591b213SSam Leffler 		return EINVAL;
44635591b213SSam Leffler 	}
44645591b213SSam Leffler 
44655591b213SSam Leffler 	/*
44665591b213SSam Leffler 	 * Convert HAL channels to ieee80211 ones and insert
44675591b213SSam Leffler 	 * them in the table according to their channel number.
44685591b213SSam Leffler 	 */
44695591b213SSam Leffler 	for (i = 0; i < nchan; i++) {
44705591b213SSam Leffler 		HAL_CHANNEL *c = &chans[i];
44715591b213SSam Leffler 		ix = ath_hal_mhz2ieee(c->channel, c->channelFlags);
44725591b213SSam Leffler 		if (ix > IEEE80211_CHAN_MAX) {
44735591b213SSam Leffler 			if_printf(ifp, "bad hal channel %u (%u/%x) ignored\n",
44745591b213SSam Leffler 				ix, c->channel, c->channelFlags);
44755591b213SSam Leffler 			continue;
44765591b213SSam Leffler 		}
44775591b213SSam Leffler 		/* NB: flags are known to be compatible */
44785591b213SSam Leffler 		if (ic->ic_channels[ix].ic_freq == 0) {
44795591b213SSam Leffler 			ic->ic_channels[ix].ic_freq = c->channel;
44805591b213SSam Leffler 			ic->ic_channels[ix].ic_flags = c->channelFlags;
44815591b213SSam Leffler 		} else {
44825591b213SSam Leffler 			/* channels overlap; e.g. 11g and 11b */
44835591b213SSam Leffler 			ic->ic_channels[ix].ic_flags |= c->channelFlags;
44845591b213SSam Leffler 		}
44855591b213SSam Leffler 	}
44865591b213SSam Leffler 	free(chans, M_TEMP);
44875591b213SSam Leffler 	return 0;
44885591b213SSam Leffler }
44895591b213SSam Leffler 
4490c42a7b7eSSam Leffler static void
44913e50ec2cSSam Leffler ath_led_done(void *arg)
4492c42a7b7eSSam Leffler {
44933e50ec2cSSam Leffler 	struct ath_softc *sc = arg;
44943e50ec2cSSam Leffler 
44953e50ec2cSSam Leffler 	sc->sc_blinking = 0;
44963e50ec2cSSam Leffler }
4497c42a7b7eSSam Leffler 
4498c42a7b7eSSam Leffler /*
44993e50ec2cSSam Leffler  * Turn the LED off: flip the pin and then set a timer so no
45003e50ec2cSSam Leffler  * update will happen for the specified duration.
4501c42a7b7eSSam Leffler  */
45023e50ec2cSSam Leffler static void
45033e50ec2cSSam Leffler ath_led_off(void *arg)
45043e50ec2cSSam Leffler {
45053e50ec2cSSam Leffler 	struct ath_softc *sc = arg;
45063e50ec2cSSam Leffler 
45073e50ec2cSSam Leffler 	ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, !sc->sc_ledon);
45083e50ec2cSSam Leffler 	callout_reset(&sc->sc_ledtimer, sc->sc_ledoff, ath_led_done, sc);
4509c42a7b7eSSam Leffler }
45103e50ec2cSSam Leffler 
45113e50ec2cSSam Leffler /*
45123e50ec2cSSam Leffler  * Blink the LED according to the specified on/off times.
45133e50ec2cSSam Leffler  */
45143e50ec2cSSam Leffler static void
45153e50ec2cSSam Leffler ath_led_blink(struct ath_softc *sc, int on, int off)
45163e50ec2cSSam Leffler {
45173e50ec2cSSam Leffler 	DPRINTF(sc, ATH_DEBUG_LED, "%s: on %u off %u\n", __func__, on, off);
45183e50ec2cSSam Leffler 	ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, sc->sc_ledon);
45193e50ec2cSSam Leffler 	sc->sc_blinking = 1;
45203e50ec2cSSam Leffler 	sc->sc_ledoff = off;
45213e50ec2cSSam Leffler 	callout_reset(&sc->sc_ledtimer, on, ath_led_off, sc);
45223e50ec2cSSam Leffler }
45233e50ec2cSSam Leffler 
45243e50ec2cSSam Leffler static void
45253e50ec2cSSam Leffler ath_led_event(struct ath_softc *sc, int event)
45263e50ec2cSSam Leffler {
45273e50ec2cSSam Leffler 
45283e50ec2cSSam Leffler 	sc->sc_ledevent = ticks;	/* time of last event */
45293e50ec2cSSam Leffler 	if (sc->sc_blinking)		/* don't interrupt active blink */
45303e50ec2cSSam Leffler 		return;
45313e50ec2cSSam Leffler 	switch (event) {
45323e50ec2cSSam Leffler 	case ATH_LED_POLL:
45333e50ec2cSSam Leffler 		ath_led_blink(sc, sc->sc_hwmap[0].ledon,
45343e50ec2cSSam Leffler 			sc->sc_hwmap[0].ledoff);
45353e50ec2cSSam Leffler 		break;
45363e50ec2cSSam Leffler 	case ATH_LED_TX:
45373e50ec2cSSam Leffler 		ath_led_blink(sc, sc->sc_hwmap[sc->sc_txrate].ledon,
45383e50ec2cSSam Leffler 			sc->sc_hwmap[sc->sc_txrate].ledoff);
45393e50ec2cSSam Leffler 		break;
45403e50ec2cSSam Leffler 	case ATH_LED_RX:
45413e50ec2cSSam Leffler 		ath_led_blink(sc, sc->sc_hwmap[sc->sc_rxrate].ledon,
45423e50ec2cSSam Leffler 			sc->sc_hwmap[sc->sc_rxrate].ledoff);
45433e50ec2cSSam Leffler 		break;
4544c42a7b7eSSam Leffler 	}
4545c42a7b7eSSam Leffler }
4546c42a7b7eSSam Leffler 
4547c42a7b7eSSam Leffler static void
4548c42a7b7eSSam Leffler ath_update_txpow(struct ath_softc *sc)
4549c42a7b7eSSam Leffler {
4550c42a7b7eSSam Leffler 	struct ieee80211com *ic = &sc->sc_ic;
4551c42a7b7eSSam Leffler 	struct ath_hal *ah = sc->sc_ah;
4552c42a7b7eSSam Leffler 	u_int32_t txpow;
4553c42a7b7eSSam Leffler 
4554c42a7b7eSSam Leffler 	if (sc->sc_curtxpow != ic->ic_txpowlimit) {
4555c42a7b7eSSam Leffler 		ath_hal_settxpowlimit(ah, ic->ic_txpowlimit);
4556c42a7b7eSSam Leffler 		/* read back in case value is clamped */
4557c42a7b7eSSam Leffler 		ath_hal_gettxpowlimit(ah, &txpow);
4558c42a7b7eSSam Leffler 		ic->ic_txpowlimit = sc->sc_curtxpow = txpow;
4559c42a7b7eSSam Leffler 	}
4560c42a7b7eSSam Leffler 	/*
4561c42a7b7eSSam Leffler 	 * Fetch max tx power level for status requests.
4562c42a7b7eSSam Leffler 	 */
4563c42a7b7eSSam Leffler 	ath_hal_getmaxtxpow(sc->sc_ah, &txpow);
4564c42a7b7eSSam Leffler 	ic->ic_bss->ni_txpower = txpow;
4565c42a7b7eSSam Leffler }
4566c42a7b7eSSam Leffler 
45676c4612b9SSam Leffler static void
45686c4612b9SSam Leffler rate_setup(struct ath_softc *sc,
45696c4612b9SSam Leffler 	const HAL_RATE_TABLE *rt, struct ieee80211_rateset *rs)
45705591b213SSam Leffler {
45715591b213SSam Leffler 	int i, maxrates;
45725591b213SSam Leffler 
45735591b213SSam Leffler 	if (rt->rateCount > IEEE80211_RATE_MAXSIZE) {
4574c42a7b7eSSam Leffler 		DPRINTF(sc, ATH_DEBUG_ANY,
4575c42a7b7eSSam Leffler 			"%s: rate table too small (%u > %u)\n",
4576c42a7b7eSSam Leffler 		       __func__, rt->rateCount, IEEE80211_RATE_MAXSIZE);
45775591b213SSam Leffler 		maxrates = IEEE80211_RATE_MAXSIZE;
45785591b213SSam Leffler 	} else
45795591b213SSam Leffler 		maxrates = rt->rateCount;
45805591b213SSam Leffler 	for (i = 0; i < maxrates; i++)
45815591b213SSam Leffler 		rs->rs_rates[i] = rt->info[i].dot11Rate;
45825591b213SSam Leffler 	rs->rs_nrates = maxrates;
45836c4612b9SSam Leffler }
45846c4612b9SSam Leffler 
45856c4612b9SSam Leffler static int
45866c4612b9SSam Leffler ath_rate_setup(struct ath_softc *sc, u_int mode)
45876c4612b9SSam Leffler {
45886c4612b9SSam Leffler 	struct ath_hal *ah = sc->sc_ah;
45896c4612b9SSam Leffler 	struct ieee80211com *ic = &sc->sc_ic;
45906c4612b9SSam Leffler 	const HAL_RATE_TABLE *rt;
45916c4612b9SSam Leffler 
45926c4612b9SSam Leffler 	switch (mode) {
45936c4612b9SSam Leffler 	case IEEE80211_MODE_11A:
45946c4612b9SSam Leffler 		rt = ath_hal_getratetable(ah, HAL_MODE_11A);
45956c4612b9SSam Leffler 		break;
45966c4612b9SSam Leffler 	case IEEE80211_MODE_11B:
45976c4612b9SSam Leffler 		rt = ath_hal_getratetable(ah, HAL_MODE_11B);
45986c4612b9SSam Leffler 		break;
45996c4612b9SSam Leffler 	case IEEE80211_MODE_11G:
46006c4612b9SSam Leffler 		rt = ath_hal_getratetable(ah, HAL_MODE_11G);
46016c4612b9SSam Leffler 		break;
46026c4612b9SSam Leffler 	case IEEE80211_MODE_TURBO_A:
46036c4612b9SSam Leffler 		/* XXX until static/dynamic turbo is fixed */
46046c4612b9SSam Leffler 		rt = ath_hal_getratetable(ah, HAL_MODE_TURBO);
46056c4612b9SSam Leffler 		break;
46066c4612b9SSam Leffler 	case IEEE80211_MODE_TURBO_G:
46076c4612b9SSam Leffler 		rt = ath_hal_getratetable(ah, HAL_MODE_108G);
46086c4612b9SSam Leffler 		break;
46096c4612b9SSam Leffler 	default:
46106c4612b9SSam Leffler 		DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid mode %u\n",
46116c4612b9SSam Leffler 			__func__, mode);
46126c4612b9SSam Leffler 		return 0;
46136c4612b9SSam Leffler 	}
46146c4612b9SSam Leffler 	sc->sc_rates[mode] = rt;
46156c4612b9SSam Leffler 	if (rt != NULL) {
46166c4612b9SSam Leffler 		rate_setup(sc, rt, &ic->ic_sup_rates[mode]);
46175591b213SSam Leffler 		return 1;
46186c4612b9SSam Leffler 	} else
46196c4612b9SSam Leffler 		return 0;
46205591b213SSam Leffler }
46215591b213SSam Leffler 
46225591b213SSam Leffler static void
46235591b213SSam Leffler ath_setcurmode(struct ath_softc *sc, enum ieee80211_phymode mode)
46245591b213SSam Leffler {
46253e50ec2cSSam Leffler #define	N(a)	(sizeof(a)/sizeof(a[0]))
46263e50ec2cSSam Leffler 	/* NB: on/off times from the Atheros NDIS driver, w/ permission */
46273e50ec2cSSam Leffler 	static const struct {
46283e50ec2cSSam Leffler 		u_int		rate;		/* tx/rx 802.11 rate */
46293e50ec2cSSam Leffler 		u_int16_t	timeOn;		/* LED on time (ms) */
46303e50ec2cSSam Leffler 		u_int16_t	timeOff;	/* LED off time (ms) */
46313e50ec2cSSam Leffler 	} blinkrates[] = {
46323e50ec2cSSam Leffler 		{ 108,  40,  10 },
46333e50ec2cSSam Leffler 		{  96,  44,  11 },
46343e50ec2cSSam Leffler 		{  72,  50,  13 },
46353e50ec2cSSam Leffler 		{  48,  57,  14 },
46363e50ec2cSSam Leffler 		{  36,  67,  16 },
46373e50ec2cSSam Leffler 		{  24,  80,  20 },
46383e50ec2cSSam Leffler 		{  22, 100,  25 },
46393e50ec2cSSam Leffler 		{  18, 133,  34 },
46403e50ec2cSSam Leffler 		{  12, 160,  40 },
46413e50ec2cSSam Leffler 		{  10, 200,  50 },
46423e50ec2cSSam Leffler 		{   6, 240,  58 },
46433e50ec2cSSam Leffler 		{   4, 267,  66 },
46443e50ec2cSSam Leffler 		{   2, 400, 100 },
46453e50ec2cSSam Leffler 		{   0, 500, 130 },
46463e50ec2cSSam Leffler 	};
46475591b213SSam Leffler 	const HAL_RATE_TABLE *rt;
46483e50ec2cSSam Leffler 	int i, j;
46495591b213SSam Leffler 
46505591b213SSam Leffler 	memset(sc->sc_rixmap, 0xff, sizeof(sc->sc_rixmap));
46515591b213SSam Leffler 	rt = sc->sc_rates[mode];
46525591b213SSam Leffler 	KASSERT(rt != NULL, ("no h/w rate set for phy mode %u", mode));
46535591b213SSam Leffler 	for (i = 0; i < rt->rateCount; i++)
46545591b213SSam Leffler 		sc->sc_rixmap[rt->info[i].dot11Rate & IEEE80211_RATE_VAL] = i;
46551b1a8e41SSam Leffler 	memset(sc->sc_hwmap, 0, sizeof(sc->sc_hwmap));
4656c42a7b7eSSam Leffler 	for (i = 0; i < 32; i++) {
4657c42a7b7eSSam Leffler 		u_int8_t ix = rt->rateCodeToIndex[i];
46583e50ec2cSSam Leffler 		if (ix == 0xff) {
46593e50ec2cSSam Leffler 			sc->sc_hwmap[i].ledon = (500 * hz) / 1000;
46603e50ec2cSSam Leffler 			sc->sc_hwmap[i].ledoff = (130 * hz) / 1000;
466116b4851aSSam Leffler 			continue;
46623e50ec2cSSam Leffler 		}
46633e50ec2cSSam Leffler 		sc->sc_hwmap[i].ieeerate =
46643e50ec2cSSam Leffler 			rt->info[ix].dot11Rate & IEEE80211_RATE_VAL;
4665d3be6f5bSSam Leffler 		sc->sc_hwmap[i].txflags = IEEE80211_RADIOTAP_F_DATAPAD;
466616b4851aSSam Leffler 		if (rt->info[ix].shortPreamble ||
466716b4851aSSam Leffler 		    rt->info[ix].phy == IEEE80211_T_OFDM)
4668d3be6f5bSSam Leffler 			sc->sc_hwmap[i].txflags |= IEEE80211_RADIOTAP_F_SHORTPRE;
4669d3be6f5bSSam Leffler 		/* NB: receive frames include FCS */
4670d3be6f5bSSam Leffler 		sc->sc_hwmap[i].rxflags = sc->sc_hwmap[i].txflags |
4671d3be6f5bSSam Leffler 			IEEE80211_RADIOTAP_F_FCS;
46723e50ec2cSSam Leffler 		/* setup blink rate table to avoid per-packet lookup */
46733e50ec2cSSam Leffler 		for (j = 0; j < N(blinkrates)-1; j++)
46743e50ec2cSSam Leffler 			if (blinkrates[j].rate == sc->sc_hwmap[i].ieeerate)
46753e50ec2cSSam Leffler 				break;
46763e50ec2cSSam Leffler 		/* NB: this uses the last entry if the rate isn't found */
46773e50ec2cSSam Leffler 		/* XXX beware of overlow */
46783e50ec2cSSam Leffler 		sc->sc_hwmap[i].ledon = (blinkrates[j].timeOn * hz) / 1000;
46793e50ec2cSSam Leffler 		sc->sc_hwmap[i].ledoff = (blinkrates[j].timeOff * hz) / 1000;
4680c42a7b7eSSam Leffler 	}
46815591b213SSam Leffler 	sc->sc_currates = rt;
46825591b213SSam Leffler 	sc->sc_curmode = mode;
46835591b213SSam Leffler 	/*
4684c42a7b7eSSam Leffler 	 * All protection frames are transmited at 2Mb/s for
4685c42a7b7eSSam Leffler 	 * 11g, otherwise at 1Mb/s.
46865591b213SSam Leffler 	 */
4687913a1ba1SSam Leffler 	if (mode == IEEE80211_MODE_11G)
4688913a1ba1SSam Leffler 		sc->sc_protrix = ath_tx_findrix(rt, 2*2);
4689913a1ba1SSam Leffler 	else
4690913a1ba1SSam Leffler 		sc->sc_protrix = ath_tx_findrix(rt, 2*1);
469155f63772SSam Leffler 	/* rate index used to send management frames */
469255f63772SSam Leffler 	sc->sc_minrateix = 0;
46938b5341deSSam Leffler 	/*
46948b5341deSSam Leffler 	 * Setup multicast rate state.
46958b5341deSSam Leffler 	 */
46968b5341deSSam Leffler 	/* XXX layering violation */
46978b5341deSSam Leffler 	sc->sc_mcastrix = ath_tx_findrix(rt, sc->sc_ic.ic_mcast_rate);
46988b5341deSSam Leffler 	sc->sc_mcastrate = sc->sc_ic.ic_mcast_rate;
4699c42a7b7eSSam Leffler 	/* NB: caller is responsible for reseting rate control state */
47003e50ec2cSSam Leffler #undef N
47015591b213SSam Leffler }
47025591b213SSam Leffler 
47035591b213SSam Leffler #ifdef AR_DEBUG
47045591b213SSam Leffler static void
47055591b213SSam Leffler ath_printrxbuf(struct ath_buf *bf, int done)
47065591b213SSam Leffler {
47075591b213SSam Leffler 	struct ath_desc *ds;
47085591b213SSam Leffler 	int i;
47095591b213SSam Leffler 
47105591b213SSam Leffler 	for (i = 0, ds = bf->bf_desc; i < bf->bf_nseg; i++, ds++) {
4711370572d9SSam Leffler 		printf("R%d (%p %p) L:%08x D:%08x %08x %08x %08x %08x %c\n",
47125591b213SSam Leffler 		    i, ds, (struct ath_desc *)bf->bf_daddr + i,
47135591b213SSam Leffler 		    ds->ds_link, ds->ds_data,
47145591b213SSam Leffler 		    ds->ds_ctl0, ds->ds_ctl1,
47155591b213SSam Leffler 		    ds->ds_hw[0], ds->ds_hw[1],
47165591b213SSam Leffler 		    !done ? ' ' : (ds->ds_rxstat.rs_status == 0) ? '*' : '!');
47175591b213SSam Leffler 	}
47185591b213SSam Leffler }
47195591b213SSam Leffler 
47205591b213SSam Leffler static void
47215591b213SSam Leffler ath_printtxbuf(struct ath_buf *bf, int done)
47225591b213SSam Leffler {
47235591b213SSam Leffler 	struct ath_desc *ds;
47245591b213SSam Leffler 	int i;
47255591b213SSam Leffler 
47265591b213SSam Leffler 	for (i = 0, ds = bf->bf_desc; i < bf->bf_nseg; i++, ds++) {
4727370572d9SSam Leffler 		printf("T%d (%p %p) L:%08x D:%08x %08x %08x %08x %08x %08x %08x %c\n",
47285591b213SSam Leffler 		    i, ds, (struct ath_desc *)bf->bf_daddr + i,
47295591b213SSam Leffler 		    ds->ds_link, ds->ds_data,
47305591b213SSam Leffler 		    ds->ds_ctl0, ds->ds_ctl1,
47315591b213SSam Leffler 		    ds->ds_hw[0], ds->ds_hw[1], ds->ds_hw[2], ds->ds_hw[3],
47325591b213SSam Leffler 		    !done ? ' ' : (ds->ds_txstat.ts_status == 0) ? '*' : '!');
47335591b213SSam Leffler 	}
47345591b213SSam Leffler }
47355591b213SSam Leffler #endif /* AR_DEBUG */
4736c42a7b7eSSam Leffler 
4737c42a7b7eSSam Leffler static void
4738c42a7b7eSSam Leffler ath_watchdog(struct ifnet *ifp)
4739c42a7b7eSSam Leffler {
4740c42a7b7eSSam Leffler 	struct ath_softc *sc = ifp->if_softc;
4741c42a7b7eSSam Leffler 	struct ieee80211com *ic = &sc->sc_ic;
4742c42a7b7eSSam Leffler 
4743c42a7b7eSSam Leffler 	ifp->if_timer = 0;
474413f4c340SRobert Watson 	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || sc->sc_invalid)
4745c42a7b7eSSam Leffler 		return;
4746c42a7b7eSSam Leffler 	if (sc->sc_tx_timer) {
4747c42a7b7eSSam Leffler 		if (--sc->sc_tx_timer == 0) {
4748c42a7b7eSSam Leffler 			if_printf(ifp, "device timeout\n");
4749c42a7b7eSSam Leffler 			ath_reset(ifp);
4750c42a7b7eSSam Leffler 			ifp->if_oerrors++;
4751c42a7b7eSSam Leffler 			sc->sc_stats.ast_watchdog++;
4752c42a7b7eSSam Leffler 		} else
4753c42a7b7eSSam Leffler 			ifp->if_timer = 1;
4754c42a7b7eSSam Leffler 	}
4755c42a7b7eSSam Leffler 	ieee80211_watchdog(ic);
4756c42a7b7eSSam Leffler }
4757c42a7b7eSSam Leffler 
4758c42a7b7eSSam Leffler /*
4759c42a7b7eSSam Leffler  * Diagnostic interface to the HAL.  This is used by various
4760c42a7b7eSSam Leffler  * tools to do things like retrieve register contents for
4761c42a7b7eSSam Leffler  * debugging.  The mechanism is intentionally opaque so that
4762c42a7b7eSSam Leffler  * it can change frequently w/o concern for compatiblity.
4763c42a7b7eSSam Leffler  */
4764c42a7b7eSSam Leffler static int
4765c42a7b7eSSam Leffler ath_ioctl_diag(struct ath_softc *sc, struct ath_diag *ad)
4766c42a7b7eSSam Leffler {
4767c42a7b7eSSam Leffler 	struct ath_hal *ah = sc->sc_ah;
4768c42a7b7eSSam Leffler 	u_int id = ad->ad_id & ATH_DIAG_ID;
4769c42a7b7eSSam Leffler 	void *indata = NULL;
4770c42a7b7eSSam Leffler 	void *outdata = NULL;
4771c42a7b7eSSam Leffler 	u_int32_t insize = ad->ad_in_size;
4772c42a7b7eSSam Leffler 	u_int32_t outsize = ad->ad_out_size;
4773c42a7b7eSSam Leffler 	int error = 0;
4774c42a7b7eSSam Leffler 
4775c42a7b7eSSam Leffler 	if (ad->ad_id & ATH_DIAG_IN) {
4776c42a7b7eSSam Leffler 		/*
4777c42a7b7eSSam Leffler 		 * Copy in data.
4778c42a7b7eSSam Leffler 		 */
4779c42a7b7eSSam Leffler 		indata = malloc(insize, M_TEMP, M_NOWAIT);
4780c42a7b7eSSam Leffler 		if (indata == NULL) {
4781c42a7b7eSSam Leffler 			error = ENOMEM;
4782c42a7b7eSSam Leffler 			goto bad;
4783c42a7b7eSSam Leffler 		}
4784c42a7b7eSSam Leffler 		error = copyin(ad->ad_in_data, indata, insize);
4785c42a7b7eSSam Leffler 		if (error)
4786c42a7b7eSSam Leffler 			goto bad;
4787c42a7b7eSSam Leffler 	}
4788c42a7b7eSSam Leffler 	if (ad->ad_id & ATH_DIAG_DYN) {
4789c42a7b7eSSam Leffler 		/*
4790c42a7b7eSSam Leffler 		 * Allocate a buffer for the results (otherwise the HAL
4791c42a7b7eSSam Leffler 		 * returns a pointer to a buffer where we can read the
4792c42a7b7eSSam Leffler 		 * results).  Note that we depend on the HAL leaving this
4793c42a7b7eSSam Leffler 		 * pointer for us to use below in reclaiming the buffer;
4794c42a7b7eSSam Leffler 		 * may want to be more defensive.
4795c42a7b7eSSam Leffler 		 */
4796c42a7b7eSSam Leffler 		outdata = malloc(outsize, M_TEMP, M_NOWAIT);
4797c42a7b7eSSam Leffler 		if (outdata == NULL) {
4798c42a7b7eSSam Leffler 			error = ENOMEM;
4799c42a7b7eSSam Leffler 			goto bad;
4800c42a7b7eSSam Leffler 		}
4801c42a7b7eSSam Leffler 	}
4802c42a7b7eSSam Leffler 	if (ath_hal_getdiagstate(ah, id, indata, insize, &outdata, &outsize)) {
4803c42a7b7eSSam Leffler 		if (outsize < ad->ad_out_size)
4804c42a7b7eSSam Leffler 			ad->ad_out_size = outsize;
4805c42a7b7eSSam Leffler 		if (outdata != NULL)
4806c42a7b7eSSam Leffler 			error = copyout(outdata, ad->ad_out_data,
4807c42a7b7eSSam Leffler 					ad->ad_out_size);
4808c42a7b7eSSam Leffler 	} else {
4809c42a7b7eSSam Leffler 		error = EINVAL;
4810c42a7b7eSSam Leffler 	}
4811c42a7b7eSSam Leffler bad:
4812c42a7b7eSSam Leffler 	if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL)
4813c42a7b7eSSam Leffler 		free(indata, M_TEMP);
4814c42a7b7eSSam Leffler 	if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL)
4815c42a7b7eSSam Leffler 		free(outdata, M_TEMP);
4816c42a7b7eSSam Leffler 	return error;
4817c42a7b7eSSam Leffler }
4818c42a7b7eSSam Leffler 
4819c42a7b7eSSam Leffler static int
4820c42a7b7eSSam Leffler ath_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
4821c42a7b7eSSam Leffler {
4822c42a7b7eSSam Leffler #define	IS_RUNNING(ifp) \
482313f4c340SRobert Watson 	((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING))
4824c42a7b7eSSam Leffler 	struct ath_softc *sc = ifp->if_softc;
4825c42a7b7eSSam Leffler 	struct ieee80211com *ic = &sc->sc_ic;
4826c42a7b7eSSam Leffler 	struct ifreq *ifr = (struct ifreq *)data;
4827c42a7b7eSSam Leffler 	int error = 0;
4828c42a7b7eSSam Leffler 
4829c42a7b7eSSam Leffler 	ATH_LOCK(sc);
4830c42a7b7eSSam Leffler 	switch (cmd) {
4831c42a7b7eSSam Leffler 	case SIOCSIFFLAGS:
4832c42a7b7eSSam Leffler 		if (IS_RUNNING(ifp)) {
4833c42a7b7eSSam Leffler 			/*
4834c42a7b7eSSam Leffler 			 * To avoid rescanning another access point,
4835c42a7b7eSSam Leffler 			 * do not call ath_init() here.  Instead,
4836c42a7b7eSSam Leffler 			 * only reflect promisc mode settings.
4837c42a7b7eSSam Leffler 			 */
4838c42a7b7eSSam Leffler 			ath_mode_init(sc);
4839c42a7b7eSSam Leffler 		} else if (ifp->if_flags & IFF_UP) {
4840c42a7b7eSSam Leffler 			/*
4841c42a7b7eSSam Leffler 			 * Beware of being called during attach/detach
4842c42a7b7eSSam Leffler 			 * to reset promiscuous mode.  In that case we
4843c42a7b7eSSam Leffler 			 * will still be marked UP but not RUNNING.
4844c42a7b7eSSam Leffler 			 * However trying to re-init the interface
4845c42a7b7eSSam Leffler 			 * is the wrong thing to do as we've already
4846c42a7b7eSSam Leffler 			 * torn down much of our state.  There's
4847c42a7b7eSSam Leffler 			 * probably a better way to deal with this.
4848c42a7b7eSSam Leffler 			 */
4849c42a7b7eSSam Leffler 			if (!sc->sc_invalid && ic->ic_bss != NULL)
4850fc74a9f9SBrooks Davis 				ath_init(sc);	/* XXX lose error */
4851c42a7b7eSSam Leffler 		} else
4852c42a7b7eSSam Leffler 			ath_stop_locked(ifp);
4853c42a7b7eSSam Leffler 		break;
4854c42a7b7eSSam Leffler 	case SIOCADDMULTI:
4855c42a7b7eSSam Leffler 	case SIOCDELMULTI:
4856c42a7b7eSSam Leffler 		/*
4857c42a7b7eSSam Leffler 		 * The upper layer has already installed/removed
4858c42a7b7eSSam Leffler 		 * the multicast address(es), just recalculate the
4859c42a7b7eSSam Leffler 		 * multicast filter for the card.
4860c42a7b7eSSam Leffler 		 */
486113f4c340SRobert Watson 		if (ifp->if_drv_flags & IFF_DRV_RUNNING)
4862c42a7b7eSSam Leffler 			ath_mode_init(sc);
4863c42a7b7eSSam Leffler 		break;
4864c42a7b7eSSam Leffler 	case SIOCGATHSTATS:
4865c42a7b7eSSam Leffler 		/* NB: embed these numbers to get a consistent view */
4866c42a7b7eSSam Leffler 		sc->sc_stats.ast_tx_packets = ifp->if_opackets;
4867c42a7b7eSSam Leffler 		sc->sc_stats.ast_rx_packets = ifp->if_ipackets;
4868c42a7b7eSSam Leffler 		sc->sc_stats.ast_rx_rssi = ieee80211_getrssi(ic);
4869c42a7b7eSSam Leffler 		ATH_UNLOCK(sc);
4870c42a7b7eSSam Leffler 		/*
4871c42a7b7eSSam Leffler 		 * NB: Drop the softc lock in case of a page fault;
4872c42a7b7eSSam Leffler 		 * we'll accept any potential inconsisentcy in the
4873c42a7b7eSSam Leffler 		 * statistics.  The alternative is to copy the data
4874c42a7b7eSSam Leffler 		 * to a local structure.
4875c42a7b7eSSam Leffler 		 */
4876c42a7b7eSSam Leffler 		return copyout(&sc->sc_stats,
4877c42a7b7eSSam Leffler 				ifr->ifr_data, sizeof (sc->sc_stats));
4878c42a7b7eSSam Leffler 	case SIOCGATHDIAG:
4879c42a7b7eSSam Leffler 		error = ath_ioctl_diag(sc, (struct ath_diag *) ifr);
4880c42a7b7eSSam Leffler 		break;
4881c42a7b7eSSam Leffler 	default:
4882c42a7b7eSSam Leffler 		error = ieee80211_ioctl(ic, cmd, data);
4883c42a7b7eSSam Leffler 		if (error == ENETRESET) {
4884c42a7b7eSSam Leffler 			if (IS_RUNNING(ifp) &&
4885c42a7b7eSSam Leffler 			    ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
4886fc74a9f9SBrooks Davis 				ath_init(sc);	/* XXX lose error */
4887c42a7b7eSSam Leffler 			error = 0;
4888c42a7b7eSSam Leffler 		}
4889c42a7b7eSSam Leffler 		if (error == ERESTART)
4890c42a7b7eSSam Leffler 			error = IS_RUNNING(ifp) ? ath_reset(ifp) : 0;
4891c42a7b7eSSam Leffler 		break;
4892c42a7b7eSSam Leffler 	}
4893c42a7b7eSSam Leffler 	ATH_UNLOCK(sc);
4894c42a7b7eSSam Leffler 	return error;
4895a614e076SSam Leffler #undef IS_RUNNING
4896c42a7b7eSSam Leffler }
4897c42a7b7eSSam Leffler 
4898c42a7b7eSSam Leffler static int
4899c42a7b7eSSam Leffler ath_sysctl_slottime(SYSCTL_HANDLER_ARGS)
4900c42a7b7eSSam Leffler {
4901c42a7b7eSSam Leffler 	struct ath_softc *sc = arg1;
4902c42a7b7eSSam Leffler 	u_int slottime = ath_hal_getslottime(sc->sc_ah);
4903c42a7b7eSSam Leffler 	int error;
4904c42a7b7eSSam Leffler 
4905c42a7b7eSSam Leffler 	error = sysctl_handle_int(oidp, &slottime, 0, req);
4906c42a7b7eSSam Leffler 	if (error || !req->newptr)
4907c42a7b7eSSam Leffler 		return error;
4908c42a7b7eSSam Leffler 	return !ath_hal_setslottime(sc->sc_ah, slottime) ? EINVAL : 0;
4909c42a7b7eSSam Leffler }
4910c42a7b7eSSam Leffler 
4911c42a7b7eSSam Leffler static int
4912c42a7b7eSSam Leffler ath_sysctl_acktimeout(SYSCTL_HANDLER_ARGS)
4913c42a7b7eSSam Leffler {
4914c42a7b7eSSam Leffler 	struct ath_softc *sc = arg1;
4915c42a7b7eSSam Leffler 	u_int acktimeout = ath_hal_getacktimeout(sc->sc_ah);
4916c42a7b7eSSam Leffler 	int error;
4917c42a7b7eSSam Leffler 
4918c42a7b7eSSam Leffler 	error = sysctl_handle_int(oidp, &acktimeout, 0, req);
4919c42a7b7eSSam Leffler 	if (error || !req->newptr)
4920c42a7b7eSSam Leffler 		return error;
4921c42a7b7eSSam Leffler 	return !ath_hal_setacktimeout(sc->sc_ah, acktimeout) ? EINVAL : 0;
4922c42a7b7eSSam Leffler }
4923c42a7b7eSSam Leffler 
4924c42a7b7eSSam Leffler static int
4925c42a7b7eSSam Leffler ath_sysctl_ctstimeout(SYSCTL_HANDLER_ARGS)
4926c42a7b7eSSam Leffler {
4927c42a7b7eSSam Leffler 	struct ath_softc *sc = arg1;
4928c42a7b7eSSam Leffler 	u_int ctstimeout = ath_hal_getctstimeout(sc->sc_ah);
4929c42a7b7eSSam Leffler 	int error;
4930c42a7b7eSSam Leffler 
4931c42a7b7eSSam Leffler 	error = sysctl_handle_int(oidp, &ctstimeout, 0, req);
4932c42a7b7eSSam Leffler 	if (error || !req->newptr)
4933c42a7b7eSSam Leffler 		return error;
4934c42a7b7eSSam Leffler 	return !ath_hal_setctstimeout(sc->sc_ah, ctstimeout) ? EINVAL : 0;
4935c42a7b7eSSam Leffler }
4936c42a7b7eSSam Leffler 
4937c42a7b7eSSam Leffler static int
4938c42a7b7eSSam Leffler ath_sysctl_softled(SYSCTL_HANDLER_ARGS)
4939c42a7b7eSSam Leffler {
4940c42a7b7eSSam Leffler 	struct ath_softc *sc = arg1;
4941c42a7b7eSSam Leffler 	int softled = sc->sc_softled;
4942c42a7b7eSSam Leffler 	int error;
4943c42a7b7eSSam Leffler 
4944c42a7b7eSSam Leffler 	error = sysctl_handle_int(oidp, &softled, 0, req);
4945c42a7b7eSSam Leffler 	if (error || !req->newptr)
4946c42a7b7eSSam Leffler 		return error;
49473e50ec2cSSam Leffler 	softled = (softled != 0);
4948c42a7b7eSSam Leffler 	if (softled != sc->sc_softled) {
49493e50ec2cSSam Leffler 		if (softled) {
49503e50ec2cSSam Leffler 			/* NB: handle any sc_ledpin change */
4951c42a7b7eSSam Leffler 			ath_hal_gpioCfgOutput(sc->sc_ah, sc->sc_ledpin);
49523e50ec2cSSam Leffler 			ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin,
49533e50ec2cSSam Leffler 				!sc->sc_ledon);
49543e50ec2cSSam Leffler 		}
4955c42a7b7eSSam Leffler 		sc->sc_softled = softled;
4956c42a7b7eSSam Leffler 	}
4957c42a7b7eSSam Leffler 	return 0;
4958c42a7b7eSSam Leffler }
4959c42a7b7eSSam Leffler 
4960c42a7b7eSSam Leffler static int
4961c42a7b7eSSam Leffler ath_sysctl_rxantenna(SYSCTL_HANDLER_ARGS)
4962c42a7b7eSSam Leffler {
4963c42a7b7eSSam Leffler 	struct ath_softc *sc = arg1;
4964c42a7b7eSSam Leffler 	u_int defantenna = ath_hal_getdefantenna(sc->sc_ah);
4965c42a7b7eSSam Leffler 	int error;
4966c42a7b7eSSam Leffler 
4967c42a7b7eSSam Leffler 	error = sysctl_handle_int(oidp, &defantenna, 0, req);
4968c42a7b7eSSam Leffler 	if (!error && req->newptr)
4969c42a7b7eSSam Leffler 		ath_hal_setdefantenna(sc->sc_ah, defantenna);
4970c42a7b7eSSam Leffler 	return error;
4971c42a7b7eSSam Leffler }
4972c42a7b7eSSam Leffler 
4973c42a7b7eSSam Leffler static int
4974c42a7b7eSSam Leffler ath_sysctl_diversity(SYSCTL_HANDLER_ARGS)
4975c42a7b7eSSam Leffler {
4976c42a7b7eSSam Leffler 	struct ath_softc *sc = arg1;
4977c59005e9SSam Leffler 	u_int diversity = ath_hal_getdiversity(sc->sc_ah);
4978c42a7b7eSSam Leffler 	int error;
4979c42a7b7eSSam Leffler 
4980c42a7b7eSSam Leffler 	error = sysctl_handle_int(oidp, &diversity, 0, req);
4981c42a7b7eSSam Leffler 	if (error || !req->newptr)
4982c42a7b7eSSam Leffler 		return error;
4983c59005e9SSam Leffler 	if (!ath_hal_setdiversity(sc->sc_ah, diversity))
4984c59005e9SSam Leffler 		return EINVAL;
4985c42a7b7eSSam Leffler 	sc->sc_diversity = diversity;
4986c59005e9SSam Leffler 	return 0;
4987c42a7b7eSSam Leffler }
4988c42a7b7eSSam Leffler 
4989c42a7b7eSSam Leffler static int
4990c42a7b7eSSam Leffler ath_sysctl_diag(SYSCTL_HANDLER_ARGS)
4991c42a7b7eSSam Leffler {
4992c42a7b7eSSam Leffler 	struct ath_softc *sc = arg1;
4993c42a7b7eSSam Leffler 	u_int32_t diag;
4994c42a7b7eSSam Leffler 	int error;
4995c42a7b7eSSam Leffler 
4996c42a7b7eSSam Leffler 	if (!ath_hal_getdiag(sc->sc_ah, &diag))
4997c42a7b7eSSam Leffler 		return EINVAL;
4998c42a7b7eSSam Leffler 	error = sysctl_handle_int(oidp, &diag, 0, req);
4999c42a7b7eSSam Leffler 	if (error || !req->newptr)
5000c42a7b7eSSam Leffler 		return error;
5001c42a7b7eSSam Leffler 	return !ath_hal_setdiag(sc->sc_ah, diag) ? EINVAL : 0;
5002c42a7b7eSSam Leffler }
5003c42a7b7eSSam Leffler 
5004c42a7b7eSSam Leffler static int
5005c42a7b7eSSam Leffler ath_sysctl_tpscale(SYSCTL_HANDLER_ARGS)
5006c42a7b7eSSam Leffler {
5007c42a7b7eSSam Leffler 	struct ath_softc *sc = arg1;
5008fc74a9f9SBrooks Davis 	struct ifnet *ifp = sc->sc_ifp;
5009c42a7b7eSSam Leffler 	u_int32_t scale;
5010c42a7b7eSSam Leffler 	int error;
5011c42a7b7eSSam Leffler 
5012c42a7b7eSSam Leffler 	ath_hal_gettpscale(sc->sc_ah, &scale);
5013c42a7b7eSSam Leffler 	error = sysctl_handle_int(oidp, &scale, 0, req);
5014c42a7b7eSSam Leffler 	if (error || !req->newptr)
5015c42a7b7eSSam Leffler 		return error;
5016c42a7b7eSSam Leffler 	return !ath_hal_settpscale(sc->sc_ah, scale) ? EINVAL : ath_reset(ifp);
5017c42a7b7eSSam Leffler }
5018c42a7b7eSSam Leffler 
5019c42a7b7eSSam Leffler static int
5020c42a7b7eSSam Leffler ath_sysctl_tpc(SYSCTL_HANDLER_ARGS)
5021c42a7b7eSSam Leffler {
5022c42a7b7eSSam Leffler 	struct ath_softc *sc = arg1;
5023c42a7b7eSSam Leffler 	u_int tpc = ath_hal_gettpc(sc->sc_ah);
5024c42a7b7eSSam Leffler 	int error;
5025c42a7b7eSSam Leffler 
5026c42a7b7eSSam Leffler 	error = sysctl_handle_int(oidp, &tpc, 0, req);
5027c42a7b7eSSam Leffler 	if (error || !req->newptr)
5028c42a7b7eSSam Leffler 		return error;
5029c42a7b7eSSam Leffler 	return !ath_hal_settpc(sc->sc_ah, tpc) ? EINVAL : 0;
5030c42a7b7eSSam Leffler }
5031c42a7b7eSSam Leffler 
503217f3f177SSam Leffler static int
503317f3f177SSam Leffler ath_sysctl_regdomain(SYSCTL_HANDLER_ARGS)
503417f3f177SSam Leffler {
503517f3f177SSam Leffler 	struct ath_softc *sc = arg1;
503617f3f177SSam Leffler 	u_int32_t rd;
503717f3f177SSam Leffler 	int error;
503817f3f177SSam Leffler 
503917f3f177SSam Leffler 	if (!ath_hal_getregdomain(sc->sc_ah, &rd))
504017f3f177SSam Leffler 		return EINVAL;
504117f3f177SSam Leffler 	error = sysctl_handle_int(oidp, &rd, 0, req);
504217f3f177SSam Leffler 	if (error || !req->newptr)
504317f3f177SSam Leffler 		return error;
504417f3f177SSam Leffler 	return !ath_hal_setregdomain(sc->sc_ah, rd) ? EINVAL : 0;
504517f3f177SSam Leffler }
504617f3f177SSam Leffler 
5047c42a7b7eSSam Leffler static void
5048c42a7b7eSSam Leffler ath_sysctlattach(struct ath_softc *sc)
5049c42a7b7eSSam Leffler {
5050c42a7b7eSSam Leffler 	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
5051c42a7b7eSSam Leffler 	struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
5052c59005e9SSam Leffler 	struct ath_hal *ah = sc->sc_ah;
5053c42a7b7eSSam Leffler 
5054c42a7b7eSSam Leffler 	ath_hal_getcountrycode(sc->sc_ah, &sc->sc_countrycode);
5055c42a7b7eSSam Leffler 	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
5056c42a7b7eSSam Leffler 		"countrycode", CTLFLAG_RD, &sc->sc_countrycode, 0,
5057c42a7b7eSSam Leffler 		"EEPROM country code");
505817f3f177SSam Leffler 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
505917f3f177SSam Leffler 		"regdomain", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
506017f3f177SSam Leffler 		ath_sysctl_regdomain, "I", "EEPROM regdomain code");
5061c42a7b7eSSam Leffler 	sc->sc_debug = ath_debug;
5062c42a7b7eSSam Leffler 	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
5063c42a7b7eSSam Leffler 		"debug", CTLFLAG_RW, &sc->sc_debug, 0,
5064c42a7b7eSSam Leffler 		"control debugging printfs");
5065c42a7b7eSSam Leffler 
5066c42a7b7eSSam Leffler 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
5067c42a7b7eSSam Leffler 		"slottime", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
5068c42a7b7eSSam Leffler 		ath_sysctl_slottime, "I", "802.11 slot time (us)");
5069c42a7b7eSSam Leffler 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
5070c42a7b7eSSam Leffler 		"acktimeout", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
5071c42a7b7eSSam Leffler 		ath_sysctl_acktimeout, "I", "802.11 ACK timeout (us)");
5072c42a7b7eSSam Leffler 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
5073c42a7b7eSSam Leffler 		"ctstimeout", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
5074c42a7b7eSSam Leffler 		ath_sysctl_ctstimeout, "I", "802.11 CTS timeout (us)");
5075c42a7b7eSSam Leffler 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
5076c42a7b7eSSam Leffler 		"softled", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
5077c42a7b7eSSam Leffler 		ath_sysctl_softled, "I", "enable/disable software LED support");
5078c42a7b7eSSam Leffler 	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
5079c42a7b7eSSam Leffler 		"ledpin", CTLFLAG_RW, &sc->sc_ledpin, 0,
5080c42a7b7eSSam Leffler 		"GPIO pin connected to LED");
5081c42a7b7eSSam Leffler 	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
50823e50ec2cSSam Leffler 		"ledon", CTLFLAG_RW, &sc->sc_ledon, 0,
50833e50ec2cSSam Leffler 		"setting to turn LED on");
50843e50ec2cSSam Leffler 	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
50853e50ec2cSSam Leffler 		"ledidle", CTLFLAG_RW, &sc->sc_ledidle, 0,
50863e50ec2cSSam Leffler 		"idle time for inactivity LED (ticks)");
50873e50ec2cSSam Leffler 	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
5088c42a7b7eSSam Leffler 		"txantenna", CTLFLAG_RW, &sc->sc_txantenna, 0,
5089c42a7b7eSSam Leffler 		"tx antenna (0=auto)");
5090c42a7b7eSSam Leffler 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
5091c42a7b7eSSam Leffler 		"rxantenna", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
5092c42a7b7eSSam Leffler 		ath_sysctl_rxantenna, "I", "default/rx antenna");
5093c59005e9SSam Leffler 	if (ath_hal_hasdiversity(ah))
5094c42a7b7eSSam Leffler 		SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
5095c42a7b7eSSam Leffler 			"diversity", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
5096c42a7b7eSSam Leffler 			ath_sysctl_diversity, "I", "antenna diversity");
5097c42a7b7eSSam Leffler 	sc->sc_txintrperiod = ATH_TXINTR_PERIOD;
5098c42a7b7eSSam Leffler 	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
5099c42a7b7eSSam Leffler 		"txintrperiod", CTLFLAG_RW, &sc->sc_txintrperiod, 0,
5100c42a7b7eSSam Leffler 		"tx descriptor batching");
5101c42a7b7eSSam Leffler 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
5102c42a7b7eSSam Leffler 		"diag", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
5103c42a7b7eSSam Leffler 		ath_sysctl_diag, "I", "h/w diagnostic control");
5104c42a7b7eSSam Leffler 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
5105c42a7b7eSSam Leffler 		"tpscale", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
5106c42a7b7eSSam Leffler 		ath_sysctl_tpscale, "I", "tx power scaling");
5107c59005e9SSam Leffler 	if (ath_hal_hastpc(ah))
5108c42a7b7eSSam Leffler 		SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
5109c42a7b7eSSam Leffler 			"tpc", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
5110c42a7b7eSSam Leffler 			ath_sysctl_tpc, "I", "enable/disable per-packet TPC");
51117b0c77ecSSam Leffler 	sc->sc_monpass = HAL_RXERR_DECRYPT | HAL_RXERR_MIC;
51127b0c77ecSSam Leffler 	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
51137b0c77ecSSam Leffler 		"monpass", CTLFLAG_RW, &sc->sc_monpass, 0,
51147b0c77ecSSam Leffler 		"mask of error frames to pass when monitoring");
5115c42a7b7eSSam Leffler }
5116c42a7b7eSSam Leffler 
5117c42a7b7eSSam Leffler static void
5118c42a7b7eSSam Leffler ath_bpfattach(struct ath_softc *sc)
5119c42a7b7eSSam Leffler {
5120fc74a9f9SBrooks Davis 	struct ifnet *ifp = sc->sc_ifp;
5121c42a7b7eSSam Leffler 
5122c42a7b7eSSam Leffler 	bpfattach2(ifp, DLT_IEEE802_11_RADIO,
5123c42a7b7eSSam Leffler 		sizeof(struct ieee80211_frame) + sizeof(sc->sc_tx_th),
5124c42a7b7eSSam Leffler 		&sc->sc_drvbpf);
5125c42a7b7eSSam Leffler 	/*
5126c42a7b7eSSam Leffler 	 * Initialize constant fields.
5127c42a7b7eSSam Leffler 	 * XXX make header lengths a multiple of 32-bits so subsequent
5128c42a7b7eSSam Leffler 	 *     headers are properly aligned; this is a kludge to keep
5129c42a7b7eSSam Leffler 	 *     certain applications happy.
5130c42a7b7eSSam Leffler 	 *
5131c42a7b7eSSam Leffler 	 * NB: the channel is setup each time we transition to the
5132c42a7b7eSSam Leffler 	 *     RUN state to avoid filling it in for each frame.
5133c42a7b7eSSam Leffler 	 */
5134c42a7b7eSSam Leffler 	sc->sc_tx_th_len = roundup(sizeof(sc->sc_tx_th), sizeof(u_int32_t));
5135c42a7b7eSSam Leffler 	sc->sc_tx_th.wt_ihdr.it_len = htole16(sc->sc_tx_th_len);
5136c42a7b7eSSam Leffler 	sc->sc_tx_th.wt_ihdr.it_present = htole32(ATH_TX_RADIOTAP_PRESENT);
5137c42a7b7eSSam Leffler 
5138d3be6f5bSSam Leffler 	sc->sc_rx_th_len = roundup(sizeof(sc->sc_rx_th), sizeof(u_int32_t));
5139d3be6f5bSSam Leffler 	sc->sc_rx_th.wr_ihdr.it_len = htole16(sc->sc_rx_th_len);
5140c42a7b7eSSam Leffler 	sc->sc_rx_th.wr_ihdr.it_present = htole32(ATH_RX_RADIOTAP_PRESENT);
5141c42a7b7eSSam Leffler }
5142c42a7b7eSSam Leffler 
5143c42a7b7eSSam Leffler /*
5144c42a7b7eSSam Leffler  * Announce various information on device/driver attach.
5145c42a7b7eSSam Leffler  */
5146c42a7b7eSSam Leffler static void
5147c42a7b7eSSam Leffler ath_announce(struct ath_softc *sc)
5148c42a7b7eSSam Leffler {
5149c42a7b7eSSam Leffler #define	HAL_MODE_DUALBAND	(HAL_MODE_11A|HAL_MODE_11B)
5150fc74a9f9SBrooks Davis 	struct ifnet *ifp = sc->sc_ifp;
5151c42a7b7eSSam Leffler 	struct ath_hal *ah = sc->sc_ah;
5152c42a7b7eSSam Leffler 	u_int modes, cc;
5153c42a7b7eSSam Leffler 
5154c42a7b7eSSam Leffler 	if_printf(ifp, "mac %d.%d phy %d.%d",
5155c42a7b7eSSam Leffler 		ah->ah_macVersion, ah->ah_macRev,
5156c42a7b7eSSam Leffler 		ah->ah_phyRev >> 4, ah->ah_phyRev & 0xf);
5157c42a7b7eSSam Leffler 	/*
5158c42a7b7eSSam Leffler 	 * Print radio revision(s).  We check the wireless modes
5159c42a7b7eSSam Leffler 	 * to avoid falsely printing revs for inoperable parts.
5160c42a7b7eSSam Leffler 	 * Dual-band radio revs are returned in the 5Ghz rev number.
5161c42a7b7eSSam Leffler 	 */
5162c42a7b7eSSam Leffler 	ath_hal_getcountrycode(ah, &cc);
5163c42a7b7eSSam Leffler 	modes = ath_hal_getwirelessmodes(ah, cc);
5164c42a7b7eSSam Leffler 	if ((modes & HAL_MODE_DUALBAND) == HAL_MODE_DUALBAND) {
5165c42a7b7eSSam Leffler 		if (ah->ah_analog5GhzRev && ah->ah_analog2GhzRev)
5166c42a7b7eSSam Leffler 			printf(" 5ghz radio %d.%d 2ghz radio %d.%d",
5167c42a7b7eSSam Leffler 				ah->ah_analog5GhzRev >> 4,
5168c42a7b7eSSam Leffler 				ah->ah_analog5GhzRev & 0xf,
5169c42a7b7eSSam Leffler 				ah->ah_analog2GhzRev >> 4,
5170c42a7b7eSSam Leffler 				ah->ah_analog2GhzRev & 0xf);
5171c42a7b7eSSam Leffler 		else
5172c42a7b7eSSam Leffler 			printf(" radio %d.%d", ah->ah_analog5GhzRev >> 4,
5173c42a7b7eSSam Leffler 				ah->ah_analog5GhzRev & 0xf);
5174c42a7b7eSSam Leffler 	} else
5175c42a7b7eSSam Leffler 		printf(" radio %d.%d", ah->ah_analog5GhzRev >> 4,
5176c42a7b7eSSam Leffler 			ah->ah_analog5GhzRev & 0xf);
5177c42a7b7eSSam Leffler 	printf("\n");
5178c42a7b7eSSam Leffler 	if (bootverbose) {
5179c42a7b7eSSam Leffler 		int i;
5180c42a7b7eSSam Leffler 		for (i = 0; i <= WME_AC_VO; i++) {
5181c42a7b7eSSam Leffler 			struct ath_txq *txq = sc->sc_ac2q[i];
5182c42a7b7eSSam Leffler 			if_printf(ifp, "Use hw queue %u for %s traffic\n",
5183c42a7b7eSSam Leffler 				txq->axq_qnum, ieee80211_wme_acnames[i]);
5184c42a7b7eSSam Leffler 		}
5185c42a7b7eSSam Leffler 		if_printf(ifp, "Use hw queue %u for CAB traffic\n",
5186c42a7b7eSSam Leffler 			sc->sc_cabq->axq_qnum);
5187c42a7b7eSSam Leffler 		if_printf(ifp, "Use hw queue %u for beacons\n", sc->sc_bhalq);
5188c42a7b7eSSam Leffler 	}
5189e2d787faSSam Leffler 	if (ath_rxbuf != ATH_RXBUF)
5190e2d787faSSam Leffler 		if_printf(ifp, "using %u rx buffers\n", ath_rxbuf);
5191e2d787faSSam Leffler 	if (ath_txbuf != ATH_TXBUF)
5192e2d787faSSam Leffler 		if_printf(ifp, "using %u tx buffers\n", ath_txbuf);
5193c42a7b7eSSam Leffler #undef HAL_MODE_DUALBAND
5194c42a7b7eSSam Leffler }
5195