Date: Sat, 26 Sep 2015 07:08:36 +0000 (UTC) From: Adrian Chadd <adrian@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r288253 - in head/sys: contrib/dev/otus dev/otus modules modules/otus modules/otusfw modules/otusfw/otusfw_init modules/otusfw/otusfw_main Message-ID: <201509260708.t8Q78aRB007765@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: adrian Date: Sat Sep 26 07:08:35 2015 New Revision: 288253 URL: https://svnweb.freebsd.org/changeset/base/288253 Log: 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. Added: head/sys/contrib/dev/otus/ head/sys/contrib/dev/otus/otus-init (contents, props changed) head/sys/contrib/dev/otus/otus-license head/sys/contrib/dev/otus/otus-main (contents, props changed) head/sys/dev/otus/ head/sys/dev/otus/if_otus.c (contents, props changed) head/sys/dev/otus/if_otusreg.h (contents, props changed) head/sys/modules/otus/ head/sys/modules/otus/Makefile (contents, props changed) head/sys/modules/otusfw/ head/sys/modules/otusfw/Makefile (contents, props changed) head/sys/modules/otusfw/otusfw_init/ head/sys/modules/otusfw/otusfw_init/Makefile (contents, props changed) head/sys/modules/otusfw/otusfw_main/ head/sys/modules/otusfw/otusfw_main/Makefile (contents, props changed) Modified: head/sys/modules/Makefile Added: head/sys/contrib/dev/otus/otus-init ============================================================================== Binary file. No diff available. Added: head/sys/contrib/dev/otus/otus-license ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/contrib/dev/otus/otus-license Sat Sep 26 07:08:35 2015 (r288253) @@ -0,0 +1,47 @@ +Copyright (c) 2008, Atheros Communications, Inc. +All rights reserved. + +Redistribution. Redistribution and use in binary form, without +modification, are permitted provided that the following conditions are +met: + +* Redistributions must reproduce the above copyright notice and the + following disclaimer in the documentation and/or other materials + provided with the distribution. + +* Neither the name of Atheros Communications, Inc. nor the names of + its suppliers may be used to endorse or promote products derived + from this software without specific prior written permission. + +* No reverse engineering, decompilation, or disassembly of this + software is permitted. + +Limited patent license. Atheros Communications, Inc. grants a +world-wide, royalty-free, non-exclusive license under patents it +now or hereafter owns or controls to make, have made, use, import, +offer to sell and sell ("Utilize") this software, but solely to +the extent that any such patent is necessary to Utilize the software +alone, or in combination with an operating system licensed under an +approved Open Source license as listed by the Open Source Initiative +at http://opensource.org/licenses. The patent license shall not +apply to any other combinations which include this software. No +hardware per se is licensed hereunder. + +DISCLAIMER. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +The following files are under this license: + +otus-init otus-main + +These files are needed by otus(4) devices. Added: head/sys/contrib/dev/otus/otus-main ============================================================================== Binary file. No diff available. Added: head/sys/dev/otus/if_otus.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/dev/otus/if_otus.c Sat Sep 26 07:08:35 2015 (r288253) @@ -0,0 +1,3099 @@ +/* $OpenBSD: if_otus.c,v 1.46 2015/03/14 03:38:49 jsg Exp $ */ + +/*- + * Copyright (c) 2009 Damien Bergamini <damien.bergamini@free.fr> + * Copyright (c) 2015 Adrian Chadd <adrian@FreeBSD.org> + * + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/endian.h> +#include <sys/sockio.h> +#include <sys/mbuf.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/systm.h> +#include <sys/conf.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/firmware.h> +#include <sys/module.h> +#include <sys/taskqueue.h> + +#include <machine/bus.h> +#include <machine/resource.h> + +#include <net/bpf.h> +#include <net/if.h> +#include <net/if_var.h> +#include <net/if_arp.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/if_ether.h> +#include <netinet/ip.h> + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_regdomain.h> +#include <net80211/ieee80211_radiotap.h> +#include <net80211/ieee80211_ratectl.h> +#include <net80211/ieee80211_input.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include "usbdevs.h" + +#define USB_DEBUG_VAR otus_debug +#include <dev/usb/usb_debug.h> + +#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; +} *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201509260708.t8Q78aRB007765>