xref: /openbsd-src/sys/dev/usb/if_athn_usb.c (revision 720f4ea4e7a4da998bab272c6bdc876115dcf380)
1*720f4ea4Sstsp /*	$OpenBSD: if_athn_usb.c,v 1.67 2024/05/29 07:27:33 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/systm.h>
3013236e8dSdamien #include <sys/timeout.h>
3113236e8dSdamien #include <sys/device.h>
329b18ffb8Sguenther #include <sys/endian.h>
3313236e8dSdamien 
3413236e8dSdamien #include <machine/bus.h>
3513236e8dSdamien #include <machine/intr.h>
3613236e8dSdamien 
3713236e8dSdamien #if NBPFILTER > 0
3813236e8dSdamien #include <net/bpf.h>
3913236e8dSdamien #endif
4013236e8dSdamien #include <net/if.h>
4113236e8dSdamien #include <net/if_dl.h>
4213236e8dSdamien #include <net/if_media.h>
4313236e8dSdamien 
4413236e8dSdamien #include <netinet/in.h>
4513236e8dSdamien #include <netinet/if_ether.h>
4613236e8dSdamien 
4713236e8dSdamien #include <net80211/ieee80211_var.h>
4813236e8dSdamien #include <net80211/ieee80211_amrr.h>
49d2dd70acSstsp #include <net80211/ieee80211_ra.h>
5013236e8dSdamien #include <net80211/ieee80211_radiotap.h>
5113236e8dSdamien 
5213236e8dSdamien #include <dev/ic/athnreg.h>
53ff1dd4b7Sstsp #include <dev/ic/ar5008reg.h>
5413236e8dSdamien #include <dev/ic/athnvar.h>
5513236e8dSdamien 
5613236e8dSdamien #include <dev/usb/usb.h>
5713236e8dSdamien #include <dev/usb/usbdi.h>
5813236e8dSdamien #include <dev/usb/usbdevs.h>
5913236e8dSdamien 
6013236e8dSdamien #include <dev/usb/if_athn_usb.h>
6113236e8dSdamien 
6213236e8dSdamien static const struct athn_usb_type {
6313236e8dSdamien 	struct usb_devno	devno;
6413236e8dSdamien 	u_int			flags;
6513236e8dSdamien } athn_usb_devs[] = {
6613236e8dSdamien 	{{ USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_AR9280 },
6713236e8dSdamien 	   ATHN_USB_FLAG_AR7010 },
6813236e8dSdamien 	{{ USB_VENDOR_ACTIONTEC, USB_PRODUCT_ACTIONTEC_AR9287 },
6913236e8dSdamien 	   ATHN_USB_FLAG_AR7010 },
7013236e8dSdamien 	{{ USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_AR9271_1 }},
7113236e8dSdamien 	{{ USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_AR9271_2 }},
7213236e8dSdamien 	{{ USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_AR9271_3 }},
7313236e8dSdamien 	{{ USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_AR9280 },
7413236e8dSdamien 	   ATHN_USB_FLAG_AR7010 },
7513236e8dSdamien 	{{ USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_AR9287 },
7613236e8dSdamien 	   ATHN_USB_FLAG_AR7010 },
7713236e8dSdamien 	{{ USB_VENDOR_AZUREWAVE, USB_PRODUCT_AZUREWAVE_AR9271_1 }},
7813236e8dSdamien 	{{ USB_VENDOR_AZUREWAVE, USB_PRODUCT_AZUREWAVE_AR9271_2 }},
7913236e8dSdamien 	{{ USB_VENDOR_AZUREWAVE, USB_PRODUCT_AZUREWAVE_AR9271_3 }},
8013236e8dSdamien 	{{ USB_VENDOR_AZUREWAVE, USB_PRODUCT_AZUREWAVE_AR9271_4 }},
8113236e8dSdamien 	{{ USB_VENDOR_AZUREWAVE, USB_PRODUCT_AZUREWAVE_AR9271_5 }},
8213236e8dSdamien 	{{ USB_VENDOR_AZUREWAVE, USB_PRODUCT_AZUREWAVE_AR9271_6 }},
8313236e8dSdamien 	{{ USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_AR9271 }},
8413236e8dSdamien 	{{ USB_VENDOR_LITEON, USB_PRODUCT_LITEON_AR9271 }},
8513236e8dSdamien 	{{ USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_WNA1100 }},
8613236e8dSdamien 	{{ USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_WNDA3200 },
8713236e8dSdamien 	   ATHN_USB_FLAG_AR7010 },
88ddf3247cStedu 	{{ USB_VENDOR_PANASONIC, USB_PRODUCT_PANASONIC_N5HBZ0000055 },
89ddf3247cStedu 	   ATHN_USB_FLAG_AR7010 },
90937de54fSstsp 	{{ USB_VENDOR_MELCO, USB_PRODUCT_MELCO_UWABR100 },
91937de54fSstsp 	   ATHN_USB_FLAG_AR7010 },
9213236e8dSdamien 	{{ USB_VENDOR_VIA, USB_PRODUCT_VIA_AR9271 }}
9313236e8dSdamien };
9413236e8dSdamien #define athn_usb_lookup(v, p)	\
9513236e8dSdamien 	((const struct athn_usb_type *)usb_lookup(athn_usb_devs, v, p))
9613236e8dSdamien 
9713236e8dSdamien int		athn_usb_match(struct device *, void *, void *);
9813236e8dSdamien void		athn_usb_attach(struct device *, struct device *, void *);
9913236e8dSdamien int		athn_usb_detach(struct device *, int);
100ef89f9e6Smpi void		athn_usb_attachhook(struct device *);
10113236e8dSdamien int		athn_usb_open_pipes(struct athn_usb_softc *);
10213236e8dSdamien void		athn_usb_close_pipes(struct athn_usb_softc *);
10313236e8dSdamien int		athn_usb_alloc_rx_list(struct athn_usb_softc *);
10413236e8dSdamien void		athn_usb_free_rx_list(struct athn_usb_softc *);
10513236e8dSdamien int		athn_usb_alloc_tx_list(struct athn_usb_softc *);
10613236e8dSdamien void		athn_usb_free_tx_list(struct athn_usb_softc *);
10713236e8dSdamien int		athn_usb_alloc_tx_cmd(struct athn_usb_softc *);
10813236e8dSdamien void		athn_usb_free_tx_cmd(struct athn_usb_softc *);
10913236e8dSdamien void		athn_usb_task(void *);
11013236e8dSdamien void		athn_usb_do_async(struct athn_usb_softc *,
11113236e8dSdamien 		    void (*)(struct athn_usb_softc *, void *), void *, int);
11213236e8dSdamien void		athn_usb_wait_async(struct athn_usb_softc *);
11313236e8dSdamien int		athn_usb_load_firmware(struct athn_usb_softc *);
11413236e8dSdamien int		athn_usb_htc_msg(struct athn_usb_softc *, uint16_t, void *,
11513236e8dSdamien 		    int);
11613236e8dSdamien int		athn_usb_htc_setup(struct athn_usb_softc *);
11713236e8dSdamien int		athn_usb_htc_connect_svc(struct athn_usb_softc *, uint16_t,
11813236e8dSdamien 		    uint8_t, uint8_t, uint8_t *);
11913236e8dSdamien int		athn_usb_wmi_xcmd(struct athn_usb_softc *, uint16_t, void *,
12013236e8dSdamien 		    int, void *);
12113236e8dSdamien int		athn_usb_read_rom(struct athn_softc *);
12213236e8dSdamien uint32_t	athn_usb_read(struct athn_softc *, uint32_t);
12313236e8dSdamien void		athn_usb_write(struct athn_softc *, uint32_t, uint32_t);
12413236e8dSdamien void		athn_usb_write_barrier(struct athn_softc *);
12513236e8dSdamien int		athn_usb_media_change(struct ifnet *);
126dd463f29Sstsp void		athn_usb_next_scan(void *);
12713236e8dSdamien int		athn_usb_newstate(struct ieee80211com *, enum ieee80211_state,
12813236e8dSdamien 		    int);
12913236e8dSdamien void		athn_usb_newstate_cb(struct athn_usb_softc *, void *);
13013236e8dSdamien void		athn_usb_newassoc(struct ieee80211com *,
13113236e8dSdamien 		    struct ieee80211_node *, int);
13213236e8dSdamien void		athn_usb_newassoc_cb(struct athn_usb_softc *, void *);
133f47be805Sstsp struct ieee80211_node *athn_usb_node_alloc(struct ieee80211com *);
134f47be805Sstsp void		athn_usb_count_active_sta(void *, struct ieee80211_node *);
135f47be805Sstsp void		athn_usb_newauth_cb(struct athn_usb_softc *, void *);
136f47be805Sstsp int		athn_usb_newauth(struct ieee80211com *,
137f47be805Sstsp 		    struct ieee80211_node *, int, uint16_t);
138f47be805Sstsp void		athn_usb_node_free(struct ieee80211com *,
13913236e8dSdamien 		    struct ieee80211_node *);
140f47be805Sstsp void		athn_usb_node_free_cb(struct athn_usb_softc *, void *);
14113236e8dSdamien int		athn_usb_ampdu_tx_start(struct ieee80211com *,
14213236e8dSdamien 		    struct ieee80211_node *, uint8_t);
14313236e8dSdamien void		athn_usb_ampdu_tx_start_cb(struct athn_usb_softc *, void *);
14413236e8dSdamien void		athn_usb_ampdu_tx_stop(struct ieee80211com *,
14513236e8dSdamien 		    struct ieee80211_node *, uint8_t);
14613236e8dSdamien void		athn_usb_ampdu_tx_stop_cb(struct athn_usb_softc *, void *);
147f47be805Sstsp void		athn_usb_clean_nodes(void *, struct ieee80211_node *);
14813236e8dSdamien int		athn_usb_create_node(struct athn_usb_softc *,
14913236e8dSdamien 		    struct ieee80211_node *);
150f47be805Sstsp int		athn_usb_node_set_rates(struct athn_usb_softc *,
151f47be805Sstsp 		    struct ieee80211_node *);
152f47be805Sstsp int		athn_usb_remove_node(struct athn_usb_softc *,
153f47be805Sstsp 		    struct ieee80211_node *);
15413236e8dSdamien void		athn_usb_rx_enable(struct athn_softc *);
15575f8bb6dSdamien int		athn_set_chan(struct athn_softc *, struct ieee80211_channel *,
15675f8bb6dSdamien 		    struct ieee80211_channel *);
15713236e8dSdamien int		athn_usb_switch_chan(struct athn_softc *,
15813236e8dSdamien 		    struct ieee80211_channel *, struct ieee80211_channel *);
15913236e8dSdamien void		athn_usb_updateedca(struct ieee80211com *);
16013236e8dSdamien void		athn_usb_updateedca_cb(struct athn_usb_softc *, void *);
16113236e8dSdamien void		athn_usb_updateslot(struct ieee80211com *);
16213236e8dSdamien void		athn_usb_updateslot_cb(struct athn_usb_softc *, void *);
16313236e8dSdamien int		athn_usb_set_key(struct ieee80211com *,
16413236e8dSdamien 		    struct ieee80211_node *, struct ieee80211_key *);
16513236e8dSdamien void		athn_usb_set_key_cb(struct athn_usb_softc *, void *);
16613236e8dSdamien void		athn_usb_delete_key(struct ieee80211com *,
16713236e8dSdamien 		    struct ieee80211_node *, struct ieee80211_key *);
16813236e8dSdamien void		athn_usb_delete_key_cb(struct athn_usb_softc *, void *);
169ab0b1be7Smglocker void		athn_usb_bcneof(struct usbd_xfer *, void *,
17013236e8dSdamien 		    usbd_status);
17113236e8dSdamien void		athn_usb_swba(struct athn_usb_softc *);
172f47be805Sstsp void		athn_usb_tx_status(void *, struct ieee80211_node *);
17313236e8dSdamien void		athn_usb_rx_wmi_ctrl(struct athn_usb_softc *, uint8_t *, int);
174ab0b1be7Smglocker void		athn_usb_intr(struct usbd_xfer *, void *,
17513236e8dSdamien 		    usbd_status);
17613236e8dSdamien void		athn_usb_rx_radiotap(struct athn_softc *, struct mbuf *,
17713236e8dSdamien 		    struct ar_rx_status *);
1788fbaf8a2Sstsp void		athn_usb_rx_frame(struct athn_usb_softc *, struct mbuf *,
1798fbaf8a2Sstsp 		    struct mbuf_list *);
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 *);
192ff1dd4b7Sstsp int		ar5008_ccmp_decap(struct athn_softc *, struct mbuf *,
193ff1dd4b7Sstsp 		    struct ieee80211_node *);
194ff1dd4b7Sstsp int		ar5008_ccmp_encap(struct mbuf *, u_int, struct ieee80211_key *);
19513236e8dSdamien 
19613236e8dSdamien /* Shortcut. */
19713236e8dSdamien #define athn_usb_wmi_cmd(sc, cmd_id)	\
19813236e8dSdamien 	athn_usb_wmi_xcmd(sc, cmd_id, NULL, 0, NULL)
19913236e8dSdamien 
20013236e8dSdamien /* Extern functions. */
20113236e8dSdamien void		athn_led_init(struct athn_softc *);
2025632af28Sdamien void		athn_set_led(struct athn_softc *, int);
20313236e8dSdamien void		athn_btcoex_init(struct athn_softc *);
20413236e8dSdamien void		athn_set_rxfilter(struct athn_softc *, uint32_t);
20513236e8dSdamien int		athn_reset(struct athn_softc *, int);
20613236e8dSdamien void		athn_init_pll(struct athn_softc *,
20713236e8dSdamien 		    const struct ieee80211_channel *);
20813236e8dSdamien int		athn_set_power_awake(struct athn_softc *);
20913236e8dSdamien void		athn_set_power_sleep(struct athn_softc *);
21013236e8dSdamien void		athn_reset_key(struct athn_softc *, int);
21113236e8dSdamien int		athn_set_key(struct ieee80211com *, struct ieee80211_node *,
21213236e8dSdamien 		    struct ieee80211_key *);
21313236e8dSdamien void		athn_delete_key(struct ieee80211com *, struct ieee80211_node *,
21413236e8dSdamien 		    struct ieee80211_key *);
21513236e8dSdamien void		athn_rx_start(struct athn_softc *);
21613236e8dSdamien void		athn_set_sta_timers(struct athn_softc *);
21713236e8dSdamien void		athn_set_hostap_timers(struct athn_softc *);
21813236e8dSdamien void		athn_set_opmode(struct athn_softc *);
21913236e8dSdamien void		athn_set_bss(struct athn_softc *, struct ieee80211_node *);
22013236e8dSdamien int		athn_hw_reset(struct athn_softc *, struct ieee80211_channel *,
22113236e8dSdamien 		    struct ieee80211_channel *, int);
22213236e8dSdamien void		athn_updateedca(struct ieee80211com *);
22313236e8dSdamien void		athn_updateslot(struct ieee80211com *);
22413236e8dSdamien 
22513236e8dSdamien const struct cfattach athn_usb_ca = {
22613236e8dSdamien 	sizeof(struct athn_usb_softc),
22713236e8dSdamien 	athn_usb_match,
22813236e8dSdamien 	athn_usb_attach,
22953c6612dSmpi 	athn_usb_detach
23013236e8dSdamien };
23113236e8dSdamien 
23213236e8dSdamien int
athn_usb_match(struct device * parent,void * match,void * aux)23313236e8dSdamien athn_usb_match(struct device *parent, void *match, void *aux)
23413236e8dSdamien {
23513236e8dSdamien 	struct usb_attach_arg *uaa = aux;
23613236e8dSdamien 
2377ebc5b51Smpi 	if (uaa->iface == NULL || uaa->configno != 1)
23813236e8dSdamien 		return (UMATCH_NONE);
23913236e8dSdamien 
24013236e8dSdamien 	return ((athn_usb_lookup(uaa->vendor, uaa->product) != NULL) ?
2417ebc5b51Smpi 	    UMATCH_VENDOR_PRODUCT_CONF_IFACE : UMATCH_NONE);
24213236e8dSdamien }
24313236e8dSdamien 
24413236e8dSdamien void
athn_usb_attach(struct device * parent,struct device * self,void * aux)24513236e8dSdamien athn_usb_attach(struct device *parent, struct device *self, void *aux)
24613236e8dSdamien {
24713236e8dSdamien 	struct athn_usb_softc *usc = (struct athn_usb_softc *)self;
24813236e8dSdamien 	struct athn_softc *sc = &usc->sc_sc;
24913236e8dSdamien 	struct usb_attach_arg *uaa = aux;
25013236e8dSdamien 
25113236e8dSdamien 	usc->sc_udev = uaa->device;
2527ebc5b51Smpi 	usc->sc_iface = uaa->iface;
25313236e8dSdamien 
25413236e8dSdamien 	usc->flags = athn_usb_lookup(uaa->vendor, uaa->product)->flags;
25513236e8dSdamien 	sc->flags |= ATHN_FLAG_USB;
25613236e8dSdamien #ifdef notyet
25713236e8dSdamien 	/* Check if it is a combo WiFi+Bluetooth (WB193) device. */
25813236e8dSdamien 	if (strncmp(product, "wb193", 5) == 0)
25913236e8dSdamien 		sc->flags |= ATHN_FLAG_BTCOEX3WIRE;
26013236e8dSdamien #endif
26113236e8dSdamien 
26213236e8dSdamien 	sc->ops.read = athn_usb_read;
26313236e8dSdamien 	sc->ops.write = athn_usb_write;
26413236e8dSdamien 	sc->ops.write_barrier = athn_usb_write_barrier;
26513236e8dSdamien 
26613236e8dSdamien 	usb_init_task(&usc->sc_task, athn_usb_task, sc, USB_TASK_TYPE_GENERIC);
26713236e8dSdamien 
26813236e8dSdamien 	if (athn_usb_open_pipes(usc) != 0)
26913236e8dSdamien 		return;
27013236e8dSdamien 
27113236e8dSdamien 	/* Allocate xfer for firmware commands. */
27213236e8dSdamien 	if (athn_usb_alloc_tx_cmd(usc) != 0)
27313236e8dSdamien 		return;
27413236e8dSdamien 
275ef89f9e6Smpi 	config_mountroot(self, athn_usb_attachhook);
27613236e8dSdamien }
27713236e8dSdamien 
27813236e8dSdamien int
athn_usb_detach(struct device * self,int flags)27913236e8dSdamien athn_usb_detach(struct device *self, int flags)
28013236e8dSdamien {
28113236e8dSdamien 	struct athn_usb_softc *usc = (struct athn_usb_softc *)self;
28213236e8dSdamien 	struct athn_softc *sc = &usc->sc_sc;
28313236e8dSdamien 
284ffb60851Smikeb 	if (usc->sc_athn_attached)
28513236e8dSdamien 		athn_detach(sc);
28613236e8dSdamien 
28713236e8dSdamien 	/* Wait for all async commands to complete. */
28813236e8dSdamien 	athn_usb_wait_async(usc);
28913236e8dSdamien 
290dfa43ce4Sstsp 	usbd_ref_wait(usc->sc_udev);
291dfa43ce4Sstsp 
29213236e8dSdamien 	/* Abort and close Tx/Rx pipes. */
29313236e8dSdamien 	athn_usb_close_pipes(usc);
29413236e8dSdamien 
29513236e8dSdamien 	/* Free Tx/Rx buffers. */
29613236e8dSdamien 	athn_usb_free_tx_cmd(usc);
29713236e8dSdamien 	athn_usb_free_tx_list(usc);
29813236e8dSdamien 	athn_usb_free_rx_list(usc);
29913236e8dSdamien 
30013236e8dSdamien 	return (0);
30113236e8dSdamien }
30213236e8dSdamien 
30313236e8dSdamien void
athn_usb_attachhook(struct device * self)304ef89f9e6Smpi athn_usb_attachhook(struct device *self)
30513236e8dSdamien {
306ef89f9e6Smpi 	struct athn_usb_softc *usc = (struct athn_usb_softc *)self;
30713236e8dSdamien 	struct athn_softc *sc = &usc->sc_sc;
30813236e8dSdamien 	struct athn_ops *ops = &sc->ops;
30913236e8dSdamien 	struct ieee80211com *ic = &sc->sc_ic;
31013236e8dSdamien 	struct ifnet *ifp = &ic->ic_if;
31113236e8dSdamien 	int s, i, error;
31213236e8dSdamien 
31313236e8dSdamien 	/* Load firmware. */
31413236e8dSdamien 	error = athn_usb_load_firmware(usc);
31513236e8dSdamien 	if (error != 0) {
31657392abcSstsp 		printf("%s: could not load firmware\n", sc->sc_dev.dv_xname);
31713236e8dSdamien 		return;
31813236e8dSdamien 	}
31913236e8dSdamien 
32013236e8dSdamien 	/* Setup the host transport communication interface. */
32113236e8dSdamien 	error = athn_usb_htc_setup(usc);
32213236e8dSdamien 	if (error != 0)
32313236e8dSdamien 		return;
32413236e8dSdamien 
32513236e8dSdamien 	/* We're now ready to attach the bus agnostic driver. */
32613236e8dSdamien 	s = splnet();
32713236e8dSdamien 	error = athn_attach(sc);
32813236e8dSdamien 	if (error != 0) {
32913236e8dSdamien 		splx(s);
33013236e8dSdamien 		return;
33113236e8dSdamien 	}
332ffb60851Smikeb 	usc->sc_athn_attached = 1;
33313236e8dSdamien 	/* Override some operations for USB. */
33413236e8dSdamien 	ifp->if_ioctl = athn_usb_ioctl;
33513236e8dSdamien 	ifp->if_start = athn_usb_start;
33613236e8dSdamien 	ifp->if_watchdog = athn_usb_watchdog;
337f47be805Sstsp 	ic->ic_node_alloc = athn_usb_node_alloc;
338f47be805Sstsp 	ic->ic_newauth = athn_usb_newauth;
33913236e8dSdamien 	ic->ic_newassoc = athn_usb_newassoc;
340f47be805Sstsp #ifndef IEEE80211_STA_ONLY
341f47be805Sstsp 	usc->sc_node_free = ic->ic_node_free;
342f47be805Sstsp 	ic->ic_node_free = athn_usb_node_free;
343f47be805Sstsp #endif
34413236e8dSdamien 	ic->ic_updateslot = athn_usb_updateslot;
34513236e8dSdamien 	ic->ic_updateedca = athn_usb_updateedca;
34613236e8dSdamien 	ic->ic_set_key = athn_usb_set_key;
34713236e8dSdamien 	ic->ic_delete_key = athn_usb_delete_key;
348ff1dd4b7Sstsp #ifdef notyet
34913236e8dSdamien 	ic->ic_ampdu_tx_start = athn_usb_ampdu_tx_start;
35013236e8dSdamien 	ic->ic_ampdu_tx_stop = athn_usb_ampdu_tx_stop;
35113236e8dSdamien #endif
35213236e8dSdamien 	ic->ic_newstate = athn_usb_newstate;
35377574026Sbluhm 	ic->ic_media.ifm_change_cb = athn_usb_media_change;
354dd463f29Sstsp 	timeout_set(&sc->scan_to, athn_usb_next_scan, usc);
35513236e8dSdamien 
35613236e8dSdamien 	ops->rx_enable = athn_usb_rx_enable;
35713236e8dSdamien 	splx(s);
35813236e8dSdamien 
35913236e8dSdamien 	/* Reset HW key cache entries. */
36013236e8dSdamien 	for (i = 0; i < sc->kc_entries; i++)
36113236e8dSdamien 		athn_reset_key(sc, i);
36213236e8dSdamien 
36313236e8dSdamien 	ops->enable_antenna_diversity(sc);
36413236e8dSdamien 
36513236e8dSdamien #ifdef ATHN_BT_COEXISTENCE
36613236e8dSdamien 	/* Configure bluetooth coexistence for combo chips. */
36713236e8dSdamien 	if (sc->flags & ATHN_FLAG_BTCOEX)
36813236e8dSdamien 		athn_btcoex_init(sc);
36913236e8dSdamien #endif
37013236e8dSdamien 	/* Configure LED. */
37113236e8dSdamien 	athn_led_init(sc);
37213236e8dSdamien }
37313236e8dSdamien 
37413236e8dSdamien int
athn_usb_open_pipes(struct athn_usb_softc * usc)37513236e8dSdamien athn_usb_open_pipes(struct athn_usb_softc *usc)
37613236e8dSdamien {
37713236e8dSdamien 	usb_endpoint_descriptor_t *ed;
37813236e8dSdamien 	int isize, error;
37913236e8dSdamien 
38013236e8dSdamien 	error = usbd_open_pipe(usc->sc_iface, AR_PIPE_TX_DATA, 0,
38113236e8dSdamien 	    &usc->tx_data_pipe);
38213236e8dSdamien 	if (error != 0) {
38313236e8dSdamien 		printf("%s: could not open Tx bulk pipe\n",
38413236e8dSdamien 		    usc->usb_dev.dv_xname);
38513236e8dSdamien 		goto fail;
38613236e8dSdamien 	}
38713236e8dSdamien 
38813236e8dSdamien 	error = usbd_open_pipe(usc->sc_iface, AR_PIPE_RX_DATA, 0,
38913236e8dSdamien 	    &usc->rx_data_pipe);
39013236e8dSdamien 	if (error != 0) {
39113236e8dSdamien 		printf("%s: could not open Rx bulk pipe\n",
39213236e8dSdamien 		    usc->usb_dev.dv_xname);
39313236e8dSdamien 		goto fail;
39413236e8dSdamien 	}
39513236e8dSdamien 
39613236e8dSdamien 	ed = usbd_get_endpoint_descriptor(usc->sc_iface, AR_PIPE_RX_INTR);
39713236e8dSdamien 	if (ed == NULL) {
39813236e8dSdamien 		printf("%s: could not retrieve Rx intr pipe descriptor\n",
39913236e8dSdamien 		    usc->usb_dev.dv_xname);
40013236e8dSdamien 		goto fail;
40113236e8dSdamien 	}
40213236e8dSdamien 	isize = UGETW(ed->wMaxPacketSize);
40313236e8dSdamien 	if (isize == 0) {
40413236e8dSdamien 		printf("%s: invalid Rx intr pipe descriptor\n",
40513236e8dSdamien 		    usc->usb_dev.dv_xname);
40613236e8dSdamien 		goto fail;
40713236e8dSdamien 	}
40813236e8dSdamien 	usc->ibuf = malloc(isize, M_USBDEV, M_NOWAIT);
40913236e8dSdamien 	if (usc->ibuf == NULL) {
41013236e8dSdamien 		printf("%s: could not allocate Rx intr buffer\n",
41113236e8dSdamien 		    usc->usb_dev.dv_xname);
41213236e8dSdamien 		goto fail;
41313236e8dSdamien 	}
414234dfda1Sderaadt 	usc->ibuflen = isize;
41513236e8dSdamien 	error = usbd_open_pipe_intr(usc->sc_iface, AR_PIPE_RX_INTR,
41613236e8dSdamien 	    USBD_SHORT_XFER_OK, &usc->rx_intr_pipe, usc, usc->ibuf, isize,
41713236e8dSdamien 	    athn_usb_intr, USBD_DEFAULT_INTERVAL);
41813236e8dSdamien 	if (error != 0) {
41913236e8dSdamien 		printf("%s: could not open Rx intr pipe\n",
42013236e8dSdamien 		    usc->usb_dev.dv_xname);
42113236e8dSdamien 		goto fail;
42213236e8dSdamien 	}
42313236e8dSdamien 
42413236e8dSdamien 	error = usbd_open_pipe(usc->sc_iface, AR_PIPE_TX_INTR, 0,
42513236e8dSdamien 	    &usc->tx_intr_pipe);
42613236e8dSdamien 	if (error != 0) {
42713236e8dSdamien 		printf("%s: could not open Tx intr pipe\n",
42813236e8dSdamien 		    usc->usb_dev.dv_xname);
42913236e8dSdamien 		goto fail;
43013236e8dSdamien 	}
43113236e8dSdamien  fail:
43213236e8dSdamien 	if (error != 0)
43313236e8dSdamien 		athn_usb_close_pipes(usc);
43413236e8dSdamien 	return (error);
43513236e8dSdamien }
43613236e8dSdamien 
43713236e8dSdamien void
athn_usb_close_pipes(struct athn_usb_softc * usc)43813236e8dSdamien athn_usb_close_pipes(struct athn_usb_softc *usc)
43913236e8dSdamien {
4401b4b7f67Sstsp 	if (usc->tx_data_pipe != NULL) {
44113236e8dSdamien 		usbd_close_pipe(usc->tx_data_pipe);
4421b4b7f67Sstsp 		usc->tx_data_pipe = NULL;
44313236e8dSdamien 	}
4441b4b7f67Sstsp 	if (usc->rx_data_pipe != NULL) {
4451b4b7f67Sstsp 		usbd_close_pipe(usc->rx_data_pipe);
4461b4b7f67Sstsp 		usc->rx_data_pipe = NULL;
4471b4b7f67Sstsp 	}
4481b4b7f67Sstsp 	if (usc->tx_intr_pipe != NULL) {
4491b4b7f67Sstsp 		usbd_close_pipe(usc->tx_intr_pipe);
4501b4b7f67Sstsp 		usc->tx_intr_pipe = NULL;
4511b4b7f67Sstsp 	}
4521b4b7f67Sstsp 	if (usc->rx_intr_pipe != NULL) {
4531b4b7f67Sstsp 		usbd_close_pipe(usc->rx_intr_pipe);
4541b4b7f67Sstsp 		usc->rx_intr_pipe = NULL;
4551b4b7f67Sstsp 	}
4561b4b7f67Sstsp 	if (usc->ibuf != NULL) {
457234dfda1Sderaadt 		free(usc->ibuf, M_USBDEV, usc->ibuflen);
4581b4b7f67Sstsp 		usc->ibuf = NULL;
4591b4b7f67Sstsp 	}
46013236e8dSdamien }
46113236e8dSdamien 
46213236e8dSdamien int
athn_usb_alloc_rx_list(struct athn_usb_softc * usc)46313236e8dSdamien athn_usb_alloc_rx_list(struct athn_usb_softc *usc)
46413236e8dSdamien {
46513236e8dSdamien 	struct athn_usb_rx_data *data;
46613236e8dSdamien 	int i, error = 0;
46713236e8dSdamien 
46813236e8dSdamien 	for (i = 0; i < ATHN_USB_RX_LIST_COUNT; i++) {
46913236e8dSdamien 		data = &usc->rx_data[i];
47013236e8dSdamien 
47113236e8dSdamien 		data->sc = usc;	/* Backpointer for callbacks. */
47213236e8dSdamien 
47313236e8dSdamien 		data->xfer = usbd_alloc_xfer(usc->sc_udev);
47413236e8dSdamien 		if (data->xfer == NULL) {
47513236e8dSdamien 			printf("%s: could not allocate xfer\n",
47613236e8dSdamien 			    usc->usb_dev.dv_xname);
47713236e8dSdamien 			error = ENOMEM;
47813236e8dSdamien 			break;
47913236e8dSdamien 		}
48013236e8dSdamien 		data->buf = usbd_alloc_buffer(data->xfer, ATHN_USB_RXBUFSZ);
48113236e8dSdamien 		if (data->buf == NULL) {
48213236e8dSdamien 			printf("%s: could not allocate xfer buffer\n",
48313236e8dSdamien 			    usc->usb_dev.dv_xname);
48413236e8dSdamien 			error = ENOMEM;
48513236e8dSdamien 			break;
48613236e8dSdamien 		}
48713236e8dSdamien 	}
48813236e8dSdamien 	if (error != 0)
48913236e8dSdamien 		athn_usb_free_rx_list(usc);
49013236e8dSdamien 	return (error);
49113236e8dSdamien }
49213236e8dSdamien 
49313236e8dSdamien void
athn_usb_free_rx_list(struct athn_usb_softc * usc)49413236e8dSdamien athn_usb_free_rx_list(struct athn_usb_softc *usc)
49513236e8dSdamien {
49613236e8dSdamien 	int i;
49713236e8dSdamien 
49813236e8dSdamien 	/* NB: Caller must abort pipe first. */
49913236e8dSdamien 	for (i = 0; i < ATHN_USB_RX_LIST_COUNT; i++) {
50013236e8dSdamien 		if (usc->rx_data[i].xfer != NULL)
50113236e8dSdamien 			usbd_free_xfer(usc->rx_data[i].xfer);
50213236e8dSdamien 		usc->rx_data[i].xfer = NULL;
50313236e8dSdamien 	}
50413236e8dSdamien }
50513236e8dSdamien 
50613236e8dSdamien int
athn_usb_alloc_tx_list(struct athn_usb_softc * usc)50713236e8dSdamien athn_usb_alloc_tx_list(struct athn_usb_softc *usc)
50813236e8dSdamien {
50913236e8dSdamien 	struct athn_usb_tx_data *data;
51013236e8dSdamien 	int i, error = 0;
51113236e8dSdamien 
51213236e8dSdamien 	TAILQ_INIT(&usc->tx_free_list);
51313236e8dSdamien 	for (i = 0; i < ATHN_USB_TX_LIST_COUNT; i++) {
51413236e8dSdamien 		data = &usc->tx_data[i];
51513236e8dSdamien 
51613236e8dSdamien 		data->sc = usc;	/* Backpointer for callbacks. */
51713236e8dSdamien 
51813236e8dSdamien 		data->xfer = usbd_alloc_xfer(usc->sc_udev);
51913236e8dSdamien 		if (data->xfer == NULL) {
52013236e8dSdamien 			printf("%s: could not allocate xfer\n",
52113236e8dSdamien 			    usc->usb_dev.dv_xname);
52213236e8dSdamien 			error = ENOMEM;
52313236e8dSdamien 			break;
52413236e8dSdamien 		}
52513236e8dSdamien 		data->buf = usbd_alloc_buffer(data->xfer, ATHN_USB_TXBUFSZ);
52613236e8dSdamien 		if (data->buf == NULL) {
52713236e8dSdamien 			printf("%s: could not allocate xfer buffer\n",
52813236e8dSdamien 			    usc->usb_dev.dv_xname);
52913236e8dSdamien 			error = ENOMEM;
53013236e8dSdamien 			break;
53113236e8dSdamien 		}
53213236e8dSdamien 		/* Append this Tx buffer to our free list. */
53313236e8dSdamien 		TAILQ_INSERT_TAIL(&usc->tx_free_list, data, next);
53413236e8dSdamien 	}
53513236e8dSdamien 	if (error != 0)
53613236e8dSdamien 		athn_usb_free_tx_list(usc);
53713236e8dSdamien 	return (error);
53813236e8dSdamien }
53913236e8dSdamien 
54013236e8dSdamien void
athn_usb_free_tx_list(struct athn_usb_softc * usc)54113236e8dSdamien athn_usb_free_tx_list(struct athn_usb_softc *usc)
54213236e8dSdamien {
54313236e8dSdamien 	int i;
54413236e8dSdamien 
54513236e8dSdamien 	/* NB: Caller must abort pipe first. */
54613236e8dSdamien 	for (i = 0; i < ATHN_USB_TX_LIST_COUNT; i++) {
54713236e8dSdamien 		if (usc->tx_data[i].xfer != NULL)
54813236e8dSdamien 			usbd_free_xfer(usc->tx_data[i].xfer);
54913236e8dSdamien 		usc->tx_data[i].xfer = NULL;
55013236e8dSdamien 	}
55113236e8dSdamien }
55213236e8dSdamien 
55313236e8dSdamien int
athn_usb_alloc_tx_cmd(struct athn_usb_softc * usc)55413236e8dSdamien athn_usb_alloc_tx_cmd(struct athn_usb_softc *usc)
55513236e8dSdamien {
55613236e8dSdamien 	struct athn_usb_tx_data *data = &usc->tx_cmd;
55713236e8dSdamien 
55813236e8dSdamien 	data->sc = usc;	/* Backpointer for callbacks. */
55913236e8dSdamien 
56013236e8dSdamien 	data->xfer = usbd_alloc_xfer(usc->sc_udev);
56113236e8dSdamien 	if (data->xfer == NULL) {
56213236e8dSdamien 		printf("%s: could not allocate xfer\n",
56313236e8dSdamien 		    usc->usb_dev.dv_xname);
56413236e8dSdamien 		return (ENOMEM);
56513236e8dSdamien 	}
56613236e8dSdamien 	data->buf = usbd_alloc_buffer(data->xfer, ATHN_USB_TXCMDSZ);
56713236e8dSdamien 	if (data->buf == NULL) {
56813236e8dSdamien 		printf("%s: could not allocate xfer buffer\n",
56913236e8dSdamien 		    usc->usb_dev.dv_xname);
57013236e8dSdamien 		return (ENOMEM);
57113236e8dSdamien 	}
57213236e8dSdamien 	return (0);
57313236e8dSdamien }
57413236e8dSdamien 
57513236e8dSdamien void
athn_usb_free_tx_cmd(struct athn_usb_softc * usc)57613236e8dSdamien athn_usb_free_tx_cmd(struct athn_usb_softc *usc)
57713236e8dSdamien {
57813236e8dSdamien 	if (usc->tx_cmd.xfer != NULL)
57913236e8dSdamien 		usbd_free_xfer(usc->tx_cmd.xfer);
58013236e8dSdamien 	usc->tx_cmd.xfer = NULL;
58113236e8dSdamien }
58213236e8dSdamien 
58313236e8dSdamien void
athn_usb_task(void * arg)58413236e8dSdamien athn_usb_task(void *arg)
58513236e8dSdamien {
58613236e8dSdamien 	struct athn_usb_softc *usc = arg;
58713236e8dSdamien 	struct athn_usb_host_cmd_ring *ring = &usc->cmdq;
58813236e8dSdamien 	struct athn_usb_host_cmd *cmd;
58913236e8dSdamien 	int s;
59013236e8dSdamien 
59113236e8dSdamien 	/* Process host commands. */
59213236e8dSdamien 	s = splusb();
59313236e8dSdamien 	while (ring->next != ring->cur) {
59413236e8dSdamien 		cmd = &ring->cmd[ring->next];
59513236e8dSdamien 		splx(s);
59613236e8dSdamien 		/* Invoke callback. */
59713236e8dSdamien 		cmd->cb(usc, cmd->data);
59813236e8dSdamien 		s = splusb();
59913236e8dSdamien 		ring->queued--;
60013236e8dSdamien 		ring->next = (ring->next + 1) % ATHN_USB_HOST_CMD_RING_COUNT;
60113236e8dSdamien 	}
60213236e8dSdamien 	splx(s);
60313236e8dSdamien }
60413236e8dSdamien 
60513236e8dSdamien void
athn_usb_do_async(struct athn_usb_softc * usc,void (* cb)(struct athn_usb_softc *,void *),void * arg,int len)60613236e8dSdamien athn_usb_do_async(struct athn_usb_softc *usc,
60713236e8dSdamien     void (*cb)(struct athn_usb_softc *, void *), void *arg, int len)
60813236e8dSdamien {
60913236e8dSdamien 	struct athn_usb_host_cmd_ring *ring = &usc->cmdq;
61013236e8dSdamien 	struct athn_usb_host_cmd *cmd;
61113236e8dSdamien 	int s;
61213236e8dSdamien 
613d25f2410Sstsp 	if (ring->queued == ATHN_USB_HOST_CMD_RING_COUNT) {
614d25f2410Sstsp 		printf("%s: host cmd queue overrun\n", usc->usb_dev.dv_xname);
61513236e8dSdamien 		return;	/* XXX */
616d25f2410Sstsp 	}
617d25f2410Sstsp 
61813236e8dSdamien 	s = splusb();
61913236e8dSdamien 	cmd = &ring->cmd[ring->cur];
62013236e8dSdamien 	cmd->cb = cb;
62113236e8dSdamien 	KASSERT(len <= sizeof(cmd->data));
62213236e8dSdamien 	memcpy(cmd->data, arg, len);
62313236e8dSdamien 	ring->cur = (ring->cur + 1) % ATHN_USB_HOST_CMD_RING_COUNT;
62413236e8dSdamien 
62513236e8dSdamien 	/* If there is no pending command already, schedule a task. */
62613236e8dSdamien 	if (++ring->queued == 1)
62713236e8dSdamien 		usb_add_task(usc->sc_udev, &usc->sc_task);
62813236e8dSdamien 	splx(s);
62913236e8dSdamien }
63013236e8dSdamien 
63113236e8dSdamien void
athn_usb_wait_async(struct athn_usb_softc * usc)63213236e8dSdamien athn_usb_wait_async(struct athn_usb_softc *usc)
63313236e8dSdamien {
63413236e8dSdamien 	/* Wait for all queued asynchronous commands to complete. */
635d25f2410Sstsp 	usb_wait_task(usc->sc_udev, &usc->sc_task);
63613236e8dSdamien }
63713236e8dSdamien 
63813236e8dSdamien int
athn_usb_load_firmware(struct athn_usb_softc * usc)63913236e8dSdamien athn_usb_load_firmware(struct athn_usb_softc *usc)
64013236e8dSdamien {
64113236e8dSdamien 	usb_device_descriptor_t *dd;
64213236e8dSdamien 	usb_device_request_t req;
64313236e8dSdamien 	const char *name;
64413236e8dSdamien 	u_char *fw, *ptr;
645c9ee9455Sderaadt 	size_t fwsize, size;
64613236e8dSdamien 	uint32_t addr;
64713236e8dSdamien 	int s, mlen, error;
64813236e8dSdamien 
64913236e8dSdamien 	/* Determine which firmware image to load. */
65013236e8dSdamien 	if (usc->flags & ATHN_USB_FLAG_AR7010) {
65113236e8dSdamien 		dd = usbd_get_device_descriptor(usc->sc_udev);
652f47be805Sstsp 		name = "athn-open-ar7010";
65313236e8dSdamien 	} else
654f47be805Sstsp 		name = "athn-open-ar9271";
65513236e8dSdamien 	/* Read firmware image from the filesystem. */
656c9ee9455Sderaadt 	if ((error = loadfirmware(name, &fw, &fwsize)) != 0) {
65713236e8dSdamien 		printf("%s: failed loadfirmware of file %s (error %d)\n",
65813236e8dSdamien 		    usc->usb_dev.dv_xname, name, error);
65913236e8dSdamien 		return (error);
66013236e8dSdamien 	}
66113236e8dSdamien 	/* Load firmware image. */
66213236e8dSdamien 	ptr = fw;
66313236e8dSdamien 	addr = AR9271_FIRMWARE >> 8;
66413236e8dSdamien 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
66513236e8dSdamien 	req.bRequest = AR_FW_DOWNLOAD;
66613236e8dSdamien 	USETW(req.wIndex, 0);
667c9ee9455Sderaadt 	size = fwsize;
66813236e8dSdamien 	while (size > 0) {
66913236e8dSdamien 		mlen = MIN(size, 4096);
67013236e8dSdamien 
67113236e8dSdamien 		USETW(req.wValue, addr);
67213236e8dSdamien 		USETW(req.wLength, mlen);
67313236e8dSdamien 		error = usbd_do_request(usc->sc_udev, &req, ptr);
67413236e8dSdamien 		if (error != 0) {
675c9ee9455Sderaadt 			free(fw, M_DEVBUF, fwsize);
67613236e8dSdamien 			return (error);
67713236e8dSdamien 		}
67813236e8dSdamien 		addr += mlen >> 8;
67913236e8dSdamien 		ptr  += mlen;
68013236e8dSdamien 		size -= mlen;
68113236e8dSdamien 	}
682c9ee9455Sderaadt 	free(fw, M_DEVBUF, fwsize);
68313236e8dSdamien 
68413236e8dSdamien 	/* Start firmware. */
68513236e8dSdamien 	if (usc->flags & ATHN_USB_FLAG_AR7010)
68613236e8dSdamien 		addr = AR7010_FIRMWARE_TEXT >> 8;
68713236e8dSdamien 	else
68813236e8dSdamien 		addr = AR9271_FIRMWARE_TEXT >> 8;
68913236e8dSdamien 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
69013236e8dSdamien 	req.bRequest = AR_FW_DOWNLOAD_COMP;
69113236e8dSdamien 	USETW(req.wIndex, 0);
69213236e8dSdamien 	USETW(req.wValue, addr);
69313236e8dSdamien 	USETW(req.wLength, 0);
69413236e8dSdamien 	s = splusb();
69513236e8dSdamien 	usc->wait_msg_id = AR_HTC_MSG_READY;
69613236e8dSdamien 	error = usbd_do_request(usc->sc_udev, &req, NULL);
69757392abcSstsp 	/* Wait at most 1 second for firmware to boot. */
69813236e8dSdamien 	if (error == 0 && usc->wait_msg_id != 0)
69900f6cb32Smpi 		error = tsleep_nsec(&usc->wait_msg_id, 0, "athnfw",
70000f6cb32Smpi 		    SEC_TO_NSEC(1));
70113236e8dSdamien 	usc->wait_msg_id = 0;
70213236e8dSdamien 	splx(s);
70313236e8dSdamien 	return (error);
70413236e8dSdamien }
70513236e8dSdamien 
70613236e8dSdamien int
athn_usb_htc_msg(struct athn_usb_softc * usc,uint16_t msg_id,void * buf,int len)70713236e8dSdamien athn_usb_htc_msg(struct athn_usb_softc *usc, uint16_t msg_id, void *buf,
70813236e8dSdamien     int len)
70913236e8dSdamien {
71013236e8dSdamien 	struct athn_usb_tx_data *data = &usc->tx_cmd;
71113236e8dSdamien 	struct ar_htc_frame_hdr *htc;
71213236e8dSdamien 	struct ar_htc_msg_hdr *msg;
71313236e8dSdamien 
71413236e8dSdamien 	htc = (struct ar_htc_frame_hdr *)data->buf;
71513236e8dSdamien 	memset(htc, 0, sizeof(*htc));
71613236e8dSdamien 	htc->endpoint_id = 0;
71713236e8dSdamien 	htc->payload_len = htobe16(sizeof(*msg) + len);
71813236e8dSdamien 
71913236e8dSdamien 	msg = (struct ar_htc_msg_hdr *)&htc[1];
72013236e8dSdamien 	msg->msg_id = htobe16(msg_id);
72113236e8dSdamien 
72213236e8dSdamien 	memcpy(&msg[1], buf, len);
72313236e8dSdamien 
72413236e8dSdamien 	usbd_setup_xfer(data->xfer, usc->tx_intr_pipe, NULL, data->buf,
72513236e8dSdamien 	    sizeof(*htc) + sizeof(*msg) + len,
726aa88c704Smpi 	    USBD_SHORT_XFER_OK | USBD_NO_COPY | USBD_SYNCHRONOUS,
727aa88c704Smpi 	    ATHN_USB_CMD_TIMEOUT, NULL);
728aa88c704Smpi 	return (usbd_transfer(data->xfer));
72913236e8dSdamien }
73013236e8dSdamien 
73113236e8dSdamien int
athn_usb_htc_setup(struct athn_usb_softc * usc)73213236e8dSdamien athn_usb_htc_setup(struct athn_usb_softc *usc)
73313236e8dSdamien {
73413236e8dSdamien 	struct ar_htc_msg_config_pipe cfg;
73513236e8dSdamien 	int s, error;
73613236e8dSdamien 
73713236e8dSdamien 	/*
73813236e8dSdamien 	 * Connect WMI services to USB pipes.
73913236e8dSdamien 	 */
74013236e8dSdamien 	error = athn_usb_htc_connect_svc(usc, AR_SVC_WMI_CONTROL,
74113236e8dSdamien 	    AR_PIPE_TX_INTR, AR_PIPE_RX_INTR, &usc->ep_ctrl);
74213236e8dSdamien 	if (error != 0)
74313236e8dSdamien 		return (error);
74413236e8dSdamien 	error = athn_usb_htc_connect_svc(usc, AR_SVC_WMI_BEACON,
74513236e8dSdamien 	    AR_PIPE_TX_DATA, AR_PIPE_RX_DATA, &usc->ep_bcn);
74613236e8dSdamien 	if (error != 0)
74713236e8dSdamien 		return (error);
74813236e8dSdamien 	error = athn_usb_htc_connect_svc(usc, AR_SVC_WMI_CAB,
74913236e8dSdamien 	    AR_PIPE_TX_DATA, AR_PIPE_RX_DATA, &usc->ep_cab);
75013236e8dSdamien 	if (error != 0)
75113236e8dSdamien 		return (error);
75213236e8dSdamien 	error = athn_usb_htc_connect_svc(usc, AR_SVC_WMI_UAPSD,
75313236e8dSdamien 	    AR_PIPE_TX_DATA, AR_PIPE_RX_DATA, &usc->ep_uapsd);
75413236e8dSdamien 	if (error != 0)
75513236e8dSdamien 		return (error);
75613236e8dSdamien 	error = athn_usb_htc_connect_svc(usc, AR_SVC_WMI_MGMT,
75713236e8dSdamien 	    AR_PIPE_TX_DATA, AR_PIPE_RX_DATA, &usc->ep_mgmt);
75813236e8dSdamien 	if (error != 0)
75913236e8dSdamien 		return (error);
76013236e8dSdamien 	error = athn_usb_htc_connect_svc(usc, AR_SVC_WMI_DATA_BE,
76113236e8dSdamien 	    AR_PIPE_TX_DATA, AR_PIPE_RX_DATA, &usc->ep_data[EDCA_AC_BE]);
76213236e8dSdamien 	if (error != 0)
76313236e8dSdamien 		return (error);
76413236e8dSdamien 	error = athn_usb_htc_connect_svc(usc, AR_SVC_WMI_DATA_BK,
76513236e8dSdamien 	    AR_PIPE_TX_DATA, AR_PIPE_RX_DATA, &usc->ep_data[EDCA_AC_BK]);
76613236e8dSdamien 	if (error != 0)
76713236e8dSdamien 		return (error);
76813236e8dSdamien 	error = athn_usb_htc_connect_svc(usc, AR_SVC_WMI_DATA_VI,
76913236e8dSdamien 	    AR_PIPE_TX_DATA, AR_PIPE_RX_DATA, &usc->ep_data[EDCA_AC_VI]);
77013236e8dSdamien 	if (error != 0)
77113236e8dSdamien 		return (error);
77213236e8dSdamien 	error = athn_usb_htc_connect_svc(usc, AR_SVC_WMI_DATA_VO,
77313236e8dSdamien 	    AR_PIPE_TX_DATA, AR_PIPE_RX_DATA, &usc->ep_data[EDCA_AC_VO]);
77413236e8dSdamien 	if (error != 0)
77513236e8dSdamien 		return (error);
77613236e8dSdamien 
77713236e8dSdamien 	/* Set credits for WLAN Tx pipe. */
77813236e8dSdamien 	memset(&cfg, 0, sizeof(cfg));
77913236e8dSdamien 	cfg.pipe_id = UE_GET_ADDR(AR_PIPE_TX_DATA);
78013236e8dSdamien 	cfg.credits = (usc->flags & ATHN_USB_FLAG_AR7010) ? 45 : 33;
78113236e8dSdamien 	s = splusb();
78213236e8dSdamien 	usc->wait_msg_id = AR_HTC_MSG_CONF_PIPE_RSP;
78313236e8dSdamien 	error = athn_usb_htc_msg(usc, AR_HTC_MSG_CONF_PIPE, &cfg, sizeof(cfg));
78413236e8dSdamien 	if (error == 0 && usc->wait_msg_id != 0)
78500f6cb32Smpi 		error = tsleep_nsec(&usc->wait_msg_id, 0, "athnhtc",
78600f6cb32Smpi 		    SEC_TO_NSEC(1));
78713236e8dSdamien 	usc->wait_msg_id = 0;
78813236e8dSdamien 	splx(s);
78913236e8dSdamien 	if (error != 0) {
79013236e8dSdamien 		printf("%s: could not configure pipe\n",
79113236e8dSdamien 		    usc->usb_dev.dv_xname);
79213236e8dSdamien 		return (error);
79313236e8dSdamien 	}
79413236e8dSdamien 
79513236e8dSdamien 	error = athn_usb_htc_msg(usc, AR_HTC_MSG_SETUP_COMPLETE, NULL, 0);
79613236e8dSdamien 	if (error != 0) {
79713236e8dSdamien 		printf("%s: could not complete setup\n",
79813236e8dSdamien 		    usc->usb_dev.dv_xname);
79913236e8dSdamien 		return (error);
80013236e8dSdamien 	}
80113236e8dSdamien 	return (0);
80213236e8dSdamien }
80313236e8dSdamien 
80413236e8dSdamien int
athn_usb_htc_connect_svc(struct athn_usb_softc * usc,uint16_t svc_id,uint8_t ul_pipe,uint8_t dl_pipe,uint8_t * endpoint_id)80513236e8dSdamien athn_usb_htc_connect_svc(struct athn_usb_softc *usc, uint16_t svc_id,
80613236e8dSdamien     uint8_t ul_pipe, uint8_t dl_pipe, uint8_t *endpoint_id)
80713236e8dSdamien {
80813236e8dSdamien 	struct ar_htc_msg_conn_svc msg;
80913236e8dSdamien 	struct ar_htc_msg_conn_svc_rsp rsp;
81013236e8dSdamien 	int s, error;
81113236e8dSdamien 
81213236e8dSdamien 	memset(&msg, 0, sizeof(msg));
81313236e8dSdamien 	msg.svc_id = htobe16(svc_id);
81413236e8dSdamien 	msg.dl_pipeid = UE_GET_ADDR(dl_pipe);
81513236e8dSdamien 	msg.ul_pipeid = UE_GET_ADDR(ul_pipe);
81613236e8dSdamien 	s = splusb();
81713236e8dSdamien 	usc->msg_conn_svc_rsp = &rsp;
81813236e8dSdamien 	usc->wait_msg_id = AR_HTC_MSG_CONN_SVC_RSP;
81913236e8dSdamien 	error = athn_usb_htc_msg(usc, AR_HTC_MSG_CONN_SVC, &msg, sizeof(msg));
82013236e8dSdamien 	/* Wait at most 1 second for response. */
82113236e8dSdamien 	if (error == 0 && usc->wait_msg_id != 0)
82200f6cb32Smpi 		error = tsleep_nsec(&usc->wait_msg_id, 0, "athnhtc",
82300f6cb32Smpi 		    SEC_TO_NSEC(1));
82413236e8dSdamien 	usc->wait_msg_id = 0;
82513236e8dSdamien 	splx(s);
82613236e8dSdamien 	if (error != 0) {
82713236e8dSdamien 		printf("%s: error waiting for service %d connection\n",
82813236e8dSdamien 		    usc->usb_dev.dv_xname, svc_id);
82913236e8dSdamien 		return (error);
83013236e8dSdamien 	}
83113236e8dSdamien 	if (rsp.status != AR_HTC_SVC_SUCCESS) {
83213236e8dSdamien 		printf("%s: service %d connection failed, error %d\n",
83313236e8dSdamien 		    usc->usb_dev.dv_xname, svc_id, rsp.status);
83413236e8dSdamien 		return (EIO);
83513236e8dSdamien 	}
83613236e8dSdamien 	DPRINTF(("service %d successfully connected to endpoint %d\n",
83713236e8dSdamien 	    svc_id, rsp.endpoint_id));
83813236e8dSdamien 
83913236e8dSdamien 	/* Return endpoint id. */
84013236e8dSdamien 	*endpoint_id = rsp.endpoint_id;
84113236e8dSdamien 	return (0);
84213236e8dSdamien }
84313236e8dSdamien 
84413236e8dSdamien int
athn_usb_wmi_xcmd(struct athn_usb_softc * usc,uint16_t cmd_id,void * ibuf,int ilen,void * obuf)84513236e8dSdamien athn_usb_wmi_xcmd(struct athn_usb_softc *usc, uint16_t cmd_id, void *ibuf,
84613236e8dSdamien     int ilen, void *obuf)
84713236e8dSdamien {
84813236e8dSdamien 	struct athn_usb_tx_data *data = &usc->tx_cmd;
84913236e8dSdamien 	struct ar_htc_frame_hdr *htc;
85013236e8dSdamien 	struct ar_wmi_cmd_hdr *wmi;
85113236e8dSdamien 	int s, error;
85213236e8dSdamien 
853d25f2410Sstsp 	if (usbd_is_dying(usc->sc_udev))
854d25f2410Sstsp 		return ENXIO;
855d25f2410Sstsp 
856d25f2410Sstsp 	s = splusb();
857d25f2410Sstsp 	while (usc->wait_cmd_id) {
858d25f2410Sstsp 		/*
859d25f2410Sstsp 		 * The previous USB transfer is not done yet. We can't use
860d25f2410Sstsp 		 * data->xfer until it is done or we'll cause major confusion
861d25f2410Sstsp 		 * in the USB stack.
862d25f2410Sstsp 		 */
863b66625d1Smpi 		tsleep_nsec(&usc->wait_cmd_id, 0, "athnwmx",
864b66625d1Smpi 		    MSEC_TO_NSEC(ATHN_USB_CMD_TIMEOUT));
865d25f2410Sstsp 		if (usbd_is_dying(usc->sc_udev)) {
866d25f2410Sstsp 			splx(s);
867d25f2410Sstsp 			return ENXIO;
868d25f2410Sstsp 		}
869d25f2410Sstsp 	}
870d25f2410Sstsp 	splx(s);
871d25f2410Sstsp 
87213236e8dSdamien 	htc = (struct ar_htc_frame_hdr *)data->buf;
87313236e8dSdamien 	memset(htc, 0, sizeof(*htc));
87413236e8dSdamien 	htc->endpoint_id = usc->ep_ctrl;
87513236e8dSdamien 	htc->payload_len = htobe16(sizeof(*wmi) + ilen);
87613236e8dSdamien 
87713236e8dSdamien 	wmi = (struct ar_wmi_cmd_hdr *)&htc[1];
87813236e8dSdamien 	wmi->cmd_id = htobe16(cmd_id);
87913236e8dSdamien 	usc->wmi_seq_no++;
88013236e8dSdamien 	wmi->seq_no = htobe16(usc->wmi_seq_no);
88113236e8dSdamien 
88213236e8dSdamien 	memcpy(&wmi[1], ibuf, ilen);
88313236e8dSdamien 
884d25f2410Sstsp 	usbd_setup_xfer(data->xfer, usc->tx_intr_pipe, NULL, data->buf,
88513236e8dSdamien 	    sizeof(*htc) + sizeof(*wmi) + ilen,
88613236e8dSdamien 	    USBD_SHORT_XFER_OK | USBD_NO_COPY, ATHN_USB_CMD_TIMEOUT,
887d25f2410Sstsp 	    NULL);
88813236e8dSdamien 	s = splusb();
88913236e8dSdamien 	error = usbd_transfer(data->xfer);
89013236e8dSdamien 	if (__predict_false(error != USBD_IN_PROGRESS && error != 0)) {
89113236e8dSdamien 		splx(s);
89213236e8dSdamien 		return (error);
89313236e8dSdamien 	}
89413236e8dSdamien 	usc->obuf = obuf;
89513236e8dSdamien 	usc->wait_cmd_id = cmd_id;
896d25f2410Sstsp 	/*
897d25f2410Sstsp 	 * Wait for WMI command complete interrupt. In case it does not fire
898d25f2410Sstsp 	 * wait until the USB transfer times out to avoid racing the transfer.
899d25f2410Sstsp 	 */
900b66625d1Smpi 	error = tsleep_nsec(&usc->wait_cmd_id, 0, "athnwmi",
901b66625d1Smpi 	    MSEC_TO_NSEC(ATHN_USB_CMD_TIMEOUT));
902d25f2410Sstsp 	if (error) {
903d25f2410Sstsp 		if (error == EWOULDBLOCK) {
90420592b50Sstsp 			printf("%s: firmware command 0x%x timed out\n",
905d25f2410Sstsp 			    usc->usb_dev.dv_xname, cmd_id);
906d25f2410Sstsp 			error = ETIMEDOUT;
907d25f2410Sstsp 		}
908d25f2410Sstsp 	}
909d25f2410Sstsp 
910d25f2410Sstsp 	/*
911d25f2410Sstsp 	 * Both the WMI command and transfer are done or have timed out.
912d25f2410Sstsp 	 * Allow other threads to enter this function and use data->xfer.
913d25f2410Sstsp 	 */
91413236e8dSdamien 	usc->wait_cmd_id = 0;
915d25f2410Sstsp 	wakeup(&usc->wait_cmd_id);
916d25f2410Sstsp 
91713236e8dSdamien 	splx(s);
91813236e8dSdamien 	return (error);
91913236e8dSdamien }
92013236e8dSdamien 
92113236e8dSdamien int
athn_usb_read_rom(struct athn_softc * sc)92213236e8dSdamien athn_usb_read_rom(struct athn_softc *sc)
92313236e8dSdamien {
92413236e8dSdamien 	struct athn_usb_softc *usc = (struct athn_usb_softc *)sc;
92513236e8dSdamien 	uint32_t addrs[8], vals[8], addr;
92613236e8dSdamien 	uint16_t *eep;
92713236e8dSdamien 	int i, j, error;
92813236e8dSdamien 
92913236e8dSdamien 	/* Read EEPROM by blocks of 16 bytes. */
93013236e8dSdamien 	eep = sc->eep;
93113236e8dSdamien 	addr = AR_EEPROM_OFFSET(sc->eep_base);
93213236e8dSdamien 	for (i = 0; i < sc->eep_size / 16; i++) {
93313236e8dSdamien 		for (j = 0; j < 8; j++, addr += 4)
93413236e8dSdamien 			addrs[j] = htobe32(addr);
93513236e8dSdamien 		error = athn_usb_wmi_xcmd(usc, AR_WMI_CMD_REG_READ,
93613236e8dSdamien 		    addrs, sizeof(addrs), vals);
93713236e8dSdamien 		if (error != 0)
93813236e8dSdamien 			break;
93913236e8dSdamien 		for (j = 0; j < 8; j++)
94013236e8dSdamien 			*eep++ = betoh32(vals[j]);
94113236e8dSdamien 	}
94213236e8dSdamien 	return (error);
94313236e8dSdamien }
94413236e8dSdamien 
94513236e8dSdamien uint32_t
athn_usb_read(struct athn_softc * sc,uint32_t addr)94613236e8dSdamien athn_usb_read(struct athn_softc *sc, uint32_t addr)
94713236e8dSdamien {
94813236e8dSdamien 	struct athn_usb_softc *usc = (struct athn_usb_softc *)sc;
94913236e8dSdamien 	uint32_t val;
95013236e8dSdamien 	int error;
95113236e8dSdamien 
95213236e8dSdamien 	/* Flush pending writes for strict consistency. */
95313236e8dSdamien 	athn_usb_write_barrier(sc);
95413236e8dSdamien 
95513236e8dSdamien 	addr = htobe32(addr);
95613236e8dSdamien 	error = athn_usb_wmi_xcmd(usc, AR_WMI_CMD_REG_READ,
95713236e8dSdamien 	    &addr, sizeof(addr), &val);
95813236e8dSdamien 	if (error != 0)
95913236e8dSdamien 		return (0xdeadbeef);
96013236e8dSdamien 	return (betoh32(val));
96113236e8dSdamien }
96213236e8dSdamien 
96313236e8dSdamien void
athn_usb_write(struct athn_softc * sc,uint32_t addr,uint32_t val)96413236e8dSdamien athn_usb_write(struct athn_softc *sc, uint32_t addr, uint32_t val)
96513236e8dSdamien {
96613236e8dSdamien 	struct athn_usb_softc *usc = (struct athn_usb_softc *)sc;
96713236e8dSdamien 
96813236e8dSdamien 	usc->wbuf[usc->wcount].addr = htobe32(addr);
96913236e8dSdamien 	usc->wbuf[usc->wcount].val  = htobe32(val);
97013236e8dSdamien 	if (++usc->wcount == AR_MAX_WRITE_COUNT)
97113236e8dSdamien 		athn_usb_write_barrier(sc);
97213236e8dSdamien }
97313236e8dSdamien 
97413236e8dSdamien void
athn_usb_write_barrier(struct athn_softc * sc)97513236e8dSdamien athn_usb_write_barrier(struct athn_softc *sc)
97613236e8dSdamien {
97713236e8dSdamien 	struct athn_usb_softc *usc = (struct athn_usb_softc *)sc;
97813236e8dSdamien 
97913236e8dSdamien 	if (usc->wcount == 0)
98013236e8dSdamien 		return;	/* Nothing to write. */
98113236e8dSdamien 
98213236e8dSdamien 	(void)athn_usb_wmi_xcmd(usc, AR_WMI_CMD_REG_WRITE,
98313236e8dSdamien 	    usc->wbuf, usc->wcount * sizeof(usc->wbuf[0]), NULL);
98413236e8dSdamien 	usc->wcount = 0;	/* Always flush buffer. */
98513236e8dSdamien }
98613236e8dSdamien 
98713236e8dSdamien int
athn_usb_media_change(struct ifnet * ifp)98813236e8dSdamien athn_usb_media_change(struct ifnet *ifp)
98913236e8dSdamien {
990dfa43ce4Sstsp 	struct athn_usb_softc *usc = (struct athn_usb_softc *)ifp->if_softc;
99113236e8dSdamien 	int error;
99213236e8dSdamien 
993dfa43ce4Sstsp 	if (usbd_is_dying(usc->sc_udev))
994dfa43ce4Sstsp 		return ENXIO;
995dfa43ce4Sstsp 
99613236e8dSdamien 	error = ieee80211_media_change(ifp);
99713236e8dSdamien 	if (error != ENETRESET)
99813236e8dSdamien 		return (error);
99913236e8dSdamien 
100013236e8dSdamien 	if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
100113236e8dSdamien 	    (IFF_UP | IFF_RUNNING)) {
100213236e8dSdamien 		athn_usb_stop(ifp);
100313236e8dSdamien 		error = athn_usb_init(ifp);
100413236e8dSdamien 	}
100513236e8dSdamien 	return (error);
100613236e8dSdamien }
100713236e8dSdamien 
1008dd463f29Sstsp void
athn_usb_next_scan(void * arg)1009dd463f29Sstsp athn_usb_next_scan(void *arg)
1010dd463f29Sstsp {
1011dd463f29Sstsp 	struct athn_usb_softc *usc = arg;
1012dd463f29Sstsp 	struct athn_softc *sc = &usc->sc_sc;
1013dd463f29Sstsp 	struct ieee80211com *ic = &sc->sc_ic;
1014dd463f29Sstsp 	int s;
1015dd463f29Sstsp 
1016dd463f29Sstsp 	if (usbd_is_dying(usc->sc_udev))
1017dd463f29Sstsp 		return;
1018dd463f29Sstsp 
1019dd463f29Sstsp 	usbd_ref_incr(usc->sc_udev);
1020dd463f29Sstsp 
1021dd463f29Sstsp 	s = splnet();
1022dd463f29Sstsp 	if (ic->ic_state == IEEE80211_S_SCAN)
1023dd463f29Sstsp 		ieee80211_next_scan(&ic->ic_if);
1024dd463f29Sstsp 	splx(s);
1025dd463f29Sstsp 
1026dd463f29Sstsp 	usbd_ref_decr(usc->sc_udev);
1027dd463f29Sstsp }
1028dd463f29Sstsp 
102913236e8dSdamien int
athn_usb_newstate(struct ieee80211com * ic,enum ieee80211_state nstate,int arg)103013236e8dSdamien athn_usb_newstate(struct ieee80211com *ic, enum ieee80211_state nstate,
103113236e8dSdamien     int arg)
103213236e8dSdamien {
103313236e8dSdamien 	struct athn_usb_softc *usc = ic->ic_softc;
103413236e8dSdamien 	struct athn_usb_cmd_newstate cmd;
103513236e8dSdamien 
103613236e8dSdamien 	/* Do it in a process context. */
103713236e8dSdamien 	cmd.state = nstate;
103813236e8dSdamien 	cmd.arg = arg;
103913236e8dSdamien 	athn_usb_do_async(usc, athn_usb_newstate_cb, &cmd, sizeof(cmd));
104013236e8dSdamien 	return (0);
104113236e8dSdamien }
104213236e8dSdamien 
104313236e8dSdamien void
athn_usb_newstate_cb(struct athn_usb_softc * usc,void * arg)104413236e8dSdamien athn_usb_newstate_cb(struct athn_usb_softc *usc, void *arg)
104513236e8dSdamien {
104613236e8dSdamien 	struct athn_usb_cmd_newstate *cmd = arg;
104713236e8dSdamien 	struct athn_softc *sc = &usc->sc_sc;
104813236e8dSdamien 	struct ieee80211com *ic = &sc->sc_ic;
104913236e8dSdamien 	enum ieee80211_state ostate;
105003574ff2Sdamien 	uint32_t reg, imask;
105113236e8dSdamien 	int s, error;
105213236e8dSdamien 
105313236e8dSdamien 	timeout_del(&sc->calib_to);
105413236e8dSdamien 
105513236e8dSdamien 	s = splnet();
105613236e8dSdamien 	ostate = ic->ic_state;
105713236e8dSdamien 
1058f47be805Sstsp 	if (ostate == IEEE80211_S_RUN && ic->ic_opmode == IEEE80211_M_STA) {
1059f47be805Sstsp 		athn_usb_remove_node(usc, ic->ic_bss);
1060ea37cbb6Sstsp 		reg = AR_READ(sc, AR_RX_FILTER);
1061ea37cbb6Sstsp 		reg = (reg & ~AR_RX_FILTER_MYBEACON) |
1062ea37cbb6Sstsp 		    AR_RX_FILTER_BEACON;
1063ea37cbb6Sstsp 		AR_WRITE(sc, AR_RX_FILTER, reg);
1064ea37cbb6Sstsp 		AR_WRITE_BARRIER(sc);
1065ea37cbb6Sstsp 	}
106613236e8dSdamien 	switch (cmd->state) {
106713236e8dSdamien 	case IEEE80211_S_INIT:
10685632af28Sdamien 		athn_set_led(sc, 0);
106913236e8dSdamien 		break;
107013236e8dSdamien 	case IEEE80211_S_SCAN:
107103574ff2Sdamien 		/* Make the LED blink while scanning. */
10725632af28Sdamien 		athn_set_led(sc, !sc->led_state);
1073f47be805Sstsp 		error = athn_usb_switch_chan(sc, ic->ic_bss->ni_chan, NULL);
1074f47be805Sstsp 		if (error)
1075f47be805Sstsp 			printf("%s: could not switch to channel %d\n",
1076f47be805Sstsp 			    usc->usb_dev.dv_xname,
1077f47be805Sstsp 			    ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan));
1078dfa43ce4Sstsp 		if (!usbd_is_dying(usc->sc_udev))
107913236e8dSdamien 			timeout_add_msec(&sc->scan_to, 200);
108013236e8dSdamien 		break;
108113236e8dSdamien 	case IEEE80211_S_AUTH:
10825632af28Sdamien 		athn_set_led(sc, 0);
108313236e8dSdamien 		error = athn_usb_switch_chan(sc, ic->ic_bss->ni_chan, NULL);
1084f47be805Sstsp 		if (error)
1085f47be805Sstsp 			printf("%s: could not switch to channel %d\n",
1086f47be805Sstsp 			    usc->usb_dev.dv_xname,
1087f47be805Sstsp 			    ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan));
108813236e8dSdamien 		break;
108913236e8dSdamien 	case IEEE80211_S_ASSOC:
109013236e8dSdamien 		break;
109113236e8dSdamien 	case IEEE80211_S_RUN:
10925632af28Sdamien 		athn_set_led(sc, 1);
109303574ff2Sdamien 
109413236e8dSdamien 		if (ic->ic_opmode == IEEE80211_M_MONITOR)
109513236e8dSdamien 			break;
109603574ff2Sdamien 
1097f47be805Sstsp 		if (ic->ic_opmode == IEEE80211_M_STA) {
1098ea37cbb6Sstsp 			/* Create node entry for our BSS */
109913236e8dSdamien 			error = athn_usb_create_node(usc, ic->ic_bss);
1100f47be805Sstsp 			if (error)
1101f47be805Sstsp 				printf("%s: could not update firmware station "
1102f47be805Sstsp 				    "table\n", usc->usb_dev.dv_xname);
1103f47be805Sstsp 		}
110403574ff2Sdamien 		athn_set_bss(sc, ic->ic_bss);
110513236e8dSdamien 		athn_usb_wmi_cmd(usc, AR_WMI_CMD_DISABLE_INTR);
110613236e8dSdamien #ifndef IEEE80211_STA_ONLY
110713236e8dSdamien 		if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
1108f47be805Sstsp 			athn_usb_switch_chan(sc, ic->ic_bss->ni_chan, NULL);
110913236e8dSdamien 			athn_set_hostap_timers(sc);
1110ead380efSjsing 			/* Enable software beacon alert interrupts. */
111113236e8dSdamien 			imask = htobe32(AR_IMR_SWBA);
111213236e8dSdamien 		} else
111313236e8dSdamien #endif
111413236e8dSdamien 		{
111513236e8dSdamien 			athn_set_sta_timers(sc);
111613236e8dSdamien 			/* Enable beacon miss interrupts. */
111713236e8dSdamien 			imask = htobe32(AR_IMR_BMISS);
111813236e8dSdamien 
111903574ff2Sdamien 			/* Stop receiving beacons from other BSS. */
112003574ff2Sdamien 			reg = AR_READ(sc, AR_RX_FILTER);
112103574ff2Sdamien 			reg = (reg & ~AR_RX_FILTER_BEACON) |
112203574ff2Sdamien 			    AR_RX_FILTER_MYBEACON;
112303574ff2Sdamien 			AR_WRITE(sc, AR_RX_FILTER, reg);
112403574ff2Sdamien 			AR_WRITE_BARRIER(sc);
112513236e8dSdamien 		}
112613236e8dSdamien 		athn_usb_wmi_xcmd(usc, AR_WMI_CMD_ENABLE_INTR,
112713236e8dSdamien 		    &imask, sizeof(imask), NULL);
112813236e8dSdamien 		break;
112913236e8dSdamien 	}
113013236e8dSdamien 	(void)sc->sc_newstate(ic, cmd->state, cmd->arg);
113113236e8dSdamien 	splx(s);
113213236e8dSdamien }
113313236e8dSdamien 
113413236e8dSdamien void
athn_usb_newassoc(struct ieee80211com * ic,struct ieee80211_node * ni,int isnew)113513236e8dSdamien athn_usb_newassoc(struct ieee80211com *ic, struct ieee80211_node *ni,
113613236e8dSdamien     int isnew)
113713236e8dSdamien {
113810c7bb96Sbrad #ifndef IEEE80211_STA_ONLY
113913236e8dSdamien 	struct athn_usb_softc *usc = ic->ic_softc;
114013236e8dSdamien 
1141f47be805Sstsp 	if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
1142f47be805Sstsp 	    ic->ic_state != IEEE80211_S_RUN)
114313236e8dSdamien 		return;
1144f47be805Sstsp 
1145f47be805Sstsp 	/* Update the node's supported rates in a process context. */
114613236e8dSdamien 	ieee80211_ref_node(ni);
114713236e8dSdamien 	athn_usb_do_async(usc, athn_usb_newassoc_cb, &ni, sizeof(ni));
1148f47be805Sstsp #endif
114913236e8dSdamien }
115013236e8dSdamien 
1151f47be805Sstsp #ifndef IEEE80211_STA_ONLY
115213236e8dSdamien void
athn_usb_newassoc_cb(struct athn_usb_softc * usc,void * arg)115313236e8dSdamien athn_usb_newassoc_cb(struct athn_usb_softc *usc, void *arg)
115413236e8dSdamien {
115513236e8dSdamien 	struct ieee80211com *ic = &usc->sc_sc.sc_ic;
115613236e8dSdamien 	struct ieee80211_node *ni = *(void **)arg;
1157f47be805Sstsp 	struct athn_node *an = (struct athn_node *)ni;
115813236e8dSdamien 	int s;
115913236e8dSdamien 
1160f47be805Sstsp 	if (ic->ic_state != IEEE80211_S_RUN)
1161f47be805Sstsp 		return;
1162f47be805Sstsp 
116313236e8dSdamien 	s = splnet();
116413236e8dSdamien 	/* NB: Node may have left before we got scheduled. */
1165f47be805Sstsp 	if (an->sta_index != 0)
1166f47be805Sstsp 		(void)athn_usb_node_set_rates(usc, ni);
116713236e8dSdamien 	ieee80211_release_node(ic, ni);
116813236e8dSdamien 	splx(s);
1169f47be805Sstsp }
117010c7bb96Sbrad #endif
1171f47be805Sstsp 
1172f47be805Sstsp struct ieee80211_node *
athn_usb_node_alloc(struct ieee80211com * ic)1173f47be805Sstsp athn_usb_node_alloc(struct ieee80211com *ic)
1174f47be805Sstsp {
1175f47be805Sstsp 	struct athn_node *an;
1176f47be805Sstsp 
11778985a220Smglocker 	an = malloc(sizeof(struct athn_node), M_USBDEV, M_NOWAIT | M_ZERO);
1178f47be805Sstsp 	return (struct ieee80211_node *)an;
117913236e8dSdamien }
118013236e8dSdamien 
1181f47be805Sstsp 
1182f47be805Sstsp #ifndef IEEE80211_STA_ONLY
118313236e8dSdamien void
athn_usb_count_active_sta(void * arg,struct ieee80211_node * ni)1184f47be805Sstsp athn_usb_count_active_sta(void *arg, struct ieee80211_node *ni)
1185f47be805Sstsp {
1186f47be805Sstsp 	int *nsta = arg;
1187f47be805Sstsp 	struct athn_node *an = (struct athn_node *)ni;
1188f47be805Sstsp 
1189f47be805Sstsp 	if (an->sta_index == 0)
1190f47be805Sstsp 		return;
1191f47be805Sstsp 
1192f47be805Sstsp 	if ((ni->ni_state == IEEE80211_STA_AUTH ||
1193f47be805Sstsp 	    ni->ni_state == IEEE80211_STA_ASSOC) &&
1194f47be805Sstsp 	    ni->ni_inact < IEEE80211_INACT_MAX)
1195f47be805Sstsp 		(*nsta)++;
1196f47be805Sstsp }
1197f47be805Sstsp 
1198f47be805Sstsp struct athn_usb_newauth_cb_arg {
1199f47be805Sstsp 	struct ieee80211_node *ni;
1200f47be805Sstsp 	uint16_t seq;
1201f47be805Sstsp };
1202f47be805Sstsp 
1203f47be805Sstsp void
athn_usb_newauth_cb(struct athn_usb_softc * usc,void * arg)1204f47be805Sstsp athn_usb_newauth_cb(struct athn_usb_softc *usc, void *arg)
1205f47be805Sstsp {
1206f47be805Sstsp 	struct ieee80211com *ic = &usc->sc_sc.sc_ic;
1207f47be805Sstsp 	struct athn_usb_newauth_cb_arg *a = arg;
1208f47be805Sstsp 	struct ieee80211_node *ni = a->ni;
1209f47be805Sstsp 	uint16_t seq = a->seq;
1210f47be805Sstsp 	struct athn_node *an = (struct athn_node *)ni;
1211f47be805Sstsp 	int s, error = 0;
1212f47be805Sstsp 
1213f47be805Sstsp 	if (ic->ic_state != IEEE80211_S_RUN)
1214f47be805Sstsp 		return;
1215f47be805Sstsp 
1216f47be805Sstsp 	s = splnet();
1217f47be805Sstsp 	if (an->sta_index == 0) {
1218f47be805Sstsp 		error = athn_usb_create_node(usc, ni);
1219f47be805Sstsp 		if (error)
1220f47be805Sstsp 			printf("%s: could not add station %s to firmware "
1221f47be805Sstsp 			    "table\n", usc->usb_dev.dv_xname,
1222f47be805Sstsp 			    ether_sprintf(ni->ni_macaddr));
1223f47be805Sstsp 	}
1224f47be805Sstsp 	if (error == 0)
1225f47be805Sstsp 		ieee80211_auth_open_confirm(ic, ni, seq);
1226f47be805Sstsp 	ieee80211_unref_node(&ni);
1227f47be805Sstsp 	splx(s);
1228f47be805Sstsp }
1229f47be805Sstsp #endif
1230f47be805Sstsp 
1231f47be805Sstsp int
athn_usb_newauth(struct ieee80211com * ic,struct ieee80211_node * ni,int isnew,uint16_t seq)1232f47be805Sstsp athn_usb_newauth(struct ieee80211com *ic, struct ieee80211_node *ni,
1233f47be805Sstsp     int isnew, uint16_t seq)
1234f47be805Sstsp {
1235f47be805Sstsp #ifndef IEEE80211_STA_ONLY
1236f47be805Sstsp 	struct athn_usb_softc *usc = ic->ic_softc;
1237f47be805Sstsp 	struct ifnet *ifp = &ic->ic_if;
1238f47be805Sstsp 	struct athn_node *an = (struct athn_node *)ni;
1239f47be805Sstsp 	int nsta;
1240044d754cSstsp 	struct athn_usb_newauth_cb_arg arg;
1241f47be805Sstsp 
1242f47be805Sstsp 	if (ic->ic_opmode != IEEE80211_M_HOSTAP)
1243f47be805Sstsp 		return 0;
1244f47be805Sstsp 
1245f47be805Sstsp 	if (!isnew && an->sta_index != 0) /* already in firmware table */
1246f47be805Sstsp 		return 0;
1247f47be805Sstsp 
1248f47be805Sstsp 	/* Check if we have room in the firmware table. */
1249f47be805Sstsp 	nsta = 1; /* Account for default node. */
1250f47be805Sstsp 	ieee80211_iterate_nodes(ic, athn_usb_count_active_sta, &nsta);
1251f47be805Sstsp 	if (nsta >= AR_USB_MAX_STA) {
1252f47be805Sstsp 		if (ifp->if_flags & IFF_DEBUG)
1253f47be805Sstsp 			printf("%s: cannot authenticate station %s: firmware "
1254f47be805Sstsp 			    "table is full\n", usc->usb_dev.dv_xname,
1255f47be805Sstsp 			    ether_sprintf(ni->ni_macaddr));
1256f47be805Sstsp 		return ENOSPC;
1257f47be805Sstsp 	}
1258f47be805Sstsp 
1259f47be805Sstsp 	/*
1260f47be805Sstsp 	 * In a process context, try to add this node to the
1261f47be805Sstsp 	 * firmware table and confirm the AUTH request.
1262f47be805Sstsp 	 */
1263044d754cSstsp 	arg.ni = ieee80211_ref_node(ni);
1264044d754cSstsp 	arg.seq = seq;
1265044d754cSstsp 	athn_usb_do_async(usc, athn_usb_newauth_cb, &arg, sizeof(arg));
1266f47be805Sstsp 	return EBUSY;
1267f47be805Sstsp #else
1268f47be805Sstsp 	return 0;
1269f47be805Sstsp #endif /* IEEE80211_STA_ONLY */
1270f47be805Sstsp }
1271f47be805Sstsp 
1272f47be805Sstsp #ifndef IEEE80211_STA_ONLY
1273f47be805Sstsp void
athn_usb_node_free(struct ieee80211com * ic,struct ieee80211_node * ni)1274f47be805Sstsp athn_usb_node_free(struct ieee80211com *ic, struct ieee80211_node *ni)
127513236e8dSdamien {
127613236e8dSdamien 	struct athn_usb_softc *usc = ic->ic_softc;
1277f47be805Sstsp 	struct athn_node *an = (struct athn_node *)ni;
127813236e8dSdamien 
1279f47be805Sstsp 	/*
1280f47be805Sstsp 	 * Remove the node from the firmware table in a process context.
1281f47be805Sstsp 	 * Pass an index rather than the pointer which we will free.
1282f47be805Sstsp 	 */
1283f47be805Sstsp 	if (an->sta_index != 0)
1284f47be805Sstsp 		athn_usb_do_async(usc, athn_usb_node_free_cb,
1285f47be805Sstsp 		    &an->sta_index, sizeof(an->sta_index));
1286f47be805Sstsp 	usc->sc_node_free(ic, ni);
128713236e8dSdamien }
128813236e8dSdamien 
128913236e8dSdamien void
athn_usb_node_free_cb(struct athn_usb_softc * usc,void * arg)1290f47be805Sstsp athn_usb_node_free_cb(struct athn_usb_softc *usc, void *arg)
129113236e8dSdamien {
1292f47be805Sstsp 	struct ieee80211com *ic = &usc->sc_sc.sc_ic;
1293f47be805Sstsp 	struct ifnet *ifp = &ic->ic_if;
129413236e8dSdamien 	uint8_t sta_index = *(uint8_t *)arg;
1295f47be805Sstsp 	int error;
129613236e8dSdamien 
1297f47be805Sstsp 	error = athn_usb_wmi_xcmd(usc, AR_WMI_CMD_NODE_REMOVE,
129813236e8dSdamien 	    &sta_index, sizeof(sta_index), NULL);
1299f47be805Sstsp 	if (error) {
1300f47be805Sstsp 		printf("%s: could not remove station %u from firmware table\n",
1301f47be805Sstsp 		    usc->usb_dev.dv_xname, sta_index);
1302f47be805Sstsp 		return;
130313236e8dSdamien 	}
1304f47be805Sstsp 	usc->free_node_slots |= (1 << sta_index);
1305f47be805Sstsp 	if (ifp->if_flags & IFF_DEBUG)
1306f47be805Sstsp 		printf("%s: station %u removed from firmware table\n",
1307f47be805Sstsp 		    usc->usb_dev.dv_xname, sta_index);
1308f47be805Sstsp }
1309f47be805Sstsp #endif /* IEEE80211_STA_ONLY */
131013236e8dSdamien 
131113236e8dSdamien int
athn_usb_ampdu_tx_start(struct ieee80211com * ic,struct ieee80211_node * ni,uint8_t tid)131213236e8dSdamien athn_usb_ampdu_tx_start(struct ieee80211com *ic, struct ieee80211_node *ni,
131313236e8dSdamien     uint8_t tid)
131413236e8dSdamien {
131513236e8dSdamien 	struct athn_usb_softc *usc = ic->ic_softc;
131613236e8dSdamien 	struct athn_node *an = (struct athn_node *)ni;
131713236e8dSdamien 	struct athn_usb_aggr_cmd cmd;
131813236e8dSdamien 
131913236e8dSdamien 	/* Do it in a process context. */
132013236e8dSdamien 	cmd.sta_index = an->sta_index;
132113236e8dSdamien 	cmd.tid = tid;
132213236e8dSdamien 	athn_usb_do_async(usc, athn_usb_ampdu_tx_start_cb, &cmd, sizeof(cmd));
132313236e8dSdamien 	return (0);
132413236e8dSdamien }
132513236e8dSdamien 
132613236e8dSdamien void
athn_usb_ampdu_tx_start_cb(struct athn_usb_softc * usc,void * arg)132713236e8dSdamien athn_usb_ampdu_tx_start_cb(struct athn_usb_softc *usc, void *arg)
132813236e8dSdamien {
132913236e8dSdamien 	struct athn_usb_aggr_cmd *cmd = arg;
133013236e8dSdamien 	struct ar_htc_target_aggr aggr;
133113236e8dSdamien 
133213236e8dSdamien 	memset(&aggr, 0, sizeof(aggr));
133313236e8dSdamien 	aggr.sta_index = cmd->sta_index;
133413236e8dSdamien 	aggr.tidno = cmd->tid;
133513236e8dSdamien 	aggr.aggr_enable = 1;
133613236e8dSdamien 	(void)athn_usb_wmi_xcmd(usc, AR_WMI_CMD_TX_AGGR_ENABLE,
133713236e8dSdamien 	    &aggr, sizeof(aggr), NULL);
133813236e8dSdamien }
133913236e8dSdamien 
134013236e8dSdamien void
athn_usb_ampdu_tx_stop(struct ieee80211com * ic,struct ieee80211_node * ni,uint8_t tid)134113236e8dSdamien athn_usb_ampdu_tx_stop(struct ieee80211com *ic, struct ieee80211_node *ni,
134213236e8dSdamien     uint8_t tid)
134313236e8dSdamien {
134413236e8dSdamien 	struct athn_usb_softc *usc = ic->ic_softc;
134513236e8dSdamien 	struct athn_node *an = (struct athn_node *)ni;
134613236e8dSdamien 	struct athn_usb_aggr_cmd cmd;
134713236e8dSdamien 
134813236e8dSdamien 	/* Do it in a process context. */
134913236e8dSdamien 	cmd.sta_index = an->sta_index;
135013236e8dSdamien 	cmd.tid = tid;
135113236e8dSdamien 	athn_usb_do_async(usc, athn_usb_ampdu_tx_stop_cb, &cmd, sizeof(cmd));
135213236e8dSdamien }
135313236e8dSdamien 
135413236e8dSdamien void
athn_usb_ampdu_tx_stop_cb(struct athn_usb_softc * usc,void * arg)135513236e8dSdamien athn_usb_ampdu_tx_stop_cb(struct athn_usb_softc *usc, void *arg)
135613236e8dSdamien {
135713236e8dSdamien 	struct athn_usb_aggr_cmd *cmd = arg;
135813236e8dSdamien 	struct ar_htc_target_aggr aggr;
135913236e8dSdamien 
136013236e8dSdamien 	memset(&aggr, 0, sizeof(aggr));
136113236e8dSdamien 	aggr.sta_index = cmd->sta_index;
136213236e8dSdamien 	aggr.tidno = cmd->tid;
136313236e8dSdamien 	aggr.aggr_enable = 0;
136413236e8dSdamien 	(void)athn_usb_wmi_xcmd(usc, AR_WMI_CMD_TX_AGGR_ENABLE,
136513236e8dSdamien 	    &aggr, sizeof(aggr), NULL);
136613236e8dSdamien }
136713236e8dSdamien 
1368f47be805Sstsp #ifndef IEEE80211_STA_ONLY
1369f47be805Sstsp /* Try to find a node we can evict to make room in the firmware table. */
1370f47be805Sstsp void
athn_usb_clean_nodes(void * arg,struct ieee80211_node * ni)1371f47be805Sstsp athn_usb_clean_nodes(void *arg, struct ieee80211_node *ni)
1372f47be805Sstsp {
1373f47be805Sstsp 	struct athn_usb_softc *usc = arg;
1374f47be805Sstsp 	struct ieee80211com *ic = &usc->sc_sc.sc_ic;
1375f47be805Sstsp 	struct athn_node *an = (struct athn_node *)ni;
1376f47be805Sstsp 
1377f47be805Sstsp 	/*
1378f47be805Sstsp 	 * Don't remove the default node (used for management frames).
1379f47be805Sstsp 	 * Nodes which are not in the firmware table also have index zero.
1380f47be805Sstsp 	 */
1381f47be805Sstsp 	if (an->sta_index == 0)
1382f47be805Sstsp 		return;
1383f47be805Sstsp 
1384f47be805Sstsp 	/* Remove non-associated nodes. */
1385f47be805Sstsp 	if (ni->ni_state != IEEE80211_STA_AUTH &&
1386f47be805Sstsp 	    ni->ni_state != IEEE80211_STA_ASSOC) {
1387f47be805Sstsp 		athn_usb_remove_node(usc, ni);
1388f47be805Sstsp 		return;
1389f47be805Sstsp 	}
1390f47be805Sstsp 
1391f47be805Sstsp 	/*
1392f47be805Sstsp 	 * Kick off inactive associated nodes. This won't help
1393f47be805Sstsp 	 * immediately but will help if the new STA retries later.
1394f47be805Sstsp 	 */
1395f47be805Sstsp 	if (ni->ni_inact >= IEEE80211_INACT_MAX) {
1396f47be805Sstsp 		IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
1397f47be805Sstsp 		    IEEE80211_REASON_AUTH_EXPIRE);
1398f47be805Sstsp 		ieee80211_node_leave(ic, ni);
1399f47be805Sstsp 	}
1400f47be805Sstsp }
1401f47be805Sstsp #endif
1402f47be805Sstsp 
140313236e8dSdamien int
athn_usb_create_node(struct athn_usb_softc * usc,struct ieee80211_node * ni)140413236e8dSdamien athn_usb_create_node(struct athn_usb_softc *usc, struct ieee80211_node *ni)
140513236e8dSdamien {
140613236e8dSdamien 	struct athn_node *an = (struct athn_node *)ni;
140713236e8dSdamien 	struct ar_htc_target_sta sta;
1408f47be805Sstsp 	int error, sta_index;
1409f47be805Sstsp #ifndef IEEE80211_STA_ONLY
1410f47be805Sstsp 	struct ieee80211com *ic = &usc->sc_sc.sc_ic;
1411f47be805Sstsp 	struct ifnet *ifp = &ic->ic_if;
141213236e8dSdamien 
1413f47be805Sstsp 	/* Firmware cannot handle more than 8 STAs. Try to make room first. */
1414f47be805Sstsp 	if (ic->ic_opmode == IEEE80211_M_HOSTAP)
1415f47be805Sstsp 		ieee80211_iterate_nodes(ic, athn_usb_clean_nodes, usc);
1416f47be805Sstsp #endif
1417f47be805Sstsp 	if (usc->free_node_slots == 0x00)
1418dd463f29Sstsp 		return ENOBUFS;
1419dd463f29Sstsp 
1420f47be805Sstsp 	sta_index = ffs(usc->free_node_slots) - 1;
1421f47be805Sstsp 	if (sta_index < 0 || sta_index >= AR_USB_MAX_STA)
1422f47be805Sstsp 		return ENOSPC;
142313236e8dSdamien 
142413236e8dSdamien 	/* Create node entry on target. */
142513236e8dSdamien 	memset(&sta, 0, sizeof(sta));
142613236e8dSdamien 	IEEE80211_ADDR_COPY(sta.macaddr, ni->ni_macaddr);
142713236e8dSdamien 	IEEE80211_ADDR_COPY(sta.bssid, ni->ni_bssid);
1428f47be805Sstsp 	sta.sta_index = sta_index;
142913236e8dSdamien 	sta.maxampdu = 0xffff;
143013236e8dSdamien 	if (ni->ni_flags & IEEE80211_NODE_HT)
143113236e8dSdamien 		sta.flags |= htobe16(AR_HTC_STA_HT);
143213236e8dSdamien 	error = athn_usb_wmi_xcmd(usc, AR_WMI_CMD_NODE_CREATE,
143313236e8dSdamien 	    &sta, sizeof(sta), NULL);
143413236e8dSdamien 	if (error != 0)
143513236e8dSdamien 		return (error);
1436f47be805Sstsp 	an->sta_index = sta_index;
1437f47be805Sstsp 	usc->free_node_slots &= ~(1 << an->sta_index);
1438f47be805Sstsp 
1439f47be805Sstsp #ifndef IEEE80211_STA_ONLY
1440f47be805Sstsp 	if (ifp->if_flags & IFF_DEBUG)
1441f47be805Sstsp 		printf("%s: station %u (%s) added to firmware table\n",
1442f47be805Sstsp 		    usc->usb_dev.dv_xname, sta_index,
1443f47be805Sstsp 		    ether_sprintf(ni->ni_macaddr));
1444f47be805Sstsp #endif
1445f47be805Sstsp 	return athn_usb_node_set_rates(usc, ni);
1446f47be805Sstsp }
1447f47be805Sstsp 
1448f47be805Sstsp int
athn_usb_node_set_rates(struct athn_usb_softc * usc,struct ieee80211_node * ni)1449f47be805Sstsp athn_usb_node_set_rates(struct athn_usb_softc *usc, struct ieee80211_node *ni)
1450f47be805Sstsp {
1451f47be805Sstsp 	struct athn_node *an = (struct athn_node *)ni;
1452f47be805Sstsp 	struct ar_htc_target_rate rate;
1453f47be805Sstsp 	int i, j;
145413236e8dSdamien 
145513236e8dSdamien 	/* Setup supported rates. */
145613236e8dSdamien 	memset(&rate, 0, sizeof(rate));
1457f47be805Sstsp 	rate.sta_index = an->sta_index;
145813236e8dSdamien 	rate.isnew = 1;
145913236e8dSdamien 	rate.lg_rates.rs_nrates = ni->ni_rates.rs_nrates;
146013236e8dSdamien 	memcpy(rate.lg_rates.rs_rates, ni->ni_rates.rs_rates,
146113236e8dSdamien 	    ni->ni_rates.rs_nrates);
146213236e8dSdamien 	if (ni->ni_flags & IEEE80211_NODE_HT) {
146313236e8dSdamien 		rate.capflags |= htobe32(AR_RC_HT_FLAG);
14647363c99eSstsp 		/* Setup HT rates. */
14657363c99eSstsp 		for (i = 0, j = 0; i < IEEE80211_HT_NUM_MCS; i++) {
14667363c99eSstsp 			if (!isset(ni->ni_rxmcs, i))
14677363c99eSstsp 				continue;
14687363c99eSstsp 			if (j >= AR_HTC_RATE_MAX)
14697363c99eSstsp 				break;
14707363c99eSstsp 			rate.ht_rates.rs_rates[j++] = i;
14717363c99eSstsp 		}
14727363c99eSstsp 		rate.ht_rates.rs_nrates = j;
14737363c99eSstsp 
14747363c99eSstsp 		if (ni->ni_rxmcs[1]) /* dual-stream MIMO rates */
14757363c99eSstsp 			rate.capflags |= htobe32(AR_RC_DS_FLAG);
147613236e8dSdamien #ifdef notyet
147713236e8dSdamien 		if (ni->ni_htcaps & IEEE80211_HTCAP_CBW20_40)
147813236e8dSdamien 			rate.capflags |= htobe32(AR_RC_40_FLAG);
147913236e8dSdamien 		if (ni->ni_htcaps & IEEE80211_HTCAP_SGI40)
148013236e8dSdamien 			rate.capflags |= htobe32(AR_RC_SGI_FLAG);
148113236e8dSdamien 		if (ni->ni_htcaps & IEEE80211_HTCAP_SGI20)
148213236e8dSdamien 			rate.capflags |= htobe32(AR_RC_SGI_FLAG);
148313236e8dSdamien #endif
148413236e8dSdamien 	}
1485f47be805Sstsp 
1486f47be805Sstsp 	return athn_usb_wmi_xcmd(usc, AR_WMI_CMD_RC_RATE_UPDATE,
148713236e8dSdamien 	    &rate, sizeof(rate), NULL);
1488f47be805Sstsp }
1489f47be805Sstsp 
1490f47be805Sstsp int
athn_usb_remove_node(struct athn_usb_softc * usc,struct ieee80211_node * ni)1491f47be805Sstsp athn_usb_remove_node(struct athn_usb_softc *usc, struct ieee80211_node *ni)
1492f47be805Sstsp {
1493f47be805Sstsp 	struct athn_node *an = (struct athn_node *)ni;
1494f47be805Sstsp 	int error;
1495f47be805Sstsp #ifndef IEEE80211_STA_ONLY
1496f47be805Sstsp 	struct ieee80211com *ic = &usc->sc_sc.sc_ic;
1497f47be805Sstsp 	struct ifnet *ifp = &ic->ic_if;
1498f47be805Sstsp #endif
1499f47be805Sstsp 
1500f47be805Sstsp 	error = athn_usb_wmi_xcmd(usc, AR_WMI_CMD_NODE_REMOVE,
1501f47be805Sstsp 	    &an->sta_index, sizeof(an->sta_index), NULL);
1502f47be805Sstsp 	if (error) {
1503f47be805Sstsp 		printf("%s: could not remove station %u (%s) from "
1504f47be805Sstsp 		    "firmware table\n", usc->usb_dev.dv_xname, an->sta_index,
1505f47be805Sstsp 		    ether_sprintf(ni->ni_macaddr));
1506f47be805Sstsp 		return error;
1507f47be805Sstsp 	}
1508f47be805Sstsp 
1509f47be805Sstsp #ifndef IEEE80211_STA_ONLY
1510f47be805Sstsp 	if (ifp->if_flags & IFF_DEBUG)
1511f47be805Sstsp 		printf("%s: station %u (%s) removed from firmware table\n",
1512f47be805Sstsp 		    usc->usb_dev.dv_xname, an->sta_index,
1513f47be805Sstsp 		    ether_sprintf(ni->ni_macaddr));
1514f47be805Sstsp #endif
1515f47be805Sstsp 
1516f47be805Sstsp 	usc->free_node_slots |= (1 << an->sta_index);
1517f47be805Sstsp 	an->sta_index = 0;
1518f47be805Sstsp 	return 0;
151913236e8dSdamien }
152013236e8dSdamien 
152113236e8dSdamien void
athn_usb_rx_enable(struct athn_softc * sc)152213236e8dSdamien athn_usb_rx_enable(struct athn_softc *sc)
152313236e8dSdamien {
152413236e8dSdamien 	AR_WRITE(sc, AR_CR, AR_CR_RXE);
152513236e8dSdamien 	AR_WRITE_BARRIER(sc);
152613236e8dSdamien }
152713236e8dSdamien 
152813236e8dSdamien int
athn_usb_switch_chan(struct athn_softc * sc,struct ieee80211_channel * c,struct ieee80211_channel * extc)152913236e8dSdamien athn_usb_switch_chan(struct athn_softc *sc, struct ieee80211_channel *c,
153013236e8dSdamien     struct ieee80211_channel *extc)
153113236e8dSdamien {
153213236e8dSdamien 	struct athn_usb_softc *usc = (struct athn_usb_softc *)sc;
153313236e8dSdamien 	uint16_t mode;
153413236e8dSdamien 	int error;
153513236e8dSdamien 
153613236e8dSdamien 	/* Disable interrupts. */
153713236e8dSdamien 	error = athn_usb_wmi_cmd(usc, AR_WMI_CMD_DISABLE_INTR);
153813236e8dSdamien 	if (error != 0)
153975f8bb6dSdamien 		goto reset;
154013236e8dSdamien 	/* Stop all Tx queues. */
154113236e8dSdamien 	error = athn_usb_wmi_cmd(usc, AR_WMI_CMD_DRAIN_TXQ_ALL);
154213236e8dSdamien 	if (error != 0)
154375f8bb6dSdamien 		goto reset;
154413236e8dSdamien 	/* Stop Rx. */
154513236e8dSdamien 	error = athn_usb_wmi_cmd(usc, AR_WMI_CMD_STOP_RECV);
154613236e8dSdamien 	if (error != 0)
154775f8bb6dSdamien 		goto reset;
154813236e8dSdamien 
154975f8bb6dSdamien 	/* If band or bandwidth changes, we need to do a full reset. */
155075f8bb6dSdamien 	if (c->ic_flags != sc->curchan->ic_flags ||
155175f8bb6dSdamien 	    ((extc != NULL) ^ (sc->curchanext != NULL))) {
155275f8bb6dSdamien 		DPRINTFN(2, ("channel band switch\n"));
155375f8bb6dSdamien 		goto reset;
155475f8bb6dSdamien 	}
155575f8bb6dSdamien 
155675f8bb6dSdamien 	error = athn_set_chan(sc, c, extc);
155775f8bb6dSdamien 	if (AR_SREV_9271(sc) && error == 0)
155875f8bb6dSdamien 		ar9271_load_ani(sc);
155975f8bb6dSdamien 	if (error != 0) {
156075f8bb6dSdamien  reset:		/* Error found, try a full reset. */
156175f8bb6dSdamien 		DPRINTFN(3, ("needs a full reset\n"));
156213236e8dSdamien 		error = athn_hw_reset(sc, c, extc, 0);
156375f8bb6dSdamien 		if (error != 0)	/* Hopeless case. */
156475f8bb6dSdamien 			return (error);
1565f47be805Sstsp 
1566f47be805Sstsp 		error = athn_set_chan(sc, c, extc);
1567f47be805Sstsp 		if (AR_SREV_9271(sc) && error == 0)
1568f47be805Sstsp 			ar9271_load_ani(sc);
1569f47be805Sstsp 		if (error != 0)
1570f47be805Sstsp 			return (error);
157175f8bb6dSdamien 	}
157213236e8dSdamien 
1573f47be805Sstsp 	sc->ops.set_txpower(sc, c, extc);
1574f47be805Sstsp 
157503574ff2Sdamien 	error = athn_usb_wmi_cmd(usc, AR_WMI_CMD_START_RECV);
157603574ff2Sdamien 	if (error != 0)
157775f8bb6dSdamien 		return (error);
157813236e8dSdamien 	athn_rx_start(sc);
157913236e8dSdamien 
158013236e8dSdamien 	mode = htobe16(IEEE80211_IS_CHAN_2GHZ(c) ?
158113236e8dSdamien 	    AR_HTC_MODE_11NG : AR_HTC_MODE_11NA);
158213236e8dSdamien 	error = athn_usb_wmi_xcmd(usc, AR_WMI_CMD_SET_MODE,
158313236e8dSdamien 	    &mode, sizeof(mode), NULL);
158413236e8dSdamien 	if (error != 0)
158575f8bb6dSdamien 		return (error);
158675f8bb6dSdamien 
158713236e8dSdamien 	/* Re-enable interrupts. */
158813236e8dSdamien 	error = athn_usb_wmi_cmd(usc, AR_WMI_CMD_ENABLE_INTR);
158913236e8dSdamien 	return (error);
159013236e8dSdamien }
159113236e8dSdamien 
159213236e8dSdamien void
athn_usb_updateedca(struct ieee80211com * ic)159313236e8dSdamien athn_usb_updateedca(struct ieee80211com *ic)
159413236e8dSdamien {
159513236e8dSdamien 	struct athn_usb_softc *usc = ic->ic_softc;
159613236e8dSdamien 
159713236e8dSdamien 	/* Do it in a process context. */
159813236e8dSdamien 	athn_usb_do_async(usc, athn_usb_updateedca_cb, NULL, 0);
159913236e8dSdamien }
160013236e8dSdamien 
160113236e8dSdamien void
athn_usb_updateedca_cb(struct athn_usb_softc * usc,void * arg)160213236e8dSdamien athn_usb_updateedca_cb(struct athn_usb_softc *usc, void *arg)
160313236e8dSdamien {
160413236e8dSdamien 	int s;
160513236e8dSdamien 
160613236e8dSdamien 	s = splnet();
160713236e8dSdamien 	athn_updateedca(&usc->sc_sc.sc_ic);
160813236e8dSdamien 	splx(s);
160913236e8dSdamien }
161013236e8dSdamien 
161113236e8dSdamien void
athn_usb_updateslot(struct ieee80211com * ic)161213236e8dSdamien athn_usb_updateslot(struct ieee80211com *ic)
161313236e8dSdamien {
161413236e8dSdamien 	struct athn_usb_softc *usc = ic->ic_softc;
161513236e8dSdamien 
161613236e8dSdamien 	return;	/* XXX */
161713236e8dSdamien 	/* Do it in a process context. */
161813236e8dSdamien 	athn_usb_do_async(usc, athn_usb_updateslot_cb, NULL, 0);
161913236e8dSdamien }
162013236e8dSdamien 
162113236e8dSdamien void
athn_usb_updateslot_cb(struct athn_usb_softc * usc,void * arg)162213236e8dSdamien athn_usb_updateslot_cb(struct athn_usb_softc *usc, void *arg)
162313236e8dSdamien {
162413236e8dSdamien 	int s;
162513236e8dSdamien 
162613236e8dSdamien 	s = splnet();
162713236e8dSdamien 	athn_updateslot(&usc->sc_sc.sc_ic);
162813236e8dSdamien 	splx(s);
162913236e8dSdamien }
163013236e8dSdamien 
163113236e8dSdamien int
athn_usb_set_key(struct ieee80211com * ic,struct ieee80211_node * ni,struct ieee80211_key * k)163213236e8dSdamien athn_usb_set_key(struct ieee80211com *ic, struct ieee80211_node *ni,
163313236e8dSdamien     struct ieee80211_key *k)
163413236e8dSdamien {
163513236e8dSdamien 	struct athn_usb_softc *usc = ic->ic_softc;
163613236e8dSdamien 	struct athn_usb_cmd_key cmd;
163713236e8dSdamien 
163813236e8dSdamien 	/* Defer setting of WEP keys until interface is brought up. */
163913236e8dSdamien 	if ((ic->ic_if.if_flags & (IFF_UP | IFF_RUNNING)) !=
164013236e8dSdamien 	    (IFF_UP | IFF_RUNNING))
164113236e8dSdamien 		return (0);
164213236e8dSdamien 
1643*720f4ea4Sstsp 	if (k->k_cipher != IEEE80211_CIPHER_CCMP) {
1644*720f4ea4Sstsp 		/* Use software crypto for ciphers other than CCMP. */
1645*720f4ea4Sstsp 		return ieee80211_set_key(ic, ni, k);
1646*720f4ea4Sstsp 	}
1647*720f4ea4Sstsp 
164813236e8dSdamien 	/* Do it in a process context. */
164913236e8dSdamien 	cmd.ni = (ni != NULL) ? ieee80211_ref_node(ni) : NULL;
165013236e8dSdamien 	cmd.key = k;
165113236e8dSdamien 	athn_usb_do_async(usc, athn_usb_set_key_cb, &cmd, sizeof(cmd));
16524fac4e76Skrw 	usc->sc_key_tasks++;
16534fac4e76Skrw 	return EBUSY;
165413236e8dSdamien }
165513236e8dSdamien 
165613236e8dSdamien void
athn_usb_set_key_cb(struct athn_usb_softc * usc,void * arg)165713236e8dSdamien athn_usb_set_key_cb(struct athn_usb_softc *usc, void *arg)
165813236e8dSdamien {
165913236e8dSdamien 	struct ieee80211com *ic = &usc->sc_sc.sc_ic;
166013236e8dSdamien 	struct athn_usb_cmd_key *cmd = arg;
166113236e8dSdamien 	int s;
166213236e8dSdamien 
16634fac4e76Skrw 	usc->sc_key_tasks--;
16644fac4e76Skrw 
166513236e8dSdamien 	s = splnet();
1666ff1dd4b7Sstsp 	athn_usb_write_barrier(&usc->sc_sc);
166713236e8dSdamien 	athn_set_key(ic, cmd->ni, cmd->key);
16684fac4e76Skrw 	if (usc->sc_key_tasks == 0) {
16694fac4e76Skrw 		DPRINTF(("marking port %s valid\n",
16704fac4e76Skrw 		    ether_sprintf(cmd->ni->ni_macaddr)));
16714fac4e76Skrw 		cmd->ni->ni_port_valid = 1;
16724fac4e76Skrw 		ieee80211_set_link_state(ic, LINK_STATE_UP);
16734fac4e76Skrw 	}
167413236e8dSdamien 	if (cmd->ni != NULL)
167513236e8dSdamien 		ieee80211_release_node(ic, cmd->ni);
167613236e8dSdamien 	splx(s);
167713236e8dSdamien }
167813236e8dSdamien 
167913236e8dSdamien void
athn_usb_delete_key(struct ieee80211com * ic,struct ieee80211_node * ni,struct ieee80211_key * k)168013236e8dSdamien athn_usb_delete_key(struct ieee80211com *ic, struct ieee80211_node *ni,
168113236e8dSdamien     struct ieee80211_key *k)
168213236e8dSdamien {
168313236e8dSdamien 	struct athn_usb_softc *usc = ic->ic_softc;
168413236e8dSdamien 	struct athn_usb_cmd_key cmd;
168513236e8dSdamien 
168613236e8dSdamien 	if (!(ic->ic_if.if_flags & IFF_RUNNING) ||
168713236e8dSdamien 	    ic->ic_state != IEEE80211_S_RUN)
168813236e8dSdamien 		return;	/* Nothing to do. */
168913236e8dSdamien 
1690*720f4ea4Sstsp 	if (k->k_cipher != IEEE80211_CIPHER_CCMP) {
1691*720f4ea4Sstsp 		ieee80211_delete_key(ic, ni, k);
1692*720f4ea4Sstsp 		return;
1693*720f4ea4Sstsp 	}
1694*720f4ea4Sstsp 
169513236e8dSdamien 	/* Do it in a process context. */
169613236e8dSdamien 	cmd.ni = (ni != NULL) ? ieee80211_ref_node(ni) : NULL;
169713236e8dSdamien 	cmd.key = k;
169813236e8dSdamien 	athn_usb_do_async(usc, athn_usb_delete_key_cb, &cmd, sizeof(cmd));
169913236e8dSdamien }
170013236e8dSdamien 
170113236e8dSdamien void
athn_usb_delete_key_cb(struct athn_usb_softc * usc,void * arg)170213236e8dSdamien athn_usb_delete_key_cb(struct athn_usb_softc *usc, void *arg)
170313236e8dSdamien {
170413236e8dSdamien 	struct ieee80211com *ic = &usc->sc_sc.sc_ic;
170513236e8dSdamien 	struct athn_usb_cmd_key *cmd = arg;
170613236e8dSdamien 	int s;
170713236e8dSdamien 
170813236e8dSdamien 	s = splnet();
170913236e8dSdamien 	athn_delete_key(ic, cmd->ni, cmd->key);
171013236e8dSdamien 	if (cmd->ni != NULL)
171113236e8dSdamien 		ieee80211_release_node(ic, cmd->ni);
171213236e8dSdamien 	splx(s);
171313236e8dSdamien }
171413236e8dSdamien 
171513236e8dSdamien #ifndef IEEE80211_STA_ONLY
171613236e8dSdamien void
athn_usb_bcneof(struct usbd_xfer * xfer,void * priv,usbd_status status)1717ab0b1be7Smglocker athn_usb_bcneof(struct usbd_xfer *xfer, void *priv,
171813236e8dSdamien     usbd_status status)
171913236e8dSdamien {
172013236e8dSdamien 	struct athn_usb_tx_data *data = priv;
172113236e8dSdamien 	struct athn_usb_softc *usc = data->sc;
172213236e8dSdamien 
172313236e8dSdamien 	if (__predict_false(status == USBD_STALLED))
172413236e8dSdamien 		usbd_clear_endpoint_stall_async(usc->tx_data_pipe);
1725af7c78e3Sdamien 	usc->tx_bcn = data;
172613236e8dSdamien }
172713236e8dSdamien 
172813236e8dSdamien /*
172913236e8dSdamien  * Process Software Beacon Alert interrupts.
173013236e8dSdamien  */
173113236e8dSdamien void
athn_usb_swba(struct athn_usb_softc * usc)173213236e8dSdamien athn_usb_swba(struct athn_usb_softc *usc)
173313236e8dSdamien {
173413236e8dSdamien 	struct athn_softc *sc = &usc->sc_sc;
173513236e8dSdamien 	struct ieee80211com *ic = &sc->sc_ic;
173613236e8dSdamien 	struct athn_usb_tx_data *data;
173713236e8dSdamien 	struct ieee80211_frame *wh;
173813236e8dSdamien 	struct ar_stream_hdr *hdr;
173913236e8dSdamien 	struct ar_htc_frame_hdr *htc;
174013236e8dSdamien 	struct ar_tx_bcn *bcn;
174113236e8dSdamien 	struct mbuf *m;
174213236e8dSdamien 	int error;
174313236e8dSdamien 
174413236e8dSdamien 	if (ic->ic_dtim_count == 0)
174513236e8dSdamien 		ic->ic_dtim_count = ic->ic_dtim_period - 1;
174613236e8dSdamien 	else
174713236e8dSdamien 		ic->ic_dtim_count--;
174813236e8dSdamien 
174913236e8dSdamien 	/* Make sure previous beacon has been sent. */
1750af7c78e3Sdamien 	if (usc->tx_bcn == NULL)
175113236e8dSdamien 		return;
1752af7c78e3Sdamien 	data = usc->tx_bcn;
175313236e8dSdamien 
175413236e8dSdamien 	/* Get new beacon. */
175513236e8dSdamien 	m = ieee80211_beacon_alloc(ic, ic->ic_bss);
175613236e8dSdamien 	if (__predict_false(m == NULL))
175713236e8dSdamien 		return;
175813236e8dSdamien 	/* Assign sequence number. */
175913236e8dSdamien 	wh = mtod(m, struct ieee80211_frame *);
176013236e8dSdamien 	*(uint16_t *)&wh->i_seq[0] =
176113236e8dSdamien 	    htole16(ic->ic_bss->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT);
176213236e8dSdamien 	ic->ic_bss->ni_txseq++;
176313236e8dSdamien 
176413236e8dSdamien 	hdr = (struct ar_stream_hdr *)data->buf;
176513236e8dSdamien 	hdr->tag = htole16(AR_USB_TX_STREAM_TAG);
176613236e8dSdamien 	hdr->len = htole16(sizeof(*htc) + sizeof(*bcn) + m->m_pkthdr.len);
176713236e8dSdamien 
176813236e8dSdamien 	htc = (struct ar_htc_frame_hdr *)&hdr[1];
176913236e8dSdamien 	memset(htc, 0, sizeof(*htc));
177013236e8dSdamien 	htc->endpoint_id = usc->ep_bcn;
177113236e8dSdamien 	htc->payload_len = htobe16(sizeof(*bcn) + m->m_pkthdr.len);
177213236e8dSdamien 
177313236e8dSdamien 	bcn = (struct ar_tx_bcn *)&htc[1];
177413236e8dSdamien 	memset(bcn, 0, sizeof(*bcn));
177513236e8dSdamien 	bcn->vif_idx = 0;
177613236e8dSdamien 
17775c7fed39Sdlg 	m_copydata(m, 0, m->m_pkthdr.len, &bcn[1]);
177813236e8dSdamien 
177913236e8dSdamien 	usbd_setup_xfer(data->xfer, usc->tx_data_pipe, data, data->buf,
178013236e8dSdamien 	    sizeof(*hdr) + sizeof(*htc) + sizeof(*bcn) + m->m_pkthdr.len,
178113236e8dSdamien 	    USBD_SHORT_XFER_OK | USBD_NO_COPY, ATHN_USB_TX_TIMEOUT,
178213236e8dSdamien 	    athn_usb_bcneof);
178313236e8dSdamien 
178413236e8dSdamien 	m_freem(m);
1785af7c78e3Sdamien 	usc->tx_bcn = NULL;
178613236e8dSdamien 	error = usbd_transfer(data->xfer);
178713236e8dSdamien 	if (__predict_false(error != USBD_IN_PROGRESS && error != 0))
1788af7c78e3Sdamien 		usc->tx_bcn = data;
178913236e8dSdamien }
179013236e8dSdamien #endif
179113236e8dSdamien 
1792f47be805Sstsp /* Update current transmit rate for a node based on firmware Tx status. */
1793f47be805Sstsp void
athn_usb_tx_status(void * arg,struct ieee80211_node * ni)1794f47be805Sstsp athn_usb_tx_status(void *arg, struct ieee80211_node *ni)
1795f47be805Sstsp {
1796f47be805Sstsp 	struct ar_wmi_evt_txstatus *ts = arg;
1797f47be805Sstsp 	struct athn_node *an = (struct athn_node *)ni;
1798f47be805Sstsp 	uint8_t rate_index = (ts->rate & AR_HTC_TXSTAT_RATE);
1799f47be805Sstsp 
1800f47be805Sstsp 	if (an->sta_index != ts->cookie) /* Tx report for a different node */
1801f47be805Sstsp 		return;
1802f47be805Sstsp 
1803f47be805Sstsp 	if (ts->flags & AR_HTC_TXSTAT_MCS) {
1804f47be805Sstsp 		if (isset(ni->ni_rxmcs, rate_index))
1805f47be805Sstsp 			ni->ni_txmcs = rate_index;
1806f47be805Sstsp 	} else if (rate_index < ni->ni_rates.rs_nrates)
1807f47be805Sstsp 		ni->ni_txrate = rate_index;
1808f47be805Sstsp }
1809f47be805Sstsp 
181013236e8dSdamien void
athn_usb_rx_wmi_ctrl(struct athn_usb_softc * usc,uint8_t * buf,int len)181113236e8dSdamien athn_usb_rx_wmi_ctrl(struct athn_usb_softc *usc, uint8_t *buf, int len)
181213236e8dSdamien {
181313236e8dSdamien 	struct ar_wmi_cmd_hdr *wmi;
181413236e8dSdamien 	uint16_t cmd_id;
181513236e8dSdamien 
181613236e8dSdamien 	if (__predict_false(len < sizeof(*wmi)))
181713236e8dSdamien 		return;
181813236e8dSdamien 	wmi = (struct ar_wmi_cmd_hdr *)buf;
181913236e8dSdamien 	cmd_id = betoh16(wmi->cmd_id);
182013236e8dSdamien 
182113236e8dSdamien 	if (!(cmd_id & AR_WMI_EVT_FLAG)) {
182213236e8dSdamien 		if (usc->wait_cmd_id != cmd_id)
182313236e8dSdamien 			return;	/* Unexpected reply. */
182413236e8dSdamien 		if (usc->obuf != NULL) {
182513236e8dSdamien 			/* Copy answer into caller supplied buffer. */
182613236e8dSdamien 			memcpy(usc->obuf, &wmi[1], len - sizeof(*wmi));
182713236e8dSdamien 		}
182813236e8dSdamien 		/* Notify caller of completion. */
182913236e8dSdamien 		wakeup(&usc->wait_cmd_id);
183013236e8dSdamien 		return;
183113236e8dSdamien 	}
183213236e8dSdamien 	switch (cmd_id & 0xfff) {
183313236e8dSdamien #ifndef IEEE80211_STA_ONLY
183413236e8dSdamien 	case AR_WMI_EVT_SWBA:
183513236e8dSdamien 		athn_usb_swba(usc);
183613236e8dSdamien 		break;
183713236e8dSdamien #endif
1838f47be805Sstsp 	case AR_WMI_EVT_TXSTATUS: {
1839f47be805Sstsp 		struct ar_wmi_evt_txstatus_list *tsl;
1840f47be805Sstsp 		int i;
1841f47be805Sstsp 
1842f47be805Sstsp 		tsl = (struct ar_wmi_evt_txstatus_list *)&wmi[1];
1843f47be805Sstsp 		for (i = 0; i < tsl->count && i < nitems(tsl->ts); i++) {
1844f47be805Sstsp 			struct ieee80211com *ic = &usc->sc_sc.sc_ic;
1845f47be805Sstsp 			struct athn_node *an = (struct athn_node *)ic->ic_bss;
1846f47be805Sstsp 			struct ar_wmi_evt_txstatus *ts = &tsl->ts[i];
1847f47be805Sstsp 			uint8_t qid;
1848f47be805Sstsp 
1849f47be805Sstsp 			/* Skip the node we use to send management frames. */
1850f47be805Sstsp 			if (ts->cookie == 0)
1851f47be805Sstsp 				continue;
1852f47be805Sstsp 
1853f47be805Sstsp 			/* Skip Tx reports for non-data frame endpoints. */
1854f47be805Sstsp 			qid = (ts->rate & AR_HTC_TXSTAT_EPID) >>
1855f47be805Sstsp 				AR_HTC_TXSTAT_EPID_SHIFT;
1856f47be805Sstsp 			if (qid != usc->ep_data[EDCA_AC_BE] &&
1857f47be805Sstsp 			    qid != usc->ep_data[EDCA_AC_BK] &&
1858f47be805Sstsp 			    qid != usc->ep_data[EDCA_AC_VI] &&
1859f47be805Sstsp 			    qid != usc->ep_data[EDCA_AC_VO])
1860f47be805Sstsp 				continue;
1861f47be805Sstsp 
1862f47be805Sstsp 			if (ts->cookie == an->sta_index)
1863f47be805Sstsp 				athn_usb_tx_status(ts, ic->ic_bss);
1864f47be805Sstsp 			else
1865f47be805Sstsp 				ieee80211_iterate_nodes(ic, athn_usb_tx_status,
1866f47be805Sstsp 				    ts);
1867f47be805Sstsp 		}
186813236e8dSdamien 		break;
1869f47be805Sstsp 	}
187013236e8dSdamien 	case AR_WMI_EVT_FATAL:
187113236e8dSdamien 		printf("%s: fatal firmware error\n", usc->usb_dev.dv_xname);
187213236e8dSdamien 		break;
187313236e8dSdamien 	default:
187413236e8dSdamien 		DPRINTF(("WMI event %d ignored\n", cmd_id));
187513236e8dSdamien 		break;
187613236e8dSdamien 	}
187713236e8dSdamien }
187813236e8dSdamien 
187913236e8dSdamien void
athn_usb_intr(struct usbd_xfer * xfer,void * priv,usbd_status status)1880ab0b1be7Smglocker athn_usb_intr(struct usbd_xfer *xfer, void *priv,
188113236e8dSdamien     usbd_status status)
188213236e8dSdamien {
188313236e8dSdamien 	struct athn_usb_softc *usc = priv;
188413236e8dSdamien 	struct ar_htc_frame_hdr *htc;
188513236e8dSdamien 	struct ar_htc_msg_hdr *msg;
188613236e8dSdamien 	uint8_t *buf = usc->ibuf;
188713236e8dSdamien 	uint16_t msg_id;
188813236e8dSdamien 	int len;
188913236e8dSdamien 
189013236e8dSdamien 	if (__predict_false(status != USBD_NORMAL_COMPLETION)) {
189113236e8dSdamien 		DPRINTF(("intr status=%d\n", status));
189213236e8dSdamien 		if (status == USBD_STALLED)
189313236e8dSdamien 			usbd_clear_endpoint_stall_async(usc->rx_intr_pipe);
1894d25f2410Sstsp 		else if (status == USBD_IOERROR) {
1895d25f2410Sstsp 			/*
1896d25f2410Sstsp 			 * The device has gone away. If async commands are
1897d25f2410Sstsp 			 * pending or running ensure the device dies ASAP
1898d25f2410Sstsp 			 * and any blocked processes are woken up.
1899d25f2410Sstsp 			 */
1900d25f2410Sstsp 			if (usc->cmdq.queued > 0)
1901d25f2410Sstsp 				usbd_deactivate(usc->sc_udev);
1902d25f2410Sstsp 		}
190313236e8dSdamien 		return;
190413236e8dSdamien 	}
190513236e8dSdamien 	usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
190613236e8dSdamien 
190713236e8dSdamien 	/* Skip watchdog pattern if present. */
190813236e8dSdamien 	if (len >= 4 && *(uint32_t *)buf == htobe32(0x00c60000)) {
190913236e8dSdamien 		buf += 4;
191013236e8dSdamien 		len -= 4;
191113236e8dSdamien 	}
191213236e8dSdamien 	if (__predict_false(len < sizeof(*htc)))
191313236e8dSdamien 		return;
191413236e8dSdamien 	htc = (struct ar_htc_frame_hdr *)buf;
191513236e8dSdamien 	/* Skip HTC header. */
191613236e8dSdamien 	buf += sizeof(*htc);
191713236e8dSdamien 	len -= sizeof(*htc);
191813236e8dSdamien 
191913236e8dSdamien 	if (htc->endpoint_id != 0) {
192013236e8dSdamien 		if (__predict_false(htc->endpoint_id != usc->ep_ctrl))
192113236e8dSdamien 			return;
192213236e8dSdamien 		/* Remove trailer if present. */
192313236e8dSdamien 		if (htc->flags & AR_HTC_FLAG_TRAILER) {
192413236e8dSdamien 			if (__predict_false(len < htc->control[0]))
192513236e8dSdamien 				return;
192613236e8dSdamien 			len -= htc->control[0];
192713236e8dSdamien 		}
192813236e8dSdamien 		athn_usb_rx_wmi_ctrl(usc, buf, len);
192913236e8dSdamien 		return;
193013236e8dSdamien 	}
193113236e8dSdamien 	/* Endpoint 0 carries HTC messages. */
193213236e8dSdamien 	if (__predict_false(len < sizeof(*msg)))
193313236e8dSdamien 		return;
193413236e8dSdamien 	msg = (struct ar_htc_msg_hdr *)buf;
193513236e8dSdamien 	msg_id = betoh16(msg->msg_id);
193613236e8dSdamien 	DPRINTF(("Rx HTC message %d\n", msg_id));
193713236e8dSdamien 	switch (msg_id) {
193813236e8dSdamien 	case AR_HTC_MSG_READY:
193913236e8dSdamien 		if (usc->wait_msg_id != msg_id)
194013236e8dSdamien 			break;
194113236e8dSdamien 		usc->wait_msg_id = 0;
194213236e8dSdamien 		wakeup(&usc->wait_msg_id);
194313236e8dSdamien 		break;
194413236e8dSdamien 	case AR_HTC_MSG_CONN_SVC_RSP:
194513236e8dSdamien 		if (usc->wait_msg_id != msg_id)
194613236e8dSdamien 			break;
194713236e8dSdamien 		if (usc->msg_conn_svc_rsp != NULL) {
194813236e8dSdamien 			memcpy(usc->msg_conn_svc_rsp, &msg[1],
194913236e8dSdamien 			    sizeof(struct ar_htc_msg_conn_svc_rsp));
195013236e8dSdamien 		}
195113236e8dSdamien 		usc->wait_msg_id = 0;
195213236e8dSdamien 		wakeup(&usc->wait_msg_id);
195313236e8dSdamien 		break;
195413236e8dSdamien 	case AR_HTC_MSG_CONF_PIPE_RSP:
195513236e8dSdamien 		if (usc->wait_msg_id != msg_id)
195613236e8dSdamien 			break;
195713236e8dSdamien 		usc->wait_msg_id = 0;
195813236e8dSdamien 		wakeup(&usc->wait_msg_id);
195913236e8dSdamien 		break;
196013236e8dSdamien 	default:
196113236e8dSdamien 		DPRINTF(("HTC message %d ignored\n", msg_id));
196213236e8dSdamien 		break;
196313236e8dSdamien 	}
196413236e8dSdamien }
196513236e8dSdamien 
196613236e8dSdamien #if NBPFILTER > 0
196713236e8dSdamien void
athn_usb_rx_radiotap(struct athn_softc * sc,struct mbuf * m,struct ar_rx_status * rs)196813236e8dSdamien athn_usb_rx_radiotap(struct athn_softc *sc, struct mbuf *m,
196913236e8dSdamien     struct ar_rx_status *rs)
197013236e8dSdamien {
197113236e8dSdamien #define IEEE80211_RADIOTAP_F_SHORTGI	0x80	/* XXX from FBSD */
197213236e8dSdamien 
197313236e8dSdamien 	struct athn_rx_radiotap_header *tap = &sc->sc_rxtap;
197413236e8dSdamien 	struct ieee80211com *ic = &sc->sc_ic;
197513236e8dSdamien 	struct mbuf mb;
197613236e8dSdamien 	uint8_t rate;
197713236e8dSdamien 
197813236e8dSdamien 	tap->wr_flags = IEEE80211_RADIOTAP_F_FCS;
197913236e8dSdamien 	tap->wr_tsft = htole64(betoh64(rs->rs_tstamp));
198013236e8dSdamien 	tap->wr_chan_freq = htole16(ic->ic_bss->ni_chan->ic_freq);
198113236e8dSdamien 	tap->wr_chan_flags = htole16(ic->ic_bss->ni_chan->ic_flags);
198213236e8dSdamien 	tap->wr_dbm_antsignal = rs->rs_rssi;
198313236e8dSdamien 	/* XXX noise. */
198413236e8dSdamien 	tap->wr_antenna = rs->rs_antenna;
198513236e8dSdamien 	tap->wr_rate = 0;	/* In case it can't be found below. */
198613236e8dSdamien 	rate = rs->rs_rate;
198713236e8dSdamien 	if (rate & 0x80) {		/* HT. */
198813236e8dSdamien 		/* Bit 7 set means HT MCS instead of rate. */
198913236e8dSdamien 		tap->wr_rate = rate;
199013236e8dSdamien 		if (!(rs->rs_flags & AR_RXS_FLAG_GI))
199113236e8dSdamien 			tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTGI;
199213236e8dSdamien 
199313236e8dSdamien 	} else if (rate & 0x10) {	/* CCK. */
199413236e8dSdamien 		if (rate & 0x04)
199513236e8dSdamien 			tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
199613236e8dSdamien 		switch (rate & ~0x14) {
199713236e8dSdamien 		case 0xb: tap->wr_rate =   2; break;
199813236e8dSdamien 		case 0xa: tap->wr_rate =   4; break;
199913236e8dSdamien 		case 0x9: tap->wr_rate =  11; break;
200013236e8dSdamien 		case 0x8: tap->wr_rate =  22; break;
200113236e8dSdamien 		}
200213236e8dSdamien 	} else {			/* OFDM. */
200313236e8dSdamien 		switch (rate) {
200413236e8dSdamien 		case 0xb: tap->wr_rate =  12; break;
200513236e8dSdamien 		case 0xf: tap->wr_rate =  18; break;
200613236e8dSdamien 		case 0xa: tap->wr_rate =  24; break;
200713236e8dSdamien 		case 0xe: tap->wr_rate =  36; break;
200813236e8dSdamien 		case 0x9: tap->wr_rate =  48; break;
200913236e8dSdamien 		case 0xd: tap->wr_rate =  72; break;
201013236e8dSdamien 		case 0x8: tap->wr_rate =  96; break;
201113236e8dSdamien 		case 0xc: tap->wr_rate = 108; break;
201213236e8dSdamien 		}
201313236e8dSdamien 	}
201413236e8dSdamien 	mb.m_data = (caddr_t)tap;
201513236e8dSdamien 	mb.m_len = sc->sc_rxtap_len;
201613236e8dSdamien 	mb.m_next = m;
201713236e8dSdamien 	mb.m_nextpkt = NULL;
201813236e8dSdamien 	mb.m_type = 0;
201913236e8dSdamien 	mb.m_flags = 0;
202013236e8dSdamien 	bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_IN);
202113236e8dSdamien }
202213236e8dSdamien #endif
202313236e8dSdamien 
202413236e8dSdamien void
athn_usb_rx_frame(struct athn_usb_softc * usc,struct mbuf * m,struct mbuf_list * ml)20258fbaf8a2Sstsp athn_usb_rx_frame(struct athn_usb_softc *usc, struct mbuf *m,
20268fbaf8a2Sstsp     struct mbuf_list *ml)
202713236e8dSdamien {
202813236e8dSdamien 	struct athn_softc *sc = &usc->sc_sc;
202913236e8dSdamien 	struct ieee80211com *ic = &sc->sc_ic;
203013236e8dSdamien 	struct ifnet *ifp = &ic->ic_if;
203113236e8dSdamien 	struct ieee80211_frame *wh;
203213236e8dSdamien 	struct ieee80211_node *ni;
203313236e8dSdamien 	struct ieee80211_rxinfo rxi;
203413236e8dSdamien 	struct ar_htc_frame_hdr *htc;
203513236e8dSdamien 	struct ar_rx_status *rs;
203613236e8dSdamien 	uint16_t datalen;
203733c953a9Smikeb 	int s;
203813236e8dSdamien 
2039d82c2cdfSdamien 	if (__predict_false(m->m_len < sizeof(*htc)))
2040d82c2cdfSdamien 		goto skip;
204113236e8dSdamien 	htc = mtod(m, struct ar_htc_frame_hdr *);
204213236e8dSdamien 	if (__predict_false(htc->endpoint_id == 0)) {
204313236e8dSdamien 		DPRINTF(("bad endpoint %d\n", htc->endpoint_id));
2044d82c2cdfSdamien 		goto skip;
204513236e8dSdamien 	}
204613236e8dSdamien 	if (htc->flags & AR_HTC_FLAG_TRAILER) {
2047d82c2cdfSdamien 		if (m->m_len < htc->control[0])
2048d82c2cdfSdamien 			goto skip;
204913236e8dSdamien 		m_adj(m, -(int)htc->control[0]);
205013236e8dSdamien 	}
205113236e8dSdamien 	m_adj(m, sizeof(*htc));	/* Strip HTC header. */
2052d82c2cdfSdamien 
2053d82c2cdfSdamien 	if (__predict_false(m->m_len < sizeof(*rs)))
2054d82c2cdfSdamien 		goto skip;
205513236e8dSdamien 	rs = mtod(m, struct ar_rx_status *);
2056d82c2cdfSdamien 
205713236e8dSdamien 	/* Make sure that payload fits. */
205813236e8dSdamien 	datalen = betoh16(rs->rs_datalen);
2059d82c2cdfSdamien 	if (__predict_false(m->m_len < sizeof(*rs) + datalen))
2060d82c2cdfSdamien 		goto skip;
2061d82c2cdfSdamien 
2062d82c2cdfSdamien 	if (__predict_false(datalen < sizeof(*wh) + IEEE80211_CRC_LEN))
2063d82c2cdfSdamien 		goto skip;
2064d82c2cdfSdamien 
2065ff1dd4b7Sstsp 	if (rs->rs_status != 0) {
2066ff1dd4b7Sstsp 		if (rs->rs_status & AR_RXS_RXERR_DECRYPT)
2067ff1dd4b7Sstsp 			ic->ic_stats.is_ccmp_dec_errs++;
2068ff1dd4b7Sstsp 		ifp->if_ierrors++;
2069ff1dd4b7Sstsp 		goto skip;
2070ff1dd4b7Sstsp 	}
207113236e8dSdamien 	m_adj(m, sizeof(*rs));	/* Strip Rx status. */
207213236e8dSdamien 
207333c953a9Smikeb 	s = splnet();
207433c953a9Smikeb 
2075d82c2cdfSdamien 	/* Grab a reference to the source node. */
2076d82c2cdfSdamien 	wh = mtod(m, struct ieee80211_frame *);
2077d82c2cdfSdamien 	ni = ieee80211_find_rxnode(ic, wh);
207813236e8dSdamien 
2079d82c2cdfSdamien 	/* Remove any HW padding after the 802.11 header. */
2080d82c2cdfSdamien 	if (!(wh->i_fc[0] & IEEE80211_FC0_TYPE_CTL)) {
2081d82c2cdfSdamien 		u_int hdrlen = ieee80211_get_hdrlen(wh);
2082d82c2cdfSdamien 		if (hdrlen & 3) {
20839b2ad23fSderaadt 			memmove((caddr_t)wh + 2, wh, hdrlen);
2084d82c2cdfSdamien 			m_adj(m, 2);
2085d82c2cdfSdamien 		}
2086ff1dd4b7Sstsp 		wh = mtod(m, struct ieee80211_frame *);
2087d82c2cdfSdamien 	}
208813236e8dSdamien #if NBPFILTER > 0
208913236e8dSdamien 	if (__predict_false(sc->sc_drvbpf != NULL))
209013236e8dSdamien 		athn_usb_rx_radiotap(sc, m, rs);
209113236e8dSdamien #endif
209213236e8dSdamien 	/* Trim 802.11 FCS after radiotap. */
209313236e8dSdamien 	m_adj(m, -IEEE80211_CRC_LEN);
209413236e8dSdamien 
2095d82c2cdfSdamien 	/* Send the frame to the 802.11 layer. */
209652a13037Sstsp 	memset(&rxi, 0, sizeof(rxi));
209713236e8dSdamien 	rxi.rxi_rssi = rs->rs_rssi + AR_USB_DEFAULT_NF;
209813236e8dSdamien 	rxi.rxi_tstamp = betoh64(rs->rs_tstamp);
2099ff1dd4b7Sstsp 	if (!(wh->i_fc[0] & IEEE80211_FC0_TYPE_CTL) &&
2100ff1dd4b7Sstsp 	    (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) &&
2101ff1dd4b7Sstsp 	    (ic->ic_flags & IEEE80211_F_RSNON) &&
2102ff1dd4b7Sstsp 	    (ni->ni_flags & IEEE80211_NODE_RXPROT) &&
2103ff1dd4b7Sstsp 	    (ni->ni_rsncipher == IEEE80211_CIPHER_CCMP ||
2104ff1dd4b7Sstsp 	    (IEEE80211_IS_MULTICAST(wh->i_addr1) &&
210586e3bb8aSstsp 	    ni->ni_rsngroupcipher == IEEE80211_CIPHER_CCMP))) {
2106ff1dd4b7Sstsp 		if (ar5008_ccmp_decap(sc, m, ni) != 0) {
2107ff1dd4b7Sstsp 			ifp->if_ierrors++;
2108ff1dd4b7Sstsp 			ieee80211_release_node(ic, ni);
2109ff1dd4b7Sstsp 			splx(s);
2110ff1dd4b7Sstsp 			goto skip;
2111ff1dd4b7Sstsp 		}
2112ff1dd4b7Sstsp 		rxi.rxi_flags |= IEEE80211_RXI_HWDEC;
2113ff1dd4b7Sstsp 	}
21148fbaf8a2Sstsp 	ieee80211_inputm(ifp, m, ni, &rxi, ml);
2115d82c2cdfSdamien 
211613236e8dSdamien 	/* Node is no longer needed. */
211713236e8dSdamien 	ieee80211_release_node(ic, ni);
211833c953a9Smikeb 	splx(s);
2119d82c2cdfSdamien 	return;
2120d82c2cdfSdamien  skip:
2121d82c2cdfSdamien 	m_freem(m);
212213236e8dSdamien }
212313236e8dSdamien 
212413236e8dSdamien void
athn_usb_rxeof(struct usbd_xfer * xfer,void * priv,usbd_status status)2125ab0b1be7Smglocker athn_usb_rxeof(struct usbd_xfer *xfer, void *priv,
212613236e8dSdamien     usbd_status status)
212713236e8dSdamien {
21288fbaf8a2Sstsp 	struct mbuf_list ml = MBUF_LIST_INITIALIZER();
212913236e8dSdamien 	struct athn_usb_rx_data *data = priv;
213013236e8dSdamien 	struct athn_usb_softc *usc = data->sc;
21313fe3de2aSstsp 	struct athn_softc *sc = &usc->sc_sc;
21323fe3de2aSstsp 	struct ifnet *ifp = &sc->sc_ic.ic_if;
213313236e8dSdamien 	struct athn_usb_rx_stream *stream = &usc->rx_stream;
213413236e8dSdamien 	uint8_t *buf = data->buf;
213513236e8dSdamien 	struct ar_stream_hdr *hdr;
213613236e8dSdamien 	struct mbuf *m;
213713236e8dSdamien 	uint16_t pktlen;
213813236e8dSdamien 	int off, len;
213913236e8dSdamien 
214013236e8dSdamien 	if (__predict_false(status != USBD_NORMAL_COMPLETION)) {
214113236e8dSdamien 		DPRINTF(("RX status=%d\n", status));
214213236e8dSdamien 		if (status == USBD_STALLED)
214313236e8dSdamien 			usbd_clear_endpoint_stall_async(usc->rx_data_pipe);
214413236e8dSdamien 		if (status != USBD_CANCELLED)
214513236e8dSdamien 			goto resubmit;
214613236e8dSdamien 		return;
214713236e8dSdamien 	}
214813236e8dSdamien 	usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
214913236e8dSdamien 
215013236e8dSdamien 	if (stream->left > 0) {
215113236e8dSdamien 		if (len >= stream->left) {
215213236e8dSdamien 			/* We have all our pktlen bytes now. */
215313236e8dSdamien 			if (__predict_true(stream->m != NULL)) {
215413236e8dSdamien 				memcpy(mtod(stream->m, uint8_t *) +
215513236e8dSdamien 				    stream->moff, buf, stream->left);
21568fbaf8a2Sstsp 				athn_usb_rx_frame(usc, stream->m, &ml);
215713236e8dSdamien 				stream->m = NULL;
215813236e8dSdamien 			}
215913236e8dSdamien 			/* Next header is 32-bit aligned. */
216013236e8dSdamien 			off = (stream->left + 3) & ~3;
216113236e8dSdamien 			buf += off;
216213236e8dSdamien 			len -= off;
216313236e8dSdamien 			stream->left = 0;
216413236e8dSdamien 		} else {
216513236e8dSdamien 			/* Still need more bytes, save what we have. */
216613236e8dSdamien 			if (__predict_true(stream->m != NULL)) {
216713236e8dSdamien 				memcpy(mtod(stream->m, uint8_t *) +
216813236e8dSdamien 				    stream->moff, buf, len);
216913236e8dSdamien 				stream->moff += len;
217013236e8dSdamien 			}
217113236e8dSdamien 			stream->left -= len;
217213236e8dSdamien 			goto resubmit;
217313236e8dSdamien 		}
217413236e8dSdamien 	}
217513236e8dSdamien 	KASSERT(stream->left == 0);
217613236e8dSdamien 	while (len >= sizeof(*hdr)) {
217713236e8dSdamien 		hdr = (struct ar_stream_hdr *)buf;
217813236e8dSdamien 		if (hdr->tag != htole16(AR_USB_RX_STREAM_TAG)) {
217913236e8dSdamien 			DPRINTF(("invalid tag 0x%x\n", hdr->tag));
218013236e8dSdamien 			break;
218113236e8dSdamien 		}
218213236e8dSdamien 		pktlen = letoh16(hdr->len);
218313236e8dSdamien 		buf += sizeof(*hdr);
218413236e8dSdamien 		len -= sizeof(*hdr);
218513236e8dSdamien 
218613236e8dSdamien 		if (__predict_true(pktlen <= MCLBYTES)) {
218713236e8dSdamien 			/* Allocate an mbuf to store the next pktlen bytes. */
218813236e8dSdamien 			MGETHDR(m, M_DONTWAIT, MT_DATA);
218913236e8dSdamien 			if (__predict_true(m != NULL)) {
219013236e8dSdamien 				m->m_pkthdr.len = m->m_len = pktlen;
219113236e8dSdamien 				if (pktlen > MHLEN) {
219213236e8dSdamien 					MCLGET(m, M_DONTWAIT);
219313236e8dSdamien 					if (!(m->m_flags & M_EXT)) {
219413236e8dSdamien 						m_free(m);
219513236e8dSdamien 						m = NULL;
219613236e8dSdamien 					}
219713236e8dSdamien 				}
219813236e8dSdamien 			}
219913236e8dSdamien 		} else	/* Drop frames larger than MCLBYTES. */
220013236e8dSdamien 			m = NULL;
22013fe3de2aSstsp 
22023fe3de2aSstsp 		if (m == NULL)
22033fe3de2aSstsp 			ifp->if_ierrors++;
22043fe3de2aSstsp 
220513236e8dSdamien 		/*
220613236e8dSdamien 		 * NB: m can be NULL, in which case the next pktlen bytes
220713236e8dSdamien 		 * will be discarded from the Rx stream.
220813236e8dSdamien 		 */
220913236e8dSdamien 		if (pktlen > len) {
221013236e8dSdamien 			/* Need more bytes, save what we have. */
221113236e8dSdamien 			stream->m = m;	/* NB: m can be NULL. */
221213236e8dSdamien 			if (__predict_true(stream->m != NULL)) {
221313236e8dSdamien 				memcpy(mtod(stream->m, uint8_t *), buf, len);
221413236e8dSdamien 				stream->moff = len;
221513236e8dSdamien 			}
221613236e8dSdamien 			stream->left = pktlen - len;
221713236e8dSdamien 			goto resubmit;
221813236e8dSdamien 		}
221913236e8dSdamien 		if (__predict_true(m != NULL)) {
222013236e8dSdamien 			/* We have all the pktlen bytes in this xfer. */
222113236e8dSdamien 			memcpy(mtod(m, uint8_t *), buf, pktlen);
22228fbaf8a2Sstsp 			athn_usb_rx_frame(usc, m, &ml);
222313236e8dSdamien 		}
222413236e8dSdamien 
222513236e8dSdamien 		/* Next header is 32-bit aligned. */
222613236e8dSdamien 		off = (pktlen + 3) & ~3;
222713236e8dSdamien 		buf += off;
222813236e8dSdamien 		len -= off;
222913236e8dSdamien 	}
22308fbaf8a2Sstsp 	if_input(ifp, &ml);
223113236e8dSdamien 
223213236e8dSdamien  resubmit:
223313236e8dSdamien 	/* Setup a new transfer. */
223413236e8dSdamien 	usbd_setup_xfer(xfer, usc->rx_data_pipe, data, data->buf,
223513236e8dSdamien 	    ATHN_USB_RXBUFSZ, USBD_SHORT_XFER_OK | USBD_NO_COPY,
223613236e8dSdamien 	    USBD_NO_TIMEOUT, athn_usb_rxeof);
223713236e8dSdamien 	(void)usbd_transfer(xfer);
223813236e8dSdamien }
223913236e8dSdamien 
224013236e8dSdamien void
athn_usb_txeof(struct usbd_xfer * xfer,void * priv,usbd_status status)2241ab0b1be7Smglocker athn_usb_txeof(struct usbd_xfer *xfer, void *priv,
224213236e8dSdamien     usbd_status status)
224313236e8dSdamien {
224413236e8dSdamien 	struct athn_usb_tx_data *data = priv;
224513236e8dSdamien 	struct athn_usb_softc *usc = data->sc;
224613236e8dSdamien 	struct athn_softc *sc = &usc->sc_sc;
224713236e8dSdamien 	struct ifnet *ifp = &sc->sc_ic.ic_if;
224813236e8dSdamien 	int s;
224913236e8dSdamien 
225013236e8dSdamien 	s = splnet();
225113236e8dSdamien 	/* Put this Tx buffer back to our free list. */
225213236e8dSdamien 	TAILQ_INSERT_TAIL(&usc->tx_free_list, data, next);
225313236e8dSdamien 
225413236e8dSdamien 	if (__predict_false(status != USBD_NORMAL_COMPLETION)) {
225513236e8dSdamien 		DPRINTF(("TX status=%d\n", status));
225613236e8dSdamien 		if (status == USBD_STALLED)
225713236e8dSdamien 			usbd_clear_endpoint_stall_async(usc->tx_data_pipe);
225813236e8dSdamien 		ifp->if_oerrors++;
225913236e8dSdamien 		splx(s);
226013236e8dSdamien 		/* XXX Why return? */
226113236e8dSdamien 		return;
226213236e8dSdamien 	}
226313236e8dSdamien 	sc->sc_tx_timer = 0;
226413236e8dSdamien 
226513236e8dSdamien 	/* We just released a Tx buffer, notify Tx. */
2266de6cd8fbSdlg 	if (ifq_is_oactive(&ifp->if_snd)) {
2267de6cd8fbSdlg 		ifq_clr_oactive(&ifp->if_snd);
226813236e8dSdamien 		ifp->if_start(ifp);
226913236e8dSdamien 	}
227013236e8dSdamien 	splx(s);
227113236e8dSdamien }
227213236e8dSdamien 
227313236e8dSdamien int
athn_usb_tx(struct athn_softc * sc,struct mbuf * m,struct ieee80211_node * ni)227413236e8dSdamien athn_usb_tx(struct athn_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
227513236e8dSdamien {
227613236e8dSdamien 	struct athn_usb_softc *usc = (struct athn_usb_softc *)sc;
227713236e8dSdamien 	struct athn_node *an = (struct athn_node *)ni;
227813236e8dSdamien 	struct ieee80211com *ic = &sc->sc_ic;
227913236e8dSdamien 	struct ieee80211_frame *wh;
228013236e8dSdamien 	struct ieee80211_key *k = NULL;
228113236e8dSdamien 	struct athn_usb_tx_data *data;
228213236e8dSdamien 	struct ar_stream_hdr *hdr;
228313236e8dSdamien 	struct ar_htc_frame_hdr *htc;
228413236e8dSdamien 	struct ar_tx_frame *txf;
228513236e8dSdamien 	struct ar_tx_mgmt *txm;
228613236e8dSdamien 	uint8_t *frm;
228713236e8dSdamien 	uint16_t qos;
2288f47be805Sstsp 	uint8_t qid, tid = 0;
228913236e8dSdamien 	int hasqos, xferlen, error;
229013236e8dSdamien 
229113236e8dSdamien 	wh = mtod(m, struct ieee80211_frame *);
229213236e8dSdamien 	if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
229313236e8dSdamien 		k = ieee80211_get_txkey(ic, wh, ni);
2294ff1dd4b7Sstsp 		if (k->k_cipher == IEEE80211_CIPHER_CCMP) {
2295ff1dd4b7Sstsp 			u_int hdrlen = ieee80211_get_hdrlen(wh);
2296ff1dd4b7Sstsp 			if (ar5008_ccmp_encap(m, hdrlen, k) != 0)
2297ff1dd4b7Sstsp 				return (ENOBUFS);
2298ff1dd4b7Sstsp 		} else {
229913236e8dSdamien 			if ((m = ieee80211_encrypt(ic, m, k)) == NULL)
230013236e8dSdamien 				return (ENOBUFS);
2301ff1dd4b7Sstsp 			k = NULL; /* skip hardware crypto further below */
2302ff1dd4b7Sstsp 		}
230313236e8dSdamien 		wh = mtod(m, struct ieee80211_frame *);
230413236e8dSdamien 	}
230513236e8dSdamien 	if ((hasqos = ieee80211_has_qos(wh))) {
230613236e8dSdamien 		qos = ieee80211_get_qos(wh);
230713236e8dSdamien 		tid = qos & IEEE80211_QOS_TID;
230813236e8dSdamien 		qid = ieee80211_up_to_ac(ic, tid);
230913236e8dSdamien 	} else
231013236e8dSdamien 		qid = EDCA_AC_BE;
231113236e8dSdamien 
231213236e8dSdamien 	/* Grab a Tx buffer from our free list. */
231313236e8dSdamien 	data = TAILQ_FIRST(&usc->tx_free_list);
231413236e8dSdamien 	TAILQ_REMOVE(&usc->tx_free_list, data, next);
231513236e8dSdamien 
231613236e8dSdamien #if NBPFILTER > 0
231713236e8dSdamien 	/* XXX Change radiotap Tx header for USB (no txrate). */
231813236e8dSdamien 	if (__predict_false(sc->sc_drvbpf != NULL)) {
231913236e8dSdamien 		struct athn_tx_radiotap_header *tap = &sc->sc_txtap;
232013236e8dSdamien 		struct mbuf mb;
232113236e8dSdamien 
232213236e8dSdamien 		tap->wt_flags = 0;
232313236e8dSdamien 		tap->wt_chan_freq = htole16(ic->ic_bss->ni_chan->ic_freq);
232413236e8dSdamien 		tap->wt_chan_flags = htole16(ic->ic_bss->ni_chan->ic_flags);
232513236e8dSdamien 		mb.m_data = (caddr_t)tap;
232613236e8dSdamien 		mb.m_len = sc->sc_txtap_len;
232713236e8dSdamien 		mb.m_next = m;
232813236e8dSdamien 		mb.m_nextpkt = NULL;
232913236e8dSdamien 		mb.m_type = 0;
233013236e8dSdamien 		mb.m_flags = 0;
233113236e8dSdamien 		bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_OUT);
233213236e8dSdamien 	}
233313236e8dSdamien #endif
233413236e8dSdamien 
233513236e8dSdamien 	/* NB: We don't take advantage of USB Tx stream mode for now. */
233613236e8dSdamien 	hdr = (struct ar_stream_hdr *)data->buf;
233713236e8dSdamien 	hdr->tag = htole16(AR_USB_TX_STREAM_TAG);
233813236e8dSdamien 
233913236e8dSdamien 	htc = (struct ar_htc_frame_hdr *)&hdr[1];
234013236e8dSdamien 	memset(htc, 0, sizeof(*htc));
234113236e8dSdamien 	if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
234213236e8dSdamien 	    IEEE80211_FC0_TYPE_DATA) {
234313236e8dSdamien 		htc->endpoint_id = usc->ep_data[qid];
234413236e8dSdamien 
234513236e8dSdamien 		txf = (struct ar_tx_frame *)&htc[1];
234613236e8dSdamien 		memset(txf, 0, sizeof(*txf));
234713236e8dSdamien 		txf->data_type = AR_HTC_NORMAL;
2348f47be805Sstsp 		txf->node_idx = an->sta_index;
234913236e8dSdamien 		txf->vif_idx = 0;
235013236e8dSdamien 		txf->tid = tid;
235113236e8dSdamien 		if (m->m_pkthdr.len + IEEE80211_CRC_LEN > ic->ic_rtsthreshold)
235213236e8dSdamien 			txf->flags |= htobe32(AR_HTC_TX_RTSCTS);
235313236e8dSdamien 		else if (ic->ic_flags & IEEE80211_F_USEPROT) {
235413236e8dSdamien 			if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
235513236e8dSdamien 				txf->flags |= htobe32(AR_HTC_TX_CTSONLY);
235613236e8dSdamien 			else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
235713236e8dSdamien 				txf->flags |= htobe32(AR_HTC_TX_RTSCTS);
235813236e8dSdamien 		}
2359ff1dd4b7Sstsp 
2360ff1dd4b7Sstsp 		if (k != NULL) {
2361ff1dd4b7Sstsp 			/* Map 802.11 cipher to hardware encryption type. */
2362ff1dd4b7Sstsp 			if (k->k_cipher == IEEE80211_CIPHER_CCMP) {
2363ff1dd4b7Sstsp 				txf->key_type = AR_ENCR_TYPE_AES;
2364ff1dd4b7Sstsp 			} else
2365ff1dd4b7Sstsp 				panic("unsupported cipher");
2366ff1dd4b7Sstsp 			/*
2367ff1dd4b7Sstsp 			 * NB: The key cache entry index is stored in the key
2368ff1dd4b7Sstsp 			 * private field when the key is installed.
2369ff1dd4b7Sstsp 			 */
2370ff1dd4b7Sstsp 			txf->key_idx = (uintptr_t)k->k_priv;
2371ff1dd4b7Sstsp 		} else
237213236e8dSdamien 			txf->key_idx = 0xff;
2373ff1dd4b7Sstsp 
2374f47be805Sstsp 		txf->cookie = an->sta_index;
237513236e8dSdamien 		frm = (uint8_t *)&txf[1];
237613236e8dSdamien 	} else {
237713236e8dSdamien 		htc->endpoint_id = usc->ep_mgmt;
237813236e8dSdamien 
237913236e8dSdamien 		txm = (struct ar_tx_mgmt *)&htc[1];
238013236e8dSdamien 		memset(txm, 0, sizeof(*txm));
2381f47be805Sstsp 		txm->node_idx = an->sta_index;
238213236e8dSdamien 		txm->vif_idx = 0;
238313236e8dSdamien 		txm->key_idx = 0xff;
2384f47be805Sstsp 		txm->cookie = an->sta_index;
238513236e8dSdamien 		frm = (uint8_t *)&txm[1];
238613236e8dSdamien 	}
238713236e8dSdamien 	/* Copy payload. */
23885c7fed39Sdlg 	m_copydata(m, 0, m->m_pkthdr.len, frm);
238913236e8dSdamien 	frm += m->m_pkthdr.len;
239013236e8dSdamien 	m_freem(m);
239113236e8dSdamien 
239213236e8dSdamien 	/* Finalize headers. */
239313236e8dSdamien 	htc->payload_len = htobe16(frm - (uint8_t *)&htc[1]);
239413236e8dSdamien 	hdr->len = htole16(frm - (uint8_t *)&hdr[1]);
239513236e8dSdamien 	xferlen = frm - data->buf;
239613236e8dSdamien 
239713236e8dSdamien 	usbd_setup_xfer(data->xfer, usc->tx_data_pipe, data, data->buf,
239813236e8dSdamien 	    xferlen, USBD_FORCE_SHORT_XFER | USBD_NO_COPY, ATHN_USB_TX_TIMEOUT,
239913236e8dSdamien 	    athn_usb_txeof);
240013236e8dSdamien 	error = usbd_transfer(data->xfer);
240113236e8dSdamien 	if (__predict_false(error != USBD_IN_PROGRESS && error != 0)) {
240213236e8dSdamien 		/* Put this Tx buffer back to our free list. */
240313236e8dSdamien 		TAILQ_INSERT_TAIL(&usc->tx_free_list, data, next);
240413236e8dSdamien 		return (error);
240513236e8dSdamien 	}
240613236e8dSdamien 	ieee80211_release_node(ic, ni);
240713236e8dSdamien 	return (0);
240813236e8dSdamien }
240913236e8dSdamien 
241013236e8dSdamien void
athn_usb_start(struct ifnet * ifp)241113236e8dSdamien athn_usb_start(struct ifnet *ifp)
241213236e8dSdamien {
241313236e8dSdamien 	struct athn_softc *sc = ifp->if_softc;
241413236e8dSdamien 	struct athn_usb_softc *usc = (struct athn_usb_softc *)sc;
241513236e8dSdamien 	struct ieee80211com *ic = &sc->sc_ic;
241613236e8dSdamien 	struct ieee80211_node *ni;
241713236e8dSdamien 	struct mbuf *m;
241813236e8dSdamien 
2419de6cd8fbSdlg 	if (!(ifp->if_flags & IFF_RUNNING) || ifq_is_oactive(&ifp->if_snd))
242013236e8dSdamien 		return;
242113236e8dSdamien 
242213236e8dSdamien 	for (;;) {
242313236e8dSdamien 		if (TAILQ_EMPTY(&usc->tx_free_list)) {
2424de6cd8fbSdlg 			ifq_set_oactive(&ifp->if_snd);
242513236e8dSdamien 			break;
242613236e8dSdamien 		}
242713236e8dSdamien 		/* Send pending management frames first. */
2428351e1934Sdlg 		m = mq_dequeue(&ic->ic_mgtq);
242913236e8dSdamien 		if (m != NULL) {
24306da4b19dSmpi 			ni = m->m_pkthdr.ph_cookie;
243113236e8dSdamien 			goto sendit;
243213236e8dSdamien 		}
243313236e8dSdamien 		if (ic->ic_state != IEEE80211_S_RUN)
243413236e8dSdamien 			break;
243513236e8dSdamien 
243613236e8dSdamien 		/* Encapsulate and send data frames. */
243763bcfa73Spatrick 		m = ifq_dequeue(&ifp->if_snd);
243813236e8dSdamien 		if (m == NULL)
243913236e8dSdamien 			break;
244013236e8dSdamien #if NBPFILTER > 0
244113236e8dSdamien 		if (ifp->if_bpf != NULL)
244213236e8dSdamien 			bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
244313236e8dSdamien #endif
244413236e8dSdamien 		if ((m = ieee80211_encap(ifp, m, &ni)) == NULL)
244513236e8dSdamien 			continue;
244613236e8dSdamien  sendit:
244713236e8dSdamien #if NBPFILTER > 0
244813236e8dSdamien 		if (ic->ic_rawbpf != NULL)
244913236e8dSdamien 			bpf_mtap(ic->ic_rawbpf, m, BPF_DIRECTION_OUT);
245013236e8dSdamien #endif
245113236e8dSdamien 		if (athn_usb_tx(sc, m, ni) != 0) {
245213236e8dSdamien 			ieee80211_release_node(ic, ni);
245313236e8dSdamien 			ifp->if_oerrors++;
245413236e8dSdamien 			continue;
245513236e8dSdamien 		}
245613236e8dSdamien 
245713236e8dSdamien 		sc->sc_tx_timer = 5;
245813236e8dSdamien 		ifp->if_timer = 1;
245913236e8dSdamien 	}
246013236e8dSdamien }
246113236e8dSdamien 
246213236e8dSdamien void
athn_usb_watchdog(struct ifnet * ifp)246313236e8dSdamien athn_usb_watchdog(struct ifnet *ifp)
246413236e8dSdamien {
246513236e8dSdamien 	struct athn_softc *sc = ifp->if_softc;
246613236e8dSdamien 
246713236e8dSdamien 	ifp->if_timer = 0;
246813236e8dSdamien 
246913236e8dSdamien 	if (sc->sc_tx_timer > 0) {
247013236e8dSdamien 		if (--sc->sc_tx_timer == 0) {
247113236e8dSdamien 			printf("%s: device timeout\n", sc->sc_dev.dv_xname);
247213236e8dSdamien 			/* athn_usb_init(ifp); XXX needs a process context! */
247313236e8dSdamien 			ifp->if_oerrors++;
247413236e8dSdamien 			return;
247513236e8dSdamien 		}
247613236e8dSdamien 		ifp->if_timer = 1;
247713236e8dSdamien 	}
247813236e8dSdamien 	ieee80211_watchdog(ifp);
247913236e8dSdamien }
248013236e8dSdamien 
248113236e8dSdamien int
athn_usb_ioctl(struct ifnet * ifp,u_long cmd,caddr_t data)248213236e8dSdamien athn_usb_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
248313236e8dSdamien {
248413236e8dSdamien 	struct athn_softc *sc = ifp->if_softc;
2485dfa43ce4Sstsp 	struct athn_usb_softc *usc = (struct athn_usb_softc *)sc;
248613236e8dSdamien 	struct ieee80211com *ic = &sc->sc_ic;
248713236e8dSdamien 	int s, error = 0;
248813236e8dSdamien 
2489dfa43ce4Sstsp 	if (usbd_is_dying(usc->sc_udev))
2490dfa43ce4Sstsp 		return ENXIO;
2491dfa43ce4Sstsp 
2492dfa43ce4Sstsp 	usbd_ref_incr(usc->sc_udev);
2493dfa43ce4Sstsp 
249413236e8dSdamien 	s = splnet();
249513236e8dSdamien 
249613236e8dSdamien 	switch (cmd) {
249713236e8dSdamien 	case SIOCSIFADDR:
249813236e8dSdamien 		ifp->if_flags |= IFF_UP;
249913236e8dSdamien 		/* FALLTHROUGH */
250013236e8dSdamien 	case SIOCSIFFLAGS:
250113236e8dSdamien 		if (ifp->if_flags & IFF_UP) {
250213236e8dSdamien 			if (!(ifp->if_flags & IFF_RUNNING))
250313236e8dSdamien 				error = athn_usb_init(ifp);
250413236e8dSdamien 		} else {
250513236e8dSdamien 			if (ifp->if_flags & IFF_RUNNING)
250613236e8dSdamien 				athn_usb_stop(ifp);
250713236e8dSdamien 		}
250813236e8dSdamien 		break;
250913236e8dSdamien 	case SIOCS80211CHANNEL:
251013236e8dSdamien 		error = ieee80211_ioctl(ifp, cmd, data);
251113236e8dSdamien 		if (error == ENETRESET &&
251213236e8dSdamien 		    ic->ic_opmode == IEEE80211_M_MONITOR) {
251313236e8dSdamien 			if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
251413236e8dSdamien 			    (IFF_UP | IFF_RUNNING)) {
251513236e8dSdamien 				athn_usb_switch_chan(sc, ic->ic_ibss_chan,
251613236e8dSdamien 				    NULL);
251713236e8dSdamien 			}
251813236e8dSdamien 			error = 0;
251913236e8dSdamien 		}
252013236e8dSdamien 		break;
252113236e8dSdamien 	default:
252213236e8dSdamien 		error = ieee80211_ioctl(ifp, cmd, data);
252313236e8dSdamien 	}
252413236e8dSdamien 
252513236e8dSdamien 	if (error == ENETRESET) {
252613236e8dSdamien 		error = 0;
252713236e8dSdamien 		if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
252813236e8dSdamien 		    (IFF_UP | IFF_RUNNING)) {
252913236e8dSdamien 			athn_usb_stop(ifp);
253013236e8dSdamien 			error = athn_usb_init(ifp);
253113236e8dSdamien 		}
253213236e8dSdamien 	}
253313236e8dSdamien 	splx(s);
2534dfa43ce4Sstsp 
2535dfa43ce4Sstsp 	usbd_ref_decr(usc->sc_udev);
2536dfa43ce4Sstsp 
253713236e8dSdamien 	return (error);
253813236e8dSdamien }
253913236e8dSdamien 
254013236e8dSdamien int
athn_usb_init(struct ifnet * ifp)254113236e8dSdamien athn_usb_init(struct ifnet *ifp)
254213236e8dSdamien {
254313236e8dSdamien 	struct athn_softc *sc = ifp->if_softc;
254413236e8dSdamien 	struct athn_usb_softc *usc = (struct athn_usb_softc *)sc;
254513236e8dSdamien 	struct athn_ops *ops = &sc->ops;
254613236e8dSdamien 	struct ieee80211com *ic = &sc->sc_ic;
254713236e8dSdamien 	struct ieee80211_channel *c, *extc;
254813236e8dSdamien 	struct athn_usb_rx_data *data;
254913236e8dSdamien 	struct ar_htc_target_vif hvif;
255013236e8dSdamien 	struct ar_htc_target_sta sta;
255113236e8dSdamien 	struct ar_htc_cap_target hic;
255213236e8dSdamien 	uint16_t mode;
255313236e8dSdamien 	int i, error;
255413236e8dSdamien 
255513236e8dSdamien 	/* Init host async commands ring. */
255613236e8dSdamien 	usc->cmdq.cur = usc->cmdq.next = usc->cmdq.queued = 0;
255713236e8dSdamien 
255813236e8dSdamien 	/* Allocate Tx/Rx buffers. */
255913236e8dSdamien 	error = athn_usb_alloc_rx_list(usc);
256013236e8dSdamien 	if (error != 0)
256113236e8dSdamien 		goto fail;
256213236e8dSdamien 	error = athn_usb_alloc_tx_list(usc);
256313236e8dSdamien 	if (error != 0)
256413236e8dSdamien 		goto fail;
2565af7c78e3Sdamien 	/* Steal one buffer for beacons. */
2566af7c78e3Sdamien 	usc->tx_bcn = TAILQ_FIRST(&usc->tx_free_list);
2567af7c78e3Sdamien 	TAILQ_REMOVE(&usc->tx_free_list, usc->tx_bcn, next);
256813236e8dSdamien 
256913236e8dSdamien 	c = ic->ic_bss->ni_chan = ic->ic_ibss_chan;
257013236e8dSdamien 	extc = NULL;
257113236e8dSdamien 
257213236e8dSdamien 	/* In case a new MAC address has been configured. */
257313236e8dSdamien 	IEEE80211_ADDR_COPY(ic->ic_myaddr, LLADDR(ifp->if_sadl));
257413236e8dSdamien 
257513236e8dSdamien 	error = athn_set_power_awake(sc);
257613236e8dSdamien 	if (error != 0)
257713236e8dSdamien 		goto fail;
257813236e8dSdamien 
257913236e8dSdamien 	error = athn_usb_wmi_cmd(usc, AR_WMI_CMD_FLUSH_RECV);
258013236e8dSdamien 	if (error != 0)
258113236e8dSdamien 		goto fail;
258213236e8dSdamien 
258313236e8dSdamien 	error = athn_hw_reset(sc, c, extc, 1);
258413236e8dSdamien 	if (error != 0)
258513236e8dSdamien 		goto fail;
258613236e8dSdamien 
258713236e8dSdamien 	ops->set_txpower(sc, c, extc);
258813236e8dSdamien 
258913236e8dSdamien 	mode = htobe16(IEEE80211_IS_CHAN_2GHZ(c) ?
259013236e8dSdamien 	    AR_HTC_MODE_11NG : AR_HTC_MODE_11NA);
259113236e8dSdamien 	error = athn_usb_wmi_xcmd(usc, AR_WMI_CMD_SET_MODE,
259213236e8dSdamien 	    &mode, sizeof(mode), NULL);
259313236e8dSdamien 	if (error != 0)
259413236e8dSdamien 		goto fail;
259513236e8dSdamien 
259613236e8dSdamien 	error = athn_usb_wmi_cmd(usc, AR_WMI_CMD_ATH_INIT);
259713236e8dSdamien 	if (error != 0)
259813236e8dSdamien 		goto fail;
259913236e8dSdamien 
260003574ff2Sdamien 	error = athn_usb_wmi_cmd(usc, AR_WMI_CMD_START_RECV);
260103574ff2Sdamien 	if (error != 0)
260203574ff2Sdamien 		goto fail;
260303574ff2Sdamien 
260413236e8dSdamien 	athn_rx_start(sc);
260513236e8dSdamien 
260613236e8dSdamien 	/* Create main interface on target. */
260713236e8dSdamien 	memset(&hvif, 0, sizeof(hvif));
260813236e8dSdamien 	hvif.index = 0;
260913236e8dSdamien 	IEEE80211_ADDR_COPY(hvif.myaddr, ic->ic_myaddr);
261013236e8dSdamien 	switch (ic->ic_opmode) {
261113236e8dSdamien 	case IEEE80211_M_STA:
261213236e8dSdamien 		hvif.opmode = htobe32(AR_HTC_M_STA);
261313236e8dSdamien 		break;
261413236e8dSdamien 	case IEEE80211_M_MONITOR:
261513236e8dSdamien 		hvif.opmode = htobe32(AR_HTC_M_MONITOR);
261613236e8dSdamien 		break;
261713236e8dSdamien #ifndef IEEE80211_STA_ONLY
261813236e8dSdamien 	case IEEE80211_M_IBSS:
261913236e8dSdamien 		hvif.opmode = htobe32(AR_HTC_M_IBSS);
262013236e8dSdamien 		break;
262113236e8dSdamien 	case IEEE80211_M_AHDEMO:
262213236e8dSdamien 		hvif.opmode = htobe32(AR_HTC_M_AHDEMO);
262313236e8dSdamien 		break;
262413236e8dSdamien 	case IEEE80211_M_HOSTAP:
262513236e8dSdamien 		hvif.opmode = htobe32(AR_HTC_M_HOSTAP);
262613236e8dSdamien 		break;
262713236e8dSdamien #endif
262813236e8dSdamien 	}
262913236e8dSdamien 	hvif.rtsthreshold = htobe16(ic->ic_rtsthreshold);
263013236e8dSdamien 	DPRINTF(("creating VAP\n"));
263113236e8dSdamien 	error = athn_usb_wmi_xcmd(usc, AR_WMI_CMD_VAP_CREATE,
263213236e8dSdamien 	    &hvif, sizeof(hvif), NULL);
263313236e8dSdamien 	if (error != 0)
263413236e8dSdamien 		goto fail;
263513236e8dSdamien 
263613236e8dSdamien 	/* Create a fake node to send management frames before assoc. */
263713236e8dSdamien 	memset(&sta, 0, sizeof(sta));
263813236e8dSdamien 	IEEE80211_ADDR_COPY(sta.macaddr, ic->ic_myaddr);
263913236e8dSdamien 	sta.sta_index = 0;
264013236e8dSdamien 	sta.is_vif_sta = 1;
264113236e8dSdamien 	sta.vif_index = hvif.index;
264213236e8dSdamien 	sta.maxampdu = 0xffff;
264313236e8dSdamien 	DPRINTF(("creating default node\n"));
264413236e8dSdamien 	error = athn_usb_wmi_xcmd(usc, AR_WMI_CMD_NODE_CREATE,
264513236e8dSdamien 	    &sta, sizeof(sta), NULL);
264613236e8dSdamien 	if (error != 0)
264713236e8dSdamien 		goto fail;
2648f47be805Sstsp 	usc->free_node_slots = ~(1 << sta.sta_index);
264913236e8dSdamien 
265013236e8dSdamien 	/* Update target capabilities. */
265113236e8dSdamien 	memset(&hic, 0, sizeof(hic));
265213236e8dSdamien 	hic.ampdu_limit = htobe32(0x0000ffff);
265313236e8dSdamien 	hic.ampdu_subframes = 20;
2654f47be805Sstsp 	hic.txchainmask = sc->txchainmask;
265513236e8dSdamien 	DPRINTF(("updating target configuration\n"));
265613236e8dSdamien 	error = athn_usb_wmi_xcmd(usc, AR_WMI_CMD_TARGET_IC_UPDATE,
265713236e8dSdamien 	    &hic, sizeof(hic), NULL);
265813236e8dSdamien 	if (error != 0)
265913236e8dSdamien 		goto fail;
266013236e8dSdamien 
266113236e8dSdamien 	/* Queue Rx xfers. */
266213236e8dSdamien 	for (i = 0; i < ATHN_USB_RX_LIST_COUNT; i++) {
266313236e8dSdamien 		data = &usc->rx_data[i];
266413236e8dSdamien 
266513236e8dSdamien 		usbd_setup_xfer(data->xfer, usc->rx_data_pipe, data, data->buf,
266613236e8dSdamien 		    ATHN_USB_RXBUFSZ, USBD_SHORT_XFER_OK | USBD_NO_COPY,
266713236e8dSdamien 		    USBD_NO_TIMEOUT, athn_usb_rxeof);
266813236e8dSdamien 		error = usbd_transfer(data->xfer);
266913236e8dSdamien 		if (error != 0 && error != USBD_IN_PROGRESS)
267013236e8dSdamien 			goto fail;
267113236e8dSdamien 	}
267213236e8dSdamien 	/* We're ready to go. */
267313236e8dSdamien 	ifp->if_flags |= IFF_RUNNING;
2674de6cd8fbSdlg 	ifq_clr_oactive(&ifp->if_snd);
267513236e8dSdamien 
267613236e8dSdamien #ifdef notyet
267713236e8dSdamien 	if (ic->ic_flags & IEEE80211_F_WEPON) {
267813236e8dSdamien 		/* Install WEP keys. */
267913236e8dSdamien 		for (i = 0; i < IEEE80211_WEP_NKID; i++)
268013236e8dSdamien 			athn_usb_set_key(ic, NULL, &ic->ic_nw_keys[i]);
268113236e8dSdamien 	}
268213236e8dSdamien #endif
268313236e8dSdamien 	if (ic->ic_opmode == IEEE80211_M_MONITOR)
268413236e8dSdamien 		ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
268513236e8dSdamien 	else
268613236e8dSdamien 		ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
268713236e8dSdamien 	athn_usb_wait_async(usc);
268813236e8dSdamien 	return (0);
268913236e8dSdamien  fail:
269013236e8dSdamien 	athn_usb_stop(ifp);
269113236e8dSdamien 	return (error);
269213236e8dSdamien }
269313236e8dSdamien 
269413236e8dSdamien void
athn_usb_stop(struct ifnet * ifp)269513236e8dSdamien athn_usb_stop(struct ifnet *ifp)
269613236e8dSdamien {
269713236e8dSdamien 	struct athn_softc *sc = ifp->if_softc;
269813236e8dSdamien 	struct athn_usb_softc *usc = (struct athn_usb_softc *)sc;
269913236e8dSdamien 	struct ieee80211com *ic = &sc->sc_ic;
270013236e8dSdamien 	struct ar_htc_target_vif hvif;
270113236e8dSdamien 	uint8_t sta_index;
270213236e8dSdamien 	int s;
270313236e8dSdamien 
270413236e8dSdamien 	sc->sc_tx_timer = 0;
270513236e8dSdamien 	ifp->if_timer = 0;
2706de6cd8fbSdlg 	ifp->if_flags &= ~IFF_RUNNING;
2707de6cd8fbSdlg 	ifq_clr_oactive(&ifp->if_snd);
270813236e8dSdamien 
270913236e8dSdamien 	s = splusb();
271013236e8dSdamien 	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
271113236e8dSdamien 
271213236e8dSdamien 	/* Wait for all async commands to complete. */
271313236e8dSdamien 	athn_usb_wait_async(usc);
271413236e8dSdamien 
271513236e8dSdamien 	timeout_del(&sc->scan_to);
271613236e8dSdamien 	timeout_del(&sc->calib_to);
271713236e8dSdamien 
2718f47be805Sstsp 	/* Remove all non-default nodes. */
2719f47be805Sstsp 	for (sta_index = 1; sta_index < AR_USB_MAX_STA; sta_index++) {
2720f47be805Sstsp 		if (usc->free_node_slots & (1 << sta_index))
2721f47be805Sstsp 			continue;
2722f47be805Sstsp 		(void)athn_usb_wmi_xcmd(usc, AR_WMI_CMD_NODE_REMOVE,
2723f47be805Sstsp 		    &sta_index, sizeof(sta_index), NULL);
2724f47be805Sstsp 	}
2725f47be805Sstsp 
2726f47be805Sstsp 	/* Remove main interface. This also invalidates our default node. */
272713236e8dSdamien 	memset(&hvif, 0, sizeof(hvif));
272813236e8dSdamien 	hvif.index = 0;
272913236e8dSdamien 	IEEE80211_ADDR_COPY(hvif.myaddr, ic->ic_myaddr);
273013236e8dSdamien 	(void)athn_usb_wmi_xcmd(usc, AR_WMI_CMD_VAP_REMOVE,
273113236e8dSdamien 	    &hvif, sizeof(hvif), NULL);
2732f47be805Sstsp 
2733f47be805Sstsp 	usc->free_node_slots = 0xff;
273413236e8dSdamien 
273513236e8dSdamien 	(void)athn_usb_wmi_cmd(usc, AR_WMI_CMD_DISABLE_INTR);
273613236e8dSdamien 	(void)athn_usb_wmi_cmd(usc, AR_WMI_CMD_DRAIN_TXQ_ALL);
273713236e8dSdamien 	(void)athn_usb_wmi_cmd(usc, AR_WMI_CMD_STOP_RECV);
273813236e8dSdamien 
273913236e8dSdamien 	athn_reset(sc, 0);
274013236e8dSdamien 	athn_init_pll(sc, NULL);
274113236e8dSdamien 	athn_set_power_awake(sc);
274213236e8dSdamien 	athn_reset(sc, 1);
274313236e8dSdamien 	athn_init_pll(sc, NULL);
274413236e8dSdamien 	athn_set_power_sleep(sc);
274513236e8dSdamien 
274613236e8dSdamien 	/* Abort Tx/Rx. */
274713236e8dSdamien 	usbd_abort_pipe(usc->tx_data_pipe);
274813236e8dSdamien 	usbd_abort_pipe(usc->rx_data_pipe);
274913236e8dSdamien 
275013236e8dSdamien 	/* Free Tx/Rx buffers. */
275113236e8dSdamien 	athn_usb_free_tx_list(usc);
275213236e8dSdamien 	athn_usb_free_rx_list(usc);
275313236e8dSdamien 	splx(s);
275413236e8dSdamien 
275513236e8dSdamien 	/* Flush Rx stream. */
275613236e8dSdamien 	m_freem(usc->rx_stream.m);
275703574ff2Sdamien 	usc->rx_stream.m = NULL;
275813236e8dSdamien 	usc->rx_stream.left = 0;
275913236e8dSdamien }
2760