diff options
author | weongyo <weongyo@FreeBSD.org> | 2008-03-25 06:32:33 +0000 |
---|---|---|
committer | weongyo <weongyo@FreeBSD.org> | 2008-03-25 06:32:33 +0000 |
commit | 9a9594d179e3c5f3f47d805d0479f17ee4475f84 (patch) | |
tree | b93f9a4683497d690c51a5b90df665ddeb05a595 /sys | |
parent | 953e13c915c537826014e9efb6bc8a4009727f72 (diff) | |
download | FreeBSD-src-9a9594d179e3c5f3f47d805d0479f17ee4475f84.zip FreeBSD-src-9a9594d179e3c5f3f47d805d0479f17ee4475f84.tar.gz |
Add support for Marvell Libertas 88W8335 based PCI network adapters.
Reviewed by: sam, many wireless people
Approved by: thompsa (mentor)
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/malo/if_malo.c | 2472 | ||||
-rw-r--r-- | sys/dev/malo/if_malo.h | 586 | ||||
-rw-r--r-- | sys/dev/malo/if_malo_pci.c | 373 | ||||
-rw-r--r-- | sys/dev/malo/if_malohal.c | 918 | ||||
-rw-r--r-- | sys/dev/malo/if_malohal.h | 234 | ||||
-rw-r--r-- | sys/dev/malo/if_maloioctl.h | 114 | ||||
-rw-r--r-- | sys/modules/malo/Makefile | 8 |
7 files changed, 4705 insertions, 0 deletions
diff --git a/sys/dev/malo/if_malo.c b/sys/dev/malo/if_malo.c new file mode 100644 index 0000000..a1a6285 --- /dev/null +++ b/sys/dev/malo/if_malo.c @@ -0,0 +1,2472 @@ +/*- + * Copyright (c) 2008 Weongyo Jeong <weongyo@freebsd.org> + * Copyright (c) 2007 Marvell Semiconductor, Inc. + * Copyright (c) 2007 Sam Leffler, Errno Consulting + * 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 + +#include <sys/param.h> +#include <sys/endian.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/sysctl.h> +#include <sys/taskqueue.h> + +#include <machine/bus.h> +#include <sys/bus.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> +#include <net/ethernet.h> + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_regdomain.h> + +#include <net/bpf.h> + +#include <dev/malo/if_malo.h> + +SYSCTL_NODE(_hw, OID_AUTO, malo, CTLFLAG_RD, 0, + "Marvell 88w8335 driver parameters"); + +static int malo_txcoalesce = 8; /* # tx pkts to q before poking f/w*/ +SYSCTL_INT(_hw_malo, OID_AUTO, txcoalesce, CTLFLAG_RW, &malo_txcoalesce, + 0, "tx buffers to send at once"); +TUNABLE_INT("hw.malo.txcoalesce", &malo_txcoalesce); +static int malo_rxbuf = MALO_RXBUF; /* # rx buffers to allocate */ +SYSCTL_INT(_hw_malo, OID_AUTO, rxbuf, CTLFLAG_RW, &malo_rxbuf, + 0, "rx buffers allocated"); +TUNABLE_INT("hw.malo.rxbuf", &malo_rxbuf); +static int malo_rxquota = MALO_RXBUF; /* # max buffers to process */ +SYSCTL_INT(_hw_malo, OID_AUTO, rxquota, CTLFLAG_RW, &malo_rxquota, + 0, "max rx buffers to process per interrupt"); +TUNABLE_INT("hw.malo.rxquota", &malo_rxquota); +static int malo_txbuf = MALO_TXBUF; /* # tx buffers to allocate */ +SYSCTL_INT(_hw_malo, OID_AUTO, txbuf, CTLFLAG_RW, &malo_txbuf, + 0, "tx buffers allocated"); +TUNABLE_INT("hw.malo.txbuf", &malo_txbuf); + +#ifdef MALO_DEBUG +static int malo_debug = 0; +SYSCTL_INT(_hw_malo, OID_AUTO, debug, CTLFLAG_RW, &malo_debug, + 0, "control debugging printfs"); +TUNABLE_INT("hw.malo.debug", &malo_debug); +enum { + MALO_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ + MALO_DEBUG_XMIT_DESC = 0x00000002, /* xmit descriptors */ + MALO_DEBUG_RECV = 0x00000004, /* basic recv operation */ + MALO_DEBUG_RECV_DESC = 0x00000008, /* recv descriptors */ + MALO_DEBUG_RESET = 0x00000010, /* reset processing */ + MALO_DEBUG_INTR = 0x00000040, /* ISR */ + MALO_DEBUG_TX_PROC = 0x00000080, /* tx ISR proc */ + MALO_DEBUG_RX_PROC = 0x00000100, /* rx ISR proc */ + MALO_DEBUG_STATE = 0x00000400, /* 802.11 state transitions */ + MALO_DEBUG_NODE = 0x00000800, /* node management */ + MALO_DEBUG_RECV_ALL = 0x00001000, /* trace all frames (beacons) */ + MALO_DEBUG_FW = 0x00008000, /* firmware */ + MALO_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->malo_debug & MALO_DEBUG_RECV) && \ + ((sc->malo_debug & MALO_DEBUG_RECV_ALL) || !IS_BEACON(wh))) || \ + (sc->malo_ifp->if_flags & (IFF_DEBUG|IFF_LINK2)) == \ + (IFF_DEBUG|IFF_LINK2)) +#define IFF_DUMPPKTS_XMIT(sc) \ + ((sc->malo_debug & MALO_DEBUG_XMIT) || \ + (sc->malo_ifp->if_flags & (IFF_DEBUG | IFF_LINK2)) == \ + (IFF_DEBUG | IFF_LINK2)) +#define DPRINTF(sc, m, fmt, ...) do { \ + if (sc->malo_debug & (m)) \ + printf(fmt, __VA_ARGS__); \ +} while (0) +#else +#define DPRINTF(sc, m, fmt, ...) do { \ + (void) sc; \ +} while (0) +#endif + +MALLOC_DEFINE(M_MALODEV, "malodev", "malo driver dma buffers"); + +static int malo_dma_setup(struct malo_softc *); +static int malo_setup_hwdma(struct malo_softc *); +static void malo_txq_init(struct malo_softc *, struct malo_txq *, int); +static void malo_tx_cleanupq(struct malo_softc *, struct malo_txq *); +static void malo_start(struct ifnet *); +static void malo_watchdog(struct ifnet *); +static int malo_ioctl(struct ifnet *, u_long, caddr_t); +static void malo_updateslot(struct ifnet *); +static int malo_newstate(struct ieee80211com *, enum ieee80211_state, int); +static void malo_scan_start(struct ieee80211com *); +static void malo_scan_end(struct ieee80211com *); +static void malo_set_channel(struct ieee80211com *); +static int malo_raw_xmit(struct ieee80211_node *, struct mbuf *, + const struct ieee80211_bpf_params *); +static int malo_media_change(struct ifnet *); +static void malo_bpfattach(struct malo_softc *); +static void malo_sysctlattach(struct malo_softc *); +static void malo_announce(struct malo_softc *); +static void malo_dma_cleanup(struct malo_softc *); +static void malo_stop_locked(struct ifnet *, int); +static int malo_chan_set(struct malo_softc *, struct ieee80211_channel *); +static int malo_mode_init(struct malo_softc *); +static void malo_tx_proc(void *, int); +static void malo_rx_proc(void *, int); +static void malo_init(void *); + +/* + * Read/Write shorthands for accesses to BAR 0. Note that all BAR 1 + * operations are done in the "hal" except getting H/W MAC address at + * malo_attach and there should be no reference to them here. + */ +static uint32_t +malo_bar0_read4(struct malo_softc *sc, bus_size_t off) +{ + return bus_space_read_4(sc->malo_io0t, sc->malo_io0h, off); +} + +static void +malo_bar0_write4(struct malo_softc *sc, bus_size_t off, uint32_t val) +{ + DPRINTF(sc, MALO_DEBUG_FW, "%s: off 0x%x val 0x%x\n", + __func__, off, val); + + bus_space_write_4(sc->malo_io0t, sc->malo_io0h, off, val); +} + +static uint8_t +malo_bar1_read1(struct malo_softc *sc, bus_size_t off) +{ + return bus_space_read_1(sc->malo_io1t, sc->malo_io1h, off); +} + +int +malo_attach(uint16_t devid, struct malo_softc *sc) +{ + int error, i; + struct ieee80211com *ic = &sc->malo_ic; + struct ifnet *ifp; + struct malo_hal *mh; + uint8_t bands; + + ifp = sc->malo_ifp = if_alloc(IFT_ETHER); + if (ifp == NULL) { + device_printf(sc->malo_dev, "can not if_alloc()\n"); + return ENOSPC; + } + + MALO_LOCK_INIT(sc); + + /* set these up early for if_printf use */ + if_initname(ifp, device_get_name(sc->malo_dev), + device_get_unit(sc->malo_dev)); + + /* + * NB: get mac address from hardware directly here before we set DMAs + * for HAL because we don't want to disturb operations of HAL at BAR 1. + */ + for (i = 0; i < IEEE80211_ADDR_LEN; i++) { + /* XXX remove a magic number but we don't have documents. */ + ic->ic_myaddr[i] = malo_bar1_read1(sc, 0xa528 + i); + DELAY(1000); + } + + mh = malo_hal_attach(sc->malo_dev, devid, + sc->malo_io1h, sc->malo_io1t, sc->malo_dmat); + if (mh == NULL) { + if_printf(ifp, "unable to attach HAL\n"); + error = EIO; + goto bad; + } + sc->malo_mh = mh; + + sc->malo_txantenna = 0x2; /* h/w default */ + sc->malo_rxantenna = 0xffff; /* h/w default */ + + /* + * Allocate tx + rx descriptors and populate the lists. + * We immediately push the information to the firmware + * as otherwise it gets upset. + */ + error = malo_dma_setup(sc); + if (error != 0) { + if_printf(ifp, "failed to setup descriptors: %d\n", error); + goto bad1; + } + + sc->malo_tq = taskqueue_create_fast("malo_taskq", M_NOWAIT, + taskqueue_thread_enqueue, &sc->malo_tq); + taskqueue_start_threads(&sc->malo_tq, 1, PI_NET, + "%s taskq", ifp->if_xname); + + TASK_INIT(&sc->malo_rxtask, 0, malo_rx_proc, sc); + TASK_INIT(&sc->malo_txtask, 0, malo_tx_proc, sc); + + ifp->if_softc = sc; + ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; + ifp->if_start = malo_start; + ifp->if_watchdog = malo_watchdog; + ifp->if_ioctl = malo_ioctl; + ifp->if_init = malo_init; + IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); + ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; + IFQ_SET_READY(&ifp->if_snd); + + /* NB: firmware looks that it does not export regdomain info API. */ + bands = 0; + setbit(&bands, IEEE80211_MODE_11B); + setbit(&bands, IEEE80211_MODE_11G); + ieee80211_init_channels(ic, 0, CTRY_DEFAULT, bands, 0, 1); + + 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_BGSCAN /* capable of bg scanning */ + | IEEE80211_C_MONITOR /* monitor mode */ + | IEEE80211_C_SHPREAMBLE /* short preamble supported */ + | IEEE80211_C_SHSLOT /* short slot time supported */ + | IEEE80211_C_TXPMGT /* capable of txpow mgt */ + | IEEE80211_C_WPA /* capable of WPA1+WPA2 */ + ; + + /* + * 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 malo_txrec) - + sizeof(struct ieee80211_frame); + + /* call MI attach routine. */ + ieee80211_ifattach(ic); + /* override default methods */ + ic->ic_updateslot = malo_updateslot; + ic->ic_raw_xmit = malo_raw_xmit; + + sc->malo_newstate = ic->ic_newstate; + ic->ic_newstate = malo_newstate; + + ic->ic_scan_start = malo_scan_start; + ic->ic_scan_end = malo_scan_end; + ic->ic_set_channel = malo_set_channel; + + /* complete initialization */ + ieee80211_media_init(ic, malo_media_change, ieee80211_media_status); + + sc->malo_invalid = 0; /* ready to go, enable int handling */ + + malo_bpfattach(sc); + + /* + * Setup dynamic sysctl's. + */ + malo_sysctlattach(sc); + + if (bootverbose) + ieee80211_announce(ic); + + return 0; +bad1: + malo_hal_detach(mh); +bad: + if_free(ifp); + sc->malo_invalid = 1; + + return error; +} + +int +malo_intr(void *arg) +{ + struct malo_softc *sc = arg; + struct malo_hal *mh = sc->malo_mh; + uint32_t status; + + if (sc->malo_invalid) { + /* + * The hardware is not ready/present, don't touch anything. + * Note this can happen early on if the IRQ is shared. + */ + DPRINTF(sc, MALO_DEBUG_ANY, "%s: invalid; ignored\n", __func__); + return (FILTER_STRAY); + } + + /* + * Figure out the reason(s) for the interrupt. + */ + malo_hal_getisr(mh, &status); /* NB: clears ISR too */ + if (status == 0) /* must be a shared irq */ + return (FILTER_STRAY); + + DPRINTF(sc, MALO_DEBUG_INTR, "%s: status 0x%x imask 0x%x\n", + __func__, status, sc->malo_imask); + + if (status & MALO_A2HRIC_BIT_RX_RDY) + taskqueue_enqueue_fast(sc->malo_tq, &sc->malo_rxtask); + if (status & MALO_A2HRIC_BIT_TX_DONE) + taskqueue_enqueue_fast(sc->malo_tq, &sc->malo_txtask); + if (status & MALO_A2HRIC_BIT_OPC_DONE) + malo_hal_cmddone(mh); + if (status & MALO_A2HRIC_BIT_MAC_EVENT) + ; + if (status & MALO_A2HRIC_BIT_RX_PROBLEM) + ; + if (status & MALO_A2HRIC_BIT_ICV_ERROR) { + /* TKIP ICV error */ + sc->malo_stats.mst_rx_badtkipicv++; + } + +#ifdef MALO_DEBUG + if (((status | sc->malo_imask) ^ sc->malo_imask) != 0) + DPRINTF(sc, MALO_DEBUG_INTR, + "%s: can't handle interrupt status 0x%x\n", + __func__, status); +#endif + + return (FILTER_HANDLED); +} + +static void +malo_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; +} + +static int +malo_desc_setup(struct malo_softc *sc, const char *name, + struct malo_descdma *dd, + int nbuf, size_t bufsize, int ndesc, size_t descsize) +{ + int error; + struct ifnet *ifp = sc->malo_ifp; + uint8_t *ds; + + DPRINTF(sc, MALO_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->malo_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, + malo_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, MALO_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; +} + +#define DS2PHYS(_dd, _ds) \ + ((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc)) + +static int +malo_rxdma_setup(struct malo_softc *sc) +{ + struct ifnet *ifp = sc->malo_ifp; + int error, bsize, i; + struct malo_rxbuf *bf; + struct malo_rxdesc *ds; + + error = malo_desc_setup(sc, "rx", &sc->malo_rxdma, + malo_rxbuf, sizeof(struct malo_rxbuf), + 1, sizeof(struct malo_rxdesc)); + if (error != 0) + return error; + + /* + * Allocate rx buffers and set them up. + */ + bsize = malo_rxbuf * sizeof(struct malo_rxbuf); + bf = malloc(bsize, M_MALODEV, M_NOWAIT | M_ZERO); + if (bf == NULL) { + if_printf(ifp, "malloc of %u rx buffers failed\n", bsize); + return error; + } + sc->malo_rxdma.dd_bufptr = bf; + + STAILQ_INIT(&sc->malo_rxbuf); + ds = sc->malo_rxdma.dd_desc; + for (i = 0; i < malo_rxbuf; i++, bf++, ds++) { + bf->bf_desc = ds; + bf->bf_daddr = DS2PHYS(&sc->malo_rxdma, ds); + error = bus_dmamap_create(sc->malo_dmat, BUS_DMA_NOWAIT, + &bf->bf_dmamap); + if (error != 0) { + if_printf(ifp, "%s: unable to dmamap for rx buffer, " + "error %d\n", __func__, error); + return error; + } + /* NB: tail is intentional to preserve descriptor order */ + STAILQ_INSERT_TAIL(&sc->malo_rxbuf, bf, bf_list); + } + return 0; +} + +static int +malo_txdma_setup(struct malo_softc *sc, struct malo_txq *txq) +{ + struct ifnet *ifp = sc->malo_ifp; + int error, bsize, i; + struct malo_txbuf *bf; + struct malo_txdesc *ds; + + error = malo_desc_setup(sc, "tx", &txq->dma, + malo_txbuf, sizeof(struct malo_txbuf), + MALO_TXDESC, sizeof(struct malo_txdesc)); + if (error != 0) + return error; + + /* allocate and setup tx buffers */ + bsize = malo_txbuf * sizeof(struct malo_txbuf); + bf = malloc(bsize, M_MALODEV, M_NOWAIT | M_ZERO); + if (bf == NULL) { + if_printf(ifp, "malloc of %u tx buffers failed\n", + malo_txbuf); + return ENOMEM; + } + txq->dma.dd_bufptr = bf; + + STAILQ_INIT(&txq->free); + txq->nfree = 0; + ds = txq->dma.dd_desc; + for (i = 0; i < malo_txbuf; i++, bf++, ds += MALO_TXDESC) { + bf->bf_desc = ds; + bf->bf_daddr = DS2PHYS(&txq->dma, ds); + error = bus_dmamap_create(sc->malo_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; + } + STAILQ_INSERT_TAIL(&txq->free, bf, bf_list); + txq->nfree++; + } + + return 0; +} + +static void +malo_desc_cleanup(struct malo_softc *sc, struct malo_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)); +} + +static void +malo_rxdma_cleanup(struct malo_softc *sc) +{ + struct malo_rxbuf *bf; + + STAILQ_FOREACH(bf, &sc->malo_rxbuf, bf_list) { + if (bf->bf_m != NULL) { + m_freem(bf->bf_m); + bf->bf_m = NULL; + } + if (bf->bf_dmamap != NULL) { + bus_dmamap_destroy(sc->malo_dmat, bf->bf_dmamap); + bf->bf_dmamap = NULL; + } + } + STAILQ_INIT(&sc->malo_rxbuf); + if (sc->malo_rxdma.dd_bufptr != NULL) { + free(sc->malo_rxdma.dd_bufptr, M_MALODEV); + sc->malo_rxdma.dd_bufptr = NULL; + } + if (sc->malo_rxdma.dd_desc_len != 0) + malo_desc_cleanup(sc, &sc->malo_rxdma); +} + +static void +malo_txdma_cleanup(struct malo_softc *sc, struct malo_txq *txq) +{ + struct malo_txbuf *bf; + struct ieee80211_node *ni; + + STAILQ_FOREACH(bf, &txq->free, bf_list) { + if (bf->bf_m != NULL) { + m_freem(bf->bf_m); + bf->bf_m = NULL; + } + ni = bf->bf_node; + bf->bf_node = NULL; + if (ni != NULL) { + /* + * Reclaim node reference. + */ + ieee80211_free_node(ni); + } + if (bf->bf_dmamap != NULL) { + bus_dmamap_destroy(sc->malo_dmat, bf->bf_dmamap); + bf->bf_dmamap = NULL; + } + } + STAILQ_INIT(&txq->free); + txq->nfree = 0; + if (txq->dma.dd_bufptr != NULL) { + free(txq->dma.dd_bufptr, M_MALODEV); + txq->dma.dd_bufptr = NULL; + } + if (txq->dma.dd_desc_len != 0) + malo_desc_cleanup(sc, &txq->dma); +} + +static void +malo_dma_cleanup(struct malo_softc *sc) +{ + int i; + + for (i = 0; i < MALO_NUM_TX_QUEUES; i++) + malo_txdma_cleanup(sc, &sc->malo_txq[i]); + + malo_rxdma_cleanup(sc); +} + +static int +malo_dma_setup(struct malo_softc *sc) +{ + int error, i; + + /* rxdma initializing. */ + error = malo_rxdma_setup(sc); + if (error != 0) + return error; + + /* NB: we just have 1 tx queue now. */ + for (i = 0; i < MALO_NUM_TX_QUEUES; i++) { + error = malo_txdma_setup(sc, &sc->malo_txq[i]); + if (error != 0) { + malo_dma_cleanup(sc); + + return error; + } + + malo_txq_init(sc, &sc->malo_txq[i], i); + } + + return 0; +} + +static void +malo_hal_set_rxtxdma(struct malo_softc *sc) +{ + int i; + + malo_bar0_write4(sc, sc->malo_hwspecs.rxdesc_read, + sc->malo_hwdma.rxdesc_read); + malo_bar0_write4(sc, sc->malo_hwspecs.rxdesc_write, + sc->malo_hwdma.rxdesc_read); + + for (i = 0; i < MALO_NUM_TX_QUEUES; i++) { + malo_bar0_write4(sc, + sc->malo_hwspecs.wcbbase[i], sc->malo_hwdma.wcbbase[i]); + } +} + +/* + * 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 malo_hal_sethwdma. + */ +static int +malo_setup_hwdma(struct malo_softc *sc) +{ + int i; + struct malo_txq *txq; + + sc->malo_hwdma.rxdesc_read = sc->malo_rxdma.dd_desc_paddr; + + for (i = 0; i < MALO_NUM_TX_QUEUES; i++) { + txq = &sc->malo_txq[i]; + sc->malo_hwdma.wcbbase[i] = txq->dma.dd_desc_paddr; + } + sc->malo_hwdma.maxnum_txwcb = malo_txbuf; + sc->malo_hwdma.maxnum_wcb = MALO_NUM_TX_QUEUES; + + malo_hal_set_rxtxdma(sc); + + return 0; +} + +static void +malo_txq_init(struct malo_softc *sc, struct malo_txq *txq, int qnum) +{ + struct malo_txbuf *bf, *bn; + struct malo_txdesc *ds; + + MALO_TXQ_LOCK_INIT(sc, txq); + txq->qnum = qnum; + txq->txpri = 0; /* XXX */ + + 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->physnext = htole32(bn->bf_daddr); + } + STAILQ_INIT(&txq->active); +} + +/* + * Reclaim resources for a setup queue. + */ +static void +malo_tx_cleanupq(struct malo_softc *sc, struct malo_txq *txq) +{ + /* XXX hal work? */ + MALO_TXQ_LOCK_DESTROY(txq); +} + +/* + * Allocate a tx buffer for sending a frame. + */ +static struct malo_txbuf * +malo_getbuf(struct malo_softc *sc, struct malo_txq *txq) +{ + struct malo_txbuf *bf; + + MALO_TXQ_LOCK(txq); + bf = STAILQ_FIRST(&txq->free); + if (bf != NULL) { + STAILQ_REMOVE_HEAD(&txq->free, bf_list); + txq->nfree--; + } + MALO_TXQ_UNLOCK(txq); + if (bf == NULL) { + DPRINTF(sc, MALO_DEBUG_XMIT, + "%s: out of xmit buffers on q %d\n", __func__, txq->qnum); + sc->malo_stats.mst_tx_qstop++; + } + return bf; +} + +static int +malo_tx_dmasetup(struct malo_softc *sc, struct malo_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->malo_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 = MALO_TXDESC + 1; + } else if (error != 0) { + sc->malo_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->malo_stats.mst_tx_linear++; + m = m_defrag(m0, M_DONTWAIT); + if (m == NULL) { + m_freem(m0); + sc->malo_stats.mst_tx_nombuf++; + return ENOMEM; + } + m0 = m; + error = bus_dmamap_load_mbuf_sg(sc->malo_dmat, bf->bf_dmamap, m0, + bf->bf_segs, &bf->bf_nseg, + BUS_DMA_NOWAIT); + if (error != 0) { + sc->malo_stats.mst_tx_busdma++; + m_freem(m0); + return error; + } + KASSERT(bf->bf_nseg <= MALO_TXDESC, + ("too many segments after defrag; nseg %u", bf->bf_nseg)); + } else if (bf->bf_nseg == 0) { /* null packet, discard */ + sc->malo_stats.mst_tx_nodata++; + m_freem(m0); + return EIO; + } + DPRINTF(sc, MALO_DEBUG_XMIT, "%s: m %p len %u\n", + __func__, m0, m0->m_pkthdr.len); + bus_dmamap_sync(sc->malo_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE); + bf->bf_m = m0; + + return 0; +} + +#ifdef MALO_DEBUG +static void +malo_printrxbuf(const struct malo_rxbuf *bf, u_int ix) +{ + const struct malo_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 SNR:%02x NF:%02x CHAN:%02x" + " RATE:%02x QOS:%04x\n", + ix, ds, (const struct malo_desc *)bf->bf_daddr, + le32toh(ds->physnext), le32toh(ds->physbuffdata), + ds->rxcontrol, + ds->rxcontrol != MALO_RXD_CTRL_DRIVER_OWN ? + "" : (status & MALO_RXD_STATUS_OK) ? " *" : " !", + ds->status, le16toh(ds->pktlen), ds->snr, ds->nf, ds->channel, + ds->rate, le16toh(ds->qosctrl)); +} + +static void +malo_printtxbuf(const struct malo_txbuf *bf, u_int qnum, u_int ix) +{ + const struct malo_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 malo_txdesc *)bf->bf_daddr); + printf(" NEXT:%08x DATA:%08x LEN:%04x STAT:%08x%s\n", + le32toh(ds->physnext), + le32toh(ds->pktptr), le16toh(ds->pktlen), status, + status & MALO_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->sap_pktinfo), le16toh(ds->format)); +#if 0 + { + const uint8_t *cp = (const uint8_t *) ds; + int i; + for (i = 0; i < sizeof(struct malo_txdesc); i++) { + printf("%02x ", cp[i]); + if (((i+1) % 16) == 0) + printf("\n"); + } + printf("\n"); + } +#endif +} +#endif /* MALO_DEBUG */ + +static __inline void +malo_updatetxrate(struct ieee80211_node *ni, int rix) +{ +#define N(x) (sizeof(x)/sizeof(x[0])) + static const int ieeerates[] = + { 2, 4, 11, 22, 44, 12, 18, 24, 36, 48, 96, 108 }; + if (rix < N(ieeerates)) + ni->ni_txrate = ieeerates[rix]; +#undef N +} + +static int +malo_fix2rate(int fix_rate) +{ +#define N(x) (sizeof(x)/sizeof(x[0])) + static const int rates[] = + { 2, 4, 11, 22, 12, 18, 24, 36, 48, 96, 108 }; + return (fix_rate < N(rates) ? rates[fix_rate] : 0); +#undef N +} + +/* idiomatic shorthands: MS = mask+shift, SM = shift+mask */ +#define MS(v,x) (((v) & x) >> x##_S) +#define SM(v,x) (((v) << x##_S) & x) + +/* + * Process completed xmit descriptors from the specified queue. + */ +static int +malo_tx_processq(struct malo_softc *sc, struct malo_txq *txq) +{ + struct malo_txbuf *bf; + struct malo_txdesc *ds; + struct ieee80211_node *ni; + int nreaped; + uint32_t status; + + DPRINTF(sc, MALO_DEBUG_TX_PROC, "%s: tx queue %u\n", + __func__, txq->qnum); + for (nreaped = 0;; nreaped++) { + MALO_TXQ_LOCK(txq); + bf = STAILQ_FIRST(&txq->active); + if (bf == NULL) { + MALO_TXQ_UNLOCK(txq); + break; + } + ds = bf->bf_desc; + MALO_TXDESC_SYNC(txq, ds, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + if (ds->status & htole32(MALO_TXD_STATUS_FW_OWNED)) { + MALO_TXQ_UNLOCK(txq); + break; + } + STAILQ_REMOVE_HEAD(&txq->active, bf_list); + MALO_TXQ_UNLOCK(txq); + +#ifdef MALO_DEBUG + if (sc->malo_debug & MALO_DEBUG_XMIT_DESC) + malo_printtxbuf(bf, txq->qnum, nreaped); +#endif + ni = bf->bf_node; + if (ni != NULL) { + status = le32toh(ds->status); + if (status & MALO_TXD_STATUS_OK) { + uint16_t format = le16toh(ds->format); + uint8_t txant = MS(format, MALO_TXD_ANTENNA); + + sc->malo_stats.mst_ant_tx[txant]++; + if (status & MALO_TXD_STATUS_OK_RETRY) + sc->malo_stats.mst_tx_retries++; + if (status & MALO_TXD_STATUS_OK_MORE_RETRY) + sc->malo_stats.mst_tx_mretries++; + malo_updatetxrate(ni, ds->datarate); + sc->malo_stats.mst_tx_rate = ds->datarate; + } else { + if (status & MALO_TXD_STATUS_FAILED_LINK_ERROR) + sc->malo_stats.mst_tx_linkerror++; + if (status & MALO_TXD_STATUS_FAILED_XRETRY) + sc->malo_stats.mst_tx_xretries++; + if (status & MALO_TXD_STATUS_FAILED_AGING) + sc->malo_stats.mst_tx_aging++; + } + /* + * 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 & MALO_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(MALO_TXD_STATUS_IDLE); + ds->pktlen = htole32(0); + + bus_dmamap_sync(sc->malo_dmat, bf->bf_dmamap, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->malo_dmat, bf->bf_dmamap); + m_freem(bf->bf_m); + bf->bf_m = NULL; + bf->bf_node = NULL; + + MALO_TXQ_LOCK(txq); + STAILQ_INSERT_TAIL(&txq->free, bf, bf_list); + txq->nfree++; + MALO_TXQ_UNLOCK(txq); + } + return nreaped; +} + +/* + * Deferred processing of transmit interrupt. + */ +static void +malo_tx_proc(void *arg, int npending) +{ + struct malo_softc *sc = arg; + struct ifnet *ifp = sc->malo_ifp; + int i, nreaped; + + /* + * Process each active queue. + */ + nreaped = 0; + for (i = 0; i < MALO_NUM_TX_QUEUES; i++) { + if (!STAILQ_EMPTY(&sc->malo_txq[i].active)) + nreaped += malo_tx_processq(sc, &sc->malo_txq[i]); + } + + if (nreaped != 0) { + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + ifp->if_timer = 0; + malo_start(ifp); + } +} + +static int +malo_tx_start(struct malo_softc *sc, struct ieee80211_node *ni, + struct malo_txbuf *bf, struct mbuf *m0) +{ +#define IEEE80211_DIR_DSTODS(wh) \ + ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS) +#define IS_DATA_FRAME(wh) \ + ((wh->i_fc[0] & (IEEE80211_FC0_TYPE_MASK)) == IEEE80211_FC0_TYPE_DATA) + int error, ismcast, iswep; + int copyhdrlen, hdrlen, pktlen; + struct ieee80211_frame *wh; + struct ieee80211com *ic = &sc->malo_ic; + struct ifnet *ifp = sc->malo_ifp; + struct malo_txdesc *ds; + struct malo_txrec *tr; + struct malo_txq *txq; + uint16_t qos; + + wh = mtod(m0, struct ieee80211_frame *); + iswep = wh->i_fc[1] & IEEE80211_FC1_WEP; + ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); + copyhdrlen = hdrlen = ieee80211_anyhdrsize(wh); + 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) { + 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(ic, 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. + */ + pktlen = m0->m_pkthdr.len; + + /* packet header may have moved, reset our local pointer */ + wh = mtod(m0, struct ieee80211_frame *); + } + + if (bpf_peers_present(sc->malo_drvbpf)) { + sc->malo_tx_th.wt_flags = 0; /* XXX */ + if (iswep) + sc->malo_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_WEP; + sc->malo_tx_th.wt_txpower = ni->ni_txpower; + sc->malo_tx_th.wt_antenna = sc->malo_txantenna; + + bpf_mtap2(sc->malo_drvbpf, + &sc->malo_tx_th, sc->malo_tx_th_len, 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 + * malo_attach. + */ + if (hdrlen < sizeof(struct malo_txrec)) { + const int space = sizeof(struct malo_txrec) - hdrlen; + if (M_LEADINGSPACE(m0) < space) { + /* NB: should never happen */ + device_printf(sc->malo_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); + /* XXX stat */ + return EIO; + } + M_PREPEND(m0, space, M_NOWAIT); + } + tr = mtod(m0, struct malo_txrec *); + 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 = malo_tx_dmasetup(sc, bf, m0); + if (error != 0) + return error; + bf->bf_node = ni; /* NB: held reference */ + m0 = bf->bf_m; /* NB: may have changed */ + tr = mtod(m0, struct malo_txrec *); + wh = (struct ieee80211_frame *)&tr->wh; + + /* + * Formulate tx descriptor. + */ + ds = bf->bf_desc; + txq = bf->bf_txq; + + ds->qosctrl = qos; /* NB: already little-endian */ + ds->pktptr = htole32(bf->bf_segs[0].ds_addr); + ds->pktlen = htole16(bf->bf_segs[0].ds_len); + /* NB: pPhysNext setup once, don't touch */ + ds->datarate = IS_DATA_FRAME(wh) ? 1 : 0; + ds->sap_pktinfo = 0; + ds->format = 0; + + /* + * Select transmit rate. + */ + switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { + case IEEE80211_FC0_TYPE_MGT: + sc->malo_stats.mst_tx_mgmt++; + /* fall thru... */ + case IEEE80211_FC0_TYPE_CTL: + ds->txpriority = 1; + break; + case IEEE80211_FC0_TYPE_DATA: + 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__); + /* XXX statistic */ + m_freem(m0); + return EIO; + } + +#ifdef MALO_DEBUG + 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); +#endif + + MALO_TXQ_LOCK(txq); + if (!IS_DATA_FRAME(wh)) + ds->status |= htole32(1); + ds->status |= htole32(MALO_TXD_STATUS_FW_OWNED); + STAILQ_INSERT_TAIL(&txq->active, bf, bf_list); + MALO_TXDESC_SYNC(txq, ds, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + ifp->if_opackets++; + ifp->if_timer = 5; + MALO_TXQ_UNLOCK(txq); + return 0; +#undef IEEE80211_DIR_DSTODS +} + +static void +malo_start(struct ifnet *ifp) +{ + int nqueued = 0; + struct ether_header *eh; + struct malo_softc *sc = ifp->if_softc; + struct ieee80211_frame *wh; + struct ieee80211_node *ni; + struct ieee80211com *ic = &sc->malo_ic; + struct malo_txbuf *bf = NULL; + struct malo_txq *txq = NULL; + struct mbuf *m; + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || sc->malo_invalid) + return; + + for (;;) { + /* + * Poll the management queue for frames; they + * have priority over normal data frames. + */ + IF_DEQUEUE(&ic->ic_mgtq, m); + if (m == NULL) { + /* + * No data frames go out unless we're associated. + */ + if (ic->ic_state != IEEE80211_S_RUN) { + DPRINTF(sc, MALO_DEBUG_XMIT, + "%s: discard data packet, state %s\n", + __func__, + ieee80211_state_name[ic->ic_state]); + sc->malo_stats.mst_tx_discard++; + break; + } + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) + break; + /* + * Cancel any background scan. + */ + if (ic->ic_flags & IEEE80211_F_SCAN) + ieee80211_cancel_scan(ic); + + /* + * Find the node for the destination so we can do + * things like power save and fast frames aggregation. + */ + if (m->m_len < sizeof(struct ether_header) && + (m = m_pullup(m, sizeof(struct ether_header))) == + NULL) { + ic->ic_stats.is_tx_nobuf++; /* XXX */ + ni = NULL; + goto bad; + } + eh = mtod(m, struct ether_header *); + ni = ieee80211_find_txnode(ic, eh->ether_dhost); + if (ni == NULL) { + /* NB: ieee80211_find_txnode does stat+msg */ + m_freem(m); + goto bad; + } + /* calculate priority so we can find the tx queue */ + if (ieee80211_classify(ic, m, ni)) { + DPRINTF(sc, MALO_DEBUG_XMIT, + "%s: discard, classification failure\n", + __func__); + m_freem(m); + goto bad; + } + + txq = &sc->malo_txq[0]; + + bf = malo_getbuf(sc, txq); + if (bf == NULL) { + IFQ_DRV_PREPEND(&ifp->if_snd, m); + ieee80211_free_node(ni); + + /* XXX blocks other traffic */ + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + sc->malo_stats.mst_tx_qstop++; + break; + } + ifp->if_opackets++; + + if (bpf_peers_present(ifp->if_bpf)) + bpf_mtap(ifp->if_bpf, m); + + /* + * Encapsulate the packet in prep for transmission. + */ + m = ieee80211_encap(ic, m, ni); + if (m == NULL) { + DPRINTF(sc, MALO_DEBUG_XMIT, + "%s: encapsulation failure\n", __func__); + sc->malo_stats.mst_tx_encap++; + goto bad; + } + } else { + /* + * 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 through + * the ic_mgtq (e.g. null data frames). + */ + txq = &sc->malo_txq[0]; + bf = malo_getbuf(sc, txq); + if (bf == NULL) { + IF_PREPEND(&ic->ic_mgtq, m); + /* XXX stat */ + break; + } + + /* + * Hack! The referenced node pointer is in the + * rcvif field of the packet header. This is + * placed there by ieee80211_mgmt_output because + * we need to hold the reference with the frame + * and there's no other way (other than packet + * tags which we consider too expensive to use) + * to pass it along. + */ + ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; + m->m_pkthdr.rcvif = NULL; + + wh = mtod(m, struct ieee80211_frame *); + sc->malo_stats.mst_tx_mgmt++; + + if (bpf_peers_present(ic->ic_rawbpf)) + bpf_mtap(ic->ic_rawbpf, m); + } + + /* + * Pass the frame to the h/w for transmission. + */ + if (malo_tx_start(sc, ni, bf, m)) { + bad: + ifp->if_oerrors++; + if (bf != NULL) { + bf->bf_m = NULL; + bf->bf_node = NULL; + MALO_TXQ_LOCK(txq); + STAILQ_INSERT_HEAD(&txq->free, bf, bf_list); + MALO_TXQ_UNLOCK(txq); + } + ieee80211_free_node(ni); + continue; + } + nqueued++; + + if (nqueued >= malo_txcoalesce) { + /* + * Poke the firmware to process queued frames; + * see below about (lack of) locking. + */ + nqueued = 0; + malo_hal_txstart(sc->malo_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 + * malo_tx_start is an optimization to avoid poking the + * firmware for each packet. + * + * NB: the queue id isn't used so 0 is ok. + */ + malo_hal_txstart(sc->malo_mh, 0/*XXX*/); + } +} + +static void +malo_watchdog(struct ifnet *ifp) +{ + struct malo_softc *sc = ifp->if_softc; + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) && !sc->malo_invalid) { + if_printf(ifp, "watchdog timeout\n"); + + /* XXX no way to reset h/w. now */ + + ifp->if_oerrors++; + sc->malo_stats.mst_watchdog++; + } +} + +static int +malo_hal_reset(struct malo_softc *sc) +{ + static int first = 0; + struct ieee80211com *ic = &sc->malo_ic; + struct malo_hal *mh = sc->malo_mh; + + if (first == 0) { + /* + * NB: when the device firstly is initialized, sometimes + * firmware could override rx/tx dma registers so we re-set + * these values once. + */ + malo_hal_set_rxtxdma(sc); + first = 1; + } + + malo_hal_setantenna(mh, MHA_ANTENNATYPE_RX, sc->malo_rxantenna); + malo_hal_setantenna(mh, MHA_ANTENNATYPE_TX, sc->malo_txantenna); + malo_hal_setradio(mh, 1, MHP_AUTO_PREAMBLE); + malo_chan_set(sc, ic->ic_curchan); + + /* XXX needs other stuffs? */ + + return 1; +} + +static __inline struct mbuf * +malo_getrxmbuf(struct malo_softc *sc, struct malo_rxbuf *bf) +{ + struct mbuf *m; + bus_addr_t paddr; + int error; + + /* XXX don't need mbuf, just dma buffer */ + m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); + if (m == NULL) { + sc->malo_stats.mst_rx_nombuf++; /* XXX */ + return NULL; + } + error = bus_dmamap_load(sc->malo_dmat, bf->bf_dmamap, + mtod(m, caddr_t), MJUMPAGESIZE, + malo_load_cb, &paddr, BUS_DMA_NOWAIT); + if (error != 0) { + if_printf(sc->malo_ifp, + "%s: bus_dmamap_load failed, error %d\n", __func__, error); + m_freem(m); + return NULL; + } + bf->bf_data = paddr; + bus_dmamap_sync(sc->malo_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE); + + return m; +} + +static int +malo_rxbuf_init(struct malo_softc *sc, struct malo_rxbuf *bf) +{ + struct malo_rxdesc *ds; + + ds = bf->bf_desc; + if (bf->bf_m == NULL) { + bf->bf_m = malo_getrxmbuf(sc, bf); + if (bf->bf_m == NULL) { + /* mark descriptor to be skipped */ + ds->rxcontrol = MALO_RXD_CTRL_OS_OWN; + /* NB: don't need PREREAD */ + MALO_RXDESC_SYNC(sc, ds, BUS_DMASYNC_PREWRITE); + return ENOMEM; + } + } + + /* + * Setup descriptor. + */ + ds->qosctrl = 0; + ds->snr = 0; + ds->status = MALO_RXD_STATUS_IDLE; + ds->channel = 0; + ds->pktlen = htole16(MALO_RXSIZE); + ds->nf = 0; + ds->physbuffdata = htole32(bf->bf_data); + /* NB: don't touch pPhysNext, set once */ + ds->rxcontrol = MALO_RXD_CTRL_DRIVER_OWN; + MALO_RXDESC_SYNC(sc, ds, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + return 0; +} + +/* + * Setup the rx data structures. This should only be done once or we may get + * out of sync with the firmware. + */ +static int +malo_startrecv(struct malo_softc *sc) +{ + struct malo_rxbuf *bf, *prev; + struct malo_rxdesc *ds; + + if (sc->malo_recvsetup == 1) { + malo_mode_init(sc); /* set filters, etc. */ + return 0; + } + + prev = NULL; + STAILQ_FOREACH(bf, &sc->malo_rxbuf, bf_list) { + int error = malo_rxbuf_init(sc, bf); + if (error != 0) { + DPRINTF(sc, MALO_DEBUG_RECV, + "%s: malo_rxbuf_init failed %d\n", + __func__, error); + return error; + } + if (prev != NULL) { + ds = prev->bf_desc; + ds->physnext = htole32(bf->bf_daddr); + } + prev = bf; + } + if (prev != NULL) { + ds = prev->bf_desc; + ds->physnext = + htole32(STAILQ_FIRST(&sc->malo_rxbuf)->bf_daddr); + } + + sc->malo_recvsetup = 1; + + malo_mode_init(sc); /* set filters, etc. */ + + return 0; +} + +static void +malo_init(void *arg) +{ + struct malo_softc *sc = (struct malo_softc *) arg; + struct ieee80211com *ic = &sc->malo_ic; + struct ifnet *ifp = sc->malo_ifp; + struct malo_hal *mh = sc->malo_mh; + int error; + + DPRINTF(sc, MALO_DEBUG_ANY, "%s: if_flags 0x%x\n", + __func__, ifp->if_flags); + + if (!sc->malo_fw_loaded) { + /* + * Load firmware so we can get setup. + */ + error = malo_hal_fwload(mh, "malo8335-h", "malo8335-m"); + if (error != 0) { + if_printf(ifp, "unable to setup firmware\n"); + return; + } + /* XXX gethwspecs() extracts correct informations? not maybe! */ + error = malo_hal_gethwspecs(mh, &sc->malo_hwspecs); + if (error != 0) { + if_printf(ifp, "unable to fetch h/w specs\n"); + return; + } + + DPRINTF(sc, MALO_DEBUG_FW, + "malo_hal_gethwspecs: hwversion 0x%x hostif 0x%x" + "maxnum_wcb 0x%x maxnum_mcaddr 0x%x maxnum_tx_wcb 0x%x" + "regioncode 0x%x num_antenna 0x%x fw_releasenum 0x%x" + "wcbbase0 0x%x rxdesc_read 0x%x rxdesc_write 0x%x" + "ul_fw_awakecookie 0x%x w[4] = %x %x %x %x", + sc->malo_hwspecs.hwversion, + sc->malo_hwspecs.hostinterface, sc->malo_hwspecs.maxnum_wcb, + sc->malo_hwspecs.maxnum_mcaddr, + sc->malo_hwspecs.maxnum_tx_wcb, + sc->malo_hwspecs.regioncode, sc->malo_hwspecs.num_antenna, + sc->malo_hwspecs.fw_releasenum, sc->malo_hwspecs.wcbbase0, + sc->malo_hwspecs.rxdesc_read, sc->malo_hwspecs.rxdesc_write, + sc->malo_hwspecs.ul_fw_awakecookie, + sc->malo_hwspecs.wcbbase[0], sc->malo_hwspecs.wcbbase[1], + sc->malo_hwspecs.wcbbase[2], sc->malo_hwspecs.wcbbase[3]); + + error = malo_setup_hwdma(sc); /* push to firmware */ + /* NB: malo_setupdma prints msg */ + if (error != 0) { + if_printf(ifp, "%s: failed to set up h/w dma\n", + __func__); + return; + } + + /* set reddomain. */ + ic->ic_regdomain = sc->malo_hwspecs.regioncode; + + malo_announce(sc); + + sc->malo_fw_loaded = 1; + } + + MALO_LOCK(sc); + + /* + * Stop anything previously setup. This is safe whether this is + * the first time through or not. + */ + malo_stop_locked(ifp, 0); + + /* + * Push state to the firmware. + */ + if (!malo_hal_reset(sc)) { + if_printf(ifp, "%s: unable to reset hardware\n", __func__); + goto done; + } + + /* + * Setup recv (once); transmit is already good to go. + */ + error = malo_startrecv(sc); + if (error != 0) { + if_printf(ifp, "%s: unable to start recv logic, error %d\n", + __func__, error); + goto done; + } + + /* + * Enable interrupts. + */ + sc->malo_imask = MALO_A2HRIC_BIT_RX_RDY + | MALO_A2HRIC_BIT_TX_DONE + | MALO_A2HRIC_BIT_OPC_DONE + | MALO_A2HRIC_BIT_MAC_EVENT + | MALO_A2HRIC_BIT_RX_PROBLEM + | MALO_A2HRIC_BIT_ICV_ERROR + | MALO_A2HRIC_BIT_RADAR_DETECT + | MALO_A2HRIC_BIT_CHAN_SWITCH; + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + ic->ic_state = IEEE80211_S_INIT; + IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp)); + + malo_hal_intrset(mh, sc->malo_imask); + + /* + * The hardware should be ready to go now so it's safe to kick + * the 802.11 state machine as it's likely to immediately call back + * to us to send mgmt frames. + */ + if (ic->ic_opmode != IEEE80211_M_MONITOR) { + if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL) + ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); + } else + ieee80211_new_state(ic, IEEE80211_S_RUN, -1); + +done: + if (error != 0) + if_printf(ifp, + "error(%d) occurred during the initializing.\n", error); + + MALO_UNLOCK(sc); + + return; +} + +/* + * Set the multicast filter contents into the hardware. + */ +static void +malo_setmcastfilter(struct malo_softc *sc) +{ + struct ieee80211com *ic = &sc->malo_ic; + struct ifmultiaddr *ifma; + struct ifnet *ifp = sc->malo_ifp; + uint8_t macs[IEEE80211_ADDR_LEN * MALO_HAL_MCAST_MAX]; + uint8_t *mp; + int nmc; + + mp = macs; + nmc = 0; + + if (ic->ic_opmode == IEEE80211_M_MONITOR || + (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC))) + goto all; + + IF_ADDR_LOCK(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + + if (nmc == MALO_HAL_MCAST_MAX) { + ifp->if_flags |= IFF_ALLMULTI; + IF_ADDR_UNLOCK(ifp); + goto all; + } + IEEE80211_ADDR_COPY(mp, + LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); + + mp += IEEE80211_ADDR_LEN, nmc++; + } + IF_ADDR_UNLOCK(ifp); + + malo_hal_setmcast(sc->malo_mh, nmc, macs); + +all: + /* + * XXX we don't know how to set the f/w for supporting + * IFF_ALLMULTI | IFF_PROMISC cases + */ + return; +} + +static int +malo_mode_init(struct malo_softc *sc) +{ + struct ieee80211com *ic = &sc->malo_ic; + struct ifnet *ifp = ic->ic_ifp; + struct malo_hal *mh = sc->malo_mh; + + /* + * Handle any link-level address change. Note that we only + * need to force ic_myaddr; any other addresses are handled + * as a byproduct of the ifnet code marking the interface + * down then up. + */ + IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp)); + + /* + * 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. + */ + malo_hal_setpromisc(mh, (ifp->if_flags & IFF_PROMISC) && + ic->ic_opmode != IEEE80211_M_HOSTAP); + malo_setmcastfilter(sc); + + return ENXIO; +} + +static void +malo_tx_draintxq(struct malo_softc *sc, struct malo_txq *txq) +{ + struct ieee80211_node *ni; + struct malo_txbuf *bf; + u_int ix; + + /* + * NB: this assumes output has been stopped and + * we do not need to block malo_tx_tasklet + */ + for (ix = 0;; ix++) { + MALO_TXQ_LOCK(txq); + bf = STAILQ_FIRST(&txq->active); + if (bf == NULL) { + MALO_TXQ_UNLOCK(txq); + break; + } + STAILQ_REMOVE_HEAD(&txq->active, bf_list); + MALO_TXQ_UNLOCK(txq); +#ifdef MALO_DEBUG + if (sc->malo_debug & MALO_DEBUG_RESET) { + const struct malo_txrec *tr = + mtod(bf->bf_m, const struct malo_txrec *); + malo_printtxbuf(bf, txq->qnum, ix); + ieee80211_dump_pkt(&sc->malo_ic, + (const uint8_t *)&tr->wh, + bf->bf_m->m_len - sizeof(tr->fwlen), 0, -1); + } +#endif /* MALO_DEBUG */ + bus_dmamap_unload(sc->malo_dmat, bf->bf_dmamap); + ni = bf->bf_node; + bf->bf_node = NULL; + if (ni != NULL) { + /* + * Reclaim node reference. + */ + ieee80211_free_node(ni); + } + m_freem(bf->bf_m); + bf->bf_m = NULL; + + MALO_TXQ_LOCK(txq); + STAILQ_INSERT_TAIL(&txq->free, bf, bf_list); + txq->nfree++; + MALO_TXQ_UNLOCK(txq); + } +} + +static void +malo_stop_locked(struct ifnet *ifp, int disable) +{ + int i; + struct malo_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->malo_ic; + struct malo_hal *mh = sc->malo_mh; + + DPRINTF(sc, MALO_DEBUG_ANY, "%s: invalid %u if_flags 0x%x\n", + __func__, sc->malo_invalid, ifp->if_flags); + + MALO_LOCK_ASSERT(sc); + + if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) + return; + + /* + * Shutdown the hardware and driver: + * reset 802.11 state machine + * turn off timers + * disable interrupts + * turn off the radio + * clear transmit machinery + * clear receive machinery + * drain and release tx queues + * reclaim beacon resources + * power down hardware + * + * Note that some of this work is not possible if the hardware + * is gone (invalid). + */ + ieee80211_new_state(ic, IEEE80211_S_INIT, -1); + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + ifp->if_timer = 0; + if (sc->malo_fw_loaded == 1) { + /* diable interrupt. */ + malo_hal_intrset(mh, 0); + /* turn off the radio. */ + malo_hal_setradio(mh, 0, MHP_AUTO_PREAMBLE); + } + + /* drain and release tx queues. */ + for (i = 0; i < MALO_NUM_TX_QUEUES; i++) + malo_tx_draintxq(sc, &sc->malo_txq[i]); +} + +static int +malo_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ +#define MALO_IS_RUNNING(ifp) \ + ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING)) + struct malo_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->malo_ic; + int error = 0; + + MALO_LOCK(sc); + + switch (cmd) { + case SIOCSIFFLAGS: + if (MALO_IS_RUNNING(ifp)) { + /* + * To avoid rescanning another access point, + * do not call malo_init() here. Instead, + * only reflect promisc mode settings. + */ + malo_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->malo_invalid) + malo_init(sc); + } else + malo_stop_locked(ifp, 1); + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + /* + * The upper layer has already installed/removed + * the multicast address(es), just recalculate the + * multicast filter for the card. + */ + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + malo_mode_init(sc); + break; + default: + error = ieee80211_ioctl(ic, cmd, data); + if (error == ENETRESET) { + if (MALO_IS_RUNNING(ifp) && + ic->ic_roaming != IEEE80211_ROAMING_MANUAL) + malo_init(sc); + error = 0; + } + if (error == ERESTART) { + /* XXX we need to reset the device here. */ + error = 0; + } + break; + } + + MALO_UNLOCK(sc); + + return error; +#undef MALO_IS_RUNNING +} + +/* + * 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 +malo_updateslot(struct ifnet *ifp) +{ + struct malo_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->malo_ic; + struct malo_hal *mh = sc->malo_mh; + int error; + + /* NB: can be called early; suppress needless cmds */ + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + return; + + DPRINTF(sc, MALO_DEBUG_RESET, + "%s: chan %u MHz/flags 0x%x %s slot, (ic_flags 0x%x)\n", + __func__, ic->ic_curchan->ic_freq, ic->ic_curchan->ic_flags, + ic->ic_flags & IEEE80211_F_SHSLOT ? "short" : "long", ic->ic_flags); + + if (ic->ic_flags & IEEE80211_F_SHSLOT) + error = malo_hal_set_slot(mh, 1); + else + error = malo_hal_set_slot(mh, 0); + + if (error != 0) + device_printf(sc->malo_dev, "setting %s slot failed\n", + ic->ic_flags & IEEE80211_F_SHSLOT ? "short" : "long"); +} + +static int +malo_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) +{ + struct ieee80211_node *ni = ic->ic_bss; + struct ifnet *ifp = ic->ic_ifp; + struct malo_softc *sc = ifp->if_softc; + struct malo_hal *mh = sc->malo_mh; + int error; + + DPRINTF(sc, MALO_DEBUG_STATE, "%s: %s -> %s\n", __func__, + ieee80211_state_name[ic->ic_state], + ieee80211_state_name[nstate]); + + /* + * Carry out firmware actions per-state. + */ + switch (nstate) { + case IEEE80211_S_INIT: + case IEEE80211_S_SCAN: + case IEEE80211_S_AUTH: + /* NB: do nothing. */ + break; + case IEEE80211_S_ASSOC: + malo_hal_setradio(mh, 1, + (ic->ic_flags & IEEE80211_F_SHPREAMBLE) ? + MHP_SHORT_PREAMBLE : MHP_LONG_PREAMBLE); + break; + case IEEE80211_S_RUN: + DPRINTF(sc, MALO_DEBUG_STATE, + "%s: %s(RUN): ic_flags 0x%08x bintvl %d bssid %s " + "capinfo 0x%04x chan %d\n", + ifp->if_xname, __func__, ic->ic_flags, + ni->ni_intval, ether_sprintf(ni->ni_bssid), ni->ni_capinfo, + ieee80211_chan2ieee(ic, ic->ic_curchan)); + + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + DPRINTF(sc, MALO_DEBUG_STATE, "%s: %s: aid 0x%x\n", + ic->ic_ifp->if_xname, __func__, ni->ni_associd); + malo_hal_setassocid(sc->malo_mh, + ni->ni_bssid, ni->ni_associd); + + if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) + /* automatic rate adaption */ + malo_hal_set_rate(mh, ic->ic_curmode, 0); + else + /* fixed rate */ + malo_hal_set_rate(mh, ic->ic_curmode, + malo_fix2rate(ic->ic_fixed_rate)); + break; + default: + break; + } + + break; + default: + if_printf(ifp, "%s: can't handle state %s -> %s\n", + __func__, ieee80211_state_name[ic->ic_state], + ieee80211_state_name[nstate]); + } + + /* + * Invoke the parent method to complete the work. + */ + error = sc->malo_newstate(ic, nstate, arg); + + return error; +} + +static int +malo_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 malo_softc *sc = ifp->if_softc; + struct malo_txbuf *bf; + struct malo_txq *txq; + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || sc->malo_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->malo_txq[0]; + bf = malo_getbuf(sc, txq); + if (bf == NULL) { + /* 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 (malo_tx_start(sc, ni, bf, m) != 0) { + ifp->if_oerrors++; + bf->bf_m = NULL; + bf->bf_node = NULL; + MALO_TXQ_LOCK(txq); + STAILQ_INSERT_HEAD(&txq->free, bf, bf_list); + txq->nfree++; + MALO_TXQ_UNLOCK(txq); + + 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 malo_tx_start is + * an optimization to avoid poking the firmware for each packet. + * + * NB: the queue id isn't used so 0 is ok. + */ + malo_hal_txstart(sc->malo_mh, 0/*XXX*/); + + return 0; +} + +static int +malo_media_change(struct ifnet *ifp) +{ +#define IS_UP(ifp) \ + ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING)) + int error; + + error = ieee80211_media_change(ifp); + if (error == ENETRESET) { + struct malo_softc *sc = ifp->if_softc; + + if (IS_UP(ifp)) + malo_init(sc); + error = 0; + } + return error; +#undef IS_UP +} + +static void +malo_bpfattach(struct malo_softc *sc) +{ + struct ifnet *ifp = sc->malo_ifp; + + bpfattach2(ifp, DLT_IEEE802_11_RADIO, + sizeof(struct ieee80211_frame) + sizeof(sc->malo_tx_th), + &sc->malo_drvbpf); + + /* + * Initialize constant fields. + * XXX make header lengths a multiple of 32-bits so subsequent + * headers are properly aligned; this is a kludge to keep + * certain applications happy. + * + * NB: the channel is setup each time we transition to the + * RUN state to avoid filling it in for each frame. + */ + sc->malo_tx_th_len = roundup(sizeof(sc->malo_tx_th), sizeof(uint32_t)); + sc->malo_tx_th.wt_ihdr.it_len = htole16(sc->malo_tx_th_len); + sc->malo_tx_th.wt_ihdr.it_present = htole32(MALO_TX_RADIOTAP_PRESENT); + + sc->malo_rx_th_len = roundup(sizeof(sc->malo_rx_th), sizeof(uint32_t)); + sc->malo_rx_th.wr_ihdr.it_len = htole16(sc->malo_rx_th_len); + sc->malo_rx_th.wr_ihdr.it_present = htole32(MALO_RX_RADIOTAP_PRESENT); +} + +static void +malo_sysctlattach(struct malo_softc *sc) +{ +#ifdef MALO_DEBUG + struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->malo_dev); + struct sysctl_oid *tree = device_get_sysctl_tree(sc->malo_dev); + + sc->malo_debug = malo_debug; + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "debug", CTLFLAG_RW, &sc->malo_debug, 0, + "control debugging printfs"); +#endif +} + +static void +malo_announce(struct malo_softc *sc) +{ + struct ifnet *ifp = sc->malo_ifp; + + if_printf(ifp, "versions [hw %d fw %d.%d.%d.%d] (regioncode %d)\n", + sc->malo_hwspecs.hwversion, + (sc->malo_hwspecs.fw_releasenum >> 24) & 0xff, + (sc->malo_hwspecs.fw_releasenum >> 16) & 0xff, + (sc->malo_hwspecs.fw_releasenum >> 8) & 0xff, + (sc->malo_hwspecs.fw_releasenum >> 0) & 0xff, + sc->malo_hwspecs.regioncode); + + if (bootverbose || malo_rxbuf != MALO_RXBUF) + if_printf(ifp, "using %u rx buffers\n", malo_rxbuf); + if (bootverbose || malo_txbuf != MALO_TXBUF) + if_printf(ifp, "using %u tx buffers\n", malo_txbuf); +} + +/* + * Convert net80211 channel to a HAL channel. + */ +static void +malo_mapchan(struct malo_hal_channel *hc, const struct ieee80211_channel *chan) +{ + hc->channel = chan->ic_ieee; + + *(uint32_t *)&hc->flags = 0; + if (IEEE80211_IS_CHAN_2GHZ(chan)) + hc->flags.freqband = MALO_FREQ_BAND_2DOT4GHZ; +} + +/* + * Set/change channels. If the channel is really being changed, + * it's done by reseting the chip. To accomplish this we must + * first cleanup any pending DMA, then restart stuff after a la + * malo_init. + */ +static int +malo_chan_set(struct malo_softc *sc, struct ieee80211_channel *chan) +{ + struct malo_hal *mh = sc->malo_mh; + struct malo_hal_channel hchan; + + DPRINTF(sc, MALO_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. + */ + malo_mapchan(&hchan, chan); + malo_hal_intrset(mh, 0); /* disable interrupts */ + malo_hal_setchannel(mh, &hchan); + malo_hal_settxpower(mh, &hchan); + + /* + * Update internal state. + */ + sc->malo_tx_th.wt_chan_freq = htole16(chan->ic_freq); + sc->malo_rx_th.wr_chan_freq = htole16(chan->ic_freq); + if (IEEE80211_IS_CHAN_ANYG(chan)) { + sc->malo_tx_th.wt_chan_flags = htole16(IEEE80211_CHAN_G); + sc->malo_rx_th.wr_chan_flags = htole16(IEEE80211_CHAN_G); + } else { + sc->malo_tx_th.wt_chan_flags = htole16(IEEE80211_CHAN_B); + sc->malo_rx_th.wr_chan_flags = htole16(IEEE80211_CHAN_B); + } + sc->malo_curchan = hchan; + malo_hal_intrset(mh, sc->malo_imask); + + return 0; +} + +static void +malo_scan_start(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + struct malo_softc *sc = ifp->if_softc; + + DPRINTF(sc, MALO_DEBUG_STATE, "%s\n", __func__); +} + +static void +malo_scan_end(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + struct malo_softc *sc = ifp->if_softc; + + DPRINTF(sc, MALO_DEBUG_STATE, "%s\n", __func__); +} + +static void +malo_set_channel(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + struct malo_softc *sc = ifp->if_softc; + + (void) malo_chan_set(sc, ic->ic_curchan); +} + +static void +malo_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 malo_softc *sc = arg; + struct malo_rxbuf *bf; + struct ieee80211com *ic = &sc->malo_ic; + struct ifnet *ifp = sc->malo_ifp; + struct malo_rxdesc *ds; + struct mbuf *m, *mnew; + struct ieee80211_qosframe *wh; + struct ieee80211_qosframe_addr4 *wh4; + struct ieee80211_node *ni; + int off, len, hdrlen, pktlen, rssi, ntodo; + uint8_t *data, status; + uint32_t readptr, writeptr; + + DPRINTF(sc, MALO_DEBUG_RX_PROC, + "%s: pending %u rdptr(0x%x) 0x%x wrptr(0x%x) 0x%x\n", + __func__, npending, + sc->malo_hwspecs.rxdesc_read, + malo_bar0_read4(sc, sc->malo_hwspecs.rxdesc_read), + sc->malo_hwspecs.rxdesc_write, + malo_bar0_read4(sc, sc->malo_hwspecs.rxdesc_write)); + + readptr = malo_bar0_read4(sc, sc->malo_hwspecs.rxdesc_read); + writeptr = malo_bar0_read4(sc, sc->malo_hwspecs.rxdesc_write); + if (readptr == writeptr) + return; + + bf = sc->malo_rxnext; + for (ntodo = malo_rxquota; ntodo > 0 && (readptr != writeptr); + ntodo--) { + if (bf == NULL) { + bf = STAILQ_FIRST(&sc->malo_rxbuf); + break; + } + ds = bf->bf_desc; + if (bf->bf_m == 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, MALO_DEBUG_ANY, + "%s: rx buf w/o dma memory\n", __func__); + (void)malo_rxbuf_init(sc, bf); + break; + } + MALO_RXDESC_SYNC(sc, ds, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + if (ds->rxcontrol != MALO_RXD_CTRL_DMA_OWN) + break; + + readptr = le32toh(ds->physnext); + +#ifdef MALO_DEBUG + if (sc->malo_debug & MALO_DEBUG_RECV_DESC) + malo_printrxbuf(bf, 0); +#endif + status = ds->status; + if (status & MALO_RXD_STATUS_DECRYPT_ERR_MASK) { + ifp->if_ierrors++; + goto rx_next; + } + /* + * Sync the data buffer. + */ + len = le16toh(ds->pktlen); + bus_dmamap_sync(sc->malo_dmat, bf->bf_dmamap, + 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. + */ + m = bf->bf_m; + data = mtod(m, uint8_t *); + hdrlen = ieee80211_anyhdrsize(data + sizeof(uint16_t)); + off = sizeof(uint16_t) + sizeof(struct ieee80211_frame_addr4); + + /* + * Calculate RSSI. XXX wrong + */ + rssi = 2 * ((int) ds->snr - ds->nf); /* NB: .5 dBm */ + if (rssi > 100) + rssi = 100; + + 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. + */ + + /* XXX don't need mbuf, just dma buffer */ + mnew = malo_getrxmbuf(sc, bf); + if (mnew == NULL) { + ifp->if_ierrors++; + goto rx_next; + } + + /* + * Attach the dma buffer to the mbuf; malo_rxbuf_init will + * re-setup the rx descriptor using the replacement dma + * buffer we just installed above. + */ + bf->bf_m = mnew; + m->m_data += off - hdrlen; + m->m_pkthdr.len = m->m_len = pktlen; + m->m_pkthdr.rcvif = ifp; + + /* + * 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; + } + } + if (sc->malo_drvbpf != NULL) { + sc->malo_rx_th.wr_flags = 0; + sc->malo_rx_th.wr_rate = ds->rate; + sc->malo_rx_th.wr_antsignal = rssi; + sc->malo_rx_th.wr_antnoise = ds->nf; + + bpf_mtap2(sc->malo_drvbpf, + &sc->malo_rx_th, sc->malo_rx_th_len, m); + } +#ifdef MALO_DEBUG + if (IFF_DUMPPKTS_RECV(sc, wh)) { + ieee80211_dump_pkt(ic, mtod(m, caddr_t), + len, ds->rate, rssi); + } +#endif + ifp->if_ipackets++; + + /* dispatch */ + ni = ieee80211_find_rxnode(ic, + (const struct ieee80211_frame_min *) wh); + (void) ieee80211_input(ic, m, ni, rssi, ds->nf, 0/*XXX*/); + ieee80211_free_node(ni); + +rx_next: + /* NB: ignore ENOMEM so we process more descriptors */ + (void) malo_rxbuf_init(sc, bf); + bf = STAILQ_NEXT(bf, bf_list); + } + + malo_bar0_write4(sc, sc->malo_hwspecs.rxdesc_read, readptr); + sc->malo_rxnext = bf; + + if ((ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0 && + !IFQ_IS_EMPTY(&ifp->if_snd)) + malo_start(ifp); +#undef IEEE80211_DIR_DSTODS +} + +static void +malo_stop(struct ifnet *ifp, int disable) +{ + struct malo_softc *sc = ifp->if_softc; + + MALO_LOCK(sc); + + malo_stop_locked(ifp, disable); + + MALO_UNLOCK(sc); +} + +/* + * Reclaim all tx queue resources. + */ +static void +malo_tx_cleanup(struct malo_softc *sc) +{ + int i; + + for (i = 0; i < MALO_NUM_TX_QUEUES; i++) + malo_tx_cleanupq(sc, &sc->malo_txq[i]); +} + +int +malo_detach(struct malo_softc *sc) +{ + struct ifnet *ifp = sc->malo_ifp; + + DPRINTF(sc, MALO_DEBUG_ANY, "%s: if_flags %x\n", + __func__, ifp->if_flags); + + malo_stop(ifp, 1); + + if (sc->malo_tq != NULL) { + taskqueue_drain(sc->malo_tq, &sc->malo_rxtask); + taskqueue_drain(sc->malo_tq, &sc->malo_txtask); + taskqueue_free(sc->malo_tq); + sc->malo_tq = NULL; + } + + bpfdetach(ifp); + + /* + * 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(&sc->malo_ic); + malo_dma_cleanup(sc); + malo_tx_cleanup(sc); + malo_hal_detach(sc->malo_mh); + if_free(ifp); + + MALO_LOCK_DESTROY(sc); + + return 0; +} + +void +malo_shutdown(struct malo_softc *sc) +{ + + malo_stop(sc->malo_ifp, 1); +} + +void +malo_suspend(struct malo_softc *sc) +{ + struct ifnet *ifp = sc->malo_ifp; + + DPRINTF(sc, MALO_DEBUG_ANY, "%s: if_flags %x\n", + __func__, ifp->if_flags); + + malo_stop(ifp, 1); +} + +void +malo_resume(struct malo_softc *sc) +{ + struct ifnet *ifp = sc->malo_ifp; + + DPRINTF(sc, MALO_DEBUG_ANY, "%s: if_flags %x\n", + __func__, ifp->if_flags); + + if (ifp->if_flags & IFF_UP) { + malo_init(sc); + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + malo_start(ifp); + } +} diff --git a/sys/dev/malo/if_malo.h b/sys/dev/malo/if_malo.h new file mode 100644 index 0000000..0cb5bc6 --- /dev/null +++ b/sys/dev/malo/if_malo.h @@ -0,0 +1,586 @@ +/*- + * Copyright (c) 2007 Marvell Semiconductor, Inc. + * Copyright (c) 2007 Sam Leffler, Errno Consulting + * Copyright (c) 2008 Weongyo Jeong <weongyo@freebsd.org> + * 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 88W8335 Wireless LAN controller. + */ +#ifndef _DEV_MALO_H +#define _DEV_MALO_H + +#include <net80211/ieee80211_radiotap.h> +#include <dev/malo/if_malohal.h> +#include <dev/malo/if_maloioctl.h> + +#ifndef MALO_TXBUF +#define MALO_TXBUF 256 /* number of TX descriptors/buffers */ +#endif +#ifndef MALO_RXBUF +#define MALO_RXBUF 256 /* number of RX descriptors/buffers */ +#endif + +#define MALO_TXDESC 1 /* max tx descriptors/segments */ + +#define MALO_RXSIZE PAGE_SIZE +#define MALO_RSSI_DUMMY_MARKER 127 +#define MALO_RSSI_EP_MULTIPLIER (1<<7) /* pow2 to optimize out * and / */ + +#define MALO_REG_INT_CODE 0x00000C14 +/* From host to ARM */ +#define MALO_REG_H2A_INTERRUPT_EVENTS 0x00000C18 + +/* bit definitions for MALO_REG_H2A_INTERRUPT_CAUSE */ +#define MALO_H2ARIC_BIT_PPA_READY 0x00000001 +#define MALO_H2ARIC_BIT_DOOR_BELL 0x00000002 /* bit 1 */ +#define MALO_H2ARIC_BIT_PS 0x00000004 +#define MALO_H2ARIC_BIT_PSPOLL 0x00000008 /* bit 3 */ + +/* From ARM to host */ +#define MALO_REG_A2H_INTERRUPT_CAUSE 0x00000C30 +#define MALO_REG_A2H_INTERRUPT_MASK 0x00000C34 +#define MALO_REG_A2H_INTERRUPT_CLEAR_SEL 0x00000C38 +#define MALO_REG_A2H_INTERRUPT_STATUS_MASK 0x00000C3C + +/* bit definitions for MALO_REG_A2H_INTERRUPT_CAUSE */ +#define MALO_A2HRIC_BIT_TX_DONE 0x00000001 /* bit 0 */ +#define MALO_A2HRIC_BIT_RX_RDY 0x00000002 /* bit 1 */ +#define MALO_A2HRIC_BIT_OPC_DONE 0x00000004 +#define MALO_A2HRIC_BIT_MAC_EVENT 0x00000008 +#define MALO_A2HRIC_BIT_RX_PROBLEM 0x00000010 +#define MALO_A2HRIC_BIT_RADIO_OFF 0x00000020 /* bit 5 */ +#define MALO_A2HRIC_BIT_RADIO_ON 0x00000040 +#define MALO_A2HRIC_BIT_RADAR_DETECT 0x00000080 +#define MALO_A2HRIC_BIT_ICV_ERROR 0x00000100 +#define MALO_A2HRIC_BIT_MIC_ERROR 0x00000200 /* bit 9 */ +#define MALO_A2HRIC_BIT_QUEUE_EMPTY 0x00000400 +#define MALO_A2HRIC_BIT_QUEUE_FULL 0x00000800 +#define MALO_A2HRIC_BIT_CHAN_SWITCH 0x00001000 +#define MALO_A2HRIC_BIT_TX_WATCHDOG 0x00002000 +#define MALO_A2HRIC_BIT_BA_WATCHDOG 0x00004000 + +#define MALO_ISR_SRC_BITS \ + (MALO_A2HRIC_BIT_RX_RDY | \ + MALO_A2HRIC_BIT_TX_DONE | \ + MALO_A2HRIC_BIT_OPC_DONE | \ + MALO_A2HRIC_BIT_MAC_EVENT | \ + MALO_A2HRIC_BIT_MIC_ERROR | \ + MALO_A2HRIC_BIT_ICV_ERROR | \ + MALO_A2HRIC_BIT_RADAR_DETECT | \ + MALO_A2HRIC_BIT_CHAN_SWITCH | \ + MALO_A2HRIC_BIT_TX_WATCHDOG | \ + MALO_A2HRIC_BIT_QUEUE_EMPTY) +#define MALO_ISR_RESET (1<<15) + +#define MALO_A2HRIC_BIT_MASK MALO_ISR_SRC_BITS + +/* map to 0x80000000 on BAR1 */ +#define MALO_REG_GEN_PTR 0x00000C10 +#define MALO_REG_INT_CODE 0x00000C14 +#define MALO_REG_SCRATCH 0x00000C40 + +/* + * 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 MALO_HOSTCMD_STA_MODE 0x5A +#define MALO_HOSTCMD_STA_FWRDY_SIGNATURE 0xF0F1F2F4 + +/* + * 16 bit host command code + */ +#define MALO_HOSTCMD_NONE 0x0000 +#define MALO_HOSTCMD_CODE_DNLD 0x0001 +#define MALO_HOSTCMD_GET_HW_SPEC 0x0003 +#define MALO_HOSTCMD_SET_HW_SPEC 0x0004 +#define MALO_HOSTCMD_MAC_MULTICAST_ADR 0x0010 +#define MALO_HOSTCMD_SET_WEPKEY 0x0013 +#define MALO_HOSTCMD_802_11_RADIO_CONTROL 0x001c +#define MALO_HOSTCMD_802_11_RF_TX_POWER 0x001e +#define MALO_HOSTCMD_802_11_RF_ANTENNA 0x0020 +#define MALO_HOSTCMD_SET_PRE_SCAN 0x0107 +#define MALO_HOSTCMD_SET_POST_SCAN 0x0108 +#define MALO_HOSTCMD_SET_RF_CHANNEL 0x010a +#define MALO_HOSTCMD_SET_AID 0x010d +#define MALO_HOSTCMD_SET_RATE 0x0110 +#define MALO_HOSTCMD_SET_SLOT 0x0114 +/* define DFS lab commands */ +#define MALO_HOSTCMD_SET_FIXED_RATE 0x0126 +#define MALO_HOSTCMD_SET_REGION_POWER 0x0128 +#define MALO_HOSTCMD_GET_CALTABLE 0x1134 + +/* + * definition of action or option for each command. + */ +/* define general purpose action */ +#define MALO_HOSTCMD_ACT_GEN_GET 0x0000 +#define MALO_HOSTCMD_ACT_GEN_SET 0x0001 +#define MALO_HOSTCMD_ACT_GEN_SET_LIST 0x0002 + +/* define action or option for HostCmd_FW_USE_FIXED_RATE */ +#define MALO_HOSTCMD_ACT_USE_FIXED_RATE 0x0001 +#define MALO_HOSTCMD_ACT_NOT_USE_FIXED_RATE 0x0002 + +/* INT code register event definition */ +#define MALO_INT_CODE_CMD_FINISHED 0x00000005 + +struct malo_cmd_header { + uint16_t cmd; + uint16_t length; + uint16_t seqnum; + uint16_t result; +} __packed; + +struct malo_cmd_caltable { + struct malo_cmd_header cmdhdr; + uint8_t annex; + uint8_t index; + uint8_t len; + uint8_t reserverd; +#define MALO_CAL_TBL_SIZE 160 + uint8_t caltbl[MALO_CAL_TBL_SIZE]; +} __packed; + +struct malo_cmd_get_hwspec { + struct malo_cmd_header cmdhdr; + u_int8_t version; /* version of the HW */ + u_int8_t hostif; /* host interface */ + /* Max. number of WCB FW can handle */ + u_int16_t num_wcb; + /* MaxNbr of MC addresses FW can handle */ + u_int16_t num_mcastaddr; + /* MAC address programmed in HW */ + u_int8_t permaddr[6]; + u_int16_t regioncode; + /* Number of antenna used */ + u_int16_t num_antenna; + /* 4 byte of FW release number */ + u_int32_t fw_releasenum; + u_int32_t wcbbase0; + u_int32_t rxpdwr_ptr; + u_int32_t rxpdrd_ptr; + u_int32_t ul_fw_awakecookie; + u_int32_t wcbbase1; + u_int32_t wcbbase2; + u_int32_t wcbbase3; +} __packed; + +struct malo_cmd_set_hwspec { + struct malo_cmd_header cmdhdr; + uint8_t version; /* HW revision */ + uint8_t hostif; /* Host interface */ + /* Max. number of Multicast address FW can handle */ + uint16_t num_mcastaddr; + uint8_t permaddr[6]; /* MAC address */ + uint16_t regioncode; /* Region Code */ + /* 4 byte of FW release number */ + uint32_t fwreleasenum; + /* Firmware awake cookie */ + uint32_t ul_fw_awakecookie; + /* Device capabilities (see above) */ + uint32_t devicecaps; + uint32_t rxpdwrptr; /* Rx shared memory queue */ + /* # TX queues in WcbBase array */ + uint32_t num_txqueues; + /* TX WCB Rings */ + uint32_t wcbbase[MALO_MAX_TXWCB_QUEUES]; + uint32_t flags; + uint32_t txwcbnum_per_queue; + uint32_t total_rxwcb; +} __packed; + +/* DS 802.11 */ +struct malo_cmd_rf_antenna { + struct malo_cmd_header cmdhdr; + uint16_t action; + /* Number of antennas or 0xffff (diversity) */ + uint16_t mode; +} __packed; + +struct malo_cmd_radio_control { + struct malo_cmd_header cmdhdr; + uint16_t action; + /* + * bit 0 : 1 = on, 0 = off + * bit 1 : 1 = long, 0 = short + * bit 2 : 1 = auto, 0 = fix + */ + uint16_t control; + uint16_t radio_on; +} __packed; + +struct malo_cmd_fw_set_wmmmode { + struct malo_cmd_header cmdhdr; + uint16_t action; /* 0 -> unset, 1 -> set */ +} __packed; + +struct malo_cmd_fw_set_rf_channel { + struct malo_cmd_header cmdhdr; + uint16_t action; + uint8_t cur_channel; /* channel # */ +} __packed; + +#define MALO_TX_POWER_LEVEL_TOTAL 8 +struct malo_cmd_rf_tx_power { + struct malo_cmd_header cmdhdr; + uint16_t action; + uint16_t support_txpower_level; + uint16_t current_txpower_level; + uint16_t reserved; + uint16_t power_levellist[MALO_TX_POWER_LEVEL_TOTAL]; +} __packed; + +struct malo_fixrate_flag { + /* lower rate after the retry count. 0 = legacy, 1 = HT */ + uint32_t type; + /* 0: retry count is not valid, 1: use retry count specified */ + uint32_t retrycount_valid; +} __packed; + +struct malo_fixed_rate_entry { + struct malo_fixrate_flag typeflags; + /* legacy rate(not index) or an MCS code. */ + uint32_t fixedrate; + uint32_t retrycount; +} __packed; + +struct malo_cmd_fw_use_fixed_rate { + struct malo_cmd_header cmdhdr; + /* + * MALO_HOSTCMD_ACT_GEN_GET 0x0000 + * MALO_HOSTCMD_ACT_GEN_SET 0x0001 + * MALO_HOSTCMD_ACT_NOT_USE_FIXED_RATE 0x0002 + */ + uint32_t action; + /* use fixed rate specified but firmware can drop to */ + uint32_t allowratedrop; + uint32_t entrycount; + struct malo_fixed_rate_entry fixedrate_table[4]; + uint8_t multicast_rate; + uint8_t multirate_txtype; + uint8_t management_rate; +} __packed; + +#define MALO_RATE_INDEX_MAX_ARRAY 14 + +struct malo_cmd_fw_set_aid { + struct malo_cmd_header cmdhdr; + uint16_t associd; + uint8_t macaddr[6]; /* AP's Mac Address(BSSID) */ + uint32_t gprotection; + uint8_t aprates[MALO_RATE_INDEX_MAX_ARRAY]; +} __packed; + +struct malo_cmd_prescan { + struct malo_cmd_header cmdhdr; +} __packed; + +struct malo_cmd_postscan { + struct malo_cmd_header cmdhdr; + uint32_t isibss; + uint8_t bssid[6]; +} __packed; + +struct malo_cmd_fw_setslot { + struct malo_cmd_header cmdhdr; + uint16_t action; + /* slot = 0 if regular, slot = 1 if short. */ + uint8_t slot; +}; + +struct malo_cmd_set_rate { + struct malo_cmd_header cmdhdr; + uint8_t dataratetype; + uint8_t rateindex; + uint8_t aprates[14]; +} __packed; + +struct malo_cmd_wepkey { + struct malo_cmd_header cmdhdr; + uint16_t action; + uint8_t len; + uint8_t flags; + uint16_t index; + uint8_t value[IEEE80211_KEYBUF_SIZE]; + uint8_t txmickey[IEEE80211_WEP_MICLEN]; + uint8_t rxmickey[IEEE80211_WEP_MICLEN]; + uint64_t rxseqctr; + uint64_t txseqctr; +} __packed; + +struct malo_cmd_mcast { + struct malo_cmd_header cmdhdr; + uint16_t action; + uint16_t numaddr; +#define MALO_HAL_MCAST_MAX 32 + uint8_t maclist[6*32]; +} __packed; + +/* + * DMA state for tx/rx descriptors. + */ + +/* + * Common "base class" for tx/rx descriptor resources + * allocated using the bus dma api. + */ +struct malo_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 */ +}; + +/* + * 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 malo_txdesc { + uint32_t status; +#define MALO_TXD_STATUS_IDLE 0x00000000 +#define MALO_TXD_STATUS_USED 0x00000001 +#define MALO_TXD_STATUS_OK 0x00000001 +#define MALO_TXD_STATUS_OK_RETRY 0x00000002 +#define MALO_TXD_STATUS_OK_MORE_RETRY 0x00000004 +#define MALO_TXD_STATUS_MULTICAST_TX 0x00000008 +#define MALO_TXD_STATUS_BROADCAST_TX 0x00000010 +#define MALO_TXD_STATUS_FAILED_LINK_ERROR 0x00000020 +#define MALO_TXD_STATUS_FAILED_EXCEED_LIMIT 0x00000040 +#define MALO_TXD_STATUS_FAILED_XRETRY MALO_TXD_STATUS_FAILED_EXCEED_LIMIT +#define MALO_TXD_STATUS_FAILED_AGING 0x00000080 +#define MALO_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 physnext; + uint32_t sap_pktinfo; + uint16_t format; +#define MALO_TXD_FORMAT 0x0001 /* frame format/rate */ +#define MALO_TXD_FORMAT_LEGACY 0x0000 /* legacy rate frame */ +#define MALO_TXD_RATE 0x01f8 /* tx rate (legacy)/ MCS */ +#define MALO_TXD_RATE_S 3 +/* NB: 3 is reserved */ +#define MALO_TXD_ANTENNA 0x1800 /* antenna select */ +#define MALO_TXD_ANTENNA_S 11 + uint16_t pad; /* align to 4-byte boundary */ +} __packed; + +#define MALO_TXDESC_SYNC(txq, ds, how) do { \ + bus_dmamap_sync((txq)->dma.dd_dmat, (txq)->dma.dd_dmamap, how); \ +} while(0) + +struct malo_rxdesc { + uint8_t rxcontrol; /* control element */ +#define MALO_RXD_CTRL_DRIVER_OWN 0x00 +#define MALO_RXD_CTRL_OS_OWN 0x04 +#define MALO_RXD_CTRL_DMA_OWN 0x80 + uint8_t snr; /* signal to noise ratio */ + uint8_t status; /* status field w/ USED bit */ +#define MALO_RXD_STATUS_IDLE 0x00 +#define MALO_RXD_STATUS_OK 0x01 +#define MALO_RXD_STATUS_MULTICAST_RX 0x02 +#define MALO_RXD_STATUS_BROADCAST_RX 0x04 +#define MALO_RXD_STATUS_FRAGMENT_RX 0x08 +#define MALO_RXD_STATUS_GENERAL_DECRYPT_ERR 0xff +#define MALO_RXD_STATUS_DECRYPT_ERR_MASK 0x80 +#define MALO_RXD_STATUS_TKIP_MIC_DECRYPT_ERR 0x02 +#define MALO_RXD_STATUS_WEP_ICV_DECRYPT_ERR 0x04 +#define MALO_RXD_STATUS_TKIP_ICV_DECRYPT_ERR 0x08 + uint8_t channel; /* channel # pkt received on */ + uint16_t pktlen; /* total length of received data */ + uint8_t nf; /* noise floor */ + uint8_t rate; /* received data rate */ + uint32_t physbuffdata; /* physical address of payload data */ + uint32_t physnext; /* physical address of next RX desc */ + uint16_t qosctrl; /* received QosCtrl field variable */ + uint16_t htsig2; /* like name states */ +} __packed; + +#define MALO_RXDESC_SYNC(sc, ds, how) do { \ + bus_dmamap_sync((sc)->malo_rxdma.dd_dmat, \ + (sc)->malo_rxdma.dd_dmamap, how); \ +} while (0) + +struct malo_rxbuf { + STAILQ_ENTRY(malo_rxbuf) bf_list; + void *bf_desc; /* h/w descriptor */ + bus_addr_t bf_daddr; /* physical addr of desc */ + bus_dmamap_t bf_dmamap; + bus_addr_t bf_data; /* physical addr of rx data */ + struct mbuf *bf_m; /* jumbo mbuf */ +}; +typedef STAILQ_HEAD(, malo_rxbuf) malo_rxbufhead; + +/* + * 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 malo_txbuf { + STAILQ_ENTRY(malo_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[MALO_TXDESC]; + struct mbuf *bf_m; + struct ieee80211_node *bf_node; + struct malo_txq *bf_txq; /* backpointer to tx q/ring */ +}; +typedef STAILQ_HEAD(, malo_txbuf) malo_txbufhead; + +/* + * 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 malo_txq { + struct malo_descdma dma; /* bus dma resources */ + struct mtx lock; /* tx q lock */ + char name[12]; /* e.g. "malo0_txq4" */ + int qnum; /* f/w q number */ + int txpri; /* f/w tx priority */ + int nfree; /* # buffers on free list */ + malo_txbufhead free; /* queue of free buffers */ + malo_txbufhead active; /* queue of active buffers */ +}; + +#define MALO_TXQ_LOCK_INIT(_sc, _tq) do { \ + snprintf((_tq)->name, sizeof((_tq)->name), "%s_txq%u", \ + device_get_nameunit((_sc)->malo_dev), (_tq)->qnum); \ + mtx_init(&(_tq)->lock, (_tq)->name, NULL, MTX_DEF); \ +} while (0) +#define MALO_TXQ_LOCK_DESTROY(_tq) mtx_destroy(&(_tq)->lock) +#define MALO_TXQ_LOCK(_tq) mtx_lock(&(_tq)->lock) +#define MALO_TXQ_UNLOCK(_tq) mtx_unlock(&(_tq)->lock) +#define MALO_TXQ_LOCK_ASSERT(_tq) mtx_assert(&(_tq)->lock, MA_OWNED) + +/* + * 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 malo_txrec { + uint16_t fwlen; + struct ieee80211_frame_addr4 wh; +} __packed; + +struct malo_softc { + struct ieee80211com malo_ic; /* IEEE 802.11 common */ + device_t malo_dev; + struct ifnet *malo_ifp; /* interface common */ + struct mtx malo_mtx; /* master lock (recursive) */ + struct taskqueue *malo_tq; /* private task queue */ + + bus_dma_tag_t malo_dmat; /* bus DMA tag */ + bus_space_handle_t malo_io0h; /* BAR 0 */ + bus_space_tag_t malo_io0t; + bus_space_handle_t malo_io1h; /* BAR 1 */ + bus_space_tag_t malo_io1t; + + unsigned int malo_invalid : 1,/* disable hardware accesses */ + malo_recvsetup : 1, /* recv setup */ + malo_fixedrate : 1, /* use fixed tx rate */ + malo_fw_loaded : 1; /* fw loaded */ + + struct malo_hal *malo_mh; /* h/w access layer */ + struct malo_hal_hwspec malo_hwspecs; /* h/w capabilities */ + struct malo_hal_txrxdma malo_hwdma; /* h/w dma setup */ + uint32_t malo_imask; /* interrupt mask copy */ + struct malo_hal_channel malo_curchan; + u_int16_t malo_rxantenna; /* rx antenna */ + u_int16_t malo_txantenna; /* tx antenna */ + + struct malo_descdma malo_rxdma; /* rx bus dma resources */ + malo_rxbufhead malo_rxbuf; /* rx buffers */ + struct malo_rxbuf *malo_rxnext; /* next rx buffer to process */ + struct task malo_rxtask; /* rx int processing */ + + struct malo_txq malo_txq[MALO_NUM_TX_QUEUES]; + struct task malo_txtask; /* tx int processing */ + + int (*malo_newstate)(struct ieee80211com *, + enum ieee80211_state, int); + + struct bpf_if *malo_drvbpf; + struct malo_tx_radiotap_header malo_tx_th; + int malo_tx_th_len; + struct malo_rx_radiotap_header malo_rx_th; + int malo_rx_th_len; + + struct malo_stats malo_stats; /* interface statistics */ + int malo_debug; +}; + +#define MALO_LOCK_INIT(_sc) \ + mtx_init(&(_sc)->malo_mtx, device_get_nameunit((_sc)->malo_dev), \ + NULL, MTX_DEF | MTX_RECURSE) +#define MALO_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->malo_mtx) +#define MALO_LOCK(_sc) mtx_lock(&(_sc)->malo_mtx) +#define MALO_UNLOCK(_sc) mtx_unlock(&(_sc)->malo_mtx) +#define MALO_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->malo_mtx, MA_OWNED) + +#define MALO_RXFREE_INIT(_sc) \ + mtx_init(&(_sc)->malo_rxlock, device_get_nameunit((_sc)->malo_dev), \ + NULL, MTX_DEF) +#define MALO_RXFREE_DESTROY(_sc) mtx_destroy(&(_sc)->malo_rxlock) +#define MALO_RXFREE_LOCK(_sc) mtx_lock(&(_sc)->malo_rxlock) +#define MALO_RXFREE_UNLOCK(_sc) mtx_unlock(&(_sc)->malo_rxlock) +#define MALO_RXFREE_ASSERT(_sc) mtx_assert(&(_sc)->malo_rxlock, \ + MA_OWNED) + +int malo_attach(uint16_t, struct malo_softc *); +int malo_intr(void *); +int malo_detach(struct malo_softc *); +void malo_shutdown(struct malo_softc *); +void malo_suspend(struct malo_softc *); +void malo_resume(struct malo_softc *); + +#endif diff --git a/sys/dev/malo/if_malo_pci.c b/sys/dev/malo/if_malo_pci.c new file mode 100644 index 0000000..a4c8e51 --- /dev/null +++ b/sys/dev/malo/if_malo_pci.c @@ -0,0 +1,373 @@ +/*- + * Copyright (c) 2007 Marvell Semiconductor, Inc. + * Copyright (c) 2007 Sam Leffler, Errno Consulting + * Copyright (c) 2008 Weongyo Jeong <weongyo@freebsd.org> + * 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 88W8335 Wireless LAN controller driver. + */ + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/socket.h> +#include <sys/sysctl.h> + +#include <machine/bus.h> +#include <sys/bus.h> +#include <sys/rman.h> + +#include <net/if.h> +#include <net/if_media.h> + +#include <net80211/ieee80211_var.h> + +#include <dev/malo/if_malo.h> + +#include <dev/pci/pcivar.h> +#include <dev/pci/pcireg.h> + +/* + * PCI glue. + */ + +#define MALO_RESOURCE_MAX 2 +#define MALO_MSI_MESSAGES 1 + +struct malo_pci_softc { + struct malo_softc malo_sc; + struct resource_spec *malo_mem_spec; + struct resource *malo_res_mem[MALO_RESOURCE_MAX]; + struct resource_spec *malo_irq_spec; + struct resource *malo_res_irq[MALO_MSI_MESSAGES]; + void *malo_intrhand[MALO_MSI_MESSAGES]; + int malo_msi; +}; + +/* + * Tunable variables. + */ +SYSCTL_DECL(_hw_malo); +SYSCTL_NODE(_hw_malo, OID_AUTO, pci, CTLFLAG_RD, 0, + "Marvell 88W8335 driver PCI parameters"); + +static int msi_disable = 0; /* MSI disabled */ +SYSCTL_INT(_hw_malo_pci, OID_AUTO, msi_disable, CTLFLAG_RW, &msi_disable, + 0, "MSI disabled"); +TUNABLE_INT("hw.malo.pci.msi_disable", &msi_disable); + +/* + * Devices supported by this driver. + */ +#define VENDORID_MARVELL 0X11AB +#define DEVICEID_MRVL_88W8310 0X1FA7 +#define DEVICEID_MRVL_88W8335R1 0X1FAA +#define DEVICEID_MRVL_88W8335R2 0X1FAB + +static struct malo_product { + uint16_t mp_vendorid; + uint16_t mp_deviceid; + const char *mp_name; +} malo_products[] = { + { VENDORID_MARVELL, DEVICEID_MRVL_88W8310, + "Marvell Libertas 88W8310 802.11g Wireless Adapter" }, + { VENDORID_MARVELL, DEVICEID_MRVL_88W8335R1, + "Marvell Libertas 88W8335 802.11g Wireless Adapter" }, + { VENDORID_MARVELL, DEVICEID_MRVL_88W8335R2, + "Marvell Libertas 88W8335 802.11g Wireless Adapter" } +}; + +static struct resource_spec malo_res_spec_mem[] = { + { SYS_RES_MEMORY, PCIR_BAR(0), RF_ACTIVE }, + { SYS_RES_MEMORY, PCIR_BAR(1), RF_ACTIVE }, + { -1, 0, 0 } +}; + +static struct resource_spec malo_res_spec_legacy[] = { + { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, + { -1, 0, 0 } +}; + +static struct resource_spec malo_res_spec_msi[] = { + { SYS_RES_IRQ, 1, RF_ACTIVE }, + { -1, 0, 0 } +}; + +static int malo_pci_detach(device_t); + +static int +malo_pci_probe(device_t dev) +{ +#define N(a) (sizeof(a) / sizeof((a)[0])) + struct malo_product *mp; + uint16_t vendor, devid; + int i; + + vendor = pci_get_vendor(dev); + devid = pci_get_device(dev); + mp = malo_products; + + for (i = 0; i < N(malo_products); i++, mp++) { + if (vendor == mp->mp_vendorid && devid == mp->mp_deviceid) { + device_set_desc(dev, mp->mp_name); + return (BUS_PROBE_DEFAULT); + } + } + + return (ENXIO); +#undef N +} + +static int +malo_pci_setup(device_t dev) +{ + + /* + * Enable memory mapping and bus mastering. + */ + if (pci_enable_busmaster(dev) != 0) + return -1; + if (pci_enable_io(dev, SYS_RES_MEMORY) != 0) + return -1; + + return 0; +} + +static int +malo_pci_attach(device_t dev) +{ + int error = ENXIO, i, msic, reg; + struct malo_pci_softc *psc = device_get_softc(dev); + struct malo_softc *sc = &psc->malo_sc; + + sc->malo_dev = dev; + + /* + * Enable memory mapping and bus mastering. + */ + if (malo_pci_setup(dev)) + return (ENXIO); + + /* + * Setup memory-mapping of PCI registers. + */ + psc->malo_mem_spec = malo_res_spec_mem; + error = bus_alloc_resources(dev, psc->malo_mem_spec, psc->malo_res_mem); + if (error) { + device_printf(dev, "couldn't allocate memory resources\n"); + return (ENXIO); + } + + /* + * Arrange and allocate interrupt line. + */ + sc->malo_invalid = 1; + + if (pci_find_extcap(dev, PCIY_EXPRESS, ®) == 0) { + msic = pci_msi_count(dev); + if (bootverbose) + device_printf(dev, "MSI count : %d\n", msic); + } else + msic = 0; + + psc->malo_irq_spec = malo_res_spec_legacy; + if (msic == MALO_MSI_MESSAGES && msi_disable == 0) { + if (pci_alloc_msi(dev, &msic) == 0) { + if (msic == MALO_MSI_MESSAGES) { + device_printf(dev, "Using %d MSI messages\n", + msic); + psc->malo_irq_spec = malo_res_spec_msi; + psc->malo_msi = 1; + } else + pci_release_msi(dev); + } + } + + error = bus_alloc_resources(dev, psc->malo_irq_spec, psc->malo_res_irq); + if (error) { + device_printf(dev, "couldn't allocate IRQ resources\n"); + goto bad; + } + + if (psc->malo_msi == 0) + error = bus_setup_intr(dev, psc->malo_res_irq[0], + INTR_TYPE_NET | INTR_MPSAFE, malo_intr, NULL, sc, + &psc->malo_intrhand[0]); + else { + for (i = 0; i < MALO_MSI_MESSAGES; i++) { + error = bus_setup_intr(dev, psc->malo_res_irq[i], + INTR_TYPE_NET | INTR_MPSAFE, malo_intr, NULL, sc, + &psc->malo_intrhand[i]); + if (error != 0) + break; + } + } + + /* + * 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 */ + 0, /* nsegments */ + BUS_SPACE_MAXADDR, /* maxsegsize */ + BUS_DMA_ALLOCNOW, /* flags */ + NULL, /* lockfunc */ + NULL, /* lockarg */ + &sc->malo_dmat)) { + device_printf(dev, "cannot allocate DMA tag\n"); + goto bad1; + } + + sc->malo_io0t = rman_get_bustag(psc->malo_res_mem[0]); + sc->malo_io0h = rman_get_bushandle(psc->malo_res_mem[0]); + sc->malo_io1t = rman_get_bustag(psc->malo_res_mem[1]); + sc->malo_io1h = rman_get_bushandle(psc->malo_res_mem[1]); + + error = malo_attach(pci_get_device(dev), sc); + + if (error != 0) { + malo_pci_detach(dev); + return (error); + } + + return (error); +bad1: + if (psc->malo_msi == 0) + bus_teardown_intr(dev, psc->malo_res_irq[0], + psc->malo_intrhand[0]); + else { + for (i = 0; i < MALO_MSI_MESSAGES; i++) + bus_teardown_intr(dev, psc->malo_res_irq[i], + psc->malo_intrhand[i]); + } + +bad: + if (psc->malo_msi != 0) + pci_release_msi(dev); + + return (error); +} + +static int +malo_pci_detach(device_t dev) +{ + int i; + struct malo_pci_softc *psc = device_get_softc(dev); + struct malo_softc *sc = &psc->malo_sc; + + /* check if device was removed */ + sc->malo_invalid = !bus_child_present(dev); + + malo_detach(sc); + + bus_generic_detach(dev); + + if (psc->malo_msi == 0) + bus_teardown_intr(dev, psc->malo_res_irq[0], + psc->malo_intrhand[0]); + else { + for (i = 0; i < MALO_MSI_MESSAGES; i++) + bus_teardown_intr(dev, psc->malo_res_irq[i], + psc->malo_intrhand[i]); + + pci_release_msi(dev); + } + + bus_release_resources(dev, psc->malo_irq_spec, psc->malo_res_irq); + bus_dma_tag_destroy(sc->malo_dmat); + bus_release_resources(dev, psc->malo_mem_spec, psc->malo_res_mem); + + return (0); +} + +static int +malo_pci_shutdown(device_t dev) +{ + struct malo_pci_softc *psc = device_get_softc(dev); + + malo_shutdown(&psc->malo_sc); + + return (0); +} + +static int +malo_pci_suspend(device_t dev) +{ + struct malo_pci_softc *psc = device_get_softc(dev); + + malo_suspend(&psc->malo_sc); + + return (0); +} + +static int +malo_pci_resume(device_t dev) +{ + struct malo_pci_softc *psc = device_get_softc(dev); + + if (!malo_pci_setup(dev)) + return ENXIO; + + malo_resume(&psc->malo_sc); + + return (0); +} + +static device_method_t malo_pci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, malo_pci_probe), + DEVMETHOD(device_attach, malo_pci_attach), + DEVMETHOD(device_detach, malo_pci_detach), + DEVMETHOD(device_shutdown, malo_pci_shutdown), + DEVMETHOD(device_suspend, malo_pci_suspend), + DEVMETHOD(device_resume, malo_pci_resume), + { 0,0 } +}; + +static driver_t malo_pci_driver = { + "malo", + malo_pci_methods, + sizeof(struct malo_pci_softc) +}; + +static devclass_t malo_devclass; +DRIVER_MODULE(if_malo, pci, malo_pci_driver, malo_devclass, 0, 0); +DRIVER_MODULE(if_malo, cardbus, malo_pci_driver, malo_devclass, 0, 0); +MODULE_VERSION(if_malo, 1); +MODULE_DEPEND(if_malo, wlan, 1, 1, 1); /* 802.11 media layer */ +MODULE_DEPEND(if_malo, malofw_fw, 1, 1, 1); diff --git a/sys/dev/malo/if_malohal.c b/sys/dev/malo/if_malohal.c new file mode 100644 index 0000000..f1c8c7b --- /dev/null +++ b/sys/dev/malo/if_malohal.c @@ -0,0 +1,918 @@ +/*- + * Copyright (c) 2007 Marvell Semiconductor, Inc. + * Copyright (c) 2007 Sam Leffler, Errno Consulting + * Copyright (c) 2008 Weongyo Jeong <weongyo@freebsd.org> + * 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 + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/endian.h> +#include <sys/kernel.h> +#include <sys/firmware.h> +#include <sys/socket.h> + +#include <machine/bus.h> +#include <sys/bus.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> + +#include <net80211/ieee80211_var.h> + +#include <dev/malo/if_malo.h> + +#define MALO_WAITOK 1 +#define MALO_NOWAIT 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) + +static __inline uint32_t +malo_hal_read4(struct malo_hal *mh, bus_size_t off) +{ + return bus_space_read_4(mh->mh_iot, mh->mh_ioh, off); +} + +static __inline void +malo_hal_write4(struct malo_hal *mh, bus_size_t off, uint32_t val) +{ + bus_space_write_4(mh->mh_iot, mh->mh_ioh, off, val); +} + +static void +malo_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 malo_hal * +malo_hal_attach(device_t dev, uint16_t devid, + bus_space_handle_t ioh, bus_space_tag_t iot, bus_dma_tag_t tag) +{ + int error; + struct malo_hal *mh; + + mh = malloc(sizeof(struct malo_hal), M_DEVBUF, M_NOWAIT | M_ZERO); + if (mh == NULL) + return NULL; + + mh->mh_dev = dev; + mh->mh_ioh = ioh; + mh->mh_iot = iot; + + 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 */ + MALO_CMDBUF_SIZE, /* maxsize */ + 1, /* nsegments */ + MALO_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 fail; + } + + /* 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 fail; + } + + 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 fail; + } + + error = bus_dmamap_load(mh->mh_dmat, mh->mh_dmamap, + mh->mh_cmdbuf, MALO_CMDBUF_SIZE, + malo_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 fail; + } + + return (mh); + +fail: + free(mh, M_DEVBUF); + + if (mh->mh_dmamap != NULL) { + bus_dmamap_unload(mh->mh_dmat, mh->mh_dmamap); + if (mh->mh_cmdbuf != NULL) + bus_dmamem_free(mh->mh_dmat, mh->mh_cmdbuf, + mh->mh_dmamap); + bus_dmamap_destroy(mh->mh_dmat, mh->mh_dmamap); + } + if (mh->mh_dmat) + bus_dma_tag_destroy(mh->mh_dmat); + + return (NULL); +} + +/* + * Low level firmware cmd block handshake support. + */ + +static void +malo_hal_send_cmd(struct malo_hal *mh) +{ + uint32_t dummy; + + bus_dmamap_sync(mh->mh_dmat, mh->mh_dmamap, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + malo_hal_write4(mh, MALO_REG_GEN_PTR, mh->mh_cmdaddr); + dummy = malo_hal_read4(mh, MALO_REG_INT_CODE); + + malo_hal_write4(mh, MALO_REG_H2A_INTERRUPT_EVENTS, + MALO_H2ARIC_BIT_DOOR_BELL); +} + +static int +malo_hal_waitforcmd(struct malo_hal *mh, uint16_t cmd) +{ +#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(cmd)) + return 1; + + DELAY(1 * 1000); + } + + return 0; +#undef MAX_WAIT_FW_COMPLETE_ITERATIONS +} + +static int +malo_hal_execute_cmd(struct malo_hal *mh, unsigned short cmd) +{ + MALO_HAL_LOCK_ASSERT(mh); + + if ((mh->mh_flags & MHF_FWHANG) && + (mh->mh_debug & MALO_HAL_DEBUG_IGNHANG) == 0) { + device_printf(mh->mh_dev, "firmware hung, skipping cmd 0x%x\n", + cmd); + return ENXIO; + } + + if (malo_hal_read4(mh, MALO_REG_INT_CODE) == 0xffffffff) { + device_printf(mh->mh_dev, "%s: device not present!\n", + __func__); + return EIO; + } + + malo_hal_send_cmd(mh); + if (!malo_hal_waitforcmd(mh, cmd | 0x8000)) { + device_printf(mh->mh_dev, + "timeout waiting for f/w cmd 0x%x\n", cmd); + mh->mh_flags |= MHF_FWHANG; + return ETIMEDOUT; + } + + bus_dmamap_sync(mh->mh_dmat, mh->mh_dmamap, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + return 0; +} + +static int +malo_hal_get_cal_table(struct malo_hal *mh, uint8_t annex, uint8_t index) +{ + struct malo_cmd_caltable *cmd; + int ret; + + MALO_HAL_LOCK_ASSERT(mh); + + _CMD_SETUP(cmd, struct malo_cmd_caltable, MALO_HOSTCMD_GET_CALTABLE); + cmd->annex = annex; + cmd->index = index; + + ret = malo_hal_execute_cmd(mh, MALO_HOSTCMD_GET_CALTABLE); + if (ret == 0 && cmd->caltbl[0] != annex && annex != 0 && annex != 255) + ret = EIO; + return ret; +} + +static int +malo_hal_get_pwrcal_table(struct malo_hal *mh, struct malo_hal_caldata *cal) +{ + const uint8_t *data; + int len; + + MALO_HAL_LOCK(mh); + /* NB: we hold the lock so it's ok to use cmdbuf */ + data = ((const struct malo_cmd_caltable *) mh->mh_cmdbuf)->caltbl; + if (malo_hal_get_cal_table(mh, 33, 0) == 0) { + len = (data[2] | (data[3] << 8)) - 12; + /* XXX validate len */ + memcpy(cal->pt_ratetable_20m, &data[12], len); + } + mh->mh_flags |= MHF_CALDATA; + MALO_HAL_UNLOCK(mh); + + return 0; +} + +/* + * Reset internal state after a firmware download. + */ +static int +malo_hal_resetstate(struct malo_hal *mh) +{ + /* + * Fetch cal data for later use. + * XXX may want to fetch other stuff too. + */ + if ((mh->mh_flags & MHF_CALDATA) == 0) + malo_hal_get_pwrcal_table(mh, &mh->mh_caldata); + return 0; +} + +static void +malo_hal_fw_reset(struct malo_hal *mh) +{ + + if (malo_hal_read4(mh, MALO_REG_INT_CODE) == 0xffffffff) { + device_printf(mh->mh_dev, "%s: device not present!\n", + __func__); + return; + } + + malo_hal_write4(mh, MALO_REG_H2A_INTERRUPT_EVENTS, MALO_ISR_RESET); + mh->mh_flags &= ~MHF_FWHANG; +} + +static void +malo_hal_trigger_pcicmd(struct malo_hal *mh) +{ + uint32_t dummy; + + bus_dmamap_sync(mh->mh_dmat, mh->mh_dmamap, BUS_DMASYNC_PREWRITE); + + malo_hal_write4(mh, MALO_REG_GEN_PTR, mh->mh_cmdaddr); + dummy = malo_hal_read4(mh, MALO_REG_INT_CODE); + + malo_hal_write4(mh, MALO_REG_INT_CODE, 0x00); + dummy = malo_hal_read4(mh, MALO_REG_INT_CODE); + + malo_hal_write4(mh, MALO_REG_H2A_INTERRUPT_EVENTS, + MALO_H2ARIC_BIT_DOOR_BELL); + dummy = malo_hal_read4(mh, MALO_REG_INT_CODE); +} + +static int +malo_hal_waitfor(struct malo_hal *mh, uint32_t val) +{ + int i; + + for (i = 0; i < MALO_FW_MAX_NUM_CHECKS; i++) { + DELAY(MALO_FW_CHECK_USECS); + if (malo_hal_read4(mh, MALO_REG_INT_CODE) == val) + return 0; + } + + return -1; +} + +/* + * Firmware block xmit when talking to the boot-rom. + */ +static int +malo_hal_send_helper(struct malo_hal *mh, int bsize, + const void *data, size_t dsize, int waitfor) +{ + mh->mh_cmdbuf[0] = htole16(MALO_HOSTCMD_CODE_DNLD); + mh->mh_cmdbuf[1] = htole16(bsize); + memcpy(&mh->mh_cmdbuf[4], data , dsize); + + malo_hal_trigger_pcicmd(mh); + + if (waitfor == MALO_NOWAIT) + goto pass; + + /* XXX 2000 vs 200 */ + if (malo_hal_waitfor(mh, MALO_INT_CODE_CMD_FINISHED) != 0) { + device_printf(mh->mh_dev, + "%s: timeout waiting for CMD_FINISHED, INT_CODE 0x%x\n", + __func__, malo_hal_read4(mh, MALO_REG_INT_CODE)); + + return ETIMEDOUT; + } + +pass: + malo_hal_write4(mh, MALO_REG_INT_CODE, 0); + + return (0); +} + +static int +malo_hal_fwload_helper(struct malo_hal *mh, char *helper) +{ + const struct firmware *fw; + int error; + + fw = firmware_get(helper); + if (fw == NULL) { + device_printf(mh->mh_dev, "could not read microcode %s!\n", + helper); + return (EIO); + } + + device_printf(mh->mh_dev, "load %s firmware image (%u bytes)\n", + helper, fw->datasize); + + error = malo_hal_send_helper(mh, fw->datasize, fw->data, fw->datasize, + MALO_WAITOK); + if (error != 0) + goto fail; + + /* tell the card we're done and... */ + error = malo_hal_send_helper(mh, 0, NULL, 0, MALO_NOWAIT); + +fail: + firmware_put(fw, FIRMWARE_UNLOAD); + + return (error); +} + +/* + * Firmware block xmit when talking to the 1st-stage loader. + */ +static int +malo_hal_send_main(struct malo_hal *mh, const void *data, size_t dsize, + uint16_t seqnum, int waitfor) +{ + mh->mh_cmdbuf[0] = htole16(MALO_HOSTCMD_CODE_DNLD); + mh->mh_cmdbuf[1] = htole16(dsize); + mh->mh_cmdbuf[2] = htole16(seqnum); + mh->mh_cmdbuf[3] = 0; + memcpy(&mh->mh_cmdbuf[4], data, dsize); + + malo_hal_trigger_pcicmd(mh); + + if (waitfor == MALO_NOWAIT) + goto pass; + + if (malo_hal_waitfor(mh, MALO_INT_CODE_CMD_FINISHED) != 0) { + device_printf(mh->mh_dev, + "%s: timeout waiting for CMD_FINISHED, INT_CODE 0x%x\n", + __func__, malo_hal_read4(mh, MALO_REG_INT_CODE)); + + return ETIMEDOUT; + } + +pass: + malo_hal_write4(mh, MALO_REG_INT_CODE, 0); + + return 0; +} + +static int +malo_hal_fwload_main(struct malo_hal *mh, char *firmware) +{ + const struct firmware *fw; + const uint8_t *fp; + int error; + size_t count; + uint16_t seqnum; + uint32_t blocksize; + + error = 0; + + fw = firmware_get(firmware); + if (fw == NULL) { + device_printf(mh->mh_dev, "could not read firmware %s!\n", + firmware); + return (EIO); + } + + device_printf(mh->mh_dev, "load %s firmware image (%u bytes)\n", + firmware, fw->datasize); + + seqnum = 1; + for (count = 0; count < fw->datasize; count += blocksize) { + blocksize = MIN(256, fw->datasize - count); + fp = (const uint8_t *)fw->data + count; + + error = malo_hal_send_main(mh, fp, blocksize, seqnum++, + MALO_NOWAIT); + if (error != 0) + goto fail; + DELAY(500); + } + + /* + * send a command with size 0 to tell that the firmware has been + * uploaded + */ + error = malo_hal_send_main(mh, NULL, 0, seqnum++, MALO_NOWAIT); + DELAY(100); + +fail: + firmware_put(fw, FIRMWARE_UNLOAD); + + return (error); +} + +int +malo_hal_fwload(struct malo_hal *mh, char *helper, char *firmware) +{ + int error, i; + uint32_t fwreadysig, opmode; + + /* + * NB: now malo(4) supports only STA mode. It will be better if it + * supports AP mode. + */ + fwreadysig = MALO_HOSTCMD_STA_FWRDY_SIGNATURE; + opmode = MALO_HOSTCMD_STA_MODE; + + malo_hal_fw_reset(mh); + + malo_hal_write4(mh, MALO_REG_A2H_INTERRUPT_CLEAR_SEL, + MALO_A2HRIC_BIT_MASK); + malo_hal_write4(mh, MALO_REG_A2H_INTERRUPT_CAUSE, 0x00); + malo_hal_write4(mh, MALO_REG_A2H_INTERRUPT_MASK, 0x00); + malo_hal_write4(mh, MALO_REG_A2H_INTERRUPT_STATUS_MASK, + MALO_A2HRIC_BIT_MASK); + + error = malo_hal_fwload_helper(mh, helper); + if (error != 0) { + device_printf(mh->mh_dev, "failed to load bootrom loader.\n"); + goto fail; + } + + DELAY(200 * MALO_FW_CHECK_USECS); + + error = malo_hal_fwload_main(mh, firmware); + if (error != 0) { + device_printf(mh->mh_dev, "failed to load firmware.\n"); + goto fail; + } + + /* + * 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; + + if (opmode != MALO_HOSTCMD_STA_MODE) + malo_hal_trigger_pcicmd(mh); + + for (i = 0; i < MALO_FW_MAX_NUM_CHECKS; i++) { + malo_hal_write4(mh, MALO_REG_GEN_PTR, opmode); + DELAY(MALO_FW_CHECK_USECS); + if (malo_hal_read4(mh, MALO_REG_INT_CODE) == fwreadysig) { + malo_hal_write4(mh, MALO_REG_INT_CODE, 0x00); + return malo_hal_resetstate(mh); + } + } + + return ETIMEDOUT; +fail: + malo_hal_fw_reset(mh); + + return (error); +} + +/* + * Return "hw specs". Note this must be the first cmd MUST be done after + * a firmware download or the f/w will lockup. + */ +int +malo_hal_gethwspecs(struct malo_hal *mh, struct malo_hal_hwspec *hw) +{ + struct malo_cmd_get_hwspec *cmd; + int ret; + + MALO_HAL_LOCK(mh); + + _CMD_SETUP(cmd, struct malo_cmd_get_hwspec, MALO_HOSTCMD_GET_HW_SPEC); + memset(&cmd->permaddr[0], 0xff, IEEE80211_ADDR_LEN); + cmd->ul_fw_awakecookie = htole32((unsigned int)mh->mh_cmdaddr + 2048); + + ret = malo_hal_execute_cmd(mh, MALO_HOSTCMD_GET_HW_SPEC); + if (ret == 0) { + IEEE80211_ADDR_COPY(hw->macaddr, cmd->permaddr); + hw->wcbbase[0] = le32toh(cmd->wcbbase0) & 0x0000ffff; + hw->wcbbase[1] = le32toh(cmd->wcbbase1) & 0x0000ffff; + hw->wcbbase[2] = le32toh(cmd->wcbbase2) & 0x0000ffff; + hw->wcbbase[3] = le32toh(cmd->wcbbase3) & 0x0000ffff; + hw->rxdesc_read = le32toh(cmd->rxpdrd_ptr)& 0x0000ffff; + hw->rxdesc_write = le32toh(cmd->rxpdwr_ptr)& 0x0000ffff; + hw->regioncode = le16toh(cmd->regioncode) & 0x00ff; + hw->fw_releasenum = le32toh(cmd->fw_releasenum); + hw->maxnum_wcb = le16toh(cmd->num_wcb); + hw->maxnum_mcaddr = le16toh(cmd->num_mcastaddr); + hw->num_antenna = le16toh(cmd->num_antenna); + hw->hwversion = cmd->version; + hw->hostinterface = cmd->hostif; + } + + MALO_HAL_UNLOCK(mh); + + return ret; +} + +void +malo_hal_detach(struct malo_hal *mh) +{ + + 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); +} + +/* + * Configure antenna use. Takes effect immediately. + * + * XXX tx antenna setting ignored + * XXX rx antenna setting should always be 3 (for now) + */ +int +malo_hal_setantenna(struct malo_hal *mh, enum malo_hal_antenna dirset, int ant) +{ + struct malo_cmd_rf_antenna *cmd; + int ret; + + if (!(dirset == MHA_ANTENNATYPE_RX || dirset == MHA_ANTENNATYPE_TX)) + return EINVAL; + + MALO_HAL_LOCK(mh); + + _CMD_SETUP(cmd, struct malo_cmd_rf_antenna, + MALO_HOSTCMD_802_11_RF_ANTENNA); + cmd->action = htole16(dirset); + if (ant == 0) { /* default to all/both antennae */ + /* XXX never reach now. */ + ant = 3; + } + cmd->mode = htole16(ant); + + ret = malo_hal_execute_cmd(mh, MALO_HOSTCMD_802_11_RF_ANTENNA); + + MALO_HAL_UNLOCK(mh); + + return ret; +} + +/* + * Configure radio. Takes effect immediately. + * + * XXX preamble installed after set fixed rate cmd + */ +int +malo_hal_setradio(struct malo_hal *mh, int onoff, + enum malo_hal_preamble preamble) +{ + struct malo_cmd_radio_control *cmd; + int ret; + + MALO_HAL_LOCK(mh); + + _CMD_SETUP(cmd, struct malo_cmd_radio_control, + MALO_HOSTCMD_802_11_RADIO_CONTROL); + cmd->action = htole16(MALO_HOSTCMD_ACT_GEN_SET); + if (onoff == 0) + cmd->control = 0; + else + cmd->control = htole16(preamble); + cmd->radio_on = htole16(onoff); + + ret = malo_hal_execute_cmd(mh, MALO_HOSTCMD_802_11_RADIO_CONTROL); + + MALO_HAL_UNLOCK(mh); + + return ret; +} + +/* + * Set the interrupt mask. + */ +void +malo_hal_intrset(struct malo_hal *mh, uint32_t mask) +{ + + malo_hal_write4(mh, MALO_REG_A2H_INTERRUPT_MASK, 0); + (void)malo_hal_read4(mh, MALO_REG_INT_CODE); + + mh->mh_imask = mask; + malo_hal_write4(mh, MALO_REG_A2H_INTERRUPT_MASK, mask); + (void)malo_hal_read4(mh, MALO_REG_INT_CODE); +} + +int +malo_hal_setchannel(struct malo_hal *mh, const struct malo_hal_channel *chan) +{ + struct malo_cmd_fw_set_rf_channel *cmd; + int ret; + + MALO_HAL_LOCK(mh); + + _CMD_SETUP(cmd, struct malo_cmd_fw_set_rf_channel, + MALO_HOSTCMD_SET_RF_CHANNEL); + cmd->action = htole16(MALO_HOSTCMD_ACT_GEN_SET); + cmd->cur_channel = chan->channel; + + ret = malo_hal_execute_cmd(mh, MALO_HOSTCMD_SET_RF_CHANNEL); + + MALO_HAL_UNLOCK(mh); + + return ret; +} + +int +malo_hal_settxpower(struct malo_hal *mh, const struct malo_hal_channel *c) +{ + struct malo_cmd_rf_tx_power *cmd; + const struct malo_hal_caldata *cal = &mh->mh_caldata; + uint8_t chan = c->channel; + uint16_t pow; + int i, idx, ret; + + MALO_HAL_LOCK(mh); + + _CMD_SETUP(cmd, struct malo_cmd_rf_tx_power, + MALO_HOSTCMD_802_11_RF_TX_POWER); + cmd->action = htole16(MALO_HOSTCMD_ACT_GEN_SET_LIST); + for (i = 0; i < 4; i++) { + idx = (chan - 1) * 4 + i; + pow = cal->pt_ratetable_20m[idx]; + cmd->power_levellist[i] = htole16(pow); + } + ret = malo_hal_execute_cmd(mh, MALO_HOSTCMD_802_11_RF_TX_POWER); + + MALO_HAL_UNLOCK(mh); + + return ret; +} + +int +malo_hal_setpromisc(struct malo_hal *mh, int enable) +{ + /* XXX need host cmd */ + return 0; +} + +int +malo_hal_setassocid(struct malo_hal *mh, + const uint8_t bssid[IEEE80211_ADDR_LEN], uint16_t associd) +{ + struct malo_cmd_fw_set_aid *cmd; + int ret; + + MALO_HAL_LOCK(mh); + + _CMD_SETUP(cmd, struct malo_cmd_fw_set_aid, + MALO_HOSTCMD_SET_AID); + cmd->cmdhdr.seqnum = 1; + cmd->associd = htole16(associd); + IEEE80211_ADDR_COPY(&cmd->macaddr[0], bssid); + + ret = malo_hal_execute_cmd(mh, MALO_HOSTCMD_SET_AID); + MALO_HAL_UNLOCK(mh); + return ret; +} + +/* + * 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 +malo_hal_txstart(struct malo_hal *mh, int qnum) +{ + bus_space_write_4(mh->mh_iot, mh->mh_ioh, + MALO_REG_H2A_INTERRUPT_EVENTS, MALO_H2ARIC_BIT_PPA_READY); + (void) bus_space_read_4(mh->mh_iot, mh->mh_ioh, MALO_REG_INT_CODE); +} + +/* + * Return the current ISR setting and clear the cause. + */ +void +malo_hal_getisr(struct malo_hal *mh, uint32_t *status) +{ + uint32_t cause; + + cause = bus_space_read_4(mh->mh_iot, mh->mh_ioh, + MALO_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, + MALO_REG_A2H_INTERRUPT_CAUSE, cause &~ mh->mh_imask); + (void) bus_space_read_4(mh->mh_iot, mh->mh_ioh, + MALO_REG_INT_CODE); + cause &= mh->mh_imask; + } + + *status = cause; +} + +/* + * Callback from the driver on a cmd done interrupt. Nothing to do right + * now as we spin waiting for cmd completion. + */ +void +malo_hal_cmddone(struct malo_hal *mh) +{ + /* NB : do nothing. */ +} + +int +malo_hal_prescan(struct malo_hal *mh) +{ + struct malo_cmd_prescan *cmd; + int ret; + + MALO_HAL_LOCK(mh); + + _CMD_SETUP(cmd, struct malo_cmd_prescan, MALO_HOSTCMD_SET_PRE_SCAN); + cmd->cmdhdr.seqnum = 1; + + ret = malo_hal_execute_cmd(mh, MALO_HOSTCMD_SET_PRE_SCAN); + + MALO_HAL_UNLOCK(mh); + + return ret; +} + +int +malo_hal_postscan(struct malo_hal *mh, uint8_t *macaddr, uint8_t ibsson) +{ + struct malo_cmd_postscan *cmd; + int ret; + + MALO_HAL_LOCK(mh); + + _CMD_SETUP(cmd, struct malo_cmd_postscan, MALO_HOSTCMD_SET_POST_SCAN); + cmd->cmdhdr.seqnum = 1; + cmd->isibss = htole32(ibsson); + IEEE80211_ADDR_COPY(&cmd->bssid[0], macaddr); + + ret = malo_hal_execute_cmd(mh, MALO_HOSTCMD_SET_POST_SCAN); + + MALO_HAL_UNLOCK(mh); + + return ret; +} + +int +malo_hal_set_slot(struct malo_hal *mh, int is_short) +{ + int ret; + struct malo_cmd_fw_setslot *cmd; + + MALO_HAL_LOCK(mh); + + _CMD_SETUP(cmd, struct malo_cmd_fw_setslot, MALO_HOSTCMD_SET_SLOT); + cmd->action = htole16(MALO_HOSTCMD_ACT_GEN_SET); + cmd->slot = (is_short == 1 ? 1 : 0); + + ret = malo_hal_execute_cmd(mh, MALO_HOSTCMD_SET_SLOT); + + MALO_HAL_UNLOCK(mh); + + return ret; +} + +int +malo_hal_set_rate(struct malo_hal *mh, uint16_t curmode, uint8_t rate) +{ + int i, ret; + struct malo_cmd_set_rate *cmd; + + MALO_HAL_LOCK(mh); + + _CMD_SETUP(cmd, struct malo_cmd_set_rate, MALO_HOSTCMD_SET_RATE); + cmd->aprates[0] = 2; + cmd->aprates[1] = 4; + cmd->aprates[2] = 11; + cmd->aprates[3] = 22; + if (curmode == IEEE80211_MODE_11G) { + cmd->aprates[4] = 0; /* XXX reserved? */ + cmd->aprates[5] = 12; + cmd->aprates[6] = 18; + cmd->aprates[7] = 24; + cmd->aprates[8] = 36; + cmd->aprates[9] = 48; + cmd->aprates[10] = 72; + cmd->aprates[11] = 96; + cmd->aprates[12] = 108; + } + + if (rate != 0) { + /* fixed rate */ + for (i = 0; i < 13; i++) { + if (cmd->aprates[i] == rate) { + cmd->rateindex = i; + cmd->dataratetype = 1; + break; + } + } + } + + ret = malo_hal_execute_cmd(mh, MALO_HOSTCMD_SET_RATE); + + MALO_HAL_UNLOCK(mh); + + return ret; +} + +int +malo_hal_setmcast(struct malo_hal *mh, int nmc, const uint8_t macs[]) +{ + struct malo_cmd_mcast *cmd; + int ret; + + if (nmc > MALO_HAL_MCAST_MAX) + return EINVAL; + + MALO_HAL_LOCK(mh); + + _CMD_SETUP(cmd, struct malo_cmd_mcast, MALO_HOSTCMD_MAC_MULTICAST_ADR); + memcpy(cmd->maclist, macs, nmc * IEEE80211_ADDR_LEN); + cmd->numaddr = htole16(nmc); + cmd->action = htole16(0xffff); + + ret = malo_hal_execute_cmd(mh, MALO_HOSTCMD_MAC_MULTICAST_ADR); + + MALO_HAL_UNLOCK(mh); + + return ret; +} diff --git a/sys/dev/malo/if_malohal.h b/sys/dev/malo/if_malohal.h new file mode 100644 index 0000000..80c27ac --- /dev/null +++ b/sys/dev/malo/if_malohal.h @@ -0,0 +1,234 @@ +/*- + * Copyright (c) 2007 Marvell Semiconductor, Inc. + * Copyright (c) 2007 Sam Leffler, Errno Consulting + * Copyright (c) 2008 Weongyo Jeong <weongyo@freebsd.org> + * 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 _DEV_MALOHAL_H +#define _DEV_MALOHAL_H + +#define MALO_NUM_TX_QUEUES 1 +#define MALO_MAX_TXWCB_QUEUES MALO_NUM_TX_QUEUES + +/* size of f/w command buffer */ +#define MALO_CMDBUF_SIZE 0x4000 + +#define MALO_FW_CHECK_USECS (5 * 1000) /* 5ms */ +#define MALO_FW_MAX_NUM_CHECKS 200 + +/* + * Calibration data builtin to the firmware. The firmware image + * has a single set of calibration tables that we retrieve right + * after download. This can be overriden by the driver (e.g. for + * a different regdomain and/or tx power setup). + */ +struct malo_hal_caldata { + /* pt is short for `power target'. */ +#define MALO_PWTAGETRATETABLE20M (14 * 4) + uint8_t pt_ratetable_20m[MALO_PWTAGETRATETABLE20M]; +}; + +/* + * Get Hardware/Firmware capabilities. + */ +struct malo_hal_hwspec { + uint8_t hwversion; /* version of the HW */ + uint8_t hostinterface; /* host interface */ + uint16_t maxnum_wcb; /* max # of WCB FW handles */ + /* max # of mcast addresses FW handles*/ + uint16_t maxnum_mcaddr; + uint16_t maxnum_tx_wcb; /* max # of tx descs per WCB */ + /* MAC address programmed in HW */ + uint8_t macaddr[6]; + uint16_t regioncode; /* EEPROM region code */ + uint16_t num_antenna; /* Number of antenna used */ + uint32_t fw_releasenum; /* firmware release number */ + uint32_t wcbbase0; + uint32_t rxdesc_read; + uint32_t rxdesc_write; + uint32_t ul_fw_awakecookie; + uint32_t wcbbase[4]; +}; + +/* + * Supply tx/rx dma-related settings to the firmware. + */ +struct malo_hal_txrxdma { + uint32_t maxnum_wcb; /* max # of WCB FW handles */ + uint32_t maxnum_txwcb; /* max # of tx descs per WCB */ + uint32_t rxdesc_read; + uint32_t rxdesc_write; + uint32_t wcbbase[4]; +}; + +/* + * 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). + * + * XXX low/up cases. + */ +struct malo_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 */ +}; + +/* + * 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) + */ +enum malo_hal_antenna { + MHA_ANTENNATYPE_RX = 1, + MHA_ANTENNATYPE_TX = 2, +}; + +/* + * Set Radio Configuration. + * + * onoff != 0 turns radio on; otherwise off. + * if radio is enabled, the preamble is set too. + */ +enum malo_hal_preamble { + MHP_LONG_PREAMBLE = 1, + MHP_SHORT_PREAMBLE = 3, + MHP_AUTO_PREAMBLE = 5, +}; + +struct malo_hal_channel_flags { + uint32_t freqband : 6, +#define MALO_FREQ_BAND_2DOT4GHZ 0x1 + : 26; /* reserved */ +}; + +struct malo_hal_channel { + uint32_t channel; + struct malo_hal_channel_flags flags; +}; + +struct malo_hal_txrate { + uint8_t mcastrate; /* rate for multicast frames */ + 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 */ +}; + +struct malo_hal { + device_t mh_dev; + + bus_space_handle_t mh_ioh; /* BAR 1 copied from softc */ + bus_space_tag_t mh_iot; + uint32_t mh_imask; /* interrupt mask */ + int mh_flags; +#define MHF_CALDATA 0x0001 /* cal data retrieved */ +#define MHF_FWHANG 0x0002 /* fw appears hung */ + + char mh_mtxname[12]; + struct mtx mh_mtx; + bus_dma_tag_t mh_dmat; /* bus DMA tag 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 */ + + struct malo_hal_caldata mh_caldata; + + int mh_debug; +#define MALO_HAL_DEBUG_SENDCMD 0x00000001 +#define MALO_HAL_DEBUG_CMDDONE 0x00000002 +#define MALO_HAL_DEBUG_IGNHANG 0X00000004 +}; + +#define MALO_HAL_LOCK(mh) mtx_lock(&mh->mh_mtx) +#define MALO_HAL_LOCK_ASSERT(mh) mtx_assert(&mh->mh_mtx, MA_OWNED) +#define MALO_HAL_UNLOCK(mh) mtx_unlock(&mh->mh_mtx) + +struct malo_hal *malo_hal_attach(device_t, uint16_t, + bus_space_handle_t, bus_space_tag_t, + bus_dma_tag_t); +int malo_hal_fwload(struct malo_hal *, char *, char *); +int malo_hal_gethwspecs(struct malo_hal *, + struct malo_hal_hwspec *); +void malo_hal_detach(struct malo_hal *); +void malo_hal_intrset(struct malo_hal *, uint32_t); +int malo_hal_setantenna(struct malo_hal *, + enum malo_hal_antenna, int); +int malo_hal_setradio(struct malo_hal *, int, + enum malo_hal_preamble); +int malo_hal_setchannel(struct malo_hal *, + const struct malo_hal_channel *); +int malo_hal_setmaxtxpwr(struct malo_hal *, uint16_t); +int malo_hal_settxpower(struct malo_hal *, const struct malo_hal_channel *); +int malo_hal_setpromisc(struct malo_hal *, int); +int malo_hal_setassocid(struct malo_hal *, + const uint8_t[], uint16_t); +void malo_hal_txstart(struct malo_hal *, int); +void malo_hal_getisr(struct malo_hal *, uint32_t *); +void malo_hal_cmddone(struct malo_hal *); +int malo_hal_prescan(struct malo_hal *); +int malo_hal_postscan(struct malo_hal *, uint8_t *, uint8_t); +int malo_hal_set_slot(struct malo_hal *, int); +int malo_hal_set_rate(struct malo_hal *, uint16_t, uint8_t); +int malo_hal_setmcast(struct malo_hal *, int, const uint8_t[]); + +#endif diff --git a/sys/dev/malo/if_maloioctl.h b/sys/dev/malo/if_maloioctl.h new file mode 100644 index 0000000..0647b15 --- /dev/null +++ b/sys/dev/malo/if_maloioctl.h @@ -0,0 +1,114 @@ +/*- + * Copyright (c) 2007 Marvell Semiconductor, Inc. + * Copyright (c) 2007 Sam Leffler, Errno Consulting + * Copyright (c) 2008 Weongyo Jeong <weongyo@freebsd.org> + * 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_MALO_MVIOCTL_H +#define _DEV_MALO_MVIOCTL_H + +struct malo_stats { + struct malo_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_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]; +}; + +#define SIOCGMVSTATS _IOWR('i', 137, struct ifreq) + +/* + * Radio capture format. + */ +#define MALO_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 malo_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 MALO_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 malo_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_MALO_MVIOCTL_H */ diff --git a/sys/modules/malo/Makefile b/sys/modules/malo/Makefile new file mode 100644 index 0000000..c0e88c5 --- /dev/null +++ b/sys/modules/malo/Makefile @@ -0,0 +1,8 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../dev/malo + +KMOD = if_malo +SRCS = if_malo.c if_malohal.c if_malo_pci.c device_if.h bus_if.h pci_if.h + +.include <bsd.kmod.mk> |