aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/mwl
diff options
context:
space:
mode:
authorSam Leffler <sam@FreeBSD.org>2009-06-01 18:07:01 +0000
committerSam Leffler <sam@FreeBSD.org>2009-06-01 18:07:01 +0000
commitcf4c5a533126ca1ddb1f070af73f8f53b9e77fd4 (patch)
tree4aa57b01fcce3ef5523174cf4075c853234998b3 /sys/dev/mwl
parenta43cee7009182f029996c7c7c33c812385e8fe41 (diff)
Notes
Diffstat (limited to 'sys/dev/mwl')
-rw-r--r--sys/dev/mwl/if_mwl.c4990
-rw-r--r--sys/dev/mwl/if_mwl_pci.c316
-rw-r--r--sys/dev/mwl/if_mwlioctl.h136
-rw-r--r--sys/dev/mwl/if_mwlvar.h354
-rw-r--r--sys/dev/mwl/mwldiag.h108
-rw-r--r--sys/dev/mwl/mwlhal.c2703
-rw-r--r--sys/dev/mwl/mwlhal.h666
-rw-r--r--sys/dev/mwl/mwlreg.h1352
8 files changed, 10625 insertions, 0 deletions
diff --git a/sys/dev/mwl/if_mwl.c b/sys/dev/mwl/if_mwl.c
new file mode 100644
index 0000000000000..ae13c9d9168ca
--- /dev/null
+++ b/sys/dev/mwl/if_mwl.c
@@ -0,0 +1,4990 @@
+/*-
+ * Copyright (c) 2007-2009 Sam Leffler, Errno Consulting
+ * Copyright (c) 2007-2008 Marvell Semiconductor, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * 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 NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Driver for the Marvell 88W8363 Wireless LAN controller.
+ */
+
+#include "opt_inet.h"
+#include "opt_mwl.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/errno.h>
+#include <sys/callout.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kthread.h>
+#include <sys/taskqueue.h>
+
+#include <machine/bus.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_llc.h>
+
+#include <net/bpf.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_regdomain.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#endif /* INET */
+
+#include <dev/mwl/if_mwlvar.h>
+#include <dev/mwl/mwldiag.h>
+
+/* idiomatic shorthands: MS = mask+shift, SM = shift+mask */
+#define MS(v,x) (((v) & x) >> x##_S)
+#define SM(v,x) (((v) << x##_S) & x)
+
+static struct ieee80211vap *mwl_vap_create(struct ieee80211com *,
+ const char name[IFNAMSIZ], int unit, int opmode,
+ int flags, const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void mwl_vap_delete(struct ieee80211vap *);
+static int mwl_setupdma(struct mwl_softc *);
+static int mwl_hal_reset(struct mwl_softc *sc);
+static int mwl_init_locked(struct mwl_softc *);
+static void mwl_init(void *);
+static void mwl_stop_locked(struct ifnet *, int);
+static int mwl_reset(struct ieee80211vap *, u_long);
+static void mwl_stop(struct ifnet *, int);
+static void mwl_start(struct ifnet *);
+static int mwl_raw_xmit(struct ieee80211_node *, struct mbuf *,
+ const struct ieee80211_bpf_params *);
+static int mwl_media_change(struct ifnet *);
+static void mwl_watchdog(struct ifnet *);
+static int mwl_ioctl(struct ifnet *, u_long, caddr_t);
+static void mwl_radar_proc(void *, int);
+static void mwl_chanswitch_proc(void *, int);
+static void mwl_bawatchdog_proc(void *, int);
+static int mwl_key_alloc(struct ieee80211vap *,
+ struct ieee80211_key *,
+ ieee80211_keyix *, ieee80211_keyix *);
+static int mwl_key_delete(struct ieee80211vap *,
+ const struct ieee80211_key *);
+static int mwl_key_set(struct ieee80211vap *, const struct ieee80211_key *,
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static int mwl_mode_init(struct mwl_softc *);
+static void mwl_update_mcast(struct ifnet *);
+static void mwl_update_promisc(struct ifnet *);
+static void mwl_updateslot(struct ifnet *);
+static int mwl_beacon_setup(struct ieee80211vap *);
+static void mwl_beacon_update(struct ieee80211vap *, int);
+#ifdef MWL_HOST_PS_SUPPORT
+static void mwl_update_ps(struct ieee80211vap *, int);
+static int mwl_set_tim(struct ieee80211_node *, int);
+#endif
+static int mwl_dma_setup(struct mwl_softc *);
+static void mwl_dma_cleanup(struct mwl_softc *);
+static struct ieee80211_node *mwl_node_alloc(struct ieee80211vap *,
+ const uint8_t [IEEE80211_ADDR_LEN]);
+static void mwl_node_cleanup(struct ieee80211_node *);
+static void mwl_node_drain(struct ieee80211_node *);
+static void mwl_node_getsignal(const struct ieee80211_node *,
+ int8_t *, int8_t *);
+static void mwl_node_getmimoinfo(const struct ieee80211_node *,
+ struct ieee80211_mimo_info *);
+static int mwl_rxbuf_init(struct mwl_softc *, struct mwl_rxbuf *);
+static void mwl_rx_proc(void *, int);
+static void mwl_txq_init(struct mwl_softc *sc, struct mwl_txq *, int);
+static int mwl_tx_setup(struct mwl_softc *, int, int);
+static int mwl_wme_update(struct ieee80211com *);
+static void mwl_tx_cleanupq(struct mwl_softc *, struct mwl_txq *);
+static void mwl_tx_cleanup(struct mwl_softc *);
+static uint16_t mwl_calcformat(uint8_t rate, const struct ieee80211_node *);
+static int mwl_tx_start(struct mwl_softc *, struct ieee80211_node *,
+ struct mwl_txbuf *, struct mbuf *);
+static void mwl_tx_proc(void *, int);
+static int mwl_chan_set(struct mwl_softc *, struct ieee80211_channel *);
+static void mwl_draintxq(struct mwl_softc *);
+static void mwl_cleartxq(struct mwl_softc *, struct ieee80211vap *);
+static void mwl_recv_action(struct ieee80211_node *,
+ const uint8_t *, const uint8_t *);
+static int mwl_addba_request(struct ieee80211_node *,
+ struct ieee80211_tx_ampdu *, int dialogtoken,
+ int baparamset, int batimeout);
+static int mwl_addba_response(struct ieee80211_node *,
+ struct ieee80211_tx_ampdu *, int status,
+ int baparamset, int batimeout);
+static void mwl_addba_stop(struct ieee80211_node *,
+ struct ieee80211_tx_ampdu *);
+static int mwl_startrecv(struct mwl_softc *);
+static MWL_HAL_APMODE mwl_getapmode(const struct ieee80211vap *,
+ struct ieee80211_channel *);
+static int mwl_setapmode(struct ieee80211vap *, struct ieee80211_channel*);
+static void mwl_scan_start(struct ieee80211com *);
+static void mwl_scan_end(struct ieee80211com *);
+static void mwl_set_channel(struct ieee80211com *);
+static int mwl_peerstadb(struct ieee80211_node *,
+ int aid, int staid, MWL_HAL_PEERINFO *pi);
+static int mwl_localstadb(struct ieee80211vap *);
+static int mwl_newstate(struct ieee80211vap *, enum ieee80211_state, int);
+static int allocstaid(struct mwl_softc *sc, int aid);
+static void delstaid(struct mwl_softc *sc, int staid);
+static void mwl_newassoc(struct ieee80211_node *, int);
+static void mwl_agestations(void *);
+static int mwl_setregdomain(struct ieee80211com *,
+ struct ieee80211_regdomain *, int,
+ struct ieee80211_channel []);
+static void mwl_getradiocaps(struct ieee80211com *, int, int *,
+ struct ieee80211_channel []);
+static int mwl_getchannels(struct mwl_softc *);
+
+static void mwl_sysctlattach(struct mwl_softc *);
+static void mwl_announce(struct mwl_softc *);
+
+SYSCTL_NODE(_hw, OID_AUTO, mwl, CTLFLAG_RD, 0, "Marvell driver parameters");
+
+static int mwl_rxdesc = MWL_RXDESC; /* # rx desc's to allocate */
+SYSCTL_INT(_hw_mwl, OID_AUTO, rxdesc, CTLFLAG_RW, &mwl_rxdesc,
+ 0, "rx descriptors allocated");
+static int mwl_rxbuf = MWL_RXBUF; /* # rx buffers to allocate */
+SYSCTL_INT(_hw_mwl, OID_AUTO, rxbuf, CTLFLAG_RW, &mwl_rxbuf,
+ 0, "rx buffers allocated");
+TUNABLE_INT("hw.mwl.rxbuf", &mwl_rxbuf);
+static int mwl_txbuf = MWL_TXBUF; /* # tx buffers to allocate */
+SYSCTL_INT(_hw_mwl, OID_AUTO, txbuf, CTLFLAG_RW, &mwl_txbuf,
+ 0, "tx buffers allocated");
+TUNABLE_INT("hw.mwl.txbuf", &mwl_txbuf);
+static int mwl_txcoalesce = 8; /* # tx packets to q before poking f/w*/
+SYSCTL_INT(_hw_mwl, OID_AUTO, txcoalesce, CTLFLAG_RW, &mwl_txcoalesce,
+ 0, "tx buffers to send at once");
+TUNABLE_INT("hw.mwl.txcoalesce", &mwl_txcoalesce);
+static int mwl_rxquota = MWL_RXBUF; /* # max buffers to process */
+SYSCTL_INT(_hw_mwl, OID_AUTO, rxquota, CTLFLAG_RW, &mwl_rxquota,
+ 0, "max rx buffers to process per interrupt");
+TUNABLE_INT("hw.mwl.rxquota", &mwl_rxquota);
+static int mwl_rxdmalow = 3; /* # min buffers for wakeup */
+SYSCTL_INT(_hw_mwl, OID_AUTO, rxdmalow, CTLFLAG_RW, &mwl_rxdmalow,
+ 0, "min free rx buffers before restarting traffic");
+TUNABLE_INT("hw.mwl.rxdmalow", &mwl_rxdmalow);
+
+#ifdef MWL_DEBUG
+static int mwl_debug = 0;
+SYSCTL_INT(_hw_mwl, OID_AUTO, debug, CTLFLAG_RW, &mwl_debug,
+ 0, "control debugging printfs");
+TUNABLE_INT("hw.mwl.debug", &mwl_debug);
+enum {
+ MWL_DEBUG_XMIT = 0x00000001, /* basic xmit operation */
+ MWL_DEBUG_XMIT_DESC = 0x00000002, /* xmit descriptors */
+ MWL_DEBUG_RECV = 0x00000004, /* basic recv operation */
+ MWL_DEBUG_RECV_DESC = 0x00000008, /* recv descriptors */
+ MWL_DEBUG_RESET = 0x00000010, /* reset processing */
+ MWL_DEBUG_BEACON = 0x00000020, /* beacon handling */
+ MWL_DEBUG_INTR = 0x00000040, /* ISR */
+ MWL_DEBUG_TX_PROC = 0x00000080, /* tx ISR proc */
+ MWL_DEBUG_RX_PROC = 0x00000100, /* rx ISR proc */
+ MWL_DEBUG_KEYCACHE = 0x00000200, /* key cache management */
+ MWL_DEBUG_STATE = 0x00000400, /* 802.11 state transitions */
+ MWL_DEBUG_NODE = 0x00000800, /* node management */
+ MWL_DEBUG_RECV_ALL = 0x00001000, /* trace all frames (beacons) */
+ MWL_DEBUG_TSO = 0x00002000, /* TSO processing */
+ MWL_DEBUG_AMPDU = 0x00004000, /* BA stream handling */
+ MWL_DEBUG_ANY = 0xffffffff
+};
+#define IS_BEACON(wh) \
+ ((wh->i_fc[0] & (IEEE80211_FC0_TYPE_MASK|IEEE80211_FC0_SUBTYPE_MASK)) == \
+ (IEEE80211_FC0_TYPE_MGT|IEEE80211_FC0_SUBTYPE_BEACON))
+#define IFF_DUMPPKTS_RECV(sc, wh) \
+ (((sc->sc_debug & MWL_DEBUG_RECV) && \
+ ((sc->sc_debug & MWL_DEBUG_RECV_ALL) || !IS_BEACON(wh))) || \
+ (sc->sc_ifp->if_flags & (IFF_DEBUG|IFF_LINK2)) == (IFF_DEBUG|IFF_LINK2))
+#define IFF_DUMPPKTS_XMIT(sc) \
+ ((sc->sc_debug & MWL_DEBUG_XMIT) || \
+ (sc->sc_ifp->if_flags & (IFF_DEBUG|IFF_LINK2)) == (IFF_DEBUG|IFF_LINK2))
+#define DPRINTF(sc, m, fmt, ...) do { \
+ if (sc->sc_debug & (m)) \
+ printf(fmt, __VA_ARGS__); \
+} while (0)
+#define KEYPRINTF(sc, hk, mac) do { \
+ if (sc->sc_debug & MWL_DEBUG_KEYCACHE) \
+ mwl_keyprint(sc, __func__, hk, mac); \
+} while (0)
+static void mwl_printrxbuf(const struct mwl_rxbuf *bf, u_int ix);
+static void mwl_printtxbuf(const struct mwl_txbuf *bf, u_int qnum, u_int ix);
+#else
+#define IFF_DUMPPKTS_RECV(sc, wh) \
+ ((sc->sc_ifp->if_flags & (IFF_DEBUG|IFF_LINK2)) == (IFF_DEBUG|IFF_LINK2))
+#define IFF_DUMPPKTS_XMIT(sc) \
+ ((sc->sc_ifp->if_flags & (IFF_DEBUG|IFF_LINK2)) == (IFF_DEBUG|IFF_LINK2))
+#define DPRINTF(sc, m, fmt, ...) do { \
+ (void) sc; \
+} while (0)
+#define KEYPRINTF(sc, k, mac) do { \
+ (void) sc; \
+} while (0)
+#endif
+
+MALLOC_DEFINE(M_MWLDEV, "mwldev", "mwl driver dma buffers");
+
+/*
+ * Each packet has fixed front matter: a 2-byte length
+ * of the payload, followed by a 4-address 802.11 header
+ * (regardless of the actual header and always w/o any
+ * QoS header). The payload then follows.
+ */
+struct mwltxrec {
+ uint16_t fwlen;
+ struct ieee80211_frame_addr4 wh;
+} __packed;
+
+/*
+ * Read/Write shorthands for accesses to BAR 0. Note
+ * that all BAR 1 operations are done in the "hal" and
+ * there should be no reference to them here.
+ */
+static __inline uint32_t
+RD4(struct mwl_softc *sc, bus_size_t off)
+{
+ return bus_space_read_4(sc->sc_io0t, sc->sc_io0h, off);
+}
+
+static __inline void
+WR4(struct mwl_softc *sc, bus_size_t off, uint32_t val)
+{
+ bus_space_write_4(sc->sc_io0t, sc->sc_io0h, off, val);
+}
+
+int
+mwl_attach(uint16_t devid, struct mwl_softc *sc)
+{
+ struct ifnet *ifp;
+ struct ieee80211com *ic;
+ struct mwl_hal *mh;
+ int error = 0;
+
+ DPRINTF(sc, MWL_DEBUG_ANY, "%s: devid 0x%x\n", __func__, devid);
+
+ ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
+ if (ifp == NULL) {
+ device_printf(sc->sc_dev, "can not if_alloc()\n");
+ return ENOSPC;
+ }
+ ic = ifp->if_l2com;
+
+ /* set these up early for if_printf use */
+ if_initname(ifp, device_get_name(sc->sc_dev),
+ device_get_unit(sc->sc_dev));
+
+ mh = mwl_hal_attach(sc->sc_dev, devid,
+ sc->sc_io1h, sc->sc_io1t, sc->sc_dmat);
+ if (mh == NULL) {
+ if_printf(ifp, "unable to attach HAL\n");
+ error = EIO;
+ goto bad;
+ }
+ sc->sc_mh = mh;
+ /*
+ * Load firmware so we can get setup. We arbitrarily
+ * pick station firmware; we'll re-load firmware as
+ * needed so setting up the wrong mode isn't a big deal.
+ */
+ if (mwl_hal_fwload(mh, NULL) != 0) {
+ if_printf(ifp, "unable to setup builtin firmware\n");
+ error = EIO;
+ goto bad1;
+ }
+ if (mwl_hal_gethwspecs(mh, &sc->sc_hwspecs) != 0) {
+ if_printf(ifp, "unable to fetch h/w specs\n");
+ error = EIO;
+ goto bad1;
+ }
+ error = mwl_getchannels(sc);
+ if (error != 0)
+ goto bad1;
+
+ sc->sc_txantenna = 0; /* h/w default */
+ sc->sc_rxantenna = 0; /* h/w default */
+ sc->sc_invalid = 0; /* ready to go, enable int handling */
+ sc->sc_ageinterval = MWL_AGEINTERVAL;
+
+ /*
+ * Allocate tx+rx descriptors and populate the lists.
+ * We immediately push the information to the firmware
+ * as otherwise it gets upset.
+ */
+ error = mwl_dma_setup(sc);
+ if (error != 0) {
+ if_printf(ifp, "failed to setup descriptors: %d\n", error);
+ goto bad1;
+ }
+ error = mwl_setupdma(sc); /* push to firmware */
+ if (error != 0) /* NB: mwl_setupdma prints msg */
+ goto bad1;
+
+ callout_init(&sc->sc_timer, CALLOUT_MPSAFE);
+
+ sc->sc_tq = taskqueue_create("mwl_taskq", M_NOWAIT,
+ taskqueue_thread_enqueue, &sc->sc_tq);
+ taskqueue_start_threads(&sc->sc_tq, 1, PI_NET,
+ "%s taskq", ifp->if_xname);
+
+ TASK_INIT(&sc->sc_rxtask, 0, mwl_rx_proc, sc);
+ TASK_INIT(&sc->sc_radartask, 0, mwl_radar_proc, sc);
+ TASK_INIT(&sc->sc_chanswitchtask, 0, mwl_chanswitch_proc, sc);
+ TASK_INIT(&sc->sc_bawatchdogtask, 0, mwl_bawatchdog_proc, sc);
+
+ /* NB: insure BK queue is the lowest priority h/w queue */
+ if (!mwl_tx_setup(sc, WME_AC_BK, MWL_WME_AC_BK)) {
+ if_printf(ifp, "unable to setup xmit queue for %s traffic!\n",
+ ieee80211_wme_acnames[WME_AC_BK]);
+ error = EIO;
+ goto bad2;
+ }
+ if (!mwl_tx_setup(sc, WME_AC_BE, MWL_WME_AC_BE) ||
+ !mwl_tx_setup(sc, WME_AC_VI, MWL_WME_AC_VI) ||
+ !mwl_tx_setup(sc, WME_AC_VO, MWL_WME_AC_VO)) {
+ /*
+ * Not enough hardware tx queues to properly do WME;
+ * just punt and assign them all to the same h/w queue.
+ * We could do a better job of this if, for example,
+ * we allocate queues when we switch from station to
+ * AP mode.
+ */
+ if (sc->sc_ac2q[WME_AC_VI] != NULL)
+ mwl_tx_cleanupq(sc, sc->sc_ac2q[WME_AC_VI]);
+ if (sc->sc_ac2q[WME_AC_BE] != NULL)
+ mwl_tx_cleanupq(sc, sc->sc_ac2q[WME_AC_BE]);
+ sc->sc_ac2q[WME_AC_BE] = sc->sc_ac2q[WME_AC_BK];
+ sc->sc_ac2q[WME_AC_VI] = sc->sc_ac2q[WME_AC_BK];
+ sc->sc_ac2q[WME_AC_VO] = sc->sc_ac2q[WME_AC_BK];
+ }
+ TASK_INIT(&sc->sc_txtask, 0, mwl_tx_proc, sc);
+
+ ifp->if_softc = sc;
+ ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST;
+ ifp->if_start = mwl_start;
+ ifp->if_watchdog = mwl_watchdog;
+ ifp->if_ioctl = mwl_ioctl;
+ ifp->if_init = mwl_init;
+ IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
+ ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN;
+ IFQ_SET_READY(&ifp->if_snd);
+
+ ic->ic_ifp = ifp;
+ /* XXX not right but it's not used anywhere important */
+ ic->ic_phytype = IEEE80211_T_OFDM;
+ ic->ic_opmode = IEEE80211_M_STA;
+ ic->ic_caps =
+ IEEE80211_C_STA /* station mode supported */
+ | IEEE80211_C_HOSTAP /* hostap mode */
+ | IEEE80211_C_MONITOR /* monitor mode */
+#if 0
+ | IEEE80211_C_IBSS /* ibss, nee adhoc, mode */
+ | IEEE80211_C_AHDEMO /* adhoc demo mode */
+#endif
+ | IEEE80211_C_WDS /* WDS supported */
+ | IEEE80211_C_SHPREAMBLE /* short preamble supported */
+ | IEEE80211_C_SHSLOT /* short slot time supported */
+ | IEEE80211_C_WME /* WME/WMM supported */
+ | IEEE80211_C_BURST /* xmit bursting supported */
+ | IEEE80211_C_WPA /* capable of WPA1+WPA2 */
+ | IEEE80211_C_BGSCAN /* capable of bg scanning */
+ | IEEE80211_C_TXFRAG /* handle tx frags */
+ | IEEE80211_C_TXPMGT /* capable of txpow mgt */
+ | IEEE80211_C_DFS /* DFS supported */
+ ;
+
+ ic->ic_htcaps =
+ IEEE80211_HTCAP_SMPS_ENA /* SM PS mode enabled */
+ | IEEE80211_HTCAP_CHWIDTH40 /* 40MHz channel width */
+ | IEEE80211_HTCAP_SHORTGI20 /* short GI in 20MHz */
+ | IEEE80211_HTCAP_SHORTGI40 /* short GI in 40MHz */
+ | IEEE80211_HTCAP_RXSTBC_2STREAM/* 1-2 spatial streams */
+#if MWL_AGGR_SIZE == 7935
+ | IEEE80211_HTCAP_MAXAMSDU_7935 /* max A-MSDU length */
+#else
+ | IEEE80211_HTCAP_MAXAMSDU_3839 /* max A-MSDU length */
+#endif
+#if 0
+ | IEEE80211_HTCAP_PSMP /* PSMP supported */
+ | IEEE80211_HTCAP_40INTOLERANT /* 40MHz intolerant */
+#endif
+ /* s/w capabilities */
+ | IEEE80211_HTC_HT /* HT operation */
+ | IEEE80211_HTC_AMPDU /* tx A-MPDU */
+ | IEEE80211_HTC_AMSDU /* tx A-MSDU */
+ | IEEE80211_HTC_SMPS /* SMPS available */
+ ;
+
+ /*
+ * Mark h/w crypto support.
+ * XXX no way to query h/w support.
+ */
+ ic->ic_cryptocaps |= IEEE80211_CRYPTO_WEP
+ | IEEE80211_CRYPTO_AES_CCM
+ | IEEE80211_CRYPTO_TKIP
+ | IEEE80211_CRYPTO_TKIPMIC
+ ;
+ /*
+ * Transmit requires space in the packet for a special
+ * format transmit record and optional padding between
+ * this record and the payload. Ask the net80211 layer
+ * to arrange this when encapsulating packets so we can
+ * add it efficiently.
+ */
+ ic->ic_headroom = sizeof(struct mwltxrec) -
+ sizeof(struct ieee80211_frame);
+
+ /* call MI attach routine. */
+ ieee80211_ifattach(ic, sc->sc_hwspecs.macAddr);
+ ic->ic_setregdomain = mwl_setregdomain;
+ ic->ic_getradiocaps = mwl_getradiocaps;
+ /* override default methods */
+ ic->ic_raw_xmit = mwl_raw_xmit;
+ ic->ic_newassoc = mwl_newassoc;
+ ic->ic_updateslot = mwl_updateslot;
+ ic->ic_update_mcast = mwl_update_mcast;
+ ic->ic_update_promisc = mwl_update_promisc;
+ ic->ic_wme.wme_update = mwl_wme_update;
+
+ ic->ic_node_alloc = mwl_node_alloc;
+ sc->sc_node_cleanup = ic->ic_node_cleanup;
+ ic->ic_node_cleanup = mwl_node_cleanup;
+ sc->sc_node_drain = ic->ic_node_drain;
+ ic->ic_node_drain = mwl_node_drain;
+ ic->ic_node_getsignal = mwl_node_getsignal;
+ ic->ic_node_getmimoinfo = mwl_node_getmimoinfo;
+
+ ic->ic_scan_start = mwl_scan_start;
+ ic->ic_scan_end = mwl_scan_end;
+ ic->ic_set_channel = mwl_set_channel;
+
+ sc->sc_recv_action = ic->ic_recv_action;
+ ic->ic_recv_action = mwl_recv_action;
+ sc->sc_addba_request = ic->ic_addba_request;
+ ic->ic_addba_request = mwl_addba_request;
+ sc->sc_addba_response = ic->ic_addba_response;
+ ic->ic_addba_response = mwl_addba_response;
+ sc->sc_addba_stop = ic->ic_addba_stop;
+ ic->ic_addba_stop = mwl_addba_stop;
+
+ ic->ic_vap_create = mwl_vap_create;
+ ic->ic_vap_delete = mwl_vap_delete;
+
+ ieee80211_radiotap_attach(ic,
+ &sc->sc_tx_th.wt_ihdr, sizeof(sc->sc_tx_th),
+ MWL_TX_RADIOTAP_PRESENT,
+ &sc->sc_rx_th.wr_ihdr, sizeof(sc->sc_rx_th),
+ MWL_RX_RADIOTAP_PRESENT);
+ /*
+ * Setup dynamic sysctl's now that country code and
+ * regdomain are available from the hal.
+ */
+ mwl_sysctlattach(sc);
+
+ if (bootverbose)
+ ieee80211_announce(ic);
+ mwl_announce(sc);
+ return 0;
+bad2:
+ mwl_dma_cleanup(sc);
+bad1:
+ mwl_hal_detach(mh);
+bad:
+ if_free(ifp);
+ sc->sc_invalid = 1;
+ return error;
+}
+
+int
+mwl_detach(struct mwl_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+
+ DPRINTF(sc, MWL_DEBUG_ANY, "%s: if_flags %x\n",
+ __func__, ifp->if_flags);
+
+ mwl_stop(ifp, 1);
+ /*
+ * NB: the order of these is important:
+ * o call the 802.11 layer before detaching the hal to
+ * insure callbacks into the driver to delete global
+ * key cache entries can be handled
+ * o reclaim the tx queue data structures after calling
+ * the 802.11 layer as we'll get called back to reclaim
+ * node state and potentially want to use them
+ * o to cleanup the tx queues the hal is called, so detach
+ * it last
+ * Other than that, it's straightforward...
+ */
+ ieee80211_ifdetach(ic);
+ mwl_dma_cleanup(sc);
+ mwl_tx_cleanup(sc);
+ mwl_hal_detach(sc->sc_mh);
+ if_free(ifp);
+
+ return 0;
+}
+
+/*
+ * MAC address handling for multiple BSS on the same radio.
+ * The first vap uses the MAC address from the EEPROM. For
+ * subsequent vap's we set the U/L bit (bit 1) in the MAC
+ * address and use the next six bits as an index.
+ */
+static void
+assign_address(struct mwl_softc *sc, uint8_t mac[IEEE80211_ADDR_LEN], int clone)
+{
+ int i;
+
+ if (clone && mwl_hal_ismbsscapable(sc->sc_mh)) {
+ /* NB: we only do this if h/w supports multiple bssid */
+ for (i = 0; i < 32; i++)
+ if ((sc->sc_bssidmask & (1<<i)) == 0)
+ break;
+ if (i != 0)
+ mac[0] |= (i << 2)|0x2;
+ } else
+ i = 0;
+ sc->sc_bssidmask |= 1<<i;
+ if (i == 0)
+ sc->sc_nbssid0++;
+}
+
+static void
+reclaim_address(struct mwl_softc *sc, uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ int i = mac[0] >> 2;
+ if (i != 0 || --sc->sc_nbssid0 == 0)
+ sc->sc_bssidmask &= ~(1<<i);
+}
+
+static struct ieee80211vap *
+mwl_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac0[IEEE80211_ADDR_LEN])
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct mwl_softc *sc = ifp->if_softc;
+ struct mwl_hal *mh = sc->sc_mh;
+ struct ieee80211vap *vap, *apvap;
+ struct mwl_hal_vap *hvap;
+ struct mwl_vap *mvp;
+ uint8_t mac[IEEE80211_ADDR_LEN];
+
+ IEEE80211_ADDR_COPY(mac, mac0);
+ switch (opmode) {
+ case IEEE80211_M_HOSTAP:
+ if ((flags & IEEE80211_CLONE_MACADDR) == 0)
+ assign_address(sc, mac, flags & IEEE80211_CLONE_BSSID);
+ hvap = mwl_hal_newvap(mh, MWL_HAL_AP, mac);
+ if (hvap == NULL) {
+ if ((flags & IEEE80211_CLONE_MACADDR) == 0)
+ reclaim_address(sc, mac);
+ return NULL;
+ }
+ break;
+ case IEEE80211_M_STA:
+ if ((flags & IEEE80211_CLONE_MACADDR) == 0)
+ assign_address(sc, mac, flags & IEEE80211_CLONE_BSSID);
+ hvap = mwl_hal_newvap(mh, MWL_HAL_STA, mac);
+ if (hvap == NULL) {
+ if ((flags & IEEE80211_CLONE_MACADDR) == 0)
+ reclaim_address(sc, mac);
+ return NULL;
+ }
+ /* no h/w beacon miss support; always use s/w */
+ flags |= IEEE80211_CLONE_NOBEACONS;
+ break;
+ case IEEE80211_M_WDS:
+ hvap = NULL; /* NB: we use associated AP vap */
+ if (sc->sc_napvaps == 0)
+ return NULL; /* no existing AP vap */
+ break;
+ case IEEE80211_M_MONITOR:
+ hvap = NULL;
+ break;
+ case IEEE80211_M_IBSS:
+ case IEEE80211_M_AHDEMO:
+ default:
+ return NULL;
+ }
+
+ mvp = (struct mwl_vap *) malloc(sizeof(struct mwl_vap),
+ M_80211_VAP, M_NOWAIT | M_ZERO);
+ if (mvp == NULL) {
+ if (hvap != NULL) {
+ mwl_hal_delvap(hvap);
+ if ((flags & IEEE80211_CLONE_MACADDR) == 0)
+ reclaim_address(sc, mac);
+ }
+ /* XXX msg */
+ return NULL;
+ }
+ mvp->mv_hvap = hvap;
+ if (opmode == IEEE80211_M_WDS) {
+ /*
+ * WDS vaps must have an associated AP vap; find one.
+ * XXX not right.
+ */
+ TAILQ_FOREACH(apvap, &ic->ic_vaps, iv_next)
+ if (apvap->iv_opmode == IEEE80211_M_HOSTAP) {
+ mvp->mv_ap_hvap = MWL_VAP(apvap)->mv_hvap;
+ break;
+ }
+ KASSERT(mvp->mv_ap_hvap != NULL, ("no ap vap"));
+ }
+ vap = &mvp->mv_vap;
+ ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac);
+ if (hvap != NULL)
+ IEEE80211_ADDR_COPY(vap->iv_myaddr, mac);
+ /* override with driver methods */
+ mvp->mv_newstate = vap->iv_newstate;
+ vap->iv_newstate = mwl_newstate;
+ vap->iv_max_keyix = 0; /* XXX */
+ vap->iv_key_alloc = mwl_key_alloc;
+ vap->iv_key_delete = mwl_key_delete;
+ vap->iv_key_set = mwl_key_set;
+#ifdef MWL_HOST_PS_SUPPORT
+ if (opmode == IEEE80211_M_HOSTAP) {
+ vap->iv_update_ps = mwl_update_ps;
+ mvp->mv_set_tim = vap->iv_set_tim;
+ vap->iv_set_tim = mwl_set_tim;
+ }
+#endif
+ vap->iv_reset = mwl_reset;
+ vap->iv_update_beacon = mwl_beacon_update;
+
+ /* override max aid so sta's cannot assoc when we're out of sta id's */
+ vap->iv_max_aid = MWL_MAXSTAID;
+ /* override default A-MPDU rx parameters */
+ vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_64K;
+ vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_4;
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, mwl_media_change, ieee80211_media_status);
+
+ switch (vap->iv_opmode) {
+ case IEEE80211_M_HOSTAP:
+ case IEEE80211_M_STA:
+ /*
+ * Setup sta db entry for local address.
+ */
+ mwl_localstadb(vap);
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP)
+ sc->sc_napvaps++;
+ else
+ sc->sc_nstavaps++;
+ break;
+ case IEEE80211_M_WDS:
+ sc->sc_nwdsvaps++;
+ break;
+ default:
+ break;
+ }
+ /*
+ * Setup overall operating mode.
+ */
+ if (sc->sc_napvaps)
+ ic->ic_opmode = IEEE80211_M_HOSTAP;
+ else if (sc->sc_nstavaps)
+ ic->ic_opmode = IEEE80211_M_STA;
+ else
+ ic->ic_opmode = opmode;
+
+ return vap;
+}
+
+static void
+mwl_vap_delete(struct ieee80211vap *vap)
+{
+ struct mwl_vap *mvp = MWL_VAP(vap);
+ struct ifnet *parent = vap->iv_ic->ic_ifp;
+ struct mwl_softc *sc = parent->if_softc;
+ struct mwl_hal *mh = sc->sc_mh;
+ struct mwl_hal_vap *hvap = mvp->mv_hvap;
+ enum ieee80211_opmode opmode = vap->iv_opmode;
+
+ /* XXX disallow ap vap delete if WDS still present */
+ if (parent->if_drv_flags & IFF_DRV_RUNNING) {
+ /* quiesce h/w while we remove the vap */
+ mwl_hal_intrset(mh, 0); /* disable interrupts */
+ }
+ ieee80211_vap_detach(vap);
+ switch (opmode) {
+ case IEEE80211_M_HOSTAP:
+ case IEEE80211_M_STA:
+ KASSERT(hvap != NULL, ("no hal vap handle"));
+ (void) mwl_hal_delstation(hvap, vap->iv_myaddr);
+ mwl_hal_delvap(hvap);
+ if (opmode == IEEE80211_M_HOSTAP)
+ sc->sc_napvaps--;
+ else
+ sc->sc_nstavaps--;
+ /* XXX don't do it for IEEE80211_CLONE_MACADDR */
+ reclaim_address(sc, vap->iv_myaddr);
+ break;
+ case IEEE80211_M_WDS:
+ sc->sc_nwdsvaps--;
+ break;
+ default:
+ break;
+ }
+ mwl_cleartxq(sc, vap);
+ free(mvp, M_80211_VAP);
+ if (parent->if_drv_flags & IFF_DRV_RUNNING)
+ mwl_hal_intrset(mh, sc->sc_imask);
+}
+
+void
+mwl_suspend(struct mwl_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+
+ DPRINTF(sc, MWL_DEBUG_ANY, "%s: if_flags %x\n",
+ __func__, ifp->if_flags);
+
+ mwl_stop(ifp, 1);
+}
+
+void
+mwl_resume(struct mwl_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+
+ DPRINTF(sc, MWL_DEBUG_ANY, "%s: if_flags %x\n",
+ __func__, ifp->if_flags);
+
+ if (ifp->if_flags & IFF_UP)
+ mwl_init(sc);
+}
+
+void
+mwl_shutdown(void *arg)
+{
+ struct mwl_softc *sc = arg;
+
+ mwl_stop(sc->sc_ifp, 1);
+}
+
+/*
+ * Interrupt handler. Most of the actual processing is deferred.
+ */
+void
+mwl_intr(void *arg)
+{
+ struct mwl_softc *sc = arg;
+ struct mwl_hal *mh = sc->sc_mh;
+ uint32_t status;
+
+ if (sc->sc_invalid) {
+ /*
+ * The hardware is not ready/present, don't touch anything.
+ * Note this can happen early on if the IRQ is shared.
+ */
+ DPRINTF(sc, MWL_DEBUG_ANY, "%s: invalid; ignored\n", __func__);
+ return;
+ }
+ /*
+ * Figure out the reason(s) for the interrupt.
+ */
+ mwl_hal_getisr(mh, &status); /* NB: clears ISR too */
+ if (status == 0) /* must be a shared irq */
+ return;
+
+ DPRINTF(sc, MWL_DEBUG_INTR, "%s: status 0x%x imask 0x%x\n",
+ __func__, status, sc->sc_imask);
+ if (status & MACREG_A2HRIC_BIT_RX_RDY)
+ taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask);
+ if (status & MACREG_A2HRIC_BIT_TX_DONE)
+ taskqueue_enqueue(sc->sc_tq, &sc->sc_txtask);
+ if (status & MACREG_A2HRIC_BIT_BA_WATCHDOG)
+ taskqueue_enqueue(sc->sc_tq, &sc->sc_bawatchdogtask);
+ if (status & MACREG_A2HRIC_BIT_OPC_DONE)
+ mwl_hal_cmddone(mh);
+ if (status & MACREG_A2HRIC_BIT_MAC_EVENT) {
+ ;
+ }
+ if (status & MACREG_A2HRIC_BIT_ICV_ERROR) {
+ /* TKIP ICV error */
+ sc->sc_stats.mst_rx_badtkipicv++;
+ }
+ if (status & MACREG_A2HRIC_BIT_QUEUE_EMPTY) {
+ /* 11n aggregation queue is empty, re-fill */
+ ;
+ }
+ if (status & MACREG_A2HRIC_BIT_QUEUE_FULL) {
+ ;
+ }
+ if (status & MACREG_A2HRIC_BIT_RADAR_DETECT) {
+ /* radar detected, process event */
+ taskqueue_enqueue(sc->sc_tq, &sc->sc_radartask);
+ }
+ if (status & MACREG_A2HRIC_BIT_CHAN_SWITCH) {
+ /* DFS channel switch */
+ taskqueue_enqueue(sc->sc_tq, &sc->sc_chanswitchtask);
+ }
+}
+
+static void
+mwl_radar_proc(void *arg, int pending)
+{
+ struct mwl_softc *sc = arg;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+
+ DPRINTF(sc, MWL_DEBUG_ANY, "%s: radar detected, pending %u\n",
+ __func__, pending);
+
+ sc->sc_stats.mst_radardetect++;
+
+ IEEE80211_LOCK(ic);
+ ieee80211_dfs_notify_radar(ic, ic->ic_curchan);
+ IEEE80211_UNLOCK(ic);
+}
+
+static void
+mwl_chanswitch_proc(void *arg, int pending)
+{
+ struct mwl_softc *sc = arg;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+
+ DPRINTF(sc, MWL_DEBUG_ANY, "%s: channel switch notice, pending %u\n",
+ __func__, pending);
+
+ IEEE80211_LOCK(ic);
+ sc->sc_csapending = 0;
+ ieee80211_csa_completeswitch(ic);
+ IEEE80211_UNLOCK(ic);
+}
+
+static void
+mwl_bawatchdog(const MWL_HAL_BASTREAM *sp)
+{
+ struct ieee80211_node *ni = sp->data[0];
+
+ /* send DELBA and drop the stream */
+ ieee80211_ampdu_stop(ni, sp->data[1], IEEE80211_REASON_UNSPECIFIED);
+}
+
+static void
+mwl_bawatchdog_proc(void *arg, int pending)
+{
+ struct mwl_softc *sc = arg;
+ struct mwl_hal *mh = sc->sc_mh;
+ const MWL_HAL_BASTREAM *sp;
+ uint8_t bitmap, n;
+
+ sc->sc_stats.mst_bawatchdog++;
+
+ if (mwl_hal_getwatchdogbitmap(mh, &bitmap) != 0) {
+ DPRINTF(sc, MWL_DEBUG_AMPDU,
+ "%s: could not get bitmap\n", __func__);
+ sc->sc_stats.mst_bawatchdog_failed++;
+ return;
+ }
+ DPRINTF(sc, MWL_DEBUG_AMPDU, "%s: bitmap 0x%x\n", __func__, bitmap);
+ if (bitmap == 0xff) {
+ n = 0;
+ /* disable all ba streams */
+ for (bitmap = 0; bitmap < 8; bitmap++) {
+ sp = mwl_hal_bastream_lookup(mh, bitmap);
+ if (sp != NULL) {
+ mwl_bawatchdog(sp);
+ n++;
+ }
+ }
+ if (n == 0) {
+ DPRINTF(sc, MWL_DEBUG_AMPDU,
+ "%s: no BA streams found\n", __func__);
+ sc->sc_stats.mst_bawatchdog_empty++;
+ }
+ } else if (bitmap != 0xaa) {
+ /* disable a single ba stream */
+ sp = mwl_hal_bastream_lookup(mh, bitmap);
+ if (sp != NULL) {
+ mwl_bawatchdog(sp);
+ } else {
+ DPRINTF(sc, MWL_DEBUG_AMPDU,
+ "%s: no BA stream %d\n", __func__, bitmap);
+ sc->sc_stats.mst_bawatchdog_notfound++;
+ }
+ }
+}
+
+/*
+ * Convert net80211 channel to a HAL channel.
+ */
+static void
+mwl_mapchan(MWL_HAL_CHANNEL *hc, const struct ieee80211_channel *chan)
+{
+ hc->channel = chan->ic_ieee;
+
+ *(uint32_t *)&hc->channelFlags = 0;
+ if (IEEE80211_IS_CHAN_2GHZ(chan))
+ hc->channelFlags.FreqBand = MWL_FREQ_BAND_2DOT4GHZ;
+ else if (IEEE80211_IS_CHAN_5GHZ(chan))
+ hc->channelFlags.FreqBand = MWL_FREQ_BAND_5GHZ;
+ if (IEEE80211_IS_CHAN_HT40(chan)) {
+ hc->channelFlags.ChnlWidth = MWL_CH_40_MHz_WIDTH;
+ if (IEEE80211_IS_CHAN_HT40U(chan))
+ hc->channelFlags.ExtChnlOffset = MWL_EXT_CH_ABOVE_CTRL_CH;
+ else
+ hc->channelFlags.ExtChnlOffset = MWL_EXT_CH_BELOW_CTRL_CH;
+ } else
+ hc->channelFlags.ChnlWidth = MWL_CH_20_MHz_WIDTH;
+ /* XXX 10MHz channels */
+}
+
+/*
+ * Inform firmware of our tx/rx dma setup. The BAR 0
+ * writes below are for compatibility with older firmware.
+ * For current firmware we send this information with a
+ * cmd block via mwl_hal_sethwdma.
+ */
+static int
+mwl_setupdma(struct mwl_softc *sc)
+{
+ int error, i;
+
+ sc->sc_hwdma.rxDescRead = sc->sc_rxdma.dd_desc_paddr;
+ WR4(sc, sc->sc_hwspecs.rxDescRead, sc->sc_hwdma.rxDescRead);
+ WR4(sc, sc->sc_hwspecs.rxDescWrite, sc->sc_hwdma.rxDescRead);
+
+ for (i = 0; i < MWL_NUM_TX_QUEUES; i++) {
+ struct mwl_txq *txq = &sc->sc_txq[i];
+ sc->sc_hwdma.wcbBase[i] = txq->dma.dd_desc_paddr;
+ WR4(sc, sc->sc_hwspecs.wcbBase[i], sc->sc_hwdma.wcbBase[i]);
+ }
+ sc->sc_hwdma.maxNumTxWcb = mwl_txbuf;
+ sc->sc_hwdma.maxNumWCB = MWL_NUM_TX_QUEUES;
+
+ error = mwl_hal_sethwdma(sc->sc_mh, &sc->sc_hwdma);
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "unable to setup tx/rx dma; hal status %u\n", error);
+ /* XXX */
+ }
+ return error;
+}
+
+/*
+ * Inform firmware of tx rate parameters.
+ * Called after a channel change.
+ */
+static int
+mwl_setcurchanrates(struct mwl_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ const struct ieee80211_rateset *rs;
+ MWL_HAL_TXRATE rates;
+
+ memset(&rates, 0, sizeof(rates));
+ rs = ieee80211_get_suprates(ic, ic->ic_curchan);
+ /* rate used to send management frames */
+ rates.MgtRate = rs->rs_rates[0] & IEEE80211_RATE_VAL;
+ /* rate used to send multicast frames */
+ rates.McastRate = rates.MgtRate;
+
+ return mwl_hal_settxrate_auto(sc->sc_mh, &rates);
+}
+
+/*
+ * Inform firmware of tx rate parameters. Called whenever
+ * user-settable params change and after a channel change.
+ */
+static int
+mwl_setrates(struct ieee80211vap *vap)
+{
+ struct mwl_vap *mvp = MWL_VAP(vap);
+ struct ieee80211_node *ni = vap->iv_bss;
+ const struct ieee80211_txparam *tp = ni->ni_txparms;
+ MWL_HAL_TXRATE rates;
+
+ KASSERT(vap->iv_state == IEEE80211_S_RUN, ("state %d", vap->iv_state));
+
+ /*
+ * Update the h/w rate map.
+ * NB: 0x80 for MCS is passed through unchanged
+ */
+ memset(&rates, 0, sizeof(rates));
+ /* rate used to send management frames */
+ rates.MgtRate = tp->mgmtrate;
+ /* rate used to send multicast frames */
+ rates.McastRate = tp->mcastrate;
+
+ /* while here calculate EAPOL fixed rate cookie */
+ mvp->mv_eapolformat = htole16(mwl_calcformat(rates.MgtRate, ni));
+
+ return mwl_hal_settxrate(mvp->mv_hvap, RATE_AUTO, &rates);
+}
+
+/*
+ * Setup a fixed xmit rate cookie for EAPOL frames.
+ */
+static void
+mwl_seteapolformat(struct ieee80211vap *vap)
+{
+ struct mwl_vap *mvp = MWL_VAP(vap);
+ struct ieee80211_node *ni = vap->iv_bss;
+ enum ieee80211_phymode mode;
+ uint8_t rate;
+
+ KASSERT(vap->iv_state == IEEE80211_S_RUN, ("state %d", vap->iv_state));
+
+ mode = ieee80211_chan2mode(ni->ni_chan);
+ /*
+ * Use legacy rates when operating a mixed HT+non-HT bss.
+ * NB: this may violate POLA for sta and wds vap's.
+ */
+ if (mode == IEEE80211_MODE_11NA &&
+ (vap->iv_flags_ext & IEEE80211_FEXT_PUREN) == 0)
+ rate = vap->iv_txparms[IEEE80211_MODE_11A].mgmtrate;
+ else if (mode == IEEE80211_MODE_11NG &&
+ (vap->iv_flags_ext & IEEE80211_FEXT_PUREN) == 0)
+ rate = vap->iv_txparms[IEEE80211_MODE_11G].mgmtrate;
+ else
+ rate = vap->iv_txparms[mode].mgmtrate;
+
+ mvp->mv_eapolformat = htole16(mwl_calcformat(rate, ni));
+}
+
+/*
+ * Map SKU+country code to region code for radar bin'ing.
+ */
+static int
+mwl_map2regioncode(const struct ieee80211_regdomain *rd)
+{
+ switch (rd->regdomain) {
+ case SKU_FCC:
+ case SKU_FCC3:
+ return DOMAIN_CODE_FCC;
+ case SKU_CA:
+ return DOMAIN_CODE_IC;
+ case SKU_ETSI:
+ case SKU_ETSI2:
+ case SKU_ETSI3:
+ if (rd->country == CTRY_SPAIN)
+ return DOMAIN_CODE_SPAIN;
+ if (rd->country == CTRY_FRANCE || rd->country == CTRY_FRANCE2)
+ return DOMAIN_CODE_FRANCE;
+ /* XXX force 1.3.1 radar type */
+ return DOMAIN_CODE_ETSI_131;
+ case SKU_JAPAN:
+ return DOMAIN_CODE_MKK;
+ case SKU_ROW:
+ return DOMAIN_CODE_DGT; /* Taiwan */
+ case SKU_APAC:
+ case SKU_APAC2:
+ case SKU_APAC3:
+ return DOMAIN_CODE_AUS; /* Australia */
+ }
+ /* XXX KOREA? */
+ return DOMAIN_CODE_FCC; /* XXX? */
+}
+
+static int
+mwl_hal_reset(struct mwl_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct mwl_hal *mh = sc->sc_mh;
+
+ mwl_hal_setantenna(mh, WL_ANTENNATYPE_RX, sc->sc_rxantenna);
+ mwl_hal_setantenna(mh, WL_ANTENNATYPE_TX, sc->sc_txantenna);
+ mwl_hal_setradio(mh, 1, WL_AUTO_PREAMBLE);
+ mwl_hal_setwmm(sc->sc_mh, (ic->ic_flags & IEEE80211_F_WME) != 0);
+ mwl_chan_set(sc, ic->ic_curchan);
+ mwl_hal_setrateadaptmode(mh, ic->ic_regdomain.location == 'O');
+ mwl_hal_setoptimizationlevel(mh,
+ (ic->ic_flags & IEEE80211_F_BURST) != 0);
+
+ mwl_hal_setregioncode(mh, mwl_map2regioncode(&ic->ic_regdomain));
+
+ return 1;
+}
+
+static int
+mwl_init_locked(struct mwl_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct mwl_hal *mh = sc->sc_mh;
+ int error = 0;
+
+ DPRINTF(sc, MWL_DEBUG_ANY, "%s: if_flags 0x%x\n",
+ __func__, ifp->if_flags);
+
+ MWL_LOCK_ASSERT(sc);
+
+ /*
+ * Stop anything previously setup. This is safe
+ * whether this is the first time through or not.
+ */
+ mwl_stop_locked(ifp, 0);
+
+ /*
+ * Push vap-independent state to the firmware.
+ */
+ if (!mwl_hal_reset(sc)) {
+ if_printf(ifp, "unable to reset hardware\n");
+ return EIO;
+ }
+
+ /*
+ * Setup recv (once); transmit is already good to go.
+ */
+ error = mwl_startrecv(sc);
+ if (error != 0) {
+ if_printf(ifp, "unable to start recv logic\n");
+ return error;
+ }
+
+ /*
+ * Enable interrupts.
+ */
+ sc->sc_imask = MACREG_A2HRIC_BIT_RX_RDY
+ | MACREG_A2HRIC_BIT_TX_DONE
+ | MACREG_A2HRIC_BIT_OPC_DONE
+#if 0
+ | MACREG_A2HRIC_BIT_MAC_EVENT
+#endif
+ | MACREG_A2HRIC_BIT_ICV_ERROR
+ | MACREG_A2HRIC_BIT_RADAR_DETECT
+ | MACREG_A2HRIC_BIT_CHAN_SWITCH
+#if 0
+ | MACREG_A2HRIC_BIT_QUEUE_EMPTY
+#endif
+ | MACREG_A2HRIC_BIT_BA_WATCHDOG
+ ;
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ mwl_hal_intrset(mh, sc->sc_imask);
+
+ return 0;
+}
+
+static void
+mwl_init(void *arg)
+{
+ struct mwl_softc *sc = arg;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ int error = 0;
+
+ DPRINTF(sc, MWL_DEBUG_ANY, "%s: if_flags 0x%x\n",
+ __func__, ifp->if_flags);
+
+ MWL_LOCK(sc);
+ error = mwl_init_locked(sc);
+ MWL_UNLOCK(sc);
+
+ if (error == 0)
+ ieee80211_start_all(ic); /* start all vap's */
+}
+
+static void
+mwl_stop_locked(struct ifnet *ifp, int disable)
+{
+ struct mwl_softc *sc = ifp->if_softc;
+
+ DPRINTF(sc, MWL_DEBUG_ANY, "%s: invalid %u if_flags 0x%x\n",
+ __func__, sc->sc_invalid, ifp->if_flags);
+
+ MWL_LOCK_ASSERT(sc);
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ /*
+ * Shutdown the hardware and driver.
+ */
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ ifp->if_timer = 0;
+ mwl_draintxq(sc);
+ }
+}
+
+static void
+mwl_stop(struct ifnet *ifp, int disable)
+{
+ struct mwl_softc *sc = ifp->if_softc;
+
+ MWL_LOCK(sc);
+ mwl_stop_locked(ifp, disable);
+ MWL_UNLOCK(sc);
+}
+
+static int
+mwl_reset_vap(struct ieee80211vap *vap, int state)
+{
+ struct mwl_hal_vap *hvap = MWL_VAP(vap)->mv_hvap;
+ struct ieee80211com *ic = vap->iv_ic;
+
+ if (state == IEEE80211_S_RUN)
+ mwl_setrates(vap);
+ /* XXX off by 1? */
+ mwl_hal_setrtsthreshold(hvap, vap->iv_rtsthreshold);
+ /* XXX auto? 20/40 split? */
+ mwl_hal_sethtgi(hvap, (vap->iv_flags_ext &
+ (IEEE80211_FEXT_SHORTGI20|IEEE80211_FEXT_SHORTGI40)) ? 1 : 0);
+ mwl_hal_setnprot(hvap, ic->ic_htprotmode == IEEE80211_PROT_NONE ?
+ HTPROTECT_NONE : HTPROTECT_AUTO);
+ /* XXX txpower cap */
+
+ /* re-setup beacons */
+ if (state == IEEE80211_S_RUN &&
+ (vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_IBSS)) {
+ mwl_setapmode(vap, vap->iv_bss->ni_chan);
+ mwl_hal_setnprotmode(hvap,
+ MS(ic->ic_curhtprotmode, IEEE80211_HTINFO_OPMODE));
+ return mwl_beacon_setup(vap);
+ }
+ return 0;
+}
+
+/*
+ * Reset the hardware w/o losing operational state.
+ * Used to to reset or reload hardware state for a vap.
+ */
+static int
+mwl_reset(struct ieee80211vap *vap, u_long cmd)
+{
+ struct mwl_hal_vap *hvap = MWL_VAP(vap)->mv_hvap;
+ int error = 0;
+
+ if (hvap != NULL) { /* WDS, MONITOR, etc. */
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ifnet *ifp = ic->ic_ifp;
+ struct mwl_softc *sc = ifp->if_softc;
+ struct mwl_hal *mh = sc->sc_mh;
+
+ /* XXX do we need to disable interrupts? */
+ mwl_hal_intrset(mh, 0); /* disable interrupts */
+ error = mwl_reset_vap(vap, vap->iv_state);
+ mwl_hal_intrset(mh, sc->sc_imask);
+ }
+ return error;
+}
+
+/*
+ * Allocate a tx buffer for sending a frame. The
+ * packet is assumed to have the WME AC stored so
+ * we can use it to select the appropriate h/w queue.
+ */
+static struct mwl_txbuf *
+mwl_gettxbuf(struct mwl_softc *sc, struct mwl_txq *txq)
+{
+ struct mwl_txbuf *bf;
+
+ /*
+ * Grab a TX buffer and associated resources.
+ */
+ MWL_TXQ_LOCK(txq);
+ bf = STAILQ_FIRST(&txq->free);
+ if (bf != NULL) {
+ STAILQ_REMOVE_HEAD(&txq->free, bf_list);
+ txq->nfree--;
+ }
+ MWL_TXQ_UNLOCK(txq);
+ if (bf == NULL)
+ DPRINTF(sc, MWL_DEBUG_XMIT,
+ "%s: out of xmit buffers on q %d\n", __func__, txq->qnum);
+ return bf;
+}
+
+/*
+ * Return a tx buffer to the queue it came from. Note there
+ * are two cases because we must preserve the order of buffers
+ * as it reflects the fixed order of descriptors in memory
+ * (the firmware pre-fetches descriptors so we cannot reorder).
+ */
+static void
+mwl_puttxbuf_head(struct mwl_txq *txq, struct mwl_txbuf *bf)
+{
+ bf->bf_m = NULL;
+ bf->bf_node = NULL;
+ MWL_TXQ_LOCK(txq);
+ STAILQ_INSERT_HEAD(&txq->free, bf, bf_list);
+ txq->nfree++;
+ MWL_TXQ_UNLOCK(txq);
+}
+
+static void
+mwl_puttxbuf_tail(struct mwl_txq *txq, struct mwl_txbuf *bf)
+{
+ bf->bf_m = NULL;
+ bf->bf_node = NULL;
+ MWL_TXQ_LOCK(txq);
+ STAILQ_INSERT_TAIL(&txq->free, bf, bf_list);
+ txq->nfree++;
+ MWL_TXQ_UNLOCK(txq);
+}
+
+static void
+mwl_start(struct ifnet *ifp)
+{
+ struct mwl_softc *sc = ifp->if_softc;
+ struct ieee80211_node *ni;
+ struct mwl_txbuf *bf;
+ struct mbuf *m;
+ struct mwl_txq *txq = NULL; /* XXX silence gcc */
+ int nqueued;
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || sc->sc_invalid)
+ return;
+ nqueued = 0;
+ for (;;) {
+ bf = NULL;
+ IFQ_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+ /*
+ * Grab the node for the destination.
+ */
+ ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
+ KASSERT(ni != NULL, ("no node"));
+ m->m_pkthdr.rcvif = NULL; /* committed, clear ref */
+ /*
+ * Grab a TX buffer and associated resources.
+ * We honor the classification by the 802.11 layer.
+ */
+ txq = sc->sc_ac2q[M_WME_GETAC(m)];
+ bf = mwl_gettxbuf(sc, txq);
+ if (bf == NULL) {
+ m_freem(m);
+ ieee80211_free_node(ni);
+#ifdef MWL_TX_NODROP
+ sc->sc_stats.mst_tx_qstop++;
+ /* XXX blocks other traffic */
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ break;
+#else
+ DPRINTF(sc, MWL_DEBUG_XMIT,
+ "%s: tail drop on q %d\n", __func__, txq->qnum);
+ sc->sc_stats.mst_tx_qdrop++;
+ continue;
+#endif /* MWL_TX_NODROP */
+ }
+
+ /*
+ * Pass the frame to the h/w for transmission.
+ */
+ if (mwl_tx_start(sc, ni, bf, m)) {
+ ifp->if_oerrors++;
+ mwl_puttxbuf_head(txq, bf);
+ ieee80211_free_node(ni);
+ continue;
+ }
+ nqueued++;
+ if (nqueued >= mwl_txcoalesce) {
+ /*
+ * Poke the firmware to process queued frames;
+ * see below about (lack of) locking.
+ */
+ nqueued = 0;
+ mwl_hal_txstart(sc->sc_mh, 0/*XXX*/);
+ }
+ }
+ if (nqueued) {
+ /*
+ * NB: We don't need to lock against tx done because
+ * this just prods the firmware to check the transmit
+ * descriptors. The firmware will also start fetching
+ * descriptors by itself if it notices new ones are
+ * present when it goes to deliver a tx done interrupt
+ * to the host. So if we race with tx done processing
+ * it's ok. Delivering the kick here rather than in
+ * mwl_tx_start is an optimization to avoid poking the
+ * firmware for each packet.
+ *
+ * NB: the queue id isn't used so 0 is ok.
+ */
+ mwl_hal_txstart(sc->sc_mh, 0/*XXX*/);
+ }
+}
+
+static int
+mwl_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = ic->ic_ifp;
+ struct mwl_softc *sc = ifp->if_softc;
+ struct mwl_txbuf *bf;
+ struct mwl_txq *txq;
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || sc->sc_invalid) {
+ ieee80211_free_node(ni);
+ m_freem(m);
+ return ENETDOWN;
+ }
+ /*
+ * Grab a TX buffer and associated resources.
+ * Note that we depend on the classification
+ * by the 802.11 layer to get to the right h/w
+ * queue. Management frames must ALWAYS go on
+ * queue 1 but we cannot just force that here
+ * because we may receive non-mgt frames.
+ */
+ txq = sc->sc_ac2q[M_WME_GETAC(m)];
+ bf = mwl_gettxbuf(sc, txq);
+ if (bf == NULL) {
+ sc->sc_stats.mst_tx_qstop++;
+ /* XXX blocks other traffic */
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ ieee80211_free_node(ni);
+ m_freem(m);
+ return ENOBUFS;
+ }
+ /*
+ * Pass the frame to the h/w for transmission.
+ */
+ if (mwl_tx_start(sc, ni, bf, m)) {
+ ifp->if_oerrors++;
+ mwl_puttxbuf_head(txq, bf);
+
+ ieee80211_free_node(ni);
+ return EIO; /* XXX */
+ }
+ /*
+ * NB: We don't need to lock against tx done because
+ * this just prods the firmware to check the transmit
+ * descriptors. The firmware will also start fetching
+ * descriptors by itself if it notices new ones are
+ * present when it goes to deliver a tx done interrupt
+ * to the host. So if we race with tx done processing
+ * it's ok. Delivering the kick here rather than in
+ * mwl_tx_start is an optimization to avoid poking the
+ * firmware for each packet.
+ *
+ * NB: the queue id isn't used so 0 is ok.
+ */
+ mwl_hal_txstart(sc->sc_mh, 0/*XXX*/);
+ return 0;
+}
+
+static int
+mwl_media_change(struct ifnet *ifp)
+{
+ struct ieee80211vap *vap = ifp->if_softc;
+ int error;
+
+ error = ieee80211_media_change(ifp);
+ /* NB: only the fixed rate can change and that doesn't need a reset */
+ if (error == ENETRESET) {
+ mwl_setrates(vap);
+ error = 0;
+ }
+ return error;
+}
+
+#ifdef MWL_DEBUG
+static void
+mwl_keyprint(struct mwl_softc *sc, const char *tag,
+ const MWL_HAL_KEYVAL *hk, const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ static const char *ciphers[] = {
+ "WEP",
+ "TKIP",
+ "AES-CCM",
+ };
+ int i, n;
+
+ printf("%s: [%u] %-7s", tag, hk->keyIndex, ciphers[hk->keyTypeId]);
+ for (i = 0, n = hk->keyLen; i < n; i++)
+ printf(" %02x", hk->key.aes[i]);
+ printf(" mac %s", ether_sprintf(mac));
+ if (hk->keyTypeId == KEY_TYPE_ID_TKIP) {
+ printf(" %s", "rxmic");
+ for (i = 0; i < sizeof(hk->key.tkip.rxMic); i++)
+ printf(" %02x", hk->key.tkip.rxMic[i]);
+ printf(" txmic");
+ for (i = 0; i < sizeof(hk->key.tkip.txMic); i++)
+ printf(" %02x", hk->key.tkip.txMic[i]);
+ }
+ printf(" flags 0x%x\n", hk->keyFlags);
+}
+#endif
+
+/*
+ * Allocate a key cache slot for a unicast key. The
+ * firmware handles key allocation and every station is
+ * guaranteed key space so we are always successful.
+ */
+static int
+mwl_key_alloc(struct ieee80211vap *vap, struct ieee80211_key *k,
+ ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
+{
+ struct mwl_softc *sc = vap->iv_ic->ic_ifp->if_softc;
+
+ if (k->wk_keyix != IEEE80211_KEYIX_NONE ||
+ (k->wk_flags & IEEE80211_KEY_GROUP)) {
+ if (!(&vap->iv_nw_keys[0] <= k &&
+ k < &vap->iv_nw_keys[IEEE80211_WEP_NKID])) {
+ /* should not happen */
+ DPRINTF(sc, MWL_DEBUG_KEYCACHE,
+ "%s: bogus group key\n", __func__);
+ return 0;
+ }
+ /* give the caller what they requested */
+ *keyix = *rxkeyix = k - vap->iv_nw_keys;
+ } else {
+ /*
+ * Firmware handles key allocation.
+ */
+ *keyix = *rxkeyix = 0;
+ }
+ return 1;
+}
+
+/*
+ * Delete a key entry allocated by mwl_key_alloc.
+ */
+static int
+mwl_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
+{
+ struct mwl_softc *sc = vap->iv_ic->ic_ifp->if_softc;
+ struct mwl_hal_vap *hvap = MWL_VAP(vap)->mv_hvap;
+ MWL_HAL_KEYVAL hk;
+ const uint8_t bcastaddr[IEEE80211_ADDR_LEN] =
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+ if (hvap == NULL) {
+ if (vap->iv_opmode != IEEE80211_M_WDS) {
+ /* XXX monitor mode? */
+ DPRINTF(sc, MWL_DEBUG_KEYCACHE,
+ "%s: no hvap for opmode %d\n", __func__,
+ vap->iv_opmode);
+ return 0;
+ }
+ hvap = MWL_VAP(vap)->mv_ap_hvap;
+ }
+
+ DPRINTF(sc, MWL_DEBUG_KEYCACHE, "%s: delete key %u\n",
+ __func__, k->wk_keyix);
+
+ memset(&hk, 0, sizeof(hk));
+ hk.keyIndex = k->wk_keyix;
+ switch (k->wk_cipher->ic_cipher) {
+ case IEEE80211_CIPHER_WEP:
+ hk.keyTypeId = KEY_TYPE_ID_WEP;
+ break;
+ case IEEE80211_CIPHER_TKIP:
+ hk.keyTypeId = KEY_TYPE_ID_TKIP;
+ break;
+ case IEEE80211_CIPHER_AES_CCM:
+ hk.keyTypeId = KEY_TYPE_ID_AES;
+ break;
+ default:
+ /* XXX should not happen */
+ DPRINTF(sc, MWL_DEBUG_KEYCACHE, "%s: unknown cipher %d\n",
+ __func__, k->wk_cipher->ic_cipher);
+ return 0;
+ }
+ return (mwl_hal_keyreset(hvap, &hk, bcastaddr) == 0); /*XXX*/
+}
+
+static __inline int
+addgroupflags(MWL_HAL_KEYVAL *hk, const struct ieee80211_key *k)
+{
+ if (k->wk_flags & IEEE80211_KEY_GROUP) {
+ if (k->wk_flags & IEEE80211_KEY_XMIT)
+ hk->keyFlags |= KEY_FLAG_TXGROUPKEY;
+ if (k->wk_flags & IEEE80211_KEY_RECV)
+ hk->keyFlags |= KEY_FLAG_RXGROUPKEY;
+ return 1;
+ } else
+ return 0;
+}
+
+/*
+ * Set the key cache contents for the specified key. Key cache
+ * slot(s) must already have been allocated by mwl_key_alloc.
+ */
+static int
+mwl_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k,
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+#define GRPXMIT (IEEE80211_KEY_XMIT | IEEE80211_KEY_GROUP)
+/* NB: static wep keys are marked GROUP+tx/rx; GTK will be tx or rx */
+#define IEEE80211_IS_STATICKEY(k) \
+ (((k)->wk_flags & (GRPXMIT|IEEE80211_KEY_RECV)) == \
+ (GRPXMIT|IEEE80211_KEY_RECV))
+ struct mwl_softc *sc = vap->iv_ic->ic_ifp->if_softc;
+ struct mwl_hal_vap *hvap = MWL_VAP(vap)->mv_hvap;
+ const struct ieee80211_cipher *cip = k->wk_cipher;
+ const uint8_t *macaddr;
+ MWL_HAL_KEYVAL hk;
+
+ KASSERT((k->wk_flags & IEEE80211_KEY_SWCRYPT) == 0,
+ ("s/w crypto set?"));
+
+ if (hvap == NULL) {
+ if (vap->iv_opmode != IEEE80211_M_WDS) {
+ /* XXX monitor mode? */
+ DPRINTF(sc, MWL_DEBUG_KEYCACHE,
+ "%s: no hvap for opmode %d\n", __func__,
+ vap->iv_opmode);
+ return 0;
+ }
+ hvap = MWL_VAP(vap)->mv_ap_hvap;
+ }
+ memset(&hk, 0, sizeof(hk));
+ hk.keyIndex = k->wk_keyix;
+ switch (cip->ic_cipher) {
+ case IEEE80211_CIPHER_WEP:
+ hk.keyTypeId = KEY_TYPE_ID_WEP;
+ hk.keyLen = k->wk_keylen;
+ if (k->wk_keyix == vap->iv_def_txkey)
+ hk.keyFlags = KEY_FLAG_WEP_TXKEY;
+ if (!IEEE80211_IS_STATICKEY(k)) {
+ /* NB: WEP is never used for the PTK */
+ (void) addgroupflags(&hk, k);
+ }
+ break;
+ case IEEE80211_CIPHER_TKIP:
+ hk.keyTypeId = KEY_TYPE_ID_TKIP;
+ hk.key.tkip.tsc.high = (uint32_t)(k->wk_keytsc >> 16);
+ hk.key.tkip.tsc.low = (uint16_t)k->wk_keytsc;
+ hk.keyFlags = KEY_FLAG_TSC_VALID | KEY_FLAG_MICKEY_VALID;
+ hk.keyLen = k->wk_keylen + IEEE80211_MICBUF_SIZE;
+ if (!addgroupflags(&hk, k))
+ hk.keyFlags |= KEY_FLAG_PAIRWISE;
+ break;
+ case IEEE80211_CIPHER_AES_CCM:
+ hk.keyTypeId = KEY_TYPE_ID_AES;
+ hk.keyLen = k->wk_keylen;
+ if (!addgroupflags(&hk, k))
+ hk.keyFlags |= KEY_FLAG_PAIRWISE;
+ break;
+ default:
+ /* XXX should not happen */
+ DPRINTF(sc, MWL_DEBUG_KEYCACHE, "%s: unknown cipher %d\n",
+ __func__, k->wk_cipher->ic_cipher);
+ return 0;
+ }
+ /*
+ * NB: tkip mic keys get copied here too; the layout
+ * just happens to match that in ieee80211_key.
+ */
+ memcpy(hk.key.aes, k->wk_key, hk.keyLen);
+
+ /*
+ * Locate address of sta db entry for writing key;
+ * the convention unfortunately is somewhat different
+ * than how net80211, hostapd, and wpa_supplicant think.
+ */
+ if (vap->iv_opmode == IEEE80211_M_STA) {
+ /*
+ * NB: keys plumbed before the sta reaches AUTH state
+ * will be discarded or written to the wrong sta db
+ * entry because iv_bss is meaningless. This is ok
+ * (right now) because we handle deferred plumbing of
+ * WEP keys when the sta reaches AUTH state.
+ */
+ macaddr = vap->iv_bss->ni_bssid;
+ } else if (vap->iv_opmode == IEEE80211_M_WDS &&
+ vap->iv_state != IEEE80211_S_RUN) {
+ /*
+ * Prior to RUN state a WDS vap will not it's BSS node
+ * setup so we will plumb the key to the wrong mac
+ * address (it'll be our local address). Workaround
+ * this for the moment by grabbing the correct address.
+ */
+ macaddr = vap->iv_des_bssid;
+ } else if ((k->wk_flags & GRPXMIT) == GRPXMIT)
+ macaddr = vap->iv_myaddr;
+ else
+ macaddr = mac;
+ KEYPRINTF(sc, &hk, macaddr);
+ return (mwl_hal_keyset(hvap, &hk, macaddr) == 0);
+#undef IEEE80211_IS_STATICKEY
+#undef GRPXMIT
+}
+
+/* unaligned little endian access */
+#define LE_READ_2(p) \
+ ((uint16_t) \
+ ((((const uint8_t *)(p))[0] ) | \
+ (((const uint8_t *)(p))[1] << 8)))
+#define LE_READ_4(p) \
+ ((uint32_t) \
+ ((((const uint8_t *)(p))[0] ) | \
+ (((const uint8_t *)(p))[1] << 8) | \
+ (((const uint8_t *)(p))[2] << 16) | \
+ (((const uint8_t *)(p))[3] << 24)))
+
+/*
+ * Set the multicast filter contents into the hardware.
+ * XXX f/w has no support; just defer to the os.
+ */
+static void
+mwl_setmcastfilter(struct mwl_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+#if 0
+ struct ether_multi *enm;
+ struct ether_multistep estep;
+ uint8_t macs[IEEE80211_ADDR_LEN*MWL_HAL_MCAST_MAX];/* XXX stack use */
+ uint8_t *mp;
+ int nmc;
+
+ mp = macs;
+ nmc = 0;
+ ETHER_FIRST_MULTI(estep, &sc->sc_ec, enm);
+ while (enm != NULL) {
+ /* XXX Punt on ranges. */
+ if (nmc == MWL_HAL_MCAST_MAX ||
+ !IEEE80211_ADDR_EQ(enm->enm_addrlo, enm->enm_addrhi)) {
+ ifp->if_flags |= IFF_ALLMULTI;
+ return;
+ }
+ IEEE80211_ADDR_COPY(mp, enm->enm_addrlo);
+ mp += IEEE80211_ADDR_LEN, nmc++;
+ ETHER_NEXT_MULTI(estep, enm);
+ }
+ ifp->if_flags &= ~IFF_ALLMULTI;
+ mwl_hal_setmcast(sc->sc_mh, nmc, macs);
+#else
+ /* XXX no mcast filter support; we get everything */
+ ifp->if_flags |= IFF_ALLMULTI;
+#endif
+}
+
+static int
+mwl_mode_init(struct mwl_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct mwl_hal *mh = sc->sc_mh;
+
+ /*
+ * NB: Ignore promisc in hostap mode; it's set by the
+ * bridge. This is wrong but we have no way to
+ * identify internal requests (from the bridge)
+ * versus external requests such as for tcpdump.
+ */
+ mwl_hal_setpromisc(mh, (ifp->if_flags & IFF_PROMISC) &&
+ ic->ic_opmode != IEEE80211_M_HOSTAP);
+ mwl_setmcastfilter(sc);
+
+ return 0;
+}
+
+/*
+ * Callback from the 802.11 layer after a multicast state change.
+ */
+static void
+mwl_update_mcast(struct ifnet *ifp)
+{
+ struct mwl_softc *sc = ifp->if_softc;
+
+ mwl_setmcastfilter(sc);
+}
+
+/*
+ * Callback from the 802.11 layer after a promiscuous mode change.
+ * Note this interface does not check the operating mode as this
+ * is an internal callback and we are expected to honor the current
+ * state (e.g. this is used for setting the interface in promiscuous
+ * mode when operating in hostap mode to do ACS).
+ */
+static void
+mwl_update_promisc(struct ifnet *ifp)
+{
+ struct mwl_softc *sc = ifp->if_softc;
+
+ mwl_hal_setpromisc(sc->sc_mh, (ifp->if_flags & IFF_PROMISC) != 0);
+}
+
+/*
+ * Callback from the 802.11 layer to update the slot time
+ * based on the current setting. We use it to notify the
+ * firmware of ERP changes and the f/w takes care of things
+ * like slot time and preamble.
+ */
+static void
+mwl_updateslot(struct ifnet *ifp)
+{
+ struct mwl_softc *sc = ifp->if_softc;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct mwl_hal *mh = sc->sc_mh;
+ int prot;
+
+ /* NB: can be called early; suppress needless cmds */
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ return;
+
+ /*
+ * Calculate the ERP flags. The firwmare will use
+ * this to carry out the appropriate measures.
+ */
+ prot = 0;
+ if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) {
+ if ((ic->ic_flags & IEEE80211_F_SHSLOT) == 0)
+ prot |= IEEE80211_ERP_NON_ERP_PRESENT;
+ if (ic->ic_flags & IEEE80211_F_USEPROT)
+ prot |= IEEE80211_ERP_USE_PROTECTION;
+ if (ic->ic_flags & IEEE80211_F_USEBARKER)
+ prot |= IEEE80211_ERP_LONG_PREAMBLE;
+ }
+
+ DPRINTF(sc, MWL_DEBUG_RESET,
+ "%s: chan %u MHz/flags 0x%x %s slot, (prot 0x%x ic_flags 0x%x)\n",
+ __func__, ic->ic_curchan->ic_freq, ic->ic_curchan->ic_flags,
+ ic->ic_flags & IEEE80211_F_SHSLOT ? "short" : "long", prot,
+ ic->ic_flags);
+
+ mwl_hal_setgprot(mh, prot);
+}
+
+/*
+ * Setup the beacon frame.
+ */
+static int
+mwl_beacon_setup(struct ieee80211vap *vap)
+{
+ struct mwl_hal_vap *hvap = MWL_VAP(vap)->mv_hvap;
+ struct ieee80211_node *ni = vap->iv_bss;
+ struct ieee80211_beacon_offsets bo;
+ struct mbuf *m;
+
+ m = ieee80211_beacon_alloc(ni, &bo);
+ if (m == NULL)
+ return ENOBUFS;
+ mwl_hal_setbeacon(hvap, mtod(m, const void *), m->m_len);
+ m_free(m);
+
+ return 0;
+}
+
+/*
+ * Update the beacon frame in response to a change.
+ */
+static void
+mwl_beacon_update(struct ieee80211vap *vap, int item)
+{
+ struct mwl_hal_vap *hvap = MWL_VAP(vap)->mv_hvap;
+ struct ieee80211com *ic = vap->iv_ic;
+
+ KASSERT(hvap != NULL, ("no beacon"));
+ switch (item) {
+ case IEEE80211_BEACON_ERP:
+ mwl_updateslot(ic->ic_ifp);
+ break;
+ case IEEE80211_BEACON_HTINFO:
+ mwl_hal_setnprotmode(hvap,
+ MS(ic->ic_curhtprotmode, IEEE80211_HTINFO_OPMODE));
+ break;
+ case IEEE80211_BEACON_CAPS:
+ case IEEE80211_BEACON_WME:
+ case IEEE80211_BEACON_APPIE:
+ case IEEE80211_BEACON_CSA:
+ break;
+ case IEEE80211_BEACON_TIM:
+ /* NB: firmware always forms TIM */
+ return;
+ }
+ /* XXX retain beacon frame and update */
+ mwl_beacon_setup(vap);
+}
+
+static void
+mwl_load_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
+{
+ bus_addr_t *paddr = (bus_addr_t*) arg;
+ KASSERT(error == 0, ("error %u on bus_dma callback", error));
+ *paddr = segs->ds_addr;
+}
+
+#ifdef MWL_HOST_PS_SUPPORT
+/*
+ * Handle power save station occupancy changes.
+ */
+static void
+mwl_update_ps(struct ieee80211vap *vap, int nsta)
+{
+ struct mwl_vap *mvp = MWL_VAP(vap);
+
+ if (nsta == 0 || mvp->mv_last_ps_sta == 0)
+ mwl_hal_setpowersave_bss(mvp->mv_hvap, nsta);
+ mvp->mv_last_ps_sta = nsta;
+}
+
+/*
+ * Handle associated station power save state changes.
+ */
+static int
+mwl_set_tim(struct ieee80211_node *ni, int set)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct mwl_vap *mvp = MWL_VAP(vap);
+
+ if (mvp->mv_set_tim(ni, set)) { /* NB: state change */
+ mwl_hal_setpowersave_sta(mvp->mv_hvap,
+ IEEE80211_AID(ni->ni_associd), set);
+ return 1;
+ } else
+ return 0;
+}
+#endif /* MWL_HOST_PS_SUPPORT */
+
+static int
+mwl_desc_setup(struct mwl_softc *sc, const char *name,
+ struct mwl_descdma *dd,
+ int nbuf, size_t bufsize, int ndesc, size_t descsize)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ uint8_t *ds;
+ int error;
+
+ DPRINTF(sc, MWL_DEBUG_RESET,
+ "%s: %s DMA: %u bufs (%ju) %u desc/buf (%ju)\n",
+ __func__, name, nbuf, (uintmax_t) bufsize,
+ ndesc, (uintmax_t) descsize);
+
+ dd->dd_name = name;
+ dd->dd_desc_len = nbuf * ndesc * descsize;
+
+ /*
+ * Setup DMA descriptor area.
+ */
+ error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), /* parent */
+ PAGE_SIZE, 0, /* alignment, bounds */
+ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ dd->dd_desc_len, /* maxsize */
+ 1, /* nsegments */
+ dd->dd_desc_len, /* maxsegsize */
+ BUS_DMA_ALLOCNOW, /* flags */
+ NULL, /* lockfunc */
+ NULL, /* lockarg */
+ &dd->dd_dmat);
+ if (error != 0) {
+ if_printf(ifp, "cannot allocate %s DMA tag\n", dd->dd_name);
+ return error;
+ }
+
+ /* allocate descriptors */
+ error = bus_dmamap_create(dd->dd_dmat, BUS_DMA_NOWAIT, &dd->dd_dmamap);
+ if (error != 0) {
+ if_printf(ifp, "unable to create dmamap for %s descriptors, "
+ "error %u\n", dd->dd_name, error);
+ goto fail0;
+ }
+
+ error = bus_dmamem_alloc(dd->dd_dmat, (void**) &dd->dd_desc,
+ BUS_DMA_NOWAIT | BUS_DMA_COHERENT,
+ &dd->dd_dmamap);
+ if (error != 0) {
+ if_printf(ifp, "unable to alloc memory for %u %s descriptors, "
+ "error %u\n", nbuf * ndesc, dd->dd_name, error);
+ goto fail1;
+ }
+
+ error = bus_dmamap_load(dd->dd_dmat, dd->dd_dmamap,
+ dd->dd_desc, dd->dd_desc_len,
+ mwl_load_cb, &dd->dd_desc_paddr,
+ BUS_DMA_NOWAIT);
+ if (error != 0) {
+ if_printf(ifp, "unable to map %s descriptors, error %u\n",
+ dd->dd_name, error);
+ goto fail2;
+ }
+
+ ds = dd->dd_desc;
+ memset(ds, 0, dd->dd_desc_len);
+ DPRINTF(sc, MWL_DEBUG_RESET, "%s: %s DMA map: %p (%lu) -> %p (%lu)\n",
+ __func__, dd->dd_name, ds, (u_long) dd->dd_desc_len,
+ (caddr_t) dd->dd_desc_paddr, /*XXX*/ (u_long) dd->dd_desc_len);
+
+ return 0;
+fail2:
+ bus_dmamem_free(dd->dd_dmat, dd->dd_desc, dd->dd_dmamap);
+fail1:
+ bus_dmamap_destroy(dd->dd_dmat, dd->dd_dmamap);
+fail0:
+ bus_dma_tag_destroy(dd->dd_dmat);
+ memset(dd, 0, sizeof(*dd));
+ return error;
+#undef DS2PHYS
+}
+
+static void
+mwl_desc_cleanup(struct mwl_softc *sc, struct mwl_descdma *dd)
+{
+ bus_dmamap_unload(dd->dd_dmat, dd->dd_dmamap);
+ bus_dmamem_free(dd->dd_dmat, dd->dd_desc, dd->dd_dmamap);
+ bus_dmamap_destroy(dd->dd_dmat, dd->dd_dmamap);
+ bus_dma_tag_destroy(dd->dd_dmat);
+
+ memset(dd, 0, sizeof(*dd));
+}
+
+/*
+ * Construct a tx q's free list. The order of entries on
+ * the list must reflect the physical layout of tx descriptors
+ * because the firmware pre-fetches descriptors.
+ *
+ * XXX might be better to use indices into the buffer array.
+ */
+static void
+mwl_txq_reset(struct mwl_softc *sc, struct mwl_txq *txq)
+{
+ struct mwl_txbuf *bf;
+ int i;
+
+ bf = txq->dma.dd_bufptr;
+ STAILQ_INIT(&txq->free);
+ for (i = 0; i < mwl_txbuf; i++, bf++)
+ STAILQ_INSERT_TAIL(&txq->free, bf, bf_list);
+ txq->nfree = i;
+}
+
+#define DS2PHYS(_dd, _ds) \
+ ((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc))
+
+static int
+mwl_txdma_setup(struct mwl_softc *sc, struct mwl_txq *txq)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ int error, bsize, i;
+ struct mwl_txbuf *bf;
+ struct mwl_txdesc *ds;
+
+ error = mwl_desc_setup(sc, "tx", &txq->dma,
+ mwl_txbuf, sizeof(struct mwl_txbuf),
+ MWL_TXDESC, sizeof(struct mwl_txdesc));
+ if (error != 0)
+ return error;
+
+ /* allocate and setup tx buffers */
+ bsize = mwl_txbuf * sizeof(struct mwl_txbuf);
+ bf = malloc(bsize, M_MWLDEV, M_NOWAIT | M_ZERO);
+ if (bf == NULL) {
+ if_printf(ifp, "malloc of %u tx buffers failed\n",
+ mwl_txbuf);
+ return ENOMEM;
+ }
+ txq->dma.dd_bufptr = bf;
+
+ ds = txq->dma.dd_desc;
+ for (i = 0; i < mwl_txbuf; i++, bf++, ds += MWL_TXDESC) {
+ bf->bf_desc = ds;
+ bf->bf_daddr = DS2PHYS(&txq->dma, ds);
+ error = bus_dmamap_create(sc->sc_dmat, BUS_DMA_NOWAIT,
+ &bf->bf_dmamap);
+ if (error != 0) {
+ if_printf(ifp, "unable to create dmamap for tx "
+ "buffer %u, error %u\n", i, error);
+ return error;
+ }
+ }
+ mwl_txq_reset(sc, txq);
+ return 0;
+}
+
+static void
+mwl_txdma_cleanup(struct mwl_softc *sc, struct mwl_txq *txq)
+{
+ struct mwl_txbuf *bf;
+ int i;
+
+ bf = txq->dma.dd_bufptr;
+ for (i = 0; i < mwl_txbuf; i++, bf++) {
+ KASSERT(bf->bf_m == NULL, ("mbuf on free list"));
+ KASSERT(bf->bf_node == NULL, ("node on free list"));
+ if (bf->bf_dmamap != NULL)
+ bus_dmamap_destroy(sc->sc_dmat, bf->bf_dmamap);
+ }
+ STAILQ_INIT(&txq->free);
+ txq->nfree = 0;
+ if (txq->dma.dd_bufptr != NULL) {
+ free(txq->dma.dd_bufptr, M_MWLDEV);
+ txq->dma.dd_bufptr = NULL;
+ }
+ if (txq->dma.dd_desc_len != 0)
+ mwl_desc_cleanup(sc, &txq->dma);
+}
+
+static int
+mwl_rxdma_setup(struct mwl_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ int error, jumbosize, bsize, i;
+ struct mwl_rxbuf *bf;
+ struct mwl_jumbo *rbuf;
+ struct mwl_rxdesc *ds;
+ caddr_t data;
+
+ error = mwl_desc_setup(sc, "rx", &sc->sc_rxdma,
+ mwl_rxdesc, sizeof(struct mwl_rxbuf),
+ 1, sizeof(struct mwl_rxdesc));
+ if (error != 0)
+ return error;
+
+ /*
+ * Receive is done to a private pool of jumbo buffers.
+ * This allows us to attach to mbuf's and avoid re-mapping
+ * memory on each rx we post. We allocate a large chunk
+ * of memory and manage it in the driver. The mbuf free
+ * callback method is used to reclaim frames after sending
+ * them up the stack. By default we allocate 2x the number of
+ * rx descriptors configured so we have some slop to hold
+ * us while frames are processed.
+ */
+ if (mwl_rxbuf < 2*mwl_rxdesc) {
+ if_printf(ifp,
+ "too few rx dma buffers (%d); increasing to %d\n",
+ mwl_rxbuf, 2*mwl_rxdesc);
+ mwl_rxbuf = 2*mwl_rxdesc;
+ }
+ jumbosize = roundup(MWL_AGGR_SIZE, PAGE_SIZE);
+ sc->sc_rxmemsize = mwl_rxbuf*jumbosize;
+
+ error = bus_dma_tag_create(sc->sc_dmat, /* parent */
+ PAGE_SIZE, 0, /* alignment, bounds */
+ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ sc->sc_rxmemsize, /* maxsize */
+ 1, /* nsegments */
+ sc->sc_rxmemsize, /* maxsegsize */
+ BUS_DMA_ALLOCNOW, /* flags */
+ NULL, /* lockfunc */
+ NULL, /* lockarg */
+ &sc->sc_rxdmat);
+ error = bus_dmamap_create(sc->sc_rxdmat, BUS_DMA_NOWAIT, &sc->sc_rxmap);
+ if (error != 0) {
+ if_printf(ifp, "could not create rx DMA map\n");
+ return error;
+ }
+
+ error = bus_dmamem_alloc(sc->sc_rxdmat, (void**) &sc->sc_rxmem,
+ BUS_DMA_NOWAIT | BUS_DMA_COHERENT,
+ &sc->sc_rxmap);
+ if (error != 0) {
+ if_printf(ifp, "could not alloc %ju bytes of rx DMA memory\n",
+ (uintmax_t) sc->sc_rxmemsize);
+ return error;
+ }
+
+ error = bus_dmamap_load(sc->sc_rxdmat, sc->sc_rxmap,
+ sc->sc_rxmem, sc->sc_rxmemsize,
+ mwl_load_cb, &sc->sc_rxmem_paddr,
+ BUS_DMA_NOWAIT);
+ if (error != 0) {
+ if_printf(ifp, "could not load rx DMA map\n");
+ return error;
+ }
+
+ /*
+ * Allocate rx buffers and set them up.
+ */
+ bsize = mwl_rxdesc * sizeof(struct mwl_rxbuf);
+ bf = malloc(bsize, M_MWLDEV, M_NOWAIT | M_ZERO);
+ if (bf == NULL) {
+ if_printf(ifp, "malloc of %u rx buffers failed\n", bsize);
+ return error;
+ }
+ sc->sc_rxdma.dd_bufptr = bf;
+
+ STAILQ_INIT(&sc->sc_rxbuf);
+ ds = sc->sc_rxdma.dd_desc;
+ for (i = 0; i < mwl_rxdesc; i++, bf++, ds++) {
+ bf->bf_desc = ds;
+ bf->bf_daddr = DS2PHYS(&sc->sc_rxdma, ds);
+ /* pre-assign dma buffer */
+ bf->bf_data = ((uint8_t *)sc->sc_rxmem) + (i*jumbosize);
+ /* NB: tail is intentional to preserve descriptor order */
+ STAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list);
+ }
+
+ /*
+ * Place remainder of dma memory buffers on the free list.
+ */
+ SLIST_INIT(&sc->sc_rxfree);
+ for (; i < mwl_rxbuf; i++) {
+ data = ((uint8_t *)sc->sc_rxmem) + (i*jumbosize);
+ rbuf = MWL_JUMBO_DATA2BUF(data);
+ SLIST_INSERT_HEAD(&sc->sc_rxfree, rbuf, next);
+ sc->sc_nrxfree++;
+ }
+ MWL_RXFREE_INIT(sc);
+ return 0;
+}
+#undef DS2PHYS
+
+static void
+mwl_rxdma_cleanup(struct mwl_softc *sc)
+{
+ if (sc->sc_rxmap != NULL)
+ bus_dmamap_unload(sc->sc_rxdmat, sc->sc_rxmap);
+ if (sc->sc_rxmem != NULL) {
+ bus_dmamem_free(sc->sc_rxdmat, sc->sc_rxmem, sc->sc_rxmap);
+ sc->sc_rxmem = NULL;
+ }
+ if (sc->sc_rxmap != NULL) {
+ bus_dmamap_destroy(sc->sc_rxdmat, sc->sc_rxmap);
+ sc->sc_rxmap = NULL;
+ }
+ if (sc->sc_rxdma.dd_bufptr != NULL) {
+ free(sc->sc_rxdma.dd_bufptr, M_MWLDEV);
+ sc->sc_rxdma.dd_bufptr = NULL;
+ }
+ if (sc->sc_rxdma.dd_desc_len != 0)
+ mwl_desc_cleanup(sc, &sc->sc_rxdma);
+ MWL_RXFREE_DESTROY(sc);
+}
+
+static int
+mwl_dma_setup(struct mwl_softc *sc)
+{
+ int error, i;
+
+ error = mwl_rxdma_setup(sc);
+ if (error != 0)
+ return error;
+
+ for (i = 0; i < MWL_NUM_TX_QUEUES; i++) {
+ error = mwl_txdma_setup(sc, &sc->sc_txq[i]);
+ if (error != 0) {
+ mwl_dma_cleanup(sc);
+ return error;
+ }
+ }
+ return 0;
+}
+
+static void
+mwl_dma_cleanup(struct mwl_softc *sc)
+{
+ int i;
+
+ for (i = 0; i < MWL_NUM_TX_QUEUES; i++)
+ mwl_txdma_cleanup(sc, &sc->sc_txq[i]);
+ mwl_rxdma_cleanup(sc);
+}
+
+static struct ieee80211_node *
+mwl_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct mwl_softc *sc = ic->ic_ifp->if_softc;
+ const size_t space = sizeof(struct mwl_node);
+ struct mwl_node *mn;
+
+ mn = malloc(space, M_80211_NODE, M_NOWAIT|M_ZERO);
+ if (mn == NULL) {
+ /* XXX stat+msg */
+ return NULL;
+ }
+ DPRINTF(sc, MWL_DEBUG_NODE, "%s: mn %p\n", __func__, mn);
+ return &mn->mn_node;
+}
+
+static void
+mwl_node_cleanup(struct ieee80211_node *ni)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct mwl_softc *sc = ic->ic_ifp->if_softc;
+ struct mwl_node *mn = MWL_NODE(ni);
+
+ DPRINTF(sc, MWL_DEBUG_NODE, "%s: ni %p ic %p staid %d\n",
+ __func__, ni, ni->ni_ic, mn->mn_staid);
+
+ if (mn->mn_staid != 0) {
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ if (mn->mn_hvap != NULL) {
+ if (vap->iv_opmode == IEEE80211_M_STA)
+ mwl_hal_delstation(mn->mn_hvap, vap->iv_myaddr);
+ else
+ mwl_hal_delstation(mn->mn_hvap, ni->ni_macaddr);
+ }
+ /*
+ * NB: legacy WDS peer sta db entry is installed using
+ * the associate ap's hvap; use it again to delete it.
+ * XXX can vap be NULL?
+ */
+ else if (vap->iv_opmode == IEEE80211_M_WDS &&
+ MWL_VAP(vap)->mv_ap_hvap != NULL)
+ mwl_hal_delstation(MWL_VAP(vap)->mv_ap_hvap,
+ ni->ni_macaddr);
+ delstaid(sc, mn->mn_staid);
+ mn->mn_staid = 0;
+ }
+ sc->sc_node_cleanup(ni);
+}
+
+/*
+ * Reclaim rx dma buffers from packets sitting on the ampdu
+ * reorder queue for a station. We replace buffers with a
+ * system cluster (if available).
+ */
+static void
+mwl_ampdu_rxdma_reclaim(struct ieee80211_rx_ampdu *rap)
+{
+#if 0
+ int i, n, off;
+ struct mbuf *m;
+ void *cl;
+
+ n = rap->rxa_qframes;
+ for (i = 0; i < rap->rxa_wnd && n > 0; i++) {
+ m = rap->rxa_m[i];
+ if (m == NULL)
+ continue;
+ n--;
+ /* our dma buffers have a well-known free routine */
+ if ((m->m_flags & M_EXT) == 0 ||
+ m->m_ext.ext_free != mwl_ext_free)
+ continue;
+ /*
+ * Try to allocate a cluster and move the data.
+ */
+ off = m->m_data - m->m_ext.ext_buf;
+ if (off + m->m_pkthdr.len > MCLBYTES) {
+ /* XXX no AMSDU for now */
+ continue;
+ }
+ cl = pool_cache_get_paddr(&mclpool_cache, 0,
+ &m->m_ext.ext_paddr);
+ if (cl != NULL) {
+ /*
+ * Copy the existing data to the cluster, remove
+ * the rx dma buffer, and attach the cluster in
+ * its place. Note we preserve the offset to the
+ * data so frames being bridged can still prepend
+ * their headers without adding another mbuf.
+ */
+ memcpy((caddr_t) cl + off, m->m_data, m->m_pkthdr.len);
+ MEXTREMOVE(m);
+ MEXTADD(m, cl, MCLBYTES, 0, NULL, &mclpool_cache);
+ /* setup mbuf like _MCLGET does */
+ m->m_flags |= M_CLUSTER | M_EXT_RW;
+ _MOWNERREF(m, M_EXT | M_CLUSTER);
+ /* NB: m_data is clobbered by MEXTADDR, adjust */
+ m->m_data += off;
+ }
+ }
+#endif
+}
+
+/*
+ * Callback to reclaim resources. We first let the
+ * net80211 layer do it's thing, then if we are still
+ * blocked by a lack of rx dma buffers we walk the ampdu
+ * reorder q's to reclaim buffers by copying to a system
+ * cluster.
+ */
+static void
+mwl_node_drain(struct ieee80211_node *ni)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct mwl_softc *sc = ic->ic_ifp->if_softc;
+ struct mwl_node *mn = MWL_NODE(ni);
+
+ DPRINTF(sc, MWL_DEBUG_NODE, "%s: ni %p vap %p staid %d\n",
+ __func__, ni, ni->ni_vap, mn->mn_staid);
+
+ /* NB: call up first to age out ampdu q's */
+ sc->sc_node_drain(ni);
+
+ /* XXX better to not check low water mark? */
+ if (sc->sc_rxblocked && mn->mn_staid != 0 &&
+ (ni->ni_flags & IEEE80211_NODE_HT)) {
+ uint8_t tid;
+ /*
+ * Walk the reorder q and reclaim rx dma buffers by copying
+ * the packet contents into clusters.
+ */
+ for (tid = 0; tid < WME_NUM_TID; tid++) {
+ struct ieee80211_rx_ampdu *rap;
+
+ rap = &ni->ni_rx_ampdu[tid];
+ if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0)
+ continue;
+ if (rap->rxa_qframes)
+ mwl_ampdu_rxdma_reclaim(rap);
+ }
+ }
+}
+
+static void
+mwl_node_getsignal(const struct ieee80211_node *ni, int8_t *rssi, int8_t *noise)
+{
+ *rssi = ni->ni_ic->ic_node_getrssi(ni);
+#ifdef MWL_ANT_INFO_SUPPORT
+#if 0
+ /* XXX need to smooth data */
+ *noise = -MWL_NODE_CONST(ni)->mn_ai.nf;
+#else
+ *noise = -95; /* XXX */
+#endif
+#else
+ *noise = -95; /* XXX */
+#endif
+}
+
+/*
+ * Convert Hardware per-antenna rssi info to common format:
+ * Let a1, a2, a3 represent the amplitudes per chain
+ * Let amax represent max[a1, a2, a3]
+ * Rssi1_dBm = RSSI_dBm + 20*log10(a1/amax)
+ * Rssi1_dBm = RSSI_dBm + 20*log10(a1) - 20*log10(amax)
+ * We store a table that is 4*20*log10(idx) - the extra 4 is to store or
+ * maintain some extra precision.
+ *
+ * Values are stored in .5 db format capped at 127.
+ */
+static void
+mwl_node_getmimoinfo(const struct ieee80211_node *ni,
+ struct ieee80211_mimo_info *mi)
+{
+#define CVT(_dst, _src) do { \
+ (_dst) = rssi + ((logdbtbl[_src] - logdbtbl[rssi_max]) >> 2); \
+ (_dst) = (_dst) > 64 ? 127 : ((_dst) << 1); \
+} while (0)
+ static const int8_t logdbtbl[32] = {
+ 0, 0, 24, 38, 48, 56, 62, 68,
+ 72, 76, 80, 83, 86, 89, 92, 94,
+ 96, 98, 100, 102, 104, 106, 107, 109,
+ 110, 112, 113, 115, 116, 117, 118, 119
+ };
+ const struct mwl_node *mn = MWL_NODE_CONST(ni);
+ uint8_t rssi = mn->mn_ai.rsvd1/2; /* XXX */
+ uint32_t rssi_max;
+
+ rssi_max = mn->mn_ai.rssi_a;
+ if (mn->mn_ai.rssi_b > rssi_max)
+ rssi_max = mn->mn_ai.rssi_b;
+ if (mn->mn_ai.rssi_c > rssi_max)
+ rssi_max = mn->mn_ai.rssi_c;
+
+ CVT(mi->rssi[0], mn->mn_ai.rssi_a);
+ CVT(mi->rssi[1], mn->mn_ai.rssi_b);
+ CVT(mi->rssi[2], mn->mn_ai.rssi_c);
+
+ mi->noise[0] = mn->mn_ai.nf_a;
+ mi->noise[1] = mn->mn_ai.nf_b;
+ mi->noise[2] = mn->mn_ai.nf_c;
+#undef CVT
+}
+
+static __inline void *
+mwl_getrxdma(struct mwl_softc *sc)
+{
+ struct mwl_jumbo *buf;
+ void *data;
+
+ /*
+ * Allocate from jumbo pool.
+ */
+ MWL_RXFREE_LOCK(sc);
+ buf = SLIST_FIRST(&sc->sc_rxfree);
+ if (buf == NULL) {
+ DPRINTF(sc, MWL_DEBUG_ANY,
+ "%s: out of rx dma buffers\n", __func__);
+ sc->sc_stats.mst_rx_nodmabuf++;
+ data = NULL;
+ } else {
+ SLIST_REMOVE_HEAD(&sc->sc_rxfree, next);
+ sc->sc_nrxfree--;
+ data = MWL_JUMBO_BUF2DATA(buf);
+ }
+ MWL_RXFREE_UNLOCK(sc);
+ return data;
+}
+
+static __inline void
+mwl_putrxdma(struct mwl_softc *sc, void *data)
+{
+ struct mwl_jumbo *buf;
+
+ /* XXX bounds check data */
+ MWL_RXFREE_LOCK(sc);
+ buf = MWL_JUMBO_DATA2BUF(data);
+ SLIST_INSERT_HEAD(&sc->sc_rxfree, buf, next);
+ sc->sc_nrxfree++;
+ MWL_RXFREE_UNLOCK(sc);
+}
+
+static int
+mwl_rxbuf_init(struct mwl_softc *sc, struct mwl_rxbuf *bf)
+{
+ struct mwl_rxdesc *ds;
+
+ ds = bf->bf_desc;
+ if (bf->bf_data == NULL) {
+ bf->bf_data = mwl_getrxdma(sc);
+ if (bf->bf_data == NULL) {
+ /* mark descriptor to be skipped */
+ ds->RxControl = EAGLE_RXD_CTRL_OS_OWN;
+ /* NB: don't need PREREAD */
+ MWL_RXDESC_SYNC(sc, ds, BUS_DMASYNC_PREWRITE);
+ sc->sc_stats.mst_rxbuf_failed++;
+ return ENOMEM;
+ }
+ }
+ /*
+ * NB: DMA buffer contents is known to be unmodified
+ * so there's no need to flush the data cache.
+ */
+
+ /*
+ * Setup descriptor.
+ */
+ ds->QosCtrl = 0;
+ ds->RSSI = 0;
+ ds->Status = EAGLE_RXD_STATUS_IDLE;
+ ds->Channel = 0;
+ ds->PktLen = htole16(MWL_AGGR_SIZE);
+ ds->SQ2 = 0;
+ ds->pPhysBuffData = htole32(MWL_JUMBO_DMA_ADDR(sc, bf->bf_data));
+ /* NB: don't touch pPhysNext, set once */
+ ds->RxControl = EAGLE_RXD_CTRL_DRIVER_OWN;
+ MWL_RXDESC_SYNC(sc, ds, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+
+ return 0;
+}
+
+static void
+mwl_ext_free(void *data, void *arg)
+{
+ struct mwl_softc *sc = arg;
+
+ /* XXX bounds check data */
+ mwl_putrxdma(sc, data);
+ /*
+ * If we were previously blocked by a lack of rx dma buffers
+ * check if we now have enough to restart rx interrupt handling.
+ * NB: we know we are called at splvm which is above splnet.
+ */
+ if (sc->sc_rxblocked && sc->sc_nrxfree > mwl_rxdmalow) {
+ sc->sc_rxblocked = 0;
+ mwl_hal_intrset(sc->sc_mh, sc->sc_imask);
+ }
+}
+
+struct mwl_frame_bar {
+ u_int8_t i_fc[2];
+ u_int8_t i_dur[2];
+ u_int8_t i_ra[IEEE80211_ADDR_LEN];
+ u_int8_t i_ta[IEEE80211_ADDR_LEN];
+ /* ctl, seq, FCS */
+} __packed;
+
+/*
+ * Like ieee80211_anyhdrsize, but handles BAR frames
+ * specially so the logic below to piece the 802.11
+ * header together works.
+ */
+static __inline int
+mwl_anyhdrsize(const void *data)
+{
+ const struct ieee80211_frame *wh = data;
+
+ if ((wh->i_fc[0]&IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL) {
+ switch (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) {
+ case IEEE80211_FC0_SUBTYPE_CTS:
+ case IEEE80211_FC0_SUBTYPE_ACK:
+ return sizeof(struct ieee80211_frame_ack);
+ case IEEE80211_FC0_SUBTYPE_BAR:
+ return sizeof(struct mwl_frame_bar);
+ }
+ return sizeof(struct ieee80211_frame_min);
+ } else
+ return ieee80211_hdrsize(data);
+}
+
+static void
+mwl_handlemicerror(struct ieee80211com *ic, const uint8_t *data)
+{
+ const struct ieee80211_frame *wh;
+ struct ieee80211_node *ni;
+
+ wh = (const struct ieee80211_frame *)(data + sizeof(uint16_t));
+ ni = ieee80211_find_rxnode(ic, (const struct ieee80211_frame_min *) wh);
+ if (ni != NULL) {
+ ieee80211_notify_michael_failure(ni->ni_vap, wh, 0);
+ ieee80211_free_node(ni);
+ }
+}
+
+/*
+ * Convert hardware signal strength to rssi. The value
+ * provided by the device has the noise floor added in;
+ * we need to compensate for this but we don't have that
+ * so we use a fixed value.
+ *
+ * The offset of 8 is good for both 2.4 and 5GHz. The LNA
+ * offset is already set as part of the initial gain. This
+ * will give at least +/- 3dB for 2.4GHz and +/- 5dB for 5GHz.
+ */
+static __inline int
+cvtrssi(uint8_t ssi)
+{
+ int rssi = (int) ssi + 8;
+ /* XXX hack guess until we have a real noise floor */
+ rssi = 2*(87 - rssi); /* NB: .5 dBm units */
+ return (rssi < 0 ? 0 : rssi > 127 ? 127 : rssi);
+}
+
+static void
+mwl_rx_proc(void *arg, int npending)
+{
+#define IEEE80211_DIR_DSTODS(wh) \
+ ((((const struct ieee80211_frame *)wh)->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS)
+ struct mwl_softc *sc = arg;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct mwl_rxbuf *bf;
+ struct mwl_rxdesc *ds;
+ struct mbuf *m;
+ struct ieee80211_qosframe *wh;
+ struct ieee80211_qosframe_addr4 *wh4;
+ struct ieee80211_node *ni;
+ struct mwl_node *mn;
+ int off, len, hdrlen, pktlen, rssi, ntodo;
+ uint8_t *data, status;
+ void *newdata;
+ int16_t nf;
+
+ DPRINTF(sc, MWL_DEBUG_RX_PROC, "%s: pending %u rdptr 0x%x wrptr 0x%x\n",
+ __func__, npending, RD4(sc, sc->sc_hwspecs.rxDescRead),
+ RD4(sc, sc->sc_hwspecs.rxDescWrite));
+ nf = -96; /* XXX */
+ bf = sc->sc_rxnext;
+ for (ntodo = mwl_rxquota; ntodo > 0; ntodo--) {
+ if (bf == NULL)
+ bf = STAILQ_FIRST(&sc->sc_rxbuf);
+ ds = bf->bf_desc;
+ data = bf->bf_data;
+ if (data == NULL) {
+ /*
+ * If data allocation failed previously there
+ * will be no buffer; try again to re-populate it.
+ * Note the firmware will not advance to the next
+ * descriptor with a dma buffer so we must mimic
+ * this or we'll get out of sync.
+ */
+ DPRINTF(sc, MWL_DEBUG_ANY,
+ "%s: rx buf w/o dma memory\n", __func__);
+ (void) mwl_rxbuf_init(sc, bf);
+ sc->sc_stats.mst_rx_dmabufmissing++;
+ break;
+ }
+ MWL_RXDESC_SYNC(sc, ds,
+ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+ if (ds->RxControl != EAGLE_RXD_CTRL_DMA_OWN)
+ break;
+#ifdef MWL_DEBUG
+ if (sc->sc_debug & MWL_DEBUG_RECV_DESC)
+ mwl_printrxbuf(bf, 0);
+#endif
+ status = ds->Status;
+ if (status & EAGLE_RXD_STATUS_DECRYPT_ERR_MASK) {
+ ifp->if_ierrors++;
+ sc->sc_stats.mst_rx_crypto++;
+ /*
+ * NB: Check EAGLE_RXD_STATUS_GENERAL_DECRYPT_ERR
+ * for backwards compatibility.
+ */
+ if (status != EAGLE_RXD_STATUS_GENERAL_DECRYPT_ERR &&
+ (status & EAGLE_RXD_STATUS_TKIP_MIC_DECRYPT_ERR)) {
+ /*
+ * MIC error, notify upper layers.
+ */
+ bus_dmamap_sync(sc->sc_rxdmat, sc->sc_rxmap,
+ BUS_DMASYNC_POSTREAD);
+ mwl_handlemicerror(ic, data);
+ sc->sc_stats.mst_rx_tkipmic++;
+ }
+ /* XXX too painful to tap packets */
+ goto rx_next;
+ }
+ /*
+ * Sync the data buffer.
+ */
+ len = le16toh(ds->PktLen);
+ bus_dmamap_sync(sc->sc_rxdmat, sc->sc_rxmap, BUS_DMASYNC_POSTREAD);
+ /*
+ * The 802.11 header is provided all or in part at the front;
+ * use it to calculate the true size of the header that we'll
+ * construct below. We use this to figure out where to copy
+ * payload prior to constructing the header.
+ */
+ hdrlen = mwl_anyhdrsize(data + sizeof(uint16_t));
+ off = sizeof(uint16_t) + sizeof(struct ieee80211_frame_addr4);
+
+ /* calculate rssi early so we can re-use for each aggregate */
+ rssi = cvtrssi(ds->RSSI);
+
+ pktlen = hdrlen + (len - off);
+ /*
+ * NB: we know our frame is at least as large as
+ * IEEE80211_MIN_LEN because there is a 4-address
+ * frame at the front. Hence there's no need to
+ * vet the packet length. If the frame in fact
+ * is too small it should be discarded at the
+ * net80211 layer.
+ */
+
+ /*
+ * Attach dma buffer to an mbuf. We tried
+ * doing this based on the packet size (i.e.
+ * copying small packets) but it turns out to
+ * be a net loss. The tradeoff might be system
+ * dependent (cache architecture is important).
+ */
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ DPRINTF(sc, MWL_DEBUG_ANY,
+ "%s: no rx mbuf\n", __func__);
+ sc->sc_stats.mst_rx_nombuf++;
+ goto rx_next;
+ }
+ /*
+ * Acquire the replacement dma buffer before
+ * processing the frame. If we're out of dma
+ * buffers we disable rx interrupts and wait
+ * for the free pool to reach mlw_rxdmalow buffers
+ * before starting to do work again. If the firmware
+ * runs out of descriptors then it will toss frames
+ * which is better than our doing it as that can
+ * starve our processing. It is also important that
+ * we always process rx'd frames in case they are
+ * A-MPDU as otherwise the host's view of the BA
+ * window may get out of sync with the firmware.
+ */
+ newdata = mwl_getrxdma(sc);
+ if (newdata == NULL) {
+ /* NB: stat+msg in mwl_getrxdma */
+ m_free(m);
+ /* disable RX interrupt and mark state */
+ mwl_hal_intrset(sc->sc_mh,
+ sc->sc_imask &~ MACREG_A2HRIC_BIT_RX_RDY);
+ sc->sc_rxblocked = 1;
+ ieee80211_drain(ic);
+ /* XXX check rxblocked and immediately start again? */
+ goto rx_stop;
+ }
+ bf->bf_data = newdata;
+ /*
+ * Attach the dma buffer to the mbuf;
+ * mwl_rxbuf_init will re-setup the rx
+ * descriptor using the replacement dma
+ * buffer we just installed above.
+ */
+ MEXTADD(m, data, MWL_AGGR_SIZE, mwl_ext_free,
+ data, sc, 0, EXT_NET_DRV);
+ m->m_data += off - hdrlen;
+ m->m_pkthdr.len = m->m_len = pktlen;
+ m->m_pkthdr.rcvif = ifp;
+ /* NB: dma buffer assumed read-only */
+
+ /*
+ * Piece 802.11 header together.
+ */
+ wh = mtod(m, struct ieee80211_qosframe *);
+ /* NB: don't need to do this sometimes but ... */
+ /* XXX special case so we can memcpy after m_devget? */
+ ovbcopy(data + sizeof(uint16_t), wh, hdrlen);
+ if (IEEE80211_QOS_HAS_SEQ(wh)) {
+ if (IEEE80211_DIR_DSTODS(wh)) {
+ wh4 = mtod(m,
+ struct ieee80211_qosframe_addr4*);
+ *(uint16_t *)wh4->i_qos = ds->QosCtrl;
+ } else {
+ *(uint16_t *)wh->i_qos = ds->QosCtrl;
+ }
+ }
+ /*
+ * The f/w strips WEP header but doesn't clear
+ * the WEP bit; mark the packet with M_WEP so
+ * net80211 will treat the data as decrypted.
+ * While here also clear the PWR_MGT bit since
+ * power save is handled by the firmware and
+ * passing this up will potentially cause the
+ * upper layer to put a station in power save
+ * (except when configured with MWL_HOST_PS_SUPPORT).
+ */
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP)
+ m->m_flags |= M_WEP;
+#ifdef MWL_HOST_PS_SUPPORT
+ wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+#else
+ wh->i_fc[1] &= ~(IEEE80211_FC1_WEP | IEEE80211_FC1_PWR_MGT);
+#endif
+
+ if (ieee80211_radiotap_active(ic)) {
+ struct mwl_rx_radiotap_header *tap = &sc->sc_rx_th;
+
+ tap->wr_flags = 0;
+ tap->wr_rate = ds->Rate;
+ tap->wr_antsignal = rssi + nf;
+ tap->wr_antnoise = nf;
+ }
+ if (IFF_DUMPPKTS_RECV(sc, wh)) {
+ ieee80211_dump_pkt(ic, mtod(m, caddr_t),
+ len, ds->Rate, rssi);
+ }
+ ifp->if_ipackets++;
+
+ /* dispatch */
+ ni = ieee80211_find_rxnode(ic,
+ (const struct ieee80211_frame_min *) wh);
+ if (ni != NULL) {
+ mn = MWL_NODE(ni);
+#ifdef MWL_ANT_INFO_SUPPORT
+ mn->mn_ai.rssi_a = ds->ai.rssi_a;
+ mn->mn_ai.rssi_b = ds->ai.rssi_b;
+ mn->mn_ai.rssi_c = ds->ai.rssi_c;
+ mn->mn_ai.rsvd1 = rssi;
+#endif
+ /* tag AMPDU aggregates for reorder processing */
+#if 0
+ if ((ds->Rate & 0x80) && (ds->HtSig2 & 0x8))
+#else
+ if (ni->ni_flags & IEEE80211_NODE_HT)
+#endif
+ m->m_flags |= M_AMPDU;
+ (void) ieee80211_input(ni, m, rssi, nf);
+ ieee80211_free_node(ni);
+ } else
+ (void) ieee80211_input_all(ic, m, rssi, nf);
+rx_next:
+ /* NB: ignore ENOMEM so we process more descriptors */
+ (void) mwl_rxbuf_init(sc, bf);
+ bf = STAILQ_NEXT(bf, bf_list);
+ }
+rx_stop:
+ sc->sc_rxnext = bf;
+
+ if ((ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0 &&
+ !IFQ_IS_EMPTY(&ifp->if_snd)) {
+ /* NB: kick fw; the tx thread may have been preempted */
+ mwl_hal_txstart(sc->sc_mh, 0);
+ mwl_start(ifp);
+ }
+#undef IEEE80211_DIR_DSTODS
+}
+
+static void
+mwl_txq_init(struct mwl_softc *sc, struct mwl_txq *txq, int qnum)
+{
+ struct mwl_txbuf *bf, *bn;
+ struct mwl_txdesc *ds;
+
+ MWL_TXQ_LOCK_INIT(sc, txq);
+ txq->qnum = qnum;
+ txq->txpri = 0; /* XXX */
+#if 0
+ /* NB: q setup by mwl_txdma_setup XXX */
+ STAILQ_INIT(&txq->free);
+#endif
+ STAILQ_FOREACH(bf, &txq->free, bf_list) {
+ bf->bf_txq = txq;
+
+ ds = bf->bf_desc;
+ bn = STAILQ_NEXT(bf, bf_list);
+ if (bn == NULL)
+ bn = STAILQ_FIRST(&txq->free);
+ ds->pPhysNext = htole32(bn->bf_daddr);
+ }
+ STAILQ_INIT(&txq->active);
+}
+
+/*
+ * Setup a hardware data transmit queue for the specified
+ * access control. We record the mapping from ac's
+ * to h/w queues for use by mwl_tx_start.
+ */
+static int
+mwl_tx_setup(struct mwl_softc *sc, int ac, int mvtype)
+{
+#define N(a) (sizeof(a)/sizeof(a[0]))
+ struct mwl_txq *txq;
+
+ if (ac >= N(sc->sc_ac2q)) {
+ device_printf(sc->sc_dev, "AC %u out of range, max %zu!\n",
+ ac, N(sc->sc_ac2q));
+ return 0;
+ }
+ if (mvtype >= MWL_NUM_TX_QUEUES) {
+ device_printf(sc->sc_dev, "mvtype %u out of range, max %u!\n",
+ mvtype, MWL_NUM_TX_QUEUES);
+ return 0;
+ }
+ txq = &sc->sc_txq[mvtype];
+ mwl_txq_init(sc, txq, mvtype);
+ sc->sc_ac2q[ac] = txq;
+ return 1;
+#undef N
+}
+
+/*
+ * Update WME parameters for a transmit queue.
+ */
+static int
+mwl_txq_update(struct mwl_softc *sc, int ac)
+{
+#define MWL_EXPONENT_TO_VALUE(v) ((1<<v)-1)
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct mwl_txq *txq = sc->sc_ac2q[ac];
+ struct wmeParams *wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[ac];
+ struct mwl_hal *mh = sc->sc_mh;
+ int aifs, cwmin, cwmax, txoplim;
+
+ aifs = wmep->wmep_aifsn;
+ /* XXX in sta mode need to pass log values for cwmin/max */
+ cwmin = MWL_EXPONENT_TO_VALUE(wmep->wmep_logcwmin);
+ cwmax = MWL_EXPONENT_TO_VALUE(wmep->wmep_logcwmax);
+ txoplim = wmep->wmep_txopLimit; /* NB: units of 32us */
+
+ if (mwl_hal_setedcaparams(mh, txq->qnum, cwmin, cwmax, aifs, txoplim)) {
+ device_printf(sc->sc_dev, "unable to update hardware queue "
+ "parameters for %s traffic!\n",
+ ieee80211_wme_acnames[ac]);
+ return 0;
+ }
+ return 1;
+#undef MWL_EXPONENT_TO_VALUE
+}
+
+/*
+ * Callback from the 802.11 layer to update WME parameters.
+ */
+static int
+mwl_wme_update(struct ieee80211com *ic)
+{
+ struct mwl_softc *sc = ic->ic_ifp->if_softc;
+
+ return !mwl_txq_update(sc, WME_AC_BE) ||
+ !mwl_txq_update(sc, WME_AC_BK) ||
+ !mwl_txq_update(sc, WME_AC_VI) ||
+ !mwl_txq_update(sc, WME_AC_VO) ? EIO : 0;
+}
+
+/*
+ * Reclaim resources for a setup queue.
+ */
+static void
+mwl_tx_cleanupq(struct mwl_softc *sc, struct mwl_txq *txq)
+{
+ /* XXX hal work? */
+ MWL_TXQ_LOCK_DESTROY(txq);
+}
+
+/*
+ * Reclaim all tx queue resources.
+ */
+static void
+mwl_tx_cleanup(struct mwl_softc *sc)
+{
+ int i;
+
+ for (i = 0; i < MWL_NUM_TX_QUEUES; i++)
+ mwl_tx_cleanupq(sc, &sc->sc_txq[i]);
+}
+
+static int
+mwl_tx_dmasetup(struct mwl_softc *sc, struct mwl_txbuf *bf, struct mbuf *m0)
+{
+ struct mbuf *m;
+ int error;
+
+ /*
+ * Load the DMA map so any coalescing is done. This
+ * also calculates the number of descriptors we need.
+ */
+ error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m0,
+ bf->bf_segs, &bf->bf_nseg,
+ BUS_DMA_NOWAIT);
+ if (error == EFBIG) {
+ /* XXX packet requires too many descriptors */
+ bf->bf_nseg = MWL_TXDESC+1;
+ } else if (error != 0) {
+ sc->sc_stats.mst_tx_busdma++;
+ m_freem(m0);
+ return error;
+ }
+ /*
+ * Discard null packets and check for packets that
+ * require too many TX descriptors. We try to convert
+ * the latter to a cluster.
+ */
+ if (error == EFBIG) { /* too many desc's, linearize */
+ sc->sc_stats.mst_tx_linear++;
+#if MWL_TXDESC > 1
+ m = m_collapse(m0, M_DONTWAIT, MWL_TXDESC);
+#else
+ m = m_defrag(m0, M_DONTWAIT);
+#endif
+ if (m == NULL) {
+ m_freem(m0);
+ sc->sc_stats.mst_tx_nombuf++;
+ return ENOMEM;
+ }
+ m0 = m;
+ error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m0,
+ bf->bf_segs, &bf->bf_nseg,
+ BUS_DMA_NOWAIT);
+ if (error != 0) {
+ sc->sc_stats.mst_tx_busdma++;
+ m_freem(m0);
+ return error;
+ }
+ KASSERT(bf->bf_nseg <= MWL_TXDESC,
+ ("too many segments after defrag; nseg %u", bf->bf_nseg));
+ } else if (bf->bf_nseg == 0) { /* null packet, discard */
+ sc->sc_stats.mst_tx_nodata++;
+ m_freem(m0);
+ return EIO;
+ }
+ DPRINTF(sc, MWL_DEBUG_XMIT, "%s: m %p len %u\n",
+ __func__, m0, m0->m_pkthdr.len);
+ bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE);
+ bf->bf_m = m0;
+
+ return 0;
+}
+
+static __inline int
+mwl_cvtlegacyrate(int rate)
+{
+ switch (rate) {
+ case 2: return 0;
+ case 4: return 1;
+ case 11: return 2;
+ case 22: return 3;
+ case 44: return 4;
+ case 12: return 5;
+ case 18: return 6;
+ case 24: return 7;
+ case 36: return 8;
+ case 48: return 9;
+ case 72: return 10;
+ case 96: return 11;
+ case 108:return 12;
+ }
+ return 0;
+}
+
+/*
+ * Calculate fixed tx rate information per client state;
+ * this value is suitable for writing to the Format field
+ * of a tx descriptor.
+ */
+static uint16_t
+mwl_calcformat(uint8_t rate, const struct ieee80211_node *ni)
+{
+ uint16_t fmt;
+
+ fmt = SM(3, EAGLE_TXD_ANTENNA)
+ | (IEEE80211_IS_CHAN_HT40D(ni->ni_chan) ?
+ EAGLE_TXD_EXTCHAN_LO : EAGLE_TXD_EXTCHAN_HI);
+ if (rate & 0x80) { /* HT MCS */
+ fmt |= EAGLE_TXD_FORMAT_HT
+ /* NB: 0x80 implicitly stripped from ucastrate */
+ | SM(rate, EAGLE_TXD_RATE);
+ /* XXX short/long GI may be wrong; re-check */
+ if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) {
+ fmt |= EAGLE_TXD_CHW_40
+ | (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40 ?
+ EAGLE_TXD_GI_SHORT : EAGLE_TXD_GI_LONG);
+ } else {
+ fmt |= EAGLE_TXD_CHW_20
+ | (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI20 ?
+ EAGLE_TXD_GI_SHORT : EAGLE_TXD_GI_LONG);
+ }
+ } else { /* legacy rate */
+ fmt |= EAGLE_TXD_FORMAT_LEGACY
+ | SM(mwl_cvtlegacyrate(rate), EAGLE_TXD_RATE)
+ | EAGLE_TXD_CHW_20
+ /* XXX iv_flags & IEEE80211_F_SHPREAMBLE? */
+ | (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE ?
+ EAGLE_TXD_PREAMBLE_SHORT : EAGLE_TXD_PREAMBLE_LONG);
+ }
+ return fmt;
+}
+
+static int
+mwl_tx_start(struct mwl_softc *sc, struct ieee80211_node *ni, struct mwl_txbuf *bf,
+ struct mbuf *m0)
+{
+#define IEEE80211_DIR_DSTODS(wh) \
+ ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS)
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = ni->ni_vap;
+ int error, iswep, ismcast;
+ int hdrlen, copyhdrlen, pktlen;
+ struct mwl_txdesc *ds;
+ struct mwl_txq *txq;
+ struct ieee80211_frame *wh;
+ struct mwltxrec *tr;
+ struct mwl_node *mn;
+ uint16_t qos;
+#if MWL_TXDESC > 1
+ int i;
+#endif
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ iswep = wh->i_fc[1] & IEEE80211_FC1_WEP;
+ ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1);
+ hdrlen = ieee80211_anyhdrsize(wh);
+ copyhdrlen = hdrlen;
+ pktlen = m0->m_pkthdr.len;
+ if (IEEE80211_QOS_HAS_SEQ(wh)) {
+ if (IEEE80211_DIR_DSTODS(wh)) {
+ qos = *(uint16_t *)
+ (((struct ieee80211_qosframe_addr4 *) wh)->i_qos);
+ copyhdrlen -= sizeof(qos);
+ } else
+ qos = *(uint16_t *)
+ (((struct ieee80211_qosframe *) wh)->i_qos);
+ } else
+ qos = 0;
+
+ if (iswep) {
+ const struct ieee80211_cipher *cip;
+ struct ieee80211_key *k;
+
+ /*
+ * Construct the 802.11 header+trailer for an encrypted
+ * frame. The only reason this can fail is because of an
+ * unknown or unsupported cipher/key type.
+ *
+ * NB: we do this even though the firmware will ignore
+ * what we've done for WEP and TKIP as we need the
+ * ExtIV filled in for CCMP and this also adjusts
+ * the headers which simplifies our work below.
+ */
+ k = ieee80211_crypto_encap(ni, m0);
+ if (k == NULL) {
+ /*
+ * This can happen when the key is yanked after the
+ * frame was queued. Just discard the frame; the
+ * 802.11 layer counts failures and provides
+ * debugging/diagnostics.
+ */
+ m_freem(m0);
+ return EIO;
+ }
+ /*
+ * Adjust the packet length for the crypto additions
+ * done during encap and any other bits that the f/w
+ * will add later on.
+ */
+ cip = k->wk_cipher;
+ pktlen += cip->ic_header + cip->ic_miclen + cip->ic_trailer;
+
+ /* packet header may have moved, reset our local pointer */
+ wh = mtod(m0, struct ieee80211_frame *);
+ }
+
+ if (ieee80211_radiotap_active_vap(vap)) {
+ sc->sc_tx_th.wt_flags = 0; /* XXX */
+ if (iswep)
+ sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_WEP;
+#if 0
+ sc->sc_tx_th.wt_rate = ds->DataRate;
+#endif
+ sc->sc_tx_th.wt_txpower = ni->ni_txpower;
+ sc->sc_tx_th.wt_antenna = sc->sc_txantenna;
+
+ ieee80211_radiotap_tx(vap, m0);
+ }
+ /*
+ * Copy up/down the 802.11 header; the firmware requires
+ * we present a 2-byte payload length followed by a
+ * 4-address header (w/o QoS), followed (optionally) by
+ * any WEP/ExtIV header (but only filled in for CCMP).
+ * We are assured the mbuf has sufficient headroom to
+ * prepend in-place by the setup of ic_headroom in
+ * mwl_attach.
+ */
+ if (hdrlen < sizeof(struct mwltxrec)) {
+ const int space = sizeof(struct mwltxrec) - hdrlen;
+ if (M_LEADINGSPACE(m0) < space) {
+ /* NB: should never happen */
+ device_printf(sc->sc_dev,
+ "not enough headroom, need %d found %zd, "
+ "m_flags 0x%x m_len %d\n",
+ space, M_LEADINGSPACE(m0), m0->m_flags, m0->m_len);
+ ieee80211_dump_pkt(ic,
+ mtod(m0, const uint8_t *), m0->m_len, 0, -1);
+ m_freem(m0);
+ sc->sc_stats.mst_tx_noheadroom++;
+ return EIO;
+ }
+ M_PREPEND(m0, space, M_NOWAIT);
+ }
+ tr = mtod(m0, struct mwltxrec *);
+ if (wh != (struct ieee80211_frame *) &tr->wh)
+ ovbcopy(wh, &tr->wh, hdrlen);
+ /*
+ * Note: the "firmware length" is actually the length
+ * of the fully formed "802.11 payload". That is, it's
+ * everything except for the 802.11 header. In particular
+ * this includes all crypto material including the MIC!
+ */
+ tr->fwlen = htole16(pktlen - hdrlen);
+
+ /*
+ * Load the DMA map so any coalescing is done. This
+ * also calculates the number of descriptors we need.
+ */
+ error = mwl_tx_dmasetup(sc, bf, m0);
+ if (error != 0) {
+ /* NB: stat collected in mwl_tx_dmasetup */
+ DPRINTF(sc, MWL_DEBUG_XMIT,
+ "%s: unable to setup dma\n", __func__);
+ return error;
+ }
+ bf->bf_node = ni; /* NB: held reference */
+ m0 = bf->bf_m; /* NB: may have changed */
+ tr = mtod(m0, struct mwltxrec *);
+ wh = (struct ieee80211_frame *)&tr->wh;
+
+ /*
+ * Formulate tx descriptor.
+ */
+ ds = bf->bf_desc;
+ txq = bf->bf_txq;
+
+ ds->QosCtrl = qos; /* NB: already little-endian */
+#if MWL_TXDESC == 1
+ /*
+ * NB: multiframes should be zero because the descriptors
+ * are initialized to zero. This should handle the case
+ * where the driver is built with MWL_TXDESC=1 but we are
+ * using firmware with multi-segment support.
+ */
+ ds->PktPtr = htole32(bf->bf_segs[0].ds_addr);
+ ds->PktLen = htole16(bf->bf_segs[0].ds_len);
+#else
+ ds->multiframes = htole32(bf->bf_nseg);
+ ds->PktLen = htole16(m0->m_pkthdr.len);
+ for (i = 0; i < bf->bf_nseg; i++) {
+ ds->PktPtrArray[i] = htole32(bf->bf_segs[i].ds_addr);
+ ds->PktLenArray[i] = htole16(bf->bf_segs[i].ds_len);
+ }
+#endif
+ /* NB: pPhysNext, DataRate, and SapPktInfo setup once, don't touch */
+ ds->Format = 0;
+ ds->pad = 0;
+
+ mn = MWL_NODE(ni);
+ /*
+ * Select transmit rate.
+ */
+ switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
+ case IEEE80211_FC0_TYPE_MGT:
+ sc->sc_stats.mst_tx_mgmt++;
+ /* fall thru... */
+ case IEEE80211_FC0_TYPE_CTL:
+ /* NB: assign to BE q to avoid bursting */
+ ds->TxPriority = MWL_WME_AC_BE;
+ break;
+ case IEEE80211_FC0_TYPE_DATA:
+ if (!ismcast) {
+ const struct ieee80211_txparam *tp = ni->ni_txparms;
+ /*
+ * EAPOL frames get forced to a fixed rate and w/o
+ * aggregation; otherwise check for any fixed rate
+ * for the client (may depend on association state).
+ */
+ if (m0->m_flags & M_EAPOL) {
+ const struct mwl_vap *mvp = MWL_VAP_CONST(vap);
+ ds->Format = mvp->mv_eapolformat;
+ ds->pad = htole16(
+ EAGLE_TXD_FIXED_RATE | EAGLE_TXD_DONT_AGGR);
+ } else if (tp != NULL && /* XXX temp dwds WAR */
+ tp->ucastrate != IEEE80211_FIXED_RATE_NONE) {
+ /* XXX pre-calculate per node */
+ ds->Format = htole16(
+ mwl_calcformat(tp->ucastrate, ni));
+ ds->pad = htole16(EAGLE_TXD_FIXED_RATE);
+ }
+ /* NB: EAPOL frames will never have qos set */
+ if (qos == 0)
+ ds->TxPriority = txq->qnum;
+#if MWL_MAXBA > 3
+ else if (mwl_bastream_match(&mn->mn_ba[3], qos))
+ ds->TxPriority = mn->mn_ba[3].txq;
+#endif
+#if MWL_MAXBA > 2
+ else if (mwl_bastream_match(&mn->mn_ba[2], qos))
+ ds->TxPriority = mn->mn_ba[2].txq;
+#endif
+#if MWL_MAXBA > 1
+ else if (mwl_bastream_match(&mn->mn_ba[1], qos))
+ ds->TxPriority = mn->mn_ba[1].txq;
+#endif
+#if MWL_MAXBA > 0
+ else if (mwl_bastream_match(&mn->mn_ba[0], qos))
+ ds->TxPriority = mn->mn_ba[0].txq;
+#endif
+ else
+ ds->TxPriority = txq->qnum;
+ } else
+ ds->TxPriority = txq->qnum;
+ break;
+ default:
+ if_printf(ifp, "bogus frame type 0x%x (%s)\n",
+ wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK, __func__);
+ sc->sc_stats.mst_tx_badframetype++;
+ m_freem(m0);
+ return EIO;
+ }
+
+ if (IFF_DUMPPKTS_XMIT(sc))
+ ieee80211_dump_pkt(ic,
+ mtod(m0, const uint8_t *)+sizeof(uint16_t),
+ m0->m_len - sizeof(uint16_t), ds->DataRate, -1);
+
+ MWL_TXQ_LOCK(txq);
+ ds->Status = htole32(EAGLE_TXD_STATUS_FW_OWNED);
+ STAILQ_INSERT_TAIL(&txq->active, bf, bf_list);
+ MWL_TXDESC_SYNC(txq, ds, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+
+ ifp->if_opackets++;
+ ifp->if_timer = 5;
+ MWL_TXQ_UNLOCK(txq);
+
+ return 0;
+#undef IEEE80211_DIR_DSTODS
+}
+
+static __inline int
+mwl_cvtlegacyrix(int rix)
+{
+#define N(x) (sizeof(x)/sizeof(x[0]))
+ static const int ieeerates[] =
+ { 2, 4, 11, 22, 44, 12, 18, 24, 36, 48, 72, 96, 108 };
+ return (rix < N(ieeerates) ? ieeerates[rix] : 0);
+#undef N
+}
+
+/*
+ * Process completed xmit descriptors from the specified queue.
+ */
+static int
+mwl_tx_processq(struct mwl_softc *sc, struct mwl_txq *txq)
+{
+#define EAGLE_TXD_STATUS_MCAST \
+ (EAGLE_TXD_STATUS_MULTICAST_TX | EAGLE_TXD_STATUS_BROADCAST_TX)
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct mwl_txbuf *bf;
+ struct mwl_txdesc *ds;
+ struct ieee80211_node *ni;
+ struct mwl_node *an;
+ int nreaped;
+ uint32_t status;
+
+ DPRINTF(sc, MWL_DEBUG_TX_PROC, "%s: tx queue %u\n", __func__, txq->qnum);
+ for (nreaped = 0;; nreaped++) {
+ MWL_TXQ_LOCK(txq);
+ bf = STAILQ_FIRST(&txq->active);
+ if (bf == NULL) {
+ MWL_TXQ_UNLOCK(txq);
+ break;
+ }
+ ds = bf->bf_desc;
+ MWL_TXDESC_SYNC(txq, ds,
+ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+ if (ds->Status & htole32(EAGLE_TXD_STATUS_FW_OWNED)) {
+ MWL_TXQ_UNLOCK(txq);
+ break;
+ }
+ STAILQ_REMOVE_HEAD(&txq->active, bf_list);
+ MWL_TXQ_UNLOCK(txq);
+
+#ifdef MWL_DEBUG
+ if (sc->sc_debug & MWL_DEBUG_XMIT_DESC)
+ mwl_printtxbuf(bf, txq->qnum, nreaped);
+#endif
+ ni = bf->bf_node;
+ if (ni != NULL) {
+ an = MWL_NODE(ni);
+ status = le32toh(ds->Status);
+ if (status & EAGLE_TXD_STATUS_OK) {
+ uint16_t Format = le16toh(ds->Format);
+ uint8_t txant = MS(Format, EAGLE_TXD_ANTENNA);
+
+ sc->sc_stats.mst_ant_tx[txant]++;
+ if (status & EAGLE_TXD_STATUS_OK_RETRY)
+ sc->sc_stats.mst_tx_retries++;
+ if (status & EAGLE_TXD_STATUS_OK_MORE_RETRY)
+ sc->sc_stats.mst_tx_mretries++;
+ if (txq->qnum >= MWL_WME_AC_VO)
+ ic->ic_wme.wme_hipri_traffic++;
+ ni->ni_txrate = MS(Format, EAGLE_TXD_RATE);
+ if ((Format & EAGLE_TXD_FORMAT_HT) == 0) {
+ ni->ni_txrate = mwl_cvtlegacyrix(
+ ni->ni_txrate);
+ } else
+ ni->ni_txrate |= IEEE80211_RATE_MCS;
+ sc->sc_stats.mst_tx_rate = ni->ni_txrate;
+ } else {
+ if (status & EAGLE_TXD_STATUS_FAILED_LINK_ERROR)
+ sc->sc_stats.mst_tx_linkerror++;
+ if (status & EAGLE_TXD_STATUS_FAILED_XRETRY)
+ sc->sc_stats.mst_tx_xretries++;
+ if (status & EAGLE_TXD_STATUS_FAILED_AGING)
+ sc->sc_stats.mst_tx_aging++;
+ if (bf->bf_m->m_flags & M_FF)
+ sc->sc_stats.mst_ff_txerr++;
+ }
+ /*
+ * Do any tx complete callback. Note this must
+ * be done before releasing the node reference.
+ * XXX no way to figure out if frame was ACK'd
+ */
+ if (bf->bf_m->m_flags & M_TXCB) {
+ /* XXX strip fw len in case header inspected */
+ m_adj(bf->bf_m, sizeof(uint16_t));
+ ieee80211_process_callback(ni, bf->bf_m,
+ (status & EAGLE_TXD_STATUS_OK) == 0);
+ }
+ /*
+ * Reclaim reference to node.
+ *
+ * NB: the node may be reclaimed here if, for example
+ * this is a DEAUTH message that was sent and the
+ * node was timed out due to inactivity.
+ */
+ ieee80211_free_node(ni);
+ }
+ ds->Status = htole32(EAGLE_TXD_STATUS_IDLE);
+
+ bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap,
+ BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
+ m_freem(bf->bf_m);
+
+ mwl_puttxbuf_tail(txq, bf);
+ }
+ return nreaped;
+#undef EAGLE_TXD_STATUS_MCAST
+}
+
+/*
+ * Deferred processing of transmit interrupt; special-cased
+ * for four hardware queues, 0-3.
+ */
+static void
+mwl_tx_proc(void *arg, int npending)
+{
+ struct mwl_softc *sc = arg;
+ struct ifnet *ifp = sc->sc_ifp;
+ int nreaped;
+
+ /*
+ * Process each active queue.
+ */
+ nreaped = 0;
+ if (!STAILQ_EMPTY(&sc->sc_txq[0].active))
+ nreaped += mwl_tx_processq(sc, &sc->sc_txq[0]);
+ if (!STAILQ_EMPTY(&sc->sc_txq[1].active))
+ nreaped += mwl_tx_processq(sc, &sc->sc_txq[1]);
+ if (!STAILQ_EMPTY(&sc->sc_txq[2].active))
+ nreaped += mwl_tx_processq(sc, &sc->sc_txq[2]);
+ if (!STAILQ_EMPTY(&sc->sc_txq[3].active))
+ nreaped += mwl_tx_processq(sc, &sc->sc_txq[3]);
+
+ if (nreaped != 0) {
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ ifp->if_timer = 0;
+ if (!IFQ_IS_EMPTY(&ifp->if_snd)) {
+ /* NB: kick fw; the tx thread may have been preempted */
+ mwl_hal_txstart(sc->sc_mh, 0);
+ mwl_start(ifp);
+ }
+ }
+}
+
+static void
+mwl_tx_draintxq(struct mwl_softc *sc, struct mwl_txq *txq)
+{
+ struct ieee80211_node *ni;
+ struct mwl_txbuf *bf;
+ u_int ix;
+
+ /*
+ * NB: this assumes output has been stopped and
+ * we do not need to block mwl_tx_tasklet
+ */
+ for (ix = 0;; ix++) {
+ MWL_TXQ_LOCK(txq);
+ bf = STAILQ_FIRST(&txq->active);
+ if (bf == NULL) {
+ MWL_TXQ_UNLOCK(txq);
+ break;
+ }
+ STAILQ_REMOVE_HEAD(&txq->active, bf_list);
+ MWL_TXQ_UNLOCK(txq);
+#ifdef MWL_DEBUG
+ if (sc->sc_debug & MWL_DEBUG_RESET) {
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ const struct mwltxrec *tr =
+ mtod(bf->bf_m, const struct mwltxrec *);
+ mwl_printtxbuf(bf, txq->qnum, ix);
+ ieee80211_dump_pkt(ic, (const uint8_t *)&tr->wh,
+ bf->bf_m->m_len - sizeof(tr->fwlen), 0, -1);
+ }
+#endif /* MWL_DEBUG */
+ bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
+ ni = bf->bf_node;
+ if (ni != NULL) {
+ /*
+ * Reclaim node reference.
+ */
+ ieee80211_free_node(ni);
+ }
+ m_freem(bf->bf_m);
+
+ mwl_puttxbuf_tail(txq, bf);
+ }
+}
+
+/*
+ * Drain the transmit queues and reclaim resources.
+ */
+static void
+mwl_draintxq(struct mwl_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ int i;
+
+ for (i = 0; i < MWL_NUM_TX_QUEUES; i++)
+ mwl_tx_draintxq(sc, &sc->sc_txq[i]);
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ ifp->if_timer = 0;
+}
+
+#ifdef MWL_DIAGAPI
+/*
+ * Reset the transmit queues to a pristine state after a fw download.
+ */
+static void
+mwl_resettxq(struct mwl_softc *sc)
+{
+ int i;
+
+ for (i = 0; i < MWL_NUM_TX_QUEUES; i++)
+ mwl_txq_reset(sc, &sc->sc_txq[i]);
+}
+#endif /* MWL_DIAGAPI */
+
+/*
+ * Clear the transmit queues of any frames submitted for the
+ * specified vap. This is done when the vap is deleted so we
+ * don't potentially reference the vap after it is gone.
+ * Note we cannot remove the frames; we only reclaim the node
+ * reference.
+ */
+static void
+mwl_cleartxq(struct mwl_softc *sc, struct ieee80211vap *vap)
+{
+ struct mwl_txq *txq;
+ struct mwl_txbuf *bf;
+ int i;
+
+ for (i = 0; i < MWL_NUM_TX_QUEUES; i++) {
+ txq = &sc->sc_txq[i];
+ MWL_TXQ_LOCK(txq);
+ STAILQ_FOREACH(bf, &txq->active, bf_list) {
+ struct ieee80211_node *ni = bf->bf_node;
+ if (ni != NULL && ni->ni_vap == vap) {
+ bf->bf_node = NULL;
+ ieee80211_free_node(ni);
+ }
+ }
+ MWL_TXQ_UNLOCK(txq);
+ }
+}
+
+static void
+mwl_recv_action(struct ieee80211_node *ni, const uint8_t *frm, const uint8_t *efrm)
+{
+ struct mwl_softc *sc = ni->ni_ic->ic_ifp->if_softc;
+ const struct ieee80211_action *ia;
+
+ ia = (const struct ieee80211_action *) frm;
+ if (ia->ia_category == IEEE80211_ACTION_CAT_HT &&
+ ia->ia_action == IEEE80211_ACTION_HT_MIMOPWRSAVE) {
+ const struct ieee80211_action_ht_mimopowersave *mps =
+ (const struct ieee80211_action_ht_mimopowersave *) ia;
+
+ mwl_hal_setmimops(sc->sc_mh, ni->ni_macaddr,
+ mps->am_control & IEEE80211_A_HT_MIMOPWRSAVE_ENA,
+ MS(mps->am_control, IEEE80211_A_HT_MIMOPWRSAVE_MODE));
+ } else
+ sc->sc_recv_action(ni, frm, efrm);
+}
+
+static int
+mwl_addba_request(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
+ int dialogtoken, int baparamset, int batimeout)
+{
+ struct mwl_softc *sc = ni->ni_ic->ic_ifp->if_softc;
+ struct mwl_node *mn = MWL_NODE(ni);
+ struct mwl_bastate *bas;
+
+ bas = tap->txa_private;
+ if (bas == NULL) {
+ const MWL_HAL_BASTREAM *sp;
+ /*
+ * Check for a free BA stream slot.
+ */
+#if MWL_MAXBA > 3
+ if (mn->mn_ba[3].bastream == NULL)
+ bas = &mn->mn_ba[3];
+ else
+#endif
+#if MWL_MAXBA > 2
+ if (mn->mn_ba[2].bastream == NULL)
+ bas = &mn->mn_ba[2];
+ else
+#endif
+#if MWL_MAXBA > 1
+ if (mn->mn_ba[1].bastream == NULL)
+ bas = &mn->mn_ba[1];
+ else
+#endif
+#if MWL_MAXBA > 0
+ if (mn->mn_ba[0].bastream == NULL)
+ bas = &mn->mn_ba[0];
+ else
+#endif
+ {
+ /* sta already has max BA streams */
+ /* XXX assign BA stream to highest priority tid */
+ DPRINTF(sc, MWL_DEBUG_AMPDU,
+ "%s: already has max bastreams\n", __func__);
+ sc->sc_stats.mst_ampdu_reject++;
+ return 0;
+ }
+ /* NB: no held reference to ni */
+ sp = mwl_hal_bastream_alloc(sc->sc_mh,
+ 1/* XXX immediate*/, ni->ni_macaddr, tap->txa_ac,
+ ni->ni_htparam, ni, tap);
+ if (sp == NULL) {
+ /*
+ * No available stream, return 0 so no
+ * a-mpdu aggregation will be done.
+ */
+ DPRINTF(sc, MWL_DEBUG_AMPDU,
+ "%s: no bastream available\n", __func__);
+ sc->sc_stats.mst_ampdu_nostream++;
+ return 0;
+ }
+ DPRINTF(sc, MWL_DEBUG_AMPDU, "%s: alloc bastream %p\n",
+ __func__, sp);
+ /* NB: qos is left zero so we won't match in mwl_tx_start */
+ bas->bastream = sp;
+ tap->txa_private = bas;
+ }
+ /* fetch current seq# from the firmware; if available */
+ if (mwl_hal_bastream_get_seqno(sc->sc_mh, bas->bastream,
+ &tap->txa_start) != 0)
+ tap->txa_start = 0;
+ return sc->sc_addba_request(ni, tap, dialogtoken, baparamset, batimeout);
+}
+
+static int
+mwl_addba_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
+ int code, int baparamset, int batimeout)
+{
+ struct mwl_softc *sc = ni->ni_ic->ic_ifp->if_softc;
+ struct mwl_bastate *bas;
+
+ bas = tap->txa_private;
+ if (bas == NULL) {
+ /* XXX should not happen */
+ DPRINTF(sc, MWL_DEBUG_AMPDU,
+ "%s: no BA stream allocated, AC %d\n",
+ __func__, tap->txa_ac);
+ sc->sc_stats.mst_addba_nostream++;
+ return 0;
+ }
+ if (code == IEEE80211_STATUS_SUCCESS) {
+ int bufsiz, error;
+
+ /*
+ * Tell the firmware to setup the BA stream;
+ * we know resources are available because we
+ * pre-allocated one before forming the request.
+ */
+ bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ);
+ if (bufsiz == 0)
+ bufsiz = IEEE80211_AGGR_BAWMAX;
+ error = mwl_hal_bastream_create(sc->sc_mh, bas->bastream,
+ bufsiz, bufsiz-1, tap->txa_start);
+ if (error != 0) {
+ /*
+ * Setup failed, return immediately so no a-mpdu
+ * aggregation will be done.
+ */
+ mwl_hal_bastream_destroy(sc->sc_mh, bas->bastream);
+ mwl_bastream_free(bas);
+ tap->txa_private = NULL;
+
+ DPRINTF(sc, MWL_DEBUG_AMPDU,
+ "%s: create failed, error %d, bufsiz %d AC %d "
+ "htparam 0x%x\n", __func__, error, bufsiz,
+ tap->txa_ac, ni->ni_htparam);
+ sc->sc_stats.mst_bacreate_failed++;
+ return 0;
+ }
+ /* NB: cache txq to avoid ptr indirect */
+ mwl_bastream_setup(bas, tap->txa_ac, bas->bastream->txq);
+ DPRINTF(sc, MWL_DEBUG_AMPDU,
+ "%s: bastream %p assigned to txq %d AC %d bufsiz %d "
+ "htparam 0x%x\n", __func__, bas->bastream,
+ bas->txq, tap->txa_ac, bufsiz, ni->ni_htparam);
+ } else {
+ /*
+ * Other side NAK'd us; return the resources.
+ */
+ DPRINTF(sc, MWL_DEBUG_AMPDU,
+ "%s: request failed with code %d, destroy bastream %p\n",
+ __func__, code, bas->bastream);
+ mwl_hal_bastream_destroy(sc->sc_mh, bas->bastream);
+ mwl_bastream_free(bas);
+ tap->txa_private = NULL;
+ }
+ /* NB: firmware sends BAR so we don't need to */
+ return sc->sc_addba_response(ni, tap, code, baparamset, batimeout);
+}
+
+static void
+mwl_addba_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
+{
+ struct mwl_softc *sc = ni->ni_ic->ic_ifp->if_softc;
+ struct mwl_bastate *bas;
+
+ bas = tap->txa_private;
+ if (bas != NULL) {
+ DPRINTF(sc, MWL_DEBUG_AMPDU, "%s: destroy bastream %p\n",
+ __func__, bas->bastream);
+ mwl_hal_bastream_destroy(sc->sc_mh, bas->bastream);
+ mwl_bastream_free(bas);
+ tap->txa_private = NULL;
+ }
+ sc->sc_addba_stop(ni, tap);
+}
+
+/*
+ * Setup the rx data structures. This should only be
+ * done once or we may get out of sync with the firmware.
+ */
+static int
+mwl_startrecv(struct mwl_softc *sc)
+{
+ if (!sc->sc_recvsetup) {
+ struct mwl_rxbuf *bf, *prev;
+ struct mwl_rxdesc *ds;
+
+ prev = NULL;
+ STAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) {
+ int error = mwl_rxbuf_init(sc, bf);
+ if (error != 0) {
+ DPRINTF(sc, MWL_DEBUG_RECV,
+ "%s: mwl_rxbuf_init failed %d\n",
+ __func__, error);
+ return error;
+ }
+ if (prev != NULL) {
+ ds = prev->bf_desc;
+ ds->pPhysNext = htole32(bf->bf_daddr);
+ }
+ prev = bf;
+ }
+ if (prev != NULL) {
+ ds = prev->bf_desc;
+ ds->pPhysNext =
+ htole32(STAILQ_FIRST(&sc->sc_rxbuf)->bf_daddr);
+ }
+ sc->sc_recvsetup = 1;
+ }
+ mwl_mode_init(sc); /* set filters, etc. */
+ return 0;
+}
+
+static MWL_HAL_APMODE
+mwl_getapmode(const struct ieee80211vap *vap, struct ieee80211_channel *chan)
+{
+ MWL_HAL_APMODE mode;
+
+ if (IEEE80211_IS_CHAN_HT(chan)) {
+ if (vap->iv_flags_ext & IEEE80211_FEXT_PUREN)
+ mode = AP_MODE_N_ONLY;
+ else if (IEEE80211_IS_CHAN_5GHZ(chan))
+ mode = AP_MODE_AandN;
+ else if (vap->iv_flags & IEEE80211_F_PUREG)
+ mode = AP_MODE_GandN;
+ else
+ mode = AP_MODE_BandGandN;
+ } else if (IEEE80211_IS_CHAN_ANYG(chan)) {
+ if (vap->iv_flags & IEEE80211_F_PUREG)
+ mode = AP_MODE_G_ONLY;
+ else
+ mode = AP_MODE_MIXED;
+ } else if (IEEE80211_IS_CHAN_B(chan))
+ mode = AP_MODE_B_ONLY;
+ else if (IEEE80211_IS_CHAN_A(chan))
+ mode = AP_MODE_A_ONLY;
+ else
+ mode = AP_MODE_MIXED; /* XXX should not happen? */
+ return mode;
+}
+
+static int
+mwl_setapmode(struct ieee80211vap *vap, struct ieee80211_channel *chan)
+{
+ struct mwl_hal_vap *hvap = MWL_VAP(vap)->mv_hvap;
+ return mwl_hal_setapmode(hvap, mwl_getapmode(vap, chan));
+}
+
+/*
+ * Set/change channels.
+ */
+static int
+mwl_chan_set(struct mwl_softc *sc, struct ieee80211_channel *chan)
+{
+ struct mwl_hal *mh = sc->sc_mh;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ MWL_HAL_CHANNEL hchan;
+ int maxtxpow;
+
+ DPRINTF(sc, MWL_DEBUG_RESET, "%s: chan %u MHz/flags 0x%x\n",
+ __func__, chan->ic_freq, chan->ic_flags);
+
+ /*
+ * Convert to a HAL channel description with
+ * the flags constrained to reflect the current
+ * operating mode.
+ */
+ mwl_mapchan(&hchan, chan);
+ mwl_hal_intrset(mh, 0); /* disable interrupts */
+#if 0
+ mwl_draintxq(sc); /* clear pending tx frames */
+#endif
+ mwl_hal_setchannel(mh, &hchan);
+ /*
+ * Tx power is cap'd by the regulatory setting and
+ * possibly a user-set limit. We pass the min of
+ * these to the hal to apply them to the cal data
+ * for this channel.
+ * XXX min bound?
+ */
+ maxtxpow = 2*chan->ic_maxregpower;
+ if (maxtxpow > ic->ic_txpowlimit)
+ maxtxpow = ic->ic_txpowlimit;
+ mwl_hal_settxpower(mh, &hchan, maxtxpow / 2);
+ /* NB: potentially change mcast/mgt rates */
+ mwl_setcurchanrates(sc);
+
+ /*
+ * Update internal state.
+ */
+ sc->sc_tx_th.wt_chan_freq = htole16(chan->ic_freq);
+ sc->sc_rx_th.wr_chan_freq = htole16(chan->ic_freq);
+ if (IEEE80211_IS_CHAN_A(chan)) {
+ sc->sc_tx_th.wt_chan_flags = htole16(IEEE80211_CHAN_A);
+ sc->sc_rx_th.wr_chan_flags = htole16(IEEE80211_CHAN_A);
+ } else if (IEEE80211_IS_CHAN_ANYG(chan)) {
+ sc->sc_tx_th.wt_chan_flags = htole16(IEEE80211_CHAN_G);
+ sc->sc_rx_th.wr_chan_flags = htole16(IEEE80211_CHAN_G);
+ } else {
+ sc->sc_tx_th.wt_chan_flags = htole16(IEEE80211_CHAN_B);
+ sc->sc_rx_th.wr_chan_flags = htole16(IEEE80211_CHAN_B);
+ }
+ sc->sc_curchan = hchan;
+ mwl_hal_intrset(mh, sc->sc_imask);
+
+ return 0;
+}
+
+static void
+mwl_scan_start(struct ieee80211com *ic)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct mwl_softc *sc = ifp->if_softc;
+
+ DPRINTF(sc, MWL_DEBUG_STATE, "%s\n", __func__);
+}
+
+static void
+mwl_scan_end(struct ieee80211com *ic)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct mwl_softc *sc = ifp->if_softc;
+
+ DPRINTF(sc, MWL_DEBUG_STATE, "%s\n", __func__);
+}
+
+static void
+mwl_set_channel(struct ieee80211com *ic)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct mwl_softc *sc = ifp->if_softc;
+
+ (void) mwl_chan_set(sc, ic->ic_curchan);
+}
+
+/*
+ * Handle a channel switch request. We inform the firmware
+ * and mark the global state to suppress various actions.
+ * NB: we issue only one request to the fw; we may be called
+ * multiple times if there are multiple vap's.
+ */
+static void
+mwl_startcsa(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct mwl_softc *sc = ic->ic_ifp->if_softc;
+ MWL_HAL_CHANNEL hchan;
+
+ if (sc->sc_csapending)
+ return;
+
+ mwl_mapchan(&hchan, ic->ic_csa_newchan);
+ /* 1 =>'s quiet channel */
+ mwl_hal_setchannelswitchie(sc->sc_mh, &hchan, 1, ic->ic_csa_count);
+ sc->sc_csapending = 1;
+}
+
+/*
+ * Plumb any static WEP key for the station. This is
+ * necessary as we must propagate the key from the
+ * global key table of the vap to each sta db entry.
+ */
+static void
+mwl_setanywepkey(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ if ((vap->iv_flags & (IEEE80211_F_PRIVACY|IEEE80211_F_WPA)) ==
+ IEEE80211_F_PRIVACY &&
+ vap->iv_def_txkey != IEEE80211_KEYIX_NONE &&
+ vap->iv_nw_keys[vap->iv_def_txkey].wk_keyix != IEEE80211_KEYIX_NONE)
+ (void) mwl_key_set(vap, &vap->iv_nw_keys[vap->iv_def_txkey], mac);
+}
+
+static int
+mwl_peerstadb(struct ieee80211_node *ni, int aid, int staid, MWL_HAL_PEERINFO *pi)
+{
+#define WME(ie) ((const struct ieee80211_wme_info *) ie)
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct mwl_hal_vap *hvap;
+ int error;
+
+ if (vap->iv_opmode == IEEE80211_M_WDS) {
+ /*
+ * WDS vap's do not have a f/w vap; instead they piggyback
+ * on an AP vap and we must install the sta db entry and
+ * crypto state using that AP's handle (the WDS vap has none).
+ */
+ hvap = MWL_VAP(vap)->mv_ap_hvap;
+ } else
+ hvap = MWL_VAP(vap)->mv_hvap;
+ error = mwl_hal_newstation(hvap, ni->ni_macaddr,
+ aid, staid, pi,
+ ni->ni_flags & (IEEE80211_NODE_QOS | IEEE80211_NODE_HT),
+ ni->ni_ies.wme_ie != NULL ? WME(ni->ni_ies.wme_ie)->wme_info : 0);
+ if (error == 0) {
+ /*
+ * Setup security for this station. For sta mode this is
+ * needed even though do the same thing on transition to
+ * AUTH state because the call to mwl_hal_newstation
+ * clobbers the crypto state we setup.
+ */
+ mwl_setanywepkey(vap, ni->ni_macaddr);
+ }
+ return error;
+#undef WME
+}
+
+static void
+mwl_setglobalkeys(struct ieee80211vap *vap)
+{
+ struct ieee80211_key *wk;
+
+ wk = &vap->iv_nw_keys[0];
+ for (; wk < &vap->iv_nw_keys[IEEE80211_WEP_NKID]; wk++)
+ if (wk->wk_keyix != IEEE80211_KEYIX_NONE)
+ (void) mwl_key_set(vap, wk, vap->iv_myaddr);
+}
+
+/*
+ * Re-create the local sta db entry for a vap to ensure
+ * up to date WME state is pushed to the firmware. Because
+ * this resets crypto state this must be followed by a
+ * reload of any keys in the global key table.
+ */
+static int
+mwl_localstadb(struct ieee80211vap *vap)
+{
+#define WME(ie) ((const struct ieee80211_wme_info *) ie)
+ struct mwl_hal_vap *hvap = MWL_VAP(vap)->mv_hvap;
+ struct ieee80211_node *bss;
+ int error;
+
+ switch (vap->iv_opmode) {
+ case IEEE80211_M_STA:
+ bss = vap->iv_bss;
+ error = mwl_hal_newstation(hvap, vap->iv_myaddr,
+ 0, 0, NULL, bss->ni_flags & IEEE80211_NODE_QOS,
+ bss->ni_ies.wme_ie != NULL ?
+ WME(bss->ni_ies.wme_ie)->wme_info : 0);
+ if (error == 0)
+ mwl_setglobalkeys(vap);
+ break;
+ case IEEE80211_M_HOSTAP:
+ error = mwl_hal_newstation(hvap, vap->iv_myaddr,
+ 0, 0, NULL, vap->iv_flags & IEEE80211_F_WME, 0);
+ if (error == 0)
+ mwl_setglobalkeys(vap);
+ break;
+ default:
+ error = 0;
+ break;
+ }
+ return error;
+#undef WME
+}
+
+static int
+mwl_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct mwl_vap *mvp = MWL_VAP(vap);
+ struct mwl_hal_vap *hvap = mvp->mv_hvap;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node *ni = NULL;
+ struct ifnet *ifp = ic->ic_ifp;
+ struct mwl_softc *sc = ifp->if_softc;
+ struct mwl_hal *mh = sc->sc_mh;
+ enum ieee80211_state ostate = vap->iv_state;
+ int error;
+
+ DPRINTF(sc, MWL_DEBUG_STATE, "%s: %s: %s -> %s\n",
+ vap->iv_ifp->if_xname, __func__,
+ ieee80211_state_name[ostate], ieee80211_state_name[nstate]);
+
+ callout_stop(&sc->sc_timer);
+ /*
+ * Clear current radar detection state.
+ */
+ if (ostate == IEEE80211_S_CAC) {
+ /* stop quiet mode radar detection */
+ mwl_hal_setradardetection(mh, DR_CHK_CHANNEL_AVAILABLE_STOP);
+ } else if (sc->sc_radarena) {
+ /* stop in-service radar detection */
+ mwl_hal_setradardetection(mh, DR_DFS_DISABLE);
+ sc->sc_radarena = 0;
+ }
+ /*
+ * Carry out per-state actions before doing net80211 work.
+ */
+ if (nstate == IEEE80211_S_INIT) {
+ /* NB: only ap+sta vap's have a fw entity */
+ if (hvap != NULL)
+ mwl_hal_stop(hvap);
+ } else if (nstate == IEEE80211_S_SCAN) {
+ mwl_hal_start(hvap);
+ /* NB: this disables beacon frames */
+ mwl_hal_setinframode(hvap);
+ } else if (nstate == IEEE80211_S_AUTH) {
+ /*
+ * Must create a sta db entry in case a WEP key needs to
+ * be plumbed. This entry will be overwritten if we
+ * associate; otherwise it will be reclaimed on node free.
+ */
+ ni = vap->iv_bss;
+ MWL_NODE(ni)->mn_hvap = hvap;
+ (void) mwl_peerstadb(ni, 0, 0, NULL);
+ } else if (nstate == IEEE80211_S_CSA) {
+ /* XXX move to below? */
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP)
+ mwl_startcsa(vap);
+ } else if (nstate == IEEE80211_S_CAC) {
+ /* XXX move to below? */
+ /* stop ap xmit and enable quiet mode radar detection */
+ mwl_hal_setradardetection(mh, DR_CHK_CHANNEL_AVAILABLE_START);
+ }
+
+ /*
+ * Invoke the parent method to do net80211 work.
+ */
+ error = mvp->mv_newstate(vap, nstate, arg);
+
+ /*
+ * Carry out work that must be done after net80211 runs;
+ * this work requires up to date state (e.g. iv_bss).
+ */
+ if (error == 0 && nstate == IEEE80211_S_RUN) {
+ /* NB: collect bss node again, it may have changed */
+ ni = vap->iv_bss;
+
+ DPRINTF(sc, MWL_DEBUG_STATE,
+ "%s: %s(RUN): iv_flags 0x%08x bintvl %d bssid %s "
+ "capinfo 0x%04x chan %d\n",
+ vap->iv_ifp->if_xname, __func__, vap->iv_flags,
+ ni->ni_intval, ether_sprintf(ni->ni_bssid), ni->ni_capinfo,
+ ieee80211_chan2ieee(ic, ic->ic_curchan));
+
+ /*
+ * Recreate local sta db entry to update WME state.
+ */
+ mwl_localstadb(vap);
+ switch (vap->iv_opmode) {
+ case IEEE80211_M_HOSTAP:
+ if (ostate == IEEE80211_S_CAC) {
+ /* enable in-service radar detection */
+ mwl_hal_setradardetection(mh,
+ DR_IN_SERVICE_MONITOR_START);
+ sc->sc_radarena = 1;
+ }
+ /*
+ * Allocate and setup the beacon frame
+ * (and related state).
+ */
+ error = mwl_reset_vap(vap, IEEE80211_S_RUN);
+ if (error != 0) {
+ DPRINTF(sc, MWL_DEBUG_STATE,
+ "%s: beacon setup failed, error %d\n",
+ __func__, error);
+ goto bad;
+ }
+ /* NB: must be after setting up beacon */
+ mwl_hal_start(hvap);
+ break;
+ case IEEE80211_M_STA:
+ DPRINTF(sc, MWL_DEBUG_STATE, "%s: %s: aid 0x%x\n",
+ vap->iv_ifp->if_xname, __func__, ni->ni_associd);
+ /*
+ * Set state now that we're associated.
+ */
+ mwl_hal_setassocid(hvap, ni->ni_bssid, ni->ni_associd);
+ mwl_setrates(vap);
+ mwl_hal_setrtsthreshold(hvap, vap->iv_rtsthreshold);
+ break;
+ case IEEE80211_M_WDS:
+ DPRINTF(sc, MWL_DEBUG_STATE, "%s: %s: bssid %s\n",
+ vap->iv_ifp->if_xname, __func__,
+ ether_sprintf(ni->ni_bssid));
+ mwl_seteapolformat(vap);
+ break;
+ default:
+ break;
+ }
+ /*
+ * Set CS mode according to operating channel;
+ * this mostly an optimization for 5GHz.
+ *
+ * NB: must follow mwl_hal_start which resets csmode
+ */
+ if (IEEE80211_IS_CHAN_5GHZ(ic->ic_bsschan))
+ mwl_hal_setcsmode(mh, CSMODE_AGGRESSIVE);
+ else
+ mwl_hal_setcsmode(mh, CSMODE_AUTO_ENA);
+ /*
+ * Start timer to prod firmware.
+ */
+ if (sc->sc_ageinterval != 0)
+ callout_reset(&sc->sc_timer, sc->sc_ageinterval*hz,
+ mwl_agestations, sc);
+ } else if (nstate == IEEE80211_S_SLEEP) {
+ /* XXX set chip in power save */
+ }
+bad:
+ return error;
+}
+
+/*
+ * Convert a legacy rate set to a firmware bitmask.
+ */
+static uint32_t
+get_rate_bitmap(const struct ieee80211_rateset *rs)
+{
+ uint32_t rates;
+ int i;
+
+ rates = 0;
+ for (i = 0; i < rs->rs_nrates; i++)
+ switch (rs->rs_rates[i] & IEEE80211_RATE_VAL) {
+ case 2: rates |= 0x001; break;
+ case 4: rates |= 0x002; break;
+ case 11: rates |= 0x004; break;
+ case 22: rates |= 0x008; break;
+ case 44: rates |= 0x010; break;
+ case 12: rates |= 0x020; break;
+ case 18: rates |= 0x040; break;
+ case 24: rates |= 0x080; break;
+ case 36: rates |= 0x100; break;
+ case 48: rates |= 0x200; break;
+ case 72: rates |= 0x400; break;
+ case 96: rates |= 0x800; break;
+ case 108: rates |= 0x1000; break;
+ }
+ return rates;
+}
+
+/*
+ * Construct an HT firmware bitmask from an HT rate set.
+ */
+static uint32_t
+get_htrate_bitmap(const struct ieee80211_htrateset *rs)
+{
+ uint32_t rates;
+ int i;
+
+ rates = 0;
+ for (i = 0; i < rs->rs_nrates; i++) {
+ if (rs->rs_rates[i] < 16)
+ rates |= 1<<rs->rs_rates[i];
+ }
+ return rates;
+}
+
+/*
+ * Manage station id's; these are separate from AID's
+ * as AID's may have values out of the range of possible
+ * station id's acceptable to the firmware.
+ */
+static int
+allocstaid(struct mwl_softc *sc, int aid)
+{
+ int staid;
+
+ if (!(0 < aid && aid < MWL_MAXSTAID) || isset(sc->sc_staid, aid)) {
+ /* NB: don't use 0 */
+ for (staid = 1; staid < MWL_MAXSTAID; staid++)
+ if (isclr(sc->sc_staid, staid))
+ break;
+ } else
+ staid = aid;
+ setbit(sc->sc_staid, staid);
+ return staid;
+}
+
+static void
+delstaid(struct mwl_softc *sc, int staid)
+{
+ clrbit(sc->sc_staid, staid);
+}
+
+/*
+ * Setup driver-specific state for a newly associated node.
+ * Note that we're called also on a re-associate, the isnew
+ * param tells us if this is the first time or not.
+ */
+static void
+mwl_newassoc(struct ieee80211_node *ni, int isnew)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct mwl_softc *sc = vap->iv_ic->ic_ifp->if_softc;
+ struct mwl_node *mn = MWL_NODE(ni);
+ MWL_HAL_PEERINFO pi;
+ uint16_t aid;
+ int error;
+
+ aid = IEEE80211_AID(ni->ni_associd);
+ if (isnew) {
+ mn->mn_staid = allocstaid(sc, aid);
+ mn->mn_hvap = MWL_VAP(vap)->mv_hvap;
+ } else {
+ mn = MWL_NODE(ni);
+ /* XXX reset BA stream? */
+ }
+ DPRINTF(sc, MWL_DEBUG_NODE, "%s: mac %s isnew %d aid %d staid %d\n",
+ __func__, ether_sprintf(ni->ni_macaddr), isnew, aid, mn->mn_staid);
+ /*
+ * Craft station database entry for station.
+ * NB: use host byte order here, the hal handles byte swapping.
+ */
+ memset(&pi, 0, sizeof(pi));
+ pi.LegacyRateBitMap = get_rate_bitmap(&ni->ni_rates);
+ pi.CapInfo = ni->ni_capinfo;
+ if (ni->ni_flags & IEEE80211_NODE_HT) {
+ /* HT capabilities, etc */
+ pi.HTCapabilitiesInfo = ni->ni_htcap;
+ /* XXX pi.HTCapabilitiesInfo */
+ pi.MacHTParamInfo = ni->ni_htparam;
+ pi.HTRateBitMap = get_htrate_bitmap(&ni->ni_htrates);
+ pi.AddHtInfo.ControlChan = ni->ni_htctlchan;
+ pi.AddHtInfo.AddChan = ni->ni_ht2ndchan;
+ pi.AddHtInfo.OpMode = ni->ni_htopmode;
+ pi.AddHtInfo.stbc = ni->ni_htstbc;
+
+ /* constrain according to local configuration */
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_SHORTGI40) == 0)
+ pi.HTCapabilitiesInfo &= ~IEEE80211_HTCAP_SHORTGI40;
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_SHORTGI20) == 0)
+ pi.HTCapabilitiesInfo &= ~IEEE80211_HTCAP_SHORTGI20;
+ if (ni->ni_chw != 40)
+ pi.HTCapabilitiesInfo &= ~IEEE80211_HTCAP_CHWIDTH40;
+ }
+ error = mwl_peerstadb(ni, aid, mn->mn_staid, &pi);
+ if (error != 0) {
+ DPRINTF(sc, MWL_DEBUG_NODE,
+ "%s: error %d creating sta db entry\n",
+ __func__, error);
+ /* XXX how to deal with error? */
+ }
+}
+
+/*
+ * Periodically poke the firmware to age out station state
+ * (power save queues, pending tx aggregates).
+ */
+static void
+mwl_agestations(void *arg)
+{
+ struct mwl_softc *sc = arg;
+
+ mwl_hal_setkeepalive(sc->sc_mh);
+ if (sc->sc_ageinterval != 0) /* NB: catch dynamic changes */
+ callout_reset(&sc->sc_timer, sc->sc_ageinterval*hz,
+ mwl_agestations, sc);
+}
+
+static const struct mwl_hal_channel *
+findhalchannel(const MWL_HAL_CHANNELINFO *ci, int ieee)
+{
+ int i;
+
+ for (i = 0; i < ci->nchannels; i++) {
+ const struct mwl_hal_channel *hc = &ci->channels[i];
+ if (hc->ieee == ieee)
+ return hc;
+ }
+ return NULL;
+}
+
+static int
+mwl_setregdomain(struct ieee80211com *ic, struct ieee80211_regdomain *rd,
+ int nchan, struct ieee80211_channel chans[])
+{
+ struct mwl_softc *sc = ic->ic_ifp->if_softc;
+ struct mwl_hal *mh = sc->sc_mh;
+ const MWL_HAL_CHANNELINFO *ci;
+ int i;
+
+ for (i = 0; i < nchan; i++) {
+ struct ieee80211_channel *c = &chans[i];
+ const struct mwl_hal_channel *hc;
+
+ if (IEEE80211_IS_CHAN_2GHZ(c)) {
+ mwl_hal_getchannelinfo(mh, MWL_FREQ_BAND_2DOT4GHZ,
+ IEEE80211_IS_CHAN_HT40(c) ?
+ MWL_CH_40_MHz_WIDTH : MWL_CH_20_MHz_WIDTH, &ci);
+ } else if (IEEE80211_IS_CHAN_5GHZ(c)) {
+ mwl_hal_getchannelinfo(mh, MWL_FREQ_BAND_5GHZ,
+ IEEE80211_IS_CHAN_HT40(c) ?
+ MWL_CH_40_MHz_WIDTH : MWL_CH_20_MHz_WIDTH, &ci);
+ } else {
+ if_printf(ic->ic_ifp,
+ "%s: channel %u freq %u/0x%x not 2.4/5GHz\n",
+ __func__, c->ic_ieee, c->ic_freq, c->ic_flags);
+ return EINVAL;
+ }
+ /*
+ * Verify channel has cal data and cap tx power.
+ */
+ hc = findhalchannel(ci, c->ic_ieee);
+ if (hc != NULL) {
+ if (c->ic_maxpower > 2*hc->maxTxPow)
+ c->ic_maxpower = 2*hc->maxTxPow;
+ goto next;
+ }
+ if (IEEE80211_IS_CHAN_HT40(c)) {
+ /*
+ * Look for the extension channel since the
+ * hal table only has the primary channel.
+ */
+ hc = findhalchannel(ci, c->ic_extieee);
+ if (hc != NULL) {
+ if (c->ic_maxpower > 2*hc->maxTxPow)
+ c->ic_maxpower = 2*hc->maxTxPow;
+ goto next;
+ }
+ }
+ if_printf(ic->ic_ifp,
+ "%s: no cal data for channel %u ext %u freq %u/0x%x\n",
+ __func__, c->ic_ieee, c->ic_extieee,
+ c->ic_freq, c->ic_flags);
+ return EINVAL;
+ next:
+ ;
+ }
+ return 0;
+}
+
+#define IEEE80211_CHAN_HTG (IEEE80211_CHAN_HT|IEEE80211_CHAN_G)
+#define IEEE80211_CHAN_HTA (IEEE80211_CHAN_HT|IEEE80211_CHAN_A)
+
+static void
+addchan(struct ieee80211_channel *c, int freq, int flags, int ieee, int txpow)
+{
+ c->ic_freq = freq;
+ c->ic_flags = flags;
+ c->ic_ieee = ieee;
+ c->ic_minpower = 0;
+ c->ic_maxpower = 2*txpow;
+ c->ic_maxregpower = txpow;
+}
+
+static const struct ieee80211_channel *
+findchannel(const struct ieee80211_channel chans[], int nchans,
+ int freq, int flags)
+{
+ const struct ieee80211_channel *c;
+ int i;
+
+ for (i = 0; i < nchans; i++) {
+ c = &chans[i];
+ if (c->ic_freq == freq && c->ic_flags == flags)
+ return c;
+ }
+ return NULL;
+}
+
+static void
+addht40channels(struct ieee80211_channel chans[], int maxchans, int *nchans,
+ const MWL_HAL_CHANNELINFO *ci, int flags)
+{
+ struct ieee80211_channel *c;
+ const struct ieee80211_channel *extc;
+ const struct mwl_hal_channel *hc;
+ int i;
+
+ c = &chans[*nchans];
+
+ flags &= ~IEEE80211_CHAN_HT;
+ for (i = 0; i < ci->nchannels; i++) {
+ /*
+ * Each entry defines an HT40 channel pair; find the
+ * extension channel above and the insert the pair.
+ */
+ hc = &ci->channels[i];
+ extc = findchannel(chans, *nchans, hc->freq+20,
+ flags | IEEE80211_CHAN_HT20);
+ if (extc != NULL) {
+ if (*nchans >= maxchans)
+ break;
+ addchan(c, hc->freq, flags | IEEE80211_CHAN_HT40U,
+ hc->ieee, hc->maxTxPow);
+ c->ic_extieee = extc->ic_ieee;
+ c++, (*nchans)++;
+ if (*nchans >= maxchans)
+ break;
+ addchan(c, extc->ic_freq, flags | IEEE80211_CHAN_HT40D,
+ extc->ic_ieee, hc->maxTxPow);
+ c->ic_extieee = hc->ieee;
+ c++, (*nchans)++;
+ }
+ }
+}
+
+static void
+addchannels(struct ieee80211_channel chans[], int maxchans, int *nchans,
+ const MWL_HAL_CHANNELINFO *ci, int flags)
+{
+ struct ieee80211_channel *c;
+ int i;
+
+ c = &chans[*nchans];
+
+ for (i = 0; i < ci->nchannels; i++) {
+ const struct mwl_hal_channel *hc;
+
+ hc = &ci->channels[i];
+ if (*nchans >= maxchans)
+ break;
+ addchan(c, hc->freq, flags, hc->ieee, hc->maxTxPow);
+ c++, (*nchans)++;
+ if (flags == IEEE80211_CHAN_G || flags == IEEE80211_CHAN_HTG) {
+ /* g channel have a separate b-only entry */
+ if (*nchans >= maxchans)
+ break;
+ c[0] = c[-1];
+ c[-1].ic_flags = IEEE80211_CHAN_B;
+ c++, (*nchans)++;
+ }
+ if (flags == IEEE80211_CHAN_HTG) {
+ /* HT g channel have a separate g-only entry */
+ if (*nchans >= maxchans)
+ break;
+ c[-1].ic_flags = IEEE80211_CHAN_G;
+ c[0] = c[-1];
+ c[0].ic_flags &= ~IEEE80211_CHAN_HT;
+ c[0].ic_flags |= IEEE80211_CHAN_HT20; /* HT20 */
+ c++, (*nchans)++;
+ }
+ if (flags == IEEE80211_CHAN_HTA) {
+ /* HT a channel have a separate a-only entry */
+ if (*nchans >= maxchans)
+ break;
+ c[-1].ic_flags = IEEE80211_CHAN_A;
+ c[0] = c[-1];
+ c[0].ic_flags &= ~IEEE80211_CHAN_HT;
+ c[0].ic_flags |= IEEE80211_CHAN_HT20; /* HT20 */
+ c++, (*nchans)++;
+ }
+ }
+}
+
+static void
+getchannels(struct mwl_softc *sc, int maxchans, int *nchans,
+ struct ieee80211_channel chans[])
+{
+ const MWL_HAL_CHANNELINFO *ci;
+
+ /*
+ * Use the channel info from the hal to craft the
+ * channel list. Note that we pass back an unsorted
+ * list; the caller is required to sort it for us
+ * (if desired).
+ */
+ *nchans = 0;
+ if (mwl_hal_getchannelinfo(sc->sc_mh,
+ MWL_FREQ_BAND_2DOT4GHZ, MWL_CH_20_MHz_WIDTH, &ci) == 0)
+ addchannels(chans, maxchans, nchans, ci, IEEE80211_CHAN_HTG);
+ if (mwl_hal_getchannelinfo(sc->sc_mh,
+ MWL_FREQ_BAND_5GHZ, MWL_CH_20_MHz_WIDTH, &ci) == 0)
+ addchannels(chans, maxchans, nchans, ci, IEEE80211_CHAN_HTA);
+ if (mwl_hal_getchannelinfo(sc->sc_mh,
+ MWL_FREQ_BAND_2DOT4GHZ, MWL_CH_40_MHz_WIDTH, &ci) == 0)
+ addht40channels(chans, maxchans, nchans, ci, IEEE80211_CHAN_HTG);
+ if (mwl_hal_getchannelinfo(sc->sc_mh,
+ MWL_FREQ_BAND_5GHZ, MWL_CH_40_MHz_WIDTH, &ci) == 0)
+ addht40channels(chans, maxchans, nchans, ci, IEEE80211_CHAN_HTA);
+}
+
+static void
+mwl_getradiocaps(struct ieee80211com *ic,
+ int maxchans, int *nchans, struct ieee80211_channel chans[])
+{
+ struct mwl_softc *sc = ic->ic_ifp->if_softc;
+
+ getchannels(sc, maxchans, nchans, chans);
+}
+
+static int
+mwl_getchannels(struct mwl_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+
+ /*
+ * Use the channel info from the hal to craft the
+ * channel list for net80211. Note that we pass up
+ * an unsorted list; net80211 will sort it for us.
+ */
+ memset(ic->ic_channels, 0, sizeof(ic->ic_channels));
+ ic->ic_nchans = 0;
+ getchannels(sc, IEEE80211_CHAN_MAX, &ic->ic_nchans, ic->ic_channels);
+
+ ic->ic_regdomain.regdomain = SKU_DEBUG;
+ ic->ic_regdomain.country = CTRY_DEFAULT;
+ ic->ic_regdomain.location = 'I';
+ ic->ic_regdomain.isocc[0] = ' '; /* XXX? */
+ ic->ic_regdomain.isocc[1] = ' ';
+ return (ic->ic_nchans == 0 ? EIO : 0);
+}
+#undef IEEE80211_CHAN_HTA
+#undef IEEE80211_CHAN_HTG
+
+#ifdef MWL_DEBUG
+static void
+mwl_printrxbuf(const struct mwl_rxbuf *bf, u_int ix)
+{
+ const struct mwl_rxdesc *ds = bf->bf_desc;
+ uint32_t status = le32toh(ds->Status);
+
+ printf("R[%2u] (DS.V:%p DS.P:%p) NEXT:%08x DATA:%08x RC:%02x%s\n"
+ " STAT:%02x LEN:%04x RSSI:%02x CHAN:%02x RATE:%02x QOS:%04x HT:%04x\n",
+ ix, ds, (const struct mwl_desc *)bf->bf_daddr,
+ le32toh(ds->pPhysNext), le32toh(ds->pPhysBuffData),
+ ds->RxControl,
+ ds->RxControl != EAGLE_RXD_CTRL_DRIVER_OWN ?
+ "" : (status & EAGLE_RXD_STATUS_OK) ? " *" : " !",
+ ds->Status, le16toh(ds->PktLen), ds->RSSI, ds->Channel,
+ ds->Rate, le16toh(ds->QosCtrl), le16toh(ds->HtSig2));
+}
+
+static void
+mwl_printtxbuf(const struct mwl_txbuf *bf, u_int qnum, u_int ix)
+{
+ const struct mwl_txdesc *ds = bf->bf_desc;
+ uint32_t status = le32toh(ds->Status);
+
+ printf("Q%u[%3u]", qnum, ix);
+ printf(" (DS.V:%p DS.P:%p)\n",
+ ds, (const struct mwl_txdesc *)bf->bf_daddr);
+ printf(" NEXT:%08x DATA:%08x LEN:%04x STAT:%08x%s\n",
+ le32toh(ds->pPhysNext),
+ le32toh(ds->PktPtr), le16toh(ds->PktLen), status,
+ status & EAGLE_TXD_STATUS_USED ?
+ "" : (status & 3) != 0 ? " *" : " !");
+ printf(" RATE:%02x PRI:%x QOS:%04x SAP:%08x FORMAT:%04x\n",
+ ds->DataRate, ds->TxPriority, le16toh(ds->QosCtrl),
+ le32toh(ds->SapPktInfo), le16toh(ds->Format));
+#if MWL_TXDESC > 1
+ printf(" MULTIFRAMES:%u LEN:%04x %04x %04x %04x %04x %04x\n"
+ , le32toh(ds->multiframes)
+ , le16toh(ds->PktLenArray[0]), le16toh(ds->PktLenArray[1])
+ , le16toh(ds->PktLenArray[2]), le16toh(ds->PktLenArray[3])
+ , le16toh(ds->PktLenArray[4]), le16toh(ds->PktLenArray[5])
+ );
+ printf(" DATA:%08x %08x %08x %08x %08x %08x\n"
+ , le32toh(ds->PktPtrArray[0]), le32toh(ds->PktPtrArray[1])
+ , le32toh(ds->PktPtrArray[2]), le32toh(ds->PktPtrArray[3])
+ , le32toh(ds->PktPtrArray[4]), le32toh(ds->PktPtrArray[5])
+ );
+#endif
+#if 0
+{ const uint8_t *cp = (const uint8_t *) ds;
+ int i;
+ for (i = 0; i < sizeof(struct mwl_txdesc); i++) {
+ printf("%02x ", cp[i]);
+ if (((i+1) % 16) == 0)
+ printf("\n");
+ }
+ printf("\n");
+}
+#endif
+}
+#endif /* MWL_DEBUG */
+
+#if 0
+static void
+mwl_txq_dump(struct mwl_txq *txq)
+{
+ struct mwl_txbuf *bf;
+ int i = 0;
+
+ MWL_TXQ_LOCK(txq);
+ STAILQ_FOREACH(bf, &txq->active, bf_list) {
+ struct mwl_txdesc *ds = bf->bf_desc;
+ MWL_TXDESC_SYNC(txq, ds,
+ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+#ifdef MWL_DEBUG
+ mwl_printtxbuf(bf, txq->qnum, i);
+#endif
+ i++;
+ }
+ MWL_TXQ_UNLOCK(txq);
+}
+#endif
+
+static void
+mwl_watchdog(struct ifnet *ifp)
+{
+ struct mwl_softc *sc = ifp->if_softc;
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) && !sc->sc_invalid) {
+ if (mwl_hal_setkeepalive(sc->sc_mh))
+ if_printf(ifp, "transmit timeout (firmware hung?)\n");
+ else
+ if_printf(ifp, "transmit timeout\n");
+#if 0
+ mwl_reset(ifp);
+mwl_txq_dump(&sc->sc_txq[0]);/*XXX*/
+#endif
+ ifp->if_oerrors++;
+ sc->sc_stats.mst_watchdog++;
+ }
+}
+
+#ifdef MWL_DIAGAPI
+/*
+ * Diagnostic interface to the HAL. This is used by various
+ * tools to do things like retrieve register contents for
+ * debugging. The mechanism is intentionally opaque so that
+ * it can change frequently w/o concern for compatiblity.
+ */
+static int
+mwl_ioctl_diag(struct mwl_softc *sc, struct mwl_diag *md)
+{
+ struct mwl_hal *mh = sc->sc_mh;
+ u_int id = md->md_id & MWL_DIAG_ID;
+ void *indata = NULL;
+ void *outdata = NULL;
+ u_int32_t insize = md->md_in_size;
+ u_int32_t outsize = md->md_out_size;
+ int error = 0;
+
+ if (md->md_id & MWL_DIAG_IN) {
+ /*
+ * Copy in data.
+ */
+ indata = malloc(insize, M_TEMP, M_NOWAIT);
+ if (indata == NULL) {
+ error = ENOMEM;
+ goto bad;
+ }
+ error = copyin(md->md_in_data, indata, insize);
+ if (error)
+ goto bad;
+ }
+ if (md->md_id & MWL_DIAG_DYN) {
+ /*
+ * Allocate a buffer for the results (otherwise the HAL
+ * returns a pointer to a buffer where we can read the
+ * results). Note that we depend on the HAL leaving this
+ * pointer for us to use below in reclaiming the buffer;
+ * may want to be more defensive.
+ */
+ outdata = malloc(outsize, M_TEMP, M_NOWAIT);
+ if (outdata == NULL) {
+ error = ENOMEM;
+ goto bad;
+ }
+ }
+ if (mwl_hal_getdiagstate(mh, id, indata, insize, &outdata, &outsize)) {
+ if (outsize < md->md_out_size)
+ md->md_out_size = outsize;
+ if (outdata != NULL)
+ error = copyout(outdata, md->md_out_data,
+ md->md_out_size);
+ } else {
+ error = EINVAL;
+ }
+bad:
+ if ((md->md_id & MWL_DIAG_IN) && indata != NULL)
+ free(indata, M_TEMP);
+ if ((md->md_id & MWL_DIAG_DYN) && outdata != NULL)
+ free(outdata, M_TEMP);
+ return error;
+}
+
+static int
+mwl_ioctl_reset(struct mwl_softc *sc, struct mwl_diag *md)
+{
+ struct mwl_hal *mh = sc->sc_mh;
+ int error;
+
+ MWL_LOCK_ASSERT(sc);
+
+ if (md->md_id == 0 && mwl_hal_fwload(mh, NULL) != 0) {
+ device_printf(sc->sc_dev, "unable to load firmware\n");
+ return EIO;
+ }
+ if (mwl_hal_gethwspecs(mh, &sc->sc_hwspecs) != 0) {
+ device_printf(sc->sc_dev, "unable to fetch h/w specs\n");
+ return EIO;
+ }
+ error = mwl_setupdma(sc);
+ if (error != 0) {
+ /* NB: mwl_setupdma prints a msg */
+ return error;
+ }
+ /*
+ * Reset tx/rx data structures; after reload we must
+ * re-start the driver's notion of the next xmit/recv.
+ */
+ mwl_draintxq(sc); /* clear pending frames */
+ mwl_resettxq(sc); /* rebuild tx q lists */
+ sc->sc_rxnext = NULL; /* force rx to start at the list head */
+ return 0;
+}
+#endif /* MWL_DIAGAPI */
+
+static int
+mwl_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+#define IS_RUNNING(ifp) \
+ ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING))
+ struct mwl_softc *sc = ifp->if_softc;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifreq *ifr = (struct ifreq *)data;
+ int error = 0, startall;
+
+ switch (cmd) {
+ case SIOCSIFFLAGS:
+ MWL_LOCK(sc);
+ startall = 0;
+ if (IS_RUNNING(ifp)) {
+ /*
+ * To avoid rescanning another access point,
+ * do not call mwl_init() here. Instead,
+ * only reflect promisc mode settings.
+ */
+ mwl_mode_init(sc);
+ } else if (ifp->if_flags & IFF_UP) {
+ /*
+ * Beware of being called during attach/detach
+ * to reset promiscuous mode. In that case we
+ * will still be marked UP but not RUNNING.
+ * However trying to re-init the interface
+ * is the wrong thing to do as we've already
+ * torn down much of our state. There's
+ * probably a better way to deal with this.
+ */
+ if (!sc->sc_invalid) {
+ mwl_init_locked(sc); /* XXX lose error */
+ startall = 1;
+ }
+ } else
+ mwl_stop_locked(ifp, 1);
+ MWL_UNLOCK(sc);
+ if (startall)
+ ieee80211_start_all(ic);
+ break;
+ case SIOCGMVSTATS:
+ mwl_hal_gethwstats(sc->sc_mh, &sc->sc_stats.hw_stats);
+ /* NB: embed these numbers to get a consistent view */
+ sc->sc_stats.mst_tx_packets = ifp->if_opackets;
+ sc->sc_stats.mst_rx_packets = ifp->if_ipackets;
+ /*
+ * NB: Drop the softc lock in case of a page fault;
+ * we'll accept any potential inconsisentcy in the
+ * statistics. The alternative is to copy the data
+ * to a local structure.
+ */
+ return copyout(&sc->sc_stats,
+ ifr->ifr_data, sizeof (sc->sc_stats));
+#ifdef MWL_DIAGAPI
+ case SIOCGMVDIAG:
+ /* XXX check privs */
+ return mwl_ioctl_diag(sc, (struct mwl_diag *) ifr);
+ case SIOCGMVRESET:
+ /* XXX check privs */
+ MWL_LOCK(sc);
+ error = mwl_ioctl_reset(sc,(struct mwl_diag *) ifr);
+ MWL_UNLOCK(sc);
+ break;
+#endif /* MWL_DIAGAPI */
+ case SIOCGIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
+ break;
+ case SIOCGIFADDR:
+ error = ether_ioctl(ifp, cmd, data);
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ return error;
+#undef IS_RUNNING
+}
+
+#ifdef MWL_DEBUG
+static int
+mwl_sysctl_debug(SYSCTL_HANDLER_ARGS)
+{
+ struct mwl_softc *sc = arg1;
+ int debug, error;
+
+ debug = sc->sc_debug | (mwl_hal_getdebug(sc->sc_mh) << 24);
+ error = sysctl_handle_int(oidp, &debug, 0, req);
+ if (error || !req->newptr)
+ return error;
+ mwl_hal_setdebug(sc->sc_mh, debug >> 24);
+ sc->sc_debug = debug & 0x00ffffff;
+ return 0;
+}
+#endif /* MWL_DEBUG */
+
+static void
+mwl_sysctlattach(struct mwl_softc *sc)
+{
+#ifdef MWL_DEBUG
+ struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
+ struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
+
+ sc->sc_debug = mwl_debug;
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "debug", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+ mwl_sysctl_debug, "I", "control debugging printfs");
+#endif
+}
+
+/*
+ * Announce various information on device/driver attach.
+ */
+static void
+mwl_announce(struct mwl_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+
+ if_printf(ifp, "Rev A%d hardware, v%d.%d.%d.%d firmware (regioncode %d)\n",
+ sc->sc_hwspecs.hwVersion,
+ (sc->sc_hwspecs.fwReleaseNumber>>24) & 0xff,
+ (sc->sc_hwspecs.fwReleaseNumber>>16) & 0xff,
+ (sc->sc_hwspecs.fwReleaseNumber>>8) & 0xff,
+ (sc->sc_hwspecs.fwReleaseNumber>>0) & 0xff,
+ sc->sc_hwspecs.regionCode);
+ sc->sc_fwrelease = sc->sc_hwspecs.fwReleaseNumber;
+
+ if (bootverbose) {
+ int i;
+ for (i = 0; i <= WME_AC_VO; i++) {
+ struct mwl_txq *txq = sc->sc_ac2q[i];
+ if_printf(ifp, "Use hw queue %u for %s traffic\n",
+ txq->qnum, ieee80211_wme_acnames[i]);
+ }
+ }
+ if (bootverbose || mwl_rxdesc != MWL_RXDESC)
+ if_printf(ifp, "using %u rx descriptors\n", mwl_rxdesc);
+ if (bootverbose || mwl_rxbuf != MWL_RXBUF)
+ if_printf(ifp, "using %u rx buffers\n", mwl_rxbuf);
+ if (bootverbose || mwl_txbuf != MWL_TXBUF)
+ if_printf(ifp, "using %u tx buffers\n", mwl_txbuf);
+ if (bootverbose && mwl_hal_ismbsscapable(sc->sc_mh))
+ if_printf(ifp, "multi-bss support\n");
+#ifdef MWL_TX_NODROP
+ if (bootverbose)
+ if_printf(ifp, "no tx drop\n");
+#endif
+}
diff --git a/sys/dev/mwl/if_mwl_pci.c b/sys/dev/mwl/if_mwl_pci.c
new file mode 100644
index 0000000000000..2fb09227d0bde
--- /dev/null
+++ b/sys/dev/mwl/if_mwl_pci.c
@@ -0,0 +1,316 @@
+/*-
+ * Copyright (c) 2007-2009 Sam Leffler, Errno Consulting
+ * Copyright (c) 2007-2009 Marvell Semiconductor, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * 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 NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ */
+
+#include <sys/cdefs.h>
+#ifdef __FreeBSD__
+__FBSDID("$FreeBSD$");
+#endif
+
+/*
+ * PCI front-end for the Marvell Wireless LAN controller driver.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/module.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/errno.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/if_arp.h>
+
+#include <net80211/ieee80211_var.h>
+
+#include <dev/mwl/if_mwlvar.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+
+/*
+ * PCI glue.
+ */
+
+struct mwl_pci_softc {
+ struct mwl_softc sc_sc;
+ struct resource *sc_sr0; /* BAR0 memory resource */
+ struct resource *sc_sr1; /* BAR1 memory resource */
+ struct resource *sc_irq; /* irq resource */
+ void *sc_ih; /* interrupt handler */
+};
+
+#define BS_BAR0 0x10
+#define BS_BAR1 0x14
+
+struct mwl_pci_ident {
+ uint16_t vendor;
+ uint16_t device;
+ const char *name;
+};
+
+static const struct mwl_pci_ident mwl_pci_ids[] = {
+ { 0x11ab, 0x2a02, "Marvell 88W8363" },
+ { 0x11ab, 0x2a03, "Marvell 88W8363" },
+ { 0x11ab, 0x2a0a, "Marvell 88W8363" },
+ { 0x11ab, 0x2a0b, "Marvell 88W8363" },
+ { 0x11ab, 0x2a0c, "Marvell 88W8363" },
+ { 0x11ab, 0x2a21, "Marvell 88W8363" },
+ { 0x11ab, 0x2a24, "Marvell 88W8363" },
+
+ { 0, 0, NULL }
+};
+
+const static struct mwl_pci_ident *
+mwl_pci_lookup(int vendor, int device)
+{
+ const struct mwl_pci_ident *ident;
+
+ for (ident = mwl_pci_ids; ident->name != NULL; ident++)
+ if (vendor == ident->vendor && device == ident->device)
+ return ident;
+ return NULL;
+}
+
+static int
+mwl_pci_probe(device_t dev)
+{
+ const struct mwl_pci_ident *ident;
+
+ ident = mwl_pci_lookup(pci_get_vendor(dev), pci_get_device(dev));
+ if (ident != NULL) {
+ device_set_desc(dev, ident->name);
+ return BUS_PROBE_DEFAULT;
+ }
+ return ENXIO;
+}
+
+static u_int32_t
+mwl_pci_setup(device_t dev)
+{
+ u_int32_t cmd;
+
+ /*
+ * Enable memory mapping and bus mastering.
+ */
+ cmd = pci_read_config(dev, PCIR_COMMAND, 4);
+ cmd |= PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN;
+ pci_write_config(dev, PCIR_COMMAND, cmd, 4);
+ cmd = pci_read_config(dev, PCIR_COMMAND, 4);
+ if ((cmd & PCIM_CMD_MEMEN) == 0) {
+ device_printf(dev, "failed to enable memory mapping\n");
+ return 0;
+ }
+ if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
+ device_printf(dev, "failed to enable bus mastering\n");
+ return 0;
+ }
+ return 1;
+}
+
+static int
+mwl_pci_attach(device_t dev)
+{
+ struct mwl_pci_softc *psc = device_get_softc(dev);
+ struct mwl_softc *sc = &psc->sc_sc;
+ int rid, error = ENXIO;
+
+ sc->sc_dev = dev;
+
+ /*
+ * Enable memory mapping and bus mastering.
+ */
+ if (!mwl_pci_setup(dev))
+ return 0;
+ /*
+ * Setup memory-mapping of PCI registers.
+ */
+ rid = BS_BAR0;
+ psc->sc_sr0 = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (psc->sc_sr0 == NULL) {
+ device_printf(dev, "cannot map BAR0 register space\n");
+ goto bad;
+ }
+ rid = BS_BAR1;
+ psc->sc_sr1 = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (psc->sc_sr1 == NULL) {
+ device_printf(dev, "cannot map BAR1 register space\n");
+ goto bad1;
+ }
+ sc->sc_invalid = 1;
+
+ /*
+ * Arrange interrupt line.
+ */
+ rid = 0;
+ psc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_SHAREABLE|RF_ACTIVE);
+ if (psc->sc_irq == NULL) {
+ device_printf(dev, "could not map interrupt\n");
+ goto bad2;
+ }
+ if (bus_setup_intr(dev, psc->sc_irq,
+ INTR_TYPE_NET | INTR_MPSAFE,
+ NULL, mwl_intr, sc, &psc->sc_ih)) {
+ device_printf(dev, "could not establish interrupt\n");
+ goto bad3;
+ }
+
+ /*
+ * Setup DMA descriptor area.
+ */
+ if (bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */
+ 1, 0, /* alignment, bounds */
+ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ BUS_SPACE_MAXADDR, /* maxsize */
+ MWL_TXDESC, /* nsegments */
+ BUS_SPACE_MAXADDR, /* maxsegsize */
+ BUS_DMA_ALLOCNOW, /* flags */
+ NULL, /* lockfunc */
+ NULL, /* lockarg */
+ &sc->sc_dmat)) {
+ device_printf(dev, "cannot allocate DMA tag\n");
+ goto bad4;
+ }
+
+ /*
+ * Finish off the attach.
+ */
+ MWL_LOCK_INIT(sc);
+ sc->sc_io0t = rman_get_bustag(psc->sc_sr0);
+ sc->sc_io0h = rman_get_bushandle(psc->sc_sr0);
+ sc->sc_io1t = rman_get_bustag(psc->sc_sr1);
+ sc->sc_io1h = rman_get_bushandle(psc->sc_sr1);
+ if (mwl_attach(pci_get_device(dev), sc) == 0)
+ return (0);
+
+ MWL_LOCK_DESTROY(sc);
+ bus_dma_tag_destroy(sc->sc_dmat);
+bad4:
+ bus_teardown_intr(dev, psc->sc_irq, psc->sc_ih);
+bad3:
+ bus_release_resource(dev, SYS_RES_IRQ, 0, psc->sc_irq);
+bad2:
+ bus_release_resource(dev, SYS_RES_MEMORY, BS_BAR1, psc->sc_sr1);
+bad1:
+ bus_release_resource(dev, SYS_RES_MEMORY, BS_BAR0, psc->sc_sr0);
+bad:
+ return (error);
+}
+
+static int
+mwl_pci_detach(device_t dev)
+{
+ struct mwl_pci_softc *psc = device_get_softc(dev);
+ struct mwl_softc *sc = &psc->sc_sc;
+
+ /* check if device was removed */
+ sc->sc_invalid = !bus_child_present(dev);
+
+ mwl_detach(sc);
+
+ bus_generic_detach(dev);
+ bus_teardown_intr(dev, psc->sc_irq, psc->sc_ih);
+ bus_release_resource(dev, SYS_RES_IRQ, 0, psc->sc_irq);
+
+ bus_dma_tag_destroy(sc->sc_dmat);
+ bus_release_resource(dev, SYS_RES_MEMORY, BS_BAR1, psc->sc_sr1);
+ bus_release_resource(dev, SYS_RES_MEMORY, BS_BAR0, psc->sc_sr0);
+
+ MWL_LOCK_DESTROY(sc);
+
+ return (0);
+}
+
+static int
+mwl_pci_shutdown(device_t dev)
+{
+ struct mwl_pci_softc *psc = device_get_softc(dev);
+
+ mwl_shutdown(&psc->sc_sc);
+ return (0);
+}
+
+static int
+mwl_pci_suspend(device_t dev)
+{
+ struct mwl_pci_softc *psc = device_get_softc(dev);
+
+ mwl_suspend(&psc->sc_sc);
+
+ return (0);
+}
+
+static int
+mwl_pci_resume(device_t dev)
+{
+ struct mwl_pci_softc *psc = device_get_softc(dev);
+
+ if (!mwl_pci_setup(dev))
+ return ENXIO;
+
+ mwl_resume(&psc->sc_sc);
+
+ return (0);
+}
+
+static device_method_t mwl_pci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, mwl_pci_probe),
+ DEVMETHOD(device_attach, mwl_pci_attach),
+ DEVMETHOD(device_detach, mwl_pci_detach),
+ DEVMETHOD(device_shutdown, mwl_pci_shutdown),
+ DEVMETHOD(device_suspend, mwl_pci_suspend),
+ DEVMETHOD(device_resume, mwl_pci_resume),
+
+ { 0,0 }
+};
+static driver_t mwl_pci_driver = {
+ "mwl",
+ mwl_pci_methods,
+ sizeof (struct mwl_pci_softc)
+};
+static devclass_t mwl_devclass;
+DRIVER_MODULE(mwl, pci, mwl_pci_driver, mwl_devclass, 0, 0);
+MODULE_VERSION(mwl, 1);
+MODULE_DEPEND(mwl, wlan, 1, 1, 1); /* 802.11 media layer */
+MODULE_DEPEND(mwl, mwlfw_fw, 1, 1, 1); /* firmware */
diff --git a/sys/dev/mwl/if_mwlioctl.h b/sys/dev/mwl/if_mwlioctl.h
new file mode 100644
index 0000000000000..e0760194c42a1
--- /dev/null
+++ b/sys/dev/mwl/if_mwlioctl.h
@@ -0,0 +1,136 @@
+/*-
+ * Copyright (c) 2007-2009 Sam Leffler, Errno Consulting
+ * Copyright (c) 2007-2009 Marvell Semiconductor, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * 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 NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Ioctl-related defintions for the Marvel Wireless LAN controller driver.
+ */
+#ifndef _DEV_MWL_MVIOCTL_H
+#define _DEV_MWL_MVIOCTL_H
+
+struct mwl_stats {
+ struct mwl_hal_hwstats hw_stats; /* XXX tied to h/w defs */
+ uint32_t mst_failure; /* generic hardware failure */
+ uint32_t mst_rx_badtkipicv;
+ uint32_t mst_tx_discard;
+ uint32_t mst_tx_qstop;
+ uint32_t mst_tx_encap;
+ uint32_t mst_tx_mgmt;
+ uint32_t mst_rx_nombuf;
+ uint32_t mst_rx_busdma;
+ uint32_t mst_rx_tooshort;
+ uint32_t mst_tx_busdma;
+ uint32_t mst_tx_linear;
+ uint32_t mst_tx_nombuf;
+ uint32_t mst_tx_nodata;
+ uint32_t mst_tx_shortpre;
+ uint32_t mst_tx_retries;
+ uint32_t mst_tx_mretries;
+ uint32_t mst_tx_linkerror;
+ uint32_t mst_tx_xretries;
+ uint32_t mst_tx_aging;
+ uint32_t mst_tx_qdrop;
+ uint32_t mst_ff_txerr;
+ uint32_t mst_watchdog;
+ uint32_t mst_tx_packets;
+ uint32_t mst_rx_packets;
+ int8_t mst_rx_rssi;
+ int8_t mst_rx_noise;
+ uint8_t mst_tx_rate;
+ uint32_t mst_ant_tx[4];
+ uint32_t mst_ant_rx[4];
+ uint32_t mst_tx_tso;
+ uint32_t mst_tso_badeth;
+ uint32_t mst_tso_nohdr;
+ uint32_t mst_tso_badsplit;
+ uint32_t mst_rx_crypto;
+ uint32_t mst_rx_tkipmic;
+ uint32_t mst_rx_nodmabuf;
+ uint32_t mst_tx_noheadroom;
+ uint32_t mst_tx_badframetype;
+ uint32_t mst_ampdu_nostream;
+ uint32_t mst_ampdu_reject;
+ uint32_t mst_addba_nostream;
+ uint32_t mst_bacreate_failed;
+ uint32_t mst_bawatchdog;
+ uint32_t mst_radardetect;
+ uint32_t mst_rx_dmabufmissing;
+ uint32_t mst_bawatchdog_notfound;
+ uint32_t mst_bawatchdog_empty;
+ uint32_t mst_bawatchdog_failed;
+ uint32_t mst_rxbuf_failed;
+ uint32_t mst_pad[31];
+};
+
+#define SIOCGMVSTATS _IOWR('i', 137, struct ifreq)
+
+/*
+ * Radio capture format.
+ */
+#define MWL_RX_RADIOTAP_PRESENT ( \
+ (1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_RATE) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL) | \
+ (1 << IEEE80211_RADIOTAP_ANTENNA) | \
+ (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \
+ (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | \
+ 0)
+
+struct mwl_rx_radiotap_header {
+ struct ieee80211_radiotap_header wr_ihdr;
+ u_int8_t wr_flags;
+ u_int8_t wr_rate;
+ u_int16_t wr_chan_freq;
+ u_int16_t wr_chan_flags;
+ int8_t wr_antsignal;
+ int8_t wr_antnoise;
+ u_int8_t wr_antenna;
+};
+
+#define MWL_TX_RADIOTAP_PRESENT ( \
+ (1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_RATE) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL) | \
+ (1 << IEEE80211_RADIOTAP_DBM_TX_POWER) | \
+ (1 << IEEE80211_RADIOTAP_ANTENNA) | \
+ 0)
+
+struct mwl_tx_radiotap_header {
+ struct ieee80211_radiotap_header wt_ihdr;
+ u_int8_t wt_flags;
+ u_int8_t wt_rate;
+ u_int16_t wt_chan_freq;
+ u_int16_t wt_chan_flags;
+ u_int8_t wt_txpower;
+ u_int8_t wt_antenna;
+};
+
+#endif /* _DEV_MWL_MVIOCTL_H */
diff --git a/sys/dev/mwl/if_mwlvar.h b/sys/dev/mwl/if_mwlvar.h
new file mode 100644
index 0000000000000..6730ef6d51bc7
--- /dev/null
+++ b/sys/dev/mwl/if_mwlvar.h
@@ -0,0 +1,354 @@
+/*-
+ * Copyright (c) 2007-2009 Sam Leffler, Errno Consulting
+ * Copyright (c) 2007-2009 Marvell Semiconductor, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * 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 NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Definitions for the Marvell 88W8363 Wireless LAN controller.
+ */
+#ifndef _DEV_MWL_MVVAR_H
+#define _DEV_MWL_MVVAR_H
+
+#include <sys/endian.h>
+#include <net80211/ieee80211_radiotap.h>
+#include <dev/mwl/mwlhal.h>
+#include <dev/mwl/mwlreg.h>
+#include <dev/mwl/if_mwlioctl.h>
+
+#ifndef MWL_TXBUF
+#define MWL_TXBUF 256 /* number of TX descriptors/buffers */
+#endif
+#ifndef MWL_RXDESC
+#define MWL_RXDESC 256 /* number of RX descriptors */
+#endif
+#ifndef MWL_RXBUF
+#define MWL_RXBUF ((5*MWL_RXDESC)/2)/* number of RX dma buffers */
+#endif
+#ifndef MWL_MAXBA
+#define MWL_MAXBA 2 /* max BA streams/sta */
+#endif
+
+#ifdef MWL_SGDMA_SUPPORT
+#define MWL_TXDESC 6 /* max tx descriptors/segments */
+#else
+#define MWL_TXDESC 1 /* max tx descriptors/segments */
+#endif
+#ifndef MWL_AGGR_SIZE
+#define MWL_AGGR_SIZE 3839 /* max tx agregation size */
+#endif
+#define MWL_AGEINTERVAL 1 /* poke f/w every sec to age q's */
+#define MWL_MAXSTAID 64 /* max of 64 stations */
+
+/*
+ * DMA state for tx/rx descriptors.
+ */
+
+/*
+ * Software backed version of tx/rx descriptors. We keep
+ * the software state out of the h/w descriptor structure
+ * so that may be allocated in uncached memory w/o paying
+ * performance hit.
+ */
+struct mwl_txbuf {
+ STAILQ_ENTRY(mwl_txbuf) bf_list;
+ void *bf_desc; /* h/w descriptor */
+ bus_addr_t bf_daddr; /* physical addr of desc */
+ bus_dmamap_t bf_dmamap; /* DMA map for descriptors */
+ int bf_nseg;
+ bus_dma_segment_t bf_segs[MWL_TXDESC];
+ struct mbuf *bf_m;
+ struct ieee80211_node *bf_node;
+ struct mwl_txq *bf_txq; /* backpointer to tx q/ring */
+};
+typedef STAILQ_HEAD(, mwl_txbuf) mwl_txbufhead;
+
+/*
+ * Common "base class" for tx/rx descriptor resources
+ * allocated using the bus dma api.
+ */
+struct mwl_descdma {
+ const char* dd_name;
+ void *dd_desc; /* descriptors */
+ bus_addr_t dd_desc_paddr; /* physical addr of dd_desc */
+ bus_size_t dd_desc_len; /* size of dd_desc */
+ bus_dma_segment_t dd_dseg;
+ int dd_dnseg; /* number of segments */
+ bus_dma_tag_t dd_dmat; /* bus DMA tag */
+ bus_dmamap_t dd_dmamap; /* DMA map for descriptors */
+ void *dd_bufptr; /* associated buffers */
+};
+
+/*
+ * TX/RX ring definitions. There are 4 tx rings, one
+ * per AC, and 1 rx ring. Note carefully that transmit
+ * descriptors are treated as a contiguous chunk and the
+ * firmware pre-fetches descriptors. This means that we
+ * must preserve order when moving descriptors between
+ * the active+free lists; otherwise we may stall transmit.
+ */
+struct mwl_txq {
+ struct mwl_descdma dma; /* bus dma resources */
+ struct mtx lock; /* tx q lock */
+ char name[12]; /* e.g. "mwl0_txq4" */
+ int qnum; /* f/w q number */
+ int txpri; /* f/w tx priority */
+ int nfree; /* # buffers on free list */
+ mwl_txbufhead free; /* queue of free buffers */
+ mwl_txbufhead active; /* queue of active buffers */
+};
+
+#define MWL_TXQ_LOCK_INIT(_sc, _tq) do { \
+ snprintf((_tq)->name, sizeof((_tq)->name), "%s_txq%u", \
+ device_get_nameunit((_sc)->sc_dev), (_tq)->qnum); \
+ mtx_init(&(_tq)->lock, (_tq)->name, NULL, MTX_DEF); \
+} while (0)
+#define MWL_TXQ_LOCK_DESTROY(_tq) mtx_destroy(&(_tq)->lock)
+#define MWL_TXQ_LOCK(_tq) mtx_lock(&(_tq)->lock)
+#define MWL_TXQ_UNLOCK(_tq) mtx_unlock(&(_tq)->lock)
+#define MWL_TXQ_LOCK_ASSERT(_tq) mtx_assert(&(_tq)->lock, MA_OWNED)
+
+#define MWL_TXDESC_SYNC(txq, ds, how) do { \
+ bus_dmamap_sync((txq)->dma.dd_dmat, (txq)->dma.dd_dmamap, how); \
+} while(0)
+
+/*
+ * RX dma buffers that are not in use are kept on a list.
+ */
+struct mwl_jumbo {
+ SLIST_ENTRY(mwl_jumbo) next;
+};
+typedef SLIST_HEAD(, mwl_jumbo) mwl_jumbohead;
+
+#define MWL_JUMBO_DATA2BUF(_data) ((struct mwl_jumbo *)(_data))
+#define MWL_JUMBO_BUF2DATA(_buf) ((uint8_t *)(_buf))
+#define MWL_JUMBO_OFFSET(_sc, _data) \
+ (((const uint8_t *)(_data)) - (const uint8_t *)((_sc)->sc_rxmem))
+#define MWL_JUMBO_DMA_ADDR(_sc, _data) \
+ ((_sc)->sc_rxmem_paddr + MWL_JUMBO_OFFSET(_sc, _data))
+
+struct mwl_rxbuf {
+ STAILQ_ENTRY(mwl_rxbuf) bf_list;
+ void *bf_desc; /* h/w descriptor */
+ bus_addr_t bf_daddr; /* physical addr of desc */
+ uint8_t *bf_data; /* rx data area */
+};
+typedef STAILQ_HEAD(, mwl_rxbuf) mwl_rxbufhead;
+
+#define MWL_RXDESC_SYNC(sc, ds, how) do { \
+ bus_dmamap_sync((sc)->sc_rxdma.dd_dmat, (sc)->sc_rxdma.dd_dmamap, how);\
+} while (0)
+
+/*
+ * BA stream state. One of these is setup for each stream
+ * allocated/created for use. We pre-allocate the h/w stream
+ * before sending ADDBA request then complete the setup when
+ * get ADDBA response (success). The completed state is setup
+ * to optimize the fast path in mwl_txstart--we precalculate
+ * the QoS control bits in the outbound frame and use those
+ * to identify which BA stream to use (assigning the h/w q to
+ * the TxPriority field of the descriptor).
+ *
+ * NB: Each station may have at most MWL_MAXBA streams at one time.
+ */
+struct mwl_bastate {
+ uint16_t qos; /* QoS ctl for BA stream */
+ uint8_t txq; /* h/w q for BA stream */
+ const MWL_HAL_BASTREAM *bastream; /* A-MPDU BA stream */
+};
+
+static __inline__ void
+mwl_bastream_setup(struct mwl_bastate *bas, int ac, int txq)
+{
+ bas->txq = txq;
+ bas->qos = htole16(WME_AC_TO_TID(ac) | IEEE80211_QOS_ACKPOLICY_BA);
+}
+
+static __inline__ void
+mwl_bastream_free(struct mwl_bastate *bas)
+{
+ bas->qos = 0;
+ bas->bastream = NULL;
+ /* NB: don't need to clear txq */
+}
+
+/*
+ * Check the QoS control bits from an outbound frame against the
+ * value calculated when a BA stream is setup (above). We need
+ * to match the TID and also the ACK policy so we only match AMPDU
+ * frames. The bits from the frame are assumed in network byte
+ * order, hence the potential byte swap.
+ */
+static __inline__ int
+mwl_bastream_match(const struct mwl_bastate *bas, uint16_t qos)
+{
+ return (qos & htole16(IEEE80211_QOS_TID|IEEE80211_QOS_ACKPOLICY)) ==
+ bas->qos;
+}
+
+/* driver-specific node state */
+struct mwl_node {
+ struct ieee80211_node mn_node; /* base class */
+ struct mwl_ant_info mn_ai; /* antenna info */
+ uint32_t mn_avgrssi; /* average rssi over all rx frames */
+ uint16_t mn_staid; /* firmware station id */
+ struct mwl_bastate mn_ba[MWL_MAXBA];
+ struct mwl_hal_vap *mn_hvap; /* hal vap handle */
+};
+#define MWL_NODE(ni) ((struct mwl_node *)(ni))
+#define MWL_NODE_CONST(ni) ((const struct mwl_node *)(ni))
+
+/*
+ * Driver-specific vap state.
+ */
+struct mwl_vap {
+ struct ieee80211vap mv_vap; /* base class */
+ struct mwl_hal_vap *mv_hvap; /* hal vap handle */
+ struct mwl_hal_vap *mv_ap_hvap; /* ap hal vap handle for wds */
+ uint16_t mv_last_ps_sta; /* last count of ps sta's */
+ uint16_t mv_eapolformat; /* fixed tx rate for EAPOL */
+ int (*mv_newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+ int (*mv_set_tim)(struct ieee80211_node *, int);
+};
+#define MWL_VAP(vap) ((struct mwl_vap *)(vap))
+#define MWL_VAP_CONST(vap) ((const struct mwl_vap *)(vap))
+
+struct mwl_softc {
+ struct ifnet *sc_ifp; /* interface common */
+ struct mwl_stats sc_stats; /* interface statistics */
+ int sc_debug;
+ device_t sc_dev;
+ bus_dma_tag_t sc_dmat; /* bus DMA tag */
+ bus_space_handle_t sc_io0h; /* BAR 0 */
+ bus_space_tag_t sc_io0t;
+ bus_space_handle_t sc_io1h; /* BAR 1 */
+ bus_space_tag_t sc_io1t;
+ struct mtx sc_mtx; /* master lock (recursive) */
+ struct taskqueue *sc_tq; /* private task queue */
+ unsigned int sc_invalid : 1, /* disable hardware accesses */
+ sc_recvsetup:1, /* recv setup */
+ sc_csapending:1,/* 11h channel switch pending */
+ sc_radarena : 1,/* radar detection enabled */
+ sc_rxblocked: 1;/* rx waiting for dma buffers */
+
+ struct mwl_hal *sc_mh; /* h/w access layer */
+ struct mwl_hal_vap *sc_hvap; /* hal vap handle */
+ struct mwl_hal_hwspec sc_hwspecs; /* h/w capabilities */
+ uint32_t sc_fwrelease; /* release # of loaded f/w */
+ struct mwl_hal_txrxdma sc_hwdma; /* h/w dma setup */
+ uint32_t sc_imask; /* interrupt mask copy */
+ enum ieee80211_phymode sc_curmode;
+ u_int16_t sc_curaid; /* current association id */
+ u_int8_t sc_curbssid[IEEE80211_ADDR_LEN];
+ MWL_HAL_CHANNEL sc_curchan;
+ MWL_HAL_TXRATE_HANDLING sc_txratehandling;
+ u_int16_t sc_rxantenna; /* rx antenna */
+ u_int16_t sc_txantenna; /* tx antenna */
+ uint8_t sc_napvaps; /* # ap mode vaps */
+ uint8_t sc_nwdsvaps; /* # wds mode vaps */
+ uint8_t sc_nstavaps; /* # sta mode vaps */
+ uint8_t sc_nbssid0; /* # vap's using base mac */
+ uint32_t sc_bssidmask; /* bssid mask */
+
+ void (*sc_recv_mgmt)(struct ieee80211com *,
+ struct mbuf *,
+ struct ieee80211_node *,
+ int, int, int, u_int32_t);
+ int (*sc_newstate)(struct ieee80211com *,
+ enum ieee80211_state, int);
+ void (*sc_node_cleanup)(struct ieee80211_node *);
+ void (*sc_node_drain)(struct ieee80211_node *);
+ void (*sc_recv_action)(struct ieee80211_node *,
+ const uint8_t *, const uint8_t *);
+ int (*sc_addba_request)(struct ieee80211_node *,
+ struct ieee80211_tx_ampdu *,
+ int dialogtoken, int baparamset,
+ int batimeout);
+ int (*sc_addba_response)(struct ieee80211_node *,
+ struct ieee80211_tx_ampdu *,
+ int status, int baparamset,
+ int batimeout);
+ void (*sc_addba_stop)(struct ieee80211_node *,
+ struct ieee80211_tx_ampdu *);
+
+ struct mwl_descdma sc_rxdma; /* rx bus dma resources */
+ mwl_rxbufhead sc_rxbuf; /* rx buffers */
+ struct mwl_rxbuf *sc_rxnext; /* next rx buffer to process */
+ struct task sc_rxtask; /* rx int processing */
+ void *sc_rxmem; /* rx dma buffer pool */
+ bus_dma_tag_t sc_rxdmat; /* rx bus DMA tag */
+ bus_size_t sc_rxmemsize; /* rx dma buffer pool size */
+ bus_dmamap_t sc_rxmap; /* map for rx dma buffers */
+ bus_addr_t sc_rxmem_paddr; /* physical addr of sc_rxmem */
+ mwl_jumbohead sc_rxfree; /* list of free dma buffers */
+ int sc_nrxfree; /* # buffers on rx free list */
+ struct mtx sc_rxlock; /* lock on sc_rxfree */
+
+ struct mwl_txq sc_txq[MWL_NUM_TX_QUEUES];
+ struct mwl_txq *sc_ac2q[5]; /* WME AC -> h/w q map */
+ struct mbuf *sc_aggrq; /* aggregation q */
+ struct task sc_txtask; /* tx int processing */
+ struct task sc_bawatchdogtask;/* BA watchdog processing */
+
+ struct task sc_radartask; /* radar detect processing */
+ struct task sc_chanswitchtask;/* chan switch processing */
+
+ uint8_t sc_staid[MWL_MAXSTAID/NBBY];
+ int sc_ageinterval;
+ struct callout sc_timer; /* periodic work */
+
+ struct mwl_tx_radiotap_header sc_tx_th;
+ struct mwl_rx_radiotap_header sc_rx_th;
+};
+
+#define MWL_LOCK_INIT(_sc) \
+ mtx_init(&(_sc)->sc_mtx, device_get_nameunit((_sc)->sc_dev), \
+ NULL, MTX_DEF | MTX_RECURSE)
+#define MWL_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx)
+#define MWL_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define MWL_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#define MWL_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
+
+#define MWL_RXFREE_INIT(_sc) \
+ mtx_init(&(_sc)->sc_rxlock, device_get_nameunit((_sc)->sc_dev), \
+ NULL, MTX_DEF)
+#define MWL_RXFREE_DESTROY(_sc) mtx_destroy(&(_sc)->sc_rxlock)
+#define MWL_RXFREE_LOCK(_sc) mtx_lock(&(_sc)->sc_rxlock)
+#define MWL_RXFREE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_rxlock)
+#define MWL_RXFREE_ASSERT(_sc) mtx_assert(&(_sc)->sc_rxlock, MA_OWNED)
+
+int mwl_attach(u_int16_t, struct mwl_softc *);
+int mwl_detach(struct mwl_softc *);
+void mwl_resume(struct mwl_softc *);
+void mwl_suspend(struct mwl_softc *);
+void mwl_shutdown(void *);
+void mwl_intr(void *);
+
+#endif /* _DEV_MWL_MVVAR_H */
diff --git a/sys/dev/mwl/mwldiag.h b/sys/dev/mwl/mwldiag.h
new file mode 100644
index 0000000000000..65c8eb5941ddb
--- /dev/null
+++ b/sys/dev/mwl/mwldiag.h
@@ -0,0 +1,108 @@
+/*-
+ * Copyright (c) 2007-2009 Sam Leffler, Errno Consulting
+ * Copyright (c) 2007-2009 Marvell Semiconductor, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * 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 NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MWL_DIAG_H_
+#define _MWL_DIAG_H_
+/*
+ * Diagnostic interface. This is an open-ended interface that
+ * is opaque to applications. Diagnostic programs use this to
+ * retrieve internal data structures, etc. There is no guarantee
+ * that calling conventions for calls other than MWL_DIAG_REVS
+ * are stable between HAL releases; a diagnostic application must
+ * use the HAL revision information to deal with ABI/API differences.
+ *
+ * NB: do not renumber these, certain codes are publicly used.
+ */
+enum {
+ MWL_DIAG_CMD_REVS = 0, /* MAC/PHY/Radio revs */
+ MWL_DIAG_CMD_REGS = 1, /* Registers */
+ MWL_DIAG_CMD_HOSTCMD = 2, /* issue arbitrary cmd */
+ MWL_DIAG_CMD_FWLOAD = 3, /* load firmware */
+};
+
+/*
+ * Device revision information.
+ */
+typedef struct {
+ uint16_t mh_devid; /* PCI device ID */
+ uint16_t mh_subvendorid; /* PCI subvendor ID */
+ uint16_t mh_macRev; /* MAC revision */
+ uint16_t mh_phyRev; /* PHY revision */
+} MWL_DIAG_REVS;
+
+typedef struct {
+ uint16_t start; /* first register */
+ uint16_t end; /* ending register or zero */
+} MWL_DIAG_REGRANGE;
+
+/*
+ * Registers are mapped into virtual banks; the hal converts
+ * r/w operations through the diag api to host cmds as required.
+ *
+ * NB: register offsets are 16-bits and we need to avoid real
+ * register mappings in BAR1.
+ */
+#define MWL_DIAG_BASE_MAC 0xa000
+#define MWL_DIAG_ISMAC(r) \
+ (MWL_DIAG_BASE_MAC <= (r) && (r) < (MWL_DIAG_BASE_MAC+0x1000))
+#define MWL_DIAG_BASE_BB 0xe000
+#define MWL_DIAG_ISBB(r) \
+ (MWL_DIAG_BASE_BB <= (r) && (r) < (MWL_DIAG_BASE_BB+0x1000))
+#define MWL_DIAG_BASE_RF 0xf000
+#define MWL_DIAG_ISRF(r) \
+ (MWL_DIAG_BASE_RF <= (r) && (r) < (MWL_DIAG_BASE_RF+0x1000))
+
+/*
+ * Firmware download
+ */
+typedef struct {
+ uint32_t opmode; /* operating mode */
+ uint32_t signature; /* f/w ready signature */
+ char name[1]; /* variable length pathname */
+} MWL_DIAG_FWLOAD;
+
+struct mwl_diag {
+ char md_name[IFNAMSIZ]; /* if name, e.g. "mv0" */
+ uint16_t md_id;
+#define MWL_DIAG_DYN 0x8000 /* allocate buffer in caller */
+#define MWL_DIAG_IN 0x4000 /* copy in parameters */
+#define MWL_DIAG_OUT 0x0000 /* copy out results (always) */
+#define MWL_DIAG_ID 0x0fff
+ uint16_t md_in_size; /* pack to fit, yech */
+ void * md_in_data;
+ void * md_out_data;
+ u_int md_out_size;
+
+};
+#define SIOCGMVDIAG _IOWR('i', 138, struct mwl_diag)
+#define SIOCGMVRESET _IOW('i', 139, struct mwl_diag)
+#endif /* _MWL_DIAG_H_ */
diff --git a/sys/dev/mwl/mwlhal.c b/sys/dev/mwl/mwlhal.c
new file mode 100644
index 0000000000000..0ffa0b1bc1978
--- /dev/null
+++ b/sys/dev/mwl/mwlhal.c
@@ -0,0 +1,2703 @@
+/*-
+ * Copyright (c) 2007-2009 Sam Leffler, Errno Consulting
+ * Copyright (c) 2007-2009 Marvell Semiconductor, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * 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 NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/malloc.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/kernel.h>
+#include <sys/errno.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+
+#include <sys/linker.h>
+#include <sys/firmware.h>
+
+#include <machine/bus.h>
+
+#include <dev/mwl/mwlhal.h>
+#include <dev/mwl/mwlreg.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <dev/mwl/mwldiag.h>
+
+#define MWLHAL_DEBUG /* debug msgs */
+
+typedef enum {
+ WL_ANTENNAMODE_RX = 0xffff,
+ WL_ANTENNAMODE_TX = 2,
+} wlantennamode_e;
+
+typedef enum {
+ WL_TX_POWERLEVEL_LOW = 5,
+ WL_TX_POWERLEVEL_MEDIUM = 10,
+ WL_TX_POWERLEVEL_HIGH = 15,
+} wltxpowerlevel_e;
+
+#define MWL_CMDBUF_SIZE 0x4000 /* size of f/w command buffer */
+#define MWL_BASTREAMS_MAX 7 /* max BA streams (NB: fw >3.3.5.9) */
+#define MWL_BAQID_MAX 8 /* max BA Q id's (NB: fw >3.3.5.9) */
+#define MWL_MBSS_AP_MAX 8 /* max ap vap's */
+#define MWL_MBSS_STA_MAX 24 /* max station/client vap's */
+#define MWL_MBSS_MAX (MWL_MBSS_AP_MAX+MWL_MBSS_STA_MAX)
+
+/*
+ * BA stream -> queue ID mapping
+ *
+ * The first 2 streams map to h/w; the remaining streams are
+ * implemented in firmware.
+ */
+static const int ba2qid[MWL_BASTREAMS_MAX] = {
+ 5, 6 /* h/w supported */
+#if MWL_BASTREAMS_MAX == 7
+ , 7, 0, 1, 2, 3 /* f/w supported */
+#endif
+};
+static int qid2ba[MWL_BAQID_MAX];
+
+#define IEEE80211_ADDR_LEN 6 /* XXX */
+#define IEEE80211_ADDR_COPY(_dst, _src) \
+ memcpy(_dst, _src, IEEE80211_ADDR_LEN)
+#define IEEE80211_ADDR_EQ(_dst, _src) \
+ (memcmp(_dst, _src, IEEE80211_ADDR_LEN) == 0)
+
+#define _CMD_SETUP(pCmd, type, cmd) do { \
+ pCmd = (type *)&mh->mh_cmdbuf[0]; \
+ memset(pCmd, 0, sizeof(type)); \
+ pCmd->CmdHdr.Cmd = htole16(cmd); \
+ pCmd->CmdHdr.Length = htole16(sizeof(type)); \
+} while (0)
+
+#define _VCMD_SETUP(vap, pCmd, type, cmd) do { \
+ _CMD_SETUP(pCmd, type, cmd); \
+ pCmd->CmdHdr.MacId = vap->macid; \
+} while (0)
+
+#define PWTAGETRATETABLE20M 14*4
+#define PWTAGETRATETABLE40M 9*4
+#define PWTAGETRATETABLE20M_5G 35*4
+#define PWTAGETRATETABLE40M_5G 16*4
+
+struct mwl_hal_bastream {
+ MWL_HAL_BASTREAM public; /* public state */
+ uint8_t stream; /* stream # */
+ uint8_t setup; /* f/w cmd sent */
+ uint8_t ba_type;
+ uint8_t tid;
+ uint8_t paraminfo;
+ uint8_t macaddr[IEEE80211_ADDR_LEN];
+};
+
+struct mwl_hal_priv;
+
+struct mwl_hal_vap {
+ struct mwl_hal_priv *mh; /* back pointer */
+ uint16_t bss_type; /* f/w type */
+ uint8_t vap_type; /* MWL_HAL_BSSTYPE */
+ uint8_t macid; /* for passing to f/w */
+ uint8_t flags;
+#define MVF_RUNNING 0x01 /* BSS_START issued */
+#define MVF_STATION 0x02 /* sta db entry created */
+ uint8_t mac[IEEE80211_ADDR_LEN];/* mac address */
+};
+#define MWLVAP(_vap) ((_vap)->mh)
+
+/*
+ * Per-device state. We allocate a single cmd buffer for
+ * submitting operations to the firmware. Access to this
+ * buffer (and the f/w) are single-threaded. At present
+ * we spin waiting for cmds to complete which is bad. Not
+ * sure if it's possible to submit multiple requests or
+ * control when we get cmd done interrupts. There's no
+ * documentation and no example code to indicate what can
+ * or cannot be done so all we can do right now is follow the
+ * linux driver logic. This falls apart when the f/w fails;
+ * the system comes to a crawl as we spin waiting for operations
+ * to finish.
+ */
+struct mwl_hal_priv {
+ struct mwl_hal public; /* public area */
+ device_t mh_dev;
+ char mh_mtxname[12];
+ struct mtx mh_mtx;
+ bus_dma_tag_t mh_dmat; /* bus DMA tag for cmd buffer */
+ bus_dma_segment_t mh_seg; /* segment for cmd buffer */
+ bus_dmamap_t mh_dmamap; /* DMA map for cmd buffer */
+ uint16_t *mh_cmdbuf; /* f/w cmd buffer */
+ bus_addr_t mh_cmdaddr; /* physaddr of cmd buffer */
+ int mh_flags;
+#define MHF_CALDATA 0x0001 /* cal data retrieved */
+#define MHF_FWHANG 0x0002 /* fw appears hung */
+#define MHF_MBSS 0x0004 /* mbss enabled */
+ struct mwl_hal_vap mh_vaps[MWL_MBSS_MAX+1];
+ int mh_bastreams; /* bit mask of available BA streams */
+ int mh_regioncode; /* XXX last region code sent to fw */
+ struct mwl_hal_bastream mh_streams[MWL_BASTREAMS_MAX];
+ int mh_debug;
+ MWL_HAL_CHANNELINFO mh_20M;
+ MWL_HAL_CHANNELINFO mh_40M;
+ MWL_HAL_CHANNELINFO mh_20M_5G;
+ MWL_HAL_CHANNELINFO mh_40M_5G;
+ int mh_SDRAMSIZE_Addr;
+ uint32_t mh_RTSSuccesses;/* cumulative stats for read-on-clear */
+ uint32_t mh_RTSFailures;
+ uint32_t mh_RxDuplicateFrames;
+ uint32_t mh_FCSErrorCount;
+ MWL_DIAG_REVS mh_revs;
+};
+#define MWLPRIV(_mh) ((struct mwl_hal_priv *)(_mh))
+
+static int mwl_hal_setmac_locked(struct mwl_hal_vap *,
+ const uint8_t addr[IEEE80211_ADDR_LEN]);
+static int mwlExecuteCmd(struct mwl_hal_priv *, unsigned short cmd);
+static int mwlGetPwrCalTable(struct mwl_hal_priv *);
+#ifdef MWLHAL_DEBUG
+static const char *mwlcmdname(int cmd);
+static void dumpresult(struct mwl_hal_priv *, int showresult);
+#endif /* MWLHAL_DEBUG */
+
+SYSCTL_DECL(_hw_mwl);
+SYSCTL_NODE(_hw_mwl, OID_AUTO, hal, CTLFLAG_RD, 0, "Marvell HAL parameters");
+
+static __inline void
+MWL_HAL_LOCK(struct mwl_hal_priv *mh)
+{
+ mtx_lock(&mh->mh_mtx);
+}
+
+static __inline void
+MWL_HAL_LOCK_ASSERT(struct mwl_hal_priv *mh)
+{
+ mtx_assert(&mh->mh_mtx, MA_OWNED);
+}
+
+static __inline void
+MWL_HAL_UNLOCK(struct mwl_hal_priv *mh)
+{
+ mtx_unlock(&mh->mh_mtx);
+}
+
+static __inline uint32_t
+RD4(struct mwl_hal_priv *mh, bus_size_t off)
+{
+ return bus_space_read_4(mh->public.mh_iot, mh->public.mh_ioh, off);
+}
+
+static __inline void
+WR4(struct mwl_hal_priv *mh, bus_size_t off, uint32_t val)
+{
+ bus_space_write_4(mh->public.mh_iot, mh->public.mh_ioh, off, val);
+}
+
+static void
+mwl_hal_load_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
+{
+ bus_addr_t *paddr = (bus_addr_t*) arg;
+ KASSERT(error == 0, ("error %u on bus_dma callback", error));
+ *paddr = segs->ds_addr;
+}
+
+/*
+ * Setup for communication with the device. We allocate
+ * a command buffer and map it for bus dma use. The pci
+ * device id is used to identify whether the device has
+ * SRAM on it (in which case f/w download must include a
+ * memory controller reset). All bus i/o operations happen
+ * in BAR 1; the driver passes in the tag and handle we need.
+ */
+struct mwl_hal *
+mwl_hal_attach(device_t dev, uint16_t devid,
+ bus_space_handle_t ioh, bus_space_tag_t iot, bus_dma_tag_t tag)
+{
+ struct mwl_hal_priv *mh;
+ struct mwl_hal_vap *hvap;
+ int error, i;
+
+ mh = malloc(sizeof(struct mwl_hal_priv), M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (mh == NULL)
+ return NULL;
+ mh->mh_dev = dev;
+ mh->public.mh_ioh = ioh;
+ mh->public.mh_iot = iot;
+ for (i = 0; i < MWL_BASTREAMS_MAX; i++) {
+ mh->mh_streams[i].public.txq = ba2qid[i];
+ mh->mh_streams[i].stream = i;
+ /* construct back-mapping while we're at it */
+ if (mh->mh_streams[i].public.txq < MWL_BAQID_MAX)
+ qid2ba[mh->mh_streams[i].public.txq] = i;
+ else
+ device_printf(dev, "unexpected BA tx qid %d for "
+ "stream %d\n", mh->mh_streams[i].public.txq, i);
+ }
+ /* setup constant portion of vap state */
+ /* XXX should get max ap/client vap's from f/w */
+ i = 0;
+ hvap = &mh->mh_vaps[i];
+ hvap->vap_type = MWL_HAL_AP;
+ hvap->bss_type = htole16(WL_MAC_TYPE_PRIMARY_AP);
+ hvap->macid = 0;
+ for (i++; i < MWL_MBSS_AP_MAX; i++) {
+ hvap = &mh->mh_vaps[i];
+ hvap->vap_type = MWL_HAL_AP;
+ hvap->bss_type = htole16(WL_MAC_TYPE_SECONDARY_AP);
+ hvap->macid = i;
+ }
+ hvap = &mh->mh_vaps[i];
+ hvap->vap_type = MWL_HAL_STA;
+ hvap->bss_type = htole16(WL_MAC_TYPE_PRIMARY_CLIENT);
+ hvap->macid = i;
+ for (i++; i < MWL_MBSS_STA_MAX; i++) {
+ hvap = &mh->mh_vaps[i];
+ hvap->vap_type = MWL_HAL_STA;
+ hvap->bss_type = htole16(WL_MAC_TYPE_SECONDARY_CLIENT);
+ hvap->macid = i;
+ }
+ mh->mh_revs.mh_devid = devid;
+ snprintf(mh->mh_mtxname, sizeof(mh->mh_mtxname),
+ "%s_hal", device_get_nameunit(dev));
+ mtx_init(&mh->mh_mtx, mh->mh_mtxname, NULL, MTX_DEF);
+
+ /*
+ * Allocate the command buffer and map into the address
+ * space of the h/w. We request "coherent" memory which
+ * will be uncached on some architectures.
+ */
+ error = bus_dma_tag_create(tag, /* parent */
+ PAGE_SIZE, 0, /* alignment, bounds */
+ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ MWL_CMDBUF_SIZE, /* maxsize */
+ 1, /* nsegments */
+ MWL_CMDBUF_SIZE, /* maxsegsize */
+ BUS_DMA_ALLOCNOW, /* flags */
+ NULL, /* lockfunc */
+ NULL, /* lockarg */
+ &mh->mh_dmat);
+ if (error != 0) {
+ device_printf(dev, "unable to allocate memory for cmd buffer, "
+ "error %u\n", error);
+ goto fail0;
+ }
+
+ /* allocate descriptors */
+ error = bus_dmamap_create(mh->mh_dmat, BUS_DMA_NOWAIT, &mh->mh_dmamap);
+ if (error != 0) {
+ device_printf(dev, "unable to create dmamap for cmd buffers, "
+ "error %u\n", error);
+ goto fail0;
+ }
+
+ error = bus_dmamem_alloc(mh->mh_dmat, (void**) &mh->mh_cmdbuf,
+ BUS_DMA_NOWAIT | BUS_DMA_COHERENT,
+ &mh->mh_dmamap);
+ if (error != 0) {
+ device_printf(dev, "unable to allocate memory for cmd buffer, "
+ "error %u\n", error);
+ goto fail1;
+ }
+
+ error = bus_dmamap_load(mh->mh_dmat, mh->mh_dmamap,
+ mh->mh_cmdbuf, MWL_CMDBUF_SIZE,
+ mwl_hal_load_cb, &mh->mh_cmdaddr,
+ BUS_DMA_NOWAIT);
+ if (error != 0) {
+ device_printf(dev, "unable to load cmd buffer, error %u\n",
+ error);
+ goto fail2;
+ }
+
+ /*
+ * Some cards have SDRAM. When loading firmware we need
+ * to reset the SDRAM controller prior to doing this.
+ * When the SDRAMSIZE is non-zero we do that work in
+ * mwl_hal_fwload.
+ */
+ switch (devid) {
+ case 0x2a02: /* CB82 */
+ case 0x2a03: /* CB85 */
+ case 0x2a08: /* MC85_B1 */
+ case 0x2a0b: /* CB85AP */
+ case 0x2a24:
+ mh->mh_SDRAMSIZE_Addr = 0x40fe70b7; /* 8M SDRAM */
+ break;
+ case 0x2a04: /* MC85 */
+ mh->mh_SDRAMSIZE_Addr = 0x40fc70b7; /* 16M SDRAM */
+ break;
+ default:
+ break;
+ }
+ return &mh->public;
+fail2:
+ bus_dmamem_free(mh->mh_dmat, mh->mh_cmdbuf, mh->mh_dmamap);
+fail1:
+ bus_dmamap_destroy(mh->mh_dmat, mh->mh_dmamap);
+fail0:
+ bus_dma_tag_destroy(mh->mh_dmat);
+ mtx_destroy(&mh->mh_mtx);
+ free(mh, M_DEVBUF);
+ return NULL;
+}
+
+void
+mwl_hal_detach(struct mwl_hal *mh0)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+
+ bus_dmamem_free(mh->mh_dmat, mh->mh_cmdbuf, mh->mh_dmamap);
+ bus_dmamap_destroy(mh->mh_dmat, mh->mh_dmamap);
+ bus_dma_tag_destroy(mh->mh_dmat);
+ mtx_destroy(&mh->mh_mtx);
+ free(mh, M_DEVBUF);
+}
+
+/*
+ * Reset internal state after a firmware download.
+ */
+static int
+mwlResetHalState(struct mwl_hal_priv *mh)
+{
+ int i;
+
+ /* XXX get from f/w */
+ mh->mh_bastreams = (1<<MWL_BASTREAMS_MAX)-1;
+ for (i = 0; i < MWL_MBSS_MAX; i++)
+ mh->mh_vaps[i].mh = NULL;
+ /*
+ * Clear cumulative stats.
+ */
+ mh->mh_RTSSuccesses = 0;
+ mh->mh_RTSFailures = 0;
+ mh->mh_RxDuplicateFrames = 0;
+ mh->mh_FCSErrorCount = 0;
+ /*
+ * Fetch cal data for later use.
+ * XXX may want to fetch other stuff too.
+ */
+ /* XXX check return */
+ if ((mh->mh_flags & MHF_CALDATA) == 0)
+ mwlGetPwrCalTable(mh);
+ return 0;
+}
+
+struct mwl_hal_vap *
+mwl_hal_newvap(struct mwl_hal *mh0, MWL_HAL_BSSTYPE type,
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ struct mwl_hal_vap *vap;
+ int i;
+
+ MWL_HAL_LOCK(mh);
+ /* NB: could optimize but not worth it w/ max 32 bss */
+ for (i = 0; i < MWL_MBSS_MAX; i++) {
+ vap = &mh->mh_vaps[i];
+ if (vap->vap_type == type && vap->mh == NULL) {
+ vap->mh = mh;
+ mwl_hal_setmac_locked(vap, mac);
+ break;
+ }
+ }
+ MWL_HAL_UNLOCK(mh);
+ return (i < MWL_MBSS_MAX) ? vap : NULL;
+}
+
+void
+mwl_hal_delvap(struct mwl_hal_vap *vap)
+{
+ /* NB: locking not needed for single write */
+ vap->mh = NULL;
+}
+
+/*
+ * Manipulate the debug mask. Note debug
+ * msgs are only provided when this code is
+ * compiled with MWLHAL_DEBUG defined.
+ */
+
+void
+mwl_hal_setdebug(struct mwl_hal *mh, int debug)
+{
+ MWLPRIV(mh)->mh_debug = debug;
+}
+
+int
+mwl_hal_getdebug(struct mwl_hal *mh)
+{
+ return MWLPRIV(mh)->mh_debug;
+}
+
+void
+mwl_hal_setbastreams(struct mwl_hal *mh, int mask)
+{
+ MWLPRIV(mh)->mh_bastreams = mask & ((1<<MWL_BASTREAMS_MAX)-1);
+}
+
+int
+mwl_hal_getbastreams(struct mwl_hal *mh)
+{
+ return MWLPRIV(mh)->mh_bastreams;
+}
+
+int
+mwl_hal_ismbsscapable(struct mwl_hal *mh)
+{
+ return (MWLPRIV(mh)->mh_flags & MHF_MBSS) != 0;
+}
+
+#if 0
+/* XXX inlined */
+/*
+ * Return the current ISR setting and clear the cause.
+ * XXX maybe make inline
+ */
+void
+mwl_hal_getisr(struct mwl_hal *mh0, uint32_t *status)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ uint32_t cause;
+
+ cause = RD4(mh, MACREG_REG_A2H_INTERRUPT_CAUSE);
+ if (cause == 0xffffffff) { /* card removed */
+device_printf(mh->mh_dev, "%s: cause 0x%x\n", __func__, cause);
+ cause = 0;
+ } else if (cause != 0) {
+ /* clear cause bits */
+ WR4(mh, MACREG_REG_A2H_INTERRUPT_CAUSE,
+ cause &~ mh->public.mh_imask);
+ RD4(mh, MACREG_REG_INT_CODE); /* XXX flush write? */
+ }
+ *status = cause;
+}
+#endif
+
+/*
+ * Set the interrupt mask.
+ */
+void
+mwl_hal_intrset(struct mwl_hal *mh0, uint32_t mask)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+
+ WR4(mh, MACREG_REG_A2H_INTERRUPT_MASK, 0);
+ RD4(mh, MACREG_REG_INT_CODE);
+
+ mh->public.mh_imask = mask;
+ WR4(mh, MACREG_REG_A2H_INTERRUPT_MASK, mask);
+ RD4(mh, MACREG_REG_INT_CODE);
+}
+
+#if 0
+/* XXX inlined */
+/*
+ * Kick the firmware to tell it there are new tx descriptors
+ * for processing. The driver says what h/w q has work in
+ * case the f/w ever gets smarter.
+ */
+void
+mwl_hal_txstart(struct mwl_hal *mh0, int qnum)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ uint32_t dummy;
+
+ WR4(mh, MACREG_REG_H2A_INTERRUPT_EVENTS, MACREG_H2ARIC_BIT_PPA_READY);
+ dummy = RD4(mh, MACREG_REG_INT_CODE);
+}
+#endif
+
+/*
+ * Callback from the driver on a cmd done interrupt.
+ * Nothing to do right now as we spin waiting for
+ * cmd completion.
+ */
+void
+mwl_hal_cmddone(struct mwl_hal *mh0)
+{
+#if 0
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+
+ if (mh->mh_debug & MWL_HAL_DEBUG_CMDDONE) {
+ device_printf(mh->mh_dev, "cmd done interrupt:\n");
+ dumpresult(mh, 1);
+ }
+#endif
+}
+
+/*
+ * Return "hw specs". Note this must be the first
+ * cmd MUST be done after a firmware download or the
+ * f/w will lockup.
+ * XXX move into the hal so driver doesn't need to be responsible
+ */
+int
+mwl_hal_gethwspecs(struct mwl_hal *mh0, struct mwl_hal_hwspec *hw)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ HostCmd_DS_GET_HW_SPEC *pCmd;
+ int retval, minrev;
+
+ MWL_HAL_LOCK(mh);
+ _CMD_SETUP(pCmd, HostCmd_DS_GET_HW_SPEC, HostCmd_CMD_GET_HW_SPEC);
+ memset(&pCmd->PermanentAddr[0], 0xff, IEEE80211_ADDR_LEN);
+ pCmd->ulFwAwakeCookie = htole32((unsigned int)mh->mh_cmdaddr+2048);
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_GET_HW_SPEC);
+ if (retval == 0) {
+ IEEE80211_ADDR_COPY(hw->macAddr, pCmd->PermanentAddr);
+ hw->wcbBase[0] = le32toh(pCmd->WcbBase0) & 0x0000ffff;
+ hw->wcbBase[1] = le32toh(pCmd->WcbBase1) & 0x0000ffff;
+ hw->wcbBase[2] = le32toh(pCmd->WcbBase2) & 0x0000ffff;
+ hw->wcbBase[3] = le32toh(pCmd->WcbBase3) & 0x0000ffff;
+ hw->rxDescRead = le32toh(pCmd->RxPdRdPtr)& 0x0000ffff;
+ hw->rxDescWrite = le32toh(pCmd->RxPdWrPtr)& 0x0000ffff;
+ hw->regionCode = le16toh(pCmd->RegionCode) & 0x00ff;
+ hw->fwReleaseNumber = le32toh(pCmd->FWReleaseNumber);
+ hw->maxNumWCB = le16toh(pCmd->NumOfWCB);
+ hw->maxNumMCAddr = le16toh(pCmd->NumOfMCastAddr);
+ hw->numAntennas = le16toh(pCmd->NumberOfAntenna);
+ hw->hwVersion = pCmd->Version;
+ hw->hostInterface = pCmd->HostIf;
+
+ mh->mh_revs.mh_macRev = hw->hwVersion; /* XXX */
+ mh->mh_revs.mh_phyRev = hw->hostInterface; /* XXX */
+
+ minrev = ((hw->fwReleaseNumber) >> 16) & 0xff;
+ if (minrev >= 4) {
+ /* starting with 3.4.x.x s/w BA streams supported */
+ mh->mh_bastreams &= (1<<MWL_BASTREAMS_MAX)-1;
+ } else
+ mh->mh_bastreams &= (1<<2)-1;
+ }
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+/*
+ * Inform the f/w about location of the tx/rx dma data structures
+ * and related state. This cmd must be done immediately after a
+ * mwl_hal_gethwspecs call or the f/w will lockup.
+ */
+int
+mwl_hal_sethwdma(struct mwl_hal *mh0, const struct mwl_hal_txrxdma *dma)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ HostCmd_DS_SET_HW_SPEC *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ _CMD_SETUP(pCmd, HostCmd_DS_SET_HW_SPEC, HostCmd_CMD_SET_HW_SPEC);
+ pCmd->WcbBase[0] = htole32(dma->wcbBase[0]);
+ pCmd->WcbBase[1] = htole32(dma->wcbBase[1]);
+ pCmd->WcbBase[2] = htole32(dma->wcbBase[2]);
+ pCmd->WcbBase[3] = htole32(dma->wcbBase[3]);
+ pCmd->TxWcbNumPerQueue = htole32(dma->maxNumTxWcb);
+ pCmd->NumTxQueues = htole32(dma->maxNumWCB);
+ pCmd->TotalRxWcb = htole32(1); /* XXX */
+ pCmd->RxPdWrPtr = htole32(dma->rxDescRead);
+ pCmd->Flags = htole32(SET_HW_SPEC_HOSTFORM_BEACON
+#ifdef MWL_HOST_PS_SUPPORT
+ | SET_HW_SPEC_HOST_POWERSAVE
+#endif
+ | SET_HW_SPEC_HOSTFORM_PROBERESP);
+ /* disable multi-bss operation for A1-A4 parts */
+ if (mh->mh_revs.mh_macRev < 5)
+ pCmd->Flags |= htole32(SET_HW_SPEC_DISABLEMBSS);
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_HW_SPEC);
+ if (retval == 0) {
+ if (pCmd->Flags & htole32(SET_HW_SPEC_DISABLEMBSS))
+ mh->mh_flags &= ~MHF_MBSS;
+ else
+ mh->mh_flags |= MHF_MBSS;
+ }
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+/*
+ * Retrieve statistics from the f/w.
+ * XXX should be in memory shared w/ driver
+ */
+int
+mwl_hal_gethwstats(struct mwl_hal *mh0, struct mwl_hal_hwstats *stats)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ HostCmd_DS_802_11_GET_STAT *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ _CMD_SETUP(pCmd, HostCmd_DS_802_11_GET_STAT,
+ HostCmd_CMD_802_11_GET_STAT);
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_802_11_GET_STAT);
+ if (retval == 0) {
+ const uint32_t *sp = (const uint32_t *)&pCmd->TxRetrySuccesses;
+ uint32_t *dp = (uint32_t *)&stats->TxRetrySuccesses;
+ int i;
+
+ for (i = 0; i < sizeof(*stats)/sizeof(uint32_t); i++)
+ dp[i] = le32toh(sp[i]);
+ /*
+ * Update stats not returned by f/w but available
+ * through public registers. Note these registers
+ * are "clear on read" so we maintain cumulative data.
+ * XXX register defines
+ */
+ mh->mh_RTSSuccesses += RD4(mh, 0xa834);
+ mh->mh_RTSFailures += RD4(mh, 0xa830);
+ mh->mh_RxDuplicateFrames += RD4(mh, 0xa84c);
+ mh->mh_FCSErrorCount += RD4(mh, 0xa840);
+ }
+ MWL_HAL_UNLOCK(mh);
+
+ stats->RTSSuccesses = mh->mh_RTSSuccesses;
+ stats->RTSFailures = mh->mh_RTSFailures;
+ stats->RxDuplicateFrames = mh->mh_RxDuplicateFrames;
+ stats->FCSErrorCount = mh->mh_FCSErrorCount;
+ return retval;
+}
+
+/*
+ * Set HT guard interval handling.
+ * Takes effect immediately.
+ */
+int
+mwl_hal_sethtgi(struct mwl_hal_vap *vap, int GIType)
+{
+ struct mwl_hal_priv *mh = MWLVAP(vap);
+ HostCmd_FW_HT_GUARD_INTERVAL *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ _VCMD_SETUP(vap, pCmd, HostCmd_FW_HT_GUARD_INTERVAL,
+ HostCmd_CMD_HT_GUARD_INTERVAL);
+ pCmd->Action = htole32(HostCmd_ACT_GEN_SET);
+
+ if (GIType == 0) {
+ pCmd->GIType = htole32(GI_TYPE_LONG);
+ } else if (GIType == 1) {
+ pCmd->GIType = htole32(GI_TYPE_LONG | GI_TYPE_SHORT);
+ } else {
+ pCmd->GIType = htole32(GI_TYPE_LONG);
+ }
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_HT_GUARD_INTERVAL);
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+/*
+ * Configure radio.
+ * Takes effect immediately.
+ * XXX preamble installed after set fixed rate cmd
+ */
+int
+mwl_hal_setradio(struct mwl_hal *mh0, int onoff, MWL_HAL_PREAMBLE preamble)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ HostCmd_DS_802_11_RADIO_CONTROL *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ _CMD_SETUP(pCmd, HostCmd_DS_802_11_RADIO_CONTROL,
+ HostCmd_CMD_802_11_RADIO_CONTROL);
+ pCmd->Action = htole16(HostCmd_ACT_GEN_SET);
+ if (onoff == 0)
+ pCmd->Control = 0;
+ else
+ pCmd->Control = htole16(preamble);
+ pCmd->RadioOn = htole16(onoff);
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_802_11_RADIO_CONTROL);
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+/*
+ * Configure antenna use.
+ * Takes effect immediately.
+ * XXX tx antenna setting ignored
+ * XXX rx antenna setting should always be 3 (for now)
+ */
+int
+mwl_hal_setantenna(struct mwl_hal *mh0, MWL_HAL_ANTENNA dirSet, int ant)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ HostCmd_DS_802_11_RF_ANTENNA *pCmd;
+ int retval;
+
+ if (!(dirSet == WL_ANTENNATYPE_RX || dirSet == WL_ANTENNATYPE_TX))
+ return EINVAL;
+
+ MWL_HAL_LOCK(mh);
+ _CMD_SETUP(pCmd, HostCmd_DS_802_11_RF_ANTENNA,
+ HostCmd_CMD_802_11_RF_ANTENNA);
+ pCmd->Action = htole16(dirSet);
+ if (ant == 0) /* default to all/both antennae */
+ ant = 3;
+ pCmd->AntennaMode = htole16(ant);
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_802_11_RF_ANTENNA);
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+/*
+ * Set packet size threshold for implicit use of RTS.
+ * Takes effect immediately.
+ * XXX packet length > threshold =>'s RTS
+ */
+int
+mwl_hal_setrtsthreshold(struct mwl_hal_vap *vap, int threshold)
+{
+ struct mwl_hal_priv *mh = MWLVAP(vap);
+ HostCmd_DS_802_11_RTS_THSD *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ _VCMD_SETUP(vap, pCmd, HostCmd_DS_802_11_RTS_THSD,
+ HostCmd_CMD_802_11_RTS_THSD);
+ pCmd->Action = htole16(HostCmd_ACT_GEN_SET);
+ pCmd->Threshold = htole16(threshold);
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_802_11_RTS_THSD);
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+/*
+ * Enable sta-mode operation (disables beacon frame xmit).
+ */
+int
+mwl_hal_setinframode(struct mwl_hal_vap *vap)
+{
+ struct mwl_hal_priv *mh = MWLVAP(vap);
+ HostCmd_FW_SET_INFRA_MODE *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ _VCMD_SETUP(vap, pCmd, HostCmd_FW_SET_INFRA_MODE,
+ HostCmd_CMD_SET_INFRA_MODE);
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_INFRA_MODE);
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+/*
+ * Configure radar detection in support of 802.11h.
+ */
+int
+mwl_hal_setradardetection(struct mwl_hal *mh0, MWL_HAL_RADAR action)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ HostCmd_802_11h_Detect_Radar *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ _CMD_SETUP(pCmd, HostCmd_802_11h_Detect_Radar,
+ HostCmd_CMD_802_11H_DETECT_RADAR);
+ pCmd->CmdHdr.Length = htole16(sizeof(HostCmd_802_11h_Detect_Radar));
+ pCmd->Action = htole16(action);
+ if (mh->mh_regioncode == DOMAIN_CODE_ETSI_131)
+ pCmd->RadarTypeCode = htole16(131);
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_802_11H_DETECT_RADAR);
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+/*
+ * Convert public channel flags definition to a
+ * value suitable for feeding to the firmware.
+ * Note this includes byte swapping.
+ */
+static uint32_t
+cvtChannelFlags(const MWL_HAL_CHANNEL *chan)
+{
+ uint32_t w;
+
+ /*
+ * NB: f/w only understands FREQ_BAND_5GHZ, supplying the more
+ * precise band info causes it to lockup (sometimes).
+ */
+ w = (chan->channelFlags.FreqBand == MWL_FREQ_BAND_2DOT4GHZ) ?
+ FREQ_BAND_2DOT4GHZ : FREQ_BAND_5GHZ;
+ switch (chan->channelFlags.ChnlWidth) {
+ case MWL_CH_10_MHz_WIDTH:
+ w |= CH_10_MHz_WIDTH;
+ break;
+ case MWL_CH_20_MHz_WIDTH:
+ w |= CH_20_MHz_WIDTH;
+ break;
+ case MWL_CH_40_MHz_WIDTH:
+ default:
+ w |= CH_40_MHz_WIDTH;
+ break;
+ }
+ switch (chan->channelFlags.ExtChnlOffset) {
+ case MWL_EXT_CH_NONE:
+ w |= EXT_CH_NONE;
+ break;
+ case MWL_EXT_CH_ABOVE_CTRL_CH:
+ w |= EXT_CH_ABOVE_CTRL_CH;
+ break;
+ case MWL_EXT_CH_BELOW_CTRL_CH:
+ w |= EXT_CH_BELOW_CTRL_CH;
+ break;
+ }
+ return htole32(w);
+}
+
+/*
+ * Start a channel switch announcement countdown. The IE
+ * in the beacon frame is allowed to go out and the firmware
+ * counts down and notifies the host when it's time to switch
+ * channels.
+ */
+int
+mwl_hal_setchannelswitchie(struct mwl_hal *mh0,
+ const MWL_HAL_CHANNEL *nextchan, uint32_t mode, uint32_t count)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ HostCmd_SET_SWITCH_CHANNEL *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ _CMD_SETUP(pCmd, HostCmd_SET_SWITCH_CHANNEL,
+ HostCmd_CMD_SET_SWITCH_CHANNEL);
+ pCmd->Next11hChannel = htole32(nextchan->channel);
+ pCmd->Mode = htole32(mode);
+ pCmd->InitialCount = htole32(count+1);
+ pCmd->ChannelFlags = cvtChannelFlags(nextchan);
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_SWITCH_CHANNEL);
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+/*
+ * Set the region code that selects the radar bin'ing agorithm.
+ */
+int
+mwl_hal_setregioncode(struct mwl_hal *mh0, int regionCode)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ HostCmd_SET_REGIONCODE_INFO *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ _CMD_SETUP(pCmd, HostCmd_SET_REGIONCODE_INFO,
+ HostCmd_CMD_SET_REGION_CODE);
+ /* XXX map pseudo-codes to fw codes */
+ switch (regionCode) {
+ case DOMAIN_CODE_ETSI_131:
+ pCmd->regionCode = htole16(DOMAIN_CODE_ETSI);
+ break;
+ default:
+ pCmd->regionCode = htole16(regionCode);
+ break;
+ }
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_REGION_CODE);
+ if (retval == 0)
+ mh->mh_regioncode = regionCode;
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+#define RATEVAL(r) ((r) &~ RATE_MCS)
+#define RATETYPE(r) (((r) & RATE_MCS) ? HT_RATE_TYPE : LEGACY_RATE_TYPE)
+
+int
+mwl_hal_settxrate(struct mwl_hal_vap *vap, MWL_HAL_TXRATE_HANDLING handling,
+ const MWL_HAL_TXRATE *rate)
+{
+ struct mwl_hal_priv *mh = MWLVAP(vap);
+ HostCmd_FW_USE_FIXED_RATE *pCmd;
+ FIXED_RATE_ENTRY *fp;
+ int retval, i, n;
+
+ MWL_HAL_LOCK(mh);
+ _VCMD_SETUP(vap, pCmd, HostCmd_FW_USE_FIXED_RATE,
+ HostCmd_CMD_SET_FIXED_RATE);
+
+ pCmd->MulticastRate = RATEVAL(rate->McastRate);
+ pCmd->MultiRateTxType = RATETYPE(rate->McastRate);
+ /* NB: no rate type field */
+ pCmd->ManagementRate = RATEVAL(rate->MgtRate);
+ memset(pCmd->FixedRateTable, 0, sizeof(pCmd->FixedRateTable));
+ if (handling == RATE_FIXED) {
+ pCmd->Action = htole32(HostCmd_ACT_GEN_SET);
+ pCmd->AllowRateDrop = htole32(FIXED_RATE_WITHOUT_AUTORATE_DROP);
+ fp = pCmd->FixedRateTable;
+ fp->FixedRate =
+ htole32(RATEVAL(rate->RateSeries[0].Rate));
+ fp->FixRateTypeFlags.FixRateType =
+ htole32(RATETYPE(rate->RateSeries[0].Rate));
+ pCmd->EntryCount = htole32(1);
+ } else if (handling == RATE_FIXED_DROP) {
+ pCmd->Action = htole32(HostCmd_ACT_GEN_SET);
+ pCmd->AllowRateDrop = htole32(FIXED_RATE_WITH_AUTO_RATE_DROP);
+ n = 0;
+ fp = pCmd->FixedRateTable;
+ for (i = 0; i < 4; i++) {
+ if (rate->RateSeries[0].TryCount == 0)
+ break;
+ fp->FixRateTypeFlags.FixRateType =
+ htole32(RATETYPE(rate->RateSeries[i].Rate));
+ fp->FixedRate =
+ htole32(RATEVAL(rate->RateSeries[i].Rate));
+ fp->FixRateTypeFlags.RetryCountValid =
+ htole32(RETRY_COUNT_VALID);
+ fp->RetryCount =
+ htole32(rate->RateSeries[i].TryCount-1);
+ n++;
+ }
+ pCmd->EntryCount = htole32(n);
+ } else
+ pCmd->Action = htole32(HostCmd_ACT_NOT_USE_FIXED_RATE);
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_FIXED_RATE);
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+int
+mwl_hal_settxrate_auto(struct mwl_hal *mh0, const MWL_HAL_TXRATE *rate)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ HostCmd_FW_USE_FIXED_RATE *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ _CMD_SETUP(pCmd, HostCmd_FW_USE_FIXED_RATE,
+ HostCmd_CMD_SET_FIXED_RATE);
+
+ pCmd->MulticastRate = RATEVAL(rate->McastRate);
+ pCmd->MultiRateTxType = RATETYPE(rate->McastRate);
+ /* NB: no rate type field */
+ pCmd->ManagementRate = RATEVAL(rate->MgtRate);
+ memset(pCmd->FixedRateTable, 0, sizeof(pCmd->FixedRateTable));
+ pCmd->Action = htole32(HostCmd_ACT_NOT_USE_FIXED_RATE);
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_FIXED_RATE);
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+#undef RATEVAL
+#undef RATETYPE
+
+int
+mwl_hal_setslottime(struct mwl_hal *mh0, int usecs)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ HostCmd_FW_SET_SLOT *pCmd;
+ int retval;
+
+ if (usecs != 9 && usecs != 20)
+ return EINVAL;
+
+ MWL_HAL_LOCK(mh);
+ _CMD_SETUP(pCmd, HostCmd_FW_SET_SLOT,
+ HostCmd_CMD_802_11_SET_SLOT);
+ pCmd->Action = htole16(HostCmd_ACT_GEN_SET);
+ pCmd->Slot = (usecs == 9 ? 1 : 0);
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_802_11_SET_SLOT);
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+int
+mwl_hal_adjusttxpower(struct mwl_hal *mh0, uint32_t level)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ HostCmd_DS_802_11_RF_TX_POWER *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ _CMD_SETUP(pCmd, HostCmd_DS_802_11_RF_TX_POWER,
+ HostCmd_CMD_802_11_RF_TX_POWER);
+ pCmd->Action = htole16(HostCmd_ACT_GEN_SET);
+
+ if (level < 30) {
+ pCmd->SupportTxPowerLevel = htole16(WL_TX_POWERLEVEL_LOW);
+ } else if (level >= 30 && level < 60) {
+ pCmd->SupportTxPowerLevel = htole16(WL_TX_POWERLEVEL_MEDIUM);
+ } else {
+ pCmd->SupportTxPowerLevel = htole16(WL_TX_POWERLEVEL_HIGH);
+ }
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_802_11_RF_TX_POWER);
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+static const struct mwl_hal_channel *
+findchannel(const struct mwl_hal_priv *mh, const MWL_HAL_CHANNEL *c)
+{
+ const struct mwl_hal_channel *hc;
+ const MWL_HAL_CHANNELINFO *ci;
+ int chan = c->channel, i;
+
+ if (c->channelFlags.FreqBand == MWL_FREQ_BAND_2DOT4GHZ) {
+ i = chan - 1;
+ if (c->channelFlags.ChnlWidth == MWL_CH_40_MHz_WIDTH) {
+ ci = &mh->mh_40M;
+ if (c->channelFlags.ExtChnlOffset == MWL_EXT_CH_BELOW_CTRL_CH)
+ i -= 4;
+ } else
+ ci = &mh->mh_20M;
+ /* 2.4G channel table is directly indexed */
+ hc = ((unsigned)i < ci->nchannels) ? &ci->channels[i] : NULL;
+ } else if (c->channelFlags.FreqBand == MWL_FREQ_BAND_5GHZ) {
+ if (c->channelFlags.ChnlWidth == MWL_CH_40_MHz_WIDTH) {
+ ci = &mh->mh_40M_5G;
+ if (c->channelFlags.ExtChnlOffset == MWL_EXT_CH_BELOW_CTRL_CH)
+ chan -= 4;
+ } else
+ ci = &mh->mh_20M_5G;
+ /* 5GHz channel table is sparse and must be searched */
+ for (i = 0; i < ci->nchannels; i++)
+ if (ci->channels[i].ieee == chan)
+ break;
+ hc = (i < ci->nchannels) ? &ci->channels[i] : NULL;
+ } else
+ hc = NULL;
+ return hc;
+}
+
+int
+mwl_hal_settxpower(struct mwl_hal *mh0, const MWL_HAL_CHANNEL *c, uint8_t maxtxpow)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ HostCmd_DS_802_11_RF_TX_POWER *pCmd;
+ const struct mwl_hal_channel *hc;
+ int i, retval;
+
+ hc = findchannel(mh, c);
+ if (hc == NULL) {
+ /* XXX temp while testing */
+ device_printf(mh->mh_dev,
+ "%s: no cal data for channel %u band %u width %u ext %u\n",
+ __func__, c->channel, c->channelFlags.FreqBand,
+ c->channelFlags.ChnlWidth, c->channelFlags.ExtChnlOffset);
+ return EINVAL;
+ }
+
+ MWL_HAL_LOCK(mh);
+ _CMD_SETUP(pCmd, HostCmd_DS_802_11_RF_TX_POWER,
+ HostCmd_CMD_802_11_RF_TX_POWER);
+ pCmd->Action = htole16(HostCmd_ACT_GEN_SET_LIST);
+ i = 0;
+ /* NB: 5Ghz cal data have the channel # in [0]; don't truncate */
+ if (c->channelFlags.FreqBand == MWL_FREQ_BAND_5GHZ)
+ pCmd->PowerLevelList[i++] = htole16(hc->targetPowers[0]);
+ for (; i < 4; i++) {
+ uint16_t pow = hc->targetPowers[i];
+ if (pow > maxtxpow)
+ pow = maxtxpow;
+ pCmd->PowerLevelList[i] = htole16(pow);
+ }
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_802_11_RF_TX_POWER);
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+int
+mwl_hal_getchannelinfo(struct mwl_hal *mh0, int band, int chw,
+ const MWL_HAL_CHANNELINFO **ci)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+
+ switch (band) {
+ case MWL_FREQ_BAND_2DOT4GHZ:
+ *ci = (chw == MWL_CH_20_MHz_WIDTH) ? &mh->mh_20M : &mh->mh_40M;
+ break;
+ case MWL_FREQ_BAND_5GHZ:
+ *ci = (chw == MWL_CH_20_MHz_WIDTH) ?
+ &mh->mh_20M_5G : &mh->mh_40M_5G;
+ break;
+ default:
+ return EINVAL;
+ }
+ return ((*ci)->freqLow == (*ci)->freqHigh) ? EINVAL : 0;
+}
+
+int
+mwl_hal_setmcast(struct mwl_hal *mh0, int nmc, const uint8_t macs[])
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ HostCmd_DS_MAC_MULTICAST_ADR *pCmd;
+ int retval;
+
+ if (nmc > MWL_HAL_MCAST_MAX)
+ return EINVAL;
+
+ MWL_HAL_LOCK(mh);
+ _CMD_SETUP(pCmd, HostCmd_DS_MAC_MULTICAST_ADR,
+ HostCmd_CMD_MAC_MULTICAST_ADR);
+ memcpy(pCmd->MACList, macs, nmc*IEEE80211_ADDR_LEN);
+ pCmd->NumOfAdrs = htole16(nmc);
+ pCmd->Action = htole16(0xffff);
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_MAC_MULTICAST_ADR);
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+int
+mwl_hal_keyset(struct mwl_hal_vap *vap, const MWL_HAL_KEYVAL *kv,
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct mwl_hal_priv *mh = MWLVAP(vap);
+ HostCmd_FW_UPDATE_ENCRYPTION_SET_KEY *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ _VCMD_SETUP(vap, pCmd, HostCmd_FW_UPDATE_ENCRYPTION_SET_KEY,
+ HostCmd_CMD_UPDATE_ENCRYPTION);
+ if (kv->keyFlags & (KEY_FLAG_TXGROUPKEY|KEY_FLAG_RXGROUPKEY))
+ pCmd->ActionType = htole32(EncrActionTypeSetGroupKey);
+ else
+ pCmd->ActionType = htole32(EncrActionTypeSetKey);
+ pCmd->KeyParam.Length = htole16(sizeof(pCmd->KeyParam));
+ pCmd->KeyParam.KeyTypeId = htole16(kv->keyTypeId);
+ pCmd->KeyParam.KeyInfo = htole32(kv->keyFlags);
+ pCmd->KeyParam.KeyIndex = htole32(kv->keyIndex);
+ /* NB: includes TKIP MIC keys */
+ memcpy(&pCmd->KeyParam.Key, &kv->key, kv->keyLen);
+ switch (kv->keyTypeId) {
+ case KEY_TYPE_ID_WEP:
+ pCmd->KeyParam.KeyLen = htole16(kv->keyLen);
+ break;
+ case KEY_TYPE_ID_TKIP:
+ pCmd->KeyParam.KeyLen = htole16(sizeof(TKIP_TYPE_KEY));
+ pCmd->KeyParam.Key.TkipKey.TkipRsc.low =
+ htole16(kv->key.tkip.rsc.low);
+ pCmd->KeyParam.Key.TkipKey.TkipRsc.high =
+ htole32(kv->key.tkip.rsc.high);
+ pCmd->KeyParam.Key.TkipKey.TkipTsc.low =
+ htole16(kv->key.tkip.tsc.low);
+ pCmd->KeyParam.Key.TkipKey.TkipTsc.high =
+ htole32(kv->key.tkip.tsc.high);
+ break;
+ case KEY_TYPE_ID_AES:
+ pCmd->KeyParam.KeyLen = htole16(sizeof(AES_TYPE_KEY));
+ break;
+ }
+#ifdef MWL_MBSS_SUPPORT
+ IEEE80211_ADDR_COPY(pCmd->KeyParam.Macaddr, mac);
+#else
+ IEEE80211_ADDR_COPY(pCmd->Macaddr, mac);
+#endif
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_UPDATE_ENCRYPTION);
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+int
+mwl_hal_keyreset(struct mwl_hal_vap *vap, const MWL_HAL_KEYVAL *kv, const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct mwl_hal_priv *mh = MWLVAP(vap);
+ HostCmd_FW_UPDATE_ENCRYPTION_SET_KEY *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ _VCMD_SETUP(vap, pCmd, HostCmd_FW_UPDATE_ENCRYPTION_SET_KEY,
+ HostCmd_CMD_UPDATE_ENCRYPTION);
+ pCmd->ActionType = htole16(EncrActionTypeRemoveKey);
+ pCmd->KeyParam.Length = htole16(sizeof(pCmd->KeyParam));
+ pCmd->KeyParam.KeyTypeId = htole16(kv->keyTypeId);
+ pCmd->KeyParam.KeyInfo = htole32(kv->keyFlags);
+ pCmd->KeyParam.KeyIndex = htole32(kv->keyIndex);
+#ifdef MWL_MBSS_SUPPORT
+ IEEE80211_ADDR_COPY(pCmd->KeyParam.Macaddr, mac);
+#else
+ IEEE80211_ADDR_COPY(pCmd->Macaddr, mac);
+#endif
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_UPDATE_ENCRYPTION);
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+static int
+mwl_hal_setmac_locked(struct mwl_hal_vap *vap,
+ const uint8_t addr[IEEE80211_ADDR_LEN])
+{
+ struct mwl_hal_priv *mh = MWLVAP(vap);
+ HostCmd_DS_SET_MAC *pCmd;
+
+ _VCMD_SETUP(vap, pCmd, HostCmd_DS_SET_MAC, HostCmd_CMD_SET_MAC_ADDR);
+ IEEE80211_ADDR_COPY(&pCmd->MacAddr[0], addr);
+#ifdef MWL_MBSS_SUPPORT
+ pCmd->MacType = vap->bss_type; /* NB: already byte swapped */
+ IEEE80211_ADDR_COPY(vap->mac, addr); /* XXX do only if success */
+#endif
+ return mwlExecuteCmd(mh, HostCmd_CMD_SET_MAC_ADDR);
+}
+
+int
+mwl_hal_setmac(struct mwl_hal_vap *vap, const uint8_t addr[IEEE80211_ADDR_LEN])
+{
+ struct mwl_hal_priv *mh = MWLVAP(vap);
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ retval = mwl_hal_setmac_locked(vap, addr);
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+int
+mwl_hal_setbeacon(struct mwl_hal_vap *vap, const void *frame, size_t frameLen)
+{
+ struct mwl_hal_priv *mh = MWLVAP(vap);
+ HostCmd_DS_SET_BEACON *pCmd;
+ int retval;
+
+ /* XXX verify frameLen fits */
+ MWL_HAL_LOCK(mh);
+ _VCMD_SETUP(vap, pCmd, HostCmd_DS_SET_BEACON, HostCmd_CMD_SET_BEACON);
+ /* XXX override _VCMD_SETUP */
+ pCmd->CmdHdr.Length = htole16(sizeof(HostCmd_DS_SET_BEACON)-1+frameLen);
+ pCmd->FrmBodyLen = htole16(frameLen);
+ memcpy(pCmd->FrmBody, frame, frameLen);
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_BEACON);
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+int
+mwl_hal_setpowersave_bss(struct mwl_hal_vap *vap, uint8_t nsta)
+{
+ struct mwl_hal_priv *mh = MWLVAP(vap);
+ HostCmd_SET_POWERSAVESTATION *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ _VCMD_SETUP(vap, pCmd, HostCmd_SET_POWERSAVESTATION,
+ HostCmd_CMD_SET_POWERSAVESTATION);
+ pCmd->NumberOfPowersave = nsta;
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_POWERSAVESTATION);
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+int
+mwl_hal_setpowersave_sta(struct mwl_hal_vap *vap, uint16_t aid, int ena)
+{
+ struct mwl_hal_priv *mh = MWLVAP(vap);
+ HostCmd_SET_TIM *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ _VCMD_SETUP(vap, pCmd, HostCmd_SET_TIM, HostCmd_CMD_SET_TIM);
+ pCmd->Aid = htole16(aid);
+ pCmd->Set = htole32(ena);
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_TIM);
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+int
+mwl_hal_setassocid(struct mwl_hal_vap *vap,
+ const uint8_t bssId[IEEE80211_ADDR_LEN], uint16_t assocId)
+{
+ struct mwl_hal_priv *mh = MWLVAP(vap);
+ HostCmd_FW_SET_AID *pCmd = (HostCmd_FW_SET_AID *) &mh->mh_cmdbuf[0];
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ _VCMD_SETUP(vap, pCmd, HostCmd_FW_SET_AID, HostCmd_CMD_SET_AID);
+ pCmd->AssocID = htole16(assocId);
+ IEEE80211_ADDR_COPY(&pCmd->MacAddr[0], bssId);
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_AID);
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+int
+mwl_hal_setchannel(struct mwl_hal *mh0, const MWL_HAL_CHANNEL *chan)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ HostCmd_FW_SET_RF_CHANNEL *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ _CMD_SETUP(pCmd, HostCmd_FW_SET_RF_CHANNEL, HostCmd_CMD_SET_RF_CHANNEL);
+ pCmd->Action = htole16(HostCmd_ACT_GEN_SET);
+ pCmd->CurrentChannel = chan->channel;
+ pCmd->ChannelFlags = cvtChannelFlags(chan); /* NB: byte-swapped */
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_RF_CHANNEL);
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+static int
+bastream_check_available(struct mwl_hal_priv *mh, int qid,
+ const uint8_t Macaddr[IEEE80211_ADDR_LEN],
+ uint8_t Tid, uint8_t ParamInfo)
+{
+ HostCmd_FW_BASTREAM *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK_ASSERT(mh);
+
+ _CMD_SETUP(pCmd, HostCmd_FW_BASTREAM, HostCmd_CMD_BASTREAM);
+ pCmd->ActionType = htole32(BaCheckCreateStream);
+ pCmd->BaInfo.CreateParams.BarThrs = htole32(63);
+ pCmd->BaInfo.CreateParams.WindowSize = htole32(64);
+ pCmd->BaInfo.CreateParams.IdleThrs = htole32(0x22000);
+ IEEE80211_ADDR_COPY(&pCmd->BaInfo.CreateParams.PeerMacAddr[0], Macaddr);
+ pCmd->BaInfo.CreateParams.DialogToken = 10;
+ pCmd->BaInfo.CreateParams.Tid = Tid;
+ pCmd->BaInfo.CreateParams.QueueId = qid;
+ pCmd->BaInfo.CreateParams.ParamInfo = (uint8_t) ParamInfo;
+#if 0
+ cvtBAFlags(&pCmd->BaInfo.CreateParams.Flags, sp->ba_type, 0);
+#else
+ pCmd->BaInfo.CreateParams.Flags =
+ htole32(BASTREAM_FLAG_IMMEDIATE_TYPE)
+ | htole32(BASTREAM_FLAG_DIRECTION_UPSTREAM)
+ ;
+#endif
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_BASTREAM);
+ if (retval == 0) {
+ /*
+ * NB: BA stream create may fail when the stream is
+ * h/w backed under some (as yet not understood) conditions.
+ * Check the result code to catch this.
+ */
+ if (le16toh(pCmd->CmdHdr.Result) != HostCmd_RESULT_OK)
+ retval = EIO;
+ }
+ return retval;
+}
+
+const MWL_HAL_BASTREAM *
+mwl_hal_bastream_alloc(struct mwl_hal *mh0, int ba_type,
+ const uint8_t Macaddr[IEEE80211_ADDR_LEN],
+ uint8_t Tid, uint8_t ParamInfo, void *a1, void *a2)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ struct mwl_hal_bastream *sp;
+ int s;
+
+ MWL_HAL_LOCK(mh);
+ if (mh->mh_bastreams == 0) {
+ /* no streams available */
+ MWL_HAL_UNLOCK(mh);
+ return NULL;
+ }
+ for (s = 0; (mh->mh_bastreams & (1<<s)) == 0; s++)
+ ;
+ if (bastream_check_available(mh, s, Macaddr, Tid, ParamInfo)) {
+ MWL_HAL_UNLOCK(mh);
+ return NULL;
+ }
+ sp = &mh->mh_streams[s];
+ mh->mh_bastreams &= ~(1<<s);
+ sp->public.data[0] = a1;
+ sp->public.data[1] = a2;
+ IEEE80211_ADDR_COPY(sp->macaddr, Macaddr);
+ sp->tid = Tid;
+ sp->paraminfo = ParamInfo;
+ sp->setup = 0;
+ sp->ba_type = ba_type;
+ MWL_HAL_UNLOCK(mh);
+ return sp != NULL ? &sp->public : NULL;
+}
+
+const MWL_HAL_BASTREAM *
+mwl_hal_bastream_lookup(struct mwl_hal *mh0, int s)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+
+ if (!(0 <= s && s < MWL_BASTREAMS_MAX))
+ return NULL;
+ if (mh->mh_bastreams & (1<<s))
+ return NULL;
+ return &mh->mh_streams[s].public;
+}
+
+#ifndef __DECONST
+#define __DECONST(type, var) ((type)(uintptr_t)(const void *)(var))
+#endif
+
+int
+mwl_hal_bastream_create(struct mwl_hal *mh0,
+ const MWL_HAL_BASTREAM *s, int BarThrs, int WindowSize, uint16_t seqno)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ struct mwl_hal_bastream *sp = __DECONST(struct mwl_hal_bastream *, s);
+ HostCmd_FW_BASTREAM *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ _CMD_SETUP(pCmd, HostCmd_FW_BASTREAM, HostCmd_CMD_BASTREAM);
+ pCmd->ActionType = htole32(BaCreateStream);
+ pCmd->BaInfo.CreateParams.BarThrs = htole32(63);//BarThrs;
+ pCmd->BaInfo.CreateParams.WindowSize = htole32(64); /*WindowSize;*/
+ pCmd->BaInfo.CreateParams.IdleThrs = htole32(0x22000);
+ IEEE80211_ADDR_COPY(&pCmd->BaInfo.CreateParams.PeerMacAddr[0],
+ sp->macaddr);
+#if 0
+ pCmd->BaInfo.CreateParams.DialogToken = DialogToken;
+#else
+ pCmd->BaInfo.CreateParams.DialogToken = 10;
+#endif
+ pCmd->BaInfo.CreateParams.Tid = sp->tid;
+ pCmd->BaInfo.CreateParams.QueueId = sp->stream;
+ pCmd->BaInfo.CreateParams.ParamInfo = sp->paraminfo;
+ /* NB: ResetSeqNo known to be zero */
+ pCmd->BaInfo.CreateParams.StartSeqNo = htole16(seqno);
+#if 0
+ cvtBAFlags(&pCmd->BaInfo.CreateParams.Flags, sp->ba_type, 0);
+#else
+ pCmd->BaInfo.CreateParams.Flags =
+ htole32(BASTREAM_FLAG_IMMEDIATE_TYPE)
+ | htole32(BASTREAM_FLAG_DIRECTION_UPSTREAM)
+ ;
+#endif
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_BASTREAM);
+ if (retval == 0) {
+ /*
+ * NB: BA stream create may fail when the stream is
+ * h/w backed under some (as yet not understood) conditions.
+ * Check the result code to catch this.
+ */
+ if (le16toh(pCmd->CmdHdr.Result) != HostCmd_RESULT_OK)
+ retval = EIO;
+ else
+ sp->setup = 1;
+ }
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+int
+mwl_hal_bastream_destroy(struct mwl_hal *mh0, const MWL_HAL_BASTREAM *s)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ struct mwl_hal_bastream *sp = __DECONST(struct mwl_hal_bastream *, s);
+ HostCmd_FW_BASTREAM *pCmd;
+ int retval;
+
+ if (sp->stream >= MWL_BASTREAMS_MAX) {
+ /* XXX */
+ return EINVAL;
+ }
+ MWL_HAL_LOCK(mh);
+ if (sp->setup) {
+ _CMD_SETUP(pCmd, HostCmd_FW_BASTREAM, HostCmd_CMD_BASTREAM);
+ pCmd->ActionType = htole32(BaDestroyStream);
+ pCmd->BaInfo.DestroyParams.FwBaContext.Context =
+ htole32(sp->stream);
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_BASTREAM);
+ } else
+ retval = 0;
+ /* NB: always reclaim stream */
+ mh->mh_bastreams |= 1<<sp->stream;
+ sp->public.data[0] = NULL;
+ sp->public.data[1] = NULL;
+ sp->setup = 0;
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+int
+mwl_hal_bastream_get_seqno(struct mwl_hal *mh0,
+ const MWL_HAL_BASTREAM *s, uint16_t *pseqno)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ struct mwl_hal_bastream *sp = __DECONST(struct mwl_hal_bastream *, s);
+ HostCmd_GET_SEQNO *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ _CMD_SETUP(pCmd, HostCmd_GET_SEQNO, HostCmd_CMD_GET_SEQNO);
+ IEEE80211_ADDR_COPY(pCmd->MacAddr, sp->macaddr);
+ pCmd->TID = sp->tid;
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_GET_SEQNO);
+ if (retval == 0)
+ *pseqno = le16toh(pCmd->SeqNo);
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+int
+mwl_hal_getwatchdogbitmap(struct mwl_hal *mh0, uint8_t bitmap[1])
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ HostCmd_FW_GET_WATCHDOG_BITMAP *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ _CMD_SETUP(pCmd, HostCmd_FW_GET_WATCHDOG_BITMAP,
+ HostCmd_CMD_GET_WATCHDOG_BITMAP);
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_GET_WATCHDOG_BITMAP);
+ if (retval == 0) {
+ bitmap[0] = pCmd->Watchdogbitmap;
+ /* fw returns qid, map it to BA stream */
+ if (bitmap[0] < MWL_BAQID_MAX)
+ bitmap[0] = qid2ba[bitmap[0]];
+ }
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+static void
+cvtPeerInfo(PeerInfo_t *to, const MWL_HAL_PEERINFO *from)
+{
+ to->LegacyRateBitMap = htole32(from->LegacyRateBitMap);
+ to->HTRateBitMap = htole32(from->HTRateBitMap);
+ to->CapInfo = htole16(from->CapInfo);
+ to->HTCapabilitiesInfo = htole16(from->HTCapabilitiesInfo);
+ to->MacHTParamInfo = from->MacHTParamInfo;
+ to->AddHtInfo.ControlChan = from->AddHtInfo.ControlChan;
+ to->AddHtInfo.AddChan = from->AddHtInfo.AddChan;
+ to->AddHtInfo.OpMode = htole16(from->AddHtInfo.OpMode);
+ to->AddHtInfo.stbc = htole16(from->AddHtInfo.stbc);
+}
+
+/* XXX station id must be in [0..63] */
+int
+mwl_hal_newstation(struct mwl_hal_vap *vap,
+ const uint8_t addr[IEEE80211_ADDR_LEN], uint16_t aid, uint16_t sid,
+ const MWL_HAL_PEERINFO *peer, int isQosSta, int wmeInfo)
+{
+ struct mwl_hal_priv *mh = MWLVAP(vap);
+ HostCmd_FW_SET_NEW_STN *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ _VCMD_SETUP(vap, pCmd, HostCmd_FW_SET_NEW_STN, HostCmd_CMD_SET_NEW_STN);
+ pCmd->AID = htole16(aid);
+ pCmd->StnId = htole16(sid);
+ pCmd->Action = htole16(0); /* SET */
+ if (peer != NULL) {
+ /* NB: must fix up byte order */
+ cvtPeerInfo(&pCmd->PeerInfo, peer);
+ }
+ IEEE80211_ADDR_COPY(&pCmd->MacAddr[0], addr);
+ pCmd->Qosinfo = wmeInfo;
+ pCmd->isQosSta = (isQosSta != 0);
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_NEW_STN);
+ if (retval == 0 && IEEE80211_ADDR_EQ(vap->mac, addr))
+ vap->flags |= MVF_STATION;
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+int
+mwl_hal_delstation(struct mwl_hal_vap *vap,
+ const uint8_t addr[IEEE80211_ADDR_LEN])
+{
+ struct mwl_hal_priv *mh = MWLVAP(vap);
+ HostCmd_FW_SET_NEW_STN *pCmd;
+ int retval, islocal;
+
+ MWL_HAL_LOCK(mh);
+ islocal = IEEE80211_ADDR_EQ(vap->mac, addr);
+ if (!islocal || (vap->flags & MVF_STATION)) {
+ _VCMD_SETUP(vap, pCmd, HostCmd_FW_SET_NEW_STN,
+ HostCmd_CMD_SET_NEW_STN);
+ pCmd->Action = htole16(2); /* REMOVE */
+ IEEE80211_ADDR_COPY(&pCmd->MacAddr[0], addr);
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_NEW_STN);
+ if (islocal)
+ vap->flags &= ~MVF_STATION;
+ } else
+ retval = 0;
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+/*
+ * Prod the firmware to age packets on station power
+ * save queues and reap frames on the tx aggregation q's.
+ */
+int
+mwl_hal_setkeepalive(struct mwl_hal *mh0)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ HostCmd_FW_SET_KEEP_ALIVE_TICK *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ _CMD_SETUP(pCmd, HostCmd_FW_SET_KEEP_ALIVE_TICK,
+ HostCmd_CMD_SET_KEEP_ALIVE);
+ /*
+ * NB: tick must be 0 to prod the f/w;
+ * a non-zero value is a noop.
+ */
+ pCmd->tick = 0;
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_KEEP_ALIVE);
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+int
+mwl_hal_setapmode(struct mwl_hal_vap *vap, MWL_HAL_APMODE ApMode)
+{
+ struct mwl_hal_priv *mh = MWLVAP(vap);
+ HostCmd_FW_SET_APMODE *pCmd;
+ int retval;
+
+ /* XXX validate ApMode? */
+
+ MWL_HAL_LOCK(mh);
+ _VCMD_SETUP(vap, pCmd, HostCmd_FW_SET_APMODE, HostCmd_CMD_SET_APMODE);
+ pCmd->ApMode = ApMode;
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_APMODE);
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+int
+mwl_hal_stop(struct mwl_hal_vap *vap)
+{
+ struct mwl_hal_priv *mh = MWLVAP(vap);
+ HostCmd_DS_BSS_START *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ if (vap->flags & MVF_RUNNING) {
+ _VCMD_SETUP(vap, pCmd, HostCmd_DS_BSS_START,
+ HostCmd_CMD_BSS_START);
+ pCmd->Enable = htole32(HostCmd_ACT_GEN_OFF);
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_BSS_START);
+ } else
+ retval = 0;
+ /* NB: mark !running regardless */
+ vap->flags &= ~MVF_RUNNING;
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+int
+mwl_hal_start(struct mwl_hal_vap *vap)
+{
+ struct mwl_hal_priv *mh = MWLVAP(vap);
+ HostCmd_DS_BSS_START *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ _VCMD_SETUP(vap, pCmd, HostCmd_DS_BSS_START, HostCmd_CMD_BSS_START);
+ pCmd->Enable = htole32(HostCmd_ACT_GEN_ON);
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_BSS_START);
+ if (retval == 0)
+ vap->flags |= MVF_RUNNING;
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+int
+mwl_hal_setgprot(struct mwl_hal *mh0, int prot)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ HostCmd_FW_SET_G_PROTECT_FLAG *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ _CMD_SETUP(pCmd, HostCmd_FW_SET_G_PROTECT_FLAG,
+ HostCmd_CMD_SET_G_PROTECT_FLAG);
+ pCmd->GProtectFlag = htole32(prot);
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_G_PROTECT_FLAG);
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+int
+mwl_hal_setwmm(struct mwl_hal *mh0, int onoff)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ HostCmd_FW_SetWMMMode *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ _CMD_SETUP(pCmd, HostCmd_FW_SetWMMMode,
+ HostCmd_CMD_SET_WMM_MODE);
+ pCmd->Action = htole16(onoff);
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_WMM_MODE);
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+int
+mwl_hal_setedcaparams(struct mwl_hal *mh0, uint8_t qnum,
+ uint32_t CWmin, uint32_t CWmax, uint8_t AIFSN, uint16_t TXOPLimit)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ HostCmd_FW_SET_EDCA_PARAMS *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ _CMD_SETUP(pCmd, HostCmd_FW_SET_EDCA_PARAMS,
+ HostCmd_CMD_SET_EDCA_PARAMS);
+ /*
+ * NB: CWmin and CWmax are always set.
+ * TxOpLimit is set if bit 0x2 is marked in Action
+ * AIFSN is set if bit 0x4 is marked in Action
+ */
+ pCmd->Action = htole16(0xffff); /* NB: set everything */
+ pCmd->TxOP = htole16(TXOPLimit);
+ pCmd->CWMax = htole32(CWmax);
+ pCmd->CWMin = htole32(CWmin);
+ pCmd->AIFSN = AIFSN;
+ pCmd->TxQNum = qnum; /* XXX check */
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_EDCA_PARAMS);
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+/* XXX 0 = indoor, 1 = outdoor */
+int
+mwl_hal_setrateadaptmode(struct mwl_hal *mh0, uint16_t mode)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ HostCmd_DS_SET_RATE_ADAPT_MODE *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ _CMD_SETUP(pCmd, HostCmd_DS_SET_RATE_ADAPT_MODE,
+ HostCmd_CMD_SET_RATE_ADAPT_MODE);
+ pCmd->Action = htole16(HostCmd_ACT_GEN_SET);
+ pCmd->RateAdaptMode = htole16(mode);
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_RATE_ADAPT_MODE);
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+int
+mwl_hal_setcsmode(struct mwl_hal *mh0, MWL_HAL_CSMODE csmode)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ HostCmd_DS_SET_LINKADAPT_CS_MODE *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ _CMD_SETUP(pCmd, HostCmd_DS_SET_LINKADAPT_CS_MODE,
+ HostCmd_CMD_SET_LINKADAPT_CS_MODE);
+ pCmd->Action = htole16(HostCmd_ACT_GEN_SET);
+ pCmd->CSMode = htole16(csmode);
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_LINKADAPT_CS_MODE);
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+int
+mwl_hal_setnprot(struct mwl_hal_vap *vap, MWL_HAL_HTPROTECT mode)
+{
+ struct mwl_hal_priv *mh = MWLVAP(vap);
+ HostCmd_FW_SET_N_PROTECT_FLAG *pCmd;
+ int retval;
+
+ /* XXX validate mode */
+ MWL_HAL_LOCK(mh);
+ _VCMD_SETUP(vap, pCmd, HostCmd_FW_SET_N_PROTECT_FLAG,
+ HostCmd_CMD_SET_N_PROTECT_FLAG);
+ pCmd->NProtectFlag = htole32(mode);
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_N_PROTECT_FLAG);
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+int
+mwl_hal_setnprotmode(struct mwl_hal_vap *vap, uint8_t mode)
+{
+ struct mwl_hal_priv *mh = MWLVAP(vap);
+ HostCmd_FW_SET_N_PROTECT_OPMODE *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ _VCMD_SETUP(vap, pCmd, HostCmd_FW_SET_N_PROTECT_OPMODE,
+ HostCmd_CMD_SET_N_PROTECT_OPMODE);
+ pCmd->NProtectOpMode = mode;
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_N_PROTECT_OPMODE);
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+int
+mwl_hal_setoptimizationlevel(struct mwl_hal *mh0, int level)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ HostCmd_FW_SET_OPTIMIZATION_LEVEL *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ _CMD_SETUP(pCmd, HostCmd_FW_SET_OPTIMIZATION_LEVEL,
+ HostCmd_CMD_SET_OPTIMIZATION_LEVEL);
+ pCmd->OptLevel = level;
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_OPTIMIZATION_LEVEL);
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+int
+mwl_hal_setmimops(struct mwl_hal *mh0, const uint8_t addr[IEEE80211_ADDR_LEN],
+ uint8_t enable, uint8_t mode)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ HostCmd_FW_SET_MIMOPSHT *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ _CMD_SETUP(pCmd, HostCmd_FW_SET_MIMOPSHT, HostCmd_CMD_SET_MIMOPSHT);
+ IEEE80211_ADDR_COPY(pCmd->Addr, addr);
+ pCmd->Enable = enable;
+ pCmd->Mode = mode;
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_MIMOPSHT);
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+static int
+mwlGetCalTable(struct mwl_hal_priv *mh, uint8_t annex, uint8_t index)
+{
+ HostCmd_FW_GET_CALTABLE *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK_ASSERT(mh);
+
+ _CMD_SETUP(pCmd, HostCmd_FW_GET_CALTABLE, HostCmd_CMD_GET_CALTABLE);
+ pCmd->annex = annex;
+ pCmd->index = index;
+ memset(pCmd->calTbl, 0, sizeof(pCmd->calTbl));
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_GET_CALTABLE);
+ if (retval == 0 &&
+ pCmd->calTbl[0] != annex && annex != 0 && annex != 255)
+ retval = EIO;
+ return retval;
+}
+
+/*
+ * Calculate the max tx power from the channel's cal data.
+ */
+static void
+setmaxtxpow(struct mwl_hal_channel *hc, int i, int maxix)
+{
+ hc->maxTxPow = hc->targetPowers[i];
+ for (i++; i < maxix; i++)
+ if (hc->targetPowers[i] > hc->maxTxPow)
+ hc->maxTxPow = hc->targetPowers[i];
+}
+
+/*
+ * Construct channel info for 5GHz channels from cal data.
+ */
+static void
+get5Ghz(MWL_HAL_CHANNELINFO *ci, const uint8_t table[], int len)
+{
+ int i, j, f, l, h;
+
+ l = 32000;
+ h = 0;
+ j = 0;
+ for (i = 0; i < len; i += 4) {
+ struct mwl_hal_channel *hc;
+
+ if (table[i] == 0)
+ continue;
+ f = 5000 + 5*table[i];
+ if (f < l)
+ l = f;
+ if (f > h)
+ h = f;
+ hc = &ci->channels[j];
+ hc->freq = f;
+ hc->ieee = table[i];
+ memcpy(hc->targetPowers, &table[i], 4);
+ setmaxtxpow(hc, 1, 4); /* NB: col 1 is the freq, skip*/
+ j++;
+ }
+ ci->nchannels = j;
+ ci->freqLow = (l == 32000) ? 0 : l;
+ ci->freqHigh = h;
+}
+
+static uint16_t
+ieee2mhz(int chan)
+{
+ if (chan == 14)
+ return 2484;
+ if (chan < 14)
+ return 2407 + chan*5;
+ return 2512 + (chan-15)*20;
+}
+
+/*
+ * Construct channel info for 2.4GHz channels from cal data.
+ */
+static void
+get2Ghz(MWL_HAL_CHANNELINFO *ci, const uint8_t table[], int len)
+{
+ int i, j;
+
+ j = 0;
+ for (i = 0; i < len; i += 4) {
+ struct mwl_hal_channel *hc = &ci->channels[j];
+ hc->ieee = 1+j;
+ hc->freq = ieee2mhz(1+j);
+ memcpy(hc->targetPowers, &table[i], 4);
+ setmaxtxpow(hc, 0, 4);
+ j++;
+ }
+ ci->nchannels = j;
+ ci->freqLow = ieee2mhz(1);
+ ci->freqHigh = ieee2mhz(j);
+}
+
+#undef DUMPCALDATA
+#ifdef DUMPCALDATA
+static void
+dumpcaldata(const char *name, const uint8_t *table, int n)
+{
+ int i;
+ printf("\n%s:\n", name);
+ for (i = 0; i < n; i += 4)
+ printf("[%2d] %3d %3d %3d %3d\n", i/4, table[i+0], table[i+1], table[i+2], table[i+3]);
+}
+#endif
+
+static int
+mwlGetPwrCalTable(struct mwl_hal_priv *mh)
+{
+ const uint8_t *data;
+ MWL_HAL_CHANNELINFO *ci;
+ int len;
+
+ MWL_HAL_LOCK(mh);
+ /* NB: we hold the lock so it's ok to use cmdbuf */
+ data = ((const HostCmd_FW_GET_CALTABLE *) mh->mh_cmdbuf)->calTbl;
+ if (mwlGetCalTable(mh, 33, 0) == 0) {
+ len = (data[2] | (data[3] << 8)) - 12;
+ if (len > PWTAGETRATETABLE20M)
+ len = PWTAGETRATETABLE20M;
+#ifdef DUMPCALDATA
+dumpcaldata("2.4G 20M", &data[12], len);/*XXX*/
+#endif
+ get2Ghz(&mh->mh_20M, &data[12], len);
+ }
+ if (mwlGetCalTable(mh, 34, 0) == 0) {
+ len = (data[2] | (data[3] << 8)) - 12;
+ if (len > PWTAGETRATETABLE40M)
+ len = PWTAGETRATETABLE40M;
+#ifdef DUMPCALDATA
+dumpcaldata("2.4G 40M", &data[12], len);/*XXX*/
+#endif
+ ci = &mh->mh_40M;
+ get2Ghz(ci, &data[12], len);
+ }
+ if (mwlGetCalTable(mh, 35, 0) == 0) {
+ len = (data[2] | (data[3] << 8)) - 20;
+ if (len > PWTAGETRATETABLE20M_5G)
+ len = PWTAGETRATETABLE20M_5G;
+#ifdef DUMPCALDATA
+dumpcaldata("5G 20M", &data[20], len);/*XXX*/
+#endif
+ get5Ghz(&mh->mh_20M_5G, &data[20], len);
+ }
+ if (mwlGetCalTable(mh, 36, 0) == 0) {
+ len = (data[2] | (data[3] << 8)) - 20;
+ if (len > PWTAGETRATETABLE40M_5G)
+ len = PWTAGETRATETABLE40M_5G;
+#ifdef DUMPCALDATA
+dumpcaldata("5G 40M", &data[20], len);/*XXX*/
+#endif
+ ci = &mh->mh_40M_5G;
+ get5Ghz(ci, &data[20], len);
+ }
+ mh->mh_flags |= MHF_CALDATA;
+ MWL_HAL_UNLOCK(mh);
+ return 0;
+}
+
+int
+mwl_hal_getregioncode(struct mwl_hal *mh0, uint8_t *countryCode)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ retval = mwlGetCalTable(mh, 0, 0);
+ if (retval == 0) {
+ const HostCmd_FW_GET_CALTABLE *pCmd =
+ (const HostCmd_FW_GET_CALTABLE *) mh->mh_cmdbuf;
+ *countryCode = pCmd->calTbl[16];
+ }
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+int
+mwl_hal_setpromisc(struct mwl_hal *mh0, int ena)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ uint32_t v;
+
+ MWL_HAL_LOCK(mh);
+ v = RD4(mh, MACREG_REG_PROMISCUOUS);
+ WR4(mh, MACREG_REG_PROMISCUOUS, ena ? v | 1 : v &~ 1);
+ MWL_HAL_UNLOCK(mh);
+ return 0;
+}
+
+int
+mwl_hal_getpromisc(struct mwl_hal *mh0)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ uint32_t v;
+
+ MWL_HAL_LOCK(mh);
+ v = RD4(mh, MACREG_REG_PROMISCUOUS);
+ MWL_HAL_UNLOCK(mh);
+ return (v & 1) != 0;
+}
+
+int
+mwl_hal_GetBeacon(struct mwl_hal *mh0, uint8_t *pBcn, uint16_t *pLen)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ HostCmd_FW_GET_BEACON *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ _CMD_SETUP(pCmd, HostCmd_FW_GET_BEACON, HostCmd_CMD_GET_BEACON);
+ pCmd->Bcnlen = htole16(0);
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_GET_BEACON);
+ if (retval == 0) {
+ /* XXX bounds check */
+ memcpy(pBcn, &pCmd->Bcn, pCmd->Bcnlen);
+ *pLen = pCmd->Bcnlen;
+ }
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+int
+mwl_hal_SetRifs(struct mwl_hal *mh0, uint8_t QNum)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ HostCmd_FW_SET_RIFS *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ _CMD_SETUP(pCmd, HostCmd_FW_SET_RIFS, HostCmd_CMD_SET_RIFS);
+ pCmd->QNum = QNum;
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_RIFS);
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+/*
+ * Diagnostic api's for set/get registers.
+ */
+
+static int
+getRFReg(struct mwl_hal_priv *mh, int flag, uint32_t reg, uint32_t *val)
+{
+ HostCmd_DS_RF_REG_ACCESS *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ _CMD_SETUP(pCmd, HostCmd_DS_RF_REG_ACCESS, HostCmd_CMD_RF_REG_ACCESS);
+ pCmd->Offset = htole16(reg);
+ pCmd->Action = htole16(flag);
+ pCmd->Value = htole32(*val);
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_RF_REG_ACCESS);
+ if (retval == 0)
+ *val = pCmd->Value;
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+static int
+getBBReg(struct mwl_hal_priv *mh, int flag, uint32_t reg, uint32_t *val)
+{
+ HostCmd_DS_BBP_REG_ACCESS *pCmd;
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ _CMD_SETUP(pCmd, HostCmd_DS_BBP_REG_ACCESS, HostCmd_CMD_BBP_REG_ACCESS);
+ pCmd->Offset = htole16(reg);
+ pCmd->Action = htole16(flag);
+ pCmd->Value = htole32(*val);
+
+ retval = mwlExecuteCmd(mh, HostCmd_CMD_BBP_REG_ACCESS);
+ if (retval == 0)
+ *val = pCmd->Value;
+ MWL_HAL_UNLOCK(mh);
+ return retval;
+}
+
+static u_int
+mwl_hal_getregdump(struct mwl_hal_priv *mh, const MWL_DIAG_REGRANGE *regs,
+ void *dstbuf, int space)
+{
+ uint32_t *dp = dstbuf;
+ int i;
+
+ for (i = 0; space >= 2*sizeof(uint32_t); i++) {
+ u_int r = regs[i].start;
+ u_int e = regs[i].end;
+ *dp++ = (r<<16) | e;
+ space -= sizeof(uint32_t);
+ do {
+ if (MWL_DIAG_ISMAC(r))
+ *dp = RD4(mh, r);
+ else if (MWL_DIAG_ISBB(r))
+ getBBReg(mh, HostCmd_ACT_GEN_READ,
+ r - MWL_DIAG_BASE_BB, dp);
+ else if (MWL_DIAG_ISRF(r))
+ getRFReg(mh, HostCmd_ACT_GEN_READ,
+ r - MWL_DIAG_BASE_RF, dp);
+ else if (r < 0x1000 || r == MACREG_REG_FW_PRESENT)
+ *dp = RD4(mh, r);
+ else
+ *dp = 0xffffffff;
+ dp++;
+ r += sizeof(uint32_t);
+ space -= sizeof(uint32_t);
+ } while (r <= e && space >= sizeof(uint32_t));
+ }
+ return (char *) dp - (char *) dstbuf;
+}
+
+int
+mwl_hal_getdiagstate(struct mwl_hal *mh0, int request,
+ const void *args, uint32_t argsize,
+ void **result, uint32_t *resultsize)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+
+ switch (request) {
+ case MWL_DIAG_CMD_REVS:
+ *result = &mh->mh_revs;
+ *resultsize = sizeof(mh->mh_revs);
+ return 1;
+ case MWL_DIAG_CMD_REGS:
+ *resultsize = mwl_hal_getregdump(mh, args, *result, *resultsize);
+ return 1;
+ case MWL_DIAG_CMD_HOSTCMD: {
+ FWCmdHdr *pCmd = (FWCmdHdr *) &mh->mh_cmdbuf[0];
+ int retval;
+
+ MWL_HAL_LOCK(mh);
+ memcpy(pCmd, args, argsize);
+ retval = mwlExecuteCmd(mh, le16toh(pCmd->Cmd));
+ *result = (*resultsize != 0) ? pCmd : NULL;
+ MWL_HAL_UNLOCK(mh);
+ return (retval == 0);
+ }
+ case MWL_DIAG_CMD_FWLOAD:
+ if (mwl_hal_fwload(mh0, __DECONST(void *, args))) {
+ device_printf(mh->mh_dev, "problem loading fw image\n");
+ return 0;
+ }
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Low level firmware cmd block handshake support.
+ */
+
+static void
+mwlSendCmd(struct mwl_hal_priv *mh)
+{
+ uint32_t dummy;
+
+ bus_dmamap_sync(mh->mh_dmat, mh->mh_dmamap,
+ BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+
+ WR4(mh, MACREG_REG_GEN_PTR, mh->mh_cmdaddr);
+ dummy = RD4(mh, MACREG_REG_INT_CODE);
+
+ WR4(mh, MACREG_REG_H2A_INTERRUPT_EVENTS, MACREG_H2ARIC_BIT_DOOR_BELL);
+}
+
+static int
+mwlWaitForCmdComplete(struct mwl_hal_priv *mh, uint16_t cmdCode)
+{
+#define MAX_WAIT_FW_COMPLETE_ITERATIONS 10000
+ int i;
+
+ for (i = 0; i < MAX_WAIT_FW_COMPLETE_ITERATIONS; i++) {
+ if (mh->mh_cmdbuf[0] == le16toh(cmdCode))
+ return 1;
+ DELAY(1*1000);
+ }
+ return 0;
+#undef MAX_WAIT_FW_COMPLETE_ITERATIONS
+}
+
+static int
+mwlExecuteCmd(struct mwl_hal_priv *mh, unsigned short cmd)
+{
+
+ MWL_HAL_LOCK_ASSERT(mh);
+
+ if ((mh->mh_flags & MHF_FWHANG) &&
+ (mh->mh_debug & MWL_HAL_DEBUG_IGNHANG) == 0) {
+#ifdef MWLHAL_DEBUG
+ device_printf(mh->mh_dev, "firmware hung, skipping cmd %s\n",
+ mwlcmdname(cmd));
+#else
+ device_printf(mh->mh_dev, "firmware hung, skipping cmd 0x%x\n",
+ cmd);
+#endif
+ return ENXIO;
+ }
+ if (RD4(mh, MACREG_REG_INT_CODE) == 0xffffffff) {
+ device_printf(mh->mh_dev, "%s: device not present!\n",
+ __func__);
+ return EIO;
+ }
+#ifdef MWLHAL_DEBUG
+ if (mh->mh_debug & MWL_HAL_DEBUG_SENDCMD)
+ dumpresult(mh, 0);
+#endif
+ mwlSendCmd(mh);
+ if (!mwlWaitForCmdComplete(mh, 0x8000 | cmd)) {
+#ifdef MWLHAL_DEBUG
+ device_printf(mh->mh_dev,
+ "timeout waiting for f/w cmd %s\n", mwlcmdname(cmd));
+#else
+ device_printf(mh->mh_dev,
+ "timeout waiting for f/w cmd 0x%x\n", cmd);
+#endif
+ mh->mh_flags |= MHF_FWHANG;
+ return ETIMEDOUT;
+ }
+ bus_dmamap_sync(mh->mh_dmat, mh->mh_dmamap,
+ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+#ifdef MWLHAL_DEBUG
+ if (mh->mh_debug & MWL_HAL_DEBUG_CMDDONE)
+ dumpresult(mh, 1);
+#endif
+ return 0;
+}
+
+/*
+ * Firmware download support.
+ */
+#define FW_DOWNLOAD_BLOCK_SIZE 256
+#define FW_CHECK_USECS (5*1000) /* 5ms */
+#define FW_MAX_NUM_CHECKS 200
+
+#if 0
+/* XXX read f/w from file */
+#include <dev/mwl/mwlbootfw.h>
+#include <dev/mwl/mwl88W8363fw.h>
+#endif
+
+static void
+mwlFwReset(struct mwl_hal_priv *mh)
+{
+ if (RD4(mh, MACREG_REG_INT_CODE) == 0xffffffff) {
+ device_printf(mh->mh_dev, "%s: device not present!\n",
+ __func__);
+ return;
+ }
+ WR4(mh, MACREG_REG_H2A_INTERRUPT_EVENTS, ISR_RESET);
+ mh->mh_flags &= ~MHF_FWHANG;
+}
+
+static void
+mwlTriggerPciCmd(struct mwl_hal_priv *mh)
+{
+ uint32_t dummy;
+
+ bus_dmamap_sync(mh->mh_dmat, mh->mh_dmamap, BUS_DMASYNC_PREWRITE);
+
+ WR4(mh, MACREG_REG_GEN_PTR, mh->mh_cmdaddr);
+ dummy = RD4(mh, MACREG_REG_INT_CODE);
+
+ WR4(mh, MACREG_REG_INT_CODE, 0x00);
+ dummy = RD4(mh, MACREG_REG_INT_CODE);
+
+ WR4(mh, MACREG_REG_H2A_INTERRUPT_EVENTS, MACREG_H2ARIC_BIT_DOOR_BELL);
+ dummy = RD4(mh, MACREG_REG_INT_CODE);
+}
+
+static int
+mwlWaitFor(struct mwl_hal_priv *mh, uint32_t val)
+{
+ int i;
+
+ for (i = 0; i < FW_MAX_NUM_CHECKS; i++) {
+ DELAY(FW_CHECK_USECS);
+ if (RD4(mh, MACREG_REG_INT_CODE) == val)
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Firmware block xmit when talking to the boot-rom.
+ */
+static int
+mwlSendBlock(struct mwl_hal_priv *mh, int bsize, const void *data, size_t dsize)
+{
+ mh->mh_cmdbuf[0] = htole16(HostCmd_CMD_CODE_DNLD);
+ mh->mh_cmdbuf[1] = htole16(bsize);
+ memcpy(&mh->mh_cmdbuf[4], data , dsize);
+ mwlTriggerPciCmd(mh);
+ /* XXX 2000 vs 200 */
+ if (mwlWaitFor(mh, MACREG_INT_CODE_CMD_FINISHED)) {
+ WR4(mh, MACREG_REG_INT_CODE, 0);
+ return 1;
+ }
+ device_printf(mh->mh_dev,
+ "%s: timeout waiting for CMD_FINISHED, INT_CODE 0x%x\n",
+ __func__, RD4(mh, MACREG_REG_INT_CODE));
+ return 0;
+}
+
+/*
+ * Firmware block xmit when talking to the 1st-stage loader.
+ */
+static int
+mwlSendBlock2(struct mwl_hal_priv *mh, const void *data, size_t dsize)
+{
+ memcpy(&mh->mh_cmdbuf[0], data, dsize);
+ mwlTriggerPciCmd(mh);
+ if (mwlWaitFor(mh, MACREG_INT_CODE_CMD_FINISHED)) {
+ WR4(mh, MACREG_REG_INT_CODE, 0);
+ return 1;
+ }
+ device_printf(mh->mh_dev,
+ "%s: timeout waiting for CMD_FINISHED, INT_CODE 0x%x\n",
+ __func__, RD4(mh, MACREG_REG_INT_CODE));
+ return 0;
+}
+
+static void
+mwlPokeSdramController(struct mwl_hal_priv *mh, int SDRAMSIZE_Addr)
+{
+ /** Set up sdram controller for superflyv2 **/
+ WR4(mh, 0x00006014, 0x33);
+ WR4(mh, 0x00006018, 0xa3a2632);
+ WR4(mh, 0x00006010, SDRAMSIZE_Addr);
+}
+
+int
+mwl_hal_fwload(struct mwl_hal *mh0, void *fwargs)
+{
+ struct mwl_hal_priv *mh = MWLPRIV(mh0);
+ const char *fwname = "mw88W8363fw";
+ const char *fwbootname = "mwlboot";
+ const struct firmware *fwboot = NULL;
+ const struct firmware *fw;
+ /* XXX get from firmware header */
+ uint32_t FwReadySignature = HostCmd_SOFTAP_FWRDY_SIGNATURE;
+ uint32_t OpMode = HostCmd_SOFTAP_MODE;
+ const uint8_t *fp, *ep;
+ const uint8_t *fmdata;
+ uint32_t blocksize, nbytes, fmsize;
+ int i, error, ntries;
+
+ fw = firmware_get(fwname);
+ if (fw == NULL) {
+ device_printf(mh->mh_dev,
+ "could not load firmware image %s\n", fwname);
+ return ENXIO;
+ }
+ fmdata = fw->data;
+ fmsize = fw->datasize;
+ if (fmsize < 4) {
+ device_printf(mh->mh_dev, "firmware image %s too small\n",
+ fwname);
+ error = ENXIO;
+ goto bad2;
+ }
+ if (fmdata[0] == 0x01 && fmdata[1] == 0x00 &&
+ fmdata[2] == 0x00 && fmdata[3] == 0x00) {
+ /*
+ * 2-stage load, get the boot firmware.
+ */
+ fwboot = firmware_get(fwbootname);
+ if (fwboot == NULL) {
+ device_printf(mh->mh_dev,
+ "could not load firmware image %s\n", fwbootname);
+ error = ENXIO;
+ goto bad2;
+ }
+ } else
+ fwboot = NULL;
+
+ mwlFwReset(mh);
+
+ WR4(mh, MACREG_REG_A2H_INTERRUPT_CLEAR_SEL, MACREG_A2HRIC_BIT_MASK);
+ WR4(mh, MACREG_REG_A2H_INTERRUPT_CAUSE, 0x00);
+ WR4(mh, MACREG_REG_A2H_INTERRUPT_MASK, 0x00);
+ WR4(mh, MACREG_REG_A2H_INTERRUPT_STATUS_MASK, MACREG_A2HRIC_BIT_MASK);
+ if (mh->mh_SDRAMSIZE_Addr != 0) {
+ /** Set up sdram controller for superflyv2 **/
+ mwlPokeSdramController(mh, mh->mh_SDRAMSIZE_Addr);
+ }
+ device_printf(mh->mh_dev, "load %s firmware image (%u bytes)\n",
+ fwname, fmsize);
+ if (fwboot != NULL) {
+ /*
+ * Do 2-stage load. The 1st stage loader is setup
+ * with the bootrom loader then we load the real
+ * image using a different handshake. With this
+ * mechanism the firmware is segmented into chunks
+ * that have a CRC. If a chunk is incorrect we'll
+ * be told to retransmit.
+ */
+ /* XXX assumes hlpimage fits in a block */
+ /* NB: zero size block indicates download is finished */
+ if (!mwlSendBlock(mh, fwboot->datasize, fwboot->data, fwboot->datasize) ||
+ !mwlSendBlock(mh, 0, NULL, 0)) {
+ error = ETIMEDOUT;
+ goto bad;
+ }
+ DELAY(200*FW_CHECK_USECS);
+ if (mh->mh_SDRAMSIZE_Addr != 0) {
+ /** Set up sdram controller for superflyv2 **/
+ mwlPokeSdramController(mh, mh->mh_SDRAMSIZE_Addr);
+ }
+ nbytes = ntries = 0; /* NB: silence compiler */
+ for (fp = fmdata, ep = fp + fmsize; fp < ep; ) {
+ WR4(mh, MACREG_REG_INT_CODE, 0);
+ blocksize = RD4(mh, MACREG_REG_SCRATCH);
+ if (blocksize == 0) /* download complete */
+ break;
+ if (blocksize > 0x00000c00) {
+ error = EINVAL;
+ goto bad;
+ }
+ if ((blocksize & 0x1) == 0) {
+ /* block successfully downloaded, advance */
+ fp += nbytes;
+ ntries = 0;
+ } else {
+ if (++ntries > 2) {
+ /*
+ * Guard against f/w telling us to
+ * retry infinitely.
+ */
+ error = ELOOP;
+ goto bad;
+ }
+ /* clear NAK bit/flag */
+ blocksize &= ~0x1;
+ }
+ if (blocksize > ep - fp) {
+ /* XXX this should not happen, what to do? */
+ blocksize = ep - fp;
+ }
+ nbytes = blocksize;
+ if (!mwlSendBlock2(mh, fp, nbytes)) {
+ error = ETIMEDOUT;
+ goto bad;
+ }
+ }
+ } else {
+ for (fp = fmdata, ep = fp + fmsize; fp < ep;) {
+ nbytes = ep - fp;
+ if (nbytes > FW_DOWNLOAD_BLOCK_SIZE)
+ nbytes = FW_DOWNLOAD_BLOCK_SIZE;
+ if (!mwlSendBlock(mh, FW_DOWNLOAD_BLOCK_SIZE, fp, nbytes)) {
+ error = EIO;
+ goto bad;
+ }
+ fp += nbytes;
+ }
+ }
+ /* done with firmware... */
+ if (fwboot != NULL)
+ firmware_put(fwboot, FIRMWARE_UNLOAD);
+ firmware_put(fw, FIRMWARE_UNLOAD);
+ /*
+ * Wait for firmware to startup; we monitor the
+ * INT_CODE register waiting for a signature to
+ * written back indicating it's ready to go.
+ */
+ mh->mh_cmdbuf[1] = 0;
+ /*
+ * XXX WAR for mfg fw download
+ */
+ if (OpMode != HostCmd_STA_MODE)
+ mwlTriggerPciCmd(mh);
+ for (i = 0; i < FW_MAX_NUM_CHECKS; i++) {
+ WR4(mh, MACREG_REG_GEN_PTR, OpMode);
+ DELAY(FW_CHECK_USECS);
+ if (RD4(mh, MACREG_REG_INT_CODE) == FwReadySignature) {
+ WR4(mh, MACREG_REG_INT_CODE, 0x00);
+ return mwlResetHalState(mh);
+ }
+ }
+ return ETIMEDOUT;
+bad:
+ mwlFwReset(mh);
+bad2:
+ /* done with firmware... */
+ if (fwboot != NULL)
+ firmware_put(fwboot, FIRMWARE_UNLOAD);
+ firmware_put(fw, FIRMWARE_UNLOAD);
+ return error;
+}
+
+#ifdef MWLHAL_DEBUG
+static const char *
+mwlcmdname(int cmd)
+{
+ static char buf[12];
+#define CMD(x) case HostCmd_CMD_##x: return #x
+ switch (cmd) {
+ CMD(CODE_DNLD);
+ CMD(GET_HW_SPEC);
+ CMD(SET_HW_SPEC);
+ CMD(MAC_MULTICAST_ADR);
+ CMD(802_11_GET_STAT);
+ CMD(MAC_REG_ACCESS);
+ CMD(BBP_REG_ACCESS);
+ CMD(RF_REG_ACCESS);
+ CMD(802_11_RADIO_CONTROL);
+ CMD(802_11_RF_TX_POWER);
+ CMD(802_11_RF_ANTENNA);
+ CMD(SET_BEACON);
+ CMD(SET_RF_CHANNEL);
+ CMD(SET_AID);
+ CMD(SET_INFRA_MODE);
+ CMD(SET_G_PROTECT_FLAG);
+ CMD(802_11_RTS_THSD);
+ CMD(802_11_SET_SLOT);
+ CMD(SET_EDCA_PARAMS);
+ CMD(802_11H_DETECT_RADAR);
+ CMD(SET_WMM_MODE);
+ CMD(HT_GUARD_INTERVAL);
+ CMD(SET_FIXED_RATE);
+ CMD(SET_LINKADAPT_CS_MODE);
+ CMD(SET_MAC_ADDR);
+ CMD(SET_RATE_ADAPT_MODE);
+ CMD(BSS_START);
+ CMD(SET_NEW_STN);
+ CMD(SET_KEEP_ALIVE);
+ CMD(SET_APMODE);
+ CMD(SET_SWITCH_CHANNEL);
+ CMD(UPDATE_ENCRYPTION);
+ CMD(BASTREAM);
+ CMD(SET_RIFS);
+ CMD(SET_N_PROTECT_FLAG);
+ CMD(SET_N_PROTECT_OPMODE);
+ CMD(SET_OPTIMIZATION_LEVEL);
+ CMD(GET_CALTABLE);
+ CMD(SET_MIMOPSHT);
+ CMD(GET_BEACON);
+ CMD(SET_REGION_CODE);
+ CMD(SET_POWERSAVESTATION);
+ CMD(SET_TIM);
+ CMD(GET_TIM);
+ CMD(GET_SEQNO);
+ }
+ snprintf(buf, sizeof(buf), "0x%x", cmd);
+ return buf;
+#undef CMD
+}
+
+static void
+dumpresult(struct mwl_hal_priv *mh, int showresult)
+{
+ const FWCmdHdr *h = (const FWCmdHdr *)mh->mh_cmdbuf;
+ const uint8_t *cp;
+ int len, i;
+
+ len = le16toh(h->Length);
+#ifdef MWL_MBSS_SUPPORT
+ device_printf(mh->mh_dev, "Cmd %s Length %d SeqNum %d MacId %d",
+ mwlcmdname(le16toh(h->Cmd) &~ 0x8000), len, h->SeqNum, h->MacId);
+#else
+ device_printf(mh->mh_dev, "Cmd %s Length %d SeqNum %d",
+ mwlcmdname(le16toh(h->Cmd) &~ 0x8000), len, le16toh(h->SeqNum));
+#endif
+ if (showresult) {
+ const char *results[] =
+ { "OK", "ERROR", "NOT_SUPPORT", "PENDING", "BUSY",
+ "PARTIAL_DATA" };
+ int result = le16toh(h->Result);
+
+ if (result <= HostCmd_RESULT_PARTIAL_DATA)
+ printf(" Result %s", results[result]);
+ else
+ printf(" Result %d", result);
+ }
+ cp = (const uint8_t *)h;
+ for (i = 0; i < len; i++) {
+ if ((i % 16) == 0)
+ printf("\n%02x", cp[i]);
+ else
+ printf(" %02x", cp[i]);
+ }
+ printf("\n");
+}
+#endif /* MWLHAL_DEBUG */
diff --git a/sys/dev/mwl/mwlhal.h b/sys/dev/mwl/mwlhal.h
new file mode 100644
index 0000000000000..c82b427f00791
--- /dev/null
+++ b/sys/dev/mwl/mwlhal.h
@@ -0,0 +1,666 @@
+/*-
+ * Copyright (c) 2007-2009 Sam Leffler, Errno Consulting
+ * Copyright (c) 2007-2009 Marvell Semiconductor, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * 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 NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MWL_HAL_H_
+#define _MWL_HAL_H_
+/*
+ * Hardware Access Layer for Marvell Wireless Devices.
+ */
+
+#define MWL_MBSS_SUPPORT /* enable multi-bss support */
+
+/*
+ * Define total number of TX queues in the shared memory.
+ * This count includes the EDCA queues, Block Ack queues, and HCCA queues
+ * In addition to this, there could be a management packet queue some
+ * time in the future
+ */
+#define MWL_NUM_EDCA_QUEUES 4
+#define MWL_NUM_HCCA_QUEUES 0
+#define MWL_NUM_BA_QUEUES 0
+#define MWL_NUM_MGMT_QUEUES 0
+#define MWL_NUM_TX_QUEUES \
+ (MWL_NUM_EDCA_QUEUES + MWL_NUM_HCCA_QUEUES + MWL_NUM_BA_QUEUES + \
+ MWL_NUM_MGMT_QUEUES)
+#define MWL_MAX_RXWCB_QUEUES 1
+
+#define MWL_MAX_SUPPORTED_RATES 12
+#define MWL_MAX_SUPPORTED_MCS 32
+
+typedef enum {
+ MWL_HAL_OK
+} MWL_HAL_STATUS;
+
+/*
+ * Transmit queue assignment.
+ */
+enum {
+ MWL_WME_AC_BK = 0, /* background access category */
+ MWL_WME_AC_BE = 1, /* best effort access category*/
+ MWL_WME_AC_VI = 2, /* video access category */
+ MWL_WME_AC_VO = 3, /* voice access category */
+};
+
+struct device;
+
+struct mwl_hal {
+ bus_space_handle_t mh_ioh; /* BAR 1 copied from softc */
+ bus_space_tag_t mh_iot;
+ uint32_t mh_imask; /* interrupt mask */
+ /* remainder is opaque to driver */
+};
+struct mwl_hal *mwl_hal_attach(struct device *dev, uint16_t devid,
+ bus_space_handle_t ioh, bus_space_tag_t iot, bus_dma_tag_t tag);
+void mwl_hal_detach(struct mwl_hal *);
+
+/*
+ * Query whether multi-bss support is available/enabled.
+ */
+int mwl_hal_ismbsscapable(struct mwl_hal *);
+
+typedef enum {
+ MWL_HAL_AP,
+ MWL_HAL_STA, /* infrastructure mode */
+ MWL_HAL_IBSS /* ibss/adhoc mode */
+} MWL_HAL_BSSTYPE;
+struct mwl_hal_vap;
+
+struct mwl_hal_vap *mwl_hal_newvap(struct mwl_hal *, MWL_HAL_BSSTYPE,
+ const uint8_t mac[6]);
+void mwl_hal_delvap(struct mwl_hal_vap *);
+
+enum {
+ MWL_HAL_DEBUG_SENDCMD = 0x00000001,
+ MWL_HAL_DEBUG_CMDDONE = 0x00000002,
+ MWL_HAL_DEBUG_IGNHANG = 0x00000004,
+};
+void mwl_hal_setdebug(struct mwl_hal *, int);
+int mwl_hal_getdebug(struct mwl_hal *);
+
+typedef struct {
+ uint16_t freqLow, freqHigh;
+ int nchannels;
+ struct mwl_hal_channel {
+ uint16_t freq; /* channel center */
+ uint8_t ieee; /* channel number */
+ int8_t maxTxPow; /* max tx power (dBm) */
+ uint8_t targetPowers[4];/* target powers (dBm) */
+#define MWL_HAL_MAXCHAN 40
+ } channels[MWL_HAL_MAXCHAN];
+} MWL_HAL_CHANNELINFO;
+int mwl_hal_getchannelinfo(struct mwl_hal *, int band, int chw,
+ const MWL_HAL_CHANNELINFO **);
+
+/*
+ * Return the current ISR setting and clear the cause.
+ */
+static __inline void
+mwl_hal_getisr(struct mwl_hal *mh, uint32_t *status)
+{
+#define MACREG_REG_A2H_INTERRUPT_CAUSE 0x00000C30 // (From ARM to host)
+#define MACREG_REG_INT_CODE 0x00000C14
+ uint32_t cause;
+
+ cause = bus_space_read_4(mh->mh_iot, mh->mh_ioh,
+ MACREG_REG_A2H_INTERRUPT_CAUSE);
+ if (cause == 0xffffffff) { /* card removed */
+ cause = 0;
+ } else if (cause != 0) {
+ /* clear cause bits */
+ bus_space_write_4(mh->mh_iot, mh->mh_ioh,
+ MACREG_REG_A2H_INTERRUPT_CAUSE, cause &~ mh->mh_imask);
+ (void) bus_space_read_4(mh->mh_iot, mh->mh_ioh,
+ MACREG_REG_INT_CODE);
+ cause &= mh->mh_imask;
+ }
+ *status = cause;
+#undef MACREG_REG_INT_CODE
+#undef MACREG_REG_A2H_INTERRUPT_CAUSE
+}
+
+void mwl_hal_intrset(struct mwl_hal *mh, uint32_t mask);
+
+/*
+ * Kick the firmware to tell it there are new tx descriptors
+ * for processing. The driver says what h/w q has work in
+ * case the f/w ever gets smarter.
+ */
+static __inline void
+mwl_hal_txstart(struct mwl_hal *mh, int qnum)
+{
+#define MACREG_REG_H2A_INTERRUPT_EVENTS 0x00000C18 // (From host to ARM)
+#define MACREG_H2ARIC_BIT_PPA_READY 0x00000001 // bit 0
+#define MACREG_REG_INT_CODE 0x00000C14
+
+ bus_space_write_4(mh->mh_iot, mh->mh_ioh,
+ MACREG_REG_H2A_INTERRUPT_EVENTS, MACREG_H2ARIC_BIT_PPA_READY);
+ (void) bus_space_read_4(mh->mh_iot, mh->mh_ioh, MACREG_REG_INT_CODE);
+#undef MACREG_REG_INT_CODE
+#undef MACREG_H2ARIC_BIT_PPA_READY
+#undef MACREG_REG_H2A_INTERRUPT_EVENTS
+}
+
+void mwl_hal_cmddone(struct mwl_hal *mh);
+
+typedef struct {
+ uint32_t FreqBand : 6,
+#define MWL_FREQ_BAND_2DOT4GHZ 0x1
+#define MWL_FREQ_BAND_5GHZ 0x4
+ ChnlWidth: 5,
+#define MWL_CH_10_MHz_WIDTH 0x1
+#define MWL_CH_20_MHz_WIDTH 0x2
+#define MWL_CH_40_MHz_WIDTH 0x4
+ ExtChnlOffset: 2,
+#define MWL_EXT_CH_NONE 0x0
+#define MWL_EXT_CH_ABOVE_CTRL_CH 0x1
+#define MWL_EXT_CH_BELOW_CTRL_CH 0x3
+ : 19; /* reserved */
+} MWL_HAL_CHANNEL_FLAGS;
+
+typedef struct {
+ uint32_t channel;
+ MWL_HAL_CHANNEL_FLAGS channelFlags;
+} MWL_HAL_CHANNEL;
+
+/*
+ * Get Hardware/Firmware capabilities.
+ */
+struct mwl_hal_hwspec {
+ uint8_t hwVersion; /* version of the HW */
+ uint8_t hostInterface; /* host interface */
+ uint16_t maxNumWCB; /* max # of WCB FW handles */
+ uint16_t maxNumMCAddr; /* max # of mcast addresses FW handles*/
+ uint16_t maxNumTxWcb; /* max # of tx descs per WCB */
+ uint8_t macAddr[6]; /* MAC address programmed in HW */
+ uint16_t regionCode; /* EEPROM region code */
+ uint16_t numAntennas; /* Number of antenna used */
+ uint32_t fwReleaseNumber; /* firmware release number */
+ uint32_t wcbBase0;
+ uint32_t rxDescRead;
+ uint32_t rxDescWrite;
+ uint32_t ulFwAwakeCookie;
+ uint32_t wcbBase[4];
+};
+int mwl_hal_gethwspecs(struct mwl_hal *mh, struct mwl_hal_hwspec *);
+
+/*
+ * Supply tx/rx dma-related settings to the firmware.
+ */
+struct mwl_hal_txrxdma {
+ uint32_t maxNumWCB; /* max # of WCB FW handles */
+ uint32_t maxNumTxWcb; /* max # of tx descs per WCB */
+ uint32_t rxDescRead;
+ uint32_t rxDescWrite;
+ uint32_t wcbBase[4];
+};
+int mwl_hal_sethwdma(struct mwl_hal *mh, const struct mwl_hal_txrxdma *);
+
+/*
+ * Get Hardware Statistics.
+ *
+ * Items marked with ! are deprecated and not ever updated. In
+ * some cases this is because work has been moved to the host (e.g.
+ * rx defragmentation).
+ */
+struct mwl_hal_hwstats {
+ uint32_t TxRetrySuccesses; /* tx success w/ 1 retry */
+ uint32_t TxMultipleRetrySuccesses;/* tx success w/ >1 retry */
+ uint32_t TxFailures; /* tx fail due to no ACK */
+ uint32_t RTSSuccesses; /* CTS rx'd for RTS */
+ uint32_t RTSFailures; /* CTS not rx'd for RTS */
+ uint32_t AckFailures; /* same as TxFailures */
+ uint32_t RxDuplicateFrames; /* rx discard for dup seqno */
+ uint32_t FCSErrorCount; /* rx discard for bad FCS */
+ uint32_t TxWatchDogTimeouts; /* MAC tx hang (f/w recovery) */
+ uint32_t RxOverflows; /* no f/w buffer for rx data */
+ uint32_t RxFragErrors; /* !rx fail due to defrag */
+ uint32_t RxMemErrors; /* out of mem or desc corrupted
+ in some way */
+ uint32_t RxPointerErrors; /* MAC internal ptr problem */
+ uint32_t TxUnderflows; /* !tx underflow on dma */
+ uint32_t TxDone; /* MAC tx ops completed
+ (possibly w/ error) */
+ uint32_t TxDoneBufTryPut; /* ! */
+ uint32_t TxDoneBufPut; /* same as TxDone */
+ uint32_t Wait4TxBuf; /* !no f/w buf avail when
+ supplied a tx descriptor */
+ uint32_t TxAttempts; /* tx descriptors processed */
+ uint32_t TxSuccesses; /* tx attempts successful */
+ uint32_t TxFragments; /* tx with fragmentation */
+ uint32_t TxMulticasts; /* tx multicast frames */
+ uint32_t RxNonCtlPkts; /* rx non-control frames */
+ uint32_t RxMulticasts; /* rx multicast frames */
+ uint32_t RxUndecryptableFrames; /* rx failed due to crypto */
+ uint32_t RxICVErrors; /* rx failed due to ICV check */
+ uint32_t RxExcludedFrames; /* rx discarded, e.g. bssid */
+};
+int mwl_hal_gethwstats(struct mwl_hal *mh, struct mwl_hal_hwstats *);
+
+/*
+ * Set HT Guard Interval.
+ *
+ * GIType = 0: enable long and short GI
+ * GIType = 1: enable short GI
+ * GIType = 2: enable long GI
+ */
+int mwl_hal_sethtgi(struct mwl_hal_vap *, int GIType);
+
+/*
+ * Set Radio Configuration.
+ *
+ * onoff != 0 turns radio on; otherwise off.
+ * if radio is enabled, the preamble is set too.
+ */
+typedef enum {
+ WL_LONG_PREAMBLE = 1,
+ WL_SHORT_PREAMBLE = 3,
+ WL_AUTO_PREAMBLE = 5,
+} MWL_HAL_PREAMBLE;
+int mwl_hal_setradio(struct mwl_hal *mh, int onoff, MWL_HAL_PREAMBLE preamble);
+
+/*
+ * Set Antenna Configuration (legacy operation).
+ *
+ * The RX antenna can be selected using the the bitmask
+ * ant (bit 0 = antenna 1, bit 1 = antenna 2, etc.)
+ * (diversity?XXX)
+ */
+typedef enum {
+ WL_ANTENNATYPE_RX = 1,
+ WL_ANTENNATYPE_TX = 2,
+} MWL_HAL_ANTENNA;
+int mwl_hal_setantenna(struct mwl_hal *mh, MWL_HAL_ANTENNA dirSet, int ant);
+
+/*
+ * Set the threshold for using RTS on TX.
+ */
+int mwl_hal_setrtsthreshold(struct mwl_hal_vap *, int threshold);
+
+/*
+ * Set the adapter to operate in infrastructure mode.
+ */
+int mwl_hal_setinframode(struct mwl_hal_vap *);
+
+/*
+ * Set Radar Detection Configuration.
+ */
+typedef enum {
+ DR_DFS_DISABLE = 0,
+ DR_CHK_CHANNEL_AVAILABLE_START = 1,
+ DR_CHK_CHANNEL_AVAILABLE_STOP = 2,
+ DR_IN_SERVICE_MONITOR_START = 3
+} MWL_HAL_RADAR;
+int mwl_hal_setradardetection(struct mwl_hal *mh, MWL_HAL_RADAR action);
+/*
+ * Set the region code that selects the radar bin'ing agorithm.
+ */
+int mwl_hal_setregioncode(struct mwl_hal *mh, int regionCode);
+
+/*
+ * Initiate an 802.11h-based channel switch. The CSA ie
+ * is included in the next beacon(s) using the specified
+ * information and the firmware counts down until switch
+ * time after which it notifies the driver by delivering
+ * an interrupt with MACREG_A2HRIC_BIT_CHAN_SWITCH set in
+ * the cause register.
+ */
+int mwl_hal_setchannelswitchie(struct mwl_hal *,
+ const MWL_HAL_CHANNEL *nextchan, uint32_t mode, uint32_t count);
+
+/*
+ * Set regdomain code (IEEE SKU).
+ */
+enum {
+ DOMAIN_CODE_FCC = 0x10, /* USA */
+ DOMAIN_CODE_IC = 0x20, /* Canda */
+ DOMAIN_CODE_ETSI = 0x30, /* Europe */
+ DOMAIN_CODE_SPAIN = 0x31, /* Spain */
+ DOMAIN_CODE_FRANCE = 0x32, /* France */
+ DOMAIN_CODE_ETSI_131 = 0x130,/* ETSI w/ 1.3.1 radar type */
+ DOMAIN_CODE_MKK = 0x40, /* Japan */
+ DOMAIN_CODE_MKK2 = 0x41, /* Japan w/ 10MHz chan spacing */
+ DOMAIN_CODE_DGT = 0x80, /* Taiwan */
+ DOMAIN_CODE_AUS = 0x81, /* Australia */
+};
+
+/*
+ * Transmit rate control. Rate codes with bit 0x80 set are
+ * interpreted as MCS codes (this limits us to 0-127). The
+ * transmit rate can be set to a single fixed rate or can
+ * be configured to start at an initial rate and drop based
+ * on retry counts.
+ */
+typedef enum {
+ RATE_AUTO = 0, /* rate selected by firmware */
+ RATE_FIXED = 2, /* rate fixed */
+ RATE_FIXED_DROP = 1, /* rate starts fixed but may drop */
+} MWL_HAL_TXRATE_HANDLING;
+
+typedef struct {
+ uint8_t McastRate; /* rate for multicast frames */
+#define RATE_MCS 0x80 /* rate is an MCS index */
+ uint8_t MgtRate; /* rate for management frames */
+ struct {
+ uint8_t TryCount; /* try this many times */
+ uint8_t Rate; /* use this tx rate */
+ } RateSeries[4]; /* rate series */
+} MWL_HAL_TXRATE;
+
+int mwl_hal_settxrate(struct mwl_hal_vap *,
+ MWL_HAL_TXRATE_HANDLING handling, const MWL_HAL_TXRATE *rate);
+/* NB: hack for setting rates while scanning */
+int mwl_hal_settxrate_auto(struct mwl_hal *, const MWL_HAL_TXRATE *rate);
+
+/*
+ * Set the Slot Time Configuration.
+ * NB: usecs must either be 9 or 20 for now.
+ */
+int mwl_hal_setslottime(struct mwl_hal *mh, int usecs);
+
+/*
+ * Adjust current transmit power settings according to powerLevel.
+ * This translates to low/medium/high use of the current tx power rate tables.
+ */
+int mwl_hal_adjusttxpower(struct mwl_hal *, uint32_t powerLevel);
+/*
+ * Set the transmit power for the specified channel; the power
+ * is taken from the calibration data and capped according to
+ * the specified max tx power (in dBm).
+ */
+int mwl_hal_settxpower(struct mwl_hal *, const MWL_HAL_CHANNEL *,
+ uint8_t maxtxpow);
+
+/*
+ * Set the Multicast Address Filter.
+ * A packed array addresses is specified.
+ */
+#define MWL_HAL_MCAST_MAX 32
+int mwl_hal_setmcast(struct mwl_hal *mh, int nmc, const uint8_t macs[]);
+
+/*
+ * Crypto Configuration.
+ */
+typedef struct {
+ uint16_t pad;
+ uint16_t keyTypeId;
+#define KEY_TYPE_ID_WEP 0
+#define KEY_TYPE_ID_TKIP 1
+#define KEY_TYPE_ID_AES 2 /* AES-CCMP */
+ uint32_t keyFlags;
+#define KEY_FLAG_INUSE 0x00000001 /* indicate key is in use */
+#define KEY_FLAG_RXGROUPKEY 0x00000002 /* Group key for RX only */
+#define KEY_FLAG_TXGROUPKEY 0x00000004 /* Group key for TX */
+#define KEY_FLAG_PAIRWISE 0x00000008 /* pairwise */
+#define KEY_FLAG_RXONLY 0x00000010 /* only used for RX */
+#define KEY_FLAG_AUTHENTICATOR 0x00000020 /* Key is for Authenticator */
+#define KEY_FLAG_TSC_VALID 0x00000040 /* Sequence counters valid */
+#define KEY_FLAG_WEP_TXKEY 0x01000000 /* Tx key for WEP */
+#define KEY_FLAG_MICKEY_VALID 0x02000000 /* Tx/Rx MIC keys are valid */
+ uint32_t keyIndex; /* for WEP only; actual key index */
+ uint16_t keyLen; /* key size in bytes */
+ union { /* key material, keyLen gives size */
+ uint8_t wep[16]; /* enough for 128 bits */
+ uint8_t aes[16];
+ struct {
+ /* NB: group or pairwise key is determined by keyFlags */
+ uint8_t keyMaterial[16];
+ uint8_t txMic[8];
+ uint8_t rxMic[8];
+ struct {
+ uint16_t low;
+ uint32_t high;
+ } rsc;
+ struct {
+ uint16_t low;
+ uint32_t high;
+ } tsc;
+ } __packed tkip;
+ }__packed key;
+} __packed MWL_HAL_KEYVAL;
+
+/*
+ * Plumb a unicast/group key. The mac address identifies
+ * the station, use the broadcast address for group keys.
+ */
+int mwl_hal_keyset(struct mwl_hal_vap *, const MWL_HAL_KEYVAL *kv,
+ const uint8_t mac[6]);
+
+/*
+ * Plumb a unicast/group key. The mac address identifies
+ * the station, use the broadcast address for group keys.
+ */
+int mwl_hal_keyreset(struct mwl_hal_vap *, const MWL_HAL_KEYVAL *kv,
+ const uint8_t mac[6]);
+
+/*
+ * Set the MAC address.
+ */
+int mwl_hal_setmac(struct mwl_hal_vap *, const uint8_t addr[6]);
+
+/*
+ * Set the beacon frame contents. The firmware will modify the
+ * frame only to add CSA and WME ie's and to fill in dynamic fields
+ * such as the sequence #..
+ */
+int mwl_hal_setbeacon(struct mwl_hal_vap *, const void *, size_t);
+
+/*
+ * Handle power save operation for AP operation when offloaded to
+ * the host (SET_HW_SPEC_HOST_POWERSAVE). mwl_hal_setbss_powersave
+ * informs the firmware whether 1+ associated stations are in power
+ * save mode (it will then buffer mcast traffic). mwl_hal_setsta_powersave
+ * specifies a change in power save state for an associated station.
+ */
+int mwl_hal_setpowersave_bss(struct mwl_hal_vap *, uint8_t nsta);
+int mwl_hal_setpowersave_sta(struct mwl_hal_vap *, uint16_t aid, int ena);
+
+/*
+ * Set Association Configuration for station operation.
+ */
+int mwl_hal_setassocid(struct mwl_hal_vap *, const uint8_t bssId[6],
+ uint16_t assocId);
+
+/*
+ * Set the current channel.
+ */
+int mwl_hal_setchannel(struct mwl_hal *mh, const MWL_HAL_CHANNEL *c);
+
+/*
+ * A-MPDU Block Ack (BA) stream support. There are several
+ * streams that the driver must multiplex. Once assigned
+ * to a station the driver queues frames to a corresponding
+ * transmit queue and the firmware handles all the work.
+ *
+ * XXX no way to find out how many streams are supported
+ */
+typedef struct {
+ void *data[2]; /* opaque data */
+ int txq;
+} MWL_HAL_BASTREAM;
+
+const MWL_HAL_BASTREAM *mwl_hal_bastream_alloc(struct mwl_hal *mh,
+ int ba_type, const uint8_t Macaddr[16], uint8_t Tid,
+ uint8_t ParamInfo, void *, void *);
+const MWL_HAL_BASTREAM *mwl_hal_bastream_lookup(struct mwl_hal *mh, int s);
+int mwl_hal_bastream_create(struct mwl_hal *mh, const MWL_HAL_BASTREAM *,
+ int BarThrs, int WindowSize, uint16_t seqno);
+int mwl_hal_bastream_destroy(struct mwl_hal *mh, const MWL_HAL_BASTREAM *);
+int mwl_hal_bastream_get_seqno(struct mwl_hal *mh, const MWL_HAL_BASTREAM *,
+ uint16_t *pseqno);
+int mwl_hal_getwatchdogbitmap(struct mwl_hal *mh, uint8_t bitmap[1]);
+/* for sysctl hookup for debugging */
+void mwl_hal_setbastreams(struct mwl_hal *mh, int mask);
+int mwl_hal_getbastreams(struct mwl_hal *mh);
+
+/*
+ * Inform the firmware of a new association station.
+ * The address is the MAC address of the peer station.
+ * The AID is supplied sans the 0xc000 bits. The station
+ * ID is defined by the caller. The peer information must
+ * be supplied.
+ *
+ * NB: All values are in host byte order; any byte swapping
+ * is handled by the hal.
+ */
+typedef struct {
+ uint32_t LegacyRateBitMap;
+ uint32_t HTRateBitMap;
+ uint16_t CapInfo;
+ uint16_t HTCapabilitiesInfo;
+ uint8_t MacHTParamInfo;
+ uint8_t Rev;
+ struct {
+ uint8_t ControlChan;
+ uint8_t AddChan;
+ uint8_t OpMode;
+ uint8_t stbc;
+ } __packed AddHtInfo;
+} __packed MWL_HAL_PEERINFO;
+int mwl_hal_newstation(struct mwl_hal_vap *, const uint8_t addr[6],
+ uint16_t aid, uint16_t sid, const MWL_HAL_PEERINFO *,
+ int isQosSta, int wmeInfo);
+int mwl_hal_delstation(struct mwl_hal_vap *, const uint8_t addr[6]);
+
+/*
+ * Prod the firmware to age packets on station power
+ * save queues and reap frames on the tx aggregation q's.
+ */
+int mwl_hal_setkeepalive(struct mwl_hal *mh);
+
+typedef enum {
+ AP_MODE_B_ONLY = 1,
+ AP_MODE_G_ONLY = 2,
+ AP_MODE_MIXED = 3,
+ AP_MODE_N_ONLY = 4,
+ AP_MODE_BandN = 5,
+ AP_MODE_GandN = 6,
+ AP_MODE_BandGandN = 7,
+ AP_MODE_A_ONLY = 8,
+ AP_MODE_AandG = 10,
+ AP_MODE_AandN = 12,
+} MWL_HAL_APMODE;
+int mwl_hal_setapmode(struct mwl_hal_vap *, MWL_HAL_APMODE);
+
+/*
+ * Enable/disable firmware operation. mwl_hal_start is
+ * also used to sync state updates, e.g. beacon frame
+ * reconstruction after content changes.
+ */
+int mwl_hal_stop(struct mwl_hal_vap *);
+int mwl_hal_start(struct mwl_hal_vap *);
+
+/*
+ * Enable/disable 11g protection use. This call specifies
+ * the ERP information element flags to use.
+ */
+int mwl_hal_setgprot(struct mwl_hal *, int);
+
+/*
+ * Enable/disable WMM support.
+ */
+int mwl_hal_setwmm(struct mwl_hal *mh, int onoff);
+
+/*
+ * Configure WMM EDCA parameters for the specified h/w ring.
+ */
+int mwl_hal_setedcaparams(struct mwl_hal *mh, uint8_t qnum,
+ uint32_t CWmin, uint32_t CWmax, uint8_t AIFSN, uint16_t TXOPLimit);
+
+/*
+ * Configure rate adaptation for indooor/outdoor operation.
+ * XXX wtf?
+ */
+int mwl_hal_setrateadaptmode(struct mwl_hal *mh, uint16_t mode);
+
+typedef enum {
+ CSMODE_CONSERVATIVE = 0,
+ CSMODE_AGGRESSIVE = 1,
+ CSMODE_AUTO_ENA = 2,
+ CSMODE_AUTO_DIS = 3,
+} MWL_HAL_CSMODE;
+int mwl_hal_setcsmode(struct mwl_hal *mh, MWL_HAL_CSMODE csmode);
+
+/*
+ * Configure 11n protection on/off.
+ */
+typedef enum {
+ HTPROTECT_NONE = 0, /* disable */
+ HTPROTECT_OPT = 1, /* optional */
+ HTPROTECT_HT20 = 2, /* protect only HT20 */
+ HTPROTECT_HT2040 = 3, /* protect HT20/40 */
+ HTPROTECT_AUTO = 4, /* automatic */
+} MWL_HAL_HTPROTECT;
+int mwl_hal_setnprot(struct mwl_hal_vap *, MWL_HAL_HTPROTECT mode);
+/*
+ * Configure 11n protection mechanism for when protection is enabled.
+ */
+int mwl_hal_setnprotmode(struct mwl_hal_vap *, uint8_t mode);
+
+/*
+ * Enable/disable Marvell "turbo mode"".
+ */
+int mwl_hal_setoptimizationlevel(struct mwl_hal *mh, int onoff);
+
+/*
+ * Set MIMO Power Save handling for a station; the enable and mode
+ * values come directly from the Action frame.
+ */
+int mwl_hal_setmimops(struct mwl_hal *mh, const uint8_t addr[6],
+ uint8_t enable, uint8_t mode);
+
+/*
+ * Retrieve the region/country code from the EEPROM.
+ */
+int mwl_hal_getregioncode(struct mwl_hal *mh, uint8_t *countryCode);
+int mwl_hal_GetBeacon(struct mwl_hal *mh, uint8_t *pBcn, uint16_t *pLen);
+int mwl_hal_SetRifs(struct mwl_hal *mh, uint8_t QNum);
+
+/*
+ * Set/get promiscuous mode.
+ */
+int mwl_hal_setpromisc(struct mwl_hal *, int ena);
+int mwl_hal_getpromisc(struct mwl_hal *);
+
+/*
+ * Diagnostic interface. This is an open-ended interface that
+ * is opaque to applications. Diagnostic programs use this to
+ * retrieve internal data structures, etc. There is no guarantee
+ * that calling conventions for calls other than MWL_DIAG_REVS
+ * are stable between HAL releases; a diagnostic application must
+ * use the HAL revision information to deal with ABI/API differences.
+ */
+int mwl_hal_getdiagstate(struct mwl_hal *mh, int request,
+ const void *args, uint32_t argsize,
+ void **result, uint32_t *resultsize);
+
+int mwl_hal_fwload(struct mwl_hal *mh, void *fwargs);
+#endif /* _MWL_HAL_H_ */
diff --git a/sys/dev/mwl/mwlreg.h b/sys/dev/mwl/mwlreg.h
new file mode 100644
index 0000000000000..17ebd61580906
--- /dev/null
+++ b/sys/dev/mwl/mwlreg.h
@@ -0,0 +1,1352 @@
+/*-
+ * Copyright (c) 2007-2009 Sam Leffler, Errno Consulting
+ * Copyright (c) 2007-2009 Marvell Semiconductor, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * 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 NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Definitions for the Marvell Wireless LAN controller Hardware Access Layer.
+ */
+#ifndef _MWL_HALREG_H_
+#define _MWL_HALREG_H_
+
+#define MWL_ANT_INFO_SUPPORT /* per-antenna data in rx descriptor */
+
+#define MACREG_REG_TSF_LOW 0xa600 /* TSF lo */
+#define MACREG_REG_TSF_HIGH 0xa604 /* TSF hi */
+#define MACREG_REG_CHIP_REV 0xa814 /* chip rev */
+
+// Map to 0x80000000 (Bus control) on BAR0
+#define MACREG_REG_H2A_INTERRUPT_EVENTS 0x00000C18 // (From host to ARM)
+#define MACREG_REG_H2A_INTERRUPT_CAUSE 0x00000C1C // (From host to ARM)
+#define MACREG_REG_H2A_INTERRUPT_MASK 0x00000C20 // (From host to ARM)
+#define MACREG_REG_H2A_INTERRUPT_CLEAR_SEL 0x00000C24 // (From host to ARM)
+#define MACREG_REG_H2A_INTERRUPT_STATUS_MASK 0x00000C28 // (From host to ARM)
+
+#define MACREG_REG_A2H_INTERRUPT_EVENTS 0x00000C2C // (From ARM to host)
+#define MACREG_REG_A2H_INTERRUPT_CAUSE 0x00000C30 // (From ARM to host)
+#define MACREG_REG_A2H_INTERRUPT_MASK 0x00000C34 // (From ARM to host)
+#define MACREG_REG_A2H_INTERRUPT_CLEAR_SEL 0x00000C38 // (From ARM to host)
+#define MACREG_REG_A2H_INTERRUPT_STATUS_MASK 0x00000C3C // (From ARM to host)
+
+
+// Map to 0x80000000 on BAR1
+#define MACREG_REG_GEN_PTR 0x00000C10
+#define MACREG_REG_INT_CODE 0x00000C14
+#define MACREG_REG_SCRATCH 0x00000C40
+#define MACREG_REG_FW_PRESENT 0x0000BFFC
+
+#define MACREG_REG_PROMISCUOUS 0xA300
+
+// Bit definitio for MACREG_REG_A2H_INTERRUPT_CAUSE (A2HRIC)
+#define MACREG_A2HRIC_BIT_TX_DONE 0x00000001 // bit 0
+#define MACREG_A2HRIC_BIT_RX_RDY 0x00000002 // bit 1
+#define MACREG_A2HRIC_BIT_OPC_DONE 0x00000004 // bit 2
+#define MACREG_A2HRIC_BIT_MAC_EVENT 0x00000008 // bit 3
+#define MACREG_A2HRIC_BIT_RX_PROBLEM 0x00000010 // bit 4
+
+#define MACREG_A2HRIC_BIT_RADIO_OFF 0x00000020 // bit 5
+#define MACREG_A2HRIC_BIT_RADIO_ON 0x00000040 // bit 6
+
+#define MACREG_A2HRIC_BIT_RADAR_DETECT 0x00000080 // bit 7
+
+#define MACREG_A2HRIC_BIT_ICV_ERROR 0x00000100 // bit 8
+#define MACREG_A2HRIC_BIT_MIC_ERROR 0x00000200 // bit 9
+#define MACREG_A2HRIC_BIT_QUEUE_EMPTY 0x00004000
+#define MACREG_A2HRIC_BIT_QUEUE_FULL 0x00000800
+#define MACREG_A2HRIC_BIT_CHAN_SWITCH 0x00001000
+#define MACREG_A2HRIC_BIT_TX_WATCHDOG 0x00002000
+#define MACREG_A2HRIC_BIT_BA_WATCHDOG 0x00000400
+#define ISR_SRC_BITS ((MACREG_A2HRIC_BIT_RX_RDY) | \
+ (MACREG_A2HRIC_BIT_TX_DONE) | \
+ (MACREG_A2HRIC_BIT_OPC_DONE) | \
+ (MACREG_A2HRIC_BIT_MAC_EVENT)| \
+ (MACREG_A2HRIC_BIT_MIC_ERROR)| \
+ (MACREG_A2HRIC_BIT_ICV_ERROR)| \
+ (MACREG_A2HRIC_BIT_RADAR_DETECT)| \
+ (MACREG_A2HRIC_BIT_CHAN_SWITCH)| \
+ (MACREG_A2HRIC_BIT_TX_WATCHDOG)| \
+ (MACREG_A2HRIC_BIT_QUEUE_EMPTY)| \
+ (MACREG_A2HRIC_BIT_BA_WATCHDOG))
+
+#define MACREG_A2HRIC_BIT_MASK ISR_SRC_BITS
+
+
+// Bit definitio for MACREG_REG_H2A_INTERRUPT_CAUSE (H2ARIC)
+#define MACREG_H2ARIC_BIT_PPA_READY 0x00000001 // bit 0
+#define MACREG_H2ARIC_BIT_DOOR_BELL 0x00000002 // bit 1
+#define ISR_RESET (1<<15)
+
+// INT code register event definition
+#define MACREG_INT_CODE_CMD_FINISHED 0x00000005
+
+/*
+ * Host/Firmware Interface definitions.
+ */
+
+/**
+ * Define total number of TX queues in the shared memory.
+ * This count includes the EDCA queues, Block Ack queues, and HCCA queues
+ * In addition to this, there could be a management packet queue some
+ * time in the future
+ */
+#define NUM_EDCA_QUEUES 4
+#define NUM_HCCA_QUEUES 0
+#define NUM_BA_QUEUES 0
+#define NUM_MGMT_QUEUES 0
+#define TOTAL_TX_QUEUES \
+ (NUM_EDCA_QUEUES + NUM_HCCA_QUEUES + NUM_BA_QUEUES + NUM_MGMT_QUEUES)
+#define MAX_TXWCB_QUEUES TOTAL_TX_QUEUES
+#define MAX_RXWCB_QUEUES 1
+
+//=============================================================================
+// PUBLIC DEFINITIONS
+//=============================================================================
+
+#define RATE_INDEX_MAX_ARRAY 14
+#define WOW_MAX_STATION 32
+
+/*
+ * Hardware tx/rx descriptors.
+ *
+ * NB: tx descriptor size must match f/w expected size
+ * because f/w prefetch's the next descriptor linearly
+ * and doesn't chase the next pointer.
+ */
+struct mwl_txdesc {
+ uint32_t Status;
+#define EAGLE_TXD_STATUS_IDLE 0x00000000
+#define EAGLE_TXD_STATUS_USED 0x00000001
+#define EAGLE_TXD_STATUS_OK 0x00000001
+#define EAGLE_TXD_STATUS_OK_RETRY 0x00000002
+#define EAGLE_TXD_STATUS_OK_MORE_RETRY 0x00000004
+#define EAGLE_TXD_STATUS_MULTICAST_TX 0x00000008
+#define EAGLE_TXD_STATUS_BROADCAST_TX 0x00000010
+#define EAGLE_TXD_STATUS_FAILED_LINK_ERROR 0x00000020
+#define EAGLE_TXD_STATUS_FAILED_EXCEED_LIMIT 0x00000040
+#define EAGLE_TXD_STATUS_FAILED_XRETRY EAGLE_TXD_STATUS_FAILED_EXCEED_LIMIT
+#define EAGLE_TXD_STATUS_FAILED_AGING 0x00000080
+#define EAGLE_TXD_STATUS_FW_OWNED 0x80000000
+ uint8_t DataRate;
+ uint8_t TxPriority;
+ uint16_t QosCtrl;
+ uint32_t PktPtr;
+ uint16_t PktLen;
+ uint8_t DestAddr[6];
+ uint32_t pPhysNext;
+ uint32_t SapPktInfo;
+#define EAGLE_TXD_MODE_BONLY 1
+#define EAGLE_TXD_MODE_GONLY 2
+#define EAGLE_TXD_MODE_BG 3
+#define EAGLE_TXD_MODE_NONLY 4
+#define EAGLE_TXD_MODE_BN 5
+#define EAGLE_TXD_MODE_GN 6
+#define EAGLE_TXD_MODE_BGN 7
+#define EAGLE_TXD_MODE_AONLY 8
+#define EAGLE_TXD_MODE_AG 10
+#define EAGLE_TXD_MODE_AN 12
+ uint16_t Format;
+#define EAGLE_TXD_FORMAT 0x0001 /* frame format/rate */
+#define EAGLE_TXD_FORMAT_LEGACY 0x0000 /* legacy rate frame */
+#define EAGLE_TXD_FORMAT_HT 0x0001 /* HT rate frame */
+#define EAGLE_TXD_GI 0x0002 /* guard interval */
+#define EAGLE_TXD_GI_SHORT 0x0002 /* short guard interval */
+#define EAGLE_TXD_GI_LONG 0x0000 /* long guard interval */
+#define EAGLE_TXD_CHW 0x0004 /* channel width */
+#define EAGLE_TXD_CHW_20 0x0000 /* 20MHz channel width */
+#define EAGLE_TXD_CHW_40 0x0004 /* 40MHz channel width */
+#define EAGLE_TXD_RATE 0x01f8 /* tx rate (legacy)/ MCS */
+#define EAGLE_TXD_RATE_S 3
+#define EAGLE_TXD_ADV 0x0600 /* advanced coding */
+#define EAGLE_TXD_ADV_S 9
+#define EAGLE_TXD_ADV_NONE 0x0000
+#define EAGLE_TXD_ADV_LDPC 0x0200
+#define EAGLE_TXD_ADV_RS 0x0400
+/* NB: 3 is reserved */
+#define EAGLE_TXD_ANTENNA 0x1800 /* antenna select */
+#define EAGLE_TXD_ANTENNA_S 11
+#define EAGLE_TXD_EXTCHAN 0x6000 /* extension channel */
+#define EAGLE_TXD_EXTCHAN_S 13
+#define EAGLE_TXD_EXTCHAN_HI 0x0000 /* above */
+#define EAGLE_TXD_EXTCHAN_LO 0x2000 /* below */
+#define EAGLE_TXD_PREAMBLE 0x8000
+#define EAGLE_TXD_PREAMBLE_SHORT 0x8000 /* short preamble */
+#define EAGLE_TXD_PREAMBLE_LONG 0x0000 /* long preamble */
+ uint16_t pad; /* align to 4-byte boundary */
+#define EAGLE_TXD_FIXED_RATE 0x0100 /* get tx rate from Format */
+#define EAGLE_TXD_DONT_AGGR 0x0200 /* don't aggregate frame */
+} __packed;
+
+struct mwl_ant_info {
+ uint8_t rssi_a; /* RSSI for antenna A */
+ uint8_t rssi_b; /* RSSI for antenna B */
+ uint8_t rssi_c; /* RSSI for antenna C */
+ uint8_t rsvd1; /* Reserved */
+ uint8_t nf_a; /* Noise floor for antenna A */
+ uint8_t nf_b; /* Noise floor for antenna B */
+ uint8_t nf_c; /* Noise floor for antenna C */
+ uint8_t rsvd2; /* Reserved */
+ uint8_t nf; /* Noise floor */
+} __packed;
+
+struct mwl_rxdesc {
+ uint8_t RxControl; /* control element */
+#define EAGLE_RXD_CTRL_DRIVER_OWN 0x00
+#define EAGLE_RXD_CTRL_OS_OWN 0x04
+#define EAGLE_RXD_CTRL_DMA_OWN 0x80
+ uint8_t RSSI; /* received signal strengt indication */
+ uint8_t Status; /* status field w/ USED bit */
+#define EAGLE_RXD_STATUS_IDLE 0x00
+#define EAGLE_RXD_STATUS_OK 0x01
+#define EAGLE_RXD_STATUS_MULTICAST_RX 0x02
+#define EAGLE_RXD_STATUS_BROADCAST_RX 0x04
+#define EAGLE_RXD_STATUS_FRAGMENT_RX 0x08
+#define EAGLE_RXD_STATUS_GENERAL_DECRYPT_ERR 0xff
+#define EAGLE_RXD_STATUS_DECRYPT_ERR_MASK 0x80
+#define EAGLE_RXD_STATUS_TKIP_MIC_DECRYPT_ERR 0x02
+#define EAGLE_RXD_STATUS_WEP_ICV_DECRYPT_ERR 0x04
+#define EAGLE_RXD_STATUS_TKIP_ICV_DECRYPT_ERR 0x08
+ uint8_t Channel; /* channel # pkt received on */
+ uint16_t PktLen; /* total length of received data */
+ uint8_t SQ2; /* not used */
+ uint8_t Rate; /* received data rate */
+ uint32_t pPhysBuffData; /* physical address of payload data */
+ uint32_t pPhysNext; /* physical address of next RX desc */
+ uint16_t QosCtrl; /* received QosCtrl field variable */
+ uint16_t HtSig2; /* like name states */
+#ifdef MWL_ANT_INFO_SUPPORT
+ struct mwl_ant_info ai; /* antenna info */
+#endif
+} __packed;
+
+/*
+// Define OpMode for SoftAP/Station mode
+//
+// The following mode signature has to be written to PCI scratch register#0
+// right after successfully downloading the last block of firmware and
+// before waiting for firmware ready signature
+ */
+#define HostCmd_STA_MODE 0x5A
+#define HostCmd_SOFTAP_MODE 0xA5
+
+#define HostCmd_STA_FWRDY_SIGNATURE 0xF0F1F2F4
+#define HostCmd_SOFTAP_FWRDY_SIGNATURE 0xF1F2F4A5
+
+//***************************************************************************
+//***************************************************************************
+
+//***************************************************************************
+
+#define HostCmd_CMD_CODE_DNLD 0x0001
+#define HostCmd_CMD_GET_HW_SPEC 0x0003
+#define HostCmd_CMD_SET_HW_SPEC 0x0004
+#define HostCmd_CMD_MAC_MULTICAST_ADR 0x0010
+#define HostCmd_CMD_802_11_GET_STAT 0x0014
+#define HostCmd_CMD_MAC_REG_ACCESS 0x0019
+#define HostCmd_CMD_BBP_REG_ACCESS 0x001a
+#define HostCmd_CMD_RF_REG_ACCESS 0x001b
+#define HostCmd_CMD_802_11_RADIO_CONTROL 0x001c
+#define HostCmd_CMD_802_11_RF_TX_POWER 0x001e
+#define HostCmd_CMD_802_11_RF_ANTENNA 0x0020
+#define HostCmd_CMD_SET_BEACON 0x0100
+#define HostCmd_CMD_SET_AID 0x010d
+#define HostCmd_CMD_SET_RF_CHANNEL 0x010a
+#define HostCmd_CMD_SET_INFRA_MODE 0x010e
+#define HostCmd_CMD_SET_G_PROTECT_FLAG 0x010f
+#define HostCmd_CMD_802_11_RTS_THSD 0x0113
+#define HostCmd_CMD_802_11_SET_SLOT 0x0114
+
+#define HostCmd_CMD_802_11H_DETECT_RADAR 0x0120
+#define HostCmd_CMD_SET_WMM_MODE 0x0123
+#define HostCmd_CMD_HT_GUARD_INTERVAL 0x0124
+#define HostCmd_CMD_SET_FIXED_RATE 0x0126
+#define HostCmd_CMD_SET_LINKADAPT_CS_MODE 0x0129
+#define HostCmd_CMD_SET_MAC_ADDR 0x0202
+#define HostCmd_CMD_SET_RATE_ADAPT_MODE 0x0203
+#define HostCmd_CMD_GET_WATCHDOG_BITMAP 0x0205
+
+//SoftAP command code
+#define HostCmd_CMD_BSS_START 0x1100
+#define HostCmd_CMD_SET_NEW_STN 0x1111
+#define HostCmd_CMD_SET_KEEP_ALIVE 0x1112
+#define HostCmd_CMD_SET_APMODE 0x1114
+#define HostCmd_CMD_SET_SWITCH_CHANNEL 0x1121
+
+/*
+ @HWENCR@
+ Command to update firmware encryption keys.
+*/
+#define HostCmd_CMD_UPDATE_ENCRYPTION 0x1122
+/*
+ @11E-BA@
+ Command to create/destroy block ACK
+*/
+#define HostCmd_CMD_BASTREAM 0x1125
+#define HostCmd_CMD_SET_RIFS 0x1126
+#define HostCmd_CMD_SET_N_PROTECT_FLAG 0x1131
+#define HostCmd_CMD_SET_N_PROTECT_OPMODE 0x1132
+#define HostCmd_CMD_SET_OPTIMIZATION_LEVEL 0x1133
+#define HostCmd_CMD_GET_CALTABLE 0x1134
+#define HostCmd_CMD_SET_MIMOPSHT 0x1135
+#define HostCmd_CMD_GET_BEACON 0x1138
+#define HostCmd_CMD_SET_REGION_CODE 0x1139
+#define HostCmd_CMD_SET_POWERSAVESTATION 0x1140
+#define HostCmd_CMD_SET_TIM 0x1141
+#define HostCmd_CMD_GET_TIM 0x1142
+#define HostCmd_CMD_GET_SEQNO 0x1143
+
+/*
+// Define general result code for each command
+ */
+#define HostCmd_RESULT_OK 0x0000 // OK
+#define HostCmd_RESULT_ERROR 0x0001 // Genenral error
+#define HostCmd_RESULT_NOT_SUPPORT 0x0002 // Command is not valid
+#define HostCmd_RESULT_PENDING 0x0003 // Command is pending (will be processed)
+#define HostCmd_RESULT_BUSY 0x0004 // System is busy (command ignored)
+#define HostCmd_RESULT_PARTIAL_DATA 0x0005 // Data buffer is not big enough
+
+
+/*
+// Definition of action or option for each command
+//
+// Define general purpose action
+ */
+#define HostCmd_ACT_GEN_READ 0x0000
+#define HostCmd_ACT_GEN_WRITE 0x0001
+#define HostCmd_ACT_GEN_GET 0x0000
+#define HostCmd_ACT_GEN_SET 0x0001
+#define HostCmd_ACT_GEN_OFF 0x0000
+#define HostCmd_ACT_GEN_ON 0x0001
+
+#define HostCmd_ACT_DIFF_CHANNEL 0x0002
+#define HostCmd_ACT_GEN_SET_LIST 0x0002
+
+// Define action or option for HostCmd_FW_USE_FIXED_RATE
+#define HostCmd_ACT_USE_FIXED_RATE 0x0001
+#define HostCmd_ACT_NOT_USE_FIXED_RATE 0x0002
+
+// Define action or option for HostCmd_CMD_802_11_SET_WEP
+//#define HostCmd_ACT_ENABLE 0x0001 // Use MAC control for WEP on/off
+//#define HostCmd_ACT_DISABLE 0x0000
+#define HostCmd_ACT_ADD 0x0002
+#define HostCmd_ACT_REMOVE 0x0004
+#define HostCmd_ACT_USE_DEFAULT 0x0008
+
+#define HostCmd_TYPE_WEP_40_BIT 0x0001 // 40 bit
+#define HostCmd_TYPE_WEP_104_BIT 0x0002 // 104 bit
+#define HostCmd_TYPE_WEP_128_BIT 0x0003 // 128 bit
+#define HostCmd_TYPE_WEP_TX_KEY 0x0004 // TX WEP
+
+#define HostCmd_NUM_OF_WEP_KEYS 4
+
+#define HostCmd_WEP_KEY_INDEX_MASK 0x3fffffff
+
+
+// Define action or option for HostCmd_CMD_802_11_RESET
+#define HostCmd_ACT_HALT 0x0001
+#define HostCmd_ACT_RESTART 0x0002
+
+// Define action or option for HostCmd_CMD_802_11_RADIO_CONTROL
+#define HostCmd_TYPE_AUTO_PREAMBLE 0x0001
+#define HostCmd_TYPE_SHORT_PREAMBLE 0x0002
+#define HostCmd_TYPE_LONG_PREAMBLE 0x0003
+
+// Define action or option for CMD_802_11_RF_CHANNEL
+#define HostCmd_TYPE_802_11A 0x0001
+#define HostCmd_TYPE_802_11B 0x0002
+
+// Define action or option for HostCmd_CMD_802_11_RF_TX_POWER
+#define HostCmd_ACT_TX_POWER_OPT_SET_HIGH 0x0003
+#define HostCmd_ACT_TX_POWER_OPT_SET_MID 0x0002
+#define HostCmd_ACT_TX_POWER_OPT_SET_LOW 0x0001
+#define HostCmd_ACT_TX_POWER_OPT_SET_AUTO 0x0000
+
+#define HostCmd_ACT_TX_POWER_LEVEL_MIN 0x000e // in dbm
+#define HostCmd_ACT_TX_POWER_LEVEL_GAP 0x0001 // in dbm
+// Define action or option for HostCmd_CMD_802_11_DATA_RATE
+#define HostCmd_ACT_SET_TX_AUTO 0x0000
+#define HostCmd_ACT_SET_TX_FIX_RATE 0x0001
+#define HostCmd_ACT_GET_TX_RATE 0x0002
+
+#define HostCmd_ACT_SET_RX 0x0001
+#define HostCmd_ACT_SET_TX 0x0002
+#define HostCmd_ACT_SET_BOTH 0x0003
+#define HostCmd_ACT_GET_RX 0x0004
+#define HostCmd_ACT_GET_TX 0x0008
+#define HostCmd_ACT_GET_BOTH 0x000c
+
+#define TYPE_ANTENNA_DIVERSITY 0xffff
+
+// Define action or option for HostCmd_CMD_802_11_PS_MODE
+#define HostCmd_TYPE_CAM 0x0000
+#define HostCmd_TYPE_MAX_PSP 0x0001
+#define HostCmd_TYPE_FAST_PSP 0x0002
+
+#define HostCmd_CMD_SET_EDCA_PARAMS 0x0115
+
+//=============================================================================
+// HOST COMMAND DEFINITIONS
+//=============================================================================
+
+//
+// Definition of data structure for each command
+//
+// Define general data structure
+typedef struct {
+ uint16_t Cmd;
+ uint16_t Length;
+#ifdef MWL_MBSS_SUPPORT
+ uint8_t SeqNum;
+ uint8_t MacId;
+#else
+ uint16_t SeqNum;
+#endif
+ uint16_t Result;
+} __packed FWCmdHdr;
+
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint8_t Version; // HW revision
+ uint8_t HostIf; // Host interface
+ uint16_t NumOfMCastAdr; // Max. number of Multicast address FW can handle
+ uint8_t PermanentAddr[6]; // MAC address
+ uint16_t RegionCode; // Region Code
+ uint32_t FWReleaseNumber; // 4 byte of FW release number
+ uint32_t ulFwAwakeCookie; // Firmware awake cookie
+ uint32_t DeviceCaps; // Device capabilities (see above)
+ uint32_t RxPdWrPtr; // Rx shared memory queue
+ uint32_t NumTxQueues; // # TX queues in WcbBase array
+ uint32_t WcbBase[MAX_TXWCB_QUEUES]; // TX WCB Rings
+ uint32_t Flags;
+#define SET_HW_SPEC_DISABLEMBSS 0x08
+#define SET_HW_SPEC_HOSTFORM_BEACON 0x10
+#define SET_HW_SPEC_HOSTFORM_PROBERESP 0x20
+#define SET_HW_SPEC_HOST_POWERSAVE 0x40
+#define SET_HW_SPEC_HOSTENCRDECR_MGMT 0x80
+ uint32_t TxWcbNumPerQueue;
+ uint32_t TotalRxWcb;
+} __packed HostCmd_DS_SET_HW_SPEC;
+
+typedef struct {
+ FWCmdHdr CmdHdr;
+ u_int8_t Version; /* version of the HW */
+ u_int8_t HostIf; /* host interface */
+ u_int16_t NumOfWCB; /* Max. number of WCB FW can handle */
+ u_int16_t NumOfMCastAddr; /* MaxNbr of MC addresses FW can handle */
+ u_int8_t PermanentAddr[6]; /* MAC address programmed in HW */
+ u_int16_t RegionCode;
+ u_int16_t NumberOfAntenna; /* Number of antenna used */
+ u_int32_t FWReleaseNumber; /* 4 byte of FW release number */
+ u_int32_t WcbBase0;
+ u_int32_t RxPdWrPtr;
+ u_int32_t RxPdRdPtr;
+ u_int32_t ulFwAwakeCookie;
+ u_int32_t WcbBase1;
+ u_int32_t WcbBase2;
+ u_int32_t WcbBase3;
+} __packed HostCmd_DS_GET_HW_SPEC;
+
+typedef struct {
+ FWCmdHdr CmdHdr;
+ u_int32_t Enable; /* FALSE: Disable or TRUE: Enable */
+} __packed HostCmd_DS_BSS_START;
+
+
+typedef struct {
+ u_int8_t ElemId;
+ u_int8_t Len;
+ u_int8_t OuiType[4]; /* 00:50:f2:01 */
+ u_int8_t Ver[2];
+ u_int8_t GrpKeyCipher[4];
+ u_int8_t PwsKeyCnt[2];
+ u_int8_t PwsKeyCipherList[4];
+ u_int8_t AuthKeyCnt[2];
+ u_int8_t AuthKeyList[4];
+} __packed RsnIE_t;
+
+typedef struct {
+ u_int8_t ElemId;
+ u_int8_t Len;
+ u_int8_t Ver[2];
+ u_int8_t GrpKeyCipher[4];
+ u_int8_t PwsKeyCnt[2];
+ u_int8_t PwsKeyCipherList[4];
+ u_int8_t AuthKeyCnt[2];
+ u_int8_t AuthKeyList[4];
+ u_int8_t RsnCap[2];
+} __packed Rsn48IE_t;
+
+typedef struct {
+ u_int8_t ElementId;
+ u_int8_t Len;
+ u_int8_t CfpCnt;
+ u_int8_t CfpPeriod;
+ u_int16_t CfpMaxDuration;
+ u_int16_t CfpDurationRemaining;
+} __packed CfParams_t;
+
+typedef struct {
+ u_int8_t ElementId;
+ u_int8_t Len;
+ u_int16_t AtimWindow;
+} __packed IbssParams_t;
+
+typedef union {
+ CfParams_t CfParamSet;
+ IbssParams_t IbssParamSet;
+} __packed SsParams_t;
+
+typedef struct {
+ u_int8_t ElementId;
+ u_int8_t Len;
+ u_int16_t DwellTime;
+ u_int8_t HopSet;
+ u_int8_t HopPattern;
+ u_int8_t HopIndex;
+} __packed FhParams_t;
+
+typedef struct {
+ u_int8_t ElementId;
+ u_int8_t Len;
+ u_int8_t CurrentChan;
+} __packed DsParams_t;
+
+typedef union {
+ FhParams_t FhParamSet;
+ DsParams_t DsParamSet;
+} __packed PhyParams_t;
+
+typedef struct {
+ u_int8_t FirstChannelNum;
+ u_int8_t NumOfChannels;
+ u_int8_t MaxTxPwrLevel;
+} __packed ChannelInfo_t;
+
+typedef struct {
+ u_int8_t ElementId;
+ u_int8_t Len;
+ u_int8_t CountryStr[3];
+ ChannelInfo_t ChannelInfo[40];
+} __packed Country_t;
+
+typedef struct {
+ u_int8_t AIFSN : 4;
+ u_int8_t ACM : 1;
+ u_int8_t ACI : 2;
+ u_int8_t rsvd : 1;
+
+}__packed ACIAIFSN_field_t;
+
+typedef struct {
+ u_int8_t ECW_min : 4;
+ u_int8_t ECW_max : 4;
+}__packed ECWmin_max_field_t;
+
+typedef struct {
+ ACIAIFSN_field_t ACI_AIFSN;
+ ECWmin_max_field_t ECW_min_max;
+ u_int16_t TXOP_lim;
+}__packed ACparam_rcd_t;
+
+typedef struct {
+ u_int8_t ElementId;
+ u_int8_t Len;
+ u_int8_t OUI[3];
+ u_int8_t Type;
+ u_int8_t Subtype;
+ u_int8_t version;
+ u_int8_t rsvd;
+ ACparam_rcd_t AC_BE;
+ ACparam_rcd_t AC_BK;
+ ACparam_rcd_t AC_VI;
+ ACparam_rcd_t AC_VO;
+} __packed WMM_param_elem_t ;
+
+typedef struct {
+#ifdef MWL_MBSS_SUPPORT
+ u_int8_t StaMacAddr[6];
+#endif
+ u_int8_t SsId[32];
+ u_int8_t BssType;
+ u_int16_t BcnPeriod;
+ u_int8_t DtimPeriod;
+ SsParams_t SsParamSet;
+ PhyParams_t PhyParamSet;
+ u_int16_t ProbeDelay;
+ u_int16_t CapInfo; /* see below */
+ u_int8_t BssBasicRateSet[14];
+ u_int8_t OpRateSet[14];
+ RsnIE_t RsnIE;
+ Rsn48IE_t Rsn48IE;
+ WMM_param_elem_t WMMParam;
+ Country_t Country;
+ u_int32_t ApRFType; /* 0->B, 1->G, 2->Mixed, 3->A, 4->11J */
+} __packed StartCmd_t;
+
+#define HostCmd_CAPINFO_DEFAULT 0x0000
+#define HostCmd_CAPINFO_ESS 0x0001
+#define HostCmd_CAPINFO_IBSS 0x0002
+#define HostCmd_CAPINFO_CF_POLLABLE 0x0004
+#define HostCmd_CAPINFO_CF_REQUEST 0x0008
+#define HostCmd_CAPINFO_PRIVACY 0x0010
+#define HostCmd_CAPINFO_SHORT_PREAMBLE 0x0020
+#define HostCmd_CAPINFO_PBCC 0x0040
+#define HostCmd_CAPINFO_CHANNEL_AGILITY 0x0080
+#define HostCmd_CAPINFO_SHORT_SLOT 0x0400
+#define HostCmd_CAPINFO_DSSS_OFDM 0x2000
+
+typedef struct {
+ FWCmdHdr CmdHdr;
+ StartCmd_t StartCmd;
+} __packed HostCmd_DS_AP_BEACON;
+
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint16_t FrmBodyLen;
+ uint8_t FrmBody[1]; /* NB: variable length */
+} __packed HostCmd_DS_SET_BEACON;
+
+// Define data structure for HostCmd_CMD_MAC_MULTICAST_ADR
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint16_t Action;
+ uint16_t NumOfAdrs;
+#define MWL_HAL_MCAST_MAX 32
+ uint8_t MACList[6*32];
+} __packed HostCmd_DS_MAC_MULTICAST_ADR;
+
+// Indicate to FW the current state of AP ERP info
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint32_t GProtectFlag;
+} __packed HostCmd_FW_SET_G_PROTECT_FLAG;
+
+typedef struct {
+ FWCmdHdr CmdHdr;
+} __packed HostCmd_FW_SET_INFRA_MODE;
+
+// Define data structure for HostCmd_CMD_802_11_RF_CHANNEL
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint16_t Action;
+ uint8_t CurrentChannel; /* channel # */
+ uint32_t ChannelFlags; /* see below */
+} __packed HostCmd_FW_SET_RF_CHANNEL;
+
+/* bits 0-5 specify frequency band */
+#define FREQ_BAND_2DOT4GHZ 0x0001
+#define FREQ_BAND_4DOT9GHZ 0x0002 /* XXX not implemented */
+#define FREQ_BAND_5GHZ 0x0004
+#define FREQ_BAND_5DOT2GHZ 0x0008 /* XXX not implemented */
+/* bits 6-10 specify channel width */
+#define CH_AUTO_WIDTH 0x0000 /* XXX not used? */
+#define CH_10_MHz_WIDTH 0x0040
+#define CH_20_MHz_WIDTH 0x0080
+#define CH_40_MHz_WIDTH 0x0100
+/* bits 11-12 specify extension channel */
+#define EXT_CH_NONE 0x0000 /* no extension channel */
+#define EXT_CH_ABOVE_CTRL_CH 0x0800 /* extension channel above */
+#define EXT_CH_AUTO 0x1000 /* XXX not used? */
+#define EXT_CH_BELOW_CTRL_CH 0x1800 /* extension channel below */
+/* bits 13-31 are reserved */
+
+#define FIXED_RATE_WITH_AUTO_RATE_DROP 0
+#define FIXED_RATE_WITHOUT_AUTORATE_DROP 1
+
+#define LEGACY_RATE_TYPE 0
+#define HT_RATE_TYPE 1
+
+#define RETRY_COUNT_VALID 0
+#define RETRY_COUNT_INVALID 1
+
+typedef struct {
+ // lower rate after the retry count
+ uint32_t FixRateType; //0: legacy, 1: HT
+ uint32_t RetryCountValid; //0: retry count is not valid, 1: use retry count specified
+} __packed FIX_RATE_FLAG;
+
+typedef struct {
+ FIX_RATE_FLAG FixRateTypeFlags;
+ uint32_t FixedRate; // legacy rate(not index) or an MCS code.
+ uint32_t RetryCount;
+} __packed FIXED_RATE_ENTRY;
+
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint32_t Action; //HostCmd_ACT_GEN_GET 0x0000
+ //HostCmd_ACT_GEN_SET 0x0001
+ //HostCmd_ACT_NOT_USE_FIXED_RATE 0x0002
+ uint32_t AllowRateDrop; // use fixed rate specified but firmware can drop to
+ uint32_t EntryCount;
+ FIXED_RATE_ENTRY FixedRateTable[4];
+ uint8_t MulticastRate;
+ uint8_t MultiRateTxType;
+ uint8_t ManagementRate;
+} __packed HostCmd_FW_USE_FIXED_RATE;
+
+typedef struct {
+ uint32_t AllowRateDrop;
+ uint32_t EntryCount;
+ FIXED_RATE_ENTRY FixedRateTable[4];
+} __packed USE_FIXED_RATE_INFO;
+
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint32_t Action;
+ uint32_t GIType;
+#define GI_TYPE_LONG 0x0001
+#define GI_TYPE_SHORT 0x0002
+} __packed HostCmd_FW_HT_GUARD_INTERVAL;
+
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint32_t Action;
+ uint8_t RxAntennaMap;
+ uint8_t TxAntennaMap;
+} __packed HostCmd_FW_HT_MIMO_CONFIG;
+
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint16_t Action;
+ uint8_t Slot; // Slot=0 if regular, Slot=1 if short.
+} __packed HostCmd_FW_SET_SLOT;
+
+
+// Define data structure for HostCmd_CMD_802_11_GET_STAT
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint32_t TxRetrySuccesses;
+ uint32_t TxMultipleRetrySuccesses;
+ uint32_t TxFailures;
+ uint32_t RTSSuccesses;
+ uint32_t RTSFailures;
+ uint32_t AckFailures;
+ uint32_t RxDuplicateFrames;
+ uint32_t FCSErrorCount;
+ uint32_t TxWatchDogTimeouts;
+ uint32_t RxOverflows; //used
+ uint32_t RxFragErrors; //used
+ uint32_t RxMemErrors; //used
+ uint32_t PointerErrors; //used
+ uint32_t TxUnderflows; //used
+ uint32_t TxDone;
+ uint32_t TxDoneBufTryPut;
+ uint32_t TxDoneBufPut;
+ uint32_t Wait4TxBuf; // Put size of requested buffer in here
+ uint32_t TxAttempts;
+ uint32_t TxSuccesses;
+ uint32_t TxFragments;
+ uint32_t TxMulticasts;
+ uint32_t RxNonCtlPkts;
+ uint32_t RxMulticasts;
+ uint32_t RxUndecryptableFrames;
+ uint32_t RxICVErrors;
+ uint32_t RxExcludedFrames;
+} __packed HostCmd_DS_802_11_GET_STAT;
+
+
+// Define data structure for HostCmd_CMD_MAC_REG_ACCESS
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint16_t Action;
+ uint16_t Offset;
+ uint32_t Value;
+ uint16_t Reserved;
+} __packed HostCmd_DS_MAC_REG_ACCESS;
+
+// Define data structure for HostCmd_CMD_BBP_REG_ACCESS
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint16_t Action;
+ uint16_t Offset;
+ uint8_t Value;
+ uint8_t Reserverd[3];
+} __packed HostCmd_DS_BBP_REG_ACCESS;
+
+// Define data structure for HostCmd_CMD_RF_REG_ACCESS
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint16_t Action;
+ uint16_t Offset;
+ uint8_t Value;
+ uint8_t Reserverd[3];
+} __packed HostCmd_DS_RF_REG_ACCESS;
+
+
+// Define data structure for HostCmd_CMD_802_11_RADIO_CONTROL
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint16_t Action;
+ uint16_t Control; // @bit0: 1/0,on/off, @bit1: 1/0, long/short @bit2: 1/0,auto/fix
+ uint16_t RadioOn;
+} __packed HostCmd_DS_802_11_RADIO_CONTROL;
+
+
+#define TX_POWER_LEVEL_TOTAL 8
+// Define data structure for HostCmd_CMD_802_11_RF_TX_POWER
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint16_t Action;
+ uint16_t SupportTxPowerLevel;
+ uint16_t CurrentTxPowerLevel;
+ uint16_t Reserved;
+ uint16_t PowerLevelList[TX_POWER_LEVEL_TOTAL];
+} __packed HostCmd_DS_802_11_RF_TX_POWER;
+
+// Define data structure for HostCmd_CMD_802_11_RF_ANTENNA
+typedef struct _HostCmd_DS_802_11_RF_ANTENNA {
+ FWCmdHdr CmdHdr;
+ uint16_t Action;
+ uint16_t AntennaMode; // Number of antennas or 0xffff(diversity)
+} __packed HostCmd_DS_802_11_RF_ANTENNA;
+
+// Define data structure for HostCmd_CMD_802_11_PS_MODE
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint16_t Action;
+ uint16_t PowerMode; // CAM, Max.PSP or Fast PSP
+} __packed HostCmd_DS_802_11_PS_MODE;
+
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint16_t Action;
+ uint16_t Threshold;
+} __packed HostCmd_DS_802_11_RTS_THSD;
+
+// used for stand alone bssid sets/clears
+typedef struct {
+ FWCmdHdr CmdHdr;
+#ifdef MWL_MBSS_SUPPORT
+ uint16_t MacType;
+#define WL_MAC_TYPE_PRIMARY_CLIENT 0
+#define WL_MAC_TYPE_SECONDARY_CLIENT 1
+#define WL_MAC_TYPE_PRIMARY_AP 2
+#define WL_MAC_TYPE_SECONDARY_AP 3
+#endif
+ uint8_t MacAddr[6];
+} __packed HostCmd_DS_SET_MAC,
+ HostCmd_FW_SET_BSSID,
+ HostCmd_FW_SET_MAC;
+
+// Indicate to FW to send out PS Poll
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint32_t PSPoll;
+} __packed HostCmd_FW_TX_POLL;
+
+// used for AID sets/clears
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint16_t AssocID;
+ uint8_t MacAddr[6]; //AP's Mac Address(BSSID)
+ uint32_t GProtection;
+ uint8_t ApRates[ RATE_INDEX_MAX_ARRAY];
+} __packed HostCmd_FW_SET_AID;
+
+typedef struct {
+ uint32_t LegacyRateBitMap;
+ uint32_t HTRateBitMap;
+ uint16_t CapInfo;
+ uint16_t HTCapabilitiesInfo;
+ uint8_t MacHTParamInfo;
+ uint8_t Rev;
+ struct {
+ uint8_t ControlChan;
+ uint8_t AddChan;
+ uint16_t OpMode;
+ uint16_t stbc;
+ } __packed AddHtInfo;
+} __packed PeerInfo_t;
+
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint16_t AID;
+ uint8_t MacAddr[6];
+ uint16_t StnId;
+ uint16_t Action;
+ uint16_t Reserved;
+ PeerInfo_t PeerInfo;
+ uint8_t Qosinfo;
+ uint8_t isQosSta;
+} __packed HostCmd_FW_SET_NEW_STN;
+
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint8_t tick;
+} __packed HostCmd_FW_SET_KEEP_ALIVE_TICK;
+
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint8_t QNum;
+} __packed HostCmd_FW_SET_RIFS;
+
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint8_t ApMode;
+} __packed HostCmd_FW_SET_APMODE;
+
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint16_t Action; // see following
+ uint16_t RadarTypeCode;
+} __packed HostCmd_802_11h_Detect_Radar;
+
+#define DR_DFS_DISABLE 0
+#define DR_CHK_CHANNEL_AVAILABLE_START 1
+#define DR_CHK_CHANNEL_AVAILABLE_STOP 2
+#define DR_IN_SERVICE_MONITOR_START 3
+
+//New Structure for Update Tim 30/9/2003
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint16_t Aid;
+ uint32_t Set;
+} __packed HostCmd_UpdateTIM;
+
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint32_t SsidBroadcastEnable;
+} __packed HostCmd_SSID_BROADCAST;
+
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint32_t WdsEnable;
+} __packed HostCmd_WDS;
+
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint32_t Next11hChannel;
+ uint32_t Mode;
+ uint32_t InitialCount;
+ uint32_t ChannelFlags ;
+} __packed HostCmd_SET_SWITCH_CHANNEL;
+
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint32_t SpectrumMgmt;
+} __packed HostCmd_SET_SPECTRUM_MGMT;
+
+typedef struct {
+ FWCmdHdr CmdHdr;
+ int32_t PowerConstraint;
+} __packed HostCmd_SET_POWER_CONSTRAINT;
+
+typedef struct {
+ uint8_t FirstChannelNo;
+ uint8_t NoofChannel;
+ uint8_t MaxTransmitPw;
+} __packed DomainChannelEntry;
+
+typedef struct {
+ uint8_t CountryString[3];
+ uint8_t GChannelLen;
+ DomainChannelEntry DomainEntryG[1]; /** Assume only 1 G zone **/
+ uint8_t AChannelLen;
+ DomainChannelEntry DomainEntryA[20]; /** Assume max of 5 A zone **/
+} __packed DomainCountryInfo;
+
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint32_t Action ; // 0 -> unset, 1 ->set
+ DomainCountryInfo DomainInfo ;
+} __packed HostCmd_SET_COUNTRY_INFO;
+
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint16_t regionCode ;
+} __packed HostCmd_SET_REGIONCODE_INFO;
+
+// for HostCmd_CMD_SET_WMM_MODE
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint16_t Action; // 0->unset, 1->set
+} __packed HostCmd_FW_SetWMMMode;
+
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint16_t Action; // 0->unset, 1->set
+ uint16_t IeListLen;
+ uint8_t IeList[200];
+} __packed HostCmd_FW_SetIEs;
+
+#define EDCA_PARAM_SIZE 18
+#define BA_PARAM_SIZE 2
+
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint16_t Action; //0 = get all, 0x1 =set CWMin/Max, 0x2 = set TXOP , 0x4 =set AIFSN
+ uint16_t TxOP; // in unit of 32 us
+ uint32_t CWMax; // 0~15
+ uint32_t CWMin; // 0~15
+ uint8_t AIFSN;
+ uint8_t TxQNum; // Tx Queue number.
+} __packed HostCmd_FW_SET_EDCA_PARAMS;
+
+/******************************************************************************
+ @HWENCR@
+ Hardware Encryption related data structures and constant definitions.
+ Note that all related changes are marked with the @HWENCR@ tag.
+*******************************************************************************/
+
+#define MAX_ENCR_KEY_LENGTH 16 /* max 128 bits - depends on type */
+#define MIC_KEY_LENGTH 8 /* size of Tx/Rx MIC key - 8 bytes*/
+
+#define ENCR_KEY_TYPE_ID_WEP 0x00 /* Key type is WEP */
+#define ENCR_KEY_TYPE_ID_TKIP 0x01 /* Key type is TKIP */
+#define ENCR_KEY_TYPE_ID_AES 0x02 /* Key type is AES-CCMP */
+
+/* flags used in structure - same as driver EKF_XXX flags */
+#define ENCR_KEY_FLAG_INUSE 0x00000001 /* indicate key is in use */
+#define ENCR_KEY_FLAG_RXGROUPKEY 0x00000002 /* Group key for RX only */
+#define ENCR_KEY_FLAG_TXGROUPKEY 0x00000004 /* Group key for TX */
+#define ENCR_KEY_FLAG_PAIRWISE 0x00000008 /* pairwise */
+#define ENCR_KEY_FLAG_RXONLY 0x00000010 /* only used for RX */
+// These flags are new additions - for hardware encryption commands only.
+#define ENCR_KEY_FLAG_AUTHENTICATOR 0x00000020 /* Key is for Authenticator */
+#define ENCR_KEY_FLAG_TSC_VALID 0x00000040 /* Sequence counters valid */
+#define ENCR_KEY_FLAG_WEP_TXKEY 0x01000000 /* Tx key for WEP */
+#define ENCR_KEY_FLAG_MICKEY_VALID 0x02000000 /* Tx/Rx MIC keys are valid */
+
+/*
+ UPDATE_ENCRYPTION command action type.
+*/
+typedef enum {
+ // request to enable/disable HW encryption
+ EncrActionEnableHWEncryption,
+ // request to set encryption key
+ EncrActionTypeSetKey,
+ // request to remove one or more keys
+ EncrActionTypeRemoveKey,
+ EncrActionTypeSetGroupKey
+} ENCR_ACTION_TYPE;
+
+/*
+ Key material definitions (for WEP, TKIP, & AES-CCMP)
+*/
+
+/*
+ WEP Key material definition
+ ----------------------------
+ WEPKey --> An array of 'MAX_ENCR_KEY_LENGTH' bytes.
+ Note that we do not support 152bit WEP keys
+*/
+typedef struct {
+ // WEP key material (max 128bit)
+ uint8_t KeyMaterial[ MAX_ENCR_KEY_LENGTH ];
+} __packed WEP_TYPE_KEY;
+
+/*
+ TKIP Key material definition
+ ----------------------------
+ This structure defines TKIP key material. Note that
+ the TxMicKey and RxMicKey may or may not be valid.
+*/
+/* TKIP Sequence counter - 24 bits */
+/* Incremented on each fragment MPDU */
+typedef struct {
+ uint16_t low;
+ uint32_t high;
+} __packed ENCR_TKIPSEQCNT;
+
+typedef struct {
+ // TKIP Key material. Key type (group or pairwise key) is
+ // determined by flags in KEY_PARAM_SET structure.
+ uint8_t KeyMaterial[ MAX_ENCR_KEY_LENGTH ];
+ uint8_t TkipTxMicKey[ MIC_KEY_LENGTH ];
+ uint8_t TkipRxMicKey[ MIC_KEY_LENGTH ];
+ ENCR_TKIPSEQCNT TkipRsc;
+ ENCR_TKIPSEQCNT TkipTsc;
+} __packed TKIP_TYPE_KEY;
+
+/*
+ AES-CCMP Key material definition
+ --------------------------------
+ This structure defines AES-CCMP key material.
+*/
+typedef struct {
+ // AES Key material
+ uint8_t KeyMaterial[ MAX_ENCR_KEY_LENGTH ];
+} __packed AES_TYPE_KEY;
+
+/*
+ Encryption key definition.
+ --------------------------
+ This structure provides all required/essential
+ information about the key being set/removed.
+*/
+typedef struct {
+ uint16_t Length; // Total length of this structure
+ uint16_t KeyTypeId; // Key type - WEP, TKIP or AES-CCMP.
+ uint32_t KeyInfo; // key flags (ENCR_KEY_FLAG_XXX_
+ uint32_t KeyIndex; // For WEP only - actual key index
+ uint16_t KeyLen; // Size of the key
+ union { // Key material (variable size array)
+ WEP_TYPE_KEY WepKey;
+ TKIP_TYPE_KEY TkipKey;
+ AES_TYPE_KEY AesKey;
+ }__packed Key;
+#ifdef MWL_MBSS_SUPPORT
+ uint8_t Macaddr[6];
+#endif
+} __packed KEY_PARAM_SET;
+
+/*
+ HostCmd_FW_UPDATE_ENCRYPTION
+ ----------------------------
+ Define data structure for updating firmware encryption keys.
+
+*/
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint32_t ActionType; // ENCR_ACTION_TYPE
+ uint32_t DataLength; // size of the data buffer attached.
+#ifdef MWL_MBSS_SUPPORT
+ uint8_t macaddr[6];
+#endif
+ uint8_t ActionData[1];
+} __packed HostCmd_FW_UPDATE_ENCRYPTION;
+
+
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint32_t ActionType; // ENCR_ACTION_TYPE
+ uint32_t DataLength; // size of the data buffer attached.
+ KEY_PARAM_SET KeyParam;
+#ifndef MWL_MBSS_SUPPORT
+ uint8_t Macaddr[8]; /* XXX? */
+#endif
+} __packed HostCmd_FW_UPDATE_ENCRYPTION_SET_KEY;
+
+typedef struct {
+ // Rate flags - see above.
+ uint32_t Flags;
+ // Rate in 500Kbps units.
+ uint8_t RateKbps;
+ // 802.11 rate to conversion table index value.
+ // This is the value required by the firmware/hardware.
+ uint16_t RateCodeToIndex;
+}__packed RATE_INFO;
+
+/*
+ UPDATE_STADB command action type.
+*/
+typedef enum {
+ // request to add entry to stainfo db
+ StaInfoDbActionAddEntry,
+ // request to modify peer entry
+ StaInfoDbActionModifyEntry,
+ // request to remove peer from stainfo db
+ StaInfoDbActionRemoveEntry
+}__packed STADB_ACTION_TYPE;
+
+/*
+ @11E-BA@
+ 802.11e/WMM Related command(s)/data structures
+*/
+
+// Flag to indicate if the stream is an immediate block ack stream.
+// if this bit is not set, the stream is delayed block ack stream.
+#define BASTREAM_FLAG_DELAYED_TYPE 0x00
+#define BASTREAM_FLAG_IMMEDIATE_TYPE 0x01
+
+// Flag to indicate the direction of the stream (upstream/downstream).
+// If this bit is not set, the direction is downstream.
+#define BASTREAM_FLAG_DIRECTION_UPSTREAM 0x00
+#define BASTREAM_FLAG_DIRECTION_DOWNSTREAM 0x02
+#define BASTREAM_FLAG_DIRECTION_DLP 0x04
+#define BASTREAM_FLAG_DIRECTION_BOTH 0x06
+
+typedef enum {
+ BaCreateStream,
+ BaUpdateStream,
+ BaDestroyStream,
+ BaFlushStream,
+ BaCheckCreateStream
+} BASTREAM_ACTION_TYPE;
+
+typedef struct {
+ uint32_t Context;
+} __packed BASTREAM_CONTEXT;
+
+// parameters for block ack creation
+typedef struct {
+ // BA Creation flags - see above
+ uint32_t Flags;
+ // idle threshold
+ uint32_t IdleThrs;
+ // block ack transmit threshold (after how many pkts should we send BAR?)
+ uint32_t BarThrs;
+ // receiver window size
+ uint32_t WindowSize;
+ // MAC Address of the BA partner
+ uint8_t PeerMacAddr[6];
+ // Dialog Token
+ uint8_t DialogToken;
+ //TID for the traffic stream in this BA
+ uint8_t Tid;
+ // shared memory queue ID (not sure if this is required)
+ uint8_t QueueId;
+ uint8_t ParamInfo;
+ // returned by firmware - firmware context pointer.
+ // this context pointer will be passed to firmware for all future commands.
+ BASTREAM_CONTEXT FwBaContext;
+ uint8_t ResetSeqNo; /** 0 or 1**/
+ uint16_t StartSeqNo;
+}__packed BASTREAM_CREATE_STREAM;
+
+// new transmit sequence number information
+typedef struct {
+ // BA flags - see above
+ uint32_t Flags;
+ // returned by firmware in the create ba stream response
+ BASTREAM_CONTEXT FwBaContext;
+ // new sequence number for this block ack stream
+ uint16_t BaSeqNum;
+}__packed BASTREAM_UPDATE_STREAM;
+
+typedef struct {
+ // BA Stream flags
+ uint32_t Flags;
+ // returned by firmware in the create ba stream response
+ BASTREAM_CONTEXT FwBaContext;
+}__packed BASTREAM_STREAM_INFO;
+
+//Command to create/destroy block ACK
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint32_t ActionType;
+ union
+ {
+ // information required to create BA Stream...
+ BASTREAM_CREATE_STREAM CreateParams;
+ // update starting/new sequence number etc.
+ BASTREAM_UPDATE_STREAM UpdtSeqNum;
+ // destroy an existing stream...
+ BASTREAM_STREAM_INFO DestroyParams;
+ // destroy an existing stream...
+ BASTREAM_STREAM_INFO FlushParams;
+ }__packed BaInfo;
+}__packed HostCmd_FW_BASTREAM;
+
+// Define data structure for HostCmd_CMD_GET_WATCHDOG_BITMAP
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint8_t Watchdogbitmap; // for SW/BA
+} __packed HostCmd_FW_GET_WATCHDOG_BITMAP;
+
+
+
+// Define data structure for HostCmd_CMD_SET_REGION_POWER
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint16_t MaxPowerLevel;
+ uint16_t Reserved;
+} __packed HostCmd_DS_SET_REGION_POWER;
+
+// Define data structure for HostCmd_CMD_SET_RATE_ADAPT_MODE
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint16_t Action;
+ uint16_t RateAdaptMode;
+} __packed HostCmd_DS_SET_RATE_ADAPT_MODE;
+
+// Define data structure for HostCmd_CMD_SET_LINKADAPT_CS_MODE
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint16_t Action;
+ uint16_t CSMode;
+} __packed HostCmd_DS_SET_LINKADAPT_CS_MODE;
+
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint32_t NProtectFlag;
+} __packed HostCmd_FW_SET_N_PROTECT_FLAG;
+
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint8_t NProtectOpMode;
+} __packed HostCmd_FW_SET_N_PROTECT_OPMODE;
+
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint8_t OptLevel;
+} __packed HostCmd_FW_SET_OPTIMIZATION_LEVEL;
+
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint8_t annex;
+ uint8_t index;
+ uint8_t len;
+ uint8_t Reserverd;
+#define CAL_TBL_SIZE 160
+ uint8_t calTbl[CAL_TBL_SIZE];
+} __packed HostCmd_FW_GET_CALTABLE;
+
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint8_t Addr[6];
+ uint8_t Enable;
+ uint8_t Mode;
+} __packed HostCmd_FW_SET_MIMOPSHT;
+
+#define MAX_BEACON_SIZE 1024
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint16_t Bcnlen;
+ uint8_t Reserverd[2];
+ uint8_t Bcn[MAX_BEACON_SIZE];
+} __packed HostCmd_FW_GET_BEACON;
+
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint8_t NumberOfPowersave;
+ uint8_t reserved;
+} __packed HostCmd_SET_POWERSAVESTATION;
+
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint16_t Aid;
+ uint32_t Set;
+ uint8_t reserved;
+} __packed HostCmd_SET_TIM;
+
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint8_t TrafficMap[251];
+ uint8_t reserved;
+} __packed HostCmd_GET_TIM;
+
+typedef struct {
+ FWCmdHdr CmdHdr;
+ uint8_t MacAddr[6];
+ uint8_t TID;
+ uint16_t SeqNo;
+ uint8_t reserved;
+} __packed HostCmd_GET_SEQNO;
+#endif /* _MWL_HALREG_H_ */