1*9172SFei.Feng@Sun.COM /* 2*9172SFei.Feng@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 3*9172SFei.Feng@Sun.COM * Use is subject to license terms. 4*9172SFei.Feng@Sun.COM */ 5*9172SFei.Feng@Sun.COM 6*9172SFei.Feng@Sun.COM /* 7*9172SFei.Feng@Sun.COM * Copyright (c) 2007, 2008 8*9172SFei.Feng@Sun.COM * Damien Bergamini <damien.bergamini@free.fr> 9*9172SFei.Feng@Sun.COM * 10*9172SFei.Feng@Sun.COM * Permission to use, copy, modify, and distribute this software for any 11*9172SFei.Feng@Sun.COM * purpose with or without fee is hereby granted, provided that the above 12*9172SFei.Feng@Sun.COM * copyright notice and this permission notice appear in all copies. 13*9172SFei.Feng@Sun.COM * 14*9172SFei.Feng@Sun.COM * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 15*9172SFei.Feng@Sun.COM * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 16*9172SFei.Feng@Sun.COM * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 17*9172SFei.Feng@Sun.COM * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 18*9172SFei.Feng@Sun.COM * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 19*9172SFei.Feng@Sun.COM * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 20*9172SFei.Feng@Sun.COM * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 21*9172SFei.Feng@Sun.COM */ 22*9172SFei.Feng@Sun.COM 23*9172SFei.Feng@Sun.COM /* 24*9172SFei.Feng@Sun.COM * Ralink Technology RT2860 chipset driver 25*9172SFei.Feng@Sun.COM * http://www.ralinktech.com/ 26*9172SFei.Feng@Sun.COM */ 27*9172SFei.Feng@Sun.COM 28*9172SFei.Feng@Sun.COM #include <sys/types.h> 29*9172SFei.Feng@Sun.COM #include <sys/byteorder.h> 30*9172SFei.Feng@Sun.COM #include <sys/conf.h> 31*9172SFei.Feng@Sun.COM #include <sys/cmn_err.h> 32*9172SFei.Feng@Sun.COM #include <sys/stat.h> 33*9172SFei.Feng@Sun.COM #include <sys/ddi.h> 34*9172SFei.Feng@Sun.COM #include <sys/sunddi.h> 35*9172SFei.Feng@Sun.COM #include <sys/strsubr.h> 36*9172SFei.Feng@Sun.COM #include <inet/common.h> 37*9172SFei.Feng@Sun.COM #include <sys/note.h> 38*9172SFei.Feng@Sun.COM #include <sys/stream.h> 39*9172SFei.Feng@Sun.COM #include <sys/strsun.h> 40*9172SFei.Feng@Sun.COM #include <sys/modctl.h> 41*9172SFei.Feng@Sun.COM #include <sys/devops.h> 42*9172SFei.Feng@Sun.COM #include <sys/mac_provider.h> 43*9172SFei.Feng@Sun.COM #include <sys/mac_wifi.h> 44*9172SFei.Feng@Sun.COM #include <sys/net80211.h> 45*9172SFei.Feng@Sun.COM #include <sys/net80211_proto.h> 46*9172SFei.Feng@Sun.COM #include <sys/varargs.h> 47*9172SFei.Feng@Sun.COM #include <sys/pci.h> 48*9172SFei.Feng@Sun.COM #include <sys/crypto/common.h> 49*9172SFei.Feng@Sun.COM #include <sys/crypto/api.h> 50*9172SFei.Feng@Sun.COM #include <inet/wifi_ioctl.h> 51*9172SFei.Feng@Sun.COM 52*9172SFei.Feng@Sun.COM #include "rt2860_reg.h" 53*9172SFei.Feng@Sun.COM #include "rt2860_var.h" 54*9172SFei.Feng@Sun.COM 55*9172SFei.Feng@Sun.COM #define RT2860_DBG_80211 (1 << 0) 56*9172SFei.Feng@Sun.COM #define RT2860_DBG_DMA (1 << 1) 57*9172SFei.Feng@Sun.COM #define RT2860_DBG_EEPROM (1 << 2) 58*9172SFei.Feng@Sun.COM #define RT2860_DBG_FW (1 << 3) 59*9172SFei.Feng@Sun.COM #define RT2860_DBG_HW (1 << 4) 60*9172SFei.Feng@Sun.COM #define RT2860_DBG_INTR (1 << 5) 61*9172SFei.Feng@Sun.COM #define RT2860_DBG_RX (1 << 6) 62*9172SFei.Feng@Sun.COM #define RT2860_DBG_SCAN (1 << 7) 63*9172SFei.Feng@Sun.COM #define RT2860_DBG_TX (1 << 8) 64*9172SFei.Feng@Sun.COM #define RT2860_DBG_RADIO (1 << 9) 65*9172SFei.Feng@Sun.COM #define RT2860_DBG_RESUME (1 << 10) 66*9172SFei.Feng@Sun.COM #define RT2860_DBG_MSG (1 << 11) 67*9172SFei.Feng@Sun.COM 68*9172SFei.Feng@Sun.COM uint32_t rt2860_dbg_flags = 0x0; 69*9172SFei.Feng@Sun.COM 70*9172SFei.Feng@Sun.COM #ifdef DEBUG 71*9172SFei.Feng@Sun.COM #define RWN_DEBUG \ 72*9172SFei.Feng@Sun.COM rt2860_debug 73*9172SFei.Feng@Sun.COM #else 74*9172SFei.Feng@Sun.COM #define RWN_DEBUG 75*9172SFei.Feng@Sun.COM #endif 76*9172SFei.Feng@Sun.COM 77*9172SFei.Feng@Sun.COM static void *rt2860_soft_state_p = NULL; 78*9172SFei.Feng@Sun.COM static uint8_t rt2860_fw_bin [] = { 79*9172SFei.Feng@Sun.COM #include "fw-rt2860/rt2860.ucode" 80*9172SFei.Feng@Sun.COM }; 81*9172SFei.Feng@Sun.COM 82*9172SFei.Feng@Sun.COM static const struct ieee80211_rateset rt2560_rateset_11b = 83*9172SFei.Feng@Sun.COM { 4, { 2, 4, 11, 22 } }; 84*9172SFei.Feng@Sun.COM 85*9172SFei.Feng@Sun.COM static const struct ieee80211_rateset rt2560_rateset_11g = 86*9172SFei.Feng@Sun.COM { 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } }; 87*9172SFei.Feng@Sun.COM 88*9172SFei.Feng@Sun.COM static const struct { 89*9172SFei.Feng@Sun.COM uint32_t reg; 90*9172SFei.Feng@Sun.COM uint32_t val; 91*9172SFei.Feng@Sun.COM } rt2860_def_mac[] = { 92*9172SFei.Feng@Sun.COM RT2860_DEF_MAC 93*9172SFei.Feng@Sun.COM }; 94*9172SFei.Feng@Sun.COM 95*9172SFei.Feng@Sun.COM static const struct { 96*9172SFei.Feng@Sun.COM uint8_t reg; 97*9172SFei.Feng@Sun.COM uint8_t val; 98*9172SFei.Feng@Sun.COM } rt2860_def_bbp[] = { 99*9172SFei.Feng@Sun.COM RT2860_DEF_BBP 100*9172SFei.Feng@Sun.COM }; 101*9172SFei.Feng@Sun.COM 102*9172SFei.Feng@Sun.COM static const struct rfprog { 103*9172SFei.Feng@Sun.COM uint8_t chan; 104*9172SFei.Feng@Sun.COM uint32_t r1, r2, r3, r4; 105*9172SFei.Feng@Sun.COM } rt2860_rf2850[] = { 106*9172SFei.Feng@Sun.COM RT2860_RF2850 107*9172SFei.Feng@Sun.COM }; 108*9172SFei.Feng@Sun.COM 109*9172SFei.Feng@Sun.COM /* 110*9172SFei.Feng@Sun.COM * PIO access attributes for registers 111*9172SFei.Feng@Sun.COM */ 112*9172SFei.Feng@Sun.COM static ddi_device_acc_attr_t rwn_csr_accattr = { 113*9172SFei.Feng@Sun.COM DDI_DEVICE_ATTR_V0, 114*9172SFei.Feng@Sun.COM DDI_STRUCTURE_LE_ACC, 115*9172SFei.Feng@Sun.COM DDI_STRICTORDER_ACC 116*9172SFei.Feng@Sun.COM }; 117*9172SFei.Feng@Sun.COM 118*9172SFei.Feng@Sun.COM /* 119*9172SFei.Feng@Sun.COM * DMA access attributes for descriptors: NOT to be byte swapped. 120*9172SFei.Feng@Sun.COM */ 121*9172SFei.Feng@Sun.COM static ddi_device_acc_attr_t rt2860_desc_accattr = { 122*9172SFei.Feng@Sun.COM DDI_DEVICE_ATTR_V0, 123*9172SFei.Feng@Sun.COM DDI_STRUCTURE_LE_ACC, 124*9172SFei.Feng@Sun.COM DDI_STRICTORDER_ACC 125*9172SFei.Feng@Sun.COM }; 126*9172SFei.Feng@Sun.COM 127*9172SFei.Feng@Sun.COM static ddi_device_acc_attr_t rt2860_buf_accattr = { 128*9172SFei.Feng@Sun.COM DDI_DEVICE_ATTR_V0, 129*9172SFei.Feng@Sun.COM DDI_NEVERSWAP_ACC, 130*9172SFei.Feng@Sun.COM DDI_STRICTORDER_ACC, 131*9172SFei.Feng@Sun.COM DDI_DEFAULT_ACC 132*9172SFei.Feng@Sun.COM }; 133*9172SFei.Feng@Sun.COM 134*9172SFei.Feng@Sun.COM /* 135*9172SFei.Feng@Sun.COM * Describes the chip's DMA engine 136*9172SFei.Feng@Sun.COM */ 137*9172SFei.Feng@Sun.COM static ddi_dma_attr_t rt2860_dma_attr = { 138*9172SFei.Feng@Sun.COM DMA_ATTR_V0, /* dma_attr version */ 139*9172SFei.Feng@Sun.COM 0x0, /* dma_attr_addr_lo */ 140*9172SFei.Feng@Sun.COM 0xffffffffU, /* dma_attr_addr_hi */ 141*9172SFei.Feng@Sun.COM 0xffffffffU, /* dma_attr_count_max */ 142*9172SFei.Feng@Sun.COM 16, /* dma_attr_align */ 143*9172SFei.Feng@Sun.COM 0x00000fff, /* dma_attr_burstsizes */ 144*9172SFei.Feng@Sun.COM 1, /* dma_attr_minxfer */ 145*9172SFei.Feng@Sun.COM 0xffffffffU, /* dma_attr_maxxfer */ 146*9172SFei.Feng@Sun.COM 0xffffffffU, /* dma_attr_seg */ 147*9172SFei.Feng@Sun.COM 1, /* dma_attr_sgllen */ 148*9172SFei.Feng@Sun.COM 1, /* dma_attr_granular */ 149*9172SFei.Feng@Sun.COM 0 /* dma_attr_flags */ 150*9172SFei.Feng@Sun.COM }; 151*9172SFei.Feng@Sun.COM 152*9172SFei.Feng@Sun.COM static uint16_t rt2860_eeprom_read(struct rt2860_softc *, uint8_t); 153*9172SFei.Feng@Sun.COM static int rt2860_read_eeprom(struct rt2860_softc *); 154*9172SFei.Feng@Sun.COM const char *rt2860_get_rf(uint8_t); 155*9172SFei.Feng@Sun.COM static int rt2860_alloc_dma_mem(dev_info_t *, ddi_dma_attr_t *, size_t, 156*9172SFei.Feng@Sun.COM ddi_device_acc_attr_t *, uint_t, uint_t, struct dma_area *); 157*9172SFei.Feng@Sun.COM static void rt2860_free_dma_mem(struct dma_area *); 158*9172SFei.Feng@Sun.COM static int rt2860_alloc_tx_ring(struct rt2860_softc *, 159*9172SFei.Feng@Sun.COM struct rt2860_tx_ring *); 160*9172SFei.Feng@Sun.COM static void rt2860_free_tx_ring(struct rt2860_softc *, 161*9172SFei.Feng@Sun.COM struct rt2860_tx_ring *); 162*9172SFei.Feng@Sun.COM static int rt2860_alloc_rx_ring(struct rt2860_softc *, 163*9172SFei.Feng@Sun.COM struct rt2860_rx_ring *); 164*9172SFei.Feng@Sun.COM static void rt2860_free_rx_ring(struct rt2860_softc *, 165*9172SFei.Feng@Sun.COM struct rt2860_rx_ring *); 166*9172SFei.Feng@Sun.COM static int rt2860_alloc_tx_pool(struct rt2860_softc *); 167*9172SFei.Feng@Sun.COM static void rt2860_free_tx_pool(struct rt2860_softc *); 168*9172SFei.Feng@Sun.COM static uint16_t rt2860_txtime(int, int, uint32_t); 169*9172SFei.Feng@Sun.COM static int rt2860_ack_rate(struct ieee80211com *, int); 170*9172SFei.Feng@Sun.COM static int rt2860_send(ieee80211com_t *, mblk_t *, uint8_t); 171*9172SFei.Feng@Sun.COM static uint8_t rt2860_maxrssi_chain(struct rt2860_softc *, 172*9172SFei.Feng@Sun.COM const struct rt2860_rxwi *); 173*9172SFei.Feng@Sun.COM static void rt2860_drain_stats_fifo(struct rt2860_softc *); 174*9172SFei.Feng@Sun.COM static void rt2860_tx_intr(struct rt2860_softc *, int); 175*9172SFei.Feng@Sun.COM static void rt2860_rx_intr(struct rt2860_softc *); 176*9172SFei.Feng@Sun.COM static uint_t rt2860_softintr(caddr_t); 177*9172SFei.Feng@Sun.COM static uint_t rt2860_intr(caddr_t); 178*9172SFei.Feng@Sun.COM static void rt2860_set_region_4(struct rt2860_softc *, 179*9172SFei.Feng@Sun.COM uint32_t, uint32_t, int); 180*9172SFei.Feng@Sun.COM static int rt2860_load_microcode(struct rt2860_softc *); 181*9172SFei.Feng@Sun.COM static void rt2860_set_macaddr(struct rt2860_softc *, const uint8_t *); 182*9172SFei.Feng@Sun.COM static int rt2860_bbp_init(struct rt2860_softc *); 183*9172SFei.Feng@Sun.COM static uint8_t rt2860_mcu_bbp_read(struct rt2860_softc *, uint8_t); 184*9172SFei.Feng@Sun.COM static void rt2860_mcu_bbp_write(struct rt2860_softc *, uint8_t, uint8_t); 185*9172SFei.Feng@Sun.COM static int rt2860_mcu_cmd(struct rt2860_softc *, uint8_t, uint16_t); 186*9172SFei.Feng@Sun.COM static void rt2860_rf_write(struct rt2860_softc *, uint8_t, uint32_t); 187*9172SFei.Feng@Sun.COM static void rt2860_select_chan_group(struct rt2860_softc *, int); 188*9172SFei.Feng@Sun.COM static void rt2860_set_chan(struct rt2860_softc *, 189*9172SFei.Feng@Sun.COM struct ieee80211_channel *); 190*9172SFei.Feng@Sun.COM static void rt2860_updateprot(struct ieee80211com *); 191*9172SFei.Feng@Sun.COM static void rt2860_set_leds(struct rt2860_softc *, uint16_t); 192*9172SFei.Feng@Sun.COM static void rt2860_next_scan(void *); 193*9172SFei.Feng@Sun.COM static void rt2860_iter_func(void *, struct ieee80211_node *); 194*9172SFei.Feng@Sun.COM static void rt2860_updateslot(struct rt2860_softc *); 195*9172SFei.Feng@Sun.COM static uint8_t rt2860_rate2mcs(uint8_t); 196*9172SFei.Feng@Sun.COM static void rt2860_enable_mrr(struct rt2860_softc *); 197*9172SFei.Feng@Sun.COM static void rt2860_set_txpreamble(struct rt2860_softc *); 198*9172SFei.Feng@Sun.COM static void rt2860_set_basicrates(struct rt2860_softc *); 199*9172SFei.Feng@Sun.COM static void rt2860_set_bssid(struct rt2860_softc *, const uint8_t *); 200*9172SFei.Feng@Sun.COM static void rt2860_amrr_node_init(const struct rt2860_amrr *, 201*9172SFei.Feng@Sun.COM struct rt2860_amrr_node *); 202*9172SFei.Feng@Sun.COM static void rt2860_amrr_choose(struct rt2860_amrr *, 203*9172SFei.Feng@Sun.COM struct ieee80211_node *, struct rt2860_amrr_node *); 204*9172SFei.Feng@Sun.COM static void rt2860_newassoc(struct ieee80211com *, struct ieee80211_node *, 205*9172SFei.Feng@Sun.COM int); 206*9172SFei.Feng@Sun.COM static void rt2860_enable_tsf_sync(struct rt2860_softc *); 207*9172SFei.Feng@Sun.COM static int rt2860_newstate(struct ieee80211com *, 208*9172SFei.Feng@Sun.COM enum ieee80211_state, int); 209*9172SFei.Feng@Sun.COM static int rt2860_init(struct rt2860_softc *); 210*9172SFei.Feng@Sun.COM static void rt2860_stop(struct rt2860_softc *); 211*9172SFei.Feng@Sun.COM static int rt2860_quiesce(dev_info_t *t); 212*9172SFei.Feng@Sun.COM 213*9172SFei.Feng@Sun.COM /* 214*9172SFei.Feng@Sun.COM * device operations 215*9172SFei.Feng@Sun.COM */ 216*9172SFei.Feng@Sun.COM static int rt2860_attach(dev_info_t *, ddi_attach_cmd_t); 217*9172SFei.Feng@Sun.COM static int rt2860_detach(dev_info_t *, ddi_detach_cmd_t); 218*9172SFei.Feng@Sun.COM 219*9172SFei.Feng@Sun.COM /* 220*9172SFei.Feng@Sun.COM * Module Loading Data & Entry Points 221*9172SFei.Feng@Sun.COM */ 222*9172SFei.Feng@Sun.COM DDI_DEFINE_STREAM_OPS(rwn_dev_ops, nulldev, nulldev, rt2860_attach, 223*9172SFei.Feng@Sun.COM rt2860_detach, nodev, NULL, D_MP, NULL, rt2860_quiesce); 224*9172SFei.Feng@Sun.COM 225*9172SFei.Feng@Sun.COM static struct modldrv rwn_modldrv = { 226*9172SFei.Feng@Sun.COM &mod_driverops, /* Type of module. This one is a driver */ 227*9172SFei.Feng@Sun.COM "Ralink RT2700/2800 driver v1.1", /* short description */ 228*9172SFei.Feng@Sun.COM &rwn_dev_ops /* driver specific ops */ 229*9172SFei.Feng@Sun.COM }; 230*9172SFei.Feng@Sun.COM 231*9172SFei.Feng@Sun.COM static struct modlinkage modlinkage = { 232*9172SFei.Feng@Sun.COM MODREV_1, 233*9172SFei.Feng@Sun.COM (void *)&rwn_modldrv, 234*9172SFei.Feng@Sun.COM NULL 235*9172SFei.Feng@Sun.COM }; 236*9172SFei.Feng@Sun.COM 237*9172SFei.Feng@Sun.COM static int rt2860_m_stat(void *, uint_t, uint64_t *); 238*9172SFei.Feng@Sun.COM static int rt2860_m_start(void *); 239*9172SFei.Feng@Sun.COM static void rt2860_m_stop(void *); 240*9172SFei.Feng@Sun.COM static int rt2860_m_promisc(void *, boolean_t); 241*9172SFei.Feng@Sun.COM static int rt2860_m_multicst(void *, boolean_t, const uint8_t *); 242*9172SFei.Feng@Sun.COM static int rt2860_m_unicst(void *, const uint8_t *); 243*9172SFei.Feng@Sun.COM static mblk_t *rt2860_m_tx(void *, mblk_t *); 244*9172SFei.Feng@Sun.COM static void rt2860_m_ioctl(void *, queue_t *, mblk_t *); 245*9172SFei.Feng@Sun.COM static int rt2860_m_setprop(void *arg, const char *pr_name, 246*9172SFei.Feng@Sun.COM mac_prop_id_t wldp_pr_num, 247*9172SFei.Feng@Sun.COM uint_t wldp_length, const void *wldp_buf); 248*9172SFei.Feng@Sun.COM static int rt2860_m_getprop(void *arg, const char *pr_name, 249*9172SFei.Feng@Sun.COM mac_prop_id_t wldp_pr_num, uint_t pr_flags, 250*9172SFei.Feng@Sun.COM uint_t wldp_length, void *wldp_buf, uint_t *); 251*9172SFei.Feng@Sun.COM 252*9172SFei.Feng@Sun.COM static mac_callbacks_t rt2860_m_callbacks = { 253*9172SFei.Feng@Sun.COM MC_IOCTL | MC_SETPROP | MC_GETPROP, 254*9172SFei.Feng@Sun.COM rt2860_m_stat, 255*9172SFei.Feng@Sun.COM rt2860_m_start, 256*9172SFei.Feng@Sun.COM rt2860_m_stop, 257*9172SFei.Feng@Sun.COM rt2860_m_promisc, 258*9172SFei.Feng@Sun.COM rt2860_m_multicst, 259*9172SFei.Feng@Sun.COM rt2860_m_unicst, 260*9172SFei.Feng@Sun.COM rt2860_m_tx, 261*9172SFei.Feng@Sun.COM rt2860_m_ioctl, 262*9172SFei.Feng@Sun.COM NULL, 263*9172SFei.Feng@Sun.COM NULL, 264*9172SFei.Feng@Sun.COM NULL, 265*9172SFei.Feng@Sun.COM rt2860_m_setprop, 266*9172SFei.Feng@Sun.COM rt2860_m_getprop 267*9172SFei.Feng@Sun.COM }; 268*9172SFei.Feng@Sun.COM 269*9172SFei.Feng@Sun.COM #ifdef DEBUG 270*9172SFei.Feng@Sun.COM void 271*9172SFei.Feng@Sun.COM rt2860_debug(uint32_t dbg_flags, const int8_t *fmt, ...) 272*9172SFei.Feng@Sun.COM { 273*9172SFei.Feng@Sun.COM va_list args; 274*9172SFei.Feng@Sun.COM 275*9172SFei.Feng@Sun.COM if (dbg_flags & rt2860_dbg_flags) { 276*9172SFei.Feng@Sun.COM va_start(args, fmt); 277*9172SFei.Feng@Sun.COM vcmn_err(CE_CONT, fmt, args); 278*9172SFei.Feng@Sun.COM va_end(args); 279*9172SFei.Feng@Sun.COM } 280*9172SFei.Feng@Sun.COM } 281*9172SFei.Feng@Sun.COM #endif 282*9172SFei.Feng@Sun.COM 283*9172SFei.Feng@Sun.COM const char * 284*9172SFei.Feng@Sun.COM rt2860_get_rf(uint8_t rev) 285*9172SFei.Feng@Sun.COM { 286*9172SFei.Feng@Sun.COM switch (rev) { 287*9172SFei.Feng@Sun.COM case RT2860_RF_2820: return "RT2820"; 288*9172SFei.Feng@Sun.COM case RT2860_RF_2850: return "RT2850"; 289*9172SFei.Feng@Sun.COM case RT2860_RF_2720: return "RT2720"; 290*9172SFei.Feng@Sun.COM case RT2860_RF_2750: return "RT2750"; 291*9172SFei.Feng@Sun.COM default: return "unknown"; 292*9172SFei.Feng@Sun.COM } 293*9172SFei.Feng@Sun.COM } 294*9172SFei.Feng@Sun.COM 295*9172SFei.Feng@Sun.COM /* 296*9172SFei.Feng@Sun.COM * Read 16 bits at address 'addr' from the serial EEPROM (either 93C46, 297*9172SFei.Feng@Sun.COM * 93C66 or 93C86). 298*9172SFei.Feng@Sun.COM */ 299*9172SFei.Feng@Sun.COM static uint16_t 300*9172SFei.Feng@Sun.COM rt2860_eeprom_read(struct rt2860_softc *sc, uint8_t addr) 301*9172SFei.Feng@Sun.COM { 302*9172SFei.Feng@Sun.COM int n; 303*9172SFei.Feng@Sun.COM uint16_t val; 304*9172SFei.Feng@Sun.COM uint32_t tmp; 305*9172SFei.Feng@Sun.COM 306*9172SFei.Feng@Sun.COM /* clock C once before the first command */ 307*9172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, 0); 308*9172SFei.Feng@Sun.COM 309*9172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, RT2860_S); 310*9172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, RT2860_S | RT2860_C); 311*9172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, RT2860_S); 312*9172SFei.Feng@Sun.COM 313*9172SFei.Feng@Sun.COM /* write start bit (1) */ 314*9172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, RT2860_S | RT2860_D); 315*9172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, RT2860_S | RT2860_D | RT2860_C); 316*9172SFei.Feng@Sun.COM 317*9172SFei.Feng@Sun.COM /* write READ opcode (10) */ 318*9172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, RT2860_S | RT2860_D); 319*9172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, RT2860_S | RT2860_D | RT2860_C); 320*9172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, RT2860_S); 321*9172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, RT2860_S | RT2860_C); 322*9172SFei.Feng@Sun.COM 323*9172SFei.Feng@Sun.COM /* write address (A5-A0 or A7-A0) */ 324*9172SFei.Feng@Sun.COM n = ((RT2860_READ(sc, RT2860_PCI_EECTRL) & 0x30) == 0) ? 5 : 7; 325*9172SFei.Feng@Sun.COM for (; n >= 0; n--) { 326*9172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, RT2860_S | 327*9172SFei.Feng@Sun.COM (((addr >> n) & 1) << RT2860_SHIFT_D)); 328*9172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, RT2860_S | 329*9172SFei.Feng@Sun.COM (((addr >> n) & 1) << RT2860_SHIFT_D) | RT2860_C); 330*9172SFei.Feng@Sun.COM } 331*9172SFei.Feng@Sun.COM 332*9172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, RT2860_S); 333*9172SFei.Feng@Sun.COM 334*9172SFei.Feng@Sun.COM /* read data Q15-Q0 */ 335*9172SFei.Feng@Sun.COM val = 0; 336*9172SFei.Feng@Sun.COM for (n = 15; n >= 0; n--) { 337*9172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, RT2860_S | RT2860_C); 338*9172SFei.Feng@Sun.COM tmp = RT2860_READ(sc, RT2860_PCI_EECTRL); 339*9172SFei.Feng@Sun.COM val |= ((tmp & RT2860_Q) >> RT2860_SHIFT_Q) << n; 340*9172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, RT2860_S); 341*9172SFei.Feng@Sun.COM } 342*9172SFei.Feng@Sun.COM 343*9172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, 0); 344*9172SFei.Feng@Sun.COM 345*9172SFei.Feng@Sun.COM /* clear Chip Select and clock C */ 346*9172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, RT2860_S); 347*9172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, 0); 348*9172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, RT2860_C); 349*9172SFei.Feng@Sun.COM 350*9172SFei.Feng@Sun.COM return (val); 351*9172SFei.Feng@Sun.COM } 352*9172SFei.Feng@Sun.COM 353*9172SFei.Feng@Sun.COM /* 354*9172SFei.Feng@Sun.COM * Add `delta' (signed) to each 4-bit sub-word of a 32-bit word. 355*9172SFei.Feng@Sun.COM * Used to adjust per-rate Tx power registers. 356*9172SFei.Feng@Sun.COM */ 357*9172SFei.Feng@Sun.COM static inline uint32_t 358*9172SFei.Feng@Sun.COM b4inc(uint32_t b32, int8_t delta) 359*9172SFei.Feng@Sun.COM { 360*9172SFei.Feng@Sun.COM int8_t i, b4; 361*9172SFei.Feng@Sun.COM 362*9172SFei.Feng@Sun.COM for (i = 0; i < 8; i++) { 363*9172SFei.Feng@Sun.COM b4 = b32 & 0xf; 364*9172SFei.Feng@Sun.COM b4 += delta; 365*9172SFei.Feng@Sun.COM if (b4 < 0) 366*9172SFei.Feng@Sun.COM b4 = 0; 367*9172SFei.Feng@Sun.COM else if (b4 > 0xf) 368*9172SFei.Feng@Sun.COM b4 = 0xf; 369*9172SFei.Feng@Sun.COM b32 = b32 >> 4 | b4 << 28; 370*9172SFei.Feng@Sun.COM } 371*9172SFei.Feng@Sun.COM return (b32); 372*9172SFei.Feng@Sun.COM } 373*9172SFei.Feng@Sun.COM 374*9172SFei.Feng@Sun.COM static int 375*9172SFei.Feng@Sun.COM rt2860_read_eeprom(struct rt2860_softc *sc) 376*9172SFei.Feng@Sun.COM { 377*9172SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic; 378*9172SFei.Feng@Sun.COM int ridx, ant, i; 379*9172SFei.Feng@Sun.COM int8_t delta_2ghz, delta_5ghz; 380*9172SFei.Feng@Sun.COM uint16_t val; 381*9172SFei.Feng@Sun.COM 382*9172SFei.Feng@Sun.COM /* read EEPROM version */ 383*9172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_VERSION); 384*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): " 385*9172SFei.Feng@Sun.COM "EEPROM rev=%d, FAE=%d\n", 386*9172SFei.Feng@Sun.COM val & 0xff, val >> 8); 387*9172SFei.Feng@Sun.COM 388*9172SFei.Feng@Sun.COM /* read MAC address */ 389*9172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_MAC01); 390*9172SFei.Feng@Sun.COM ic->ic_macaddr[0] = val & 0xff; 391*9172SFei.Feng@Sun.COM ic->ic_macaddr[1] = val >> 8; 392*9172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_MAC23); 393*9172SFei.Feng@Sun.COM ic->ic_macaddr[2] = val & 0xff; 394*9172SFei.Feng@Sun.COM ic->ic_macaddr[3] = val >> 8; 395*9172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_MAC45); 396*9172SFei.Feng@Sun.COM ic->ic_macaddr[4] = val & 0xff; 397*9172SFei.Feng@Sun.COM ic->ic_macaddr[5] = val >> 8; 398*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): " 399*9172SFei.Feng@Sun.COM "MAC address is: %x:%x:%x:%x:%x:%x\n", 400*9172SFei.Feng@Sun.COM ic->ic_macaddr[0], ic->ic_macaddr[1], 401*9172SFei.Feng@Sun.COM ic->ic_macaddr[2], ic->ic_macaddr[3], 402*9172SFei.Feng@Sun.COM ic->ic_macaddr[4], ic->ic_macaddr[5]); 403*9172SFei.Feng@Sun.COM 404*9172SFei.Feng@Sun.COM /* read country code */ 405*9172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_COUNTRY); 406*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): " 407*9172SFei.Feng@Sun.COM "EEPROM region code=0x%04x\n", val); 408*9172SFei.Feng@Sun.COM 409*9172SFei.Feng@Sun.COM /* read default BBP settings */ 410*9172SFei.Feng@Sun.COM for (i = 0; i < 8; i++) { 411*9172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_BBP_BASE + i); 412*9172SFei.Feng@Sun.COM sc->bbp[i].val = val & 0xff; 413*9172SFei.Feng@Sun.COM sc->bbp[i].reg = val >> 8; 414*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): " 415*9172SFei.Feng@Sun.COM "BBP%d=0x%02x\n", 416*9172SFei.Feng@Sun.COM sc->bbp[i].reg, sc->bbp[i].val); 417*9172SFei.Feng@Sun.COM } 418*9172SFei.Feng@Sun.COM 419*9172SFei.Feng@Sun.COM /* read RF frequency offset from EEPROM */ 420*9172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_FREQ_LEDS); 421*9172SFei.Feng@Sun.COM sc->freq = ((val & 0xff) != 0xff) ? val & 0xff : 0; 422*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): " 423*9172SFei.Feng@Sun.COM "EEPROM freq offset %d\n", sc->freq & 0xff); 424*9172SFei.Feng@Sun.COM 425*9172SFei.Feng@Sun.COM if ((sc->leds = val >> 8) != 0xff) { 426*9172SFei.Feng@Sun.COM /* read LEDs operating mode */ 427*9172SFei.Feng@Sun.COM sc->led[0] = rt2860_eeprom_read(sc, RT2860_EEPROM_LED1); 428*9172SFei.Feng@Sun.COM sc->led[1] = rt2860_eeprom_read(sc, RT2860_EEPROM_LED2); 429*9172SFei.Feng@Sun.COM sc->led[2] = rt2860_eeprom_read(sc, RT2860_EEPROM_LED3); 430*9172SFei.Feng@Sun.COM } else { 431*9172SFei.Feng@Sun.COM /* broken EEPROM, use default settings */ 432*9172SFei.Feng@Sun.COM sc->leds = 0x01; 433*9172SFei.Feng@Sun.COM sc->led[0] = 0x5555; 434*9172SFei.Feng@Sun.COM sc->led[1] = 0x2221; 435*9172SFei.Feng@Sun.COM sc->led[2] = 0xa9f8; 436*9172SFei.Feng@Sun.COM } 437*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): " 438*9172SFei.Feng@Sun.COM "EEPROM LED mode=0x%02x, LEDs=0x%04x/0x%04x/0x%04x\n", 439*9172SFei.Feng@Sun.COM sc->leds, sc->led[0], sc->led[1], sc->led[2]); 440*9172SFei.Feng@Sun.COM 441*9172SFei.Feng@Sun.COM /* read RF information */ 442*9172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_ANTENNA); 443*9172SFei.Feng@Sun.COM if (val == 0xffff) { 444*9172SFei.Feng@Sun.COM /* broken EEPROM, default to RF2820 1T2R */ 445*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): " 446*9172SFei.Feng@Sun.COM "invalid EEPROM antenna info, using default\n"); 447*9172SFei.Feng@Sun.COM sc->rf_rev = RT2860_RF_2820; 448*9172SFei.Feng@Sun.COM sc->ntxchains = 1; 449*9172SFei.Feng@Sun.COM sc->nrxchains = 2; 450*9172SFei.Feng@Sun.COM } else { 451*9172SFei.Feng@Sun.COM sc->rf_rev = (val >> 8) & 0xf; 452*9172SFei.Feng@Sun.COM sc->ntxchains = (val >> 4) & 0xf; 453*9172SFei.Feng@Sun.COM sc->nrxchains = val & 0xf; 454*9172SFei.Feng@Sun.COM } 455*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): " 456*9172SFei.Feng@Sun.COM "EEPROM RF rev=0x%02x chains=%dT%dR\n", 457*9172SFei.Feng@Sun.COM sc->rf_rev, sc->ntxchains, sc->nrxchains); 458*9172SFei.Feng@Sun.COM 459*9172SFei.Feng@Sun.COM /* check if RF supports automatic Tx access gain control */ 460*9172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_CONFIG); 461*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): " 462*9172SFei.Feng@Sun.COM "EEPROM CFG 0x%04x\n", val); 463*9172SFei.Feng@Sun.COM if ((val & 0xff) != 0xff) 464*9172SFei.Feng@Sun.COM sc->calib_2ghz = sc->calib_5ghz = 0; /* XXX (val >> 1) & 1 */; 465*9172SFei.Feng@Sun.COM 466*9172SFei.Feng@Sun.COM if (sc->sc_flags & RT2860_ADVANCED_PS) { 467*9172SFei.Feng@Sun.COM /* read PCIe power save level */ 468*9172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_PCIE_PSLEVEL); 469*9172SFei.Feng@Sun.COM if ((val & 0xff) != 0xff) { 470*9172SFei.Feng@Sun.COM sc->pslevel = val & 0x3; 471*9172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_REV); 472*9172SFei.Feng@Sun.COM if (val >> 8 != 0x92 || !(val & 0x80)) 473*9172SFei.Feng@Sun.COM sc->pslevel = MIN(sc->pslevel, 1); 474*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, 475*9172SFei.Feng@Sun.COM "rwn: rt2860_read_eeprom(): " 476*9172SFei.Feng@Sun.COM "EEPROM PCIe PS Level=%d\n", 477*9172SFei.Feng@Sun.COM sc->pslevel); 478*9172SFei.Feng@Sun.COM } 479*9172SFei.Feng@Sun.COM } 480*9172SFei.Feng@Sun.COM /* read power settings for 2GHz channels */ 481*9172SFei.Feng@Sun.COM for (i = 0; i < 14; i += 2) { 482*9172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, 483*9172SFei.Feng@Sun.COM RT2860_EEPROM_PWR2GHZ_BASE1 + i / 2); 484*9172SFei.Feng@Sun.COM sc->txpow1[i + 0] = (int8_t)(val & 0xff); 485*9172SFei.Feng@Sun.COM sc->txpow1[i + 1] = (int8_t)(val >> 8); 486*9172SFei.Feng@Sun.COM 487*9172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, 488*9172SFei.Feng@Sun.COM RT2860_EEPROM_PWR2GHZ_BASE2 + i / 2); 489*9172SFei.Feng@Sun.COM sc->txpow2[i + 0] = (int8_t)(val & 0xff); 490*9172SFei.Feng@Sun.COM sc->txpow2[i + 1] = (int8_t)(val >> 8); 491*9172SFei.Feng@Sun.COM } 492*9172SFei.Feng@Sun.COM /* fix broken Tx power entries */ 493*9172SFei.Feng@Sun.COM for (i = 0; i < 14; i++) { 494*9172SFei.Feng@Sun.COM if (sc->txpow1[i] < 0 || sc->txpow1[i] > 31) 495*9172SFei.Feng@Sun.COM sc->txpow1[i] = 5; 496*9172SFei.Feng@Sun.COM if (sc->txpow2[i] < 0 || sc->txpow2[i] > 31) 497*9172SFei.Feng@Sun.COM sc->txpow2[i] = 5; 498*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): " 499*9172SFei.Feng@Sun.COM "chan %d: power1=%d, power2=%d\n", 500*9172SFei.Feng@Sun.COM rt2860_rf2850[i].chan, sc->txpow1[i], sc->txpow2[i]); 501*9172SFei.Feng@Sun.COM } 502*9172SFei.Feng@Sun.COM /* read power settings for 5GHz channels */ 503*9172SFei.Feng@Sun.COM for (i = 0; i < 36; i += 2) { 504*9172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, 505*9172SFei.Feng@Sun.COM RT2860_EEPROM_PWR5GHZ_BASE1 + i / 2); 506*9172SFei.Feng@Sun.COM sc->txpow1[i + 14] = (int8_t)(val & 0xff); 507*9172SFei.Feng@Sun.COM sc->txpow1[i + 15] = (int8_t)(val >> 8); 508*9172SFei.Feng@Sun.COM 509*9172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, 510*9172SFei.Feng@Sun.COM RT2860_EEPROM_PWR5GHZ_BASE2 + i / 2); 511*9172SFei.Feng@Sun.COM sc->txpow2[i + 14] = (int8_t)(val & 0xff); 512*9172SFei.Feng@Sun.COM sc->txpow2[i + 15] = (int8_t)(val >> 8); 513*9172SFei.Feng@Sun.COM } 514*9172SFei.Feng@Sun.COM /* fix broken Tx power entries */ 515*9172SFei.Feng@Sun.COM for (i = 0; i < 36; i++) { 516*9172SFei.Feng@Sun.COM if (sc->txpow1[14 + i] < -7 || sc->txpow1[14 + i] > 15) 517*9172SFei.Feng@Sun.COM sc->txpow1[14 + i] = 5; 518*9172SFei.Feng@Sun.COM if (sc->txpow2[14 + i] < -7 || sc->txpow2[14 + i] > 15) 519*9172SFei.Feng@Sun.COM sc->txpow2[14 + i] = 5; 520*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): " 521*9172SFei.Feng@Sun.COM "chan %d: power1=%d, power2=%d\n", 522*9172SFei.Feng@Sun.COM rt2860_rf2850[14 + i].chan, sc->txpow1[14 + i], 523*9172SFei.Feng@Sun.COM sc->txpow2[14 + i]); 524*9172SFei.Feng@Sun.COM } 525*9172SFei.Feng@Sun.COM 526*9172SFei.Feng@Sun.COM /* read Tx power compensation for each Tx rate */ 527*9172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_DELTAPWR); 528*9172SFei.Feng@Sun.COM delta_2ghz = delta_5ghz = 0; 529*9172SFei.Feng@Sun.COM if ((val & 0xff) != 0xff && (val & 0x80)) { 530*9172SFei.Feng@Sun.COM delta_2ghz = val & 0xf; 531*9172SFei.Feng@Sun.COM if (!(val & 0x40)) /* negative number */ 532*9172SFei.Feng@Sun.COM delta_2ghz = -delta_2ghz; 533*9172SFei.Feng@Sun.COM } 534*9172SFei.Feng@Sun.COM val >>= 8; 535*9172SFei.Feng@Sun.COM if ((val & 0xff) != 0xff && (val & 0x80)) { 536*9172SFei.Feng@Sun.COM delta_5ghz = val & 0xf; 537*9172SFei.Feng@Sun.COM if (!(val & 0x40)) /* negative number */ 538*9172SFei.Feng@Sun.COM delta_5ghz = -delta_5ghz; 539*9172SFei.Feng@Sun.COM } 540*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): " 541*9172SFei.Feng@Sun.COM "power compensation=%d (2GHz), %d (5GHz) \n", 542*9172SFei.Feng@Sun.COM delta_2ghz, delta_5ghz); 543*9172SFei.Feng@Sun.COM 544*9172SFei.Feng@Sun.COM for (ridx = 0; ridx < 5; ridx++) { 545*9172SFei.Feng@Sun.COM uint32_t reg; 546*9172SFei.Feng@Sun.COM 547*9172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_RPWR + ridx); 548*9172SFei.Feng@Sun.COM reg = (uint32_t)val << 16; 549*9172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_RPWR + ridx + 1); 550*9172SFei.Feng@Sun.COM reg |= val; 551*9172SFei.Feng@Sun.COM 552*9172SFei.Feng@Sun.COM sc->txpow20mhz[ridx] = reg; 553*9172SFei.Feng@Sun.COM sc->txpow40mhz_2ghz[ridx] = b4inc(reg, delta_2ghz); 554*9172SFei.Feng@Sun.COM sc->txpow40mhz_5ghz[ridx] = b4inc(reg, delta_5ghz); 555*9172SFei.Feng@Sun.COM 556*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): " 557*9172SFei.Feng@Sun.COM "ridx %d: power 20MHz=0x%08x, 40MHz/2GHz=0x%08x, " 558*9172SFei.Feng@Sun.COM "40MHz/5GHz=0x%08x\n", ridx, sc->txpow20mhz[ridx], 559*9172SFei.Feng@Sun.COM sc->txpow40mhz_2ghz[ridx], sc->txpow40mhz_5ghz[ridx]); 560*9172SFei.Feng@Sun.COM } 561*9172SFei.Feng@Sun.COM 562*9172SFei.Feng@Sun.COM /* read factory-calibrated samples for temperature compensation */ 563*9172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI1_2GHZ); 564*9172SFei.Feng@Sun.COM sc->tssi_2ghz[0] = val & 0xff; /* [-4] */ 565*9172SFei.Feng@Sun.COM sc->tssi_2ghz[1] = val >> 8; /* [-3] */ 566*9172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI2_2GHZ); 567*9172SFei.Feng@Sun.COM sc->tssi_2ghz[2] = val & 0xff; /* [-2] */ 568*9172SFei.Feng@Sun.COM sc->tssi_2ghz[3] = val >> 8; /* [-1] */ 569*9172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI3_2GHZ); 570*9172SFei.Feng@Sun.COM sc->tssi_2ghz[4] = val & 0xff; /* [+0] */ 571*9172SFei.Feng@Sun.COM sc->tssi_2ghz[5] = val >> 8; /* [+1] */ 572*9172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI4_2GHZ); 573*9172SFei.Feng@Sun.COM sc->tssi_2ghz[6] = val & 0xff; /* [+2] */ 574*9172SFei.Feng@Sun.COM sc->tssi_2ghz[7] = val >> 8; /* [+3] */ 575*9172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI5_2GHZ); 576*9172SFei.Feng@Sun.COM sc->tssi_2ghz[8] = val & 0xff; /* [+4] */ 577*9172SFei.Feng@Sun.COM sc->step_2ghz = val >> 8; 578*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): " 579*9172SFei.Feng@Sun.COM "TSSI 2GHz: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x " 580*9172SFei.Feng@Sun.COM "0x%02x 0x%02x step=%d\n", sc->tssi_2ghz[0], sc->tssi_2ghz[1], 581*9172SFei.Feng@Sun.COM sc->tssi_2ghz[2], sc->tssi_2ghz[3], sc->tssi_2ghz[4], 582*9172SFei.Feng@Sun.COM sc->tssi_2ghz[5], sc->tssi_2ghz[6], sc->tssi_2ghz[7], 583*9172SFei.Feng@Sun.COM sc->tssi_2ghz[8], sc->step_2ghz); 584*9172SFei.Feng@Sun.COM /* check that ref value is correct, otherwise disable calibration */ 585*9172SFei.Feng@Sun.COM if (sc->tssi_2ghz[4] == 0xff) 586*9172SFei.Feng@Sun.COM sc->calib_2ghz = 0; 587*9172SFei.Feng@Sun.COM 588*9172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI1_5GHZ); 589*9172SFei.Feng@Sun.COM sc->tssi_5ghz[0] = val & 0xff; /* [-4] */ 590*9172SFei.Feng@Sun.COM sc->tssi_5ghz[1] = val >> 8; /* [-3] */ 591*9172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI2_5GHZ); 592*9172SFei.Feng@Sun.COM sc->tssi_5ghz[2] = val & 0xff; /* [-2] */ 593*9172SFei.Feng@Sun.COM sc->tssi_5ghz[3] = val >> 8; /* [-1] */ 594*9172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI3_5GHZ); 595*9172SFei.Feng@Sun.COM sc->tssi_5ghz[4] = val & 0xff; /* [+0] */ 596*9172SFei.Feng@Sun.COM sc->tssi_5ghz[5] = val >> 8; /* [+1] */ 597*9172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI4_5GHZ); 598*9172SFei.Feng@Sun.COM sc->tssi_5ghz[6] = val & 0xff; /* [+2] */ 599*9172SFei.Feng@Sun.COM sc->tssi_5ghz[7] = val >> 8; /* [+3] */ 600*9172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI5_5GHZ); 601*9172SFei.Feng@Sun.COM sc->tssi_5ghz[8] = val & 0xff; /* [+4] */ 602*9172SFei.Feng@Sun.COM sc->step_5ghz = val >> 8; 603*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): " 604*9172SFei.Feng@Sun.COM "TSSI 5GHz: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x " 605*9172SFei.Feng@Sun.COM "0x%02x 0x%02x step=%d\n", sc->tssi_5ghz[0], sc->tssi_5ghz[1], 606*9172SFei.Feng@Sun.COM sc->tssi_5ghz[2], sc->tssi_5ghz[3], sc->tssi_5ghz[4], 607*9172SFei.Feng@Sun.COM sc->tssi_5ghz[5], sc->tssi_5ghz[6], sc->tssi_5ghz[7], 608*9172SFei.Feng@Sun.COM sc->tssi_5ghz[8], sc->step_5ghz); 609*9172SFei.Feng@Sun.COM /* check that ref value is correct, otherwise disable calibration */ 610*9172SFei.Feng@Sun.COM if (sc->tssi_5ghz[4] == 0xff) 611*9172SFei.Feng@Sun.COM sc->calib_5ghz = 0; 612*9172SFei.Feng@Sun.COM 613*9172SFei.Feng@Sun.COM /* read RSSI offsets and LNA gains from EEPROM */ 614*9172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_RSSI1_2GHZ); 615*9172SFei.Feng@Sun.COM sc->rssi_2ghz[0] = val & 0xff; /* Ant A */ 616*9172SFei.Feng@Sun.COM sc->rssi_2ghz[1] = val >> 8; /* Ant B */ 617*9172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_RSSI2_2GHZ); 618*9172SFei.Feng@Sun.COM sc->rssi_2ghz[2] = val & 0xff; /* Ant C */ 619*9172SFei.Feng@Sun.COM sc->lna[2] = val >> 8; /* channel group 2 */ 620*9172SFei.Feng@Sun.COM 621*9172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_RSSI1_5GHZ); 622*9172SFei.Feng@Sun.COM sc->rssi_5ghz[0] = val & 0xff; /* Ant A */ 623*9172SFei.Feng@Sun.COM sc->rssi_5ghz[1] = val >> 8; /* Ant B */ 624*9172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_RSSI2_5GHZ); 625*9172SFei.Feng@Sun.COM sc->rssi_5ghz[2] = val & 0xff; /* Ant C */ 626*9172SFei.Feng@Sun.COM sc->lna[3] = val >> 8; /* channel group 3 */ 627*9172SFei.Feng@Sun.COM 628*9172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_LNA); 629*9172SFei.Feng@Sun.COM sc->lna[0] = val & 0xff; /* channel group 0 */ 630*9172SFei.Feng@Sun.COM sc->lna[1] = val >> 8; /* channel group 1 */ 631*9172SFei.Feng@Sun.COM 632*9172SFei.Feng@Sun.COM /* fix broken 5GHz LNA entries */ 633*9172SFei.Feng@Sun.COM if (sc->lna[2] == 0 || sc->lna[2] == 0xff) { 634*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): " 635*9172SFei.Feng@Sun.COM "invalid LNA for channel group %d\n", 2); 636*9172SFei.Feng@Sun.COM sc->lna[2] = sc->lna[1]; 637*9172SFei.Feng@Sun.COM } 638*9172SFei.Feng@Sun.COM if (sc->lna[3] == 0 || sc->lna[3] == 0xff) { 639*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): " 640*9172SFei.Feng@Sun.COM "invalid LNA for channel group %d\n", 3); 641*9172SFei.Feng@Sun.COM sc->lna[3] = sc->lna[1]; 642*9172SFei.Feng@Sun.COM } 643*9172SFei.Feng@Sun.COM 644*9172SFei.Feng@Sun.COM /* fix broken RSSI offset entries */ 645*9172SFei.Feng@Sun.COM for (ant = 0; ant < 3; ant++) { 646*9172SFei.Feng@Sun.COM if (sc->rssi_2ghz[ant] < -10 || sc->rssi_2ghz[ant] > 10) { 647*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, 648*9172SFei.Feng@Sun.COM "rwn: rt2860_read_eeprom(): " 649*9172SFei.Feng@Sun.COM "invalid RSSI%d offset: %d (2GHz)\n", 650*9172SFei.Feng@Sun.COM ant + 1, sc->rssi_2ghz[ant]); 651*9172SFei.Feng@Sun.COM sc->rssi_2ghz[ant] = 0; 652*9172SFei.Feng@Sun.COM } 653*9172SFei.Feng@Sun.COM if (sc->rssi_5ghz[ant] < -10 || sc->rssi_5ghz[ant] > 10) { 654*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, 655*9172SFei.Feng@Sun.COM "rwn: rt2860_read_eeprom(): " 656*9172SFei.Feng@Sun.COM "invalid RSSI%d offset: %d (2GHz)\n", 657*9172SFei.Feng@Sun.COM ant + 1, sc->rssi_5ghz[ant]); 658*9172SFei.Feng@Sun.COM sc->rssi_5ghz[ant] = 0; 659*9172SFei.Feng@Sun.COM } 660*9172SFei.Feng@Sun.COM } 661*9172SFei.Feng@Sun.COM 662*9172SFei.Feng@Sun.COM return (RT2860_SUCCESS); 663*9172SFei.Feng@Sun.COM } 664*9172SFei.Feng@Sun.COM 665*9172SFei.Feng@Sun.COM /* 666*9172SFei.Feng@Sun.COM * Allocate an DMA memory and a DMA handle for accessing it 667*9172SFei.Feng@Sun.COM */ 668*9172SFei.Feng@Sun.COM static int 669*9172SFei.Feng@Sun.COM rt2860_alloc_dma_mem(dev_info_t *devinfo, ddi_dma_attr_t *dma_attr, 670*9172SFei.Feng@Sun.COM size_t memsize, ddi_device_acc_attr_t *attr_p, uint_t alloc_flags, 671*9172SFei.Feng@Sun.COM uint_t bind_flags, struct dma_area *dma_p) 672*9172SFei.Feng@Sun.COM { 673*9172SFei.Feng@Sun.COM int err; 674*9172SFei.Feng@Sun.COM 675*9172SFei.Feng@Sun.COM /* 676*9172SFei.Feng@Sun.COM * Allocate handle 677*9172SFei.Feng@Sun.COM */ 678*9172SFei.Feng@Sun.COM err = ddi_dma_alloc_handle(devinfo, dma_attr, 679*9172SFei.Feng@Sun.COM DDI_DMA_SLEEP, NULL, &dma_p->dma_hdl); 680*9172SFei.Feng@Sun.COM if (err != DDI_SUCCESS) { 681*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_DMA, "rwn: rwn_allo_dma_mem(): " 682*9172SFei.Feng@Sun.COM "failed to alloc handle\n"); 683*9172SFei.Feng@Sun.COM goto fail1; 684*9172SFei.Feng@Sun.COM } 685*9172SFei.Feng@Sun.COM 686*9172SFei.Feng@Sun.COM /* 687*9172SFei.Feng@Sun.COM * Allocate memory 688*9172SFei.Feng@Sun.COM */ 689*9172SFei.Feng@Sun.COM err = ddi_dma_mem_alloc(dma_p->dma_hdl, memsize, attr_p, 690*9172SFei.Feng@Sun.COM alloc_flags, DDI_DMA_SLEEP, NULL, &dma_p->mem_va, 691*9172SFei.Feng@Sun.COM &dma_p->alength, &dma_p->acc_hdl); 692*9172SFei.Feng@Sun.COM if (err != DDI_SUCCESS) { 693*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_DMA, "rwn: rwn_alloc_dma_mem(): " 694*9172SFei.Feng@Sun.COM "failed to alloc mem\n"); 695*9172SFei.Feng@Sun.COM goto fail2; 696*9172SFei.Feng@Sun.COM } 697*9172SFei.Feng@Sun.COM 698*9172SFei.Feng@Sun.COM /* 699*9172SFei.Feng@Sun.COM * Bind the two together 700*9172SFei.Feng@Sun.COM */ 701*9172SFei.Feng@Sun.COM err = ddi_dma_addr_bind_handle(dma_p->dma_hdl, NULL, 702*9172SFei.Feng@Sun.COM dma_p->mem_va, dma_p->alength, bind_flags, 703*9172SFei.Feng@Sun.COM DDI_DMA_SLEEP, NULL, &dma_p->cookie, &dma_p->ncookies); 704*9172SFei.Feng@Sun.COM if (err != DDI_DMA_MAPPED) { 705*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_DMA, "rwn: rwn_alloc_dma_mem(): " 706*9172SFei.Feng@Sun.COM "failed to bind handle\n"); 707*9172SFei.Feng@Sun.COM goto fail3; 708*9172SFei.Feng@Sun.COM } 709*9172SFei.Feng@Sun.COM 710*9172SFei.Feng@Sun.COM if (dma_p->ncookies != 1) { 711*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_DMA, "rwn: rwn_alloc_dma_mem(): " 712*9172SFei.Feng@Sun.COM "failed to alloc cookies\n"); 713*9172SFei.Feng@Sun.COM goto fail4; 714*9172SFei.Feng@Sun.COM } 715*9172SFei.Feng@Sun.COM 716*9172SFei.Feng@Sun.COM dma_p->nslots = ~0U; 717*9172SFei.Feng@Sun.COM dma_p->size = ~0U; 718*9172SFei.Feng@Sun.COM dma_p->token = ~0U; 719*9172SFei.Feng@Sun.COM dma_p->offset = 0; 720*9172SFei.Feng@Sun.COM return (DDI_SUCCESS); 721*9172SFei.Feng@Sun.COM 722*9172SFei.Feng@Sun.COM fail4: 723*9172SFei.Feng@Sun.COM (void) ddi_dma_unbind_handle(dma_p->dma_hdl); 724*9172SFei.Feng@Sun.COM fail3: 725*9172SFei.Feng@Sun.COM ddi_dma_mem_free(&dma_p->acc_hdl); 726*9172SFei.Feng@Sun.COM fail2: 727*9172SFei.Feng@Sun.COM ddi_dma_free_handle(&dma_p->dma_hdl); 728*9172SFei.Feng@Sun.COM fail1: 729*9172SFei.Feng@Sun.COM return (err); 730*9172SFei.Feng@Sun.COM } 731*9172SFei.Feng@Sun.COM 732*9172SFei.Feng@Sun.COM static void 733*9172SFei.Feng@Sun.COM rt2860_free_dma_mem(struct dma_area *dma_p) 734*9172SFei.Feng@Sun.COM { 735*9172SFei.Feng@Sun.COM if (dma_p->dma_hdl != NULL) { 736*9172SFei.Feng@Sun.COM (void) ddi_dma_unbind_handle(dma_p->dma_hdl); 737*9172SFei.Feng@Sun.COM if (dma_p->acc_hdl != NULL) { 738*9172SFei.Feng@Sun.COM ddi_dma_mem_free(&dma_p->acc_hdl); 739*9172SFei.Feng@Sun.COM dma_p->acc_hdl = NULL; 740*9172SFei.Feng@Sun.COM } 741*9172SFei.Feng@Sun.COM ddi_dma_free_handle(&dma_p->dma_hdl); 742*9172SFei.Feng@Sun.COM dma_p->ncookies = 0; 743*9172SFei.Feng@Sun.COM dma_p->dma_hdl = NULL; 744*9172SFei.Feng@Sun.COM } 745*9172SFei.Feng@Sun.COM } 746*9172SFei.Feng@Sun.COM 747*9172SFei.Feng@Sun.COM /*ARGSUSED*/ 748*9172SFei.Feng@Sun.COM static int 749*9172SFei.Feng@Sun.COM rt2860_alloc_tx_ring(struct rt2860_softc *sc, struct rt2860_tx_ring *ring) 750*9172SFei.Feng@Sun.COM { 751*9172SFei.Feng@Sun.COM int size, err; 752*9172SFei.Feng@Sun.COM 753*9172SFei.Feng@Sun.COM size = RT2860_TX_RING_COUNT * sizeof (struct rt2860_txd); 754*9172SFei.Feng@Sun.COM 755*9172SFei.Feng@Sun.COM err = rt2860_alloc_dma_mem(sc->sc_dev, &rt2860_dma_attr, size, 756*9172SFei.Feng@Sun.COM &rt2860_desc_accattr, DDI_DMA_CONSISTENT, 757*9172SFei.Feng@Sun.COM DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 758*9172SFei.Feng@Sun.COM &ring->txdesc_dma); 759*9172SFei.Feng@Sun.COM if (err != DDI_SUCCESS) { 760*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_DMA, "rwn: rt2860_alloc_tx_ring(): " 761*9172SFei.Feng@Sun.COM "failed to alloc dma mem\n"); 762*9172SFei.Feng@Sun.COM goto fail1; 763*9172SFei.Feng@Sun.COM } 764*9172SFei.Feng@Sun.COM 765*9172SFei.Feng@Sun.COM ring->txd = (struct rt2860_txd *)ring->txdesc_dma.mem_va; 766*9172SFei.Feng@Sun.COM ring->paddr = ring->txdesc_dma.cookie.dmac_address; 767*9172SFei.Feng@Sun.COM 768*9172SFei.Feng@Sun.COM ring->cur = 0; 769*9172SFei.Feng@Sun.COM ring->next = 0; 770*9172SFei.Feng@Sun.COM ring->queued = 0; 771*9172SFei.Feng@Sun.COM 772*9172SFei.Feng@Sun.COM (void) bzero(ring->txd, size); 773*9172SFei.Feng@Sun.COM RT2860_DMA_SYNC(ring->txdesc_dma, DDI_DMA_SYNC_FORDEV); 774*9172SFei.Feng@Sun.COM return (DDI_SUCCESS); 775*9172SFei.Feng@Sun.COM fail1: 776*9172SFei.Feng@Sun.COM return (err); 777*9172SFei.Feng@Sun.COM } 778*9172SFei.Feng@Sun.COM 779*9172SFei.Feng@Sun.COM void 780*9172SFei.Feng@Sun.COM rt2860_reset_tx_ring(struct rt2860_softc *sc, struct rt2860_tx_ring *ring) 781*9172SFei.Feng@Sun.COM { 782*9172SFei.Feng@Sun.COM struct rt2860_tx_data *data; 783*9172SFei.Feng@Sun.COM int i; 784*9172SFei.Feng@Sun.COM 785*9172SFei.Feng@Sun.COM for (i = 0; i < RT2860_TX_RING_COUNT; i++) { 786*9172SFei.Feng@Sun.COM ring->txd[i].sdl0 &= ~LE_16(RT2860_TX_DDONE); 787*9172SFei.Feng@Sun.COM 788*9172SFei.Feng@Sun.COM if ((data = ring->data[i]) == NULL) 789*9172SFei.Feng@Sun.COM continue; /* nothing mapped in this slot */ 790*9172SFei.Feng@Sun.COM 791*9172SFei.Feng@Sun.COM /* by pass if it's quiesced */ 792*9172SFei.Feng@Sun.COM if (!(sc->sc_flags & RT2860_F_QUIESCE)) 793*9172SFei.Feng@Sun.COM RT2860_DMA_SYNC(data->txbuf_dma, DDI_DMA_SYNC_FORDEV); 794*9172SFei.Feng@Sun.COM 795*9172SFei.Feng@Sun.COM if (data->ni != NULL) { 796*9172SFei.Feng@Sun.COM ieee80211_free_node(data->ni); 797*9172SFei.Feng@Sun.COM data->ni = NULL; /* node already freed */ 798*9172SFei.Feng@Sun.COM } 799*9172SFei.Feng@Sun.COM 800*9172SFei.Feng@Sun.COM SLIST_INSERT_HEAD(&sc->data_pool, data, next); 801*9172SFei.Feng@Sun.COM ring->data[i] = NULL; 802*9172SFei.Feng@Sun.COM } 803*9172SFei.Feng@Sun.COM 804*9172SFei.Feng@Sun.COM /* by pass if it's quiesced */ 805*9172SFei.Feng@Sun.COM if (!(sc->sc_flags & RT2860_F_QUIESCE)) 806*9172SFei.Feng@Sun.COM RT2860_DMA_SYNC(ring->txdesc_dma, DDI_DMA_SYNC_FORDEV); 807*9172SFei.Feng@Sun.COM 808*9172SFei.Feng@Sun.COM ring->queued = 0; 809*9172SFei.Feng@Sun.COM ring->cur = ring->next = 0; 810*9172SFei.Feng@Sun.COM } 811*9172SFei.Feng@Sun.COM 812*9172SFei.Feng@Sun.COM /*ARGSUSED*/ 813*9172SFei.Feng@Sun.COM static void 814*9172SFei.Feng@Sun.COM rt2860_free_tx_ring(struct rt2860_softc *sc, struct rt2860_tx_ring *ring) 815*9172SFei.Feng@Sun.COM { 816*9172SFei.Feng@Sun.COM if (ring->txd != NULL) { 817*9172SFei.Feng@Sun.COM rt2860_free_dma_mem(&ring->txdesc_dma); 818*9172SFei.Feng@Sun.COM } 819*9172SFei.Feng@Sun.COM } 820*9172SFei.Feng@Sun.COM 821*9172SFei.Feng@Sun.COM static int 822*9172SFei.Feng@Sun.COM rt2860_alloc_rx_ring(struct rt2860_softc *sc, struct rt2860_rx_ring *ring) 823*9172SFei.Feng@Sun.COM { 824*9172SFei.Feng@Sun.COM struct rt2860_rx_data *data; 825*9172SFei.Feng@Sun.COM struct rt2860_rxd *rxd; 826*9172SFei.Feng@Sun.COM int i, err, size, datalen; 827*9172SFei.Feng@Sun.COM 828*9172SFei.Feng@Sun.COM size = RT2860_RX_RING_COUNT * sizeof (struct rt2860_rxd); 829*9172SFei.Feng@Sun.COM 830*9172SFei.Feng@Sun.COM err = rt2860_alloc_dma_mem(sc->sc_dev, &rt2860_dma_attr, size, 831*9172SFei.Feng@Sun.COM &rt2860_desc_accattr, DDI_DMA_CONSISTENT, 832*9172SFei.Feng@Sun.COM DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 833*9172SFei.Feng@Sun.COM &ring->rxdesc_dma); 834*9172SFei.Feng@Sun.COM if (err != DDI_SUCCESS) { 835*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_DMA, "rwn: rt2860_alloc_rx_ring(): " 836*9172SFei.Feng@Sun.COM "failed to alloc dma mem\n"); 837*9172SFei.Feng@Sun.COM goto fail1; 838*9172SFei.Feng@Sun.COM } 839*9172SFei.Feng@Sun.COM 840*9172SFei.Feng@Sun.COM ring->rxd = (struct rt2860_rxd *)ring->rxdesc_dma.mem_va; 841*9172SFei.Feng@Sun.COM ring->paddr = ring->rxdesc_dma.cookie.dmac_address; 842*9172SFei.Feng@Sun.COM bzero(ring->rxd, size); 843*9172SFei.Feng@Sun.COM 844*9172SFei.Feng@Sun.COM /* 845*9172SFei.Feng@Sun.COM * Pre-allocate Rx buffers and populate Rx ring. 846*9172SFei.Feng@Sun.COM */ 847*9172SFei.Feng@Sun.COM datalen = RT2860_RX_RING_COUNT * sizeof (struct rt2860_rx_data); 848*9172SFei.Feng@Sun.COM bzero(ring->data, datalen); 849*9172SFei.Feng@Sun.COM for (i = 0; i < RT2860_RX_RING_COUNT; i++) { 850*9172SFei.Feng@Sun.COM rxd = &ring->rxd[i]; 851*9172SFei.Feng@Sun.COM data = &ring->data[i]; 852*9172SFei.Feng@Sun.COM /* alloc DMA memory */ 853*9172SFei.Feng@Sun.COM (void) rt2860_alloc_dma_mem(sc->sc_dev, &rt2860_dma_attr, 854*9172SFei.Feng@Sun.COM sc->sc_dmabuf_size, 855*9172SFei.Feng@Sun.COM &rt2860_buf_accattr, 856*9172SFei.Feng@Sun.COM DDI_DMA_STREAMING, 857*9172SFei.Feng@Sun.COM DDI_DMA_READ | DDI_DMA_STREAMING, 858*9172SFei.Feng@Sun.COM &data->rxbuf_dma); 859*9172SFei.Feng@Sun.COM rxd->sdp0 = LE_32(data->rxbuf_dma.cookie.dmac_address); 860*9172SFei.Feng@Sun.COM rxd->sdl0 = LE_16(sc->sc_dmabuf_size); 861*9172SFei.Feng@Sun.COM } 862*9172SFei.Feng@Sun.COM 863*9172SFei.Feng@Sun.COM ring->cur = 0; 864*9172SFei.Feng@Sun.COM 865*9172SFei.Feng@Sun.COM RT2860_DMA_SYNC(ring->rxdesc_dma, DDI_DMA_SYNC_FORDEV); 866*9172SFei.Feng@Sun.COM return (DDI_SUCCESS); 867*9172SFei.Feng@Sun.COM fail2: 868*9172SFei.Feng@Sun.COM rt2860_free_dma_mem(&ring->rxdesc_dma); 869*9172SFei.Feng@Sun.COM fail1: 870*9172SFei.Feng@Sun.COM return (err); 871*9172SFei.Feng@Sun.COM } 872*9172SFei.Feng@Sun.COM 873*9172SFei.Feng@Sun.COM /*ARGSUSED*/ 874*9172SFei.Feng@Sun.COM void 875*9172SFei.Feng@Sun.COM rt2860_reset_rx_ring(struct rt2860_softc *sc, struct rt2860_rx_ring *ring) 876*9172SFei.Feng@Sun.COM { 877*9172SFei.Feng@Sun.COM int i; 878*9172SFei.Feng@Sun.COM 879*9172SFei.Feng@Sun.COM for (i = 0; i < RT2860_RX_RING_COUNT; i++) 880*9172SFei.Feng@Sun.COM ring->rxd[i].sdl0 &= ~LE_16(RT2860_RX_DDONE); 881*9172SFei.Feng@Sun.COM 882*9172SFei.Feng@Sun.COM RT2860_DMA_SYNC(ring->rxdesc_dma, DDI_DMA_SYNC_FORDEV); 883*9172SFei.Feng@Sun.COM 884*9172SFei.Feng@Sun.COM ring->cur = 0; 885*9172SFei.Feng@Sun.COM } 886*9172SFei.Feng@Sun.COM 887*9172SFei.Feng@Sun.COM /*ARGSUSED*/ 888*9172SFei.Feng@Sun.COM static void 889*9172SFei.Feng@Sun.COM rt2860_free_rx_ring(struct rt2860_softc *sc, struct rt2860_rx_ring *ring) 890*9172SFei.Feng@Sun.COM { 891*9172SFei.Feng@Sun.COM struct rt2860_rx_data *data; 892*9172SFei.Feng@Sun.COM int i, count; 893*9172SFei.Feng@Sun.COM 894*9172SFei.Feng@Sun.COM if (ring->rxd != NULL) 895*9172SFei.Feng@Sun.COM rt2860_free_dma_mem(&ring->rxdesc_dma); 896*9172SFei.Feng@Sun.COM 897*9172SFei.Feng@Sun.COM count = RT2860_RX_RING_COUNT; 898*9172SFei.Feng@Sun.COM if (ring->data != NULL) { 899*9172SFei.Feng@Sun.COM for (i = 0; i < count; i++) { 900*9172SFei.Feng@Sun.COM data = &ring->data[i]; 901*9172SFei.Feng@Sun.COM rt2860_free_dma_mem(&data->rxbuf_dma); 902*9172SFei.Feng@Sun.COM } 903*9172SFei.Feng@Sun.COM } 904*9172SFei.Feng@Sun.COM } 905*9172SFei.Feng@Sun.COM 906*9172SFei.Feng@Sun.COM static int 907*9172SFei.Feng@Sun.COM rt2860_alloc_tx_pool(struct rt2860_softc *sc) 908*9172SFei.Feng@Sun.COM { 909*9172SFei.Feng@Sun.COM struct rt2860_tx_data *data; 910*9172SFei.Feng@Sun.COM int i, err, size; 911*9172SFei.Feng@Sun.COM 912*9172SFei.Feng@Sun.COM size = RT2860_TX_POOL_COUNT * sizeof (struct rt2860_txwi); 913*9172SFei.Feng@Sun.COM 914*9172SFei.Feng@Sun.COM /* init data_pool early in case of failure.. */ 915*9172SFei.Feng@Sun.COM SLIST_INIT(&sc->data_pool); 916*9172SFei.Feng@Sun.COM 917*9172SFei.Feng@Sun.COM err = rt2860_alloc_dma_mem(sc->sc_dev, &rt2860_dma_attr, size, 918*9172SFei.Feng@Sun.COM &rt2860_desc_accattr, DDI_DMA_CONSISTENT, 919*9172SFei.Feng@Sun.COM DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 920*9172SFei.Feng@Sun.COM &sc->txpool_dma); 921*9172SFei.Feng@Sun.COM if (err != DDI_SUCCESS) { 922*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_DMA, "rwn: rt2860_alloc_tx_pool(): " 923*9172SFei.Feng@Sun.COM "failed to alloc dma mem\n"); 924*9172SFei.Feng@Sun.COM goto fail1; 925*9172SFei.Feng@Sun.COM } 926*9172SFei.Feng@Sun.COM 927*9172SFei.Feng@Sun.COM sc->txwi = (struct rt2860_txwi *)sc->txpool_dma.mem_va; 928*9172SFei.Feng@Sun.COM (void) bzero(sc->txwi, size); 929*9172SFei.Feng@Sun.COM RT2860_DMA_SYNC(sc->txpool_dma, DDI_DMA_SYNC_FORDEV); 930*9172SFei.Feng@Sun.COM 931*9172SFei.Feng@Sun.COM for (i = 0; i < RT2860_TX_POOL_COUNT; i++) { 932*9172SFei.Feng@Sun.COM data = &sc->data[i]; 933*9172SFei.Feng@Sun.COM 934*9172SFei.Feng@Sun.COM err = rt2860_alloc_dma_mem(sc->sc_dev, &rt2860_dma_attr, 935*9172SFei.Feng@Sun.COM sc->sc_dmabuf_size, 936*9172SFei.Feng@Sun.COM &rt2860_buf_accattr, DDI_DMA_CONSISTENT, 937*9172SFei.Feng@Sun.COM DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 938*9172SFei.Feng@Sun.COM &data->txbuf_dma); 939*9172SFei.Feng@Sun.COM if (err != DDI_SUCCESS) { 940*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_DMA, 941*9172SFei.Feng@Sun.COM "rwn: rt2860_alloc_tx_pool(): " 942*9172SFei.Feng@Sun.COM "failed to alloc dma mem\n"); 943*9172SFei.Feng@Sun.COM goto fail2; 944*9172SFei.Feng@Sun.COM } 945*9172SFei.Feng@Sun.COM data->txwi = &sc->txwi[i]; 946*9172SFei.Feng@Sun.COM data->paddr = sc->txpool_dma.cookie.dmac_address + 947*9172SFei.Feng@Sun.COM i * sizeof (struct rt2860_txwi); 948*9172SFei.Feng@Sun.COM 949*9172SFei.Feng@Sun.COM SLIST_INSERT_HEAD(&sc->data_pool, data, next); 950*9172SFei.Feng@Sun.COM } 951*9172SFei.Feng@Sun.COM return (DDI_SUCCESS); 952*9172SFei.Feng@Sun.COM fail2: 953*9172SFei.Feng@Sun.COM rt2860_free_dma_mem(&sc->txpool_dma); 954*9172SFei.Feng@Sun.COM fail1: 955*9172SFei.Feng@Sun.COM return (err); 956*9172SFei.Feng@Sun.COM } 957*9172SFei.Feng@Sun.COM 958*9172SFei.Feng@Sun.COM static void 959*9172SFei.Feng@Sun.COM rt2860_free_tx_pool(struct rt2860_softc *sc) 960*9172SFei.Feng@Sun.COM { 961*9172SFei.Feng@Sun.COM struct rt2860_tx_data *data; 962*9172SFei.Feng@Sun.COM int i; 963*9172SFei.Feng@Sun.COM 964*9172SFei.Feng@Sun.COM if (sc->txwi != NULL) { 965*9172SFei.Feng@Sun.COM rt2860_free_dma_mem(&sc->txpool_dma); 966*9172SFei.Feng@Sun.COM } 967*9172SFei.Feng@Sun.COM 968*9172SFei.Feng@Sun.COM for (i = 0; i < RT2860_TX_POOL_COUNT; i++) { 969*9172SFei.Feng@Sun.COM data = &sc->data[i]; 970*9172SFei.Feng@Sun.COM rt2860_free_dma_mem(&data->txbuf_dma); 971*9172SFei.Feng@Sun.COM } 972*9172SFei.Feng@Sun.COM } 973*9172SFei.Feng@Sun.COM 974*9172SFei.Feng@Sun.COM /* quickly determine if a given rate is CCK or OFDM */ 975*9172SFei.Feng@Sun.COM #define RT2860_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22) 976*9172SFei.Feng@Sun.COM 977*9172SFei.Feng@Sun.COM #define RT2860_ACK_SIZE 14 /* 10 + 4(FCS) */ 978*9172SFei.Feng@Sun.COM #define RT2860_SIFS_TIME 10 979*9172SFei.Feng@Sun.COM 980*9172SFei.Feng@Sun.COM static uint8_t 981*9172SFei.Feng@Sun.COM rt2860_rate2mcs(uint8_t rate) 982*9172SFei.Feng@Sun.COM { 983*9172SFei.Feng@Sun.COM switch (rate) { 984*9172SFei.Feng@Sun.COM /* CCK rates */ 985*9172SFei.Feng@Sun.COM case 2: 986*9172SFei.Feng@Sun.COM return (0); 987*9172SFei.Feng@Sun.COM case 4: 988*9172SFei.Feng@Sun.COM return (1); 989*9172SFei.Feng@Sun.COM case 11: 990*9172SFei.Feng@Sun.COM return (2); 991*9172SFei.Feng@Sun.COM case 22: 992*9172SFei.Feng@Sun.COM return (3); 993*9172SFei.Feng@Sun.COM /* OFDM rates */ 994*9172SFei.Feng@Sun.COM case 12: 995*9172SFei.Feng@Sun.COM return (0); 996*9172SFei.Feng@Sun.COM case 18: 997*9172SFei.Feng@Sun.COM return (1); 998*9172SFei.Feng@Sun.COM case 24: 999*9172SFei.Feng@Sun.COM return (2); 1000*9172SFei.Feng@Sun.COM case 36: 1001*9172SFei.Feng@Sun.COM return (3); 1002*9172SFei.Feng@Sun.COM case 48: 1003*9172SFei.Feng@Sun.COM return (4); 1004*9172SFei.Feng@Sun.COM case 72: 1005*9172SFei.Feng@Sun.COM return (5); 1006*9172SFei.Feng@Sun.COM case 96: 1007*9172SFei.Feng@Sun.COM return (6); 1008*9172SFei.Feng@Sun.COM case 108: 1009*9172SFei.Feng@Sun.COM return (7); 1010*9172SFei.Feng@Sun.COM } 1011*9172SFei.Feng@Sun.COM 1012*9172SFei.Feng@Sun.COM return (0); /* shouldn't get there */ 1013*9172SFei.Feng@Sun.COM } 1014*9172SFei.Feng@Sun.COM 1015*9172SFei.Feng@Sun.COM /* 1016*9172SFei.Feng@Sun.COM * Return the expected ack rate for a frame transmitted at rate `rate'. 1017*9172SFei.Feng@Sun.COM */ 1018*9172SFei.Feng@Sun.COM static int 1019*9172SFei.Feng@Sun.COM rt2860_ack_rate(struct ieee80211com *ic, int rate) 1020*9172SFei.Feng@Sun.COM { 1021*9172SFei.Feng@Sun.COM switch (rate) { 1022*9172SFei.Feng@Sun.COM /* CCK rates */ 1023*9172SFei.Feng@Sun.COM case 2: 1024*9172SFei.Feng@Sun.COM return (2); 1025*9172SFei.Feng@Sun.COM case 4: 1026*9172SFei.Feng@Sun.COM case 11: 1027*9172SFei.Feng@Sun.COM case 22: 1028*9172SFei.Feng@Sun.COM return ((ic->ic_curmode == IEEE80211_MODE_11B) ? 4 : rate); 1029*9172SFei.Feng@Sun.COM 1030*9172SFei.Feng@Sun.COM /* OFDM rates */ 1031*9172SFei.Feng@Sun.COM case 12: 1032*9172SFei.Feng@Sun.COM case 18: 1033*9172SFei.Feng@Sun.COM return (12); 1034*9172SFei.Feng@Sun.COM case 24: 1035*9172SFei.Feng@Sun.COM case 36: 1036*9172SFei.Feng@Sun.COM return (24); 1037*9172SFei.Feng@Sun.COM case 48: 1038*9172SFei.Feng@Sun.COM case 72: 1039*9172SFei.Feng@Sun.COM case 96: 1040*9172SFei.Feng@Sun.COM case 108: 1041*9172SFei.Feng@Sun.COM return (48); 1042*9172SFei.Feng@Sun.COM } 1043*9172SFei.Feng@Sun.COM 1044*9172SFei.Feng@Sun.COM /* default to 1Mbps */ 1045*9172SFei.Feng@Sun.COM return (2); 1046*9172SFei.Feng@Sun.COM } 1047*9172SFei.Feng@Sun.COM 1048*9172SFei.Feng@Sun.COM 1049*9172SFei.Feng@Sun.COM /* 1050*9172SFei.Feng@Sun.COM * Compute the duration (in us) needed to transmit `len' bytes at rate `rate'. 1051*9172SFei.Feng@Sun.COM * The function automatically determines the operating mode depending on the 1052*9172SFei.Feng@Sun.COM * given rate. `flags' indicates whether short preamble is in use or not. 1053*9172SFei.Feng@Sun.COM */ 1054*9172SFei.Feng@Sun.COM static uint16_t 1055*9172SFei.Feng@Sun.COM rt2860_txtime(int len, int rate, uint32_t flags) 1056*9172SFei.Feng@Sun.COM { 1057*9172SFei.Feng@Sun.COM uint16_t txtime; 1058*9172SFei.Feng@Sun.COM 1059*9172SFei.Feng@Sun.COM if (RT2860_RATE_IS_OFDM(rate)) { 1060*9172SFei.Feng@Sun.COM /* IEEE Std 802.11g-2003, pp. 44 */ 1061*9172SFei.Feng@Sun.COM txtime = (8 + 4 * len + 3 + rate - 1) / rate; 1062*9172SFei.Feng@Sun.COM txtime = 16 + 4 + 4 * txtime + 6; 1063*9172SFei.Feng@Sun.COM } else { 1064*9172SFei.Feng@Sun.COM /* IEEE Std 802.11b-1999, pp. 28 */ 1065*9172SFei.Feng@Sun.COM txtime = (16 * len + rate - 1) / rate; 1066*9172SFei.Feng@Sun.COM if (rate != 2 && (flags & IEEE80211_F_SHPREAMBLE)) 1067*9172SFei.Feng@Sun.COM txtime += 72 + 24; 1068*9172SFei.Feng@Sun.COM else 1069*9172SFei.Feng@Sun.COM txtime += 144 + 48; 1070*9172SFei.Feng@Sun.COM } 1071*9172SFei.Feng@Sun.COM return (txtime); 1072*9172SFei.Feng@Sun.COM } 1073*9172SFei.Feng@Sun.COM 1074*9172SFei.Feng@Sun.COM static int 1075*9172SFei.Feng@Sun.COM rt2860_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type) 1076*9172SFei.Feng@Sun.COM { 1077*9172SFei.Feng@Sun.COM struct rt2860_softc *sc = (struct rt2860_softc *)ic; 1078*9172SFei.Feng@Sun.COM struct rt2860_tx_ring *ring; 1079*9172SFei.Feng@Sun.COM struct rt2860_tx_data *data; 1080*9172SFei.Feng@Sun.COM struct rt2860_txd *txd; 1081*9172SFei.Feng@Sun.COM struct rt2860_txwi *txwi; 1082*9172SFei.Feng@Sun.COM struct ieee80211_frame *wh; 1083*9172SFei.Feng@Sun.COM struct ieee80211_node *ni; 1084*9172SFei.Feng@Sun.COM int qid, off, rate, err; 1085*9172SFei.Feng@Sun.COM int mblen, pktlen; 1086*9172SFei.Feng@Sun.COM uint_t hdrlen; 1087*9172SFei.Feng@Sun.COM uint8_t mcs, pid, qsel; 1088*9172SFei.Feng@Sun.COM uint16_t dur; 1089*9172SFei.Feng@Sun.COM mblk_t *m, *m0; 1090*9172SFei.Feng@Sun.COM 1091*9172SFei.Feng@Sun.COM err = DDI_SUCCESS; 1092*9172SFei.Feng@Sun.COM 1093*9172SFei.Feng@Sun.COM mutex_enter(&sc->sc_txlock); 1094*9172SFei.Feng@Sun.COM if (RT2860_IS_SUSPEND(sc)) { 1095*9172SFei.Feng@Sun.COM err = ENXIO; 1096*9172SFei.Feng@Sun.COM goto fail1; 1097*9172SFei.Feng@Sun.COM } 1098*9172SFei.Feng@Sun.COM 1099*9172SFei.Feng@Sun.COM if ((type & IEEE80211_FC0_TYPE_MASK) != 1100*9172SFei.Feng@Sun.COM IEEE80211_FC0_TYPE_DATA) 1101*9172SFei.Feng@Sun.COM qid = sc->mgtqid; 1102*9172SFei.Feng@Sun.COM else 1103*9172SFei.Feng@Sun.COM qid = EDCA_AC_BE; 1104*9172SFei.Feng@Sun.COM ring = &sc->txq[qid]; 1105*9172SFei.Feng@Sun.COM 1106*9172SFei.Feng@Sun.COM if (SLIST_EMPTY(&sc->data_pool) || (ring->queued > 15)) { 1107*9172SFei.Feng@Sun.COM sc->sc_need_sched = 1; 1108*9172SFei.Feng@Sun.COM sc->sc_tx_nobuf++; 1109*9172SFei.Feng@Sun.COM err = ENOMEM; 1110*9172SFei.Feng@Sun.COM goto fail1; 1111*9172SFei.Feng@Sun.COM } 1112*9172SFei.Feng@Sun.COM 1113*9172SFei.Feng@Sun.COM /* the data pool contains at least one element, pick the first */ 1114*9172SFei.Feng@Sun.COM data = SLIST_FIRST(&sc->data_pool); 1115*9172SFei.Feng@Sun.COM 1116*9172SFei.Feng@Sun.COM m = allocb(msgdsize(mp) + 32, BPRI_MED); 1117*9172SFei.Feng@Sun.COM if (m == NULL) { 1118*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_TX, "rwn: rt2860_send():" 1119*9172SFei.Feng@Sun.COM "rt2560_mgmt_send: can't alloc mblk.\n"); 1120*9172SFei.Feng@Sun.COM err = DDI_FAILURE; 1121*9172SFei.Feng@Sun.COM goto fail1; 1122*9172SFei.Feng@Sun.COM } 1123*9172SFei.Feng@Sun.COM 1124*9172SFei.Feng@Sun.COM for (off = 0, m0 = mp; m0 != NULL; m0 = m0->b_cont) { 1125*9172SFei.Feng@Sun.COM mblen = MBLKL(m0); 1126*9172SFei.Feng@Sun.COM (void) bcopy(m0->b_rptr, m->b_rptr + off, mblen); 1127*9172SFei.Feng@Sun.COM off += mblen; 1128*9172SFei.Feng@Sun.COM } 1129*9172SFei.Feng@Sun.COM m->b_wptr += off; 1130*9172SFei.Feng@Sun.COM 1131*9172SFei.Feng@Sun.COM wh = (struct ieee80211_frame *)m->b_rptr; 1132*9172SFei.Feng@Sun.COM ni = ieee80211_find_txnode(ic, wh->i_addr1); 1133*9172SFei.Feng@Sun.COM if (ni == NULL) { 1134*9172SFei.Feng@Sun.COM err = DDI_FAILURE; 1135*9172SFei.Feng@Sun.COM sc->sc_tx_err++; 1136*9172SFei.Feng@Sun.COM goto fail2; 1137*9172SFei.Feng@Sun.COM } 1138*9172SFei.Feng@Sun.COM 1139*9172SFei.Feng@Sun.COM if ((type & IEEE80211_FC0_TYPE_MASK) == 1140*9172SFei.Feng@Sun.COM IEEE80211_FC0_TYPE_DATA) 1141*9172SFei.Feng@Sun.COM (void) ieee80211_encap(ic, m, ni); 1142*9172SFei.Feng@Sun.COM 1143*9172SFei.Feng@Sun.COM if (wh->i_fc[1] & IEEE80211_FC1_WEP) { 1144*9172SFei.Feng@Sun.COM struct ieee80211_key *k; 1145*9172SFei.Feng@Sun.COM k = ieee80211_crypto_encap(ic, m); 1146*9172SFei.Feng@Sun.COM if (k == NULL) { 1147*9172SFei.Feng@Sun.COM sc->sc_tx_err++; 1148*9172SFei.Feng@Sun.COM err = DDI_FAILURE; 1149*9172SFei.Feng@Sun.COM goto fail3; 1150*9172SFei.Feng@Sun.COM } 1151*9172SFei.Feng@Sun.COM /* packet header may have moved, reset our local pointer */ 1152*9172SFei.Feng@Sun.COM wh = (struct ieee80211_frame *)m->b_rptr; 1153*9172SFei.Feng@Sun.COM } 1154*9172SFei.Feng@Sun.COM pktlen = msgdsize(m); 1155*9172SFei.Feng@Sun.COM hdrlen = sizeof (*wh); 1156*9172SFei.Feng@Sun.COM 1157*9172SFei.Feng@Sun.COM /* pickup a rate */ 1158*9172SFei.Feng@Sun.COM if (IEEE80211_IS_MULTICAST(wh->i_addr1) || 1159*9172SFei.Feng@Sun.COM ((type & IEEE80211_FC0_TYPE_MASK) != 1160*9172SFei.Feng@Sun.COM IEEE80211_FC0_TYPE_DATA)) 1161*9172SFei.Feng@Sun.COM rate = ni->in_rates.ir_rates[0]; 1162*9172SFei.Feng@Sun.COM else { 1163*9172SFei.Feng@Sun.COM if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) 1164*9172SFei.Feng@Sun.COM rate = ic->ic_fixed_rate; 1165*9172SFei.Feng@Sun.COM else 1166*9172SFei.Feng@Sun.COM rate = ni->in_rates.ir_rates[ni->in_txrate]; 1167*9172SFei.Feng@Sun.COM } 1168*9172SFei.Feng@Sun.COM rate &= IEEE80211_RATE_VAL; 1169*9172SFei.Feng@Sun.COM 1170*9172SFei.Feng@Sun.COM /* get MCS code from rate */ 1171*9172SFei.Feng@Sun.COM mcs = rt2860_rate2mcs(rate); 1172*9172SFei.Feng@Sun.COM 1173*9172SFei.Feng@Sun.COM /* setup TX Wireless Information */ 1174*9172SFei.Feng@Sun.COM txwi = data->txwi; 1175*9172SFei.Feng@Sun.COM (void) bzero(txwi, sizeof (struct rt2860_txwi)); 1176*9172SFei.Feng@Sun.COM txwi->wcid = (type == IEEE80211_FC0_TYPE_DATA) ? 1177*9172SFei.Feng@Sun.COM RT2860_AID2WCID(ni->in_associd) : 0xff; 1178*9172SFei.Feng@Sun.COM txwi->len = LE_16(pktlen); 1179*9172SFei.Feng@Sun.COM if (!RT2860_RATE_IS_OFDM(rate)) { 1180*9172SFei.Feng@Sun.COM txwi->phy = LE_16(RT2860_PHY_CCK); 1181*9172SFei.Feng@Sun.COM if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) 1182*9172SFei.Feng@Sun.COM mcs |= RT2860_PHY_SHPRE; 1183*9172SFei.Feng@Sun.COM } else 1184*9172SFei.Feng@Sun.COM txwi->phy = LE_16(RT2860_PHY_OFDM); 1185*9172SFei.Feng@Sun.COM txwi->phy |= LE_16(mcs); 1186*9172SFei.Feng@Sun.COM 1187*9172SFei.Feng@Sun.COM /* 1188*9172SFei.Feng@Sun.COM * We store the MCS code into the driver-private PacketID field. 1189*9172SFei.Feng@Sun.COM * The PacketID is latched into TX_STAT_FIFO when Tx completes so 1190*9172SFei.Feng@Sun.COM * that we know at which initial rate the frame was transmitted. 1191*9172SFei.Feng@Sun.COM * We add 1 to the MCS code because setting the PacketID field to 1192*9172SFei.Feng@Sun.COM * 0 means that we don't want feedback in TX_STAT_FIFO. 1193*9172SFei.Feng@Sun.COM */ 1194*9172SFei.Feng@Sun.COM pid = (mcs + 1) & 0xf; 1195*9172SFei.Feng@Sun.COM txwi->len |= LE_16(pid << RT2860_TX_PID_SHIFT); 1196*9172SFei.Feng@Sun.COM 1197*9172SFei.Feng@Sun.COM /* check if RTS/CTS or CTS-to-self protection is required */ 1198*9172SFei.Feng@Sun.COM if (!IEEE80211_IS_MULTICAST(wh->i_addr1) && 1199*9172SFei.Feng@Sun.COM (pktlen + IEEE80211_CRC_LEN > ic->ic_rtsthreshold || 1200*9172SFei.Feng@Sun.COM ((ic->ic_flags & 1201*9172SFei.Feng@Sun.COM IEEE80211_F_USEPROT) && RT2860_RATE_IS_OFDM(rate)))) 1202*9172SFei.Feng@Sun.COM txwi->txop = RT2860_TX_TXOP_HT; 1203*9172SFei.Feng@Sun.COM else 1204*9172SFei.Feng@Sun.COM txwi->txop = RT2860_TX_TXOP_BACKOFF; 1205*9172SFei.Feng@Sun.COM 1206*9172SFei.Feng@Sun.COM if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { 1207*9172SFei.Feng@Sun.COM txwi->xflags |= RT2860_TX_ACK; 1208*9172SFei.Feng@Sun.COM 1209*9172SFei.Feng@Sun.COM dur = rt2860_txtime(RT2860_ACK_SIZE, rt2860_ack_rate(ic, rate), 1210*9172SFei.Feng@Sun.COM ic->ic_flags) + sc->sifs; 1211*9172SFei.Feng@Sun.COM *(uint16_t *)wh->i_dur = LE_16(dur); 1212*9172SFei.Feng@Sun.COM } 1213*9172SFei.Feng@Sun.COM 1214*9172SFei.Feng@Sun.COM /* copy and trim 802.11 header */ 1215*9172SFei.Feng@Sun.COM bcopy(wh, &txwi->wh, hdrlen); 1216*9172SFei.Feng@Sun.COM m->b_rptr += hdrlen; 1217*9172SFei.Feng@Sun.COM bcopy(m->b_rptr, data->txbuf_dma.mem_va, pktlen - hdrlen); 1218*9172SFei.Feng@Sun.COM 1219*9172SFei.Feng@Sun.COM qsel = (qid < EDCA_NUM_AC) ? RT2860_TX_QSEL_EDCA : RT2860_TX_QSEL_MGMT; 1220*9172SFei.Feng@Sun.COM 1221*9172SFei.Feng@Sun.COM /* first segment is TXWI + 802.11 header */ 1222*9172SFei.Feng@Sun.COM txd = &ring->txd[ring->cur]; 1223*9172SFei.Feng@Sun.COM txd->sdp0 = LE_32(data->paddr); 1224*9172SFei.Feng@Sun.COM txd->sdl0 = LE_16(16 + hdrlen); 1225*9172SFei.Feng@Sun.COM txd->flags = qsel; 1226*9172SFei.Feng@Sun.COM 1227*9172SFei.Feng@Sun.COM /* finalize last segment */ 1228*9172SFei.Feng@Sun.COM txd->sdp1 = LE_32(data->txbuf_dma.cookie.dmac_address); 1229*9172SFei.Feng@Sun.COM txd->sdl1 = LE_16(pktlen - hdrlen | RT2860_TX_LS1); 1230*9172SFei.Feng@Sun.COM 1231*9172SFei.Feng@Sun.COM /* remove from the free pool and link it into the SW Tx slot */ 1232*9172SFei.Feng@Sun.COM SLIST_REMOVE_HEAD(&sc->data_pool, next); 1233*9172SFei.Feng@Sun.COM data->ni = ieee80211_ref_node(ni); 1234*9172SFei.Feng@Sun.COM ring->data[ring->cur] = data; 1235*9172SFei.Feng@Sun.COM 1236*9172SFei.Feng@Sun.COM (void) ddi_dma_sync(sc->txpool_dma.dma_hdl, 1237*9172SFei.Feng@Sun.COM _PTRDIFF(txwi, sc->txwi), 1238*9172SFei.Feng@Sun.COM (hdrlen + 16 + 2), 1239*9172SFei.Feng@Sun.COM DDI_DMA_SYNC_FORDEV); 1240*9172SFei.Feng@Sun.COM RT2860_DMA_SYNC(data->txbuf_dma, DDI_DMA_SYNC_FORDEV); 1241*9172SFei.Feng@Sun.COM RT2860_DMA_SYNC(ring->txdesc_dma, DDI_DMA_SYNC_FORDEV); 1242*9172SFei.Feng@Sun.COM 1243*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_TX, "rwn: rt2860_send():" 1244*9172SFei.Feng@Sun.COM "sending frame qid=%d wcid=%d rate=%d cur = %x\n", 1245*9172SFei.Feng@Sun.COM qid, txwi->wcid, rate, ring->cur); 1246*9172SFei.Feng@Sun.COM 1247*9172SFei.Feng@Sun.COM ring->queued++; 1248*9172SFei.Feng@Sun.COM ring->cur = (ring->cur + 1) % RT2860_TX_RING_COUNT; 1249*9172SFei.Feng@Sun.COM 1250*9172SFei.Feng@Sun.COM /* kick Tx */ 1251*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_TX_CTX_IDX(qid), ring->cur); 1252*9172SFei.Feng@Sun.COM 1253*9172SFei.Feng@Sun.COM sc->sc_tx_timer = 5; 1254*9172SFei.Feng@Sun.COM 1255*9172SFei.Feng@Sun.COM ic->ic_stats.is_tx_frags++; 1256*9172SFei.Feng@Sun.COM ic->ic_stats.is_tx_bytes += pktlen; 1257*9172SFei.Feng@Sun.COM 1258*9172SFei.Feng@Sun.COM fail3: 1259*9172SFei.Feng@Sun.COM ieee80211_free_node(ni); 1260*9172SFei.Feng@Sun.COM fail2: 1261*9172SFei.Feng@Sun.COM freemsg(m); 1262*9172SFei.Feng@Sun.COM fail1: 1263*9172SFei.Feng@Sun.COM if ((type & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_DATA || 1264*9172SFei.Feng@Sun.COM err == DDI_SUCCESS) 1265*9172SFei.Feng@Sun.COM freemsg(mp); 1266*9172SFei.Feng@Sun.COM mutex_exit(&sc->sc_txlock); 1267*9172SFei.Feng@Sun.COM return (err); 1268*9172SFei.Feng@Sun.COM } 1269*9172SFei.Feng@Sun.COM 1270*9172SFei.Feng@Sun.COM /* 1271*9172SFei.Feng@Sun.COM * This function is called periodically (every 200ms) during scanning to 1272*9172SFei.Feng@Sun.COM * switch from one channel to another. 1273*9172SFei.Feng@Sun.COM */ 1274*9172SFei.Feng@Sun.COM static void 1275*9172SFei.Feng@Sun.COM rt2860_next_scan(void *arg) 1276*9172SFei.Feng@Sun.COM { 1277*9172SFei.Feng@Sun.COM struct rt2860_softc *sc = (struct rt2860_softc *)arg; 1278*9172SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic; 1279*9172SFei.Feng@Sun.COM 1280*9172SFei.Feng@Sun.COM if (ic->ic_state == IEEE80211_S_SCAN) 1281*9172SFei.Feng@Sun.COM (void) ieee80211_next_scan(ic); 1282*9172SFei.Feng@Sun.COM } 1283*9172SFei.Feng@Sun.COM 1284*9172SFei.Feng@Sun.COM static void 1285*9172SFei.Feng@Sun.COM rt2860_updateslot(struct rt2860_softc *sc) 1286*9172SFei.Feng@Sun.COM { 1287*9172SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic; 1288*9172SFei.Feng@Sun.COM uint32_t tmp; 1289*9172SFei.Feng@Sun.COM 1290*9172SFei.Feng@Sun.COM tmp = RT2860_READ(sc, RT2860_BKOFF_SLOT_CFG); 1291*9172SFei.Feng@Sun.COM tmp &= ~0xff; 1292*9172SFei.Feng@Sun.COM tmp |= (ic->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20; 1293*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_BKOFF_SLOT_CFG, tmp); 1294*9172SFei.Feng@Sun.COM } 1295*9172SFei.Feng@Sun.COM 1296*9172SFei.Feng@Sun.COM static void 1297*9172SFei.Feng@Sun.COM rt2860_iter_func(void *arg, struct ieee80211_node *ni) 1298*9172SFei.Feng@Sun.COM { 1299*9172SFei.Feng@Sun.COM struct rt2860_softc *sc = (struct rt2860_softc *)arg; 1300*9172SFei.Feng@Sun.COM uint8_t wcid; 1301*9172SFei.Feng@Sun.COM 1302*9172SFei.Feng@Sun.COM wcid = RT2860_AID2WCID(ni->in_associd); 1303*9172SFei.Feng@Sun.COM rt2860_amrr_choose(&sc->amrr, ni, &sc->amn[wcid]); 1304*9172SFei.Feng@Sun.COM } 1305*9172SFei.Feng@Sun.COM 1306*9172SFei.Feng@Sun.COM static void 1307*9172SFei.Feng@Sun.COM rt2860_updatestats(void *arg) 1308*9172SFei.Feng@Sun.COM { 1309*9172SFei.Feng@Sun.COM struct rt2860_softc *sc = (struct rt2860_softc *)arg; 1310*9172SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic; 1311*9172SFei.Feng@Sun.COM 1312*9172SFei.Feng@Sun.COM if (ic->ic_opmode == IEEE80211_M_STA) 1313*9172SFei.Feng@Sun.COM rt2860_iter_func(sc, ic->ic_bss); 1314*9172SFei.Feng@Sun.COM else 1315*9172SFei.Feng@Sun.COM ieee80211_iterate_nodes(&ic->ic_sta, rt2860_iter_func, arg); 1316*9172SFei.Feng@Sun.COM 1317*9172SFei.Feng@Sun.COM sc->sc_rssadapt_id = timeout(rt2860_updatestats, (void *)sc, 1318*9172SFei.Feng@Sun.COM drv_usectohz(500 * 1000)); 1319*9172SFei.Feng@Sun.COM } 1320*9172SFei.Feng@Sun.COM 1321*9172SFei.Feng@Sun.COM static void 1322*9172SFei.Feng@Sun.COM rt2860_enable_mrr(struct rt2860_softc *sc) 1323*9172SFei.Feng@Sun.COM { 1324*9172SFei.Feng@Sun.COM #define CCK(mcs) (mcs) 1325*9172SFei.Feng@Sun.COM #define OFDM(mcs) ((uint32_t)1 << 3 | (mcs)) 1326*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_LG_FBK_CFG0, 1327*9172SFei.Feng@Sun.COM OFDM(6) << 28 | /* 54->48 */ 1328*9172SFei.Feng@Sun.COM OFDM(5) << 24 | /* 48->36 */ 1329*9172SFei.Feng@Sun.COM OFDM(4) << 20 | /* 36->24 */ 1330*9172SFei.Feng@Sun.COM OFDM(3) << 16 | /* 24->18 */ 1331*9172SFei.Feng@Sun.COM OFDM(2) << 12 | /* 18->12 */ 1332*9172SFei.Feng@Sun.COM OFDM(1) << 8 | /* 12-> 9 */ 1333*9172SFei.Feng@Sun.COM OFDM(0) << 4 | /* 9-> 6 */ 1334*9172SFei.Feng@Sun.COM OFDM(0)); /* 6-> 6 */ 1335*9172SFei.Feng@Sun.COM 1336*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_LG_FBK_CFG1, 1337*9172SFei.Feng@Sun.COM CCK(2) << 12 | /* 11->5.5 */ 1338*9172SFei.Feng@Sun.COM CCK(1) << 8 | /* 5.5-> 2 */ 1339*9172SFei.Feng@Sun.COM CCK(0) << 4 | /* 2-> 1 */ 1340*9172SFei.Feng@Sun.COM CCK(0)); /* 1-> 1 */ 1341*9172SFei.Feng@Sun.COM #undef OFDM 1342*9172SFei.Feng@Sun.COM #undef CCK 1343*9172SFei.Feng@Sun.COM } 1344*9172SFei.Feng@Sun.COM 1345*9172SFei.Feng@Sun.COM static void 1346*9172SFei.Feng@Sun.COM rt2860_set_txpreamble(struct rt2860_softc *sc) 1347*9172SFei.Feng@Sun.COM { 1348*9172SFei.Feng@Sun.COM uint32_t tmp; 1349*9172SFei.Feng@Sun.COM 1350*9172SFei.Feng@Sun.COM tmp = RT2860_READ(sc, RT2860_AUTO_RSP_CFG); 1351*9172SFei.Feng@Sun.COM tmp &= ~RT2860_CCK_SHORT_EN; 1352*9172SFei.Feng@Sun.COM if (sc->sc_ic.ic_flags & IEEE80211_F_SHPREAMBLE) 1353*9172SFei.Feng@Sun.COM tmp |= RT2860_CCK_SHORT_EN; 1354*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_AUTO_RSP_CFG, tmp); 1355*9172SFei.Feng@Sun.COM } 1356*9172SFei.Feng@Sun.COM 1357*9172SFei.Feng@Sun.COM static void 1358*9172SFei.Feng@Sun.COM rt2860_set_bssid(struct rt2860_softc *sc, const uint8_t *bssid) 1359*9172SFei.Feng@Sun.COM { 1360*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_MAC_BSSID_DW0, 1361*9172SFei.Feng@Sun.COM bssid[0] | bssid[1] << 8 | bssid[2] << 16 | bssid[3] << 24); 1362*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_MAC_BSSID_DW1, 1363*9172SFei.Feng@Sun.COM bssid[4] | bssid[5] << 8); 1364*9172SFei.Feng@Sun.COM } 1365*9172SFei.Feng@Sun.COM 1366*9172SFei.Feng@Sun.COM static void 1367*9172SFei.Feng@Sun.COM rt2860_set_basicrates(struct rt2860_softc *sc) 1368*9172SFei.Feng@Sun.COM { 1369*9172SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic; 1370*9172SFei.Feng@Sun.COM 1371*9172SFei.Feng@Sun.COM /* set basic rates mask */ 1372*9172SFei.Feng@Sun.COM if (ic->ic_curmode == IEEE80211_MODE_11B) 1373*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_LEGACY_BASIC_RATE, 0x003); 1374*9172SFei.Feng@Sun.COM else if (ic->ic_curmode == IEEE80211_MODE_11A) 1375*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_LEGACY_BASIC_RATE, 0x150); 1376*9172SFei.Feng@Sun.COM else /* 11g */ 1377*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_LEGACY_BASIC_RATE, 0x15f); 1378*9172SFei.Feng@Sun.COM } 1379*9172SFei.Feng@Sun.COM 1380*9172SFei.Feng@Sun.COM static void 1381*9172SFei.Feng@Sun.COM rt2860_amrr_node_init(const struct rt2860_amrr *amrr, 1382*9172SFei.Feng@Sun.COM struct rt2860_amrr_node *amn) 1383*9172SFei.Feng@Sun.COM { 1384*9172SFei.Feng@Sun.COM amn->amn_success = 0; 1385*9172SFei.Feng@Sun.COM amn->amn_recovery = 0; 1386*9172SFei.Feng@Sun.COM amn->amn_txcnt = amn->amn_retrycnt = 0; 1387*9172SFei.Feng@Sun.COM amn->amn_success_threshold = amrr->amrr_min_success_threshold; 1388*9172SFei.Feng@Sun.COM } 1389*9172SFei.Feng@Sun.COM 1390*9172SFei.Feng@Sun.COM static void 1391*9172SFei.Feng@Sun.COM rt2860_amrr_choose(struct rt2860_amrr *amrr, struct ieee80211_node *ni, 1392*9172SFei.Feng@Sun.COM struct rt2860_amrr_node *amn) 1393*9172SFei.Feng@Sun.COM { 1394*9172SFei.Feng@Sun.COM #define RV(rate) ((rate) & IEEE80211_RATE_VAL) 1395*9172SFei.Feng@Sun.COM #define is_success(amn) \ 1396*9172SFei.Feng@Sun.COM ((amn)->amn_retrycnt < (amn)->amn_txcnt / 10) 1397*9172SFei.Feng@Sun.COM #define is_failure(amn) \ 1398*9172SFei.Feng@Sun.COM ((amn)->amn_retrycnt > (amn)->amn_txcnt / 3) 1399*9172SFei.Feng@Sun.COM #define is_enough(amn) \ 1400*9172SFei.Feng@Sun.COM ((amn)->amn_txcnt > 10) 1401*9172SFei.Feng@Sun.COM #define is_min_rate(ni) \ 1402*9172SFei.Feng@Sun.COM ((ni)->in_txrate == 0) 1403*9172SFei.Feng@Sun.COM #define is_max_rate(ni) \ 1404*9172SFei.Feng@Sun.COM ((ni)->in_txrate == (ni)->in_rates.ir_nrates - 1) 1405*9172SFei.Feng@Sun.COM #define increase_rate(ni) \ 1406*9172SFei.Feng@Sun.COM ((ni)->in_txrate++) 1407*9172SFei.Feng@Sun.COM #define decrease_rate(ni) \ 1408*9172SFei.Feng@Sun.COM ((ni)->in_txrate--) 1409*9172SFei.Feng@Sun.COM #define reset_cnt(amn) \ 1410*9172SFei.Feng@Sun.COM { (amn)->amn_txcnt = (amn)->amn_retrycnt = 0; } 1411*9172SFei.Feng@Sun.COM 1412*9172SFei.Feng@Sun.COM int need_change = 0; 1413*9172SFei.Feng@Sun.COM 1414*9172SFei.Feng@Sun.COM if (is_success(amn) && is_enough(amn)) { 1415*9172SFei.Feng@Sun.COM amn->amn_success++; 1416*9172SFei.Feng@Sun.COM if (amn->amn_success >= amn->amn_success_threshold && 1417*9172SFei.Feng@Sun.COM !is_max_rate(ni)) { 1418*9172SFei.Feng@Sun.COM amn->amn_recovery = 1; 1419*9172SFei.Feng@Sun.COM amn->amn_success = 0; 1420*9172SFei.Feng@Sun.COM increase_rate(ni); 1421*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_amrr_choose(): " 1422*9172SFei.Feng@Sun.COM "increase rate = %d, #tx = %d, #retries = %d\n", 1423*9172SFei.Feng@Sun.COM RV(ni->in_rates.ir_rates[ni->in_txrate]), 1424*9172SFei.Feng@Sun.COM amn->amn_txcnt, amn->amn_retrycnt); 1425*9172SFei.Feng@Sun.COM need_change = 1; 1426*9172SFei.Feng@Sun.COM } else { 1427*9172SFei.Feng@Sun.COM amn->amn_recovery = 0; 1428*9172SFei.Feng@Sun.COM } 1429*9172SFei.Feng@Sun.COM } else if (is_failure(amn)) { 1430*9172SFei.Feng@Sun.COM amn->amn_success = 0; 1431*9172SFei.Feng@Sun.COM if (!is_min_rate(ni)) { 1432*9172SFei.Feng@Sun.COM if (amn->amn_recovery) { 1433*9172SFei.Feng@Sun.COM amn->amn_success_threshold *= 2; 1434*9172SFei.Feng@Sun.COM if (amn->amn_success_threshold > 1435*9172SFei.Feng@Sun.COM amrr->amrr_max_success_threshold) 1436*9172SFei.Feng@Sun.COM amn->amn_success_threshold = 1437*9172SFei.Feng@Sun.COM amrr->amrr_max_success_threshold; 1438*9172SFei.Feng@Sun.COM } else { 1439*9172SFei.Feng@Sun.COM amn->amn_success_threshold = 1440*9172SFei.Feng@Sun.COM amrr->amrr_min_success_threshold; 1441*9172SFei.Feng@Sun.COM } 1442*9172SFei.Feng@Sun.COM decrease_rate(ni); 1443*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_amrr_choose(): " 1444*9172SFei.Feng@Sun.COM "decrease rate = %d, #tx = %d, #retries = %d\n", 1445*9172SFei.Feng@Sun.COM RV(ni->in_rates.ir_rates[ni->in_txrate]), 1446*9172SFei.Feng@Sun.COM amn->amn_txcnt, amn->amn_retrycnt); 1447*9172SFei.Feng@Sun.COM need_change = 1; 1448*9172SFei.Feng@Sun.COM } 1449*9172SFei.Feng@Sun.COM amn->amn_recovery = 0; 1450*9172SFei.Feng@Sun.COM } 1451*9172SFei.Feng@Sun.COM 1452*9172SFei.Feng@Sun.COM if (is_enough(amn) || need_change) 1453*9172SFei.Feng@Sun.COM reset_cnt(amn); 1454*9172SFei.Feng@Sun.COM #undef RV 1455*9172SFei.Feng@Sun.COM } 1456*9172SFei.Feng@Sun.COM 1457*9172SFei.Feng@Sun.COM static void 1458*9172SFei.Feng@Sun.COM rt2860_newassoc(struct ieee80211com *ic, struct ieee80211_node *in, int isnew) 1459*9172SFei.Feng@Sun.COM { 1460*9172SFei.Feng@Sun.COM struct rt2860_softc *sc = (struct rt2860_softc *)ic; 1461*9172SFei.Feng@Sun.COM uint32_t off; 1462*9172SFei.Feng@Sun.COM uint8_t *fptr, wcid = 0; 1463*9172SFei.Feng@Sun.COM int i; 1464*9172SFei.Feng@Sun.COM 1465*9172SFei.Feng@Sun.COM if (isnew && in->in_associd != 0) { 1466*9172SFei.Feng@Sun.COM /* only interested in true associations */ 1467*9172SFei.Feng@Sun.COM wcid = RT2860_AID2WCID(in->in_associd); 1468*9172SFei.Feng@Sun.COM 1469*9172SFei.Feng@Sun.COM /* init WCID table entry */ 1470*9172SFei.Feng@Sun.COM off = RT2860_WCID_ENTRY(wcid); 1471*9172SFei.Feng@Sun.COM fptr = in->in_macaddr; 1472*9172SFei.Feng@Sun.COM for (i = 0; i < IEEE80211_ADDR_LEN; i++) 1473*9172SFei.Feng@Sun.COM rt2860_mem_write1(sc, off++, *fptr++); 1474*9172SFei.Feng@Sun.COM } 1475*9172SFei.Feng@Sun.COM rt2860_amrr_node_init(&sc->amrr, &sc->amn[wcid]); 1476*9172SFei.Feng@Sun.COM 1477*9172SFei.Feng@Sun.COM /* set rate to some reasonable initial value */ 1478*9172SFei.Feng@Sun.COM i = in->in_rates.ir_nrates - 1; 1479*9172SFei.Feng@Sun.COM for (; i > 0 && (in->in_rates.ir_rates[i] & IEEE80211_RATE_VAL) > 72; ) 1480*9172SFei.Feng@Sun.COM i--; 1481*9172SFei.Feng@Sun.COM in->in_txrate = i; 1482*9172SFei.Feng@Sun.COM 1483*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_80211, "rwn: rt2860_newassoc(): " 1484*9172SFei.Feng@Sun.COM "new assoc isnew=%d WCID=%d, initial rate=%d\n", 1485*9172SFei.Feng@Sun.COM isnew, wcid, 1486*9172SFei.Feng@Sun.COM in->in_rates.ir_rates[i] & IEEE80211_RATE_VAL); 1487*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_80211, "rwn: rt2860_newassoc(): " 1488*9172SFei.Feng@Sun.COM "addr=%x:%x:%x:%x:%x:%x\n", 1489*9172SFei.Feng@Sun.COM in->in_macaddr[0], in->in_macaddr[1], in->in_macaddr[2], 1490*9172SFei.Feng@Sun.COM in->in_macaddr[3], in->in_macaddr[4], in->in_macaddr[5]); 1491*9172SFei.Feng@Sun.COM } 1492*9172SFei.Feng@Sun.COM 1493*9172SFei.Feng@Sun.COM void 1494*9172SFei.Feng@Sun.COM rt2860_enable_tsf_sync(struct rt2860_softc *sc) 1495*9172SFei.Feng@Sun.COM { 1496*9172SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic; 1497*9172SFei.Feng@Sun.COM uint32_t tmp; 1498*9172SFei.Feng@Sun.COM 1499*9172SFei.Feng@Sun.COM tmp = RT2860_READ(sc, RT2860_BCN_TIME_CFG); 1500*9172SFei.Feng@Sun.COM 1501*9172SFei.Feng@Sun.COM tmp &= ~0x1fffff; 1502*9172SFei.Feng@Sun.COM tmp |= ic->ic_bss->in_intval * 16; 1503*9172SFei.Feng@Sun.COM tmp |= RT2860_TSF_TIMER_EN | RT2860_TBTT_TIMER_EN; 1504*9172SFei.Feng@Sun.COM if (ic->ic_opmode == IEEE80211_M_STA) { 1505*9172SFei.Feng@Sun.COM /* 1506*9172SFei.Feng@Sun.COM * Local TSF is always updated with remote TSF on beacon 1507*9172SFei.Feng@Sun.COM * reception. 1508*9172SFei.Feng@Sun.COM */ 1509*9172SFei.Feng@Sun.COM tmp |= 1 << RT2860_TSF_SYNC_MODE_SHIFT; 1510*9172SFei.Feng@Sun.COM } 1511*9172SFei.Feng@Sun.COM 1512*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_BCN_TIME_CFG, tmp); 1513*9172SFei.Feng@Sun.COM } 1514*9172SFei.Feng@Sun.COM 1515*9172SFei.Feng@Sun.COM static int 1516*9172SFei.Feng@Sun.COM rt2860_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) 1517*9172SFei.Feng@Sun.COM { 1518*9172SFei.Feng@Sun.COM struct rt2860_softc *sc = (struct rt2860_softc *)ic; 1519*9172SFei.Feng@Sun.COM enum ieee80211_state ostate; 1520*9172SFei.Feng@Sun.COM int err; 1521*9172SFei.Feng@Sun.COM uint32_t tmp; 1522*9172SFei.Feng@Sun.COM 1523*9172SFei.Feng@Sun.COM ostate = ic->ic_state; 1524*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_80211, "rwn: rt2860_newstate(): " 1525*9172SFei.Feng@Sun.COM "%x -> %x!\n", ostate, nstate); 1526*9172SFei.Feng@Sun.COM 1527*9172SFei.Feng@Sun.COM RT2860_GLOCK(sc); 1528*9172SFei.Feng@Sun.COM if (sc->sc_scan_id != 0) { 1529*9172SFei.Feng@Sun.COM (void) untimeout(sc->sc_scan_id); 1530*9172SFei.Feng@Sun.COM sc->sc_scan_id = 0; 1531*9172SFei.Feng@Sun.COM } 1532*9172SFei.Feng@Sun.COM if (sc->sc_rssadapt_id != 0) { 1533*9172SFei.Feng@Sun.COM (void) untimeout(sc->sc_rssadapt_id); 1534*9172SFei.Feng@Sun.COM sc->sc_rssadapt_id = 0; 1535*9172SFei.Feng@Sun.COM } 1536*9172SFei.Feng@Sun.COM if (ostate == IEEE80211_S_RUN) { 1537*9172SFei.Feng@Sun.COM /* turn link LED off */ 1538*9172SFei.Feng@Sun.COM rt2860_set_leds(sc, RT2860_LED_RADIO); 1539*9172SFei.Feng@Sun.COM } 1540*9172SFei.Feng@Sun.COM 1541*9172SFei.Feng@Sun.COM switch (nstate) { 1542*9172SFei.Feng@Sun.COM case IEEE80211_S_INIT: 1543*9172SFei.Feng@Sun.COM if (ostate == IEEE80211_S_RUN) { 1544*9172SFei.Feng@Sun.COM /* abort TSF synchronization */ 1545*9172SFei.Feng@Sun.COM tmp = RT2860_READ(sc, RT2860_BCN_TIME_CFG); 1546*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_BCN_TIME_CFG, 1547*9172SFei.Feng@Sun.COM tmp & ~(RT2860_BCN_TX_EN | RT2860_TSF_TIMER_EN | 1548*9172SFei.Feng@Sun.COM RT2860_TBTT_TIMER_EN)); 1549*9172SFei.Feng@Sun.COM } 1550*9172SFei.Feng@Sun.COM break; 1551*9172SFei.Feng@Sun.COM 1552*9172SFei.Feng@Sun.COM case IEEE80211_S_SCAN: 1553*9172SFei.Feng@Sun.COM rt2860_set_chan(sc, ic->ic_curchan); 1554*9172SFei.Feng@Sun.COM sc->sc_scan_id = timeout(rt2860_next_scan, (void *)sc, 1555*9172SFei.Feng@Sun.COM drv_usectohz(200000)); 1556*9172SFei.Feng@Sun.COM break; 1557*9172SFei.Feng@Sun.COM 1558*9172SFei.Feng@Sun.COM case IEEE80211_S_AUTH: 1559*9172SFei.Feng@Sun.COM case IEEE80211_S_ASSOC: 1560*9172SFei.Feng@Sun.COM rt2860_set_chan(sc, ic->ic_curchan); 1561*9172SFei.Feng@Sun.COM break; 1562*9172SFei.Feng@Sun.COM 1563*9172SFei.Feng@Sun.COM case IEEE80211_S_RUN: 1564*9172SFei.Feng@Sun.COM rt2860_set_chan(sc, ic->ic_curchan); 1565*9172SFei.Feng@Sun.COM 1566*9172SFei.Feng@Sun.COM if (ic->ic_opmode != IEEE80211_M_MONITOR) { 1567*9172SFei.Feng@Sun.COM rt2860_updateslot(sc); 1568*9172SFei.Feng@Sun.COM rt2860_enable_mrr(sc); 1569*9172SFei.Feng@Sun.COM rt2860_set_txpreamble(sc); 1570*9172SFei.Feng@Sun.COM rt2860_set_basicrates(sc); 1571*9172SFei.Feng@Sun.COM rt2860_set_bssid(sc, ic->ic_bss->in_bssid); 1572*9172SFei.Feng@Sun.COM } 1573*9172SFei.Feng@Sun.COM if (ic->ic_opmode == IEEE80211_M_STA) { 1574*9172SFei.Feng@Sun.COM /* fake a join to init the tx rate */ 1575*9172SFei.Feng@Sun.COM rt2860_newassoc(ic, ic->ic_bss, 1); 1576*9172SFei.Feng@Sun.COM } 1577*9172SFei.Feng@Sun.COM 1578*9172SFei.Feng@Sun.COM if (ic->ic_opmode != IEEE80211_M_MONITOR) { 1579*9172SFei.Feng@Sun.COM rt2860_enable_tsf_sync(sc); 1580*9172SFei.Feng@Sun.COM sc->sc_rssadapt_id = timeout(rt2860_updatestats, 1581*9172SFei.Feng@Sun.COM (void *)sc, drv_usectohz(500 * 1000)); 1582*9172SFei.Feng@Sun.COM } 1583*9172SFei.Feng@Sun.COM 1584*9172SFei.Feng@Sun.COM /* turn link LED on */ 1585*9172SFei.Feng@Sun.COM rt2860_set_leds(sc, RT2860_LED_RADIO | 1586*9172SFei.Feng@Sun.COM (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan) ? 1587*9172SFei.Feng@Sun.COM RT2860_LED_LINK_2GHZ : RT2860_LED_LINK_5GHZ)); 1588*9172SFei.Feng@Sun.COM break; 1589*9172SFei.Feng@Sun.COM } 1590*9172SFei.Feng@Sun.COM 1591*9172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 1592*9172SFei.Feng@Sun.COM 1593*9172SFei.Feng@Sun.COM err = sc->sc_newstate(ic, nstate, arg); 1594*9172SFei.Feng@Sun.COM 1595*9172SFei.Feng@Sun.COM return (err); 1596*9172SFei.Feng@Sun.COM } 1597*9172SFei.Feng@Sun.COM 1598*9172SFei.Feng@Sun.COM /* 1599*9172SFei.Feng@Sun.COM * Return the Rx chain with the highest RSSI for a given frame. 1600*9172SFei.Feng@Sun.COM */ 1601*9172SFei.Feng@Sun.COM static uint8_t 1602*9172SFei.Feng@Sun.COM rt2860_maxrssi_chain(struct rt2860_softc *sc, const struct rt2860_rxwi *rxwi) 1603*9172SFei.Feng@Sun.COM { 1604*9172SFei.Feng@Sun.COM uint8_t rxchain = 0; 1605*9172SFei.Feng@Sun.COM 1606*9172SFei.Feng@Sun.COM if (sc->nrxchains > 1) 1607*9172SFei.Feng@Sun.COM if (rxwi->rssi[1] > rxwi->rssi[rxchain]) 1608*9172SFei.Feng@Sun.COM rxchain = 1; 1609*9172SFei.Feng@Sun.COM if (sc->nrxchains > 2) 1610*9172SFei.Feng@Sun.COM if (rxwi->rssi[2] > rxwi->rssi[rxchain]) 1611*9172SFei.Feng@Sun.COM rxchain = 2; 1612*9172SFei.Feng@Sun.COM 1613*9172SFei.Feng@Sun.COM return (rxchain); 1614*9172SFei.Feng@Sun.COM } 1615*9172SFei.Feng@Sun.COM 1616*9172SFei.Feng@Sun.COM static void 1617*9172SFei.Feng@Sun.COM rt2860_drain_stats_fifo(struct rt2860_softc *sc) 1618*9172SFei.Feng@Sun.COM { 1619*9172SFei.Feng@Sun.COM struct rt2860_amrr_node *amn; 1620*9172SFei.Feng@Sun.COM uint32_t stat; 1621*9172SFei.Feng@Sun.COM uint8_t wcid, mcs, pid; 1622*9172SFei.Feng@Sun.COM 1623*9172SFei.Feng@Sun.COM /* drain Tx status FIFO (maxsize = 16) */ 1624*9172SFei.Feng@Sun.COM while ((stat = RT2860_READ(sc, RT2860_TX_STAT_FIFO)) & RT2860_TXQ_VLD) { 1625*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_TX, "rwn: rt2860_drain_stats_fifo(): " 1626*9172SFei.Feng@Sun.COM "tx stat 0x%08\n", stat); 1627*9172SFei.Feng@Sun.COM 1628*9172SFei.Feng@Sun.COM wcid = (stat >> 8) & 0xff; 1629*9172SFei.Feng@Sun.COM 1630*9172SFei.Feng@Sun.COM /* if no ACK was requested, no feedback is available */ 1631*9172SFei.Feng@Sun.COM if (!(stat & RT2860_TXQ_ACKREQ) || wcid == 0xff) 1632*9172SFei.Feng@Sun.COM continue; 1633*9172SFei.Feng@Sun.COM /* update per-STA AMRR stats */ 1634*9172SFei.Feng@Sun.COM amn = &sc->amn[wcid]; 1635*9172SFei.Feng@Sun.COM amn->amn_txcnt++; 1636*9172SFei.Feng@Sun.COM if (stat & RT2860_TXQ_OK) { 1637*9172SFei.Feng@Sun.COM /* 1638*9172SFei.Feng@Sun.COM * Check if there were retries, ie if the Tx success 1639*9172SFei.Feng@Sun.COM * rate is different from the requested rate. Note 1640*9172SFei.Feng@Sun.COM * that it works only because we do not allow rate 1641*9172SFei.Feng@Sun.COM * fallback from OFDM to CCK. 1642*9172SFei.Feng@Sun.COM */ 1643*9172SFei.Feng@Sun.COM mcs = (stat >> RT2860_TXQ_MCS_SHIFT) & 0x7f; 1644*9172SFei.Feng@Sun.COM pid = (stat >> RT2860_TXQ_PID_SHIFT) & 0xf; 1645*9172SFei.Feng@Sun.COM if (mcs + 1 != pid) 1646*9172SFei.Feng@Sun.COM amn->amn_retrycnt++; 1647*9172SFei.Feng@Sun.COM } else 1648*9172SFei.Feng@Sun.COM amn->amn_retrycnt++; 1649*9172SFei.Feng@Sun.COM } 1650*9172SFei.Feng@Sun.COM } 1651*9172SFei.Feng@Sun.COM 1652*9172SFei.Feng@Sun.COM /*ARGSUSED*/ 1653*9172SFei.Feng@Sun.COM static void 1654*9172SFei.Feng@Sun.COM rt2860_tx_intr(struct rt2860_softc *sc, int qid) 1655*9172SFei.Feng@Sun.COM { 1656*9172SFei.Feng@Sun.COM struct rt2860_tx_ring *ring = &sc->txq[qid]; 1657*9172SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic; 1658*9172SFei.Feng@Sun.COM uint32_t hw; 1659*9172SFei.Feng@Sun.COM 1660*9172SFei.Feng@Sun.COM rt2860_drain_stats_fifo(sc); 1661*9172SFei.Feng@Sun.COM 1662*9172SFei.Feng@Sun.COM mutex_enter(&sc->sc_txlock); 1663*9172SFei.Feng@Sun.COM hw = RT2860_READ(sc, RT2860_TX_DTX_IDX(qid)); 1664*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_TX, "rwn: rwn_tx_intr():" 1665*9172SFei.Feng@Sun.COM "hw = %x, ring->next = %x, queued = %d\n", 1666*9172SFei.Feng@Sun.COM hw, ring->next, ring->queued); 1667*9172SFei.Feng@Sun.COM while (ring->next != hw) { 1668*9172SFei.Feng@Sun.COM struct rt2860_txd *txd = &ring->txd[ring->next]; 1669*9172SFei.Feng@Sun.COM struct rt2860_tx_data *data = ring->data[ring->next]; 1670*9172SFei.Feng@Sun.COM 1671*9172SFei.Feng@Sun.COM if (data != NULL) { 1672*9172SFei.Feng@Sun.COM RT2860_DMA_SYNC(data->txbuf_dma, DDI_DMA_SYNC_FORDEV); 1673*9172SFei.Feng@Sun.COM if (data->ni != NULL) { 1674*9172SFei.Feng@Sun.COM ieee80211_free_node(data->ni); 1675*9172SFei.Feng@Sun.COM data->ni = NULL; 1676*9172SFei.Feng@Sun.COM } 1677*9172SFei.Feng@Sun.COM SLIST_INSERT_HEAD(&sc->data_pool, data, next); 1678*9172SFei.Feng@Sun.COM ring->data[ring->next] = NULL; 1679*9172SFei.Feng@Sun.COM } 1680*9172SFei.Feng@Sun.COM 1681*9172SFei.Feng@Sun.COM txd->sdl0 &= ~LE_16(RT2860_TX_DDONE); 1682*9172SFei.Feng@Sun.COM 1683*9172SFei.Feng@Sun.COM (void) ddi_dma_sync(ring->txdesc_dma.dma_hdl, 1684*9172SFei.Feng@Sun.COM ring->next * sizeof (struct rt2860_txd), 1685*9172SFei.Feng@Sun.COM sizeof (struct rt2860_txd), 1686*9172SFei.Feng@Sun.COM DDI_DMA_SYNC_FORDEV); 1687*9172SFei.Feng@Sun.COM 1688*9172SFei.Feng@Sun.COM ring->queued--; 1689*9172SFei.Feng@Sun.COM ring->next = (ring->next + 1) % RT2860_TX_RING_COUNT; 1690*9172SFei.Feng@Sun.COM 1691*9172SFei.Feng@Sun.COM if (sc->sc_need_sched && 1692*9172SFei.Feng@Sun.COM (ring->queued < RT2860_TX_RING_COUNT)) { 1693*9172SFei.Feng@Sun.COM sc->sc_need_sched = 0; 1694*9172SFei.Feng@Sun.COM mac_tx_update(ic->ic_mach); 1695*9172SFei.Feng@Sun.COM } 1696*9172SFei.Feng@Sun.COM } 1697*9172SFei.Feng@Sun.COM sc->sc_tx_timer = 0; 1698*9172SFei.Feng@Sun.COM mutex_exit(&sc->sc_txlock); 1699*9172SFei.Feng@Sun.COM } 1700*9172SFei.Feng@Sun.COM static void 1701*9172SFei.Feng@Sun.COM rt2860_rx_intr(struct rt2860_softc *sc) 1702*9172SFei.Feng@Sun.COM { 1703*9172SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic; 1704*9172SFei.Feng@Sun.COM struct ieee80211_node *ni; 1705*9172SFei.Feng@Sun.COM struct ieee80211_frame *wh; 1706*9172SFei.Feng@Sun.COM int pktlen; 1707*9172SFei.Feng@Sun.COM uint8_t ant, rssi, *rxbuf; 1708*9172SFei.Feng@Sun.COM mblk_t *mp0; 1709*9172SFei.Feng@Sun.COM 1710*9172SFei.Feng@Sun.COM mutex_enter(&sc->sc_rxlock); 1711*9172SFei.Feng@Sun.COM for (;;) { 1712*9172SFei.Feng@Sun.COM struct rt2860_rx_data *data = &sc->rxq.data[sc->rxq.cur]; 1713*9172SFei.Feng@Sun.COM struct rt2860_rxd *rxd = &sc->rxq.rxd[sc->rxq.cur]; 1714*9172SFei.Feng@Sun.COM struct rt2860_rxwi *rxwi; 1715*9172SFei.Feng@Sun.COM 1716*9172SFei.Feng@Sun.COM (void) ddi_dma_sync(sc->rxq.rxdesc_dma.dma_hdl, 1717*9172SFei.Feng@Sun.COM sc->rxq.cur * sizeof (struct rt2860_rxd), 1718*9172SFei.Feng@Sun.COM sizeof (struct rt2860_rxd), 1719*9172SFei.Feng@Sun.COM DDI_DMA_SYNC_FORKERNEL); 1720*9172SFei.Feng@Sun.COM 1721*9172SFei.Feng@Sun.COM if (!(rxd->sdl0 & LE_16(RT2860_RX_DDONE))) { 1722*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_RX, "rwn: rt2860_rx_intr(): " 1723*9172SFei.Feng@Sun.COM "rx done!\n"); 1724*9172SFei.Feng@Sun.COM break; 1725*9172SFei.Feng@Sun.COM } 1726*9172SFei.Feng@Sun.COM 1727*9172SFei.Feng@Sun.COM if (rxd->flags & 1728*9172SFei.Feng@Sun.COM LE_32(RT2860_RX_CRCERR | RT2860_RX_ICVERR)) { 1729*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_RX, "rwn: rt2860_rx_intr(): " 1730*9172SFei.Feng@Sun.COM "rx crc error & rx icv error!\n"); 1731*9172SFei.Feng@Sun.COM sc->sc_rx_err++; 1732*9172SFei.Feng@Sun.COM goto skip; 1733*9172SFei.Feng@Sun.COM } 1734*9172SFei.Feng@Sun.COM 1735*9172SFei.Feng@Sun.COM if (rxd->flags & LE_32(RT2860_RX_MICERR)) { 1736*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_RX, "rwn: rt2860_rx_intr(): " 1737*9172SFei.Feng@Sun.COM "rx mic error!\n"); 1738*9172SFei.Feng@Sun.COM sc->sc_rx_err++; 1739*9172SFei.Feng@Sun.COM goto skip; 1740*9172SFei.Feng@Sun.COM } 1741*9172SFei.Feng@Sun.COM 1742*9172SFei.Feng@Sun.COM (void) ddi_dma_sync(data->rxbuf_dma.dma_hdl, 1743*9172SFei.Feng@Sun.COM data->rxbuf_dma.offset, 1744*9172SFei.Feng@Sun.COM data->rxbuf_dma.alength, 1745*9172SFei.Feng@Sun.COM DDI_DMA_SYNC_FORCPU); 1746*9172SFei.Feng@Sun.COM 1747*9172SFei.Feng@Sun.COM rxbuf = (uint8_t *)data->rxbuf_dma.mem_va; 1748*9172SFei.Feng@Sun.COM rxd->sdp0 = LE_32(data->rxbuf_dma.cookie.dmac_address); 1749*9172SFei.Feng@Sun.COM rxwi = (struct rt2860_rxwi *)rxbuf; 1750*9172SFei.Feng@Sun.COM rxbuf = (uint8_t *)(rxwi + 1); 1751*9172SFei.Feng@Sun.COM pktlen = LE_16(rxwi->len) & 0xfff; 1752*9172SFei.Feng@Sun.COM 1753*9172SFei.Feng@Sun.COM mp0 = allocb(sc->sc_dmabuf_size, BPRI_MED); 1754*9172SFei.Feng@Sun.COM if (mp0 == NULL) { 1755*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_RX, "rwn: rt2860_rx_intr():" 1756*9172SFei.Feng@Sun.COM "alloc mblk error\n"); 1757*9172SFei.Feng@Sun.COM sc->sc_rx_nobuf++; 1758*9172SFei.Feng@Sun.COM goto skip; 1759*9172SFei.Feng@Sun.COM } 1760*9172SFei.Feng@Sun.COM bcopy(rxbuf, mp0->b_rptr, pktlen); 1761*9172SFei.Feng@Sun.COM mp0->b_wptr += pktlen; 1762*9172SFei.Feng@Sun.COM 1763*9172SFei.Feng@Sun.COM wh = (struct ieee80211_frame *)mp0->b_rptr; 1764*9172SFei.Feng@Sun.COM 1765*9172SFei.Feng@Sun.COM /* HW may insert 2 padding bytes after 802.11 header */ 1766*9172SFei.Feng@Sun.COM if (rxd->flags & LE_32(RT2860_RX_L2PAD)) { 1767*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_RX, "rwn: rt2860_rx_intr():" 1768*9172SFei.Feng@Sun.COM "2 padding bytes after 80211 header!\n"); 1769*9172SFei.Feng@Sun.COM } 1770*9172SFei.Feng@Sun.COM 1771*9172SFei.Feng@Sun.COM ant = rt2860_maxrssi_chain(sc, rxwi); 1772*9172SFei.Feng@Sun.COM rssi = rxwi->rssi[ant]; 1773*9172SFei.Feng@Sun.COM /* grab a reference to the source node */ 1774*9172SFei.Feng@Sun.COM ni = ieee80211_find_rxnode(ic, wh); 1775*9172SFei.Feng@Sun.COM 1776*9172SFei.Feng@Sun.COM ieee80211_input(ic, mp0, ni, rssi, 0); 1777*9172SFei.Feng@Sun.COM 1778*9172SFei.Feng@Sun.COM /* node is no longer needed */ 1779*9172SFei.Feng@Sun.COM ieee80211_free_node(ni); 1780*9172SFei.Feng@Sun.COM skip: 1781*9172SFei.Feng@Sun.COM rxd->sdl0 &= ~LE_16(RT2860_RX_DDONE); 1782*9172SFei.Feng@Sun.COM 1783*9172SFei.Feng@Sun.COM (void) ddi_dma_sync(sc->rxq.rxdesc_dma.dma_hdl, 1784*9172SFei.Feng@Sun.COM sc->rxq.cur * sizeof (struct rt2860_rxd), 1785*9172SFei.Feng@Sun.COM sizeof (struct rt2860_rxd), 1786*9172SFei.Feng@Sun.COM DDI_DMA_SYNC_FORDEV); 1787*9172SFei.Feng@Sun.COM 1788*9172SFei.Feng@Sun.COM sc->rxq.cur = (sc->rxq.cur + 1) % RT2860_RX_RING_COUNT; 1789*9172SFei.Feng@Sun.COM } 1790*9172SFei.Feng@Sun.COM mutex_exit(&sc->sc_rxlock); 1791*9172SFei.Feng@Sun.COM 1792*9172SFei.Feng@Sun.COM /* tell HW what we have processed */ 1793*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_RX_CALC_IDX, 1794*9172SFei.Feng@Sun.COM (sc->rxq.cur - 1) % RT2860_RX_RING_COUNT); 1795*9172SFei.Feng@Sun.COM } 1796*9172SFei.Feng@Sun.COM 1797*9172SFei.Feng@Sun.COM static uint_t 1798*9172SFei.Feng@Sun.COM rt2860_softintr(caddr_t data) 1799*9172SFei.Feng@Sun.COM { 1800*9172SFei.Feng@Sun.COM struct rt2860_softc *sc = (struct rt2860_softc *)data; 1801*9172SFei.Feng@Sun.COM 1802*9172SFei.Feng@Sun.COM /* 1803*9172SFei.Feng@Sun.COM * Check if the soft interrupt is triggered by another 1804*9172SFei.Feng@Sun.COM * driver at the same level. 1805*9172SFei.Feng@Sun.COM */ 1806*9172SFei.Feng@Sun.COM RT2860_GLOCK(sc); 1807*9172SFei.Feng@Sun.COM if (sc->sc_rx_pend) { 1808*9172SFei.Feng@Sun.COM sc->sc_rx_pend = 0; 1809*9172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 1810*9172SFei.Feng@Sun.COM rt2860_rx_intr(sc); 1811*9172SFei.Feng@Sun.COM return (DDI_INTR_CLAIMED); 1812*9172SFei.Feng@Sun.COM } 1813*9172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 1814*9172SFei.Feng@Sun.COM 1815*9172SFei.Feng@Sun.COM return (DDI_INTR_UNCLAIMED); 1816*9172SFei.Feng@Sun.COM } 1817*9172SFei.Feng@Sun.COM 1818*9172SFei.Feng@Sun.COM static uint_t 1819*9172SFei.Feng@Sun.COM rt2860_intr(caddr_t arg) 1820*9172SFei.Feng@Sun.COM { 1821*9172SFei.Feng@Sun.COM struct rt2860_softc *sc = (struct rt2860_softc *)arg; 1822*9172SFei.Feng@Sun.COM uint32_t r; 1823*9172SFei.Feng@Sun.COM 1824*9172SFei.Feng@Sun.COM RT2860_GLOCK(sc); 1825*9172SFei.Feng@Sun.COM 1826*9172SFei.Feng@Sun.COM if ((!RT2860_IS_RUNNING(sc)) || RT2860_IS_SUSPEND(sc)) { 1827*9172SFei.Feng@Sun.COM /* 1828*9172SFei.Feng@Sun.COM * The hardware is not ready/present, don't touch anything. 1829*9172SFei.Feng@Sun.COM * Note this can happen early on if the IRQ is shared. 1830*9172SFei.Feng@Sun.COM */ 1831*9172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 1832*9172SFei.Feng@Sun.COM return (DDI_INTR_UNCLAIMED); 1833*9172SFei.Feng@Sun.COM } 1834*9172SFei.Feng@Sun.COM 1835*9172SFei.Feng@Sun.COM r = RT2860_READ(sc, RT2860_INT_STATUS); 1836*9172SFei.Feng@Sun.COM if (r == 0xffffffff) { 1837*9172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 1838*9172SFei.Feng@Sun.COM return (DDI_INTR_UNCLAIMED); 1839*9172SFei.Feng@Sun.COM } 1840*9172SFei.Feng@Sun.COM if (r == 0) { 1841*9172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 1842*9172SFei.Feng@Sun.COM return (DDI_INTR_UNCLAIMED); 1843*9172SFei.Feng@Sun.COM } 1844*9172SFei.Feng@Sun.COM 1845*9172SFei.Feng@Sun.COM /* acknowledge interrupts */ 1846*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_INT_STATUS, r); 1847*9172SFei.Feng@Sun.COM 1848*9172SFei.Feng@Sun.COM if (r & RT2860_TX_COHERENT) 1849*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr()" 1850*9172SFei.Feng@Sun.COM "RT2860_TX_COHERENT\n"); 1851*9172SFei.Feng@Sun.COM 1852*9172SFei.Feng@Sun.COM if (r & RT2860_RX_COHERENT) 1853*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr()" 1854*9172SFei.Feng@Sun.COM "RT2860_RX_COHERENT\n"); 1855*9172SFei.Feng@Sun.COM 1856*9172SFei.Feng@Sun.COM if (r & RT2860_MAC_INT_2) { 1857*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr(): " 1858*9172SFei.Feng@Sun.COM "RT2860_MAC_INT_2\n"); 1859*9172SFei.Feng@Sun.COM rt2860_drain_stats_fifo(sc); 1860*9172SFei.Feng@Sun.COM } 1861*9172SFei.Feng@Sun.COM 1862*9172SFei.Feng@Sun.COM if (r & RT2860_TX_DONE_INT5) { 1863*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr(): " 1864*9172SFei.Feng@Sun.COM "RT2860_TX_DONE_INT5\n"); 1865*9172SFei.Feng@Sun.COM rt2860_tx_intr(sc, 5); 1866*9172SFei.Feng@Sun.COM } 1867*9172SFei.Feng@Sun.COM 1868*9172SFei.Feng@Sun.COM if (r & RT2860_RX_DONE_INT) { 1869*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr()" 1870*9172SFei.Feng@Sun.COM "RT2860_RX_INT\n"); 1871*9172SFei.Feng@Sun.COM sc->sc_rx_pend = 1; 1872*9172SFei.Feng@Sun.COM ddi_trigger_softintr(sc->sc_softintr_hdl); 1873*9172SFei.Feng@Sun.COM } 1874*9172SFei.Feng@Sun.COM 1875*9172SFei.Feng@Sun.COM if (r & RT2860_TX_DONE_INT4) { 1876*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr(): " 1877*9172SFei.Feng@Sun.COM "RT2860_TX_DONE_INT4\n"); 1878*9172SFei.Feng@Sun.COM rt2860_tx_intr(sc, 4); 1879*9172SFei.Feng@Sun.COM } 1880*9172SFei.Feng@Sun.COM 1881*9172SFei.Feng@Sun.COM if (r & RT2860_TX_DONE_INT3) { 1882*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr(): " 1883*9172SFei.Feng@Sun.COM "RT2860_TX_DONE_INT3\n"); 1884*9172SFei.Feng@Sun.COM rt2860_tx_intr(sc, 3); 1885*9172SFei.Feng@Sun.COM } 1886*9172SFei.Feng@Sun.COM 1887*9172SFei.Feng@Sun.COM if (r & RT2860_TX_DONE_INT2) { 1888*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr(): " 1889*9172SFei.Feng@Sun.COM "RT2860_TX_DONE_INT2\n"); 1890*9172SFei.Feng@Sun.COM rt2860_tx_intr(sc, 2); 1891*9172SFei.Feng@Sun.COM } 1892*9172SFei.Feng@Sun.COM 1893*9172SFei.Feng@Sun.COM if (r & RT2860_TX_DONE_INT1) { 1894*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr(): " 1895*9172SFei.Feng@Sun.COM "RT2860_TX_DONE_INT1\n"); 1896*9172SFei.Feng@Sun.COM rt2860_tx_intr(sc, 1); 1897*9172SFei.Feng@Sun.COM } 1898*9172SFei.Feng@Sun.COM 1899*9172SFei.Feng@Sun.COM if (r & RT2860_TX_DONE_INT0) { 1900*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr(): " 1901*9172SFei.Feng@Sun.COM "RT2860_TX_DONE_INT0\n"); 1902*9172SFei.Feng@Sun.COM rt2860_tx_intr(sc, 0); 1903*9172SFei.Feng@Sun.COM } 1904*9172SFei.Feng@Sun.COM 1905*9172SFei.Feng@Sun.COM if (r & RT2860_MAC_INT_0) { 1906*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr(): " 1907*9172SFei.Feng@Sun.COM "RT2860_MAC_INT_0\n"); 1908*9172SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic; 1909*9172SFei.Feng@Sun.COM /* check if protection mode has changed */ 1910*9172SFei.Feng@Sun.COM if ((sc->sc_ic_flags ^ ic->ic_flags) & IEEE80211_F_USEPROT) { 1911*9172SFei.Feng@Sun.COM rt2860_updateprot(ic); 1912*9172SFei.Feng@Sun.COM sc->sc_ic_flags = ic->ic_flags; 1913*9172SFei.Feng@Sun.COM } 1914*9172SFei.Feng@Sun.COM } 1915*9172SFei.Feng@Sun.COM 1916*9172SFei.Feng@Sun.COM if (r & RT2860_MAC_INT_3) 1917*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr(): " 1918*9172SFei.Feng@Sun.COM "RT2860_MAC_INT_3\n"); 1919*9172SFei.Feng@Sun.COM 1920*9172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 1921*9172SFei.Feng@Sun.COM 1922*9172SFei.Feng@Sun.COM return (DDI_INTR_CLAIMED); 1923*9172SFei.Feng@Sun.COM } 1924*9172SFei.Feng@Sun.COM 1925*9172SFei.Feng@Sun.COM static void 1926*9172SFei.Feng@Sun.COM rt2860_set_region_4(struct rt2860_softc *sc, 1927*9172SFei.Feng@Sun.COM uint32_t addr, uint32_t data, int size) 1928*9172SFei.Feng@Sun.COM { 1929*9172SFei.Feng@Sun.COM for (; size > 0; size--, data++, addr += 4) 1930*9172SFei.Feng@Sun.COM ddi_put32((sc)->sc_io_handle, 1931*9172SFei.Feng@Sun.COM (uint32_t *)((uintptr_t)(sc)->sc_io_base + addr), data); 1932*9172SFei.Feng@Sun.COM } 1933*9172SFei.Feng@Sun.COM 1934*9172SFei.Feng@Sun.COM static int 1935*9172SFei.Feng@Sun.COM rt2860_load_microcode(struct rt2860_softc *sc) 1936*9172SFei.Feng@Sun.COM { 1937*9172SFei.Feng@Sun.COM int ntries; 1938*9172SFei.Feng@Sun.COM size_t size; 1939*9172SFei.Feng@Sun.COM uint8_t *ucode, *fptr; 1940*9172SFei.Feng@Sun.COM uint32_t off, i; 1941*9172SFei.Feng@Sun.COM 1942*9172SFei.Feng@Sun.COM ucode = rt2860_fw_bin; 1943*9172SFei.Feng@Sun.COM size = sizeof (rt2860_fw_bin); 1944*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_FW, "rwn: rt2860_load_microcode(): " 1945*9172SFei.Feng@Sun.COM "The size of ucode is: %x\n", size); 1946*9172SFei.Feng@Sun.COM 1947*9172SFei.Feng@Sun.COM /* set "host program ram write selection" bit */ 1948*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_SYS_CTRL, RT2860_HST_PM_SEL); 1949*9172SFei.Feng@Sun.COM /* write microcode image */ 1950*9172SFei.Feng@Sun.COM fptr = ucode; 1951*9172SFei.Feng@Sun.COM off = RT2860_FW_BASE; 1952*9172SFei.Feng@Sun.COM for (i = 0; i < size; i++) { 1953*9172SFei.Feng@Sun.COM rt2860_mem_write1(sc, off++, *fptr++); 1954*9172SFei.Feng@Sun.COM } 1955*9172SFei.Feng@Sun.COM /* kick microcontroller unit */ 1956*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_SYS_CTRL, 0); 1957*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_SYS_CTRL, RT2860_MCU_RESET); 1958*9172SFei.Feng@Sun.COM 1959*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_H2M_BBPAGENT, 0); 1960*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_H2M_MAILBOX, 0); 1961*9172SFei.Feng@Sun.COM 1962*9172SFei.Feng@Sun.COM /* wait until microcontroller is ready */ 1963*9172SFei.Feng@Sun.COM for (ntries = 0; ntries < 1000; ntries++) { 1964*9172SFei.Feng@Sun.COM if (RT2860_READ(sc, RT2860_SYS_CTRL) & RT2860_MCU_READY) 1965*9172SFei.Feng@Sun.COM break; 1966*9172SFei.Feng@Sun.COM DELAY(1000); 1967*9172SFei.Feng@Sun.COM } 1968*9172SFei.Feng@Sun.COM if (ntries == 1000) { 1969*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_FW, "rwn: rt2860_load_microcode(): " 1970*9172SFei.Feng@Sun.COM "timeout waiting for MCU to initialie\n"); 1971*9172SFei.Feng@Sun.COM return (ETIMEDOUT); 1972*9172SFei.Feng@Sun.COM } 1973*9172SFei.Feng@Sun.COM 1974*9172SFei.Feng@Sun.COM return (0); 1975*9172SFei.Feng@Sun.COM } 1976*9172SFei.Feng@Sun.COM 1977*9172SFei.Feng@Sun.COM static void 1978*9172SFei.Feng@Sun.COM rt2860_set_macaddr(struct rt2860_softc *sc, const uint8_t *addr) 1979*9172SFei.Feng@Sun.COM { 1980*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_MAC_ADDR_DW0, 1981*9172SFei.Feng@Sun.COM addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24); 1982*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_MAC_ADDR_DW1, 1983*9172SFei.Feng@Sun.COM addr[4] | addr[5] << 8); 1984*9172SFei.Feng@Sun.COM } 1985*9172SFei.Feng@Sun.COM 1986*9172SFei.Feng@Sun.COM /* 1987*9172SFei.Feng@Sun.COM * Send a command to the 8051 microcontroller unit. 1988*9172SFei.Feng@Sun.COM */ 1989*9172SFei.Feng@Sun.COM static int 1990*9172SFei.Feng@Sun.COM rt2860_mcu_cmd(struct rt2860_softc *sc, uint8_t cmd, uint16_t arg) 1991*9172SFei.Feng@Sun.COM { 1992*9172SFei.Feng@Sun.COM int ntries; 1993*9172SFei.Feng@Sun.COM 1994*9172SFei.Feng@Sun.COM for (ntries = 0; ntries < 100; ntries++) { 1995*9172SFei.Feng@Sun.COM if (!(RT2860_READ(sc, RT2860_H2M_MAILBOX) & RT2860_H2M_BUSY)) 1996*9172SFei.Feng@Sun.COM break; 1997*9172SFei.Feng@Sun.COM DELAY(2); 1998*9172SFei.Feng@Sun.COM } 1999*9172SFei.Feng@Sun.COM if (ntries == 100) 2000*9172SFei.Feng@Sun.COM return (EIO); 2001*9172SFei.Feng@Sun.COM 2002*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_H2M_MAILBOX, 2003*9172SFei.Feng@Sun.COM RT2860_H2M_BUSY | RT2860_TOKEN_NO_INTR << 16 | arg); 2004*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_HOST_CMD, cmd); 2005*9172SFei.Feng@Sun.COM 2006*9172SFei.Feng@Sun.COM return (RT2860_SUCCESS); 2007*9172SFei.Feng@Sun.COM } 2008*9172SFei.Feng@Sun.COM 2009*9172SFei.Feng@Sun.COM /* 2010*9172SFei.Feng@Sun.COM * Reading and writing from/to the BBP is different from RT2560 and RT2661. 2011*9172SFei.Feng@Sun.COM * We access the BBP through the 8051 microcontroller unit which means that 2012*9172SFei.Feng@Sun.COM * the microcode must be loaded first. 2013*9172SFei.Feng@Sun.COM */ 2014*9172SFei.Feng@Sun.COM static uint8_t 2015*9172SFei.Feng@Sun.COM rt2860_mcu_bbp_read(struct rt2860_softc *sc, uint8_t reg) 2016*9172SFei.Feng@Sun.COM { 2017*9172SFei.Feng@Sun.COM uint32_t val; 2018*9172SFei.Feng@Sun.COM int ntries; 2019*9172SFei.Feng@Sun.COM 2020*9172SFei.Feng@Sun.COM for (ntries = 0; ntries < 100; ntries++) { 2021*9172SFei.Feng@Sun.COM if (!(RT2860_READ(sc, 2022*9172SFei.Feng@Sun.COM RT2860_H2M_BBPAGENT) & RT2860_BBP_CSR_KICK)) 2023*9172SFei.Feng@Sun.COM break; 2024*9172SFei.Feng@Sun.COM DELAY(1); 2025*9172SFei.Feng@Sun.COM } 2026*9172SFei.Feng@Sun.COM if (ntries == 100) { 2027*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_FW, "rwn: rt2860_mcu_bbp_read():" 2028*9172SFei.Feng@Sun.COM "could not read from BBP through MCU\n"); 2029*9172SFei.Feng@Sun.COM return (0); 2030*9172SFei.Feng@Sun.COM } 2031*9172SFei.Feng@Sun.COM 2032*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_H2M_BBPAGENT, RT2860_BBP_RW_PARALLEL | 2033*9172SFei.Feng@Sun.COM RT2860_BBP_CSR_KICK | RT2860_BBP_CSR_READ | reg << 8); 2034*9172SFei.Feng@Sun.COM 2035*9172SFei.Feng@Sun.COM (void) rt2860_mcu_cmd(sc, RT2860_MCU_CMD_BBP, 0); 2036*9172SFei.Feng@Sun.COM DELAY(1000); 2037*9172SFei.Feng@Sun.COM 2038*9172SFei.Feng@Sun.COM for (ntries = 0; ntries < 100; ntries++) { 2039*9172SFei.Feng@Sun.COM val = RT2860_READ(sc, RT2860_H2M_BBPAGENT); 2040*9172SFei.Feng@Sun.COM if (!(val & RT2860_BBP_CSR_KICK)) 2041*9172SFei.Feng@Sun.COM return (val & 0xff); 2042*9172SFei.Feng@Sun.COM DELAY(1); 2043*9172SFei.Feng@Sun.COM } 2044*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_FW, "rwn: rt2860_mcu_bbp_read():" 2045*9172SFei.Feng@Sun.COM "could not read from BBP through MCU\n"); 2046*9172SFei.Feng@Sun.COM 2047*9172SFei.Feng@Sun.COM return (0); 2048*9172SFei.Feng@Sun.COM } 2049*9172SFei.Feng@Sun.COM 2050*9172SFei.Feng@Sun.COM static void 2051*9172SFei.Feng@Sun.COM rt2860_mcu_bbp_write(struct rt2860_softc *sc, uint8_t reg, uint8_t val) 2052*9172SFei.Feng@Sun.COM { 2053*9172SFei.Feng@Sun.COM int ntries; 2054*9172SFei.Feng@Sun.COM 2055*9172SFei.Feng@Sun.COM for (ntries = 0; ntries < 100; ntries++) { 2056*9172SFei.Feng@Sun.COM if (!(RT2860_READ(sc, 2057*9172SFei.Feng@Sun.COM RT2860_H2M_BBPAGENT) & RT2860_BBP_CSR_KICK)) 2058*9172SFei.Feng@Sun.COM break; 2059*9172SFei.Feng@Sun.COM DELAY(1); 2060*9172SFei.Feng@Sun.COM } 2061*9172SFei.Feng@Sun.COM if (ntries == 100) { 2062*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_FW, "rwn: rt2860_mcu_bbp_write():" 2063*9172SFei.Feng@Sun.COM "could not write to BBP through MCU\n"); 2064*9172SFei.Feng@Sun.COM return; 2065*9172SFei.Feng@Sun.COM } 2066*9172SFei.Feng@Sun.COM 2067*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_H2M_BBPAGENT, RT2860_BBP_RW_PARALLEL | 2068*9172SFei.Feng@Sun.COM RT2860_BBP_CSR_KICK | reg << 8 | val); 2069*9172SFei.Feng@Sun.COM 2070*9172SFei.Feng@Sun.COM (void) rt2860_mcu_cmd(sc, RT2860_MCU_CMD_BBP, 0); 2071*9172SFei.Feng@Sun.COM DELAY(1000); 2072*9172SFei.Feng@Sun.COM } 2073*9172SFei.Feng@Sun.COM 2074*9172SFei.Feng@Sun.COM static int 2075*9172SFei.Feng@Sun.COM rt2860_bbp_init(struct rt2860_softc *sc) 2076*9172SFei.Feng@Sun.COM { 2077*9172SFei.Feng@Sun.COM int i, ntries; 2078*9172SFei.Feng@Sun.COM 2079*9172SFei.Feng@Sun.COM /* wait for BBP to wake up */ 2080*9172SFei.Feng@Sun.COM for (ntries = 0; ntries < 20; ntries++) { 2081*9172SFei.Feng@Sun.COM uint8_t bbp0 = rt2860_mcu_bbp_read(sc, 0); 2082*9172SFei.Feng@Sun.COM if (bbp0 != 0 && bbp0 != 0xff) 2083*9172SFei.Feng@Sun.COM break; 2084*9172SFei.Feng@Sun.COM } 2085*9172SFei.Feng@Sun.COM if (ntries == 20) { 2086*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_FW, "rwn: rt2860_bbp_init():" 2087*9172SFei.Feng@Sun.COM "timeout waiting for BBP to wake up\n"); 2088*9172SFei.Feng@Sun.COM return (ETIMEDOUT); 2089*9172SFei.Feng@Sun.COM } 2090*9172SFei.Feng@Sun.COM 2091*9172SFei.Feng@Sun.COM /* initialize BBP registers to default values */ 2092*9172SFei.Feng@Sun.COM for (i = 0; i < 12; i++) { 2093*9172SFei.Feng@Sun.COM rt2860_mcu_bbp_write(sc, rt2860_def_bbp[i].reg, 2094*9172SFei.Feng@Sun.COM rt2860_def_bbp[i].val); 2095*9172SFei.Feng@Sun.COM } 2096*9172SFei.Feng@Sun.COM 2097*9172SFei.Feng@Sun.COM /* fix BBP69 and BBP73 for RT2860C */ 2098*9172SFei.Feng@Sun.COM if (sc->mac_rev == 0x28600100) { 2099*9172SFei.Feng@Sun.COM rt2860_mcu_bbp_write(sc, 69, 0x16); 2100*9172SFei.Feng@Sun.COM rt2860_mcu_bbp_write(sc, 73, 0x12); 2101*9172SFei.Feng@Sun.COM } 2102*9172SFei.Feng@Sun.COM 2103*9172SFei.Feng@Sun.COM return (0); 2104*9172SFei.Feng@Sun.COM } 2105*9172SFei.Feng@Sun.COM 2106*9172SFei.Feng@Sun.COM static void 2107*9172SFei.Feng@Sun.COM rt2860_rf_write(struct rt2860_softc *sc, uint8_t reg, uint32_t val) 2108*9172SFei.Feng@Sun.COM { 2109*9172SFei.Feng@Sun.COM uint32_t tmp; 2110*9172SFei.Feng@Sun.COM int ntries; 2111*9172SFei.Feng@Sun.COM 2112*9172SFei.Feng@Sun.COM for (ntries = 0; ntries < 100; ntries++) { 2113*9172SFei.Feng@Sun.COM if (!(RT2860_READ(sc, RT2860_RF_CSR_CFG0) & RT2860_RF_REG_CTRL)) 2114*9172SFei.Feng@Sun.COM break; 2115*9172SFei.Feng@Sun.COM DELAY(1); 2116*9172SFei.Feng@Sun.COM } 2117*9172SFei.Feng@Sun.COM if (ntries == 100) { 2118*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_FW, "rwn: rwn_init()" 2119*9172SFei.Feng@Sun.COM "could not write to RF\n"); 2120*9172SFei.Feng@Sun.COM return; 2121*9172SFei.Feng@Sun.COM } 2122*9172SFei.Feng@Sun.COM 2123*9172SFei.Feng@Sun.COM /* RF registers are 24-bit on the RT2860 */ 2124*9172SFei.Feng@Sun.COM tmp = RT2860_RF_REG_CTRL | 24 << RT2860_RF_REG_WIDTH_SHIFT | 2125*9172SFei.Feng@Sun.COM (val & 0x3fffff) << 2 | (reg & 3); 2126*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_RF_CSR_CFG0, tmp); 2127*9172SFei.Feng@Sun.COM } 2128*9172SFei.Feng@Sun.COM 2129*9172SFei.Feng@Sun.COM static void 2130*9172SFei.Feng@Sun.COM rt2860_select_chan_group(struct rt2860_softc *sc, int group) 2131*9172SFei.Feng@Sun.COM { 2132*9172SFei.Feng@Sun.COM uint32_t tmp; 2133*9172SFei.Feng@Sun.COM 2134*9172SFei.Feng@Sun.COM rt2860_mcu_bbp_write(sc, 62, 0x37 - sc->lna[group]); 2135*9172SFei.Feng@Sun.COM rt2860_mcu_bbp_write(sc, 63, 0x37 - sc->lna[group]); 2136*9172SFei.Feng@Sun.COM rt2860_mcu_bbp_write(sc, 64, 0x37 - sc->lna[group]); 2137*9172SFei.Feng@Sun.COM rt2860_mcu_bbp_write(sc, 82, (group == 0) ? 0x62 : 0xf2); 2138*9172SFei.Feng@Sun.COM 2139*9172SFei.Feng@Sun.COM tmp = RT2860_READ(sc, RT2860_TX_BAND_CFG); 2140*9172SFei.Feng@Sun.COM tmp &= ~(RT2860_5G_BAND_SEL_N | RT2860_5G_BAND_SEL_P); 2141*9172SFei.Feng@Sun.COM tmp |= (group == 0) ? RT2860_5G_BAND_SEL_N : RT2860_5G_BAND_SEL_P; 2142*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_TX_BAND_CFG, tmp); 2143*9172SFei.Feng@Sun.COM 2144*9172SFei.Feng@Sun.COM /* enable appropriate Power Amplifiers and Low Noise Amplifiers */ 2145*9172SFei.Feng@Sun.COM tmp = RT2860_RFTR_EN | RT2860_TRSW_EN; 2146*9172SFei.Feng@Sun.COM if (group == 0) { /* 2GHz */ 2147*9172SFei.Feng@Sun.COM tmp |= RT2860_PA_PE_G0_EN | RT2860_LNA_PE_G0_EN; 2148*9172SFei.Feng@Sun.COM if (sc->ntxchains > 1) 2149*9172SFei.Feng@Sun.COM tmp |= RT2860_PA_PE_G1_EN; 2150*9172SFei.Feng@Sun.COM if (sc->nrxchains > 1) 2151*9172SFei.Feng@Sun.COM tmp |= RT2860_LNA_PE_G1_EN; 2152*9172SFei.Feng@Sun.COM } else { /* 5GHz */ 2153*9172SFei.Feng@Sun.COM tmp |= RT2860_PA_PE_A0_EN | RT2860_LNA_PE_A0_EN; 2154*9172SFei.Feng@Sun.COM if (sc->ntxchains > 1) 2155*9172SFei.Feng@Sun.COM tmp |= RT2860_PA_PE_A1_EN; 2156*9172SFei.Feng@Sun.COM if (sc->nrxchains > 1) 2157*9172SFei.Feng@Sun.COM tmp |= RT2860_LNA_PE_A1_EN; 2158*9172SFei.Feng@Sun.COM } 2159*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_TX_PIN_CFG, tmp); 2160*9172SFei.Feng@Sun.COM 2161*9172SFei.Feng@Sun.COM rt2860_mcu_bbp_write(sc, 66, 0x2e + sc->lna[group]); 2162*9172SFei.Feng@Sun.COM } 2163*9172SFei.Feng@Sun.COM static void 2164*9172SFei.Feng@Sun.COM rt2860_set_chan(struct rt2860_softc *sc, struct ieee80211_channel *c) 2165*9172SFei.Feng@Sun.COM { 2166*9172SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic; 2167*9172SFei.Feng@Sun.COM const struct rfprog *rfprog = rt2860_rf2850; 2168*9172SFei.Feng@Sun.COM uint_t i, chan, group; 2169*9172SFei.Feng@Sun.COM uint8_t txpow1, txpow2; 2170*9172SFei.Feng@Sun.COM uint32_t r2, r3, r4; 2171*9172SFei.Feng@Sun.COM 2172*9172SFei.Feng@Sun.COM chan = ieee80211_chan2ieee(ic, c); 2173*9172SFei.Feng@Sun.COM if (chan == 0 || chan == IEEE80211_CHAN_ANY) { 2174*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_FW, "Unkonwn channel!\n"); 2175*9172SFei.Feng@Sun.COM return; 2176*9172SFei.Feng@Sun.COM } 2177*9172SFei.Feng@Sun.COM 2178*9172SFei.Feng@Sun.COM /* find the settings for this channel (we know it exists) */ 2179*9172SFei.Feng@Sun.COM for (i = 0; rfprog[i].chan != chan; ) 2180*9172SFei.Feng@Sun.COM i++; 2181*9172SFei.Feng@Sun.COM 2182*9172SFei.Feng@Sun.COM r2 = rfprog[i].r2; 2183*9172SFei.Feng@Sun.COM if (sc->ntxchains == 1) 2184*9172SFei.Feng@Sun.COM r2 |= 1 << 12; /* 1T: disable Tx chain 2 */ 2185*9172SFei.Feng@Sun.COM if (sc->nrxchains == 1) 2186*9172SFei.Feng@Sun.COM r2 |= 1 << 15 | 1 << 4; /* 1R: disable Rx chains 2 & 3 */ 2187*9172SFei.Feng@Sun.COM else if (sc->nrxchains == 2) 2188*9172SFei.Feng@Sun.COM r2 |= 1 << 4; /* 2R: disable Rx chain 3 */ 2189*9172SFei.Feng@Sun.COM 2190*9172SFei.Feng@Sun.COM /* use Tx power values from EEPROM */ 2191*9172SFei.Feng@Sun.COM txpow1 = sc->txpow1[i]; 2192*9172SFei.Feng@Sun.COM txpow2 = sc->txpow2[i]; 2193*9172SFei.Feng@Sun.COM if (IEEE80211_IS_CHAN_5GHZ(c)) { 2194*9172SFei.Feng@Sun.COM txpow1 = txpow1 << 1 | 1; 2195*9172SFei.Feng@Sun.COM txpow2 = txpow2 << 1 | 1; 2196*9172SFei.Feng@Sun.COM } 2197*9172SFei.Feng@Sun.COM r3 = rfprog[i].r3 | txpow1 << 7; 2198*9172SFei.Feng@Sun.COM r4 = rfprog[i].r4 | sc->freq << 13 | txpow2 << 4; 2199*9172SFei.Feng@Sun.COM 2200*9172SFei.Feng@Sun.COM rt2860_rf_write(sc, RAL_RF1, rfprog[i].r1); 2201*9172SFei.Feng@Sun.COM rt2860_rf_write(sc, RAL_RF2, r2); 2202*9172SFei.Feng@Sun.COM rt2860_rf_write(sc, RAL_RF3, r3); 2203*9172SFei.Feng@Sun.COM rt2860_rf_write(sc, RAL_RF4, r4); 2204*9172SFei.Feng@Sun.COM 2205*9172SFei.Feng@Sun.COM DELAY(200); 2206*9172SFei.Feng@Sun.COM 2207*9172SFei.Feng@Sun.COM rt2860_rf_write(sc, RAL_RF1, rfprog[i].r1); 2208*9172SFei.Feng@Sun.COM rt2860_rf_write(sc, RAL_RF2, r2); 2209*9172SFei.Feng@Sun.COM rt2860_rf_write(sc, RAL_RF3, r3 | 1); 2210*9172SFei.Feng@Sun.COM rt2860_rf_write(sc, RAL_RF4, r4); 2211*9172SFei.Feng@Sun.COM 2212*9172SFei.Feng@Sun.COM DELAY(200); 2213*9172SFei.Feng@Sun.COM 2214*9172SFei.Feng@Sun.COM rt2860_rf_write(sc, RAL_RF1, rfprog[i].r1); 2215*9172SFei.Feng@Sun.COM rt2860_rf_write(sc, RAL_RF2, r2); 2216*9172SFei.Feng@Sun.COM rt2860_rf_write(sc, RAL_RF3, r3); 2217*9172SFei.Feng@Sun.COM rt2860_rf_write(sc, RAL_RF4, r4); 2218*9172SFei.Feng@Sun.COM 2219*9172SFei.Feng@Sun.COM /* 802.11a uses a 16 microseconds short interframe space */ 2220*9172SFei.Feng@Sun.COM sc->sifs = IEEE80211_IS_CHAN_5GHZ(c) ? 16 : 10; 2221*9172SFei.Feng@Sun.COM 2222*9172SFei.Feng@Sun.COM /* determine channel group */ 2223*9172SFei.Feng@Sun.COM if (chan <= 14) 2224*9172SFei.Feng@Sun.COM group = 0; 2225*9172SFei.Feng@Sun.COM else if (chan <= 64) 2226*9172SFei.Feng@Sun.COM group = 1; 2227*9172SFei.Feng@Sun.COM else if (chan <= 128) 2228*9172SFei.Feng@Sun.COM group = 2; 2229*9172SFei.Feng@Sun.COM else 2230*9172SFei.Feng@Sun.COM group = 3; 2231*9172SFei.Feng@Sun.COM 2232*9172SFei.Feng@Sun.COM /* XXX necessary only when group has changed! */ 2233*9172SFei.Feng@Sun.COM rt2860_select_chan_group(sc, group); 2234*9172SFei.Feng@Sun.COM 2235*9172SFei.Feng@Sun.COM DELAY(1000); 2236*9172SFei.Feng@Sun.COM } 2237*9172SFei.Feng@Sun.COM 2238*9172SFei.Feng@Sun.COM static void 2239*9172SFei.Feng@Sun.COM rt2860_updateprot(struct ieee80211com *ic) 2240*9172SFei.Feng@Sun.COM { 2241*9172SFei.Feng@Sun.COM struct rt2860_softc *sc = (struct rt2860_softc *)ic; 2242*9172SFei.Feng@Sun.COM uint32_t tmp; 2243*9172SFei.Feng@Sun.COM 2244*9172SFei.Feng@Sun.COM tmp = RT2860_RTSTH_EN | RT2860_PROT_NAV_SHORT | RT2860_TXOP_ALLOW_ALL; 2245*9172SFei.Feng@Sun.COM /* setup protection frame rate (MCS code) */ 2246*9172SFei.Feng@Sun.COM tmp |= (ic->ic_curmode == IEEE80211_MODE_11A) ? 0 : 3; 2247*9172SFei.Feng@Sun.COM 2248*9172SFei.Feng@Sun.COM /* CCK frames don't require protection */ 2249*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_CCK_PROT_CFG, tmp); 2250*9172SFei.Feng@Sun.COM 2251*9172SFei.Feng@Sun.COM if (ic->ic_flags & IEEE80211_F_USEPROT) { 2252*9172SFei.Feng@Sun.COM if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) 2253*9172SFei.Feng@Sun.COM tmp |= RT2860_PROT_CTRL_RTS_CTS; 2254*9172SFei.Feng@Sun.COM else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) 2255*9172SFei.Feng@Sun.COM tmp |= RT2860_PROT_CTRL_CTS; 2256*9172SFei.Feng@Sun.COM } 2257*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_OFDM_PROT_CFG, tmp); 2258*9172SFei.Feng@Sun.COM } 2259*9172SFei.Feng@Sun.COM 2260*9172SFei.Feng@Sun.COM static void 2261*9172SFei.Feng@Sun.COM rt2860_set_leds(struct rt2860_softc *sc, uint16_t which) 2262*9172SFei.Feng@Sun.COM { 2263*9172SFei.Feng@Sun.COM (void) rt2860_mcu_cmd(sc, RT2860_MCU_CMD_LEDS, 2264*9172SFei.Feng@Sun.COM which | (sc->leds & 0x7f)); 2265*9172SFei.Feng@Sun.COM } 2266*9172SFei.Feng@Sun.COM 2267*9172SFei.Feng@Sun.COM static int 2268*9172SFei.Feng@Sun.COM rt2860_init(struct rt2860_softc *sc) 2269*9172SFei.Feng@Sun.COM { 2270*9172SFei.Feng@Sun.COM #define N(a) (sizeof (a) / sizeof ((a)[0])) 2271*9172SFei.Feng@Sun.COM struct ieee80211com *ic; 2272*9172SFei.Feng@Sun.COM int i, err, qid, ridx, ntries; 2273*9172SFei.Feng@Sun.COM uint8_t bbp1, bbp3; 2274*9172SFei.Feng@Sun.COM uint32_t tmp; 2275*9172SFei.Feng@Sun.COM 2276*9172SFei.Feng@Sun.COM ic = &sc->sc_ic; 2277*9172SFei.Feng@Sun.COM 2278*9172SFei.Feng@Sun.COM rt2860_stop(sc); 2279*9172SFei.Feng@Sun.COM tmp = RT2860_READ(sc, RT2860_WPDMA_GLO_CFG); 2280*9172SFei.Feng@Sun.COM tmp &= 0xff0; 2281*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_WPDMA_GLO_CFG, tmp | RT2860_TX_WB_DDONE); 2282*9172SFei.Feng@Sun.COM 2283*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_WPDMA_RST_IDX, 0xffffffff); 2284*9172SFei.Feng@Sun.COM 2285*9172SFei.Feng@Sun.COM /* PBF hardware reset */ 2286*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_SYS_CTRL, 0xe1f); 2287*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_SYS_CTRL, 0xe00); 2288*9172SFei.Feng@Sun.COM 2289*9172SFei.Feng@Sun.COM if (!(sc->sc_flags & RT2860_FWLOADED)) { 2290*9172SFei.Feng@Sun.COM if ((err = rt2860_load_microcode(sc)) != 0) { 2291*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_init(): " 2292*9172SFei.Feng@Sun.COM "could not load 8051 microcode\n"); 2293*9172SFei.Feng@Sun.COM rt2860_stop(sc); 2294*9172SFei.Feng@Sun.COM return (err); 2295*9172SFei.Feng@Sun.COM } 2296*9172SFei.Feng@Sun.COM RT2860_GLOCK(sc); 2297*9172SFei.Feng@Sun.COM sc->sc_flags |= RT2860_FWLOADED; 2298*9172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 2299*9172SFei.Feng@Sun.COM } 2300*9172SFei.Feng@Sun.COM 2301*9172SFei.Feng@Sun.COM rt2860_set_macaddr(sc, ic->ic_macaddr); 2302*9172SFei.Feng@Sun.COM 2303*9172SFei.Feng@Sun.COM /* init Tx power for all Tx rates (from EEPROM) */ 2304*9172SFei.Feng@Sun.COM for (ridx = 0; ridx < 5; ridx++) { 2305*9172SFei.Feng@Sun.COM if (sc->txpow20mhz[ridx] == 0xffffffff) 2306*9172SFei.Feng@Sun.COM continue; 2307*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_TX_PWR_CFG(ridx), sc->txpow20mhz[ridx]); 2308*9172SFei.Feng@Sun.COM } 2309*9172SFei.Feng@Sun.COM 2310*9172SFei.Feng@Sun.COM for (ntries = 0; ntries < 100; ntries++) { 2311*9172SFei.Feng@Sun.COM tmp = RT2860_READ(sc, RT2860_WPDMA_GLO_CFG); 2312*9172SFei.Feng@Sun.COM if ((tmp & (RT2860_TX_DMA_BUSY | RT2860_RX_DMA_BUSY)) == 0) 2313*9172SFei.Feng@Sun.COM break; 2314*9172SFei.Feng@Sun.COM DELAY(1000); 2315*9172SFei.Feng@Sun.COM } 2316*9172SFei.Feng@Sun.COM if (ntries == 100) { 2317*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_DMA, "rwn: rt2860_init():" 2318*9172SFei.Feng@Sun.COM "timeout waiting for DMA engine\n"); 2319*9172SFei.Feng@Sun.COM rt2860_stop(sc); 2320*9172SFei.Feng@Sun.COM return (ETIMEDOUT); 2321*9172SFei.Feng@Sun.COM } 2322*9172SFei.Feng@Sun.COM tmp &= 0xff0; 2323*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_WPDMA_GLO_CFG, tmp | RT2860_TX_WB_DDONE); 2324*9172SFei.Feng@Sun.COM 2325*9172SFei.Feng@Sun.COM /* reset Rx ring and all 6 Tx rings */ 2326*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_WPDMA_RST_IDX, 0x1003f); 2327*9172SFei.Feng@Sun.COM 2328*9172SFei.Feng@Sun.COM /* PBF hardware reset */ 2329*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_SYS_CTRL, 0xe1f); 2330*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_SYS_CTRL, 0xe00); 2331*9172SFei.Feng@Sun.COM 2332*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_MAC_SYS_CTRL, 2333*9172SFei.Feng@Sun.COM RT2860_BBP_HRST | RT2860_MAC_SRST); 2334*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_MAC_SYS_CTRL, 0); 2335*9172SFei.Feng@Sun.COM 2336*9172SFei.Feng@Sun.COM for (i = 0; i < N(rt2860_def_mac); i++) 2337*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, rt2860_def_mac[i].reg, rt2860_def_mac[i].val); 2338*9172SFei.Feng@Sun.COM 2339*9172SFei.Feng@Sun.COM /* wait while MAC is busy */ 2340*9172SFei.Feng@Sun.COM for (ntries = 0; ntries < 100; ntries++) { 2341*9172SFei.Feng@Sun.COM if (!(RT2860_READ(sc, RT2860_MAC_STATUS_REG) & 2342*9172SFei.Feng@Sun.COM (RT2860_RX_STATUS_BUSY | RT2860_TX_STATUS_BUSY))) 2343*9172SFei.Feng@Sun.COM break; 2344*9172SFei.Feng@Sun.COM DELAY(1000); 2345*9172SFei.Feng@Sun.COM } 2346*9172SFei.Feng@Sun.COM if (ntries == 100) { 2347*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_FW, "rwn: rt2860_init():" 2348*9172SFei.Feng@Sun.COM "timeout waiting for MAC\n"); 2349*9172SFei.Feng@Sun.COM rt2860_stop(sc); 2350*9172SFei.Feng@Sun.COM return (ETIMEDOUT); 2351*9172SFei.Feng@Sun.COM } 2352*9172SFei.Feng@Sun.COM 2353*9172SFei.Feng@Sun.COM /* clear Host to MCU mailbox */ 2354*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_H2M_BBPAGENT, 0); 2355*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_H2M_MAILBOX, 0); 2356*9172SFei.Feng@Sun.COM 2357*9172SFei.Feng@Sun.COM if ((err = rt2860_bbp_init(sc)) != 0) { 2358*9172SFei.Feng@Sun.COM rt2860_stop(sc); 2359*9172SFei.Feng@Sun.COM return (err); 2360*9172SFei.Feng@Sun.COM } 2361*9172SFei.Feng@Sun.COM 2362*9172SFei.Feng@Sun.COM /* init Tx rings (4 EDCAs + HCCA + Mgt) */ 2363*9172SFei.Feng@Sun.COM for (qid = 0; qid < 6; qid++) { 2364*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_TX_BASE_PTR(qid), sc->txq[qid].paddr); 2365*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_TX_MAX_CNT(qid), RT2860_TX_RING_COUNT); 2366*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_TX_CTX_IDX(qid), 0); 2367*9172SFei.Feng@Sun.COM } 2368*9172SFei.Feng@Sun.COM 2369*9172SFei.Feng@Sun.COM /* init Rx ring */ 2370*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_RX_BASE_PTR, sc->rxq.paddr); 2371*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_RX_MAX_CNT, RT2860_RX_RING_COUNT); 2372*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_RX_CALC_IDX, RT2860_RX_RING_COUNT - 1); 2373*9172SFei.Feng@Sun.COM 2374*9172SFei.Feng@Sun.COM /* setup maximum buffer sizes */ 2375*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_MAX_LEN_CFG, 1 << 12 | 2376*9172SFei.Feng@Sun.COM (sc->sc_dmabuf_size - sizeof (struct rt2860_rxwi) - 2)); 2377*9172SFei.Feng@Sun.COM 2378*9172SFei.Feng@Sun.COM for (ntries = 0; ntries < 100; ntries++) { 2379*9172SFei.Feng@Sun.COM tmp = RT2860_READ(sc, RT2860_WPDMA_GLO_CFG); 2380*9172SFei.Feng@Sun.COM if ((tmp & (RT2860_TX_DMA_BUSY | RT2860_RX_DMA_BUSY)) == 0) 2381*9172SFei.Feng@Sun.COM break; 2382*9172SFei.Feng@Sun.COM DELAY(1000); 2383*9172SFei.Feng@Sun.COM } 2384*9172SFei.Feng@Sun.COM if (ntries == 100) { 2385*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_DMA, "rwn: rt2860_init():" 2386*9172SFei.Feng@Sun.COM "timeout waiting for DMA engine\n"); 2387*9172SFei.Feng@Sun.COM rt2860_stop(sc); 2388*9172SFei.Feng@Sun.COM return (ETIMEDOUT); 2389*9172SFei.Feng@Sun.COM } 2390*9172SFei.Feng@Sun.COM tmp &= 0xff0; 2391*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_WPDMA_GLO_CFG, tmp | RT2860_TX_WB_DDONE); 2392*9172SFei.Feng@Sun.COM 2393*9172SFei.Feng@Sun.COM /* disable interrupts mitigation */ 2394*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_DELAY_INT_CFG, 0); 2395*9172SFei.Feng@Sun.COM 2396*9172SFei.Feng@Sun.COM /* write vendor-specific BBP values (from EEPROM) */ 2397*9172SFei.Feng@Sun.COM for (i = 0; i < 8; i++) { 2398*9172SFei.Feng@Sun.COM if (sc->bbp[i].reg == 0 || sc->bbp[i].reg == 0xff) 2399*9172SFei.Feng@Sun.COM continue; 2400*9172SFei.Feng@Sun.COM rt2860_mcu_bbp_write(sc, sc->bbp[i].reg, sc->bbp[i].val); 2401*9172SFei.Feng@Sun.COM } 2402*9172SFei.Feng@Sun.COM 2403*9172SFei.Feng@Sun.COM /* send LEDs operating mode to microcontroller */ 2404*9172SFei.Feng@Sun.COM (void) rt2860_mcu_cmd(sc, RT2860_MCU_CMD_LED1, sc->led[0]); 2405*9172SFei.Feng@Sun.COM (void) rt2860_mcu_cmd(sc, RT2860_MCU_CMD_LED2, sc->led[1]); 2406*9172SFei.Feng@Sun.COM (void) rt2860_mcu_cmd(sc, RT2860_MCU_CMD_LED3, sc->led[2]); 2407*9172SFei.Feng@Sun.COM 2408*9172SFei.Feng@Sun.COM /* disable non-existing Rx chains */ 2409*9172SFei.Feng@Sun.COM bbp3 = rt2860_mcu_bbp_read(sc, 3); 2410*9172SFei.Feng@Sun.COM bbp3 &= ~(1 << 3 | 1 << 4); 2411*9172SFei.Feng@Sun.COM if (sc->nrxchains == 2) 2412*9172SFei.Feng@Sun.COM bbp3 |= 1 << 3; 2413*9172SFei.Feng@Sun.COM else if (sc->nrxchains == 3) 2414*9172SFei.Feng@Sun.COM bbp3 |= 1 << 4; 2415*9172SFei.Feng@Sun.COM rt2860_mcu_bbp_write(sc, 3, bbp3); 2416*9172SFei.Feng@Sun.COM 2417*9172SFei.Feng@Sun.COM /* disable non-existing Tx chains */ 2418*9172SFei.Feng@Sun.COM bbp1 = rt2860_mcu_bbp_read(sc, 1); 2419*9172SFei.Feng@Sun.COM if (sc->ntxchains == 1) 2420*9172SFei.Feng@Sun.COM bbp1 &= ~(1 << 3 | 1 << 4); 2421*9172SFei.Feng@Sun.COM rt2860_mcu_bbp_write(sc, 1, bbp1); 2422*9172SFei.Feng@Sun.COM 2423*9172SFei.Feng@Sun.COM /* select default channel */ 2424*9172SFei.Feng@Sun.COM rt2860_set_chan(sc, ic->ic_curchan); 2425*9172SFei.Feng@Sun.COM 2426*9172SFei.Feng@Sun.COM /* XXX not clear what the following 8051 command does.. */ 2427*9172SFei.Feng@Sun.COM (void) rt2860_mcu_cmd(sc, RT2860_MCU_CMD_BOOT, 0); 2428*9172SFei.Feng@Sun.COM 2429*9172SFei.Feng@Sun.COM /* set RTS threshold */ 2430*9172SFei.Feng@Sun.COM tmp = RT2860_READ(sc, RT2860_TX_RTS_CFG); 2431*9172SFei.Feng@Sun.COM tmp &= ~0xffff00; 2432*9172SFei.Feng@Sun.COM tmp |= ic->ic_rtsthreshold << 8; 2433*9172SFei.Feng@Sun.COM 2434*9172SFei.Feng@Sun.COM /* setup initial protection mode */ 2435*9172SFei.Feng@Sun.COM sc->sc_ic_flags = ic->ic_flags; 2436*9172SFei.Feng@Sun.COM rt2860_updateprot(ic); 2437*9172SFei.Feng@Sun.COM 2438*9172SFei.Feng@Sun.COM /* enable Tx/Rx DMA engine */ 2439*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_MAC_SYS_CTRL, RT2860_MAC_TX_EN); 2440*9172SFei.Feng@Sun.COM for (ntries = 0; ntries < 200; ntries++) { 2441*9172SFei.Feng@Sun.COM tmp = RT2860_READ(sc, RT2860_WPDMA_GLO_CFG); 2442*9172SFei.Feng@Sun.COM if ((tmp & (RT2860_TX_DMA_BUSY | RT2860_RX_DMA_BUSY)) == 0) 2443*9172SFei.Feng@Sun.COM break; 2444*9172SFei.Feng@Sun.COM DELAY(1000); 2445*9172SFei.Feng@Sun.COM } 2446*9172SFei.Feng@Sun.COM if (ntries == 200) { 2447*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_DMA, "rwn: rt2860_int():" 2448*9172SFei.Feng@Sun.COM "timeout waiting for DMA engine\n"); 2449*9172SFei.Feng@Sun.COM rt2860_stop(sc); 2450*9172SFei.Feng@Sun.COM return (ETIMEDOUT); 2451*9172SFei.Feng@Sun.COM } 2452*9172SFei.Feng@Sun.COM 2453*9172SFei.Feng@Sun.COM DELAY(50); 2454*9172SFei.Feng@Sun.COM 2455*9172SFei.Feng@Sun.COM tmp |= RT2860_TX_WB_DDONE | RT2860_RX_DMA_EN | RT2860_TX_DMA_EN | 2456*9172SFei.Feng@Sun.COM RT2860_WPDMA_BT_SIZE64 << RT2860_WPDMA_BT_SIZE_SHIFT; 2457*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_WPDMA_GLO_CFG, tmp); 2458*9172SFei.Feng@Sun.COM 2459*9172SFei.Feng@Sun.COM /* turn radio LED on */ 2460*9172SFei.Feng@Sun.COM rt2860_set_leds(sc, RT2860_LED_RADIO); 2461*9172SFei.Feng@Sun.COM 2462*9172SFei.Feng@Sun.COM /* set Rx filter */ 2463*9172SFei.Feng@Sun.COM tmp = RT2860_DROP_CRC_ERR | RT2860_DROP_PHY_ERR; 2464*9172SFei.Feng@Sun.COM if (ic->ic_opmode != IEEE80211_M_MONITOR) { 2465*9172SFei.Feng@Sun.COM tmp |= RT2860_DROP_UC_NOME | RT2860_DROP_DUPL | 2466*9172SFei.Feng@Sun.COM RT2860_DROP_CTS | RT2860_DROP_BA | RT2860_DROP_ACK | 2467*9172SFei.Feng@Sun.COM RT2860_DROP_VER_ERR | RT2860_DROP_CTRL_RSV | 2468*9172SFei.Feng@Sun.COM RT2860_DROP_CFACK | RT2860_DROP_CFEND; 2469*9172SFei.Feng@Sun.COM if (ic->ic_opmode == IEEE80211_M_STA) 2470*9172SFei.Feng@Sun.COM tmp |= RT2860_DROP_RTS | RT2860_DROP_PSPOLL; 2471*9172SFei.Feng@Sun.COM } 2472*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_RX_FILTR_CFG, tmp); 2473*9172SFei.Feng@Sun.COM 2474*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_MAC_SYS_CTRL, 2475*9172SFei.Feng@Sun.COM RT2860_MAC_RX_EN | RT2860_MAC_TX_EN); 2476*9172SFei.Feng@Sun.COM 2477*9172SFei.Feng@Sun.COM /* clear pending interrupts */ 2478*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_INT_STATUS, 0xffffffff); 2479*9172SFei.Feng@Sun.COM /* enable interrupts */ 2480*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_INT_MASK, 0x3fffc); 2481*9172SFei.Feng@Sun.COM 2482*9172SFei.Feng@Sun.COM if (sc->sc_flags & RT2860_ADVANCED_PS) 2483*9172SFei.Feng@Sun.COM (void) rt2860_mcu_cmd(sc, RT2860_MCU_CMD_PSLEVEL, sc->pslevel); 2484*9172SFei.Feng@Sun.COM 2485*9172SFei.Feng@Sun.COM return (DDI_SUCCESS); 2486*9172SFei.Feng@Sun.COM } 2487*9172SFei.Feng@Sun.COM 2488*9172SFei.Feng@Sun.COM static int 2489*9172SFei.Feng@Sun.COM rt2860_quiesce(dev_info_t *dip) 2490*9172SFei.Feng@Sun.COM { 2491*9172SFei.Feng@Sun.COM struct rt2860_softc *sc; 2492*9172SFei.Feng@Sun.COM 2493*9172SFei.Feng@Sun.COM sc = ddi_get_soft_state(rt2860_soft_state_p, ddi_get_instance(dip)); 2494*9172SFei.Feng@Sun.COM if (sc == NULL) 2495*9172SFei.Feng@Sun.COM return (DDI_FAILURE); 2496*9172SFei.Feng@Sun.COM 2497*9172SFei.Feng@Sun.COM #ifdef DEBUG 2498*9172SFei.Feng@Sun.COM rt2860_dbg_flags = 0; 2499*9172SFei.Feng@Sun.COM #endif 2500*9172SFei.Feng@Sun.COM 2501*9172SFei.Feng@Sun.COM /* 2502*9172SFei.Feng@Sun.COM * No more blocking is allowed while we are in quiesce(9E) entry point 2503*9172SFei.Feng@Sun.COM */ 2504*9172SFei.Feng@Sun.COM sc->sc_flags |= RT2860_F_QUIESCE; 2505*9172SFei.Feng@Sun.COM 2506*9172SFei.Feng@Sun.COM /* 2507*9172SFei.Feng@Sun.COM * Disable and mask all interrupts 2508*9172SFei.Feng@Sun.COM */ 2509*9172SFei.Feng@Sun.COM rt2860_stop(sc); 2510*9172SFei.Feng@Sun.COM return (DDI_SUCCESS); 2511*9172SFei.Feng@Sun.COM } 2512*9172SFei.Feng@Sun.COM 2513*9172SFei.Feng@Sun.COM static void 2514*9172SFei.Feng@Sun.COM rt2860_stop(struct rt2860_softc *sc) 2515*9172SFei.Feng@Sun.COM { 2516*9172SFei.Feng@Sun.COM int qid; 2517*9172SFei.Feng@Sun.COM uint32_t tmp; 2518*9172SFei.Feng@Sun.COM 2519*9172SFei.Feng@Sun.COM /* by pass if it's quiesced */ 2520*9172SFei.Feng@Sun.COM if (!(sc->sc_flags & RT2860_F_QUIESCE)) 2521*9172SFei.Feng@Sun.COM RT2860_GLOCK(sc); 2522*9172SFei.Feng@Sun.COM if (sc->sc_flags == RT2860_F_RUNNING) 2523*9172SFei.Feng@Sun.COM rt2860_set_leds(sc, 0); /* turn all LEDs off */ 2524*9172SFei.Feng@Sun.COM sc->sc_tx_timer = 0; 2525*9172SFei.Feng@Sun.COM /* by pass if it's quiesced */ 2526*9172SFei.Feng@Sun.COM if (!(sc->sc_flags & RT2860_F_QUIESCE)) 2527*9172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 2528*9172SFei.Feng@Sun.COM 2529*9172SFei.Feng@Sun.COM /* clear RX WCID search table */ 2530*9172SFei.Feng@Sun.COM rt2860_set_region_4(sc, RT2860_WCID_ENTRY(0), 0, 512); 2531*9172SFei.Feng@Sun.COM /* clear pairwise key table */ 2532*9172SFei.Feng@Sun.COM rt2860_set_region_4(sc, RT2860_PKEY(0), 0, 2048); 2533*9172SFei.Feng@Sun.COM /* clear IV/EIV table */ 2534*9172SFei.Feng@Sun.COM rt2860_set_region_4(sc, RT2860_IVEIV(0), 0, 512); 2535*9172SFei.Feng@Sun.COM /* clear WCID attribute table */ 2536*9172SFei.Feng@Sun.COM rt2860_set_region_4(sc, RT2860_WCID_ATTR(0), 0, 256); 2537*9172SFei.Feng@Sun.COM /* clear shared key table */ 2538*9172SFei.Feng@Sun.COM rt2860_set_region_4(sc, RT2860_SKEY(0, 0), 0, 8 * 32); 2539*9172SFei.Feng@Sun.COM /* clear shared key mode */ 2540*9172SFei.Feng@Sun.COM rt2860_set_region_4(sc, RT2860_SKEY_MODE_0_7, 0, 4); 2541*9172SFei.Feng@Sun.COM 2542*9172SFei.Feng@Sun.COM /* disable interrupts */ 2543*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_INT_MASK, 0); 2544*9172SFei.Feng@Sun.COM 2545*9172SFei.Feng@Sun.COM /* disable Rx */ 2546*9172SFei.Feng@Sun.COM tmp = RT2860_READ(sc, RT2860_MAC_SYS_CTRL); 2547*9172SFei.Feng@Sun.COM tmp &= ~(RT2860_MAC_RX_EN | RT2860_MAC_TX_EN); 2548*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_MAC_SYS_CTRL, tmp); 2549*9172SFei.Feng@Sun.COM 2550*9172SFei.Feng@Sun.COM /* reset adapter */ 2551*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_MAC_SYS_CTRL, 2552*9172SFei.Feng@Sun.COM RT2860_BBP_HRST | RT2860_MAC_SRST); 2553*9172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_MAC_SYS_CTRL, 0); 2554*9172SFei.Feng@Sun.COM 2555*9172SFei.Feng@Sun.COM /* reset Tx and Rx rings (and reclaim TXWIs) */ 2556*9172SFei.Feng@Sun.COM for (qid = 0; qid < 6; qid++) 2557*9172SFei.Feng@Sun.COM rt2860_reset_tx_ring(sc, &sc->txq[qid]); 2558*9172SFei.Feng@Sun.COM rt2860_reset_rx_ring(sc, &sc->rxq); 2559*9172SFei.Feng@Sun.COM 2560*9172SFei.Feng@Sun.COM /* by pass if it's quiesced */ 2561*9172SFei.Feng@Sun.COM if (!(sc->sc_flags & RT2860_F_QUIESCE)) 2562*9172SFei.Feng@Sun.COM RT2860_GLOCK(sc); 2563*9172SFei.Feng@Sun.COM sc->sc_flags &= ~RT2860_UPD_BEACON; 2564*9172SFei.Feng@Sun.COM /* by pass if it's quiesced */ 2565*9172SFei.Feng@Sun.COM if (!(sc->sc_flags & RT2860_F_QUIESCE)) 2566*9172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 2567*9172SFei.Feng@Sun.COM } 2568*9172SFei.Feng@Sun.COM 2569*9172SFei.Feng@Sun.COM static int 2570*9172SFei.Feng@Sun.COM rt2860_m_start(void *arg) 2571*9172SFei.Feng@Sun.COM { 2572*9172SFei.Feng@Sun.COM struct rt2860_softc *sc = (struct rt2860_softc *)arg; 2573*9172SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic; 2574*9172SFei.Feng@Sun.COM int err; 2575*9172SFei.Feng@Sun.COM 2576*9172SFei.Feng@Sun.COM err = rt2860_init(sc); 2577*9172SFei.Feng@Sun.COM if (err != DDI_SUCCESS) { 2578*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_m_start():" 2579*9172SFei.Feng@Sun.COM "Hardware initialization failed\n"); 2580*9172SFei.Feng@Sun.COM goto fail1; 2581*9172SFei.Feng@Sun.COM } 2582*9172SFei.Feng@Sun.COM 2583*9172SFei.Feng@Sun.COM ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 2584*9172SFei.Feng@Sun.COM 2585*9172SFei.Feng@Sun.COM RT2860_GLOCK(sc); 2586*9172SFei.Feng@Sun.COM sc->sc_flags |= RT2860_F_RUNNING; 2587*9172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 2588*9172SFei.Feng@Sun.COM 2589*9172SFei.Feng@Sun.COM return (err); 2590*9172SFei.Feng@Sun.COM fail1: 2591*9172SFei.Feng@Sun.COM rt2860_stop(sc); 2592*9172SFei.Feng@Sun.COM return (err); 2593*9172SFei.Feng@Sun.COM } 2594*9172SFei.Feng@Sun.COM 2595*9172SFei.Feng@Sun.COM static void 2596*9172SFei.Feng@Sun.COM rt2860_m_stop(void *arg) 2597*9172SFei.Feng@Sun.COM { 2598*9172SFei.Feng@Sun.COM struct rt2860_softc *sc = (struct rt2860_softc *)arg; 2599*9172SFei.Feng@Sun.COM 2600*9172SFei.Feng@Sun.COM (void) rt2860_stop(sc); 2601*9172SFei.Feng@Sun.COM 2602*9172SFei.Feng@Sun.COM ieee80211_new_state(&sc->sc_ic, IEEE80211_S_INIT, -1); 2603*9172SFei.Feng@Sun.COM 2604*9172SFei.Feng@Sun.COM RT2860_GLOCK(sc); 2605*9172SFei.Feng@Sun.COM sc->sc_flags &= ~RT2860_F_RUNNING; 2606*9172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 2607*9172SFei.Feng@Sun.COM } 2608*9172SFei.Feng@Sun.COM 2609*9172SFei.Feng@Sun.COM static void 2610*9172SFei.Feng@Sun.COM rt2860_m_ioctl(void* arg, queue_t *wq, mblk_t *mp) 2611*9172SFei.Feng@Sun.COM { 2612*9172SFei.Feng@Sun.COM struct rt2860_softc *sc = (struct rt2860_softc *)arg; 2613*9172SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic; 2614*9172SFei.Feng@Sun.COM int err; 2615*9172SFei.Feng@Sun.COM 2616*9172SFei.Feng@Sun.COM err = ieee80211_ioctl(ic, wq, mp); 2617*9172SFei.Feng@Sun.COM RT2860_GLOCK(sc); 2618*9172SFei.Feng@Sun.COM if (err == ENETRESET) { 2619*9172SFei.Feng@Sun.COM if (ic->ic_des_esslen) { 2620*9172SFei.Feng@Sun.COM if (RT2860_IS_RUNNING(sc)) { 2621*9172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 2622*9172SFei.Feng@Sun.COM (void) rt2860_init(sc); 2623*9172SFei.Feng@Sun.COM (void) ieee80211_new_state(ic, 2624*9172SFei.Feng@Sun.COM IEEE80211_S_SCAN, -1); 2625*9172SFei.Feng@Sun.COM RT2860_GLOCK(sc); 2626*9172SFei.Feng@Sun.COM } 2627*9172SFei.Feng@Sun.COM } 2628*9172SFei.Feng@Sun.COM } 2629*9172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 2630*9172SFei.Feng@Sun.COM } 2631*9172SFei.Feng@Sun.COM 2632*9172SFei.Feng@Sun.COM /* 2633*9172SFei.Feng@Sun.COM * Call back function for get/set proporty 2634*9172SFei.Feng@Sun.COM */ 2635*9172SFei.Feng@Sun.COM static int 2636*9172SFei.Feng@Sun.COM rt2860_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, 2637*9172SFei.Feng@Sun.COM uint_t pr_flags, uint_t wldp_length, void *wldp_buf, uint_t *perm) 2638*9172SFei.Feng@Sun.COM { 2639*9172SFei.Feng@Sun.COM struct rt2860_softc *sc = (struct rt2860_softc *)arg; 2640*9172SFei.Feng@Sun.COM int err = 0; 2641*9172SFei.Feng@Sun.COM 2642*9172SFei.Feng@Sun.COM err = ieee80211_getprop(&sc->sc_ic, pr_name, wldp_pr_num, 2643*9172SFei.Feng@Sun.COM pr_flags, wldp_length, wldp_buf, perm); 2644*9172SFei.Feng@Sun.COM 2645*9172SFei.Feng@Sun.COM return (err); 2646*9172SFei.Feng@Sun.COM } 2647*9172SFei.Feng@Sun.COM 2648*9172SFei.Feng@Sun.COM static int 2649*9172SFei.Feng@Sun.COM rt2860_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, 2650*9172SFei.Feng@Sun.COM uint_t wldp_length, const void *wldp_buf) 2651*9172SFei.Feng@Sun.COM { 2652*9172SFei.Feng@Sun.COM struct rt2860_softc *sc = (struct rt2860_softc *)arg; 2653*9172SFei.Feng@Sun.COM ieee80211com_t *ic = &sc->sc_ic; 2654*9172SFei.Feng@Sun.COM int err; 2655*9172SFei.Feng@Sun.COM 2656*9172SFei.Feng@Sun.COM err = ieee80211_setprop(ic, pr_name, wldp_pr_num, wldp_length, 2657*9172SFei.Feng@Sun.COM wldp_buf); 2658*9172SFei.Feng@Sun.COM RT2860_GLOCK(sc); 2659*9172SFei.Feng@Sun.COM if (err == ENETRESET) { 2660*9172SFei.Feng@Sun.COM if (ic->ic_des_esslen) { 2661*9172SFei.Feng@Sun.COM if (RT2860_IS_RUNNING(sc)) { 2662*9172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 2663*9172SFei.Feng@Sun.COM (void) rt2860_init(sc); 2664*9172SFei.Feng@Sun.COM (void) ieee80211_new_state(ic, 2665*9172SFei.Feng@Sun.COM IEEE80211_S_SCAN, -1); 2666*9172SFei.Feng@Sun.COM RT2860_GLOCK(sc); 2667*9172SFei.Feng@Sun.COM } 2668*9172SFei.Feng@Sun.COM } 2669*9172SFei.Feng@Sun.COM err = 0; 2670*9172SFei.Feng@Sun.COM } 2671*9172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 2672*9172SFei.Feng@Sun.COM return (err); 2673*9172SFei.Feng@Sun.COM } 2674*9172SFei.Feng@Sun.COM 2675*9172SFei.Feng@Sun.COM static mblk_t * 2676*9172SFei.Feng@Sun.COM rt2860_m_tx(void *arg, mblk_t *mp) 2677*9172SFei.Feng@Sun.COM { 2678*9172SFei.Feng@Sun.COM struct rt2860_softc *sc = (struct rt2860_softc *)arg; 2679*9172SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic; 2680*9172SFei.Feng@Sun.COM mblk_t *next; 2681*9172SFei.Feng@Sun.COM 2682*9172SFei.Feng@Sun.COM if (RT2860_IS_SUSPEND(sc)) { 2683*9172SFei.Feng@Sun.COM freemsgchain(mp); 2684*9172SFei.Feng@Sun.COM return (NULL); 2685*9172SFei.Feng@Sun.COM } 2686*9172SFei.Feng@Sun.COM 2687*9172SFei.Feng@Sun.COM /* 2688*9172SFei.Feng@Sun.COM * No data frames go out unless we're associated; this 2689*9172SFei.Feng@Sun.COM * should not happen as the 802.11 layer does not enable 2690*9172SFei.Feng@Sun.COM * the xmit queue until we enter the RUN state. 2691*9172SFei.Feng@Sun.COM */ 2692*9172SFei.Feng@Sun.COM if (ic->ic_state != IEEE80211_S_RUN) { 2693*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_TX, "rwn: rt2860_tx_data(): " 2694*9172SFei.Feng@Sun.COM "discard, state %u\n", ic->ic_state); 2695*9172SFei.Feng@Sun.COM freemsgchain(mp); 2696*9172SFei.Feng@Sun.COM return (NULL); 2697*9172SFei.Feng@Sun.COM } 2698*9172SFei.Feng@Sun.COM 2699*9172SFei.Feng@Sun.COM while (mp != NULL) { 2700*9172SFei.Feng@Sun.COM next = mp->b_next; 2701*9172SFei.Feng@Sun.COM mp->b_next = NULL; 2702*9172SFei.Feng@Sun.COM if (rt2860_send(ic, mp, IEEE80211_FC0_TYPE_DATA) != 2703*9172SFei.Feng@Sun.COM DDI_SUCCESS) { 2704*9172SFei.Feng@Sun.COM mp->b_next = next; 2705*9172SFei.Feng@Sun.COM break; 2706*9172SFei.Feng@Sun.COM } 2707*9172SFei.Feng@Sun.COM mp = next; 2708*9172SFei.Feng@Sun.COM } 2709*9172SFei.Feng@Sun.COM return (mp); 2710*9172SFei.Feng@Sun.COM } 2711*9172SFei.Feng@Sun.COM 2712*9172SFei.Feng@Sun.COM /*ARGSUSED*/ 2713*9172SFei.Feng@Sun.COM static int 2714*9172SFei.Feng@Sun.COM rt2860_m_unicst(void *arg, const uint8_t *macaddr) 2715*9172SFei.Feng@Sun.COM { 2716*9172SFei.Feng@Sun.COM return (ENOTSUP); 2717*9172SFei.Feng@Sun.COM } 2718*9172SFei.Feng@Sun.COM 2719*9172SFei.Feng@Sun.COM /*ARGSUSED*/ 2720*9172SFei.Feng@Sun.COM static int 2721*9172SFei.Feng@Sun.COM rt2860_m_multicst(void *arg, boolean_t add, const uint8_t *mca) 2722*9172SFei.Feng@Sun.COM { 2723*9172SFei.Feng@Sun.COM return (ENOTSUP); 2724*9172SFei.Feng@Sun.COM } 2725*9172SFei.Feng@Sun.COM 2726*9172SFei.Feng@Sun.COM /*ARGSUSED*/ 2727*9172SFei.Feng@Sun.COM static int 2728*9172SFei.Feng@Sun.COM rt2860_m_promisc(void *arg, boolean_t on) 2729*9172SFei.Feng@Sun.COM { 2730*9172SFei.Feng@Sun.COM return (0); 2731*9172SFei.Feng@Sun.COM } 2732*9172SFei.Feng@Sun.COM 2733*9172SFei.Feng@Sun.COM static int 2734*9172SFei.Feng@Sun.COM rt2860_m_stat(void *arg, uint_t stat, uint64_t *val) 2735*9172SFei.Feng@Sun.COM { 2736*9172SFei.Feng@Sun.COM struct rt2860_softc *sc = (struct rt2860_softc *)arg; 2737*9172SFei.Feng@Sun.COM ieee80211com_t *ic = &sc->sc_ic; 2738*9172SFei.Feng@Sun.COM ieee80211_node_t *ni = ic->ic_bss; 2739*9172SFei.Feng@Sun.COM struct ieee80211_rateset *rs = &ni->in_rates; 2740*9172SFei.Feng@Sun.COM 2741*9172SFei.Feng@Sun.COM RT2860_GLOCK(sc); 2742*9172SFei.Feng@Sun.COM switch (stat) { 2743*9172SFei.Feng@Sun.COM case MAC_STAT_IFSPEED: 2744*9172SFei.Feng@Sun.COM *val = ((ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) ? 2745*9172SFei.Feng@Sun.COM (rs->ir_rates[ni->in_txrate] & IEEE80211_RATE_VAL) 2746*9172SFei.Feng@Sun.COM : ic->ic_fixed_rate) / 2 * 1000000; 2747*9172SFei.Feng@Sun.COM break; 2748*9172SFei.Feng@Sun.COM case MAC_STAT_NOXMTBUF: 2749*9172SFei.Feng@Sun.COM *val = sc->sc_tx_nobuf; 2750*9172SFei.Feng@Sun.COM break; 2751*9172SFei.Feng@Sun.COM case MAC_STAT_NORCVBUF: 2752*9172SFei.Feng@Sun.COM *val = sc->sc_rx_nobuf; 2753*9172SFei.Feng@Sun.COM break; 2754*9172SFei.Feng@Sun.COM case MAC_STAT_IERRORS: 2755*9172SFei.Feng@Sun.COM *val = sc->sc_rx_err; 2756*9172SFei.Feng@Sun.COM break; 2757*9172SFei.Feng@Sun.COM case MAC_STAT_RBYTES: 2758*9172SFei.Feng@Sun.COM *val = ic->ic_stats.is_rx_bytes; 2759*9172SFei.Feng@Sun.COM break; 2760*9172SFei.Feng@Sun.COM case MAC_STAT_IPACKETS: 2761*9172SFei.Feng@Sun.COM *val = ic->ic_stats.is_rx_frags; 2762*9172SFei.Feng@Sun.COM break; 2763*9172SFei.Feng@Sun.COM case MAC_STAT_OBYTES: 2764*9172SFei.Feng@Sun.COM *val = ic->ic_stats.is_tx_bytes; 2765*9172SFei.Feng@Sun.COM break; 2766*9172SFei.Feng@Sun.COM case MAC_STAT_OPACKETS: 2767*9172SFei.Feng@Sun.COM *val = ic->ic_stats.is_tx_frags; 2768*9172SFei.Feng@Sun.COM break; 2769*9172SFei.Feng@Sun.COM case MAC_STAT_OERRORS: 2770*9172SFei.Feng@Sun.COM case WIFI_STAT_TX_FAILED: 2771*9172SFei.Feng@Sun.COM *val = sc->sc_tx_err; 2772*9172SFei.Feng@Sun.COM break; 2773*9172SFei.Feng@Sun.COM case WIFI_STAT_TX_RETRANS: 2774*9172SFei.Feng@Sun.COM *val = sc->sc_tx_retries; 2775*9172SFei.Feng@Sun.COM break; 2776*9172SFei.Feng@Sun.COM case WIFI_STAT_FCS_ERRORS: 2777*9172SFei.Feng@Sun.COM case WIFI_STAT_WEP_ERRORS: 2778*9172SFei.Feng@Sun.COM case WIFI_STAT_TX_FRAGS: 2779*9172SFei.Feng@Sun.COM case WIFI_STAT_MCAST_TX: 2780*9172SFei.Feng@Sun.COM case WIFI_STAT_RTS_SUCCESS: 2781*9172SFei.Feng@Sun.COM case WIFI_STAT_RTS_FAILURE: 2782*9172SFei.Feng@Sun.COM case WIFI_STAT_ACK_FAILURE: 2783*9172SFei.Feng@Sun.COM case WIFI_STAT_RX_FRAGS: 2784*9172SFei.Feng@Sun.COM case WIFI_STAT_MCAST_RX: 2785*9172SFei.Feng@Sun.COM case WIFI_STAT_RX_DUPS: 2786*9172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 2787*9172SFei.Feng@Sun.COM return (ieee80211_stat(ic, stat, val)); 2788*9172SFei.Feng@Sun.COM default: 2789*9172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 2790*9172SFei.Feng@Sun.COM return (ENOTSUP); 2791*9172SFei.Feng@Sun.COM } 2792*9172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 2793*9172SFei.Feng@Sun.COM 2794*9172SFei.Feng@Sun.COM return (0); 2795*9172SFei.Feng@Sun.COM } 2796*9172SFei.Feng@Sun.COM 2797*9172SFei.Feng@Sun.COM static int 2798*9172SFei.Feng@Sun.COM rt2860_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd) 2799*9172SFei.Feng@Sun.COM { 2800*9172SFei.Feng@Sun.COM struct rt2860_softc *sc; 2801*9172SFei.Feng@Sun.COM struct ieee80211com *ic; 2802*9172SFei.Feng@Sun.COM int i, err, qid, ntries, instance; 2803*9172SFei.Feng@Sun.COM uint8_t cachelsz; 2804*9172SFei.Feng@Sun.COM uint16_t command, vendor_id, device_id; 2805*9172SFei.Feng@Sun.COM char strbuf[32]; 2806*9172SFei.Feng@Sun.COM wifi_data_t wd = { 0 }; 2807*9172SFei.Feng@Sun.COM mac_register_t *macp; 2808*9172SFei.Feng@Sun.COM 2809*9172SFei.Feng@Sun.COM switch (cmd) { 2810*9172SFei.Feng@Sun.COM case DDI_ATTACH: 2811*9172SFei.Feng@Sun.COM break; 2812*9172SFei.Feng@Sun.COM case DDI_RESUME: 2813*9172SFei.Feng@Sun.COM sc = ddi_get_soft_state(rt2860_soft_state_p, 2814*9172SFei.Feng@Sun.COM ddi_get_instance(devinfo)); 2815*9172SFei.Feng@Sun.COM ASSERT(sc != NULL); 2816*9172SFei.Feng@Sun.COM RT2860_GLOCK(sc); 2817*9172SFei.Feng@Sun.COM sc->sc_flags &= ~RT2860_F_SUSPEND; 2818*9172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 2819*9172SFei.Feng@Sun.COM if (RT2860_IS_RUNNING(sc)) 2820*9172SFei.Feng@Sun.COM (void) rt2860_init(sc); 2821*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_RESUME, "rwn: rt2860_attach(): " 2822*9172SFei.Feng@Sun.COM "resume now\n"); 2823*9172SFei.Feng@Sun.COM return (DDI_SUCCESS); 2824*9172SFei.Feng@Sun.COM default: 2825*9172SFei.Feng@Sun.COM return (DDI_FAILURE); 2826*9172SFei.Feng@Sun.COM } 2827*9172SFei.Feng@Sun.COM 2828*9172SFei.Feng@Sun.COM instance = ddi_get_instance(devinfo); 2829*9172SFei.Feng@Sun.COM 2830*9172SFei.Feng@Sun.COM err = ddi_soft_state_zalloc(rt2860_soft_state_p, instance); 2831*9172SFei.Feng@Sun.COM if (err != DDI_SUCCESS) { 2832*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): " 2833*9172SFei.Feng@Sun.COM "unable to alloc soft_state_p\n"); 2834*9172SFei.Feng@Sun.COM return (err); 2835*9172SFei.Feng@Sun.COM } 2836*9172SFei.Feng@Sun.COM 2837*9172SFei.Feng@Sun.COM sc = ddi_get_soft_state(rt2860_soft_state_p, instance); 2838*9172SFei.Feng@Sun.COM ic = (ieee80211com_t *)&sc->sc_ic; 2839*9172SFei.Feng@Sun.COM sc->sc_dev = devinfo; 2840*9172SFei.Feng@Sun.COM 2841*9172SFei.Feng@Sun.COM /* pci configuration */ 2842*9172SFei.Feng@Sun.COM err = ddi_regs_map_setup(devinfo, 0, &sc->sc_cfg_base, 0, 0, 2843*9172SFei.Feng@Sun.COM &rwn_csr_accattr, &sc->sc_cfg_handle); 2844*9172SFei.Feng@Sun.COM if (err != DDI_SUCCESS) { 2845*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): " 2846*9172SFei.Feng@Sun.COM "ddi_regs_map_setup() failed"); 2847*9172SFei.Feng@Sun.COM goto fail1; 2848*9172SFei.Feng@Sun.COM } 2849*9172SFei.Feng@Sun.COM 2850*9172SFei.Feng@Sun.COM cachelsz = ddi_get8(sc->sc_cfg_handle, 2851*9172SFei.Feng@Sun.COM (uint8_t *)(sc->sc_cfg_base + PCI_CONF_CACHE_LINESZ)); 2852*9172SFei.Feng@Sun.COM if (cachelsz == 0) 2853*9172SFei.Feng@Sun.COM cachelsz = 0x10; 2854*9172SFei.Feng@Sun.COM sc->sc_cachelsz = cachelsz << 2; 2855*9172SFei.Feng@Sun.COM sc->sc_dmabuf_size = roundup(IEEE80211_MAX_LEN, sc->sc_cachelsz); 2856*9172SFei.Feng@Sun.COM 2857*9172SFei.Feng@Sun.COM vendor_id = ddi_get16(sc->sc_cfg_handle, 2858*9172SFei.Feng@Sun.COM (uint16_t *)((uintptr_t)(sc->sc_cfg_base) + PCI_CONF_VENID)); 2859*9172SFei.Feng@Sun.COM device_id = ddi_get16(sc->sc_cfg_handle, 2860*9172SFei.Feng@Sun.COM (uint16_t *)((uintptr_t)(sc->sc_cfg_base) + PCI_CONF_DEVID)); 2861*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): " 2862*9172SFei.Feng@Sun.COM "vendor 0x%x, device id 0x%x, cache size %d\n", 2863*9172SFei.Feng@Sun.COM vendor_id, device_id, cachelsz); 2864*9172SFei.Feng@Sun.COM 2865*9172SFei.Feng@Sun.COM /* 2866*9172SFei.Feng@Sun.COM * Enable response to memory space accesses, 2867*9172SFei.Feng@Sun.COM * and enabe bus master. 2868*9172SFei.Feng@Sun.COM */ 2869*9172SFei.Feng@Sun.COM command = PCI_COMM_MAE | PCI_COMM_ME; 2870*9172SFei.Feng@Sun.COM ddi_put16(sc->sc_cfg_handle, 2871*9172SFei.Feng@Sun.COM (uint16_t *)((uintptr_t)(sc->sc_cfg_base) + PCI_CONF_COMM), 2872*9172SFei.Feng@Sun.COM command); 2873*9172SFei.Feng@Sun.COM ddi_put8(sc->sc_cfg_handle, 2874*9172SFei.Feng@Sun.COM (uint8_t *)(sc->sc_cfg_base + PCI_CONF_LATENCY_TIMER), 0xa8); 2875*9172SFei.Feng@Sun.COM ddi_put8(sc->sc_cfg_handle, 2876*9172SFei.Feng@Sun.COM (uint8_t *)(sc->sc_cfg_base + PCI_CONF_ILINE), 0x10); 2877*9172SFei.Feng@Sun.COM 2878*9172SFei.Feng@Sun.COM /* pci i/o space */ 2879*9172SFei.Feng@Sun.COM err = ddi_regs_map_setup(devinfo, 1, 2880*9172SFei.Feng@Sun.COM &sc->sc_io_base, 0, 0, &rwn_csr_accattr, &sc->sc_io_handle); 2881*9172SFei.Feng@Sun.COM if (err != DDI_SUCCESS) { 2882*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): " 2883*9172SFei.Feng@Sun.COM "ddi_regs_map_setup() failed"); 2884*9172SFei.Feng@Sun.COM goto fail2; 2885*9172SFei.Feng@Sun.COM } 2886*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): " 2887*9172SFei.Feng@Sun.COM "PCI configuration is done successfully\n"); 2888*9172SFei.Feng@Sun.COM 2889*9172SFei.Feng@Sun.COM /* wait for NIC to initialize */ 2890*9172SFei.Feng@Sun.COM for (ntries = 0; ntries < 100; ntries++) { 2891*9172SFei.Feng@Sun.COM sc->mac_rev = RT2860_READ(sc, RT2860_ASIC_VER_ID); 2892*9172SFei.Feng@Sun.COM if (sc->mac_rev != 0 && sc->mac_rev != 0xffffffff) 2893*9172SFei.Feng@Sun.COM break; 2894*9172SFei.Feng@Sun.COM DELAY(10); 2895*9172SFei.Feng@Sun.COM } 2896*9172SFei.Feng@Sun.COM if (ntries == 100) { 2897*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): " 2898*9172SFei.Feng@Sun.COM "timeout waiting for NIC initialize\n"); 2899*9172SFei.Feng@Sun.COM return (DDI_FAILURE); 2900*9172SFei.Feng@Sun.COM } 2901*9172SFei.Feng@Sun.COM 2902*9172SFei.Feng@Sun.COM if ((sc->mac_rev >> 16) != 0x2860 && 2903*9172SFei.Feng@Sun.COM (device_id == PRODUCT_RALINK_RT2890 || 2904*9172SFei.Feng@Sun.COM device_id == PRODUCT_RALINK_RT2790 || 2905*9172SFei.Feng@Sun.COM device_id == PRODUCT_AWT_RT2890)) 2906*9172SFei.Feng@Sun.COM sc->sc_flags |= RT2860_ADVANCED_PS; 2907*9172SFei.Feng@Sun.COM 2908*9172SFei.Feng@Sun.COM /* retrieve RF rev. no and various other things from EEPROM */ 2909*9172SFei.Feng@Sun.COM (void) rt2860_read_eeprom(sc); 2910*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): " 2911*9172SFei.Feng@Sun.COM "MAC/BBP RT%X (rev 0x%04X), RF %s (%dT%dR)\n", 2912*9172SFei.Feng@Sun.COM sc->mac_rev >> 16, sc->mac_rev & 0xffff, 2913*9172SFei.Feng@Sun.COM rt2860_get_rf(sc->rf_rev), sc->ntxchains, sc->nrxchains); 2914*9172SFei.Feng@Sun.COM 2915*9172SFei.Feng@Sun.COM /* 2916*9172SFei.Feng@Sun.COM * Allocate Tx (4 EDCAs + HCCA + Mgt) and Rx rings. 2917*9172SFei.Feng@Sun.COM */ 2918*9172SFei.Feng@Sun.COM for (qid = 0; qid < 6; qid++) { 2919*9172SFei.Feng@Sun.COM if ((err = rt2860_alloc_tx_ring(sc, &sc->txq[qid])) != 0) { 2920*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): " 2921*9172SFei.Feng@Sun.COM "could not allocate Tx ring %d\n", qid); 2922*9172SFei.Feng@Sun.COM goto fail3; 2923*9172SFei.Feng@Sun.COM } 2924*9172SFei.Feng@Sun.COM } 2925*9172SFei.Feng@Sun.COM 2926*9172SFei.Feng@Sun.COM if ((err = rt2860_alloc_rx_ring(sc, &sc->rxq)) != 0) { 2927*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): " 2928*9172SFei.Feng@Sun.COM "could not allocte Rx ring\n"); 2929*9172SFei.Feng@Sun.COM goto fail4; 2930*9172SFei.Feng@Sun.COM } 2931*9172SFei.Feng@Sun.COM 2932*9172SFei.Feng@Sun.COM if ((err = rt2860_alloc_tx_pool(sc)) != 0) { 2933*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): " 2934*9172SFei.Feng@Sun.COM "could not allocte Tx pool\n"); 2935*9172SFei.Feng@Sun.COM goto fail5; 2936*9172SFei.Feng@Sun.COM } 2937*9172SFei.Feng@Sun.COM 2938*9172SFei.Feng@Sun.COM mutex_init(&sc->sc_genlock, NULL, MUTEX_DRIVER, NULL); 2939*9172SFei.Feng@Sun.COM mutex_init(&sc->sc_txlock, NULL, MUTEX_DRIVER, NULL); 2940*9172SFei.Feng@Sun.COM mutex_init(&sc->sc_rxlock, NULL, MUTEX_DRIVER, NULL); 2941*9172SFei.Feng@Sun.COM 2942*9172SFei.Feng@Sun.COM /* mgmt ring is broken on RT2860C, use EDCA AC VO ring instead */ 2943*9172SFei.Feng@Sun.COM sc->mgtqid = (sc->mac_rev == 0x28600100) ? EDCA_AC_VO : 5; 2944*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach():" 2945*9172SFei.Feng@Sun.COM "mgtqid = %x\n", sc->mgtqid); 2946*9172SFei.Feng@Sun.COM 2947*9172SFei.Feng@Sun.COM ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ 2948*9172SFei.Feng@Sun.COM ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ 2949*9172SFei.Feng@Sun.COM ic->ic_state = IEEE80211_S_INIT; 2950*9172SFei.Feng@Sun.COM 2951*9172SFei.Feng@Sun.COM /* set device capabilities */ 2952*9172SFei.Feng@Sun.COM ic->ic_caps = 2953*9172SFei.Feng@Sun.COM IEEE80211_C_TXPMGT | /* tx power management */ 2954*9172SFei.Feng@Sun.COM IEEE80211_C_SHPREAMBLE | /* short preamble supported */ 2955*9172SFei.Feng@Sun.COM IEEE80211_C_SHSLOT; /* short slot time supported */ 2956*9172SFei.Feng@Sun.COM 2957*9172SFei.Feng@Sun.COM /* WPA/WPA2 support */ 2958*9172SFei.Feng@Sun.COM ic->ic_caps |= IEEE80211_C_WPA; /* Support WPA/WPA2 */ 2959*9172SFei.Feng@Sun.COM 2960*9172SFei.Feng@Sun.COM /* set supported .11b and .11g rates */ 2961*9172SFei.Feng@Sun.COM ic->ic_sup_rates[IEEE80211_MODE_11B] = rt2560_rateset_11b; 2962*9172SFei.Feng@Sun.COM ic->ic_sup_rates[IEEE80211_MODE_11G] = rt2560_rateset_11g; 2963*9172SFei.Feng@Sun.COM 2964*9172SFei.Feng@Sun.COM /* set supported .11b and .11g channels (1 through 14) */ 2965*9172SFei.Feng@Sun.COM for (i = 1; i <= 14; i++) { 2966*9172SFei.Feng@Sun.COM ic->ic_sup_channels[i].ich_freq = 2967*9172SFei.Feng@Sun.COM ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ); 2968*9172SFei.Feng@Sun.COM ic->ic_sup_channels[i].ich_flags = 2969*9172SFei.Feng@Sun.COM IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | 2970*9172SFei.Feng@Sun.COM IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ; 2971*9172SFei.Feng@Sun.COM } 2972*9172SFei.Feng@Sun.COM 2973*9172SFei.Feng@Sun.COM ic->ic_xmit = rt2860_send; 2974*9172SFei.Feng@Sun.COM 2975*9172SFei.Feng@Sun.COM ieee80211_attach(ic); 2976*9172SFei.Feng@Sun.COM 2977*9172SFei.Feng@Sun.COM /* register WPA door */ 2978*9172SFei.Feng@Sun.COM ieee80211_register_door(ic, ddi_driver_name(devinfo), 2979*9172SFei.Feng@Sun.COM ddi_get_instance(devinfo)); 2980*9172SFei.Feng@Sun.COM 2981*9172SFei.Feng@Sun.COM /* override state transition machine */ 2982*9172SFei.Feng@Sun.COM sc->sc_newstate = ic->ic_newstate; 2983*9172SFei.Feng@Sun.COM ic->ic_newstate = rt2860_newstate; 2984*9172SFei.Feng@Sun.COM ieee80211_media_init(ic); 2985*9172SFei.Feng@Sun.COM ic->ic_def_txkey = 0; 2986*9172SFei.Feng@Sun.COM 2987*9172SFei.Feng@Sun.COM err = ddi_add_softintr(devinfo, DDI_SOFTINT_LOW, 2988*9172SFei.Feng@Sun.COM &sc->sc_softintr_hdl, NULL, 0, rt2860_softintr, (caddr_t)sc); 2989*9172SFei.Feng@Sun.COM if (err != DDI_SUCCESS) { 2990*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): " 2991*9172SFei.Feng@Sun.COM "ddi_add_softintr() failed"); 2992*9172SFei.Feng@Sun.COM goto fail8; 2993*9172SFei.Feng@Sun.COM } 2994*9172SFei.Feng@Sun.COM 2995*9172SFei.Feng@Sun.COM err = ddi_get_iblock_cookie(devinfo, 0, &sc->sc_iblock); 2996*9172SFei.Feng@Sun.COM if (err != DDI_SUCCESS) { 2997*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): " 2998*9172SFei.Feng@Sun.COM "Can not get iblock cookie for INT\n"); 2999*9172SFei.Feng@Sun.COM goto fail7; 3000*9172SFei.Feng@Sun.COM } 3001*9172SFei.Feng@Sun.COM 3002*9172SFei.Feng@Sun.COM err = ddi_add_intr(devinfo, 0, NULL, NULL, rt2860_intr, (caddr_t)sc); 3003*9172SFei.Feng@Sun.COM if (err != DDI_SUCCESS) { 3004*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): " 3005*9172SFei.Feng@Sun.COM "unable to add device interrupt handler\n"); 3006*9172SFei.Feng@Sun.COM goto fail7; 3007*9172SFei.Feng@Sun.COM } 3008*9172SFei.Feng@Sun.COM 3009*9172SFei.Feng@Sun.COM /* 3010*9172SFei.Feng@Sun.COM * Provide initial settings for the WiFi plugin; whenever this 3011*9172SFei.Feng@Sun.COM * information changes, we need to call mac_plugindata_update() 3012*9172SFei.Feng@Sun.COM */ 3013*9172SFei.Feng@Sun.COM wd.wd_opmode = ic->ic_opmode; 3014*9172SFei.Feng@Sun.COM wd.wd_secalloc = WIFI_SEC_NONE; 3015*9172SFei.Feng@Sun.COM IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid); 3016*9172SFei.Feng@Sun.COM 3017*9172SFei.Feng@Sun.COM if ((macp = mac_alloc(MAC_VERSION)) == NULL) { 3018*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): " 3019*9172SFei.Feng@Sun.COM "MAC version mismatch\n"); 3020*9172SFei.Feng@Sun.COM goto fail9; 3021*9172SFei.Feng@Sun.COM } 3022*9172SFei.Feng@Sun.COM 3023*9172SFei.Feng@Sun.COM macp->m_type_ident = MAC_PLUGIN_IDENT_WIFI; 3024*9172SFei.Feng@Sun.COM macp->m_driver = sc; 3025*9172SFei.Feng@Sun.COM macp->m_dip = devinfo; 3026*9172SFei.Feng@Sun.COM macp->m_src_addr = ic->ic_macaddr; 3027*9172SFei.Feng@Sun.COM macp->m_callbacks = &rt2860_m_callbacks; 3028*9172SFei.Feng@Sun.COM macp->m_min_sdu = 0; 3029*9172SFei.Feng@Sun.COM macp->m_max_sdu = IEEE80211_MTU; 3030*9172SFei.Feng@Sun.COM macp->m_pdata = &wd; 3031*9172SFei.Feng@Sun.COM macp->m_pdata_size = sizeof (wd); 3032*9172SFei.Feng@Sun.COM 3033*9172SFei.Feng@Sun.COM err = mac_register(macp, &ic->ic_mach); 3034*9172SFei.Feng@Sun.COM mac_free(macp); 3035*9172SFei.Feng@Sun.COM if (err != 0) { 3036*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): " 3037*9172SFei.Feng@Sun.COM "mac_register err %x\n", err); 3038*9172SFei.Feng@Sun.COM goto fail9; 3039*9172SFei.Feng@Sun.COM } 3040*9172SFei.Feng@Sun.COM 3041*9172SFei.Feng@Sun.COM /* 3042*9172SFei.Feng@Sun.COM * Create minor node of type DDI_NT_NET_WIFI 3043*9172SFei.Feng@Sun.COM */ 3044*9172SFei.Feng@Sun.COM (void) snprintf(strbuf, sizeof (strbuf), "%s%d", 3045*9172SFei.Feng@Sun.COM "rwn", instance); 3046*9172SFei.Feng@Sun.COM err = ddi_create_minor_node(devinfo, strbuf, S_IFCHR, 3047*9172SFei.Feng@Sun.COM instance + 1, DDI_NT_NET_WIFI, 0); 3048*9172SFei.Feng@Sun.COM 3049*9172SFei.Feng@Sun.COM /* 3050*9172SFei.Feng@Sun.COM * Notify link is down now 3051*9172SFei.Feng@Sun.COM */ 3052*9172SFei.Feng@Sun.COM mac_link_update(ic->ic_mach, LINK_STATE_DOWN); 3053*9172SFei.Feng@Sun.COM 3054*9172SFei.Feng@Sun.COM sc->sc_flags &= ~RT2860_F_RUNNING; 3055*9172SFei.Feng@Sun.COM 3056*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach() successfully.\n"); 3057*9172SFei.Feng@Sun.COM return (DDI_SUCCESS); 3058*9172SFei.Feng@Sun.COM fail9: 3059*9172SFei.Feng@Sun.COM ddi_remove_softintr(sc->sc_softintr_hdl); 3060*9172SFei.Feng@Sun.COM fail8: 3061*9172SFei.Feng@Sun.COM ddi_remove_intr(devinfo, 0, sc->sc_iblock); 3062*9172SFei.Feng@Sun.COM fail7: 3063*9172SFei.Feng@Sun.COM mutex_destroy(&sc->sc_genlock); 3064*9172SFei.Feng@Sun.COM mutex_destroy(&sc->sc_txlock); 3065*9172SFei.Feng@Sun.COM mutex_destroy(&sc->sc_rxlock); 3066*9172SFei.Feng@Sun.COM fail6: 3067*9172SFei.Feng@Sun.COM rt2860_free_tx_pool(sc); 3068*9172SFei.Feng@Sun.COM fail5: 3069*9172SFei.Feng@Sun.COM rt2860_free_rx_ring(sc, &sc->rxq); 3070*9172SFei.Feng@Sun.COM fail4: 3071*9172SFei.Feng@Sun.COM while (--qid >= 0) 3072*9172SFei.Feng@Sun.COM rt2860_free_tx_ring(sc, &sc->txq[qid]); 3073*9172SFei.Feng@Sun.COM 3074*9172SFei.Feng@Sun.COM fail3: 3075*9172SFei.Feng@Sun.COM ddi_regs_map_free(&sc->sc_io_handle); 3076*9172SFei.Feng@Sun.COM fail2: 3077*9172SFei.Feng@Sun.COM ddi_regs_map_free(&sc->sc_cfg_handle); 3078*9172SFei.Feng@Sun.COM fail1: 3079*9172SFei.Feng@Sun.COM return (err); 3080*9172SFei.Feng@Sun.COM } 3081*9172SFei.Feng@Sun.COM 3082*9172SFei.Feng@Sun.COM static int 3083*9172SFei.Feng@Sun.COM rt2860_detach(dev_info_t *devinfo, ddi_detach_cmd_t cmd) 3084*9172SFei.Feng@Sun.COM { 3085*9172SFei.Feng@Sun.COM struct rt2860_softc *sc; 3086*9172SFei.Feng@Sun.COM int qid; 3087*9172SFei.Feng@Sun.COM 3088*9172SFei.Feng@Sun.COM sc = ddi_get_soft_state(rt2860_soft_state_p, ddi_get_instance(devinfo)); 3089*9172SFei.Feng@Sun.COM 3090*9172SFei.Feng@Sun.COM switch (cmd) { 3091*9172SFei.Feng@Sun.COM case DDI_DETACH: 3092*9172SFei.Feng@Sun.COM break; 3093*9172SFei.Feng@Sun.COM case DDI_SUSPEND: 3094*9172SFei.Feng@Sun.COM if (RT2860_IS_RUNNING(sc)) 3095*9172SFei.Feng@Sun.COM rt2860_stop(sc); 3096*9172SFei.Feng@Sun.COM RT2860_GLOCK(sc); 3097*9172SFei.Feng@Sun.COM sc->sc_flags &= ~RT2860_FWLOADED; 3098*9172SFei.Feng@Sun.COM sc->sc_flags |= RT2860_F_SUSPEND; 3099*9172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 3100*9172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_RESUME, "rwn: rt2860_detach(): " 3101*9172SFei.Feng@Sun.COM "suspend now\n"); 3102*9172SFei.Feng@Sun.COM return (DDI_SUCCESS); 3103*9172SFei.Feng@Sun.COM default: 3104*9172SFei.Feng@Sun.COM return (DDI_FAILURE); 3105*9172SFei.Feng@Sun.COM } 3106*9172SFei.Feng@Sun.COM 3107*9172SFei.Feng@Sun.COM if (mac_disable(sc->sc_ic.ic_mach) != 0) 3108*9172SFei.Feng@Sun.COM return (DDI_FAILURE); 3109*9172SFei.Feng@Sun.COM 3110*9172SFei.Feng@Sun.COM rt2860_stop(sc); 3111*9172SFei.Feng@Sun.COM 3112*9172SFei.Feng@Sun.COM /* 3113*9172SFei.Feng@Sun.COM * Unregister from the MAC layer subsystem 3114*9172SFei.Feng@Sun.COM */ 3115*9172SFei.Feng@Sun.COM (void) mac_unregister(sc->sc_ic.ic_mach); 3116*9172SFei.Feng@Sun.COM 3117*9172SFei.Feng@Sun.COM ddi_remove_intr(devinfo, 0, sc->sc_iblock); 3118*9172SFei.Feng@Sun.COM ddi_remove_softintr(sc->sc_softintr_hdl); 3119*9172SFei.Feng@Sun.COM 3120*9172SFei.Feng@Sun.COM /* 3121*9172SFei.Feng@Sun.COM * detach ieee80211 layer 3122*9172SFei.Feng@Sun.COM */ 3123*9172SFei.Feng@Sun.COM ieee80211_detach(&sc->sc_ic); 3124*9172SFei.Feng@Sun.COM 3125*9172SFei.Feng@Sun.COM rt2860_free_tx_pool(sc); 3126*9172SFei.Feng@Sun.COM rt2860_free_rx_ring(sc, &sc->rxq); 3127*9172SFei.Feng@Sun.COM for (qid = 0; qid < 6; qid++) 3128*9172SFei.Feng@Sun.COM rt2860_free_tx_ring(sc, &sc->txq[qid]); 3129*9172SFei.Feng@Sun.COM 3130*9172SFei.Feng@Sun.COM ddi_regs_map_free(&sc->sc_io_handle); 3131*9172SFei.Feng@Sun.COM 3132*9172SFei.Feng@Sun.COM mutex_destroy(&sc->sc_genlock); 3133*9172SFei.Feng@Sun.COM mutex_destroy(&sc->sc_txlock); 3134*9172SFei.Feng@Sun.COM mutex_destroy(&sc->sc_rxlock); 3135*9172SFei.Feng@Sun.COM 3136*9172SFei.Feng@Sun.COM ddi_remove_minor_node(devinfo, NULL); 3137*9172SFei.Feng@Sun.COM ddi_soft_state_free(rt2860_soft_state_p, ddi_get_instance(devinfo)); 3138*9172SFei.Feng@Sun.COM 3139*9172SFei.Feng@Sun.COM return (DDI_SUCCESS); 3140*9172SFei.Feng@Sun.COM } 3141*9172SFei.Feng@Sun.COM 3142*9172SFei.Feng@Sun.COM int 3143*9172SFei.Feng@Sun.COM _info(struct modinfo *modinfop) 3144*9172SFei.Feng@Sun.COM { 3145*9172SFei.Feng@Sun.COM return (mod_info(&modlinkage, modinfop)); 3146*9172SFei.Feng@Sun.COM } 3147*9172SFei.Feng@Sun.COM 3148*9172SFei.Feng@Sun.COM int 3149*9172SFei.Feng@Sun.COM _init(void) 3150*9172SFei.Feng@Sun.COM { 3151*9172SFei.Feng@Sun.COM int status; 3152*9172SFei.Feng@Sun.COM 3153*9172SFei.Feng@Sun.COM status = ddi_soft_state_init(&rt2860_soft_state_p, 3154*9172SFei.Feng@Sun.COM sizeof (struct rt2860_softc), 1); 3155*9172SFei.Feng@Sun.COM if (status != 0) 3156*9172SFei.Feng@Sun.COM return (status); 3157*9172SFei.Feng@Sun.COM 3158*9172SFei.Feng@Sun.COM mac_init_ops(&rwn_dev_ops, "rwn"); 3159*9172SFei.Feng@Sun.COM status = mod_install(&modlinkage); 3160*9172SFei.Feng@Sun.COM if (status != 0) { 3161*9172SFei.Feng@Sun.COM mac_fini_ops(&rwn_dev_ops); 3162*9172SFei.Feng@Sun.COM ddi_soft_state_fini(&rt2860_soft_state_p); 3163*9172SFei.Feng@Sun.COM } 3164*9172SFei.Feng@Sun.COM return (status); 3165*9172SFei.Feng@Sun.COM } 3166*9172SFei.Feng@Sun.COM 3167*9172SFei.Feng@Sun.COM int 3168*9172SFei.Feng@Sun.COM _fini(void) 3169*9172SFei.Feng@Sun.COM { 3170*9172SFei.Feng@Sun.COM int status; 3171*9172SFei.Feng@Sun.COM 3172*9172SFei.Feng@Sun.COM status = mod_remove(&modlinkage); 3173*9172SFei.Feng@Sun.COM if (status == 0) { 3174*9172SFei.Feng@Sun.COM mac_fini_ops(&rwn_dev_ops); 3175*9172SFei.Feng@Sun.COM ddi_soft_state_fini(&rt2860_soft_state_p); 3176*9172SFei.Feng@Sun.COM } 3177*9172SFei.Feng@Sun.COM return (status); 3178*9172SFei.Feng@Sun.COM } 3179