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