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