1193240Ssam/*- 2193240Ssam * Copyright (c) 2007-2009 Sam Leffler, Errno Consulting 3193240Ssam * Copyright (c) 2007-2009 Marvell Semiconductor, Inc. 4193240Ssam * All rights reserved. 5193240Ssam * 6193240Ssam * Redistribution and use in source and binary forms, with or without 7193240Ssam * modification, are permitted provided that the following conditions 8193240Ssam * are met: 9193240Ssam * 1. Redistributions of source code must retain the above copyright 10193240Ssam * notice, this list of conditions and the following disclaimer, 11193240Ssam * without modification. 12193240Ssam * 2. Redistributions in binary form must reproduce at minimum a disclaimer 13193240Ssam * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 14193240Ssam * redistribution must be conditioned upon including a substantially 15193240Ssam * similar Disclaimer requirement for further binary redistribution. 16193240Ssam * 17193240Ssam * NO WARRANTY 18193240Ssam * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19193240Ssam * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20193240Ssam * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 21193240Ssam * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 22193240Ssam * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 23193240Ssam * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24193240Ssam * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25193240Ssam * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 26193240Ssam * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27193240Ssam * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 28193240Ssam * THE POSSIBILITY OF SUCH DAMAGES. 29193240Ssam * 30193240Ssam * $FreeBSD$ 31193240Ssam */ 32193240Ssam 33193240Ssam#include <sys/param.h> 34193240Ssam#include <sys/systm.h> 35193240Ssam#include <sys/sysctl.h> 36193240Ssam#include <sys/malloc.h> 37193240Ssam#include <sys/lock.h> 38193240Ssam#include <sys/mutex.h> 39193240Ssam#include <sys/kernel.h> 40193240Ssam#include <sys/errno.h> 41193240Ssam#include <sys/bus.h> 42193240Ssam#include <sys/endian.h> 43193240Ssam 44193240Ssam#include <sys/linker.h> 45193240Ssam#include <sys/firmware.h> 46193240Ssam 47193240Ssam#include <machine/bus.h> 48193240Ssam 49193240Ssam#include <dev/mwl/mwlhal.h> 50193240Ssam#include <dev/mwl/mwlreg.h> 51193240Ssam 52193240Ssam#include <sys/socket.h> 53193240Ssam#include <sys/sockio.h> 54193240Ssam#include <net/if.h> 55193240Ssam#include <dev/mwl/mwldiag.h> 56193240Ssam 57193240Ssam#define MWLHAL_DEBUG /* debug msgs */ 58193240Ssam 59193240Ssamtypedef enum { 60193240Ssam WL_ANTENNAMODE_RX = 0xffff, 61193240Ssam WL_ANTENNAMODE_TX = 2, 62193240Ssam} wlantennamode_e; 63193240Ssam 64193240Ssamtypedef enum { 65193240Ssam WL_TX_POWERLEVEL_LOW = 5, 66193240Ssam WL_TX_POWERLEVEL_MEDIUM = 10, 67193240Ssam WL_TX_POWERLEVEL_HIGH = 15, 68193240Ssam} wltxpowerlevel_e; 69193240Ssam 70193240Ssam#define MWL_CMDBUF_SIZE 0x4000 /* size of f/w command buffer */ 71193240Ssam#define MWL_BASTREAMS_MAX 7 /* max BA streams (NB: fw >3.3.5.9) */ 72193240Ssam#define MWL_BAQID_MAX 8 /* max BA Q id's (NB: fw >3.3.5.9) */ 73193240Ssam#define MWL_MBSS_AP_MAX 8 /* max ap vap's */ 74193240Ssam#define MWL_MBSS_STA_MAX 24 /* max station/client vap's */ 75193240Ssam#define MWL_MBSS_MAX (MWL_MBSS_AP_MAX+MWL_MBSS_STA_MAX) 76193240Ssam 77193240Ssam/* 78193240Ssam * BA stream -> queue ID mapping 79193240Ssam * 80193240Ssam * The first 2 streams map to h/w; the remaining streams are 81193240Ssam * implemented in firmware. 82193240Ssam */ 83193240Ssamstatic const int ba2qid[MWL_BASTREAMS_MAX] = { 84193240Ssam 5, 6 /* h/w supported */ 85193240Ssam#if MWL_BASTREAMS_MAX == 7 86193240Ssam , 7, 0, 1, 2, 3 /* f/w supported */ 87193240Ssam#endif 88193240Ssam}; 89193240Ssamstatic int qid2ba[MWL_BAQID_MAX]; 90193240Ssam 91193240Ssam#define IEEE80211_ADDR_LEN 6 /* XXX */ 92193240Ssam#define IEEE80211_ADDR_COPY(_dst, _src) \ 93193240Ssam memcpy(_dst, _src, IEEE80211_ADDR_LEN) 94193240Ssam#define IEEE80211_ADDR_EQ(_dst, _src) \ 95193240Ssam (memcmp(_dst, _src, IEEE80211_ADDR_LEN) == 0) 96193240Ssam 97193240Ssam#define _CMD_SETUP(pCmd, type, cmd) do { \ 98193240Ssam pCmd = (type *)&mh->mh_cmdbuf[0]; \ 99193240Ssam memset(pCmd, 0, sizeof(type)); \ 100193240Ssam pCmd->CmdHdr.Cmd = htole16(cmd); \ 101193240Ssam pCmd->CmdHdr.Length = htole16(sizeof(type)); \ 102193240Ssam} while (0) 103193240Ssam 104193240Ssam#define _VCMD_SETUP(vap, pCmd, type, cmd) do { \ 105193240Ssam _CMD_SETUP(pCmd, type, cmd); \ 106193240Ssam pCmd->CmdHdr.MacId = vap->macid; \ 107193240Ssam} while (0) 108193240Ssam 109193240Ssam#define PWTAGETRATETABLE20M 14*4 110193240Ssam#define PWTAGETRATETABLE40M 9*4 111193240Ssam#define PWTAGETRATETABLE20M_5G 35*4 112193240Ssam#define PWTAGETRATETABLE40M_5G 16*4 113193240Ssam 114193240Ssamstruct mwl_hal_bastream { 115193240Ssam MWL_HAL_BASTREAM public; /* public state */ 116193240Ssam uint8_t stream; /* stream # */ 117193240Ssam uint8_t setup; /* f/w cmd sent */ 118195171Ssam uint8_t ba_policy; /* direct/delayed BA policy */ 119193240Ssam uint8_t tid; 120193240Ssam uint8_t paraminfo; 121193240Ssam uint8_t macaddr[IEEE80211_ADDR_LEN]; 122193240Ssam}; 123193240Ssam 124193240Ssamstruct mwl_hal_priv; 125193240Ssam 126193240Ssamstruct mwl_hal_vap { 127193240Ssam struct mwl_hal_priv *mh; /* back pointer */ 128193240Ssam uint16_t bss_type; /* f/w type */ 129193240Ssam uint8_t vap_type; /* MWL_HAL_BSSTYPE */ 130193240Ssam uint8_t macid; /* for passing to f/w */ 131193240Ssam uint8_t flags; 132193240Ssam#define MVF_RUNNING 0x01 /* BSS_START issued */ 133193240Ssam#define MVF_STATION 0x02 /* sta db entry created */ 134193240Ssam uint8_t mac[IEEE80211_ADDR_LEN];/* mac address */ 135193240Ssam}; 136193240Ssam#define MWLVAP(_vap) ((_vap)->mh) 137193240Ssam 138193240Ssam/* 139193240Ssam * Per-device state. We allocate a single cmd buffer for 140193240Ssam * submitting operations to the firmware. Access to this 141193240Ssam * buffer (and the f/w) are single-threaded. At present 142193240Ssam * we spin waiting for cmds to complete which is bad. Not 143193240Ssam * sure if it's possible to submit multiple requests or 144193240Ssam * control when we get cmd done interrupts. There's no 145193240Ssam * documentation and no example code to indicate what can 146193240Ssam * or cannot be done so all we can do right now is follow the 147193240Ssam * linux driver logic. This falls apart when the f/w fails; 148193240Ssam * the system comes to a crawl as we spin waiting for operations 149193240Ssam * to finish. 150193240Ssam */ 151193240Ssamstruct mwl_hal_priv { 152193240Ssam struct mwl_hal public; /* public area */ 153193240Ssam device_t mh_dev; 154193240Ssam char mh_mtxname[12]; 155193240Ssam struct mtx mh_mtx; 156193240Ssam bus_dma_tag_t mh_dmat; /* bus DMA tag for cmd buffer */ 157193240Ssam bus_dma_segment_t mh_seg; /* segment for cmd buffer */ 158193240Ssam bus_dmamap_t mh_dmamap; /* DMA map for cmd buffer */ 159193240Ssam uint16_t *mh_cmdbuf; /* f/w cmd buffer */ 160193240Ssam bus_addr_t mh_cmdaddr; /* physaddr of cmd buffer */ 161193240Ssam int mh_flags; 162193240Ssam#define MHF_CALDATA 0x0001 /* cal data retrieved */ 163193240Ssam#define MHF_FWHANG 0x0002 /* fw appears hung */ 164193240Ssam#define MHF_MBSS 0x0004 /* mbss enabled */ 165193240Ssam struct mwl_hal_vap mh_vaps[MWL_MBSS_MAX+1]; 166193240Ssam int mh_bastreams; /* bit mask of available BA streams */ 167193240Ssam int mh_regioncode; /* XXX last region code sent to fw */ 168193240Ssam struct mwl_hal_bastream mh_streams[MWL_BASTREAMS_MAX]; 169193240Ssam int mh_debug; 170193240Ssam MWL_HAL_CHANNELINFO mh_20M; 171193240Ssam MWL_HAL_CHANNELINFO mh_40M; 172193240Ssam MWL_HAL_CHANNELINFO mh_20M_5G; 173193240Ssam MWL_HAL_CHANNELINFO mh_40M_5G; 174193240Ssam int mh_SDRAMSIZE_Addr; 175193240Ssam uint32_t mh_RTSSuccesses;/* cumulative stats for read-on-clear */ 176193240Ssam uint32_t mh_RTSFailures; 177193240Ssam uint32_t mh_RxDuplicateFrames; 178193240Ssam uint32_t mh_FCSErrorCount; 179193240Ssam MWL_DIAG_REVS mh_revs; 180193240Ssam}; 181193240Ssam#define MWLPRIV(_mh) ((struct mwl_hal_priv *)(_mh)) 182193240Ssam 183193240Ssamstatic int mwl_hal_setmac_locked(struct mwl_hal_vap *, 184193240Ssam const uint8_t addr[IEEE80211_ADDR_LEN]); 185193240Ssamstatic int mwlExecuteCmd(struct mwl_hal_priv *, unsigned short cmd); 186193240Ssamstatic int mwlGetPwrCalTable(struct mwl_hal_priv *); 187193240Ssam#ifdef MWLHAL_DEBUG 188193240Ssamstatic const char *mwlcmdname(int cmd); 189193240Ssamstatic void dumpresult(struct mwl_hal_priv *, int showresult); 190193240Ssam#endif /* MWLHAL_DEBUG */ 191193240Ssam 192193240SsamSYSCTL_DECL(_hw_mwl); 193227309Sedstatic SYSCTL_NODE(_hw_mwl, OID_AUTO, hal, CTLFLAG_RD, 0, 194227309Sed "Marvell HAL parameters"); 195193240Ssam 196193240Ssamstatic __inline void 197193240SsamMWL_HAL_LOCK(struct mwl_hal_priv *mh) 198193240Ssam{ 199193240Ssam mtx_lock(&mh->mh_mtx); 200193240Ssam} 201193240Ssam 202193240Ssamstatic __inline void 203193240SsamMWL_HAL_LOCK_ASSERT(struct mwl_hal_priv *mh) 204193240Ssam{ 205193240Ssam mtx_assert(&mh->mh_mtx, MA_OWNED); 206193240Ssam} 207193240Ssam 208193240Ssamstatic __inline void 209193240SsamMWL_HAL_UNLOCK(struct mwl_hal_priv *mh) 210193240Ssam{ 211193240Ssam mtx_unlock(&mh->mh_mtx); 212193240Ssam} 213193240Ssam 214193240Ssamstatic __inline uint32_t 215193240SsamRD4(struct mwl_hal_priv *mh, bus_size_t off) 216193240Ssam{ 217193240Ssam return bus_space_read_4(mh->public.mh_iot, mh->public.mh_ioh, off); 218193240Ssam} 219193240Ssam 220193240Ssamstatic __inline void 221193240SsamWR4(struct mwl_hal_priv *mh, bus_size_t off, uint32_t val) 222193240Ssam{ 223193240Ssam bus_space_write_4(mh->public.mh_iot, mh->public.mh_ioh, off, val); 224193240Ssam} 225193240Ssam 226193240Ssamstatic void 227193240Ssammwl_hal_load_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 228193240Ssam{ 229193240Ssam bus_addr_t *paddr = (bus_addr_t*) arg; 230193240Ssam KASSERT(error == 0, ("error %u on bus_dma callback", error)); 231193240Ssam *paddr = segs->ds_addr; 232193240Ssam} 233193240Ssam 234193240Ssam/* 235193240Ssam * Setup for communication with the device. We allocate 236193240Ssam * a command buffer and map it for bus dma use. The pci 237193240Ssam * device id is used to identify whether the device has 238193240Ssam * SRAM on it (in which case f/w download must include a 239193240Ssam * memory controller reset). All bus i/o operations happen 240193240Ssam * in BAR 1; the driver passes in the tag and handle we need. 241193240Ssam */ 242193240Ssamstruct mwl_hal * 243193240Ssammwl_hal_attach(device_t dev, uint16_t devid, 244193240Ssam bus_space_handle_t ioh, bus_space_tag_t iot, bus_dma_tag_t tag) 245193240Ssam{ 246193240Ssam struct mwl_hal_priv *mh; 247193240Ssam struct mwl_hal_vap *hvap; 248193240Ssam int error, i; 249193240Ssam 250193240Ssam mh = malloc(sizeof(struct mwl_hal_priv), M_DEVBUF, M_NOWAIT | M_ZERO); 251193240Ssam if (mh == NULL) 252193240Ssam return NULL; 253193240Ssam mh->mh_dev = dev; 254193240Ssam mh->public.mh_ioh = ioh; 255193240Ssam mh->public.mh_iot = iot; 256193240Ssam for (i = 0; i < MWL_BASTREAMS_MAX; i++) { 257193240Ssam mh->mh_streams[i].public.txq = ba2qid[i]; 258193240Ssam mh->mh_streams[i].stream = i; 259193240Ssam /* construct back-mapping while we're at it */ 260193240Ssam if (mh->mh_streams[i].public.txq < MWL_BAQID_MAX) 261193240Ssam qid2ba[mh->mh_streams[i].public.txq] = i; 262193240Ssam else 263193240Ssam device_printf(dev, "unexpected BA tx qid %d for " 264193240Ssam "stream %d\n", mh->mh_streams[i].public.txq, i); 265193240Ssam } 266193240Ssam /* setup constant portion of vap state */ 267193240Ssam /* XXX should get max ap/client vap's from f/w */ 268193240Ssam i = 0; 269193240Ssam hvap = &mh->mh_vaps[i]; 270193240Ssam hvap->vap_type = MWL_HAL_AP; 271193240Ssam hvap->bss_type = htole16(WL_MAC_TYPE_PRIMARY_AP); 272193240Ssam hvap->macid = 0; 273193240Ssam for (i++; i < MWL_MBSS_AP_MAX; i++) { 274193240Ssam hvap = &mh->mh_vaps[i]; 275193240Ssam hvap->vap_type = MWL_HAL_AP; 276193240Ssam hvap->bss_type = htole16(WL_MAC_TYPE_SECONDARY_AP); 277193240Ssam hvap->macid = i; 278193240Ssam } 279193240Ssam hvap = &mh->mh_vaps[i]; 280193240Ssam hvap->vap_type = MWL_HAL_STA; 281193240Ssam hvap->bss_type = htole16(WL_MAC_TYPE_PRIMARY_CLIENT); 282193240Ssam hvap->macid = i; 283216835Sbschmidt for (i++; i < MWL_MBSS_MAX; i++) { 284193240Ssam hvap = &mh->mh_vaps[i]; 285193240Ssam hvap->vap_type = MWL_HAL_STA; 286193240Ssam hvap->bss_type = htole16(WL_MAC_TYPE_SECONDARY_CLIENT); 287193240Ssam hvap->macid = i; 288193240Ssam } 289193240Ssam mh->mh_revs.mh_devid = devid; 290193240Ssam snprintf(mh->mh_mtxname, sizeof(mh->mh_mtxname), 291193240Ssam "%s_hal", device_get_nameunit(dev)); 292193240Ssam mtx_init(&mh->mh_mtx, mh->mh_mtxname, NULL, MTX_DEF); 293193240Ssam 294193240Ssam /* 295193240Ssam * Allocate the command buffer and map into the address 296193240Ssam * space of the h/w. We request "coherent" memory which 297193240Ssam * will be uncached on some architectures. 298193240Ssam */ 299193240Ssam error = bus_dma_tag_create(tag, /* parent */ 300193240Ssam PAGE_SIZE, 0, /* alignment, bounds */ 301193240Ssam BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 302193240Ssam BUS_SPACE_MAXADDR, /* highaddr */ 303193240Ssam NULL, NULL, /* filter, filterarg */ 304193240Ssam MWL_CMDBUF_SIZE, /* maxsize */ 305193240Ssam 1, /* nsegments */ 306193240Ssam MWL_CMDBUF_SIZE, /* maxsegsize */ 307193240Ssam BUS_DMA_ALLOCNOW, /* flags */ 308193240Ssam NULL, /* lockfunc */ 309193240Ssam NULL, /* lockarg */ 310193240Ssam &mh->mh_dmat); 311193240Ssam if (error != 0) { 312193240Ssam device_printf(dev, "unable to allocate memory for cmd buffer, " 313193240Ssam "error %u\n", error); 314193240Ssam goto fail0; 315193240Ssam } 316193240Ssam 317193240Ssam /* allocate descriptors */ 318193240Ssam error = bus_dmamap_create(mh->mh_dmat, BUS_DMA_NOWAIT, &mh->mh_dmamap); 319193240Ssam if (error != 0) { 320193240Ssam device_printf(dev, "unable to create dmamap for cmd buffers, " 321193240Ssam "error %u\n", error); 322193240Ssam goto fail0; 323193240Ssam } 324193240Ssam 325193240Ssam error = bus_dmamem_alloc(mh->mh_dmat, (void**) &mh->mh_cmdbuf, 326193240Ssam BUS_DMA_NOWAIT | BUS_DMA_COHERENT, 327193240Ssam &mh->mh_dmamap); 328193240Ssam if (error != 0) { 329193240Ssam device_printf(dev, "unable to allocate memory for cmd buffer, " 330193240Ssam "error %u\n", error); 331193240Ssam goto fail1; 332193240Ssam } 333193240Ssam 334193240Ssam error = bus_dmamap_load(mh->mh_dmat, mh->mh_dmamap, 335193240Ssam mh->mh_cmdbuf, MWL_CMDBUF_SIZE, 336193240Ssam mwl_hal_load_cb, &mh->mh_cmdaddr, 337193240Ssam BUS_DMA_NOWAIT); 338193240Ssam if (error != 0) { 339193240Ssam device_printf(dev, "unable to load cmd buffer, error %u\n", 340193240Ssam error); 341193240Ssam goto fail2; 342193240Ssam } 343193240Ssam 344193240Ssam /* 345193240Ssam * Some cards have SDRAM. When loading firmware we need 346193240Ssam * to reset the SDRAM controller prior to doing this. 347193240Ssam * When the SDRAMSIZE is non-zero we do that work in 348193240Ssam * mwl_hal_fwload. 349193240Ssam */ 350193240Ssam switch (devid) { 351193240Ssam case 0x2a02: /* CB82 */ 352193240Ssam case 0x2a03: /* CB85 */ 353193240Ssam case 0x2a08: /* MC85_B1 */ 354193240Ssam case 0x2a0b: /* CB85AP */ 355193240Ssam case 0x2a24: 356193240Ssam mh->mh_SDRAMSIZE_Addr = 0x40fe70b7; /* 8M SDRAM */ 357193240Ssam break; 358193240Ssam case 0x2a04: /* MC85 */ 359193240Ssam mh->mh_SDRAMSIZE_Addr = 0x40fc70b7; /* 16M SDRAM */ 360193240Ssam break; 361193240Ssam default: 362193240Ssam break; 363193240Ssam } 364193240Ssam return &mh->public; 365193240Ssamfail2: 366193240Ssam bus_dmamem_free(mh->mh_dmat, mh->mh_cmdbuf, mh->mh_dmamap); 367193240Ssamfail1: 368193240Ssam bus_dmamap_destroy(mh->mh_dmat, mh->mh_dmamap); 369193240Ssamfail0: 370193240Ssam bus_dma_tag_destroy(mh->mh_dmat); 371193240Ssam mtx_destroy(&mh->mh_mtx); 372193240Ssam free(mh, M_DEVBUF); 373193240Ssam return NULL; 374193240Ssam} 375193240Ssam 376193240Ssamvoid 377193240Ssammwl_hal_detach(struct mwl_hal *mh0) 378193240Ssam{ 379193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 380193240Ssam 381193240Ssam bus_dmamem_free(mh->mh_dmat, mh->mh_cmdbuf, mh->mh_dmamap); 382193240Ssam bus_dmamap_destroy(mh->mh_dmat, mh->mh_dmamap); 383193240Ssam bus_dma_tag_destroy(mh->mh_dmat); 384193240Ssam mtx_destroy(&mh->mh_mtx); 385193240Ssam free(mh, M_DEVBUF); 386193240Ssam} 387193240Ssam 388193240Ssam/* 389193240Ssam * Reset internal state after a firmware download. 390193240Ssam */ 391193240Ssamstatic int 392193240SsammwlResetHalState(struct mwl_hal_priv *mh) 393193240Ssam{ 394193240Ssam int i; 395193240Ssam 396193240Ssam /* XXX get from f/w */ 397193240Ssam mh->mh_bastreams = (1<<MWL_BASTREAMS_MAX)-1; 398193240Ssam for (i = 0; i < MWL_MBSS_MAX; i++) 399193240Ssam mh->mh_vaps[i].mh = NULL; 400193240Ssam /* 401193240Ssam * Clear cumulative stats. 402193240Ssam */ 403193240Ssam mh->mh_RTSSuccesses = 0; 404193240Ssam mh->mh_RTSFailures = 0; 405193240Ssam mh->mh_RxDuplicateFrames = 0; 406193240Ssam mh->mh_FCSErrorCount = 0; 407193240Ssam /* 408193240Ssam * Fetch cal data for later use. 409193240Ssam * XXX may want to fetch other stuff too. 410193240Ssam */ 411193240Ssam /* XXX check return */ 412193240Ssam if ((mh->mh_flags & MHF_CALDATA) == 0) 413193240Ssam mwlGetPwrCalTable(mh); 414193240Ssam return 0; 415193240Ssam} 416193240Ssam 417193240Ssamstruct mwl_hal_vap * 418193240Ssammwl_hal_newvap(struct mwl_hal *mh0, MWL_HAL_BSSTYPE type, 419193240Ssam const uint8_t mac[IEEE80211_ADDR_LEN]) 420193240Ssam{ 421193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 422193240Ssam struct mwl_hal_vap *vap; 423193240Ssam int i; 424193240Ssam 425193240Ssam MWL_HAL_LOCK(mh); 426193240Ssam /* NB: could optimize but not worth it w/ max 32 bss */ 427193240Ssam for (i = 0; i < MWL_MBSS_MAX; i++) { 428193240Ssam vap = &mh->mh_vaps[i]; 429193240Ssam if (vap->vap_type == type && vap->mh == NULL) { 430193240Ssam vap->mh = mh; 431193240Ssam mwl_hal_setmac_locked(vap, mac); 432193240Ssam break; 433193240Ssam } 434193240Ssam } 435193240Ssam MWL_HAL_UNLOCK(mh); 436193240Ssam return (i < MWL_MBSS_MAX) ? vap : NULL; 437193240Ssam} 438193240Ssam 439193240Ssamvoid 440193240Ssammwl_hal_delvap(struct mwl_hal_vap *vap) 441193240Ssam{ 442193240Ssam /* NB: locking not needed for single write */ 443193240Ssam vap->mh = NULL; 444193240Ssam} 445193240Ssam 446193240Ssam/* 447193240Ssam * Manipulate the debug mask. Note debug 448193240Ssam * msgs are only provided when this code is 449193240Ssam * compiled with MWLHAL_DEBUG defined. 450193240Ssam */ 451193240Ssam 452193240Ssamvoid 453193240Ssammwl_hal_setdebug(struct mwl_hal *mh, int debug) 454193240Ssam{ 455193240Ssam MWLPRIV(mh)->mh_debug = debug; 456193240Ssam} 457193240Ssam 458193240Ssamint 459193240Ssammwl_hal_getdebug(struct mwl_hal *mh) 460193240Ssam{ 461193240Ssam return MWLPRIV(mh)->mh_debug; 462193240Ssam} 463193240Ssam 464193240Ssamvoid 465193240Ssammwl_hal_setbastreams(struct mwl_hal *mh, int mask) 466193240Ssam{ 467193240Ssam MWLPRIV(mh)->mh_bastreams = mask & ((1<<MWL_BASTREAMS_MAX)-1); 468193240Ssam} 469193240Ssam 470193240Ssamint 471193240Ssammwl_hal_getbastreams(struct mwl_hal *mh) 472193240Ssam{ 473193240Ssam return MWLPRIV(mh)->mh_bastreams; 474193240Ssam} 475193240Ssam 476193240Ssamint 477193240Ssammwl_hal_ismbsscapable(struct mwl_hal *mh) 478193240Ssam{ 479193240Ssam return (MWLPRIV(mh)->mh_flags & MHF_MBSS) != 0; 480193240Ssam} 481193240Ssam 482193240Ssam#if 0 483193240Ssam/* XXX inlined */ 484193240Ssam/* 485193240Ssam * Return the current ISR setting and clear the cause. 486193240Ssam * XXX maybe make inline 487193240Ssam */ 488193240Ssamvoid 489193240Ssammwl_hal_getisr(struct mwl_hal *mh0, uint32_t *status) 490193240Ssam{ 491193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 492193240Ssam uint32_t cause; 493193240Ssam 494193240Ssam cause = RD4(mh, MACREG_REG_A2H_INTERRUPT_CAUSE); 495193240Ssam if (cause == 0xffffffff) { /* card removed */ 496193240Ssamdevice_printf(mh->mh_dev, "%s: cause 0x%x\n", __func__, cause); 497193240Ssam cause = 0; 498193240Ssam } else if (cause != 0) { 499193240Ssam /* clear cause bits */ 500193240Ssam WR4(mh, MACREG_REG_A2H_INTERRUPT_CAUSE, 501193240Ssam cause &~ mh->public.mh_imask); 502193240Ssam RD4(mh, MACREG_REG_INT_CODE); /* XXX flush write? */ 503193240Ssam } 504193240Ssam *status = cause; 505193240Ssam} 506193240Ssam#endif 507193240Ssam 508193240Ssam/* 509193240Ssam * Set the interrupt mask. 510193240Ssam */ 511193240Ssamvoid 512193240Ssammwl_hal_intrset(struct mwl_hal *mh0, uint32_t mask) 513193240Ssam{ 514193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 515193240Ssam 516193240Ssam WR4(mh, MACREG_REG_A2H_INTERRUPT_MASK, 0); 517193240Ssam RD4(mh, MACREG_REG_INT_CODE); 518193240Ssam 519193240Ssam mh->public.mh_imask = mask; 520193240Ssam WR4(mh, MACREG_REG_A2H_INTERRUPT_MASK, mask); 521193240Ssam RD4(mh, MACREG_REG_INT_CODE); 522193240Ssam} 523193240Ssam 524193240Ssam#if 0 525193240Ssam/* XXX inlined */ 526193240Ssam/* 527193240Ssam * Kick the firmware to tell it there are new tx descriptors 528193240Ssam * for processing. The driver says what h/w q has work in 529193240Ssam * case the f/w ever gets smarter. 530193240Ssam */ 531193240Ssamvoid 532193240Ssammwl_hal_txstart(struct mwl_hal *mh0, int qnum) 533193240Ssam{ 534193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 535193240Ssam uint32_t dummy; 536193240Ssam 537193240Ssam WR4(mh, MACREG_REG_H2A_INTERRUPT_EVENTS, MACREG_H2ARIC_BIT_PPA_READY); 538193240Ssam dummy = RD4(mh, MACREG_REG_INT_CODE); 539193240Ssam} 540193240Ssam#endif 541193240Ssam 542193240Ssam/* 543193240Ssam * Callback from the driver on a cmd done interrupt. 544193240Ssam * Nothing to do right now as we spin waiting for 545193240Ssam * cmd completion. 546193240Ssam */ 547193240Ssamvoid 548193240Ssammwl_hal_cmddone(struct mwl_hal *mh0) 549193240Ssam{ 550193240Ssam#if 0 551193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 552193240Ssam 553193240Ssam if (mh->mh_debug & MWL_HAL_DEBUG_CMDDONE) { 554193240Ssam device_printf(mh->mh_dev, "cmd done interrupt:\n"); 555193240Ssam dumpresult(mh, 1); 556193240Ssam } 557193240Ssam#endif 558193240Ssam} 559193240Ssam 560193240Ssam/* 561193240Ssam * Return "hw specs". Note this must be the first 562193240Ssam * cmd MUST be done after a firmware download or the 563193240Ssam * f/w will lockup. 564193240Ssam * XXX move into the hal so driver doesn't need to be responsible 565193240Ssam */ 566193240Ssamint 567193240Ssammwl_hal_gethwspecs(struct mwl_hal *mh0, struct mwl_hal_hwspec *hw) 568193240Ssam{ 569193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 570193240Ssam HostCmd_DS_GET_HW_SPEC *pCmd; 571193240Ssam int retval, minrev; 572193240Ssam 573193240Ssam MWL_HAL_LOCK(mh); 574193240Ssam _CMD_SETUP(pCmd, HostCmd_DS_GET_HW_SPEC, HostCmd_CMD_GET_HW_SPEC); 575193240Ssam memset(&pCmd->PermanentAddr[0], 0xff, IEEE80211_ADDR_LEN); 576193240Ssam pCmd->ulFwAwakeCookie = htole32((unsigned int)mh->mh_cmdaddr+2048); 577193240Ssam 578193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_GET_HW_SPEC); 579193240Ssam if (retval == 0) { 580193240Ssam IEEE80211_ADDR_COPY(hw->macAddr, pCmd->PermanentAddr); 581193240Ssam hw->wcbBase[0] = le32toh(pCmd->WcbBase0) & 0x0000ffff; 582195171Ssam hw->wcbBase[1] = le32toh(pCmd->WcbBase1[0]) & 0x0000ffff; 583195171Ssam hw->wcbBase[2] = le32toh(pCmd->WcbBase1[1]) & 0x0000ffff; 584195171Ssam hw->wcbBase[3] = le32toh(pCmd->WcbBase1[2]) & 0x0000ffff; 585193240Ssam hw->rxDescRead = le32toh(pCmd->RxPdRdPtr)& 0x0000ffff; 586193240Ssam hw->rxDescWrite = le32toh(pCmd->RxPdWrPtr)& 0x0000ffff; 587193240Ssam hw->regionCode = le16toh(pCmd->RegionCode) & 0x00ff; 588193240Ssam hw->fwReleaseNumber = le32toh(pCmd->FWReleaseNumber); 589193240Ssam hw->maxNumWCB = le16toh(pCmd->NumOfWCB); 590193240Ssam hw->maxNumMCAddr = le16toh(pCmd->NumOfMCastAddr); 591193240Ssam hw->numAntennas = le16toh(pCmd->NumberOfAntenna); 592193240Ssam hw->hwVersion = pCmd->Version; 593193240Ssam hw->hostInterface = pCmd->HostIf; 594193240Ssam 595193240Ssam mh->mh_revs.mh_macRev = hw->hwVersion; /* XXX */ 596193240Ssam mh->mh_revs.mh_phyRev = hw->hostInterface; /* XXX */ 597193240Ssam 598193240Ssam minrev = ((hw->fwReleaseNumber) >> 16) & 0xff; 599193240Ssam if (minrev >= 4) { 600193240Ssam /* starting with 3.4.x.x s/w BA streams supported */ 601193240Ssam mh->mh_bastreams &= (1<<MWL_BASTREAMS_MAX)-1; 602193240Ssam } else 603193240Ssam mh->mh_bastreams &= (1<<2)-1; 604193240Ssam } 605193240Ssam MWL_HAL_UNLOCK(mh); 606193240Ssam return retval; 607193240Ssam} 608193240Ssam 609193240Ssam/* 610193240Ssam * Inform the f/w about location of the tx/rx dma data structures 611193240Ssam * and related state. This cmd must be done immediately after a 612193240Ssam * mwl_hal_gethwspecs call or the f/w will lockup. 613193240Ssam */ 614193240Ssamint 615193240Ssammwl_hal_sethwdma(struct mwl_hal *mh0, const struct mwl_hal_txrxdma *dma) 616193240Ssam{ 617193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 618193240Ssam HostCmd_DS_SET_HW_SPEC *pCmd; 619193240Ssam int retval; 620193240Ssam 621193240Ssam MWL_HAL_LOCK(mh); 622193240Ssam _CMD_SETUP(pCmd, HostCmd_DS_SET_HW_SPEC, HostCmd_CMD_SET_HW_SPEC); 623193240Ssam pCmd->WcbBase[0] = htole32(dma->wcbBase[0]); 624193240Ssam pCmd->WcbBase[1] = htole32(dma->wcbBase[1]); 625193240Ssam pCmd->WcbBase[2] = htole32(dma->wcbBase[2]); 626193240Ssam pCmd->WcbBase[3] = htole32(dma->wcbBase[3]); 627193240Ssam pCmd->TxWcbNumPerQueue = htole32(dma->maxNumTxWcb); 628193240Ssam pCmd->NumTxQueues = htole32(dma->maxNumWCB); 629193240Ssam pCmd->TotalRxWcb = htole32(1); /* XXX */ 630193240Ssam pCmd->RxPdWrPtr = htole32(dma->rxDescRead); 631193240Ssam pCmd->Flags = htole32(SET_HW_SPEC_HOSTFORM_BEACON 632193240Ssam#ifdef MWL_HOST_PS_SUPPORT 633193240Ssam | SET_HW_SPEC_HOST_POWERSAVE 634193240Ssam#endif 635193240Ssam | SET_HW_SPEC_HOSTFORM_PROBERESP); 636193240Ssam /* disable multi-bss operation for A1-A4 parts */ 637193240Ssam if (mh->mh_revs.mh_macRev < 5) 638193240Ssam pCmd->Flags |= htole32(SET_HW_SPEC_DISABLEMBSS); 639193240Ssam 640193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_HW_SPEC); 641193240Ssam if (retval == 0) { 642193240Ssam if (pCmd->Flags & htole32(SET_HW_SPEC_DISABLEMBSS)) 643193240Ssam mh->mh_flags &= ~MHF_MBSS; 644193240Ssam else 645193240Ssam mh->mh_flags |= MHF_MBSS; 646193240Ssam } 647193240Ssam MWL_HAL_UNLOCK(mh); 648193240Ssam return retval; 649193240Ssam} 650193240Ssam 651193240Ssam/* 652193240Ssam * Retrieve statistics from the f/w. 653193240Ssam * XXX should be in memory shared w/ driver 654193240Ssam */ 655193240Ssamint 656193240Ssammwl_hal_gethwstats(struct mwl_hal *mh0, struct mwl_hal_hwstats *stats) 657193240Ssam{ 658193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 659193240Ssam HostCmd_DS_802_11_GET_STAT *pCmd; 660193240Ssam int retval; 661193240Ssam 662193240Ssam MWL_HAL_LOCK(mh); 663193240Ssam _CMD_SETUP(pCmd, HostCmd_DS_802_11_GET_STAT, 664193240Ssam HostCmd_CMD_802_11_GET_STAT); 665193240Ssam 666193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_802_11_GET_STAT); 667193240Ssam if (retval == 0) { 668193240Ssam const uint32_t *sp = (const uint32_t *)&pCmd->TxRetrySuccesses; 669193240Ssam uint32_t *dp = (uint32_t *)&stats->TxRetrySuccesses; 670193240Ssam int i; 671193240Ssam 672193240Ssam for (i = 0; i < sizeof(*stats)/sizeof(uint32_t); i++) 673193240Ssam dp[i] = le32toh(sp[i]); 674193240Ssam /* 675193240Ssam * Update stats not returned by f/w but available 676193240Ssam * through public registers. Note these registers 677193240Ssam * are "clear on read" so we maintain cumulative data. 678193240Ssam * XXX register defines 679193240Ssam */ 680193240Ssam mh->mh_RTSSuccesses += RD4(mh, 0xa834); 681193240Ssam mh->mh_RTSFailures += RD4(mh, 0xa830); 682193240Ssam mh->mh_RxDuplicateFrames += RD4(mh, 0xa84c); 683193240Ssam mh->mh_FCSErrorCount += RD4(mh, 0xa840); 684193240Ssam } 685193240Ssam MWL_HAL_UNLOCK(mh); 686193240Ssam 687193240Ssam stats->RTSSuccesses = mh->mh_RTSSuccesses; 688193240Ssam stats->RTSFailures = mh->mh_RTSFailures; 689193240Ssam stats->RxDuplicateFrames = mh->mh_RxDuplicateFrames; 690193240Ssam stats->FCSErrorCount = mh->mh_FCSErrorCount; 691193240Ssam return retval; 692193240Ssam} 693193240Ssam 694193240Ssam/* 695193240Ssam * Set HT guard interval handling. 696193240Ssam * Takes effect immediately. 697193240Ssam */ 698193240Ssamint 699193240Ssammwl_hal_sethtgi(struct mwl_hal_vap *vap, int GIType) 700193240Ssam{ 701193240Ssam struct mwl_hal_priv *mh = MWLVAP(vap); 702193240Ssam HostCmd_FW_HT_GUARD_INTERVAL *pCmd; 703193240Ssam int retval; 704193240Ssam 705193240Ssam MWL_HAL_LOCK(mh); 706193240Ssam _VCMD_SETUP(vap, pCmd, HostCmd_FW_HT_GUARD_INTERVAL, 707193240Ssam HostCmd_CMD_HT_GUARD_INTERVAL); 708193240Ssam pCmd->Action = htole32(HostCmd_ACT_GEN_SET); 709193240Ssam 710193240Ssam if (GIType == 0) { 711193240Ssam pCmd->GIType = htole32(GI_TYPE_LONG); 712193240Ssam } else if (GIType == 1) { 713193240Ssam pCmd->GIType = htole32(GI_TYPE_LONG | GI_TYPE_SHORT); 714193240Ssam } else { 715193240Ssam pCmd->GIType = htole32(GI_TYPE_LONG); 716193240Ssam } 717193240Ssam 718193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_HT_GUARD_INTERVAL); 719193240Ssam MWL_HAL_UNLOCK(mh); 720193240Ssam return retval; 721193240Ssam} 722193240Ssam 723193240Ssam/* 724193240Ssam * Configure radio. 725193240Ssam * Takes effect immediately. 726193240Ssam * XXX preamble installed after set fixed rate cmd 727193240Ssam */ 728193240Ssamint 729193240Ssammwl_hal_setradio(struct mwl_hal *mh0, int onoff, MWL_HAL_PREAMBLE preamble) 730193240Ssam{ 731193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 732193240Ssam HostCmd_DS_802_11_RADIO_CONTROL *pCmd; 733193240Ssam int retval; 734193240Ssam 735193240Ssam MWL_HAL_LOCK(mh); 736193240Ssam _CMD_SETUP(pCmd, HostCmd_DS_802_11_RADIO_CONTROL, 737193240Ssam HostCmd_CMD_802_11_RADIO_CONTROL); 738193240Ssam pCmd->Action = htole16(HostCmd_ACT_GEN_SET); 739193240Ssam if (onoff == 0) 740193240Ssam pCmd->Control = 0; 741193240Ssam else 742193240Ssam pCmd->Control = htole16(preamble); 743193240Ssam pCmd->RadioOn = htole16(onoff); 744193240Ssam 745193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_802_11_RADIO_CONTROL); 746193240Ssam MWL_HAL_UNLOCK(mh); 747193240Ssam return retval; 748193240Ssam} 749193240Ssam 750193240Ssam/* 751193240Ssam * Configure antenna use. 752193240Ssam * Takes effect immediately. 753193240Ssam * XXX tx antenna setting ignored 754193240Ssam * XXX rx antenna setting should always be 3 (for now) 755193240Ssam */ 756193240Ssamint 757193240Ssammwl_hal_setantenna(struct mwl_hal *mh0, MWL_HAL_ANTENNA dirSet, int ant) 758193240Ssam{ 759193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 760193240Ssam HostCmd_DS_802_11_RF_ANTENNA *pCmd; 761193240Ssam int retval; 762193240Ssam 763193240Ssam if (!(dirSet == WL_ANTENNATYPE_RX || dirSet == WL_ANTENNATYPE_TX)) 764193240Ssam return EINVAL; 765193240Ssam 766193240Ssam MWL_HAL_LOCK(mh); 767193240Ssam _CMD_SETUP(pCmd, HostCmd_DS_802_11_RF_ANTENNA, 768193240Ssam HostCmd_CMD_802_11_RF_ANTENNA); 769193240Ssam pCmd->Action = htole16(dirSet); 770193240Ssam if (ant == 0) /* default to all/both antennae */ 771193240Ssam ant = 3; 772193240Ssam pCmd->AntennaMode = htole16(ant); 773193240Ssam 774193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_802_11_RF_ANTENNA); 775193240Ssam MWL_HAL_UNLOCK(mh); 776193240Ssam return retval; 777193240Ssam} 778193240Ssam 779193240Ssam/* 780193240Ssam * Set packet size threshold for implicit use of RTS. 781193240Ssam * Takes effect immediately. 782193240Ssam * XXX packet length > threshold =>'s RTS 783193240Ssam */ 784193240Ssamint 785193240Ssammwl_hal_setrtsthreshold(struct mwl_hal_vap *vap, int threshold) 786193240Ssam{ 787193240Ssam struct mwl_hal_priv *mh = MWLVAP(vap); 788193240Ssam HostCmd_DS_802_11_RTS_THSD *pCmd; 789193240Ssam int retval; 790193240Ssam 791193240Ssam MWL_HAL_LOCK(mh); 792193240Ssam _VCMD_SETUP(vap, pCmd, HostCmd_DS_802_11_RTS_THSD, 793193240Ssam HostCmd_CMD_802_11_RTS_THSD); 794193240Ssam pCmd->Action = htole16(HostCmd_ACT_GEN_SET); 795193240Ssam pCmd->Threshold = htole16(threshold); 796193240Ssam 797193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_802_11_RTS_THSD); 798193240Ssam MWL_HAL_UNLOCK(mh); 799193240Ssam return retval; 800193240Ssam} 801193240Ssam 802193240Ssam/* 803193240Ssam * Enable sta-mode operation (disables beacon frame xmit). 804193240Ssam */ 805193240Ssamint 806193240Ssammwl_hal_setinframode(struct mwl_hal_vap *vap) 807193240Ssam{ 808193240Ssam struct mwl_hal_priv *mh = MWLVAP(vap); 809193240Ssam HostCmd_FW_SET_INFRA_MODE *pCmd; 810193240Ssam int retval; 811193240Ssam 812193240Ssam MWL_HAL_LOCK(mh); 813193240Ssam _VCMD_SETUP(vap, pCmd, HostCmd_FW_SET_INFRA_MODE, 814193240Ssam HostCmd_CMD_SET_INFRA_MODE); 815193240Ssam 816193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_INFRA_MODE); 817193240Ssam MWL_HAL_UNLOCK(mh); 818193240Ssam return retval; 819193240Ssam} 820193240Ssam 821193240Ssam/* 822193240Ssam * Configure radar detection in support of 802.11h. 823193240Ssam */ 824193240Ssamint 825193240Ssammwl_hal_setradardetection(struct mwl_hal *mh0, MWL_HAL_RADAR action) 826193240Ssam{ 827193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 828193240Ssam HostCmd_802_11h_Detect_Radar *pCmd; 829193240Ssam int retval; 830193240Ssam 831193240Ssam MWL_HAL_LOCK(mh); 832193240Ssam _CMD_SETUP(pCmd, HostCmd_802_11h_Detect_Radar, 833193240Ssam HostCmd_CMD_802_11H_DETECT_RADAR); 834193240Ssam pCmd->CmdHdr.Length = htole16(sizeof(HostCmd_802_11h_Detect_Radar)); 835193240Ssam pCmd->Action = htole16(action); 836193240Ssam if (mh->mh_regioncode == DOMAIN_CODE_ETSI_131) 837193240Ssam pCmd->RadarTypeCode = htole16(131); 838193240Ssam 839193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_802_11H_DETECT_RADAR); 840193240Ssam MWL_HAL_UNLOCK(mh); 841193240Ssam return retval; 842193240Ssam} 843193240Ssam 844193240Ssam/* 845193240Ssam * Convert public channel flags definition to a 846193240Ssam * value suitable for feeding to the firmware. 847193240Ssam * Note this includes byte swapping. 848193240Ssam */ 849193240Ssamstatic uint32_t 850193240SsamcvtChannelFlags(const MWL_HAL_CHANNEL *chan) 851193240Ssam{ 852193240Ssam uint32_t w; 853193240Ssam 854193240Ssam /* 855193240Ssam * NB: f/w only understands FREQ_BAND_5GHZ, supplying the more 856193240Ssam * precise band info causes it to lockup (sometimes). 857193240Ssam */ 858193240Ssam w = (chan->channelFlags.FreqBand == MWL_FREQ_BAND_2DOT4GHZ) ? 859193240Ssam FREQ_BAND_2DOT4GHZ : FREQ_BAND_5GHZ; 860193240Ssam switch (chan->channelFlags.ChnlWidth) { 861193240Ssam case MWL_CH_10_MHz_WIDTH: 862193240Ssam w |= CH_10_MHz_WIDTH; 863193240Ssam break; 864193240Ssam case MWL_CH_20_MHz_WIDTH: 865193240Ssam w |= CH_20_MHz_WIDTH; 866193240Ssam break; 867193240Ssam case MWL_CH_40_MHz_WIDTH: 868193240Ssam default: 869193240Ssam w |= CH_40_MHz_WIDTH; 870193240Ssam break; 871193240Ssam } 872193240Ssam switch (chan->channelFlags.ExtChnlOffset) { 873193240Ssam case MWL_EXT_CH_NONE: 874193240Ssam w |= EXT_CH_NONE; 875193240Ssam break; 876193240Ssam case MWL_EXT_CH_ABOVE_CTRL_CH: 877193240Ssam w |= EXT_CH_ABOVE_CTRL_CH; 878193240Ssam break; 879193240Ssam case MWL_EXT_CH_BELOW_CTRL_CH: 880193240Ssam w |= EXT_CH_BELOW_CTRL_CH; 881193240Ssam break; 882193240Ssam } 883193240Ssam return htole32(w); 884193240Ssam} 885193240Ssam 886193240Ssam/* 887193240Ssam * Start a channel switch announcement countdown. The IE 888193240Ssam * in the beacon frame is allowed to go out and the firmware 889193240Ssam * counts down and notifies the host when it's time to switch 890193240Ssam * channels. 891193240Ssam */ 892193240Ssamint 893193240Ssammwl_hal_setchannelswitchie(struct mwl_hal *mh0, 894193240Ssam const MWL_HAL_CHANNEL *nextchan, uint32_t mode, uint32_t count) 895193240Ssam{ 896193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 897193240Ssam HostCmd_SET_SWITCH_CHANNEL *pCmd; 898193240Ssam int retval; 899193240Ssam 900193240Ssam MWL_HAL_LOCK(mh); 901193240Ssam _CMD_SETUP(pCmd, HostCmd_SET_SWITCH_CHANNEL, 902193240Ssam HostCmd_CMD_SET_SWITCH_CHANNEL); 903193240Ssam pCmd->Next11hChannel = htole32(nextchan->channel); 904193240Ssam pCmd->Mode = htole32(mode); 905193240Ssam pCmd->InitialCount = htole32(count+1); 906193240Ssam pCmd->ChannelFlags = cvtChannelFlags(nextchan); 907193240Ssam 908193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_SWITCH_CHANNEL); 909193240Ssam MWL_HAL_UNLOCK(mh); 910193240Ssam return retval; 911193240Ssam} 912193240Ssam 913193240Ssam/* 914193240Ssam * Set the region code that selects the radar bin'ing agorithm. 915193240Ssam */ 916193240Ssamint 917193240Ssammwl_hal_setregioncode(struct mwl_hal *mh0, int regionCode) 918193240Ssam{ 919193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 920193240Ssam HostCmd_SET_REGIONCODE_INFO *pCmd; 921193240Ssam int retval; 922193240Ssam 923193240Ssam MWL_HAL_LOCK(mh); 924193240Ssam _CMD_SETUP(pCmd, HostCmd_SET_REGIONCODE_INFO, 925193240Ssam HostCmd_CMD_SET_REGION_CODE); 926193240Ssam /* XXX map pseudo-codes to fw codes */ 927193240Ssam switch (regionCode) { 928193240Ssam case DOMAIN_CODE_ETSI_131: 929193240Ssam pCmd->regionCode = htole16(DOMAIN_CODE_ETSI); 930193240Ssam break; 931193240Ssam default: 932193240Ssam pCmd->regionCode = htole16(regionCode); 933193240Ssam break; 934193240Ssam } 935193240Ssam 936193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_REGION_CODE); 937193240Ssam if (retval == 0) 938193240Ssam mh->mh_regioncode = regionCode; 939193240Ssam MWL_HAL_UNLOCK(mh); 940193240Ssam return retval; 941193240Ssam} 942193240Ssam 943193240Ssam#define RATEVAL(r) ((r) &~ RATE_MCS) 944193240Ssam#define RATETYPE(r) (((r) & RATE_MCS) ? HT_RATE_TYPE : LEGACY_RATE_TYPE) 945193240Ssam 946193240Ssamint 947193240Ssammwl_hal_settxrate(struct mwl_hal_vap *vap, MWL_HAL_TXRATE_HANDLING handling, 948193240Ssam const MWL_HAL_TXRATE *rate) 949193240Ssam{ 950193240Ssam struct mwl_hal_priv *mh = MWLVAP(vap); 951193240Ssam HostCmd_FW_USE_FIXED_RATE *pCmd; 952193240Ssam FIXED_RATE_ENTRY *fp; 953193240Ssam int retval, i, n; 954193240Ssam 955193240Ssam MWL_HAL_LOCK(mh); 956193240Ssam _VCMD_SETUP(vap, pCmd, HostCmd_FW_USE_FIXED_RATE, 957193240Ssam HostCmd_CMD_SET_FIXED_RATE); 958193240Ssam 959193240Ssam pCmd->MulticastRate = RATEVAL(rate->McastRate); 960193240Ssam pCmd->MultiRateTxType = RATETYPE(rate->McastRate); 961193240Ssam /* NB: no rate type field */ 962193240Ssam pCmd->ManagementRate = RATEVAL(rate->MgtRate); 963193240Ssam memset(pCmd->FixedRateTable, 0, sizeof(pCmd->FixedRateTable)); 964193240Ssam if (handling == RATE_FIXED) { 965193240Ssam pCmd->Action = htole32(HostCmd_ACT_GEN_SET); 966193240Ssam pCmd->AllowRateDrop = htole32(FIXED_RATE_WITHOUT_AUTORATE_DROP); 967193240Ssam fp = pCmd->FixedRateTable; 968193240Ssam fp->FixedRate = 969193240Ssam htole32(RATEVAL(rate->RateSeries[0].Rate)); 970193240Ssam fp->FixRateTypeFlags.FixRateType = 971193240Ssam htole32(RATETYPE(rate->RateSeries[0].Rate)); 972193240Ssam pCmd->EntryCount = htole32(1); 973193240Ssam } else if (handling == RATE_FIXED_DROP) { 974193240Ssam pCmd->Action = htole32(HostCmd_ACT_GEN_SET); 975193240Ssam pCmd->AllowRateDrop = htole32(FIXED_RATE_WITH_AUTO_RATE_DROP); 976193240Ssam n = 0; 977193240Ssam fp = pCmd->FixedRateTable; 978193240Ssam for (i = 0; i < 4; i++) { 979193240Ssam if (rate->RateSeries[0].TryCount == 0) 980193240Ssam break; 981193240Ssam fp->FixRateTypeFlags.FixRateType = 982193240Ssam htole32(RATETYPE(rate->RateSeries[i].Rate)); 983193240Ssam fp->FixedRate = 984193240Ssam htole32(RATEVAL(rate->RateSeries[i].Rate)); 985193240Ssam fp->FixRateTypeFlags.RetryCountValid = 986193240Ssam htole32(RETRY_COUNT_VALID); 987193240Ssam fp->RetryCount = 988193240Ssam htole32(rate->RateSeries[i].TryCount-1); 989193240Ssam n++; 990193240Ssam } 991193240Ssam pCmd->EntryCount = htole32(n); 992193240Ssam } else 993193240Ssam pCmd->Action = htole32(HostCmd_ACT_NOT_USE_FIXED_RATE); 994193240Ssam 995193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_FIXED_RATE); 996193240Ssam MWL_HAL_UNLOCK(mh); 997193240Ssam return retval; 998193240Ssam} 999193240Ssam 1000193240Ssamint 1001193240Ssammwl_hal_settxrate_auto(struct mwl_hal *mh0, const MWL_HAL_TXRATE *rate) 1002193240Ssam{ 1003193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 1004193240Ssam HostCmd_FW_USE_FIXED_RATE *pCmd; 1005193240Ssam int retval; 1006193240Ssam 1007193240Ssam MWL_HAL_LOCK(mh); 1008193240Ssam _CMD_SETUP(pCmd, HostCmd_FW_USE_FIXED_RATE, 1009193240Ssam HostCmd_CMD_SET_FIXED_RATE); 1010193240Ssam 1011193240Ssam pCmd->MulticastRate = RATEVAL(rate->McastRate); 1012193240Ssam pCmd->MultiRateTxType = RATETYPE(rate->McastRate); 1013193240Ssam /* NB: no rate type field */ 1014193240Ssam pCmd->ManagementRate = RATEVAL(rate->MgtRate); 1015193240Ssam memset(pCmd->FixedRateTable, 0, sizeof(pCmd->FixedRateTable)); 1016193240Ssam pCmd->Action = htole32(HostCmd_ACT_NOT_USE_FIXED_RATE); 1017193240Ssam 1018193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_FIXED_RATE); 1019193240Ssam MWL_HAL_UNLOCK(mh); 1020193240Ssam return retval; 1021193240Ssam} 1022193240Ssam 1023193240Ssam#undef RATEVAL 1024193240Ssam#undef RATETYPE 1025193240Ssam 1026193240Ssamint 1027193240Ssammwl_hal_setslottime(struct mwl_hal *mh0, int usecs) 1028193240Ssam{ 1029193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 1030193240Ssam HostCmd_FW_SET_SLOT *pCmd; 1031193240Ssam int retval; 1032193240Ssam 1033193240Ssam if (usecs != 9 && usecs != 20) 1034193240Ssam return EINVAL; 1035193240Ssam 1036193240Ssam MWL_HAL_LOCK(mh); 1037193240Ssam _CMD_SETUP(pCmd, HostCmd_FW_SET_SLOT, 1038193240Ssam HostCmd_CMD_802_11_SET_SLOT); 1039193240Ssam pCmd->Action = htole16(HostCmd_ACT_GEN_SET); 1040193240Ssam pCmd->Slot = (usecs == 9 ? 1 : 0); 1041193240Ssam 1042193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_802_11_SET_SLOT); 1043193240Ssam MWL_HAL_UNLOCK(mh); 1044193240Ssam return retval; 1045193240Ssam} 1046193240Ssam 1047193240Ssamint 1048193240Ssammwl_hal_adjusttxpower(struct mwl_hal *mh0, uint32_t level) 1049193240Ssam{ 1050193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 1051193240Ssam HostCmd_DS_802_11_RF_TX_POWER *pCmd; 1052193240Ssam int retval; 1053193240Ssam 1054193240Ssam MWL_HAL_LOCK(mh); 1055193240Ssam _CMD_SETUP(pCmd, HostCmd_DS_802_11_RF_TX_POWER, 1056193240Ssam HostCmd_CMD_802_11_RF_TX_POWER); 1057193240Ssam pCmd->Action = htole16(HostCmd_ACT_GEN_SET); 1058193240Ssam 1059193240Ssam if (level < 30) { 1060193240Ssam pCmd->SupportTxPowerLevel = htole16(WL_TX_POWERLEVEL_LOW); 1061193240Ssam } else if (level >= 30 && level < 60) { 1062193240Ssam pCmd->SupportTxPowerLevel = htole16(WL_TX_POWERLEVEL_MEDIUM); 1063193240Ssam } else { 1064193240Ssam pCmd->SupportTxPowerLevel = htole16(WL_TX_POWERLEVEL_HIGH); 1065193240Ssam } 1066193240Ssam 1067193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_802_11_RF_TX_POWER); 1068193240Ssam MWL_HAL_UNLOCK(mh); 1069193240Ssam return retval; 1070193240Ssam} 1071193240Ssam 1072193240Ssamstatic const struct mwl_hal_channel * 1073193240Ssamfindchannel(const struct mwl_hal_priv *mh, const MWL_HAL_CHANNEL *c) 1074193240Ssam{ 1075193240Ssam const struct mwl_hal_channel *hc; 1076193240Ssam const MWL_HAL_CHANNELINFO *ci; 1077193240Ssam int chan = c->channel, i; 1078193240Ssam 1079193240Ssam if (c->channelFlags.FreqBand == MWL_FREQ_BAND_2DOT4GHZ) { 1080193240Ssam i = chan - 1; 1081193240Ssam if (c->channelFlags.ChnlWidth == MWL_CH_40_MHz_WIDTH) { 1082193240Ssam ci = &mh->mh_40M; 1083193240Ssam if (c->channelFlags.ExtChnlOffset == MWL_EXT_CH_BELOW_CTRL_CH) 1084193240Ssam i -= 4; 1085193240Ssam } else 1086193240Ssam ci = &mh->mh_20M; 1087193240Ssam /* 2.4G channel table is directly indexed */ 1088193240Ssam hc = ((unsigned)i < ci->nchannels) ? &ci->channels[i] : NULL; 1089193240Ssam } else if (c->channelFlags.FreqBand == MWL_FREQ_BAND_5GHZ) { 1090193240Ssam if (c->channelFlags.ChnlWidth == MWL_CH_40_MHz_WIDTH) { 1091193240Ssam ci = &mh->mh_40M_5G; 1092193240Ssam if (c->channelFlags.ExtChnlOffset == MWL_EXT_CH_BELOW_CTRL_CH) 1093193240Ssam chan -= 4; 1094193240Ssam } else 1095193240Ssam ci = &mh->mh_20M_5G; 1096193240Ssam /* 5GHz channel table is sparse and must be searched */ 1097193240Ssam for (i = 0; i < ci->nchannels; i++) 1098193240Ssam if (ci->channels[i].ieee == chan) 1099193240Ssam break; 1100193240Ssam hc = (i < ci->nchannels) ? &ci->channels[i] : NULL; 1101193240Ssam } else 1102193240Ssam hc = NULL; 1103193240Ssam return hc; 1104193240Ssam} 1105193240Ssam 1106193240Ssamint 1107193240Ssammwl_hal_settxpower(struct mwl_hal *mh0, const MWL_HAL_CHANNEL *c, uint8_t maxtxpow) 1108193240Ssam{ 1109193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 1110193240Ssam HostCmd_DS_802_11_RF_TX_POWER *pCmd; 1111193240Ssam const struct mwl_hal_channel *hc; 1112193240Ssam int i, retval; 1113193240Ssam 1114193240Ssam hc = findchannel(mh, c); 1115193240Ssam if (hc == NULL) { 1116193240Ssam /* XXX temp while testing */ 1117193240Ssam device_printf(mh->mh_dev, 1118193240Ssam "%s: no cal data for channel %u band %u width %u ext %u\n", 1119193240Ssam __func__, c->channel, c->channelFlags.FreqBand, 1120193240Ssam c->channelFlags.ChnlWidth, c->channelFlags.ExtChnlOffset); 1121193240Ssam return EINVAL; 1122193240Ssam } 1123193240Ssam 1124193240Ssam MWL_HAL_LOCK(mh); 1125193240Ssam _CMD_SETUP(pCmd, HostCmd_DS_802_11_RF_TX_POWER, 1126193240Ssam HostCmd_CMD_802_11_RF_TX_POWER); 1127193240Ssam pCmd->Action = htole16(HostCmd_ACT_GEN_SET_LIST); 1128193240Ssam i = 0; 1129193240Ssam /* NB: 5Ghz cal data have the channel # in [0]; don't truncate */ 1130193240Ssam if (c->channelFlags.FreqBand == MWL_FREQ_BAND_5GHZ) 1131193240Ssam pCmd->PowerLevelList[i++] = htole16(hc->targetPowers[0]); 1132193240Ssam for (; i < 4; i++) { 1133193240Ssam uint16_t pow = hc->targetPowers[i]; 1134193240Ssam if (pow > maxtxpow) 1135193240Ssam pow = maxtxpow; 1136193240Ssam pCmd->PowerLevelList[i] = htole16(pow); 1137193240Ssam } 1138193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_802_11_RF_TX_POWER); 1139193240Ssam MWL_HAL_UNLOCK(mh); 1140193240Ssam return retval; 1141193240Ssam} 1142193240Ssam 1143193240Ssamint 1144193240Ssammwl_hal_getchannelinfo(struct mwl_hal *mh0, int band, int chw, 1145193240Ssam const MWL_HAL_CHANNELINFO **ci) 1146193240Ssam{ 1147193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 1148193240Ssam 1149193240Ssam switch (band) { 1150193240Ssam case MWL_FREQ_BAND_2DOT4GHZ: 1151193240Ssam *ci = (chw == MWL_CH_20_MHz_WIDTH) ? &mh->mh_20M : &mh->mh_40M; 1152193240Ssam break; 1153193240Ssam case MWL_FREQ_BAND_5GHZ: 1154193240Ssam *ci = (chw == MWL_CH_20_MHz_WIDTH) ? 1155193240Ssam &mh->mh_20M_5G : &mh->mh_40M_5G; 1156193240Ssam break; 1157193240Ssam default: 1158193240Ssam return EINVAL; 1159193240Ssam } 1160193240Ssam return ((*ci)->freqLow == (*ci)->freqHigh) ? EINVAL : 0; 1161193240Ssam} 1162193240Ssam 1163193240Ssamint 1164193240Ssammwl_hal_setmcast(struct mwl_hal *mh0, int nmc, const uint8_t macs[]) 1165193240Ssam{ 1166193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 1167193240Ssam HostCmd_DS_MAC_MULTICAST_ADR *pCmd; 1168193240Ssam int retval; 1169193240Ssam 1170193240Ssam if (nmc > MWL_HAL_MCAST_MAX) 1171193240Ssam return EINVAL; 1172193240Ssam 1173193240Ssam MWL_HAL_LOCK(mh); 1174193240Ssam _CMD_SETUP(pCmd, HostCmd_DS_MAC_MULTICAST_ADR, 1175193240Ssam HostCmd_CMD_MAC_MULTICAST_ADR); 1176193240Ssam memcpy(pCmd->MACList, macs, nmc*IEEE80211_ADDR_LEN); 1177193240Ssam pCmd->NumOfAdrs = htole16(nmc); 1178193240Ssam pCmd->Action = htole16(0xffff); 1179193240Ssam 1180193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_MAC_MULTICAST_ADR); 1181193240Ssam MWL_HAL_UNLOCK(mh); 1182193240Ssam return retval; 1183193240Ssam} 1184193240Ssam 1185193240Ssamint 1186193240Ssammwl_hal_keyset(struct mwl_hal_vap *vap, const MWL_HAL_KEYVAL *kv, 1187193240Ssam const uint8_t mac[IEEE80211_ADDR_LEN]) 1188193240Ssam{ 1189193240Ssam struct mwl_hal_priv *mh = MWLVAP(vap); 1190193240Ssam HostCmd_FW_UPDATE_ENCRYPTION_SET_KEY *pCmd; 1191193240Ssam int retval; 1192193240Ssam 1193193240Ssam MWL_HAL_LOCK(mh); 1194193240Ssam _VCMD_SETUP(vap, pCmd, HostCmd_FW_UPDATE_ENCRYPTION_SET_KEY, 1195193240Ssam HostCmd_CMD_UPDATE_ENCRYPTION); 1196193240Ssam if (kv->keyFlags & (KEY_FLAG_TXGROUPKEY|KEY_FLAG_RXGROUPKEY)) 1197193240Ssam pCmd->ActionType = htole32(EncrActionTypeSetGroupKey); 1198193240Ssam else 1199193240Ssam pCmd->ActionType = htole32(EncrActionTypeSetKey); 1200193240Ssam pCmd->KeyParam.Length = htole16(sizeof(pCmd->KeyParam)); 1201193240Ssam pCmd->KeyParam.KeyTypeId = htole16(kv->keyTypeId); 1202193240Ssam pCmd->KeyParam.KeyInfo = htole32(kv->keyFlags); 1203193240Ssam pCmd->KeyParam.KeyIndex = htole32(kv->keyIndex); 1204193240Ssam /* NB: includes TKIP MIC keys */ 1205193240Ssam memcpy(&pCmd->KeyParam.Key, &kv->key, kv->keyLen); 1206193240Ssam switch (kv->keyTypeId) { 1207193240Ssam case KEY_TYPE_ID_WEP: 1208193240Ssam pCmd->KeyParam.KeyLen = htole16(kv->keyLen); 1209193240Ssam break; 1210193240Ssam case KEY_TYPE_ID_TKIP: 1211193240Ssam pCmd->KeyParam.KeyLen = htole16(sizeof(TKIP_TYPE_KEY)); 1212193240Ssam pCmd->KeyParam.Key.TkipKey.TkipRsc.low = 1213193240Ssam htole16(kv->key.tkip.rsc.low); 1214193240Ssam pCmd->KeyParam.Key.TkipKey.TkipRsc.high = 1215193240Ssam htole32(kv->key.tkip.rsc.high); 1216193240Ssam pCmd->KeyParam.Key.TkipKey.TkipTsc.low = 1217193240Ssam htole16(kv->key.tkip.tsc.low); 1218193240Ssam pCmd->KeyParam.Key.TkipKey.TkipTsc.high = 1219193240Ssam htole32(kv->key.tkip.tsc.high); 1220193240Ssam break; 1221193240Ssam case KEY_TYPE_ID_AES: 1222193240Ssam pCmd->KeyParam.KeyLen = htole16(sizeof(AES_TYPE_KEY)); 1223193240Ssam break; 1224193240Ssam } 1225193240Ssam#ifdef MWL_MBSS_SUPPORT 1226193240Ssam IEEE80211_ADDR_COPY(pCmd->KeyParam.Macaddr, mac); 1227193240Ssam#else 1228193240Ssam IEEE80211_ADDR_COPY(pCmd->Macaddr, mac); 1229193240Ssam#endif 1230193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_UPDATE_ENCRYPTION); 1231193240Ssam MWL_HAL_UNLOCK(mh); 1232193240Ssam return retval; 1233193240Ssam} 1234193240Ssam 1235193240Ssamint 1236193240Ssammwl_hal_keyreset(struct mwl_hal_vap *vap, const MWL_HAL_KEYVAL *kv, const uint8_t mac[IEEE80211_ADDR_LEN]) 1237193240Ssam{ 1238193240Ssam struct mwl_hal_priv *mh = MWLVAP(vap); 1239193240Ssam HostCmd_FW_UPDATE_ENCRYPTION_SET_KEY *pCmd; 1240193240Ssam int retval; 1241193240Ssam 1242193240Ssam MWL_HAL_LOCK(mh); 1243193240Ssam _VCMD_SETUP(vap, pCmd, HostCmd_FW_UPDATE_ENCRYPTION_SET_KEY, 1244193240Ssam HostCmd_CMD_UPDATE_ENCRYPTION); 1245193240Ssam pCmd->ActionType = htole16(EncrActionTypeRemoveKey); 1246193240Ssam pCmd->KeyParam.Length = htole16(sizeof(pCmd->KeyParam)); 1247193240Ssam pCmd->KeyParam.KeyTypeId = htole16(kv->keyTypeId); 1248193240Ssam pCmd->KeyParam.KeyInfo = htole32(kv->keyFlags); 1249193240Ssam pCmd->KeyParam.KeyIndex = htole32(kv->keyIndex); 1250193240Ssam#ifdef MWL_MBSS_SUPPORT 1251193240Ssam IEEE80211_ADDR_COPY(pCmd->KeyParam.Macaddr, mac); 1252193240Ssam#else 1253193240Ssam IEEE80211_ADDR_COPY(pCmd->Macaddr, mac); 1254193240Ssam#endif 1255193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_UPDATE_ENCRYPTION); 1256193240Ssam MWL_HAL_UNLOCK(mh); 1257193240Ssam return retval; 1258193240Ssam} 1259193240Ssam 1260193240Ssamstatic int 1261193240Ssammwl_hal_setmac_locked(struct mwl_hal_vap *vap, 1262193240Ssam const uint8_t addr[IEEE80211_ADDR_LEN]) 1263193240Ssam{ 1264193240Ssam struct mwl_hal_priv *mh = MWLVAP(vap); 1265193240Ssam HostCmd_DS_SET_MAC *pCmd; 1266193240Ssam 1267193240Ssam _VCMD_SETUP(vap, pCmd, HostCmd_DS_SET_MAC, HostCmd_CMD_SET_MAC_ADDR); 1268193240Ssam IEEE80211_ADDR_COPY(&pCmd->MacAddr[0], addr); 1269193240Ssam#ifdef MWL_MBSS_SUPPORT 1270193240Ssam pCmd->MacType = vap->bss_type; /* NB: already byte swapped */ 1271193240Ssam IEEE80211_ADDR_COPY(vap->mac, addr); /* XXX do only if success */ 1272193240Ssam#endif 1273193240Ssam return mwlExecuteCmd(mh, HostCmd_CMD_SET_MAC_ADDR); 1274193240Ssam} 1275193240Ssam 1276193240Ssamint 1277193240Ssammwl_hal_setmac(struct mwl_hal_vap *vap, const uint8_t addr[IEEE80211_ADDR_LEN]) 1278193240Ssam{ 1279193240Ssam struct mwl_hal_priv *mh = MWLVAP(vap); 1280193240Ssam int retval; 1281193240Ssam 1282193240Ssam MWL_HAL_LOCK(mh); 1283193240Ssam retval = mwl_hal_setmac_locked(vap, addr); 1284193240Ssam MWL_HAL_UNLOCK(mh); 1285193240Ssam return retval; 1286193240Ssam} 1287193240Ssam 1288193240Ssamint 1289193240Ssammwl_hal_setbeacon(struct mwl_hal_vap *vap, const void *frame, size_t frameLen) 1290193240Ssam{ 1291193240Ssam struct mwl_hal_priv *mh = MWLVAP(vap); 1292193240Ssam HostCmd_DS_SET_BEACON *pCmd; 1293193240Ssam int retval; 1294193240Ssam 1295193240Ssam /* XXX verify frameLen fits */ 1296193240Ssam MWL_HAL_LOCK(mh); 1297193240Ssam _VCMD_SETUP(vap, pCmd, HostCmd_DS_SET_BEACON, HostCmd_CMD_SET_BEACON); 1298193240Ssam /* XXX override _VCMD_SETUP */ 1299193240Ssam pCmd->CmdHdr.Length = htole16(sizeof(HostCmd_DS_SET_BEACON)-1+frameLen); 1300193240Ssam pCmd->FrmBodyLen = htole16(frameLen); 1301193240Ssam memcpy(pCmd->FrmBody, frame, frameLen); 1302193240Ssam 1303193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_BEACON); 1304193240Ssam MWL_HAL_UNLOCK(mh); 1305193240Ssam return retval; 1306193240Ssam} 1307193240Ssam 1308193240Ssamint 1309193240Ssammwl_hal_setpowersave_bss(struct mwl_hal_vap *vap, uint8_t nsta) 1310193240Ssam{ 1311193240Ssam struct mwl_hal_priv *mh = MWLVAP(vap); 1312193240Ssam HostCmd_SET_POWERSAVESTATION *pCmd; 1313193240Ssam int retval; 1314193240Ssam 1315193240Ssam MWL_HAL_LOCK(mh); 1316193240Ssam _VCMD_SETUP(vap, pCmd, HostCmd_SET_POWERSAVESTATION, 1317193240Ssam HostCmd_CMD_SET_POWERSAVESTATION); 1318193240Ssam pCmd->NumberOfPowersave = nsta; 1319193240Ssam 1320193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_POWERSAVESTATION); 1321193240Ssam MWL_HAL_UNLOCK(mh); 1322193240Ssam return retval; 1323193240Ssam} 1324193240Ssam 1325193240Ssamint 1326193240Ssammwl_hal_setpowersave_sta(struct mwl_hal_vap *vap, uint16_t aid, int ena) 1327193240Ssam{ 1328193240Ssam struct mwl_hal_priv *mh = MWLVAP(vap); 1329193240Ssam HostCmd_SET_TIM *pCmd; 1330193240Ssam int retval; 1331193240Ssam 1332193240Ssam MWL_HAL_LOCK(mh); 1333193240Ssam _VCMD_SETUP(vap, pCmd, HostCmd_SET_TIM, HostCmd_CMD_SET_TIM); 1334193240Ssam pCmd->Aid = htole16(aid); 1335193240Ssam pCmd->Set = htole32(ena); 1336193240Ssam 1337193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_TIM); 1338193240Ssam MWL_HAL_UNLOCK(mh); 1339193240Ssam return retval; 1340193240Ssam} 1341193240Ssam 1342193240Ssamint 1343193240Ssammwl_hal_setassocid(struct mwl_hal_vap *vap, 1344193240Ssam const uint8_t bssId[IEEE80211_ADDR_LEN], uint16_t assocId) 1345193240Ssam{ 1346193240Ssam struct mwl_hal_priv *mh = MWLVAP(vap); 1347193240Ssam HostCmd_FW_SET_AID *pCmd = (HostCmd_FW_SET_AID *) &mh->mh_cmdbuf[0]; 1348193240Ssam int retval; 1349193240Ssam 1350193240Ssam MWL_HAL_LOCK(mh); 1351193240Ssam _VCMD_SETUP(vap, pCmd, HostCmd_FW_SET_AID, HostCmd_CMD_SET_AID); 1352193240Ssam pCmd->AssocID = htole16(assocId); 1353193240Ssam IEEE80211_ADDR_COPY(&pCmd->MacAddr[0], bssId); 1354193240Ssam 1355193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_AID); 1356193240Ssam MWL_HAL_UNLOCK(mh); 1357193240Ssam return retval; 1358193240Ssam} 1359193240Ssam 1360193240Ssamint 1361193240Ssammwl_hal_setchannel(struct mwl_hal *mh0, const MWL_HAL_CHANNEL *chan) 1362193240Ssam{ 1363193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 1364193240Ssam HostCmd_FW_SET_RF_CHANNEL *pCmd; 1365193240Ssam int retval; 1366193240Ssam 1367193240Ssam MWL_HAL_LOCK(mh); 1368193240Ssam _CMD_SETUP(pCmd, HostCmd_FW_SET_RF_CHANNEL, HostCmd_CMD_SET_RF_CHANNEL); 1369193240Ssam pCmd->Action = htole16(HostCmd_ACT_GEN_SET); 1370193240Ssam pCmd->CurrentChannel = chan->channel; 1371193240Ssam pCmd->ChannelFlags = cvtChannelFlags(chan); /* NB: byte-swapped */ 1372193240Ssam 1373193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_RF_CHANNEL); 1374193240Ssam MWL_HAL_UNLOCK(mh); 1375193240Ssam return retval; 1376193240Ssam} 1377193240Ssam 1378193240Ssamstatic int 1379195171Ssambastream_check_available(struct mwl_hal_vap *vap, int qid, 1380193240Ssam const uint8_t Macaddr[IEEE80211_ADDR_LEN], 1381193240Ssam uint8_t Tid, uint8_t ParamInfo) 1382193240Ssam{ 1383195171Ssam struct mwl_hal_priv *mh = MWLVAP(vap); 1384193240Ssam HostCmd_FW_BASTREAM *pCmd; 1385193240Ssam int retval; 1386193240Ssam 1387193240Ssam MWL_HAL_LOCK_ASSERT(mh); 1388193240Ssam 1389195171Ssam _VCMD_SETUP(vap, pCmd, HostCmd_FW_BASTREAM, HostCmd_CMD_BASTREAM); 1390193240Ssam pCmd->ActionType = htole32(BaCheckCreateStream); 1391193240Ssam pCmd->BaInfo.CreateParams.BarThrs = htole32(63); 1392193240Ssam pCmd->BaInfo.CreateParams.WindowSize = htole32(64); 1393193240Ssam pCmd->BaInfo.CreateParams.IdleThrs = htole32(0x22000); 1394193240Ssam IEEE80211_ADDR_COPY(&pCmd->BaInfo.CreateParams.PeerMacAddr[0], Macaddr); 1395193240Ssam pCmd->BaInfo.CreateParams.DialogToken = 10; 1396193240Ssam pCmd->BaInfo.CreateParams.Tid = Tid; 1397193240Ssam pCmd->BaInfo.CreateParams.QueueId = qid; 1398193240Ssam pCmd->BaInfo.CreateParams.ParamInfo = (uint8_t) ParamInfo; 1399193240Ssam#if 0 1400195171Ssam cvtBAFlags(&pCmd->BaInfo.CreateParams.Flags, sp->ba_policy, 0); 1401193240Ssam#else 1402193240Ssam pCmd->BaInfo.CreateParams.Flags = 1403193240Ssam htole32(BASTREAM_FLAG_IMMEDIATE_TYPE) 1404193240Ssam | htole32(BASTREAM_FLAG_DIRECTION_UPSTREAM) 1405193240Ssam ; 1406193240Ssam#endif 1407193240Ssam 1408193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_BASTREAM); 1409193240Ssam if (retval == 0) { 1410193240Ssam /* 1411193240Ssam * NB: BA stream create may fail when the stream is 1412193240Ssam * h/w backed under some (as yet not understood) conditions. 1413193240Ssam * Check the result code to catch this. 1414193240Ssam */ 1415193240Ssam if (le16toh(pCmd->CmdHdr.Result) != HostCmd_RESULT_OK) 1416193240Ssam retval = EIO; 1417193240Ssam } 1418193240Ssam return retval; 1419193240Ssam} 1420193240Ssam 1421193240Ssamconst MWL_HAL_BASTREAM * 1422195171Ssammwl_hal_bastream_alloc(struct mwl_hal_vap *vap, int ba_policy, 1423193240Ssam const uint8_t Macaddr[IEEE80211_ADDR_LEN], 1424193240Ssam uint8_t Tid, uint8_t ParamInfo, void *a1, void *a2) 1425193240Ssam{ 1426195171Ssam struct mwl_hal_priv *mh = MWLVAP(vap); 1427193240Ssam struct mwl_hal_bastream *sp; 1428193240Ssam int s; 1429193240Ssam 1430193240Ssam MWL_HAL_LOCK(mh); 1431193240Ssam if (mh->mh_bastreams == 0) { 1432193240Ssam /* no streams available */ 1433193240Ssam MWL_HAL_UNLOCK(mh); 1434193240Ssam return NULL; 1435193240Ssam } 1436193240Ssam for (s = 0; (mh->mh_bastreams & (1<<s)) == 0; s++) 1437193240Ssam ; 1438195171Ssam if (bastream_check_available(vap, s, Macaddr, Tid, ParamInfo)) { 1439193240Ssam MWL_HAL_UNLOCK(mh); 1440193240Ssam return NULL; 1441193240Ssam } 1442193240Ssam sp = &mh->mh_streams[s]; 1443193240Ssam mh->mh_bastreams &= ~(1<<s); 1444193240Ssam sp->public.data[0] = a1; 1445193240Ssam sp->public.data[1] = a2; 1446193240Ssam IEEE80211_ADDR_COPY(sp->macaddr, Macaddr); 1447193240Ssam sp->tid = Tid; 1448193240Ssam sp->paraminfo = ParamInfo; 1449193240Ssam sp->setup = 0; 1450195171Ssam sp->ba_policy = ba_policy; 1451193240Ssam MWL_HAL_UNLOCK(mh); 1452193240Ssam return sp != NULL ? &sp->public : NULL; 1453193240Ssam} 1454193240Ssam 1455193240Ssamconst MWL_HAL_BASTREAM * 1456193240Ssammwl_hal_bastream_lookup(struct mwl_hal *mh0, int s) 1457193240Ssam{ 1458193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 1459193240Ssam 1460193240Ssam if (!(0 <= s && s < MWL_BASTREAMS_MAX)) 1461193240Ssam return NULL; 1462193240Ssam if (mh->mh_bastreams & (1<<s)) 1463193240Ssam return NULL; 1464193240Ssam return &mh->mh_streams[s].public; 1465193240Ssam} 1466193240Ssam 1467193240Ssam#ifndef __DECONST 1468193240Ssam#define __DECONST(type, var) ((type)(uintptr_t)(const void *)(var)) 1469193240Ssam#endif 1470193240Ssam 1471193240Ssamint 1472195171Ssammwl_hal_bastream_create(struct mwl_hal_vap *vap, 1473193240Ssam const MWL_HAL_BASTREAM *s, int BarThrs, int WindowSize, uint16_t seqno) 1474193240Ssam{ 1475195171Ssam struct mwl_hal_priv *mh = MWLVAP(vap); 1476193240Ssam struct mwl_hal_bastream *sp = __DECONST(struct mwl_hal_bastream *, s); 1477193240Ssam HostCmd_FW_BASTREAM *pCmd; 1478193240Ssam int retval; 1479193240Ssam 1480193240Ssam MWL_HAL_LOCK(mh); 1481195171Ssam _VCMD_SETUP(vap, pCmd, HostCmd_FW_BASTREAM, HostCmd_CMD_BASTREAM); 1482193240Ssam pCmd->ActionType = htole32(BaCreateStream); 1483195171Ssam pCmd->BaInfo.CreateParams.BarThrs = htole32(BarThrs); 1484195171Ssam pCmd->BaInfo.CreateParams.WindowSize = htole32(WindowSize); 1485193240Ssam pCmd->BaInfo.CreateParams.IdleThrs = htole32(0x22000); 1486193240Ssam IEEE80211_ADDR_COPY(&pCmd->BaInfo.CreateParams.PeerMacAddr[0], 1487193240Ssam sp->macaddr); 1488195171Ssam /* XXX proxy STA */ 1489195171Ssam memset(&pCmd->BaInfo.CreateParams.StaSrcMacAddr, 0, IEEE80211_ADDR_LEN); 1490193240Ssam#if 0 1491193240Ssam pCmd->BaInfo.CreateParams.DialogToken = DialogToken; 1492193240Ssam#else 1493193240Ssam pCmd->BaInfo.CreateParams.DialogToken = 10; 1494193240Ssam#endif 1495193240Ssam pCmd->BaInfo.CreateParams.Tid = sp->tid; 1496193240Ssam pCmd->BaInfo.CreateParams.QueueId = sp->stream; 1497193240Ssam pCmd->BaInfo.CreateParams.ParamInfo = sp->paraminfo; 1498193240Ssam /* NB: ResetSeqNo known to be zero */ 1499193240Ssam pCmd->BaInfo.CreateParams.StartSeqNo = htole16(seqno); 1500193240Ssam#if 0 1501195171Ssam cvtBAFlags(&pCmd->BaInfo.CreateParams.Flags, sp->ba_policy, 0); 1502193240Ssam#else 1503193240Ssam pCmd->BaInfo.CreateParams.Flags = 1504193240Ssam htole32(BASTREAM_FLAG_IMMEDIATE_TYPE) 1505193240Ssam | htole32(BASTREAM_FLAG_DIRECTION_UPSTREAM) 1506193240Ssam ; 1507193240Ssam#endif 1508193240Ssam 1509193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_BASTREAM); 1510193240Ssam if (retval == 0) { 1511193240Ssam /* 1512193240Ssam * NB: BA stream create may fail when the stream is 1513193240Ssam * h/w backed under some (as yet not understood) conditions. 1514193240Ssam * Check the result code to catch this. 1515193240Ssam */ 1516193240Ssam if (le16toh(pCmd->CmdHdr.Result) != HostCmd_RESULT_OK) 1517193240Ssam retval = EIO; 1518193240Ssam else 1519193240Ssam sp->setup = 1; 1520193240Ssam } 1521193240Ssam MWL_HAL_UNLOCK(mh); 1522193240Ssam return retval; 1523193240Ssam} 1524193240Ssam 1525193240Ssamint 1526193240Ssammwl_hal_bastream_destroy(struct mwl_hal *mh0, const MWL_HAL_BASTREAM *s) 1527193240Ssam{ 1528193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 1529193240Ssam struct mwl_hal_bastream *sp = __DECONST(struct mwl_hal_bastream *, s); 1530193240Ssam HostCmd_FW_BASTREAM *pCmd; 1531193240Ssam int retval; 1532193240Ssam 1533193240Ssam if (sp->stream >= MWL_BASTREAMS_MAX) { 1534193240Ssam /* XXX */ 1535193240Ssam return EINVAL; 1536193240Ssam } 1537193240Ssam MWL_HAL_LOCK(mh); 1538193240Ssam if (sp->setup) { 1539193240Ssam _CMD_SETUP(pCmd, HostCmd_FW_BASTREAM, HostCmd_CMD_BASTREAM); 1540193240Ssam pCmd->ActionType = htole32(BaDestroyStream); 1541193240Ssam pCmd->BaInfo.DestroyParams.FwBaContext.Context = 1542193240Ssam htole32(sp->stream); 1543193240Ssam 1544193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_BASTREAM); 1545193240Ssam } else 1546193240Ssam retval = 0; 1547193240Ssam /* NB: always reclaim stream */ 1548193240Ssam mh->mh_bastreams |= 1<<sp->stream; 1549193240Ssam sp->public.data[0] = NULL; 1550193240Ssam sp->public.data[1] = NULL; 1551193240Ssam sp->setup = 0; 1552193240Ssam MWL_HAL_UNLOCK(mh); 1553193240Ssam return retval; 1554193240Ssam} 1555193240Ssam 1556193240Ssamint 1557193240Ssammwl_hal_bastream_get_seqno(struct mwl_hal *mh0, 1558195171Ssam const MWL_HAL_BASTREAM *s, const uint8_t Macaddr[IEEE80211_ADDR_LEN], 1559195171Ssam uint16_t *pseqno) 1560193240Ssam{ 1561193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 1562193240Ssam struct mwl_hal_bastream *sp = __DECONST(struct mwl_hal_bastream *, s); 1563193240Ssam HostCmd_GET_SEQNO *pCmd; 1564193240Ssam int retval; 1565193240Ssam 1566193240Ssam MWL_HAL_LOCK(mh); 1567193240Ssam _CMD_SETUP(pCmd, HostCmd_GET_SEQNO, HostCmd_CMD_GET_SEQNO); 1568195171Ssam IEEE80211_ADDR_COPY(pCmd->MacAddr, Macaddr); 1569193240Ssam pCmd->TID = sp->tid; 1570193240Ssam 1571193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_GET_SEQNO); 1572193240Ssam if (retval == 0) 1573193240Ssam *pseqno = le16toh(pCmd->SeqNo); 1574193240Ssam MWL_HAL_UNLOCK(mh); 1575193240Ssam return retval; 1576193240Ssam} 1577193240Ssam 1578193240Ssamint 1579193240Ssammwl_hal_getwatchdogbitmap(struct mwl_hal *mh0, uint8_t bitmap[1]) 1580193240Ssam{ 1581193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 1582193240Ssam HostCmd_FW_GET_WATCHDOG_BITMAP *pCmd; 1583193240Ssam int retval; 1584193240Ssam 1585193240Ssam MWL_HAL_LOCK(mh); 1586193240Ssam _CMD_SETUP(pCmd, HostCmd_FW_GET_WATCHDOG_BITMAP, 1587193240Ssam HostCmd_CMD_GET_WATCHDOG_BITMAP); 1588193240Ssam 1589193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_GET_WATCHDOG_BITMAP); 1590193240Ssam if (retval == 0) { 1591193240Ssam bitmap[0] = pCmd->Watchdogbitmap; 1592193240Ssam /* fw returns qid, map it to BA stream */ 1593193240Ssam if (bitmap[0] < MWL_BAQID_MAX) 1594193240Ssam bitmap[0] = qid2ba[bitmap[0]]; 1595193240Ssam } 1596193240Ssam MWL_HAL_UNLOCK(mh); 1597193240Ssam return retval; 1598193240Ssam} 1599193240Ssam 1600195171Ssam/* 1601195171Ssam * Configure aggressive Ampdu rate mode. 1602195171Ssam */ 1603195171Ssamint 1604195171Ssammwl_hal_setaggampduratemode(struct mwl_hal *mh0, int mode, int threshold) 1605195171Ssam{ 1606195171Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 1607195171Ssam HostCmd_FW_AMPDU_RETRY_RATEDROP_MODE *pCmd; 1608195171Ssam int retval; 1609195171Ssam 1610195171Ssam MWL_HAL_LOCK(mh); 1611195171Ssam _CMD_SETUP(pCmd, HostCmd_FW_AMPDU_RETRY_RATEDROP_MODE, 1612195171Ssam HostCmd_CMD_AMPDU_RETRY_RATEDROP_MODE); 1613195171Ssam pCmd->Action = htole16(1); 1614195171Ssam pCmd->Option = htole32(mode); 1615195171Ssam pCmd->Threshold = htole32(threshold); 1616195171Ssam 1617195171Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_AMPDU_RETRY_RATEDROP_MODE); 1618195171Ssam MWL_HAL_UNLOCK(mh); 1619195171Ssam return retval; 1620195171Ssam} 1621195171Ssam 1622195171Ssamint 1623195171Ssammwl_hal_getaggampduratemode(struct mwl_hal *mh0, int *mode, int *threshold) 1624195171Ssam{ 1625195171Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 1626195171Ssam HostCmd_FW_AMPDU_RETRY_RATEDROP_MODE *pCmd; 1627195171Ssam int retval; 1628195171Ssam 1629195171Ssam MWL_HAL_LOCK(mh); 1630195171Ssam _CMD_SETUP(pCmd, HostCmd_FW_AMPDU_RETRY_RATEDROP_MODE, 1631195171Ssam HostCmd_CMD_AMPDU_RETRY_RATEDROP_MODE); 1632195171Ssam pCmd->Action = htole16(0); 1633195171Ssam 1634195171Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_AMPDU_RETRY_RATEDROP_MODE); 1635195171Ssam MWL_HAL_UNLOCK(mh); 1636195171Ssam *mode = le32toh(pCmd->Option); 1637195171Ssam *threshold = le32toh(pCmd->Threshold); 1638195171Ssam return retval; 1639195171Ssam} 1640195171Ssam 1641195171Ssam/* 1642195171Ssam * Set CFEND status Enable/Disable 1643195171Ssam */ 1644195171Ssamint 1645195171Ssammwl_hal_setcfend(struct mwl_hal *mh0, int ena) 1646195171Ssam{ 1647195171Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 1648195171Ssam HostCmd_CFEND_ENABLE *pCmd; 1649195171Ssam int retval; 1650195171Ssam 1651195171Ssam MWL_HAL_LOCK(mh); 1652195171Ssam _CMD_SETUP(pCmd, HostCmd_CFEND_ENABLE, 1653195171Ssam HostCmd_CMD_CFEND_ENABLE); 1654195171Ssam pCmd->Enable = htole32(ena); 1655195171Ssam 1656195171Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_CFEND_ENABLE); 1657195171Ssam MWL_HAL_UNLOCK(mh); 1658195171Ssam return retval; 1659195171Ssam} 1660195171Ssam 1661195171Ssamint 1662195171Ssammwl_hal_setdwds(struct mwl_hal *mh0, int ena) 1663195171Ssam{ 1664195171Ssam HostCmd_DWDS_ENABLE *pCmd; 1665195171Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 1666195171Ssam int retval; 1667195171Ssam 1668195171Ssam MWL_HAL_LOCK(mh); 1669195171Ssam _CMD_SETUP(pCmd, HostCmd_DWDS_ENABLE, HostCmd_CMD_DWDS_ENABLE); 1670195171Ssam pCmd->Enable = htole32(ena); 1671195171Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_DWDS_ENABLE); 1672195171Ssam MWL_HAL_UNLOCK(mh); 1673195171Ssam return retval; 1674195171Ssam} 1675195171Ssam 1676193240Ssamstatic void 1677193240SsamcvtPeerInfo(PeerInfo_t *to, const MWL_HAL_PEERINFO *from) 1678193240Ssam{ 1679193240Ssam to->LegacyRateBitMap = htole32(from->LegacyRateBitMap); 1680193240Ssam to->HTRateBitMap = htole32(from->HTRateBitMap); 1681193240Ssam to->CapInfo = htole16(from->CapInfo); 1682193240Ssam to->HTCapabilitiesInfo = htole16(from->HTCapabilitiesInfo); 1683193240Ssam to->MacHTParamInfo = from->MacHTParamInfo; 1684193240Ssam to->AddHtInfo.ControlChan = from->AddHtInfo.ControlChan; 1685193240Ssam to->AddHtInfo.AddChan = from->AddHtInfo.AddChan; 1686193240Ssam to->AddHtInfo.OpMode = htole16(from->AddHtInfo.OpMode); 1687193240Ssam to->AddHtInfo.stbc = htole16(from->AddHtInfo.stbc); 1688193240Ssam} 1689193240Ssam 1690193240Ssam/* XXX station id must be in [0..63] */ 1691193240Ssamint 1692193240Ssammwl_hal_newstation(struct mwl_hal_vap *vap, 1693193240Ssam const uint8_t addr[IEEE80211_ADDR_LEN], uint16_t aid, uint16_t sid, 1694193240Ssam const MWL_HAL_PEERINFO *peer, int isQosSta, int wmeInfo) 1695193240Ssam{ 1696193240Ssam struct mwl_hal_priv *mh = MWLVAP(vap); 1697193240Ssam HostCmd_FW_SET_NEW_STN *pCmd; 1698193240Ssam int retval; 1699193240Ssam 1700193240Ssam MWL_HAL_LOCK(mh); 1701193240Ssam _VCMD_SETUP(vap, pCmd, HostCmd_FW_SET_NEW_STN, HostCmd_CMD_SET_NEW_STN); 1702193240Ssam pCmd->AID = htole16(aid); 1703193240Ssam pCmd->StnId = htole16(sid); 1704193240Ssam pCmd->Action = htole16(0); /* SET */ 1705193240Ssam if (peer != NULL) { 1706193240Ssam /* NB: must fix up byte order */ 1707193240Ssam cvtPeerInfo(&pCmd->PeerInfo, peer); 1708193240Ssam } 1709193240Ssam IEEE80211_ADDR_COPY(&pCmd->MacAddr[0], addr); 1710193240Ssam pCmd->Qosinfo = wmeInfo; 1711193240Ssam pCmd->isQosSta = (isQosSta != 0); 1712193240Ssam 1713193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_NEW_STN); 1714193240Ssam if (retval == 0 && IEEE80211_ADDR_EQ(vap->mac, addr)) 1715193240Ssam vap->flags |= MVF_STATION; 1716193240Ssam MWL_HAL_UNLOCK(mh); 1717193240Ssam return retval; 1718193240Ssam} 1719193240Ssam 1720193240Ssamint 1721193240Ssammwl_hal_delstation(struct mwl_hal_vap *vap, 1722193240Ssam const uint8_t addr[IEEE80211_ADDR_LEN]) 1723193240Ssam{ 1724193240Ssam struct mwl_hal_priv *mh = MWLVAP(vap); 1725193240Ssam HostCmd_FW_SET_NEW_STN *pCmd; 1726193240Ssam int retval, islocal; 1727193240Ssam 1728193240Ssam MWL_HAL_LOCK(mh); 1729193240Ssam islocal = IEEE80211_ADDR_EQ(vap->mac, addr); 1730193240Ssam if (!islocal || (vap->flags & MVF_STATION)) { 1731193240Ssam _VCMD_SETUP(vap, pCmd, HostCmd_FW_SET_NEW_STN, 1732193240Ssam HostCmd_CMD_SET_NEW_STN); 1733193240Ssam pCmd->Action = htole16(2); /* REMOVE */ 1734193240Ssam IEEE80211_ADDR_COPY(&pCmd->MacAddr[0], addr); 1735193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_NEW_STN); 1736193240Ssam if (islocal) 1737193240Ssam vap->flags &= ~MVF_STATION; 1738193240Ssam } else 1739193240Ssam retval = 0; 1740193240Ssam MWL_HAL_UNLOCK(mh); 1741193240Ssam return retval; 1742193240Ssam} 1743193240Ssam 1744193240Ssam/* 1745193240Ssam * Prod the firmware to age packets on station power 1746193240Ssam * save queues and reap frames on the tx aggregation q's. 1747193240Ssam */ 1748193240Ssamint 1749193240Ssammwl_hal_setkeepalive(struct mwl_hal *mh0) 1750193240Ssam{ 1751193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 1752193240Ssam HostCmd_FW_SET_KEEP_ALIVE_TICK *pCmd; 1753193240Ssam int retval; 1754193240Ssam 1755193240Ssam MWL_HAL_LOCK(mh); 1756193240Ssam _CMD_SETUP(pCmd, HostCmd_FW_SET_KEEP_ALIVE_TICK, 1757193240Ssam HostCmd_CMD_SET_KEEP_ALIVE); 1758193240Ssam /* 1759193240Ssam * NB: tick must be 0 to prod the f/w; 1760193240Ssam * a non-zero value is a noop. 1761193240Ssam */ 1762193240Ssam pCmd->tick = 0; 1763193240Ssam 1764193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_KEEP_ALIVE); 1765193240Ssam MWL_HAL_UNLOCK(mh); 1766193240Ssam return retval; 1767193240Ssam} 1768193240Ssam 1769193240Ssamint 1770193240Ssammwl_hal_setapmode(struct mwl_hal_vap *vap, MWL_HAL_APMODE ApMode) 1771193240Ssam{ 1772193240Ssam struct mwl_hal_priv *mh = MWLVAP(vap); 1773193240Ssam HostCmd_FW_SET_APMODE *pCmd; 1774193240Ssam int retval; 1775193240Ssam 1776193240Ssam /* XXX validate ApMode? */ 1777193240Ssam 1778193240Ssam MWL_HAL_LOCK(mh); 1779193240Ssam _VCMD_SETUP(vap, pCmd, HostCmd_FW_SET_APMODE, HostCmd_CMD_SET_APMODE); 1780193240Ssam pCmd->ApMode = ApMode; 1781193240Ssam 1782193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_APMODE); 1783193240Ssam MWL_HAL_UNLOCK(mh); 1784193240Ssam return retval; 1785193240Ssam} 1786193240Ssam 1787193240Ssamint 1788193240Ssammwl_hal_stop(struct mwl_hal_vap *vap) 1789193240Ssam{ 1790193240Ssam struct mwl_hal_priv *mh = MWLVAP(vap); 1791193240Ssam HostCmd_DS_BSS_START *pCmd; 1792193240Ssam int retval; 1793193240Ssam 1794193240Ssam MWL_HAL_LOCK(mh); 1795193240Ssam if (vap->flags & MVF_RUNNING) { 1796193240Ssam _VCMD_SETUP(vap, pCmd, HostCmd_DS_BSS_START, 1797193240Ssam HostCmd_CMD_BSS_START); 1798193240Ssam pCmd->Enable = htole32(HostCmd_ACT_GEN_OFF); 1799193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_BSS_START); 1800193240Ssam } else 1801193240Ssam retval = 0; 1802193240Ssam /* NB: mark !running regardless */ 1803193240Ssam vap->flags &= ~MVF_RUNNING; 1804193240Ssam MWL_HAL_UNLOCK(mh); 1805193240Ssam return retval; 1806193240Ssam} 1807193240Ssam 1808193240Ssamint 1809193240Ssammwl_hal_start(struct mwl_hal_vap *vap) 1810193240Ssam{ 1811193240Ssam struct mwl_hal_priv *mh = MWLVAP(vap); 1812193240Ssam HostCmd_DS_BSS_START *pCmd; 1813193240Ssam int retval; 1814193240Ssam 1815193240Ssam MWL_HAL_LOCK(mh); 1816193240Ssam _VCMD_SETUP(vap, pCmd, HostCmd_DS_BSS_START, HostCmd_CMD_BSS_START); 1817193240Ssam pCmd->Enable = htole32(HostCmd_ACT_GEN_ON); 1818193240Ssam 1819193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_BSS_START); 1820193240Ssam if (retval == 0) 1821193240Ssam vap->flags |= MVF_RUNNING; 1822193240Ssam MWL_HAL_UNLOCK(mh); 1823193240Ssam return retval; 1824193240Ssam} 1825193240Ssam 1826193240Ssamint 1827193240Ssammwl_hal_setgprot(struct mwl_hal *mh0, int prot) 1828193240Ssam{ 1829193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 1830193240Ssam HostCmd_FW_SET_G_PROTECT_FLAG *pCmd; 1831193240Ssam int retval; 1832193240Ssam 1833193240Ssam MWL_HAL_LOCK(mh); 1834193240Ssam _CMD_SETUP(pCmd, HostCmd_FW_SET_G_PROTECT_FLAG, 1835193240Ssam HostCmd_CMD_SET_G_PROTECT_FLAG); 1836193240Ssam pCmd->GProtectFlag = htole32(prot); 1837193240Ssam 1838193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_G_PROTECT_FLAG); 1839193240Ssam MWL_HAL_UNLOCK(mh); 1840193240Ssam return retval; 1841193240Ssam} 1842193240Ssam 1843193240Ssamint 1844193240Ssammwl_hal_setwmm(struct mwl_hal *mh0, int onoff) 1845193240Ssam{ 1846193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 1847193240Ssam HostCmd_FW_SetWMMMode *pCmd; 1848193240Ssam int retval; 1849193240Ssam 1850193240Ssam MWL_HAL_LOCK(mh); 1851193240Ssam _CMD_SETUP(pCmd, HostCmd_FW_SetWMMMode, 1852193240Ssam HostCmd_CMD_SET_WMM_MODE); 1853193240Ssam pCmd->Action = htole16(onoff); 1854193240Ssam 1855193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_WMM_MODE); 1856193240Ssam MWL_HAL_UNLOCK(mh); 1857193240Ssam return retval; 1858193240Ssam} 1859193240Ssam 1860193240Ssamint 1861193240Ssammwl_hal_setedcaparams(struct mwl_hal *mh0, uint8_t qnum, 1862193240Ssam uint32_t CWmin, uint32_t CWmax, uint8_t AIFSN, uint16_t TXOPLimit) 1863193240Ssam{ 1864193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 1865193240Ssam HostCmd_FW_SET_EDCA_PARAMS *pCmd; 1866193240Ssam int retval; 1867193240Ssam 1868193240Ssam MWL_HAL_LOCK(mh); 1869193240Ssam _CMD_SETUP(pCmd, HostCmd_FW_SET_EDCA_PARAMS, 1870193240Ssam HostCmd_CMD_SET_EDCA_PARAMS); 1871193240Ssam /* 1872193240Ssam * NB: CWmin and CWmax are always set. 1873193240Ssam * TxOpLimit is set if bit 0x2 is marked in Action 1874193240Ssam * AIFSN is set if bit 0x4 is marked in Action 1875193240Ssam */ 1876193240Ssam pCmd->Action = htole16(0xffff); /* NB: set everything */ 1877193240Ssam pCmd->TxOP = htole16(TXOPLimit); 1878193240Ssam pCmd->CWMax = htole32(CWmax); 1879193240Ssam pCmd->CWMin = htole32(CWmin); 1880193240Ssam pCmd->AIFSN = AIFSN; 1881193240Ssam pCmd->TxQNum = qnum; /* XXX check */ 1882193240Ssam 1883193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_EDCA_PARAMS); 1884193240Ssam MWL_HAL_UNLOCK(mh); 1885193240Ssam return retval; 1886193240Ssam} 1887193240Ssam 1888193240Ssam/* XXX 0 = indoor, 1 = outdoor */ 1889193240Ssamint 1890193240Ssammwl_hal_setrateadaptmode(struct mwl_hal *mh0, uint16_t mode) 1891193240Ssam{ 1892193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 1893193240Ssam HostCmd_DS_SET_RATE_ADAPT_MODE *pCmd; 1894193240Ssam int retval; 1895193240Ssam 1896193240Ssam MWL_HAL_LOCK(mh); 1897193240Ssam _CMD_SETUP(pCmd, HostCmd_DS_SET_RATE_ADAPT_MODE, 1898193240Ssam HostCmd_CMD_SET_RATE_ADAPT_MODE); 1899193240Ssam pCmd->Action = htole16(HostCmd_ACT_GEN_SET); 1900193240Ssam pCmd->RateAdaptMode = htole16(mode); 1901193240Ssam 1902193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_RATE_ADAPT_MODE); 1903193240Ssam MWL_HAL_UNLOCK(mh); 1904193240Ssam return retval; 1905193240Ssam} 1906193240Ssam 1907193240Ssamint 1908193240Ssammwl_hal_setcsmode(struct mwl_hal *mh0, MWL_HAL_CSMODE csmode) 1909193240Ssam{ 1910193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 1911193240Ssam HostCmd_DS_SET_LINKADAPT_CS_MODE *pCmd; 1912193240Ssam int retval; 1913193240Ssam 1914193240Ssam MWL_HAL_LOCK(mh); 1915193240Ssam _CMD_SETUP(pCmd, HostCmd_DS_SET_LINKADAPT_CS_MODE, 1916193240Ssam HostCmd_CMD_SET_LINKADAPT_CS_MODE); 1917193240Ssam pCmd->Action = htole16(HostCmd_ACT_GEN_SET); 1918193240Ssam pCmd->CSMode = htole16(csmode); 1919193240Ssam 1920193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_LINKADAPT_CS_MODE); 1921193240Ssam MWL_HAL_UNLOCK(mh); 1922193240Ssam return retval; 1923193240Ssam} 1924193240Ssam 1925193240Ssamint 1926193240Ssammwl_hal_setnprot(struct mwl_hal_vap *vap, MWL_HAL_HTPROTECT mode) 1927193240Ssam{ 1928193240Ssam struct mwl_hal_priv *mh = MWLVAP(vap); 1929193240Ssam HostCmd_FW_SET_N_PROTECT_FLAG *pCmd; 1930193240Ssam int retval; 1931193240Ssam 1932193240Ssam /* XXX validate mode */ 1933193240Ssam MWL_HAL_LOCK(mh); 1934193240Ssam _VCMD_SETUP(vap, pCmd, HostCmd_FW_SET_N_PROTECT_FLAG, 1935193240Ssam HostCmd_CMD_SET_N_PROTECT_FLAG); 1936193240Ssam pCmd->NProtectFlag = htole32(mode); 1937193240Ssam 1938193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_N_PROTECT_FLAG); 1939193240Ssam MWL_HAL_UNLOCK(mh); 1940193240Ssam return retval; 1941193240Ssam} 1942193240Ssam 1943193240Ssamint 1944193240Ssammwl_hal_setnprotmode(struct mwl_hal_vap *vap, uint8_t mode) 1945193240Ssam{ 1946193240Ssam struct mwl_hal_priv *mh = MWLVAP(vap); 1947193240Ssam HostCmd_FW_SET_N_PROTECT_OPMODE *pCmd; 1948193240Ssam int retval; 1949193240Ssam 1950193240Ssam MWL_HAL_LOCK(mh); 1951193240Ssam _VCMD_SETUP(vap, pCmd, HostCmd_FW_SET_N_PROTECT_OPMODE, 1952193240Ssam HostCmd_CMD_SET_N_PROTECT_OPMODE); 1953193240Ssam pCmd->NProtectOpMode = mode; 1954193240Ssam 1955193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_N_PROTECT_OPMODE); 1956193240Ssam MWL_HAL_UNLOCK(mh); 1957193240Ssam return retval; 1958193240Ssam} 1959193240Ssam 1960193240Ssamint 1961193240Ssammwl_hal_setoptimizationlevel(struct mwl_hal *mh0, int level) 1962193240Ssam{ 1963193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 1964193240Ssam HostCmd_FW_SET_OPTIMIZATION_LEVEL *pCmd; 1965193240Ssam int retval; 1966193240Ssam 1967193240Ssam MWL_HAL_LOCK(mh); 1968193240Ssam _CMD_SETUP(pCmd, HostCmd_FW_SET_OPTIMIZATION_LEVEL, 1969193240Ssam HostCmd_CMD_SET_OPTIMIZATION_LEVEL); 1970193240Ssam pCmd->OptLevel = level; 1971193240Ssam 1972193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_OPTIMIZATION_LEVEL); 1973193240Ssam MWL_HAL_UNLOCK(mh); 1974193240Ssam return retval; 1975193240Ssam} 1976193240Ssam 1977193240Ssamint 1978193240Ssammwl_hal_setmimops(struct mwl_hal *mh0, const uint8_t addr[IEEE80211_ADDR_LEN], 1979193240Ssam uint8_t enable, uint8_t mode) 1980193240Ssam{ 1981193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 1982193240Ssam HostCmd_FW_SET_MIMOPSHT *pCmd; 1983193240Ssam int retval; 1984193240Ssam 1985193240Ssam MWL_HAL_LOCK(mh); 1986193240Ssam _CMD_SETUP(pCmd, HostCmd_FW_SET_MIMOPSHT, HostCmd_CMD_SET_MIMOPSHT); 1987193240Ssam IEEE80211_ADDR_COPY(pCmd->Addr, addr); 1988193240Ssam pCmd->Enable = enable; 1989193240Ssam pCmd->Mode = mode; 1990193240Ssam 1991193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_MIMOPSHT); 1992193240Ssam MWL_HAL_UNLOCK(mh); 1993193240Ssam return retval; 1994193240Ssam} 1995193240Ssam 1996193240Ssamstatic int 1997193240SsammwlGetCalTable(struct mwl_hal_priv *mh, uint8_t annex, uint8_t index) 1998193240Ssam{ 1999193240Ssam HostCmd_FW_GET_CALTABLE *pCmd; 2000193240Ssam int retval; 2001193240Ssam 2002193240Ssam MWL_HAL_LOCK_ASSERT(mh); 2003193240Ssam 2004193240Ssam _CMD_SETUP(pCmd, HostCmd_FW_GET_CALTABLE, HostCmd_CMD_GET_CALTABLE); 2005193240Ssam pCmd->annex = annex; 2006193240Ssam pCmd->index = index; 2007193240Ssam memset(pCmd->calTbl, 0, sizeof(pCmd->calTbl)); 2008193240Ssam 2009193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_GET_CALTABLE); 2010193240Ssam if (retval == 0 && 2011193240Ssam pCmd->calTbl[0] != annex && annex != 0 && annex != 255) 2012193240Ssam retval = EIO; 2013193240Ssam return retval; 2014193240Ssam} 2015193240Ssam 2016193240Ssam/* 2017193240Ssam * Calculate the max tx power from the channel's cal data. 2018193240Ssam */ 2019193240Ssamstatic void 2020193240Ssamsetmaxtxpow(struct mwl_hal_channel *hc, int i, int maxix) 2021193240Ssam{ 2022193240Ssam hc->maxTxPow = hc->targetPowers[i]; 2023193240Ssam for (i++; i < maxix; i++) 2024193240Ssam if (hc->targetPowers[i] > hc->maxTxPow) 2025193240Ssam hc->maxTxPow = hc->targetPowers[i]; 2026193240Ssam} 2027193240Ssam 2028193240Ssam/* 2029193240Ssam * Construct channel info for 5GHz channels from cal data. 2030193240Ssam */ 2031193240Ssamstatic void 2032193240Ssamget5Ghz(MWL_HAL_CHANNELINFO *ci, const uint8_t table[], int len) 2033193240Ssam{ 2034193240Ssam int i, j, f, l, h; 2035193240Ssam 2036193240Ssam l = 32000; 2037193240Ssam h = 0; 2038193240Ssam j = 0; 2039193240Ssam for (i = 0; i < len; i += 4) { 2040193240Ssam struct mwl_hal_channel *hc; 2041193240Ssam 2042193240Ssam if (table[i] == 0) 2043193240Ssam continue; 2044193240Ssam f = 5000 + 5*table[i]; 2045193240Ssam if (f < l) 2046193240Ssam l = f; 2047193240Ssam if (f > h) 2048193240Ssam h = f; 2049193240Ssam hc = &ci->channels[j]; 2050193240Ssam hc->freq = f; 2051193240Ssam hc->ieee = table[i]; 2052193240Ssam memcpy(hc->targetPowers, &table[i], 4); 2053193240Ssam setmaxtxpow(hc, 1, 4); /* NB: col 1 is the freq, skip*/ 2054193240Ssam j++; 2055193240Ssam } 2056193240Ssam ci->nchannels = j; 2057193240Ssam ci->freqLow = (l == 32000) ? 0 : l; 2058193240Ssam ci->freqHigh = h; 2059193240Ssam} 2060193240Ssam 2061193240Ssamstatic uint16_t 2062193240Ssamieee2mhz(int chan) 2063193240Ssam{ 2064193240Ssam if (chan == 14) 2065193240Ssam return 2484; 2066193240Ssam if (chan < 14) 2067193240Ssam return 2407 + chan*5; 2068193240Ssam return 2512 + (chan-15)*20; 2069193240Ssam} 2070193240Ssam 2071193240Ssam/* 2072193240Ssam * Construct channel info for 2.4GHz channels from cal data. 2073193240Ssam */ 2074193240Ssamstatic void 2075193240Ssamget2Ghz(MWL_HAL_CHANNELINFO *ci, const uint8_t table[], int len) 2076193240Ssam{ 2077193240Ssam int i, j; 2078193240Ssam 2079193240Ssam j = 0; 2080193240Ssam for (i = 0; i < len; i += 4) { 2081193240Ssam struct mwl_hal_channel *hc = &ci->channels[j]; 2082193240Ssam hc->ieee = 1+j; 2083193240Ssam hc->freq = ieee2mhz(1+j); 2084193240Ssam memcpy(hc->targetPowers, &table[i], 4); 2085193240Ssam setmaxtxpow(hc, 0, 4); 2086193240Ssam j++; 2087193240Ssam } 2088193240Ssam ci->nchannels = j; 2089193240Ssam ci->freqLow = ieee2mhz(1); 2090193240Ssam ci->freqHigh = ieee2mhz(j); 2091193240Ssam} 2092193240Ssam 2093193240Ssam#undef DUMPCALDATA 2094193240Ssam#ifdef DUMPCALDATA 2095193240Ssamstatic void 2096193240Ssamdumpcaldata(const char *name, const uint8_t *table, int n) 2097193240Ssam{ 2098193240Ssam int i; 2099193240Ssam printf("\n%s:\n", name); 2100193240Ssam for (i = 0; i < n; i += 4) 2101193240Ssam printf("[%2d] %3d %3d %3d %3d\n", i/4, table[i+0], table[i+1], table[i+2], table[i+3]); 2102193240Ssam} 2103193240Ssam#endif 2104193240Ssam 2105193240Ssamstatic int 2106193240SsammwlGetPwrCalTable(struct mwl_hal_priv *mh) 2107193240Ssam{ 2108193240Ssam const uint8_t *data; 2109193240Ssam MWL_HAL_CHANNELINFO *ci; 2110193240Ssam int len; 2111193240Ssam 2112193240Ssam MWL_HAL_LOCK(mh); 2113193240Ssam /* NB: we hold the lock so it's ok to use cmdbuf */ 2114193240Ssam data = ((const HostCmd_FW_GET_CALTABLE *) mh->mh_cmdbuf)->calTbl; 2115193240Ssam if (mwlGetCalTable(mh, 33, 0) == 0) { 2116193240Ssam len = (data[2] | (data[3] << 8)) - 12; 2117193240Ssam if (len > PWTAGETRATETABLE20M) 2118193240Ssam len = PWTAGETRATETABLE20M; 2119193240Ssam#ifdef DUMPCALDATA 2120193240Ssamdumpcaldata("2.4G 20M", &data[12], len);/*XXX*/ 2121193240Ssam#endif 2122193240Ssam get2Ghz(&mh->mh_20M, &data[12], len); 2123193240Ssam } 2124193240Ssam if (mwlGetCalTable(mh, 34, 0) == 0) { 2125193240Ssam len = (data[2] | (data[3] << 8)) - 12; 2126193240Ssam if (len > PWTAGETRATETABLE40M) 2127193240Ssam len = PWTAGETRATETABLE40M; 2128193240Ssam#ifdef DUMPCALDATA 2129193240Ssamdumpcaldata("2.4G 40M", &data[12], len);/*XXX*/ 2130193240Ssam#endif 2131193240Ssam ci = &mh->mh_40M; 2132193240Ssam get2Ghz(ci, &data[12], len); 2133193240Ssam } 2134193240Ssam if (mwlGetCalTable(mh, 35, 0) == 0) { 2135193240Ssam len = (data[2] | (data[3] << 8)) - 20; 2136193240Ssam if (len > PWTAGETRATETABLE20M_5G) 2137193240Ssam len = PWTAGETRATETABLE20M_5G; 2138193240Ssam#ifdef DUMPCALDATA 2139193240Ssamdumpcaldata("5G 20M", &data[20], len);/*XXX*/ 2140193240Ssam#endif 2141193240Ssam get5Ghz(&mh->mh_20M_5G, &data[20], len); 2142193240Ssam } 2143193240Ssam if (mwlGetCalTable(mh, 36, 0) == 0) { 2144193240Ssam len = (data[2] | (data[3] << 8)) - 20; 2145193240Ssam if (len > PWTAGETRATETABLE40M_5G) 2146193240Ssam len = PWTAGETRATETABLE40M_5G; 2147193240Ssam#ifdef DUMPCALDATA 2148193240Ssamdumpcaldata("5G 40M", &data[20], len);/*XXX*/ 2149193240Ssam#endif 2150193240Ssam ci = &mh->mh_40M_5G; 2151193240Ssam get5Ghz(ci, &data[20], len); 2152193240Ssam } 2153193240Ssam mh->mh_flags |= MHF_CALDATA; 2154193240Ssam MWL_HAL_UNLOCK(mh); 2155193240Ssam return 0; 2156193240Ssam} 2157193240Ssam 2158193240Ssamint 2159193240Ssammwl_hal_getregioncode(struct mwl_hal *mh0, uint8_t *countryCode) 2160193240Ssam{ 2161193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 2162193240Ssam int retval; 2163193240Ssam 2164193240Ssam MWL_HAL_LOCK(mh); 2165193240Ssam retval = mwlGetCalTable(mh, 0, 0); 2166193240Ssam if (retval == 0) { 2167193240Ssam const HostCmd_FW_GET_CALTABLE *pCmd = 2168193240Ssam (const HostCmd_FW_GET_CALTABLE *) mh->mh_cmdbuf; 2169193240Ssam *countryCode = pCmd->calTbl[16]; 2170193240Ssam } 2171193240Ssam MWL_HAL_UNLOCK(mh); 2172193240Ssam return retval; 2173193240Ssam} 2174193240Ssam 2175193240Ssamint 2176193240Ssammwl_hal_setpromisc(struct mwl_hal *mh0, int ena) 2177193240Ssam{ 2178193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 2179193240Ssam uint32_t v; 2180193240Ssam 2181193240Ssam MWL_HAL_LOCK(mh); 2182193240Ssam v = RD4(mh, MACREG_REG_PROMISCUOUS); 2183193240Ssam WR4(mh, MACREG_REG_PROMISCUOUS, ena ? v | 1 : v &~ 1); 2184193240Ssam MWL_HAL_UNLOCK(mh); 2185193240Ssam return 0; 2186193240Ssam} 2187193240Ssam 2188193240Ssamint 2189193240Ssammwl_hal_getpromisc(struct mwl_hal *mh0) 2190193240Ssam{ 2191193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 2192193240Ssam uint32_t v; 2193193240Ssam 2194193240Ssam MWL_HAL_LOCK(mh); 2195193240Ssam v = RD4(mh, MACREG_REG_PROMISCUOUS); 2196193240Ssam MWL_HAL_UNLOCK(mh); 2197193240Ssam return (v & 1) != 0; 2198193240Ssam} 2199193240Ssam 2200193240Ssamint 2201193240Ssammwl_hal_GetBeacon(struct mwl_hal *mh0, uint8_t *pBcn, uint16_t *pLen) 2202193240Ssam{ 2203193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 2204193240Ssam HostCmd_FW_GET_BEACON *pCmd; 2205193240Ssam int retval; 2206193240Ssam 2207193240Ssam MWL_HAL_LOCK(mh); 2208193240Ssam _CMD_SETUP(pCmd, HostCmd_FW_GET_BEACON, HostCmd_CMD_GET_BEACON); 2209193240Ssam pCmd->Bcnlen = htole16(0); 2210193240Ssam 2211193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_GET_BEACON); 2212193240Ssam if (retval == 0) { 2213193240Ssam /* XXX bounds check */ 2214193240Ssam memcpy(pBcn, &pCmd->Bcn, pCmd->Bcnlen); 2215193240Ssam *pLen = pCmd->Bcnlen; 2216193240Ssam } 2217193240Ssam MWL_HAL_UNLOCK(mh); 2218193240Ssam return retval; 2219193240Ssam} 2220193240Ssam 2221193240Ssamint 2222193240Ssammwl_hal_SetRifs(struct mwl_hal *mh0, uint8_t QNum) 2223193240Ssam{ 2224193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 2225193240Ssam HostCmd_FW_SET_RIFS *pCmd; 2226193240Ssam int retval; 2227193240Ssam 2228193240Ssam MWL_HAL_LOCK(mh); 2229193240Ssam _CMD_SETUP(pCmd, HostCmd_FW_SET_RIFS, HostCmd_CMD_SET_RIFS); 2230193240Ssam pCmd->QNum = QNum; 2231193240Ssam 2232193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_RIFS); 2233193240Ssam MWL_HAL_UNLOCK(mh); 2234193240Ssam return retval; 2235193240Ssam} 2236193240Ssam 2237193240Ssam/* 2238193240Ssam * Diagnostic api's for set/get registers. 2239193240Ssam */ 2240193240Ssam 2241193240Ssamstatic int 2242193240SsamgetRFReg(struct mwl_hal_priv *mh, int flag, uint32_t reg, uint32_t *val) 2243193240Ssam{ 2244193240Ssam HostCmd_DS_RF_REG_ACCESS *pCmd; 2245193240Ssam int retval; 2246193240Ssam 2247193240Ssam MWL_HAL_LOCK(mh); 2248193240Ssam _CMD_SETUP(pCmd, HostCmd_DS_RF_REG_ACCESS, HostCmd_CMD_RF_REG_ACCESS); 2249193240Ssam pCmd->Offset = htole16(reg); 2250193240Ssam pCmd->Action = htole16(flag); 2251193240Ssam pCmd->Value = htole32(*val); 2252193240Ssam 2253193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_RF_REG_ACCESS); 2254193240Ssam if (retval == 0) 2255193240Ssam *val = pCmd->Value; 2256193240Ssam MWL_HAL_UNLOCK(mh); 2257193240Ssam return retval; 2258193240Ssam} 2259193240Ssam 2260193240Ssamstatic int 2261193240SsamgetBBReg(struct mwl_hal_priv *mh, int flag, uint32_t reg, uint32_t *val) 2262193240Ssam{ 2263193240Ssam HostCmd_DS_BBP_REG_ACCESS *pCmd; 2264193240Ssam int retval; 2265193240Ssam 2266193240Ssam MWL_HAL_LOCK(mh); 2267193240Ssam _CMD_SETUP(pCmd, HostCmd_DS_BBP_REG_ACCESS, HostCmd_CMD_BBP_REG_ACCESS); 2268193240Ssam pCmd->Offset = htole16(reg); 2269193240Ssam pCmd->Action = htole16(flag); 2270193240Ssam pCmd->Value = htole32(*val); 2271193240Ssam 2272193240Ssam retval = mwlExecuteCmd(mh, HostCmd_CMD_BBP_REG_ACCESS); 2273193240Ssam if (retval == 0) 2274193240Ssam *val = pCmd->Value; 2275193240Ssam MWL_HAL_UNLOCK(mh); 2276193240Ssam return retval; 2277193240Ssam} 2278193240Ssam 2279193240Ssamstatic u_int 2280193240Ssammwl_hal_getregdump(struct mwl_hal_priv *mh, const MWL_DIAG_REGRANGE *regs, 2281193240Ssam void *dstbuf, int space) 2282193240Ssam{ 2283193240Ssam uint32_t *dp = dstbuf; 2284193240Ssam int i; 2285193240Ssam 2286193240Ssam for (i = 0; space >= 2*sizeof(uint32_t); i++) { 2287193240Ssam u_int r = regs[i].start; 2288193240Ssam u_int e = regs[i].end; 2289193240Ssam *dp++ = (r<<16) | e; 2290193240Ssam space -= sizeof(uint32_t); 2291193240Ssam do { 2292193240Ssam if (MWL_DIAG_ISMAC(r)) 2293193240Ssam *dp = RD4(mh, r); 2294193240Ssam else if (MWL_DIAG_ISBB(r)) 2295193240Ssam getBBReg(mh, HostCmd_ACT_GEN_READ, 2296193240Ssam r - MWL_DIAG_BASE_BB, dp); 2297193240Ssam else if (MWL_DIAG_ISRF(r)) 2298193240Ssam getRFReg(mh, HostCmd_ACT_GEN_READ, 2299193240Ssam r - MWL_DIAG_BASE_RF, dp); 2300193240Ssam else if (r < 0x1000 || r == MACREG_REG_FW_PRESENT) 2301193240Ssam *dp = RD4(mh, r); 2302193240Ssam else 2303193240Ssam *dp = 0xffffffff; 2304193240Ssam dp++; 2305193240Ssam r += sizeof(uint32_t); 2306193240Ssam space -= sizeof(uint32_t); 2307193240Ssam } while (r <= e && space >= sizeof(uint32_t)); 2308193240Ssam } 2309193240Ssam return (char *) dp - (char *) dstbuf; 2310193240Ssam} 2311193240Ssam 2312193240Ssamint 2313193240Ssammwl_hal_getdiagstate(struct mwl_hal *mh0, int request, 2314193240Ssam const void *args, uint32_t argsize, 2315193240Ssam void **result, uint32_t *resultsize) 2316193240Ssam{ 2317193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 2318193240Ssam 2319193240Ssam switch (request) { 2320193240Ssam case MWL_DIAG_CMD_REVS: 2321193240Ssam *result = &mh->mh_revs; 2322193240Ssam *resultsize = sizeof(mh->mh_revs); 2323193240Ssam return 1; 2324193240Ssam case MWL_DIAG_CMD_REGS: 2325193240Ssam *resultsize = mwl_hal_getregdump(mh, args, *result, *resultsize); 2326193240Ssam return 1; 2327193240Ssam case MWL_DIAG_CMD_HOSTCMD: { 2328193240Ssam FWCmdHdr *pCmd = (FWCmdHdr *) &mh->mh_cmdbuf[0]; 2329193240Ssam int retval; 2330193240Ssam 2331193240Ssam MWL_HAL_LOCK(mh); 2332193240Ssam memcpy(pCmd, args, argsize); 2333193240Ssam retval = mwlExecuteCmd(mh, le16toh(pCmd->Cmd)); 2334193240Ssam *result = (*resultsize != 0) ? pCmd : NULL; 2335193240Ssam MWL_HAL_UNLOCK(mh); 2336193240Ssam return (retval == 0); 2337193240Ssam } 2338193240Ssam case MWL_DIAG_CMD_FWLOAD: 2339193240Ssam if (mwl_hal_fwload(mh0, __DECONST(void *, args))) { 2340193240Ssam device_printf(mh->mh_dev, "problem loading fw image\n"); 2341193240Ssam return 0; 2342193240Ssam } 2343193240Ssam return 1; 2344193240Ssam } 2345193240Ssam return 0; 2346193240Ssam} 2347193240Ssam 2348193240Ssam/* 2349193240Ssam * Low level firmware cmd block handshake support. 2350193240Ssam */ 2351193240Ssam 2352193240Ssamstatic void 2353193240SsammwlSendCmd(struct mwl_hal_priv *mh) 2354193240Ssam{ 2355193240Ssam uint32_t dummy; 2356193240Ssam 2357193240Ssam bus_dmamap_sync(mh->mh_dmat, mh->mh_dmamap, 2358193240Ssam BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 2359193240Ssam 2360193240Ssam WR4(mh, MACREG_REG_GEN_PTR, mh->mh_cmdaddr); 2361193240Ssam dummy = RD4(mh, MACREG_REG_INT_CODE); 2362193240Ssam 2363193240Ssam WR4(mh, MACREG_REG_H2A_INTERRUPT_EVENTS, MACREG_H2ARIC_BIT_DOOR_BELL); 2364193240Ssam} 2365193240Ssam 2366193240Ssamstatic int 2367193240SsammwlWaitForCmdComplete(struct mwl_hal_priv *mh, uint16_t cmdCode) 2368193240Ssam{ 2369193240Ssam#define MAX_WAIT_FW_COMPLETE_ITERATIONS 10000 2370193240Ssam int i; 2371193240Ssam 2372193240Ssam for (i = 0; i < MAX_WAIT_FW_COMPLETE_ITERATIONS; i++) { 2373193240Ssam if (mh->mh_cmdbuf[0] == le16toh(cmdCode)) 2374193240Ssam return 1; 2375193240Ssam DELAY(1*1000); 2376193240Ssam } 2377193240Ssam return 0; 2378193240Ssam#undef MAX_WAIT_FW_COMPLETE_ITERATIONS 2379193240Ssam} 2380193240Ssam 2381193240Ssamstatic int 2382193240SsammwlExecuteCmd(struct mwl_hal_priv *mh, unsigned short cmd) 2383193240Ssam{ 2384193240Ssam 2385193240Ssam MWL_HAL_LOCK_ASSERT(mh); 2386193240Ssam 2387193240Ssam if ((mh->mh_flags & MHF_FWHANG) && 2388193240Ssam (mh->mh_debug & MWL_HAL_DEBUG_IGNHANG) == 0) { 2389193240Ssam#ifdef MWLHAL_DEBUG 2390193240Ssam device_printf(mh->mh_dev, "firmware hung, skipping cmd %s\n", 2391193240Ssam mwlcmdname(cmd)); 2392193240Ssam#else 2393193240Ssam device_printf(mh->mh_dev, "firmware hung, skipping cmd 0x%x\n", 2394193240Ssam cmd); 2395193240Ssam#endif 2396193240Ssam return ENXIO; 2397193240Ssam } 2398193240Ssam if (RD4(mh, MACREG_REG_INT_CODE) == 0xffffffff) { 2399193240Ssam device_printf(mh->mh_dev, "%s: device not present!\n", 2400193240Ssam __func__); 2401193240Ssam return EIO; 2402193240Ssam } 2403193240Ssam#ifdef MWLHAL_DEBUG 2404193240Ssam if (mh->mh_debug & MWL_HAL_DEBUG_SENDCMD) 2405193240Ssam dumpresult(mh, 0); 2406193240Ssam#endif 2407193240Ssam mwlSendCmd(mh); 2408193240Ssam if (!mwlWaitForCmdComplete(mh, 0x8000 | cmd)) { 2409193240Ssam#ifdef MWLHAL_DEBUG 2410193240Ssam device_printf(mh->mh_dev, 2411193240Ssam "timeout waiting for f/w cmd %s\n", mwlcmdname(cmd)); 2412193240Ssam#else 2413193240Ssam device_printf(mh->mh_dev, 2414193240Ssam "timeout waiting for f/w cmd 0x%x\n", cmd); 2415193240Ssam#endif 2416193240Ssam mh->mh_flags |= MHF_FWHANG; 2417193240Ssam return ETIMEDOUT; 2418193240Ssam } 2419193240Ssam bus_dmamap_sync(mh->mh_dmat, mh->mh_dmamap, 2420193240Ssam BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 2421193240Ssam#ifdef MWLHAL_DEBUG 2422193240Ssam if (mh->mh_debug & MWL_HAL_DEBUG_CMDDONE) 2423193240Ssam dumpresult(mh, 1); 2424193240Ssam#endif 2425193240Ssam return 0; 2426193240Ssam} 2427193240Ssam 2428193240Ssam/* 2429193240Ssam * Firmware download support. 2430193240Ssam */ 2431193240Ssam#define FW_DOWNLOAD_BLOCK_SIZE 256 2432193240Ssam#define FW_CHECK_USECS (5*1000) /* 5ms */ 2433193240Ssam#define FW_MAX_NUM_CHECKS 200 2434193240Ssam 2435193240Ssam#if 0 2436193240Ssam/* XXX read f/w from file */ 2437193240Ssam#include <dev/mwl/mwlbootfw.h> 2438193240Ssam#include <dev/mwl/mwl88W8363fw.h> 2439193240Ssam#endif 2440193240Ssam 2441193240Ssamstatic void 2442193240SsammwlFwReset(struct mwl_hal_priv *mh) 2443193240Ssam{ 2444193240Ssam if (RD4(mh, MACREG_REG_INT_CODE) == 0xffffffff) { 2445193240Ssam device_printf(mh->mh_dev, "%s: device not present!\n", 2446193240Ssam __func__); 2447193240Ssam return; 2448193240Ssam } 2449193240Ssam WR4(mh, MACREG_REG_H2A_INTERRUPT_EVENTS, ISR_RESET); 2450193240Ssam mh->mh_flags &= ~MHF_FWHANG; 2451193240Ssam} 2452193240Ssam 2453193240Ssamstatic void 2454193240SsammwlTriggerPciCmd(struct mwl_hal_priv *mh) 2455193240Ssam{ 2456193240Ssam uint32_t dummy; 2457193240Ssam 2458193240Ssam bus_dmamap_sync(mh->mh_dmat, mh->mh_dmamap, BUS_DMASYNC_PREWRITE); 2459193240Ssam 2460193240Ssam WR4(mh, MACREG_REG_GEN_PTR, mh->mh_cmdaddr); 2461193240Ssam dummy = RD4(mh, MACREG_REG_INT_CODE); 2462193240Ssam 2463193240Ssam WR4(mh, MACREG_REG_INT_CODE, 0x00); 2464193240Ssam dummy = RD4(mh, MACREG_REG_INT_CODE); 2465193240Ssam 2466193240Ssam WR4(mh, MACREG_REG_H2A_INTERRUPT_EVENTS, MACREG_H2ARIC_BIT_DOOR_BELL); 2467193240Ssam dummy = RD4(mh, MACREG_REG_INT_CODE); 2468193240Ssam} 2469193240Ssam 2470193240Ssamstatic int 2471193240SsammwlWaitFor(struct mwl_hal_priv *mh, uint32_t val) 2472193240Ssam{ 2473193240Ssam int i; 2474193240Ssam 2475193240Ssam for (i = 0; i < FW_MAX_NUM_CHECKS; i++) { 2476193240Ssam DELAY(FW_CHECK_USECS); 2477193240Ssam if (RD4(mh, MACREG_REG_INT_CODE) == val) 2478193240Ssam return 1; 2479193240Ssam } 2480193240Ssam return 0; 2481193240Ssam} 2482193240Ssam 2483193240Ssam/* 2484193240Ssam * Firmware block xmit when talking to the boot-rom. 2485193240Ssam */ 2486193240Ssamstatic int 2487193240SsammwlSendBlock(struct mwl_hal_priv *mh, int bsize, const void *data, size_t dsize) 2488193240Ssam{ 2489193240Ssam mh->mh_cmdbuf[0] = htole16(HostCmd_CMD_CODE_DNLD); 2490193240Ssam mh->mh_cmdbuf[1] = htole16(bsize); 2491193240Ssam memcpy(&mh->mh_cmdbuf[4], data , dsize); 2492193240Ssam mwlTriggerPciCmd(mh); 2493193240Ssam /* XXX 2000 vs 200 */ 2494193240Ssam if (mwlWaitFor(mh, MACREG_INT_CODE_CMD_FINISHED)) { 2495193240Ssam WR4(mh, MACREG_REG_INT_CODE, 0); 2496193240Ssam return 1; 2497193240Ssam } 2498193240Ssam device_printf(mh->mh_dev, 2499193240Ssam "%s: timeout waiting for CMD_FINISHED, INT_CODE 0x%x\n", 2500193240Ssam __func__, RD4(mh, MACREG_REG_INT_CODE)); 2501193240Ssam return 0; 2502193240Ssam} 2503193240Ssam 2504193240Ssam/* 2505193240Ssam * Firmware block xmit when talking to the 1st-stage loader. 2506193240Ssam */ 2507193240Ssamstatic int 2508193240SsammwlSendBlock2(struct mwl_hal_priv *mh, const void *data, size_t dsize) 2509193240Ssam{ 2510193240Ssam memcpy(&mh->mh_cmdbuf[0], data, dsize); 2511193240Ssam mwlTriggerPciCmd(mh); 2512193240Ssam if (mwlWaitFor(mh, MACREG_INT_CODE_CMD_FINISHED)) { 2513193240Ssam WR4(mh, MACREG_REG_INT_CODE, 0); 2514193240Ssam return 1; 2515193240Ssam } 2516193240Ssam device_printf(mh->mh_dev, 2517193240Ssam "%s: timeout waiting for CMD_FINISHED, INT_CODE 0x%x\n", 2518193240Ssam __func__, RD4(mh, MACREG_REG_INT_CODE)); 2519193240Ssam return 0; 2520193240Ssam} 2521193240Ssam 2522193240Ssamstatic void 2523193240SsammwlPokeSdramController(struct mwl_hal_priv *mh, int SDRAMSIZE_Addr) 2524193240Ssam{ 2525193240Ssam /** Set up sdram controller for superflyv2 **/ 2526193240Ssam WR4(mh, 0x00006014, 0x33); 2527193240Ssam WR4(mh, 0x00006018, 0xa3a2632); 2528193240Ssam WR4(mh, 0x00006010, SDRAMSIZE_Addr); 2529193240Ssam} 2530193240Ssam 2531193240Ssamint 2532193240Ssammwl_hal_fwload(struct mwl_hal *mh0, void *fwargs) 2533193240Ssam{ 2534193240Ssam struct mwl_hal_priv *mh = MWLPRIV(mh0); 2535193240Ssam const char *fwname = "mw88W8363fw"; 2536193240Ssam const char *fwbootname = "mwlboot"; 2537193240Ssam const struct firmware *fwboot = NULL; 2538193240Ssam const struct firmware *fw; 2539193240Ssam /* XXX get from firmware header */ 2540193240Ssam uint32_t FwReadySignature = HostCmd_SOFTAP_FWRDY_SIGNATURE; 2541193240Ssam uint32_t OpMode = HostCmd_SOFTAP_MODE; 2542193240Ssam const uint8_t *fp, *ep; 2543193240Ssam const uint8_t *fmdata; 2544193240Ssam uint32_t blocksize, nbytes, fmsize; 2545193240Ssam int i, error, ntries; 2546193240Ssam 2547193240Ssam fw = firmware_get(fwname); 2548193240Ssam if (fw == NULL) { 2549193240Ssam device_printf(mh->mh_dev, 2550193240Ssam "could not load firmware image %s\n", fwname); 2551193240Ssam return ENXIO; 2552193240Ssam } 2553193240Ssam fmdata = fw->data; 2554193240Ssam fmsize = fw->datasize; 2555193240Ssam if (fmsize < 4) { 2556193240Ssam device_printf(mh->mh_dev, "firmware image %s too small\n", 2557193240Ssam fwname); 2558193240Ssam error = ENXIO; 2559193240Ssam goto bad2; 2560193240Ssam } 2561193240Ssam if (fmdata[0] == 0x01 && fmdata[1] == 0x00 && 2562193240Ssam fmdata[2] == 0x00 && fmdata[3] == 0x00) { 2563193240Ssam /* 2564193240Ssam * 2-stage load, get the boot firmware. 2565193240Ssam */ 2566193240Ssam fwboot = firmware_get(fwbootname); 2567193240Ssam if (fwboot == NULL) { 2568193240Ssam device_printf(mh->mh_dev, 2569193240Ssam "could not load firmware image %s\n", fwbootname); 2570193240Ssam error = ENXIO; 2571193240Ssam goto bad2; 2572193240Ssam } 2573193240Ssam } else 2574193240Ssam fwboot = NULL; 2575193240Ssam 2576193240Ssam mwlFwReset(mh); 2577193240Ssam 2578193240Ssam WR4(mh, MACREG_REG_A2H_INTERRUPT_CLEAR_SEL, MACREG_A2HRIC_BIT_MASK); 2579193240Ssam WR4(mh, MACREG_REG_A2H_INTERRUPT_CAUSE, 0x00); 2580193240Ssam WR4(mh, MACREG_REG_A2H_INTERRUPT_MASK, 0x00); 2581193240Ssam WR4(mh, MACREG_REG_A2H_INTERRUPT_STATUS_MASK, MACREG_A2HRIC_BIT_MASK); 2582193240Ssam if (mh->mh_SDRAMSIZE_Addr != 0) { 2583193240Ssam /** Set up sdram controller for superflyv2 **/ 2584193240Ssam mwlPokeSdramController(mh, mh->mh_SDRAMSIZE_Addr); 2585193240Ssam } 2586193240Ssam device_printf(mh->mh_dev, "load %s firmware image (%u bytes)\n", 2587193240Ssam fwname, fmsize); 2588193240Ssam if (fwboot != NULL) { 2589193240Ssam /* 2590193240Ssam * Do 2-stage load. The 1st stage loader is setup 2591193240Ssam * with the bootrom loader then we load the real 2592193240Ssam * image using a different handshake. With this 2593193240Ssam * mechanism the firmware is segmented into chunks 2594193240Ssam * that have a CRC. If a chunk is incorrect we'll 2595193240Ssam * be told to retransmit. 2596193240Ssam */ 2597193240Ssam /* XXX assumes hlpimage fits in a block */ 2598193240Ssam /* NB: zero size block indicates download is finished */ 2599193240Ssam if (!mwlSendBlock(mh, fwboot->datasize, fwboot->data, fwboot->datasize) || 2600193240Ssam !mwlSendBlock(mh, 0, NULL, 0)) { 2601193240Ssam error = ETIMEDOUT; 2602193240Ssam goto bad; 2603193240Ssam } 2604193240Ssam DELAY(200*FW_CHECK_USECS); 2605193240Ssam if (mh->mh_SDRAMSIZE_Addr != 0) { 2606193240Ssam /** Set up sdram controller for superflyv2 **/ 2607193240Ssam mwlPokeSdramController(mh, mh->mh_SDRAMSIZE_Addr); 2608193240Ssam } 2609193240Ssam nbytes = ntries = 0; /* NB: silence compiler */ 2610193240Ssam for (fp = fmdata, ep = fp + fmsize; fp < ep; ) { 2611193240Ssam WR4(mh, MACREG_REG_INT_CODE, 0); 2612193240Ssam blocksize = RD4(mh, MACREG_REG_SCRATCH); 2613193240Ssam if (blocksize == 0) /* download complete */ 2614193240Ssam break; 2615193240Ssam if (blocksize > 0x00000c00) { 2616193240Ssam error = EINVAL; 2617193240Ssam goto bad; 2618193240Ssam } 2619193240Ssam if ((blocksize & 0x1) == 0) { 2620193240Ssam /* block successfully downloaded, advance */ 2621193240Ssam fp += nbytes; 2622193240Ssam ntries = 0; 2623193240Ssam } else { 2624193240Ssam if (++ntries > 2) { 2625193240Ssam /* 2626193240Ssam * Guard against f/w telling us to 2627193240Ssam * retry infinitely. 2628193240Ssam */ 2629193240Ssam error = ELOOP; 2630193240Ssam goto bad; 2631193240Ssam } 2632193240Ssam /* clear NAK bit/flag */ 2633193240Ssam blocksize &= ~0x1; 2634193240Ssam } 2635193240Ssam if (blocksize > ep - fp) { 2636193240Ssam /* XXX this should not happen, what to do? */ 2637193240Ssam blocksize = ep - fp; 2638193240Ssam } 2639193240Ssam nbytes = blocksize; 2640193240Ssam if (!mwlSendBlock2(mh, fp, nbytes)) { 2641193240Ssam error = ETIMEDOUT; 2642193240Ssam goto bad; 2643193240Ssam } 2644193240Ssam } 2645193240Ssam } else { 2646193240Ssam for (fp = fmdata, ep = fp + fmsize; fp < ep;) { 2647193240Ssam nbytes = ep - fp; 2648193240Ssam if (nbytes > FW_DOWNLOAD_BLOCK_SIZE) 2649193240Ssam nbytes = FW_DOWNLOAD_BLOCK_SIZE; 2650193240Ssam if (!mwlSendBlock(mh, FW_DOWNLOAD_BLOCK_SIZE, fp, nbytes)) { 2651193240Ssam error = EIO; 2652193240Ssam goto bad; 2653193240Ssam } 2654193240Ssam fp += nbytes; 2655193240Ssam } 2656193240Ssam } 2657193240Ssam /* done with firmware... */ 2658193240Ssam if (fwboot != NULL) 2659193240Ssam firmware_put(fwboot, FIRMWARE_UNLOAD); 2660193240Ssam firmware_put(fw, FIRMWARE_UNLOAD); 2661193240Ssam /* 2662193240Ssam * Wait for firmware to startup; we monitor the 2663193240Ssam * INT_CODE register waiting for a signature to 2664193240Ssam * written back indicating it's ready to go. 2665193240Ssam */ 2666193240Ssam mh->mh_cmdbuf[1] = 0; 2667193240Ssam /* 2668193240Ssam * XXX WAR for mfg fw download 2669193240Ssam */ 2670193240Ssam if (OpMode != HostCmd_STA_MODE) 2671193240Ssam mwlTriggerPciCmd(mh); 2672193240Ssam for (i = 0; i < FW_MAX_NUM_CHECKS; i++) { 2673193240Ssam WR4(mh, MACREG_REG_GEN_PTR, OpMode); 2674193240Ssam DELAY(FW_CHECK_USECS); 2675193240Ssam if (RD4(mh, MACREG_REG_INT_CODE) == FwReadySignature) { 2676193240Ssam WR4(mh, MACREG_REG_INT_CODE, 0x00); 2677193240Ssam return mwlResetHalState(mh); 2678193240Ssam } 2679193240Ssam } 2680193240Ssam return ETIMEDOUT; 2681193240Ssambad: 2682193240Ssam mwlFwReset(mh); 2683193240Ssambad2: 2684193240Ssam /* done with firmware... */ 2685193240Ssam if (fwboot != NULL) 2686193240Ssam firmware_put(fwboot, FIRMWARE_UNLOAD); 2687193240Ssam firmware_put(fw, FIRMWARE_UNLOAD); 2688193240Ssam return error; 2689193240Ssam} 2690193240Ssam 2691193240Ssam#ifdef MWLHAL_DEBUG 2692193240Ssamstatic const char * 2693193240Ssammwlcmdname(int cmd) 2694193240Ssam{ 2695193240Ssam static char buf[12]; 2696193240Ssam#define CMD(x) case HostCmd_CMD_##x: return #x 2697193240Ssam switch (cmd) { 2698193240Ssam CMD(CODE_DNLD); 2699193240Ssam CMD(GET_HW_SPEC); 2700193240Ssam CMD(SET_HW_SPEC); 2701193240Ssam CMD(MAC_MULTICAST_ADR); 2702193240Ssam CMD(802_11_GET_STAT); 2703193240Ssam CMD(MAC_REG_ACCESS); 2704193240Ssam CMD(BBP_REG_ACCESS); 2705193240Ssam CMD(RF_REG_ACCESS); 2706193240Ssam CMD(802_11_RADIO_CONTROL); 2707193240Ssam CMD(802_11_RF_TX_POWER); 2708193240Ssam CMD(802_11_RF_ANTENNA); 2709193240Ssam CMD(SET_BEACON); 2710193240Ssam CMD(SET_RF_CHANNEL); 2711193240Ssam CMD(SET_AID); 2712193240Ssam CMD(SET_INFRA_MODE); 2713193240Ssam CMD(SET_G_PROTECT_FLAG); 2714193240Ssam CMD(802_11_RTS_THSD); 2715193240Ssam CMD(802_11_SET_SLOT); 2716193240Ssam CMD(SET_EDCA_PARAMS); 2717193240Ssam CMD(802_11H_DETECT_RADAR); 2718193240Ssam CMD(SET_WMM_MODE); 2719193240Ssam CMD(HT_GUARD_INTERVAL); 2720193240Ssam CMD(SET_FIXED_RATE); 2721193240Ssam CMD(SET_LINKADAPT_CS_MODE); 2722193240Ssam CMD(SET_MAC_ADDR); 2723193240Ssam CMD(SET_RATE_ADAPT_MODE); 2724193240Ssam CMD(BSS_START); 2725193240Ssam CMD(SET_NEW_STN); 2726193240Ssam CMD(SET_KEEP_ALIVE); 2727193240Ssam CMD(SET_APMODE); 2728193240Ssam CMD(SET_SWITCH_CHANNEL); 2729193240Ssam CMD(UPDATE_ENCRYPTION); 2730193240Ssam CMD(BASTREAM); 2731193240Ssam CMD(SET_RIFS); 2732193240Ssam CMD(SET_N_PROTECT_FLAG); 2733193240Ssam CMD(SET_N_PROTECT_OPMODE); 2734193240Ssam CMD(SET_OPTIMIZATION_LEVEL); 2735193240Ssam CMD(GET_CALTABLE); 2736193240Ssam CMD(SET_MIMOPSHT); 2737193240Ssam CMD(GET_BEACON); 2738193240Ssam CMD(SET_REGION_CODE); 2739193240Ssam CMD(SET_POWERSAVESTATION); 2740193240Ssam CMD(SET_TIM); 2741193240Ssam CMD(GET_TIM); 2742193240Ssam CMD(GET_SEQNO); 2743195171Ssam CMD(DWDS_ENABLE); 2744195171Ssam CMD(AMPDU_RETRY_RATEDROP_MODE); 2745195171Ssam CMD(CFEND_ENABLE); 2746193240Ssam } 2747193240Ssam snprintf(buf, sizeof(buf), "0x%x", cmd); 2748193240Ssam return buf; 2749193240Ssam#undef CMD 2750193240Ssam} 2751193240Ssam 2752193240Ssamstatic void 2753193240Ssamdumpresult(struct mwl_hal_priv *mh, int showresult) 2754193240Ssam{ 2755193240Ssam const FWCmdHdr *h = (const FWCmdHdr *)mh->mh_cmdbuf; 2756193240Ssam const uint8_t *cp; 2757193240Ssam int len, i; 2758193240Ssam 2759193240Ssam len = le16toh(h->Length); 2760193240Ssam#ifdef MWL_MBSS_SUPPORT 2761193240Ssam device_printf(mh->mh_dev, "Cmd %s Length %d SeqNum %d MacId %d", 2762193240Ssam mwlcmdname(le16toh(h->Cmd) &~ 0x8000), len, h->SeqNum, h->MacId); 2763193240Ssam#else 2764193240Ssam device_printf(mh->mh_dev, "Cmd %s Length %d SeqNum %d", 2765193240Ssam mwlcmdname(le16toh(h->Cmd) &~ 0x8000), len, le16toh(h->SeqNum)); 2766193240Ssam#endif 2767193240Ssam if (showresult) { 2768193240Ssam const char *results[] = 2769193240Ssam { "OK", "ERROR", "NOT_SUPPORT", "PENDING", "BUSY", 2770193240Ssam "PARTIAL_DATA" }; 2771193240Ssam int result = le16toh(h->Result); 2772193240Ssam 2773193240Ssam if (result <= HostCmd_RESULT_PARTIAL_DATA) 2774193240Ssam printf(" Result %s", results[result]); 2775193240Ssam else 2776193240Ssam printf(" Result %d", result); 2777193240Ssam } 2778193240Ssam cp = (const uint8_t *)h; 2779193240Ssam for (i = 0; i < len; i++) { 2780193240Ssam if ((i % 16) == 0) 2781193240Ssam printf("\n%02x", cp[i]); 2782193240Ssam else 2783193240Ssam printf(" %02x", cp[i]); 2784193240Ssam } 2785193240Ssam printf("\n"); 2786193240Ssam} 2787193240Ssam#endif /* MWLHAL_DEBUG */ 2788