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