From a9fcb51fbb8d2c5b47a35bcae5b4d5fb771c5985 Mon Sep 17 00:00:00 2001 From: Adrian Chadd Date: Sat, 26 Sep 2015 07:08:35 +0000 Subject: [PATCH] Add an initial driver for the AR9170 series draft-11n hardware from Atheros. Thanks to OpenBSD for providing a driver based on the original Atheros open source driver circa 2008. This uses the early, pre-carl9170 atheros provided firmware. It only supports 11bg at the moment. I've not tested it with 11a (and so the TX rate control logic may be slightly wrong!) so if you do have the dual-band version of this hardware please do let me know. Tested: * AR9170, TP-Link WN821N 2GHz. TODO: * Hook this up to a non-module build. --- sys/contrib/dev/otus/otus-init | Bin 0 -> 83968 bytes sys/contrib/dev/otus/otus-license | 47 + sys/contrib/dev/otus/otus-main | Bin 0 -> 3508 bytes sys/dev/otus/if_otus.c | 3099 +++++++++++++++++++++++ sys/dev/otus/if_otusreg.h | 1025 ++++++++ sys/modules/Makefile | 2 + sys/modules/otus/Makefile | 10 + sys/modules/otusfw/Makefile | 5 + sys/modules/otusfw/otusfw_init/Makefile | 11 + sys/modules/otusfw/otusfw_main/Makefile | 11 + 10 files changed, 4210 insertions(+) create mode 100644 sys/contrib/dev/otus/otus-init create mode 100644 sys/contrib/dev/otus/otus-license create mode 100644 sys/contrib/dev/otus/otus-main create mode 100644 sys/dev/otus/if_otus.c create mode 100644 sys/dev/otus/if_otusreg.h create mode 100644 sys/modules/otus/Makefile create mode 100644 sys/modules/otusfw/Makefile create mode 100644 sys/modules/otusfw/otusfw_init/Makefile create mode 100644 sys/modules/otusfw/otusfw_main/Makefile diff --git a/sys/contrib/dev/otus/otus-init b/sys/contrib/dev/otus/otus-init new file mode 100644 index 0000000000000000000000000000000000000000..d422bdbdf2284b5302a455c327b7b85842951b77 GIT binary patch literal 83968 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%0AnlY2H|7a3W(SQbJg8EBy<0n9!?*ICg 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 + * Copyright (c) 2015 Adrian Chadd + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Driver for Atheros AR9001U chipset. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include "usbdevs.h" + +#define USB_DEBUG_VAR otus_debug +#include + +#include "if_otusreg.h" + +static int otus_debug = 0; +static SYSCTL_NODE(_hw_usb, OID_AUTO, otus, CTLFLAG_RW, 0, "USB otus"); +SYSCTL_INT(_hw_usb_otus, OID_AUTO, debug, CTLFLAG_RWTUN, &otus_debug, 0, + "Debug level"); +#define OTUS_DEBUG_XMIT 0x00000001 +#define OTUS_DEBUG_RECV 0x00000002 +#define OTUS_DEBUG_TXDONE 0x00000004 +#define OTUS_DEBUG_RXDONE 0x00000008 +#define OTUS_DEBUG_CMD 0x00000010 +#define OTUS_DEBUG_CMDDONE 0x00000020 +#define OTUS_DEBUG_RESET 0x00000040 +#define OTUS_DEBUG_STATE 0x00000080 +#define OTUS_DEBUG_CMDNOTIFY 0x00000100 +#define OTUS_DEBUG_REGIO 0x00000200 +#define OTUS_DEBUG_IRQ 0x00000400 +#define OTUS_DEBUG_TXCOMP 0x00000800 +#define OTUS_DEBUG_ANY 0xffffffff + +#define OTUS_DPRINTF(sc, dm, ...) \ + do { \ + if ((dm == OTUS_DEBUG_ANY) || (dm & otus_debug)) \ + device_printf(sc->sc_dev, __VA_ARGS__); \ + } while (0) + +#define OTUS_DEV(v, p) { USB_VPI(v, p, 0) } +static const STRUCT_USB_HOST_ID otus_devs[] = { + OTUS_DEV(USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_WN7512), + OTUS_DEV(USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_3CRUSBN275), + OTUS_DEV(USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_TG121N), + OTUS_DEV(USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_AR9170), + OTUS_DEV(USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_WN612), + OTUS_DEV(USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_WN821NV2), + OTUS_DEV(USB_VENDOR_AVM, USB_PRODUCT_AVM_FRITZWLAN), + OTUS_DEV(USB_VENDOR_CACE, USB_PRODUCT_CACE_AIRPCAPNX), + OTUS_DEV(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWA130D1), + OTUS_DEV(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWA160A1), + OTUS_DEV(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWA160A2), + OTUS_DEV(USB_VENDOR_IODATA, USB_PRODUCT_IODATA_WNGDNUS2), + OTUS_DEV(USB_VENDOR_NEC, USB_PRODUCT_NEC_WL300NUG), + OTUS_DEV(USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_WN111V2), + OTUS_DEV(USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_WNA1000), + OTUS_DEV(USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_WNDA3100), + OTUS_DEV(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GW_US300), + OTUS_DEV(USB_VENDOR_WISTRONNEWEB, USB_PRODUCT_WISTRONNEWEB_O8494), + OTUS_DEV(USB_VENDOR_WISTRONNEWEB, USB_PRODUCT_WISTRONNEWEB_WNC0600), + OTUS_DEV(USB_VENDOR_ZCOM, USB_PRODUCT_ZCOM_UB81), + OTUS_DEV(USB_VENDOR_ZCOM, USB_PRODUCT_ZCOM_UB82), + OTUS_DEV(USB_VENDOR_ZYDAS, USB_PRODUCT_ZYDAS_ZD1221), + OTUS_DEV(USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_NWD271N), +}; + +static device_probe_t otus_match; +static device_attach_t otus_attach; +static device_detach_t otus_detach; + +static int otus_attachhook(struct otus_softc *); +void otus_get_chanlist(struct otus_softc *); +int otus_load_firmware(struct otus_softc *, const char *, + uint32_t); +int otus_open_pipes(struct otus_softc *); +void otus_close_pipes(struct otus_softc *); + +static int otus_alloc_tx_cmd_list(struct otus_softc *); +static void otus_free_tx_cmd_list(struct otus_softc *); + +static int otus_alloc_rx_list(struct otus_softc *); +static void otus_free_rx_list(struct otus_softc *); +static int otus_alloc_tx_list(struct otus_softc *); +static void otus_free_tx_list(struct otus_softc *); +static void otus_free_list(struct otus_softc *, struct otus_data [], int); +static struct otus_data *_otus_getbuf(struct otus_softc *); +static struct otus_data *otus_getbuf(struct otus_softc *); +static void otus_freebuf(struct otus_softc *, struct otus_data *); + +static struct otus_tx_cmd *_otus_get_txcmd(struct otus_softc *); +static struct otus_tx_cmd *otus_get_txcmd(struct otus_softc *); +static void otus_free_txcmd(struct otus_softc *, struct otus_tx_cmd *); + +void otus_next_scan(void *, int); +static void otus_tx_task(void *, int pending); +static void otus_wme_update_task(void *, int pending); +void otus_do_async(struct otus_softc *, + void (*)(struct otus_softc *, void *), void *, int); +int otus_newstate(struct ieee80211vap *, enum ieee80211_state, + int); +int otus_cmd(struct otus_softc *, uint8_t, const void *, int, + void *); +void otus_write(struct otus_softc *, uint32_t, uint32_t); +int otus_write_barrier(struct otus_softc *); +struct ieee80211_node *otus_node_alloc(struct ieee80211com *); +int otus_media_change(struct ifnet *); +int otus_read_eeprom(struct otus_softc *); +void otus_newassoc(struct ieee80211_node *, int); +void otus_cmd_rxeof(struct otus_softc *, uint8_t *, int); +void otus_sub_rxeof(struct otus_softc *, uint8_t *, int, + struct mbufq *); +static int otus_tx(struct otus_softc *, struct ieee80211_node *, + struct mbuf *, struct otus_data *); +int otus_ioctl(struct ifnet *, u_long, caddr_t); +int otus_set_multi(struct otus_softc *); +static void otus_updateedca(struct otus_softc *sc); +static void otus_updateslot(struct otus_softc *sc); +int otus_init_mac(struct otus_softc *); +uint32_t otus_phy_get_def(struct otus_softc *, uint32_t); +int otus_set_board_values(struct otus_softc *, + struct ieee80211_channel *); +int otus_program_phy(struct otus_softc *, + struct ieee80211_channel *); +int otus_set_rf_bank4(struct otus_softc *, + struct ieee80211_channel *); +void otus_get_delta_slope(uint32_t, uint32_t *, uint32_t *); +static int otus_set_chan(struct otus_softc *, struct ieee80211_channel *, + int); +int otus_set_key(struct ieee80211com *, struct ieee80211_node *, + struct ieee80211_key *); +void otus_set_key_cb(struct otus_softc *, void *); +void otus_delete_key(struct ieee80211com *, struct ieee80211_node *, + struct ieee80211_key *); +void otus_delete_key_cb(struct otus_softc *, void *); +void otus_calibrate_to(void *, int); +int otus_set_bssid(struct otus_softc *, const uint8_t *); +int otus_set_macaddr(struct otus_softc *, const uint8_t *); +void otus_led_newstate_type1(struct otus_softc *); +void otus_led_newstate_type2(struct otus_softc *); +void otus_led_newstate_type3(struct otus_softc *); +int otus_init(struct otus_softc *sc); +void otus_stop(struct otus_softc *sc); + +static device_method_t otus_methods[] = { + DEVMETHOD(device_probe, otus_match), + DEVMETHOD(device_attach, otus_attach), + DEVMETHOD(device_detach, otus_detach), + + DEVMETHOD_END +}; + +static driver_t otus_driver = { + .name = "otus", + .methods = otus_methods, + .size = sizeof(struct otus_softc) +}; + +static devclass_t otus_devclass; + +DRIVER_MODULE(otus, uhub, otus_driver, otus_devclass, NULL, 0); +MODULE_DEPEND(otus, wlan, 1, 1, 1); +MODULE_DEPEND(otus, usb, 1, 1, 1); +MODULE_DEPEND(otus, firmware, 1, 1, 1); +MODULE_VERSION(otus, 1); + +static usb_callback_t otus_bulk_tx_callback; +static usb_callback_t otus_bulk_rx_callback; +static usb_callback_t otus_bulk_irq_callback; +static usb_callback_t otus_bulk_cmd_callback; + +static const struct usb_config otus_config[OTUS_N_XFER] = { + [OTUS_BULK_TX] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .bufsize = 0x200, + .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .callback = otus_bulk_tx_callback, + .timeout = 5000, /* ms */ + }, + [OTUS_BULK_RX] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .bufsize = OTUS_RXBUFSZ, + .flags = { .ext_buffer = 1, .pipe_bof = 1,.short_xfer_ok = 1,}, + .callback = otus_bulk_rx_callback, + }, + [OTUS_BULK_IRQ] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .bufsize = OTUS_MAX_CTRLSZ, + .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .callback = otus_bulk_irq_callback, + }, + [OTUS_BULK_CMD] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .bufsize = OTUS_MAX_CTRLSZ, + .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .callback = otus_bulk_cmd_callback, + .timeout = 5000, /* ms */ + }, +}; + +static int +otus_match(device_t self) +{ + struct usb_attach_arg *uaa = device_get_ivars(self); + + if (uaa->usb_mode != USB_MODE_HOST || + uaa->info.bIfaceIndex != 0 || + uaa->info.bConfigIndex != 0) + return (ENXIO); + + return (usbd_lookup_id_by_uaa(otus_devs, sizeof(otus_devs), uaa)); +} + +static int +otus_attach(device_t self) +{ + struct usb_attach_arg *uaa = device_get_ivars(self); + struct otus_softc *sc = device_get_softc(self); + int error; + uint8_t iface_index; + + device_set_usb_desc(self); + sc->sc_udev = uaa->device; + sc->sc_dev = self; + + mtx_init(&sc->sc_mtx, device_get_nameunit(self), MTX_NETWORK_LOCK, + MTX_DEF); + + TIMEOUT_TASK_INIT(taskqueue_thread, &sc->scan_to, 0, otus_next_scan, sc); + TIMEOUT_TASK_INIT(taskqueue_thread, &sc->calib_to, 0, otus_calibrate_to, sc); + TASK_INIT(&sc->tx_task, 0, otus_tx_task, sc); + TASK_INIT(&sc->wme_update_task, 0, otus_wme_update_task, sc); + mbufq_init(&sc->sc_snd, ifqmaxlen); + + iface_index = 0; + error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, + otus_config, OTUS_N_XFER, sc, &sc->sc_mtx); + if (error) { + device_printf(sc->sc_dev, + "could not allocate USB transfers, err=%s\n", + usbd_errstr(error)); + goto fail_usb; + } + + if ((error = otus_open_pipes(sc)) != 0) { + device_printf(sc->sc_dev, "%s: could not open pipes\n", + __func__); + goto fail; + } + + /* XXX check return status; fail out if appropriate */ + if (otus_attachhook(sc) != 0) + goto fail; + + return (0); + +fail: + otus_close_pipes(sc); +fail_usb: + mtx_destroy(&sc->sc_mtx); + return (ENXIO); +} + +static int +otus_detach(device_t self) +{ + struct otus_softc *sc = device_get_softc(self); + struct ieee80211com *ic = &sc->sc_ic; + + otus_stop(sc); + + usbd_transfer_unsetup(sc->sc_xfer, OTUS_N_XFER); + + taskqueue_drain_timeout(taskqueue_thread, &sc->scan_to); + taskqueue_drain_timeout(taskqueue_thread, &sc->calib_to); + taskqueue_drain(taskqueue_thread, &sc->tx_task); + taskqueue_drain(taskqueue_thread, &sc->wme_update_task); + +#if 0 + /* Wait for all queued asynchronous commands to complete. */ + usb_rem_wait_task(sc->sc_udev, &sc->sc_task); + + usbd_ref_wait(sc->sc_udev); +#endif + + ieee80211_ifdetach(ic); + otus_close_pipes(sc); + mtx_destroy(&sc->sc_mtx); + return 0; +} + +static void +otus_delay_ms(struct otus_softc *sc, int ms) +{ + + DELAY(1000 * ms); +} + +static struct ieee80211vap * +otus_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, + enum ieee80211_opmode opmode, int flags, + const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + struct otus_vap *uvp; + struct ieee80211vap *vap; + + if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ + return (NULL); + + uvp = malloc(sizeof(struct otus_vap), M_80211_VAP, M_WAITOK | M_ZERO); + vap = &uvp->vap; + + if (ieee80211_vap_setup(ic, vap, name, unit, opmode, + flags, bssid) != 0) { + /* out of memory */ + free(uvp, M_80211_VAP); + return (NULL); + } + + /* override state transition machine */ + uvp->newstate = vap->iv_newstate; + vap->iv_newstate = otus_newstate; + + /* XXX TODO: double-check */ + vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_16; + vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_32K; + + ieee80211_ratectl_init(vap); + + /* complete setup */ + ieee80211_vap_attach(vap, ieee80211_media_change, + ieee80211_media_status, mac); + ic->ic_opmode = opmode; + + return (vap); +} + +static void +otus_vap_delete(struct ieee80211vap *vap) +{ + struct otus_vap *uvp = OTUS_VAP(vap); + + ieee80211_ratectl_deinit(vap); + ieee80211_vap_detach(vap); + free(uvp, M_80211_VAP); +} + +static void +otus_parent(struct ieee80211com *ic) +{ + struct otus_softc *sc = ic->ic_softc; + int startall = 0; + + if (ic->ic_nrunning > 0) { + if (!sc->sc_running) { + otus_init(sc); + startall = 1; + } else { + (void) otus_set_multi(sc); + } + } else if (sc->sc_running) + otus_stop(sc); + + if (startall) + ieee80211_start_all(ic); +} + +static void +otus_drain_mbufq(struct otus_softc *sc) +{ + struct mbuf *m; + struct ieee80211_node *ni; + + OTUS_LOCK_ASSERT(sc); + while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { + ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; + m->m_pkthdr.rcvif = NULL; + ieee80211_free_node(ni); + m_freem(m); + } +} + +static void +otus_tx_start(struct otus_softc *sc) +{ + + taskqueue_enqueue(taskqueue_thread, &sc->tx_task); +} + +static int +otus_transmit(struct ieee80211com *ic, struct mbuf *m) +{ + struct otus_softc *sc = ic->ic_softc; + int error; + + OTUS_LOCK(sc); + if (! sc->sc_running) { + OTUS_UNLOCK(sc); + return (ENXIO); + } + + /* XXX TODO: handle fragments */ + error = mbufq_enqueue(&sc->sc_snd, m); + if (error) { + OTUS_DPRINTF(sc, OTUS_DEBUG_XMIT, + "%s: mbufq_enqueue failed: %d\n", + __func__, + error); + OTUS_UNLOCK(sc); + return (error); + } + OTUS_UNLOCK(sc); + + /* Kick TX */ + otus_tx_start(sc); + + return (0); +} + +static void +_otus_start(struct otus_softc *sc) +{ + struct ieee80211_node *ni; + struct otus_data *bf; + struct mbuf *m; + + OTUS_LOCK_ASSERT(sc); + + while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { + bf = otus_getbuf(sc); + if (bf == NULL) { + OTUS_DPRINTF(sc, OTUS_DEBUG_XMIT, + "%s: failed to get buffer\n", __func__); + mbufq_prepend(&sc->sc_snd, m); + break; + } + + ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; + m->m_pkthdr.rcvif = NULL; + + if (otus_tx(sc, ni, m, bf) != 0) { + OTUS_DPRINTF(sc, OTUS_DEBUG_XMIT, + "%s: failed to transmit\n", __func__); + if_inc_counter(ni->ni_vap->iv_ifp, + IFCOUNTER_OERRORS, 1); + otus_freebuf(sc, bf); + ieee80211_free_node(ni); + m_freem(m); + break; + } + } +} + +static void +otus_tx_task(void *arg, int pending) +{ + struct otus_softc *sc = arg; + + OTUS_LOCK(sc); + _otus_start(sc); + OTUS_UNLOCK(sc); +} + +static int +otus_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, + const struct ieee80211_bpf_params *params) +{ + struct ieee80211com *ic= ni->ni_ic; + struct otus_softc *sc = ic->ic_softc; + struct otus_data *bf = NULL; + int error = 0; + + /* Don't transmit if we're not running */ + OTUS_LOCK(sc); + if (! sc->sc_running) { + error = ENETDOWN; + goto error; + } + + bf = otus_getbuf(sc); + if (bf == NULL) { + error = ENOBUFS; + goto error; + } + + /* + * XXX TODO: support TX bpf params + */ + if (otus_tx(sc, ni, m, bf) != 0) { + error = EIO; + goto error; + } + + OTUS_UNLOCK(sc); + return (0); +error: + if (bf) + otus_freebuf(sc, bf); + OTUS_UNLOCK(sc); + ieee80211_free_node(ni); + m_freem(m); + return (ENXIO); +} + +static void +otus_update_chw(struct ieee80211com *ic) +{ + + printf("%s: TODO\n", __func__); +} + +static void +otus_set_channel(struct ieee80211com *ic) +{ + struct otus_softc *sc = ic->ic_softc; + OTUS_DPRINTF(sc, OTUS_DEBUG_RESET, "%s: set channel: %d\n", + __func__, + ic->ic_curchan->ic_freq); + + OTUS_LOCK(sc); + (void) otus_set_chan(sc, ic->ic_curchan, 0); + OTUS_UNLOCK(sc); +} + +static void +otus_wme_update_task(void *arg, int pending) +{ + struct otus_softc *sc = arg; + + OTUS_LOCK(sc); + /* + * XXX TODO: take temporary copy of EDCA information + * when scheduling this so we have a more time-correct view + * of things. + */ + otus_updateedca(sc); + OTUS_UNLOCK(sc); +} + +static void +otus_wme_schedule_update(struct otus_softc *sc) +{ + + taskqueue_enqueue(taskqueue_thread, &sc->wme_update_task); +} + +/* + * This is called by net80211 in RX packet context, so we + * can't sleep here. + * + * TODO: have net80211 schedule an update itself for its + * own internal taskqueue. + */ +static int +otus_wme_update(struct ieee80211com *ic) +{ + struct otus_softc *sc = ic->ic_softc; + + otus_wme_schedule_update(sc); + return (0); +} + +static int +otus_ampdu_enable(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) +{ + + /* For now, no A-MPDU TX support in the driver */ + return (0); +} + +static void +otus_scan_start(struct ieee80211com *ic) +{ + +// printf("%s: TODO\n", __func__); +} + +static void +otus_scan_end(struct ieee80211com *ic) +{ + +// printf("%s: TODO\n", __func__); +} + +static void +otus_update_mcast(struct ieee80211com *ic) +{ + struct otus_softc *sc = ic->ic_softc; + + (void) otus_set_multi(sc); +} + +static int +otus_attachhook(struct otus_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + usb_device_request_t req; + uint32_t in, out; + int error; + uint8_t bands; + + /* Not locked */ + error = otus_load_firmware(sc, "otusfw_init", AR_FW_INIT_ADDR); + if (error != 0) { + device_printf(sc->sc_dev, "%s: could not load %s firmware\n", + __func__, "init"); + return (ENXIO); + } + + /* XXX not locked? */ + otus_delay_ms(sc, 1000); + + /* Not locked */ + error = otus_load_firmware(sc, "otusfw_main", AR_FW_MAIN_ADDR); + if (error != 0) { + device_printf(sc->sc_dev, "%s: could not load %s firmware\n", + __func__, "main"); + return (ENXIO); + } + + OTUS_LOCK(sc); + + /* Tell device that firmware transfer is complete. */ + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = AR_FW_DOWNLOAD_COMPLETE; + USETW(req.wValue, 0); + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + if (usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, &req, NULL, + 0, NULL, 250) != 0) { + OTUS_UNLOCK(sc); + device_printf(sc->sc_dev, + "%s: firmware initialization failed\n", + __func__); + return (ENXIO); + } + + /* Send an ECHO command to check that everything is settled. */ + in = 0xbadc0ffe; + if (otus_cmd(sc, AR_CMD_ECHO, &in, sizeof in, &out) != 0) { + OTUS_UNLOCK(sc); + device_printf(sc->sc_dev, + "%s: echo command failed\n", __func__); + return (ENXIO); + } + if (in != out) { + OTUS_UNLOCK(sc); + device_printf(sc->sc_dev, + "%s: echo reply mismatch: 0x%08x!=0x%08x\n", + __func__, in, out); + return (ENXIO); + } + + /* Read entire EEPROM. */ + if (otus_read_eeprom(sc) != 0) { + OTUS_UNLOCK(sc); + device_printf(sc->sc_dev, + "%s: could not read EEPROM\n", + __func__); + return (ENXIO); + } + + OTUS_UNLOCK(sc); + + sc->txmask = sc->eeprom.baseEepHeader.txMask; + sc->rxmask = sc->eeprom.baseEepHeader.rxMask; + sc->capflags = sc->eeprom.baseEepHeader.opCapFlags; + IEEE80211_ADDR_COPY(ic->ic_macaddr, sc->eeprom.baseEepHeader.macAddr); + sc->sc_led_newstate = otus_led_newstate_type3; /* XXX */ + + device_printf(sc->sc_dev, + "MAC/BBP AR9170, RF AR%X, MIMO %dT%dR, address %s\n", + (sc->capflags & AR5416_OPFLAGS_11A) ? + 0x9104 : ((sc->txmask == 0x5) ? 0x9102 : 0x9101), + (sc->txmask == 0x5) ? 2 : 1, (sc->rxmask == 0x5) ? 2 : 1, + ether_sprintf(ic->ic_macaddr)); + + ic->ic_softc = sc; + ic->ic_name = device_get_nameunit(sc->sc_dev); + ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ + ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ + + /* Set device capabilities. */ + ic->ic_caps = + IEEE80211_C_STA | /* station mode */ +#if 0 + IEEE80211_C_BGSCAN | /* Background scan. */ +#endif + IEEE80211_C_SHPREAMBLE | /* Short preamble supported. */ + IEEE80211_C_WME | /* WME/QoS */ + IEEE80211_C_SHSLOT | /* Short slot time supported. */ + IEEE80211_C_FF | /* Atheros fast-frames supported. */ + IEEE80211_C_WPA; /* WPA/RSN. */ + + /* XXX TODO: 11n */ + +#if 0 + if (sc->eeprom.baseEepHeader.opCapFlags & AR5416_OPFLAGS_11G) { + /* Set supported .11b and .11g rates. */ + ic->ic_sup_rates[IEEE80211_MODE_11B] = + ieee80211_std_rateset_11b; + ic->ic_sup_rates[IEEE80211_MODE_11G] = + ieee80211_std_rateset_11g; + } + if (sc->eeprom.baseEepHeader.opCapFlags & AR5416_OPFLAGS_11A) { + /* Set supported .11a rates. */ + ic->ic_sup_rates[IEEE80211_MODE_11A] = + ieee80211_std_rateset_11a; + } +#endif + +#if 0 + /* Build the list of supported channels. */ + otus_get_chanlist(sc); +#else + /* Set supported .11b and .11g rates. */ + bands = 0; + if (sc->eeprom.baseEepHeader.opCapFlags & AR5416_OPFLAGS_11G) { + setbit(&bands, IEEE80211_MODE_11B); + setbit(&bands, IEEE80211_MODE_11G); + } + if (sc->eeprom.baseEepHeader.opCapFlags & AR5416_OPFLAGS_11A) { + setbit(&bands, IEEE80211_MODE_11A); + } +#if 0 + if (sc->sc_ht) + setbit(&bands, IEEE80211_MODE_11NG); +#endif + ieee80211_init_channels(ic, NULL, &bands); +#endif + + ieee80211_ifattach(ic); + ic->ic_raw_xmit = otus_raw_xmit; + ic->ic_scan_start = otus_scan_start; + ic->ic_scan_end = otus_scan_end; + ic->ic_set_channel = otus_set_channel; + ic->ic_vap_create = otus_vap_create; + ic->ic_vap_delete = otus_vap_delete; + ic->ic_update_mcast = otus_update_mcast; + ic->ic_update_promisc = otus_update_mcast; + ic->ic_parent = otus_parent; + ic->ic_transmit = otus_transmit; + ic->ic_update_chw = otus_update_chw; + ic->ic_ampdu_enable = otus_ampdu_enable; + ic->ic_wme.wme_update = otus_wme_update; + ic->ic_newassoc = otus_newassoc; + +#ifdef notyet + ic->ic_set_key = otus_set_key; + ic->ic_delete_key = otus_delete_key; +#endif + + ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr, + sizeof(sc->sc_txtap), OTUS_TX_RADIOTAP_PRESENT, + &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), + OTUS_RX_RADIOTAP_PRESENT); + + return (0); +} + +void +otus_get_chanlist(struct otus_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + uint16_t domain; + uint8_t chan; + int i; + + /* XXX regulatory domain. */ + domain = le16toh(sc->eeprom.baseEepHeader.regDmn[0]); + OTUS_DPRINTF(sc, OTUS_DEBUG_RESET, "regdomain=0x%04x\n", domain); + + if (sc->eeprom.baseEepHeader.opCapFlags & AR5416_OPFLAGS_11G) { + for (i = 0; i < 14; i++) { + chan = ar_chans[i]; + ic->ic_channels[chan].ic_freq = + ieee80211_ieee2mhz(chan, IEEE80211_CHAN_2GHZ); + ic->ic_channels[chan].ic_flags = + IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | + IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ; + } + } + if (sc->eeprom.baseEepHeader.opCapFlags & AR5416_OPFLAGS_11A) { + for (i = 14; i < nitems(ar_chans); i++) { + chan = ar_chans[i]; + ic->ic_channels[chan].ic_freq = + ieee80211_ieee2mhz(chan, IEEE80211_CHAN_5GHZ); + ic->ic_channels[chan].ic_flags = IEEE80211_CHAN_A; + } + } +} + +int +otus_load_firmware(struct otus_softc *sc, const char *name, uint32_t addr) +{ + usb_device_request_t req; + char *ptr; + const struct firmware *fw; + int mlen, error, size; + + error = 0; + + /* Read firmware image from the filesystem. */ + if ((fw = firmware_get(name)) == NULL) { + device_printf(sc->sc_dev, + "%s: failed loadfirmware of file %s\n", __func__, name); + return (ENXIO); + } + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = AR_FW_DOWNLOAD; + USETW(req.wIndex, 0); + + OTUS_LOCK(sc); + + /* XXX const */ + ptr = __DECONST(char *, fw->data); + size = fw->datasize; + addr >>= 8; + while (size > 0) { + mlen = MIN(size, 4096); + + USETW(req.wValue, addr); + USETW(req.wLength, mlen); + if (usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, + &req, ptr, 0, NULL, 250) != 0) { + error = EIO; + break; + } + addr += mlen >> 8; + ptr += mlen; + size -= mlen; + } + + OTUS_UNLOCK(sc); + + firmware_put(fw, FIRMWARE_UNLOAD); + if (error != 0) + device_printf(sc->sc_dev, + "%s: %s: error=%d\n", __func__, name, error); + return error; +} + +int +otus_open_pipes(struct otus_softc *sc) +{ +#if 0 + int isize, error; + int i; +#endif + int error; + + OTUS_UNLOCK_ASSERT(sc); + + if ((error = otus_alloc_tx_cmd_list(sc)) != 0) { + device_printf(sc->sc_dev, + "%s: could not allocate command xfer\n", + __func__); + goto fail; + } + + if ((error = otus_alloc_tx_list(sc)) != 0) { + device_printf(sc->sc_dev, "%s: could not allocate Tx xfers\n", + __func__); + goto fail; + } + + if ((error = otus_alloc_rx_list(sc)) != 0) { + device_printf(sc->sc_dev, "%s: could not allocate Rx xfers\n", + __func__); + goto fail; + } + + /* Enable RX transfers; needed for initial firmware messages */ + OTUS_LOCK(sc); + usbd_transfer_start(sc->sc_xfer[OTUS_BULK_RX]); + usbd_transfer_start(sc->sc_xfer[OTUS_BULK_IRQ]); + OTUS_UNLOCK(sc); + return 0; + +fail: otus_close_pipes(sc); + return error; +} + +void +otus_close_pipes(struct otus_softc *sc) +{ + otus_free_tx_cmd_list(sc); + otus_free_tx_list(sc); + otus_free_rx_list(sc); + + usbd_transfer_unsetup(sc->sc_xfer, OTUS_N_XFER); +} + +static void +otus_free_cmd_list(struct otus_softc *sc, struct otus_tx_cmd cmd[], int ndata) +{ + int i; + + /* XXX TODO: someone has to have waken up waiters! */ + for (i = 0; i < ndata; i++) { + struct otus_tx_cmd *dp = &cmd[i]; + + if (dp->buf != NULL) { + free(dp->buf, M_USBDEV); + dp->buf = NULL; + } + } +} + +static int +otus_alloc_cmd_list(struct otus_softc *sc, struct otus_tx_cmd cmd[], + int ndata, int maxsz) +{ + int i, error; + + for (i = 0; i < ndata; i++) { + struct otus_tx_cmd *dp = &cmd[i]; + dp->buf = malloc(maxsz, M_USBDEV, M_NOWAIT); + dp->odata = NULL; + if (dp->buf == NULL) { + device_printf(sc->sc_dev, + "could not allocate buffer\n"); + error = ENOMEM; + goto fail; + } + } + + return (0); +fail: + otus_free_cmd_list(sc, cmd, ndata); + return (error); +} + +static int +otus_alloc_tx_cmd_list(struct otus_softc *sc) +{ + int error, i; + + error = otus_alloc_cmd_list(sc, sc->sc_cmd, OTUS_CMD_LIST_COUNT, + OTUS_MAX_TXCMDSZ); + if (error != 0) + return (error); + + STAILQ_INIT(&sc->sc_cmd_active); + STAILQ_INIT(&sc->sc_cmd_inactive); + STAILQ_INIT(&sc->sc_cmd_pending); + STAILQ_INIT(&sc->sc_cmd_waiting); + + for (i = 0; i < OTUS_CMD_LIST_COUNT; i++) + STAILQ_INSERT_HEAD(&sc->sc_cmd_inactive, &sc->sc_cmd[i], + next_cmd); + + return (0); +} + +static void +otus_free_tx_cmd_list(struct otus_softc *sc) +{ + + /* + * XXX TODO: something needs to wake up any pending/sleeping + * waiters! + */ + STAILQ_INIT(&sc->sc_cmd_active); + STAILQ_INIT(&sc->sc_cmd_inactive); + STAILQ_INIT(&sc->sc_cmd_pending); + STAILQ_INIT(&sc->sc_cmd_waiting); + + otus_free_cmd_list(sc, sc->sc_cmd, OTUS_CMD_LIST_COUNT); +} + +static int +otus_alloc_list(struct otus_softc *sc, struct otus_data data[], + int ndata, int maxsz) +{ + int i, error; + + for (i = 0; i < ndata; i++) { + struct otus_data *dp = &data[i]; + dp->sc = sc; + dp->m = NULL; + dp->buf = malloc(maxsz, M_USBDEV, M_NOWAIT); + if (dp->buf == NULL) { + device_printf(sc->sc_dev, + "could not allocate buffer\n"); + error = ENOMEM; + goto fail; + } + dp->ni = NULL; + } + + return (0); +fail: + otus_free_list(sc, data, ndata); + return (error); +} + +static int +otus_alloc_rx_list(struct otus_softc *sc) +{ + int error, i; + + error = otus_alloc_list(sc, sc->sc_rx, OTUS_RX_LIST_COUNT, + OTUS_RXBUFSZ); + if (error != 0) + return (error); + + STAILQ_INIT(&sc->sc_rx_active); + STAILQ_INIT(&sc->sc_rx_inactive); + + for (i = 0; i < OTUS_RX_LIST_COUNT; i++) + STAILQ_INSERT_HEAD(&sc->sc_rx_inactive, &sc->sc_rx[i], next); + + return (0); +} + +static int +otus_alloc_tx_list(struct otus_softc *sc) +{ + int error, i; + + error = otus_alloc_list(sc, sc->sc_tx, OTUS_TX_LIST_COUNT, + OTUS_TXBUFSZ); + if (error != 0) + return (error); + + STAILQ_INIT(&sc->sc_tx_inactive); + + for (i = 0; i != OTUS_N_XFER; i++) { + STAILQ_INIT(&sc->sc_tx_active[i]); + STAILQ_INIT(&sc->sc_tx_pending[i]); + } + + for (i = 0; i < OTUS_TX_LIST_COUNT; i++) { + STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, &sc->sc_tx[i], next); + } + + return (0); +} + +static void +otus_free_tx_list(struct otus_softc *sc) +{ + int i; + + /* prevent further allocations from TX list(s) */ + STAILQ_INIT(&sc->sc_tx_inactive); + + for (i = 0; i != OTUS_N_XFER; i++) { + STAILQ_INIT(&sc->sc_tx_active[i]); + STAILQ_INIT(&sc->sc_tx_pending[i]); + } + + otus_free_list(sc, sc->sc_tx, OTUS_TX_LIST_COUNT); +} + +static void +otus_free_rx_list(struct otus_softc *sc) +{ + /* prevent further allocations from RX list(s) */ + STAILQ_INIT(&sc->sc_rx_inactive); + STAILQ_INIT(&sc->sc_rx_active); + + otus_free_list(sc, sc->sc_rx, OTUS_RX_LIST_COUNT); +} + +static void +otus_free_list(struct otus_softc *sc, struct otus_data data[], int ndata) +{ + int i; + + for (i = 0; i < ndata; i++) { + struct otus_data *dp = &data[i]; + + if (dp->buf != NULL) { + free(dp->buf, M_USBDEV); + dp->buf = NULL; + } + if (dp->ni != NULL) { + ieee80211_free_node(dp->ni); + dp->ni = NULL; + } + } +} + +static struct otus_data * +_otus_getbuf(struct otus_softc *sc) +{ + struct otus_data *bf; + + bf = STAILQ_FIRST(&sc->sc_tx_inactive); + if (bf != NULL) + STAILQ_REMOVE_HEAD(&sc->sc_tx_inactive, next); + else + bf = NULL; + return (bf); +} + +static struct otus_data * +otus_getbuf(struct otus_softc *sc) +{ + struct otus_data *bf; + + OTUS_LOCK_ASSERT(sc); + + bf = _otus_getbuf(sc); + return (bf); +} + +static void +otus_freebuf(struct otus_softc *sc, struct otus_data *bf) +{ + + OTUS_LOCK_ASSERT(sc); + STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, bf, next); +} + +static struct otus_tx_cmd * +_otus_get_txcmd(struct otus_softc *sc) +{ + struct otus_tx_cmd *bf; + + bf = STAILQ_FIRST(&sc->sc_cmd_inactive); + if (bf != NULL) + STAILQ_REMOVE_HEAD(&sc->sc_cmd_inactive, next_cmd); + else + bf = NULL; + return (bf); +} + +static struct otus_tx_cmd * +otus_get_txcmd(struct otus_softc *sc) +{ + struct otus_tx_cmd *bf; + + OTUS_LOCK_ASSERT(sc); + + bf = _otus_get_txcmd(sc); + if (bf == NULL) { + device_printf(sc->sc_dev, "%s: no tx cmd buffers\n", + __func__); + } + return (bf); +} + +static void +otus_free_txcmd(struct otus_softc *sc, struct otus_tx_cmd *bf) +{ + + OTUS_LOCK_ASSERT(sc); + STAILQ_INSERT_TAIL(&sc->sc_cmd_inactive, bf, next_cmd); +} + +void +otus_next_scan(void *arg, int pending) +{ +#if 0 + struct otus_softc *sc = arg; + + if (usbd_is_dying(sc->sc_udev)) + return; + + usbd_ref_incr(sc->sc_udev); + + if (sc->sc_ic.ic_state == IEEE80211_S_SCAN) + ieee80211_next_scan(&sc->sc_ic.ic_if); + + usbd_ref_decr(sc->sc_udev); +#endif +} + +int +otus_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + struct otus_vap *uvp = OTUS_VAP(vap); + struct ieee80211com *ic = vap->iv_ic; + struct otus_softc *sc = ic->ic_softc; + struct ieee80211_node *ni; + enum ieee80211_state ostate; + + ostate = vap->iv_state; + OTUS_DPRINTF(sc, OTUS_DEBUG_STATE, "%s: %s -> %s\n", __func__, + ieee80211_state_name[ostate], + ieee80211_state_name[nstate]); + + IEEE80211_UNLOCK(ic); + + OTUS_LOCK(sc); + + /* XXX TODO: more fleshing out! */ + + switch (nstate) { + case IEEE80211_S_RUN: + ni = ieee80211_ref_node(vap->iv_bss); + + if (ic->ic_opmode == IEEE80211_M_STA) { + otus_updateslot(sc); + otus_set_bssid(sc, ni->ni_bssid); + + /* Start calibration timer. */ + taskqueue_enqueue_timeout(taskqueue_thread, + &sc->calib_to, hz); + } + break; + default: + break; + } + + /* XXX TODO: calibration? */ + + sc->sc_led_newstate(sc); + + OTUS_UNLOCK(sc); + IEEE80211_LOCK(ic); + return (uvp->newstate(vap, nstate, arg)); +} + +int +otus_cmd(struct otus_softc *sc, uint8_t code, const void *idata, int ilen, + void *odata) +{ + struct otus_tx_cmd *cmd; + struct ar_cmd_hdr *hdr; + int xferlen, error; + + OTUS_LOCK_ASSERT(sc); + + /* Always bulk-out a multiple of 4 bytes. */ + xferlen = (sizeof (*hdr) + ilen + 3) & ~3; + + cmd = otus_get_txcmd(sc); + if (cmd == NULL) { + device_printf(sc->sc_dev, "%s: failed to get buf\n", + __func__); + return (EIO); + } + + hdr = (struct ar_cmd_hdr *)cmd->buf; + hdr->code = code; + hdr->len = ilen; + hdr->token = ++sc->token; /* Don't care about endianness. */ + cmd->token = hdr->token; + /* XXX TODO: check max cmd length? */ + memcpy((uint8_t *)&hdr[1], idata, ilen); + + OTUS_DPRINTF(sc, OTUS_DEBUG_CMD, + "%s: sending command code=0x%02x len=%d token=%d\n", + __func__, code, ilen, hdr->token); + + cmd->odata = odata; + cmd->buflen = xferlen; + + /* Queue the command to the endpoint */ + STAILQ_INSERT_TAIL(&sc->sc_cmd_pending, cmd, next_cmd); + usbd_transfer_start(sc->sc_xfer[OTUS_BULK_CMD]); + + /* Sleep on the command; wait for it to complete */ + error = msleep(cmd, &sc->sc_mtx, PCATCH, "otuscmd", hz); + + /* + * At this point we don't own cmd any longer; it'll be + * freed by the cmd bulk path or the RX notification + * path. If the data is made available then it'll be copied + * to the caller. All that is left to do is communicate + * status back to the caller. + */ + if (error != 0) { + device_printf(sc->sc_dev, + "%s: timeout waiting for command 0x%02x reply\n", + __func__, code); + } + return error; +} + +void +otus_write(struct otus_softc *sc, uint32_t reg, uint32_t val) +{ + + OTUS_LOCK_ASSERT(sc); + + sc->write_buf[sc->write_idx].reg = htole32(reg); + sc->write_buf[sc->write_idx].val = htole32(val); + + if (++sc->write_idx > AR_MAX_WRITE_IDX) + (void)otus_write_barrier(sc); +} + +int +otus_write_barrier(struct otus_softc *sc) +{ + int error; + + OTUS_LOCK_ASSERT(sc); + + if (sc->write_idx == 0) + return 0; /* Nothing to flush. */ + + OTUS_DPRINTF(sc, OTUS_DEBUG_REGIO, "%s: called; %d updates\n", + __func__, + sc->write_idx); + + error = otus_cmd(sc, AR_CMD_WREG, sc->write_buf, + sizeof (sc->write_buf[0]) * sc->write_idx, NULL); + sc->write_idx = 0; + return error; +} + +struct ieee80211_node * +otus_node_alloc(struct ieee80211com *ic) +{ + return malloc(sizeof (struct otus_node), M_DEVBUF, M_NOWAIT | M_ZERO); +} + +#if 0 +int +otus_media_change(struct ifnet *ifp) +{ + struct otus_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + uint8_t rate, ridx; + int error; + + error = ieee80211_media_change(ifp); + if (error != ENETRESET) + return error; + + if (ic->ic_fixed_rate != -1) { + rate = ic->ic_sup_rates[ic->ic_curmode]. + rs_rates[ic->ic_fixed_rate] & IEEE80211_RATE_VAL; + for (ridx = 0; ridx <= OTUS_RIDX_MAX; ridx++) + if (otus_rates[ridx].rate == rate) + break; + sc->fixed_ridx = ridx; + } + + if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING)) + error = otus_init(sc); + + return error; +} +#endif + +int +otus_read_eeprom(struct otus_softc *sc) +{ + uint32_t regs[8], reg; + uint8_t *eep; + int i, j, error; + + OTUS_LOCK_ASSERT(sc); + + /* Read EEPROM by blocks of 32 bytes. */ + eep = (uint8_t *)&sc->eeprom; + reg = AR_EEPROM_OFFSET; + for (i = 0; i < sizeof (sc->eeprom) / 32; i++) { + for (j = 0; j < 8; j++, reg += 4) + regs[j] = htole32(reg); + error = otus_cmd(sc, AR_CMD_RREG, regs, sizeof regs, eep); + if (error != 0) + break; + eep += 32; + } + return error; +} + +void +otus_newassoc(struct ieee80211_node *ni, int isnew) +{ + struct ieee80211com *ic = ni->ni_ic; + struct otus_softc *sc = ic->ic_softc; + struct otus_node *on = OTUS_NODE(ni); + + OTUS_DPRINTF(sc, OTUS_DEBUG_STATE, "new assoc isnew=%d addr=%s\n", + isnew, ether_sprintf(ni->ni_macaddr)); + + on->tx_done = 0; + on->tx_err = 0; + on->tx_retries = 0; +} + +static void +otus_cmd_handle_response(struct otus_softc *sc, struct ar_cmd_hdr *hdr) +{ + struct otus_tx_cmd *cmd; + + OTUS_LOCK_ASSERT(sc); + + OTUS_DPRINTF(sc, OTUS_DEBUG_CMDDONE, + "%s: received reply code=0x%02x len=%d token=%d\n", + __func__, + hdr->code, hdr->len, hdr->token); + + /* + * Walk the list, freeing items that aren't ours, + * stopping when we hit our token. + */ + while ((cmd = STAILQ_FIRST(&sc->sc_cmd_waiting)) != NULL) { + STAILQ_REMOVE_HEAD(&sc->sc_cmd_waiting, next_cmd); + OTUS_DPRINTF(sc, OTUS_DEBUG_CMDDONE, + "%s: cmd=%p; hdr.token=%d, cmd.token=%d\n", + __func__, + cmd, + (int) hdr->token, + (int) cmd->token); + if (hdr->token == cmd->token) { + /* Copy answer into caller's supplied buffer. */ + if (cmd->odata != NULL) + memcpy(cmd->odata, &hdr[1], hdr->len); + wakeup(cmd); + } + + STAILQ_INSERT_TAIL(&sc->sc_cmd_inactive, cmd, next_cmd); + } +} + +void +otus_cmd_rxeof(struct otus_softc *sc, uint8_t *buf, int len) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ar_cmd_hdr *hdr; + + OTUS_LOCK_ASSERT(sc); + + if (__predict_false(len < sizeof (*hdr))) { + OTUS_DPRINTF(sc, OTUS_DEBUG_CMDDONE, + "cmd too small %d\n", len); + return; + } + hdr = (struct ar_cmd_hdr *)buf; + if (__predict_false(sizeof (*hdr) + hdr->len > len || + sizeof (*hdr) + hdr->len > 64)) { + OTUS_DPRINTF(sc, OTUS_DEBUG_CMDDONE, + "cmd too large %d\n", hdr->len); + return; + } + + OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, + "%s: code=%.02x\n", + __func__, + hdr->code); + + /* + * XXX TODO: has to reach into the cmd queue "waiting for + * an RX response" list, grab the head entry and check + */ + if ((hdr->code & 0xc0) != 0xc0) { + otus_cmd_handle_response(sc, hdr); + return; + } + + /* Received unsolicited notification. */ + switch (hdr->code & 0x3f) { + case AR_EVT_BEACON: + break; + case AR_EVT_TX_COMP: + { + struct ar_evt_tx_comp *tx = (struct ar_evt_tx_comp *)&hdr[1]; + struct ieee80211_node *ni; + + ni = ieee80211_find_node(&ic->ic_sta, tx->macaddr); + if (ni == NULL) { + device_printf(sc->sc_dev, + "%s: txcomp on unknown node (%s)\n", + __func__, + ether_sprintf(tx->macaddr)); + break; + } + + OTUS_DPRINTF(sc, OTUS_DEBUG_TXCOMP, + "tx completed %s status=%d phy=0x%x\n", + ether_sprintf(tx->macaddr), le16toh(tx->status), + le32toh(tx->phy)); + + switch (le16toh(tx->status)) { + case AR_TX_STATUS_COMP: +#if 0 + ackfailcnt = 0; + ieee80211_ratectl_tx_complete(ni->ni_vap, ni, + IEEE80211_RATECTL_TX_SUCCESS, &ackfailcnt, NULL); +#endif + /* + * We don't get the above; only error notifications. + * Sigh. So, don't worry about this. + */ + break; + case AR_TX_STATUS_RETRY_COMP: + OTUS_NODE(ni)->tx_retries++; + break; + case AR_TX_STATUS_FAILED: + OTUS_NODE(ni)->tx_err++; + break; + } + ieee80211_free_node(ni); + break; + } + case AR_EVT_TBTT: + break; + case AR_EVT_DO_BB_RESET: + /* + * This is "tell driver to reset baseband" from ar9170-fw. + * + * I'm not sure what we should do here, so I'm going to + * fall through; it gets generated when RTSRetryCnt internally + * reaches '5' - I guess the firmware authors thought that + * meant that the BB may have gone deaf or something. + */ + default: + device_printf(sc->sc_dev, + "%s: received notification code=0x%02x len=%d\n", + __func__, + hdr->code, hdr->len); + } +} + +void +otus_sub_rxeof(struct otus_softc *sc, uint8_t *buf, int len, struct mbufq *rxq) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_rx_stats rxs; +#if 0 + struct ieee80211_node *ni; +#endif + struct ar_rx_tail *tail; + struct ieee80211_frame *wh; + struct mbuf *m; + uint8_t *plcp; +// int s; + int mlen; + + if (__predict_false(len < AR_PLCP_HDR_LEN)) { + OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, + "sub-xfer too short %d\n", len); + return; + } + plcp = buf; + + /* All bits in the PLCP header are set to 1 for non-MPDU. */ + if (memcmp(plcp, AR_PLCP_HDR_INTR, AR_PLCP_HDR_LEN) == 0) { + otus_cmd_rxeof(sc, plcp + AR_PLCP_HDR_LEN, + len - AR_PLCP_HDR_LEN); + return; + } + + /* Received MPDU. */ + if (__predict_false(len < AR_PLCP_HDR_LEN + sizeof (*tail))) { + OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, "MPDU too short %d\n", len); + counter_u64_add(ic->ic_ierrors, 1); + return; + } + tail = (struct ar_rx_tail *)(plcp + len - sizeof (*tail)); + + /* Discard error frames. */ + if (__predict_false(tail->error != 0)) { + OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, "error frame 0x%02x\n", tail->error); + if (tail->error & AR_RX_ERROR_FCS) { + OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, "bad FCS\n"); + } else if (tail->error & AR_RX_ERROR_MMIC) { + /* Report Michael MIC failures to net80211. */ +#if 0 + ieee80211_notify_michael_failure(ni->ni_vap, wh, keyidx); +#endif + device_printf(sc->sc_dev, "%s: MIC failure\n", __func__); + } + counter_u64_add(ic->ic_ierrors, 1); + return; + } + /* Compute MPDU's length. */ + mlen = len - AR_PLCP_HDR_LEN - sizeof (*tail); + /* Make sure there's room for an 802.11 header + FCS. */ + if (__predict_false(mlen < IEEE80211_MIN_LEN)) { + counter_u64_add(ic->ic_ierrors, 1); + return; + } + mlen -= IEEE80211_CRC_LEN; /* strip 802.11 FCS */ + + wh = (struct ieee80211_frame *)(plcp + AR_PLCP_HDR_LEN); + + m = m_get2(mlen, M_NOWAIT, MT_DATA, M_PKTHDR); + if (m == NULL) { + device_printf(sc->sc_dev, "%s: failed m_get2()\n", __func__); + counter_u64_add(ic->ic_ierrors, 1); + } + + /* Finalize mbuf. */ + memcpy(mtod(m, uint8_t *), wh, mlen); + m->m_pkthdr.len = m->m_len = mlen; + +#if 0 + if (__predict_false(sc->sc_drvbpf != NULL)) { + struct otus_rx_radiotap_header *tap = &sc->sc_rxtap; + struct mbuf mb; + + tap->wr_flags = 0; + tap->wr_chan_freq = htole16(ic->ic_ibss_chan->ic_freq); + tap->wr_chan_flags = htole16(ic->ic_ibss_chan->ic_flags); + tap->wr_antsignal = tail->rssi; + tap->wr_rate = 2; /* In case it can't be found below. */ + switch (tail->status & AR_RX_STATUS_MT_MASK) { + case AR_RX_STATUS_MT_CCK: + switch (plcp[0]) { + case 10: tap->wr_rate = 2; break; + case 20: tap->wr_rate = 4; break; + case 55: tap->wr_rate = 11; break; + case 110: tap->wr_rate = 22; break; + } + if (tail->status & AR_RX_STATUS_SHPREAMBLE) + tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; + break; + case AR_RX_STATUS_MT_OFDM: + switch (plcp[0] & 0xf) { + case 0xb: tap->wr_rate = 12; break; + case 0xf: tap->wr_rate = 18; break; + case 0xa: tap->wr_rate = 24; break; + case 0xe: tap->wr_rate = 36; break; + case 0x9: tap->wr_rate = 48; break; + case 0xd: tap->wr_rate = 72; break; + case 0x8: tap->wr_rate = 96; break; + case 0xc: tap->wr_rate = 108; break; + } + break; + } + mb.m_data = (caddr_t)tap; + mb.m_len = sc->sc_rxtap_len; + mb.m_next = m; + mb.m_nextpkt = NULL; + mb.m_type = 0; + mb.m_flags = 0; + bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_IN); + } +#endif + + /* Add RSSI/NF to this mbuf */ + bzero(&rxs, sizeof(rxs)); + rxs.r_flags = IEEE80211_R_NF | IEEE80211_R_RSSI; + rxs.nf = sc->sc_nf[0]; /* XXX chain 0 != combined rssi/nf */ + rxs.rssi = tail->rssi; + /* XXX TODO: add MIMO RSSI/NF as well */ + ieee80211_add_rx_params(m, &rxs); + + /* XXX make a method */ + STAILQ_INSERT_TAIL(&rxq->mq_head, m, m_stailqpkt); + +#if 0 + OTUS_UNLOCK(sc); + ni = ieee80211_find_rxnode(ic, wh); + rxi.rxi_flags = 0; + rxi.rxi_rssi = tail->rssi; + rxi.rxi_tstamp = 0; /* unused */ + ieee80211_input(ifp, m, ni, &rxi); + + /* Node is no longer needed. */ + ieee80211_release_node(ic, ni); + OTUS_LOCK(sc); +#endif +} + +static void +otus_rxeof(struct usb_xfer *xfer, struct otus_data *data, struct mbufq *rxq) +{ + struct otus_softc *sc = usbd_xfer_softc(xfer); + caddr_t buf = data->buf; + struct ar_rx_head *head; + uint16_t hlen; + int len; + + usbd_xfer_status(xfer, &len, NULL, NULL, NULL); + + while (len >= sizeof (*head)) { + head = (struct ar_rx_head *)buf; + if (__predict_false(head->tag != htole16(AR_RX_HEAD_TAG))) { + OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, + "tag not valid 0x%x\n", le16toh(head->tag)); + break; + } + hlen = le16toh(head->len); + if (__predict_false(sizeof (*head) + hlen > len)) { + OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, + "xfer too short %d/%d\n", len, hlen); + break; + } + /* Process sub-xfer. */ + otus_sub_rxeof(sc, (uint8_t *)&head[1], hlen, rxq); + + /* Next sub-xfer is aligned on a 32-bit boundary. */ + hlen = (sizeof (*head) + hlen + 3) & ~3; + buf += hlen; + len -= hlen; + } +} + +static void +otus_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct otus_softc *sc = usbd_xfer_softc(xfer); + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_frame *wh; + struct ieee80211_node *ni; + struct mbuf *m; + struct mbufq scrx; + struct otus_data *data; + + OTUS_LOCK_ASSERT(sc); + + mbufq_init(&scrx, 1024); + +#if 0 + device_printf(sc->sc_dev, "%s: called; state=%d; error=%d\n", + __func__, + USB_GET_STATE(xfer), + error); +#endif + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + data = STAILQ_FIRST(&sc->sc_rx_active); + if (data == NULL) + goto tr_setup; + STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next); + otus_rxeof(xfer, data, &scrx); + STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next); + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + /* + * XXX TODO: what if sc_rx isn't empty, but data + * is empty? Then we leak mbufs. + */ + data = STAILQ_FIRST(&sc->sc_rx_inactive); + if (data == NULL) { + //KASSERT(m == NULL, ("mbuf isn't NULL")); + return; + } + STAILQ_REMOVE_HEAD(&sc->sc_rx_inactive, next); + STAILQ_INSERT_TAIL(&sc->sc_rx_active, data, next); + usbd_xfer_set_frame_data(xfer, 0, data->buf, + usbd_xfer_max_len(xfer)); + usbd_transfer_submit(xfer); + /* + * To avoid LOR we should unlock our private mutex here to call + * ieee80211_input() because here is at the end of a USB + * callback and safe to unlock. + */ + OTUS_UNLOCK(sc); + while ((m = mbufq_dequeue(&scrx)) != NULL) { + wh = mtod(m, struct ieee80211_frame *); + ni = ieee80211_find_rxnode(ic, + (struct ieee80211_frame_min *)wh); + if (ni != NULL) { + if (ni->ni_flags & IEEE80211_NODE_HT) + m->m_flags |= M_AMPDU; + (void)ieee80211_input_mimo(ni, m, NULL); + ieee80211_free_node(ni); + } else + (void)ieee80211_input_mimo_all(ic, m, NULL); + } + OTUS_LOCK(sc); + break; + default: + /* needs it to the inactive queue due to a error. */ + data = STAILQ_FIRST(&sc->sc_rx_active); + if (data != NULL) { + STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next); + STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next); + } + if (error != USB_ERR_CANCELLED) { + usbd_xfer_set_stall(xfer); + counter_u64_add(ic->ic_ierrors, 1); + goto tr_setup; + } + break; + } +} + +static void +otus_txeof(struct usb_xfer *xfer, struct otus_data *data) +{ + struct otus_softc *sc = usbd_xfer_softc(xfer); + + OTUS_DPRINTF(sc, OTUS_DEBUG_TXDONE, + "%s: called; data=%p\n", __func__, data); + + OTUS_LOCK_ASSERT(sc); + + if (data->m) { + /* XXX status? */ + /* XXX we get TX status via the RX path.. */ + ieee80211_tx_complete(data->ni, data->m, 0); + data->m = NULL; + data->ni = NULL; + } +} + +static void +otus_txcmdeof(struct usb_xfer *xfer, struct otus_tx_cmd *cmd) +{ + struct otus_softc *sc = usbd_xfer_softc(xfer); + + OTUS_LOCK_ASSERT(sc); + + OTUS_DPRINTF(sc, OTUS_DEBUG_CMDDONE, + "%s: called; data=%p; odata=%p\n", + __func__, cmd, cmd->odata); + + /* + * Non-response commands still need wakeup so the caller + * knows it was submitted and completed OK; response commands should + * wait until they're ACKed by the firmware with a response. + */ + if (cmd->odata) { + STAILQ_INSERT_TAIL(&sc->sc_cmd_waiting, cmd, next_cmd); + } else { + wakeup(cmd); + otus_free_txcmd(sc, cmd); + } +} + +static void +otus_bulk_tx_callback(struct usb_xfer *xfer, usb_error_t error) +{ + uint8_t which = OTUS_BULK_TX; + struct otus_softc *sc = usbd_xfer_softc(xfer); + struct ieee80211com *ic = &sc->sc_ic; + struct otus_data *data; + + OTUS_LOCK_ASSERT(sc); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + data = STAILQ_FIRST(&sc->sc_tx_active[which]); + if (data == NULL) + goto tr_setup; + OTUS_DPRINTF(sc, OTUS_DEBUG_TXDONE, + "%s: transfer done %p\n", __func__, data); + STAILQ_REMOVE_HEAD(&sc->sc_tx_active[which], next); + otus_txeof(xfer, data); + otus_freebuf(sc, data); + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + data = STAILQ_FIRST(&sc->sc_tx_pending[which]); + if (data == NULL) { + OTUS_DPRINTF(sc, OTUS_DEBUG_XMIT, + "%s: empty pending queue sc %p\n", __func__, sc); + goto finish; + } + STAILQ_REMOVE_HEAD(&sc->sc_tx_pending[which], next); + STAILQ_INSERT_TAIL(&sc->sc_tx_active[which], data, next); + usbd_xfer_set_frame_data(xfer, 0, data->buf, data->buflen); + OTUS_DPRINTF(sc, OTUS_DEBUG_XMIT, + "%s: submitting transfer %p\n", __func__, data); + usbd_transfer_submit(xfer); + break; + default: + data = STAILQ_FIRST(&sc->sc_tx_active[which]); + if (data != NULL) { + STAILQ_REMOVE_HEAD(&sc->sc_tx_active[which], next); + otus_txeof(xfer, data); + otus_freebuf(sc, data); + } + counter_u64_add(ic->ic_oerrors, 1); + + if (error != USB_ERR_CANCELLED) { + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + break; + } + +finish: + /* Kick TX */ + otus_tx_start(sc); +} + +static void +otus_bulk_cmd_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct otus_softc *sc = usbd_xfer_softc(xfer); +#if 0 + struct ieee80211com *ic = &sc->sc_ic; +#endif + struct otus_tx_cmd *cmd; + + OTUS_LOCK_ASSERT(sc); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + cmd = STAILQ_FIRST(&sc->sc_cmd_active); + if (cmd == NULL) + goto tr_setup; + OTUS_DPRINTF(sc, OTUS_DEBUG_CMDDONE, + "%s: transfer done %p\n", __func__, cmd); + STAILQ_REMOVE_HEAD(&sc->sc_cmd_active, next_cmd); + otus_txcmdeof(xfer, cmd); + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + cmd = STAILQ_FIRST(&sc->sc_cmd_pending); + if (cmd == NULL) { + OTUS_DPRINTF(sc, OTUS_DEBUG_CMD, + "%s: empty pending queue sc %p\n", __func__, sc); + return; + } + STAILQ_REMOVE_HEAD(&sc->sc_cmd_pending, next_cmd); + STAILQ_INSERT_TAIL(&sc->sc_cmd_active, cmd, next_cmd); + usbd_xfer_set_frame_data(xfer, 0, cmd->buf, cmd->buflen); + OTUS_DPRINTF(sc, OTUS_DEBUG_CMD, + "%s: submitting transfer %p; buf=%p, buflen=%d\n", __func__, cmd, cmd->buf, cmd->buflen); + usbd_transfer_submit(xfer); + break; + default: + cmd = STAILQ_FIRST(&sc->sc_cmd_active); + if (cmd != NULL) { + STAILQ_REMOVE_HEAD(&sc->sc_cmd_active, next_cmd); + otus_txcmdeof(xfer, cmd); + } + + if (error != USB_ERR_CANCELLED) { + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + break; + } +} + +/* + * This isn't used by carl9170; it however may be used by the + * initial bootloader. + */ +static void +otus_bulk_irq_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct otus_softc *sc = usbd_xfer_softc(xfer); + int actlen; + int sumlen; + + usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); + OTUS_DPRINTF(sc, OTUS_DEBUG_IRQ, + "%s: called; state=%d\n", __func__, USB_GET_STATE(xfer)); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + /* + * Read usb frame data, if any. + * "actlen" has the total length for all frames + * transferred. + */ + OTUS_DPRINTF(sc, OTUS_DEBUG_IRQ, + "%s: comp; %d bytes\n", + __func__, + actlen); +#if 0 + pc = usbd_xfer_get_frame(xfer, 0); + otus_dump_usb_rx_page(sc, pc, actlen); +#endif + /* XXX fallthrough */ + case USB_ST_SETUP: + /* + * Setup xfer frame lengths/count and data + */ + OTUS_DPRINTF(sc, OTUS_DEBUG_IRQ, "%s: setup\n", __func__); + usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); + usbd_transfer_submit(xfer); + break; + + default: /* Error */ + /* + * Print error message and clear stall + * for example. + */ + OTUS_DPRINTF(sc, OTUS_DEBUG_IRQ, "%s: ERROR?\n", __func__); + break; + } +} + +/* + * Map net80211 rate to hw rate for otus MAC/PHY. + */ +static uint8_t +otus_rate_to_hw_rate(struct otus_softc *sc, uint8_t rate) +{ + int is_2ghz; + + is_2ghz = !! (IEEE80211_IS_CHAN_2GHZ(sc->sc_ic.ic_curchan)); + + switch (rate) { + /* CCK */ + case 2: + return (0x0); + case 4: + return (0x1); + case 11: + return (0x2); + case 22: + return (0x3); + /* OFDM */ + case 12: + return (0xb); + case 18: + return (0xf); + case 24: + return (0xa); + case 36: + return (0xe); + case 48: + return (0x9); + case 72: + return (0xd); + case 96: + return (0x8); + case 108: + return (0xc); + default: + device_printf(sc->sc_dev, "%s: unknown rate '%d'\n", + __func__, (int) rate); + case 0: + if (is_2ghz) + return (0x0); /* 1MB CCK */ + else + return (0xb); /* 6MB OFDM */ + + /* XXX TODO: HT */ + } +} + +static int +otus_hw_rate_is_ofdm(struct otus_softc *sc, uint8_t hw_rate) +{ + + switch (hw_rate) { + case 0x0: + case 0x1: + case 0x2: + case 0x3: + return (0); + default: + return (1); + } +} + + +static void +otus_tx_update_ratectl(struct otus_softc *sc, struct ieee80211_node *ni) +{ + int tx, tx_success, tx_retry; + + tx = OTUS_NODE(ni)->tx_done; + tx_success = OTUS_NODE(ni)->tx_done - OTUS_NODE(ni)->tx_err; + tx_retry = OTUS_NODE(ni)->tx_retries; + + ieee80211_ratectl_tx_update(ni->ni_vap, ni, &tx, &tx_success, + &tx_retry); +} + +/* + * XXX TODO: support tx bpf parameters for configuration! + */ +static int +otus_tx(struct otus_softc *sc, struct ieee80211_node *ni, struct mbuf *m, + struct otus_data *data) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_frame *wh; + struct ieee80211_key *k; + struct ar_tx_head *head; + uint32_t phyctl; + uint16_t macctl, qos; + uint8_t qid, rate; + int hasqos, xferlen; + + wh = mtod(m, struct ieee80211_frame *); + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { + k = ieee80211_crypto_encap(ni, m); + if (k == NULL) { + device_printf(sc->sc_dev, + "%s: m=%p: ieee80211_crypto_encap returns NULL\n", + __func__, + m); + return (ENOBUFS); + } + wh = mtod(m, struct ieee80211_frame *); + } + + /* Calculate transfer length; ensure data buffer is large enough */ + xferlen = sizeof (*head) + m->m_pkthdr.len; + if (xferlen > OTUS_TXBUFSZ) { + device_printf(sc->sc_dev, + "%s: 802.11 TX frame is %d bytes, max %d bytes\n", + __func__, + xferlen, + OTUS_TXBUFSZ); + return (ENOBUFS); + } + + hasqos = !! IEEE80211_QOS_HAS_SEQ(wh); + + if (hasqos) { + uint8_t tid; + qos = ((const struct ieee80211_qosframe *)wh)->i_qos[0]; + tid = qos & IEEE80211_QOS_TID; + qid = TID_TO_WME_AC(tid); + } else { + qos = 0; + qid = WME_AC_BE; + } + + /* Pickup a rate index. */ + if (IEEE80211_IS_MULTICAST(wh->i_addr1) || + (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_DATA) { + /* Get lowest rate */ + rate = otus_rate_to_hw_rate(sc, 0); + } else { + (void) ieee80211_ratectl_rate(ni, NULL, 0); + rate = otus_rate_to_hw_rate(sc, ni->ni_txrate); + } + + phyctl = 0; + macctl = AR_TX_MAC_BACKOFF | AR_TX_MAC_HW_DUR | AR_TX_MAC_QID(qid); + + if (IEEE80211_IS_MULTICAST(wh->i_addr1) || + (hasqos && ((qos & IEEE80211_QOS_ACKPOLICY) == + IEEE80211_QOS_ACKPOLICY_NOACK))) + macctl |= AR_TX_MAC_NOACK; + + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + if (m->m_pkthdr.len + IEEE80211_CRC_LEN >= vap->iv_rtsthreshold) + macctl |= AR_TX_MAC_RTS; + else if (ic->ic_flags & IEEE80211_F_USEPROT) { + if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) + macctl |= AR_TX_MAC_CTS; + else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) + macctl |= AR_TX_MAC_RTS; + } + } + + phyctl |= AR_TX_PHY_MCS(rate); + if (otus_hw_rate_is_ofdm(sc, rate)) { + phyctl |= AR_TX_PHY_MT_OFDM; + /* Always use all tx antennas for now, just to be safe */ + phyctl |= AR_TX_PHY_ANTMSK(sc->txmask); + } else { /* CCK */ + phyctl |= AR_TX_PHY_MT_CCK; + phyctl |= AR_TX_PHY_ANTMSK(sc->txmask); + } + + /* Update net80211 with the current counters */ + otus_tx_update_ratectl(sc, ni); + + /* Update rate control stats for frames that are ACK'ed. */ + if (!(macctl & AR_TX_MAC_NOACK)) + OTUS_NODE(ni)->tx_done++; + + + /* Fill Tx descriptor. */ + head = (struct ar_tx_head *)data->buf; + head->len = htole16(m->m_pkthdr.len + IEEE80211_CRC_LEN); + head->macctl = htole16(macctl); + head->phyctl = htole32(phyctl); + + m_copydata(m, 0, m->m_pkthdr.len, (caddr_t)&head[1]); + + data->buflen = xferlen; + data->ni = ni; + data->m = m; + + OTUS_DPRINTF(sc, OTUS_DEBUG_XMIT, + "%s: tx: m=%p; data=%p; len=%d mac=0x%04x phy=0x%08x rate=0x%02x, ni_txrate=%d\n", + __func__, m, data, head->len, head->macctl, head->phyctl, + (int) rate, (int) ni->ni_txrate); + + /* Submit transfer */ + STAILQ_INSERT_TAIL(&sc->sc_tx_pending[OTUS_BULK_TX], data, next); + usbd_transfer_start(sc->sc_xfer[OTUS_BULK_TX]); + + return 0; +} + +int +otus_set_multi(struct otus_softc *sc) +{ + uint32_t lo, hi; + struct ieee80211com *ic = &sc->sc_ic; + int r; + + if (ic->ic_allmulti > 0 || ic->ic_promisc > 0 || + ic->ic_opmode == IEEE80211_M_MONITOR) { + lo = 0xffffffff; + hi = 0xffffffff; + } else { + struct ieee80211vap *vap; + struct ifnet *ifp; + struct ifmultiaddr *ifma; + + lo = hi = 0; + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + ifp = vap->iv_ifp; + if_maddr_rlock(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + caddr_t dl; + uint32_t val; + + dl = LLADDR((struct sockaddr_dl *) ifma->ifma_addr); + val = LE_READ_4(dl + 4); + /* Get address byte 5 */ + val = val & 0x0000ff00; + val = val >> 8; + + /* As per below, shift it >> 2 to get only 6 bits */ + val = val >> 2; + if (val < 32) + lo |= 1 << val; + else + hi |= 1 << (val - 32); + } + if_maddr_runlock(ifp); + } + } +#if 0 + /* XXX openbsd code */ + while (enm != NULL) { + bit = enm->enm_addrlo[5] >> 2; + if (bit < 32) + lo |= 1 << bit; + else + hi |= 1 << (bit - 32); + ETHER_NEXT_MULTI(step, enm); + } +#endif + + hi |= 1U << 31; /* Make sure the broadcast bit is set. */ + + OTUS_LOCK(sc); + otus_write(sc, AR_MAC_REG_GROUP_HASH_TBL_L, lo); + otus_write(sc, AR_MAC_REG_GROUP_HASH_TBL_H, hi); + r = otus_write_barrier(sc); + OTUS_UNLOCK(sc); + return (r); +} + +static void +otus_updateedca(struct otus_softc *sc) +{ +#define EXP2(val) ((1 << (val)) - 1) +#define AIFS(val) ((val) * 9 + 10) + struct ieee80211com *ic = &sc->sc_ic; + const struct wmeParams *edca; + + OTUS_LOCK_ASSERT(sc); + + edca = ic->ic_wme.wme_chanParams.cap_wmeParams; + + /* Set CWmin/CWmax values. */ + otus_write(sc, AR_MAC_REG_AC0_CW, + EXP2(edca[WME_AC_BE].wmep_logcwmax) << 16 | + EXP2(edca[WME_AC_BE].wmep_logcwmin)); + otus_write(sc, AR_MAC_REG_AC1_CW, + EXP2(edca[WME_AC_BK].wmep_logcwmax) << 16 | + EXP2(edca[WME_AC_BK].wmep_logcwmin)); + otus_write(sc, AR_MAC_REG_AC2_CW, + EXP2(edca[WME_AC_VI].wmep_logcwmax) << 16 | + EXP2(edca[WME_AC_VI].wmep_logcwmin)); + otus_write(sc, AR_MAC_REG_AC3_CW, + EXP2(edca[WME_AC_VO].wmep_logcwmax) << 16 | + EXP2(edca[WME_AC_VO].wmep_logcwmin)); + otus_write(sc, AR_MAC_REG_AC4_CW, /* Special TXQ. */ + EXP2(edca[WME_AC_VO].wmep_logcwmax) << 16 | + EXP2(edca[WME_AC_VO].wmep_logcwmin)); + + /* Set AIFSN values. */ + otus_write(sc, AR_MAC_REG_AC1_AC0_AIFS, + AIFS(edca[WME_AC_VI].wmep_aifsn) << 24 | + AIFS(edca[WME_AC_BK].wmep_aifsn) << 12 | + AIFS(edca[WME_AC_BE].wmep_aifsn)); + otus_write(sc, AR_MAC_REG_AC3_AC2_AIFS, + AIFS(edca[WME_AC_VO].wmep_aifsn) << 16 | /* Special TXQ. */ + AIFS(edca[WME_AC_VO].wmep_aifsn) << 4 | + AIFS(edca[WME_AC_VI].wmep_aifsn) >> 8); + + /* Set TXOP limit. */ + otus_write(sc, AR_MAC_REG_AC1_AC0_TXOP, + edca[WME_AC_BK].wmep_txopLimit << 16 | + edca[WME_AC_BE].wmep_txopLimit); + otus_write(sc, AR_MAC_REG_AC3_AC2_TXOP, + edca[WME_AC_VO].wmep_txopLimit << 16 | + edca[WME_AC_VI].wmep_txopLimit); + + /* XXX ACK policy? */ + + (void)otus_write_barrier(sc); + +#undef AIFS +#undef EXP2 +} + +static void +otus_updateslot(struct otus_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + uint32_t slottime; + + OTUS_LOCK_ASSERT(sc); + + slottime = (ic->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20; + otus_write(sc, AR_MAC_REG_SLOT_TIME, slottime << 10); + (void)otus_write_barrier(sc); +} + +int +otus_init_mac(struct otus_softc *sc) +{ + int error; + + OTUS_LOCK_ASSERT(sc); + + otus_write(sc, AR_MAC_REG_ACK_EXTENSION, 0x40); + otus_write(sc, AR_MAC_REG_RETRY_MAX, 0); + otus_write(sc, AR_MAC_REG_SNIFFER, 0x2000000); + otus_write(sc, AR_MAC_REG_RX_THRESHOLD, 0xc1f80); + otus_write(sc, AR_MAC_REG_RX_PE_DELAY, 0x70); + otus_write(sc, AR_MAC_REG_EIFS_AND_SIFS, 0xa144000); + otus_write(sc, AR_MAC_REG_SLOT_TIME, 9 << 10); + otus_write(sc, 0x1c3b2c, 0x19000000); + /* NAV protects ACK only (in TXOP). */ + otus_write(sc, 0x1c3b38, 0x201); + /* Set beacon Tx power to 0x7. */ + otus_write(sc, AR_MAC_REG_BCN_HT1, 0x8000170); + otus_write(sc, AR_MAC_REG_BACKOFF_PROTECT, 0x105); + otus_write(sc, 0x1c3b9c, 0x10000a); + /* Filter any control frames, BAR is bit 24. */ + otus_write(sc, 0x1c368c, 0x0500ffff); + otus_write(sc, 0x1c3c40, 0x1); + otus_write(sc, AR_MAC_REG_BASIC_RATE, 0x150f); + otus_write(sc, AR_MAC_REG_MANDATORY_RATE, 0x150f); + otus_write(sc, AR_MAC_REG_RTS_CTS_RATE, 0x10b01bb); + otus_write(sc, 0x1c3694, 0x4003c1e); + /* Enable LED0 and LED1. */ + otus_write(sc, 0x1d0100, 0x3); + otus_write(sc, 0x1d0104, 0x3); + /* Switch MAC to OTUS interface. */ + otus_write(sc, 0x1c3600, 0x3); + otus_write(sc, 0x1c3c50, 0xffff); + otus_write(sc, 0x1c3680, 0xf00008); + /* Disable Rx timeout (workaround). */ + otus_write(sc, 0x1c362c, 0); + + /* Set USB Rx stream mode maximum frame number to 2. */ + otus_write(sc, 0x1e1110, 0x4); + /* Set USB Rx stream mode timeout to 10us. */ + otus_write(sc, 0x1e1114, 0x80); + + /* Set clock frequency to 88/80MHz. */ + otus_write(sc, 0x1d4008, 0x73); + /* Set WLAN DMA interrupt mode: generate intr per packet. */ + otus_write(sc, 0x1c3d7c, 0x110011); + otus_write(sc, 0x1c3bb0, 0x4); + otus_write(sc, AR_MAC_REG_TXOP_NOT_ENOUGH_INDICATION, 0x141e0f48); + + /* Disable HW decryption for now. */ + otus_write(sc, 0x1c3678, 0x78); + + if ((error = otus_write_barrier(sc)) != 0) + return error; + + /* Set default EDCA parameters. */ + otus_updateedca(sc); + + return 0; +} + +/* + * Return default value for PHY register based on current operating mode. + */ +uint32_t +otus_phy_get_def(struct otus_softc *sc, uint32_t reg) +{ + int i; + + for (i = 0; i < nitems(ar5416_phy_regs); i++) + if (AR_PHY(ar5416_phy_regs[i]) == reg) + return sc->phy_vals[i]; + return 0; /* Register not found. */ +} + +/* + * Update PHY's programming based on vendor-specific data stored in EEPROM. + * This is for FEM-type devices only. + */ +int +otus_set_board_values(struct otus_softc *sc, struct ieee80211_channel *c) +{ + const struct ModalEepHeader *eep; + uint32_t tmp, offset; + + if (IEEE80211_IS_CHAN_5GHZ(c)) + eep = &sc->eeprom.modalHeader[0]; + else + eep = &sc->eeprom.modalHeader[1]; + + /* Offset of chain 2. */ + offset = 2 * 0x1000; + + tmp = le32toh(eep->antCtrlCommon); + otus_write(sc, AR_PHY_SWITCH_COM, tmp); + + tmp = le32toh(eep->antCtrlChain[0]); + otus_write(sc, AR_PHY_SWITCH_CHAIN_0, tmp); + + tmp = le32toh(eep->antCtrlChain[1]); + otus_write(sc, AR_PHY_SWITCH_CHAIN_0 + offset, tmp); + + if (1 /* sc->sc_sco == AR_SCO_SCN */) { + tmp = otus_phy_get_def(sc, AR_PHY_SETTLING); + tmp &= ~(0x7f << 7); + tmp |= (eep->switchSettling & 0x7f) << 7; + otus_write(sc, AR_PHY_SETTLING, tmp); + } + + tmp = otus_phy_get_def(sc, AR_PHY_DESIRED_SZ); + tmp &= ~0xffff; + tmp |= eep->pgaDesiredSize << 8 | eep->adcDesiredSize; + otus_write(sc, AR_PHY_DESIRED_SZ, tmp); + + tmp = eep->txEndToXpaOff << 24 | eep->txEndToXpaOff << 16 | + eep->txFrameToXpaOn << 8 | eep->txFrameToXpaOn; + otus_write(sc, AR_PHY_RF_CTL4, tmp); + + tmp = otus_phy_get_def(sc, AR_PHY_RF_CTL3); + tmp &= ~(0xff << 16); + tmp |= eep->txEndToRxOn << 16; + otus_write(sc, AR_PHY_RF_CTL3, tmp); + + tmp = otus_phy_get_def(sc, AR_PHY_CCA); + tmp &= ~(0x7f << 12); + tmp |= (eep->thresh62 & 0x7f) << 12; + otus_write(sc, AR_PHY_CCA, tmp); + + tmp = otus_phy_get_def(sc, AR_PHY_RXGAIN); + tmp &= ~(0x3f << 12); + tmp |= (eep->txRxAttenCh[0] & 0x3f) << 12; + otus_write(sc, AR_PHY_RXGAIN, tmp); + + tmp = otus_phy_get_def(sc, AR_PHY_RXGAIN + offset); + tmp &= ~(0x3f << 12); + tmp |= (eep->txRxAttenCh[1] & 0x3f) << 12; + otus_write(sc, AR_PHY_RXGAIN + offset, tmp); + + tmp = otus_phy_get_def(sc, AR_PHY_GAIN_2GHZ); + tmp &= ~(0x3f << 18); + tmp |= (eep->rxTxMarginCh[0] & 0x3f) << 18; + if (IEEE80211_IS_CHAN_5GHZ(c)) { + tmp &= ~(0xf << 10); + tmp |= (eep->bswMargin[0] & 0xf) << 10; + } + otus_write(sc, AR_PHY_GAIN_2GHZ, tmp); + + tmp = otus_phy_get_def(sc, AR_PHY_GAIN_2GHZ + offset); + tmp &= ~(0x3f << 18); + tmp |= (eep->rxTxMarginCh[1] & 0x3f) << 18; + otus_write(sc, AR_PHY_GAIN_2GHZ + offset, tmp); + + tmp = otus_phy_get_def(sc, AR_PHY_TIMING_CTRL4); + tmp &= ~(0x3f << 5 | 0x1f); + tmp |= (eep->iqCalICh[0] & 0x3f) << 5 | (eep->iqCalQCh[0] & 0x1f); + otus_write(sc, AR_PHY_TIMING_CTRL4, tmp); + + tmp = otus_phy_get_def(sc, AR_PHY_TIMING_CTRL4 + offset); + tmp &= ~(0x3f << 5 | 0x1f); + tmp |= (eep->iqCalICh[1] & 0x3f) << 5 | (eep->iqCalQCh[1] & 0x1f); + otus_write(sc, AR_PHY_TIMING_CTRL4 + offset, tmp); + + tmp = otus_phy_get_def(sc, AR_PHY_TPCRG1); + tmp &= ~(0xf << 16); + tmp |= (eep->xpd & 0xf) << 16; + otus_write(sc, AR_PHY_TPCRG1, tmp); + + return otus_write_barrier(sc); +} + +int +otus_program_phy(struct otus_softc *sc, struct ieee80211_channel *c) +{ + const uint32_t *vals; + int error, i; + + /* Select PHY programming based on band and bandwidth. */ + if (IEEE80211_IS_CHAN_2GHZ(c)) + vals = ar5416_phy_vals_2ghz_20mhz; + else + vals = ar5416_phy_vals_5ghz_20mhz; + for (i = 0; i < nitems(ar5416_phy_regs); i++) + otus_write(sc, AR_PHY(ar5416_phy_regs[i]), vals[i]); + sc->phy_vals = vals; + + if (sc->eeprom.baseEepHeader.deviceType == 0x80) /* FEM */ + if ((error = otus_set_board_values(sc, c)) != 0) + return error; + + /* Initial Tx power settings. */ + otus_write(sc, AR_PHY_POWER_TX_RATE_MAX, 0x7f); + otus_write(sc, AR_PHY_POWER_TX_RATE1, 0x3f3f3f3f); + otus_write(sc, AR_PHY_POWER_TX_RATE2, 0x3f3f3f3f); + otus_write(sc, AR_PHY_POWER_TX_RATE3, 0x3f3f3f3f); + otus_write(sc, AR_PHY_POWER_TX_RATE4, 0x3f3f3f3f); + otus_write(sc, AR_PHY_POWER_TX_RATE5, 0x3f3f3f3f); + otus_write(sc, AR_PHY_POWER_TX_RATE6, 0x3f3f3f3f); + otus_write(sc, AR_PHY_POWER_TX_RATE7, 0x3f3f3f3f); + otus_write(sc, AR_PHY_POWER_TX_RATE8, 0x3f3f3f3f); + otus_write(sc, AR_PHY_POWER_TX_RATE9, 0x3f3f3f3f); + + if (IEEE80211_IS_CHAN_2GHZ(c)) + otus_write(sc, 0x1d4014, 0x5163); + else + otus_write(sc, 0x1d4014, 0x5143); + + return otus_write_barrier(sc); +} + +static __inline uint8_t +otus_reverse_bits(uint8_t v) +{ + v = ((v >> 1) & 0x55) | ((v & 0x55) << 1); + v = ((v >> 2) & 0x33) | ((v & 0x33) << 2); + v = ((v >> 4) & 0x0f) | ((v & 0x0f) << 4); + return v; +} + +int +otus_set_rf_bank4(struct otus_softc *sc, struct ieee80211_channel *c) +{ + uint8_t chansel, d0, d1; + uint16_t data; + int error; + + OTUS_LOCK_ASSERT(sc); + + d0 = 0; + if (IEEE80211_IS_CHAN_5GHZ(c)) { + chansel = (c->ic_freq - 4800) / 5; + if (chansel & 1) + d0 |= AR_BANK4_AMODE_REFSEL(2); + else + d0 |= AR_BANK4_AMODE_REFSEL(1); + } else { + d0 |= AR_BANK4_AMODE_REFSEL(2); + if (c->ic_freq == 2484) { /* CH 14 */ + d0 |= AR_BANK4_BMODE_LF_SYNTH_FREQ; + chansel = 10 + (c->ic_freq - 2274) / 5; + } else + chansel = 16 + (c->ic_freq - 2272) / 5; + chansel <<= 2; + } + d0 |= AR_BANK4_ADDR(1) | AR_BANK4_CHUP; + d1 = otus_reverse_bits(chansel); + + /* Write bits 0-4 of d0 and d1. */ + data = (d1 & 0x1f) << 5 | (d0 & 0x1f); + otus_write(sc, AR_PHY(44), data); + /* Write bits 5-7 of d0 and d1. */ + data = (d1 >> 5) << 5 | (d0 >> 5); + otus_write(sc, AR_PHY(58), data); + + if ((error = otus_write_barrier(sc)) == 0) + otus_delay_ms(sc, 10); + return error; +} + +void +otus_get_delta_slope(uint32_t coeff, uint32_t *exponent, uint32_t *mantissa) +{ +#define COEFF_SCALE_SHIFT 24 + uint32_t exp, man; + + /* exponent = 14 - floor(log2(coeff)) */ + for (exp = 31; exp > 0; exp--) + if (coeff & (1 << exp)) + break; + KASSERT(exp != 0, ("exp")); + exp = 14 - (exp - COEFF_SCALE_SHIFT); + + /* mantissa = floor(coeff * 2^exponent + 0.5) */ + man = coeff + (1 << (COEFF_SCALE_SHIFT - exp - 1)); + + *mantissa = man >> (COEFF_SCALE_SHIFT - exp); + *exponent = exp - 16; +#undef COEFF_SCALE_SHIFT +} + +static int +otus_set_chan(struct otus_softc *sc, struct ieee80211_channel *c, int assoc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ar_cmd_frequency cmd; + struct ar_rsp_frequency rsp; + const uint32_t *vals; + uint32_t coeff, exp, man, tmp; + uint8_t code; + int error, chan, i; + + error = 0; + chan = ieee80211_chan2ieee(ic, c); + + OTUS_DPRINTF(sc, OTUS_DEBUG_RESET, + "setting channel %d (%dMHz)\n", chan, c->ic_freq); + + tmp = IEEE80211_IS_CHAN_2GHZ(c) ? 0x105 : 0x104; + otus_write(sc, AR_MAC_REG_DYNAMIC_SIFS_ACK, tmp); + if ((error = otus_write_barrier(sc)) != 0) + goto finish; + + /* Disable BB Heavy Clip. */ + otus_write(sc, AR_PHY_HEAVY_CLIP_ENABLE, 0x200); + if ((error = otus_write_barrier(sc)) != 0) + goto finish; + + /* XXX Is that FREQ_START ? */ + error = otus_cmd(sc, AR_CMD_FREQ_STRAT, NULL, 0, NULL); + if (error != 0) + goto finish; + + /* Reprogram PHY and RF on channel band or bandwidth changes. */ + if (sc->bb_reset || c->ic_flags != sc->sc_curchan->ic_flags) { + OTUS_DPRINTF(sc, OTUS_DEBUG_RESET, "band switch\n"); + + /* Cold/Warm reset BB/ADDA. */ + otus_write(sc, 0x1d4004, sc->bb_reset ? 0x800 : 0x400); + if ((error = otus_write_barrier(sc)) != 0) + goto finish; + otus_write(sc, 0x1d4004, 0); + if ((error = otus_write_barrier(sc)) != 0) + goto finish; + sc->bb_reset = 0; + + if ((error = otus_program_phy(sc, c)) != 0) { + device_printf(sc->sc_dev, + "%s: could not program PHY\n", + __func__); + goto finish; + } + + /* Select RF programming based on band. */ + if (IEEE80211_IS_CHAN_5GHZ(c)) + vals = ar5416_banks_vals_5ghz; + else + vals = ar5416_banks_vals_2ghz; + for (i = 0; i < nitems(ar5416_banks_regs); i++) + otus_write(sc, AR_PHY(ar5416_banks_regs[i]), vals[i]); + if ((error = otus_write_barrier(sc)) != 0) { + device_printf(sc->sc_dev, + "%s: could not program RF\n", + __func__); + goto finish; + } + code = AR_CMD_RF_INIT; + } else { + code = AR_CMD_FREQUENCY; + } + + if ((error = otus_set_rf_bank4(sc, c)) != 0) + goto finish; + + tmp = (sc->txmask == 0x5) ? 0x340 : 0x240; + otus_write(sc, AR_PHY_TURBO, tmp); + if ((error = otus_write_barrier(sc)) != 0) + goto finish; + + /* Send firmware command to set channel. */ + cmd.freq = htole32((uint32_t)c->ic_freq * 1000); + cmd.dynht2040 = htole32(0); + cmd.htena = htole32(1); + /* Set Delta Slope (exponent and mantissa). */ + coeff = (100 << 24) / c->ic_freq; + otus_get_delta_slope(coeff, &exp, &man); + cmd.dsc_exp = htole32(exp); + cmd.dsc_man = htole32(man); + OTUS_DPRINTF(sc, OTUS_DEBUG_RESET, + "ds coeff=%u exp=%u man=%u\n", coeff, exp, man); + /* For Short GI, coeff is 9/10 that of normal coeff. */ + coeff = (9 * coeff) / 10; + otus_get_delta_slope(coeff, &exp, &man); + cmd.dsc_shgi_exp = htole32(exp); + cmd.dsc_shgi_man = htole32(man); + OTUS_DPRINTF(sc, OTUS_DEBUG_RESET, + "ds shgi coeff=%u exp=%u man=%u\n", coeff, exp, man); + /* Set wait time for AGC and noise calibration (100 or 200ms). */ + cmd.check_loop_count = assoc ? htole32(2000) : htole32(1000); + OTUS_DPRINTF(sc, OTUS_DEBUG_RESET, + "%s\n", (code == AR_CMD_RF_INIT) ? "RF_INIT" : "FREQUENCY"); + error = otus_cmd(sc, code, &cmd, sizeof cmd, &rsp); + if (error != 0) + goto finish; + if ((rsp.status & htole32(AR_CAL_ERR_AGC | AR_CAL_ERR_NF_VAL)) != 0) { + OTUS_DPRINTF(sc, OTUS_DEBUG_RESET, + "status=0x%x\n", le32toh(rsp.status)); + /* Force cold reset on next channel. */ + sc->bb_reset = 1; + } +#ifdef USB_DEBUG + if (otus_debug & OTUS_DEBUG_RESET) { + device_printf(sc->sc_dev, "calibration status=0x%x\n", + le32toh(rsp.status)); + for (i = 0; i < 2; i++) { /* 2 Rx chains */ + /* Sign-extend 9-bit NF values. */ + device_printf(sc->sc_dev, + "noisefloor chain %d=%d\n", i, + (((int32_t)le32toh(rsp.nf[i])) << 4) >> 23); + device_printf(sc->sc_dev, + "noisefloor ext chain %d=%d\n", i, + ((int32_t)le32toh(rsp.nf_ext[i])) >> 23); + } + } +#endif + for (i = 0; i < OTUS_NUM_CHAINS; i++) { + sc->sc_nf[i] = ((((int32_t)le32toh(rsp.nf[i])) << 4) >> 23); + } + sc->sc_curchan = c; +finish: + return (error); +} + +#ifdef notyet +int +otus_set_key(struct ieee80211com *ic, struct ieee80211_node *ni, + struct ieee80211_key *k) +{ + struct otus_softc *sc = ic->ic_softc; + struct otus_cmd_key cmd; + + /* Defer setting of WEP keys until interface is brought up. */ + if ((ic->ic_if.if_flags & (IFF_UP | IFF_RUNNING)) != + (IFF_UP | IFF_RUNNING)) + return 0; + + /* Do it in a process context. */ + cmd.key = *k; + cmd.associd = (ni != NULL) ? ni->ni_associd : 0; + otus_do_async(sc, otus_set_key_cb, &cmd, sizeof cmd); + return 0; +} + +void +otus_set_key_cb(struct otus_softc *sc, void *arg) +{ + struct otus_cmd_key *cmd = arg; + struct ieee80211_key *k = &cmd->key; + struct ar_cmd_ekey key; + uint16_t cipher; + int error; + + memset(&key, 0, sizeof key); + if (k->k_flags & IEEE80211_KEY_GROUP) { + key.uid = htole16(k->k_id); + IEEE80211_ADDR_COPY(key.macaddr, sc->sc_ic.ic_myaddr); + key.macaddr[0] |= 0x80; + } else { + key.uid = htole16(OTUS_UID(cmd->associd)); + IEEE80211_ADDR_COPY(key.macaddr, ni->ni_macaddr); + } + key.kix = htole16(0); + /* Map net80211 cipher to hardware. */ + switch (k->k_cipher) { + case IEEE80211_CIPHER_WEP40: + cipher = AR_CIPHER_WEP64; + break; + case IEEE80211_CIPHER_WEP104: + cipher = AR_CIPHER_WEP128; + break; + case IEEE80211_CIPHER_TKIP: + cipher = AR_CIPHER_TKIP; + break; + case IEEE80211_CIPHER_CCMP: + cipher = AR_CIPHER_AES; + break; + default: + return; + } + key.cipher = htole16(cipher); + memcpy(key.key, k->k_key, MIN(k->k_len, 16)); + error = otus_cmd(sc, AR_CMD_EKEY, &key, sizeof key, NULL); + if (error != 0 || k->k_cipher != IEEE80211_CIPHER_TKIP) + return; + + /* TKIP: set Tx/Rx MIC Key. */ + key.kix = htole16(1); + memcpy(key.key, k->k_key + 16, 16); + (void)otus_cmd(sc, AR_CMD_EKEY, &key, sizeof key, NULL); +} + +void +otus_delete_key(struct ieee80211com *ic, struct ieee80211_node *ni, + struct ieee80211_key *k) +{ + struct otus_softc *sc = ic->ic_softc; + struct otus_cmd_key cmd; + + if (!(ic->ic_if.if_flags & IFF_RUNNING) || + ic->ic_state != IEEE80211_S_RUN) + return; /* Nothing to do. */ + + /* Do it in a process context. */ + cmd.key = *k; + cmd.associd = (ni != NULL) ? ni->ni_associd : 0; + otus_do_async(sc, otus_delete_key_cb, &cmd, sizeof cmd); +} + +void +otus_delete_key_cb(struct otus_softc *sc, void *arg) +{ + struct otus_cmd_key *cmd = arg; + struct ieee80211_key *k = &cmd->key; + uint32_t uid; + + if (k->k_flags & IEEE80211_KEY_GROUP) + uid = htole32(k->k_id); + else + uid = htole32(OTUS_UID(cmd->associd)); + (void)otus_cmd(sc, AR_CMD_DKEY, &uid, sizeof uid, NULL); +} +#endif + +/* + * XXX TODO: check if we have to be doing any calibration in the host + * or whether it's purely a firmware thing. + */ +void +otus_calibrate_to(void *arg, int pending) +{ +#if 0 + struct otus_softc *sc = arg; + + device_printf(sc->sc_dev, "%s: called\n", __func__); + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni; + int s; + + if (usbd_is_dying(sc->sc_udev)) + return; + + usbd_ref_incr(sc->sc_udev); + + s = splnet(); + ni = ic->ic_bss; + ieee80211_amrr_choose(&sc->amrr, ni, &((struct otus_node *)ni)->amn); + splx(s); + + if (!usbd_is_dying(sc->sc_udev)) + timeout_add_sec(&sc->calib_to, 1); + + usbd_ref_decr(sc->sc_udev); +#endif +} + +int +otus_set_bssid(struct otus_softc *sc, const uint8_t *bssid) +{ + + OTUS_LOCK_ASSERT(sc); + + otus_write(sc, AR_MAC_REG_BSSID_L, + bssid[0] | bssid[1] << 8 | bssid[2] << 16 | bssid[3] << 24); + otus_write(sc, AR_MAC_REG_BSSID_H, + bssid[4] | bssid[5] << 8); + return otus_write_barrier(sc); +} + +int +otus_set_macaddr(struct otus_softc *sc, const uint8_t *addr) +{ + OTUS_LOCK_ASSERT(sc); + + otus_write(sc, AR_MAC_REG_MAC_ADDR_L, + addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24); + otus_write(sc, AR_MAC_REG_MAC_ADDR_H, + addr[4] | addr[5] << 8); + return otus_write_barrier(sc); +} + +/* Default single-LED. */ +void +otus_led_newstate_type1(struct otus_softc *sc) +{ + /* TBD */ + device_printf(sc->sc_dev, "%s: TODO\n", __func__); +} + +/* NETGEAR, dual-LED. */ +void +otus_led_newstate_type2(struct otus_softc *sc) +{ + /* TBD */ + device_printf(sc->sc_dev, "%s: TODO\n", __func__); +} + +/* NETGEAR, single-LED/3 colors (blue, red, purple.) */ +void +otus_led_newstate_type3(struct otus_softc *sc) +{ +#if 0 + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + + uint32_t state = sc->led_state; + + OTUS_LOCK_ASSERT(sc); + + if (!vap) { + state = 0; /* led off */ + } else if (vap->iv_state == IEEE80211_S_INIT) { + state = 0; /* LED off. */ + } else if (vap->iv_state == IEEE80211_S_RUN) { + /* Associated, LED always on. */ + if (IEEE80211_IS_CHAN_2GHZ(sc->sc_curchan)) + state = AR_LED0_ON; /* 2GHz=>Red. */ + else + state = AR_LED1_ON; /* 5GHz=>Blue. */ + } else { + /* Scanning, blink LED. */ + state ^= AR_LED0_ON | AR_LED1_ON; + if (IEEE80211_IS_CHAN_2GHZ(sc->sc_curchan)) + state &= ~AR_LED1_ON; + else + state &= ~AR_LED0_ON; + } + if (state != sc->led_state) { + otus_write(sc, 0x1d0104, state); + if (otus_write_barrier(sc) == 0) + sc->led_state = state; + } +#endif +} + +int +otus_init(struct otus_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + int error; + + OTUS_UNLOCK_ASSERT(sc); + + OTUS_LOCK(sc); + + /* Drain any pending TX frames */ + otus_drain_mbufq(sc); + + /* Init MAC */ + if ((error = otus_init_mac(sc)) != 0) { + OTUS_UNLOCK(sc); + device_printf(sc->sc_dev, + "%s: could not initialize MAC\n", __func__); + return error; + } + + (void) otus_set_macaddr(sc, ic->ic_macaddr); + +#if 0 + switch (ic->ic_opmode) { +#ifdef notyet +#ifndef IEEE80211_STA_ONLY + case IEEE80211_M_HOSTAP: + otus_write(sc, 0x1c3700, 0x0f0000a1); + otus_write(sc, 0x1c3c40, 0x1); + break; + case IEEE80211_M_IBSS: + otus_write(sc, 0x1c3700, 0x0f000000); + otus_write(sc, 0x1c3c40, 0x1); + break; +#endif +#endif + case IEEE80211_M_STA: + otus_write(sc, 0x1c3700, 0x0f000002); + otus_write(sc, 0x1c3c40, 0x1); + break; + default: + break; + } +#endif + + /* Expect STA operation */ + otus_write(sc, 0x1c3700, 0x0f000002); + otus_write(sc, 0x1c3c40, 0x1); + + /* XXX ic_opmode? */ + otus_write(sc, AR_MAC_REG_SNIFFER, + (ic->ic_opmode == IEEE80211_M_MONITOR) ? 0x2000001 : 0x2000000); + (void)otus_write_barrier(sc); + + sc->bb_reset = 1; /* Force cold reset. */ + + if ((error = otus_set_chan(sc, ic->ic_curchan, 0)) != 0) { + OTUS_UNLOCK(sc); + device_printf(sc->sc_dev, + "%s: could not set channel\n", __func__); + return error; + } + + /* Start Rx. */ + otus_write(sc, 0x1c3d30, 0x100); + (void)otus_write_barrier(sc); + + sc->sc_running = 1; + + OTUS_UNLOCK(sc); + return 0; +} + +void +otus_stop(struct otus_softc *sc) +{ +#if 0 + int s; +#endif + + OTUS_UNLOCK_ASSERT(sc); + + OTUS_LOCK(sc); + sc->sc_running = 0; + sc->sc_tx_timer = 0; + OTUS_UNLOCK(sc); + + taskqueue_drain_timeout(taskqueue_thread, &sc->scan_to); + taskqueue_drain_timeout(taskqueue_thread, &sc->calib_to); + taskqueue_drain(taskqueue_thread, &sc->tx_task); + taskqueue_drain(taskqueue_thread, &sc->wme_update_task); + + OTUS_LOCK(sc); + sc->sc_running = 0; + /* Stop Rx. */ + otus_write(sc, 0x1c3d30, 0); + (void)otus_write_barrier(sc); + + /* Drain any pending TX frames */ + otus_drain_mbufq(sc); + + OTUS_UNLOCK(sc); +} diff --git a/sys/dev/otus/if_otusreg.h b/sys/dev/otus/if_otusreg.h new file mode 100644 index 00000000000..7381c5674dc --- /dev/null +++ b/sys/dev/otus/if_otusreg.h @@ -0,0 +1,1025 @@ +/* $OpenBSD: if_otusreg.h,v 1.9 2013/11/26 20:33:18 deraadt Exp $ */ + +/*- + * Copyright (c) 2009 Damien Bergamini + * Copyright (c) 2007-2008 Atheros Communications, Inc. + * Copyright (c) 2015 Adrian Chadd + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $FreeBSD$ + */ +#ifndef __IF_OTUSREG_H__ +#define __IF_OTUSREG_H__ + +/* USB Endpoints addresses. */ +#define AR_EPT_BULK_TX_NO (UE_DIR_OUT | 1) +#define AR_EPT_BULK_RX_NO (UE_DIR_IN | 2) +#define AR_EPT_INTR_RX_NO (UE_DIR_IN | 3) +#define AR_EPT_INTR_TX_NO (UE_DIR_OUT | 4) + +/* USB Requests. */ +#define AR_FW_DOWNLOAD 0x30 +#define AR_FW_DOWNLOAD_COMPLETE 0x31 + +/* Maximum number of writes that can fit in a single FW command is 7. */ +#define AR_MAX_WRITE_IDX 6 /* 56 bytes */ + +#define AR_FW_INIT_ADDR 0x102800 +#define AR_FW_MAIN_ADDR 0x200000 +#define AR_USB_MODE_CTRL 0x1e1108 + +/* + * AR9170 MAC registers. + */ +#define AR_MAC_REG_BASE 0x1c3000 +#define AR_MAC_REG_MAC_ADDR_L (AR_MAC_REG_BASE + 0x610) +#define AR_MAC_REG_MAC_ADDR_H (AR_MAC_REG_BASE + 0x614) +#define AR_MAC_REG_BSSID_L (AR_MAC_REG_BASE + 0x618) +#define AR_MAC_REG_BSSID_H (AR_MAC_REG_BASE + 0x61c) +#define AR_MAC_REG_GROUP_HASH_TBL_L (AR_MAC_REG_BASE + 0x624) +#define AR_MAC_REG_GROUP_HASH_TBL_H (AR_MAC_REG_BASE + 0x628) +#define AR_MAC_REG_BASIC_RATE (AR_MAC_REG_BASE + 0x630) +#define AR_MAC_REG_MANDATORY_RATE (AR_MAC_REG_BASE + 0x634) +#define AR_MAC_REG_RTS_CTS_RATE (AR_MAC_REG_BASE + 0x638) +#define AR_MAC_REG_BACKOFF_PROTECT (AR_MAC_REG_BASE + 0x63c) +#define AR_MAC_REG_RX_THRESHOLD (AR_MAC_REG_BASE + 0x640) +#define AR_MAC_REG_RX_PE_DELAY (AR_MAC_REG_BASE + 0x64c) +#define AR_MAC_REG_DYNAMIC_SIFS_ACK (AR_MAC_REG_BASE + 0x658) +#define AR_MAC_REG_SNIFFER (AR_MAC_REG_BASE + 0x674) +#define AR_MAC_REG_ACK_EXTENSION (AR_MAC_REG_BASE + 0x690) +#define AR_MAC_REG_EIFS_AND_SIFS (AR_MAC_REG_BASE + 0x698) +#define AR_MAC_REG_BUSY (AR_MAC_REG_BASE + 0x6e8) +#define AR_MAC_REG_BUSY_EXT (AR_MAC_REG_BASE + 0x6ec) +#define AR_MAC_REG_SLOT_TIME (AR_MAC_REG_BASE + 0x6f0) +#define AR_MAC_REG_AC0_CW (AR_MAC_REG_BASE + 0xb00) +#define AR_MAC_REG_AC1_CW (AR_MAC_REG_BASE + 0xb04) +#define AR_MAC_REG_AC2_CW (AR_MAC_REG_BASE + 0xb08) +#define AR_MAC_REG_AC3_CW (AR_MAC_REG_BASE + 0xb0c) +#define AR_MAC_REG_AC4_CW (AR_MAC_REG_BASE + 0xb10) +#define AR_MAC_REG_AC1_AC0_AIFS (AR_MAC_REG_BASE + 0xb14) +#define AR_MAC_REG_AC3_AC2_AIFS (AR_MAC_REG_BASE + 0xb18) +#define AR_MAC_REG_RETRY_MAX (AR_MAC_REG_BASE + 0xb28) +#define AR_MAC_REG_TXOP_NOT_ENOUGH_INDICATION \ + (AR_MAC_REG_BASE + 0xb30) +#define AR_MAC_REG_AC1_AC0_TXOP (AR_MAC_REG_BASE + 0xb44) +#define AR_MAC_REG_AC3_AC2_TXOP (AR_MAC_REG_BASE + 0xb48) +#define AR_MAC_REG_OFDM_PHY_ERRORS (AR_MAC_REG_BASE + 0xcb4) +#define AR_MAC_REG_CCK_PHY_ERRORS (AR_MAC_REG_BASE + 0xcb8) +#define AR_MAC_REG_BCN_HT1 (AR_MAC_REG_BASE + 0xda0) + +/* Possible values for register AR_USB_MODE_CTRL. */ +#define AR_USB_DS_ENA (1 << 0) +#define AR_USB_US_ENA (1 << 1) +#define AR_USB_US_PACKET_MODE (1 << 3) +#define AR_USB_RX_STREAM_4K (0 << 4) +#define AR_USB_RX_STREAM_8K (1 << 4) +#define AR_USB_RX_STREAM_16K (2 << 4) +#define AR_USB_RX_STREAM_32K (3 << 4) +#define AR_USB_TX_STREAM_MODE (1 << 6) + +#define AR_LED0_ON (1 << 0) +#define AR_LED1_ON (1 << 1) + +/* + * PHY registers. + */ +#define AR_PHY_BASE 0x1c5800 +#define AR_PHY(reg) (AR_PHY_BASE + (reg) * 4) +#define AR_PHY_TURBO (AR_PHY_BASE + 0x0004) +#define AR_PHY_RF_CTL3 (AR_PHY_BASE + 0x0028) +#define AR_PHY_RF_CTL4 (AR_PHY_BASE + 0x0034) +#define AR_PHY_SETTLING (AR_PHY_BASE + 0x0044) +#define AR_PHY_RXGAIN (AR_PHY_BASE + 0x0048) +#define AR_PHY_DESIRED_SZ (AR_PHY_BASE + 0x0050) +#define AR_PHY_FIND_SIG (AR_PHY_BASE + 0x0058) +#define AR_PHY_AGC_CTL1 (AR_PHY_BASE + 0x005c) +#define AR_PHY_SFCORR (AR_PHY_BASE + 0x0068) +#define AR_PHY_SFCORR_LOW (AR_PHY_BASE + 0x006c) +#define AR_PHY_TIMING_CTRL4 (AR_PHY_BASE + 0x0120) +#define AR_PHY_TIMING5 (AR_PHY_BASE + 0x0124) +#define AR_PHY_POWER_TX_RATE1 (AR_PHY_BASE + 0x0134) +#define AR_PHY_POWER_TX_RATE2 (AR_PHY_BASE + 0x0138) +#define AR_PHY_POWER_TX_RATE_MAX (AR_PHY_BASE + 0x013c) +#define AR_PHY_SWITCH_CHAIN_0 (AR_PHY_BASE + 0x0160) +#define AR_PHY_SWITCH_COM (AR_PHY_BASE + 0x0164) +#define AR_PHY_HEAVY_CLIP_ENABLE (AR_PHY_BASE + 0x01e0) +#define AR_PHY_CCK_DETECT (AR_PHY_BASE + 0x0a08) +#define AR_PHY_GAIN_2GHZ (AR_PHY_BASE + 0x0a0c) +#define AR_PHY_POWER_TX_RATE3 (AR_PHY_BASE + 0x0a34) +#define AR_PHY_POWER_TX_RATE4 (AR_PHY_BASE + 0x0a38) +#define AR_PHY_TPCRG1 (AR_PHY_BASE + 0x0a58) +#define AR_PHY_POWER_TX_RATE5 (AR_PHY_BASE + 0x0b8c) +#define AR_PHY_POWER_TX_RATE6 (AR_PHY_BASE + 0x0b90) +#define AR_PHY_POWER_TX_RATE7 (AR_PHY_BASE + 0x0bcc) +#define AR_PHY_POWER_TX_RATE8 (AR_PHY_BASE + 0x0bd0) +#define AR_PHY_POWER_TX_RATE9 (AR_PHY_BASE + 0x0bd4) +#define AR_PHY_CCA (AR_PHY_BASE + 0x3064) + +#define AR_SEEPROM_HW_TYPE_OFFSET 0x1374 +#define AR_EEPROM_OFFSET 0x1600 + +#define AR_BANK4_CHUP (1 << 0) +#define AR_BANK4_BMODE_LF_SYNTH_FREQ (1 << 1) +#define AR_BANK4_AMODE_REFSEL(x) ((x) << 2) +#define AR_BANK4_ADDR(x) ((x) << 5) + +/* Tx descriptor. */ +struct ar_tx_head { + uint16_t len; + uint16_t macctl; +#define AR_TX_MAC_RTS (1 << 0) +#define AR_TX_MAC_CTS (1 << 1) +#define AR_TX_MAC_BACKOFF (1 << 3) +#define AR_TX_MAC_NOACK (1 << 2) +#define AR_TX_MAC_HW_DUR (1 << 9) +#define AR_TX_MAC_QID(qid) ((qid) << 10) +#define AR_TX_MAC_RATE_PROBING (1 << 15) + + uint32_t phyctl; +/* Modulation type. */ +#define AR_TX_PHY_MT_CCK 0 +#define AR_TX_PHY_MT_OFDM 1 +#define AR_TX_PHY_MT_HT 2 +#define AR_TX_PHY_GF (1 << 2) +#define AR_TX_PHY_BW_SHIFT 3 +#define AR_TX_PHY_TPC_SHIFT 9 +#define AR_TX_PHY_ANTMSK(msk) ((msk) << 15) +#define AR_TX_PHY_MCS(mcs) ((mcs) << 18) +#define AR_TX_PHY_SHGI (1U << 31) +} __packed; + +/* USB Rx stream mode header. */ +struct ar_rx_head { + uint16_t len; + uint16_t tag; +#define AR_RX_HEAD_TAG 0x4e00 +} __packed; + +/* Rx descriptor. */ +struct ar_rx_tail { + uint8_t rssi_ant[3]; + uint8_t rssi_ant_ext[3]; + uint8_t rssi; /* Combined RSSI. */ + uint8_t evm[2][6]; /* Error Vector Magnitude. */ + uint8_t phy_err; + uint8_t sa_idx; + uint8_t da_idx; + uint8_t error; +#define AR_RX_ERROR_TIMEOUT (1 << 0) +#define AR_RX_ERROR_OVERRUN (1 << 1) +#define AR_RX_ERROR_DECRYPT (1 << 2) +#define AR_RX_ERROR_FCS (1 << 3) +#define AR_RX_ERROR_BAD_RA (1 << 4) +#define AR_RX_ERROR_PLCP (1 << 5) +#define AR_RX_ERROR_MMIC (1 << 6) + + uint8_t status; +/* Modulation type (same as AR_TX_PHY_MT). */ +#define AR_RX_STATUS_MT_MASK 0x3 +#define AR_RX_STATUS_MT_CCK 0 +#define AR_RX_STATUS_MT_OFDM 1 +#define AR_RX_STATUS_MT_HT 2 +#define AR_RX_STATUS_SHPREAMBLE (1 << 3) +} __packed; + +#define AR_PLCP_HDR_LEN 12 +/* Magic PLCP header for firmware notifications through Rx bulk pipe. */ +static uint8_t AR_PLCP_HDR_INTR[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +/* Firmware command/reply header. */ +struct ar_cmd_hdr { + uint8_t len; + uint8_t code; +#define AR_CMD_RREG 0x00 +#define AR_CMD_WREG 0x01 +#define AR_CMD_RMEM 0x02 +#define AR_CMD_WMEM 0x03 +#define AR_CMD_BITAND 0x04 +#define AR_CMD_BITOR 0x05 +#define AR_CMD_EKEY 0x28 +#define AR_CMD_DKEY 0x29 +#define AR_CMD_FREQUENCY 0x30 +#define AR_CMD_RF_INIT 0x31 +#define AR_CMD_SYNTH 0x32 +#define AR_CMD_FREQ_STRAT 0x33 +#define AR_CMD_ECHO 0x80 +#define AR_CMD_TALLY 0x81 +#define AR_CMD_TALLY_APD 0x82 +#define AR_CMD_CONFIG 0x83 +#define AR_CMD_RESET 0x90 +#define AR_CMD_DKRESET 0x91 +#define AR_CMD_DKTX_STATUS 0x92 +#define AR_CMD_FDC 0xa0 +#define AR_CMD_WREEPROM 0xb0 +#define AR_CMD_WFLASH AR_CMD_WREEPROM +#define AR_CMD_FLASH_ERASE 0xb1 +#define AR_CMD_FLASH_PROG 0xb2 +#define AR_CMD_FLASH_CHKSUM 0xb3 +#define AR_CMD_FLASH_READ 0xb4 +#define AR_CMD_FW_DL_INIT 0xb5 +#define AR_CMD_MEM_WREEPROM 0xbb +/* Those have the 2 MSB set to 1. */ +#define AR_EVT_BEACON 0x00 +#define AR_EVT_TX_COMP 0x01 +#define AR_EVT_TBTT 0x02 +#define AR_EVT_ATIM 0x03 +#define AR_EVT_DO_BB_RESET 0x09 + + uint16_t token; /* Driver private data. */ +} __packed; + +/* Structure for command AR_CMD_RF_INIT/AR_CMD_FREQUENCY. */ +struct ar_cmd_frequency { + uint32_t freq; + uint32_t dynht2040; + uint32_t htena; + uint32_t dsc_exp; + uint32_t dsc_man; + uint32_t dsc_shgi_exp; + uint32_t dsc_shgi_man; + uint32_t check_loop_count; +} __packed; + +/* Firmware reply for command AR_CMD_FREQUENCY. */ +struct ar_rsp_frequency { + uint32_t status; +#define AR_CAL_ERR_AGC (1 << 0) /* AGC cal unfinished. */ +#define AR_CAL_ERR_NF (1 << 1) /* Noise cal unfinished. */ +#define AR_CAL_ERR_NF_VAL (1 << 2) /* NF value unexpected. */ + + uint32_t nf[3]; /* Noisefloor. */ + uint32_t nf_ext[3]; /* Noisefloor ext. */ +} __packed; + +/* Structure for command AR_CMD_EKEY. */ +struct ar_cmd_ekey { + uint16_t uid; /* user ID */ + uint16_t kix; + uint16_t cipher; +#define AR_CIPHER_NONE 0 +#define AR_CIPHER_WEP64 1 +#define AR_CIPHER_TKIP 2 +#define AR_CIPHER_AES 4 +#define AR_CIPHER_WEP128 5 +#define AR_CIPHER_WEP256 6 +#define AR_CIPHER_CENC 7 + + uint8_t macaddr[IEEE80211_ADDR_LEN]; + uint8_t key[16]; +} __packed; + +/* Structure for event AR_EVT_TX_COMP. */ +struct ar_evt_tx_comp { + uint8_t macaddr[IEEE80211_ADDR_LEN]; + uint32_t phy; + uint16_t status; +#define AR_TX_STATUS_COMP 0 +#define AR_TX_STATUS_RETRY_COMP 1 +#define AR_TX_STATUS_FAILED 2 +} __packed; + +/* List of supported channels. */ +static const uint8_t ar_chans[] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, 116, 120, 124, + 128, 132, 136, 140, 149, 153, 157, 161, 165, 34, 38, 42, 46 +}; + +/* + * This data is automatically generated from the "otus.ini" file. + * It is stored in a different way though, to reduce kernel's .rodata + * section overhead (5.1KB instead of 8.5KB). + */ + +/* NB: apply AR_PHY(). */ +static const uint16_t ar5416_phy_regs[] = { + 0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007, 0x008, + 0x009, 0x00a, 0x00b, 0x00c, 0x00d, 0x00e, 0x00f, 0x010, 0x011, + 0x012, 0x013, 0x014, 0x015, 0x016, 0x017, 0x018, 0x01a, 0x01b, + 0x040, 0x041, 0x042, 0x043, 0x045, 0x046, 0x047, 0x048, 0x049, + 0x04a, 0x04b, 0x04d, 0x04e, 0x04f, 0x051, 0x052, 0x053, 0x055, + 0x056, 0x058, 0x059, 0x05c, 0x05d, 0x05e, 0x05f, 0x060, 0x061, + 0x062, 0x063, 0x064, 0x065, 0x066, 0x067, 0x068, 0x069, 0x06a, + 0x06b, 0x06c, 0x06d, 0x070, 0x071, 0x072, 0x073, 0x074, 0x075, + 0x076, 0x077, 0x078, 0x079, 0x07a, 0x07b, 0x07c, 0x07f, 0x080, + 0x081, 0x082, 0x083, 0x084, 0x085, 0x086, 0x087, 0x088, 0x089, + 0x08a, 0x08b, 0x08c, 0x08d, 0x08e, 0x08f, 0x090, 0x091, 0x092, + 0x093, 0x094, 0x095, 0x096, 0x097, 0x098, 0x099, 0x09a, 0x09b, + 0x09c, 0x09d, 0x09e, 0x09f, 0x0a0, 0x0a1, 0x0a2, 0x0a3, 0x0a4, + 0x0a5, 0x0a6, 0x0a7, 0x0a8, 0x0a9, 0x0aa, 0x0ab, 0x0ac, 0x0ad, + 0x0ae, 0x0af, 0x0b0, 0x0b1, 0x0b2, 0x0b3, 0x0b4, 0x0b5, 0x0b6, + 0x0b7, 0x0b8, 0x0b9, 0x0ba, 0x0bb, 0x0bc, 0x0bd, 0x0be, 0x0bf, + 0x0c0, 0x0c1, 0x0c2, 0x0c3, 0x0c4, 0x0c5, 0x0c6, 0x0c7, 0x0c8, + 0x0c9, 0x0ca, 0x0cb, 0x0cc, 0x0cd, 0x0ce, 0x0cf, 0x0d0, 0x0d1, + 0x0d2, 0x0d3, 0x0d4, 0x0d5, 0x0d6, 0x0d7, 0x0d8, 0x0d9, 0x0da, + 0x0db, 0x0dc, 0x0dd, 0x0de, 0x0df, 0x0e0, 0x0e1, 0x0e2, 0x0e3, + 0x0e4, 0x0e5, 0x0e6, 0x0e7, 0x0e8, 0x0e9, 0x0ea, 0x0eb, 0x0ec, + 0x0ed, 0x0ee, 0x0ef, 0x0f0, 0x0f1, 0x0f2, 0x0f3, 0x0f4, 0x0f5, + 0x0f6, 0x0f7, 0x0f8, 0x0f9, 0x0fa, 0x0fb, 0x0fc, 0x0fd, 0x0fe, + 0x0ff, 0x100, 0x103, 0x104, 0x105, 0x106, 0x107, 0x108, 0x109, + 0x10a, 0x10b, 0x10c, 0x10d, 0x10e, 0x10f, 0x13c, 0x13d, 0x13e, + 0x13f, 0x280, 0x281, 0x282, 0x283, 0x284, 0x285, 0x286, 0x287, + 0x288, 0x289, 0x28a, 0x28b, 0x28c, 0x28d, 0x28e, 0x28f, 0x290, + 0x291, 0x292, 0x293, 0x294, 0x295, 0x296, 0x297, 0x298, 0x299, + 0x29a, 0x29b, 0x29d, 0x29e, 0x29f, 0x2c0, 0x2c1, 0x2c2, 0x2c3, + 0x2c4, 0x2c5, 0x2c6, 0x2c7, 0x2c8, 0x2c9, 0x2ca, 0x2cb, 0x2cc, + 0x2cd, 0x2ce, 0x2cf, 0x2d0, 0x2d1, 0x2d2, 0x2d3, 0x2d4, 0x2d5, + 0x2d6, 0x2e2, 0x2e3, 0x2e4, 0x2e5, 0x2e6, 0x2e7, 0x2e8, 0x2e9, + 0x2ea, 0x2eb, 0x2ec, 0x2ed, 0x2ee, 0x2ef, 0x2f0, 0x2f1, 0x2f2, + 0x2f3, 0x2f4, 0x2f5, 0x2f6, 0x2f7, 0x2f8, 0x412, 0x448, 0x458, + 0x683, 0x69b, 0x812, 0x848, 0x858, 0xa83, 0xa9b, 0xc19, 0xc57, + 0xc5a, 0xc6f, 0xe9c, 0xed7, 0xed8, 0xed9, 0xeda, 0xedb, 0xedc, + 0xedd, 0xede, 0xedf, 0xee0, 0xee1 +}; + +static const uint32_t ar5416_phy_vals_5ghz_20mhz[] = { + 0x00000007, 0x00000300, 0x00000000, 0xad848e19, 0x7d14e000, + 0x9c0a9f6b, 0x00000090, 0x00000000, 0x02020200, 0x00000e0e, + 0x0a020001, 0x0000a000, 0x00000000, 0x00000e0e, 0x00000007, + 0x00200400, 0x206a002e, 0x1372161e, 0x001a6a65, 0x1284233c, + 0x6c48b4e4, 0x00000859, 0x7ec80d2e, 0x31395c5e, 0x0004dd10, + 0x409a4190, 0x050cb081, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x000007d0, 0x00000118, 0x10000fff, 0x0510081c, + 0xd0058a15, 0x00000001, 0x00000004, 0x3f3f3f3f, 0x3f3f3f3f, + 0x0000007f, 0xdfb81020, 0x9280b212, 0x00020028, 0x5d50e188, + 0x00081fff, 0x00009b40, 0x00001120, 0x190fb515, 0x00000000, + 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000007, 0x001fff00, 0x006f00c4, 0x03051000, + 0x00000820, 0x038919be, 0x06336f77, 0x60f6532c, 0x08f186c8, + 0x00046384, 0x00000000, 0x00000000, 0x00000000, 0x00000200, + 0x64646464, 0x3c787878, 0x000000aa, 0x00000000, 0x00001042, + 0x00000000, 0x00000040, 0x00000080, 0x000001a1, 0x000001e1, + 0x00000021, 0x00000061, 0x00000168, 0x000001a8, 0x000001e8, + 0x00000028, 0x00000068, 0x00000189, 0x000001c9, 0x00000009, + 0x00000049, 0x00000089, 0x00000170, 0x000001b0, 0x000001f0, + 0x00000030, 0x00000070, 0x00000191, 0x000001d1, 0x00000011, + 0x00000051, 0x00000091, 0x000001b8, 0x000001f8, 0x00000038, + 0x00000078, 0x00000199, 0x000001d9, 0x00000019, 0x00000059, + 0x00000099, 0x000000d9, 0x000000f9, 0x000000f9, 0x000000f9, + 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, + 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, + 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, + 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, + 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, 0x00000000, + 0x00000001, 0x00000002, 0x00000003, 0x00000004, 0x00000005, + 0x00000008, 0x00000009, 0x0000000a, 0x0000000b, 0x0000000c, + 0x0000000d, 0x00000010, 0x00000011, 0x00000012, 0x00000013, + 0x00000014, 0x00000015, 0x00000018, 0x00000019, 0x0000001a, + 0x0000001b, 0x0000001c, 0x0000001d, 0x00000020, 0x00000021, + 0x00000022, 0x00000023, 0x00000024, 0x00000025, 0x00000028, + 0x00000029, 0x0000002a, 0x0000002b, 0x0000002c, 0x0000002d, + 0x00000030, 0x00000031, 0x00000032, 0x00000033, 0x00000034, + 0x00000035, 0x00000035, 0x00000035, 0x00000035, 0x00000035, + 0x00000035, 0x00000035, 0x00000035, 0x00000035, 0x00000035, + 0x00000035, 0x00000035, 0x00000035, 0x00000035, 0x00000035, + 0x00000035, 0x00000035, 0x00000035, 0x00000035, 0x00000035, + 0x00000035, 0x00000010, 0x0000001a, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000008, 0x00000440, 0xd6be4788, 0x012e8160, + 0x40806333, 0x00106c10, 0x009c4060, 0x1883800a, 0x018830c6, + 0x00000400, 0x000009b5, 0x00000000, 0x00000108, 0x3f3f3f3f, + 0x3f3f3f3f, 0x13c889af, 0x38490a20, 0x00007bb6, 0x0fff3ffc, + 0x00000001, 0x0000a000, 0x00000000, 0x0cc75380, 0x0f0f0f01, + 0xdfa91f01, 0x00418a11, 0x00000000, 0x09249126, 0x0a1a9caa, + 0x1ce739ce, 0x051701ce, 0x18010000, 0x30032602, 0x48073e06, + 0x560b4c0a, 0x641a600f, 0x7a4f6e1b, 0x8c5b7e5a, 0x9d0f96cf, + 0xb51fa69f, 0xcb3fbd07, 0x0000d7bf, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x0003ffff, 0x79a8aa1f, + 0x08000000, 0x3f3f3f3f, 0x3f3f3f3f, 0x1ce739ce, 0x000001ce, + 0x00000007, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, + 0x00000000, 0x1ce739ce, 0x000000c0, 0x00180a65, 0x0510001c, + 0x00009b40, 0x012e8160, 0x09249126, 0x00180a65, 0x0510001c, + 0x00009b40, 0x012e8160, 0x09249126, 0x0001c600, 0x004b6a8e, + 0x000003ce, 0x00181400, 0x00820820, 0x066c420f, 0x0f282207, + 0x17601685, 0x1f801104, 0x37a00c03, 0x3fc40883, 0x57c00803, + 0x5fd80682, 0x7fe00482, 0x7f3c7bba, 0xf3307ff0 +}; + +#ifdef notyet +static const uint32_t ar5416_phy_vals_5ghz_40mhz[] = { + 0x00000007, 0x000003c4, 0x00000000, 0xad848e19, 0x7d14e000, + 0x9c0a9f6b, 0x00000090, 0x00000000, 0x02020200, 0x00000e0e, + 0x0a020001, 0x0000a000, 0x00000000, 0x00000e0e, 0x00000007, + 0x00200400, 0x206a002e, 0x13721c1e, 0x001a6a65, 0x1284233c, + 0x6c48b4e4, 0x00000859, 0x7ec80d2e, 0x31395c5e, 0x0004dd10, + 0x409a4190, 0x050cb081, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x000007d0, 0x00000230, 0x10000fff, 0x0510081c, + 0xd0058a15, 0x00000001, 0x00000004, 0x3f3f3f3f, 0x3f3f3f3f, + 0x0000007f, 0xdfb81020, 0x9280b212, 0x00020028, 0x5d50e188, + 0x00081fff, 0x00009b40, 0x00001120, 0x190fb515, 0x00000000, + 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000007, 0x001fff00, 0x006f00c4, 0x03051000, + 0x00000820, 0x038919be, 0x06336f77, 0x60f6532c, 0x08f186c8, + 0x00046384, 0x00000000, 0x00000000, 0x00000000, 0x00000200, + 0x64646464, 0x3c787878, 0x000000aa, 0x00000000, 0x00001042, + 0x00000000, 0x00000040, 0x00000080, 0x000001a1, 0x000001e1, + 0x00000021, 0x00000061, 0x00000168, 0x000001a8, 0x000001e8, + 0x00000028, 0x00000068, 0x00000189, 0x000001c9, 0x00000009, + 0x00000049, 0x00000089, 0x00000170, 0x000001b0, 0x000001f0, + 0x00000030, 0x00000070, 0x00000191, 0x000001d1, 0x00000011, + 0x00000051, 0x00000091, 0x000001b8, 0x000001f8, 0x00000038, + 0x00000078, 0x00000199, 0x000001d9, 0x00000019, 0x00000059, + 0x00000099, 0x000000d9, 0x000000f9, 0x000000f9, 0x000000f9, + 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, + 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, + 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, + 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, + 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, 0x00000000, + 0x00000001, 0x00000002, 0x00000003, 0x00000004, 0x00000005, + 0x00000008, 0x00000009, 0x0000000a, 0x0000000b, 0x0000000c, + 0x0000000d, 0x00000010, 0x00000011, 0x00000012, 0x00000013, + 0x00000014, 0x00000015, 0x00000018, 0x00000019, 0x0000001a, + 0x0000001b, 0x0000001c, 0x0000001d, 0x00000020, 0x00000021, + 0x00000022, 0x00000023, 0x00000024, 0x00000025, 0x00000028, + 0x00000029, 0x0000002a, 0x0000002b, 0x0000002c, 0x0000002d, + 0x00000030, 0x00000031, 0x00000032, 0x00000033, 0x00000034, + 0x00000035, 0x00000035, 0x00000035, 0x00000035, 0x00000035, + 0x00000035, 0x00000035, 0x00000035, 0x00000035, 0x00000035, + 0x00000035, 0x00000035, 0x00000035, 0x00000035, 0x00000035, + 0x00000035, 0x00000035, 0x00000035, 0x00000035, 0x00000035, + 0x00000035, 0x00000010, 0x0000001a, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000008, 0x00000440, 0xd6be4788, 0x012e8160, + 0x40806333, 0x00106c10, 0x009c4060, 0x1883800a, 0x018830c6, + 0x00000400, 0x000009b5, 0x00000000, 0x00000210, 0x3f3f3f3f, + 0x3f3f3f3f, 0x13c889af, 0x38490a20, 0x00007bb6, 0x0fff3ffc, + 0x00000001, 0x0000a000, 0x00000000, 0x0cc75380, 0x0f0f0f01, + 0xdfa91f01, 0x00418a11, 0x00000000, 0x09249126, 0x0a1a9caa, + 0x1ce739ce, 0x051701ce, 0x18010000, 0x30032602, 0x48073e06, + 0x560b4c0a, 0x641a600f, 0x7a4f6e1b, 0x8c5b7e5a, 0x9d0f96cf, + 0xb51fa69f, 0xcb3fbcbf, 0x0000d7bf, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x0003ffff, 0x79a8aa1f, + 0x08000000, 0x3f3f3f3f, 0x3f3f3f3f, 0x1ce739ce, 0x000001ce, + 0x00000007, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, + 0x00000000, 0x1ce739ce, 0x000000c0, 0x00180a65, 0x0510001c, + 0x00009b40, 0x012e8160, 0x09249126, 0x00180a65, 0x0510001c, + 0x00009b40, 0x012e8160, 0x09249126, 0x0001c600, 0x004b6a8e, + 0x000003ce, 0x00181400, 0x00820820, 0x066c420f, 0x0f282207, + 0x17601685, 0x1f801104, 0x37a00c03, 0x3fc40883, 0x57c00803, + 0x5fd80682, 0x7fe00482, 0x7f3c7bba, 0xf3307ff0 +}; +#endif + +#ifdef notyet +static const uint32_t ar5416_phy_vals_2ghz_40mhz[] = { + 0x00000007, 0x000003c4, 0x00000000, 0xad848e19, 0x7d14e000, + 0x9c0a9f6b, 0x00000090, 0x00000000, 0x02020200, 0x00000e0e, + 0x0a020001, 0x0000a000, 0x00000000, 0x00000e0e, 0x00000007, + 0x00200400, 0x206a002e, 0x13721c24, 0x00197a68, 0x1284233c, + 0x6c48b0e4, 0x00000859, 0x7ec80d2e, 0x31395c5e, 0x0004dd20, + 0x409a4190, 0x050cb081, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000898, 0x00000268, 0x10000fff, 0x0510001c, + 0xd0058a15, 0x00000001, 0x00000004, 0x3f3f3f3f, 0x3f3f3f3f, + 0x0000007f, 0xdfb81020, 0x9280b212, 0x00020028, 0x5d50e188, + 0x00081fff, 0x00009b40, 0x00001120, 0x190fb515, 0x00000000, + 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000007, 0x001fff00, 0x006f00c4, 0x03051000, + 0x00000820, 0x038919be, 0x06336f77, 0x60f6532c, 0x08f186c8, + 0x00046384, 0x00000000, 0x00000000, 0x00000000, 0x00000200, + 0x64646464, 0x3c787878, 0x000000aa, 0x00000000, 0x00001042, + 0x00000000, 0x00000040, 0x00000080, 0x00000141, 0x00000181, + 0x000001c1, 0x00000001, 0x00000041, 0x000001a8, 0x000001e8, + 0x00000028, 0x00000068, 0x000000a8, 0x00000169, 0x000001a9, + 0x000001e9, 0x00000029, 0x00000069, 0x00000190, 0x000001d0, + 0x00000010, 0x00000050, 0x00000090, 0x00000151, 0x00000191, + 0x000001d1, 0x00000011, 0x00000051, 0x00000198, 0x000001d8, + 0x00000018, 0x00000058, 0x00000098, 0x00000159, 0x00000199, + 0x000001d9, 0x00000019, 0x00000059, 0x00000099, 0x000000d9, + 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, + 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, + 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, + 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, + 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, 0x00000000, + 0x00000001, 0x00000002, 0x00000003, 0x00000004, 0x00000005, + 0x00000008, 0x00000009, 0x0000000a, 0x0000000b, 0x0000000c, + 0x0000000d, 0x00000010, 0x00000011, 0x00000012, 0x00000013, + 0x00000014, 0x00000015, 0x00000018, 0x00000019, 0x0000001a, + 0x0000001b, 0x0000001c, 0x0000001d, 0x00000020, 0x00000021, + 0x00000022, 0x00000023, 0x00000024, 0x00000025, 0x00000028, + 0x00000029, 0x0000002a, 0x0000002b, 0x0000002c, 0x0000002d, + 0x00000030, 0x00000031, 0x00000032, 0x00000033, 0x00000034, + 0x00000035, 0x00000035, 0x00000035, 0x00000035, 0x00000035, + 0x00000035, 0x00000035, 0x00000035, 0x00000035, 0x00000035, + 0x00000035, 0x00000035, 0x00000035, 0x00000035, 0x00000035, + 0x00000035, 0x00000035, 0x00000035, 0x00000035, 0x00000035, + 0x00000035, 0x00000010, 0x0000001a, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x0000000e, 0x00000440, 0xd03e4788, 0x012a8160, + 0x40806333, 0x00106c10, 0x009c4060, 0x1883800a, 0x018830c6, + 0x00000400, 0x000009b5, 0x00000000, 0x00000210, 0x3f3f3f3f, + 0x3f3f3f3f, 0x13c889af, 0x38490a20, 0x00007bb6, 0x0fff3ffc, + 0x00000001, 0x0000a000, 0x00000000, 0x0cc75380, 0x0f0f0f01, + 0xdfa91f01, 0x00418a11, 0x00000000, 0x09249126, 0x0a1a7caa, + 0x1ce739ce, 0x051701ce, 0x18010000, 0x2e032402, 0x4a0a3c06, + 0x621a540b, 0x764f6c1b, 0x845b7a5a, 0x950f8ccf, 0xa5cf9b4f, + 0xbddfaf1f, 0xd1ffc93f, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x0003ffff, 0x79a8aa1f, + 0x08000000, 0x3f3f3f3f, 0x3f3f3f3f, 0x1ce739ce, 0x000001ce, + 0x00000007, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, + 0x00000000, 0x1ce739ce, 0x000000c0, 0x00180a68, 0x0510001c, + 0x00009b40, 0x012a8160, 0x09249126, 0x00180a68, 0x0510001c, + 0x00009b40, 0x012a8160, 0x09249126, 0x0001c600, 0x004b6a8e, + 0x000003ce, 0x00181400, 0x00820820, 0x066c420f, 0x0f282207, + 0x17601685, 0x1f801104, 0x37a00c03, 0x3fc40883, 0x57c00803, + 0x5fd80682, 0x7fe00482, 0x7f3c7bba, 0xf3307ff0 +}; +#endif + +static const uint32_t ar5416_phy_vals_2ghz_20mhz[] = { + 0x00000007, 0x00000300, 0x00000000, 0xad848e19, 0x7d14e000, + 0x9c0a9f6b, 0x00000090, 0x00000000, 0x02020200, 0x00000e0e, + 0x0a020001, 0x0000a000, 0x00000000, 0x00000e0e, 0x00000007, + 0x00200400, 0x206a002e, 0x137216a4, 0x00197a68, 0x1284233c, + 0x6c48b0e4, 0x00000859, 0x7ec80d2e, 0x31395c5e, 0x0004dd20, + 0x409a4190, 0x050cb081, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000898, 0x00000134, 0x10000fff, 0x0510001c, + 0xd0058a15, 0x00000001, 0x00000004, 0x3f3f3f3f, 0x3f3f3f3f, + 0x0000007f, 0xdfb81020, 0x9280b212, 0x00020028, 0x5d50e188, + 0x00081fff, 0x00009b40, 0x00001120, 0x190fb515, 0x00000000, + 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000007, 0x001fff00, 0x006f00c4, 0x03051000, + 0x00000820, 0x038919be, 0x06336f77, 0x60f6532c, 0x08f186c8, + 0x00046384, 0x00000000, 0x00000000, 0x00000000, 0x00000200, + 0x64646464, 0x3c787878, 0x000000aa, 0x00000000, 0x00001042, + 0x00000000, 0x00000040, 0x00000080, 0x00000141, 0x00000181, + 0x000001c1, 0x00000001, 0x00000041, 0x000001a8, 0x000001e8, + 0x00000028, 0x00000068, 0x000000a8, 0x00000169, 0x000001a9, + 0x000001e9, 0x00000029, 0x00000069, 0x00000190, 0x000001d0, + 0x00000010, 0x00000050, 0x00000090, 0x00000151, 0x00000191, + 0x000001d1, 0x00000011, 0x00000051, 0x00000198, 0x000001d8, + 0x00000018, 0x00000058, 0x00000098, 0x00000159, 0x00000199, + 0x000001d9, 0x00000019, 0x00000059, 0x00000099, 0x000000d9, + 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, + 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, + 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, + 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, + 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, 0x00000000, + 0x00000001, 0x00000002, 0x00000003, 0x00000004, 0x00000005, + 0x00000008, 0x00000009, 0x0000000a, 0x0000000b, 0x0000000c, + 0x0000000d, 0x00000010, 0x00000011, 0x00000012, 0x00000013, + 0x00000014, 0x00000015, 0x00000018, 0x00000019, 0x0000001a, + 0x0000001b, 0x0000001c, 0x0000001d, 0x00000020, 0x00000021, + 0x00000022, 0x00000023, 0x00000024, 0x00000025, 0x00000028, + 0x00000029, 0x0000002a, 0x0000002b, 0x0000002c, 0x0000002d, + 0x00000030, 0x00000031, 0x00000032, 0x00000033, 0x00000034, + 0x00000035, 0x00000035, 0x00000035, 0x00000035, 0x00000035, + 0x00000035, 0x00000035, 0x00000035, 0x00000035, 0x00000035, + 0x00000035, 0x00000035, 0x00000035, 0x00000035, 0x00000035, + 0x00000035, 0x00000035, 0x00000035, 0x00000035, 0x00000035, + 0x00000035, 0x00000010, 0x0000001a, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x0000000e, 0x00000440, 0xd03e4788, 0x012a8160, + 0x40806333, 0x00106c10, 0x009c4060, 0x1883800a, 0x018830c6, + 0x00000400, 0x000009b5, 0x00000000, 0x00000108, 0x3f3f3f3f, + 0x3f3f3f3f, 0x13c889af, 0x38490a20, 0x00007bb6, 0x0fff3ffc, + 0x00000001, 0x0000a000, 0x00000000, 0x0cc75380, 0x0f0f0f01, + 0xdfa91f01, 0x00418a11, 0x00000000, 0x09249126, 0x0a1a7caa, + 0x1ce739ce, 0x051701ce, 0x18010000, 0x2e032402, 0x4a0a3c06, + 0x621a540b, 0x764f6c1b, 0x845b7a5a, 0x950f8ccf, 0xa5cf9b4f, + 0xbddfaf1f, 0xd1ffc93f, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x0003ffff, 0x79a8aa1f, + 0x08000000, 0x3f3f3f3f, 0x3f3f3f3f, 0x1ce739ce, 0x000001ce, + 0x00000007, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, + 0x00000000, 0x1ce739ce, 0x000000c0, 0x00180a68, 0x0510001c, + 0x00009b40, 0x012a8160, 0x09249126, 0x00180a68, 0x0510001c, + 0x00009b40, 0x012a8160, 0x09249126, 0x0001c600, 0x004b6a8e, + 0x000003ce, 0x00181400, 0x00820820, 0x066c420f, 0x0f282207, + 0x17601685, 0x1f801104, 0x37a00c03, 0x3fc40883, 0x57c00803, + 0x5fd80682, 0x7fe00482, 0x7f3c7bba, 0xf3307ff0 +}; + +/* NB: apply AR_PHY(). */ +static const uint8_t ar5416_banks_regs[] = { + 0x2c, 0x38, 0x2c, 0x3b, 0x2c, 0x38, 0x3c, 0x2c, 0x3a, 0x2c, 0x39, + 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, + 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, + 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, + 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, + 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x38, 0x2c, 0x2c, + 0x2c, 0x3c +}; + +static const uint32_t ar5416_banks_vals_5ghz[] = { + 0x1e5795e5, 0x02008020, 0x02108421, 0x00000008, 0x0e73ff17, + 0x00000420, 0x01400018, 0x000001a1, 0x00000001, 0x00000013, + 0x00000002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00004000, 0x00006c00, 0x00002c00, 0x00004800, + 0x00004000, 0x00006000, 0x00001000, 0x00004000, 0x00007c00, + 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 0x00087c00, + 0x00007c00, 0x00005400, 0x00000c00, 0x00001800, 0x00007c00, + 0x00006c00, 0x00006c00, 0x00007c00, 0x00002c00, 0x00003c00, + 0x00003800, 0x00001c00, 0x00000800, 0x00000408, 0x00004c15, + 0x00004188, 0x0000201e, 0x00010408, 0x00000801, 0x00000c08, + 0x0000181e, 0x00001016, 0x00002800, 0x00004010, 0x0000081c, + 0x00000115, 0x00000015, 0x00000066, 0x0000001c, 0x00000000, + 0x00000004, 0x00000015, 0x0000001f, 0x00000000, 0x000000a0, + 0x00000000, 0x00000040, 0x0000001c +}; + +static const uint32_t ar5416_banks_vals_2ghz[] = { + 0x1e5795e5, 0x02008020, 0x02108421, 0x00000008, 0x0e73ff17, + 0x00000420, 0x01c00018, 0x000001a1, 0x00000001, 0x00000013, + 0x00000002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00004000, 0x00006c00, 0x00002c00, 0x00004800, + 0x00004000, 0x00006000, 0x00001000, 0x00004000, 0x00007c00, + 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 0x00087c00, + 0x00007c00, 0x00005400, 0x00000c00, 0x00001800, 0x00007c00, + 0x00006c00, 0x00006c00, 0x00007c00, 0x00002c00, 0x00003c00, + 0x00003800, 0x00001c00, 0x00000800, 0x00000408, 0x00004c15, + 0x00004188, 0x0000201e, 0x00010408, 0x00000801, 0x00000c08, + 0x0000181e, 0x00001016, 0x00002800, 0x00004010, 0x0000081c, + 0x00000115, 0x00000015, 0x00000066, 0x0000001c, 0x00000000, + 0x00000004, 0x00000015, 0x0000001f, 0x00000400, 0x000000a0, + 0x00000000, 0x00000040, 0x0000001c +}; + +/* + * EEPROM. + */ +/* Possible flags for opCapFlags. */ +#define AR5416_OPFLAGS_11A 0x01 +#define AR5416_OPFLAGS_11G 0x02 +#define AR5416_OPFLAGS_5G_HT40 0x04 +#define AR5416_OPFLAGS_2G_HT40 0x08 +#define AR5416_OPFLAGS_5G_HT20 0x10 +#define AR5416_OPFLAGS_2G_HT20 0x20 + +#define AR5416_NUM_5G_CAL_PIERS 8 +#define AR5416_NUM_2G_CAL_PIERS 4 +#define AR5416_NUM_5G_20_TARGET_POWERS 8 +#define AR5416_NUM_5G_40_TARGET_POWERS 8 +#define AR5416_NUM_2G_CCK_TARGET_POWERS 3 +#define AR5416_NUM_2G_20_TARGET_POWERS 4 +#define AR5416_NUM_2G_40_TARGET_POWERS 4 +#define AR5416_NUM_CTLS 24 +#define AR5416_NUM_BAND_EDGES 8 +#define AR5416_NUM_PD_GAINS 4 +#define AR5416_PD_GAIN_ICEPTS 5 +#define AR5416_EEPROM_MODAL_SPURS 5 +#define AR5416_MAX_CHAINS 2 + +struct BaseEepHeader { + uint16_t length; + uint16_t checksum; + uint16_t version; + uint8_t opCapFlags; + uint8_t eepMisc; + uint16_t regDmn[2]; + uint8_t macAddr[6]; + uint8_t rxMask; + uint8_t txMask; + uint16_t rfSilent; + uint16_t blueToothOptions; + uint16_t deviceCap; + uint32_t binBuildNumber; + uint8_t deviceType; + uint8_t futureBase[33]; +} __packed; + +struct spurChanStruct { + uint16_t spurChan; + uint8_t spurRangeLow; + uint8_t spurRangeHigh; +} __packed; + +struct ModalEepHeader { + uint32_t antCtrlChain[AR5416_MAX_CHAINS]; + uint32_t antCtrlCommon; + int8_t antennaGainCh[AR5416_MAX_CHAINS]; + uint8_t switchSettling; + uint8_t txRxAttenCh[AR5416_MAX_CHAINS]; + uint8_t rxTxMarginCh[AR5416_MAX_CHAINS]; + uint8_t adcDesiredSize; + int8_t pgaDesiredSize; + uint8_t xlnaGainCh[AR5416_MAX_CHAINS]; + uint8_t txEndToXpaOff; + uint8_t txEndToRxOn; + uint8_t txFrameToXpaOn; + uint8_t thresh62; + uint8_t noiseFloorThreshCh[AR5416_MAX_CHAINS]; + uint8_t xpdGain; + uint8_t xpd; + int8_t iqCalICh[AR5416_MAX_CHAINS]; + int8_t iqCalQCh[AR5416_MAX_CHAINS]; + uint8_t pdGainOverlap; + uint8_t ob; + uint8_t db; + uint8_t xpaBiasLvl; + uint8_t pwrDecreaseFor2Chain; + uint8_t pwrDecreaseFor3Chain; + uint8_t txFrameToDataStart; + uint8_t txFrameToPaOn; + uint8_t ht40PowerIncForPdadc; + uint8_t bswAtten[AR5416_MAX_CHAINS]; + uint8_t bswMargin[AR5416_MAX_CHAINS]; + uint8_t swSettleHt40; + uint8_t futureModal[22]; + struct spurChanStruct spurChans[AR5416_EEPROM_MODAL_SPURS]; +} __packed; + +struct calDataPerFreq { + uint8_t pwrPdg[AR5416_NUM_PD_GAINS][AR5416_PD_GAIN_ICEPTS]; + uint8_t vpdPdg[AR5416_NUM_PD_GAINS][AR5416_PD_GAIN_ICEPTS]; +} __packed; + +struct CalTargetPowerLegacy { + uint8_t bChannel; + uint8_t tPow2x[4]; +} __packed; + +struct CalTargetPowerHt { + uint8_t bChannel; + uint8_t tPow2x[8]; +} __packed; + +struct CalCtlEdges { + uint8_t bChannel; + uint8_t tPowerFlag; +} __packed; + +struct CalCtlData { + struct CalCtlEdges ctlEdges[AR5416_MAX_CHAINS][AR5416_NUM_BAND_EDGES]; +} __packed; + +struct ar5416eeprom { + struct BaseEepHeader baseEepHeader; + uint8_t custData[64]; + struct ModalEepHeader modalHeader[2]; + uint8_t calFreqPier5G[AR5416_NUM_5G_CAL_PIERS]; + uint8_t calFreqPier2G[AR5416_NUM_2G_CAL_PIERS]; + struct calDataPerFreq calPierData5G[AR5416_MAX_CHAINS] + [AR5416_NUM_5G_CAL_PIERS]; + struct calDataPerFreq calPierData2G[AR5416_MAX_CHAINS] + [AR5416_NUM_2G_CAL_PIERS]; + struct CalTargetPowerLegacy calTPow5G[AR5416_NUM_5G_20_TARGET_POWERS]; + struct CalTargetPowerHt calTPow5GHT20[AR5416_NUM_5G_20_TARGET_POWERS]; + struct CalTargetPowerHt calTPow5GHT40[AR5416_NUM_5G_40_TARGET_POWERS]; + struct CalTargetPowerLegacy calTPowCck[AR5416_NUM_2G_CCK_TARGET_POWERS]; + struct CalTargetPowerLegacy calTPow2G[AR5416_NUM_2G_20_TARGET_POWERS]; + struct CalTargetPowerHt calTPow2GHT20[AR5416_NUM_2G_20_TARGET_POWERS]; + struct CalTargetPowerHt calTPow2GHT40[AR5416_NUM_2G_40_TARGET_POWERS]; + uint8_t ctlIndex[AR5416_NUM_CTLS]; + struct CalCtlData ctlData[AR5416_NUM_CTLS]; + uint8_t padding; +} __packed; + +#define OTUS_NUM_CHAINS 2 + +#define OTUS_UID(aid) (IEEE80211_AID(aid) + 4) + +#define OTUS_MAX_TXCMDSZ 64 +#define OTUS_RXBUFSZ (8 * 1024) +/* Bumped for later A-MSDU and legacy fast-frames TX support */ +#define OTUS_TXBUFSZ (8 * 1024) + +/* Default EDCA parameters for when QoS is disabled. */ +static const struct wmeParams otus_edca_def[WME_NUM_AC] = { + { 4, 10, 3, 0 }, + { 4, 10, 7, 0 }, + { 3, 4, 2, 94 }, + { 2, 3, 2, 47 } +}; + +#define OTUS_RIDX_CCK1 0 +#define OTUS_RIDX_OFDM6 4 +#define OTUS_RIDX_OFDM24 8 +#define OTUS_RIDX_MAX 11 +static const struct otus_rate { + uint8_t rate; + uint8_t mcs; +} otus_rates[] = { + { 2, 0x0 }, + { 4, 0x1 }, + { 11, 0x2 }, + { 22, 0x3 }, + { 12, 0xb }, + { 18, 0xf }, + { 24, 0xa }, + { 36, 0xe }, + { 48, 0x9 }, + { 72, 0xd }, + { 96, 0x8 }, + { 108, 0xc } +}; + +struct otus_rx_radiotap_header { + struct ieee80211_radiotap_header wr_ihdr; + uint8_t wr_flags; + uint8_t wr_rate; + uint16_t wr_chan_freq; + uint16_t wr_chan_flags; + uint8_t wr_antsignal; +} __packed; + +#define OTUS_RX_RADIOTAP_PRESENT \ + (1 << IEEE80211_RADIOTAP_FLAGS | \ + 1 << IEEE80211_RADIOTAP_RATE | \ + 1 << IEEE80211_RADIOTAP_CHANNEL | \ + 1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) + +struct otus_tx_radiotap_header { + struct ieee80211_radiotap_header wt_ihdr; + uint8_t wt_flags; + uint8_t wt_rate; + uint16_t wt_chan_freq; + uint16_t wt_chan_flags; +} __packed; + +#define OTUS_TX_RADIOTAP_PRESENT \ + (1 << IEEE80211_RADIOTAP_FLAGS | \ + 1 << IEEE80211_RADIOTAP_RATE | \ + 1 << IEEE80211_RADIOTAP_CHANNEL) + +struct otus_softc; + +/* Firmware commands */ +struct otus_tx_cmd { + uint8_t *buf; + uint16_t buflen; + void * *odata; + uint16_t token; + STAILQ_ENTRY(otus_tx_cmd) next_cmd; +}; + +/* TX, RX buffers */ +struct otus_data { + struct otus_softc *sc; + uint8_t *buf; + uint16_t buflen; + struct mbuf *m; + struct ieee80211_node *ni; + STAILQ_ENTRY(otus_data) next; +}; + +struct otus_node { + struct ieee80211_node ni; + uint64_t tx_done; + uint64_t tx_err; + uint64_t tx_retries; +}; + +#define OTUS_CONFIG_INDEX 0 +#define OTUS_IFACE_INDEX 0 + +/* + * The carl9170 firmware has the following specification: + * + * 0 - USB control + * 1 - TX + * 2 - RX + * 3 - IRQ + * 4 - CMD + * .. + * 10 - end + */ +enum { + OTUS_BULK_TX, + OTUS_BULK_RX, + OTUS_BULK_IRQ, + OTUS_BULK_CMD, + OTUS_N_XFER +}; + +struct otus_vap { + struct ieee80211vap vap; + int (*newstate)(struct ieee80211vap *, + enum ieee80211_state, int); +}; +#define OTUS_VAP(vap) ((struct otus_vap *)(vap)) +#define OTUS_NODE(ni) ((struct otus_node *)(ni)) + +#define OTUS_LOCK(sc) mtx_lock(&(sc)->sc_mtx) +#define OTUS_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) +#define OTUS_LOCK_ASSERT(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED) +#define OTUS_UNLOCK_ASSERT(sc) mtx_assert(&(sc)->sc_mtx, MA_NOTOWNED) + +/* XXX the TX/RX endpoint dump says it's 0x200, (512)? */ +#define OTUS_MAX_TXSZ 512 +#define OTUS_MAX_RXSZ 512 +/* intr/cmd endpoint dump says 0x40 */ +#define OTUS_MAX_CTRLSZ 64 + +#define OTUS_CMD_LIST_COUNT 32 +#define OTUS_RX_LIST_COUNT 128 +#define OTUS_TX_LIST_COUNT 32 + +struct otus_softc { + struct ieee80211com sc_ic; + struct mbufq sc_snd; + device_t sc_dev; + struct usb_device *sc_udev; + int (*sc_newstate)(struct ieee80211com *, + enum ieee80211_state, int); + void (*sc_led_newstate)(struct otus_softc *); + struct usbd_interface *sc_iface; + struct mtx sc_mtx; + + struct ar5416eeprom eeprom; + uint8_t capflags; + uint8_t rxmask; + uint8_t txmask; + int sc_running:1, + sc_calibrating:1, + sc_scanning:1; + + int sc_if_flags; + int sc_tx_timer; + int fixed_ridx; + int bb_reset; + + struct ieee80211_channel *sc_curchan; + + struct task tx_task; + struct task wme_update_task; + struct timeout_task scan_to; + struct timeout_task calib_to; + + /* register batch writes */ + int write_idx; + + uint32_t led_state; + + /* current firmware message serial / token number */ + int token; + + /* current noisefloor, from SET_FREQUENCY */ + int sc_nf[OTUS_NUM_CHAINS]; + + const uint32_t *phy_vals; + + struct { + uint32_t reg; + uint32_t val; + } __packed write_buf[AR_MAX_WRITE_IDX + 1]; + + struct otus_data sc_rx[OTUS_RX_LIST_COUNT]; + struct otus_data sc_tx[OTUS_TX_LIST_COUNT]; + struct otus_tx_cmd sc_cmd[OTUS_CMD_LIST_COUNT]; + + struct usb_xfer *sc_xfer[OTUS_N_XFER]; + + STAILQ_HEAD(, otus_data) sc_rx_active; + STAILQ_HEAD(, otus_data) sc_rx_inactive; + STAILQ_HEAD(, otus_data) sc_tx_active[OTUS_N_XFER]; + STAILQ_HEAD(, otus_data) sc_tx_inactive; + STAILQ_HEAD(, otus_data) sc_tx_pending[OTUS_N_XFER]; + + STAILQ_HEAD(, otus_tx_cmd) sc_cmd_active; + STAILQ_HEAD(, otus_tx_cmd) sc_cmd_inactive; + STAILQ_HEAD(, otus_tx_cmd) sc_cmd_pending; + STAILQ_HEAD(, otus_tx_cmd) sc_cmd_waiting; + + union { + struct otus_rx_radiotap_header th; + uint8_t pad[64]; + } sc_rxtapu; +#define sc_rxtap sc_rxtapu.th + int sc_rxtap_len; + + union { + struct otus_tx_radiotap_header th; + uint8_t pad[64]; + } sc_txtapu; +#define sc_txtap sc_txtapu.th + int sc_txtap_len; +}; + +#endif /* __IF_OTUSREG_H__ */ diff --git a/sys/modules/Makefile b/sys/modules/Makefile index df6b01a9b78..f5f9b4c3a3e 100644 --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -270,6 +270,8 @@ SUBDIR= \ ${_nxge} \ ${_opensolaris} \ oce \ + otus \ + otusfw \ ow \ ${_padlock} \ ${_padlock_rng} \ diff --git a/sys/modules/otus/Makefile b/sys/modules/otus/Makefile new file mode 100644 index 00000000000..a7a84a87d9c --- /dev/null +++ b/sys/modules/otus/Makefile @@ -0,0 +1,10 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../dev/otus + +KMOD = if_otus +SRCS = if_otus.c if_otusreg.h \ + bus_if.h device_if.h \ + opt_bus.h opt_usb.h usb_if.h usbdevs.h + +.include diff --git a/sys/modules/otusfw/Makefile b/sys/modules/otusfw/Makefile new file mode 100644 index 00000000000..0a47f66c9ca --- /dev/null +++ b/sys/modules/otusfw/Makefile @@ -0,0 +1,5 @@ +# $FreeBSD$ + +SUBDIR=otusfw_init otusfw_main + +.include diff --git a/sys/modules/otusfw/otusfw_init/Makefile b/sys/modules/otusfw/otusfw_init/Makefile new file mode 100644 index 00000000000..d188e9a83d9 --- /dev/null +++ b/sys/modules/otusfw/otusfw_init/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD$ + +KMOD= otusfw_init +FIRMWS= otusfw_init:otusfw_init:1 + +CLEANFILES= otus_init + +otusfw_init: ${.CURDIR}/../../../contrib/dev/otus/otus-init + cp ${.CURDIR}/../../../contrib/dev/otus/otus-init ${.TARGET} + +.include diff --git a/sys/modules/otusfw/otusfw_main/Makefile b/sys/modules/otusfw/otusfw_main/Makefile new file mode 100644 index 00000000000..94e1aeec1da --- /dev/null +++ b/sys/modules/otusfw/otusfw_main/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD$ + +KMOD= otusfw_main +FIRMWS= otusfw_main:otusfw_main:1 + +CLEANFILES= otusfw_main + +otusfw_main: ${.CURDIR}/../../../contrib/dev/otus/otus-main + cp ${.CURDIR}/../../../contrib/dev/otus/otus-main ${.TARGET} + +.include