1*9999SWang.Lin@Sun.COM /* 2*9999SWang.Lin@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 3*9999SWang.Lin@Sun.COM * Use is subject to license terms. 4*9999SWang.Lin@Sun.COM */ 5*9999SWang.Lin@Sun.COM 6*9999SWang.Lin@Sun.COM /* 7*9999SWang.Lin@Sun.COM * Copyright (c) 2008 Atheros Communications Inc. 8*9999SWang.Lin@Sun.COM * 9*9999SWang.Lin@Sun.COM * Permission to use, copy, modify, and/or distribute this software for any 10*9999SWang.Lin@Sun.COM * purpose with or without fee is hereby granted, provided that the above 11*9999SWang.Lin@Sun.COM * copyright notice and this permission notice appear in all copies. 12*9999SWang.Lin@Sun.COM * 13*9999SWang.Lin@Sun.COM * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 14*9999SWang.Lin@Sun.COM * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15*9999SWang.Lin@Sun.COM * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 16*9999SWang.Lin@Sun.COM * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17*9999SWang.Lin@Sun.COM * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18*9999SWang.Lin@Sun.COM * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 19*9999SWang.Lin@Sun.COM * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20*9999SWang.Lin@Sun.COM */ 21*9999SWang.Lin@Sun.COM 22*9999SWang.Lin@Sun.COM #include <sys/param.h> 23*9999SWang.Lin@Sun.COM #include <sys/types.h> 24*9999SWang.Lin@Sun.COM #include <sys/signal.h> 25*9999SWang.Lin@Sun.COM #include <sys/stream.h> 26*9999SWang.Lin@Sun.COM #include <sys/termio.h> 27*9999SWang.Lin@Sun.COM #include <sys/errno.h> 28*9999SWang.Lin@Sun.COM #include <sys/file.h> 29*9999SWang.Lin@Sun.COM #include <sys/cmn_err.h> 30*9999SWang.Lin@Sun.COM #include <sys/stropts.h> 31*9999SWang.Lin@Sun.COM #include <sys/strsubr.h> 32*9999SWang.Lin@Sun.COM #include <sys/strtty.h> 33*9999SWang.Lin@Sun.COM #include <sys/kbio.h> 34*9999SWang.Lin@Sun.COM #include <sys/cred.h> 35*9999SWang.Lin@Sun.COM #include <sys/stat.h> 36*9999SWang.Lin@Sun.COM #include <sys/consdev.h> 37*9999SWang.Lin@Sun.COM #include <sys/kmem.h> 38*9999SWang.Lin@Sun.COM #include <sys/modctl.h> 39*9999SWang.Lin@Sun.COM #include <sys/ddi.h> 40*9999SWang.Lin@Sun.COM #include <sys/sunddi.h> 41*9999SWang.Lin@Sun.COM #include <sys/pci.h> 42*9999SWang.Lin@Sun.COM #include <sys/errno.h> 43*9999SWang.Lin@Sun.COM #include <sys/mac_provider.h> 44*9999SWang.Lin@Sun.COM #include <sys/dlpi.h> 45*9999SWang.Lin@Sun.COM #include <sys/ethernet.h> 46*9999SWang.Lin@Sun.COM #include <sys/list.h> 47*9999SWang.Lin@Sun.COM #include <sys/byteorder.h> 48*9999SWang.Lin@Sun.COM #include <sys/strsun.h> 49*9999SWang.Lin@Sun.COM #include <sys/policy.h> 50*9999SWang.Lin@Sun.COM #include <inet/common.h> 51*9999SWang.Lin@Sun.COM #include <inet/nd.h> 52*9999SWang.Lin@Sun.COM #include <inet/mi.h> 53*9999SWang.Lin@Sun.COM #include <inet/wifi_ioctl.h> 54*9999SWang.Lin@Sun.COM #include <sys/mac_wifi.h> 55*9999SWang.Lin@Sun.COM 56*9999SWang.Lin@Sun.COM #include "arn_ath9k.h" 57*9999SWang.Lin@Sun.COM #include "arn_core.h" 58*9999SWang.Lin@Sun.COM #include "arn_reg.h" 59*9999SWang.Lin@Sun.COM #include "arn_hw.h" 60*9999SWang.Lin@Sun.COM 61*9999SWang.Lin@Sun.COM #define ARN_MAX_RSSI 63 /* max rssi */ 62*9999SWang.Lin@Sun.COM 63*9999SWang.Lin@Sun.COM /* 64*9999SWang.Lin@Sun.COM * PIO access attributes for registers 65*9999SWang.Lin@Sun.COM */ 66*9999SWang.Lin@Sun.COM static ddi_device_acc_attr_t arn_reg_accattr = { 67*9999SWang.Lin@Sun.COM DDI_DEVICE_ATTR_V0, 68*9999SWang.Lin@Sun.COM DDI_STRUCTURE_LE_ACC, 69*9999SWang.Lin@Sun.COM DDI_STRICTORDER_ACC 70*9999SWang.Lin@Sun.COM }; 71*9999SWang.Lin@Sun.COM 72*9999SWang.Lin@Sun.COM /* 73*9999SWang.Lin@Sun.COM * DMA access attributes for descriptors: NOT to be byte swapped. 74*9999SWang.Lin@Sun.COM */ 75*9999SWang.Lin@Sun.COM static ddi_device_acc_attr_t arn_desc_accattr = { 76*9999SWang.Lin@Sun.COM DDI_DEVICE_ATTR_V0, 77*9999SWang.Lin@Sun.COM DDI_STRUCTURE_LE_ACC, 78*9999SWang.Lin@Sun.COM DDI_STRICTORDER_ACC 79*9999SWang.Lin@Sun.COM }; 80*9999SWang.Lin@Sun.COM 81*9999SWang.Lin@Sun.COM /* 82*9999SWang.Lin@Sun.COM * Describes the chip's DMA engine 83*9999SWang.Lin@Sun.COM */ 84*9999SWang.Lin@Sun.COM static ddi_dma_attr_t arn_dma_attr = { 85*9999SWang.Lin@Sun.COM DMA_ATTR_V0, /* version number */ 86*9999SWang.Lin@Sun.COM 0, /* low address */ 87*9999SWang.Lin@Sun.COM 0xffffffffU, /* high address */ 88*9999SWang.Lin@Sun.COM 0x3ffffU, /* counter register max */ 89*9999SWang.Lin@Sun.COM 1, /* alignment */ 90*9999SWang.Lin@Sun.COM 0xFFF, /* burst sizes */ 91*9999SWang.Lin@Sun.COM 1, /* minimum transfer size */ 92*9999SWang.Lin@Sun.COM 0x3ffffU, /* max transfer size */ 93*9999SWang.Lin@Sun.COM 0xffffffffU, /* address register max */ 94*9999SWang.Lin@Sun.COM 1, /* no scatter-gather */ 95*9999SWang.Lin@Sun.COM 1, /* granularity of device */ 96*9999SWang.Lin@Sun.COM 0, /* DMA flags */ 97*9999SWang.Lin@Sun.COM }; 98*9999SWang.Lin@Sun.COM 99*9999SWang.Lin@Sun.COM static ddi_dma_attr_t arn_desc_dma_attr = { 100*9999SWang.Lin@Sun.COM DMA_ATTR_V0, /* version number */ 101*9999SWang.Lin@Sun.COM 0, /* low address */ 102*9999SWang.Lin@Sun.COM 0xffffffffU, /* high address */ 103*9999SWang.Lin@Sun.COM 0xffffffffU, /* counter register max */ 104*9999SWang.Lin@Sun.COM 0x1000, /* alignment */ 105*9999SWang.Lin@Sun.COM 0xFFF, /* burst sizes */ 106*9999SWang.Lin@Sun.COM 1, /* minimum transfer size */ 107*9999SWang.Lin@Sun.COM 0xffffffffU, /* max transfer size */ 108*9999SWang.Lin@Sun.COM 0xffffffffU, /* address register max */ 109*9999SWang.Lin@Sun.COM 1, /* no scatter-gather */ 110*9999SWang.Lin@Sun.COM 1, /* granularity of device */ 111*9999SWang.Lin@Sun.COM 0, /* DMA flags */ 112*9999SWang.Lin@Sun.COM }; 113*9999SWang.Lin@Sun.COM 114*9999SWang.Lin@Sun.COM #define ATH_DEF_CACHE_BYTES 32 /* default cache line size */ 115*9999SWang.Lin@Sun.COM 116*9999SWang.Lin@Sun.COM static kmutex_t arn_loglock; 117*9999SWang.Lin@Sun.COM static void *arn_soft_state_p = NULL; 118*9999SWang.Lin@Sun.COM /* scan interval, ms? */ 119*9999SWang.Lin@Sun.COM static int arn_dwelltime = 200; /* 150 */ 120*9999SWang.Lin@Sun.COM 121*9999SWang.Lin@Sun.COM static int arn_m_stat(void *, uint_t, uint64_t *); 122*9999SWang.Lin@Sun.COM static int arn_m_start(void *); 123*9999SWang.Lin@Sun.COM static void arn_m_stop(void *); 124*9999SWang.Lin@Sun.COM static int arn_m_promisc(void *, boolean_t); 125*9999SWang.Lin@Sun.COM static int arn_m_multicst(void *, boolean_t, const uint8_t *); 126*9999SWang.Lin@Sun.COM static int arn_m_unicst(void *, const uint8_t *); 127*9999SWang.Lin@Sun.COM static mblk_t *arn_m_tx(void *, mblk_t *); 128*9999SWang.Lin@Sun.COM static void arn_m_ioctl(void *, queue_t *, mblk_t *); 129*9999SWang.Lin@Sun.COM static int arn_m_setprop(void *, const char *, mac_prop_id_t, 130*9999SWang.Lin@Sun.COM uint_t, const void *); 131*9999SWang.Lin@Sun.COM static int arn_m_getprop(void *, const char *, mac_prop_id_t, 132*9999SWang.Lin@Sun.COM uint_t, uint_t, void *, uint_t *); 133*9999SWang.Lin@Sun.COM 134*9999SWang.Lin@Sun.COM /* MAC Callcack Functions */ 135*9999SWang.Lin@Sun.COM static mac_callbacks_t arn_m_callbacks = { 136*9999SWang.Lin@Sun.COM MC_IOCTL | MC_SETPROP | MC_GETPROP, 137*9999SWang.Lin@Sun.COM arn_m_stat, 138*9999SWang.Lin@Sun.COM arn_m_start, 139*9999SWang.Lin@Sun.COM arn_m_stop, 140*9999SWang.Lin@Sun.COM arn_m_promisc, 141*9999SWang.Lin@Sun.COM arn_m_multicst, 142*9999SWang.Lin@Sun.COM arn_m_unicst, 143*9999SWang.Lin@Sun.COM arn_m_tx, 144*9999SWang.Lin@Sun.COM arn_m_ioctl, 145*9999SWang.Lin@Sun.COM NULL, 146*9999SWang.Lin@Sun.COM NULL, 147*9999SWang.Lin@Sun.COM NULL, 148*9999SWang.Lin@Sun.COM arn_m_setprop, 149*9999SWang.Lin@Sun.COM arn_m_getprop 150*9999SWang.Lin@Sun.COM }; 151*9999SWang.Lin@Sun.COM 152*9999SWang.Lin@Sun.COM /* 153*9999SWang.Lin@Sun.COM * ARN_DBG_HW 154*9999SWang.Lin@Sun.COM * ARN_DBG_REG_IO 155*9999SWang.Lin@Sun.COM * ARN_DBG_QUEUE 156*9999SWang.Lin@Sun.COM * ARN_DBG_EEPROM 157*9999SWang.Lin@Sun.COM * ARN_DBG_XMIT 158*9999SWang.Lin@Sun.COM * ARN_DBG_RECV 159*9999SWang.Lin@Sun.COM * ARN_DBG_CALIBRATE 160*9999SWang.Lin@Sun.COM * ARN_DBG_CHANNEL 161*9999SWang.Lin@Sun.COM * ARN_DBG_INTERRUPT 162*9999SWang.Lin@Sun.COM * ARN_DBG_REGULATORY 163*9999SWang.Lin@Sun.COM * ARN_DBG_ANI 164*9999SWang.Lin@Sun.COM * ARN_DBG_POWER_MGMT 165*9999SWang.Lin@Sun.COM * ARN_DBG_KEYCACHE 166*9999SWang.Lin@Sun.COM * ARN_DBG_BEACON 167*9999SWang.Lin@Sun.COM * ARN_DBG_RATE 168*9999SWang.Lin@Sun.COM * ARN_DBG_INIT 169*9999SWang.Lin@Sun.COM * ARN_DBG_ATTACH 170*9999SWang.Lin@Sun.COM * ARN_DBG_DEATCH 171*9999SWang.Lin@Sun.COM * ARN_DBG_AGGR 172*9999SWang.Lin@Sun.COM * ARN_DBG_RESET 173*9999SWang.Lin@Sun.COM * ARN_DBG_FATAL 174*9999SWang.Lin@Sun.COM * ARN_DBG_ANY 175*9999SWang.Lin@Sun.COM * ARN_DBG_ALL 176*9999SWang.Lin@Sun.COM */ 177*9999SWang.Lin@Sun.COM uint32_t arn_dbg_mask = 0; 178*9999SWang.Lin@Sun.COM 179*9999SWang.Lin@Sun.COM /* 180*9999SWang.Lin@Sun.COM * Exception/warning cases not leading to panic. 181*9999SWang.Lin@Sun.COM */ 182*9999SWang.Lin@Sun.COM void 183*9999SWang.Lin@Sun.COM arn_problem(const int8_t *fmt, ...) 184*9999SWang.Lin@Sun.COM { 185*9999SWang.Lin@Sun.COM va_list args; 186*9999SWang.Lin@Sun.COM 187*9999SWang.Lin@Sun.COM mutex_enter(&arn_loglock); 188*9999SWang.Lin@Sun.COM 189*9999SWang.Lin@Sun.COM va_start(args, fmt); 190*9999SWang.Lin@Sun.COM vcmn_err(CE_WARN, fmt, args); 191*9999SWang.Lin@Sun.COM va_end(args); 192*9999SWang.Lin@Sun.COM 193*9999SWang.Lin@Sun.COM mutex_exit(&arn_loglock); 194*9999SWang.Lin@Sun.COM } 195*9999SWang.Lin@Sun.COM 196*9999SWang.Lin@Sun.COM /* 197*9999SWang.Lin@Sun.COM * Normal log information independent of debug. 198*9999SWang.Lin@Sun.COM */ 199*9999SWang.Lin@Sun.COM void 200*9999SWang.Lin@Sun.COM arn_log(const int8_t *fmt, ...) 201*9999SWang.Lin@Sun.COM { 202*9999SWang.Lin@Sun.COM va_list args; 203*9999SWang.Lin@Sun.COM 204*9999SWang.Lin@Sun.COM mutex_enter(&arn_loglock); 205*9999SWang.Lin@Sun.COM 206*9999SWang.Lin@Sun.COM va_start(args, fmt); 207*9999SWang.Lin@Sun.COM vcmn_err(CE_CONT, fmt, args); 208*9999SWang.Lin@Sun.COM va_end(args); 209*9999SWang.Lin@Sun.COM 210*9999SWang.Lin@Sun.COM mutex_exit(&arn_loglock); 211*9999SWang.Lin@Sun.COM } 212*9999SWang.Lin@Sun.COM 213*9999SWang.Lin@Sun.COM void 214*9999SWang.Lin@Sun.COM arn_dbg(uint32_t dbg_flags, const int8_t *fmt, ...) 215*9999SWang.Lin@Sun.COM { 216*9999SWang.Lin@Sun.COM va_list args; 217*9999SWang.Lin@Sun.COM 218*9999SWang.Lin@Sun.COM if (dbg_flags & arn_dbg_mask) { 219*9999SWang.Lin@Sun.COM mutex_enter(&arn_loglock); 220*9999SWang.Lin@Sun.COM va_start(args, fmt); 221*9999SWang.Lin@Sun.COM vcmn_err(CE_CONT, fmt, args); 222*9999SWang.Lin@Sun.COM va_end(args); 223*9999SWang.Lin@Sun.COM mutex_exit(&arn_loglock); 224*9999SWang.Lin@Sun.COM } 225*9999SWang.Lin@Sun.COM } 226*9999SWang.Lin@Sun.COM 227*9999SWang.Lin@Sun.COM /* 228*9999SWang.Lin@Sun.COM * Read and write, they both share the same lock. We do this to serialize 229*9999SWang.Lin@Sun.COM * reads and writes on Atheros 802.11n PCI devices only. This is required 230*9999SWang.Lin@Sun.COM * as the FIFO on these devices can only accept sanely 2 requests. After 231*9999SWang.Lin@Sun.COM * that the device goes bananas. Serializing the reads/writes prevents this 232*9999SWang.Lin@Sun.COM * from happening. 233*9999SWang.Lin@Sun.COM */ 234*9999SWang.Lin@Sun.COM void 235*9999SWang.Lin@Sun.COM arn_iowrite32(struct ath_hal *ah, uint32_t reg_offset, uint32_t val) 236*9999SWang.Lin@Sun.COM { 237*9999SWang.Lin@Sun.COM struct arn_softc *sc = ah->ah_sc; 238*9999SWang.Lin@Sun.COM if (ah->ah_config.serialize_regmode == SER_REG_MODE_ON) { 239*9999SWang.Lin@Sun.COM mutex_enter(&sc->sc_serial_rw); 240*9999SWang.Lin@Sun.COM ddi_put32(sc->sc_io_handle, 241*9999SWang.Lin@Sun.COM (uint32_t *)((uintptr_t)(sc->mem) + (reg_offset)), val); 242*9999SWang.Lin@Sun.COM mutex_exit(&sc->sc_serial_rw); 243*9999SWang.Lin@Sun.COM } else { 244*9999SWang.Lin@Sun.COM ddi_put32(sc->sc_io_handle, 245*9999SWang.Lin@Sun.COM (uint32_t *)((uintptr_t)(sc->mem) + (reg_offset)), val); 246*9999SWang.Lin@Sun.COM } 247*9999SWang.Lin@Sun.COM } 248*9999SWang.Lin@Sun.COM 249*9999SWang.Lin@Sun.COM unsigned int 250*9999SWang.Lin@Sun.COM arn_ioread32(struct ath_hal *ah, uint32_t reg_offset) 251*9999SWang.Lin@Sun.COM { 252*9999SWang.Lin@Sun.COM uint32_t val; 253*9999SWang.Lin@Sun.COM struct arn_softc *sc = ah->ah_sc; 254*9999SWang.Lin@Sun.COM if (ah->ah_config.serialize_regmode == SER_REG_MODE_ON) { 255*9999SWang.Lin@Sun.COM mutex_enter(&sc->sc_serial_rw); 256*9999SWang.Lin@Sun.COM val = ddi_get32(sc->sc_io_handle, 257*9999SWang.Lin@Sun.COM (uint32_t *)((uintptr_t)(sc->mem) + (reg_offset))); 258*9999SWang.Lin@Sun.COM mutex_exit(&sc->sc_serial_rw); 259*9999SWang.Lin@Sun.COM } else { 260*9999SWang.Lin@Sun.COM val = ddi_get32(sc->sc_io_handle, 261*9999SWang.Lin@Sun.COM (uint32_t *)((uintptr_t)(sc->mem) + (reg_offset))); 262*9999SWang.Lin@Sun.COM } 263*9999SWang.Lin@Sun.COM 264*9999SWang.Lin@Sun.COM return (val); 265*9999SWang.Lin@Sun.COM } 266*9999SWang.Lin@Sun.COM 267*9999SWang.Lin@Sun.COM void 268*9999SWang.Lin@Sun.COM arn_rx_buf_link(struct arn_softc *sc, struct ath_buf *bf) 269*9999SWang.Lin@Sun.COM { 270*9999SWang.Lin@Sun.COM struct ath_desc *ds; 271*9999SWang.Lin@Sun.COM 272*9999SWang.Lin@Sun.COM ds = bf->bf_desc; 273*9999SWang.Lin@Sun.COM ds->ds_link = bf->bf_daddr; 274*9999SWang.Lin@Sun.COM ds->ds_data = bf->bf_dma.cookie.dmac_address; 275*9999SWang.Lin@Sun.COM /* virtual addr of the beginning of the buffer. */ 276*9999SWang.Lin@Sun.COM ds->ds_vdata = bf->bf_dma.mem_va; 277*9999SWang.Lin@Sun.COM 278*9999SWang.Lin@Sun.COM /* 279*9999SWang.Lin@Sun.COM * setup rx descriptors. The bf_dma.alength here tells the H/W 280*9999SWang.Lin@Sun.COM * how much data it can DMA to us and that we are prepared 281*9999SWang.Lin@Sun.COM * to process 282*9999SWang.Lin@Sun.COM */ 283*9999SWang.Lin@Sun.COM (void) ath9k_hw_setuprxdesc(sc->sc_ah, ds, 284*9999SWang.Lin@Sun.COM bf->bf_dma.alength, /* buffer size */ 285*9999SWang.Lin@Sun.COM 0); 286*9999SWang.Lin@Sun.COM 287*9999SWang.Lin@Sun.COM if (sc->sc_rxlink != NULL) 288*9999SWang.Lin@Sun.COM *sc->sc_rxlink = bf->bf_daddr; 289*9999SWang.Lin@Sun.COM sc->sc_rxlink = &ds->ds_link; 290*9999SWang.Lin@Sun.COM } 291*9999SWang.Lin@Sun.COM 292*9999SWang.Lin@Sun.COM /* 293*9999SWang.Lin@Sun.COM * Allocate an area of memory and a DMA handle for accessing it 294*9999SWang.Lin@Sun.COM */ 295*9999SWang.Lin@Sun.COM static int 296*9999SWang.Lin@Sun.COM arn_alloc_dma_mem(dev_info_t *devinfo, ddi_dma_attr_t *dma_attr, size_t memsize, 297*9999SWang.Lin@Sun.COM ddi_device_acc_attr_t *attr_p, uint_t alloc_flags, 298*9999SWang.Lin@Sun.COM uint_t bind_flags, dma_area_t *dma_p) 299*9999SWang.Lin@Sun.COM { 300*9999SWang.Lin@Sun.COM int err; 301*9999SWang.Lin@Sun.COM 302*9999SWang.Lin@Sun.COM /* 303*9999SWang.Lin@Sun.COM * Allocate handle 304*9999SWang.Lin@Sun.COM */ 305*9999SWang.Lin@Sun.COM err = ddi_dma_alloc_handle(devinfo, dma_attr, 306*9999SWang.Lin@Sun.COM DDI_DMA_SLEEP, NULL, &dma_p->dma_hdl); 307*9999SWang.Lin@Sun.COM if (err != DDI_SUCCESS) 308*9999SWang.Lin@Sun.COM return (DDI_FAILURE); 309*9999SWang.Lin@Sun.COM 310*9999SWang.Lin@Sun.COM /* 311*9999SWang.Lin@Sun.COM * Allocate memory 312*9999SWang.Lin@Sun.COM */ 313*9999SWang.Lin@Sun.COM err = ddi_dma_mem_alloc(dma_p->dma_hdl, memsize, attr_p, 314*9999SWang.Lin@Sun.COM alloc_flags, DDI_DMA_SLEEP, NULL, &dma_p->mem_va, 315*9999SWang.Lin@Sun.COM &dma_p->alength, &dma_p->acc_hdl); 316*9999SWang.Lin@Sun.COM if (err != DDI_SUCCESS) 317*9999SWang.Lin@Sun.COM return (DDI_FAILURE); 318*9999SWang.Lin@Sun.COM 319*9999SWang.Lin@Sun.COM /* 320*9999SWang.Lin@Sun.COM * Bind the two together 321*9999SWang.Lin@Sun.COM */ 322*9999SWang.Lin@Sun.COM err = ddi_dma_addr_bind_handle(dma_p->dma_hdl, NULL, 323*9999SWang.Lin@Sun.COM dma_p->mem_va, dma_p->alength, bind_flags, 324*9999SWang.Lin@Sun.COM DDI_DMA_SLEEP, NULL, &dma_p->cookie, &dma_p->ncookies); 325*9999SWang.Lin@Sun.COM if (err != DDI_DMA_MAPPED) 326*9999SWang.Lin@Sun.COM return (DDI_FAILURE); 327*9999SWang.Lin@Sun.COM 328*9999SWang.Lin@Sun.COM dma_p->nslots = ~0U; 329*9999SWang.Lin@Sun.COM dma_p->size = ~0U; 330*9999SWang.Lin@Sun.COM dma_p->token = ~0U; 331*9999SWang.Lin@Sun.COM dma_p->offset = 0; 332*9999SWang.Lin@Sun.COM return (DDI_SUCCESS); 333*9999SWang.Lin@Sun.COM } 334*9999SWang.Lin@Sun.COM 335*9999SWang.Lin@Sun.COM /* 336*9999SWang.Lin@Sun.COM * Free one allocated area of DMAable memory 337*9999SWang.Lin@Sun.COM */ 338*9999SWang.Lin@Sun.COM static void 339*9999SWang.Lin@Sun.COM arn_free_dma_mem(dma_area_t *dma_p) 340*9999SWang.Lin@Sun.COM { 341*9999SWang.Lin@Sun.COM if (dma_p->dma_hdl != NULL) { 342*9999SWang.Lin@Sun.COM (void) ddi_dma_unbind_handle(dma_p->dma_hdl); 343*9999SWang.Lin@Sun.COM if (dma_p->acc_hdl != NULL) { 344*9999SWang.Lin@Sun.COM ddi_dma_mem_free(&dma_p->acc_hdl); 345*9999SWang.Lin@Sun.COM dma_p->acc_hdl = NULL; 346*9999SWang.Lin@Sun.COM } 347*9999SWang.Lin@Sun.COM ddi_dma_free_handle(&dma_p->dma_hdl); 348*9999SWang.Lin@Sun.COM dma_p->ncookies = 0; 349*9999SWang.Lin@Sun.COM dma_p->dma_hdl = NULL; 350*9999SWang.Lin@Sun.COM } 351*9999SWang.Lin@Sun.COM } 352*9999SWang.Lin@Sun.COM 353*9999SWang.Lin@Sun.COM /* 354*9999SWang.Lin@Sun.COM * Initialize tx, rx. or beacon buffer list. Allocate DMA memory for 355*9999SWang.Lin@Sun.COM * each buffer. 356*9999SWang.Lin@Sun.COM */ 357*9999SWang.Lin@Sun.COM static int 358*9999SWang.Lin@Sun.COM arn_buflist_setup(dev_info_t *devinfo, struct arn_softc *sc, list_t *bflist, 359*9999SWang.Lin@Sun.COM struct ath_buf **pbf, struct ath_desc **pds, int nbuf, uint_t dmabflags) 360*9999SWang.Lin@Sun.COM { 361*9999SWang.Lin@Sun.COM int i, err; 362*9999SWang.Lin@Sun.COM struct ath_buf *bf = *pbf; 363*9999SWang.Lin@Sun.COM struct ath_desc *ds = *pds; 364*9999SWang.Lin@Sun.COM 365*9999SWang.Lin@Sun.COM list_create(bflist, sizeof (struct ath_buf), 366*9999SWang.Lin@Sun.COM offsetof(struct ath_buf, bf_node)); 367*9999SWang.Lin@Sun.COM for (i = 0; i < nbuf; i++, bf++, ds++) { 368*9999SWang.Lin@Sun.COM bf->bf_desc = ds; 369*9999SWang.Lin@Sun.COM bf->bf_daddr = sc->sc_desc_dma.cookie.dmac_address + 370*9999SWang.Lin@Sun.COM ((uintptr_t)ds - (uintptr_t)sc->sc_desc); 371*9999SWang.Lin@Sun.COM list_insert_tail(bflist, bf); 372*9999SWang.Lin@Sun.COM 373*9999SWang.Lin@Sun.COM /* alloc DMA memory */ 374*9999SWang.Lin@Sun.COM err = arn_alloc_dma_mem(devinfo, &arn_dma_attr, 375*9999SWang.Lin@Sun.COM sc->sc_dmabuf_size, &arn_desc_accattr, DDI_DMA_STREAMING, 376*9999SWang.Lin@Sun.COM dmabflags, &bf->bf_dma); 377*9999SWang.Lin@Sun.COM if (err != DDI_SUCCESS) 378*9999SWang.Lin@Sun.COM return (err); 379*9999SWang.Lin@Sun.COM } 380*9999SWang.Lin@Sun.COM *pbf = bf; 381*9999SWang.Lin@Sun.COM *pds = ds; 382*9999SWang.Lin@Sun.COM 383*9999SWang.Lin@Sun.COM return (DDI_SUCCESS); 384*9999SWang.Lin@Sun.COM } 385*9999SWang.Lin@Sun.COM 386*9999SWang.Lin@Sun.COM /* 387*9999SWang.Lin@Sun.COM * Destroy tx, rx or beacon buffer list. Free DMA memory. 388*9999SWang.Lin@Sun.COM */ 389*9999SWang.Lin@Sun.COM static void 390*9999SWang.Lin@Sun.COM arn_buflist_cleanup(list_t *buflist) 391*9999SWang.Lin@Sun.COM { 392*9999SWang.Lin@Sun.COM struct ath_buf *bf; 393*9999SWang.Lin@Sun.COM 394*9999SWang.Lin@Sun.COM if (!buflist) 395*9999SWang.Lin@Sun.COM return; 396*9999SWang.Lin@Sun.COM 397*9999SWang.Lin@Sun.COM bf = list_head(buflist); 398*9999SWang.Lin@Sun.COM while (bf != NULL) { 399*9999SWang.Lin@Sun.COM if (bf->bf_m != NULL) { 400*9999SWang.Lin@Sun.COM freemsg(bf->bf_m); 401*9999SWang.Lin@Sun.COM bf->bf_m = NULL; 402*9999SWang.Lin@Sun.COM } 403*9999SWang.Lin@Sun.COM /* Free DMA buffer */ 404*9999SWang.Lin@Sun.COM arn_free_dma_mem(&bf->bf_dma); 405*9999SWang.Lin@Sun.COM if (bf->bf_in != NULL) { 406*9999SWang.Lin@Sun.COM ieee80211_free_node(bf->bf_in); 407*9999SWang.Lin@Sun.COM bf->bf_in = NULL; 408*9999SWang.Lin@Sun.COM } 409*9999SWang.Lin@Sun.COM list_remove(buflist, bf); 410*9999SWang.Lin@Sun.COM bf = list_head(buflist); 411*9999SWang.Lin@Sun.COM } 412*9999SWang.Lin@Sun.COM list_destroy(buflist); 413*9999SWang.Lin@Sun.COM } 414*9999SWang.Lin@Sun.COM 415*9999SWang.Lin@Sun.COM static void 416*9999SWang.Lin@Sun.COM arn_desc_free(struct arn_softc *sc) 417*9999SWang.Lin@Sun.COM { 418*9999SWang.Lin@Sun.COM arn_buflist_cleanup(&sc->sc_txbuf_list); 419*9999SWang.Lin@Sun.COM arn_buflist_cleanup(&sc->sc_rxbuf_list); 420*9999SWang.Lin@Sun.COM #ifdef ARN_IBSS 421*9999SWang.Lin@Sun.COM arn_buflist_cleanup(&sc->sc_bcbuf_list); 422*9999SWang.Lin@Sun.COM #endif 423*9999SWang.Lin@Sun.COM 424*9999SWang.Lin@Sun.COM /* Free descriptor DMA buffer */ 425*9999SWang.Lin@Sun.COM arn_free_dma_mem(&sc->sc_desc_dma); 426*9999SWang.Lin@Sun.COM 427*9999SWang.Lin@Sun.COM kmem_free((void *)sc->sc_vbufptr, sc->sc_vbuflen); 428*9999SWang.Lin@Sun.COM sc->sc_vbufptr = NULL; 429*9999SWang.Lin@Sun.COM } 430*9999SWang.Lin@Sun.COM 431*9999SWang.Lin@Sun.COM static int 432*9999SWang.Lin@Sun.COM arn_desc_alloc(dev_info_t *devinfo, struct arn_softc *sc) 433*9999SWang.Lin@Sun.COM { 434*9999SWang.Lin@Sun.COM int err; 435*9999SWang.Lin@Sun.COM size_t size; 436*9999SWang.Lin@Sun.COM struct ath_desc *ds; 437*9999SWang.Lin@Sun.COM struct ath_buf *bf; 438*9999SWang.Lin@Sun.COM 439*9999SWang.Lin@Sun.COM #ifdef ARN_IBSS 440*9999SWang.Lin@Sun.COM size = sizeof (struct ath_desc) * (ATH_TXBUF + ATH_RXBUF + ATH_BCBUF); 441*9999SWang.Lin@Sun.COM #else 442*9999SWang.Lin@Sun.COM size = sizeof (struct ath_desc) * (ATH_TXBUF + ATH_RXBUF); 443*9999SWang.Lin@Sun.COM #endif 444*9999SWang.Lin@Sun.COM 445*9999SWang.Lin@Sun.COM err = arn_alloc_dma_mem(devinfo, &arn_desc_dma_attr, size, 446*9999SWang.Lin@Sun.COM &arn_desc_accattr, DDI_DMA_CONSISTENT, 447*9999SWang.Lin@Sun.COM DDI_DMA_RDWR | DDI_DMA_CONSISTENT, &sc->sc_desc_dma); 448*9999SWang.Lin@Sun.COM 449*9999SWang.Lin@Sun.COM /* virtual address of the first descriptor */ 450*9999SWang.Lin@Sun.COM sc->sc_desc = (struct ath_desc *)sc->sc_desc_dma.mem_va; 451*9999SWang.Lin@Sun.COM 452*9999SWang.Lin@Sun.COM ds = sc->sc_desc; 453*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INIT, "arn: arn_desc_alloc(): DMA map: " 454*9999SWang.Lin@Sun.COM "%p (%d) -> %p\n", 455*9999SWang.Lin@Sun.COM sc->sc_desc, sc->sc_desc_dma.alength, 456*9999SWang.Lin@Sun.COM sc->sc_desc_dma.cookie.dmac_address)); 457*9999SWang.Lin@Sun.COM 458*9999SWang.Lin@Sun.COM /* allocate data structures to describe TX/RX DMA buffers */ 459*9999SWang.Lin@Sun.COM #ifdef ARN_IBSS 460*9999SWang.Lin@Sun.COM sc->sc_vbuflen = sizeof (struct ath_buf) * (ATH_TXBUF + ATH_RXBUF + 461*9999SWang.Lin@Sun.COM ATH_BCBUF); 462*9999SWang.Lin@Sun.COM #else 463*9999SWang.Lin@Sun.COM sc->sc_vbuflen = sizeof (struct ath_buf) * (ATH_TXBUF + ATH_RXBUF); 464*9999SWang.Lin@Sun.COM #endif 465*9999SWang.Lin@Sun.COM bf = (struct ath_buf *)kmem_zalloc(sc->sc_vbuflen, KM_SLEEP); 466*9999SWang.Lin@Sun.COM sc->sc_vbufptr = bf; 467*9999SWang.Lin@Sun.COM 468*9999SWang.Lin@Sun.COM /* DMA buffer size for each TX/RX packet */ 469*9999SWang.Lin@Sun.COM sc->sc_dmabuf_size = roundup(1000 + sizeof (struct ieee80211_frame) + 470*9999SWang.Lin@Sun.COM IEEE80211_MTU + IEEE80211_CRC_LEN + 471*9999SWang.Lin@Sun.COM (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + 472*9999SWang.Lin@Sun.COM IEEE80211_WEP_CRCLEN), sc->sc_cachelsz); 473*9999SWang.Lin@Sun.COM 474*9999SWang.Lin@Sun.COM /* create RX buffer list */ 475*9999SWang.Lin@Sun.COM err = arn_buflist_setup(devinfo, sc, &sc->sc_rxbuf_list, &bf, &ds, 476*9999SWang.Lin@Sun.COM ATH_RXBUF, DDI_DMA_READ | DDI_DMA_STREAMING); 477*9999SWang.Lin@Sun.COM if (err != DDI_SUCCESS) { 478*9999SWang.Lin@Sun.COM arn_desc_free(sc); 479*9999SWang.Lin@Sun.COM return (err); 480*9999SWang.Lin@Sun.COM } 481*9999SWang.Lin@Sun.COM 482*9999SWang.Lin@Sun.COM /* create TX buffer list */ 483*9999SWang.Lin@Sun.COM err = arn_buflist_setup(devinfo, sc, &sc->sc_txbuf_list, &bf, &ds, 484*9999SWang.Lin@Sun.COM ATH_TXBUF, DDI_DMA_STREAMING); 485*9999SWang.Lin@Sun.COM if (err != DDI_SUCCESS) { 486*9999SWang.Lin@Sun.COM arn_desc_free(sc); 487*9999SWang.Lin@Sun.COM return (err); 488*9999SWang.Lin@Sun.COM } 489*9999SWang.Lin@Sun.COM 490*9999SWang.Lin@Sun.COM /* create beacon buffer list */ 491*9999SWang.Lin@Sun.COM #ifdef ARN_IBSS 492*9999SWang.Lin@Sun.COM err = arn_buflist_setup(devinfo, sc, &sc->sc_bcbuf_list, &bf, &ds, 493*9999SWang.Lin@Sun.COM ATH_BCBUF, DDI_DMA_STREAMING); 494*9999SWang.Lin@Sun.COM if (err != DDI_SUCCESS) { 495*9999SWang.Lin@Sun.COM arn_desc_free(sc); 496*9999SWang.Lin@Sun.COM return (err); 497*9999SWang.Lin@Sun.COM } 498*9999SWang.Lin@Sun.COM #endif 499*9999SWang.Lin@Sun.COM 500*9999SWang.Lin@Sun.COM return (DDI_SUCCESS); 501*9999SWang.Lin@Sun.COM } 502*9999SWang.Lin@Sun.COM 503*9999SWang.Lin@Sun.COM static struct ath_rate_table * 504*9999SWang.Lin@Sun.COM /* LINTED E_STATIC_UNUSED */ 505*9999SWang.Lin@Sun.COM arn_get_ratetable(struct arn_softc *sc, uint32_t mode) 506*9999SWang.Lin@Sun.COM { 507*9999SWang.Lin@Sun.COM struct ath_rate_table *rate_table = NULL; 508*9999SWang.Lin@Sun.COM 509*9999SWang.Lin@Sun.COM switch (mode) { 510*9999SWang.Lin@Sun.COM case IEEE80211_MODE_11A: 511*9999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11A]; 512*9999SWang.Lin@Sun.COM break; 513*9999SWang.Lin@Sun.COM case IEEE80211_MODE_11B: 514*9999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11B]; 515*9999SWang.Lin@Sun.COM break; 516*9999SWang.Lin@Sun.COM case IEEE80211_MODE_11G: 517*9999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11G]; 518*9999SWang.Lin@Sun.COM break; 519*9999SWang.Lin@Sun.COM #ifdef ARB_11N 520*9999SWang.Lin@Sun.COM case IEEE80211_MODE_11NA_HT20: 521*9999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11NA_HT20]; 522*9999SWang.Lin@Sun.COM break; 523*9999SWang.Lin@Sun.COM case IEEE80211_MODE_11NG_HT20: 524*9999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11NG_HT20]; 525*9999SWang.Lin@Sun.COM break; 526*9999SWang.Lin@Sun.COM case IEEE80211_MODE_11NA_HT40PLUS: 527*9999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11NA_HT40PLUS]; 528*9999SWang.Lin@Sun.COM break; 529*9999SWang.Lin@Sun.COM case IEEE80211_MODE_11NA_HT40MINUS: 530*9999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11NA_HT40MINUS]; 531*9999SWang.Lin@Sun.COM break; 532*9999SWang.Lin@Sun.COM case IEEE80211_MODE_11NG_HT40PLUS: 533*9999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11NG_HT40PLUS]; 534*9999SWang.Lin@Sun.COM break; 535*9999SWang.Lin@Sun.COM case IEEE80211_MODE_11NG_HT40MINUS: 536*9999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11NG_HT40MINUS]; 537*9999SWang.Lin@Sun.COM break; 538*9999SWang.Lin@Sun.COM #endif 539*9999SWang.Lin@Sun.COM default: 540*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_FATAL, "arn: arn_get_ratetable(): " 541*9999SWang.Lin@Sun.COM "invalid mode %u\n", mode)); 542*9999SWang.Lin@Sun.COM return (NULL); 543*9999SWang.Lin@Sun.COM } 544*9999SWang.Lin@Sun.COM 545*9999SWang.Lin@Sun.COM return (rate_table); 546*9999SWang.Lin@Sun.COM 547*9999SWang.Lin@Sun.COM } 548*9999SWang.Lin@Sun.COM 549*9999SWang.Lin@Sun.COM static void 550*9999SWang.Lin@Sun.COM arn_setcurmode(struct arn_softc *sc, enum wireless_mode mode) 551*9999SWang.Lin@Sun.COM { 552*9999SWang.Lin@Sun.COM struct ath_rate_table *rt; 553*9999SWang.Lin@Sun.COM int i; 554*9999SWang.Lin@Sun.COM 555*9999SWang.Lin@Sun.COM for (i = 0; i < sizeof (sc->asc_rixmap); i++) 556*9999SWang.Lin@Sun.COM sc->asc_rixmap[i] = 0xff; 557*9999SWang.Lin@Sun.COM 558*9999SWang.Lin@Sun.COM rt = sc->hw_rate_table[mode]; 559*9999SWang.Lin@Sun.COM ASSERT(rt != NULL); 560*9999SWang.Lin@Sun.COM 561*9999SWang.Lin@Sun.COM for (i = 0; i < rt->rate_cnt; i++) 562*9999SWang.Lin@Sun.COM sc->asc_rixmap[rt->info[i].dot11rate & 563*9999SWang.Lin@Sun.COM IEEE80211_RATE_VAL] = (uint8_t)i; /* LINT */ 564*9999SWang.Lin@Sun.COM 565*9999SWang.Lin@Sun.COM sc->sc_currates = rt; 566*9999SWang.Lin@Sun.COM sc->sc_curmode = mode; 567*9999SWang.Lin@Sun.COM 568*9999SWang.Lin@Sun.COM /* 569*9999SWang.Lin@Sun.COM * All protection frames are transmited at 2Mb/s for 570*9999SWang.Lin@Sun.COM * 11g, otherwise at 1Mb/s. 571*9999SWang.Lin@Sun.COM * XXX select protection rate index from rate table. 572*9999SWang.Lin@Sun.COM */ 573*9999SWang.Lin@Sun.COM sc->sc_protrix = (mode == ATH9K_MODE_11G ? 1 : 0); 574*9999SWang.Lin@Sun.COM } 575*9999SWang.Lin@Sun.COM 576*9999SWang.Lin@Sun.COM static enum wireless_mode 577*9999SWang.Lin@Sun.COM arn_chan2mode(struct ath9k_channel *chan) 578*9999SWang.Lin@Sun.COM { 579*9999SWang.Lin@Sun.COM if (chan->chanmode == CHANNEL_A) 580*9999SWang.Lin@Sun.COM return (ATH9K_MODE_11A); 581*9999SWang.Lin@Sun.COM else if (chan->chanmode == CHANNEL_G) 582*9999SWang.Lin@Sun.COM return (ATH9K_MODE_11G); 583*9999SWang.Lin@Sun.COM else if (chan->chanmode == CHANNEL_B) 584*9999SWang.Lin@Sun.COM return (ATH9K_MODE_11B); 585*9999SWang.Lin@Sun.COM else if (chan->chanmode == CHANNEL_A_HT20) 586*9999SWang.Lin@Sun.COM return (ATH9K_MODE_11NA_HT20); 587*9999SWang.Lin@Sun.COM else if (chan->chanmode == CHANNEL_G_HT20) 588*9999SWang.Lin@Sun.COM return (ATH9K_MODE_11NG_HT20); 589*9999SWang.Lin@Sun.COM else if (chan->chanmode == CHANNEL_A_HT40PLUS) 590*9999SWang.Lin@Sun.COM return (ATH9K_MODE_11NA_HT40PLUS); 591*9999SWang.Lin@Sun.COM else if (chan->chanmode == CHANNEL_A_HT40MINUS) 592*9999SWang.Lin@Sun.COM return (ATH9K_MODE_11NA_HT40MINUS); 593*9999SWang.Lin@Sun.COM else if (chan->chanmode == CHANNEL_G_HT40PLUS) 594*9999SWang.Lin@Sun.COM return (ATH9K_MODE_11NG_HT40PLUS); 595*9999SWang.Lin@Sun.COM else if (chan->chanmode == CHANNEL_G_HT40MINUS) 596*9999SWang.Lin@Sun.COM return (ATH9K_MODE_11NG_HT40MINUS); 597*9999SWang.Lin@Sun.COM 598*9999SWang.Lin@Sun.COM return (ATH9K_MODE_11B); 599*9999SWang.Lin@Sun.COM } 600*9999SWang.Lin@Sun.COM 601*9999SWang.Lin@Sun.COM static void 602*9999SWang.Lin@Sun.COM arn_update_txpow(struct arn_softc *sc) 603*9999SWang.Lin@Sun.COM { 604*9999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah; 605*9999SWang.Lin@Sun.COM uint32_t txpow; 606*9999SWang.Lin@Sun.COM 607*9999SWang.Lin@Sun.COM if (sc->sc_curtxpow != sc->sc_config.txpowlimit) { 608*9999SWang.Lin@Sun.COM (void) ath9k_hw_set_txpowerlimit(ah, sc->sc_config.txpowlimit); 609*9999SWang.Lin@Sun.COM /* read back in case value is clamped */ 610*9999SWang.Lin@Sun.COM (void) ath9k_hw_getcapability(ah, ATH9K_CAP_TXPOW, 1, &txpow); 611*9999SWang.Lin@Sun.COM sc->sc_curtxpow = (uint32_t)txpow; 612*9999SWang.Lin@Sun.COM } 613*9999SWang.Lin@Sun.COM } 614*9999SWang.Lin@Sun.COM 615*9999SWang.Lin@Sun.COM static void 616*9999SWang.Lin@Sun.COM arn_setup_rates(struct arn_softc *sc, uint32_t mode) 617*9999SWang.Lin@Sun.COM { 618*9999SWang.Lin@Sun.COM int i, maxrates; 619*9999SWang.Lin@Sun.COM struct ath_rate_table *rate_table = NULL; 620*9999SWang.Lin@Sun.COM struct ieee80211_rateset *rateset; 621*9999SWang.Lin@Sun.COM ieee80211com_t *ic = (ieee80211com_t *)sc; 622*9999SWang.Lin@Sun.COM 623*9999SWang.Lin@Sun.COM /* rate_table = arn_get_ratetable(sc, mode); */ 624*9999SWang.Lin@Sun.COM switch (mode) { 625*9999SWang.Lin@Sun.COM case IEEE80211_MODE_11A: 626*9999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11A]; 627*9999SWang.Lin@Sun.COM break; 628*9999SWang.Lin@Sun.COM case IEEE80211_MODE_11B: 629*9999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11B]; 630*9999SWang.Lin@Sun.COM break; 631*9999SWang.Lin@Sun.COM case IEEE80211_MODE_11G: 632*9999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11G]; 633*9999SWang.Lin@Sun.COM break; 634*9999SWang.Lin@Sun.COM #ifdef ARN_11N 635*9999SWang.Lin@Sun.COM case IEEE80211_MODE_11NA_HT20: 636*9999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11NA_HT20]; 637*9999SWang.Lin@Sun.COM break; 638*9999SWang.Lin@Sun.COM case IEEE80211_MODE_11NG_HT20: 639*9999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11NG_HT20]; 640*9999SWang.Lin@Sun.COM break; 641*9999SWang.Lin@Sun.COM case IEEE80211_MODE_11NA_HT40PLUS: 642*9999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11NA_HT40PLUS]; 643*9999SWang.Lin@Sun.COM break; 644*9999SWang.Lin@Sun.COM case IEEE80211_MODE_11NA_HT40MINUS: 645*9999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11NA_HT40MINUS]; 646*9999SWang.Lin@Sun.COM break; 647*9999SWang.Lin@Sun.COM case IEEE80211_MODE_11NG_HT40PLUS: 648*9999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11NG_HT40PLUS]; 649*9999SWang.Lin@Sun.COM break; 650*9999SWang.Lin@Sun.COM case IEEE80211_MODE_11NG_HT40MINUS: 651*9999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11NG_HT40MINUS]; 652*9999SWang.Lin@Sun.COM break; 653*9999SWang.Lin@Sun.COM #endif 654*9999SWang.Lin@Sun.COM default: 655*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_RATE, "arn: arn_get_ratetable(): " 656*9999SWang.Lin@Sun.COM "invalid mode %u\n", mode)); 657*9999SWang.Lin@Sun.COM break; 658*9999SWang.Lin@Sun.COM } 659*9999SWang.Lin@Sun.COM if (rate_table == NULL) 660*9999SWang.Lin@Sun.COM return; 661*9999SWang.Lin@Sun.COM if (rate_table->rate_cnt > ATH_RATE_MAX) { 662*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_RATE, "arn: arn_rate_setup(): " 663*9999SWang.Lin@Sun.COM "rate table too small (%u > %u)\n", 664*9999SWang.Lin@Sun.COM rate_table->rate_cnt, IEEE80211_RATE_MAXSIZE)); 665*9999SWang.Lin@Sun.COM maxrates = ATH_RATE_MAX; 666*9999SWang.Lin@Sun.COM } else 667*9999SWang.Lin@Sun.COM maxrates = rate_table->rate_cnt; 668*9999SWang.Lin@Sun.COM 669*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_RATE, "arn: arn_rate_setup(): " 670*9999SWang.Lin@Sun.COM "maxrates is %d\n", maxrates)); 671*9999SWang.Lin@Sun.COM 672*9999SWang.Lin@Sun.COM rateset = &ic->ic_sup_rates[mode]; 673*9999SWang.Lin@Sun.COM for (i = 0; i < maxrates; i++) { 674*9999SWang.Lin@Sun.COM rateset->ir_rates[i] = rate_table->info[i].dot11rate; 675*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_RATE, "arn: arn_rate_setup(): " 676*9999SWang.Lin@Sun.COM "%d\n", rate_table->info[i].dot11rate)); 677*9999SWang.Lin@Sun.COM } 678*9999SWang.Lin@Sun.COM rateset->ir_nrates = (uint8_t)maxrates; /* ??? */ 679*9999SWang.Lin@Sun.COM } 680*9999SWang.Lin@Sun.COM 681*9999SWang.Lin@Sun.COM static int 682*9999SWang.Lin@Sun.COM arn_setup_channels(struct arn_softc *sc) 683*9999SWang.Lin@Sun.COM { 684*9999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah; 685*9999SWang.Lin@Sun.COM ieee80211com_t *ic = (ieee80211com_t *)sc; 686*9999SWang.Lin@Sun.COM int nchan, i, index; 687*9999SWang.Lin@Sun.COM uint8_t regclassids[ATH_REGCLASSIDS_MAX]; 688*9999SWang.Lin@Sun.COM uint32_t nregclass = 0; 689*9999SWang.Lin@Sun.COM struct ath9k_channel *c; 690*9999SWang.Lin@Sun.COM 691*9999SWang.Lin@Sun.COM /* Fill in ah->ah_channels */ 692*9999SWang.Lin@Sun.COM if (!ath9k_regd_init_channels(ah, ATH_CHAN_MAX, (uint32_t *)&nchan, 693*9999SWang.Lin@Sun.COM regclassids, ATH_REGCLASSIDS_MAX, &nregclass, CTRY_DEFAULT, 694*9999SWang.Lin@Sun.COM B_FALSE, 1)) { 695*9999SWang.Lin@Sun.COM uint32_t rd = ah->ah_currentRD; 696*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_CHANNEL, "arn: arn_setup_channels(): " 697*9999SWang.Lin@Sun.COM "unable to collect channel list; " 698*9999SWang.Lin@Sun.COM "regdomain likely %u country code %u\n", 699*9999SWang.Lin@Sun.COM rd, CTRY_DEFAULT)); 700*9999SWang.Lin@Sun.COM return (EINVAL); 701*9999SWang.Lin@Sun.COM } 702*9999SWang.Lin@Sun.COM 703*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_CHANNEL, "arn: arn_setup_channels(): " 704*9999SWang.Lin@Sun.COM "number of channel is %d\n", nchan)); 705*9999SWang.Lin@Sun.COM 706*9999SWang.Lin@Sun.COM for (i = 0; i < nchan; i++) { 707*9999SWang.Lin@Sun.COM c = &ah->ah_channels[i]; 708*9999SWang.Lin@Sun.COM uint16_t flags; 709*9999SWang.Lin@Sun.COM index = ath9k_hw_mhz2ieee(ah, c->channel, c->channelFlags); 710*9999SWang.Lin@Sun.COM 711*9999SWang.Lin@Sun.COM if (index > IEEE80211_CHAN_MAX) { 712*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_CHANNEL, 713*9999SWang.Lin@Sun.COM "arn: arn_setup_channels(): " 714*9999SWang.Lin@Sun.COM "bad hal channel %d (%u/%x) ignored\n", 715*9999SWang.Lin@Sun.COM index, c->channel, c->channelFlags)); 716*9999SWang.Lin@Sun.COM continue; 717*9999SWang.Lin@Sun.COM } 718*9999SWang.Lin@Sun.COM /* NB: flags are known to be compatible */ 719*9999SWang.Lin@Sun.COM if (index < 0) { 720*9999SWang.Lin@Sun.COM /* 721*9999SWang.Lin@Sun.COM * can't handle frequency <2400MHz (negative 722*9999SWang.Lin@Sun.COM * channels) right now 723*9999SWang.Lin@Sun.COM */ 724*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_CHANNEL, 725*9999SWang.Lin@Sun.COM "arn: arn_setup_channels(): " 726*9999SWang.Lin@Sun.COM "hal channel %d (%u/%x) " 727*9999SWang.Lin@Sun.COM "cannot be handled, ignored\n", 728*9999SWang.Lin@Sun.COM index, c->channel, c->channelFlags)); 729*9999SWang.Lin@Sun.COM continue; 730*9999SWang.Lin@Sun.COM } 731*9999SWang.Lin@Sun.COM 732*9999SWang.Lin@Sun.COM /* 733*9999SWang.Lin@Sun.COM * Calculate net80211 flags; most are compatible 734*9999SWang.Lin@Sun.COM * but some need massaging. Note the static turbo 735*9999SWang.Lin@Sun.COM * conversion can be removed once net80211 is updated 736*9999SWang.Lin@Sun.COM * to understand static vs. dynamic turbo. 737*9999SWang.Lin@Sun.COM */ 738*9999SWang.Lin@Sun.COM 739*9999SWang.Lin@Sun.COM flags = c->channelFlags & (CHANNEL_ALL | CHANNEL_PASSIVE); 740*9999SWang.Lin@Sun.COM 741*9999SWang.Lin@Sun.COM if (ic->ic_sup_channels[index].ich_freq == 0) { 742*9999SWang.Lin@Sun.COM ic->ic_sup_channels[index].ich_freq = c->channel; 743*9999SWang.Lin@Sun.COM ic->ic_sup_channels[index].ich_flags = flags; 744*9999SWang.Lin@Sun.COM } else { 745*9999SWang.Lin@Sun.COM /* channels overlap; e.g. 11g and 11b */ 746*9999SWang.Lin@Sun.COM ic->ic_sup_channels[index].ich_flags |= flags; 747*9999SWang.Lin@Sun.COM } 748*9999SWang.Lin@Sun.COM if ((c->channelFlags & CHANNEL_G) == CHANNEL_G) { 749*9999SWang.Lin@Sun.COM sc->sc_have11g = 1; 750*9999SWang.Lin@Sun.COM ic->ic_caps |= IEEE80211_C_SHPREAMBLE | 751*9999SWang.Lin@Sun.COM IEEE80211_C_SHSLOT; /* short slot time */ 752*9999SWang.Lin@Sun.COM } 753*9999SWang.Lin@Sun.COM } 754*9999SWang.Lin@Sun.COM 755*9999SWang.Lin@Sun.COM return (0); 756*9999SWang.Lin@Sun.COM } 757*9999SWang.Lin@Sun.COM 758*9999SWang.Lin@Sun.COM uint32_t 759*9999SWang.Lin@Sun.COM arn_chan2flags(ieee80211com_t *isc, struct ieee80211_channel *chan) 760*9999SWang.Lin@Sun.COM { 761*9999SWang.Lin@Sun.COM static const uint32_t modeflags[] = { 762*9999SWang.Lin@Sun.COM 0, /* IEEE80211_MODE_AUTO */ 763*9999SWang.Lin@Sun.COM CHANNEL_A, /* IEEE80211_MODE_11A */ 764*9999SWang.Lin@Sun.COM CHANNEL_B, /* IEEE80211_MODE_11B */ 765*9999SWang.Lin@Sun.COM CHANNEL_G, /* IEEE80211_MODE_11G */ 766*9999SWang.Lin@Sun.COM 0, /* */ 767*9999SWang.Lin@Sun.COM 0, /* */ 768*9999SWang.Lin@Sun.COM 0 /* */ 769*9999SWang.Lin@Sun.COM }; 770*9999SWang.Lin@Sun.COM return (modeflags[ieee80211_chan2mode(isc, chan)]); 771*9999SWang.Lin@Sun.COM } 772*9999SWang.Lin@Sun.COM 773*9999SWang.Lin@Sun.COM /* 774*9999SWang.Lin@Sun.COM * Update internal state after a channel change. 775*9999SWang.Lin@Sun.COM */ 776*9999SWang.Lin@Sun.COM void 777*9999SWang.Lin@Sun.COM arn_chan_change(struct arn_softc *sc, struct ieee80211_channel *chan) 778*9999SWang.Lin@Sun.COM { 779*9999SWang.Lin@Sun.COM struct ieee80211com *ic = &sc->sc_isc; 780*9999SWang.Lin@Sun.COM enum ieee80211_phymode mode; 781*9999SWang.Lin@Sun.COM enum wireless_mode wlmode; 782*9999SWang.Lin@Sun.COM 783*9999SWang.Lin@Sun.COM /* 784*9999SWang.Lin@Sun.COM * Change channels and update the h/w rate map 785*9999SWang.Lin@Sun.COM * if we're switching; e.g. 11a to 11b/g. 786*9999SWang.Lin@Sun.COM */ 787*9999SWang.Lin@Sun.COM mode = ieee80211_chan2mode(ic, chan); 788*9999SWang.Lin@Sun.COM switch (mode) { 789*9999SWang.Lin@Sun.COM case IEEE80211_MODE_11A: 790*9999SWang.Lin@Sun.COM wlmode = ATH9K_MODE_11A; 791*9999SWang.Lin@Sun.COM break; 792*9999SWang.Lin@Sun.COM case IEEE80211_MODE_11B: 793*9999SWang.Lin@Sun.COM wlmode = ATH9K_MODE_11B; 794*9999SWang.Lin@Sun.COM break; 795*9999SWang.Lin@Sun.COM case IEEE80211_MODE_11G: 796*9999SWang.Lin@Sun.COM wlmode = ATH9K_MODE_11B; 797*9999SWang.Lin@Sun.COM break; 798*9999SWang.Lin@Sun.COM default: 799*9999SWang.Lin@Sun.COM break; 800*9999SWang.Lin@Sun.COM } 801*9999SWang.Lin@Sun.COM if (wlmode != sc->sc_curmode) 802*9999SWang.Lin@Sun.COM arn_setcurmode(sc, wlmode); 803*9999SWang.Lin@Sun.COM 804*9999SWang.Lin@Sun.COM } 805*9999SWang.Lin@Sun.COM 806*9999SWang.Lin@Sun.COM /* 807*9999SWang.Lin@Sun.COM * Set/change channels. If the channel is really being changed, it's done 808*9999SWang.Lin@Sun.COM * by reseting the chip. To accomplish this we must first cleanup any pending 809*9999SWang.Lin@Sun.COM * DMA, then restart stuff. 810*9999SWang.Lin@Sun.COM */ 811*9999SWang.Lin@Sun.COM static int 812*9999SWang.Lin@Sun.COM arn_set_channel(struct arn_softc *sc, struct ath9k_channel *hchan) 813*9999SWang.Lin@Sun.COM { 814*9999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah; 815*9999SWang.Lin@Sun.COM ieee80211com_t *ic = &sc->sc_isc; 816*9999SWang.Lin@Sun.COM boolean_t fastcc = B_TRUE; 817*9999SWang.Lin@Sun.COM boolean_t stopped; 818*9999SWang.Lin@Sun.COM struct ieee80211_channel chan; 819*9999SWang.Lin@Sun.COM enum wireless_mode curmode; 820*9999SWang.Lin@Sun.COM 821*9999SWang.Lin@Sun.COM if (sc->sc_flags & SC_OP_INVALID) 822*9999SWang.Lin@Sun.COM return (EIO); 823*9999SWang.Lin@Sun.COM 824*9999SWang.Lin@Sun.COM if (hchan->channel != sc->sc_ah->ah_curchan->channel || 825*9999SWang.Lin@Sun.COM hchan->channelFlags != sc->sc_ah->ah_curchan->channelFlags || 826*9999SWang.Lin@Sun.COM (sc->sc_flags & SC_OP_CHAINMASK_UPDATE) || 827*9999SWang.Lin@Sun.COM (sc->sc_flags & SC_OP_FULL_RESET)) { 828*9999SWang.Lin@Sun.COM int status; 829*9999SWang.Lin@Sun.COM 830*9999SWang.Lin@Sun.COM /* 831*9999SWang.Lin@Sun.COM * This is only performed if the channel settings have 832*9999SWang.Lin@Sun.COM * actually changed. 833*9999SWang.Lin@Sun.COM * 834*9999SWang.Lin@Sun.COM * To switch channels clear any pending DMA operations; 835*9999SWang.Lin@Sun.COM * wait long enough for the RX fifo to drain, reset the 836*9999SWang.Lin@Sun.COM * hardware at the new frequency, and then re-enable 837*9999SWang.Lin@Sun.COM * the relevant bits of the h/w. 838*9999SWang.Lin@Sun.COM */ 839*9999SWang.Lin@Sun.COM (void) ath9k_hw_set_interrupts(ah, 0); /* disable interrupts */ 840*9999SWang.Lin@Sun.COM arn_draintxq(sc, B_FALSE); /* clear pending tx frames */ 841*9999SWang.Lin@Sun.COM stopped = arn_stoprecv(sc); /* turn off frame recv */ 842*9999SWang.Lin@Sun.COM 843*9999SWang.Lin@Sun.COM /* 844*9999SWang.Lin@Sun.COM * XXX: do not flush receive queue here. We don't want 845*9999SWang.Lin@Sun.COM * to flush data frames already in queue because of 846*9999SWang.Lin@Sun.COM * changing channel. 847*9999SWang.Lin@Sun.COM */ 848*9999SWang.Lin@Sun.COM 849*9999SWang.Lin@Sun.COM if (!stopped || (sc->sc_flags & SC_OP_FULL_RESET)) 850*9999SWang.Lin@Sun.COM fastcc = B_FALSE; 851*9999SWang.Lin@Sun.COM 852*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_CHANNEL, "arn: arn_set_channel(): " 853*9999SWang.Lin@Sun.COM "(%u MHz) -> (%u MHz), cflags:%x, chanwidth: %d\n", 854*9999SWang.Lin@Sun.COM sc->sc_ah->ah_curchan->channel, 855*9999SWang.Lin@Sun.COM hchan->channel, hchan->channelFlags, sc->tx_chan_width)); 856*9999SWang.Lin@Sun.COM 857*9999SWang.Lin@Sun.COM if (!ath9k_hw_reset(ah, hchan, sc->tx_chan_width, 858*9999SWang.Lin@Sun.COM sc->sc_tx_chainmask, sc->sc_rx_chainmask, 859*9999SWang.Lin@Sun.COM sc->sc_ht_extprotspacing, fastcc, &status)) { 860*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_FATAL, "arn: arn_set_channel(): " 861*9999SWang.Lin@Sun.COM "unable to reset channel %u (%uMhz) " 862*9999SWang.Lin@Sun.COM "flags 0x%x hal status %u\n", 863*9999SWang.Lin@Sun.COM ath9k_hw_mhz2ieee(ah, hchan->channel, 864*9999SWang.Lin@Sun.COM hchan->channelFlags), 865*9999SWang.Lin@Sun.COM hchan->channel, hchan->channelFlags, status)); 866*9999SWang.Lin@Sun.COM return (EIO); 867*9999SWang.Lin@Sun.COM } 868*9999SWang.Lin@Sun.COM 869*9999SWang.Lin@Sun.COM sc->sc_curchan = *hchan; 870*9999SWang.Lin@Sun.COM 871*9999SWang.Lin@Sun.COM sc->sc_flags &= ~SC_OP_CHAINMASK_UPDATE; 872*9999SWang.Lin@Sun.COM sc->sc_flags &= ~SC_OP_FULL_RESET; 873*9999SWang.Lin@Sun.COM 874*9999SWang.Lin@Sun.COM if (arn_startrecv(sc) != 0) { 875*9999SWang.Lin@Sun.COM arn_problem("arn: arn_set_channel(): " 876*9999SWang.Lin@Sun.COM "unable to restart recv logic\n"); 877*9999SWang.Lin@Sun.COM return (EIO); 878*9999SWang.Lin@Sun.COM } 879*9999SWang.Lin@Sun.COM 880*9999SWang.Lin@Sun.COM chan.ich_freq = hchan->channel; 881*9999SWang.Lin@Sun.COM chan.ich_flags = hchan->channelFlags; 882*9999SWang.Lin@Sun.COM ic->ic_ibss_chan = &chan; 883*9999SWang.Lin@Sun.COM 884*9999SWang.Lin@Sun.COM /* 885*9999SWang.Lin@Sun.COM * Change channels and update the h/w rate map 886*9999SWang.Lin@Sun.COM * if we're switching; e.g. 11a to 11b/g. 887*9999SWang.Lin@Sun.COM */ 888*9999SWang.Lin@Sun.COM curmode = arn_chan2mode(hchan); 889*9999SWang.Lin@Sun.COM if (curmode != sc->sc_curmode) 890*9999SWang.Lin@Sun.COM arn_setcurmode(sc, arn_chan2mode(hchan)); 891*9999SWang.Lin@Sun.COM 892*9999SWang.Lin@Sun.COM arn_update_txpow(sc); 893*9999SWang.Lin@Sun.COM 894*9999SWang.Lin@Sun.COM (void) ath9k_hw_set_interrupts(ah, sc->sc_imask); 895*9999SWang.Lin@Sun.COM } 896*9999SWang.Lin@Sun.COM 897*9999SWang.Lin@Sun.COM return (0); 898*9999SWang.Lin@Sun.COM } 899*9999SWang.Lin@Sun.COM 900*9999SWang.Lin@Sun.COM /* 901*9999SWang.Lin@Sun.COM * This routine performs the periodic noise floor calibration function 902*9999SWang.Lin@Sun.COM * that is used to adjust and optimize the chip performance. This 903*9999SWang.Lin@Sun.COM * takes environmental changes (location, temperature) into account. 904*9999SWang.Lin@Sun.COM * When the task is complete, it reschedules itself depending on the 905*9999SWang.Lin@Sun.COM * appropriate interval that was calculated. 906*9999SWang.Lin@Sun.COM */ 907*9999SWang.Lin@Sun.COM static void 908*9999SWang.Lin@Sun.COM arn_ani_calibrate(void *arg) 909*9999SWang.Lin@Sun.COM 910*9999SWang.Lin@Sun.COM { 911*9999SWang.Lin@Sun.COM ieee80211com_t *ic = (ieee80211com_t *)arg; 912*9999SWang.Lin@Sun.COM struct arn_softc *sc = (struct arn_softc *)ic; 913*9999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah; 914*9999SWang.Lin@Sun.COM boolean_t longcal = B_FALSE; 915*9999SWang.Lin@Sun.COM boolean_t shortcal = B_FALSE; 916*9999SWang.Lin@Sun.COM boolean_t aniflag = B_FALSE; 917*9999SWang.Lin@Sun.COM unsigned int timestamp = drv_hztousec(ddi_get_lbolt())/1000; 918*9999SWang.Lin@Sun.COM uint32_t cal_interval; 919*9999SWang.Lin@Sun.COM 920*9999SWang.Lin@Sun.COM /* 921*9999SWang.Lin@Sun.COM * don't calibrate when we're scanning. 922*9999SWang.Lin@Sun.COM * we are most likely not on our home channel. 923*9999SWang.Lin@Sun.COM */ 924*9999SWang.Lin@Sun.COM if (ic->ic_state != IEEE80211_S_RUN) 925*9999SWang.Lin@Sun.COM goto settimer; 926*9999SWang.Lin@Sun.COM 927*9999SWang.Lin@Sun.COM /* Long calibration runs independently of short calibration. */ 928*9999SWang.Lin@Sun.COM if ((timestamp - sc->sc_ani.sc_longcal_timer) >= ATH_LONG_CALINTERVAL) { 929*9999SWang.Lin@Sun.COM longcal = B_TRUE; 930*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_CALIBRATE, "arn: " 931*9999SWang.Lin@Sun.COM "%s: longcal @%lu\n", __func__, drv_hztousec)); 932*9999SWang.Lin@Sun.COM sc->sc_ani.sc_longcal_timer = timestamp; 933*9999SWang.Lin@Sun.COM } 934*9999SWang.Lin@Sun.COM 935*9999SWang.Lin@Sun.COM /* Short calibration applies only while sc_caldone is FALSE */ 936*9999SWang.Lin@Sun.COM if (!sc->sc_ani.sc_caldone) { 937*9999SWang.Lin@Sun.COM if ((timestamp - sc->sc_ani.sc_shortcal_timer) >= 938*9999SWang.Lin@Sun.COM ATH_SHORT_CALINTERVAL) { 939*9999SWang.Lin@Sun.COM shortcal = B_TRUE; 940*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_CALIBRATE, "arn: " 941*9999SWang.Lin@Sun.COM "%s: shortcal @%lu\n", 942*9999SWang.Lin@Sun.COM __func__, drv_hztousec)); 943*9999SWang.Lin@Sun.COM sc->sc_ani.sc_shortcal_timer = timestamp; 944*9999SWang.Lin@Sun.COM sc->sc_ani.sc_resetcal_timer = timestamp; 945*9999SWang.Lin@Sun.COM } 946*9999SWang.Lin@Sun.COM } else { 947*9999SWang.Lin@Sun.COM if ((timestamp - sc->sc_ani.sc_resetcal_timer) >= 948*9999SWang.Lin@Sun.COM ATH_RESTART_CALINTERVAL) { 949*9999SWang.Lin@Sun.COM ath9k_hw_reset_calvalid(ah, ah->ah_curchan, 950*9999SWang.Lin@Sun.COM &sc->sc_ani.sc_caldone); 951*9999SWang.Lin@Sun.COM if (sc->sc_ani.sc_caldone) 952*9999SWang.Lin@Sun.COM sc->sc_ani.sc_resetcal_timer = timestamp; 953*9999SWang.Lin@Sun.COM } 954*9999SWang.Lin@Sun.COM } 955*9999SWang.Lin@Sun.COM 956*9999SWang.Lin@Sun.COM /* Verify whether we must check ANI */ 957*9999SWang.Lin@Sun.COM if ((timestamp - sc->sc_ani.sc_checkani_timer) >= 958*9999SWang.Lin@Sun.COM ATH_ANI_POLLINTERVAL) { 959*9999SWang.Lin@Sun.COM aniflag = B_TRUE; 960*9999SWang.Lin@Sun.COM sc->sc_ani.sc_checkani_timer = timestamp; 961*9999SWang.Lin@Sun.COM } 962*9999SWang.Lin@Sun.COM 963*9999SWang.Lin@Sun.COM /* Skip all processing if there's nothing to do. */ 964*9999SWang.Lin@Sun.COM if (longcal || shortcal || aniflag) { 965*9999SWang.Lin@Sun.COM /* Call ANI routine if necessary */ 966*9999SWang.Lin@Sun.COM if (aniflag) 967*9999SWang.Lin@Sun.COM ath9k_hw_ani_monitor(ah, &sc->sc_halstats, 968*9999SWang.Lin@Sun.COM ah->ah_curchan); 969*9999SWang.Lin@Sun.COM 970*9999SWang.Lin@Sun.COM /* Perform calibration if necessary */ 971*9999SWang.Lin@Sun.COM if (longcal || shortcal) { 972*9999SWang.Lin@Sun.COM boolean_t iscaldone = B_FALSE; 973*9999SWang.Lin@Sun.COM 974*9999SWang.Lin@Sun.COM if (ath9k_hw_calibrate(ah, ah->ah_curchan, 975*9999SWang.Lin@Sun.COM sc->sc_rx_chainmask, longcal, &iscaldone)) { 976*9999SWang.Lin@Sun.COM if (longcal) 977*9999SWang.Lin@Sun.COM sc->sc_ani.sc_noise_floor = 978*9999SWang.Lin@Sun.COM ath9k_hw_getchan_noise(ah, 979*9999SWang.Lin@Sun.COM ah->ah_curchan); 980*9999SWang.Lin@Sun.COM 981*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_CALIBRATE, "arn: " 982*9999SWang.Lin@Sun.COM "%s: calibrate chan %u/%x nf: %d\n", 983*9999SWang.Lin@Sun.COM __func__, 984*9999SWang.Lin@Sun.COM ah->ah_curchan->channel, 985*9999SWang.Lin@Sun.COM ah->ah_curchan->channelFlags, 986*9999SWang.Lin@Sun.COM sc->sc_ani.sc_noise_floor)); 987*9999SWang.Lin@Sun.COM } else { 988*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_CALIBRATE, "arn: " 989*9999SWang.Lin@Sun.COM "%s: calibrate chan %u/%x failed\n", 990*9999SWang.Lin@Sun.COM __func__, 991*9999SWang.Lin@Sun.COM ah->ah_curchan->channel, 992*9999SWang.Lin@Sun.COM ah->ah_curchan->channelFlags)); 993*9999SWang.Lin@Sun.COM } 994*9999SWang.Lin@Sun.COM sc->sc_ani.sc_caldone = iscaldone; 995*9999SWang.Lin@Sun.COM } 996*9999SWang.Lin@Sun.COM } 997*9999SWang.Lin@Sun.COM 998*9999SWang.Lin@Sun.COM settimer: 999*9999SWang.Lin@Sun.COM /* 1000*9999SWang.Lin@Sun.COM * Set timer interval based on previous results. 1001*9999SWang.Lin@Sun.COM * The interval must be the shortest necessary to satisfy ANI, 1002*9999SWang.Lin@Sun.COM * short calibration and long calibration. 1003*9999SWang.Lin@Sun.COM */ 1004*9999SWang.Lin@Sun.COM cal_interval = ATH_LONG_CALINTERVAL; 1005*9999SWang.Lin@Sun.COM if (sc->sc_ah->ah_config.enable_ani) 1006*9999SWang.Lin@Sun.COM cal_interval = 1007*9999SWang.Lin@Sun.COM min(cal_interval, (uint32_t)ATH_ANI_POLLINTERVAL); 1008*9999SWang.Lin@Sun.COM 1009*9999SWang.Lin@Sun.COM if (!sc->sc_ani.sc_caldone) 1010*9999SWang.Lin@Sun.COM cal_interval = min(cal_interval, 1011*9999SWang.Lin@Sun.COM (uint32_t)ATH_SHORT_CALINTERVAL); 1012*9999SWang.Lin@Sun.COM 1013*9999SWang.Lin@Sun.COM sc->sc_scan_timer = 0; 1014*9999SWang.Lin@Sun.COM sc->sc_scan_timer = timeout(arn_ani_calibrate, (void *)sc, 1015*9999SWang.Lin@Sun.COM drv_usectohz(cal_interval * 1000)); 1016*9999SWang.Lin@Sun.COM } 1017*9999SWang.Lin@Sun.COM 1018*9999SWang.Lin@Sun.COM static void 1019*9999SWang.Lin@Sun.COM arn_stop_caltimer(struct arn_softc *sc) 1020*9999SWang.Lin@Sun.COM { 1021*9999SWang.Lin@Sun.COM timeout_id_t tmp_id = 0; 1022*9999SWang.Lin@Sun.COM 1023*9999SWang.Lin@Sun.COM while ((sc->sc_cal_timer != 0) && (tmp_id != sc->sc_cal_timer)) { 1024*9999SWang.Lin@Sun.COM tmp_id = sc->sc_cal_timer; 1025*9999SWang.Lin@Sun.COM (void) untimeout(tmp_id); 1026*9999SWang.Lin@Sun.COM } 1027*9999SWang.Lin@Sun.COM sc->sc_cal_timer = 0; 1028*9999SWang.Lin@Sun.COM } 1029*9999SWang.Lin@Sun.COM 1030*9999SWang.Lin@Sun.COM static uint_t 1031*9999SWang.Lin@Sun.COM arn_isr(caddr_t arg) 1032*9999SWang.Lin@Sun.COM { 1033*9999SWang.Lin@Sun.COM /* LINTED E_BAD_PTR_CAST_ALIGN */ 1034*9999SWang.Lin@Sun.COM struct arn_softc *sc = (struct arn_softc *)arg; 1035*9999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah; 1036*9999SWang.Lin@Sun.COM enum ath9k_int status; 1037*9999SWang.Lin@Sun.COM ieee80211com_t *ic = (ieee80211com_t *)sc; 1038*9999SWang.Lin@Sun.COM 1039*9999SWang.Lin@Sun.COM ARN_LOCK(sc); 1040*9999SWang.Lin@Sun.COM 1041*9999SWang.Lin@Sun.COM if (sc->sc_flags & SC_OP_INVALID) { 1042*9999SWang.Lin@Sun.COM /* 1043*9999SWang.Lin@Sun.COM * The hardware is not ready/present, don't 1044*9999SWang.Lin@Sun.COM * touch anything. Note this can happen early 1045*9999SWang.Lin@Sun.COM * on if the IRQ is shared. 1046*9999SWang.Lin@Sun.COM */ 1047*9999SWang.Lin@Sun.COM ARN_UNLOCK(sc); 1048*9999SWang.Lin@Sun.COM return (DDI_INTR_UNCLAIMED); 1049*9999SWang.Lin@Sun.COM } 1050*9999SWang.Lin@Sun.COM if (!ath9k_hw_intrpend(ah)) { /* shared irq, not for us */ 1051*9999SWang.Lin@Sun.COM ARN_UNLOCK(sc); 1052*9999SWang.Lin@Sun.COM return (DDI_INTR_UNCLAIMED); 1053*9999SWang.Lin@Sun.COM } 1054*9999SWang.Lin@Sun.COM 1055*9999SWang.Lin@Sun.COM /* 1056*9999SWang.Lin@Sun.COM * Figure out the reason(s) for the interrupt. Note 1057*9999SWang.Lin@Sun.COM * that the hal returns a pseudo-ISR that may include 1058*9999SWang.Lin@Sun.COM * bits we haven't explicitly enabled so we mask the 1059*9999SWang.Lin@Sun.COM * value to insure we only process bits we requested. 1060*9999SWang.Lin@Sun.COM */ 1061*9999SWang.Lin@Sun.COM (void) ath9k_hw_getisr(ah, &status); /* NB: clears ISR too */ 1062*9999SWang.Lin@Sun.COM 1063*9999SWang.Lin@Sun.COM status &= sc->sc_imask; /* discard unasked-for bits */ 1064*9999SWang.Lin@Sun.COM 1065*9999SWang.Lin@Sun.COM /* 1066*9999SWang.Lin@Sun.COM * If there are no status bits set, then this interrupt was not 1067*9999SWang.Lin@Sun.COM * for me (should have been caught above). 1068*9999SWang.Lin@Sun.COM */ 1069*9999SWang.Lin@Sun.COM if (!status) { 1070*9999SWang.Lin@Sun.COM ARN_UNLOCK(sc); 1071*9999SWang.Lin@Sun.COM return (DDI_INTR_UNCLAIMED); 1072*9999SWang.Lin@Sun.COM } 1073*9999SWang.Lin@Sun.COM 1074*9999SWang.Lin@Sun.COM sc->sc_intrstatus = status; 1075*9999SWang.Lin@Sun.COM 1076*9999SWang.Lin@Sun.COM if (status & ATH9K_INT_FATAL) { 1077*9999SWang.Lin@Sun.COM /* need a chip reset */ 1078*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): " 1079*9999SWang.Lin@Sun.COM "ATH9K_INT_FATAL\n")); 1080*9999SWang.Lin@Sun.COM goto reset; 1081*9999SWang.Lin@Sun.COM } else if (status & ATH9K_INT_RXORN) { 1082*9999SWang.Lin@Sun.COM /* need a chip reset */ 1083*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): " 1084*9999SWang.Lin@Sun.COM "ATH9K_INT_RXORN\n")); 1085*9999SWang.Lin@Sun.COM goto reset; 1086*9999SWang.Lin@Sun.COM } else { 1087*9999SWang.Lin@Sun.COM if (status & ATH9K_INT_RXEOL) { 1088*9999SWang.Lin@Sun.COM /* 1089*9999SWang.Lin@Sun.COM * NB: the hardware should re-read the link when 1090*9999SWang.Lin@Sun.COM * RXE bit is written, but it doesn't work 1091*9999SWang.Lin@Sun.COM * at least on older hardware revs. 1092*9999SWang.Lin@Sun.COM */ 1093*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): " 1094*9999SWang.Lin@Sun.COM "ATH9K_INT_RXEOL\n")); 1095*9999SWang.Lin@Sun.COM sc->sc_rxlink = NULL; 1096*9999SWang.Lin@Sun.COM } 1097*9999SWang.Lin@Sun.COM if (status & ATH9K_INT_TXURN) { 1098*9999SWang.Lin@Sun.COM /* bump tx trigger level */ 1099*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): " 1100*9999SWang.Lin@Sun.COM "ATH9K_INT_TXURN\n")); 1101*9999SWang.Lin@Sun.COM (void) ath9k_hw_updatetxtriglevel(ah, B_TRUE); 1102*9999SWang.Lin@Sun.COM } 1103*9999SWang.Lin@Sun.COM /* XXX: optimize this */ 1104*9999SWang.Lin@Sun.COM if (status & ATH9K_INT_RX) { 1105*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): " 1106*9999SWang.Lin@Sun.COM "ATH9K_INT_RX\n")); 1107*9999SWang.Lin@Sun.COM sc->sc_rx_pend = 1; 1108*9999SWang.Lin@Sun.COM ddi_trigger_softintr(sc->sc_softint_id); 1109*9999SWang.Lin@Sun.COM } 1110*9999SWang.Lin@Sun.COM if (status & ATH9K_INT_TX) { 1111*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): " 1112*9999SWang.Lin@Sun.COM "ATH9K_INT_TX\n")); 1113*9999SWang.Lin@Sun.COM if (ddi_taskq_dispatch(sc->sc_tq, 1114*9999SWang.Lin@Sun.COM arn_tx_int_proc, sc, DDI_NOSLEEP) != 1115*9999SWang.Lin@Sun.COM DDI_SUCCESS) { 1116*9999SWang.Lin@Sun.COM arn_problem("arn: arn_isr(): " 1117*9999SWang.Lin@Sun.COM "No memory for tx taskq\n"); 1118*9999SWang.Lin@Sun.COM } 1119*9999SWang.Lin@Sun.COM } 1120*9999SWang.Lin@Sun.COM #ifdef ARN_ATH9K_INT_MIB 1121*9999SWang.Lin@Sun.COM if (status & ATH9K_INT_MIB) { 1122*9999SWang.Lin@Sun.COM /* 1123*9999SWang.Lin@Sun.COM * Disable interrupts until we service the MIB 1124*9999SWang.Lin@Sun.COM * interrupt; otherwise it will continue to 1125*9999SWang.Lin@Sun.COM * fire. 1126*9999SWang.Lin@Sun.COM */ 1127*9999SWang.Lin@Sun.COM (void) ath9k_hw_set_interrupts(ah, 0); 1128*9999SWang.Lin@Sun.COM /* 1129*9999SWang.Lin@Sun.COM * Let the hal handle the event. We assume 1130*9999SWang.Lin@Sun.COM * it will clear whatever condition caused 1131*9999SWang.Lin@Sun.COM * the interrupt. 1132*9999SWang.Lin@Sun.COM */ 1133*9999SWang.Lin@Sun.COM ath9k_hw_procmibevent(ah, &sc->sc_halstats); 1134*9999SWang.Lin@Sun.COM (void) ath9k_hw_set_interrupts(ah, sc->sc_imask); 1135*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): " 1136*9999SWang.Lin@Sun.COM "ATH9K_INT_MIB\n")); 1137*9999SWang.Lin@Sun.COM } 1138*9999SWang.Lin@Sun.COM #endif 1139*9999SWang.Lin@Sun.COM 1140*9999SWang.Lin@Sun.COM #ifdef ARN_ATH9K_INT_TIM_TIMER 1141*9999SWang.Lin@Sun.COM if (status & ATH9K_INT_TIM_TIMER) { 1142*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): " 1143*9999SWang.Lin@Sun.COM "ATH9K_INT_TIM_TIMER\n")); 1144*9999SWang.Lin@Sun.COM if (!(ah->ah_caps.hw_caps & 1145*9999SWang.Lin@Sun.COM ATH9K_HW_CAP_AUTOSLEEP)) { 1146*9999SWang.Lin@Sun.COM /* 1147*9999SWang.Lin@Sun.COM * Clear RxAbort bit so that we can 1148*9999SWang.Lin@Sun.COM * receive frames 1149*9999SWang.Lin@Sun.COM */ 1150*9999SWang.Lin@Sun.COM ath9k_hw_setrxabort(ah, 0); 1151*9999SWang.Lin@Sun.COM goto reset; 1152*9999SWang.Lin@Sun.COM } 1153*9999SWang.Lin@Sun.COM } 1154*9999SWang.Lin@Sun.COM #endif 1155*9999SWang.Lin@Sun.COM 1156*9999SWang.Lin@Sun.COM if (status & ATH9K_INT_BMISS) { 1157*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): " 1158*9999SWang.Lin@Sun.COM "ATH9K_INT_BMISS\n")); 1159*9999SWang.Lin@Sun.COM 1160*9999SWang.Lin@Sun.COM if (ddi_taskq_dispatch(sc->sc_tq, arn_bmiss_proc, 1161*9999SWang.Lin@Sun.COM sc, DDI_NOSLEEP) != DDI_SUCCESS) { 1162*9999SWang.Lin@Sun.COM arn_problem("arn: arn_isr(): " 1163*9999SWang.Lin@Sun.COM "No memory available for bmiss taskq\n"); 1164*9999SWang.Lin@Sun.COM } 1165*9999SWang.Lin@Sun.COM } 1166*9999SWang.Lin@Sun.COM 1167*9999SWang.Lin@Sun.COM ARN_UNLOCK(sc); 1168*9999SWang.Lin@Sun.COM 1169*9999SWang.Lin@Sun.COM #ifdef ARN_ATH9K_INT_CST 1170*9999SWang.Lin@Sun.COM /* carrier sense timeout */ 1171*9999SWang.Lin@Sun.COM if (status & ATH9K_INT_CST) { 1172*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): " 1173*9999SWang.Lin@Sun.COM "ATH9K_INT_CST\n")); 1174*9999SWang.Lin@Sun.COM return (DDI_INTR_CLAIMED); 1175*9999SWang.Lin@Sun.COM } 1176*9999SWang.Lin@Sun.COM #endif 1177*9999SWang.Lin@Sun.COM 1178*9999SWang.Lin@Sun.COM if (status & ATH9K_INT_SWBA) { 1179*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): " 1180*9999SWang.Lin@Sun.COM "ATH9K_INT_SWBA\n")); 1181*9999SWang.Lin@Sun.COM /* This will occur only in Host-AP or Ad-Hoc mode */ 1182*9999SWang.Lin@Sun.COM return (DDI_INTR_CLAIMED); 1183*9999SWang.Lin@Sun.COM } 1184*9999SWang.Lin@Sun.COM } 1185*9999SWang.Lin@Sun.COM 1186*9999SWang.Lin@Sun.COM return (DDI_INTR_CLAIMED); 1187*9999SWang.Lin@Sun.COM reset: 1188*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INTERRUPT, "Rset for fatal err\n")); 1189*9999SWang.Lin@Sun.COM (void) arn_reset(ic); 1190*9999SWang.Lin@Sun.COM ARN_UNLOCK(sc); 1191*9999SWang.Lin@Sun.COM return (DDI_INTR_CLAIMED); 1192*9999SWang.Lin@Sun.COM } 1193*9999SWang.Lin@Sun.COM 1194*9999SWang.Lin@Sun.COM static int 1195*9999SWang.Lin@Sun.COM arn_get_channel(struct arn_softc *sc, struct ieee80211_channel *chan) 1196*9999SWang.Lin@Sun.COM { 1197*9999SWang.Lin@Sun.COM int i; 1198*9999SWang.Lin@Sun.COM 1199*9999SWang.Lin@Sun.COM for (i = 0; i < sc->sc_ah->ah_nchan; i++) { 1200*9999SWang.Lin@Sun.COM if (sc->sc_ah->ah_channels[i].channel == chan->ich_freq) 1201*9999SWang.Lin@Sun.COM return (i); 1202*9999SWang.Lin@Sun.COM } 1203*9999SWang.Lin@Sun.COM 1204*9999SWang.Lin@Sun.COM return (-1); 1205*9999SWang.Lin@Sun.COM } 1206*9999SWang.Lin@Sun.COM 1207*9999SWang.Lin@Sun.COM int 1208*9999SWang.Lin@Sun.COM arn_reset(ieee80211com_t *ic) 1209*9999SWang.Lin@Sun.COM { 1210*9999SWang.Lin@Sun.COM struct arn_softc *sc = (struct arn_softc *)ic; 1211*9999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah; 1212*9999SWang.Lin@Sun.COM int status; 1213*9999SWang.Lin@Sun.COM int error = 0; 1214*9999SWang.Lin@Sun.COM 1215*9999SWang.Lin@Sun.COM (void) ath9k_hw_set_interrupts(ah, 0); 1216*9999SWang.Lin@Sun.COM arn_draintxq(sc, 0); 1217*9999SWang.Lin@Sun.COM (void) arn_stoprecv(sc); 1218*9999SWang.Lin@Sun.COM 1219*9999SWang.Lin@Sun.COM if (!ath9k_hw_reset(ah, sc->sc_ah->ah_curchan, sc->tx_chan_width, 1220*9999SWang.Lin@Sun.COM sc->sc_tx_chainmask, sc->sc_rx_chainmask, 1221*9999SWang.Lin@Sun.COM sc->sc_ht_extprotspacing, B_FALSE, &status)) { 1222*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_RESET, "arn: arn_reset(): " 1223*9999SWang.Lin@Sun.COM "unable to reset hardware; hal status %u\n", status)); 1224*9999SWang.Lin@Sun.COM error = EIO; 1225*9999SWang.Lin@Sun.COM } 1226*9999SWang.Lin@Sun.COM 1227*9999SWang.Lin@Sun.COM if (arn_startrecv(sc) != 0) 1228*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_RESET, "arn: arn_reset(): " 1229*9999SWang.Lin@Sun.COM "unable to start recv logic\n")); 1230*9999SWang.Lin@Sun.COM 1231*9999SWang.Lin@Sun.COM /* 1232*9999SWang.Lin@Sun.COM * We may be doing a reset in response to a request 1233*9999SWang.Lin@Sun.COM * that changes the channel so update any state that 1234*9999SWang.Lin@Sun.COM * might change as a result. 1235*9999SWang.Lin@Sun.COM */ 1236*9999SWang.Lin@Sun.COM arn_setcurmode(sc, arn_chan2mode(sc->sc_ah->ah_curchan)); 1237*9999SWang.Lin@Sun.COM 1238*9999SWang.Lin@Sun.COM arn_update_txpow(sc); 1239*9999SWang.Lin@Sun.COM 1240*9999SWang.Lin@Sun.COM if (sc->sc_flags & SC_OP_BEACONS) 1241*9999SWang.Lin@Sun.COM arn_beacon_config(sc); /* restart beacons */ 1242*9999SWang.Lin@Sun.COM 1243*9999SWang.Lin@Sun.COM (void) ath9k_hw_set_interrupts(ah, sc->sc_imask); 1244*9999SWang.Lin@Sun.COM 1245*9999SWang.Lin@Sun.COM return (error); 1246*9999SWang.Lin@Sun.COM } 1247*9999SWang.Lin@Sun.COM 1248*9999SWang.Lin@Sun.COM int 1249*9999SWang.Lin@Sun.COM arn_get_hal_qnum(uint16_t queue, struct arn_softc *sc) 1250*9999SWang.Lin@Sun.COM { 1251*9999SWang.Lin@Sun.COM int qnum; 1252*9999SWang.Lin@Sun.COM 1253*9999SWang.Lin@Sun.COM switch (queue) { 1254*9999SWang.Lin@Sun.COM case WME_AC_VO: 1255*9999SWang.Lin@Sun.COM qnum = sc->sc_haltype2q[ATH9K_WME_AC_VO]; 1256*9999SWang.Lin@Sun.COM break; 1257*9999SWang.Lin@Sun.COM case WME_AC_VI: 1258*9999SWang.Lin@Sun.COM qnum = sc->sc_haltype2q[ATH9K_WME_AC_VI]; 1259*9999SWang.Lin@Sun.COM break; 1260*9999SWang.Lin@Sun.COM case WME_AC_BE: 1261*9999SWang.Lin@Sun.COM qnum = sc->sc_haltype2q[ATH9K_WME_AC_BE]; 1262*9999SWang.Lin@Sun.COM break; 1263*9999SWang.Lin@Sun.COM case WME_AC_BK: 1264*9999SWang.Lin@Sun.COM qnum = sc->sc_haltype2q[ATH9K_WME_AC_BK]; 1265*9999SWang.Lin@Sun.COM break; 1266*9999SWang.Lin@Sun.COM default: 1267*9999SWang.Lin@Sun.COM qnum = sc->sc_haltype2q[ATH9K_WME_AC_BE]; 1268*9999SWang.Lin@Sun.COM break; 1269*9999SWang.Lin@Sun.COM } 1270*9999SWang.Lin@Sun.COM 1271*9999SWang.Lin@Sun.COM return (qnum); 1272*9999SWang.Lin@Sun.COM } 1273*9999SWang.Lin@Sun.COM 1274*9999SWang.Lin@Sun.COM static struct { 1275*9999SWang.Lin@Sun.COM uint32_t version; 1276*9999SWang.Lin@Sun.COM const char *name; 1277*9999SWang.Lin@Sun.COM } ath_mac_bb_names[] = { 1278*9999SWang.Lin@Sun.COM { AR_SREV_VERSION_5416_PCI, "5416" }, 1279*9999SWang.Lin@Sun.COM { AR_SREV_VERSION_5416_PCIE, "5418" }, 1280*9999SWang.Lin@Sun.COM { AR_SREV_VERSION_9100, "9100" }, 1281*9999SWang.Lin@Sun.COM { AR_SREV_VERSION_9160, "9160" }, 1282*9999SWang.Lin@Sun.COM { AR_SREV_VERSION_9280, "9280" }, 1283*9999SWang.Lin@Sun.COM { AR_SREV_VERSION_9285, "9285" } 1284*9999SWang.Lin@Sun.COM }; 1285*9999SWang.Lin@Sun.COM 1286*9999SWang.Lin@Sun.COM static struct { 1287*9999SWang.Lin@Sun.COM uint16_t version; 1288*9999SWang.Lin@Sun.COM const char *name; 1289*9999SWang.Lin@Sun.COM } ath_rf_names[] = { 1290*9999SWang.Lin@Sun.COM { 0, "5133" }, 1291*9999SWang.Lin@Sun.COM { AR_RAD5133_SREV_MAJOR, "5133" }, 1292*9999SWang.Lin@Sun.COM { AR_RAD5122_SREV_MAJOR, "5122" }, 1293*9999SWang.Lin@Sun.COM { AR_RAD2133_SREV_MAJOR, "2133" }, 1294*9999SWang.Lin@Sun.COM { AR_RAD2122_SREV_MAJOR, "2122" } 1295*9999SWang.Lin@Sun.COM }; 1296*9999SWang.Lin@Sun.COM 1297*9999SWang.Lin@Sun.COM /* 1298*9999SWang.Lin@Sun.COM * Return the MAC/BB name. "????" is returned if the MAC/BB is unknown. 1299*9999SWang.Lin@Sun.COM */ 1300*9999SWang.Lin@Sun.COM 1301*9999SWang.Lin@Sun.COM static const char * 1302*9999SWang.Lin@Sun.COM arn_mac_bb_name(uint32_t mac_bb_version) 1303*9999SWang.Lin@Sun.COM { 1304*9999SWang.Lin@Sun.COM int i; 1305*9999SWang.Lin@Sun.COM 1306*9999SWang.Lin@Sun.COM for (i = 0; i < ARRAY_SIZE(ath_mac_bb_names); i++) { 1307*9999SWang.Lin@Sun.COM if (ath_mac_bb_names[i].version == mac_bb_version) { 1308*9999SWang.Lin@Sun.COM return (ath_mac_bb_names[i].name); 1309*9999SWang.Lin@Sun.COM } 1310*9999SWang.Lin@Sun.COM } 1311*9999SWang.Lin@Sun.COM 1312*9999SWang.Lin@Sun.COM return ("????"); 1313*9999SWang.Lin@Sun.COM } 1314*9999SWang.Lin@Sun.COM 1315*9999SWang.Lin@Sun.COM /* 1316*9999SWang.Lin@Sun.COM * Return the RF name. "????" is returned if the RF is unknown. 1317*9999SWang.Lin@Sun.COM */ 1318*9999SWang.Lin@Sun.COM 1319*9999SWang.Lin@Sun.COM static const char * 1320*9999SWang.Lin@Sun.COM arn_rf_name(uint16_t rf_version) 1321*9999SWang.Lin@Sun.COM { 1322*9999SWang.Lin@Sun.COM int i; 1323*9999SWang.Lin@Sun.COM 1324*9999SWang.Lin@Sun.COM for (i = 0; i < ARRAY_SIZE(ath_rf_names); i++) { 1325*9999SWang.Lin@Sun.COM if (ath_rf_names[i].version == rf_version) { 1326*9999SWang.Lin@Sun.COM return (ath_rf_names[i].name); 1327*9999SWang.Lin@Sun.COM } 1328*9999SWang.Lin@Sun.COM } 1329*9999SWang.Lin@Sun.COM 1330*9999SWang.Lin@Sun.COM return ("????"); 1331*9999SWang.Lin@Sun.COM } 1332*9999SWang.Lin@Sun.COM 1333*9999SWang.Lin@Sun.COM static void 1334*9999SWang.Lin@Sun.COM arn_next_scan(void *arg) 1335*9999SWang.Lin@Sun.COM { 1336*9999SWang.Lin@Sun.COM ieee80211com_t *ic = arg; 1337*9999SWang.Lin@Sun.COM struct arn_softc *sc = (struct arn_softc *)ic; 1338*9999SWang.Lin@Sun.COM 1339*9999SWang.Lin@Sun.COM sc->sc_scan_timer = 0; 1340*9999SWang.Lin@Sun.COM if (ic->ic_state == IEEE80211_S_SCAN) { 1341*9999SWang.Lin@Sun.COM sc->sc_scan_timer = timeout(arn_next_scan, (void *)sc, 1342*9999SWang.Lin@Sun.COM drv_usectohz(arn_dwelltime * 1000)); 1343*9999SWang.Lin@Sun.COM ieee80211_next_scan(ic); 1344*9999SWang.Lin@Sun.COM } 1345*9999SWang.Lin@Sun.COM } 1346*9999SWang.Lin@Sun.COM 1347*9999SWang.Lin@Sun.COM static void 1348*9999SWang.Lin@Sun.COM arn_stop_scantimer(struct arn_softc *sc) 1349*9999SWang.Lin@Sun.COM { 1350*9999SWang.Lin@Sun.COM timeout_id_t tmp_id = 0; 1351*9999SWang.Lin@Sun.COM 1352*9999SWang.Lin@Sun.COM while ((sc->sc_scan_timer != 0) && (tmp_id != sc->sc_scan_timer)) { 1353*9999SWang.Lin@Sun.COM tmp_id = sc->sc_scan_timer; 1354*9999SWang.Lin@Sun.COM (void) untimeout(tmp_id); 1355*9999SWang.Lin@Sun.COM } 1356*9999SWang.Lin@Sun.COM sc->sc_scan_timer = 0; 1357*9999SWang.Lin@Sun.COM } 1358*9999SWang.Lin@Sun.COM 1359*9999SWang.Lin@Sun.COM static int32_t 1360*9999SWang.Lin@Sun.COM arn_newstate(ieee80211com_t *ic, enum ieee80211_state nstate, int arg) 1361*9999SWang.Lin@Sun.COM { 1362*9999SWang.Lin@Sun.COM struct arn_softc *sc = (struct arn_softc *)ic; 1363*9999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah; 1364*9999SWang.Lin@Sun.COM struct ieee80211_node *in; 1365*9999SWang.Lin@Sun.COM int32_t i, error; 1366*9999SWang.Lin@Sun.COM uint8_t *bssid; 1367*9999SWang.Lin@Sun.COM uint32_t rfilt; 1368*9999SWang.Lin@Sun.COM enum ieee80211_state ostate; 1369*9999SWang.Lin@Sun.COM struct ath9k_channel *channel; 1370*9999SWang.Lin@Sun.COM int pos; 1371*9999SWang.Lin@Sun.COM 1372*9999SWang.Lin@Sun.COM /* Should set up & init LED here */ 1373*9999SWang.Lin@Sun.COM 1374*9999SWang.Lin@Sun.COM if (sc->sc_flags & SC_OP_INVALID) 1375*9999SWang.Lin@Sun.COM return (0); 1376*9999SWang.Lin@Sun.COM 1377*9999SWang.Lin@Sun.COM ostate = ic->ic_state; 1378*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INIT, "arn: arn_newstate(): " 1379*9999SWang.Lin@Sun.COM "%x -> %x!\n", ostate, nstate)); 1380*9999SWang.Lin@Sun.COM 1381*9999SWang.Lin@Sun.COM ARN_LOCK(sc); 1382*9999SWang.Lin@Sun.COM 1383*9999SWang.Lin@Sun.COM if (nstate != IEEE80211_S_SCAN) 1384*9999SWang.Lin@Sun.COM arn_stop_scantimer(sc); 1385*9999SWang.Lin@Sun.COM if (nstate != IEEE80211_S_RUN) 1386*9999SWang.Lin@Sun.COM arn_stop_caltimer(sc); 1387*9999SWang.Lin@Sun.COM 1388*9999SWang.Lin@Sun.COM /* Should set LED here */ 1389*9999SWang.Lin@Sun.COM 1390*9999SWang.Lin@Sun.COM if (nstate == IEEE80211_S_INIT) { 1391*9999SWang.Lin@Sun.COM sc->sc_imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS); 1392*9999SWang.Lin@Sun.COM /* 1393*9999SWang.Lin@Sun.COM * Disable interrupts. 1394*9999SWang.Lin@Sun.COM */ 1395*9999SWang.Lin@Sun.COM (void) ath9k_hw_set_interrupts 1396*9999SWang.Lin@Sun.COM (ah, sc->sc_imask &~ ATH9K_INT_GLOBAL); 1397*9999SWang.Lin@Sun.COM 1398*9999SWang.Lin@Sun.COM #ifdef ARN_IBSS 1399*9999SWang.Lin@Sun.COM if (ic->ic_opmode == IEEE80211_M_IBSS) { 1400*9999SWang.Lin@Sun.COM (void) ath9k_hw_stoptxdma(ah, sc->sc_beaconq); 1401*9999SWang.Lin@Sun.COM arn_beacon_return(sc); 1402*9999SWang.Lin@Sun.COM } 1403*9999SWang.Lin@Sun.COM #endif 1404*9999SWang.Lin@Sun.COM ARN_UNLOCK(sc); 1405*9999SWang.Lin@Sun.COM ieee80211_stop_watchdog(ic); 1406*9999SWang.Lin@Sun.COM goto done; 1407*9999SWang.Lin@Sun.COM } 1408*9999SWang.Lin@Sun.COM in = ic->ic_bss; 1409*9999SWang.Lin@Sun.COM 1410*9999SWang.Lin@Sun.COM pos = arn_get_channel(sc, ic->ic_curchan); 1411*9999SWang.Lin@Sun.COM 1412*9999SWang.Lin@Sun.COM if (pos == -1) { 1413*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_FATAL, "arn: " 1414*9999SWang.Lin@Sun.COM "%s: Invalid channel\n", __func__)); 1415*9999SWang.Lin@Sun.COM error = EINVAL; 1416*9999SWang.Lin@Sun.COM ARN_UNLOCK(sc); 1417*9999SWang.Lin@Sun.COM goto bad; 1418*9999SWang.Lin@Sun.COM } 1419*9999SWang.Lin@Sun.COM sc->tx_chan_width = ATH9K_HT_MACMODE_20; 1420*9999SWang.Lin@Sun.COM sc->sc_ah->ah_channels[pos].chanmode = 1421*9999SWang.Lin@Sun.COM arn_chan2flags(ic, ic->ic_curchan); 1422*9999SWang.Lin@Sun.COM channel = &sc->sc_ah->ah_channels[pos]; 1423*9999SWang.Lin@Sun.COM if (channel == NULL) { 1424*9999SWang.Lin@Sun.COM arn_problem("arn_newstate(): channel == NULL"); 1425*9999SWang.Lin@Sun.COM ARN_UNLOCK(sc); 1426*9999SWang.Lin@Sun.COM goto bad; 1427*9999SWang.Lin@Sun.COM } 1428*9999SWang.Lin@Sun.COM error = arn_set_channel(sc, channel); 1429*9999SWang.Lin@Sun.COM if (error != 0) { 1430*9999SWang.Lin@Sun.COM if (nstate != IEEE80211_S_SCAN) { 1431*9999SWang.Lin@Sun.COM ARN_UNLOCK(sc); 1432*9999SWang.Lin@Sun.COM ieee80211_reset_chan(ic); 1433*9999SWang.Lin@Sun.COM goto bad; 1434*9999SWang.Lin@Sun.COM } 1435*9999SWang.Lin@Sun.COM } 1436*9999SWang.Lin@Sun.COM 1437*9999SWang.Lin@Sun.COM /* 1438*9999SWang.Lin@Sun.COM * Get the receive filter according to the 1439*9999SWang.Lin@Sun.COM * operating mode and state 1440*9999SWang.Lin@Sun.COM */ 1441*9999SWang.Lin@Sun.COM rfilt = arn_calcrxfilter(sc); 1442*9999SWang.Lin@Sun.COM 1443*9999SWang.Lin@Sun.COM if (nstate == IEEE80211_S_SCAN) 1444*9999SWang.Lin@Sun.COM bssid = ic->ic_macaddr; 1445*9999SWang.Lin@Sun.COM else 1446*9999SWang.Lin@Sun.COM bssid = in->in_bssid; 1447*9999SWang.Lin@Sun.COM 1448*9999SWang.Lin@Sun.COM ath9k_hw_setrxfilter(ah, rfilt); 1449*9999SWang.Lin@Sun.COM 1450*9999SWang.Lin@Sun.COM if (nstate == IEEE80211_S_RUN && ic->ic_opmode != IEEE80211_M_IBSS) 1451*9999SWang.Lin@Sun.COM ath9k_hw_write_associd(ah, bssid, in->in_associd); 1452*9999SWang.Lin@Sun.COM else 1453*9999SWang.Lin@Sun.COM ath9k_hw_write_associd(ah, bssid, 0); 1454*9999SWang.Lin@Sun.COM 1455*9999SWang.Lin@Sun.COM /* Check for WLAN_CAPABILITY_PRIVACY ? */ 1456*9999SWang.Lin@Sun.COM if (ic->ic_flags & IEEE80211_F_PRIVACY) { 1457*9999SWang.Lin@Sun.COM for (i = 0; i < IEEE80211_WEP_NKID; i++) { 1458*9999SWang.Lin@Sun.COM if (ath9k_hw_keyisvalid(ah, (uint16_t)i)) 1459*9999SWang.Lin@Sun.COM (void) ath9k_hw_keysetmac(ah, (uint16_t)i, 1460*9999SWang.Lin@Sun.COM bssid); 1461*9999SWang.Lin@Sun.COM } 1462*9999SWang.Lin@Sun.COM } 1463*9999SWang.Lin@Sun.COM 1464*9999SWang.Lin@Sun.COM if (nstate == IEEE80211_S_RUN) { 1465*9999SWang.Lin@Sun.COM switch (ic->ic_opmode) { 1466*9999SWang.Lin@Sun.COM #ifdef ARN_IBSS 1467*9999SWang.Lin@Sun.COM case IEEE80211_M_IBSS: 1468*9999SWang.Lin@Sun.COM /* 1469*9999SWang.Lin@Sun.COM * Allocate and setup the beacon frame. 1470*9999SWang.Lin@Sun.COM * Stop any previous beacon DMA. 1471*9999SWang.Lin@Sun.COM */ 1472*9999SWang.Lin@Sun.COM (void) ath9k_hw_stoptxdma(ah, sc->sc_beaconq); 1473*9999SWang.Lin@Sun.COM arn_beacon_return(sc); 1474*9999SWang.Lin@Sun.COM error = arn_beacon_alloc(sc, in); 1475*9999SWang.Lin@Sun.COM if (error != 0) { 1476*9999SWang.Lin@Sun.COM ARN_UNLOCK(sc); 1477*9999SWang.Lin@Sun.COM goto bad; 1478*9999SWang.Lin@Sun.COM } 1479*9999SWang.Lin@Sun.COM /* 1480*9999SWang.Lin@Sun.COM * If joining an adhoc network defer beacon timer 1481*9999SWang.Lin@Sun.COM * configuration to the next beacon frame so we 1482*9999SWang.Lin@Sun.COM * have a current TSF to use. Otherwise we're 1483*9999SWang.Lin@Sun.COM * starting an ibss/bss so there's no need to delay. 1484*9999SWang.Lin@Sun.COM */ 1485*9999SWang.Lin@Sun.COM if (ic->ic_opmode == IEEE80211_M_IBSS && 1486*9999SWang.Lin@Sun.COM ic->ic_bss->in_tstamp.tsf != 0) { 1487*9999SWang.Lin@Sun.COM sc->sc_bsync = 1; 1488*9999SWang.Lin@Sun.COM } else { 1489*9999SWang.Lin@Sun.COM arn_beacon_config(sc); 1490*9999SWang.Lin@Sun.COM } 1491*9999SWang.Lin@Sun.COM break; 1492*9999SWang.Lin@Sun.COM #endif /* ARN_IBSS */ 1493*9999SWang.Lin@Sun.COM case IEEE80211_M_STA: 1494*9999SWang.Lin@Sun.COM if (ostate != IEEE80211_S_RUN) { 1495*9999SWang.Lin@Sun.COM /* 1496*9999SWang.Lin@Sun.COM * Defer beacon timer configuration to the next 1497*9999SWang.Lin@Sun.COM * beacon frame so we have a current TSF to use. 1498*9999SWang.Lin@Sun.COM * Any TSF collected when scanning is likely old 1499*9999SWang.Lin@Sun.COM */ 1500*9999SWang.Lin@Sun.COM #ifdef ARN_IBSS 1501*9999SWang.Lin@Sun.COM sc->sc_bsync = 1; 1502*9999SWang.Lin@Sun.COM #else 1503*9999SWang.Lin@Sun.COM /* Configure the beacon and sleep timers. */ 1504*9999SWang.Lin@Sun.COM arn_beacon_config(sc); 1505*9999SWang.Lin@Sun.COM #endif /* ARN_IBSS */ 1506*9999SWang.Lin@Sun.COM } 1507*9999SWang.Lin@Sun.COM break; 1508*9999SWang.Lin@Sun.COM default: 1509*9999SWang.Lin@Sun.COM break; 1510*9999SWang.Lin@Sun.COM } 1511*9999SWang.Lin@Sun.COM } else { 1512*9999SWang.Lin@Sun.COM sc->sc_imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS); 1513*9999SWang.Lin@Sun.COM (void) ath9k_hw_set_interrupts(ah, sc->sc_imask); 1514*9999SWang.Lin@Sun.COM } 1515*9999SWang.Lin@Sun.COM 1516*9999SWang.Lin@Sun.COM /* 1517*9999SWang.Lin@Sun.COM * Reset the rate control state. 1518*9999SWang.Lin@Sun.COM */ 1519*9999SWang.Lin@Sun.COM arn_rate_ctl_reset(sc, nstate); 1520*9999SWang.Lin@Sun.COM 1521*9999SWang.Lin@Sun.COM ARN_UNLOCK(sc); 1522*9999SWang.Lin@Sun.COM done: 1523*9999SWang.Lin@Sun.COM /* 1524*9999SWang.Lin@Sun.COM * Invoke the parent method to complete the work. 1525*9999SWang.Lin@Sun.COM */ 1526*9999SWang.Lin@Sun.COM error = sc->sc_newstate(ic, nstate, arg); 1527*9999SWang.Lin@Sun.COM 1528*9999SWang.Lin@Sun.COM /* 1529*9999SWang.Lin@Sun.COM * Finally, start any timers. 1530*9999SWang.Lin@Sun.COM */ 1531*9999SWang.Lin@Sun.COM if (nstate == IEEE80211_S_RUN) { 1532*9999SWang.Lin@Sun.COM ieee80211_start_watchdog(ic, 1); 1533*9999SWang.Lin@Sun.COM ASSERT(sc->sc_cal_timer == 0); 1534*9999SWang.Lin@Sun.COM sc->sc_cal_timer = timeout(arn_ani_calibrate, (void *)sc, 1535*9999SWang.Lin@Sun.COM drv_usectohz(100 * 1000)); 1536*9999SWang.Lin@Sun.COM } else if ((nstate == IEEE80211_S_SCAN) && (ostate != nstate)) { 1537*9999SWang.Lin@Sun.COM /* start ap/neighbor scan timer */ 1538*9999SWang.Lin@Sun.COM /* ASSERT(sc->sc_scan_timer == 0); */ 1539*9999SWang.Lin@Sun.COM if (sc->sc_scan_timer != 0) { 1540*9999SWang.Lin@Sun.COM (void) untimeout(sc->sc_scan_timer); 1541*9999SWang.Lin@Sun.COM sc->sc_scan_timer = 0; 1542*9999SWang.Lin@Sun.COM } 1543*9999SWang.Lin@Sun.COM sc->sc_scan_timer = timeout(arn_next_scan, (void *)sc, 1544*9999SWang.Lin@Sun.COM drv_usectohz(arn_dwelltime * 1000)); 1545*9999SWang.Lin@Sun.COM } 1546*9999SWang.Lin@Sun.COM 1547*9999SWang.Lin@Sun.COM bad: 1548*9999SWang.Lin@Sun.COM return (error); 1549*9999SWang.Lin@Sun.COM } 1550*9999SWang.Lin@Sun.COM 1551*9999SWang.Lin@Sun.COM static void 1552*9999SWang.Lin@Sun.COM arn_watchdog(void *arg) 1553*9999SWang.Lin@Sun.COM { 1554*9999SWang.Lin@Sun.COM struct arn_softc *sc = arg; 1555*9999SWang.Lin@Sun.COM ieee80211com_t *ic = &sc->sc_isc; 1556*9999SWang.Lin@Sun.COM int ntimer = 0; 1557*9999SWang.Lin@Sun.COM 1558*9999SWang.Lin@Sun.COM ARN_LOCK(sc); 1559*9999SWang.Lin@Sun.COM ic->ic_watchdog_timer = 0; 1560*9999SWang.Lin@Sun.COM if (sc->sc_flags & SC_OP_INVALID) { 1561*9999SWang.Lin@Sun.COM ARN_UNLOCK(sc); 1562*9999SWang.Lin@Sun.COM return; 1563*9999SWang.Lin@Sun.COM } 1564*9999SWang.Lin@Sun.COM 1565*9999SWang.Lin@Sun.COM if (ic->ic_state == IEEE80211_S_RUN) { 1566*9999SWang.Lin@Sun.COM /* 1567*9999SWang.Lin@Sun.COM * Start the background rate control thread if we 1568*9999SWang.Lin@Sun.COM * are not configured to use a fixed xmit rate. 1569*9999SWang.Lin@Sun.COM */ 1570*9999SWang.Lin@Sun.COM if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) { 1571*9999SWang.Lin@Sun.COM sc->sc_stats.ast_rate_calls ++; 1572*9999SWang.Lin@Sun.COM if (ic->ic_opmode == IEEE80211_M_STA) 1573*9999SWang.Lin@Sun.COM arn_rate_ctl(ic, ic->ic_bss); 1574*9999SWang.Lin@Sun.COM else 1575*9999SWang.Lin@Sun.COM ieee80211_iterate_nodes(&ic->ic_sta, 1576*9999SWang.Lin@Sun.COM arn_rate_ctl, sc); 1577*9999SWang.Lin@Sun.COM } 1578*9999SWang.Lin@Sun.COM 1579*9999SWang.Lin@Sun.COM ntimer = 1; 1580*9999SWang.Lin@Sun.COM } 1581*9999SWang.Lin@Sun.COM ARN_UNLOCK(sc); 1582*9999SWang.Lin@Sun.COM 1583*9999SWang.Lin@Sun.COM ieee80211_watchdog(ic); 1584*9999SWang.Lin@Sun.COM if (ntimer != 0) 1585*9999SWang.Lin@Sun.COM ieee80211_start_watchdog(ic, ntimer); 1586*9999SWang.Lin@Sun.COM } 1587*9999SWang.Lin@Sun.COM 1588*9999SWang.Lin@Sun.COM static struct ieee80211_node * 1589*9999SWang.Lin@Sun.COM arn_node_alloc(ieee80211com_t *ic) 1590*9999SWang.Lin@Sun.COM { 1591*9999SWang.Lin@Sun.COM struct ath_node *an; 1592*9999SWang.Lin@Sun.COM struct arn_softc *sc = (struct arn_softc *)ic; 1593*9999SWang.Lin@Sun.COM 1594*9999SWang.Lin@Sun.COM an = kmem_zalloc(sizeof (struct ath_node), KM_SLEEP); 1595*9999SWang.Lin@Sun.COM arn_rate_update(sc, &an->an_node, 0); 1596*9999SWang.Lin@Sun.COM 1597*9999SWang.Lin@Sun.COM return ((an != NULL) ? &an->an_node : NULL); 1598*9999SWang.Lin@Sun.COM } 1599*9999SWang.Lin@Sun.COM 1600*9999SWang.Lin@Sun.COM static void 1601*9999SWang.Lin@Sun.COM arn_node_free(struct ieee80211_node *in) 1602*9999SWang.Lin@Sun.COM { 1603*9999SWang.Lin@Sun.COM ieee80211com_t *ic = in->in_ic; 1604*9999SWang.Lin@Sun.COM struct arn_softc *sc = (struct arn_softc *)ic; 1605*9999SWang.Lin@Sun.COM struct ath_buf *bf; 1606*9999SWang.Lin@Sun.COM struct ath_txq *txq; 1607*9999SWang.Lin@Sun.COM int32_t i; 1608*9999SWang.Lin@Sun.COM 1609*9999SWang.Lin@Sun.COM for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { 1610*9999SWang.Lin@Sun.COM if (ARN_TXQ_SETUP(sc, i)) { 1611*9999SWang.Lin@Sun.COM txq = &sc->sc_txq[i]; 1612*9999SWang.Lin@Sun.COM mutex_enter(&txq->axq_lock); 1613*9999SWang.Lin@Sun.COM bf = list_head(&txq->axq_list); 1614*9999SWang.Lin@Sun.COM while (bf != NULL) { 1615*9999SWang.Lin@Sun.COM if (bf->bf_in == in) { 1616*9999SWang.Lin@Sun.COM bf->bf_in = NULL; 1617*9999SWang.Lin@Sun.COM } 1618*9999SWang.Lin@Sun.COM bf = list_next(&txq->axq_list, bf); 1619*9999SWang.Lin@Sun.COM } 1620*9999SWang.Lin@Sun.COM mutex_exit(&txq->axq_lock); 1621*9999SWang.Lin@Sun.COM } 1622*9999SWang.Lin@Sun.COM } 1623*9999SWang.Lin@Sun.COM 1624*9999SWang.Lin@Sun.COM ic->ic_node_cleanup(in); 1625*9999SWang.Lin@Sun.COM if (in->in_wpa_ie != NULL) 1626*9999SWang.Lin@Sun.COM ieee80211_free(in->in_wpa_ie); 1627*9999SWang.Lin@Sun.COM kmem_free(in, sizeof (struct ath_node)); 1628*9999SWang.Lin@Sun.COM } 1629*9999SWang.Lin@Sun.COM 1630*9999SWang.Lin@Sun.COM /* 1631*9999SWang.Lin@Sun.COM * Allocate tx/rx key slots for TKIP. We allocate one slot for 1632*9999SWang.Lin@Sun.COM * each key. MIC is right after the decrypt/encrypt key. 1633*9999SWang.Lin@Sun.COM */ 1634*9999SWang.Lin@Sun.COM static uint16_t 1635*9999SWang.Lin@Sun.COM arn_key_alloc_pair(struct arn_softc *sc, ieee80211_keyix *txkeyix, 1636*9999SWang.Lin@Sun.COM ieee80211_keyix *rxkeyix) 1637*9999SWang.Lin@Sun.COM { 1638*9999SWang.Lin@Sun.COM uint16_t i, keyix; 1639*9999SWang.Lin@Sun.COM 1640*9999SWang.Lin@Sun.COM ASSERT(!sc->sc_splitmic); 1641*9999SWang.Lin@Sun.COM for (i = 0; i < ARRAY_SIZE(sc->sc_keymap)/4; i++) { 1642*9999SWang.Lin@Sun.COM uint8_t b = sc->sc_keymap[i]; 1643*9999SWang.Lin@Sun.COM if (b == 0xff) 1644*9999SWang.Lin@Sun.COM continue; 1645*9999SWang.Lin@Sun.COM for (keyix = i * NBBY; keyix < (i + 1) * NBBY; 1646*9999SWang.Lin@Sun.COM keyix++, b >>= 1) { 1647*9999SWang.Lin@Sun.COM if ((b & 1) || is_set(keyix+64, sc->sc_keymap)) { 1648*9999SWang.Lin@Sun.COM /* full pair unavailable */ 1649*9999SWang.Lin@Sun.COM continue; 1650*9999SWang.Lin@Sun.COM } 1651*9999SWang.Lin@Sun.COM set_bit(keyix, sc->sc_keymap); 1652*9999SWang.Lin@Sun.COM set_bit(keyix+64, sc->sc_keymap); 1653*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_KEYCACHE, 1654*9999SWang.Lin@Sun.COM "arn_key_alloc_pair(): key pair %u,%u\n", 1655*9999SWang.Lin@Sun.COM keyix, keyix+64)); 1656*9999SWang.Lin@Sun.COM *txkeyix = *rxkeyix = keyix; 1657*9999SWang.Lin@Sun.COM return (1); 1658*9999SWang.Lin@Sun.COM } 1659*9999SWang.Lin@Sun.COM } 1660*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_KEYCACHE, "arn_key_alloc_pair():" 1661*9999SWang.Lin@Sun.COM " out of pair space\n")); 1662*9999SWang.Lin@Sun.COM 1663*9999SWang.Lin@Sun.COM return (0); 1664*9999SWang.Lin@Sun.COM } 1665*9999SWang.Lin@Sun.COM 1666*9999SWang.Lin@Sun.COM /* 1667*9999SWang.Lin@Sun.COM * Allocate tx/rx key slots for TKIP. We allocate two slots for 1668*9999SWang.Lin@Sun.COM * each key, one for decrypt/encrypt and the other for the MIC. 1669*9999SWang.Lin@Sun.COM */ 1670*9999SWang.Lin@Sun.COM static int 1671*9999SWang.Lin@Sun.COM arn_key_alloc_2pair(struct arn_softc *sc, ieee80211_keyix *txkeyix, 1672*9999SWang.Lin@Sun.COM ieee80211_keyix *rxkeyix) 1673*9999SWang.Lin@Sun.COM { 1674*9999SWang.Lin@Sun.COM uint16_t i, keyix; 1675*9999SWang.Lin@Sun.COM 1676*9999SWang.Lin@Sun.COM ASSERT(sc->sc_splitmic); 1677*9999SWang.Lin@Sun.COM for (i = 0; i < ARRAY_SIZE(sc->sc_keymap)/4; i++) { 1678*9999SWang.Lin@Sun.COM uint8_t b = sc->sc_keymap[i]; 1679*9999SWang.Lin@Sun.COM if (b != 0xff) { 1680*9999SWang.Lin@Sun.COM /* 1681*9999SWang.Lin@Sun.COM * One or more slots in this byte are free. 1682*9999SWang.Lin@Sun.COM */ 1683*9999SWang.Lin@Sun.COM keyix = i*NBBY; 1684*9999SWang.Lin@Sun.COM while (b & 1) { 1685*9999SWang.Lin@Sun.COM again: 1686*9999SWang.Lin@Sun.COM keyix++; 1687*9999SWang.Lin@Sun.COM b >>= 1; 1688*9999SWang.Lin@Sun.COM } 1689*9999SWang.Lin@Sun.COM /* XXX IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV */ 1690*9999SWang.Lin@Sun.COM if (is_set(keyix+32, sc->sc_keymap) || 1691*9999SWang.Lin@Sun.COM is_set(keyix+64, sc->sc_keymap) || 1692*9999SWang.Lin@Sun.COM is_set(keyix+32+64, sc->sc_keymap)) { 1693*9999SWang.Lin@Sun.COM /* full pair unavailable */ 1694*9999SWang.Lin@Sun.COM if (keyix == (i+1)*NBBY) { 1695*9999SWang.Lin@Sun.COM /* no slots were appropriate, advance */ 1696*9999SWang.Lin@Sun.COM continue; 1697*9999SWang.Lin@Sun.COM } 1698*9999SWang.Lin@Sun.COM goto again; 1699*9999SWang.Lin@Sun.COM } 1700*9999SWang.Lin@Sun.COM set_bit(keyix, sc->sc_keymap); 1701*9999SWang.Lin@Sun.COM set_bit(keyix+64, sc->sc_keymap); 1702*9999SWang.Lin@Sun.COM set_bit(keyix+32, sc->sc_keymap); 1703*9999SWang.Lin@Sun.COM set_bit(keyix+32+64, sc->sc_keymap); 1704*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_KEYCACHE, 1705*9999SWang.Lin@Sun.COM "arn_key_alloc_2pair(): key pair %u,%u %u,%u\n", 1706*9999SWang.Lin@Sun.COM keyix, keyix+64, 1707*9999SWang.Lin@Sun.COM keyix+32, keyix+32+64)); 1708*9999SWang.Lin@Sun.COM *txkeyix = *rxkeyix = keyix; 1709*9999SWang.Lin@Sun.COM return (1); 1710*9999SWang.Lin@Sun.COM } 1711*9999SWang.Lin@Sun.COM } 1712*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_KEYCACHE, "arn_key_alloc_2pair(): " 1713*9999SWang.Lin@Sun.COM " out of pair space\n")); 1714*9999SWang.Lin@Sun.COM 1715*9999SWang.Lin@Sun.COM return (0); 1716*9999SWang.Lin@Sun.COM } 1717*9999SWang.Lin@Sun.COM /* 1718*9999SWang.Lin@Sun.COM * Allocate a single key cache slot. 1719*9999SWang.Lin@Sun.COM */ 1720*9999SWang.Lin@Sun.COM static int 1721*9999SWang.Lin@Sun.COM arn_key_alloc_single(struct arn_softc *sc, ieee80211_keyix *txkeyix, 1722*9999SWang.Lin@Sun.COM ieee80211_keyix *rxkeyix) 1723*9999SWang.Lin@Sun.COM { 1724*9999SWang.Lin@Sun.COM uint16_t i, keyix; 1725*9999SWang.Lin@Sun.COM 1726*9999SWang.Lin@Sun.COM /* try i,i+32,i+64,i+32+64 to minimize key pair conflicts */ 1727*9999SWang.Lin@Sun.COM for (i = 0; i < ARRAY_SIZE(sc->sc_keymap); i++) { 1728*9999SWang.Lin@Sun.COM uint8_t b = sc->sc_keymap[i]; 1729*9999SWang.Lin@Sun.COM 1730*9999SWang.Lin@Sun.COM if (b != 0xff) { 1731*9999SWang.Lin@Sun.COM /* 1732*9999SWang.Lin@Sun.COM * One or more slots are free. 1733*9999SWang.Lin@Sun.COM */ 1734*9999SWang.Lin@Sun.COM keyix = i*NBBY; 1735*9999SWang.Lin@Sun.COM while (b & 1) 1736*9999SWang.Lin@Sun.COM keyix++, b >>= 1; 1737*9999SWang.Lin@Sun.COM set_bit(keyix, sc->sc_keymap); 1738*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_KEYCACHE, "arn_key_alloc_single(): " 1739*9999SWang.Lin@Sun.COM "key %u\n", keyix)); 1740*9999SWang.Lin@Sun.COM *txkeyix = *rxkeyix = keyix; 1741*9999SWang.Lin@Sun.COM return (1); 1742*9999SWang.Lin@Sun.COM } 1743*9999SWang.Lin@Sun.COM } 1744*9999SWang.Lin@Sun.COM return (0); 1745*9999SWang.Lin@Sun.COM } 1746*9999SWang.Lin@Sun.COM 1747*9999SWang.Lin@Sun.COM /* 1748*9999SWang.Lin@Sun.COM * Allocate one or more key cache slots for a unicast key. The 1749*9999SWang.Lin@Sun.COM * key itself is needed only to identify the cipher. For hardware 1750*9999SWang.Lin@Sun.COM * TKIP with split cipher+MIC keys we allocate two key cache slot 1751*9999SWang.Lin@Sun.COM * pairs so that we can setup separate TX and RX MIC keys. Note 1752*9999SWang.Lin@Sun.COM * that the MIC key for a TKIP key at slot i is assumed by the 1753*9999SWang.Lin@Sun.COM * hardware to be at slot i+64. This limits TKIP keys to the first 1754*9999SWang.Lin@Sun.COM * 64 entries. 1755*9999SWang.Lin@Sun.COM */ 1756*9999SWang.Lin@Sun.COM /* ARGSUSED */ 1757*9999SWang.Lin@Sun.COM int 1758*9999SWang.Lin@Sun.COM arn_key_alloc(ieee80211com_t *ic, const struct ieee80211_key *k, 1759*9999SWang.Lin@Sun.COM ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix) 1760*9999SWang.Lin@Sun.COM { 1761*9999SWang.Lin@Sun.COM struct arn_softc *sc = (struct arn_softc *)ic; 1762*9999SWang.Lin@Sun.COM 1763*9999SWang.Lin@Sun.COM /* 1764*9999SWang.Lin@Sun.COM * We allocate two pair for TKIP when using the h/w to do 1765*9999SWang.Lin@Sun.COM * the MIC. For everything else, including software crypto, 1766*9999SWang.Lin@Sun.COM * we allocate a single entry. Note that s/w crypto requires 1767*9999SWang.Lin@Sun.COM * a pass-through slot on the 5211 and 5212. The 5210 does 1768*9999SWang.Lin@Sun.COM * not support pass-through cache entries and we map all 1769*9999SWang.Lin@Sun.COM * those requests to slot 0. 1770*9999SWang.Lin@Sun.COM */ 1771*9999SWang.Lin@Sun.COM if (k->wk_flags & IEEE80211_KEY_SWCRYPT) { 1772*9999SWang.Lin@Sun.COM return (arn_key_alloc_single(sc, keyix, rxkeyix)); 1773*9999SWang.Lin@Sun.COM } else if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP && 1774*9999SWang.Lin@Sun.COM (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) { 1775*9999SWang.Lin@Sun.COM if (sc->sc_splitmic) 1776*9999SWang.Lin@Sun.COM return (arn_key_alloc_2pair(sc, keyix, rxkeyix)); 1777*9999SWang.Lin@Sun.COM else 1778*9999SWang.Lin@Sun.COM return (arn_key_alloc_pair(sc, keyix, rxkeyix)); 1779*9999SWang.Lin@Sun.COM } else { 1780*9999SWang.Lin@Sun.COM return (arn_key_alloc_single(sc, keyix, rxkeyix)); 1781*9999SWang.Lin@Sun.COM } 1782*9999SWang.Lin@Sun.COM } 1783*9999SWang.Lin@Sun.COM 1784*9999SWang.Lin@Sun.COM /* 1785*9999SWang.Lin@Sun.COM * Delete an entry in the key cache allocated by ath_key_alloc. 1786*9999SWang.Lin@Sun.COM */ 1787*9999SWang.Lin@Sun.COM int 1788*9999SWang.Lin@Sun.COM arn_key_delete(ieee80211com_t *ic, const struct ieee80211_key *k) 1789*9999SWang.Lin@Sun.COM { 1790*9999SWang.Lin@Sun.COM struct arn_softc *sc = (struct arn_softc *)ic; 1791*9999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah; 1792*9999SWang.Lin@Sun.COM const struct ieee80211_cipher *cip = k->wk_cipher; 1793*9999SWang.Lin@Sun.COM ieee80211_keyix keyix = k->wk_keyix; 1794*9999SWang.Lin@Sun.COM 1795*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_KEYCACHE, "arn_key_delete():" 1796*9999SWang.Lin@Sun.COM " delete key %u ic_cipher=0x%x\n", keyix, cip->ic_cipher)); 1797*9999SWang.Lin@Sun.COM 1798*9999SWang.Lin@Sun.COM (void) ath9k_hw_keyreset(ah, keyix); 1799*9999SWang.Lin@Sun.COM /* 1800*9999SWang.Lin@Sun.COM * Handle split tx/rx keying required for TKIP with h/w MIC. 1801*9999SWang.Lin@Sun.COM */ 1802*9999SWang.Lin@Sun.COM if (cip->ic_cipher == IEEE80211_CIPHER_TKIP && 1803*9999SWang.Lin@Sun.COM (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && sc->sc_splitmic) 1804*9999SWang.Lin@Sun.COM (void) ath9k_hw_keyreset(ah, keyix+32); /* RX key */ 1805*9999SWang.Lin@Sun.COM 1806*9999SWang.Lin@Sun.COM if (keyix >= IEEE80211_WEP_NKID) { 1807*9999SWang.Lin@Sun.COM /* 1808*9999SWang.Lin@Sun.COM * Don't touch keymap entries for global keys so 1809*9999SWang.Lin@Sun.COM * they are never considered for dynamic allocation. 1810*9999SWang.Lin@Sun.COM */ 1811*9999SWang.Lin@Sun.COM clr_bit(keyix, sc->sc_keymap); 1812*9999SWang.Lin@Sun.COM if (cip->ic_cipher == IEEE80211_CIPHER_TKIP && 1813*9999SWang.Lin@Sun.COM (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) { 1814*9999SWang.Lin@Sun.COM /* 1815*9999SWang.Lin@Sun.COM * If splitmic is true +64 is TX key MIC, 1816*9999SWang.Lin@Sun.COM * else +64 is RX key + RX key MIC. 1817*9999SWang.Lin@Sun.COM */ 1818*9999SWang.Lin@Sun.COM clr_bit(keyix+64, sc->sc_keymap); 1819*9999SWang.Lin@Sun.COM if (sc->sc_splitmic) { 1820*9999SWang.Lin@Sun.COM /* Rx key */ 1821*9999SWang.Lin@Sun.COM clr_bit(keyix+32, sc->sc_keymap); 1822*9999SWang.Lin@Sun.COM /* RX key MIC */ 1823*9999SWang.Lin@Sun.COM clr_bit(keyix+32+64, sc->sc_keymap); 1824*9999SWang.Lin@Sun.COM } 1825*9999SWang.Lin@Sun.COM } 1826*9999SWang.Lin@Sun.COM } 1827*9999SWang.Lin@Sun.COM return (1); 1828*9999SWang.Lin@Sun.COM } 1829*9999SWang.Lin@Sun.COM 1830*9999SWang.Lin@Sun.COM /* 1831*9999SWang.Lin@Sun.COM * Set a TKIP key into the hardware. This handles the 1832*9999SWang.Lin@Sun.COM * potential distribution of key state to multiple key 1833*9999SWang.Lin@Sun.COM * cache slots for TKIP. 1834*9999SWang.Lin@Sun.COM */ 1835*9999SWang.Lin@Sun.COM static int 1836*9999SWang.Lin@Sun.COM arn_keyset_tkip(struct arn_softc *sc, const struct ieee80211_key *k, 1837*9999SWang.Lin@Sun.COM struct ath9k_keyval *hk, const uint8_t mac[IEEE80211_ADDR_LEN]) 1838*9999SWang.Lin@Sun.COM { 1839*9999SWang.Lin@Sun.COM uint8_t *key_rxmic = NULL; 1840*9999SWang.Lin@Sun.COM uint8_t *key_txmic = NULL; 1841*9999SWang.Lin@Sun.COM uint8_t *key = (uint8_t *)&(k->wk_key[0]); 1842*9999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah; 1843*9999SWang.Lin@Sun.COM 1844*9999SWang.Lin@Sun.COM key_txmic = key + 16; 1845*9999SWang.Lin@Sun.COM key_rxmic = key + 24; 1846*9999SWang.Lin@Sun.COM 1847*9999SWang.Lin@Sun.COM if (mac == NULL) { 1848*9999SWang.Lin@Sun.COM /* Group key installation */ 1849*9999SWang.Lin@Sun.COM (void) memcpy(hk->kv_mic, key_rxmic, sizeof (hk->kv_mic)); 1850*9999SWang.Lin@Sun.COM return (ath9k_hw_set_keycache_entry(ah, k->wk_keyix, hk, 1851*9999SWang.Lin@Sun.COM mac, B_FALSE)); 1852*9999SWang.Lin@Sun.COM } 1853*9999SWang.Lin@Sun.COM if (!sc->sc_splitmic) { 1854*9999SWang.Lin@Sun.COM /* 1855*9999SWang.Lin@Sun.COM * data key goes at first index, 1856*9999SWang.Lin@Sun.COM * the hal handles the MIC keys at index+64. 1857*9999SWang.Lin@Sun.COM */ 1858*9999SWang.Lin@Sun.COM (void) memcpy(hk->kv_mic, key_rxmic, sizeof (hk->kv_mic)); 1859*9999SWang.Lin@Sun.COM (void) memcpy(hk->kv_txmic, key_txmic, sizeof (hk->kv_txmic)); 1860*9999SWang.Lin@Sun.COM return (ath9k_hw_set_keycache_entry(ah, k->wk_keyix, hk, 1861*9999SWang.Lin@Sun.COM mac, B_FALSE)); 1862*9999SWang.Lin@Sun.COM } 1863*9999SWang.Lin@Sun.COM /* 1864*9999SWang.Lin@Sun.COM * TX key goes at first index, RX key at +32. 1865*9999SWang.Lin@Sun.COM * The hal handles the MIC keys at index+64. 1866*9999SWang.Lin@Sun.COM */ 1867*9999SWang.Lin@Sun.COM (void) memcpy(hk->kv_mic, key_txmic, sizeof (hk->kv_mic)); 1868*9999SWang.Lin@Sun.COM if (!(ath9k_hw_set_keycache_entry(ah, k->wk_keyix, hk, NULL, 1869*9999SWang.Lin@Sun.COM B_FALSE))) { 1870*9999SWang.Lin@Sun.COM /* Txmic entry failed. No need to proceed further */ 1871*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_KEYCACHE, 1872*9999SWang.Lin@Sun.COM "%s Setting TX MIC Key Failed\n", __func__)); 1873*9999SWang.Lin@Sun.COM return (0); 1874*9999SWang.Lin@Sun.COM } 1875*9999SWang.Lin@Sun.COM 1876*9999SWang.Lin@Sun.COM (void) memcpy(hk->kv_mic, key_rxmic, sizeof (hk->kv_mic)); 1877*9999SWang.Lin@Sun.COM 1878*9999SWang.Lin@Sun.COM /* XXX delete tx key on failure? */ 1879*9999SWang.Lin@Sun.COM return (ath9k_hw_set_keycache_entry(ah, k->wk_keyix, hk, mac, B_FALSE)); 1880*9999SWang.Lin@Sun.COM 1881*9999SWang.Lin@Sun.COM } 1882*9999SWang.Lin@Sun.COM 1883*9999SWang.Lin@Sun.COM int 1884*9999SWang.Lin@Sun.COM arn_key_set(ieee80211com_t *ic, const struct ieee80211_key *k, 1885*9999SWang.Lin@Sun.COM const uint8_t mac[IEEE80211_ADDR_LEN]) 1886*9999SWang.Lin@Sun.COM { 1887*9999SWang.Lin@Sun.COM struct arn_softc *sc = (struct arn_softc *)ic; 1888*9999SWang.Lin@Sun.COM const struct ieee80211_cipher *cip = k->wk_cipher; 1889*9999SWang.Lin@Sun.COM struct ath9k_keyval hk; 1890*9999SWang.Lin@Sun.COM 1891*9999SWang.Lin@Sun.COM /* cipher table */ 1892*9999SWang.Lin@Sun.COM static const uint8_t ciphermap[] = { 1893*9999SWang.Lin@Sun.COM ATH9K_CIPHER_WEP, /* IEEE80211_CIPHER_WEP */ 1894*9999SWang.Lin@Sun.COM ATH9K_CIPHER_TKIP, /* IEEE80211_CIPHER_TKIP */ 1895*9999SWang.Lin@Sun.COM ATH9K_CIPHER_AES_OCB, /* IEEE80211_CIPHER_AES_OCB */ 1896*9999SWang.Lin@Sun.COM ATH9K_CIPHER_AES_CCM, /* IEEE80211_CIPHER_AES_CCM */ 1897*9999SWang.Lin@Sun.COM ATH9K_CIPHER_CKIP, /* IEEE80211_CIPHER_CKIP */ 1898*9999SWang.Lin@Sun.COM ATH9K_CIPHER_CLR, /* IEEE80211_CIPHER_NONE */ 1899*9999SWang.Lin@Sun.COM }; 1900*9999SWang.Lin@Sun.COM 1901*9999SWang.Lin@Sun.COM bzero(&hk, sizeof (hk)); 1902*9999SWang.Lin@Sun.COM 1903*9999SWang.Lin@Sun.COM /* 1904*9999SWang.Lin@Sun.COM * Software crypto uses a "clear key" so non-crypto 1905*9999SWang.Lin@Sun.COM * state kept in the key cache are maintainedd so that 1906*9999SWang.Lin@Sun.COM * rx frames have an entry to match. 1907*9999SWang.Lin@Sun.COM */ 1908*9999SWang.Lin@Sun.COM if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) == 0) { 1909*9999SWang.Lin@Sun.COM ASSERT(cip->ic_cipher < 6); 1910*9999SWang.Lin@Sun.COM hk.kv_type = ciphermap[cip->ic_cipher]; 1911*9999SWang.Lin@Sun.COM hk.kv_len = k->wk_keylen; 1912*9999SWang.Lin@Sun.COM bcopy(k->wk_key, hk.kv_val, k->wk_keylen); 1913*9999SWang.Lin@Sun.COM } else { 1914*9999SWang.Lin@Sun.COM hk.kv_type = ATH9K_CIPHER_CLR; 1915*9999SWang.Lin@Sun.COM } 1916*9999SWang.Lin@Sun.COM 1917*9999SWang.Lin@Sun.COM if (hk.kv_type == ATH9K_CIPHER_TKIP && 1918*9999SWang.Lin@Sun.COM (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) { 1919*9999SWang.Lin@Sun.COM return (arn_keyset_tkip(sc, k, &hk, mac)); 1920*9999SWang.Lin@Sun.COM } else { 1921*9999SWang.Lin@Sun.COM return (ath9k_hw_set_keycache_entry(sc->sc_ah, 1922*9999SWang.Lin@Sun.COM k->wk_keyix, &hk, mac, B_FALSE)); 1923*9999SWang.Lin@Sun.COM } 1924*9999SWang.Lin@Sun.COM } 1925*9999SWang.Lin@Sun.COM 1926*9999SWang.Lin@Sun.COM /* 1927*9999SWang.Lin@Sun.COM * Enable/Disable short slot timing 1928*9999SWang.Lin@Sun.COM */ 1929*9999SWang.Lin@Sun.COM void 1930*9999SWang.Lin@Sun.COM arn_set_shortslot(ieee80211com_t *ic, int onoff) 1931*9999SWang.Lin@Sun.COM { 1932*9999SWang.Lin@Sun.COM struct ath_hal *ah = ((struct arn_softc *)ic)->sc_ah; 1933*9999SWang.Lin@Sun.COM 1934*9999SWang.Lin@Sun.COM if (onoff) 1935*9999SWang.Lin@Sun.COM (void) ath9k_hw_setslottime(ah, ATH9K_SLOT_TIME_9); 1936*9999SWang.Lin@Sun.COM else 1937*9999SWang.Lin@Sun.COM (void) ath9k_hw_setslottime(ah, ATH9K_SLOT_TIME_20); 1938*9999SWang.Lin@Sun.COM } 1939*9999SWang.Lin@Sun.COM 1940*9999SWang.Lin@Sun.COM static int 1941*9999SWang.Lin@Sun.COM arn_open(struct arn_softc *sc) 1942*9999SWang.Lin@Sun.COM { 1943*9999SWang.Lin@Sun.COM ieee80211com_t *ic = (ieee80211com_t *)sc; 1944*9999SWang.Lin@Sun.COM struct ieee80211_channel *curchan = ic->ic_curchan; 1945*9999SWang.Lin@Sun.COM struct ath9k_channel *init_channel; 1946*9999SWang.Lin@Sun.COM int error = 0, pos, status; 1947*9999SWang.Lin@Sun.COM 1948*9999SWang.Lin@Sun.COM ARN_LOCK_ASSERT(sc); 1949*9999SWang.Lin@Sun.COM 1950*9999SWang.Lin@Sun.COM pos = arn_get_channel(sc, curchan); 1951*9999SWang.Lin@Sun.COM if (pos == -1) { 1952*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_FATAL, "arn: " 1953*9999SWang.Lin@Sun.COM "%s: Invalid channel\n", __func__)); 1954*9999SWang.Lin@Sun.COM error = EINVAL; 1955*9999SWang.Lin@Sun.COM goto error; 1956*9999SWang.Lin@Sun.COM } 1957*9999SWang.Lin@Sun.COM 1958*9999SWang.Lin@Sun.COM sc->tx_chan_width = ATH9K_HT_MACMODE_20; 1959*9999SWang.Lin@Sun.COM 1960*9999SWang.Lin@Sun.COM if (sc->sc_curmode == ATH9K_MODE_11A) { 1961*9999SWang.Lin@Sun.COM sc->sc_ah->ah_channels[pos].chanmode = CHANNEL_A; 1962*9999SWang.Lin@Sun.COM } else { 1963*9999SWang.Lin@Sun.COM sc->sc_ah->ah_channels[pos].chanmode = CHANNEL_G; 1964*9999SWang.Lin@Sun.COM } 1965*9999SWang.Lin@Sun.COM 1966*9999SWang.Lin@Sun.COM init_channel = &sc->sc_ah->ah_channels[pos]; 1967*9999SWang.Lin@Sun.COM 1968*9999SWang.Lin@Sun.COM /* Reset SERDES registers */ 1969*9999SWang.Lin@Sun.COM ath9k_hw_configpcipowersave(sc->sc_ah, 0); 1970*9999SWang.Lin@Sun.COM 1971*9999SWang.Lin@Sun.COM /* 1972*9999SWang.Lin@Sun.COM * The basic interface to setting the hardware in a good 1973*9999SWang.Lin@Sun.COM * state is ``reset''. On return the hardware is known to 1974*9999SWang.Lin@Sun.COM * be powered up and with interrupts disabled. This must 1975*9999SWang.Lin@Sun.COM * be followed by initialization of the appropriate bits 1976*9999SWang.Lin@Sun.COM * and then setup of the interrupt mask. 1977*9999SWang.Lin@Sun.COM */ 1978*9999SWang.Lin@Sun.COM if (!ath9k_hw_reset(sc->sc_ah, init_channel, 1979*9999SWang.Lin@Sun.COM sc->tx_chan_width, sc->sc_tx_chainmask, 1980*9999SWang.Lin@Sun.COM sc->sc_rx_chainmask, sc->sc_ht_extprotspacing, 1981*9999SWang.Lin@Sun.COM B_FALSE, &status)) { 1982*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_FATAL, "arn: " 1983*9999SWang.Lin@Sun.COM "%s: unable to reset hardware; hal status %u " 1984*9999SWang.Lin@Sun.COM "(freq %u flags 0x%x)\n", __func__, status, 1985*9999SWang.Lin@Sun.COM init_channel->channel, init_channel->channelFlags)); 1986*9999SWang.Lin@Sun.COM 1987*9999SWang.Lin@Sun.COM error = EIO; 1988*9999SWang.Lin@Sun.COM goto error; 1989*9999SWang.Lin@Sun.COM } 1990*9999SWang.Lin@Sun.COM 1991*9999SWang.Lin@Sun.COM /* 1992*9999SWang.Lin@Sun.COM * This is needed only to setup initial state 1993*9999SWang.Lin@Sun.COM * but it's best done after a reset. 1994*9999SWang.Lin@Sun.COM */ 1995*9999SWang.Lin@Sun.COM arn_update_txpow(sc); 1996*9999SWang.Lin@Sun.COM 1997*9999SWang.Lin@Sun.COM /* 1998*9999SWang.Lin@Sun.COM * Setup the hardware after reset: 1999*9999SWang.Lin@Sun.COM * The receive engine is set going. 2000*9999SWang.Lin@Sun.COM * Frame transmit is handled entirely 2001*9999SWang.Lin@Sun.COM * in the frame output path; there's nothing to do 2002*9999SWang.Lin@Sun.COM * here except setup the interrupt mask. 2003*9999SWang.Lin@Sun.COM */ 2004*9999SWang.Lin@Sun.COM if (arn_startrecv(sc) != 0) { 2005*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INIT, "arn: " 2006*9999SWang.Lin@Sun.COM "%s: unable to start recv logic\n", __func__)); 2007*9999SWang.Lin@Sun.COM error = EIO; 2008*9999SWang.Lin@Sun.COM goto error; 2009*9999SWang.Lin@Sun.COM } 2010*9999SWang.Lin@Sun.COM 2011*9999SWang.Lin@Sun.COM /* Setup our intr mask. */ 2012*9999SWang.Lin@Sun.COM sc->sc_imask = ATH9K_INT_RX | ATH9K_INT_TX | 2013*9999SWang.Lin@Sun.COM ATH9K_INT_RXEOL | ATH9K_INT_RXORN | 2014*9999SWang.Lin@Sun.COM ATH9K_INT_FATAL | ATH9K_INT_GLOBAL; 2015*9999SWang.Lin@Sun.COM #ifdef ARN_ATH9K_HW_CAP_GTT 2016*9999SWang.Lin@Sun.COM if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_GTT) 2017*9999SWang.Lin@Sun.COM sc->sc_imask |= ATH9K_INT_GTT; 2018*9999SWang.Lin@Sun.COM #endif 2019*9999SWang.Lin@Sun.COM 2020*9999SWang.Lin@Sun.COM #ifdef ARN_ATH9K_HW_CAP_GTT 2021*9999SWang.Lin@Sun.COM if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT) 2022*9999SWang.Lin@Sun.COM sc->sc_imask |= ATH9K_INT_CST; 2023*9999SWang.Lin@Sun.COM #endif 2024*9999SWang.Lin@Sun.COM 2025*9999SWang.Lin@Sun.COM /* 2026*9999SWang.Lin@Sun.COM * Enable MIB interrupts when there are hardware phy counters. 2027*9999SWang.Lin@Sun.COM * Note we only do this (at the moment) for station mode. 2028*9999SWang.Lin@Sun.COM */ 2029*9999SWang.Lin@Sun.COM #ifdef ARN_ATH9K_INT_MIB 2030*9999SWang.Lin@Sun.COM if (ath9k_hw_phycounters(sc->sc_ah) && 2031*9999SWang.Lin@Sun.COM ((sc->sc_ah->ah_opmode == ATH9K_M_STA) || 2032*9999SWang.Lin@Sun.COM (sc->sc_ah->ah_opmode == ATH9K_M_IBSS))) 2033*9999SWang.Lin@Sun.COM sc->sc_imask |= ATH9K_INT_MIB; 2034*9999SWang.Lin@Sun.COM #endif 2035*9999SWang.Lin@Sun.COM /* 2036*9999SWang.Lin@Sun.COM * Some hardware processes the TIM IE and fires an 2037*9999SWang.Lin@Sun.COM * interrupt when the TIM bit is set. For hardware 2038*9999SWang.Lin@Sun.COM * that does, if not overridden by configuration, 2039*9999SWang.Lin@Sun.COM * enable the TIM interrupt when operating as station. 2040*9999SWang.Lin@Sun.COM */ 2041*9999SWang.Lin@Sun.COM #ifdef ARN_ATH9K_INT_TIM 2042*9999SWang.Lin@Sun.COM if ((sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_ENHANCEDPM) && 2043*9999SWang.Lin@Sun.COM (sc->sc_ah->ah_opmode == ATH9K_M_STA) && 2044*9999SWang.Lin@Sun.COM !sc->sc_config.swBeaconProcess) 2045*9999SWang.Lin@Sun.COM sc->sc_imask |= ATH9K_INT_TIM; 2046*9999SWang.Lin@Sun.COM #endif 2047*9999SWang.Lin@Sun.COM if (arn_chan2mode(init_channel) != sc->sc_curmode) 2048*9999SWang.Lin@Sun.COM arn_setcurmode(sc, arn_chan2mode(init_channel)); 2049*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INIT, "arn: " 2050*9999SWang.Lin@Sun.COM "%s: current mode after arn_setcurmode is %d\n", 2051*9999SWang.Lin@Sun.COM __func__, sc->sc_curmode)); 2052*9999SWang.Lin@Sun.COM 2053*9999SWang.Lin@Sun.COM sc->sc_isrunning = 1; 2054*9999SWang.Lin@Sun.COM 2055*9999SWang.Lin@Sun.COM /* Disable BMISS interrupt when we're not associated */ 2056*9999SWang.Lin@Sun.COM sc->sc_imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS); 2057*9999SWang.Lin@Sun.COM (void) ath9k_hw_set_interrupts(sc->sc_ah, sc->sc_imask); 2058*9999SWang.Lin@Sun.COM 2059*9999SWang.Lin@Sun.COM return (0); 2060*9999SWang.Lin@Sun.COM 2061*9999SWang.Lin@Sun.COM error: 2062*9999SWang.Lin@Sun.COM return (error); 2063*9999SWang.Lin@Sun.COM } 2064*9999SWang.Lin@Sun.COM 2065*9999SWang.Lin@Sun.COM static void 2066*9999SWang.Lin@Sun.COM arn_close(struct arn_softc *sc) 2067*9999SWang.Lin@Sun.COM { 2068*9999SWang.Lin@Sun.COM ieee80211com_t *ic = (ieee80211com_t *)sc; 2069*9999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah; 2070*9999SWang.Lin@Sun.COM 2071*9999SWang.Lin@Sun.COM ARN_LOCK_ASSERT(sc); 2072*9999SWang.Lin@Sun.COM 2073*9999SWang.Lin@Sun.COM if (!sc->sc_isrunning) 2074*9999SWang.Lin@Sun.COM return; 2075*9999SWang.Lin@Sun.COM 2076*9999SWang.Lin@Sun.COM /* 2077*9999SWang.Lin@Sun.COM * Shutdown the hardware and driver 2078*9999SWang.Lin@Sun.COM * Note that some of this work is not possible if the 2079*9999SWang.Lin@Sun.COM * hardware is gone (invalid). 2080*9999SWang.Lin@Sun.COM */ 2081*9999SWang.Lin@Sun.COM ARN_UNLOCK(sc); 2082*9999SWang.Lin@Sun.COM ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 2083*9999SWang.Lin@Sun.COM ieee80211_stop_watchdog(ic); 2084*9999SWang.Lin@Sun.COM ARN_LOCK(sc); 2085*9999SWang.Lin@Sun.COM 2086*9999SWang.Lin@Sun.COM /* 2087*9999SWang.Lin@Sun.COM * make sure h/w will not generate any interrupt 2088*9999SWang.Lin@Sun.COM * before setting the invalid flag. 2089*9999SWang.Lin@Sun.COM */ 2090*9999SWang.Lin@Sun.COM (void) ath9k_hw_set_interrupts(ah, 0); 2091*9999SWang.Lin@Sun.COM 2092*9999SWang.Lin@Sun.COM if (!(sc->sc_flags & SC_OP_INVALID)) { 2093*9999SWang.Lin@Sun.COM arn_draintxq(sc, 0); 2094*9999SWang.Lin@Sun.COM (void) arn_stoprecv(sc); 2095*9999SWang.Lin@Sun.COM (void) ath9k_hw_phy_disable(ah); 2096*9999SWang.Lin@Sun.COM } else { 2097*9999SWang.Lin@Sun.COM sc->sc_rxlink = NULL; 2098*9999SWang.Lin@Sun.COM } 2099*9999SWang.Lin@Sun.COM 2100*9999SWang.Lin@Sun.COM sc->sc_isrunning = 0; 2101*9999SWang.Lin@Sun.COM } 2102*9999SWang.Lin@Sun.COM 2103*9999SWang.Lin@Sun.COM /* 2104*9999SWang.Lin@Sun.COM * MAC callback functions 2105*9999SWang.Lin@Sun.COM */ 2106*9999SWang.Lin@Sun.COM static int 2107*9999SWang.Lin@Sun.COM arn_m_stat(void *arg, uint_t stat, uint64_t *val) 2108*9999SWang.Lin@Sun.COM { 2109*9999SWang.Lin@Sun.COM struct arn_softc *sc = arg; 2110*9999SWang.Lin@Sun.COM ieee80211com_t *ic = (ieee80211com_t *)sc; 2111*9999SWang.Lin@Sun.COM struct ieee80211_node *in; 2112*9999SWang.Lin@Sun.COM struct ieee80211_rateset *rs; 2113*9999SWang.Lin@Sun.COM 2114*9999SWang.Lin@Sun.COM ARN_LOCK(sc); 2115*9999SWang.Lin@Sun.COM switch (stat) { 2116*9999SWang.Lin@Sun.COM case MAC_STAT_IFSPEED: 2117*9999SWang.Lin@Sun.COM in = ic->ic_bss; 2118*9999SWang.Lin@Sun.COM rs = &in->in_rates; 2119*9999SWang.Lin@Sun.COM *val = (rs->ir_rates[in->in_txrate] & IEEE80211_RATE_VAL) / 2 * 2120*9999SWang.Lin@Sun.COM 1000000ull; 2121*9999SWang.Lin@Sun.COM break; 2122*9999SWang.Lin@Sun.COM case MAC_STAT_NOXMTBUF: 2123*9999SWang.Lin@Sun.COM *val = sc->sc_stats.ast_tx_nobuf + 2124*9999SWang.Lin@Sun.COM sc->sc_stats.ast_tx_nobufmgt; 2125*9999SWang.Lin@Sun.COM break; 2126*9999SWang.Lin@Sun.COM case MAC_STAT_IERRORS: 2127*9999SWang.Lin@Sun.COM *val = sc->sc_stats.ast_rx_tooshort; 2128*9999SWang.Lin@Sun.COM break; 2129*9999SWang.Lin@Sun.COM case MAC_STAT_RBYTES: 2130*9999SWang.Lin@Sun.COM *val = ic->ic_stats.is_rx_bytes; 2131*9999SWang.Lin@Sun.COM break; 2132*9999SWang.Lin@Sun.COM case MAC_STAT_IPACKETS: 2133*9999SWang.Lin@Sun.COM *val = ic->ic_stats.is_rx_frags; 2134*9999SWang.Lin@Sun.COM break; 2135*9999SWang.Lin@Sun.COM case MAC_STAT_OBYTES: 2136*9999SWang.Lin@Sun.COM *val = ic->ic_stats.is_tx_bytes; 2137*9999SWang.Lin@Sun.COM break; 2138*9999SWang.Lin@Sun.COM case MAC_STAT_OPACKETS: 2139*9999SWang.Lin@Sun.COM *val = ic->ic_stats.is_tx_frags; 2140*9999SWang.Lin@Sun.COM break; 2141*9999SWang.Lin@Sun.COM case MAC_STAT_OERRORS: 2142*9999SWang.Lin@Sun.COM case WIFI_STAT_TX_FAILED: 2143*9999SWang.Lin@Sun.COM *val = sc->sc_stats.ast_tx_fifoerr + 2144*9999SWang.Lin@Sun.COM sc->sc_stats.ast_tx_xretries + 2145*9999SWang.Lin@Sun.COM sc->sc_stats.ast_tx_discard; 2146*9999SWang.Lin@Sun.COM break; 2147*9999SWang.Lin@Sun.COM case WIFI_STAT_TX_RETRANS: 2148*9999SWang.Lin@Sun.COM *val = sc->sc_stats.ast_tx_xretries; 2149*9999SWang.Lin@Sun.COM break; 2150*9999SWang.Lin@Sun.COM case WIFI_STAT_FCS_ERRORS: 2151*9999SWang.Lin@Sun.COM *val = sc->sc_stats.ast_rx_crcerr; 2152*9999SWang.Lin@Sun.COM break; 2153*9999SWang.Lin@Sun.COM case WIFI_STAT_WEP_ERRORS: 2154*9999SWang.Lin@Sun.COM *val = sc->sc_stats.ast_rx_badcrypt; 2155*9999SWang.Lin@Sun.COM break; 2156*9999SWang.Lin@Sun.COM case WIFI_STAT_TX_FRAGS: 2157*9999SWang.Lin@Sun.COM case WIFI_STAT_MCAST_TX: 2158*9999SWang.Lin@Sun.COM case WIFI_STAT_RTS_SUCCESS: 2159*9999SWang.Lin@Sun.COM case WIFI_STAT_RTS_FAILURE: 2160*9999SWang.Lin@Sun.COM case WIFI_STAT_ACK_FAILURE: 2161*9999SWang.Lin@Sun.COM case WIFI_STAT_RX_FRAGS: 2162*9999SWang.Lin@Sun.COM case WIFI_STAT_MCAST_RX: 2163*9999SWang.Lin@Sun.COM case WIFI_STAT_RX_DUPS: 2164*9999SWang.Lin@Sun.COM ARN_UNLOCK(sc); 2165*9999SWang.Lin@Sun.COM return (ieee80211_stat(ic, stat, val)); 2166*9999SWang.Lin@Sun.COM default: 2167*9999SWang.Lin@Sun.COM ARN_UNLOCK(sc); 2168*9999SWang.Lin@Sun.COM return (ENOTSUP); 2169*9999SWang.Lin@Sun.COM } 2170*9999SWang.Lin@Sun.COM ARN_UNLOCK(sc); 2171*9999SWang.Lin@Sun.COM 2172*9999SWang.Lin@Sun.COM return (0); 2173*9999SWang.Lin@Sun.COM } 2174*9999SWang.Lin@Sun.COM 2175*9999SWang.Lin@Sun.COM int 2176*9999SWang.Lin@Sun.COM arn_m_start(void *arg) 2177*9999SWang.Lin@Sun.COM { 2178*9999SWang.Lin@Sun.COM struct arn_softc *sc = arg; 2179*9999SWang.Lin@Sun.COM int err = 0; 2180*9999SWang.Lin@Sun.COM 2181*9999SWang.Lin@Sun.COM ARN_LOCK(sc); 2182*9999SWang.Lin@Sun.COM 2183*9999SWang.Lin@Sun.COM /* 2184*9999SWang.Lin@Sun.COM * Stop anything previously setup. This is safe 2185*9999SWang.Lin@Sun.COM * whether this is the first time through or not. 2186*9999SWang.Lin@Sun.COM */ 2187*9999SWang.Lin@Sun.COM 2188*9999SWang.Lin@Sun.COM arn_close(sc); 2189*9999SWang.Lin@Sun.COM 2190*9999SWang.Lin@Sun.COM if ((err = arn_open(sc)) != 0) { 2191*9999SWang.Lin@Sun.COM ARN_UNLOCK(sc); 2192*9999SWang.Lin@Sun.COM return (err); 2193*9999SWang.Lin@Sun.COM } 2194*9999SWang.Lin@Sun.COM 2195*9999SWang.Lin@Sun.COM /* H/W is reday now */ 2196*9999SWang.Lin@Sun.COM sc->sc_flags &= ~SC_OP_INVALID; 2197*9999SWang.Lin@Sun.COM 2198*9999SWang.Lin@Sun.COM ARN_UNLOCK(sc); 2199*9999SWang.Lin@Sun.COM 2200*9999SWang.Lin@Sun.COM return (0); 2201*9999SWang.Lin@Sun.COM } 2202*9999SWang.Lin@Sun.COM 2203*9999SWang.Lin@Sun.COM static void 2204*9999SWang.Lin@Sun.COM arn_m_stop(void *arg) 2205*9999SWang.Lin@Sun.COM { 2206*9999SWang.Lin@Sun.COM struct arn_softc *sc = arg; 2207*9999SWang.Lin@Sun.COM 2208*9999SWang.Lin@Sun.COM ARN_LOCK(sc); 2209*9999SWang.Lin@Sun.COM arn_close(sc); 2210*9999SWang.Lin@Sun.COM 2211*9999SWang.Lin@Sun.COM /* disable HAL and put h/w to sleep */ 2212*9999SWang.Lin@Sun.COM (void) ath9k_hw_disable(sc->sc_ah); 2213*9999SWang.Lin@Sun.COM ath9k_hw_configpcipowersave(sc->sc_ah, 1); 2214*9999SWang.Lin@Sun.COM 2215*9999SWang.Lin@Sun.COM /* XXX: hardware will not be ready in suspend state */ 2216*9999SWang.Lin@Sun.COM sc->sc_flags |= SC_OP_INVALID; 2217*9999SWang.Lin@Sun.COM ARN_UNLOCK(sc); 2218*9999SWang.Lin@Sun.COM } 2219*9999SWang.Lin@Sun.COM 2220*9999SWang.Lin@Sun.COM static int 2221*9999SWang.Lin@Sun.COM arn_m_promisc(void *arg, boolean_t on) 2222*9999SWang.Lin@Sun.COM { 2223*9999SWang.Lin@Sun.COM struct arn_softc *sc = arg; 2224*9999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah; 2225*9999SWang.Lin@Sun.COM uint32_t rfilt; 2226*9999SWang.Lin@Sun.COM 2227*9999SWang.Lin@Sun.COM ARN_LOCK(sc); 2228*9999SWang.Lin@Sun.COM 2229*9999SWang.Lin@Sun.COM rfilt = ath9k_hw_getrxfilter(ah); 2230*9999SWang.Lin@Sun.COM if (on) 2231*9999SWang.Lin@Sun.COM rfilt |= ATH9K_RX_FILTER_PROM; 2232*9999SWang.Lin@Sun.COM else 2233*9999SWang.Lin@Sun.COM rfilt &= ~ATH9K_RX_FILTER_PROM; 2234*9999SWang.Lin@Sun.COM sc->sc_promisc = on; 2235*9999SWang.Lin@Sun.COM ath9k_hw_setrxfilter(ah, rfilt); 2236*9999SWang.Lin@Sun.COM 2237*9999SWang.Lin@Sun.COM ARN_UNLOCK(sc); 2238*9999SWang.Lin@Sun.COM 2239*9999SWang.Lin@Sun.COM return (0); 2240*9999SWang.Lin@Sun.COM } 2241*9999SWang.Lin@Sun.COM 2242*9999SWang.Lin@Sun.COM static int 2243*9999SWang.Lin@Sun.COM arn_m_multicst(void *arg, boolean_t add, const uint8_t *mca) 2244*9999SWang.Lin@Sun.COM { 2245*9999SWang.Lin@Sun.COM struct arn_softc *sc = arg; 2246*9999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah; 2247*9999SWang.Lin@Sun.COM uint32_t val, index, bit; 2248*9999SWang.Lin@Sun.COM uint8_t pos; 2249*9999SWang.Lin@Sun.COM uint32_t *mfilt = sc->sc_mcast_hash; 2250*9999SWang.Lin@Sun.COM 2251*9999SWang.Lin@Sun.COM ARN_LOCK(sc); 2252*9999SWang.Lin@Sun.COM 2253*9999SWang.Lin@Sun.COM /* calculate XOR of eight 6bit values */ 2254*9999SWang.Lin@Sun.COM val = ARN_LE_READ_32(mca + 0); 2255*9999SWang.Lin@Sun.COM pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; 2256*9999SWang.Lin@Sun.COM val = ARN_LE_READ_32(mca + 3); 2257*9999SWang.Lin@Sun.COM pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; 2258*9999SWang.Lin@Sun.COM pos &= 0x3f; 2259*9999SWang.Lin@Sun.COM index = pos / 32; 2260*9999SWang.Lin@Sun.COM bit = 1 << (pos % 32); 2261*9999SWang.Lin@Sun.COM 2262*9999SWang.Lin@Sun.COM if (add) { /* enable multicast */ 2263*9999SWang.Lin@Sun.COM sc->sc_mcast_refs[pos]++; 2264*9999SWang.Lin@Sun.COM mfilt[index] |= bit; 2265*9999SWang.Lin@Sun.COM } else { /* disable multicast */ 2266*9999SWang.Lin@Sun.COM if (--sc->sc_mcast_refs[pos] == 0) 2267*9999SWang.Lin@Sun.COM mfilt[index] &= ~bit; 2268*9999SWang.Lin@Sun.COM } 2269*9999SWang.Lin@Sun.COM ath9k_hw_setmcastfilter(ah, mfilt[0], mfilt[1]); 2270*9999SWang.Lin@Sun.COM 2271*9999SWang.Lin@Sun.COM ARN_UNLOCK(sc); 2272*9999SWang.Lin@Sun.COM return (0); 2273*9999SWang.Lin@Sun.COM } 2274*9999SWang.Lin@Sun.COM 2275*9999SWang.Lin@Sun.COM static int 2276*9999SWang.Lin@Sun.COM arn_m_unicst(void *arg, const uint8_t *macaddr) 2277*9999SWang.Lin@Sun.COM { 2278*9999SWang.Lin@Sun.COM struct arn_softc *sc = arg; 2279*9999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah; 2280*9999SWang.Lin@Sun.COM ieee80211com_t *ic = (ieee80211com_t *)sc; 2281*9999SWang.Lin@Sun.COM 2282*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_XMIT, "ath: ath_gld_saddr(): " 2283*9999SWang.Lin@Sun.COM "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", 2284*9999SWang.Lin@Sun.COM macaddr[0], macaddr[1], macaddr[2], 2285*9999SWang.Lin@Sun.COM macaddr[3], macaddr[4], macaddr[5])); 2286*9999SWang.Lin@Sun.COM 2287*9999SWang.Lin@Sun.COM ARN_LOCK(sc); 2288*9999SWang.Lin@Sun.COM IEEE80211_ADDR_COPY(sc->sc_isc.ic_macaddr, macaddr); 2289*9999SWang.Lin@Sun.COM (void) ath9k_hw_setmac(ah, sc->sc_isc.ic_macaddr); 2290*9999SWang.Lin@Sun.COM (void) arn_reset(ic); 2291*9999SWang.Lin@Sun.COM ARN_UNLOCK(sc); 2292*9999SWang.Lin@Sun.COM return (0); 2293*9999SWang.Lin@Sun.COM } 2294*9999SWang.Lin@Sun.COM 2295*9999SWang.Lin@Sun.COM static mblk_t * 2296*9999SWang.Lin@Sun.COM arn_m_tx(void *arg, mblk_t *mp) 2297*9999SWang.Lin@Sun.COM { 2298*9999SWang.Lin@Sun.COM struct arn_softc *sc = arg; 2299*9999SWang.Lin@Sun.COM int error = 0; 2300*9999SWang.Lin@Sun.COM mblk_t *next; 2301*9999SWang.Lin@Sun.COM ieee80211com_t *ic = (ieee80211com_t *)sc; 2302*9999SWang.Lin@Sun.COM 2303*9999SWang.Lin@Sun.COM /* 2304*9999SWang.Lin@Sun.COM * No data frames go out unless we're associated; this 2305*9999SWang.Lin@Sun.COM * should not happen as the 802.11 layer does not enable 2306*9999SWang.Lin@Sun.COM * the xmit queue until we enter the RUN state. 2307*9999SWang.Lin@Sun.COM */ 2308*9999SWang.Lin@Sun.COM if (ic->ic_state != IEEE80211_S_RUN) { 2309*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_XMIT, "arn: arn_m_tx(): " 2310*9999SWang.Lin@Sun.COM "discard, state %u\n", ic->ic_state)); 2311*9999SWang.Lin@Sun.COM sc->sc_stats.ast_tx_discard++; 2312*9999SWang.Lin@Sun.COM freemsgchain(mp); 2313*9999SWang.Lin@Sun.COM return (NULL); 2314*9999SWang.Lin@Sun.COM } 2315*9999SWang.Lin@Sun.COM 2316*9999SWang.Lin@Sun.COM while (mp != NULL) { 2317*9999SWang.Lin@Sun.COM next = mp->b_next; 2318*9999SWang.Lin@Sun.COM mp->b_next = NULL; 2319*9999SWang.Lin@Sun.COM error = arn_tx(ic, mp, IEEE80211_FC0_TYPE_DATA); 2320*9999SWang.Lin@Sun.COM if (error != 0) { 2321*9999SWang.Lin@Sun.COM mp->b_next = next; 2322*9999SWang.Lin@Sun.COM if (error == ENOMEM) { 2323*9999SWang.Lin@Sun.COM break; 2324*9999SWang.Lin@Sun.COM } else { 2325*9999SWang.Lin@Sun.COM freemsgchain(mp); 2326*9999SWang.Lin@Sun.COM return (NULL); 2327*9999SWang.Lin@Sun.COM } 2328*9999SWang.Lin@Sun.COM } 2329*9999SWang.Lin@Sun.COM mp = next; 2330*9999SWang.Lin@Sun.COM } 2331*9999SWang.Lin@Sun.COM 2332*9999SWang.Lin@Sun.COM return (mp); 2333*9999SWang.Lin@Sun.COM } 2334*9999SWang.Lin@Sun.COM 2335*9999SWang.Lin@Sun.COM static void 2336*9999SWang.Lin@Sun.COM arn_m_ioctl(void *arg, queue_t *wq, mblk_t *mp) 2337*9999SWang.Lin@Sun.COM { 2338*9999SWang.Lin@Sun.COM struct arn_softc *sc = arg; 2339*9999SWang.Lin@Sun.COM int32_t err; 2340*9999SWang.Lin@Sun.COM 2341*9999SWang.Lin@Sun.COM err = ieee80211_ioctl(&sc->sc_isc, wq, mp); 2342*9999SWang.Lin@Sun.COM 2343*9999SWang.Lin@Sun.COM ARN_LOCK(sc); 2344*9999SWang.Lin@Sun.COM if (err == ENETRESET) { 2345*9999SWang.Lin@Sun.COM if (!(sc->sc_flags & SC_OP_INVALID)) { 2346*9999SWang.Lin@Sun.COM ARN_UNLOCK(sc); 2347*9999SWang.Lin@Sun.COM 2348*9999SWang.Lin@Sun.COM (void) arn_m_start(sc); 2349*9999SWang.Lin@Sun.COM 2350*9999SWang.Lin@Sun.COM (void) ieee80211_new_state(&sc->sc_isc, 2351*9999SWang.Lin@Sun.COM IEEE80211_S_SCAN, -1); 2352*9999SWang.Lin@Sun.COM ARN_LOCK(sc); 2353*9999SWang.Lin@Sun.COM } 2354*9999SWang.Lin@Sun.COM } 2355*9999SWang.Lin@Sun.COM ARN_UNLOCK(sc); 2356*9999SWang.Lin@Sun.COM } 2357*9999SWang.Lin@Sun.COM 2358*9999SWang.Lin@Sun.COM static int 2359*9999SWang.Lin@Sun.COM arn_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, 2360*9999SWang.Lin@Sun.COM uint_t wldp_length, const void *wldp_buf) 2361*9999SWang.Lin@Sun.COM { 2362*9999SWang.Lin@Sun.COM struct arn_softc *sc = arg; 2363*9999SWang.Lin@Sun.COM int err; 2364*9999SWang.Lin@Sun.COM 2365*9999SWang.Lin@Sun.COM err = ieee80211_setprop(&sc->sc_isc, pr_name, wldp_pr_num, 2366*9999SWang.Lin@Sun.COM wldp_length, wldp_buf); 2367*9999SWang.Lin@Sun.COM 2368*9999SWang.Lin@Sun.COM ARN_LOCK(sc); 2369*9999SWang.Lin@Sun.COM 2370*9999SWang.Lin@Sun.COM if (err == ENETRESET) { 2371*9999SWang.Lin@Sun.COM if (!(sc->sc_flags & SC_OP_INVALID)) { 2372*9999SWang.Lin@Sun.COM ARN_UNLOCK(sc); 2373*9999SWang.Lin@Sun.COM (void) arn_m_start(sc); 2374*9999SWang.Lin@Sun.COM (void) ieee80211_new_state(&sc->sc_isc, 2375*9999SWang.Lin@Sun.COM IEEE80211_S_SCAN, -1); 2376*9999SWang.Lin@Sun.COM ARN_LOCK(sc); 2377*9999SWang.Lin@Sun.COM } 2378*9999SWang.Lin@Sun.COM err = 0; 2379*9999SWang.Lin@Sun.COM } 2380*9999SWang.Lin@Sun.COM 2381*9999SWang.Lin@Sun.COM ARN_UNLOCK(sc); 2382*9999SWang.Lin@Sun.COM 2383*9999SWang.Lin@Sun.COM return (err); 2384*9999SWang.Lin@Sun.COM } 2385*9999SWang.Lin@Sun.COM 2386*9999SWang.Lin@Sun.COM /* ARGSUSED */ 2387*9999SWang.Lin@Sun.COM static int 2388*9999SWang.Lin@Sun.COM arn_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, 2389*9999SWang.Lin@Sun.COM uint_t pr_flags, uint_t wldp_length, void *wldp_buf, uint_t *perm) 2390*9999SWang.Lin@Sun.COM { 2391*9999SWang.Lin@Sun.COM struct arn_softc *sc = arg; 2392*9999SWang.Lin@Sun.COM int err = 0; 2393*9999SWang.Lin@Sun.COM 2394*9999SWang.Lin@Sun.COM err = ieee80211_getprop(&sc->sc_isc, pr_name, wldp_pr_num, 2395*9999SWang.Lin@Sun.COM pr_flags, wldp_length, wldp_buf, perm); 2396*9999SWang.Lin@Sun.COM 2397*9999SWang.Lin@Sun.COM return (err); 2398*9999SWang.Lin@Sun.COM } 2399*9999SWang.Lin@Sun.COM 2400*9999SWang.Lin@Sun.COM /* return bus cachesize in 4B word units */ 2401*9999SWang.Lin@Sun.COM static void 2402*9999SWang.Lin@Sun.COM arn_pci_config_cachesize(struct arn_softc *sc) 2403*9999SWang.Lin@Sun.COM { 2404*9999SWang.Lin@Sun.COM uint8_t csz; 2405*9999SWang.Lin@Sun.COM 2406*9999SWang.Lin@Sun.COM /* 2407*9999SWang.Lin@Sun.COM * Cache line size is used to size and align various 2408*9999SWang.Lin@Sun.COM * structures used to communicate with the hardware. 2409*9999SWang.Lin@Sun.COM */ 2410*9999SWang.Lin@Sun.COM csz = pci_config_get8(sc->sc_cfg_handle, PCI_CONF_CACHE_LINESZ); 2411*9999SWang.Lin@Sun.COM if (csz == 0) { 2412*9999SWang.Lin@Sun.COM /* 2413*9999SWang.Lin@Sun.COM * We must have this setup properly for rx buffer 2414*9999SWang.Lin@Sun.COM * DMA to work so force a reasonable value here if it 2415*9999SWang.Lin@Sun.COM * comes up zero. 2416*9999SWang.Lin@Sun.COM */ 2417*9999SWang.Lin@Sun.COM csz = ATH_DEF_CACHE_BYTES / sizeof (uint32_t); 2418*9999SWang.Lin@Sun.COM pci_config_put8(sc->sc_cfg_handle, PCI_CONF_CACHE_LINESZ, 2419*9999SWang.Lin@Sun.COM csz); 2420*9999SWang.Lin@Sun.COM } 2421*9999SWang.Lin@Sun.COM sc->sc_cachelsz = csz << 2; 2422*9999SWang.Lin@Sun.COM } 2423*9999SWang.Lin@Sun.COM 2424*9999SWang.Lin@Sun.COM static int 2425*9999SWang.Lin@Sun.COM arn_pci_setup(struct arn_softc *sc) 2426*9999SWang.Lin@Sun.COM { 2427*9999SWang.Lin@Sun.COM uint16_t command; 2428*9999SWang.Lin@Sun.COM 2429*9999SWang.Lin@Sun.COM /* 2430*9999SWang.Lin@Sun.COM * Enable memory mapping and bus mastering 2431*9999SWang.Lin@Sun.COM */ 2432*9999SWang.Lin@Sun.COM ASSERT(sc != NULL); 2433*9999SWang.Lin@Sun.COM command = pci_config_get16(sc->sc_cfg_handle, PCI_CONF_COMM); 2434*9999SWang.Lin@Sun.COM command |= PCI_COMM_MAE | PCI_COMM_ME; 2435*9999SWang.Lin@Sun.COM pci_config_put16(sc->sc_cfg_handle, PCI_CONF_COMM, command); 2436*9999SWang.Lin@Sun.COM command = pci_config_get16(sc->sc_cfg_handle, PCI_CONF_COMM); 2437*9999SWang.Lin@Sun.COM if ((command & PCI_COMM_MAE) == 0) { 2438*9999SWang.Lin@Sun.COM arn_problem("arn: arn_pci_setup(): " 2439*9999SWang.Lin@Sun.COM "failed to enable memory mapping\n"); 2440*9999SWang.Lin@Sun.COM return (EIO); 2441*9999SWang.Lin@Sun.COM } 2442*9999SWang.Lin@Sun.COM if ((command & PCI_COMM_ME) == 0) { 2443*9999SWang.Lin@Sun.COM arn_problem("arn: arn_pci_setup(): " 2444*9999SWang.Lin@Sun.COM "failed to enable bus mastering\n"); 2445*9999SWang.Lin@Sun.COM return (EIO); 2446*9999SWang.Lin@Sun.COM } 2447*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INIT, "arn: arn_pci_setup(): " 2448*9999SWang.Lin@Sun.COM "set command reg to 0x%x \n", command)); 2449*9999SWang.Lin@Sun.COM 2450*9999SWang.Lin@Sun.COM return (0); 2451*9999SWang.Lin@Sun.COM } 2452*9999SWang.Lin@Sun.COM 2453*9999SWang.Lin@Sun.COM static void 2454*9999SWang.Lin@Sun.COM arn_get_hw_encap(struct arn_softc *sc) 2455*9999SWang.Lin@Sun.COM { 2456*9999SWang.Lin@Sun.COM ieee80211com_t *ic; 2457*9999SWang.Lin@Sun.COM struct ath_hal *ah; 2458*9999SWang.Lin@Sun.COM 2459*9999SWang.Lin@Sun.COM ic = (ieee80211com_t *)sc; 2460*9999SWang.Lin@Sun.COM ah = sc->sc_ah; 2461*9999SWang.Lin@Sun.COM 2462*9999SWang.Lin@Sun.COM if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER, 2463*9999SWang.Lin@Sun.COM ATH9K_CIPHER_AES_CCM, NULL)) 2464*9999SWang.Lin@Sun.COM ic->ic_caps |= IEEE80211_C_AES_CCM; 2465*9999SWang.Lin@Sun.COM if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER, 2466*9999SWang.Lin@Sun.COM ATH9K_CIPHER_AES_OCB, NULL)) 2467*9999SWang.Lin@Sun.COM ic->ic_caps |= IEEE80211_C_AES; 2468*9999SWang.Lin@Sun.COM if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER, 2469*9999SWang.Lin@Sun.COM ATH9K_CIPHER_TKIP, NULL)) 2470*9999SWang.Lin@Sun.COM ic->ic_caps |= IEEE80211_C_TKIP; 2471*9999SWang.Lin@Sun.COM if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER, 2472*9999SWang.Lin@Sun.COM ATH9K_CIPHER_WEP, NULL)) 2473*9999SWang.Lin@Sun.COM ic->ic_caps |= IEEE80211_C_WEP; 2474*9999SWang.Lin@Sun.COM if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER, 2475*9999SWang.Lin@Sun.COM ATH9K_CIPHER_MIC, NULL)) 2476*9999SWang.Lin@Sun.COM ic->ic_caps |= IEEE80211_C_TKIPMIC; 2477*9999SWang.Lin@Sun.COM } 2478*9999SWang.Lin@Sun.COM 2479*9999SWang.Lin@Sun.COM static int 2480*9999SWang.Lin@Sun.COM arn_resume(dev_info_t *devinfo) 2481*9999SWang.Lin@Sun.COM { 2482*9999SWang.Lin@Sun.COM struct arn_softc *sc; 2483*9999SWang.Lin@Sun.COM int ret = DDI_SUCCESS; 2484*9999SWang.Lin@Sun.COM 2485*9999SWang.Lin@Sun.COM sc = ddi_get_soft_state(arn_soft_state_p, ddi_get_instance(devinfo)); 2486*9999SWang.Lin@Sun.COM if (sc == NULL) { 2487*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INIT, "ath: ath_resume(): " 2488*9999SWang.Lin@Sun.COM "failed to get soft state\n")); 2489*9999SWang.Lin@Sun.COM return (DDI_FAILURE); 2490*9999SWang.Lin@Sun.COM } 2491*9999SWang.Lin@Sun.COM 2492*9999SWang.Lin@Sun.COM ARN_LOCK(sc); 2493*9999SWang.Lin@Sun.COM /* 2494*9999SWang.Lin@Sun.COM * Set up config space command register(s). Refuse 2495*9999SWang.Lin@Sun.COM * to resume on failure. 2496*9999SWang.Lin@Sun.COM */ 2497*9999SWang.Lin@Sun.COM if (arn_pci_setup(sc) != 0) { 2498*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INIT, "ath: ath_resume(): " 2499*9999SWang.Lin@Sun.COM "ath_pci_setup() failed\n")); 2500*9999SWang.Lin@Sun.COM ARN_UNLOCK(sc); 2501*9999SWang.Lin@Sun.COM return (DDI_FAILURE); 2502*9999SWang.Lin@Sun.COM } 2503*9999SWang.Lin@Sun.COM 2504*9999SWang.Lin@Sun.COM if (!(sc->sc_flags & SC_OP_INVALID)) 2505*9999SWang.Lin@Sun.COM ret = arn_open(sc); 2506*9999SWang.Lin@Sun.COM ARN_UNLOCK(sc); 2507*9999SWang.Lin@Sun.COM 2508*9999SWang.Lin@Sun.COM return (ret); 2509*9999SWang.Lin@Sun.COM } 2510*9999SWang.Lin@Sun.COM 2511*9999SWang.Lin@Sun.COM static int 2512*9999SWang.Lin@Sun.COM arn_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd) 2513*9999SWang.Lin@Sun.COM { 2514*9999SWang.Lin@Sun.COM struct arn_softc *sc; 2515*9999SWang.Lin@Sun.COM int instance; 2516*9999SWang.Lin@Sun.COM int status; 2517*9999SWang.Lin@Sun.COM int32_t err; 2518*9999SWang.Lin@Sun.COM uint16_t vendor_id; 2519*9999SWang.Lin@Sun.COM uint16_t device_id; 2520*9999SWang.Lin@Sun.COM uint32_t i; 2521*9999SWang.Lin@Sun.COM uint32_t val; 2522*9999SWang.Lin@Sun.COM char strbuf[32]; 2523*9999SWang.Lin@Sun.COM ieee80211com_t *ic; 2524*9999SWang.Lin@Sun.COM struct ath_hal *ah; 2525*9999SWang.Lin@Sun.COM wifi_data_t wd = { 0 }; 2526*9999SWang.Lin@Sun.COM mac_register_t *macp; 2527*9999SWang.Lin@Sun.COM 2528*9999SWang.Lin@Sun.COM switch (cmd) { 2529*9999SWang.Lin@Sun.COM case DDI_ATTACH: 2530*9999SWang.Lin@Sun.COM break; 2531*9999SWang.Lin@Sun.COM case DDI_RESUME: 2532*9999SWang.Lin@Sun.COM return (arn_resume(devinfo)); 2533*9999SWang.Lin@Sun.COM default: 2534*9999SWang.Lin@Sun.COM return (DDI_FAILURE); 2535*9999SWang.Lin@Sun.COM } 2536*9999SWang.Lin@Sun.COM 2537*9999SWang.Lin@Sun.COM instance = ddi_get_instance(devinfo); 2538*9999SWang.Lin@Sun.COM if (ddi_soft_state_zalloc(arn_soft_state_p, instance) != DDI_SUCCESS) { 2539*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: " 2540*9999SWang.Lin@Sun.COM "%s: Unable to alloc softstate\n", __func__)); 2541*9999SWang.Lin@Sun.COM return (DDI_FAILURE); 2542*9999SWang.Lin@Sun.COM } 2543*9999SWang.Lin@Sun.COM 2544*9999SWang.Lin@Sun.COM sc = ddi_get_soft_state(arn_soft_state_p, ddi_get_instance(devinfo)); 2545*9999SWang.Lin@Sun.COM ic = (ieee80211com_t *)sc; 2546*9999SWang.Lin@Sun.COM sc->sc_dev = devinfo; 2547*9999SWang.Lin@Sun.COM 2548*9999SWang.Lin@Sun.COM mutex_init(&sc->sc_genlock, NULL, MUTEX_DRIVER, NULL); 2549*9999SWang.Lin@Sun.COM mutex_init(&sc->sc_serial_rw, NULL, MUTEX_DRIVER, NULL); 2550*9999SWang.Lin@Sun.COM mutex_init(&sc->sc_txbuflock, NULL, MUTEX_DRIVER, NULL); 2551*9999SWang.Lin@Sun.COM mutex_init(&sc->sc_rxbuflock, NULL, MUTEX_DRIVER, NULL); 2552*9999SWang.Lin@Sun.COM mutex_init(&sc->sc_resched_lock, NULL, MUTEX_DRIVER, NULL); 2553*9999SWang.Lin@Sun.COM #ifdef ARN_IBSS 2554*9999SWang.Lin@Sun.COM mutex_init(&sc->sc_bcbuflock, NULL, MUTEX_DRIVER, NULL); 2555*9999SWang.Lin@Sun.COM #endif 2556*9999SWang.Lin@Sun.COM 2557*9999SWang.Lin@Sun.COM sc->sc_flags |= SC_OP_INVALID; 2558*9999SWang.Lin@Sun.COM 2559*9999SWang.Lin@Sun.COM err = pci_config_setup(devinfo, &sc->sc_cfg_handle); 2560*9999SWang.Lin@Sun.COM if (err != DDI_SUCCESS) { 2561*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): " 2562*9999SWang.Lin@Sun.COM "pci_config_setup() failed")); 2563*9999SWang.Lin@Sun.COM goto attach_fail0; 2564*9999SWang.Lin@Sun.COM } 2565*9999SWang.Lin@Sun.COM 2566*9999SWang.Lin@Sun.COM if (arn_pci_setup(sc) != 0) 2567*9999SWang.Lin@Sun.COM goto attach_fail1; 2568*9999SWang.Lin@Sun.COM 2569*9999SWang.Lin@Sun.COM /* Cache line size set up */ 2570*9999SWang.Lin@Sun.COM arn_pci_config_cachesize(sc); 2571*9999SWang.Lin@Sun.COM 2572*9999SWang.Lin@Sun.COM vendor_id = pci_config_get16(sc->sc_cfg_handle, PCI_CONF_VENID); 2573*9999SWang.Lin@Sun.COM device_id = pci_config_get16(sc->sc_cfg_handle, PCI_CONF_DEVID); 2574*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): vendor 0x%x, " 2575*9999SWang.Lin@Sun.COM "device id 0x%x, cache size %d\n", 2576*9999SWang.Lin@Sun.COM vendor_id, device_id, 2577*9999SWang.Lin@Sun.COM pci_config_get8(sc->sc_cfg_handle, PCI_CONF_CACHE_LINESZ))); 2578*9999SWang.Lin@Sun.COM 2579*9999SWang.Lin@Sun.COM pci_config_put8(sc->sc_cfg_handle, PCI_CONF_LATENCY_TIMER, 0xa8); 2580*9999SWang.Lin@Sun.COM val = pci_config_get32(sc->sc_cfg_handle, 0x40); 2581*9999SWang.Lin@Sun.COM if ((val & 0x0000ff00) != 0) 2582*9999SWang.Lin@Sun.COM pci_config_put32(sc->sc_cfg_handle, 0x40, val & 0xffff00ff); 2583*9999SWang.Lin@Sun.COM 2584*9999SWang.Lin@Sun.COM err = ddi_regs_map_setup(devinfo, 1, 2585*9999SWang.Lin@Sun.COM &sc->mem, 0, 0, &arn_reg_accattr, &sc->sc_io_handle); 2586*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): " 2587*9999SWang.Lin@Sun.COM "regs map1 = %x err=%d\n", sc->mem, err)); 2588*9999SWang.Lin@Sun.COM if (err != DDI_SUCCESS) { 2589*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): " 2590*9999SWang.Lin@Sun.COM "ddi_regs_map_setup() failed")); 2591*9999SWang.Lin@Sun.COM goto attach_fail1; 2592*9999SWang.Lin@Sun.COM } 2593*9999SWang.Lin@Sun.COM 2594*9999SWang.Lin@Sun.COM ah = ath9k_hw_attach(device_id, sc, sc->mem, &status); 2595*9999SWang.Lin@Sun.COM if (ah == NULL) { 2596*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): " 2597*9999SWang.Lin@Sun.COM "unable to attach hw: H/W status %u\n", 2598*9999SWang.Lin@Sun.COM status)); 2599*9999SWang.Lin@Sun.COM goto attach_fail2; 2600*9999SWang.Lin@Sun.COM } 2601*9999SWang.Lin@Sun.COM sc->sc_ah = ah; 2602*9999SWang.Lin@Sun.COM 2603*9999SWang.Lin@Sun.COM ath9k_hw_getmac(ah, ic->ic_macaddr); 2604*9999SWang.Lin@Sun.COM 2605*9999SWang.Lin@Sun.COM /* Get the hardware key cache size. */ 2606*9999SWang.Lin@Sun.COM sc->sc_keymax = ah->ah_caps.keycache_size; 2607*9999SWang.Lin@Sun.COM if (sc->sc_keymax > ATH_KEYMAX) { 2608*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): " 2609*9999SWang.Lin@Sun.COM "Warning, using only %u entries in %u key cache\n", 2610*9999SWang.Lin@Sun.COM ATH_KEYMAX, sc->sc_keymax)); 2611*9999SWang.Lin@Sun.COM sc->sc_keymax = ATH_KEYMAX; 2612*9999SWang.Lin@Sun.COM } 2613*9999SWang.Lin@Sun.COM 2614*9999SWang.Lin@Sun.COM /* 2615*9999SWang.Lin@Sun.COM * Reset the key cache since some parts do not 2616*9999SWang.Lin@Sun.COM * reset the contents on initial power up. 2617*9999SWang.Lin@Sun.COM */ 2618*9999SWang.Lin@Sun.COM for (i = 0; i < sc->sc_keymax; i++) 2619*9999SWang.Lin@Sun.COM (void) ath9k_hw_keyreset(ah, (uint16_t)i); 2620*9999SWang.Lin@Sun.COM /* 2621*9999SWang.Lin@Sun.COM * Mark key cache slots associated with global keys 2622*9999SWang.Lin@Sun.COM * as in use. If we knew TKIP was not to be used we 2623*9999SWang.Lin@Sun.COM * could leave the +32, +64, and +32+64 slots free. 2624*9999SWang.Lin@Sun.COM * XXX only for splitmic. 2625*9999SWang.Lin@Sun.COM */ 2626*9999SWang.Lin@Sun.COM for (i = 0; i < IEEE80211_WEP_NKID; i++) { 2627*9999SWang.Lin@Sun.COM set_bit(i, sc->sc_keymap); 2628*9999SWang.Lin@Sun.COM set_bit(i + 32, sc->sc_keymap); 2629*9999SWang.Lin@Sun.COM set_bit(i + 64, sc->sc_keymap); 2630*9999SWang.Lin@Sun.COM set_bit(i + 32 + 64, sc->sc_keymap); 2631*9999SWang.Lin@Sun.COM } 2632*9999SWang.Lin@Sun.COM 2633*9999SWang.Lin@Sun.COM /* Collect the channel list using the default country code */ 2634*9999SWang.Lin@Sun.COM err = arn_setup_channels(sc); 2635*9999SWang.Lin@Sun.COM if (err == EINVAL) { 2636*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): " 2637*9999SWang.Lin@Sun.COM "ERR:arn_setup_channels\n")); 2638*9999SWang.Lin@Sun.COM goto attach_fail3; 2639*9999SWang.Lin@Sun.COM } 2640*9999SWang.Lin@Sun.COM 2641*9999SWang.Lin@Sun.COM /* default to STA mode */ 2642*9999SWang.Lin@Sun.COM sc->sc_ah->ah_opmode = ATH9K_M_STA; 2643*9999SWang.Lin@Sun.COM 2644*9999SWang.Lin@Sun.COM /* Setup rate tables */ 2645*9999SWang.Lin@Sun.COM arn_rate_attach(sc); 2646*9999SWang.Lin@Sun.COM arn_setup_rates(sc, IEEE80211_MODE_11A); 2647*9999SWang.Lin@Sun.COM arn_setup_rates(sc, IEEE80211_MODE_11B); 2648*9999SWang.Lin@Sun.COM arn_setup_rates(sc, IEEE80211_MODE_11G); 2649*9999SWang.Lin@Sun.COM 2650*9999SWang.Lin@Sun.COM /* Setup current mode here */ 2651*9999SWang.Lin@Sun.COM arn_setcurmode(sc, ATH9K_MODE_11G); 2652*9999SWang.Lin@Sun.COM 2653*9999SWang.Lin@Sun.COM /* 802.11g features */ 2654*9999SWang.Lin@Sun.COM if (sc->sc_have11g) 2655*9999SWang.Lin@Sun.COM ic->ic_caps |= IEEE80211_C_SHPREAMBLE | 2656*9999SWang.Lin@Sun.COM IEEE80211_C_SHSLOT; /* short slot time */ 2657*9999SWang.Lin@Sun.COM 2658*9999SWang.Lin@Sun.COM /* temp workaround */ 2659*9999SWang.Lin@Sun.COM sc->sc_mrretry = 1; 2660*9999SWang.Lin@Sun.COM 2661*9999SWang.Lin@Sun.COM /* Setup tx/rx descriptors */ 2662*9999SWang.Lin@Sun.COM err = arn_desc_alloc(devinfo, sc); 2663*9999SWang.Lin@Sun.COM if (err != DDI_SUCCESS) { 2664*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): " 2665*9999SWang.Lin@Sun.COM "failed to allocate descriptors: %d\n", err)); 2666*9999SWang.Lin@Sun.COM goto attach_fail3; 2667*9999SWang.Lin@Sun.COM } 2668*9999SWang.Lin@Sun.COM 2669*9999SWang.Lin@Sun.COM if ((sc->sc_tq = ddi_taskq_create(devinfo, "ath_taskq", 1, 2670*9999SWang.Lin@Sun.COM TASKQ_DEFAULTPRI, 0)) == NULL) { 2671*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): " 2672*9999SWang.Lin@Sun.COM "ERR:ddi_taskq_create\n")); 2673*9999SWang.Lin@Sun.COM goto attach_fail4; 2674*9999SWang.Lin@Sun.COM } 2675*9999SWang.Lin@Sun.COM 2676*9999SWang.Lin@Sun.COM /* 2677*9999SWang.Lin@Sun.COM * Allocate hardware transmit queues: one queue for 2678*9999SWang.Lin@Sun.COM * beacon frames and one data queue for each QoS 2679*9999SWang.Lin@Sun.COM * priority. Note that the hal handles reseting 2680*9999SWang.Lin@Sun.COM * these queues at the needed time. 2681*9999SWang.Lin@Sun.COM */ 2682*9999SWang.Lin@Sun.COM #ifdef ARN_IBSS 2683*9999SWang.Lin@Sun.COM sc->sc_beaconq = arn_beaconq_setup(ah); 2684*9999SWang.Lin@Sun.COM if (sc->sc_beaconq == (-1)) { 2685*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): " 2686*9999SWang.Lin@Sun.COM "unable to setup a beacon xmit queue\n")); 2687*9999SWang.Lin@Sun.COM goto attach_fail4; 2688*9999SWang.Lin@Sun.COM } 2689*9999SWang.Lin@Sun.COM #endif 2690*9999SWang.Lin@Sun.COM #ifdef ARN_HOSTAP 2691*9999SWang.Lin@Sun.COM sc->sc_cabq = arn_txq_setup(sc, ATH9K_TX_QUEUE_CAB, 0); 2692*9999SWang.Lin@Sun.COM if (sc->sc_cabq == NULL) { 2693*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): " 2694*9999SWang.Lin@Sun.COM "unable to setup CAB xmit queue\n")); 2695*9999SWang.Lin@Sun.COM goto attach_fail4; 2696*9999SWang.Lin@Sun.COM } 2697*9999SWang.Lin@Sun.COM 2698*9999SWang.Lin@Sun.COM sc->sc_config.cabqReadytime = ATH_CABQ_READY_TIME; 2699*9999SWang.Lin@Sun.COM ath_cabq_update(sc); 2700*9999SWang.Lin@Sun.COM #endif 2701*9999SWang.Lin@Sun.COM 2702*9999SWang.Lin@Sun.COM for (i = 0; i < ARRAY_SIZE(sc->sc_haltype2q); i++) 2703*9999SWang.Lin@Sun.COM sc->sc_haltype2q[i] = -1; 2704*9999SWang.Lin@Sun.COM 2705*9999SWang.Lin@Sun.COM /* Setup data queues */ 2706*9999SWang.Lin@Sun.COM /* NB: ensure BK queue is the lowest priority h/w queue */ 2707*9999SWang.Lin@Sun.COM if (!arn_tx_setup(sc, ATH9K_WME_AC_BK)) { 2708*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): " 2709*9999SWang.Lin@Sun.COM "unable to setup xmit queue for BK traffic\n")); 2710*9999SWang.Lin@Sun.COM goto attach_fail4; 2711*9999SWang.Lin@Sun.COM } 2712*9999SWang.Lin@Sun.COM if (!arn_tx_setup(sc, ATH9K_WME_AC_BE)) { 2713*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): " 2714*9999SWang.Lin@Sun.COM "unable to setup xmit queue for BE traffic\n")); 2715*9999SWang.Lin@Sun.COM goto attach_fail4; 2716*9999SWang.Lin@Sun.COM } 2717*9999SWang.Lin@Sun.COM if (!arn_tx_setup(sc, ATH9K_WME_AC_VI)) { 2718*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): " 2719*9999SWang.Lin@Sun.COM "unable to setup xmit queue for VI traffic\n")); 2720*9999SWang.Lin@Sun.COM goto attach_fail4; 2721*9999SWang.Lin@Sun.COM } 2722*9999SWang.Lin@Sun.COM if (!arn_tx_setup(sc, ATH9K_WME_AC_VO)) { 2723*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): " 2724*9999SWang.Lin@Sun.COM "unable to setup xmit queue for VO traffic\n")); 2725*9999SWang.Lin@Sun.COM goto attach_fail4; 2726*9999SWang.Lin@Sun.COM } 2727*9999SWang.Lin@Sun.COM 2728*9999SWang.Lin@Sun.COM /* 2729*9999SWang.Lin@Sun.COM * Initializes the noise floor to a reasonable default value. 2730*9999SWang.Lin@Sun.COM * Later on this will be updated during ANI processing. 2731*9999SWang.Lin@Sun.COM */ 2732*9999SWang.Lin@Sun.COM 2733*9999SWang.Lin@Sun.COM sc->sc_ani.sc_noise_floor = ATH_DEFAULT_NOISE_FLOOR; 2734*9999SWang.Lin@Sun.COM 2735*9999SWang.Lin@Sun.COM 2736*9999SWang.Lin@Sun.COM if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER, 2737*9999SWang.Lin@Sun.COM ATH9K_CIPHER_TKIP, NULL)) { 2738*9999SWang.Lin@Sun.COM /* 2739*9999SWang.Lin@Sun.COM * Whether we should enable h/w TKIP MIC. 2740*9999SWang.Lin@Sun.COM * XXX: if we don't support WME TKIP MIC, then we wouldn't 2741*9999SWang.Lin@Sun.COM * report WMM capable, so it's always safe to turn on 2742*9999SWang.Lin@Sun.COM * TKIP MIC in this case. 2743*9999SWang.Lin@Sun.COM */ 2744*9999SWang.Lin@Sun.COM (void) ath9k_hw_setcapability(sc->sc_ah, ATH9K_CAP_TKIP_MIC, 2745*9999SWang.Lin@Sun.COM 0, 1, NULL); 2746*9999SWang.Lin@Sun.COM } 2747*9999SWang.Lin@Sun.COM 2748*9999SWang.Lin@Sun.COM /* Get cipher releated capability information */ 2749*9999SWang.Lin@Sun.COM arn_get_hw_encap(sc); 2750*9999SWang.Lin@Sun.COM 2751*9999SWang.Lin@Sun.COM /* 2752*9999SWang.Lin@Sun.COM * Check whether the separate key cache entries 2753*9999SWang.Lin@Sun.COM * are required to handle both tx+rx MIC keys. 2754*9999SWang.Lin@Sun.COM * With split mic keys the number of stations is limited 2755*9999SWang.Lin@Sun.COM * to 27 otherwise 59. 2756*9999SWang.Lin@Sun.COM */ 2757*9999SWang.Lin@Sun.COM if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER, 2758*9999SWang.Lin@Sun.COM ATH9K_CIPHER_TKIP, NULL) && 2759*9999SWang.Lin@Sun.COM ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER, 2760*9999SWang.Lin@Sun.COM ATH9K_CIPHER_MIC, NULL) && 2761*9999SWang.Lin@Sun.COM ath9k_hw_getcapability(ah, ATH9K_CAP_TKIP_SPLIT, 2762*9999SWang.Lin@Sun.COM 0, NULL)) 2763*9999SWang.Lin@Sun.COM sc->sc_splitmic = 1; 2764*9999SWang.Lin@Sun.COM 2765*9999SWang.Lin@Sun.COM /* turn on mcast key search if possible */ 2766*9999SWang.Lin@Sun.COM if (!ath9k_hw_getcapability(ah, ATH9K_CAP_MCAST_KEYSRCH, 0, NULL)) 2767*9999SWang.Lin@Sun.COM (void) ath9k_hw_setcapability(ah, ATH9K_CAP_MCAST_KEYSRCH, 1, 2768*9999SWang.Lin@Sun.COM 1, NULL); 2769*9999SWang.Lin@Sun.COM 2770*9999SWang.Lin@Sun.COM sc->sc_config.txpowlimit = ATH_TXPOWER_MAX; 2771*9999SWang.Lin@Sun.COM sc->sc_config.txpowlimit_override = 0; 2772*9999SWang.Lin@Sun.COM 2773*9999SWang.Lin@Sun.COM #ifdef ARN_11N 2774*9999SWang.Lin@Sun.COM /* 11n Capabilities */ 2775*9999SWang.Lin@Sun.COM if (ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT) { 2776*9999SWang.Lin@Sun.COM sc->sc_flags |= SC_OP_TXAGGR; 2777*9999SWang.Lin@Sun.COM sc->sc_flags |= SC_OP_RXAGGR; 2778*9999SWang.Lin@Sun.COM } 2779*9999SWang.Lin@Sun.COM #endif 2780*9999SWang.Lin@Sun.COM 2781*9999SWang.Lin@Sun.COM #ifdef ARN_11N 2782*9999SWang.Lin@Sun.COM sc->sc_tx_chainmask = ah->ah_caps.tx_chainmask; 2783*9999SWang.Lin@Sun.COM sc->sc_rx_chainmask = ah->ah_caps.rx_chainmask; 2784*9999SWang.Lin@Sun.COM #else 2785*9999SWang.Lin@Sun.COM sc->sc_tx_chainmask = 1; 2786*9999SWang.Lin@Sun.COM sc->sc_rx_chainmask = 1; 2787*9999SWang.Lin@Sun.COM #endif 2788*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): " 2789*9999SWang.Lin@Sun.COM "tx_chainmask = %d, rx_chainmask = %d\n", 2790*9999SWang.Lin@Sun.COM sc->sc_tx_chainmask, sc->sc_rx_chainmask)); 2791*9999SWang.Lin@Sun.COM 2792*9999SWang.Lin@Sun.COM (void) ath9k_hw_setcapability(ah, ATH9K_CAP_DIVERSITY, 1, B_TRUE, NULL); 2793*9999SWang.Lin@Sun.COM sc->sc_defant = ath9k_hw_getdefantenna(ah); 2794*9999SWang.Lin@Sun.COM 2795*9999SWang.Lin@Sun.COM ath9k_hw_getmac(ah, sc->sc_myaddr); 2796*9999SWang.Lin@Sun.COM if (ah->ah_caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) { 2797*9999SWang.Lin@Sun.COM ath9k_hw_getbssidmask(ah, sc->sc_bssidmask); 2798*9999SWang.Lin@Sun.COM ATH_SET_VAP_BSSID_MASK(sc->sc_bssidmask); 2799*9999SWang.Lin@Sun.COM (void) ath9k_hw_setbssidmask(ah, sc->sc_bssidmask); 2800*9999SWang.Lin@Sun.COM } 2801*9999SWang.Lin@Sun.COM 2802*9999SWang.Lin@Sun.COM /* set default value to short slot time */ 2803*9999SWang.Lin@Sun.COM sc->sc_slottime = ATH9K_SLOT_TIME_9; 2804*9999SWang.Lin@Sun.COM (void) ath9k_hw_setslottime(ah, ATH9K_SLOT_TIME_9); 2805*9999SWang.Lin@Sun.COM 2806*9999SWang.Lin@Sun.COM /* initialize beacon slots */ 2807*9999SWang.Lin@Sun.COM for (i = 0; i < ARRAY_SIZE(sc->sc_bslot); i++) 2808*9999SWang.Lin@Sun.COM sc->sc_bslot[i] = ATH_IF_ID_ANY; 2809*9999SWang.Lin@Sun.COM 2810*9999SWang.Lin@Sun.COM /* save MISC configurations */ 2811*9999SWang.Lin@Sun.COM sc->sc_config.swBeaconProcess = 1; 2812*9999SWang.Lin@Sun.COM 2813*9999SWang.Lin@Sun.COM 2814*9999SWang.Lin@Sun.COM ic->ic_caps |= IEEE80211_C_WPA; /* Support WPA/WPA2 */ 2815*9999SWang.Lin@Sun.COM ic->ic_phytype = IEEE80211_T_OFDM; 2816*9999SWang.Lin@Sun.COM ic->ic_opmode = IEEE80211_M_STA; 2817*9999SWang.Lin@Sun.COM ic->ic_state = IEEE80211_S_INIT; 2818*9999SWang.Lin@Sun.COM ic->ic_maxrssi = ARN_MAX_RSSI; 2819*9999SWang.Lin@Sun.COM ic->ic_set_shortslot = arn_set_shortslot; 2820*9999SWang.Lin@Sun.COM ic->ic_xmit = arn_tx; 2821*9999SWang.Lin@Sun.COM ieee80211_attach(ic); 2822*9999SWang.Lin@Sun.COM 2823*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): " 2824*9999SWang.Lin@Sun.COM "ic->ic_curchan->ich_freq: %d\n", ic->ic_curchan->ich_freq)); 2825*9999SWang.Lin@Sun.COM 2826*9999SWang.Lin@Sun.COM /* different instance has different WPA door */ 2827*9999SWang.Lin@Sun.COM (void) snprintf(ic->ic_wpadoor, MAX_IEEE80211STR, "%s_%s%d", WPA_DOOR, 2828*9999SWang.Lin@Sun.COM ddi_driver_name(devinfo), 2829*9999SWang.Lin@Sun.COM ddi_get_instance(devinfo)); 2830*9999SWang.Lin@Sun.COM 2831*9999SWang.Lin@Sun.COM /* Override 80211 default routines */ 2832*9999SWang.Lin@Sun.COM ic->ic_reset = arn_reset; 2833*9999SWang.Lin@Sun.COM sc->sc_newstate = ic->ic_newstate; 2834*9999SWang.Lin@Sun.COM ic->ic_newstate = arn_newstate; 2835*9999SWang.Lin@Sun.COM #ifdef ARN_IBSS 2836*9999SWang.Lin@Sun.COM sc->sc_recv_mgmt = ic->ic_recv_mgmt; 2837*9999SWang.Lin@Sun.COM ic->ic_recv_mgmt = arn_recv_mgmt; 2838*9999SWang.Lin@Sun.COM #endif 2839*9999SWang.Lin@Sun.COM ic->ic_watchdog = arn_watchdog; 2840*9999SWang.Lin@Sun.COM ic->ic_node_alloc = arn_node_alloc; 2841*9999SWang.Lin@Sun.COM ic->ic_node_free = arn_node_free; 2842*9999SWang.Lin@Sun.COM ic->ic_crypto.cs_key_alloc = arn_key_alloc; 2843*9999SWang.Lin@Sun.COM ic->ic_crypto.cs_key_delete = arn_key_delete; 2844*9999SWang.Lin@Sun.COM ic->ic_crypto.cs_key_set = arn_key_set; 2845*9999SWang.Lin@Sun.COM 2846*9999SWang.Lin@Sun.COM ieee80211_media_init(ic); 2847*9999SWang.Lin@Sun.COM 2848*9999SWang.Lin@Sun.COM /* 2849*9999SWang.Lin@Sun.COM * initialize default tx key 2850*9999SWang.Lin@Sun.COM */ 2851*9999SWang.Lin@Sun.COM ic->ic_def_txkey = 0; 2852*9999SWang.Lin@Sun.COM 2853*9999SWang.Lin@Sun.COM sc->sc_rx_pend = 0; 2854*9999SWang.Lin@Sun.COM (void) ath9k_hw_set_interrupts(sc->sc_ah, 0); 2855*9999SWang.Lin@Sun.COM err = ddi_add_softintr(devinfo, DDI_SOFTINT_LOW, 2856*9999SWang.Lin@Sun.COM &sc->sc_softint_id, NULL, 0, arn_softint_handler, (caddr_t)sc); 2857*9999SWang.Lin@Sun.COM if (err != DDI_SUCCESS) { 2858*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): " 2859*9999SWang.Lin@Sun.COM "ddi_add_softintr() failed....\n")); 2860*9999SWang.Lin@Sun.COM goto attach_fail5; 2861*9999SWang.Lin@Sun.COM } 2862*9999SWang.Lin@Sun.COM 2863*9999SWang.Lin@Sun.COM if (ddi_get_iblock_cookie(devinfo, 0, &sc->sc_iblock) 2864*9999SWang.Lin@Sun.COM != DDI_SUCCESS) { 2865*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): " 2866*9999SWang.Lin@Sun.COM "Can not get iblock cookie for INT\n")); 2867*9999SWang.Lin@Sun.COM goto attach_fail6; 2868*9999SWang.Lin@Sun.COM } 2869*9999SWang.Lin@Sun.COM 2870*9999SWang.Lin@Sun.COM if (ddi_add_intr(devinfo, 0, NULL, NULL, arn_isr, 2871*9999SWang.Lin@Sun.COM (caddr_t)sc) != DDI_SUCCESS) { 2872*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): " 2873*9999SWang.Lin@Sun.COM "Can not set intr for ARN driver\n")); 2874*9999SWang.Lin@Sun.COM goto attach_fail6; 2875*9999SWang.Lin@Sun.COM } 2876*9999SWang.Lin@Sun.COM 2877*9999SWang.Lin@Sun.COM /* 2878*9999SWang.Lin@Sun.COM * Provide initial settings for the WiFi plugin; whenever this 2879*9999SWang.Lin@Sun.COM * information changes, we need to call mac_plugindata_update() 2880*9999SWang.Lin@Sun.COM */ 2881*9999SWang.Lin@Sun.COM wd.wd_opmode = ic->ic_opmode; 2882*9999SWang.Lin@Sun.COM wd.wd_secalloc = WIFI_SEC_NONE; 2883*9999SWang.Lin@Sun.COM IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid); 2884*9999SWang.Lin@Sun.COM 2885*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): " 2886*9999SWang.Lin@Sun.COM "IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid)" 2887*9999SWang.Lin@Sun.COM "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", 2888*9999SWang.Lin@Sun.COM wd.wd_bssid[0], wd.wd_bssid[1], wd.wd_bssid[2], 2889*9999SWang.Lin@Sun.COM wd.wd_bssid[3], wd.wd_bssid[4], wd.wd_bssid[5])); 2890*9999SWang.Lin@Sun.COM 2891*9999SWang.Lin@Sun.COM if ((macp = mac_alloc(MAC_VERSION)) == NULL) { 2892*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): " 2893*9999SWang.Lin@Sun.COM "MAC version mismatch\n")); 2894*9999SWang.Lin@Sun.COM goto attach_fail7; 2895*9999SWang.Lin@Sun.COM } 2896*9999SWang.Lin@Sun.COM 2897*9999SWang.Lin@Sun.COM macp->m_type_ident = MAC_PLUGIN_IDENT_WIFI; 2898*9999SWang.Lin@Sun.COM macp->m_driver = sc; 2899*9999SWang.Lin@Sun.COM macp->m_dip = devinfo; 2900*9999SWang.Lin@Sun.COM macp->m_src_addr = ic->ic_macaddr; 2901*9999SWang.Lin@Sun.COM macp->m_callbacks = &arn_m_callbacks; 2902*9999SWang.Lin@Sun.COM macp->m_min_sdu = 0; 2903*9999SWang.Lin@Sun.COM macp->m_max_sdu = IEEE80211_MTU; 2904*9999SWang.Lin@Sun.COM macp->m_pdata = &wd; 2905*9999SWang.Lin@Sun.COM macp->m_pdata_size = sizeof (wd); 2906*9999SWang.Lin@Sun.COM 2907*9999SWang.Lin@Sun.COM err = mac_register(macp, &ic->ic_mach); 2908*9999SWang.Lin@Sun.COM mac_free(macp); 2909*9999SWang.Lin@Sun.COM if (err != 0) { 2910*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): " 2911*9999SWang.Lin@Sun.COM "mac_register err %x\n", err)); 2912*9999SWang.Lin@Sun.COM goto attach_fail7; 2913*9999SWang.Lin@Sun.COM } 2914*9999SWang.Lin@Sun.COM 2915*9999SWang.Lin@Sun.COM /* Create minor node of type DDI_NT_NET_WIFI */ 2916*9999SWang.Lin@Sun.COM (void) snprintf(strbuf, sizeof (strbuf), "%s%d", 2917*9999SWang.Lin@Sun.COM ARN_NODENAME, instance); 2918*9999SWang.Lin@Sun.COM err = ddi_create_minor_node(devinfo, strbuf, S_IFCHR, 2919*9999SWang.Lin@Sun.COM instance + 1, DDI_NT_NET_WIFI, 0); 2920*9999SWang.Lin@Sun.COM if (err != DDI_SUCCESS) 2921*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "WARN: arn: arn_attach(): " 2922*9999SWang.Lin@Sun.COM "Create minor node failed - %d\n", err)); 2923*9999SWang.Lin@Sun.COM 2924*9999SWang.Lin@Sun.COM mac_link_update(ic->ic_mach, LINK_STATE_DOWN); 2925*9999SWang.Lin@Sun.COM 2926*9999SWang.Lin@Sun.COM sc->sc_promisc = B_FALSE; 2927*9999SWang.Lin@Sun.COM bzero(sc->sc_mcast_refs, sizeof (sc->sc_mcast_refs)); 2928*9999SWang.Lin@Sun.COM bzero(sc->sc_mcast_hash, sizeof (sc->sc_mcast_hash)); 2929*9999SWang.Lin@Sun.COM 2930*9999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): " 2931*9999SWang.Lin@Sun.COM "Atheros AR%s MAC/BB Rev:%x " 2932*9999SWang.Lin@Sun.COM "AR%s RF Rev:%x: mem=0x%lx\n", 2933*9999SWang.Lin@Sun.COM arn_mac_bb_name(ah->ah_macVersion), 2934*9999SWang.Lin@Sun.COM ah->ah_macRev, 2935*9999SWang.Lin@Sun.COM arn_rf_name((ah->ah_analog5GhzRev & AR_RADIO_SREV_MAJOR)), 2936*9999SWang.Lin@Sun.COM ah->ah_phyRev, 2937*9999SWang.Lin@Sun.COM (unsigned long)sc->mem)); 2938*9999SWang.Lin@Sun.COM 2939*9999SWang.Lin@Sun.COM /* XXX: hardware will not be ready until arn_open() being called */ 2940*9999SWang.Lin@Sun.COM sc->sc_flags |= SC_OP_INVALID; 2941*9999SWang.Lin@Sun.COM sc->sc_isrunning = 0; 2942*9999SWang.Lin@Sun.COM 2943*9999SWang.Lin@Sun.COM return (DDI_SUCCESS); 2944*9999SWang.Lin@Sun.COM 2945*9999SWang.Lin@Sun.COM attach_fail7: 2946*9999SWang.Lin@Sun.COM ddi_remove_intr(devinfo, 0, sc->sc_iblock); 2947*9999SWang.Lin@Sun.COM attach_fail6: 2948*9999SWang.Lin@Sun.COM ddi_remove_softintr(sc->sc_softint_id); 2949*9999SWang.Lin@Sun.COM attach_fail5: 2950*9999SWang.Lin@Sun.COM (void) ieee80211_detach(ic); 2951*9999SWang.Lin@Sun.COM attach_fail4: 2952*9999SWang.Lin@Sun.COM arn_desc_free(sc); 2953*9999SWang.Lin@Sun.COM if (sc->sc_tq) 2954*9999SWang.Lin@Sun.COM ddi_taskq_destroy(sc->sc_tq); 2955*9999SWang.Lin@Sun.COM attach_fail3: 2956*9999SWang.Lin@Sun.COM ath9k_hw_detach(ah); 2957*9999SWang.Lin@Sun.COM attach_fail2: 2958*9999SWang.Lin@Sun.COM ddi_regs_map_free(&sc->sc_io_handle); 2959*9999SWang.Lin@Sun.COM attach_fail1: 2960*9999SWang.Lin@Sun.COM pci_config_teardown(&sc->sc_cfg_handle); 2961*9999SWang.Lin@Sun.COM attach_fail0: 2962*9999SWang.Lin@Sun.COM sc->sc_flags |= SC_OP_INVALID; 2963*9999SWang.Lin@Sun.COM /* cleanup tx queues */ 2964*9999SWang.Lin@Sun.COM mutex_destroy(&sc->sc_txbuflock); 2965*9999SWang.Lin@Sun.COM for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { 2966*9999SWang.Lin@Sun.COM if (ARN_TXQ_SETUP(sc, i)) { 2967*9999SWang.Lin@Sun.COM /* arn_tx_cleanupq(asc, &asc->sc_txq[i]); */ 2968*9999SWang.Lin@Sun.COM mutex_destroy(&((&sc->sc_txq[i])->axq_lock)); 2969*9999SWang.Lin@Sun.COM } 2970*9999SWang.Lin@Sun.COM } 2971*9999SWang.Lin@Sun.COM mutex_destroy(&sc->sc_rxbuflock); 2972*9999SWang.Lin@Sun.COM mutex_destroy(&sc->sc_serial_rw); 2973*9999SWang.Lin@Sun.COM mutex_destroy(&sc->sc_genlock); 2974*9999SWang.Lin@Sun.COM mutex_destroy(&sc->sc_resched_lock); 2975*9999SWang.Lin@Sun.COM #ifdef ARN_IBSS 2976*9999SWang.Lin@Sun.COM mutex_destroy(&sc->sc_bcbuflock); 2977*9999SWang.Lin@Sun.COM #endif 2978*9999SWang.Lin@Sun.COM 2979*9999SWang.Lin@Sun.COM ddi_soft_state_free(arn_soft_state_p, instance); 2980*9999SWang.Lin@Sun.COM 2981*9999SWang.Lin@Sun.COM return (DDI_FAILURE); 2982*9999SWang.Lin@Sun.COM 2983*9999SWang.Lin@Sun.COM } 2984*9999SWang.Lin@Sun.COM 2985*9999SWang.Lin@Sun.COM /* 2986*9999SWang.Lin@Sun.COM * Suspend transmit/receive for powerdown 2987*9999SWang.Lin@Sun.COM */ 2988*9999SWang.Lin@Sun.COM static int 2989*9999SWang.Lin@Sun.COM arn_suspend(struct arn_softc *sc) 2990*9999SWang.Lin@Sun.COM { 2991*9999SWang.Lin@Sun.COM ARN_LOCK(sc); 2992*9999SWang.Lin@Sun.COM arn_close(sc); 2993*9999SWang.Lin@Sun.COM ARN_UNLOCK(sc); 2994*9999SWang.Lin@Sun.COM 2995*9999SWang.Lin@Sun.COM return (DDI_SUCCESS); 2996*9999SWang.Lin@Sun.COM } 2997*9999SWang.Lin@Sun.COM 2998*9999SWang.Lin@Sun.COM static int32_t 2999*9999SWang.Lin@Sun.COM arn_detach(dev_info_t *devinfo, ddi_detach_cmd_t cmd) 3000*9999SWang.Lin@Sun.COM { 3001*9999SWang.Lin@Sun.COM struct arn_softc *sc; 3002*9999SWang.Lin@Sun.COM int i; 3003*9999SWang.Lin@Sun.COM 3004*9999SWang.Lin@Sun.COM sc = ddi_get_soft_state(arn_soft_state_p, ddi_get_instance(devinfo)); 3005*9999SWang.Lin@Sun.COM ASSERT(sc != NULL); 3006*9999SWang.Lin@Sun.COM 3007*9999SWang.Lin@Sun.COM switch (cmd) { 3008*9999SWang.Lin@Sun.COM case DDI_DETACH: 3009*9999SWang.Lin@Sun.COM break; 3010*9999SWang.Lin@Sun.COM 3011*9999SWang.Lin@Sun.COM case DDI_SUSPEND: 3012*9999SWang.Lin@Sun.COM return (arn_suspend(sc)); 3013*9999SWang.Lin@Sun.COM 3014*9999SWang.Lin@Sun.COM default: 3015*9999SWang.Lin@Sun.COM return (DDI_FAILURE); 3016*9999SWang.Lin@Sun.COM } 3017*9999SWang.Lin@Sun.COM 3018*9999SWang.Lin@Sun.COM if (mac_disable(sc->sc_isc.ic_mach) != 0) 3019*9999SWang.Lin@Sun.COM return (DDI_FAILURE); 3020*9999SWang.Lin@Sun.COM 3021*9999SWang.Lin@Sun.COM arn_stop_scantimer(sc); 3022*9999SWang.Lin@Sun.COM arn_stop_caltimer(sc); 3023*9999SWang.Lin@Sun.COM 3024*9999SWang.Lin@Sun.COM /* disable interrupts */ 3025*9999SWang.Lin@Sun.COM (void) ath9k_hw_set_interrupts(sc->sc_ah, 0); 3026*9999SWang.Lin@Sun.COM 3027*9999SWang.Lin@Sun.COM /* 3028*9999SWang.Lin@Sun.COM * Unregister from the MAC layer subsystem 3029*9999SWang.Lin@Sun.COM */ 3030*9999SWang.Lin@Sun.COM (void) mac_unregister(sc->sc_isc.ic_mach); 3031*9999SWang.Lin@Sun.COM 3032*9999SWang.Lin@Sun.COM /* free intterrupt resources */ 3033*9999SWang.Lin@Sun.COM ddi_remove_intr(devinfo, 0, sc->sc_iblock); 3034*9999SWang.Lin@Sun.COM ddi_remove_softintr(sc->sc_softint_id); 3035*9999SWang.Lin@Sun.COM 3036*9999SWang.Lin@Sun.COM /* 3037*9999SWang.Lin@Sun.COM * NB: the order of these is important: 3038*9999SWang.Lin@Sun.COM * o call the 802.11 layer before detaching the hal to 3039*9999SWang.Lin@Sun.COM * insure callbacks into the driver to delete global 3040*9999SWang.Lin@Sun.COM * key cache entries can be handled 3041*9999SWang.Lin@Sun.COM * o reclaim the tx queue data structures after calling 3042*9999SWang.Lin@Sun.COM * the 802.11 layer as we'll get called back to reclaim 3043*9999SWang.Lin@Sun.COM * node state and potentially want to use them 3044*9999SWang.Lin@Sun.COM * o to cleanup the tx queues the hal is called, so detach 3045*9999SWang.Lin@Sun.COM * it last 3046*9999SWang.Lin@Sun.COM */ 3047*9999SWang.Lin@Sun.COM ieee80211_detach(&sc->sc_isc); 3048*9999SWang.Lin@Sun.COM 3049*9999SWang.Lin@Sun.COM arn_desc_free(sc); 3050*9999SWang.Lin@Sun.COM 3051*9999SWang.Lin@Sun.COM ddi_taskq_destroy(sc->sc_tq); 3052*9999SWang.Lin@Sun.COM 3053*9999SWang.Lin@Sun.COM if (!(sc->sc_flags & SC_OP_INVALID)) 3054*9999SWang.Lin@Sun.COM (void) ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE); 3055*9999SWang.Lin@Sun.COM 3056*9999SWang.Lin@Sun.COM /* cleanup tx queues */ 3057*9999SWang.Lin@Sun.COM mutex_destroy(&sc->sc_txbuflock); 3058*9999SWang.Lin@Sun.COM for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { 3059*9999SWang.Lin@Sun.COM if (ARN_TXQ_SETUP(sc, i)) { 3060*9999SWang.Lin@Sun.COM arn_tx_cleanupq(sc, &sc->sc_txq[i]); 3061*9999SWang.Lin@Sun.COM mutex_destroy(&((&sc->sc_txq[i])->axq_lock)); 3062*9999SWang.Lin@Sun.COM } 3063*9999SWang.Lin@Sun.COM } 3064*9999SWang.Lin@Sun.COM 3065*9999SWang.Lin@Sun.COM ath9k_hw_detach(sc->sc_ah); 3066*9999SWang.Lin@Sun.COM 3067*9999SWang.Lin@Sun.COM /* free io handle */ 3068*9999SWang.Lin@Sun.COM ddi_regs_map_free(&sc->sc_io_handle); 3069*9999SWang.Lin@Sun.COM pci_config_teardown(&sc->sc_cfg_handle); 3070*9999SWang.Lin@Sun.COM 3071*9999SWang.Lin@Sun.COM /* destroy locks */ 3072*9999SWang.Lin@Sun.COM mutex_destroy(&sc->sc_genlock); 3073*9999SWang.Lin@Sun.COM mutex_destroy(&sc->sc_serial_rw); 3074*9999SWang.Lin@Sun.COM mutex_destroy(&sc->sc_rxbuflock); 3075*9999SWang.Lin@Sun.COM mutex_destroy(&sc->sc_resched_lock); 3076*9999SWang.Lin@Sun.COM #ifdef ARN_IBSS 3077*9999SWang.Lin@Sun.COM mutex_destroy(&sc->sc_bcbuflock); 3078*9999SWang.Lin@Sun.COM #endif 3079*9999SWang.Lin@Sun.COM 3080*9999SWang.Lin@Sun.COM ddi_remove_minor_node(devinfo, NULL); 3081*9999SWang.Lin@Sun.COM ddi_soft_state_free(arn_soft_state_p, ddi_get_instance(devinfo)); 3082*9999SWang.Lin@Sun.COM 3083*9999SWang.Lin@Sun.COM return (DDI_SUCCESS); 3084*9999SWang.Lin@Sun.COM } 3085*9999SWang.Lin@Sun.COM 3086*9999SWang.Lin@Sun.COM /* 3087*9999SWang.Lin@Sun.COM * quiesce(9E) entry point. 3088*9999SWang.Lin@Sun.COM * 3089*9999SWang.Lin@Sun.COM * This function is called when the system is single-threaded at high 3090*9999SWang.Lin@Sun.COM * PIL with preemption disabled. Therefore, this function must not be 3091*9999SWang.Lin@Sun.COM * blocked. 3092*9999SWang.Lin@Sun.COM * 3093*9999SWang.Lin@Sun.COM * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure. 3094*9999SWang.Lin@Sun.COM * DDI_FAILURE indicates an error condition and should almost never happen. 3095*9999SWang.Lin@Sun.COM */ 3096*9999SWang.Lin@Sun.COM static int32_t 3097*9999SWang.Lin@Sun.COM arn_quiesce(dev_info_t *devinfo) 3098*9999SWang.Lin@Sun.COM { 3099*9999SWang.Lin@Sun.COM struct arn_softc *sc; 3100*9999SWang.Lin@Sun.COM int i; 3101*9999SWang.Lin@Sun.COM struct ath_hal *ah; 3102*9999SWang.Lin@Sun.COM 3103*9999SWang.Lin@Sun.COM sc = ddi_get_soft_state(arn_soft_state_p, ddi_get_instance(devinfo)); 3104*9999SWang.Lin@Sun.COM 3105*9999SWang.Lin@Sun.COM if (sc == NULL || (ah = sc->sc_ah) == NULL) 3106*9999SWang.Lin@Sun.COM return (DDI_FAILURE); 3107*9999SWang.Lin@Sun.COM 3108*9999SWang.Lin@Sun.COM /* 3109*9999SWang.Lin@Sun.COM * Disable interrupts 3110*9999SWang.Lin@Sun.COM */ 3111*9999SWang.Lin@Sun.COM (void) ath9k_hw_set_interrupts(ah, 0); 3112*9999SWang.Lin@Sun.COM 3113*9999SWang.Lin@Sun.COM /* 3114*9999SWang.Lin@Sun.COM * Disable TX HW 3115*9999SWang.Lin@Sun.COM */ 3116*9999SWang.Lin@Sun.COM for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { 3117*9999SWang.Lin@Sun.COM if (ARN_TXQ_SETUP(sc, i)) 3118*9999SWang.Lin@Sun.COM (void) ath9k_hw_stoptxdma(ah, sc->sc_txq[i].axq_qnum); 3119*9999SWang.Lin@Sun.COM } 3120*9999SWang.Lin@Sun.COM 3121*9999SWang.Lin@Sun.COM /* 3122*9999SWang.Lin@Sun.COM * Disable RX HW 3123*9999SWang.Lin@Sun.COM */ 3124*9999SWang.Lin@Sun.COM ath9k_hw_stoppcurecv(ah); 3125*9999SWang.Lin@Sun.COM ath9k_hw_setrxfilter(ah, 0); 3126*9999SWang.Lin@Sun.COM (void) ath9k_hw_stopdmarecv(ah); 3127*9999SWang.Lin@Sun.COM drv_usecwait(3000); 3128*9999SWang.Lin@Sun.COM 3129*9999SWang.Lin@Sun.COM /* 3130*9999SWang.Lin@Sun.COM * Power down HW 3131*9999SWang.Lin@Sun.COM */ 3132*9999SWang.Lin@Sun.COM (void) ath9k_hw_phy_disable(ah); 3133*9999SWang.Lin@Sun.COM 3134*9999SWang.Lin@Sun.COM return (DDI_SUCCESS); 3135*9999SWang.Lin@Sun.COM } 3136*9999SWang.Lin@Sun.COM 3137*9999SWang.Lin@Sun.COM DDI_DEFINE_STREAM_OPS(arn_dev_ops, nulldev, nulldev, arn_attach, arn_detach, 3138*9999SWang.Lin@Sun.COM nodev, NULL, D_MP, NULL, arn_quiesce); 3139*9999SWang.Lin@Sun.COM 3140*9999SWang.Lin@Sun.COM static struct modldrv arn_modldrv = { 3141*9999SWang.Lin@Sun.COM &mod_driverops, /* Type of module. This one is a driver */ 3142*9999SWang.Lin@Sun.COM "arn-Atheros 9000 series driver:vertion 1.1", /* short description */ 3143*9999SWang.Lin@Sun.COM &arn_dev_ops /* driver specific ops */ 3144*9999SWang.Lin@Sun.COM }; 3145*9999SWang.Lin@Sun.COM 3146*9999SWang.Lin@Sun.COM static struct modlinkage modlinkage = { 3147*9999SWang.Lin@Sun.COM MODREV_1, (void *)&arn_modldrv, NULL 3148*9999SWang.Lin@Sun.COM }; 3149*9999SWang.Lin@Sun.COM 3150*9999SWang.Lin@Sun.COM int 3151*9999SWang.Lin@Sun.COM _info(struct modinfo *modinfop) 3152*9999SWang.Lin@Sun.COM { 3153*9999SWang.Lin@Sun.COM return (mod_info(&modlinkage, modinfop)); 3154*9999SWang.Lin@Sun.COM } 3155*9999SWang.Lin@Sun.COM 3156*9999SWang.Lin@Sun.COM int 3157*9999SWang.Lin@Sun.COM _init(void) 3158*9999SWang.Lin@Sun.COM { 3159*9999SWang.Lin@Sun.COM int status; 3160*9999SWang.Lin@Sun.COM 3161*9999SWang.Lin@Sun.COM status = ddi_soft_state_init 3162*9999SWang.Lin@Sun.COM (&arn_soft_state_p, sizeof (struct arn_softc), 1); 3163*9999SWang.Lin@Sun.COM if (status != 0) 3164*9999SWang.Lin@Sun.COM return (status); 3165*9999SWang.Lin@Sun.COM 3166*9999SWang.Lin@Sun.COM mutex_init(&arn_loglock, NULL, MUTEX_DRIVER, NULL); 3167*9999SWang.Lin@Sun.COM mac_init_ops(&arn_dev_ops, "arn"); 3168*9999SWang.Lin@Sun.COM status = mod_install(&modlinkage); 3169*9999SWang.Lin@Sun.COM if (status != 0) { 3170*9999SWang.Lin@Sun.COM mac_fini_ops(&arn_dev_ops); 3171*9999SWang.Lin@Sun.COM mutex_destroy(&arn_loglock); 3172*9999SWang.Lin@Sun.COM ddi_soft_state_fini(&arn_soft_state_p); 3173*9999SWang.Lin@Sun.COM } 3174*9999SWang.Lin@Sun.COM 3175*9999SWang.Lin@Sun.COM return (status); 3176*9999SWang.Lin@Sun.COM } 3177*9999SWang.Lin@Sun.COM 3178*9999SWang.Lin@Sun.COM int 3179*9999SWang.Lin@Sun.COM _fini(void) 3180*9999SWang.Lin@Sun.COM { 3181*9999SWang.Lin@Sun.COM int status; 3182*9999SWang.Lin@Sun.COM 3183*9999SWang.Lin@Sun.COM status = mod_remove(&modlinkage); 3184*9999SWang.Lin@Sun.COM if (status == 0) { 3185*9999SWang.Lin@Sun.COM mac_fini_ops(&arn_dev_ops); 3186*9999SWang.Lin@Sun.COM mutex_destroy(&arn_loglock); 3187*9999SWang.Lin@Sun.COM ddi_soft_state_fini(&arn_soft_state_p); 3188*9999SWang.Lin@Sun.COM } 3189*9999SWang.Lin@Sun.COM return (status); 3190*9999SWang.Lin@Sun.COM } 3191