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