xref: /openbsd-src/sys/dev/usb/if_athn_usb.c (revision 044d754c22e30eb37eecb51e33e2b3aa406c0080)
1*044d754cSstsp /*	$OpenBSD: if_athn_usb.c,v 1.52 2018/12/06 07:50:38 stsp Exp $	*/
213236e8dSdamien 
313236e8dSdamien /*-
413236e8dSdamien  * Copyright (c) 2011 Damien Bergamini <damien.bergamini@free.fr>
59f962d91Sstsp  * Copyright (c) 2018 Stefan Sperling <stsp@openbsd.org>
613236e8dSdamien  *
713236e8dSdamien  * Permission to use, copy, modify, and distribute this software for any
813236e8dSdamien  * purpose with or without fee is hereby granted, provided that the above
913236e8dSdamien  * copyright notice and this permission notice appear in all copies.
1013236e8dSdamien  *
1113236e8dSdamien  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1213236e8dSdamien  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1313236e8dSdamien  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1413236e8dSdamien  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1513236e8dSdamien  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1613236e8dSdamien  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1713236e8dSdamien  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1813236e8dSdamien  */
1913236e8dSdamien 
2013236e8dSdamien /*
2113236e8dSdamien  * USB front-end for Atheros AR9271 and AR7010 chipsets.
2213236e8dSdamien  */
2313236e8dSdamien 
2413236e8dSdamien #include "bpfilter.h"
2513236e8dSdamien 
2613236e8dSdamien #include <sys/param.h>
2713236e8dSdamien #include <sys/sockio.h>
2813236e8dSdamien #include <sys/mbuf.h>
2913236e8dSdamien #include <sys/kernel.h>
3013236e8dSdamien #include <sys/socket.h>
3113236e8dSdamien #include <sys/systm.h>
3213236e8dSdamien #include <sys/timeout.h>
3313236e8dSdamien #include <sys/conf.h>
3413236e8dSdamien #include <sys/device.h>
359b18ffb8Sguenther #include <sys/endian.h>
3613236e8dSdamien 
3713236e8dSdamien #include <machine/bus.h>
3813236e8dSdamien #include <machine/intr.h>
3913236e8dSdamien 
4013236e8dSdamien #if NBPFILTER > 0
4113236e8dSdamien #include <net/bpf.h>
4213236e8dSdamien #endif
4313236e8dSdamien #include <net/if.h>
4413236e8dSdamien #include <net/if_dl.h>
4513236e8dSdamien #include <net/if_media.h>
4613236e8dSdamien 
4713236e8dSdamien #include <netinet/in.h>
4813236e8dSdamien #include <netinet/if_ether.h>
4913236e8dSdamien 
5013236e8dSdamien #include <net80211/ieee80211_var.h>
5113236e8dSdamien #include <net80211/ieee80211_amrr.h>
527363c99eSstsp #include <net80211/ieee80211_mira.h>
5313236e8dSdamien #include <net80211/ieee80211_radiotap.h>
5413236e8dSdamien 
5513236e8dSdamien #include <dev/ic/athnreg.h>
5613236e8dSdamien #include <dev/ic/athnvar.h>
5713236e8dSdamien 
5813236e8dSdamien #include <dev/usb/usb.h>
5913236e8dSdamien #include <dev/usb/usbdi.h>
6013236e8dSdamien #include <dev/usb/usbdi_util.h>
6113236e8dSdamien #include <dev/usb/usbdevs.h>
6213236e8dSdamien 
6313236e8dSdamien #include <dev/usb/if_athn_usb.h>
6413236e8dSdamien 
6513236e8dSdamien static const struct athn_usb_type {
6613236e8dSdamien 	struct usb_devno	devno;
6713236e8dSdamien 	u_int			flags;
6813236e8dSdamien } athn_usb_devs[] = {
6913236e8dSdamien 	{{ USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_AR9280 },
7013236e8dSdamien 	   ATHN_USB_FLAG_AR7010 },
7113236e8dSdamien 	{{ USB_VENDOR_ACTIONTEC, USB_PRODUCT_ACTIONTEC_AR9287 },
7213236e8dSdamien 	   ATHN_USB_FLAG_AR7010 },
7313236e8dSdamien 	{{ USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_AR9271_1 }},
7413236e8dSdamien 	{{ USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_AR9271_2 }},
7513236e8dSdamien 	{{ USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_AR9271_3 }},
7613236e8dSdamien 	{{ USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_AR9280 },
7713236e8dSdamien 	   ATHN_USB_FLAG_AR7010 },
7813236e8dSdamien 	{{ USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_AR9287 },
7913236e8dSdamien 	   ATHN_USB_FLAG_AR7010 },
8013236e8dSdamien 	{{ USB_VENDOR_AZUREWAVE, USB_PRODUCT_AZUREWAVE_AR9271_1 }},
8113236e8dSdamien 	{{ USB_VENDOR_AZUREWAVE, USB_PRODUCT_AZUREWAVE_AR9271_2 }},
8213236e8dSdamien 	{{ USB_VENDOR_AZUREWAVE, USB_PRODUCT_AZUREWAVE_AR9271_3 }},
8313236e8dSdamien 	{{ USB_VENDOR_AZUREWAVE, USB_PRODUCT_AZUREWAVE_AR9271_4 }},
8413236e8dSdamien 	{{ USB_VENDOR_AZUREWAVE, USB_PRODUCT_AZUREWAVE_AR9271_5 }},
8513236e8dSdamien 	{{ USB_VENDOR_AZUREWAVE, USB_PRODUCT_AZUREWAVE_AR9271_6 }},
8613236e8dSdamien 	{{ USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_AR9271 }},
8713236e8dSdamien 	{{ USB_VENDOR_LITEON, USB_PRODUCT_LITEON_AR9271 }},
8813236e8dSdamien 	{{ USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_WNA1100 }},
8913236e8dSdamien 	{{ USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_WNDA3200 },
9013236e8dSdamien 	   ATHN_USB_FLAG_AR7010 },
91ddf3247cStedu 	{{ USB_VENDOR_PANASONIC, USB_PRODUCT_PANASONIC_N5HBZ0000055 },
92ddf3247cStedu 	   ATHN_USB_FLAG_AR7010 },
9313236e8dSdamien 	{{ USB_VENDOR_VIA, USB_PRODUCT_VIA_AR9271 }}
9413236e8dSdamien };
9513236e8dSdamien #define athn_usb_lookup(v, p)	\
9613236e8dSdamien 	((const struct athn_usb_type *)usb_lookup(athn_usb_devs, v, p))
9713236e8dSdamien 
9813236e8dSdamien int		athn_usb_match(struct device *, void *, void *);
9913236e8dSdamien void		athn_usb_attach(struct device *, struct device *, void *);
10013236e8dSdamien int		athn_usb_detach(struct device *, int);
101ef89f9e6Smpi void		athn_usb_attachhook(struct device *);
10213236e8dSdamien int		athn_usb_open_pipes(struct athn_usb_softc *);
10313236e8dSdamien void		athn_usb_close_pipes(struct athn_usb_softc *);
10413236e8dSdamien int		athn_usb_alloc_rx_list(struct athn_usb_softc *);
10513236e8dSdamien void		athn_usb_free_rx_list(struct athn_usb_softc *);
10613236e8dSdamien int		athn_usb_alloc_tx_list(struct athn_usb_softc *);
10713236e8dSdamien void		athn_usb_free_tx_list(struct athn_usb_softc *);
10813236e8dSdamien int		athn_usb_alloc_tx_cmd(struct athn_usb_softc *);
10913236e8dSdamien void		athn_usb_free_tx_cmd(struct athn_usb_softc *);
11013236e8dSdamien void		athn_usb_task(void *);
11113236e8dSdamien void		athn_usb_do_async(struct athn_usb_softc *,
11213236e8dSdamien 		    void (*)(struct athn_usb_softc *, void *), void *, int);
11313236e8dSdamien void		athn_usb_wait_async(struct athn_usb_softc *);
11413236e8dSdamien int		athn_usb_load_firmware(struct athn_usb_softc *);
11513236e8dSdamien int		athn_usb_htc_msg(struct athn_usb_softc *, uint16_t, void *,
11613236e8dSdamien 		    int);
11713236e8dSdamien int		athn_usb_htc_setup(struct athn_usb_softc *);
11813236e8dSdamien int		athn_usb_htc_connect_svc(struct athn_usb_softc *, uint16_t,
11913236e8dSdamien 		    uint8_t, uint8_t, uint8_t *);
12013236e8dSdamien int		athn_usb_wmi_xcmd(struct athn_usb_softc *, uint16_t, void *,
12113236e8dSdamien 		    int, void *);
12213236e8dSdamien int		athn_usb_read_rom(struct athn_softc *);
12313236e8dSdamien uint32_t	athn_usb_read(struct athn_softc *, uint32_t);
12413236e8dSdamien void		athn_usb_write(struct athn_softc *, uint32_t, uint32_t);
12513236e8dSdamien void		athn_usb_write_barrier(struct athn_softc *);
12613236e8dSdamien int		athn_usb_media_change(struct ifnet *);
127dd463f29Sstsp void		athn_usb_next_scan(void *);
12813236e8dSdamien int		athn_usb_newstate(struct ieee80211com *, enum ieee80211_state,
12913236e8dSdamien 		    int);
13013236e8dSdamien void		athn_usb_newstate_cb(struct athn_usb_softc *, void *);
13113236e8dSdamien void		athn_usb_newassoc(struct ieee80211com *,
13213236e8dSdamien 		    struct ieee80211_node *, int);
13313236e8dSdamien void		athn_usb_newassoc_cb(struct athn_usb_softc *, void *);
134f47be805Sstsp struct ieee80211_node *athn_usb_node_alloc(struct ieee80211com *);
135f47be805Sstsp void		athn_usb_count_active_sta(void *, struct ieee80211_node *);
136f47be805Sstsp void		athn_usb_newauth_cb(struct athn_usb_softc *, void *);
137f47be805Sstsp int		athn_usb_newauth(struct ieee80211com *,
138f47be805Sstsp 		    struct ieee80211_node *, int, uint16_t);
139f47be805Sstsp void		athn_usb_node_free(struct ieee80211com *,
14013236e8dSdamien 		    struct ieee80211_node *);
141f47be805Sstsp void		athn_usb_node_free_cb(struct athn_usb_softc *, void *);
14213236e8dSdamien int		athn_usb_ampdu_tx_start(struct ieee80211com *,
14313236e8dSdamien 		    struct ieee80211_node *, uint8_t);
14413236e8dSdamien void		athn_usb_ampdu_tx_start_cb(struct athn_usb_softc *, void *);
14513236e8dSdamien void		athn_usb_ampdu_tx_stop(struct ieee80211com *,
14613236e8dSdamien 		    struct ieee80211_node *, uint8_t);
14713236e8dSdamien void		athn_usb_ampdu_tx_stop_cb(struct athn_usb_softc *, void *);
148f47be805Sstsp void		athn_usb_clean_nodes(void *, struct ieee80211_node *);
14913236e8dSdamien int		athn_usb_create_node(struct athn_usb_softc *,
15013236e8dSdamien 		    struct ieee80211_node *);
151f47be805Sstsp int		athn_usb_node_set_rates(struct athn_usb_softc *,
152f47be805Sstsp 		    struct ieee80211_node *);
153f47be805Sstsp int		athn_usb_remove_node(struct athn_usb_softc *,
154f47be805Sstsp 		    struct ieee80211_node *);
15513236e8dSdamien void		athn_usb_rx_enable(struct athn_softc *);
15675f8bb6dSdamien int		athn_set_chan(struct athn_softc *, struct ieee80211_channel *,
15775f8bb6dSdamien 		    struct ieee80211_channel *);
15813236e8dSdamien int		athn_usb_switch_chan(struct athn_softc *,
15913236e8dSdamien 		    struct ieee80211_channel *, struct ieee80211_channel *);
16013236e8dSdamien void		athn_usb_updateedca(struct ieee80211com *);
16113236e8dSdamien void		athn_usb_updateedca_cb(struct athn_usb_softc *, void *);
16213236e8dSdamien void		athn_usb_updateslot(struct ieee80211com *);
16313236e8dSdamien void		athn_usb_updateslot_cb(struct athn_usb_softc *, void *);
16413236e8dSdamien int		athn_usb_set_key(struct ieee80211com *,
16513236e8dSdamien 		    struct ieee80211_node *, struct ieee80211_key *);
16613236e8dSdamien void		athn_usb_set_key_cb(struct athn_usb_softc *, void *);
16713236e8dSdamien void		athn_usb_delete_key(struct ieee80211com *,
16813236e8dSdamien 		    struct ieee80211_node *, struct ieee80211_key *);
16913236e8dSdamien void		athn_usb_delete_key_cb(struct athn_usb_softc *, void *);
170ab0b1be7Smglocker void		athn_usb_bcneof(struct usbd_xfer *, void *,
17113236e8dSdamien 		    usbd_status);
17213236e8dSdamien void		athn_usb_swba(struct athn_usb_softc *);
173f47be805Sstsp void		athn_usb_tx_status(void *, struct ieee80211_node *);
17413236e8dSdamien void		athn_usb_rx_wmi_ctrl(struct athn_usb_softc *, uint8_t *, int);
175ab0b1be7Smglocker void		athn_usb_intr(struct usbd_xfer *, void *,
17613236e8dSdamien 		    usbd_status);
17713236e8dSdamien void		athn_usb_rx_radiotap(struct athn_softc *, struct mbuf *,
17813236e8dSdamien 		    struct ar_rx_status *);
17913236e8dSdamien void		athn_usb_rx_frame(struct athn_usb_softc *, struct mbuf *);
180ab0b1be7Smglocker void		athn_usb_rxeof(struct usbd_xfer *, void *,
18113236e8dSdamien 		    usbd_status);
182ab0b1be7Smglocker void		athn_usb_txeof(struct usbd_xfer *, void *,
18313236e8dSdamien 		    usbd_status);
18413236e8dSdamien int		athn_usb_tx(struct athn_softc *, struct mbuf *,
18513236e8dSdamien 		    struct ieee80211_node *);
18613236e8dSdamien void		athn_usb_start(struct ifnet *);
18713236e8dSdamien void		athn_usb_watchdog(struct ifnet *);
18813236e8dSdamien int		athn_usb_ioctl(struct ifnet *, u_long, caddr_t);
18913236e8dSdamien int		athn_usb_init(struct ifnet *);
19013236e8dSdamien void		athn_usb_stop(struct ifnet *);
19175f8bb6dSdamien void		ar9271_load_ani(struct athn_softc *);
19213236e8dSdamien 
19313236e8dSdamien /* Shortcut. */
19413236e8dSdamien #define athn_usb_wmi_cmd(sc, cmd_id)	\
19513236e8dSdamien 	athn_usb_wmi_xcmd(sc, cmd_id, NULL, 0, NULL)
19613236e8dSdamien 
19713236e8dSdamien /* Extern functions. */
19813236e8dSdamien void		athn_led_init(struct athn_softc *);
1995632af28Sdamien void		athn_set_led(struct athn_softc *, int);
20013236e8dSdamien void		athn_btcoex_init(struct athn_softc *);
20113236e8dSdamien void		athn_set_rxfilter(struct athn_softc *, uint32_t);
20213236e8dSdamien int		athn_reset(struct athn_softc *, int);
20313236e8dSdamien void		athn_init_pll(struct athn_softc *,
20413236e8dSdamien 		    const struct ieee80211_channel *);
20513236e8dSdamien int		athn_set_power_awake(struct athn_softc *);
20613236e8dSdamien void		athn_set_power_sleep(struct athn_softc *);
20713236e8dSdamien void		athn_reset_key(struct athn_softc *, int);
20813236e8dSdamien int		athn_set_key(struct ieee80211com *, struct ieee80211_node *,
20913236e8dSdamien 		    struct ieee80211_key *);
21013236e8dSdamien void		athn_delete_key(struct ieee80211com *, struct ieee80211_node *,
21113236e8dSdamien 		    struct ieee80211_key *);
21213236e8dSdamien void		athn_rx_start(struct athn_softc *);
21313236e8dSdamien void		athn_set_sta_timers(struct athn_softc *);
21413236e8dSdamien void		athn_set_hostap_timers(struct athn_softc *);
21513236e8dSdamien void		athn_set_opmode(struct athn_softc *);
21613236e8dSdamien void		athn_set_bss(struct athn_softc *, struct ieee80211_node *);
21713236e8dSdamien int		athn_hw_reset(struct athn_softc *, struct ieee80211_channel *,
21813236e8dSdamien 		    struct ieee80211_channel *, int);
21913236e8dSdamien void		athn_updateedca(struct ieee80211com *);
22013236e8dSdamien void		athn_updateslot(struct ieee80211com *);
22113236e8dSdamien 
22213236e8dSdamien const struct cfattach athn_usb_ca = {
22313236e8dSdamien 	sizeof(struct athn_usb_softc),
22413236e8dSdamien 	athn_usb_match,
22513236e8dSdamien 	athn_usb_attach,
22653c6612dSmpi 	athn_usb_detach
22713236e8dSdamien };
22813236e8dSdamien 
22913236e8dSdamien int
23013236e8dSdamien athn_usb_match(struct device *parent, void *match, void *aux)
23113236e8dSdamien {
23213236e8dSdamien 	struct usb_attach_arg *uaa = aux;
23313236e8dSdamien 
2347ebc5b51Smpi 	if (uaa->iface == NULL || uaa->configno != 1)
23513236e8dSdamien 		return (UMATCH_NONE);
23613236e8dSdamien 
23713236e8dSdamien 	return ((athn_usb_lookup(uaa->vendor, uaa->product) != NULL) ?
2387ebc5b51Smpi 	    UMATCH_VENDOR_PRODUCT_CONF_IFACE : UMATCH_NONE);
23913236e8dSdamien }
24013236e8dSdamien 
24113236e8dSdamien void
24213236e8dSdamien athn_usb_attach(struct device *parent, struct device *self, void *aux)
24313236e8dSdamien {
24413236e8dSdamien 	struct athn_usb_softc *usc = (struct athn_usb_softc *)self;
24513236e8dSdamien 	struct athn_softc *sc = &usc->sc_sc;
24613236e8dSdamien 	struct usb_attach_arg *uaa = aux;
24713236e8dSdamien 
24813236e8dSdamien 	usc->sc_udev = uaa->device;
2497ebc5b51Smpi 	usc->sc_iface = uaa->iface;
25013236e8dSdamien 
25113236e8dSdamien 	usc->flags = athn_usb_lookup(uaa->vendor, uaa->product)->flags;
25213236e8dSdamien 	sc->flags |= ATHN_FLAG_USB;
25313236e8dSdamien #ifdef notyet
25413236e8dSdamien 	/* Check if it is a combo WiFi+Bluetooth (WB193) device. */
25513236e8dSdamien 	if (strncmp(product, "wb193", 5) == 0)
25613236e8dSdamien 		sc->flags |= ATHN_FLAG_BTCOEX3WIRE;
25713236e8dSdamien #endif
25813236e8dSdamien 
25913236e8dSdamien 	sc->ops.read = athn_usb_read;
26013236e8dSdamien 	sc->ops.write = athn_usb_write;
26113236e8dSdamien 	sc->ops.write_barrier = athn_usb_write_barrier;
26213236e8dSdamien 
26313236e8dSdamien 	usb_init_task(&usc->sc_task, athn_usb_task, sc, USB_TASK_TYPE_GENERIC);
26413236e8dSdamien 
26513236e8dSdamien 	if (athn_usb_open_pipes(usc) != 0)
26613236e8dSdamien 		return;
26713236e8dSdamien 
26813236e8dSdamien 	/* Allocate xfer for firmware commands. */
26913236e8dSdamien 	if (athn_usb_alloc_tx_cmd(usc) != 0)
27013236e8dSdamien 		return;
27113236e8dSdamien 
272ef89f9e6Smpi 	config_mountroot(self, athn_usb_attachhook);
27313236e8dSdamien }
27413236e8dSdamien 
27513236e8dSdamien int
27613236e8dSdamien athn_usb_detach(struct device *self, int flags)
27713236e8dSdamien {
27813236e8dSdamien 	struct athn_usb_softc *usc = (struct athn_usb_softc *)self;
27913236e8dSdamien 	struct athn_softc *sc = &usc->sc_sc;
28013236e8dSdamien 
281ffb60851Smikeb 	if (usc->sc_athn_attached)
28213236e8dSdamien 		athn_detach(sc);
28313236e8dSdamien 
28413236e8dSdamien 	/* Wait for all async commands to complete. */
28513236e8dSdamien 	athn_usb_wait_async(usc);
28613236e8dSdamien 
287dfa43ce4Sstsp 	usbd_ref_wait(usc->sc_udev);
288dfa43ce4Sstsp 
28913236e8dSdamien 	/* Abort and close Tx/Rx pipes. */
29013236e8dSdamien 	athn_usb_close_pipes(usc);
29113236e8dSdamien 
29213236e8dSdamien 	/* Free Tx/Rx buffers. */
29313236e8dSdamien 	athn_usb_free_tx_cmd(usc);
29413236e8dSdamien 	athn_usb_free_tx_list(usc);
29513236e8dSdamien 	athn_usb_free_rx_list(usc);
29613236e8dSdamien 
29713236e8dSdamien 	return (0);
29813236e8dSdamien }
29913236e8dSdamien 
30013236e8dSdamien void
301ef89f9e6Smpi athn_usb_attachhook(struct device *self)
30213236e8dSdamien {
303ef89f9e6Smpi 	struct athn_usb_softc *usc = (struct athn_usb_softc *)self;
30413236e8dSdamien 	struct athn_softc *sc = &usc->sc_sc;
30513236e8dSdamien 	struct athn_ops *ops = &sc->ops;
30613236e8dSdamien 	struct ieee80211com *ic = &sc->sc_ic;
30713236e8dSdamien 	struct ifnet *ifp = &ic->ic_if;
30813236e8dSdamien 	int s, i, error;
30913236e8dSdamien 
31013236e8dSdamien 	/* Load firmware. */
31113236e8dSdamien 	error = athn_usb_load_firmware(usc);
31213236e8dSdamien 	if (error != 0) {
31357392abcSstsp 		printf("%s: could not load firmware\n", sc->sc_dev.dv_xname);
31413236e8dSdamien 		return;
31513236e8dSdamien 	}
31613236e8dSdamien 
31713236e8dSdamien 	/* Setup the host transport communication interface. */
31813236e8dSdamien 	error = athn_usb_htc_setup(usc);
31913236e8dSdamien 	if (error != 0)
32013236e8dSdamien 		return;
32113236e8dSdamien 
32213236e8dSdamien 	/* We're now ready to attach the bus agnostic driver. */
32313236e8dSdamien 	s = splnet();
32413236e8dSdamien 	error = athn_attach(sc);
32513236e8dSdamien 	if (error != 0) {
32613236e8dSdamien 		splx(s);
32713236e8dSdamien 		return;
32813236e8dSdamien 	}
329ffb60851Smikeb 	usc->sc_athn_attached = 1;
33013236e8dSdamien 	/* Override some operations for USB. */
33113236e8dSdamien 	ifp->if_ioctl = athn_usb_ioctl;
33213236e8dSdamien 	ifp->if_start = athn_usb_start;
33313236e8dSdamien 	ifp->if_watchdog = athn_usb_watchdog;
334f47be805Sstsp 	ic->ic_node_alloc = athn_usb_node_alloc;
335f47be805Sstsp 	ic->ic_newauth = athn_usb_newauth;
33613236e8dSdamien 	ic->ic_newassoc = athn_usb_newassoc;
337f47be805Sstsp #ifndef IEEE80211_STA_ONLY
338f47be805Sstsp 	usc->sc_node_free = ic->ic_node_free;
339f47be805Sstsp 	ic->ic_node_free = athn_usb_node_free;
340f47be805Sstsp #endif
34113236e8dSdamien 	ic->ic_updateslot = athn_usb_updateslot;
34213236e8dSdamien 	ic->ic_updateedca = athn_usb_updateedca;
34313236e8dSdamien #ifdef notyet
34413236e8dSdamien 	ic->ic_set_key = athn_usb_set_key;
34513236e8dSdamien 	ic->ic_delete_key = athn_usb_delete_key;
34613236e8dSdamien 	ic->ic_ampdu_tx_start = athn_usb_ampdu_tx_start;
34713236e8dSdamien 	ic->ic_ampdu_tx_stop = athn_usb_ampdu_tx_stop;
34813236e8dSdamien #endif
34913236e8dSdamien 	ic->ic_newstate = athn_usb_newstate;
35013236e8dSdamien 	ic->ic_media.ifm_change = athn_usb_media_change;
351dd463f29Sstsp 	timeout_set(&sc->scan_to, athn_usb_next_scan, usc);
35213236e8dSdamien 
35313236e8dSdamien 	ops->rx_enable = athn_usb_rx_enable;
35413236e8dSdamien 	splx(s);
35513236e8dSdamien 
35613236e8dSdamien 	/* Reset HW key cache entries. */
35713236e8dSdamien 	for (i = 0; i < sc->kc_entries; i++)
35813236e8dSdamien 		athn_reset_key(sc, i);
35913236e8dSdamien 
36013236e8dSdamien 	ops->enable_antenna_diversity(sc);
36113236e8dSdamien 
36213236e8dSdamien #ifdef ATHN_BT_COEXISTENCE
36313236e8dSdamien 	/* Configure bluetooth coexistence for combo chips. */
36413236e8dSdamien 	if (sc->flags & ATHN_FLAG_BTCOEX)
36513236e8dSdamien 		athn_btcoex_init(sc);
36613236e8dSdamien #endif
36713236e8dSdamien 	/* Configure LED. */
36813236e8dSdamien 	athn_led_init(sc);
36913236e8dSdamien }
37013236e8dSdamien 
37113236e8dSdamien int
37213236e8dSdamien athn_usb_open_pipes(struct athn_usb_softc *usc)
37313236e8dSdamien {
37413236e8dSdamien 	usb_endpoint_descriptor_t *ed;
37513236e8dSdamien 	int isize, error;
37613236e8dSdamien 
37713236e8dSdamien 	error = usbd_open_pipe(usc->sc_iface, AR_PIPE_TX_DATA, 0,
37813236e8dSdamien 	    &usc->tx_data_pipe);
37913236e8dSdamien 	if (error != 0) {
38013236e8dSdamien 		printf("%s: could not open Tx bulk pipe\n",
38113236e8dSdamien 		    usc->usb_dev.dv_xname);
38213236e8dSdamien 		goto fail;
38313236e8dSdamien 	}
38413236e8dSdamien 
38513236e8dSdamien 	error = usbd_open_pipe(usc->sc_iface, AR_PIPE_RX_DATA, 0,
38613236e8dSdamien 	    &usc->rx_data_pipe);
38713236e8dSdamien 	if (error != 0) {
38813236e8dSdamien 		printf("%s: could not open Rx bulk pipe\n",
38913236e8dSdamien 		    usc->usb_dev.dv_xname);
39013236e8dSdamien 		goto fail;
39113236e8dSdamien 	}
39213236e8dSdamien 
39313236e8dSdamien 	ed = usbd_get_endpoint_descriptor(usc->sc_iface, AR_PIPE_RX_INTR);
39413236e8dSdamien 	if (ed == NULL) {
39513236e8dSdamien 		printf("%s: could not retrieve Rx intr pipe descriptor\n",
39613236e8dSdamien 		    usc->usb_dev.dv_xname);
39713236e8dSdamien 		goto fail;
39813236e8dSdamien 	}
39913236e8dSdamien 	isize = UGETW(ed->wMaxPacketSize);
40013236e8dSdamien 	if (isize == 0) {
40113236e8dSdamien 		printf("%s: invalid Rx intr pipe descriptor\n",
40213236e8dSdamien 		    usc->usb_dev.dv_xname);
40313236e8dSdamien 		goto fail;
40413236e8dSdamien 	}
40513236e8dSdamien 	usc->ibuf = malloc(isize, M_USBDEV, M_NOWAIT);
40613236e8dSdamien 	if (usc->ibuf == NULL) {
40713236e8dSdamien 		printf("%s: could not allocate Rx intr buffer\n",
40813236e8dSdamien 		    usc->usb_dev.dv_xname);
40913236e8dSdamien 		goto fail;
41013236e8dSdamien 	}
411234dfda1Sderaadt 	usc->ibuflen = isize;
41213236e8dSdamien 	error = usbd_open_pipe_intr(usc->sc_iface, AR_PIPE_RX_INTR,
41313236e8dSdamien 	    USBD_SHORT_XFER_OK, &usc->rx_intr_pipe, usc, usc->ibuf, isize,
41413236e8dSdamien 	    athn_usb_intr, USBD_DEFAULT_INTERVAL);
41513236e8dSdamien 	if (error != 0) {
41613236e8dSdamien 		printf("%s: could not open Rx intr pipe\n",
41713236e8dSdamien 		    usc->usb_dev.dv_xname);
41813236e8dSdamien 		goto fail;
41913236e8dSdamien 	}
42013236e8dSdamien 
42113236e8dSdamien 	error = usbd_open_pipe(usc->sc_iface, AR_PIPE_TX_INTR, 0,
42213236e8dSdamien 	    &usc->tx_intr_pipe);
42313236e8dSdamien 	if (error != 0) {
42413236e8dSdamien 		printf("%s: could not open Tx intr pipe\n",
42513236e8dSdamien 		    usc->usb_dev.dv_xname);
42613236e8dSdamien 		goto fail;
42713236e8dSdamien 	}
42813236e8dSdamien  fail:
42913236e8dSdamien 	if (error != 0)
43013236e8dSdamien 		athn_usb_close_pipes(usc);
43113236e8dSdamien 	return (error);
43213236e8dSdamien }
43313236e8dSdamien 
43413236e8dSdamien void
43513236e8dSdamien athn_usb_close_pipes(struct athn_usb_softc *usc)
43613236e8dSdamien {
4371b4b7f67Sstsp 	if (usc->tx_data_pipe != NULL) {
43813236e8dSdamien 		usbd_close_pipe(usc->tx_data_pipe);
4391b4b7f67Sstsp 		usc->tx_data_pipe = NULL;
44013236e8dSdamien 	}
4411b4b7f67Sstsp 	if (usc->rx_data_pipe != NULL) {
4421b4b7f67Sstsp 		usbd_close_pipe(usc->rx_data_pipe);
4431b4b7f67Sstsp 		usc->rx_data_pipe = NULL;
4441b4b7f67Sstsp 	}
4451b4b7f67Sstsp 	if (usc->tx_intr_pipe != NULL) {
4461b4b7f67Sstsp 		usbd_close_pipe(usc->tx_intr_pipe);
4471b4b7f67Sstsp 		usc->tx_intr_pipe = NULL;
4481b4b7f67Sstsp 	}
4491b4b7f67Sstsp 	if (usc->rx_intr_pipe != NULL) {
4501b4b7f67Sstsp 		usbd_close_pipe(usc->rx_intr_pipe);
4511b4b7f67Sstsp 		usc->rx_intr_pipe = NULL;
4521b4b7f67Sstsp 	}
4531b4b7f67Sstsp 	if (usc->ibuf != NULL) {
454234dfda1Sderaadt 		free(usc->ibuf, M_USBDEV, usc->ibuflen);
4551b4b7f67Sstsp 		usc->ibuf = NULL;
4561b4b7f67Sstsp 	}
45713236e8dSdamien }
45813236e8dSdamien 
45913236e8dSdamien int
46013236e8dSdamien athn_usb_alloc_rx_list(struct athn_usb_softc *usc)
46113236e8dSdamien {
46213236e8dSdamien 	struct athn_usb_rx_data *data;
46313236e8dSdamien 	int i, error = 0;
46413236e8dSdamien 
46513236e8dSdamien 	for (i = 0; i < ATHN_USB_RX_LIST_COUNT; i++) {
46613236e8dSdamien 		data = &usc->rx_data[i];
46713236e8dSdamien 
46813236e8dSdamien 		data->sc = usc;	/* Backpointer for callbacks. */
46913236e8dSdamien 
47013236e8dSdamien 		data->xfer = usbd_alloc_xfer(usc->sc_udev);
47113236e8dSdamien 		if (data->xfer == NULL) {
47213236e8dSdamien 			printf("%s: could not allocate xfer\n",
47313236e8dSdamien 			    usc->usb_dev.dv_xname);
47413236e8dSdamien 			error = ENOMEM;
47513236e8dSdamien 			break;
47613236e8dSdamien 		}
47713236e8dSdamien 		data->buf = usbd_alloc_buffer(data->xfer, ATHN_USB_RXBUFSZ);
47813236e8dSdamien 		if (data->buf == NULL) {
47913236e8dSdamien 			printf("%s: could not allocate xfer buffer\n",
48013236e8dSdamien 			    usc->usb_dev.dv_xname);
48113236e8dSdamien 			error = ENOMEM;
48213236e8dSdamien 			break;
48313236e8dSdamien 		}
48413236e8dSdamien 	}
48513236e8dSdamien 	if (error != 0)
48613236e8dSdamien 		athn_usb_free_rx_list(usc);
48713236e8dSdamien 	return (error);
48813236e8dSdamien }
48913236e8dSdamien 
49013236e8dSdamien void
49113236e8dSdamien athn_usb_free_rx_list(struct athn_usb_softc *usc)
49213236e8dSdamien {
49313236e8dSdamien 	int i;
49413236e8dSdamien 
49513236e8dSdamien 	/* NB: Caller must abort pipe first. */
49613236e8dSdamien 	for (i = 0; i < ATHN_USB_RX_LIST_COUNT; i++) {
49713236e8dSdamien 		if (usc->rx_data[i].xfer != NULL)
49813236e8dSdamien 			usbd_free_xfer(usc->rx_data[i].xfer);
49913236e8dSdamien 		usc->rx_data[i].xfer = NULL;
50013236e8dSdamien 	}
50113236e8dSdamien }
50213236e8dSdamien 
50313236e8dSdamien int
50413236e8dSdamien athn_usb_alloc_tx_list(struct athn_usb_softc *usc)
50513236e8dSdamien {
50613236e8dSdamien 	struct athn_usb_tx_data *data;
50713236e8dSdamien 	int i, error = 0;
50813236e8dSdamien 
50913236e8dSdamien 	TAILQ_INIT(&usc->tx_free_list);
51013236e8dSdamien 	for (i = 0; i < ATHN_USB_TX_LIST_COUNT; i++) {
51113236e8dSdamien 		data = &usc->tx_data[i];
51213236e8dSdamien 
51313236e8dSdamien 		data->sc = usc;	/* Backpointer for callbacks. */
51413236e8dSdamien 
51513236e8dSdamien 		data->xfer = usbd_alloc_xfer(usc->sc_udev);
51613236e8dSdamien 		if (data->xfer == NULL) {
51713236e8dSdamien 			printf("%s: could not allocate xfer\n",
51813236e8dSdamien 			    usc->usb_dev.dv_xname);
51913236e8dSdamien 			error = ENOMEM;
52013236e8dSdamien 			break;
52113236e8dSdamien 		}
52213236e8dSdamien 		data->buf = usbd_alloc_buffer(data->xfer, ATHN_USB_TXBUFSZ);
52313236e8dSdamien 		if (data->buf == NULL) {
52413236e8dSdamien 			printf("%s: could not allocate xfer buffer\n",
52513236e8dSdamien 			    usc->usb_dev.dv_xname);
52613236e8dSdamien 			error = ENOMEM;
52713236e8dSdamien 			break;
52813236e8dSdamien 		}
52913236e8dSdamien 		/* Append this Tx buffer to our free list. */
53013236e8dSdamien 		TAILQ_INSERT_TAIL(&usc->tx_free_list, data, next);
53113236e8dSdamien 	}
53213236e8dSdamien 	if (error != 0)
53313236e8dSdamien 		athn_usb_free_tx_list(usc);
53413236e8dSdamien 	return (error);
53513236e8dSdamien }
53613236e8dSdamien 
53713236e8dSdamien void
53813236e8dSdamien athn_usb_free_tx_list(struct athn_usb_softc *usc)
53913236e8dSdamien {
54013236e8dSdamien 	int i;
54113236e8dSdamien 
54213236e8dSdamien 	/* NB: Caller must abort pipe first. */
54313236e8dSdamien 	for (i = 0; i < ATHN_USB_TX_LIST_COUNT; i++) {
54413236e8dSdamien 		if (usc->tx_data[i].xfer != NULL)
54513236e8dSdamien 			usbd_free_xfer(usc->tx_data[i].xfer);
54613236e8dSdamien 		usc->tx_data[i].xfer = NULL;
54713236e8dSdamien 	}
54813236e8dSdamien }
54913236e8dSdamien 
55013236e8dSdamien int
55113236e8dSdamien athn_usb_alloc_tx_cmd(struct athn_usb_softc *usc)
55213236e8dSdamien {
55313236e8dSdamien 	struct athn_usb_tx_data *data = &usc->tx_cmd;
55413236e8dSdamien 
55513236e8dSdamien 	data->sc = usc;	/* Backpointer for callbacks. */
55613236e8dSdamien 
55713236e8dSdamien 	data->xfer = usbd_alloc_xfer(usc->sc_udev);
55813236e8dSdamien 	if (data->xfer == NULL) {
55913236e8dSdamien 		printf("%s: could not allocate xfer\n",
56013236e8dSdamien 		    usc->usb_dev.dv_xname);
56113236e8dSdamien 		return (ENOMEM);
56213236e8dSdamien 	}
56313236e8dSdamien 	data->buf = usbd_alloc_buffer(data->xfer, ATHN_USB_TXCMDSZ);
56413236e8dSdamien 	if (data->buf == NULL) {
56513236e8dSdamien 		printf("%s: could not allocate xfer buffer\n",
56613236e8dSdamien 		    usc->usb_dev.dv_xname);
56713236e8dSdamien 		return (ENOMEM);
56813236e8dSdamien 	}
56913236e8dSdamien 	return (0);
57013236e8dSdamien }
57113236e8dSdamien 
57213236e8dSdamien void
57313236e8dSdamien athn_usb_free_tx_cmd(struct athn_usb_softc *usc)
57413236e8dSdamien {
57513236e8dSdamien 	if (usc->tx_cmd.xfer != NULL)
57613236e8dSdamien 		usbd_free_xfer(usc->tx_cmd.xfer);
57713236e8dSdamien 	usc->tx_cmd.xfer = NULL;
57813236e8dSdamien }
57913236e8dSdamien 
58013236e8dSdamien void
58113236e8dSdamien athn_usb_task(void *arg)
58213236e8dSdamien {
58313236e8dSdamien 	struct athn_usb_softc *usc = arg;
58413236e8dSdamien 	struct athn_usb_host_cmd_ring *ring = &usc->cmdq;
58513236e8dSdamien 	struct athn_usb_host_cmd *cmd;
58613236e8dSdamien 	int s;
58713236e8dSdamien 
58813236e8dSdamien 	/* Process host commands. */
58913236e8dSdamien 	s = splusb();
59013236e8dSdamien 	while (ring->next != ring->cur) {
59113236e8dSdamien 		cmd = &ring->cmd[ring->next];
59213236e8dSdamien 		splx(s);
59313236e8dSdamien 		/* Invoke callback. */
59413236e8dSdamien 		cmd->cb(usc, cmd->data);
59513236e8dSdamien 		s = splusb();
59613236e8dSdamien 		ring->queued--;
59713236e8dSdamien 		ring->next = (ring->next + 1) % ATHN_USB_HOST_CMD_RING_COUNT;
59813236e8dSdamien 	}
59913236e8dSdamien 	splx(s);
60013236e8dSdamien }
60113236e8dSdamien 
60213236e8dSdamien void
60313236e8dSdamien athn_usb_do_async(struct athn_usb_softc *usc,
60413236e8dSdamien     void (*cb)(struct athn_usb_softc *, void *), void *arg, int len)
60513236e8dSdamien {
60613236e8dSdamien 	struct athn_usb_host_cmd_ring *ring = &usc->cmdq;
60713236e8dSdamien 	struct athn_usb_host_cmd *cmd;
60813236e8dSdamien 	int s;
60913236e8dSdamien 
610d25f2410Sstsp 	if (ring->queued == ATHN_USB_HOST_CMD_RING_COUNT) {
611d25f2410Sstsp 		printf("%s: host cmd queue overrun\n", usc->usb_dev.dv_xname);
61213236e8dSdamien 		return;	/* XXX */
613d25f2410Sstsp 	}
614d25f2410Sstsp 
61513236e8dSdamien 	s = splusb();
61613236e8dSdamien 	cmd = &ring->cmd[ring->cur];
61713236e8dSdamien 	cmd->cb = cb;
61813236e8dSdamien 	KASSERT(len <= sizeof(cmd->data));
61913236e8dSdamien 	memcpy(cmd->data, arg, len);
62013236e8dSdamien 	ring->cur = (ring->cur + 1) % ATHN_USB_HOST_CMD_RING_COUNT;
62113236e8dSdamien 
62213236e8dSdamien 	/* If there is no pending command already, schedule a task. */
62313236e8dSdamien 	if (++ring->queued == 1)
62413236e8dSdamien 		usb_add_task(usc->sc_udev, &usc->sc_task);
62513236e8dSdamien 	splx(s);
62613236e8dSdamien }
62713236e8dSdamien 
62813236e8dSdamien void
62913236e8dSdamien athn_usb_wait_async(struct athn_usb_softc *usc)
63013236e8dSdamien {
63113236e8dSdamien 	/* Wait for all queued asynchronous commands to complete. */
632d25f2410Sstsp 	usb_wait_task(usc->sc_udev, &usc->sc_task);
63313236e8dSdamien }
63413236e8dSdamien 
63513236e8dSdamien int
63613236e8dSdamien athn_usb_load_firmware(struct athn_usb_softc *usc)
63713236e8dSdamien {
63813236e8dSdamien 	usb_device_descriptor_t *dd;
63913236e8dSdamien 	usb_device_request_t req;
64013236e8dSdamien 	const char *name;
64113236e8dSdamien 	u_char *fw, *ptr;
642c9ee9455Sderaadt 	size_t fwsize, size;
64313236e8dSdamien 	uint32_t addr;
64413236e8dSdamien 	int s, mlen, error;
64513236e8dSdamien 
64613236e8dSdamien 	/* Determine which firmware image to load. */
64713236e8dSdamien 	if (usc->flags & ATHN_USB_FLAG_AR7010) {
64813236e8dSdamien 		dd = usbd_get_device_descriptor(usc->sc_udev);
649f47be805Sstsp 		name = "athn-open-ar7010";
65013236e8dSdamien 	} else
651f47be805Sstsp 		name = "athn-open-ar9271";
65213236e8dSdamien 	/* Read firmware image from the filesystem. */
653c9ee9455Sderaadt 	if ((error = loadfirmware(name, &fw, &fwsize)) != 0) {
65413236e8dSdamien 		printf("%s: failed loadfirmware of file %s (error %d)\n",
65513236e8dSdamien 		    usc->usb_dev.dv_xname, name, error);
65613236e8dSdamien 		return (error);
65713236e8dSdamien 	}
65813236e8dSdamien 	/* Load firmware image. */
65913236e8dSdamien 	ptr = fw;
66013236e8dSdamien 	addr = AR9271_FIRMWARE >> 8;
66113236e8dSdamien 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
66213236e8dSdamien 	req.bRequest = AR_FW_DOWNLOAD;
66313236e8dSdamien 	USETW(req.wIndex, 0);
664c9ee9455Sderaadt 	size = fwsize;
66513236e8dSdamien 	while (size > 0) {
66613236e8dSdamien 		mlen = MIN(size, 4096);
66713236e8dSdamien 
66813236e8dSdamien 		USETW(req.wValue, addr);
66913236e8dSdamien 		USETW(req.wLength, mlen);
67013236e8dSdamien 		error = usbd_do_request(usc->sc_udev, &req, ptr);
67113236e8dSdamien 		if (error != 0) {
672c9ee9455Sderaadt 			free(fw, M_DEVBUF, fwsize);
67313236e8dSdamien 			return (error);
67413236e8dSdamien 		}
67513236e8dSdamien 		addr += mlen >> 8;
67613236e8dSdamien 		ptr  += mlen;
67713236e8dSdamien 		size -= mlen;
67813236e8dSdamien 	}
679c9ee9455Sderaadt 	free(fw, M_DEVBUF, fwsize);
68013236e8dSdamien 
68113236e8dSdamien 	/* Start firmware. */
68213236e8dSdamien 	if (usc->flags & ATHN_USB_FLAG_AR7010)
68313236e8dSdamien 		addr = AR7010_FIRMWARE_TEXT >> 8;
68413236e8dSdamien 	else
68513236e8dSdamien 		addr = AR9271_FIRMWARE_TEXT >> 8;
68613236e8dSdamien 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
68713236e8dSdamien 	req.bRequest = AR_FW_DOWNLOAD_COMP;
68813236e8dSdamien 	USETW(req.wIndex, 0);
68913236e8dSdamien 	USETW(req.wValue, addr);
69013236e8dSdamien 	USETW(req.wLength, 0);
69113236e8dSdamien 	s = splusb();
69213236e8dSdamien 	usc->wait_msg_id = AR_HTC_MSG_READY;
69313236e8dSdamien 	error = usbd_do_request(usc->sc_udev, &req, NULL);
69457392abcSstsp 	/* Wait at most 1 second for firmware to boot. */
69513236e8dSdamien 	if (error == 0 && usc->wait_msg_id != 0)
69657392abcSstsp 		error = tsleep(&usc->wait_msg_id, 0, "athnfw", hz);
69713236e8dSdamien 	usc->wait_msg_id = 0;
69813236e8dSdamien 	splx(s);
69913236e8dSdamien 	return (error);
70013236e8dSdamien }
70113236e8dSdamien 
70213236e8dSdamien int
70313236e8dSdamien athn_usb_htc_msg(struct athn_usb_softc *usc, uint16_t msg_id, void *buf,
70413236e8dSdamien     int len)
70513236e8dSdamien {
70613236e8dSdamien 	struct athn_usb_tx_data *data = &usc->tx_cmd;
70713236e8dSdamien 	struct ar_htc_frame_hdr *htc;
70813236e8dSdamien 	struct ar_htc_msg_hdr *msg;
70913236e8dSdamien 
71013236e8dSdamien 	htc = (struct ar_htc_frame_hdr *)data->buf;
71113236e8dSdamien 	memset(htc, 0, sizeof(*htc));
71213236e8dSdamien 	htc->endpoint_id = 0;
71313236e8dSdamien 	htc->payload_len = htobe16(sizeof(*msg) + len);
71413236e8dSdamien 
71513236e8dSdamien 	msg = (struct ar_htc_msg_hdr *)&htc[1];
71613236e8dSdamien 	msg->msg_id = htobe16(msg_id);
71713236e8dSdamien 
71813236e8dSdamien 	memcpy(&msg[1], buf, len);
71913236e8dSdamien 
72013236e8dSdamien 	usbd_setup_xfer(data->xfer, usc->tx_intr_pipe, NULL, data->buf,
72113236e8dSdamien 	    sizeof(*htc) + sizeof(*msg) + len,
722aa88c704Smpi 	    USBD_SHORT_XFER_OK | USBD_NO_COPY | USBD_SYNCHRONOUS,
723aa88c704Smpi 	    ATHN_USB_CMD_TIMEOUT, NULL);
724aa88c704Smpi 	return (usbd_transfer(data->xfer));
72513236e8dSdamien }
72613236e8dSdamien 
72713236e8dSdamien int
72813236e8dSdamien athn_usb_htc_setup(struct athn_usb_softc *usc)
72913236e8dSdamien {
73013236e8dSdamien 	struct ar_htc_msg_config_pipe cfg;
73113236e8dSdamien 	int s, error;
73213236e8dSdamien 
73313236e8dSdamien 	/*
73413236e8dSdamien 	 * Connect WMI services to USB pipes.
73513236e8dSdamien 	 */
73613236e8dSdamien 	error = athn_usb_htc_connect_svc(usc, AR_SVC_WMI_CONTROL,
73713236e8dSdamien 	    AR_PIPE_TX_INTR, AR_PIPE_RX_INTR, &usc->ep_ctrl);
73813236e8dSdamien 	if (error != 0)
73913236e8dSdamien 		return (error);
74013236e8dSdamien 	error = athn_usb_htc_connect_svc(usc, AR_SVC_WMI_BEACON,
74113236e8dSdamien 	    AR_PIPE_TX_DATA, AR_PIPE_RX_DATA, &usc->ep_bcn);
74213236e8dSdamien 	if (error != 0)
74313236e8dSdamien 		return (error);
74413236e8dSdamien 	error = athn_usb_htc_connect_svc(usc, AR_SVC_WMI_CAB,
74513236e8dSdamien 	    AR_PIPE_TX_DATA, AR_PIPE_RX_DATA, &usc->ep_cab);
74613236e8dSdamien 	if (error != 0)
74713236e8dSdamien 		return (error);
74813236e8dSdamien 	error = athn_usb_htc_connect_svc(usc, AR_SVC_WMI_UAPSD,
74913236e8dSdamien 	    AR_PIPE_TX_DATA, AR_PIPE_RX_DATA, &usc->ep_uapsd);
75013236e8dSdamien 	if (error != 0)
75113236e8dSdamien 		return (error);
75213236e8dSdamien 	error = athn_usb_htc_connect_svc(usc, AR_SVC_WMI_MGMT,
75313236e8dSdamien 	    AR_PIPE_TX_DATA, AR_PIPE_RX_DATA, &usc->ep_mgmt);
75413236e8dSdamien 	if (error != 0)
75513236e8dSdamien 		return (error);
75613236e8dSdamien 	error = athn_usb_htc_connect_svc(usc, AR_SVC_WMI_DATA_BE,
75713236e8dSdamien 	    AR_PIPE_TX_DATA, AR_PIPE_RX_DATA, &usc->ep_data[EDCA_AC_BE]);
75813236e8dSdamien 	if (error != 0)
75913236e8dSdamien 		return (error);
76013236e8dSdamien 	error = athn_usb_htc_connect_svc(usc, AR_SVC_WMI_DATA_BK,
76113236e8dSdamien 	    AR_PIPE_TX_DATA, AR_PIPE_RX_DATA, &usc->ep_data[EDCA_AC_BK]);
76213236e8dSdamien 	if (error != 0)
76313236e8dSdamien 		return (error);
76413236e8dSdamien 	error = athn_usb_htc_connect_svc(usc, AR_SVC_WMI_DATA_VI,
76513236e8dSdamien 	    AR_PIPE_TX_DATA, AR_PIPE_RX_DATA, &usc->ep_data[EDCA_AC_VI]);
76613236e8dSdamien 	if (error != 0)
76713236e8dSdamien 		return (error);
76813236e8dSdamien 	error = athn_usb_htc_connect_svc(usc, AR_SVC_WMI_DATA_VO,
76913236e8dSdamien 	    AR_PIPE_TX_DATA, AR_PIPE_RX_DATA, &usc->ep_data[EDCA_AC_VO]);
77013236e8dSdamien 	if (error != 0)
77113236e8dSdamien 		return (error);
77213236e8dSdamien 
77313236e8dSdamien 	/* Set credits for WLAN Tx pipe. */
77413236e8dSdamien 	memset(&cfg, 0, sizeof(cfg));
77513236e8dSdamien 	cfg.pipe_id = UE_GET_ADDR(AR_PIPE_TX_DATA);
77613236e8dSdamien 	cfg.credits = (usc->flags & ATHN_USB_FLAG_AR7010) ? 45 : 33;
77713236e8dSdamien 	s = splusb();
77813236e8dSdamien 	usc->wait_msg_id = AR_HTC_MSG_CONF_PIPE_RSP;
77913236e8dSdamien 	error = athn_usb_htc_msg(usc, AR_HTC_MSG_CONF_PIPE, &cfg, sizeof(cfg));
78013236e8dSdamien 	if (error == 0 && usc->wait_msg_id != 0)
78113236e8dSdamien 		error = tsleep(&usc->wait_msg_id, 0, "athnhtc", hz);
78213236e8dSdamien 	usc->wait_msg_id = 0;
78313236e8dSdamien 	splx(s);
78413236e8dSdamien 	if (error != 0) {
78513236e8dSdamien 		printf("%s: could not configure pipe\n",
78613236e8dSdamien 		    usc->usb_dev.dv_xname);
78713236e8dSdamien 		return (error);
78813236e8dSdamien 	}
78913236e8dSdamien 
79013236e8dSdamien 	error = athn_usb_htc_msg(usc, AR_HTC_MSG_SETUP_COMPLETE, NULL, 0);
79113236e8dSdamien 	if (error != 0) {
79213236e8dSdamien 		printf("%s: could not complete setup\n",
79313236e8dSdamien 		    usc->usb_dev.dv_xname);
79413236e8dSdamien 		return (error);
79513236e8dSdamien 	}
79613236e8dSdamien 	return (0);
79713236e8dSdamien }
79813236e8dSdamien 
79913236e8dSdamien int
80013236e8dSdamien athn_usb_htc_connect_svc(struct athn_usb_softc *usc, uint16_t svc_id,
80113236e8dSdamien     uint8_t ul_pipe, uint8_t dl_pipe, uint8_t *endpoint_id)
80213236e8dSdamien {
80313236e8dSdamien 	struct ar_htc_msg_conn_svc msg;
80413236e8dSdamien 	struct ar_htc_msg_conn_svc_rsp rsp;
80513236e8dSdamien 	int s, error;
80613236e8dSdamien 
80713236e8dSdamien 	memset(&msg, 0, sizeof(msg));
80813236e8dSdamien 	msg.svc_id = htobe16(svc_id);
80913236e8dSdamien 	msg.dl_pipeid = UE_GET_ADDR(dl_pipe);
81013236e8dSdamien 	msg.ul_pipeid = UE_GET_ADDR(ul_pipe);
81113236e8dSdamien 	s = splusb();
81213236e8dSdamien 	usc->msg_conn_svc_rsp = &rsp;
81313236e8dSdamien 	usc->wait_msg_id = AR_HTC_MSG_CONN_SVC_RSP;
81413236e8dSdamien 	error = athn_usb_htc_msg(usc, AR_HTC_MSG_CONN_SVC, &msg, sizeof(msg));
81513236e8dSdamien 	/* Wait at most 1 second for response. */
81613236e8dSdamien 	if (error == 0 && usc->wait_msg_id != 0)
81713236e8dSdamien 		error = tsleep(&usc->wait_msg_id, 0, "athnhtc", hz);
81813236e8dSdamien 	usc->wait_msg_id = 0;
81913236e8dSdamien 	splx(s);
82013236e8dSdamien 	if (error != 0) {
82113236e8dSdamien 		printf("%s: error waiting for service %d connection\n",
82213236e8dSdamien 		    usc->usb_dev.dv_xname, svc_id);
82313236e8dSdamien 		return (error);
82413236e8dSdamien 	}
82513236e8dSdamien 	if (rsp.status != AR_HTC_SVC_SUCCESS) {
82613236e8dSdamien 		printf("%s: service %d connection failed, error %d\n",
82713236e8dSdamien 		    usc->usb_dev.dv_xname, svc_id, rsp.status);
82813236e8dSdamien 		return (EIO);
82913236e8dSdamien 	}
83013236e8dSdamien 	DPRINTF(("service %d successfully connected to endpoint %d\n",
83113236e8dSdamien 	    svc_id, rsp.endpoint_id));
83213236e8dSdamien 
83313236e8dSdamien 	/* Return endpoint id. */
83413236e8dSdamien 	*endpoint_id = rsp.endpoint_id;
83513236e8dSdamien 	return (0);
83613236e8dSdamien }
83713236e8dSdamien 
83813236e8dSdamien int
83913236e8dSdamien athn_usb_wmi_xcmd(struct athn_usb_softc *usc, uint16_t cmd_id, void *ibuf,
84013236e8dSdamien     int ilen, void *obuf)
84113236e8dSdamien {
84213236e8dSdamien 	struct athn_usb_tx_data *data = &usc->tx_cmd;
84313236e8dSdamien 	struct ar_htc_frame_hdr *htc;
84413236e8dSdamien 	struct ar_wmi_cmd_hdr *wmi;
84513236e8dSdamien 	int s, error;
84613236e8dSdamien 
847d25f2410Sstsp 	if (usbd_is_dying(usc->sc_udev))
848d25f2410Sstsp 		return ENXIO;
849d25f2410Sstsp 
850d25f2410Sstsp 	s = splusb();
851d25f2410Sstsp 	while (usc->wait_cmd_id) {
852d25f2410Sstsp 		/*
853d25f2410Sstsp 		 * The previous USB transfer is not done yet. We can't use
854d25f2410Sstsp 		 * data->xfer until it is done or we'll cause major confusion
855d25f2410Sstsp 		 * in the USB stack.
856d25f2410Sstsp 		 */
857d25f2410Sstsp 		tsleep(&usc->wait_cmd_id, 0, "athnwmx", ATHN_USB_CMD_TIMEOUT);
858d25f2410Sstsp 		if (usbd_is_dying(usc->sc_udev)) {
859d25f2410Sstsp 			splx(s);
860d25f2410Sstsp 			return ENXIO;
861d25f2410Sstsp 		}
862d25f2410Sstsp 	}
863d25f2410Sstsp 	splx(s);
864d25f2410Sstsp 
86513236e8dSdamien 	htc = (struct ar_htc_frame_hdr *)data->buf;
86613236e8dSdamien 	memset(htc, 0, sizeof(*htc));
86713236e8dSdamien 	htc->endpoint_id = usc->ep_ctrl;
86813236e8dSdamien 	htc->payload_len = htobe16(sizeof(*wmi) + ilen);
86913236e8dSdamien 
87013236e8dSdamien 	wmi = (struct ar_wmi_cmd_hdr *)&htc[1];
87113236e8dSdamien 	wmi->cmd_id = htobe16(cmd_id);
87213236e8dSdamien 	usc->wmi_seq_no++;
87313236e8dSdamien 	wmi->seq_no = htobe16(usc->wmi_seq_no);
87413236e8dSdamien 
87513236e8dSdamien 	memcpy(&wmi[1], ibuf, ilen);
87613236e8dSdamien 
877d25f2410Sstsp 	usbd_setup_xfer(data->xfer, usc->tx_intr_pipe, NULL, data->buf,
87813236e8dSdamien 	    sizeof(*htc) + sizeof(*wmi) + ilen,
87913236e8dSdamien 	    USBD_SHORT_XFER_OK | USBD_NO_COPY, ATHN_USB_CMD_TIMEOUT,
880d25f2410Sstsp 	    NULL);
88113236e8dSdamien 	s = splusb();
88213236e8dSdamien 	error = usbd_transfer(data->xfer);
88313236e8dSdamien 	if (__predict_false(error != USBD_IN_PROGRESS && error != 0)) {
88413236e8dSdamien 		splx(s);
88513236e8dSdamien 		return (error);
88613236e8dSdamien 	}
88713236e8dSdamien 	usc->obuf = obuf;
88813236e8dSdamien 	usc->wait_cmd_id = cmd_id;
889d25f2410Sstsp 	/*
890d25f2410Sstsp 	 * Wait for WMI command complete interrupt. In case it does not fire
891d25f2410Sstsp 	 * wait until the USB transfer times out to avoid racing the transfer.
892d25f2410Sstsp 	 */
893d25f2410Sstsp 	error = tsleep(&usc->wait_cmd_id, 0, "athnwmi", ATHN_USB_CMD_TIMEOUT);
894d25f2410Sstsp 	if (error) {
895d25f2410Sstsp 		if (error == EWOULDBLOCK) {
89620592b50Sstsp 			printf("%s: firmware command 0x%x timed out\n",
897d25f2410Sstsp 			    usc->usb_dev.dv_xname, cmd_id);
898d25f2410Sstsp 			error = ETIMEDOUT;
899d25f2410Sstsp 		}
900d25f2410Sstsp 	}
901d25f2410Sstsp 
902d25f2410Sstsp 	/*
903d25f2410Sstsp 	 * Both the WMI command and transfer are done or have timed out.
904d25f2410Sstsp 	 * Allow other threads to enter this function and use data->xfer.
905d25f2410Sstsp 	 */
90613236e8dSdamien 	usc->wait_cmd_id = 0;
907d25f2410Sstsp 	wakeup(&usc->wait_cmd_id);
908d25f2410Sstsp 
90913236e8dSdamien 	splx(s);
91013236e8dSdamien 	return (error);
91113236e8dSdamien }
91213236e8dSdamien 
91313236e8dSdamien int
91413236e8dSdamien athn_usb_read_rom(struct athn_softc *sc)
91513236e8dSdamien {
91613236e8dSdamien 	struct athn_usb_softc *usc = (struct athn_usb_softc *)sc;
91713236e8dSdamien 	uint32_t addrs[8], vals[8], addr;
91813236e8dSdamien 	uint16_t *eep;
91913236e8dSdamien 	int i, j, error;
92013236e8dSdamien 
92113236e8dSdamien 	/* Read EEPROM by blocks of 16 bytes. */
92213236e8dSdamien 	eep = sc->eep;
92313236e8dSdamien 	addr = AR_EEPROM_OFFSET(sc->eep_base);
92413236e8dSdamien 	for (i = 0; i < sc->eep_size / 16; i++) {
92513236e8dSdamien 		for (j = 0; j < 8; j++, addr += 4)
92613236e8dSdamien 			addrs[j] = htobe32(addr);
92713236e8dSdamien 		error = athn_usb_wmi_xcmd(usc, AR_WMI_CMD_REG_READ,
92813236e8dSdamien 		    addrs, sizeof(addrs), vals);
92913236e8dSdamien 		if (error != 0)
93013236e8dSdamien 			break;
93113236e8dSdamien 		for (j = 0; j < 8; j++)
93213236e8dSdamien 			*eep++ = betoh32(vals[j]);
93313236e8dSdamien 	}
93413236e8dSdamien 	return (error);
93513236e8dSdamien }
93613236e8dSdamien 
93713236e8dSdamien uint32_t
93813236e8dSdamien athn_usb_read(struct athn_softc *sc, uint32_t addr)
93913236e8dSdamien {
94013236e8dSdamien 	struct athn_usb_softc *usc = (struct athn_usb_softc *)sc;
94113236e8dSdamien 	uint32_t val;
94213236e8dSdamien 	int error;
94313236e8dSdamien 
94413236e8dSdamien 	/* Flush pending writes for strict consistency. */
94513236e8dSdamien 	athn_usb_write_barrier(sc);
94613236e8dSdamien 
94713236e8dSdamien 	addr = htobe32(addr);
94813236e8dSdamien 	error = athn_usb_wmi_xcmd(usc, AR_WMI_CMD_REG_READ,
94913236e8dSdamien 	    &addr, sizeof(addr), &val);
95013236e8dSdamien 	if (error != 0)
95113236e8dSdamien 		return (0xdeadbeef);
95213236e8dSdamien 	return (betoh32(val));
95313236e8dSdamien }
95413236e8dSdamien 
95513236e8dSdamien void
95613236e8dSdamien athn_usb_write(struct athn_softc *sc, uint32_t addr, uint32_t val)
95713236e8dSdamien {
95813236e8dSdamien 	struct athn_usb_softc *usc = (struct athn_usb_softc *)sc;
95913236e8dSdamien 
96013236e8dSdamien 	usc->wbuf[usc->wcount].addr = htobe32(addr);
96113236e8dSdamien 	usc->wbuf[usc->wcount].val  = htobe32(val);
96213236e8dSdamien 	if (++usc->wcount == AR_MAX_WRITE_COUNT)
96313236e8dSdamien 		athn_usb_write_barrier(sc);
96413236e8dSdamien }
96513236e8dSdamien 
96613236e8dSdamien void
96713236e8dSdamien athn_usb_write_barrier(struct athn_softc *sc)
96813236e8dSdamien {
96913236e8dSdamien 	struct athn_usb_softc *usc = (struct athn_usb_softc *)sc;
97013236e8dSdamien 
97113236e8dSdamien 	if (usc->wcount == 0)
97213236e8dSdamien 		return;	/* Nothing to write. */
97313236e8dSdamien 
97413236e8dSdamien 	(void)athn_usb_wmi_xcmd(usc, AR_WMI_CMD_REG_WRITE,
97513236e8dSdamien 	    usc->wbuf, usc->wcount * sizeof(usc->wbuf[0]), NULL);
97613236e8dSdamien 	usc->wcount = 0;	/* Always flush buffer. */
97713236e8dSdamien }
97813236e8dSdamien 
97913236e8dSdamien int
98013236e8dSdamien athn_usb_media_change(struct ifnet *ifp)
98113236e8dSdamien {
982dfa43ce4Sstsp 	struct athn_usb_softc *usc = (struct athn_usb_softc *)ifp->if_softc;
98313236e8dSdamien 	int error;
98413236e8dSdamien 
985dfa43ce4Sstsp 	if (usbd_is_dying(usc->sc_udev))
986dfa43ce4Sstsp 		return ENXIO;
987dfa43ce4Sstsp 
98813236e8dSdamien 	error = ieee80211_media_change(ifp);
98913236e8dSdamien 	if (error != ENETRESET)
99013236e8dSdamien 		return (error);
99113236e8dSdamien 
99213236e8dSdamien 	if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
99313236e8dSdamien 	    (IFF_UP | IFF_RUNNING)) {
99413236e8dSdamien 		athn_usb_stop(ifp);
99513236e8dSdamien 		error = athn_usb_init(ifp);
99613236e8dSdamien 	}
99713236e8dSdamien 	return (error);
99813236e8dSdamien }
99913236e8dSdamien 
1000dd463f29Sstsp void
1001dd463f29Sstsp athn_usb_next_scan(void *arg)
1002dd463f29Sstsp {
1003dd463f29Sstsp 	struct athn_usb_softc *usc = arg;
1004dd463f29Sstsp 	struct athn_softc *sc = &usc->sc_sc;
1005dd463f29Sstsp 	struct ieee80211com *ic = &sc->sc_ic;
1006dd463f29Sstsp 	int s;
1007dd463f29Sstsp 
1008dd463f29Sstsp 	if (usbd_is_dying(usc->sc_udev))
1009dd463f29Sstsp 		return;
1010dd463f29Sstsp 
1011dd463f29Sstsp 	usbd_ref_incr(usc->sc_udev);
1012dd463f29Sstsp 
1013dd463f29Sstsp 	s = splnet();
1014dd463f29Sstsp 	if (ic->ic_state == IEEE80211_S_SCAN)
1015dd463f29Sstsp 		ieee80211_next_scan(&ic->ic_if);
1016dd463f29Sstsp 	splx(s);
1017dd463f29Sstsp 
1018dd463f29Sstsp 	usbd_ref_decr(usc->sc_udev);
1019dd463f29Sstsp }
1020dd463f29Sstsp 
102113236e8dSdamien int
102213236e8dSdamien athn_usb_newstate(struct ieee80211com *ic, enum ieee80211_state nstate,
102313236e8dSdamien     int arg)
102413236e8dSdamien {
102513236e8dSdamien 	struct athn_usb_softc *usc = ic->ic_softc;
102613236e8dSdamien 	struct athn_usb_cmd_newstate cmd;
102713236e8dSdamien 
102813236e8dSdamien 	/* Do it in a process context. */
102913236e8dSdamien 	cmd.state = nstate;
103013236e8dSdamien 	cmd.arg = arg;
103113236e8dSdamien 	athn_usb_do_async(usc, athn_usb_newstate_cb, &cmd, sizeof(cmd));
103213236e8dSdamien 	return (0);
103313236e8dSdamien }
103413236e8dSdamien 
103513236e8dSdamien void
103613236e8dSdamien athn_usb_newstate_cb(struct athn_usb_softc *usc, void *arg)
103713236e8dSdamien {
103813236e8dSdamien 	struct athn_usb_cmd_newstate *cmd = arg;
103913236e8dSdamien 	struct athn_softc *sc = &usc->sc_sc;
104013236e8dSdamien 	struct ieee80211com *ic = &sc->sc_ic;
104113236e8dSdamien 	enum ieee80211_state ostate;
104203574ff2Sdamien 	uint32_t reg, imask;
104313236e8dSdamien 	int s, error;
104413236e8dSdamien 
104513236e8dSdamien 	timeout_del(&sc->calib_to);
104613236e8dSdamien 
104713236e8dSdamien 	s = splnet();
104813236e8dSdamien 	ostate = ic->ic_state;
104913236e8dSdamien 
1050f47be805Sstsp 	if (ostate == IEEE80211_S_RUN && ic->ic_opmode == IEEE80211_M_STA) {
1051f47be805Sstsp 		athn_usb_remove_node(usc, ic->ic_bss);
1052ea37cbb6Sstsp 		reg = AR_READ(sc, AR_RX_FILTER);
1053ea37cbb6Sstsp 		reg = (reg & ~AR_RX_FILTER_MYBEACON) |
1054ea37cbb6Sstsp 		    AR_RX_FILTER_BEACON;
1055ea37cbb6Sstsp 		AR_WRITE(sc, AR_RX_FILTER, reg);
1056ea37cbb6Sstsp 		AR_WRITE_BARRIER(sc);
1057ea37cbb6Sstsp 	}
105813236e8dSdamien 	switch (cmd->state) {
105913236e8dSdamien 	case IEEE80211_S_INIT:
10605632af28Sdamien 		athn_set_led(sc, 0);
106113236e8dSdamien 		break;
106213236e8dSdamien 	case IEEE80211_S_SCAN:
106303574ff2Sdamien 		/* Make the LED blink while scanning. */
10645632af28Sdamien 		athn_set_led(sc, !sc->led_state);
1065f47be805Sstsp 		error = athn_usb_switch_chan(sc, ic->ic_bss->ni_chan, NULL);
1066f47be805Sstsp 		if (error)
1067f47be805Sstsp 			printf("%s: could not switch to channel %d\n",
1068f47be805Sstsp 			    usc->usb_dev.dv_xname,
1069f47be805Sstsp 			    ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan));
1070dfa43ce4Sstsp 		if (!usbd_is_dying(usc->sc_udev))
107113236e8dSdamien 			timeout_add_msec(&sc->scan_to, 200);
107213236e8dSdamien 		break;
107313236e8dSdamien 	case IEEE80211_S_AUTH:
10745632af28Sdamien 		athn_set_led(sc, 0);
107513236e8dSdamien 		error = athn_usb_switch_chan(sc, ic->ic_bss->ni_chan, NULL);
1076f47be805Sstsp 		if (error)
1077f47be805Sstsp 			printf("%s: could not switch to channel %d\n",
1078f47be805Sstsp 			    usc->usb_dev.dv_xname,
1079f47be805Sstsp 			    ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan));
108013236e8dSdamien 		break;
108113236e8dSdamien 	case IEEE80211_S_ASSOC:
108213236e8dSdamien 		break;
108313236e8dSdamien 	case IEEE80211_S_RUN:
10845632af28Sdamien 		athn_set_led(sc, 1);
108503574ff2Sdamien 
108613236e8dSdamien 		if (ic->ic_opmode == IEEE80211_M_MONITOR)
108713236e8dSdamien 			break;
108803574ff2Sdamien 
1089f47be805Sstsp 		if (ic->ic_opmode == IEEE80211_M_STA) {
1090ea37cbb6Sstsp 			/* Create node entry for our BSS */
109113236e8dSdamien 			error = athn_usb_create_node(usc, ic->ic_bss);
1092f47be805Sstsp 			if (error)
1093f47be805Sstsp 				printf("%s: could not update firmware station "
1094f47be805Sstsp 				    "table\n", usc->usb_dev.dv_xname);
1095f47be805Sstsp 		}
109603574ff2Sdamien 		athn_set_bss(sc, ic->ic_bss);
109713236e8dSdamien 		athn_usb_wmi_cmd(usc, AR_WMI_CMD_DISABLE_INTR);
109813236e8dSdamien #ifndef IEEE80211_STA_ONLY
109913236e8dSdamien 		if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
1100f47be805Sstsp 			athn_usb_switch_chan(sc, ic->ic_bss->ni_chan, NULL);
110113236e8dSdamien 			athn_set_hostap_timers(sc);
1102ead380efSjsing 			/* Enable software beacon alert interrupts. */
110313236e8dSdamien 			imask = htobe32(AR_IMR_SWBA);
110413236e8dSdamien 		} else
110513236e8dSdamien #endif
110613236e8dSdamien 		{
110713236e8dSdamien 			athn_set_sta_timers(sc);
110813236e8dSdamien 			/* Enable beacon miss interrupts. */
110913236e8dSdamien 			imask = htobe32(AR_IMR_BMISS);
111013236e8dSdamien 
111103574ff2Sdamien 			/* Stop receiving beacons from other BSS. */
111203574ff2Sdamien 			reg = AR_READ(sc, AR_RX_FILTER);
111303574ff2Sdamien 			reg = (reg & ~AR_RX_FILTER_BEACON) |
111403574ff2Sdamien 			    AR_RX_FILTER_MYBEACON;
111503574ff2Sdamien 			AR_WRITE(sc, AR_RX_FILTER, reg);
111603574ff2Sdamien 			AR_WRITE_BARRIER(sc);
111713236e8dSdamien 		}
111813236e8dSdamien 		athn_usb_wmi_xcmd(usc, AR_WMI_CMD_ENABLE_INTR,
111913236e8dSdamien 		    &imask, sizeof(imask), NULL);
112013236e8dSdamien 		break;
112113236e8dSdamien 	}
112213236e8dSdamien 	(void)sc->sc_newstate(ic, cmd->state, cmd->arg);
112313236e8dSdamien 	splx(s);
112413236e8dSdamien }
112513236e8dSdamien 
112613236e8dSdamien void
112713236e8dSdamien athn_usb_newassoc(struct ieee80211com *ic, struct ieee80211_node *ni,
112813236e8dSdamien     int isnew)
112913236e8dSdamien {
113010c7bb96Sbrad #ifndef IEEE80211_STA_ONLY
113113236e8dSdamien 	struct athn_usb_softc *usc = ic->ic_softc;
113213236e8dSdamien 
1133f47be805Sstsp 	if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
1134f47be805Sstsp 	    ic->ic_state != IEEE80211_S_RUN)
113513236e8dSdamien 		return;
1136f47be805Sstsp 
1137f47be805Sstsp 	/* Update the node's supported rates in a process context. */
113813236e8dSdamien 	ieee80211_ref_node(ni);
113913236e8dSdamien 	athn_usb_do_async(usc, athn_usb_newassoc_cb, &ni, sizeof(ni));
1140f47be805Sstsp #endif
114113236e8dSdamien }
114213236e8dSdamien 
1143f47be805Sstsp #ifndef IEEE80211_STA_ONLY
114413236e8dSdamien void
114513236e8dSdamien athn_usb_newassoc_cb(struct athn_usb_softc *usc, void *arg)
114613236e8dSdamien {
114713236e8dSdamien 	struct ieee80211com *ic = &usc->sc_sc.sc_ic;
114813236e8dSdamien 	struct ieee80211_node *ni = *(void **)arg;
1149f47be805Sstsp 	struct athn_node *an = (struct athn_node *)ni;
115013236e8dSdamien 	int s;
115113236e8dSdamien 
1152f47be805Sstsp 	if (ic->ic_state != IEEE80211_S_RUN)
1153f47be805Sstsp 		return;
1154f47be805Sstsp 
115513236e8dSdamien 	s = splnet();
115613236e8dSdamien 	/* NB: Node may have left before we got scheduled. */
1157f47be805Sstsp 	if (an->sta_index != 0)
1158f47be805Sstsp 		(void)athn_usb_node_set_rates(usc, ni);
115913236e8dSdamien 	ieee80211_release_node(ic, ni);
116013236e8dSdamien 	splx(s);
1161f47be805Sstsp }
116210c7bb96Sbrad #endif
1163f47be805Sstsp 
1164f47be805Sstsp struct ieee80211_node *
1165f47be805Sstsp athn_usb_node_alloc(struct ieee80211com *ic)
1166f47be805Sstsp {
1167f47be805Sstsp 	struct athn_node *an;
1168f47be805Sstsp 
1169f47be805Sstsp 	an = malloc(sizeof(struct athn_node), M_DEVBUF, M_NOWAIT | M_ZERO);
1170f47be805Sstsp 	return (struct ieee80211_node *)an;
117113236e8dSdamien }
117213236e8dSdamien 
1173f47be805Sstsp 
1174f47be805Sstsp #ifndef IEEE80211_STA_ONLY
117513236e8dSdamien void
1176f47be805Sstsp athn_usb_count_active_sta(void *arg, struct ieee80211_node *ni)
1177f47be805Sstsp {
1178f47be805Sstsp 	int *nsta = arg;
1179f47be805Sstsp 	struct athn_node *an = (struct athn_node *)ni;
1180f47be805Sstsp 
1181f47be805Sstsp 	if (an->sta_index == 0)
1182f47be805Sstsp 		return;
1183f47be805Sstsp 
1184f47be805Sstsp 	if ((ni->ni_state == IEEE80211_STA_AUTH ||
1185f47be805Sstsp 	    ni->ni_state == IEEE80211_STA_ASSOC) &&
1186f47be805Sstsp 	    ni->ni_inact < IEEE80211_INACT_MAX)
1187f47be805Sstsp 		(*nsta)++;
1188f47be805Sstsp }
1189f47be805Sstsp 
1190f47be805Sstsp struct athn_usb_newauth_cb_arg {
1191f47be805Sstsp 	struct ieee80211_node *ni;
1192f47be805Sstsp 	uint16_t seq;
1193f47be805Sstsp };
1194f47be805Sstsp 
1195f47be805Sstsp void
1196f47be805Sstsp athn_usb_newauth_cb(struct athn_usb_softc *usc, void *arg)
1197f47be805Sstsp {
1198f47be805Sstsp 	struct ieee80211com *ic = &usc->sc_sc.sc_ic;
1199f47be805Sstsp 	struct athn_usb_newauth_cb_arg *a = arg;
1200f47be805Sstsp 	struct ieee80211_node *ni = a->ni;
1201f47be805Sstsp 	uint16_t seq = a->seq;
1202f47be805Sstsp 	struct athn_node *an = (struct athn_node *)ni;
1203f47be805Sstsp 	int s, error = 0;
1204f47be805Sstsp 
1205f47be805Sstsp 	if (ic->ic_state != IEEE80211_S_RUN)
1206f47be805Sstsp 		return;
1207f47be805Sstsp 
1208f47be805Sstsp 	s = splnet();
1209f47be805Sstsp 	if (an->sta_index == 0) {
1210f47be805Sstsp 		error = athn_usb_create_node(usc, ni);
1211f47be805Sstsp 		if (error)
1212f47be805Sstsp 			printf("%s: could not add station %s to firmware "
1213f47be805Sstsp 			    "table\n", usc->usb_dev.dv_xname,
1214f47be805Sstsp 			    ether_sprintf(ni->ni_macaddr));
1215f47be805Sstsp 	}
1216f47be805Sstsp 	if (error == 0)
1217f47be805Sstsp 		ieee80211_auth_open_confirm(ic, ni, seq);
1218f47be805Sstsp 	ieee80211_unref_node(&ni);
1219f47be805Sstsp 	splx(s);
1220f47be805Sstsp }
1221f47be805Sstsp #endif
1222f47be805Sstsp 
1223f47be805Sstsp int
1224f47be805Sstsp athn_usb_newauth(struct ieee80211com *ic, struct ieee80211_node *ni,
1225f47be805Sstsp     int isnew, uint16_t seq)
1226f47be805Sstsp {
1227f47be805Sstsp #ifndef IEEE80211_STA_ONLY
1228f47be805Sstsp 	struct athn_usb_softc *usc = ic->ic_softc;
1229f47be805Sstsp 	struct ifnet *ifp = &ic->ic_if;
1230f47be805Sstsp 	struct athn_node *an = (struct athn_node *)ni;
1231f47be805Sstsp 	int nsta;
1232*044d754cSstsp 	struct athn_usb_newauth_cb_arg arg;
1233f47be805Sstsp 
1234f47be805Sstsp 	if (ic->ic_opmode != IEEE80211_M_HOSTAP)
1235f47be805Sstsp 		return 0;
1236f47be805Sstsp 
1237f47be805Sstsp 	if (!isnew && an->sta_index != 0) /* already in firmware table */
1238f47be805Sstsp 		return 0;
1239f47be805Sstsp 
1240f47be805Sstsp 	/* Check if we have room in the firmware table. */
1241f47be805Sstsp 	nsta = 1; /* Account for default node. */
1242f47be805Sstsp 	ieee80211_iterate_nodes(ic, athn_usb_count_active_sta, &nsta);
1243f47be805Sstsp 	if (nsta >= AR_USB_MAX_STA) {
1244f47be805Sstsp 		if (ifp->if_flags & IFF_DEBUG)
1245f47be805Sstsp 			printf("%s: cannot authenticate station %s: firmware "
1246f47be805Sstsp 			    "table is full\n", usc->usb_dev.dv_xname,
1247f47be805Sstsp 			    ether_sprintf(ni->ni_macaddr));
1248f47be805Sstsp 		return ENOSPC;
1249f47be805Sstsp 	}
1250f47be805Sstsp 
1251f47be805Sstsp 	/*
1252f47be805Sstsp 	 * In a process context, try to add this node to the
1253f47be805Sstsp 	 * firmware table and confirm the AUTH request.
1254f47be805Sstsp 	 */
1255*044d754cSstsp 	arg.ni = ieee80211_ref_node(ni);
1256*044d754cSstsp 	arg.seq = seq;
1257*044d754cSstsp 	athn_usb_do_async(usc, athn_usb_newauth_cb, &arg, sizeof(arg));
1258f47be805Sstsp 	return EBUSY;
1259f47be805Sstsp #else
1260f47be805Sstsp 	return 0;
1261f47be805Sstsp #endif /* IEEE80211_STA_ONLY */
1262f47be805Sstsp }
1263f47be805Sstsp 
1264f47be805Sstsp #ifndef IEEE80211_STA_ONLY
1265f47be805Sstsp void
1266f47be805Sstsp athn_usb_node_free(struct ieee80211com *ic, struct ieee80211_node *ni)
126713236e8dSdamien {
126813236e8dSdamien 	struct athn_usb_softc *usc = ic->ic_softc;
1269f47be805Sstsp 	struct athn_node *an = (struct athn_node *)ni;
127013236e8dSdamien 
1271f47be805Sstsp 	/*
1272f47be805Sstsp 	 * Remove the node from the firmware table in a process context.
1273f47be805Sstsp 	 * Pass an index rather than the pointer which we will free.
1274f47be805Sstsp 	 */
1275f47be805Sstsp 	if (an->sta_index != 0)
1276f47be805Sstsp 		athn_usb_do_async(usc, athn_usb_node_free_cb,
1277f47be805Sstsp 		    &an->sta_index, sizeof(an->sta_index));
1278f47be805Sstsp 	usc->sc_node_free(ic, ni);
127913236e8dSdamien }
128013236e8dSdamien 
128113236e8dSdamien void
1282f47be805Sstsp athn_usb_node_free_cb(struct athn_usb_softc *usc, void *arg)
128313236e8dSdamien {
1284f47be805Sstsp 	struct ieee80211com *ic = &usc->sc_sc.sc_ic;
1285f47be805Sstsp 	struct ifnet *ifp = &ic->ic_if;
128613236e8dSdamien 	uint8_t sta_index = *(uint8_t *)arg;
1287f47be805Sstsp 	int error;
128813236e8dSdamien 
1289f47be805Sstsp 	error = athn_usb_wmi_xcmd(usc, AR_WMI_CMD_NODE_REMOVE,
129013236e8dSdamien 	    &sta_index, sizeof(sta_index), NULL);
1291f47be805Sstsp 	if (error) {
1292f47be805Sstsp 		printf("%s: could not remove station %u from firmware table\n",
1293f47be805Sstsp 		    usc->usb_dev.dv_xname, sta_index);
1294f47be805Sstsp 		return;
129513236e8dSdamien 	}
1296f47be805Sstsp 	usc->free_node_slots |= (1 << sta_index);
1297f47be805Sstsp 	if (ifp->if_flags & IFF_DEBUG)
1298f47be805Sstsp 		printf("%s: station %u removed from firmware table\n",
1299f47be805Sstsp 		    usc->usb_dev.dv_xname, sta_index);
1300f47be805Sstsp }
1301f47be805Sstsp #endif /* IEEE80211_STA_ONLY */
130213236e8dSdamien 
130313236e8dSdamien int
130413236e8dSdamien athn_usb_ampdu_tx_start(struct ieee80211com *ic, struct ieee80211_node *ni,
130513236e8dSdamien     uint8_t tid)
130613236e8dSdamien {
130713236e8dSdamien 	struct athn_usb_softc *usc = ic->ic_softc;
130813236e8dSdamien 	struct athn_node *an = (struct athn_node *)ni;
130913236e8dSdamien 	struct athn_usb_aggr_cmd cmd;
131013236e8dSdamien 
131113236e8dSdamien 	/* Do it in a process context. */
131213236e8dSdamien 	cmd.sta_index = an->sta_index;
131313236e8dSdamien 	cmd.tid = tid;
131413236e8dSdamien 	athn_usb_do_async(usc, athn_usb_ampdu_tx_start_cb, &cmd, sizeof(cmd));
131513236e8dSdamien 	return (0);
131613236e8dSdamien }
131713236e8dSdamien 
131813236e8dSdamien void
131913236e8dSdamien athn_usb_ampdu_tx_start_cb(struct athn_usb_softc *usc, void *arg)
132013236e8dSdamien {
132113236e8dSdamien 	struct athn_usb_aggr_cmd *cmd = arg;
132213236e8dSdamien 	struct ar_htc_target_aggr aggr;
132313236e8dSdamien 
132413236e8dSdamien 	memset(&aggr, 0, sizeof(aggr));
132513236e8dSdamien 	aggr.sta_index = cmd->sta_index;
132613236e8dSdamien 	aggr.tidno = cmd->tid;
132713236e8dSdamien 	aggr.aggr_enable = 1;
132813236e8dSdamien 	(void)athn_usb_wmi_xcmd(usc, AR_WMI_CMD_TX_AGGR_ENABLE,
132913236e8dSdamien 	    &aggr, sizeof(aggr), NULL);
133013236e8dSdamien }
133113236e8dSdamien 
133213236e8dSdamien void
133313236e8dSdamien athn_usb_ampdu_tx_stop(struct ieee80211com *ic, struct ieee80211_node *ni,
133413236e8dSdamien     uint8_t tid)
133513236e8dSdamien {
133613236e8dSdamien 	struct athn_usb_softc *usc = ic->ic_softc;
133713236e8dSdamien 	struct athn_node *an = (struct athn_node *)ni;
133813236e8dSdamien 	struct athn_usb_aggr_cmd cmd;
133913236e8dSdamien 
134013236e8dSdamien 	/* Do it in a process context. */
134113236e8dSdamien 	cmd.sta_index = an->sta_index;
134213236e8dSdamien 	cmd.tid = tid;
134313236e8dSdamien 	athn_usb_do_async(usc, athn_usb_ampdu_tx_stop_cb, &cmd, sizeof(cmd));
134413236e8dSdamien }
134513236e8dSdamien 
134613236e8dSdamien void
134713236e8dSdamien athn_usb_ampdu_tx_stop_cb(struct athn_usb_softc *usc, void *arg)
134813236e8dSdamien {
134913236e8dSdamien 	struct athn_usb_aggr_cmd *cmd = arg;
135013236e8dSdamien 	struct ar_htc_target_aggr aggr;
135113236e8dSdamien 
135213236e8dSdamien 	memset(&aggr, 0, sizeof(aggr));
135313236e8dSdamien 	aggr.sta_index = cmd->sta_index;
135413236e8dSdamien 	aggr.tidno = cmd->tid;
135513236e8dSdamien 	aggr.aggr_enable = 0;
135613236e8dSdamien 	(void)athn_usb_wmi_xcmd(usc, AR_WMI_CMD_TX_AGGR_ENABLE,
135713236e8dSdamien 	    &aggr, sizeof(aggr), NULL);
135813236e8dSdamien }
135913236e8dSdamien 
1360f47be805Sstsp #ifndef IEEE80211_STA_ONLY
1361f47be805Sstsp /* Try to find a node we can evict to make room in the firmware table. */
1362f47be805Sstsp void
1363f47be805Sstsp athn_usb_clean_nodes(void *arg, struct ieee80211_node *ni)
1364f47be805Sstsp {
1365f47be805Sstsp 	struct athn_usb_softc *usc = arg;
1366f47be805Sstsp 	struct ieee80211com *ic = &usc->sc_sc.sc_ic;
1367f47be805Sstsp 	struct athn_node *an = (struct athn_node *)ni;
1368f47be805Sstsp 
1369f47be805Sstsp 	/*
1370f47be805Sstsp 	 * Don't remove the default node (used for management frames).
1371f47be805Sstsp 	 * Nodes which are not in the firmware table also have index zero.
1372f47be805Sstsp 	 */
1373f47be805Sstsp 	if (an->sta_index == 0)
1374f47be805Sstsp 		return;
1375f47be805Sstsp 
1376f47be805Sstsp 	/* Remove non-associated nodes. */
1377f47be805Sstsp 	if (ni->ni_state != IEEE80211_STA_AUTH &&
1378f47be805Sstsp 	    ni->ni_state != IEEE80211_STA_ASSOC) {
1379f47be805Sstsp 		athn_usb_remove_node(usc, ni);
1380f47be805Sstsp 		return;
1381f47be805Sstsp 	}
1382f47be805Sstsp 
1383f47be805Sstsp 	/*
1384f47be805Sstsp 	 * Kick off inactive associated nodes. This won't help
1385f47be805Sstsp 	 * immediately but will help if the new STA retries later.
1386f47be805Sstsp 	 */
1387f47be805Sstsp 	if (ni->ni_inact >= IEEE80211_INACT_MAX) {
1388f47be805Sstsp 		IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
1389f47be805Sstsp 		    IEEE80211_REASON_AUTH_EXPIRE);
1390f47be805Sstsp 		ieee80211_node_leave(ic, ni);
1391f47be805Sstsp 	}
1392f47be805Sstsp }
1393f47be805Sstsp #endif
1394f47be805Sstsp 
139513236e8dSdamien int
139613236e8dSdamien athn_usb_create_node(struct athn_usb_softc *usc, struct ieee80211_node *ni)
139713236e8dSdamien {
139813236e8dSdamien 	struct athn_node *an = (struct athn_node *)ni;
139913236e8dSdamien 	struct ar_htc_target_sta sta;
1400f47be805Sstsp 	int error, sta_index;
1401f47be805Sstsp #ifndef IEEE80211_STA_ONLY
1402f47be805Sstsp 	struct ieee80211com *ic = &usc->sc_sc.sc_ic;
1403f47be805Sstsp 	struct ifnet *ifp = &ic->ic_if;
140413236e8dSdamien 
1405f47be805Sstsp 	/* Firmware cannot handle more than 8 STAs. Try to make room first. */
1406f47be805Sstsp 	if (ic->ic_opmode == IEEE80211_M_HOSTAP)
1407f47be805Sstsp 		ieee80211_iterate_nodes(ic, athn_usb_clean_nodes, usc);
1408f47be805Sstsp #endif
1409f47be805Sstsp 	if (usc->free_node_slots == 0x00)
1410dd463f29Sstsp 		return ENOBUFS;
1411dd463f29Sstsp 
1412f47be805Sstsp 	sta_index = ffs(usc->free_node_slots) - 1;
1413f47be805Sstsp 	if (sta_index < 0 || sta_index >= AR_USB_MAX_STA)
1414f47be805Sstsp 		return ENOSPC;
141513236e8dSdamien 
141613236e8dSdamien 	/* Create node entry on target. */
141713236e8dSdamien 	memset(&sta, 0, sizeof(sta));
141813236e8dSdamien 	IEEE80211_ADDR_COPY(sta.macaddr, ni->ni_macaddr);
141913236e8dSdamien 	IEEE80211_ADDR_COPY(sta.bssid, ni->ni_bssid);
1420f47be805Sstsp 	sta.sta_index = sta_index;
142113236e8dSdamien 	sta.maxampdu = 0xffff;
142213236e8dSdamien 	if (ni->ni_flags & IEEE80211_NODE_HT)
142313236e8dSdamien 		sta.flags |= htobe16(AR_HTC_STA_HT);
142413236e8dSdamien 	error = athn_usb_wmi_xcmd(usc, AR_WMI_CMD_NODE_CREATE,
142513236e8dSdamien 	    &sta, sizeof(sta), NULL);
142613236e8dSdamien 	if (error != 0)
142713236e8dSdamien 		return (error);
1428f47be805Sstsp 	an->sta_index = sta_index;
1429f47be805Sstsp 	usc->free_node_slots &= ~(1 << an->sta_index);
1430f47be805Sstsp 
1431f47be805Sstsp #ifndef IEEE80211_STA_ONLY
1432f47be805Sstsp 	if (ifp->if_flags & IFF_DEBUG)
1433f47be805Sstsp 		printf("%s: station %u (%s) added to firmware table\n",
1434f47be805Sstsp 		    usc->usb_dev.dv_xname, sta_index,
1435f47be805Sstsp 		    ether_sprintf(ni->ni_macaddr));
1436f47be805Sstsp #endif
1437f47be805Sstsp 	return athn_usb_node_set_rates(usc, ni);
1438f47be805Sstsp }
1439f47be805Sstsp 
1440f47be805Sstsp int
1441f47be805Sstsp athn_usb_node_set_rates(struct athn_usb_softc *usc, struct ieee80211_node *ni)
1442f47be805Sstsp {
1443f47be805Sstsp 	struct athn_node *an = (struct athn_node *)ni;
1444f47be805Sstsp 	struct ar_htc_target_rate rate;
1445f47be805Sstsp 	int i, j;
144613236e8dSdamien 
144713236e8dSdamien 	/* Setup supported rates. */
144813236e8dSdamien 	memset(&rate, 0, sizeof(rate));
1449f47be805Sstsp 	rate.sta_index = an->sta_index;
145013236e8dSdamien 	rate.isnew = 1;
145113236e8dSdamien 	rate.lg_rates.rs_nrates = ni->ni_rates.rs_nrates;
145213236e8dSdamien 	memcpy(rate.lg_rates.rs_rates, ni->ni_rates.rs_rates,
145313236e8dSdamien 	    ni->ni_rates.rs_nrates);
145413236e8dSdamien 	if (ni->ni_flags & IEEE80211_NODE_HT) {
145513236e8dSdamien 		rate.capflags |= htobe32(AR_RC_HT_FLAG);
14567363c99eSstsp 		/* Setup HT rates. */
14577363c99eSstsp 		for (i = 0, j = 0; i < IEEE80211_HT_NUM_MCS; i++) {
14587363c99eSstsp 			if (!isset(ni->ni_rxmcs, i))
14597363c99eSstsp 				continue;
14607363c99eSstsp 			if (j >= AR_HTC_RATE_MAX)
14617363c99eSstsp 				break;
14627363c99eSstsp 			rate.ht_rates.rs_rates[j++] = i;
14637363c99eSstsp 		}
14647363c99eSstsp 		rate.ht_rates.rs_nrates = j;
14657363c99eSstsp 
14667363c99eSstsp 		if (ni->ni_rxmcs[1]) /* dual-stream MIMO rates */
14677363c99eSstsp 			rate.capflags |= htobe32(AR_RC_DS_FLAG);
146813236e8dSdamien #ifdef notyet
146913236e8dSdamien 		if (ni->ni_htcaps & IEEE80211_HTCAP_CBW20_40)
147013236e8dSdamien 			rate.capflags |= htobe32(AR_RC_40_FLAG);
147113236e8dSdamien 		if (ni->ni_htcaps & IEEE80211_HTCAP_SGI40)
147213236e8dSdamien 			rate.capflags |= htobe32(AR_RC_SGI_FLAG);
147313236e8dSdamien 		if (ni->ni_htcaps & IEEE80211_HTCAP_SGI20)
147413236e8dSdamien 			rate.capflags |= htobe32(AR_RC_SGI_FLAG);
147513236e8dSdamien #endif
147613236e8dSdamien 	}
1477f47be805Sstsp 
1478f47be805Sstsp 	return athn_usb_wmi_xcmd(usc, AR_WMI_CMD_RC_RATE_UPDATE,
147913236e8dSdamien 	    &rate, sizeof(rate), NULL);
1480f47be805Sstsp }
1481f47be805Sstsp 
1482f47be805Sstsp int
1483f47be805Sstsp athn_usb_remove_node(struct athn_usb_softc *usc, struct ieee80211_node *ni)
1484f47be805Sstsp {
1485f47be805Sstsp 	struct athn_node *an = (struct athn_node *)ni;
1486f47be805Sstsp 	int error;
1487f47be805Sstsp #ifndef IEEE80211_STA_ONLY
1488f47be805Sstsp 	struct ieee80211com *ic = &usc->sc_sc.sc_ic;
1489f47be805Sstsp 	struct ifnet *ifp = &ic->ic_if;
1490f47be805Sstsp #endif
1491f47be805Sstsp 
1492f47be805Sstsp 	error = athn_usb_wmi_xcmd(usc, AR_WMI_CMD_NODE_REMOVE,
1493f47be805Sstsp 	    &an->sta_index, sizeof(an->sta_index), NULL);
1494f47be805Sstsp 	if (error) {
1495f47be805Sstsp 		printf("%s: could not remove station %u (%s) from "
1496f47be805Sstsp 		    "firmware table\n", usc->usb_dev.dv_xname, an->sta_index,
1497f47be805Sstsp 		    ether_sprintf(ni->ni_macaddr));
1498f47be805Sstsp 		return error;
1499f47be805Sstsp 	}
1500f47be805Sstsp 
1501f47be805Sstsp #ifndef IEEE80211_STA_ONLY
1502f47be805Sstsp 	if (ifp->if_flags & IFF_DEBUG)
1503f47be805Sstsp 		printf("%s: station %u (%s) removed from firmware table\n",
1504f47be805Sstsp 		    usc->usb_dev.dv_xname, an->sta_index,
1505f47be805Sstsp 		    ether_sprintf(ni->ni_macaddr));
1506f47be805Sstsp #endif
1507f47be805Sstsp 
1508f47be805Sstsp 	usc->free_node_slots |= (1 << an->sta_index);
1509f47be805Sstsp 	an->sta_index = 0;
1510f47be805Sstsp 	return 0;
151113236e8dSdamien }
151213236e8dSdamien 
151313236e8dSdamien void
151413236e8dSdamien athn_usb_rx_enable(struct athn_softc *sc)
151513236e8dSdamien {
151613236e8dSdamien 	AR_WRITE(sc, AR_CR, AR_CR_RXE);
151713236e8dSdamien 	AR_WRITE_BARRIER(sc);
151813236e8dSdamien }
151913236e8dSdamien 
152013236e8dSdamien int
152113236e8dSdamien athn_usb_switch_chan(struct athn_softc *sc, struct ieee80211_channel *c,
152213236e8dSdamien     struct ieee80211_channel *extc)
152313236e8dSdamien {
152413236e8dSdamien 	struct athn_usb_softc *usc = (struct athn_usb_softc *)sc;
152513236e8dSdamien 	uint16_t mode;
152613236e8dSdamien 	int error;
152713236e8dSdamien 
152813236e8dSdamien 	/* Disable interrupts. */
152913236e8dSdamien 	error = athn_usb_wmi_cmd(usc, AR_WMI_CMD_DISABLE_INTR);
153013236e8dSdamien 	if (error != 0)
153175f8bb6dSdamien 		goto reset;
153213236e8dSdamien 	/* Stop all Tx queues. */
153313236e8dSdamien 	error = athn_usb_wmi_cmd(usc, AR_WMI_CMD_DRAIN_TXQ_ALL);
153413236e8dSdamien 	if (error != 0)
153575f8bb6dSdamien 		goto reset;
153613236e8dSdamien 	/* Stop Rx. */
153713236e8dSdamien 	error = athn_usb_wmi_cmd(usc, AR_WMI_CMD_STOP_RECV);
153813236e8dSdamien 	if (error != 0)
153975f8bb6dSdamien 		goto reset;
154013236e8dSdamien 
154175f8bb6dSdamien 	/* If band or bandwidth changes, we need to do a full reset. */
154275f8bb6dSdamien 	if (c->ic_flags != sc->curchan->ic_flags ||
154375f8bb6dSdamien 	    ((extc != NULL) ^ (sc->curchanext != NULL))) {
154475f8bb6dSdamien 		DPRINTFN(2, ("channel band switch\n"));
154575f8bb6dSdamien 		goto reset;
154675f8bb6dSdamien 	}
154775f8bb6dSdamien 
154875f8bb6dSdamien 	error = athn_set_chan(sc, c, extc);
154975f8bb6dSdamien 	if (AR_SREV_9271(sc) && error == 0)
155075f8bb6dSdamien 		ar9271_load_ani(sc);
155175f8bb6dSdamien 	if (error != 0) {
155275f8bb6dSdamien  reset:		/* Error found, try a full reset. */
155375f8bb6dSdamien 		DPRINTFN(3, ("needs a full reset\n"));
155413236e8dSdamien 		error = athn_hw_reset(sc, c, extc, 0);
155575f8bb6dSdamien 		if (error != 0)	/* Hopeless case. */
155675f8bb6dSdamien 			return (error);
1557f47be805Sstsp 
1558f47be805Sstsp 		error = athn_set_chan(sc, c, extc);
1559f47be805Sstsp 		if (AR_SREV_9271(sc) && error == 0)
1560f47be805Sstsp 			ar9271_load_ani(sc);
1561f47be805Sstsp 		if (error != 0)
1562f47be805Sstsp 			return (error);
156375f8bb6dSdamien 	}
156413236e8dSdamien 
1565f47be805Sstsp 	sc->ops.set_txpower(sc, c, extc);
1566f47be805Sstsp 
156703574ff2Sdamien 	error = athn_usb_wmi_cmd(usc, AR_WMI_CMD_START_RECV);
156803574ff2Sdamien 	if (error != 0)
156975f8bb6dSdamien 		return (error);
157013236e8dSdamien 	athn_rx_start(sc);
157113236e8dSdamien 
157213236e8dSdamien 	mode = htobe16(IEEE80211_IS_CHAN_2GHZ(c) ?
157313236e8dSdamien 	    AR_HTC_MODE_11NG : AR_HTC_MODE_11NA);
157413236e8dSdamien 	error = athn_usb_wmi_xcmd(usc, AR_WMI_CMD_SET_MODE,
157513236e8dSdamien 	    &mode, sizeof(mode), NULL);
157613236e8dSdamien 	if (error != 0)
157775f8bb6dSdamien 		return (error);
157875f8bb6dSdamien 
157913236e8dSdamien 	/* Re-enable interrupts. */
158013236e8dSdamien 	error = athn_usb_wmi_cmd(usc, AR_WMI_CMD_ENABLE_INTR);
158113236e8dSdamien 	return (error);
158213236e8dSdamien }
158313236e8dSdamien 
158413236e8dSdamien void
158513236e8dSdamien athn_usb_updateedca(struct ieee80211com *ic)
158613236e8dSdamien {
158713236e8dSdamien 	struct athn_usb_softc *usc = ic->ic_softc;
158813236e8dSdamien 
158913236e8dSdamien 	/* Do it in a process context. */
159013236e8dSdamien 	athn_usb_do_async(usc, athn_usb_updateedca_cb, NULL, 0);
159113236e8dSdamien }
159213236e8dSdamien 
159313236e8dSdamien void
159413236e8dSdamien athn_usb_updateedca_cb(struct athn_usb_softc *usc, void *arg)
159513236e8dSdamien {
159613236e8dSdamien 	int s;
159713236e8dSdamien 
159813236e8dSdamien 	s = splnet();
159913236e8dSdamien 	athn_updateedca(&usc->sc_sc.sc_ic);
160013236e8dSdamien 	splx(s);
160113236e8dSdamien }
160213236e8dSdamien 
160313236e8dSdamien void
160413236e8dSdamien athn_usb_updateslot(struct ieee80211com *ic)
160513236e8dSdamien {
160613236e8dSdamien 	struct athn_usb_softc *usc = ic->ic_softc;
160713236e8dSdamien 
160813236e8dSdamien 	return;	/* XXX */
160913236e8dSdamien 	/* Do it in a process context. */
161013236e8dSdamien 	athn_usb_do_async(usc, athn_usb_updateslot_cb, NULL, 0);
161113236e8dSdamien }
161213236e8dSdamien 
161313236e8dSdamien void
161413236e8dSdamien athn_usb_updateslot_cb(struct athn_usb_softc *usc, void *arg)
161513236e8dSdamien {
161613236e8dSdamien 	int s;
161713236e8dSdamien 
161813236e8dSdamien 	s = splnet();
161913236e8dSdamien 	athn_updateslot(&usc->sc_sc.sc_ic);
162013236e8dSdamien 	splx(s);
162113236e8dSdamien }
162213236e8dSdamien 
162313236e8dSdamien int
162413236e8dSdamien athn_usb_set_key(struct ieee80211com *ic, struct ieee80211_node *ni,
162513236e8dSdamien     struct ieee80211_key *k)
162613236e8dSdamien {
162713236e8dSdamien 	struct athn_usb_softc *usc = ic->ic_softc;
162813236e8dSdamien 	struct athn_usb_cmd_key cmd;
162913236e8dSdamien 
163013236e8dSdamien 	/* Defer setting of WEP keys until interface is brought up. */
163113236e8dSdamien 	if ((ic->ic_if.if_flags & (IFF_UP | IFF_RUNNING)) !=
163213236e8dSdamien 	    (IFF_UP | IFF_RUNNING))
163313236e8dSdamien 		return (0);
163413236e8dSdamien 
163513236e8dSdamien 	/* Do it in a process context. */
163613236e8dSdamien 	cmd.ni = (ni != NULL) ? ieee80211_ref_node(ni) : NULL;
163713236e8dSdamien 	cmd.key = k;
163813236e8dSdamien 	athn_usb_do_async(usc, athn_usb_set_key_cb, &cmd, sizeof(cmd));
163913236e8dSdamien 	return (0);
164013236e8dSdamien }
164113236e8dSdamien 
164213236e8dSdamien void
164313236e8dSdamien athn_usb_set_key_cb(struct athn_usb_softc *usc, void *arg)
164413236e8dSdamien {
164513236e8dSdamien 	struct ieee80211com *ic = &usc->sc_sc.sc_ic;
164613236e8dSdamien 	struct athn_usb_cmd_key *cmd = arg;
164713236e8dSdamien 	int s;
164813236e8dSdamien 
164913236e8dSdamien 	s = splnet();
165013236e8dSdamien 	athn_set_key(ic, cmd->ni, cmd->key);
165113236e8dSdamien 	if (cmd->ni != NULL)
165213236e8dSdamien 		ieee80211_release_node(ic, cmd->ni);
165313236e8dSdamien 	splx(s);
165413236e8dSdamien }
165513236e8dSdamien 
165613236e8dSdamien void
165713236e8dSdamien athn_usb_delete_key(struct ieee80211com *ic, struct ieee80211_node *ni,
165813236e8dSdamien     struct ieee80211_key *k)
165913236e8dSdamien {
166013236e8dSdamien 	struct athn_usb_softc *usc = ic->ic_softc;
166113236e8dSdamien 	struct athn_usb_cmd_key cmd;
166213236e8dSdamien 
166313236e8dSdamien 	if (!(ic->ic_if.if_flags & IFF_RUNNING) ||
166413236e8dSdamien 	    ic->ic_state != IEEE80211_S_RUN)
166513236e8dSdamien 		return;	/* Nothing to do. */
166613236e8dSdamien 
166713236e8dSdamien 	/* Do it in a process context. */
166813236e8dSdamien 	cmd.ni = (ni != NULL) ? ieee80211_ref_node(ni) : NULL;
166913236e8dSdamien 	cmd.key = k;
167013236e8dSdamien 	athn_usb_do_async(usc, athn_usb_delete_key_cb, &cmd, sizeof(cmd));
167113236e8dSdamien }
167213236e8dSdamien 
167313236e8dSdamien void
167413236e8dSdamien athn_usb_delete_key_cb(struct athn_usb_softc *usc, void *arg)
167513236e8dSdamien {
167613236e8dSdamien 	struct ieee80211com *ic = &usc->sc_sc.sc_ic;
167713236e8dSdamien 	struct athn_usb_cmd_key *cmd = arg;
167813236e8dSdamien 	int s;
167913236e8dSdamien 
168013236e8dSdamien 	s = splnet();
168113236e8dSdamien 	athn_delete_key(ic, cmd->ni, cmd->key);
168213236e8dSdamien 	if (cmd->ni != NULL)
168313236e8dSdamien 		ieee80211_release_node(ic, cmd->ni);
168413236e8dSdamien 	splx(s);
168513236e8dSdamien }
168613236e8dSdamien 
168713236e8dSdamien #ifndef IEEE80211_STA_ONLY
168813236e8dSdamien void
1689ab0b1be7Smglocker athn_usb_bcneof(struct usbd_xfer *xfer, void *priv,
169013236e8dSdamien     usbd_status status)
169113236e8dSdamien {
169213236e8dSdamien 	struct athn_usb_tx_data *data = priv;
169313236e8dSdamien 	struct athn_usb_softc *usc = data->sc;
169413236e8dSdamien 
169513236e8dSdamien 	if (__predict_false(status == USBD_STALLED))
169613236e8dSdamien 		usbd_clear_endpoint_stall_async(usc->tx_data_pipe);
1697af7c78e3Sdamien 	usc->tx_bcn = data;
169813236e8dSdamien }
169913236e8dSdamien 
170013236e8dSdamien /*
170113236e8dSdamien  * Process Software Beacon Alert interrupts.
170213236e8dSdamien  */
170313236e8dSdamien void
170413236e8dSdamien athn_usb_swba(struct athn_usb_softc *usc)
170513236e8dSdamien {
170613236e8dSdamien 	struct athn_softc *sc = &usc->sc_sc;
170713236e8dSdamien 	struct ieee80211com *ic = &sc->sc_ic;
170813236e8dSdamien 	struct athn_usb_tx_data *data;
170913236e8dSdamien 	struct ieee80211_frame *wh;
171013236e8dSdamien 	struct ar_stream_hdr *hdr;
171113236e8dSdamien 	struct ar_htc_frame_hdr *htc;
171213236e8dSdamien 	struct ar_tx_bcn *bcn;
171313236e8dSdamien 	struct mbuf *m;
171413236e8dSdamien 	int error;
171513236e8dSdamien 
171613236e8dSdamien 	if (ic->ic_dtim_count == 0)
171713236e8dSdamien 		ic->ic_dtim_count = ic->ic_dtim_period - 1;
171813236e8dSdamien 	else
171913236e8dSdamien 		ic->ic_dtim_count--;
172013236e8dSdamien 
172113236e8dSdamien 	/* Make sure previous beacon has been sent. */
1722af7c78e3Sdamien 	if (usc->tx_bcn == NULL)
172313236e8dSdamien 		return;
1724af7c78e3Sdamien 	data = usc->tx_bcn;
172513236e8dSdamien 
172613236e8dSdamien 	/* Get new beacon. */
172713236e8dSdamien 	m = ieee80211_beacon_alloc(ic, ic->ic_bss);
172813236e8dSdamien 	if (__predict_false(m == NULL))
172913236e8dSdamien 		return;
173013236e8dSdamien 	/* Assign sequence number. */
173113236e8dSdamien 	wh = mtod(m, struct ieee80211_frame *);
173213236e8dSdamien 	*(uint16_t *)&wh->i_seq[0] =
173313236e8dSdamien 	    htole16(ic->ic_bss->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT);
173413236e8dSdamien 	ic->ic_bss->ni_txseq++;
173513236e8dSdamien 
173613236e8dSdamien 	hdr = (struct ar_stream_hdr *)data->buf;
173713236e8dSdamien 	hdr->tag = htole16(AR_USB_TX_STREAM_TAG);
173813236e8dSdamien 	hdr->len = htole16(sizeof(*htc) + sizeof(*bcn) + m->m_pkthdr.len);
173913236e8dSdamien 
174013236e8dSdamien 	htc = (struct ar_htc_frame_hdr *)&hdr[1];
174113236e8dSdamien 	memset(htc, 0, sizeof(*htc));
174213236e8dSdamien 	htc->endpoint_id = usc->ep_bcn;
174313236e8dSdamien 	htc->payload_len = htobe16(sizeof(*bcn) + m->m_pkthdr.len);
174413236e8dSdamien 
174513236e8dSdamien 	bcn = (struct ar_tx_bcn *)&htc[1];
174613236e8dSdamien 	memset(bcn, 0, sizeof(*bcn));
174713236e8dSdamien 	bcn->vif_idx = 0;
174813236e8dSdamien 
174913236e8dSdamien 	m_copydata(m, 0, m->m_pkthdr.len, (caddr_t)&bcn[1]);
175013236e8dSdamien 
175113236e8dSdamien 	usbd_setup_xfer(data->xfer, usc->tx_data_pipe, data, data->buf,
175213236e8dSdamien 	    sizeof(*hdr) + sizeof(*htc) + sizeof(*bcn) + m->m_pkthdr.len,
175313236e8dSdamien 	    USBD_SHORT_XFER_OK | USBD_NO_COPY, ATHN_USB_TX_TIMEOUT,
175413236e8dSdamien 	    athn_usb_bcneof);
175513236e8dSdamien 
175613236e8dSdamien 	m_freem(m);
1757af7c78e3Sdamien 	usc->tx_bcn = NULL;
175813236e8dSdamien 	error = usbd_transfer(data->xfer);
175913236e8dSdamien 	if (__predict_false(error != USBD_IN_PROGRESS && error != 0))
1760af7c78e3Sdamien 		usc->tx_bcn = data;
176113236e8dSdamien }
176213236e8dSdamien #endif
176313236e8dSdamien 
1764f47be805Sstsp /* Update current transmit rate for a node based on firmware Tx status. */
1765f47be805Sstsp void
1766f47be805Sstsp athn_usb_tx_status(void *arg, struct ieee80211_node *ni)
1767f47be805Sstsp {
1768f47be805Sstsp 	struct ar_wmi_evt_txstatus *ts = arg;
1769f47be805Sstsp 	struct athn_node *an = (struct athn_node *)ni;
1770f47be805Sstsp 	uint8_t rate_index = (ts->rate & AR_HTC_TXSTAT_RATE);
1771f47be805Sstsp 
1772f47be805Sstsp 	if (an->sta_index != ts->cookie) /* Tx report for a different node */
1773f47be805Sstsp 		return;
1774f47be805Sstsp 
1775f47be805Sstsp 	if (ts->flags & AR_HTC_TXSTAT_MCS) {
1776f47be805Sstsp 		if (isset(ni->ni_rxmcs, rate_index))
1777f47be805Sstsp 			ni->ni_txmcs = rate_index;
1778f47be805Sstsp 	} else if (rate_index < ni->ni_rates.rs_nrates)
1779f47be805Sstsp 		ni->ni_txrate = rate_index;
1780f47be805Sstsp }
1781f47be805Sstsp 
178213236e8dSdamien void
178313236e8dSdamien athn_usb_rx_wmi_ctrl(struct athn_usb_softc *usc, uint8_t *buf, int len)
178413236e8dSdamien {
178513236e8dSdamien 	struct ar_wmi_cmd_hdr *wmi;
178613236e8dSdamien 	uint16_t cmd_id;
178713236e8dSdamien 
178813236e8dSdamien 	if (__predict_false(len < sizeof(*wmi)))
178913236e8dSdamien 		return;
179013236e8dSdamien 	wmi = (struct ar_wmi_cmd_hdr *)buf;
179113236e8dSdamien 	cmd_id = betoh16(wmi->cmd_id);
179213236e8dSdamien 
179313236e8dSdamien 	if (!(cmd_id & AR_WMI_EVT_FLAG)) {
179413236e8dSdamien 		if (usc->wait_cmd_id != cmd_id)
179513236e8dSdamien 			return;	/* Unexpected reply. */
179613236e8dSdamien 		if (usc->obuf != NULL) {
179713236e8dSdamien 			/* Copy answer into caller supplied buffer. */
179813236e8dSdamien 			memcpy(usc->obuf, &wmi[1], len - sizeof(*wmi));
179913236e8dSdamien 		}
180013236e8dSdamien 		/* Notify caller of completion. */
180113236e8dSdamien 		wakeup(&usc->wait_cmd_id);
180213236e8dSdamien 		return;
180313236e8dSdamien 	}
180413236e8dSdamien 	switch (cmd_id & 0xfff) {
180513236e8dSdamien #ifndef IEEE80211_STA_ONLY
180613236e8dSdamien 	case AR_WMI_EVT_SWBA:
180713236e8dSdamien 		athn_usb_swba(usc);
180813236e8dSdamien 		break;
180913236e8dSdamien #endif
1810f47be805Sstsp 	case AR_WMI_EVT_TXSTATUS: {
1811f47be805Sstsp 		struct ar_wmi_evt_txstatus_list *tsl;
1812f47be805Sstsp 		int i;
1813f47be805Sstsp 
1814f47be805Sstsp 		tsl = (struct ar_wmi_evt_txstatus_list *)&wmi[1];
1815f47be805Sstsp 		for (i = 0; i < tsl->count && i < nitems(tsl->ts); i++) {
1816f47be805Sstsp 			struct ieee80211com *ic = &usc->sc_sc.sc_ic;
1817f47be805Sstsp 			struct athn_node *an = (struct athn_node *)ic->ic_bss;
1818f47be805Sstsp 			struct ar_wmi_evt_txstatus *ts = &tsl->ts[i];
1819f47be805Sstsp 			uint8_t qid;
1820f47be805Sstsp 
1821f47be805Sstsp 			/* Skip the node we use to send management frames. */
1822f47be805Sstsp 			if (ts->cookie == 0)
1823f47be805Sstsp 				continue;
1824f47be805Sstsp 
1825f47be805Sstsp 			/* Skip Tx reports for non-data frame endpoints. */
1826f47be805Sstsp 			qid = (ts->rate & AR_HTC_TXSTAT_EPID) >>
1827f47be805Sstsp 				AR_HTC_TXSTAT_EPID_SHIFT;
1828f47be805Sstsp 			if (qid != usc->ep_data[EDCA_AC_BE] &&
1829f47be805Sstsp 			    qid != usc->ep_data[EDCA_AC_BK] &&
1830f47be805Sstsp 			    qid != usc->ep_data[EDCA_AC_VI] &&
1831f47be805Sstsp 			    qid != usc->ep_data[EDCA_AC_VO])
1832f47be805Sstsp 				continue;
1833f47be805Sstsp 
1834f47be805Sstsp 			if (ts->cookie == an->sta_index)
1835f47be805Sstsp 				athn_usb_tx_status(ts, ic->ic_bss);
1836f47be805Sstsp 			else
1837f47be805Sstsp 				ieee80211_iterate_nodes(ic, athn_usb_tx_status,
1838f47be805Sstsp 				    ts);
1839f47be805Sstsp 		}
184013236e8dSdamien 		break;
1841f47be805Sstsp 	}
184213236e8dSdamien 	case AR_WMI_EVT_FATAL:
184313236e8dSdamien 		printf("%s: fatal firmware error\n", usc->usb_dev.dv_xname);
184413236e8dSdamien 		break;
184513236e8dSdamien 	default:
184613236e8dSdamien 		DPRINTF(("WMI event %d ignored\n", cmd_id));
184713236e8dSdamien 		break;
184813236e8dSdamien 	}
184913236e8dSdamien }
185013236e8dSdamien 
185113236e8dSdamien void
1852ab0b1be7Smglocker athn_usb_intr(struct usbd_xfer *xfer, void *priv,
185313236e8dSdamien     usbd_status status)
185413236e8dSdamien {
185513236e8dSdamien 	struct athn_usb_softc *usc = priv;
185613236e8dSdamien 	struct ar_htc_frame_hdr *htc;
185713236e8dSdamien 	struct ar_htc_msg_hdr *msg;
185813236e8dSdamien 	uint8_t *buf = usc->ibuf;
185913236e8dSdamien 	uint16_t msg_id;
186013236e8dSdamien 	int len;
186113236e8dSdamien 
186213236e8dSdamien 	if (__predict_false(status != USBD_NORMAL_COMPLETION)) {
186313236e8dSdamien 		DPRINTF(("intr status=%d\n", status));
186413236e8dSdamien 		if (status == USBD_STALLED)
186513236e8dSdamien 			usbd_clear_endpoint_stall_async(usc->rx_intr_pipe);
1866d25f2410Sstsp 		else if (status == USBD_IOERROR) {
1867d25f2410Sstsp 			/*
1868d25f2410Sstsp 			 * The device has gone away. If async commands are
1869d25f2410Sstsp 			 * pending or running ensure the device dies ASAP
1870d25f2410Sstsp 			 * and any blocked processes are woken up.
1871d25f2410Sstsp 			 */
1872d25f2410Sstsp 			if (usc->cmdq.queued > 0)
1873d25f2410Sstsp 				usbd_deactivate(usc->sc_udev);
1874d25f2410Sstsp 		}
187513236e8dSdamien 		return;
187613236e8dSdamien 	}
187713236e8dSdamien 	usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
187813236e8dSdamien 
187913236e8dSdamien 	/* Skip watchdog pattern if present. */
188013236e8dSdamien 	if (len >= 4 && *(uint32_t *)buf == htobe32(0x00c60000)) {
188113236e8dSdamien 		buf += 4;
188213236e8dSdamien 		len -= 4;
188313236e8dSdamien 	}
188413236e8dSdamien 	if (__predict_false(len < sizeof(*htc)))
188513236e8dSdamien 		return;
188613236e8dSdamien 	htc = (struct ar_htc_frame_hdr *)buf;
188713236e8dSdamien 	/* Skip HTC header. */
188813236e8dSdamien 	buf += sizeof(*htc);
188913236e8dSdamien 	len -= sizeof(*htc);
189013236e8dSdamien 
189113236e8dSdamien 	if (htc->endpoint_id != 0) {
189213236e8dSdamien 		if (__predict_false(htc->endpoint_id != usc->ep_ctrl))
189313236e8dSdamien 			return;
189413236e8dSdamien 		/* Remove trailer if present. */
189513236e8dSdamien 		if (htc->flags & AR_HTC_FLAG_TRAILER) {
189613236e8dSdamien 			if (__predict_false(len < htc->control[0]))
189713236e8dSdamien 				return;
189813236e8dSdamien 			len -= htc->control[0];
189913236e8dSdamien 		}
190013236e8dSdamien 		athn_usb_rx_wmi_ctrl(usc, buf, len);
190113236e8dSdamien 		return;
190213236e8dSdamien 	}
190313236e8dSdamien 	/* Endpoint 0 carries HTC messages. */
190413236e8dSdamien 	if (__predict_false(len < sizeof(*msg)))
190513236e8dSdamien 		return;
190613236e8dSdamien 	msg = (struct ar_htc_msg_hdr *)buf;
190713236e8dSdamien 	msg_id = betoh16(msg->msg_id);
190813236e8dSdamien 	DPRINTF(("Rx HTC message %d\n", msg_id));
190913236e8dSdamien 	switch (msg_id) {
191013236e8dSdamien 	case AR_HTC_MSG_READY:
191113236e8dSdamien 		if (usc->wait_msg_id != msg_id)
191213236e8dSdamien 			break;
191313236e8dSdamien 		usc->wait_msg_id = 0;
191413236e8dSdamien 		wakeup(&usc->wait_msg_id);
191513236e8dSdamien 		break;
191613236e8dSdamien 	case AR_HTC_MSG_CONN_SVC_RSP:
191713236e8dSdamien 		if (usc->wait_msg_id != msg_id)
191813236e8dSdamien 			break;
191913236e8dSdamien 		if (usc->msg_conn_svc_rsp != NULL) {
192013236e8dSdamien 			memcpy(usc->msg_conn_svc_rsp, &msg[1],
192113236e8dSdamien 			    sizeof(struct ar_htc_msg_conn_svc_rsp));
192213236e8dSdamien 		}
192313236e8dSdamien 		usc->wait_msg_id = 0;
192413236e8dSdamien 		wakeup(&usc->wait_msg_id);
192513236e8dSdamien 		break;
192613236e8dSdamien 	case AR_HTC_MSG_CONF_PIPE_RSP:
192713236e8dSdamien 		if (usc->wait_msg_id != msg_id)
192813236e8dSdamien 			break;
192913236e8dSdamien 		usc->wait_msg_id = 0;
193013236e8dSdamien 		wakeup(&usc->wait_msg_id);
193113236e8dSdamien 		break;
193213236e8dSdamien 	default:
193313236e8dSdamien 		DPRINTF(("HTC message %d ignored\n", msg_id));
193413236e8dSdamien 		break;
193513236e8dSdamien 	}
193613236e8dSdamien }
193713236e8dSdamien 
193813236e8dSdamien #if NBPFILTER > 0
193913236e8dSdamien void
194013236e8dSdamien athn_usb_rx_radiotap(struct athn_softc *sc, struct mbuf *m,
194113236e8dSdamien     struct ar_rx_status *rs)
194213236e8dSdamien {
194313236e8dSdamien #define IEEE80211_RADIOTAP_F_SHORTGI	0x80	/* XXX from FBSD */
194413236e8dSdamien 
194513236e8dSdamien 	struct athn_rx_radiotap_header *tap = &sc->sc_rxtap;
194613236e8dSdamien 	struct ieee80211com *ic = &sc->sc_ic;
194713236e8dSdamien 	struct mbuf mb;
194813236e8dSdamien 	uint8_t rate;
194913236e8dSdamien 
195013236e8dSdamien 	tap->wr_flags = IEEE80211_RADIOTAP_F_FCS;
195113236e8dSdamien 	tap->wr_tsft = htole64(betoh64(rs->rs_tstamp));
195213236e8dSdamien 	tap->wr_chan_freq = htole16(ic->ic_bss->ni_chan->ic_freq);
195313236e8dSdamien 	tap->wr_chan_flags = htole16(ic->ic_bss->ni_chan->ic_flags);
195413236e8dSdamien 	tap->wr_dbm_antsignal = rs->rs_rssi;
195513236e8dSdamien 	/* XXX noise. */
195613236e8dSdamien 	tap->wr_antenna = rs->rs_antenna;
195713236e8dSdamien 	tap->wr_rate = 0;	/* In case it can't be found below. */
195813236e8dSdamien 	rate = rs->rs_rate;
195913236e8dSdamien 	if (rate & 0x80) {		/* HT. */
196013236e8dSdamien 		/* Bit 7 set means HT MCS instead of rate. */
196113236e8dSdamien 		tap->wr_rate = rate;
196213236e8dSdamien 		if (!(rs->rs_flags & AR_RXS_FLAG_GI))
196313236e8dSdamien 			tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTGI;
196413236e8dSdamien 
196513236e8dSdamien 	} else if (rate & 0x10) {	/* CCK. */
196613236e8dSdamien 		if (rate & 0x04)
196713236e8dSdamien 			tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
196813236e8dSdamien 		switch (rate & ~0x14) {
196913236e8dSdamien 		case 0xb: tap->wr_rate =   2; break;
197013236e8dSdamien 		case 0xa: tap->wr_rate =   4; break;
197113236e8dSdamien 		case 0x9: tap->wr_rate =  11; break;
197213236e8dSdamien 		case 0x8: tap->wr_rate =  22; break;
197313236e8dSdamien 		}
197413236e8dSdamien 	} else {			/* OFDM. */
197513236e8dSdamien 		switch (rate) {
197613236e8dSdamien 		case 0xb: tap->wr_rate =  12; break;
197713236e8dSdamien 		case 0xf: tap->wr_rate =  18; break;
197813236e8dSdamien 		case 0xa: tap->wr_rate =  24; break;
197913236e8dSdamien 		case 0xe: tap->wr_rate =  36; break;
198013236e8dSdamien 		case 0x9: tap->wr_rate =  48; break;
198113236e8dSdamien 		case 0xd: tap->wr_rate =  72; break;
198213236e8dSdamien 		case 0x8: tap->wr_rate =  96; break;
198313236e8dSdamien 		case 0xc: tap->wr_rate = 108; break;
198413236e8dSdamien 		}
198513236e8dSdamien 	}
198613236e8dSdamien 	mb.m_data = (caddr_t)tap;
198713236e8dSdamien 	mb.m_len = sc->sc_rxtap_len;
198813236e8dSdamien 	mb.m_next = m;
198913236e8dSdamien 	mb.m_nextpkt = NULL;
199013236e8dSdamien 	mb.m_type = 0;
199113236e8dSdamien 	mb.m_flags = 0;
199213236e8dSdamien 	bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_IN);
199313236e8dSdamien }
199413236e8dSdamien #endif
199513236e8dSdamien 
199613236e8dSdamien void
199713236e8dSdamien athn_usb_rx_frame(struct athn_usb_softc *usc, struct mbuf *m)
199813236e8dSdamien {
199913236e8dSdamien 	struct athn_softc *sc = &usc->sc_sc;
200013236e8dSdamien 	struct ieee80211com *ic = &sc->sc_ic;
200113236e8dSdamien 	struct ifnet *ifp = &ic->ic_if;
200213236e8dSdamien 	struct ieee80211_frame *wh;
200313236e8dSdamien 	struct ieee80211_node *ni;
200413236e8dSdamien 	struct ieee80211_rxinfo rxi;
200513236e8dSdamien 	struct ar_htc_frame_hdr *htc;
200613236e8dSdamien 	struct ar_rx_status *rs;
200713236e8dSdamien 	uint16_t datalen;
200833c953a9Smikeb 	int s;
200913236e8dSdamien 
2010d82c2cdfSdamien 	if (__predict_false(m->m_len < sizeof(*htc)))
2011d82c2cdfSdamien 		goto skip;
201213236e8dSdamien 	htc = mtod(m, struct ar_htc_frame_hdr *);
201313236e8dSdamien 	if (__predict_false(htc->endpoint_id == 0)) {
201413236e8dSdamien 		DPRINTF(("bad endpoint %d\n", htc->endpoint_id));
2015d82c2cdfSdamien 		goto skip;
201613236e8dSdamien 	}
201713236e8dSdamien 	if (htc->flags & AR_HTC_FLAG_TRAILER) {
2018d82c2cdfSdamien 		if (m->m_len < htc->control[0])
2019d82c2cdfSdamien 			goto skip;
202013236e8dSdamien 		m_adj(m, -(int)htc->control[0]);
202113236e8dSdamien 	}
202213236e8dSdamien 	m_adj(m, sizeof(*htc));	/* Strip HTC header. */
2023d82c2cdfSdamien 
2024d82c2cdfSdamien 	if (__predict_false(m->m_len < sizeof(*rs)))
2025d82c2cdfSdamien 		goto skip;
202613236e8dSdamien 	rs = mtod(m, struct ar_rx_status *);
2027d82c2cdfSdamien 
202813236e8dSdamien 	/* Make sure that payload fits. */
202913236e8dSdamien 	datalen = betoh16(rs->rs_datalen);
2030d82c2cdfSdamien 	if (__predict_false(m->m_len < sizeof(*rs) + datalen))
2031d82c2cdfSdamien 		goto skip;
2032d82c2cdfSdamien 
2033d82c2cdfSdamien 	if (__predict_false(datalen < sizeof(*wh) + IEEE80211_CRC_LEN))
2034d82c2cdfSdamien 		goto skip;
2035d82c2cdfSdamien 
203613236e8dSdamien 	m_adj(m, sizeof(*rs));	/* Strip Rx status. */
203713236e8dSdamien 
203833c953a9Smikeb 	s = splnet();
203933c953a9Smikeb 
2040d82c2cdfSdamien 	/* Grab a reference to the source node. */
2041d82c2cdfSdamien 	wh = mtod(m, struct ieee80211_frame *);
2042d82c2cdfSdamien 	ni = ieee80211_find_rxnode(ic, wh);
204313236e8dSdamien 
2044d82c2cdfSdamien 	/* Remove any HW padding after the 802.11 header. */
2045d82c2cdfSdamien 	if (!(wh->i_fc[0] & IEEE80211_FC0_TYPE_CTL)) {
2046d82c2cdfSdamien 		u_int hdrlen = ieee80211_get_hdrlen(wh);
2047d82c2cdfSdamien 		if (hdrlen & 3) {
20489b2ad23fSderaadt 			memmove((caddr_t)wh + 2, wh, hdrlen);
2049d82c2cdfSdamien 			m_adj(m, 2);
2050d82c2cdfSdamien 		}
2051d82c2cdfSdamien 	}
205213236e8dSdamien #if NBPFILTER > 0
205313236e8dSdamien 	if (__predict_false(sc->sc_drvbpf != NULL))
205413236e8dSdamien 		athn_usb_rx_radiotap(sc, m, rs);
205513236e8dSdamien #endif
205613236e8dSdamien 	/* Trim 802.11 FCS after radiotap. */
205713236e8dSdamien 	m_adj(m, -IEEE80211_CRC_LEN);
205813236e8dSdamien 
2059d82c2cdfSdamien 	/* Send the frame to the 802.11 layer. */
206013236e8dSdamien 	rxi.rxi_flags = 0;
206113236e8dSdamien 	rxi.rxi_rssi = rs->rs_rssi + AR_USB_DEFAULT_NF;
206213236e8dSdamien 	rxi.rxi_tstamp = betoh64(rs->rs_tstamp);
206313236e8dSdamien 	ieee80211_input(ifp, m, ni, &rxi);
2064d82c2cdfSdamien 
206513236e8dSdamien 	/* Node is no longer needed. */
206613236e8dSdamien 	ieee80211_release_node(ic, ni);
206733c953a9Smikeb 	splx(s);
2068d82c2cdfSdamien 	return;
2069d82c2cdfSdamien  skip:
2070d82c2cdfSdamien 	m_freem(m);
207113236e8dSdamien }
207213236e8dSdamien 
207313236e8dSdamien void
2074ab0b1be7Smglocker athn_usb_rxeof(struct usbd_xfer *xfer, void *priv,
207513236e8dSdamien     usbd_status status)
207613236e8dSdamien {
207713236e8dSdamien 	struct athn_usb_rx_data *data = priv;
207813236e8dSdamien 	struct athn_usb_softc *usc = data->sc;
20793fe3de2aSstsp 	struct athn_softc *sc = &usc->sc_sc;
20803fe3de2aSstsp 	struct ifnet *ifp = &sc->sc_ic.ic_if;
208113236e8dSdamien 	struct athn_usb_rx_stream *stream = &usc->rx_stream;
208213236e8dSdamien 	uint8_t *buf = data->buf;
208313236e8dSdamien 	struct ar_stream_hdr *hdr;
208413236e8dSdamien 	struct mbuf *m;
208513236e8dSdamien 	uint16_t pktlen;
208613236e8dSdamien 	int off, len;
208713236e8dSdamien 
208813236e8dSdamien 	if (__predict_false(status != USBD_NORMAL_COMPLETION)) {
208913236e8dSdamien 		DPRINTF(("RX status=%d\n", status));
209013236e8dSdamien 		if (status == USBD_STALLED)
209113236e8dSdamien 			usbd_clear_endpoint_stall_async(usc->rx_data_pipe);
209213236e8dSdamien 		if (status != USBD_CANCELLED)
209313236e8dSdamien 			goto resubmit;
209413236e8dSdamien 		return;
209513236e8dSdamien 	}
209613236e8dSdamien 	usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
209713236e8dSdamien 
209813236e8dSdamien 	if (stream->left > 0) {
209913236e8dSdamien 		if (len >= stream->left) {
210013236e8dSdamien 			/* We have all our pktlen bytes now. */
210113236e8dSdamien 			if (__predict_true(stream->m != NULL)) {
210213236e8dSdamien 				memcpy(mtod(stream->m, uint8_t *) +
210313236e8dSdamien 				    stream->moff, buf, stream->left);
210413236e8dSdamien 				athn_usb_rx_frame(usc, stream->m);
210513236e8dSdamien 				stream->m = NULL;
210613236e8dSdamien 			}
210713236e8dSdamien 			/* Next header is 32-bit aligned. */
210813236e8dSdamien 			off = (stream->left + 3) & ~3;
210913236e8dSdamien 			buf += off;
211013236e8dSdamien 			len -= off;
211113236e8dSdamien 			stream->left = 0;
211213236e8dSdamien 		} else {
211313236e8dSdamien 			/* Still need more bytes, save what we have. */
211413236e8dSdamien 			if (__predict_true(stream->m != NULL)) {
211513236e8dSdamien 				memcpy(mtod(stream->m, uint8_t *) +
211613236e8dSdamien 				    stream->moff, buf, len);
211713236e8dSdamien 				stream->moff += len;
211813236e8dSdamien 			}
211913236e8dSdamien 			stream->left -= len;
212013236e8dSdamien 			goto resubmit;
212113236e8dSdamien 		}
212213236e8dSdamien 	}
212313236e8dSdamien 	KASSERT(stream->left == 0);
212413236e8dSdamien 	while (len >= sizeof(*hdr)) {
212513236e8dSdamien 		hdr = (struct ar_stream_hdr *)buf;
212613236e8dSdamien 		if (hdr->tag != htole16(AR_USB_RX_STREAM_TAG)) {
212713236e8dSdamien 			DPRINTF(("invalid tag 0x%x\n", hdr->tag));
212813236e8dSdamien 			break;
212913236e8dSdamien 		}
213013236e8dSdamien 		pktlen = letoh16(hdr->len);
213113236e8dSdamien 		buf += sizeof(*hdr);
213213236e8dSdamien 		len -= sizeof(*hdr);
213313236e8dSdamien 
213413236e8dSdamien 		if (__predict_true(pktlen <= MCLBYTES)) {
213513236e8dSdamien 			/* Allocate an mbuf to store the next pktlen bytes. */
213613236e8dSdamien 			MGETHDR(m, M_DONTWAIT, MT_DATA);
213713236e8dSdamien 			if (__predict_true(m != NULL)) {
213813236e8dSdamien 				m->m_pkthdr.len = m->m_len = pktlen;
213913236e8dSdamien 				if (pktlen > MHLEN) {
214013236e8dSdamien 					MCLGET(m, M_DONTWAIT);
214113236e8dSdamien 					if (!(m->m_flags & M_EXT)) {
214213236e8dSdamien 						m_free(m);
214313236e8dSdamien 						m = NULL;
214413236e8dSdamien 					}
214513236e8dSdamien 				}
214613236e8dSdamien 			}
214713236e8dSdamien 		} else	/* Drop frames larger than MCLBYTES. */
214813236e8dSdamien 			m = NULL;
21493fe3de2aSstsp 
21503fe3de2aSstsp 		if (m == NULL)
21513fe3de2aSstsp 			ifp->if_ierrors++;
21523fe3de2aSstsp 
215313236e8dSdamien 		/*
215413236e8dSdamien 		 * NB: m can be NULL, in which case the next pktlen bytes
215513236e8dSdamien 		 * will be discarded from the Rx stream.
215613236e8dSdamien 		 */
215713236e8dSdamien 		if (pktlen > len) {
215813236e8dSdamien 			/* Need more bytes, save what we have. */
215913236e8dSdamien 			stream->m = m;	/* NB: m can be NULL. */
216013236e8dSdamien 			if (__predict_true(stream->m != NULL)) {
216113236e8dSdamien 				memcpy(mtod(stream->m, uint8_t *), buf, len);
216213236e8dSdamien 				stream->moff = len;
216313236e8dSdamien 			}
216413236e8dSdamien 			stream->left = pktlen - len;
216513236e8dSdamien 			goto resubmit;
216613236e8dSdamien 		}
216713236e8dSdamien 		if (__predict_true(m != NULL)) {
216813236e8dSdamien 			/* We have all the pktlen bytes in this xfer. */
216913236e8dSdamien 			memcpy(mtod(m, uint8_t *), buf, pktlen);
217013236e8dSdamien 			athn_usb_rx_frame(usc, m);
217113236e8dSdamien 		}
217213236e8dSdamien 
217313236e8dSdamien 		/* Next header is 32-bit aligned. */
217413236e8dSdamien 		off = (pktlen + 3) & ~3;
217513236e8dSdamien 		buf += off;
217613236e8dSdamien 		len -= off;
217713236e8dSdamien 	}
217813236e8dSdamien 
217913236e8dSdamien  resubmit:
218013236e8dSdamien 	/* Setup a new transfer. */
218113236e8dSdamien 	usbd_setup_xfer(xfer, usc->rx_data_pipe, data, data->buf,
218213236e8dSdamien 	    ATHN_USB_RXBUFSZ, USBD_SHORT_XFER_OK | USBD_NO_COPY,
218313236e8dSdamien 	    USBD_NO_TIMEOUT, athn_usb_rxeof);
218413236e8dSdamien 	(void)usbd_transfer(xfer);
218513236e8dSdamien }
218613236e8dSdamien 
218713236e8dSdamien void
2188ab0b1be7Smglocker athn_usb_txeof(struct usbd_xfer *xfer, void *priv,
218913236e8dSdamien     usbd_status status)
219013236e8dSdamien {
219113236e8dSdamien 	struct athn_usb_tx_data *data = priv;
219213236e8dSdamien 	struct athn_usb_softc *usc = data->sc;
219313236e8dSdamien 	struct athn_softc *sc = &usc->sc_sc;
219413236e8dSdamien 	struct ifnet *ifp = &sc->sc_ic.ic_if;
219513236e8dSdamien 	int s;
219613236e8dSdamien 
219713236e8dSdamien 	s = splnet();
219813236e8dSdamien 	/* Put this Tx buffer back to our free list. */
219913236e8dSdamien 	TAILQ_INSERT_TAIL(&usc->tx_free_list, data, next);
220013236e8dSdamien 
220113236e8dSdamien 	if (__predict_false(status != USBD_NORMAL_COMPLETION)) {
220213236e8dSdamien 		DPRINTF(("TX status=%d\n", status));
220313236e8dSdamien 		if (status == USBD_STALLED)
220413236e8dSdamien 			usbd_clear_endpoint_stall_async(usc->tx_data_pipe);
220513236e8dSdamien 		ifp->if_oerrors++;
220613236e8dSdamien 		splx(s);
220713236e8dSdamien 		/* XXX Why return? */
220813236e8dSdamien 		return;
220913236e8dSdamien 	}
221013236e8dSdamien 	sc->sc_tx_timer = 0;
221113236e8dSdamien 
221213236e8dSdamien 	/* We just released a Tx buffer, notify Tx. */
2213de6cd8fbSdlg 	if (ifq_is_oactive(&ifp->if_snd)) {
2214de6cd8fbSdlg 		ifq_clr_oactive(&ifp->if_snd);
221513236e8dSdamien 		ifp->if_start(ifp);
221613236e8dSdamien 	}
221713236e8dSdamien 	splx(s);
221813236e8dSdamien }
221913236e8dSdamien 
222013236e8dSdamien int
222113236e8dSdamien athn_usb_tx(struct athn_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
222213236e8dSdamien {
222313236e8dSdamien 	struct athn_usb_softc *usc = (struct athn_usb_softc *)sc;
222413236e8dSdamien 	struct athn_node *an = (struct athn_node *)ni;
222513236e8dSdamien 	struct ieee80211com *ic = &sc->sc_ic;
222613236e8dSdamien 	struct ieee80211_frame *wh;
222713236e8dSdamien 	struct ieee80211_key *k = NULL;
222813236e8dSdamien 	struct athn_usb_tx_data *data;
222913236e8dSdamien 	struct ar_stream_hdr *hdr;
223013236e8dSdamien 	struct ar_htc_frame_hdr *htc;
223113236e8dSdamien 	struct ar_tx_frame *txf;
223213236e8dSdamien 	struct ar_tx_mgmt *txm;
223313236e8dSdamien 	uint8_t *frm;
223413236e8dSdamien 	uint16_t qos;
2235f47be805Sstsp 	uint8_t qid, tid = 0;
223613236e8dSdamien 	int hasqos, xferlen, error;
223713236e8dSdamien 
223813236e8dSdamien 	wh = mtod(m, struct ieee80211_frame *);
223913236e8dSdamien 	if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
224013236e8dSdamien 		k = ieee80211_get_txkey(ic, wh, ni);
224113236e8dSdamien 		if ((m = ieee80211_encrypt(ic, m, k)) == NULL)
224213236e8dSdamien 			return (ENOBUFS);
224313236e8dSdamien 		wh = mtod(m, struct ieee80211_frame *);
224413236e8dSdamien 	}
224513236e8dSdamien 	if ((hasqos = ieee80211_has_qos(wh))) {
224613236e8dSdamien 		qos = ieee80211_get_qos(wh);
224713236e8dSdamien 		tid = qos & IEEE80211_QOS_TID;
224813236e8dSdamien 		qid = ieee80211_up_to_ac(ic, tid);
224913236e8dSdamien 	} else
225013236e8dSdamien 		qid = EDCA_AC_BE;
225113236e8dSdamien 
225213236e8dSdamien 	/* Grab a Tx buffer from our free list. */
225313236e8dSdamien 	data = TAILQ_FIRST(&usc->tx_free_list);
225413236e8dSdamien 	TAILQ_REMOVE(&usc->tx_free_list, data, next);
225513236e8dSdamien 
225613236e8dSdamien #if NBPFILTER > 0
225713236e8dSdamien 	/* XXX Change radiotap Tx header for USB (no txrate). */
225813236e8dSdamien 	if (__predict_false(sc->sc_drvbpf != NULL)) {
225913236e8dSdamien 		struct athn_tx_radiotap_header *tap = &sc->sc_txtap;
226013236e8dSdamien 		struct mbuf mb;
226113236e8dSdamien 
226213236e8dSdamien 		tap->wt_flags = 0;
226313236e8dSdamien 		tap->wt_chan_freq = htole16(ic->ic_bss->ni_chan->ic_freq);
226413236e8dSdamien 		tap->wt_chan_flags = htole16(ic->ic_bss->ni_chan->ic_flags);
226513236e8dSdamien 		mb.m_data = (caddr_t)tap;
226613236e8dSdamien 		mb.m_len = sc->sc_txtap_len;
226713236e8dSdamien 		mb.m_next = m;
226813236e8dSdamien 		mb.m_nextpkt = NULL;
226913236e8dSdamien 		mb.m_type = 0;
227013236e8dSdamien 		mb.m_flags = 0;
227113236e8dSdamien 		bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_OUT);
227213236e8dSdamien 	}
227313236e8dSdamien #endif
227413236e8dSdamien 
227513236e8dSdamien 	/* NB: We don't take advantage of USB Tx stream mode for now. */
227613236e8dSdamien 	hdr = (struct ar_stream_hdr *)data->buf;
227713236e8dSdamien 	hdr->tag = htole16(AR_USB_TX_STREAM_TAG);
227813236e8dSdamien 
227913236e8dSdamien 	htc = (struct ar_htc_frame_hdr *)&hdr[1];
228013236e8dSdamien 	memset(htc, 0, sizeof(*htc));
228113236e8dSdamien 	if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
228213236e8dSdamien 	    IEEE80211_FC0_TYPE_DATA) {
228313236e8dSdamien 		htc->endpoint_id = usc->ep_data[qid];
228413236e8dSdamien 
228513236e8dSdamien 		txf = (struct ar_tx_frame *)&htc[1];
228613236e8dSdamien 		memset(txf, 0, sizeof(*txf));
228713236e8dSdamien 		txf->data_type = AR_HTC_NORMAL;
2288f47be805Sstsp 		txf->node_idx = an->sta_index;
228913236e8dSdamien 		txf->vif_idx = 0;
229013236e8dSdamien 		txf->tid = tid;
229113236e8dSdamien 		if (m->m_pkthdr.len + IEEE80211_CRC_LEN > ic->ic_rtsthreshold)
229213236e8dSdamien 			txf->flags |= htobe32(AR_HTC_TX_RTSCTS);
229313236e8dSdamien 		else if (ic->ic_flags & IEEE80211_F_USEPROT) {
229413236e8dSdamien 			if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
229513236e8dSdamien 				txf->flags |= htobe32(AR_HTC_TX_CTSONLY);
229613236e8dSdamien 			else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
229713236e8dSdamien 				txf->flags |= htobe32(AR_HTC_TX_RTSCTS);
229813236e8dSdamien 		}
229913236e8dSdamien 		txf->key_idx = 0xff;
2300f47be805Sstsp 		txf->cookie = an->sta_index;
230113236e8dSdamien 		frm = (uint8_t *)&txf[1];
230213236e8dSdamien 	} else {
230313236e8dSdamien 		htc->endpoint_id = usc->ep_mgmt;
230413236e8dSdamien 
230513236e8dSdamien 		txm = (struct ar_tx_mgmt *)&htc[1];
230613236e8dSdamien 		memset(txm, 0, sizeof(*txm));
2307f47be805Sstsp 		txm->node_idx = an->sta_index;
230813236e8dSdamien 		txm->vif_idx = 0;
230913236e8dSdamien 		txm->key_idx = 0xff;
2310f47be805Sstsp 		txm->cookie = an->sta_index;
231113236e8dSdamien 		frm = (uint8_t *)&txm[1];
231213236e8dSdamien 	}
231313236e8dSdamien 	/* Copy payload. */
231413236e8dSdamien 	m_copydata(m, 0, m->m_pkthdr.len, (caddr_t)frm);
231513236e8dSdamien 	frm += m->m_pkthdr.len;
231613236e8dSdamien 	m_freem(m);
231713236e8dSdamien 
231813236e8dSdamien 	/* Finalize headers. */
231913236e8dSdamien 	htc->payload_len = htobe16(frm - (uint8_t *)&htc[1]);
232013236e8dSdamien 	hdr->len = htole16(frm - (uint8_t *)&hdr[1]);
232113236e8dSdamien 	xferlen = frm - data->buf;
232213236e8dSdamien 
232313236e8dSdamien 	usbd_setup_xfer(data->xfer, usc->tx_data_pipe, data, data->buf,
232413236e8dSdamien 	    xferlen, USBD_FORCE_SHORT_XFER | USBD_NO_COPY, ATHN_USB_TX_TIMEOUT,
232513236e8dSdamien 	    athn_usb_txeof);
232613236e8dSdamien 	error = usbd_transfer(data->xfer);
232713236e8dSdamien 	if (__predict_false(error != USBD_IN_PROGRESS && error != 0)) {
232813236e8dSdamien 		/* Put this Tx buffer back to our free list. */
232913236e8dSdamien 		TAILQ_INSERT_TAIL(&usc->tx_free_list, data, next);
233013236e8dSdamien 		return (error);
233113236e8dSdamien 	}
233213236e8dSdamien 	ieee80211_release_node(ic, ni);
233313236e8dSdamien 	return (0);
233413236e8dSdamien }
233513236e8dSdamien 
233613236e8dSdamien void
233713236e8dSdamien athn_usb_start(struct ifnet *ifp)
233813236e8dSdamien {
233913236e8dSdamien 	struct athn_softc *sc = ifp->if_softc;
234013236e8dSdamien 	struct athn_usb_softc *usc = (struct athn_usb_softc *)sc;
234113236e8dSdamien 	struct ieee80211com *ic = &sc->sc_ic;
234213236e8dSdamien 	struct ieee80211_node *ni;
234313236e8dSdamien 	struct mbuf *m;
234413236e8dSdamien 
2345de6cd8fbSdlg 	if (!(ifp->if_flags & IFF_RUNNING) || ifq_is_oactive(&ifp->if_snd))
234613236e8dSdamien 		return;
234713236e8dSdamien 
234813236e8dSdamien 	for (;;) {
234913236e8dSdamien 		if (TAILQ_EMPTY(&usc->tx_free_list)) {
2350de6cd8fbSdlg 			ifq_set_oactive(&ifp->if_snd);
235113236e8dSdamien 			break;
235213236e8dSdamien 		}
235313236e8dSdamien 		/* Send pending management frames first. */
2354351e1934Sdlg 		m = mq_dequeue(&ic->ic_mgtq);
235513236e8dSdamien 		if (m != NULL) {
23566da4b19dSmpi 			ni = m->m_pkthdr.ph_cookie;
235713236e8dSdamien 			goto sendit;
235813236e8dSdamien 		}
235913236e8dSdamien 		if (ic->ic_state != IEEE80211_S_RUN)
236013236e8dSdamien 			break;
236113236e8dSdamien 
236213236e8dSdamien 		/* Encapsulate and send data frames. */
236313236e8dSdamien 		IFQ_DEQUEUE(&ifp->if_snd, m);
236413236e8dSdamien 		if (m == NULL)
236513236e8dSdamien 			break;
236613236e8dSdamien #if NBPFILTER > 0
236713236e8dSdamien 		if (ifp->if_bpf != NULL)
236813236e8dSdamien 			bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
236913236e8dSdamien #endif
237013236e8dSdamien 		if ((m = ieee80211_encap(ifp, m, &ni)) == NULL)
237113236e8dSdamien 			continue;
237213236e8dSdamien  sendit:
237313236e8dSdamien #if NBPFILTER > 0
237413236e8dSdamien 		if (ic->ic_rawbpf != NULL)
237513236e8dSdamien 			bpf_mtap(ic->ic_rawbpf, m, BPF_DIRECTION_OUT);
237613236e8dSdamien #endif
237713236e8dSdamien 		if (athn_usb_tx(sc, m, ni) != 0) {
237813236e8dSdamien 			ieee80211_release_node(ic, ni);
237913236e8dSdamien 			ifp->if_oerrors++;
238013236e8dSdamien 			continue;
238113236e8dSdamien 		}
238213236e8dSdamien 
238313236e8dSdamien 		sc->sc_tx_timer = 5;
238413236e8dSdamien 		ifp->if_timer = 1;
238513236e8dSdamien 	}
238613236e8dSdamien }
238713236e8dSdamien 
238813236e8dSdamien void
238913236e8dSdamien athn_usb_watchdog(struct ifnet *ifp)
239013236e8dSdamien {
239113236e8dSdamien 	struct athn_softc *sc = ifp->if_softc;
239213236e8dSdamien 
239313236e8dSdamien 	ifp->if_timer = 0;
239413236e8dSdamien 
239513236e8dSdamien 	if (sc->sc_tx_timer > 0) {
239613236e8dSdamien 		if (--sc->sc_tx_timer == 0) {
239713236e8dSdamien 			printf("%s: device timeout\n", sc->sc_dev.dv_xname);
239813236e8dSdamien 			/* athn_usb_init(ifp); XXX needs a process context! */
239913236e8dSdamien 			ifp->if_oerrors++;
240013236e8dSdamien 			return;
240113236e8dSdamien 		}
240213236e8dSdamien 		ifp->if_timer = 1;
240313236e8dSdamien 	}
240413236e8dSdamien 	ieee80211_watchdog(ifp);
240513236e8dSdamien }
240613236e8dSdamien 
240713236e8dSdamien int
240813236e8dSdamien athn_usb_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
240913236e8dSdamien {
241013236e8dSdamien 	struct athn_softc *sc = ifp->if_softc;
2411dfa43ce4Sstsp 	struct athn_usb_softc *usc = (struct athn_usb_softc *)sc;
241213236e8dSdamien 	struct ieee80211com *ic = &sc->sc_ic;
241313236e8dSdamien 	int s, error = 0;
241413236e8dSdamien 
2415dfa43ce4Sstsp 	if (usbd_is_dying(usc->sc_udev))
2416dfa43ce4Sstsp 		return ENXIO;
2417dfa43ce4Sstsp 
2418dfa43ce4Sstsp 	usbd_ref_incr(usc->sc_udev);
2419dfa43ce4Sstsp 
242013236e8dSdamien 	s = splnet();
242113236e8dSdamien 
242213236e8dSdamien 	switch (cmd) {
242313236e8dSdamien 	case SIOCSIFADDR:
242413236e8dSdamien 		ifp->if_flags |= IFF_UP;
242513236e8dSdamien 		/* FALLTHROUGH */
242613236e8dSdamien 	case SIOCSIFFLAGS:
242713236e8dSdamien 		if (ifp->if_flags & IFF_UP) {
242813236e8dSdamien 			if (!(ifp->if_flags & IFF_RUNNING))
242913236e8dSdamien 				error = athn_usb_init(ifp);
243013236e8dSdamien 		} else {
243113236e8dSdamien 			if (ifp->if_flags & IFF_RUNNING)
243213236e8dSdamien 				athn_usb_stop(ifp);
243313236e8dSdamien 		}
243413236e8dSdamien 		break;
243513236e8dSdamien 	case SIOCS80211CHANNEL:
243613236e8dSdamien 		error = ieee80211_ioctl(ifp, cmd, data);
243713236e8dSdamien 		if (error == ENETRESET &&
243813236e8dSdamien 		    ic->ic_opmode == IEEE80211_M_MONITOR) {
243913236e8dSdamien 			if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
244013236e8dSdamien 			    (IFF_UP | IFF_RUNNING)) {
244113236e8dSdamien 				athn_usb_switch_chan(sc, ic->ic_ibss_chan,
244213236e8dSdamien 				    NULL);
244313236e8dSdamien 			}
244413236e8dSdamien 			error = 0;
244513236e8dSdamien 		}
244613236e8dSdamien 		break;
244713236e8dSdamien 	default:
244813236e8dSdamien 		error = ieee80211_ioctl(ifp, cmd, data);
244913236e8dSdamien 	}
245013236e8dSdamien 
245113236e8dSdamien 	if (error == ENETRESET) {
245213236e8dSdamien 		error = 0;
245313236e8dSdamien 		if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
245413236e8dSdamien 		    (IFF_UP | IFF_RUNNING)) {
245513236e8dSdamien 			athn_usb_stop(ifp);
245613236e8dSdamien 			error = athn_usb_init(ifp);
245713236e8dSdamien 		}
245813236e8dSdamien 	}
245913236e8dSdamien 	splx(s);
2460dfa43ce4Sstsp 
2461dfa43ce4Sstsp 	usbd_ref_decr(usc->sc_udev);
2462dfa43ce4Sstsp 
246313236e8dSdamien 	return (error);
246413236e8dSdamien }
246513236e8dSdamien 
246613236e8dSdamien int
246713236e8dSdamien athn_usb_init(struct ifnet *ifp)
246813236e8dSdamien {
246913236e8dSdamien 	struct athn_softc *sc = ifp->if_softc;
247013236e8dSdamien 	struct athn_usb_softc *usc = (struct athn_usb_softc *)sc;
247113236e8dSdamien 	struct athn_ops *ops = &sc->ops;
247213236e8dSdamien 	struct ieee80211com *ic = &sc->sc_ic;
247313236e8dSdamien 	struct ieee80211_channel *c, *extc;
247413236e8dSdamien 	struct athn_usb_rx_data *data;
247513236e8dSdamien 	struct ar_htc_target_vif hvif;
247613236e8dSdamien 	struct ar_htc_target_sta sta;
247713236e8dSdamien 	struct ar_htc_cap_target hic;
247813236e8dSdamien 	uint16_t mode;
247913236e8dSdamien 	int i, error;
248013236e8dSdamien 
248113236e8dSdamien 	/* Init host async commands ring. */
248213236e8dSdamien 	usc->cmdq.cur = usc->cmdq.next = usc->cmdq.queued = 0;
248313236e8dSdamien 
248413236e8dSdamien 	/* Allocate Tx/Rx buffers. */
248513236e8dSdamien 	error = athn_usb_alloc_rx_list(usc);
248613236e8dSdamien 	if (error != 0)
248713236e8dSdamien 		goto fail;
248813236e8dSdamien 	error = athn_usb_alloc_tx_list(usc);
248913236e8dSdamien 	if (error != 0)
249013236e8dSdamien 		goto fail;
2491af7c78e3Sdamien 	/* Steal one buffer for beacons. */
2492af7c78e3Sdamien 	usc->tx_bcn = TAILQ_FIRST(&usc->tx_free_list);
2493af7c78e3Sdamien 	TAILQ_REMOVE(&usc->tx_free_list, usc->tx_bcn, next);
249413236e8dSdamien 
249513236e8dSdamien 	c = ic->ic_bss->ni_chan = ic->ic_ibss_chan;
249613236e8dSdamien 	extc = NULL;
249713236e8dSdamien 
249813236e8dSdamien 	/* In case a new MAC address has been configured. */
249913236e8dSdamien 	IEEE80211_ADDR_COPY(ic->ic_myaddr, LLADDR(ifp->if_sadl));
250013236e8dSdamien 
250113236e8dSdamien 	error = athn_set_power_awake(sc);
250213236e8dSdamien 	if (error != 0)
250313236e8dSdamien 		goto fail;
250413236e8dSdamien 
250513236e8dSdamien 	error = athn_usb_wmi_cmd(usc, AR_WMI_CMD_FLUSH_RECV);
250613236e8dSdamien 	if (error != 0)
250713236e8dSdamien 		goto fail;
250813236e8dSdamien 
250913236e8dSdamien 	error = athn_hw_reset(sc, c, extc, 1);
251013236e8dSdamien 	if (error != 0)
251113236e8dSdamien 		goto fail;
251213236e8dSdamien 
251313236e8dSdamien 	ops->set_txpower(sc, c, extc);
251413236e8dSdamien 
251513236e8dSdamien 	mode = htobe16(IEEE80211_IS_CHAN_2GHZ(c) ?
251613236e8dSdamien 	    AR_HTC_MODE_11NG : AR_HTC_MODE_11NA);
251713236e8dSdamien 	error = athn_usb_wmi_xcmd(usc, AR_WMI_CMD_SET_MODE,
251813236e8dSdamien 	    &mode, sizeof(mode), NULL);
251913236e8dSdamien 	if (error != 0)
252013236e8dSdamien 		goto fail;
252113236e8dSdamien 
252213236e8dSdamien 	error = athn_usb_wmi_cmd(usc, AR_WMI_CMD_ATH_INIT);
252313236e8dSdamien 	if (error != 0)
252413236e8dSdamien 		goto fail;
252513236e8dSdamien 
252603574ff2Sdamien 	error = athn_usb_wmi_cmd(usc, AR_WMI_CMD_START_RECV);
252703574ff2Sdamien 	if (error != 0)
252803574ff2Sdamien 		goto fail;
252903574ff2Sdamien 
253013236e8dSdamien 	athn_rx_start(sc);
253113236e8dSdamien 
253213236e8dSdamien 	/* Create main interface on target. */
253313236e8dSdamien 	memset(&hvif, 0, sizeof(hvif));
253413236e8dSdamien 	hvif.index = 0;
253513236e8dSdamien 	IEEE80211_ADDR_COPY(hvif.myaddr, ic->ic_myaddr);
253613236e8dSdamien 	switch (ic->ic_opmode) {
253713236e8dSdamien 	case IEEE80211_M_STA:
253813236e8dSdamien 		hvif.opmode = htobe32(AR_HTC_M_STA);
253913236e8dSdamien 		break;
254013236e8dSdamien 	case IEEE80211_M_MONITOR:
254113236e8dSdamien 		hvif.opmode = htobe32(AR_HTC_M_MONITOR);
254213236e8dSdamien 		break;
254313236e8dSdamien #ifndef IEEE80211_STA_ONLY
254413236e8dSdamien 	case IEEE80211_M_IBSS:
254513236e8dSdamien 		hvif.opmode = htobe32(AR_HTC_M_IBSS);
254613236e8dSdamien 		break;
254713236e8dSdamien 	case IEEE80211_M_AHDEMO:
254813236e8dSdamien 		hvif.opmode = htobe32(AR_HTC_M_AHDEMO);
254913236e8dSdamien 		break;
255013236e8dSdamien 	case IEEE80211_M_HOSTAP:
255113236e8dSdamien 		hvif.opmode = htobe32(AR_HTC_M_HOSTAP);
255213236e8dSdamien 		break;
255313236e8dSdamien #endif
255413236e8dSdamien 	}
255513236e8dSdamien 	hvif.rtsthreshold = htobe16(ic->ic_rtsthreshold);
255613236e8dSdamien 	DPRINTF(("creating VAP\n"));
255713236e8dSdamien 	error = athn_usb_wmi_xcmd(usc, AR_WMI_CMD_VAP_CREATE,
255813236e8dSdamien 	    &hvif, sizeof(hvif), NULL);
255913236e8dSdamien 	if (error != 0)
256013236e8dSdamien 		goto fail;
256113236e8dSdamien 
256213236e8dSdamien 	/* Create a fake node to send management frames before assoc. */
256313236e8dSdamien 	memset(&sta, 0, sizeof(sta));
256413236e8dSdamien 	IEEE80211_ADDR_COPY(sta.macaddr, ic->ic_myaddr);
256513236e8dSdamien 	sta.sta_index = 0;
256613236e8dSdamien 	sta.is_vif_sta = 1;
256713236e8dSdamien 	sta.vif_index = hvif.index;
256813236e8dSdamien 	sta.maxampdu = 0xffff;
256913236e8dSdamien 	DPRINTF(("creating default node\n"));
257013236e8dSdamien 	error = athn_usb_wmi_xcmd(usc, AR_WMI_CMD_NODE_CREATE,
257113236e8dSdamien 	    &sta, sizeof(sta), NULL);
257213236e8dSdamien 	if (error != 0)
257313236e8dSdamien 		goto fail;
2574f47be805Sstsp 	usc->free_node_slots = ~(1 << sta.sta_index);
257513236e8dSdamien 
257613236e8dSdamien 	/* Update target capabilities. */
257713236e8dSdamien 	memset(&hic, 0, sizeof(hic));
257813236e8dSdamien 	hic.ampdu_limit = htobe32(0x0000ffff);
257913236e8dSdamien 	hic.ampdu_subframes = 20;
2580f47be805Sstsp 	hic.txchainmask = sc->txchainmask;
258113236e8dSdamien 	DPRINTF(("updating target configuration\n"));
258213236e8dSdamien 	error = athn_usb_wmi_xcmd(usc, AR_WMI_CMD_TARGET_IC_UPDATE,
258313236e8dSdamien 	    &hic, sizeof(hic), NULL);
258413236e8dSdamien 	if (error != 0)
258513236e8dSdamien 		goto fail;
258613236e8dSdamien 
258713236e8dSdamien 	/* Queue Rx xfers. */
258813236e8dSdamien 	for (i = 0; i < ATHN_USB_RX_LIST_COUNT; i++) {
258913236e8dSdamien 		data = &usc->rx_data[i];
259013236e8dSdamien 
259113236e8dSdamien 		usbd_setup_xfer(data->xfer, usc->rx_data_pipe, data, data->buf,
259213236e8dSdamien 		    ATHN_USB_RXBUFSZ, USBD_SHORT_XFER_OK | USBD_NO_COPY,
259313236e8dSdamien 		    USBD_NO_TIMEOUT, athn_usb_rxeof);
259413236e8dSdamien 		error = usbd_transfer(data->xfer);
259513236e8dSdamien 		if (error != 0 && error != USBD_IN_PROGRESS)
259613236e8dSdamien 			goto fail;
259713236e8dSdamien 	}
259813236e8dSdamien 	/* We're ready to go. */
259913236e8dSdamien 	ifp->if_flags |= IFF_RUNNING;
2600de6cd8fbSdlg 	ifq_clr_oactive(&ifp->if_snd);
260113236e8dSdamien 
260213236e8dSdamien #ifdef notyet
260313236e8dSdamien 	if (ic->ic_flags & IEEE80211_F_WEPON) {
260413236e8dSdamien 		/* Install WEP keys. */
260513236e8dSdamien 		for (i = 0; i < IEEE80211_WEP_NKID; i++)
260613236e8dSdamien 			athn_usb_set_key(ic, NULL, &ic->ic_nw_keys[i]);
260713236e8dSdamien 	}
260813236e8dSdamien #endif
260913236e8dSdamien 	if (ic->ic_opmode == IEEE80211_M_MONITOR)
261013236e8dSdamien 		ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
261113236e8dSdamien 	else
261213236e8dSdamien 		ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
261313236e8dSdamien 	athn_usb_wait_async(usc);
261413236e8dSdamien 	return (0);
261513236e8dSdamien  fail:
261613236e8dSdamien 	athn_usb_stop(ifp);
261713236e8dSdamien 	return (error);
261813236e8dSdamien }
261913236e8dSdamien 
262013236e8dSdamien void
262113236e8dSdamien athn_usb_stop(struct ifnet *ifp)
262213236e8dSdamien {
262313236e8dSdamien 	struct athn_softc *sc = ifp->if_softc;
262413236e8dSdamien 	struct athn_usb_softc *usc = (struct athn_usb_softc *)sc;
262513236e8dSdamien 	struct ieee80211com *ic = &sc->sc_ic;
262613236e8dSdamien 	struct ar_htc_target_vif hvif;
262713236e8dSdamien 	uint8_t sta_index;
262813236e8dSdamien 	int s;
262913236e8dSdamien 
263013236e8dSdamien 	sc->sc_tx_timer = 0;
263113236e8dSdamien 	ifp->if_timer = 0;
2632de6cd8fbSdlg 	ifp->if_flags &= ~IFF_RUNNING;
2633de6cd8fbSdlg 	ifq_clr_oactive(&ifp->if_snd);
263413236e8dSdamien 
263513236e8dSdamien 	s = splusb();
263613236e8dSdamien 	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
263713236e8dSdamien 
263813236e8dSdamien 	/* Wait for all async commands to complete. */
263913236e8dSdamien 	athn_usb_wait_async(usc);
264013236e8dSdamien 
264113236e8dSdamien 	timeout_del(&sc->scan_to);
264213236e8dSdamien 	timeout_del(&sc->calib_to);
264313236e8dSdamien 
2644f47be805Sstsp 	/* Remove all non-default nodes. */
2645f47be805Sstsp 	for (sta_index = 1; sta_index < AR_USB_MAX_STA; sta_index++) {
2646f47be805Sstsp 		if (usc->free_node_slots & (1 << sta_index))
2647f47be805Sstsp 			continue;
2648f47be805Sstsp 		(void)athn_usb_wmi_xcmd(usc, AR_WMI_CMD_NODE_REMOVE,
2649f47be805Sstsp 		    &sta_index, sizeof(sta_index), NULL);
2650f47be805Sstsp 	}
2651f47be805Sstsp 
2652f47be805Sstsp 	/* Remove main interface. This also invalidates our default node. */
265313236e8dSdamien 	memset(&hvif, 0, sizeof(hvif));
265413236e8dSdamien 	hvif.index = 0;
265513236e8dSdamien 	IEEE80211_ADDR_COPY(hvif.myaddr, ic->ic_myaddr);
265613236e8dSdamien 	(void)athn_usb_wmi_xcmd(usc, AR_WMI_CMD_VAP_REMOVE,
265713236e8dSdamien 	    &hvif, sizeof(hvif), NULL);
2658f47be805Sstsp 
2659f47be805Sstsp 	usc->free_node_slots = 0xff;
266013236e8dSdamien 
266113236e8dSdamien 	(void)athn_usb_wmi_cmd(usc, AR_WMI_CMD_DISABLE_INTR);
266213236e8dSdamien 	(void)athn_usb_wmi_cmd(usc, AR_WMI_CMD_DRAIN_TXQ_ALL);
266313236e8dSdamien 	(void)athn_usb_wmi_cmd(usc, AR_WMI_CMD_STOP_RECV);
266413236e8dSdamien 
266513236e8dSdamien 	athn_reset(sc, 0);
266613236e8dSdamien 	athn_init_pll(sc, NULL);
266713236e8dSdamien 	athn_set_power_awake(sc);
266813236e8dSdamien 	athn_reset(sc, 1);
266913236e8dSdamien 	athn_init_pll(sc, NULL);
267013236e8dSdamien 	athn_set_power_sleep(sc);
267113236e8dSdamien 
267213236e8dSdamien 	/* Abort Tx/Rx. */
267313236e8dSdamien 	usbd_abort_pipe(usc->tx_data_pipe);
267413236e8dSdamien 	usbd_abort_pipe(usc->rx_data_pipe);
267513236e8dSdamien 
267613236e8dSdamien 	/* Free Tx/Rx buffers. */
267713236e8dSdamien 	athn_usb_free_tx_list(usc);
267813236e8dSdamien 	athn_usb_free_rx_list(usc);
267913236e8dSdamien 	splx(s);
268013236e8dSdamien 
268113236e8dSdamien 	/* Flush Rx stream. */
268213236e8dSdamien 	m_freem(usc->rx_stream.m);
268303574ff2Sdamien 	usc->rx_stream.m = NULL;
268413236e8dSdamien 	usc->rx_stream.left = 0;
268513236e8dSdamien }
2686