xref: /illumos-gate/usr/src/uts/common/io/rtw/rtw.c (revision 0dc2366f7b9f9f36e10909b1e95edbf2a261c2ac)
1a72f7ea6Sql /*
2*0dc2366fSVenugopal Iyer  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
3a72f7ea6Sql  * Use is subject to license terms.
4a72f7ea6Sql  */
5a72f7ea6Sql 
6a72f7ea6Sql /*
7a72f7ea6Sql  * Copyright (c) 2004 David Young.  All rights reserved.
8a72f7ea6Sql  *
9a72f7ea6Sql  * This code was written by David Young.
10a72f7ea6Sql  *
11a72f7ea6Sql  * Redistribution and use in source and binary forms, with or without
12a72f7ea6Sql  * modification, are permitted provided that the following conditions
13a72f7ea6Sql  * are met:
14a72f7ea6Sql  * 1. Redistributions of source code must retain the above copyright
15a72f7ea6Sql  *    notice, this list of conditions and the following disclaimer.
16a72f7ea6Sql  * 2. Redistributions in binary form must reproduce the above copyright
17a72f7ea6Sql  *    notice, this list of conditions and the following disclaimer in the
18a72f7ea6Sql  *    documentation and/or other materials provided with the distribution.
19a72f7ea6Sql  * 3. Neither the name of the author nor the names of any co-contributors
20a72f7ea6Sql  *    may be used to endorse or promote products derived from this software
21a72f7ea6Sql  *    without specific prior written permission.
22a72f7ea6Sql  *
23a72f7ea6Sql  * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY
24a72f7ea6Sql  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
25a72f7ea6Sql  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
26a72f7ea6Sql  * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL David
27a72f7ea6Sql  * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28a72f7ea6Sql  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29a72f7ea6Sql  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30a72f7ea6Sql  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31a72f7ea6Sql  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32a72f7ea6Sql  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33a72f7ea6Sql  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
34a72f7ea6Sql  * OF SUCH DAMAGE.
35a72f7ea6Sql  */
369aa73b68SQin Michael Li #include <sys/sysmacros.h>
37a72f7ea6Sql #include <sys/pci.h>
389aa73b68SQin Michael Li #include <sys/stat.h>
39a72f7ea6Sql #include <sys/strsubr.h>
409aa73b68SQin Michael Li #include <sys/strsun.h>
419aa73b68SQin Michael Li #include <sys/mac_provider.h>
42a72f7ea6Sql #include <sys/mac_wifi.h>
439aa73b68SQin Michael Li #include <sys/net80211.h>
44*0dc2366fSVenugopal Iyer #include <sys/byteorder.h>
45a72f7ea6Sql #include "rtwreg.h"
46a72f7ea6Sql #include "rtwvar.h"
47a72f7ea6Sql #include "smc93cx6var.h"
48a72f7ea6Sql #include "rtwphy.h"
49a72f7ea6Sql #include "rtwphyio.h"
50a72f7ea6Sql 
51a72f7ea6Sql /*
52a72f7ea6Sql  * PIO access attributes for registers
53a72f7ea6Sql  */
54a72f7ea6Sql static ddi_device_acc_attr_t rtw_reg_accattr = {
55a72f7ea6Sql 	DDI_DEVICE_ATTR_V0,
56a72f7ea6Sql 	DDI_STRUCTURE_LE_ACC,
57a72f7ea6Sql 	DDI_STRICTORDER_ACC,
58a72f7ea6Sql 	DDI_DEFAULT_ACC
59a72f7ea6Sql };
60a72f7ea6Sql 
61a72f7ea6Sql /*
62a72f7ea6Sql  * DMA access attributes for descriptors and bufs: NOT to be byte swapped.
63a72f7ea6Sql  */
64a72f7ea6Sql static ddi_device_acc_attr_t rtw_desc_accattr = {
65a72f7ea6Sql 	DDI_DEVICE_ATTR_V0,
66a72f7ea6Sql 	DDI_NEVERSWAP_ACC,
67a72f7ea6Sql 	DDI_STRICTORDER_ACC,
68a72f7ea6Sql 	DDI_DEFAULT_ACC
69a72f7ea6Sql };
70a72f7ea6Sql static ddi_device_acc_attr_t rtw_buf_accattr = {
71a72f7ea6Sql 	DDI_DEVICE_ATTR_V0,
72a72f7ea6Sql 	DDI_NEVERSWAP_ACC,
73a72f7ea6Sql 	DDI_STRICTORDER_ACC,
74a72f7ea6Sql 	DDI_DEFAULT_ACC
75a72f7ea6Sql };
76a72f7ea6Sql 
77a72f7ea6Sql /*
78a72f7ea6Sql  * Describes the chip's DMA engine
79a72f7ea6Sql  */
80a72f7ea6Sql static ddi_dma_attr_t dma_attr_desc = {
81a72f7ea6Sql 	DMA_ATTR_V0,			/* dma_attr version */
82a72f7ea6Sql 	0x0000000000000000ull,		/* dma_attr_addr_lo */
83020c4770Sql 	0xFFFFFFFF,			/* dma_attr_addr_hi */
84a72f7ea6Sql 	0x00000000FFFFFFFFull,		/* dma_attr_count_max */
85a72f7ea6Sql 	0x100,				/* dma_attr_align */
86a72f7ea6Sql 	0xFFFFFFFF,			/* dma_attr_burstsizes */
87a72f7ea6Sql 	0x00000001,			/* dma_attr_minxfer */
88a72f7ea6Sql 	0x00000000FFFFull,		/* dma_attr_maxxfer */
89a72f7ea6Sql 	0xFFFFFFFFFFFFFFFFull,		/* dma_attr_seg */
90a72f7ea6Sql 	1,				/* dma_attr_sgllen */
91a72f7ea6Sql 	1,				/* dma_attr_granular */
92a72f7ea6Sql 	0				/* dma_attr_flags */
93a72f7ea6Sql };
94a72f7ea6Sql 
95a72f7ea6Sql static ddi_dma_attr_t dma_attr_rxbuf = {
96a72f7ea6Sql 	DMA_ATTR_V0,			/* dma_attr version */
97a72f7ea6Sql 	0x0000000000000000ull,		/* dma_attr_addr_lo */
98020c4770Sql 	0xFFFFFFFF,			/* dma_attr_addr_hi */
99a72f7ea6Sql 	0x00000000FFFFFFFFull,		/* dma_attr_count_max */
100a72f7ea6Sql 	(uint32_t)16,			/* dma_attr_align */
101a72f7ea6Sql 	0xFFFFFFFF,			/* dma_attr_burstsizes */
102a72f7ea6Sql 	0x00000001,			/* dma_attr_minxfer */
103a72f7ea6Sql 	0x00000000FFFFull,		/* dma_attr_maxxfer */
104a72f7ea6Sql 	0xFFFFFFFFFFFFFFFFull,		/* dma_attr_seg */
105a72f7ea6Sql 	1,				/* dma_attr_sgllen */
106a72f7ea6Sql 	1,				/* dma_attr_granular */
107a72f7ea6Sql 	0				/* dma_attr_flags */
108a72f7ea6Sql };
109a72f7ea6Sql 
110a72f7ea6Sql static ddi_dma_attr_t dma_attr_txbuf = {
111a72f7ea6Sql 	DMA_ATTR_V0,			/* dma_attr version */
112a72f7ea6Sql 	0x0000000000000000ull,		/* dma_attr_addr_lo */
113020c4770Sql 	0xFFFFFFFF,			/* dma_attr_addr_hi */
114a72f7ea6Sql 	0x00000000FFFFFFFFull,		/* dma_attr_count_max */
115a72f7ea6Sql 	(uint32_t)16,			/* dma_attr_align */
116a72f7ea6Sql 	0xFFFFFFFF,			/* dma_attr_burstsizes */
117a72f7ea6Sql 	0x00000001,			/* dma_attr_minxfer */
118a72f7ea6Sql 	0x00000000FFFFull,		/* dma_attr_maxxfer */
119a72f7ea6Sql 	0xFFFFFFFFFFFFFFFFull,		/* dma_attr_seg */
120a72f7ea6Sql 	1,				/* dma_attr_sgllen */
121a72f7ea6Sql 	1,				/* dma_attr_granular */
122a72f7ea6Sql 	0				/* dma_attr_flags */
123a72f7ea6Sql };
124a72f7ea6Sql 
125a72f7ea6Sql 
126a72f7ea6Sql static void *rtw_soft_state_p = NULL;
127a72f7ea6Sql 
1289aa73b68SQin Michael Li static void	rtw_stop(void *);
1299aa73b68SQin Michael Li static int	rtw_attach(dev_info_t *, ddi_attach_cmd_t);
1309aa73b68SQin Michael Li static int	rtw_detach(dev_info_t *, ddi_detach_cmd_t);
1319aa73b68SQin Michael Li static int	rtw_quiesce(dev_info_t *);
132a72f7ea6Sql static int	rtw_m_stat(void *,  uint_t, uint64_t *);
133a72f7ea6Sql static int	rtw_m_start(void *);
134a72f7ea6Sql static void	rtw_m_stop(void *);
135a72f7ea6Sql static int	rtw_m_promisc(void *, boolean_t);
136a72f7ea6Sql static int	rtw_m_multicst(void *, boolean_t, const uint8_t *);
137a72f7ea6Sql static int	rtw_m_unicst(void *, const uint8_t *);
138a72f7ea6Sql static mblk_t	*rtw_m_tx(void *, mblk_t *);
139a72f7ea6Sql static void	rtw_m_ioctl(void *, queue_t *, mblk_t *);
14094d05f6cSQin Michael Li static int	rtw_m_setprop(void *, const char *, mac_prop_id_t,
14194d05f6cSQin Michael Li     uint_t, const void *);
14294d05f6cSQin Michael Li static int	rtw_m_getprop(void *, const char *, mac_prop_id_t,
143*0dc2366fSVenugopal Iyer     uint_t, void *);
144*0dc2366fSVenugopal Iyer static void	rtw_m_propinfo(void *, const char *, mac_prop_id_t,
145*0dc2366fSVenugopal Iyer     mac_prop_info_handle_t);
14694d05f6cSQin Michael Li 
147a72f7ea6Sql static mac_callbacks_t rtw_m_callbacks = {
148*0dc2366fSVenugopal Iyer 	MC_IOCTL | MC_SETPROP | MC_GETPROP | MC_PROPINFO,
149a72f7ea6Sql 	rtw_m_stat,
150a72f7ea6Sql 	rtw_m_start,
151a72f7ea6Sql 	rtw_m_stop,
152a72f7ea6Sql 	rtw_m_promisc,
153a72f7ea6Sql 	rtw_m_multicst,
154a72f7ea6Sql 	rtw_m_unicst,
155a72f7ea6Sql 	rtw_m_tx,
156*0dc2366fSVenugopal Iyer 	NULL,
15794d05f6cSQin Michael Li 	rtw_m_ioctl,
15894d05f6cSQin Michael Li 	NULL,		/* mc_getcapab */
15994d05f6cSQin Michael Li 	NULL,
16094d05f6cSQin Michael Li 	NULL,
16194d05f6cSQin Michael Li 	rtw_m_setprop,
162*0dc2366fSVenugopal Iyer 	rtw_m_getprop,
163*0dc2366fSVenugopal Iyer 	rtw_m_propinfo
164a72f7ea6Sql };
165a72f7ea6Sql 
166a72f7ea6Sql DDI_DEFINE_STREAM_OPS(rtw_dev_ops, nulldev, nulldev, rtw_attach, rtw_detach,
16794d05f6cSQin Michael Li     nodev, NULL, D_MP, NULL, rtw_quiesce);
168a72f7ea6Sql 
169a72f7ea6Sql static struct modldrv rtw_modldrv = {
170a72f7ea6Sql 	&mod_driverops,		/* Type of module.  This one is a driver */
1719aa73b68SQin Michael Li 	"realtek 8180L driver 1.7",	/* short description */
172a72f7ea6Sql 	&rtw_dev_ops		/* driver specific ops */
173a72f7ea6Sql };
174a72f7ea6Sql 
175a72f7ea6Sql static struct modlinkage modlinkage = {
176a72f7ea6Sql 	MODREV_1, (void *)&rtw_modldrv, NULL
177a72f7ea6Sql };
178a72f7ea6Sql 
179a72f7ea6Sql static uint32_t rtw_qlen[RTW_NTXPRI] = {
180a72f7ea6Sql 	RTW_TXQLENLO,
181a72f7ea6Sql 	RTW_TXQLENMD,
182a72f7ea6Sql 	RTW_TXQLENHI,
183a72f7ea6Sql 	RTW_TXQLENBCN
184a72f7ea6Sql };
185a72f7ea6Sql 
186020c4770Sql uint32_t rtw_dbg_flags = 0;
187a72f7ea6Sql 	/*
188a72f7ea6Sql 	 * RTW_DEBUG_ATTACH | RTW_DEBUG_TUNE |
189a72f7ea6Sql 	 * RTW_DEBUG_ACCESS | RTW_DEBUG_INIT | RTW_DEBUG_PKTFILT |
190a72f7ea6Sql 	 * RTW_DEBUG_RECV | RTW_DEBUG_XMIT | RTW_DEBUG_80211 | RTW_DEBUG_INTR |
191a72f7ea6Sql 	 * RTW_DEBUG_PKTDUMP;
192a72f7ea6Sql 	 */
193a72f7ea6Sql 
19494d05f6cSQin Michael Li /*
19594d05f6cSQin Michael Li  * Supported rates for 802.11b modes (in 500Kbps unit).
19694d05f6cSQin Michael Li  */
19794d05f6cSQin Michael Li static const struct ieee80211_rateset rtw_rateset_11b =
19894d05f6cSQin Michael Li 	{ 4, { 2, 4, 11, 22 } };
19994d05f6cSQin Michael Li 
200a72f7ea6Sql int
201a72f7ea6Sql _info(struct modinfo *modinfop)
202a72f7ea6Sql {
203a72f7ea6Sql 	return (mod_info(&modlinkage, modinfop));
204a72f7ea6Sql }
205a72f7ea6Sql 
206a72f7ea6Sql int
207a72f7ea6Sql _init(void)
208a72f7ea6Sql {
209a72f7ea6Sql 	int status;
210a72f7ea6Sql 
211a72f7ea6Sql 	status = ddi_soft_state_init(&rtw_soft_state_p,
212a72f7ea6Sql 	    sizeof (rtw_softc_t), 1);
213a72f7ea6Sql 	if (status != 0)
214a72f7ea6Sql 		return (status);
215a72f7ea6Sql 
216a72f7ea6Sql 	mac_init_ops(&rtw_dev_ops, "rtw");
217a72f7ea6Sql 	status = mod_install(&modlinkage);
218a72f7ea6Sql 	if (status != 0) {
219a72f7ea6Sql 		mac_fini_ops(&rtw_dev_ops);
220a72f7ea6Sql 		ddi_soft_state_fini(&rtw_soft_state_p);
221a72f7ea6Sql 	}
222a72f7ea6Sql 	return (status);
223a72f7ea6Sql }
224a72f7ea6Sql 
225a72f7ea6Sql int
226a72f7ea6Sql _fini(void)
227a72f7ea6Sql {
228a72f7ea6Sql 	int status;
229a72f7ea6Sql 
230a72f7ea6Sql 	status = mod_remove(&modlinkage);
231a72f7ea6Sql 	if (status == 0) {
232a72f7ea6Sql 		mac_fini_ops(&rtw_dev_ops);
233a72f7ea6Sql 		ddi_soft_state_fini(&rtw_soft_state_p);
234a72f7ea6Sql 	}
235a72f7ea6Sql 	return (status);
236a72f7ea6Sql }
237a72f7ea6Sql 
238a72f7ea6Sql void
239a72f7ea6Sql rtw_dbg(uint32_t dbg_flags, const int8_t *fmt, ...)
240a72f7ea6Sql {
241a72f7ea6Sql 	va_list args;
242a72f7ea6Sql 
243a72f7ea6Sql 	if (dbg_flags & rtw_dbg_flags) {
244a72f7ea6Sql 		va_start(args, fmt);
245a72f7ea6Sql 		vcmn_err(CE_CONT, fmt, args);
246a72f7ea6Sql 		va_end(args);
247a72f7ea6Sql 	}
248a72f7ea6Sql }
249a72f7ea6Sql 
250a72f7ea6Sql #ifdef DEBUG
251a72f7ea6Sql static void
252a72f7ea6Sql rtw_print_regs(struct rtw_regs *regs, const char *dvname, const char *where)
253a72f7ea6Sql {
254a72f7ea6Sql #define	PRINTREG32(sc, reg)				\
255a72f7ea6Sql 	RTW_DPRINTF(RTW_DEBUG_REGDUMP,			\
256a72f7ea6Sql 	    "%s: reg[ " #reg " / %03x ] = %08x\n",	\
257a72f7ea6Sql 	    dvname, reg, RTW_READ(regs, reg))
258a72f7ea6Sql 
259a72f7ea6Sql #define	PRINTREG16(sc, reg)				\
260a72f7ea6Sql 	RTW_DPRINTF(RTW_DEBUG_REGDUMP,			\
261a72f7ea6Sql 	    "%s: reg[ " #reg " / %03x ] = %04x\n",	\
262a72f7ea6Sql 	    dvname, reg, RTW_READ16(regs, reg))
263a72f7ea6Sql 
264a72f7ea6Sql #define	PRINTREG8(sc, reg)				\
265a72f7ea6Sql 	RTW_DPRINTF(RTW_DEBUG_REGDUMP,			\
266a72f7ea6Sql 	    "%s: reg[ " #reg " / %03x ] = %02x\n",	\
267a72f7ea6Sql 	    dvname, reg, RTW_READ8(regs, reg))
268a72f7ea6Sql 
269a72f7ea6Sql 	RTW_DPRINTF(RTW_DEBUG_REGDUMP, "%s: %s\n", dvname, where);
270a72f7ea6Sql 
271a72f7ea6Sql 	PRINTREG32(regs, RTW_IDR0);
272a72f7ea6Sql 	PRINTREG32(regs, RTW_IDR1);
273a72f7ea6Sql 	PRINTREG32(regs, RTW_MAR0);
274a72f7ea6Sql 	PRINTREG32(regs, RTW_MAR1);
275a72f7ea6Sql 	PRINTREG32(regs, RTW_TSFTRL);
276a72f7ea6Sql 	PRINTREG32(regs, RTW_TSFTRH);
277a72f7ea6Sql 	PRINTREG32(regs, RTW_TLPDA);
278a72f7ea6Sql 	PRINTREG32(regs, RTW_TNPDA);
279a72f7ea6Sql 	PRINTREG32(regs, RTW_THPDA);
280a72f7ea6Sql 	PRINTREG32(regs, RTW_TCR);
281a72f7ea6Sql 	PRINTREG32(regs, RTW_RCR);
282a72f7ea6Sql 	PRINTREG32(regs, RTW_TINT);
283a72f7ea6Sql 	PRINTREG32(regs, RTW_TBDA);
284a72f7ea6Sql 	PRINTREG32(regs, RTW_ANAPARM);
285a72f7ea6Sql 	PRINTREG32(regs, RTW_BB);
286a72f7ea6Sql 	PRINTREG32(regs, RTW_PHYCFG);
287a72f7ea6Sql 	PRINTREG32(regs, RTW_WAKEUP0L);
288a72f7ea6Sql 	PRINTREG32(regs, RTW_WAKEUP0H);
289a72f7ea6Sql 	PRINTREG32(regs, RTW_WAKEUP1L);
290a72f7ea6Sql 	PRINTREG32(regs, RTW_WAKEUP1H);
291a72f7ea6Sql 	PRINTREG32(regs, RTW_WAKEUP2LL);
292a72f7ea6Sql 	PRINTREG32(regs, RTW_WAKEUP2LH);
293a72f7ea6Sql 	PRINTREG32(regs, RTW_WAKEUP2HL);
294a72f7ea6Sql 	PRINTREG32(regs, RTW_WAKEUP2HH);
295a72f7ea6Sql 	PRINTREG32(regs, RTW_WAKEUP3LL);
296a72f7ea6Sql 	PRINTREG32(regs, RTW_WAKEUP3LH);
297a72f7ea6Sql 	PRINTREG32(regs, RTW_WAKEUP3HL);
298a72f7ea6Sql 	PRINTREG32(regs, RTW_WAKEUP3HH);
299a72f7ea6Sql 	PRINTREG32(regs, RTW_WAKEUP4LL);
300a72f7ea6Sql 	PRINTREG32(regs, RTW_WAKEUP4LH);
301a72f7ea6Sql 	PRINTREG32(regs, RTW_WAKEUP4HL);
302a72f7ea6Sql 	PRINTREG32(regs, RTW_WAKEUP4HH);
303a72f7ea6Sql 	PRINTREG32(regs, RTW_DK0);
304a72f7ea6Sql 	PRINTREG32(regs, RTW_DK1);
305a72f7ea6Sql 	PRINTREG32(regs, RTW_DK2);
306a72f7ea6Sql 	PRINTREG32(regs, RTW_DK3);
307a72f7ea6Sql 	PRINTREG32(regs, RTW_RETRYCTR);
308a72f7ea6Sql 	PRINTREG32(regs, RTW_RDSAR);
309a72f7ea6Sql 	PRINTREG32(regs, RTW_FER);
310a72f7ea6Sql 	PRINTREG32(regs, RTW_FEMR);
311a72f7ea6Sql 	PRINTREG32(regs, RTW_FPSR);
312a72f7ea6Sql 	PRINTREG32(regs, RTW_FFER);
313a72f7ea6Sql 
314a72f7ea6Sql 	/* 16-bit registers */
315a72f7ea6Sql 	PRINTREG16(regs, RTW_BRSR);
316a72f7ea6Sql 	PRINTREG16(regs, RTW_IMR);
317a72f7ea6Sql 	PRINTREG16(regs, RTW_ISR);
318a72f7ea6Sql 	PRINTREG16(regs, RTW_BCNITV);
319a72f7ea6Sql 	PRINTREG16(regs, RTW_ATIMWND);
320a72f7ea6Sql 	PRINTREG16(regs, RTW_BINTRITV);
321a72f7ea6Sql 	PRINTREG16(regs, RTW_ATIMTRITV);
322a72f7ea6Sql 	PRINTREG16(regs, RTW_CRC16ERR);
323a72f7ea6Sql 	PRINTREG16(regs, RTW_CRC0);
324a72f7ea6Sql 	PRINTREG16(regs, RTW_CRC1);
325a72f7ea6Sql 	PRINTREG16(regs, RTW_CRC2);
326a72f7ea6Sql 	PRINTREG16(regs, RTW_CRC3);
327a72f7ea6Sql 	PRINTREG16(regs, RTW_CRC4);
328a72f7ea6Sql 	PRINTREG16(regs, RTW_CWR);
329a72f7ea6Sql 
330a72f7ea6Sql 	/* 8-bit registers */
331a72f7ea6Sql 	PRINTREG8(regs, RTW_CR);
332a72f7ea6Sql 	PRINTREG8(regs, RTW_9346CR);
333a72f7ea6Sql 	PRINTREG8(regs, RTW_CONFIG0);
334a72f7ea6Sql 	PRINTREG8(regs, RTW_CONFIG1);
335a72f7ea6Sql 	PRINTREG8(regs, RTW_CONFIG2);
336a72f7ea6Sql 	PRINTREG8(regs, RTW_MSR);
337a72f7ea6Sql 	PRINTREG8(regs, RTW_CONFIG3);
338a72f7ea6Sql 	PRINTREG8(regs, RTW_CONFIG4);
339a72f7ea6Sql 	PRINTREG8(regs, RTW_TESTR);
340a72f7ea6Sql 	PRINTREG8(regs, RTW_PSR);
341a72f7ea6Sql 	PRINTREG8(regs, RTW_SCR);
342a72f7ea6Sql 	PRINTREG8(regs, RTW_PHYDELAY);
343a72f7ea6Sql 	PRINTREG8(regs, RTW_CRCOUNT);
344a72f7ea6Sql 	PRINTREG8(regs, RTW_PHYADDR);
345a72f7ea6Sql 	PRINTREG8(regs, RTW_PHYDATAW);
346a72f7ea6Sql 	PRINTREG8(regs, RTW_PHYDATAR);
347a72f7ea6Sql 	PRINTREG8(regs, RTW_CONFIG5);
348a72f7ea6Sql 	PRINTREG8(regs, RTW_TPPOLL);
349a72f7ea6Sql 
350a72f7ea6Sql 	PRINTREG16(regs, RTW_BSSID16);
351a72f7ea6Sql 	PRINTREG32(regs, RTW_BSSID32);
352a72f7ea6Sql #undef PRINTREG32
353a72f7ea6Sql #undef PRINTREG16
354a72f7ea6Sql #undef PRINTREG8
355a72f7ea6Sql }
356a72f7ea6Sql 
357a72f7ea6Sql #endif /* DEBUG */
358a72f7ea6Sql static const char *
359a72f7ea6Sql rtw_access_string(enum rtw_access access)
360a72f7ea6Sql {
361a72f7ea6Sql 	switch (access) {
362a72f7ea6Sql 	case RTW_ACCESS_NONE:
363a72f7ea6Sql 		return ("none");
364a72f7ea6Sql 	case RTW_ACCESS_CONFIG:
365a72f7ea6Sql 		return ("config");
366a72f7ea6Sql 	case RTW_ACCESS_ANAPARM:
367a72f7ea6Sql 		return ("anaparm");
368a72f7ea6Sql 	default:
369a72f7ea6Sql 		return ("unknown");
370a72f7ea6Sql 	}
371a72f7ea6Sql }
372a72f7ea6Sql 
373a72f7ea6Sql /*
374a72f7ea6Sql  * Enable registers, switch register banks.
375a72f7ea6Sql  */
376a72f7ea6Sql void
377a72f7ea6Sql rtw_config0123_enable(struct rtw_regs *regs, int enable)
378a72f7ea6Sql {
379a72f7ea6Sql 	uint8_t ecr;
380a72f7ea6Sql 	ecr = RTW_READ8(regs, RTW_9346CR);
381a72f7ea6Sql 	ecr &= ~(RTW_9346CR_EEM_MASK | RTW_9346CR_EECS | RTW_9346CR_EESK);
382a72f7ea6Sql 	if (enable)
383a72f7ea6Sql 		ecr |= RTW_9346CR_EEM_CONFIG;
384a72f7ea6Sql 	else {
385a72f7ea6Sql 		RTW_WBW(regs, RTW_9346CR, MAX(RTW_CONFIG0, RTW_CONFIG3));
386a72f7ea6Sql 		ecr |= RTW_9346CR_EEM_NORMAL;
387a72f7ea6Sql 	}
388a72f7ea6Sql 	RTW_WRITE8(regs, RTW_9346CR, ecr);
389a72f7ea6Sql 	RTW_SYNC(regs, RTW_9346CR, RTW_9346CR);
390a72f7ea6Sql }
391a72f7ea6Sql 
392a72f7ea6Sql /*
393a72f7ea6Sql  * requires rtw_config0123_enable(, 1)
394a72f7ea6Sql  */
395a72f7ea6Sql void
396a72f7ea6Sql rtw_anaparm_enable(struct rtw_regs *regs, int enable)
397a72f7ea6Sql {
398a72f7ea6Sql 	uint8_t cfg3;
399a72f7ea6Sql 
400a72f7ea6Sql 	cfg3 = RTW_READ8(regs, RTW_CONFIG3);
401a72f7ea6Sql 	cfg3 |= RTW_CONFIG3_CLKRUNEN;
402a72f7ea6Sql 	if (enable)
403a72f7ea6Sql 		cfg3 |= RTW_CONFIG3_PARMEN;
404a72f7ea6Sql 	else
405a72f7ea6Sql 		cfg3 &= ~RTW_CONFIG3_PARMEN;
406a72f7ea6Sql 	RTW_WRITE8(regs, RTW_CONFIG3, cfg3);
407a72f7ea6Sql 	RTW_SYNC(regs, RTW_CONFIG3, RTW_CONFIG3);
408a72f7ea6Sql }
409a72f7ea6Sql 
410a72f7ea6Sql /*
411a72f7ea6Sql  * requires rtw_anaparm_enable(, 1)
412a72f7ea6Sql  */
413a72f7ea6Sql void
414a72f7ea6Sql rtw_txdac_enable(rtw_softc_t *rsc, int enable)
415a72f7ea6Sql {
416a72f7ea6Sql 	uint32_t anaparm;
417a72f7ea6Sql 	struct rtw_regs *regs = &rsc->sc_regs;
418a72f7ea6Sql 
419a72f7ea6Sql 	anaparm = RTW_READ(regs, RTW_ANAPARM);
420a72f7ea6Sql 	if (enable)
421a72f7ea6Sql 		anaparm &= ~RTW_ANAPARM_TXDACOFF;
422a72f7ea6Sql 	else
423a72f7ea6Sql 		anaparm |= RTW_ANAPARM_TXDACOFF;
424a72f7ea6Sql 	RTW_WRITE(regs, RTW_ANAPARM, anaparm);
425a72f7ea6Sql 	RTW_SYNC(regs, RTW_ANAPARM, RTW_ANAPARM);
426a72f7ea6Sql }
427a72f7ea6Sql 
428a72f7ea6Sql static void
429a72f7ea6Sql rtw_set_access1(struct rtw_regs *regs, enum rtw_access naccess)
430a72f7ea6Sql {
431a72f7ea6Sql 	ASSERT(naccess >= RTW_ACCESS_NONE && naccess <= RTW_ACCESS_ANAPARM);
432a72f7ea6Sql 	ASSERT(regs->r_access >= RTW_ACCESS_NONE &&
433a72f7ea6Sql 	    regs->r_access <= RTW_ACCESS_ANAPARM);
434a72f7ea6Sql 
435a72f7ea6Sql 	if (naccess == regs->r_access)
436a72f7ea6Sql 		return;
437a72f7ea6Sql 
438a72f7ea6Sql 	switch (naccess) {
439a72f7ea6Sql 	case RTW_ACCESS_NONE:
440a72f7ea6Sql 		switch (regs->r_access) {
441a72f7ea6Sql 		case RTW_ACCESS_ANAPARM:
442a72f7ea6Sql 			rtw_anaparm_enable(regs, 0);
443a72f7ea6Sql 			/*FALLTHROUGH*/
444a72f7ea6Sql 		case RTW_ACCESS_CONFIG:
445a72f7ea6Sql 			rtw_config0123_enable(regs, 0);
446a72f7ea6Sql 			/*FALLTHROUGH*/
447a72f7ea6Sql 		case RTW_ACCESS_NONE:
448a72f7ea6Sql 			break;
449a72f7ea6Sql 		}
450a72f7ea6Sql 		break;
451a72f7ea6Sql 	case RTW_ACCESS_CONFIG:
452a72f7ea6Sql 		switch (regs->r_access) {
453a72f7ea6Sql 		case RTW_ACCESS_NONE:
454a72f7ea6Sql 			rtw_config0123_enable(regs, 1);
455a72f7ea6Sql 			/*FALLTHROUGH*/
456a72f7ea6Sql 		case RTW_ACCESS_CONFIG:
457a72f7ea6Sql 			break;
458a72f7ea6Sql 		case RTW_ACCESS_ANAPARM:
459a72f7ea6Sql 			rtw_anaparm_enable(regs, 0);
460a72f7ea6Sql 			break;
461a72f7ea6Sql 		}
462a72f7ea6Sql 		break;
463a72f7ea6Sql 	case RTW_ACCESS_ANAPARM:
464a72f7ea6Sql 		switch (regs->r_access) {
465a72f7ea6Sql 		case RTW_ACCESS_NONE:
466a72f7ea6Sql 			rtw_config0123_enable(regs, 1);
467a72f7ea6Sql 			/*FALLTHROUGH*/
468a72f7ea6Sql 		case RTW_ACCESS_CONFIG:
469a72f7ea6Sql 			rtw_anaparm_enable(regs, 1);
470a72f7ea6Sql 			/*FALLTHROUGH*/
471a72f7ea6Sql 		case RTW_ACCESS_ANAPARM:
472a72f7ea6Sql 			break;
473a72f7ea6Sql 		}
474a72f7ea6Sql 		break;
475a72f7ea6Sql 	}
476a72f7ea6Sql }
477a72f7ea6Sql 
478a72f7ea6Sql void
479a72f7ea6Sql rtw_set_access(struct rtw_regs *regs, enum rtw_access access)
480a72f7ea6Sql {
481a72f7ea6Sql 	rtw_set_access1(regs, access);
482a72f7ea6Sql 	RTW_DPRINTF(RTW_DEBUG_ACCESS,
483a72f7ea6Sql 	    "%s: access %s -> %s\n", __func__,
484a72f7ea6Sql 	    rtw_access_string(regs->r_access),
485a72f7ea6Sql 	    rtw_access_string(access));
486a72f7ea6Sql 	regs->r_access = access;
487a72f7ea6Sql }
488a72f7ea6Sql 
489a72f7ea6Sql 
490a72f7ea6Sql void
491a72f7ea6Sql rtw_continuous_tx_enable(rtw_softc_t *rsc, int enable)
492a72f7ea6Sql {
493a72f7ea6Sql 	struct rtw_regs *regs = &rsc->sc_regs;
494a72f7ea6Sql 
495a72f7ea6Sql 	uint32_t tcr;
496a72f7ea6Sql 	tcr = RTW_READ(regs, RTW_TCR);
497a72f7ea6Sql 	tcr &= ~RTW_TCR_LBK_MASK;
498a72f7ea6Sql 	if (enable)
499a72f7ea6Sql 		tcr |= RTW_TCR_LBK_CONT;
500a72f7ea6Sql 	else
501a72f7ea6Sql 		tcr |= RTW_TCR_LBK_NORMAL;
502a72f7ea6Sql 	RTW_WRITE(regs, RTW_TCR, tcr);
503a72f7ea6Sql 	RTW_SYNC(regs, RTW_TCR, RTW_TCR);
504a72f7ea6Sql 	rtw_set_access(regs, RTW_ACCESS_ANAPARM);
505a72f7ea6Sql 	rtw_txdac_enable(rsc, !enable);
506a72f7ea6Sql 	rtw_set_access(regs, RTW_ACCESS_ANAPARM);
507a72f7ea6Sql 	rtw_set_access(regs, RTW_ACCESS_NONE);
508a72f7ea6Sql }
509a72f7ea6Sql 
510a72f7ea6Sql static int
511a72f7ea6Sql rtw_chip_reset1(struct rtw_regs *regs, const char *dvname)
512a72f7ea6Sql {
513a72f7ea6Sql 	uint8_t cr;
514a72f7ea6Sql 	int i;
515a72f7ea6Sql 
516a72f7ea6Sql 	RTW_WRITE8(regs, RTW_CR, RTW_CR_RST);
517a72f7ea6Sql 
518a72f7ea6Sql 	RTW_WBR(regs, RTW_CR, RTW_CR);
519a72f7ea6Sql 
520a72f7ea6Sql 	for (i = 0; i < 1000; i++) {
521a72f7ea6Sql 		cr = RTW_READ8(regs, RTW_CR);
522a72f7ea6Sql 		if ((cr & RTW_CR_RST) == 0) {
523a72f7ea6Sql 			RTW_DPRINTF(RTW_DEBUG_RESET,
524a72f7ea6Sql 			    "%s: reset in %dus\n", dvname, i);
525a72f7ea6Sql 			return (0);
526a72f7ea6Sql 		}
527a72f7ea6Sql 		RTW_RBR(regs, RTW_CR, RTW_CR);
528a72f7ea6Sql 		DELAY(10); /* 10us */
529a72f7ea6Sql 	}
530a72f7ea6Sql 
531a72f7ea6Sql 	cmn_err(CE_WARN, "%s: reset failed\n", dvname);
532a72f7ea6Sql 	return (ETIMEDOUT);
533a72f7ea6Sql }
534a72f7ea6Sql 
535a72f7ea6Sql static int
536a72f7ea6Sql rtw_chip_reset(struct rtw_regs *regs, const char *dvname)
537a72f7ea6Sql {
538a72f7ea6Sql 	RTW_WBW(regs, RTW_CR, RTW_TCR);
539a72f7ea6Sql 	return (rtw_chip_reset1(regs, dvname));
540a72f7ea6Sql }
541a72f7ea6Sql 
542a72f7ea6Sql static void
543a72f7ea6Sql rtw_disable_interrupts(struct rtw_regs *regs)
544a72f7ea6Sql {
545a72f7ea6Sql 	RTW_WRITE16(regs, RTW_IMR, 0);
546a72f7ea6Sql 	RTW_WRITE16(regs, RTW_ISR, 0xffff);
547a72f7ea6Sql 	(void) RTW_READ16(regs, RTW_IMR);
548a72f7ea6Sql }
549a72f7ea6Sql 
550a72f7ea6Sql static void
551a72f7ea6Sql rtw_enable_interrupts(rtw_softc_t *rsc)
552a72f7ea6Sql {
553a72f7ea6Sql 	struct rtw_regs *regs = &rsc->sc_regs;
554a72f7ea6Sql 
555a72f7ea6Sql 	rsc->sc_inten = RTW_INTR_RX | RTW_INTR_TX | RTW_INTR_IOERROR;
556a72f7ea6Sql 
557a72f7ea6Sql 	RTW_WRITE16(regs, RTW_IMR, rsc->sc_inten);
558a72f7ea6Sql 	RTW_WRITE16(regs, RTW_ISR, 0xffff);
559a72f7ea6Sql 
560a72f7ea6Sql 	/* XXX necessary? */
561a72f7ea6Sql 	if (rsc->sc_intr_ack != NULL)
562a72f7ea6Sql 		(*rsc->sc_intr_ack)(regs);
563a72f7ea6Sql }
564a72f7ea6Sql 
565a72f7ea6Sql static int
566a72f7ea6Sql rtw_recall_eeprom(struct rtw_regs *regs, const char *dvname)
567a72f7ea6Sql {
568a72f7ea6Sql 	int i;
569a72f7ea6Sql 	uint8_t ecr;
570a72f7ea6Sql 
571a72f7ea6Sql 	ecr = RTW_READ8(regs, RTW_9346CR);
572a72f7ea6Sql 	ecr = (ecr & ~RTW_9346CR_EEM_MASK) | RTW_9346CR_EEM_AUTOLOAD;
573a72f7ea6Sql 	RTW_WRITE8(regs, RTW_9346CR, ecr);
574a72f7ea6Sql 
575a72f7ea6Sql 	RTW_WBR(regs, RTW_9346CR, RTW_9346CR);
576a72f7ea6Sql 
577a72f7ea6Sql 	/* wait 25ms for completion */
578a72f7ea6Sql 	for (i = 0; i < 250; i++) {
579a72f7ea6Sql 		ecr = RTW_READ8(regs, RTW_9346CR);
580a72f7ea6Sql 		if ((ecr & RTW_9346CR_EEM_MASK) == RTW_9346CR_EEM_NORMAL) {
581a72f7ea6Sql 			RTW_DPRINTF(RTW_DEBUG_RESET,
582a72f7ea6Sql 			    "%s: recall EEPROM in %dus\n", dvname, i * 100);
583a72f7ea6Sql 			return (0);
584a72f7ea6Sql 		}
585a72f7ea6Sql 		RTW_RBR(regs, RTW_9346CR, RTW_9346CR);
586a72f7ea6Sql 		DELAY(100);
587a72f7ea6Sql 	}
588a72f7ea6Sql 	cmn_err(CE_WARN, "%s: recall EEPROM failed\n", dvname);
589a72f7ea6Sql 	return (ETIMEDOUT);
590a72f7ea6Sql }
591a72f7ea6Sql 
592a72f7ea6Sql static int
593a72f7ea6Sql rtw_reset(rtw_softc_t *rsc)
594a72f7ea6Sql {
595a72f7ea6Sql 	int rc;
596a72f7ea6Sql 
597a72f7ea6Sql 	rc = rtw_chip_reset(&rsc->sc_regs, "rtw");
598a72f7ea6Sql 	if (rc != 0)
599a72f7ea6Sql 		return (rc);
600a72f7ea6Sql 
601a72f7ea6Sql 	(void) rtw_recall_eeprom(&rsc->sc_regs, "rtw");
602a72f7ea6Sql 	return (0);
603a72f7ea6Sql }
604a72f7ea6Sql 
605a72f7ea6Sql void
606a72f7ea6Sql rtw_set_mode(struct rtw_regs *regs, int mode)
607a72f7ea6Sql {
608a72f7ea6Sql 	uint8_t command;
609a72f7ea6Sql 	command = RTW_READ8(regs, RTW_9346CR);
610a72f7ea6Sql 	command = command &~ RTW_EPROM_CMD_OPERATING_MODE_MASK;
611a72f7ea6Sql 	command = command | (mode<<RTW_EPROM_CMD_OPERATING_MODE_SHIFT);
612a72f7ea6Sql 	command = command &~ (1<<RTW_EPROM_CS_SHIFT);
613a72f7ea6Sql 	command = command &~ (1<<RTW_EPROM_CK_SHIFT);
614a72f7ea6Sql 	RTW_WRITE8(regs, RTW_9346CR, command);
615a72f7ea6Sql }
616a72f7ea6Sql 
617a72f7ea6Sql void
618a72f7ea6Sql rtw_dma_start(struct rtw_regs *regs, int priority)
619a72f7ea6Sql {
620a72f7ea6Sql 	uint8_t check = 0;
621a72f7ea6Sql 
622a72f7ea6Sql 	check = RTW_READ8(regs, RTW_TPPOLL);
623a72f7ea6Sql 	switch (priority) {
624a72f7ea6Sql 	case (0):
625a72f7ea6Sql 		RTW_WRITE8(regs, RTW_TPPOLL,
626a72f7ea6Sql 		    (1<< RTW_TX_DMA_POLLING_LOWPRIORITY_SHIFT) | check);
627a72f7ea6Sql 		break;
628a72f7ea6Sql 	case (1):
629a72f7ea6Sql 		RTW_WRITE8(regs, RTW_TPPOLL,
630a72f7ea6Sql 		    (1<< RTW_TX_DMA_POLLING_NORMPRIORITY_SHIFT) | check);
631a72f7ea6Sql 		break;
632a72f7ea6Sql 	case (2):
633a72f7ea6Sql 		RTW_WRITE8(regs, RTW_TPPOLL,
634a72f7ea6Sql 		    (1<< RTW_TX_DMA_POLLING_HIPRIORITY_SHIFT) | check);
635a72f7ea6Sql 		break;
636a72f7ea6Sql 	}
637a72f7ea6Sql 	(void) RTW_READ8(regs, RTW_TPPOLL);
638a72f7ea6Sql }
639a72f7ea6Sql 
640a72f7ea6Sql void
641a72f7ea6Sql rtw_beacon_tx_disable(struct rtw_regs *regs)
642a72f7ea6Sql {
643a72f7ea6Sql 	uint8_t mask = 0;
644a72f7ea6Sql 	mask |= (1 << RTW_TX_DMA_STOP_BEACON_SHIFT);
645a72f7ea6Sql 	rtw_set_mode(regs, RTW_EPROM_CMD_CONFIG);
646a72f7ea6Sql 	RTW_WRITE8(regs, RTW_TPPOLL, mask);
647a72f7ea6Sql 	rtw_set_mode(regs, RTW_EPROM_CMD_NORMAL);
648a72f7ea6Sql }
649a72f7ea6Sql 
650a72f7ea6Sql static void
651a72f7ea6Sql rtw_io_enable(rtw_softc_t *rsc, uint8_t flags, int enable);
652a72f7ea6Sql 
653a72f7ea6Sql void
654a72f7ea6Sql rtw_rtx_disable(rtw_softc_t *rsc)
655a72f7ea6Sql {
656a72f7ea6Sql 	struct rtw_regs *regs = &rsc->sc_regs;
657a72f7ea6Sql 
658a72f7ea6Sql 	rtw_io_enable(rsc, RTW_CR_RE|RTW_CR_TE, 0);
659a72f7ea6Sql 	(void) RTW_READ8(regs, RTW_CR);
660a72f7ea6Sql }
661a72f7ea6Sql 
662a72f7ea6Sql static void
663a72f7ea6Sql rtw_srom_free(struct rtw_srom *sr)
664a72f7ea6Sql {
665a72f7ea6Sql 	if (sr->sr_content == NULL)
666a72f7ea6Sql 		return;
667a72f7ea6Sql 	kmem_free(sr->sr_content, sr->sr_size);
668a72f7ea6Sql 	sr->sr_size = 0;
669a72f7ea6Sql 	sr->sr_content = NULL;
670a72f7ea6Sql }
671a72f7ea6Sql 
672a72f7ea6Sql /*ARGSUSED*/
673a72f7ea6Sql static void
674a72f7ea6Sql rtw_srom_defaults(struct rtw_srom *sr, uint32_t *flags, uint8_t *cs_threshold,
675a72f7ea6Sql     enum rtw_rfchipid *rfchipid, uint32_t *rcr)
676a72f7ea6Sql {
677a72f7ea6Sql 	*flags |= (RTW_F_DIGPHY|RTW_F_ANTDIV);
678a72f7ea6Sql 	*cs_threshold = RTW_SR_ENERGYDETTHR_DEFAULT;
679a72f7ea6Sql 	*rcr |= RTW_RCR_ENCS1;
680a72f7ea6Sql 	*rfchipid = RTW_RFCHIPID_PHILIPS;
681a72f7ea6Sql }
682a72f7ea6Sql 
683a72f7ea6Sql static int
684a72f7ea6Sql rtw_srom_parse(struct rtw_srom *sr, uint32_t *flags, uint8_t *cs_threshold,
685a72f7ea6Sql     enum rtw_rfchipid *rfchipid, uint32_t *rcr, enum rtw_locale *locale,
686a72f7ea6Sql     const char *dvname)
687a72f7ea6Sql {
688a72f7ea6Sql 	int i;
689a72f7ea6Sql 	const char *rfname, *paname;
690a72f7ea6Sql 	char scratch[sizeof ("unknown 0xXX")];
691a72f7ea6Sql 	uint16_t version;
692a72f7ea6Sql 	uint8_t mac[IEEE80211_ADDR_LEN];
693a72f7ea6Sql 
694a72f7ea6Sql 	*flags &= ~(RTW_F_DIGPHY|RTW_F_DFLANTB|RTW_F_ANTDIV);
695a72f7ea6Sql 	*rcr &= ~(RTW_RCR_ENCS1 | RTW_RCR_ENCS2);
696a72f7ea6Sql 
697a72f7ea6Sql 	version = RTW_SR_GET16(sr, RTW_SR_VERSION);
698a72f7ea6Sql 	RTW_DPRINTF(RTW_DEBUG_IOSTATE, "%s: SROM version %d.%d", dvname,
699a72f7ea6Sql 	    version >> 8, version & 0xff);
700a72f7ea6Sql 
701a72f7ea6Sql 	if (version <= 0x0101) {
702a72f7ea6Sql 		cmn_err(CE_NOTE, " is not understood, limping along "
703a72f7ea6Sql 		    "with defaults\n");
704a72f7ea6Sql 		rtw_srom_defaults(sr, flags, cs_threshold, rfchipid, rcr);
705a72f7ea6Sql 		return (0);
706a72f7ea6Sql 	}
707a72f7ea6Sql 
708a72f7ea6Sql 	for (i = 0; i < IEEE80211_ADDR_LEN; i++)
709a72f7ea6Sql 		mac[i] = RTW_SR_GET(sr, RTW_SR_MAC + i);
710a72f7ea6Sql 
711a72f7ea6Sql 	RTW_DPRINTF(RTW_DEBUG_ATTACH,
712a72f7ea6Sql 	    "%s: EEPROM MAC %s\n", dvname, mac);
713a72f7ea6Sql 
714a72f7ea6Sql 	*cs_threshold = RTW_SR_GET(sr, RTW_SR_ENERGYDETTHR);
715a72f7ea6Sql 
716a72f7ea6Sql 	if ((RTW_SR_GET(sr, RTW_SR_CONFIG2) & RTW_CONFIG2_ANT) != 0)
717a72f7ea6Sql 		*flags |= RTW_F_ANTDIV;
718a72f7ea6Sql 
719a72f7ea6Sql 	/*
720a72f7ea6Sql 	 * Note well: the sense of the RTW_SR_RFPARM_DIGPHY bit seems
721a72f7ea6Sql 	 * to be reversed.
722a72f7ea6Sql 	 */
723a72f7ea6Sql 	if ((RTW_SR_GET(sr, RTW_SR_RFPARM) & RTW_SR_RFPARM_DIGPHY) == 0)
724a72f7ea6Sql 		*flags |= RTW_F_DIGPHY;
725a72f7ea6Sql 	if ((RTW_SR_GET(sr, RTW_SR_RFPARM) & RTW_SR_RFPARM_DFLANTB) != 0)
726a72f7ea6Sql 		*flags |= RTW_F_DFLANTB;
727a72f7ea6Sql 
728a72f7ea6Sql 	*rcr |= LSHIFT(MASK_AND_RSHIFT(RTW_SR_GET(sr, RTW_SR_RFPARM),
729a72f7ea6Sql 	    RTW_SR_RFPARM_CS_MASK), RTW_RCR_ENCS1);
730a72f7ea6Sql 
731a72f7ea6Sql 	*rfchipid = RTW_SR_GET(sr, RTW_SR_RFCHIPID);
732a72f7ea6Sql 	switch (*rfchipid) {
733a72f7ea6Sql 	case RTW_RFCHIPID_GCT:		/* this combo seen in the wild */
734a72f7ea6Sql 		rfname = "GCT GRF5101";
735a72f7ea6Sql 		paname = "Winspring WS9901";
736a72f7ea6Sql 		break;
737a72f7ea6Sql 	case RTW_RFCHIPID_MAXIM:
738a72f7ea6Sql 		rfname = "Maxim MAX2820";	/* guess */
739a72f7ea6Sql 		paname = "Maxim MAX2422";	/* guess */
740a72f7ea6Sql 		break;
741a72f7ea6Sql 	case RTW_RFCHIPID_INTERSIL:
742a72f7ea6Sql 		rfname = "Intersil HFA3873";	/* guess */
743a72f7ea6Sql 		paname = "Intersil <unknown>";
744a72f7ea6Sql 		break;
745a72f7ea6Sql 	case RTW_RFCHIPID_PHILIPS:	/* this combo seen in the wild */
746a72f7ea6Sql 		rfname = "Philips SA2400A";
747a72f7ea6Sql 		paname = "Philips SA2411";
748a72f7ea6Sql 		break;
749a72f7ea6Sql 	case RTW_RFCHIPID_RFMD:
750a72f7ea6Sql 		/*
751a72f7ea6Sql 		 * this is the same front-end as an atw(4)!
752a72f7ea6Sql 		 */
753a72f7ea6Sql 		rfname = "RFMD RF2948B, "	/* mentioned in Realtek docs */
754a72f7ea6Sql 		    "LNA: RFMD RF2494, "	/* mentioned in Realtek docs */
755a72f7ea6Sql 		    "SYN: Silicon Labs Si4126";
756a72f7ea6Sql 		paname = "RFMD RF2189";		/* mentioned in Realtek docs */
757a72f7ea6Sql 		break;
758a72f7ea6Sql 	case RTW_RFCHIPID_RESERVED:
759a72f7ea6Sql 		rfname = paname = "reserved";
760a72f7ea6Sql 		break;
761a72f7ea6Sql 	default:
762a72f7ea6Sql 		(void) snprintf(scratch, sizeof (scratch),
763a72f7ea6Sql 		    "unknown 0x%02x", *rfchipid);
764a72f7ea6Sql 		rfname = paname = scratch;
765a72f7ea6Sql 	}
766a72f7ea6Sql 	RTW_DPRINTF(RTW_DEBUG_PHY, "%s: RF: %s, PA: %s\n",
767a72f7ea6Sql 	    dvname, rfname, paname);
768a72f7ea6Sql 
769a72f7ea6Sql 	switch (RTW_SR_GET(sr, RTW_SR_CONFIG0) & RTW_CONFIG0_GL_MASK) {
770a72f7ea6Sql 	case RTW_CONFIG0_GL_USA:
771a72f7ea6Sql 		*locale = RTW_LOCALE_USA;
772a72f7ea6Sql 		break;
773a72f7ea6Sql 	case RTW_CONFIG0_GL_EUROPE:
774a72f7ea6Sql 		*locale = RTW_LOCALE_EUROPE;
775a72f7ea6Sql 		break;
776a72f7ea6Sql 	case RTW_CONFIG0_GL_JAPAN:
777a72f7ea6Sql 		*locale = RTW_LOCALE_JAPAN;
778a72f7ea6Sql 		break;
779a72f7ea6Sql 	default:
780a72f7ea6Sql 		*locale = RTW_LOCALE_UNKNOWN;
781a72f7ea6Sql 		break;
782a72f7ea6Sql 	}
783a72f7ea6Sql 	return (0);
784a72f7ea6Sql }
785a72f7ea6Sql 
786a72f7ea6Sql /*
787a72f7ea6Sql  * Returns -1 on failure.
788a72f7ea6Sql  */
789a72f7ea6Sql static int
790a72f7ea6Sql rtw_srom_read(struct rtw_regs *regs, uint32_t flags, struct rtw_srom *sr,
791a72f7ea6Sql     const char *dvname)
792a72f7ea6Sql {
793a72f7ea6Sql 	int rc;
794a72f7ea6Sql 	struct seeprom_descriptor sd;
795a72f7ea6Sql 	uint8_t ecr;
796a72f7ea6Sql 
797a72f7ea6Sql 	(void) memset(&sd, 0, sizeof (sd));
798a72f7ea6Sql 
799a72f7ea6Sql 	ecr = RTW_READ8(regs, RTW_9346CR);
800a72f7ea6Sql 
801a72f7ea6Sql 	if ((flags & RTW_F_9356SROM) != 0) {
802a72f7ea6Sql 		RTW_DPRINTF(RTW_DEBUG_ATTACH, "%s: 93c56 SROM\n", dvname);
803a72f7ea6Sql 		sr->sr_size = 256;
804a72f7ea6Sql 		sd.sd_chip = C56_66;
805a72f7ea6Sql 	} else {
806a72f7ea6Sql 		RTW_DPRINTF(RTW_DEBUG_ATTACH, "%s: 93c46 SROM\n", dvname);
807a72f7ea6Sql 		sr->sr_size = 128;
808a72f7ea6Sql 		sd.sd_chip = C46;
809a72f7ea6Sql 	}
810a72f7ea6Sql 
811a72f7ea6Sql 	ecr &= ~(RTW_9346CR_EEDI | RTW_9346CR_EEDO | RTW_9346CR_EESK |
812a72f7ea6Sql 	    RTW_9346CR_EEM_MASK | RTW_9346CR_EECS);
813a72f7ea6Sql 	ecr |= RTW_9346CR_EEM_PROGRAM;
814a72f7ea6Sql 
815a72f7ea6Sql 	RTW_WRITE8(regs, RTW_9346CR, ecr);
816a72f7ea6Sql 
817a72f7ea6Sql 	sr->sr_content = kmem_zalloc(sr->sr_size, KM_SLEEP);
818a72f7ea6Sql 
819a72f7ea6Sql 	if (sr->sr_content == NULL) {
820a72f7ea6Sql 		cmn_err(CE_WARN, "%s: unable to allocate SROM buffer\n",
821a72f7ea6Sql 		    dvname);
822a72f7ea6Sql 		return (ENOMEM);
823a72f7ea6Sql 	}
824a72f7ea6Sql 
825a72f7ea6Sql 	(void) memset(sr->sr_content, 0, sr->sr_size);
826a72f7ea6Sql 
827a72f7ea6Sql 	/*
828a72f7ea6Sql 	 * RTL8180 has a single 8-bit register for controlling the
829a72f7ea6Sql 	 * 93cx6 SROM.  There is no "ready" bit. The RTL8180
830a72f7ea6Sql 	 * input/output sense is the reverse of read_seeprom's.
831a72f7ea6Sql 	 */
832a72f7ea6Sql 	sd.sd_handle = regs->r_handle;
833a72f7ea6Sql 	sd.sd_base = regs->r_base;
834a72f7ea6Sql 	sd.sd_regsize = 1;
835a72f7ea6Sql 	sd.sd_control_offset = RTW_9346CR;
836a72f7ea6Sql 	sd.sd_status_offset = RTW_9346CR;
837a72f7ea6Sql 	sd.sd_dataout_offset = RTW_9346CR;
838a72f7ea6Sql 	sd.sd_CK = RTW_9346CR_EESK;
839a72f7ea6Sql 	sd.sd_CS = RTW_9346CR_EECS;
840a72f7ea6Sql 	sd.sd_DI = RTW_9346CR_EEDO;
841a72f7ea6Sql 	sd.sd_DO = RTW_9346CR_EEDI;
842a72f7ea6Sql 	/*
843a72f7ea6Sql 	 * make read_seeprom enter EEPROM read/write mode
844a72f7ea6Sql 	 */
845a72f7ea6Sql 	sd.sd_MS = ecr;
846a72f7ea6Sql 	sd.sd_RDY = 0;
847a72f7ea6Sql 
848a72f7ea6Sql 	/*
849a72f7ea6Sql 	 * TBD bus barriers
850a72f7ea6Sql 	 */
851a72f7ea6Sql 	if (!read_seeprom(&sd, sr->sr_content, 0, sr->sr_size/2)) {
852a72f7ea6Sql 		cmn_err(CE_WARN, "%s: could not read SROM\n", dvname);
853a72f7ea6Sql 		kmem_free(sr->sr_content, sr->sr_size);
854a72f7ea6Sql 		sr->sr_content = NULL;
855a72f7ea6Sql 		return (-1);	/* XXX */
856a72f7ea6Sql 	}
857a72f7ea6Sql 
858a72f7ea6Sql 	/*
859a72f7ea6Sql 	 * end EEPROM read/write mode
860a72f7ea6Sql 	 */
861a72f7ea6Sql 	RTW_WRITE8(regs, RTW_9346CR,
862a72f7ea6Sql 	    (ecr & ~RTW_9346CR_EEM_MASK) | RTW_9346CR_EEM_NORMAL);
863a72f7ea6Sql 	RTW_WBRW(regs, RTW_9346CR, RTW_9346CR);
864a72f7ea6Sql 
865a72f7ea6Sql 	if ((rc = rtw_recall_eeprom(regs, dvname)) != 0)
866a72f7ea6Sql 		return (rc);
867a72f7ea6Sql 
868a72f7ea6Sql #ifdef SROM_DEBUG
869a72f7ea6Sql 	{
870a72f7ea6Sql 		int i;
871a72f7ea6Sql 		RTW_DPRINTF(RTW_DEBUG_ATTACH,
872a72f7ea6Sql 		    "\n%s: serial ROM:\n\t", dvname);
873a72f7ea6Sql 		for (i = 0; i < sr->sr_size/2; i++) {
874a72f7ea6Sql 			RTW_DPRINTF(RTW_DEBUG_ATTACH,
875a72f7ea6Sql 			    "offset-0x%x: %04x", 2*i, sr->sr_content[i]);
876a72f7ea6Sql 		}
877a72f7ea6Sql 	}
878a72f7ea6Sql #endif /* DEBUG */
879a72f7ea6Sql 	return (0);
880a72f7ea6Sql }
881a72f7ea6Sql 
882a72f7ea6Sql static void
883a72f7ea6Sql rtw_set_rfprog(struct rtw_regs *regs, enum rtw_rfchipid rfchipid,
884a72f7ea6Sql     const char *dvname)
885a72f7ea6Sql {
886a72f7ea6Sql 	uint8_t cfg4;
887a72f7ea6Sql 	const char *method;
888a72f7ea6Sql 
889a72f7ea6Sql 	cfg4 = RTW_READ8(regs, RTW_CONFIG4) & ~RTW_CONFIG4_RFTYPE_MASK;
890a72f7ea6Sql 
891a72f7ea6Sql 	switch (rfchipid) {
892a72f7ea6Sql 	default:
893a72f7ea6Sql 		cfg4 |= LSHIFT(0, RTW_CONFIG4_RFTYPE_MASK);
894a72f7ea6Sql 		method = "fallback";
895a72f7ea6Sql 		break;
896a72f7ea6Sql 	case RTW_RFCHIPID_INTERSIL:
897a72f7ea6Sql 		cfg4 |= RTW_CONFIG4_RFTYPE_INTERSIL;
898a72f7ea6Sql 		method = "Intersil";
899a72f7ea6Sql 		break;
900a72f7ea6Sql 	case RTW_RFCHIPID_PHILIPS:
901a72f7ea6Sql 		cfg4 |= RTW_CONFIG4_RFTYPE_PHILIPS;
902a72f7ea6Sql 		method = "Philips";
903a72f7ea6Sql 		break;
904a72f7ea6Sql 	case RTW_RFCHIPID_GCT:	/* XXX a guess */
905a72f7ea6Sql 	case RTW_RFCHIPID_RFMD:
906a72f7ea6Sql 		cfg4 |= RTW_CONFIG4_RFTYPE_RFMD;
907a72f7ea6Sql 		method = "RFMD";
908a72f7ea6Sql 		break;
909a72f7ea6Sql 	}
910a72f7ea6Sql 
911a72f7ea6Sql 	RTW_WRITE8(regs, RTW_CONFIG4, cfg4);
912a72f7ea6Sql 
913a72f7ea6Sql 	RTW_WBR(regs, RTW_CONFIG4, RTW_CONFIG4);
914a72f7ea6Sql 
915a72f7ea6Sql 	RTW_DPRINTF(RTW_DEBUG_INIT,
916a72f7ea6Sql 	    "%s: %s RF programming method, %02x\n", dvname, method,
917a72f7ea6Sql 	    RTW_READ8(regs, RTW_CONFIG4));
918a72f7ea6Sql }
919a72f7ea6Sql 
920a72f7ea6Sql static void
921a72f7ea6Sql rtw_init_channels(enum rtw_locale locale,
922a72f7ea6Sql     struct ieee80211_channel (*chans)[IEEE80211_CHAN_MAX+1],
923a72f7ea6Sql     const char *dvname)
924a72f7ea6Sql {
925a72f7ea6Sql 	int i;
926a72f7ea6Sql 	const char *name = NULL;
927a72f7ea6Sql #define	ADD_CHANNEL(_chans, _chan) {			\
928a72f7ea6Sql 	(*_chans)[_chan].ich_flags = IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_CCK;\
929a72f7ea6Sql 	(*_chans)[_chan].ich_freq =				\
930a72f7ea6Sql 	    ieee80211_ieee2mhz(_chan, (*_chans)[_chan].ich_flags);\
931a72f7ea6Sql }
932a72f7ea6Sql 
933a72f7ea6Sql 	switch (locale) {
934a72f7ea6Sql 	case RTW_LOCALE_USA:	/* 1-11 */
935a72f7ea6Sql 		name = "USA";
936a72f7ea6Sql 		for (i = 1; i <= 11; i++)
937a72f7ea6Sql 			ADD_CHANNEL(chans, i);
938a72f7ea6Sql 		break;
939a72f7ea6Sql 	case RTW_LOCALE_JAPAN:	/* 1-14 */
940a72f7ea6Sql 		name = "Japan";
941a72f7ea6Sql 		ADD_CHANNEL(chans, 14);
942a72f7ea6Sql 		for (i = 1; i <= 14; i++)
943a72f7ea6Sql 			ADD_CHANNEL(chans, i);
944a72f7ea6Sql 		break;
945a72f7ea6Sql 	case RTW_LOCALE_EUROPE:	/* 1-13 */
946a72f7ea6Sql 		name = "Europe";
947a72f7ea6Sql 		for (i = 1; i <= 13; i++)
948a72f7ea6Sql 			ADD_CHANNEL(chans, i);
949a72f7ea6Sql 		break;
950a72f7ea6Sql 	default:			/* 10-11 allowed by most countries */
951a72f7ea6Sql 		name = "<unknown>";
952a72f7ea6Sql 		for (i = 10; i <= 11; i++)
953a72f7ea6Sql 			ADD_CHANNEL(chans, i);
954a72f7ea6Sql 		break;
955a72f7ea6Sql 	}
956a72f7ea6Sql 	RTW_DPRINTF(RTW_DEBUG_ATTACH, "%s: Geographic Location %s\n",
957a72f7ea6Sql 	    dvname, name);
958a72f7ea6Sql #undef ADD_CHANNEL
959a72f7ea6Sql }
960a72f7ea6Sql 
961a72f7ea6Sql static void
962a72f7ea6Sql rtw_set80211props(struct ieee80211com *ic)
963a72f7ea6Sql {
964a72f7ea6Sql 	ic->ic_phytype = IEEE80211_T_DS;
965a72f7ea6Sql 	ic->ic_opmode = IEEE80211_M_STA;
966a72f7ea6Sql 	ic->ic_caps = IEEE80211_C_PMGT | IEEE80211_C_IBSS |
96794d05f6cSQin Michael Li 	    IEEE80211_C_SHPREAMBLE;
96894d05f6cSQin Michael Li 	/* IEEE80211_C_HOSTAP | IEEE80211_C_MONITOR | IEEE80211_C_WEP */
969a72f7ea6Sql 
97094d05f6cSQin Michael Li 	ic->ic_sup_rates[IEEE80211_MODE_11B] = rtw_rateset_11b;
971a72f7ea6Sql }
972a72f7ea6Sql 
973a72f7ea6Sql /*ARGSUSED*/
974a72f7ea6Sql static void
975a72f7ea6Sql rtw_identify_country(struct rtw_regs *regs, enum rtw_locale *locale,
976a72f7ea6Sql     const char *dvname)
977a72f7ea6Sql {
978a72f7ea6Sql 	uint8_t cfg0 = RTW_READ8(regs, RTW_CONFIG0);
979a72f7ea6Sql 
980a72f7ea6Sql 	switch (cfg0 & RTW_CONFIG0_GL_MASK) {
981a72f7ea6Sql 	case RTW_CONFIG0_GL_USA:
982a72f7ea6Sql 		*locale = RTW_LOCALE_USA;
983a72f7ea6Sql 		break;
984a72f7ea6Sql 	case RTW_CONFIG0_GL_JAPAN:
985a72f7ea6Sql 		*locale = RTW_LOCALE_JAPAN;
986a72f7ea6Sql 		break;
987a72f7ea6Sql 	case RTW_CONFIG0_GL_EUROPE:
988a72f7ea6Sql 		*locale = RTW_LOCALE_EUROPE;
989a72f7ea6Sql 		break;
990a72f7ea6Sql 	default:
991a72f7ea6Sql 		*locale = RTW_LOCALE_UNKNOWN;
992a72f7ea6Sql 		break;
993a72f7ea6Sql 	}
994a72f7ea6Sql }
995a72f7ea6Sql 
996a72f7ea6Sql static int
997a72f7ea6Sql rtw_identify_sta(struct rtw_regs *regs, uint8_t *addr,
998a72f7ea6Sql     const char *dvname)
999a72f7ea6Sql {
1000a72f7ea6Sql 	uint32_t idr0 = RTW_READ(regs, RTW_IDR0),
1001a72f7ea6Sql 	    idr1 = RTW_READ(regs, RTW_IDR1);
1002a72f7ea6Sql 
1003a72f7ea6Sql 	*addr = MASK_AND_RSHIFT(idr0, BITS(0,  7));
1004a72f7ea6Sql 	*(addr + 1) = MASK_AND_RSHIFT(idr0, BITS(8,  15));
1005a72f7ea6Sql 	*(addr + 2) = MASK_AND_RSHIFT(idr0, BITS(16, 23));
1006a72f7ea6Sql 	*(addr + 3) = MASK_AND_RSHIFT(idr0, BITS(24, 31));
1007a72f7ea6Sql 
1008a72f7ea6Sql 	*(addr + 4) = MASK_AND_RSHIFT(idr1, BITS(0,  7));
1009a72f7ea6Sql 	*(addr + 5) = MASK_AND_RSHIFT(idr1, BITS(8, 15));
1010a72f7ea6Sql 
1011a72f7ea6Sql 	RTW_DPRINTF(RTW_DEBUG_ATTACH,
1012a72f7ea6Sql 	    "%s: 802.11mac address %x:%x:%x:%x:%x:%x\n", dvname,
1013a72f7ea6Sql 	    *addr, *(addr+1), *(addr+2), *(addr+3), *(addr+4), *(addr+5));
1014a72f7ea6Sql 
1015a72f7ea6Sql 	return (0);
1016a72f7ea6Sql }
1017a72f7ea6Sql 
1018a72f7ea6Sql static uint8_t
1019a72f7ea6Sql rtw_chan2txpower(struct rtw_srom *sr, struct ieee80211com *ic,
1020a72f7ea6Sql     struct ieee80211_channel *chan)
1021a72f7ea6Sql {
1022a72f7ea6Sql 	uint32_t idx = RTW_SR_TXPOWER1 + ieee80211_chan2ieee(ic, chan) - 1;
1023a72f7ea6Sql 	return (RTW_SR_GET(sr, idx));
1024a72f7ea6Sql }
1025a72f7ea6Sql 
1026a72f7ea6Sql static void
1027a72f7ea6Sql rtw_rxdesc_init(rtw_softc_t *rsc, struct rtw_rxbuf *rbf, int idx, int is_last)
1028a72f7ea6Sql {
1029a72f7ea6Sql 	uint32_t ctl = 0;
1030a72f7ea6Sql 	uint8_t *buf = (uint8_t *)rbf->bf_dma.mem_va;
1031a72f7ea6Sql 
1032a72f7ea6Sql 	ASSERT(rbf != NULL);
1033a72f7ea6Sql 	rbf->rxdesc->rd_buf = (rbf->bf_dma.cookie.dmac_address);
1034a72f7ea6Sql 	bzero(buf, rbf->bf_dma.alength);
1035a72f7ea6Sql 	RTW_DMA_SYNC(rbf->bf_dma, DDI_DMA_SYNC_FORDEV);
1036a72f7ea6Sql 
1037a72f7ea6Sql 	ctl = (rbf->bf_dma.alength & 0xfff) | RTW_RXCTL_OWN;
1038a72f7ea6Sql 
1039a72f7ea6Sql 	if (is_last)
1040a72f7ea6Sql 		ctl |= RTW_RXCTL_EOR;
1041a72f7ea6Sql 
1042a72f7ea6Sql 	rbf->rxdesc->rd_ctl = (ctl);
1043a72f7ea6Sql 	/* sync the mbuf */
1044a72f7ea6Sql 
1045a72f7ea6Sql 	/* sync the descriptor */
1046a72f7ea6Sql 	RTW_DMA_SYNC_DESC(rsc->sc_desc_dma,
1047a72f7ea6Sql 	    RTW_DESC_OFFSET(hd_rx, idx),
1048a72f7ea6Sql 	    sizeof (struct rtw_rxdesc),
1049a72f7ea6Sql 	    DDI_DMA_SYNC_FORDEV);
1050a72f7ea6Sql }
1051a72f7ea6Sql 
1052a72f7ea6Sql static void
1053a72f7ea6Sql rtw_idle(struct rtw_regs *regs)
1054a72f7ea6Sql {
1055a72f7ea6Sql 	int active;
1056a72f7ea6Sql 
1057a72f7ea6Sql 	/* request stop DMA; wait for packets to stop transmitting. */
1058a72f7ea6Sql 
1059a72f7ea6Sql 	RTW_WRITE8(regs, RTW_TPPOLL, RTW_TPPOLL_SALL);
1060a72f7ea6Sql 
1061a72f7ea6Sql 	for (active = 0; active < 300 &&
106294d05f6cSQin Michael Li 	    (RTW_READ8(regs, RTW_TPPOLL) & RTW_TPPOLL_ALL) != 0; active++)
1063a72f7ea6Sql 		drv_usecwait(10);
1064a72f7ea6Sql }
1065a72f7ea6Sql 
1066a72f7ea6Sql static void
1067a72f7ea6Sql rtw_io_enable(rtw_softc_t *rsc, uint8_t flags, int enable)
1068a72f7ea6Sql {
1069a72f7ea6Sql 	uint8_t cr;
1070a72f7ea6Sql 	struct rtw_regs *regs = &rsc->sc_regs;
1071a72f7ea6Sql 
1072a72f7ea6Sql 	RTW_DPRINTF(RTW_DEBUG_IOSTATE, "%s: %s 0x%02x\n", __func__,
1073a72f7ea6Sql 	    enable ? "enable" : "disable", flags);
1074a72f7ea6Sql 
1075a72f7ea6Sql 	cr = RTW_READ8(regs, RTW_CR);
107694d05f6cSQin Michael Li 
1077a72f7ea6Sql 	/* The receive engine will always start at RDSAR.  */
1078a72f7ea6Sql 	if (enable && (flags & ~cr & RTW_CR_RE)) {
1079a72f7ea6Sql 		RTW_DMA_SYNC_DESC(rsc->sc_desc_dma,
1080a72f7ea6Sql 		    RTW_DESC_OFFSET(hd_rx, 0),
1081a72f7ea6Sql 		    sizeof (struct rtw_rxdesc),
1082a72f7ea6Sql 		    DDI_DMA_SYNC_FORCPU);
1083a72f7ea6Sql 		rsc->rx_next = 0;
1084a72f7ea6Sql 		rtw_rxdesc_init(rsc, rsc->rxbuf_h, 0, 0);
1085a72f7ea6Sql 	}
108694d05f6cSQin Michael Li 
1087a72f7ea6Sql 	if (enable)
1088a72f7ea6Sql 		cr |= flags;
1089a72f7ea6Sql 	else
1090a72f7ea6Sql 		cr &= ~flags;
1091a72f7ea6Sql 	RTW_WRITE8(regs, RTW_CR, cr);
1092a72f7ea6Sql 	(void) RTW_READ8(regs, RTW_CR);
1093a72f7ea6Sql }
1094a72f7ea6Sql 
1095a72f7ea6Sql /*
1096a72f7ea6Sql  * Allocate an area of memory and a DMA handle for accessing it
1097a72f7ea6Sql  */
1098a72f7ea6Sql static int
1099a72f7ea6Sql rtw_alloc_dma_mem(dev_info_t *devinfo, ddi_dma_attr_t *dma_attr,
1100a72f7ea6Sql 	size_t memsize, ddi_device_acc_attr_t *attr_p, uint_t alloc_flags,
1101a72f7ea6Sql 	uint_t bind_flags, dma_area_t *dma_p)
1102a72f7ea6Sql {
1103a72f7ea6Sql 	int err;
1104a72f7ea6Sql 
1105a72f7ea6Sql 	/*
1106a72f7ea6Sql 	 * Allocate handle
1107a72f7ea6Sql 	 */
1108a72f7ea6Sql 	err = ddi_dma_alloc_handle(devinfo, dma_attr,
1109a72f7ea6Sql 	    DDI_DMA_SLEEP, NULL, &dma_p->dma_hdl);
1110a72f7ea6Sql 	if (err != DDI_SUCCESS)
1111a72f7ea6Sql 		return (DDI_FAILURE);
1112a72f7ea6Sql 
1113a72f7ea6Sql 	/*
1114a72f7ea6Sql 	 * Allocate memory
1115a72f7ea6Sql 	 */
1116a72f7ea6Sql 	err = ddi_dma_mem_alloc(dma_p->dma_hdl, memsize, attr_p,
1117a72f7ea6Sql 	    alloc_flags, DDI_DMA_SLEEP, NULL, &dma_p->mem_va,
1118a72f7ea6Sql 	    &dma_p->alength, &dma_p->acc_hdl);
1119a72f7ea6Sql 	if (err != DDI_SUCCESS)
1120a72f7ea6Sql 		return (DDI_FAILURE);
1121a72f7ea6Sql 
1122a72f7ea6Sql 	/*
1123a72f7ea6Sql 	 * Bind the two together
1124a72f7ea6Sql 	 */
1125a72f7ea6Sql 	err = ddi_dma_addr_bind_handle(dma_p->dma_hdl, NULL,
1126a72f7ea6Sql 	    dma_p->mem_va, dma_p->alength, bind_flags,
1127a72f7ea6Sql 	    DDI_DMA_SLEEP, NULL, &dma_p->cookie, &dma_p->ncookies);
1128a72f7ea6Sql 	if ((dma_p->ncookies != 1) || (err != DDI_DMA_MAPPED))
1129a72f7ea6Sql 		return (DDI_FAILURE);
1130a72f7ea6Sql 
1131a72f7ea6Sql 	dma_p->nslots = ~0U;
1132a72f7ea6Sql 	dma_p->size = ~0U;
1133a72f7ea6Sql 	dma_p->token = ~0U;
1134a72f7ea6Sql 	dma_p->offset = 0;
1135a72f7ea6Sql 	return (DDI_SUCCESS);
1136a72f7ea6Sql }
1137a72f7ea6Sql 
1138a72f7ea6Sql /*
1139a72f7ea6Sql  * Free one allocated area of DMAable memory
1140a72f7ea6Sql  */
1141a72f7ea6Sql static void
1142a72f7ea6Sql rtw_free_dma_mem(dma_area_t *dma_p)
1143a72f7ea6Sql {
1144a72f7ea6Sql 	if (dma_p->dma_hdl != NULL) {
1145a72f7ea6Sql 		(void) ddi_dma_unbind_handle(dma_p->dma_hdl);
1146a72f7ea6Sql 		if (dma_p->acc_hdl != NULL) {
1147a72f7ea6Sql 			ddi_dma_mem_free(&dma_p->acc_hdl);
1148a72f7ea6Sql 			dma_p->acc_hdl = NULL;
1149a72f7ea6Sql 		}
1150a72f7ea6Sql 		ddi_dma_free_handle(&dma_p->dma_hdl);
1151a72f7ea6Sql 		dma_p->ncookies = 0;
1152a72f7ea6Sql 		dma_p->dma_hdl = NULL;
1153a72f7ea6Sql 	}
1154a72f7ea6Sql }
1155a72f7ea6Sql 
1156a72f7ea6Sql static void
1157a72f7ea6Sql rtw_dma_free(rtw_softc_t *rsc)
1158a72f7ea6Sql {
1159a72f7ea6Sql 	struct rtw_txbuf *txbf;
1160a72f7ea6Sql 	struct rtw_rxbuf *rxbf;
1161a72f7ea6Sql 	int i, j;
1162a72f7ea6Sql 
1163a72f7ea6Sql 	/* Free TX DMA buffer */
1164a72f7ea6Sql 	for (i = 0; i < RTW_NTXPRI; i++) {
1165a72f7ea6Sql 		txbf = list_head(&rsc->sc_txq[i].tx_free_list);
1166a72f7ea6Sql 		while (txbf != NULL) {
1167a72f7ea6Sql 			rtw_free_dma_mem(&txbf->bf_dma);
1168a72f7ea6Sql 			list_remove(&rsc->sc_txq[i].tx_free_list, txbf);
1169a72f7ea6Sql 			txbf = list_head(&rsc->sc_txq[i].tx_free_list);
1170a72f7ea6Sql 		}
1171a72f7ea6Sql 		list_destroy(&rsc->sc_txq[i].tx_free_list);
1172a72f7ea6Sql 		txbf = list_head(&rsc->sc_txq[i].tx_dirty_list);
1173a72f7ea6Sql 		while (txbf != NULL) {
1174a72f7ea6Sql 			rtw_free_dma_mem(&txbf->bf_dma);
1175a72f7ea6Sql 			list_remove(&rsc->sc_txq[i].tx_dirty_list, txbf);
1176a72f7ea6Sql 			txbf = list_head(&rsc->sc_txq[i].tx_dirty_list);
1177a72f7ea6Sql 		}
1178a72f7ea6Sql 		list_destroy(&rsc->sc_txq[i].tx_dirty_list);
1179a72f7ea6Sql 
1180a72f7ea6Sql 		if (rsc->sc_txq[i].txbuf_h != NULL) {
1181a72f7ea6Sql 			kmem_free(rsc->sc_txq[i].txbuf_h,
1182a72f7ea6Sql 			    sizeof (struct rtw_txbuf) * rtw_qlen[i]);
1183a72f7ea6Sql 			rsc->sc_txq[i].txbuf_h = NULL;
1184a72f7ea6Sql 		}
1185a72f7ea6Sql 	}
1186a72f7ea6Sql 
1187a72f7ea6Sql 	/* Free RX DMA buffer */
1188a72f7ea6Sql 	rxbf = rsc->rxbuf_h;
1189a72f7ea6Sql 	for (j = 0; j < RTW_RXQLEN; j++) {
1190a72f7ea6Sql 		rtw_free_dma_mem(&rxbf->bf_dma);
1191a72f7ea6Sql 		rxbf++;
1192a72f7ea6Sql 	}
1193a72f7ea6Sql 
1194a72f7ea6Sql 	if (rsc->rxbuf_h != NULL) {
1195020c4770Sql 		kmem_free(rsc->rxbuf_h,
1196020c4770Sql 		    sizeof (struct rtw_rxbuf) * RTW_RXQLEN);
1197a72f7ea6Sql 		rsc->rxbuf_h = NULL;
1198a72f7ea6Sql 	}
1199a72f7ea6Sql 
1200a72f7ea6Sql 	rtw_free_dma_mem(&rsc->sc_desc_dma);
1201a72f7ea6Sql }
1202a72f7ea6Sql 
1203a72f7ea6Sql static int
1204a72f7ea6Sql rtw_dma_init(dev_info_t *devinfo, rtw_softc_t *rsc)
1205a72f7ea6Sql {
1206a72f7ea6Sql 	int i, j, err;
1207a72f7ea6Sql 	size_t size;
1208a72f7ea6Sql 	uint32_t buflen;
1209a72f7ea6Sql 	struct rtw_txdesc *txds;
1210a72f7ea6Sql 	struct rtw_rxdesc *rxds;
1211a72f7ea6Sql 	struct rtw_txbuf *txbf;
1212a72f7ea6Sql 	struct rtw_rxbuf *rxbf;
1213a72f7ea6Sql 	uint32_t phybaseaddr, ptx[RTW_NTXPRI], prx;
1214a72f7ea6Sql 	caddr_t virbaseaddr, vtx[RTW_NTXPRI], vrx;
1215a72f7ea6Sql 
1216a72f7ea6Sql 	/* DMA buffer size for each TX/RX packet */
1217a72f7ea6Sql 	rsc->sc_dmabuf_size = roundup(sizeof (struct ieee80211_frame) + 0x100 +
1218a72f7ea6Sql 	    IEEE80211_MTU + IEEE80211_CRC_LEN + sizeof (struct ieee80211_llc) +
1219a72f7ea6Sql 	    (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN +
1220a72f7ea6Sql 	    IEEE80211_WEP_CRCLEN), rsc->sc_cachelsz);
1221a72f7ea6Sql 	size = sizeof (struct rtw_descs);
1222a72f7ea6Sql 	err = rtw_alloc_dma_mem(devinfo, &dma_attr_desc, size,
1223a72f7ea6Sql 	    &rtw_desc_accattr,
1224a72f7ea6Sql 	    DDI_DMA_CONSISTENT, DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
1225a72f7ea6Sql 	    &rsc->sc_desc_dma);
1226a72f7ea6Sql 	if (err != DDI_SUCCESS)
1227a72f7ea6Sql 		goto error;
1228a72f7ea6Sql 	phybaseaddr = rsc->sc_desc_dma.cookie.dmac_address;
1229a72f7ea6Sql 	virbaseaddr = rsc->sc_desc_dma.mem_va;
1230a72f7ea6Sql 	ptx[0] = RTW_RING_BASE(phybaseaddr, hd_txlo);
1231a72f7ea6Sql 	ptx[1] = RTW_RING_BASE(phybaseaddr, hd_txmd);
1232a72f7ea6Sql 	ptx[2] = RTW_RING_BASE(phybaseaddr, hd_txhi);
1233a72f7ea6Sql 	ptx[3] = RTW_RING_BASE(phybaseaddr, hd_bcn);
1234a72f7ea6Sql 	vtx[0] = (caddr_t)(RTW_RING_BASE(virbaseaddr, hd_txlo));
1235a72f7ea6Sql 	vtx[1] = (caddr_t)(RTW_RING_BASE(virbaseaddr, hd_txmd));
1236a72f7ea6Sql 	vtx[2] = (caddr_t)(RTW_RING_BASE(virbaseaddr, hd_txhi));
1237a72f7ea6Sql 	vtx[3] = (caddr_t)(RTW_RING_BASE(virbaseaddr, hd_bcn));
1238a72f7ea6Sql 	for (i = 0; i < RTW_NTXPRI; i++) {
1239a72f7ea6Sql 		RTW_DPRINTF(RTW_DEBUG_DMA, "p[%d]=%x, v[%d]=%x", i, ptx[i],
1240a72f7ea6Sql 		    i, vtx[i]);
1241a72f7ea6Sql 		RTW_DPRINTF(RTW_DEBUG_DMA, "ring%d:", i);
1242a72f7ea6Sql 		list_create(&rsc->sc_txq[i].tx_free_list,
1243a72f7ea6Sql 		    sizeof (struct rtw_txbuf),
1244a72f7ea6Sql 		    offsetof(struct rtw_txbuf, bf_node));
1245a72f7ea6Sql 		list_create(&rsc->sc_txq[i].tx_dirty_list,
1246a72f7ea6Sql 		    sizeof (struct rtw_txbuf),
1247a72f7ea6Sql 		    offsetof(struct rtw_txbuf, bf_node));
1248a72f7ea6Sql 		/* virtual address of the first descriptor */
1249020c4770Sql 		rsc->sc_txq[i].txdesc_h =
1250020c4770Sql 		    (struct rtw_txdesc *)(uintptr_t)vtx[i];
1251a72f7ea6Sql 
1252a72f7ea6Sql 		txds = rsc->sc_txq[i].txdesc_h;
1253a72f7ea6Sql 		/* allocate data structures to describe TX DMA buffers */
1254a72f7ea6Sql 		buflen = sizeof (struct rtw_txbuf) * rtw_qlen[i];
1255a72f7ea6Sql 		txbf = (struct rtw_txbuf *)kmem_zalloc(buflen, KM_SLEEP);
1256a72f7ea6Sql 		rsc->sc_txq[i].txbuf_h = txbf;
1257a72f7ea6Sql 		for (j = 0; j < rtw_qlen[i]; j++, txbf++, txds++) {
1258a72f7ea6Sql 			txbf->txdesc = txds;
1259020c4770Sql 			txbf->bf_daddr = ptx[i] + ((uintptr_t)txds -
1260020c4770Sql 			    (uintptr_t)rsc->sc_txq[i].txdesc_h);
1261a72f7ea6Sql 			list_insert_tail(&rsc->sc_txq[i].tx_free_list, txbf);
1262a72f7ea6Sql 
1263a72f7ea6Sql 			/* alloc DMA memory */
1264a72f7ea6Sql 			err = rtw_alloc_dma_mem(devinfo, &dma_attr_txbuf,
1265a72f7ea6Sql 			    rsc->sc_dmabuf_size,
1266a72f7ea6Sql 			    &rtw_buf_accattr,
1267a72f7ea6Sql 			    DDI_DMA_STREAMING,
1268a72f7ea6Sql 			    DDI_DMA_WRITE | DDI_DMA_STREAMING,
1269a72f7ea6Sql 			    &txbf->bf_dma);
1270a72f7ea6Sql 			if (err != DDI_SUCCESS)
1271a72f7ea6Sql 				goto error;
1272a72f7ea6Sql 			RTW_DPRINTF(RTW_DEBUG_DMA, "pbufaddr[%d]=%x",
1273a72f7ea6Sql 			    j, txbf->bf_dma.cookie.dmac_address);
1274a72f7ea6Sql 		}
1275a72f7ea6Sql 	}
1276a72f7ea6Sql 	prx = RTW_RING_BASE(phybaseaddr, hd_rx);
1277a72f7ea6Sql 	vrx = (caddr_t)(RTW_RING_BASE(virbaseaddr, hd_rx));
1278a72f7ea6Sql 	/* virtual address of the first descriptor */
1279020c4770Sql 	rsc->rxdesc_h = (struct rtw_rxdesc *)(uintptr_t)vrx;
1280a72f7ea6Sql 	rxds = rsc->rxdesc_h;
1281a72f7ea6Sql 
1282a72f7ea6Sql 	/* allocate data structures to describe RX DMA buffers */
1283a72f7ea6Sql 	buflen = sizeof (struct rtw_rxbuf) * RTW_RXQLEN;
1284a72f7ea6Sql 	rxbf = (struct rtw_rxbuf *)kmem_zalloc(buflen, KM_SLEEP);
1285a72f7ea6Sql 	rsc->rxbuf_h = rxbf;
1286a72f7ea6Sql 
1287a72f7ea6Sql 	for (j = 0; j < RTW_RXQLEN; j++, rxbf++, rxds++) {
1288a72f7ea6Sql 		rxbf->rxdesc = rxds;
1289020c4770Sql 		rxbf->bf_daddr =
1290020c4770Sql 		    prx + ((uintptr_t)rxds - (uintptr_t)rsc->rxdesc_h);
1291a72f7ea6Sql 
1292a72f7ea6Sql 		/* alloc DMA memory */
1293a72f7ea6Sql 		err = rtw_alloc_dma_mem(devinfo, &dma_attr_rxbuf,
1294a72f7ea6Sql 		    rsc->sc_dmabuf_size,
1295a72f7ea6Sql 		    &rtw_buf_accattr,
1296a72f7ea6Sql 		    DDI_DMA_STREAMING, DDI_DMA_READ | DDI_DMA_STREAMING,
1297a72f7ea6Sql 		    &rxbf->bf_dma);
1298a72f7ea6Sql 		if (err != DDI_SUCCESS)
1299a72f7ea6Sql 			goto error;
1300a72f7ea6Sql 	}
1301a72f7ea6Sql 
1302a72f7ea6Sql 	return (DDI_SUCCESS);
1303a72f7ea6Sql error:
1304a72f7ea6Sql 	return (DDI_FAILURE);
1305a72f7ea6Sql }
1306a72f7ea6Sql 
1307a72f7ea6Sql static void
1308a72f7ea6Sql rtw_hwring_setup(rtw_softc_t *rsc)
1309a72f7ea6Sql {
1310a72f7ea6Sql 	struct rtw_regs *regs = &rsc->sc_regs;
1311a72f7ea6Sql 	uint32_t phybaseaddr;
1312a72f7ea6Sql 
1313a72f7ea6Sql 	phybaseaddr = rsc->sc_desc_dma.cookie.dmac_address;
1314a72f7ea6Sql 
1315a72f7ea6Sql 	RTW_WRITE(regs, RTW_RDSAR, RTW_RING_BASE(phybaseaddr, hd_rx));
1316a72f7ea6Sql 	RTW_WRITE(regs, RTW_TLPDA, RTW_RING_BASE(phybaseaddr, hd_txlo));
1317a72f7ea6Sql 	RTW_WRITE(regs, RTW_TNPDA, RTW_RING_BASE(phybaseaddr, hd_txmd));
1318a72f7ea6Sql 	RTW_WRITE(regs, RTW_THPDA, RTW_RING_BASE(phybaseaddr, hd_txhi));
1319a72f7ea6Sql 	RTW_WRITE(regs, RTW_TBDA, RTW_RING_BASE(phybaseaddr, hd_bcn));
1320a72f7ea6Sql 	rsc->hw_start = RTW_READ(regs, RTW_TNPDA);
1321a72f7ea6Sql 	rsc->hw_go = RTW_READ(regs, RTW_TNPDA);
1322a72f7ea6Sql }
1323a72f7ea6Sql 
1324a72f7ea6Sql static void
1325a72f7ea6Sql rtw_swring_setup(rtw_softc_t *rsc, int flag)
1326a72f7ea6Sql {
1327a72f7ea6Sql 	int i, j;
1328a72f7ea6Sql 	int is_last;
1329a72f7ea6Sql 	struct rtw_txbuf *txbf;
1330a72f7ea6Sql 	struct rtw_rxbuf *rxbf;
1331a72f7ea6Sql 	uint32_t phybaseaddr, ptx[RTW_NTXPRI], baddr_desc, taddr_desc;
1332a72f7ea6Sql 
1333a72f7ea6Sql 	phybaseaddr = rsc->sc_desc_dma.cookie.dmac_address;
1334a72f7ea6Sql 	ptx[0] = RTW_RING_BASE(phybaseaddr, hd_txlo);
1335a72f7ea6Sql 	ptx[1] = RTW_RING_BASE(phybaseaddr, hd_txmd);
1336a72f7ea6Sql 	ptx[2] = RTW_RING_BASE(phybaseaddr, hd_txhi);
1337a72f7ea6Sql 	ptx[3] = RTW_RING_BASE(phybaseaddr, hd_bcn);
1338a72f7ea6Sql 	RTW_DMA_SYNC(rsc->sc_desc_dma, DDI_DMA_SYNC_FORDEV);
1339a72f7ea6Sql 	/* sync tx desc and tx buf */
1340a72f7ea6Sql 	for (i = 0; i < RTW_NTXPRI; i++) {
1341a72f7ea6Sql 		rsc->sc_txq[i].tx_prod = rsc->sc_txq[i].tx_cons = 0;
1342a72f7ea6Sql 		rsc->sc_txq[i].tx_nfree = rtw_qlen[i];
1343a72f7ea6Sql 		txbf = list_head(&rsc->sc_txq[i].tx_free_list);
1344a72f7ea6Sql 		while (txbf != NULL) {
1345a72f7ea6Sql 			list_remove(&rsc->sc_txq[i].tx_free_list, txbf);
1346a72f7ea6Sql 			txbf = list_head(&rsc->sc_txq[i].tx_free_list);
1347a72f7ea6Sql 		}
1348a72f7ea6Sql 		txbf = list_head(&rsc->sc_txq[i].tx_dirty_list);
1349a72f7ea6Sql 		while (txbf != NULL) {
1350a72f7ea6Sql 			list_remove(&rsc->sc_txq[i].tx_dirty_list, txbf);
1351a72f7ea6Sql 			txbf = list_head(&rsc->sc_txq[i].tx_dirty_list);
1352a72f7ea6Sql 		}
1353a72f7ea6Sql 		txbf = rsc->sc_txq[i].txbuf_h;
1354a72f7ea6Sql 		baddr_desc = ptx[i];
1355a72f7ea6Sql 		taddr_desc = baddr_desc + sizeof (struct rtw_txdesc);
1356a72f7ea6Sql 		for (j = 0; j < rtw_qlen[i]; j++) {
1357a72f7ea6Sql 			list_insert_tail(&rsc->sc_txq[i].tx_free_list, txbf);
1358a72f7ea6Sql 			if (j == (rtw_qlen[i] - 1)) {
1359a72f7ea6Sql 				is_last = 1;
1360a72f7ea6Sql 			} else {
1361a72f7ea6Sql 				is_last = 0;
1362a72f7ea6Sql 			}
1363a72f7ea6Sql 
1364a72f7ea6Sql 			if (is_last) {
1365a72f7ea6Sql 				txbf->txdesc->td_next = baddr_desc;
1366a72f7ea6Sql 			} else {
1367a72f7ea6Sql 				txbf->txdesc->td_next = taddr_desc;
1368a72f7ea6Sql 			}
1369a72f7ea6Sql 			txbf->next_bf_daddr = txbf->txdesc->td_next;
1370a72f7ea6Sql 			RTW_DMA_SYNC(txbf->bf_dma, DDI_DMA_SYNC_FORDEV);
1371a72f7ea6Sql 			txbf->order = j;
1372a72f7ea6Sql 			txbf++;
1373a72f7ea6Sql 			taddr_desc += sizeof (struct rtw_txdesc);
1374a72f7ea6Sql 		}
1375a72f7ea6Sql 	}
1376a72f7ea6Sql 	if (!flag)
1377a72f7ea6Sql 		return;
1378a72f7ea6Sql 
1379a72f7ea6Sql 	/* sync rx desc and rx buf */
1380a72f7ea6Sql 	rsc->rx_next = 0;
1381a72f7ea6Sql 	rxbf = rsc->rxbuf_h;
1382a72f7ea6Sql 	for (j = 0; j < RTW_RXQLEN; j++) {
1383a72f7ea6Sql 		RTW_DMA_SYNC(rxbf->bf_dma, DDI_DMA_SYNC_FORCPU);
1384a72f7ea6Sql 		if (j == (RTW_RXQLEN - 1))
1385a72f7ea6Sql 			is_last = 1;
1386a72f7ea6Sql 		else
1387a72f7ea6Sql 			is_last = 0;
1388a72f7ea6Sql 		rtw_rxdesc_init(rsc, rxbf, j, is_last);
1389a72f7ea6Sql 		rxbf++;
1390a72f7ea6Sql 	}
1391a72f7ea6Sql }
1392a72f7ea6Sql 
1393a72f7ea6Sql static void
1394a72f7ea6Sql rtw_resume_ticks(rtw_softc_t *rsc)
1395a72f7ea6Sql {
1396a72f7ea6Sql 	RTW_WRITE(&rsc->sc_regs, RTW_TINT, 0xffffffff);
1397a72f7ea6Sql }
1398a72f7ea6Sql 
1399a72f7ea6Sql const char *
1400a72f7ea6Sql rtw_pwrstate_string(enum rtw_pwrstate power)
1401a72f7ea6Sql {
1402a72f7ea6Sql 	switch (power) {
1403a72f7ea6Sql 	case RTW_ON:
1404a72f7ea6Sql 		return ("on");
1405a72f7ea6Sql 	case RTW_SLEEP:
1406a72f7ea6Sql 		return ("sleep");
1407a72f7ea6Sql 	case RTW_OFF:
1408a72f7ea6Sql 		return ("off");
1409a72f7ea6Sql 	default:
1410a72f7ea6Sql 		return ("unknown");
1411a72f7ea6Sql 	}
1412a72f7ea6Sql }
1413a72f7ea6Sql 
1414a72f7ea6Sql /*
1415a72f7ea6Sql  * XXX For Maxim, I am using the RFMD settings gleaned from the
1416a72f7ea6Sql  * reference driver, plus a magic Maxim "ON" value that comes from
1417a72f7ea6Sql  * the Realtek document "Windows PG for Rtl8180."
1418a72f7ea6Sql  */
1419a72f7ea6Sql /*ARGSUSED*/
1420a72f7ea6Sql static void
1421a72f7ea6Sql rtw_maxim_pwrstate(struct rtw_regs *regs, enum rtw_pwrstate power,
1422a72f7ea6Sql     int before_rf, int digphy)
1423a72f7ea6Sql {
1424a72f7ea6Sql 	uint32_t anaparm;
1425a72f7ea6Sql 
1426a72f7ea6Sql 	anaparm = RTW_READ(regs, RTW_ANAPARM);
1427a72f7ea6Sql 	anaparm &= ~(RTW_ANAPARM_RFPOW_MASK | RTW_ANAPARM_TXDACOFF);
1428a72f7ea6Sql 
1429a72f7ea6Sql 	switch (power) {
1430a72f7ea6Sql 	case RTW_OFF:
1431a72f7ea6Sql 		if (before_rf)
1432a72f7ea6Sql 			return;
1433a72f7ea6Sql 		anaparm |= RTW_ANAPARM_RFPOW_MAXIM_OFF;
1434a72f7ea6Sql 		anaparm |= RTW_ANAPARM_TXDACOFF;
1435a72f7ea6Sql 		break;
1436a72f7ea6Sql 	case RTW_SLEEP:
1437a72f7ea6Sql 		if (!before_rf)
1438a72f7ea6Sql 			return;
1439a72f7ea6Sql 		anaparm |= RTW_ANAPARM_RFPOW_MAXIM_SLEEP;
1440a72f7ea6Sql 		anaparm |= RTW_ANAPARM_TXDACOFF;
1441a72f7ea6Sql 		break;
1442a72f7ea6Sql 	case RTW_ON:
1443a72f7ea6Sql 		if (!before_rf)
1444a72f7ea6Sql 			return;
1445a72f7ea6Sql 		anaparm |= RTW_ANAPARM_RFPOW_MAXIM_ON;
1446a72f7ea6Sql 		break;
1447a72f7ea6Sql 	}
1448a72f7ea6Sql 	RTW_DPRINTF(RTW_DEBUG_PWR,
1449a72f7ea6Sql 	    "%s: power state %s, %s RF, reg[ANAPARM] <- %08x\n",
1450a72f7ea6Sql 	    __func__, rtw_pwrstate_string(power),
1451a72f7ea6Sql 	    (before_rf) ? "before" : "after", anaparm);
1452a72f7ea6Sql 
1453a72f7ea6Sql 	RTW_WRITE(regs, RTW_ANAPARM, anaparm);
1454a72f7ea6Sql 	RTW_SYNC(regs, RTW_ANAPARM, RTW_ANAPARM);
1455a72f7ea6Sql }
1456a72f7ea6Sql 
1457a72f7ea6Sql /*
1458a72f7ea6Sql  * XXX I am using the RFMD settings gleaned from the reference
1459a72f7ea6Sql  * driver.  They agree
1460a72f7ea6Sql  */
1461a72f7ea6Sql /*ARGSUSED*/
1462a72f7ea6Sql static void
1463a72f7ea6Sql rtw_rfmd_pwrstate(struct rtw_regs *regs, enum rtw_pwrstate power,
1464a72f7ea6Sql     int before_rf, int digphy)
1465a72f7ea6Sql {
1466a72f7ea6Sql 	uint32_t anaparm;
1467a72f7ea6Sql 
1468a72f7ea6Sql 	anaparm = RTW_READ(regs, RTW_ANAPARM);
1469a72f7ea6Sql 	anaparm &= ~(RTW_ANAPARM_RFPOW_MASK | RTW_ANAPARM_TXDACOFF);
1470a72f7ea6Sql 
1471a72f7ea6Sql 	switch (power) {
1472a72f7ea6Sql 	case RTW_OFF:
1473a72f7ea6Sql 		if (before_rf)
1474a72f7ea6Sql 			return;
1475a72f7ea6Sql 		anaparm |= RTW_ANAPARM_RFPOW_RFMD_OFF;
1476a72f7ea6Sql 		anaparm |= RTW_ANAPARM_TXDACOFF;
1477a72f7ea6Sql 		break;
1478a72f7ea6Sql 	case RTW_SLEEP:
1479a72f7ea6Sql 		if (!before_rf)
1480a72f7ea6Sql 			return;
1481a72f7ea6Sql 		anaparm |= RTW_ANAPARM_RFPOW_RFMD_SLEEP;
1482a72f7ea6Sql 		anaparm |= RTW_ANAPARM_TXDACOFF;
1483a72f7ea6Sql 		break;
1484a72f7ea6Sql 	case RTW_ON:
1485a72f7ea6Sql 		if (!before_rf)
1486a72f7ea6Sql 			return;
1487a72f7ea6Sql 		anaparm |= RTW_ANAPARM_RFPOW_RFMD_ON;
1488a72f7ea6Sql 		break;
1489a72f7ea6Sql 	}
1490a72f7ea6Sql 	RTW_DPRINTF(RTW_DEBUG_PWR,
1491a72f7ea6Sql 	    "%s: power state %s, %s RF, reg[ANAPARM] <- %08x\n",
1492a72f7ea6Sql 	    __func__, rtw_pwrstate_string(power),
1493a72f7ea6Sql 	    (before_rf) ? "before" : "after", anaparm);
1494a72f7ea6Sql 
1495a72f7ea6Sql 	RTW_WRITE(regs, RTW_ANAPARM, anaparm);
1496a72f7ea6Sql 	RTW_SYNC(regs, RTW_ANAPARM, RTW_ANAPARM);
1497a72f7ea6Sql }
1498a72f7ea6Sql 
1499a72f7ea6Sql static void
1500a72f7ea6Sql rtw_philips_pwrstate(struct rtw_regs *regs, enum rtw_pwrstate power,
1501a72f7ea6Sql     int before_rf, int digphy)
1502a72f7ea6Sql {
1503a72f7ea6Sql 	uint32_t anaparm;
1504a72f7ea6Sql 
1505a72f7ea6Sql 	anaparm = RTW_READ(regs, RTW_ANAPARM);
1506a72f7ea6Sql 	anaparm &= ~(RTW_ANAPARM_RFPOW_MASK | RTW_ANAPARM_TXDACOFF);
1507a72f7ea6Sql 
1508a72f7ea6Sql 	switch (power) {
1509a72f7ea6Sql 	case RTW_OFF:
1510a72f7ea6Sql 		if (before_rf)
1511a72f7ea6Sql 			return;
1512a72f7ea6Sql 		anaparm |= RTW_ANAPARM_RFPOW_PHILIPS_OFF;
1513a72f7ea6Sql 		anaparm |= RTW_ANAPARM_TXDACOFF;
1514a72f7ea6Sql 		break;
1515a72f7ea6Sql 	case RTW_SLEEP:
1516a72f7ea6Sql 		if (!before_rf)
1517a72f7ea6Sql 			return;
1518a72f7ea6Sql 		anaparm |= RTW_ANAPARM_RFPOW_PHILIPS_SLEEP;
1519a72f7ea6Sql 		anaparm |= RTW_ANAPARM_TXDACOFF;
1520a72f7ea6Sql 		break;
1521a72f7ea6Sql 	case RTW_ON:
1522a72f7ea6Sql 		if (!before_rf)
1523a72f7ea6Sql 			return;
1524a72f7ea6Sql 		if (digphy) {
1525a72f7ea6Sql 			anaparm |= RTW_ANAPARM_RFPOW_DIG_PHILIPS_ON;
1526a72f7ea6Sql 			/* XXX guess */
1527a72f7ea6Sql 			anaparm |= RTW_ANAPARM_TXDACOFF;
1528a72f7ea6Sql 		} else
1529a72f7ea6Sql 			anaparm |= RTW_ANAPARM_RFPOW_ANA_PHILIPS_ON;
1530a72f7ea6Sql 		break;
1531a72f7ea6Sql 	}
1532a72f7ea6Sql 	RTW_DPRINTF(RTW_DEBUG_PWR,
1533a72f7ea6Sql 	    "%s: power state %s, %s RF, reg[ANAPARM] <- %08x\n",
1534a72f7ea6Sql 	    __func__, rtw_pwrstate_string(power),
1535a72f7ea6Sql 	    (before_rf) ? "before" : "after", anaparm);
1536a72f7ea6Sql 
1537a72f7ea6Sql 	RTW_WRITE(regs, RTW_ANAPARM, anaparm);
1538a72f7ea6Sql 	RTW_SYNC(regs, RTW_ANAPARM, RTW_ANAPARM);
1539a72f7ea6Sql }
1540a72f7ea6Sql 
1541a72f7ea6Sql static void
1542a72f7ea6Sql rtw_pwrstate0(rtw_softc_t *rsc, enum rtw_pwrstate power, int before_rf,
1543a72f7ea6Sql     int digphy)
1544a72f7ea6Sql {
1545a72f7ea6Sql 	struct rtw_regs *regs = &rsc->sc_regs;
1546a72f7ea6Sql 
1547a72f7ea6Sql 	rtw_set_access(regs, RTW_ACCESS_ANAPARM);
1548a72f7ea6Sql 
1549a72f7ea6Sql 	(*rsc->sc_pwrstate_cb)(regs, power, before_rf, digphy);
1550a72f7ea6Sql 
1551a72f7ea6Sql 	rtw_set_access(regs, RTW_ACCESS_NONE);
1552a72f7ea6Sql }
1553a72f7ea6Sql 
1554a72f7ea6Sql static void
1555a72f7ea6Sql rtw_rf_destroy(struct rtw_rf *rf)
1556a72f7ea6Sql {
1557a72f7ea6Sql 	(*rf->rf_destroy)(rf);
1558a72f7ea6Sql }
1559a72f7ea6Sql 
1560a72f7ea6Sql static int
1561a72f7ea6Sql rtw_rf_pwrstate(struct rtw_rf *rf, enum rtw_pwrstate power)
1562a72f7ea6Sql {
1563a72f7ea6Sql 	return (*rf->rf_pwrstate)(rf, power);
1564a72f7ea6Sql }
1565a72f7ea6Sql 
1566a72f7ea6Sql static int
1567a72f7ea6Sql rtw_pwrstate(rtw_softc_t *rsc, enum rtw_pwrstate power)
1568a72f7ea6Sql {
1569a72f7ea6Sql 	int rc;
1570a72f7ea6Sql 
1571a72f7ea6Sql 	RTW_DPRINTF(RTW_DEBUG_PWR,
1572a72f7ea6Sql 	    "%s: %s->%s\n", __func__,
1573a72f7ea6Sql 	    rtw_pwrstate_string(rsc->sc_pwrstate), rtw_pwrstate_string(power));
1574a72f7ea6Sql 
1575a72f7ea6Sql 	if (rsc->sc_pwrstate == power)
1576a72f7ea6Sql 		return (0);
1577a72f7ea6Sql 
1578a72f7ea6Sql 	rtw_pwrstate0(rsc, power, 1, rsc->sc_flags & RTW_F_DIGPHY);
1579a72f7ea6Sql 	rc = rtw_rf_pwrstate(rsc->sc_rf, power);
1580a72f7ea6Sql 	rtw_pwrstate0(rsc, power, 0, rsc->sc_flags & RTW_F_DIGPHY);
1581a72f7ea6Sql 
1582a72f7ea6Sql 	switch (power) {
1583a72f7ea6Sql 	case RTW_ON:
1584a72f7ea6Sql 		/* TBD set LEDs */
1585a72f7ea6Sql 		break;
1586a72f7ea6Sql 	case RTW_SLEEP:
1587a72f7ea6Sql 		/* TBD */
1588a72f7ea6Sql 		break;
1589a72f7ea6Sql 	case RTW_OFF:
1590a72f7ea6Sql 		/* TBD */
1591a72f7ea6Sql 		break;
1592a72f7ea6Sql 	}
1593a72f7ea6Sql 	if (rc == 0)
1594a72f7ea6Sql 		rsc->sc_pwrstate = power;
1595a72f7ea6Sql 	else
1596a72f7ea6Sql 		rsc->sc_pwrstate = RTW_OFF;
1597a72f7ea6Sql 	return (rc);
1598a72f7ea6Sql }
1599a72f7ea6Sql 
1600a72f7ea6Sql void
1601a72f7ea6Sql rtw_disable(rtw_softc_t *rsc)
1602a72f7ea6Sql {
1603a72f7ea6Sql 	int rc;
1604a72f7ea6Sql 
1605a72f7ea6Sql 	if ((rsc->sc_flags & RTW_F_ENABLED) == 0)
1606a72f7ea6Sql 		return;
1607a72f7ea6Sql 
1608a72f7ea6Sql 	/* turn off PHY */
1609a72f7ea6Sql 	if ((rsc->sc_flags & RTW_F_INVALID) == 0 &&
1610a72f7ea6Sql 	    (rc = rtw_pwrstate(rsc, RTW_OFF)) != 0) {
1611a72f7ea6Sql 		cmn_err(CE_WARN, "failed to turn off PHY (%d)\n", rc);
1612a72f7ea6Sql 	}
1613a72f7ea6Sql 
1614a72f7ea6Sql 	if (rsc->sc_disable != NULL)
1615a72f7ea6Sql 		(*rsc->sc_disable)(rsc);
1616a72f7ea6Sql 
1617a72f7ea6Sql 	rsc->sc_flags &= ~RTW_F_ENABLED;
1618a72f7ea6Sql }
1619a72f7ea6Sql 
1620a72f7ea6Sql int
1621a72f7ea6Sql rtw_enable(rtw_softc_t *rsc)
1622a72f7ea6Sql {
1623a72f7ea6Sql 	if ((rsc->sc_flags & RTW_F_ENABLED) == 0) {
1624a72f7ea6Sql 		if (rsc->sc_enable != NULL && (*rsc->sc_enable)(rsc) != 0) {
1625a72f7ea6Sql 			cmn_err(CE_WARN, "device enable failed\n");
1626a72f7ea6Sql 			return (EIO);
1627a72f7ea6Sql 		}
1628a72f7ea6Sql 		rsc->sc_flags |= RTW_F_ENABLED;
1629a72f7ea6Sql 		if (rtw_pwrstate(rsc, RTW_ON) != 0)
1630a72f7ea6Sql 			cmn_err(CE_WARN, "PHY turn on failed\n");
1631a72f7ea6Sql 	}
1632a72f7ea6Sql 	return (0);
1633a72f7ea6Sql }
1634a72f7ea6Sql 
1635a72f7ea6Sql static void
1636a72f7ea6Sql rtw_set_nettype(rtw_softc_t *rsc, enum ieee80211_opmode opmode)
1637a72f7ea6Sql {
1638a72f7ea6Sql 	uint8_t msr;
1639a72f7ea6Sql 
1640a72f7ea6Sql 	/* I'm guessing that MSR is protected as CONFIG[0123] are. */
1641a72f7ea6Sql 	rtw_set_access(&rsc->sc_regs, RTW_ACCESS_CONFIG);
1642a72f7ea6Sql 
1643a72f7ea6Sql 	msr = RTW_READ8(&rsc->sc_regs, RTW_MSR) & ~RTW_MSR_NETYPE_MASK;
1644a72f7ea6Sql 
1645a72f7ea6Sql 	switch (opmode) {
1646a72f7ea6Sql 	case IEEE80211_M_AHDEMO:
1647a72f7ea6Sql 	case IEEE80211_M_IBSS:
1648a72f7ea6Sql 		msr |= RTW_MSR_NETYPE_ADHOC_OK;
1649a72f7ea6Sql 		break;
1650a72f7ea6Sql 	case IEEE80211_M_HOSTAP:
1651a72f7ea6Sql 		msr |= RTW_MSR_NETYPE_AP_OK;
1652a72f7ea6Sql 		break;
1653a72f7ea6Sql 	case IEEE80211_M_STA:
1654a72f7ea6Sql 		msr |= RTW_MSR_NETYPE_INFRA_OK;
1655a72f7ea6Sql 		break;
1656a72f7ea6Sql 	}
1657a72f7ea6Sql 	RTW_WRITE8(&rsc->sc_regs, RTW_MSR, msr);
1658a72f7ea6Sql 
1659a72f7ea6Sql 	rtw_set_access(&rsc->sc_regs, RTW_ACCESS_NONE);
1660a72f7ea6Sql }
1661a72f7ea6Sql 
1662a72f7ea6Sql static void
1663a72f7ea6Sql rtw_pktfilt_load(rtw_softc_t *rsc)
1664a72f7ea6Sql {
1665a72f7ea6Sql 	struct rtw_regs *regs = &rsc->sc_regs;
1666a72f7ea6Sql 	struct ieee80211com *ic = &rsc->sc_ic;
1667a72f7ea6Sql 
1668a72f7ea6Sql 	/* XXX might be necessary to stop Rx/Tx engines while setting filters */
1669a72f7ea6Sql 	rsc->sc_rcr &= ~RTW_RCR_PKTFILTER_MASK;
1670a72f7ea6Sql 	rsc->sc_rcr &= ~(RTW_RCR_MXDMA_MASK | RTW_RCR_RXFTH_MASK);
1671a72f7ea6Sql 
1672a72f7ea6Sql 	rsc->sc_rcr |= RTW_RCR_PKTFILTER_DEFAULT;
1673a72f7ea6Sql 	/* MAC auto-reset PHY (huh?) */
1674a72f7ea6Sql 	rsc->sc_rcr |= RTW_RCR_ENMARP;
1675a72f7ea6Sql 	/* DMA whole Rx packets, only.  Set Tx DMA burst size to 1024 bytes. */
1676a72f7ea6Sql 	rsc->sc_rcr |= RTW_RCR_RXFTH_WHOLE |RTW_RCR_MXDMA_1024;
1677a72f7ea6Sql 
1678a72f7ea6Sql 	switch (ic->ic_opmode) {
1679a72f7ea6Sql 	case IEEE80211_M_AHDEMO:
1680a72f7ea6Sql 	case IEEE80211_M_IBSS:
1681a72f7ea6Sql 		/* receive broadcasts in our BSS */
1682a72f7ea6Sql 		rsc->sc_rcr |= RTW_RCR_ADD3;
1683a72f7ea6Sql 		break;
1684a72f7ea6Sql 	default:
1685a72f7ea6Sql 		break;
1686a72f7ea6Sql 	}
1687a72f7ea6Sql #if 0
1688a72f7ea6Sql 	/* XXX accept all broadcast if scanning */
1689a72f7ea6Sql 	rsc->sc_rcr |= RTW_RCR_AB;	/* accept all broadcast */
1690a72f7ea6Sql #endif
1691a72f7ea6Sql 	RTW_WRITE(regs, RTW_MAR0, 0xffffffff);
1692a72f7ea6Sql 	RTW_WRITE(regs, RTW_MAR1, 0xffffffff);
1693a72f7ea6Sql 	rsc->sc_rcr |= RTW_RCR_AM;
1694a72f7ea6Sql 	RTW_WRITE(regs, RTW_RCR, rsc->sc_rcr);
1695a72f7ea6Sql 	RTW_SYNC(regs, RTW_MAR0, RTW_RCR); /* RTW_MAR0 < RTW_MAR1 < RTW_RCR */
1696a72f7ea6Sql 
1697a72f7ea6Sql 	RTW_DPRINTF(RTW_DEBUG_PKTFILT,
1698a72f7ea6Sql 	    "RTW_MAR0 %08x RTW_MAR1 %08x RTW_RCR %08x\n",
1699a72f7ea6Sql 	    RTW_READ(regs, RTW_MAR0),
1700a72f7ea6Sql 	    RTW_READ(regs, RTW_MAR1), RTW_READ(regs, RTW_RCR));
1701a72f7ea6Sql 	RTW_WRITE(regs, RTW_RCR, rsc->sc_rcr);
1702a72f7ea6Sql }
1703a72f7ea6Sql 
1704a72f7ea6Sql static void
1705a72f7ea6Sql rtw_transmit_config(struct rtw_regs *regs)
1706a72f7ea6Sql {
1707a72f7ea6Sql 	uint32_t tcr;
1708a72f7ea6Sql 
1709a72f7ea6Sql 	tcr = RTW_READ(regs, RTW_TCR);
1710a72f7ea6Sql 
1711a72f7ea6Sql 	tcr |= RTW_TCR_CWMIN;
1712a72f7ea6Sql 	tcr &= ~RTW_TCR_MXDMA_MASK;
1713a72f7ea6Sql 	tcr |= RTW_TCR_MXDMA_1024;
1714a72f7ea6Sql 	tcr |= RTW_TCR_SAT;		/* send ACK as fast as possible */
1715a72f7ea6Sql 	tcr &= ~RTW_TCR_LBK_MASK;
1716a72f7ea6Sql 	tcr |= RTW_TCR_LBK_NORMAL;	/* normal operating mode */
1717a72f7ea6Sql 
1718a72f7ea6Sql 	/* set short/long retry limits */
1719a72f7ea6Sql 	tcr &= ~(RTW_TCR_SRL_MASK|RTW_TCR_LRL_MASK);
1720a72f7ea6Sql 	tcr |= LSHIFT(0x4, RTW_TCR_SRL_MASK) | LSHIFT(0x4, RTW_TCR_LRL_MASK);
1721a72f7ea6Sql 
1722a72f7ea6Sql 	tcr &= ~RTW_TCR_CRC;	/* NIC appends CRC32 */
1723a72f7ea6Sql 	RTW_WRITE(regs, RTW_TCR, tcr);
1724a72f7ea6Sql 	RTW_SYNC(regs, RTW_TCR, RTW_TCR);
1725a72f7ea6Sql }
1726a72f7ea6Sql 
1727a72f7ea6Sql int
1728a72f7ea6Sql rtw_refine_setting(rtw_softc_t *rsc)
1729a72f7ea6Sql {
1730a72f7ea6Sql 	struct rtw_regs *regs;
1731a72f7ea6Sql 	int rc = 0;
1732a72f7ea6Sql 
1733a72f7ea6Sql 	regs = &rsc->sc_regs;
1734a72f7ea6Sql 	rc = rtw_reset(rsc);
1735a72f7ea6Sql 	if (rc != 0)
1736a72f7ea6Sql 		return (-1);
1737a72f7ea6Sql 
1738a72f7ea6Sql 	rtw_beacon_tx_disable(regs);
1739a72f7ea6Sql 	rtw_io_enable(rsc, RTW_CR_RE|RTW_CR_TE, 1);
1740a72f7ea6Sql 	rtw_set_mode(regs, RTW_EPROM_CMD_CONFIG);
1741a72f7ea6Sql 
1742a72f7ea6Sql 	rtw_transmit_config(regs);
1743a72f7ea6Sql 	rtw_pktfilt_load(rsc);
1744a72f7ea6Sql 	rtw_set_access(regs, RTW_ACCESS_CONFIG);
1745a72f7ea6Sql 	RTW_WRITE(regs, RTW_TINT, 0xffffffff);
1746a72f7ea6Sql 	RTW_WRITE8(regs, RTW_MSR, 0x0);	/* no link */
1747a72f7ea6Sql 	RTW_WRITE16(regs, RTW_BRSR, 0);
1748a72f7ea6Sql 
1749a72f7ea6Sql 	rtw_set_access(regs, RTW_ACCESS_ANAPARM);
1750a72f7ea6Sql 	rtw_set_access(regs, RTW_ACCESS_NONE);
1751a72f7ea6Sql 	RTW_WRITE(regs, RTW_FEMR, 0xffff);
1752a72f7ea6Sql 	RTW_SYNC(regs, RTW_FEMR, RTW_FEMR);
1753a72f7ea6Sql 	rtw_set_rfprog(regs, rsc->sc_rfchipid, "rtw");
1754a72f7ea6Sql 
1755a72f7ea6Sql 	RTW_WRITE8(regs, RTW_PHYDELAY, rsc->sc_phydelay);
1756a72f7ea6Sql 	RTW_WRITE8(regs, RTW_CRCOUNT, RTW_CRCOUNT_MAGIC);
1757a72f7ea6Sql 	rtw_set_mode(regs, RTW_EPROM_CMD_NORMAL);
1758a72f7ea6Sql 	return (0);
1759a72f7ea6Sql }
1760a72f7ea6Sql 
1761a72f7ea6Sql static int
1762a72f7ea6Sql rtw_tune(rtw_softc_t *rsc)
1763a72f7ea6Sql {
1764a72f7ea6Sql 	struct ieee80211com *ic = &rsc->sc_ic;
1765a72f7ea6Sql 	uint32_t chan;
1766a72f7ea6Sql 	int rc;
1767a72f7ea6Sql 	int antdiv = rsc->sc_flags & RTW_F_ANTDIV,
1768a72f7ea6Sql 	    dflantb = rsc->sc_flags & RTW_F_DFLANTB;
1769a72f7ea6Sql 
1770a72f7ea6Sql 	ASSERT(ic->ic_curchan != NULL);
1771a72f7ea6Sql 
1772a72f7ea6Sql 	chan = ieee80211_chan2ieee(ic, ic->ic_curchan);
1773a72f7ea6Sql 	RTW_DPRINTF(RTW_DEBUG_TUNE, "rtw: chan no = %x", chan);
1774a72f7ea6Sql 
1775a72f7ea6Sql 	if (chan == IEEE80211_CHAN_ANY) {
1776a72f7ea6Sql 		cmn_err(CE_WARN, "%s: chan == IEEE80211_CHAN_ANY\n", __func__);
1777a72f7ea6Sql 		return (-1);
1778a72f7ea6Sql 	}
1779a72f7ea6Sql 
1780a72f7ea6Sql 	if (chan == rsc->sc_cur_chan) {
1781a72f7ea6Sql 		RTW_DPRINTF(RTW_DEBUG_TUNE,
1782a72f7ea6Sql 		    "%s: already tuned chan %d\n", __func__, chan);
1783a72f7ea6Sql 		return (0);
1784a72f7ea6Sql 	}
1785a72f7ea6Sql 	rtw_idle(&rsc->sc_regs);
1786a72f7ea6Sql 	rtw_io_enable(rsc, RTW_CR_RE | RTW_CR_TE, 0);
1787a72f7ea6Sql 	ASSERT((rsc->sc_flags & RTW_F_ENABLED) != 0);
1788a72f7ea6Sql 
1789a72f7ea6Sql 	if ((rc = rtw_phy_init(&rsc->sc_regs, rsc->sc_rf,
1790a72f7ea6Sql 	    rtw_chan2txpower(&rsc->sc_srom, ic, ic->ic_curchan),
1791a72f7ea6Sql 	    rsc->sc_csthr, ic->ic_curchan->ich_freq, antdiv,
1792a72f7ea6Sql 	    dflantb, RTW_ON)) != 0) {
1793a72f7ea6Sql 		/* XXX condition on powersaving */
1794a72f7ea6Sql 		cmn_err(CE_NOTE, "phy init failed\n");
1795a72f7ea6Sql 	}
1796a72f7ea6Sql 	rtw_io_enable(rsc, RTW_CR_RE | RTW_CR_TE, 1);
1797a72f7ea6Sql 	rtw_resume_ticks(rsc);
1798a72f7ea6Sql 	rsc->sc_cur_chan = chan;
1799a72f7ea6Sql 	return (rc);
1800a72f7ea6Sql }
1801a72f7ea6Sql 
1802a72f7ea6Sql static int
1803a72f7ea6Sql rtw_init(rtw_softc_t *rsc)
1804a72f7ea6Sql {
1805a72f7ea6Sql 	struct ieee80211com *ic = &rsc->sc_ic;
1806a72f7ea6Sql 	int rc = 0;
1807a72f7ea6Sql 
18089aa73b68SQin Michael Li 	rtw_stop(rsc);
18099aa73b68SQin Michael Li 	mutex_enter(&rsc->sc_genlock);
1810a72f7ea6Sql 	if ((rc = rtw_enable(rsc)) != 0)
1811a72f7ea6Sql 		goto out;
1812a72f7ea6Sql 	rc = rtw_refine_setting(rsc);
18139aa73b68SQin Michael Li 	if (rc != 0) {
18149aa73b68SQin Michael Li 		mutex_exit(&rsc->sc_genlock);
1815a72f7ea6Sql 		return (rc);
18169aa73b68SQin Michael Li 	}
1817a72f7ea6Sql 	rtw_swring_setup(rsc, 1);
1818a72f7ea6Sql 	rtw_hwring_setup(rsc);
1819a72f7ea6Sql 	RTW_WRITE16(&rsc->sc_regs, RTW_BSSID16, 0x0);
1820a72f7ea6Sql 	RTW_WRITE(&rsc->sc_regs, RTW_BSSID32, 0x0);
1821a72f7ea6Sql 	rtw_enable_interrupts(rsc);
1822a72f7ea6Sql 
1823a72f7ea6Sql 	ic->ic_ibss_chan = &ic->ic_sup_channels[1];
1824a72f7ea6Sql 	ic->ic_curchan = ic->ic_ibss_chan;
1825a72f7ea6Sql 	RTW_DPRINTF(RTW_DEBUG_TUNE, "%s: channel %d freq %d flags 0x%04x\n",
1826a72f7ea6Sql 	    __func__, ieee80211_chan2ieee(ic, ic->ic_curchan),
1827a72f7ea6Sql 	    ic->ic_curchan->ich_freq, ic->ic_curchan->ich_flags);
18289aa73b68SQin Michael Li 	rsc->sc_invalid = 0;
1829a72f7ea6Sql out:
18309aa73b68SQin Michael Li 	mutex_exit(&rsc->sc_genlock);
1831a72f7ea6Sql 	return (rc);
1832a72f7ea6Sql }
1833a72f7ea6Sql 
1834a72f7ea6Sql static struct rtw_rf *
1835a72f7ea6Sql rtw_rf_attach(rtw_softc_t *rsc, enum rtw_rfchipid rfchipid, int digphy)
1836a72f7ea6Sql {
1837a72f7ea6Sql 	rtw_rf_write_t rf_write;
1838a72f7ea6Sql 	struct rtw_rf *rf;
1839a72f7ea6Sql 	int rtw_host_rfio;
1840a72f7ea6Sql 
1841a72f7ea6Sql 	switch (rfchipid) {
1842a72f7ea6Sql 	default:
1843a72f7ea6Sql 		rf_write = rtw_rf_hostwrite;
1844a72f7ea6Sql 		break;
1845a72f7ea6Sql 	case RTW_RFCHIPID_INTERSIL:
1846a72f7ea6Sql 	case RTW_RFCHIPID_PHILIPS:
1847a72f7ea6Sql 	case RTW_RFCHIPID_GCT:	/* XXX a guess */
1848a72f7ea6Sql 	case RTW_RFCHIPID_RFMD:
1849a72f7ea6Sql 		rtw_host_rfio = 1;
1850a72f7ea6Sql 		rf_write = (rtw_host_rfio) ? rtw_rf_hostwrite : rtw_rf_macwrite;
1851a72f7ea6Sql 		break;
1852a72f7ea6Sql 	}
1853a72f7ea6Sql 
1854a72f7ea6Sql 	switch (rfchipid) {
1855a72f7ea6Sql 	case RTW_RFCHIPID_MAXIM:
1856a72f7ea6Sql 		rf = rtw_max2820_create(&rsc->sc_regs, rf_write, 0);
1857a72f7ea6Sql 		rsc->sc_pwrstate_cb = rtw_maxim_pwrstate;
1858a72f7ea6Sql 		break;
1859a72f7ea6Sql 	case RTW_RFCHIPID_PHILIPS:
1860a72f7ea6Sql 		rf = rtw_sa2400_create(&rsc->sc_regs, rf_write, digphy);
1861a72f7ea6Sql 		rsc->sc_pwrstate_cb = rtw_philips_pwrstate;
1862a72f7ea6Sql 		break;
1863a72f7ea6Sql 	case RTW_RFCHIPID_RFMD:
1864a72f7ea6Sql 		/* XXX RFMD has no RF constructor */
1865a72f7ea6Sql 		rsc->sc_pwrstate_cb = rtw_rfmd_pwrstate;
1866a72f7ea6Sql 		/*FALLTHROUGH*/
1867a72f7ea6Sql 	default:
1868a72f7ea6Sql 		return (NULL);
1869a72f7ea6Sql 	}
1870a72f7ea6Sql 	if (rf != NULL) {
1871a72f7ea6Sql 		rf->rf_continuous_tx_cb =
1872a72f7ea6Sql 		    (rtw_continuous_tx_cb_t)rtw_continuous_tx_enable;
1873a72f7ea6Sql 		rf->rf_continuous_tx_arg = (void *)rsc;
1874a72f7ea6Sql 	}
1875a72f7ea6Sql 	return (rf);
1876a72f7ea6Sql }
1877a72f7ea6Sql 
1878a72f7ea6Sql /*
1879a72f7ea6Sql  * Revision C and later use a different PHY delay setting than
1880a72f7ea6Sql  * revisions A and B.
1881a72f7ea6Sql  */
1882a72f7ea6Sql static uint8_t
1883a72f7ea6Sql rtw_check_phydelay(struct rtw_regs *regs, uint32_t rcr0)
1884a72f7ea6Sql {
1885a72f7ea6Sql #define	REVAB (RTW_RCR_MXDMA_UNLIMITED | RTW_RCR_AICV)
1886a72f7ea6Sql #define	REVC (REVAB | RTW_RCR_RXFTH_WHOLE)
1887a72f7ea6Sql 
1888a72f7ea6Sql 	uint8_t phydelay = LSHIFT(0x6, RTW_PHYDELAY_PHYDELAY);
1889a72f7ea6Sql 
1890a72f7ea6Sql 	RTW_WRITE(regs, RTW_RCR, REVAB);
1891a72f7ea6Sql 	RTW_WBW(regs, RTW_RCR, RTW_RCR);
1892a72f7ea6Sql 	RTW_WRITE(regs, RTW_RCR, REVC);
1893a72f7ea6Sql 
1894a72f7ea6Sql 	RTW_WBR(regs, RTW_RCR, RTW_RCR);
1895a72f7ea6Sql 	if ((RTW_READ(regs, RTW_RCR) & REVC) == REVC)
1896a72f7ea6Sql 		phydelay |= RTW_PHYDELAY_REVC_MAGIC;
1897a72f7ea6Sql 
1898a72f7ea6Sql 	RTW_WRITE(regs, RTW_RCR, rcr0);	/* restore RCR */
1899a72f7ea6Sql 	RTW_SYNC(regs, RTW_RCR, RTW_RCR);
1900a72f7ea6Sql 
1901a72f7ea6Sql 	return (phydelay);
1902a72f7ea6Sql #undef REVC
1903a72f7ea6Sql }
1904a72f7ea6Sql 
1905a72f7ea6Sql static void rtw_intr_rx(rtw_softc_t *rsc);
1906a72f7ea6Sql static void rtw_ring_recycling(rtw_softc_t *rsc, uint16_t isr, uint32_t pri);
1907a72f7ea6Sql 
1908a72f7ea6Sql static int
1909a72f7ea6Sql rtw_get_rate(struct ieee80211com *ic)
1910a72f7ea6Sql {
1911a72f7ea6Sql 	uint8_t (*rates)[IEEE80211_RATE_MAXSIZE];
1912a72f7ea6Sql 	int rate;
1913a72f7ea6Sql 
1914a72f7ea6Sql 	rates = &ic->ic_bss->in_rates.ir_rates;
1915a72f7ea6Sql 
1916a72f7ea6Sql 	if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE)
1917a72f7ea6Sql 		rate = ic->ic_fixed_rate;
1918a72f7ea6Sql 	else if (ic->ic_state == IEEE80211_S_RUN)
1919a72f7ea6Sql 		rate = (*rates)[ic->ic_bss->in_txrate];
1920a72f7ea6Sql 	else
1921a72f7ea6Sql 		rate = 0;
1922a72f7ea6Sql 	return (rate & IEEE80211_RATE_VAL);
1923a72f7ea6Sql }
1924a72f7ea6Sql 
1925a72f7ea6Sql /*
1926a72f7ea6Sql  * Arguments in:
1927a72f7ea6Sql  *
1928a72f7ea6Sql  * paylen:  payload length (no FCS, no WEP header)
1929a72f7ea6Sql  *
1930a72f7ea6Sql  * hdrlen:  header length
1931a72f7ea6Sql  *
1932a72f7ea6Sql  * rate:    MSDU speed, units 500kb/s
1933a72f7ea6Sql  *
1934a72f7ea6Sql  * flags:   IEEE80211_F_SHPREAMBLE (use short preamble),
1935a72f7ea6Sql  *          IEEE80211_F_SHSLOT (use short slot length)
1936a72f7ea6Sql  *
1937a72f7ea6Sql  * Arguments out:
1938a72f7ea6Sql  *
1939a72f7ea6Sql  * d:       802.11 Duration field for RTS,
1940a72f7ea6Sql  *          802.11 Duration field for data frame,
1941a72f7ea6Sql  *          PLCP Length for data frame,
1942a72f7ea6Sql  *          residual octets at end of data slot
1943a72f7ea6Sql  */
1944a72f7ea6Sql static int
1945a72f7ea6Sql rtw_compute_duration1(int len, int use_ack, uint32_t flags, int rate,
1946a72f7ea6Sql     struct rtw_ieee80211_duration *d)
1947a72f7ea6Sql {
1948a72f7ea6Sql 	int pre, ctsrate;
1949a72f7ea6Sql 	uint16_t ack, bitlen, data_dur, remainder;
1950a72f7ea6Sql 
1951a72f7ea6Sql 	/*
1952a72f7ea6Sql 	 * RTS reserves medium for SIFS | CTS | SIFS | (DATA) | SIFS | ACK
1953a72f7ea6Sql 	 * DATA reserves medium for SIFS | ACK
1954a72f7ea6Sql 	 *
1955a72f7ea6Sql 	 * XXXMYC: no ACK on multicast/broadcast or control packets
1956a72f7ea6Sql 	 */
1957a72f7ea6Sql 
1958a72f7ea6Sql 	bitlen = len * 8;
1959a72f7ea6Sql 
1960a72f7ea6Sql 	pre = IEEE80211_DUR_DS_SIFS;
1961a72f7ea6Sql 	if ((flags & IEEE80211_F_SHPREAMBLE) != 0)
1962a72f7ea6Sql 		pre += IEEE80211_DUR_DS_SHORT_PREAMBLE +
1963a72f7ea6Sql 		    IEEE80211_DUR_DS_FAST_PLCPHDR;
1964a72f7ea6Sql 	else
1965a72f7ea6Sql 		pre += IEEE80211_DUR_DS_LONG_PREAMBLE +
1966a72f7ea6Sql 		    IEEE80211_DUR_DS_SLOW_PLCPHDR;
1967a72f7ea6Sql 
1968a72f7ea6Sql 	d->d_residue = 0;
1969a72f7ea6Sql 	data_dur = (bitlen * 2) / rate;
1970a72f7ea6Sql 	remainder = (bitlen * 2) % rate;
1971a72f7ea6Sql 	if (remainder != 0) {
1972a72f7ea6Sql 		if (rate == 22)
1973a72f7ea6Sql 			d->d_residue = (rate - remainder) / 16;
1974a72f7ea6Sql 		data_dur++;
1975a72f7ea6Sql 	}
1976a72f7ea6Sql 
1977a72f7ea6Sql 	switch (rate) {
1978a72f7ea6Sql 	case 2:		/* 1 Mb/s */
1979a72f7ea6Sql 	case 4:		/* 2 Mb/s */
1980a72f7ea6Sql 		/* 1 - 2 Mb/s WLAN: send ACK/CTS at 1 Mb/s */
1981a72f7ea6Sql 		ctsrate = 2;
1982a72f7ea6Sql 		break;
1983a72f7ea6Sql 	case 11:	/* 5.5 Mb/s */
1984a72f7ea6Sql 	case 22:	/* 11  Mb/s */
1985a72f7ea6Sql 	case 44:	/* 22  Mb/s */
1986a72f7ea6Sql 		/* 5.5 - 11 Mb/s WLAN: send ACK/CTS at 2 Mb/s */
1987a72f7ea6Sql 		ctsrate = 4;
1988a72f7ea6Sql 		break;
1989a72f7ea6Sql 	default:
1990a72f7ea6Sql 		/* TBD */
1991a72f7ea6Sql 		return (-1);
1992a72f7ea6Sql 	}
1993a72f7ea6Sql 
1994a72f7ea6Sql 	d->d_plcp_len = data_dur;
1995a72f7ea6Sql 
1996a72f7ea6Sql 	ack = (use_ack) ? pre + (IEEE80211_DUR_DS_SLOW_ACK * 2) / ctsrate : 0;
1997a72f7ea6Sql 
1998a72f7ea6Sql 	d->d_rts_dur =
1999a72f7ea6Sql 	    pre + (IEEE80211_DUR_DS_SLOW_CTS * 2) / ctsrate +
2000a72f7ea6Sql 	    pre + data_dur +
2001a72f7ea6Sql 	    ack;
2002a72f7ea6Sql 
2003a72f7ea6Sql 	d->d_data_dur = ack;
2004a72f7ea6Sql 
2005a72f7ea6Sql 	return (0);
2006a72f7ea6Sql }
2007a72f7ea6Sql 
2008a72f7ea6Sql /*
2009a72f7ea6Sql  * Arguments in:
2010a72f7ea6Sql  *
2011a72f7ea6Sql  * wh:      802.11 header
2012a72f7ea6Sql  *
2013a72f7ea6Sql  * paylen:  payload length (no FCS, no WEP header)
2014a72f7ea6Sql  *
2015a72f7ea6Sql  * rate:    MSDU speed, units 500kb/s
2016a72f7ea6Sql  *
2017a72f7ea6Sql  * fraglen: fragment length, set to maximum (or higher) for no
2018a72f7ea6Sql  *          fragmentation
2019a72f7ea6Sql  *
2020a72f7ea6Sql  * flags:   IEEE80211_F_PRIVACY (hardware adds WEP),
2021a72f7ea6Sql  *          IEEE80211_F_SHPREAMBLE (use short preamble),
2022a72f7ea6Sql  *          IEEE80211_F_SHSLOT (use short slot length)
2023a72f7ea6Sql  *
2024a72f7ea6Sql  * Arguments out:
2025a72f7ea6Sql  *
2026a72f7ea6Sql  * d0: 802.11 Duration fields (RTS/Data), PLCP Length, Service fields
2027a72f7ea6Sql  *     of first/only fragment
2028a72f7ea6Sql  *
2029a72f7ea6Sql  * dn: 802.11 Duration fields (RTS/Data), PLCP Length, Service fields
2030a72f7ea6Sql  *     of first/only fragment
2031a72f7ea6Sql  */
2032a72f7ea6Sql static int
2033a72f7ea6Sql rtw_compute_duration(struct ieee80211_frame *wh, int len,
2034a72f7ea6Sql     uint32_t flags, int fraglen, int rate, struct rtw_ieee80211_duration *d0,
2035a72f7ea6Sql     struct rtw_ieee80211_duration *dn, int *npktp)
2036a72f7ea6Sql {
2037a72f7ea6Sql 	int ack, rc;
2038a72f7ea6Sql 	int firstlen, hdrlen, lastlen, lastlen0, npkt, overlen, paylen;
2039a72f7ea6Sql 
2040a72f7ea6Sql 	/* don't think about addr4 here */
2041a72f7ea6Sql 	hdrlen = sizeof (struct ieee80211_frame);
2042a72f7ea6Sql 
2043a72f7ea6Sql 	paylen = len - hdrlen;
2044a72f7ea6Sql 
2045a72f7ea6Sql 	if ((wh->i_fc[1] & IEEE80211_FC1_WEP) != 0) {
2046a72f7ea6Sql 		overlen = 8 + IEEE80211_CRC_LEN;
2047a72f7ea6Sql 		paylen -= 8;
2048a72f7ea6Sql 	} else
2049a72f7ea6Sql 		overlen = IEEE80211_CRC_LEN;
2050a72f7ea6Sql 
2051a72f7ea6Sql 	npkt = paylen / fraglen;
2052a72f7ea6Sql 	lastlen0 = paylen % fraglen;
2053a72f7ea6Sql 
2054a72f7ea6Sql 	if (npkt == 0)			/* no fragments */
2055a72f7ea6Sql 		lastlen = paylen + overlen;
2056a72f7ea6Sql 	else if (lastlen0 != 0) {	/* a short "tail" fragment */
2057a72f7ea6Sql 		lastlen = lastlen0 + overlen;
2058a72f7ea6Sql 		npkt++;
2059a72f7ea6Sql 	} else			/* full-length "tail" fragment */
2060a72f7ea6Sql 		lastlen = fraglen + overlen;
2061a72f7ea6Sql 
2062a72f7ea6Sql 	if (npktp != NULL)
2063a72f7ea6Sql 		*npktp = npkt;
2064a72f7ea6Sql 
2065a72f7ea6Sql 	if (npkt > 1)
2066a72f7ea6Sql 		firstlen = fraglen + overlen;
2067a72f7ea6Sql 	else
2068a72f7ea6Sql 		firstlen = paylen + overlen;
2069a72f7ea6Sql 
2070a72f7ea6Sql 	ack = !IEEE80211_IS_MULTICAST(wh->i_addr1) &&
2071a72f7ea6Sql 	    (wh->i_fc[1] & IEEE80211_FC0_TYPE_MASK) !=
2072a72f7ea6Sql 	    IEEE80211_FC0_TYPE_CTL;
2073a72f7ea6Sql 
2074a72f7ea6Sql 	rc = rtw_compute_duration1(firstlen + hdrlen,
2075a72f7ea6Sql 	    ack, flags, rate, d0);
2076a72f7ea6Sql 	if (rc == -1)
2077a72f7ea6Sql 		return (rc);
2078a72f7ea6Sql 
2079a72f7ea6Sql 	if (npkt <= 1) {
2080a72f7ea6Sql 		*dn = *d0;
2081a72f7ea6Sql 		return (0);
2082a72f7ea6Sql 	}
2083a72f7ea6Sql 	return (rtw_compute_duration1(lastlen + hdrlen, ack, flags,
2084a72f7ea6Sql 	    rate, dn));
2085a72f7ea6Sql }
2086a72f7ea6Sql 
2087a72f7ea6Sql static int
2088a72f7ea6Sql rtw_assembly_80211(rtw_softc_t *rsc, struct rtw_txbuf *bf,
2089a72f7ea6Sql     mblk_t *mp)
2090a72f7ea6Sql {
2091a72f7ea6Sql 	ieee80211com_t *ic;
2092a72f7ea6Sql 	struct rtw_txdesc *ds;
2093a72f7ea6Sql 	struct ieee80211_frame *wh;
2094a72f7ea6Sql 	uint8_t *buf;
2095a72f7ea6Sql 	uint32_t ctl0 = 0, ctl1 = 0;
2096a72f7ea6Sql 	int npkt, rate;
2097a72f7ea6Sql 	struct rtw_ieee80211_duration d0, dn;
2098a72f7ea6Sql 	int32_t iswep, pktlen, mblen;
2099a72f7ea6Sql 	mblk_t *mp0;
2100a72f7ea6Sql 
2101a72f7ea6Sql 	ic = &rsc->sc_ic;
2102a72f7ea6Sql 	ds = bf->txdesc;
2103a72f7ea6Sql 	buf = (uint8_t *)bf->bf_dma.mem_va;
2104a72f7ea6Sql 	bzero(buf, bf->bf_dma.alength);
2105a72f7ea6Sql 	bzero((uint8_t *)ds, sizeof (struct rtw_txdesc));
2106a72f7ea6Sql 	wh = (struct ieee80211_frame *)mp->b_rptr;
2107a72f7ea6Sql 	iswep = wh->i_fc[1] & IEEE80211_FC1_WEP;
2108a72f7ea6Sql 
2109a72f7ea6Sql 	/* ieee80211_crypto_encap() needs a single mblk */
2110a72f7ea6Sql 	mp0 = allocb(bf->bf_dma.alength, BPRI_MED);
2111a72f7ea6Sql 	if (mp0 == NULL) {
2112a72f7ea6Sql 		cmn_err(CE_WARN, "%s: allocb(mp) error", __func__);
2113a72f7ea6Sql 		return (-1);
2114a72f7ea6Sql 	}
2115a72f7ea6Sql 	for (; mp != NULL; mp = mp->b_cont) {
2116020c4770Sql 			mblen = (uintptr_t)mp->b_wptr - (uintptr_t)mp->b_rptr;
2117a72f7ea6Sql 			bcopy(mp->b_rptr, mp0->b_wptr, mblen);
2118a72f7ea6Sql 			mp0->b_wptr += mblen;
2119a72f7ea6Sql 	}
2120a72f7ea6Sql 
2121a72f7ea6Sql 	if (iswep) {
2122a72f7ea6Sql 		struct ieee80211_key *k;
2123a72f7ea6Sql 
2124a72f7ea6Sql 		k = ieee80211_crypto_encap(ic, mp0);
2125a72f7ea6Sql 		if (k == NULL) {
2126a72f7ea6Sql 			cmn_err(CE_WARN, "%s: ieee80211_crypto_encap() error",
2127a72f7ea6Sql 			    __func__);
2128a72f7ea6Sql 			freemsg(mp0);
2129a72f7ea6Sql 			return (-1);
2130a72f7ea6Sql 		}
2131a72f7ea6Sql 	}
2132a72f7ea6Sql 	pktlen = msgdsize(mp0);
2133a72f7ea6Sql 
2134a72f7ea6Sql #if 0
2135a72f7ea6Sql 	RTW_DPRINTF(RTW_DEBUG_XMIT, "-----------send------begin--------");
2136a72f7ea6Sql 	ieee80211_dump_pkt((uint8_t *)(mp0->b_rptr), pktlen, 0, 0);
2137a72f7ea6Sql 	RTW_DPRINTF(RTW_DEBUG_XMIT, "-----------send------end--------");
2138a72f7ea6Sql #endif
2139a72f7ea6Sql 	/* RTW_DMA_SYNC(bf->bf_dma, DDI_DMA_SYNC_FORDEV); */
2140a72f7ea6Sql 	if (pktlen > bf->bf_dma.alength) {
2141a72f7ea6Sql 		cmn_err(CE_WARN, "%s: overlength packet pktlen = %d\n",
2142a72f7ea6Sql 		    __func__, pktlen);
2143a72f7ea6Sql 		freemsg(mp0);
2144a72f7ea6Sql 		return (-1);
2145a72f7ea6Sql 	}
2146a72f7ea6Sql 	bcopy(mp0->b_rptr, buf, pktlen);
2147a72f7ea6Sql 	RTW_DMA_SYNC(bf->bf_dma, DDI_DMA_SYNC_FORDEV);
2148a72f7ea6Sql 
2149a72f7ea6Sql 	/* setup descriptor */
2150a72f7ea6Sql 	ctl0 = RTW_TXCTL0_RTSRATE_1MBPS;
2151a72f7ea6Sql 
2152a72f7ea6Sql 	if (((ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0) &&
2153a72f7ea6Sql 	    (ic->ic_bss->in_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) {
2154a72f7ea6Sql 		ctl0 |= RTW_TXCTL0_SPLCP;
2155a72f7ea6Sql 	}
2156a72f7ea6Sql 	/* XXX do real rate control */
2157a72f7ea6Sql 	if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
2158a72f7ea6Sql 	    IEEE80211_FC0_TYPE_MGT)
2159a72f7ea6Sql 		rate = 2;
2160a72f7ea6Sql 	else {
2161a72f7ea6Sql 		rate = MAX(2, rtw_get_rate(ic));
2162a72f7ea6Sql 	}
2163a72f7ea6Sql 	ctl0 = ctl0 |
2164a72f7ea6Sql 	    LSHIFT(pktlen, RTW_TXCTL0_TPKTSIZE_MASK);
2165a72f7ea6Sql 
2166a72f7ea6Sql 	RTW_DPRINTF(RTW_DEBUG_XMIT, "%s: rate = %d", __func__, rate);
2167a72f7ea6Sql 
2168a72f7ea6Sql 	switch (rate) {
2169a72f7ea6Sql 	default:
2170a72f7ea6Sql 	case 2:
2171a72f7ea6Sql 		ctl0 |= RTW_TXCTL0_RATE_1MBPS;
2172a72f7ea6Sql 		break;
2173a72f7ea6Sql 	case 4:
2174a72f7ea6Sql 		ctl0 |= RTW_TXCTL0_RATE_2MBPS;
2175a72f7ea6Sql 		break;
2176a72f7ea6Sql 	case 11:
2177a72f7ea6Sql 		ctl0 |= RTW_TXCTL0_RATE_5MBPS;
2178a72f7ea6Sql 		break;
2179a72f7ea6Sql 	case 22:
2180a72f7ea6Sql 		ctl0 |= RTW_TXCTL0_RATE_11MBPS;
2181a72f7ea6Sql 		break;
2182a72f7ea6Sql 	}
2183a72f7ea6Sql 
2184a72f7ea6Sql 	/* XXX >= ? Compare after fragmentation? */
2185a72f7ea6Sql 	if (pktlen > ic->ic_rtsthreshold) {
2186a72f7ea6Sql 		ctl0 |= RTW_TXCTL0_RTSEN;
2187a72f7ea6Sql 		cmn_err(CE_NOTE, "%s: fragmentation: pktlen = %d",
2188a72f7ea6Sql 		    __func__, pktlen);
2189a72f7ea6Sql 	}
2190a72f7ea6Sql 
2191a72f7ea6Sql 	if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
2192a72f7ea6Sql 	    IEEE80211_FC0_TYPE_MGT) {
2193a72f7ea6Sql 		ctl0 &= ~(RTW_TXCTL0_SPLCP | RTW_TXCTL0_RTSEN);
2194a72f7ea6Sql 		if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
2195a72f7ea6Sql 		    IEEE80211_FC0_SUBTYPE_BEACON)
2196a72f7ea6Sql 			ctl0 |= RTW_TXCTL0_BEACON;
2197a72f7ea6Sql 	}
2198a72f7ea6Sql 
2199a72f7ea6Sql 	if (rtw_compute_duration(wh, pktlen,
2200a72f7ea6Sql 	    ic->ic_flags, ic->ic_fragthreshold,
2201a72f7ea6Sql 	    rate, &d0, &dn, &npkt) == -1) {
2202a72f7ea6Sql 		RTW_DPRINTF(RTW_DEBUG_XMIT,
2203a72f7ea6Sql 		    "%s: fail compute duration\n", __func__);
2204a72f7ea6Sql 		freemsg(mp0);
2205a72f7ea6Sql 		return (-1);
2206a72f7ea6Sql 	}
2207020c4770Sql 	*(uint16_t *)(uintptr_t)wh->i_dur = (d0.d_data_dur);
2208a72f7ea6Sql 
2209a72f7ea6Sql 	ctl1 = LSHIFT(d0.d_plcp_len, RTW_TXCTL1_LENGTH_MASK) |
2210a72f7ea6Sql 	    LSHIFT(d0.d_rts_dur, RTW_TXCTL1_RTSDUR_MASK);
2211a72f7ea6Sql 
2212a72f7ea6Sql 	if (d0.d_residue)
2213a72f7ea6Sql 		ctl1 |= RTW_TXCTL1_LENGEXT;
2214a72f7ea6Sql 
2215a72f7ea6Sql 	RTW_DPRINTF(RTW_DEBUG_XMIT, "%s: duration=%x, ctl1=%x", __func__,
2216020c4770Sql 	    *(uint16_t *)(uintptr_t)wh->i_dur, ctl1);
2217a72f7ea6Sql 
2218a72f7ea6Sql 	if (bf->bf_dma.alength > RTW_TXLEN_LENGTH_MASK) {
2219a72f7ea6Sql 		RTW_DPRINTF(RTW_DEBUG_XMIT,
2220a72f7ea6Sql 		    "%s: seg too long\n", __func__);
2221a72f7ea6Sql 		freemsg(mp0);
2222a72f7ea6Sql 		return (-1);
2223a72f7ea6Sql 	}
2224a72f7ea6Sql 	ds->td_ctl0 = ctl0;
2225a72f7ea6Sql 	ds->td_ctl0 |= RTW_TXCTL0_OWN | RTW_TXCTL0_LS | RTW_TXCTL0_FS;
2226a72f7ea6Sql 	ds->td_ctl1 = ctl1;
2227a72f7ea6Sql 	ds->td_buf = bf->bf_dma.cookie.dmac_address;
2228a72f7ea6Sql 	ds->td_len = pktlen & 0xfff;
2229a72f7ea6Sql 	ds->td_next = bf->next_bf_daddr;
2230a72f7ea6Sql 
2231a72f7ea6Sql 	RTW_DMA_SYNC_DESC(rsc->sc_desc_dma,
2232a72f7ea6Sql 	    RTW_DESC_OFFSET(hd_txmd, bf->order),
2233a72f7ea6Sql 	    sizeof (struct rtw_txdesc),
2234a72f7ea6Sql 	    DDI_DMA_SYNC_FORDEV);
2235a72f7ea6Sql 
2236a72f7ea6Sql 	RTW_DPRINTF(RTW_DEBUG_XMIT,
2237a72f7ea6Sql 	    "descriptor: order = %d, phy_addr=%x, ctl0=%x,"
2238a72f7ea6Sql 	    " ctl1=%x, buf=%x, len=%x, next=%x", bf->order,
2239a72f7ea6Sql 	    bf->bf_daddr, ds->td_ctl0, ds->td_ctl1,
2240a72f7ea6Sql 	    ds->td_buf, ds->td_len, ds->td_next);
2241a72f7ea6Sql 	rsc->sc_pktxmt64++;
2242a72f7ea6Sql 	rsc->sc_bytexmt64 += pktlen;
2243a72f7ea6Sql 
2244a72f7ea6Sql 	freemsg(mp0);
2245a72f7ea6Sql 	return (0);
2246a72f7ea6Sql }
2247a72f7ea6Sql 
2248a72f7ea6Sql static int
2249a72f7ea6Sql rtw_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type)
2250a72f7ea6Sql {
2251a72f7ea6Sql 	rtw_softc_t *rsc = (rtw_softc_t *)ic;
2252a72f7ea6Sql 	struct ieee80211_node *in = ic->ic_bss;
2253a72f7ea6Sql 	struct rtw_txbuf *bf = NULL;
2254a72f7ea6Sql 	int ret, i = RTW_TXPRIMD;
2255a72f7ea6Sql 
2256a72f7ea6Sql 	mutex_enter(&rsc->sc_txlock);
2257a72f7ea6Sql 	mutex_enter(&rsc->sc_txq[i].txbuf_lock);
2258a72f7ea6Sql 	bf = list_head(&rsc->sc_txq[i].tx_free_list);
2259a72f7ea6Sql 
2260a72f7ea6Sql 	if ((bf == NULL) || (rsc->sc_txq[i].tx_nfree <= 4)) {
2261a72f7ea6Sql 		RTW_DPRINTF(RTW_DEBUG_XMIT, "%s: no tx buf\n", __func__);
2262a72f7ea6Sql 		rsc->sc_noxmtbuf++;
2263a72f7ea6Sql 		if ((type & IEEE80211_FC0_TYPE_MASK) ==
2264a72f7ea6Sql 		    IEEE80211_FC0_TYPE_DATA) {
2265a72f7ea6Sql 			RTW_DPRINTF(RTW_DEBUG_XMIT, "%s: need reschedule\n",
2266a72f7ea6Sql 			    __func__);
2267a72f7ea6Sql 			rsc->sc_need_reschedule = 1;
2268a72f7ea6Sql 		} else {
2269a72f7ea6Sql 			freemsg(mp);
2270a72f7ea6Sql 		}
2271a72f7ea6Sql 		mutex_exit(&rsc->sc_txq[i].txbuf_lock);
2272a72f7ea6Sql 		mutex_exit(&rsc->sc_txlock);
2273a72f7ea6Sql 		return (1);
2274a72f7ea6Sql 	}
2275a72f7ea6Sql 	list_remove(&rsc->sc_txq[i].tx_free_list, bf);
2276a72f7ea6Sql 	rsc->sc_txq[i].tx_nfree--;
2277a72f7ea6Sql 
2278a72f7ea6Sql 	/* assemble 802.11 frame here */
2279a72f7ea6Sql 	ret = rtw_assembly_80211(rsc, bf, mp);
2280a72f7ea6Sql 	if (ret != 0) {
2281a72f7ea6Sql 		cmn_err(CE_WARN, "%s assembly frame error\n", __func__);
2282a72f7ea6Sql 		mutex_exit(&rsc->sc_txq[i].txbuf_lock);
2283a72f7ea6Sql 		mutex_exit(&rsc->sc_txlock);
2284a72f7ea6Sql 		if ((type & IEEE80211_FC0_TYPE_MASK) !=
2285a72f7ea6Sql 		    IEEE80211_FC0_TYPE_DATA) {
2286a72f7ea6Sql 			freemsg(mp);
2287a72f7ea6Sql 		}
2288a72f7ea6Sql 		return (1);
2289a72f7ea6Sql 	}
2290a72f7ea6Sql 	list_insert_tail(&rsc->sc_txq[i].tx_dirty_list, bf);
2291a72f7ea6Sql 	bf->bf_in = in;
2292a72f7ea6Sql 	rtw_dma_start(&rsc->sc_regs, i);
2293a72f7ea6Sql 
2294a72f7ea6Sql 	mutex_exit(&rsc->sc_txq[i].txbuf_lock);
2295a72f7ea6Sql 	mutex_exit(&rsc->sc_txlock);
2296a72f7ea6Sql 
2297a72f7ea6Sql 	freemsg(mp);
2298a72f7ea6Sql 	return (0);
2299a72f7ea6Sql }
2300a72f7ea6Sql 
2301a72f7ea6Sql static mblk_t *
2302a72f7ea6Sql rtw_m_tx(void *arg, mblk_t *mp)
2303a72f7ea6Sql {
2304a72f7ea6Sql 	rtw_softc_t *rsc = arg;
2305a72f7ea6Sql 	ieee80211com_t *ic = (ieee80211com_t *)rsc;
2306a72f7ea6Sql 	mblk_t *next;
2307a72f7ea6Sql 
2308a72f7ea6Sql 	if (ic->ic_state != IEEE80211_S_RUN) {
2309a72f7ea6Sql 		freemsgchain(mp);
2310a72f7ea6Sql 		return (NULL);
2311a72f7ea6Sql 	}
2312a72f7ea6Sql 
2313a72f7ea6Sql 	while (mp != NULL) {
2314a72f7ea6Sql 		next = mp->b_next;
2315a72f7ea6Sql 		mp->b_next = NULL;
2316a72f7ea6Sql 
2317a72f7ea6Sql 		if (rtw_send(ic, mp, IEEE80211_FC0_TYPE_DATA)) {
2318a72f7ea6Sql 			mp->b_next = next;
2319a72f7ea6Sql 			break;
2320a72f7ea6Sql 		}
2321a72f7ea6Sql 		mp = next;
2322a72f7ea6Sql 	}
2323a72f7ea6Sql 
2324a72f7ea6Sql 	return (mp);
2325a72f7ea6Sql 
2326a72f7ea6Sql }
2327a72f7ea6Sql 
2328a72f7ea6Sql static void
2329a72f7ea6Sql rtw_next_scan(void *arg)
2330a72f7ea6Sql {
2331a72f7ea6Sql 	ieee80211com_t *ic = arg;
2332a72f7ea6Sql 	rtw_softc_t *rsc = (rtw_softc_t *)arg;
2333a72f7ea6Sql 
2334a72f7ea6Sql 	rsc->sc_scan_id = 0;
2335a72f7ea6Sql 	if (ic->ic_state == IEEE80211_S_SCAN) {
2336a72f7ea6Sql 		RTW_DPRINTF(RTW_DEBUG_TUNE, "rtw_next_scan\n");
2337a72f7ea6Sql 		(void) ieee80211_next_scan(ic);
2338a72f7ea6Sql 	}
2339a72f7ea6Sql 
2340a72f7ea6Sql }
2341a72f7ea6Sql 
2342a72f7ea6Sql static void
2343a72f7ea6Sql rtw_join_bss(rtw_softc_t *rsc, uint8_t *bssid, uint16_t intval0)
2344a72f7ea6Sql {
2345a72f7ea6Sql 	uint16_t bcnitv, intval;
2346a72f7ea6Sql 	int i;
2347a72f7ea6Sql 	struct rtw_regs *regs = &rsc->sc_regs;
2348a72f7ea6Sql 
2349a72f7ea6Sql 	for (i = 0; i < IEEE80211_ADDR_LEN; i++)
2350a72f7ea6Sql 		RTW_WRITE8(regs, RTW_BSSID + i, bssid[i]);
2351a72f7ea6Sql 
2352a72f7ea6Sql 	RTW_SYNC(regs, RTW_BSSID16, RTW_BSSID32);
2353a72f7ea6Sql 	rtw_set_access(regs, RTW_ACCESS_CONFIG);
2354a72f7ea6Sql 
2355a72f7ea6Sql 	RTW_WRITE8(regs, RTW_MSR, 0x8);	/* sta mode link ok */
2356a72f7ea6Sql 	intval = MIN(intval0, PRESHIFT(RTW_BCNITV_BCNITV_MASK));
2357a72f7ea6Sql 
2358a72f7ea6Sql 	bcnitv = RTW_READ16(regs, RTW_BCNITV) & ~RTW_BCNITV_BCNITV_MASK;
2359a72f7ea6Sql 	bcnitv |= LSHIFT(intval, RTW_BCNITV_BCNITV_MASK);
2360a72f7ea6Sql 	RTW_WRITE16(regs, RTW_BCNITV, bcnitv);
2361a72f7ea6Sql 	RTW_WRITE16(regs, RTW_ATIMWND, LSHIFT(1, RTW_ATIMWND_ATIMWND));
2362a72f7ea6Sql 	RTW_WRITE16(regs, RTW_ATIMTRITV, LSHIFT(2, RTW_ATIMTRITV_ATIMTRITV));
2363a72f7ea6Sql 
2364a72f7ea6Sql 	rtw_set_access(regs, RTW_ACCESS_NONE);
2365a72f7ea6Sql 
2366a72f7ea6Sql 	/* TBD WEP */
2367a72f7ea6Sql 	/* RTW_WRITE8(regs, RTW_SCR, 0); */
2368a72f7ea6Sql 
2369a72f7ea6Sql 	rtw_io_enable(rsc, RTW_CR_RE | RTW_CR_TE, 1);
2370a72f7ea6Sql }
2371a72f7ea6Sql 
2372a72f7ea6Sql /*
2373a72f7ea6Sql  * Set the starting transmit rate for a node.
2374a72f7ea6Sql  */
2375a72f7ea6Sql static void
2376a72f7ea6Sql rtw_rate_ctl_start(rtw_softc_t *rsc, struct ieee80211_node *in)
2377a72f7ea6Sql {
2378a72f7ea6Sql 	ieee80211com_t *ic = (ieee80211com_t *)rsc;
2379a72f7ea6Sql 	int32_t srate;
2380a72f7ea6Sql 
2381a72f7ea6Sql 	if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) {
2382a72f7ea6Sql 		/*
2383a72f7ea6Sql 		 * No fixed rate is requested. For 11b start with
2384a72f7ea6Sql 		 * the highest negotiated rate; otherwise, for 11g
2385a72f7ea6Sql 		 * and 11a, we start "in the middle" at 24Mb or 36Mb.
2386a72f7ea6Sql 		 */
2387a72f7ea6Sql 		srate = in->in_rates.ir_nrates - 1;
2388a72f7ea6Sql 		if (ic->ic_curmode != IEEE80211_MODE_11B) {
2389a72f7ea6Sql 			/*
2390a72f7ea6Sql 			 * Scan the negotiated rate set to find the
2391a72f7ea6Sql 			 * closest rate.
2392a72f7ea6Sql 			 */
2393a72f7ea6Sql 			/* NB: the rate set is assumed sorted */
2394a72f7ea6Sql 			for (; srate >= 0 && IEEE80211_RATE(srate) > 72;
2395a72f7ea6Sql 			    srate--)
2396a72f7ea6Sql 				;
2397a72f7ea6Sql 		}
2398a72f7ea6Sql 	} else {
2399a72f7ea6Sql 		/*
2400a72f7ea6Sql 		 * A fixed rate is to be used;  We know the rate is
2401a72f7ea6Sql 		 * there because the rate set is checked when the
2402a72f7ea6Sql 		 * station associates.
2403a72f7ea6Sql 		 */
2404a72f7ea6Sql 		/* NB: the rate set is assumed sorted */
2405a72f7ea6Sql 		srate = in->in_rates.ir_nrates - 1;
2406a72f7ea6Sql 		for (; srate >= 0 && IEEE80211_RATE(srate) != ic->ic_fixed_rate;
2407a72f7ea6Sql 		    srate--)
2408a72f7ea6Sql 			;
2409a72f7ea6Sql 	}
2410a72f7ea6Sql 	in->in_txrate = srate;
2411a72f7ea6Sql }
2412a72f7ea6Sql 
2413a72f7ea6Sql 
2414a72f7ea6Sql /*
2415a72f7ea6Sql  * Reset the rate control state for each 802.11 state transition.
2416a72f7ea6Sql  */
2417a72f7ea6Sql static void
2418a72f7ea6Sql rtw_rate_ctl_reset(rtw_softc_t *rsc, enum ieee80211_state state)
2419a72f7ea6Sql {
2420a72f7ea6Sql 	ieee80211com_t *ic = &rsc->sc_ic;
2421a72f7ea6Sql 	ieee80211_node_t *in;
2422a72f7ea6Sql 
2423a72f7ea6Sql 	if (ic->ic_opmode == IEEE80211_M_STA) {
2424a72f7ea6Sql 		/*
2425a72f7ea6Sql 		 * Reset local xmit state; this is really only
2426a72f7ea6Sql 		 * meaningful when operating in station mode.
2427a72f7ea6Sql 		 */
2428a72f7ea6Sql 		in = (struct ieee80211_node *)ic->ic_bss;
2429a72f7ea6Sql 
2430a72f7ea6Sql 		if (state == IEEE80211_S_RUN) {
2431a72f7ea6Sql 			rtw_rate_ctl_start(rsc, in);
2432a72f7ea6Sql 		} else {
2433a72f7ea6Sql 			in->in_txrate = 0;
2434a72f7ea6Sql 		}
2435a72f7ea6Sql 	}
2436a72f7ea6Sql }
2437a72f7ea6Sql 
2438a72f7ea6Sql /*
2439a72f7ea6Sql  * Examine and potentially adjust the transmit rate.
2440a72f7ea6Sql  */
2441a72f7ea6Sql static void
2442a72f7ea6Sql rtw_rate_ctl(void *arg)
2443a72f7ea6Sql {
2444a72f7ea6Sql 	ieee80211com_t	*ic = (ieee80211com_t *)arg;
2445a72f7ea6Sql 	rtw_softc_t *rsc = (rtw_softc_t *)ic;
2446a72f7ea6Sql 	struct ieee80211_node *in = ic->ic_bss;
2447a72f7ea6Sql 	struct ieee80211_rateset *rs = &in->in_rates;
24489aa73b68SQin Michael Li 	int32_t mod = 1, nrate, enough;
2449a72f7ea6Sql 
2450a72f7ea6Sql 	mutex_enter(&rsc->sc_genlock);
24519aa73b68SQin Michael Li 	enough = (rsc->sc_tx_ok + rsc->sc_tx_err) >= 600? 1 : 0;
2452a72f7ea6Sql 
24539aa73b68SQin Michael Li 	/* err ratio is high -> down */
2454a72f7ea6Sql 	if (enough && rsc->sc_tx_ok < rsc->sc_tx_err)
2455a72f7ea6Sql 		mod = -1;
2456a72f7ea6Sql 
2457a72f7ea6Sql 	nrate = in->in_txrate;
2458a72f7ea6Sql 	switch (mod) {
2459a72f7ea6Sql 	case -1:
2460a72f7ea6Sql 		if (nrate > 0) {
2461a72f7ea6Sql 			nrate--;
2462a72f7ea6Sql 		}
2463a72f7ea6Sql 		break;
2464a72f7ea6Sql 	case 1:
2465a72f7ea6Sql 		if (nrate + 1 < rs->ir_nrates) {
2466a72f7ea6Sql 			nrate++;
2467a72f7ea6Sql 		}
2468a72f7ea6Sql 		break;
2469a72f7ea6Sql 	}
2470a72f7ea6Sql 
24719aa73b68SQin Michael Li 	if (nrate != in->in_txrate)
2472a72f7ea6Sql 		in->in_txrate = nrate;
24739aa73b68SQin Michael Li 	rsc->sc_tx_ok = rsc->sc_tx_err = rsc->sc_tx_retr = 0;
2474a72f7ea6Sql 	mutex_exit(&rsc->sc_genlock);
2475a72f7ea6Sql 	if (ic->ic_state == IEEE80211_S_RUN)
2476a72f7ea6Sql 		rsc->sc_ratectl_id = timeout(rtw_rate_ctl, ic,
2477a72f7ea6Sql 		    drv_usectohz(1000000));
2478a72f7ea6Sql }
2479a72f7ea6Sql 
2480a72f7ea6Sql static int32_t
2481a72f7ea6Sql rtw_new_state(ieee80211com_t *ic, enum ieee80211_state nstate, int arg)
2482a72f7ea6Sql {
2483a72f7ea6Sql 	rtw_softc_t *rsc = (rtw_softc_t *)ic;
2484a72f7ea6Sql 	int error;
2485a72f7ea6Sql 	enum ieee80211_state ostate;
2486a72f7ea6Sql 
2487a72f7ea6Sql 	ostate = ic->ic_state;
2488a72f7ea6Sql 
2489a72f7ea6Sql 	RTW_DPRINTF(RTW_DEBUG_ATTACH,
2490a72f7ea6Sql 	    "rtw_new_state: ostate:0x%x, nstate:0x%x, opmode:0x%x\n",
2491a72f7ea6Sql 	    ostate, nstate, ic->ic_opmode);
2492a72f7ea6Sql 
2493a72f7ea6Sql 
2494a72f7ea6Sql 	mutex_enter(&rsc->sc_genlock);
2495a72f7ea6Sql 	if (rsc->sc_scan_id != 0) {
2496a72f7ea6Sql 		(void) untimeout(rsc->sc_scan_id);
2497a72f7ea6Sql 		rsc->sc_scan_id = 0;
2498a72f7ea6Sql 	}
2499a72f7ea6Sql 	if (rsc->sc_ratectl_id != 0) {
2500a72f7ea6Sql 		(void) untimeout(rsc->sc_ratectl_id);
2501a72f7ea6Sql 		rsc->sc_ratectl_id = 0;
2502a72f7ea6Sql 	}
2503a72f7ea6Sql 	rtw_rate_ctl_reset(rsc, nstate);
2504a72f7ea6Sql 	if (ostate == IEEE80211_S_INIT && nstate != IEEE80211_S_INIT)
2505a72f7ea6Sql 		(void) rtw_pwrstate(rsc, RTW_ON);
25069aa73b68SQin Michael Li 	if (nstate != IEEE80211_S_INIT) {
25079aa73b68SQin Michael Li 		if ((error = rtw_tune(rsc)) != 0) {
25089aa73b68SQin Michael Li 			mutex_exit(&rsc->sc_genlock);
25099aa73b68SQin Michael Li 			return (error);
25109aa73b68SQin Michael Li 		}
2511a72f7ea6Sql 	}
2512a72f7ea6Sql 	switch (nstate) {
2513a72f7ea6Sql 	case IEEE80211_S_INIT:
2514a72f7ea6Sql 		RTW_DPRINTF(RTW_DEBUG_ATTACH, "rtw_new_state: S_INIT\n");
2515a72f7ea6Sql 		break;
2516a72f7ea6Sql 	case IEEE80211_S_SCAN:
2517a72f7ea6Sql 		RTW_DPRINTF(RTW_DEBUG_ATTACH, "rtw_new_state: S_SCAN\n");
2518a72f7ea6Sql 		rsc->sc_scan_id = timeout(rtw_next_scan, ic,
2519a72f7ea6Sql 		    drv_usectohz(200000));
2520a72f7ea6Sql 		rtw_set_nettype(rsc, IEEE80211_M_MONITOR);
2521a72f7ea6Sql 		break;
2522a72f7ea6Sql 	case IEEE80211_S_RUN:
2523a72f7ea6Sql 		RTW_DPRINTF(RTW_DEBUG_ATTACH, "rtw_new_state: S_RUN\n");
2524a72f7ea6Sql 		switch (ic->ic_opmode) {
2525a72f7ea6Sql 		case IEEE80211_M_HOSTAP:
2526a72f7ea6Sql 		case IEEE80211_M_IBSS:
2527a72f7ea6Sql 			rtw_set_nettype(rsc, IEEE80211_M_MONITOR);
2528a72f7ea6Sql 			/* TBD */
2529a72f7ea6Sql 			/*FALLTHROUGH*/
2530a72f7ea6Sql 		case IEEE80211_M_AHDEMO:
2531a72f7ea6Sql 		case IEEE80211_M_STA:
2532a72f7ea6Sql 			RTW_DPRINTF(RTW_DEBUG_ATTACH,
2533a72f7ea6Sql 			    "rtw_new_state: sta\n");
2534a72f7ea6Sql 			rtw_join_bss(rsc, ic->ic_bss->in_bssid, 0);
2535a72f7ea6Sql 			rsc->sc_ratectl_id = timeout(rtw_rate_ctl, ic,
2536a72f7ea6Sql 			    drv_usectohz(1000000));
2537a72f7ea6Sql 			break;
2538a72f7ea6Sql 		case IEEE80211_M_MONITOR:
2539a72f7ea6Sql 			break;
2540a72f7ea6Sql 		}
2541a72f7ea6Sql 		rtw_set_nettype(rsc, ic->ic_opmode);
2542a72f7ea6Sql 		break;
2543a72f7ea6Sql 	case IEEE80211_S_ASSOC:
2544a72f7ea6Sql 	case IEEE80211_S_AUTH:
2545a72f7ea6Sql 		break;
2546a72f7ea6Sql 	}
2547a72f7ea6Sql 
2548a72f7ea6Sql 	mutex_exit(&rsc->sc_genlock);
2549a72f7ea6Sql 	/*
2550a72f7ea6Sql 	 * Invoke the parent method to complete the work.
2551a72f7ea6Sql 	 */
2552a72f7ea6Sql 	error = rsc->sc_newstate(ic, nstate, arg);
2553a72f7ea6Sql 
2554a72f7ea6Sql 	return (error);
2555a72f7ea6Sql }
2556a72f7ea6Sql 
2557a72f7ea6Sql static void
2558a72f7ea6Sql rtw_intr_rx(rtw_softc_t *rsc)
2559a72f7ea6Sql {
2560a72f7ea6Sql #define	IS_BEACON(__fc0)						\
2561a72f7ea6Sql 	((__fc0 & (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) ==\
2562a72f7ea6Sql 	(IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_BEACON))
2563a72f7ea6Sql 	/*
2564a72f7ea6Sql 	 * ratetbl[4] = {2, 4, 11, 22};
2565a72f7ea6Sql 	 */
2566a72f7ea6Sql 	struct rtw_rxbuf *bf;
2567a72f7ea6Sql 	struct rtw_rxdesc *ds;
2568a72f7ea6Sql 	int hwrate, len, rssi;
2569a72f7ea6Sql 	uint32_t hstat, hrssi, htsftl;
2570a72f7ea6Sql 	int is_last, next, n = 0, i;
2571a72f7ea6Sql 	struct ieee80211_frame *wh;
2572a72f7ea6Sql 	ieee80211com_t *ic = (ieee80211com_t *)rsc;
2573a72f7ea6Sql 	mblk_t *mp;
2574a72f7ea6Sql 
2575a72f7ea6Sql 	RTW_DPRINTF(RTW_DEBUG_RECV, "%s rtw_intr_rx: enter ic_state=%x\n",
2576a72f7ea6Sql 	    __func__, rsc->sc_ic.ic_state);
2577a72f7ea6Sql 	mutex_enter(&rsc->rxbuf_lock);
2578a72f7ea6Sql 	next = rsc->rx_next;
2579a72f7ea6Sql 	mutex_exit(&rsc->rxbuf_lock);
2580a72f7ea6Sql 	for (i = 0; i < RTW_RXQLEN; i++) {
2581a72f7ea6Sql 		RTW_DMA_SYNC_DESC(rsc->sc_desc_dma,
2582a72f7ea6Sql 		    RTW_DESC_OFFSET(hd_rx, next),
2583a72f7ea6Sql 		    sizeof (struct rtw_rxdesc),
2584a72f7ea6Sql 		    DDI_DMA_SYNC_FORKERNEL);
2585a72f7ea6Sql 		n++;
2586a72f7ea6Sql 		bf = rsc->rxbuf_h + next;
2587a72f7ea6Sql 		ds = bf->rxdesc;
2588a72f7ea6Sql 		hstat = (ds->rd_stat);
2589a72f7ea6Sql 		hrssi = ds->rd_rssi;
2590a72f7ea6Sql 		htsftl = ds->rd_tsftl;
2591a72f7ea6Sql 		/* htsfth = ds->rd_tsfth; */
2592a72f7ea6Sql 		RTW_DPRINTF(RTW_DEBUG_RECV, "%s: stat=%x\n", __func__, hstat);
2593a72f7ea6Sql 		/* still belongs to NIC */
2594a72f7ea6Sql 		if ((hstat & RTW_RXSTAT_OWN) != 0) {
2595a72f7ea6Sql 			if (n > 1) {
2596a72f7ea6Sql 				RTW_DPRINTF(RTW_DEBUG_RECV,
2597a72f7ea6Sql 				    "%s: n > 1\n", __func__);
2598a72f7ea6Sql 				break;
2599a72f7ea6Sql 			}
2600a72f7ea6Sql 			RTW_DMA_SYNC_DESC(rsc->sc_desc_dma,
2601a72f7ea6Sql 			    RTW_DESC_OFFSET(hd_rx, 0),
2602a72f7ea6Sql 			    sizeof (struct rtw_rxdesc),
2603a72f7ea6Sql 			    DDI_DMA_SYNC_FORCPU);
2604a72f7ea6Sql 			bf = rsc->rxbuf_h;
2605a72f7ea6Sql 			ds = bf->rxdesc;
2606a72f7ea6Sql 			hstat = (ds->rd_stat);
2607a72f7ea6Sql 			if ((hstat & RTW_RXSTAT_OWN) != 0)
2608a72f7ea6Sql 				break;
2609a72f7ea6Sql 			next = 0 /* RTW_RXQLEN - 1 */;
2610a72f7ea6Sql 			continue;
2611a72f7ea6Sql 		}
2612a72f7ea6Sql 
2613a72f7ea6Sql 		rsc->sc_pktrcv64++;
2614a72f7ea6Sql 		if ((hstat & RTW_RXSTAT_IOERROR) != 0) {
2615a72f7ea6Sql 			RTW_DPRINTF(RTW_DEBUG_RECV,
2616a72f7ea6Sql 			    "rtw: DMA error/FIFO overflow %08x, "
2617a72f7ea6Sql 			    "rx descriptor %d\n",
2618a72f7ea6Sql 			    hstat & RTW_RXSTAT_IOERROR, next);
2619a72f7ea6Sql 			goto next;
2620a72f7ea6Sql 		}
2621a72f7ea6Sql 
2622a72f7ea6Sql 		len = MASK_AND_RSHIFT(hstat, RTW_RXSTAT_LENGTH_MASK);
2623a72f7ea6Sql 		rsc->sc_bytercv64 += len;
2624a72f7ea6Sql 
2625a72f7ea6Sql 		/* CRC is included with the packet; trim it off. */
2626a72f7ea6Sql 		/* len -= IEEE80211_CRC_LEN; */
2627a72f7ea6Sql 
2628a72f7ea6Sql 		hwrate = MASK_AND_RSHIFT(hstat, RTW_RXSTAT_RATE_MASK);
2629a72f7ea6Sql 		if (hwrate >= 4) {
2630a72f7ea6Sql 			goto next;
2631a72f7ea6Sql 		}
2632a72f7ea6Sql 
2633a72f7ea6Sql 		if ((hstat & RTW_RXSTAT_RES) != 0 &&
2634a72f7ea6Sql 		    rsc->sc_ic.ic_opmode != IEEE80211_M_MONITOR) {
2635a72f7ea6Sql 			goto next;
2636a72f7ea6Sql 		}
2637a72f7ea6Sql 
2638a72f7ea6Sql 		/* if bad flags, skip descriptor */
2639a72f7ea6Sql 		if ((hstat & RTW_RXSTAT_ONESEG) != RTW_RXSTAT_ONESEG) {
2640a72f7ea6Sql 			RTW_DPRINTF(RTW_DEBUG_RECV,
2641a72f7ea6Sql 			    "rtw too many rx segments\n");
2642a72f7ea6Sql 			goto next;
2643a72f7ea6Sql 		}
2644a72f7ea6Sql 
2645a72f7ea6Sql 		if (rsc->sc_rfchipid == RTW_RFCHIPID_PHILIPS)
2646a72f7ea6Sql 			rssi = MASK_AND_RSHIFT(hrssi, RTW_RXRSSI_RSSI);
2647a72f7ea6Sql 		else {
2648a72f7ea6Sql 			rssi = MASK_AND_RSHIFT(hrssi, RTW_RXRSSI_IMR_RSSI);
2649a72f7ea6Sql 			/*
2650a72f7ea6Sql 			 * TBD find out each front-end's LNA gain in the
2651a72f7ea6Sql 			 * front-end's units
2652a72f7ea6Sql 			 */
2653a72f7ea6Sql 			if ((hrssi & RTW_RXRSSI_IMR_LNA) == 0)
2654a72f7ea6Sql 				rssi |= 0x80;
2655a72f7ea6Sql 		}
2656a72f7ea6Sql 		/* sq = MASK_AND_RSHIFT(hrssi, RTW_RXRSSI_SQ); */
2657a72f7ea6Sql 
2658a72f7ea6Sql 
2659a72f7ea6Sql 		/* deal with the frame itself here */
2660a72f7ea6Sql 		mp = allocb(rsc->sc_dmabuf_size, BPRI_MED);
2661a72f7ea6Sql 		if (mp == NULL) {
2662a72f7ea6Sql 			cmn_err(CE_WARN, "rtw: alloc mblk error");
2663a72f7ea6Sql 			rsc->sc_norcvbuf++;
2664a72f7ea6Sql 			return;
2665a72f7ea6Sql 		}
2666a72f7ea6Sql 		len -= IEEE80211_CRC_LEN;
2667a72f7ea6Sql 		RTW_DMA_SYNC(bf->bf_dma, DDI_DMA_SYNC_FORKERNEL);
2668a72f7ea6Sql 		bcopy(bf->bf_dma.mem_va, mp->b_rptr, len);
2669a72f7ea6Sql 		mp->b_wptr += len;
2670a72f7ea6Sql 		wh = (struct ieee80211_frame *)mp->b_rptr;
2671a72f7ea6Sql 		if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
2672a72f7ea6Sql 		    IEEE80211_FC0_TYPE_CTL) {
2673a72f7ea6Sql 			cmn_err(CE_WARN, "TYPE CTL !!\n");
2674a72f7ea6Sql 			freemsg(mp);
2675a72f7ea6Sql 			goto next;
2676a72f7ea6Sql 		}
2677a72f7ea6Sql 		(void) ieee80211_input(ic, mp, ic->ic_bss, rssi, htsftl);
2678a72f7ea6Sql next:
2679a72f7ea6Sql 		if (next == 63)
2680a72f7ea6Sql 			is_last = 1;
2681a72f7ea6Sql 		else
2682a72f7ea6Sql 			is_last = 0;
2683a72f7ea6Sql 		rtw_rxdesc_init(rsc, bf, next, is_last);
2684a72f7ea6Sql 
2685a72f7ea6Sql 		next = (next + 1)%RTW_RXQLEN;
2686a72f7ea6Sql 		RTW_DPRINTF(RTW_DEBUG_RECV, "%s: next = %d\n", __func__, next);
2687a72f7ea6Sql 	}
2688a72f7ea6Sql 	mutex_enter(&rsc->rxbuf_lock);
2689a72f7ea6Sql 	rsc->rx_next = next;
2690a72f7ea6Sql 	mutex_exit(&rsc->rxbuf_lock);
2691a72f7ea6Sql }
2692a72f7ea6Sql 
2693a72f7ea6Sql static void
2694a72f7ea6Sql rtw_ring_recycling(rtw_softc_t *rsc, uint16_t isr, uint32_t pri)
2695a72f7ea6Sql {
2696a72f7ea6Sql 	struct rtw_txbuf *bf;
2697a72f7ea6Sql 	struct rtw_txdesc *ds;
2698a72f7ea6Sql 	uint32_t hstat;
2699a72f7ea6Sql 	uint32_t  head = 0;
2700a72f7ea6Sql 	uint32_t  cnt = 0, idx = 0;
2701a72f7ea6Sql 
2702a72f7ea6Sql 	mutex_enter(&rsc->sc_txq[pri].txbuf_lock);
2703a72f7ea6Sql 	head = RTW_READ(&rsc->sc_regs, RTW_TNPDA);
2704a72f7ea6Sql 	if (head == rsc->hw_go) {
2705a72f7ea6Sql 		mutex_exit(&rsc->sc_txq[pri].txbuf_lock);
2706a72f7ea6Sql 		return;
2707a72f7ea6Sql 	}
2708a72f7ea6Sql 	RTW_DPRINTF(RTW_DEBUG_XMIT, "rtw_ring_recycling: enter ic_state=%x\n",
2709a72f7ea6Sql 	    rsc->sc_ic.ic_state);
2710a72f7ea6Sql 
2711a72f7ea6Sql 	bf = list_head(&rsc->sc_txq[pri].tx_dirty_list);
2712a72f7ea6Sql 	if (bf == NULL) {
2713a72f7ea6Sql 		RTW_DPRINTF(RTW_DEBUG_XMIT,
2714a72f7ea6Sql 		    "rtw_ring_recycling: dirty bf[%d] NULL\n", pri);
2715a72f7ea6Sql 		mutex_exit(&rsc->sc_txq[pri].txbuf_lock);
2716a72f7ea6Sql 		return;
2717a72f7ea6Sql 	}
2718a72f7ea6Sql 
2719a72f7ea6Sql 	while ((bf != NULL) && (rsc->hw_go != head)) {
2720a72f7ea6Sql 		cnt++;
2721a72f7ea6Sql 		idx = (rsc->hw_go - rsc->hw_start) / sizeof (struct rtw_txdesc);
2722a72f7ea6Sql 		if (idx == 63)
2723a72f7ea6Sql 			rsc->hw_go = rsc->hw_start;
2724a72f7ea6Sql 		else
2725a72f7ea6Sql 			rsc->hw_go += sizeof (struct rtw_txdesc);
2726a72f7ea6Sql 		RTW_DMA_SYNC_DESC(rsc->sc_desc_dma,
2727a72f7ea6Sql 		    RTW_DESC_OFFSET(hd_txmd, idx),
2728a72f7ea6Sql 		    sizeof (struct rtw_txdesc),
2729a72f7ea6Sql 		    DDI_DMA_SYNC_FORCPU);
2730a72f7ea6Sql 
2731a72f7ea6Sql 		RTW_DPRINTF(RTW_DEBUG_XMIT, "Head = 0x%x\n", head);
2732a72f7ea6Sql 		ds = bf->txdesc;
2733a72f7ea6Sql 		hstat = (ds->td_stat);
2734a72f7ea6Sql 		ds->td_len = ds->td_len & 0xfff;
2735a72f7ea6Sql 		RTW_DPRINTF(RTW_DEBUG_XMIT,
2736a72f7ea6Sql 		    "%s rtw_ring_recycling: stat=%x, pri=%x\n",
2737a72f7ea6Sql 		    __func__, hstat, pri);
2738a72f7ea6Sql 		if (hstat & RTW_TXSTAT_TOK)
2739a72f7ea6Sql 			rsc->sc_tx_ok++;
2740a72f7ea6Sql 		else {
2741a72f7ea6Sql 			RTW_DPRINTF(RTW_DEBUG_XMIT,
2742a72f7ea6Sql 			    "TX err @%d, o %d, retry[%d], isr[0x%x], cnt %d\n",
2743a72f7ea6Sql 			    idx, (hstat & RTW_TXSTAT_OWN)?1:0,
2744a72f7ea6Sql 			    (hstat & RTW_TXSTAT_DRC_MASK), isr, cnt);
2745a72f7ea6Sql 			if ((hstat & RTW_TXSTAT_DRC_MASK) <= 4) {
2746a72f7ea6Sql 				rsc->sc_tx_ok++;
2747a72f7ea6Sql 			} else {
2748a72f7ea6Sql 				rsc->sc_tx_err++;
2749a72f7ea6Sql 			}
2750a72f7ea6Sql 		}
2751a72f7ea6Sql 		rsc->sc_tx_retr +=
2752a72f7ea6Sql 		    (hstat & RTW_TXSTAT_DRC_MASK);
2753a72f7ea6Sql 		rsc->sc_xmtretry +=
2754a72f7ea6Sql 		    (hstat & RTW_TXSTAT_DRC_MASK);
2755a72f7ea6Sql 		list_remove(&rsc->sc_txq[pri].tx_dirty_list, bf);
2756a72f7ea6Sql 		list_insert_tail(&rsc->sc_txq[pri].tx_free_list,
2757a72f7ea6Sql 		    bf);
2758a72f7ea6Sql 		(rsc->sc_txq[pri].tx_nfree)++;
2759a72f7ea6Sql 		if (rsc->sc_need_reschedule == 1) {
2760a72f7ea6Sql 			mac_tx_update(rsc->sc_ic.ic_mach);
2761a72f7ea6Sql 			rsc->sc_need_reschedule = 0;
2762a72f7ea6Sql 		}
2763a72f7ea6Sql 		RTW_DPRINTF(RTW_DEBUG_XMIT,
2764a72f7ea6Sql 		    "rtw_ring_recycling: nfree[%d]=%d\n",
2765a72f7ea6Sql 		    pri, rsc->sc_txq[pri].tx_nfree);
2766a72f7ea6Sql 		bzero((uint8_t *)ds, sizeof (struct rtw_txdesc));
2767a72f7ea6Sql 		RTW_DMA_SYNC_DESC(rsc->sc_desc_dma,
2768a72f7ea6Sql 		    RTW_DESC_OFFSET(hd_txmd, idx),
2769a72f7ea6Sql 		    sizeof (struct rtw_txdesc),
2770a72f7ea6Sql 		    DDI_DMA_SYNC_FORDEV);
2771a72f7ea6Sql 		bf = list_head(&rsc->sc_txq[pri].tx_dirty_list);
2772a72f7ea6Sql 	}
2773a72f7ea6Sql 	mutex_exit(&rsc->sc_txq[pri].txbuf_lock);
2774a72f7ea6Sql }
2775a72f7ea6Sql 
2776a72f7ea6Sql static void
2777a72f7ea6Sql rtw_intr_timeout(rtw_softc_t *rsc)
2778a72f7ea6Sql {
2779a72f7ea6Sql 	rtw_resume_ticks(rsc);
2780a72f7ea6Sql }
2781a72f7ea6Sql 
2782a72f7ea6Sql static uint_t
2783a72f7ea6Sql rtw_intr(caddr_t arg)
2784a72f7ea6Sql {
2785020c4770Sql 	/* LINTED E_BAD_PTR_CAST_ALIGN */
2786a72f7ea6Sql 	rtw_softc_t *rsc = (rtw_softc_t *)arg;
2787a72f7ea6Sql 	struct rtw_regs *regs = &rsc->sc_regs;
2788a72f7ea6Sql 	uint16_t isr = 0;
2789a72f7ea6Sql 
2790a72f7ea6Sql 	mutex_enter(&rsc->sc_genlock);
2791a72f7ea6Sql 	isr = RTW_READ16(regs, RTW_ISR);
2792a72f7ea6Sql 	RTW_WRITE16(regs, RTW_ISR, isr);
2793a72f7ea6Sql 
2794a72f7ea6Sql 	if (isr == 0) {
2795a72f7ea6Sql 		mutex_exit(&rsc->sc_genlock);
2796a72f7ea6Sql 		return (DDI_INTR_UNCLAIMED);
2797a72f7ea6Sql 	}
2798a72f7ea6Sql 
2799a72f7ea6Sql #ifdef DEBUG
2800a72f7ea6Sql #define	PRINTINTR(flag) { \
2801a72f7ea6Sql 	if ((isr & flag) != 0) { \
2802a72f7ea6Sql 		RTW_DPRINTF(RTW_DEBUG_INTR, "|" #flag); \
2803a72f7ea6Sql 	} \
2804a72f7ea6Sql }
2805a72f7ea6Sql 
2806a72f7ea6Sql 	if ((rtw_dbg_flags & RTW_DEBUG_INTR) != 0 && isr != 0) {
2807a72f7ea6Sql 
2808a72f7ea6Sql 		RTW_DPRINTF(RTW_DEBUG_INTR, "rtw: reg[ISR] = %x", isr);
2809a72f7ea6Sql 
2810a72f7ea6Sql 		PRINTINTR(RTW_INTR_TXFOVW);
2811a72f7ea6Sql 		PRINTINTR(RTW_INTR_TIMEOUT);
2812a72f7ea6Sql 		PRINTINTR(RTW_INTR_BCNINT);
2813a72f7ea6Sql 		PRINTINTR(RTW_INTR_ATIMINT);
2814a72f7ea6Sql 		PRINTINTR(RTW_INTR_TBDER);
2815a72f7ea6Sql 		PRINTINTR(RTW_INTR_TBDOK);
2816a72f7ea6Sql 		PRINTINTR(RTW_INTR_THPDER);
2817a72f7ea6Sql 		PRINTINTR(RTW_INTR_THPDOK);
2818a72f7ea6Sql 		PRINTINTR(RTW_INTR_TNPDER);
2819a72f7ea6Sql 		PRINTINTR(RTW_INTR_TNPDOK);
2820a72f7ea6Sql 		PRINTINTR(RTW_INTR_RXFOVW);
2821a72f7ea6Sql 		PRINTINTR(RTW_INTR_RDU);
2822a72f7ea6Sql 		PRINTINTR(RTW_INTR_TLPDER);
2823a72f7ea6Sql 		PRINTINTR(RTW_INTR_TLPDOK);
2824a72f7ea6Sql 		PRINTINTR(RTW_INTR_RER);
2825a72f7ea6Sql 		PRINTINTR(RTW_INTR_ROK);
2826a72f7ea6Sql 	}
2827a72f7ea6Sql #undef PRINTINTR
2828a72f7ea6Sql #endif /* DEBUG */
2829a72f7ea6Sql 
2830a72f7ea6Sql 	rsc->sc_intr++;
2831a72f7ea6Sql 
2832a72f7ea6Sql 	if ((isr & RTW_INTR_RX) != 0) {
2833a72f7ea6Sql 		mutex_exit(&rsc->sc_genlock);
2834a72f7ea6Sql 		rtw_intr_rx(rsc);
2835a72f7ea6Sql 		mutex_enter(&rsc->sc_genlock);
2836a72f7ea6Sql 	}
2837a72f7ea6Sql 	if ((isr & RTW_INTR_TIMEOUT) != 0)
2838a72f7ea6Sql 		rtw_intr_timeout(rsc);
2839a72f7ea6Sql 
2840a72f7ea6Sql 	if ((isr & RTW_INTR_TX) != 0)
2841a72f7ea6Sql 		rtw_ring_recycling(rsc, isr, 1);
2842a72f7ea6Sql 	mutex_exit(&rsc->sc_genlock);
2843a72f7ea6Sql 	return (DDI_INTR_CLAIMED);
2844a72f7ea6Sql }
2845a72f7ea6Sql 
2846a72f7ea6Sql static void
28479aa73b68SQin Michael Li rtw_stop(void *arg)
2848a72f7ea6Sql {
2849a72f7ea6Sql 	rtw_softc_t *rsc = (rtw_softc_t *)arg;
2850a72f7ea6Sql 	struct rtw_regs *regs = &rsc->sc_regs;
2851a72f7ea6Sql 
2852a72f7ea6Sql 	mutex_enter(&rsc->sc_genlock);
2853a72f7ea6Sql 	rtw_disable_interrupts(regs);
2854a72f7ea6Sql 	rtw_io_enable(rsc, RTW_CR_RE | RTW_CR_TE, 0);
2855a72f7ea6Sql 	RTW_WRITE8(regs, RTW_TPPOLL, RTW_TPPOLL_SALL);
28569aa73b68SQin Michael Li 	rsc->sc_invalid = 1;
2857a72f7ea6Sql 	mutex_exit(&rsc->sc_genlock);
28589aa73b68SQin Michael Li }
2859a72f7ea6Sql 
28609aa73b68SQin Michael Li static void
28619aa73b68SQin Michael Li rtw_m_stop(void *arg)
28629aa73b68SQin Michael Li {
28639aa73b68SQin Michael Li 	rtw_softc_t *rsc = (rtw_softc_t *)arg;
28649aa73b68SQin Michael Li 
28659aa73b68SQin Michael Li 	(void) ieee80211_new_state(&rsc->sc_ic, IEEE80211_S_INIT, -1);
28669aa73b68SQin Michael Li 	rtw_stop(rsc);
2867a72f7ea6Sql }
2868a72f7ea6Sql 
286994d05f6cSQin Michael Li /*
287094d05f6cSQin Michael Li  * quiesce(9E) entry point.
287194d05f6cSQin Michael Li  *
287294d05f6cSQin Michael Li  * This function is called when the system is single-threaded at high
287394d05f6cSQin Michael Li  * PIL with preemption disabled. Therefore, this function must not be
287494d05f6cSQin Michael Li  * blocked.
287594d05f6cSQin Michael Li  *
287694d05f6cSQin Michael Li  * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure.
287794d05f6cSQin Michael Li  * DDI_FAILURE indicates an error condition and should almost never happen.
287894d05f6cSQin Michael Li  */
287994d05f6cSQin Michael Li int
288094d05f6cSQin Michael Li rtw_quiesce(dev_info_t *dip)
288194d05f6cSQin Michael Li {
288294d05f6cSQin Michael Li 	rtw_softc_t  *rsc = NULL;
288394d05f6cSQin Michael Li 	struct rtw_regs *regs;
288494d05f6cSQin Michael Li 
288594d05f6cSQin Michael Li 	rsc = ddi_get_soft_state(rtw_soft_state_p, ddi_get_instance(dip));
288694d05f6cSQin Michael Li 	ASSERT(rsc != NULL);
288794d05f6cSQin Michael Li 	regs = &rsc->sc_regs;
288894d05f6cSQin Michael Li 
288994d05f6cSQin Michael Li 	rtw_dbg_flags = 0;
289094d05f6cSQin Michael Li 	rtw_disable_interrupts(regs);
289194d05f6cSQin Michael Li 	rtw_io_enable(rsc, RTW_CR_RE | RTW_CR_TE, 0);
289294d05f6cSQin Michael Li 	RTW_WRITE8(regs, RTW_TPPOLL, RTW_TPPOLL_SALL);
289394d05f6cSQin Michael Li 
289494d05f6cSQin Michael Li 	return (DDI_SUCCESS);
289594d05f6cSQin Michael Li }
289694d05f6cSQin Michael Li 
289794d05f6cSQin Michael Li /*
289894d05f6cSQin Michael Li  * callback functions for /get/set properties
289994d05f6cSQin Michael Li  */
290094d05f6cSQin Michael Li static int
290194d05f6cSQin Michael Li rtw_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
290294d05f6cSQin Michael Li     uint_t wldp_length, const void *wldp_buf)
290394d05f6cSQin Michael Li {
29049aa73b68SQin Michael Li 	rtw_softc_t *rsc = (rtw_softc_t *)arg;
29059aa73b68SQin Michael Li 	struct ieee80211com *ic = &rsc->sc_ic;
290694d05f6cSQin Michael Li 	int err;
290794d05f6cSQin Michael Li 
29089aa73b68SQin Michael Li 	err = ieee80211_setprop(ic, pr_name, wldp_pr_num,
290994d05f6cSQin Michael Li 	    wldp_length, wldp_buf);
291094d05f6cSQin Michael Li 	if (err == ENETRESET) {
29119aa73b68SQin Michael Li 		if (ic->ic_des_esslen && (rsc->sc_invalid == 0)) {
29129aa73b68SQin Michael Li 			(void) rtw_init(rsc);
29139aa73b68SQin Michael Li 			(void) ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
291494d05f6cSQin Michael Li 		}
291594d05f6cSQin Michael Li 		err = 0;
291694d05f6cSQin Michael Li 	}
291794d05f6cSQin Michael Li 	return (err);
291894d05f6cSQin Michael Li }
291994d05f6cSQin Michael Li 
292094d05f6cSQin Michael Li static int
292194d05f6cSQin Michael Li rtw_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
2922*0dc2366fSVenugopal Iyer     uint_t wldp_length, void *wldp_buf)
292394d05f6cSQin Michael Li {
292494d05f6cSQin Michael Li 	rtw_softc_t *rsc = arg;
292594d05f6cSQin Michael Li 	int err;
292694d05f6cSQin Michael Li 
292794d05f6cSQin Michael Li 	err = ieee80211_getprop(&rsc->sc_ic, pr_name, wldp_pr_num,
2928*0dc2366fSVenugopal Iyer 	    wldp_length, wldp_buf);
292994d05f6cSQin Michael Li 
293094d05f6cSQin Michael Li 	return (err);
293194d05f6cSQin Michael Li }
293294d05f6cSQin Michael Li 
2933*0dc2366fSVenugopal Iyer static void
2934*0dc2366fSVenugopal Iyer rtw_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
2935*0dc2366fSVenugopal Iyer     mac_prop_info_handle_t prh)
2936*0dc2366fSVenugopal Iyer {
2937*0dc2366fSVenugopal Iyer 	rtw_softc_t *rsc = arg;
2938*0dc2366fSVenugopal Iyer 
2939*0dc2366fSVenugopal Iyer 	ieee80211_propinfo(&rsc->sc_ic, pr_name, wldp_pr_num, prh);
2940*0dc2366fSVenugopal Iyer }
2941a72f7ea6Sql 
2942a72f7ea6Sql static int
2943a72f7ea6Sql rtw_m_start(void *arg)
2944a72f7ea6Sql {
2945a72f7ea6Sql 	rtw_softc_t *rsc = (rtw_softc_t *)arg;
2946a72f7ea6Sql 	ieee80211com_t *ic = (ieee80211com_t *)rsc;
2947a72f7ea6Sql 	int ret;
2948a72f7ea6Sql #ifdef DEBUG
2949a72f7ea6Sql 	rtw_print_regs(&rsc->sc_regs, "rtw", "rtw_start");
2950a72f7ea6Sql #endif
29519aa73b68SQin Michael Li 
2952a72f7ea6Sql 	ret = rtw_init(rsc);
2953a72f7ea6Sql 	if (ret) {
2954a72f7ea6Sql 		cmn_err(CE_WARN, "rtw: failed to do rtw_init\n");
29559aa73b68SQin Michael Li 		return (EIO);
2956a72f7ea6Sql 	}
2957a72f7ea6Sql 	(void) ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
2958a72f7ea6Sql 	return (0);
2959a72f7ea6Sql }
2960a72f7ea6Sql 
2961a72f7ea6Sql 
2962a72f7ea6Sql static int
2963a72f7ea6Sql rtw_m_unicst(void *arg, const uint8_t *macaddr)
2964a72f7ea6Sql {
2965a72f7ea6Sql 	rtw_softc_t *rsc = (rtw_softc_t *)arg;
2966a72f7ea6Sql 	ieee80211com_t *ic = (ieee80211com_t *)rsc;
2967a72f7ea6Sql 	struct rtw_regs *regs = &rsc->sc_regs;
2968a72f7ea6Sql 	uint32_t t;
2969a72f7ea6Sql 
2970a72f7ea6Sql 	mutex_enter(&rsc->sc_genlock);
2971a72f7ea6Sql 	bcopy(macaddr, ic->ic_macaddr, 6);
2972a72f7ea6Sql 	t = ((*macaddr)<<24) | ((*(macaddr + 1))<<16) |
2973a72f7ea6Sql 	    ((*(macaddr + 2))<<8) | (*(macaddr + 3));
2974a72f7ea6Sql 	RTW_WRITE(regs, RTW_IDR0, ntohl(t));
2975a72f7ea6Sql 	t = ((*(macaddr + 4))<<24) | ((*(macaddr + 5))<<16);
2976a72f7ea6Sql 	RTW_WRITE(regs, RTW_IDR1, ntohl(t));
2977a72f7ea6Sql 	mutex_exit(&rsc->sc_genlock);
2978a72f7ea6Sql 	return (0);
2979a72f7ea6Sql }
2980a72f7ea6Sql 
2981a72f7ea6Sql static int
2982a72f7ea6Sql rtw_m_promisc(void *arg, boolean_t on)
2983a72f7ea6Sql {
2984a72f7ea6Sql 	rtw_softc_t *rsc = (rtw_softc_t *)arg;
2985a72f7ea6Sql 	struct rtw_regs *regs = &rsc->sc_regs;
2986a72f7ea6Sql 
2987a72f7ea6Sql 	mutex_enter(&rsc->sc_genlock);
2988a72f7ea6Sql 
2989a72f7ea6Sql 	if (on)
2990a72f7ea6Sql 		rsc->sc_rcr |= RTW_RCR_PROMIC;
2991a72f7ea6Sql 	else
2992a72f7ea6Sql 		rsc->sc_rcr &= ~RTW_RCR_PROMIC;
2993a72f7ea6Sql 
2994a72f7ea6Sql 	RTW_WRITE(regs, RTW_RCR, rsc->sc_rcr);
2995a72f7ea6Sql 
2996a72f7ea6Sql 	mutex_exit(&rsc->sc_genlock);
2997a72f7ea6Sql 	return (0);
2998a72f7ea6Sql }
2999a72f7ea6Sql 
3000a72f7ea6Sql static int
3001a72f7ea6Sql rtw_m_multicst(void *arg, boolean_t add, const uint8_t *macaddr)
3002a72f7ea6Sql {
3003a72f7ea6Sql 	rtw_softc_t *rsc = (rtw_softc_t *)arg;
3004a72f7ea6Sql 	struct rtw_regs *regs = &rsc->sc_regs;
3005a72f7ea6Sql 	uint32_t t;
3006a72f7ea6Sql 
3007a72f7ea6Sql 	mutex_enter(&rsc->sc_genlock);
3008a72f7ea6Sql 	if (add) {
3009a72f7ea6Sql 		rsc->sc_rcr |= RTW_RCR_AM;
3010a72f7ea6Sql 		t = ((*macaddr)<<24) | ((*(macaddr + 1))<<16) |
3011a72f7ea6Sql 		    ((*(macaddr + 2))<<8) | (*(macaddr + 3));
3012a72f7ea6Sql 		RTW_WRITE(regs, RTW_MAR0, ntohl(t));
3013a72f7ea6Sql 		t = ((*(macaddr + 4))<<24) | ((*(macaddr + 5))<<16);
3014a72f7ea6Sql 		RTW_WRITE(regs, RTW_MAR1, ntohl(t));
3015a72f7ea6Sql 		RTW_WRITE(regs, RTW_RCR, rsc->sc_rcr);
3016a72f7ea6Sql 		RTW_SYNC(regs, RTW_MAR0, RTW_RCR);
3017a72f7ea6Sql 	} else {
3018a72f7ea6Sql 		rsc->sc_rcr &= ~RTW_RCR_AM;
3019a72f7ea6Sql 		RTW_WRITE(regs, RTW_MAR0, 0);
3020a72f7ea6Sql 		RTW_WRITE(regs, RTW_MAR1, 0);
3021a72f7ea6Sql 		RTW_WRITE(regs, RTW_RCR, rsc->sc_rcr);
3022a72f7ea6Sql 		RTW_SYNC(regs, RTW_MAR0, RTW_RCR);
3023a72f7ea6Sql 	}
3024a72f7ea6Sql 	mutex_exit(&rsc->sc_genlock);
3025a72f7ea6Sql 	return (0);
3026a72f7ea6Sql }
3027a72f7ea6Sql 
3028a72f7ea6Sql static void
30299aa73b68SQin Michael Li rtw_m_ioctl(void* arg, queue_t *wq, mblk_t *mp)
3030a72f7ea6Sql {
3031a72f7ea6Sql 	rtw_softc_t *rsc = arg;
30329aa73b68SQin Michael Li 	struct ieee80211com *ic = &rsc->sc_ic;
30339aa73b68SQin Michael Li 	int err;
3034a72f7ea6Sql 
30359aa73b68SQin Michael Li 	err = ieee80211_ioctl(ic, wq, mp);
3036a72f7ea6Sql 	if (err == ENETRESET) {
30379aa73b68SQin Michael Li 		if (ic->ic_des_esslen && (rsc->sc_invalid == 0)) {
30389aa73b68SQin Michael Li 			(void) rtw_init(rsc);
30399aa73b68SQin Michael Li 			(void) ieee80211_new_state(ic,
3040a72f7ea6Sql 			    IEEE80211_S_SCAN, -1);
3041a72f7ea6Sql 		}
3042a72f7ea6Sql 	}
3043a72f7ea6Sql }
3044a72f7ea6Sql 
3045a72f7ea6Sql static int
3046a72f7ea6Sql rtw_m_stat(void *arg, uint_t stat, uint64_t *val)
3047a72f7ea6Sql {
3048a72f7ea6Sql 	rtw_softc_t *rsc = (rtw_softc_t *)arg;
30499aa73b68SQin Michael Li 	ieee80211com_t *ic = &rsc->sc_ic;
30509aa73b68SQin Michael Li 	struct ieee80211_node *in = 0;
30519aa73b68SQin Michael Li 	struct ieee80211_rateset *rs = 0;
3052a72f7ea6Sql 
3053a72f7ea6Sql 	mutex_enter(&rsc->sc_genlock);
3054a72f7ea6Sql 	switch (stat) {
3055a72f7ea6Sql 	case MAC_STAT_IFSPEED:
30569aa73b68SQin Michael Li 		in = ic->ic_bss;
30579aa73b68SQin Michael Li 		rs = &in->in_rates;
30589aa73b68SQin Michael Li 		*val = ((ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) ?
30599aa73b68SQin Michael Li 		    (rs->ir_rates[in->in_txrate] & IEEE80211_RATE_VAL)
30609aa73b68SQin Michael Li 		    : ic->ic_fixed_rate) / 2 * 1000000;
3061a72f7ea6Sql 		break;
3062a72f7ea6Sql 	case MAC_STAT_NOXMTBUF:
3063a72f7ea6Sql 		*val = rsc->sc_noxmtbuf;
3064a72f7ea6Sql 		break;
3065a72f7ea6Sql 	case MAC_STAT_NORCVBUF:
3066a72f7ea6Sql 		*val = rsc->sc_norcvbuf;
3067a72f7ea6Sql 		break;
3068a72f7ea6Sql 	case MAC_STAT_RBYTES:
3069a72f7ea6Sql 		*val = rsc->sc_bytercv64;
3070a72f7ea6Sql 		break;
3071a72f7ea6Sql 	case MAC_STAT_IPACKETS:
3072a72f7ea6Sql 		*val = rsc->sc_pktrcv64;
3073a72f7ea6Sql 		break;
3074a72f7ea6Sql 	case MAC_STAT_OBYTES:
3075a72f7ea6Sql 		*val = rsc->sc_bytexmt64;
3076a72f7ea6Sql 		break;
3077a72f7ea6Sql 	case MAC_STAT_OPACKETS:
3078a72f7ea6Sql 		*val = rsc->sc_pktxmt64;
3079a72f7ea6Sql 		break;
3080a72f7ea6Sql 	case WIFI_STAT_TX_RETRANS:
3081a72f7ea6Sql 		*val = rsc->sc_xmtretry;
3082a72f7ea6Sql 		break;
3083a72f7ea6Sql 	case WIFI_STAT_TX_FRAGS:
3084a72f7ea6Sql 	case WIFI_STAT_MCAST_TX:
3085a72f7ea6Sql 	case WIFI_STAT_RTS_SUCCESS:
3086a72f7ea6Sql 	case WIFI_STAT_RTS_FAILURE:
3087a72f7ea6Sql 	case WIFI_STAT_ACK_FAILURE:
3088a72f7ea6Sql 	case WIFI_STAT_RX_FRAGS:
3089a72f7ea6Sql 	case WIFI_STAT_MCAST_RX:
3090a72f7ea6Sql 	case WIFI_STAT_RX_DUPS:
3091a72f7ea6Sql 		mutex_exit(&rsc->sc_genlock);
3092a72f7ea6Sql 		return (ieee80211_stat(ic, stat, val));
3093a72f7ea6Sql 	default:
3094a72f7ea6Sql 		*val = 0;
3095a72f7ea6Sql 		break;
3096a72f7ea6Sql 	}
3097a72f7ea6Sql 	mutex_exit(&rsc->sc_genlock);
3098a72f7ea6Sql 
3099a72f7ea6Sql 	return (0);
3100a72f7ea6Sql }
3101a72f7ea6Sql 
3102a72f7ea6Sql 
3103a72f7ea6Sql static void
3104a72f7ea6Sql rtw_mutex_destroy(rtw_softc_t *rsc)
3105a72f7ea6Sql {
3106a72f7ea6Sql 	int i;
3107a72f7ea6Sql 
3108a72f7ea6Sql 	mutex_destroy(&rsc->rxbuf_lock);
3109a72f7ea6Sql 	mutex_destroy(&rsc->sc_txlock);
3110a72f7ea6Sql 	for (i = 0; i < RTW_NTXPRI; i++) {
3111a72f7ea6Sql 		mutex_destroy(&rsc->sc_txq[RTW_NTXPRI - 1 - i].txbuf_lock);
3112a72f7ea6Sql 	}
3113a72f7ea6Sql 	mutex_destroy(&rsc->sc_genlock);
3114a72f7ea6Sql }
3115a72f7ea6Sql 
3116a72f7ea6Sql static int
3117a72f7ea6Sql rtw_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd)
3118a72f7ea6Sql {
3119a72f7ea6Sql 	rtw_softc_t *rsc;
3120a72f7ea6Sql 	ieee80211com_t *ic;
3121a72f7ea6Sql 	uint8_t csz;
3122a72f7ea6Sql 	uint32_t i;
3123a72f7ea6Sql 	uint16_t vendor_id, device_id, command;
3124a72f7ea6Sql 	int32_t err;
3125a72f7ea6Sql 	char strbuf[32];
3126a72f7ea6Sql 	wifi_data_t wd = { 0 };
3127a72f7ea6Sql 	mac_register_t *macp;
3128a72f7ea6Sql 	int instance = ddi_get_instance(devinfo);
3129a72f7ea6Sql 
3130a72f7ea6Sql 	switch (cmd) {
3131a72f7ea6Sql 	case DDI_ATTACH:
3132a72f7ea6Sql 		break;
31339aa73b68SQin Michael Li 	case DDI_RESUME:
31349aa73b68SQin Michael Li 		rsc = ddi_get_soft_state(rtw_soft_state_p,
31359aa73b68SQin Michael Li 		    ddi_get_instance(devinfo));
31369aa73b68SQin Michael Li 		ASSERT(rsc != NULL);
31379aa73b68SQin Michael Li 		mutex_enter(&rsc->sc_genlock);
31389aa73b68SQin Michael Li 		rsc->sc_flags &= ~RTW_F_SUSPEND;
31399aa73b68SQin Michael Li 		mutex_exit(&rsc->sc_genlock);
31409aa73b68SQin Michael Li 		if ((rsc->sc_flags & RTW_F_PLUMBED)) {
31419aa73b68SQin Michael Li 			err = rtw_init(rsc);
31429aa73b68SQin Michael Li 			if (err == 0) {
31439aa73b68SQin Michael Li 				mutex_enter(&rsc->sc_genlock);
31449aa73b68SQin Michael Li 				rsc->sc_flags &= ~RTW_F_PLUMBED;
31459aa73b68SQin Michael Li 				mutex_exit(&rsc->sc_genlock);
31469aa73b68SQin Michael Li 			}
31479aa73b68SQin Michael Li 		}
31489aa73b68SQin Michael Li 		return (DDI_SUCCESS);
3149a72f7ea6Sql 	default:
3150a72f7ea6Sql 		return (DDI_FAILURE);
3151a72f7ea6Sql 	}
3152a72f7ea6Sql 
3153a72f7ea6Sql 	if (ddi_soft_state_zalloc(rtw_soft_state_p,
3154a72f7ea6Sql 	    ddi_get_instance(devinfo)) != DDI_SUCCESS) {
3155a72f7ea6Sql 		RTW_DPRINTF(RTW_DEBUG_ATTACH, "rtw: rtw_attach(): "
3156a72f7ea6Sql 		    "Unable to alloc softstate\n");
3157a72f7ea6Sql 		return (DDI_FAILURE);
3158a72f7ea6Sql 	}
3159a72f7ea6Sql 
3160a72f7ea6Sql 	rsc = ddi_get_soft_state(rtw_soft_state_p, ddi_get_instance(devinfo));
3161a72f7ea6Sql 	ic = &rsc->sc_ic;
3162a72f7ea6Sql 	rsc->sc_dev = devinfo;
3163a72f7ea6Sql 
3164a72f7ea6Sql 	err = ddi_regs_map_setup(devinfo, 0, (caddr_t *)&rsc->sc_cfg_base, 0, 0,
3165a72f7ea6Sql 	    &rtw_reg_accattr, &rsc->sc_cfg_handle);
3166a72f7ea6Sql 	if (err != DDI_SUCCESS) {
3167a72f7ea6Sql 		RTW_DPRINTF(RTW_DEBUG_ATTACH, "rtw: rtw_attach(): "
3168a72f7ea6Sql 		    "ddi_regs_map_setup() failed");
3169a72f7ea6Sql 		goto attach_fail0;
3170a72f7ea6Sql 	}
3171a72f7ea6Sql 	csz = ddi_get8(rsc->sc_cfg_handle,
3172a72f7ea6Sql 	    (uint8_t *)(rsc->sc_cfg_base + PCI_CONF_CACHE_LINESZ));
3173a72f7ea6Sql 	if (!csz)
3174a72f7ea6Sql 		csz = 16;
3175a72f7ea6Sql 	rsc->sc_cachelsz = csz << 2;
3176a72f7ea6Sql 	vendor_id = ddi_get16(rsc->sc_cfg_handle,
3177020c4770Sql 	    (uint16_t *)((uintptr_t)rsc->sc_cfg_base + PCI_CONF_VENID));
3178a72f7ea6Sql 	device_id = ddi_get16(rsc->sc_cfg_handle,
3179020c4770Sql 	    (uint16_t *)((uintptr_t)rsc->sc_cfg_base + PCI_CONF_DEVID));
3180a72f7ea6Sql 	RTW_DPRINTF(RTW_DEBUG_ATTACH, "rtw: rtw_attach(): vendor 0x%x, "
3181a72f7ea6Sql 	    "device id 0x%x, cache size %d\n", vendor_id, device_id, csz);
3182a72f7ea6Sql 
3183a72f7ea6Sql 	/*
3184a72f7ea6Sql 	 * Enable response to memory space accesses,
3185a72f7ea6Sql 	 * and enabe bus master.
3186a72f7ea6Sql 	 */
3187a72f7ea6Sql 	command = PCI_COMM_MAE | PCI_COMM_ME;
3188a72f7ea6Sql 	ddi_put16(rsc->sc_cfg_handle,
3189020c4770Sql 	    (uint16_t *)((uintptr_t)rsc->sc_cfg_base + PCI_CONF_COMM), command);
3190a72f7ea6Sql 	RTW_DPRINTF(RTW_DEBUG_ATTACH, "rtw: rtw_attach(): "
3191a72f7ea6Sql 	    "set command reg to 0x%x \n", command);
3192a72f7ea6Sql 
3193a72f7ea6Sql 	ddi_put8(rsc->sc_cfg_handle,
3194a72f7ea6Sql 	    (uint8_t *)(rsc->sc_cfg_base + PCI_CONF_LATENCY_TIMER), 0xa8);
3195a72f7ea6Sql 
3196a72f7ea6Sql 	ddi_regs_map_free(&rsc->sc_cfg_handle);
3197a72f7ea6Sql 
3198a72f7ea6Sql 	err = ddi_regs_map_setup(devinfo, 2, (caddr_t *)&rsc->sc_regs.r_base,
3199a72f7ea6Sql 	    0, 0, &rtw_reg_accattr, &rsc->sc_regs.r_handle);
3200a72f7ea6Sql 	if (err != DDI_SUCCESS) {
3201a72f7ea6Sql 		RTW_DPRINTF(RTW_DEBUG_ATTACH, "rtw: rtw_attach(): "
3202a72f7ea6Sql 		    "ddi_regs_map_setup() failed");
3203a72f7ea6Sql 		goto attach_fail0;
3204a72f7ea6Sql 	}
3205a72f7ea6Sql 	RTW_DPRINTF(RTW_DEBUG_ATTACH, "rtw: r_base=%x, r_handle=%x\n",
3206a72f7ea6Sql 	    rsc->sc_regs.r_base, rsc->sc_regs.r_handle);
3207a72f7ea6Sql 
3208a72f7ea6Sql 	err = rtw_dma_init(devinfo, rsc);
3209a72f7ea6Sql 	if (err != DDI_SUCCESS) {
3210a72f7ea6Sql 		RTW_DPRINTF(RTW_DEBUG_ATTACH, "rtw: rtw_attach(): "
3211a72f7ea6Sql 		    "failed to init dma: %d\n", err);
3212a72f7ea6Sql 		goto attach_fail1;
3213a72f7ea6Sql 	}
3214a72f7ea6Sql 
3215a72f7ea6Sql 	/*
3216a72f7ea6Sql 	 * Stop the transmit and receive processes. First stop DMA,
3217a72f7ea6Sql 	 * then disable receiver and transmitter.
3218a72f7ea6Sql 	 */
3219a72f7ea6Sql 	RTW_WRITE8(&rsc->sc_regs, RTW_TPPOLL, RTW_TPPOLL_SALL);
3220a72f7ea6Sql 	rtw_io_enable(rsc, RTW_CR_RE | RTW_CR_TE, 0);
3221a72f7ea6Sql 
3222a72f7ea6Sql 	/* Reset the chip to a known state. */
3223a72f7ea6Sql 	if (rtw_reset(rsc) != 0) {
3224a72f7ea6Sql 		RTW_DPRINTF(RTW_DEBUG_ATTACH, "rtw: rtw_attach(): "
3225a72f7ea6Sql 		    "failed to reset\n");
3226a72f7ea6Sql 		goto attach_fail2;
3227a72f7ea6Sql 	}
3228a72f7ea6Sql 	rsc->sc_rcr = RTW_READ(&rsc->sc_regs, RTW_RCR);
3229a72f7ea6Sql 
3230a72f7ea6Sql 	if ((rsc->sc_rcr & RTW_RCR_9356SEL) != 0)
3231a72f7ea6Sql 		rsc->sc_flags |= RTW_F_9356SROM;
3232a72f7ea6Sql 
3233a72f7ea6Sql 	if (rtw_srom_read(&rsc->sc_regs, rsc->sc_flags, &rsc->sc_srom,
3234a72f7ea6Sql 	    "rtw") != 0) {
3235a72f7ea6Sql 		RTW_DPRINTF(RTW_DEBUG_ATTACH, "rtw: rtw_attach(): "
3236a72f7ea6Sql 		    "failed to read srom\n");
3237a72f7ea6Sql 		goto attach_fail2;
3238a72f7ea6Sql 	}
3239a72f7ea6Sql 
3240a72f7ea6Sql 	if (rtw_srom_parse(&rsc->sc_srom, &rsc->sc_flags, &rsc->sc_csthr,
3241a72f7ea6Sql 	    &rsc->sc_rfchipid, &rsc->sc_rcr, &rsc->sc_locale,
3242a72f7ea6Sql 	    "rtw") != 0) {
3243a72f7ea6Sql 		RTW_DPRINTF(RTW_DEBUG_ATTACH, "rtw_attach():"
3244a72f7ea6Sql 		    " malformed serial ROM\n");
3245a72f7ea6Sql 		goto attach_fail3;
3246a72f7ea6Sql 	}
3247a72f7ea6Sql 
3248a72f7ea6Sql 	RTW_DPRINTF(RTW_DEBUG_PHY, "rtw: %s PHY\n",
3249a72f7ea6Sql 	    ((rsc->sc_flags & RTW_F_DIGPHY) != 0) ? "digital" : "analog");
3250a72f7ea6Sql 
3251a72f7ea6Sql 
3252a72f7ea6Sql 	rsc->sc_rf = rtw_rf_attach(rsc, rsc->sc_rfchipid,
3253a72f7ea6Sql 	    rsc->sc_flags & RTW_F_DIGPHY);
3254a72f7ea6Sql 
3255a72f7ea6Sql 	if (rsc->sc_rf == NULL) {
3256a72f7ea6Sql 		cmn_err(CE_WARN, "rtw: rtw_attach(): could not attach RF\n");
3257a72f7ea6Sql 		goto attach_fail3;
3258a72f7ea6Sql 	}
3259a72f7ea6Sql 	rsc->sc_phydelay = rtw_check_phydelay(&rsc->sc_regs, rsc->sc_rcr);
3260a72f7ea6Sql 
3261a72f7ea6Sql 	RTW_DPRINTF(RTW_DEBUG_ATTACH,
3262a72f7ea6Sql 	    "rtw: PHY delay %d\n", rsc->sc_phydelay);
3263a72f7ea6Sql 
3264a72f7ea6Sql 	if (rsc->sc_locale == RTW_LOCALE_UNKNOWN)
3265a72f7ea6Sql 		rtw_identify_country(&rsc->sc_regs, &rsc->sc_locale,
3266a72f7ea6Sql 		    "rtw");
3267a72f7ea6Sql 
3268a72f7ea6Sql 	rtw_init_channels(rsc->sc_locale, &rsc->sc_ic.ic_sup_channels,
3269a72f7ea6Sql 	    "rtw");
3270a72f7ea6Sql 
3271a72f7ea6Sql 	rtw_set80211props(ic);
3272a72f7ea6Sql 
3273a72f7ea6Sql 	if (rtw_identify_sta(&rsc->sc_regs, ic->ic_macaddr,
3274a72f7ea6Sql 	    "rtw") != 0)
3275a72f7ea6Sql 		goto attach_fail4;
3276a72f7ea6Sql 
3277a72f7ea6Sql 	ic->ic_xmit = rtw_send;
3278a72f7ea6Sql 	ieee80211_attach(ic);
3279a72f7ea6Sql 
3280a72f7ea6Sql 	rsc->sc_newstate = ic->ic_newstate;
3281a72f7ea6Sql 	ic->ic_newstate = rtw_new_state;
3282a72f7ea6Sql 	ieee80211_media_init(ic);
3283a72f7ea6Sql 	ic->ic_def_txkey = 0;
3284a72f7ea6Sql 
3285a72f7ea6Sql 	if (ddi_get_iblock_cookie(devinfo, 0, &(rsc->sc_iblock))
3286a72f7ea6Sql 	    != DDI_SUCCESS) {
3287a72f7ea6Sql 		RTW_DPRINTF(RTW_DEBUG_ATTACH, "rtw: rtw_attach(): "
3288a72f7ea6Sql 		    "Can not get iblock cookie for INT\n");
3289a72f7ea6Sql 		goto attach_fail5;
3290a72f7ea6Sql 	}
3291a72f7ea6Sql 
3292a72f7ea6Sql 	mutex_init(&rsc->sc_genlock, NULL, MUTEX_DRIVER, rsc->sc_iblock);
3293a72f7ea6Sql 	for (i = 0; i < RTW_NTXPRI; i++) {
3294a72f7ea6Sql 		mutex_init(&rsc->sc_txq[i].txbuf_lock, NULL, MUTEX_DRIVER,
3295a72f7ea6Sql 		    rsc->sc_iblock);
3296a72f7ea6Sql 	}
3297a72f7ea6Sql 	mutex_init(&rsc->rxbuf_lock, NULL, MUTEX_DRIVER, rsc->sc_iblock);
3298a72f7ea6Sql 	mutex_init(&rsc->sc_txlock, NULL, MUTEX_DRIVER, rsc->sc_iblock);
3299a72f7ea6Sql 
3300a72f7ea6Sql 	if (ddi_add_intr(devinfo, 0, &rsc->sc_iblock, NULL, rtw_intr,
3301a72f7ea6Sql 	    (caddr_t)(rsc)) != DDI_SUCCESS) {
3302a72f7ea6Sql 		RTW_DPRINTF(RTW_DEBUG_ATTACH, "rtw: rtw_attach(): "
3303a72f7ea6Sql 		    "Can not add intr for rtw driver\n");
3304a72f7ea6Sql 		goto attach_fail7;
3305a72f7ea6Sql 	}
3306a72f7ea6Sql 
3307a72f7ea6Sql 	/*
3308a72f7ea6Sql 	 * Provide initial settings for the WiFi plugin; whenever this
3309a72f7ea6Sql 	 * information changes, we need to call mac_plugindata_update()
3310a72f7ea6Sql 	 */
3311a72f7ea6Sql 	wd.wd_opmode = ic->ic_opmode;
3312a72f7ea6Sql 	wd.wd_secalloc = WIFI_SEC_NONE;
3313a72f7ea6Sql 	IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid);
3314a72f7ea6Sql 
3315a72f7ea6Sql 	if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
3316a72f7ea6Sql 		RTW_DPRINTF(RTW_DEBUG_ATTACH, "rtw: rtw_attach(): "
3317a72f7ea6Sql 		    "MAC version mismatch\n");
3318a72f7ea6Sql 		goto attach_fail8;
3319a72f7ea6Sql 	}
3320a72f7ea6Sql 
3321a72f7ea6Sql 	macp->m_type_ident	= MAC_PLUGIN_IDENT_WIFI;
3322a72f7ea6Sql 	macp->m_driver		= rsc;
3323a72f7ea6Sql 	macp->m_dip		= devinfo;
3324a72f7ea6Sql 	macp->m_src_addr	= ic->ic_macaddr;
3325a72f7ea6Sql 	macp->m_callbacks	= &rtw_m_callbacks;
3326a72f7ea6Sql 	macp->m_min_sdu		= 0;
3327a72f7ea6Sql 	macp->m_max_sdu		= IEEE80211_MTU;
3328a72f7ea6Sql 	macp->m_pdata		= &wd;
3329a72f7ea6Sql 	macp->m_pdata_size	= sizeof (wd);
3330a72f7ea6Sql 
3331a72f7ea6Sql 	err = mac_register(macp, &ic->ic_mach);
3332a72f7ea6Sql 	mac_free(macp);
3333a72f7ea6Sql 	if (err != 0) {
3334a72f7ea6Sql 		RTW_DPRINTF(RTW_DEBUG_ATTACH, "rtw: rtw_attach(): "
3335a72f7ea6Sql 		    "mac_register err %x\n", err);
3336a72f7ea6Sql 		goto attach_fail8;
3337a72f7ea6Sql 	}
3338a72f7ea6Sql 
3339a72f7ea6Sql 	/* Create minor node of type DDI_NT_NET_WIFI */
3340a72f7ea6Sql 	(void) snprintf(strbuf, sizeof (strbuf), "%s%d",
3341a72f7ea6Sql 	    "rtw", instance);
3342a72f7ea6Sql 	err = ddi_create_minor_node(devinfo, strbuf, S_IFCHR,
3343a72f7ea6Sql 	    instance + 1, DDI_NT_NET_WIFI, 0);
3344a72f7ea6Sql 	if (err != DDI_SUCCESS) {
3345a72f7ea6Sql 		RTW_DPRINTF(RTW_DEBUG_ATTACH, "WARN: rtw: rtw_attach(): "
3346a72f7ea6Sql 		    "Create minor node failed - %d\n", err);
3347a72f7ea6Sql 		goto attach_fail9;
3348a72f7ea6Sql 	}
3349a72f7ea6Sql 	mac_link_update(ic->ic_mach, LINK_STATE_DOWN);
3350a72f7ea6Sql 	rsc->sc_flags |= RTW_F_ATTACHED;
3351a72f7ea6Sql 	rsc->sc_need_reschedule = 0;
3352a72f7ea6Sql 	rsc->sc_invalid = 1;
3353a72f7ea6Sql 	return (DDI_SUCCESS);
3354a72f7ea6Sql attach_fail9:
33559aa73b68SQin Michael Li 	(void) mac_disable(ic->ic_mach);
3356a72f7ea6Sql 	(void) mac_unregister(ic->ic_mach);
3357a72f7ea6Sql attach_fail8:
3358a72f7ea6Sql 	ddi_remove_intr(devinfo, 0, rsc->sc_iblock);
3359a72f7ea6Sql attach_fail7:
3360a72f7ea6Sql attach_fail6:
3361a72f7ea6Sql 	rtw_mutex_destroy(rsc);
3362a72f7ea6Sql attach_fail5:
3363a72f7ea6Sql 	ieee80211_detach(ic);
3364a72f7ea6Sql attach_fail4:
3365a72f7ea6Sql 	rtw_rf_destroy(rsc->sc_rf);
3366a72f7ea6Sql attach_fail3:
3367a72f7ea6Sql 	rtw_srom_free(&rsc->sc_srom);
3368a72f7ea6Sql attach_fail2:
3369a72f7ea6Sql 	rtw_dma_free(rsc);
3370a72f7ea6Sql attach_fail1:
3371a72f7ea6Sql 	ddi_regs_map_free(&rsc->sc_regs.r_handle);
3372a72f7ea6Sql attach_fail0:
3373a72f7ea6Sql 	ddi_soft_state_free(rtw_soft_state_p, ddi_get_instance(devinfo));
3374a72f7ea6Sql 	return (DDI_FAILURE);
3375a72f7ea6Sql }
3376a72f7ea6Sql 
3377a72f7ea6Sql static int32_t
3378a72f7ea6Sql rtw_detach(dev_info_t *devinfo, ddi_detach_cmd_t cmd)
3379a72f7ea6Sql {
3380a72f7ea6Sql 	rtw_softc_t *rsc;
3381a72f7ea6Sql 
3382a72f7ea6Sql 	rsc = ddi_get_soft_state(rtw_soft_state_p, ddi_get_instance(devinfo));
3383a72f7ea6Sql 	ASSERT(rsc != NULL);
3384a72f7ea6Sql 
3385a72f7ea6Sql 	switch (cmd) {
3386a72f7ea6Sql 	case DDI_DETACH:
3387a72f7ea6Sql 		break;
33889aa73b68SQin Michael Li 	case DDI_SUSPEND:
33899aa73b68SQin Michael Li 		ieee80211_new_state(&rsc->sc_ic, IEEE80211_S_INIT, -1);
33909aa73b68SQin Michael Li 		mutex_enter(&rsc->sc_genlock);
33919aa73b68SQin Michael Li 		rsc->sc_flags |= RTW_F_SUSPEND;
33929aa73b68SQin Michael Li 		mutex_exit(&rsc->sc_genlock);
33939aa73b68SQin Michael Li 		if (rsc->sc_invalid == 0) {
33949aa73b68SQin Michael Li 			rtw_stop(rsc);
33959aa73b68SQin Michael Li 			mutex_enter(&rsc->sc_genlock);
33969aa73b68SQin Michael Li 			rsc->sc_flags |= RTW_F_PLUMBED;
33979aa73b68SQin Michael Li 			mutex_exit(&rsc->sc_genlock);
33989aa73b68SQin Michael Li 		}
33999aa73b68SQin Michael Li 		return (DDI_SUCCESS);
3400a72f7ea6Sql 	default:
3401a72f7ea6Sql 		return (DDI_FAILURE);
3402a72f7ea6Sql 	}
3403a72f7ea6Sql 	if (!(rsc->sc_flags & RTW_F_ATTACHED))
3404a72f7ea6Sql 		return (DDI_FAILURE);
3405a72f7ea6Sql 
340642516a0cSxinghua wen - Sun Microsystems - Beijing China 	if (mac_disable(rsc->sc_ic.ic_mach) != 0)
340742516a0cSxinghua wen - Sun Microsystems - Beijing China 		return (DDI_FAILURE);
340842516a0cSxinghua wen - Sun Microsystems - Beijing China 
3409a72f7ea6Sql 	/* free intterrupt resources */
3410a72f7ea6Sql 	ddi_remove_intr(devinfo, 0, rsc->sc_iblock);
3411a72f7ea6Sql 
3412a72f7ea6Sql 	rtw_mutex_destroy(rsc);
3413a72f7ea6Sql 	ieee80211_detach((ieee80211com_t *)rsc);
3414a72f7ea6Sql 	/*
3415a72f7ea6Sql 	 * Unregister from the MAC layer subsystem
3416a72f7ea6Sql 	 */
3417a72f7ea6Sql 	(void) mac_unregister(rsc->sc_ic.ic_mach);
3418a72f7ea6Sql 
3419a72f7ea6Sql 	rtw_rf_destroy(rsc->sc_rf);
3420a72f7ea6Sql 	rtw_srom_free(&rsc->sc_srom);
3421a72f7ea6Sql 	rtw_dma_free(rsc);
3422a72f7ea6Sql 	ddi_remove_minor_node(devinfo, NULL);
3423a72f7ea6Sql 	ddi_regs_map_free(&rsc->sc_regs.r_handle);
3424a72f7ea6Sql 
3425a72f7ea6Sql 	ddi_soft_state_free(rtw_soft_state_p, ddi_get_instance(devinfo));
3426a72f7ea6Sql 
3427a72f7ea6Sql 	return (DDI_SUCCESS);
3428a72f7ea6Sql }
3429