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