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