1/*-
2 * Copyright (c) 2007-2009 Sam Leffler, Errno Consulting
3 * Copyright (c) 2007-2009 Marvell Semiconductor, Inc.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer,
11 *    without modification.
12 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
13 *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
14 *    redistribution must be conditioned upon including a substantially
15 *    similar Disclaimer requirement for further binary redistribution.
16 *
17 * NO WARRANTY
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
21 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
23 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
26 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
28 * THE POSSIBILITY OF SUCH DAMAGES.
29 *
30 * $FreeBSD$
31 */
32
33#include <sys/param.h>
34#include <sys/systm.h>
35#include <sys/sysctl.h>
36#include <sys/malloc.h>
37#include <sys/lock.h>
38#include <sys/mutex.h>
39#include <sys/kernel.h>
40#include <sys/errno.h>
41#include <sys/bus.h>
42#include <sys/endian.h>
43
44#include <sys/linker.h>
45#include <sys/firmware.h>
46
47#include <machine/bus.h>
48
49#include <dev/mwl/mwlhal.h>
50#include <dev/mwl/mwlreg.h>
51
52#include <sys/socket.h>
53#include <sys/sockio.h>
54#include <net/if.h>
55#include <dev/mwl/mwldiag.h>
56
57#define	MWLHAL_DEBUG			/* debug msgs */
58
59typedef enum {
60    WL_ANTENNAMODE_RX = 0xffff,
61    WL_ANTENNAMODE_TX = 2,
62} wlantennamode_e;
63
64typedef enum {
65    WL_TX_POWERLEVEL_LOW = 5,
66    WL_TX_POWERLEVEL_MEDIUM = 10,
67    WL_TX_POWERLEVEL_HIGH = 15,
68} wltxpowerlevel_e;
69
70#define	MWL_CMDBUF_SIZE	0x4000		/* size of f/w command buffer */
71#define	MWL_BASTREAMS_MAX	7	/* max BA streams (NB: fw >3.3.5.9) */
72#define	MWL_BAQID_MAX		8	/* max BA Q id's (NB: fw >3.3.5.9) */
73#define	MWL_MBSS_AP_MAX		8	/* max ap vap's */
74#define	MWL_MBSS_STA_MAX	24	/* max station/client vap's */
75#define	MWL_MBSS_MAX	(MWL_MBSS_AP_MAX+MWL_MBSS_STA_MAX)
76
77/*
78 * BA stream -> queue ID mapping
79 *
80 * The first 2 streams map to h/w; the remaining streams are
81 * implemented in firmware.
82 */
83static const int ba2qid[MWL_BASTREAMS_MAX] = {
84	5, 6				/* h/w supported */
85#if MWL_BASTREAMS_MAX == 7
86	, 7, 0, 1, 2, 3 		/* f/w supported */
87#endif
88};
89static int qid2ba[MWL_BAQID_MAX];
90
91#define	IEEE80211_ADDR_LEN	6	/* XXX */
92#define	IEEE80211_ADDR_COPY(_dst, _src) \
93	memcpy(_dst, _src, IEEE80211_ADDR_LEN)
94#define	IEEE80211_ADDR_EQ(_dst, _src) \
95	(memcmp(_dst, _src, IEEE80211_ADDR_LEN) == 0)
96
97#define	_CMD_SETUP(pCmd, type, cmd) do {				\
98	pCmd = (type *)&mh->mh_cmdbuf[0];				\
99	memset(pCmd, 0, sizeof(type));					\
100	pCmd->CmdHdr.Cmd = htole16(cmd);				\
101	pCmd->CmdHdr.Length = htole16(sizeof(type));			\
102} while (0)
103
104#define	_VCMD_SETUP(vap, pCmd, type, cmd) do {				\
105	_CMD_SETUP(pCmd, type, cmd);					\
106	pCmd->CmdHdr.MacId = vap->macid;				\
107} while (0)
108
109#define	PWTAGETRATETABLE20M	14*4
110#define	PWTAGETRATETABLE40M	9*4
111#define	PWTAGETRATETABLE20M_5G	35*4
112#define	PWTAGETRATETABLE40M_5G	16*4
113
114struct mwl_hal_bastream {
115	MWL_HAL_BASTREAM public;	/* public state */
116	uint8_t	stream;			/* stream # */
117	uint8_t	setup;			/* f/w cmd sent */
118	uint8_t ba_policy;		/* direct/delayed BA policy */
119	uint8_t	tid;
120	uint8_t	paraminfo;
121	uint8_t macaddr[IEEE80211_ADDR_LEN];
122};
123
124struct mwl_hal_priv;
125
126struct mwl_hal_vap {
127	struct mwl_hal_priv *mh;	/* back pointer */
128	uint16_t bss_type;		/* f/w type */
129	uint8_t vap_type;		/* MWL_HAL_BSSTYPE */
130	uint8_t	macid;			/* for passing to f/w */
131	uint8_t flags;
132#define	MVF_RUNNING	0x01		/* BSS_START issued */
133#define	MVF_STATION	0x02		/* sta db entry created */
134	uint8_t mac[IEEE80211_ADDR_LEN];/* mac address */
135};
136#define	MWLVAP(_vap)	((_vap)->mh)
137
138/*
139 * Per-device state.  We allocate a single cmd buffer for
140 * submitting operations to the firmware.  Access to this
141 * buffer (and the f/w) are single-threaded.  At present
142 * we spin waiting for cmds to complete which is bad.  Not
143 * sure if it's possible to submit multiple requests or
144 * control when we get cmd done interrupts.  There's no
145 * documentation and no example code to indicate what can
146 * or cannot be done so all we can do right now is follow the
147 * linux driver logic.  This falls apart when the f/w fails;
148 * the system comes to a crawl as we spin waiting for operations
149 * to finish.
150 */
151struct mwl_hal_priv {
152	struct mwl_hal	public;		/* public area */
153	device_t	mh_dev;
154	char		mh_mtxname[12];
155	struct mtx	mh_mtx;
156	bus_dma_tag_t	mh_dmat;	/* bus DMA tag for cmd buffer */
157	bus_dma_segment_t mh_seg;	/* segment for cmd buffer */
158	bus_dmamap_t	mh_dmamap;	/* DMA map for cmd buffer */
159	uint16_t	*mh_cmdbuf;	/* f/w cmd buffer */
160	bus_addr_t	mh_cmdaddr;	/* physaddr of cmd buffer */
161	int		mh_flags;
162#define	MHF_CALDATA	0x0001		/* cal data retrieved */
163#define	MHF_FWHANG	0x0002		/* fw appears hung */
164#define	MHF_MBSS	0x0004		/* mbss enabled */
165	struct mwl_hal_vap mh_vaps[MWL_MBSS_MAX+1];
166	int		mh_bastreams;	/* bit mask of available BA streams */
167	int		mh_regioncode;	/* XXX last region code sent to fw */
168	struct mwl_hal_bastream mh_streams[MWL_BASTREAMS_MAX];
169	int		mh_debug;
170	MWL_HAL_CHANNELINFO mh_20M;
171	MWL_HAL_CHANNELINFO mh_40M;
172	MWL_HAL_CHANNELINFO mh_20M_5G;
173	MWL_HAL_CHANNELINFO mh_40M_5G;
174	int		mh_SDRAMSIZE_Addr;
175	uint32_t	mh_RTSSuccesses;/* cumulative stats for read-on-clear */
176	uint32_t	mh_RTSFailures;
177	uint32_t	mh_RxDuplicateFrames;
178	uint32_t	mh_FCSErrorCount;
179	MWL_DIAG_REVS	mh_revs;
180};
181#define	MWLPRIV(_mh)	((struct mwl_hal_priv *)(_mh))
182
183static int mwl_hal_setmac_locked(struct mwl_hal_vap *,
184	const uint8_t addr[IEEE80211_ADDR_LEN]);
185static int mwlExecuteCmd(struct mwl_hal_priv *, unsigned short cmd);
186static int mwlGetPwrCalTable(struct mwl_hal_priv *);
187#ifdef MWLHAL_DEBUG
188static const char *mwlcmdname(int cmd);
189static void dumpresult(struct mwl_hal_priv *, int showresult);
190#endif /* MWLHAL_DEBUG */
191
192SYSCTL_DECL(_hw_mwl);
193static SYSCTL_NODE(_hw_mwl, OID_AUTO, hal, CTLFLAG_RD, 0,
194    "Marvell HAL parameters");
195
196static __inline void
197MWL_HAL_LOCK(struct mwl_hal_priv *mh)
198{
199	mtx_lock(&mh->mh_mtx);
200}
201
202static __inline void
203MWL_HAL_LOCK_ASSERT(struct mwl_hal_priv *mh)
204{
205	mtx_assert(&mh->mh_mtx, MA_OWNED);
206}
207
208static __inline void
209MWL_HAL_UNLOCK(struct mwl_hal_priv *mh)
210{
211	mtx_unlock(&mh->mh_mtx);
212}
213
214static __inline uint32_t
215RD4(struct mwl_hal_priv *mh, bus_size_t off)
216{
217	return bus_space_read_4(mh->public.mh_iot, mh->public.mh_ioh, off);
218}
219
220static __inline void
221WR4(struct mwl_hal_priv *mh, bus_size_t off, uint32_t val)
222{
223	bus_space_write_4(mh->public.mh_iot, mh->public.mh_ioh, off, val);
224}
225
226static void
227mwl_hal_load_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
228{
229	bus_addr_t *paddr = (bus_addr_t*) arg;
230	KASSERT(error == 0, ("error %u on bus_dma callback", error));
231	*paddr = segs->ds_addr;
232}
233
234/*
235 * Setup for communication with the device.  We allocate
236 * a command buffer and map it for bus dma use.  The pci
237 * device id is used to identify whether the device has
238 * SRAM on it (in which case f/w download must include a
239 * memory controller reset).  All bus i/o operations happen
240 * in BAR 1; the driver passes in the tag and handle we need.
241 */
242struct mwl_hal *
243mwl_hal_attach(device_t dev, uint16_t devid,
244    bus_space_handle_t ioh, bus_space_tag_t iot, bus_dma_tag_t tag)
245{
246	struct mwl_hal_priv *mh;
247	struct mwl_hal_vap *hvap;
248	int error, i;
249
250	mh = malloc(sizeof(struct mwl_hal_priv), M_DEVBUF, M_NOWAIT | M_ZERO);
251	if (mh == NULL)
252		return NULL;
253	mh->mh_dev = dev;
254	mh->public.mh_ioh = ioh;
255	mh->public.mh_iot = iot;
256	for (i = 0; i < MWL_BASTREAMS_MAX; i++) {
257		mh->mh_streams[i].public.txq = ba2qid[i];
258		mh->mh_streams[i].stream = i;
259		/* construct back-mapping while we're at it */
260		if (mh->mh_streams[i].public.txq < MWL_BAQID_MAX)
261			qid2ba[mh->mh_streams[i].public.txq] = i;
262		else
263			device_printf(dev, "unexpected BA tx qid %d for "
264			    "stream %d\n", mh->mh_streams[i].public.txq, i);
265	}
266	/* setup constant portion of vap state */
267	/* XXX should get max ap/client vap's from f/w */
268	i = 0;
269	hvap = &mh->mh_vaps[i];
270	hvap->vap_type = MWL_HAL_AP;
271	hvap->bss_type = htole16(WL_MAC_TYPE_PRIMARY_AP);
272	hvap->macid = 0;
273	for (i++; i < MWL_MBSS_AP_MAX; i++) {
274		hvap = &mh->mh_vaps[i];
275		hvap->vap_type = MWL_HAL_AP;
276		hvap->bss_type = htole16(WL_MAC_TYPE_SECONDARY_AP);
277		hvap->macid = i;
278	}
279	hvap = &mh->mh_vaps[i];
280	hvap->vap_type = MWL_HAL_STA;
281	hvap->bss_type = htole16(WL_MAC_TYPE_PRIMARY_CLIENT);
282	hvap->macid = i;
283	for (i++; i < MWL_MBSS_MAX; i++) {
284		hvap = &mh->mh_vaps[i];
285		hvap->vap_type = MWL_HAL_STA;
286		hvap->bss_type = htole16(WL_MAC_TYPE_SECONDARY_CLIENT);
287		hvap->macid = i;
288	}
289	mh->mh_revs.mh_devid = devid;
290	snprintf(mh->mh_mtxname, sizeof(mh->mh_mtxname),
291	    "%s_hal", device_get_nameunit(dev));
292	mtx_init(&mh->mh_mtx, mh->mh_mtxname, NULL, MTX_DEF);
293
294	/*
295	 * Allocate the command buffer and map into the address
296	 * space of the h/w.  We request "coherent" memory which
297	 * will be uncached on some architectures.
298	 */
299	error = bus_dma_tag_create(tag,		/* parent */
300		       PAGE_SIZE, 0,		/* alignment, bounds */
301		       BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
302		       BUS_SPACE_MAXADDR,	/* highaddr */
303		       NULL, NULL,		/* filter, filterarg */
304		       MWL_CMDBUF_SIZE,		/* maxsize */
305		       1,			/* nsegments */
306		       MWL_CMDBUF_SIZE,		/* maxsegsize */
307		       BUS_DMA_ALLOCNOW,	/* flags */
308		       NULL,			/* lockfunc */
309		       NULL,			/* lockarg */
310		       &mh->mh_dmat);
311	if (error != 0) {
312		device_printf(dev, "unable to allocate memory for cmd tag, "
313			"error %u\n", error);
314		goto fail0;
315	}
316
317	/* allocate descriptors */
318	error = bus_dmamem_alloc(mh->mh_dmat, (void**) &mh->mh_cmdbuf,
319				 BUS_DMA_NOWAIT | BUS_DMA_COHERENT,
320				 &mh->mh_dmamap);
321	if (error != 0) {
322		device_printf(dev, "unable to allocate memory for cmd buffer, "
323			"error %u\n", error);
324		goto fail1;
325	}
326
327	error = bus_dmamap_load(mh->mh_dmat, mh->mh_dmamap,
328				mh->mh_cmdbuf, MWL_CMDBUF_SIZE,
329				mwl_hal_load_cb, &mh->mh_cmdaddr,
330				BUS_DMA_NOWAIT);
331	if (error != 0) {
332		device_printf(dev, "unable to load cmd buffer, error %u\n",
333			error);
334		goto fail2;
335	}
336
337	/*
338	 * Some cards have SDRAM.  When loading firmware we need
339	 * to reset the SDRAM controller prior to doing this.
340	 * When the SDRAMSIZE is non-zero we do that work in
341	 * mwl_hal_fwload.
342	 */
343	switch (devid) {
344	case 0x2a02:		/* CB82 */
345	case 0x2a03:		/* CB85 */
346	case 0x2a08:		/* MC85_B1 */
347	case 0x2a0b:		/* CB85AP */
348	case 0x2a24:
349		mh->mh_SDRAMSIZE_Addr = 0x40fe70b7;	/* 8M SDRAM */
350		break;
351	case 0x2a04:		/* MC85 */
352		mh->mh_SDRAMSIZE_Addr = 0x40fc70b7;	/* 16M SDRAM */
353		break;
354	default:
355		break;
356	}
357	return &mh->public;
358fail2:
359	bus_dmamem_free(mh->mh_dmat, mh->mh_cmdbuf, mh->mh_dmamap);
360fail1:
361	bus_dma_tag_destroy(mh->mh_dmat);
362fail0:
363	mtx_destroy(&mh->mh_mtx);
364	free(mh, M_DEVBUF);
365	return NULL;
366}
367
368void
369mwl_hal_detach(struct mwl_hal *mh0)
370{
371	struct mwl_hal_priv *mh = MWLPRIV(mh0);
372
373	bus_dmamem_free(mh->mh_dmat, mh->mh_cmdbuf, mh->mh_dmamap);
374	bus_dma_tag_destroy(mh->mh_dmat);
375	mtx_destroy(&mh->mh_mtx);
376	free(mh, M_DEVBUF);
377}
378
379/*
380 * Reset internal state after a firmware download.
381 */
382static int
383mwlResetHalState(struct mwl_hal_priv *mh)
384{
385	int i;
386
387	/* XXX get from f/w */
388	mh->mh_bastreams = (1<<MWL_BASTREAMS_MAX)-1;
389	for (i = 0; i < MWL_MBSS_MAX; i++)
390		mh->mh_vaps[i].mh = NULL;
391	/*
392	 * Clear cumulative stats.
393	 */
394	mh->mh_RTSSuccesses = 0;
395	mh->mh_RTSFailures = 0;
396	mh->mh_RxDuplicateFrames = 0;
397	mh->mh_FCSErrorCount = 0;
398	/*
399	 * Fetch cal data for later use.
400	 * XXX may want to fetch other stuff too.
401	 */
402	/* XXX check return */
403	if ((mh->mh_flags & MHF_CALDATA) == 0)
404		mwlGetPwrCalTable(mh);
405	return 0;
406}
407
408struct mwl_hal_vap *
409mwl_hal_newvap(struct mwl_hal *mh0, MWL_HAL_BSSTYPE type,
410	const uint8_t mac[IEEE80211_ADDR_LEN])
411{
412	struct mwl_hal_priv *mh = MWLPRIV(mh0);
413	struct mwl_hal_vap *vap;
414	int i;
415
416	MWL_HAL_LOCK(mh);
417	/* NB: could optimize but not worth it w/ max 32 bss */
418	for (i = 0; i < MWL_MBSS_MAX; i++) {
419		vap = &mh->mh_vaps[i];
420		if (vap->vap_type == type && vap->mh == NULL) {
421			vap->mh = mh;
422			mwl_hal_setmac_locked(vap, mac);
423			break;
424		}
425	}
426	MWL_HAL_UNLOCK(mh);
427	return (i < MWL_MBSS_MAX) ? vap : NULL;
428}
429
430void
431mwl_hal_delvap(struct mwl_hal_vap *vap)
432{
433	/* NB: locking not needed for single write */
434	vap->mh = NULL;
435}
436
437/*
438 * Manipulate the debug mask.  Note debug
439 * msgs are only provided when this code is
440 * compiled with MWLHAL_DEBUG defined.
441 */
442
443void
444mwl_hal_setdebug(struct mwl_hal *mh, int debug)
445{
446	MWLPRIV(mh)->mh_debug = debug;
447}
448
449int
450mwl_hal_getdebug(struct mwl_hal *mh)
451{
452	return MWLPRIV(mh)->mh_debug;
453}
454
455void
456mwl_hal_setbastreams(struct mwl_hal *mh, int mask)
457{
458	MWLPRIV(mh)->mh_bastreams = mask & ((1<<MWL_BASTREAMS_MAX)-1);
459}
460
461int
462mwl_hal_getbastreams(struct mwl_hal *mh)
463{
464	return MWLPRIV(mh)->mh_bastreams;
465}
466
467int
468mwl_hal_ismbsscapable(struct mwl_hal *mh)
469{
470	return (MWLPRIV(mh)->mh_flags & MHF_MBSS) != 0;
471}
472
473#if 0
474/* XXX inlined */
475/*
476 * Return the current ISR setting and clear the cause.
477 * XXX maybe make inline
478 */
479void
480mwl_hal_getisr(struct mwl_hal *mh0, uint32_t *status)
481{
482	struct mwl_hal_priv *mh = MWLPRIV(mh0);
483	uint32_t cause;
484
485	cause = RD4(mh, MACREG_REG_A2H_INTERRUPT_CAUSE);
486	if (cause == 0xffffffff) {	/* card removed */
487device_printf(mh->mh_dev, "%s: cause 0x%x\n", __func__, cause);
488		cause = 0;
489	} else if (cause != 0) {
490		/* clear cause bits */
491		WR4(mh, MACREG_REG_A2H_INTERRUPT_CAUSE,
492		    cause &~ mh->public.mh_imask);
493		RD4(mh, MACREG_REG_INT_CODE);	/* XXX flush write? */
494	}
495	*status = cause;
496}
497#endif
498
499/*
500 * Set the interrupt mask.
501 */
502void
503mwl_hal_intrset(struct mwl_hal *mh0, uint32_t mask)
504{
505	struct mwl_hal_priv *mh = MWLPRIV(mh0);
506
507	WR4(mh, MACREG_REG_A2H_INTERRUPT_MASK, 0);
508	RD4(mh, MACREG_REG_INT_CODE);
509
510	mh->public.mh_imask = mask;
511	WR4(mh, MACREG_REG_A2H_INTERRUPT_MASK, mask);
512	RD4(mh, MACREG_REG_INT_CODE);
513}
514
515#if 0
516/* XXX inlined */
517/*
518 * Kick the firmware to tell it there are new tx descriptors
519 * for processing.  The driver says what h/w q has work in
520 * case the f/w ever gets smarter.
521 */
522void
523mwl_hal_txstart(struct mwl_hal *mh0, int qnum)
524{
525	struct mwl_hal_priv *mh = MWLPRIV(mh0);
526	uint32_t dummy;
527
528	WR4(mh, MACREG_REG_H2A_INTERRUPT_EVENTS, MACREG_H2ARIC_BIT_PPA_READY);
529	dummy = RD4(mh, MACREG_REG_INT_CODE);
530}
531#endif
532
533/*
534 * Callback from the driver on a cmd done interrupt.
535 * Nothing to do right now as we spin waiting for
536 * cmd completion.
537 */
538void
539mwl_hal_cmddone(struct mwl_hal *mh0)
540{
541#if 0
542	struct mwl_hal_priv *mh = MWLPRIV(mh0);
543
544	if (mh->mh_debug & MWL_HAL_DEBUG_CMDDONE) {
545		device_printf(mh->mh_dev, "cmd done interrupt:\n");
546		dumpresult(mh, 1);
547	}
548#endif
549}
550
551/*
552 * Return "hw specs".  Note this must be the first
553 * cmd MUST be done after a firmware download or the
554 * f/w will lockup.
555 * XXX move into the hal so driver doesn't need to be responsible
556 */
557int
558mwl_hal_gethwspecs(struct mwl_hal *mh0, struct mwl_hal_hwspec *hw)
559{
560	struct mwl_hal_priv *mh = MWLPRIV(mh0);
561	HostCmd_DS_GET_HW_SPEC *pCmd;
562	int retval, minrev;
563
564	MWL_HAL_LOCK(mh);
565	_CMD_SETUP(pCmd, HostCmd_DS_GET_HW_SPEC, HostCmd_CMD_GET_HW_SPEC);
566	memset(&pCmd->PermanentAddr[0], 0xff, IEEE80211_ADDR_LEN);
567	pCmd->ulFwAwakeCookie = htole32((unsigned int)mh->mh_cmdaddr+2048);
568
569	retval = mwlExecuteCmd(mh, HostCmd_CMD_GET_HW_SPEC);
570	if (retval == 0) {
571		IEEE80211_ADDR_COPY(hw->macAddr, pCmd->PermanentAddr);
572		hw->wcbBase[0] = le32toh(pCmd->WcbBase0) & 0x0000ffff;
573		hw->wcbBase[1] = le32toh(pCmd->WcbBase1[0]) & 0x0000ffff;
574		hw->wcbBase[2] = le32toh(pCmd->WcbBase1[1]) & 0x0000ffff;
575		hw->wcbBase[3] = le32toh(pCmd->WcbBase1[2]) & 0x0000ffff;
576		hw->rxDescRead = le32toh(pCmd->RxPdRdPtr)& 0x0000ffff;
577		hw->rxDescWrite = le32toh(pCmd->RxPdWrPtr)& 0x0000ffff;
578		hw->regionCode = le16toh(pCmd->RegionCode) & 0x00ff;
579		hw->fwReleaseNumber = le32toh(pCmd->FWReleaseNumber);
580		hw->maxNumWCB = le16toh(pCmd->NumOfWCB);
581		hw->maxNumMCAddr = le16toh(pCmd->NumOfMCastAddr);
582		hw->numAntennas = le16toh(pCmd->NumberOfAntenna);
583		hw->hwVersion = pCmd->Version;
584		hw->hostInterface = pCmd->HostIf;
585
586		mh->mh_revs.mh_macRev = hw->hwVersion;		/* XXX */
587		mh->mh_revs.mh_phyRev = hw->hostInterface;	/* XXX */
588
589		minrev = ((hw->fwReleaseNumber) >> 16) & 0xff;
590		if (minrev >= 4) {
591			/* starting with 3.4.x.x s/w BA streams supported */
592			mh->mh_bastreams &= (1<<MWL_BASTREAMS_MAX)-1;
593		} else
594			mh->mh_bastreams &= (1<<2)-1;
595	}
596	MWL_HAL_UNLOCK(mh);
597	return retval;
598}
599
600/*
601 * Inform the f/w about location of the tx/rx dma data structures
602 * and related state.  This cmd must be done immediately after a
603 * mwl_hal_gethwspecs call or the f/w will lockup.
604 */
605int
606mwl_hal_sethwdma(struct mwl_hal *mh0, const struct mwl_hal_txrxdma *dma)
607{
608	struct mwl_hal_priv *mh = MWLPRIV(mh0);
609	HostCmd_DS_SET_HW_SPEC *pCmd;
610	int retval;
611
612	MWL_HAL_LOCK(mh);
613	_CMD_SETUP(pCmd, HostCmd_DS_SET_HW_SPEC, HostCmd_CMD_SET_HW_SPEC);
614	pCmd->WcbBase[0] = htole32(dma->wcbBase[0]);
615	pCmd->WcbBase[1] = htole32(dma->wcbBase[1]);
616	pCmd->WcbBase[2] = htole32(dma->wcbBase[2]);
617	pCmd->WcbBase[3] = htole32(dma->wcbBase[3]);
618	pCmd->TxWcbNumPerQueue = htole32(dma->maxNumTxWcb);
619	pCmd->NumTxQueues = htole32(dma->maxNumWCB);
620	pCmd->TotalRxWcb = htole32(1);		/* XXX */
621	pCmd->RxPdWrPtr = htole32(dma->rxDescRead);
622	pCmd->Flags = htole32(SET_HW_SPEC_HOSTFORM_BEACON
623#ifdef MWL_HOST_PS_SUPPORT
624		    | SET_HW_SPEC_HOST_POWERSAVE
625#endif
626		    | SET_HW_SPEC_HOSTFORM_PROBERESP);
627	/* disable multi-bss operation for A1-A4 parts */
628	if (mh->mh_revs.mh_macRev < 5)
629		pCmd->Flags |= htole32(SET_HW_SPEC_DISABLEMBSS);
630
631	retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_HW_SPEC);
632	if (retval == 0) {
633		if (pCmd->Flags & htole32(SET_HW_SPEC_DISABLEMBSS))
634			mh->mh_flags &= ~MHF_MBSS;
635		else
636			mh->mh_flags |= MHF_MBSS;
637	}
638	MWL_HAL_UNLOCK(mh);
639	return retval;
640}
641
642/*
643 * Retrieve statistics from the f/w.
644 * XXX should be in memory shared w/ driver
645 */
646int
647mwl_hal_gethwstats(struct mwl_hal *mh0, struct mwl_hal_hwstats *stats)
648{
649	struct mwl_hal_priv *mh = MWLPRIV(mh0);
650	HostCmd_DS_802_11_GET_STAT *pCmd;
651	int retval;
652
653	MWL_HAL_LOCK(mh);
654	_CMD_SETUP(pCmd, HostCmd_DS_802_11_GET_STAT,
655		HostCmd_CMD_802_11_GET_STAT);
656
657	retval = mwlExecuteCmd(mh, HostCmd_CMD_802_11_GET_STAT);
658	if (retval == 0) {
659		const uint32_t *sp = (const uint32_t *)&pCmd->TxRetrySuccesses;
660		uint32_t *dp = (uint32_t *)&stats->TxRetrySuccesses;
661		int i;
662
663		for (i = 0; i < sizeof(*stats)/sizeof(uint32_t); i++)
664			dp[i] = le32toh(sp[i]);
665		/*
666		 * Update stats not returned by f/w but available
667		 * through public registers.  Note these registers
668		 * are "clear on read" so we maintain cumulative data.
669		 * XXX register defines
670		 */
671		mh->mh_RTSSuccesses += RD4(mh, 0xa834);
672		mh->mh_RTSFailures += RD4(mh, 0xa830);
673		mh->mh_RxDuplicateFrames += RD4(mh, 0xa84c);
674		mh->mh_FCSErrorCount += RD4(mh, 0xa840);
675	}
676	MWL_HAL_UNLOCK(mh);
677
678	stats->RTSSuccesses = mh->mh_RTSSuccesses;
679	stats->RTSFailures = mh->mh_RTSFailures;
680	stats->RxDuplicateFrames = mh->mh_RxDuplicateFrames;
681	stats->FCSErrorCount = mh->mh_FCSErrorCount;
682	return retval;
683}
684
685/*
686 * Set HT guard interval handling.
687 * Takes effect immediately.
688 */
689int
690mwl_hal_sethtgi(struct mwl_hal_vap *vap, int GIType)
691{
692	struct mwl_hal_priv *mh = MWLVAP(vap);
693	HostCmd_FW_HT_GUARD_INTERVAL *pCmd;
694	int retval;
695
696	MWL_HAL_LOCK(mh);
697	_VCMD_SETUP(vap, pCmd, HostCmd_FW_HT_GUARD_INTERVAL,
698		HostCmd_CMD_HT_GUARD_INTERVAL);
699	pCmd->Action = htole32(HostCmd_ACT_GEN_SET);
700
701	if (GIType == 0) {
702		pCmd->GIType = htole32(GI_TYPE_LONG);
703	} else if (GIType == 1) {
704		pCmd->GIType = htole32(GI_TYPE_LONG | GI_TYPE_SHORT);
705	} else {
706		pCmd->GIType = htole32(GI_TYPE_LONG);
707	}
708
709	retval = mwlExecuteCmd(mh, HostCmd_CMD_HT_GUARD_INTERVAL);
710	MWL_HAL_UNLOCK(mh);
711	return retval;
712}
713
714/*
715 * Configure radio.
716 * Takes effect immediately.
717 * XXX preamble installed after set fixed rate cmd
718 */
719int
720mwl_hal_setradio(struct mwl_hal *mh0, int onoff, MWL_HAL_PREAMBLE preamble)
721{
722	struct mwl_hal_priv *mh = MWLPRIV(mh0);
723	HostCmd_DS_802_11_RADIO_CONTROL *pCmd;
724	int retval;
725
726	MWL_HAL_LOCK(mh);
727	_CMD_SETUP(pCmd, HostCmd_DS_802_11_RADIO_CONTROL,
728		HostCmd_CMD_802_11_RADIO_CONTROL);
729	pCmd->Action = htole16(HostCmd_ACT_GEN_SET);
730	if (onoff == 0)
731		pCmd->Control = 0;
732	else
733		pCmd->Control = htole16(preamble);
734	pCmd->RadioOn = htole16(onoff);
735
736	retval = mwlExecuteCmd(mh, HostCmd_CMD_802_11_RADIO_CONTROL);
737	MWL_HAL_UNLOCK(mh);
738	return retval;
739}
740
741/*
742 * Configure antenna use.
743 * Takes effect immediately.
744 * XXX tx antenna setting ignored
745 * XXX rx antenna setting should always be 3 (for now)
746 */
747int
748mwl_hal_setantenna(struct mwl_hal *mh0, MWL_HAL_ANTENNA dirSet, int ant)
749{
750	struct mwl_hal_priv *mh = MWLPRIV(mh0);
751	HostCmd_DS_802_11_RF_ANTENNA *pCmd;
752	int retval;
753
754	if (!(dirSet == WL_ANTENNATYPE_RX || dirSet == WL_ANTENNATYPE_TX))
755		return EINVAL;
756
757	MWL_HAL_LOCK(mh);
758	_CMD_SETUP(pCmd, HostCmd_DS_802_11_RF_ANTENNA,
759		HostCmd_CMD_802_11_RF_ANTENNA);
760	pCmd->Action = htole16(dirSet);
761	if (ant == 0)			/* default to all/both antennae */
762		ant = 3;
763	pCmd->AntennaMode = htole16(ant);
764
765	retval = mwlExecuteCmd(mh, HostCmd_CMD_802_11_RF_ANTENNA);
766	MWL_HAL_UNLOCK(mh);
767	return retval;
768}
769
770/*
771 * Set packet size threshold for implicit use of RTS.
772 * Takes effect immediately.
773 * XXX packet length > threshold =>'s RTS
774 */
775int
776mwl_hal_setrtsthreshold(struct mwl_hal_vap *vap, int threshold)
777{
778	struct mwl_hal_priv *mh = MWLVAP(vap);
779	HostCmd_DS_802_11_RTS_THSD *pCmd;
780	int retval;
781
782	MWL_HAL_LOCK(mh);
783	_VCMD_SETUP(vap, pCmd, HostCmd_DS_802_11_RTS_THSD,
784		HostCmd_CMD_802_11_RTS_THSD);
785	pCmd->Action  = htole16(HostCmd_ACT_GEN_SET);
786	pCmd->Threshold = htole16(threshold);
787
788	retval = mwlExecuteCmd(mh, HostCmd_CMD_802_11_RTS_THSD);
789	MWL_HAL_UNLOCK(mh);
790	return retval;
791}
792
793/*
794 * Enable sta-mode operation (disables beacon frame xmit).
795 */
796int
797mwl_hal_setinframode(struct mwl_hal_vap *vap)
798{
799	struct mwl_hal_priv *mh = MWLVAP(vap);
800	HostCmd_FW_SET_INFRA_MODE *pCmd;
801	int retval;
802
803	MWL_HAL_LOCK(mh);
804	_VCMD_SETUP(vap, pCmd, HostCmd_FW_SET_INFRA_MODE,
805		HostCmd_CMD_SET_INFRA_MODE);
806
807	retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_INFRA_MODE);
808	MWL_HAL_UNLOCK(mh);
809	return retval;
810}
811
812/*
813 * Configure radar detection in support of 802.11h.
814 */
815int
816mwl_hal_setradardetection(struct mwl_hal *mh0, MWL_HAL_RADAR action)
817{
818	struct mwl_hal_priv *mh = MWLPRIV(mh0);
819	HostCmd_802_11h_Detect_Radar *pCmd;
820	int retval;
821
822	MWL_HAL_LOCK(mh);
823	_CMD_SETUP(pCmd, HostCmd_802_11h_Detect_Radar,
824		HostCmd_CMD_802_11H_DETECT_RADAR);
825	pCmd->CmdHdr.Length = htole16(sizeof(HostCmd_802_11h_Detect_Radar));
826	pCmd->Action = htole16(action);
827	if (mh->mh_regioncode == DOMAIN_CODE_ETSI_131)
828		pCmd->RadarTypeCode = htole16(131);
829
830	retval = mwlExecuteCmd(mh, HostCmd_CMD_802_11H_DETECT_RADAR);
831	MWL_HAL_UNLOCK(mh);
832	return retval;
833}
834
835/*
836 * Convert public channel flags definition to a
837 * value suitable for feeding to the firmware.
838 * Note this includes byte swapping.
839 */
840static uint32_t
841cvtChannelFlags(const MWL_HAL_CHANNEL *chan)
842{
843	uint32_t w;
844
845	/*
846	 * NB: f/w only understands FREQ_BAND_5GHZ, supplying the more
847	 *     precise band info causes it to lockup (sometimes).
848	 */
849	w = (chan->channelFlags.FreqBand == MWL_FREQ_BAND_2DOT4GHZ) ?
850		FREQ_BAND_2DOT4GHZ : FREQ_BAND_5GHZ;
851	switch (chan->channelFlags.ChnlWidth) {
852	case MWL_CH_10_MHz_WIDTH:
853		w |= CH_10_MHz_WIDTH;
854		break;
855	case MWL_CH_20_MHz_WIDTH:
856		w |= CH_20_MHz_WIDTH;
857		break;
858	case MWL_CH_40_MHz_WIDTH:
859	default:
860		w |= CH_40_MHz_WIDTH;
861		break;
862	}
863	switch (chan->channelFlags.ExtChnlOffset) {
864	case MWL_EXT_CH_NONE:
865		w |= EXT_CH_NONE;
866		break;
867	case MWL_EXT_CH_ABOVE_CTRL_CH:
868		w |= EXT_CH_ABOVE_CTRL_CH;
869		break;
870	case MWL_EXT_CH_BELOW_CTRL_CH:
871		w |= EXT_CH_BELOW_CTRL_CH;
872		break;
873	}
874	return htole32(w);
875}
876
877/*
878 * Start a channel switch announcement countdown.  The IE
879 * in the beacon frame is allowed to go out and the firmware
880 * counts down and notifies the host when it's time to switch
881 * channels.
882 */
883int
884mwl_hal_setchannelswitchie(struct mwl_hal *mh0,
885	const MWL_HAL_CHANNEL *nextchan, uint32_t mode, uint32_t count)
886{
887	struct mwl_hal_priv *mh = MWLPRIV(mh0);
888	HostCmd_SET_SWITCH_CHANNEL *pCmd;
889	int retval;
890
891	MWL_HAL_LOCK(mh);
892	_CMD_SETUP(pCmd, HostCmd_SET_SWITCH_CHANNEL,
893		HostCmd_CMD_SET_SWITCH_CHANNEL);
894	pCmd->Next11hChannel = htole32(nextchan->channel);
895	pCmd->Mode = htole32(mode);
896	pCmd->InitialCount = htole32(count+1);
897	pCmd->ChannelFlags = cvtChannelFlags(nextchan);
898
899	retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_SWITCH_CHANNEL);
900	MWL_HAL_UNLOCK(mh);
901	return retval;
902}
903
904/*
905 * Set the region code that selects the radar bin'ing agorithm.
906 */
907int
908mwl_hal_setregioncode(struct mwl_hal *mh0, int regionCode)
909{
910	struct mwl_hal_priv *mh = MWLPRIV(mh0);
911	HostCmd_SET_REGIONCODE_INFO *pCmd;
912	int retval;
913
914	MWL_HAL_LOCK(mh);
915	_CMD_SETUP(pCmd, HostCmd_SET_REGIONCODE_INFO,
916		HostCmd_CMD_SET_REGION_CODE);
917	/* XXX map pseudo-codes to fw codes */
918	switch (regionCode) {
919	case DOMAIN_CODE_ETSI_131:
920		pCmd->regionCode = htole16(DOMAIN_CODE_ETSI);
921		break;
922	default:
923		pCmd->regionCode = htole16(regionCode);
924		break;
925	}
926
927	retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_REGION_CODE);
928	if (retval == 0)
929		mh->mh_regioncode = regionCode;
930	MWL_HAL_UNLOCK(mh);
931	return retval;
932}
933
934#define	RATEVAL(r)	((r) &~ RATE_MCS)
935#define	RATETYPE(r)	(((r) & RATE_MCS) ? HT_RATE_TYPE : LEGACY_RATE_TYPE)
936
937int
938mwl_hal_settxrate(struct mwl_hal_vap *vap, MWL_HAL_TXRATE_HANDLING handling,
939	const MWL_HAL_TXRATE *rate)
940{
941	struct mwl_hal_priv *mh = MWLVAP(vap);
942	HostCmd_FW_USE_FIXED_RATE *pCmd;
943	FIXED_RATE_ENTRY *fp;
944	int retval, i, n;
945
946	MWL_HAL_LOCK(mh);
947	_VCMD_SETUP(vap, pCmd, HostCmd_FW_USE_FIXED_RATE,
948		HostCmd_CMD_SET_FIXED_RATE);
949
950	pCmd->MulticastRate = RATEVAL(rate->McastRate);
951	pCmd->MultiRateTxType = RATETYPE(rate->McastRate);
952	/* NB: no rate type field */
953	pCmd->ManagementRate = RATEVAL(rate->MgtRate);
954	memset(pCmd->FixedRateTable, 0, sizeof(pCmd->FixedRateTable));
955	if (handling == RATE_FIXED) {
956		pCmd->Action = htole32(HostCmd_ACT_GEN_SET);
957		pCmd->AllowRateDrop = htole32(FIXED_RATE_WITHOUT_AUTORATE_DROP);
958		fp = pCmd->FixedRateTable;
959		fp->FixedRate =
960		    htole32(RATEVAL(rate->RateSeries[0].Rate));
961		fp->FixRateTypeFlags.FixRateType =
962		    htole32(RATETYPE(rate->RateSeries[0].Rate));
963		pCmd->EntryCount = htole32(1);
964	} else if (handling == RATE_FIXED_DROP) {
965		pCmd->Action = htole32(HostCmd_ACT_GEN_SET);
966		pCmd->AllowRateDrop = htole32(FIXED_RATE_WITH_AUTO_RATE_DROP);
967		n = 0;
968		fp = pCmd->FixedRateTable;
969		for (i = 0; i < 4; i++) {
970			if (rate->RateSeries[0].TryCount == 0)
971				break;
972			fp->FixRateTypeFlags.FixRateType =
973			    htole32(RATETYPE(rate->RateSeries[i].Rate));
974			fp->FixedRate =
975			    htole32(RATEVAL(rate->RateSeries[i].Rate));
976			fp->FixRateTypeFlags.RetryCountValid =
977			    htole32(RETRY_COUNT_VALID);
978			fp->RetryCount =
979			    htole32(rate->RateSeries[i].TryCount-1);
980			n++;
981		}
982		pCmd->EntryCount = htole32(n);
983	} else
984		pCmd->Action = htole32(HostCmd_ACT_NOT_USE_FIXED_RATE);
985
986	retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_FIXED_RATE);
987	MWL_HAL_UNLOCK(mh);
988	return retval;
989}
990
991int
992mwl_hal_settxrate_auto(struct mwl_hal *mh0, const MWL_HAL_TXRATE *rate)
993{
994	struct mwl_hal_priv *mh = MWLPRIV(mh0);
995	HostCmd_FW_USE_FIXED_RATE *pCmd;
996	int retval;
997
998	MWL_HAL_LOCK(mh);
999	_CMD_SETUP(pCmd, HostCmd_FW_USE_FIXED_RATE,
1000		HostCmd_CMD_SET_FIXED_RATE);
1001
1002	pCmd->MulticastRate = RATEVAL(rate->McastRate);
1003	pCmd->MultiRateTxType = RATETYPE(rate->McastRate);
1004	/* NB: no rate type field */
1005	pCmd->ManagementRate = RATEVAL(rate->MgtRate);
1006	memset(pCmd->FixedRateTable, 0, sizeof(pCmd->FixedRateTable));
1007	pCmd->Action = htole32(HostCmd_ACT_NOT_USE_FIXED_RATE);
1008
1009	retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_FIXED_RATE);
1010	MWL_HAL_UNLOCK(mh);
1011	return retval;
1012}
1013
1014#undef RATEVAL
1015#undef RATETYPE
1016
1017int
1018mwl_hal_setslottime(struct mwl_hal *mh0, int usecs)
1019{
1020	struct mwl_hal_priv *mh = MWLPRIV(mh0);
1021	HostCmd_FW_SET_SLOT *pCmd;
1022	int retval;
1023
1024	if (usecs != 9 && usecs != 20)
1025		return EINVAL;
1026
1027	MWL_HAL_LOCK(mh);
1028	_CMD_SETUP(pCmd, HostCmd_FW_SET_SLOT,
1029	    HostCmd_CMD_802_11_SET_SLOT);
1030	pCmd->Action = htole16(HostCmd_ACT_GEN_SET);
1031	pCmd->Slot = (usecs == 9 ? 1 : 0);
1032
1033	retval = mwlExecuteCmd(mh, HostCmd_CMD_802_11_SET_SLOT);
1034	MWL_HAL_UNLOCK(mh);
1035	return retval;
1036}
1037
1038int
1039mwl_hal_adjusttxpower(struct mwl_hal *mh0, uint32_t level)
1040{
1041	struct mwl_hal_priv *mh = MWLPRIV(mh0);
1042	HostCmd_DS_802_11_RF_TX_POWER *pCmd;
1043	int retval;
1044
1045	MWL_HAL_LOCK(mh);
1046	_CMD_SETUP(pCmd, HostCmd_DS_802_11_RF_TX_POWER,
1047	    HostCmd_CMD_802_11_RF_TX_POWER);
1048	pCmd->Action = htole16(HostCmd_ACT_GEN_SET);
1049
1050	if (level < 30) {
1051		pCmd->SupportTxPowerLevel = htole16(WL_TX_POWERLEVEL_LOW);
1052	} else if (level >= 30 && level < 60) {
1053		pCmd->SupportTxPowerLevel = htole16(WL_TX_POWERLEVEL_MEDIUM);
1054	} else {
1055		pCmd->SupportTxPowerLevel = htole16(WL_TX_POWERLEVEL_HIGH);
1056	}
1057
1058	retval = mwlExecuteCmd(mh, HostCmd_CMD_802_11_RF_TX_POWER);
1059	MWL_HAL_UNLOCK(mh);
1060	return retval;
1061}
1062
1063static const struct mwl_hal_channel *
1064findchannel(const struct mwl_hal_priv *mh, const MWL_HAL_CHANNEL *c)
1065{
1066	const struct mwl_hal_channel *hc;
1067	const MWL_HAL_CHANNELINFO *ci;
1068	int chan = c->channel, i;
1069
1070	if (c->channelFlags.FreqBand == MWL_FREQ_BAND_2DOT4GHZ) {
1071		i = chan - 1;
1072		if (c->channelFlags.ChnlWidth == MWL_CH_40_MHz_WIDTH) {
1073			ci = &mh->mh_40M;
1074			if (c->channelFlags.ExtChnlOffset == MWL_EXT_CH_BELOW_CTRL_CH)
1075				i -= 4;
1076		} else
1077			ci = &mh->mh_20M;
1078		/* 2.4G channel table is directly indexed */
1079		hc = ((unsigned)i < ci->nchannels) ? &ci->channels[i] : NULL;
1080	} else if (c->channelFlags.FreqBand == MWL_FREQ_BAND_5GHZ) {
1081		if (c->channelFlags.ChnlWidth == MWL_CH_40_MHz_WIDTH) {
1082			ci = &mh->mh_40M_5G;
1083			if (c->channelFlags.ExtChnlOffset == MWL_EXT_CH_BELOW_CTRL_CH)
1084				chan -= 4;
1085		} else
1086			ci = &mh->mh_20M_5G;
1087		/* 5GHz channel table is sparse and must be searched */
1088		for (i = 0; i < ci->nchannels; i++)
1089			if (ci->channels[i].ieee == chan)
1090				break;
1091		hc = (i < ci->nchannels) ? &ci->channels[i] : NULL;
1092	} else
1093		hc = NULL;
1094	return hc;
1095}
1096
1097int
1098mwl_hal_settxpower(struct mwl_hal *mh0, const MWL_HAL_CHANNEL *c, uint8_t maxtxpow)
1099{
1100	struct mwl_hal_priv *mh = MWLPRIV(mh0);
1101	HostCmd_DS_802_11_RF_TX_POWER *pCmd;
1102	const struct mwl_hal_channel *hc;
1103	int i, retval;
1104
1105	hc = findchannel(mh, c);
1106	if (hc == NULL) {
1107		/* XXX temp while testing */
1108		device_printf(mh->mh_dev,
1109		    "%s: no cal data for channel %u band %u width %u ext %u\n",
1110		    __func__, c->channel, c->channelFlags.FreqBand,
1111		    c->channelFlags.ChnlWidth, c->channelFlags.ExtChnlOffset);
1112		return EINVAL;
1113	}
1114
1115	MWL_HAL_LOCK(mh);
1116	_CMD_SETUP(pCmd, HostCmd_DS_802_11_RF_TX_POWER,
1117	    HostCmd_CMD_802_11_RF_TX_POWER);
1118	pCmd->Action = htole16(HostCmd_ACT_GEN_SET_LIST);
1119	i = 0;
1120	/* NB: 5Ghz cal data have the channel # in [0]; don't truncate */
1121	if (c->channelFlags.FreqBand == MWL_FREQ_BAND_5GHZ)
1122		pCmd->PowerLevelList[i++] = htole16(hc->targetPowers[0]);
1123	for (; i < 4; i++) {
1124		uint16_t pow = hc->targetPowers[i];
1125		if (pow > maxtxpow)
1126			pow = maxtxpow;
1127		pCmd->PowerLevelList[i] = htole16(pow);
1128	}
1129	retval = mwlExecuteCmd(mh, HostCmd_CMD_802_11_RF_TX_POWER);
1130	MWL_HAL_UNLOCK(mh);
1131	return retval;
1132}
1133
1134int
1135mwl_hal_getchannelinfo(struct mwl_hal *mh0, int band, int chw,
1136	const MWL_HAL_CHANNELINFO **ci)
1137{
1138	struct mwl_hal_priv *mh = MWLPRIV(mh0);
1139
1140	switch (band) {
1141	case MWL_FREQ_BAND_2DOT4GHZ:
1142		*ci = (chw == MWL_CH_20_MHz_WIDTH) ? &mh->mh_20M : &mh->mh_40M;
1143		break;
1144	case MWL_FREQ_BAND_5GHZ:
1145		*ci = (chw == MWL_CH_20_MHz_WIDTH) ?
1146		     &mh->mh_20M_5G : &mh->mh_40M_5G;
1147		break;
1148	default:
1149		return EINVAL;
1150	}
1151	return ((*ci)->freqLow == (*ci)->freqHigh) ? EINVAL : 0;
1152}
1153
1154int
1155mwl_hal_setmcast(struct mwl_hal *mh0, int nmc, const uint8_t macs[])
1156{
1157	struct mwl_hal_priv *mh = MWLPRIV(mh0);
1158	HostCmd_DS_MAC_MULTICAST_ADR *pCmd;
1159	int retval;
1160
1161	if (nmc > MWL_HAL_MCAST_MAX)
1162		return EINVAL;
1163
1164	MWL_HAL_LOCK(mh);
1165	_CMD_SETUP(pCmd, HostCmd_DS_MAC_MULTICAST_ADR,
1166		HostCmd_CMD_MAC_MULTICAST_ADR);
1167	memcpy(pCmd->MACList, macs, nmc*IEEE80211_ADDR_LEN);
1168	pCmd->NumOfAdrs = htole16(nmc);
1169	pCmd->Action = htole16(0xffff);
1170
1171	retval = mwlExecuteCmd(mh, HostCmd_CMD_MAC_MULTICAST_ADR);
1172	MWL_HAL_UNLOCK(mh);
1173	return retval;
1174}
1175
1176int
1177mwl_hal_keyset(struct mwl_hal_vap *vap, const MWL_HAL_KEYVAL *kv,
1178	const uint8_t mac[IEEE80211_ADDR_LEN])
1179{
1180	struct mwl_hal_priv *mh = MWLVAP(vap);
1181	HostCmd_FW_UPDATE_ENCRYPTION_SET_KEY *pCmd;
1182	int retval;
1183
1184	MWL_HAL_LOCK(mh);
1185	_VCMD_SETUP(vap, pCmd, HostCmd_FW_UPDATE_ENCRYPTION_SET_KEY,
1186		HostCmd_CMD_UPDATE_ENCRYPTION);
1187	if (kv->keyFlags & (KEY_FLAG_TXGROUPKEY|KEY_FLAG_RXGROUPKEY))
1188		pCmd->ActionType = htole32(EncrActionTypeSetGroupKey);
1189	else
1190		pCmd->ActionType = htole32(EncrActionTypeSetKey);
1191	pCmd->KeyParam.Length = htole16(sizeof(pCmd->KeyParam));
1192	pCmd->KeyParam.KeyTypeId = htole16(kv->keyTypeId);
1193	pCmd->KeyParam.KeyInfo = htole32(kv->keyFlags);
1194	pCmd->KeyParam.KeyIndex = htole32(kv->keyIndex);
1195	/* NB: includes TKIP MIC keys */
1196	memcpy(&pCmd->KeyParam.Key, &kv->key, kv->keyLen);
1197	switch (kv->keyTypeId) {
1198	case KEY_TYPE_ID_WEP:
1199		pCmd->KeyParam.KeyLen = htole16(kv->keyLen);
1200		break;
1201	case KEY_TYPE_ID_TKIP:
1202		pCmd->KeyParam.KeyLen = htole16(sizeof(TKIP_TYPE_KEY));
1203		pCmd->KeyParam.Key.TkipKey.TkipRsc.low =
1204			htole16(kv->key.tkip.rsc.low);
1205		pCmd->KeyParam.Key.TkipKey.TkipRsc.high =
1206			htole32(kv->key.tkip.rsc.high);
1207		pCmd->KeyParam.Key.TkipKey.TkipTsc.low =
1208			htole16(kv->key.tkip.tsc.low);
1209		pCmd->KeyParam.Key.TkipKey.TkipTsc.high =
1210			htole32(kv->key.tkip.tsc.high);
1211		break;
1212	case KEY_TYPE_ID_AES:
1213		pCmd->KeyParam.KeyLen = htole16(sizeof(AES_TYPE_KEY));
1214		break;
1215	}
1216#ifdef MWL_MBSS_SUPPORT
1217	IEEE80211_ADDR_COPY(pCmd->KeyParam.Macaddr, mac);
1218#else
1219	IEEE80211_ADDR_COPY(pCmd->Macaddr, mac);
1220#endif
1221	retval = mwlExecuteCmd(mh, HostCmd_CMD_UPDATE_ENCRYPTION);
1222	MWL_HAL_UNLOCK(mh);
1223	return retval;
1224}
1225
1226int
1227mwl_hal_keyreset(struct mwl_hal_vap *vap, const MWL_HAL_KEYVAL *kv, const uint8_t mac[IEEE80211_ADDR_LEN])
1228{
1229	struct mwl_hal_priv *mh = MWLVAP(vap);
1230	HostCmd_FW_UPDATE_ENCRYPTION_SET_KEY *pCmd;
1231	int retval;
1232
1233	MWL_HAL_LOCK(mh);
1234	_VCMD_SETUP(vap, pCmd, HostCmd_FW_UPDATE_ENCRYPTION_SET_KEY,
1235		HostCmd_CMD_UPDATE_ENCRYPTION);
1236	pCmd->ActionType = htole16(EncrActionTypeRemoveKey);
1237	pCmd->KeyParam.Length = htole16(sizeof(pCmd->KeyParam));
1238	pCmd->KeyParam.KeyTypeId = htole16(kv->keyTypeId);
1239	pCmd->KeyParam.KeyInfo = htole32(kv->keyFlags);
1240	pCmd->KeyParam.KeyIndex = htole32(kv->keyIndex);
1241#ifdef MWL_MBSS_SUPPORT
1242	IEEE80211_ADDR_COPY(pCmd->KeyParam.Macaddr, mac);
1243#else
1244	IEEE80211_ADDR_COPY(pCmd->Macaddr, mac);
1245#endif
1246	retval = mwlExecuteCmd(mh, HostCmd_CMD_UPDATE_ENCRYPTION);
1247	MWL_HAL_UNLOCK(mh);
1248	return retval;
1249}
1250
1251static int
1252mwl_hal_setmac_locked(struct mwl_hal_vap *vap,
1253	const uint8_t addr[IEEE80211_ADDR_LEN])
1254{
1255	struct mwl_hal_priv *mh = MWLVAP(vap);
1256	HostCmd_DS_SET_MAC *pCmd;
1257
1258	_VCMD_SETUP(vap, pCmd, HostCmd_DS_SET_MAC, HostCmd_CMD_SET_MAC_ADDR);
1259	IEEE80211_ADDR_COPY(&pCmd->MacAddr[0], addr);
1260#ifdef MWL_MBSS_SUPPORT
1261	pCmd->MacType = vap->bss_type;		/* NB: already byte swapped */
1262	IEEE80211_ADDR_COPY(vap->mac, addr);	/* XXX do only if success */
1263#endif
1264	return mwlExecuteCmd(mh, HostCmd_CMD_SET_MAC_ADDR);
1265}
1266
1267int
1268mwl_hal_setmac(struct mwl_hal_vap *vap, const uint8_t addr[IEEE80211_ADDR_LEN])
1269{
1270	struct mwl_hal_priv *mh = MWLVAP(vap);
1271	int retval;
1272
1273	MWL_HAL_LOCK(mh);
1274	retval = mwl_hal_setmac_locked(vap, addr);
1275	MWL_HAL_UNLOCK(mh);
1276	return retval;
1277}
1278
1279int
1280mwl_hal_setbeacon(struct mwl_hal_vap *vap, const void *frame, size_t frameLen)
1281{
1282	struct mwl_hal_priv *mh = MWLVAP(vap);
1283	HostCmd_DS_SET_BEACON *pCmd;
1284	int retval;
1285
1286	/* XXX verify frameLen fits */
1287	MWL_HAL_LOCK(mh);
1288	_VCMD_SETUP(vap, pCmd, HostCmd_DS_SET_BEACON, HostCmd_CMD_SET_BEACON);
1289	/* XXX override _VCMD_SETUP */
1290	pCmd->CmdHdr.Length = htole16(sizeof(HostCmd_DS_SET_BEACON)-1+frameLen);
1291	pCmd->FrmBodyLen = htole16(frameLen);
1292	memcpy(pCmd->FrmBody, frame, frameLen);
1293
1294	retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_BEACON);
1295	MWL_HAL_UNLOCK(mh);
1296	return retval;
1297}
1298
1299int
1300mwl_hal_setpowersave_bss(struct mwl_hal_vap *vap, uint8_t nsta)
1301{
1302	struct mwl_hal_priv *mh = MWLVAP(vap);
1303	HostCmd_SET_POWERSAVESTATION *pCmd;
1304	int retval;
1305
1306	MWL_HAL_LOCK(mh);
1307	_VCMD_SETUP(vap, pCmd, HostCmd_SET_POWERSAVESTATION,
1308		HostCmd_CMD_SET_POWERSAVESTATION);
1309	pCmd->NumberOfPowersave = nsta;
1310
1311	retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_POWERSAVESTATION);
1312	MWL_HAL_UNLOCK(mh);
1313	return retval;
1314}
1315
1316int
1317mwl_hal_setpowersave_sta(struct mwl_hal_vap *vap, uint16_t aid, int ena)
1318{
1319	struct mwl_hal_priv *mh = MWLVAP(vap);
1320	HostCmd_SET_TIM *pCmd;
1321	int retval;
1322
1323	MWL_HAL_LOCK(mh);
1324	_VCMD_SETUP(vap, pCmd, HostCmd_SET_TIM, HostCmd_CMD_SET_TIM);
1325	pCmd->Aid = htole16(aid);
1326	pCmd->Set = htole32(ena);
1327
1328	retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_TIM);
1329	MWL_HAL_UNLOCK(mh);
1330	return retval;
1331}
1332
1333int
1334mwl_hal_setassocid(struct mwl_hal_vap *vap,
1335	const uint8_t bssId[IEEE80211_ADDR_LEN], uint16_t assocId)
1336{
1337	struct mwl_hal_priv *mh = MWLVAP(vap);
1338	HostCmd_FW_SET_AID *pCmd = (HostCmd_FW_SET_AID *) &mh->mh_cmdbuf[0];
1339	int retval;
1340
1341	MWL_HAL_LOCK(mh);
1342	_VCMD_SETUP(vap, pCmd, HostCmd_FW_SET_AID, HostCmd_CMD_SET_AID);
1343	pCmd->AssocID = htole16(assocId);
1344	IEEE80211_ADDR_COPY(&pCmd->MacAddr[0], bssId);
1345
1346	retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_AID);
1347	MWL_HAL_UNLOCK(mh);
1348	return retval;
1349}
1350
1351int
1352mwl_hal_setchannel(struct mwl_hal *mh0, const MWL_HAL_CHANNEL *chan)
1353{
1354	struct mwl_hal_priv *mh = MWLPRIV(mh0);
1355	HostCmd_FW_SET_RF_CHANNEL *pCmd;
1356	int retval;
1357
1358	MWL_HAL_LOCK(mh);
1359	_CMD_SETUP(pCmd, HostCmd_FW_SET_RF_CHANNEL, HostCmd_CMD_SET_RF_CHANNEL);
1360	pCmd->Action = htole16(HostCmd_ACT_GEN_SET);
1361	pCmd->CurrentChannel = chan->channel;
1362	pCmd->ChannelFlags = cvtChannelFlags(chan);	/* NB: byte-swapped */
1363
1364	retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_RF_CHANNEL);
1365	MWL_HAL_UNLOCK(mh);
1366	return retval;
1367}
1368
1369static int
1370bastream_check_available(struct mwl_hal_vap *vap, int qid,
1371	const uint8_t Macaddr[IEEE80211_ADDR_LEN],
1372	uint8_t Tid, uint8_t ParamInfo)
1373{
1374	struct mwl_hal_priv *mh = MWLVAP(vap);
1375	HostCmd_FW_BASTREAM *pCmd;
1376	int retval;
1377
1378	MWL_HAL_LOCK_ASSERT(mh);
1379
1380	_VCMD_SETUP(vap, pCmd, HostCmd_FW_BASTREAM, HostCmd_CMD_BASTREAM);
1381	pCmd->ActionType = htole32(BaCheckCreateStream);
1382	pCmd->BaInfo.CreateParams.BarThrs = htole32(63);
1383	pCmd->BaInfo.CreateParams.WindowSize = htole32(64);
1384	pCmd->BaInfo.CreateParams.IdleThrs = htole32(0x22000);
1385	IEEE80211_ADDR_COPY(&pCmd->BaInfo.CreateParams.PeerMacAddr[0], Macaddr);
1386	pCmd->BaInfo.CreateParams.DialogToken = 10;
1387	pCmd->BaInfo.CreateParams.Tid = Tid;
1388	pCmd->BaInfo.CreateParams.QueueId = qid;
1389	pCmd->BaInfo.CreateParams.ParamInfo = (uint8_t) ParamInfo;
1390#if 0
1391	cvtBAFlags(&pCmd->BaInfo.CreateParams.Flags, sp->ba_policy, 0);
1392#else
1393	pCmd->BaInfo.CreateParams.Flags =
1394			  htole32(BASTREAM_FLAG_IMMEDIATE_TYPE)
1395			| htole32(BASTREAM_FLAG_DIRECTION_UPSTREAM)
1396			;
1397#endif
1398
1399	retval = mwlExecuteCmd(mh, HostCmd_CMD_BASTREAM);
1400	if (retval == 0) {
1401		/*
1402		 * NB: BA stream create may fail when the stream is
1403		 * h/w backed under some (as yet not understood) conditions.
1404		 * Check the result code to catch this.
1405		 */
1406		if (le16toh(pCmd->CmdHdr.Result) != HostCmd_RESULT_OK)
1407			retval = EIO;
1408	}
1409	return retval;
1410}
1411
1412const MWL_HAL_BASTREAM *
1413mwl_hal_bastream_alloc(struct mwl_hal_vap *vap, int ba_policy,
1414	const uint8_t Macaddr[IEEE80211_ADDR_LEN],
1415	uint8_t Tid, uint8_t ParamInfo, void *a1, void *a2)
1416{
1417	struct mwl_hal_priv *mh = MWLVAP(vap);
1418	struct mwl_hal_bastream *sp;
1419	int s;
1420
1421	MWL_HAL_LOCK(mh);
1422	if (mh->mh_bastreams == 0) {
1423		/* no streams available */
1424		MWL_HAL_UNLOCK(mh);
1425		return NULL;
1426	}
1427	for (s = 0; (mh->mh_bastreams & (1<<s)) == 0; s++)
1428		;
1429	if (bastream_check_available(vap, s, Macaddr, Tid, ParamInfo)) {
1430		MWL_HAL_UNLOCK(mh);
1431		return NULL;
1432	}
1433	sp = &mh->mh_streams[s];
1434	mh->mh_bastreams &= ~(1<<s);
1435	sp->public.data[0] = a1;
1436	sp->public.data[1] = a2;
1437	IEEE80211_ADDR_COPY(sp->macaddr, Macaddr);
1438	sp->tid = Tid;
1439	sp->paraminfo = ParamInfo;
1440	sp->setup = 0;
1441	sp->ba_policy = ba_policy;
1442	MWL_HAL_UNLOCK(mh);
1443	return &sp->public;
1444}
1445
1446const MWL_HAL_BASTREAM *
1447mwl_hal_bastream_lookup(struct mwl_hal *mh0, int s)
1448{
1449	struct mwl_hal_priv *mh = MWLPRIV(mh0);
1450
1451	if (!(0 <= s && s < MWL_BASTREAMS_MAX))
1452		return NULL;
1453	if (mh->mh_bastreams & (1<<s))
1454		return NULL;
1455	return &mh->mh_streams[s].public;
1456}
1457
1458#ifndef	__DECONST
1459#define	__DECONST(type, var)	((type)(uintptr_t)(const void *)(var))
1460#endif
1461
1462int
1463mwl_hal_bastream_create(struct mwl_hal_vap *vap,
1464	const MWL_HAL_BASTREAM *s, int BarThrs, int WindowSize, uint16_t seqno)
1465{
1466	struct mwl_hal_priv *mh = MWLVAP(vap);
1467	struct mwl_hal_bastream *sp = __DECONST(struct mwl_hal_bastream *, s);
1468	HostCmd_FW_BASTREAM *pCmd;
1469	int retval;
1470
1471	MWL_HAL_LOCK(mh);
1472	_VCMD_SETUP(vap, pCmd, HostCmd_FW_BASTREAM, HostCmd_CMD_BASTREAM);
1473	pCmd->ActionType = htole32(BaCreateStream);
1474	pCmd->BaInfo.CreateParams.BarThrs = htole32(BarThrs);
1475	pCmd->BaInfo.CreateParams.WindowSize = htole32(WindowSize);
1476	pCmd->BaInfo.CreateParams.IdleThrs = htole32(0x22000);
1477	IEEE80211_ADDR_COPY(&pCmd->BaInfo.CreateParams.PeerMacAddr[0],
1478	    sp->macaddr);
1479	/* XXX proxy STA */
1480	memset(&pCmd->BaInfo.CreateParams.StaSrcMacAddr, 0, IEEE80211_ADDR_LEN);
1481#if 0
1482	pCmd->BaInfo.CreateParams.DialogToken = DialogToken;
1483#else
1484	pCmd->BaInfo.CreateParams.DialogToken = 10;
1485#endif
1486	pCmd->BaInfo.CreateParams.Tid = sp->tid;
1487	pCmd->BaInfo.CreateParams.QueueId = sp->stream;
1488	pCmd->BaInfo.CreateParams.ParamInfo = sp->paraminfo;
1489	/* NB: ResetSeqNo known to be zero */
1490	pCmd->BaInfo.CreateParams.StartSeqNo = htole16(seqno);
1491#if 0
1492	cvtBAFlags(&pCmd->BaInfo.CreateParams.Flags, sp->ba_policy, 0);
1493#else
1494	pCmd->BaInfo.CreateParams.Flags =
1495			  htole32(BASTREAM_FLAG_IMMEDIATE_TYPE)
1496			| htole32(BASTREAM_FLAG_DIRECTION_UPSTREAM)
1497			;
1498#endif
1499
1500	retval = mwlExecuteCmd(mh, HostCmd_CMD_BASTREAM);
1501	if (retval == 0) {
1502		/*
1503		 * NB: BA stream create may fail when the stream is
1504		 * h/w backed under some (as yet not understood) conditions.
1505		 * Check the result code to catch this.
1506		 */
1507		if (le16toh(pCmd->CmdHdr.Result) != HostCmd_RESULT_OK)
1508			retval = EIO;
1509		else
1510			sp->setup = 1;
1511	}
1512	MWL_HAL_UNLOCK(mh);
1513	return retval;
1514}
1515
1516int
1517mwl_hal_bastream_destroy(struct mwl_hal *mh0, const MWL_HAL_BASTREAM *s)
1518{
1519	struct mwl_hal_priv *mh = MWLPRIV(mh0);
1520	struct mwl_hal_bastream *sp = __DECONST(struct mwl_hal_bastream *, s);
1521	HostCmd_FW_BASTREAM *pCmd;
1522	int retval;
1523
1524	if (sp->stream >= MWL_BASTREAMS_MAX) {
1525		/* XXX */
1526		return EINVAL;
1527	}
1528	MWL_HAL_LOCK(mh);
1529	if (sp->setup) {
1530		_CMD_SETUP(pCmd, HostCmd_FW_BASTREAM, HostCmd_CMD_BASTREAM);
1531		pCmd->ActionType = htole32(BaDestroyStream);
1532		pCmd->BaInfo.DestroyParams.FwBaContext.Context =
1533		    htole32(sp->stream);
1534
1535		retval = mwlExecuteCmd(mh, HostCmd_CMD_BASTREAM);
1536	} else
1537		retval = 0;
1538	/* NB: always reclaim stream */
1539	mh->mh_bastreams |= 1<<sp->stream;
1540	sp->public.data[0] = NULL;
1541	sp->public.data[1] = NULL;
1542	sp->setup = 0;
1543	MWL_HAL_UNLOCK(mh);
1544	return retval;
1545}
1546
1547int
1548mwl_hal_bastream_get_seqno(struct mwl_hal *mh0,
1549	const MWL_HAL_BASTREAM *s, const uint8_t Macaddr[IEEE80211_ADDR_LEN],
1550	uint16_t *pseqno)
1551{
1552	struct mwl_hal_priv *mh = MWLPRIV(mh0);
1553	struct mwl_hal_bastream *sp = __DECONST(struct mwl_hal_bastream *, s);
1554	HostCmd_GET_SEQNO *pCmd;
1555	int retval;
1556
1557	MWL_HAL_LOCK(mh);
1558	_CMD_SETUP(pCmd, HostCmd_GET_SEQNO, HostCmd_CMD_GET_SEQNO);
1559	IEEE80211_ADDR_COPY(pCmd->MacAddr, Macaddr);
1560	pCmd->TID = sp->tid;
1561
1562	retval = mwlExecuteCmd(mh, HostCmd_CMD_GET_SEQNO);
1563	if (retval == 0)
1564		*pseqno = le16toh(pCmd->SeqNo);
1565	MWL_HAL_UNLOCK(mh);
1566	return retval;
1567}
1568
1569int
1570mwl_hal_getwatchdogbitmap(struct mwl_hal *mh0, uint8_t bitmap[1])
1571{
1572	struct mwl_hal_priv *mh = MWLPRIV(mh0);
1573	HostCmd_FW_GET_WATCHDOG_BITMAP *pCmd;
1574	int retval;
1575
1576	MWL_HAL_LOCK(mh);
1577	_CMD_SETUP(pCmd, HostCmd_FW_GET_WATCHDOG_BITMAP,
1578		HostCmd_CMD_GET_WATCHDOG_BITMAP);
1579
1580	retval = mwlExecuteCmd(mh, HostCmd_CMD_GET_WATCHDOG_BITMAP);
1581	if (retval == 0) {
1582		bitmap[0] = pCmd->Watchdogbitmap;
1583		/* fw returns qid, map it to BA stream */
1584		if (bitmap[0] < MWL_BAQID_MAX)
1585			bitmap[0] = qid2ba[bitmap[0]];
1586	}
1587	MWL_HAL_UNLOCK(mh);
1588	return retval;
1589}
1590
1591/*
1592 * Configure aggressive Ampdu rate mode.
1593 */
1594int
1595mwl_hal_setaggampduratemode(struct mwl_hal *mh0, int mode, int threshold)
1596{
1597	struct mwl_hal_priv *mh = MWLPRIV(mh0);
1598	HostCmd_FW_AMPDU_RETRY_RATEDROP_MODE *pCmd;
1599	int retval;
1600
1601	MWL_HAL_LOCK(mh);
1602	_CMD_SETUP(pCmd, HostCmd_FW_AMPDU_RETRY_RATEDROP_MODE,
1603		HostCmd_CMD_AMPDU_RETRY_RATEDROP_MODE);
1604	pCmd->Action = htole16(1);
1605	pCmd->Option = htole32(mode);
1606	pCmd->Threshold = htole32(threshold);
1607
1608	retval = mwlExecuteCmd(mh, HostCmd_CMD_AMPDU_RETRY_RATEDROP_MODE);
1609	MWL_HAL_UNLOCK(mh);
1610	return retval;
1611}
1612
1613int
1614mwl_hal_getaggampduratemode(struct mwl_hal *mh0, int *mode, int *threshold)
1615{
1616	struct mwl_hal_priv *mh = MWLPRIV(mh0);
1617	HostCmd_FW_AMPDU_RETRY_RATEDROP_MODE *pCmd;
1618	int retval;
1619
1620	MWL_HAL_LOCK(mh);
1621	_CMD_SETUP(pCmd, HostCmd_FW_AMPDU_RETRY_RATEDROP_MODE,
1622		HostCmd_CMD_AMPDU_RETRY_RATEDROP_MODE);
1623	pCmd->Action = htole16(0);
1624
1625	retval = mwlExecuteCmd(mh, HostCmd_CMD_AMPDU_RETRY_RATEDROP_MODE);
1626	MWL_HAL_UNLOCK(mh);
1627	*mode =  le32toh(pCmd->Option);
1628	*threshold = le32toh(pCmd->Threshold);
1629	return retval;
1630}
1631
1632/*
1633 * Set CFEND status Enable/Disable
1634 */
1635int
1636mwl_hal_setcfend(struct mwl_hal *mh0, int ena)
1637{
1638	struct mwl_hal_priv *mh = MWLPRIV(mh0);
1639	HostCmd_CFEND_ENABLE *pCmd;
1640	int retval;
1641
1642	MWL_HAL_LOCK(mh);
1643	_CMD_SETUP(pCmd, HostCmd_CFEND_ENABLE,
1644		HostCmd_CMD_CFEND_ENABLE);
1645	pCmd->Enable = htole32(ena);
1646
1647	retval = mwlExecuteCmd(mh, HostCmd_CMD_CFEND_ENABLE);
1648	MWL_HAL_UNLOCK(mh);
1649	return retval;
1650}
1651
1652int
1653mwl_hal_setdwds(struct mwl_hal *mh0, int ena)
1654{
1655	HostCmd_DWDS_ENABLE *pCmd;
1656	struct mwl_hal_priv *mh = MWLPRIV(mh0);
1657   	int retval;
1658
1659	MWL_HAL_LOCK(mh);
1660	_CMD_SETUP(pCmd, HostCmd_DWDS_ENABLE, HostCmd_CMD_DWDS_ENABLE);
1661	pCmd->Enable = htole32(ena);
1662	retval = mwlExecuteCmd(mh, HostCmd_CMD_DWDS_ENABLE);
1663  	MWL_HAL_UNLOCK(mh);
1664	return retval;
1665}
1666
1667static void
1668cvtPeerInfo(PeerInfo_t *to, const MWL_HAL_PEERINFO *from)
1669{
1670	to->LegacyRateBitMap = htole32(from->LegacyRateBitMap);
1671	to->HTRateBitMap = htole32(from->HTRateBitMap);
1672	to->CapInfo = htole16(from->CapInfo);
1673	to->HTCapabilitiesInfo = htole16(from->HTCapabilitiesInfo);
1674	to->MacHTParamInfo = from->MacHTParamInfo;
1675	to->AddHtInfo.ControlChan = from->AddHtInfo.ControlChan;
1676	to->AddHtInfo.AddChan = from->AddHtInfo.AddChan;
1677	to->AddHtInfo.OpMode = htole16(from->AddHtInfo.OpMode);
1678	to->AddHtInfo.stbc = htole16(from->AddHtInfo.stbc);
1679}
1680
1681/* XXX station id must be in [0..63] */
1682int
1683mwl_hal_newstation(struct mwl_hal_vap *vap,
1684	const uint8_t addr[IEEE80211_ADDR_LEN], uint16_t aid, uint16_t sid,
1685	const MWL_HAL_PEERINFO *peer, int isQosSta, int wmeInfo)
1686{
1687	struct mwl_hal_priv *mh = MWLVAP(vap);
1688	HostCmd_FW_SET_NEW_STN *pCmd;
1689	int retval;
1690
1691	MWL_HAL_LOCK(mh);
1692	_VCMD_SETUP(vap, pCmd, HostCmd_FW_SET_NEW_STN, HostCmd_CMD_SET_NEW_STN);
1693	pCmd->AID = htole16(aid);
1694	pCmd->StnId = htole16(sid);
1695	pCmd->Action = htole16(0);	/* SET */
1696	if (peer != NULL) {
1697		/* NB: must fix up byte order */
1698		cvtPeerInfo(&pCmd->PeerInfo, peer);
1699	}
1700	IEEE80211_ADDR_COPY(&pCmd->MacAddr[0], addr);
1701	pCmd->Qosinfo = wmeInfo;
1702	pCmd->isQosSta = (isQosSta != 0);
1703
1704	retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_NEW_STN);
1705	if (retval == 0 && IEEE80211_ADDR_EQ(vap->mac, addr))
1706		vap->flags |= MVF_STATION;
1707	MWL_HAL_UNLOCK(mh);
1708	return retval;
1709}
1710
1711int
1712mwl_hal_delstation(struct mwl_hal_vap *vap,
1713	const uint8_t addr[IEEE80211_ADDR_LEN])
1714{
1715	struct mwl_hal_priv *mh = MWLVAP(vap);
1716	HostCmd_FW_SET_NEW_STN *pCmd;
1717	int retval, islocal;
1718
1719	MWL_HAL_LOCK(mh);
1720	islocal = IEEE80211_ADDR_EQ(vap->mac, addr);
1721	if (!islocal || (vap->flags & MVF_STATION)) {
1722		_VCMD_SETUP(vap, pCmd, HostCmd_FW_SET_NEW_STN,
1723		    HostCmd_CMD_SET_NEW_STN);
1724		pCmd->Action = htole16(2);	/* REMOVE */
1725		IEEE80211_ADDR_COPY(&pCmd->MacAddr[0], addr);
1726		retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_NEW_STN);
1727		if (islocal)
1728			vap->flags &= ~MVF_STATION;
1729	} else
1730		retval = 0;
1731	MWL_HAL_UNLOCK(mh);
1732	return retval;
1733}
1734
1735/*
1736 * Prod the firmware to age packets on station power
1737 * save queues and reap frames on the tx aggregation q's.
1738 */
1739int
1740mwl_hal_setkeepalive(struct mwl_hal *mh0)
1741{
1742	struct mwl_hal_priv *mh = MWLPRIV(mh0);
1743	HostCmd_FW_SET_KEEP_ALIVE_TICK *pCmd;
1744	int retval;
1745
1746	MWL_HAL_LOCK(mh);
1747	_CMD_SETUP(pCmd, HostCmd_FW_SET_KEEP_ALIVE_TICK,
1748		HostCmd_CMD_SET_KEEP_ALIVE);
1749	/*
1750	 * NB: tick must be 0 to prod the f/w;
1751	 *     a non-zero value is a noop.
1752	 */
1753	pCmd->tick = 0;
1754
1755	retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_KEEP_ALIVE);
1756	MWL_HAL_UNLOCK(mh);
1757	return retval;
1758}
1759
1760int
1761mwl_hal_setapmode(struct mwl_hal_vap *vap, MWL_HAL_APMODE ApMode)
1762{
1763	struct mwl_hal_priv *mh = MWLVAP(vap);
1764	HostCmd_FW_SET_APMODE *pCmd;
1765	int retval;
1766
1767	/* XXX validate ApMode? */
1768
1769	MWL_HAL_LOCK(mh);
1770	_VCMD_SETUP(vap, pCmd, HostCmd_FW_SET_APMODE, HostCmd_CMD_SET_APMODE);
1771	pCmd->ApMode = ApMode;
1772
1773	retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_APMODE);
1774	MWL_HAL_UNLOCK(mh);
1775	return retval;
1776}
1777
1778int
1779mwl_hal_stop(struct mwl_hal_vap *vap)
1780{
1781	struct mwl_hal_priv *mh = MWLVAP(vap);
1782	HostCmd_DS_BSS_START *pCmd;
1783	int retval;
1784
1785	MWL_HAL_LOCK(mh);
1786	if (vap->flags & MVF_RUNNING) {
1787		_VCMD_SETUP(vap, pCmd, HostCmd_DS_BSS_START,
1788		    HostCmd_CMD_BSS_START);
1789		pCmd->Enable = htole32(HostCmd_ACT_GEN_OFF);
1790		retval = mwlExecuteCmd(mh, HostCmd_CMD_BSS_START);
1791	} else
1792		retval = 0;
1793	/* NB: mark !running regardless */
1794	vap->flags &= ~MVF_RUNNING;
1795	MWL_HAL_UNLOCK(mh);
1796	return retval;
1797}
1798
1799int
1800mwl_hal_start(struct mwl_hal_vap *vap)
1801{
1802	struct mwl_hal_priv *mh = MWLVAP(vap);
1803	HostCmd_DS_BSS_START *pCmd;
1804	int retval;
1805
1806	MWL_HAL_LOCK(mh);
1807	_VCMD_SETUP(vap, pCmd, HostCmd_DS_BSS_START, HostCmd_CMD_BSS_START);
1808	pCmd->Enable = htole32(HostCmd_ACT_GEN_ON);
1809
1810	retval = mwlExecuteCmd(mh, HostCmd_CMD_BSS_START);
1811	if (retval == 0)
1812		vap->flags |= MVF_RUNNING;
1813	MWL_HAL_UNLOCK(mh);
1814	return retval;
1815}
1816
1817int
1818mwl_hal_setgprot(struct mwl_hal *mh0, int prot)
1819{
1820	struct mwl_hal_priv *mh = MWLPRIV(mh0);
1821	HostCmd_FW_SET_G_PROTECT_FLAG *pCmd;
1822	int retval;
1823
1824	MWL_HAL_LOCK(mh);
1825	_CMD_SETUP(pCmd, HostCmd_FW_SET_G_PROTECT_FLAG,
1826		HostCmd_CMD_SET_G_PROTECT_FLAG);
1827	pCmd->GProtectFlag  = htole32(prot);
1828
1829	retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_G_PROTECT_FLAG);
1830	MWL_HAL_UNLOCK(mh);
1831	return retval;
1832}
1833
1834int
1835mwl_hal_setwmm(struct mwl_hal *mh0, int onoff)
1836{
1837	struct mwl_hal_priv *mh = MWLPRIV(mh0);
1838	HostCmd_FW_SetWMMMode *pCmd;
1839	int retval;
1840
1841	MWL_HAL_LOCK(mh);
1842	_CMD_SETUP(pCmd, HostCmd_FW_SetWMMMode,
1843		HostCmd_CMD_SET_WMM_MODE);
1844	pCmd->Action = htole16(onoff);
1845
1846	retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_WMM_MODE);
1847	MWL_HAL_UNLOCK(mh);
1848	return retval;
1849}
1850
1851int
1852mwl_hal_setedcaparams(struct mwl_hal *mh0, uint8_t qnum,
1853	uint32_t CWmin, uint32_t CWmax, uint8_t AIFSN,  uint16_t TXOPLimit)
1854{
1855	struct mwl_hal_priv *mh = MWLPRIV(mh0);
1856	HostCmd_FW_SET_EDCA_PARAMS *pCmd;
1857	int retval;
1858
1859	MWL_HAL_LOCK(mh);
1860	_CMD_SETUP(pCmd, HostCmd_FW_SET_EDCA_PARAMS,
1861		HostCmd_CMD_SET_EDCA_PARAMS);
1862	/*
1863	 * NB: CWmin and CWmax are always set.
1864	 *     TxOpLimit is set if bit 0x2 is marked in Action
1865	 *     AIFSN is set if bit 0x4 is marked in Action
1866	 */
1867	pCmd->Action = htole16(0xffff);	/* NB: set everything */
1868	pCmd->TxOP = htole16(TXOPLimit);
1869	pCmd->CWMax = htole32(CWmax);
1870	pCmd->CWMin = htole32(CWmin);
1871	pCmd->AIFSN = AIFSN;
1872	pCmd->TxQNum = qnum;		/* XXX check */
1873
1874	retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_EDCA_PARAMS);
1875	MWL_HAL_UNLOCK(mh);
1876	return retval;
1877}
1878
1879/* XXX 0 = indoor, 1 = outdoor */
1880int
1881mwl_hal_setrateadaptmode(struct mwl_hal *mh0, uint16_t mode)
1882{
1883	struct mwl_hal_priv *mh = MWLPRIV(mh0);
1884	HostCmd_DS_SET_RATE_ADAPT_MODE *pCmd;
1885	int retval;
1886
1887	MWL_HAL_LOCK(mh);
1888	_CMD_SETUP(pCmd, HostCmd_DS_SET_RATE_ADAPT_MODE,
1889		HostCmd_CMD_SET_RATE_ADAPT_MODE);
1890	pCmd->Action = htole16(HostCmd_ACT_GEN_SET);
1891	pCmd->RateAdaptMode = htole16(mode);
1892
1893	retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_RATE_ADAPT_MODE);
1894	MWL_HAL_UNLOCK(mh);
1895	return retval;
1896}
1897
1898int
1899mwl_hal_setcsmode(struct mwl_hal *mh0, MWL_HAL_CSMODE csmode)
1900{
1901	struct mwl_hal_priv *mh = MWLPRIV(mh0);
1902	HostCmd_DS_SET_LINKADAPT_CS_MODE *pCmd;
1903	int retval;
1904
1905	MWL_HAL_LOCK(mh);
1906	_CMD_SETUP(pCmd, HostCmd_DS_SET_LINKADAPT_CS_MODE,
1907		HostCmd_CMD_SET_LINKADAPT_CS_MODE);
1908	pCmd->Action = htole16(HostCmd_ACT_GEN_SET);
1909	pCmd->CSMode = htole16(csmode);
1910
1911	retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_LINKADAPT_CS_MODE);
1912	MWL_HAL_UNLOCK(mh);
1913	return retval;
1914}
1915
1916int
1917mwl_hal_setnprot(struct mwl_hal_vap *vap, MWL_HAL_HTPROTECT mode)
1918{
1919	struct mwl_hal_priv *mh = MWLVAP(vap);
1920	HostCmd_FW_SET_N_PROTECT_FLAG *pCmd;
1921	int retval;
1922
1923	/* XXX validate mode */
1924	MWL_HAL_LOCK(mh);
1925	_VCMD_SETUP(vap, pCmd, HostCmd_FW_SET_N_PROTECT_FLAG,
1926		HostCmd_CMD_SET_N_PROTECT_FLAG);
1927	pCmd->NProtectFlag  = htole32(mode);
1928
1929	retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_N_PROTECT_FLAG);
1930	MWL_HAL_UNLOCK(mh);
1931	return retval;
1932}
1933
1934int
1935mwl_hal_setnprotmode(struct mwl_hal_vap *vap, uint8_t mode)
1936{
1937	struct mwl_hal_priv *mh = MWLVAP(vap);
1938	HostCmd_FW_SET_N_PROTECT_OPMODE *pCmd;
1939	int retval;
1940
1941	MWL_HAL_LOCK(mh);
1942	_VCMD_SETUP(vap, pCmd, HostCmd_FW_SET_N_PROTECT_OPMODE,
1943		HostCmd_CMD_SET_N_PROTECT_OPMODE);
1944	pCmd->NProtectOpMode = mode;
1945
1946	retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_N_PROTECT_OPMODE);
1947	MWL_HAL_UNLOCK(mh);
1948	return retval;
1949}
1950
1951int
1952mwl_hal_setoptimizationlevel(struct mwl_hal *mh0, int level)
1953{
1954	struct mwl_hal_priv *mh = MWLPRIV(mh0);
1955	HostCmd_FW_SET_OPTIMIZATION_LEVEL *pCmd;
1956	int retval;
1957
1958	MWL_HAL_LOCK(mh);
1959	_CMD_SETUP(pCmd, HostCmd_FW_SET_OPTIMIZATION_LEVEL,
1960		HostCmd_CMD_SET_OPTIMIZATION_LEVEL);
1961	pCmd->OptLevel = level;
1962
1963	retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_OPTIMIZATION_LEVEL);
1964	MWL_HAL_UNLOCK(mh);
1965	return retval;
1966}
1967
1968int
1969mwl_hal_setmimops(struct mwl_hal *mh0, const uint8_t addr[IEEE80211_ADDR_LEN],
1970	uint8_t enable, uint8_t mode)
1971{
1972	struct mwl_hal_priv *mh = MWLPRIV(mh0);
1973	HostCmd_FW_SET_MIMOPSHT *pCmd;
1974	int retval;
1975
1976	MWL_HAL_LOCK(mh);
1977	_CMD_SETUP(pCmd, HostCmd_FW_SET_MIMOPSHT, HostCmd_CMD_SET_MIMOPSHT);
1978	IEEE80211_ADDR_COPY(pCmd->Addr, addr);
1979	pCmd->Enable = enable;
1980	pCmd->Mode = mode;
1981
1982	retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_MIMOPSHT);
1983	MWL_HAL_UNLOCK(mh);
1984	return retval;
1985}
1986
1987static int
1988mwlGetCalTable(struct mwl_hal_priv *mh, uint8_t annex, uint8_t index)
1989{
1990	HostCmd_FW_GET_CALTABLE *pCmd;
1991	int retval;
1992
1993	MWL_HAL_LOCK_ASSERT(mh);
1994
1995	_CMD_SETUP(pCmd, HostCmd_FW_GET_CALTABLE, HostCmd_CMD_GET_CALTABLE);
1996	pCmd->annex = annex;
1997	pCmd->index = index;
1998	memset(pCmd->calTbl, 0, sizeof(pCmd->calTbl));
1999
2000	retval = mwlExecuteCmd(mh, HostCmd_CMD_GET_CALTABLE);
2001	if (retval == 0 &&
2002	    pCmd->calTbl[0] != annex && annex != 0 && annex != 255)
2003		retval = EIO;
2004	return retval;
2005}
2006
2007/*
2008 * Calculate the max tx power from the channel's cal data.
2009 */
2010static void
2011setmaxtxpow(struct mwl_hal_channel *hc, int i, int maxix)
2012{
2013	hc->maxTxPow = hc->targetPowers[i];
2014	for (i++; i < maxix; i++)
2015		if (hc->targetPowers[i] > hc->maxTxPow)
2016			hc->maxTxPow = hc->targetPowers[i];
2017}
2018
2019/*
2020 * Construct channel info for 5GHz channels from cal data.
2021 */
2022static void
2023get5Ghz(MWL_HAL_CHANNELINFO *ci, const uint8_t table[], int len)
2024{
2025	int i, j, f, l, h;
2026
2027	l = 32000;
2028	h = 0;
2029	j = 0;
2030	for (i = 0; i < len; i += 4) {
2031		struct mwl_hal_channel *hc;
2032
2033		if (table[i] == 0)
2034			continue;
2035		f = 5000 + 5*table[i];
2036		if (f < l)
2037			l = f;
2038		if (f > h)
2039			h = f;
2040		hc = &ci->channels[j];
2041		hc->freq = f;
2042		hc->ieee = table[i];
2043		memcpy(hc->targetPowers, &table[i], 4);
2044		setmaxtxpow(hc, 1, 4);		/* NB: col 1 is the freq, skip*/
2045		j++;
2046	}
2047	ci->nchannels = j;
2048	ci->freqLow = (l == 32000) ? 0 : l;
2049	ci->freqHigh = h;
2050}
2051
2052static uint16_t
2053ieee2mhz(int chan)
2054{
2055	if (chan == 14)
2056		return 2484;
2057	if (chan < 14)
2058		return 2407 + chan*5;
2059	return 2512 + (chan-15)*20;
2060}
2061
2062/*
2063 * Construct channel info for 2.4GHz channels from cal data.
2064 */
2065static void
2066get2Ghz(MWL_HAL_CHANNELINFO *ci, const uint8_t table[], int len)
2067{
2068	int i, j;
2069
2070	j = 0;
2071	for (i = 0; i < len; i += 4) {
2072		struct mwl_hal_channel *hc = &ci->channels[j];
2073		hc->ieee = 1+j;
2074		hc->freq = ieee2mhz(1+j);
2075		memcpy(hc->targetPowers, &table[i], 4);
2076		setmaxtxpow(hc, 0, 4);
2077		j++;
2078	}
2079	ci->nchannels = j;
2080	ci->freqLow = ieee2mhz(1);
2081	ci->freqHigh = ieee2mhz(j);
2082}
2083
2084#undef DUMPCALDATA
2085#ifdef DUMPCALDATA
2086static void
2087dumpcaldata(const char *name, const uint8_t *table, int n)
2088{
2089	int i;
2090	printf("\n%s:\n", name);
2091	for (i = 0; i < n; i += 4)
2092		printf("[%2d] %3d %3d %3d %3d\n", i/4, table[i+0], table[i+1], table[i+2], table[i+3]);
2093}
2094#endif
2095
2096static int
2097mwlGetPwrCalTable(struct mwl_hal_priv *mh)
2098{
2099	const uint8_t *data;
2100	MWL_HAL_CHANNELINFO *ci;
2101	int len;
2102
2103	MWL_HAL_LOCK(mh);
2104	/* NB: we hold the lock so it's ok to use cmdbuf */
2105	data = ((const HostCmd_FW_GET_CALTABLE *) mh->mh_cmdbuf)->calTbl;
2106	if (mwlGetCalTable(mh, 33, 0) == 0) {
2107		len = (data[2] | (data[3] << 8)) - 12;
2108		if (len > PWTAGETRATETABLE20M)
2109			len = PWTAGETRATETABLE20M;
2110#ifdef DUMPCALDATA
2111dumpcaldata("2.4G 20M", &data[12], len);/*XXX*/
2112#endif
2113		get2Ghz(&mh->mh_20M, &data[12], len);
2114	}
2115	if (mwlGetCalTable(mh, 34, 0) == 0) {
2116		len = (data[2] | (data[3] << 8)) - 12;
2117		if (len > PWTAGETRATETABLE40M)
2118			len = PWTAGETRATETABLE40M;
2119#ifdef DUMPCALDATA
2120dumpcaldata("2.4G 40M", &data[12], len);/*XXX*/
2121#endif
2122		ci = &mh->mh_40M;
2123		get2Ghz(ci, &data[12], len);
2124	}
2125	if (mwlGetCalTable(mh, 35, 0) == 0) {
2126		len = (data[2] | (data[3] << 8)) - 20;
2127		if (len > PWTAGETRATETABLE20M_5G)
2128			len = PWTAGETRATETABLE20M_5G;
2129#ifdef DUMPCALDATA
2130dumpcaldata("5G 20M", &data[20], len);/*XXX*/
2131#endif
2132		get5Ghz(&mh->mh_20M_5G, &data[20], len);
2133	}
2134	if (mwlGetCalTable(mh, 36, 0) == 0) {
2135		len = (data[2] | (data[3] << 8)) - 20;
2136		if (len > PWTAGETRATETABLE40M_5G)
2137			len = PWTAGETRATETABLE40M_5G;
2138#ifdef DUMPCALDATA
2139dumpcaldata("5G 40M", &data[20], len);/*XXX*/
2140#endif
2141		ci = &mh->mh_40M_5G;
2142		get5Ghz(ci, &data[20], len);
2143	}
2144	mh->mh_flags |= MHF_CALDATA;
2145	MWL_HAL_UNLOCK(mh);
2146	return 0;
2147}
2148
2149int
2150mwl_hal_getregioncode(struct mwl_hal *mh0, uint8_t *countryCode)
2151{
2152	struct mwl_hal_priv *mh = MWLPRIV(mh0);
2153	int retval;
2154
2155	MWL_HAL_LOCK(mh);
2156	retval = mwlGetCalTable(mh, 0, 0);
2157	if (retval == 0) {
2158		const HostCmd_FW_GET_CALTABLE *pCmd =
2159		    (const HostCmd_FW_GET_CALTABLE *) mh->mh_cmdbuf;
2160		*countryCode = pCmd->calTbl[16];
2161	}
2162	MWL_HAL_UNLOCK(mh);
2163	return retval;
2164}
2165
2166int
2167mwl_hal_setpromisc(struct mwl_hal *mh0, int ena)
2168{
2169	struct mwl_hal_priv *mh = MWLPRIV(mh0);
2170	uint32_t v;
2171
2172	MWL_HAL_LOCK(mh);
2173	v = RD4(mh, MACREG_REG_PROMISCUOUS);
2174	WR4(mh, MACREG_REG_PROMISCUOUS, ena ? v | 1 : v &~ 1);
2175	MWL_HAL_UNLOCK(mh);
2176	return 0;
2177}
2178
2179int
2180mwl_hal_getpromisc(struct mwl_hal *mh0)
2181{
2182	struct mwl_hal_priv *mh = MWLPRIV(mh0);
2183	uint32_t v;
2184
2185	MWL_HAL_LOCK(mh);
2186	v = RD4(mh, MACREG_REG_PROMISCUOUS);
2187	MWL_HAL_UNLOCK(mh);
2188	return (v & 1) != 0;
2189}
2190
2191int
2192mwl_hal_GetBeacon(struct mwl_hal *mh0, uint8_t *pBcn, uint16_t *pLen)
2193{
2194	struct mwl_hal_priv *mh = MWLPRIV(mh0);
2195	HostCmd_FW_GET_BEACON *pCmd;
2196	int retval;
2197
2198	MWL_HAL_LOCK(mh);
2199	_CMD_SETUP(pCmd, HostCmd_FW_GET_BEACON, HostCmd_CMD_GET_BEACON);
2200	pCmd->Bcnlen = htole16(0);
2201
2202	retval = mwlExecuteCmd(mh, HostCmd_CMD_GET_BEACON);
2203	if (retval == 0) {
2204		/* XXX bounds check */
2205		memcpy(pBcn, &pCmd->Bcn, pCmd->Bcnlen);
2206		*pLen = pCmd->Bcnlen;
2207	}
2208	MWL_HAL_UNLOCK(mh);
2209	return retval;
2210}
2211
2212int
2213mwl_hal_SetRifs(struct mwl_hal *mh0, uint8_t QNum)
2214{
2215	struct mwl_hal_priv *mh = MWLPRIV(mh0);
2216	HostCmd_FW_SET_RIFS  *pCmd;
2217	int retval;
2218
2219	MWL_HAL_LOCK(mh);
2220	_CMD_SETUP(pCmd, HostCmd_FW_SET_RIFS, HostCmd_CMD_SET_RIFS);
2221	pCmd->QNum = QNum;
2222
2223	retval = mwlExecuteCmd(mh, HostCmd_CMD_SET_RIFS);
2224	MWL_HAL_UNLOCK(mh);
2225	return retval;
2226}
2227
2228/*
2229 * Diagnostic api's for set/get registers.
2230 */
2231
2232static int
2233getRFReg(struct mwl_hal_priv *mh, int flag, uint32_t reg, uint32_t *val)
2234{
2235	HostCmd_DS_RF_REG_ACCESS *pCmd;
2236	int retval;
2237
2238	MWL_HAL_LOCK(mh);
2239	_CMD_SETUP(pCmd, HostCmd_DS_RF_REG_ACCESS, HostCmd_CMD_RF_REG_ACCESS);
2240	pCmd->Offset =  htole16(reg);
2241	pCmd->Action = htole16(flag);
2242	pCmd->Value = htole32(*val);
2243
2244	retval = mwlExecuteCmd(mh, HostCmd_CMD_RF_REG_ACCESS);
2245	if (retval == 0)
2246		*val = pCmd->Value;
2247	MWL_HAL_UNLOCK(mh);
2248	return retval;
2249}
2250
2251static int
2252getBBReg(struct mwl_hal_priv *mh, int flag, uint32_t reg, uint32_t *val)
2253{
2254	HostCmd_DS_BBP_REG_ACCESS *pCmd;
2255	int retval;
2256
2257	MWL_HAL_LOCK(mh);
2258	_CMD_SETUP(pCmd, HostCmd_DS_BBP_REG_ACCESS, HostCmd_CMD_BBP_REG_ACCESS);
2259	pCmd->Offset =  htole16(reg);
2260	pCmd->Action = htole16(flag);
2261	pCmd->Value = htole32(*val);
2262
2263	retval = mwlExecuteCmd(mh, HostCmd_CMD_BBP_REG_ACCESS);
2264	if (retval == 0)
2265		*val = pCmd->Value;
2266	MWL_HAL_UNLOCK(mh);
2267	return retval;
2268}
2269
2270static u_int
2271mwl_hal_getregdump(struct mwl_hal_priv *mh, const MWL_DIAG_REGRANGE *regs,
2272	void *dstbuf, int space)
2273{
2274	uint32_t *dp = dstbuf;
2275	int i;
2276
2277	for (i = 0; space >= 2*sizeof(uint32_t); i++) {
2278		u_int r = regs[i].start;
2279		u_int e = regs[i].end;
2280		*dp++ = (r<<16) | e;
2281		space -= sizeof(uint32_t);
2282		do {
2283			if (MWL_DIAG_ISMAC(r))
2284				*dp = RD4(mh, r);
2285			else if (MWL_DIAG_ISBB(r))
2286				getBBReg(mh, HostCmd_ACT_GEN_READ,
2287				    r - MWL_DIAG_BASE_BB, dp);
2288			else if (MWL_DIAG_ISRF(r))
2289				getRFReg(mh, HostCmd_ACT_GEN_READ,
2290				    r - MWL_DIAG_BASE_RF, dp);
2291			else if (r < 0x1000 || r == MACREG_REG_FW_PRESENT)
2292				*dp = RD4(mh, r);
2293			else
2294				*dp = 0xffffffff;
2295			dp++;
2296			r += sizeof(uint32_t);
2297			space -= sizeof(uint32_t);
2298		} while (r <= e && space >= sizeof(uint32_t));
2299	}
2300	return (char *) dp - (char *) dstbuf;
2301}
2302
2303int
2304mwl_hal_getdiagstate(struct mwl_hal *mh0, int request,
2305	const void *args, uint32_t argsize,
2306	void **result, uint32_t *resultsize)
2307{
2308	struct mwl_hal_priv *mh = MWLPRIV(mh0);
2309
2310	switch (request) {
2311	case MWL_DIAG_CMD_REVS:
2312		*result = &mh->mh_revs;
2313		*resultsize = sizeof(mh->mh_revs);
2314		return 1;
2315	case MWL_DIAG_CMD_REGS:
2316		*resultsize = mwl_hal_getregdump(mh, args, *result, *resultsize);
2317		return 1;
2318	case MWL_DIAG_CMD_HOSTCMD: {
2319		FWCmdHdr *pCmd = (FWCmdHdr *) &mh->mh_cmdbuf[0];
2320		int retval;
2321
2322		MWL_HAL_LOCK(mh);
2323		memcpy(pCmd, args, argsize);
2324		retval = mwlExecuteCmd(mh, le16toh(pCmd->Cmd));
2325		*result = (*resultsize != 0) ? pCmd : NULL;
2326		MWL_HAL_UNLOCK(mh);
2327		return (retval == 0);
2328	}
2329	case MWL_DIAG_CMD_FWLOAD:
2330		if (mwl_hal_fwload(mh0, __DECONST(void *, args))) {
2331			device_printf(mh->mh_dev, "problem loading fw image\n");
2332			return 0;
2333		}
2334		return 1;
2335	}
2336	return 0;
2337}
2338
2339/*
2340 * Low level firmware cmd block handshake support.
2341 */
2342
2343static void
2344mwlSendCmd(struct mwl_hal_priv *mh)
2345{
2346	uint32_t dummy;
2347
2348	bus_dmamap_sync(mh->mh_dmat, mh->mh_dmamap,
2349	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
2350
2351	WR4(mh, MACREG_REG_GEN_PTR, mh->mh_cmdaddr);
2352	dummy = RD4(mh, MACREG_REG_INT_CODE);
2353
2354	WR4(mh, MACREG_REG_H2A_INTERRUPT_EVENTS, MACREG_H2ARIC_BIT_DOOR_BELL);
2355}
2356
2357static int
2358mwlWaitForCmdComplete(struct mwl_hal_priv *mh, uint16_t cmdCode)
2359{
2360#define MAX_WAIT_FW_COMPLETE_ITERATIONS 10000
2361	int i;
2362
2363	for (i = 0; i < MAX_WAIT_FW_COMPLETE_ITERATIONS; i++) {
2364		if (mh->mh_cmdbuf[0] == le16toh(cmdCode))
2365			return 1;
2366		DELAY(1*1000);
2367	}
2368	return 0;
2369#undef MAX_WAIT_FW_COMPLETE_ITERATIONS
2370}
2371
2372static int
2373mwlExecuteCmd(struct mwl_hal_priv *mh, unsigned short cmd)
2374{
2375
2376	MWL_HAL_LOCK_ASSERT(mh);
2377
2378	if ((mh->mh_flags & MHF_FWHANG) &&
2379	    (mh->mh_debug & MWL_HAL_DEBUG_IGNHANG) == 0) {
2380#ifdef MWLHAL_DEBUG
2381		device_printf(mh->mh_dev, "firmware hung, skipping cmd %s\n",
2382			mwlcmdname(cmd));
2383#else
2384		device_printf(mh->mh_dev, "firmware hung, skipping cmd 0x%x\n",
2385			cmd);
2386#endif
2387		return ENXIO;
2388	}
2389	if (RD4(mh,  MACREG_REG_INT_CODE) == 0xffffffff) {
2390		device_printf(mh->mh_dev, "%s: device not present!\n",
2391		    __func__);
2392		return EIO;
2393	}
2394#ifdef MWLHAL_DEBUG
2395	if (mh->mh_debug & MWL_HAL_DEBUG_SENDCMD)
2396		dumpresult(mh, 0);
2397#endif
2398	mwlSendCmd(mh);
2399	if (!mwlWaitForCmdComplete(mh, 0x8000 | cmd)) {
2400#ifdef MWLHAL_DEBUG
2401		device_printf(mh->mh_dev,
2402		    "timeout waiting for f/w cmd %s\n", mwlcmdname(cmd));
2403#else
2404		device_printf(mh->mh_dev,
2405		    "timeout waiting for f/w cmd 0x%x\n", cmd);
2406#endif
2407		mh->mh_flags |= MHF_FWHANG;
2408		return ETIMEDOUT;
2409	}
2410	bus_dmamap_sync(mh->mh_dmat, mh->mh_dmamap,
2411	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
2412#ifdef MWLHAL_DEBUG
2413	if (mh->mh_debug & MWL_HAL_DEBUG_CMDDONE)
2414		dumpresult(mh, 1);
2415#endif
2416	return 0;
2417}
2418
2419/*
2420 * Firmware download support.
2421 */
2422#define FW_DOWNLOAD_BLOCK_SIZE	256
2423#define FW_CHECK_USECS		(5*1000)	/* 5ms */
2424#define FW_MAX_NUM_CHECKS	200
2425
2426#if 0
2427/* XXX read f/w from file */
2428#include <dev/mwl/mwlbootfw.h>
2429#include <dev/mwl/mwl88W8363fw.h>
2430#endif
2431
2432static void
2433mwlFwReset(struct mwl_hal_priv *mh)
2434{
2435	if (RD4(mh,  MACREG_REG_INT_CODE) == 0xffffffff) {
2436		device_printf(mh->mh_dev, "%s: device not present!\n",
2437		    __func__);
2438		return;
2439	}
2440	WR4(mh, MACREG_REG_H2A_INTERRUPT_EVENTS, ISR_RESET);
2441	mh->mh_flags &= ~MHF_FWHANG;
2442}
2443
2444static void
2445mwlTriggerPciCmd(struct mwl_hal_priv *mh)
2446{
2447	uint32_t dummy;
2448
2449	bus_dmamap_sync(mh->mh_dmat, mh->mh_dmamap, BUS_DMASYNC_PREWRITE);
2450
2451	WR4(mh, MACREG_REG_GEN_PTR, mh->mh_cmdaddr);
2452	dummy = RD4(mh, MACREG_REG_INT_CODE);
2453
2454	WR4(mh, MACREG_REG_INT_CODE, 0x00);
2455	dummy = RD4(mh, MACREG_REG_INT_CODE);
2456
2457	WR4(mh, MACREG_REG_H2A_INTERRUPT_EVENTS, MACREG_H2ARIC_BIT_DOOR_BELL);
2458	dummy = RD4(mh, MACREG_REG_INT_CODE);
2459}
2460
2461static int
2462mwlWaitFor(struct mwl_hal_priv *mh, uint32_t val)
2463{
2464	int i;
2465
2466	for (i = 0; i < FW_MAX_NUM_CHECKS; i++) {
2467		DELAY(FW_CHECK_USECS);
2468		if (RD4(mh, MACREG_REG_INT_CODE) == val)
2469			return 1;
2470	}
2471	return 0;
2472}
2473
2474/*
2475 * Firmware block xmit when talking to the boot-rom.
2476 */
2477static int
2478mwlSendBlock(struct mwl_hal_priv *mh, int bsize, const void *data, size_t dsize)
2479{
2480	mh->mh_cmdbuf[0] = htole16(HostCmd_CMD_CODE_DNLD);
2481	mh->mh_cmdbuf[1] = htole16(bsize);
2482	memcpy(&mh->mh_cmdbuf[4], data , dsize);
2483	mwlTriggerPciCmd(mh);
2484	/* XXX 2000 vs 200 */
2485	if (mwlWaitFor(mh, MACREG_INT_CODE_CMD_FINISHED)) {
2486		WR4(mh, MACREG_REG_INT_CODE, 0);
2487		return 1;
2488	}
2489	device_printf(mh->mh_dev,
2490	    "%s: timeout waiting for CMD_FINISHED, INT_CODE 0x%x\n",
2491	    __func__, RD4(mh, MACREG_REG_INT_CODE));
2492	return 0;
2493}
2494
2495/*
2496 * Firmware block xmit when talking to the 1st-stage loader.
2497 */
2498static int
2499mwlSendBlock2(struct mwl_hal_priv *mh, const void *data, size_t dsize)
2500{
2501	memcpy(&mh->mh_cmdbuf[0], data, dsize);
2502	mwlTriggerPciCmd(mh);
2503	if (mwlWaitFor(mh, MACREG_INT_CODE_CMD_FINISHED)) {
2504		WR4(mh, MACREG_REG_INT_CODE, 0);
2505		return 1;
2506	}
2507	device_printf(mh->mh_dev,
2508	    "%s: timeout waiting for CMD_FINISHED, INT_CODE 0x%x\n",
2509	    __func__, RD4(mh, MACREG_REG_INT_CODE));
2510	return 0;
2511}
2512
2513static void
2514mwlPokeSdramController(struct mwl_hal_priv *mh, int SDRAMSIZE_Addr)
2515{
2516	/** Set up sdram controller for superflyv2 **/
2517	WR4(mh, 0x00006014, 0x33);
2518	WR4(mh, 0x00006018, 0xa3a2632);
2519	WR4(mh, 0x00006010, SDRAMSIZE_Addr);
2520}
2521
2522int
2523mwl_hal_fwload(struct mwl_hal *mh0, void *fwargs)
2524{
2525	struct mwl_hal_priv *mh = MWLPRIV(mh0);
2526	const char *fwname = "mw88W8363fw";
2527	const char *fwbootname = "mwlboot";
2528	const struct firmware *fwboot = NULL;
2529	const struct firmware *fw;
2530	/* XXX get from firmware header */
2531	uint32_t FwReadySignature = HostCmd_SOFTAP_FWRDY_SIGNATURE;
2532	uint32_t OpMode = HostCmd_SOFTAP_MODE;
2533	const uint8_t *fp, *ep;
2534	const uint8_t *fmdata;
2535	uint32_t blocksize, nbytes, fmsize;
2536	int i, error, ntries;
2537
2538	fw = firmware_get(fwname);
2539	if (fw == NULL) {
2540		device_printf(mh->mh_dev,
2541		    "could not load firmware image %s\n", fwname);
2542		return ENXIO;
2543	}
2544	fmdata = fw->data;
2545	fmsize = fw->datasize;
2546	if (fmsize < 4) {
2547		device_printf(mh->mh_dev, "firmware image %s too small\n",
2548		    fwname);
2549		error = ENXIO;
2550		goto bad2;
2551	}
2552	if (fmdata[0] == 0x01 && fmdata[1] == 0x00 &&
2553	    fmdata[2] == 0x00 && fmdata[3] == 0x00) {
2554		/*
2555		 * 2-stage load, get the boot firmware.
2556		 */
2557		fwboot = firmware_get(fwbootname);
2558		if (fwboot == NULL) {
2559			device_printf(mh->mh_dev,
2560			    "could not load firmware image %s\n", fwbootname);
2561			error = ENXIO;
2562			goto bad2;
2563		}
2564	} else
2565		fwboot = NULL;
2566
2567	mwlFwReset(mh);
2568
2569	WR4(mh, MACREG_REG_A2H_INTERRUPT_CLEAR_SEL, MACREG_A2HRIC_BIT_MASK);
2570	WR4(mh, MACREG_REG_A2H_INTERRUPT_CAUSE, 0x00);
2571	WR4(mh, MACREG_REG_A2H_INTERRUPT_MASK, 0x00);
2572	WR4(mh, MACREG_REG_A2H_INTERRUPT_STATUS_MASK, MACREG_A2HRIC_BIT_MASK);
2573	if (mh->mh_SDRAMSIZE_Addr != 0) {
2574		/** Set up sdram controller for superflyv2 **/
2575		mwlPokeSdramController(mh, mh->mh_SDRAMSIZE_Addr);
2576	}
2577	device_printf(mh->mh_dev, "load %s firmware image (%u bytes)\n",
2578	    fwname, fmsize);
2579	if (fwboot != NULL) {
2580		/*
2581		 * Do 2-stage load.  The 1st stage loader is setup
2582		 * with the bootrom loader then we load the real
2583		 * image using a different handshake. With this
2584		 * mechanism the firmware is segmented into chunks
2585		 * that have a CRC.  If a chunk is incorrect we'll
2586		 * be told to retransmit.
2587		 */
2588		/* XXX assumes hlpimage fits in a block */
2589		/* NB: zero size block indicates download is finished */
2590		if (!mwlSendBlock(mh, fwboot->datasize, fwboot->data, fwboot->datasize) ||
2591		    !mwlSendBlock(mh, 0, NULL, 0)) {
2592			error = ETIMEDOUT;
2593			goto bad;
2594		}
2595		DELAY(200*FW_CHECK_USECS);
2596		if (mh->mh_SDRAMSIZE_Addr != 0) {
2597			/** Set up sdram controller for superflyv2 **/
2598			mwlPokeSdramController(mh, mh->mh_SDRAMSIZE_Addr);
2599		}
2600		nbytes = ntries = 0;		/* NB: silence compiler */
2601		for (fp = fmdata, ep = fp + fmsize; fp < ep; ) {
2602			WR4(mh, MACREG_REG_INT_CODE, 0);
2603			blocksize = RD4(mh, MACREG_REG_SCRATCH);
2604			if (blocksize == 0)	/* download complete */
2605				break;
2606			if (blocksize > 0x00000c00) {
2607				error = EINVAL;
2608				goto bad;
2609			}
2610			if ((blocksize & 0x1) == 0) {
2611				/* block successfully downloaded, advance */
2612				fp += nbytes;
2613				ntries = 0;
2614			} else {
2615				if (++ntries > 2) {
2616					/*
2617					 * Guard against f/w telling us to
2618					 * retry infinitely.
2619					 */
2620					error = ELOOP;
2621					goto bad;
2622				}
2623				/* clear NAK bit/flag */
2624				blocksize &= ~0x1;
2625			}
2626			if (blocksize > ep - fp) {
2627				/* XXX this should not happen, what to do? */
2628				blocksize = ep - fp;
2629			}
2630			nbytes = blocksize;
2631			if (!mwlSendBlock2(mh, fp, nbytes)) {
2632				error = ETIMEDOUT;
2633				goto bad;
2634			}
2635		}
2636	} else {
2637		for (fp = fmdata, ep = fp + fmsize; fp < ep;) {
2638			nbytes = ep - fp;
2639			if (nbytes > FW_DOWNLOAD_BLOCK_SIZE)
2640				nbytes = FW_DOWNLOAD_BLOCK_SIZE;
2641			if (!mwlSendBlock(mh, FW_DOWNLOAD_BLOCK_SIZE, fp, nbytes)) {
2642				error = EIO;
2643				goto bad;
2644			}
2645			fp += nbytes;
2646		}
2647	}
2648	/* done with firmware... */
2649	if (fwboot != NULL)
2650		firmware_put(fwboot, FIRMWARE_UNLOAD);
2651	firmware_put(fw, FIRMWARE_UNLOAD);
2652	/*
2653	 * Wait for firmware to startup; we monitor the
2654	 * INT_CODE register waiting for a signature to
2655	 * written back indicating it's ready to go.
2656	 */
2657	mh->mh_cmdbuf[1] = 0;
2658	/*
2659	 * XXX WAR for mfg fw download
2660	 */
2661	if (OpMode != HostCmd_STA_MODE)
2662		mwlTriggerPciCmd(mh);
2663	for (i = 0; i < FW_MAX_NUM_CHECKS; i++) {
2664		WR4(mh, MACREG_REG_GEN_PTR, OpMode);
2665		DELAY(FW_CHECK_USECS);
2666		if (RD4(mh, MACREG_REG_INT_CODE) == FwReadySignature) {
2667			WR4(mh, MACREG_REG_INT_CODE, 0x00);
2668			return mwlResetHalState(mh);
2669		}
2670	}
2671	return ETIMEDOUT;
2672bad:
2673	mwlFwReset(mh);
2674bad2:
2675	/* done with firmware... */
2676	if (fwboot != NULL)
2677		firmware_put(fwboot, FIRMWARE_UNLOAD);
2678	firmware_put(fw, FIRMWARE_UNLOAD);
2679	return error;
2680}
2681
2682#ifdef MWLHAL_DEBUG
2683static const char *
2684mwlcmdname(int cmd)
2685{
2686	static char buf[12];
2687#define	CMD(x)	case HostCmd_CMD_##x: return #x
2688	switch (cmd) {
2689	CMD(CODE_DNLD);
2690	CMD(GET_HW_SPEC);
2691	CMD(SET_HW_SPEC);
2692	CMD(MAC_MULTICAST_ADR);
2693	CMD(802_11_GET_STAT);
2694	CMD(MAC_REG_ACCESS);
2695	CMD(BBP_REG_ACCESS);
2696	CMD(RF_REG_ACCESS);
2697	CMD(802_11_RADIO_CONTROL);
2698	CMD(802_11_RF_TX_POWER);
2699	CMD(802_11_RF_ANTENNA);
2700	CMD(SET_BEACON);
2701	CMD(SET_RF_CHANNEL);
2702	CMD(SET_AID);
2703	CMD(SET_INFRA_MODE);
2704	CMD(SET_G_PROTECT_FLAG);
2705	CMD(802_11_RTS_THSD);
2706	CMD(802_11_SET_SLOT);
2707	CMD(SET_EDCA_PARAMS);
2708	CMD(802_11H_DETECT_RADAR);
2709	CMD(SET_WMM_MODE);
2710	CMD(HT_GUARD_INTERVAL);
2711	CMD(SET_FIXED_RATE);
2712	CMD(SET_LINKADAPT_CS_MODE);
2713	CMD(SET_MAC_ADDR);
2714	CMD(SET_RATE_ADAPT_MODE);
2715	CMD(BSS_START);
2716	CMD(SET_NEW_STN);
2717	CMD(SET_KEEP_ALIVE);
2718	CMD(SET_APMODE);
2719	CMD(SET_SWITCH_CHANNEL);
2720	CMD(UPDATE_ENCRYPTION);
2721	CMD(BASTREAM);
2722	CMD(SET_RIFS);
2723	CMD(SET_N_PROTECT_FLAG);
2724	CMD(SET_N_PROTECT_OPMODE);
2725	CMD(SET_OPTIMIZATION_LEVEL);
2726	CMD(GET_CALTABLE);
2727	CMD(SET_MIMOPSHT);
2728	CMD(GET_BEACON);
2729	CMD(SET_REGION_CODE);
2730	CMD(SET_POWERSAVESTATION);
2731	CMD(SET_TIM);
2732	CMD(GET_TIM);
2733	CMD(GET_SEQNO);
2734	CMD(DWDS_ENABLE);
2735	CMD(AMPDU_RETRY_RATEDROP_MODE);
2736	CMD(CFEND_ENABLE);
2737	}
2738	snprintf(buf, sizeof(buf), "0x%x", cmd);
2739	return buf;
2740#undef CMD
2741}
2742
2743static void
2744dumpresult(struct mwl_hal_priv *mh, int showresult)
2745{
2746	const FWCmdHdr *h = (const FWCmdHdr *)mh->mh_cmdbuf;
2747	const uint8_t *cp;
2748	int len, i;
2749
2750	len = le16toh(h->Length);
2751#ifdef MWL_MBSS_SUPPORT
2752	device_printf(mh->mh_dev, "Cmd %s Length %d SeqNum %d MacId %d",
2753	    mwlcmdname(le16toh(h->Cmd) &~ 0x8000), len, h->SeqNum, h->MacId);
2754#else
2755	device_printf(mh->mh_dev, "Cmd %s Length %d SeqNum %d",
2756	    mwlcmdname(le16toh(h->Cmd) &~ 0x8000), len, le16toh(h->SeqNum));
2757#endif
2758	if (showresult) {
2759		const char *results[] =
2760		    { "OK", "ERROR", "NOT_SUPPORT", "PENDING", "BUSY",
2761		      "PARTIAL_DATA" };
2762		int result = le16toh(h->Result);
2763
2764		if (result <= HostCmd_RESULT_PARTIAL_DATA)
2765			printf(" Result %s", results[result]);
2766		else
2767			printf(" Result %d", result);
2768	}
2769	cp = (const uint8_t *)h;
2770	for (i = 0; i < len; i++) {
2771		if ((i % 16) == 0)
2772			printf("\n%02x", cp[i]);
2773		else
2774			printf(" %02x", cp[i]);
2775	}
2776	printf("\n");
2777}
2778#endif /* MWLHAL_DEBUG */
2779