freebsd-nq/sys/dev/mwl/mwlhal.c
Pedro F. Giffuni 718cf2ccb9 sys/dev: further adoption of SPDX licensing ID tags.
Mainly focus on files that use BSD 2-Clause license, however the tool I
was using misidentified many licenses so this was mostly a manual - error
prone - task.

The Software Package Data Exchange (SPDX) group provides a specification
to make it easier for automated tools to detect and summarize well known
opensource licenses. We are gradually adopting the specification, noting
that the tags are considered only advisory and do not, in any way,
superceed or replace the license texts.
2017-11-27 14:52:40 +00:00

2781 lines
71 KiB
C

/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2007-2009 Sam Leffler, Errno Consulting
* Copyright (c) 2007-2009 Marvell Semiconductor, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
* redistribution must be conditioned upon including a substantially
* similar Disclaimer requirement for further binary redistribution.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGES.
*
* $FreeBSD$
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sysctl.h>
#include <sys/malloc.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/kernel.h>
#include <sys/errno.h>
#include <sys/bus.h>
#include <sys/endian.h>
#include <sys/linker.h>
#include <sys/firmware.h>
#include <machine/bus.h>
#include <dev/mwl/mwlhal.h>
#include <dev/mwl/mwlreg.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <net/if.h>
#include <dev/mwl/mwldiag.h>
#define MWLHAL_DEBUG /* debug msgs */
typedef enum {
WL_ANTENNAMODE_RX = 0xffff,
WL_ANTENNAMODE_TX = 2,
} wlantennamode_e;
typedef enum {
WL_TX_POWERLEVEL_LOW = 5,
WL_TX_POWERLEVEL_MEDIUM = 10,
WL_TX_POWERLEVEL_HIGH = 15,
} wltxpowerlevel_e;
#define MWL_CMDBUF_SIZE 0x4000 /* size of f/w command buffer */
#define MWL_BASTREAMS_MAX 7 /* max BA streams (NB: fw >3.3.5.9) */
#define MWL_BAQID_MAX 8 /* max BA Q id's (NB: fw >3.3.5.9) */
#define MWL_MBSS_AP_MAX 8 /* max ap vap's */
#define MWL_MBSS_STA_MAX 24 /* max station/client vap's */
#define MWL_MBSS_MAX (MWL_MBSS_AP_MAX+MWL_MBSS_STA_MAX)
/*
* BA stream -> queue ID mapping
*
* The first 2 streams map to h/w; the remaining streams are
* implemented in firmware.
*/
static const int ba2qid[MWL_BASTREAMS_MAX] = {
5, 6 /* h/w supported */
#if MWL_BASTREAMS_MAX == 7
, 7, 0, 1, 2, 3 /* f/w supported */
#endif
};
static int qid2ba[MWL_BAQID_MAX];
#define IEEE80211_ADDR_LEN 6 /* XXX */
#define IEEE80211_ADDR_COPY(_dst, _src) \
memcpy(_dst, _src, IEEE80211_ADDR_LEN)
#define IEEE80211_ADDR_EQ(_dst, _src) \
(memcmp(_dst, _src, IEEE80211_ADDR_LEN) == 0)
#define _CMD_SETUP(pCmd, type, cmd) do { \
pCmd = (type *)&mh->mh_cmdbuf[0]; \
memset(pCmd, 0, sizeof(type)); \
pCmd->CmdHdr.Cmd = htole16(cmd); \
pCmd->CmdHdr.Length = htole16(sizeof(type)); \
} while (0)
#define _VCMD_SETUP(vap, pCmd, type, cmd) do { \
_CMD_SETUP(pCmd, type, cmd); \
pCmd->CmdHdr.MacId = vap->macid; \
} while (0)
#define PWTAGETRATETABLE20M 14*4
#define PWTAGETRATETABLE40M 9*4
#define PWTAGETRATETABLE20M_5G 35*4
#define PWTAGETRATETABLE40M_5G 16*4
struct mwl_hal_bastream {
MWL_HAL_BASTREAM public; /* public state */
uint8_t stream; /* stream # */
uint8_t setup; /* f/w cmd sent */
uint8_t ba_policy; /* direct/delayed BA policy */
uint8_t tid;
uint8_t paraminfo;
uint8_t macaddr[IEEE80211_ADDR_LEN];
};
struct mwl_hal_priv;
struct mwl_hal_vap {
struct mwl_hal_priv *mh; /* back pointer */
uint16_t bss_type; /* f/w type */
uint8_t vap_type; /* MWL_HAL_BSSTYPE */
uint8_t macid; /* for passing to f/w */
uint8_t flags;
#define MVF_RUNNING 0x01 /* BSS_START issued */
#define MVF_STATION 0x02 /* sta db entry created */
uint8_t mac[IEEE80211_ADDR_LEN];/* mac address */
};
#define MWLVAP(_vap) ((_vap)->mh)
/*
* Per-device state. We allocate a single cmd buffer for
* submitting operations to the firmware. Access to this
* buffer (and the f/w) are single-threaded. At present
* we spin waiting for cmds to complete which is bad. Not
* sure if it's possible to submit multiple requests or
* control when we get cmd done interrupts. There's no
* documentation and no example code to indicate what can
* or cannot be done so all we can do right now is follow the
* linux driver logic. This falls apart when the f/w fails;
* the system comes to a crawl as we spin waiting for operations
* to finish.
*/
struct mwl_hal_priv {
struct mwl_hal public; /* public area */
device_t mh_dev;
char mh_mtxname[12];
struct mtx mh_mtx;
bus_dma_tag_t mh_dmat; /* bus DMA tag for cmd buffer */
bus_dma_segment_t mh_seg; /* segment for cmd buffer */
bus_dmamap_t mh_dmamap; /* DMA map for cmd buffer */
uint16_t *mh_cmdbuf; /* f/w cmd buffer */
bus_addr_t mh_cmdaddr; /* physaddr of cmd buffer */
int mh_flags;
#define MHF_CALDATA 0x0001 /* cal data retrieved */
#define MHF_FWHANG 0x0002 /* fw appears hung */
#define MHF_MBSS 0x0004 /* mbss enabled */
struct mwl_hal_vap mh_vaps[MWL_MBSS_MAX+1];
int mh_bastreams; /* bit mask of available BA streams */
int mh_regioncode; /* XXX last region code sent to fw */
struct mwl_hal_bastream mh_streams[MWL_BASTREAMS_MAX];
int mh_debug;
MWL_HAL_CHANNELINFO mh_20M;
MWL_HAL_CHANNELINFO mh_40M;
MWL_HAL_CHANNELINFO mh_20M_5G;
MWL_HAL_CHANNELINFO mh_40M_5G;
int mh_SDRAMSIZE_Addr;
uint32_t mh_RTSSuccesses;/* cumulative stats for read-on-clear */
uint32_t mh_RTSFailures;
uint32_t mh_RxDuplicateFrames;
uint32_t mh_FCSErrorCount;
MWL_DIAG_REVS mh_revs;
};
#define MWLPRIV(_mh) ((struct mwl_hal_priv *)(_mh))
static int mwl_hal_setmac_locked(struct mwl_hal_vap *,
const uint8_t addr[IEEE80211_ADDR_LEN]);
static int mwlExecuteCmd(struct mwl_hal_priv *, unsigned short cmd);
static int mwlGetPwrCalTable(struct mwl_hal_priv *);
#ifdef MWLHAL_DEBUG
static const char *mwlcmdname(int cmd);
static void dumpresult(struct mwl_hal_priv *, int showresult);
#endif /* MWLHAL_DEBUG */
SYSCTL_DECL(_hw_mwl);
static SYSCTL_NODE(_hw_mwl, OID_AUTO, hal, CTLFLAG_RD, 0,
"Marvell HAL parameters");
static __inline void
MWL_HAL_LOCK(struct mwl_hal_priv *mh)
{
mtx_lock(&mh->mh_mtx);
}
static __inline void
MWL_HAL_LOCK_ASSERT(struct mwl_hal_priv *mh)
{
mtx_assert(&mh->mh_mtx, MA_OWNED);
}
static __inline void
MWL_HAL_UNLOCK(struct mwl_hal_priv *mh)
{
mtx_unlock(&mh->mh_mtx);
}
static __inline uint32_t
RD4(struct mwl_hal_priv *mh, bus_size_t off)
{
return bus_space_read_4(mh->public.mh_iot, mh->public.mh_ioh, off);
}
static __inline void
WR4(struct mwl_hal_priv *mh, bus_size_t off, uint32_t val)
{
bus_space_write_4(mh->public.mh_iot, mh->public.mh_ioh, off, val);
}
static void
mwl_hal_load_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
{
bus_addr_t *paddr = (bus_addr_t*) arg;
KASSERT(error == 0, ("error %u on bus_dma callback", error));
*paddr = segs->ds_addr;
}
/*
* Setup for communication with the device. We allocate
* a command buffer and map it for bus dma use. The pci
* device id is used to identify whether the device has
* SRAM on it (in which case f/w download must include a
* memory controller reset). All bus i/o operations happen
* in BAR 1; the driver passes in the tag and handle we need.
*/
struct mwl_hal *
mwl_hal_attach(device_t dev, uint16_t devid,
bus_space_handle_t ioh, bus_space_tag_t iot, bus_dma_tag_t tag)
{
struct mwl_hal_priv *mh;
struct mwl_hal_vap *hvap;
int error, i;
mh = malloc(sizeof(struct mwl_hal_priv), M_DEVBUF, M_NOWAIT | M_ZERO);
if (mh == NULL)
return NULL;
mh->mh_dev = dev;
mh->public.mh_ioh = ioh;
mh->public.mh_iot = iot;
for (i = 0; i < MWL_BASTREAMS_MAX; i++) {
mh->mh_streams[i].public.txq = ba2qid[i];
mh->mh_streams[i].stream = i;
/* construct back-mapping while we're at it */
if (mh->mh_streams[i].public.txq < MWL_BAQID_MAX)
qid2ba[mh->mh_streams[i].public.txq] = i;
else
device_printf(dev, "unexpected BA tx qid %d for "
"stream %d\n", mh->mh_streams[i].public.txq, i);
}
/* setup constant portion of vap state */
/* XXX should get max ap/client vap's from f/w */
i = 0;
hvap = &mh->mh_vaps[i];
hvap->vap_type = MWL_HAL_AP;
hvap->bss_type = htole16(WL_MAC_TYPE_PRIMARY_AP);
hvap->macid = 0;
for (i++; i < MWL_MBSS_AP_MAX; i++) {
hvap = &mh->mh_vaps[i];
hvap->vap_type = MWL_HAL_AP;
hvap->bss_type = htole16(WL_MAC_TYPE_SECONDARY_AP);
hvap->macid = i;
}
hvap = &mh->mh_vaps[i];
hvap->vap_type = MWL_HAL_STA;
hvap->bss_type = htole16(WL_MAC_TYPE_PRIMARY_CLIENT);
hvap->macid = i;
for (i++; i < MWL_MBSS_MAX; i++) {
hvap = &mh->mh_vaps[i];
hvap->vap_type = MWL_HAL_STA;
hvap->bss_type = htole16(WL_MAC_TYPE_SECONDARY_CLIENT);
hvap->macid = i;
}
mh->mh_revs.mh_devid = devid;
snprintf(mh->mh_mtxname, sizeof(mh->mh_mtxname),
"%s_hal", device_get_nameunit(dev));
mtx_init(&mh->mh_mtx, mh->mh_mtxname, NULL, MTX_DEF);
/*
* Allocate the command buffer and map into the address
* space of the h/w. We request "coherent" memory which
* will be uncached on some architectures.
*/
error = bus_dma_tag_create(tag, /* parent */
PAGE_SIZE, 0, /* alignment, bounds */
BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
BUS_SPACE_MAXADDR, /* highaddr */
NULL, NULL, /* filter, filterarg */
MWL_CMDBUF_SIZE, /* maxsize */
1, /* nsegments */
MWL_CMDBUF_SIZE, /* maxsegsize */
BUS_DMA_ALLOCNOW, /* flags */
NULL, /* lockfunc */
NULL, /* lockarg */
&mh->mh_dmat);
if (error != 0) {
device_printf(dev, "unable to allocate memory for cmd tag, "
"error %u\n", error);
goto fail0;
}
/* allocate descriptors */
error = bus_dmamem_alloc(mh->mh_dmat, (void**) &mh->mh_cmdbuf,
BUS_DMA_NOWAIT | BUS_DMA_COHERENT,
&mh->mh_dmamap);
if (error != 0) {
device_printf(dev, "unable to allocate memory for cmd buffer, "
"error %u\n", error);
goto fail1;
}
error = bus_dmamap_load(mh->mh_dmat, mh->mh_dmamap,
mh->mh_cmdbuf, MWL_CMDBUF_SIZE,
mwl_hal_load_cb, &mh->mh_cmdaddr,
BUS_DMA_NOWAIT);
if (error != 0) {
device_printf(dev, "unable to load cmd buffer, error %u\n",
error);
goto fail2;
}
/*
* Some cards have SDRAM. When loading firmware we need
* to reset the SDRAM controller prior to doing this.
* When the SDRAMSIZE is non-zero we do that work in
* mwl_hal_fwload.
*/
switch (devid) {
case 0x2a02: /* CB82 */
case 0x2a03: /* CB85 */
case 0x2a08: /* MC85_B1 */
case 0x2a0b: /* CB85AP */
case 0x2a24:
mh->mh_SDRAMSIZE_Addr = 0x40fe70b7; /* 8M SDRAM */
break;
case 0x2a04: /* MC85 */
mh->mh_SDRAMSIZE_Addr = 0x40fc70b7; /* 16M SDRAM */
break;
default:
break;
}
return &mh->public;
fail2:
bus_dmamem_free(mh->mh_dmat, mh->mh_cmdbuf, mh->mh_dmamap);
fail1:
bus_dma_tag_destroy(mh->mh_dmat);
fail0:
mtx_destroy(&mh->mh_mtx);
free(mh, M_DEVBUF);
return NULL;
}
void
mwl_hal_detach(struct mwl_hal *mh0)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
bus_dmamem_free(mh->mh_dmat, mh->mh_cmdbuf, mh->mh_dmamap);
bus_dma_tag_destroy(mh->mh_dmat);
mtx_destroy(&mh->mh_mtx);
free(mh, M_DEVBUF);
}
/*
* Reset internal state after a firmware download.
*/
static int
mwlResetHalState(struct mwl_hal_priv *mh)
{
int i;
/* XXX get from f/w */
mh->mh_bastreams = (1<<MWL_BASTREAMS_MAX)-1;
for (i = 0; i < MWL_MBSS_MAX; i++)
mh->mh_vaps[i].mh = NULL;
/*
* Clear cumulative stats.
*/
mh->mh_RTSSuccesses = 0;
mh->mh_RTSFailures = 0;
mh->mh_RxDuplicateFrames = 0;
mh->mh_FCSErrorCount = 0;
/*
* Fetch cal data for later use.
* XXX may want to fetch other stuff too.
*/
/* XXX check return */
if ((mh->mh_flags & MHF_CALDATA) == 0)
mwlGetPwrCalTable(mh);
return 0;
}
struct mwl_hal_vap *
mwl_hal_newvap(struct mwl_hal *mh0, MWL_HAL_BSSTYPE type,
const uint8_t mac[IEEE80211_ADDR_LEN])
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
struct mwl_hal_vap *vap;
int i;
MWL_HAL_LOCK(mh);
/* NB: could optimize but not worth it w/ max 32 bss */
for (i = 0; i < MWL_MBSS_MAX; i++) {
vap = &mh->mh_vaps[i];
if (vap->vap_type == type && vap->mh == NULL) {
vap->mh = mh;
mwl_hal_setmac_locked(vap, mac);
break;
}
}
MWL_HAL_UNLOCK(mh);
return (i < MWL_MBSS_MAX) ? vap : NULL;
}
void
mwl_hal_delvap(struct mwl_hal_vap *vap)
{
/* NB: locking not needed for single write */
vap->mh = NULL;
}
/*
* Manipulate the debug mask. Note debug
* msgs are only provided when this code is
* compiled with MWLHAL_DEBUG defined.
*/
void
mwl_hal_setdebug(struct mwl_hal *mh, int debug)
{
MWLPRIV(mh)->mh_debug = debug;
}
int
mwl_hal_getdebug(struct mwl_hal *mh)
{
return MWLPRIV(mh)->mh_debug;
}
void
mwl_hal_setbastreams(struct mwl_hal *mh, int mask)
{
MWLPRIV(mh)->mh_bastreams = mask & ((1<<MWL_BASTREAMS_MAX)-1);
}
int
mwl_hal_getbastreams(struct mwl_hal *mh)
{
return MWLPRIV(mh)->mh_bastreams;
}
int
mwl_hal_ismbsscapable(struct mwl_hal *mh)
{
return (MWLPRIV(mh)->mh_flags & MHF_MBSS) != 0;
}
#if 0
/* XXX inlined */
/*
* Return the current ISR setting and clear the cause.
* XXX maybe make inline
*/
void
mwl_hal_getisr(struct mwl_hal *mh0, uint32_t *status)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
uint32_t cause;
cause = RD4(mh, MACREG_REG_A2H_INTERRUPT_CAUSE);
if (cause == 0xffffffff) { /* card removed */
device_printf(mh->mh_dev, "%s: cause 0x%x\n", __func__, cause);
cause = 0;
} else if (cause != 0) {
/* clear cause bits */
WR4(mh, MACREG_REG_A2H_INTERRUPT_CAUSE,
cause &~ mh->public.mh_imask);
RD4(mh, MACREG_REG_INT_CODE); /* XXX flush write? */
}
*status = cause;
}
#endif
/*
* Set the interrupt mask.
*/
void
mwl_hal_intrset(struct mwl_hal *mh0, uint32_t mask)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
WR4(mh, MACREG_REG_A2H_INTERRUPT_MASK, 0);
RD4(mh, MACREG_REG_INT_CODE);
mh->public.mh_imask = mask;
WR4(mh, MACREG_REG_A2H_INTERRUPT_MASK, mask);
RD4(mh, MACREG_REG_INT_CODE);
}
#if 0
/* XXX inlined */
/*
* Kick the firmware to tell it there are new tx descriptors
* for processing. The driver says what h/w q has work in
* case the f/w ever gets smarter.
*/
void
mwl_hal_txstart(struct mwl_hal *mh0, int qnum)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
uint32_t dummy;
WR4(mh, MACREG_REG_H2A_INTERRUPT_EVENTS, MACREG_H2ARIC_BIT_PPA_READY);
dummy = RD4(mh, MACREG_REG_INT_CODE);
}
#endif
/*
* Callback from the driver on a cmd done interrupt.
* Nothing to do right now as we spin waiting for
* cmd completion.
*/
void
mwl_hal_cmddone(struct mwl_hal *mh0)
{
#if 0
struct mwl_hal_priv *mh = MWLPRIV(mh0);
if (mh->mh_debug & MWL_HAL_DEBUG_CMDDONE) {
device_printf(mh->mh_dev, "cmd done interrupt:\n");
dumpresult(mh, 1);
}
#endif
}
/*
* Return "hw specs". Note this must be the first
* cmd MUST be done after a firmware download or the
* f/w will lockup.
* XXX move into the hal so driver doesn't need to be responsible
*/
int
mwl_hal_gethwspecs(struct mwl_hal *mh0, struct mwl_hal_hwspec *hw)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
HostCmd_DS_GET_HW_SPEC *pCmd;
int retval, minrev;
MWL_HAL_LOCK(mh);
_CMD_SETUP(pCmd, HostCmd_DS_GET_HW_SPEC, HostCmd_CMD_GET_HW_SPEC);
memset(&pCmd->PermanentAddr[0], 0xff, IEEE80211_ADDR_LEN);
pCmd->ulFwAwakeCookie = htole32((unsigned int)mh->mh_cmdaddr+2048);
retval = mwlExecuteCmd(mh, HostCmd_CMD_GET_HW_SPEC);
if (retval == 0) {
IEEE80211_ADDR_COPY(hw->macAddr, pCmd->PermanentAddr);
hw->wcbBase[0] = le32toh(pCmd->WcbBase0) & 0x0000ffff;
hw->wcbBase[1] = le32toh(pCmd->WcbBase1[0]) & 0x0000ffff;
hw->wcbBase[2] = le32toh(pCmd->WcbBase1[1]) & 0x0000ffff;
hw->wcbBase[3] = le32toh(pCmd->WcbBase1[2]) & 0x0000ffff;
hw->rxDescRead = le32toh(pCmd->RxPdRdPtr)& 0x0000ffff;
hw->rxDescWrite = le32toh(pCmd->RxPdWrPtr)& 0x0000ffff;
hw->regionCode = le16toh(pCmd->RegionCode) & 0x00ff;
hw->fwReleaseNumber = le32toh(pCmd->FWReleaseNumber);
hw->maxNumWCB = le16toh(pCmd->NumOfWCB);
hw->maxNumMCAddr = le16toh(pCmd->NumOfMCastAddr);
hw->numAntennas = le16toh(pCmd->NumberOfAntenna);
hw->hwVersion = pCmd->Version;
hw->hostInterface = pCmd->HostIf;
mh->mh_revs.mh_macRev = hw->hwVersion; /* XXX */
mh->mh_revs.mh_phyRev = hw->hostInterface; /* XXX */
minrev = ((hw->fwReleaseNumber) >> 16) & 0xff;
if (minrev >= 4) {
/* starting with 3.4.x.x s/w BA streams supported */
mh->mh_bastreams &= (1<<MWL_BASTREAMS_MAX)-1;
} else
mh->mh_bastreams &= (1<<2)-1;
}
MWL_HAL_UNLOCK(mh);
return retval;
}
/*
* Inform the f/w about location of the tx/rx dma data structures
* and related state. This cmd must be done immediately after a
* mwl_hal_gethwspecs call or the f/w will lockup.
*/
int
mwl_hal_sethwdma(struct mwl_hal *mh0, const struct mwl_hal_txrxdma *dma)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
HostCmd_DS_SET_HW_SPEC *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_CMD_SETUP(pCmd, HostCmd_DS_SET_HW_SPEC, HostCmd_CMD_SET_HW_SPEC);
pCmd->WcbBase[0] = htole32(dma->wcbBase[0]);
pCmd->WcbBase[1] = htole32(dma->wcbBase[1]);
pCmd->WcbBase[2] = htole32(dma->wcbBase[2]);
pCmd->WcbBase[3] = htole32(dma->wcbBase[3]);
pCmd->TxWcbNumPerQueue = htole32(dma->maxNumTxWcb);
pCmd->NumTxQueues = htole32(dma->maxNumWCB);
pCmd->TotalRxWcb = htole32(1); /* XXX */
pCmd->RxPdWrPtr = htole32(dma->rxDescRead);
pCmd->Flags = htole32(SET_HW_SPEC_HOSTFORM_BEACON
#ifdef MWL_HOST_PS_SUPPORT
| SET_HW_SPEC_HOST_POWERSAVE
#endif
| SET_HW_SPEC_HOSTFORM_PROBERESP);
/* disable multi-bss operation for A1-A4 parts */
if (mh->mh_revs.mh_macRev < 5)
pCmd->Flags |= htole32(SET_HW_SPEC_DISABLEMBSS);
retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_HW_SPEC);
if (retval == 0) {
if (pCmd->Flags & htole32(SET_HW_SPEC_DISABLEMBSS))
mh->mh_flags &= ~MHF_MBSS;
else
mh->mh_flags |= MHF_MBSS;
}
MWL_HAL_UNLOCK(mh);
return retval;
}
/*
* Retrieve statistics from the f/w.
* XXX should be in memory shared w/ driver
*/
int
mwl_hal_gethwstats(struct mwl_hal *mh0, struct mwl_hal_hwstats *stats)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
HostCmd_DS_802_11_GET_STAT *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_CMD_SETUP(pCmd, HostCmd_DS_802_11_GET_STAT,
HostCmd_CMD_802_11_GET_STAT);
retval = mwlExecuteCmd(mh, HostCmd_CMD_802_11_GET_STAT);
if (retval == 0) {
const uint32_t *sp = (const uint32_t *)&pCmd->TxRetrySuccesses;
uint32_t *dp = (uint32_t *)&stats->TxRetrySuccesses;
int i;
for (i = 0; i < sizeof(*stats)/sizeof(uint32_t); i++)
dp[i] = le32toh(sp[i]);
/*
* Update stats not returned by f/w but available
* through public registers. Note these registers
* are "clear on read" so we maintain cumulative data.
* XXX register defines
*/
mh->mh_RTSSuccesses += RD4(mh, 0xa834);
mh->mh_RTSFailures += RD4(mh, 0xa830);
mh->mh_RxDuplicateFrames += RD4(mh, 0xa84c);
mh->mh_FCSErrorCount += RD4(mh, 0xa840);
}
MWL_HAL_UNLOCK(mh);
stats->RTSSuccesses = mh->mh_RTSSuccesses;
stats->RTSFailures = mh->mh_RTSFailures;
stats->RxDuplicateFrames = mh->mh_RxDuplicateFrames;
stats->FCSErrorCount = mh->mh_FCSErrorCount;
return retval;
}
/*
* Set HT guard interval handling.
* Takes effect immediately.
*/
int
mwl_hal_sethtgi(struct mwl_hal_vap *vap, int GIType)
{
struct mwl_hal_priv *mh = MWLVAP(vap);
HostCmd_FW_HT_GUARD_INTERVAL *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_VCMD_SETUP(vap, pCmd, HostCmd_FW_HT_GUARD_INTERVAL,
HostCmd_CMD_HT_GUARD_INTERVAL);
pCmd->Action = htole32(HostCmd_ACT_GEN_SET);
if (GIType == 0) {
pCmd->GIType = htole32(GI_TYPE_LONG);
} else if (GIType == 1) {
pCmd->GIType = htole32(GI_TYPE_LONG | GI_TYPE_SHORT);
} else {
pCmd->GIType = htole32(GI_TYPE_LONG);
}
retval = mwlExecuteCmd(mh, HostCmd_CMD_HT_GUARD_INTERVAL);
MWL_HAL_UNLOCK(mh);
return retval;
}
/*
* Configure radio.
* Takes effect immediately.
* XXX preamble installed after set fixed rate cmd
*/
int
mwl_hal_setradio(struct mwl_hal *mh0, int onoff, MWL_HAL_PREAMBLE preamble)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
HostCmd_DS_802_11_RADIO_CONTROL *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_CMD_SETUP(pCmd, HostCmd_DS_802_11_RADIO_CONTROL,
HostCmd_CMD_802_11_RADIO_CONTROL);
pCmd->Action = htole16(HostCmd_ACT_GEN_SET);
if (onoff == 0)
pCmd->Control = 0;
else
pCmd->Control = htole16(preamble);
pCmd->RadioOn = htole16(onoff);
retval = mwlExecuteCmd(mh, HostCmd_CMD_802_11_RADIO_CONTROL);
MWL_HAL_UNLOCK(mh);
return retval;
}
/*
* Configure antenna use.
* Takes effect immediately.
* XXX tx antenna setting ignored
* XXX rx antenna setting should always be 3 (for now)
*/
int
mwl_hal_setantenna(struct mwl_hal *mh0, MWL_HAL_ANTENNA dirSet, int ant)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
HostCmd_DS_802_11_RF_ANTENNA *pCmd;
int retval;
if (!(dirSet == WL_ANTENNATYPE_RX || dirSet == WL_ANTENNATYPE_TX))
return EINVAL;
MWL_HAL_LOCK(mh);
_CMD_SETUP(pCmd, HostCmd_DS_802_11_RF_ANTENNA,
HostCmd_CMD_802_11_RF_ANTENNA);
pCmd->Action = htole16(dirSet);
if (ant == 0) /* default to all/both antennae */
ant = 3;
pCmd->AntennaMode = htole16(ant);
retval = mwlExecuteCmd(mh, HostCmd_CMD_802_11_RF_ANTENNA);
MWL_HAL_UNLOCK(mh);
return retval;
}
/*
* Set packet size threshold for implicit use of RTS.
* Takes effect immediately.
* XXX packet length > threshold =>'s RTS
*/
int
mwl_hal_setrtsthreshold(struct mwl_hal_vap *vap, int threshold)
{
struct mwl_hal_priv *mh = MWLVAP(vap);
HostCmd_DS_802_11_RTS_THSD *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_VCMD_SETUP(vap, pCmd, HostCmd_DS_802_11_RTS_THSD,
HostCmd_CMD_802_11_RTS_THSD);
pCmd->Action = htole16(HostCmd_ACT_GEN_SET);
pCmd->Threshold = htole16(threshold);
retval = mwlExecuteCmd(mh, HostCmd_CMD_802_11_RTS_THSD);
MWL_HAL_UNLOCK(mh);
return retval;
}
/*
* Enable sta-mode operation (disables beacon frame xmit).
*/
int
mwl_hal_setinframode(struct mwl_hal_vap *vap)
{
struct mwl_hal_priv *mh = MWLVAP(vap);
HostCmd_FW_SET_INFRA_MODE *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_VCMD_SETUP(vap, pCmd, HostCmd_FW_SET_INFRA_MODE,
HostCmd_CMD_SET_INFRA_MODE);
retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_INFRA_MODE);
MWL_HAL_UNLOCK(mh);
return retval;
}
/*
* Configure radar detection in support of 802.11h.
*/
int
mwl_hal_setradardetection(struct mwl_hal *mh0, MWL_HAL_RADAR action)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
HostCmd_802_11h_Detect_Radar *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_CMD_SETUP(pCmd, HostCmd_802_11h_Detect_Radar,
HostCmd_CMD_802_11H_DETECT_RADAR);
pCmd->CmdHdr.Length = htole16(sizeof(HostCmd_802_11h_Detect_Radar));
pCmd->Action = htole16(action);
if (mh->mh_regioncode == DOMAIN_CODE_ETSI_131)
pCmd->RadarTypeCode = htole16(131);
retval = mwlExecuteCmd(mh, HostCmd_CMD_802_11H_DETECT_RADAR);
MWL_HAL_UNLOCK(mh);
return retval;
}
/*
* Convert public channel flags definition to a
* value suitable for feeding to the firmware.
* Note this includes byte swapping.
*/
static uint32_t
cvtChannelFlags(const MWL_HAL_CHANNEL *chan)
{
uint32_t w;
/*
* NB: f/w only understands FREQ_BAND_5GHZ, supplying the more
* precise band info causes it to lockup (sometimes).
*/
w = (chan->channelFlags.FreqBand == MWL_FREQ_BAND_2DOT4GHZ) ?
FREQ_BAND_2DOT4GHZ : FREQ_BAND_5GHZ;
switch (chan->channelFlags.ChnlWidth) {
case MWL_CH_10_MHz_WIDTH:
w |= CH_10_MHz_WIDTH;
break;
case MWL_CH_20_MHz_WIDTH:
w |= CH_20_MHz_WIDTH;
break;
case MWL_CH_40_MHz_WIDTH:
default:
w |= CH_40_MHz_WIDTH;
break;
}
switch (chan->channelFlags.ExtChnlOffset) {
case MWL_EXT_CH_NONE:
w |= EXT_CH_NONE;
break;
case MWL_EXT_CH_ABOVE_CTRL_CH:
w |= EXT_CH_ABOVE_CTRL_CH;
break;
case MWL_EXT_CH_BELOW_CTRL_CH:
w |= EXT_CH_BELOW_CTRL_CH;
break;
}
return htole32(w);
}
/*
* Start a channel switch announcement countdown. The IE
* in the beacon frame is allowed to go out and the firmware
* counts down and notifies the host when it's time to switch
* channels.
*/
int
mwl_hal_setchannelswitchie(struct mwl_hal *mh0,
const MWL_HAL_CHANNEL *nextchan, uint32_t mode, uint32_t count)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
HostCmd_SET_SWITCH_CHANNEL *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_CMD_SETUP(pCmd, HostCmd_SET_SWITCH_CHANNEL,
HostCmd_CMD_SET_SWITCH_CHANNEL);
pCmd->Next11hChannel = htole32(nextchan->channel);
pCmd->Mode = htole32(mode);
pCmd->InitialCount = htole32(count+1);
pCmd->ChannelFlags = cvtChannelFlags(nextchan);
retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_SWITCH_CHANNEL);
MWL_HAL_UNLOCK(mh);
return retval;
}
/*
* Set the region code that selects the radar bin'ing agorithm.
*/
int
mwl_hal_setregioncode(struct mwl_hal *mh0, int regionCode)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
HostCmd_SET_REGIONCODE_INFO *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_CMD_SETUP(pCmd, HostCmd_SET_REGIONCODE_INFO,
HostCmd_CMD_SET_REGION_CODE);
/* XXX map pseudo-codes to fw codes */
switch (regionCode) {
case DOMAIN_CODE_ETSI_131:
pCmd->regionCode = htole16(DOMAIN_CODE_ETSI);
break;
default:
pCmd->regionCode = htole16(regionCode);
break;
}
retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_REGION_CODE);
if (retval == 0)
mh->mh_regioncode = regionCode;
MWL_HAL_UNLOCK(mh);
return retval;
}
#define RATEVAL(r) ((r) &~ RATE_MCS)
#define RATETYPE(r) (((r) & RATE_MCS) ? HT_RATE_TYPE : LEGACY_RATE_TYPE)
int
mwl_hal_settxrate(struct mwl_hal_vap *vap, MWL_HAL_TXRATE_HANDLING handling,
const MWL_HAL_TXRATE *rate)
{
struct mwl_hal_priv *mh = MWLVAP(vap);
HostCmd_FW_USE_FIXED_RATE *pCmd;
FIXED_RATE_ENTRY *fp;
int retval, i, n;
MWL_HAL_LOCK(mh);
_VCMD_SETUP(vap, pCmd, HostCmd_FW_USE_FIXED_RATE,
HostCmd_CMD_SET_FIXED_RATE);
pCmd->MulticastRate = RATEVAL(rate->McastRate);
pCmd->MultiRateTxType = RATETYPE(rate->McastRate);
/* NB: no rate type field */
pCmd->ManagementRate = RATEVAL(rate->MgtRate);
memset(pCmd->FixedRateTable, 0, sizeof(pCmd->FixedRateTable));
if (handling == RATE_FIXED) {
pCmd->Action = htole32(HostCmd_ACT_GEN_SET);
pCmd->AllowRateDrop = htole32(FIXED_RATE_WITHOUT_AUTORATE_DROP);
fp = pCmd->FixedRateTable;
fp->FixedRate =
htole32(RATEVAL(rate->RateSeries[0].Rate));
fp->FixRateTypeFlags.FixRateType =
htole32(RATETYPE(rate->RateSeries[0].Rate));
pCmd->EntryCount = htole32(1);
} else if (handling == RATE_FIXED_DROP) {
pCmd->Action = htole32(HostCmd_ACT_GEN_SET);
pCmd->AllowRateDrop = htole32(FIXED_RATE_WITH_AUTO_RATE_DROP);
n = 0;
fp = pCmd->FixedRateTable;
for (i = 0; i < 4; i++) {
if (rate->RateSeries[0].TryCount == 0)
break;
fp->FixRateTypeFlags.FixRateType =
htole32(RATETYPE(rate->RateSeries[i].Rate));
fp->FixedRate =
htole32(RATEVAL(rate->RateSeries[i].Rate));
fp->FixRateTypeFlags.RetryCountValid =
htole32(RETRY_COUNT_VALID);
fp->RetryCount =
htole32(rate->RateSeries[i].TryCount-1);
n++;
}
pCmd->EntryCount = htole32(n);
} else
pCmd->Action = htole32(HostCmd_ACT_NOT_USE_FIXED_RATE);
retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_FIXED_RATE);
MWL_HAL_UNLOCK(mh);
return retval;
}
int
mwl_hal_settxrate_auto(struct mwl_hal *mh0, const MWL_HAL_TXRATE *rate)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
HostCmd_FW_USE_FIXED_RATE *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_CMD_SETUP(pCmd, HostCmd_FW_USE_FIXED_RATE,
HostCmd_CMD_SET_FIXED_RATE);
pCmd->MulticastRate = RATEVAL(rate->McastRate);
pCmd->MultiRateTxType = RATETYPE(rate->McastRate);
/* NB: no rate type field */
pCmd->ManagementRate = RATEVAL(rate->MgtRate);
memset(pCmd->FixedRateTable, 0, sizeof(pCmd->FixedRateTable));
pCmd->Action = htole32(HostCmd_ACT_NOT_USE_FIXED_RATE);
retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_FIXED_RATE);
MWL_HAL_UNLOCK(mh);
return retval;
}
#undef RATEVAL
#undef RATETYPE
int
mwl_hal_setslottime(struct mwl_hal *mh0, int usecs)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
HostCmd_FW_SET_SLOT *pCmd;
int retval;
if (usecs != 9 && usecs != 20)
return EINVAL;
MWL_HAL_LOCK(mh);
_CMD_SETUP(pCmd, HostCmd_FW_SET_SLOT,
HostCmd_CMD_802_11_SET_SLOT);
pCmd->Action = htole16(HostCmd_ACT_GEN_SET);
pCmd->Slot = (usecs == 9 ? 1 : 0);
retval = mwlExecuteCmd(mh, HostCmd_CMD_802_11_SET_SLOT);
MWL_HAL_UNLOCK(mh);
return retval;
}
int
mwl_hal_adjusttxpower(struct mwl_hal *mh0, uint32_t level)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
HostCmd_DS_802_11_RF_TX_POWER *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_CMD_SETUP(pCmd, HostCmd_DS_802_11_RF_TX_POWER,
HostCmd_CMD_802_11_RF_TX_POWER);
pCmd->Action = htole16(HostCmd_ACT_GEN_SET);
if (level < 30) {
pCmd->SupportTxPowerLevel = htole16(WL_TX_POWERLEVEL_LOW);
} else if (level >= 30 && level < 60) {
pCmd->SupportTxPowerLevel = htole16(WL_TX_POWERLEVEL_MEDIUM);
} else {
pCmd->SupportTxPowerLevel = htole16(WL_TX_POWERLEVEL_HIGH);
}
retval = mwlExecuteCmd(mh, HostCmd_CMD_802_11_RF_TX_POWER);
MWL_HAL_UNLOCK(mh);
return retval;
}
static const struct mwl_hal_channel *
findchannel(const struct mwl_hal_priv *mh, const MWL_HAL_CHANNEL *c)
{
const struct mwl_hal_channel *hc;
const MWL_HAL_CHANNELINFO *ci;
int chan = c->channel, i;
if (c->channelFlags.FreqBand == MWL_FREQ_BAND_2DOT4GHZ) {
i = chan - 1;
if (c->channelFlags.ChnlWidth == MWL_CH_40_MHz_WIDTH) {
ci = &mh->mh_40M;
if (c->channelFlags.ExtChnlOffset == MWL_EXT_CH_BELOW_CTRL_CH)
i -= 4;
} else
ci = &mh->mh_20M;
/* 2.4G channel table is directly indexed */
hc = ((unsigned)i < ci->nchannels) ? &ci->channels[i] : NULL;
} else if (c->channelFlags.FreqBand == MWL_FREQ_BAND_5GHZ) {
if (c->channelFlags.ChnlWidth == MWL_CH_40_MHz_WIDTH) {
ci = &mh->mh_40M_5G;
if (c->channelFlags.ExtChnlOffset == MWL_EXT_CH_BELOW_CTRL_CH)
chan -= 4;
} else
ci = &mh->mh_20M_5G;
/* 5GHz channel table is sparse and must be searched */
for (i = 0; i < ci->nchannels; i++)
if (ci->channels[i].ieee == chan)
break;
hc = (i < ci->nchannels) ? &ci->channels[i] : NULL;
} else
hc = NULL;
return hc;
}
int
mwl_hal_settxpower(struct mwl_hal *mh0, const MWL_HAL_CHANNEL *c, uint8_t maxtxpow)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
HostCmd_DS_802_11_RF_TX_POWER *pCmd;
const struct mwl_hal_channel *hc;
int i, retval;
hc = findchannel(mh, c);
if (hc == NULL) {
/* XXX temp while testing */
device_printf(mh->mh_dev,
"%s: no cal data for channel %u band %u width %u ext %u\n",
__func__, c->channel, c->channelFlags.FreqBand,
c->channelFlags.ChnlWidth, c->channelFlags.ExtChnlOffset);
return EINVAL;
}
MWL_HAL_LOCK(mh);
_CMD_SETUP(pCmd, HostCmd_DS_802_11_RF_TX_POWER,
HostCmd_CMD_802_11_RF_TX_POWER);
pCmd->Action = htole16(HostCmd_ACT_GEN_SET_LIST);
i = 0;
/* NB: 5Ghz cal data have the channel # in [0]; don't truncate */
if (c->channelFlags.FreqBand == MWL_FREQ_BAND_5GHZ)
pCmd->PowerLevelList[i++] = htole16(hc->targetPowers[0]);
for (; i < 4; i++) {
uint16_t pow = hc->targetPowers[i];
if (pow > maxtxpow)
pow = maxtxpow;
pCmd->PowerLevelList[i] = htole16(pow);
}
retval = mwlExecuteCmd(mh, HostCmd_CMD_802_11_RF_TX_POWER);
MWL_HAL_UNLOCK(mh);
return retval;
}
int
mwl_hal_getchannelinfo(struct mwl_hal *mh0, int band, int chw,
const MWL_HAL_CHANNELINFO **ci)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
switch (band) {
case MWL_FREQ_BAND_2DOT4GHZ:
*ci = (chw == MWL_CH_20_MHz_WIDTH) ? &mh->mh_20M : &mh->mh_40M;
break;
case MWL_FREQ_BAND_5GHZ:
*ci = (chw == MWL_CH_20_MHz_WIDTH) ?
&mh->mh_20M_5G : &mh->mh_40M_5G;
break;
default:
return EINVAL;
}
return ((*ci)->freqLow == (*ci)->freqHigh) ? EINVAL : 0;
}
int
mwl_hal_setmcast(struct mwl_hal *mh0, int nmc, const uint8_t macs[])
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
HostCmd_DS_MAC_MULTICAST_ADR *pCmd;
int retval;
if (nmc > MWL_HAL_MCAST_MAX)
return EINVAL;
MWL_HAL_LOCK(mh);
_CMD_SETUP(pCmd, HostCmd_DS_MAC_MULTICAST_ADR,
HostCmd_CMD_MAC_MULTICAST_ADR);
memcpy(pCmd->MACList, macs, nmc*IEEE80211_ADDR_LEN);
pCmd->NumOfAdrs = htole16(nmc);
pCmd->Action = htole16(0xffff);
retval = mwlExecuteCmd(mh, HostCmd_CMD_MAC_MULTICAST_ADR);
MWL_HAL_UNLOCK(mh);
return retval;
}
int
mwl_hal_keyset(struct mwl_hal_vap *vap, const MWL_HAL_KEYVAL *kv,
const uint8_t mac[IEEE80211_ADDR_LEN])
{
struct mwl_hal_priv *mh = MWLVAP(vap);
HostCmd_FW_UPDATE_ENCRYPTION_SET_KEY *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_VCMD_SETUP(vap, pCmd, HostCmd_FW_UPDATE_ENCRYPTION_SET_KEY,
HostCmd_CMD_UPDATE_ENCRYPTION);
if (kv->keyFlags & (KEY_FLAG_TXGROUPKEY|KEY_FLAG_RXGROUPKEY))
pCmd->ActionType = htole32(EncrActionTypeSetGroupKey);
else
pCmd->ActionType = htole32(EncrActionTypeSetKey);
pCmd->KeyParam.Length = htole16(sizeof(pCmd->KeyParam));
pCmd->KeyParam.KeyTypeId = htole16(kv->keyTypeId);
pCmd->KeyParam.KeyInfo = htole32(kv->keyFlags);
pCmd->KeyParam.KeyIndex = htole32(kv->keyIndex);
/* NB: includes TKIP MIC keys */
memcpy(&pCmd->KeyParam.Key, &kv->key, kv->keyLen);
switch (kv->keyTypeId) {
case KEY_TYPE_ID_WEP:
pCmd->KeyParam.KeyLen = htole16(kv->keyLen);
break;
case KEY_TYPE_ID_TKIP:
pCmd->KeyParam.KeyLen = htole16(sizeof(TKIP_TYPE_KEY));
pCmd->KeyParam.Key.TkipKey.TkipRsc.low =
htole16(kv->key.tkip.rsc.low);
pCmd->KeyParam.Key.TkipKey.TkipRsc.high =
htole32(kv->key.tkip.rsc.high);
pCmd->KeyParam.Key.TkipKey.TkipTsc.low =
htole16(kv->key.tkip.tsc.low);
pCmd->KeyParam.Key.TkipKey.TkipTsc.high =
htole32(kv->key.tkip.tsc.high);
break;
case KEY_TYPE_ID_AES:
pCmd->KeyParam.KeyLen = htole16(sizeof(AES_TYPE_KEY));
break;
}
#ifdef MWL_MBSS_SUPPORT
IEEE80211_ADDR_COPY(pCmd->KeyParam.Macaddr, mac);
#else
IEEE80211_ADDR_COPY(pCmd->Macaddr, mac);
#endif
retval = mwlExecuteCmd(mh, HostCmd_CMD_UPDATE_ENCRYPTION);
MWL_HAL_UNLOCK(mh);
return retval;
}
int
mwl_hal_keyreset(struct mwl_hal_vap *vap, const MWL_HAL_KEYVAL *kv, const uint8_t mac[IEEE80211_ADDR_LEN])
{
struct mwl_hal_priv *mh = MWLVAP(vap);
HostCmd_FW_UPDATE_ENCRYPTION_SET_KEY *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_VCMD_SETUP(vap, pCmd, HostCmd_FW_UPDATE_ENCRYPTION_SET_KEY,
HostCmd_CMD_UPDATE_ENCRYPTION);
pCmd->ActionType = htole16(EncrActionTypeRemoveKey);
pCmd->KeyParam.Length = htole16(sizeof(pCmd->KeyParam));
pCmd->KeyParam.KeyTypeId = htole16(kv->keyTypeId);
pCmd->KeyParam.KeyInfo = htole32(kv->keyFlags);
pCmd->KeyParam.KeyIndex = htole32(kv->keyIndex);
#ifdef MWL_MBSS_SUPPORT
IEEE80211_ADDR_COPY(pCmd->KeyParam.Macaddr, mac);
#else
IEEE80211_ADDR_COPY(pCmd->Macaddr, mac);
#endif
retval = mwlExecuteCmd(mh, HostCmd_CMD_UPDATE_ENCRYPTION);
MWL_HAL_UNLOCK(mh);
return retval;
}
static int
mwl_hal_setmac_locked(struct mwl_hal_vap *vap,
const uint8_t addr[IEEE80211_ADDR_LEN])
{
struct mwl_hal_priv *mh = MWLVAP(vap);
HostCmd_DS_SET_MAC *pCmd;
_VCMD_SETUP(vap, pCmd, HostCmd_DS_SET_MAC, HostCmd_CMD_SET_MAC_ADDR);
IEEE80211_ADDR_COPY(&pCmd->MacAddr[0], addr);
#ifdef MWL_MBSS_SUPPORT
pCmd->MacType = vap->bss_type; /* NB: already byte swapped */
IEEE80211_ADDR_COPY(vap->mac, addr); /* XXX do only if success */
#endif
return mwlExecuteCmd(mh, HostCmd_CMD_SET_MAC_ADDR);
}
int
mwl_hal_setmac(struct mwl_hal_vap *vap, const uint8_t addr[IEEE80211_ADDR_LEN])
{
struct mwl_hal_priv *mh = MWLVAP(vap);
int retval;
MWL_HAL_LOCK(mh);
retval = mwl_hal_setmac_locked(vap, addr);
MWL_HAL_UNLOCK(mh);
return retval;
}
int
mwl_hal_setbeacon(struct mwl_hal_vap *vap, const void *frame, size_t frameLen)
{
struct mwl_hal_priv *mh = MWLVAP(vap);
HostCmd_DS_SET_BEACON *pCmd;
int retval;
/* XXX verify frameLen fits */
MWL_HAL_LOCK(mh);
_VCMD_SETUP(vap, pCmd, HostCmd_DS_SET_BEACON, HostCmd_CMD_SET_BEACON);
/* XXX override _VCMD_SETUP */
pCmd->CmdHdr.Length = htole16(sizeof(HostCmd_DS_SET_BEACON)-1+frameLen);
pCmd->FrmBodyLen = htole16(frameLen);
memcpy(pCmd->FrmBody, frame, frameLen);
retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_BEACON);
MWL_HAL_UNLOCK(mh);
return retval;
}
int
mwl_hal_setpowersave_bss(struct mwl_hal_vap *vap, uint8_t nsta)
{
struct mwl_hal_priv *mh = MWLVAP(vap);
HostCmd_SET_POWERSAVESTATION *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_VCMD_SETUP(vap, pCmd, HostCmd_SET_POWERSAVESTATION,
HostCmd_CMD_SET_POWERSAVESTATION);
pCmd->NumberOfPowersave = nsta;
retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_POWERSAVESTATION);
MWL_HAL_UNLOCK(mh);
return retval;
}
int
mwl_hal_setpowersave_sta(struct mwl_hal_vap *vap, uint16_t aid, int ena)
{
struct mwl_hal_priv *mh = MWLVAP(vap);
HostCmd_SET_TIM *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_VCMD_SETUP(vap, pCmd, HostCmd_SET_TIM, HostCmd_CMD_SET_TIM);
pCmd->Aid = htole16(aid);
pCmd->Set = htole32(ena);
retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_TIM);
MWL_HAL_UNLOCK(mh);
return retval;
}
int
mwl_hal_setassocid(struct mwl_hal_vap *vap,
const uint8_t bssId[IEEE80211_ADDR_LEN], uint16_t assocId)
{
struct mwl_hal_priv *mh = MWLVAP(vap);
HostCmd_FW_SET_AID *pCmd = (HostCmd_FW_SET_AID *) &mh->mh_cmdbuf[0];
int retval;
MWL_HAL_LOCK(mh);
_VCMD_SETUP(vap, pCmd, HostCmd_FW_SET_AID, HostCmd_CMD_SET_AID);
pCmd->AssocID = htole16(assocId);
IEEE80211_ADDR_COPY(&pCmd->MacAddr[0], bssId);
retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_AID);
MWL_HAL_UNLOCK(mh);
return retval;
}
int
mwl_hal_setchannel(struct mwl_hal *mh0, const MWL_HAL_CHANNEL *chan)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
HostCmd_FW_SET_RF_CHANNEL *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_CMD_SETUP(pCmd, HostCmd_FW_SET_RF_CHANNEL, HostCmd_CMD_SET_RF_CHANNEL);
pCmd->Action = htole16(HostCmd_ACT_GEN_SET);
pCmd->CurrentChannel = chan->channel;
pCmd->ChannelFlags = cvtChannelFlags(chan); /* NB: byte-swapped */
retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_RF_CHANNEL);
MWL_HAL_UNLOCK(mh);
return retval;
}
static int
bastream_check_available(struct mwl_hal_vap *vap, int qid,
const uint8_t Macaddr[IEEE80211_ADDR_LEN],
uint8_t Tid, uint8_t ParamInfo)
{
struct mwl_hal_priv *mh = MWLVAP(vap);
HostCmd_FW_BASTREAM *pCmd;
int retval;
MWL_HAL_LOCK_ASSERT(mh);
_VCMD_SETUP(vap, pCmd, HostCmd_FW_BASTREAM, HostCmd_CMD_BASTREAM);
pCmd->ActionType = htole32(BaCheckCreateStream);
pCmd->BaInfo.CreateParams.BarThrs = htole32(63);
pCmd->BaInfo.CreateParams.WindowSize = htole32(64);
pCmd->BaInfo.CreateParams.IdleThrs = htole32(0x22000);
IEEE80211_ADDR_COPY(&pCmd->BaInfo.CreateParams.PeerMacAddr[0], Macaddr);
pCmd->BaInfo.CreateParams.DialogToken = 10;
pCmd->BaInfo.CreateParams.Tid = Tid;
pCmd->BaInfo.CreateParams.QueueId = qid;
pCmd->BaInfo.CreateParams.ParamInfo = (uint8_t) ParamInfo;
#if 0
cvtBAFlags(&pCmd->BaInfo.CreateParams.Flags, sp->ba_policy, 0);
#else
pCmd->BaInfo.CreateParams.Flags =
htole32(BASTREAM_FLAG_IMMEDIATE_TYPE)
| htole32(BASTREAM_FLAG_DIRECTION_UPSTREAM)
;
#endif
retval = mwlExecuteCmd(mh, HostCmd_CMD_BASTREAM);
if (retval == 0) {
/*
* NB: BA stream create may fail when the stream is
* h/w backed under some (as yet not understood) conditions.
* Check the result code to catch this.
*/
if (le16toh(pCmd->CmdHdr.Result) != HostCmd_RESULT_OK)
retval = EIO;
}
return retval;
}
const MWL_HAL_BASTREAM *
mwl_hal_bastream_alloc(struct mwl_hal_vap *vap, int ba_policy,
const uint8_t Macaddr[IEEE80211_ADDR_LEN],
uint8_t Tid, uint8_t ParamInfo, void *a1, void *a2)
{
struct mwl_hal_priv *mh = MWLVAP(vap);
struct mwl_hal_bastream *sp;
int s;
MWL_HAL_LOCK(mh);
if (mh->mh_bastreams == 0) {
/* no streams available */
MWL_HAL_UNLOCK(mh);
return NULL;
}
for (s = 0; (mh->mh_bastreams & (1<<s)) == 0; s++)
;
if (bastream_check_available(vap, s, Macaddr, Tid, ParamInfo)) {
MWL_HAL_UNLOCK(mh);
return NULL;
}
sp = &mh->mh_streams[s];
mh->mh_bastreams &= ~(1<<s);
sp->public.data[0] = a1;
sp->public.data[1] = a2;
IEEE80211_ADDR_COPY(sp->macaddr, Macaddr);
sp->tid = Tid;
sp->paraminfo = ParamInfo;
sp->setup = 0;
sp->ba_policy = ba_policy;
MWL_HAL_UNLOCK(mh);
return &sp->public;
}
const MWL_HAL_BASTREAM *
mwl_hal_bastream_lookup(struct mwl_hal *mh0, int s)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
if (!(0 <= s && s < MWL_BASTREAMS_MAX))
return NULL;
if (mh->mh_bastreams & (1<<s))
return NULL;
return &mh->mh_streams[s].public;
}
#ifndef __DECONST
#define __DECONST(type, var) ((type)(uintptr_t)(const void *)(var))
#endif
int
mwl_hal_bastream_create(struct mwl_hal_vap *vap,
const MWL_HAL_BASTREAM *s, int BarThrs, int WindowSize, uint16_t seqno)
{
struct mwl_hal_priv *mh = MWLVAP(vap);
struct mwl_hal_bastream *sp = __DECONST(struct mwl_hal_bastream *, s);
HostCmd_FW_BASTREAM *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_VCMD_SETUP(vap, pCmd, HostCmd_FW_BASTREAM, HostCmd_CMD_BASTREAM);
pCmd->ActionType = htole32(BaCreateStream);
pCmd->BaInfo.CreateParams.BarThrs = htole32(BarThrs);
pCmd->BaInfo.CreateParams.WindowSize = htole32(WindowSize);
pCmd->BaInfo.CreateParams.IdleThrs = htole32(0x22000);
IEEE80211_ADDR_COPY(&pCmd->BaInfo.CreateParams.PeerMacAddr[0],
sp->macaddr);
/* XXX proxy STA */
memset(&pCmd->BaInfo.CreateParams.StaSrcMacAddr, 0, IEEE80211_ADDR_LEN);
#if 0
pCmd->BaInfo.CreateParams.DialogToken = DialogToken;
#else
pCmd->BaInfo.CreateParams.DialogToken = 10;
#endif
pCmd->BaInfo.CreateParams.Tid = sp->tid;
pCmd->BaInfo.CreateParams.QueueId = sp->stream;
pCmd->BaInfo.CreateParams.ParamInfo = sp->paraminfo;
/* NB: ResetSeqNo known to be zero */
pCmd->BaInfo.CreateParams.StartSeqNo = htole16(seqno);
#if 0
cvtBAFlags(&pCmd->BaInfo.CreateParams.Flags, sp->ba_policy, 0);
#else
pCmd->BaInfo.CreateParams.Flags =
htole32(BASTREAM_FLAG_IMMEDIATE_TYPE)
| htole32(BASTREAM_FLAG_DIRECTION_UPSTREAM)
;
#endif
retval = mwlExecuteCmd(mh, HostCmd_CMD_BASTREAM);
if (retval == 0) {
/*
* NB: BA stream create may fail when the stream is
* h/w backed under some (as yet not understood) conditions.
* Check the result code to catch this.
*/
if (le16toh(pCmd->CmdHdr.Result) != HostCmd_RESULT_OK)
retval = EIO;
else
sp->setup = 1;
}
MWL_HAL_UNLOCK(mh);
return retval;
}
int
mwl_hal_bastream_destroy(struct mwl_hal *mh0, const MWL_HAL_BASTREAM *s)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
struct mwl_hal_bastream *sp = __DECONST(struct mwl_hal_bastream *, s);
HostCmd_FW_BASTREAM *pCmd;
int retval;
if (sp->stream >= MWL_BASTREAMS_MAX) {
/* XXX */
return EINVAL;
}
MWL_HAL_LOCK(mh);
if (sp->setup) {
_CMD_SETUP(pCmd, HostCmd_FW_BASTREAM, HostCmd_CMD_BASTREAM);
pCmd->ActionType = htole32(BaDestroyStream);
pCmd->BaInfo.DestroyParams.FwBaContext.Context =
htole32(sp->stream);
retval = mwlExecuteCmd(mh, HostCmd_CMD_BASTREAM);
} else
retval = 0;
/* NB: always reclaim stream */
mh->mh_bastreams |= 1<<sp->stream;
sp->public.data[0] = NULL;
sp->public.data[1] = NULL;
sp->setup = 0;
MWL_HAL_UNLOCK(mh);
return retval;
}
int
mwl_hal_bastream_get_seqno(struct mwl_hal *mh0,
const MWL_HAL_BASTREAM *s, const uint8_t Macaddr[IEEE80211_ADDR_LEN],
uint16_t *pseqno)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
struct mwl_hal_bastream *sp = __DECONST(struct mwl_hal_bastream *, s);
HostCmd_GET_SEQNO *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_CMD_SETUP(pCmd, HostCmd_GET_SEQNO, HostCmd_CMD_GET_SEQNO);
IEEE80211_ADDR_COPY(pCmd->MacAddr, Macaddr);
pCmd->TID = sp->tid;
retval = mwlExecuteCmd(mh, HostCmd_CMD_GET_SEQNO);
if (retval == 0)
*pseqno = le16toh(pCmd->SeqNo);
MWL_HAL_UNLOCK(mh);
return retval;
}
int
mwl_hal_getwatchdogbitmap(struct mwl_hal *mh0, uint8_t bitmap[1])
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
HostCmd_FW_GET_WATCHDOG_BITMAP *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_CMD_SETUP(pCmd, HostCmd_FW_GET_WATCHDOG_BITMAP,
HostCmd_CMD_GET_WATCHDOG_BITMAP);
retval = mwlExecuteCmd(mh, HostCmd_CMD_GET_WATCHDOG_BITMAP);
if (retval == 0) {
bitmap[0] = pCmd->Watchdogbitmap;
/* fw returns qid, map it to BA stream */
if (bitmap[0] < MWL_BAQID_MAX)
bitmap[0] = qid2ba[bitmap[0]];
}
MWL_HAL_UNLOCK(mh);
return retval;
}
/*
* Configure aggressive Ampdu rate mode.
*/
int
mwl_hal_setaggampduratemode(struct mwl_hal *mh0, int mode, int threshold)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
HostCmd_FW_AMPDU_RETRY_RATEDROP_MODE *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_CMD_SETUP(pCmd, HostCmd_FW_AMPDU_RETRY_RATEDROP_MODE,
HostCmd_CMD_AMPDU_RETRY_RATEDROP_MODE);
pCmd->Action = htole16(1);
pCmd->Option = htole32(mode);
pCmd->Threshold = htole32(threshold);
retval = mwlExecuteCmd(mh, HostCmd_CMD_AMPDU_RETRY_RATEDROP_MODE);
MWL_HAL_UNLOCK(mh);
return retval;
}
int
mwl_hal_getaggampduratemode(struct mwl_hal *mh0, int *mode, int *threshold)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
HostCmd_FW_AMPDU_RETRY_RATEDROP_MODE *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_CMD_SETUP(pCmd, HostCmd_FW_AMPDU_RETRY_RATEDROP_MODE,
HostCmd_CMD_AMPDU_RETRY_RATEDROP_MODE);
pCmd->Action = htole16(0);
retval = mwlExecuteCmd(mh, HostCmd_CMD_AMPDU_RETRY_RATEDROP_MODE);
MWL_HAL_UNLOCK(mh);
*mode = le32toh(pCmd->Option);
*threshold = le32toh(pCmd->Threshold);
return retval;
}
/*
* Set CFEND status Enable/Disable
*/
int
mwl_hal_setcfend(struct mwl_hal *mh0, int ena)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
HostCmd_CFEND_ENABLE *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_CMD_SETUP(pCmd, HostCmd_CFEND_ENABLE,
HostCmd_CMD_CFEND_ENABLE);
pCmd->Enable = htole32(ena);
retval = mwlExecuteCmd(mh, HostCmd_CMD_CFEND_ENABLE);
MWL_HAL_UNLOCK(mh);
return retval;
}
int
mwl_hal_setdwds(struct mwl_hal *mh0, int ena)
{
HostCmd_DWDS_ENABLE *pCmd;
struct mwl_hal_priv *mh = MWLPRIV(mh0);
int retval;
MWL_HAL_LOCK(mh);
_CMD_SETUP(pCmd, HostCmd_DWDS_ENABLE, HostCmd_CMD_DWDS_ENABLE);
pCmd->Enable = htole32(ena);
retval = mwlExecuteCmd(mh, HostCmd_CMD_DWDS_ENABLE);
MWL_HAL_UNLOCK(mh);
return retval;
}
static void
cvtPeerInfo(PeerInfo_t *to, const MWL_HAL_PEERINFO *from)
{
to->LegacyRateBitMap = htole32(from->LegacyRateBitMap);
to->HTRateBitMap = htole32(from->HTRateBitMap);
to->CapInfo = htole16(from->CapInfo);
to->HTCapabilitiesInfo = htole16(from->HTCapabilitiesInfo);
to->MacHTParamInfo = from->MacHTParamInfo;
to->AddHtInfo.ControlChan = from->AddHtInfo.ControlChan;
to->AddHtInfo.AddChan = from->AddHtInfo.AddChan;
to->AddHtInfo.OpMode = htole16(from->AddHtInfo.OpMode);
to->AddHtInfo.stbc = htole16(from->AddHtInfo.stbc);
}
/* XXX station id must be in [0..63] */
int
mwl_hal_newstation(struct mwl_hal_vap *vap,
const uint8_t addr[IEEE80211_ADDR_LEN], uint16_t aid, uint16_t sid,
const MWL_HAL_PEERINFO *peer, int isQosSta, int wmeInfo)
{
struct mwl_hal_priv *mh = MWLVAP(vap);
HostCmd_FW_SET_NEW_STN *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_VCMD_SETUP(vap, pCmd, HostCmd_FW_SET_NEW_STN, HostCmd_CMD_SET_NEW_STN);
pCmd->AID = htole16(aid);
pCmd->StnId = htole16(sid);
pCmd->Action = htole16(0); /* SET */
if (peer != NULL) {
/* NB: must fix up byte order */
cvtPeerInfo(&pCmd->PeerInfo, peer);
}
IEEE80211_ADDR_COPY(&pCmd->MacAddr[0], addr);
pCmd->Qosinfo = wmeInfo;
pCmd->isQosSta = (isQosSta != 0);
retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_NEW_STN);
if (retval == 0 && IEEE80211_ADDR_EQ(vap->mac, addr))
vap->flags |= MVF_STATION;
MWL_HAL_UNLOCK(mh);
return retval;
}
int
mwl_hal_delstation(struct mwl_hal_vap *vap,
const uint8_t addr[IEEE80211_ADDR_LEN])
{
struct mwl_hal_priv *mh = MWLVAP(vap);
HostCmd_FW_SET_NEW_STN *pCmd;
int retval, islocal;
MWL_HAL_LOCK(mh);
islocal = IEEE80211_ADDR_EQ(vap->mac, addr);
if (!islocal || (vap->flags & MVF_STATION)) {
_VCMD_SETUP(vap, pCmd, HostCmd_FW_SET_NEW_STN,
HostCmd_CMD_SET_NEW_STN);
pCmd->Action = htole16(2); /* REMOVE */
IEEE80211_ADDR_COPY(&pCmd->MacAddr[0], addr);
retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_NEW_STN);
if (islocal)
vap->flags &= ~MVF_STATION;
} else
retval = 0;
MWL_HAL_UNLOCK(mh);
return retval;
}
/*
* Prod the firmware to age packets on station power
* save queues and reap frames on the tx aggregation q's.
*/
int
mwl_hal_setkeepalive(struct mwl_hal *mh0)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
HostCmd_FW_SET_KEEP_ALIVE_TICK *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_CMD_SETUP(pCmd, HostCmd_FW_SET_KEEP_ALIVE_TICK,
HostCmd_CMD_SET_KEEP_ALIVE);
/*
* NB: tick must be 0 to prod the f/w;
* a non-zero value is a noop.
*/
pCmd->tick = 0;
retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_KEEP_ALIVE);
MWL_HAL_UNLOCK(mh);
return retval;
}
int
mwl_hal_setapmode(struct mwl_hal_vap *vap, MWL_HAL_APMODE ApMode)
{
struct mwl_hal_priv *mh = MWLVAP(vap);
HostCmd_FW_SET_APMODE *pCmd;
int retval;
/* XXX validate ApMode? */
MWL_HAL_LOCK(mh);
_VCMD_SETUP(vap, pCmd, HostCmd_FW_SET_APMODE, HostCmd_CMD_SET_APMODE);
pCmd->ApMode = ApMode;
retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_APMODE);
MWL_HAL_UNLOCK(mh);
return retval;
}
int
mwl_hal_stop(struct mwl_hal_vap *vap)
{
struct mwl_hal_priv *mh = MWLVAP(vap);
HostCmd_DS_BSS_START *pCmd;
int retval;
MWL_HAL_LOCK(mh);
if (vap->flags & MVF_RUNNING) {
_VCMD_SETUP(vap, pCmd, HostCmd_DS_BSS_START,
HostCmd_CMD_BSS_START);
pCmd->Enable = htole32(HostCmd_ACT_GEN_OFF);
retval = mwlExecuteCmd(mh, HostCmd_CMD_BSS_START);
} else
retval = 0;
/* NB: mark !running regardless */
vap->flags &= ~MVF_RUNNING;
MWL_HAL_UNLOCK(mh);
return retval;
}
int
mwl_hal_start(struct mwl_hal_vap *vap)
{
struct mwl_hal_priv *mh = MWLVAP(vap);
HostCmd_DS_BSS_START *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_VCMD_SETUP(vap, pCmd, HostCmd_DS_BSS_START, HostCmd_CMD_BSS_START);
pCmd->Enable = htole32(HostCmd_ACT_GEN_ON);
retval = mwlExecuteCmd(mh, HostCmd_CMD_BSS_START);
if (retval == 0)
vap->flags |= MVF_RUNNING;
MWL_HAL_UNLOCK(mh);
return retval;
}
int
mwl_hal_setgprot(struct mwl_hal *mh0, int prot)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
HostCmd_FW_SET_G_PROTECT_FLAG *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_CMD_SETUP(pCmd, HostCmd_FW_SET_G_PROTECT_FLAG,
HostCmd_CMD_SET_G_PROTECT_FLAG);
pCmd->GProtectFlag = htole32(prot);
retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_G_PROTECT_FLAG);
MWL_HAL_UNLOCK(mh);
return retval;
}
int
mwl_hal_setwmm(struct mwl_hal *mh0, int onoff)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
HostCmd_FW_SetWMMMode *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_CMD_SETUP(pCmd, HostCmd_FW_SetWMMMode,
HostCmd_CMD_SET_WMM_MODE);
pCmd->Action = htole16(onoff);
retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_WMM_MODE);
MWL_HAL_UNLOCK(mh);
return retval;
}
int
mwl_hal_setedcaparams(struct mwl_hal *mh0, uint8_t qnum,
uint32_t CWmin, uint32_t CWmax, uint8_t AIFSN, uint16_t TXOPLimit)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
HostCmd_FW_SET_EDCA_PARAMS *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_CMD_SETUP(pCmd, HostCmd_FW_SET_EDCA_PARAMS,
HostCmd_CMD_SET_EDCA_PARAMS);
/*
* NB: CWmin and CWmax are always set.
* TxOpLimit is set if bit 0x2 is marked in Action
* AIFSN is set if bit 0x4 is marked in Action
*/
pCmd->Action = htole16(0xffff); /* NB: set everything */
pCmd->TxOP = htole16(TXOPLimit);
pCmd->CWMax = htole32(CWmax);
pCmd->CWMin = htole32(CWmin);
pCmd->AIFSN = AIFSN;
pCmd->TxQNum = qnum; /* XXX check */
retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_EDCA_PARAMS);
MWL_HAL_UNLOCK(mh);
return retval;
}
/* XXX 0 = indoor, 1 = outdoor */
int
mwl_hal_setrateadaptmode(struct mwl_hal *mh0, uint16_t mode)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
HostCmd_DS_SET_RATE_ADAPT_MODE *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_CMD_SETUP(pCmd, HostCmd_DS_SET_RATE_ADAPT_MODE,
HostCmd_CMD_SET_RATE_ADAPT_MODE);
pCmd->Action = htole16(HostCmd_ACT_GEN_SET);
pCmd->RateAdaptMode = htole16(mode);
retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_RATE_ADAPT_MODE);
MWL_HAL_UNLOCK(mh);
return retval;
}
int
mwl_hal_setcsmode(struct mwl_hal *mh0, MWL_HAL_CSMODE csmode)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
HostCmd_DS_SET_LINKADAPT_CS_MODE *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_CMD_SETUP(pCmd, HostCmd_DS_SET_LINKADAPT_CS_MODE,
HostCmd_CMD_SET_LINKADAPT_CS_MODE);
pCmd->Action = htole16(HostCmd_ACT_GEN_SET);
pCmd->CSMode = htole16(csmode);
retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_LINKADAPT_CS_MODE);
MWL_HAL_UNLOCK(mh);
return retval;
}
int
mwl_hal_setnprot(struct mwl_hal_vap *vap, MWL_HAL_HTPROTECT mode)
{
struct mwl_hal_priv *mh = MWLVAP(vap);
HostCmd_FW_SET_N_PROTECT_FLAG *pCmd;
int retval;
/* XXX validate mode */
MWL_HAL_LOCK(mh);
_VCMD_SETUP(vap, pCmd, HostCmd_FW_SET_N_PROTECT_FLAG,
HostCmd_CMD_SET_N_PROTECT_FLAG);
pCmd->NProtectFlag = htole32(mode);
retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_N_PROTECT_FLAG);
MWL_HAL_UNLOCK(mh);
return retval;
}
int
mwl_hal_setnprotmode(struct mwl_hal_vap *vap, uint8_t mode)
{
struct mwl_hal_priv *mh = MWLVAP(vap);
HostCmd_FW_SET_N_PROTECT_OPMODE *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_VCMD_SETUP(vap, pCmd, HostCmd_FW_SET_N_PROTECT_OPMODE,
HostCmd_CMD_SET_N_PROTECT_OPMODE);
pCmd->NProtectOpMode = mode;
retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_N_PROTECT_OPMODE);
MWL_HAL_UNLOCK(mh);
return retval;
}
int
mwl_hal_setoptimizationlevel(struct mwl_hal *mh0, int level)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
HostCmd_FW_SET_OPTIMIZATION_LEVEL *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_CMD_SETUP(pCmd, HostCmd_FW_SET_OPTIMIZATION_LEVEL,
HostCmd_CMD_SET_OPTIMIZATION_LEVEL);
pCmd->OptLevel = level;
retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_OPTIMIZATION_LEVEL);
MWL_HAL_UNLOCK(mh);
return retval;
}
int
mwl_hal_setmimops(struct mwl_hal *mh0, const uint8_t addr[IEEE80211_ADDR_LEN],
uint8_t enable, uint8_t mode)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
HostCmd_FW_SET_MIMOPSHT *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_CMD_SETUP(pCmd, HostCmd_FW_SET_MIMOPSHT, HostCmd_CMD_SET_MIMOPSHT);
IEEE80211_ADDR_COPY(pCmd->Addr, addr);
pCmd->Enable = enable;
pCmd->Mode = mode;
retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_MIMOPSHT);
MWL_HAL_UNLOCK(mh);
return retval;
}
static int
mwlGetCalTable(struct mwl_hal_priv *mh, uint8_t annex, uint8_t index)
{
HostCmd_FW_GET_CALTABLE *pCmd;
int retval;
MWL_HAL_LOCK_ASSERT(mh);
_CMD_SETUP(pCmd, HostCmd_FW_GET_CALTABLE, HostCmd_CMD_GET_CALTABLE);
pCmd->annex = annex;
pCmd->index = index;
memset(pCmd->calTbl, 0, sizeof(pCmd->calTbl));
retval = mwlExecuteCmd(mh, HostCmd_CMD_GET_CALTABLE);
if (retval == 0 &&
pCmd->calTbl[0] != annex && annex != 0 && annex != 255)
retval = EIO;
return retval;
}
/*
* Calculate the max tx power from the channel's cal data.
*/
static void
setmaxtxpow(struct mwl_hal_channel *hc, int i, int maxix)
{
hc->maxTxPow = hc->targetPowers[i];
for (i++; i < maxix; i++)
if (hc->targetPowers[i] > hc->maxTxPow)
hc->maxTxPow = hc->targetPowers[i];
}
/*
* Construct channel info for 5GHz channels from cal data.
*/
static void
get5Ghz(MWL_HAL_CHANNELINFO *ci, const uint8_t table[], int len)
{
int i, j, f, l, h;
l = 32000;
h = 0;
j = 0;
for (i = 0; i < len; i += 4) {
struct mwl_hal_channel *hc;
if (table[i] == 0)
continue;
f = 5000 + 5*table[i];
if (f < l)
l = f;
if (f > h)
h = f;
hc = &ci->channels[j];
hc->freq = f;
hc->ieee = table[i];
memcpy(hc->targetPowers, &table[i], 4);
setmaxtxpow(hc, 1, 4); /* NB: col 1 is the freq, skip*/
j++;
}
ci->nchannels = j;
ci->freqLow = (l == 32000) ? 0 : l;
ci->freqHigh = h;
}
static uint16_t
ieee2mhz(int chan)
{
if (chan == 14)
return 2484;
if (chan < 14)
return 2407 + chan*5;
return 2512 + (chan-15)*20;
}
/*
* Construct channel info for 2.4GHz channels from cal data.
*/
static void
get2Ghz(MWL_HAL_CHANNELINFO *ci, const uint8_t table[], int len)
{
int i, j;
j = 0;
for (i = 0; i < len; i += 4) {
struct mwl_hal_channel *hc = &ci->channels[j];
hc->ieee = 1+j;
hc->freq = ieee2mhz(1+j);
memcpy(hc->targetPowers, &table[i], 4);
setmaxtxpow(hc, 0, 4);
j++;
}
ci->nchannels = j;
ci->freqLow = ieee2mhz(1);
ci->freqHigh = ieee2mhz(j);
}
#undef DUMPCALDATA
#ifdef DUMPCALDATA
static void
dumpcaldata(const char *name, const uint8_t *table, int n)
{
int i;
printf("\n%s:\n", name);
for (i = 0; i < n; i += 4)
printf("[%2d] %3d %3d %3d %3d\n", i/4, table[i+0], table[i+1], table[i+2], table[i+3]);
}
#endif
static int
mwlGetPwrCalTable(struct mwl_hal_priv *mh)
{
const uint8_t *data;
MWL_HAL_CHANNELINFO *ci;
int len;
MWL_HAL_LOCK(mh);
/* NB: we hold the lock so it's ok to use cmdbuf */
data = ((const HostCmd_FW_GET_CALTABLE *) mh->mh_cmdbuf)->calTbl;
if (mwlGetCalTable(mh, 33, 0) == 0) {
len = (data[2] | (data[3] << 8)) - 12;
if (len > PWTAGETRATETABLE20M)
len = PWTAGETRATETABLE20M;
#ifdef DUMPCALDATA
dumpcaldata("2.4G 20M", &data[12], len);/*XXX*/
#endif
get2Ghz(&mh->mh_20M, &data[12], len);
}
if (mwlGetCalTable(mh, 34, 0) == 0) {
len = (data[2] | (data[3] << 8)) - 12;
if (len > PWTAGETRATETABLE40M)
len = PWTAGETRATETABLE40M;
#ifdef DUMPCALDATA
dumpcaldata("2.4G 40M", &data[12], len);/*XXX*/
#endif
ci = &mh->mh_40M;
get2Ghz(ci, &data[12], len);
}
if (mwlGetCalTable(mh, 35, 0) == 0) {
len = (data[2] | (data[3] << 8)) - 20;
if (len > PWTAGETRATETABLE20M_5G)
len = PWTAGETRATETABLE20M_5G;
#ifdef DUMPCALDATA
dumpcaldata("5G 20M", &data[20], len);/*XXX*/
#endif
get5Ghz(&mh->mh_20M_5G, &data[20], len);
}
if (mwlGetCalTable(mh, 36, 0) == 0) {
len = (data[2] | (data[3] << 8)) - 20;
if (len > PWTAGETRATETABLE40M_5G)
len = PWTAGETRATETABLE40M_5G;
#ifdef DUMPCALDATA
dumpcaldata("5G 40M", &data[20], len);/*XXX*/
#endif
ci = &mh->mh_40M_5G;
get5Ghz(ci, &data[20], len);
}
mh->mh_flags |= MHF_CALDATA;
MWL_HAL_UNLOCK(mh);
return 0;
}
int
mwl_hal_getregioncode(struct mwl_hal *mh0, uint8_t *countryCode)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
int retval;
MWL_HAL_LOCK(mh);
retval = mwlGetCalTable(mh, 0, 0);
if (retval == 0) {
const HostCmd_FW_GET_CALTABLE *pCmd =
(const HostCmd_FW_GET_CALTABLE *) mh->mh_cmdbuf;
*countryCode = pCmd->calTbl[16];
}
MWL_HAL_UNLOCK(mh);
return retval;
}
int
mwl_hal_setpromisc(struct mwl_hal *mh0, int ena)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
uint32_t v;
MWL_HAL_LOCK(mh);
v = RD4(mh, MACREG_REG_PROMISCUOUS);
WR4(mh, MACREG_REG_PROMISCUOUS, ena ? v | 1 : v &~ 1);
MWL_HAL_UNLOCK(mh);
return 0;
}
int
mwl_hal_getpromisc(struct mwl_hal *mh0)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
uint32_t v;
MWL_HAL_LOCK(mh);
v = RD4(mh, MACREG_REG_PROMISCUOUS);
MWL_HAL_UNLOCK(mh);
return (v & 1) != 0;
}
int
mwl_hal_GetBeacon(struct mwl_hal *mh0, uint8_t *pBcn, uint16_t *pLen)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
HostCmd_FW_GET_BEACON *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_CMD_SETUP(pCmd, HostCmd_FW_GET_BEACON, HostCmd_CMD_GET_BEACON);
pCmd->Bcnlen = htole16(0);
retval = mwlExecuteCmd(mh, HostCmd_CMD_GET_BEACON);
if (retval == 0) {
/* XXX bounds check */
memcpy(pBcn, &pCmd->Bcn, pCmd->Bcnlen);
*pLen = pCmd->Bcnlen;
}
MWL_HAL_UNLOCK(mh);
return retval;
}
int
mwl_hal_SetRifs(struct mwl_hal *mh0, uint8_t QNum)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
HostCmd_FW_SET_RIFS *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_CMD_SETUP(pCmd, HostCmd_FW_SET_RIFS, HostCmd_CMD_SET_RIFS);
pCmd->QNum = QNum;
retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_RIFS);
MWL_HAL_UNLOCK(mh);
return retval;
}
/*
* Diagnostic api's for set/get registers.
*/
static int
getRFReg(struct mwl_hal_priv *mh, int flag, uint32_t reg, uint32_t *val)
{
HostCmd_DS_RF_REG_ACCESS *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_CMD_SETUP(pCmd, HostCmd_DS_RF_REG_ACCESS, HostCmd_CMD_RF_REG_ACCESS);
pCmd->Offset = htole16(reg);
pCmd->Action = htole16(flag);
pCmd->Value = htole32(*val);
retval = mwlExecuteCmd(mh, HostCmd_CMD_RF_REG_ACCESS);
if (retval == 0)
*val = pCmd->Value;
MWL_HAL_UNLOCK(mh);
return retval;
}
static int
getBBReg(struct mwl_hal_priv *mh, int flag, uint32_t reg, uint32_t *val)
{
HostCmd_DS_BBP_REG_ACCESS *pCmd;
int retval;
MWL_HAL_LOCK(mh);
_CMD_SETUP(pCmd, HostCmd_DS_BBP_REG_ACCESS, HostCmd_CMD_BBP_REG_ACCESS);
pCmd->Offset = htole16(reg);
pCmd->Action = htole16(flag);
pCmd->Value = htole32(*val);
retval = mwlExecuteCmd(mh, HostCmd_CMD_BBP_REG_ACCESS);
if (retval == 0)
*val = pCmd->Value;
MWL_HAL_UNLOCK(mh);
return retval;
}
static u_int
mwl_hal_getregdump(struct mwl_hal_priv *mh, const MWL_DIAG_REGRANGE *regs,
void *dstbuf, int space)
{
uint32_t *dp = dstbuf;
int i;
for (i = 0; space >= 2*sizeof(uint32_t); i++) {
u_int r = regs[i].start;
u_int e = regs[i].end;
*dp++ = (r<<16) | e;
space -= sizeof(uint32_t);
do {
if (MWL_DIAG_ISMAC(r))
*dp = RD4(mh, r);
else if (MWL_DIAG_ISBB(r))
getBBReg(mh, HostCmd_ACT_GEN_READ,
r - MWL_DIAG_BASE_BB, dp);
else if (MWL_DIAG_ISRF(r))
getRFReg(mh, HostCmd_ACT_GEN_READ,
r - MWL_DIAG_BASE_RF, dp);
else if (r < 0x1000 || r == MACREG_REG_FW_PRESENT)
*dp = RD4(mh, r);
else
*dp = 0xffffffff;
dp++;
r += sizeof(uint32_t);
space -= sizeof(uint32_t);
} while (r <= e && space >= sizeof(uint32_t));
}
return (char *) dp - (char *) dstbuf;
}
int
mwl_hal_getdiagstate(struct mwl_hal *mh0, int request,
const void *args, uint32_t argsize,
void **result, uint32_t *resultsize)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
switch (request) {
case MWL_DIAG_CMD_REVS:
*result = &mh->mh_revs;
*resultsize = sizeof(mh->mh_revs);
return 1;
case MWL_DIAG_CMD_REGS:
*resultsize = mwl_hal_getregdump(mh, args, *result, *resultsize);
return 1;
case MWL_DIAG_CMD_HOSTCMD: {
FWCmdHdr *pCmd = (FWCmdHdr *) &mh->mh_cmdbuf[0];
int retval;
MWL_HAL_LOCK(mh);
memcpy(pCmd, args, argsize);
retval = mwlExecuteCmd(mh, le16toh(pCmd->Cmd));
*result = (*resultsize != 0) ? pCmd : NULL;
MWL_HAL_UNLOCK(mh);
return (retval == 0);
}
case MWL_DIAG_CMD_FWLOAD:
if (mwl_hal_fwload(mh0, __DECONST(void *, args))) {
device_printf(mh->mh_dev, "problem loading fw image\n");
return 0;
}
return 1;
}
return 0;
}
/*
* Low level firmware cmd block handshake support.
*/
static void
mwlSendCmd(struct mwl_hal_priv *mh)
{
uint32_t dummy;
bus_dmamap_sync(mh->mh_dmat, mh->mh_dmamap,
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
WR4(mh, MACREG_REG_GEN_PTR, mh->mh_cmdaddr);
dummy = RD4(mh, MACREG_REG_INT_CODE);
WR4(mh, MACREG_REG_H2A_INTERRUPT_EVENTS, MACREG_H2ARIC_BIT_DOOR_BELL);
}
static int
mwlWaitForCmdComplete(struct mwl_hal_priv *mh, uint16_t cmdCode)
{
#define MAX_WAIT_FW_COMPLETE_ITERATIONS 10000
int i;
for (i = 0; i < MAX_WAIT_FW_COMPLETE_ITERATIONS; i++) {
if (mh->mh_cmdbuf[0] == le16toh(cmdCode))
return 1;
DELAY(1*1000);
}
return 0;
#undef MAX_WAIT_FW_COMPLETE_ITERATIONS
}
static int
mwlExecuteCmd(struct mwl_hal_priv *mh, unsigned short cmd)
{
MWL_HAL_LOCK_ASSERT(mh);
if ((mh->mh_flags & MHF_FWHANG) &&
(mh->mh_debug & MWL_HAL_DEBUG_IGNHANG) == 0) {
#ifdef MWLHAL_DEBUG
device_printf(mh->mh_dev, "firmware hung, skipping cmd %s\n",
mwlcmdname(cmd));
#else
device_printf(mh->mh_dev, "firmware hung, skipping cmd 0x%x\n",
cmd);
#endif
return ENXIO;
}
if (RD4(mh, MACREG_REG_INT_CODE) == 0xffffffff) {
device_printf(mh->mh_dev, "%s: device not present!\n",
__func__);
return EIO;
}
#ifdef MWLHAL_DEBUG
if (mh->mh_debug & MWL_HAL_DEBUG_SENDCMD)
dumpresult(mh, 0);
#endif
mwlSendCmd(mh);
if (!mwlWaitForCmdComplete(mh, 0x8000 | cmd)) {
#ifdef MWLHAL_DEBUG
device_printf(mh->mh_dev,
"timeout waiting for f/w cmd %s\n", mwlcmdname(cmd));
#else
device_printf(mh->mh_dev,
"timeout waiting for f/w cmd 0x%x\n", cmd);
#endif
mh->mh_flags |= MHF_FWHANG;
return ETIMEDOUT;
}
bus_dmamap_sync(mh->mh_dmat, mh->mh_dmamap,
BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
#ifdef MWLHAL_DEBUG
if (mh->mh_debug & MWL_HAL_DEBUG_CMDDONE)
dumpresult(mh, 1);
#endif
return 0;
}
/*
* Firmware download support.
*/
#define FW_DOWNLOAD_BLOCK_SIZE 256
#define FW_CHECK_USECS (5*1000) /* 5ms */
#define FW_MAX_NUM_CHECKS 200
#if 0
/* XXX read f/w from file */
#include <dev/mwl/mwlbootfw.h>
#include <dev/mwl/mwl88W8363fw.h>
#endif
static void
mwlFwReset(struct mwl_hal_priv *mh)
{
if (RD4(mh, MACREG_REG_INT_CODE) == 0xffffffff) {
device_printf(mh->mh_dev, "%s: device not present!\n",
__func__);
return;
}
WR4(mh, MACREG_REG_H2A_INTERRUPT_EVENTS, ISR_RESET);
mh->mh_flags &= ~MHF_FWHANG;
}
static void
mwlTriggerPciCmd(struct mwl_hal_priv *mh)
{
uint32_t dummy;
bus_dmamap_sync(mh->mh_dmat, mh->mh_dmamap, BUS_DMASYNC_PREWRITE);
WR4(mh, MACREG_REG_GEN_PTR, mh->mh_cmdaddr);
dummy = RD4(mh, MACREG_REG_INT_CODE);
WR4(mh, MACREG_REG_INT_CODE, 0x00);
dummy = RD4(mh, MACREG_REG_INT_CODE);
WR4(mh, MACREG_REG_H2A_INTERRUPT_EVENTS, MACREG_H2ARIC_BIT_DOOR_BELL);
dummy = RD4(mh, MACREG_REG_INT_CODE);
}
static int
mwlWaitFor(struct mwl_hal_priv *mh, uint32_t val)
{
int i;
for (i = 0; i < FW_MAX_NUM_CHECKS; i++) {
DELAY(FW_CHECK_USECS);
if (RD4(mh, MACREG_REG_INT_CODE) == val)
return 1;
}
return 0;
}
/*
* Firmware block xmit when talking to the boot-rom.
*/
static int
mwlSendBlock(struct mwl_hal_priv *mh, int bsize, const void *data, size_t dsize)
{
mh->mh_cmdbuf[0] = htole16(HostCmd_CMD_CODE_DNLD);
mh->mh_cmdbuf[1] = htole16(bsize);
memcpy(&mh->mh_cmdbuf[4], data , dsize);
mwlTriggerPciCmd(mh);
/* XXX 2000 vs 200 */
if (mwlWaitFor(mh, MACREG_INT_CODE_CMD_FINISHED)) {
WR4(mh, MACREG_REG_INT_CODE, 0);
return 1;
}
device_printf(mh->mh_dev,
"%s: timeout waiting for CMD_FINISHED, INT_CODE 0x%x\n",
__func__, RD4(mh, MACREG_REG_INT_CODE));
return 0;
}
/*
* Firmware block xmit when talking to the 1st-stage loader.
*/
static int
mwlSendBlock2(struct mwl_hal_priv *mh, const void *data, size_t dsize)
{
memcpy(&mh->mh_cmdbuf[0], data, dsize);
mwlTriggerPciCmd(mh);
if (mwlWaitFor(mh, MACREG_INT_CODE_CMD_FINISHED)) {
WR4(mh, MACREG_REG_INT_CODE, 0);
return 1;
}
device_printf(mh->mh_dev,
"%s: timeout waiting for CMD_FINISHED, INT_CODE 0x%x\n",
__func__, RD4(mh, MACREG_REG_INT_CODE));
return 0;
}
static void
mwlPokeSdramController(struct mwl_hal_priv *mh, int SDRAMSIZE_Addr)
{
/** Set up sdram controller for superflyv2 **/
WR4(mh, 0x00006014, 0x33);
WR4(mh, 0x00006018, 0xa3a2632);
WR4(mh, 0x00006010, SDRAMSIZE_Addr);
}
int
mwl_hal_fwload(struct mwl_hal *mh0, void *fwargs)
{
struct mwl_hal_priv *mh = MWLPRIV(mh0);
const char *fwname = "mw88W8363fw";
const char *fwbootname = "mwlboot";
const struct firmware *fwboot = NULL;
const struct firmware *fw;
/* XXX get from firmware header */
uint32_t FwReadySignature = HostCmd_SOFTAP_FWRDY_SIGNATURE;
uint32_t OpMode = HostCmd_SOFTAP_MODE;
const uint8_t *fp, *ep;
const uint8_t *fmdata;
uint32_t blocksize, nbytes, fmsize;
int i, error, ntries;
fw = firmware_get(fwname);
if (fw == NULL) {
device_printf(mh->mh_dev,
"could not load firmware image %s\n", fwname);
return ENXIO;
}
fmdata = fw->data;
fmsize = fw->datasize;
if (fmsize < 4) {
device_printf(mh->mh_dev, "firmware image %s too small\n",
fwname);
error = ENXIO;
goto bad2;
}
if (fmdata[0] == 0x01 && fmdata[1] == 0x00 &&
fmdata[2] == 0x00 && fmdata[3] == 0x00) {
/*
* 2-stage load, get the boot firmware.
*/
fwboot = firmware_get(fwbootname);
if (fwboot == NULL) {
device_printf(mh->mh_dev,
"could not load firmware image %s\n", fwbootname);
error = ENXIO;
goto bad2;
}
} else
fwboot = NULL;
mwlFwReset(mh);
WR4(mh, MACREG_REG_A2H_INTERRUPT_CLEAR_SEL, MACREG_A2HRIC_BIT_MASK);
WR4(mh, MACREG_REG_A2H_INTERRUPT_CAUSE, 0x00);
WR4(mh, MACREG_REG_A2H_INTERRUPT_MASK, 0x00);
WR4(mh, MACREG_REG_A2H_INTERRUPT_STATUS_MASK, MACREG_A2HRIC_BIT_MASK);
if (mh->mh_SDRAMSIZE_Addr != 0) {
/** Set up sdram controller for superflyv2 **/
mwlPokeSdramController(mh, mh->mh_SDRAMSIZE_Addr);
}
device_printf(mh->mh_dev, "load %s firmware image (%u bytes)\n",
fwname, fmsize);
if (fwboot != NULL) {
/*
* Do 2-stage load. The 1st stage loader is setup
* with the bootrom loader then we load the real
* image using a different handshake. With this
* mechanism the firmware is segmented into chunks
* that have a CRC. If a chunk is incorrect we'll
* be told to retransmit.
*/
/* XXX assumes hlpimage fits in a block */
/* NB: zero size block indicates download is finished */
if (!mwlSendBlock(mh, fwboot->datasize, fwboot->data, fwboot->datasize) ||
!mwlSendBlock(mh, 0, NULL, 0)) {
error = ETIMEDOUT;
goto bad;
}
DELAY(200*FW_CHECK_USECS);
if (mh->mh_SDRAMSIZE_Addr != 0) {
/** Set up sdram controller for superflyv2 **/
mwlPokeSdramController(mh, mh->mh_SDRAMSIZE_Addr);
}
nbytes = ntries = 0; /* NB: silence compiler */
for (fp = fmdata, ep = fp + fmsize; fp < ep; ) {
WR4(mh, MACREG_REG_INT_CODE, 0);
blocksize = RD4(mh, MACREG_REG_SCRATCH);
if (blocksize == 0) /* download complete */
break;
if (blocksize > 0x00000c00) {
error = EINVAL;
goto bad;
}
if ((blocksize & 0x1) == 0) {
/* block successfully downloaded, advance */
fp += nbytes;
ntries = 0;
} else {
if (++ntries > 2) {
/*
* Guard against f/w telling us to
* retry infinitely.
*/
error = ELOOP;
goto bad;
}
/* clear NAK bit/flag */
blocksize &= ~0x1;
}
if (blocksize > ep - fp) {
/* XXX this should not happen, what to do? */
blocksize = ep - fp;
}
nbytes = blocksize;
if (!mwlSendBlock2(mh, fp, nbytes)) {
error = ETIMEDOUT;
goto bad;
}
}
} else {
for (fp = fmdata, ep = fp + fmsize; fp < ep;) {
nbytes = ep - fp;
if (nbytes > FW_DOWNLOAD_BLOCK_SIZE)
nbytes = FW_DOWNLOAD_BLOCK_SIZE;
if (!mwlSendBlock(mh, FW_DOWNLOAD_BLOCK_SIZE, fp, nbytes)) {
error = EIO;
goto bad;
}
fp += nbytes;
}
}
/* done with firmware... */
if (fwboot != NULL)
firmware_put(fwboot, FIRMWARE_UNLOAD);
firmware_put(fw, FIRMWARE_UNLOAD);
/*
* Wait for firmware to startup; we monitor the
* INT_CODE register waiting for a signature to
* written back indicating it's ready to go.
*/
mh->mh_cmdbuf[1] = 0;
/*
* XXX WAR for mfg fw download
*/
if (OpMode != HostCmd_STA_MODE)
mwlTriggerPciCmd(mh);
for (i = 0; i < FW_MAX_NUM_CHECKS; i++) {
WR4(mh, MACREG_REG_GEN_PTR, OpMode);
DELAY(FW_CHECK_USECS);
if (RD4(mh, MACREG_REG_INT_CODE) == FwReadySignature) {
WR4(mh, MACREG_REG_INT_CODE, 0x00);
return mwlResetHalState(mh);
}
}
return ETIMEDOUT;
bad:
mwlFwReset(mh);
bad2:
/* done with firmware... */
if (fwboot != NULL)
firmware_put(fwboot, FIRMWARE_UNLOAD);
firmware_put(fw, FIRMWARE_UNLOAD);
return error;
}
#ifdef MWLHAL_DEBUG
static const char *
mwlcmdname(int cmd)
{
static char buf[12];
#define CMD(x) case HostCmd_CMD_##x: return #x
switch (cmd) {
CMD(CODE_DNLD);
CMD(GET_HW_SPEC);
CMD(SET_HW_SPEC);
CMD(MAC_MULTICAST_ADR);
CMD(802_11_GET_STAT);
CMD(MAC_REG_ACCESS);
CMD(BBP_REG_ACCESS);
CMD(RF_REG_ACCESS);
CMD(802_11_RADIO_CONTROL);
CMD(802_11_RF_TX_POWER);
CMD(802_11_RF_ANTENNA);
CMD(SET_BEACON);
CMD(SET_RF_CHANNEL);
CMD(SET_AID);
CMD(SET_INFRA_MODE);
CMD(SET_G_PROTECT_FLAG);
CMD(802_11_RTS_THSD);
CMD(802_11_SET_SLOT);
CMD(SET_EDCA_PARAMS);
CMD(802_11H_DETECT_RADAR);
CMD(SET_WMM_MODE);
CMD(HT_GUARD_INTERVAL);
CMD(SET_FIXED_RATE);
CMD(SET_LINKADAPT_CS_MODE);
CMD(SET_MAC_ADDR);
CMD(SET_RATE_ADAPT_MODE);
CMD(BSS_START);
CMD(SET_NEW_STN);
CMD(SET_KEEP_ALIVE);
CMD(SET_APMODE);
CMD(SET_SWITCH_CHANNEL);
CMD(UPDATE_ENCRYPTION);
CMD(BASTREAM);
CMD(SET_RIFS);
CMD(SET_N_PROTECT_FLAG);
CMD(SET_N_PROTECT_OPMODE);
CMD(SET_OPTIMIZATION_LEVEL);
CMD(GET_CALTABLE);
CMD(SET_MIMOPSHT);
CMD(GET_BEACON);
CMD(SET_REGION_CODE);
CMD(SET_POWERSAVESTATION);
CMD(SET_TIM);
CMD(GET_TIM);
CMD(GET_SEQNO);
CMD(DWDS_ENABLE);
CMD(AMPDU_RETRY_RATEDROP_MODE);
CMD(CFEND_ENABLE);
}
snprintf(buf, sizeof(buf), "0x%x", cmd);
return buf;
#undef CMD
}
static void
dumpresult(struct mwl_hal_priv *mh, int showresult)
{
const FWCmdHdr *h = (const FWCmdHdr *)mh->mh_cmdbuf;
const uint8_t *cp;
int len, i;
len = le16toh(h->Length);
#ifdef MWL_MBSS_SUPPORT
device_printf(mh->mh_dev, "Cmd %s Length %d SeqNum %d MacId %d",
mwlcmdname(le16toh(h->Cmd) &~ 0x8000), len, h->SeqNum, h->MacId);
#else
device_printf(mh->mh_dev, "Cmd %s Length %d SeqNum %d",
mwlcmdname(le16toh(h->Cmd) &~ 0x8000), len, le16toh(h->SeqNum));
#endif
if (showresult) {
const char *results[] =
{ "OK", "ERROR", "NOT_SUPPORT", "PENDING", "BUSY",
"PARTIAL_DATA" };
int result = le16toh(h->Result);
if (result <= HostCmd_RESULT_PARTIAL_DATA)
printf(" Result %s", results[result]);
else
printf(" Result %d", result);
}
cp = (const uint8_t *)h;
for (i = 0; i < len; i++) {
if ((i % 16) == 0)
printf("\n%02x", cp[i]);
else
printf(" %02x", cp[i]);
}
printf("\n");
}
#endif /* MWLHAL_DEBUG */