1*c33449aaSjakemsr /* $OpenBSD: if_otus.c,v 1.19 2010/10/23 15:42:09 jakemsr Exp $ */ 268baf11cSdamien 368baf11cSdamien /*- 468baf11cSdamien * Copyright (c) 2009 Damien Bergamini <damien.bergamini@free.fr> 568baf11cSdamien * 668baf11cSdamien * Permission to use, copy, modify, and distribute this software for any 768baf11cSdamien * purpose with or without fee is hereby granted, provided that the above 868baf11cSdamien * copyright notice and this permission notice appear in all copies. 968baf11cSdamien * 1068baf11cSdamien * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1168baf11cSdamien * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1268baf11cSdamien * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1368baf11cSdamien * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1468baf11cSdamien * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1568baf11cSdamien * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1668baf11cSdamien * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1768baf11cSdamien */ 1868baf11cSdamien 1968baf11cSdamien /*- 2068baf11cSdamien * Driver for Atheros AR9001U chipset. 2168baf11cSdamien * http://www.atheros.com/pt/bulletins/AR9001USBBulletin.pdf 2268baf11cSdamien */ 2368baf11cSdamien 2468baf11cSdamien #include "bpfilter.h" 2568baf11cSdamien 2668baf11cSdamien #include <sys/param.h> 2768baf11cSdamien #include <sys/sockio.h> 2868baf11cSdamien #include <sys/mbuf.h> 2968baf11cSdamien #include <sys/kernel.h> 3068baf11cSdamien #include <sys/socket.h> 3168baf11cSdamien #include <sys/systm.h> 3268baf11cSdamien #include <sys/timeout.h> 3368baf11cSdamien #include <sys/conf.h> 3468baf11cSdamien #include <sys/device.h> 3568baf11cSdamien 3668baf11cSdamien #include <machine/bus.h> 3768baf11cSdamien #include <machine/endian.h> 3868baf11cSdamien #include <machine/intr.h> 3968baf11cSdamien 4068baf11cSdamien #if NBPFILTER > 0 4168baf11cSdamien #include <net/bpf.h> 4268baf11cSdamien #endif 4368baf11cSdamien #include <net/if.h> 4468baf11cSdamien #include <net/if_arp.h> 4568baf11cSdamien #include <net/if_dl.h> 4668baf11cSdamien #include <net/if_media.h> 4768baf11cSdamien #include <net/if_types.h> 4868baf11cSdamien 4968baf11cSdamien #include <netinet/in.h> 5068baf11cSdamien #include <netinet/in_systm.h> 5168baf11cSdamien #include <netinet/in_var.h> 5268baf11cSdamien #include <netinet/if_ether.h> 5368baf11cSdamien #include <netinet/ip.h> 5468baf11cSdamien 5568baf11cSdamien #include <net80211/ieee80211_var.h> 5668baf11cSdamien #include <net80211/ieee80211_amrr.h> 5768baf11cSdamien #include <net80211/ieee80211_radiotap.h> 5868baf11cSdamien 5968baf11cSdamien #include <dev/usb/usb.h> 6068baf11cSdamien #include <dev/usb/usbdi.h> 6168baf11cSdamien #include <dev/usb/usbdi_util.h> 6268baf11cSdamien #include <dev/usb/usbdevs.h> 6368baf11cSdamien 6468baf11cSdamien #include <dev/usb/if_otusreg.h> 6568baf11cSdamien 6668baf11cSdamien #ifdef USB_DEBUG 6768baf11cSdamien #define OTUS_DEBUG 6868baf11cSdamien #endif 6968baf11cSdamien 7068baf11cSdamien #ifdef OTUS_DEBUG 7168baf11cSdamien #define DPRINTF(x) do { if (otus_debug) printf x; } while (0) 7268baf11cSdamien #define DPRINTFN(n, x) do { if (otus_debug >= (n)) printf x; } while (0) 7368baf11cSdamien int otus_debug = 1; 7468baf11cSdamien #else 7568baf11cSdamien #define DPRINTF(x) 7668baf11cSdamien #define DPRINTFN(n, x) 7768baf11cSdamien #endif 7868baf11cSdamien 7968baf11cSdamien static const struct usb_devno otus_devs[] = { 8068baf11cSdamien { USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_WN7512 }, 8108c50b53Sdamien { USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_3CRUSBN275 }, 8268baf11cSdamien { USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_TG121N }, 8368baf11cSdamien { USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_AR9170 }, 8408c50b53Sdamien { USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_WN612 }, 85a145d460Sdamien { USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_WN821NV2 }, 86bd3ca424Sdamien { USB_VENDOR_AVM, USB_PRODUCT_AVM_FRITZWLAN }, 8706c5b759Sdamien { USB_VENDOR_CACE, USB_PRODUCT_CACE_AIRPCAPNX }, 889ca817faSjsg { USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWA130D1 }, 8943333fa6Sdamien { USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWA160A1 }, 9043333fa6Sdamien { USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWA160A2 }, 9168baf11cSdamien { USB_VENDOR_IODATA, USB_PRODUCT_IODATA_WNGDNUS2 }, 92a5c5b7a3Sdamien { USB_VENDOR_NEC, USB_PRODUCT_NEC_WL300NUG }, 9368baf11cSdamien { USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_WN111V2 }, 9408c50b53Sdamien { USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_WNA1000 }, 9568baf11cSdamien { USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_WNDA3100 }, 9668baf11cSdamien { USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GW_US300 }, 9708c50b53Sdamien { USB_VENDOR_WISTRONNEWEB, USB_PRODUCT_WISTRONNEWEB_O8494 }, 9808c50b53Sdamien { USB_VENDOR_WISTRONNEWEB, USB_PRODUCT_WISTRONNEWEB_WNC0600 }, 9968baf11cSdamien { USB_VENDOR_ZCOM, USB_PRODUCT_ZCOM_UB81 }, 10068baf11cSdamien { USB_VENDOR_ZCOM, USB_PRODUCT_ZCOM_UB82 }, 101bd3ca424Sdamien { USB_VENDOR_ZYDAS, USB_PRODUCT_ZYDAS_ZD1221 }, 102bd3ca424Sdamien { USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_NWD271N } 10368baf11cSdamien }; 10468baf11cSdamien 10568baf11cSdamien int otus_match(struct device *, void *, void *); 10668baf11cSdamien void otus_attach(struct device *, struct device *, void *); 10768baf11cSdamien int otus_detach(struct device *, int); 10868baf11cSdamien void otus_attachhook(void *); 10968baf11cSdamien void otus_get_chanlist(struct otus_softc *); 110d7d7f774Sdamien int otus_load_firmware(struct otus_softc *, const char *, 111d7d7f774Sdamien uint32_t); 112d7d7f774Sdamien int otus_open_pipes(struct otus_softc *); 113d7d7f774Sdamien void otus_close_pipes(struct otus_softc *); 11468baf11cSdamien int otus_alloc_tx_cmd(struct otus_softc *); 11568baf11cSdamien void otus_free_tx_cmd(struct otus_softc *); 11668baf11cSdamien int otus_alloc_tx_data_list(struct otus_softc *); 11768baf11cSdamien void otus_free_tx_data_list(struct otus_softc *); 11868baf11cSdamien int otus_alloc_rx_data_list(struct otus_softc *); 11968baf11cSdamien void otus_free_rx_data_list(struct otus_softc *); 12068baf11cSdamien void otus_next_scan(void *); 12168baf11cSdamien void otus_task(void *); 12268baf11cSdamien void otus_do_async(struct otus_softc *, 12368baf11cSdamien void (*)(struct otus_softc *, void *), void *, int); 12468baf11cSdamien int otus_newstate(struct ieee80211com *, enum ieee80211_state, 12568baf11cSdamien int); 12668baf11cSdamien void otus_newstate_cb(struct otus_softc *, void *); 127d7d7f774Sdamien int otus_cmd(struct otus_softc *, uint8_t, const void *, int, 128d7d7f774Sdamien void *); 129d7d7f774Sdamien void otus_write(struct otus_softc *, uint32_t, uint32_t); 130d7d7f774Sdamien int otus_write_barrier(struct otus_softc *); 131d7d7f774Sdamien struct ieee80211_node *otus_node_alloc(struct ieee80211com *); 132d7d7f774Sdamien int otus_media_change(struct ifnet *); 13368baf11cSdamien int otus_read_eeprom(struct otus_softc *); 13468baf11cSdamien void otus_newassoc(struct ieee80211com *, struct ieee80211_node *, 13568baf11cSdamien int); 136d7d7f774Sdamien void otus_intr(usbd_xfer_handle, usbd_private_handle, usbd_status); 13768baf11cSdamien void otus_cmd_rxeof(struct otus_softc *, uint8_t *, int); 13868baf11cSdamien void otus_sub_rxeof(struct otus_softc *, uint8_t *, int); 13968baf11cSdamien void otus_rxeof(usbd_xfer_handle, usbd_private_handle, usbd_status); 14068baf11cSdamien void otus_txeof(usbd_xfer_handle, usbd_private_handle, usbd_status); 14168baf11cSdamien int otus_tx(struct otus_softc *, struct mbuf *, 14268baf11cSdamien struct ieee80211_node *); 14368baf11cSdamien void otus_start(struct ifnet *); 14468baf11cSdamien void otus_watchdog(struct ifnet *); 14568baf11cSdamien int otus_ioctl(struct ifnet *, u_long, caddr_t); 14668baf11cSdamien int otus_set_multi(struct otus_softc *); 14768baf11cSdamien void otus_updateedca(struct ieee80211com *); 14868baf11cSdamien void otus_updateedca_cb(struct otus_softc *, void *); 14968baf11cSdamien void otus_updateslot(struct ieee80211com *); 15068baf11cSdamien void otus_updateslot_cb(struct otus_softc *, void *); 15168baf11cSdamien int otus_init_mac(struct otus_softc *); 15268baf11cSdamien uint32_t otus_phy_get_def(struct otus_softc *, uint32_t); 15368baf11cSdamien int otus_set_board_values(struct otus_softc *, 15468baf11cSdamien struct ieee80211_channel *); 15568baf11cSdamien int otus_program_phy(struct otus_softc *, 15668baf11cSdamien struct ieee80211_channel *); 1576e30e88dSdamien int otus_set_rf_bank4(struct otus_softc *, 1586e30e88dSdamien struct ieee80211_channel *); 15968baf11cSdamien void otus_get_delta_slope(uint32_t, uint32_t *, uint32_t *); 1606e30e88dSdamien int otus_set_chan(struct otus_softc *, struct ieee80211_channel *, 1616e30e88dSdamien int); 16268baf11cSdamien int otus_set_key(struct ieee80211com *, struct ieee80211_node *, 16368baf11cSdamien struct ieee80211_key *); 16468baf11cSdamien void otus_set_key_cb(struct otus_softc *, void *); 16568baf11cSdamien void otus_delete_key(struct ieee80211com *, struct ieee80211_node *, 16668baf11cSdamien struct ieee80211_key *); 16768baf11cSdamien void otus_delete_key_cb(struct otus_softc *, void *); 16868baf11cSdamien void otus_calibrate_to(void *); 16968baf11cSdamien int otus_set_bssid(struct otus_softc *, const uint8_t *); 17068baf11cSdamien int otus_set_macaddr(struct otus_softc *, const uint8_t *); 17168baf11cSdamien void otus_led_newstate_type1(struct otus_softc *); 17268baf11cSdamien void otus_led_newstate_type2(struct otus_softc *); 17368baf11cSdamien void otus_led_newstate_type3(struct otus_softc *); 17468baf11cSdamien int otus_init(struct ifnet *); 17568baf11cSdamien void otus_stop(struct ifnet *); 17668baf11cSdamien 17768baf11cSdamien struct cfdriver otus_cd = { 17868baf11cSdamien NULL, "otus", DV_IFNET 17968baf11cSdamien }; 18068baf11cSdamien 18168baf11cSdamien const struct cfattach otus_ca = { 18268baf11cSdamien sizeof (struct otus_softc), otus_match, otus_attach, otus_detach 18368baf11cSdamien }; 18468baf11cSdamien 18568baf11cSdamien int 18668baf11cSdamien otus_match(struct device *parent, void *match, void *aux) 18768baf11cSdamien { 18868baf11cSdamien struct usb_attach_arg *uaa = aux; 18968baf11cSdamien 19068baf11cSdamien if (uaa->iface != NULL) 19168baf11cSdamien return UMATCH_NONE; 19268baf11cSdamien 19368baf11cSdamien return (usb_lookup(otus_devs, uaa->vendor, uaa->product) != NULL) ? 19468baf11cSdamien UMATCH_VENDOR_PRODUCT : UMATCH_NONE; 19568baf11cSdamien } 19668baf11cSdamien 19768baf11cSdamien void 19868baf11cSdamien otus_attach(struct device *parent, struct device *self, void *aux) 19968baf11cSdamien { 20068baf11cSdamien struct otus_softc *sc = (struct otus_softc *)self; 20168baf11cSdamien struct usb_attach_arg *uaa = aux; 20268baf11cSdamien int error; 20368baf11cSdamien 20468baf11cSdamien sc->sc_udev = uaa->device; 20568baf11cSdamien 206*c33449aaSjakemsr usb_init_task(&sc->sc_task, otus_task, sc, USB_TASK_TYPE_GENERIC); 20768baf11cSdamien timeout_set(&sc->scan_to, otus_next_scan, sc); 20868baf11cSdamien timeout_set(&sc->calib_to, otus_calibrate_to, sc); 20968baf11cSdamien 21068baf11cSdamien sc->amrr.amrr_min_success_threshold = 1; 21168baf11cSdamien sc->amrr.amrr_max_success_threshold = 10; 21268baf11cSdamien 21368baf11cSdamien if (usbd_set_config_no(sc->sc_udev, 1, 0) != 0) { 21468baf11cSdamien printf("%s: could not set configuration no\n", 21568baf11cSdamien sc->sc_dev.dv_xname); 21668baf11cSdamien return; 21768baf11cSdamien } 21868baf11cSdamien 21968baf11cSdamien /* Get the first interface handle. */ 22068baf11cSdamien error = usbd_device2interface_handle(sc->sc_udev, 0, &sc->sc_iface); 22168baf11cSdamien if (error != 0) { 22268baf11cSdamien printf("%s: could not get interface handle\n", 22368baf11cSdamien sc->sc_dev.dv_xname); 22468baf11cSdamien return; 22568baf11cSdamien } 22668baf11cSdamien 22768baf11cSdamien if ((error = otus_open_pipes(sc)) != 0) { 22868baf11cSdamien printf("%s: could not open pipes\n", sc->sc_dev.dv_xname); 22968baf11cSdamien return; 23068baf11cSdamien } 23168baf11cSdamien 23268baf11cSdamien if (rootvp == NULL) 23368baf11cSdamien mountroothook_establish(otus_attachhook, sc); 23468baf11cSdamien else 23568baf11cSdamien otus_attachhook(sc); 23668baf11cSdamien 23768baf11cSdamien usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, &sc->sc_dev); 23868baf11cSdamien } 23968baf11cSdamien 24068baf11cSdamien int 24168baf11cSdamien otus_detach(struct device *self, int flags) 24268baf11cSdamien { 24368baf11cSdamien struct otus_softc *sc = (struct otus_softc *)self; 24468baf11cSdamien struct ifnet *ifp = &sc->sc_ic.ic_if; 24568baf11cSdamien int s; 24668baf11cSdamien 24768baf11cSdamien s = splnet(); 24868baf11cSdamien 24968baf11cSdamien /* Wait for all queued asynchronous commands to complete. */ 25068baf11cSdamien while (sc->cmdq.queued > 0) 25168baf11cSdamien tsleep(&sc->cmdq, 0, "cmdq", 0); 25268baf11cSdamien 25368baf11cSdamien timeout_del(&sc->scan_to); 25468baf11cSdamien timeout_del(&sc->calib_to); 25568baf11cSdamien 25688185ad4Sdamien if (ifp->if_flags != 0) { /* if_attach() has been called. */ 25768baf11cSdamien ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); 25868baf11cSdamien ieee80211_ifdetach(ifp); 25968baf11cSdamien if_detach(ifp); 26068baf11cSdamien } 26168baf11cSdamien 26268baf11cSdamien otus_close_pipes(sc); 26368baf11cSdamien 26468baf11cSdamien splx(s); 26568baf11cSdamien 26668baf11cSdamien usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, &sc->sc_dev); 26768baf11cSdamien 26868baf11cSdamien return 0; 26968baf11cSdamien } 27068baf11cSdamien 27168baf11cSdamien void 27268baf11cSdamien otus_attachhook(void *xsc) 27368baf11cSdamien { 27468baf11cSdamien struct otus_softc *sc = xsc; 27568baf11cSdamien struct ieee80211com *ic = &sc->sc_ic; 27668baf11cSdamien struct ifnet *ifp = &ic->ic_if; 27768baf11cSdamien usb_device_request_t req; 27868baf11cSdamien uint32_t in, out; 27968baf11cSdamien int error; 28068baf11cSdamien 28168baf11cSdamien error = otus_load_firmware(sc, "otus-init", AR_FW_INIT_ADDR); 28268baf11cSdamien if (error != 0) { 28368baf11cSdamien printf("%s: could not load %s firmware\n", 28468baf11cSdamien sc->sc_dev.dv_xname, "init"); 28568baf11cSdamien return; 28668baf11cSdamien } 28768baf11cSdamien 28868baf11cSdamien usbd_delay_ms(sc->sc_udev, 1000); 28968baf11cSdamien 29068baf11cSdamien error = otus_load_firmware(sc, "otus-main", AR_FW_MAIN_ADDR); 29168baf11cSdamien if (error != 0) { 29268baf11cSdamien printf("%s: could not load %s firmware\n", 29368baf11cSdamien sc->sc_dev.dv_xname, "main"); 29468baf11cSdamien return; 29568baf11cSdamien } 29668baf11cSdamien 29768baf11cSdamien /* Tell device that firmware transfer is complete. */ 29868baf11cSdamien req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 29968baf11cSdamien req.bRequest = AR_FW_DOWNLOAD_COMPLETE; 30068baf11cSdamien USETW(req.wValue, 0); 30168baf11cSdamien USETW(req.wIndex, 0); 30268baf11cSdamien USETW(req.wLength, 0); 30368baf11cSdamien if (usbd_do_request(sc->sc_udev, &req, NULL) != 0) { 30468baf11cSdamien printf("%s: firmware initialization failed\n", 30568baf11cSdamien sc->sc_dev.dv_xname); 30668baf11cSdamien return; 30768baf11cSdamien } 30868baf11cSdamien 30968baf11cSdamien /* Send an ECHO command to check that everything is settled. */ 31068baf11cSdamien in = 0xbadc0ffe; 31168baf11cSdamien if (otus_cmd(sc, AR_CMD_ECHO, &in, sizeof in, &out) != 0) { 31268baf11cSdamien printf("%s: echo command failed\n", sc->sc_dev.dv_xname); 31368baf11cSdamien return; 31468baf11cSdamien } 31568baf11cSdamien if (in != out) { 316d7d7f774Sdamien printf("%s: echo reply mismatch: 0x%08x!=0x%08x\n", 317d7d7f774Sdamien sc->sc_dev.dv_xname, in, out); 31868baf11cSdamien return; 31968baf11cSdamien } 32068baf11cSdamien 32168baf11cSdamien /* Read entire EEPROM. */ 32268baf11cSdamien if (otus_read_eeprom(sc) != 0) { 32368baf11cSdamien printf("%s: could not read EEPROM\n", sc->sc_dev.dv_xname); 32468baf11cSdamien return; 32568baf11cSdamien } 32668baf11cSdamien 327803f2018Sdamien sc->txmask = sc->eeprom.baseEepHeader.txMask; 328803f2018Sdamien sc->rxmask = sc->eeprom.baseEepHeader.rxMask; 3296e30e88dSdamien sc->capflags = sc->eeprom.baseEepHeader.opCapFlags; 33068baf11cSdamien IEEE80211_ADDR_COPY(ic->ic_myaddr, sc->eeprom.baseEepHeader.macAddr); 331803f2018Sdamien sc->sc_led_newstate = otus_led_newstate_type3; /* XXX */ 33268baf11cSdamien 3336e30e88dSdamien printf("%s: MAC/BBP AR9170, RF AR%X, MIMO %dT%dR, address %s\n", 3346e30e88dSdamien sc->sc_dev.dv_xname, (sc->capflags & AR5416_OPFLAGS_11A) ? 3356e30e88dSdamien 0x9104 : ((sc->txmask == 0x5) ? 0x9102 : 0x9101), 3366e30e88dSdamien (sc->txmask == 0x5) ? 2 : 1, (sc->rxmask == 0x5) ? 2 : 1, 33768baf11cSdamien ether_sprintf(ic->ic_myaddr)); 33868baf11cSdamien 33968baf11cSdamien ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ 34068baf11cSdamien ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ 34168baf11cSdamien ic->ic_state = IEEE80211_S_INIT; 34268baf11cSdamien 34368baf11cSdamien /* Set device capabilities. */ 34468baf11cSdamien ic->ic_caps = 34568baf11cSdamien IEEE80211_C_MONITOR | /* monitor mode supported */ 34668baf11cSdamien IEEE80211_C_SHPREAMBLE | /* short preamble supported */ 34768baf11cSdamien IEEE80211_C_SHSLOT | /* short slot time supported */ 34868baf11cSdamien IEEE80211_C_WEP | /* WEP */ 34968baf11cSdamien IEEE80211_C_RSN; /* WPA/RSN */ 35068baf11cSdamien 3514351e075Sdamien if (sc->eeprom.baseEepHeader.opCapFlags & AR5416_OPFLAGS_11G) { 35268baf11cSdamien /* Set supported .11b and .11g rates. */ 3534351e075Sdamien ic->ic_sup_rates[IEEE80211_MODE_11B] = 3544351e075Sdamien ieee80211_std_rateset_11b; 3554351e075Sdamien ic->ic_sup_rates[IEEE80211_MODE_11G] = 3564351e075Sdamien ieee80211_std_rateset_11g; 3574351e075Sdamien } 3584351e075Sdamien if (sc->eeprom.baseEepHeader.opCapFlags & AR5416_OPFLAGS_11A) { 3594351e075Sdamien /* Set supported .11a rates. */ 3604351e075Sdamien ic->ic_sup_rates[IEEE80211_MODE_11A] = 3614351e075Sdamien ieee80211_std_rateset_11a; 3624351e075Sdamien } 36368baf11cSdamien 36468baf11cSdamien /* Build the list of supported channels. */ 36568baf11cSdamien otus_get_chanlist(sc); 36668baf11cSdamien 36768baf11cSdamien ifp->if_softc = sc; 36868baf11cSdamien ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 36968baf11cSdamien ifp->if_ioctl = otus_ioctl; 37068baf11cSdamien ifp->if_start = otus_start; 37168baf11cSdamien ifp->if_watchdog = otus_watchdog; 37268baf11cSdamien IFQ_SET_READY(&ifp->if_snd); 37368baf11cSdamien memcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ); 37468baf11cSdamien 37568baf11cSdamien if_attach(ifp); 37668baf11cSdamien ieee80211_ifattach(ifp); 37768baf11cSdamien ic->ic_node_alloc = otus_node_alloc; 37868baf11cSdamien ic->ic_newassoc = otus_newassoc; 37968baf11cSdamien ic->ic_updateslot = otus_updateslot; 38068baf11cSdamien ic->ic_updateedca = otus_updateedca; 38168baf11cSdamien #ifdef notyet 38268baf11cSdamien ic->ic_set_key = otus_set_key; 38368baf11cSdamien ic->ic_delete_key = otus_delete_key; 38468baf11cSdamien #endif 38568baf11cSdamien /* Override state transition machine. */ 38668baf11cSdamien sc->sc_newstate = ic->ic_newstate; 38768baf11cSdamien ic->ic_newstate = otus_newstate; 38868baf11cSdamien ieee80211_media_init(ifp, otus_media_change, ieee80211_media_status); 38968baf11cSdamien 39068baf11cSdamien #if NBPFILTER > 0 39168baf11cSdamien bpfattach(&sc->sc_drvbpf, ifp, DLT_IEEE802_11_RADIO, 39268baf11cSdamien sizeof (struct ieee80211_frame) + IEEE80211_RADIOTAP_HDRLEN); 39368baf11cSdamien 39468baf11cSdamien sc->sc_rxtap_len = sizeof sc->sc_rxtapu; 39568baf11cSdamien sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len); 39668baf11cSdamien sc->sc_rxtap.wr_ihdr.it_present = htole32(OTUS_RX_RADIOTAP_PRESENT); 39768baf11cSdamien 39868baf11cSdamien sc->sc_txtap_len = sizeof sc->sc_txtapu; 39968baf11cSdamien sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len); 40068baf11cSdamien sc->sc_txtap.wt_ihdr.it_present = htole32(OTUS_TX_RADIOTAP_PRESENT); 40168baf11cSdamien #endif 40268baf11cSdamien } 40368baf11cSdamien 40468baf11cSdamien void 40568baf11cSdamien otus_get_chanlist(struct otus_softc *sc) 40668baf11cSdamien { 40768baf11cSdamien struct ieee80211com *ic = &sc->sc_ic; 40868baf11cSdamien uint16_t domain; 40968baf11cSdamien uint8_t chan; 41068baf11cSdamien int i; 41168baf11cSdamien 41268baf11cSdamien /* XXX regulatory domain. */ 41368baf11cSdamien domain = letoh16(sc->eeprom.baseEepHeader.regDmn[0]); 41468baf11cSdamien DPRINTF(("regdomain=0x%04x\n", domain)); 41568baf11cSdamien 41668baf11cSdamien if (sc->eeprom.baseEepHeader.opCapFlags & AR5416_OPFLAGS_11G) { 41768baf11cSdamien for (i = 0; i < 14; i++) { 41868baf11cSdamien chan = ar_chans[i]; 41968baf11cSdamien ic->ic_channels[chan].ic_freq = 42068baf11cSdamien ieee80211_ieee2mhz(chan, IEEE80211_CHAN_2GHZ); 42168baf11cSdamien ic->ic_channels[chan].ic_flags = 42268baf11cSdamien IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | 42368baf11cSdamien IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ; 42468baf11cSdamien } 42568baf11cSdamien } 42668baf11cSdamien if (sc->eeprom.baseEepHeader.opCapFlags & AR5416_OPFLAGS_11A) { 42768baf11cSdamien for (i = 14; i < nitems(ar_chans); i++) { 42868baf11cSdamien chan = ar_chans[i]; 42968baf11cSdamien ic->ic_channels[chan].ic_freq = 43068baf11cSdamien ieee80211_ieee2mhz(chan, IEEE80211_CHAN_5GHZ); 43168baf11cSdamien ic->ic_channels[chan].ic_flags = IEEE80211_CHAN_A; 43268baf11cSdamien } 43368baf11cSdamien } 43468baf11cSdamien } 43568baf11cSdamien 43668baf11cSdamien int 43768baf11cSdamien otus_load_firmware(struct otus_softc *sc, const char *name, uint32_t addr) 43868baf11cSdamien { 43968baf11cSdamien usb_device_request_t req; 44068baf11cSdamien size_t size; 44168baf11cSdamien u_char *fw, *ptr; 44268baf11cSdamien int mlen, error; 44368baf11cSdamien 44468baf11cSdamien /* Read firmware image from the filesystem. */ 44568baf11cSdamien if ((error = loadfirmware(name, &fw, &size)) != 0) { 44668baf11cSdamien printf("%s: failed loadfirmware of file %s (error %d)\n", 44768baf11cSdamien sc->sc_dev.dv_xname, name, error); 44868baf11cSdamien return error; 44968baf11cSdamien } 45068baf11cSdamien req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 45168baf11cSdamien req.bRequest = AR_FW_DOWNLOAD; 45268baf11cSdamien USETW(req.wIndex, 0); 45368baf11cSdamien 45468baf11cSdamien ptr = fw; 45568baf11cSdamien addr >>= 8; 45668baf11cSdamien while (size > 0) { 45768baf11cSdamien mlen = MIN(size, 4096); 45868baf11cSdamien 45968baf11cSdamien USETW(req.wValue, addr); 46068baf11cSdamien USETW(req.wLength, mlen); 46168baf11cSdamien if (usbd_do_request(sc->sc_udev, &req, ptr) != 0) { 46268baf11cSdamien error = EIO; 46368baf11cSdamien break; 46468baf11cSdamien } 46568baf11cSdamien addr += mlen >> 8; 46668baf11cSdamien ptr += mlen; 46768baf11cSdamien size -= mlen; 46868baf11cSdamien } 46968baf11cSdamien free(fw, M_DEVBUF); 47068baf11cSdamien return error; 47168baf11cSdamien } 47268baf11cSdamien 47368baf11cSdamien int 47468baf11cSdamien otus_open_pipes(struct otus_softc *sc) 47568baf11cSdamien { 47668baf11cSdamien usb_endpoint_descriptor_t *ed; 47768baf11cSdamien int i, isize, error; 47868baf11cSdamien 47968baf11cSdamien error = usbd_open_pipe(sc->sc_iface, AR_EPT_BULK_RX_NO, 0, 48068baf11cSdamien &sc->data_rx_pipe); 48168baf11cSdamien if (error != 0) { 48268baf11cSdamien printf("%s: could not open Rx bulk pipe\n", 48368baf11cSdamien sc->sc_dev.dv_xname); 48468baf11cSdamien goto fail; 48568baf11cSdamien } 48668baf11cSdamien 48768baf11cSdamien ed = usbd_get_endpoint_descriptor(sc->sc_iface, AR_EPT_INTR_RX_NO); 48868baf11cSdamien if (ed == NULL) { 48968baf11cSdamien printf("%s: could not retrieve Rx intr pipe descriptor\n", 49068baf11cSdamien sc->sc_dev.dv_xname); 49168baf11cSdamien goto fail; 49268baf11cSdamien } 49368baf11cSdamien isize = UGETW(ed->wMaxPacketSize); 49468baf11cSdamien if (isize == 0) { 49568baf11cSdamien printf("%s: invalid Rx intr pipe descriptor\n", 49668baf11cSdamien sc->sc_dev.dv_xname); 49768baf11cSdamien goto fail; 49868baf11cSdamien } 49968baf11cSdamien sc->ibuf = malloc(isize, M_USBDEV, M_NOWAIT); 50068baf11cSdamien if (sc->ibuf == NULL) { 50168baf11cSdamien printf("%s: could not allocate Rx intr buffer\n", 50268baf11cSdamien sc->sc_dev.dv_xname); 50368baf11cSdamien goto fail; 50468baf11cSdamien } 50568baf11cSdamien error = usbd_open_pipe_intr(sc->sc_iface, AR_EPT_INTR_RX_NO, 50668baf11cSdamien USBD_SHORT_XFER_OK, &sc->cmd_rx_pipe, sc, sc->ibuf, isize, 50768baf11cSdamien otus_intr, USBD_DEFAULT_INTERVAL); 50868baf11cSdamien if (error != 0) { 50968baf11cSdamien printf("%s: could not open Rx intr pipe\n", 51068baf11cSdamien sc->sc_dev.dv_xname); 51168baf11cSdamien goto fail; 51268baf11cSdamien } 51368baf11cSdamien 51468baf11cSdamien error = usbd_open_pipe(sc->sc_iface, AR_EPT_BULK_TX_NO, 0, 51568baf11cSdamien &sc->data_tx_pipe); 51668baf11cSdamien if (error != 0) { 51768baf11cSdamien printf("%s: could not open Tx bulk pipe\n", 51868baf11cSdamien sc->sc_dev.dv_xname); 51968baf11cSdamien goto fail; 52068baf11cSdamien } 52168baf11cSdamien 52268baf11cSdamien error = usbd_open_pipe(sc->sc_iface, AR_EPT_INTR_TX_NO, 0, 52368baf11cSdamien &sc->cmd_tx_pipe); 52468baf11cSdamien if (error != 0) { 52568baf11cSdamien printf("%s: could not open Tx intr pipe\n", 52668baf11cSdamien sc->sc_dev.dv_xname); 52768baf11cSdamien goto fail; 52868baf11cSdamien } 52968baf11cSdamien 53068baf11cSdamien if (otus_alloc_tx_cmd(sc) != 0) { 53168baf11cSdamien printf("%s: could not allocate command xfer\n", 53268baf11cSdamien sc->sc_dev.dv_xname); 53368baf11cSdamien goto fail; 53468baf11cSdamien } 53568baf11cSdamien 53668baf11cSdamien if (otus_alloc_tx_data_list(sc) != 0) { 53768baf11cSdamien printf("%s: could not allocate Tx xfers\n", 53868baf11cSdamien sc->sc_dev.dv_xname); 53968baf11cSdamien goto fail; 54068baf11cSdamien } 54168baf11cSdamien 54268baf11cSdamien if (otus_alloc_rx_data_list(sc) != 0) { 54368baf11cSdamien printf("%s: could not allocate Rx xfers\n", 54468baf11cSdamien sc->sc_dev.dv_xname); 54568baf11cSdamien goto fail; 54668baf11cSdamien } 54768baf11cSdamien 54868baf11cSdamien for (i = 0; i < OTUS_RX_DATA_LIST_COUNT; i++) { 54968baf11cSdamien struct otus_rx_data *data = &sc->rx_data[i]; 55068baf11cSdamien 55168baf11cSdamien usbd_setup_xfer(data->xfer, sc->data_rx_pipe, data, data->buf, 55268baf11cSdamien OTUS_RXBUFSZ, USBD_SHORT_XFER_OK | USBD_NO_COPY, 55368baf11cSdamien USBD_NO_TIMEOUT, otus_rxeof); 55468baf11cSdamien error = usbd_transfer(data->xfer); 55568baf11cSdamien if (error != USBD_IN_PROGRESS && error != 0) { 55668baf11cSdamien printf("%s: could not queue Rx xfer\n", 55768baf11cSdamien sc->sc_dev.dv_xname); 55868baf11cSdamien goto fail; 55968baf11cSdamien } 56068baf11cSdamien } 56168baf11cSdamien return 0; 56268baf11cSdamien 56368baf11cSdamien fail: otus_close_pipes(sc); 56468baf11cSdamien return error; 56568baf11cSdamien } 56668baf11cSdamien 56768baf11cSdamien void 56868baf11cSdamien otus_close_pipes(struct otus_softc *sc) 56968baf11cSdamien { 5704351e075Sdamien otus_free_tx_cmd(sc); 5714351e075Sdamien otus_free_tx_data_list(sc); 5724351e075Sdamien otus_free_rx_data_list(sc); 5734351e075Sdamien 57468baf11cSdamien if (sc->data_rx_pipe != NULL) 57568baf11cSdamien usbd_close_pipe(sc->data_rx_pipe); 5764351e075Sdamien if (sc->cmd_rx_pipe != NULL) { 5774351e075Sdamien usbd_abort_pipe(sc->cmd_rx_pipe); 57868baf11cSdamien usbd_close_pipe(sc->cmd_rx_pipe); 5794351e075Sdamien } 58068baf11cSdamien if (sc->ibuf != NULL) 58168baf11cSdamien free(sc->ibuf, M_USBDEV); 58268baf11cSdamien if (sc->data_tx_pipe != NULL) 58368baf11cSdamien usbd_close_pipe(sc->data_tx_pipe); 58468baf11cSdamien if (sc->cmd_tx_pipe != NULL) 58568baf11cSdamien usbd_close_pipe(sc->cmd_tx_pipe); 58668baf11cSdamien } 58768baf11cSdamien 58868baf11cSdamien int 58968baf11cSdamien otus_alloc_tx_cmd(struct otus_softc *sc) 59068baf11cSdamien { 59168baf11cSdamien struct otus_tx_cmd *cmd = &sc->tx_cmd; 59268baf11cSdamien 59368baf11cSdamien cmd->xfer = usbd_alloc_xfer(sc->sc_udev); 59468baf11cSdamien if (cmd->xfer == NULL) { 59568baf11cSdamien printf("%s: could not allocate xfer\n", 59668baf11cSdamien sc->sc_dev.dv_xname); 59768baf11cSdamien return ENOMEM; 59868baf11cSdamien } 59968baf11cSdamien cmd->buf = usbd_alloc_buffer(cmd->xfer, OTUS_MAX_TXCMDSZ); 60068baf11cSdamien if (cmd->buf == NULL) { 60168baf11cSdamien printf("%s: could not allocate xfer buffer\n", 60268baf11cSdamien sc->sc_dev.dv_xname); 60368baf11cSdamien usbd_free_xfer(cmd->xfer); 60468baf11cSdamien return ENOMEM; 60568baf11cSdamien } 60668baf11cSdamien return 0; 60768baf11cSdamien } 60868baf11cSdamien 60968baf11cSdamien void 61068baf11cSdamien otus_free_tx_cmd(struct otus_softc *sc) 61168baf11cSdamien { 6124351e075Sdamien /* Make sure no transfers are pending. */ 61368baf11cSdamien usbd_abort_pipe(sc->cmd_tx_pipe); 61468baf11cSdamien 61568baf11cSdamien if (sc->tx_cmd.xfer != NULL) 61668baf11cSdamien usbd_free_xfer(sc->tx_cmd.xfer); 61768baf11cSdamien } 61868baf11cSdamien 61968baf11cSdamien int 62068baf11cSdamien otus_alloc_tx_data_list(struct otus_softc *sc) 62168baf11cSdamien { 62268baf11cSdamien struct otus_tx_data *data; 62368baf11cSdamien int i, error; 62468baf11cSdamien 62568baf11cSdamien for (i = 0; i < OTUS_TX_DATA_LIST_COUNT; i++) { 62668baf11cSdamien data = &sc->tx_data[i]; 62768baf11cSdamien 62868baf11cSdamien data->sc = sc; /* Backpointer for callbacks. */ 62968baf11cSdamien 63068baf11cSdamien data->xfer = usbd_alloc_xfer(sc->sc_udev); 63168baf11cSdamien if (data->xfer == NULL) { 63268baf11cSdamien printf("%s: could not allocate xfer\n", 63368baf11cSdamien sc->sc_dev.dv_xname); 63468baf11cSdamien error = ENOMEM; 63568baf11cSdamien goto fail; 63668baf11cSdamien } 63768baf11cSdamien data->buf = usbd_alloc_buffer(data->xfer, OTUS_TXBUFSZ); 63868baf11cSdamien if (data->buf == NULL) { 63968baf11cSdamien printf("%s: could not allocate xfer buffer\n", 64068baf11cSdamien sc->sc_dev.dv_xname); 64168baf11cSdamien error = ENOMEM; 64268baf11cSdamien goto fail; 64368baf11cSdamien } 64468baf11cSdamien } 64568baf11cSdamien return 0; 64668baf11cSdamien 64768baf11cSdamien fail: otus_free_tx_data_list(sc); 64868baf11cSdamien return error; 64968baf11cSdamien } 65068baf11cSdamien 65168baf11cSdamien void 65268baf11cSdamien otus_free_tx_data_list(struct otus_softc *sc) 65368baf11cSdamien { 65468baf11cSdamien int i; 65568baf11cSdamien 6564351e075Sdamien /* Make sure no transfers are pending. */ 65768baf11cSdamien usbd_abort_pipe(sc->data_tx_pipe); 65868baf11cSdamien 65968baf11cSdamien for (i = 0; i < OTUS_TX_DATA_LIST_COUNT; i++) 66068baf11cSdamien if (sc->tx_data[i].xfer != NULL) 66168baf11cSdamien usbd_free_xfer(sc->tx_data[i].xfer); 66268baf11cSdamien } 66368baf11cSdamien 66468baf11cSdamien int 66568baf11cSdamien otus_alloc_rx_data_list(struct otus_softc *sc) 66668baf11cSdamien { 66768baf11cSdamien struct otus_rx_data *data; 66868baf11cSdamien int i, error; 66968baf11cSdamien 67068baf11cSdamien for (i = 0; i < OTUS_RX_DATA_LIST_COUNT; i++) { 67168baf11cSdamien data = &sc->rx_data[i]; 67268baf11cSdamien 67368baf11cSdamien data->sc = sc; /* Backpointer for callbacks. */ 67468baf11cSdamien 67568baf11cSdamien data->xfer = usbd_alloc_xfer(sc->sc_udev); 67668baf11cSdamien if (data->xfer == NULL) { 67768baf11cSdamien printf("%s: could not allocate xfer\n", 67868baf11cSdamien sc->sc_dev.dv_xname); 67968baf11cSdamien error = ENOMEM; 68068baf11cSdamien goto fail; 68168baf11cSdamien } 68268baf11cSdamien data->buf = usbd_alloc_buffer(data->xfer, OTUS_RXBUFSZ); 68368baf11cSdamien if (data->buf == NULL) { 68468baf11cSdamien printf("%s: could not allocate xfer buffer\n", 68568baf11cSdamien sc->sc_dev.dv_xname); 68668baf11cSdamien error = ENOMEM; 68768baf11cSdamien goto fail; 68868baf11cSdamien } 68968baf11cSdamien } 69068baf11cSdamien return 0; 69168baf11cSdamien 69268baf11cSdamien fail: otus_free_rx_data_list(sc); 69368baf11cSdamien return error; 69468baf11cSdamien } 69568baf11cSdamien 69668baf11cSdamien void 69768baf11cSdamien otus_free_rx_data_list(struct otus_softc *sc) 69868baf11cSdamien { 69968baf11cSdamien int i; 70068baf11cSdamien 70168baf11cSdamien /* Make sure no transfers are pending. */ 70268baf11cSdamien usbd_abort_pipe(sc->data_rx_pipe); 70368baf11cSdamien 70468baf11cSdamien for (i = 0; i < OTUS_RX_DATA_LIST_COUNT; i++) 70568baf11cSdamien if (sc->rx_data[i].xfer != NULL) 70668baf11cSdamien usbd_free_xfer(sc->rx_data[i].xfer); 70768baf11cSdamien } 70868baf11cSdamien 70968baf11cSdamien void 71068baf11cSdamien otus_next_scan(void *arg) 71168baf11cSdamien { 71268baf11cSdamien struct otus_softc *sc = arg; 71368baf11cSdamien 71468baf11cSdamien if (sc->sc_ic.ic_state == IEEE80211_S_SCAN) 71568baf11cSdamien ieee80211_next_scan(&sc->sc_ic.ic_if); 71668baf11cSdamien } 71768baf11cSdamien 71868baf11cSdamien void 71968baf11cSdamien otus_task(void *arg) 72068baf11cSdamien { 72168baf11cSdamien struct otus_softc *sc = arg; 72268baf11cSdamien struct otus_host_cmd_ring *ring = &sc->cmdq; 72368baf11cSdamien struct otus_host_cmd *cmd; 72468baf11cSdamien int s; 72568baf11cSdamien 72668baf11cSdamien /* Process host commands. */ 72768baf11cSdamien s = splusb(); 72868baf11cSdamien while (ring->next != ring->cur) { 72968baf11cSdamien cmd = &ring->cmd[ring->next]; 73068baf11cSdamien splx(s); 73168baf11cSdamien /* Callback. */ 73268baf11cSdamien cmd->cb(sc, cmd->data); 73368baf11cSdamien s = splusb(); 73468baf11cSdamien ring->queued--; 73568baf11cSdamien ring->next = (ring->next + 1) % OTUS_HOST_CMD_RING_COUNT; 73668baf11cSdamien } 73768baf11cSdamien wakeup(ring); 73868baf11cSdamien splx(s); 73968baf11cSdamien } 74068baf11cSdamien 74168baf11cSdamien void 74268baf11cSdamien otus_do_async(struct otus_softc *sc, void (*cb)(struct otus_softc *, void *), 74368baf11cSdamien void *arg, int len) 74468baf11cSdamien { 74568baf11cSdamien struct otus_host_cmd_ring *ring = &sc->cmdq; 74668baf11cSdamien struct otus_host_cmd *cmd; 74768baf11cSdamien int s; 74868baf11cSdamien 74968baf11cSdamien s = splusb(); 75068baf11cSdamien cmd = &ring->cmd[ring->cur]; 75168baf11cSdamien cmd->cb = cb; 75268baf11cSdamien KASSERT(len <= sizeof (cmd->data)); 75368baf11cSdamien memcpy(cmd->data, arg, len); 75468baf11cSdamien ring->cur = (ring->cur + 1) % OTUS_HOST_CMD_RING_COUNT; 75568baf11cSdamien 75668baf11cSdamien /* If there is no pending command already, schedule a task. */ 75768baf11cSdamien if (++ring->queued == 1) 75868baf11cSdamien usb_add_task(sc->sc_udev, &sc->sc_task); 75968baf11cSdamien splx(s); 76068baf11cSdamien } 76168baf11cSdamien 76268baf11cSdamien int 76368baf11cSdamien otus_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) 76468baf11cSdamien { 76568baf11cSdamien struct otus_softc *sc = ic->ic_softc; 76668baf11cSdamien struct otus_cmd_newstate cmd; 76768baf11cSdamien 76868baf11cSdamien /* Do it in a process context. */ 76968baf11cSdamien cmd.state = nstate; 77068baf11cSdamien cmd.arg = arg; 77168baf11cSdamien otus_do_async(sc, otus_newstate_cb, &cmd, sizeof cmd); 77268baf11cSdamien return 0; 77368baf11cSdamien } 77468baf11cSdamien 77568baf11cSdamien void 77668baf11cSdamien otus_newstate_cb(struct otus_softc *sc, void *arg) 77768baf11cSdamien { 77868baf11cSdamien struct otus_cmd_newstate *cmd = arg; 77968baf11cSdamien struct ieee80211com *ic = &sc->sc_ic; 78068baf11cSdamien struct ieee80211_node *ni; 78168baf11cSdamien int s; 78268baf11cSdamien 78368baf11cSdamien s = splnet(); 78468baf11cSdamien 78568baf11cSdamien switch (cmd->state) { 78668baf11cSdamien case IEEE80211_S_INIT: 78768baf11cSdamien break; 78868baf11cSdamien 78968baf11cSdamien case IEEE80211_S_SCAN: 7906e30e88dSdamien (void)otus_set_chan(sc, ic->ic_bss->ni_chan, 0); 79168baf11cSdamien timeout_add_msec(&sc->scan_to, 200); 79268baf11cSdamien break; 79368baf11cSdamien 79468baf11cSdamien case IEEE80211_S_AUTH: 79568baf11cSdamien case IEEE80211_S_ASSOC: 7966e30e88dSdamien (void)otus_set_chan(sc, ic->ic_bss->ni_chan, 0); 79768baf11cSdamien break; 79868baf11cSdamien 79968baf11cSdamien case IEEE80211_S_RUN: 8006e30e88dSdamien (void)otus_set_chan(sc, ic->ic_bss->ni_chan, 1); 80168baf11cSdamien 80268baf11cSdamien ni = ic->ic_bss; 80368baf11cSdamien 80468baf11cSdamien if (ic->ic_opmode == IEEE80211_M_STA) { 80568baf11cSdamien otus_updateslot(ic); 80668baf11cSdamien otus_set_bssid(sc, ni->ni_bssid); 80768baf11cSdamien 80868baf11cSdamien /* Fake a join to init the Tx rate. */ 80968baf11cSdamien otus_newassoc(ic, ni, 1); 81068baf11cSdamien 81168baf11cSdamien /* Start calibration timer. */ 81268baf11cSdamien timeout_add_sec(&sc->calib_to, 1); 81368baf11cSdamien } 81468baf11cSdamien break; 81568baf11cSdamien } 81668baf11cSdamien 81768baf11cSdamien sc->sc_led_newstate(sc); 81868baf11cSdamien (void)sc->sc_newstate(ic, cmd->state, cmd->arg); 81968baf11cSdamien 82068baf11cSdamien splx(s); 82168baf11cSdamien } 82268baf11cSdamien 82368baf11cSdamien int 82468baf11cSdamien otus_cmd(struct otus_softc *sc, uint8_t code, const void *idata, int ilen, 82568baf11cSdamien void *odata) 82668baf11cSdamien { 82768baf11cSdamien struct otus_tx_cmd *cmd = &sc->tx_cmd; 82868baf11cSdamien struct ar_cmd_hdr *hdr; 82968baf11cSdamien int s, xferlen, error; 83068baf11cSdamien 83168baf11cSdamien /* Always bulk-out a multiple of 4 bytes. */ 83268baf11cSdamien xferlen = (sizeof (*hdr) + ilen + 3) & ~3; 83368baf11cSdamien 83468baf11cSdamien hdr = (struct ar_cmd_hdr *)cmd->buf; 83568baf11cSdamien hdr->code = code; 83668baf11cSdamien hdr->len = ilen; 83768baf11cSdamien hdr->token = ++cmd->token; /* Don't care about endianness. */ 83868baf11cSdamien memcpy((uint8_t *)&hdr[1], idata, ilen); 83968baf11cSdamien 84068baf11cSdamien DPRINTFN(2, ("sending command code=0x%02x len=%d token=%d\n", 84168baf11cSdamien code, ilen, hdr->token)); 84268baf11cSdamien 84368baf11cSdamien s = splusb(); 84468baf11cSdamien cmd->odata = odata; 84568baf11cSdamien cmd->done = 0; 84668baf11cSdamien 84768baf11cSdamien usbd_setup_xfer(cmd->xfer, sc->cmd_tx_pipe, cmd, cmd->buf, xferlen, 84868baf11cSdamien USBD_FORCE_SHORT_XFER | USBD_NO_COPY, OTUS_CMD_TIMEOUT, NULL); 84968baf11cSdamien error = usbd_sync_transfer(cmd->xfer); 85068baf11cSdamien if (error != 0) { 85168baf11cSdamien splx(s); 85268baf11cSdamien printf("%s: could not send command 0x%x (error=%s)\n", 85368baf11cSdamien sc->sc_dev.dv_xname, code, usbd_errstr(error)); 85468baf11cSdamien return EIO; 85568baf11cSdamien } 85668baf11cSdamien if (!cmd->done) 85768baf11cSdamien error = tsleep(cmd, PCATCH, "otuscmd", hz); 85868baf11cSdamien cmd->odata = NULL; /* In case answer is received too late. */ 85968baf11cSdamien splx(s); 86068baf11cSdamien if (error != 0) { 86168baf11cSdamien printf("%s: timeout waiting for command 0x%02x reply\n", 86268baf11cSdamien sc->sc_dev.dv_xname, code); 86368baf11cSdamien } 86468baf11cSdamien return error; 86568baf11cSdamien } 86668baf11cSdamien 86768baf11cSdamien void 86868baf11cSdamien otus_write(struct otus_softc *sc, uint32_t reg, uint32_t val) 86968baf11cSdamien { 87068baf11cSdamien sc->write_buf[sc->write_idx].reg = htole32(reg); 87168baf11cSdamien sc->write_buf[sc->write_idx].val = htole32(val); 87268baf11cSdamien 87368baf11cSdamien if (++sc->write_idx > AR_MAX_WRITE_IDX) 87468baf11cSdamien (void)otus_write_barrier(sc); 87568baf11cSdamien } 87668baf11cSdamien 87768baf11cSdamien int 87868baf11cSdamien otus_write_barrier(struct otus_softc *sc) 87968baf11cSdamien { 88068baf11cSdamien int error; 88168baf11cSdamien 88268baf11cSdamien if (sc->write_idx == 0) 88368baf11cSdamien return 0; /* Nothing to flush. */ 88468baf11cSdamien 88568baf11cSdamien error = otus_cmd(sc, AR_CMD_WREG, sc->write_buf, 88668baf11cSdamien sizeof (sc->write_buf[0]) * sc->write_idx, NULL); 88768baf11cSdamien sc->write_idx = 0; 88868baf11cSdamien return error; 88968baf11cSdamien } 89068baf11cSdamien 89168baf11cSdamien struct ieee80211_node * 89268baf11cSdamien otus_node_alloc(struct ieee80211com *ic) 89368baf11cSdamien { 89468baf11cSdamien return malloc(sizeof (struct otus_node), M_DEVBUF, M_NOWAIT | M_ZERO); 89568baf11cSdamien } 89668baf11cSdamien 89768baf11cSdamien int 89868baf11cSdamien otus_media_change(struct ifnet *ifp) 89968baf11cSdamien { 90068baf11cSdamien struct otus_softc *sc = ifp->if_softc; 90168baf11cSdamien struct ieee80211com *ic = &sc->sc_ic; 90268baf11cSdamien uint8_t rate, ridx; 90368baf11cSdamien int error; 90468baf11cSdamien 90568baf11cSdamien error = ieee80211_media_change(ifp); 90668baf11cSdamien if (error != ENETRESET) 90768baf11cSdamien return error; 90868baf11cSdamien 90968baf11cSdamien if (ic->ic_fixed_rate != -1) { 91068baf11cSdamien rate = ic->ic_sup_rates[ic->ic_curmode]. 91168baf11cSdamien rs_rates[ic->ic_fixed_rate] & IEEE80211_RATE_VAL; 91268baf11cSdamien for (ridx = 0; ridx <= OTUS_RIDX_MAX; ridx++) 91368baf11cSdamien if (otus_rates[ridx].rate == rate) 91468baf11cSdamien break; 91568baf11cSdamien sc->fixed_ridx = ridx; 91668baf11cSdamien } 91768baf11cSdamien 91868baf11cSdamien if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING)) 91968baf11cSdamien error = otus_init(ifp); 92068baf11cSdamien 92168baf11cSdamien return error; 92268baf11cSdamien } 92368baf11cSdamien 92468baf11cSdamien int 92568baf11cSdamien otus_read_eeprom(struct otus_softc *sc) 92668baf11cSdamien { 92768baf11cSdamien uint32_t regs[8], reg; 92868baf11cSdamien uint8_t *eep; 92968baf11cSdamien int i, j, error; 93068baf11cSdamien 93168baf11cSdamien /* Read EEPROM by blocks of 32 bytes. */ 93268baf11cSdamien eep = (uint8_t *)&sc->eeprom; 933803f2018Sdamien reg = AR_EEPROM_OFFSET; 93468baf11cSdamien for (i = 0; i < sizeof (sc->eeprom) / 32; i++) { 93568baf11cSdamien for (j = 0; j < 8; j++, reg += 4) 93668baf11cSdamien regs[j] = htole32(reg); 93768baf11cSdamien error = otus_cmd(sc, AR_CMD_RREG, regs, sizeof regs, eep); 93868baf11cSdamien if (error != 0) 93968baf11cSdamien break; 94068baf11cSdamien eep += 32; 94168baf11cSdamien } 94268baf11cSdamien return error; 94368baf11cSdamien } 94468baf11cSdamien 94568baf11cSdamien void 94668baf11cSdamien otus_newassoc(struct ieee80211com *ic, struct ieee80211_node *ni, int isnew) 94768baf11cSdamien { 94868baf11cSdamien struct otus_softc *sc = ic->ic_softc; 94968baf11cSdamien struct otus_node *on = (void *)ni; 95068baf11cSdamien struct ieee80211_rateset *rs = &ni->ni_rates; 95168baf11cSdamien uint8_t rate; 95268baf11cSdamien int ridx, i; 95368baf11cSdamien 95468baf11cSdamien DPRINTF(("new assoc isnew=%d addr=%s\n", 95568baf11cSdamien isnew, ether_sprintf(ni->ni_macaddr))); 95668baf11cSdamien 95768baf11cSdamien ieee80211_amrr_node_init(&sc->amrr, &on->amn); 95868baf11cSdamien /* Start at lowest available bit-rate, AMRR will raise. */ 95968baf11cSdamien ni->ni_txrate = 0; 96068baf11cSdamien 96168baf11cSdamien for (i = 0; i < rs->rs_nrates; i++) { 96268baf11cSdamien rate = rs->rs_rates[i] & IEEE80211_RATE_VAL; 96368baf11cSdamien /* Convert 802.11 rate to hardware rate index. */ 96468baf11cSdamien for (ridx = 0; ridx <= OTUS_RIDX_MAX; ridx++) 96568baf11cSdamien if (otus_rates[ridx].rate == rate) 96668baf11cSdamien break; 96768baf11cSdamien on->ridx[i] = ridx; 96868baf11cSdamien DPRINTF(("rate=0x%02x ridx=%d\n", 96968baf11cSdamien rs->rs_rates[i], on->ridx[i])); 97068baf11cSdamien } 97168baf11cSdamien } 97268baf11cSdamien 973d7d7f774Sdamien /* ARGSUSED */ 974d7d7f774Sdamien void 975d7d7f774Sdamien otus_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) 976d7d7f774Sdamien { 977d7d7f774Sdamien #if 0 978d7d7f774Sdamien struct otus_softc *sc = priv; 979d7d7f774Sdamien int len; 980d7d7f774Sdamien 981d7d7f774Sdamien /* 982d7d7f774Sdamien * The Rx intr pipe is unused with current firmware. Notifications 983d7d7f774Sdamien * and replies to commands are sent through the Rx bulk pipe instead 984d7d7f774Sdamien * (with a magic PLCP header.) 985d7d7f774Sdamien */ 986d7d7f774Sdamien if (__predict_false(status != USBD_NORMAL_COMPLETION)) { 987d7d7f774Sdamien DPRINTF(("intr status=%d\n", status)); 988d7d7f774Sdamien if (status == USBD_STALLED) 989d7d7f774Sdamien usbd_clear_endpoint_stall_async(sc->cmd_rx_pipe); 990d7d7f774Sdamien return; 991d7d7f774Sdamien } 992d7d7f774Sdamien usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL); 993d7d7f774Sdamien 994d7d7f774Sdamien otus_cmd_rxeof(sc, sc->ibuf, len); 995d7d7f774Sdamien #endif 996d7d7f774Sdamien } 997d7d7f774Sdamien 99868baf11cSdamien void 99968baf11cSdamien otus_cmd_rxeof(struct otus_softc *sc, uint8_t *buf, int len) 100068baf11cSdamien { 100168baf11cSdamien struct ieee80211com *ic = &sc->sc_ic; 100268baf11cSdamien struct otus_tx_cmd *cmd; 100368baf11cSdamien struct ar_cmd_hdr *hdr; 100468baf11cSdamien int s; 100568baf11cSdamien 100668baf11cSdamien if (__predict_false(len < sizeof (*hdr))) { 100768baf11cSdamien DPRINTF(("cmd too small %d\n", len)); 100868baf11cSdamien return; 100968baf11cSdamien } 101068baf11cSdamien hdr = (struct ar_cmd_hdr *)buf; 101168baf11cSdamien if (__predict_false(sizeof (*hdr) + hdr->len > len || 101268baf11cSdamien sizeof (*hdr) + hdr->len > 64)) { 101368baf11cSdamien DPRINTF(("cmd too large %d\n", hdr->len)); 101468baf11cSdamien return; 101568baf11cSdamien } 101668baf11cSdamien 101768baf11cSdamien if ((hdr->code & 0xc0) != 0xc0) { 101868baf11cSdamien DPRINTFN(2, ("received reply code=0x%02x len=%d token=%d\n", 101968baf11cSdamien hdr->code, hdr->len, hdr->token)); 102068baf11cSdamien cmd = &sc->tx_cmd; 102168baf11cSdamien if (__predict_false(hdr->token != cmd->token)) 102268baf11cSdamien return; 102368baf11cSdamien /* Copy answer into caller's supplied buffer. */ 102468baf11cSdamien if (cmd->odata != NULL) 102568baf11cSdamien memcpy(cmd->odata, &hdr[1], hdr->len); 102668baf11cSdamien cmd->done = 1; 102768baf11cSdamien wakeup(cmd); 102868baf11cSdamien return; 102968baf11cSdamien } 103068baf11cSdamien 103168baf11cSdamien /* Received unsolicited notification. */ 103268baf11cSdamien DPRINTF(("received notification code=0x%02x len=%d\n", 103368baf11cSdamien hdr->code, hdr->len)); 103468baf11cSdamien switch (hdr->code & 0x3f) { 103568baf11cSdamien case AR_EVT_BEACON: 103668baf11cSdamien break; 103768baf11cSdamien case AR_EVT_TX_COMP: 103868baf11cSdamien { 103968baf11cSdamien struct ar_evt_tx_comp *tx = (struct ar_evt_tx_comp *)&hdr[1]; 104068baf11cSdamien struct ieee80211_node *ni; 104168baf11cSdamien struct otus_node *on; 104268baf11cSdamien 104368baf11cSdamien DPRINTF(("tx completed %s status=%d phy=0x%x\n", 104468baf11cSdamien ether_sprintf(tx->macaddr), letoh16(tx->status), 104568baf11cSdamien letoh32(tx->phy))); 104668baf11cSdamien s = splnet(); 104768baf11cSdamien #ifdef notyet 104868baf11cSdamien #ifndef IEEE80211_STA_ONLY 104968baf11cSdamien if (ic->ic_opmode != IEEE80211_M_STA) { 105068baf11cSdamien ni = ieee80211_find_node(ic, tx->macaddr); 105168baf11cSdamien if (__predict_false(ni == NULL)) { 105268baf11cSdamien splx(s); 105368baf11cSdamien break; 105468baf11cSdamien } 105568baf11cSdamien } else 105668baf11cSdamien #endif 105768baf11cSdamien #endif 105868baf11cSdamien ni = ic->ic_bss; 105968baf11cSdamien /* Update rate control statistics. */ 106068baf11cSdamien on = (void *)ni; 106168baf11cSdamien /* NB: we do not set the TX_MAC_RATE_PROBING flag. */ 106268baf11cSdamien if (__predict_true(tx->status != 0)) 106368baf11cSdamien on->amn.amn_retrycnt++; 106468baf11cSdamien splx(s); 106568baf11cSdamien break; 106668baf11cSdamien } 106768baf11cSdamien case AR_EVT_TBTT: 106868baf11cSdamien break; 106968baf11cSdamien } 107068baf11cSdamien } 107168baf11cSdamien 107268baf11cSdamien void 107368baf11cSdamien otus_sub_rxeof(struct otus_softc *sc, uint8_t *buf, int len) 107468baf11cSdamien { 107568baf11cSdamien struct ieee80211com *ic = &sc->sc_ic; 107668baf11cSdamien struct ifnet *ifp = &ic->ic_if; 107768baf11cSdamien struct ieee80211_rxinfo rxi; 107868baf11cSdamien struct ieee80211_node *ni; 107968baf11cSdamien struct ar_rx_tail *tail; 108068baf11cSdamien struct ieee80211_frame *wh; 108168baf11cSdamien struct mbuf *m; 108268baf11cSdamien uint8_t *plcp; 108368baf11cSdamien int s, mlen, align; 108468baf11cSdamien 108568baf11cSdamien if (__predict_false(len < AR_PLCP_HDR_LEN)) { 108668baf11cSdamien DPRINTF(("sub-xfer too short %d\n", len)); 108768baf11cSdamien return; 108868baf11cSdamien } 108988185ad4Sdamien plcp = buf; 109068baf11cSdamien 109168baf11cSdamien /* All bits in the PLCP header are set to 1 for non-MPDU. */ 109268baf11cSdamien if (memcmp(plcp, AR_PLCP_HDR_INTR, AR_PLCP_HDR_LEN) == 0) { 109368baf11cSdamien otus_cmd_rxeof(sc, plcp + AR_PLCP_HDR_LEN, 109468baf11cSdamien len - AR_PLCP_HDR_LEN); 109568baf11cSdamien return; 109668baf11cSdamien } 109768baf11cSdamien 109868baf11cSdamien /* Received MPDU. */ 109968baf11cSdamien if (__predict_false(len < AR_PLCP_HDR_LEN + sizeof (*tail))) { 110068baf11cSdamien DPRINTF(("MPDU too short %d\n", len)); 1101b86c5724Sdamien ifp->if_ierrors++; 110268baf11cSdamien return; 110368baf11cSdamien } 110468baf11cSdamien tail = (struct ar_rx_tail *)(plcp + len - sizeof (*tail)); 110568baf11cSdamien 110668baf11cSdamien /* Discard error frames. */ 110768baf11cSdamien if (__predict_false(tail->error != 0)) { 110868baf11cSdamien DPRINTF(("error frame 0x%02x\n", tail->error)); 11094351e075Sdamien if (tail->error & AR_RX_ERROR_FCS) { 11104351e075Sdamien DPRINTFN(3, ("bad FCS\n")); 11114351e075Sdamien } else if (tail->error & AR_RX_ERROR_MMIC) { 1112b86c5724Sdamien /* Report Michael MIC failures to net80211. */ 1113b86c5724Sdamien ic->ic_stats.is_rx_locmicfail++; 1114b86c5724Sdamien ieee80211_michael_mic_failure(ic, 0); 1115b86c5724Sdamien } 1116b86c5724Sdamien ifp->if_ierrors++; 111768baf11cSdamien return; 111868baf11cSdamien } 111968baf11cSdamien /* Compute MPDU's length. */ 112068baf11cSdamien mlen = len - AR_PLCP_HDR_LEN - sizeof (*tail); 112168baf11cSdamien /* Make sure there's room for an 802.11 header + FCS. */ 1122b86c5724Sdamien if (__predict_false(mlen < IEEE80211_MIN_LEN)) { 1123b86c5724Sdamien ifp->if_ierrors++; 112468baf11cSdamien return; 1125b86c5724Sdamien } 112668baf11cSdamien mlen -= IEEE80211_CRC_LEN; /* strip 802.11 FCS */ 112768baf11cSdamien 112868baf11cSdamien wh = (struct ieee80211_frame *)(plcp + AR_PLCP_HDR_LEN); 112968baf11cSdamien /* Provide a 32-bit aligned protocol header to the stack. */ 113068baf11cSdamien align = (ieee80211_has_qos(wh) ^ ieee80211_has_addr4(wh)) ? 2 : 0; 113168baf11cSdamien 113268baf11cSdamien MGETHDR(m, M_DONTWAIT, MT_DATA); 113368baf11cSdamien if (__predict_false(m == NULL)) { 113468baf11cSdamien ifp->if_ierrors++; 113568baf11cSdamien return; 113668baf11cSdamien } 113768baf11cSdamien if (align + mlen > MHLEN) { 113868baf11cSdamien MCLGET(m, M_DONTWAIT); 113968baf11cSdamien if (__predict_false(!(m->m_flags & M_EXT))) { 114068baf11cSdamien ifp->if_ierrors++; 114168baf11cSdamien m_freem(m); 114268baf11cSdamien return; 114368baf11cSdamien } 114468baf11cSdamien } 114568baf11cSdamien /* Finalize mbuf. */ 114668baf11cSdamien m->m_pkthdr.rcvif = ifp; 114768baf11cSdamien m->m_data += align; 114868baf11cSdamien memcpy(mtod(m, caddr_t), wh, mlen); 114968baf11cSdamien m->m_pkthdr.len = m->m_len = mlen; 115068baf11cSdamien 115168baf11cSdamien #if NBPFILTER > 0 115268baf11cSdamien if (__predict_false(sc->sc_drvbpf != NULL)) { 115368baf11cSdamien struct otus_rx_radiotap_header *tap = &sc->sc_rxtap; 115468baf11cSdamien struct mbuf mb; 115568baf11cSdamien 115668baf11cSdamien tap->wr_flags = 0; 115768baf11cSdamien tap->wr_chan_freq = htole16(ic->ic_ibss_chan->ic_freq); 115868baf11cSdamien tap->wr_chan_flags = htole16(ic->ic_ibss_chan->ic_flags); 115968baf11cSdamien tap->wr_antsignal = tail->rssi; 116068baf11cSdamien tap->wr_rate = 2; /* In case it can't be found below. */ 116168baf11cSdamien switch (tail->status & AR_RX_STATUS_MT_MASK) { 116268baf11cSdamien case AR_RX_STATUS_MT_CCK: 116368baf11cSdamien switch (plcp[0]) { 116488185ad4Sdamien case 10: tap->wr_rate = 2; break; 116588185ad4Sdamien case 20: tap->wr_rate = 4; break; 116688185ad4Sdamien case 55: tap->wr_rate = 11; break; 116788185ad4Sdamien case 110: tap->wr_rate = 22; break; 116868baf11cSdamien } 116968baf11cSdamien if (tail->status & AR_RX_STATUS_SHPREAMBLE) 117068baf11cSdamien tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; 117168baf11cSdamien break; 117268baf11cSdamien case AR_RX_STATUS_MT_OFDM: 117368baf11cSdamien switch (plcp[0] & 0xf) { 117468baf11cSdamien case 0xb: tap->wr_rate = 12; break; 117568baf11cSdamien case 0xf: tap->wr_rate = 18; break; 117668baf11cSdamien case 0xa: tap->wr_rate = 24; break; 117768baf11cSdamien case 0xe: tap->wr_rate = 36; break; 117868baf11cSdamien case 0x9: tap->wr_rate = 48; break; 117968baf11cSdamien case 0xd: tap->wr_rate = 72; break; 118068baf11cSdamien case 0x8: tap->wr_rate = 96; break; 118168baf11cSdamien case 0xc: tap->wr_rate = 108; break; 118268baf11cSdamien } 118368baf11cSdamien break; 118468baf11cSdamien } 118568baf11cSdamien mb.m_data = (caddr_t)tap; 118668baf11cSdamien mb.m_len = sc->sc_rxtap_len; 118768baf11cSdamien mb.m_next = m; 118868baf11cSdamien mb.m_nextpkt = NULL; 118968baf11cSdamien mb.m_type = 0; 119068baf11cSdamien mb.m_flags = 0; 119168baf11cSdamien bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_IN); 119268baf11cSdamien } 119368baf11cSdamien #endif 119468baf11cSdamien 119568baf11cSdamien s = splnet(); 119668baf11cSdamien ni = ieee80211_find_rxnode(ic, wh); 119768baf11cSdamien rxi.rxi_flags = 0; 119868baf11cSdamien rxi.rxi_rssi = tail->rssi; 119968baf11cSdamien rxi.rxi_tstamp = 0; /* unused */ 120068baf11cSdamien ieee80211_input(ifp, m, ni, &rxi); 120168baf11cSdamien 120268baf11cSdamien /* Node is no longer needed. */ 120368baf11cSdamien ieee80211_release_node(ic, ni); 120468baf11cSdamien splx(s); 120568baf11cSdamien } 120668baf11cSdamien 120768baf11cSdamien void 120868baf11cSdamien otus_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) 120968baf11cSdamien { 121068baf11cSdamien struct otus_rx_data *data = priv; 121168baf11cSdamien struct otus_softc *sc = data->sc; 121268baf11cSdamien caddr_t buf = data->buf; 121368baf11cSdamien struct ar_rx_head *head; 121468baf11cSdamien uint16_t hlen; 121568baf11cSdamien int len; 121668baf11cSdamien 121768baf11cSdamien if (__predict_false(status != USBD_NORMAL_COMPLETION)) { 121868baf11cSdamien DPRINTF(("RX status=%d\n", status)); 121968baf11cSdamien if (status == USBD_STALLED) 122068baf11cSdamien usbd_clear_endpoint_stall_async(sc->data_rx_pipe); 122168baf11cSdamien if (status != USBD_CANCELLED) 122268baf11cSdamien goto resubmit; 122368baf11cSdamien return; 122468baf11cSdamien } 122568baf11cSdamien usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL); 122668baf11cSdamien 122768baf11cSdamien while (len >= sizeof (*head)) { 122868baf11cSdamien head = (struct ar_rx_head *)buf; 122968baf11cSdamien if (__predict_false(head->tag != htole16(AR_RX_HEAD_TAG))) { 123068baf11cSdamien DPRINTF(("tag not valid 0x%x\n", letoh16(head->tag))); 123168baf11cSdamien break; 123268baf11cSdamien } 123368baf11cSdamien hlen = letoh16(head->len); 123468baf11cSdamien if (__predict_false(sizeof (*head) + hlen > len)) { 123568baf11cSdamien DPRINTF(("xfer too short %d/%d\n", len, hlen)); 123668baf11cSdamien break; 123768baf11cSdamien } 123868baf11cSdamien /* Process sub-xfer. */ 123968baf11cSdamien otus_sub_rxeof(sc, (uint8_t *)&head[1], hlen); 124068baf11cSdamien 124168baf11cSdamien /* Next sub-xfer is aligned on a 32-bit boundary. */ 124268baf11cSdamien hlen = (sizeof (*head) + hlen + 3) & ~3; 124368baf11cSdamien buf += hlen; 124468baf11cSdamien len -= hlen; 124568baf11cSdamien } 124668baf11cSdamien 124768baf11cSdamien resubmit: 124868baf11cSdamien usbd_setup_xfer(xfer, sc->data_rx_pipe, data, data->buf, OTUS_RXBUFSZ, 124968baf11cSdamien USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, otus_rxeof); 125068baf11cSdamien (void)usbd_transfer(data->xfer); 125168baf11cSdamien } 125268baf11cSdamien 125368baf11cSdamien void 125468baf11cSdamien otus_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) 125568baf11cSdamien { 125668baf11cSdamien struct otus_tx_data *data = priv; 125768baf11cSdamien struct otus_softc *sc = data->sc; 125868baf11cSdamien struct ieee80211com *ic = &sc->sc_ic; 125968baf11cSdamien struct ifnet *ifp = &ic->ic_if; 126068baf11cSdamien int s; 126168baf11cSdamien 126268baf11cSdamien if (__predict_false(status != USBD_NORMAL_COMPLETION)) { 126368baf11cSdamien DPRINTF(("TX status=%d\n", status)); 126468baf11cSdamien if (status == USBD_STALLED) 126568baf11cSdamien usbd_clear_endpoint_stall_async(sc->data_tx_pipe); 126668baf11cSdamien ifp->if_oerrors++; 126768baf11cSdamien return; 126868baf11cSdamien } 126968baf11cSdamien s = splnet(); 127068baf11cSdamien sc->tx_queued--; 127168baf11cSdamien sc->sc_tx_timer = 0; 127268baf11cSdamien ifp->if_flags &= ~IFF_OACTIVE; 127368baf11cSdamien otus_start(ifp); 127468baf11cSdamien splx(s); 127568baf11cSdamien } 127668baf11cSdamien 127768baf11cSdamien int 127868baf11cSdamien otus_tx(struct otus_softc *sc, struct mbuf *m, struct ieee80211_node *ni) 127968baf11cSdamien { 128068baf11cSdamien struct ieee80211com *ic = &sc->sc_ic; 128168baf11cSdamien struct otus_node *on = (void *)ni; 128268baf11cSdamien struct otus_tx_data *data; 128368baf11cSdamien struct ieee80211_frame *wh; 128468baf11cSdamien struct ieee80211_key *k; 128568baf11cSdamien struct ar_tx_head *head; 128668baf11cSdamien uint32_t phyctl; 128768baf11cSdamien uint16_t macctl, qos; 128888185ad4Sdamien uint8_t tid, qid; 128968baf11cSdamien int error, ridx, hasqos, xferlen; 129068baf11cSdamien 129168baf11cSdamien wh = mtod(m, struct ieee80211_frame *); 129268baf11cSdamien if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { 129368baf11cSdamien k = ieee80211_get_txkey(ic, wh, ni); 129468baf11cSdamien if ((m = ieee80211_encrypt(ic, m, k)) == NULL) 129568baf11cSdamien return ENOBUFS; 129668baf11cSdamien wh = mtod(m, struct ieee80211_frame *); 129768baf11cSdamien } 129868baf11cSdamien 129988185ad4Sdamien if ((hasqos = ieee80211_has_qos(wh))) { 130068baf11cSdamien qos = ieee80211_get_qos(wh); 130188185ad4Sdamien tid = qos & IEEE80211_QOS_TID; 130288185ad4Sdamien qid = ieee80211_up_to_ac(ic, tid); 130388185ad4Sdamien } else 130488185ad4Sdamien qid = EDCA_AC_BE; 130568baf11cSdamien 130668baf11cSdamien /* Pickup a rate index. */ 130768baf11cSdamien if (IEEE80211_IS_MULTICAST(wh->i_addr1) || 130868baf11cSdamien (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_DATA) 130968baf11cSdamien ridx = (ic->ic_curmode == IEEE80211_MODE_11A) ? 131068baf11cSdamien OTUS_RIDX_OFDM6 : OTUS_RIDX_CCK1; 131168baf11cSdamien else if (ic->ic_fixed_rate != -1) 131268baf11cSdamien ridx = sc->fixed_ridx; 131368baf11cSdamien else 131468baf11cSdamien ridx = on->ridx[ni->ni_txrate]; 131568baf11cSdamien 131668baf11cSdamien phyctl = 0; 13174351e075Sdamien macctl = AR_TX_MAC_BACKOFF | AR_TX_MAC_HW_DUR | AR_TX_MAC_QID(qid); 131868baf11cSdamien 131968baf11cSdamien if (IEEE80211_IS_MULTICAST(wh->i_addr1) || 132068baf11cSdamien (hasqos && ((qos & IEEE80211_QOS_ACK_POLICY_MASK) == 132168baf11cSdamien IEEE80211_QOS_ACK_POLICY_NOACK))) 132268baf11cSdamien macctl |= AR_TX_MAC_NOACK; 132368baf11cSdamien 1324d7d7f774Sdamien if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { 1325d7d7f774Sdamien if (m->m_pkthdr.len + IEEE80211_CRC_LEN >= ic->ic_rtsthreshold) 132668baf11cSdamien macctl |= AR_TX_MAC_RTS; 132768baf11cSdamien else if ((ic->ic_flags & IEEE80211_F_USEPROT) && 132868baf11cSdamien ridx >= OTUS_RIDX_OFDM6) { 132968baf11cSdamien if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) 133068baf11cSdamien macctl |= AR_TX_MAC_CTS; 133168baf11cSdamien else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) 133268baf11cSdamien macctl |= AR_TX_MAC_RTS; 133368baf11cSdamien } 1334d7d7f774Sdamien } 133568baf11cSdamien 133668baf11cSdamien phyctl |= AR_TX_PHY_MCS(otus_rates[ridx].mcs); 133768baf11cSdamien if (ridx >= OTUS_RIDX_OFDM6) { 133868baf11cSdamien phyctl |= AR_TX_PHY_MT_OFDM; 133968baf11cSdamien if (ridx <= OTUS_RIDX_OFDM24) 1340803f2018Sdamien phyctl |= AR_TX_PHY_ANTMSK(sc->txmask); 134168baf11cSdamien else 134268baf11cSdamien phyctl |= AR_TX_PHY_ANTMSK(1); 134368baf11cSdamien } else { /* CCK */ 134468baf11cSdamien phyctl |= AR_TX_PHY_MT_CCK; 1345803f2018Sdamien phyctl |= AR_TX_PHY_ANTMSK(sc->txmask); 134668baf11cSdamien } 134768baf11cSdamien 1348d7d7f774Sdamien /* Update rate control stats for frames that are ACK'ed. */ 134968baf11cSdamien if (!(macctl & AR_TX_MAC_NOACK)) 135068baf11cSdamien ((struct otus_node *)ni)->amn.amn_txcnt++; 135168baf11cSdamien 135268baf11cSdamien data = &sc->tx_data[sc->tx_cur]; 135368baf11cSdamien /* Fill Tx descriptor. */ 135468baf11cSdamien head = (struct ar_tx_head *)data->buf; 135568baf11cSdamien head->len = htole16(m->m_pkthdr.len + IEEE80211_CRC_LEN); 135668baf11cSdamien head->macctl = htole16(macctl); 135768baf11cSdamien head->phyctl = htole32(phyctl); 135868baf11cSdamien 135968baf11cSdamien #if NBPFILTER > 0 136068baf11cSdamien if (__predict_false(sc->sc_drvbpf != NULL)) { 136168baf11cSdamien struct otus_tx_radiotap_header *tap = &sc->sc_txtap; 136268baf11cSdamien struct mbuf mb; 136368baf11cSdamien 136468baf11cSdamien tap->wt_flags = 0; 136568baf11cSdamien tap->wt_rate = otus_rates[ridx].rate; 136668baf11cSdamien tap->wt_chan_freq = htole16(ic->ic_bss->ni_chan->ic_freq); 136768baf11cSdamien tap->wt_chan_flags = htole16(ic->ic_bss->ni_chan->ic_flags); 136868baf11cSdamien 136968baf11cSdamien mb.m_data = (caddr_t)tap; 137068baf11cSdamien mb.m_len = sc->sc_txtap_len; 137168baf11cSdamien mb.m_next = m; 137268baf11cSdamien mb.m_nextpkt = NULL; 137368baf11cSdamien mb.m_type = 0; 137468baf11cSdamien mb.m_flags = 0; 137568baf11cSdamien bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_OUT); 137668baf11cSdamien } 137768baf11cSdamien #endif 137868baf11cSdamien 137968baf11cSdamien xferlen = sizeof (*head) + m->m_pkthdr.len; 138068baf11cSdamien m_copydata(m, 0, m->m_pkthdr.len, (caddr_t)&head[1]); 138168baf11cSdamien m_freem(m); 138268baf11cSdamien ieee80211_release_node(ic, ni); 138368baf11cSdamien 138468baf11cSdamien DPRINTFN(5, ("tx queued=%d len=%d mac=0x%04x phy=0x%08x rate=%d\n", 138568baf11cSdamien sc->tx_queued, head->len, head->macctl, head->phyctl, 138668baf11cSdamien otus_rates[ridx].rate)); 138768baf11cSdamien usbd_setup_xfer(data->xfer, sc->data_tx_pipe, data, data->buf, xferlen, 138868baf11cSdamien USBD_FORCE_SHORT_XFER | USBD_NO_COPY, OTUS_TX_TIMEOUT, otus_txeof); 138968baf11cSdamien error = usbd_transfer(data->xfer); 139068baf11cSdamien if (__predict_false(error != USBD_IN_PROGRESS && error != 0)) 139168baf11cSdamien return error; 139268baf11cSdamien 139368baf11cSdamien sc->tx_queued++; 139468baf11cSdamien sc->tx_cur = (sc->tx_cur + 1) % OTUS_TX_DATA_LIST_COUNT; 139568baf11cSdamien 139668baf11cSdamien return 0; 139768baf11cSdamien } 139868baf11cSdamien 139968baf11cSdamien void 140068baf11cSdamien otus_start(struct ifnet *ifp) 140168baf11cSdamien { 140268baf11cSdamien struct otus_softc *sc = ifp->if_softc; 140368baf11cSdamien struct ieee80211com *ic = &sc->sc_ic; 140468baf11cSdamien struct ieee80211_node *ni; 140568baf11cSdamien struct mbuf *m; 140668baf11cSdamien 140768baf11cSdamien if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING) 140868baf11cSdamien return; 140968baf11cSdamien 141068baf11cSdamien for (;;) { 141168baf11cSdamien if (sc->tx_queued >= OTUS_TX_DATA_LIST_COUNT) { 141268baf11cSdamien ifp->if_flags |= IFF_OACTIVE; 141368baf11cSdamien break; 141468baf11cSdamien } 141568baf11cSdamien /* Send pending management frames first. */ 141668baf11cSdamien IF_DEQUEUE(&ic->ic_mgtq, m); 141768baf11cSdamien if (m != NULL) { 141868baf11cSdamien ni = (void *)m->m_pkthdr.rcvif; 141968baf11cSdamien goto sendit; 142068baf11cSdamien } 142168baf11cSdamien if (ic->ic_state != IEEE80211_S_RUN) 142268baf11cSdamien break; 142368baf11cSdamien 142468baf11cSdamien /* Encapsulate and send data frames. */ 142568baf11cSdamien IFQ_DEQUEUE(&ifp->if_snd, m); 142668baf11cSdamien if (m == NULL) 142768baf11cSdamien break; 142868baf11cSdamien #if NBPFILTER > 0 142968baf11cSdamien if (ifp->if_bpf != NULL) 143068baf11cSdamien bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT); 143168baf11cSdamien #endif 143268baf11cSdamien if ((m = ieee80211_encap(ifp, m, &ni)) == NULL) 143368baf11cSdamien continue; 143468baf11cSdamien sendit: 143568baf11cSdamien #if NBPFILTER > 0 143668baf11cSdamien if (ic->ic_rawbpf != NULL) 143768baf11cSdamien bpf_mtap(ic->ic_rawbpf, m, BPF_DIRECTION_OUT); 143868baf11cSdamien #endif 143968baf11cSdamien if (otus_tx(sc, m, ni) != 0) { 144068baf11cSdamien ieee80211_release_node(ic, ni); 144168baf11cSdamien ifp->if_oerrors++; 144268baf11cSdamien continue; 144368baf11cSdamien } 144468baf11cSdamien 144568baf11cSdamien sc->sc_tx_timer = 5; 144668baf11cSdamien ifp->if_timer = 1; 144768baf11cSdamien } 144868baf11cSdamien } 144968baf11cSdamien 145068baf11cSdamien void 145168baf11cSdamien otus_watchdog(struct ifnet *ifp) 145268baf11cSdamien { 145368baf11cSdamien struct otus_softc *sc = ifp->if_softc; 145468baf11cSdamien 145568baf11cSdamien ifp->if_timer = 0; 145668baf11cSdamien 145768baf11cSdamien if (sc->sc_tx_timer > 0) { 145868baf11cSdamien if (--sc->sc_tx_timer == 0) { 145968baf11cSdamien printf("%s: device timeout\n", sc->sc_dev.dv_xname); 146068baf11cSdamien /* otus_init(ifp); XXX needs a process context! */ 146168baf11cSdamien ifp->if_oerrors++; 146268baf11cSdamien return; 146368baf11cSdamien } 146468baf11cSdamien ifp->if_timer = 1; 146568baf11cSdamien } 146668baf11cSdamien ieee80211_watchdog(ifp); 146768baf11cSdamien } 146868baf11cSdamien 146968baf11cSdamien int 147068baf11cSdamien otus_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 147168baf11cSdamien { 147268baf11cSdamien struct otus_softc *sc = ifp->if_softc; 147368baf11cSdamien struct ieee80211com *ic = &sc->sc_ic; 147468baf11cSdamien struct ifaddr *ifa; 147568baf11cSdamien struct ifreq *ifr; 147668baf11cSdamien int s, error = 0; 147768baf11cSdamien 147868baf11cSdamien s = splnet(); 147968baf11cSdamien 148068baf11cSdamien switch (cmd) { 148168baf11cSdamien case SIOCSIFADDR: 148268baf11cSdamien ifa = (struct ifaddr *)data; 148368baf11cSdamien ifp->if_flags |= IFF_UP; 148468baf11cSdamien #ifdef INET 148568baf11cSdamien if (ifa->ifa_addr->sa_family == AF_INET) 148668baf11cSdamien arp_ifinit(&ic->ic_ac, ifa); 148768baf11cSdamien #endif 148868baf11cSdamien /* FALLTHROUGH */ 148968baf11cSdamien case SIOCSIFFLAGS: 149068baf11cSdamien if (ifp->if_flags & IFF_UP) { 149168baf11cSdamien if ((ifp->if_flags & IFF_RUNNING) && 149268baf11cSdamien ((ifp->if_flags ^ sc->sc_if_flags) & 149368baf11cSdamien (IFF_ALLMULTI | IFF_PROMISC)) != 0) { 149468baf11cSdamien otus_set_multi(sc); 149568baf11cSdamien } else if (!(ifp->if_flags & IFF_RUNNING)) 149668baf11cSdamien otus_init(ifp); 149768baf11cSdamien 149868baf11cSdamien } else if (ifp->if_flags & IFF_RUNNING) 149968baf11cSdamien otus_stop(ifp); 150068baf11cSdamien 150168baf11cSdamien sc->sc_if_flags = ifp->if_flags; 150268baf11cSdamien break; 150368baf11cSdamien case SIOCADDMULTI: 150468baf11cSdamien case SIOCDELMULTI: 150568baf11cSdamien ifr = (struct ifreq *)data; 150668baf11cSdamien error = (cmd == SIOCADDMULTI) ? 150768baf11cSdamien ether_addmulti(ifr, &ic->ic_ac) : 150868baf11cSdamien ether_delmulti(ifr, &ic->ic_ac); 150968baf11cSdamien if (error == ENETRESET) 151068baf11cSdamien error = 0; 151168baf11cSdamien break; 151288185ad4Sdamien case SIOCS80211CHANNEL: 151388185ad4Sdamien error = ieee80211_ioctl(ifp, cmd, data); 151488185ad4Sdamien if (error == ENETRESET && 151588185ad4Sdamien ic->ic_opmode == IEEE80211_M_MONITOR) { 151688185ad4Sdamien if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == 151788185ad4Sdamien (IFF_UP | IFF_RUNNING)) 15186e30e88dSdamien otus_set_chan(sc, ic->ic_ibss_chan, 0); 151988185ad4Sdamien error = 0; 152088185ad4Sdamien } 152188185ad4Sdamien break; 152268baf11cSdamien default: 152368baf11cSdamien error = ieee80211_ioctl(ifp, cmd, data); 152468baf11cSdamien } 152568baf11cSdamien 152668baf11cSdamien if (error == ENETRESET) { 152768baf11cSdamien if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == 152868baf11cSdamien (IFF_UP | IFF_RUNNING)) 152968baf11cSdamien otus_init(ifp); 153068baf11cSdamien error = 0; 153168baf11cSdamien } 153268baf11cSdamien 153368baf11cSdamien splx(s); 153468baf11cSdamien return error; 153568baf11cSdamien } 153668baf11cSdamien 153768baf11cSdamien int 153868baf11cSdamien otus_set_multi(struct otus_softc *sc) 153968baf11cSdamien { 154068baf11cSdamien struct arpcom *ac = &sc->sc_ic.ic_ac; 154168baf11cSdamien struct ifnet *ifp = &ac->ac_if; 154268baf11cSdamien struct ether_multi *enm; 154368baf11cSdamien struct ether_multistep step; 154468baf11cSdamien uint32_t lo, hi; 154568baf11cSdamien uint8_t bit; 154668baf11cSdamien 154768baf11cSdamien if ((ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) != 0) { 154868baf11cSdamien lo = hi = 0xffffffff; 154968baf11cSdamien goto done; 155068baf11cSdamien } 155168baf11cSdamien lo = hi = 0; 155268baf11cSdamien ETHER_FIRST_MULTI(step, ac, enm); 155368baf11cSdamien while (enm != NULL) { 155468baf11cSdamien if (bcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN)) { 155568baf11cSdamien ifp->if_flags |= IFF_ALLMULTI; 155668baf11cSdamien lo = hi = 0xffffffff; 155768baf11cSdamien goto done; 155868baf11cSdamien } 155968baf11cSdamien bit = enm->enm_addrlo[5] >> 2; 156068baf11cSdamien if (bit < 32) 156168baf11cSdamien lo |= 1 << bit; 156268baf11cSdamien else 156368baf11cSdamien hi |= 1 << (bit - 32); 156468baf11cSdamien ETHER_NEXT_MULTI(step, enm); 156568baf11cSdamien } 156668baf11cSdamien done: 156788185ad4Sdamien hi |= 1 << 31; /* Make sure the broadcast bit is set. */ 156868baf11cSdamien otus_write(sc, AR_MAC_REG_GROUP_HASH_TBL_L, lo); 156968baf11cSdamien otus_write(sc, AR_MAC_REG_GROUP_HASH_TBL_H, hi); 157068baf11cSdamien return otus_write_barrier(sc); 157168baf11cSdamien } 157268baf11cSdamien 157368baf11cSdamien void 157468baf11cSdamien otus_updateedca(struct ieee80211com *ic) 157568baf11cSdamien { 157668baf11cSdamien /* Do it in a process context. */ 157768baf11cSdamien otus_do_async(ic->ic_softc, otus_updateedca_cb, NULL, 0); 157868baf11cSdamien } 157968baf11cSdamien 158068baf11cSdamien /* ARGSUSED */ 158168baf11cSdamien void 158268baf11cSdamien otus_updateedca_cb(struct otus_softc *sc, void *arg) 158368baf11cSdamien { 158468baf11cSdamien #define EXP2(val) ((1 << (val)) - 1) 158568baf11cSdamien #define AIFS(val) ((val) * 9 + 10) 158668baf11cSdamien struct ieee80211com *ic = &sc->sc_ic; 158768baf11cSdamien const struct ieee80211_edca_ac_params *edca; 158868baf11cSdamien int s; 158968baf11cSdamien 159068baf11cSdamien s = splnet(); 159168baf11cSdamien 159268baf11cSdamien edca = (ic->ic_flags & IEEE80211_F_QOS) ? 159368baf11cSdamien ic->ic_edca_ac : otus_edca_def; 159468baf11cSdamien 159568baf11cSdamien /* Set CWmin/CWmax values. */ 159668baf11cSdamien otus_write(sc, AR_MAC_REG_AC0_CW, 159768baf11cSdamien EXP2(edca[EDCA_AC_BE].ac_ecwmax) << 16 | 159868baf11cSdamien EXP2(edca[EDCA_AC_BE].ac_ecwmin)); 159968baf11cSdamien otus_write(sc, AR_MAC_REG_AC1_CW, 160068baf11cSdamien EXP2(edca[EDCA_AC_BK].ac_ecwmax) << 16 | 160168baf11cSdamien EXP2(edca[EDCA_AC_BK].ac_ecwmin)); 160268baf11cSdamien otus_write(sc, AR_MAC_REG_AC2_CW, 160368baf11cSdamien EXP2(edca[EDCA_AC_VI].ac_ecwmax) << 16 | 160468baf11cSdamien EXP2(edca[EDCA_AC_VI].ac_ecwmin)); 160568baf11cSdamien otus_write(sc, AR_MAC_REG_AC3_CW, 160668baf11cSdamien EXP2(edca[EDCA_AC_VO].ac_ecwmax) << 16 | 160768baf11cSdamien EXP2(edca[EDCA_AC_VO].ac_ecwmin)); 160868baf11cSdamien otus_write(sc, AR_MAC_REG_AC4_CW, /* Special TXQ. */ 160968baf11cSdamien EXP2(edca[EDCA_AC_VO].ac_ecwmax) << 16 | 161068baf11cSdamien EXP2(edca[EDCA_AC_VO].ac_ecwmin)); 161168baf11cSdamien 161268baf11cSdamien /* Set AIFSN values. */ 161368baf11cSdamien otus_write(sc, AR_MAC_REG_AC1_AC0_AIFS, 161468baf11cSdamien AIFS(edca[EDCA_AC_VI].ac_aifsn) << 24 | 161568baf11cSdamien AIFS(edca[EDCA_AC_BK].ac_aifsn) << 12 | 161668baf11cSdamien AIFS(edca[EDCA_AC_BE].ac_aifsn)); 161768baf11cSdamien otus_write(sc, AR_MAC_REG_AC3_AC2_AIFS, 161868baf11cSdamien AIFS(edca[EDCA_AC_VO].ac_aifsn) << 16 | /* Special TXQ. */ 161968baf11cSdamien AIFS(edca[EDCA_AC_VO].ac_aifsn) << 4 | 162068baf11cSdamien AIFS(edca[EDCA_AC_VI].ac_aifsn) >> 8); 162168baf11cSdamien 162268baf11cSdamien /* Set TXOP limit. */ 162368baf11cSdamien otus_write(sc, AR_MAC_REG_AC1_AC0_TXOP, 162468baf11cSdamien edca[EDCA_AC_BK].ac_txoplimit << 16 | 162568baf11cSdamien edca[EDCA_AC_BE].ac_txoplimit); 162668baf11cSdamien otus_write(sc, AR_MAC_REG_AC3_AC2_TXOP, 162768baf11cSdamien edca[EDCA_AC_VO].ac_txoplimit << 16 | 162868baf11cSdamien edca[EDCA_AC_VI].ac_txoplimit); 162968baf11cSdamien 163068baf11cSdamien splx(s); 163168baf11cSdamien 163268baf11cSdamien (void)otus_write_barrier(sc); 163368baf11cSdamien #undef AIFS 163468baf11cSdamien #undef EXP2 163568baf11cSdamien } 163668baf11cSdamien 163768baf11cSdamien void 163868baf11cSdamien otus_updateslot(struct ieee80211com *ic) 163968baf11cSdamien { 164068baf11cSdamien /* Do it in a process context. */ 164168baf11cSdamien otus_do_async(ic->ic_softc, otus_updateslot_cb, NULL, 0); 164268baf11cSdamien } 164368baf11cSdamien 164468baf11cSdamien /* ARGSUSED */ 164568baf11cSdamien void 164668baf11cSdamien otus_updateslot_cb(struct otus_softc *sc, void *arg) 164768baf11cSdamien { 164868baf11cSdamien uint32_t slottime; 164968baf11cSdamien 165068baf11cSdamien slottime = (sc->sc_ic.ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20; 165168baf11cSdamien otus_write(sc, AR_MAC_REG_SLOT_TIME, slottime << 10); 165268baf11cSdamien (void)otus_write_barrier(sc); 165368baf11cSdamien } 165468baf11cSdamien 165568baf11cSdamien int 165668baf11cSdamien otus_init_mac(struct otus_softc *sc) 165768baf11cSdamien { 165868baf11cSdamien int error; 165968baf11cSdamien 166068baf11cSdamien otus_write(sc, AR_MAC_REG_ACK_EXTENSION, 0x40); 166168baf11cSdamien otus_write(sc, AR_MAC_REG_RETRY_MAX, 0); 166268baf11cSdamien otus_write(sc, AR_MAC_REG_SNIFFER, 0x2000000); 166368baf11cSdamien otus_write(sc, AR_MAC_REG_RX_THRESHOLD, 0xc1f80); 166468baf11cSdamien otus_write(sc, AR_MAC_REG_RX_PE_DELAY, 0x70); 166568baf11cSdamien otus_write(sc, AR_MAC_REG_EIFS_AND_SIFS, 0xa144000); 166668baf11cSdamien otus_write(sc, AR_MAC_REG_SLOT_TIME, 9 << 10); 166768baf11cSdamien otus_write(sc, 0x1c3b2c, 0x19000000); 166868baf11cSdamien /* NAV protects ACK only (in TXOP). */ 166968baf11cSdamien otus_write(sc, 0x1c3b38, 0x201); 167068baf11cSdamien /* Set beacon Tx power to 0x7. */ 167168baf11cSdamien otus_write(sc, AR_MAC_REG_BCN_HT1, 0x8000170); 167268baf11cSdamien otus_write(sc, AR_MAC_REG_BACKOFF_PROTECT, 0x105); 167368baf11cSdamien otus_write(sc, 0x1c3b9c, 0x10000a); 167468baf11cSdamien /* Filter any control frames, BAR is bit 24. */ 167568baf11cSdamien otus_write(sc, 0x1c368c, 0x0500ffff); 167668baf11cSdamien otus_write(sc, 0x1c3c40, 0x1); 167768baf11cSdamien otus_write(sc, AR_MAC_REG_BASIC_RATE, 0x150f); 167868baf11cSdamien otus_write(sc, AR_MAC_REG_MANDATORY_RATE, 0x150f); 167968baf11cSdamien otus_write(sc, AR_MAC_REG_RTS_CTS_RATE, 0x10b01bb); 168068baf11cSdamien otus_write(sc, 0x1c3694, 0x4003c1e); 168168baf11cSdamien /* Enable LED0 and LED1. */ 168268baf11cSdamien otus_write(sc, 0x1d0100, 0x3); 168368baf11cSdamien otus_write(sc, 0x1d0104, 0x3); 168468baf11cSdamien /* Switch MAC to OTUS interface. */ 168568baf11cSdamien otus_write(sc, 0x1c3600, 0x3); 168668baf11cSdamien otus_write(sc, 0x1c3c50, 0xffff); 168768baf11cSdamien otus_write(sc, 0x1c3680, 0xf00008); 168868baf11cSdamien /* Disable Rx timeout (workaround). */ 168968baf11cSdamien otus_write(sc, 0x1c362c, 0); 169068baf11cSdamien 169168baf11cSdamien /* Set USB Rx stream mode maximum frame number to 2. */ 169268baf11cSdamien otus_write(sc, 0x1e1110, 0x4); 169368baf11cSdamien /* Set USB Rx stream mode timeout to 10us. */ 169468baf11cSdamien otus_write(sc, 0x1e1114, 0x80); 169568baf11cSdamien 169668baf11cSdamien /* Set clock frequency to 88/80MHz. */ 169768baf11cSdamien otus_write(sc, 0x1d4008, 0x73); 169868baf11cSdamien /* Set WLAN DMA interrupt mode: generate intr per packet. */ 169968baf11cSdamien otus_write(sc, 0x1c3d7c, 0x110011); 170068baf11cSdamien otus_write(sc, 0x1c3bb0, 0x4); 170168baf11cSdamien otus_write(sc, AR_MAC_REG_TXOP_NOT_ENOUGH_INDICATION, 0x141e0f48); 170268baf11cSdamien 170368baf11cSdamien /* Disable HW decryption for now. */ 170468baf11cSdamien otus_write(sc, 0x1c3678, 0x78); 170568baf11cSdamien 170668baf11cSdamien if ((error = otus_write_barrier(sc)) != 0) 170768baf11cSdamien return error; 170868baf11cSdamien 170968baf11cSdamien /* Set default EDCA parameters. */ 171068baf11cSdamien otus_updateedca_cb(sc, NULL); 171168baf11cSdamien 171268baf11cSdamien return 0; 171368baf11cSdamien } 171468baf11cSdamien 171568baf11cSdamien /* 171668baf11cSdamien * Return default value for PHY register based on current operating mode. 171768baf11cSdamien */ 171868baf11cSdamien uint32_t 171968baf11cSdamien otus_phy_get_def(struct otus_softc *sc, uint32_t reg) 172068baf11cSdamien { 172168baf11cSdamien int i; 172268baf11cSdamien 172368baf11cSdamien for (i = 0; i < nitems(ar5416_phy_regs); i++) 172468baf11cSdamien if (AR_PHY(ar5416_phy_regs[i]) == reg) 172568baf11cSdamien return sc->phy_vals[i]; 172688185ad4Sdamien return 0; /* Register not found. */ 172768baf11cSdamien } 172868baf11cSdamien 172968baf11cSdamien /* 173068baf11cSdamien * Update PHY's programming based on vendor-specific data stored in EEPROM. 173168baf11cSdamien * This is for FEM-type devices only. 173268baf11cSdamien */ 173368baf11cSdamien int 173468baf11cSdamien otus_set_board_values(struct otus_softc *sc, struct ieee80211_channel *c) 173568baf11cSdamien { 173668baf11cSdamien const struct ModalEepHeader *eep; 173768baf11cSdamien uint32_t tmp, offset; 173868baf11cSdamien 173968baf11cSdamien if (IEEE80211_IS_CHAN_5GHZ(c)) 174068baf11cSdamien eep = &sc->eeprom.modalHeader[0]; 174168baf11cSdamien else 174268baf11cSdamien eep = &sc->eeprom.modalHeader[1]; 174368baf11cSdamien 174468baf11cSdamien /* Offset of chain 2. */ 174568baf11cSdamien offset = 2 * 0x1000; 174668baf11cSdamien 174768baf11cSdamien tmp = letoh32(eep->antCtrlCommon); 1748803f2018Sdamien otus_write(sc, AR_PHY_SWITCH_COM, tmp); 174968baf11cSdamien 175068baf11cSdamien tmp = letoh32(eep->antCtrlChain[0]); 1751803f2018Sdamien otus_write(sc, AR_PHY_SWITCH_CHAIN_0, tmp); 175268baf11cSdamien 175368baf11cSdamien tmp = letoh32(eep->antCtrlChain[1]); 1754803f2018Sdamien otus_write(sc, AR_PHY_SWITCH_CHAIN_0 + offset, tmp); 175568baf11cSdamien 175668baf11cSdamien if (1 /* sc->sc_sco == AR_SCO_SCN */) { 175768baf11cSdamien tmp = otus_phy_get_def(sc, AR_PHY_SETTLING); 175868baf11cSdamien tmp &= ~(0x7f << 7); 175968baf11cSdamien tmp |= (eep->switchSettling & 0x7f) << 7; 1760803f2018Sdamien otus_write(sc, AR_PHY_SETTLING, tmp); 176168baf11cSdamien } 176268baf11cSdamien 176368baf11cSdamien tmp = otus_phy_get_def(sc, AR_PHY_DESIRED_SZ); 176468baf11cSdamien tmp &= ~0xffff; 176568baf11cSdamien tmp |= eep->pgaDesiredSize << 8 | eep->adcDesiredSize; 1766803f2018Sdamien otus_write(sc, AR_PHY_DESIRED_SZ, tmp); 176768baf11cSdamien 176868baf11cSdamien tmp = eep->txEndToXpaOff << 24 | eep->txEndToXpaOff << 16 | 176968baf11cSdamien eep->txFrameToXpaOn << 8 | eep->txFrameToXpaOn; 1770803f2018Sdamien otus_write(sc, AR_PHY_RF_CTL4, tmp); 177168baf11cSdamien 177268baf11cSdamien tmp = otus_phy_get_def(sc, AR_PHY_RF_CTL3); 177368baf11cSdamien tmp &= ~(0xff << 16); 177468baf11cSdamien tmp |= eep->txEndToRxOn << 16; 1775803f2018Sdamien otus_write(sc, AR_PHY_RF_CTL3, tmp); 177668baf11cSdamien 177768baf11cSdamien tmp = otus_phy_get_def(sc, AR_PHY_CCA); 177868baf11cSdamien tmp &= ~(0x7f << 12); 177968baf11cSdamien tmp |= (eep->thresh62 & 0x7f) << 12; 1780803f2018Sdamien otus_write(sc, AR_PHY_CCA, tmp); 178168baf11cSdamien 178268baf11cSdamien tmp = otus_phy_get_def(sc, AR_PHY_RXGAIN); 178368baf11cSdamien tmp &= ~(0x3f << 12); 178468baf11cSdamien tmp |= (eep->txRxAttenCh[0] & 0x3f) << 12; 1785803f2018Sdamien otus_write(sc, AR_PHY_RXGAIN, tmp); 178668baf11cSdamien 178768baf11cSdamien tmp = otus_phy_get_def(sc, AR_PHY_RXGAIN + offset); 178868baf11cSdamien tmp &= ~(0x3f << 12); 178968baf11cSdamien tmp |= (eep->txRxAttenCh[1] & 0x3f) << 12; 1790803f2018Sdamien otus_write(sc, AR_PHY_RXGAIN + offset, tmp); 179168baf11cSdamien 179268baf11cSdamien tmp = otus_phy_get_def(sc, AR_PHY_GAIN_2GHZ); 179368baf11cSdamien tmp &= ~(0x3f << 18); 179468baf11cSdamien tmp |= (eep->rxTxMarginCh[0] & 0x3f) << 18; 179568baf11cSdamien if (IEEE80211_IS_CHAN_5GHZ(c)) { 179668baf11cSdamien tmp &= ~(0xf << 10); 179768baf11cSdamien tmp |= (eep->bswMargin[0] & 0xf) << 10; 179868baf11cSdamien } 1799803f2018Sdamien otus_write(sc, AR_PHY_GAIN_2GHZ, tmp); 180068baf11cSdamien 180168baf11cSdamien tmp = otus_phy_get_def(sc, AR_PHY_GAIN_2GHZ + offset); 180268baf11cSdamien tmp &= ~(0x3f << 18); 180368baf11cSdamien tmp |= (eep->rxTxMarginCh[1] & 0x3f) << 18; 1804803f2018Sdamien otus_write(sc, AR_PHY_GAIN_2GHZ + offset, tmp); 180568baf11cSdamien 180668baf11cSdamien tmp = otus_phy_get_def(sc, AR_PHY_TIMING_CTRL4); 180768baf11cSdamien tmp &= ~(0x3f << 5 | 0x1f); 180868baf11cSdamien tmp |= (eep->iqCalICh[0] & 0x3f) << 5 | (eep->iqCalQCh[0] & 0x1f); 1809803f2018Sdamien otus_write(sc, AR_PHY_TIMING_CTRL4, tmp); 181068baf11cSdamien 181168baf11cSdamien tmp = otus_phy_get_def(sc, AR_PHY_TIMING_CTRL4 + offset); 181268baf11cSdamien tmp &= ~(0x3f << 5 | 0x1f); 181368baf11cSdamien tmp |= (eep->iqCalICh[1] & 0x3f) << 5 | (eep->iqCalQCh[1] & 0x1f); 1814803f2018Sdamien otus_write(sc, AR_PHY_TIMING_CTRL4 + offset, tmp); 181568baf11cSdamien 1816803f2018Sdamien tmp = otus_phy_get_def(sc, AR_PHY_TPCRG1); 181768baf11cSdamien tmp &= ~(0xf << 16); 181868baf11cSdamien tmp |= (eep->xpd & 0xf) << 16; 1819803f2018Sdamien otus_write(sc, AR_PHY_TPCRG1, tmp); 182068baf11cSdamien 182168baf11cSdamien return otus_write_barrier(sc); 182268baf11cSdamien } 182368baf11cSdamien 182468baf11cSdamien int 182568baf11cSdamien otus_program_phy(struct otus_softc *sc, struct ieee80211_channel *c) 182668baf11cSdamien { 182768baf11cSdamien const uint32_t *vals; 182868baf11cSdamien int error, i; 182968baf11cSdamien 183068baf11cSdamien /* Select PHY programming based on band and bandwidth. */ 183168baf11cSdamien if (IEEE80211_IS_CHAN_2GHZ(c)) 183268baf11cSdamien vals = ar5416_phy_vals_2ghz_20mhz; 183368baf11cSdamien else 183468baf11cSdamien vals = ar5416_phy_vals_5ghz_20mhz; 183568baf11cSdamien for (i = 0; i < nitems(ar5416_phy_regs); i++) 1836803f2018Sdamien otus_write(sc, AR_PHY(ar5416_phy_regs[i]), vals[i]); 183768baf11cSdamien sc->phy_vals = vals; 183868baf11cSdamien 183968baf11cSdamien if (sc->eeprom.baseEepHeader.deviceType == 0x80) /* FEM */ 184068baf11cSdamien if ((error = otus_set_board_values(sc, c)) != 0) 184168baf11cSdamien return error; 184268baf11cSdamien 184368baf11cSdamien /* Initial Tx power settings. */ 1844803f2018Sdamien otus_write(sc, AR_PHY_POWER_TX_RATE_MAX, 0x7f); 1845803f2018Sdamien otus_write(sc, AR_PHY_POWER_TX_RATE1, 0x3f3f3f3f); 1846803f2018Sdamien otus_write(sc, AR_PHY_POWER_TX_RATE2, 0x3f3f3f3f); 1847803f2018Sdamien otus_write(sc, AR_PHY_POWER_TX_RATE3, 0x3f3f3f3f); 1848803f2018Sdamien otus_write(sc, AR_PHY_POWER_TX_RATE4, 0x3f3f3f3f); 1849803f2018Sdamien otus_write(sc, AR_PHY_POWER_TX_RATE5, 0x3f3f3f3f); 1850803f2018Sdamien otus_write(sc, AR_PHY_POWER_TX_RATE6, 0x3f3f3f3f); 1851803f2018Sdamien otus_write(sc, AR_PHY_POWER_TX_RATE7, 0x3f3f3f3f); 1852803f2018Sdamien otus_write(sc, AR_PHY_POWER_TX_RATE8, 0x3f3f3f3f); 1853803f2018Sdamien otus_write(sc, AR_PHY_POWER_TX_RATE9, 0x3f3f3f3f); 185468baf11cSdamien 185568baf11cSdamien if (IEEE80211_IS_CHAN_2GHZ(c)) 185668baf11cSdamien otus_write(sc, 0x1d4014, 0x5163); 185768baf11cSdamien else 185868baf11cSdamien otus_write(sc, 0x1d4014, 0x5143); 185968baf11cSdamien 186068baf11cSdamien return otus_write_barrier(sc); 186168baf11cSdamien } 186268baf11cSdamien 186368baf11cSdamien static __inline uint8_t 186468baf11cSdamien otus_reverse_bits(uint8_t v) 186568baf11cSdamien { 186668baf11cSdamien v = ((v >> 1) & 0x55) | ((v & 0x55) << 1); 186768baf11cSdamien v = ((v >> 2) & 0x33) | ((v & 0x33) << 2); 186868baf11cSdamien v = ((v >> 4) & 0x0f) | ((v & 0x0f) << 4); 186968baf11cSdamien return v; 187068baf11cSdamien } 187168baf11cSdamien 18726e30e88dSdamien int 18736e30e88dSdamien otus_set_rf_bank4(struct otus_softc *sc, struct ieee80211_channel *c) 18746e30e88dSdamien { 18756e30e88dSdamien uint8_t chansel, d0, d1; 18766e30e88dSdamien uint16_t data; 18776e30e88dSdamien int error; 18786e30e88dSdamien 18796e30e88dSdamien d0 = 0; 18806e30e88dSdamien if (IEEE80211_IS_CHAN_5GHZ(c)) { 18816e30e88dSdamien chansel = (c->ic_freq - 4800) / 5; 18826e30e88dSdamien if (chansel & 1) 18836e30e88dSdamien d0 |= AR_BANK4_AMODE_REFSEL(2); 18846e30e88dSdamien else 18856e30e88dSdamien d0 |= AR_BANK4_AMODE_REFSEL(1); 18866e30e88dSdamien } else { 18876e30e88dSdamien d0 |= AR_BANK4_AMODE_REFSEL(2); 18886e30e88dSdamien if (c->ic_freq == 2484) { /* CH 14 */ 18896e30e88dSdamien d0 |= AR_BANK4_BMODE_LF_SYNTH_FREQ; 18906e30e88dSdamien chansel = 10 + (c->ic_freq - 2274) / 5; 18916e30e88dSdamien } else 18926e30e88dSdamien chansel = 16 + (c->ic_freq - 2272) / 5; 18936e30e88dSdamien chansel <<= 2; 18946e30e88dSdamien } 18956e30e88dSdamien d0 |= AR_BANK4_ADDR(1) | AR_BANK4_CHUP; 18966e30e88dSdamien d1 = otus_reverse_bits(chansel); 18976e30e88dSdamien 18986e30e88dSdamien /* Write bits 0-4 of d0 and d1. */ 18996e30e88dSdamien data = (d1 & 0x1f) << 5 | (d0 & 0x1f); 19006e30e88dSdamien otus_write(sc, AR_PHY(44), data); 19016e30e88dSdamien /* Write bits 5-7 of d0 and d1. */ 19026e30e88dSdamien data = (d1 >> 5) << 5 | (d0 >> 5); 19036e30e88dSdamien otus_write(sc, AR_PHY(58), data); 19046e30e88dSdamien 19056e30e88dSdamien if ((error = otus_write_barrier(sc)) == 0) 19066e30e88dSdamien usbd_delay_ms(sc->sc_udev, 10); 19076e30e88dSdamien return error; 19086e30e88dSdamien } 19096e30e88dSdamien 191068baf11cSdamien void 191168baf11cSdamien otus_get_delta_slope(uint32_t coeff, uint32_t *exponent, uint32_t *mantissa) 191268baf11cSdamien { 191368baf11cSdamien #define COEFF_SCALE_SHIFT 24 191468baf11cSdamien uint32_t exp, man; 191568baf11cSdamien 191668baf11cSdamien /* exponent = 14 - floor(log2(coeff)) */ 191768baf11cSdamien for (exp = 31; exp > 0; exp--) 191868baf11cSdamien if (coeff & (1 << exp)) 191968baf11cSdamien break; 192068baf11cSdamien KASSERT(exp != 0); 192168baf11cSdamien exp = 14 - (exp - COEFF_SCALE_SHIFT); 192268baf11cSdamien 192368baf11cSdamien /* mantissa = floor(coeff * 2^exponent + 0.5) */ 192468baf11cSdamien man = coeff + (1 << (COEFF_SCALE_SHIFT - exp - 1)); 192568baf11cSdamien 192668baf11cSdamien *mantissa = man >> (COEFF_SCALE_SHIFT - exp); 192768baf11cSdamien *exponent = exp - 16; 192868baf11cSdamien #undef COEFF_SCALE_SHIFT 192968baf11cSdamien } 193068baf11cSdamien 193168baf11cSdamien int 19326e30e88dSdamien otus_set_chan(struct otus_softc *sc, struct ieee80211_channel *c, int assoc) 193368baf11cSdamien { 193468baf11cSdamien struct ieee80211com *ic = &sc->sc_ic; 193568baf11cSdamien struct ar_cmd_frequency cmd; 193668baf11cSdamien struct ar_rsp_frequency rsp; 193768baf11cSdamien const uint32_t *vals; 193868baf11cSdamien uint32_t coeff, exp, man, tmp; 19396e30e88dSdamien uint8_t code; 194068baf11cSdamien int error, chan, i; 194168baf11cSdamien 194268baf11cSdamien chan = ieee80211_chan2ieee(ic, c); 19436e30e88dSdamien DPRINTF(("setting channel %d (%dMHz)\n", chan, c->ic_freq)); 194468baf11cSdamien 194568baf11cSdamien tmp = IEEE80211_IS_CHAN_2GHZ(c) ? 0x105 : 0x104; 194668baf11cSdamien otus_write(sc, AR_MAC_REG_DYNAMIC_SIFS_ACK, tmp); 19476e30e88dSdamien if ((error = otus_write_barrier(sc)) != 0) 19486e30e88dSdamien return error; 194968baf11cSdamien 195088185ad4Sdamien /* Disable BB Heavy Clip. */ 1951803f2018Sdamien otus_write(sc, AR_PHY_HEAVY_CLIP_ENABLE, 0x200); 19526e30e88dSdamien if ((error = otus_write_barrier(sc)) != 0) 19536e30e88dSdamien return error; 195468baf11cSdamien 19556e30e88dSdamien /* XXX Is that FREQ_START ? */ 195668baf11cSdamien error = otus_cmd(sc, AR_CMD_FREQ_STRAT, NULL, 0, NULL); 195768baf11cSdamien if (error != 0) 195868baf11cSdamien return error; 195968baf11cSdamien 196088185ad4Sdamien /* Reprogram PHY and RF on channel band or bandwidth changes. */ 19616e30e88dSdamien if (sc->bb_reset || c->ic_flags != sc->sc_curchan->ic_flags) { 196268baf11cSdamien DPRINTF(("band switch\n")); 196368baf11cSdamien 19646e30e88dSdamien /* Cold/Warm reset BB/ADDA. */ 19656e30e88dSdamien otus_write(sc, 0x1d4004, sc->bb_reset ? 0x800 : 0x400); 19666e30e88dSdamien if ((error = otus_write_barrier(sc)) != 0) 19676e30e88dSdamien return error; 196868baf11cSdamien otus_write(sc, 0x1d4004, 0); 19696e30e88dSdamien if ((error = otus_write_barrier(sc)) != 0) 19706e30e88dSdamien return error; 19716e30e88dSdamien sc->bb_reset = 0; 197268baf11cSdamien 197368baf11cSdamien if ((error = otus_program_phy(sc, c)) != 0) { 197468baf11cSdamien printf("%s: could not program PHY\n", 197568baf11cSdamien sc->sc_dev.dv_xname); 197668baf11cSdamien return error; 197768baf11cSdamien } 197868baf11cSdamien 197968baf11cSdamien /* Select RF programming based on band. */ 198068baf11cSdamien if (IEEE80211_IS_CHAN_5GHZ(c)) 198168baf11cSdamien vals = ar5416_banks_vals_5ghz; 198268baf11cSdamien else 198368baf11cSdamien vals = ar5416_banks_vals_2ghz; 1984803f2018Sdamien for (i = 0; i < nitems(ar5416_banks_regs); i++) 1985803f2018Sdamien otus_write(sc, AR_PHY(ar5416_banks_regs[i]), vals[i]); 19866e30e88dSdamien if ((error = otus_write_barrier(sc)) != 0) { 19876e30e88dSdamien printf("%s: could not program RF\n", 19886e30e88dSdamien sc->sc_dev.dv_xname); 198968baf11cSdamien return error; 19906e30e88dSdamien } 199168baf11cSdamien code = AR_CMD_RF_INIT; 199268baf11cSdamien } else { 199368baf11cSdamien code = AR_CMD_FREQUENCY; 199468baf11cSdamien } 199568baf11cSdamien 19966e30e88dSdamien if ((error = otus_set_rf_bank4(sc, c)) != 0) 199768baf11cSdamien return error; 199868baf11cSdamien 1999803f2018Sdamien tmp = (sc->txmask == 0x5) ? 0x340 : 0x240; 2000803f2018Sdamien otus_write(sc, AR_PHY_TURBO, tmp); 20016e30e88dSdamien if ((error = otus_write_barrier(sc)) != 0) 20026e30e88dSdamien return error; 200368baf11cSdamien 200468baf11cSdamien /* Send firmware command to set channel. */ 200568baf11cSdamien cmd.freq = htole32((uint32_t)c->ic_freq * 1000); 200668baf11cSdamien cmd.dynht2040 = htole32(0); 200768baf11cSdamien cmd.htena = htole32(1); 200868baf11cSdamien /* Set Delta Slope (exponent and mantissa). */ 200968baf11cSdamien coeff = (100 << 24) / c->ic_freq; 201068baf11cSdamien otus_get_delta_slope(coeff, &exp, &man); 20116e30e88dSdamien cmd.dsc_exp = htole32(exp); 20126e30e88dSdamien cmd.dsc_man = htole32(man); 20136e30e88dSdamien DPRINTF(("ds coeff=%u exp=%u man=%u\n", coeff, exp, man)); 20146e30e88dSdamien /* For Short GI, coeff is 9/10 that of normal coeff. */ 201568baf11cSdamien coeff = (9 * coeff) / 10; 201668baf11cSdamien otus_get_delta_slope(coeff, &exp, &man); 20176e30e88dSdamien cmd.dsc_shgi_exp = htole32(exp); 20186e30e88dSdamien cmd.dsc_shgi_man = htole32(man); 20196e30e88dSdamien DPRINTF(("ds shgi coeff=%u exp=%u man=%u\n", coeff, exp, man)); 20206e30e88dSdamien /* Set wait time for AGC and noise calibration (100 or 200ms). */ 20216e30e88dSdamien cmd.check_loop_count = assoc ? htole32(2000) : htole32(1000); 20224351e075Sdamien DPRINTF(("%s\n", (code == AR_CMD_RF_INIT) ? "RF_INIT" : "FREQUENCY")); 20236e30e88dSdamien error = otus_cmd(sc, code, &cmd, sizeof cmd, &rsp); 202468baf11cSdamien if (error != 0) 202568baf11cSdamien return error; 20266e30e88dSdamien if ((rsp.status & htole32(AR_CAL_ERR_AGC | AR_CAL_ERR_NF_VAL)) != 0) { 202768baf11cSdamien DPRINTF(("status=0x%x\n", letoh32(rsp.status))); 20286e30e88dSdamien /* Force cold reset on next channel. */ 20296e30e88dSdamien sc->bb_reset = 1; 20306e30e88dSdamien } 20316e30e88dSdamien #ifdef OTUS_DEBUG 20326e30e88dSdamien if (otus_debug) { 20336e30e88dSdamien printf("calibration status=0x%x\n", letoh32(rsp.status)); 20346e30e88dSdamien for (i = 0; i < 2; i++) { /* 2 Rx chains */ 20356e30e88dSdamien /* Sign-extend 9-bit NF values. */ 20366e30e88dSdamien printf("noisefloor chain %d=%d\n", i, 20376e30e88dSdamien (((int32_t)letoh32(rsp.nf[i])) << 4) >> 23); 20386e30e88dSdamien printf("noisefloor ext chain %d=%d\n", i, 20396e30e88dSdamien ((int32_t)letoh32(rsp.nf_ext[i])) >> 23); 20406e30e88dSdamien } 20416e30e88dSdamien } 20426e30e88dSdamien #endif 204368baf11cSdamien sc->sc_curchan = c; 204468baf11cSdamien return 0; 204568baf11cSdamien } 204668baf11cSdamien 204768baf11cSdamien #ifdef notyet 204868baf11cSdamien int 204968baf11cSdamien otus_set_key(struct ieee80211com *ic, struct ieee80211_node *ni, 205068baf11cSdamien struct ieee80211_key *k) 205168baf11cSdamien { 205268baf11cSdamien struct otus_softc *sc = ic->ic_softc; 205368baf11cSdamien struct otus_cmd_key cmd; 205468baf11cSdamien 20550760dad6Sdamien /* Defer setting of WEP keys until interface is brought up. */ 20560760dad6Sdamien if ((ic->ic_if.if_flags & (IFF_UP | IFF_RUNNING)) != 20570760dad6Sdamien (IFF_UP | IFF_RUNNING)) 20580760dad6Sdamien return 0; 20590760dad6Sdamien 206068baf11cSdamien /* Do it in a process context. */ 206168baf11cSdamien cmd.key = *k; 206268baf11cSdamien cmd.associd = (ni != NULL) ? ni->ni_associd : 0; 206368baf11cSdamien otus_do_async(sc, otus_set_key_cb, &cmd, sizeof cmd); 206468baf11cSdamien return 0; 206568baf11cSdamien } 206668baf11cSdamien 206768baf11cSdamien void 206868baf11cSdamien otus_set_key_cb(struct otus_softc *sc, void *arg) 206968baf11cSdamien { 207068baf11cSdamien struct otus_cmd_key *cmd = arg; 207168baf11cSdamien struct ieee80211_key *k = &cmd->key; 207268baf11cSdamien struct ar_cmd_ekey key; 207368baf11cSdamien uint16_t cipher; 207468baf11cSdamien int error; 207568baf11cSdamien 207668baf11cSdamien memset(&key, 0, sizeof key); 207768baf11cSdamien if (k->k_flags & IEEE80211_KEY_GROUP) { 207868baf11cSdamien key.uid = htole16(k->k_id); 207968baf11cSdamien IEEE80211_ADDR_COPY(key.macaddr, sc->sc_ic.ic_myaddr); 20804351e075Sdamien key.macaddr[0] |= 0x80; 208168baf11cSdamien } else { 208268baf11cSdamien key.uid = htole16(OTUS_UID(cmd->associd)); 208368baf11cSdamien IEEE80211_ADDR_COPY(key.macaddr, ni->ni_macaddr); 208468baf11cSdamien } 20854351e075Sdamien key.kix = htole16(0); 208668baf11cSdamien /* Map net80211 cipher to hardware. */ 208768baf11cSdamien switch (k->k_cipher) { 208868baf11cSdamien case IEEE80211_CIPHER_WEP40: 208968baf11cSdamien cipher = AR_CIPHER_WEP64; 209068baf11cSdamien break; 209168baf11cSdamien case IEEE80211_CIPHER_WEP104: 209268baf11cSdamien cipher = AR_CIPHER_WEP128; 209368baf11cSdamien break; 209468baf11cSdamien case IEEE80211_CIPHER_TKIP: 209568baf11cSdamien cipher = AR_CIPHER_TKIP; 209668baf11cSdamien break; 209768baf11cSdamien case IEEE80211_CIPHER_CCMP: 209868baf11cSdamien cipher = AR_CIPHER_AES; 209968baf11cSdamien break; 210068baf11cSdamien default: 210168baf11cSdamien return; 210268baf11cSdamien } 210368baf11cSdamien key.cipher = htole16(cipher); 210468baf11cSdamien memcpy(key.key, k->k_key, MIN(k->k_len, 16)); 210568baf11cSdamien error = otus_cmd(sc, AR_CMD_EKEY, &key, sizeof key, NULL); 210668baf11cSdamien if (error != 0 || k->k_cipher != IEEE80211_CIPHER_TKIP) 210768baf11cSdamien return; 210868baf11cSdamien 210968baf11cSdamien /* TKIP: set Tx/Rx MIC Key. */ 21104351e075Sdamien key.kix = htole16(1); 211168baf11cSdamien memcpy(key.key, k->k_key + 16, 16); 211268baf11cSdamien (void)otus_cmd(sc, AR_CMD_EKEY, &key, sizeof key, NULL); 211368baf11cSdamien } 211468baf11cSdamien 211568baf11cSdamien void 211668baf11cSdamien otus_delete_key(struct ieee80211com *ic, struct ieee80211_node *ni, 211768baf11cSdamien struct ieee80211_key *k) 211868baf11cSdamien { 211968baf11cSdamien struct otus_softc *sc = ic->ic_softc; 212068baf11cSdamien struct otus_cmd_key cmd; 212168baf11cSdamien 212268baf11cSdamien if (!(ic->ic_if.if_flags & IFF_RUNNING) || 212368baf11cSdamien ic->ic_state != IEEE80211_S_RUN) 212468baf11cSdamien return; /* Nothing to do. */ 212568baf11cSdamien 212668baf11cSdamien /* Do it in a process context. */ 212768baf11cSdamien cmd.key = *k; 212868baf11cSdamien cmd.associd = (ni != NULL) ? ni->ni_associd : 0; 212968baf11cSdamien otus_do_async(sc, otus_delete_key_cb, &cmd, sizeof cmd); 213068baf11cSdamien } 213168baf11cSdamien 213268baf11cSdamien void 213368baf11cSdamien otus_delete_key_cb(struct otus_softc *sc, void *arg) 213468baf11cSdamien { 213568baf11cSdamien struct otus_cmd_key *cmd = arg; 213668baf11cSdamien struct ieee80211_key *k = &cmd->key; 213768baf11cSdamien uint32_t uid; 213868baf11cSdamien 213968baf11cSdamien if (k->k_flags & IEEE80211_KEY_GROUP) 214068baf11cSdamien uid = htole32(k->k_id); 214168baf11cSdamien else 214268baf11cSdamien uid = htole32(OTUS_UID(cmd->associd)); 214368baf11cSdamien (void)otus_cmd(sc, AR_CMD_DKEY, &uid, sizeof uid, NULL); 214468baf11cSdamien } 214568baf11cSdamien #endif 214668baf11cSdamien 214768baf11cSdamien void 214868baf11cSdamien otus_calibrate_to(void *arg) 214968baf11cSdamien { 215068baf11cSdamien struct otus_softc *sc = arg; 215168baf11cSdamien struct ieee80211com *ic = &sc->sc_ic; 215268baf11cSdamien struct ieee80211_node *ni; 215368baf11cSdamien int s; 215468baf11cSdamien 215568baf11cSdamien s = splnet(); 215668baf11cSdamien ni = ic->ic_bss; 215768baf11cSdamien ieee80211_amrr_choose(&sc->amrr, ni, &((struct otus_node *)ni)->amn); 215868baf11cSdamien splx(s); 215968baf11cSdamien 216068baf11cSdamien timeout_add_sec(&sc->calib_to, 1); 216168baf11cSdamien } 216268baf11cSdamien 216368baf11cSdamien int 216468baf11cSdamien otus_set_bssid(struct otus_softc *sc, const uint8_t *bssid) 216568baf11cSdamien { 21666e30e88dSdamien otus_write(sc, AR_MAC_REG_BSSID_L, 216768baf11cSdamien bssid[0] | bssid[1] << 8 | bssid[2] << 16 | bssid[3] << 24); 21686e30e88dSdamien otus_write(sc, AR_MAC_REG_BSSID_H, 216968baf11cSdamien bssid[4] | bssid[5] << 8); 217068baf11cSdamien return otus_write_barrier(sc); 217168baf11cSdamien } 217268baf11cSdamien 217368baf11cSdamien int 217468baf11cSdamien otus_set_macaddr(struct otus_softc *sc, const uint8_t *addr) 217568baf11cSdamien { 217668baf11cSdamien otus_write(sc, AR_MAC_REG_MAC_ADDR_L, 217768baf11cSdamien addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24); 217868baf11cSdamien otus_write(sc, AR_MAC_REG_MAC_ADDR_H, 217968baf11cSdamien addr[4] | addr[5] << 8); 218068baf11cSdamien return otus_write_barrier(sc); 218168baf11cSdamien } 218268baf11cSdamien 218368baf11cSdamien /* Default single-LED. */ 218468baf11cSdamien void 218568baf11cSdamien otus_led_newstate_type1(struct otus_softc *sc) 218668baf11cSdamien { 218768baf11cSdamien /* TBD */ 218868baf11cSdamien } 218968baf11cSdamien 219068baf11cSdamien /* NETGEAR, dual-LED. */ 219168baf11cSdamien void 219268baf11cSdamien otus_led_newstate_type2(struct otus_softc *sc) 219368baf11cSdamien { 219468baf11cSdamien /* TBD */ 219568baf11cSdamien } 219668baf11cSdamien 219768baf11cSdamien /* NETGEAR, single-LED/3 colors (blue, red, purple.) */ 219868baf11cSdamien void 219968baf11cSdamien otus_led_newstate_type3(struct otus_softc *sc) 220068baf11cSdamien { 220168baf11cSdamien struct ieee80211com *ic = &sc->sc_ic; 220268baf11cSdamien uint32_t state = sc->led_state; 220368baf11cSdamien 220468baf11cSdamien if (ic->ic_state == IEEE80211_S_INIT) { 220568baf11cSdamien state = 0; /* LED off. */ 220668baf11cSdamien } else if (ic->ic_state == IEEE80211_S_RUN) { 220768baf11cSdamien /* Associated, LED always on. */ 220868baf11cSdamien if (IEEE80211_IS_CHAN_2GHZ(sc->sc_curchan)) 220968baf11cSdamien state = AR_LED0_ON; /* 2GHz=>Red. */ 221068baf11cSdamien else 221168baf11cSdamien state = AR_LED1_ON; /* 5GHz=>Blue. */ 221268baf11cSdamien } else { 221368baf11cSdamien /* Scanning, blink LED. */ 221468baf11cSdamien state ^= AR_LED0_ON | AR_LED1_ON; 221568baf11cSdamien if (IEEE80211_IS_CHAN_2GHZ(sc->sc_curchan)) 221668baf11cSdamien state &= ~AR_LED1_ON; 221768baf11cSdamien else 221868baf11cSdamien state &= ~AR_LED0_ON; 221968baf11cSdamien } 222068baf11cSdamien if (state != sc->led_state) { 222168baf11cSdamien otus_write(sc, 0x1d0104, state); 222268baf11cSdamien if (otus_write_barrier(sc) == 0) 222368baf11cSdamien sc->led_state = state; 222468baf11cSdamien } 222568baf11cSdamien } 222668baf11cSdamien 222768baf11cSdamien int 222868baf11cSdamien otus_init(struct ifnet *ifp) 222968baf11cSdamien { 223068baf11cSdamien struct otus_softc *sc = ifp->if_softc; 223168baf11cSdamien struct ieee80211com *ic = &sc->sc_ic; 223268baf11cSdamien int error; 223368baf11cSdamien 223468baf11cSdamien /* Init host command ring. */ 223568baf11cSdamien sc->cmdq.cur = sc->cmdq.next = sc->cmdq.queued = 0; 223668baf11cSdamien 223768baf11cSdamien if ((error = otus_init_mac(sc)) != 0) { 223868baf11cSdamien printf("%s: could not initialize MAC\n", sc->sc_dev.dv_xname); 223968baf11cSdamien return error; 224068baf11cSdamien } 224168baf11cSdamien 224268baf11cSdamien IEEE80211_ADDR_COPY(ic->ic_myaddr, LLADDR(ifp->if_sadl)); 224368baf11cSdamien (void)otus_set_macaddr(sc, ic->ic_myaddr); 224468baf11cSdamien 224568baf11cSdamien switch (ic->ic_opmode) { 224668baf11cSdamien #ifdef notyet 224768baf11cSdamien #ifndef IEEE80211_STA_ONLY 224868baf11cSdamien case IEEE80211_M_HOSTAP: 224968baf11cSdamien otus_write(sc, 0x1c3700, 0x0f0000a1); 225068baf11cSdamien otus_write(sc, 0x1c3c40, 0x1); 225168baf11cSdamien break; 225268baf11cSdamien case IEEE80211_M_IBSS: 225368baf11cSdamien otus_write(sc, 0x1c3700, 0x0f000000); 225468baf11cSdamien otus_write(sc, 0x1c3c40, 0x1); 225568baf11cSdamien break; 225668baf11cSdamien #endif 225768baf11cSdamien #endif 225868baf11cSdamien case IEEE80211_M_STA: 225968baf11cSdamien otus_write(sc, 0x1c3700, 0x0f000002); 226068baf11cSdamien otus_write(sc, 0x1c3c40, 0x1); 226168baf11cSdamien break; 226268baf11cSdamien default: 226368baf11cSdamien break; 226468baf11cSdamien } 226568baf11cSdamien otus_write(sc, AR_MAC_REG_SNIFFER, 226668baf11cSdamien (ic->ic_opmode == IEEE80211_M_MONITOR) ? 0x2000001 : 0x2000000); 226768baf11cSdamien (void)otus_write_barrier(sc); 226868baf11cSdamien 22696e30e88dSdamien sc->bb_reset = 1; /* Force cold reset. */ 227068baf11cSdamien ic->ic_bss->ni_chan = ic->ic_ibss_chan; 22716e30e88dSdamien if ((error = otus_set_chan(sc, ic->ic_ibss_chan, 0)) != 0) { 227268baf11cSdamien printf("%s: could not set channel\n", sc->sc_dev.dv_xname); 227368baf11cSdamien return error; 227468baf11cSdamien } 227568baf11cSdamien 227668baf11cSdamien /* Start Rx. */ 227768baf11cSdamien otus_write(sc, 0x1c3d30, 0x100); 2278d7d7f774Sdamien (void)otus_write_barrier(sc); 227968baf11cSdamien 228068baf11cSdamien ifp->if_flags &= ~IFF_OACTIVE; 228168baf11cSdamien ifp->if_flags |= IFF_RUNNING; 228268baf11cSdamien 228368baf11cSdamien if (ic->ic_opmode == IEEE80211_M_MONITOR) 228468baf11cSdamien ieee80211_new_state(ic, IEEE80211_S_RUN, -1); 228568baf11cSdamien else 228668baf11cSdamien ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); 228768baf11cSdamien 228868baf11cSdamien return 0; 228968baf11cSdamien } 229068baf11cSdamien 229168baf11cSdamien void 229268baf11cSdamien otus_stop(struct ifnet *ifp) 229368baf11cSdamien { 229468baf11cSdamien struct otus_softc *sc = ifp->if_softc; 229568baf11cSdamien struct ieee80211com *ic = &sc->sc_ic; 229668baf11cSdamien int s; 229768baf11cSdamien 229868baf11cSdamien sc->sc_tx_timer = 0; 229968baf11cSdamien ifp->if_timer = 0; 230068baf11cSdamien ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); 230168baf11cSdamien 230268baf11cSdamien timeout_del(&sc->scan_to); 230368baf11cSdamien timeout_del(&sc->calib_to); 230468baf11cSdamien 230568baf11cSdamien s = splusb(); 230668baf11cSdamien ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 230768baf11cSdamien /* Wait for all queued asynchronous commands to complete. */ 230868baf11cSdamien while (sc->cmdq.queued > 0) 230968baf11cSdamien tsleep(&sc->cmdq, 0, "cmdq", 0); 231068baf11cSdamien splx(s); 231168baf11cSdamien 231268baf11cSdamien /* Stop Rx. */ 231368baf11cSdamien otus_write(sc, 0x1c3d30, 0); 231468baf11cSdamien (void)otus_write_barrier(sc); 231568baf11cSdamien 231668baf11cSdamien sc->tx_queued = 0; 231768baf11cSdamien } 2318