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